文章目录

  • 系列文章目录
  • 为什么要学ISERDESE2
  • 一、ISERDESE2原语
  • 1.ISERDESE2端口说明
  • 2.ISERDESE2属性说明
  • 二、ISERDESE2工程代码
  • 1.工程代码
  • 2.测试代码
  • 三、ISERDESE2仿真
  • 1.不带Bitslip的仿真
  • 2.带Bitslip的仿真



为什么要学ISERDESE2

  在LVDS学习笔记之 IDELAYE2应用及仿真中作者已说明高速接口为什么需要延时。根据tap的值可以进行数据位的微调,如果当clk和data信号延时较大时,仅仅使用IDELAYE2无法达到预期,此时ISERDESE2就派上用途。
  ISERDESE2是专用的串并转换器,无需过多复杂的时序操作,并且其有sdr和ddr两种模式,很适合应用到高速源同步应用中。

一、ISERDESE2原语


ISERDESE2_ISERDESE2

  上图为ISERDESE2例化框图。ISERDESE2是FPGA的一个专用的串并联转换器,具有特定的时钟和逻辑特性,旨在促进高速源同步应用的实现。iserdes2避免了在FPGA结构中设计反序列化器时遇到的额外时序复杂性。其具有以下应用:

  1. 专用解串器/串并转换器:
      该转换器支持单数据速率(SDR)和双数据速率(DDR)模式。在SDR模式下,串并联转换器创建一个2-、3-、4-、5-、6-、7-或8位宽并行字。在DDR模式下,当使用一个ISERDESE2时,串并联转换器创建4、6、8位宽的并行字模式,当使用两个级联时创建10或14位宽的并行字模式
  2. Bitslip 子模块:
      Bitslip子模块允许设计人员重新排列进入FPGA结构的并行数据流的序列。这可以用于训练包含训练模式的源同步接口。
  3. 专用内存接口
      ISERDESE2包含专用电路(包括OCLK输入引脚),在ISERDESE2块中完全处理频闪到fpga的时钟域交叉,更利于提高性能和简化设计。
  4. 支持网络接口、DDR3接口、QDR接口、异步接口。

1.ISERDESE2端口说明

ISERDESE2_数据_02


下们对几个比较重要的端口进行阐述:

  1. 寄存器输出端口 Q1到Q8
      Q1到Q8是ISERDESE2模块的并行输出端口,一个ISERDESE2块可支持8bits输出,在DDR模式下宽度最多支持14bits。接收的第一位在Q的最高为出现,如下图所示:

      OSERDESE2要输出的串行数据从高到低位为[H G F E D C B A],当这些串行数据传输到ISERDESE2的输入端D后,首先进入ISERDESE2的是A,此时在Q1输出,紧接着进入B,ISERDESE2内部的移为寄存器会将A推向Q2,B从Q1输出,以此类推,最终输出的数据为[A B C D E F G H]。原来最高位变成了现在的最低位,这也是为什么ISERDESE2作为反序器的原因。
  2. 组合输出端口 O
      组合输出端口O在ISERDESE2模块中是一个没有寄存器的输出,这个数据会根据数据输入端口(D)或者延时数据输入端口(DDLY)的值输出。
  3. BITSLIP
      若在CLKDIV上升沿检测到BITSLIP引脚为高时,会执行一次bit位流动操作,随后输出端口Q1-Q8会移位。

      上图分别为在SDR模式和DDR模式下BITSLIP的操作。
    SDR模式下:每次的BITSLIP操作,会使输出左移1位;当8次操作后,恢复到最初状态;
    DDR模式下:每次的BITSLIP操作,会使输出交替进行右移1位和左移3位操作,当8次操作后,恢复到最初状态;

      上图为DDR模式下BITSLIP的时序图:
    在1时刻:Bitslip不为高,此时Q4到Q1输出为CDAB;
    在2时刻:Bitslip在CLKDIV的上升沿保持了一个时钟周期的高电平,触发了右移一位的操作;
    在3时刻:经历了3个时钟周期,完成右移一位的操作,此时Q4到Q1输出BCDA。
  4. 时钟使能输入 CE1和CE2

      CE1和CE2引脚用来作为时钟使能的时钟源,通过属性 NUM_CE来选择。当 NUM_CE=1时使用CE1作为时钟使能信号;当 NUM_CE=2时,在CLKDIV=0时使用CE2R,在CLKDIV=1时使用CE1R作为时钟使能信号。
  5. 高速时钟输入 CLK
      该时钟用于对输入的串行数据流进行处理。
  6. 高速时钟输入 CLKB
      该时钟用于对输入的串行数据流进行处理。CLKB连接到CLK的反相端, MEMORY_QDR模式除外。
  7. 分频时钟输入 CLKDIV
      分频时钟CLKDIV是由CLK分频得到的,它驱动串并联转换器、Bitslip子模块和CE模块的输出。
  8. 来自IOB的串行输入数据 D
      串行输入数据端口(D)是一个高速的数据输入端口。
  9. 来自IDELAYE2的串行输入数据 DDLY
      串行输入数据端口(DDLY)是一个高速的数据输入端口。
  10. 来自OSERDESE2的串行输入数据 OFB
      串行输入数据端口(OFB)是一个高速的数据输入端口。
  11. 用于频闪灯内存接口和过采样模式的高速时钟 OCLK
  12. 复位输入 RST

