当前位置: 首页 > news >正文

深入解析SCT分散加载文件

在原有的sct的目录下新建一个文件夹放我们自己的sct文件

可以这样找到sct文件

1、什么是分散加载文件?

分散加载文件通常以.sct结尾,英文名是:Linker Control File,scatter loading,链接器根据这个文件的配置来分配各个节区的地址空间,并且生成分散加载代码,因此我们只要修改分散加载文件,链接器就能自动帮我们确定代码、变量等这些内容在内存中(Flash和RAM上)的地址。

2、分散加载文件在什么时候起作用?

它主要是在链接阶段产生作用,如果你之前用过gcc+makefile编译mcu项目,那可以看到在目录中会有.ld结尾

的文件,这个文件和我们这里的分散加载文件作用类似,都是在链接阶段起作用。

编译链接流程:

3.分散加载文件的格式

这个 sct 文件是 ARM Cortex-M 系列芯片的分散加载描述文件,用于定义程序在编译后,代码和数据在 Flash(ROM)与 RAM 中的存储地址和分配规则。

1.核心结构解析:3 个关键区块

整个文件由「加载域(Load Region)」和其包含的「执行域(Execution Region)」组成,加载域对应物理存储(如 Flash),执行域对应实际运行时的存储(如 Flash 或 RAM)。

(1)加载域:LR_IROM1

这是整个程序的总加载区域,所有代码最终会被烧录到这个区域。

  • 0x08000000:加载起始地址,是 STM32 等主流 Cortex-M 芯片的Flash 默认起始地址

  • 0x00080000:加载区域大小,即 512KB(16 进制0x80000 = 十进制 524288 字节 = 512KB),代表 Flash 的可用空间。

  • 作用:规定了程序 “烧录到哪里” 以及 “最多能烧录多大”。

(2)执行域 1:ER_IROM1(代码区)

这是只读代码和数据的执行区域,与加载域地址相同,说明代码直接在 Flash 中运行(无需复制到 RAM)。

  • 0x08000000:执行起始地址,与加载地址一致,对应 Flash 地址。

  • 0x00080000:执行区域大小,与加载区域大小一致,限制 Flash 中可运行的代码总量。

  • 内部包含的内容(按优先级顺序):

    • *.o (RESET, +First):所有目标文件的「复位向量表(RESET)」,且强制放在该区域的最开头(芯片上电后最先读取复位向量)。

    • *(InRoot$$Sections):链接器内部使用的关键段,确保 C 库初始化等核心代码被正确包含。

    • .ANY (+RO):所有目标文件的「只读数据段(RO)」,包括代码指令、const 修饰的常量。

    • .ANY (+XO):所有目标文件的「可执行只读数据段(XO)」,通常是编译器优化后的只读代码或数据。

(3)执行域 2:RW_IRAM1(数据区)

这是可读写数据的执行区域,对应芯片的 RAM,程序运行时的变量会存放在这里。

  • 0x20000000:执行起始地址,是 STM32 等 Cortex-M 芯片的RAM 默认起始地址

  • 0x00020000:执行区域大小,即 128KB(16 进制0x20000 = 十进制 131072 字节 = 128KB),代表 RAM 的可用空间。

  • 内部包含的内容:

    • .ANY (+RW +ZI):所有目标文件的「可读写数据段(RW)」和「零初始化数据段(ZI)」。

      • RW:已初始化的全局变量 / 静态变量(如int a = 10;),上电后会从 Flash 复制到 RAM。

      • ZI:未初始化的全局变量 / 静态变量(如int b;),上电后会被自动清零。


2.关键规则与作用

  1. 地址映射规则:明确 Flash(0x08000000 开始)存代码和只读数据,RAM(0x20000000 开始)存可读写变量,避免存储冲突。

  2. 大小限制规则:Flash 最大用 512KB,RAM 最大用 128KB,超过会导致编译报错,防止程序超出芯片硬件资源。

  3. 优先级规则:复位向量表强制放 Flash 最开头,确保芯片上电后能正确找到启动入口,这是 Cortex-M 芯片启动的硬性要求。


