[ArmCompiler6--armlink]Keil-MDK中scatter load机制

来源:互联网 发布:熊神进淘宝店 编辑:程序博客网 时间:2024/06/04 18:03

简述

Keil MDK工具中,Armlink使用scatter加载机制来描述一份image在memory map中的划分。
image的memory map由regions和output sections组成。

Scatter加载

需要使用Scatter加载的场景

  • 复杂的memory map,比如各种切分等
  • 不同类别的memory设备,RAM, ROM, SRAM,外设等
  • 固定函数放在指定位置
  • 使用symbols来识别heap和stack

当使用scatter file时,如下的符号将未被定义

  • Image–RO–Base.
  • Image–RO–Limit.
  • Image–RW–Base.
  • Image–RW–Limit.
  • Image–XO–Base.
  • Image–XO–Limit.
  • Image–ZI–Base.
  • Image–ZI–Limit.
    使用scatter file时,如果没有为stack和heap使用特定region,或没有重新实现__user_setup_stackheap(), 将会产生错误信息。

scatter file指定stack和heap

ARM C库为__user_setup_stackheap()提供了多种实现,并会根据statter file情况自动选择合适的一种实现。当然如果自己实现了user_setup_heap()的话,ARM C库提供的所有实现都会被覆盖。
另外在scatter file中定义两个特别的execution regions(ARM_LIB_HEAP和ARM_LIB_STACK)来选择两种region内存模式,这两种region都有EMPTY属性,一旦使用了这两种region内存模式,ARM C库提供的所有实现也会被无效掉,因为ARM C库中的user_setup_heap()使用了如下的synbols:

  • Image–ARM_LIB_STACK–Base.
  • Image–ARM_LIB_STACK–ZI–Limit.
  • Image–ARM_LIB_HEAP–Base.
  • Image–ARM_LIB_HEAP–ZI–Limit.

需要确保只有一个ARM_LIB_STACK或ARM_LIB_HEAP region的指定,且必须分配空间,因为user_setup_heap()会使用这些数据:

LOAD_FLASH …{    …    ARM_LIB_STACK 0x40000 EMPTY -0x20000  ; Stack region growing down    { }    ARM_LIB_HEAP 0x28000000 EMPTY 0x80000 ; Heap region growing up    { }    …}

另外也可以只在scatter file中定义ARM_LIB_STACKHEAP,这样user_setup_stackheap()将使用如下symbol的值:

  • Image–ARM_LIB_STACKHEAP–Base
  • Image–ARM_LIB_STACKHEAP–ZI–Limit

复杂image scatter file示例

这里写图片描述
与此图对应的scatter file如下:

LOAD_ROM_1 0x0000              ; Start address for first load region (0x0000){    EXEC_ROM_1 0x0000          ; Start address for first exec region (0x0000)    {        program1.o (+RO)       ; Place all code and RO data from                               ; program1.o into this exec region    }    DRAM 0x18000 0x8000        ; Start address for this exec region (0x18000),                               ; Maximum size of this exec region (0x8000)    {        program1.o (+RW, +ZI)  ; Place all RW and ZI data from                               ; program1.o into this exec region    }}LOAD_ROM_2 0x4000              ; Start address for second load region (0x4000){    EXEC_ROM_2 0x4000    {        program2.o (+RO)       ; Place all code and RO data from                               ; program2.o into this exec region    }    SRAM 0x8000 0x8000    {        program2.o (+RW, +ZI)  ; Place all RW and ZI data from                               ; program2.o into this exec region    }}

这里是比较复杂的例子,即将program1和program2分别放在两个load region中。在一些不这么复杂的情况下,ARM推荐 *或.ANY 来指代剩下的code和data。

Root execution region的情况

Root region指的是load address和execution address相同的region。
image的初始化entry point必须在root region之内。

ABSOLUTE属性

ABSOLUTE属性可以用来指定root region。
为了使execution region address和load region address一样,如下条件满足其中之一即可:

条件 说明 一 为execution region和load region设置相同的基地址(base address) 二 在load region中的第一个execution region添上+0,另外如果该load region中的其余所有execution region都添上+0,除了ZI之后的execution region都是root regions

ABSOLUTE属性用法示例如下:

LR_1 0x040000                   ; load region starts at 0x40000   {                               ; start of execution region descriptions          ER_RO 0x040000 ABSOLUTE     ; load address = execution address    {        * (+RO)                 ; all RO sections (must include section with                                 ; initial entry point)    }    …                         ; rest of scatter-loading description}

FIXED属性

FIXED属性用于创建固定地址的root regions。
这里写图片描述
对应的scatter file如下:

