作者:<span id="js_author_name" datarewardsn="" datatimestamp="" datacanreward="0">碎碎思</span> <span id="profileBt">,文章来源:<a href="https://mp.weixin.qq.com/s?__biz=Mzg4ODA5NzM1Nw==&mid=2247486147&am… href="https://mp.weixin.qq.com/s?__biz=Mzg4ODA5NzM1Nw==&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>