ARM 之十 ARMCC(Keil) map 文件(映射文件)详解

本文深入探讨了ARMCC(Keil)编译器生成的map文件,解释了其作为映射文件的作用,内容涵盖map文件的来源、语法、用法以及Keil的配置选项。map文件包含了链接器的详细信息,如全局符号、交叉引用、内存映射等,对于程序分析和优化至关重要。文章还详细介绍了如何在Keil中配置生成map文件,并展示了map文件的典型内容,如Section Cross References、Memory Map等,帮助读者理解和利用map文件进行软件开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在看这篇文章之前

  1. 需要对 ARM ELF 文件有一定的了解。了解什么是域(Region)、节(Section,也称为节区)、段(Segment)、镜像(Image)、镜像文件(Image File)等概念
  2. 需要对编译、连接过程有一定的了解

map 文件是什么

  map 文件对应的中文名应该是映射文件,用来展示(映射)项目构建的链接阶段的细节。通常包含程序的全局符号、交叉引用和内存映射等等信息。目前,大多数编译套件(主要是其中的链接器)都可以生成 Map 文件。常见的 GCC、VC、IAR 都可以输出 map 文件(PC平台的 map 文件与 ARM 平台的差别较大)。

  在 ARM 的官方文档中,并没有找到有关于 ARM 内核的 map 文件的介绍文档。不过倒是有个 C51 生成的 map 文件的说明文档:Listing (MAP) File。但是 C51 的 map 文件和 ARM 核的 map 文件差别比较大,也没啥参考价值!</

