Keil MDK(MicroVision Development Kit)中的 sct 文件(Scatter-Loading Description File) 是链接器(ARM Linker)用于精确控制代码和数据在嵌入式设备内存中布局的关键配置文件。它取代了传统的启动代码内存初始化,允许开发者灵活定义内存分区、地址分配和段(section)的加载/执行位置。
sct 文件的核心作用
- 内存映射管理
定义物理存储设备(如 Flash、RAM、外部存储器)的地址范围、大小和属性(如RO
(只读)、RW
(读写)、ZI
(零初始化))。 - 代码/数据定位
指定目标文件(.o
)中的各个段(如代码、常量、变量、堆栈)应放置到哪个存储区域。 - 执行域控制
分离加载地址(Load Address,程序存储的位置)和执行地址(Execution Address,程序运行的位置),常用于支持 XIP(eXecute In Place)或数据从 Flash 加载到 RAM 中运行。 - 多区域分配
支持复杂存储架构(如多块 RAM、外部 SDRAM、QSPI Flash)。 - 优化启动速度
减少不必要的拷贝操作(如只拷贝需要初始化的数据段)。
sct 文件基本结构
; 注释格式:分号开头
; 1. 定义存储区域(Memory Regions)
ROM_LOAD 0x08000000 0x00100000 { ; 加载域:起始地址 + 大小
; 2. 定义执行域(Execution Regions)
ROM_EXEC 0x08000000 0x00100000 { ; 执行域地址(常与加载域相同)
*.o (RESET, +First) ; 向量表必须放在开头
* (InRoot$$Sections) ; 内核和库的关键段(如初始化代码__main函数)
.ANY (+RO) ; 所有只读段(代码+常量)
}
RAM 0x20000000 0x00030000 { ; RAM 执行域
.ANY (+RW, +ZI) ; 所有读写数据 + 零初始化数据
}
; 特殊段(如堆栈)
ARM_LIB_HEAP 0x20030000 EMPTY 0x00004000 { } ; 堆区域(未初始化)
ARM_LIB_STACK 0x20034000 EMPTY -0x00001000 { } ; 栈(向下生长)
}
关键语法解析
-
加载域(Load Region)
- 定义程序烧录位置(通常是 Flash)。
- 格式:
区域名 起始地址 大小 { 执行域列表 }
FLASH 0x80000000 0x00200000 { ... } ; 2MB Flash
-
执行域(Execution Region)
- 定义代码/数据的运行位置(可能与加载域不同)。
- 可包含属性:
ABSOLUTE
(绝对地址)、RELOC
(重定位)、EMPTY
(预分配空区域)等。
SRAM 0x20000000 0x00010000 { ... } ; 64KB RAM 运行域
-
段分配规则
*
: 匹配所有目标文件(.o
).ANY
: 按需分配未被显式指定的段file.o(.section)
: 精确指定文件及段+属性
:如+RO
、+RW
、+ZI
- 特殊段:
RESET
:中断向量表(必须+First
置于开头)InRoot$$Sections
:编译器/库关键段(如__main.o
,__scatter*.o
)
-
地址偏移与对齐
offset
:偏移量(如ROM_EXEC +0x1000
)ALIGN n
:按n
字节对齐
CUSTOM_RAM 0x20008000 ALIGN 32 { ... } ; 32字节对齐
典型场景示例
1. 从 Flash 执行代码(XIP)
LR_FLASH 0x08000000 0x00100000 { ; 1MB Flash (加载域)
ER_FLASH 0x08000000 0x00100000 { ; 执行域 = 加载域
*.o (RESET, +First) ; 向量表在 Flash 开头
* (InRoot$$Sections) ; 库初始化代码
.ANY (+RO) ; 所有代码和常量
}
ER_RAM 0x20000000 0x00030000 { ; 192KB RAM
.ANY (+RW, +ZI) ; 变量和堆栈
}
}
2. 代码从 Flash 拷贝到 RAM 运行(加速)
LR_FLASH 0x08000000 0x00100000 {
ER_FLASH 0x08000000 0x00100000 {
*.o (RESET, +First)
* (InRoot$$Sections)
.ANY (+RO) ; RO 段保留在 Flash
}
; 定义代码在 RAM 中的执行位置
ER_FASTCODE 0x20000000 0x00008000 { ; 32KB RAM
fast_code.o (+RO) ; 指定需要加速的文件
}
ER_RAM 0x20008000 0x00028000 { ; 剩余 RAM
.ANY (+RW, +ZI)
}
}
需配合初始化代码(由
__main
自动处理或手动实现)。
3. 外部存储器 + 多区域管理
LR_QSPI 0x90000000 0x01000000 { ; 16MB QSPI Flash
ER_QSPI_EXEC 0x90000000 0x01000000 {
.ANY (+RO) ; 只读数据/代码
}
}
LR_SDRAM 0xC0000000 0x02000000 { ; 32MB SDRAM
ER_SDRAM_RW 0xC0000000 0x01000000 {
.ANY (+RW, +ZI) ; 全局变量
}
ER_HEAP 0xC1000000 EMPTY 0x00400000 { } ; 堆区(4MB)
ER_STACK 0xC1400000 EMPTY -0x00010000 { } ; 栈区(64KB)
}
调试与验证
-
生成映射文件
在 Keil 的 Linker 配置中启用--map
选项,编译后查看生成的.map
文件,确认各段地址是否符合预期。 -
检查链接错误
- 地址溢出:
Execution region xxx size overflow
→ 增大区域或优化代码。 - 未分配段:
Section .data cannot be assigned
→ 添加.ANY(+RW)
。
- 地址溢出:
-
使用
__attribute__
强制指定段
在代码中通过编译器扩展将变量/函数放入自定义段:__attribute__((section(".my_section"))) int fast_var;
在 sct 文件中分配:
ER_RAM 0x20000000 { *.o (.my_section) ; 分配到 RAM }
最佳实践
- 向量表固定地址:
RESET
必须位于设备指定的起始地址(如0x08000000
)。 - 库段处理:务必包含
* (InRoot$$Sections)
,避免初始化代码缺失。 - 使用
.ANY
简化:避免手动枚举所有.o
文件。 - 空区域管理:堆栈推荐用
EMPTY
(不占用 bin 文件体积)。 - 版本控制:sct 文件应与工程代码一同纳入版本管理(如 Git)。
通过掌握 sct 文件的配置,开发者能彻底控制嵌入式系统的内存布局,应对复杂存储架构、性能优化与资源受限挑战。