IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> Keil MDK下如何设置非零初始化变量 - 基于Arm Compiler 6 -> 正文阅读

[嵌入式]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只需要:将未初始化的变量放入一个特殊段。特殊段需要满足:

  1. 这是一个ZI段;
  2. 包含这个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

  1. 定义变量时,使用编译器扩展属性__attribute__((section("name"), zero_init))来将变量放入指定段中。其中section("name")选择一个指定的段,zero_init告诉编译器将变量放入ZI段。

    uint32_t phy_link_init_flag __attribute__((section("NO_INIT"), zero_init));
    
  2. 在分散加载文件中,定义名为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

  1. 定义变量时,使用编译器扩展属性__attribute__((section("name")))来将变量放入指定段中。其中section("name")选择一个指定的段。

    uint32_t phy_link_init_flag __attribute__((section(".bss.NO_INIT")));
    
  2. 在分散加载文件中,定义名为.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)扩展指令,本节描述如何实施。

  1. 定义变量,使用编译器扩展属性使用__attribute__((section(".ARM.__at_address")))

    int my_peripheral __attribute__((section(".ARM.__at_0x10000000"))) = 0;
    
  2. 在分散加载文件中,定义名为.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)
        }
    }
    
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-05-24 18:23:18  更:2022-05-24 18:23:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 1:30:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码