当前位置: 首页 > news >正文

基于 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,会结束函数的默认属性设置。

效果示例

相关文章:

  • 临床回归分析及AI推理
  • Vue3 + TypeScript 实现 PC 端鼠标横向拖动滚动
  • ModStartCMS v9.4.0 AI
  • 【Linux调整FTP端口】
  • 【人脸去遮挡前沿】三阶段级联引导学习如何突破真实场景遮挡难题?
  • java代码混淆
  • 国产化海光C86架构服务器安装windows实录
  • Elasticsearch:没有 “AG” 的 RAG?
  • 【AI论文】CipherBank:通过密码学挑战探索LLM推理能力的边界
  • 高级测试工程师 的面试题汇总
  • STM32printf重定向到串口含armcc和gcc两种方案
  • Cline原理分析-prompt
  • 【AI平台】n8n入门5:创建MCP服务,及vscode调用MCP测试
  • 解决在Mac上无法使用“ll”命令
  • 《系统分析师-第三阶段—总结(八)》
  • 免费在Colab运行Qwen3-0.6B——轻量高性能实战
  • 瑞芯微芯片算法开发初步实践
  • Seaborn
  • 基于SpringBoot的旅游网站的设计与实现
  • GEO vs SEO:从搜索引擎到生成引擎的优化新思路
  • 圆桌|如何应对特朗普政府的关税霸凌?一种联合国视角的思考
  • 孕妇乘坐高铁突发临产,广西铁路部门协助送医平安产子
  • 锦江酒店:第一季度营业收入约29.42亿元,境内酒店出租率同比增长
  • 【社论】人工智能,年轻的事业
  • 外交部亚洲司司长刘劲松向菲方严肃交涉
  • 国家发改委下达今年第二批810亿超长期特别国债资金,支持消费品以旧换新