Zynq平台AMP运行模式下的软件切换及多版本固化运行
1. Zynq双裸核AMP运行环境构建 1
1.1 Zynq架构概要 1
1.2 Zynq启动与配置 2
1.2.1 启动流程概述 3
1.2.2 PS硬件启动阶段 3
1.2.3 PS软件启动阶段 4
1.2.4 启动文件构成 4
1.2.5 启动模式 4
1.2.6 BootROM的执行 5
1.2.7 FSBL或用户代码执行 5
1.2.8 PL启动处理 6
1.2.9 PL配置路径 6
1.2.10 Device Configuration Interface 7
1.2.11 CPU1启动方式 7
1.3 官方FSBL流程 8
1.3.1 FSBL函数说明 8
1.3.2 FSBL流程分析 8
1.4 Zynq双核运行实现 10
1.4.1 AMP基础环境构建 10
1.4.2 代码设计及双核启动 11
2. Zynq复位子系统 14
2.1 Zynq复位系统概述 14
2.2 Zynq软件复位实现多位置启动 16
2.3 Zynq外设复位实现在线程序更新 17
2.3.1 APU复位及重启 17
2.3.2 DDR程序写入 19
同构多核架构 和 异构多核架构
同构多核处理器是指系统中的处理器在结构上是相同的.异构处理器是指系统中的处理器在结构上是不同的,处理器可以是通用处理器也可以是特定专用硬核
Zynq SoC融合两种架构, 两个Crotex-A9的同构多核,
在此基础上存在可编程逻辑单元(PL)组成异构多核系统.
图 1-1
Zynq-7000 SoC 概览
Application Processor Unit (应用处理单元)中:
FPU & NEON Engine : 浮点预算单元 & 矢量加速器;
MMU : 内存管理单元;
I-Cache & D-Cache : 指令一级缓存 & 数据一级缓存 (各核独立各32KB);
L2Cache : 二级缓存 (512KB);
OCM : 静态存储器 (区别DDR动态存储器) 片内高速存储器;
PS对其访问时间延迟固定且很小,但大于Cache,作为片内唯一高速存储器可以用作处理器的加速算法;
OCM空间大小256KB,分192KB +
64KB两部分,其中192KB部分存在寻址空间底部,编址从0开始,即0x0000_0000 ~
0x0002_FFFF, 64KB放在最高的寻址空间编址为0xFFFF_0000 ~ 0xFFFF_FFFF;
192KB的空间一般用来运行FSBL,由BOOTROM拷贝FSBL至OCM,同时FSBL可替换为用户程序,这也就是Zynq的无DDR启动的实现方式,可实现低功耗;
64KB的空间一般作为系统特殊编址,例如0xFFFF_FFF0是作为存储CPU1的程序启动地址来使用;
System level control regs:
系统级别控制器,功能多样,多用于外设的状态控制模式控制,在本例程中将使用CPU复位寄存器等功能;
Memery interface:
DDR接口,在本例程中DDR除了作为运行空间外,也可在当双核需要较大的数据交换是作为共享内存使用,即共享内存并不局限与OCM.
总来讲,Zynq的启动主要分为3个阶段:
图 1-2 Zynq启动三阶段
图 1-3 PS\PL Boot Process for handware anf Software
BootRom and Header Parameters
BootRom header包含引导BootRom执行流的一系列参数.
注:头部包含定义BootRom装载进OCM的FSBL用户代码镜像长度参数,代码长度必须限制在192KB之间,因为OCM
= 256KB = 192KB + 64KB.
PL Initialization and Configuration
阐述PL启动和PS之间的关系,
PL在配置bit流和初始化之前必须上电,PL上电和初启阶段独立于PS运行,但是PL上电需要维持同PS上电复位信号之间的一个定时关系;
PL受控于FSBL/USER使用GPIO或者连续的外部接口驱动,
BootRom和FSBL\User代码可以决定PL的上电状态,FSBL\User代码在PL状态改变时可以接收一个中断;
PL Boot 包含4个阶段: 启动,初始化,配置,使能.
Ps硬件启动阶段包括电源上升,时钟,复位,引脚约束采样以及PLL(锁相环)
External PS Control Pins
这一步是对启动模式的采样,决定PS以哪种方式进行启动,上电复位信号产生等等,
PS PLL Initialization
6号MIO使能PLL.
使能PLL后,BoorRom执行流需要延迟至PLL输出时钟.
如未使能PLL,PS_CLK参考时钟输入引脚绕过PLL.时钟子系统工作在PS_CLK的输入频率.
PS软件启动受控于BootRom之后是FSBL\User代码. BootROM代码受启动约束引脚, BootROM
头部,以及系统内BootROMdiamante检测影响.
Stage 0(BootROM:BootROM Header)
BootROM在上电复位\系统级复位(PS_SRST_B,debug,看门狗,软件)之后由主CPU(CPU0)执行.
BootROM从固化装置读取BootROM头部程序去决定boot流和跳转stage1;
在硬件引导之后, 两个CPU开始从本地地址0x0(BootROM的初始位置)
执行相同的BootROM代码来确认自己的身份;
注意: 单核设备包含一个处理器.
双核设备包含两个,CPU1执行到WFE命令时进入WFE模式等待触发启动,
CPU0继续执行BootROM
Stage 1(FSBL \ User Code)
参考[ug821](file:///F:\资料\zynq\技术手册\官方文档\ug821-zynq-7000-swdev.pdf),这个阶段很灵活.
可以使用官方的fsbl工程,也可以是自己的用户代码;
Stage 2(U-Boot \ System \ Application)
系统软件,也可称之为SsBL,这一阶段同样受控于用户,具体设计参考[ug821](file:///F:\资料\zynq\技术手册\官方文档\ug821-zynq-7000-swdev.pdf),
Boot Device可以存储多个组件和多个版本的组件:
BootROM header(BootROM需要)
FSBL\User 代码 elf 文件 (BootROM需要)
PL bitstraem(BootROM不访问)
System / Application elf 文件(BootROM不访问)
启动模式包括四种主启动模式和两种JTAG从启动模式
Flash设备 (主启动模式)
当从flash存储器启动一个系统boots,这种方式成为主模式启动.主要有以下几种方式:
Quad-SPI (就地执行模式)
SD 卡
NAND flash
NOR flash(可选的就地执行模式)
JTAG(从启动模式)
JTAG模式被称为从模式启动,同时TJAG模式是非安全的引导模式.
JTAG链可以级联配置,也可以单独配置.
顺序启动期间,根据MIO[2]启动引脚的设置来设置链,正常情况下系统配置为级联模式.
当TRM指向JTAG启动模式时,无特殊说明的情况下都是级联模式,同时JTAG接口在非安全启动模式中都是使能的.
级联 JTAG链 (最受欢迎)
独立 JTAG链(通用):
通过PL JTAG访问TAP控制器
在配置PL完成后通过EMIO JTAG经由 SelectIO 引脚访问DAP控制器
独立 JTAG链(很少用)
通过PL JTAG访问TAP控制器
通过MIO PJTAG访问DAP控制器
BootROM在POR或者non-POR复位之后很快开始执行,
一个POR复位会触发硬件启动阶段然后BootROM会开始执行,
一个non-reset会跳过硬件启动阶段立即执行BootROM.
BootROM执行片上内存代码用来执行系统引导过程.当执行转移FSBL\User代码完毕之前BooROM禁止一切接入ROM代码.
初始BootROM执行时,会启动APU并进行一些自检.它会读取Boot启动模式信息,如果非JTAG启动,BootROM会配置相应的启动模式控制器.之后进一步读取BootROM头部配置系统以满足所需的启动过程.
除了BootRom 头部 之外,boot device 还提供FSBL和(or)
用户代码用来在BootROM执行完毕之后接管系统控制.
Secure Boot
启动运行BootROM
Header的配置决定BootROM运行在安全模式或者非安全模式.在安全模式下,FSBL/
User代码会从flash搬运,解密并写入OCM,然后CPU从OCM执行代码.
如果系统是从安全模式下启动而随后BootROM
Header的复位信号不是来自POR复位这就表明这是非安全启动,整个系统会进行安全锁止并抛出0x201A错误码.
BootROM Header Search
当BootORM没有探测到一个有效的BootROM
Header,此时BootROM会执行一个搜索函数去寻找另一个BootROM
Header,BootROM头搜索支持 Q-SPI,NAND和NOR引导模式.
BootROM Header从不加密,搜索函数同加密的(不加密)FSBL \ User
代码镜像一起工作.
BootROM Execution Influencers
影响BootROm的主要有以下几个情况:
引脚约束
复位引脚
有效的BootROM(头寻找的和校验)
BootROM Header 启动模式以及锁止码原因的冲突.
Error Detction,Device Lockdown and Erroe Codes
当BootROM
Header执行过程中,BootROM探测到一个错误,就会对系统进行锁止并生成一个错误码,
通常有两种锁止类型:
安全锁止(不能访问设备, 需要一个POR重启整个系统)
非安全锁止 (JTAG可能是使能的,并且任何系统复位都能使BootROM继续运行
当发生系统锁止,错误码会被写进 SCLR.REBOOT_STATUS 寄存器
用户代码可以根据需求重配置PS或者随意配置PL.在就地执行选项未使能的情况下BootROM默认会将FSBL\User
代码装载进OCM.
Fsbl \ User代码执行如下:
使用Vivado 工具生成的PS7 初始化数据 初始化PS (MIO,DDR, etc.)
使用bit流文件配置PL
装载第二阶段启动bootloader 或者在DDR裸跑应用程序
第二阶段启动bootloader 或 裸跑应用程序获取系统控制权
FSBL Image FallBack and Multiboot
如果FSBL检测到一个错误或者想使用一个不同的FSBL镜像,可以devcfg.MULTIBOOT_ADDR
[MULTIBOOT_ADDR] 域写
boot域地址并执行一次软件系统复位.可以参考[ug821](file:///F:\资料\zynq\技术手册\官方文档\ug821-zynq-7000-swdev.pdf).
PL引导进程包括 启动,初始化, 配置, 使能
启动(对PL进行上电)
初始化(使用 PS软件 或者 INIT\PROGRAM 控制引脚)
配置 (途径有PS PACP, JTAG, PL ICAP)
使能PS-PL接口 (使用PS软件)
图 1-4 PL Configuratin paths
PL在安全或非安全模式下可以通过PS软件来实现配置或者重配置.
PCAP方式是最常用的部署方式,因为它不需要使用bit文件对PL进行预编程.
PL同样可以由JTAG链上的TAP控制器配置为非安全模式.
在PL配置模块中数据路径的多路复用使用devc.CTRL [PCAP_MODE] and [PCAP_PR]
比特位完成.
使用TAP控制器(开发常用)
非安全模式
通过TAP控制器初始化和配置PL
PS PCAP路径 (部署常用)
安全模式或者非安全模式
由device configuration interface (Devc) 初始化和配置PL
ICAP 路径 (不常用)
安全或非安全模式
仅通过PL中实例化的逻辑进行重新配置
此模块简称 DEVC
包括3个逻辑初始化模块,在PS软件模式下配置PL控制器
(PCAP路径),管理设备安全性,访问XADC.同时为这3个主要功能模块提供一系列控制\状态寄存器.
PS软件使用带有DMA的PCAP桥来配置PL和解密镜像,它提供了PS软件到PL配置模块的路径:
解密一个安全的FSBL\User代码
处理一个安全或者非安全的 PL比特流; 同时下载或者上传
根据需要处理PL比特流压缩命令
安全管理模块监控系统活动,维护安全的运行环境
基本的设备安全管理
加强系统级安全性,包括调试控制
XADC接口为PS软件访问模数转换器提供途径
串行接口(serial interface)
报警或过热中断
WFE状态 (wait for event)是CPU1的一个重要状态,AMP模式下.CPU
0的职责在于在CPU1上启动任务.在CPU 0引导启动过程中,BootRom 会将CPU1置为WFE模式,
此时CPU1没有使能任何进程,只对少数几个通用寄存器进行了修改.
CPU0需要少量协议才能在CPU1上启动应用程序,当CPU1接收系统事件,会立即读0xFFFFFFF0并跳转至其内容中的地址以运行程序.如果系统触发事件在更新目的地址之前发出则CPU1会继续保持在WFE模式,因为0xFFFFFFF0有WFE指定的地址作为安全网络.如果写入0xFFFFFFF0的地址为无效的或者未初始化地址,这将是未定义行为.
只有ARM-32 ISA代码支持CPU1内的初始化跳转,在跳转目标地址不支持Thumb 和Thumb
-Ⅱ代码.这就意味着目标地址必须是32bit位并且必须是一个有效的ARM-32指令.否则为未定义行为.
CPU0 在 CPU1 上启动应用程序的步骤如下:
向0xFFFFFFF0写入应用启动程序地址
执行SEV命令已触发启动CPU1并且跳转至应用程序.
0xFFFFFF00 ~
0xFFFFFFF0地址范围是保留的,在第一阶段启动和以上应用程序未完全执行前不能使用,在CPU1成功启动之前访问这个区域的任何行为都可能导致未定义行为
表 11 Xilinx官方FSBL函数
函数 | 功能 | 备注 |
---|---|---|
ps7_init() | 初始化必要的通用寄存器以及外设\时钟等 | (PCW initialization for MIO,PLL,CLK and DDR) |
SlcrUnlock(); | Unlock SLCR for SLCR register write | (system level control register) |
Xil_DCacheFlush(); | 把cache里的数据压入相应存储器 | Xil_DCacheInvalidate() 从OCM等更新Cache |
Xil_DCacheDisable(); | 关闭数据cache | 哈佛架构,存在指令cache和数据cache |
RegisterHandlers(); | 注册异常处理函数 | N\A |
DDRInitCheck(); | DDR检测 | 最基础最简单的测试 |
InitPcap(); | PACP桥初始化 | N\A |
XDcfg_GetControlRegister(); | 设置PACP控制寄存器 | 之后会检查AES的源秘钥 |
MarkFSBLIn(); | This function sets FSBL is running mask in reboot status register | Store FSBL run state in Reboot Status Register |
Xil_In32(BOOT_MODE_REG); | 读取启动模式寄存器 | 对取值进行判断确定启动模式 |
上表列出了官方FSBL的几个主要的功能函数, 如图
16所示,Xilinx的FSBL执行流程,从其中可以看到官方FSBL会对芯片功能进行一个简单的检测,初始化,以及启动镜像的装载等工作.
在Zynq启动的启动章节谈到FSBL阶段可以修改Xilinx的模板,理论上甚至可以完全替换为用户程序,但是这种操作方式经过试验,是对用户代码有一定要求的.首先用户代码肯定要限定在192KB大小,这个大小可以在SDK的编译完成后的Console窗口查看.其次,必须存在初始化以及装载等必要的函数调用,这个根据工程特性而变,必须满足运行的一切硬件条件,就本例程而言,将FSBL程序替换为hello
world程序后会出现不能顺利固化的情况.所以说最好的建议是在官方FSBL的基础上做功能性的修改或功能添加,比如官方的DDR检测就是很简单可以说毫无作用的检测,工程中可以替换为更加高效的DDR检测方式.
图 1-6: Xilinx FSBL 函数执行流程
AMP双核工程基于70Z45核心板平台实现,在完成相应的硬件设计后启动SDK开始实现软件设计.
Step1:如图 17创建两个不同processor的empty
application,命名以cpu0_controller和cpu1_slaver为例.
图 17 AMP模式step 1
Step2: 如图
18修改两个工程的运行内存空间,即修改lscript.ld中ps_ddr_0的内存地址,可以自由设置运行空间,只需要不超出物理内存大小且两工程不存在重叠即可.
同时默认共享内存空间为ps7_ram_1,代码设计时只需对这个地址按需命名即可.
如果需要大量数据交互的情况需要自行add memory,在之后的代码设计时做好约定即可.
图 18 AMP模式step 2
Step3: 如图 19在cpu1_slaver工程的 bsp
中要增加编译选项“-DUSE_AMP=1”,该编译选项将影响到
cpu1_slaver工程代码里中断控制器 SCUGIC 的初始化函数以及 Cache 操作函数的编译,
若不增加该选项,可能会出现 cpu0_controller 和 cpu1_slaver中断异常和 Cache
一致性维护异常。
图 19 AMP模式step3
完成这一步就可以进行代码编写及调试.
双核AMP的基本架构同单核的区别在于单核可以直接进行代码的编写,而双核需要进行一些固定的代码支持才能够正常的运行.首先是主核即cpu0对cpu1的启动代码,其次是双核之间的数据通信.
双核AMP的软件设计要贯彻一个思想就是规避冲突,要充分理解双核各自的私有资源和公共资源,避免因为资源的访问冲突而导致逻辑错误.如果程序的功能复杂,可以考虑软件中断实现互斥,所谓互斥就是对共享数据的状态改变通过中断触发的方式进行访问,保证同一时间只有一个CPU对某个空间进行读写操作.
中断互斥只是为了更好的提高程序的健壮性,但是无论是否使用中断,双核之间的数据传递都是通过共享内存的方式实现,大多数情况下双核传递的数据多是指针数据,数据量不会很大,所以本例程选用速度更快的OCM空间为共享内存,当然也可约定一块DDR空间.
所谓共享内存就是, CORE0 和 CORE1
在内存空间中约定一块地址及长度已知的内存区域。然后,两者之间便可通过这片区域进行数据的传递.
ZYNQ 中存在 ICache 和 DCache, ICache 用于缓存可执行程序, DCache 用于缓存数据。
一般情况下, 用于保存可执行程序的 DDR 地址范围不会被除 CPU 以外的对象访问. 因此,
一般不存在 ICache 的一致性问题. 而 DCache在很多应用中却经常会被除 CPU
以外的对象访问,所以存在一致性问题.
两个核心各自拥有独立的 L1 DCache, 并且共享同一个 L2 DCache, Snoop Control
Unit(SCU)用于维护 CORE0 和 CORE1 的 L1 DCache 与 L2 DCache
之间的一致性,无需用户干预.因此,当共享内存区域位于DDR
中时,两者之间的数据传递并不需要考虑 DCache
一致性的维护。当使用OCM时特别是使用轮询的方式去访问共享内存空间Cache的数据一致性问题就特别重要,如不进行数据一致性维护基本上数据传输不会成功.
Zynq中维护 DCache 一致性的方法为:
写入方将数据写入共享空间对应地址区域后,需将残留在 DCache
中相应地址范围内的数据全部flush。读取方在从共享空间相应地址读取数据之前,需将
DCache 中共享空间相应地址范围内的数据全部设置为 invalid,然后 CPU 会再次通过
DCache 从 DDR3中读取该地址范围内最新的数据。具体的操作函数见表 11.
直接禁用OCM的cache属性
DCache 一致性维护的原理为:在多级存储器结构中, CPU 通过 1 级或多级 Cache 与 DDR
产生连接, CPU 本身不直接访问 DDR, 而是通过 Cache 访问 DDR。 Cache
中始终会暂存一小部分(通常是 KB~几 MB 量级) CPU 最近访问的 DDR
某些地址区域中的数据。因此,在应用程序中对 DDR 进行读或写操作, 实际上都是 CPU
对 Cache进行读或写操作。当 DDR 中某个地址范围内的数据突然被除 CPU 以外的
Master(如 DMA)改变时, 若此时 Cache中保存了这些区域的数据,且这些数据在 Cache
中状态为有效时,当 CPU 需要再次读取 DDR 这片区域的数据时,就不会让 Cache 去读取
DDR 中此区域内最新的数据来更新 Cache, 再从 Cache 里读取最新的数据, 而是直接从
Cache中读取原来的旧数据, 显然这不是期望的结果. 这时, 便产生了所谓的 Cache
一致性问题.
如章节1.2.11所阐述的,双核运行中cpu0是作为主核的状态存在 ,
cpu1默认处于休眠状态,需要按照以下步骤对cpu1进行唤醒:
确保 CPU1 的程序已经装入到存储器指定地址,并将这个地址(32bit)写入到
0xFFFF_FFF0 地址。
确保第一步的地址写入 OCM 生效.这时就需要进行cache数据一致性管理,可以关闭
DCACHE 对 OCM 的缓存,也可以flush 一下 DCACHE.
调用ARM指令集的SEV 指令,唤醒 CPU1.
具体实现方式如图 110参数为cpu1的运行地址,即在lscript.ld中设置的地址
图 110 cpu1唤醒函数
在cpu0_controller工程的main函数中调用上述函数即可.之后分别对双核的程序进行简单的验证设计就可以生成可执行bin文件.新建fsbl工程后与单核不同,需要4份文件生成bin,在boot
image partitions 最后追加cpu1_salver的elf文件, 由于elf文件本身含有地址信息
BOOTGEN 会自动识别出来,所以不需要指定拷贝到 DDR3
的目的地址。且固化启动时如不进行cpu1的唤醒,只会执行cpu0的用户程序.具体见图 111.
图 111 生成双核bin文件.
开发过程中不需要固化时,在JTAG模式下可以使用如图
112的方式进行下载,此时双核程序会同时运行,注意勾选项.
图 112 双核JTAG下载
设备安全系统的一个组成部分
执行三级序列 : 上电, 存储器清除 , 系统使能
复位系统包括 : 硬件复位 \ 看门狗复位 \ JTAG控制器复位 \ 软件复位;
Zynq每个模块和系统包含复位系统产生的一个复位;
硬件复位由上电复位信号PS_POR_B信号和系统复位信号PS_SRST_B信号驱动;
PS内产生复位信号的为3个看门狗定时器;
JTAG控制器所产生的复位只能复位PS的调试部分和一个系统级的复位;
软件能独立产生每个模块的复位或一个系统级的复位;
PS内主要复位信号之间的关系如图 22
图 2-2 复位子系统层次图
上电复位流程如
图 2-3 上电复位流程图
当上电复位后,
由外部系统和PS逻辑所控制的前两部开始相应,当PS可以开始工作后,在上电复位后可以产生任何类型的复位,这些复位可以插入到流程图.
能异步的确认或使上电无效. 当上电复位后,
有条件的允许它干净的传播到时钟模块输入逻辑.
并且如果使能的话,连接到PLL时钟电路
存在一个BOOT_MODE引脚,用于使能或者旁路所有PLL.
eFUSE 控制器脱离复位, 会自动将一些数据应用到PLL,
这样可以改善性能,并且为PS内的一些RAM提供冗余信息. 用户不可见且不可操作.
完成时间控制在50~100us内
如果使能PLL,则延迟上电复位信号, 直到锁定PLL. 设置PLL时钟将花费60us
如果选择旁路PLL,则不延迟上电复位信号.
在开始执行BootROM前,通过将 0 写入到所有的地址来清除内部的RAM
各类复位复位结果如下:
本节将会在Zynq的AMP模式下使用系统软件复位的方式完成多版本bin应用的切换.
多版本不断电切换的完成依赖于章节1.2.7
中提及的multiboot,本质上来说这是一种通用的版本切换方式,可应用于远程版本升级,自检跳转等应用场景.本例程在AMP模式下做一个简单的实现.
关于multiboot的详细说明可以参考ug585的6.3.11和ug821,重点关注gloden image
search和FSBL fallback.
基本原理是当触发软件复位时,Zynq会读取multiboot寄存器值,这个值*32KB即为程序存储的偏移地址,然后在此位置boot.
Multiboot寄存器的值只能通过上电清除.
涉及寄存器:
slcr->REBOOT_STATUS和devcfg->XDCFG_MULTIBOOT_ADDR_OFFSET
multiboot的使用步骤如下:
确定固化器件的编码地址,这个地址32KB对齐
将bin文件写入固化期间,同样需要32KB对齐,同时将头部地址写入devcfg.MULTIBOOT_ADDR
[MULTIBOOT_ADDR]寄存器,这个头部地址需要进行计算,按照以写入5M的位置为例:
5MB -> 0x50_0000 -> addr = 0x50_0000 / 0x8000 = 0xA0
执行完上述流程就可以完成程序的切换,在本例程中要注意cpu1的启动问题,需要切换的bin文件的elf文件不能单独为cpu1.原因在于cpu1默认为静默的,如果不存在唤醒程序,在执行完复位后cpu1不会启动,系统只会对PL进行重新的启动.
需要注意SLCR和DEVCFG在读写前都需先unlock.具体的实现方式如图 2-4,
图 2-4 multiboot实现
由图
21可以看到,Zynq的外设复位由软件复位产生,双核AMP模式下要完成在cpu0不停机的情况下cpu1的程序更新,整体思路就是把cpu1当成一种外设,由cpu0作为主控,通过cpu0复位cpu1,cpu1复位后将其运行空间的程序进行替换,也就是擦掉DDR原有程序,直接写入替换程序,之后重新启动cpu1即可完成程序的替换.替换程序的来源不限,可以通过网口实时导入,也可从flash等空间读取.
因为两个cortex-A9同属APU,那么cpu1的复位实际上是APU的复位,原因在于所谓APU(application
processing
unit)的主要组成为cpu.Xilinx为APU复位提供上电复位,系统复位,软件复位,debug复位和系统degbug复位.
本功能的实现依赖于软件复位,与系统复位和上电复位不同,当需要对双核的某个处理器应用进行复位时,必须先停止相关的时钟,断言复位,然后重启时钟.
当然系统复位和上电复位也是需要类似的操作,但是此时硬件会自动的处理这个问题.
因此,需要复位的cpu不能运行复位代码,即不能自己复位自己,这也是采用cpu0复位cpu1的原因,这个控制也可以是PL或者JTAG.以复位cpu0为例:
按步骤注册sclr. A9_CPU_RST_CTRL的字段
A9_RST0 = 1 to assert reset to cpu0
A9_CLKSTOP0 = 1 to stop clock to cpu0
A9_RST0 = 0 to release reset to cpu0
A9_CLKSTOP0 = 0 to restart clock to cpu0
执行复位后的状态如表 2-1所示
表 2-1 APU复位后状态表
Function | State after Reset |
---|---|
CPU1 | 在执行0xFFFFFE00~0xFFFFFFF0的代码保持在WFE状态 |
L1 缓存 | 禁用 |
MMU | 禁用 |
SCU | 禁用 |
地址过滤 | 1M 到4G的地址空间被映射到OCM,其余空间路由到L2cache |
L2cache | Disabled |
L2等待状态 | Tag RAM and Data RAM 等待在可读可写可执行,读写访问延迟 |
L2生效 | 未知(再次使用前确认) |
由上表可知,在执行完A9的外设复位后,等待在WFE状态的前提是必须是0xFFFFFE00~0xFFFFFFF0区间的代码,这一点与system
software reset不同,system software
reset是会直接加载0x00出的FSBL代码,由bsp中的boot.s进行cpu
ID识别和代码跳转,很显然,我们的需求是不可能重新引导FSBL且此时也并不会加载FSBL,所以如果无论使用那个cpu作为主控,复位cpu都不会自动进入WFE(wait
for event),此时就需要手动进入WFE模式.
手动进入即参考上电复位方式,上电复位时bootrom会使cpu1进入WFE模式,之后不断查询0xFFFF_FFF0处是否为0,不为0即是该地址存储内容即是cpu1的执行地址,启动跳转至该地址执行用户代码.所以需要实现在cpu1
reset后的0x00地址的跳转,目的在于使cpu1进入WFE模式.实现检查并跳转的代码如下:
Xil_Out32(0xFFFFFF00,0xE3E0000F);
Xil_Out32(0xFFFFFF04,0xE3A01000);
Xil_Out32(0xFFFFFF08,0xE5801000);
Xil_Out32(0xFFFFFF0C,0xE320F002);
Xil_Out32(0xFFFFFF10,0xE5902000);
Xil_Out32(0xFFFFFF14,0xE1520001);
Xil_Out32(0xFFFFFF18,0x0AFFFFFB);
Xil_Out32(0xFFFFFF1C,0xE1A0F002);
//向0x00写入跳转代码指向0xFFFFFFF0
Xil_Out32(0x00000000,0xE3E0F0FF);
//清0xFFFFFFF0
Xil_Out32(0xFFFFFFF0,0x00000000);
dmb();
结合复位和重启两个模块就可以实现cpu0对cpu1的控制,做到cpu0不停机的情况下不断重启cpu1.具体实现方式如下:
当成功执行cpu1的复位和重启后,可以得到cpu1随着cpu0进程执行不断重新执行用户程序,而需要完成程序的更新还需要完成重要的一步:
新程序的写入,由上一步可知,cpu1在在复位后会向0xFFFF_FFF0写入cpu1的执行地址,本例程为0x1F50_0000,这就意味至只需要将真正的可执行代码放入0x1F50_0000即可.
至此,只要的到真正的可执行文件就可以,经过对多版本的bin文件的比对,可以得到bin文件的一个特征,bin文件的头部为0x0~0x16FF,所有的bin文件0x1700的位置都是0x4900EA,通过对正在执行的cpu1用户程序的0x1F50_0000进行读取该值也为0x4900EA,所以大胆推测真正的可执行程序从0x1700开始,制作只包含cpu1_application.elf的bin文件,将其按照如下规则写入DDR(以本工程为例)
:
计算写入地址, write_addr = 0x1F50_0000 – 0x1700 = 0x1F4F_E900
写入长度 : cpu1_application.bin= 0x11710
使用SDK dump/restore memory工具按照前两步参数将cpu1_application.bin写入DDR
图 2-5 刷写DDR
在双核程序运行时刷写,刷写后执行复位重启,即可完成cpu0不停机更新cpu1应用版本.