Fork me on GitHub

8. Data in RAM, Example

Now that we know, how to write linker scripts, we will attempt to write a program, and place the .data section in RAM.

The add program is modified to load two values from RAM, add them and store the result back to RAM. The two values and the space for result is placed in the .data section.

Listing 9. Add Data in RAM

        .data
val1:   .4byte 10               @ First number
val2:   .4byte 30               @ Second number
result: .4byte 0                @ 4 byte space for result

        .text
        .align
start:
        ldr   r0, =val1         @ r0 = &val1
        ldr   r1, =val2         @ r1 = &val2

        ldr   r2, [r0]          @ r2 = *r0
        ldr   r3, [r1]          @ r3 = *r1

        add   r4, r2, r3        @ r4 = r2 + r3

        ldr   r0, =result       @ r0 = &result
        str   r4, [r0]          @ *r0 = r4

stop:   b stop

When the program is linked, the linker script shown below is used.

SECTIONS {
        . = 0x00000000;
        .text : { * (.text); }

        . = 0xA0000000;
        .data : { * (.data); }
}

The dump of the symbol table of .elf is shown below.

$ arm-none-eabi-nm -n add-mem.elf
00000000 t start
0000001c t stop
a0000000 d val1
a0000001 d val2
a0000002 d result

The linker script seems to have solved the problem of placing the .data section in RAM. But wait, the solution is not complete yet!

8.1. RAM is Volatile!

RAM is volatile memory, and hence it is not possible to directly make the data available in RAM, on power up.

All code and data should be stored in Flash before power-up. On power-up, a startup code is supposed to copy the data from Flash to RAM, and then proceed with the execution of the program. So the program’s .data section has two addresses, a load address in Flash and a run-time address in RAM.

[Tip] Tip

In ld parlance, the load address is called LMA (Load Memory Address), and the run-time address is called VMA (Virtual Memory Address.).

The following two modifications have to be done, to make the program work correctly.

  1. The linker script has to be modified to specify both the load address and the run-time address, for the .data section.
  2. A small piece of code should copy the .data section from Flash (load address) to RAM (run-time address).

8.2. Specifying Load Address

The run-time address is what that should be used for determining the address of labels. In the previous linker script, we have specified the run-time address for the .data section. The load address is not explicitly specified, and defaults to the run-time address. This is OK, with the previous examples, since the programs were executed directly from Flash. But, if data is to be placed in RAM during execution, the load address should correspond to Flash and the run-time address should correspond to RAM.

A load address different from the run-time address can be specified using the AT keyword. The modified linker script is shown below.

SECTIONS {
        . = 0x00000000;
        .text : { * (.text); }
        etext = .; ❶

        . = 0xA0000000;
        .data : AT (etext) { * (.data); } ❷
}

Symbols can be created on the fly within the SECTIONS command by assigning values to them. Here etext is assigned the value of the location counter at that position. etext contains the address of the next free location in Flash right after all the code. This will be used later on to specify where the .data section is to be placed in Flash. Note that etext itself will not be allocated any memory, it is just an entry in the symbol table.

The AT keyword specifies the load address of the .data section. An address or symbol (whose value is a valid address) could be passed as argument to AT. Here the load address of .data is specified as the location right after all the code in Flash.

8.3. Copying .data to RAM

To copy the data from Flash to RAM, the following information is required.

  1. Address of data in Flash (flash_sdata)
  2. Address of data in RAM (ram_sdata)
  3. Size of the .data section. (data_size)

With this information the data can be copied from Flash to RAM using the following code snippet.

        ldr   r0, =flash_sdata
        ldr   r1, =ram_sdata
        ldr   r2, =data_size

copy:
        ldrb  r4, [r0], #1
        strb  r4, [r1], #1
        subs  r2, r2, #1
        bne   copy

The linker script can be slightly modified to provide these information.

Listing 10. Linker Script with Section Copy Symbols

SECTIONS {
        . = 0x00000000;
        .text : {
              * (.text);
        }
        flash_sdata = .; ❶

        . = 0xA0000000;
        ram_sdata = .; ❷
        .data : AT (flash_sdata) {
              * (.data);
        };
        ram_edata = .; ❸
        data_size = ram_edata - ram_sdata; ❹
}

Start of data in Flash is right after all the code in Flash.

Start of data in RAM is at the base address of RAM.

Obtaining the size of data is not straight forward. The data size is calculated from the difference in the start of data in RAM and the end of data in RAM. Yes, simple expressions are allowed within the linker script.

The add program with data copied to RAM from Flash is listed below.

Listing 11. Add Data in RAM (with copy)

        .data
val1:   .4byte 10               @ First number
val2:   .4byte 30               @ Second number
result: .space 4                @ 1 byte space for result

        .text

        ;; Copy data to RAM.
start:
        ldr   r0, =flash_sdata
        ldr   r1, =ram_sdata
        ldr   r2, =data_size

copy:
        ldrb  r4, [r0], #1
        strb  r4, [r1], #1
        subs  r2, r2, #1
        bne   copy

        ;; Add and store result.
        ldr   r0, =val1         @ r0 = &val1
        ldr   r1, =val2         @ r1 = &val2

        ldr   r2, [r0]          @ r2 = *r0
        ldr   r3, [r1]          @ r3 = *r1

        add   r4, r2, r3        @ r4 = r2 + r3

        ldr   r0, =result       @ r0 = &result
        str   r4, [r0]          @ *r0 = r4

stop:   b stop

The program is assembled and linked using the linker script listed in Listing 10, “Linker Script with Section Copy Symbols”. The program is executed and tested within Qemu.

qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
(qemu) xp /4dw 0xA0000000
a0000000:         10         30         40          0
[Note] Note

In a real system with an SDRAM, the memory should not be accessed right-away. The memory controller will have to be initialised before performing a memory access. Our code works because the simulated memory does not require the memory controller to be initialised.