问答 店铺
热搜: ZYNQ FPGA discuz

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

微信扫一扫 分享朋友圈

已有 76 人浏览分享

开启左侧

UDP三速以太网设计

[复制链接]
76 0
安路-FPGA课程
安路课程: 通信方案 » UDP通信原理以及三速UDP(支持巨帧)协议栈实现详解
安路系列: EG4
本帖最后由 UT发布 于 2025-4-3 15:00 编辑

软件版本:TD_5.6.4_Release_97693
操作系统:WIN10 64bit
硬件平台:适用安路(Anlogic)FPGA
登录米联客FPGA社区-www.uisrc.com视频课程、在线答疑!
1 以太网PHY芯片介绍

使用以太网协议进行数据传输时,经常需要将数据进行编解码和数模转换才能让其在物理介质(网线)上传输,这些功能被定义在在OSI模型中的物理层中,通常FPGA芯片不具备这样的功能,这就需要使用外部的PHY芯片来完成这些功能。常见的千兆以太网PHY芯片有YT8531RTL821188E1518等,YT8531芯片的结构功能框图如下图所示。

image.jpg

以太网MAC层通过RGMII接口和PHY芯片的PCS层连接,PCSPhysical Coding Sublayer,物理编码子层)主要完成数据的编码/解码、加扰/解扰。PMAPhysical Medium Attachment,物理媒介子层)在发送端通过DAC模块完成数据的数模转换,在接收端具有时钟恢复、均衡器、ADC等功能模块。在PHY芯片上有一组内部寄存器的配置接口MDIOManagement Data Input/Output Interface)和MDC,通过一定的时序规范可以读取PHY芯片内部寄存器的配置信息,或者将配置信息写入PHY芯片中。LED控制器可以通过控制LED的亮灭指示当前以太网协商的速率和工作状态。

PHY芯片的一些功能需要通过引脚的硬件连接来配置,YT8531的引脚功能如下图所示。

image.jpg

PHYAD引脚控制PHY芯片的物理地址,与IIC总线类似,一组MDIO总线可以连接多个设备,每个设备都需要设置独立的地址,主机利用这个地址对不同的设备进行访问。通过4.7千欧的电阻对TXDLYRXDLY进行上拉或下拉,可以控制PHY芯片是否对发送时钟和接收时钟进行延时,对1000Mbps速率,上拉会给时钟端2ns延时,以让时钟的边沿处于数据的中心,更容易采集到稳定的数据。CFG_EXT引脚控制是否开启芯片内部的LDO电源供电,CFG_LDO0CFG_LDO1引脚控制IO电压标准。

一般PHY芯片仅通过硬件连接配置即可正常使用,若还需要修改其它功能如速率、半双工等,需要使用软件配置的方法,即通过MDIO接口配置PHY寄存器。PHY寄存器的地址空间为5位,802.3协议中规定了地址为0-15的寄存器功能,其余寄存器可由厂商自定义。寄存器0PHY控制寄存器,该寄存器的功能如下图所示。

image.jpg

Reset:软件复位,将位15设置为1来复位PHY芯片,复位过程中该位保持为1,复位完成后自动清零,复位完成前PHY芯片不接受写入寄存器的操作。

Loopback:位14默认为0,当设置为1PHY芯片为回环模式,从MAC发送过来的数据会通过PCS端直接回环到MAC接收端,通常应用于故障诊断中。

Autoneg_En:位12为自动协商使能,设置为1时端口的工作模式通过和对端进行自动协商来确定,设置为0时通过其它位的相应配置来确定。

Speed_Selection:位13和位6联合控制端口的速率,前提是自动协商关闭,该设置才起作用。位13为低位,位6为高位,Speed_Selection设置为2b00时,端口速率为10Mbps,设置为2b01时,端口速率为100Mbps,设置为2b10时,端口速率为1000Mbps2b11保留。

Duplex_Mode:位8控制端口是全双工模式还是半双工模式,需要自动协商关闭该设置才起作用。

Power_Down:位11设置为1时,PHY芯片掉电,当从掉电中恢复时,会进行软件复位和自动协商。

通过配置寄存器0就能将PHY配置成我们想要的工作状态,其它寄存器的功能可以在手册中查看。

2 MDIO接口设计
2.1 MDIO时序介绍

MDIO接口在协议802.3中定义,主要用于以太网的MACPHY之间,MAC层器件通过MDIO配置PHY芯片的工作模式或者获取当前的工作状态。MDIO又称为SMISerial Management Interface,串行管理接口),由MDIO数据线和MDC时钟线两条信号线组成,是串行半双工的数据接口,其接口通信协议规范如下图所示。

image.jpg

Preamble32位前导码,连续发送32位高电平用于PHY芯片同步。

ST2b01作为数据开始的标志。

OP2位操作码,2b10时表示读操作,2b01时表示写操作。

PHYADPHY硬件地址,高2位默认为2b00,低3位由PHY芯片引脚的上下拉控制,通过该字段决定和哪个PHY芯片通信。

REGAD:寄存器地址,用于设置该次操作的寄存器对象。

TA:空闲时间,防止总线上产生竞争,在写操作时,FPGA驱动信号线传输2b10。读操作时,FPGA释放总线,将MDIO驱动成高阻态,在传输第一个比特时,PHY芯片也驱动信号线为高阻态,传输第二个比特时,PHY芯片接过总线控制权,将信号线驱动为低电平。

DATA16位数据,写操作时由FPGA驱动,读操作时由PHY芯片驱动。

PHY芯片硬件地址为0x01为例,MDIO接口的读写时序如下图所示。

image.jpg
image.jpg

MDC时钟由FPGA侧驱动,时钟下降沿更新数据,上升沿采样数据。当总线处于空闲状态或一次读写操作完成时,FPGAPHY芯片都必须释放总线,即将MDIO信号线驱动为高阻态。

2.2 MDIO程序设计

MDIO驱动器主要包含MDIO收发数据状态机、MDC时钟分频器、移位发送模块、移位接收模块、总线忙控制模块。MDIOMDC的输出逻辑和时序通过状态机控制。

image.jpg

接口信号功能如下:

IO_phy_mdioMDIO双向数据线。

O_phy_mdcMDC时钟线。

I_reg_addr为操作的寄存器地址。

I_phy_addrPHY的物理地址。

I_wr_data为写入寄存器的数据。

O_rd_data为从寄存器读出的数据。

I_mdio_mode为总线的操作模式,置1时为写操作,置0时为读操作。

I_mdio_req为握手请求信号。

O_mdio_busy为握手响应信号,当空闲状态接收到请求时,该信号拉高,直到一次操作完成后,该信号拉低,等待下一次握手请求信号。

MDIO控制器中的状态机跳转图如下图所示。

image.jpg

IDLE:空闲状态,状态机处于空闲状态时,将总线置为高阻态,当接收到REQ请求时,开始一次操作,进入PREAMBLE状态。

PREAMBLE:发送前导码,发送32bit的逻辑1,用于同步,发送完成后进入INFO状态

INFO:发送开始标志、操作码、物理地址、寄存器地址共14bit数据,发送完成后进入TA状态。

TA2个时钟周期用来转换总线控制权。

RD_DATA:将总线上的串行数据转成16bit并行数据。

WR_DATA:将需要写入寄存器的数据由并行转成串行输出。

