cancel
Showing results for 
Search instead for 
Did you mean: 

[partially solved] placing firmware on RAM

KShim.1738
Associate III

I placed the whole firmware on SRAM of STM32F746 and the reset handler is called from bootloader. the application firmware(app) is a simple one with led blinking and printing some messages on the terminal. I modified the linker script so that VMAs of all sections begins in SRAM. So it would be okay to place the firmware any address I want only if I can copy the sections to the correct VMA. The app works in its simplest form. But it doesn't work if I call a function with variable arguemnts like sprintf() or I call strlen().

I don't know that the problem causes this. Maybe due to newlib I use has been compiled with wrong options. Or I made a mistake with the linker script. What am I missing?

The layout for the custom bootloader and the apps on the FLASH is like below. The purpose of this layout is to update the firmware safely and roll back if something goes wrong.

0x0800 0000
 ------------
| bootloader |
|------------|
|            |
| app ver3   |
|            |
|------------|
|            |
| new ver2   |
|            |
 ------------

I don't like to change the start address of app in the linker script every time I compile the code. Fortunately the board has plenty of memory due to SDRAM. I'm not sure whether code can be run on SDRM or not. But I think I can place the whole app on the internal SRAM in worst case, which has the size of 320KB. It would be enough for Vector Table and text.

The bootloader copies the whole app from the flash to SRAM(or SDRAM if possible). And then it transfers the control to the app. It's not position independent code. The linker script will show what I am planning to do.

/* Entry Point */
ENTRY(Reset_Handler)
 
/* Highest address of the user mode stack */
_estack = 0x20050000;   /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x2000;      /* required amount of heap  */
_Min_Stack_Size = 0x2000; /* required amount of stack */
 
/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20020000, LENGTH = (320K - 128K)
FLASH (rx)     : ORIGIN = 0x08000000, LENGTH = 1024K
}
 
/* Define output sections */
SECTIONS
{
  _siisr = LOADADDR(.isr_vector);
 
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    _sisr = .;
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
    _eisr = .;
  } >RAM AT> FLASH
 
  _sitext = LOADADDR(.text);
 
  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    _stext = .;        /* define a global symbols at end of code */
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
 
    KEEP (*(.init))
    KEEP (*(.fini))
 
    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >RAM AT> FLASH
 
  _siothers = LOADADDR(.ARM.extab);
  .ARM.extab   :
  {
    . = ALIGN(4);
    _sothers = .;
    *(.ARM.extab* .gnu.linkonce.armextab.*)
  } >RAM AT> FLASH 
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >RAM AT> FLASH
 
  .SHELL_SYMBOL_TABLE :
  {
    /* section information for shell */
    . = ALIGN(4);
    PROVIDE(__shell_symbol_start = .);
    KEEP (*(.SHELL_SYMBOL_TABLE))
    . = ALIGN(4);
    PROVIDE(__shell_symbol_end = .);
  } >RAM AT> FLASH
 
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >RAM AT> FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >RAM AT> FLASH
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    . = ALIGN(4);
    _eothers = .;
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >RAM AT> FLASH
 
  /* Constant data goes into FLASH */
  _sirodata = LOADADDR(.rodata);
 
  .rodata :
  {
    . = ALIGN(4);
    _srodata = .;
    *(.rodata)
    *(.rodata*)
    . = ALIGN(4);
    _erodata = .;
  } >RAM AT> FLASH
 
  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);
 
  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
 
    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM AT> FLASH
 
  /* Uninitialized data section */
  . = ALIGN(4);
  .bss (NOLOAD):
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)
 
    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM
 
  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack (NOLOAD):
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM
 
  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }
 
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

And the binary is written into flash at 0x0802 0000 using openocd.

#!/bin/bash
 
TARGET=build/simple.bin
 
sudo openocd -f openocd.cfg -c "program ${TARGET} verify reset exit 0x08020000"

The boot loader looks like

static void start_app(void *sp, void *pc) {
    __asm("           \n\
          msr msp, r0 \n\
          bx r1       \n\
    ");
}
 
void copyF2R(void)
{
    uint32_t *pSrc;
    uint32_t *pDst;
 
    pSrc = (uint32_t *)0x08020000;
    pDst = (uint32_t *)0x20020000;
    /* the size of app is about 32KB with newlib */
    while(pDst < (uint32_t *)0x20040000) {
        *pDst++ = *pSrc++;
    }
}
 
    copyF2R();
    start_app(sp, pc);

The reset handler of the app written in C as follows.

void Reset_Handler(void)
{
    SystemInit();
 
    /* this must be right after SystemInit() */
    SCB->VTOR = (uint32_t)&_sisr;
    __DSB();
 
    __libc_init_array();
 
    /* Branch to main function */
    main();
 
    /* Infinite loop */
    while (1);
}

the following code in the app works.

uint8_t rwbuffer[64] = "rwbuffer\r\n"; /* global */
uint8_t *roptr = "rodata\r\n"; /* global */
 
    while(1)
    {
        toggleLED(LED1_Pin);
 
        HAL_UART_Transmit(&huart5, rwbuffer, 10, 0xffff);
        HAL_UART_Transmit(&huart5, roptr, 8, 0xffff);
        for(int i=0; i<100000000; i++);
    }

the code below doesn't work.

uint8_t rwbuffer[64] = "rwbuffer\r\n"; /* global */
sprintf(rwbuffer, "test %d\r\n", n++);

the code below doesn't work too.

uint32_t n; /* local */
n = strlen(rwbuffer);
HAL_UART_Transmit(&huart5, rwbuffer, n, 0xffff);

Any hints or suggestions would be appreciated.

Kyle

3 REPLIES 3
MM..1
Chief II

I dont check your idea perfectly, but i mean linked code cant work on two places, then same binary you cant use/run in ram and then in flash.

KShim.1738
Associate III

I've checked the variable argument function with mini_printf() from https://github.com/wkoszek/mini_printf. And it works, which means that it's not the problem of variable argument function.

mini_snprintf(rwbuffer, sizeof(rwbuffer), "test %d\r\n", n++);
HAL_UART_Transmit(&huart5, rwbuffer, 10, 0xffff);

I think there's something wrong with my libc.

KShim.1738
Associate III

I've considered other solutions

a. dual bank MCU such as F766, F777

b. two copies of the app are mapped to the same VMA(Virtual Memory Address) on SRAM or SDRAM

c. just keep two copies of the app with their own address space which is separated as in the picture above.

I couldn't find any problems with my libc. And I finally resorted to a boot loader and one copy of app so that I don't have to juggle with memory. Another copy will be on the server across the internet.

But I've got a hint why the app failed to run. It might be the DCache. Maybe it will run if I disable DCache before jumping to the app. Thank you for Clive about this idea. I've read his article.

I will test my idea when I have time.