基于FPGA 以太网gmii_to_rgmii模块编写 附源码

笔者使用的开发板是米联客zynq UitraScale+ xczu4ev-sfvc784-2-i开发板进行测试

由于米联客协议族源码不开源,自己写了一个简易的以太网接口转换模块只支持1000M速率。

一、接口框图:(引用原子哥)

二、RGMII接口时序简介:

(1)接收时序(PHY>FPGA)

RXC 的上下边沿与 RXD 和 RX_CTL 信号对齐,相位相同。(非延时模式)

RXC 的上下边沿与 RXD 和 RX_CTL 信号的中间位置对齐,相位相差90度(延时模式)

RXC 的时钟周期为 8ns,单个高电平或者低电平为 4ns, RXC 相对于 RXD 和 RX_CTL 延时约 2ns。

(2)发送时序(FPGA>PHY)

TXC 的上下边沿与 TXD 和 TX_CTL 信号对齐,相位相同。 (非延时模式)

RGMII 发送端口在 TXC 时钟的上升沿传输 TXD 的低 4 位和 TX_CTL 的使能信号;

下降沿传输 TXD 的高 4 位和 TX_CTL 的错误信号(实际上是使能信号和错误信号的异或值); RGMII 接收端口在RXC 时钟的上升沿传输 RXD 的低 4 位和 RX_CTL 的使能信号;

下降沿传输RXD 的高 4 位和RX_CTL 的错误信号(实际上是使能信号和错误信号的异或值)。(延时模式)

注意:笔者芯片接收时序为延时模式,发送时序为正常模式,具体模式需参考相应原理图引脚上下拉判断,也可通过MDIO配置。

三、原语使用

UitraScale+系列的开发板原语使用和7系列大不相同,需要了解IDDRE1、ODDRE1、BUFG、BUFIO、IDELAYE3 、 ODELAYE3。在本次测试中并未使用IDELAYE3 、 ODELAYE3不做研究,原因是其phy芯片rxd端采用延时模式txd采用非延时模式(输出我们采用pll的方法相位偏移90度)。

