哈咯大家好,今天带来一篇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值。
审核编辑:刘清
评论
查看更多