Keil编译后的Code,RO,RW,ZI分别表示什么以及和芯片Flash、SRAM的对应关系

转载自:

https://www.cnblogs.com/luckytimor/p/7182629.html

在使用keil开发STM32应用程序时,点击Build后在Build Output窗口中经常会有如下信息:

<ignore_js_op> 

以前一直好奇这几个参数和实际使用的STM32芯片中Flash和SRAM的对应关系,于是上网搜了一圈,做如下总结:

  • 这些参数的单位是Byte
  • 图中几个参数分别代表 
    • Code:代码的大小
    • RO:常量所占空间
    • RW:程序中已经初始化的变量所占空间
    • ZI:未初始化的static变量和全局变量以及堆栈所占的空间
  • 上述参数和芯片Flash以及SRAM的对应关系是 
    • Flash占用大小=Code+RO+RW
    • SRAM占用大小=RW+ZI

不知道有没有人会像我一样好奇为什么RW参数同时参与了Flash和SRAM占用量的计算。这是因为Flash部分的属性是Read-Only的,而SRAM虽然是Read-Write但里面数据不能掉电保存,所以只能把已经初始化的值保存到ROM里,上电后再拷贝到SRAM中进行读写操作,即两部分都需要留出RW变量所占用的空间。这里给出的解释比较详细,在此我参照着用STM32F407ZGT6图解一下。
STM32F407ZGT6的Flash大小为1MB,SRAM大小为(128KB+64KB)。这里SRAM之所以分开表示是因为在芯片内部前面的128KB和后面的64KB地址不是连续的,后面的64KB在ST官方叫做CCM (core coupled memory) ,据说是由内核直接访问的,不能由外设访问(见原帖2楼)。下面给出的示意图中只标出了前面的128KB的SRAM空间。红色部分表示STM32F407ZGT6提供的Flash和SRAM大小。

<ignore_js_op> 

结合上图说下STM32F407ZGT6的启动过程(采用Cortex-M4、Cortex-M3内核的芯片基本都是这个过程):上电后首先从 0x00000000(映射到0x08000000,这里只考虑从内部Flash启动)处获得中断向量表,然后在运行用户代码之前会在标号2处有一段引导代码负责把存在Flash中的初始化变量的值Copy到SRAM中对应的变量位置(标号3),之后把ZI区全部清零(标号4),之后才正式开始运行用户代码(标号5)

