? ? 哈咯大家好,今天帶來一篇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值。
審核編輯:劉清
評論