IAR Embedded Workbench中的初始化策略
Technical Note
Architectures:
All
Component:
linker
Updated:
2023/8/10 6:27
之前的文章“IAR Embedded Workbench中的MCU启动过程”介绍了IAR Embedded Workbench中的MCU启动过程:其中非常重要的一步是存储在RAM中的全局和静态变量的初始化。
本文主要介绍IAR Embedded Workbench中的初始化策略。
存储在RAM中的全局和静态变量的初始化
初始化初始值为0的存储在RAM中的全局和静态变量(比如 int i = 0;):
初始化初始值为非0的存储在RAM中的全局和静态变量(比如 int i = 1;),对应的初始值(Initialize variables)从相应的ROM拷贝到对应的RAM:
IAR Embedded Workbench中的初始化策略
initialize by copy
在ICF文件添加initialize by copy { readwrite };命令之后,Linker会自动添加对应的initializers,并在启动代码里面自动完成对应的初始化操作:
initialize by copy { readwrite };
MAP文件中的INIT TABLE会列出对应的初始化信息:其中初始值为0的全局和静态变量会通过__iar_zero_init3函数进行初始化:初始化的时候__iar_zero_init3函数会对相关RAM区域进行写0操作,完成对初始值为0的全局和静态变量的初始化;初始值为非0的全局和静态变量会通过__iar_copy_init3函数进行初始化:对应的Initializer bytes放到ROM,初始化的时候__iar_copy_init3函数会把ROM中的Initializer bytes拷贝到RAM中,完成对初始值为非0的全局和静态变量的初始化:
注意:initialize by copy命令需要启动代码里面调用__iar_program_start进行初始化。
在ICF文件添加initialize by copy { readwrite };命令之后,Linker会自动添加对应的initializers,并在启动代码里面自动完成对应的初始化操作,对应的__iar_zero_init3函数和__iar_copy_init3函数都是通过__iar_program_start调用,所以需要使用__iar_program_start进行初始化:
THUMB
PUBWEAK Reset_Handler
SECTION .text:CODE:REORDER:NOROOT(2)
Reset_Handler
LDR R0, =SystemInit
BLX R0
LDR R0, =__iar_program_start
BX R0
如果没有调用__iar_program_start进行初始化,link的时候会提示类似下面的错误(__iar_data_init3函数会先后调用__iar_zero_init3函数和__iar_copy_init3函数):
initialize manually
如果代码里面没有使用__iar_program_start进行初始化,就不能使用initialize by copy初始化策略对初始值为非0的全局和静态变量进行自动初始化。可以使用initialize manually初始化策略对初始值为非0的全局和静态变量进行手动初始化(当然,代码里面使用了__iar_program_start进行初始化,也是可以使用initialize manually初始化策略对初始值为非0的全局和静态变量进行手动初始化的)。
如下所示:ICF文件中使用initialize manually对.data section进行手动初始化:.data section包含的是初始值为非0的全局和静态变量,放到MY_DATA block中,并把对应的MY_DATA block放到RAM;.data_init section包含的是初始值为非0的全局和静态变量的Initializer bytes,放到MY_DATA_INIT block中, 并把对应的MY_DATA_INIT block放到ROM:
define block MY_DATA { section .data };
define block MY_DATA_INIT { section .data_init };
initialize manually { section .data };
place in IRAM_region { block MY_DATA };
place in IROM_region { block MY_DATA_INIT };
然后需要在代码里面添加对应的手动初始化.data section的代码:其中#pragma section命令用于定义对应的section名字,然后对应的section操作符__section_begin, __section_end和section_size就可以分别获取对应section的开始地址,结束地址和大小。
#pragma section = "MY_DATA"
#pragma section = "MY_DATA_INIT"
static void MyDataSectionInit(void)
{
char * from = __section_begin("MY_DATA_INIT");
char * to = __section_begin("MY_DATA");
memcpy(to, from, __section_size("MY_DATA"));
}
int main(void)
{
MyDataSectionInit();
…
}
MAP文件中.data section放到MY_DATA block中,放置到RAM,对应的.data_init section(Initializer bytes)放到MY_DATA_INIT block中,放置到ROM:
do not initialize
如果代码里面没有使用__iar_program_start进行初始化,就不能使用initialize by copy初始化策略对初始值为0的全局和静态变量进行自动初始化。可以使用do not initialize初始化策略对初始值为0的全局和静态变量进行手动初始化(当然,代码里面使用了__iar_program_start进行初始化,也是可以使用do not initialize初始化策略对初始值为0的全局和静态变量进行手动初始化的)。
如下所示,ICF文件中使用do not initialize对.bss section进行手动初始化:.bss section包含的是初始值为0的全局和静态变量,放到MY_BSS block中,并把对应的MY_BSS block放到RAM:
define block MY_BSS { section .bss };
do not initialize { section .bss };
place in IRAM_region { block MY_BSS };
然后需要在代码里面添加对应的手动初始化.bss section的代码:其中#pragma section命令用于定义对应的section名字,然后对应的section操作符__section_begin, __section_end和section_size就可以分别获得对应section的开始地址,结束地址和大小。
#pragma section = "MY_BSS"
static void MyBssSectionInit(void)
{
char * to = __section_begin("MY_BSS");
memset(to, 0, __section_size("MY_BSS"));
}
int main(void)
{
MyBssSectionInit();
…
}
MAP文件中.bss section放到MY_BSS block中,放置到RAM:
另外对于使用了__no_init的静态和全局变量,linker默认会放置到.noinit section,可以在ICF文件使用do not initialize命令,对应代码中不能对.noinit section进行初始化,这样.noinit section的内容在启动过程中不会有任何初始化操作。
do not initialize { section .noinit };
注意事项
注意:如果静态和全局变量放到了用户定义的section,并且手动明确地赋初始值0(比如 int i = 0;),默认需要对这类变量进行copy初始化,而不是zero初始化。
如下所示,静态变量TaskLedRedCounter放到了用户定义的MY_SECTION section里面,并且手动明确地赋初始值0:
#pragma location = "MY_SECTION"
static uint32_t TaskLedRedCounter = 0;
MAP文件中静态变量TaskLedRedCounter的Kind是inited,而不是uninit;并且INIT TABLE里面生成了对应的Initializer bytes:
这个跟通常的初始值为0的静态和全局变量的处理有点不一样,由于对应变量的Kind是inited,所以需要使用initialize manually初始化策略(跟初始值为非0的全局和静态变量进行手动初始化一样):
如下所示:ICF文件中使用initialize manually对MY_SECTION section进行手动初始化:
define block MY_DATA { section .data, section MY_SECTION };
define block MY_DATA_INIT { section .data_init, section MY_SECTION_init };
initialize manually { section .data, section MY_SECTION };
place in IRAM_region { block MY_DATA };
place in IROM_region { block MY_DATA_INIT };
MAP文件中MY_SECTION section放到MY_DATA block中,放置到RAM,对应的Initializer bytes放到MY_DATA_INIT block中,放置到ROM:
如果想对上面的变量使用do not initialize初始化策略(跟初始值为0的全局和静态变量进行手动初始化一样),需要使用编译器选项--do_explicit_zero_opt_in_named_sections:
MAP文件中静态变量TaskLedRedCounter的Kind是zero,并且INIT TABLE只有__iar_zero_init3函数:
在ICF文件中像处理正常的.bss section一样:把MY_SECTION section放到MY_BSS section,同时使用do not initialize命令:
define block MY_BSS { section .bss, section MY_SECTION };
do not initialize { section .bss, section MY_SECTION };
place in IRAM_region { block MY_BSS };
MAP文件中静态变量TaskLedRedCounter的Kind是期望的uninit:
更多关于Initialization decisions的信息,可以通过使能Linker > List > Generate log file > Initialization decisions选项来查看对应log文件中的信息:
总结
本文主要介绍了IAR Embedded Workbench中的初始化策略:initialize by copy, initialize manually和do not initialize,用户可以根据对应的需求来灵活合理地选择初始化策略:
-
如果对应变量在启动过程中不能被初始化,需要使用do not initialize初始化策略,并且代码里面不能对相关变量进行任何初始化;
-
如果启动代码使用了__iar_program_start:
-
如果没有特别的初始化需求,使用initialize by copy初始化策略
-
如果有特别的初始化需求,参考下面启动代码没有使用__iar_program_start的情况
-
-
如果启动代码没有使用__iar_program_start:
-
对应变量的初始值非0,使用initialize manually,并且需要添加对应的代码把ROM中的Initializer bytes拷贝到对应的RAM区域
-
通常情况下,对应变量的初始值为0,使用do not initialize,并且需要添加对应的代码对相关RAM区域进行写0操作
-
如果变量放到了用户定义的section,并且手动明确地赋初始值0默认需要对这类变量进行copy初始化(使用initialize by copy初始化策略);需要使用编译器选项--do_explicit_zero_opt_in_named_sections实现zero初始化(使用do not initialize初始化策略)
-
参考文献:
-
IAR C/C++ Development Guide (initialize directive, Manual initialization)