专栏首页OpenFPGAMII2RGMII IP核使用设计举例

MII2RGMII IP核使用设计举例

本例程将 PS 的 ETH1 通过 EMIO 方式引出, 通过 EMIO 引出的 ETH 为 GMII 接口, 将其与 GMII to RGMII IP 核连接后转换成 RGMII 接口,然后与外部子卡中的 88E1512 芯片连接。在 PS 端通过 SDK 自带的 lwip echo server 例程通过子卡,以 RJ45 电口与 PC 机实现 TCP 网络通信。

8.5.9.1 应用背景

在使用ZYNQ系列芯片时,使用PS端的ETH 通过 EMIO 引出后为标准的 GMII 接口, 如下图所示。

图8‑188 PS端的ETH 通过 EMIO 引出

或者某些IP在使用时出来的接口就是标准的GMII接口,而目前常用的千兆PHY都是使用RGMIi接口,所以为了使用外部PHY,需要通过 IP 核 GMII to RGMII 将 GMII 接口转换为 RGMII 接口,才能与 PHY 芯片连接。连接原理如下图所示。

图8‑189 GMII to RGMII连接原理

8.5.9.2 外部PHY时序

这部分参考《8.5.1RGMII PHY接口设计、8.5.2PHY_MDIO 接口设计》。

8.5.9.3 GMII_to_RGMII原理

这个IP的详细解释可以参考官方数据手册,这里简单介绍一下实现原理。

RGMII 接口是 GMII 接口的简化版, 在时钟的上升沿及下降沿都采样数据, 上升沿发送TXD[3:0]/RXD[3:0],下降沿发送 TXD[7:4]/RXD[7:4], TX_EN 传送 TX_EN(上升沿)和 TX_ER(下降沿)两种信息, RX_DV 传送 RX_DV(上升沿)和 RX_ER(下降沿)两种信息。

代码8‑13 GMII to RGMII IP实现示例代码

1.   module util_gmii_to_rgmii (  

2.  reset,  

3.  rgmii_td,  

4.  rgmii_tx_ctl,  

5.  rgmii_txc,  

6.  rgmii_rd,  

7.  rgmii_rx_ctl,  

8.  gmii_rx_clk,  

9.  rgmii_rxc,  

10.  gmii_txd,  

11.  gmii_tx_en,  

12.  gmii_tx_er,  

13.  gmii_tx_clk,  

14.  gmii_crs,  

15.  gmii_col,  

16.  gmii_rxd,  

17.  gmii_rx_dv,  

18.  gmii_rx_er,  

19.  speed_selection,  

20.  duplex_mode  

21.  );  

22.  input           rgmii_rxc;//add  

23.  input           reset;  

24.  output  [ 3:0]  rgmii_td;  

25.  output          rgmii_tx_ctl;  

26.  output          rgmii_txc;  

27.  input   [ 3:0]  rgmii_rd;  

28.  input           rgmii_rx_ctl;  

29.  output           gmii_rx_clk;  

30.  input   [ 7:0]  gmii_txd;  

31.  input           gmii_tx_en;  

32.  input           gmii_tx_er;  

33.  output          gmii_tx_clk;  

34.  output          gmii_crs;  

35.  output          gmii_col;  

36.  output  [ 7:0]  gmii_rxd;  

37.  output          gmii_rx_dv;  

38.  output          gmii_rx_er;  

39.  input  [ 1:0]   speed_selection; // 1x gigabit, 01 100Mbps, 00 10mbps  

40.  input           duplex_mode;     // 1 full, 0 half  

41.    

42.  wire gigabit;  

43.  wire gmii_tx_clk_s;  

44.  wire gmii_rx_dv_s;  

45.  

46.  wire  [ 7:0]    gmii_rxd_s;  

47.  wire            rgmii_rx_ctl_delay;  

48.  wire            rgmii_rx_ctl_s;  

49.  // registers  

50.  reg             tx_reset_d1;  

51.  reg             tx_reset_sync;  

52.  reg             rx_reset_d1;  

53.  reg   [ 7:0]    gmii_txd_r;  

54.  reg             gmii_tx_en_r;  

55.  reg             gmii_tx_er_r;  

56.  reg   [ 7:0]    gmii_txd_r_d1;  

57.  reg             gmii_tx_en_r_d1;  

