软件环境:vivado 2017.4 硬件平台:XC7Z035
在上一篇的基础上,这篇主要说下LINUX这边巨型帧(JUMBO FRAME)9K的包怎么跑通。
底图相对前一篇稍微做了点修改,加上了DMA的LOOP回环,倒不是因为ETHERNET_9K需要这样搞,而是为了后面做LINUX这边跑AXI_DMA时候,底图就不用做修改了而已,如果只想做ETHERNET_9K实验的话,DMA_LOOP那几个模块可以不添加。
AXI_ETHERNET的设置如下。接口选RGMII。
MAC特性如下。
AXI_DMA参数设置如下。
时钟模块的输入时钟根据自己系统更改。
输出时钟如下。
另外需要说明一下,如果像我这种,底图上有多个DMA模块的时候,HP口分配地址可能会有冲突报错,这时候换个HP口一般就行了,这里我用的是HP0和HP1。
最后加入管脚约束文件, Generate Bitstream,Export Hardware(注意勾选include biestream),最后launch SDK进行测试。
SDK这边主要需要以下几个文件。
第一个是设备树,用来将底层设备和总线上的驱动匹配用的,这个东西很重要。第二个是VIVADO导过来的二进制码流。第三个是FSBL,系统启动引导用的,类似于WINDOWS的BIOS。
首先说下device_tree这个文件怎么生成。
Xilinx----->Reoisitories然后在New里面选择在xilinx官方下载好的device tree支持包,点OK就行。
然后File----->New----->Board Support Package。
左下角选device_tree,不用做什么改动,直接Finish就行。
FSBL生成方法是File----->New----->Application Project。
名字zynq_fsbl----->Next。
选最后一个zynq fsbl,然后直接finish就行。
最后编译整个工程。
将整个VIVADO和SDK的工程导入制作LINUX内核的工程中,跟内核一起编译,主要说下要注意的事项。
第一,配kernel的时候,不论是用ps端的网口,还是axi_ethernet,记得把phy芯片的驱动勾上。
Device Drivers----->Network device support----->PHY Device support and infrastructure我这边用的是micrel phys
第二,device tree自动生成以后,还要稍微做些修改,可以看到自动生成的文件主要有以下几个。
通常修改也就主要是在pl.dtsi和system-top.dts这两个文件里面修改,涉及PL端在pl.dtsi里修改,涉及PS端的在system-top.dts里修改。
我这边pl.dtsi文件内容如下。
/*
* CAUTION: This file is automatically generated by Xilinx.
* Version:
* Today is: Wed Mar 13 11:13:20 2019
*/
/ {
amba_pl: amba_pl {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
ranges ;
axi_dma_0: dma@40400000 {
#dma-cells = <1>;
clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>;
compatible = "xlnx,axi-dma-1.00.a";
reg = <0x40400000 0x10000>;
xlnx,addrwidth = <0x20>;
xlnx,sg-length-width = <0x17>;
dma-channel@40400000 {
compatible = "xlnx,axi-dma-mm2s-channel";
dma-channels = <0x1>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x0>;
};
dma-channel@40400030 {
compatible = "xlnx,axi-dma-s2mm-channel";
dma-channels = <0x1>;
xlnx,datawidth = <0x20>;
xlnx,device-id = <0x0>;
};
};
axi_dma_1: dma@40410000 {
#dma-cells = <1>;
axistream-connected = <&axi_ethernet_0>;
axistream-control-connected = <&axi_ethernet_0>;
clock-names = "s_axi_lite_aclk";
clocks = <&clkc 15>;
compatible = "xlnx,eth-dma";
interrupt-names = "mm2s_introut", "s2mm_introut";
interrupt-parent = <&intc>;
interrupts = <0 31 4 0 32 4>;
reg = <0x40410000 0x10000>;
xlnx,include-dre ;
};
axi_ethernet_0: ethernet@41000000 {
axistream-connected = <&axi_dma_1>;
axistream-control-connected = <&axi_dma_1>;
clock-frequency = <100000000>;
clock-names = "ref_clk";
clocks = <&clkc 0>;
compatible = "xlnx,axi-ethernet-1.00.a";
device_type = "network";
interrupt-names = "mac_irq", "interrupt";
interrupt-parent = <&intc>;
interrupts = <0 29 1 0 30 4>;
phy-mode = "rgmii";
reg = <0x41000000 0x40000>;
xlnx = <0x0>;
xlnx,axiliteclkrate = <0x0>;
xlnx,axisclkrate = <0x0>;
xlnx,clockselection = <0x0>;
xlnx,enableasyncsgmii = <0x0>;
xlnx,gt-type = <0x0>;
xlnx,gtinex = <0x0>;
xlnx,gtlocation = <0x0>;
xlnx,gtrefclksrc = <0x0>;
xlnx,include-dre ;
xlnx,instantiatebitslice0 = <0x0>;
xlnx,phy-type = <0x3>;
xlnx,phyaddr = <0x1>;
xlnx,rable = <0x0>;
xlnx,rxcsum = <0x0>;
xlnx,rxlane0-placement = <0x0>;
xlnx,rxlane1-placement = <0x0>;
xlnx,rxmem = <0x8000>;
xlnx,rxnibblebitslice0used = <0x0>;
xlnx,tx-in-upper-nibble = <0x1>;
xlnx,txcsum = <0x0>;
xlnx,txlane0-placement = <0x0>;
xlnx,txlane1-placement = <0x0>;
axi_ethernet_0_mdio: mdio {
#address-cells = <1>;
#size-cells = <0>;
};
};
};
};
system-top.dts文件内容如下。
/*
* CAUTION: This file is automatically generated by Xilinx.
* Version:
* Today is: Wed Mar 13 11:13:20 2019
*/
/dts-v1/;
/include/ "zynq-7000.dtsi"
/include/ "pl.dtsi"
/include/ "pcw.dtsi"
/ {
chosen {
bootargs = "earlycon";
stdout-path = "serial0:115200n8";
};
aliases {
ethernet0 = "&gem0";
ethernet1 = "&axi_ethernet_0";
serial0 = "&uart1";
spi0 = &qspi;
};
memory {
device_type = "memory";
reg = <0x0 0x40000000>;
};
};
&uart1 {
u-boot,dm-pre-reloc;
};
&gem0 {
phy-handle = <ðernet_phy>;
ethernet_phy: ethernet-phy@1 {
reg = <1>;
device_type = "ethernet-phy";
};
};
&axi_ethernet_0 {
local-mac-address = [00 0a 35 00 01 22];
phy-handle = <&phy1>;
xlnx,has-mdio = <0x1>;
phy-mode = "rgmii";
mdio {
phy1: phy@1 {
device_type = "ethernet-phy";
reg = <1>;
};
};
};
这次的改动也主要就是在system-top.dts文件里改的,如果使能了ps的ethernet同时,axi_ethernet也在使用的话,aliases内容建议与我这一样。自动生成的device tree文件里,ethernet0和ethernet1的内容是反的,我当时启动以后,只有PS端的ethernet能正常使用,PL端的axi_ethernet怎么都挂不上,换了顺序以后,两个网口就都正常了。
第三,内核编译完,系统加载完,通常网络是不通的,还需要单独设置一下。设置路径在/etc/network/interfaces下,这里eth0就对应的是device tree中的PS端的ethernet,eth1对应的就是PL端的axi_ethernet。如果提示无法保存设置,先使用ifconfig eth0 down,ifconfig eth1 down,将两个网卡关掉,然后再打开interfaces设置,设置完毕后ifconfig eth0 up,ifconfig eth1 up将两个网卡启动,就OK了。
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
auto eth1
iface eth1 inet static
address 192.168.31.77
netmask 255.255.255.0
gateway 192.168.31.1
mtu 9000
这里的mtu 9000就是设置可发送和接收的最大帧长度。其它再不需要什么改动。ping一下我PC的主机试试。
可以看到,没啥子问题。
你以为这样就结束了吗?其实并没有,还是要哔哔两句,上面这种测试只能说明网是通的,9K包的收发是正常的,性能不是这样测试的,说一下,两种方法。
第一种,也是网上搜索推荐很多的,LINUX端用iperf,WINDOWS端用jperf,参数一设置,全自动跑,直接出结果。
分服务器端和客户端,也就是说,你板子又不能自测,要有人给你发,谁发,WINDOWS发呗,那WINDOWS发的话,就又要找WINDOWS端能用的jperf,然后WINDOWS端安装、设置,ZYNQ端安装、设置,一套下来头都大了,我试过,也没试出个啥所以然来,贴个测试的示意图,有耐心的话,你可以试试。
第二种,WINDOWS端自己拿C++在Visual Studio写个UDP发送的例程,一直发8190的包就行,然后ZYNQ这边也是自己写个UDP纯接收的程序,两边的代码都很简单,程序设计的灵活性也比较大。图就不贴了,说下注意的点,WINDOWS端单线程UDP发送最大速率只能到350M左右,不知道为什么,所以想要测试千兆的速率,只能把同一个UDP发送程序打开多个模拟多线程发送(或者直接写个多线程的),LINUX这边,我当时测试的这种情况下最大的接收速率在700~800M之间,数据不是特别稳定,利用top命令查看CPU利用率大概在单核70%左右,有丢包的情况,不过丢包是肯定的,毕竟多线程这样发,丢包的主要原因大概是粘包了,因为只是测试而已,所以就没再深究。