Keil 中的 sct 文件

参考

浅析 Keil 中的 sct 文件
Keil MDK设置部分程序在RAM执行的方法:ramfunc
Keil MDK的sct分散加载文件详解

一、程序的存储与运行

1、存储

程序编译后,应用程序中所有具有同一性质的数据(包括代码)被归到一个域,程序在存储或运行的时候,不同的域会呈现不同的状态,这些域的意义如下:
Code: 即代码域,它指的是编译器生成的机器指令,这些内容被存储到 ROM 区。

RO-data: Read Only data,即只读数据域,它指程序中用到的只读数据,这些数据被存储在 ROM 区,因而程序不能修改其内容。例如 C 语言中 const 关键字定义的变量就是典型的 RO-data。

RW-data: Read Write data,即可读写数据域,它指初始化为"非0值"的可读写数据,程序刚运行时,这些数据具有非0的初始值,且运行的时候它们会常驻在RAM区,因而应用程序可以修改其内容。例如 C 语言中使用定义的全局变量,且定义时赋予"非 0 值"给该变量进行初始化。

ZI-data: Zero 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 文件浅析

总结如下:
在这里插入图片描述

2、加载、运行

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

应用程序具有静止状态和运行状态。静止态的程序被存储在非易失存储器中,如 STM32 的内部 FLASH,因而系统掉电后也能正常保存。但是当程序在运行状态的时候,程序常常需要修改一些暂存数据,由于运行速度的要求,这些数据往往存放在内存中(RAM),掉电后这些数据会丢失。因此,程序在静止与运行的时候它在存储器中的表现是不一样的,见下图。
在这里插入图片描述
程序在存储状态时,RO section 及 RW Section 都被保存在 ROM 区。当程序开始运行时,内核直接从 ROM 中读取代码,并且在执行主体代码前,会先执行一段加载代码,它把 RW Section 数据从 ROM 复制到 RAM,并且在 RAM 加入 ZI Section,ZI Section 的数据都被初始化为 0。加载完后 RAM 区准备完毕,正式开始执行主体程序。

编译生成的 RW-data 的数据属于图中的 RW Section,ZI-data 的数据属于图中的 ZI Section。是否需要掉电保存,这就是把 RW-data 与 ZI-data 区别开来的原因:

  • 因为在 RAM 创建数据的时候,默认值为 0,
  • 但如果有的数据要求初值非 0,那就需要使用 ROM 记录该初始值,运行时再复制到 RAM。

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

当程序存储到 STM32 芯片的内部 FLASH 时(即 ROM 区),它占用的空间是 Code、RO-data 及 RW-data 的总和,所以如果这些内容比STM32 芯片的 FLASH 空间大,程序就无法被正常保存了。当程序在执行的时候,需要占用内部 SRAM 空间(即 RAM 区),占用的空间包括RW-data 和 ZI-data。应用程序在各个状态时各区域的组成见下表。
在这里插入图片描述
而这些区域的起始地址和大小,以及各个函数变量应该放在哪个存储器区域中就是由本文要讲的 sct 文件定义的。

二、sct 分散加载文件

1、简介

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

可以设置源文件中定义的所有变量自动按地址分配到外部 SDRAM,这样就不需要再使用关键字 attribute 按具体地址来指定了;

利用它还可以控制代码的加载区与执行区的位置,例如可以把程序代码存储到单位容量价格便宜的 NAND-FLASH 中,但在 NAND-FLASH 中的代码是不能像内部 FLASH 的代码那样直接提供给内核运行的,这时可通过修改分散加载文件,把代码加载区设定为 NAND-FLASH 的程序位置,而程序的执行区设定为 SDRAM 中的位置,这样链接器就会生成一个配套的分散加载代码,该代码会把 NAND-FLASH 中的代码加载到 SDRAM 中,内核再从 SDRAM 中运行主体代码,大部分运行 Linux 系统的代码都是这样加载的。

MDK 生成一个以工程名命名的后缀为 *.sct 的分散加载文件 (Linker Control File,scatter loading),链接器根据该文件的配置分配各个节区地址,生成分散加载代码,因此我们通过修改该文件可以定制具体节区的存储位置。

工程构建时, MDK 会根据我们选择的芯片型号,获知芯片的内部 FLASH 及内部SRAM 存储器概况。这里我选择的是STM32F103VET6型号, 这款单片机有 64 KB 的 SRAM,512 KB的 ROM 内存,可以通过规格书查到。
在这里插入图片描述
当选好型号后,Target页面的ROM和RAM设置就已经按默认的自动配置好了。
在这里插入图片描述
ROM size为0x80000即512k字节,RAM size为0x10000即64k字节,与规格书一致。

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)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00020000  {
     ; RW data(可读写数据)
   .ANY (+RW +ZI)
  }                             
}

在默认的 sct 文件配置中仅分配了 Code、RO-data、RW-data 及 ZI-data 这些大区域的地址,链接时各个节区(函数、变量等)直接根据属性排列到具体的地址空间。

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

2.1 加载域