58.  reg             gmii_tx_er_r_d1;  

59.  

60.  reg             rgmii_tx_ctl_r;  

61.  reg   [ 3:0]    gmii_txd_low;  

62.  reg             gmii_col;  

63.  reg             gmii_crs;  

64.  

65.  reg  [ 7:0]     gmii_rxd;  

66.  reg             gmii_rx_dv;  

67.  reg             gmii_rx_er;  

68.  

69.  assign gigabit        = speed_selection [1];  

70.  assign gmii_tx_clk    = gmii_tx_clk_s;  

71.  assign gmii_tx_clk_s  = gmii_rx_clk;  

72.  BUFG bufmr_rgmii_rxc(  

73.    .I(rgmii_rxc),  

74.    .O(gmii_rx_clk)  

75.   );  

76.  always @(posedge gmii_rx_clk)  

77.  begin  

78.    gmii_rxd       = gmii_rxd_s;  

79.    gmii_rx_dv     = gmii_rx_dv_s;  

80.    gmii_rx_er     = gmii_rx_dv_s ^ rgmii_rx_ctl_s;  

81.  end  

82.  

83.  always @(posedge gmii_tx_clk_s) begin  

84.    tx_reset_d1    <= reset;  

85.    tx_reset_sync  <= tx_reset_d1;  

86.  end  

87.  

88.  always @(posedge gmii_tx_clk_s)  

89.  begin  

90.    rgmii_tx_ctl_r = gmii_tx_en_r ^ gmii_tx_er_r;  

91.    gmii_txd_low   = gigabit ? gmii_txd_r[7:4] :  gmii_txd_r[3:0];  

92.    gmii_col       = duplex_mode ? 1'b0 : (gmii_tx_en_r| gmii_tx_er_r) & ( gmii_rx_dv | gmii_rx_er) ;  

93.    gmii_crs       = duplex_mode ? 1'b0 : (gmii_tx_en_r| gmii_tx_er_r| gmii_rx_dv | gmii_rx_er);  

94.  end  

95.  

96.  always @(posedge gmii_tx_clk_s) begin  

97.    if (tx_reset_sync == 1'b1) begin  

98.      gmii_txd_r   <= 8'h0;  

99.      gmii_tx_en_r <= 1'b0;  

100.      gmii_tx_er_r <= 1'b0;  

101.    end  

102.    else  

103.    begin  

104.      gmii_txd_r   <= gmii_txd;  

105.      gmii_tx_en_r <= gmii_tx_en;  

106.      gmii_tx_er_r <= gmii_tx_er;  

107.      gmii_txd_r_d1   <= gmii_txd_r;  

108.      gmii_tx_en_r_d1 <= gmii_tx_en_r;  

109.      gmii_tx_er_r_d1 <= gmii_tx_er_r;  

110.    end  

111.  end  

112.  

113.  

114.  ODDR #(  

115.    .DDR_CLK_EDGE("SAME_EDGE")  

116.  ) rgmii_txc_out (  

117.    .Q (rgmii_txc),  

118.    .C (gmii_tx_clk_s),  

119.    .CE(1),  

120.    .D1(1),  

121.    .D2(0),  

122.    .R(tx_reset_sync),  

123.    .S(0));  

124.  

125.  

126.  generate  

127.  genvar i;  

128.  for (i = 0; i < 4; i = i + 1) begin : gen_tx_data  

129.    ODDR #(  

130.      .DDR_CLK_EDGE("SAME_EDGE")  

131.    ) rgmii_td_out (  

132.      .Q (rgmii_td[i]),  

133.      .C (gmii_tx_clk_s),  

134.      .CE(1),  

135.      .D1(gmii_txd_r_d1[i]),  

136.      .D2(gmii_txd_low[i]),  

137.      .R(tx_reset_sync),  

138.      .S(0));  

139.  end  

140.  endgenerate  

141.  

142.  ODDR #(  

143.    .DDR_CLK_EDGE("SAME_EDGE")  

144.  ) rgmii_tx_ctl_out (  

145.    .Q (rgmii_tx_ctl),  

146.    .C (gmii_tx_clk_s),  

147.    .CE(1),  

148.    .D1(gmii_tx_en_r_d1),  

149.    .D2(rgmii_tx_ctl_r),  

150.    .R(tx_reset_sync),  

151.    .S(0));  

152.  

153.    

