cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F401 Using flash memory sector 0 as storage

Chuckero
Associate II

Hello,

I'm searching for a way to use sector 0 as storage memory but no luck so far.

I'm using STM32F401CCU6 and I'm working on a firmware which have to save about 300 bytes in a no volatile memory. For this microcontroller family we have this memory map:

Sector   -  Address ini    ~ Address end   - Size
Sector 0 - 0x0800 0000 ~ 0x0800 3FFF - 16 Kbytes
Sector 1 - 0x0800 4000 ~ 0x0800 7FFF - 16 Kbytes
Sector 2 - 0x0800 8000 ~ 0x0800 BFFF - 16 Kbytes
Sector 3 - 0x0800 C000 ~ 0x0800 FFFF - 16 Kbytes
Sector 4 - 0x0801 0000 ~ 0x0801 FFFF - 64 Kbytes
Sector 5 - 0x0802 0000 ~ 0x0803 FFFF - 128 Kbytes
Sector 6 - 0x0804 0000 ~ 0x0805 FFFF - 128 Kbytes
Sector 7 - 0x0806 0000 ~ 0x0807 FFFF - 128 Kbytes

So far I already used a little bit more then 66kB, which means, my firmware is using sectors 0 to 4 and increasing.

Now, I'm already saving the data in flash memory, all good for the sector 5 using HAL. But it's only 500 bytes in a block of 128Kbytes, it's a waste of memory, and when the firmware reaches the block 5 I'll have to move the data to next sector.

So, the question is: is it possible to use sector 0 as storage and make the boot start from block 1?

The best information I found about it, was this post, but it's about STM32F7 and that solution is not available for F4:
https://stackoverflow.com/questions/56896375/how-can-i-change-the-start-address-on-flash/56902284#56902284

Thank you all.

1 ACCEPTED SOLUTION

Accepted Solutions