sct 文件的加载域格式如下:

加载域名 (基地址 | ("+"地址偏移)) [属性列表] [最大容量]
"{"
	执行区域描述+
"}"

本例中为:
LR_IROM1 0x08000000 0x00080000  {
   
	...
}

1.加载域名: 在 map 文件中的描述会使用名称 LR_IROM1 来标识空间。

2.基地址 + 地址偏移:基地址为 STM32 内部 FLASH 的基地址 0x08000000,地址偏移可选

3.属性列表: 指出当前加载域的属性列表。加载域可以继承先前加载域的属性。主要有以下这些(多个使用时以空格间隔开):
(1)ABSOLUTE : 指定将内容放置在链接后不会更改的基地址所表示的固定地址上。 这是默认值 ,除非使用了 PI 或 RELOC 。
(2)ALIGN alignment : 指定对齐约束。alignment 为阿拉伯数字,最小为 4,必须是 2 的整数次幂。例如, ALIGN 4 。基地址的值必须符合该对齐约束。如果使用了 +offset ,连接器将计算并使用对齐后的地址。
(3)NOCOMPRESS : RW 数据压缩默认情况下处于启用状态。 使用 NOCOMPRESS 关键字可以指定在最终镜像中不得压缩加载域的内容。
(4)OVERLAY : 使用 OVERLAY 关键字可以在同一地址具有多个加载域。 ARM 工具不提供覆盖机制。 要在同一地址使用多个加载域,必须提供自己的叠加层管理器。 该属性不能被继承。
(5)PI : 表示当前域与位置无关。 内容不依赖于任何固定地址,并且在链接后无需任何额外处理即可移动。(如果镜像中包含 XO,则不支持此属性。)
(6)PROTECTED : 该关键字将阻止以下情况:
Overlapping of load regions
Veneer sharing
String sharing with the --merge option.
(7)RELOC : 指出当前域是可重定位的。 内容取决于固定地址。 输出重定位信息,以使内容可以通过另一个工具移动到另一个位置。(如果镜像中包含 XO,则不支持此属性。)

最大容量: 最大容量说明了这个加载域可使用的最大空间,该配置也是可选的,如果加上这个配置后,当链接器发现工程要分配到该区域的空间比容量还大,它会在工程构建过程给出提示。STM32 内部 FLASH 的大小 0x00080000(512KB)

有关属性的继承,参考:

LR1 0x8000 PI    ; 默认为 ABSOLUTE
{
   }
LR2 +0             ; LR2 从 LR1 继承 PI 属性
{
   }
LR3 0x1000         ; LR3 不继承 LR2 的任何属性,因为它没有相对基地址,恢复默认为 ABSOLUTE
{
   }
LR4 +0             ; LR4 继承 LR3 的  ABSOLUTE 属性
{
   }
LR5 +0 RELOC       ; LR5 不继承 LR4 的任何属性,因为它显式设置了 RELOC
{
   }
LR6 +0 OVERLAY     ; LR6 不继承 LR5 的任何属性,因为它显式设置了 OVERLAY
{
   }
LR7 +0             ; LR7 无法继承 OVERLAY,恢复默认为 ABSOLUTE
{
   }

2.2 执行域

执行域名 (基地址 | "+"地址偏移) [属性列表] [最大容量 ]
### 编辑和配置 SCT Scatter 文件 #### 访问 Pack Installer 为了访问并安装必要的包,在Keil MDK环境中,通过点击界面中的Pack Installer图标,或者在菜单栏中选择`Project -> Manage -> Pack Installer`来进入Pack管理器[^1]。 #### 配置 Target 设置文件中的 Linker 页面 对于分散加载的设置位于Target选项卡下的Linker页面。在此页面内,取消左上角的选择框可以解锁Scatter File编辑权限,允许用户自定义链接脚本的内容[^2]。 #### 定义 Two-Region Model 的特殊执行域 当采用Two-Region模型时,需在散列文件里声明两个特定区域——`ARM_LIB_HEAP`与`ARM_LIB_STACK`。这两个部分均带有EMPTY属性标签,指示编译工具链不为这些段分配初始数据。具体来说: ```text Image$$ARM_LIB_STACK$$Base Image$$ARM_LIB_STACK$$ZI$$Limit Image$$ARM_LIB_HEAP$$Base Image$$ARM_LIB_HEAP$$ZI$$Limit ``` 上述符号用于替代默认的堆栈初始化机制(`__user_setup_stackheap`),从而实现更灵活的内存布局控制[^3]。 #### 初始化 RAM 函数及其放置位置 考虑到某些情况下希望将函数置于RAM而非闪存运行以提高性能或响应速度的情况,可以通过指定`.o(.ramfunc)`的方式让启动代码负责把这部分指令复制到随机存储器(RAM)中去。一旦完成这一过程,相应的函数就能直接从更快捷的工作区内被执行了;这尤其适用于诸如STM32之类的微控制器平台,它们支持既能在只读存储(Flash)也能在易失性存储(RAM)间切换程序流路径的操作模式[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值