//全局缓冲, BUFG 的输出到达 FPGA 内部的 IOB、 CLB、块 RAM 的时钟延迟和抖动最小。
BUFG BUFG_inst (
  .I            (rgmii_rxc),     // 1-bit input: Clock input
  .O            (rgmii_clk_buf) // 1-bit output: Clock output
);
//BUFIO 是 IO 时钟网络,其独立于全局时钟资源,适合采集源同步数据。它只能驱动 IO Block 里面的逻辑,不能驱动  CLB 里面的 LUT, REG 等逻辑。
BUFIO BUFIO_inst (
  .I            (rgmii_rxc),      // 1-bit input: Clock input
  .O            (rgmii_rxc_bufio) // 1-bit output: Clock output
);
//将输入的上下边沿 DDR 信号,转换成两位单边沿 SDR 信号。 
IDDRE1 #(
    .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED)模式选择一般选择SAME_EDGE_PIPELINED模式
    .IS_CB_INVERTED(1'b1),          // Optional inversion for CB,高速时钟CB反向使能
    .IS_C_INVERTED(1'b0)            // Optional inversion for C,高速时钟C反向不使能
 )
 IDDRE1_inst (
    .Q1(w_gmii_rx_dv), // 1-bit output: Registered parallel output 1输出低4位
    .Q2(w_rgmii_rx_ctl), // 1-bit output: Registered parallel output 2输出高4位
    .C(rgmii_rxc_bufio),   // 1-bit input: High-speed clock高速时钟输入
    .CB(rgmii_rxc_bufio), // 1-bit input: Inversion of High-speed clock C高速时钟c的反转
与这个IS_CB_INVERTED参数互斥
    .D(rgmii_rx_ctl),   // 1-bit input: Serial Data Input8位数据输入
    .R(1'b0)    // 1-bit input: Active-High Async Reset置位/复位信号,高有效
 );
//把两路单端的数据合并到一路上输出,上下沿同时输出数据,上升沿输出 a路,下降沿输出 b 路
ODDRE1 #(
    .IS_C_INVERTED     (1'b0                   ),  // Optional inversion for C c的反转
    .IS_D1_INVERTED    (1'b0                   ),  // Unsupported, do not use无用默认
    .IS_D2_INVERTED    (1'b0                   ),  // Unsupported, do not use无用默认
    .SIM_DEVICE("ULTRASCALE"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,设备版本选择
    .SRVAL             (1'b0                   )   // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)初始化ODDRE1触发器到指定值(1'bo, 1'b1)
    )
    u_rgmii_tx_ctl 
    (
         .Q                 (rgmii_tx_ctl           ),   // 1-bit output: Data output to IOB 8位数据输出
         .C                 (gmii_tx_clk            ),   // 1-bit input: High-speed clock input高速时钟输入
         .D1                (gmii_tx_en             ), // 1-bit input: Parallel data input 1输入数据低4位
         .D2                (gmii_tx_er             ), // 1-bit input: Parallel data input 2输入数据高4位
         .SR                (0                      )    // 1-bit input: Active High Async Reset置位/复位信号,高有效
);

四、源码

代码写法有很多笔者也参考了很多厂商dmo有误请在评论区指正

module gmii_rgmii(
    input     wire                    rgmii_rxc        ,//输入时钟
    input     wire    [3:0]           rgmii_rxd        ,//输入数据
    input     wire                    rgmii_rx_ctl     ,//输入控制

    output    wire    [3:0]           rgmii_txd        ,//输出数据
    output    wire                    rgmii_tx_ctl     ,//输出控制
    output    wire                    rgmii_txc        ,//发送时钟

    output    wire                    gmii_rx_clk      ,//输出接收时钟
    output    wire     [7:0]          gmii_rxd         ,//输入8位数据
    output    wire                    gmii_rx_dv       ,//输出数据有效

    input     wire    [7:0]           gmii_txd         ,//输入8位数据
    input     wire                    gmii_tx_en       ,//输入发送使能高有效
    input     wire                    gmii_tx_er       ,//输入数据是否有误高有效
    output    wire                    gmii_tx_clk       //输出发送时钟
 );
 
     wire                     w_gmii_rx_dv   ;
     wire                     w_rgmii_rx_ctl ;
     wire                     rgmii_rxc_bufio;
     wire                     rgmii_clk_buf  ;

 /*              Rgmii rx                  */
 

BUFG BUFG_inst (
  .I            (rgmii_rxc),     // 1-bit input: Clock input
  .O            (rgmii_clk_buf) // 1-bit output: Clock output
);

BUFIO BUFIO_inst (
  .I            (rgmii_rxc),      // 1-bit input: Clock input
  .O            (rgmii_rxc_bufio) // 1-bit output: Clock output
);
assign gmii_rx_clk = rgmii_clk_buf;
assign gmii_tx_clk = rgmii_clk_buf;
assign gmii_rx_dv  = (w_gmii_rx_dv & w_rgmii_rx_ctl);

IDDRE1 #(
    .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED)
    .IS_CB_INVERTED(1'b1),          // Optional inversion for CB
    .IS_C_INVERTED(1'b0)            // Optional inversion for C
 )
 IDDRE1_inst (
    .Q1(w_gmii_rx_dv), // 1-bit output: Registered parallel output 1
    .Q2(w_rgmii_rx_ctl), // 1-bit output: Registered parallel output 2
    .C(rgmii_rxc_bufio),   // 1-bit input: High-speed clock
    .CB(rgmii_rxc_bufio), // 1-bit input: Inversion of High-speed clock C
    .D(rgmii_rx_ctl),   // 1-bit input: Serial Data Input
    .R(1'b0)    // 1-bit input: Active-High Async Reset
 );

genvar i;
generate
    for (i = 0; i < 4; i = i + 1) 
    begin : rxdata_bus
        IDDRE1 #(
            .DDR_CLK_EDGE     ("SAME_EDGE_PIPELINED"   ),  // IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED)
            .IS_CB_INVERTED   (1'b1                    ),  // Optional inversion for CB
            .IS_C_INVERTED    (1'b0                    )   // Optional inversion for C
        )
        u_rgmii_rxd
        (
            .Q1               (gmii_rxd[i]             ),  // 1-bit output: Registered parallel output 1
            .Q2               (gmii_rxd[i+4]           ),  // 1-bit output: Registered parallel output 2
            .C                (rgmii_rxc_bufio         ),  // 1-bit input: High-speed clock
            .CB               (rgmii_rxc_bufio         ),  // 1-bit input: Inversion of High-speed clock C
            .D                (rgmii_rxd[i]            ),  // 1-bit input: Serial Data Input
            .R                (0                       )   // 1-bit input: Active High Async Reset
        );
    end
 endgenerate
   
 /*                         Rgmii tx                        */
 
assign rgmii_txc=gmii_tx_clk;

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"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,
    .SRVAL             (1'b0                   )   // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
    )
    u_rgmii_tx_ctl 
    (
         .Q                 (rgmii_tx_ctl           ),   // 1-bit output: Data output to IOB
         .C                 (gmii_tx_clk            ),   // 1-bit input: High-speed clock input
         .D1                (gmii_tx_en             ),   // 1-bit input: Parallel data input 1
         .D2                (gmii_tx_er             ),   // 1-bit input: Parallel data input 2
         .SR                (0                      )    // 1-bit input: Active High Async Reset
);
    
genvar j;
generate
    for (j = 0; j < 4; j = j + 1)
     begin: txdata_bus
        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"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,
            .SRVAL             (1'b0                   )   // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1)
        )
        u_rgmii_txd
        (
            .Q                 (rgmii_txd[j]           ),  // 1-bit output: Data output to IOB
            .C                 (gmii_tx_clk            ),  // 1-bit input: High-speed clock input
            .D1                (gmii_txd[j]            ),  // 1-bit input: Parallel data input 1
            .D2                (gmii_txd[4+j]          ),  // 1-bit input: Parallel data input 2
            .SR                (0                      )   // 1-bit input: Active High Async Reset
        );
    end
endgenerate
   
endmodule

pll资源配置:

顶层例化使用

//MMCM/PLL偏移90度
clk_wiz_0 u_clk_wiz(
  // Clock out ports
  .clk_out1   (clk_125m_deg),       // output clk_out1
  // Status and control signals
  //.reset      (~sys_rst_n  ),       // input reset
  .locked     (locked      ),       // output locked
 // Clock in ports
  .clk_in1    (rgmii_txc   )        // input clk_in1
  );  

五、仿真

有点乱码不影响查看

`timescale 1ns / 1ps
module gmii_rgmii_tb();
    reg                         rgmii_rxc        ;//閹恒儲鏁归弮鍫曟�?
    reg                         clk             ;//婢跺秳锟??
    wire       [3:0]            rgmii_txd        ;//閸欐埊锟???閿熻姤鏆熼敓??
    wire                        rgmii_tx_ctl     ;//閺佺増宓�?幒褍�???
    wire                        rgmii_txc        ;//閸欐埊锟???閿熻姤妞傞敓??
    reg         [3:0]           rgmii_rxd        ;//閹恒儲鏁归弫鐗堝�?
    reg                         rgmii_rx_ctl     ;//閹恒儲鏁归弫鐗堝祦閹貉冨�?
    wire                        gmii_rx_clk      ;//閺佺増宓佺憴锝嗭�??锟介幒銉︽暪閺冨爼锟??
    reg         [7:0]           gmii_txd         ;//鏉堟挸鍙嗛弫鐗堝�?
    reg                         gmii_tx_en       ;//閺佺増宓佹担鑳�?
    reg                         gmii_tx_er       ;//閺佺増宓�?柨娆掝嚖
    wire                        gmii_tx_clk      ;//閺佺増宓�?崣鎴嫹?閿熻姤妞傞敓??
    wire         [7:0]          gmii_rxd         ;//閹恒儲鏁归弫鐗堝�?
    wire                        gmii_rx_dv       ;//閹恒儲鏁归弫鐗堝祦閺堝�???
 
 initial begin 
     clk=0;
     rgmii_rxc=0;rgmii_rxd=4'd1;gmii_txd=8'b1;
 end
 
 always #8  rgmii_rxc=!rgmii_rxc;
 always #4  clk=!clk;
 initial begin
     rgmii_rx_ctl=1;gmii_tx_en=1'b1;gmii_tx_er=1'b1;
     #100;
 end
 
 always@(posedge rgmii_rxc)begin
     #1
     if(gmii_txd==15)gmii_txd=0;
     else
     gmii_txd=gmii_txd+1'b1;
 end
 
 always@(posedge clk )begin
     #1
     if(rgmii_rxd==40)rgmii_rxd=0;
     else
     rgmii_rxd=rgmii_rxd+1'b1;
 end
 
 gmii_rgmii gmii_rgmii(
    /*input     wire                   */. rgmii_rxc       (rgmii_rxc) ,//閹恒儲鏁归弮鍫曟�?
    /*output    wire    [3:0]          */. rgmii_txd       (rgmii_txd) ,//閸欐埊锟???閿熻姤鏆熼敓??
    /*output    wire                   */. rgmii_tx_ctl    (rgmii_tx_ctl) ,//閺佺増宓�?幒褍�???
    /*output    wire                   */. rgmii_txc       (rgmii_txc) ,//閸欐埊锟???閿熻姤妞傞敓??
    /*input     wire    [3:0]          */. rgmii_rxd       (rgmii_rxd) ,//閹恒儲鏁归弫鐗堝�?
    /*input     wire                   */. rgmii_rx_ctl    (rgmii_rx_ctl) ,//閹恒儲鏁归弫鐗堝祦閹貉冨�?
 
    /*output    wire                   */. gmii_rx_clk     (gmii_rx_clk) ,//閺佺増宓佺憴锝嗭�??锟介幒銉︽暪閺冨爼锟??
    /*input     wire    [7:0]          */. gmii_txd        (gmii_txd) ,//鏉堟挸鍙嗛弫鐗堝�?
    /*input     wire                   */. gmii_tx_en      (gmii_tx_en) ,//閺佺増宓佹担鑳�?
    /*input     wire                   */. gmii_tx_er      (gmii_tx_er) ,//閺佺増宓�?柨娆掝嚖
    /*output    wire                   */. gmii_tx_clk     (gmii_tx_clk) ,//閺佺増宓�?崣鎴嫹?閿熻姤妞傞敓??
    /*output    reg     [7:0]          */. gmii_rxd        (gmii_rxd) ,//閹恒儲鏁归弫鐗堝�?
    /*output    reg                    */. gmii_rx_dv      (gmii_rx_dv)  //閹恒儲鏁归弫鐗堝祦閺堝�???
 );
 
 endmodule
 

注意:发送时钟并未体现数据中间采样是因为时钟输出采用pll使相位偏移90度在顶层实现

对了由于是高速接口需添加时序约束。时钟是由PHY芯片产生的125Mhz时钟需要添加引脚输入的主时钟约束到.xdc文件中(注意放在引脚约束前)如下:

create_clock -period 8.000 -name rgmii_rxc [get_ports rgmii_rxc]

六、板级验证

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值