This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSPM0G1107: is a bootloader stump needed when placing the emulated eeprom section at the flash base address?

Part Number: MSPM0G1107
Other Parts Discussed in Thread: SEGGER

Tool/software:

Hello there

I implemented an emulated eeprom based on TI's application note which is working fine. In the linker file I reserved a memory area for the emulated eeprom. Since the lower 32kB flash address space supports higher erase/program endurance, I placed the emulated eeprom at the flash base address (0x0000.0000) and the main program at the flash address 0x0000.1400 This was working fine, when the debugger was connected. Without a debugger connected, the main program wouldn't start.

After a bit of research I found out, that the settings of the debugger or to be precise the debugging software (SEGGER Ozone) handles the the relocating of the vector table and sets the program counter correctly.

To fix that, I added a memory area with a bootloader stump that contains a small vector table with a stack pointer and a reset handler. This reset hander than executes the jump to the main applications reset handler. This is working as expected. As much as I understand, the controller by defaults expects a vector table at the flash base address with a stack pointer and a reset handler. 

Since the emulated eeprom start address needs to be at a page address, I reserved a whole flash page for the bootloader stump although it wouldn't need as much flash memory. 

I would rather not have a additional memory section only for the bootloader stump with its own boot vector table but only have the main vector table based at the address 0x0000.0000. is there any other solution then the one I'm using at the moment? Can I somehow split my main program between two non consecutive flash areas?

My linker file:

/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size  = 0;      /* required amount of heap  */
_Min_Stack_Size = 0x80; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
    FLASH_BOOT          (RX)  : ORIGIN = 0x00000000, LENGTH = 1K
    EMULATED_EEPROM     (RW)  : ORIGIN = 0x00000400, LENGTH = 4K
    FLASH               (RX)  : ORIGIN = 0x00001400, LENGTH = 123K    
    SRAM                (RWX) : ORIGIN = 0x20200000, LENGTH = 32K
    BCR_CONFIG          (R)   : ORIGIN = 0x41C00000, LENGTH = 0x00000080
    BSL_CONFIG          (R)   : ORIGIN = 0x41C00100, LENGTH = 0x00000080

}

/* Note: SRAM length must match MPPC/MEMSS config! Please edit it manually. */

REGION_ALIAS("REGION_TEXT", FLASH);
REGION_ALIAS("REGION_PREINIT_ARRAY", FLASH);
REGION_ALIAS("REGION_INIT_ARRAY", FLASH);
REGION_ALIAS("REGION_FINI_ARRAY", FLASH);
REGION_ALIAS("REGION_BSS", SRAM);
REGION_ALIAS("REGION_NOINIT", SRAM);
REGION_ALIAS("REGION_DATA", SRAM);
REGION_ALIAS("REGION_STACK", SRAM);
REGION_ALIAS("REGION_HEAP", SRAM);
REGION_ALIAS("REGION_TEXT_RAM", SRAM);
REGION_ALIAS("REGION_ARM_EXIDX", FLASH);
REGION_ALIAS("REGION_ARM_EXTAB", FLASH);

