xilinx原语详解及仿真——ISERDESE2

  前面在讲解HDMI接口之前,讲解过IDDR、ODDR、OSERDESE2、IBUFDS等原语,之后一直有读者在问什么时候更新ISERDESE2这个原语。前文讲解过这些原语都在HDMI或者RGMII中使用过,但是ISERDESE2这个原语目前我的板子除了HDMI输入,其余并不会使用到,所以当时就没有出。

  最近在网上看并没有用户对ISERDESE2的使用讲解的很清晰,所以本文就通过手册、仿真和ILA去讲解一下这个原语的使用方式,希望对大家的使用有所帮助。

1、ISERDESE2的功能

  ISERDESE2就是一个串并转换的模块,支持单沿或者双沿传输数据,其实ISERDESE2和IDDR使用的是同一个器件,所以均有双沿转单沿的功能,这可以在vivado综合后的布局布线中查看,也是同一管脚输入的信号不能同时使用IDDR和ISERDESE2的原因。

  理想情况如下图所示,并行输出的数据就刚好被ISERDESE2采集,进而完成串并转换,然而真实情况是这样吗?

在这里插入图片描述

图1 理想情况串并转换

  由于器件上电和初始化等等原因,实际的情况更可能如下图所示,并串转换模块工作时,串并转换模块还没有工作,导致并串转换模块输出的前几个数据丢失,下图中前面黑框中三个串行数据丢失。

  此时串并转换模块就只能从后面黄色框中依次取八个数据进行转换,由此转换出的数据会是正常的数据?

  显然不是,因此在串并转换模块正常工作的初期,往往需要对转换后的数据进行判断,如果转换后的数据不对,则需要将ISERDESE2转换的起始位置更改,直到转换后的数据正确为止,之后数据的数据才是有效的。

  这个校准的过程需要用户通过ISERDESE2的BITSLIP引脚去完成,在校准的过程中,输入端必须一直循环输入一个校准序列,就像下图中一直输入8’h5c和8’h82组成的序列一样,这是相对于其他原语会难一点的地方。

在这里插入图片描述

图2 真实情况串并转换

  通过上面知道了ISERDESE2原语需要解决的问题后,下面就通过手册讲解原语的各个端口以及参数,后面通过仿真验证前面所说的原因。

2、ISERDESE2原语端口及参数

  下图是ISERDESE2的框图。

在这里插入图片描述

图3 ISERDESE2框图

2.1、ISERDESE2端口信号

  Q1~Q8:ISERDESE2模块的并行输出数据,其中Q8是最先输入的串行数据位。一个ISERDESE2块最多输出8位并行数据,在DDR双沿采样模式下,两个ISERDESE2块级联可以输出10位和14位并行数据。

  Q8是接收到的最低位数据,如下图所示,OSERDESE2将8位从高到低为HGFEDCBA的并行数据转换为串行数据输出,最先输出最低位D1的值A。当ISERDESE2块接收串行数据时,Q1出现最高位,Q8出现最低位。

在这里插入图片描述

图4 ISERDESE2接收数据

  端口O:由图3知,该端口直接连接到串行输入的几个端口,并没有经过ISERDESE2核心逻辑,可以把D或DDLY的数据直接输出。

  SHIFTIN1、SHIFTIN2、SHIFTOUT1和SHIFTOUT2与OSERDESE2中该信号的原理一致,都是在两个OSERDESE2级联的时候,把从OSERDESE2的SHIFTOUT与主OSERDESE2的SHIFTIN连接。

  ISERDESE2的输入可以来自FPGA的管脚,即D端输入信号。也可以来自IDELAYE2的输出,即DDLY端口作为输入。还可以来自OSERDESE2的输出,即OFB作为输入,与OSERDESE2连接方式如下所示,两个器件的OFB信号相连即可。

在这里插入图片描述

图5 ISERDESE2与OSERDESE2连接

  ISERDESE2与OSERDESE2连接时需要添加“OFB_USED = TRUE ”属性,并且ISERDESE2和OSERDESE2的DATA_RATE、DATA_WIDTH参数必须设置一致。

  高速时钟输入CLK,与串行数据流对齐。

  辅助时钟输入CLKB,在MEMORY_QDR模式下,CLKB应连接到唯一的相移时钟,其余模式下,CLKB连接到CLK取反信号。

  分频时钟CLKDIV,用于驱动串并转换器、Bitslip子模块和CE模块的输出,与输出的并行数据对齐。

  在SDR模式下,如果输出的X位并行数据,那么CLKDIV的频率是CLK频率的1/X,如果是DDR模式,则CLKDIV的频率是CLK频率的2/X。

  手册要求CLK和CLKDIV的相位必须对齐,一般推荐两种连接方式,一种如下所示,时钟输入管脚通过BUFIO之后直接作为串行输入数据的时钟信号,BUFIO优点就是路径短,延时小,缺点就是只能驱动当前时钟区域的IDDR和ISERDESE2的时钟管脚。而并行时钟需要通过BUFR进行分频,分频系数根据并行数据位宽和工作模式确定。

  关于BUFIO和BUFR的相关使用,可以查看前文关于时钟资源相关原语的讲解,清晰讲解过BUFG、BUFIO、BUFR、BUFH、BUFMR的使用。

在这里插入图片描述