LR_1 0x040000              ; load region starts at 0x40000   {                          ; start of execution region descriptions          ER_RO 0x040000         ; load address = execution address    {        * (+RO)            ; RO sections other than those in init.o    }    ER_INIT 0x080000 FIXED ; load address and execution address of this                           ; execution region are fixed at 0x80000    {        init.o(+RO)        ; all RO sections from init.o    }    …                    ; rest of scatter-loading description}

可以使用该属性将某个函数,或一块数据(如常数表、校验值)放置在固定的地址。

例如一些初始化代码需要放在ROM的开头,校验码可能要放在ROM的末尾,那么一些memory内容可能是无用的。使用*或.ANY将其余的code或data填满初始化代码和校验码之间的memory空间。
为维护和调试方便,Keil建议在scatter file中只使用必要的指定地址防止,将大部分的都交给linker来做。

在固定地址放置函数或数据

armlinker允许在固定地址放置一个section,具体如下:

方法 说明 一 在指定地址定义一个execution region,指定其section描述,且该execution region只包含这一个section 二 对于特殊名字的section,linker能从section名字获取其地址,这些特殊名字的section称为__at sections

为将一个函数或变量放在指定地址,该函数或变量必须放在它自己的section中:

步骤 说明 一 将函数或数据item放在其所属的源文件中 二 使用attribute((section(“name”))),将函数或数据放在自己命名的section中 三 在汇编语言中使用.section指令,汇编语言中.section是最小的memory放置单元 四 使用-ffunction_sections编译器选项,该选项会为源文件中的每个函数都产生一个ELF section。该选项对于一些函数而言会增加code size,因为一些共享地址、数据和字符串没有被减去。但是可以在链接时使用armlink –remove将这部分再减去

不使用scatter file而实现函数or数据放置在固定地址

  1. 创建main.c
#include <stdio.h>extern int sqr(int n1);const int gValue __attribute__((section(".ARM.__at_0x5000"))) = 3; // Place at 0x5000int main(){    int squared;    squared=sqr(gValue);    printf("Value squared is: %d\n", gSquared);}
  1. function.c
int sqr(int n1){    return n1*n1;}
  1. 编译与链接
armclang -target arm-arm-none-eabi -march=armv8-a -c -g function.carmclang -target arm-arm-none-eabi -march=armv8-a -c -g main.carmlink --cpu=8-A.32 --map function.o main.o -o squared.axf

使用scatter的命名section实现函数or数据放置在固定地址

  1. main.c
#include <stdio.h>extern int sqr(int n1);int gSquared __attribute__((section("foo")));  // Place in section fooint main(){    gSquared=sqr(3);    printf("Value squared is: %d\n", gSquared);}
  1. function.c
int sqr(int n1){    return n1*n1;}
  1. scatter file
LR1 0x0000 0x20000{    ER1 0x0 0x2000    {        *(+RO)                ; rest of code and read-only data    }    ER2 0x8000 0x2000    {        main.o    }    ER3 0x10000 0x2000    {        function.o        *(foo)                ; Place gSquared in ER3    }    ; RW and ZI data to be placed at 0x200000    RAM 0x200000 (0x1FF00-0x2000)    {        *(+RW, +ZI)    }    ARM_LIB_STACK 0x800000 EMPTY -0x10000    {    }    ARM_LIB_HEAP  +0 EMPTY 0x10000    {    }}
  1. 编译与链接
armclang -target arm-arm-none-eabi -march=armv8-a -c -g function.carmclang -target arm-arm-none-eabi -march=armv8-a -c -g main.carmlink --cpu=8-A.32 --map --scatter=scatter.scat function.o main.o -o squared.axf

使用scatter的指定地址实现函数or数据放置在固定地址

  1. main.c
#include <stdio.h>extern int sqr(int n1);// Place at address 0x10000const int gValue __attribute__((section(".ARM.__at_0x10000"))) = 3;int main(){    int squared;    squared=sqr(gValue);    printf("Value squared is: %d\n", squared);}
  1. function.c
int sqr(int n1){    return n1*n1;}
  1. scatter file
LR1 0x0{    ER1 0x0    {        *(+RO)                      ; rest of code and read-only data    }    ER2 +0    {        function.o        *(.ARM.__at_0x10000)        ; Place gValue at 0x10000    }    ; RW and ZI data to be placed at 0x200000    RAM 0x200000 (0x1FF00-0x2000)    {        *(+RW, +ZI)    }    ARM_LIB_STACK 0x800000 EMPTY -0x10000    {    }    ARM_LIB_HEAP  +0 EMPTY 0x10000    {    }}
  1. 编译与链接
armclang -target arm-arm-none-eabi -march=armv8-a -c -g function.carmclang -target arm-arm-none-eabi -march=armv8-a -c -g main.carmlink --cpu=8-A.32 --no_autoat --scatter=scatter.scat --map function.o main.o -o squared.axf
0 0