[X]关闭

[米联客-XILINX-H3_CZ08_7100] FPGA基础篇连载-38 LVDS Select IO高速Serdes

文档创建者:FPGA课程
浏览次数:680
最后更新:2024-09-07
文档课程分类-AMD-ZYNQ
AMD-ZYNQ: ZYNQ-FPGA部分 » 2_FPGA实验篇(仅旗舰) » 1-FPGA基础入门实验
本帖最后由 FPGA课程 于 2024-9-7 14:40 编辑

​软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!

1概述
高速串行通信优势非常巨大,只需要很少的IO引脚就可以实现高速通信,这也是当今FPGA高速接口的核心技术。比如XILINX的7代FPGA,GTX可以达到10.3125Gbps,ultrascale FPGA的GTH可以达到16Gbps。目前国产FPGA还难以达到这么高的接口速度。
高速串行通信经常需要用到XILINX FPGA内部专用的SERDESE模块来实现串并转换。LVDS配合SERDESE可以充分发挥FPGA的高速接口优势。SERDESE分输入和输出,输入采用ISERDESE,输出采用OSERDESE。OSERDESE的使用要比ISERDESE简单。不同的FPGA构架,SERDESE的结构有一些差异,在使用的时候需要注意,比如7代FPGA的SERDESE和ultrascale系列的SERDESE就有一些差异,7代的FPGA程序移植到ultrascale系需要做一些必要的修改。
米联客2022版本教程需要更加全方位介绍FPGA的基础知识,当然也包括了一些底层的接口技术。本文笔者主要根据官方技术手册,以及官方给出的demo例程,通过仿真手段,让读者更加全方位了解XILINX FPGA底层硬件的高速接口应用技术。
本文涉及到一些概念也会是读者第一次遇到,包括idelay延迟原语的使用,时钟管理原语MMCME2_ADV和PLLE2_ADV的使用,ISERDESE串并转换和OSERDESE,并串转换原语的使用。其中很关键一点时使用idelay延迟模块以及ISERDESE原语中BITSLIP功能,实现数据的正确采集。
本文需要用到的官方技术文档包括:对于7代FPGA包括ug471,xapp585。
本文首先以xapp585提供的demo介绍实现7:1的并串转换和1:7的串并转换。
2 IDELAYE介绍
XILINX FPGA高速通信中经常会用到idelaye模块对信号做细微的时序调整。
2.1IDELAYCTRL
  1. IDELAYCTRL IDELAYCTRL_inst (
  2. .RDY(RDY),       // 1-bit output: Ready output
  3. .REFCLK(REFCLK), // 1-bit input: Reference clock input
  4. .RST(RST)        // 1-bit input: Active high reset input
  5. );
复制代码

IDELAYCTRL用于管理组内所有IDELAY或ODELAY的资源一个IO BANK里面只有一个IDELAYCTRL。REFCLK是参考时钟输入,这个时钟决定了每个tap的精度,tap的分辨率计算公式如下:
TIDELAYRESOLUTION=1/(32x 2 x FREF))
这样对于200M参考时钟,1个tap分辨率为:1/200M*32*2=78ps。
2.2IDELAYE2
70db872a3b7542c8ac451fee7e556100.jpg
  1. IDELAYE2 #(
  2.       .CINVCTRL_SEL("FALSE"),          // Enable dynamic clock inversion (FALSE, TRUE)
  3.       .DELAY_SRC("IDATAIN"),           // Delay input (IDATAIN, DATAIN)
  4.       .HIGH_PERFORMANCE_MODE("FALSE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
  5.       .IDELAY_TYPE("FIXED"),           // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
  6.       .IDELAY_VALUE(0),                // Input delay tap setting (0-31)
  7.       .PIPE_SEL("FALSE"),              // Select pipelined mode, FALSE, TRUE
  8.       .REFCLK_FREQUENCY(200.0),        // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
  9.       .SIGNAL_PATTERN("DATA")          // DATA, CLOCK input signal
  10.    )
  11.    IDELAYE2_inst (
  12.       .CNTVALUEOUT(CNTVALUEOUT), // 5-bit output: Counter value output
  13.       .DATAOUT(DATAOUT),         // 1-bit output: Delayed data output
  14.       .C(C),                     // 1-bit input: Clock input
  15.       .CE(CE),                   // 1-bit input: Active high enable increment/decrement input
  16.       .CINVCTRL(CINVCTRL),       // 1-bit input: Dynamic clock inversion input
  17.       .CNTVALUEIN(CNTVALUEIN),   // 5-bit input: Counter value input
  18.       .DATAIN(DATAIN),           // 1-bit input: Internal delay data input
  19.       .IDATAIN(IDATAIN),         // 1-bit input: Data input from the I/O
  20.       .INC(INC),                 // 1-bit input: Increment / Decrement tap delay input
  21.       .LD(LD),                   // 1-bit input: Load IDELAY_VALUE input
  22.       .LDPIPEEN(LDPIPEEN),       // 1-bit input: Enable PIPELINE register to load data input
  23.       .REGRST(REGRST)            // 1-bit input: Active-high reset tap-delay input
  24.    );
复制代码

参数描述
                        
参数
                        
                        
                        
                        
默认
                        
                        
描述
                        
                        
IDELAY_TYPE
                        
                        
String: FIXED,
                        
VARIABLE,
                        
VAR_LOAD或
                        
VAR_LOAD_PIPE
                        
                        
FIXED
                        
                        
定义延时类型
                        
                        
DELAY_SRC
                        
                        
String: IDATAIN,DATAIN
                        
                        
IDATAIN
                        
                        
选择延时源
                        
                        
IDELAY_VALUE
                        
                        
Integer: 0 to 31
                        
                        
0
                        
                        
固定延时值和其他模式的初值
                        
                        
HIGH_PERFORMANCE_MODE
                        
                        
Boolean: FALSE 或TRUE
                        
                        
TRUE
                        
                        
为TRUE的时候减少输出jitter,否则减少功耗
                        
                        
SIGNAL_PATTERN
                        
                        
String:DATA, CLOCK
                        
                        
DATA
                        
                        
指定时序分析工具按时钟还是数据路径进行分析
                        
                        
REFCLK_FREQUENCY
                        
                        
Real: 190 to 210 或290 to 310
                        
                        
200
                        
                        
设置静态时序分析的Tap值
                        
                        
CINVCTRL_SEL
                        
                        
Boolean: FALSE or TRUE
                        
                        
FALSE
                        
                        
设置是否动态转换C的极性
                        
                        
PIPE_SEL
                        
                        
Boolean: FALSE or TRUE
                        
                        
FALSE
                        
                        
选择是否使用Pipline模式
                        

信号描述
                        
端口
                        
                        
方向
                        
                        
位宽
                        
                        
描述
                        
                        
C
                        
                        
I
                        
                        
1
                        
                        
时钟输入,当使用VARIABLE, VAR_LOAD或 VAR_LOAD_PIPE模式下的时钟输入,必须是由BUFG或BUFR驱动,当使用SelectIO资源时,必须和它的时钟源相同。
                        
                        
REGRST
                        
                        
I
                        
                        
1
                        
                        
复位pipeline寄存器,仅在VAR_LOAD_PIPE模式下有效
                        
                        
LD
                        
                        
I
                        
                        
1
                        
                        
VARIABLE模式:加载预编程值;
                        
VAR_LOAD模式:加载CNTVALUEIN值;
                        
VAR_LOAD_PIPE模式:加载当前pipeline寄存器的值。
                        
                        
INC
                        
                        
I
                        
                        
1
                        
                        
INC/DEC增加,减少的Tap数
                        
                        
CE
                        
                        
I
                        
                        
1
                        
                        
使能INC和DEC功能,只在VARIABLE, VAR_LOAD或 VAR_LOAD_PIPE模式下有效;当CE保持为高的时候,每一个C周期都增加或减少一个TIDELAYRESOLUTION的时延。
                        
                        
CINVCTRL
                        
                        
I
                        
                        
1
                        
                        
动态翻转C的极性,当使用该功能时,需禁止IDELAY control引脚2个cycle。
                        
                        
CNTVALUEIN
                        
                        
I
                        
                        
5
                        
                        
来自FPGA逻辑的动态Tap值
                        
                        
IDATAIN
                        
                        
I
                        
                        
1
                        
                        
来自IBUF的数据输入,输出可驱动FPGA逻辑、ILOGICE和ISERDESE
                        
                        
DATAIN
                        
                        
I
                        
                        
1
                        
                        
来自FPGA逻辑的数据输入,输出不可驱动IOB
                        
                        
LDPIPEEN
                        
                        
I
                        
                        
1
                        
                        
使能pipeline寄存器加载数据
                        
                        
DATAOUT
                        
                        
O
                        
                        
1
                        
                        
延时输出的数据
                        
                        
CNTVALUEOUT
                        
                        
O
                        
                        
5
                        
                        
Tap值输出,用于FPGA监测
                        
2.3 IDELAY Timing Diagram
IDELAY_TYPE = VARIABLE, IDELAY_VALUE = 0, and DELAY_SRC = IDATAIN
e82d0d78b07749cab1f7f64f4fec928b.jpg
Clock Event 0:
在LD有效前,CNTVALUEOUT输出为未知值;
Clock Event 1:
在C的上升沿,检测到复位(LD为高电平),加载预装值IDELAY_VALUE,数据在抽头(抽头为了形象描述数据的位的延迟位置)tap0上。
Clock Event 2:
CE和INC上的脉冲在C的上升沿被捕获。这表示递增操作。输出增加1个tap延迟输出,这个时候抽头在tap1上。
Clock Event 3:
CE和INC不再有效, 输出会保持在tap1上,直到LD,CE或INC引脚上有进一步的动作。也就是后面的数据都会由1个tap的延迟吗,如果是200M时钟就是78ps。

