浅析 Keil 中的 sct 文件


一、程序的存储与运行

1、存储

程序编译后,应用程序中所有具有同一性质的数据(包括代码)被归到一个域,程序在存储或运行的时候,不同的域会呈现不同的状态,这些域的意义如下:

  • Code:即代码域,它指的是编译器生成的机器指令,这些内容被存储到 ROM 区。
  • RO-dataRead Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在 ROM 区,因而程序不能修改其内容。
    • 例如 C 语言中 const 关键字定义的变量就是典型的 RO-data。
  • RW-data:Read Write data,即可读写数据域,它指初始化为"非0值"的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。
    • 例如 C 语言中使用定义的全局变量,且定义时赋予"非 0 值"给该变量进行初始化。
  • ZI-dataZero Initialie data,即 0 初始化数据,它指初始化为"0 值"的可读写数据域,它与 RW-data 的区别是程序刚运行时这些数据初始值全都为 0,而后续运行过程与 RW-data 的性质一样,它们也常驻在 RAM 区,因而应用程序可以更改其内容。
    • 例如 C 语言中使用定义的全局变量,且定义时赋予"0 值"给该变量进行初始化
    • 若定义该变量时没有赋予初始值,编译器会把它当 ZI-data 来对待,初始化为 0;
  • ZI-data 的栈空间(Stack)及堆空间(Heap):
    • 在 C 语言中,函数内部定义的局部变量属于栈空间,进入函数的时候从向栈空间申请内存给局部变量,退出时释放局部变量,归还内存空间。
    • 使用 malloc 动态分配的变量属于堆空间。
    • 在程序中的栈空间和堆空间都是属于 ZI-data 区域的,这些空间都会被初始值化为 0 值。编译器给出的 ZI-data 占用的空间值中包含了堆栈的大小(经实际测试,若程序中完全没有使用 malloc 动态申请堆空间,编译器会优化,不把堆空间计算在内)。

详细内容可以参考如下文章:
STM32 map 文件浅析单片机内存区域划分

总结如下:

程序组件 所属类别
机器代码指令 Code
常量 RO-data
初值非0的全局变量 RW-data
初值为0的全局变量 ZI-data
局部变量 ZI-data 栈空间
使用malloc动态分配的空间 ZI-data 堆空间

2、加载、运行

RW-dataZI-data 它们仅仅是初始值不一样而已,为什么编译器非要把它们区分开?原因如下:

应用程序具有静止状态和运行状态。静止态的程序被存储在非易失存储器中,如 STM32 的内部 FLASH,因而系统掉电后也能正常保存。但是当程序在运行状态的时候,程序常常需要修改一些暂存数据,由于运行速度的要求,这些数据往往存放在内存中(RAM),掉电后这些数据会丢失。因此,程序在静止与运行的时候它在存储器中的表现是不一样的,见下图。


程序在存储状态时,RO sectionRW Section 都被保存在 ROM 区。当程序开始运行时,内核直接从 ROM 中读取代码,并且在执行主体代码前,会先执行一段加载代码,它把 RW Section 数据从 ROM 复制到 RAM,并且在 RAM 加入 ZI SectionZI Section 的数据都被初始化为 0。加载完后 RAM 区准备完毕,正式开始执行主体程序。

编译生成的 RW-data 的数据属于图中的 RW SectionZI-data 的数据属于图中的 ZI Section。是否需要掉电保存,这就是把 RW-dataZI-data 区别开来的原因:
- 因为在 RAM 创建数据的时候,默认值为 0,
- 但如果有的数据要求初值非 0,那就需要使用 ROM 记录该初始值,运行时再复制到 RAM

STM32 的 RO 区域不需要加载到 SRAM,内核直接从 FLASH 读取指令运行。计算机系统的应用程序运行过程很类似,不过计算机系统的程序在存储状态时位于硬盘,执行的时候甚至会把上述的 RO 区域(代码、只读数据)加载到内存,加快运行速度,还有虚拟内存管理单元(MMU)辅助加载数据,使得可以运行比物理内存还大的应用程序。而 STM32 没有 MMU,所以无法支持 Linux 系统。

