• 正文
    • 一、通用芯片上電啟動(dòng)流程
    • 二、從源代碼角度看啟動(dòng)流程
    • 三、一個(gè) __low_level_init() 相關(guān)的重定向?qū)嶒?yàn)
  • 相關(guān)推薦
申請(qǐng)入駐 產(chǎn)業(yè)圖譜

深扒IAR啟動(dòng)函數(shù)流程及其底層初始化設(shè)計(jì)

03/26 14:10
724
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家分享的是IAR啟動(dòng)函數(shù)流程及其__low_level_init設(shè)計(jì)對(duì)函數(shù)重定向的影響。

上一篇文章 《IAR下RT-Thread工程自定義函數(shù)段重定向失效分析》 里我們找出了影響 IAR 鏈接器處理自定義程序段重定向的原因,主要跟 __low_level_init() 函數(shù)有關(guān),這個(gè)函數(shù)屬于 IAR 底層設(shè)計(jì),它在 IAR 啟動(dòng)函數(shù) __iar_program_start() 中會(huì)被自動(dòng)調(diào)用。

__iar_program_start() 是 IAR 標(biāo)準(zhǔn)啟動(dòng)函數(shù),也屬于 IDE 底層設(shè)計(jì)。在任何一個(gè) Cortex-M 廠商芯片的啟動(dòng)文件里(startup_xxDevice.s)都能看到它的身影,它是復(fù)位函數(shù) Reset_Handler() 和 主函數(shù) main() 之間的橋梁,今天我們就仔細(xì)說(shuō)說(shuō)這個(gè)啟動(dòng)函數(shù)以及其中 __low_level_init 設(shè)計(jì):

    Note 1:閱讀本文前需要對(duì) 《IAR鏈接文件(.icf)》、《IAR映射文件(.map)》 這兩種文件有所了解。Note 2:本文使用的 IAR EWARM 軟件版本是 v9.10.2。

一、通用芯片上電啟動(dòng)流程

在深入挖掘 IAR 啟動(dòng)函數(shù)源代碼之前,有必要先整體了解一下通用的芯片上電啟動(dòng)流程,即進(jìn)入用戶 main 函數(shù)之前內(nèi)核必須要做的事情,注意這里并不包含芯片底層外設(shè)的初始化(這是因芯片而異的)。

通用啟動(dòng)流程簡(jiǎn)單來(lái)說(shuō)分為如下四步:第一步是從 ROM 區(qū)域中斷向量表里獲取入口函數(shù)開(kāi)始執(zhí)行,設(shè)置好初始棧指針,有了正確的棧,內(nèi)核就具備函數(shù)跳轉(zhuǎn)執(zhí)行的能力了。第二步和第三步是全局變量的初始化(將全局變量初值從 ROM 區(qū)域拷貝到變量所鏈接的 RAM 區(qū)域),全局變量初始化完成,應(yīng)用程序就有了正確的初始態(tài),最后一步就是跳轉(zhuǎn)到 main 函數(shù)。

二、從源代碼角度看啟動(dòng)流程

在上一節(jié)通用啟動(dòng)流程的指導(dǎo)下,我們還需要增加一些 MCU 外設(shè)相關(guān)的初始化便形成了完整的芯片啟動(dòng)流程,現(xiàn)在我們從源代碼角度再來(lái)看一下具體實(shí)現(xiàn)。

2.1 典型的 Cortex-M 復(fù)位函數(shù)

我們知道復(fù)位函數(shù) Reset_Handler() 是芯片上電啟動(dòng)執(zhí)行的第一個(gè)函數(shù)(有時(shí)又叫入口函數(shù)),它完成了進(jìn)入用戶 main() 函數(shù)之前的全部動(dòng)作。隨便下載一家 Cortex-M 廠商芯片 SDK 包,找到 IAR 版啟動(dòng)文件,其復(fù)位函數(shù)流程都差不多,這是 Cortex-M 內(nèi)核架構(gòu)決定的。

如下是典型的復(fù)位函數(shù)代碼。復(fù)位函數(shù)里的操作包括關(guān)全局中斷、設(shè)置中斷向量表首地址、設(shè)置棧頂、系統(tǒng)初始化、開(kāi)全局中斷、進(jìn)啟動(dòng)函數(shù)。其中系統(tǒng)初始化 SystemInit() 函數(shù)是因芯片而異的,各廠商 SDK 里會(huì)有具體源代碼實(shí)現(xiàn)(一般在 system_xxDevice.c 文件里),這里面主要做芯片硬件層面的初始化,比如關(guān)看門(mén)狗、Cache 初步設(shè)置等,保證內(nèi)核不受硬件模塊狀態(tài)影響,能正常執(zhí)行指令。

????????THUMB

????????PUBWEAK?Reset_Handler
????????SECTION?.text:CODE:REORDER:NOROOT(2)
Reset_Handler
????????CPSID???I???????????????;?Mask?interrupts
????????LDR?????R0,?=0xE000ED08
????????LDR?????R1,?=__vector_table
????????STR?????R1,?[R0]
????????LDR?????R2,?[R1]
????????MSR?????MSP,?R2
????????LDR?????R0,?=SystemInit
????????BLX?????R0
????????CPSIE???I???????????????;?Unmask?interrupts
????????LDR?????R0,?=__iar_program_start
????????BX??????R0

