IAR一些破事兒
ICF
參考文章
② EWARM_DevelopmentGuide.ENU.pdf
概念
ICF是連結指令碼檔案字尾名,類似於gcc的lds和ADI SHARC DSP的ldf。它們都是用於Linker組成生成可執行檔案的。
ICF檔案模板存放於在IAR安裝目錄。以筆者使用的STM32F103VC為例。它的ICF存放路徑如下:
D:\Program Files\IAR Systems\Embedded Workbench 7.5\arm\config\linker\ST\stm32f103xC.icf
linker
目錄之下,其實還有很多目錄,對應於不同公司。可見很多公司都基於Cortex-M
內容
/*###ICF### Section handled by ICF editor, don't touch! /
/-Editor annotation file-/
/* IcfEditorFile="TOOLKIT_DIR\config\ide\IcfEditor\cortex_v1_0.xml" */
/-Specials-/
define symbol ICFEDIT_intvec_start = 0x08000000;
/-Memory Regions-/
define symbol ICFEDIT_region_ROM_start = 0x08000000;
define symbol ICFEDIT_region_ROM_end = 0x0803FFFF;
define symbol ICFEDIT_region_RAM_start = 0x20000000;
define symbol ICFEDIT_region_RAM_end = 0x2000BFFF;
/-Sizes-/
define symbol ICFEDIT_size_cstack = 0x1000;
define symbol ICFEDIT_size_heap = 0x1000;
/ End of ICF editor section. ###ICF###*/
define memory mem with size = 4G;
define region ROM_region = mem:[from ICFEDIT_region_ROM_start to ICFEDIT_region_ROM_end];
define region RAM_region = mem:[from ICFEDIT_region_RAM_start to ICFEDIT_region_RAM_end];
define block CSTACK with alignment = 8, size = ICFEDIT_size_cstack { };
define block HEAP with alignment = 8, size = ICFEDIT_size_heap { };
initialize by copy { readwrite };
do not initialize { section .noinit };
place at address mem:ICFEDIT_intvec_start { readonly section .intvec };
place in ROM_region { readonly };
place in RAM_region { readwrite,
block CSTACK, block HEAP };
模板定義:
- ROM地址空間、大小和sections
- RAM地址空間、大小和sections
- 中斷向量表地址空間
- 堆地址空間和大小
- 棧地址空間和大小
需要重點講解的知識點是。
region, block和section三個概念。region是一個地址空間,可以位於RAM,也可以位於ROM。它是最頂層的範圍(必須指定地址空間)。block可以理解為section的集合,它可以有自己的屬性,例如模板中的with alignment = 8
和size = ICFEDIT_size_cstack
。block的屬性以逗號分割。section屬於最底層的單元,可以有修飾,例如模板中的readonly
和readwrite
。注意block和section都直接存放於region。section可以存放於block。
模板中提到的/*###ICF### Section handled by ICF editor, don't touch! /
和/ End of ICF editor section. ###ICF###*/
之前的內容不要更改。這一段有IAR內嵌編輯器維護。使用方式如下:
Project => Options => Config => Edit
也就是指定Project ICF的地方。
用例
介紹用例之前,有個明顯疑問是“為什麼需要改動ICF?”。每個專案都有每個專案的需求。
例如,建榮的藍芽晶片(8051核心,並非ARM核,僅用於說明改動ICF的動機)採用bank方式存放韌體。一個韌體可能存放於多個bank。出於效能考慮,希望同一個模組儘可能存放於同一個bank,減少bank切換次數,達到優化效能的目的。
例如,有一組初始化函式,可以存放用同同一個section。這樣可能迴圈呼叫,比較便於程式設計。
例如,bootloader可以通過在固定記憶體區域存放一些引數(甚至是函式),app啟動之後再固定位置獲取資訊。
如此種種,自定義ICF是有意義的。
...
define block HEAP with alignment = 8, size = __ICFEDIT_size_heap__ { };
define symbol __ICFEDIT_size_myblock__ = 0x200;
define block MYBLOCK with alignment = 4, size = __ICFEDIT_size_myblock__ {
readonly section mysection
};
initialize by copy { readwrite };
...
place in ROM_region { readonly, block MYBLOCK};
...
本ICF中,筆者定義了一個block和一個section。自定義的block存放於模板中的ROM_region。自定義的section存放於自定義的block當中。
void CallBack_A(void)
{
printf("%s-%04d\n", __FUNCTION__, __LINE__);
}
void CallBack_B(void)
{
printf("%s-%04d\n", __FUNCTION__, __LINE__);
}
typedef void (*plugin_t)(void);
#define PLUGIN(i, f) \
__root const plugin_t Plugin_##i @ "mysection" = f;
PLUGIN(A, CallBack_A);
PLUGIN(B, CallBack_B);
引用section的語法注意。本處使用@ mysection
方式的目的是為了巨集定義方便。其實還有其他方式的。例如#pragma location="mysection"
。
void main(void)
{
...
plugin_t p = NULL;
uint32_t pBegin = 0;
uint32_t pEnd = 0;
...
#pragma section ="mysection"
pBegin = (uint32_t)__section_begin("mysection");
pEnd = (uint32_t)__section_end("mysection");
printf("mysection begin = %p\n", (void *)pBegin);
printf("mysection end = %p\n", (void *)pEnd);
while(pBegin != pEnd)
{
p = *((plugin_t *)pBegin);
p();
pBegin += sizeof(plugin_t);
}
...
}
section可以通過編譯器內建巨集__section_begin
和__section_end
來定位。這兩個內建巨集都是void *
的型別。使用的時候,需要進行強制轉換還原。另外,在C語言角度需要注意__section_begin
是指向變數自身的地址,而使用的是內容。所以需要類似進行p = *((plugin_t *)pBegin);
的強制轉換。
連線的結果可以在MAP檔案確認一下。
...
Plugin_A 0x0805580c 0x4 Data Gb main.o [1]
Plugin_B 0x08055810 0x4 Data Gb main.o [1]
...
MYBLOCK 0x0805580c 0x200 <Block>
mysection 0x0805580c 0x8 <Block>
mysection const 0x0805580c 0x4 main.o [1]
mysection const 0x08055810 0x4 main.o [1]
MYBLOCK const 0x08055814 0x1f8 <Block tail>
...
可見,一切如想象中順利。再確認一下輸出。
mysection begin = 805580c
mysection end = 8055814
CallBack_A-0359
CallBack_B-0364
可見,列印的地址805580c和MAP一致,輸出結果與預期一致,一切如想象中順利。