痞子衡嵌入式:瞭解i.MXRTxxx系列ROM API及其與i.MXRT1xxx系列的差異
阿新 • • 發佈:2020-08-07
----
大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是**i.MXRTxxx系列ROM API設計細節**。
痞子衡之前寫過兩篇文章 [《利用i.MXRT1xxx系列ROM提供的FlexSPI driver API可輕鬆IAP》](https://www.cnblogs.com/henjay724/p/13202824.html)、[《其實i.MXRT1050,1020,1015系列ROM也提供了FlexSPI driver API》](https://www.cnblogs.com/henjay724/p/13283334.html) 基本把i.MXRT1xxx全系列的ROM API及其FlexSPI NOR驅動設計都講清楚了,其實i.MXRTxxx系列的ROM API設計跟i.MXRT1xxx系列的設計思路差不多(其實本就是同一個恩智浦研發小組負責的),僅有一些微小區別,本文痞子衡主要就是點出那些區別。
### 一、ROM基址差異
ROM API程式碼首先是在BootROM裡,BootROM程式碼是出廠前固化在ROM區域的。因為架構設計的關係,i.MXRTxxx系列和i.MXRT1xxx系列的ROM區域在系統記憶體裡的對映地址不同。
下表是i.MXRTxxx系列代表型號i.MXRT500的部分系統記憶體對映,可以看到ROM區域起始地址是0x03000000(非安全域)。目前i.MXRTxxx都是Cortex-M33核心,支援TrustZone特性,所以0x13000000也是ROM起始地址(安全域),為了通用性,我們認0x03000000就可以了,這個地址在安全狀態和非安全狀態下都能被訪問。
![](http://henjay724.com/image/cnblogs/i.MXRTxxx_BootROM_API_RT500_mem_map_v2.jpg)
下表是i.MXRT1xxx系列代表型號i.MXRT1060的部分系統記憶體對映,可以看到ROM區域起始地址是0x00200000。i.MXRT1xxx系列都是Cortex-M7核心,沒有TrustZone特性,不存在i.MXRTxxx上那樣的兩種狀態域下的地址。
![](http://henjay724.com/image/cnblogs/i.MXRTxxx_BootROM_API_RT1060_mem_map.PNG)
### 二、API基址備份位置差異
在i.MXRT1xxx系列ROM API介紹的文章裡,痞子衡介紹過g_bootloaderTree地址值被複制了一份放在了BootROM中斷向量表第8個向量的位置處(該向量為ARMv7-M架構下未定義的系統向量),因此讀取0x0020001c處開始的4bytes便能找到i.MXRT1xxx系列的g_bootloaderTree。
但是由於i.MXRTxxx是Cortex-M33核心,屬於ARMv8-M架構,從下圖中可以看出ARMv8-M架構下中斷向量表第8個向量是SecureFault,已經被定義了,因此BootROM把g_bootloaderTree地址值放到了第9個向量的位置處(該向量為ARMv8-M架構下未定義的系統向量),故讀取0x03000020處開始的4bytes才能找到i.MXRTxxx系列的g_bootloaderTree(這種方式在實際API呼叫中並不可取,至於原因嘛,先賣個關子)。
![](http://henjay724.com/image/cnblogs/i.MXRTxxx_BootROM_API_CM_exception_type.PNG)
下面是i.MXRT500 BootROM工程的startup檔案(IAR版),g_bootloaderTree確實在第9個向量處:
```C
MODULE ?cstartup
;; Forward declaration of sections.
SECTION CSTACK:DATA:NOROOT(3)
SECTION .intvec:CODE:NOROOT(2)
EXTERN __iar_program_start
EXTERN g_bootloaderTree
PUBLIC __vector_table
PUBLIC __vector_table_0x1c
DATA
__vector_table
DCD sfe(CSTACK)
DCD Reset_Handler
DCD DefaultISR
DCD HardFault_Handler
DCD DefaultISR
DCD DefaultISR
DCD UsageFault_Handler
__vector_table_0x1c
DCD SecureFault_Handler
DCD g_bootloaderTree
DCD 0
DCD 0
DCD SVC_Handler
DCD DefaultISR
DCD 0
DCD DefaultISR
DCD SysTick_Handler
;; ...
```
### 三、API原型定義差異
下面是i.MXRTxxx系列ROM API原型定義及其例項(適用i.MXRT500/600),基本形式跟i.MXRT1xxx差不多,但是API功能更豐富,除了FlexSPI NOR驅動,還有iap api、USB low-level driver、otp driver等(我們知道,i.MXRTxxx與LPC系列同根同源,LPC系列ROM裡一般都會整合很多經典SDK驅動,比如內部flash、low power驅動,有了這些穩定的驅動API,LPC系列的使用者手冊裡甚至都會省去這些IP的暫存器介紹,直接就是API的介紹)。
```C
typedef struct
{
void (*runBootloader)(void *arg);
uint32_t version;
const char *copyright;
const bootloader_context_t *runtimeContext;
const kb_interface_t *kbApi;
const usb_driver_interface_t *usbDriver;
const USBD_API_T *lpcUsbDriver;
const flexspi_nor_flash_driver_t *flexspiNorDriver;
const ocotp_driver_t *otpDriver;
const skboot_authenticate_interface_t *skbootAuthenticate;
} bootloader_api_entry_t;
//! @brief Static API tree.
__root const bootloader_api_entry_t g_bootloaderTree @".rom_api_tree_section" = {
.runBootloader = bootloader_user_entry,
.version = MAKE_VERSION('K', 3, 0, 0),
.copyright = "Copyright 2019 NXP.",
.runtimeContext = &g_bootloaderContext,
.kbApi = &g_romApiInterface,
.usbDriver = &g_usbDriverInterface,
.flexspiNorDriver = &g_flexspiNorFlashDriverInterface,
.otpDriver = &g_otpDriverInterface,
.skbootAuthenticate = &g_skbootAuthenticateInterface,
};
```
### 四、API例項連結差異
i.MXRT1xxx系列ROM API例項g_bootloaderTree都是讓連結器自由連結的,因此每個具體型號的實際ROM API連結地址沒有一致的規律可循(這也是為什麼要在中斷向量表裡固定位置統一儲存一份),而這點在i.MXRTxxx上有了改進,i.MXRTxxx裡將g_bootloaderTree放到了 .rom_api_tree_section 段裡,在連結檔案裡將該段固定連結在ROM區域最後4KB處(BootROM程式碼沒有把全部ROM空間用盡)。
下面是i.MXRTxxx BootROM原始檔中g_bootloaderTree的定義,加了段修飾。此外還有額外的k_romcrc,標示API例項區域的結束。
```C
__root const bootloader_api_entry_t g_bootloaderTree @".rom_api_tree_section" = {
.runBootloader = bootloader_user_entry,
.version = MAKE_VERSION('K', 3, 0, 0),
.copyright = "Copyright 2019 NXP.",
.runtimeContext = &g_bootloaderContext,
// ...
};
__root const uint32_t k_romcrc @".romcrc" = 0xdeadbeef;
```
下面是i.MXRTxxx連結檔案(IAR工程)中 .rom_api_tree_section 段的處理(i.MXRT500型號示例,ROM空間是192KB)。你可能好奇為啥ROM_API_TREE_xx等值是放在0x13000000開始的安全域ROM空間對映,BootROM屬於上電啟動第一級,負責晶片系統的安全和啟動,當然是工作在安全狀態下,可以訪問安全域地址空間。
```text
define symbol __ICFEDIT_region_ROM_API_TREE_start__ = 0x1302f000;
define symbol __ICFEDIT_region_ROM_API_TREE_end__ = 0x1302f0ff;
define symbol __ICFEDIT_region_ROM_CRC_CHECKSUM_start__ = 0x1302fffc;
define symbol __ICFEDIT_region_ROM_CRC_CHECKSUM_end__ = 0x1302ffff;
define region ROM_API_TREE_region = mem:[from __ICFEDIT_region_ROM_API_TREE_start__ to __ICFEDIT_region_ROM_API_TREE_end__];
define region ROM_CRC_CHECKSUM = mem:[from __ICFEDIT_region_ROM_CRC_CHECKSUM_start__ to __ICFEDIT_region_ROM_CRC_CHECKSUM_end__];
place in ROM_API_TREE_region { section .rom_api_tree_section };
place in ROM_CRC_CHECKSUM { section .romcrc };
```
基於上面的設計,你才會在i.MXRT500參考手冊裡Non-Secure Boot ROM章節看到如下ROM API地址及結構資訊圖(圖中僅標了常用的API功能函式),實際ROM API呼叫時,App的執行其實都是經過ROM引導和認證的,App中既可以訪問安全域地址(0x1302f000)來呼叫API,也可以訪問非安全域地址(0x0302f000)來呼叫API。
![](http://henjay724.com/image/cnblogs/i.MXRTxxx_BootROM_API_RT500_tree_structure.PNG)
最後再來回答前面賣的關子,為什麼i.MXRTxxx系列通過BootROM中斷向量表第9個向量值來訪問ROM API這種方式並不可取?其實從BootROM煞費苦心地將g_bootloaderTree固定連結在ROM區域最後4KB處,你就能看出其用意。如果你掛上偵錯程式直接訪問i.MXRTxxx的ROM區域前20KB的空間,你會發現無法訪問,在App裡AHB方式讀這個區域,也會直接產生HardFault,因為BootROM裡做了特殊設計故意隱藏了前20KB空間,這個空間裡存放了BootROM想要保護的資料和程式碼,至於內容是啥,純屬機密,恕不奉告,哈哈。
至此,i.MXRTxxx系列ROM API設計細節痞子衡便介紹完畢了,掌聲在哪裡~~~
### 歡迎訂閱
文章會同時釋出到我的 [部落格園主頁](https://www.cnblogs.com/henjay724/)、[CSDN主頁](https://blog.csdn.net/henjay724)、[知乎主頁](https://www.zhihu.com/people/henjay724)、[微信公眾號](http://weixin.sogou.com/weixin?type=1&query=痞子衡嵌入式) 平臺上。
微信搜尋"__痞子衡嵌入式__"或者掃描下面二維碼,就可以在手機上第一時間看了哦。
![](http://henjay724.com/image/github/pzhMcu_qrcode_258x2