While working on a project using STM32F4* ARM core processor, I had a situation where I had to run the code from internal flash of the STM32F4. In almost all example applications you will find that this processor holds the executable code in the flash and executes from the internal flash. Internal flash is fast enough and large enough for most projects. However in certain cases you may need to execute from RAM and world wide web search may not get you much help. In this article I will show how to execute your code from RAM on an ARM core processor but the logic would apply to any. Assumption made here is that the code is sitting in the internal flash which needs to execute from the RAM.
First case
Let us work on a the case when you want to run only a single routine or a small module in RAM. This can be a case, for example, when you want to execute some compute extensive code in faster internal RAM which executes faster than Flash. Here is the trick to do it:
First, use attribute keyword to put the function a section "code_in_ram". Name of the section doesn't matter but use of the attribute to create a new section does.
__attribute__ ((long_call, section (".code_in_ram"))) void foo(void)
{
// Do something here
}
Second step would be to add one line in the linker script in data section, as shown below:
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(8);
_sdata = .;
*(.data)
*(.data*)
*(.code_in_ram) <--- Insert here
. = ALIGN(4);
_edata = .;
} >RAM AT> FLASH
With these two changes, the function foo would be linked in the RAM and your init code already in place in the startup *.s file shall take care of copying this code into the RAM. You don't need to know but the init code looks like:
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
How does it help? We are now telling the linker that the code in the section "code_in_ram" is supposed to be mapped to RAM but it is sitting in FLASH. Linker just follows what is in this script. After that the initialization code does the magic. Since the initialization code already has the logic in place to copy data section variables into RAM, we just piggy back the init code to copy the executable code "code_in_ram" in to the RAM. Init code is in the startup file (usually an assembly file starting with the name Startup and with .s extension).
Second case
This is the case when your entire source code is small enough to fit in the internal RAM and you want to execute it from RAM. This leaves most of your internal flash for other uses. This is an example of the Linker file and your linker file would look the same. Do not copy this linker snippet. It is here for illustration. Work on your linker script. Find where your text section is defined. It should look similar to the following:
.text :
{
. = ALIGN(4);
*(.text) <-- Remove this
*(.text*) <-- Remove this
. = ALIGN(4);
_etext = .;
} >FLASH
There may be another section in the same linker file which would look like. If you want to move every thing out of Flash, remove rodata as well.
.rodata :
{
. = ALIGN(4);
*(.rodata) <-- Remove this
*(.rodata*) <-- Remove this
. = ALIGN(4);
} >FLASH
Now look for the data section which looks like as follows: Move the text and rodata sections that you just commented out above to this section as shown below. Do not change anything else. This will make your entire code execute from RAM (Please note that tiny initialization code would still need to execute from flash before it jumps to RAM code)
_sidata = LOADADDR(.data);
.data :
{
. = ALIGN(8);
_sdata = .;
*(.data)
*(.data*)
*(.rodata) <--- Move it here in .data section
*(.rodata*) <--- Move it here
*(.text) <--- Move it here
*(.text*) <--- Move it here
. = ALIGN(4);
_edata = .;
} >RAM AT> FLASH
What we did here is same as in first case above, except that instead of copying one part of the code in RAM we are copying the entire text section (executable code) in the RAM. You can confirm that the addresses of all your code are mapped to RAM address in the map file generated by the linker and/or in the debugger.
Top comments (6)
It works, Thank you!
Just tested on STM32f100
I am glad it helped.
Great thanks for this, I was able to confirm at run time by getting the address of the function main and the address of the new ram function,
Trying it, but works not as expected.
it catch hard fault when trying load r4
/* Zero fill the bss segment. */
ldr r2, =_sbss
ldr r4, =_ebss
movs r3, #0
b LoopFillZerobss
Why did you have to touch that code?
I not touch that segment, just try run debug and look what happens. Generally not a problem with this segment, due debugging, CPU run away, but STM Cube keeps show wrong debug tracking.
but problem with entry point to start copy FLASH to RAM. and then pass control to RAM. I does`t see easy way :(