1
votes

I'm implementing a serial bootloader on an ATSAME51N20A (Cortex-M4). I'm reading in packets over UART, and writing them to internal flash. For some memory addresses the flash writes work fine, and for others it doesn't work at all.

Here is the datasheet. I have checked all the register configurations that I can find and it all seems to be OK.

Here is my initialization code :

void init(void)
{
     GPIOInit();
     USARTInit();

     NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_AUTOWS | NVMCTRL_CTRLA_WMODE_AP | NVMCTRL_CTRLA_PRM_MANUAL;
     hri_mclk_set_AHBMASK_NVMCTRL_bit(MCLK);
     hri_mclk_set_APBBMASK_NVMCTRL_bit(MCLK);

     PAC->WRCTRL.reg = PAC_WRCTRL_PERID(ID_DSU) | PAC_WRCTRL_KEY_CLR;
     MCLK->AHBMASK.reg |= MCLK_AHBMASK_DSU;
     MCLK->APBBMASK.reg |= MCLK_APBBMASK_DSU;
}

Here is my writing to flash code:


#define APPLICATION_START_ADDRESS 0x8000
#define PAGE_SIZE (512U)

void NVMErasePage(uint32_t pageOffset)
{
    // Set the NVM page address that we want to erase
    NVMCTRL->ADDR.reg = (APPLICATION_START_ADDRESS + (pageOffset * PAGE_SIZE));

    // Wait until the NVM controller is ready
    while (0 == NVMCTRL->STATUS.bit.READY);

    // Lock region size is always bigger than the row size
    NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_UR;

    while (0 == NVMCTRL->STATUS.bit.READY); // Unlocking is a fast operation

    // Erase the page that contains our address
    NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EP;

    // Wait until the NVM controller is ready
    while (0 == NVMCTRL->STATUS.bit.READY);
}

void programApplicationFlashPage(uint32_t pageOffset)
{
    uint32_t *flash_buf = (uint32_t *) (APPLICATION_START_ADDRESS + (pageOffset * PAGE_SIZE));
    for (int i = 0; i < PAGE_SIZE/4; i++)
    {
        flash_buf[i] = *(uint32_t *) (memoryLocation + (i));
    }
    while (0 == NVMCTRL->STATUS.bit.READY);
}

I am able to write flash pages from addresses 0x1A00 through 0x1E080 (which is offset 145-175) but before and after that, the memory that was supposed to be written to does not change.

According to the datasheet RUNLOCK is responsible for write protection of NVM and I verified this to be set to unlock all NVM sectors (0xFFFFFFFF).

What else could be preventing me from writing to all of NVM?

EDIT

Added code to erase flash and wait after flash/write operations. I am seeing the flash all being erased (all bits set high), but the write operation does not work.

1
Stupid question: does the memory exist? - Paul Ogilvie
Well I think the memory exists. The datasheet specifies that the embedded flash starts at adress 0x00000000 and is 1024 KB in size. - Peter Kapteyn
the posted code seems to not be waiting for one write operation to complete before starting another write operation. What is the necessary wait time (and/or) what status can be checked to determine if the flash is NOT BUSY? - user3629249
there seems to be some misunderstanding in the posted code. the LOWEST address the posted code can write to is: 0x8000 NOT 0000000 - user3629249
Don't you have to erase memory first? You can only write erased cells. - 0___________

1 Answers

2
votes

Figured it out. I was issuing the Erase Page command instead of the Erase Block command. Erase page is only for the USER data page in flash and not the main flash bank.

Here is the code that worked for me:

if((flash_addr % BLOCK_SIZE) == 0)
{
    // Set the NVM block address that we want to erase
    NVMCTRL->ADDR.reg = blockAddress;

    // Unlock the region that contains our address
    NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_UR;

    // Wait until the NVM controller is ready
    while (0 == NVMCTRL->STATUS.bit.READY);

    // Erase the block that contains our address
    NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMDEX_KEY | NVMCTRL_CTRLB_CMD_EB;

    // Wait until the NVM controller is ready
    while (0 == NVMCTRL->STATUS.bit.READY);
}

for (int i = 0; i < PAGE_SIZE /4; i++)
{
    flash_buf[i] = *(uint32_t *) (memoryLocation + (i));
}
while (0 == NVMCTRL->STATUS.bit.READY);