图6 时钟推荐

  另一种就是通过同一个MMCM产生CLK和CLKDIV两路时钟信号,这种方式更常用。原因是BUFIO和BUFR需要用户提供高频串行时钟,如果需要几百M的串行时钟,显然用户的外部晶振一般是无法提供的。

  时钟使能CE1和CE2,与参数NUM_CE的值有关。当NUM_CE为1时,使用CE1作为时钟使能信号。当NUM_CE为2时,当CLK_DIV为低电平时,CE2作为时钟使能信号,当CLK_DIV为高电平时,CE1作为时钟使能信号,对应真值表如下图所示。

在这里插入图片描述

图7 输入时钟使能模块

  BITSLIP引脚在高电平时执行与CLKDIV同步的移位操作,后文详解。

  RST复位信号,高电平有效,推荐退出复位时间与CLKDIV同步。

  过采样模式时钟OCLK,仅当INTERFACE_TYPE设置不为NETWORKING,才会使用OCLK时钟。

2.2、ISERDESE2原语参数

  上述就是ISERDESE2原语相关的端口信号,下面讲解ISERDESE2原语的参数。

  DATA_RATE用于设置ISERDESE2工作模式,可选择单沿工作模式(SDR)和双沿工作模式(DDR),默认DDR模式。

  DATA_WIDTH用于设置输出并行数据位宽,取决于DATA_RATE和INTERFACE_TYPE的设置。如下图所示,SDR模式下可以设置为2、3、4、5、6、7、8。DDR模式下单个ISERDESE2块只能设置为4、6、8,两个ISERDESE2块级联可以设置为10、14。

在这里插入图片描述

图8 支持的数据宽度

  INTERFACE_TYPE参数决定ISERDESE2是配置为内存模式还是网络模式。可选的值有MEMORY、MEMORY_DDR3、MEMORY_QDR、OVERSAMPLE、NETWORKING,默认模式是MEMORY,常用NETWORKING模式

  NUM_CE参数定义使用的时钟使能(CE1和CE2)数量,可设为1和2(默认值为2)。

  OFB_USED参数使能从OSERDESE2的OFB引脚到ISERDESE2 OFB引脚的路径,禁用外部管脚输入D和IDELAYE2的输入DLY。

  当使用DDR模式进行10位或者14位并行数据转换时,需要将两个模块级联,此时需要通过参数SERDES_MODE确定哪个是主ISERDESE2,哪个是从ISERDESE2。

  INIT_Qn用于设置第n个采样寄存器的初始值,而SRVAL_Qn用于设置复位第n个采样寄存器后的值。

  由图3知D和DDLY都是ISERDESE2的专用输入,D直接连接到IOB(直接与管脚相连),DDLY直接连接到IDELAYE2(在ISERDESE2和管脚之间有IDELAYE2加入延迟)。

  允许用户把延迟或非延迟的外部管脚输入信号作为ISERDESE2输入,通过参数IOBDELAY确定D和DDLY哪个作为ISERDESE2的输入,下图是IOBDELAY参数与输入信号的关系,经常设置位NONE,将外部管脚信号作为ISERDESE2串行数据输入。

在这里插入图片描述

图9 IOBDELAY参数值

3、位滑块(BITSLIP)

  这个信号就是进行校准的关键信号,如果没有这个信号,那么ISERDESE2的输出数据其实没有意义,很大可能是错误的。

  通过拉高ISERDESE2模块的Bitslip信号,输入的串行数据流在并行端重新排序,Bitslip与CLKDIV同步。

  下图说明了SDR和DDR模式下位滑移操作对数据采样的影响,ISERDESE2的数据宽度是八。在SDR模式下,每次Bitslip拉高都会导致输出数据左移一位。在DDR模式下,每次Bitslip拉高都会导致输出数据在右移1位和左移3位之间交替变化。

在这里插入图片描述

图10 位滑移操作示例

  上述操作其实可以看成下图所示的操作,上电后因为初始化结束比较晚等原因,ISERDESE2错过了下图黑框中前三位数据的采集时刻,从红框处开始取数据进行转换。

在这里插入图片描述

图11 采集数据

  转换结果为8’he4,并不是8’h5c或者8’h82,此时将Bitslip拉高一个CLKDIV时钟周期,则ISERDESE2会将下次采集数据的位置向后移动一位,如下图所示。

  本来第二次转换应该从下图中天蓝色方框开始,因为Bitslip拉高一个时钟,就会将开始位置向后移动一位,从红框处开始转换,转换结果继续与目标结果对比,如果不是,则继续拉高Bitslip,直到ISERDESE2的输出是8’h5c或者8’h82为止。

在这里插入图片描述

图12 采集数据

  其实用户并不需要关心Bitslip为高电平后,SDR和DDR模式下,输出数据怎么移动,只需要在这段校准的时间内保证输入串行数据一直都是8’h5c或者8’h82即可,然后去检测ISERDESE2的输出是不是8’h5c或者8’h82,不是则拉高Bitslip将转换开始的位置移动,直到找到开始转换的位置为止。

  只有当ISERDESE2处于网络模式(NETWORKING)下,位滑块(BITSLIP)才可用。Bitslip每次只能拉高一个CLKDIV周期,不能在两个CLKDIV周期内连续拉高Bitslip信号。

  在SDR和DDR模式下,从ISERDESE2检测到Bitslip的高电平开始,到ISERDESE2把Bitslip移动后的数据输出到Q1–Q8引脚为止,延迟时间为两个CLKDIV周期。

  在分析接收到的数据模式并可能发出下一个Bitslip命令之前,用户逻辑应在SDR模式下等待至少两个CLKDIV周期,在DDR模式下等待至少三个CLKDIV周期。

  综上Bitslip拉高后,需要等待至少三个时钟周期,才能检测并行输出结果是否与预期一致,进而确定是否通过拉高Bitslip信号继续调整输出。

  下图是4位并行数据的DDR模式下Bitslip的时序图,数据(D)重复的4位串行数据ABCD。ABCD可能以四种方式出现在ISERDESE2的Q1–Q4并行输出端:ABCD、BCDA、CDAB和DABC,只有ABCD才是正确的输出。

  拉高Bitslip信号选择所需的对齐方式(ABCD),下图显示了Bitslip的时序以及ISERDESE2并行输出Q1–Q4的校准时序。

