• 223查看
  • 0回复

[芯片硬件] 浅谈嵌入式MCU软件开发之GNU链接脚本添加用户自定义bss段属性NOLOAD避免生成不必要...

[复制链接]


该用户从未签到

发表于 9-3-2024 21:48:38 | 显示全部楼层 |阅读模式

汽车零部件采购、销售通信录       填写你的培训需求,我们帮你找      招募汽车专业培训老师


内容提要
引言1. 问题复现2. 解决方案2.1 在链接文件中,给新增的用户自定义bss段添加属性NOLOAD2.2 重新编译应用工程,验证结果总结
引言
在开发嵌入式MCU的①bootloader或者②标定程序或者③standby低功耗模式快速唤醒功能时,通常会自定义一些初始化值为零或者没有初始化值的用户数据段(bss)到SRAM中,用作①bootloader和APP之间的信息交互,或者②标定数据存储,或者③低功耗模式与正常模式之间的关键应用程序或者关键外设状态保存(利用掉电不丢失的standby SRAM)。
对于这些在默认应用工程链接文件中添加的用户自定义bss段,若不给其添加不初始化属性,则会在最终的编译结果(elf文件)中生成相应的初始化数据(全“0”),这些初始值对于这些段来说通常是非必要的,因为它们的初始化时用户自己根据实际应用功能需求,在恰当的时机进行。
在开发阶段,通过调试器(debugger)在线下载和调试elf文件时,不会有任何问题。
但是,到了量产阶段,使用应用工程生成的Flash编程文件--S19/HEX/BIN文件时,就会出现地址超界(out of range)的问题(如下图,是一个存在这样问题的S32K144 MCU应用工程的S19文件在生成P&E的U-Cyclone离线编程器的SAP文件时遇到的地址超界问题)。
浅谈嵌入式MCU软件开发之GNU链接脚本添加用户自定义bss段属性NOLOAD避免生成不必要的SRAM地址S19行w1.jpg

