基于 IAR Embedded Workbench 的自研 MCU 芯片软件函数与变量内存布局优化精控方法
在嵌入式软件开发领域,MCU芯片软件的架构设计与内存布局的精细规划对系统性能和稳定性起着关键作用。本文档聚焦于IAR Embedded Workbench环境下,为自研MCU芯片软件提供了一套详尽的函数和变量指定section放置方法与操作流程,兼具过程记录与详细说明,旨在打造一份实用的参考指南,助力开发者精准掌控程序的内存分布与执行逻辑。文档涵盖从默认section表的介绍,到多种放置手段的阐释,以及实际配置示例的展示,为后续的开发工作奠定坚实基础。
IAR Embedded Workbench作为一款广受认可的嵌入式开发工具,具备丰富的功能与灵活的配置选项。在该环境下,软件开发者可巧妙运用多种方法,将函数和变量精准放置于指定的section中。这一操作对于优化程序的内存使用效率、提升系统响应速度以及增强代码的可维护性具有重要意义。例如,通过将特定的代码或数据放置在合适的内存区域,可以充分利用MCU芯片的硬件特性,实现更高效的缓存利用、减少内存访问延迟等效果。
文档深入浅出地讲解了多种放置方式,包括使用@操作符、#pragma location命令、GCC风格的attribute属性以及#pragma default_variable_attributes和#pragma default_function_attributes命令等,开发者可根据实际需求灵活选择。同时,还提供了诸如as32x601_rom.icf、Port_MemMap.h和Port.c等实际配置示例,涵盖了从内存区域定义、section分配到函数与变量属性设置的完整流程,为开发者提供了直观且易于实践的参考。
默认section表
IAR Embedded Workbench中有很多默认的section用于放置对应的变量和函数:
Section | 说明 |
.bss | 存放零初始化的静态和全局变量 |
CSTACK | 存放C或C++程序使用的堆栈 |
.data | 存放静态和全局初始化变量 |
.data_init | 当使用链接器指令初始化时,存放.data段的初始值 |
.exc.text | 存放与异常相关的代码 |
HEAP | 存放用于动态分配数据的堆 |
__iar_tls.$$DATA | 存放TLS变量的初始值 |
.iar.dynexit | 存放退出时调用的表 |
.init_array | 存放一张动态初始化函数表 |
.intvec | 存放复位向量表 |
IRQ_STACK | 存放中断请求,IRQ和异常的堆栈 |
.noinit | 存放用__no_init指示的静态和全局变量 |
.preinit_array | 存放一张动态预初始化函数表 |
.prepreinit_array | 存放一预处理动态预初始化函数表 |
.rodata | 存放常量数据,const修饰的变量 |
.text | 存放程序代码 |
.textrw | 存放以__ramfunc声明的程序代码 |
.textrw_init | 存放以.textrw声明部分的初始化程序 |
Veneer$$CMSE | 存放安全网关虚表 |
除了用于您的应用程序的ELF部分之外,这些工具还出于多种目的使用许多其他ELF段:
- 以.debug开头的段通常包含DWARF格式的调试信息。
- 以.iar.debug开头的段包含IAR格式的补充调试信息
- 以.comment开头的段包含用于构建文件的工具和命令行
- 以.rel或.rela开头的段包含ELF重定位信息
- 以.symtab开头的段包含文件的符号表
- 以.strtab开头的段包含符号表中符号的名称
- 以.shstrtab开头的段包含各段的名称。
将变量放到指定的section
使用@操作符
可以使用 @ 将变量放到指定的section:
staticuint32_t TaskCounter @".mcal_const_cfg" = 1;
使用 #pragma location 命令
可以使用 #pragma location命令将变量放到指定的section:
#pragma location = ".mcal_const_cfg"staticuint32_t TaskCounter = 1;
使用 GCC 风格的属性 attribute ((section ))
可以使用 GCC 风格的属性 attribute ((section ))将变量放到指定的section:
staticuint32_t TaskCounter __attribute__ ((section (".mcal_const_cfg"))) = 1;
使用 #pragma default_variable_attributes 命令
上面的方法可以将单个变量放到指定的section,如果需要将多个变量放到指定的section,上面的方法会显得有点繁琐。可以使用 #pragma default_variable_attributes 命令将多个变量放到指定的section:
#pragma default_variable_attributes = @ ".mcal_const_cfg"staticuint32_t TaskCounter = 1; staticuint32_t TaskLedRedCounter = 2; #pragma default_variable_attributes =
将函数放到指定的section
使用@操作符
可以使用 @ 将函数放到指定的section:
voidStartTaskLedRed(void *argument) @ ".mcal_text";
使用 #pragma location 命令
可以使用 #pragma location命令将函数放到指定的section:
#pragma location = ".mcal_text"voidStartTaskLedRed(void *argument);
使用 GCC 风格的属性 attribute ((section ))
可以使用 GCC 风格的属性 attribute ((section ))将函数放到指定的section:
voidStartTaskLedRed(void *argument) __attribute__((section (".mcal_text")));
使用 #pragma default_variable_attributes 命令
上面的方法可以将单个函数放到指定的section,如果需要将多个函数放到指定的section,上面的方法会显得有点繁琐。可以使用 #pragma default_function_attributes命令将多个函数放到指定的section:
#pragma default_function_attributes = @ ".mcal_text"voidStartTaskLedRed(void *argument); voidStartTaskLedGreen(void *argument); voidStartTaskLedBlue(void *argument); #pragma default_function_attributes =
使用示例
as32x601_rom.icf
/****************************************************************************** * FILE VERSION ******************************************************************************/ define exported symbol _link_file_version_2 = 1; /****************************************************************************** * SPECIALS *****************************************************************************//****************************************************************************** * MEMORY REGIONS *****************************************************************************/ define symbol __ICFEDIT_region_FLASH_start__ = 0x10000000; define symbol __ICFEDIT_region_FLASH_end__ = 0x11FFFFFF; define symbol __ICFEDIT_region_SRAM0_start__ = 0x20000000; define symbol __ICFEDIT_region_SRAM0_end__ = 0x2001FFFF; define symbol __ICFEDIT_region_SRAM1_start__ = 0x20020000; define symbol __ICFEDIT_region_SRAM1_end__ = 0x2003FFFF; define symbol __ICFEDIT_region_SRAM2_start__ = 0x20040000; define symbol __ICFEDIT_region_SRAM2_end__ = 0x2005FFFF; define symbol __ICFEDIT_region_SRAM3_start__ = 0x20060000; define symbol __ICFEDIT_region_SRAM3_end__ = 0x2007FFFF; /****************************************************************************** * SIZES *****************************************************************************/ define symbol __ICFEDIT_size_cstack__ = 0x2000; define symbol __ICFEDIT_size_proc_stack__ = 0x0; define symbol __ICFEDIT_size_heap__ = 0x2000; /****************************************************************************** * BUILD FOR ROM *****************************************************************************/ keep symbol __iar_cstart_init_gp; define memory mem with size = 4G; define region ROM_region = mem:[from __ICFEDIT_region_FLASH_start__ to __ICFEDIT_region_FLASH_end__]; define region RAMCODE_region = mem:[from __ICFEDIT_region_SRAM0_start__ to __ICFEDIT_region_SRAM0_end__]; define region RAM_region = mem:[from __ICFEDIT_region_SRAM1_start__ to __ICFEDIT_region_SRAM1_end__] | mem:[from __ICFEDIT_region_SRAM2_start__ to __ICFEDIT_region_SRAM2_end__]; initialize by copy { readwrite }; do not initialize { section .noinit }; define block CSTACK with alignment = 16, size = CSTACK_SIZE { }; define block HEAP with alignment = 16, size = HEAP_SIZE { }; define block RW_DATA { rw section .data}; define block RW_DATA_INIT { ro section .data_init}; define block RW_BSS {rw section .bss}; define block RW_DATA_ALL with static base GPREL { block RW_DATA, block RW_BSS }; "STARTUP" : place at start of ROM_region { readonly section .init }; place in ROM_region { readonly, block RW_DATA_INIT }; place in ROM_region { readonly section .text, section .mcal_text, section .access_code_rom}; place in ROM_region { readonly section .rodata, section .mcal_const_cfg, section .mcal_const, section .mcal_const_no_cacheable}; place in RAMCODE_region { readwrite section .text, section .ramcode, block RW_DATA_ALL }; place in RAM_region { readwrite, block CSTACK, block HEAP }; place in RAM_region { section .mcal_data, section .dma_dest_buffer, section .mcal_shared_data }; place in RAM_region { section .mcal_bss, section .mcal_bss_no_cacheable, section .dma_dest_buffer_bss, section .mcal_shared_bss };
第13、14行:定义FLASH,起始地址和结束地址。 第41行:定义了ROM_region区域,起始地址和结束地址。 第63行:定义一个名为 ROM_region 的内存区域,并将三个只读代码段 .text、.mcal_text 和 .access_code_rom 放置在这个区域内。。
Port_MemMap.h
#define MEMMAP_MISSMATCH_CHECKER#if defined (_IAR_C_AS32x601_)#ifdef PORT_START_SEC_CODE#undef PORT_START_SEC_CODE#undef MEMMAP_MISSMATCH_CHECKER#pragma default_function_attributes = @ ".mcal_text"#endif#ifdef PORT_STOP_SEC_CODE#undef PORT_STOP_SEC_CODE#undef MEMMAP_MISSMATCH_CHECKER#pragma default_function_attributes =#endif#endif#ifdef MEMMAP_MISSMATCH_CHECKER#error"MemMap.h, No valid section define found."#endif
第1行:定义了一个宏 MEMMAP_MISSMATCH_CHECKER,用于检查包含的正确的符号。 第3行:定义了一个ifdef,当定义了 _IAR_C_AS32x601_ 时,会执行下面的代码。 第5行:定义了一个ifdef,当定义了 PORT_START_SEC_CODE 时,会执行下面的代码。 第11行:定义了一个ifdef,当定义了 PORT_STOP_SEC_CODE 时,会执行下面的代码。 第19行:定义了一个ifdef,当定义了 MEMMAP_MISSMATCH_CHECKER 时,说明输入定义错误,将会抛出一个错误。
Port.c
#define PORT_START_SEC_CODE#include"Port_MemMap.h" FUNC(void, PORT_CODE) Port_Init(P2CONST(Port_ConfigType, AUTOMATIC, PORT_APPL_CONST) ConfigPtr) { #if (PORT_ENABLE_DEV_ERROR_DETECT == STD_ON)/* When PostBuild is used and #(Variants) > 1, the input parameter 'ConfigPtr' is mandatory to be different than NULL_PTR. * In case of error, return immediately and report DET errors. */#if (PORT_PRECOMPILE_SUPPORT == STD_ON)if (NULL_PTR != ConfigPtr) { #elseif (NULL_PTR == ConfigPtr) { #endif/* (PORT_PRECOMPILE_SUPPORT == STD_ON) *//* If development error detection for the Port module is enabled: * The function shall raise the error PORT_E_INIT_FAILED if the parameter ConfigPtr is Null Pointer.*/ (void)Det_ReportError((uint16)PORT_MODULE_ID, PORT_INSTANCE_ID, PORT_INIT_ID, PORT_E_INIT_FAILED); } else { #endif/* (PORT_ENABLE_DEV_ERROR_DETECT == STD_ON) */#if (PORT_PRECOMPILE_SUPPORT == STD_ON) l_PortConfig_ptr = &Port_PreCompileConfig_st; /* Avoid compiler warning */ (void)ConfigPtr; #else/* (PORT_PRECOMPILE_SUPPORT == STD_OFF) */ l_PortConfig_ptr = ConfigPtr; #endif/* (PORT_PRECOMPILE_SUPPORT == STD_ON) *//* Initializes the Port driver with the given configuration */ Port_LLDriver_Init(l_PortConfig_ptr); #if (PORT_ENABLE_DEV_ERROR_DETECT == STD_ON) } #endif/* (PORT_ENABLE_DEV_ERROR_DETECT == STD_ON) */ } #define PORT_STOP_SEC_CODE#include"Port_MemMap.h"
第1行:定义了一个宏 PORT_START_SEC_CODE,将会执行#pragma default_function_attributes = @ ".mcal_text",会将函数放置在.mcal_text区域。 第32行:定义了一个宏 PORT_STOP_SEC_CODE,会结束函数的默认属性设置。