SCT文件格式与应用中的分散加载机制详解

*.sct分散加载文件是根据芯片内部FLASH和SRAM存储器概况生成的配置文件,链接器根据该文件的配置分配各个节区地址,生成分散加载代码,通过修改该文件可以定制节区的具体存储位置。例如控制代码的加载区与执行区位置,将代码加载区设定为NAND-FLASH的程序位置,执行区设定为外部SDRAM中的位置,使得链接器生成配套的分散加载代码, 把NAND-FLASH中的代码加载到外部SDRAM中,内核再从外部SDRAM中运行主体代码(大部分运行Linux系统的代码都是这样加载的),进而实现低成本存储。

一、分散加载文件的格式

sct文件中主要包含描述加载域及执行域的部分,一个文件中可包含有多个加载域,而一个加载域可由多个部分的执行域组成。 同等级的域之间使用花括号“{}”分隔开,最外层的是加载域,第二层“{}”内的是执行域, 其整体结构如图

加载域

//方括号中的为选填内容
加载域名 基地址 | ("+" 地址偏移) [属性列表] [最大容量]
"{"
    执行区域描述
"}"

执行域

//方括号中的为选填内容
执行域名 基地址 | ("+" 地址偏移) [属性列表] [最大容量 ]
"{"
    输入节区描述
"}"

输入节区描述

//除模块选择样式部分外,其余部分都可选选填
模块选择样式"("输入节区样式",""+"输入节区属性")"
模块选择样式"("输入节区样式",""+"节区特性")"

模块选择样式"("输入符号样式",""+"节区特性")"
模块选择样式"("输入符号样式",""+"输入节区属性")"

模块选择样式:用于选择o及lib目标文件作为输入节区,它可以直接使用目标文件名或“*”通配符,也可以使用“.ANY”。其中“.ANY”选择语句的优先级是最低的,所有其它选择语句选择完剩下的数据才会被“.ANY”语句选中。

输入节区(符号)样式:用于选择要控制的节区和符号。

输入节区属性:可以使用的节区属性描述符见下表。

节区属性描述符

说明

RO-CODE及CODE

只读代码段

RO-DATA及CONST

只读数据段

RO及TEXT

包括RO-CODE及RO-DATA

RW-DATA

可读写数据段

RW-CODE

可读写代码段

RW及DATA

包括RW-DATA及RW-CODE

ZI及BSS

初始化为0的可读写数据段

XO

只可执行的区域

ENTRY

节区的入口点

节区特性:节区特性可以使用“+FIRST”或“+LAST”选项配置它要存储到的位置,FIRST存储到区域的头部,LAST存储到尾部。 通常重要的节区会放在头部,而CheckSum(校验和)之类的数据会放在尾部。

二、具体应用

编写sct文件优先使用内部SRAM空间,在需要的时候使用一个关键字指定变量存储到外部SDRAM,并把堆区分配到外部SDRAM,从而可以使用C语言标准库的malloc函数动态从外部SDRAM中分配空间进行内存管理。

(1)修改启动文件,在__main执行之前初始化外部SDRAM

; Reset handler
Reset_Handler    PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  SystemInit
                IMPORT  __main

                ;从外部文件引入声明
                IMPORT SDRAM_Init

                LDR     R0, =SystemInit
                BLX     R0

                ;在__main之前调用SDRAM_Init进行初始化
                LDR   R0, =SDRAM_Init
                BLX   R0

                LDR     R0, =__main
                BX      R0
                ENDP

(2)sct文件配置

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x08000000 0x00100000  {    ; load region size_region
    ER_IROM1 0x08000000 0x00100000  {  ; load address = execution address
        *.o (RESET, +First)
        *(InRoot$$Sections)
        .ANY (+RO)
    }


    RW_IRAM1 0x20000000 0x00030000  {  ; 内部SRAM
        .ANY (+RW +ZI)      ;其余的RW/ZI-data都分配到这里
    }

    RW_ERAM1 0xD0000000 0x00800000 {  ; 外部SDRAM
        *.o(HEAP)            ;选择堆区
        .ANY (EXRAM)       ;选择EXRAM节区
    }
}

上述sct文件在保留内部SRAM执行域默认配置的基础上,新增了一个外部SDRAM的执行域, 并且使用了“*.o(HEAP)”语句把堆区分配到了外部SDRAM空间(栈区和堆区属于ZI-data类,默认情况下会分配到内部SRAM),使用“.ANY(EXRAM)”语句把名为“EXRAM”的节区分配到外部SDRAM空间。

(3)指定变量分配到节区

//使用 __attribute__ 关键字定义指定变量定义到某节区
//语法: 变量定义 __attribute__ ((section ("节区名"))) = 变量值;
uint32_t testValue  __attribute__ ((section ("EXRAM"))) =7 ;

//使用宏封装
//设置变量定义到“EXRAM”节区的宏
#define __EXRAM  __attribute__ ((section ("EXRAM")))

//使用该宏定义变量到外部SDRAM
uint32_t testValue __EXRAM =7 

上述代码使用“__attribute__ ((section(“EXRAM”)))”将定义变量分配到外部SDRAM执行域的“EXRAM”节区中。

(4)变量分配测试及结果

//设置变量定义到“EXRAM”节区的宏
#define __EXRAM  __attribute__ ((section ("EXRAM")))

//定义变量到SDRAM
uint32_t testValue __EXRAM =7 ;
//上述语句等效于
//uint32_t testValue  __attribute__ ((section ("EXRAM"))) =7 ;

//定义变量到SRAM
uint32_t testValue2  =7 ;

