/** Erases a specific memory space of the target. * * \param[in] EraseCommand NVM erase command to send to the device * \param[in] Address Address inside the memory space to erase * * \return Boolean true if the command sequence complete successfully */ bool XMEGANVM_EraseMemory(const uint8_t EraseCommand, const uint32_t Address) { /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Send the memory erase command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(EraseCommand); /* Chip erase is handled separately, since it's procedure is different to other erase types */ if (EraseCommand == XMEGA_NVM_CMD_CHIPERASE) { /* Set CMDEX bit in NVM CTRLA register to start the chip erase */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CTRLA); XPROGTarget_SendByte(1 << 0); } else { /* Other erase modes just need us to address a byte within the target memory space */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendAddress(Address); XPROGTarget_SendByte(0x00); } /* Wait until the NVM bus is ready again */ if (!(XMEGANVM_WaitWhileNVMBusBusy())) return false; return true; }
/** Reads memory from the target's memory spaces. * * \param[in] ReadAddress Start address to read from within the target's address space * \param[out] ReadBuffer Buffer to store read data into * \param[in] ReadSize Number of bytes to read * * \return Boolean true if the command sequence complete successfully */ bool XMEGANVM_ReadMemory(const uint32_t ReadAddress, uint8_t* ReadBuffer, uint16_t ReadSize) { /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Send the READNVM command to the NVM controller for reading of an arbitrary location */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(XMEGA_NVM_CMD_READNVM); /* Load the PDI pointer register with the start address we want to read from */ XPROGTarget_SendByte(PDI_CMD_ST | (PDI_POINTER_DIRECT << 2) | PDI_DATSIZE_4BYTES); XMEGANVM_SendAddress(ReadAddress); /* Send the REPEAT command with the specified number of bytes to read */ XPROGTarget_SendByte(PDI_CMD_REPEAT | PDI_DATSIZE_1BYTE); XPROGTarget_SendByte(ReadSize - 1); /* Send a LD command with indirect access and postincrement to read out the bytes */ XPROGTarget_SendByte(PDI_CMD_LD | (PDI_POINTER_INDIRECT_PI << 2) | PDI_DATSIZE_1BYTE); while (ReadSize-- && TimeoutMSRemaining) *(ReadBuffer++) = XPROGTarget_ReceiveByte(); return (TimeoutMSRemaining != 0); }
/** Waits while the target's NVM controller is busy performing an operation, exiting if the * timeout period expires. * * \return Boolean true if the NVM controller became ready within the timeout period, false otherwise */ bool XMEGANVM_WaitWhileNVMControllerBusy(void) { /* Poll the NVM STATUS register while the NVM controller is busy */ while (TimeoutMSRemaining) { /* Send a LDS command to read the NVM STATUS register to check the BUSY flag */ XPROGTarget_SendByte(PDI_CMD_LDS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_STATUS); uint8_t StatusRegister = XPROGTarget_ReceiveByte(); /* We might have timed out waiting for the status register read response, check here */ if (!(TimeoutMSRemaining)) return false; /* Check to see if the BUSY flag is still set */ if (!(StatusRegister & (1 << 7))) { TimeoutMSRemaining = COMMAND_TIMEOUT_MS; return true; } } return false; }
/** Waits while the target's NVM controller is busy performing an operation, exiting if the * timeout period expires. * * \return Boolean \c true if the NVM controller became ready within the timeout period, \c false otherwise */ bool XMEGANVM_WaitWhileNVMControllerBusy(void) { /* Preload the pointer register with the NVM STATUS register address to check the BUSY flag */ XPROGTarget_SendByte(PDI_CMD_ST(PDI_POINTER_DIRECT, PDI_DATASIZE_4BYTES)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_STATUS); /* Poll the NVM STATUS register while the NVM controller is busy */ for (;;) { /* Fetch the current status value via the pointer register (without auto-increment afterwards) */ XPROGTarget_SendByte(PDI_CMD_LD(PDI_POINTER_INDIRECT, PDI_DATASIZE_1BYTE)); uint8_t StatusRegister = XPROGTarget_ReceiveByte(); /* We might have timed out waiting for the status register read response, check here */ if (TimeoutTicksRemaining == 0){ return false; } /* Check to see if the BUSY flag is still set */ if (!(StatusRegister & (1 << 7))) { return true; } } }
/** Retrieves the CRC value of the given memory space. * * \param[in] CRCCommand NVM CRC command to issue to the target * \param[out] CRCDest CRC Destination when read from the target * * \return Boolean \c true if the command sequence complete successfully */ bool XMEGANVM_GetMemoryCRC(const uint8_t CRCCommand, uint32_t* const CRCDest) { *CRCDest = 0; /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Set the NVM command to the correct CRC read command */ XPROGTarget_SendByte(PDI_CMD_STS(PDI_DATASIZE_4BYTES, PDI_DATASIZE_1BYTE)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(CRCCommand); /* Set CMDEX bit in NVM CTRLA register to start the CRC generation */ XPROGTarget_SendByte(PDI_CMD_STS(PDI_DATASIZE_4BYTES, PDI_DATASIZE_1BYTE)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CTRLA); XPROGTarget_SendByte(XMEGA_NVM_BIT_CTRLA_CMDEX); /* Wait until the NVM bus is ready again */ if (!(XMEGANVM_WaitWhileNVMBusBusy())) return false; /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Load the PDI pointer register with the DAT0 register start address */ XPROGTarget_SendByte(PDI_CMD_ST(PDI_POINTER_DIRECT, PDI_DATASIZE_4BYTES)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_DAT0); /* Send the REPEAT command to grab the CRC bytes */ XPROGTarget_SendByte(PDI_CMD_REPEAT(PDI_DATASIZE_1BYTE)); XPROGTarget_SendByte(XMEGA_CRC_LENGTH_BYTES - 1); /* Read in the CRC bytes from the target */ XPROGTarget_SendByte(PDI_CMD_LD(PDI_POINTER_INDIRECT_PI, PDI_DATASIZE_1BYTE)); for (uint8_t i = 0; i < XMEGA_CRC_LENGTH_BYTES; i++) ((uint8_t*)CRCDest)[i] = XPROGTarget_ReceiveByte(); return (TimeoutTicksRemaining > 0); }
/** Writes byte addressed memory to the target's memory spaces. * * \param[in] WriteCommand Command to send to the device to write each memory byte * \param[in] WriteAddress Address to write to within the target's address space * \param[in] Byte Byte to write to the target * * \return Boolean true if the command sequence complete successfully */ bool XMEGANVM_WriteByteMemory(const uint8_t WriteCommand, const uint32_t WriteAddress, const uint8_t Byte) { /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Send the memory write command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(WriteCommand); /* Send new memory byte to the memory to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendAddress(WriteAddress); XPROGTarget_SendByte(Byte); return true; }
/** Writes page addressed memory to the target's memory spaces. * * \param[in] WriteBuffCommand Command to send to the device to write a byte to the memory page buffer * \param[in] EraseBuffCommand Command to send to the device to erase the memory page buffer * \param[in] WritePageCommand Command to send to the device to write the page buffer to the destination memory * \param[in] PageMode Bitfield indicating what operations need to be executed on the specified page * \param[in] WriteAddress Start address to write the page data to within the target's address space * \param[in] WriteBuffer Buffer to source data from * \param[in] WriteSize Number of bytes to write * * \return Boolean true if the command sequence complete successfully */ bool XMEGANVM_WritePageMemory(const uint8_t WriteBuffCommand, const uint8_t EraseBuffCommand, const uint8_t WritePageCommand, const uint8_t PageMode, const uint32_t WriteAddress, const uint8_t* WriteBuffer, uint16_t WriteSize) { if (PageMode & XPRG_PAGEMODE_ERASE) { /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Send the memory buffer erase command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(EraseBuffCommand); /* Set CMDEX bit in NVM CTRLA register to start the buffer erase */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CTRLA); XPROGTarget_SendByte(1 << 0); } if (WriteSize) { /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Send the memory buffer write command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(WriteBuffCommand); /* Load the PDI pointer register with the start address we want to write to */ XPROGTarget_SendByte(PDI_CMD_ST | (PDI_POINTER_DIRECT << 2) | PDI_DATSIZE_4BYTES); XMEGANVM_SendAddress(WriteAddress); /* Send the REPEAT command with the specified number of bytes to write */ XPROGTarget_SendByte(PDI_CMD_REPEAT | PDI_DATSIZE_1BYTE); XPROGTarget_SendByte(WriteSize - 1); /* Send a ST command with indirect access and postincrement to write the bytes */ XPROGTarget_SendByte(PDI_CMD_ST | (PDI_POINTER_INDIRECT_PI << 2) | PDI_DATSIZE_1BYTE); while (WriteSize--) XPROGTarget_SendByte(*(WriteBuffer++)); } if (PageMode & XPRG_PAGEMODE_WRITE) { /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Send the memory write command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(WritePageCommand); /* Send the address of the first page location to write the memory page */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendAddress(WriteAddress); XPROGTarget_SendByte(0x00); } return true; }
/** Erases a specific memory space of the target. * * \param[in] EraseCommand NVM erase command to send to the device * \param[in] Address Address inside the memory space to erase * * \return Boolean true if the command sequence complete successfully */ bool XMEGANVM_EraseMemory(const uint8_t EraseCommand, const uint32_t Address) { /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* EEPROM and Chip erasures are triggered differently to FLASH section erasures */ if (EraseCommand == XMEGA_NVM_CMD_CHIPERASE) { /* Send the memory erase command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(EraseCommand); /* Set CMDEX bit in NVM CTRLA register to start the erase sequence */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CTRLA); XPROGTarget_SendByte(1 << 0); } else if (EraseCommand == XMEGA_NVM_CMD_ERASEEEPROM) { /* Send the EEPROM page buffer erase command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(XMEGA_NVM_CMD_ERASEEEPROMPAGEBUFF); /* Set CMDEX bit in NVM CTRLA register to start the buffer erase */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CTRLA); XPROGTarget_SendByte(1 << 0); /* Wait until the NVM controller is no longer busy */ if (!(XMEGANVM_WaitWhileNVMControllerBusy())) return false; /* Send the EEPROM memory buffer write command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(XMEGA_NVM_CMD_LOADEEPROMPAGEBUFF); /* Load the PDI pointer register with the EEPROM page start address */ XPROGTarget_SendByte(PDI_CMD_ST | (PDI_POINTER_DIRECT << 2) | PDI_DATSIZE_4BYTES); XMEGANVM_SendAddress(Address); /* Send the REPEAT command with the specified number of bytes to write */ XPROGTarget_SendByte(PDI_CMD_REPEAT | PDI_DATSIZE_1BYTE); XPROGTarget_SendByte(XPROG_Param_EEPageSize - 1); /* Send a ST command with indirect access and post-increment to tag each byte in the EEPROM page buffer */ XPROGTarget_SendByte(PDI_CMD_ST | (PDI_POINTER_INDIRECT_PI << 2) | PDI_DATSIZE_1BYTE); for (uint8_t PageByte = 0; PageByte < XPROG_Param_EEPageSize; PageByte++) XPROGTarget_SendByte(0x00); /* Send the memory erase command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(EraseCommand); /* Set CMDEX bit in NVM CTRLA register to start the EEPROM erase sequence */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CTRLA); XPROGTarget_SendByte(1 << 0); } else { /* Send the memory erase command to the target */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendNVMRegAddress(XMEGA_NVM_REG_CMD); XPROGTarget_SendByte(EraseCommand); /* Other erase modes just need us to address a byte within the target memory space */ XPROGTarget_SendByte(PDI_CMD_STS | (PDI_DATSIZE_4BYTES << 2)); XMEGANVM_SendAddress(Address); XPROGTarget_SendByte(0x00); } /* Wait until the NVM bus is ready again */ if (!(XMEGANVM_WaitWhileNVMBusBusy())) return false; return true; }