IAR一些破事儿

ICF

参考文章

① IAR 链接

② 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  
  mysection                  0x0805580c      0x8  
    mysection       const    0x0805580c      0x4  main.o [1]
    mysection       const    0x08055810      0x4  main.o [1]
  MYBLOCK           const    0x08055814    0x1f8  
...

可见,一切如想象中顺利。再确认一下输出。

mysection begin = 805580c
mysection end   = 8055814
CallBack_A-0359
CallBack_B-0364

可见,打印的地址805580c和MAP一致,输出结果与预期一致,一切如想象中顺利。

你可能感兴趣的:(IAR一些破事儿)