Skip to content

LV035-SRAM APP设计 ​

一、 运行原理 ​

1. 为什么代码可以直接在 SRAM 里跑 ​

Cortex-M3 的取指只看地址落在哪个总线区域:0x20000000 起是 SRAM,挂在 D-bus / I-bus 上,同样可以被取指。只要满足三个条件,代码在哪块可以随机访问的存储器里都能执行:

  • 链接时的执行地址就是实际装载地址(位置相关代码,不能被搬运到别处);
  • 镜像的向量表在运行时被 SCB->VTOR 指向,中断才能找到处理函数;
  • 装载完成后按向量表把 MSP 换成镜像自己的栈顶。

sram-app 就是把这三点做到位的一个普通工程,没有任何"特殊运行方式"。它唯一特殊的地方在于:运行位置不是落盘位置。

2. 掉电即失 ​

SRAM 是易失介质,sram-app 一旦断电就没了,重新上电后 0x20007000 处是随机值。所以它不能像 flash-app 那样"烧一次、上电就跑",每次都要经过一次装载:

阶段介质谁来做
保存内部 Flash 第 3 块 0x08040000Bootloader 收到 app.img 后写入
装载内部 SRAM 0x20007000Bootloader 收到 go sram 后整块 memcpy
运行内部 SRAM 0x20007000跳转后由镜像自己执行

掉电后 Flash 里的那一份还在,重新上电发一条 go sram 就能再装载一次,不必重发镜像。这是把"暂存区"放进 Flash、而不是"收完直接写 SRAM"的直接收益。

3. 两条进入途径 ​

途径谁触发发生了什么
IAP 装载串口/TCP 发 app.img,再发 go sram写暂存区 → 校验 → 拷进 SRAM → 跳转
调试器直接下载sram-app-debug + SWD链接在 0x08000000,下载后上电即跑,用于验证业务逻辑

第二条途径用的是另一个 Target,链接地址不同,跑起来占的是 Flash 第 1 块——它会覆盖 Bootloader,所以只用于"先把业务代码调通"的阶段。

二、 链接地址 ​

1. IROM1 与 IRAM1 ​

项取值说明
IROM1(代码容器)0x20007000,大小 0x6000(24KB)镜像装载与执行的位置
IRAM1(数据区)0x2000D000,大小 0x2C00(11KB).data/.bss/堆/栈
栈顶0x2000FC00由向量表第 1 项给出
NOINIT0x2000FC00 起 1KB复位不清零的保留区,Bootloader 与 APP 共用约定

内部 FLASH 与 SRAM 地址分区

2. 三个边界为什么这样切 ​

  • 0x20000000 ~ 0x20007000 留给 Bootloader:它的 RW/ZI 实际用到 0x20005E18,剩下的空间是余量,不能拿来放 APP;
  • 0x20007000 ~ 0x2000D000 正好 24KB,是代码容器。取整到 4KB 边界是为了页对齐和调试查看方便;
  • 0x2000D000 ~ 0x2000FC00 放 APP 的全局变量与栈,与代码区隔开,避免栈溢出直接把指令覆盖掉(栈溢出会先撞到 NOINIT 区,症状比重叠代码更好查)。

3. 三处必须同步 ​

位置内容不同步的后果
sram-app.uvprojx 的 IROM10x20007000 / 0x6000链接出的镜像按错误地址编址,装载后必然跑飞
iap.h 的 IAP_SRAM_APP_BASE/MAX_SIZE0x20007000 / 24KB帧头地址校验拒绝,或搬运越界
pack_iap.py 的 SRAM_APP_BASE/END同上打包时就报 address or size out of range

三、 功能说明 ​

1. 功能清单 ​

功能说明
串口日志与 flash-app 相同,115200,fputc 轮询发送
行命令 reboot打印后等 TC,软复位回 Bootloader
串口回显收到整行原样回发,用于验证装载后外设是否正常
看门狗iwdg_start() 后主循环喂狗,3 秒超时
网络无

2. 与 FLASH APP 的差异 ​

