DSP

IAR一些破事儿

2019-07-13 18:38发布

ICF

参考文章

① IAR 链接 ② EWARM_DevelopmentGuide.ENU.pdf

概念

ICF是链接脚本文件后缀名,类似于gcc的lds和ADI SHARC DSP的ldf。它们都是用于Linker组成生成可执行文件的。 ICF文件模板存放于在IAR安装目录。以笔者使用的STM32F103VC为例。它的ICF存放路径如下: D:Program FilesIAR SystemsEmbedded Workbench 7.5armconfiglinkerSTstm32f103xC.icf linker目录之下,其实还有很多目录,对应于不同公司。可见很多公司都基于Cortex-M开发了自己芯片。

内容

/*###ICF### Section handled by ICF editor, don't touch! / /-Editor annotation file-/ /* IcfEditorFile="TOOLKIT_DIRconfigideIcfEditorcortex_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 ", __FUNCTION__, __LINE__); } void CallBack_B(void) { printf("%s-%04d ", __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 ", (void *)pBegin); printf("mysection end = %p ", (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一致,输出结果与预期一致,一切如想象中顺利。