STM32F407在RAM中执行程序

STM32F407在flash中执行代码的速度比在ram中执行代码的速度快。因为STM32F407有一颗“自适应实时存储器加速器”,这里不讨论ART Accelerator的加速方案。
把代码放在RAM中执行纯粹是为了学习。

将个别函数的代码放到RAM中运行

使用自己编写的链接脚本(sct文件)。
在这里插入图片描述
再来看自己编写的分散加载文件内容:

LR_IROM1 0x08000000 0x00004000  {    ; load region size_region
  ER_IROM1 0x08000000 0x00004000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 0x20000000 0x00100000  {  ; RW data
   .ANY (+RW +ZI)
   *.o (RAM_SPEED)
  }
}

LR_ROM1后边的0x08000000是加载地址(代码存储时的地址),0x00004000是加载域的大小。
ER_IROM1 0x08000000是链接地址(代码执行时的地址,PC寻址的地址),0x00004000是执行域的大小。
在链接脚本中定义了一个代码段RAM_SPEED。
程序中将函数指定到代码段RAM_SPEED。

#pragma arm section code = "RAM_SPEED"
void my_delay(void)
{
	volatile uint16_t i,j;
	for(i=0;i<1000;i++)
		for(j=0;j<1000;j++);
}
#pragma arm section

编译链接后的加载地址和链接地址如下:
在这里插入图片描述
运行时PC指针如下图:
在这里插入图片描述
PC指针确实指向了SRAM区域,而不是FLASH区域。
通过打印发现my_delay在SRAM中运行时,耗时89453us。my_delay在FLASH中运行时,耗时71542us。也证实了在F407芯片上SRAM运行程序比FLASH运行程序慢。

F407有一块CCM,我们把程序放到CCM中是否可行?
修改链接脚本

LR_IROM1 0x08000000 0x00004000  {    ; load region size_region
	ER_IROM1 0x08000000 0x00004000  {  ; load address = execution address
		*.o (RESET, +First)
		*(InRoot$$Sections)
		.ANY (+RO)
		.ANY (+XO)
	}
	RW_IRAM1 0x20000000 0x00100000  {  ; RW data
		.ANY (+RW +ZI)
	}
	RW_IRAM2 0x10000000 0x00010000  {  ; RW data
		*.o (RAM_SPEED)
	}
}

答案是不行,程序执行到my_delay的时候就hardfault了,原因是CCM呢M4内核只通过D-BUS总线相连,M4内核是通过I-BUS执行取指令的,所以不行。
还有一种方式是通过__attribute__声明函数,将函数声明在RAM_SPEED代码段,sct文件和上边那种方式一样。

__attribute__((section("RAM_SPEED"))) void my_delay(void)
{
	volatile uint16_t i,j;
	for(i=0;i<1000;i++)
		for(j=0;j<1000;j++);
}

将整个工程代码放到RAM中运行

这种方案思路是写两套代码,一套是Boot代码,一套是APP代码。Boot代码在flash中存储在flash中执行,Boot上电将APP的代码拷贝到SRAM中,再跳转到SRAM中执行APP代码。APP代码是在flash中存储,在SRAM中执行。

Boot程序:使用默认的分散加载文件,target选项卡如下:
在这里插入图片描述

	/*其它必要操作*/
	/*从flash中把代码搬运到RAM中  共60K*/
	memcpy(p,(uint8_t *)0x08004000,60*1024);

	jump2app = (IapFun)*(vu32*)(0x20000800 + 4);
	MSR_MSP(*(vu32*)0x20000800);//存0x20000800地址处开始存放程序
	jump2app();  //跳转到APP

由上图和代码可以看出,我们给Boot分配了16KByte flash,2Kbyte RAM,这个根据个人实际情况分配。
APP程序:
先看一下分散加载文件

LR_IROM1 0x08004000   0x000FC000{    ; load region size_region
  ER_IROM1 0x20000800 0x0000F000{  
   *.o (RESET, +First)  
;  *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
  }
  RW_IRAM1 +0  {  ; RW data
   .ANY (+RW +ZI)
  }
}

Boot程序占用了16K Byte flash,那么我们应用程序就从0x08004000开始存储,也就是APP 分散加载文件的加载地址是0x08004000,给APP分配了0x000FC000个Bytes flash空间。APP的执行域(链接地址)从0x20000800开始,这里想一下能不能从0x20000000开始,肯定不行的呀,如果我们改成了0x20000000,那么Boot程序拷贝的时候就往0x20000000开始的地址处拷贝数据,但是拷贝的那一刻是在Boot中完成的,Boot会使用0x20000000到0x20000800这一段RAM,所以不行。
ER_ROM1中,把RESET链接到执行域(0x20000800)最前边,RO(只读数据段)和XO(执行段)也链接到执行域。
RW_IRAM1中,把初始化不为0的数据段和BSS段放到执行域RW_IRAM1中。
到这里理论上就可以了,我们需要做的核心工作:
1.在Boot程序从Flash中拷贝APP代码到SRAM。
2.跳转到SRAM,运行APP。
3.编写APP的分散加载文件。