#! armclang --target=arm-arm-none-eabi -mcpu=cortex-m7 -E -xc -I . ; ################################################################### ; ; Processors: STM32H743ZIxx ; Compiler: Keil ARM C/C++ Compiler ; Version: v1.0 LiuYH ; ; Abstract: ; Linker file for the Keil ARM C/C++ Compiler ; ; ################################################################### ; ; RAM/ROM Image: ; start size ; rom 0x08000000 0x00200000 (2048KB) ; dtcm 0x20000000 0x00020000 ( 128KB) ; ram_d1 0x24000000 0x00080000 ( 512KB) ; ram_d2 0x30000000 0x00048000 ( 288KB) ; ram_d3 0x38000000 0x00010000 ( 64KB) ; itcm 0x00000000 0x00010000 ( 64KB) ; ram_can 0x4000AC00 0x00002800 ( 10KB) ; ; ################################################################### ; ; FLASH Allocate: ; start size ; iap 0x08000000 0x00040000 ( 256KB) ; app 0x08040000 0x00080000 ( 512KB) ; user_param 0x080C0000 0x00040000 ( 256KB) ; iap_backup 0x08100000 0x00040000 ( 256KB) ; app_backup 0x08140000 0x00080000 ( 512KB) ; unuse 0x081C0000 0x00040000 ( 256KB) ; ; RAM Allocate: ; start size ; iap_ram 0x20000000 0x00000400 ( 1KB) ; dtcm 0x20000400 0x0001FC00 ( 127KB) ; ... ; ; ################################################################### ; ************************************************************* ; *** Scatter-Loading Description File generated by uVision *** ; ************************************************************* #include "user_config.h" LR_IROM1 BSP_FLASH_START_ADDRESS BSP_FLASH_SIZE { ; load region size_region ER_IROM1 BSP_FLASH_START_ADDRESS BSP_FLASH_SIZE { ; load address = execution address *.o (RESET, +First) *(InRoot$$Sections) .ANY (+RO) } ;define IAP_RAM execution region #ifdef MC_USING_IAP IAP_RAM BSP_IAPRAM_BASE UNINIT BSP_IAPRAM_SIZE { ; RW data - 1KB DTCM *(.bss.NoInit) } #endif ;define ARM_LIB_STACK execution region ;start address is 0x20000400,size is 0x800(2K),8byte align,fill with 0xDEADBEEF ARM_LIB_STACK BSP_DTCRAM_BASE ALIGN 8 FILL 0xDEADBEEF EMPTY BSP_STACK_SIZE { } ;RW_IRAM1 follow behind ARM_LIB_STACK RW_IRAM1 +0 { ; RW data .ANY (+RW +ZI) } ;define ARM_LIB_HEAP follow behind RW_IRAM1 ARM_LIB_HEAP +0 ALIGN 8 EMPTY BSP_HEAP_SIZE { } ;ScatterAssert() check ARM_LIB_HEAP address not exceed 0x20020000 ScatterAssert(ImageLimit(ARM_LIB_HEAP) <= BSP_DTCRAM_LIMIT) RW_IRAM2 0x24000000 0x00080000 { ; RW data - 512KB AXI SRAM *(.RAM_D1) } RW_IRAM3 0x30000000 0x00040000 { ; RW data - 128KB SRAM1(0x30000000) + 128KB SRAM2(0x3002 0000) *(.RAM_D2) } RW_ETH_RX_DESC 0x30040000 0x00000060 { *(.RxDecripSection) } RW_ETH_TX_DESC 0x30040060 0x00000060 { *(.TxDecripSection) } RW_ETH_RX_BUF 0x30040100 0x00001800 { *(.RxArraySection) } RW_ETH_TX_BUF 0x30041900 0x00001800 { *(.TxArraySection) } RW_IRAM4 0x38000000 0x00010000 { ; RW data - 64KB SRAM4(0x38000000) *(.RAM_D3) } }
最新发布
06-18
<think>我们正在针对STM32H743ZIxx处理器,使用KeilARM编译器(ARMCCARMCLANG)进行开发。需要了解如何通过scatter文件(分散加载文件)分配堆栈空间和内存区域,特别是IAP_RAM、ARM_LIB_STACK和ARM_LIB_HEAP的定义。###关键点:1.**STM32H743ZI内存布局**:-该芯片具有多块RAM区域,包括:-DTCMRAM(0x20000000,128KB)-紧耦合数据内存,速度快,通常用于堆栈和关键数据。-SRAM1(0x24000000,512KB)-通用RAM。-SRAM2(0x30000000,288KB)-通用RAM。-SRAM3(0x38000000,64KB)-通用RAM(部分可保留为IAP_RAM)。-ITCMRAM(0x00000000,64KB)-紧耦合指令内存,用于代码。-其他(如备份RAM等)。2.**Scatter文件的作用**:-控制代码和数据的存放位置。-定义内存区域的起始地址和大小。-指定堆栈和堆的位置。3.**堆栈和堆的定义**:-**堆栈(Stack)**:通常由链接器生成的符号`ARM_LIB_STACK`定义,并在启动代码中初始化栈指针(SP)。-**堆(Heap)**:通常由链接器生成的符号`ARM_LIB_HEAP`定义,C库使用它来动态分配内存。4.**IAP_RAM**:通常指用于In-ApplicationProgramming(IAP)的RAM区域,即在程序运行时用于存储固件更新数据的RAM。我们通常将其放在一个不会干扰主程序运行的RAM区域(例如SRAM3)。###步骤:####1.创建Scatter文件(例如`STM32H743ZI.sct`)```scatter;定义内存区域LR_IROM10x080000000x00200000{;加载区域(Flash起始地址0x08000000,大小2MB)ER_IROM10x080000000x00200000{;执行区域(代码和只读数据)*.o(RESET,+First);向量表*(InRoot$$Sections);库的初始化代码.ANY(+RO);所有只读数据};定义RAM区域;DTCMRAM(128KB)-用于栈和堆等RW_IRAM10x200000000x00020000{;DTCM:128KB(0x20000000-0x2001FFFF).ANY(+RW+ZI);普通读写数据*(RESET_RAM);如果有在RAM中运行的代码,可以放这里};SRAM1(512KB)-通用数据RW_IRAM20x240000000x00080000{.ANY(+RW+ZI)};SRAM3(64KB)-保留为IAP_RAMRW_IRAM30x380000000x00010000{*(IAP_RAM);所有目标文件中标记为IAP_RAM的段都放在这里};堆栈区域-通常放在DTCMRAM的末端(因为栈向下增长,堆向上增长)ARM_LIB_STACK0x20020000EMPTY-0x4000{};栈:16KB(从0x20020000向下生长,实际使用0x2001C000-0x20020000)ARM_LIB_HEAP0x2001C000EMPTY0x4000{};堆:16KB(从0x2001C000开始向上生长)}```####2.解释:-**`ARM_LIB_STACK`和`ARM_LIB_HEAP`**:这些是ARMC库预定义的名称。链接器会为它们生成符号:-`Image$$ARM_LIB_STACK$$ZI$$Base`:栈基地址(高地址,因为栈向下生长)-`Image$$ARM_LIB_STACK$$ZI$$Limit`:栈限地址(低地址,即栈底)-`Image$$ARM_LIB_HEAP$$ZI$$Base`:堆基地址(低地址)-`Image$$ARM_LIB_HEAP$$ZI$$Limit`:堆限地址(高地址)在启动文件中,我们使用这些符号来初始化栈指针和堆。-**`EMPTY`属性**:表示该区域是未初始化的保留空间,不会在启动时清零。-**栈和堆的位置**:我们将栈放在DTCMRAM的末端(0x20020000),并分配16KB(向下生长到0x2001C000)。堆紧接着栈的下方(0x2001C000)开始,向上生长16KB(到0x20020000)。注意:栈和堆不能重叠,这里我们让它们相邻但不重叠(栈向下,堆向上,中间有间隙)。-**IAP_RAM**:我们将SRAM3(0x38000000)保留给IAP使用。在代码中,我们可以通过指定段(section)属性将变量放入该区域:```c__attribute__((section("IAP_RAM")))uint8_tiap_buffer[1024];```####3.修改启动文件在启动文件(如`startup_stm32h743xx.s`)中,初始化栈指针:```assembly;外部引用链接器生成的符号IMPORT__initial_sp;在向量表中,第一个字是初始栈指针__Vectors:DCD__initial_sp;栈指针初始值(由链接器提供)DCDReset_Handler;复位向量...```在Keil中,链接器会自动使用`ARM_LIB_STACK`区域的定义来设置`__initial_sp`。但如果我们自定义了栈区域(如上面的例子),需要确保链接器能够正确生成`__initial_sp`。通常,如果我们使用了预定义名称`ARM_LIB_STACK`,链接器会自动处理。####4.设置堆C库在初始化时会使用`ARM_LIB_HEAP`区域定义的堆。在代码中调用`malloc`等函数时,就会从这个区域分配内存。####5.在Keil中指定Scatter文件-打开OptionsforTarget->Linker选项卡-取消勾选`UseMemoryLayoutfromTargetDialog`-在`ScatterFile`栏中指定我们创建的scatter文件(如`STM32H743ZI.sct`)###验证:1.编译后查看生成的map文件,搜索`ARM_LIB_STACK`和`ARM_LIB_HEAP`,确认地址和大小是否正确。2.检查向量表的第一个字是否为栈顶地址(即`Image$$ARM_LIB_STACK$$ZI$$Base`)。3.在运行时,检查栈指针(SP)的初始值是否在`0x2001C000`到`0x20020000`之间(注意栈向下生长,初始SP为0x20020000)。###注意事项:-栈和堆的大小需要根据应用需求调整,确保不发生溢出。-如果使用RTOS,每个任务都需要自己的栈,此时主栈(MSP)可以较小,而任务栈在RTOS创建任务时分配(通常从堆中分配)。-确保所有RAM区域不重叠,且总大小不超过物理RAM。###相关问题:1.如何在Keil中查看生成的内存映射(map文件)?2.如果堆栈溢出,如何检测和调试?3.如何在多核系统(如STM32H7的双核)中为每个核分配独立的堆栈?4.如何为FreeRTOS任务分配栈空间?5.如何优化STM32H743的内存布局以提高性能?</think>### 在 Keil ARM 编译器中为 STM32H743ZIxx 配置堆栈和内存区域 以下是针对 STM32H743ZIxx 处理器的 scatter 文件配置指南,涵盖 IAP_RAM、ARM_LIB_STACK 和 ARM_LIB_HEAP 的定义: --- #### 1. **STM32H743ZIxx 内存布局概述** 该处理器包含多块物理内存区域: - **FLASH** (0x08000000, 2MB) - **DTCM RAM** (0x20000000, 128KB) - 高速紧耦合内存 - **AXI SRAM** (0x24000000, 512KB) - **SRAM1-4** (0x30000000-0x38000000) - **备份 SRAM** (0x38800000) 典型分配策略: - **堆栈** 放在 DTCM(高速访问) - **IAP_RAM** 放在备份 SRAM(掉电保持) - **堆** 放在 AXI SRAM(大容量) --- #### 2. **完整 Scatter 文件示例** ```scatter ; STM32H743ZIxx 分散加载配置 LR_IROM1 0x08000000 0x00200000 { ; 2MB Flash ER_IROM1 0x08000000 0x00200000 { ; 代码区 *.o (RESET, +First) ; 向量表 * (InRoot$$Sections) ; 库初始化代码 .ANY (+RO) ; 所有只读数据 } ; >>> 关键内存区域定义 <<< RW_IRAM1 0x20000000 0x00020000 { ; DTCM (128KB) .ANY (+RW +ZI) ; 常规变量 ARM_LIB_STACK 0x2001F000 EMPTY 0x1000 {} ; 栈区(4KB) } RW_IRAM2 0x24000000 0x00080000 { ; AXI SRAM (512KB) .ANY (+RW +ZI) ARM_LIB_HEAP +0 EMPTY 0x8000 {} ; 堆区(32KB)[^1] } RW_IRAM3 0x38800000 0x00001000 { ; 备份SRAM(4KB) * (IAP_RAM) ; IAP专用区 } } ``` --- #### 3. **关键区域配置详解** 1. **堆栈 (ARM_LIB_STACK)**: - 位置:DTCM 末端 (0x2001F000) - 配置:`EMPTY 0x1000` 声明 4KB 未初始化空间 - 启动文件初始化: ```assembly ; startup_stm32h743xx.s __initial_sp EQU 0x2001F000 ; 栈顶地址 ``` 2. **堆 (ARM_LIB_HEAP)**: - 位置:AXI SRAM 中段 (0x24040000) - 配置:`EMPTY 0x8000` 声明 32KB 空间 - 使用:C 库的 `malloc()` 自动使用此区域[^2] 3. **IAP_RAM**: - 位置:备份 SRAM (0x38800000) - 配置:专用段 `* (IAP_RAM)` - 代码中使用: ```c __attribute__((section("IAP_RAM"))) uint8_t iap_buffer[1024]; // IAP专用变量 ``` --- #### 4. **Keil 工程配置步骤** 1. **创建 scatter 文件**: - 保存为 `.sct` 格式(如 `STM32H743ZI.sct`) 2. **链接器设置**: - Options for Target → Linker 选项卡 - 取消勾选 "Use Memory Layout from Target Dialog" - 指定 Scatter File 路径 3. **验证配置**: - 编译后查看生成的 `*.map` 文件 - 检查关键符号地址: ``` ARM_LIB_STACK 0x2001f000 Section Empty 0x1000 ARM_LIB_HEAP 0x24040000 Section Empty 0x8000 IAP_RAM 0x38800000 Section 0x400 ``` --- #### 5. **高级配置技巧** 1. **多堆栈配置**(如RTOS): ```scatter TASK1_STACK 0x20010000 EMPTY 0x800 {} TASK2_STACK 0x20010800 EMPTY 0x800 {} ``` 2. **动态大小调整**: ```scatter ARM_LIB_HEAP +0 EMPTY HEAP_SIZE {} ``` 编译时传递 `-DHEAP_SIZE=0x10000` 3. **缓存优化**: ```scatter RW_IRAM4 0x30000000 0x00020000 { ; SRAM2 (带缓存) * (CACHE_ALIGNED_DATA) } ``` --- #### 6. **常见问题解决** - **堆栈溢出检测**:在 scatter 中预留保护区 ```scatter STACK_GUARD 0x2001E000 EMPTY 0x100 {} ; 溢出时将写入此区域 ``` - **地址对齐**:确保 8 字节对齐(AAPCS 标准) ```scatter ARM_LIB_HEAP ALIGN 8 EMPTY 0x8000 {} ``` > **关键点**:STM32H7 的 DTCM 访问速度比 AXI SRAM 快 4 倍,建议将堆栈放在 DTCM[^3] --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZC·Shou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值