维度flash-appsram-app
链接地址0x080200000x20007000
落盘位置就是运行位置0x08040000 暂存
掉电后内容保留内容丢失,需重新 go sram
装载步骤写完即可跳写完还要搬运、回读校验
调试豁免flash-app-debug 链接 0x08000000sram-app-debug 同样链接 0x08000000
代码体积上限128KB24KB(容器限制)

24KB 的上限是实打实的约束:一旦业务代码把 .text 撑到 24KB 以上,链接阶段就会报 region 溢出,此时只能压缩代码或改分区。

四、 启动与向量表 ​

1. 进入 main 之前 ​

跳转是"硬跳":Bootloader 读暂存区向量表拿到 MSP 与复位向量,设好 SCB->VTOR,__set_MSP(),然后跳到 Reset_Handler。于是 sram-app 的启动流程与普通 Flash 启动完全一致:

(1)Reset_Handler 调 SystemInit(),配时钟与 Flash 预取;

(2)__main 把 .data 初值从镜像里拷到 0x2000D000 起,把 .bss 清零;

(3)main() 里第一件事是把 VTOR 指向自己。

2. VTOR 必须在 HAL_Init 之前 ​

c
// sram-app/Core/Src/main.c
#ifndef APP_BASE
#define APP_BASE   0x20007000U      /* IAP_SRAM_APP_BASE: 与 IROM1 / pack_iap.py 一致 */
#endif

/* 向量表重定位到 APP 基址: 必须早于 HAL_Init(), 否则中断仍会走 Bootloader 的向量表 */
SCB->VTOR = APP_BASE;
HAL_Init();

HAL_Init() 里会配 SysTick 并开中断,如果那时 VTOR 还没改,SysTick 中断会按 Bootloader 的向量表跳到 0x08000000 + 0x3C——落进 Bootloader 的代码里,症状是随机 HardFault 或"程序莫名跑回 Bootloader"。

同样的道理,SystemInit() 里也不要依赖中断:它执行时 VTOR 还是 Bootloader 设的值(恰好正确),但一旦有别的镜像经由其它路径进入,就会踩到同一个坑。

3. 中断都落在 SRAM 里 ​

VTOR 指向 0x20007000 之后,所有异常向量都指向 SRAM 中的处理函数。这也意味着装载完成前不能开中断:Bootloader 在 memcpy 期间保持自己的中断设置,拷完校验通过才切 VTOR 并跳转。

五、 构建与装载 ​

1. 构建 ​

powershell
# release: 链接 0x20007000,构建后由 pack_iap.py -a 0x20007000 打成 app.img
.\build_sram.ps1 -b

# 调试: 链接 0x08000000,可 SWD 直接下载运行
.\build_sram.ps1 -b -t debug -d

release 构建的产物序列是:sram-app.axf → fromelf --bin → sram-app.bin → pack_iap.py -a 0x20007000 → app.img。

2. 装载流程 ​

步骤命令校验
写入暂存区发 app.img(串口或 TCP)帧 CRC32 + 回读 CRC32
搬运go sram向量表三条判据 + 搬运后 CRC32
跳转同上iap_load_app(0x20007000) 再查一次向量表

搬运这一步的细节值得单独看清:从 Flash 暂存区到 SRAM 容器是两块不同介质之间的拷贝,长度固定取容器容量 24KB(24576 字节),而不是帧头里记录的实际长度——暂存区没写到的尾部是 0xFF,落在容器内永远不会被执行到。

SRAM APP 的搬运与跳转流程

3. 常见问题 ​

现象原因处理
go sram 提示 no sram-app image in flash staging暂存区没写过先发 app.img,确认落盘成功
跳转后立即 HardFault镜像按 0x08000000 链接(用了 debug Target)用 release 构建重新打包
运行一段时间后复位24KB 容器被写穿,或看门狗未喂查 .map 的 region 占用与喂狗周期
复位后 APP 消失正常现象,SRAM 易失重新发 go sram
中断进了 Bootloader 的处理函数VTOR 未在 HAL_Init() 之前设置检查 main() 开头的 SCB->VTOR = APP_BASE

本文档由 markdowncli 技能辅助生成