3.适用场景与芯片推测

从地址和大小来看,这个 sct 文件大概率适用于STM32F103 系列芯片(如 STM32F103VET6,Flash 512KB、RAM 64KB,或 STM32F103ZET6,Flash 512KB、RAM 128KB),是该系列芯片的通用分散加载配置。

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************LR_IROM1 0x08000000 0x00070000  {    ; load region size_regionER_IROM1 0x08000000 0x00070000  {  ; load address = execution address*.o (RESET, +First)*(InRoot$$Sections).ANY (+RO).ANY (+XO)}RW_IRAM1 0x20000000 0x00020000  {  ; RW data.ANY (+RW +ZI)*.o (myram)}
}LR_IROM2 0x08070000 0x00010000  {    ; load region size_regionER_IROM2 0x08070000 0x00010000  {  ; load address = execution address*.o (myflash)}
}

这个 sct 文件是在之前基础上扩展的分散加载描述文件,新增了一个独立的加载域和执行域,用于管理自定义数据段(myflashmyram)。下面详细解析其结构和作用:

一、整体结构概述

文件包含两个加载域(LR_IROM1、LR_IROM2),每个加载域下各包含一个执行域(ER_IROM1、ER_IROM2),同时保留了原有的 RAM 执行域(RW_IRAM1)。核心变化是新增了LR_IROM2加载域和myflashmyram自定义段,用于将特定数据分配到指定的 Flash 或 RAM 区域。