PAUSE:一次操作完成后暂停一段时间间隔,才能允许下一次操作。

  1. module  uimdio_ctrl#(
  2.     parameter   DIV_NUM     =   25
  3. )
  4. (   input   wire                I_clk           ,//主时钟
  5.     input   wire                I_rstn          ,//复位,低有效
  6.     input   wire    [4:0]       I_phy_addr      ,//硬件地址
  7.     input   wire    [4:0]       I_reg_addr      ,//寄存器地址
  8.     input   wire                I_mdio_req      ,//操作请求
  9.     input   wire                I_mdio_mode     ,//操作模式,0write,1read
  10.     input   wire    [15:0]      I_wr_data       ,//写寄存器数据
  11.     output  reg     [15:0]      O_rd_data       ,//读寄存器数据
  12.     output  wire                O_mdio_busy     ,//操作忙标志
  13.     output  reg                 O_mdio_done     ,//读完成标志
  14.     output  wire                O_phy_mdc       ,//MDIO时钟线
  15.     inout   wire                IO_phy_mdio     //MDIO数据线
  16. );
  17. localparam  IDLE        =   0;//空闲
  18. localparam  PREAMBLE    =   1;//前导码
  19. localparam  INFO        =   2;//START + OP + PHYADDR + REGADDR
  20. localparam  TA          =   3;//控制权转换
  21. localparam  WR_DATA     =   4;//写寄存器
  22. localparam  RD_DATA     =   5;//读寄存器
  23. localparam  PAUSE       =   6;//两次操作间隔
  24. reg     [2:0]   cur_st      ;
  25. reg     [2:0]   nxt_st      ;
  26. reg     [5:0]   st_cnt      ;
  27. reg             div_clk     ;
  28. reg     [15:0]  div_cnt     ;
  29. reg             mdio_out    ;
  30. reg             mdio_en     ;
  31. assign  O_mdio_busy =   ~(cur_st == IDLE);
  32. assign  O_phy_mdc       =   ~div_clk;//时钟反转,数据在时钟的下降沿跳变
  33. assign  IO_phy_mdio     =   mdio_en ? mdio_out : 1'bz;
  34. //时钟分频计数器
  35. always@(posedge I_clk or negedge  I_rstn) begin
  36.     if(!I_rstn) begin
  37.         div_cnt <=  0;
  38.         div_clk <=  0;
  39.     end
  40.     else if(div_cnt < DIV_NUM - 1) begin
  41.         div_cnt <=  div_cnt + 1;
  42.         div_clk <=  div_clk;
  43.     end
  44.     else begin
  45.         div_cnt <=  0;
  46.         div_clk <=  ~div_clk;
  47.     end
  48. end
  49. //状态机计数器
  50. always@(posedge div_clk or negedge I_rstn) begin
  51.     if(!I_rstn)
  52.         st_cnt  <=  0;
  53.     else if(nxt_st != cur_st)
  54.         st_cnt  <=  0;
  55.     else
  56.         st_cnt  <=  st_cnt + 1;
  57. end
  58. //三段式状态机
  59. always@(posedge div_clk or negedge I_rstn) begin
  60.     if(!I_rstn)
  61.         cur_st  <=  IDLE;
  62.     else
  63.         cur_st  <=  nxt_st;
  64. end
  65. always@(*) begin
  66.     if(!I_rstn)
  67.         nxt_st  <=  IDLE;
  68.     else begin
  69.         case(cur_st)
  70.             IDLE:begin
  71.                 if(I_mdio_req)
  72.                     nxt_st  <=  PREAMBLE;
  73.                 else
  74.                     nxt_st  <=  IDLE;
  75.             end
  76.             PREAMBLE:begin
  77.                 if(st_cnt == 'd31)
  78.                     nxt_st  <=  INFO;
  79.                 else
  80.                     nxt_st  <=  PREAMBLE;
  81.             end
  82.             INFO:begin
  83.                 if(st_cnt == 'd13)
  84.                     nxt_st  <=  TA;
  85.                 else
  86.                     nxt_st  <=  INFO;
  87.             end
  88.             TA:begin
  89.                 if(st_cnt == 'd1)
  90.                     if(I_mdio_mode)
  91.                         nxt_st  <=  WR_DATA;
  92.                     else
  93.                         nxt_st  <=  RD_DATA;
  94.                 else
  95.                     nxt_st  <=  TA;
  96.             end
  97.             WR_DATA:begin
  98.                 if(st_cnt == 'd15)
  99.                     nxt_st  <=  PAUSE;
  100.                 else
  101.                     nxt_st  <=  WR_DATA;
  102.             end
  103.             RD_DATA:begin
  104.                 if(st_cnt == 'd16)
  105.                     nxt_st  <=  PAUSE;
  106.                 else
  107.                     nxt_st  <=  RD_DATA;
  108.             end
  109.             PAUSE:begin
  110.                 if(st_cnt == 'd31)
  111.                     nxt_st  <=  IDLE;
  112.                 else
  113.                     nxt_st  <=  PAUSE;
  114.             end
  115.             default:begin
  116.                 nxt_st  <=  IDLE;
  117.             end
  118.         endcase
  119.     end
  120. end
  121. always@(posedge div_clk or negedge I_rstn) begin
  122.     if(!I_rstn)
  123.         mdio_out    <=  0;
  124.     else if(cur_st == PREAMBLE)
  125.         mdio_out    <=  1;
  126.     else if(cur_st == INFO) begin
  127.         case(st_cnt)
  128.             'd0     :mdio_out   <=  0;
  129.             'd1     :mdio_out   <=  1;
  130.             'd2     :mdio_out   <=  I_mdio_mode ? 0 : 1;
  131.             'd3     :mdio_out   <=  I_mdio_mode ? 1 : 0;
  132.             'd4     :mdio_out   <=  I_phy_addr[4];
  133.             'd5     :mdio_out   <=  I_phy_addr[3];
  134.             'd6     :mdio_out   <=  I_phy_addr[2];
  135.             'd7     :mdio_out   <=  I_phy_addr[1];
  136.             'd8     :mdio_out   <=  I_phy_addr[0];
  137.             'd9     :mdio_out   <=  I_reg_addr[4];
  138.             'd10    :mdio_out   <=  I_reg_addr[3];
  139.             'd11    :mdio_out   <=  I_reg_addr[2];
  140.             'd12    :mdio_out   <=  I_reg_addr[1];
  141.             'd13    :mdio_out   <=  I_reg_addr[0];
  142.         endcase
  143.     end
  144.     else if(cur_st == TA) begin
  145.         case(st_cnt)
  146.             'd0     :mdio_out   <=  I_mdio_mode ? 1 : 1'bz;
  147.             'd1     :mdio_out   <=  I_mdio_mode ? 0 : 1'bz;
  148.         endcase
  149.     end
  150.     else if(cur_st == WR_DATA)
  151.         mdio_out    <=  I_mdio_mode ? I_wr_data[15 - st_cnt] : 0;
  152.     else
  153.         mdio_out    <=  0;
  154. end
  155. always@(posedge div_clk or negedge I_rstn) begin
  156.     if(!I_rstn)
  157.         mdio_en <=  0;
  158.     else if(cur_st == IDLE || cur_st == PAUSE)
  159.         mdio_en <=  0;
  160.     else if(cur_st == TA)
  161.         mdio_en <=  I_mdio_mode ? 1 : 0;
  162.     else if(cur_st == PREAMBLE)
  163.         mdio_en <=  1;
  164.     else
  165.         mdio_en <=  mdio_en;
  166. end
  167. always@(posedge div_clk or negedge I_rstn) begin
  168.     if(!I_rstn)
  169.         O_mdio_done <=  0;
  170.     else if(cur_st == RD_DATA && st_cnt == 'd16)
  171.         O_mdio_done <=  1;
  172.     else
  173.         O_mdio_done <=  0;
  174. end
  175. //MDIO读数据,使用mdc时钟采样
  176. always@(posedge O_phy_mdc or negedge I_rstn) begin
  177.     if(!I_rstn)
  178.         O_rd_data   <=  0;
  179.     else if(cur_st == RD_DATA && st_cnt >= 1)
  180.         O_rd_data   <=  {O_rd_data[14:0], IO_phy_mdio};
  181.     else
  182.         O_rd_data   <=  O_rd_data;
  183. end
  184. endmodule
复制代码

为了方便操作MDIO驱动器的读写,需要增加一级模块封装,该模块完成对请求信号及操作类型的控制,来对PHY寄存器进行配置。在PHY寄存器配置完成后,通过按键可以进行读操作,将寄存器的配置信息读取出来。

image.jpg

在上电后等待一定的时间间隔,释放复位,通过握手信号控制驱动器的工作状态,当busy信号为0时,拉高req信号,直到busy信号为高,代表驱动器响应成功,直到busy再次拉低,进行下一次操作。通过计数器控制写操作执行的次数,当达到指定次数时,寄存器配置完成,拉高cfgdone信号,表示不再继续进行写操作。

第一次写操作在bit11的位置写入1,让PHY芯片掉电,第二次写操作再写入速度模式和全双工模式。

  1. module  uiphy_cfg#(
  2.     parameter   SPEED       =   2'b01,
  3.     parameter   CLK_FREQ    =   32'd25_000_000
  4. )
  5. (
  6.     input   wire                I_clk       ,
  7.     input   wire                I_rstn      ,
  8.     input   wire                I_keycap    ,
  9.     output  wire                O_phyrst    ,
  10.     output  wire                O_phy_mdc   ,
  11.     inout   wire                IO_phy_mdio
  12. );
  13. localparam  CFG_NUM =   3;
  14. localparam  IDLE    =   0;
  15. localparam  REQ     =   1;
  16. localparam  DONE    =   2;
  17. reg     [23:0]      cnt;
  18. reg     [5:0]       index;
  19. reg     [23:0]      gap_cnt;
  20. reg      [1:0]       state;
  21. reg                  cfgdone;
  22. reg                 mdio_req;
  23. reg                  mode;
  24. wire [4:0]       reg_addr;
  25. wire [15:0]      rd_data;
  26. reg      [15:0]      wr_data;
  27. wire                mdio_busy;
  28. wire             mdio_done;
  29. wire             start_lock;
  30. assign  start_lock  =   cnt == 1250_000;
  31. assign  reg_addr    =   cfgdone ? 5'h0 : 5'h0;
  32. always@(posedge I_clk or negedge O_phyrst) begin
  33.     if(!O_phyrst)
  34.         cnt <=  0;
  35.     else if(cnt < 1250_000)
  36.         cnt <=  cnt + 1'b1;
  37.     else
  38.         cnt <=  cnt;
  39. end
  40. always@(posedge I_clk or negedge O_phyrst) begin
  41.     if(!O_phyrst)
  42.         state   <=  IDLE;
  43.     else if(start_lock) begin
  44.         case(state)
  45.             IDLE:begin
  46.                 if(cfgdone == IDLE)
  47.                     state   <=  REQ;
  48.                 else if(cfgdone && I_keycap)
  49.                     state   <=  REQ;
  50.                 else
  51.                     state   <=  IDLE;
  52.             end
  53.             REQ:begin
  54.                 if(mdio_busy)
  55.                     state   <=  DONE;
  56.                 else
  57.                     state   <=  REQ;
  58.             end
  59.             DONE:begin
  60.                 if(!mdio_busy)
  61.                     state   <=  IDLE;
  62.                 else
  63.                     state   <=  DONE;
  64.             end
  65.             default:begin
  66.                 state   <=  IDLE;
  67.             end
  68.         endcase
  69.     end
  70. end
  71. always@(posedge I_clk or negedge O_phyrst) begin
  72.     if(!O_phyrst)
  73.         mdio_req    <=  0;
  74.     else if(mdio_req && mdio_busy)
  75.         mdio_req    <=  0;
  76.     else if(state == REQ)
  77.         mdio_req    <=  1;
  78.     else
  79.         mdio_req    <=  0;
  80. end
  81. always@(posedge I_clk or negedge O_phyrst) begin
  82.     if(!O_phyrst)
  83.         mode    <=  0;
  84.     else if(state == REQ && cfgdone == 1)
  85.         mode    <=  0;
  86.     else if(state == REQ)
  87.         mode    <=  1;
  88.     else
  89.         mode    <=  mode;
  90. end
  91. always@(posedge I_clk or negedge O_phyrst) begin
  92.     if(!O_phyrst)
  93.         cfgdone <=  0;
  94.     else if(index == CFG_NUM - 1 && !mdio_busy)
  95.         cfgdone <=  1;
  96. end
  97. always@(posedge I_clk or negedge O_phyrst) begin
  98.     if(!O_phyrst)
  99.         index   <=  0;
  100.     else if(state == DONE && !mdio_busy && !cfgdone)
  101.         index   <=  index + 1;
  102.     else
  103.         index   <=  index;
  104. end
  105. always@(*) begin
  106.     case(index)
  107.         0:wr_data   <=  16'h0800;
  108.         1:wr_data   <=  {2'b00, SPEED[0], 6'b0000_10, SPEED[1], 6'b0000_00};
  109.         2:wr_data   <=  {2'b10, SPEED[0], 6'b0000_10, SPEED[1], 6'b0000_00};
  110.         default:wr_data <=  16'b8000;
  111.     endcase
  112. end
  113. uiphyrst#(
  114.     .CLK_FREQ           (CLK_FREQ)
  115. )
  116. uiphyrst
  117. (
  118.     .I_CLK              (I_clk          ),
  119.     .I_rstn             (I_rstn         ),
  120.     .I_phyrst           (I_rstn         ),
  121.     .O_phyrst           (O_phyrst       )
  122. );
  123. uimdio_ctrl #(
  124.     .DIV_NUM            (50             )
  125. )
  126. uimdio_ctrl
  127. (
  128.     .I_clk              (I_clk          ),
  129.     .I_rstn             (O_phyrst       ),
  130.     .I_phy_addr         (5'b00100       ),
  131.     .I_reg_addr         (reg_addr       ),
  132.     .I_mdio_req         (mdio_req       ),
  133.     .I_mdio_mode        (mode           ),
  134.     .I_wr_data          (wr_data        ),
  135.     .O_rd_data          (rd_data        ),
  136.     .O_mdio_busy        (mdio_busy      ),
  137.     .O_mdio_done        (mdio_done      ),
  138.     .O_phy_mdc          (O_phy_mdc      ),
  139.     .IO_phy_mdio        (IO_phy_mdio    )
  140. );
  141. endmodule
复制代码

仿真测试源码如下:

  1. `timescale 1ns/1ps
  2. module cfg_tb;
  3. reg clk,rst;
  4. wire    phyrst,phy_mdc,phy_mdio;
  5. pullup(phy_mdio);
  6. uiphy_cfg #(
  7.     .SPEED(2'b01),
  8.     .CLK_FREQ(32'd25_000_000)
  9. ) inst_uiphy_cfg (
  10.     .I_clk       (clk),
  11.     .I_rstn      (~rst),
  12.     .O_phyrst    (phyrst),
  13.     .O_phy_mdc   (phy_mdc),
  14.     .IO_phy_mdio (phy_mdio)
  15. );
  16. always #20 clk = ~clk;
  17. initial begin
  18.     clk =   0;
  19.     rst =   1;
  20.     #200;
  21.     rst =   0;
  22. end
  23. endmodule
复制代码

通过modelsim进行仿真验证,抓取mdio_req为高电平的时间段,可见mdio_req拉高后,MDIO驱动器将mdio_busy也拉高进行响应,当mdio_busy拉高时,mido_req立即拉低,握手成功。

image.jpg

mdio信号线上的数据如下图所示,空闲时总线为高阻态,在mdio_busy拉高后,进行写操作,总线先发送32bit的逻辑1,接着按顺序发送开始标志2b01、操作码2b01、物理地址5b00100、寄存器地址5b00000TA2b1016bit数据,然后发送数据16h0800,全部发送完成后,总线置为高阻态。

image.jpg

通过MDIOPHY芯片的工作模式设置为全双工、100Mbps速率,在PC中的“网络和Internet”设置中查看以太网的配置信息,可以看到链接速度为100Mbps,证明PHY芯片配置成功。

image.jpg

然后在程序中设置按键控制读寄存器0操作,通过chipwatcher抓取波形,上板验证效果如下图所示。读出寄存器0的配置信息为16h2100,即为写入的配置信息。

image.jpg

3 RGMII接口设计
3.1 RGMII接口时序

2.2节我们对RGMII接口的功能及引脚做了介绍,RGMII接口的时序如下图所示。

image.jpg

image.jpg

在普通模式下,数据在时钟的边沿变化,如果不做额外处理,数据无法被稳定采样,所以PHY芯片还提供了延迟模式来保证数据在稳定的区间内被采样。发送端或接受端被设置成延时模式时,时钟在PHY芯片内部会添加2ns的延时,让时钟的边沿对齐数据有效区间的中心。延时模式通过PHY芯片的引脚上下拉控制。

RGMII接口是双沿采样的,每个时钟周期传输一个字节的数据。传输数据时,在时钟上升沿传输一个字节的低4bit,在时钟下降沿传输一个字节的高4bit,所以千兆速率下RGMII接口的采样时钟为125MHz,而接口速率为1000MbpsFPGA内部逻辑难以处理双沿数据,需要在接收引脚或发送引脚的IOB上将双沿数据转换成单沿数据或将单沿数据转换成双沿数据,IOB中的IDDRODDR可以用来实现这些功能。

PH1_LOGIC_IDDR是安路PH1系列FPGA IO上将双沿采样时钟数据变换为单沿数据的单元。具有数据对齐的双速率D触发器,并具有同步/异步复位功能。可以将输入的DDR数据转为1SDR数据同步到内部时钟网络。

image.jpg

该单元的引脚功能如下:

clk:采样时钟。

dIO上输入的DDR数据。

rst:复位信号,高有效。

q01位上升沿数据输出。

q11位下降沿数据输出。

该单元可以配置成PIPED模式或NONE模式,PIPED模式下时序如下图所示。

image.jpg

NONE模式下时序图如下图所示。

image.jpg

工作在PIPED模式下时,q0q1对应一个完整字节的数据,所以在对RGMII接口采样时使用PIPED模式。

PH1_LOGIC_ODDRFPGA IO上将内部单沿数据变换为双沿数据后输出的单元,具有同步/异步复位功能。可以将数据双倍率输出到IO口上。

image.jpg

该单元的引脚功能如下:

clk:同步时钟。

d01位上升沿输入数据。

d11位下降沿输入数据。

rst:复位信号,高有效。

q1DDR双沿输出数据。

该单元的工作时序如下图所示。

image.jpg

3.2 RGMII模块设计

RGMII接口输入输出模块rgmii_interface的代码如下:

  1. module rgmii_interface (
  2. // 指示当前运行速度为10/100
  3. input            speed_10_100,   //设置当前的运行速率
  4. //RGMII发送时序调整,当PHY 接收来自FPGA的TX信号,没有使用内部2ns延迟情况需要在fpga端设置2ns延迟
  5. input                gmii_tx_clk_d,   
  6. input                gmii_tx_reset_d,   
  7. // 以下端口是RGMII物理接口:这些端口将位于FPGA的引脚上
  8. output     [3:0] rgmii_txd,
  9. output           rgmii_tx_ctl,
  10. output           rgmii_txc,
  11. input      [3:0] rgmii_rxd,
  12. input            rgmii_rx_ctl,
  13. input            rgmii_rxc,
  14. // 以下端口连接到 TEMAC核 的 内部GMII接口模块
  15. input            gmii_tx_reset,
  16. input            gmii_tx_clk,      // ggmii_tx_clk: 125mhz
  17. input      [7:0] gmii_txd,
  18. input            gmii_tx_en,
  19. input            gmii_tx_er,
  20. input            gmii_rx_reset,
  21. output               gmii_rx_clk,    //output: 125mhz(1gbps) 25mhz(100mbps)    2.5mhz(10mbps)
  22. output reg    [7:0] gmii_rxd,
  23. output reg          gmii_rx_dv,
  24. output reg          gmii_rx_er,
  25. output           gmii_crs,
  26. output           gmii_col,
  27. // 以下信号为RGMII状态信号
  28. output reg       link_status,
  29. output reg [1:0] clock_speed,
  30. output reg       duplex_status
  31. );
  32. //----------------------------------------------------------------------------
  33. // 模块 内部 信号
  34. //----------------------------------------------------------------------------
  35. reg    [3:0] gmii_txd_falling;             // gmii_txd信号在gmii_tx_clk的下降沿锁存。
  36. wire         rgmii_txc_odelay;             // RGMII接收器时钟ODDR输出.
  37. wire         rgmii_tx_ctl_odelay;          // RGMII控制信号ODDR输出.
  38. wire   [3:0] rgmii_txd_odelay;             // RGMII数据ODDR输出.
  39. wire         rgmii_tx_ctl_int;             // 内部RGMII传输控制信号.
  40. wire         rgmii_rx_ctl_delay;
  41. wire   [3:0] rgmii_rxd_delay;
  42. wire         rgmii_rx_ctl_reg;             // 内部RGMII接收器控制信号.
  43. reg          tx_en_to_ddr;
  44. wire         gmii_rx_dv_reg;               
  45. wire         gmii_rx_er_reg;               
  46. wire   [7:0] gmii_rxd_reg;                 
  47. wire         inband_ce;                    //RGMII带内状态寄存器 使能输出信号
  48. wire         rgmii_rxc_int;
  49. //==============================================================================
  50. // RGMII 发送逻辑
  51. //==============================================================================
  52. //----------------------------------------------------------------------------
  53. // RGMII 发送器时钟管理:rgmii_txc
  54. //----------------------------------------------------------------------------
  55. // 产生 rgmii_txc 时钟.
  56. PH1_LOGIC_ODDR  rgmii_txc_ddr
  57. (
  58. .q             (rgmii_txc_odelay), //output 125mhz(1gbps) 25mhz(100mbps)    2.5mhz(10mbps)
  59. .clk           (gmii_tx_clk_d),
  60. .d0            (1'b1),
  61. .d1            (1'b0),
  62. .rst           (gmii_tx_reset_d)
  63. );
  64. assign rgmii_txc = rgmii_txc_odelay;
  65.    
  66. //---------------------------------------------------------------------------
  67. // RGMII 发送逻辑 : rgmii_txd
  68. //---------------------------------------------------------------------------
  69. // 1Gbps gmii_txd  8位有效; rgmii_txc双沿使能发送8位
  70. // 10/100Mbps gmii_txd 仅低四位有效,rgmii_txc双沿使能重复发送低四位
  71. // 1Gbps时,125mhz的rgmii_txc,上升沿发送低四位,下降沿发送高四位。 一个时钟周期8bit, 125*8=1Gbps
  72. // 100Mbps时,25mhz的rgmii_txc,上升沿发送低四位,下降沿发送低四位。 相当于一个时钟周期只发一个4bit, 25*4=100mbps
  73. // 10Mbps时同100mbps,数据有效位在每byte的低四位,虽然一个时钟周期重复发低四位,相当于一个时钟周期只发一个低4位。
  74.    
  75. always @ (speed_10_100, gmii_txd)begin
  76.    if (speed_10_100 == 1'b0) // 1Gbps gmii_txd  8位有效
  77.       gmii_txd_falling     <= gmii_txd[7:4];
  78.    else      // 10/100Mbps gmii_txd高四位无效,rgmii_txc双沿使能发送低四位
  79.       gmii_txd_falling     <= gmii_txd[3:0];
  80. end
  81. genvar i;
  82. generate for (i=0; i<4; i=i+1)begin : txdata_out_bus
  83.     PH1_LOGIC_ODDR  rgmii_txd_out
  84.       (
  85.       .q             (rgmii_txd_odelay[i]),
  86.       .clk           (gmii_tx_clk),
  87.       .d0            (gmii_txd[i]),
  88.       .d1            (gmii_txd_falling[i]),
  89.       .rst           (gmii_tx_reset)
  90.       );
  91. assign rgmii_txd[i] = rgmii_txd_odelay[i];
  92. end
  93. endgenerate
  94. //---------------------------------------------------------------------------
  95. // RGMII 发送逻辑 : rgmii_tx_ctl
  96. //---------------------------------------------------------------------------
  97. // 编码 rgmii ctl 信号
  98. assign rgmii_tx_ctl_int = gmii_tx_en ^ gmii_tx_er;
  99. // 需要逻辑以确保 错误信号 将在整个时钟相位内,将tx_ctl发送为低电平
  100. always @(speed_10_100 or gmii_tx_en or gmii_tx_er)begin
  101.    if (speed_10_100)
  102.       tx_en_to_ddr = gmii_tx_en & (!gmii_tx_er );
  103.    else
  104.       tx_en_to_ddr = gmii_tx_en;
  105. end
  106.    
  107. // oDDR primitive
  108. PH1_LOGIC_ODDR  ctl_output
  109. (
  110. .q             (rgmii_tx_ctl_odelay),
  111. .clk           (gmii_tx_clk),
  112. .d0            (tx_en_to_ddr),
  113. .d1            (rgmii_tx_ctl_int),
  114. .rst           (gmii_tx_reset)
  115. );
  116. assign rgmii_tx_ctl = rgmii_tx_ctl_odelay;
  117. //==============================================================================
  118. // RGMII 接收逻辑
  119. //==============================================================================
  120. //---------------------------------------------------------------------------
  121. // RGMII 接收逻辑:rgmii_rxc
  122. //---------------------------------------------------------------------------
  123. PH1_PHY_IOCLK u_rgmii_rxc_int
  124. (
  125. .clkin (rgmii_rxc),
  126. .clkout (rgmii_rxc_int)
  127. );
  128. PH1_PHY_SCLK_V2 u_rgmii_rxc
  129. (
  130. .ce(1'b1),
  131. .clkin (rgmii_rxc),
  132. .clkout (gmii_rx_clk)
  133. );   
  134.    
  135. //---------------------------------------------------------------------------
  136. // RGMII 接收逻辑:rgmii_rxd ---->  gmii_rxd_reg
  137. //---------------------------------------------------------------------------
  138. // 1Gbps gmii_rxd  8位有效; rgmii_rxc双沿使能接收8位
  139. // 10/100Mbps gmii_rxd 仅低四位有效,rgmii_rxc双沿使能接收高低四位数据相同
  140. // 1gbps时,125mhz的rgmii_rxc,上升沿接收四位数据(对应低4bit),下降沿接收四位数据(对应高4bit)。 一个时钟周期8bit, 125mhz*8=1gbps
  141. // 100mbps时,25mhz的rgmii_rxc,上升沿接收四位数据,下降沿接收四位数据(高四位和低四位数据相同)。 相当于单沿采样,一个时钟周期只收一个4bit(数据有效位在每byte的低四位:高低四位重复), 25mhz*4=100mbps
  142. // 10mbps时同100mbps,虽然一个时钟周期上下沿重复接收相同四位数据,相当于一个时钟周期只收一个4bit。
  143. genvar j;
  144. generate for (j=0; j<4; j=j+1)begin : rxdata_bus
  145. assign rgmii_rxd_delay[j]=rgmii_rxd[j];
  146. end
  147. endgenerate
  148. // Instantiate Double Data Rate Input flip-flops.
  149. // DDR_CLK_EDGE attribute specifies output data alignment from IDDR component
  150. genvar k;
  151. generate for (k=0; k<4; k=k+1)begin : rxdata_in_bus
  152. PH1_LOGIC_IDDR  #(
  153. .PIPEMODE       ("PIPED")
  154. )
  155. rgmii_rx_data_in
  156. (
  157. .q0            (gmii_rxd_reg[k]),
  158. .q1            (gmii_rxd_reg[k+4]),
  159. .clk           (rgmii_rxc_int),
  160. .d             (rgmii_rxd_delay[k]),
  161. .rst           (1'b0)
  162. );
  163.      
  164. end
  165. endgenerate
  166.    
  167. //---------------------------------------------------------------------------
  168. // RGMII 接收逻辑:rgmii_rx_ctl ------> gmii_rx_dv、gmii_rx_er
  169. //---------------------------------------------------------------------------
  170. assign rgmii_rx_ctl_delay = rgmii_rx_ctl;
  171. PH1_LOGIC_IDDR  #(
  172. .PIPEMODE       ("PIPED")
  173. )
  174. rgmii_rx_ctl_in
  175. (
  176. .q0            (gmii_rx_dv_reg),
  177. .q1            (rgmii_rx_ctl_reg),
  178. .clk           (rgmii_rxc_int),
  179. .d             (rgmii_rx_ctl_delay),
  180. .rst           (1'b0)
  181. );
  182. // 解码 gmii_rx_er signal
  183. assign gmii_rx_er_reg = gmii_rx_dv_reg ^ rgmii_rx_ctl_reg;
  184. //----------------------------------------------------------------------------
  185. // 接收逻辑:内部信号给到输出端口:gmii_rxd、gmii_rx_dv、gmii_rx_er、gmii_col、gmii_crs
  186. //----------------------------------------------------------------------------
  187. always @ (posedge gmii_rx_clk  )begin
  188. gmii_rxd      <= gmii_rxd_reg;
  189. gmii_rx_dv    <= gmii_rx_dv_reg;
  190. gmii_rx_er    <= gmii_rx_er_reg;   
  191. end
  192. // 从RGMII创建GMII格式的冲突和载波侦听信号
  193. assign gmii_col = (gmii_tx_en | gmii_tx_er) & (gmii_rx_dv_reg | gmii_rx_er_reg);
  194. assign gmii_crs = (gmii_tx_en | gmii_tx_er) | (gmii_rx_dv_reg | gmii_rx_er_reg);
  195. //==============================================================================
  196. // RGMII 状态寄存器
  197. //==============================================================================
  198. // 在帧间间隔期间启用带内状态寄存器
  199. assign inband_ce = !(gmii_rx_dv_reg || gmii_rx_er_reg);
  200. always @ (posedge gmii_rx_clk  or posedge gmii_rx_reset)begin
  201.    if (gmii_rx_reset) begin
  202.       link_status          <= 1'b0;
  203.       clock_speed[1:0]     <= 2'b0;
  204.       duplex_status        <= 1'b0;
  205.    end
  206.    else if (inband_ce) begin
  207.       link_status          <= gmii_rxd_reg[0];
  208.       clock_speed[1:0]     <= gmii_rxd_reg[2:1];
  209.       duplex_status        <= gmii_rxd_reg[3];
  210.    end
  211. end
  212. endmodule
复制代码
3.3 数据转换模块设计

rgmii_interface模块只实现了千兆速率下的RGMII接口收发,而100Mps10Mps速率的采样时钟分别为25MHz2.5MHz,数据单沿传输。100M10M以太网的内部逻辑时钟为12.5MHz1.25MHz,数据位宽为8位,这就需要额外设计一个模块进行跨时钟域处理,并且实现8bit数据和4bit数据的相互转换,称为rgmii_cdc模块。

rgmii_cdc模块的设计思路与mac层类似,在发送端和接收端各使用两个异步FIFO,一个用来缓存数据,即data_fifo,一个用来缓存长度队列,即len_fifo。对写入data_fifo的数据数量进行计数,当写入最后一个数据时,将计数的值也写入len_fifo中。当len_fifo为非空时,代表有一帧数据等待读出,这时候将缓存的长度信息读取出来,然后通过计数器读取出一个完的帧。

image.jpg

rgmii_cdc模块的代码如下:

  1. module  rgmii_cdc
  2. (
  3.     input   wire            speed_10_100    ,
  4.     input   wire            I_rst           ,
  5.     input   wire            I_gmii_rclk     ,
  6.     input   wire            I_gmii_rvalid   ,
  7.     input   wire    [7:0]   I_gmii_rdata    ,
  8.     input   wire            I_gmii_tclk     ,
  9.     output  reg             O_gmii_tvalid   ,
  10.     output  reg     [7:0]   O_gmii_tdata    ,
  11.     input   wire            I_rx_clk        ,
  12.     output  reg             O_rx_valid      ,
  13.     output  reg     [7:0]   O_rx_data       ,
  14.     input   wire            I_tx_clk        ,
  15.     input   wire            I_tx_valid      ,
  16.     input   wire    [7:0]   I_tx_data      
  17. );
  18. reg     [7:0]       tx_gap;
  19. reg     [15:0]      tx_len_fifo_din;
  20. wire                tx_len_fifo_wren;
  21. wire                tx_len_fifo_rdemtpy;
  22. reg                 tx_len_fifo_rden;
  23. wire    [15:0]      tx_len_fifo_dout;
  24. reg                 tx_data_fifo_rden;
  25. wire    [7:0]       tx_data_fifo_dout;
  26. reg     [15:0]      tx_len;
  27. reg                 tx_valid_reg;
  28. reg                 tcnt_10_100;
  29. reg     [15:0]      tx_cnt;     
  30. reg                 tx_lock;
  31. reg                 tx_run;
  32. reg                 tx_en;
  33. assign  tx_len_fifo_wren    =   tx_valid_reg && ~I_tx_valid;
  34. always@(posedge I_tx_clk)
  35.     tx_valid_reg    <=  I_tx_valid;
  36. always@(posedge I_tx_clk or posedge I_rst) begin
  37.     if(I_rst)
  38.         tx_len_fifo_din <=  16'd0;
  39.     else if(I_tx_valid)
  40.         tx_len_fifo_din <=  tx_len_fifo_din + 1'b1;
  41.     else
  42.         tx_len_fifo_din <=  16'd0;
  43. end
  44. udp_pkg_buf #(
  45. .DATA_WIDTH_W       (8                  ),
  46. .DATA_WIDTH_R       (8                  ),
  47. .ADDR_WIDTH_W       (12                 ),
  48. .ADDR_WIDTH_R       (12                 ),
  49. .SHOW_AHEAD_EN      (1'b1               ),
  50. .OUTREG_EN          ("NOREG"            )
  51. )
  52. tx_data_fifo
  53. (
  54. .rst                (I_rst              ),
  55. .clkw               (I_tx_clk           ),
  56. .we                 (I_tx_valid         ),
  57. .di                 (I_tx_data          ),
  58. .clkr               (I_gmii_tclk        ),
  59. .re                 (tx_data_fifo_rden  ),
  60. .dout               (tx_data_fifo_dout  ),
  61. .wrusedw            (),
  62. .rdusedw            ()
  63. );
  64. udp_pkg_buf #(
  65. .DATA_WIDTH_W       (16                 ),
  66. .DATA_WIDTH_R       (16                 ),
  67. .ADDR_WIDTH_W       (6                  ),
  68. .ADDR_WIDTH_R       (6                  ),
  69. .SHOW_AHEAD_EN      (1'b1               ),
  70. .OUTREG_EN          ("NOREG"            )
  71. )
  72. tx_len_fifo
  73. (
  74. .rst                (I_rst              ),
  75. .clkw               (I_tx_clk           ),
  76. .we                 (tx_len_fifo_wren   ),
  77. .di                 (tx_len_fifo_din    ),
  78. .clkr               (I_gmii_tclk        ),
  79. .re                 (tx_len_fifo_rden   ),
  80. .dout               (tx_len_fifo_dout   ),
  81. .wrusedw            (),
  82. .rdusedw            (),
  83. .empty_flag         (tx_len_fifo_rdemtpy)
  84. );
  85. always@(posedge I_gmii_tclk or posedge I_rst) begin
  86.     if(I_rst)
  87.         tx_lock <=  1'b0;
  88.     else if(!tx_lock && !tx_len_fifo_rdemtpy)
  89.         tx_lock <=  1'b1;
  90.     else if(tx_cnt == 16'd0 && tx_gap == 8'd10 && ~speed_10_100)
  91.         tx_lock <=  1'b0;
  92.     else if(tx_cnt == 16'd0 && tx_gap == 8'd22 && speed_10_100)
  93.         tx_lock <=  1'b0;
  94.     else
  95.         tx_lock <=  tx_lock;
  96. end
  97. always@(posedge I_gmii_tclk or posedge I_rst) begin
  98.     if(I_rst)
  99.         tx_len_fifo_rden    <=  1'b0;
  100.     else if(!tx_lock && !tx_len_fifo_rdemtpy)
  101.         tx_len_fifo_rden    <=  1'b1;
  102.     else
  103.         tx_len_fifo_rden    <=  1'b0;
  104. end
  105. always@(posedge I_gmii_tclk or posedge I_rst) begin
  106.     if(I_rst)
  107.         tx_len  <=  16'd0;
  108.     else if(tx_len_fifo_rden)
  109.         tx_len  <=  tx_len_fifo_dout;
  110.     else
  111.         tx_len  <=  tx_len;
  112. end
  113. always@(posedge I_gmii_tclk or posedge I_rst) begin
  114.     if(I_rst)
  115.         tx_run  <=  1'b0;
  116.     else if(tx_lock && tx_len_fifo_rden)
  117.         tx_run  <=  1'b1;
  118.     else if(tx_cnt == tx_len - 1'b1 && ~speed_10_100)
  119.         tx_run  <=  1'b0;
  120.     else if(tx_cnt == tx_len - 1'b1 && tcnt_10_100 && speed_10_100)
  121.         tx_run  <=  1'b0;
  122.     else
  123.         tx_run  <=  tx_run;
  124. end
  125. always@(posedge I_gmii_tclk or posedge I_rst) begin
  126.     if(I_rst)
  127.         tx_data_fifo_rden   <=  1'b0;
  128.     else if(tcnt_10_100 && tx_run)
  129.         tx_data_fifo_rden   <=  1'b1;
  130.     else if(speed_10_100)
  131.         tx_data_fifo_rden   <=  1'b0;
  132.     else if(tx_lock && tx_len_fifo_rden && ~speed_10_100)
  133.         tx_data_fifo_rden   <=  1'b1;
  134.     else if(tx_cnt >= tx_len - 1'b1)
  135.         tx_data_fifo_rden   <=  1'b0;
  136.     else
  137.         tx_data_fifo_rden   <=  tx_data_fifo_rden;
  138. end
  139. always@(posedge I_gmii_tclk or posedge I_rst) begin
  140.     if(I_rst)
  141.         tcnt_10_100 <=  1'b0;
  142.     else if(tx_run && speed_10_100)
  143.         tcnt_10_100 <=  tcnt_10_100 + 1'b1;
  144.     else
  145.         tcnt_10_100 <=  1'b0;
  146. end
  147. always@(posedge I_gmii_tclk or posedge I_rst) begin
  148.     if(I_rst)
  149.         tx_gap  <=  8'd0;
  150.     else if(~speed_10_100 && tx_gap == 8'd10)
  151.         tx_gap  <=  8'd0;
  152.     else if(speed_10_100 && tx_gap == 8'd22)
  153.         tx_gap  <=  8'd0;
  154.     else if((tx_lock && tx_cnt == tx_len - 1'b1) || tx_gap > 8'd0)
  155.         tx_gap  <=  tx_gap + 1'b1;
  156. end
  157. always@(posedge I_gmii_tclk or posedge I_rst) begin
  158.     if(I_rst)
  159.         tx_cnt  <=  16'd0;
  160.     else if(tx_cnt == tx_len)
  161.         tx_cnt  <=  16'd0;
  162.     else if(tx_data_fifo_rden)
  163.         tx_cnt  <=  tx_cnt + 1'b1;
  164.     else
  165.         tx_cnt  <=  tx_cnt;
  166. end
  167. always@(posedge I_gmii_tclk or posedge I_rst) begin
  168.     if(I_rst)
  169.         tx_en   <=  1'b0;
  170.     else if(tx_run && speed_10_100)
  171.         tx_en   <=  1'b1;
  172.     else if(~tx_run)
  173.         tx_en   <=  1'b0;
  174. end
  175. always@(posedge I_gmii_tclk or posedge I_rst) begin
  176.     if(I_rst)
  177.         O_gmii_tvalid   <=  1'b0;
  178.     else if(~speed_10_100)
  179.         O_gmii_tvalid   <=  tx_data_fifo_rden;
  180.     else if(speed_10_100 && ~tx_en)
  181.         O_gmii_tvalid   <=  1'b0;
  182.     else if(speed_10_100 && tx_en)
  183.         O_gmii_tvalid   <=  1'b1;
  184.     else
  185.         O_gmii_tvalid   <=  1'b0;
  186. end
  187. always@(posedge I_gmii_tclk or posedge I_rst) begin
  188.     if(I_rst)
  189.         O_gmii_tdata    <=  8'd0;
  190.     else if(tx_run && ~speed_10_100)
  191.         O_gmii_tdata    <=  tx_data_fifo_dout;
  192.     else if(tx_en && speed_10_100)
  193.         O_gmii_tdata    <=  tcnt_10_100 ? {4'd0,tx_data_fifo_dout[3:0]} : {4'd0,tx_data_fifo_dout[7:4]};
  194.     else
  195.         O_gmii_tdata    <=  8'd0;
  196. end
  197. //==================================================================
  198. //==================================================================
  199. reg     [15:0]      rx_len_fifo_din;
  200. wire                rx_len_fifo_wren;
  201. wire                rx_len_fifo_rdemtpy;
  202. reg                 rx_len_fifo_rden;
  203. wire    [15:0]      rx_len_fifo_dout;
  204. reg     [7:0]       rx_data_fifo_din;
  205. reg                 rx_data_fifo_wren;
  206. reg                 rx_data_fifo_rden;
  207. wire    [7:0]       rx_data_fifo_dout;
  208. reg                 gmii_rvalid_reg;
  209. reg     [15:0]      rx_len;
  210. reg     [15:0]      rx_cnt;
  211. reg                 rx_lock;
  212. reg                 rcnt_10_100;
  213. assign  rx_len_fifo_wren    =   gmii_rvalid_reg && ~I_gmii_rvalid;
  214. always@(posedge I_gmii_rclk)
  215.     gmii_rvalid_reg <=  I_gmii_rvalid;
  216. always@(posedge I_gmii_rclk or posedge I_rst) begin
  217.     if(I_rst)
  218.         rx_len_fifo_din <=  0;
  219.     else if(~I_gmii_rvalid)
  220.         rx_len_fifo_din <=  0;
  221.     else if(I_gmii_rvalid && ~speed_10_100)
  222.         rx_len_fifo_din <=  rx_len_fifo_din + 1;
  223.     else if(I_gmii_rvalid && speed_10_100 && rcnt_10_100)
  224.         rx_len_fifo_din <=  rx_len_fifo_din + 1;
  225.     else
  226.         rx_len_fifo_din <=  rx_len_fifo_din;
  227. end
  228. always@(posedge I_gmii_rclk or posedge I_rst) begin
  229.     if(I_rst)
  230.         rcnt_10_100 <=  1'b0;
  231.     else if(I_gmii_rvalid && speed_10_100)
  232.         rcnt_10_100 <=  rcnt_10_100 + 1'b1;
  233.     else
  234.         rcnt_10_100 <=  1'b0;
  235. end
  236. always@(posedge I_gmii_rclk or posedge I_rst) begin
  237.     if(I_rst)
  238.         rx_data_fifo_wren   <=  1'b0;
  239.     else if(~speed_10_100)
  240.         rx_data_fifo_wren   <=  I_gmii_rvalid;
  241.     else if(speed_10_100)
  242.         rx_data_fifo_wren   <=  rcnt_10_100;
  243. end
  244. always@(posedge I_gmii_rclk or posedge I_rst) begin
  245.     if(I_rst)
  246.         rx_data_fifo_din    <=  8'd0;
  247.     else if(speed_10_100)
  248.         rx_data_fifo_din    <=  {I_gmii_rdata[3:0], rx_data_fifo_din[7:4]};
  249.     else
  250.         rx_data_fifo_din    <=  I_gmii_rdata;
  251. end
  252. udp_pkg_buf #(
  253. .DATA_WIDTH_W       (8                  ),
  254. .DATA_WIDTH_R       (8                  ),
  255. .ADDR_WIDTH_W       (12                 ),
  256. .ADDR_WIDTH_R       (12                 ),
  257. .SHOW_AHEAD_EN      (1'b1               ),
  258. .OUTREG_EN          ("NOREG"            )
  259. )
  260. rx_data_fifo
  261. (
  262. .rst                (I_rst              ),
  263. .clkw               (I_gmii_rclk        ),
  264. .we                 (rx_data_fifo_wren  ),
  265. .di                 (rx_data_fifo_din   ),
  266. .clkr               (I_rx_clk           ),
  267. .re                 (rx_data_fifo_rden  ),
  268. .dout               (rx_data_fifo_dout  ),
  269. .wrusedw            (),
  270. .rdusedw            ()
  271. );
  272. udp_pkg_buf #(
  273. .DATA_WIDTH_W       (16                 ),
  274. .DATA_WIDTH_R       (16                 ),
  275. .ADDR_WIDTH_W       (6                  ),
  276. .ADDR_WIDTH_R       (6                  ),
  277. .SHOW_AHEAD_EN      (1'b1               ),
  278. .OUTREG_EN          ("NOREG"            )
  279. )
  280. rx_len_fifo
  281. (
  282. .rst                (I_rst              ),
  283. .clkw               (I_gmii_rclk        ),
  284. .we                 (rx_len_fifo_wren   ),
  285. .di                 (rx_len_fifo_din    ),
  286. .clkr               (I_rx_clk           ),
  287. .re                 (rx_len_fifo_rden   ),
  288. .dout               (rx_len_fifo_dout   ),
  289. .wrusedw            (),
  290. .rdusedw            (),
  291. .empty_flag         (rx_len_fifo_rdemtpy)
  292. );
  293. always@(posedge I_rx_clk or posedge I_rst) begin
  294.     if(I_rst)
  295.         rx_lock <=  1'b0;
  296.     else if(!rx_lock && !rx_len_fifo_rdemtpy)
  297.         rx_lock <=  1'b1;
  298.     else if(rx_cnt == rx_len - 1'b1)
  299.         rx_lock <=  1'b0;
  300.     else
  301.         rx_lock <=  rx_lock;
  302. end
  303. always@(posedge I_rx_clk or posedge I_rst) begin
  304.     if(I_rst)
  305.         rx_len_fifo_rden    <=  1'b0;
  306.     else if(!rx_lock && !rx_len_fifo_rdemtpy)
  307.         rx_len_fifo_rden    <=  1'b1;
  308.     else
  309.         rx_len_fifo_rden    <=  1'b0;
  310. end
  311. always@(posedge I_rx_clk or posedge I_rst) begin
  312.     if(I_rst)
  313.         rx_len  <=  16'd0;
  314.     else if(rx_len_fifo_rden)
  315.         rx_len  <=  rx_len_fifo_dout;
  316.     else
  317.         rx_len  <=  rx_len;
  318. end
  319. always@(posedge I_rx_clk or posedge I_rst) begin
  320.     if(I_rst)
  321.         rx_data_fifo_rden   <=  1'b0;
  322.     else if(rx_lock && rx_len_fifo_rden)
  323.         rx_data_fifo_rden   <=  1'b1;
  324.     else if(rx_cnt == rx_len - 1'b1)
  325.         rx_data_fifo_rden   <=  1'b0;
  326.     else
  327.         rx_data_fifo_rden   <=  rx_data_fifo_rden;
  328. end
  329. always@(posedge I_rx_clk or posedge I_rst) begin
  330.     if(I_rst)
  331.         rx_cnt  <=  16'd0;
  332.     else if(rx_cnt == rx_len - 1)
  333.         rx_cnt  <=  16'd0;
  334.     else if(rx_data_fifo_rden)
  335.         rx_cnt  <=  rx_cnt + 1'b1;
  336.     else
  337.         rx_cnt  <=  rx_cnt;
  338. end
  339. always@(posedge I_rx_clk or posedge I_rst) begin
  340.     if(I_rst)
  341.         O_rx_valid  <=  1'b0;
  342.     else if(rx_data_fifo_rden)
  343.         O_rx_valid  <=  1'b1;
  344.     else
  345.         O_rx_valid  <=  1'b0;
  346. end
  347. always@(posedge I_rx_clk or posedge I_rst) begin
  348.     if(I_rst)
  349.         O_rx_data   <=  8'd0;
  350.     else if(rx_data_fifo_rden)
  351.         O_rx_data   <=  rx_data_fifo_dout;
  352.     else
  353.         O_rx_data   <=  8'd0;
  354. end
  355. endmodule
复制代码
4 三速以太网回环测试
4.1 100M以太网测试

udp协议栈的逻辑时钟为12.5MHzRGMII接口的时钟为25MHz,将参数speed_10_100设置成1b1

打开设备管理器,在网络适配器选项中选择自己的以太网卡。

image.jpg

在高级选项中将网卡的连接速度和工作模式设置成100Mbps全双工。

image.jpg

连接好网线,并将程序下栽进开发板中。此时开发板网卡自协商的速度为100Mbps。使用网络调试助手发送数据帧,FPGA将报文解析完成后,通过发送端传回PC,结果如下图所示。

image.jpg

4.2 10M以太网测试

udp协议栈的逻辑时钟为1.25MHzRGMII接口的时钟为2.5MHz,时钟选择由分频逻辑生成。选择125MHz时钟进行计数分配,代码如下:

  1. reg     clk_1_25;
  2. reg     clk_2_5;
  3. reg     clk_2_5_90;
  4. reg [7:0]   clk_cnt;
  5. always@(posedge clk_125 or negedge pll_locked) begin
  6.     if(!pll_locked)
  7.         clk_cnt <=  0;
  8.     else if(clk_cnt == 99)
  9.         clk_cnt <=  0;
  10.     else
  11.         clk_cnt <=  clk_cnt + 1;
  12. end
  13. always@(posedge clk_125 or negedge pll_locked) begin
  14.     if(!pll_locked)
  15.         clk_1_25    <=  0;
  16.     else
  17.         case(clk_cnt)
  18.             49  :clk_1_25   <=  1;
  19.             99  :clk_1_25   <=  0;
  20.             default:clk_1_25    <=  clk_1_25;
  21.         endcase
  22. end
  23. always@(posedge clk_125 or negedge pll_locked) begin
  24.     if(!pll_locked)
  25.         clk_2_5 <=  0;
  26.     else
  27.         case(clk_cnt)
  28.             24  :clk_2_5    <=  1;
  29.             49  :clk_2_5    <=  0;
  30.             74  :clk_2_5    <=  1;
  31.             99  :clk_2_5    <=  0;
  32.             default:clk_2_5 <=  clk_2_5;
  33.         endcase
  34. end
  35. always@(posedge clk_125 or negedge pll_locked) begin
  36.     if(!pll_locked)
  37.         clk_2_5_90  <=  0;
  38.     else
  39.         case(clk_cnt)
  40.             12  :clk_2_5_90 <=  1;
  41.             37  :clk_2_5_90 <=  0;
  42.             62  :clk_2_5_90 <=  1;
  43.             87  :clk_2_5_90 <=  0;
  44.             default:clk_2_5_90  <=  clk_2_5_90;
  45.         endcase
  46. end
复制代码

将网卡的连接速度和工作模式设置成10Mbps全双工。上电开发板,PC设置中显示网络连接速度为10Mbps

image.jpg

将程序下栽进开发板中,通过网络调试助手进行数据回环,结果如下图所示。

image.jpg

ping包测试结果如下图所示。

image.jpg































您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

272

主题
精彩推荐
热门资讯
网友晒图
图文推荐

  • 微信公众平台

  • 扫描访问手机版