2.2 __iar_program_start() 到底干了啥?

上一小節(jié)里我們知道復(fù)位函數(shù)里的最后一個(gè)動(dòng)作就是跳轉(zhuǎn)到啟動(dòng)函數(shù),將內(nèi)核執(zhí)行權(quán)交給 __iar_program_start(),這個(gè)啟動(dòng)函數(shù)源代碼并不在廠商 SDK 包里,而在 IAR 安裝目錄下,因?yàn)樗?IAR 的通用底層設(shè)計(jì)。

為了找到 __iar_program_start() 的源代碼,我們可以隨便編譯一個(gè) SDK 例程(痞子衡選擇的是 SDK_2.11.0_MIMXRT1170-EVKboardsevkmimxrt1170demo_appshello_worldcm7iar),查看其對(duì)應(yīng)映射文件(.map),發(fā)現(xiàn)啟動(dòng)函數(shù)來(lái)自于 cstartup_M.o,然后我們?cè)?IAR 安裝目錄下搜索 cstartup_M.c/.s 文件,最終我們?cè)谌缦侣窂秸业搅藛?dòng)函數(shù)相關(guān)的全部源文件。

IAR SystemsEmbedded Workbench 9.10.2armsrclibthumbcstartup_M.s
IAR SystemsEmbedded Workbench 9.10.2armsrclibthumbcmain.s
IAR SystemsEmbedded Workbench 9.10.2armsrclibruntimelow_level_init.c
IAR SystemsEmbedded Workbench 9.10.2armsrclibinitdata_init.c

結(jié)合啟動(dòng)函數(shù)相關(guān)源文件里的代碼,我們終于搞清了啟動(dòng)函數(shù)全部流程,也找到了我們最關(guān)心的 __low_level_init() 函數(shù)調(diào)用位置,它在 .data/.bss/.textrw 段初始化之前被執(zhí)行,所以它的功能應(yīng)該跟 SystemInit() 差不多。默認(rèn) __low_level_init() 函數(shù)是空的,返回值是 1(返回值 0/1 決定后面的 __iar_data_init3() 要不要執(zhí)行,1 是要執(zhí)行),如果你想激活這個(gè)函數(shù),需要在自己的源文件里重新定義實(shí)現(xiàn),IAR 編譯時(shí)會(huì)優(yōu)先引用重新定義的版本。

__iar_program_start() -> 
__cmain() -> 
__low_level_init() ->          // 底層初始化,默認(rèn)是個(gè)空函數(shù)
__iar_data_init3() ->          // .data, .bss, .textrw 段初始化
main()

2.3 __low_level_init() 設(shè)計(jì)注意事項(xiàng)

在 EWARM_DevelopmentGuide.ENU 手冊(cè)里搜索 __low_level_init,我們可以找到這個(gè)函數(shù)的設(shè)計(jì)初衷,官方說(shuō)法是為了給應(yīng)用程序一個(gè)早期初始化的機(jī)會(huì),本質(zhì)上就是跟 SystemInit() 一樣的作用,但是因?yàn)檫@個(gè) __low_level_init 函數(shù)只在 IAR 環(huán)境下適用,如果用了它,應(yīng)該程序代碼就不具備跨 IDE 的通用性,因此在各廠商 SDK 包里選擇了統(tǒng)一定義的 SystemInit() 來(lái)完成早期初始化工作。

IAR 開(kāi)發(fā)手冊(cè):IAR SystemsEmbedded Workbench 9.10.2armdocEWARM_DevelopmentGuide.ENU

EWARM_DevelopmentGuide.ENU 手冊(cè)里還特別提了幾點(diǎn)跟 __low_level_init 相關(guān)的注意事項(xiàng),均跟 IAR 鏈接器所識(shí)別的 initialize by copy 鏈接語(yǔ)法有關(guān),概括來(lái)說(shuō)就是因?yàn)?__low_level_init 是在 .data/.bss/.textrw 段初始化之前被執(zhí)行的,所以其代碼本身及其調(diào)用的全部代碼都不受 initialize by copy 作用,也就是這些代碼都不應(yīng)是 RAMFUNC 型。

    Note: 更準(zhǔn)確地說(shuō) initialize by copy 作用范圍其實(shí)是 __iar_data_init3() 之后的代碼

三、一個(gè) __low_level_init() 相關(guān)的重定向?qū)嶒?yàn)

最后我們?cè)僮鰝€(gè) __low_level_init() 相關(guān)的小實(shí)驗(yàn),在 SDK_2.11.0_MIMXRT1170-EVKboardsevkmimxrt1170demo_appshello_worldcm7iar 例程基礎(chǔ)上(flexspi_nor_debug build),創(chuàng)建一個(gè)包含如下內(nèi)容的 ramfunc_test.c 源文件,并將其添加進(jìn)工程編譯。

