Mysterious memory write protection

1

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.

c
microcontroller
bootloader
cortex-m
asked on Stack Overflow Mar 11, 2020 by Peter Kapteyn • edited Mar 12, 2020 by Peter Kapteyn

1 Answer

2

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

answered on Stack Overflow Mar 12, 2020 by Peter Kapteyn

User contributions licensed under CC BY-SA 3.0