第一次使用Markdown编写博客,之前都是直接用word或者onenote写好之后复制到博客上,发现文字编排效果很差,不忍翻阅下去。所以转投markdown怀抱。
这里将会新开一个章节,专门更新关于Zynq的一些心得,我希望能够完成以下几个方面:
因此,我希望能够通过Zynq片上强大的FPGA资源和ARM资源,来完成FPGA工程师和ARM工程师的协同工作,一般来说FPGA部分来完成所有高速接口驱动以及一些高速算法(并行独立或者串行复用),然后ARM部分来完成通信协议的实现,不管是私有的(如用户自定义的串口协议的封包和解包)或者标准的(如TCP/IP或者USB等),以及FPGA的流程控制,错误状态控制还有远程更新控制等。
为了完成上述化学反应,一个很重要的方面就是如何协调ARM和FPGA(都是Zynq片上的资源),这个其实很多开发板的学习手册都已经给出了答案,那就是应用AXI4总线。那剩下的问题就是,如何实现Multiboot以及fallback。
因为在S6或者其他7系列的FPGA中,是有一套非常成熟的FPGA加载机制(Xilinx有很详细的指导手册),但是来到Zynq时代,这个方式变了。为什么呢? 因为现在zynq上有ARM了,所有的加载工作实际上可以借由ARM来实现,这无疑也给用户带来了灵活的操作空间,即用户可以自己整一套属于自己的,满足要求的加载方式,这也是本文研究的重点:解析Zynq的加载方式。
这里将通过对比Zynq的TRM和FSBL源码,来一步一步解析Zynq的加载流程,如下所示
运行流程简单的说就是:
永远问自己,我们的芯片(在这里是Zynq)在POR复位或者non-POR 复位后,ARM是怎么样一步一步最终来到用户的main.c,对FPGA工程师而言,就是FPGA是怎么完成初始化任务的。
这里我们不妨写的详细一点,将所有的技术细节都呈现出来。这就需要我们打开Zynq的TRM:
可以看到,在non-POR或者POR以后,Zynq会完成:
通过上面的流程描述,我们可以获得一个表观的理解,原来Zynq加载蛮复杂的。因为Zynq里面包含了PS(APU,即两个ARM Cortex-A9 cpu)和PL(根据系列的不同,可能是A7系列,也可能是K7系列)两个部分。
这两个部分的加载,会根据用户选择的不同而不同,如下所示:
从上面可以知道,PS的加载和PL的加载并不是完全独立的,如下图所示
Note 1: if PL was power-up(needed for JTAG or secure mode), BootROM will initial PL then wait until PL complete initialization
如上流程,就是整个PS和PL加载的过程,基本上我们只需要保证合适的文件被正确的烧写到FLASH中,那么整个加载就会正确的跑下去,这个所谓的合适的文件包括:
整个BootROM是写死在Zynq的片上ROM中,其中最重要,同时也会影响后面FSBL执行的,就是BootROM Header的searching 和 loading。
类似于A7或者S6系列的multiboot过程需要一个header文件,它用于实现:
在Zynq系列中,这个Header的功能被进一步的扩大,下面我们来看一下这个Header的定义吧:
接下来逐条来解释其作用,同时留下一点伏笔,因为这些最终都会被FSBL所引用。
Interrupt Table for Execution-in-Place — 0x000 to 0x01C
这些数据用于XIP,这里不讨论
Width Detection — 0x020
用于检测Qspi的位宽到底是X1,X2,还是X4,如果是X8,还需要下面那个 Image Identification 寄存器
Image Identification — 0x024
固定为0x584C4E58,‘XLNX’,还可用于X8检测
Encryption Status — 0x028
配置FSBL/User code到底是加密还是不加密
FSBL/User Defined — 0x02C
用于保存Header的版本,应该也是xilinx自动生成的
Source Offset — 0x030
用于保存FSLB/User code image被保存的offset地址,这个Offset是相对于Header的起始位置而言的,这个在FSBL中会有体现,这留个记号
Length of Image — 0x034
用于保存被加载的FSLB/User code image的大小,<=192KB。 该数据=0时意味着不需要copy,是XIP。
FSB Load Address— 0x038
FSLB/User code image copy的目标地址,因为该image是存在FLASH中的,需要被复制到其他OCM中去。
Start of Execution — 0x03C
copy完以后,cpu需要从哪里开始执行第一条代码,<= 0x30000,也即是192KB。这个很重要,会在介绍FSBL源码的时候重新在验证并确认这个功能。 然而这样地址,或者长度之类的,都是通过配置Xilinx提供的工具自动打包生成的。
Total Image Length — 0x040
load进OCM的总长度,这个长度会大于等于Length of Image — 0x034,因为在加密模式下,还会包含HMAC头文件之类的。
QSPI Config Word — 0x044
固定为0x01
Header Checksum — 0x048
0x020 to 0x044的checksum,用于验证Header是否完整,FSBL会应用到
FSBL/User Defined— 0x04C to 0x097
自定义数据
???Boot Header Table Offset??? — 0x098
TRM中这个数据的命名好像有问题
QSPI Config Word — 0x09C
指向Image Header Table,这个Table里面会记录整个FLASH里面除了FSBL以外,还有几个image,包括Bitstream,elf等等。每一个image会有一个Header(不是这里的BootROM Header,而是专门的Header,FSBL里面在介绍),所有的Header组成一个Table。
Register Initialization Parameters — 0x0A0 to 0x89C
利用Add+Data的方式,直接对某个地址进行数据写操作,应该非常高效,估计也是xilinx工具自动生成的。
FSBL/User Defined — 0x8A0 - 0x8BF
FSBL Image or User Code Start Address — 0x8C0
FSBL Image or User Code 实际可以放置的起始地址,也就是上面的Source Offset — 0x030 >= 0x8C0
介绍了这么多,估计一时也不好消化,没有关系,上面黄色标记了的数据,会在FSBL中隆重的重新介绍。
回到老话题,BootROM会利用上述BootROM Header把FSBL拷贝到OCM中,然后让FSBL接管CPU开始run。而实际上FSBL也会应用上面的BootROM Header文件去寻找下一步所需的image,包括BitStream等,这个以后再说。
那么BootROM 是如何找到BootROM Header的呢?
如上说是的Searching方法:
因此,BootROM Header 必须放置在32KB的整数倍位置,这个应该是Xilinx的工具自动完成的
这个检查checksum的操作也同样的在FSBL中被执行,以后在介绍
介绍了这么多,总结一下:
后面的章节中将会重点介绍FSBL是如何工作的,毕竟BootROM我们用户无法修改和参与,明白原理和机制即可。
下次再见!