ramfunc_test1/3() 函數(shù)放入自定義程序段 CodeQuickAccess,ramfunc_test2/4() 函數(shù)放到默認(rèn) .textrw 段,然后重寫(xiě) __low_level_init() 函數(shù),在 __low_level_init() 函數(shù)里分別調(diào)用 ramfunc_test1/2/3/4(),其中 ramfunc_test1/2() 函數(shù)的調(diào)用在 __iar_data_init3() 前面,ramfunc_test1/2() 函數(shù)的調(diào)用在 __iar_data_init3() 后面。

void?ramfunc_test1(void)?@"CodeQuickAccess"
{
????__NOP();
}

__ramfunc?void?ramfunc_test2(void)
{
????__NOP();
}

void?ramfunc_test3(void)?@"CodeQuickAccess"
{
????__NOP();
}

__ramfunc?void?ramfunc_test4(void)
{
????__NOP();
}

//?重定義此函數(shù),讓?IAR?編譯器使用這個(gè)版本,而不是默認(rèn)版本
int?__low_level_init(void)
{
????extern?void?__iar_data_init3(void);

????ramfunc_test1();
????ramfunc_test2();
????
????//?這里增加?.data/.bss/.textrw?的初始化調(diào)用,
????//??便于區(qū)分?ramfunc_test1/2?和?ramfunc_test3/4?位置
????__iar_data_init3();
????
????ramfunc_test3();
????ramfunc_test4();
????
????return?0;
}

編譯鏈接修改后的測(cè)試工程,查看其映射文件,以及在板子上實(shí)測(cè),得到如下結(jié)果:

    結(jié)論1:放入自定義程序段的函數(shù),無(wú)論其調(diào)用位置在 __iar_data_init3() 之前還是之后,一律被 initialize by copy 忽略,函數(shù)直接鏈接在目標(biāo) RAM 區(qū),函數(shù)重定向無(wú)效;結(jié)論2:放入默認(rèn) .textrw 段的函數(shù),如果其調(diào)用位置在 __iar_data_init3() 之后,能夠被 initialize by copy 作用,函數(shù)重定向生效;結(jié)論3:放入默認(rèn) .textrw 段的函數(shù),如果其調(diào)用位置在 __iar_data_init3() 之前,從映射文件里看其能夠被 initialize by copy 作用,但在板子上實(shí)測(cè),發(fā)現(xiàn)執(zhí)行到該函數(shù)時(shí)返回會(huì)產(chǎn)生總線錯(cuò)誤,因此函數(shù)重定向也是無(wú)效的;
*******************************************************************************
*** PLACEMENT SUMMARY
***
"P1":  place in [from 0x3000'2000 to 0x30fb'ffff] { ro };
"P2":  place in [from 0x2000'0000 to 0x2003'fbff] { rw };
"P8":  place in [from 0x0 to 0x3'ffff] { section CodeQuickAccess };
initialize by copy { rw, section .textrw, section CodeQuickAccess };

  Section              Kind         Address    Size  Object
  -------              ----         -------    ----  ------
"P8":                                           0x8
    CodeQuickAccess    ro code          0x0     0x8  ramfunc_test.o [6]

"P2-P3|P5|P9", part 1 of 2:                     0xc
  RW                            0x2000'0000     0xc  <Block>
      .textrw          inited   0x2000'0004     0x8  ramfunc_test.o [6]

"P1":                                        0x443a
  .text                ro code  0x3000'63d0    0x1a  ramfunc_test.o [6]

*******************************************************************************
*** MODULE SUMMARY
***
    Module                              ro code  rw code  ro data  rw data
    ------                              -------  -------  -------  -------
    ramfunc_test.o                           34        8        8

*******************************************************************************
*** ENTRY LIST
***
    Entry                       Address   Size  Type      Object
    ----                       -------   ----  ----      ------
    ramfunc_test1                   0x1    0x4  Code  Gb  ramfunc_test.o [6]
    ramfunc_test2           0x2000'0005    0x4  Code  Gb  ramfunc_test.o [6]
    ramfunc_test3                   0x5    0x4  Code  Gb  ramfunc_test.o [6]
    ramfunc_test4           0x2000'0009    0x4  Code  Gb  ramfunc_test.o [6]

至此,IAR啟動(dòng)函數(shù)流程及其__low_level_init設(shè)計(jì)對(duì)函數(shù)重定向的影響痞子衡便介紹完畢了,掌聲在哪里~~~

相關(guān)推薦

登錄即可解鎖
  • 海量技術(shù)文章
  • 設(shè)計(jì)資源下載
  • 產(chǎn)業(yè)鏈客戶資源
  • 寫(xiě)文章/發(fā)需求
立即登錄

碩士畢業(yè)于蘇州大學(xué)電子信息學(xué)院,目前就職于恩智浦(NXP)半導(dǎo)體MCU系統(tǒng)部門(mén),擔(dān)任嵌入式系統(tǒng)應(yīng)用工程師。痞子衡會(huì)定期分享嵌入式相關(guān)文章