2

I found a start up code for arm cortex m cores on internet and using those sources but I have some doubts regarding a function from the sources and here I am pasting the code and the respective linker scripts being used here.

// very simple startup code with definition of handlers for all cortex-m cores

// location of these variables is defined in linker script
extern unsigned __data_load;

extern unsigned __data_start;
extern unsigned __data_end;

extern unsigned __bss_start;
extern unsigned __bss_end;

extern unsigned __heap_start;

extern unsigned __init_array_start;
extern unsigned __init_array_end;

extern unsigned __fini_array_start;
extern unsigned __fini_array_end;

// main application
extern void main_app();

void copy_data() {
    unsigned *src = &__data_load;
    unsigned *dst = &__data_start;
    while (dst < &__data_end) {
        *dst++ = *src++;
    }
}

void zero_bss() {
    unsigned *dst = &__bss_start;
    while (dst < &__bss_end) {
        *dst++ = 0;
    }
}

void fill_heap(unsigned fill=0x55555555) {
    unsigned *dst = &__heap_start;
    register unsigned *msp_reg;
    __asm__("mrs %0, msp\n" : "=r" (msp_reg) );
    while (dst < msp_reg) {
        *dst++ = fill;
    }
}

void call_init_array() {
    unsigned *tbl = &__init_array_start;
    while (tbl < &__init_array_end) {
        ((void (*)())*tbl++)();
    }
}

void call_fini_array() {
    unsigned *tbl = &__fini_array_start;
    while (tbl < &__fini_array_end) {
        ((void (*)())*tbl++)();
    }
}

// reset handler
void RESET_handler() {
    copy_data();
    zero_bss();
    fill_heap();
    call_init_array();
    // run application
    main_app();
    // call destructors for static instances
    call_fini_array();
    // stop
    while (true);
}

Following is the linker description being used

SECTIONS {
    . = ORIGIN(FLASH);
    .text : {
        KEEP(*(.stack))
        KEEP(*(.vectors))
        KEEP(*(.vectors*))
        KEEP(*(.text))
        . = ALIGN(4);
        *(.text*)
        . = ALIGN(4);
        KEEP(*(.rodata))
        *(.rodata*)
        . = ALIGN(4);
    } >FLASH

    .init_array ALIGN(4): {
        __init_array_start = .;
        KEEP(*(.init_array))
        __init_array_end = .;
    } >FLASH

    .fini_array ALIGN(4): {
        __fini_array_start = .;
        KEEP(*(.fini_array))
        __fini_array_end = .;
    } >FLASH
}

SECTIONS {
    __stacktop = ORIGIN(SRAM) + LENGTH(SRAM);
    __data_load = LOADADDR(.data);
    . = ORIGIN(SRAM);

    .data ALIGN(4) : {
        __data_start = .;
        *(.data)
        *(.data*)
        . = ALIGN(4);
        __data_end = .;
    } >SRAM AT >FLASH

    .bss ALIGN(4) (NOLOAD) : {
        __bss_start = .;
        *(.bss)
        *(.bss*)
        . = ALIGN(4);
        __bss_end = .;
        *(.noinit)
        *(.noinit*)
    } >SRAM

    . = ALIGN(4);
    __heap_start = .;
}

My question is in the copy_data() function why do we need to assign the address of __data_load to a pointer *src? Is __data_load = LOADADDR(.data); is same as __data_start. What does the copy_data() function doing in the program? Thanks in advance.