/* Define output sections */
SECTIONS
{

    /* section for the boot interrupt vector area                                 */
    PROVIDE (_intvecs_boot_base_address =
        DEFINED(_intvecs_boot_base_address) ? _intvecs_boot_base_address : 0x00000000);

    .intvecs_boot (_intvecs_boot_base_address) : AT (_intvecs_boot_base_address) {
        . = ALIGN(0x4);
        KEEP (*(.intvecs_boot))
        . = ALIGN(0x4);
    } > FLASH_BOOT

    __emulated_eeprom_base_address = ORIGIN(EMULATED_EEPROM);

    .emulated_eeprom : {
        . = ALIGN(0x8);
        KEEP (*(.emulated_eeprom))
        . = ALIGN(0x8);
    } > EMULATED_EEPROM    

    __program_base_address = ORIGIN(FLASH);

    /* section for the interrupt vector area                                 */
    PROVIDE (_intvecs_base_address =
        DEFINED(_intvecs_base_address) ? _intvecs_base_address : __program_base_address);

    .intvecs (_intvecs_base_address) : AT (_intvecs_base_address) {
        KEEP (*(.intvecs))
    } > REGION_TEXT

    PROVIDE (_vtable_base_address =
        DEFINED(_vtable_base_address) ? _vtable_base_address : 0x20200000);

    .vtable (_vtable_base_address) : AT (_vtable_base_address) {
        KEEP (*(.vtable))
    } > REGION_DATA

   .text : {
        CREATE_OBJECT_SYMBOLS
        KEEP (*(.text))
        . = ALIGN(0x8);
        *(.text.*)
        . = ALIGN(0x8);
        KEEP (*(.ctors))
        . = ALIGN(0x8);
        KEEP (*(.dtors))
        . = ALIGN(0x8);
        KEEP (*(.init))
        . = ALIGN(0x8);
        KEEP (*(.fini*))
        . = ALIGN(0x8);
    } > REGION_TEXT AT> REGION_TEXT

    .ramfunc : {
        __ramfunct_load__ = LOADADDR (.ramfunc);
        __ramfunct_start__ = .;
       *(.ramfunc)
       . = ALIGN(0x8);
       __ramfunct_end__ = .;
    } > REGION_TEXT_RAM AT> REGION_TEXT

    .rodata : {
        *(.rodata)
        . = ALIGN(0x8);
        *(.rodata.*)
        . = ALIGN(0x8);
    } > REGION_TEXT AT> REGION_TEXT

    .preinit_array : {
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP (*(.preinit_array*));
        PROVIDE_HIDDEN (__preinit_array_end = .);
    } > REGION_PREINIT_ARRAY AT> REGION_TEXT

    .init_array : {
        . = ALIGN(0x8);
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array*))
        PROVIDE_HIDDEN (__init_array_end = .);
    } > REGION_INIT_ARRAY AT> REGION_TEXT

    .fini_array : {
        . = ALIGN(0x8);
        PROVIDE_HIDDEN (__fini_array_start = .);
        KEEP (*(SORT(.fini_array.*)))
        KEEP (*(.fini_array*))
        PROVIDE_HIDDEN (__fini_array_end = .);
    } > REGION_FINI_ARRAY AT> REGION_TEXT


    .ARM.exidx : {
    	. = ALIGN(0x8);
        __exidx_start = .;
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
        . = ALIGN(0x8);
        __exidx_end = .;
    } > REGION_ARM_EXIDX AT> REGION_ARM_EXIDX

    .ARM.extab : {
        KEEP (*(.ARM.extab* .gnu.linkonce.armextab.*))
         . = ALIGN(0x8);
    } > REGION_ARM_EXTAB AT> REGION_ARM_EXTAB

    __etext = .;

    .data : {
        __data_load__ = LOADADDR (.data);
        __data_start__ = .;
        KEEP (*(.data))
        KEEP (*(.data*))
        . = ALIGN (8);
        __data_end__ = .;
    } > REGION_DATA AT> REGION_TEXT

    .bss : {
        __bss_start__ = .;
        *(.shbss)
        KEEP (*(.bss))
        *(.bss.*)
        *(COMMON)
        . = ALIGN (8);
        __bss_end__ = .;
    } > REGION_BSS AT> REGION_BSS

    .noinit : {
        /* place all symbols in input sections that start with .noinit */
        KEEP(*(*.noinit*))
        . = ALIGN (8);
    } > REGION_NOINIT AT> REGION_NOINIT

    .heap : {
        __heap_start__ = .;
        end = __heap_start__;
        _end = end;
        __end = end;
        KEEP (*(.heap))
        __heap_end__ = .;
        __HeapLimit = __heap_end__;
    } > REGION_HEAP AT> REGION_HEAP

    .stack (NOLOAD) : ALIGN(0x8) {
        _stack = .;
        KEEP(*(.stack))
    } > REGION_STACK AT> REGION_STACK

    .BCRConfig :
    {
        KEEP(*(.BCRConfig))
    } > BCR_CONFIG

    .BSLConfig :
    {
        KEEP(*(.BSLConfig))
    } > BSL_CONFIG

    
    __StackTop = ORIGIN(REGION_STACK) + LENGTH(REGION_STACK);
    PROVIDE(__stack = __StackTop);
}

  • Hi Roland,

    I have a few comments here.

    To start with the easiest one, you are correct that the device expects a stack pointer and reset vector at the first two locations in memory.

    For splitting your main program between two non consecutive flash areas.... I am sure that you could, but this is not something I am experienced with. While we could start figuring this out, I have an update that will mean you don't need to worry about this:

    We have decided to make an update to how we describe our flash in regards to the additional write erase cycles to better reflect the device's actual behavior, and to make things easier for customers like you:

    32kB of the device's flash memory at any address can have the extended write erase cycles, it doesn't necessarily need to be the first 32kB of memory. So the programmer can select to use any memory sectors, up to 32kB, for something like EEPROM emulation, and get the additional write erase cycles. All you need to do is make sure that you only use 100k cycles on 32kB of memory at most, and 10k cycles on the rest.

    This information has not yet been published to our datasheet, but is in the process of being added now so should be reflected there soon.

    So all of this is to say that you can place your EEPROM emulation region outside of the first 32kB of memory, wherever you'd like, and you'll still be able to get the higher endurance on up to 32kB of flash.

  • Hi Dylan

    Thanks for the fast reply. This is great news and simplifies the linker file a lot. I wasn't really eager to figure out how to split the main program over two flash areas ;). 

  • There is actually something else, that I couldn't find any information about in the datasheet or the reference manual. Not sure if it is the right place to ask here, but can you tell me how the device reacts if a flash cell is corrupt? 

    I know from other microcontrollers, that sometimes a corrupt flash cell can be marked with all zeros. Is this the case here? Is it possible to write to a corrupt flash cell at all?

  • Haha I am sure figuring that out would've been... a lot. Also happy we don't have to address the issue that way!

    For your second question, the mechanism that we use in our device to detect flash corruption is the ECC. When you write to the ECC corrected portion of flash, the ECC is calculated and stored to another part of memory. When you read from the ECC corrected portion of flash, the ECC is calculated again, and compared to the saved value. If the two ECCs are different, an NMI is triggered. Inside of an NMI handler you could choose how to handle a flash ECC error. So you could choose to ignore it, or to perform whatever action you like. The short answer is that an NMI will be triggered when you read a corrupted piece of flash data.

  • And I still should be able to write to that corrupt piece of flash data (for example writing a specific pattern to it?

  • Yes you'll still be able to write to the piece of flash data before you detect the bit flip, and then after as well, as long as your NMI handler allows the device to continue program execution after the ECC error.