1. ARMCC 生成bin  :fromelf --bin !L -o demo.bin

  ARMCC 生成asm  :fromelf --text -a -c !L -o demo.asm

  xxd -i demo.bin   //以数组形式展示bin文件,这样就不需要上位机对bin文件的拆包了。

  D:\Keil_v5\bin\fromelf.exe --bin        -o ./Objects/demo.bin ./Objects/demo.axf    //使用.axf生成.bin文件

  D:\Keil_v5\bin\fromelf.exe --i32        -o ./Objects/demo.hex ./Objects/demo.axf    //使用.axf生成.hex文件

  D:\Keil_v5\bin\fromelf.exe --text -a -c -o ./Objects/demo.dis ./Objects/demo.axf    //生成.axf生成.dis汇编文件

  armasm和GNU是汇编语言源代码的两种不同语法。它们相似,但有许多不同之处。例如,armasm语法通过标签在顶格行首的位置来识别标签,而GNU语法通过 的存在来识别标签。

  标签label等同于函数名,用于指定段的开始地址和方便被引用,

2. 编译器预定义宏  USE_HAL_DRIVER,STM32F401xC

3. 各段布局

  Program Size: Code=13110 RO-data=754 RW-data=8 ZI-data=1896  
  Total RO Size (Code + RO Data) 13864 ( 13.54kB)
  Total RW Size (RW Data + ZI Data) 1904 ( 1.86kB)
  Total ROM Size (Code + RO Data + RW Data) 13872 ( 13.55kB)

  keil 生成的镜像文件中msp的值和gcc生成的不同,gcc中是ram的上限值比如64k的0x20010000, 而armcc是代码中使用量的上限值,以上面的例子msp就是0x2000770,因为8+1896==0x770,而这1896在armcc中除了包含狭义的.bss段,还包含启动头文件中申请的1024stack和512heap,因此代码中的狭义bss的大小为1896-1536;armcc从0x20000000开始放data段(上例中为8字节),之后狭义bss段,之后是heap段,之后是stack段,armcc编译后的逻辑代码内存的访问不会超过0x2000770,即从0x2000770到0x20010000的内存是永远也不会访问的;相同点是二者都存放在0x08000000地址处。

4. stm32的代码段起始于0x08000000,同时0开始的地址是0x08000000开始地址的镜像空间,0地址和0x08000000地址的数据是全等的,可以理解为0x08000000处有flash的控制器可以对flash存储执行读写操作,而0地址是0x08000000地址的只取映射地址。总之内核从0地址开始取指,0地址的内容可通过boot0/1脚映射到flash存储区,sram存储区 或 系统BROM存储区。见图:

      

   st在地址0x1FFF0000处又放了一套完成的hex文件(即BROM),这个hex文件由armcc编译,msp是0x20002560, Reset_Handler位于地址0x1FFF4347处。同理代码如果要在SRAM中运行时,在SRAM的开头也应该放一段类似这样完整的hex文件,同时在编译时指定存放地址为sram的地址。因为此时boot1=0,boot0=1, 0地址将映射为系统存储区,因此地址0和0x1FFF0000处的内容一致。当boot0=0时,地址0和0x08000000处的内容将会一致。

  5. 因为stm32是XIP的模式,指令就执行在0x08000000开始的区域,因此链接时代码段的地址就是0x08000000开始的区域,同理代码如果在ram中运行那么链接时代码段地址就是0x20000000开始的区域,即链接时代码段的地址取决于运行时指令存储的地址而不是掉电时存储的地址区域,即如果要在ram中运行代码,那么代码即使存储在片外flash中,链接文件中的代码段地址也是0x20000000开始的区域。

6. stm32f4xx HEX文件布局:

  从0x08000000开始第一个4字节存msp的值,第二个4字节到第101个4字节依次存放所有内部异常和外部中断的isr入口地址,这404字节位于hex文件开头,即中断向量表(在armcc下中断向量表属于data段而非code段);(为啥是101,因为stm32f4一共有15个内部异常和85个外部中断,再加一个MSP);armcc会在中断向量表结尾与Reset_Handler代码段开始位置间添加__main的指令码(__main不是main);Reset_Handler代码段开始位置就是hex中03/05类型后跟的值;之后就是各isr和各函数代码段对应的指令码占用的空间,将所有的代码段和RO段放完后紧挨着放data段的数据。

7. ARMCC优化选项:

  1. plain chars is signed 的作用:  大多数编译器的int被处理为signed int,但char 有的编译器被处理为signed,有的被处理为unsigned,不同的处理主要体现在数值比较上的歧义。plain chars is signed 制定char按signed char处理,因此为了不同编译器间的可移植性,定义变量时显式制定signed/unsigned。

  2. short enums/wchar的作用: 大多数编译器将枚举enum按int型数据处理,short enums/wchar将枚举和wchar型按short处理,节省ram使用。

  3. use RTTI 的作用:RTTI(Run-Time Type Information,运行时类型信息)是C++中的一种特性,它允许程序在运行时获取对象的类型信息。

  4. no auto include 的作用:在编译或链接过程中不自动包含标准库或头文件。

  5. read-write position independent的作用:Read-write position independent(读写位置无关)代码指的是能够在内存中任意位置执行,并且可以被修改的代码。

  6. read-only position independent的作用: Read-only position independent(只读位置无关)代码是一种特殊的代码优化技术。

  7. one elf section per function的作用:  在可执行和可链接格式(Executable and Linkable Format,ELF)中,一个ELF section per function(每个函数一个ELF节)是一种组织代码和数据的策略。

  8. split load and store multiple 作用: 拆分加载和存储的批量操作,即将一个较大的数据加载操作分解成多个较小的加载操作。

  9. Execute_only Code 选项阻止编译器生成对代码段的任何数据访问, 这是一种安全特性,用于防止代码被读取或修改。

8. xx.c 源文件中各种define和include展开后结果为 Listings/xx.i文件; 源文件与指令码为 Listings/xx.txt文件

9. hex中03/05类型后跟的值与msp后跟的值必然是相等的,都是指Reset_Handler代码段的开始位置

10. ICP: 使用烧录器 xx-link 通过硬件接口 swd jtag dap 等的下载方式,不需要任何代码支撑。

  ISP: 通过硬件接口 uart spi can 配合boot-pin和系统存储区(BROM)等的下载方式,不需要用户代码支撑,但需要芯片厂家的BROM代码支撑。

  IAP: 用户代码使用有线或无限的方式接收升级数据,同时配合用户boot和芯片库提供的flash的读写擦操作api实现的升级方式;硬件boot-pin全程不需要变动,需要通过ICP/ISP提前烧录用户boot。

 

 

 

posted on 2024-06-01 16:30  lance9527  阅读(67)  评论(0编辑  收藏  举报