1. 引言
APSoc是新一代全可编程片上系统(全可编程指的是硬件和软件都可以编程),它的典型代表是赛灵思推出的Zynq 7000系列,它在单芯片内集成了基于具有丰富特点的双核ARM Cortex-A9多核处理器的处理系统和Xilinx可编程逻辑,还包含了片上储存器、外部储存器接口和一套丰富的I/O外设。APSoc具有全可编程的特性,这让设计者能使用工业标准的工具在单个平台上实现高性能和低成本的应用。APSoc目前在汽车驾驶辅助系统、摄像机、工业电机控制、智能相机、视频设备和机器视觉等领域得到了广泛的应用,具有广阔的前景 [1]。
总体上来看,一般的APSoc都分为PS和PL两大部分,其中PL部分为FPGA (现场可编程逻辑门阵列),它由可编程输入/输出单元、可编程逻辑单元、嵌入式块RAM等部分组成,它可以实现任何数字器件的功能,具有高度的灵活性与可扩展性 [2]。而PS部分包含了单个或多个的ARM处理器和一组相关的处理资源,形成了一个应用处理器单元(Application Processing Unit, APU) [3],还有众多接口,既有PS和PL之间的,也有PS和外部部件之间的,PS部分是整个系统的核心,PL可以看作是PS部分的外设。
Linux是一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统,它具有免费、开源、稳定、安全的优点,在嵌入式开发领域得到了广泛的应用 [4]。因为大部分APSoc中有ARM处理器,所以也支持Linux系统的运行,将二者相结合,可以利用Linux成熟的设备管理体系,更充分地发挥APSoc灵活多变的优势,甚至可以实现其原本不具备的功能,具有极大的研究价值 [5]。
Vivado是一款应用广泛的EDA软件,它可以对ApSoc进行设计,其最终产生的BIT文件用于加载到PL部分以实现预先设计的功能 [6]。传统加载BIT文件的方式为JTAG加载,其弊端是加载慢,加载前需要让系统停止工作,且需要专门的加载工具,非常不方便 [7]。因为APSoc中的PS部分可以运行Linux系统,并且PS和PL之间有互通的接口,那么使用Linux系统对整个芯片进行控制,以实现PL动态加载BIT是存在可能性的。本文选择国产的APSoc芯片作为试验平台,使用Vivado产生带有AXI DMA功能的BIT文件,在试验平台上部署Linux系统,研究如何实现动态加载BIT,并对预先设计的AXI DMA功能进行测试以确定试验是否成功。
2. 试验平台
本文所选的试验平台为复旦微公司的FMQL45T900开发板,其实物图如图1所示。
该开发板使用的APSoc芯片为45T900,其系统框图如图2所示
分析图2发现,该芯片的PL与PS之间有通用接口,这些通用接口使用的是AXI协议,该协议是由ARM公司提出的一种高性能、高带宽、低延迟的片内总线,支持突发传输,突发传输过程中只需要首地址,支持显著传输访问和乱序访问,在APSoc的开发中具有重要的地位,是PS与PL之间的桥梁,也是本次试验可以开展的基础 [8]。

Figure 2. 45T900 system block diagram
图2. 45T900系统框图
AXI接口分为四种,分别是M_AXI_GP、S_AXI_ACP、S_AXI_GP和S_AXI_HP,M_AXI_GP表示PS端做主机,PL做从机,PS通过该接口控制PL。本次试验中,需要使用M_AXI_GP接口对PL中的AXI DMA模块进行控制,实现动态加载要对该接口进行释放与恢复。
3. Vivado平台的搭建
3.1. Block Design图
为了测试动态加载BIT是否成功,本次试验在Vivado软件中搭建了包含AXI DMA模块的工程,Block Design如图3所示。