154.       

155.      generate  

156.      for (i = 0; i < 4; i = i + 1) begin  

157.        IDDR #(  

158.          .DDR_CLK_EDGE("SAME_EDGE_PIPELINED")  

159.        ) rgmii_rx_iddr (  

160.          .Q1(gmii_rxd_s[i]),  

161.          .Q2(gmii_rxd_s[i+4]),  

162.          .C(gmii_rx_clk),  

163.          .CE(1),  

164.          .D(rgmii_rd[i]),  

165.          .R(0),  

166.          .S(0));  

167.      end  

168.      endgenerate  

169.      

170.      IDDR #(  

171.        .DDR_CLK_EDGE("SAME_EDGE_PIPELINED")  

172.      ) rgmii_rx_ctl_iddr (  

173.        .Q1(gmii_rx_dv_s),  

174.        .Q2(rgmii_rx_ctl_s),  

175.        .C(gmii_rx_clk),  

176.        .CE(1),  

177.        .D(rgmii_rx_ctl),  

178.        .R(0),  

179.        .S(0));  

180.  

181.endmodule  

上述只是示例代码(来源Altera IP Core)实现,主要是利用原语实现双边沿接收和发送。

8.5.9.4 PL设计

按照图8‑189可以很简单的进行PL部分设计,主要就涉及到两个IP,分别是PS 的IP核和GMII_to_RGMII IP。

完成后的Block如下:

图8‑190 GMII_to_RGMII IP应用Block

1、ZYNQ PS 设置

将 ETH1 及其MDIO 通过 EMIO 引出,将 FCLK_CLK0 设置为 200M,作为 GMII to RGMII IP 核内部IDELAYCTRL 的参考时钟, FCLK_RESET0_N 通过 Utility Vector Logic 生成的非门后作为 GMII to RGMIIIP 核的复位信号。如下图所示:

图8‑191 PS设置

2、gmii_to_rgmii IP设置

第一页设置

图8‑192 第一页设置截图

将 IP 核的 PHY address 设置为 6(该值可任意设置,但不能与外部的 PHY address 相同,否则将产生冲突使 IP 核工作异常)。

IP 核中 RGMII 接口的接收数据信号和控制信号需要通过 IDELAYE2 来调整信号输入延时,使其时序满足建立和保持时间约束。因此需要在 IP 核包含与IDELAYE2 相关的 IDELAYCTRL,用来校准 IDELAYE2 每个延时 tap 的延时值。本次设计的外部PHY 88E1512 发送信号延时由芯片内部提供,因此,选择 2ns 的延时 skew 由 PHY 增加。

图8‑193 第二页设置截图

选择 shared logic 包含在 IP 核内部。这部分介绍详见8.5.1RGMII PHY接口设计。

8.5.9.5 时序约束

本例程中,时序约束主要是针对 RGMII 接口进行 input delay 和 output delay 的约束,以及IDELAYE2 延时 tap 数的设置,使 RGMII 接口的满足建立和保持时间的要求,从而达到时序收敛。

对 于 input delay 和 output delay 的 约 束 , GMII to RGMII IP 核 所 自 带 的system_gmii_to_rgmii_0_0_clocks.xdc 文件中已经包含了默认设置。对于 IDELAYE2 延时 tap 数的设置,在 system_gmii_to_rgmii_0_0.xdc 中包含了默认模板。这两个 xdc 文件位置如下图所示。

图8‑195 XDC默认模板

1、input delay 约束

system_gmii_to_rgmii_0_0_clocks.xdc 文件中, 关于 input delay 的约束如下所示。约束范围为:-1.5~-15.8ns。

按照上图中的 setup time 和 hold time, input delay 的-min 应该为-(4-1.2) =-15.8ns, -max应该为-1.2ns。显然, IP 核自带 input delay 的约束范围为-1.5ns~-15.8ns,比我们计算的结果-1.2ns~-15.8ns 略为宽松,用于 setup time 计算所需的-max 时间小了 0.3ns,为了保证时序严格收敛,需要将 system_gmii_to_rgmii_0_0_clocks.xdc 文件中 set_input_delay -max 的-1.5 全部改为-1.2。需要注意的是, 这些 xdc 文件由 IP 核自动生成,每次重新配置 IP 后, vivado 都会重新生成IP 的 output product, 因此会将被修改的 xdc 文件覆盖还原,需要手动重新再次修改 xdc 文件才行。