<think>嗯,用户的问题是关于codeROData、RWData、ZIData、Debug这几个内存段程序栈空间设置的关系。看起来用户已经对内存分区有了基本理解,现在想深入探讨栈空间配置如何影响这些内存段。用户之前询问过STM32内存数据段的详细分配原理,现在的问题明显是上次讨论的延续。从对话历史看,用户特别关注编译输出的数据段统计信息(如TotalROSize、TotalRWSize等),这可能是实际开发中遇到了内存不足的问题。从专业角度看,栈空间内存段的关系主要体现在三个方面:位置关系、尺寸影响动态行为。栈作为ZIData的子集,位于RAM末端,但它的设置会直接影响整个RAM的分配结构。用户贴出的Keil编译输出很有价值,特别是那个TotalRWSize的计算公式。这里需要重点解释栈空间设置是如何通过ZIData间接影响总RAM占用的。比如增大栈空间会使ZIData区间扩张,可能导致其他内存区域被压缩。调试部分是个难点。栈溢出破坏ZIData的现象很隐蔽,需要演示具体的监测技术。watchdog方法值得推荐,但用户可能需要更直接的检测手段,比如用J-Link实时监控SP寄存器值。考虑到用户可能正在进行实际开发,应该强调几个关键实践点:-栈安全空间设置应基于最大函数调用深度(建议用静态分析工具计算)-RTOS中每个任务栈需要独立计算冗余-HardFault处理中增加栈深度记录功能最后生成的三个延伸问题都切中要害:编译器选项的影响是最容易被忽视的优化点;双栈操作问题多发生在RTOS开发中;而堆栈碰撞监测则是稳定性的最后防线。这些应该能覆盖用户后续可能遇到的典型场景。</think> 理解内存段与栈空间的关系对嵌入式系统稳定性至关重要,以下是关键逻辑梳理: --- ### 内存段与栈空间的关系 | **数据段** | **与栈空间的关系** | **配置影响** | |------------|----------------------|--------------| | **Code & RO Data** | 独立存在于FLASH | 不影响栈大小,优化可节省FLASH空间 | | **RW Data (运行时)** | 与栈共享RAM空间 | RAM紧张时需平衡RW Data与栈的空间 | | **ZI Data (含堆)** | 与栈共享RAM,栈常位于ZI Data末尾 | 增大栈会压缩ZI Data可用空间 | | **栈空间** | 从ZI Data区域尾部动态分配 | 需单独配置大小 | ```c // 内存布局示例 0x20000000 +---------------+ ← RAM起始 | RW Data | // 初始化全局变量(已占用) +---------------+ | ZI Data | // 未初始化变量 & 堆空间 | ... | +---------------+ ← _estack - Stack_Size | 栈空间 | // 由启动文件分配 +---------------+ ← _estack (栈顶) ``` --- ### 配置栈空间的关键点 1. **栈位置** 在启动文件(如`startup_stm32.s`)中定义栈顶位置: ```assembly Stack_Size EQU 0x1000 ; 定义栈大小(4KB) AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp EQU . ; 栈顶地址(随ZI Data变化)[^1] ``` 2. **栈溢出风险** - RW/ZI Data增长 → 栈空间被压缩 - 递归过深/大局部变量 → 栈溢出覆盖ZI Data ```c void recursive_danger(uint32_t depth) { int buffer[512]; // 大局部变量占栈空间 if (depth > 0) recursive_danger(depth-1); // 递归过深 } // 栈溢出可能破坏ZI Data区变量值 ``` 3. **调试方法** - **栈哨兵检测**:初始化时填充栈区魔数,定期检查 ```c #define STACK_MAGIC 0xDEADBEEF volatile uint32_t *stack_end = (uint32_t*)&__initial_sp; *stack_end = STACK_MAGIC; // 栈底设置魔数 ``` - **HardFault分析**:栈溢出时触发,通过SP寄存器定位溢出点 ```c void HardFault_Handler(void) { while(1) { uint32_t *sp = __get_PSP(); // 获取异常时栈指针 log("Fault SP=%p", sp); // 输出异常位置 } } ``` --- ### 配置建议 1. **计算最小安全栈** - RTOS任务栈:通常 ≥ 512B - 最大中断嵌套:中断栈深度 × 嵌套层数 - 函数调用树:静态分析工具(如`armcc --callgraph`)[^3] 2. **链接脚本优化** ```ld /* STM32F4链接脚本片段 */ _Min_Heap_Size = 0x200; /* 最小堆 */ _Min_Stack_Size = 0x800; /* 最小栈2KB */ ._user_heap_stack : { . = ALIGN(8); PROVIDE ( end = . ); PROVIDE ( _end = . ); . = . + _Min_Heap_Size; . = . + _Min_Stack_Size; . = ALIGN(8); } >RAM ``` 3. **监控实践** - Keil RTX5:启用栈水印(Stack Watermark) ```c osThreadAttr_t thread_attr = { .stack_size = 1024, .stack_mem = malloc(1024), // 动态分配栈 .cb_mem = &thread_tcb, .cb_size = sizeof(thread_tcb), }; osThreadNew(.., &thread_attr); ``` --- **典型错误案例** 某工控设备因增加TCP/IP协议栈后ZI Data增长至18KB(RAM总20KB),未调整栈大小导致串口中断中栈溢出,系统随机重启。解决方法: 1. 将TCP缓冲池移至外部SRAM 2. 增大栈空间至1.5KB ```diff - _Min_Stack_Size = 0x400; // 原1KB + _Min_Stack_Size = 0x600; // 修改为1.5KB ``` --- ### 总结关系 $$RAM\_{total} = RW\_{data} + ZI\_{data} + Heap\_{size} + Stack\_{size}$$ 栈空间是ZI Data区内的**独立预留块**,增大栈会直接减少可用ZI空间。务必在编译后确认: 1. `map`文件中栈地址位于ZI Data末端 2. RAM占用满足: $$RW\_{data} + ZI\_{data} < RAM\_{total} - Stack\_{size} - Heap\_{size}$$ ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值