c++gccstm32ramflash-memory

How does one run code from RAM on a STM32?


I have this really simple "Hello world" piece of software (see my project on Github), running on a STM32WB55 Nucleo board (basically, it sends "HELLO WORLD\n" via USART1, every 1000 ms).

I would be particularly happy if I could manage to run this piece of software from RAM, instead of Flash. This MCU has 196604 bytes of RAM. And my project has a total size of 13332 bytes. So size should not be an issue.

What I'd like to do is: Program starts: load the program in RAM. Afterwards, disable flash, as to increase the overall performance of my program (faster access time) and decrease power consumption.

From what I've read, I should modify the linker script as to create a special section to place the code into and afterwards disable the MCU's flash.

Being the beginner that I am, I have no clue as to where to start.

PS: I'm using CLion (CMake) and GCC.


Solution

  • Use the linker script to place everything into RAM. I see you've already got a linker script (STM32WB55RGVX_RAM.ld) that looks like it does this. you can probably activate it by changing set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/STM32WB55RGVX_FLASH.ld)

    You can see that it defines two memory areas:

    MEMORY
    {
    RAM (xrw)           : ORIGIN = 0x20000000, LENGTH = 192K
    FLASH (rx)          : ORIGIN = 0x08000000, LENGTH = 512K
    }
    

    and later you can see that it is placing all the code bits into RAM. the "FLASH" memory area, although defined, is never actually used by this linker script and will be empty. compare with the _FLASH.ld linker script.

    After building your application you can load the the .elf using gdb. gdb will faithfully load all the sections into the areas the link has placed them.

    The last part will be actually running the code.

    If the entrypoint is correctly set gdb will also initialize the program counter to point there. Then you can use the usual continue next etc. to debug your code in RAM. the entrypoint can be moved arbitrarily with someting like -e 0x20000000 in the gcc linker command.

    # example gdb commands to execute output.elf from RAM
    
    # load the elf into gdb so that we can get debug information from it
    file output.elf
    
    # reset the mcu
    monitor reset init
    
    # load the elf into RAM
    load output.elf
    
    # verify that the register are correct
    info registers
    
    # run until main
    break main
    continue
    

    When you start execution, the first thing that will run is actually not main. What will run is all the startup code required to set up the C execution environment. This includes setting up the stack... basically everything in startup_stm32wb55rgvx.s... hence I have set a break point on main and executed until there.

    As a side note. You can build incredibly small executables when you don't actually need the startup code. If you can live with a less than complete execution environment you can set up the stack pointer yourself. This is usefull when you want to use the rest of the RAM for something else or want to optimize load times. Think post production steps to set up some things only the micro has access to. Or even post production testing. For this I recomend the python library pyswd it's an absolute godsend.

    --edit--

    I just noticed that you want the program you FLASHED to execute some code from RAM... In this case it is even simpler.

    You don't even need to edit your linker script. Simply place the function in a RAM section (conveniently RamFunc exists)

    __attribute__ ((longcall, section(".RamFunc"))) void test(){
        // disable_flash... see your datasheet
        // we do this from RAM, because disabling FLASH while executing from FLASH would be dumb.
        disable_flash();
        // do the RAM things... remember that all the symbols you use must be RAM symbols at this point.
        dothething();
    }
    
    int main(void) {
        // jump to RAM
        test();
    }