1

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.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • "Counter = Counter - 1" attempts to write to address that is in flash, which is most probably read-only area. You didn't paste your linker script. – mkmk88 Dec 08 '15 at 13:28
  • The variable is linked to a fixed address in the beginning of the section. This means that the (buffer-) variable is writable and can also be written to the flash by invoking the write command. But somehow it hangs in this command although the flash is already written. – eeucalyptus Dec 08 '15 at 13:48
  • I didn't use this particular MCU, but according to docs: http://www.atmel.com/Images/doc6175.pdf the internal flash is between 0x00100000 and 0x001FFFFF. Your code instructs the variable "counter" to be put into section ".flash". The comment in your source code states that, ".flash" is at 0x0013F000 which is in fact the internal flash. This section is most probably read only. Without seeing your linker script I cannot help more. – mkmk88 Dec 08 '15 at 14:27
  • According to the documentation write operations on the flash memory are applied to a buffer. This buffer is then written to the actual flash memory as soon as the write command is invoked. So what I did was to take a particular flash page (page 1008) and write the variable in its beginning. Then I invoke the write command and the page buffer is written to the memory. And as I said, the data is actually written and can even be read out after a reset. But the controller gets into an exception state. – eeucalyptus Dec 08 '15 at 15:02
  • Aha, OK I got it. Have you read http://www.doctort.org/adam/nerd-notes/arm-flash-memory.html then? In comments there is someone who had similar problem. – mkmk88 Dec 08 '15 at 15:37
  • The whole of NOR flash becomes unreadable during erase/write operations afair; not just sector/block/erase unit. Basically some FSM replaces the BUS during the operation and the CPU will fetch that FSM status as code. You need to place any flash programming code in RAM and disable interrupts, etc. Your flash programming routine can poll IRQ status and abort early with a flash reset in the case of an interrupt. – artless noise Dec 08 '15 at 16:03

1 Answers1

2

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.

D Krueger
  • 2,446
  • 15
  • 12