I made some experiments to place functions in RAM, using GCC, and eventually achieved my goal, so I give some information here.
I have used a NucleoH743 board, but the examples can be adapted to any MCU that is able to execute code from ram.
Oh, I just achieved to made it, so I'm not a linker script specialist, so do not hesitate to show me errors, or inaccuracies, I'll edit the post accordingly.
First, why one wants to run code in RAM ?
among many interest :
1/ performance since TCM ram has fastest access than flash, especially if the buses are heavily used by DMA.
2/ for some STM32 family which has plenty of ram but few flash, it can be interesting to load code from external memory in ram and execute it.
3/ run a bootloader that will write in the flash
We will do 3 things :
a/ place and run a function in ITCM ram
b/ place the ISR vector table in ITCM ram
c/ place all the hal functions that are called from ISR in ITCM ram
I will explain b and c step in next post.
note that to keep examples simple, constant data that are used by functions in ITCM will stay in flash, but it’s possible to also relocate theses constants in DTCM ram if needed.
A/ place and run a function in ITCM ram
To do that, there are 3 steps :
a1 : declare a section in the linker script that will locate the object in ITCM, but store it in flash
a2 : at startup, copy the code from flash to ITCM ram
a3: in the source file , use attribute to specify that the code must fit ITCM
A1 : declare a section in the linker script that will locate the object in ITCM, but store it in flash
One must modify the linker script, so first copy os/common/startup/ARMCMx/compilers/GCC/ld/STM32H743xI.ld to your cfg directory, and modify your makefile to use your linker script file instead of the distrib one
in your STM32H743xI.ld file:
after REGION_ALIAS("PROCESS_STACK_RAM", ram5), add :
Code: Select all
/* RAM region to be used for hot code executed with zero wait state access */
REGION_ALIAS("ITCM_RAM", ram6);
at the end, after INCLUDE rules_memory.ld, add :
Code: Select all
SECTIONS
{
__itcm_flash_base__ = LOADADDR(.itcm_text);
.itcm_text : ALIGN_WITH_INPUT
{
. = ALIGN(4);
PROVIDE(__itcm_text_base_ram__ = .);
/* *(.ramfunc.$ITCM_RAM) */
KEEP(*(.itcm_text*))
KEEP(*(.bss.__itcm_text_*))
KEEP(*lld.o (.text*))
. = ALIGN(4);
PROVIDE(__itcm_text_end_ram__ = .);
} >ITCM_RAM AT> TEXT_FLASH
}
A2 : at startup, copy the code from flash to ITCM ram
The copy must be done early, I choose to put the code in __early_init, in the file board.c (that must be part of your project, don’t modify the distrib one), but perhaps there is a better place to do it ?
So I call a function in __early_init :
Code: Select all
void __early_init(void) {
copy_flash_to_itcm();
stm32_gpio_init();
stm32_clock_init();
}
and at the end of board.c, add the function :
Code: Select all
void copy_flash_to_itcm(void) {
extern const unsigned char __itcm_flash_base__;
extern unsigned char __itcm_text_base_ram__;
extern unsigned char __itcm_text_end_ram__;
size_t itcm_text_size = (size_t) (&__itcm_text_end_ram__ -
&__itcm_text_base_ram__);
memcpy(&__itcm_text_base_ram__, &__itcm_flash_base__, itcm_text_size);
}
you ‘ll have to #include <string.h> at the start of board.c to use memcpy
A3 : in the source file , use attribute to specify that the code must fit ITCM
the easiest for the end : where you want to declare your “hot function” :
Code: Select all
__attribute__((section(".itcm_text")))
void testITCMfun2(void)
{
chprintf(chp, "addr of fun2 is 0x%x", (uint32_t) &testITCMfun2);
}
when called, the function will print its own address, hopefully in ITCM section
Alexandre