1. 程式人生 > >IAR一些破事兒

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 = 8size = ICFEDIT_size_cstack。block的屬性以逗號分割。section屬於最底層的單元,可以有修飾,例如模板中的readonlyreadwrite。注意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一致,輸出結果與預期一致,一切如想象中順利。