在这里插入图片描述

图13 DDR Bitslip功能图

  注意上图在Bitslip拉高后的第三个时钟,Q4-Q1才输出移位后的转换结果,与前文的延时一致。

4、代码设计

  本文利用OSERDESE2和ISERDESE2回环,来验证前文所讲ISERDESE2和Bitslip的功能。ISERDESE2采用网络模式(NETWORKING),选择DDR的串行采样方式,并行数据位宽为8位。

  OSERDESE2相关模块如下图所示,用于产生测试的并行数据,然后转换为串行数据,对应代码如下所示。

  通过一个计数器cnt,上电后前64个时钟周期用于产生用于校准的序列,为8’h5c和8’h82交替发送的串行序列。当校准序列发送完毕后,通过m序列生成伪随机数据用于测试。

//--###############################################################################################
//--#
//--# File Name		: oserdese2_ctrl
//--# Designer		: 数字站
//--# Tool			: Vivado 2021.1
//--# Design Date	: 2024.4.4
//--# Description	: 并串转换数据生成模块;
//--# Version		: 0.0
//--# Coding scheme	: GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
module oserdese2_ctrl(
    input           clk         ,//系统时钟信号;
    input           clk_div     ,
    input           rst         ,//系统复位信号,高电平有效;

    output          ofb          //直连iserdese2的OFB管脚。
); 
    reg             flag    ;//
    reg [7 : 0]     cnt     ;//
    reg [7 : 0]     din     ;//

    //辅助生成测试信号;
    always@(posedge clk_div)begin
        if(rst)begin//初始值为0;
            flag <= 1'b0;
        end
        else begin
            flag <= ~flag;
        end
    end
    
    //用于对校准序列个数进行计数。
    always@(posedge clk_div)begin
        if(rst)begin//初始值为0;
            cnt <= 'd0;
        end
        else if(&cnt)begin
            cnt <= cnt;
        end
        else begin
            cnt <= cnt + 1;
        end
    end

    //生成用于校准的循环数据;
    always@(posedge clk_div)begin
        if(rst)begin//初始值为0;
            din <= 8'h82;
        end
        else if(&cnt)begin//如果校准序列发送完毕,则发送伪随机信号用于测试。
            din <= {(din[0] ^ din[4] ^ din[5] ^ din[6]),din[7:1]};//M序列公式为x^8+x^4+x^3+x^2+1。
        end
        else if(flag)begin//发送校准序列8'h5c;
            din <= 8'h5c;
        end
        else begin//发送校准序列8'h82.
            din <= 8'h82;
        end
    end

    //例化主OSERDESE2原语
    OSERDESE2 #(
        .DATA_RATE_OQ   ( "DDR"     ),// DDR, SDR
        .DATA_RATE_TQ   ( "DDR"     ),// DDR, BUF, SDR
        .DATA_WIDTH     ( 8         ),// Parallel data width (2-8,10,14)
        .INIT_OQ        ( 1'b0      ),// Initial value of OQ output (1'b0,1'b1)
        .INIT_TQ        ( 1'b0      ),// Initial value of TQ output (1'b0,1'b1)
        .SERDES_MODE    ( "MASTER"  ),// MASTER, SLAVE
        .SRVAL_OQ       ( 1'b0      ),// OQ output value when SR is used (1'b0,1'b1)
        .SRVAL_TQ       ( 1'b0      ),// TQ output value when SR is used (1'b0,1'b1)
        .TBYTE_CTL      ( "FALSE"   ),// Enable tristate byte operation (FALSE, TRUE)
        .TBYTE_SRC      ( "FALSE"   ),// Tristate byte source (FALSE, TRUE)
        .TRISTATE_WIDTH ( 1         ) // 3-state converter width (1,4)
    )
    OSERDESE2_inst (
        .OFB        ( ofb           ),// 1-bit output: Feedback path for data
        .OQ         (               ),// 1-bit output: Data path output
        // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
        .SHIFTOUT1  (               ),
        .SHIFTOUT2  (               ),
        .TBYTEOUT   (               ),// 1-bit output: Byte group tristate
        .TFB        (               ),// 1-bit output: 3-state control
        .TQ         (               ),// 1-bit output: 3-state control
        .CLK        ( clk           ),// 1-bit input: High speed clock
        .CLKDIV     ( clk_div       ),// 1-bit input: Divided clock
        // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
        .D1         ( din[0]        ),
        .D2         ( din[1]        ),
        .D3         ( din[2]        ),
        .D4         ( din[3]        ),
        .D5         ( din[4]        ),
        .D6         ( din[5]        ),
        .D7         ( din[6]        ),
        .D8         ( din[7]        ),
        .OCE        ( 1'b1          ),// 1-bit input: Output data clock enable
        .RST        ( rst           ),// 1-bit input: Reset
        // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
        .SHIFTIN1   (               ),
        .SHIFTIN2   (               ),
        // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
        .T1         ( 1'b0          ),
        .T2         ( 1'b0          ),
        .T3         ( 1'b0          ),
        .T4         ( 1'b0          ),
        .TBYTEIN    ( 1'b0          ),// 1-bit input: Byte group tristate
        .TCE        ( 1'b0          )  // 1-bit input: 3-state clock enable
    );

endmodule

  为了方便后续上板测试,OSERDESE2的串行输出信号和ISERDESE2串行输入信号采用OFB直连,不需要经过IOB,直接在芯片内部进行回环。

  之后是ISERDESE2模块,对应的原语调用如下所示,采用DDR双沿采集数据模式,启用OFB作为串行输入,将输出Q8-Q1顺序调换,使得ISERDESE2输出并行数据与OSERDESE2并行输入数据保持一致。

    //例化主ISERDESE2原语
    ISERDESE2 #(
        .DATA_RATE          ( "DDR"         ),// DDR, SDR
        .DATA_WIDTH         ( 8             ),// Parallel data width (2-8,10,14)
        .DYN_CLKDIV_INV_EN  ( "FALSE"       ),// Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
        .DYN_CLK_INV_EN     ( "FALSE"       ),// Enable DYNCLKINVSEL inversion (FALSE, TRUE)
        .INIT_Q1            ( 1'b0          ),// INIT_Q1 : Initial value on the Q outputs (0/1)
        .INIT_Q2            ( 1'b0          ),// INIT_Q2 : Initial value on the Q outputs (0/1)
        .INIT_Q3            ( 1'b0          ),// INIT_Q3 : Initial value on the Q outputs (0/1)
        .INIT_Q4            ( 1'b0          ),// INIT_Q4 : Initial value on the Q outputs (0/1)
        .INTERFACE_TYPE     ( "NETWORKING"  ),// MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
        .IOBDELAY           ( "NONE"        ),// NONE, BOTH, IBUF, IFD
        .NUM_CE             ( 2             ),// Number of clock enables (1,2)
        .OFB_USED           ( "TRUE"        ),// Select OFB path (FALSE, TRUE)
        .SERDES_MODE        ( "MASTER"      ),// MASTER, SLAVE
        .SRVAL_Q1           ( 1'b0          ),// SRVAL_Q1 : Q output values when SR is used (0/1)
        .SRVAL_Q2           ( 1'b0          ),// SRVAL_Q2 : Q output values when SR is used (0/1)
        .SRVAL_Q3           ( 1'b0          ),// SRVAL_Q3 : Q output values when SR is used (0/1)
        .SRVAL_Q4           ( 1'b0          ) // SRVAL_Q4 : Q output values when SR is used (0/1)
    )
    ISERDESE2_inst (
        .O          (               ),// 1-bit output: Combinatorial output
        .Q1         ( q[7]          ),// Q1 - Q8: 1-bit (each) output: Registered data outputs
        .Q2         ( q[6]          ),
        .Q3         ( q[5]          ),
        .Q4         ( q[4]          ),
        .Q5         ( q[3]          ),
        .Q6         ( q[2]          ),
        .Q7         ( q[1]          ),
        .Q8         ( q[0]          ),
        .SHIFTOUT1  (               ),// SHIFTOUT1 : 1-bit (each) output: Data width expansion output ports
        .SHIFTOUT2  (               ),// SHIFTOUT2 : 1-bit (each) output: Data width expansion output ports
        .BITSLIP    ( bitslip       ),// 1-bit input: The BITSLIP pin performs a Bitslip operation synchronous to
                                      // CLKDIV when asserted (active High). Subsequently, the data seen on the Q1
                                      // to Q8 output ports will shift, as in a barrel-shifter operation, one
                                      // position every time Bitslip is invoked (DDR operation is different from SDR).
        .CE1        ( 1'b1          ),// CE1: 1-bit (each) input: Data register clock enable inputs
        .CE2        ( 1'b1          ),// CE2: 1-bit (each) input: Data register clock enable inputs
        .CLKDIVP    ( 1'b0          ),// 1-bit input: TBD
        // Clocks: 1-bit (each) input: ISERDESE2 clock input ports
        .CLK        ( clk           ),// 1-bit input: High-speed clock
        .CLKB       ( ~clk          ),// 1-bit input: High-speed secondary clock
        .CLKDIV     ( clk_div       ),// 1-bit input: Divided clock
        .OCLK       ( 1'b0          ),// 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY" 
        // Dynamic Clock Inversions: 1-bit (each) input: Dynamic clock inversion pins to switch clock polarity
        .DYNCLKDIVSEL(1'b0          ),// 1-bit input: Dynamic CLKDIV inversion
        .DYNCLKSEL  ( 1'b0          ),// 1-bit input: Dynamic CLK/CLKB inversion
        // Input Data: 1-bit (each) input: ISERDESE2 data input ports
        .D          ( 1'b0          ),// 1-bit input: Data input
        .DDLY       ( 1'b0          ),// 1-bit input: Serial data from IDELAYE2
        .OFB        ( ofb           ),// 1-bit input: Data feedback from OSERDESE2
        .OCLKB      ( 1'b0          ),// 1-bit input: High speed negative edge output clock
        .RST        ( rst           ),// 1-bit input: Active high asynchronous reset
        // SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
        .SHIFTIN1   (               ),
        .SHIFTIN2   (               )
    );

  之后需要对ISERDESE2并行输出进行校准,OSERDESE2产生的校准序列是8’h5c和8’h82交替串行序列,因此ISERDESE2转换结果也应该是8’h5c或者8’h82。如果转换结果不是这两个数据,那么将bitslip拉高一个时钟周期,对应代码如下所示。

    //当没有完成校准时,如果检测到转换结果与要求不一致,则把该信号拉高。
    always@(posedge clk_div)begin
        if(rst)begin//初始值为0;
            bitslip <= 1'b0;
        end//当没有校准,且没有处于校准阶段时检测到串并转换结果不为8'h5c或者8'h82时拉高;
        else if(((q != 8'h5c) && (q != 8'h82)) && (~slip_flag) && (~bitslip) && (~dout_vld))begin
            bitslip <= 1'b1;
        end
        else begin//其余时间拉低;
            bitslip <= 1'b0;
        end
    end

  Bitslip拉高之后,ISERDESE2输出数据不是立即有效的,需要经过几个时钟的转换时钟,手册中说SDR模式需要消耗2个时钟,而DDR模式需要消耗三个时钟。

  为了方便设计,此处直接使用一个位宽为2的计数器,每次bitslip拉高之后,等待4个时钟,之后才对ISERDESE2的并行数据进行检测,确定是否满足要求,bitslip还是否需要拉高。

    //滑块计数器,因为bitslip拉高后,输出需要经过一段时间才会有效,因此这段时间不能对输出数据进行读取判断。
    always@(posedge clk_div)begin
        if(rst)begin//初始值为0;
            slip_cnt <= 'd0;
        end
        else if(slip_flag)begin//对bitslip拉高后的时钟计数。
            slip_cnt <= slip_cnt + 'd1;
        end
    end
    
    //bitslip拉高后的标志信号,初始值为0,当bitslip拉高时拉高,当slip_cnt计数器计数到最大值时清零。
    always@(posedge clk_div)begin
        if(rst)begin//初始值为0;
            slip_flag <= 1'b0;
        end
        else if(&slip_cnt)begin
            slip_flag <= 1'b0;
        end
        else if(bitslip)begin
            slip_flag <= 1'b1;
        end
    end

  在校准阶段ISERDESE2输出可能会出现偶然结果,导致没有校准情况下,输出的部分并行数据与校准序列一致。为了避免这种情况,应该多次检测连续输出数据均与校验数据一致时,才认为校准完成。

  因此需要一个计数器,对ISERDESE2输出的正确数据进行计数,当bitslip拉高或者处于拉高后的一段时间时,证明前面检测到的数据错误,将计数器清零。

  当连续检测CNT_NUM个数据均正确时,认为校准完成,将计数器置为最大值,之后一直保持不变。

    //转换成功计数器,用于记录校准阶段,当校准阶段检测到固定个连续有效数据时,认为校准成功。
    always@(posedge clk_div)begin
        if(rst)begin//
            cnt <= 0;
        end//当在校准阶段时,当bitslip有效时拉高;
        else if((slip_flag || bitslip) && (~dout_vld))begin
            cnt <= 0;
        end
        else if(cnt == CNT_NUM - 1)begin//当检测到固定校准数据时,计数器保持该数值。
            cnt <= CNT_NUM - 1;
        end
        else if(add_cnt)begin
            cnt <= cnt + 1;
        end
    end
    
    //当不处于移动滑块状态且检测到输出数据为规定数据时加1.
    assign add_cnt = ((q == 8'h5c) || (q == 8'h82)) && (~slip_flag) && (~bitslip);
    
    //将转换后的数据输出,只有当校准完成后,输出有效指示信号磁能拉高,表示输出的数据有效。
    always@(posedge clk_div)begin
        dout <= q;
        dout_vld <= (cnt == CNT_NUM - 1);
    end

  之后是顶层模块的处理,对于串行时钟和并行时钟的处理方式有如下两种,第一种是本时钟区域的外部时钟管脚输入串行时钟信号,之后通过BUFIO作为ISERDESE2和OSERDESE2的串行时钟信号。通过BUFR分频输出的时钟作为ISERDESE2和OSERDESE2的并行数据时钟信号,对应代码如下所示。

  但是这种方式仿真会出现错误,OSERDESE2输出的数据始终是不定态,不知道为什么。另外要求输入串行时钟,然而串行时钟一般频率很高,用户的晶振一般是提供不了的,因此这种方式不太常用。

    //调用BUFIO;
    /*BUFIO u_BUFIO (
      .O    ( o_clk     ), // 1-bit output: Clock output (connect to I/O clock loads).
      .I    ( clk       )  // 1-bit input: Clock input (connect to an IBUF or BUFMR).
    );

    //调用BUFR对时钟进行分频;
    BUFR #(
        .BUFR_DIVIDE  ( "4"         ),//Values: "BYPASS, 1, 2, 3, 4, 5, 6, 7, 8" 
        .SIM_DEVICE   ( "7SERIES"   ) //Must be set to "7SERIES" 
    )
    u_BUFR (
        .O    ( clk_div     ),//1-bit output: Clock output port
        .CE   ( 1'b1        ),//1-bit input: Active high, clock enable (Divided modes only)
        .CLR  ( 1'b0        ),//1-bit input: Active high, asynchronous clear (Divided modes only)
        .I    ( clk         )
    );*/

  另一种时钟处理方式是使用同一个MMCM,生成串行时钟和并行时钟,对于输入时钟频率没有太大要求。本文外部输入100MHz时钟信号作为ISERDESE2和OSERDESE2的并行时钟信号,通过MMCM生成400MHz作为串行时钟信号。对应代码如下所示:

    clk_wiz_0 u_clk_wiz_0(
        .clk_out1   ( clk_div_o ),//output clk_out1
        .clk_out2   ( clk       ),//output clk_out2
        .reset      ( rst       ),//input reset
        .clk_in1    ( clk_div   ) //input clk_in1
    );

  MMCM的输出配置如下所示:

在这里插入图片描述

图14 MMCM输出配置

  之后顶层模块需要例化前面两个模块,对应的完整代码如下所示。

    //例化并串转换原语;
    oserdese2_ctrl u_oserdese2_ctrl(
        .clk        ( clk           ),//系统时钟信号;
        .clk_div    ( clk_div_o     ),
        .rst        ( rst           ),//系统复位信号,高电平有效;
        .ofb        ( oserdese_ofb  ) 
    );

    //例化串并转换原语;
    iserdese2_ctrl u_iserdese2_ctrl(
        .clk         ( clk              ),//系统时钟信号;
        .clk_div     ( clk_div_o        ),
        .rst         ( rst              ),//系统复位信号,高电平有效;
        .ofb         ( oserdese_ofb     ),
        .dout        ( o_iserdese2      ),//输出数据
        .dout_vld    ( o_iserdese2_vld  ) //输出数据有效指示信号;
    );

    //例化ILA
    ila_0 u_ila_0 (
        .clk    ( clk_div_o                 ),//input wire clk;
        .probe0 ( u_oserdese2_ctrl.din      ),//input wire [7:0]  probe0;
        .probe1 ( u_iserdese2_ctrl.q        ),//input wire [7:0]  probe1;
        .probe2 ( u_iserdese2_ctrl.dout     ),//input wire [7:0]  probe2;
        .probe3 ( u_iserdese2_ctrl.cnt      ),//input wire [3:0]  probe3;
        .probe4 ( u_iserdese2_ctrl.slip_cnt ),//input wire [1:0]  probe4;
        .probe5 ( u_iserdese2_ctrl.bitslip  ),//input wire [0:0]  probe5;
        .probe6 ( u_iserdese2_ctrl.slip_flag),//input wire [0:0]  probe6;
        .probe7 ( u_iserdese2_ctrl.add_cnt  ),//input wire [0:0]  probe7;
    	.probe8 ( u_iserdese2_ctrl.dout_vld ) //input wire [0:0]  probe8;
    );

  顶层模块对应的RTL视图如下所示:

在这里插入图片描述

图15 顶层模块RTL

5、工程仿真

  对应的TestBench很简单,只需要提供时钟和复位信号即可,如下所示:

//--###############################################################################################
//--#
//--# File Name		: testBench
//--# Designer		: 数字站
//--# Tool			: Vivado 2021.1
//--# Design Date	: 2024.4.4
//--# Description	: 测试串并转换原语
//--# Version		: 0.0
//--# Coding scheme	: GBK(If the Chinese comment of the file is garbled, please do not save it and check whether the file is opened in GBK encoding mode)
//--#
//--###############################################################################################
`timescale 1ns / 1ps
module test ();
    localparam	CYCLE		= 	10			    ;//系统时钟周期,单位ns,默认10ns;
    localparam	RST_TIME	= 	10			    ;//系统复位持续时间,默认10个系统时钟周期;

    reg							clk			    ;//系统时钟,默认100MHz;
    reg							rst_n		    ;//系统复位,默认低电平有效;

    wire    [7 : 0]             o_iserdese2     ;
    wire                        o_iserdese2_vld ;

    top u_top(
        .clk_div            ( clk		        ),//系统时钟信号;
        .rst_n              ( rst_n             ),//系统复位信号,低电平有效;
        .o_iserdese2        ( o_iserdese2       ),
        .o_iserdese2_vld    ( o_iserdese2_vld   )
    );

    //生成周期为CYCLE数值的系统时钟;
    initial begin
        clk = 0;
        forever #(CYCLE/2) clk = ~clk;
    end

    //生成复位信号;
    initial begin
        rst_n = 1;
        #2;
        rst_n = 0;//开始时复位10个时钟;
        #(RST_TIME*CYCLE);
        rst_n = 1;
        @(posedge o_iserdese2_vld);//校准完成;
        repeat(400)@(posedge clk);
        $stop;//停止仿真;
    end

endmodule

  之后运行vivado仿真,下图是OSERDESE2的时序图,将并行数据转换为串行数据输出,串行数据与时钟clk的双沿对齐。

在这里插入图片描述

图16 OSERDESE2时序

  之后查看ISERDESE2时序,如下图所示,在校准开始阶段,ISERDESE2输出数据Q为8’h20,并不是校准序列8’h5c或者8’h82,所以将bitslip拉高一个clk_div时钟宽度。

  等待四个clk_div时钟周期后,再次检测ISERDESE2输出并行数据,依旧不是规定数值,继续拉高bitslip进行调整。

在这里插入图片描述

图17 OSERDESE2时序

  直到某次调整之后,ISERDESE2输出的并行数据序列均为8’h5c或8’h82,如下图所示,此时就可以拉高bitslip了。

在这里插入图片描述

图18 OSERDESE2时序

  之后检测连续16个ISERDESE2并行输出数据,如果均为规定序列,没有问题,则将dout_vld拉高,表示输出数据已经对齐,是有效数据。

  如果检测过程中出现错误,则计数器清零,bitslip拉高继续调整,直到满足要求即可。

在这里插入图片描述

图19 计数器对校准序列计数

  下面是截取当转换完成后,发送端校验数据发送完成之后,发送伪随机的测试数据,ISERDESE2的输出依旧是正确的,证明前文的校准方式没有问题。

在这里插入图片描述

图20 接收伪随机序列

  上述仿真就没有问题了,之后上板通过ila抓取相关信号,查看结果是否正确。

6、上板测试

  至于在使用OFB回环时,为什么不能使用D管脚,如下图所示,ISERDESE2和OSERDESE2会使用掉一个管脚的ILOGIC和OLOGIC资源,导致这个IOB管脚不能使用,ISERDESE2的D管脚就不能使用了。

  下图中白色走线就是ISERDESE2和OSERDESE2的OFB回环走线。

在这里插入图片描述

图21 IO资源分布

  工程综合完毕之后,将代码下载到开发板中,之后把dout_vld的上升沿作为ILA的触发条件。由于该条件在复位之后只会触发一次,因此在ILA启动之后,需要按一下开发板的复位引脚,才能够触发条件,对应截图如下所示。

在这里插入图片描述

图22 ILA的触发条件设置

  如下图所示,开始校准时,ISERDESE2输出并行数据并不是校准序列的8’h5c或者8’h82。之后把bitslip拉高一个时钟,调整串并转换的起始位置,下图只调整了两次,ISERDESE2就输出了正确数据。

在这里插入图片描述

图23 校准时序

  计数器cnt对ISERDESE2输出的正确数据进行计数,当ISERDESE2连续输出16个正确数据时,认为校准成功,如果觉得16个太少,在校准的时候可以设置更大的数据。

在这里插入图片描述

图24 校准计数器

  最后就是对比校准后伪随机序列的转换了,din是OSERDESE2并串转换的输入数据,而dout是ISERDESE2串并转换的输出结果,下图中两图的数据相等,证明校准电路和串并转换均没有问题。

  din和dout有延时是正常现象,因为两者中间有很多电路和触发器嘛,延时是正常的。

在这里插入图片描述

图25 转换数据

  关于ISERDESE2的讲解到此结束了,本文虽然只是讲解了DDR模式的使用,但SDR原理也是类似的,只不过是单沿转换数据,两个时钟频率的比值不一样罢了。

  主要理解bitslip的使用,才能够真正的了解该模块,不必在意bitslip拉高后,数据该怎么移动,只需要关注ISERDESE2输出是不是需要的输出,不是就继续通过bitslip调整ISERDESE2输出。这个校准的过程需要输入一直连续输入固定序列,不然无法校准。

  需要本工程的在公众号后台回复“ISERDESE2”(不包括引号)即可,工程使用vivado2021.1,平台是zynq-7030。


  如果对文章内容理解有疑惑或者对代码不理解,可以在评论区或者后台留言,看到后均会回复!

  如果本文对您有帮助,还请多多点赞👍、评论💬和收藏⭐!您的支持是我更新的最大动力!将持续更新工程!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/516735.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python概率编程库之pymc使用详解

概要 Python PyMC库是一个强大的概率编程库,用于贝叶斯统计建模和蒙特卡罗采样。它提供了丰富的功能和灵活的API,使得贝叶斯推断和概率建模变得简单而有效。 安装与配置 首先,看看如何安装Python PyMC库并进行基本配置: pip install pymc安装完成后,可以导入PyMC库并开…

Spring Security 实现后台切换用户

Spring Security version 后端代码&#xff1a; /*** author Jerry* date 2024-03-28 17:47* spring security 切换账号*/RestController RequiredArgsConstructor RequestMapping("api/admin") public class AccountSwitchController {private final UserDetailsSe…

深入浅出 -- 系统架构之垂直架构

当业务复杂度增加、访问量逐渐增大出现高并发时&#xff0c;单体架构无法满足需求&#xff0c;可以根据业务功能对系统进行拆分&#xff0c;以提高访问效率。 垂直架构介绍 1.垂直架构一般是因为单体架构太过于庞大而进行的拆分&#xff0c;拆分后各个系统应满足独立运行互相不…

5.1 输出hw,求数组最大、小值,字符串转大、小写

5.1 输出hw&#xff0c;求数组最大、小值&#xff0c;字符串转大、小写】 1. 注释 1.1 单行注释 ;注释内容 1.2 多行注释 comment* 注释内容 *comment2. 输出“hello&#xff0c;world” 头文件&#xff0c;命名数组定义字符串&#xff0c;结束代码&#xff0c;直接在c:下…

注解(Annotation)

文章目录 1 注解概述1.1 什么是注解1.2 注解与注释1.3 注解的重要性 2 常见的Annotation作用3 三个最基本的注解3.1 Override3.2 Deprecated3.3 SuppressWarnings 4 元注解5 自定义注解的使用5.1 声明自定义注解5.2 使用自定义注解5.3 读取和处理自定义注解 6 JUnit单元测试6.1…

通讯录项目实现

引言&#xff1a;通过顺序表的逻辑实现通讯录。这里就不讲关于顺序表的函数了。如果有不明白的可以看我写的顺序表的博客。 目录 顺序表与通讯录的比较 各源文件文件大榄 Contact.c中通讯录相关函数的定义 初始化和销毁通讯录 添加联系人&#xff1a; 删除联系人&#xf…

欧拉角及其旋转矩阵,旋转顺序与内旋/外旋及其代码

目录 欧拉角介绍 旋转矩阵公式推导 旋转顺序 内旋/外旋 欧拉角介绍 欧拉角&#xff1a; 横滚&#xff1a;roll&#xff08;绕X轴旋转&#xff09;&#xff1b; 俯仰&#xff1a;pitch&#xff08;绕Y轴旋转&#xff09;&#xff1b; 偏航&#xff08;也称航向角&#x…

腾讯云服务器4核8g配置好不好?用它干啥使?

腾讯云4核8G服务器多少钱&#xff1f;腾讯云4核8G轻量应用服务器12M带宽租用价格646元15个月&#xff0c;活动页面 txybk.com/go/txy 活动链接打开如下图所示&#xff1a; 腾讯云4核8G服务器优惠价格 这台4核8G服务器是轻量应用服务器&#xff0c;详细配置为&#xff1a;轻量4核…

CSS简介

1. CSS简介 CSS(Cascading Style Sheets)层叠样式表&#xff0c;又叫级联样式表&#xff0c;简称样式表&#xff0c;文件后缀名为.css&#xff0c;用于HTML文档中元素样式的定义。CSS语法&#xff1a; <!DOCTYPE html> <html lang"en"> <head><…

C语言—用EasyX实现反弹球消砖块游戏

代码效果如下 #undef UNICODE #undef _UNICODE #include<graphics.h> #include<conio.h> #include<time.h> #include<stdio.h>#define width 640 #define high 480 #define brick_num 10int ball_x, ball_y; int ball_vx, ball_vy; int radius; int ba…

删除中间节点(狸猫换太子法)

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 每一个裂缝都是为透出光而努力&#…

香橙派OPI Airpro上的智能交通监管系统(保姆级代码)

首先&#xff0c;你需要安装必要的软件包和库&#xff0c;例如Python3和TensorFlow。然后&#xff0c;你可以编写脚本来处理数据采集、分析和报告生成等任务。以下是一个简单的示例&#xff1a; # 安装必要的软件包 sudo apt-get update sudo apt-get install -y python3 pyth…

腾讯云4核8G服务器性能怎么样?大明白来说说

腾讯云4核8G服务器价格&#xff1a;轻量4核8G12M优惠价格646元15个月、CVM S5服务器4核8G配置1437元买1年送3个月。腾讯云4核8G服务器支持多少人同时在线&#xff1f;支持30个并发数&#xff0c;可容纳日均1万IP人数访问。腾讯云百科txybk.com整理4核8G服务器支持多少人同时在线…

4月4号总结

java学习 一.接口 1.介绍 定义接口需要使用到关键字interface去定义接口。 格式如下&#xff1a; 类与接口的关系不是继承&#xff0c;而是实现&#xff0c;用关键字 implements &#xff0c;格式如下&#xff1a; 这个类去实现接口&#xff0c;其中的关系就相当于&#xf…

DFS(基础,回溯,剪枝,记忆化)搜索

DFS基础 DFS(深度优先搜索) 基于递归求解问题&#xff0c;而针对搜索的过程 对于问题的介入状态叫初始状态&#xff0c;要求的状态叫目标状态 这里的搜索就是对实时产生的状态进行分析检测&#xff0c;直到得到一个目标状态或符合要求的最佳状态为止。对于实时产生新的状态…

不到2000字,轻松带你搞懂STM32中GPIO的8种工作模式

大家好&#xff0c;我是知微&#xff01; 学习过单片机的小伙伴对GPIO肯定不陌生&#xff0c;GPIO &#xff08;general purpose input output&#xff09;是通用输入输出端口的简称&#xff0c;通俗来讲就是单片机上的引脚。 在STM32中&#xff0c;GPIO的工作模式被细分为8种…

gitcode 配置 SSH 公钥

在 gitcode 上配置SSH公钥后&#xff0c;可以通过SSH协议安全地访问远程仓库&#xff0c;无需每次都输入用户名和密码。以下是配置SSH公钥的步骤&#xff1a; 5分钟解决方案 用 OpenSSH公钥生成器 生成 公钥和私钥&#xff0c;私钥文件&#xff08;id_rsa&#xff09;下载&am…

算法设计与分析实验报告python实现(排序算法、三壶谜题、交替放置的碟子、带锁的门)

一、 实验目的 1&#xff0e;加深学生对算法设计方法的基本思想、基本步骤、基本方法的理解与掌握&#xff1b; 2&#xff0e;提高学生利用课堂所学知识解决实际问题的能力&#xff1b; 3&#xff0e;提高学生综合应用所学知识解决实际问题的能力。 二、实验任务 1、排序算法…

python中如何使用help()

help()函数帮助我们了解模块、类型、对象、方法、属性的详细信息。 1、帮助查看类型详细信息&#xff0c;包含类的创建方式、属性、方法 >>> help(list) Help on class list in module builtins: class list(object)| list() -> new empty list| list(iterable)…

EmpireCMS:帝国源码cms网站搬家/数据迁移方法教程

帝国cms迁移数据&#xff0c;从一台旧的服务器把网站文件都搬迁到新的服务器&#xff0c;会涉及到附件&#xff0c;数据库信息等&#xff0c;很多人在搬迁的时候也会遇到各种问题&#xff0c;下面是小编整理的关于如何搬迁帝国cms数据的解决方案和思路&#xff0c;方便新手站长…