如何使用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通用的。