SerDes介绍以及原语使用介绍(4)ISERDESE2原语仿真

文章目录

前言

上文详细介绍了ISERDESE2原语的使用,本文根据仿真对ISERDESE2原语的使用进一步加深印象。在仿真时,与OSERDESE进行回环。

一、iserdese2_module模块

模块设计思路如下:

  1. 发送端在上电后会一直发送64个连续读8'bBC和8'b50,接受端需要一直检测接收到的并行数据是否为这俩个数据
  2. 发现数据错误则需要滑动串并转换窗口,在滑动窗口后,至少需要3个CLKDIV周期才可以检测到滑动后的数据,这里统一等待4个时钟周期进行检测,如果正确则关闭滑动窗口锁r_slip_lock ,在下一个周期进行滑动,即拉高r_bitslip
  3. 如若接收到的数据正确,则不需要滑动,但是为了排除误判的可能,需要继续观察一段时间,当连续接收到正确的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模块

  1. 模块上电后连续发送P_INIT_CNT个连续的8'bBC和8'b50
  2. 随后开始产生自增数据,观察接收端情况

代码如下:

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

四、仿真结果分析

如下图所示,黄色刻度线和蓝色刻度线之间的过程是在进行不断对齐,蓝色刻度线之后串并转换对齐,开始正常接收数据。

正常数据如下,结果比对,仿真结果正确:成功将自增数据进行恢复。

相关推荐
仲南音2 天前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner2 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20202 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy3 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
Rambo.xia3 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20203 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子3 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安4 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun4 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd
千寻xun4 天前
二、实战篇-NVME SSD控制之ZYNQ实现(三)
fpga开发·nvme ssd·nvme协议