MII2RGMII IP核使用设计举例

作者:碎碎思 ,文章来源:OpenFPGA微信公众号

本例程将 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的详细解释可以参考官方数据手册,这里简单介绍一下实现原理。

图8‑194 GMII和RGMII接口对比

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 测试结果

最新文章