首页 > 其他分享 >FPGA以太网学习-RGMII与GMII

FPGA以太网学习-RGMII与GMII

时间:2024-07-07 16:57:14浏览次数:15  
标签:tx FPGA clk RGMII rx gmii GMII 以太网 rgmii

以太网口都叫RJ45接口,从功能角度说,网口只是信号连接,本身没有通信能力。

PHY(物理层),这边需要一个芯片,将并行的以太网数据到符合以太网物理层链路数据传输格式的电平信号转换。

上图PHY右边是经过编码后的串行数据信号,左侧是提供多种并行信号。网络变压器连接串行信号和网口。

MII接口(百兆/十兆接口)

MAC向PHY侧传输数据的时序图如下

 


MII 介质无关接口

MAC 介质访问接口

RTL8211F-CG是一款支持 RGMII 接口的以太网物理层收发器,能够工作在 10M、100M 或 1000M Base模式,对 MAC 层可提供 RGMII 接口模式,并提供了标准的 MDIO 管理接口与处理器相连。

以太网物理层芯片都有一个器件地址,该器件地址就是在介绍 PHY 管理接口 MDIO 时所说的 PHY 器件地址,该器件地址用来由 MDIO 主机(如 MAC 或处理器)寻址 MDIO 总线上连接的指定的 PHY 芯片。

对于 RTL8211F-CG 芯片,该地址分为固定部分和硬件可设定两部分。器件地址共有 5 位,其中高 2 位为固定的 00,低三位通过三个地址设置脚在芯片上电或复位时候设置, 这三位分别对应 RTL8211F 芯片的 22、27、26 脚。下表为这三个脚功能说明。

注意:手册里下面这个话好像写错了,PHY_ADDR1通过电阻下拉到低电平,其他两个通过电阻上拉到高电平。

所以器件地址为00101.


以太网MAC帧协议介绍

以太网技术的正式标准是 IEEE802.3 标准,它规定了在以太网中传输的数据帧结构,如下图所示。

从目的地址开始到数据和填充字段的最后一个为止,这些跟CRC进行计算。不包括前同步码和分隔符。

前导码的作用是为了标记一帧以太网帧即将开始传输,分隔符存在的意义是指前导码传输完毕。

MC地址对应,一对一,多播,广播。MAC地址,每个设备都唯一,需要向美国IEEE申请。48位地址,前24位IEEE指定,后24位设备厂商自行定义。

以太网中的数据部分,一般都在另一个上层协议,如TCP/IP协议。用户数据都是包含在该上层协议中。


RGMII 接口信号与时序

这个RGMII和GMII的互转,是因为需要使用RGMII并行传输协议,RGMII是上下沿采样,所以是双倍于GMII的速度

在PHY和MAC层之间传输并行数据的方法。所以从PHY到MAC这边是RGMII转GMII,从MAC到PHY这边是GMII转RGMII

用下面这个回环测试的示意图能看出来。

1、网口连接电脑以太,从电脑这边发一个数据,经过PHY串转并,通过RGMII转GMII进入,然后输出来之后,给GMII转RGMII就行了

RGMII数据在时钟的上升沿和下降沿均进行采样。

数据边沿对齐的形式使得 PCB 设计变得更加的复杂,所以,后来的 PHY 芯片都针对 RGMII接口提供了可选的内部延迟电路。(暂时不理解)

RGMII调用FPGA的ODDR原语,实现输出DDR寄存器,实例化原语就会自动访问该功能。

端口信号

ODDR属性

 

直接看代码(写注释里面)

这个跟TB文件中的代码对应,

rgmii_to_gmii.v


