Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
STM32_CCM_特殊内存
RT-Thread Studio
stm32F4
初探STM32F4 CCM在RT-Studio下的分配
5.00
发布于 2022-04-10 16:33:08 浏览:4841
订阅该版
[tocm] STM32F4系列内部有64KB CCM(Core Coupled Memory),加上内部192KB RAM,合计256KB。但是,CCM RAM在RT-Studio中并未使用。CCM部分很特殊,挂在D-Bus上,只能通过CPU访问,不能被DMA访问,官方为了适配所有平台,默认没有使用这部分CCM。 在MDK工程中新增CCM比较方便,简单编辑sct文件即可。但是,与MDK不同,RT-Studio使用gcc编译代码。 近期项目开发中RAM不够,需要开启CCM。在本论坛查了一些问题/文章,都不能完全解决`同时在CCM中使用未初始化变量和初始化变量`的需求;官方AN4296文档的配置方式也会导致bin文件过大。因此,本文在下述参考链接的基础上,提出三种简单修改lds的方案,分别对应:`仅用于未初始化变量、仅用于初始化变量、同时支持未初始化和初始化变量`三种使用场景。 简单说明一下,论坛中lds代码高亮是使用\```c, 最终查看了网页源代码才了解。 本文参考链接: [RT-Thread-link.lds修改使满足CCM RAM,但数据出错RT-Thread问答社区 - RT-Thread](https://club.rt-thread.org/ask/question/425240.html) [RT-Thread-STM32F4在RT-Thread Studio中使用CCM SRAMRT-Thread问答社区 - RT-Thread](https://club.rt-thread.org/ask/question/423945.html) [RT-Thread-rtthread studio生成hex文件正常,bin文件太大RT-Thread问答社区 - RT-Thread](https://club.rt-thread.org/ask/question/430211.html) [OpenSTM32 Community Site | Using CCM Memory](https://www.openstm32.org/Using%2BCCM%2BMemory) ## 1 第一种CCM添加方案:未初始化变量 > 第一种CCM添加方案仅仅支持将未初始化变量放置在CCM RAM中,不能在声明变量时赋初始值。 1.创建工程,将红色处按照电路实际情况进行修改。 ![image-20220409230614068.png](https://oss-club.rt-thread.org/uploads/20220410/53f98ec86a3520fa3aa11657aeb51073.png.webp) 2.删除部分main函数代码,避免打印输出影响查看后续的打印输出。编译工程,得到Flash和RAM的占用。 ![image-20220409230824509.png](https://oss-club.rt-thread.org/uploads/20220410/f69ddf34374b19b6bc24617eee8bb85e.png.webp) 3.打开并查看lds文件 ![image-20220409231049839.png](https://oss-club.rt-thread.org/uploads/20220410/35b19bd892a68fa820018d36dcfe2547.png.webp) 4.找到`linkscripts->STM32F429VI`目录下的lds文件,使用文本编辑器编辑,增加一行CCM的描述,如第5行代码所示。 ```c MEMORY { ROM (rx) : ORIGIN = 0x08000000, LENGTH = 2048k /* 2048K flash */ RAM (rw) : ORIGIN = 0x20000000, LENGTH = 192k /* 192K sram */ CCM (rw) : ORIGIN = 0x10000000, LENGTH = 64k /* 64K ccm sram */ } ``` 5.关闭lds文件,重新打开,确认CCM已经添加 ![image-20220409231448385.png](https://oss-club.rt-thread.org/uploads/20220410/3f2e4f4b0586e71bd56057d0a1ff80a9.png) 6.在lds文件中添加`.ccm section`的描述,位于.data section上方。 ```c .ccm : { . = ALIGN(4); _sccm = .; /*create a global symbol at ccmram start */ *(.ccm) /* .ccm sections */ *(.ccm*) /* .ccm* sections */ . = ALIGN(4); _eccm = .; /*create a global symbol at ccmram end */ } >CCM ``` ![image-20220410124516037.png](https://oss-club.rt-thread.org/uploads/20220410/75caca11a2bdec322185599bdd392a1c.png.webp) 7.修改main.c代码,使用`__attribute__`关键词增加几个变量到.ccm section。 ```c __attribute__((section(".ccm"))) rt_uint8_t array[4]; __attribute__((section(".ccm"))) rt_uint8_t *ptr; int main(void) { ptr = array; for(rt_uint8_t i = 0; i < 4; i++) { *ptr = 7 + i; ptr++; } rt_kprintf("ptr = 0x%08x, stores in 0x%08x\n", ptr, &ptr); for(rt_uint8_t i = 0; i < 4; i++) { rt_kprintf("array[%d] = %d\n",i,array[i]); } return RT_EOK; } ``` 8.查看Debug目录下的`rtthread.map`文件,找到.ccm部分。显然,array和ptr都被分配到了CCM RAM。 ![image-20220410124623690.png](https://oss-club.rt-thread.org/uploads/20220410/b2cfdcc4e05841633c49f09dec2ecb94.png.webp) 9.下载程序,观察结果。 ![image-20220410124950203.png](https://oss-club.rt-thread.org/uploads/20220410/aeb5948c42b50531b4c096a225e91898.png) 显然,此时数据被写入到了array,且ptr指向一切正常。`结果中,ptr=0x10000004`是因为在for循环中自增了4次。 11.打开Debug目录,发现rtthread.bin文件太大,达到了`131073KB`。 ![image-20220410125144786.png](https://oss-club.rt-thread.org/uploads/20220410/2077ad9401c1f4971b098903c555efbe.png) 12.再次修改.ccm部分,添加`NoLOAD`关键词后重新编译,bin文件大小正常。 ```c .ccm (NOLOAD): { . = ALIGN(4); _sccm = .; /*create a global symbol at ccmram start */ *(.ccm) /* .ccm sections */ *(.ccm*) /* .ccm* sections */ . = ALIGN(4); _eccm = .; /*create a global symbol at ccmram end */ } >CCM ``` ![image-20220410125327132.png](https://oss-club.rt-thread.org/uploads/20220410/f497ff642dec148c754c6944c093dc93.png) > **重要说明:**这种方式,只支持将未初始化变量放置在.ccm中。在声明时,任何对.ccm中的变量赋初始值的做法都是不对的,哪怕将将变量的初始值赋值为0也不行。 ## 2 第二种CCM添加方案:初始化变量 > 第二种CCM添加方案,支持对存储在CCM RAM中的变量赋初始值。 1.修改.ccm和.data的lds部分 ```c _siccm = LOADADDR(.ccm); .ccm : { . = ALIGN(4); _sccm = .; /*create a global symbol at ccmram start */ *(.ccm) /* .ccm sections */ *(.ccm*) /* .ccm* sections */ . = ALIGN(4); _eccm = .; /*create a global symbol at ccmram end */ } >CCM AT>ROM /* .data section which is used for initialized data */ _sidata = LOADADDR(.data); .data : { . = ALIGN(4); /* This is used by the startup in order to initialize the .data secion */ _sdata = . ; *(.data) *(.data.*) *(.gnu.linkonce.d*) PROVIDE(__dtors_start__ = .); KEEP(*(SORT(.dtors.*))) KEEP(*(.dtors)) PROVIDE(__dtors_end__ = .); . = ALIGN(4); /* This is used by the startup in order to initialize the .data secion */ _edata = . ; } >RAM AT>ROM ``` ![image-20220410131244208.png](https://oss-club.rt-thread.org/uploads/20220410/215283ce15be079526c056a57144b6f7.png.webp) 2.修改启动代码 启动代码`startup_stm32f429xx.s`位于`libraries->CMSIS->Device->ST->STM32F4xx->Source->Templates->gcc`目录下,隐藏较深。 原始的启动代码只对.data区进行了初始化,我们现在需要对.ccm区也进行初始化。 Step1. 在原始的启动代码的`.word _ebss`行后添加如下代码,从lds中引入\_sciccm, \_sccm和 \_eccm。 ```assembly /* start address for the initialization values of the .data ccm section. defined in linker script */ .word _siccm /* start address for the .ccm section. defined in linker script */ .word _sccm /* end address for the .ccm section. defined in linker script */ .word _eccm ``` Step2. 在原始的启动代码的`bcc CopyDataInit`和`ldr r2, =_sbss`两行中间插入如下代码,对.ccm中的变量进行初始化,即,从Flash中搬运至CCM RAM。简单添加了注释,很好理解。 ```assembly /* Copy the data CCM segment initializers from flash to SRAM */ movs r1, #0 /* 起始计数 */ b LoopCopyCCMInit /* 调用LoopCopyCCMInit */ CopyCCMInit: ldr r3, =_siccm /* Flash中存储的CCM RAM变量初始值的起始地址 */ ldr r3, [r3, r1] /* r3 <-[r3+r1] */ str r3, [r0, r1] /* [r0 + r1] <- r3 */ adds r1, r1, #4 /* 偏移量加4*/ LoopCopyCCMInit: ldr r0, =_sccm /* .ccm section的起始地址 */ ldr r3, =_eccm /* .ccm section的终止地址 */ adds r2, r0, r1 /* r2 <- 指针加偏移量*/ cmp r2, r3 /* 指针是否已经指向了终止地址? */ bcc CopyCCMInit ``` 3.修改main.c,编译、下载、观察运行结果 ```c __attribute__((section(".ccm"))) rt_uint8_t array[4] = {1,2,3,4}; __attribute__((section(".ccm"))) rt_uint8_t *ptr = array; int main(void) { rt_kprintf("ptr = 0x%08x, stores in 0x%08x\n", ptr, &ptr); for(rt_uint8_t i = 0; i < 4; i++) { rt_kprintf("array[%d] = %d\n",i,array[i]); } return RT_EOK; } ``` ![image-20220410132505367.png](https://oss-club.rt-thread.org/uploads/20220410/0765de83f594c23b3c22c75cafd7899d.png) 记住`55048`这个数据,很快会用到。 ![image-20220410131752727.png](https://oss-club.rt-thread.org/uploads/20220410/8671a5ae2aa4bd37d12caaf4cd76cf82.png) 显然,此时在.ccm section中的变量已经被成功赋初始值了。 4.如果在main.c再添加一个大型未初始化的数组,会发生什么? ```c __attribute__((section(".ccm"))) rt_uint8_t bigger_array[1024]; ``` ![image-20220410132706844.png](https://oss-club.rt-thread.org/uploads/20220410/fd7b70c7e1b5c1203918ea1a3c3add88.png.webp) 编译后,Flash容量变成了`65288`。显然,`65288 - 55048 = 10240`。 bigger_array虽然没有被初始化,但是它被分配在.ccm section,按照lds的约束和修改后的启动代码,STM32启动后,要从Flash中搬运10K的数据到该数组。显然,这与我们的需求并不符合。我们最终是需要将CCM配置成既支持.data,也支持.bss的方式。 ## 3 第三种CCM方案:共存 > 第三种CCM方案,既支持初始化变量放置在CCM中,也支持未初始化变量放置在CCM中。解决思路是在CCM中增加两个Section,一个用于存放.data数据,一个用于存放.bss数据。 1.在上述两种方式的指导下,我们可以迅速在.ccm seciton后方再次添加一个.ccmbss section。 ```c .ccmbss (NOLOAD): { . = ALIGN(4); _sccmbss = .; /*create a global symbol at ccmram start */ *(.ccmbss) /* .data sections */ *(.ccmbss*) /* .data* sections */ . = ALIGN(4); _eccmbss = .; /*create a global symbol at ccmram end */ } >CCM ``` 2.修改main.c ```c __attribute__((section(".ccm"))) rt_uint8_t array[4] = {1,2,3,4}; __attribute__((section(".ccm"))) rt_uint8_t *ptr = array; __attribute__((section(".ccmbss"))) rt_uint8_t bigger_array[10*1024]; int main(void) { rt_kprintf("ptr = 0x%08x, stores in 0x%08x\n", ptr, &ptr); for(rt_uint8_t i = 0; i < 4; i++) { bigger_array[i] = i; /* 假装使用了bigger_array, 防止编译器优化掉这个数组*/ rt_kprintf("array[%d] = %d, bigger_array[%d] = %d\n", i,array[i], i,bigger_array[i]); } return RT_EOK; } ``` 3.编译,查看map文件,查看Flash占用,新增的10K并未占用Flash空间,bin文件也正常。 ![image-20220410141755291.png](https://oss-club.rt-thread.org/uploads/20220410/3b6d53f1acc5b2c675b0070e6f621e44.png.webp) ![image-20220410135436743.png](https://oss-club.rt-thread.org/uploads/20220410/6704c4c43551006b761b18e806ba0e83.png) ![image-20220410135524440.png](https://oss-club.rt-thread.org/uploads/20220410/cc1eef4c069975a242d650b16d65023a.png) ![image-20220410135056130.png](https://oss-club.rt-thread.org/uploads/20220410/6a1ddffc9e2c7f56c50628475d16794f.png) 4.再次修改bigger_array的存放位置,放置在.ccm,显然,此时Flash会增大10K。并且,在初始化时,启动代码会对这10K RAM空间进行初始化。 ![image-20220410142015766.png](https://oss-club.rt-thread.org/uploads/20220410/4ee235b15466fcc436d99731810500cf.png.webp) 5.最后附上完整的lds文件,Happy RTT! ```c /* * linker script for STM32F429VI with GNU ld */ /* Program Entry, set to mark it as "used" and avoid gc */ MEMORY { ROM (rx) : ORIGIN = 0x08000000, LENGTH = 2048k /* 2048K flash */ RAM (rw) : ORIGIN = 0x20000000, LENGTH = 192k /* 192K sram */ CCM (rw) : ORIGIN = 0x10000000, LENGTH = 64k /*64K ccm ram*/ } ENTRY(Reset_Handler) _system_stack_size = 0x400; SECTIONS { .text : { . = ALIGN(4); _stext = .; KEEP(*(.isr_vector)) /* Startup code */ . = ALIGN(4); *(.text) /* remaining code */ *(.text.*) /* remaining code */ *(.rodata) /* read-only data (constants) */ *(.rodata*) *(.glue_7) *(.glue_7t) *(.gnu.linkonce.t*) /* section information for finsh shell */ . = ALIGN(4); __fsymtab_start = .; KEEP(*(FSymTab)) __fsymtab_end = .; . = ALIGN(4); __vsymtab_start = .; KEEP(*(VSymTab)) __vsymtab_end = .; /* section information for utest */ . = ALIGN(4); __rt_utest_tc_tab_start = .; KEEP(*(UtestTcTab)) __rt_utest_tc_tab_end = .; /* section information for at server */ . = ALIGN(4); __rtatcmdtab_start = .; KEEP(*(RtAtCmdTab)) __rtatcmdtab_end = .; . = ALIGN(4); /* section information for initial. */ . = ALIGN(4); __rt_init_start = .; KEEP(*(SORT(.rti_fn*))) __rt_init_end = .; . = ALIGN(4); PROVIDE(__ctors_start__ = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array)) PROVIDE(__ctors_end__ = .); . = ALIGN(4); _etext = .; } > ROM = 0 /* .ARM.exidx is sorted, so has to go in its own output section. */ __exidx_start = .; .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) /* This is used by the startup in order to initialize the .data secion */ _sidata = .; _siccmram = .; } > ROM __exidx_end = .; /* .data section which is used for initialized data */ _sidata = LOADADDR(.data); .data : { . = ALIGN(4); /* This is used by the startup in order to initialize the .data secion */ _sdata = . ; *(.data) *(.data.*) *(.gnu.linkonce.d*) PROVIDE(__dtors_start__ = .); KEEP(*(SORT(.dtors.*))) KEEP(*(.dtors)) PROVIDE(__dtors_end__ = .); . = ALIGN(4); /* This is used by the startup in order to initialize the .data secion */ _edata = . ; } >RAM AT > ROM _siccm = LOADADDR(.ccm); .ccm : { . = ALIGN(4); _sccm = .; /*create a global symbol at ccmram start */ *(.ccmram) /* .data sections */ *(.ccmram*) /* .data* sections */ . = ALIGN(4); _eccm = .; /*create a global symbol at ccmram end */ } >CCM AT>ROM .ccmbss (NOLOAD): { . = ALIGN(4); _sccmbss = .; /*create a global symbol at ccmram start */ *(.ccmbss) /* .data sections */ *(.ccmbss*) /* .data* sections */ . = ALIGN(4); _eccmbss = .; /*create a global symbol at ccmram end */ } >CCM .stack : { . = ALIGN(4); _sstack = .; . = . + _system_stack_size; . = ALIGN(4); _estack = .; } >RAM __bss_start = .; .bss : { . = ALIGN(4); /* This is used by the startup in order to initialize the .bss secion */ _sbss = .; *(.bss) *(.bss.*) *(COMMON) . = ALIGN(4); /* This is used by the startup in order to initialize the .bss secion */ _ebss = . ; *(.bss.init) } > RAM __bss_end = .; _end = .; /* Stabs debugging sections. */ .stab 0 : { *(.stab) } .stabstr 0 : { *(.stabstr) } .stab.excl 0 : { *(.stab.excl) } .stab.exclstr 0 : { *(.stab.exclstr) } .stab.index 0 : { *(.stab.index) } .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } /* DWARF debug sections. * Symbols in the DWARF debugging sections are relative to the beginning * of the section so we begin them at 0. */ /* DWARF 1 */ .debug 0 : { *(.debug) } .line 0 : { *(.line) } /* GNU DWARF 1 extensions */ .debug_srcinfo 0 : { *(.debug_srcinfo) } .debug_sfnames 0 : { *(.debug_sfnames) } /* DWARF 1.1 and DWARF 2 */ .debug_aranges 0 : { *(.debug_aranges) } .debug_pubnames 0 : { *(.debug_pubnames) } /* DWARF 2 */ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev 0 : { *(.debug_abbrev) } .debug_line 0 : { *(.debug_line) } .debug_frame 0 : { *(.debug_frame) } .debug_str 0 : { *(.debug_str) } .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } /* SGI/MIPS DWARF 2 extensions */ .debug_weaknames 0 : { *(.debug_weaknames) } .debug_funcnames 0 : { *(.debug_funcnames) } .debug_typenames 0 : { *(.debug_typenames) } .debug_varnames 0 : { *(.debug_varnames) } } ``` ## 4 小结 - .bss:存放未初始化(未初始化或者0初始化的全局变量和静态局部变量)的数据; - .data:存放已初始化(非零初始化的全局变量和静态局部变量)的数据。 其实,从第三种解决方案和启动代码中可以进一步发现,本文第三种解决方案的.ccmbss部分并没有对未初始化变量进行清0操作。官方自带启动代码中,在LoopFillZerobss函数中对.bss区的数据进行了清0操作。如果需要对.ccmbss中的数据进行清零,可以参照LoopFillZerobss进一步修改启动代码。限于篇幅,也比较简单,这部分就不再展开了。 附上个人公众号,欢迎关注: ![LCRQR.jpg](https://oss-club.rt-thread.org/uploads/20220410/08256c6c91d83e941432ccff0eaaaf3e.jpg)
30
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
lchnu
Witness, Understand, Skill
文章
10
回答
229
被采纳
88
关注TA
发私信
相关文章
1
rt_thread studio 啥时候能用呢
2
RT_Thread使用反馈帖子
3
RTT studio 下的 AT指令问题。
4
什么时候RTT Sdudio支持Ubuntu,Deepin和UOS操作系统
5
rt thread Studio 关于J-LINK下载问题
6
RT-Thread studio 调试设置问题
7
RTT-Studio 如何设置调试配置参数?
8
rt_thread studio 软件包配置
9
RT-Studio目前只能开发STM32的项目吗?
10
rtt studio 生成hex名字修改
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部