文章目录
前言
上文详细介绍了ISERDESE2原语的使用,本文根据仿真对ISERDESE2原语的使用进一步加深印象。在仿真时,与OSERDESE进行回环。
一、iserdese2_module模块
模块设计思路如下:
- 发送端在上电后会一直发送64个连续读8'bBC和8'b50,接受端需要一直检测接收到的并行数据是否为这俩个数据
- 发现数据错误则需要滑动串并转换窗口,在滑动窗口后,至少需要3个CLKDIV周期才可以检测到滑动后的数据,这里统一等待4个时钟周期进行检测,如果正确则关闭滑动窗口锁r_slip_lock ,在下一个周期进行滑动,即拉高r_bitslip。
- 如若接收到的数据正确,则不需要滑动,但是为了排除误判的可能,需要继续观察一段时间,当连续接收到正确的P_MAX_RIGHT_NUM 个数据后,拉高信号r_byte_align 信号表示对齐,此时即可一直打开滑动窗口锁r_slip_lock,不在进行窗口滑动。
代码如下:
c
module iserdese2_module(
input i_clk ,
input i_div_clk ,
input i_rst ,
input i_OFB ,
output [7 :0] o_par_data ,
output o_data_valid
);
localparam P_MAX_RIGHT_NUM = 10;
reg r_bitslip ;
reg [2 :0] r_gap_cnt ;
reg r_byte_align ;
reg r_slip_lock ;
reg [5 :0] r_right_cnt ;
wire [7 :0] w_par_data ;
assign o_par_data = w_par_data ;
assign o_data_valid = r_byte_align ;
always @(posedge i_div_clk or posedge i_rst) begin
if(i_rst)
r_gap_cnt <= 'd0;
else if(r_byte_align || r_bitslip)
r_gap_cnt <= 'd0;
else if(r_slip_lock)
r_gap_cnt <= r_gap_cnt + 'd1;
else
r_gap_cnt <= 'd0;
end
always @(posedge i_div_clk or posedge i_rst) begin
if(i_rst)
r_slip_lock <= 'd1;
else if(r_byte_align || (r_gap_cnt == 5 && !r_slip_lock))
r_slip_lock <= 'd1;
else if(w_par_data != 8'hBC && w_par_data != 8'h50 && (r_gap_cnt == 4))
r_slip_lock <= 'd0;
else
r_slip_lock <= 'd1;
end
always @(posedge i_div_clk or posedge i_rst) begin
if(i_rst)
r_bitslip <= 'd0;
else if((r_gap_cnt == 5) && !r_slip_lock)
r_bitslip <= 'd1;
else
r_bitslip <= 'd0;
end
always @(posedge i_div_clk or posedge i_rst) begin
if(i_rst)
r_right_cnt <= 'd0;
else if(r_bitslip)
r_right_cnt <= 'd0;
else if(r_right_cnt == P_MAX_RIGHT_NUM)
r_right_cnt <= r_right_cnt;
else if(r_slip_lock && (w_par_data == 8'hBC || w_par_data == 8'h50))
r_right_cnt <= r_right_cnt + 1;
else
r_right_cnt <= r_right_cnt;
end
always @(posedge i_div_clk or posedge i_rst) begin
if(i_rst)
r_byte_align <= 'd0;
else if(r_right_cnt == P_MAX_RIGHT_NUM)
r_byte_align <= 'd1;
else
r_byte_align <= 'd0;
end
ISERDESE2 #(
.DATA_RATE ("DDR"), // DDR, SDR
.DATA_WIDTH (8), // Parallel data width (2-8,10,14)
.DYN_CLKDIV_INV_EN("FALSE"), // Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
.DYN_CLK_INV_EN ("FALSE"), // Enable DYNCLKINVSEL inversion (FALSE, TRUE)
// INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
.INIT_Q1 (1'b0),
.INIT_Q2 (1'b0),
.INIT_Q3 (1'b0),
.INIT_Q4 (1'b0),
.INTERFACE_TYPE ("NETWORKING"), // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
.IOBDELAY ("NONE"), // NONE, BOTH, IBUF, IFD
.NUM_CE (2), // Number of clock enables (1,2)
.OFB_USED ("TRUE"), // Select OFB path (FALSE, TRUE)
.SERDES_MODE ("MASTER"), // MASTER, SLAVE
// SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
.SRVAL_Q1 (1'b0),
.SRVAL_Q2 (1'b0),
.SRVAL_Q3 (1'b0),
.SRVAL_Q4 (1'b0)
)
ISERDESE2_inst (
.O( ), // 1-bit output: Combinatorial output
// Q1 - Q8: 1-bit (each) output: Registered data outputs
.Q1(w_par_data[7] ),
.Q2(w_par_data[6] ),
.Q3(w_par_data[5] ),
.Q4(w_par_data[4] ),
.Q5(w_par_data[3] ),
.Q6(w_par_data[2] ),
.Q7(w_par_data[1] ),
.Q8(w_par_data[0] ),
// SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
.SHIFTOUT1(),
.SHIFTOUT2(),
.BITSLIP(r_bitslip), // 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
// CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
// to Q8 output ports will shift, as in a barrel-shifter operation, one
// position every time Bitslip is invoked (DDR operation is different from
// SDR).
// CE1, CE2: 1-bit (each) input: Data register clock enable inputs
.CE1(1'b1),
.CE2(1'b1),
.CLKDIVP(1'b0), // 1-bit input: TBD
// Clocks: 1-bit (each) input: ISERDESE2 clock input ports
.CLK(i_clk), // 1-bit input: High-speed clock
.CLKB(~i_clk), // 1-bit input: High-speed secondary clock
.CLKDIV(i_div_clk), // 1-bit input: Divided clock
.OCLK(1'b0), // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
// Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
.DYNCLKDIVSEL(1'b0), // 1-bit input: Dynamic CLKDIV inversion
.DYNCLKSEL(1'b0), // 1-bit input: Dynamic CLK/CLKB inversion
// Input Data: 1-bit (each) input: ISERDESE2 data input ports
.D(1'b0), // 1-bit input: Data input
.DDLY(1'b0), // 1-bit input: Serial data from IDELAYE2
.OFB(i_OFB), // 1-bit input: Data feedback from OSERDESE2
.OCLKB(), // 1-bit input: High speed negative edge output clock
.RST(i_rst), // 1-bit input: Active high asynchronous reset
// SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
.SHIFTIN1(),
.SHIFTIN2()
);
endmodule
二、oserdese2_module模块
- 模块上电后连续发送P_INIT_CNT个连续的8'bBC和8'b50
- 随后开始产生自增数据,观察接收端情况
代码如下:
c
module oserdese2_module(
input i_clk ,
input i_div_clk ,
input i_rst ,
output o_OFB
);
localparam P_INIT_CNT = 64;
reg [7 :0] r_par_data ;
reg [15:0] r_init_cnt ;
reg r_init_flag ;
always @(posedge i_div_clk or posedge i_rst)begin
if(i_rst)
r_init_cnt <= 'd0;
else if(r_init_cnt == P_INIT_CNT)
r_init_cnt <= P_INIT_CNT + 1;
else
r_init_cnt <= r_init_cnt + 1;
end
always @(posedge i_div_clk or posedge i_rst)begin
if(i_rst)
r_init_flag <= 'd0;
else if(r_init_cnt < P_INIT_CNT)
r_init_flag <= ~r_init_flag;
else
r_init_flag <= r_init_flag;
end
always @(posedge i_div_clk or posedge i_rst)begin
if(i_rst)
r_par_data <= 'd0;
else if(r_init_cnt < P_INIT_CNT)
r_par_data <= r_init_flag ? 8'hBC : 8'h50;
else if(r_init_cnt == P_INIT_CNT)
r_par_data <= 'd0;
else
r_par_data <= r_par_data + 'd1;
end
OSERDESE2 #(
.DATA_RATE_OQ ("DDR" ), // DDR, SDR
.DATA_RATE_TQ ("DDR" ), // DDR, BUF, SDR
.DATA_WIDTH (8 ), // Parallel data width (2-8,10,14)
.INIT_OQ (1'b0 ), // Initial value of OQ output (1'b0,1'b1)
.INIT_TQ (1'b0 ), // Initial value of TQ output (1'b0,1'b1)
.SERDES_MODE ("MASTER" ), // MASTER, SLAVE
.SRVAL_OQ (1'b0 ), // OQ output value when SR is used (1'b0,1'b1)
.SRVAL_TQ (1'b0 ), // TQ output value when SR is used (1'b0,1'b1)
.TBYTE_CTL ("FALSE" ), // Enable tristate byte operation (FALSE, TRUE)
.TBYTE_SRC ("FALSE" ), // Tristate byte source (FALSE, TRUE)
.TRISTATE_WIDTH (1 ) // 3-state converter width (1,4)
)
OSERDESE2_inst (
.OFB (o_OFB ), // 1-bit output: Feedback path for data
.OQ ( ), // 1-bit output: Data path output
// SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
.SHIFTOUT1 ( ),
.SHIFTOUT2 ( ),
.TBYTEOUT ( ), // 1-bit output: Byte group tristate
.TFB ( ), // 1-bit output: 3-state control
.TQ ( ), // 1-bit output: 3-state control
.CLK (i_clk ), // 1-bit input: High speed clock
.CLKDIV (i_div_clk ), // 1-bit input: Divided clock
// D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
.D1 (r_par_data[0] ),
.D2 (r_par_data[1] ),
.D3 (r_par_data[2] ),
.D4 (r_par_data[3] ),
.D5 (r_par_data[4] ),
.D6 (r_par_data[5] ),
.D7 (r_par_data[6] ),
.D8 (r_par_data[7] ),
.OCE (1'b1 ), // 1-bit input: Output data clock enable
.RST (i_rst ), // 1-bit input: Reset
// SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
.SHIFTIN1 ( ),
.SHIFTIN2 ( ),
// T1 - T4: 1-bit (each) input: Parallel 3-state inputs
.T1 (1'b0 ),
.T2 (1'b0 ),
.T3 (1'b0 ),
.T4 (1'b0 ),
.TBYTEIN (1'b0 ), // 1-bit input: Byte group tristate
.TCE (1'b0 ) // 1-bit input: 3-state clock enable
);
endmodule
三、顶层模块
例化clk_wiz_100M_400M模块,产生100Mhz时钟和400Mhz时钟信号,分别对应CLKDIV和CLK,这也是最常用的方法。
c
module serdes_top(
input i_clk_p ,
input i_clk_n
);
wire w_clk_100mhz ;
wire w_clk_400mhz ;
wire w_locked ;
wire w_OFB ;
wire [7 :0] w_par_data ;
wire w_data_valid ;
clk_wiz_100M_400M clk_wiz_100M_400M_u0
(
.clk_out1 (w_clk_100mhz ),
.clk_out2 (w_clk_400mhz ),
.locked (w_locked ),
.clk_in1_p (i_clk_p ),
.clk_in1_n (i_clk_n )
);
oserdese2_module oserdese2_module_u0(
.i_clk (w_clk_400mhz ),
.i_div_clk (w_clk_100mhz ),
.i_rst (!w_locked ),
.o_OFB (w_OFB )
);
iserdese2_module iserdese2_module_u0(
.i_clk (w_clk_400mhz ),
.i_div_clk (w_clk_100mhz ),
.i_rst (!w_locked ),
.i_OFB (w_OFB ),
.o_par_data (w_par_data ),
.o_data_valid (w_data_valid )
);
endmodule
四、仿真结果分析
如下图所示,黄色刻度线和蓝色刻度线之间的过程是在进行不断对齐,蓝色刻度线之后串并转换对齐,开始正常接收数据。
正常数据如下,结果比对,仿真结果正确:成功将自增数据进行恢复。