Electronic – Mysterious NVM write protection

cmicrocontrollernon-volatile-memory

I'm implementing a serial bootloader on an ATSAME51N20A (Cortex-M4). I'm reading in packets of 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 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));
    }
}

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?

Best Answer

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);