跳转到主要内容

MII2RGMII IP核使用设计举例

judy 提交于

作者:<span id="js_author_name" datarewardsn="" datatimestamp="" datacanreward="0">碎碎思</span> <span id="profileBt">,文章来源:<a href="https://mp.weixin.qq.com/s?__biz=Mzg4ODA5NzM1Nw==&amp;mid=2247486147&am… href="https://mp.weixin.qq.com/s?__biz=Mzg4ODA5NzM1Nw==&amp;mid=2247486147&am…;微信公众号</a>

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

<strong>8.5.9.1 应用背景</strong>

在使用ZYNQ系列芯片时,使用PS端的ETH 通过 EMIO 引出后为标准的 GMII 接口, 如下图所示。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑188 PS端的ETH 通过 EMIO 引出</strong></p>

或者某些IP在使用时出来的接口就是标准的GMII接口,而目前常用的千兆PHY都是使用RGMIi接口,所以为了使用外部PHY,需要通过 IP 核 GMII to RGMII 将 GMII 接口转换为 RGMII 接口,才能与 PHY 芯片连接。连接原理如下图所示。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑189 GMII to RGMII连接原理</strong></p>

<strong>8.5.9.2 外部PHY时序</strong>

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

<strong>8.5.9.3 GMII_to_RGMII原理</strong>

这个IP的详细解释可以参考官方数据手册,这里简单介绍一下实现原理。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑194 GMII和RGMII接口对比</strong></p>

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)实现,主要是利用原语实现双边沿接收和发送。

<strong>8.5.9.4 PL设计</strong>

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

完成后的Block如下:
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑190 GMII_to_RGMII IP应用Block</strong></p>

1、ZYNQ PS 设置

将 ETH1 及其MDIO 通过 EMIO 引出,将 FCLK_CLK0 设置为 200M,作为 GMII to RGMII IP 核内部IDELAYCTRL 的参考时钟, FCLK_RESET0_N 通过 Utility Vector Logic 生成的非门后作为 GMII to RGMIIIP 核的复位信号。如下图所示:
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑191 PS设置</strong></p>

2、gmii_to_rgmii IP设置

第一页设置
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑192 第一页设置截图</strong></p>

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

IP 核中 RGMII 接口的接收数据信号和控制信号需要通过 IDELAYE2 来调整信号输入延时,使其时序满足建立和保持时间约束。因此需要在 IP 核包含与IDELAYE2 相关的 IDELAYCTRL,用来校准 IDELAYE2 每个延时 tap 的延时值。本次设计的外部PHY 88E1512 发送信号延时由芯片内部提供,因此,选择 2ns 的延时 skew 由 PHY 增加。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑193 第二页设置截图</strong></p>

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

<strong>8.5.9.5 时序约束</strong>

本例程中,时序约束主要是针对 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 文件位置如下图所示。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑195 XDC默认模板</strong></p>

1、input delay 约束

system_gmii_to_rgmii_0_0_clocks.xdc 文件中, 关于 input delay 的约束如下所示。约束范围为:-1.5~-15.8ns。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>

按照上图中的 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 数的模板,如下图所示。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>

该段约束默认是被注释的,默认的延时 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
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>

<strong>8.5.9.7 程序测试</strong>

lwip 库设置

lwip 的设置如下图所示。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>

网络测试

将千兆网线插入子卡的 RJ45 座中,与电脑连接,将电脑的 ip 地址设为 192.168.1.100,子网掩码为 255.255.255.0。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>

然后给开发板上电,通过 SDK 将程序下载入开发板后,观察 SDK 串口打印信息,如下图示所示。表示千兆网络连接正常。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>

打开网络调试助手,以 TCP Client 模式连接开发板的 IP 地址 192.168.1.10,端口号 7,输入文字发送,网络调试助手便可收到开发板返回相同的文字, 如下图所示。
<center><img src="http://xilinx.eetrend.com/files/2020-06/%E5%8D%9A%E5%AE%A2/100049699-98…; alt=""></center>
<p align="center"><strong>图8‑197 测试结果</strong></p>