当程序存储到 STM32 芯片的内部 FLASH 时(即 ROM 区),它占用的空间是 CodeRO-dataRW-data 的总和,所以如果这些内容比STM32 芯片的 FLASH 空间大,程序就无法被正常保存了。当程序在执行的时候,需要占用内部 SRAM 空间(即 RAM 区),占用的空间包括RW-dataZI-data。应用程序在各个状态时各区域的组成见下表。

程序状态与区域 组成
程序执行时的只读区域(RO) Code + RO data
程序执行时的可读写区域(RW) RW data + ZI data
程序存储时占用的ROM区 Code + RO data + RW data

而这些区域的起始地址和大小,以及各个函数变量应该放在哪个存储器区域中就是由本文要讲的 sct 文件定义的。

二、sct 分散加载文件

1、简介

当工程按默认配置构建时,MDK 会根据我们选择的芯片型号,获知芯片的内部 FLASH 及内部 SRAM 存储器概况,生成一个以工程名命名的后缀为 *.sct 的分散加载文件(Linker Control Filescatter loading),链接器根据该文件的配置分配各个节区地址,生成分散加载代码,因此我们通过修改该文件可以定制具体节区的存储位置。

  • 可以设置源文件中定义的所有变量自动按地址分配到外部 SDRAM,这样就不需要再使用关键字 __attribute__ 按具体地址来指定了;
  • 利用它还可以控制代码的加载区与执行区的位置,例如可以把程序代码存储到单位容量价格便宜的 NAND-FLASH 中,但在 NAND-FLASH 中的代码是不能像内部 FLASH 的代码那样直接提供给内核运行的,这时可通过修改分散加载文件,把代码加载区设定为 NAND-FLASH 的程序位置,而程序的执行区设定为 SDRAM 中的位置,这样链接器就会生成一个配套的分散加载代码,该代码会把 NAND-FLASH 中的代码加载到 SDRAM 中,内核再从 SDRAM 中运行主体代码,大部分运行 Linux 系统的代码都是这样加载的。

2、文件格式

下面是一个由 MDK 默认生成的 sct 文件:

我使用的是 STM32F407,不同的芯片型号内存不一样

LR_IROM1 0x08000000 0x00080000  {
       ; load region size_region(加载域,基地址空间大小)
  ER_IROM1 0x08000000 0x00040000  {
     ; load address = execution address(加载地址 = 执行地址)
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .
### 创建和使用 Keil SCT 文件进行散列配置 #### 定义 SCT 文件的作用 SCT文件,即散列控制表(Scatter Loading File),用于指导链接器如何将编译后的代码和数据放置到目标硬件的不同内存区域中。这使得开发者可以精确地控制程序各部分的存储位置,从而优化性能或满足特定的应用需求[^3]。 #### 编辑和创建 SCT 文件的内容 对于ARM架构下的嵌入式系统而言,一个典型的`.sct`文件可能包含多个定义域(Load Region)、执行区(Execution Region)以及其他指令来安排不同类型的段(Section)。下面给出一段简单的例子: ```plaintext LR_IROM1 0x08000000 0x00040000 { ; Load region for internal flash memory (start address and size) ER_IROM1 0x08000000 0x00040000 { *.o (+RO) ; Read-only sections into IROM1 } } RW_IRAM1 0x20000000 0x0000A000 { ; Execution region for internal SRAM (read-write section start address and size) *.o (+RW,+ZI) ; Read/write & zero-initialized data into IRAM1 } ``` 这段脚本指定了两个主要的部分:一个是位于内部Flash中的读取只读段(`+RO`),另一个是在内部SRAM内的可读写段(`+RW`, `+ZI`)[^4]。 #### 配置 MDK-Keil 使用 SCT 文件 为了使上述自定义的.sct文件生效,在MDK环境中需要做相应的设置。具体来说就是在项目的选项里找到Linker标签页,并在此处输入所编写好的.scatter文件路径或者直接粘贴其内容至文本框内。 #### 将代码映射到 RAM 中运行 如果希望某些代码能够在RAM中被执行,则除了调整链接器脚本外,还需要确保这部分代码能够正确初始化并复制到RAM中。这意味着在应用程序启动阶段要有一个过程负责把必要的代码片段从闪存拷贝到随机访问存储器(RAM),之后再跳转过去继续执行[^5]。
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值