好记性不如铅笔头

ARM, STM32, 操作系统

STM32CubeMX简单使用笔记:CCM的简单使用和简单分析

因为项目需要,最近重新拾起来STM32,正好趁着这个机会好好的梳理下遇到的知识细节。

STM32的部分芯片有CCM内存,这个内存直接和内核相连,除了内核之外谁都不能访问,那么我们怎么将其利用起来呢?这里以STM32F407ZGT6为例,简单的笔记下如何使用CCM内存,并进行简单分析。

参考链接

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,可以看下各个分区的占用情况,如下图:

 

Leave a Reply

13 − 8 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据