2.ISERDESE2属性说明

ISERDESE2_sed_03

  1. DATA_RATE属性
      选择数据输入模式,单倍速率模式(SDR),双倍速率模式(DDR)。
  2. DATA_WIDTH属性
      DATA_WIDTH属性定义串并行转换器的并行数据输出宽度。此属性的可能值取决于INTERFACE_TYPE和DATA_RATE属性。
  3. ISERDESE2_Data_04

  4. 当DATA_WIDTH设置为大于8的宽度时,一对ISERDESE2必须配置为主从配置。
  5. INTERFACE_TYPE属性
      INTERFACE_TYPE属性决定了ISERDESE2是在内存模式还是网络模式下配置的。这个属性允许的值是MEMORY,MEMORY_DDR3、MEMORY_QDR、OVERSAMPLE或NETWORKING。
  6. NUM_CE 属性
      用来选择时钟使能源。
  7. SERDES_MODE属性
      当使用宽度扩展时,SERDES_MODE属性定义了ISERDESE2模块是主从模块还是从模块。
  8. IOBDELAY属性
      用来选择输出源,真值表如下图所示:
  9. ISERDESE2_fpga开发_05

二、ISERDESE2工程代码

1.工程代码

`timescale 1ns / 1ps
module ISERDESE2_TEST(
		input			clk_in_50	,	
		input			reset		,
		input			BITSLIP		,
		input			D			,
		
		output [7:0] 	Q			,
		output			O			
    );
	
	wire		clk_200		;
	wire        clk_100		;
	wire        clk_50		;	
	wire        clk_25		;	
	wire		locked		;
	wire		CLK			;
	
	wire		sys_rest	;	//高有效
	wire		RST			;
	
	
	assign	sys_rest = locked? reset:1'b1;
	assign	RST = sys_rest;
	assign	CLK = clk_200;
	assign	CLKB = ~CLK;
	assign	CLKDIV = clk_25;

	clk_wiz_0 
	clk_wiz_0_inst
 (
	.clk_200m	(clk_200	),
	.clk_100m	(clk_100	),
	.clk_50m	(clk_50		),
	.clk_25m	(clk_25		),
	.reset		(reset	),
	.locked		(locked),
	.clk_in1	(clk_in_50)
 );
  
   ISERDESE2 #(
      .DATA_RATE("SDR"),           // 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(1),                  // Number of clock enables (1,2)
      .OFB_USED("FALSE"),          // 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(O),                       // 1-bit output: Combinatorial output
      // Q1 - Q8: 1-bit (each) output: Registered data outputs
      .Q1(Q[0]),
      .Q2(Q[1]),
      .Q3(Q[2]),
      .Q4(Q[3]),
      .Q5(Q[4]),
      .Q6(Q[5]),
      // .Q7(Q[6]),
      .Q8(Q[7]),
      // SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
      .SHIFTOUT1(),
      .SHIFTOUT2(),
      .BITSLIP(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'b0),
      .CLKDIVP(1'b0),           // 1-bit input: TBD
      // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
      // .CLK(CLK),                   // 1-bit input: High-speed clock
      .CLKB(CLKB),                 // 1-bit input: High-speed secondary clock
      .CLKDIV(CLKDIV),             // 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(D),                       // 1-bit input: Data input
      .DDLY(1'b0),                 // 1-bit input: Serial data from IDELAYE2
      .OFB(1'b0),                   // 1-bit input: Data feedback from OSERDESE2
      .OCLKB(1'b0),               // 1-bit input: High speed negative edge output clock
      .RST(RST),                   // 1-bit input: Active high asynchronous reset
      // SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
      .SHIFTIN1(),
      .SHIFTIN2()
   );

   // End of ISERDESE2_inst instantiation
				
endmodule

2.测试代码

`timescale 1ns / 1ps
`define clk_period 20
`define clk_data_period 5
module ISERDESE2_TEST_tb( );
	reg			clk_in_50	;	
	reg			reset		;
	reg			BITSLIP		;
	reg			D			;
							
	wire [7:0] 	Q			;
	wire		O			;

	
	ISERDESE2_TEST
	ISERDESE2_TEST_inst
	(
		.clk_in_50	(clk_in_50	),	
		.reset		(reset		),
		.BITSLIP	(BITSLIP	),
		.D			(D			),
		.Q			(Q			),
		.O			(O			)
    );
	
	initial begin clk_in_50 =0;	end
	always #(`clk_period/2) clk_in_50=~clk_in_50;
	
	initial begin
		// no_bitslip_task;
		bitslip_task;
		$stop;
	end
	
	task init;
	begin
		reset    	=1;
		BITSLIP 	=0;
		D      	 	=0;
		#(`clk_period*2);
		reset   	=0;
		@(posedge ISERDESE2_TEST_inst.locked);
		#(`clk_period*2+3);
	end
	endtask
	
	task no_bitslip_task;
	begin
		init;
		@(posedge ISERDESE2_TEST_inst.CLKDIV);
		#37.5;
		repeat(10)begin
			gen_ser_data(8'h5a);
		end
		#(`clk_period*10);
	end
	endtask
	
	task bitslip_task;
	begin
		init;
		@(posedge ISERDESE2_TEST_inst.CLKDIV);
		#37.5;
		repeat(5)begin
			repeat(10)begin
				gen_ser_data(8'h5a);

			end
			BITSLIP = 1;
			gen_ser_data(8'h5a);
			BITSLIP = 0;
		end
		
		repeat(20)begin
			gen_ser_data(8'h5a);
		end

	end
	endtask
	

	
	task gen_ser_data;
	input [7:0] data;
	reg [7:0] i;
	begin

		for(i=0;i<8;i=i+1)begin
			D = data[0];
			data = {1'b0,data[7:1]};
			#(`clk_data_period);
		end
		D = 0;
	end
	endtask
	
endmodule

三、ISERDESE2仿真

1.不带Bitslip的仿真

  不知道是前面理解问题还是什么,仿真的结果跟我想象的不一样,可能是文档的某个细节没看到,如果读者有懂的,欢迎私信、留言,在此先谢过大家了。

  仿真结果如下:

ISERDESE2_sed_06


  这里,我们只需要关心CLK、CLKDIV、D、Q信号。一开始仿真的时候对于CLKDIV和CLK的关系,我一直没搞懂,后面才发现他们的关系。他们跟DDR或SDR的模式以及数据长度有关,为f_CLK/bit_NUM/模式,比如,我这里采用的SDR模式,8bit数据长度,CLK使用的是200M,CLKDIV的时钟为(200/8/1)=25M。若采用的是DDR模式,8bit数据长度,CLK是200M,则CLKDIV的时钟为(200/8/2)=12.5M。

  最开始我认为在每个CLKDIV内,根据CLK的上升沿进行采样(DDR模式下为CLK的双沿采样),我发送的数据为(01011010),单实际Q段得到的却是最开始得到8’h02,后面稳定在8’hd2,将这两个拼在一块得到(0000_0010_1101_0010)。可以发现,黄色字体跟我们发送的一致。因此,我有个大胆的想法,是不是在第3个CLK时钟才开始采样,是不是应该这样看:

ISERDESE2_ISERDESE2_07

后来,我根据官方自带的仿真也是这样,从结果上看,第三个时钟才开始采样,可能是我有误解,有知道的大佬给小弟解惑。
还有一点要注意的是,我这里发送的数据比较特殊,我发送的是01011010,正序和倒序都是一样。ISERDESE2输出的的倒序,最先进入采样的点在Q的最高位。有感兴趣的小伙伴可以将TB文件里发送的数据改成其他的数据仿真以下。

2.带Bitslip的仿真

  Bitslip是在CLKDIV控制下运行的,因此Bitslip的一个高脉冲要与CLKDIV的时钟周期相同,具体仿真结果如下:

ISERDESE2_sed_08


由上图可发现,最开始Q端输出为(1101_0010),在一次Bitslip脉冲后,Q端输出变为(1010_0101),Q左移了一位。当Bitslip在次来时,Q端输出变为(0100_1011),Q又左移了一位,连续5次后得到了我想输入的值0x5A(0101_1010)。

ISERDESE2_fpga开发_09

  前文也对DDR模式下Bitslip的操作做了说明,那个是官方手册上的,上面说在Bitslip到来后的CLKDIV的第三个时钟数据才会改变,根据仿真图可以发现跟手册上有冲突。