1、概述
ZYNQ 的详细介绍参考 XILINX 官方文档 UG585,在了解了 ZYNQ 的基本架构组成和丰富的资源后,下一步便是分析他的启动流程,以便更好的认识 ZYNQ 并开始开发工作;
关于 ZYNQ 启动方面的描述,这里我觉得涉及到几个大的方面:
1、系统上电启动执行过程;
2、系统复位相关流程;
3、系统时钟树;
上面 3 点在 UG585 Datasheet 中都分别是一个大的章节来介绍,这里先介绍系统上电执行过程的部分,也就是 UG585 的
Chapter 6 . Boot and Configuration
另外,启动部分,又分为两种类型,安全启动和非安全启动,这里仅对非安全启动进行介绍,更多的安全启动部分的详细内容,参考官方 UG585 DS;
2、简介
上电的时刻,PS_POR_B 信号会被置位,此刻 ZYNQ 硬件立马去采样一组称作 Boot Strap Pins 的管脚,这组管脚告诉硬件以什么方式启动(QSPI Flash、NAND Flash、NOR Flash、SD Card),以及是否开启 PLL 等(每个管脚的高低电平来表征0/1信号,稍后详细介绍);通常的做法是板端上,通过跳冒来选择启动方式,也就是原理图上通过接地或者 Vcc 来确定一组上电采样管脚,选择好了后,再上电;
PS 端首先执行 BootROM 上的启动代码,这段代码是直接在 On Chip ROM 中 XILINX 做进去的,无法看到而且也无法修改,所以 BootROM 是上电后,APU 执行的第一段代码就是 BootROM 的这部分,我的 ZYNQ-7000 是双核 Cortex-A9,两个 CPU 核心,BootROM 由 CPU_0 来执行,此刻 CPU_1 处于 WFE 等待状态;
BootROM 的主要任务是配置系统,将后续的 FSBL(First Stage Boot Loader)/ User Code 加载到 OCM 中运行,然后跳转到 FSBL ,将控制权移交,并完成它的使命;(当然,在非安全的模式下,也支持片上 XIP 运行,也就是 QSPI Flash 或者 NOR Flash 上直接运行代码);
FSBL 是咱们代码编译生成的,编译完成后生成 bin 文件,并将这个 bin 放置到我们的启动设备中(QSPI Flash 或者 NAND/NOR Flash 或者 SD Card 中);
所以呢,这里咱们便可以知道 BootROM 中,至少含有 QSPI/NAND/NOR Flash 相关的控制器驱动程序和器件的标准访问指令,以及 SDIO 的标准驱动(MMC)和 Mirco SD Card 标准的访问指令;
既然这个 FSBL 是放置在启动设备中,那么在 BootROM 采集完 Boot Strap Pins 后呢,一定就去初始化对应的外设控制器,然后去启动设备上去找咱们的 FSBL,这里 XILINX 定义了一个叫做 BootROM Header 的玩意,BootROM 会去启动设备上不断的寻找这个 BootROM Header,以便完成接下来的工作交接(关于更多 BootROM Header 的东西,下面会介绍,看官稍安勿躁);
OK,找到 BootROM Header 后,BootRom 又干了一些事情,然后控制权交给了 FSBL,这部分代码咱们可以控制,可以在加载 OS 之前,如果携带了 bit stream 的情况下呢,在此刻去配置 PL 的逻辑;
最后,加载 BootLoader,进而跑进 OS,完成启动;
3、Boot Strap Pins
前面说了,上电启动的时候呢,会触发 PS_POR_B 信号,一旦触发这个信号,就会去采集 Boot Strap Pins 几个引脚的电平状态,以便 BootRom 判断启动设备是什么,上电的流程信号如下:
其实系统复位有很多种类型,软件可以触发系统复位,WDT 也可以触发系统复位,有些复位并不触发重新采集 Boot Strap Pins 流程,这里我们讨论的 POR 上电复位流程,是要触发 Boot Strap Pins 的;其他的暂且放在以后专门的复位章节描述;
接着说 Boot Strap Pins,ZYNQ-7000 使用 7 个 pins 来描述 PS_POR_B 信号置位后,需要传递给 BootROM 的信息,如下所示:(这 7 个 pin是 MIO[8:2],每个接 20kΩ 的上拉或者下拉电阻,上拉代表逻辑 1,下拉代表逻辑 0):
上表直接描述了电压等级、PLL 是否开启,以及使用哪种类型的设备进行启动;
4、BootRom 流程
整个 BootRom 的流程如下所示:
1、初始化 PLL
2、初始化 APU
3、ROM CRC check (可选)
4、MIO 初始化
5、如果 PL 上电了的话,初始化 PL(但并没有配置 bit stream)
6、根据 Boot Strap Pins 选择启动方式,并搜索 BootROM Header
7、判断 BootROM Header 中是否有加密的 flag 以断定后期是否走加密流程(这里暂时认为走非加密流程)
8、非加密流程中,平台寄存器初始化
9、判断是否 XIP 模式(片上 Flash 执行,QSPI/NOR Flash 支持)
10、若 XIP,则直接跳转到 User Code,跑 XIP
11、若非 XIP,那么将 FSBL 加载到 OCM 中,跳转到 OCM 中执行 User Code
5、BootROM Header
5.1、Structure
上面已经描述了,BootROM Header 是 BootROM 需要去搜索并根据 BootROM Header 来进行接下来的操作的一个关键结构,它的组成如下所示:
它定义了很多的字段:
Interrupt Table for Execution-in-Place — 0x000 to 0x01C
这些数据用于XIP,这里不讨论
Width Detection — 0x020
只用于 QSPI 启动模式,用于决定 Quad-SPI device (x1, x2, or x4) 的位宽
Image Identification — 0x024
固定为 0x584C4E58,‘XLNX’
Encryption Status — 0x028
配置 FSBL/User code 是否加密
FSBL/User Defined — 0x02C
指明 Header Version,在 UG821 Zynq-7000 SoC Software Developers Guide 中详细描述
Source Offset — 0x030
用于保存 FSLB/User code image 被保存的offset地址,这个Offset是相对于Header的起始位置而言的
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/User Defined— 0x04C to 0x097
自定义数据
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
这部分包含了 256 组的 <地址+数据> 对的定义,当初始化的时候,BootROM 利用 Add+Data 的方式,直接对某个地址寄存器进行配置;
5.2、Search BootROM Header
BootROM Header 放到启动设备后呢,BootROM 代码就要去搜索这个玩意,以便决定后面该怎么执行;有两点 BootRom 需要去寻找的:
1、Image Identifucation —— 为 0x584C4E58,即 'XLNX';
2、CheckSum 正确;
如果满足上述两个条件,那么 BootRom 觉得是找到的正确的 BootROM Header;
寻找 Header 的过程,是首先在起始的地方找,如果没找到,那么跳 32KB 的地方,再次找,以此类推:
这种 32KB 一跳搜索的方式,总有一个尽头吧?UG585 规定根据不同设备,它搜索的地址空间的限制如下:
NAND:First 128MB
NOR:First 32MB
Quad-SPI,signal/dual SS with 4-bit I/O:First 16MB
Quad-SPI,dual SS with 8-bit I/O:First 32MB
SD Card:Signle Image in boot page,no searching
如果搜索到了,那么皆大欢喜,继续往下走,如果没搜索到,BootROM 会产生 lockdown,将系统锁住;
这里值得注意的是,SD Card 启动,不支持走 search 过程,和 multiboot ;SD Card boot 流程是在 SD Card 支持 FAT 16/32 文件系统,BootROM 直接从 SD Card 读取 BOOT.BIN 文件(FSBL 编译生成的文件)并加载到 OCM,更多的细节参考 UG585;
6、Lockdown
在 BootROM 启动流程中(参考前面的启动流程图),出现错误后,系统会被 lockdown ,lock down 的原因会被硬件写入 slcr.REBOOT_STATUS 寄存器的 Error Code:
具体的 BOOTROM_ERROR_CODE 详见 UG585
7、Post BootROM
7.1、State
PS 端完成 BootROM 后,APU 和 OCM 的状态如下:
MMU,ICache,Dcache,L2 Cache 全部 disable
处理器处于 supervisor 状态
ROM Code 处于 inaccessible
OCM memory 可以被访问,从 0x0000_0000 地址开始的192KB 的 ,和从 0xFFFF_0000 开始的 64KB 空间,一共256KB
CPU 0 处于 boot stage 1状态
CPU 1 处于 WFE
7.2、Memory Map
OCM 片上 RAM 一共 256KB 的大小,它分为两部分:
1、0x0000_0000 开始的 192KB
2、0xFFFF_0000 开始的 64KB
64KB 的 memory block 用于 BootROM 放置 BootROM Header 和放置 program variable;当 BootROM 完成后,这个 64KB 的空间就可以被 FSBL 使用了;
8、PL Configuration
8.1、PL Control via PS Software
PS 端软件是通过一个叫 PCAP 桥的东西控制 PL ,主要是通过一个叫 Devc 的模块来操作
这个 PCAP Bridge 的内部结构如下,是通过 AXI 总线进行互联:
8.1.1、PL initialization via PS Software
在任意时刻,devcfg.CTRL[PCFG_PROG_B] 位都可以用于对 PL 发射全局的 Reset 信号,如果设置这个 bit 被设置为低电平,PL 开始它的初始化处理,同时 devcfg.STATUS[PCFG_INIT] 这个 bit 一直为高,直到 devcfg.CTRL[PCFG_PROG_B] 被硬件设置为高,XILINX ZYNQ 初始化 PL 的顺序如下:
设置 PCFG_PROG_B 信号为高
设置 PCFG_PROG_B 信号为低
轮询 PCFG_INIT 信号状态,直到为 0
设置 PCFG_PROG_B 信号为高
轮询 PCFG_INIT 信号状态直到为 1
8.1.2、PL Configuration via PS Software
为了配置 PL,需要先使能 PCAP(AXI Interface)部分,清除终端,初始化 PL(8.1.1),关闭内部 DevC 的 loopback 功能;然后通过 DevC 的 DMA 单元进行对 PL 配置;当然,PS 和 PL 都需要 Power on;
8.1.2.1、PS Non-Secure Bring Up(no PL Power)
ZYNQ 可以支持只启动 PS 端,PL 端不上电
可以看到 PS Power-On 后,PS_POR_B 信号被拉高,如果使用 PLL 的话,需要等待 PLL Lock 信号,PLL Lock 后 BootROM 便开始运行,执行完后,便开始 FSBL;
8.1.2.2、PS Bring Up With PL Configuration
PS 也可以携带 PL 的 bit stream 来启动,在启动过程中配置 PL;可以支持加密或者非加密模式:
8.1.2.3、PL Bring Up By FSBL/User Code
当然,配置 PL 呢,不一定是要在启动的阶段,你在任意系统运行阶段来搞事情:
8.1.3、Configure PL via PCAP Bridge
上面说了配置 PL 的各个时间段,那么现在来说一下通过 PCAP 桥来配置 PL 的过程:
1、使能 PCAP Bridge 并且选择 PCAP 来进行配置:对 devcfg.CTRL[PCAP_MODE] 和 [PCAP_PR] bit 写 1;
2、清除中断:devcfg.INT_STS 寄存器写全 1;
3、初始化 PL:
设置 PCFG_PROG_B 信号为 1
设置 PCFG_PROG_B 信号为 0
轮询 PCFG_INIT 信号状态,直到为 0
设置 PCFG_PROG_B 信号为 1
清除完成中断:devcfg.INT_STS[PCFG_DONE_INT] 写 1
4、为了保证 PL Ready,需要轮询 PCFG_INIT 信号状态直到为 1
5、确保 devcfg.STATUS[DMA_CMD_Q_F] 为 0
6、关闭 PCAP 的 loopback:写 0 给 devcfg.MCTRL[INT_PCAP_LPBK] bit;
7、配置 PCAP_2x 时钟分频:
Secure Mode:对 devcfg.CTRL[QUARTER_PCAP_RATE_EN] bit 写 1
Non-Secure Mode:清除 devcfg.CTRL[QUARTER_PCAP_RATE_EN] bit 为 0
8、启动针对 bit stream 的 DMA 传输:
配置 Source Address 为 PL 的 bitstream 的存放地址;
目的地址配置为 0xFFFF_FFFF;
Source 传输数据的长度为 32-bit word 为单位的 PL bitstream 的大小;
Destination 目的地址长度为 32-bit word 为单位的 PL bitstream 的大小;
9、等待 DMA 传输完成:也就是等待 devcfg.INT_STS[DMA_DONE_INT] bit 为 1;
10、检查错误码;检查 devcfg.INT_STS 中的错误码,看看在配置过程中是否发生了错误;
11、轮询 [PCFG_DONE_INT] bit 为 1,确认配置完成;
下图为 Secure 和 Non-Secure 的 PL bitstream 配置流程:
9、Summary
简单的来说,上电复位 POR 后,硬件去采集启动管脚,并将其锁存到寄存器中,BootROM 读取启动方式后,进行对应的启动流程,包括等待 PLL Lock,寻找 BootROM Header,加载 FSBL,配置 PL 等;简易流程如下所示: