如何使用sct文件、icf文件来定位不同的内存存储变量(cortex-m3平台)keil+iar

好久没写博客了,快到国庆了,经历了一些项目和坑,还是要保持记录的好习惯,好记性不如烂笔头嘛。
    目前使用了cortex-m3内核的两款单片机:stm32f1和lpc1768的,虽说是cm3内核,但是两个芯片添加的外设是有区别的,很多外设的使用方式也是各有千秋,st在国内比较火,全国研讨会如火如荼,每年都有。lpc1768是属于NXP半导体,前身是飞利浦半导体,也算是老牌的半导体公司,相比较stm32,国内入门的论坛没有stm32火爆,但我相信质量过得去。
    
    两款单片机都是比较老了,stm32f1是2007年发布的,lpc1768是估计2009年左右,具体不清楚,我是看标准库上是这个日期,因此猜测。
    虽然不是老的芯片,但是市场依然有供应。
    可以作为arm入门的基础芯片。
    
    stm32f103zet是512flash,64ksram,而且是连续的sram分布,那么程序上使用基本上不用过多考虑;而lpc1768也是有64ksram的,但是是分为3个区域的,32ksram作为普通的sram,和stm32f1类似,使用无区别,但是另外两个16kb的内存空间是在另外的地址空间,手册原文:
    The LPC17xx contain a total of 64 kB on-chip static RAM memory. This includes the main 32 kB SRAM, accessible by the CPU and DMA controller on a higher-speed bus, and two additional 16 kB each SRAM blocks situated on a separate slave port on the AHB multilayer matrix.
    
    LPC17xx总共包含64 kB片上静态RAM存储器。 其中包括可由高速总线上的CPU和DMA控制器访问的主32 kB SRAM,以及位于AHB多层矩阵上独立从端口上的两个附加16 kB SRAM块。
    总结起来就是,32ksram的起始地址0x1000 0000,大小0x8000=64kb
    两个附加16 kB SRAM块的起始地址0x2007C000,而且是连续的,下面的计算可见一斑。
hex(0x2007C000+0x8000)= 0x20084000
hex(0x2007C000+0x4000)= 0x20080000
因此在keil设置中,可以设置两个32kb的内存空间,而且如果使用了分散加载文件,那么两个附加16 kB内存就可以完全利用起来了,lpc1768这个设计的原因是想两个内存空间可以再单片机运行的过程中,分别取数据,快加usb和ethernet数据的读写,和普通的变量区分开来————论坛大佬解释的。具体链接找不到了。。
    如何使用呢?sct文件的使用参考了硬汉论坛的pdf文档,H7系列的。

 

这里首先使用stm32来演示下:
    ; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00080000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00008000  {  ; RW data
   .ANY (+RW +ZI)
  }
  ; RW data - 32KB SRAM
  RW_IRAM2 0x20008000 0x00008000 {
  *(.RAM_D1)
  }
}

我把64kb分成两个32kb的空间, keil需要设置linker界面,使用自定义的sct文件

ac6.14编译器实例:

定义全局变量:
    __attribute__((section (".RAM_D1"))) uint32_t AXISRAMBuf[10];
    __attribute__((section (".RAM_D1"))) uint32_t temp = 0;
下面的语句也是合法的,
    uint8_t   UART_TX_BUF[10]  __attribute__((section(".ARM.__at_0x2000B00A")));    //就是将串口发送的数据定位到RAM中起始地址为0X2000b00A
    编译之后,可以在map文件看到这几个变量的具体地址
    temp                                     0x20008000   Data           4  main.o(.RAM_D1)
    AXISRAMBuf                               0x20008004   Data          40  main.o(.RAM_D1)
    UART_RX_BUF                              0x2000b000   Data          10  main.o(.ARM.__at_0x2000b000)
    UART_TX_BUF                              0x2000b00a   Data          10  main.o(.ARM.__at_0x2000B00A)

那么就是成功的,如果使用AC5编译器

