Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Executing from RAM ARM Cortex M4

I'm really confused by scatter files and the steps necessary to execute in RAM (for a bootloader).

From my understanding the startup.S file and sysinit need to be execute from Flash, and during that time the vector table needs to be copied over to RAM before jumping to main?

I also don't really understand the purpose of the scatter file, if I am copying the vectors and code to ram before jumping why do I even need it.

Here's my wrong scatter file:

LR_IROM1 0x14000000 0x00400000  {    ; load region size_region
  ER_IROM1 0x14000000 0x00400000  {  ; load address = execution address
    startup.o (RESET, +FIRST)
     * (InRoot$$Sections)
  }
  RW_IRAM1 0x10000000 0x00020000  {  ; RW data
    *.o
  }
  RW_IRAM2 0x20000000 0x00010000 {
    * (+RO,+RW,+ZI)
  }
}
like image 573
Cameron Moore Avatar asked Jun 13 '26 00:06

Cameron Moore


2 Answers

One solution for the bootloaders ram based linker script. Assuming you are using the gnu linker. There is more than one way to do this.

MEMORY
{
    ram : ORIGIN = 0x20000000, LENGTH = 0x2000
}
SECTIONS
{
    .text : { *(.text*) } > ram
    .rodata : { *(.rodata*) } > ram
    .bss : { *(.bss*) } > ram
    .data : { *(.data*) } > ram
}

If your code requires .bss to be zeroed you can add more code to the linker script and more code to the bootstrap, but gnu will do this for you if you use the above and guarantee there is at least one byte of .data somewhere (it will pad .bss with zeros to get the .data item(s) in the right relative place when doing the objcopy to a binary). Your choice on how to solve that one though. If you dont need .bss zeroed then swap .data and .bss make the binary smaller. you are either copying zeros in a very very efficient loop, or writing zeros in a maybe as efficient loop if you work the alignments in the linker script.

the copy and jump side if it would only need something like this

MEMORY
{
    rom : ORIGIN = 0x00000000, LENGTH = 0x2000
}
SECTIONS
{
    .text : { *(.text*) } > rom
    .rodata : { *(.rodata*) } > rom
}

worst case (for a simple assembly copy and jump), may be able to get rid of the .rodata line

The above are for gnu ld assuming that is what you are using, note that the memory names dont have meaning, you can instead do this:

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x2000
}
SECTIONS
{
    .text : { *(.text*) } > bob
    .rodata : { *(.rodata*) } > bob
}

or

MEMORY
{
    joe : ORIGIN = 0x00000000, LENGTH = 0x2000
}
SECTIONS
{
    .text : { *(.text*) } > joe
    .rodata : { *(.rodata*) } > joe
}

or

MEMORY
{
    pizza : ORIGIN = 0x00000000, LENGTH = 0x2000
}
SECTIONS
{
    .text : { *(.text*) } > pizza
    .rodata : { *(.rodata*) } > pizza
}

or

MEMORY
{
    thehut : ORIGIN = 0x08000000, LENGTH = 0x2000
    pizza : ORIGIN = 0x20000000, LENGTH = 0x2000
}
SECTIONS
{
    .text : { *(.text*) } > thehut
    .rodata : { *(.rodata*) } > thehut
    .bss : { *(.bss*) } > pizza
    .data : { *(.data*) } > pizza AT > thehut
}

you can attack things in the MEMORY or in the SECTIONS side with gnu ld. if you feel the need to have two .texts perhaps two .datas two .bss, etc (one for the copy/jump portion of the bootloader and one for the bootloader itself and have one linker script and link for all of the bootloader) you can do the this AT that thing or take the approach you are taking. But as well as the bootstrap for each being intimately connected to the linker script, you also have to use toolchain specific solutions to make the bootloader on ram fit into the proper .text/.bss, etc by either overriding them into some other (.my_bl_text...) or calling out object file names in the linker script or other solutions. Unfortunately the gnu linker script language has many features, and at the same time the documentation is more of a reference assuming you already know the language. Difficult to see how folks who have written elaborate linker scripts figured that out from the existing gnu documentation, and then trying to write your first one or modify someone elses. I recommend two programs the ram program and the copy jump program that contains the ram program as data.

like image 110
old_timer Avatar answered Jun 16 '26 09:06

old_timer


This scatter file worked for my STM32F3 setup, where part of the code is executed from RAM.

LR_IROM1 (iBOOTLOADER_AREA_START) (iBOOTLOADER_AREA_SIZE) 
{    
    ; Flash region
    ER_IROM1 (iBOOTLOADER_AREA_START) (iBOOTLOADER_AREA_SIZE)  
    {           
        startup_stm32f30x.o (RESET, +First)
        startup_stm32f30x.o 
        system_stm32f30x.o
        *(InRoot$$Sections)
    }

    ; Executable code in RAM
    ER_RAM_EXEC (iRAM_START) (iBOOTLOADER_AREA_SIZE)
    {
        *.o
        .ANY (+RO)
    }
  
    ; Read-write and zero-initialized data
    RW_IRAM1 (iRAM_START + iBOOTLOADER_AREA_SIZE) (iRAM_SIZE - iBOOTLOADER_AREA_SIZE)
    {  
        .ANY (+RW +ZI)
    }
}

I had to include all the sections from startup_stm32f30x.o in Flash. Before doing this, I encountered linker errors.

Additionally, I had to include system_stm32f30x.o in the Flash region because the Reset_Handler in startup_stm32f30x.s calls SystemInit() (loaceted in system_stm32f30x) before the code is copied from Flash to RAM.

If system_stm32f30x.o were located only in the RAM region, it wouldn't be available when the CPU jumps to SystemInit right after reset — causing a fault.

Here's the relevant section from startup_stm32f30x.s for reference:

Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit  ;Call SystemInit -> Has to be in Flash
                 BLX     R0
                 LDR     R0, =__main      ;In this part the code is copyed from Flash to RAM
                 BX      R0
                 ENDP

So the setup also depends on how your startup file is implemented, make sure the critical initialization code which is called bevor the __main stays in Flash.

like image 23
Fabian Avatar answered Jun 16 '26 09:06

Fabian