一、背景
背景
Xilinx公司在2010年发布了可扩展的处理器平台Zynq7000系列,它采用了28nm工艺,将FPGA与ARM cortex A9集成在一颗芯片上,实现了高性能、高集成度、低功耗。Zynq7000系列有多种芯片型号可供选择,主要差别在FPGA的规模、Coretex A9核的数量、管脚数量、成本等方面。在2013年,笔者所在公司需要开发一款嵌入式产品,其中的关键部件之一就是数字信号处理板。数字信号处理板的主要功能需求是:
(1) 处理2路来自高速ADC的信号,对信号进行实时滤波、降噪;
(2) 将处理后的信号通过以太网口、以tcp连接传输到上位机进行后续处理,每一路信号的净荷速率大约320Mbps;
(3) 数字信号处理板作为嵌入式设备的主控单元,对设备的各个模块进行配置、实时运行监测、实时故障诊断、故障上报等,同时通过以太网与上位机连接,以tcp连接实时上报状态、告警、故障,响应上位机的命令等;
综合比较了处理能力、集成度、成本、功耗等因素,我最终选择了Xilinx公司在2010年发布的Zynq7000处理器,具体型号是Zynq7020。Zynq7020上的主要资源包括:双核cotex A9,85K logic cell,4.9Mb block RAM,220 DSP slices 。
本文主要阐述数字信号处理单板的设计难点之一:双千兆以太网的设计以及调试中典型问题的解决方法。
二、以太网设计方案的选择
Zynq7020片内有2个ARM Cortex A9内核,我们分别称之为core0、core1,芯片内还内嵌了2个MAC控制器。在单板硬件上,我们部署了2个千兆以太网接口,分别称之为eth0、eht1。从功能需求可知:以太网需要传输2种类型的数据,一类是低速的控制面数据,主要是与上位机的消息交互,消息是突发性的,峰值数据速率不超过10Mbps,这对于千兆以太网负荷根本不是问题;第二类是高速的信号数据,净荷速率大约是每路320Mbps,且信号速率是恒定的,考虑到tcp报头的开销,以太网总传输速率大约在334Mbps。
对开发平台的说明:
集成开发工具SDK版本:SDK 14.5(该版本的SDK工具本身以及其所带的库文件都还有bug,库文件的bug在调试中自己加以修正了)
虚拟机:VMware_8.0.4
Ubuntu:12.04 LTS
GCC工具链:4.6.1
Linux kernel:从xilinx官网上下载了linux-xlnx-xilinx-v2013.4.tar.gz
在Zynq7020的功能部署上有三种方案可选择:
(1)方案一:core0、core1工作在SMP模式,嵌入式Linux OS管理2个核,提供完整的tcp/ip协议栈;
core0:控制eth0,实现控制面消息传输、一路334Mbps的数据传输;
coe1:控制eth1,实现一路334Mbps的数据传输;
(2)方案二:core0、core1工作在AMP模式,core0运行嵌入式Linux OS和完整的tcp/ip协议栈;core1裸跑、无OS,运行轻量级的tcp/ip协议栈lwip;
core0:控制eth0,实现控制面消息的传输;
core1:控制eth1,实现2路高速的数据传输,数据速率需达到334×2=668Mbps;
(3)方案三:core0、core1工作在AMP模式,core0运行嵌入式Linux OS和完整的tcp/ip协议栈;core1裸跑、无OS,运行轻量级的tcp/ip协议栈lwip;
core0:控制eth0,实现控制面消息传输、一路334Mbps的数据传输;
core1:控制eth1,实现一路334Mbps的数据传输;
三种方案似乎都能够实现需求,方案一实现上更简单一些,因为2个core是工作在SMP模式,不需要考虑cache、内存等核间公共资源的核间同步与互斥,也不需要考虑核间通信,但是,经过仔细的设计,测试结果却表明:这种方案不能保证每个网口稳定支持334Mbps的速率。
方案二、方案三都可以满足以太网的速率传输要求,我们在产品的第一个版本中选择了方案二(单网口支持668Mbps),在产品第二个版本中选择了方案三(每个网口分别支持334Mbps)。这两种方案中,Zynq7020的2个核都是工作在AMP模式下,相比于SMP模式,有很多设计细节需要仔细考虑,比如:
(1) 内存使用:core0、core1的代码及数据空间在大小上要合理分配,在物理上分开、避免冲突;
(2) Zynq7020中的二级cache是2个核共用的,在使用中要避免冲突;
(3) Eth0由core0驱动、eth1由core1驱动,在驱动层要做对应的更改;
(4) Lwip协议栈的性能优化,以便达到在千兆网上可稳定承载668Mbps的速率;
值得一提的是:在xilinx早期的SDK及Linux驱动中,对Zynq7020多核AMP模式的支持、对以太网驱动的支持也不完善,因此,我在产品调试中花费了大量时间和精力,解决了许多底层问题。令人欣慰的是:与SDK相关的工具问题、库文件的bug问题以及linux驱动对zynq芯片的支持问题,在xilinx后续发布的SDK版本、linux版本中,基本上都得到了修正。
下面就以太网的驱动、lwip的性能优化问题做稍微详细一些的描述。
三、以太网驱动的修改
core1是裸跑、无OS,SDK提供了lwip server的测试代码以及底层的以太网driver代码,我基于此代码进行修改,以实现在core1上支持ETH1。涉及到修改内容如下:
1、 core0上devicetree相关内容的修改,包括:
(1) 指定双核系统工作于AMP模式:在devicetree中,修改kernel的启动参数bootargs,在其中增加max_cpus=1,即:限定linux OS使用的cpu数量为1,另外一个cpu不属于linux OS管理;
(2) 在devicetree中,去掉对eth1的配置,以便kernel不对eth1进行相关的初始化;
2、 core0上的linux kernel中,对GIC中断distributor初始化(irq_gic.c)进行修改:原来所有的SPI(Shared Peripheral Interrupt)都只有core0接收,现需要将ETH1的MAC中断(中断号为77)改为由core1接收;
3、 core1上对PHY驱动的修改:修改Eth1的PHY1驱动。Zynq7020虽然有2个MAC控制器、可连接2个PHY,但是,PHY的控制总线(MDC、MDIO)是2个MAC共享的,在硬件设计中,只有MAC0的MDC、MDIO总线可以连接PHY,如下图所示。因此,core1如果要访问PHY1,需通过MAC0来中转,而这需要修改SDK的库文件来实现。
需要修改xilinx的SDK库文件有:Xemacpsif_hw.c, xemacpsif.c, xemacpsif.h, xemacpsif_physpeed.c
以上文件在xilinx的如下安装目录下:xilinx/14.5/ISE_DS/EDK/SW/ThirdParty/SW_service /lwip140_v1_04_a /src/contrib/port/Xilinx/netif。
4、 core1上增加对MAC1的初始化。由于在core0中不对ETH1进行初始化了,所以,在core1中必须完成对MAC1的初始化。包括:
(1)与MAC1相关的时钟:
AMBA peripheral clock control:SLCR.aper_clk_ctrl,寄存器地址是:0xf800012c,其中的bit7需要enable;(特别注意:这个时钟一定要首先enable,否则,MAC1的控制寄存器、配置寄存器都无法写入!)
GEM1的参考时钟:SLCR.gem1_ctrl,寄存器地址是:0xf800 0144。
(2)与MAC1相关的控制及配置寄存器;
以上4步正确完成,则在core1上的ETH1可以正常工作了,如:可以正常ping通了。
四、对lwip协议栈性能的优化
Core1上tcp/ip协议采用了轻量级的协议栈实现包lwip,为了要在单网口上承载稳定的668Mbps速率,必须从以下2个方面进行性能优化:(1)内存空间的分配及使用;(2)lwip的相关参数进行仔细调整;这二者缺一不可,否则,达不到性能要求,尤其是在单网口承载668Mbps情况下。
1、core1的内存分配
由于lwip协议栈需要大量的内存,因此,core1上总的内存空间分配不能太小,否则将严重制约lwip的性能。在性能测试中,曾出现一开始网速很低(几十Mbps)、并且找不到原因,在一次调整lwip相关参数时,编译器提示内存空间不够,这才受到启发去检查内存大小,发现链接文件中指定的整个core1上的内存只有区区4M bytes。如果没有lwip协议栈,这些内存对于应用而言倒也是绰绰有余,但是,增加了lwip后,内存就显得不足、严重影响了传输性能。重新设定了链接文件中的stack size、heap size以及整个内存的size:
Stack size = 0x20000
Heap size = 0x20000
内存size = 0x04000000
从而显著地提升了lwip的性能、提升了网口速率。
2、lwip协议栈的参数调整
在lwip的实现中,有很多参数可以通过配置来灵活地调整,实际应用需要根据具体的场景进行参数分析和参数调整。在我们的应用场景中,主要是向外发送大量的数据包,并且每个数据包大小固定、且很大(20K字节左右),而从外部接收到的数据包很少。经过分析与测试,如下的参数设置可达到优异的性能:
参数 | 调整后值 | 缺省值 | 描述 |
---|---|---|---|
n_tx_descriptor | 512 | 64 | / |
mem_size | 2097152 | 131072 | byte size of heap memory |
memp_n_pbuf | 8192 | 16 | number of memp struct pbufs |
memp_n_tcp_seg | 2048 | 256 | number of simultaenuously queued tcp segments |
tcp_snd_buf | 65535 | 8192 | tcp sender buffer space (bytes) |
tcp_wnd | 65535 | 2048 | tcp windows (bytes) |
pbuf_pool_bufsize | 2048 | 1700 | size of each pbuf in pbuf pool |
pbuf_pool_size | 10240 | 256 | number of buffers in pbuf pool |
五、小结
本文主要描述了利用Xilinx的Zynq7020 SOC实现高速数字信号采集板的方案,产品开发时间是在2013年、上市时间在2014年上半年。重点描述了方案中,通过Zynq7020实现双千兆以太网进行高速数据传输的难点,以及在实际中所解决的问题。本文中的方案二、三都已实现了产品化,其中,方案二实现了单网口稳定承载668Mbps的网速,方案三实现了2个网口分别承载334Mbps的网速。从性能、功耗、成本、集成度、可扩展性等因素考量,Zynq7000 SOC是实现高速数字信号处理板的最有竞争力的方案之一。
从2013年到现在(2019年),Xilinx的Zynq7000系列处理器已经在许多行业得到了广泛应用,Zynq处理器也在不断向前演进,形成了Zynq7000、Zynq Ultrascale+MPSOC、Zynq Ultrascale+RFSOC三种主要系列,每个系列又有面向不同应用的多种规格,AI边缘计算、AI云计算等领域都是zynq处理器可以大显身手的地方,另外,Xilinx的开发工具、开发平台也在持续演进,如:2019年10月发布的、里程碑式的统一软件平台Vitis!