大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是超級下載演算法開發筆記(2)之識別當前i.MXRT型號。
文接上篇 《超級下載演算法(RT-UFL)開發筆記(1) - 執行在不同CM核心下》,我們已經解決了超級下載演算法能夠在i.MXRT全系列下執行的問題,現在我們往前繼續推進專案。因為這個超級下載演算法將來要跑在很多個晶片型號上,有時候因為型號間差異,我們不得不針對性地弄出不同程式碼處理分支,而這一切的前提是我們能動態地獲取當前晶片型號。
本篇是開發筆記第二篇,我們們就重點聊聊如何讓超級下載演算法知道當前跑在哪個i.MXRT型號下。
一、尋找i.MXRT家族的ID資訊
恩智浦當前MCU產品線眾多,i.MXRT是2018年才開始推出的新系列,在這之前恩智浦主推的MCU是經典的Kinetis和LPC系列,痞子衡對這兩個系列產品也非常熟,型號資訊在Kinetis和LPC上都有展現。
1.1 回顧Kinetis和LPC
Kinetis系列有一個專門存放型號資訊的暫存器即SIM->SDID,這個暫存器設計得非常經典,其FAMILYID + SUBFAMID + SERIESID區域組合將Kinetis系列的家族分類特性展示得淋漓盡致。
LPC系列存放型號資訊的暫存器是SYSCON->DEVICE_ID0,這個暫存器是另外一種設計風格,低20bit直接就是LPC系列號的值,比如LPC54114xxx型號其SYSCON->DEVICE_ID0[19:0] = 0x54114,是不是簡單粗暴。
1.2 查詢i.MXRT的ID暫存器
我們知道i.MXRT從架構上分為四位數和三位數兩大陣營,四位數源自i.MX應用處理器,三位數源自LPC系列,我們分別來看。
翻看i.MXRT四位數典型型號i.MXRT1060的參考手冊,並沒有找到任何有關型號資訊的暫存器,僅有UID暫存器,但UID跟晶片型號無關。四位數上找不到型號資訊暫存器,也跟其源於i.MX有關,畢竟MPU不像MCU那樣會細分特別多的型號。
再看i.MXRT三位數典型型號i.MXRT600的參考手冊,我們找到了型號資訊,在SYSCTL0->PRODUCT_ID暫存器中,細看其暫存器設計,還是能看出LPC的影子的,SYSCTL0->PRODUCT_ID[15:4]直接就是系列號的值。
二、確認i.MXRT型號的替代方法
根據上一節的分析,我們幾乎不能用傳統Kinetis或LPC上型號資訊暫存器那一套方式來統一獲取i.MXRT型號,那有沒有替代方法呢?答案當然是有。靈感來自於痞子衡之前研究i.MXRT的ROM API時寫過的一篇文章 《瞭解i.MXRTxxx系列ROM API及其與i.MXRT1xxx系列的差異》,i.MXRT系列全部都有BootROM,每個型號的ROM區域內容並不盡然相同,我們可以通過讀幾個ROM區域不同位置的值來定位型號。
2.1 讀SCB->CPUID得到核心版本
i.MXRT四位數(都是CM7核心)的ROM基址是0x200000,而i.MXRT三位數(都是CM33核心)的ROM基址是0x3000000,基址是有差異的,所以我們首先需要先找出一種方法來區分基址,這裡可以藉助ARM Cortex-M核心模組SCB裡的CPUID暫存器。
翻看ARM Cortex-M核心的Generic User Guide,可以找到如下CPUID暫存器的定義,其中CPUID[PartNo]中記錄了核心版本,CM7的值是0xC27,CM33的值是0xD21。
我們可以很容易寫出如下ufl_get_core_type()函式。
/* See Part number of core in Cortex-Mx Generic UG */
#define CORE_ID_CM33 (0xD21)
#define CORE_ID_CM7 (0xC27)
typedef enum _core_type
{
kCoreType_Invalid = 0U,
kCoreType_CM33 = 1U,
kCoreType_CM7 = 2U,
} core_type_t;
static core_type_t ufl_get_core_type(void)
{
core_type_t coreType = kCoreType_Invalid;
uint32_t coreid = (SCB->CPUID & SCB_CPUID_PARTNO_Msk) >> SCB_CPUID_PARTNO_Pos;
switch (coreid)
{
case CORE_ID_CM33:
coreType = kCoreType_CM33;
break;
case CORE_ID_CM7:
coreType = kCoreType_CM7;
break;
default:
break;
}
return coreType;
}
2.2 通過ROM內容定位i.MXRT型號
有了ufl_get_core_type()函式,我們便可以進一步寫出如下ufl_get_imxrt_chip_id()函式,s_romFingerprint[]結構體陣列預先存放全部i.MXRT型號的ROM定位資訊(此處僅示例了RT600和RT1060),我們選了ROM偏移0x8000、0xa000、0xc000處的四位元組資料來定位,如後期隨著型號的增多,出現定位位置處資料全部雷同的巧合的話,可以更改定位位置保證定位資料一定不相同。
typedef enum _rt_chip_id
{
kChipId_Invalid = 0xFFU,
kChipId_RT6xx = 1U,
kChipId_RT106x = 2U,
} rt_chip_id_t;
#define RT_ROM_BASE_CM33 (0x03000000u)
#define RT_ROM_BASE_CM7 (0x00200000u)
#define ROM_FP_OFFSET1 (0x8000)
#define ROM_FP_OFFSET2 (0xa000)
#define ROM_FP_OFFSET3 (0xc000)
typedef struct _rom_fingerprint
{
uint32_t chipId;
uint32_t content[3];
} rom_fingerprint_t;
static const rom_fingerprint_t s_romFingerprint[] = {
{kChipId_RT6xx, {0xb108f82a, 0x0200f2c5, 0x0070f104} }, // From ROM 2.0rc5.1
{kChipId_RT106x, {0xb0893000, 0x80dbf000, 0xf2c44100} }, // From ROM 1.0rc3
};
rt_chip_id_t ufl_get_imxrt_chip_id(void)
{
rt_chip_id_t chipId = kChipId_Invalid;
core_type_t coreType;
uint32_t rtRomBase = 0;
coreType = ufl_get_core_type();
if (kCoreType_CM33 == coreType)
{
rtRomBase = RT_ROM_BASE_CM33;
}
else if (kCoreType_CM7 == coreType)
{
rtRomBase = RT_ROM_BASE_CM7;
}
else
{}
do
{
uint32_t content[3];
content[0] = *(uint32_t *)(rtRomBase + ROM_FP_OFFSET1);
content[1] = *(uint32_t *)(rtRomBase + ROM_FP_OFFSET2);
content[2] = *(uint32_t *)(rtRomBase + ROM_FP_OFFSET3);
uint32_t idx = sizeof(s_romFingerprint) / sizeof(rom_fingerprint_t);
while (idx--)
{
if (!memcmp(s_romFingerprint[idx].content, content, sizeof(content)))
{
chipId = (rt_chip_id_t)s_romFingerprint[idx].chipId;
break;
}
}
} while (0);
return chipId;
}
至此,超級下載演算法開發筆記(2)之識別當前i.MXRT型號痞子衡便介紹完畢了,掌聲在哪裡~~~
歡迎訂閱
文章會同時釋出到我的 部落格園主頁、CSDN主頁、知乎主頁、微信公眾號 平臺上。
微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。