在 Xilinx Zynq 器件中,硬件可编程逻辑 PL 是作为一项外设挂载在 ARM 处理器系统中的,那么 PL 硬件的配置自然也就由处理器负责。
本文参考了下方这篇博文,在这个资料比较匮乏的领域,这篇文章给了我很大帮助。 作者很厉害,发现了 UG585 中的错误,这个错误在之后被修正。
https://blog.csdn.net/vinnie\_\_/article/details/52829874
硬件配置
硬件的配置通过加载比特流 bitstream 完成,即我们通常说的“下比特”,bitstream 有以下几种加载情况:
- 调试阶段,来自上位机软件,比如通过 Vivado 或者 SDK 下载
- 固定启动,比特流被事先固化在芯片外围的存储介质中,比如 FLASH 或者 SD 卡中,上电后比特流被引导加载到 PL
- 动态配置,在当前硬件比特运行期间,有一份新的比特流通过网络,串口等介质传输进来,存储在DDR 或者 FLASH 等外部存储介质中,随后将硬件动态配置为新的比特流
在第一种情况中,PL 由 Jtag 下载器配置,与处理器几无关系。
其他两种情况,则都由软件负责。从下图可以看出,固化启动和动态配置的数据通路类似。只是固化启动一定是从 NAND,NOR,QSPI 等固化介质启动。而动态配置的数据一般来源于图片上方的 DRAM。
secure 安全启动是指比特流会经过 AES 等安全模块的验证后,再配置到硬件中。
具体来说,比特流从存储加载到 PS 种,之后前往硬件 PCAP 的道路是由处理器系统 PS 中的 PCAP bridge 负责。这座桥在 PS 的 DevCfg 外设中。DevCfg 外设有完善的例程和文档,本文后续的实验就围绕例程展开。
PCAP 全称是Processor Configuration Access Port, 即处理器配置接口,可以理解 PCAP 代表着硬件,那么这座桥的另一边 PS AXI 接口即代表软件。是为一座沟通软件和硬件的桥梁。
比特流的搬运是通过 PCAP 桥模块内的一个专用 DMA 实现的,这个 DMA 的特性和使用方式同 Zynq 其他 DMA 没有区别。只是专用于比特流搬运,提高比特的移动速度,以实现更快的部署。要知道大型期间的比特流可以达到近 20MB 之巨。
使用 SDK 代码实现动态重配置
在 Xilinx 的概念中有两种比特动态重配置概念,一种叫部分重配置区别于普通的重配置。部分重配置并不会重新加载一个完整的比特流,而是重新配置部分逻辑,保持其他逻辑不变(静态逻辑),以提高重配置的速度,最快能达到数毫秒之快。
部分重配置会稍显复杂,本文我们讨论完整的动态重配置,这也是更具实际意义的功能。
比如实验室的一台网络测试仪,可以在不关机的情况,通过下载不同的比特分为配置为2-3层和4-7层网络测试仪。
软件动态重配置在 Linux 下可以通过 DevCfg 驱动进行,在没有操作系统的情况下,我们也可以在 SDK 中通过用户代码进行,这里我们以 DevCfg 轮询例程为例。
例程中同时支持完整重配置以及部分重配置,程序检测当前 PL 部分的工作情况,在已部署有比特流的情况下进行部分重配置。
这里我们进行完整的重配置,上电后不加载硬件程序,而只下载软件,如下图,不勾选 program FPGA。
准备比特流
既然要动态配置比特流,那首先要准备一份比特流。
在 vivado 工程 generate bitstream 之后,在 tcl console 中运行命令
write\_cfgmem -format bin -loadbit "up 0x0 比特文件名.bit" -file 生成bin文件名.bin -size 128 -force -interface SMAPx32 -disablebitswap
write\_cfgmem 命令将比特流生成为 bin 文件,bin 文件才能用于配置。其他的字段决定 bin 文件的格式,不同的格式可能会无法正确配置硬件。
比特流需要提前放置到板子的 DDR 上,这里可以通过 SDK 里的内存 Dump&restore 工具实现。
选择一个 DDR 地址,填入 bin 文件的 byte 长度即可。如果你选择困难症的话,就用0x30000000 这个地址好了。
接下来需要根据你的 bin 文件,对 example 开头中的参数做修改,原本的参数只是装装样子的 (Dummy)。
修改为真正的存储地址和 bin 文件长度。
这里注意两点,一是长度的单位为 4 字节(32bit)。
其次你可能奇怪,为什么我们存放在 0x30000000 ,这里却填 0x30000001 。
这是因为最低 2bit == 2'b01 用来表示此时为最后一次 DMA 传输(因为我们就传一次哈)。至于为什么最低 2 bit 能借用,这和前一点有关哦。
首先复位 PL
我们的程序运行时,此时 PL 部分还没有比特流,我们下载软件时就会提醒我们,此时硬件还未就绪(Done 信号未置起),恩,没比特就对了\~
在配置硬件之前,首先需要初始化(复位一次)硬件,文档中这么描述软件初始化硬件。
这 5 步都是对寄存器的读写操作,翻译成实际代码即为
//lf set prog
u32 CtrlReg;
CtrlReg = XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET);
XDcfg_WriteReg(DcfgInstPtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET, (CtrlReg |
XDCFG_CTRL_PCFG_PROG_B_MASK));
//@lf reset prog
CtrlReg = XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET);
XDcfg_WriteReg(DcfgInstPtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET, (CtrlReg &
(~XDCFG_CTRL_PCFG_PROG_B_MASK)));
//@lf wait for reset
while((XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr, XDCFG_STATUS_OFFSET)
& XDCFG_STATUS_PCFG_INIT_MASK) == 1);
//@lf set prog
CtrlReg = XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET);
XDcfg_WriteReg(DcfgInstPtr->Config.BaseAddr,
XDCFG_CTRL_OFFSET, (CtrlReg |
XDCFG_CTRL_PCFG_PROG_B_MASK));
//@lf wait for set
while((XDcfg_ReadReg(DcfgInstPtr->Config.BaseAddr, XDCFG_STATUS_OFFSET)
& XDCFG_STATUS_PCFG_INIT_MASK) == 0);
运行 example 程序,顺利配置硬件的话,你开发板上的 Done 灯就会亮起。
如果不幸卡在这里,DMA传输未完成,请检查硬件复位(我之前也卡在这里。。)
卡在这里,等待配合完成,检查 bin 文件是否正确,以及正确地放置。
动态重配置
注意 我们要动态重配置时,我们就只能使用上述的代码将 PL 部分复位。通过这种方式清除原有的比特流之后再配置新的比特流。据其他文章说,不然将无法完成重配置。
采用手动的方式放入 bin 不是我们动态重配置的长久之计,我们需要借助以太网,串口等接口来实现比特流的传输,将新的比特放到 DDR 之后就可以按照本文的方式,复位 PL 然后重配置。我们会在后续的文章中讨论这一点。
重要参考
UG585 中的 6.4
https://www.xilinx.com/support/documentation/user\_guides/ug585-Zynq-7000-TRM.pdf
结语
本文稍稍关注了 Zynq 中 软件配置 PL 的过程,并通过例程实现比特流的重配置,后续的文章将继续关注这方面的问题,比如比特的传输以及动态部分重配置等。
推荐阅读
关注此系列,请关注专栏FPGA的逻辑