ISERDES和IODELAYE1的使用
在公司调试了一个接口,使用Xilinx V6 FPGA的普通IO管脚使用LVDS电平传输1.25GHz数据,本工程使用ISERDES1 和OSERDES1原语做串并转换。
代码说明
ISERDES1原语:
ISERDESE1 #(
.DATA_RATE("DDR"), // "SDR" or "DDR"
.DATA_WIDTH(10), // Parallel data width (2-8, 10)
.DYN_CLKDIV_INV_EN("FALSE"), // Enable DYNCLKDIVINVSEL inversion (TRUE/FALSE)
.DYN_CLK_INV_EN("FALSE"), // Enable DYNCLKINVSEL inversion (TRUE/FALSE)
// 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", or "OVERSAMPLE"
.IOBDELAY("IFD"), // "NONE", "IBUF", "IFD", "BOTH"
.NUM_CE(2), // Number of clock enables (1 or 2)
.OFB_USED("FALSE"), // Select OFB path (TRUE/FALSE)
.SERDES_MODE("MASTER"), // "MASTER" or "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)
)
ISERDESE1_inst_master (
.O( ), // 1-bit output: Combinatorial output
// Q1 - Q6: 1-bit (each) output: Registered data outputs
.Q1(data_out_iser[0]),
.Q2(data_out_iser[1]),
.Q3(data_out_iser[2]),
.Q4(data_out_iser[3]),
.Q5(data_out_iser[4]),
.Q6(data_out_iser[5]),
// SHIFTOUT1-SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
.SHIFTOUT1(shift1_ise),
.SHIFTOUT2(shift2_ise),
.BITSLIP(BITSLIP), // 1-bit input: Bitslip enable input
// CE1, CE2: 1-bit (each) input: Data register clock enable input
.CE1(1'b1),
.CE2(1'b1),
// Clocks: 1-bit (each) input: ISERDESE1 clock input ports
.CLK(clk), // 1-bit input: High-speed clock input
.CLKB(!clk), // 1-bit input: High-speed secondary clock input
.CLKDIV(clk_div), // 1-bit input: Divided clock input
.OCLK(), // 1-bit input: High speed output clock input used when
// INTERFACE_TYPE="MEMORY" memory
// Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
.DYNCLKDIVSEL(), // 1-bit input: Dynamic CLKDIV inversion input
.DYNCLKSEL(), // 1-bit input: Dynamic CLK/CLKB inversion input
// Input Data: 1-bit (each) input: ISERDESE1 data input ports
.D(ose), // 1-bit input: Data input
.DDLY(ose_dly), // 1-bit input: Serial input data from IODELAYE1
.OFB(), // 1-bit input: Data feedback input from OSERDESE1
.RST(reset), // 1-bit input: Active high asynchronous reset input
// SHIFTIN1-SHIFTIN2: 1-bit (each) input: Data width expansion input ports
.SHIFTIN1(),
.SHIFTIN2()
);
ISERDESE1 #(
.DATA_RATE("DDR"), // "SDR" or "DDR"
.DATA_WIDTH(10), // Parallel data width (2-8, 10)
.DYN_CLKDIV_INV_EN("FALSE"), // Enable DYNCLKDIVINVSEL inversion (TRUE/FALSE)
.DYN_CLK_INV_EN("FALSE"), // Enable DYNCLKINVSEL inversion (TRUE/FALSE)
// 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", or "OVERSAMPLE"
.IOBDELAY("IFD"), // "NONE", "IBUF", "IFD", "BOTH"
.NUM_CE(2), // Number of clock enables (1 or 2)
.OFB_USED("FALSE"), // Select OFB path (TRUE/FALSE)
.SERDES_MODE("SLAVE"), // "MASTER" or "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)
)
ISERDESE1_inst_slave (
.O(), // 1-bit output: Combinatorial output
// Q1 - Q6: 1-bit (each) output: Registered data outputs
.Q1(),
.Q2(),
.Q3(data_out_iser[6]),
.Q4(data_out_iser[7]),
.Q5(data_out_iser[8]),
.Q6(data_out_iser[9]),
// SHIFTOUT1-SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
.SHIFTOUT1(),
.SHIFTOUT2(),
.BITSLIP(BITSLIP), // 1-bit input: Bitslip enable input
// CE1, CE2: 1-bit (each) input: Data register clock enable input
.CE1(1'b1),
.CE2(1'b1),
// Clocks: 1-bit (each) input: ISERDESE1 clock input ports
.CLK(clk), // 1-bit input: High-speed clock input
.CLKB(!clk), // 1-bit input: High-speed secondary clock input
.CLKDIV(clk_div), // 1-bit input: Divided clock input
.OCLK(), // 1-bit input: High speed output clock input used when
// INTERFACE_TYPE="MEMORY"
// Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
.DYNCLKDIVSEL(), // 1-bit input: Dynamic CLKDIV inversion input
.DYNCLKSEL(), // 1-bit input: Dynamic CLK/CLKB inversion input
// Input Data: 1-bit (each) input: ISERDESE1 data input ports
.D(), // 1-bit input: Data input
.DDLY(), // 1-bit input: Serial input data from IODELAYE1
.OFB(), // 1-bit input: Data feedback input from OSERDESE1
.RST(reset), // 1-bit input: Active high asynchronous reset input
// SHIFTIN1-SHIFTIN2: 1-bit (each) input: Data width expansion input ports
.SHIFTIN1(shift1_ise),
.SHIFTIN2(shift2_ise)
);
ISERDES原语支持级联模式,每一个级原语支持6位并行输出,本次工程使用的是10位并行数据,所以采用两个ISERDES原语级联,级联图示如下:
本次使用10位数据,Master接收低6位,Slavej接收高4位从Q3开始。切记SHIFTOUT1、SHIFTOUT1和SHIFTIN1、SHIFTIN1连接位置,如果接反了数据会出错。
注意:但比特输出O不能用,不能用ila看,不能用做上升沿采样,否则编译出错。
时钟说明:
.DATA_RATE("DDR"), // "SDR" or "DDR"
.CLK(clk), // 1-bit input: High-speed clock input
.CLKB(!clk), // 1-bit input: High-speed secondary clock input
.CLKDIV(clk_div), // 1-bit input: Divided clock input
原语只使用两个时钟,clk clk_div,本次传输速率为1.25Gbps,所以clk = 125MHz clk_div=625MHz。
数据传输时序如下:
两根黄线之间的时间是8ns,刚好传输10bit穿行数据(DDR双沿采样)。
IODELAYE1
RTL图:
IDELAYCTRL: IDELAYCTRL 和 IDELAYE2 一般同时使用, IDELAYCTRL 对 IDELAYE2 延时进行校准。
IODELAY_GROUP 为延时 IO 分组,一般数据接口位于多个 BANK 时,才需要分组。
IDELAYCTRL 通过参考时钟 REFCLK 来校准 IDELAY2 每个 tap 的延时值,可用的 REFCLK 频率为190Mhz~210Mhz 或者 290Mhz~310Mhz。时钟频率越高对应的 tap 延时平均值越小,即延时调节精度越高。
当参考时钟为 200Mhz 时,一个 tap 为 78ps。
原语代码:
(* IODELAY_GROUP = "idelay1" *)
IDELAYCTRL IDELAYCTRL_inst (
.RDY ( ),
.REFCLK (clk_200mpll ),
.RST (reset )
);
(* IODELAY_GROUP = "idelay1" *) // Specifies group name for associated IODELAYs and IDELAYCTRL
IODELAYE1 #(
.CINVCTRL_SEL ("FALSE" ), // Enable dynamic clock inversion ("TRUE"/"FALSE")
.DELAY_SRC ("I" ), // Delay input ("I", "CLKIN", "DATAIN", "IO", "O")
.HIGH_PERFORMANCE_MODE ("FALSE" ), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
.IDELAY_TYPE ("VARIABLE" ), // "DEFAULT", "FIXED", "VARIABLE", or "VAR_LOADABLE"
.IDELAY_VALUE (0 ), // Input delay tap setting (0-32)
.ODELAY_TYPE ("FIXED" ), // "FIXED", "VARIABLE", or "VAR_LOADABLE"
.ODELAY_VALUE (0 ), // Output delay tap setting (0-32)
.REFCLK_FREQUENCY (200.0 ), // IDELAYCTRL clock input frequency in MHz
.SIGNAL_PATTERN ("DATA" ) // "DATA" or "CLOCK" input signal
)
IODELAYE1_inst1 (
.CNTVALUEOUT (cnt_dly_out ), // 5-bit output - Counter value for monitoring purpose //0.78ps
.DATAOUT (ose_dly ), // 1-bit output - Delayed data output
.C (clk_div ), // 1-bit input - Clock input
.CE (CE ), // 1-bit input - Active high enable increment/decrement function
.CINVCTRL (1'b0 ), // 1-bit input - Dynamically inverts the Clock (C) polarity
.CLKIN (1'b0 ), // 1-bit input - Clock Access into the IODELAY
.CNTVALUEIN ( ), // 5-bit input - Counter value for loadable counter application
.DATAIN (1'b0 ), // 1-bit input - Internal delay data
.IDATAIN (ose ), // 1-bit input - Delay data input
.INC (INC ), // 1-bit input - Increment / Decrement tap delay
.ODATAIN ( ), // 1-bit input - Data input for the output datapath from the device
.RST (reset ), // 1-bit input - Active high, synchronous reset, resets delay chain to IDELAY_VALUE/
// ODELAY_VALUE tap. If no value is specified, the default is 0.
.T ( ) // 1-bit input - 3-state input control. Tie high for input-only or internal delay or
// tie low for output only.
);
IODELAY原语主要是基于输入时钟clk_div(200MHz) ,对输入的串行数据IDATAIN进行ps级别的延迟,输出的穿行数据DATAOUT直接接到ISERDES1的串行输入数据。
IODELEY主要配置的参数为INC CE RST三个,RST为高复位,直接接系统复位就行,如下图所示,INC为控制延迟的方向,INC=1时,向后延迟,INC=0时,向前延迟。最小延迟步进78ps。
在没开始调延迟的时候会又144ps的延迟(具体原因还没搞懂,应该是原语本身原因)。
在INC=1的情况下,拉高一个时钟(125MHz)周期的CE后,DATAOUT相比于IDATAIN向后延迟为222ps,差值为78ps(向前向后延迟时间都一样)。
在做IODELAY的时候,需要时刻观察ISERDES输出的并行数据,此时需要使用ISERDES和OSERDES之间的标校功能。
使用标校功能需要发送端发送一段时间的固定数,用于ISEDERS标校(当然这是ISERDES和OSERDES之间通信的时候使用),如果接收端不受控制则不需要标校,直接对接收的并行数据做其他处理即可。
ISERDES1标校流程
发送端一直发送10’b0000011111,接到接收端回送标校完成标志后再发送用户数据,此流程上电的时候便开始,也可以手动开始。
上电后,发送端一直发送接10’b0000011111,收端开始使用IODELAY对串行数据进行延迟,首先拉高INC,每个周期拉高一次CE,判断ISERDES并行数据是否变化,同时加个计数器,如下所示,数据在cnt=9 (t0)和cnt=19(t1)的时候并行数据发生变化,因为数双沿采样,所以两次数据变化的时间差,就是上升沿和下降沿之间的位置,找到双沿位置后,再向前延迟(t1+t0)/2,此时数据便调节到双沿之间的位置。此时IODELAY功能便调试完毕,完毕后便可以开始BITSLIP的位调节。
BITSLIP调节:本次使用的是DDR Mode
如图所示,DDR Mode :BITSLIP调节流程为,拉高一个周期的BITSLIP后,并行数据会右移一位输出,下次拉高一个周期的BITSLIP后,并行数据会左移三位输出。
SDR Mode :拉高一个周期的BITSLIP后,并行数据会左移一位输出,下次拉高一个周期的BITSLIP后,并行数据仍会左移一位输出,高位直接放到最低位。循环左移。
每次拉高一个周期的BITSLIP后,便需要判断ISERDES并行数据是否为10’b0000011111,如果不相等则继续移位调节,如果是,则停止移位操作,发送标校完成标志。
此时接收端的并行数据便会和发送端的并行数据一致。