Execution from RAM in Embedded systems
Unlike application programs in computers, software in embedded systems do not execute from RAM. In vast majority of modern embedded system architectures, programs (instructions) are stored in microcontroller’s flash memory (code flash/ program flash) and directly executes in place. Earlier, ROM were used for program storage and execution.
Due to emerging technologies in memory devices, nowadays flash memory is being used. RAM is a volatile memory and contains nothing upon reset or power cycle. It is used to store data and hold the value of variables at run time. One can safely develop an embedded product by storing and executing the program in code flash (using downloading tool and debugger). MCU’s have dedicated portions of memory space for code flash and RAM. Memory maps in advanced microcontrollers contain Data flash, DSPR, PSPR, Boot ROM, EEPROM, debug memory etc. To make it simple, we will consider code flash to store and execute the programs and RAM to store the variables and data. There are certain situations where it becomes mandatory to run subroutines from RAM.
This article covers the need to copy a code section to RAM, various ways to implement it, its advantages and disadvantages. Copying to RAM means hosting two copies of code– one in flash and other in RAM.
Need of Execution from RAM :
- During reprogramming of flash memory (Bootloader functionality), certain functions to be executed from RAM like erase program flash, write data to program flash. The code shall not run from the same flash memory that is being erased/ programmed. In some chips, flash memory freezes while performing the operations on flash. Hence the code section is required to be placed in RAM and execute.
- During reprogramming of flash memory (Bootloader functionality), Interrupt vector addresses and ISR’s to be placed and executed from RAM.
- Bootloaders executing from RAM. The safest way for bootloader functionality is to receive the bootloader file from host device and place the complete code in RAM.
- Running programs from RAM is much faster than running from flash e.g. placing frequently executing code in RAM for time optimization.
- Some schedulers in embedded systems create task in RAM which points to program in flash.
- Run the application from RAM while flash operation is in progress.
Copy code section and Execute from RAM :
1. Some controllers provide quite a straight forward way to copy a code to RAM. All you need is, go through toolchain and user manuals. Different ways are listed below –
- Some controllers provide an attribute or an API or compiler directives to copy code to RAM.
- In some controllers, start-up routine runtime code (which itself executes from flash) copies program opcodes from flash to RAM as per need as it does for initialized variables.
- In some controllers, update of linker script would be enough to place the code section in RAM. But there must be some source code which copies code section to RAM. In most of the cases, internally done by startup routine.
2. Copy function to RAM –
- Below snippet copies the func_to_be_copied() function to RAM. dummy_endfunc() is located at the end of function to determine the size of the func_to_be_copied(). Ensure that the dummy_endfunc() is not optimized by compiler. Compiler directives like “#pragma” can be used to avoid optimization. ExecuteRamFunc() can be called to execute func_to_be_copied() from RAM.
3. Size of the function can also be calculated by the use of return statement that can be placed at the end of func_to_be_copied(). Start copying the function till you encounter the opcode of return statement. There exist a risk that the data used inside function can be same as the return statement’s opcode. For this, you need to understand the alignment requirements of the instructions by referring the user manual.
4. Copy code section to RAM –
Consider you have a piece of code placed in flash memory say a bootloader code which needs to be executed from RAM. The size of bootloader should be well known in advance. A signature byte can also be written at the end of bootloader code while downloading to flash to know the code size. If the size of bootloader is not known then the size of section allocated can be considered. The section of code can be copied to RAM in same way as explained in point no. (2). Make sure that the RAM is reserved in the linker script to be used for instruction execution.
While executing from RAM, consider the following –
- The RAM used for instruction execution shall be reserved either through linker script or by declaring global buffer.
- The start address of copied section shall be known so that the Program Counter can be pointed correctly or execute by pointing function pointer to the correct address.
- RAM test can be performed at boot time to ensure its correctness.
- Performance –
Time optimization if the frequently executed critical routines are placed in RAM. RAM execution is significantly faster than flash.
- Decrease in power consumption –
If the whole application is executing from RAM, flash can be powered off.
- Short addressing is usually required if code is executing from RAM i.e. accessing the controller’s RAM, data flash or EEPROM can be accessed by short pointers whereas if the code is executing from flash (flash are typically large memories, 100’s of KB to MB’s), it would need long addressing. In short addressing, assembler usually generates the instructions like sjmp, acall etc. which typically takes lesser number of machine cycles.
- RAM is a scarce resource, expensive and is usually of much smaller size compared to flash. If the non-static code is placed in RAM, it is the wastage of RAM space, thereby limiting the usage of variable data.
- Hosting of the same routine at two places- one in flash and other in RAM.
- Debugging code in RAM is hard. Debugging can be done in disassembly. ELF files are generally used for debugging which contains debug information, symbol tables etc. If the code is moved somewhere else, you cannot see the symbols in debugger, but the instructions executing in disassembly window can be viewed and debugged.
- Sometimes executing from RAM results in unexpected hard fault. Hence, it is recommended to first execute from flash, test the functionality and then re-execute from RAM.