DEV Community

Rajeev Bharol
Rajeev Bharol

Posted on

Executing code from RAM on STM32 ARM Microcontrollers

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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                                                              
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 (4)

Collapse
 
cometninja profile image
Daniel Casey

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,

Image description

Collapse
 
knik0 profile image
Krzysztof Nikiel

It works, Thank you!
Just tested on STM32f100

Collapse
 
rbharol profile image
rbharol

I am glad it helped.

Collapse
 
vertual2001 profile image
Anatolii

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