Figure 3. Block design of Vivado
图3. Vivado的Block Design
该工程中,system模块代表PS部分,根据开发平台的实际情况,在该模块中配置串口和DDR3控制器,并开启GP接口,以开启PS对PL的控制。axi_dma模块为本次动态加载的测试对象,设置其字段缓冲长度的有效位数为14,地址空间的宽度保持默认值32,取消使能高度优化的DMA,以便传输大量数据,开启DMA的读写通道,并且读写通道都设置为两个。添加concat IP,可以将DMA的写中断信号与读中断信号整合为一个信号输送到PS,简化了系统的设计。axi_smc ip用于管理AXI通道,系统会将AXI通道集中通过该IP进行传输,当开启HP或者GP口时,vivado会自动生成该IP,无需手动添加,axis_data_fifo作为axi_dma的附属IP,也是自动生成,作用是为axi_dma缓冲数据,避免短时间内有大量数据经过axi_dma,导致系统出现过载。
Vivado可以实现自动连接IP,但该功能无法完全满足试验的需要,经过检查,还需要将mm2s_introut和s2mm_introut与in0、in1相连接,然后将dout与system的IRQ_F2P口相连接,完成DMA的中断信号与PS的匹配。至此block design部分已经完成,校验无误之后生产顶层HDL模块用于后续的流程。
3.2. AXI DMA分析
DMA是现在计算机的重要组成部分,它允许不同速度的硬件设备进行沟通。而不需要依赖中央处理器的大量中断负载。否则,中央处理器需要从来源把每一片段的数据复制到寄存器,然后把它们再次写回到新的地方。在这个时间里,中央处理器无法执行其它的任务 [9]。
在45T900中存在两种DMA,一种是集成在PS中的硬核DMA,另一种是PL中使用的软核AXI DMA IP。使用AXI DMA对CPU的占用较少,且可以降低软件的复杂度。AXI DMA与PS的DMA控制器连接是通过AXI_GP接口,这个接口最高支持到32位宽度。AXI DMA IP核在AXI4内存映射和AXI4-Stream IP接口之间提供高带宽直接储存访问。其可选的scatter gather 功能还可以从基于处理器的系统中的中央处理单元(CPU)卸载数据移动任务。初始化、状态和管理寄存器通过AXI4-Lite从接口访问。核心的功能组成如图4所示。

Figure 4. AXI DMA system block diagram
图4. AXI DMA系统框图
在上文所示的Vivado平台中,PS开启了HP0和GP0接口。AXI DMA和AXI4 Stream Data FIFO在PL中实现。处理器通过M_AXI_GP0接口与AXI DMA通信,以设置、启动和监控数据传输。数据传输通过S_AXI_HP0接口。AXI_DMA通过S_AXI_HP0接口从DDR中读取数据后发送给AXI4 Stream Data FIFO,这种情况下AXI4 Stream Data FIFO可以相当于带有Stream接口的高速DA。
前文中有提到过,PL可以看作PS的一个外设,PS必须知道PL中各个IP的地址才能对其进行控制,所以在搭建完工程之后需要注意一下PL中的地址分配,后面用于Linux系统的定制,如图5所示
在确认了工程没有问题之后,进行分析、综合与设计实现,最后产生本次试验要用的BIT文件,也就是后面动态加载的对象。
4. PS部分软件设计
4.1. Linux系统的制作
Linux系统具有一切皆文件的特性,这方便了制作我们需要的系统。赛灵思公司推出的Petalinux工具,包括了u-boot、Linux Kernel、device-tree、rootfs等源码和库,可以让我们很方便的生成、配置、编译及自定义Linux系统。本次试验就在Petalinux的基础上进行系统的制作。
Linux系统是通过设备树对设备进行管理,设备树的引入极大地方便了基于Linux的设备驱动开发,设备树描述了各个总线以及挂载在总线上的设备的详细信息。针对不同的目标,要定制不同的设备树相匹配。上文中设计的AXI DMA模块对于Linux系统来说也可以看作一个设备,结合上文中对PL中各个模块的地址分配,我们对设备树文件进行修改,如图6所示。