__attribute__((section (".RAM_D1"))) uint32_t AXISRAMBuf[10];
__attribute__((section (".RAM_D1"))) uint32_t temp = 0;

AXISRAMBuf[0] = 11;
temp= 11;

优化等级-00
AXISRAMBuf    0x20008000 Data 40 main.o(.RAM_D1)
temp        0x20008028 Data 4 main.o(.RAM_D1)
优化等级-03
temp        0x20008000 Data 4 main.o(.RAM_D1)
AXISRAMBuf    0x20008004 Data 40 main.o(.RAM_D1)

优化等级-03
temp只定义,代码中不试用
AXISRAMBuf   0x20008000 Data 40 main.o(.RAM_D1)

可见,编译会把代码中不适用的变量,在linker的时候就,不分配空间了,相当于删除了这个变量

 

lpc1768的芯片小技巧:
使用iar编译器,测试通过,也记录下:
使用两个sram的方法参见博客链接:
https://blog.csdn.net/liming0931/article/details/108887551

定义变量,int val_addr @0x2007C000;
编译后map文件如下:
val_addr                0x2007'c000    0x4  Data  Gb  main.o [1]
成功!

keil平台下有两种方法可以将lpc1768的两个内存使用起来

1、keil选项设置

 

ac5编译器定义变量,uint32_t variable2 __attribute__((at(0x2007C42C)));  // Place at 0x2007C000(因为lpc1768的库文件core_cm3.h,不支持ac6,暂时使用ac5)

 0x2007C42C这个地址需要查看map文件后在自定义地址,否则就会出现linker警告,比如0x2007C004,

linking...

.\KEIL5OUT\check.axf: Warning: L6918W: Execution region RW_IRAM2 placed at 0x2007c000 needs padding to ensure alignment 4 of main.o(.ARM.__AT_0x2007C004).

为啥呢?

 意思是0x2007c000 到0x2007c007的空间没有使用。定义到0x2007c000链接就没有警告了,因为uart_data_to_linux,uart_data_to_g0这两个变量占用的空间比较大,linker自动将该变量分配到0x2007c000之后的内存地址中了,但是如果不适用iram2,那么只能分配到iram1中。

2、使用自定义的sct文件

iram2 记得不要打钩。

  然后定义变量:

uint32_t variable2 __attribute__((at(0x2007C050))); // Place at 0x2007C050 
__attribute__((section(".RAM_D1"))) uint32_t AXISRAMBuf[10];
__attribute__((section(".RAM_D1"))) uint32_t AXISRAMBuf2[10]

map文件如下:具体负

AXISRAMBuf     0x2007c000   Data   40 main.o(.RAM_D1)
AXISRAMBuf2       0x2007c028  Data    40 main.o(.RAM_D1)
variable2        0x2007c050    Data    4 main.o(.ARM.__AT_0x2007C050)

keil的linker模式是优先分配__attribute__((section(".RAM_D1")))这种形式定义的变量,如果使用下面的方式:

uint32_t variable2 __attribute__((at(0x2007C000))); // Place at 0x2007C000 
__attribute__((section(".RAM_D1"))) uint32_t AXISRAMBuf[10];
__attribute__((section(".RAM_D1"))) uint32_t AXISRAMBuf2[10]

linker报错,也无法生产map文件

.\KEIL5OUT\check.axf: Error: L6985E: Unable to automatically place AT section main.o(.ARM.__AT_0x2007C000) with required base address 0x2007c000. Please manually place in the scatter file using the --no_autoat option. 

--no_autoat具体怎么使用,暂时不了解。

 具体的link而使用方法,可以参考arm linker参考文件

 

总结:

使用__attribute__((at(0x2007C004))); 这种方法需要自己计算内存的地址相对比较麻烦

使用__attribute__((section(".RAM_D1"))) ,省去了手动计算地址的麻烦,相对简单。ac5,ac6通用的。

 

 

 

 

 

 

posted @ 2020-09-30 16:57  wdliming  阅读(3351)  评论(0编辑  收藏  举报