- Introduction
Pricinple & Machenism
- HA01的内存地址空间
- BOOT ROM简介
- BOOT ROM中的Bootloader执行过程
- BVT的数据结构
Practice
- 源码中关于BVT的设计
- 在集成开发环境中调试
- Conclusion
- Reference
Introduction
HA
系列微控制器基于Arm Cortex-M7
处理器内核,集成了Security Boot
的功能。Security Boot
的关键代码集成在BOOT ROM
中的bootloader
程序中,用户不可见,以确保信息安全的需要。然而,开发者在自行编译固件时,需要配合BOOT ROM
中的bootloader
,才能正常地引导到用户应用程序,完成启动过程。
本文以HA
系列的YTM32B1HA01
微控制器(下文简称HA01
)为例,将详细介绍BOOT ROM
中bootloader
的运行机制,以及用户程序与之对接的设计要点。
Pricinple & Machenism
HA01的内存地址空间
从RM手册中可以找到HA01的内存地址空间映射表,如图x所示。从表中可以看到,0x0000_0000
开始的地址区是一块ITCM
存储器(RAM),而存放用户程序的pflash则被安排在0x0200_0000
开始的地址区间。在0x0100_0000
地址开始的内存空间中,安排了一块ROM。按照芯片内部的硬件设计,芯片上电复位对硬件电路做好初始化后,先进入ROM执行预烧录的bootloader
程序,进一步引导至用户应用程序。
在Arm Cortex-M7
架构中,ITCM
是专门用来存放程序指令(Instruction),DTCM
是专门用来存放数据(Data),至于OCRAM
,就对应于普通的SRAM
。如果按照以往Arm Cortex-M
架构中只区分flash
和SRAM
的用法,DTCM
和OCRAM
都可以当做是SRAM
使用,所以用户在常规用例的linker
文件中,也可以把DTCM
和OCRAM
连续的地址空间都当做SRAM
使用。但实际上,ITCM
和DTCM
都是TCM
(Tightly Coupled Memories)存储器,分别通过I-TCM
和D-TCM
总线访问,是等同于Cache
存储器的角色,直接同Arm Cortex-M7
内核相连,而不同于OCRAM
(或者早先Arm Cortex-M架构中的RAM)是通过总线(AXI
总线)间接同Arm Cortex-M7
内核连通。如此,一般情况下,若没有Cache
加持,Cortex-M7
内核访问TCM
的效率要高于访问OCRAM
。
对这些不同同种类的内存,可以根据需要灵活配置,以获得最优的访问效率。一种典型的用法:
- 在
ITCM
中存放中断向量表。这也是为什么把ITCM
安排到0x0000_0000
地址的原因。但需要在执行到pflash
中的用户程序之后,由用户程序将存放在pflash
中的中断向量表自行搬运到ITCM
,然后修改Arm核心的VTOR
寄存器重映射到0x0000_0000
作为中断向量表的基地址。如果ITCM
的空间大于预留的中断向量表占用的空间,剩下的空间用来存放ram code
也是不错的。 - 在
DTCM
中存放栈。方便处理器快速地申请和释放。 - 在
OCRAM
中存放堆,以及用户程序中定义的全局变量、缓冲区等。 OCRAM
可能跟Cache
搭配使用,以提高访问效率,但如果处理器内核和DMA
进行数据同步,Cache
机制可能会引入一些麻烦,此时如果不想繁琐地调整Cache
的配置,也可以将需要同步的缓冲区安排到DTCM
中。
BOOT ROM简介
HA01
上集成的BOOT ROM
使用了64KB
的存储器。芯片复位后,Cortex-M7
内核从ROM里的bootloader
开始执行引导程序。其中,将解析存放在pflash
开始位置的BVT
(Boot Vector Table)。这里的bootloader
主要用于实现四个功能:
安全启动Security Boot
- 在信息安全的体系中,
Security Boot
作为整个安全系统的置信根(Root of Trust),可以使用CMAC授权算法,对用户指定的程序(例如2nd bootloader
或OTA
)进行认证,这个过程中,利用硬件的HCU外设模块进行计算,其配置信息存放在BVT中。进一步,HCU外设模块使用的密钥,存放在HCU_NVR
中,由OEM主机厂或Tier-1预先烧录,用户全程不可见。 - 如果
CMAC
验签失败,处理器内核可以继续运行bootloader
并引导至用户程序,但HCU将不会导入存放与HCU_NVR
中的密钥,一切加密相关的操作将得不到芯片硬件的支持。
快速从Powerdown模式下唤醒
- 当检测到从Powerdown模式下唤醒而产生的复位,可以跳过Security Boot和内核自检的过程,从而加速启动过程。
- 可选地启用配置独立的IVT(Interrupt Vector Table)。这需要配合REGFILE中最开始的两个存储字存放配置信息。REGFILE在Powerdown模式下仍能保存数据。
对内核进行例行自检(Structural Core Self-Test,SCST)
- 这是为了配合实现功能安全等级
ASIL-D
标准,而进行的运行时测试。
配置系统时钟和看门狗
- 根据
BVT
中的配置,在bootlaoder
过程中启用或者停用看门狗,以及对应的超时周期(HA01
的WDG
在默认情况下,使用来自于IPC
的SIRC/4
作为计数时钟源)
BOOT ROM中的Bootloader执行过程
在HA01
的RM手册中,可以找到bootloader
执行过程的示意图。如图x所示。
从图x中可以看到,芯片从上电启动到用户的main()函数,大体要经历三个阶段:
- MCU Reset Phase
- Boot Code Function
- MCU App run Phase
芯片上电后(POR),根据REGFILE
区域中的REG0
的配置值,分别进入快速启动流程(Fast Wakeup Boot Mode)或常规启动流程(Normal Boot Mode)。
在快速启动流程中:
- 解析
BVT
中预设的参数,以初始化系统时钟。 - 从
REGFILE->REG1
寄存器中获取快速启动流程的应用程序入口地址(同常规启动流程最终引导的应用程序入口地址可能不同) - 查看
IVT
的地址并确保指定IVT的地址空间位于SRAM
,后续将在用户自行编写的应用程序初始化阶段存放中断向量表。 - 跳转到快速启动流程的程序入口。
- 中间过程如果遇到任何异常,都会进入
static mode
(原地死循环)。
在常规启动流程中:
- 解析
BVT
中预设的参数。 - 若
BVT
中配置启用了Security Boot,则执行验签过程。 - 若
BVT
中配置启用了SCST Test,则执行Cortex-M7处理器内核的自检。 - 根据
BVT
中的参数,配置看门狗WDG
外设模块。 - 跳转到常规启动流程的程序入口。
- 中间过程如果遇到任何异常,都会进入
static mode
(原地死循环)。
快速启动流程中不执行Security Boot和SCST Test过程,因此可以缩短启动时间。但注意,快速启动流程的应用程序入口同常规启动的应用程序入口可能不是同一个。
BVT的数据结构
BVT
保存了启动过程中的很多关键参数,它本身位于pflash存储区,0x0200_0000
开始的一段内存空间,可由用户在编写应用程序时,创建一个结构体,然后在链接过程中将其安排至0x0200_0000
地址。BVT
的参数占用108
个字节,但实际上却需要预留2KB
的空间,这是由于pflash的一个sector大小为2KB
。用户实际开发过程中,也可以调整linker file
,避开0x0200_0000
开始的一个sector
,如此,可保持最近一次烧写的可用的BVT
配置不变。
手册中列写了BVT的字段定义,如图x所示。
从这里可以窥到一个有趣的设计,竟然提到了有CM7_0
、CM7_1
和CM7_2
三个Cortex-M7
处理器核心,并且每个核心有main
和secondary
两个核心。从描述上看,这描述的是一对锁步核(lockstep),并且每个核心都可以有独立的应用起始地址(application start address),将来用于全并行地分别执行不同的应用程序。实际上,目前的HA01使用的是一个不可拆开的单锁步核,故只有CM7_0_main
可用。可以想见,在后续的产品规划中,可能会出现最多3对锁步核,如果进一步将锁步核拆开,可以设计最多6个独立的Cortex-M7
核心。
BVT中0x4
偏移地址上的Boot Configuration Word
中进一步定义了启动相关的配置项,如图x中的表格所示。
- 这里提供了一个信息,运行bootloader程序使用了FIRC(96MHz)作为时钟源,但可以通过CPDIVS设置1分频或者2分频。
- 这里还设计了很多功能的开关,例如看门狗、6个核心的独立使能开关,SCST自检功能。
- 这里还提到了
serial security boot mode
和normal boot mode
,猜测前者可能就是验签的计算过程。
Practice
源码中关于BVT的设计
用户从SDK中导出的样例工程,就有关于对BVT的定义,以及在链接脚本中专门分配的代码段。
以hello_world
工程为例,其中的secure_boot_YTM32B1HA0.h
文件中有关于BVT类型的定义:
...
#define BVT_HEADER_SEG __attribute__((used)) __attribute__((section (".bvt_header")))
...
/*!
* @brief the BVT structure type definition
*
* Implements : bvt_header_config_t_Class
*/
typedef struct{
uint32_t bvt_marker; /* BVT marker */
uint32_t boot_config_word; /* Boot configuration word */
uint32_t sbt_config_group_addr; /* secure boot start address */
uint32_t lc_config; /* lifecycle configuration */
uint32_t cm7_0_main_app_addr; /* CM7_0 main core start address */
uint32_t cm7_0_secondary_app_addr; /* Reserved for CM7_0_SECONDARY_APP_ADDR */
uint32_t cm7_1_main_app_addr; /* Reserved for CM7_1_MAIN_APP_ADDR */
uint32_t cm7_1_secondary_app_addr; /* Reserved for CM7_1_SECONDARY_APP_ADDR */
uint32_t cm7_2_main_app_addr; /* Reserved for CM7_2_MAIN_APP_ADDR */
uint32_t cm7_2_secondary_app_addr; /* Reserved for CM7_2_SECONDARY_APP_ADDR */
uint32_t app_wdg_timeout; /* timeout set of the WDG watchdog of the application core */
} bvt_header_config_t;
而在secure_boot_YTM32B1HA0.c
文件中,有定义具体的结构体实例:
#define BOOT_CONFIG_WORD (BVT_BCW_CPDIVS_SET(1) | CM7_0_M_EN)
/* define the timeout of ADG watchdog of the application main core */
#define APP_WDG_TIMEOUT (120000) /* 10ms respect to 12MHz SIRC as reference clock*/
/* Flash erased status */
#define RESERVED ( 0xFFFFFFFF )
/* BVT Header configuration */
const bvt_header_config_t bvt_header BVT_HEADER_SEG = {
BVT_VALID_MARK, /* BVT marker */
BOOT_CONFIG_WORD, /* Boot configuration word */
(uint32_t)&secure_boot_group, /* secure boot start address */
RESERVED, /* lifecycle configuration */
DEFAULT_START_ADDRESS, /* CM7 main core start address */
RESERVED,
RESERVED,
RESERVED,
RESERVED,
RESERVED,
APP_WDG_TIMEOUT, /* timeout set of the WDG watchdog of the application core */
};
此处注意,定义的bvt_header
结构体将位于BVT_HEADER_SEG
数据段内。其中DEFAULT_START_ADDRESS
定义了同应用软件约定的启动地址,具体位于0x0200_0800
。位于secure_boot_YTM32B1HA0.h
文件中。
当下载程序后,可以在memory
的视图中可以看到,0x0200_0000
开始的位置,有存放关于BVT的配置信息。如图x所示。
/* BVT valid marker */
#define BVT_VALID_MARK ( 0xA55AA55A )
/* Default start address */
#define DEFAULT_START_ADDRESS ( 0x02000800 )
同时,在YTM32B1HA01_flash.sct
文件中,也相应指定了BVT所占用的存储空间(预留在0x0200_0000
至0x0200_07FF
,和应用程序的起始地址0x0200_0800
:
#define m_bvt_start 0x02000000
#define m_bvt_end 0x020007FF
#define m_bvt_size m_bvt_end - m_bvt_start + 1
#define m_interrupts_start 0x02000800
#define m_interrupts_size 0x00000400
。。。
LR_m_text m_bvt_start
{ ; load region size_region
ER_m_text_2 m_bvt_start FIXED m_bvt_size
{
.ANY (.bvt_header)
.ANY (.sb_config_group)
.ANY (.sb_config_section)
.ANY (.sb_cmac)
}
// vectors.
VECTOR_ROM m_interrupts_start FIXED m_interrupts_size
{
*(RESET +First)
}
// code.
ER_m_text m_text_start FIXED m_text_size
{
*(InRoot$$Sections)
.ANY (+RO)
}
。。。
当下载程序后,可以在memory的视图中可以看到,0x0200_0000
开始的位置,有存放关于BVT的配置信息。如图x所示。
在集成开发环境中调试
BOOT ROM
+ BVT
相互配合,在HA01芯片内部实现了一个bootloader
,但程序的起始地址也不再是常规默认的0x0000_0000
,如此,当在集成开发环境中调试程序时,可能会需要指定一个程序入口点。实际上,bootloader
的存在并不影响在载入调试环境时自动将程序断点到main()
函数:
- 在调试环境中,下载程序到芯片后复位,如果不显式地执行入口程序地址,芯片会自动从bootloader开始执行,然后从
BVT
中取得用户应用程序入口地址后,跳转到用户应用程序。确切地说,是进入到用户程序的Reset_Handler
函数,然后进入到main()
函数。 - 大多数集成调试环境,会“智能”地在
main()
函数开始的位置打一个断点,作为调试程序的开始,等待开发者发起进一步的调试动作。 - 这类似于以往开发者在有用户自定义的
bootloader
的环境下开发application
。
无论是Keil
还是IAR
,在集成开发环境内部启动调试时,都可以自动执行上述流程至main()
函数的断点。但是,当使用Segger Ozone
调试可执行文件时,由于默认会显式指定一个入口地址,如果没有合适的配置,默认从0x0000_0000
开始,就会出现错误。如图x。
从图x中可以看到,Ozone默认从固件程序的开始,0x0200_0000,读到了初始的PC和SP值,但这里的数据其实是BVT的内容,不是中断向量表的项目。故会报错。
此时,在配置初始载入点时,有几种可行的配置,都可以实现正常调试:
- Initial PC -> Read from Location ->
0x0100_0004
, Initial Stack Pointer -> Read from Location ->0x0100_0000
。这种设置是从BOOT ROM中的bootloader
开始启动,然后引导到用户应用程序。 - Initial PC -> Read from Location ->
0x0200_0804
, Initial Stack Pointer -> Read from Location ->0x0200_0800
。这种设置是跳过BOOT ROM中的bootloader
启动,直接从用户应用程序启动。 - Initial PC ->
Do not set
, Initial Stack Pointer ->Do not set
。这种设置是软件工具不要多做,让芯片的硬件机制起作用。此时芯片从BOOT ROM中的bootloader
开始启动,然后引导到用户应用程序。
建议使用第三种方式,充分利用硬件机制,确保对芯片有最完整的初始化过程,也是最接近在芯片正常上电启动的运行情况。
Conclusion
YTM32B1HA
系列微控制器基于Arm Cortex-M7
处理器内核,集成了BOOT ROM
,并重新划分了地址空间中的内存分配,这使得芯片上电之后的引导过程和用户应用程序的存放地址发生了一些变化,对应地,使用调试软件工具也需要做相应的适配操作,以避免出现异常的情况。
无论如何,BOOT ROM
中的bootloader
和BVT
的配置,为应用带来了一些新的功能,例如基于硬件计算引擎进行加速和加密的安全启动过程、对多核心运行的管理等等。这为支持复杂的片上系统的奠定了一定的基础,也是设计多核心SoC的启动过程的一种可行的尝试。
Reference
- YTM32B1HA0x_RM_v1.1.pdf https://yt-sdk.oss-accelerate...
- 强大CORTEX M7中TCM https://www.nxpic.org.cn/modu...
- 了解i.MXRT1xxx系列里负责动态分配ITCM/DTCM/OCRAM大小的FlexRAM模块 https://www.cnblogs.com/henja...
作者:安德鲁苏
来源:安德鲁的设计笔记本
推荐阅读
欢迎大家点赞留言,更多Arm技术文章动态请关注极术社区嵌入式客栈专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。