Figure 6. Device tree node for AXI DMA
图6. AXI DMA的设备树节点
该设备树节点描述了AXI DMA的时钟信号来源、中断号、属性名,其中REG表示该设备的地址范围信息,是设备树中非常重要的一个属性,而REG与上文中PL的地址分配相对应,也就是说设备的创建从硬件上来看是在Vivado中完成的,设备树的修改是从软件层面上添加设备。这也说明ApSoc可以任意根据我们的需要添加设备模块,体现了其灵活性。通过这些对AXI DMA的描述,驱动可以与设备相匹配并工作。
PL端也有RAM资源,可以利用这些资源作为BIT加载的载体,为此再增添一个名为devcfg的设备树节点,如图7所示,该节点描述了PL的RAM地址空间,通过读写该设备节点,可实现对PL端RAM资源的利用,为后续的动态加载BIT做准备 [10]。
本次试验使用赛灵思官方提供的AXI DMA驱动,将代码库移植到专门用于存放驱动代码的drivers目录,并且修改其上层的Kconfig和Makefiile文件。Kconfig用于使新添加的驱动可以通过图形界面显示,便于我们进行控制。Makefile文件用于执行编译,可以将驱动代码加入到内核当中。执行编译命令,得到本次试验所需的Linux镜像文件,并通过tftp网络传送到APSoc的内存中,使用uboot的bootm命令启动Linux。

Figure 7. Device tree node for DEVCFG
图7. DEVCFG的设备树节点
4.2. 动态加载程序设计
在前面准备的基础上,现在可以正式设计动态加载程序。图8为本程序的设计流程图。
函数的参数为BIT的文件名。进入程序后,首先判断PL部分是否已经加载BIT,如果已经加载,通过操作PS中的寄存器将PL复位并释放,从而清空已经加载的BIT文件。之后卸载AXI DMA驱动并关闭控制它的AXI GP接口,防止动态加载时AXI GP接口发出指令干扰加载。打开devcfg设备与BIT文件,保存为文件描述符,使用Linux中的lseek函数计算BIT文件大小,然后申请并清空大小与之相等的内存空间,之后使用write函数将BIT文件写入到devcfg设备中,完成BIT的动态加载,最后注册DMA的驱动,打开AXI GP接口,完成程序的任务。其伪代码如下。
Int ZynqPlLoad(const char * BIT)
{
*(volatileunsigned int *)(PS_USR_CMD_PL_RESET)=1;
usleep(1000);
*(volatileunsigned int *)(PS_USR_CMD_PL_RESET)=0;
ZynqLibDestory();
fd1=open(“/dev/devcfg”);
fd=open(pcPlBit);
fileSize = lseek(fd,0,SEEK_END);
pData = malloc(fileSize);
memset(pData,0,fileSize);
slRet=read(fd,pData,fileSize);
write(fd1,pData,fileSize);
ZynqLibInit();
Return 0;
}
代码中的usleep、open、lseek、malloc、write和read函数都是linux内核提供的系统函数,由此可见使用linux去进行动态加载方便了程序的实现。运用linux中一切皆文件的思想,将BIT与devcfg设备看成文件去读写,将复杂的加载过程抽象成具体的文件操作,其本质就是将BIT文件写入PL端的内存资源。
5. 加载试验
将动态加载的程序编译为可执行文件,命名为pl_load。启动linux系统,通过tftp网络将BIT文件与pl_load文件传输到系统中,执行动态加载,结果如图9所示。
分析输出结果,可以看出AXI DMA的驱动首先被卸载,之后按照程序顺序将BIT文件写入了devcfg当中,最后AXI DMA的驱动加载成功,并找到了发送与传输的通道。可以判断程序已经执行成功。但BIT是否已经加载成功,需要对其包含的AXI DMA设备进行数据传输测试,如果测试成功,则表示系统中已经存在AXI DMA设备,说明BIT加载成功,为此将专用于AXI DMA测试的官方程序传入系统中,进行测试,结果如图10所示。
由测试结果可知,系统找到了AXI DMA的发送与接收通道,并顺利完成了传输测试,AXI DMA设备功能正常,说明系统中已经存在AXI DMA设备,本次动态加载试验成功。
6. 结论与分析
本文在国产ApSoc平台上,应用linux系统研究动态加载BIT的方法,设计程序并测试成功,证明了动态加载的可行性与便捷性,同时也发现了linux与ApSoc相结合可以方便开发,扩展ApSoc的功能,具有良好的前景。