因为项目需要,最近重新拾起来STM32,正好趁着这个机会好好的梳理下遇到的知识细节。
STM32的部分芯片有CCM内存,这个内存直接和内核相连,除了内核之外谁都不能访问,那么我们怎么将其利用起来呢?这里以STM32F407ZGT6为例,简单的笔记下如何使用CCM内存,并进行简单分析。
CONTENTS
参考链接
【 https://www.st.com/resource/en/application_note/dm00083249-use-stm32f3-stm32g4-ccm-sram-with-iar-ewarm-keil-mdk-arm-and-gnu-based-toolchains-stmicroelectronics.pdf 】
【 https://blog.csdn.net/waitxhurt/article/details/81905979 】
首先看下总线矩阵图,可以看到,在STM32F407ZGT6上,CCMRAM挂在了cortexM的D总线上,因此可以作为数据源访问,但是不能作为指令源访问(没有挂在I总线上)。
为了便于分析,本文将部分函数也挂在到了CCMRAM上,此时只能进行静态分析,不能执行!!否则会HardFault!!
如何使用
增加ccmram分区存储
我们一般使用的都是Flash链接,这里修改STM32F407ZGTX_FLASH.ld:
MEMORY { /* 首先看下芯片是否支持CCMRAM,以及CCMRAM的起始地址和长度是否正确。 */ CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 128K } 。。。。 。。。。 /* 建立ccmram_section section,这里可以参考上面的 data section */ _siccmram = LOADADDR(.ccmram_section); /* 定义一个变量_siccmram,值为ccmram_sections实际上Flash中的地址,后续需要从Flash中将该区段拷贝到ccmram中 */ .ccmram_section : /* 区段名称 */ { . = ALIGN(4); /* 区段起始4字节对齐 */ _sccmram = . ; /* 定义一个变量_sccmram,值为ccmram_section在CCMRAM中的起始地址 */ *(.ccmram) /* 定义一个编程分区名称,attribute中section为ccmram的都符合 */ *(.ccmram*) /* 定义一个编程分区名称,attribute中section前缀为ccmram的都符合 */ Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.o (*) /* 演示用,可以将heap_4.o里面的所有代码和静态内存都放进来,需要总线支持指令访问!! */ . = ALIGN(4); /* 区段结束4字节对齐 */ _eccmram = . ; /* 定义一个变量_eccmram,值为ccmram_section在CCMRAM中的结束地址 */ } >CCMRAM AT> FLASH /* 声明该区段在CCMRAM中,但是需要在FLASH中保存 */ }
增加启动时ccmram分区搬移
我们修改startup_stm32f407zgtx.s 如下:
LoopCopyDataInit: ldr r0, =_sdata ldr r3, =_edata adds r2, r0, r1 cmp r2, r3 bcc CopyDataInit movs r1, #0 b LoopCopyDataInitCCMRAM /* 执行流程为 LoopCopyDataInit -> LoopCopyDataInitCCMRAM -> LoopFillZerobss */ /* 将FLASH分区中的ccmram分段拷贝到真实的ccmram分区中,LoopCopyDataInitCCMRAM伪代码如下: index = 0;(r1即为index) while(_sccmram + index <= _eccmram) { *(_sccmram + index) = *(_siccmram+index) index+=4; } */ CopyDataInit_CCMRAM: ldr r3, =_siccmram ldr r3, [r3, r1] str r3, [r0, r1] adds r1, r1, #4 LoopCopyDataInitCCMRAM: ldr r0, =_sccmram ldr r3, =_eccmram adds r2, r0, r1 cmp r2, r3 bcc CopyDataInit_CCMRAM ldr r2, =_sbss b LoopFillZerobss
代码指定Section
int32_t s_value_ram; int32_t s_value_ram_init = 1; // 全局/静态变量指定section: __attribute__((section (".ccmram_data"))) int32_t s_value_ccm; __attribute__((section (".ccmram_data"))) int32_t s_value_ccm_init = 2; void test_func1(void) { s_value_ram = 100; s_value_ram_init = 100; s_value_ccm = 100; s_value_ccm_init = 100; } // 函数指定section,需要总线支持指令访问!!: __attribute__((section (".data_func"))) void test_func2(void) { s_value_ram = 200; s_value_ram_init = 200; s_value_ccm = 200; s_value_ccm_init = 200; } // 函数指定section,需要总线支持指令访问!!: __attribute__((section (".ccmram_func"))) void test_func3(void) { s_value_ram = 300; s_value_ram_init = 300; s_value_ccm = 300; s_value_ccm_init = 300; } /* 整个文件包含,此时需要在ld文件中指明,需要总线支持指令访问!! 参考语法: Middlewares/Third_Party/FreeRTOS/Source/portable/MemMang/heap_4.o (*) 至于为什么变量和函数的section不一样,参考【 https://stackoverflow.com/questions/30076949/gcc-error-variable-causes-a-section-type-conflict 】, 这里简单处理,使用通配符匹配解决。 另外,使用data_func,而不是默认的data,可以让list文件也去额外反编译该分段,便于调试。 */
编译信息对比
我们将几个函数添加代码流程中,编译完成后,可以打开对应的list文件,可以看到如下信息:
Disassembly of section .text: 。。。。 。。。。 test_func1的编译地址都在800XXXX,标明是flash分区,text分段 void test_func1(void) { 8000570: b480 push {r7} 。。。。。。 。。。。。。 800058a: 601a str r2, [r3, #0] } 。。。。。。 。。。。。。 //函数重新跳转 08008e28 <__test_func2_veneer>: 8008e28: f85f f000 ldr.w pc, [pc] ; 8008e2c <__test_func2_veneer+0x4> 8008e2c: 20000005 .word 0x20000005 08008e30 <__test_func3_veneer>: 8008e30: f85f f000 ldr.w pc, [pc] ; 8008e34 <__test_func3_veneer+0x4> 8008e34: 10000009 .word 0x10000009 。。。。。。 。。。。。。 Disassembly of section .data: //没有指定分段的情况下,有赋值的变量在data分段中,没有赋值的都在bss分段中,启动时被设置为0 20000000 <s_value_ram_init>: 20000000: 00000001 .... test_func2的编译地址都在200XXXX,标明是RAM分区,data分段 20000004 <test_func2>: { 20000004: b480 push {r7} 。。。。。。 。。。。。。 2000001e: 601a str r2, [r3, #0] } 。。。。。。 。。。。。。 Disassembly of section .ccmram_section: //s_value_ccm和s_value_ccm_init都被指定了分段,因此放在这里 10000000 <s_value_ccm>: 10000000: 00000000 .... 10000004 <s_value_ccm_init>: 10000004: 00000002 .... test_func3的编译地址都在100XXXX,标明是CCMRAM分区,ccmram_section分段 10000008 <test_func3>: { 10000008: af00b480 .word 0xaf00b480 。。。。。。 。。。。。。 10000028: 601a7296 .word 0x601a7296 }
打开对应的map文件,可以看到如下信息:
。。。。。。 。。。。。。 *(.text*) .text.test_func1 0x0000000008000570 0x38 Core/Src/main.o 0x0000000008000570 test_func1 。。。。。。 。。。。。。 *(.data*) .data.s_value_ram_init 0x0000000020000000 0x4 Core/Src/main.o 0x0000000020000000 s_value_ram_init .data_func 0x0000000020000004 0x38 Core/Src/main.o 0x0000000020000004 test_func2 。。。。。。 。。。。。。 *(.ccmram*) .ccmram_data 0x0000000010000000 0x8 Core/Src/main.o 0x0000000010000000 s_value_ccm 0x0000000010000004 s_value_ccm_init .ccmram_func 0x0000000010000008 0x40 Core/Src/main.o 0x0000000010000008 test_func3 。。。。。。 。。。。。。 *(COMMON) s_value_ram没有被赋值,因此被放在了COMMON分段,COMMON分段就是bss分段的一部分 COMMON 0x0000000020009324 0x88 Core/Src/main.o 0x0000000020009324 defaultTaskHandle 0x0000000020009328 huart1 0x0000000020009368 s_value_ram
IDE工具辅助分析
使用STM32IDE,可以看下各个分区的占用情况,如下图:
发表评论