Try this

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20010000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;;      /* required amount of heap  */
_Min_Stack_Size = 0x400;; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
FLASH_ISR (rx)      : ORIGIN = 0x08000000, LENGTH = 48K
FLASH_HOLE (rx)      : ORIGIN = 0x0800C000, LENGTH = 16K
FLASH (rx)      : ORIGIN = 0x08010000, LENGTH = 256K-64K
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
}

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH_ISR

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH_ISR

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH_ISR

  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH_ISR

  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH_ISR

  /* Reserve section 3 from flash memory. */
  .myDataStorage :
  {
    . = ALIGN(4);
    KEEP(*(.myDataStorage)) ;
    . = ALIGN(4);
  } > FLASH_HOLE

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.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 */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >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 :
  {
    /* 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 :
  {
    . = ALIGN(4);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(4);
  } >RAM

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

 

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

View solution in original post

14 REPLIES 14
jiangfan
ST Employee

in fact, no need to make the boot start from block 1.

it is possible to use part of sector 0 as storage as shown below.

#pragma location = 0x08003000
uint8_t DATA_IN_FLASH[500] = {0xFF}; /* DATA_IN_FLASH at fixed address: Sector 0 */

You can't change the boot address.

You can carve a hole in your image so you can use sector 1, 2 or 3 as a 16KB configuration space.

You could use sector 0, but you'd want to prepend the Vector Table to your 300 bytes. You could create a vector table deeper in the IMAGE, say 0x08010000 and point SCB->VTOR at that, and then copy those first 512 bytes, or whatever the vector table size, to 0x08000000 at the front of sector 0.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Danish1
Lead II

There's an inherent risk in what @Tesla DeLorean says for using sector 0

You could use sector 0, but you'd want to prepend the Vector Table to your 300 bytes. You could create a vector table deeper in the IMAGE, say 0x08010000 and point SCB->VTOR at that, and then copy those first 512 bytes, or whatever the vector table size, to 0x08000000 at the front of sector 0.

The problem occurs if you ever need to "recycle" sector 0 - that is you've used up all the storage locations in that FLASH sector so you erase it before starting again. There will be a short period of time between the erase and rewriting the vectors in the first 512 bytes. If the processor resets in that period, perhaps due to loss-of-power, EMC event or bug in the code, the processor will not be able to restart executing your code. It will be "bricked" until attached to a JTAG programmer or similar.

So I strongly recommend using sectors 1, 2 and/or 3. Do look at ST EEPROM-emulation examples which use two sectors to guarantee no loss-of-information even with power-loss at the worst possible moment.

I agree, obviously better to use sector 1 or later instead of sector 0 to avoid potential issue. ST EEPROM-emulation example provides good start point on this topic.

Sure, but I suggested several alternatives, not really looking to get in to the wisdom of doing so, but rather answer the specific questions. Is it possible, and how..

It's not impossible,merely inadvisable.

When swimming up-hill one should perhaps be asking why no one else is doing it this way..

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Chuckero
Associate II

Thank you all for the comments,

Considering that every erase cycle will clean the entire sector (which will happen at least once a day) and the risk highlighted when using sector 0, I liked the idea of reserve a vector for a sector, like:

 

#pragma location = 0x0800C000
uint8_t DATA_IN_FLASH[16383] = {0xFF}; /* DATA_IN_FLASH at fixed address: Sector 3 */

 

 This way I will have the entire sector 3 for data storage.

Thanks again!

Chuckero
Associate II

I've tried that code but looks like it does not work in GCC. I'm using STM32CubeIDE 1.12.0.

I've searched everywhere on the internet and I didn't find a solution that works, it's the first time I touch in the linker script.
So, what I did so far was different combinations of this:
In the c file:

uint8_t __attribute__((section (".myDataStorage"))) memoryReserve[16383];

In the linker script (first thing in SECTIONS):

/* Memories definition */
MEMORY
{
  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 64K
  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 256K
}

/* Sections */
SECTIONS
{
  /* Reserve section 3 from flash memory. */
  .myDataStorage 0x0800C000 :
  {
    KEEP(*(.myDataStorage)) ;
  } > FLASH
  ...

 That way I got:
 - a warning in the STM32CubeProgrammer: Warning:The code is locked up!
 - flash memory, sectors 0, 1 and 2 (address 0x08000000 to 0x0800BFFF) are empty - 0xFF
 - flash memory, sector 3 (address 0x0800C000 to 0x0800FFFF) is empty - 0x00
 - the code starts at sector 4 (address 0x08010000)
 - the firmware does not run, because there is nothing in the boot sector (0x08000000)

I was imaging to have sectors 0, 1 and 2 for code, sector 3 for storage, and sectors 4, 5... for more code. I'm not sure if this is possible.
What is missing?

Try this

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = 0x20010000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;;      /* required amount of heap  */
_Min_Stack_Size = 0x400;; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
FLASH_ISR (rx)      : ORIGIN = 0x08000000, LENGTH = 48K
FLASH_HOLE (rx)      : ORIGIN = 0x0800C000, LENGTH = 16K
FLASH (rx)      : ORIGIN = 0x08010000, LENGTH = 256K-64K
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
}

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH_ISR

  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH_ISR

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH_ISR

  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH_ISR

  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH_ISR

  /* Reserve section 3 from flash memory. */
  .myDataStorage :
  {
    . = ALIGN(4);
    KEEP(*(.myDataStorage)) ;
    . = ALIGN(4);
  } > FLASH_HOLE

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.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 */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >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 :
  {
    /* 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 :
  {
    . = ALIGN(4);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(4);
  } >RAM

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

 

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Pavel A.
Evangelist III

In addition to what @Tesla DeLorean wrote: you cannot rewrite a portion of flash sector , even if the data written there is all FFs (I don't quite understand why, but this is from experience). You can write each "write unit" only once after erase. This means that you should write the sector 0 so that the unused space after the vectors and other stuff remains virgin. Make a separate small "image" (hex/bin) for the sector 0 and write it using CubeProgrammer or other tool, usually these tools won't fill end of sector after the data.

Then locate the start of unwritten area in sector 0 and program your data there as usual.