2、IDELAYE2 延时设置

在 GMII to RGMII 的 IP 核内部为 rgmii_rx_ctl 和 rgmii_rxd[3:0]端口都连接了 IDELAYE2 模块,用来为这些信号进入 PL 内部前引入额外的延时。IDELAYE2 延时值的大小与 xdc 中的 input delay 约束是否能收敛密切相关。一般情况下,当 input delay 约束的 setup time 出现违例,应该将 IDELAYE2的 tap 数减小,降低延时值;当 input delay 约束的 hold time 出现违例,应该将 IDELAYE2 的 tap数增大,增加延时值。

system_gmii_to_rgmii_0_0.xdc 中包含了设置 IDELAYE2 的 tap 数的模板,如下图所示。

该段约束默认是被注释的,默认的延时 tap 数为 16,我们可以将这段约束复制到工程自己新建的 xdc 文件中,编译工程后根据时序报告查看 input delay 是否时序收敛,若存在时序违例,则需要根据实际情况调整 tap 的值。

3、IO 口

IO口没什么特殊的,参看源文件即可。

8.5.9.6 PS 程序设计

1、LWIP 库修改

参考上一篇文章《基于TCP/IP协议的电口通信》。

2、创建工程

本例程使用了 SDK 自带的 lwip echo server 例程来验证子卡电口和光口的功能,因此在创建工程时选择LwIP Echo Server模板,如下图所示。该例程基于LWIP库在ARM中建立一个TCP Echo Server,IP 地址为 192.168.1.10, 端口号为 7

8.5.9.7 程序测试

lwip 库设置

lwip 的设置如下图所示。

网络测试

将千兆网线插入子卡的 RJ45 座中,与电脑连接,将电脑的 ip 地址设为 192.168.1.100,子网掩码为 255.255.255.0。

然后给开发板上电,通过 SDK 将程序下载入开发板后,观察 SDK 串口打印信息,如下图示所示。表示千兆网络连接正常。

打开网络调试助手,以 TCP Client 模式连接开发板的 IP 地址 192.168.1.10,端口号 7,输入文字发送,网络调试助手便可收到开发板返回相同的文字, 如下图所示。

图8‑197 测试结果

本文分享自微信公众号 - OpenFPGA(OpenFPGA),作者:碎碎思

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • PLL、DLL、DCM区别及应用

    主要包括PLL原理、DLL原理和DCM原理,应用可能只会简单说一说,具体以原理为主。

    碎碎思
  • 2.2 SPI协议的FPGA实现

      SPI(Serial Peripheral Interface,串行外围设备接口),是Motorola公司提出的一种同步串行接口技术,是一种高速、全双工、同...

    碎碎思
  • 深入理解阻塞和非阻塞赋值的区别

    **(2)在描述时序逻辑的always块中用非阻塞赋值,则综合成时序逻辑的电路结构。

    碎碎思
  • springMvc4+hibernate4的一个奇葩的网上众说纷纭的错误

    springMvc4+hibernate4项目运行报错如下: org.hibernate.HibernateException: Could not obtai...

    小柒2012
  • 一个SingleTask与跳转传值引发的血案

    后来想到,Activity A使用了SingleTask的launchMode,猜想可能跟这个有关,在执行界面跳转的时候,不会生成新的Activity A实例,...

    代码咖啡
  • 设计模式二十四章经之解释器模式

    我就是马云飞
  • Windows Mobile Jump Start Guide

    这篇文章是交给MSTC的作业,发上来和大家共享,希望对入门windows mobile平台开发的朋友有帮助。 ? 1. Windows Mobile简介 Win...

    ShiJiong
  • Android 插件化原理解析——Activity生命周期管理

    之前的 Android插件化原理解析 系列文章揭开了Hook机制的神秘面纱,现在我们手握倚天屠龙,那么如何通过这种技术完成插件化方案呢?具体来说,插件中的Act...

    weishu
  • Activity

    佛系编码
  • java:利用fastjson判断一个类型(java.lang.reflect.Type)是否是一个javabean

    fastjson中JSON.toJSON(Object javaObject)方法将一个java对象被序列化成json对象时,返回的对象类型有三种可能:JSON...

    用户1148648

扫码关注云+社区

领取腾讯云代金券