但是实际上由于ST官方的启动文件中调用了__main,导致我们修改APP的分散加载文件后会链接报错。如下图
在这里插入图片描述
报错的意思就是entry那些数据段不能放到非启动区域,
由于__main是库文件,所以我们无法修改,也不知道如何修改。所以我放弃了使用__main,我大概知道__main库函数做了那些工作,__main函数就是把RW数据段从flash中搬运到RAM中,把RAM中的BSS数据段清零,最后跳转到main函数,那我们就自己在启动文件中实现这些工作。
汇编文件修改成如下代码:

; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Stack_Size      EQU     0x00000400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size       EQU     0x00000200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
                EXPORT  __Vectors
                EXPORT  __Vectors_End
                EXPORT  __Vectors_Size

__Vectors       DCD     0x20000800               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     0                          ; Reserved
                DCD     SVC_Handler                ; SVCall Handler
                DCD     DebugMon_Handler           ; Debug Monitor Handler
                DCD     0                          ; Reserved
                DCD     PendSV_Handler             ; PendSV Handler
                DCD     SysTick_Handler            ; SysTick Handler

                ; External Interrupts
                DCD     WWDG_IRQHandler                   ; Window WatchDog                                        
				;省略***
                DCD     FPU_IRQHandler                    ; FPU
                                         
__Vectors_End

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
					 
					 
				IMPORT	mymain
				IMPORT 	my_memcpy
				IMPORT	my_memset
                IMPORT  SystemInit
					
				IMPORT	|Image$$RW_IRAM1$$RW$$Base|
				IMPORT	|Load$$RW_IRAM1$$RW$$Base|
				IMPORT	|Image$$RW_IRAM1$$RW$$Length|
					
				IMPORT	|Image$$RW_IRAM1$$ZI$$Base|
				IMPORT	|Image$$RW_IRAM1$$ZI$$Length|					 
					 
				;重定位RW段
				LDR     R0, =|Image$$RW_IRAM1$$RW$$Base|
				LDR     R1, =|Load$$RW_IRAM1$$RW$$Base|
				LDR     R2, =|Image$$RW_IRAM1$$RW$$Length|
				BL		my_memcpy
				
				;清除BSS段
				LDR     R0, =|Image$$RW_IRAM1$$ZI$$Base|
				LDR     R1, =0
				LDR     R2, =|Image$$RW_IRAM1$$ZI$$Length|
				BL		my_memset			 
					 
;				BL	SystemInit
                LDR     R0, =SystemInit
                BLX     R0 

;				BL	mymain
                LDR     R0, =mymain
                BX      R0					 
					 
					 
					 
				;IMPORT  SystemInit
				;IMPORT  __main

                 ;LDR     R0, =SystemInit
                 ;BLX     R0
                 ;LDR     R0, =__main
                 ;BX      R0
                 ENDP

; Dummy Exception Handlers (infinite loops which can be modified)

