RGMII接口转GMII接口的实现思路

接口/总线/驱动

1136人已加入

描述

    哈咯大家好,今天带来一篇RGMII接口转GMII接口的实现思路,废话不多说,直接进入主题。

    RGMII接口是双沿采样时钟,数据位宽为4bit,而GMII接口是单沿采样时钟,数据位宽是8bit。

    以Xilinx K7 ULTRASCALE+为例。

    RGMII→GMII方向:

    可选方法有:

    1.偏移接收时钟相位来实现正确采样数据的效果

    2.偏移数据来实现正确采样数据的效果

    本篇实现第2种方法,思路为:

    IO管脚→idelay→iddr→crc校验。

    将RGMII的数据管脚经过idelay源语,进行数据delay,再将数据经过iddr把4bit数据转换成8bit数据,最后再将转换好8bit的GMII数据经过CRC进行校验,查看设置的idelay值是否满足需求,若CRC校验通过,则固定idelay的值即可,若不通过,则重新调整idelay的值。

    部分代码如下:

 

generate        
    for (n = 0; n < 4; n = n + 1)       
    begin: DDRIN_BLOCK      
        IDELAYE3 #      
        (       
            .CASCADE            ( "NONE"                    ),  // Cascade setting (MASTER, NONE, SLAVE_END, SLAVE_MIDDLE)
            .DELAY_FORMAT       ( "TIME"                    ),  // Units of the DELAY_VALUE (COUNT, TIME)
            .DELAY_SRC          ( "IDATAIN"                 ),  // Delay input (DATAIN, IDATAIN)
            .DELAY_TYPE         ( "VAR_LOAD"                ),  // Set the type of tap delay line (FIXED, VARIABLE, VAR_LOAD)
            .DELAY_VALUE        ( 0                         ),  // Input delay value setting
            .IS_CLK_INVERTED    ( 1'b0                      ),  // Optional inversion for CLK
            .IS_RST_INVERTED    ( 1'b0                      ),  // Optional inversion for RST
            .REFCLK_FREQUENCY   ( 307.2                     ),  // IDELAYCTRL clock input frequency in MHz (200.0-800.0)
            .SIM_DEVICE         ( "ULTRASCALE_PLUS"         ),  // Set the device version for simulation functionality (ULTRASCALE,
                                                                // ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1, ULTRASCALE_PLUS_ES2)
            .UPDATE_MODE        ( "ASYNC"                   )   // Determines when updates to the delay will take effect (ASYNC, MANUAL,
                                                                // SYNC)
        )   
        IDELAYE3_inst   
        (   
            .CASC_OUT           (                           ),  // 1-bit output: Cascade delay output to ODELAY input cascade
            .CNTVALUEOUT        ( o_idelay_cnt_value_out[n] ),  // 9-bit output: Counter value output
            .DATAOUT            ( gmii_rxd_dly[n]           ),  // 1-bit output: Delayed data output
            .CASC_IN            ( 1'b0                      ),  // 1-bit input: Cascade delay input from slave ODELAY CASCADE_OUT
            .CASC_RETURN        ( 1'b0                      ),  // 1-bit input: Cascade delay returning from slave ODELAY DATAOUT
            .CE                 ( 1'd0                      ),  // 1-bit input: Active-High enable increment/decrement input
            .CLK                ( i_rgmii_rxc               ),  // 1-bit input: Clock input
            .CNTVALUEIN         ( idelay_cnt_value_in       ),  // 9-bit input: Counter value input
            .DATAIN             ( 1'd0                      ),  // 1-bit input: Data input from the logic
            .EN_VTC             ( idelay_en_vtc             ),  // 1-bit input: Keep delay constant over VT
            .IDATAIN            ( i_rgmii_rxd[n]            ),  // 1-bit input: Data input from the IOBUF
            .INC                ( 1'b1                      ),  // 1-bit input: Increment / Decrement tap delay input
            .LOAD               ( idelay_load               ),  // 1-bit input: Load DELAY_VALUE input
            .RST                ( i_rst                     )   // 1-bit input: Asynchronous Reset to the DELAY_VALUE
        );      
    
        IDDR #  
        (   
            .DDR_CLK_EDGE       ( "OPPOSITE_EDGE"           ), //"OPPOSITE_EDGE",  "SAME_EDGE, "SAME_EDGE_PIPELINED"
            .INIT_Q1            ( 1'b0                      ),
            .INIT_Q2            ( 1'b0                      ),
            .SRTYPE             ( "ASYNC"                   )
        )       
        ddr_data_in(        
            .Q1                 ( gmii_rxd[n+4]             ),
            .Q2                 ( gmii_rxd[n]               ),
            .C                  ( i_rgmii_rxc               ),
            .CE                 ( 1'b1                      ),
            .D                  ( gmii_rxd_dly[n]           ),
            .R                  ( 1'b0                      ),
            .S                  ( 1'b0                      )
        );
    end
endgenerate


IDDR #
(
    .DDR_CLK_EDGE               ( "SAME_EDGE"               ), //"OPPOSITE_EDGE",  "SAME_EDGE, "SAME_EDGE_PIPELINED"
    .INIT_Q1                    ( 1'b0                      ),
    .INIT_Q2                    ( 1'b0                      ),
    .SRTYPE                     ( "ASYNC"                   )
)                   
ddr_ctrl_in                 
(                   
    .Q1                         ( gmii_rxctrl[0]            ),
    .Q2                         ( gmii_rxctrl[1]            ),
    .C                          ( i_rgmii_rxc               ),
    .CE                         ( 1'b1                      ),
    .D                          ( i_rgmii_rxctl             ),
    .R                          ( 1'b0                      ),
    .S                          ( 1'b0                      )
);

 

GMII→RGMII方向:

    可选方法有:

    1.偏移接收时钟相位来实现正确采样数据的效果

    2.偏移数据来实现正确采样数据的效果

    本篇实现第2种方法,思路为:

    数据→oddr→odelay→IO管脚

    将GMII数据进入 oddr,把数据变成双沿,再进去odelay对其加入绝对延时,最后再输出到管脚。

     部分代码如下:

 

ODDRE1 #
(
    .IS_C_INVERTED              ( 1'b0                      ),  // Optional inversion for C
    .IS_D1_INVERTED             ( 1'b0                      ),  // Unsupported, do not use
    .IS_D2_INVERTED             ( 1'b0                      ),  // Unsupported, do not use
    .SIM_DEVICE                 ( "ULTRASCALE_PLUS"         ),  // Set the device version (ULTRASCALE, ULTRASCALE_PLUS,
                                                                // ULTRASCALE_PLUS_ES1, ULTRASCALE_PLUS_ES2)
    .SRVAL                      ( 1'b0                      )   // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
)
ODDR_txdv_inst 
(
    .Q                          ( o_rgmii_txctrl            ),  // 1-bit output: Data output to IOB
    .C                          ( i_clk                     ),  // 1-bit input: High-speed clock input
    .D1                         ( i_tvalid                  ),  // 1-bit input: Parallel data input 1
    .D2                         ( i_tvalid                  ),  // 1-bit input: Parallel data input 2
    .SR                         ( 1'b0                      )   // 1-bit input: Active High Async Reset
);


ODDRE1 #
(
    .IS_C_INVERTED              ( 1'b0                      ),  // Optional inversion for C
    .IS_D1_INVERTED             ( 1'b0                      ),  // Unsupported, do not use
    .IS_D2_INVERTED             ( 1'b0                      ),  // Unsupported, do not use
    .SIM_DEVICE                 ( "ULTRASCALE_PLUS"         ),  // Set the device version (ULTRASCALE, ULTRASCALE_PLUS,
                                                                // ULTRASCALE_PLUS_ES1, ULTRASCALE_PLUS_ES2)
    .SRVAL                      ( 1'b0                      )   // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
) 
ODDR_txc_inst 
(
    .Q                          ( o_rgmii_txc               ),  // 1-bit output: Data output to IOB
    .C                          ( i_clk_shift               ),  // 1-bit input: High-speed clock input
    .D1                         ( 1'b1                      ),  // 1-bit input: Parallel data input 1
    .D2                         ( 1'b0                      ),  // 1-bit input: Parallel data input 2
    .SR                         ( 1'b0                      )   // 1-bit input: Active High Async Reset
);


generate        
    for (n = 0; n < 4; n = n + 1)       
    begin: DDROUT_BLOCK      
        ODDRE1 #
        (
            .IS_C_INVERTED      ( 1'b0                      ),  // Optional inversion for C
            .IS_D1_INVERTED     ( 1'b0                      ),  // Unsupported, do not use
            .IS_D2_INVERTED     ( 1'b0                      ),  // Unsupported, do not use
            .SIM_DEVICE         ( "ULTRASCALE_PLUS"         ),  // Set the device version (ULTRASCALE, ULTRASCALE_PLUS,
                                                                // ULTRASCALE_PLUS_ES1, ULTRASCALE_PLUS_ES2)
            .SRVAL              ( 1'b0                      )   // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
        ) 
        ODDR_data_inst 
        (
            .Q                  ( tdata_dly[n]              ),  // 1-bit output: Data output to IOB
            .C                  ( i_clk                     ),  // 1-bit input: High-speed clock input
            .D1                 ( i_tdata[n]                ),  // 1-bit input: Parallel data input 1
            .D2                 ( i_tdata[n+4]              ),  // 1-bit input: Parallel data input 2
            .SR                 ( 1'b0                      )   // 1-bit input: Active High Async Reset
        );


        ODELAYE3 #
        (
           .CASCADE             ( "NONE"                    ),  // Cascade setting (MASTER, NONE, SLAVE_END, SLAVE_MIDDLE)
           .DELAY_FORMAT        ( "TIME"                    ),  // (COUNT, TIME)
           .DELAY_TYPE          ( "VAR_LOAD"                ),  // Set the type of tap delay line (FIXED, VARIABLE, VAR_LOAD)
           .DELAY_VALUE         ( 0                         ),  // Output delay tap setting
           .IS_CLK_INVERTED     ( 1'b0                      ),  // Optional inversion for CLK
           .IS_RST_INVERTED     ( 1'b0                      ),  // Optional inversion for RST
           .REFCLK_FREQUENCY    ( 307.2                     ),  // IDELAYCTRL clock input frequency in MHz (200.0-800.0).
           .SIM_DEVICE          ( "ULTRASCALE_PLUS"         ),  // Set the device version for simulation functionality (ULTRASCALE,
                                                                // ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1, ULTRASCALE_PLUS_ES2)
           .UPDATE_MODE         ( "ASYNC"                   )   // Determines when updates to the delay will take effect (ASYNC, MANUAL,
                                                                // SYNC)
        )
        ODELAYE3_inst 
        (
           .CASC_OUT            (                           ),  // 1-bit output: Cascade delay output to IDELAY input cascade
           .CNTVALUEOUT         ( o_odelay_cnt_value_out[n] ),  // 9-bit output: Counter value output
           .DATAOUT             ( o_rgmii_txd[n]            ),  // 1-bit output: Delayed data from ODATAIN input port
           .CASC_IN             ( 1'b0                      ),  // 1-bit input: Cascade delay input from slave IDELAY CASCADE_OUT
           .CASC_RETURN         ( 1'b0                      ),  // 1-bit input: Cascade delay returning from slave IDELAY DATAOUT
           .CE                  ( 1'd0                      ),  // 1-bit input: Active-High enable increment/decrement input
           .CLK                 ( i_clk                     ),  // 1-bit input: Clock input
           .CNTVALUEIN          ( odelay_cnt_value_in       ),  // 9-bit input: Counter value input
           .EN_VTC              ( odelay_en_vtc             ),  // 1-bit input: Keep delay constant over VT
           .INC                 ( 1'b1                      ),  // 1-bit input: Increment/Decrement tap delay input
           .LOAD                ( odelay_load               ),  // 1-bit input: Load DELAY_VALUE input
           .ODATAIN             ( tdata_dly[n]              ),  // 1-bit input: Data input
           .RST                 ( i_rst                     )   // 1-bit input: Asynchronous Reset to the DELAY_VALUE
        );
    end
endgenerate

 

注意:对于A7系列的FPGA,是没有odelay可以使用的。

另外:

    网上很多GMII转RGMII的方法,都会对添加时序约束,个人觉得,时序约束不是必要的,因为:

    设置 input delay 并不会影响 布局布线,设置 input delay只会告诉工具 端口进来的数据和 对应的时钟的相位关系,让工具去分析能不能满足时序而已。。 

只要你接了 IDDR,或者ODDR,那么就会直接使用了IOB里面的寄存器,那么你的 数据的端口到第一级寄存器 ,也就是 IOB的寄存器,是专用布线资源,那么走线就是固定的,无论你怎么改代码逻辑,或者策略都不影响 布局布线。 而 idelay ,odelay 和 IDDR, ODDR 一样,都是IOB里面的资源,idelay 只是 在你 布局布线出来的 固定 延时后,软件可以手动再加一段绝对延时上去,odelay也是一样,而你跑的A版本 调出来的,成功的 idelay延时,为什么会 在 改点逻辑 后跑出来的 B版本,使用相同的idelay延时值,会发现收到的数据不对,是因为。 

第一点:由于板卡温度的问题,例如高低温。

 第二点:电压波动的问题。 都会导致 你 本来调好的idelay延时值不对,而不对,也是 揭露 你之前 调的 idelay延时值,并没有 真正正好调到 时钟的边沿和数据处在中间的关系,容忍的窗口没那么大,所以 之前 调的 idelay延时值 并不是真正调好的值,需要重新再调。

    最后呢,关于idelay和odelay的原语,大家可以在xilinx手册ug571里面可以查看详细介绍,本UP使用的是"var load"模式一点点调的delay值。




审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分