//定义数组到SDRAM
uint8_t testGrup[3] __EXRAM ={1,2,3};
//定义数组到SRAM
uint8_t testGrup2[3] ={1,2,3};

/**
* @brief  主函数
* @param  无
* @retval 无
*/
int main(void)
{
    uint32_t inerTestValue =10;
    /*SDRAM_Init已经在启动文件的Reset_handler中调用,进入main之前已经完成初始化*/

    //  SDRAM_Init();

    /* 初始化串口 */
    Debug_USART_Config();

    uint32_t *pointer = (uint32_t*)malloc(sizeof(uint32_t)*3);

    if(pointer != NULL)
    {
        *(pointer)=1;
        *(++pointer)=2;
        *(++pointer)=3;

        free(pointer);
    }
    else
    {
        printf("\r\n使用malloc动态分配变量出错!!!\r\n");
    }
    while(1);
}

代码中定义了普通变量、指定到EXRAM节区的变量并使用动态分配内存,还把它们的值和地址通过串口打印到上位机,通过这些变量,我们可以检查变量是否能正常分配。

构建工程后,查看工程的map文件观察变量的分配情况,从map文件中可看到普通变量及栈节区都被分配到了内部SRAM的地址区域,而指定到EXRAM节区的变量及堆空间都被分配到了外部SDRAM的地址区域,与预期一致。

三、相关知识补充

  • map文件说明

  • map文件是由链接器生成的,它主要包含交叉链接信息,可以分为以下几个部分。

    节区的跨文件引用:可以了解工程中各种符号(函数、变量名)之间的引用关系。

    删除无用节区:列出了链接过程工程中未被引用的节区,这些节区将会被删除(不加入到*.axf文件,但*.o文件中仍存在)。

    符号映像表:列出了被引用的各个符号在存储器中的具体地址、占据的空间大小等信息。

    存储器映像索引:主要信息包括存储器中各个节区的类型属性、具体地址以及占据空间大小。

    映像组件大小:最常查询的内容,包含各个使用到的*.o文件、不同类型存储器和整个工程具体占据的Code、RO-data、 RW-data和ZI-data大小以及空间汇总信息。

  • 使用malloc管理外部SDRAM空间

  • C标准库的malloc函数是根据__heap_base及__heap_limit地址限制分配空间的,在启动文件代码定义中,堆基地址__heap_base由链接器自动分配未使用的基地址,而堆顶地址__heap_limit和Heap_Size可以根据用户需要自定义配置。

    Heap_Size       EQU     0x00000200
    
                    AREA    HEAP, NOINIT, READWRITE, ALIGN=3
    
    
    __heap_base
    Heap_Mem        SPACE   Heap_Size
    __heap_limit    EQU   0xd0800000    ;设置堆空间的极限地址(SDRAM),
    ;0xd0000000+0x00800000
    
                    PRESERVE8
                    THUMB
  • SDRAM用于显存的改变

  • 当在代码中直接使用宏定义SDRAM地址作为显存空间时,链接器无法跟踪其内存配置,因此链接器在自动分配变量到SDRAM时,极有可能导致空间冲突出现错误。

    解决方案之一是使用__EXRAM定义一个数组空间作为显存,由链接器自动分配空间地址,然后把数组地址作为显存地址。

    /* LCD Size (Width and Height) */
    #define  LCD_PIXEL_WIDTH    ((uint16_t)800)
    #define  LCD_PIXEL_HEIGHT   ((uint16_t)480)
    
    #define LCD_FRAME_BUFFER       ((uint32_t)0xD0000000)   //第一层首地址
    #define BUFFER_OFFSET          ((uint32_t)800*480*3)     //一层液晶的数据量
    #define LCD_PIXCELS            ((uint32_t)800*480)
    
    uint8_t LCD_FRAME_BUFFER[BUFFER_OFFSET] __EXRAM;

    说明:

    (1)如果在外部SDRAM执行域中使用“.ANY(+RW +ZI)”语句,选择将所有RW/ZI类型的数据都自动分配变量到外部SDRAM空间时,需要注意将SDRAM初始化过程所需使用的栈空间以及部分RW-data类型变量通过节区语句(*.o(STACK),xxx.o(+RW)等)分配到内部SRAM的执行域。

    (即使内部SRAM的执行域中也使用“.ANY(+RW+ZI)”语句选择所有RW及ZI属性的内容, 但对于符合两个相同选择语句的内容,链接器会优先选择使用空间较大的执行域, 即这种情况下只有当SDRAM执行域的空间使用完,RW/ZI属性的内容才会被分配到内部SRAM,所以在大部分情况下,内部SRAM执行域中的“.ANY(+RW +ZI)”语句是不起作用的)

    (2)由于SDRAM需要在初始化并正常工作后才能够将栈分配到外部SDRAM,因此就需要外部SDRAM初始化过程不使用栈空间,也就是只能使用纯粹的寄存器方式配置。具体步骤如下:

  • 修改sct文件,使用“*.o(STACK)”语句把栈空间分配到外部SDRAM的执行域;

  • 根据硬件平台修改SystemInit_ExtMemCtl函数,该函数要实现外部SDRAM的初始化过程,且不能使用栈空间(局部变量);

  • 定义DATA_IN_ExtSDRAM宏,从而使得SystemInit_ExtMemCtl函数被加进(条件)编译,并被SystemInit调用(__main之前),进而在启动文件中被Reset_handler调用执行。

  • 物联沃分享整理
    物联沃-IOTWORD物联网 » SCT文件格式与应用中的分散加载机制详解

    发表评论