Keil MDK下如何设置非零初始化变量 - 基于Arm Compiler 6
Posted zhzht19861011
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Keil MDK下如何设置非零初始化变量 - 基于Arm Compiler 6相关的知识,希望对你有一定的参考价值。
1. 编译器的自作主张
ANSI C标准规定:未明确初始化的静态数据(static data),会被初始化为0。
因此,默认情况下,编译器会将零初始化(zero-initialized)和未初始化(uninitialized)的数据放入相同数据段(data section),在运行时由C库的初始化代码将这些数据段域填充数字0。数据段可以是RW数据段(.data),也可以是ZI数据段(.bss)。
定义一个全局变量
int test1 = 0;
,则变量test1
是零初始化变量;定义一个全局变量
int test2;
,则变量test2
是未初始化变量;
运行时
一般发生在进入main
函数之前,会执行编译器生成的一些列库函数,完成数字0填充。
有时候,在热重启后,对某些变量,我们不希望编译器填充数字0。比如保持这些变量的值,以便快速恢复现场。
这就涉及到今天的主题,如何防止未初始化的变量被初始化为0。
在此之前,先来了解下专有名词段(section)。段是链接器的专有名词,可以理解为相同属性数据的集合。比如RO数据段一般存放编译后的代码和常量,ZI数据段一般存放零初始化的变量。
2. 升级到 Arm® Compiler 6,遇到了新的问题
在我的CSDN博客《Keil MDK下如何设置非零初始化变量》(地址:https://freertos.blog.csdn.net/article/details/8780837)这篇文章中,介绍了使用Arm® Compiler 5工具,如何防止未初始化的变量被初始化为0。当我将编译器升级到版本6之后,这部分代码产生了编译警告,提示忽略了未知的属性zero_init
:
…/lwip_port/source/net_analyze.c(17): warning: unknown attribute ‘zero_init’ ignored [-Wunknown-attributes]
uint32_t phy_link_init_flag _attribute_((zero_init));
这是因为编译器版本升级带来的兼容性问题。
3. 解决方案
打开Keil编译器的帮助文件,找到ARM Compiler 6 User's Guides
- Migration and Compatibility Guide
。
这是编译器的迁移和兼容性指南。
我所遇到的问题,可以在这份指南的Language extension compatibility: attributes
(语言扩展兼容性:属性)一节中找到解决方案:
Arm Compiler 5 属性 | Arm Compiler 6 属性 | 描述 |
---|---|---|
__attribute__((at(address))) | __attribute__((section(".ARM.__at_address"))) | Arm Compiler 6 中的 armlink 仍然支持以 .ARM.__at_address 的形式放置段 |
__attribute__((at(address), zero_init)) | __attribute__((section(".bss.ARM.__at_address"))) | Arm Compiler 6 中的 armlink 支持以 .bss.ARM.__at_address 的形式放置零初始化段。 .bss 前缀区分大小写,并且必须全部小写。 |
__attribute__((section(name), zero_init)) | __attribute__((section(".bss.name"))) | name 是你选择的名字。 .bss 前缀区分大小写,并且必须全部小写。 |
__attribute__((zero_init)) | 不支持 默认将零初始化变量放在 .bss 段。 | 如果变量具有初始值设定项,则 Arm Compiler 5 会生成错误。 否则,它将零初始化变量放在 .bss 段。 |
虽然有了解决方案,但具体实施仍然困难,因为解决方案描述简单,也没有例程。
本文是对解决方案的一个补充,将描述如何实施。
4. 实施的一般步骤
防止未初始化的变量被初始化为0只需要:将未初始化的变量放入一个特殊段。特殊段需要满足:
- 这是一个ZI段;
- 包含这个ZI段的可执行域(region)要具有
UNINIT
属性。
这里有两个新词:可执行域和UNINIT
属性。
可执行域是链接器的专有名词,可以包含很多的数据段。
UNINIT
属性用于创建包含未初始化数据的可执行域,该数据域的特定段不会被初始化为0。UNINIT
只对ZI数据段有效。比如下面的ER_RW
可执行域,只有ZI部分的数据不会被初始化。LR 0x8000 ER_RO +0 *(+RO) ER_RW 0x10000 UNINIT *(+RW,+ZI)
有了上面的基础知识,我们来看一下Arm® Compiler 5 和Arm® Compiler 6 是如何防止未初始化变量被初始化为0的。
5. Arm® Compiler 5 如何防止未初始化变量被初始化为0
-
定义变量时,使用编译器扩展属性
__attribute__((section("name"), zero_init))
来将变量放入指定段中。其中section("name")
选择一个指定的段,zero_init
告诉编译器将变量放入ZI段。uint32_t phy_link_init_flag __attribute__((section("NO_INIT"), zero_init));
-
在分散加载文件中,定义名为
NO_INIT
的段。注意该段所在的可执行域要具有UNINIT
属性。LR_IROM1 0x00000000 0x00080000 ; load region size_region 从0扇区开始 ER_IROM1 0x00000000 0x00080000 ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) RW_IRAM1 0x10000000 0x0000F000 ; RW data .ANY (+RW +ZI) RW_IRAM2 0x1000F000 UNINIT 0x00001000 .ANY (NO_INIT)
6. Arm® Compiler 6 如何防止未初始化变量被初始化为0
-
定义变量时,使用编译器扩展属性
__attribute__((section("name")))
来将变量放入指定段中。其中section("name")
选择一个指定的段。uint32_t phy_link_init_flag __attribute__((section(".bss.NO_INIT")));
-
在分散加载文件中,定义名为
.bss.NO_INIT
的段,其中前缀.bss
是必须的,并且只能为小写。这个前缀表明该数据段具有ZI属性。注意该段所在的可执行域要具有UNINIT
属性LR_IROM1 0x00000000 0x00080000 ; load region size_region 从0扇区开始 ER_IROM1 0x00000000 0x00080000 ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) RW_IRAM1 0x10000000 0x0000F000 ; RW data .ANY (+RW +ZI) RW_IRAM2 0x1000F000 UNINIT 0x00001000 .ANY (.bss.NO_INIT)
7. Arm® Compiler 6 如何将数据放入指定位置
Arm® Compiler 5 支持at(address)
扩展指令,但是在Arm® Compiler 6 中是不支持的,第3节给出了解决方案,使用__attribute__((section(".ARM.__at_address")))
来代替at(address)
扩展指令,本节描述如何实施。
-
定义变量,使用编译器扩展属性使用
__attribute__((section(".ARM.__at_address")))
:int my_peripheral __attribute__((section(".ARM.__at_0x10000000"))) = 0;
-
在分散加载文件中,定义名为
.ARM.__at_0x10000000
的段:LR_1 0x040000 ; load region starts at 0x40000 ; start of execution region descriptions ER_RO 0x040000 ; load address = execution address *(+RO +RW) ; all RO sections (must include section with initial entry point) LR_2 0x01000000 ER_ZI +0 UNINIT *(.bss) LR_3 0x10000000 ER_PERIPHERAL 0x10000000 UNINIT *(.ARM.__at_0x10000000)
以上是关于Keil MDK下如何设置非零初始化变量 - 基于Arm Compiler 6的主要内容,如果未能解决你的问题,请参考以下文章
mdk keil 指定变量函数存储位置,使用 Scatter-Loading Description File, __attribute__(("section“))