*.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的初始化过程,且不能使用栈空间(局部变量);文章来源:https://www.toymoban.com/news/detail-580143.html
定义DATA_IN_ExtSDRAM宏,从而使得SystemInit_ExtMemCtl函数被加进(条件)编译,并被SystemInit调用(__main之前),进而在启动文件中被Reset_handler调用执行。文章来源地址https://www.toymoban.com/news/detail-580143.html
到了这里,关于sct分散加载文件格式与应用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!