Relocating code from FLASH to RAM

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Relocating code from FLASH to RAM

12,501 Views
lpcware-support
Senior Contributor I

Introduction

In most modern MCUs with built-in flash memory, code is normally executed directly from flash memory.  Various techniques, such as pre-fetch buffering, are used to ensure that code will execute with minimal or zero wait states, even at higher clock frequencies. However sometimes there can be advantages or requirements to relocate certain code into RAM - for example small, time critical functions, or functions that are used to actually update the flash memory itself. This FAQ examines some ways of moving such code into RAM for execution.

Note : The linker script template techniques described in this FAQ apply to LPCXpresso IDE v7.9.0 and later only. For more details, please see the FAQ:


Relocating specific functions into RAM

If you have specific functions in your code base that you wish to place into a particular block of RAM, then the simplest way to do this is to decorate the function definition using the macro __RAMFUNC provided by the LPCXpresso header file cr_section_macros.h

#include <cr_section_macros.h>
 __RAMFUNC(RAM)  void fooRAM(void) {...

For more details, please see the FAQ:

Relocating particular objects into RAM

In some cases, it may be required to relocate all of the functions (and rodata) from a given object file in your project into RAM. In LPCXpresso 7.9.0 and later, this can be acheived by providing three linker script template files into a linkscripts folder within your project. For example if it was required that all code/rodata from the files foo.c and bar.c were relocated into RAM, then this could be achieved using the following linker script templates:

main_text.ldt

*(EXCLUDE_FILE(*foo.o *bar.o) .text*)

main_rodata.ldt

*(EXCLUDE_FILE(*foo.o *bar.o) .rodata)
*(EXCLUDE_FILE(*foo.o *bar.o) .rodata.*)
*(EXCLUDE_FILE(*foo.o *bar.o) .constdata)
*(EXCLUDE_FILE(*foo.o *bar.o) .constdata.*) 
. = ALIGN(${text_align});

main_data.ldt

*foo.o(.text*) 
*foo.o(.rodata .rodata.* .constdata .constdata.*) 
*bar.o(.text*) 
*bar.o(.rodata .rodata.* .constdata .constdata.*) 
. = ALIGN(${text_align}); 
*(.data*)

What each of these EXCLUDE_FILE lines (in main_text.ldt and main_rodata.ldt) is doing in pulling in all of the sections of a particular type (for example .text), except for the ones from the named object files. Then in main_data.ldt, we specify explicitly that the text and rodata sections should be pulled in from the named object files. Note that with the GNU linker, LD, the first match found in the final generated linker script is always used, which is why the EXCLUDE_FILE keyword is used in the first two template files.

Note: EXCLUDE_FILE only acts on the closest input section specified, which is why we have 4 separate EXCLUDE_FILE lines in the main_rodata.ldt file rather than just a single combined EXCLUDE_LINE.

Once you have built your project using the above linker script template files, then you can check the generated .ld file to see the actual linker script produced, together with the linker map file to confirm where the code and rodata have been placed.

Relocating particular libraries into RAM

In some cases, it may be required to relocate all of the functions (and rodata) from a given library in your project into RAM. One example of this might be if you are using a flashless LPC43xx MCU with an external SPIFI flash device being used to store and execute your main code from, but you need to actually update some data that you are also storing in the SPIFI flash. In this case, the code used to update the SPIFI flash cannot run from SPIFI flash.

In LPCXpresso 7.9.0 and later, this can be acheived by providing three linker script template files into a linkscripts folder within your project. For example if it was required that all code/rodata from the library MYLIBRARYPROJ were relocated into RAM, then this could be achieved using the following linker script templates:

main_text.ldt

*(EXCLUDE_FILE(*libMYLIBRARYPROJ.a:) .text*)

main_rodata.ldt

*(EXCLUDE_FILE(*libMYLIBRARYPROJ.a:) .rodata)
*(EXCLUDE_FILE(*libMYLIBRARYPROJ.a:) .rodata.*)
*(EXCLUDE_FILE(*libMYLIBRARYPROJ.a:) .constdata)
*(EXCLUDE_FILE(*libMYLIBRARYPROJ.a:) .constdata.*)
. = ALIGN(${text_align});

main_data.ldt

*libMYLIBRARYPROJ.a:(.text*) 
*libMYLIBRARYPROJ.a:(.rodata .rodata.* .constdata .constdata.*) 
. = ALIGN(${text_align}); 
*(.data*)

Relocating majority of application into RAM

In some situations, you may wish to run the bulk of your application code from RAM - typically just leaving startup code and the vector table in Flash. In LPCXpresso 7.9.0 and later, this can be acheived by providing three linker script template files into a linkscripts folder within your project:

main_text.ldt

*cr_startup_*.o (.text.*) 
*(.text.main) 
*(.text.__main)

main_rodata.ldt

*cr_startup_*.o (.rodata .rodata.* .constdata .constdata.*) 
. = ALIGN(${text_align});

main_data.ldt

*(.text*) 
*(.rodata .rodata.* .constdata .constdata.*) 
. = ALIGN(${text_align}); 
*(.data*)

The above linker template scripts will cause the main body of the code to be relocated into the main (first) RAM bank of the target MCU, which by default will also contain data/bss, as well as the stack and heap.

If the MCU being targeted has more than one RAM bank, then the main body of the code could be relocated into another RAM bank instead. For example, if you wanted to relocate the code into the second RAM bank, then this could be done by providing the following data.ldt file instead of the main_data.ldt above:

data.ldt

<#if memory.alias=="RAM2">
*(.text*)
*(.rodata .rodata.* .constdata .constdata.*)
. = ALIGN(${text_align});
</#if>
*(.data.$${memory.alias}*)
*(.data.$${memory.name}*)

Note : memory.alias value is taken from the Alias column of the Memory Configuration Editor.

Debug issues:

location of main()

One thing to consider when relocating the majority of an application into RAM is whether main() is left in Flash or is relocated into RAM. The advantage to leaving it in Flash is that the default breakpoint at the start of main() to still be hit when a debug session is started. With main() in RAM, a software breakpoint will get set on the RAM address of main() by the debugger - but this will then be overwritten as the startup code executes and copies the relocated code into RAM.

If the code from main() does need to be executed from RAM, a simple workaround is to create a "dummy" main() which is left in Flash that then calls a renamed real_main() - which is relocated into RAM.

Alternatively, you can modify the debug launch configuration to force a hardware breakpoint to be used for the breakpoint on main()

long branch veneers and debugging

Due to the distance in the memory map between flash memory and RAM, you will typically require a "long branch veneer" between the function in RAM and the calling function in flash. The linker can automatically generate such a veneer for direct function calls, or you can effectively generate your own by using a call via a function pointer.

One point to note is that debugging code with a linker generated veneer can sometimes cause problems. This veneer will not have any source level debug information associated with it, so that if you try to step in to a call to your ram code, typically the debugger will step over it instead.

You can work around this by single stepping at the instruction level, setting a breakpoint in your RAM code, or by changing the function call from a  direct one to a call via a function pointer.

Related FAQs

Tags (2)
0 Replies