NMI_Handler     PROC
                EXPORT  NMI_Handler                [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler          [WEAK]
                B       .
                ENDP
MemManage_Handler\
                PROC
                EXPORT  MemManage_Handler          [WEAK]
                B       .
                ENDP
BusFault_Handler\
                PROC
                EXPORT  BusFault_Handler           [WEAK]
                B       .
                ENDP
UsageFault_Handler\
                PROC
                EXPORT  UsageFault_Handler         [WEAK]
                B       .
                ENDP
SVC_Handler     PROC
                EXPORT  SVC_Handler                [WEAK]
                B       .
                ENDP
DebugMon_Handler\
                PROC
                EXPORT  DebugMon_Handler           [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler             [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler            [WEAK]
                B       .
                ENDP

Default_Handler PROC

                EXPORT  WWDG_IRQHandler                   [WEAK]                                        
                ;省略
                EXPORT  FPU_IRQHandler                    [WEAK]

WWDG_IRQHandler                                                       
 ;省略
FPU_IRQHandler
   
                B       .

                ENDP

                ALIGN

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap
                 
__user_initial_stackheap

                 LDR     R0, =  Heap_Mem
                 LDR     R1, =(Stack_Mem + Stack_Size)
                 LDR     R2, = (Heap_Mem +  Heap_Size)
                 LDR     R3, = Stack_Mem
                 BX      LR

                 ALIGN

                 ENDIF

                 END

;************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE*****

main函数的名字我修改了mymain。在汇编中调用了my_memcpy和my_memset函数,代码实现如下

void my_memcpy(void *pcdest,void *pcsrc,int len)
{
	uint8_t *dest = pcdest;
	uint8_t *src = pcsrc;
	while(len--)
	{
		*dest++ = *src++;
	}
}
void my_memset(void *pcdest,int val,int len)
{
	uint8_t *dest = pcdest;
	while(len--)
	{
		*dest++ = 0;
	}
}

我栈顶指针故意写成0x20000800,因为此时此刻已经来到了APP的世界,0x20000000到0x20000800属于Boot,我们在APP中又把这2K Bytes空间利用起来了,分配给了APP的栈空间。

这个APP程序虽然可以在RAM运行,但是有个问题,就是链接的时候把工程中的所有代码都链接到映像文件了,导致映像文件特别大,我们需要的是只链接那些调用到的代码,把所有都链接进去的原因是程序缺少指定一个入口点,这是在ARM LINK手册中看到的,如下图
在这里插入图片描述
我不知道如何解决,有知道的告诉一下。

  • 5
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在将STM32代码从Flash搬移到RAM的过程,我们首先需要确保芯片的RAM容量足够存储代码。然后,我们需要将代码的链接脚本进行修改,以便将代码从Flash的地址重新定位到RAM的地址。接下来,我们需要进行代码的复制操作,将Flash的代码复制到RAM。 为了完成这个过程,我们可以使用如下的步骤: 1. 修改链接脚本:打开链接脚本文件并进行修改,将代码的起始地址和大小重新定义为RAM的地址和大小。 2. 修改代码运行起始位置:检查代码的启动函数,确保它指向RAM的起始位置,而不是Flash的起始位置。 3. 复制代码到RAM:在代码的启动函数,我们需要添加复制代码到RAM的操作。可以使用内置的复制函数(如`memcpy()`)或者手动复制方式进行。将Flash的代码复制到RAM后,确保更新相关变量的地址。 4. 设置向量表位置:在引导代码的开头,我们需要设置向量表的位置为RAM。这需要根据具体的芯片进行设置,可以通过修改SCB的VTOR寄存器来实现。 5. 更新断向量表地址:在启动文件,确保断向量表的地址已经更新为RAM的地址。这样做可以确保芯片在断发生时正确地跳转到RAM断处理函数。 6. 验证代码正常运行:重新编译和烧写代码,并确保所有代码正常运行,没有出现异常或错误。 总结起来,将STM32代码从Flash搬移到RAM需要修改链接脚本和代码运行起始位置,并进行代码的复制操作。同时,还需要设置向量表的位置,更新断向量表地址,并验证代码的正常运行。这样做可以提高代码的执行速度,尤其适用于性能要求较高的应用。 ### 回答2: 将STM32代码从Flash搬移到RAM的主要目的是为了提高代码的执行效率和响应速度。在Flash存储的代码是只读的,因此每次执行代码时,MCU都需要从Flash读取指令,这会导致一定的读取延迟和访问速度下降。而将代码搬移到RAM后,可以直接从RAM读取指令,以极大地减少读取延迟和提高执行效率。 搬移代码的操作一般分为两步:将代码从Flash复制到RAM,并将复制后的代码重新定位到RAM的起始地址。这样,当代码执行时,MCU会首先从RAM读取指令,而不需要再每次都从Flash读取。 搬移代码的过程可以通过使用相关函数或指令来完成。在STM32,可以使用HAL库的相应函数,如HAL_FLASHEx_DATAEEPROM_Copy()函数来实现将Flash的代码复制到RAM。在复制完成后,还需要根据具体的MCU型号和使用的开发工具,设置复制后代码的起始地址,以便MCU能够正确地访问RAM的代码。 需要注意的是,将代码搬移到RAM后,需要合理利用RAM的容量和管理RAM的使用,因为RAM的容量一般比Flash有限。在复制代码之前,应该先估计代码的大小,并确保RAM有足够的空间来存储代码。此外,还可以考虑将一些频繁执行的代码块搬移到RAM,以进一步提高执行效率。 总结来说,将STM32代码从Flash搬移到RAM是为了提高代码的执行效率和响应速度。通过复制代码到RAM并设置正确的起始地址,可以减少访问延迟,提高读取速度和执行效率。然而,在搬移代码时需要注意RAM的容量限制,并合理管理RAM的使用。 ### 回答3: 将STM32的代码从Flash搬移到RAM可以提高代码执行效率和速度。通常情况下,STM32的代码存储在Flash,当在运行时需要执行部分代码时,会从Flash读取指令并执行。搬移到RAM后,所有代码都存储在RAM,运行时不再需要从Flash读取指令,而是直接在RAM执行指令,从而减少了访问存储器的时间。 实现STM32代码从Flash搬移到RAM的步骤如下: 1. 在代码工程将Flash部分的代码复制到RAM区域的一部分以确保代码可用。可以在代码声明一个特殊的RAM段,将相关函数和变量放入其。 2. 通过编译器和链接器设置,将这部分特殊RAM段的起始地址和大小与MCU的RAM进行关联。这样,编译器在生成可执行文件时会将相应的代码放到RAM区域。 3. 在代码修改启动向量,使得MCU重启后直接从RAM启动,而不是从Flash启动。这可以通过设置复位向量表的复位向量地址为RAM的起始地址来实现。 4. 对于涉及到断向量表的代码,还需要修改断向量表,使得断服务程序能够从RAM正确地执行。 通过将代码搬移到RAM,可以减少Flash访问的延迟和读取时间,提高代码执行速度和效率。这在对实时性要求较高的应用特别有用,例如控制任务响应、数据处理和实时通信等。然而,需要注意的是,将大量代码从Flash搬移到RAM可能会导致RAM资源紧张,因此需要仔细评估代码规模和RAM容量,以确保RAM能够容纳所需的代码和数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值