PS:编程器(programmer)使用的Flash编程算法不能对目标MCU的SRAM地址进行编程。
下面,本文就结合S32K144的S32DS应用工程介绍使用GNU工具链时,如何通过修改应用工程的链接脚本添加用户自定义bss段属性NOLOAD避免生成不必要的SRAM地址S19行。
1. 问题复现
根据,我之前公众号文章(点击文章标题即可直接跳转阅读)《浅谈嵌入式MCU软件开发之S32K1xx系列MCU启动过程及重映射代码到RAM中运行方法详解》,首先,在S32K144的应用工程链接文件中添加用户自定义未初始化段".usr_bss"并放置到SRAM(m_data_2 region)中,但是不为其设置任何属性,:/* Define output sections */SECTIONS{   .../*other section placements, such as text,data,and bss *//* Uninitialized user data section. */  .user_bss :  {/* This is used by the startup in order to initialize the .bss section. */    . = ALIGN(4);    __USR_BSS_START = .;    __usr_bss_start__ = .;    *(.usr_bss)    *(.usr_bss*)    . = ALIGN(4);    __usr_bss_end__ = .;    __USR_BSS_END = .;  } > m_data_2  .../*other section placements, such as stack,and heap */ }然后,在应用代码,比如main.c中定义一个变量(数组),并使用__attribute__((section (".user_bss")))将其指定到用户自定义未初始化段".usr_bss";在main() 函数中使用该变量以避免编译器将其优化掉:__attribute__((section (".user_bss"))) uint32_t my_bss[100];volatileint exit_code = 0;intmain(void){/* Write your local variable definition here */
uint32_t i = 0;/* other system initailization codes */while(1)    {for(i = 0;i<100;i++)    {      my_bss = i * i;
if(my_bss >10000)                   {                       exit_code = 1;break;                   }    }    }for(;;) {if(exit_code != 0) {break;    }  }return exit_code;这样编译该应用工程,在编译结果的Flash编程文件--S19文件(默认文件名后缀为“.srec”)中就会多一个自定义未初始化段".usr_bss"的初始化数据块(block),地址为:0x2000_0030 ~ 0x2000_1BF(SRAM地址),长度为:0x190(400)字节:
浅谈嵌入式MCU软件开发之GNU链接脚本添加用户自定义bss段属性NOLOAD避免生成不必要的SRAM地址S19行w2.jpg

其地址和长度与工程编译结果map文件中的信息一致:.user_bss0x20000030      0x1900x20000030. = ALIGN (0x4)0x20000030__USR_BSS_START = .0x20000030__usr_bss_start__ = .*(.usr_bss)*(.usr_bss*)0x20000030. = ALIGN (0x4)0x20000030__usr_bss_end__ = .0x20000030__USR_BSS_END = ..user_bss0x20000030      0x190 ./Sources/main.o0x20000030my_bss相应的编译结果存储器占用信息如下:
浅谈嵌入式MCU软件开发之GNU链接脚本添加用户自定义bss段属性NOLOAD避免生成不必要的SRAM地址S19行w3.jpg

2. 解决方案
2.1 在链接文件中,给新增的用户自定义bss段添加属性NOLOAD
为了解决这个问题,需要在应用工程的链接文件中,给新增的用户自定义bss段".usr_bss"添加不初始化属性--关键词为NOLOAD:/* Define output sections */SECTIONS{   .../*other section placements, such as text,data,and bss *//* Uninitialized user data section. */  .user_bss (NOLOAD) :  {/* This is used by the startup in order to initialize the .bss section. */    . = ALIGN(4);    __USR_BSS_START = .;    __usr_bss_start__ = .;    *(.usr_bss)    *(.usr_bss*)    . = ALIGN(4);    __usr_bss_end__ = .;    __USR_BSS_END = .;  } > m_data_2  .../*other section placements, such as stack,and heap */ }Notes: 注意,段名.user_bss和属性配置(NOLOAD)以及冒号“:”之间必须有一个英文输入的空格,且需要保证其中使用的()也是英文输入法输入的,不能是中文输入的(),否则链接时会报语法错误。
2.2 重新编译应用工程,验证结果
然后,在重新编译该应用工程,则可以看到生成的S19文件中,已经没有SRAM地址的数据块了:
浅谈嵌入式MCU软件开发之GNU链接脚本添加用户自定义bss段属性NOLOAD避免生成不必要的SRAM地址S19行w4.jpg

相应的编译结果存储器占用信息如下:
浅谈嵌入式MCU软件开发之GNU链接脚本添加用户自定义bss段属性NOLOAD避免生成不必要的SRAM地址S19行w5.jpg

可以看到,text代码段大小保持不变,data数据段减少了400个字节,但bss数据段增加了400个字节,即真正实现了将用户新增的自定义bss段".usr_bss"放置到了SRAM中,而无需生成和下载其初始化值。
总结
关于这一问题的解决方法,不同的编译器工具链添加不初始化属性的方法和所使用的关键词不同,比如,在S08和S12(X)以及MagniV S12Z系列MCU使用的CodeWarrior IDE(v6.3, v5.1/2和v10.6/7或者v11.1/2)中,就是修改其应用工程的prm链接文件,使用的关键词为“NO_INIT”(如下为S12ZVM128的prm链接文件示例供参考):SEGMENTS  /* Here all RAM/ROM areas of the device are listed. Used in PLACEMENT below. */
/* Register space  *//*    IO_SEG        = PAGED         0x000000 TO   0x000FFF; intentionally not defined */
/* RAM */      RAM           = NO_INIT  0x001000 TO 0x002FFF;
/* EEPROM */      EEPROM        = READ_ONLY   0x100000 TO 0x1001FF;
/* non-paged FLASHs */      ROM           = READ_ONLY   0xFE0000 TO 0xFFFDFF;/*   VECTORS       = READ_ONLY     0xFFFE00 TO   0xFFFFFF; intentionally not defined: used for VECTOR commands below */   //OSVECTORS      = READ_ONLY     0xFFFE10 TO   0xFFFFFF;   /* OSEK interrupt vectors (use your vector.o) */END更多关于S32DS IDE所使用的的GNU工具链的链接文件和CodeWarrior IDE工具链的prm链接文件的使用,请参考如下公众号文章(点击文章标题即可直接跳转阅读):
《浅谈嵌入式MCU软件开发之S32K1xx系列MCU启动过程及重映射代码到RAM中运行方法详解》;《浅谈嵌入式MCU软件开发之startup过程详解(在CodeWarrior 5.1 中实现RAM自定义初始化)》;《CodeWarrior IDE使用tips之prm链接文件详解(自定义存储器分区以及自定义RAM数据初始化与在RAM中运行函数)》;《CodeWarrior IDE使用Tips之利用prm链接文件实现储存器数据填充和代码编译结果CRC校验和自动生成详解》;《CodeWarrior与S32DS IDE使用 Tips之如何在应用工程中保留定义但未使用的全局常量、变量(用于参数标定)》;
   

快速发帖

您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|手机版|小黑屋|Archiver|汽车工程师之家 ( 渝ICP备18012993号-1 )

GMT+8, 1-2-2025 08:03 , Processed in 0.257009 second(s), 31 queries .

Powered by Discuz! X3.5

© 2001-2013 Comsenz Inc.