module rgmii_to_gmii(
  reset,            //复位信号

  rgmii_rx_clk,     //接收数据参考时钟,由PHY输出过来的
  rgmii_rxd,        //PHY传向MAC的数据[3:0]
                    
  rgmii_rxdv, //在RX_CTL里面的,传输GMII中的RX_DV和RX_ER信号

  gmii_rx_clk,  //gmii的参考时钟,跟RGMII的对应就行了
  gmii_rxdv,    //Reveive Data Valid,接收数据有效信号,作用类似于发送通道的TX_EN
  gmii_rxd,    //接收数据
  gmii_rxer     //接收错误error,高电平有效
);



  input         reset;

  input         rgmii_rx_clk;
  input  [3:0]  rgmii_rxd;
  input         rgmii_rxdv;

  output        gmii_rx_clk;
  output [7:0]  gmii_rxd;
  output        gmii_rxdv;
  output        gmii_rxer;
  
  wire          gmii_rxer_r;
  

  assign gmii_rx_clk = rgmii_rx_clk;   //时钟狠狠同步

  genvar i;
  generate
    for(i=0;i<4;i=i+1)
    begin: rgmii_rxd_i
      IDDR #(
        .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" 
                                              // or "SAME_EDGE_PIPELINED" Q1Q2变化发生在同一个上升沿,都是下一个
        .INIT_Q1(1'b0   ),  // Initial value of Q1: 1'b0 or 1'b1,默认0
        .INIT_Q2(1'b0   ),  // Initial value of Q2: 1'b0 or 1'b1
        .SRTYPE ("SYNC" )   // Set/Reset type: "SYNC" or "ASYNC" ,这是默认
      ) IDDR_rxd (
        .Q1   (gmii_rxd[i]   ), // 1-bit output for positive edge of clock,低4位
        .Q2   (gmii_rxd[i+4] ), // 1-bit output for negative edge of clock,高4位
        .C    (rgmii_rx_clk  ), // 1-bit clock input,rgmii参考时钟
        .CE   (1'b1          ), // 1-breset_nit clock enable input
        .D    (rgmii_rxd[i]  ), // 上升沿传GMII的RXD的低4位,下降沿传高4位
        .R    (reset         ), // 1-bit reset
        .S    (1'b0          )  // 1-bit set,复位,高电平有效
      );
    end
  endgenerate

  IDDR #(
    .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE" 
                                          // or "SAME_EDGE_PIPELINED" 
                                          //Q1Q2变化发生在同一个上升沿,但是都是下一个上升沿
    .INIT_Q1(1'b0   ),  // Initial value of Q1: 1'b0 or 1'b1
    .INIT_Q2(1'b0   ),  // Initial value of Q2: 1'b0 or 1'b1
    .SRTYPE ("SYNC" )   // Set/Reset type: "SYNC" or "ASYNC" 
  ) IDDR_rxdv (
    .Q1   (gmii_rxdv), // 1-bit output for positive edge of clock
    .Q2   (gmii_rxer_r), // 1-bit output for negative edge of clock
    .C    (rgmii_rx_clk ), // 1-bit clock input
    .CE   (1'b1         ), // 1-breset_nit clock enable input
    .D    (rgmii_rxdv   ), // 1-bit DDR data input,这个就对应RX_CTL的,因为上升沿传DV,下降沿传ER
                            //
    .R    (reset        ), // 1-bit reset
    .S    (1'b0         )  // 1-bit set
  );

    assign gmii_rxer = gmii_rxer_r^gmii_rxdv; //异或,相异为1.
endmodule
rgmii_to_gmii_tb.v
 `timescale 1ns / 1ps
`define CLK_PERIOD 8

module rgmii_to_gmii_tb();
    reg  reset;
    wire gmii_rx_clk;
    
    reg [3:0] rx_byte_cnt;//接收比特计数
    
    wire [7:0] gmii_rxd;
    wire gmii_rxdv;
    wire gmii_rxer;
    wire rgmii_rx_clk;
    reg [3:0] rgmii_rxd;
    reg rgmii_rxdv;
    reg rx_clk;
    wire locked;
    
    rgmii_to_gmii rgmii_to_gmii(
        .reset(reset),
        .gmii_rx_clk(gmii_rx_clk),  
        .gmii_rxdv(gmii_rxdv),
        .gmii_rxd(gmii_rxd),
        .gmii_rxer(gmii_rxer),
    
        .rgmii_rx_clk(rgmii_rx_clk),
        .rgmii_rxd(rgmii_rxd),
        .rgmii_rxdv(rgmii_rxdv)  //RX_CTL
    );
    
    //对rgmii_rx_clk时钟进行90度相位调制,以保证该时钟到达IDDR时候
    //与rgmii_rxd和rgmii_rxdv信号成中心对齐关系。
    //其实就是为了能够在对rgmii_rx_clk上升沿和下降沿的时候,正好处在rgmii_rxd的值的中心位置采样。
      rx_pll rx_pll
   (
    // Clock out ports
    .clk_out1(rgmii_rx_clk),     // output clk_out1,90度相位调制
    // Status and control signals
    .reset(reset), // input reset
    .locked(locked),       // output locked,是时钟稳定的信号
   // Clock in ports
    .clk_in1(rx_clk)      // input clk_in1
    );

    //clock generate
    initial rx_clk = 1'b1;
    always #(`CLK_PERIOD/2)rx_clk = ~rx_clk;  //每4ns转,周期8ns
    
    always@(rx_clk or posedge reset)
    if(reset)
        rx_byte_cnt <= 4'd0;
    else if(rgmii_rxdv && locked)   //RX_CTL的
        rx_byte_cnt <= rx_byte_cnt + 1'b1;   //16个数据
    else
        rx_byte_cnt <= 4'd0;

    always@(*)
    begin
        case(rx_byte_cnt)
        16'd0  : rgmii_rxd = 12;
        16'd1  : rgmii_rxd = 7;
        16'd2  : rgmii_rxd = 9;
        16'd3  : rgmii_rxd = 6;
        16'd4  : rgmii_rxd = 11;
        16'd5  : rgmii_rxd = 15;
        16'd6  : rgmii_rxd = 0;
        16'd7  : rgmii_rxd = 8;
        16'd8  : rgmii_rxd = 4;
        16'd9  : rgmii_rxd = 2;
        16'd10 : rgmii_rxd = 5;
        16'd11 : rgmii_rxd = 1;
        16'd12 : rgmii_rxd = 3;
        16'd13 : rgmii_rxd = 10;
        16'd14 : rgmii_rxd = 14;
        16'd15 : rgmii_rxd = 13;
        endcase
    end

  initial
    begin
    reset = 1;
    rgmii_rxdv = 0;
    #201;
    reset = 0;   //因为PLL时钟的RESET信号,高电平有效
    rgmii_rxdv = 1;
    #2000; 
    $stop;
  end

endmodule

1、这边看到计数0的时候,RGMII_RXD的值是12(C),在RGMII_RX_CLK的上升沿传输低4位,下降沿传输高四位

2、以第一个GMII的数据为例,上升沿C,下降沿7,因为是SAME_EDGE_PIPELINED,所以Q1Q2在下一个上升沿发生变化,在GMII_RXD这边就是7C的呈现。

3、时钟偏90度相位,我的理解就是方便采样取值

 

下面是GMII转RGMII接口。

gmii_to_rgmii.v
 /*例化6个ODDR就可以实现,其中4个用来发送4个TXD信号
一个用来发送TXEN和TXER信号
一个用来输出TX_CLK信号

*/
module gmii_to_rgmii(
  reset_n,

  gmii_tx_clk,   //gmii发送参考时钟,mac提供
  gmii_txd,      //gmii_txd[7:0]
  gmii_txen,    //发送使能
  gmii_txer,    //发送错误信息

  rgmii_tx_clk,
  rgmii_txd,     
  rgmii_txen     //tx_ctl,上升沿传tx_en,下降沿传tx_er
);
  input        reset_n;

  input        gmii_tx_clk;
  input  [7:0] gmii_txd;
  input        gmii_txen;
  input        gmii_txer;
  
  output       rgmii_tx_clk;
  output [3:0] rgmii_txd;
  output       rgmii_txen;

  genvar i;  //generate必须是用这个genvar定义参数
  generate
    for(i=0;i<4;i=i+1)
    begin: rgmii_txd_o
      ODDR #(
        .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" 
        .INIT  (1'b0   ),          // Initial value of Q: 1'b0 or 1'b1
        .SRTYPE("SYNC" )          // Set/Reset type: "SYNC" or "ASYNC" 
      ) ODDR_rgmii_txd (
        .Q   (rgmii_txd[i]     ), // 输出RGMII的TXD
        .C   (gmii_tx_clk      ), // gmii的发送时钟
        .CE  (1'b1             ), // 使能
        .D1  (gmii_txd[i]      ), // GMII_TXD的低4位
        .D2  (gmii_txd[i+4]    ), // GMII_TXD的高4位
        .R   (~reset_n         ), // 复位,因为复位reset跟set,高电平有效,所以reset=0的时候,这边才复位
        .S   (1'b0             )  // 高电平有效,不设置
      );
    end
  endgenerate

  ODDR #(
    .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" 
    .INIT  (1'b0   ),           // Initial value of Q: 1'b0 or 1'b1
    .SRTYPE("SYNC" )            // Set/Reset type: "SYNC" or "ASYNC" 
  ) ODDR_rgmii_txd (
    .Q   (rgmii_txen          ), // 1-bit DDR output
    .C   (gmii_tx_clk         ), // 1-bit clock input
    .CE  (1'b1                ), // 1-bit clock enable input
    .D1  (gmii_txen           ), // 上升沿采集txen信号
    .D2  (gmii_txen^gmii_txer ), // RGMII的TX_CTL传输GMII的TX_EN 和 TX_ER 两种信息,TX_CLK 的上升沿发送 TX_EN,下降沿发送 TX_ER
    //.D2  (gmii_txer ),
    //所以这边D2应该GMII_TXER也可以,等会仿真看看
    .R   (~reset_n            ), // 1-bit reset
    .S   (1'b0                )  // 1-bit set
  );

//这边对于RGMII_TX_CLK的输出,也是用ODDR输出,而不是PLL产生。
//优势在于最大程度保证输出的RGMII_TX_CLK与RGMII_TXD[3:0]保持边缘对齐
//前面的TXD和CLK都能ODDR输出,可以保证传输路径的时序模型完全一致
  ODDR #(
    .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE" 
    .INIT  (1'b0   ),           // Initial value of Q: 1'b0 or 1'b1
    .SRTYPE("SYNC" )            // Set/Reset type: "SYNC" or "ASYNC" 
  ) ODDR_rgmii_clk (
    .Q   (rgmii_tx_clk  ), // 1-bit DDR output
    .C   (gmii_tx_clk   ), // 1-bit clock input
    .CE  (1'b1          ), // 1-bit clock enable input
    .D1  (1'b1          ), // 1-bit data input (positive edge)
    .D2  (1'b0          ), // 1-bit data input (negative edge)
    .R   (~reset_n      ), // 1-bit reset
    .S   (1'b0          )  // 1-bit set
  );

endmodule
gmii_to_rgmii_tb.v
 `timescale 1ns / 1ps
`define CLK_PERIOD 8

module gmii_to_rgmii_tb();
    reg  reset_n;
    reg gmii_tx_clk;
    reg [3:0] tx_byte_cnt;  //发送数据计数
    reg [7:0] gmii_txd;    //发送数据
    reg gmii_txen;
    reg gmii_txer;
    wire rgmii_tx_clk;
    wire [3:0] rgmii_txd;
    wire rgmii_txen;
    
    gmii_to_rgmii gmii_to_rgmii(
      .reset_n(reset_n),
      .gmii_tx_clk(gmii_tx_clk),
      .gmii_txd(gmii_txd),
      .gmii_txen(gmii_txen),
      .gmii_txer(gmii_txer),
    
      .rgmii_tx_clk(rgmii_tx_clk),
      .rgmii_txd(rgmii_txd),
      .rgmii_txen(rgmii_txen)
    );

        //clock generate
        //每4ns翻转一次,周期8ns
    initial gmii_tx_clk = 1'b1;
    always #(`CLK_PERIOD/2)gmii_tx_clk = ~gmii_tx_clk;
    
    always@(posedge gmii_tx_clk or negedge reset_n)
    if(!reset_n)
        tx_byte_cnt <= 4'd0;
    else if(gmii_txen) //如果gmii的发送使能,计数开始
        tx_byte_cnt <= tx_byte_cnt + 1'b1;
    else
        tx_byte_cnt <= 4'd0;

    always@(*)
    begin
        case(tx_byte_cnt)  //计数16次,分别赋值的发送数据
        16'd0  : gmii_txd = 25;
        16'd1  : gmii_txd = 34;
        16'd2  : gmii_txd = 18;
        16'd3  : gmii_txd = 96;
        16'd4  : gmii_txd = 78;
        16'd5  : gmii_txd = 29;
        16'd6  : gmii_txd = 63;
        16'd7  : gmii_txd = 42;
        16'd8  : gmii_txd = 51;
        16'd9  : gmii_txd = 74;
        16'd10 : gmii_txd = 39;
        16'd11 : gmii_txd = 60;
        16'd12 : gmii_txd = 27;
        16'd13 : gmii_txd = 36;
        16'd14 : gmii_txd = 145;
        16'd15 : gmii_txd = 231;
        endcase
    end

  initial
    begin
    reset_n = 0;
    gmii_txen = 0;
    gmii_txer = 0;
    #201;
    reset_n = 1;
    gmii_txen = 1;
    #2000; 
    $stop;
  end

endmodule

1、gmii_txd的第一个数据是19(其实是0001 1001)换算十进制就是25,用的SAME_EDGE,所以是时钟上升沿取D1D2(低四位高四位),然后依次在一个时钟周期内赋值给Q

2、可以看到RGMII_TXD是先9再1,而9是上面的低4位,1是上面的高4位,且是在同一个时钟周期。

标签:tx,FPGA,clk,RGMII,rx,gmii,GMII,以太网,rgmii
From: https://www.cnblogs.com/cjl520/p/18229632

相关文章

  • 基于FPGA的A律压缩解压缩verilog实现,包含testbench
    1.算法仿真效果VIVADO2019.2仿真结果如下(完整代码运行后无水印):   RTL图如下所示:   2.算法涉及理论知识概要       A律压缩是一种广泛应用于语音编码的非均匀量化技术,尤其在G.711标准中被欧洲和中国等国家采纳。该技术的核心目的是在有限的带宽下高效传输......
  • 利用 STM32 实现多协议物联网网关:Modbus/Zigbee 到以太网/Wi-Fi 的数据桥接
    摘要: 随着物联网技术的飞速发展,不同通信协议之间的互联互通成为了构建智能化系统的一大挑战。本文将以实战项目为例,详细介绍如何利用STM32微控制器实现Modbus/Zigbee与以太网/Wi-Fi之间的协议转换,从而打通传感器数据上传至服务器的“最后一公里”。关键词: STM32,协议......
  • 基于MCU和FPGA的DDS信号发生器——MCU与FPGA通信部分
    前言由于项目制作时间有限,考虑到改变方案的风险,我们在遇到许多问题时并没有选择改变路线,而是在现有成果上缝缝补补,造就了现在看来十分笨重的通信模块,不过错误也是宝贵的学习经验,对于电子领域的工作者更是如此,因而笔者保留了我们制作时的失误和思考历程,供广大读者参考借鉴。总......
  • FPGA加扰与仿真
    对加扰仿真,输出结果符合预期 仿真代码如下 modulescrambler_64bit(inputwireclk,inputwirerst,inputwire[63:0]data_in,outputreg[63:0]data_out);reg[63:0]state;always@(posedgeclkorposedgerst)beginif(rst)begin......
  • 课程设计——基于FPGA的双向移位寄存器
    基于FPGA的双向移位寄存器摘 要本文使用verilogHDL语言设计双向移位寄存器,使电路受外部信号控制,实现数字信号的双向移位等功能,其电路设计模块主要分为三个部分,分别为接受判断控制信号的组合逻辑电路部分、实现存储、运算和输出数据的时序逻辑电路部分以及时钟信号输入部分......
  • FPGA必修课—FIFO
    FIFO基本概念FIFO,全称为“FirstInFirstOut”,译为“先进先出”。这是一种常见的数据存储和处理原则,其基本含义在于数据的存取顺序:最先进入的数据将最先被取出。FIFO可以被视为一种特殊类型的数据缓冲区,它按照元素到达的顺序进行数据的存取操作。学习FIFO的重要性在于它在......
  • BPI-M4 Berry以太网口和WiFi测试
    以太网口测试1、首先将网线的一端插入M4Berry的以太网接口,另一端接入路由器,并确保网络是畅通的。2、系统启动后会通过DHCP自动给以太网卡分配IP地址。可以输入以下命令查看ip:ifconfig或者ipa 可以看到eth0获取到了10.1.1.207的ip地址接下来,ping一下百度测试网......
  • IEEE 8802-3 以太网标准解读
     PHY:CarrierSense载波侦听ReceveDataValid接受数据有效CollisionDetect碰撞检测Transmitting传输中TransmitBit传输比特  SFD10101011开始ReceiveBit 接受比特Wait等待 1、MA_DATA.request定义了MAC客户端访问单独实体或者z在组地址的前提下访问多个......
  • 以太网协议的工作原理
    注:机翻,未校对。HowtheEthernetProtocolWorks–ACompleteGuide以太网协议的工作原理指南Whetheryou’vebeenawareofitornot,you’veprobablyusedtheEthernetinthepast.Doesthiscablelookfamiliar?无论您是否意识到这一点,使用过以太网的您,这......
  • FPGA学习网站推荐
    FPGA学习网站推荐本文首发于公众号:FPGA开源工坊引言FPGA的学习主要分为以下两部分语法领域内知识做FPGA开发肯定要首先去学习相应的编程语言,FPGA开发目前在国内采用最多的就是使用Verilog做开发,其次还有一些遗留下来的项目会采用VHDL做开发,现在有一部分公司也开始使用Syst......