本文介绍在RTL8211千兆网络模块基础上实现PS端的EMIO扩展以太网网口功能。
写在前面:
ZYNQ 网络功能可以有4种方式实现
- 一 PS端 MIO功能直接扩展以太网口
- 二 PS端通过EMIO的方式将以太网口映射到PL端口上
- 三 PL端口的硬件以太网功能 通过AXI的方式供PS端调用
- 四 PL端用逻辑跑以太网功能(不需要PS端参与)
我们这里 因为网口接到了 PL端上,所以理论上 可以实现二 三 四, 第二种和第三种方式 其实效果差不多(方式三更占用逻辑资源),而方式四是 FPGA直接控制网络,所以为了方便测试我们先用方式二 EMIO的方式来进行测试
本文在 vivado2018.3版本上 演示, 其他版本请自行研究
工程创建
1)新建一个项目,芯片型号选择按下面选择(板子生产了多个芯片型号,请根据自己板子的型号来进行选择)
- XA7Z020 (直接选择xc7z020CLG400-1,因为系统识别到的是xc7z020-1)
- XC7Z010-1 (xc7z010CLG400-1) (7010 芯片)
- XC7Z020-1 (xc7z020CLG400-1) 后续生产
- XC7Z020-2 (xc7z020CLG400-2) 后续生产
备注 (7020 车规级版在vivado2018.3识别出的是XC7Z020,如果工程选择xa7z020 ,在SDK环境下有时候会弹出警告,所以这里直接选择XC7Z020CLG400-1)
2) 创建一个BLOCK设计,并添加ZYNQ7 PROCESSING SYSTEM模块,软件自动生成了一个 zynq的block 如下图所示,接下来要做一些相应的设置,双击下图中的ZYNQ核
3)依次在弹窗里找到DDR Configuration→DDR Controller Configuration→DDR3,在Memory Part下拉菜单中根据自己板子上的DDR来选择相应的DDR3,本实验所用到型号:MT41K256M16RE-125,数据位宽选择16bit 最后点击“OK”,如下图所示。
因为工程暂时用不到 AXI功能,所以可以先禁用AXI功能
4) 这里把ENET0通过EMIO引出来 ,并且选中MDIO功能
之后点选OK 来保存ZYNQ的配置
5)由于 ZYNQ的PS 调用EMIO方式 默认输出是GMII,而我们的网络部分的芯片 RTL8211是 输出RGMII的,所以这里我们需要增加一个 GMII和RGMII转换的IP模块 (Gmii to Rgmii)
打开 GMII TO RGMII模块 设置更改成下面
6) 又因为GMII TO RGMII 是高电平复位 ,但是ZYNQ 是输出低电平复位的,所以这里需要增加一个反相器(搜索utility, 在检索出来的选项里 选择 utility vector logic)
在设置页里改成 not(反相器)将位宽改成1
7) 按下图方式连接好各个模块(复位 时钟 网络)
8)并通过右键 然后选择Make external 的方式引出RGMII 和MDIO_PHY功能引脚
9)由于我们的板上 网络部分的时钟25M单独有晶振控制了,所以这里不需要额外引出25M时钟
10)这里还要对Z7的模块进行一个修改,因为gmii to rgmii 模块的clkin 在手册上写要求是200mhz,所以我们这里需要对ZYNQ核的输出时钟频率进行修改
重新双击ZYNQ的核,将FCLK_CLK0的频率改成200
12)另外 因为 SDK中 网络功能的例程都需要UART功能来调试,所以这里额外在ZYNQ核心中增加串口功能(选择MIO方式,并且MIO 选择50-51)
13) 所有的都完成后点选 Run Block Automation,完成后续自动布线
14)创建硬件描述,source→Design Source ,右键我们创建的BLOCK工程,点击create HDL wrapper如下图所示(这一步的作用相当于将图纸转换成对应的硬件描述语言的功能)
15)编写好代码后 对代码进行编译
16)添加约束文件 对管脚 和 电平进行约束, 尤其要注意 最下面的时序约束部分 一定要添加,不然大概率无法ping通
set_property PACKAGE_PIN C20 [get_ports {RGMII_0_td[1]}] set_property PACKAGE_PIN E19 [get_ports RGMII_0_tx_ctl] set_property PACKAGE_PIN D20 [get_ports {RGMII_0_td[3]}] set_property PACKAGE_PIN B20 [get_ports {RGMII_0_td[0]}] set_property PACKAGE_PIN D18 [get_ports {RGMII_0_rd[2]}] set_property PACKAGE_PIN F20 [get_ports MDIO_PHY_0_mdio_io] set_property PACKAGE_PIN F19 [get_ports MDIO_PHY_0_mdc] set_property PACKAGE_PIN D19 [get_ports {RGMII_0_td[2]}] set_property PACKAGE_PIN B19 [get_ports {RGMII_0_rd[3]}] set_property PACKAGE_PIN F17 [get_ports {RGMII_0_rd[0]}] set_property PACKAGE_PIN A20 [get_ports RGMII_0_txc] set_property PACKAGE_PIN J18 [get_ports RGMII_0_rxc] set_property PACKAGE_PIN E17 [get_ports RGMII_0_rx_ctl] set_property PACKAGE_PIN E18 [get_ports {RGMII_0_rd[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[1]}] set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_tx_ctl] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[0]}] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[2]}] set_property IOSTANDARD LVCMOS33 [get_ports MDIO_PHY_0_mdio_io] set_property IOSTANDARD LVCMOS33 [get_ports MDIO_PHY_0_mdc] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_td[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[0]}] set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_txc] set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_rxc] set_property IOSTANDARD LVCMOS33 [get_ports RGMII_0_rx_ctl] set_property IOSTANDARD LVCMOS33 [get_ports {RGMII_0_rd[1]}] create_clock -period 8.000 -name RGMII_0_rxc -waveform {0.000 4.000} [get_ports RGMII_0_rxc] set_clock_groups -logically_exclusive -group [get_clocks -include_generated_clocks {gmii_clk_25m_out gmii_clk_2_5m_out}] -group [get_clocks -include_generated_clocks gmii_clk_125m_out] set_property SLEW FAST [get_ports {RGMII_0_td[0]}] set_property SLEW FAST [get_ports {RGMII_0_td[1]}] set_property SLEW FAST [get_ports {RGMII_0_td[2]}] set_property SLEW FAST [get_ports {RGMII_0_td[3]}] set_property SLEW FAST [get_ports RGMII_0_tx_ctl] set_property SLEW FAST [get_ports RGMII_0_txc] set_property SLEW FAST [get_ports MDIO_PHY_0_mdc] set_property SLEW FAST [get_ports MDIO_PHY_0_mdio_io]
17. 修改之后 点击 Generate Bitstream 进行综合以及生成Bit 文件
等待布线综合完成
PS部分工程创建
1)File→Export→Export hardware…,在弹出的对话框中勾选“include bitstream”,点击“OK”确认,如下图所示。
2)File→Lauch SDK,在弹出的对话框中,保存默认,点击“OK”,如下图所示。
系统将自动打开SDK开发环境
3)在SDK环境下创建网络工程
输入一个工程名,然后点选下一步,选择 LwIP Echo Server选项,自动导入网络SDK自带的网络例程。
4)对网络进行调试
硬件部分将网络模块的RJ45接上网线
将主板通过TYPEC 连接电脑(在设备管理器中查看主板的串口在系统中的串口号),并且在SDK 的终端中添加这个串口,如下图所示(波特率115200) 也可以用第三方串口助手来调试
接下来点选program FPGA 对FPGA进行配置
接下来点选DEBUG
最好对RUN AS进行设置 勾选 Reset entire system 和Program FPGA 再下载,否则有可能不能正常访问网络(原因在最下面的备注项)里
勾选 Reset entire system 和Program FPGA
通过串口观察到网络已经启动,并且速度为1000M, 网络IP为192.168.1.113(板子需要连接到路由器才能分配到IP) ,如果板子是直接用网线连接PC电脑,请看文章最后的备注
用相同路由器网段的 WINDOWS电脑 ping 一下这个IP 可以ping 通,说明网络连接成功了
通过网络助手(百度可以下载)连接后,发送什么数据,就能通过网络接收到什么数据(设置的端口号是7,IP地址按照之前SDK 串口读到的板子IP地址设置)
备注:有部分客户按上述先下载PL 后debug的方法操作 出现无法自动获取路由器的IP(并且无法ping通的情况),并且路由器列表上没有检测到设备(猜测可能是因为PL单独下载后,PS还没下载的情况下,路由器和板子握手失败后,就停止了板子访问网络的权限,造成板子网络部能正常工作),这种情况下可以按下面方法勾选reset entire system,和program fpga选项(让系统每次debug 都能重新配置整个fpga)
a )右键点我们的PS工程,调试界面里 点击RUN AS 里的 设置项目,如下图所示
b)设置里勾选 复位整个工程和下载FPGA部分代码的选项 如下图所示,并点击应用和RUN
c)勾选上述两个选项后,每次debug的时候,系统都会重新配置FPGA和初始化整个工程,网络功能也恢复正常了。
以上 内容仅供参考
另外,很多网站都是手动把速度设置成1000mbps 但是我这边设置了反而无法连接了,怀疑是phy芯片和demo有点冲突引起的,后续会继续研究,以及大吞吐量测试 以确定网络模块功能正常
后记: 经过IPREF测试 系统大吞吐量测试可以稳定在 940M环境下,模块工作稳定,至于上面的问题应该是PHY握手比较慢,怀疑是官方demo在手动模式下 超时引起的, 设置成自动模式连接千兆没问题(大吞吐量测试图如下, 有时间再整理下大吞吐量的测试方法,也是XILINX的官方demo)
完整工程如下,SDK程序全部都是官网的例程,具体功能还没深入研究,大家自行研究
下面是网络调试助手
板子和PC 通过网线直连的注意事项
备注: 关于板子和PC 直接通过网线直连, 之前一直发现不成功 无法ping通, 或者板子先和路由器直连,获取到IP后,再拔下网线 将板子和PC 直连,此时可以ping通,但是如果一开始直接直连,就永远都无法ping通, 经过最近的调试终于发现问题了。
板子如果和PC 直连, 此工程必须要接交叉式的网线(也就是PC连PC的网线)才可以正常ping通。( 但是很奇怪当板子跑PYNQ的时候就不需要一定用交叉的网线),怀疑是官方SDK的例程没有打开MDI SWAP 功能导致的(也可能通过程序可以打开,暂时还没找到),所以,如果大家遇到直连ping不通的情况下 ,请试着更换特殊的交叉网线再进行尝试。
直连状态会无法获得IP,那系统自动分配IP 192.168.1.10, 那本地主机也记得将对应的网络口设置成192.168.1.* 的网络段, 网关设置成192.168.1.1才可以ping通