IDELAY timing diagram in VAR_LOAD mode
847d6c883a9f46278ed7f4c6b866d171.jpg
Clock Event 0:
在LD有效前,CNTVALUEOUT输出为未知值;
Clock Event 1:
在C的上升沿采样到LD有效,此时DATAOUT延时CNTVALUEIN指定的延时Taps,改变tap Setting到Tap2,CNTVALUEOUT更新到新的Tap值;
Clock Event 2:
INC和CE有效,此时指定了增量操作,Tap值加1,DATAOUT输出从Tap2更新到Tap3,CNTVALUEOUT更新到新的Tap值;
Clock Event 3:
LD有效,DATAOUT输出延时更新到Tap10,CNTVALUEOUT更新到新的Tap值。
3 SERDESE原语介绍3.1 7系列FPGA的OSERDESE
0a50b17a667946d2b24e6fa9523f5e29.jpg
  1. OSERDESE2 #(
  2.       .DATA_RATE_OQ("DDR"),   // DDR, SDR
  3.       .DATA_RATE_TQ("DDR"),   // DDR, BUF, SDR
  4.       .DATA_WIDTH(4),         // Parallel data width (2-8,10,14)
  5.       .INIT_OQ(1'b0),         // Initial value of OQ output (1'b0,1'b1)
  6.       .INIT_TQ(1'b0),         // Initial value of TQ output (1'b0,1'b1)
  7.       .SERDES_MODE("MASTER"), // MASTER, SLAVE
  8.       .SRVAL_OQ(1'b0),        // OQ output value when SR is used (1'b0,1'b1)
  9.       .SRVAL_TQ(1'b0),        // TQ output value when SR is used (1'b0,1'b1)
  10.       .TBYTE_CTL("FALSE"),    // Enable tristate byte operation (FALSE, TRUE)
  11.       .TBYTE_SRC("FALSE"),    // Tristate byte source (FALSE, TRUE)
  12.       .TRISTATE_WIDTH(4)      // 3-state converter width (1,4)
  13.    )
  14.    OSERDESE2_inst (
  15.       .OFB(OFB),             // 1-bit output: Feedback path for data
  16.       .OQ(OQ),               // 1-bit output: Data path output
  17.       // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
  18.       .SHIFTOUT1(SHIFTOUT1),
  19.       .SHIFTOUT2(SHIFTOUT2),
  20.       .TBYTEOUT(TBYTEOUT),   // 1-bit output: Byte group tristate
  21.       .TFB(TFB),             // 1-bit output: 3-state control
  22.       .TQ(TQ),               // 1-bit output: 3-state control
  23.       .CLK(CLK),             // 1-bit input: High speed clock
  24.       .CLKDIV(CLKDIV),       // 1-bit input: Divided clock
  25.       // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
  26.       .D1(D1),
  27.       .D2(D2),
  28.       .D3(D3),
  29.       .D4(D4),
  30.       .D5(D5),
  31.       .D6(D6),
  32.       .D7(D7),
  33.       .D8(D8),
  34.       .OCE(OCE),             // 1-bit input: Output data clock enable
  35.       .RST(RST),             // 1-bit input: Reset
  36.       // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
  37.       .SHIFTIN1(SHIFTIN1),
  38.       .SHIFTIN2(SHIFTIN2),
  39.       // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
  40.       .T1(T1),
  41.       .T2(T2),
  42.       .T3(T3),
  43.       .T4(T4),
  44.       .TBYTEIN(TBYTEIN),     // 1-bit input: Byte group tristate
  45.       .TCE(TCE)              // 1-bit input: 3-state clock enable
  46.    );
复制代码

参数功能:
                        
Attribute
                        
                        
Description
                        
                        
Value
                        
                        
Default Value
                        
                        
DATA_RATE_OQ
                        
                        
数据传输是DDR或SDR方式
                        
                        
String: SDR or DDR
                        
                        
DDR
                        
                        
DATA_RATE_TQ
                        
                        
三态是DDR或SDR或BUF方式
                        
                        
String: BUF, SDR, or DDR
                        
                        
DDR
                        
                        
DATA_WIDTH
                        
                        
定义并行端口的位宽,这个值和DATA_RATE_OQ相关
                        
                        
Integer: 2, 3, 4, 5, 6, 7, 8, 10, or 14
                        
In SDR mode, 2, 3, 4, 5, 6, 7, and 8 are valid.
                        
In DDR mode, 2, 4, 6, 8, 10, and 14
                        
are valid.
                        
                        
                        
                        
SERDES_MODE
                        
                        
OSERDESE2是master还是slave
                        
                        
String: MASTER or SLAVE
                        
                        
MASTER
                        
                        
TRISTATE_WIDTH
                        
                        
定义并串转换,三态转换器的位宽
                        
                        
Integer: 1 or 4
                        
                        
4
                        
                        
TBYTE_CTL
                        
                        
仅通过MIG(ddr 控制器)使用
                        
                        
FALSE, TRUE
                        
                        
FALSE
                        
                        
TBYTE_SRC
                        
                        
仅通过MIG(ddr 控制器)使用
                        
                        
FALSE, TRUE
                        
                        
FALSE
                        
信号功能:
                        
Port Name
                        
                        
Type
                        
                        
Width
                        
                        
Description
                        
                        
OQ
                        
                        
Output
                        
                        
1
                        
                        
串行数据输出
                        
                        
OFB
                        
                        
Output
                        
                        
1
                        
                        
数据路径输出反馈到ISERDESE2或连接到ODELAYE2
                        
                        
TQ
                        
                        
Output
                        
                        
1
                        
                        
三态控制输出到IOB
                        
                        
TFB
                        
                        
Output
                        
                        
1
                        
                        
三态控制输出,说明OSERDESE2是三态
                        
                        
SHIFTOUT1
                        
                        
Output
                        
                        
1
                        
                        
进位输出以扩展数据宽度。连接到主OSERDESE2的SHIFTIN1
                        
                        
SHIFTOUT2
                        
                        
Output
                        
                        
1
                        
                        
进行输出以扩展数据宽度。连接到主OSERDESE2的SHIFTIN2
                        
                        
CLK
                        
                        
Input
                        
                        
1
                        
                        
高速时钟输入
                        
                        
CLKDIV
                        
                        
Input
                        
                        
1
                        
                        
CLK时钟的分频时钟
                        
                        
D1 to D8
                        
                        
Input
                        
                        
1
                        
                        
并行数据输入
                        
                        
TCE
                        
                        
Input
                        
                        
1
                        
                        
三态时钟使能
                        
                        
OCE
                        
                        
Input
                        
                        
1
                        
                        
输出数据时钟使能
                        
                        
TBYTEIN
                        
                        
Input
                        
                        
1
                        
                        
字节组三态输入
                        
                        
TBYTEOUT
                        
                        
Output
                        
                        
1
                        
                        
字节组三态输出
                        
                        
RST
                        
                        
Input
                        
                        
1
                        
                        
高电平复位
                        
                        
SHIFTIN1
                        
                        
Input
                        
                        
1
                        
                        
进位输入以扩展数据宽度。连接到从属OSERDESE2的SHIFTOUT1
                        
                        
SHIFTIN2
                        
                        
Input
                        
                        
1
                        
                        
进位输入以扩展数据宽度。连接到从属OSERDESE2的SHIFTOUT2
                        
                        
T1 to T4
                        
                        
Output
                        
                        
1
                        
                        
并联三态输入
                        
1、OSERDESE2级联扩展位宽
下图通过级联2个OSERDESE2实现10:1的并串转换
d0219c27a33d4398a04ee96f70dcfa0c.jpg
2、Timing Characteristics of 2:1 SDR Serialization
314f1ae9e5d44336833f5444b854c87b.jpg
Clock Event 1
OSERDESE2的D1 D2端口数据为未知
Clock Event 2
数据A B 到达OSERDESE2的D1 D2端口
Clock Event 3
在AB被采样后的一个CLK周期,数据位A出现在OQ,再下一个周期,输出数据位B
3、Timing Characteristics of 8:1 DDR Serialization
9f5b83c56a284dfb9873aecd321d735b.jpg
Clock Event 1
OSERDESE2的D1 D2端口数据为未知
Clock Event 2
数据A B 到达OSERDESE2的输入端口,并被采样
Clock Event 3
输入从OQ依次输出ABCDEFGH
Clock Event 4
下一组输出开始
4、Timing Characteristics of 4:1 DDR 3-State Controller Serialization
13df333c96ea4addb88b50c8fc45e889.jpg
Clock Event 1
T1,T2和T4被驱动为低电平以释放三态条件。 OSERDESE2中T1-T4和D1-D4的串行化路径相同(包括等待时间),因此在时钟事件1期间,EFGH位始终与T1-T4引脚上显示的0010对齐。
Clock Event 2
在将EFGH采样到OSERDESE2之后的一个CLK周期的OQ处依次出现数据位E,F,X,H
3.2 7系列FPGA的ISERDESE
8d2af9bd02dd4388aa6f21066722d016.jpg
  1. ISERDESE2 #(
  2.       .DATA_RATE("DDR"),           // DDR, SDR
  3.       .DATA_WIDTH(4),              // Parallel data width (2-8,10,14)
  4.       .DYN_CLKDIV_INV_EN("FALSE"), // Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
  5.       .DYN_CLK_INV_EN("FALSE"),    // Enable DYNCLKINVSEL inversion (FALSE, TRUE)
  6.       // INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
  7.       .INIT_Q1(1'b0),
  8.       .INIT_Q2(1'b0),
  9.       .INIT_Q3(1'b0),
  10.       .INIT_Q4(1'b0),
  11.       .INTERFACE_TYPE("MEMORY"),   // MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
  12.       .IOBDELAY("NONE"),           // NONE, BOTH, IBUF, IFD
  13.       .NUM_CE(2),                  // Number of clock enables (1,2)
  14.       .OFB_USED("FALSE"),          // Select OFB path (FALSE, TRUE)
  15.       .SERDES_MODE("MASTER"),      // MASTER, SLAVE
  16.       // SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
  17.       .SRVAL_Q1(1'b0),
  18.       .SRVAL_Q2(1'b0),
  19.       .SRVAL_Q3(1'b0),
  20.       .SRVAL_Q4(1'b0)
  21.    )
  22.    ISERDESE2_inst (
  23.       .O(O),                       // 1-bit output: Combinatorial output
  24.       // Q1 - Q8: 1-bit (each) output: Registered data outputs
  25.       .Q1(Q1),
  26.       .Q2(Q2),
  27.       .Q3(Q3),
  28.       .Q4(Q4),
  29.       .Q5(Q5),
  30.       .Q6(Q6),
  31.       .Q7(Q7),
  32.       .Q8(Q8),
  33.       // SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
  34.       .SHIFTOUT1(SHIFTOUT1),
  35.       .SHIFTOUT2(SHIFTOUT2),
  36.       .BITSLIP(BITSLIP),           // 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
  37.                                    // CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
  38.                                    // to Q8 output ports will shift, as in a barrel-shifter operation, one
  39.                                    // position every time Bitslip is invoked (DDR operation is different from
  40.                                    // SDR).

  41.       // CE1, CE2: 1-bit (each) input: Data register clock enable inputs
  42.       .CE1(CE1),
  43.       .CE2(CE2),
  44.       .CLKDIVP(CLKDIVP),           // 1-bit input: TBD
  45.       // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
  46.       .CLK(CLK),                   // 1-bit input: High-speed clock
  47.       .CLKB(CLKB),                 // 1-bit input: High-speed secondary clock
  48.       .CLKDIV(CLKDIV),             // 1-bit input: Divided clock
  49.       .OCLK(OCLK),                 // 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
  50.       // Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
  51.       .DYNCLKDIVSEL(DYNCLKDIVSEL), // 1-bit input: Dynamic CLKDIV inversion
  52.       .DYNCLKSEL(DYNCLKSEL),       // 1-bit input: Dynamic CLK/CLKB inversion
  53.       // Input Data: 1-bit (each) input: ISERDESE2 data input ports
  54.       .D(D),                       // 1-bit input: Data input
  55.       .DDLY(DDLY),                 // 1-bit input: Serial data from IDELAYE2
  56.       .OFB(OFB),                   // 1-bit input: Data feedback from OSERDESE2
  57.       .OCLKB(OCLKB),               // 1-bit input: High speed negative edge output clock
  58.       .RST(RST),                   // 1-bit input: Active high asynchronous reset
  59.       // SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
  60.       .SHIFTIN1(SHIFTIN1),
  61.       .SHIFTIN2(SHIFTIN2)
  62.    );
  63.    // End of ISERDESE2_inst instantiation
复制代码

参数功能:
                        
Attribute
                        
                        
Description
                        
                        
Value
                        
                        
Default Value
                        
                        
DATA_RATE
                        
                        
数据传输是DDR或SDR方式
                        
                        
String: SDR or DDR
                        
                        
DDR
                        
                        
DATA_WIDTH
                        
                        
定义串并转换并口的位宽
                        
                        
Integer: 2, 3, 4, 5, 6, 7, 8, 10 or 14.
                        
If DATA_RATE = DDR, value is
                        
limited to 4, 6, 8, 10, or 14.
                        
If DATA_RATE = SDR, value is
                        
limited to 2, 3, 4, 5, 6, 7, or 8.
                        
                        
4
                        
                        
DYN_CLKDIV_INV_EN
                        
                        
CLKDIV时钟极性动态设置使能,仅在MEMORY_QDR 和MEMORY_DDR3模式下使用,本文用不到。
                        
                        
Boolean: TRUE or FALSE
                        
                        
FALSE
                        
                        
DYN_CLK_INV_EN
                        
                        
CLK时钟极性动态设置使能,仅在MEMORY_QDR 和MEMORY_DDR3模式下使用,本文用不到。
                        
                        
Boolean: TRUE or FALSE
                        
                        
MEMORY
                        
                        
INTERFACE_TYPE
                        
                        
接口类型是memory 或者 networking
                        
                        
String: MEMORY,MEMORY_DDR3,MEMORY_QDR,OVERSAMPLE, orNETWORKING
                        
                        
4
                        
                        
NUM_CE
                        
                        
使能需要用到的时钟数量(CE1和CE2)
                        
                        
Integer: 1 or 2
                        
                        
2
                        
                        
OFB_USED
                        
                        
启用从OLOGICE2 / 3,OSERDESE2 OFB引脚到ISERDESE2 OFB引脚的路径。禁止使用D输入引脚。
                        
                        
Boolean: TRUE or FALSE
                        
                        
FALSE
                        
                        
SERDES_MODE
                        
                        
扩展模式下ISERDESE2是主模式还是从模式
                        
                        
String: MASTER or SLAVE
                        
                        
MASTER
                        
                        
INIT_Q1
                        
                        
设置第一个采样寄存器的初始值
                        
                        
Binary: 0 or 1
                        
                        
0
                        
                        
INIT_Q2
                        
                        
设置第二个采样寄存器的初始值
                        
                        
Binary: 0 or 1
                        
                        
0
                        
                        
INIT_Q3
                        
                        
设置第三个采样寄存器的初始值
                        
                        
Binary: 0 or 1
                        
                        
0
                        
                        
INIT_Q4
                        
                        
设置第四个采样寄存器的初始值
                        
                        
Binary: 0 or 1
                        
                        
0
                        
                        
SRVAL_Q1
                        
                        
设置第一个采样寄存器复位后的值
                        
                        
Binary: 0 or 1
                        
                        
1
                        
                        
SRVAL_Q2
                        
                        
设置第二个采样寄存器复位后的值
                        
                        
Binary: 0 or 1
                        
                        
1
                        
                        
SRVAL_Q3
                        
                        
设置第三个采样寄存器复位后的值
                        
                        
Binary: 0 or 1
                        
                        
1
                        
                        
SRVAL_Q4
                        
                        
设置第四个采样寄存器复位后的值
                        
                        
Binary: 0 or 1
                        
                        
1
                        
                        
IOBDELAY
                        
                        
设置延迟输入是否寄存输出
                        
                        
NONE, IBUF, IFD, or BOTH
                        
                        
NONE
                        
信号功能:
                        
Port Name
                        
                        
Type
                        
                        
Width
                        
                        
Description
                        
                        
Q1 – Q8
                        
                        
Output
                        
                        
1 (each)
                        
                        
串转并后的并行输出
                        
                        
O
                        
                        
Output
                        
                        
1
                        
                        
输出的是没有寄存过的输入D或者DDLY
                        
                        
SHIFTOUT1
                        
                        
Output
                        
                        
1
                        
                        
进位级联,当多个ISERDESE级联,接到下一个ISERDESE的SHIFTIN1
                        
                        
SHIFTOUT2
                        
                        
Output
                        
                        
1
                        
                        
进位级联,当多个ISERDESE级联,接到下一个ISERDESE的SHIFTIN2
                        
                        
D
                        
                        
Input
                        
                        
1
                        
                        
来自IOB串行数据输入
                        
                        
DDLY
                        
                        
Input
                        
                        
1
                        
                        
来自IDELAY2延迟输入后的数据输入
                        
                        
CLK
                        
                        
Input
                        
                        
1
                        
                        
高速时钟输入
                        
                        
CLKB
                        
                        
Input
                        
                        
1
                        
                        
第二个高速时钟输入,仅用于MEMORY_QDR模式。除非处于MEMORY_QDR模式,否则始终连接到~CLK。
                        
                        
CE1, CE2
                        
                        
Input
                        
                        
1
                        
                        
时钟输入使能
                        
                        
RST
                        
                        
Input
                        
                        
1
                        
                        
高电平复位
                        
                        
CLKDIV
                        
                        
Input
                        
                        
1
                        
                        
CLK时钟的分频时钟
                        
                        
CLKDIVP
                        
                        
Input
                        
                        
1
                        
                        
仅通过MIG工具支持。由PHASER_IN在MEMORY_DDR3模式下划分的CLK来源。 所有其他模式都接地。
                        
                        
OCLK
                        
                        
Input
                        
                        
1
                        
                        
用于存储器应用的高速时钟输入
                        
                        
BITSLIP
                        
                        
Input
                        
                        
1
                        
                        
调用Bitslip操作,
                        
                        
SHIFTIN1
                        
                        
Output
                        
                        
1
                        
                        
进位级联,当多个ISERDESE级联,接到上一个ISERDESE的SHIFTOUT1
                        
                        
SHIFTIN2
                        
                        
Output
                        
                        
1
                        
                        
进位级联,当多个ISERDESE级联,接到上一个ISERDESE的SHIFTOUT2
                        
                        
OFB
                        
                        
Output
                        
                        
1
                        
                        
来自OLOGICE2或OLOGICE3和OSERDESE2输出的反馈路径
                        
                        
DYNCLKDIVSEL
                        
                        
Input
                        
                        
1
                        
                        
动态选择CLKDIV反转
                        
                        
DYNCLKSEL
                        
                        
Input
                        
                        
1
                        
                        
动态选择CLK和CLKB反转
                        
ISERDESE2使用起来相对复杂一些,接下来我们对一些必要的概念继续介绍。
1、ISERDESE2数据转换位宽的支持
ISERDESE2 在SDR模式下数据转换的位宽可以为2、3、4、5、6、7、8bit,在DDR模式时,数据转换位宽为4、6、8bit, 2个ISERDESE2级联使用,DDR模式可以支持10、14bit。如下图所示通过2个ISERDESE2级联出10bit,14bit位宽数据接口。
59a406e8a5c6417baa82e7593724dc4f.jpg
2、串并转换后的数据顺序
a07165f88e604ddcbcff6206f8c922ac.jpg
如上图所示,可以看到数据的大小端是反了过来。
3、数据对齐
Bitslip用来实现并行数据的边界对齐。串行输入的8bit的数据,经过串并转换后,数据之间可能会错位,这是串并转换无法识别的,因此Bitslip就专门用来找到用户需要的并行数据边界。
对于SDR模式,Bitslip使能1次,则数据会左移1次,对于8bit并行数据,移动8次完成一个循环,可以这样无止境的循环,直到找到用户定义的并行数据。对于DDR模式,Bitslip工作方式不同,Bitslip使能1次,数据会右移1次或者左移3次,两者交替进行,同样移动8次完成一个循环。
f31cee460b4e486bbfbb7457cb5a0c9a.jpg
605ccbd67efc41e6b7da643ecc025b49.jpg
Clock Event 1
第一个字CDAB已被采样到ISERDESE2的输入侧寄存器中。Bitslip引脚未置位,数据没有任何重新对齐。
Clock Event 2
Bitslip引脚被置为有效,这使Bitslip控制器在内部将所有数据位向右移一位。 Bitslip在一个(仅一个)CLKDIV周期内保持高电平。
Clock Event 3
置位Bitslip后的三个CLKDIV周期,Bitslip操作完成,新的移位数据作为BCDA在输出上可用。
Clock Event 4
于ISERDESE2配置为1:4,因此Bitslip最多可以再声明两次。 在第二次移位后(此DDR还剩三个位置),在Q4-Q1上提供了(必需)输出ABCD。 在第三次移位后(右移一个位置),输出DABC在Q4-Q1上可用。 第四次移位(剩下三个位置)之后,原始输出CDAB在Q4-Q1上可用,并且Bitslip已完成所有四个输入组合的循环。
4、时钟
6d51b023cac1470b8d2453f2f517213c.jpg
1)、网络接口类型时钟要求(NETWORKING Interface Type)
CLK 和CLKDIV必须确保相位相同。
CLK由BUFIO驱动,CLKDIV由BUFR驱动
CLK由MMCM或PLL驱动,CLKDIV由同一MMCM或PLL的CLKOUT [0:6]驱动
当使用MMCM驱动ISERDESE2的CLK和CLKDIV时,不能混用提供ISERDESE2的缓冲器类型。例如,如果CLK由BUFG驱动,则CLKDIV也必须由BUFG驱动。另外,MMCM可以通过BUFIO和BUFR驱动ISERDESE2。
2、内存接口类型时钟要求(MEMORY Interface Type)
CLK由BUFIO驱动,OCLK由BUFIO驱动并且CLKDIV由BUFR驱动
CLK由MMCM或者PLL驱动,OCLK由MMCM或者PLL驱动,CLKDIV由相同的MMCM或者PLL驱动
CLK由BUFG驱动,OCLK由BUFG驱动,CLKDIV由不同的BUFG驱动
以上是笔者整理的关于ISERDESE2的资料,更多详细的资料可以阅读ug471
4 SERDESE的应用
对于用过米联客板卡的用户不知不觉中已经用上了SERDESE了,比如HDMI输出的IP就是用到了OSERDESE,实现了10:1的并串转换。
Xilinx 7系列FPGA包含ISERDES和OSERDES原语,串并设计非常简单,并且在使用逐位偏移校正时,可以根据芯片的系列和等级在415 Mbps至1200 Mbps的速度下进行更高的操作而较低的操作速度使用静态数据对齐。
本文主要以官方的xapp585介绍SERDESE的应用。一般官方给出的demo功能都会比较复杂,但是各方面需要演示的功能比较全面。
xapp585介绍了如何结合使用混合模式时钟管理器(MMCM)或锁相环(PLL)来有效地使用ISERDES和OSERDES,以使用低压差分信号(LVDS)数据接收和传输7:1数据,使用逐位偏移校正时,传输速率为415 Mbps至1200 Mb / s,具体取决于所使用的系列和速度等级。笔者把使用到了ug471,ug472,xapp585以及源码放到uisrc/06_doc路径下,方便读者查阅。其中xapp585中verilog源码放到uisrc/01_rtl中。
6244719220cc44f7855751fae77f3d6a.jpg

cef11599d9d245ba9c87bda3ef97cbfa.jpg
xapp585中提供的demo实现了7:1并串转换和1:7串并转换。对于SDR模式可以直接通过1个OSERDES实现并串转换,1个ISERDES实现串并转换。对于DDR模式,可以使用2个SERDES扩展到14个位宽实现。具体的下面我们以xapp585给出的demo分析。
4.1XAPP585源码概述
首先看下SDR模式的源码,可以看到RX端的串并转换有比TX端的并串转换要负责
a64bfe99f3e648f5aa467666669192b7.jpg
以下是DDR模式下的源码
b509a0e91b824ad191654e578ec87f07.jpg
在xapp585中,RX接收端需要实现6条line的串并转换,读者看到下面的图是不是有些疑惑?下图只有5条line多出的一条line是什么呢?这时候我们有必要看下代码,编写代码的人,必须有代码分析的能力,我们学习编程,应当以阅读代码为主,查阅资料为辅。
fb2c760dd3ce4d02aa25d3f1536037a5.jpg
下面代码来自xapp585中SDR接口RX接收部分,结合上图,可以看出demo中的代码有2个通路,每个通路数据通道是5条Line还有一条line是时钟。
04bdc2d8f29b4c44a20f39381f603274.jpg
DDR接口类似xapp585demo演示的是SDR和DDR实现相同的串并转换方案
9d0e653d2c584ab09a6c0760e1f19527.jpg
4.2并串转换PLL时钟
ISERDES和OSERDES都支持SDR和DDR模式,SDR消耗的资源少,但是受限于FPGA的最高运行时钟,如果超过了FPGA的最大运行时钟必须考虑用DDR模式。下表是7系列FPGA的时钟参数情况。
8a74f890b116479f86660a2f022498cb.jpg
a1c7539ecb724e029cb98008e2bb33f5.jpg
17f1c9665e4f486ca3759b5ac5834e09.jpg
以OSERDES中clock_generator_pll_7_to_1_diff_sdr.v为例。文件中MCME2_ADV和PLLE2_ADV原语是实现XILINX FPGA时钟管理的IP 原。Xapp585支持2种方式。本例中实际应用了PLLE2_ADV。
  1. IBUFGDS #(
  2.     .DIFF_TERM      (DIFF_TERM))
  3. clk_iob_in (
  4.     .I              (clkin_p),
  5.     .IB             (clkin_n),
  6.     .O              (clkint));

  7. generate
  8. if (USE_PLL == "FALSE") begin : loop8           // use an MMCM
  9. assign status[6] = 1'b1 ;
  10. assign status[3:2] = 2'b00 ;
  11.      
  12. MMCME2_ADV #(
  13.       .BANDWIDTH        ("OPTIMIZED"),         
  14.       .CLKFBOUT_MULT_F      (7*MMCM_MODE),              
  15.       .CLKFBOUT_PHASE       (0.0),              
  16.       .CLKIN1_PERIOD        (CLKIN_PERIOD),         
  17.       .CLKIN2_PERIOD        (CLKIN_PERIOD),         
  18.       .CLKOUT0_DIVIDE_F     (MMCM_MODE),            
  19.       .CLKOUT0_DUTY_CYCLE   (0.5),              
  20.       .CLKOUT0_PHASE        (0.0),              
  21.       .CLKOUT1_DIVIDE       (8),               
  22.       .CLKOUT1_DUTY_CYCLE   (0.5),              
  23.       .CLKOUT1_PHASE        (0.0),              
  24.       .CLKOUT2_DIVIDE       (7*MMCM_MODE),         
  25.       .CLKOUT2_DUTY_CYCLE   (0.5),              
  26.       .CLKOUT2_PHASE        (0.0),              
  27.       .CLKOUT3_DIVIDE       (8),               
  28.       .CLKOUT3_DUTY_CYCLE   (0.5),              
  29.       .CLKOUT3_PHASE        (0.0),              
  30.       .CLKOUT4_DIVIDE       (8),               
  31.       .CLKOUT4_DUTY_CYCLE   (0.5),              
  32.       .CLKOUT4_PHASE        (0.0),                  
  33.       .CLKOUT5_DIVIDE       (8),               
  34.       .CLKOUT5_DUTY_CYCLE   (0.5),              
  35.       .CLKOUT5_PHASE        (0.0),                  
  36.       .COMPENSATION     ("ZHOLD"),         
  37.       .DIVCLK_DIVIDE        (1),                    
  38.       .REF_JITTER1      (0.100))               
  39. tx_mmcme2_adv_inst (
  40.       .CLKFBOUT         (txpllmmcm_x1),            
  41.       .CLKFBOUTB        (),                     
  42.       .CLKFBSTOPPED     (),                     
  43.       .CLKINSTOPPED     (),                     
  44.       .CLKOUT0          (txpllmmcm_xn),            
  45.       .CLKOUT0B         (),                 
  46.       .CLKOUT1          (),                 
  47.       .CLKOUT1B         (),                 
  48.       .CLKOUT2          (),                 
  49.       .CLKOUT2B         (),                 
  50.       .CLKOUT3          (),                     
  51.       .CLKOUT3B         (),                 
  52.       .CLKOUT4          (),                     
  53.       .CLKOUT5          (),                     
  54.       .CLKOUT6          (),                     
  55.       .DO           (),                        
  56.       .DRDY         (),                        
  57.       .PSDONE           (),  
  58.       .PSCLK            (1'b0),  
  59.       .PSEN         (1'b0),  
  60.       .PSINCDEC         (1'b0),  
  61.       .PWRDWN           (1'b0),  
  62.       .LOCKED           (mmcm_lckd),               
  63.       .CLKFBIN          (pixel_clk),            
  64.       .CLKIN1           (clkint),               
  65.       .CLKIN2           (1'b0),                 
  66.       .CLKINSEL         (1'b1),                     
  67.       .DADDR            (7'h00),                    
  68.       .DCLK         (1'b0),                     
  69.       .DEN          (1'b0),                     
  70.       .DI           (16'h0000),            
  71.       .DWE          (1'b0),                     
  72.       .RST          (reset)) ;                  

  73.    if (PIXEL_CLOCK == "BUF_G") begin                // Final clock selection
  74.       BUFG  bufg_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  75.       assign status[1:0] = 2'b00 ;
  76.    end
  77.    else if (PIXEL_CLOCK == "BUF_R") begin
  78.       BUFR #(.BUFR_DIVIDE("1"),.SIM_DEVICE("7SERIES"))bufr_mmcm_x1 (.I(txpllmmcm_x1),.CE(1'b1),.O(pixel_clk),.CLR(1'b0)) ;
  79.       assign status[1:0] = 2'b01 ;
  80.    end
  81.    else begin
  82.       BUFH  bufh_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  83.       assign status[1:0] = 2'b10 ;
  84.    end
  85.       
  86.    if (TX_CLOCK == "BUF_G") begin               // Sample clock selection
  87.       BUFG  bufg_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  88.       assign status[5:4] = 2'b00 ;
  89.    end
  90.    else if (TX_CLOCK == "BUFIO") begin
  91.       BUFIO     bufio_mmcm_xn (.I (txpllmmcm_xn), .O(txclk)) ;
  92.       assign status[5:4] = 2'b11 ;
  93.    end
  94.    else begin
  95.       BUFH  bufh_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  96.       assign status[5:4] = 2'b10 ;
  97.    end
  98.    
  99. end
  100. else begin                          // Use a PLL
  101. assign status[6] = 1'b0 ;
  102. assign status[3:2] = 2'b00 ;

  103. PLLE2_ADV #(
  104.       .BANDWIDTH        ("OPTIMIZED"),         
  105.       .CLKFBOUT_MULT        (7*MMCM_MODE),              
  106.       .CLKFBOUT_PHASE       (0.0),              
  107.       .CLKIN1_PERIOD        (CLKIN_PERIOD),         
  108.       .CLKIN2_PERIOD        (CLKIN_PERIOD),         
  109.       .CLKOUT0_DIVIDE       (MMCM_MODE),            
  110.       .CLKOUT0_DUTY_CYCLE   (0.5),              
  111.       .CLKOUT0_PHASE        (0.0),              
  112.       .CLKOUT1_DIVIDE       (14*MMCM_MODE),         
  113.       .CLKOUT1_DUTY_CYCLE   (0.5),              
  114.       .CLKOUT1_PHASE        (0.0),              
  115.       .CLKOUT2_DIVIDE       (7*MMCM_MODE),         
  116.       .CLKOUT2_DUTY_CYCLE   (0.5),              
  117.       .CLKOUT2_PHASE        (0.0),              
  118.       .CLKOUT3_DIVIDE       (8),               
  119.       .CLKOUT3_DUTY_CYCLE   (0.5),              
  120.       .CLKOUT3_PHASE        (0.0),              
  121.       .CLKOUT4_DIVIDE       (8),               
  122.       .CLKOUT4_DUTY_CYCLE   (0.5),              
  123.       .CLKOUT4_PHASE        (0.0),                  
  124.       .CLKOUT5_DIVIDE       (8),               
  125.       .CLKOUT5_DUTY_CYCLE   (0.5),              
  126.       .CLKOUT5_PHASE        (0.0),                  
  127.       .COMPENSATION     ("ZHOLD"),         
  128.       .DIVCLK_DIVIDE        (1),                    
  129.       .REF_JITTER1      (0.100))               
  130. tx_mmcme2_adv_inst (
  131.       .CLKFBOUT         (txpllmmcm_x1),                 
  132.       .CLKOUT0          (txpllmmcm_xn),            
  133.       .CLKOUT1          (),                 
  134.       .CLKOUT2          (),                 
  135.       .CLKOUT3          (),                     
  136.       .CLKOUT4          (),                     
  137.       .CLKOUT5          (),                     
  138.       .DO           (),                        
  139.       .DRDY         (),                        
  140.       .PWRDWN           (1'b0),  
  141.       .LOCKED           (mmcm_lckd),               
  142.       .CLKFBIN          (pixel_clk),            
  143.       .CLKIN1           (clkint),               
  144.       .CLKIN2           (1'b0),                 
  145.       .CLKINSEL         (1'b1),                     
  146.       .DADDR            (7'h00),                    
  147.       .DCLK         (1'b0),                     
  148.       .DEN          (1'b0),                     
  149.       .DI           (16'h0000),            
  150.       .DWE          (1'b0),                     
  151.       .RST          (reset)) ;                  

  152.    if (PIXEL_CLOCK == "BUF_G") begin                // Final clock selection
  153.       BUFG  bufg_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  154.       assign status[1:0] = 2'b00 ;
  155.    end
  156.    else if (PIXEL_CLOCK == "BUF_R") begin
  157.       BUFR #(.BUFR_DIVIDE("1"),.SIM_DEVICE("7SERIES"))bufr_mmcm_x1 (.I(txpllmmcm_x1),.CE(1'b1),.O(pixel_clk),.CLR(1'b0)) ;
  158.       assign status[1:0] = 2'b01 ;
  159.    end
  160.    else begin
  161.       BUFH  bufh_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  162.       assign status[1:0] = 2'b10 ;
  163.    end
  164.       
  165.    if (TX_CLOCK == "BUF_G") begin               // Sample clock selection
  166.       BUFG  bufg_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  167.       assign status[5:4] = 2'b01 ;
  168.    end
  169.    else if (TX_CLOCK == "BUFIO") begin
  170.       BUFIO     bufio_mmcm_xn (.I (txpllmmcm_xn), .O(txclk)) ;
  171.       assign status[5:4] = 2'b11 ;
  172.    end
  173.    else begin
  174.       BUFH  bufh_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  175.       assign status[5:4] = 2'b10 ;
  176.    end
  177.    
  178. end
  179. endgenerate
复制代码

txclk : pixel_clk = 7:1,其中txpllmmcm_x1对应pixel_clk ,txpllmmcm_xn对应txclk对于时钟计算可阅读ug472,这里不是本文的重点。
653c3d7b85ec4c0ca9a6fa5e8b67ca61.jpg
再来看下DDR下由于无法直接用1个OSERDES实现7:1的转换,官方给出的例子使用了2个OSERDES,这个我们下面会分析,先看下时钟的关系:pixel_clk: txclk_div: txclk=2:1:7 这个比值关系对应的是每2个pixel_clk也就是2个7bits数据共14bits经过2个OSERDES以1:7的速率实现并串转换(DDR模式下,1个txclk可以发2个bit数据)。当读者看到我的分析肯定还是有疑惑所以一定要对照源码分析。
  1. IBUFGDS #(
  2.     .DIFF_TERM      (DIFF_TERM))
  3. clk_iob_in (
  4.     .I              (clkin_p),
  5.     .IB             (clkin_n),
  6.     .O              (clkint));

  7. generate
  8. if (USE_PLL == "FALSE") begin : loop8               // use an MMCM
  9. assign status[6] = 1'b1 ;
  10.      
  11. MMCME2_ADV #(
  12.       .BANDWIDTH        ("OPTIMIZED"),         
  13.       .CLKFBOUT_MULT_F      (7*MMCM_MODE),              
  14.       .CLKFBOUT_PHASE       (0.0),              
  15.       .CLKIN1_PERIOD        (CLKIN_PERIOD),         
  16.       .CLKIN2_PERIOD        (CLKIN_PERIOD),         
  17.       .CLKOUT0_DIVIDE_F     (2*MMCM_MODE),              
  18.       .CLKOUT0_DUTY_CYCLE   (0.5),              
  19.       .CLKOUT0_PHASE        (0.0),              
  20.       .CLKOUT1_DIVIDE       (14*MMCM_MODE),         
  21.       .CLKOUT1_DUTY_CYCLE   (0.5),              
  22.       .CLKOUT1_PHASE        (0.0),              
  23.       .CLKOUT2_DIVIDE       (7*MMCM_MODE),         
  24.       .CLKOUT2_DUTY_CYCLE   (0.5),              
  25.       .CLKOUT2_PHASE        (0.0),              
  26.       .CLKOUT3_DIVIDE       (8),               
  27.       .CLKOUT3_DUTY_CYCLE   (0.5),              
  28.       .CLKOUT3_PHASE        (0.0),              
  29.       .CLKOUT4_DIVIDE       (8),               
  30.       .CLKOUT4_DUTY_CYCLE   (0.5),              
  31.       .CLKOUT4_PHASE        (0.0),                  
  32.       .CLKOUT5_DIVIDE       (8),               
  33.       .CLKOUT5_DUTY_CYCLE   (0.5),              
  34.       .CLKOUT5_PHASE        (0.0),                  
  35.       .COMPENSATION     ("ZHOLD"),         
  36.       .DIVCLK_DIVIDE        (1),                    
  37.       .REF_JITTER1      (0.100))               
  38. tx_mmcme2_adv_inst (
  39.       .CLKFBOUT         (txpllmmcm_x1),                 
  40.       .CLKFBOUTB        (),                     
  41.       .CLKFBSTOPPED     (),                     
  42.       .CLKINSTOPPED     (),                     
  43.       .CLKOUT0          (txpllmmcm_xn),            
  44.       .CLKOUT0B         (),                 
  45.       .CLKOUT1          (txpllmmcm_d2),            
  46.       .CLKOUT1B         (),                 
  47.       .CLKOUT2          (),                 
  48.       .CLKOUT2B         (),                 
  49.       .CLKOUT3          (),                     
  50.       .CLKOUT3B         (),                 
  51.       .CLKOUT4          (),                     
  52.       .CLKOUT5          (),                     
  53.       .CLKOUT6          (),                     
  54.       .DO           (),                        
  55.       .DRDY         (),                        
  56.       .PSDONE           (),  
  57.       .PSCLK            (1'b0),  
  58.       .PSEN         (1'b0),  
  59.       .PSINCDEC         (1'b0),  
  60.       .PWRDWN           (1'b0),  
  61.       .LOCKED           (mmcm_lckd),               
  62.       .CLKFBIN          (pixel_clk),            
  63.       .CLKIN1           (clkint),               
  64.       .CLKIN2           (1'b0),                 
  65.       .CLKINSEL         (1'b1),                     
  66.       .DADDR            (7'h00),                    
  67.       .DCLK         (1'b0),                     
  68.       .DEN          (1'b0),                     
  69.       .DI           (16'h0000),            
  70.       .DWE          (1'b0),                     
  71.       .RST          (reset)) ;                  

  72.    if (PIXEL_CLOCK == "BUF_G") begin                // Final clock selection
  73.       BUFG  bufg_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  74.       assign status[1:0] = 2'b00 ;
  75.    end
  76.    else if (PIXEL_CLOCK == "BUF_R") begin
  77.       BUFR #(.BUFR_DIVIDE("1"),.SIM_DEVICE("7SERIES"))bufr_mmcm_x1 (.I(txpllmmcm_x1),.CE(1'b1),.O(pixel_clk),.CLR(1'b0)) ;
  78.       assign status[1:0] = 2'b01 ;
  79.    end
  80.    else begin
  81.       BUFH  bufh_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  82.       assign status[1:0] = 2'b10 ;
  83.    end

  84.    if (INTER_CLOCK == "BUF_G") begin                // Intermediate clock selection
  85.       BUFG  bufg_mmcm_d2 (.I(txpllmmcm_d2), .O(txclk_div)) ;
  86.       assign status[3:2] = 2'b00 ;
  87.    end
  88.    else if (INTER_CLOCK == "BUF_R") begin
  89.       BUFR #(.BUFR_DIVIDE("1"),.SIM_DEVICE("7SERIES"))bufr_mmcm_d2 (.I(txpllmmcm_d2),.CE(1'b1),.O(txclk_div),.CLR(1'b0)) ;
  90.       assign status[3:2] = 2'b01 ;
  91.    end
  92.    else begin
  93.       BUFH  bufh_mmcm_d2 (.I(txpllmmcm_d2), .O(txclk_div)) ;
  94.       assign status[3:2] = 2'b10 ;
  95.    end
  96.       
  97.    if (TX_CLOCK == "BUF_G") begin               // Sample clock selection
  98.       BUFG  bufg_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  99.       assign status[5:4] = 2'b00 ;
  100.    end
  101.    else if (TX_CLOCK == "BUFIO") begin
  102.       BUFIO     bufio_mmcm_xn (.I (txpllmmcm_xn), .O(txclk)) ;
  103.       assign status[5:4] = 2'b11 ;
  104.    end
  105.    else begin
  106.       BUFH  bufh_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  107.       assign status[5:4] = 2'b10 ;
  108.    end
  109.    
  110. end
  111. else begin

  112. assign status[6] = 1'b0 ;                   // Use a PLL

  113. PLLE2_ADV #(
  114.       .BANDWIDTH        ("OPTIMIZED"),         
  115.       .CLKFBOUT_MULT        (7*MMCM_MODE),              
  116.       .CLKFBOUT_PHASE       (0.0),              
  117.       .CLKIN1_PERIOD        (CLKIN_PERIOD),         
  118.       .CLKIN2_PERIOD        (CLKIN_PERIOD),         
  119.       .CLKOUT0_DIVIDE       (2*MMCM_MODE),              
  120.       .CLKOUT0_DUTY_CYCLE   (0.5),              
  121.       .CLKOUT0_PHASE        (0.0),              
  122.       .CLKOUT1_DIVIDE       (14*MMCM_MODE),         
  123.       .CLKOUT1_DUTY_CYCLE   (0.5),              
  124.       .CLKOUT1_PHASE        (0.0),              
  125.       .CLKOUT2_DIVIDE       (7*MMCM_MODE),         
  126.       .CLKOUT2_DUTY_CYCLE   (0.5),              
  127.       .CLKOUT2_PHASE        (0.0),              
  128.       .CLKOUT3_DIVIDE       (8),               
  129.       .CLKOUT3_DUTY_CYCLE   (0.5),              
  130.       .CLKOUT3_PHASE        (0.0),              
  131.       .CLKOUT4_DIVIDE       (8),               
  132.       .CLKOUT4_DUTY_CYCLE   (0.5),              
  133.       .CLKOUT4_PHASE        (0.0),                  
  134.       .CLKOUT5_DIVIDE       (8),               
  135.       .CLKOUT5_DUTY_CYCLE   (0.5),              
  136.       .CLKOUT5_PHASE        (0.0),                  
  137.       .COMPENSATION     ("ZHOLD"),         
  138.       .DIVCLK_DIVIDE        (1),                    
  139.       .REF_JITTER1      (0.100))               
  140. tx_mmcme2_adv_inst (
  141.       .CLKFBOUT         (txpllmmcm_x1),                 
  142.       .CLKOUT0          (txpllmmcm_xn),            
  143.       .CLKOUT1          (txpllmmcm_d2),            
  144.       .CLKOUT2          (),                 
  145.       .CLKOUT3          (),                     
  146.       .CLKOUT4          (),                     
  147.       .CLKOUT5          (),                     
  148.       .DO           (),                        
  149.       .DRDY         (),                        
  150.       .PWRDWN           (1'b0),  
  151.       .LOCKED           (mmcm_lckd),               
  152.       .CLKFBIN          (pixel_clk),            
  153.       .CLKIN1           (clkint),               
  154.       .CLKIN2           (1'b0),                 
  155.       .CLKINSEL         (1'b1),                     
  156.       .DADDR            (7'h00),                    
  157.       .DCLK         (1'b0),                     
  158.       .DEN          (1'b0),                     
  159.       .DI           (16'h0000),            
  160.       .DWE          (1'b0),                     
  161.       .RST          (reset)) ;                  

  162.    if (PIXEL_CLOCK == "BUF_G") begin                // Final clock selection
  163.       BUFG  bufg_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  164.       assign status[1:0] = 2'b00 ;
  165.    end
  166.    else if (PIXEL_CLOCK == "BUF_R") begin
  167.       BUFR #(.BUFR_DIVIDE("1"),.SIM_DEVICE("7SERIES"))bufr_mmcm_x1 (.I(txpllmmcm_x1),.CE(1'b1),.O(pixel_clk),.CLR(1'b0)) ;
  168.       assign status[1:0] = 2'b01 ;
  169.    end
  170.    else begin
  171.       BUFH  bufh_mmcm_x1 (.I(txpllmmcm_x1), .O(pixel_clk)) ;
  172.       assign status[1:0] = 2'b10 ;
  173.    end

  174.    if (INTER_CLOCK == "BUF_G") begin                // Intermediate clock selection
  175.       BUFG  bufg_mmcm_d2 (.I(txpllmmcm_d2), .O(txclk_div)) ;
  176.       assign status[3:2] = 2'b00 ;
  177.    end
  178.    else if (INTER_CLOCK == "BUF_R") begin
  179.       BUFR #(.BUFR_DIVIDE("1"),.SIM_DEVICE("7SERIES"))bufr_mmcm_d2 (.I(txpllmmcm_d2),.CE(1'b1),.O(txclk_div),.CLR(1'b0)) ;
  180.       assign status[3:2] = 2'b01 ;
  181.    end
  182.    else begin
  183.       BUFH  bufh_mmcm_d2 (.I(txpllmmcm_d2), .O(txclk_div)) ;
  184.       assign status[3:2] = 2'b10 ;
  185.    end
  186.       
  187.    if (TX_CLOCK == "BUF_G") begin               // Sample clock selection
  188.       BUFG  bufg_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  189.       assign status[5:4] = 2'b00 ;
  190.    end
  191.    else if (TX_CLOCK == "BUFIO") begin
  192.       BUFIO     bufio_mmcm_xn (.I (txpllmmcm_xn), .O(txclk)) ;
  193.       assign status[5:4] = 2'b11 ;
  194.    end
  195.    else begin
  196.       BUFH  bufh_mmcm_xn (.I(txpllmmcm_xn), .O(txclk)) ;
  197.       assign status[5:4] = 2'b10 ;
  198.    end
  199.    
  200. end
  201. endgenerate
复制代码

4.3并串转换OSERDESE使用
以上我们简单介绍了时钟部分的设计,接下来看数据部分,具体如何实现并串转换。首先我们看下如何将时钟通过OSERDESE发送出去。
先看下简单的SDR实现数据时钟的发送对应的文件是serdes_7_to_1_diff_sdr.v,其中clk_pattern=7'b1100001,可以在top5x2_7to1_sdr_tx.v找到parameter [6:0] TX_CLK_GEN   = 7'b1100001 ;这是一个占空比为3:4的时钟,SDR模式只要一个OSERDES就可以实现。
  1. OBUFDS io_clk_out (
  2.     .O              (clkout_p),
  3.     .OB             (clkout_n),
  4.     .I              (tx_clk_out));

  5. OSERDESE2 #(
  6.     .DATA_WIDTH         (7),            // SERDES word width
  7.     .TRISTATE_WIDTH         (1),
  8.     .DATA_RATE_OQ       ("SDR"),        // <SDR>, DDR
  9.     .DATA_RATE_TQ       ("SDR"),        // <SDR>, DDR
  10.     .SERDES_MODE        ("MASTER"))         // <DEFAULT>, MASTER, SLAVE
  11. oserdes_cm (
  12.     .OQ             (tx_clk_out),
  13.     .OCE            (1'b1),
  14.     .CLK            (txclk),
  15.     .RST            (reset_int),
  16.     .CLKDIV         (pixel_clk),
  17.     .D8             (1'b0),
  18.     .D7             (clk_pattern[6]),
  19.     .D6             (clk_pattern[5]),
  20.     .D5             (clk_pattern[4]),
  21.     .D4             (clk_pattern[3]),
  22.     .D3             (clk_pattern[2]),
  23.     .D2             (clk_pattern[1]),
  24.     .D1             (clk_pattern[0]),
  25.     .TQ             (),
  26.     .T1             (1'b0),
  27.     .T2             (1'b0),
  28.     .T3             (1'b0),
  29.     .T4             (1'b0),
  30.     .TCE            (1'b1),
  31.     .TBYTEIN        (1'b0),
  32.     .TBYTEOUT       (),
  33.     .OFB            (),
  34.     .TFB            (),
  35.     .SHIFTOUT1      (),         
  36.     .SHIFTOUT2      (),         
  37.     .SHIFTIN1       (1'b0),
  38.     .SHIFTIN2       (1'b0)) ;   
复制代码

再看下DDR模式下的数据时钟的发送,对应的文件是serdes_7_to_1_diff_ddr.v,可以看到DDR模式下采用了2个OSERDES级联实现和SDR的时钟参数一样clk_pattern=7'b1100001。如果有一些疑问可以看下前面“串并转换后的数据顺序”中图片描述。这里用2个OSERDESE2发送了2个clk_pattern。
  1. OBUFDS io_clk_out (
  2.     .O              (clkout_p),
  3.     .OB             (clkout_n),
  4.     .I              (tx_clk_out));

  5. OSERDESE2 #(
  6.     .DATA_WIDTH         (14),           // SERDES word width
  7.     .TRISTATE_WIDTH         (1),
  8.     .DATA_RATE_OQ       ("DDR"),        // <SDR>, DDR
  9.     .DATA_RATE_TQ       ("SDR"),        // <SDR>, DDR
  10.     .SERDES_MODE        ("MASTER"))         // <DEFAULT>, MASTER, SLAVE
  11. oserdes_cm (
  12.     .OQ             (tx_clk_out),
  13.     .OCE            (1'b1),
  14.     .CLK            (txclk),
  15.     .RST            (reset_intr),
  16.     .CLKDIV         (txclk_div),
  17.     .D8             (clk_pattern[0]),
  18.     .D7             (clk_pattern[6]),
  19.     .D6             (clk_pattern[5]),
  20.     .D5             (clk_pattern[4]),
  21.     .D4             (clk_pattern[3]),
  22.     .D3             (clk_pattern[2]),
  23.     .D2             (clk_pattern[1]),
  24.     .D1             (clk_pattern[0]),
  25.     .TQ             (),
  26.     .T1             (1'b0),
  27.     .T2             (1'b0),
  28.     .T3             (1'b0),
  29.     .T4             (1'b0),
  30.     .TCE            (1'b1),
  31.     .TBYTEIN        (1'b0),
  32.     .TBYTEOUT       (),
  33.     .OFB            (),
  34.     .TFB            (),
  35.     .SHIFTOUT1      (),         
  36.     .SHIFTOUT2      (),         
  37.     .SHIFTIN1       (cascade_cdi),  
  38.     .SHIFTIN2       (cascade_cti)) ;   

  39. OSERDESE2 #(
  40.     .DATA_WIDTH         (14),           // SERDES word width.
  41.     .TRISTATE_WIDTH         (1),
  42.     .DATA_RATE_OQ       ("DDR"),        // <SDR>, DDR
  43.     .DATA_RATE_TQ       ("SDR"),        // <SDR>, DDR
  44.     .SERDES_MODE        ("SLAVE"))          // <DEFAULT>, MASTER, SLAVE
  45. oserdes_cs (
  46.     .OQ             (),
  47.     .OCE            (1'b1),
  48.     .CLK            (txclk),
  49.     .RST            (reset_intr),
  50.     .CLKDIV         (txclk_div),
  51.     .D8             (clk_pattern[6]),
  52.     .D7             (clk_pattern[5]),
  53.     .D6             (clk_pattern[4]),
  54.     .D5             (clk_pattern[3]),
  55.     .D4             (clk_pattern[2]),
  56.     .D3             (clk_pattern[1]),
  57.     .D2             (1'b0),
  58.     .D1             (1'b0),
  59.     .TQ             (),
  60.     .T1             (1'b0),
  61.     .T2             (1'b0),
  62.     .T3             (1'b0),
  63.     .T4             (1'b0),
  64.     .TCE            (1'b1),
  65.     .TBYTEIN        (1'b0),
  66.     .TBYTEOUT       (),
  67.     .OFB            (),
  68.     .TFB            (),
  69.     .SHIFTOUT1      (cascade_cdi),  
  70.     .SHIFTOUT2      (cascade_cti),  
  71.     .SHIFTIN1       (1'b0),         
  72.     .SHIFTIN2       (1'b0)) ;
复制代码


再看SDR的OSERDES数据的并串发送部分,以下代码中主要用到了generate语法对0~4路5条line进行并串数据转换
  1. OBUFDS io_clk_out (
  2.     .O              (clkout_p),
  3.     .OB             (clkout_n),
  4.     .I              (tx_clk_out));

  5. OSERDESE2 #(
  6.     .DATA_WIDTH         (14),           // SERDES word width
  7.     .TRISTATE_WIDTH         (1),
  8.     .DATA_RATE_OQ       ("DDR"),        // <SDR>, DDR
  9.     .DATA_RATE_TQ       ("SDR"),        // <SDR>, DDR
  10.     .SERDES_MODE        ("MASTER"))         // <DEFAULT>, MASTER, SLAVE
  11. oserdes_cm (
  12.     .OQ             (tx_clk_out),
  13.     .OCE            (1'b1),
  14.     .CLK            (txclk),
  15.     .RST            (reset_intr),
  16.     .CLKDIV         (txclk_div),
  17.     .D8             (clk_pattern[0]),
  18.     .D7             (clk_pattern[6]),
  19.     .D6             (clk_pattern[5]),
  20.     .D5             (clk_pattern[4]),
  21.     .D4             (clk_pattern[3]),
  22.     .D3             (clk_pattern[2]),
  23.     .D2             (clk_pattern[1]),
  24.     .D1             (clk_pattern[0]),
  25.     .TQ             (),
  26.     .T1             (1'b0),
  27.     .T2             (1'b0),
  28.     .T3             (1'b0),
  29.     .T4             (1'b0),
  30.     .TCE            (1'b1),
  31.     .TBYTEIN        (1'b0),
  32.     .TBYTEOUT       (),
  33.     .OFB            (),
  34.     .TFB            (),
  35.     .SHIFTOUT1      (),         
  36.     .SHIFTOUT2      (),         
  37.     .SHIFTIN1       (cascade_cdi),  
  38.     .SHIFTIN2       (cascade_cti)) ;   

  39. OSERDESE2 #(
  40.     .DATA_WIDTH         (14),           // SERDES word width.
  41.     .TRISTATE_WIDTH         (1),
  42.     .DATA_RATE_OQ       ("DDR"),        // <SDR>, DDR
  43.     .DATA_RATE_TQ       ("SDR"),        // <SDR>, DDR
  44.     .SERDES_MODE        ("SLAVE"))          // <DEFAULT>, MASTER, SLAVE
  45. oserdes_cs (
  46.     .OQ             (),
  47.     .OCE            (1'b1),
  48.     .CLK            (txclk),
  49.     .RST            (reset_intr),
  50.     .CLKDIV         (txclk_div),
  51.     .D8             (clk_pattern[6]),
  52.     .D7             (clk_pattern[5]),
  53.     .D6             (clk_pattern[4]),
  54.     .D5             (clk_pattern[3]),
  55.     .D4             (clk_pattern[2]),
  56.     .D3             (clk_pattern[1]),
  57.     .D2             (1'b0),
  58.     .D1             (1'b0),
  59.     .TQ             (),
  60.     .T1             (1'b0),
  61.     .T2             (1'b0),
  62.     .T3             (1'b0),
  63.     .T4             (1'b0),
  64.     .TCE            (1'b1),
  65.     .TBYTEIN        (1'b0),
  66.     .TBYTEOUT       (),
  67.     .OFB            (),
  68.     .TFB            (),
  69.     .SHIFTOUT1      (cascade_cdi),  
  70.     .SHIFTOUT2      (cascade_cti),  
  71.     .SHIFTIN1       (1'b0),         
  72.     .SHIFTIN2       (1'b0)) ;
复制代码

DDR的数据的OSERDES并串发送部分,以下代码中主要用到了generate语法对0~4路5条line进行并串数据转换
  1. generate
  2. for (i = 0 ; i <= (D-1) ; i = i+1) begin : loop0

  3. OBUFDS io_data_out (
  4.     .O              (dataout_p[i]),
  5.     .OB             (dataout_n[i]),
  6.     .I              (tx_data_out[i]));

  7. // re-arrange data bits for transmission and invert lines as given by the mask
  8. // NOTE If pin inversion is required (non-zero SWAP MASK) then inverters will occur in fabric, as there are no inverters in the OSERDESE2
  9. // This can be avoided by doing the inversion (if necessary) in the user logic
  10. // TX_SWAP_MASK not available when IN_FIFO is used

  11. for (j = 0 ; j <= 13 ; j = j+1) begin : loop1
  12.     if (DATA_FORMAT == "PER_CLOCK") begin
  13.         assign mdataina[14*i+j] = dataint[D*j+i] ^ TX_SWAP_MASK[i] ;
  14.     end
  15.     else begin
  16.         if (j < 7) begin
  17.             assign mdataina[14*i+j] = dataint[(7*i)+j] ^ TX_SWAP_MASK[i] ;
  18.         end
  19.         else begin
  20.             assign mdataina[14*i+j] = dataint[(7*i)+j-7+D*7] ^ TX_SWAP_MASK[i];
  21.         end
  22.     end
  23. end

  24. OSERDESE2 #(
  25.     .DATA_WIDTH         (14),           // SERDES word width
  26.     .TRISTATE_WIDTH         (1),
  27.     .DATA_RATE_OQ       ("DDR"),        // <SDR>, DDR
  28.     .DATA_RATE_TQ       ("SDR"),        // <SDR>, DDR
  29.     .SERDES_MODE        ("MASTER"))         // <DEFAULT>, MASTER, SLAVE
  30. oserdes_m (
  31.     .OQ             (tx_data_out[i]),
  32.     .OCE            (1'b1),
  33.     .CLK            (txclk),
  34.     .RST            (reset_intr),
  35.     .CLKDIV         (txclk_div),
  36.     .D8             (mdataina[(14*i)+7]),
  37.     .D7             (mdataina[(14*i)+6]),
  38.     .D6             (mdataina[(14*i)+5]),
  39.     .D5             (mdataina[(14*i)+4]),
  40.     .D4             (mdataina[(14*i)+3]),
  41.     .D3             (mdataina[(14*i)+2]),
  42.     .D2             (mdataina[(14*i)+1]),
  43.     .D1             (mdataina[(14*i)+0]),
  44.     .TQ             (),
  45.     .T1             (1'b0),
  46.     .T2             (1'b0),
  47.     .T3             (1'b0),
  48.     .T4             (1'b0),
  49.     .TCE            (1'b1),
  50.     .TBYTEIN        (1'b0),
  51.     .TBYTEOUT       (),
  52.     .OFB            (),
  53.     .TFB            (),
  54.     .SHIFTOUT1      (),         
  55.     .SHIFTOUT2      (),         
  56.     .SHIFTIN1       (cascade_di[i]),   
  57.     .SHIFTIN2       (cascade_ti[i])) ;  

  58. OSERDESE2 #(
  59.     .DATA_WIDTH         (14),           // SERDES word width.
  60.     .TRISTATE_WIDTH         (1),
  61.     .DATA_RATE_OQ       ("DDR"),        // <SDR>, DDR
  62.     .DATA_RATE_TQ       ("SDR"),        // <SDR>, DDR
  63.     .SERDES_MODE        ("SLAVE"))          // <DEFAULT>, MASTER, SLAVE
  64. oserdes_s (
  65.     .OQ             (),
  66.     .OCE            (1'b1),
  67.     .CLK            (txclk),
  68.     .RST            (reset_intr),
  69.     .CLKDIV         (txclk_div),
  70.     .D8             (mdataina[(14*i)+13]),
  71.     .D7             (mdataina[(14*i)+12]),
  72.     .D6             (mdataina[(14*i)+11]),
  73.     .D5             (mdataina[(14*i)+10]),
  74.     .D4             (mdataina[(14*i)+9]),
  75.     .D3             (mdataina[(14*i)+8]),
  76.     .D2             (1'b0),
  77.     .D1             (1'b0),
  78.     .TQ             (),
  79.     .T1             (1'b0),
  80.     .T2             (1'b0),
  81.     .T3             (1'b0),
  82.     .T4             (1'b0),
  83.     .TCE            (1'b1),
  84.     .TBYTEIN        (1'b0),
  85.     .TBYTEOUT       (),
  86.     .OFB            (),
  87.     .TFB            (),
  88.     .SHIFTOUT1      (cascade_di[i]),   
  89.     .SHIFTOUT2      (cascade_ti[i]),   
  90.     .SHIFTIN1       (1'b0),         
  91.     .SHIFTIN2       (1'b0)) ;           

  92. end
  93. endgenerate
复制代码

4.4SDR ISERDESE使用4.4.1SDR ISERDESE时钟恢复
ISERDESE的作用是和OSERDESE相反的过程,但是比OSERDESE稍微复杂一些。ISERDESE代码需要实现自动找到最佳的数据采样点,自动完成数据对其,其中的加入了IDELAY和BITSLIP功能的使用,以及一些状态机代码。笔者下面根据XAPP585中给出的demo源码分析。
我们先看下新的概念IDELAY(ug471中有详细介绍)。
为了使用IDELAY需要先设置IDELAYCTRL,并且提供200M或者300M的参考时钟,如下代码,这段代码在文件top5x2_7to1_sdr_rx.v中,在top5x2_7to1_ddr_rx.v中也有相同的代码。
  1. IBUF iob_200m_in(
  2.     .I          (refclkin),
  3.     .O          (refclkint));

  4. BUFG bufg_200_ref (
  5.     .I          (refclkint),
  6.     .O          (refclkintbufg)) ;
  7.    
  8. IDELAYCTRL icontrol (                       // Instantiate input delay control block
  9.     .REFCLK     (refclkintbufg),
  10.     .RST        (reset),
  11.     .RDY        (delay_ready));
复制代码

代码会自动搜索最佳的delay延迟,这是如何实现的呢?首先我们看下代码中的bit_rate_value的值,在文件serdes_1_to_7_mmcm_idelay_sdr.v中有如下代码。比如设置bit_rate_value=0560,那么代表了serdes的速率是560Mbps,根据下面代码得出bt_val=5’h17(bt_val=5’d23)。如果idelay的参考时钟是200MHZ 那么每个延迟节拍是78ps,23*78ps= 1,794 ps = 557 Mb/s.最接近560Mbps.
  1. assign bit_time_value = bt_val ;

  2. always @ (bit_rate_value) begin         // Generate tap number to be used for input bit rate
  3.     if      (bit_rate_value > 16'h1068) begin bt_val <= 5'h0C ; end
  4.     else if (bit_rate_value > 16'h0986) begin bt_val <= 5'h0D ; end
  5.     else if (bit_rate_value > 16'h0916) begin bt_val <= 5'h0E ; end
  6.     else if (bit_rate_value > 16'h0855) begin bt_val <= 5'h0F ; end
  7.     else if (bit_rate_value > 16'h0801) begin bt_val <= 5'h10 ; end
  8.     else if (bit_rate_value > 16'h0754) begin bt_val <= 5'h11 ; end
  9.     else if (bit_rate_value > 16'h0712) begin bt_val <= 5'h12 ; end
  10.     else if (bit_rate_value > 16'h0675) begin bt_val <= 5'h13 ; end
  11.     else if (bit_rate_value > 16'h0641) begin bt_val <= 5'h14 ; end
  12.     else if (bit_rate_value > 16'h0611) begin bt_val <= 5'h15 ; end
  13.     else if (bit_rate_value > 16'h0583) begin bt_val <= 5'h16 ; end
  14.     else if (bit_rate_value > 16'h0557) begin bt_val <= 5'h17 ; end
  15.     else if (bit_rate_value > 16'h0534) begin bt_val <= 5'h18 ; end
  16.     else if (bit_rate_value > 16'h0513) begin bt_val <= 5'h19 ; end
  17.     else if (bit_rate_value > 16'h0493) begin bt_val <= 5'h1A ; end
  18.     else if (bit_rate_value > 16'h0475) begin bt_val <= 5'h1B ; end
  19.     else if (bit_rate_value > 16'h0458) begin bt_val <= 5'h1C ; end
  20.     else if (bit_rate_value > 16'h0442) begin bt_val <= 5'h1D ; end
  21.     else if (bit_rate_value > 16'h0427) begin bt_val <= 5'h1E ; end
  22.     else                                begin bt_val <= 5'h1F ; end
  23. end
复制代码

以上是完成了最高速度,最大延迟的参数选择,具体如何自动计算呢?先看下XAPP585中给出的计算原理。
找到传输速率对应的最大delay延迟参数,最大延迟参数的延迟不超过最大速度的周期,我们这里仿真文件中需要选择时钟为80M(默认是100M)在tb_top5x2_7to1_sdr设置always #(6250) pixelclock_p = ~pixelclock_p ;所以计算出串行速度是7*80M=560Mbps
有了个560Mbps的参数,就可以选出bit_rate_value为0557,所以bt_val为17h
先取出bt_val的中间值,17h的中间值是0Bh。把差分时钟的n延迟0Bh,把差分时钟的p延迟17h。代码如下:
  1. // Clock input

  2. IBUFGDS_DIFF_OUT #(
  3.     .DIFF_TERM      (DIFF_TERM),
  4.     .IBUF_LOW_PWR       ("FALSE"))
  5. iob_clk_in (
  6.     .I              (clkin_p),
  7.     .IB             (clkin_n),
  8.     .O              (rx_clk_in_p),
  9.     .OB                 (rx_clk_in_n));

  10. genvar i ;
  11. genvar j ;

  12. IDELAYE2 #(
  13.     .HIGH_PERFORMANCE_MODE  (HIGH_PERFORMANCE_MODE),
  14.         .IDELAY_VALUE       (1),
  15.         .DELAY_SRC      ("IDATAIN"),
  16.         .IDELAY_TYPE        ("VAR_LOAD"))
  17. idelay_cm(
  18.     .DATAOUT        (rx_clkin_p_d),
  19.     .C          (rxclk_div),
  20.     .CE         (1'b0),
  21.     .INC            (1'b0),
  22.     .DATAIN         (1'b0),
  23.     .IDATAIN        (rx_clk_in_p),
  24.     .LD         (1'b1),
  25.     .LDPIPEEN       (1'b0),
  26.     .REGRST         (1'b0),
  27.     .CINVCTRL       (1'b0),
  28.     .CNTVALUEIN     (c_delay_in),
  29.     .CNTVALUEOUT        ());

  30. IDELAYE2 #(
  31.     .HIGH_PERFORMANCE_MODE  (HIGH_PERFORMANCE_MODE),
  32.         .IDELAY_VALUE       (1),
  33.         .DELAY_SRC      ("IDATAIN"),
  34.         .IDELAY_TYPE        ("VAR_LOAD"))
  35. idelay_cs(
  36.     .DATAOUT        (rx_clk_in_n_d),
  37.     .C          (rxclk_div),
  38.     .CE         (1'b0),
  39.     .INC            (1'b0),
  40.     .DATAIN         (1'b0),
  41.     .IDATAIN        (~rx_clk_in_n),
  42.     .LD         (1'b1),
  43.     .LDPIPEEN       (1'b0),
  44.     .REGRST         (1'b0),
  45.     .CINVCTRL       (1'b0),
  46.     .CNTVALUEIN     ({1'b0, bt_val[4:1]}),
  47.     .CNTVALUEOUT        ());
复制代码

通过ISERDESE把时钟的n实现串并转换。
  1. ISERDESE2 #(
  2.     .DATA_WIDTH         (7),
  3.     .DATA_RATE          ("SDR"),
  4. //  .SERDES_MODE        ("MASTER"),
  5.     .IOBDELAY           ("IFD"),
  6.     .INTERFACE_TYPE     ("NETWORKING"))
  7. iserdes_cm (
  8.     .D              (1'b0),
  9.     .DDLY           (rx_clk_in_n_d),
  10.     .CE1            (1'b1),
  11.     .CE2            (1'b1),
  12.     .CLK            (rxclk),
  13.     .CLKB           (~rxclk),
  14.     .RST            (rstcserdes),
  15.     .CLKDIV         (rxclk_div),
  16.     .CLKDIVP        (1'b0),
  17.     .OCLK           (1'b0),
  18.     .OCLKB          (1'b0),
  19.     .DYNCLKSEL          (1'b0),
  20.     .DYNCLKDIVSEL       (1'b0),
  21.     .SHIFTIN1       (1'b0),
  22.     .SHIFTIN2       (1'b0),
  23.     .BITSLIP        (bslip),
  24.     .O          (),
  25.     .Q8             (),
  26.     .Q7             (clk_iserdes_data[0]),
  27.     .Q6             (clk_iserdes_data[1]),
  28.     .Q5             (clk_iserdes_data[2]),
  29.     .Q4             (clk_iserdes_data[3]),
  30.     .Q3             (clk_iserdes_data[4]),
  31.     .Q2             (clk_iserdes_data[5]),
  32.     .Q1             (clk_iserdes_data[6]),
  33.     .OFB            (),
  34.     .SHIFTOUT1      (),
  35.     .SHIFTOUT2      ());
复制代码

通过控制每次减1个tap,减少idelay延迟,并且通过ISERDESE检测时钟的跳变。当第一次跳变发生的时候,代表检测到了时钟的跳变沿,假设这个时候bt_val=05h,05h<0Bh 所以计算出时钟的0BH+05H =10H 具体代码如下:
  1. always @ (posedge rxclk_div) begin              //
  2.     clk_iserdes_data_d <= clk_iserdes_data ;
  3.     if ((clk_iserdes_data != clk_iserdes_data_d) && (clk_iserdes_data != 7'h00) && (clk_iserdes_data != 7'h7F)) begin
  4.         data_different <= 1'b1 ;
  5.     end
  6.     else begin
  7.         data_different <= 1'b0 ;
  8.     end
  9. end

  10. always @ (posedge rxclk_div) begin                      // clock delay shift state machine
  11.     not_rx_mmcm_lckd_int <= ~(mmcm_locked & idelay_rdy) ;
  12.     rstcserdes <= not_rx_mmcm_lckd_int | rst_iserdes ;
  13.     if (not_rx_mmcm_lckd_int == 1'b1) begin
  14.         scount <= 6'h00 ;
  15.         state2 <= 0 ;
  16.         state2_count <= 5'h00 ;
  17.         locked_out <= 1'b0 ;
  18.         chfoundc <= 1'b1 ;
  19.         c_delay_in <= bt_val ;                          // Start the delay line at the current bit period
  20.         rst_iserdes <= 1'b0 ;
  21.         c_loop_cnt <= 2'b00 ;
  22.     end
  23.     else begin
  24.         if (scount[5] == 1'b0) begin
  25.             scount <= scount + 6'h01 ;
  26.         end
  27.         state2_count <= state2_count + 5'h01 ;
  28.         if (chfoundc == 1'b1) begin
  29.             chfound <= 1'b0 ;
  30.         end
  31.         else if (chfound == 1'b0 && data_different == 1'b1) begin
  32.             chfound <= 1'b1 ;
  33.         end
  34.         if ((state2_count == 5'h1F && scount[5] == 1'b1)) begin
  35.             case(state2)
  36.             0   : begin                         // decrement delay and look for a change
  37.                   if (chfound == 1'b1 || (c_loop_cnt == 2'b11 && c_delay_in == 5'h00)) begin  // quit loop if we've been around a few times
  38.                     chfoundc <= 1'b1 ;
  39.                     state2 <= 1 ;
  40.                   end
  41.                   else begin
  42.                     chfoundc <= 1'b0 ;
  43.                     if (c_delay_in != 5'h00) begin          // check for underflow
  44.                         c_delay_in <= c_delay_in - 5'h01 ;
  45.                     end
  46.                     else begin
  47.                         c_delay_in <= bt_val ;
  48.                         c_loop_cnt <= c_loop_cnt + 2'b01 ;
  49.                     end
  50.                   end
  51.                   end
  52.             1   : begin                         // add half a bit period using input information
  53.                   state2 <= 2 ;
  54.                   if (c_delay_in < {1'b0, bt_val[4:1]}) begin       // choose the lowest delay value to minimise jitter
  55.                     c_delay_in_target <= c_delay_in + {1'b0, bt_val[4:1]} ;
  56.                   end
  57.                   else begin
  58.                     c_delay_in_target <= c_delay_in - {1'b0, bt_val[4:1]} ;
  59.                   end
  60.                   end
  61.             2   : begin
  62.                   if (c_delay_in == c_delay_in_target) begin
  63.                     state2 <= 3 ;
  64.                   end
  65.                   else begin
  66.                     if (c_delay_in_ud == 1'b1) begin        // move gently to end position to stop MMCM unlocking
  67.                         c_delay_in <= c_delay_in + 5'h01 ;
  68.                         c_delay_in_ud <= 1'b1 ;
  69.                     end
  70.                     else begin
  71.                         c_delay_in <= c_delay_in - 5'h01 ;
  72.                         c_delay_in_ud <= 1'b0 ;
  73.                     end
  74.                   end
  75.                   end
  76.             3   : begin rst_iserdes <= 1'b1 ; state2 <= 4 ; end     // remove serdes reset
  77.             default : begin                         // issue locked out signal
  78.                   rst_iserdes <= 1'b0 ;  locked_out <= 1'b1 ;
  79.                   end
  80.             endcase
  81.         end
  82.     end
  83. end
复制代码

如果解出的时钟值不是1100001或者1100011就需要用到Bslip调整,每次设置Bslip调整一次。
  1. // Bitslip state machine

  2. always @ (posedge rxclk_div)
  3. begin
  4. if (locked_out == 1'b0) begin
  5.     bslip <= 1'b0 ;
  6.     bsstate <= 1 ;
  7.     enable <= 1'b0 ;
  8.     bcount <= 4'h0 ;
  9.     bs_finished <= 1'b0 ;
  10.     not_bs_finished <= 1'b1 ;
  11. end
  12. else begin
  13.     enable <= 1'b1 ;
  14.     if (enable == 1'b1) begin
  15.         if (clk_iserdes_data != 7'b1100001) begin flag1 <= 1'b1 ; end else begin flag1 <= 1'b0 ; end
  16.         if (clk_iserdes_data != 7'b1100011) begin flag2 <= 1'b1 ; end else begin flag2 <= 1'b0 ; end
  17.             if (bsstate == 0) begin
  18.             if (flag1 == 1'b1 && flag2 == 1'b1) begin
  19.                     bslip <= 1'b1 ;                     // bitslip needed
  20.                     bsstate <= 1 ;
  21.                 end
  22.                 else begin
  23.                     bs_finished <= 1'b1 ;                   // bitslip done
  24.                     not_bs_finished <= 1'b0 ;               // bitslip done
  25.                 end
  26.         end
  27.         else if (bsstate == 1) begin
  28.                 bslip <= 1'b0 ;
  29.                 bcount <= bcount + 4'h1 ;
  30.             if (bcount == 4'hF) begin
  31.                     bsstate <= 0 ;
  32.                 end
  33.         end
  34.     end
  35. end
  36. end
复制代码

现在结合下仿真结果看下,在下图的箭头所示,接收的clk_data是7bit时钟数据,通过调整c_delay_in值调整idelay每次减1个tap,直到clk_data发生改变,可以看到这个值是0b整好是和中间值相等。这个时候状态机state2也会进入状态1,这个时候计算出c_delay_in_target=0,这个就是我们需要延迟的时间,可以看到需要延迟的时间为0,这种情况非常理想,如果实际程序跑起来一般不会是0,因为我们这里只做了RTL级行为仿真。之后进入状态2.
347f2311d2f74190b410aa0a85b3ea29.jpg
在状态2中继续调整c_delay_in直到和c_delay_in_target相等,如下图,可以看到clk_data=1100001b 是占比3:4的时钟就恢复出来了。
1b4b7b3a9f7548aeb9443276395bcf8b.jpg
4.4.2SDR ISERDESE数据恢复
再看SDR数据部分的串并接收,先看下面源码,ISERDESE的应用,关键还是如何获取的idelay的延迟参数。这里有delay_controller_wrap.v中的代码控制数据部分的延迟参数。
  1. generate
  2. for (i = 0 ; i <= D-1 ; i = i+1)
  3. begin : loop3

  4. delay_controller_wrap # (
  5.     .S          (7))
  6. dc_inst (
  7.     .m_datain       (mdataout[7*i+6:7*i]),
  8.     .s_datain       (sdataout[7*i+6:7*i]),
  9.     .enable_phase_detector  (enable_phase_detector),
  10.     .enable_monitor     (enable_monitor),
  11.     .reset          (not_bs_finished),
  12.     .clk            (rxclk_div),
  13.     .c_delay_in     ({1'b0, bt_val[4:1]}),
  14.     .m_delay_out        (m_delay_val_in[5*i+4:5*i]),
  15.     .s_delay_out        (s_delay_val_in[5*i+4:5*i]),
  16.     .data_out       (mdataoutd[7*i+6:7*i]),
  17.     .bt_val         (bt_val),
  18.     .del_mech       (1'b0),
  19.     .m_delay_1hot       (m_delay_1hot[32*i+31:32*i]),
  20.     .results        (eye_info[32*i+31:32*i])) ;

  21. // Data bit Receivers

  22. IBUFDS_DIFF_OUT #(
  23.     .DIFF_TERM      (DIFF_TERM))
  24. data_in (
  25.     .I              (datain_p[i]),
  26.     .IB             (datain_n[i]),
  27.     .O              (rx_data_in_p[i]),
  28.     .OB                 (rx_data_in_n[i]));

  29. assign rx_data_in_m[i] = rx_data_in_p[i]  ^ RX_SWAP_MASK[i] ;
  30. assign rx_data_in_s[i] = ~rx_data_in_n[i] ^ RX_SWAP_MASK[i] ;

  31. IDELAYE2 #(
  32.     .HIGH_PERFORMANCE_MODE  (HIGH_PERFORMANCE_MODE),
  33.         .IDELAY_VALUE       (0),
  34.         .DELAY_SRC      ("IDATAIN"),
  35.         .IDELAY_TYPE        ("VAR_LOAD"))
  36. idelay_m(
  37.     .DATAOUT        (rx_data_in_md[i]),
  38.     .C          (rxclk_div),
  39.     .CE         (1'b0),
  40.     .INC            (1'b0),
  41.     .DATAIN         (1'b0),
  42.     .IDATAIN        (rx_data_in_m[i]),
  43.     .LD         (1'b1),
  44.     .LDPIPEEN       (1'b0),
  45.     .REGRST         (1'b0),
  46.     .CINVCTRL       (1'b0),
  47.     .CNTVALUEIN     (m_delay_val_in[5*i+4:5*i]),
  48.     .CNTVALUEOUT        ());

  49. ISERDESE2 #(
  50.     .DATA_WIDTH         (7),
  51.     .DATA_RATE          ("SDR"),
  52.     .SERDES_MODE        ("MASTER"),
  53.     .IOBDELAY           ("IFD"),
  54.     .INTERFACE_TYPE     ("NETWORKING"))
  55. iserdes_m (
  56.     .D              (1'b0),
  57.     .DDLY           (rx_data_in_md[i]),
  58.     .CE1            (1'b1),
  59.     .CE2            (1'b1),
  60.     .CLK            (rxclk),
  61.     .CLKB           (~rxclk),
  62.     .RST            (rst_iserdes),
  63.     .CLKDIV         (rxclk_div),
  64.     .CLKDIVP        (1'b0),
  65.     .OCLK           (1'b0),
  66.     .OCLKB          (1'b0),
  67.     .DYNCLKSEL          (1'b0),
  68.     .DYNCLKDIVSEL       (1'b0),
  69.     .SHIFTIN1       (1'b0),
  70.     .SHIFTIN2       (1'b0),
  71.     .BITSLIP        (bslip),
  72.     .O          (),
  73.     .Q8             (),
  74.     .Q7             (mdataout[7*i+0]),
  75.     .Q6             (mdataout[7*i+1]),
  76.     .Q5             (mdataout[7*i+2]),
  77.     .Q4             (mdataout[7*i+3]),
  78.     .Q3             (mdataout[7*i+4]),
  79.     .Q2             (mdataout[7*i+5]),
  80.     .Q1             (mdataout[7*i+6]),
  81.     .OFB            (),
  82.     .SHIFTOUT1      (),
  83.     .SHIFTOUT2      ());

  84. IDELAYE2 #(
  85.     .HIGH_PERFORMANCE_MODE  (HIGH_PERFORMANCE_MODE),
  86.         .IDELAY_VALUE       (0),
  87.         .DELAY_SRC      ("IDATAIN"),
  88.         .IDELAY_TYPE        ("VAR_LOAD"))
  89. idelay_s(
  90.     .DATAOUT        (rx_data_in_sd[i]),
  91.     .C          (rxclk_div),
  92.     .CE         (1'b0),
  93.     .INC            (1'b0),
  94.     .DATAIN         (1'b0),
  95.     .IDATAIN        (rx_data_in_s[i]),
  96.     .LD         (1'b1),
  97.     .LDPIPEEN       (1'b0),
  98.     .REGRST         (1'b0),
  99.     .CINVCTRL       (1'b0),
  100.     .CNTVALUEIN     (s_delay_val_in[5*i+4:5*i]),
  101.     .CNTVALUEOUT        ());

  102. ISERDESE2 #(
  103.     .DATA_WIDTH         (7),
  104.     .DATA_RATE          ("SDR"),
  105. //  .SERDES_MODE        ("SLAVE"),
  106.     .IOBDELAY           ("IFD"),
  107.     .INTERFACE_TYPE     ("NETWORKING"))
  108. iserdes_s (
  109.     .D              (1'b0),
  110.     .DDLY           (rx_data_in_sd[i]),
  111.     .CE1            (1'b1),
  112.     .CE2            (1'b1),
  113.     .CLK            (rxclk),
  114.     .CLKB           (~rxclk),
  115.     .RST            (rst_iserdes),
  116.     .CLKDIV         (rxclk_div),
  117.     .CLKDIVP        (1'b0),
  118.     .OCLK           (1'b0),
  119.     .OCLKB          (1'b0),
  120.     .DYNCLKSEL          (1'b0),
  121.     .DYNCLKDIVSEL       (1'b0),
  122.     .SHIFTIN1       (1'b0),
  123.     .SHIFTIN2       (1'b0),
  124.     .BITSLIP        (bslip),
  125.     .O          (),
  126.     .Q8             (),
  127.     .Q7             (sdataout[7*i+0]),
  128.     .Q6             (sdataout[7*i+1]),
  129.     .Q5             (sdataout[7*i+2]),
  130.     .Q4             (sdataout[7*i+3]),
  131.     .Q3             (sdataout[7*i+4]),
  132.     .Q2             (sdataout[7*i+5]),
  133.     .Q1             (sdataout[7*i+6]),
  134.     .OFB            (),
  135.     .SHIFTOUT1      (),
  136.     .SHIFTOUT2      ());

  137. for (j = 0 ; j <= 6 ; j = j+1) begin : loop1            // Assign data bits to correct serdes according to required format
  138.     if (DATA_FORMAT == "PER_CLOCK") begin
  139.         assign rx_data[D*j+i] = mdataoutd[7*i+j] ;
  140.     end
  141.     else begin
  142.         assign rx_data[7*i+j] = mdataoutd[7*i+j] ;
  143.     end
  144. end
  145. end
  146. endgenerate
复制代码

delay_controller_wrap.v中的代码看起来比较难以理解,先看下仿真结果。可以看到s_delay_out的值为00h,m_delay_out的值为0bh.至于这个delay_controller_wrap.v中其他一些功能都没用到。
8cee32b1831e440ba57dda9e1db8a5bf.jpg
4.5DDR ISERDESE使用4.5.1DDR ISERDESE时钟恢复
前面分析SDR ISERDESE就花费了不少篇幅,DDR ISERDESE要更难一些,但是应用上更广泛一些。有了SDR ISERDESE部分的详细分析,DDR ISERDESE就是难度大了分析起来也简单多了。由于无法直接使用1个ISERDESE实现1:7的转换。
首先看下top5x2_7to1_ddr_rx.v中的代码,这里的时钟是151.5M, 151.5M *7=1060Mbps,bit_rate_value=1060。
  1. 10n_x_serdes_1_to_7_mmcm_idelay_ddr #(
  2.     .N          (2),
  3.     .SAMPL_CLOCK        ("BUFIO"),
  4.     .INTER_CLOCK        ("BUF_R"),
  5.     .PIXEL_CLOCK        ("BUF_G"),
  6.     .USE_PLL        ("FALSE"),
  7.     .HIGH_PERFORMANCE_MODE  ("FALSE"),
  8.         .D          (D),                // Number of data lines
  9.         .REF_FREQ       (200.0),            // Set idelay control reference frequency
  10.         .CLKIN_PERIOD       (6.600),            // Set input clock period
  11.         .MMCM_MODE      (1),                // Parameter to set multiplier for MMCM to get VCO in correct operating range. 1 multiplies input clock by 7, 2 multiplies clock by 14, etc
  12.     .DIFF_TERM      ("TRUE"),
  13.     .DATA_FORMAT        ("PER_CLOCK"))          // PER_CLOCK or PER_CHANL data formatting
  14. rx0 (                     
  15.     .clkin_p        (clkin_p),
  16.     .clkin_n        (clkin_n),
  17.     .datain_p           (datain_p),
  18.     .datain_n           (datain_n),
  19.     .enable_phase_detector  (1'b1),             // enable phase detector operation
  20.     .enable_monitor     (1'b0),             // enables data eye monitoring
  21.     .dcd_correct        (1'b0),             // enables clock duty cycle correction
  22.     .rxclk          (),
  23.     .rxclk_d4           (),             // intermediate clock, use with data monitoring logic
  24.     .idelay_rdy     (delay_ready),
  25.     .pixel_clk      (rx_pixel_clk),
  26.     .reset          (reset),
  27.     .rx_mmcm_lckd       (rx_mmcm_lckd),
  28.     .rx_mmcm_lckdps     (rx_mmcm_lckdps),
  29.     .rx_mmcm_lckdpsbs   (rx_mmcm_lckdpsbs),
  30.     .clk_data       (),
  31.     .rx_data        (rxdall),
  32.     .bit_rate_value     (16'h1050),         // required bit rate value in BCD
  33.     .bit_time_value     (),
  34.     .status         (),
  35.     .eye_info       (),             // data eye monitor per line
  36.     .m_delay_1hot       (),             // sample point monitor per line
  37.     .debug          ()) ;               // debug bus
复制代码

bit_rate_value=1060,idelay的参考时钟是200M,可以得出delay的tap最大值为0DH,如果参考时钟是300M,中有dcd_correct的功能,笔者暂时不清楚如何使用也就不分析,我们这里的时钟是200M
  1. if (REF_FREQ < 210.0) begin
  2.   always @ (bit_rate_value) begin                       // Generate tap number to be used for input bit rate (200 MHz ref clock)
  3.     if      (bit_rate_value > 16'h1984) begin bt_val <= 5'h07 ; del_mech <= 1'b1 ; end
  4.     else if (bit_rate_value > 16'h1717) begin bt_val <= 5'h08 ; del_mech <= 1'b1 ; end
  5.     else if (bit_rate_value > 16'h1514) begin bt_val <= 5'h09 ; del_mech <= 1'b1 ; end
  6.     else if (bit_rate_value > 16'h1353) begin bt_val <= 5'h0A ; del_mech <= 1'b1 ; end
  7.     else if (bit_rate_value > 16'h1224) begin bt_val <= 5'h0B ; del_mech <= 1'b1 ; end
  8.     else if (bit_rate_value > 16'h1117) begin bt_val <= 5'h0C ; del_mech <= 1'b1 ; end
  9.     else if (bit_rate_value > 16'h1027) begin bt_val <= 5'h0D ; del_mech <= 1'b1 ; end
  10.     else if (bit_rate_value > 16'h0951) begin bt_val <= 5'h0E ; del_mech <= 1'b1 ; end
  11.     else if (bit_rate_value > 16'h0885) begin bt_val <= 5'h0F ; del_mech <= 1'b1 ; end
  12.     else if (bit_rate_value > 16'h0828) begin bt_val <= 5'h10 ; del_mech <= 1'b1 ; end
  13.     else if (bit_rate_value > 16'h0778) begin bt_val <= 5'h11 ; del_mech <= 1'b1 ; end
  14.     else if (bit_rate_value > 16'h0733) begin bt_val <= 5'h12 ; del_mech <= 1'b1 ; end
  15.     else if (bit_rate_value > 16'h0694) begin bt_val <= 5'h13 ; del_mech <= 1'b1 ; end
  16.     else if (bit_rate_value > 16'h0658) begin bt_val <= 5'h14 ; del_mech <= 1'b1 ; end
  17.     else if (bit_rate_value > 16'h0626) begin bt_val <= 5'h15 ; del_mech <= 1'b1 ; end
  18.     else if (bit_rate_value > 16'h0597) begin bt_val <= 5'h16 ; del_mech <= 1'b0 ; end
  19.     else if (bit_rate_value > 16'h0570) begin bt_val <= 5'h17 ; del_mech <= 1'b0 ; end
  20.     else if (bit_rate_value > 16'h0546) begin bt_val <= 5'h18 ; del_mech <= 1'b0 ; end
  21.     else if (bit_rate_value > 16'h0524) begin bt_val <= 5'h19 ; del_mech <= 1'b0 ; end
  22.     else if (bit_rate_value > 16'h0503) begin bt_val <= 5'h1A ; del_mech <= 1'b0 ; end
  23.     else if (bit_rate_value > 16'h0484) begin bt_val <= 5'h1B ; del_mech <= 1'b0 ; end
  24.     else if (bit_rate_value > 16'h0466) begin bt_val <= 5'h1C ; del_mech <= 1'b0 ; end
  25.     else if (bit_rate_value > 16'h0450) begin bt_val <= 5'h1D ; del_mech <= 1'b0 ; end
  26.     else if (bit_rate_value > 16'h0435) begin bt_val <= 5'h1E ; del_mech <= 1'b0 ; end
  27.     else                                begin bt_val <= 5'h1F ; del_mech <= 1'b0 ; end      // min bit rate 420 Mbps
  28.   end
  29. end else begin
  30.   always @ (bit_rate_value or dcd_correct) begin                        // Generate tap number to be used for input bit rate (300 MHz ref clock)
  31.     if      ((bit_rate_value > 16'h2030 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1845 && dcd_correct == 1'b1)) begin bt_val <= 5'h0A ; del_mech <= 1'b1 ; end
  32.     else if ((bit_rate_value > 16'h1836 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1669 && dcd_correct == 1'b1)) begin bt_val <= 5'h0B ; del_mech <= 1'b1 ; end
  33.     else if ((bit_rate_value > 16'h1675 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1523 && dcd_correct == 1'b1)) begin bt_val <= 5'h0C ; del_mech <= 1'b1 ; end
  34.     else if ((bit_rate_value > 16'h1541 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1401 && dcd_correct == 1'b1)) begin bt_val <= 5'h0D ; del_mech <= 1'b1 ; end
  35.     else if ((bit_rate_value > 16'h1426 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1297 && dcd_correct == 1'b1)) begin bt_val <= 5'h0E ; del_mech <= 1'b1 ; end
  36.     else if ((bit_rate_value > 16'h1328 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1207 && dcd_correct == 1'b1)) begin bt_val <= 5'h0F ; del_mech <= 1'b1 ; end
  37.     else if ((bit_rate_value > 16'h1242 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1129 && dcd_correct == 1'b1)) begin bt_val <= 5'h10 ; del_mech <= 1'b1 ; end
  38.     else if ((bit_rate_value > 16'h1167 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1061 && dcd_correct == 1'b1)) begin bt_val <= 5'h11 ; del_mech <= 1'b1 ; end
  39.     else if ((bit_rate_value > 16'h1100 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0999 && dcd_correct == 1'b1)) begin bt_val <= 5'h12 ; del_mech <= 1'b1 ; end
  40.     else if ((bit_rate_value > 16'h1040 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0946 && dcd_correct == 1'b1)) begin bt_val <= 5'h13 ; del_mech <= 1'b1 ; end
  41.     else if ((bit_rate_value > 16'h0987 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0897 && dcd_correct == 1'b1)) begin bt_val <= 5'h14 ; del_mech <= 1'b1 ; end
  42.     else if ((bit_rate_value > 16'h0939 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0853 && dcd_correct == 1'b1)) begin bt_val <= 5'h15 ; del_mech <= 1'b1 ; end
  43.     else if ((bit_rate_value > 16'h0895 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0814 && dcd_correct == 1'b1)) begin bt_val <= 5'h16 ; del_mech <= 1'b0 ; end
  44.     else if ((bit_rate_value > 16'h0855 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0777 && dcd_correct == 1'b1)) begin bt_val <= 5'h17 ; del_mech <= 1'b0 ; end
  45.     else if ((bit_rate_value > 16'h0819 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0744 && dcd_correct == 1'b1)) begin bt_val <= 5'h18 ; del_mech <= 1'b0 ; end
  46.     else if ((bit_rate_value > 16'h0785 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0714 && dcd_correct == 1'b1)) begin bt_val <= 5'h19 ; del_mech <= 1'b0 ; end
  47.     else if ((bit_rate_value > 16'h0754 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0686 && dcd_correct == 1'b1)) begin bt_val <= 5'h1A ; del_mech <= 1'b0 ; end
  48.     else if ((bit_rate_value > 16'h0726 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0660 && dcd_correct == 1'b1)) begin bt_val <= 5'h1B ; del_mech <= 1'b0 ; end
  49.     else if ((bit_rate_value > 16'h0700 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0636 && dcd_correct == 1'b1)) begin bt_val <= 5'h1C ; del_mech <= 1'b0 ; end
  50.     else if ((bit_rate_value > 16'h0675 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0614 && dcd_correct == 1'b1)) begin bt_val <= 5'h1D ; del_mech <= 1'b0 ; end
  51.     else if ((bit_rate_value > 16'h0652 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0593 && dcd_correct == 1'b1)) begin bt_val <= 5'h1E ; del_mech <= 1'b0 ; end
  52.     else                                                                           begin bt_val <= 5'h1F ;   del_mech <= 1'b0 ; end     // min bit rate 631 Mbps
  53.   end
  54. end
复制代码

先取出bt_val的中间值,0Dh的中间值是06h。把差分时钟的n延迟06h,把差分时钟的p延迟0Dh。代码如下,由于ddr模式没有直接实现1:7转换,所以先用iserdes实现了1:4转换,再用gearbox实现4:7的转换。
  1. if (REF_FREQ < 210.0) begin
  2.   always @ (bit_rate_value) begin                       // Generate tap number to be used for input bit rate (200 MHz ref clock)
  3.     if      (bit_rate_value > 16'h1984) begin bt_val <= 5'h07 ; del_mech <= 1'b1 ; end
  4.     else if (bit_rate_value > 16'h1717) begin bt_val <= 5'h08 ; del_mech <= 1'b1 ; end
  5.     else if (bit_rate_value > 16'h1514) begin bt_val <= 5'h09 ; del_mech <= 1'b1 ; end
  6.     else if (bit_rate_value > 16'h1353) begin bt_val <= 5'h0A ; del_mech <= 1'b1 ; end
  7.     else if (bit_rate_value > 16'h1224) begin bt_val <= 5'h0B ; del_mech <= 1'b1 ; end
  8.     else if (bit_rate_value > 16'h1117) begin bt_val <= 5'h0C ; del_mech <= 1'b1 ; end
  9.     else if (bit_rate_value > 16'h1027) begin bt_val <= 5'h0D ; del_mech <= 1'b1 ; end
  10.     else if (bit_rate_value > 16'h0951) begin bt_val <= 5'h0E ; del_mech <= 1'b1 ; end
  11.     else if (bit_rate_value > 16'h0885) begin bt_val <= 5'h0F ; del_mech <= 1'b1 ; end
  12.     else if (bit_rate_value > 16'h0828) begin bt_val <= 5'h10 ; del_mech <= 1'b1 ; end
  13.     else if (bit_rate_value > 16'h0778) begin bt_val <= 5'h11 ; del_mech <= 1'b1 ; end
  14.     else if (bit_rate_value > 16'h0733) begin bt_val <= 5'h12 ; del_mech <= 1'b1 ; end
  15.     else if (bit_rate_value > 16'h0694) begin bt_val <= 5'h13 ; del_mech <= 1'b1 ; end
  16.     else if (bit_rate_value > 16'h0658) begin bt_val <= 5'h14 ; del_mech <= 1'b1 ; end
  17.     else if (bit_rate_value > 16'h0626) begin bt_val <= 5'h15 ; del_mech <= 1'b1 ; end
  18.     else if (bit_rate_value > 16'h0597) begin bt_val <= 5'h16 ; del_mech <= 1'b0 ; end
  19.     else if (bit_rate_value > 16'h0570) begin bt_val <= 5'h17 ; del_mech <= 1'b0 ; end
  20.     else if (bit_rate_value > 16'h0546) begin bt_val <= 5'h18 ; del_mech <= 1'b0 ; end
  21.     else if (bit_rate_value > 16'h0524) begin bt_val <= 5'h19 ; del_mech <= 1'b0 ; end
  22.     else if (bit_rate_value > 16'h0503) begin bt_val <= 5'h1A ; del_mech <= 1'b0 ; end
  23.     else if (bit_rate_value > 16'h0484) begin bt_val <= 5'h1B ; del_mech <= 1'b0 ; end
  24.     else if (bit_rate_value > 16'h0466) begin bt_val <= 5'h1C ; del_mech <= 1'b0 ; end
  25.     else if (bit_rate_value > 16'h0450) begin bt_val <= 5'h1D ; del_mech <= 1'b0 ; end
  26.     else if (bit_rate_value > 16'h0435) begin bt_val <= 5'h1E ; del_mech <= 1'b0 ; end
  27.     else                                begin bt_val <= 5'h1F ; del_mech <= 1'b0 ; end      // min bit rate 420 Mbps
  28.   end
  29. end else begin
  30.   always @ (bit_rate_value or dcd_correct) begin                        // Generate tap number to be used for input bit rate (300 MHz ref clock)
  31.     if      ((bit_rate_value > 16'h2030 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1845 && dcd_correct == 1'b1)) begin bt_val <= 5'h0A ; del_mech <= 1'b1 ; end
  32.     else if ((bit_rate_value > 16'h1836 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1669 && dcd_correct == 1'b1)) begin bt_val <= 5'h0B ; del_mech <= 1'b1 ; end
  33.     else if ((bit_rate_value > 16'h1675 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1523 && dcd_correct == 1'b1)) begin bt_val <= 5'h0C ; del_mech <= 1'b1 ; end
  34.     else if ((bit_rate_value > 16'h1541 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1401 && dcd_correct == 1'b1)) begin bt_val <= 5'h0D ; del_mech <= 1'b1 ; end
  35.     else if ((bit_rate_value > 16'h1426 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1297 && dcd_correct == 1'b1)) begin bt_val <= 5'h0E ; del_mech <= 1'b1 ; end
  36.     else if ((bit_rate_value > 16'h1328 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1207 && dcd_correct == 1'b1)) begin bt_val <= 5'h0F ; del_mech <= 1'b1 ; end
  37.     else if ((bit_rate_value > 16'h1242 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1129 && dcd_correct == 1'b1)) begin bt_val <= 5'h10 ; del_mech <= 1'b1 ; end
  38.     else if ((bit_rate_value > 16'h1167 && dcd_correct == 1'b0) || (bit_rate_value > 16'h1061 && dcd_correct == 1'b1)) begin bt_val <= 5'h11 ; del_mech <= 1'b1 ; end
  39.     else if ((bit_rate_value > 16'h1100 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0999 && dcd_correct == 1'b1)) begin bt_val <= 5'h12 ; del_mech <= 1'b1 ; end
  40.     else if ((bit_rate_value > 16'h1040 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0946 && dcd_correct == 1'b1)) begin bt_val <= 5'h13 ; del_mech <= 1'b1 ; end
  41.     else if ((bit_rate_value > 16'h0987 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0897 && dcd_correct == 1'b1)) begin bt_val <= 5'h14 ; del_mech <= 1'b1 ; end
  42.     else if ((bit_rate_value > 16'h0939 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0853 && dcd_correct == 1'b1)) begin bt_val <= 5'h15 ; del_mech <= 1'b1 ; end
  43.     else if ((bit_rate_value > 16'h0895 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0814 && dcd_correct == 1'b1)) begin bt_val <= 5'h16 ; del_mech <= 1'b0 ; end
  44.     else if ((bit_rate_value > 16'h0855 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0777 && dcd_correct == 1'b1)) begin bt_val <= 5'h17 ; del_mech <= 1'b0 ; end
  45.     else if ((bit_rate_value > 16'h0819 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0744 && dcd_correct == 1'b1)) begin bt_val <= 5'h18 ; del_mech <= 1'b0 ; end
  46.     else if ((bit_rate_value > 16'h0785 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0714 && dcd_correct == 1'b1)) begin bt_val <= 5'h19 ; del_mech <= 1'b0 ; end
  47.     else if ((bit_rate_value > 16'h0754 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0686 && dcd_correct == 1'b1)) begin bt_val <= 5'h1A ; del_mech <= 1'b0 ; end
  48.     else if ((bit_rate_value > 16'h0726 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0660 && dcd_correct == 1'b1)) begin bt_val <= 5'h1B ; del_mech <= 1'b0 ; end
  49.     else if ((bit_rate_value > 16'h0700 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0636 && dcd_correct == 1'b1)) begin bt_val <= 5'h1C ; del_mech <= 1'b0 ; end
  50.     else if ((bit_rate_value > 16'h0675 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0614 && dcd_correct == 1'b1)) begin bt_val <= 5'h1D ; del_mech <= 1'b0 ; end
  51.     else if ((bit_rate_value > 16'h0652 && dcd_correct == 1'b0) || (bit_rate_value > 16'h0593 && dcd_correct == 1'b1)) begin bt_val <= 5'h1E ; del_mech <= 1'b0 ; end
  52.     else                                                                           begin bt_val <= 5'h1F ;   del_mech <= 1'b0 ; end     // min bit rate 631 Mbps
  53.   end
  54. end
复制代码

4.5.2DDR ISERDESE数据恢复
Gearbox实现4:7转换,代码在gearbox_4_to_7.v中Gearbox中使用到了BRAM
  1. module gearbox_4_to_7 (input_clock, output_clock, datain, reset, jog, reset_out, dataout) ;

  2. parameter integer       D = 8 ;         // Parameter to set the number of data lines  

  3. input               input_clock ;       // high speed clock input
  4. input               output_clock ;      // low speed clock input
  5. input       [D*4-1:0]   datain ;        // data inputs
  6. input               reset ;         // Reset line
  7. input               jog ;           // jog input, slips by 4 bits
  8. output  reg [1:0]       reset_out ;     // reset out signal
  9. output  reg [D*7-1:0]   dataout ;       // data outputs
  10.                     
  11. reg [3:0]       read_addra ;            
  12. reg [3:0]       read_addrb ;            
  13. reg [3:0]       read_addrc ;            
  14. reg [3:0]       write_addr ;            
  15. reg         read_enable ;   
  16. reg         read_enable_dom_ch ;   
  17. wire    [D*4-1:0]   ramouta ;           
  18. wire    [D*4-1:0]   ramoutb ;           
  19. wire    [D*4-1:0]   ramoutc ;           
  20. reg         local_reset ;   
  21. reg         local_reset_dom_ch ;   
  22. reg [1:0]       mux ;      
  23. wire    [D*4-1:0]   dummy ;         
  24. reg         jog_int ;   
  25. reg         rst_int ;   
  26. genvar i ;

  27. always @ (posedge input_clock) begin                // generate local sync reset
  28.     if (reset == 1'b1) begin
  29.         local_reset <= 1'b1 ;
  30.         reset_out[0] <= 1'b1 ;
  31.     end else begin
  32.         local_reset <= 1'b0 ;
  33.         reset_out[0] <= 1'b0 ;
  34.     end
  35. end

  36. always @ (posedge input_clock) begin                // Gearbox input - 4 bit data at input clock frequency
  37.     if (local_reset == 1'b1) begin
  38.         write_addr <= 4'h0 ;
  39.         read_enable <= 1'b0 ;
  40.     end
  41.     else begin
  42.         if (write_addr == 4'hD) begin
  43.             write_addr <= 4'h0 ;
  44.         end
  45.         else begin
  46.             write_addr <= write_addr + 4'h1 ;
  47.         end
  48.         if (write_addr == 4'h3) begin
  49.             read_enable <= 1'b1 ;
  50.         end
  51.     end
  52. end

  53. always @ (posedge output_clock) begin   
  54.     read_enable_dom_ch <= read_enable ;
  55.     local_reset_dom_ch <= local_reset ;
  56. end

  57. always @ (posedge output_clock) begin               // Gearbox output - 10 bit data at output clock frequency
  58.     reset_out[1] <= rst_int ;
  59.     if (local_reset_dom_ch == 1'b1 || read_enable_dom_ch == 1'b0) begin
  60.         rst_int <= 1'b1 ;
  61.     end
  62.     else begin
  63.         rst_int <= 1'b0 ;
  64.     end
  65.     if (reset_out[1] == 1'b1) begin
  66.         read_addra <= 4'h0 ;
  67.         read_addrb <= 4'h1 ;
  68.         read_addrc <= 4'h2 ;
  69.         jog_int <= 1'b0 ;
  70.     end
  71.     else begin
  72.         case (jog_int)
  73.         1'b0 : begin
  74.             case (read_addra)
  75.             4'h0    : begin read_addra <= 4'h1 ; read_addrb <= 4'h2 ; read_addrc <= 4'h3 ; mux <= 2'h1 ; end
  76.             4'h1    : begin read_addra <= 4'h3 ; read_addrb <= 4'h4 ; read_addrc <= 4'h5 ; mux <= 2'h2 ; end
  77.             4'h3    : begin read_addra <= 4'h5 ; read_addrb <= 4'h6 ; read_addrc <= 4'h7 ; mux <= 2'h3 ; end
  78.             4'h5    : begin read_addra <= 4'h7 ; read_addrb <= 4'h8 ; read_addrc <= 4'h9 ; mux <= 2'h0 ; end
  79.             4'h7    : begin read_addra <= 4'h8 ; read_addrb <= 4'h9 ; read_addrc <= 4'hA ; mux <= 2'h1 ; end
  80.             4'h8    : begin read_addra <= 4'hA ; read_addrb <= 4'hB ; read_addrc <= 4'hC ; mux <= 2'h2 ; end
  81.             4'hA    : begin read_addra <= 4'hC ; read_addrb <= 4'hD ; read_addrc <= 4'hD ; mux <= 2'h3 ; jog_int <= jog ; end
  82.             default : begin read_addra <= 4'h0 ; read_addrb <= 4'h1 ; read_addrc <= 4'h2 ; mux <= 2'h0 ; end
  83.             endcase
  84.         end
  85.         1'b1 : begin
  86.             case (read_addra)
  87.             4'h1    : begin read_addra <= 4'h2 ; read_addrb <= 4'h3 ; read_addrc <= 4'h4 ; mux <= 2'h1 ; end
  88.             4'h2    : begin read_addra <= 4'h4 ; read_addrb <= 4'h5 ; read_addrc <= 4'h6 ; mux <= 2'h2 ; end
  89.             4'h4    : begin read_addra <= 4'h6 ; read_addrb <= 4'h7 ; read_addrc <= 4'h8 ; mux <= 2'h3 ; end
  90.             4'h6    : begin read_addra <= 4'h8 ; read_addrb <= 4'h9 ; read_addrc <= 4'hA ; mux <= 2'h0 ; end
  91.             4'h8    : begin read_addra <= 4'h9 ; read_addrb <= 4'hA ; read_addrc <= 4'hB ; mux <= 2'h1 ; end
  92.             4'h9    : begin read_addra <= 4'hB ; read_addrb <= 4'hC ; read_addrc <= 4'hD ; mux <= 2'h2 ; end
  93.             4'hB    : begin read_addra <= 4'hD ; read_addrb <= 4'h0 ; read_addrc <= 4'h1 ; mux <= 2'h3 ; jog_int <= jog ; end
  94.             default : begin read_addra <= 4'h1 ; read_addrb <= 4'h2 ; read_addrc <= 4'h3 ; mux <= 2'h0 ; end
  95.             endcase
  96.         end
  97.         endcase
  98.     end
  99. end

  100. generate
  101. for (i = 0 ; i <= D-1 ; i = i+1)
  102. begin : loop0

  103. always @ (posedge output_clock) begin
  104.     case (mux)
  105.     2'h0    : dataout[7*i+6:7*i] <= {                      ramoutb[4*i+2:4*i+0], ramouta[4*i+3:4*i+0]} ;
  106.     2'h1    : dataout[7*i+6:7*i] <= {ramoutc[4*i+1:4*i+0], ramoutb[4*i+3:4*i+0], ramouta[4*i+3]} ;   
  107.     2'h2    : dataout[7*i+6:7*i] <= {ramoutc[4*i+0],       ramoutb[4*i+3:4*i+0], ramouta[4*i+3:4*i+2]} ;
  108.     default : dataout[7*i+6:7*i] <= {                      ramoutb[4*i+3:4*i+0], ramouta[4*i+3:4*i+1]} ;
  109.     endcase
  110. end

  111. end
  112. endgenerate
  113.                     
  114. // Data gearboxes

  115. generate
  116. for (i = 0 ; i <= D*2-1 ; i = i+1)
  117. begin : loop2

  118. RAM32M ram_inst (
  119.     .DOA    (ramouta[2*i+1:2*i]),
  120.     .DOB    (ramoutb[2*i+1:2*i]),
  121.     .DOC    (ramoutc[2*i+1:2*i]),
  122.     .DOD    (dummy[2*i+1:2*i]),
  123.     .ADDRA  ({1'b0, read_addra}),
  124.     .ADDRB  ({1'b0, read_addrb}),
  125.     .ADDRC  ({1'b0, read_addrc}),
  126.     .ADDRD  ({1'b0, write_addr}),
  127.     .DIA    (datain[2*i+1:2*i]),
  128.     .DIB    (datain[2*i+1:2*i]),
  129.     .DIC    (datain[2*i+1:2*i]),
  130.     .DID    (dummy[2*i+1:2*i]),
  131.     .WE     (1'b1),
  132.     .WCLK   (input_clock));

  133. end
  134. endgenerate

  135. endmodule
复制代码

相对于SDR模式下只要完成1:7的时钟设计,对于DDR需要完成rxclk:rxclk_d4:pixel_clk=3.5:1.75:1的时钟设计。rxclk:rxclk_d4是完成1:4的转换。然后rxclk_d4:pixel_clk完成4:7的转换。
通过控制每次减1个tap,减少idelay延迟,并且通过ISERDESE检测时钟的跳变。当第一次跳变发生的时候,代表检测到了时钟的跳变沿,具体代码如下:
  1. always @ (posedge pixel_clk) begin                  // retiming
  2.     clk_iserdes_data_d <= clk_iserdes_data ;
  3.     if ((clk_iserdes_data != clk_iserdes_data_d) && (clk_iserdes_data != 7'h00) && (clk_iserdes_data != 7'h7F)) begin
  4.         data_different <= 1'b1 ;
  5.     end
  6.     else begin
  7.         data_different <= 1'b0 ;
  8.     end
  9. end
  10.    
  11. always @ (posedge rxclk_d4) begin                           // clock delay shift state machine
  12.     not_rx_mmcm_lckd_intd4 <= ~(mmcm_locked & idelay_rdy) ;
  13.     rstcserdes <= not_rx_mmcm_lckd_intd4 | rst_iserdes ;
  14.     if (not_rx_mmcm_lckd_intd4 == 1'b1) begin
  15.         scount <= 6'h00 ;
  16.         state2 <= 0 ;
  17.         state2_count <= 5'h00 ;
  18.         locked_out <= 1'b0 ;
  19.         chfoundc <= 1'b1 ;
  20.         c_delay_in <= bt_val ;                          // Start the delay line at the current bit period
  21.         rst_iserdes <= 1'b0 ;
  22.         c_loop_cnt <= 2'b00 ;   
  23.     end
  24.     else begin
  25.         if (scount[5] == 1'b0) begin
  26.             scount <= scount + 6'h01 ;
  27.         end
  28.         state2_count <= state2_count + 5'h01 ;
  29.         data_different_dom_ch <= data_different ;
  30.         if (chfoundc == 1'b1) begin
  31.             chfound <= 1'b0 ;
  32.         end
  33.         else if (chfound == 1'b0 && data_different_dom_ch == 1'b1) begin
  34.             chfound <= 1'b1 ;
  35.         end
  36.         if ((state2_count == 5'h1F && scount[5] == 1'b1)) begin
  37.             case(state2)                    
  38.             0   : begin                         // decrement delay and look for a change
  39.                   if (chfound == 1'b1 || (c_loop_cnt == 2'b11 && c_delay_in == 5'h00)) begin  // quit loop if we've been around a few times
  40.                     chfoundc <= 1'b1 ;
  41.                     state2 <= 1 ;
  42.                   end
  43.                   else begin
  44.                     chfoundc <= 1'b0 ;
  45.                     if (c_delay_in != 5'h00) begin          // check for underflow
  46.                         c_delay_in <= c_delay_in - 5'h01 ;
  47.                     end
  48.                     else begin
  49.                         c_delay_in <= bt_val ;
  50.                         c_loop_cnt <= c_loop_cnt + 2'b01 ;
  51.                     end
  52.                   end
  53.                   end
  54.             1   : begin                         // add half a bit period using input information
  55.                   state2 <= 2 ;
  56.                   if (c_delay_in < {1'b0, bt_val[4:1]}) begin       // choose the lowest delay value to minimise jitter
  57.                     c_delay_in_target <= c_delay_in + {1'b0, bt_val[4:1]} ;
  58.                   end
  59.                   else begin
  60.                     c_delay_in_target <= c_delay_in - {1'b0, bt_val[4:1]} ;
  61.                   end
  62.                   end
  63.             2   : begin
  64.                   if (c_delay_in == c_delay_in_target) begin
  65.                     state2 <= 3 ;
  66.                   end
  67.                   else begin
  68.                     if (c_delay_in_ud == 1'b1) begin        // move gently to end position to stop MMCM unlocking
  69.                         c_delay_in <= c_delay_in + 5'h01 ;
  70.                         c_delay_in_ud <= 1'b1 ;
  71.                     end
  72.                     else begin
  73.                         c_delay_in <= c_delay_in - 5'h01 ;
  74.                         c_delay_in_ud <= 1'b0 ;
  75.                     end
  76.                   end
  77.                   end
  78.             3   : begin rst_iserdes <= 1'b1 ; state2 <= 4 ; end     // remove serdes reset
  79.             default : begin                         // issue locked out signal
  80.                   rst_iserdes <= 1'b0 ;  locked_out <= 1'b1 ;
  81.                   end
  82.             endcase
  83.         end
  84.     end
  85. end
复制代码

时钟部分解析并且得出idelay的延迟参数仿真结果,如下分析过程可以参考SDR的模式分析
2439e91b49ba4af7a524773d27bde52a.jpg
数据部分的分析不再详细描述和SDR方式类似,正确解析输出和时钟的最终结果。
e4bfd07020994fd3a44a50ec47280fbf.jpg
由于本文以官方代码讲解为主,本文的方法也是适用于最新的ultrascale 和ultrascale+ FPGA。但是ultrascale 和ultrascale+ FPGA 使用的是serdese3 以及 idelaye3 ,具体的细节参数上有差异,笔者计划后面在本文的基础上,增加从serdese2移植到serdese3的内容。


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则