查看: 4288|回复: 0

[分享] [痞子衡]深扒IAR启动函数流程里RW/ZI段初始化实现

[复制链接]
  • TA的每日心情
    开心
    2025-7-11 08:53
  • 签到天数: 301 天

    连续签到: 2 天

    [LV.8]以坛为家I

    3868

    主题

    7472

    帖子

    0

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    39227
    最后登录
    2025-7-18
    发表于 2021-11-29 13:18:12 | 显示全部楼层 |阅读模式
    深扒IAR启动函数流程里RW/ZI段初始化实现

    大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家分享的是IAR启动函数流程里的段初始化函数__iar_data_init3实现。

    本篇是 《IAR启动函数流程及其__low_level_init设计对函数重定向的影响》 一文的后续,在上篇文章里我们在 IAR 软件安装目录下找到了标准启动函数 __iar_program_start() 相关源文件,并且分析了 __iar_program_start() 函数里的全部动作。我们知道了其中负责 .data/.bss/.textrw 段初始化工作的是 __iar_data_init3() 函数,但是这个函数的具体实现并没有详细介绍,今天我们就仔细说说这个 __iar_data_init3() 函数:
    Note 1:阅读本文前需要对 《IAR链接文件(.icf)》 有所了解。
    Note 2:本文使用的 IAR EWARM 软件版本是 v9.10.2。
    一、为什么有些段需要初始化?
    《IAR链接文件(.icf)》 一文第一小节列出了 IAR 工程里定义的全部系统段(Section)名,其中 .data/.bss/.textrw 段是需要初始化的,因为这些段是链接在 RAM 里,而 RAM 上电其内容都是随机值,所以需要一段启动代码将 .data/.bss/.textrw 段所在的 RAM 区填上对应的初值(初值来自于下载了程序镜像文件的 Flash 区),然后应用程序才能正常运行。

    Note: 除了 .data/.bss/.textrw 之外,还有一些段(.noinit/CSTACK/HEAP等)也链接在 RAM 区,但这些段对初值没有依赖,所以不需要初始化。
    1. .bss                 // 初值为 0 的静态/全局变量(RAM)
    2. .data                // 初值为非 0 的全局变量(RAM)
    3. .data_init           // .data 段的初值(Flash)
    4. .textrw              // __ramfunc 修饰的重定向函数实际执行区(RAM)
    5. .textrw_init         // .textrw 段的机器码存储区(Flash)
    复制代码
    二、RW/ZI段初始化的一般实现
    应用程序工程在编译链接结束后,.data/.bss/.textrw 段实际链接地址就确定了(这里指默认由 IAR 链接器自由分配具体链接地址,而不是用户在链接文件中指明具体链接地址的情况),我们知道了这些段的链接地址,就可以完成对应初始化工作(说白了,就是初值数据从 Flash 到 RAM 的拷贝工作),实际链接地址可以通过如下 IAR 链接器提供的接口来获取,具体拷贝过程可参看 《IAR下将关键函数重定向到RAM中执行的方法》 一文最后一节里的代码。

    Note: IAR 链接器为了后续初始化的方便,都是将程序中全部的全局变量紧挨着放到一块连续的 RAM 区域(.data),然后其全部初值也一一对应紧挨着放一起(.data_init,下载到一块连续的 Flash 区);对于 .textrw 的处理也类似。
    1. #pragma section = ".data"
    2. #pragma section = ".data_init"
    3. #pragma section = ".bss"
    4. #pragma section = ".textrw"
    5. #pragma section = ".textrw_init"

    6. uint8_t *data_ram              = __section_begin(".data");
    7. uint8_t *data_rom              = __section_begin(".data_init");
    8. uint8_t *data_rom_end          = __section_end(".data_init");
    9. uint8_t *bss_start             = __section_begin(".bss");
    10. uint8_t *bss_end               = __section_end(".bss");
    11. uint8_t *code_relocate_ram     = __section_begin(".textrw");
    12. uint8_t *code_relocate_rom     = __section_begin(".textrw_init");
    13. uint8_t *code_relocate_rom_end = __section_end(".textrw_init");
    复制代码
    段初始化的一般实现虽然简单,但有个缺点,就是对于用户自定义 RW/ZI 段或者多个分散的 RW/ZI 段无法自动适应,需要根据实际情况不断调整代码实现。

    三、__iar_data_init3() 函数实现细节
    前面铺垫了这么多,终于到了围观 IAR 标准段初始化函数 __iar_data_init3() 实现的时候了,跟这个函数相关的源文件在如下路径下,核心代码在 data_init.c 文件中:
    1. \IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\data_init.c
    2. \IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\zero_init3.c  - 存放 __iar_zero_init3 函数
    3. \IAR Systems\Embedded Workbench 9.10.2\arm\src\lib\init\copy_init3.c  - 存放 __iar_copy_init3 函数
    复制代码
    在 data_init.c 文件中有一个叫 IAR_DATA_INIT 的函数,其实它就是 __iar_data_init3,光看这个函数里的代码会让人有点摸不着头脑,因为用了 IAR 链接器里的接口及一些特殊定义,我们结合一个具体应用程序工程来讲解会更清晰。
    1. // 在 IAR 目录 \arm\inc\c\DLib_Product.h 中宏定义
    2. #define _DLIB_ELF_INIT_INTERFACE_VERSION 3

    3. // 在 IAR 目录 \arm\src\lib\init\data_init.h 中的宏定义
    4. #define IAR_DATA_INIT _GLUE(__iar_data_init, _DLIB_ELF_INIT_INTERFACE_VERSION)

    5. #pragma section = "Region$Table" const TABLE_MEM
    6. void IAR_DATA_INIT(void)
    7. {
    8.     FAddr TABLE_MEM const * pi = __section_begin("Region$Table");
    9.     table_ptr_t             pe = __section_end  ("Region$Table");
    10.     while (pi != pe)
    11.     {
    12.         init_fun_t * fun = FAddr_GetPtr(pi);
    13.         ++pi;
    14.         pi = fun(pi);
    15.     }
    16. }
    复制代码
    我们现在随便编译一个 SDK 例程(痞子衡选择的是 \SDK_2.11.0_MIMXRT1170-EVK\boards\evkmimxrt1170\demo_apps\hello_world\cm7\iar,切到 flexspi_nor_debug build,即代码 RO 段链接在 0x30000000 开始的 Flash 区,代码 RW 段链接在 0x20000000 开始的 DTCM 区),查看其对应映射文件(.map),摘出其中跟段初始化相关的一些内容如下,初始化工作包含:利用 __iar_zero_init3 函数清零起始地址为 0x20000040 长度为 0x4c 字节的 ZI 段空间,利用 __iar_copy_init3 函数拷贝 0x40 字节 RW 段数据(从 0x300060fc 到 0x20000000):
    1. *******************************************************************************
    2. *** INIT TABLE
    3. ***

    4.           Address      Size
    5.           -------      ----
    6. Zero (__iar_zero_init3)
    7.     1 destination range, total size 0x4c:
    8.           0x2000'0040  0x4c

    9. Copy (__iar_copy_init3)
    10.     1 source range, total size 0x40:
    11.           0x3000'60fc  0x40
    12.     1 destination range, total size 0x40:
    13.           0x2000'0000  0x40

    14. *******************************************************************************
    15. *** ENTRY LIST
    16. ***

    17. Entry                       Address   Size  Type      Object
    18. ----                       -------   ----  ----      ------
    19. .iar.init_table$Base   0x3000'63d4          --   Gb  - Linker created -
    20. .iar.init_table$Limit  0x3000'63f8          --   Gb  - Linker created -
    21. Region$Table$Base     0x3000'63d4          --   Gb  - Linker created -
    22. Region$Table$Limit    0x3000'63f8          --   Gb  - Linker created -
    23. __iar_copy_init3        0x3000'630d   0x2c  Code  Gb  copy_init3.o [6]
    24. __iar_zero_init3        0x3000'613d   0x3c  Code  Gb  zero_init3.o [6]
    复制代码
    在映射文件里,我们知道了 Region$$Table 区域的起止地址 [0x300063d4 - 0x300063f8),打开镜像文件或者在线调试找到这段区域里的内容,你会发现段初始化工作所需的全部信息(操作函数地址、操作数据长度、操作源地址、操作目标地址)都记录在里面,其中特别注意的是涉及 Flash 区的地址都是以相对地址来存放的:


    Note:FAddr_GetPtr 函数负责地址转换,0x300063d4 地址处的值是 0xfffffd69,那么 0x300063d4 + 0xfffffd69 = 0x13000613d,保留低 32bit 即是 __iar_zero_init3 函数地址。
    14.png
    现在我们就很好理解 __iar_data_init3 函数里的代码了,它就是从 Region$$Table 区域里按序取出初始化工作所需的信息,并去一一执行完成段初始化的工作,这种实现方法的优点在于拓展性强,IAR 链接器可根据实际应用程序工程的链接情况自由拓展 .iar.init_table 区域里的内容,而 __iar_data_init3 函数本身则不需要做任何修改。
    至此,IAR启动函数流程里的段初始化函数__iar_data_init3实现痞子衡便介绍完毕了,掌声在哪里~~~

    qiandao qiandao
    回复

    使用道具 举报

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

    本版积分规则

    关闭

    站长推荐上一条 /3 下一条

    Archiver|手机版|小黑屋|恩智浦技术社区

    GMT+8, 2025-7-19 00:49 , Processed in 0.083609 second(s), 20 queries , MemCache On.

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.

    快速回复 返回顶部 返回列表