user8540390
  • 143
  • 1
  • 10
  • 1
    It sets up the `.data` segment. Generally variables in a microcontroller end up in different segments depending on what kind they are, [see this](https://electronics.stackexchange.com/questions/237740/what-resides-in-the-different-memory-types-of-a-microcontroller/237759#237759). For how to write the whole start-up code of a MCU yourself, you can check [this](https://stackoverflow.com/questions/47933281/how-to-prevent-system-hang-before-watchdog-timer-task-kicks-in/47940277#47940277), but it's no task for a beginner and you might need to know basic assembler for the MCU. – Lundin May 29 '18 at 14:16
  • 1
    Btw this looks like C++ not C. `void fill_heap(unsigned fill=0x55555555)` is not valid C. Neither is `while (true);` since stdbool.h is never included. `call_init_array` looks like it is executing constructors for C++ objects with static storage duration. Only a bad (P)C++ programmer would write code this for a MCU: `main_app(); // call destructors for static instances call_fini_array(); // stop while (true);` I'm fairly certain this code wasn't written by a professional. – Lundin May 29 '18 at 14:29
  • Using C(++) to bootstrap C(++) is a bad idea. – old_timer May 30 '18 at 02:49
  • experimenting with your linker script, it does appear thus far that you are correct __data_load and __data_start are both in flash. which is likely not what you wanted. There are much easier ways to do this (without bootstrapping a language with the same language, without a bootstrap for the bootstrap). Use the tools themselves to simply see what they produce. – old_timer May 30 '18 at 02:52
  • start with C for your first bootstrap and linker script, and then someday if you really still feel the need, attempt C++. – old_timer May 30 '18 at 02:53

3 Answers3

2

The linker script instructs the linker to place the data in flash but link the code as if the data is in ram. In the startup code the data is then copied from the address the data is loaded at (the flash) to the address the data is supposed to be (RAM).

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
1

copy_data() copies memory reading from start address __data_load into the adress range starting from __data_start to __data_end

The total size copied is therefore __data_end - __data_start.

Of course you already have the data available at __data_load. The program copies it from FLASH to SRAM where it can be read and written as much as needed.

Attersson
  • 4,755
  • 1
  • 15
  • 29
  • The data is "loaded" in flash but there it can't be easily changed. So it has to be copied to ram first to become writable. Note: data, not rodata. – Goswin von Brederlow May 29 '18 at 14:01
  • 1
    No `>SRAM AT >FLASH` means it's virtual address is the SRAM but the load address (actual location in the image) is FLASH – Goswin von Brederlow May 29 '18 at 14:09
  • `__stacktop = ORIGIN(SRAM) + LENGTH(SRAM); __data_load = LOADADDR(.data); . = ORIGIN(SRAM);` meant the address in SRAM or flash? Can anyone tell what is the address of SRAM AT FLASH? since memory addresses are as follows: `MEMORY { FLASH(rx) : ORIGIN = 0x08000000, LENGTH = 32K SRAM(rwx) : ORIGIN = 0x20000000, LENGTH = 4K }` – user8540390 May 29 '18 at 14:22
  • `__stacktop` is `SRAM`. And `>SRAM AT >FLASH` is a virtual address in `SRAM`, mapping to an address in `FLASH`... so it is `FLASH` – Attersson May 29 '18 at 14:36
1

The problem

We have an issue in embedded code. Storage options are non-volatile but unmodifiable (flash/ROM, etc) or volatile modifiable storage. 'data' is initialized non-zero arbitrary values which can be modified (versus const or rodata). How can this be arranged?

A copy of the data is put in flash or ROM. This data is then copied to RAM where it is both read and written.


My question is in the copy_data() function why do we need to assign the address of __data_load to a pointer *src? Is __data_load = LOADADDR(.data); is same as __data_start. What does the copy_data() function doing in the program?

copy_data() is the solution to the above problem. It takes memory from the flash (load location) and copies it to RAM. A similar dichotomy can exist with virtual addressing. Where you need to arrange a physical address and virtual address contents to be the same before enabling an MMU. Linker documentation often calls the run/RAM location a 'VADDR'.

With an OS or some ROM bootloaders you may load from disk/MMC (NAND flash) to RAM and be able to circumvent copy_data(). It is only needed if your code will run directly from a non-volatile device. It can often be faster and simpler just to copy the entire image from flash to RAM. This depends on resources of course. Read access from RAM is often faster than flash. Again this will depend on your system.

artless noise
  • 21,212
  • 6
  • 68
  • 105