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.
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);
User contributions licensed under CC BY-SA 3.0