Search code examples
carmembeddedatmelflash-memory

Write to at91sam7s256 flash internally


I am currently developing a feature for a device based on Atmel's at91sam7s256 MCU. The feature is a counter with a pre-set value, which is decreased at some points. My Idea was to implement this counter in the internal flash memory, since most of the flash space is unused.

I added a separate linker section to the ld script and included a variable into this section. The linker script:

/*
    FLASH is reserved for internal settings
*/

MEMORY
{
  CODE (rx)  : ORIGIN = 0x00100000, LENGTH = 252k
  FLASH (rx) : ORIGIN = 0x0013F000, LENGTH = 4k
  DATA (rwx) : ORIGIN = 0x00200000, LENGTH = 64k
}
__FIRST_IN_RAM = ORIGIN(DATA);
__TOP_STACK    = ORIGIN(DATA) + LENGTH(DATA);

/* Section Definitions */

SECTIONS
{
    /* first section is .text which is used for code */
    . = ORIGIN(CODE);

    .text :
    {
        KEEP(*(.vectorg))
        . = ALIGN(4);
        KEEP(*(.init))
        *(.text .text.*)                   /* remaining code */
        *(.gnu.linkonce.t.*)
        *(.glue_7)
        *(.glue_7t)
        *(.gcc_except_table)
        *(.rodata)                 /* read-only data (constants) */
        *(.rodata.*)
        *(.gnu.linkonce.r.*)
        . = ALIGN(4);
    } >CODE


    . = ALIGN(4);

    /* .ctors .dtors are used for c++ constructors/destructors */

    .ctors :
    {
        PROVIDE(__ctors_start__ = .);
        KEEP(*(SORT(.ctors.*)))
        KEEP(*(.ctors))
        PROVIDE(__ctors_end__ = .);
    } >CODE

    .dtors :
    {
        PROVIDE(__dtors_start__ = .);
        KEEP(*(SORT(.dtors.*)))
        KEEP(*(.dtors))
        PROVIDE(__dtors_end__ = .);
    } >CODE

    . = ALIGN(4);

    _etext = . ;
    PROVIDE (etext = .);

    /* .data section which is used for initialized data */
    .data : AT (_etext)
    {
        _data = . ;
        KEEP(*(.vectmapped))
        . = ALIGN(4);
        *(.fastrun .fastrun.*)
        . = ALIGN(4);
        SORT(CONSTRUCTORS)
        . = ALIGN(4);
        *(.data)
        *(.data.*)
        *(.gnu.linkonce.d.*)
        . = ALIGN(4);
    } >DATA

    . = ALIGN(4);

    _edata = . ;
    PROVIDE (edata = .);

    /* .bss section which is used for uninitialized data */
    .bss (NOLOAD) :
    {
        __bss_start = . ;
        __bss_start__ = . ;
        *(.bss)
        *(.bss.*)
        *(.gnu.linkonce.b.*)
        *(COMMON)
        . = ALIGN(4);
    } >DATA

    . = ALIGN(4);

    __bss_end__ = . ;

    .flash :
    {
        . = ORIGIN(FLASH);
        *(.flash*)
        . = ALIGN(4);
    } >FLASH


    _end = .;
    PROVIDE (end = .);
}

The following routine is used when the counter is decremented:

#define INTERNAL_FLASH  __attribute__((section(".flash")))

#define AT91C_MC_WRITE_KEY  ((unsigned)0x5A << 24) // Magic number

INTERNAL_FLASH uint32_t Counter  = 30; // The section .flash begins at 0x0013F000

void prepaid_decrement(void)
{
    if(Counter > 0) {
        // write into buffer
        Counter = Counter - 1;

        volatile AT91PS_MC mc = AT91C_BASE_MC;
        // set flash mode (timing)
        mc->MC_FMR = AT91C_MC_FWS_1FWS | ((1 + (((MCK * 15) / 10000000))) << 16);

        uint32_t page = ((uint32_t)&Counter - (uint32_t)AT91C_IFLASH) / (uint32_t)AT91C_IFLASH_PAGE_SIZE;

        // start writing
        mc->MC_FCR = AT91C_MC_WRITE_KEY | AT91C_MC_FCMD_START_PROG | (page << 8);
        if(0 != (mc->MC_FSR & AT91C_MC_PROGE)) {TRACE("error!");}

        while (!(AT91C_BASE_MC->MC_FSR & AT91C_MC_FRDY));
    }
}

But the code hangs in mc->MC_FCR = AT91C_MC_WRITE_KEY | AT91C_MC_FCMD_START_PROG | (page << 8);. The lines below are never reached, because the MCU jumps into an exception loop (address 0x60 in the vector table).

Anyway, the data seems to be written correctly because after a reset the counter variable is decreased by one.

Can anyone tell me what I am doing wrong? The code is not being interrupted.


Solution

  • The code results in an exception because it is executing out of flash while attempting to program the flash. Once the flash mode register is written, instructions can no longer be read from flash. The function programming the flash should be placed in RAM.