How to place constant data in RAM

From SEGGER Wiki
Revision as of 14:40, 12 January 2023 by Lucas (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Constant data, such as const int Var = 1992; or const char* sVar = "SEGGER"; is usually placed by the compiler into the .rodata (read-only data) section, which is linked into Flash.

Note: Even data which is not explicitly declared constant may be placed into .rodata when the compiler figures that the data is not modified. This is usually the case for static (file-scope or function-scope) variables, which are not written to.

In some situations it might be beneficial to link constant data into RAM instead. This can be useful for example to improve performance on systems where access to (external) Flash is slower than access to RAM.

Place everything into RAM

If there is enough RAM available, all constant data can be linked into RAM. This solution requires a change only to the linker script, while the source code remains untouched.

with the SEGGER Linker

  1. Instruct the linker to initialize the sections .rodata and .rodata.* by copy.
  2. Move the placement of the sections from FLASH to RAM.

Before:

place in FLASH { section .rodata, section rodata.* };

or

place in FLASH with minimum size order      { block tdata_load,                                    // Thread-local-storage load image
                                             block exidx,                                          // ARM exception unwinding block
                                             block ctors,                                          // Constructors block
                                             block dtors,                                          // Destructors block
                                             readonly,                                             // Catch-all for readonly data (e.g. .rodata, .srodata)
                                             readexec                                              // Catch-all for (readonly) executable code (e.g. .text)
                                            };

After:

initialize by copy /* with packing=auto */ { section .rodata, section .rodata.* };

place in RAM { section .rodata, section .rodata.* };


with the GNU Linker and Embedded Studio

  1. Create a "no-load run" section .rodata_run in RAM.
  2. Instruct linker script generator to load initialization data into .rodata.

Before:

  <MemorySegment name="FLASH">
    <ProgramSection alignment="4" load="Yes" name=".rodata" />
  </MemorySegment>
  <MemorySegment name="RAM">
  </MemorySegment>

After:

  <MemorySegment name="FLASH">
    <ProgramSection alignment="4" load="Yes" runin=".rodata_run" name=".rodata" />
  </MemorySegment>
  <MemorySegment name="RAM">
    <ProgramSection alignment="4" load="No" name=".rodata_run" />
  </MemorySegment>


with the GNU Linker and gcc

  1. Add a run/virtual memory address for the .rodata section.
  2. Update startup code (usually crt0.s or startup.s) to copy from Flash to RAM.

Before:

  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
  } >FLASH

After:

  _sirodata = LOADADDR(.rodata); /* used by the startup to initialize rodata */
  .rodata :
  {
    . = ALIGN(4);
    _srodata = .;      /* used by the startup to initialize rodata */
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    _erodata = .;      /* used by the startup to initialize rodata */
  } >RAM AT> FLASH
 /* Copy the rodata segment initialization data from Flash to RAM */  
        movs    r1, #0
        b       .LLoopRodataInit
.LCopyRodataInit:
        ldr     r3, =_sirodata
        ldr     r3, [r3, r1]
        str     r3, [r0, r1]
        adds    r1, r1, #4
.LLoopRodataInit:
        ldr     r0, =_srodata
        ldr     r3, =_erodata
        adds    r2, r0, r1
        cmp     r2, r3
        bcc     .LCopyRodataInit

Place selected data into RAM

Usually there is not enough RAM available to place everything into it, or there are other limiting factors. In that case, only the selected data which provides the performance improvement (or other goal) can be declared as regular data.

Code changes

Data which is not declared const and is written to in a source file or has global scope is usually put into the regular data sections (.data and .bss) by the compiler. With relying on this behavior, constant data can be placed into RAM by removal of const and static keywords.

Before:

static const int Foo = 1992; /* Explicit const. Might be removed by in-file optimization. */
const int Bar = 1989;        /* Explicit const with global scope. Might only be removed by global optimization. */
static int Baz = 2022;       /* Usually regular data. Might become const or be removed by in-file optimization. */

After:

int Foo = 1992; /* Regular data. Might only be removed by global optimization. */
int Bar = 1989; /* Regular data. Might only be removed by global optimization. */
int Baz = 2022; /* Regular data. Might only be removed by global optimization. */

Extra: Code changes with Embedded Studio

If all constant data of a source file should be placed into RAM, the source code does not have to be modified.

Instead the section name for constant data of this file can be set in File Options -> Code -> Section -> Data Section Name to .ram_rodata.

Place selected data by section into RAM

Instead of relying on the compiler to put a symbol into either of the data sections, the symbols can be decorated with attributes to select to put them into a custom section. The custom section can then be placed and handled by the linker script and startup code.

Most compilers support the __attribute__ keyword and the section attribute: __attribute__((section("sectionname")))

Place data into the custom section .ram_rodata:

static const int Foo __attribute((section(".ram_rodata"))) = 1992; /* Might be removed by in-file optimization. */
const int Bar __attribute((section(".ram_rodata"))) = 1989;        /* Might only be removed by global optimization. */
static int Baz __attribute((section(".ram_rodata"))) = 2022;       /* Might be removed by in-file optimization. */

Note: Most linkers work on sections and can only remove sections from the final image which are completely unused. To enable removal of unused data in custom sections, it is recommended to put each symbol into a "sub-section".

static const int Foo __attribute((section(".ram_rodata.Foo"))) = 1992;
const int Bar __attribute((section(".ram_rodata.Bar"))) = 1989;
static int Baz __attribute((section(".ram_rodata.Baz"))) = 2022;

For convenience:

#define RO_RAM_SYM(s) s __attribute((section(".ram_rodata."#s)))
#define RO_RAM_ARR(s, n) s[n] __attribute((section(".ram_rodata."#s)))

static const int RO_RAM_SYM(Foo) = 1992;
const int RO_RAM_SYM(Bar) = 1989;
static int RO_RAM_ARR(Baz, 4) = { 1954, 1974, 1990, 2014 };