二、各区域详细解析

  1. 第一个加载域:LR_IROM1(主程序区)

  • 加载地址0x08000000(Flash 起始地址,同前)

  • 大小0x00070000(448KB,相比之前的 512KB 减少了 64KB,预留空间给新增的 LR_IROM2)

  • 作用:存储程序主体(代码、复位向量、只读数据等)。

  • 其包含的执行域ER_IROM1

    • 地址和大小与 LR_IROM1 一致(0x080000000x00070000),说明代码直接在 Flash 中运行。

    • 内容与之前基本相同:

      • 复位向量表(RESET)、链接器核心段(InRoot$$Sections)、只读代码 / 数据(+RO+XO)。

  1. RAM 执行域:RW_IRAM1(数据区)

  • 地址0x20000000(RAM 起始地址,同前)

  • 大小0x00020000(128KB,未变)

  • 新增内容:*.o (myram)

    • 含义:所有目标文件中标记为myram的段,会被强制分配到这个 RAM 区域。

    • 用途:可通过代码中的__attribute__((section("myram")))修饰变量,将其指定存放在此 RAM 区域(例如:int buf[1024] __attribute__((section("myram")));)。

  1. 第二个加载域:LR_IROM2(自定义 Flash 区)

  • 加载地址0x08070000(Flash 偏移地址,位于 LR_IROM1 之后:0x08000000 + 0x00070000 = 0x08070000

  • 大小0x00010000(64KB,正好是 LR_IROM1 减少的空间,避免地址重叠)

  • 作用:专门用于存储标记为myflash的自定义数据(非程序主体,如校准参数、配置表等)。

  • 其包含的执行域ER_IROM2

    • 地址和大小与 LR_IROM2 一致(0x080700000x00010000),数据直接存储在该 Flash 区域,运行时无需复制到 RAM。

    • 内容:*.o (myflash)

      • 含义:所有目标文件中标记为myflash的段,会被强制分配到这个 Flash 区域。

      • 用途:通过__attribute__((section("myflash")))修饰常量,将其固定存放在此区域(例如:const char config_data[] __attribute__((section("myflash"))) = "calib_param";)。

三、关键变化与用途

  1. Flash 空间拆分:将原 512KB Flash 拆分为两部分:

    1. 主程序区(448KB,0x08000000~0x0806FFFF):存代码和默认只读数据。

    2. 自定义区(64KB,0x08070000~0x0807FFFF):存专用数据(如固件参数、日志模板等),避免与主程序混杂。

  2. 自定义段管理

    1. myflash段:强制存放在指定 Flash 区域,适合存储需要持久化、不随程序更新的只读数据(如硬件校准值)。

    2. myram段:强制存放在指定 RAM 区域,适合管理特殊用途的变量(如高频访问的缓存、DMA 缓冲区等)。

  3. 地址冲突避免:两个加载域(LR_IROM1 和 LR_IROM2)的地址范围连续且不重叠(0x08000000~0x0806FFFF0x08070000~0x0807FFFF),确保编译和运行时无地址冲突。

四、适用场景

此配置适合需要在 Flash 中划分专用区域存储自定义数据的场景,例如:

  • 嵌入式设备的出厂参数(需固定地址,方便读取);

  • 程序中需要独立管理的大段只读数据(如字库、图片资源);

  • 对 RAM 有特殊分区要求的场景(如隔离普通变量和 DMA 缓冲区)。

如果需要进一步指定myflashmyram的地址范围(而非默认占满分配的空间),可以在段定义中细化,例如:*.o (myflash) 0x08070000 0x00008000(限制myflash仅用前 32KB)。

示例解析:

__attribute__ ((used, section ("myflash"))) 
void flash_function_pre()
{int i = 0;i = i / 3;return;
}void flash_function()
{int i = 0;i = i / 3;return;
}__attribute__ ((used, section ("myram"))) 
void sram_function(void)
{int i = 0;i = i / 3;return;
}__attribute__ ((used, section ("myram"))) 
void sram_function_pre(void)
{int i = 0;i = i / 3;return;
}uint32_t call_flash_func_time_record(void)
{uint32_t start_tick = HAL_GetTick();uint32_t end_tick = 0;uint32_t i_counter = 10000000;void (* volatile fpointer_1)(void) = flash_function;void (* volatile fpointer_2)(void) = flash_function_pre;while( i_counter > 0 ){fpointer_1();fpointer_2();i_counter --;}end_tick = HAL_GetTick();return (end_tick - start_tick);
}__attribute__ ((used, section ("myram"))) 
uint32_t call_sram_func_time_record(void)
{uint32_t start_tick = HAL_GetTick();uint32_t end_tick = 0;uint32_t i_counter = 10000000;void (* volatile fpointer_1)(void) = sram_function;void (* volatile fpointer_2)(void) = sram_function_pre;while( i_counter > 0 ){fpointer_1();fpointer_2();i_counter --;}end_tick = HAL_GetTick();return (end_tick - start_tick);
}

通过attribute((section("xxx")))将 4 个函数分别指定到sct文件定义的自定义段中:

  • flash_function_pre():被section("myflash")修饰,存放在LR_IROM2对应的 Flash 区域(0x08070000~0x0807FFFF)。

  • flash_function():未修饰,默认存放在主 Flash 区域(ER_IROM10x08000000~0x0806FFFF)。

  • sram_function()sram_function_pre():被section("myram")修饰,存放在RW_IRAM1对应的 RAM 区域(0x20000000~0x2001FFFF)。

  • 附加used属性:确保编译器不会因 “未被直接调用” 而优化删除这些函数(因为通过函数指针间接调用)。

简单说,used属性的核心作用是强制编译器 “保留” 某个函数 / 变量,哪怕代码里没有直接调用它,也不会把它当成 “无用代码” 删掉 —— 这正好适配你代码里 “用函数指针间接调用” 的场景。

1.先搞懂:编译器为什么会 “删代码”?

编译器在编译时会做 “死代码消除(Dead Code Elimination)” 优化。它会扫描代码,判断哪些函数 / 变量 “没用”:

  • 对函数来说,“没用” 的标准是没有任何地方直接调用它(比如没写过flash_function_pre();这种直接调用)。

  • 编译器默认不知道 “函数指针会间接调用它”,会误判这类函数是 “死代码”,最终不把它编译到最终的可执行文件(.bin/.hex)里。

举个例子:如果flash_function_pre()没加used属性,编译器看到代码里只有fpointer_2 = flash_function_pre;(赋值给指针),没有flash_function_pre();(直接调用),就会认为这个函数没用,把它从编译结果里删掉。

2.再看:used属性是怎么 “保住” 代码的?

attribute((used))是 GCC 编译器的一个特殊属性,它给编译器发了一个明确的指令:

  • “这个函数 / 变量是有用的,哪怕你没看到直接调用,也必须把它保留在最终的代码里,不能删!”

  • 这样一来,被used修饰的flash_function_pre()sram_function_pre(),即使只被赋值给函数指针,也能正常编译到指定的myflashmyram段里,后续用指针调用时才不会出现 “找不到函数” 的错误(比如硬件异常、程序跑飞)。

3.结合代码更直观

代码里,flash_function_pre()sram_function_pre()的调用逻辑是 “间接的”:

  1. 先把函数地址赋值给指针:fpointer_2 = flash_function_pre;

  2. 再通过指针调用:fpointer_2();

如果没加used,编译器在第一步时,会因为没看到flash_function_pre()的直接调用,先把它删掉;到第二步用指针调用时,指针指向的地址是 “空的” 或者 “无效的”,程序就会出错。而加了used之后,函数被保留,指针能正确指向函数地址,调用才会正常执行。

简单总结:used属性就是给编译器 “开特例”,专门应对 “间接调用” 这种编译器默认识别不了的场景,确保关键代码不被误删。

http://www.dtcms.com/a/511216.html

相关文章:

  • AIGC-Fooocus部署实践:从本地手动配置到云端一键启用的深度剖析
  • 数据结构——最小(代价)生成树
  • NumPy的hstack函数详细教程
  • 020数据结构之优先队列——算法备赛
  • 华为OD-23届考研-测试面经
  • 阿里云网站建设步骤wordpress防止频繁搜索
  • 西宁网站建设哪家公司好东莞seo网站推广
  • 2025年AI IDE的深度评测与推荐:从单一功能效率转向生态壁垒
  • OSS存储的视频,安卓和PC端浏览器打开正常,苹果端打开不播放,什么原因?
  • Spark的shuffle类型与对比
  • 【 论文精读】VIDM:基于扩散模型的视频生成新范式
  • CentOS 7 安装指定内核版本与切换内核版本
  • Spring MVC 拦截器interceptor
  • 如何在 CentOS、Ubuntu 和 Debian 云服务器上安装 Python 3
  • 《金融电子化》:构建金融韧性运行安全体系:从灾备管理到主动防御新范式​​
  • spark组件-spark core(批处理)
  • 进行网站建设视频教程装修网站cms
  • 解决Kali虚拟机中VMnet1(仅主机模式)网卡无法获取IP地址的问题
  • Linux驱动开发笔记(十一)——阻塞和非阻塞IO
  • Docker----快速入门
  • 深度学习8-卷积神经网络-CNN概述-卷积层-池化层-深度卷积神经网络-案例:服装分类
  • 厦门做外贸网站国内十大咨询公司排名
  • 架构设计过去十年与未来十年
  • Nginx 日志轮转
  • 《Linux运维总结:基于ARM64+X86_64架构CPU使用docker-compose一键离线部署mongodb 7.0.22容器版副本集群》
  • 《Linux运维总结:基于ARM64+X86_64架构CPU使用docker-compose一键离线部署mongodb 7.0.22容器版分片集群》
  • MongoDB基础与Mongoose ODM
  • 做定制网站价格教做flash的网站
  • 【流量控制】算不对 GBN 窗口?分不清 SR 重传?滑动窗口 + 3 大协议一篇吃透
  • 临时插入的紧急任务如何影响整体进度