/** 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; } } }
/** Sends the given pointer address to the target's TPI pointer register */ static void TINYNVM_SendPointerAddress(const uint16_t AbsoluteAddress) { /* Send the given 16-bit address to the target, LSB first */ XPROGTarget_SendByte(TPI_CMD_SSTPR | 0); XPROGTarget_SendByte(AbsoluteAddress & 0xFF); XPROGTarget_SendByte(TPI_CMD_SSTPR | 1); XPROGTarget_SendByte(AbsoluteAddress >> 8); }
/** Sends the given 32-bit absolute address to the target. * * \param[in] AbsoluteAddress Absolute address to send to the target */ static void XMEGANVM_SendAddress(const uint32_t AbsoluteAddress) { /* Send the given 32-bit address to the target, LSB first */ XPROGTarget_SendByte(((uint8_t*)&AbsoluteAddress)[0]); XPROGTarget_SendByte(((uint8_t*)&AbsoluteAddress)[1]); XPROGTarget_SendByte(((uint8_t*)&AbsoluteAddress)[2]); XPROGTarget_SendByte(((uint8_t*)&AbsoluteAddress)[3]); }
/** Removes access to the target's NVM controller and physically disables the target's physical TPI interface. */ void TINYNVM_DisableTPI(void) { TINYNVM_WaitWhileNVMBusBusy(); /* Clear the NVMEN bit in the TPI STATUS register to disable TPI mode */ XPROGTarget_SendByte(TPI_CMD_SSTCS | TPI_STATUS_REG); XPROGTarget_SendByte(0x00); XPROGTarget_DisableTargetTPI(); }
/** Handler for the XPROG ENTER_PROGMODE command to establish a connection with the attached device. */ static void XPROGProtocol_EnterXPROGMode(void) { Endpoint_ClearOUT(); Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPNUM); Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN); bool NVMBusEnabled = false; if (XPROG_SelectedProtocol == XPRG_PROTOCOL_PDI) { /* Enable PDI programming mode with the attached target */ XPROGTarget_EnableTargetPDI(); /* Store the RESET key into the RESET PDI register to keep the XMEGA in reset */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_RESET_REG); XPROGTarget_SendByte(PDI_RESET_KEY); /* Lower direction change guard time to 0 USART bits */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_CTRL_REG); XPROGTarget_SendByte(0x07); /* Enable access to the XPROG NVM bus by sending the documented NVM access key to the device */ XPROGTarget_SendByte(PDI_CMD_KEY); for (uint8_t i = sizeof(PDI_NVMENABLE_KEY); i > 0; i--) XPROGTarget_SendByte(PDI_NVMENABLE_KEY[i - 1]); /* Wait until the NVM bus becomes active */ NVMBusEnabled = XMEGANVM_WaitWhileNVMBusBusy(); } else if (XPROG_SelectedProtocol == XPRG_PROTOCOL_TPI) { /* Enable TPI programming mode with the attached target */ XPROGTarget_EnableTargetTPI(); /* Lower direction change guard time to 0 USART bits */ XPROGTarget_SendByte(TPI_CMD_SSTCS | TPI_CTRL_REG); XPROGTarget_SendByte(0x07); /* Enable access to the XPROG NVM bus by sending the documented NVM access key to the device */ XPROGTarget_SendByte(TPI_CMD_SKEY); for (uint8_t i = sizeof(TPI_NVMENABLE_KEY); i > 0; i--) XPROGTarget_SendByte(TPI_NVMENABLE_KEY[i - 1]); /* Wait until the NVM bus becomes active */ NVMBusEnabled = TINYNVM_WaitWhileNVMBusBusy(); } Endpoint_Write_Byte(CMD_XPROG); Endpoint_Write_Byte(XPRG_CMD_ENTER_PROGMODE); Endpoint_Write_Byte(NVMBusEnabled ? XPRG_ERR_OK : XPRG_ERR_FAILED); Endpoint_ClearIN(); }
/** Removes access to the target's NVM controller and physically disables the target's physical PDI interface. */ void XMEGANVM_DisablePDI(void) { XMEGANVM_WaitWhileNVMBusBusy(); /* Clear the RESET key in the RESET PDI register to allow the XMEGA to run */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_RESET_REG); XPROGTarget_SendByte(0x00); /* Do it twice to make sure it takes effect (silicon bug?) */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_RESET_REG); XPROGTarget_SendByte(0x00); XPROGTarget_DisableTargetPDI(); }
/** 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; }
/** Removes access to the target's NVM controller and physically disables the target's physical TPI interface. */ void TINYNVM_DisableTPI(void) { TINYNVM_WaitWhileNVMBusBusy(); do { /* Clear the NVMEN bit in the TPI STATUS register to disable TPI mode */ XPROGTarget_SendByte(TPI_CMD_SSTCS | TPI_STATUS_REG); XPROGTarget_SendByte(0x00); /* Read back the STATUS register, check to see if it took effect */ XPROGTarget_SendByte(TPI_CMD_SLDCS | PDI_RESET_REG); } while (XPROGTarget_ReceiveByte() != 0x00); XPROGTarget_DisableTargetTPI(); }
/** Enables the physical TPI interface on the target and enables access to the internal NVM controller. * * \return Boolean true if the TPI interface was enabled successfully, false otherwise */ bool TINYNVM_EnableTPI(void) { /* Enable TPI programming mode with the attached target */ XPROGTarget_EnableTargetTPI(); /* Lower direction change guard time to 32 USART bits */ XPROGTarget_SendByte(TPI_CMD_SSTCS | TPI_CTRL_REG); XPROGTarget_SendByte(0x02); /* Enable access to the XPROG NVM bus by sending the documented NVM access key to the device */ XPROGTarget_SendByte(TPI_CMD_SKEY); for (uint8_t i = sizeof(TPI_NVMENABLE_KEY); i > 0; i--) XPROGTarget_SendByte(TPI_NVMENABLE_KEY[i - 1]); /* Wait until the NVM bus becomes active */ return TINYNVM_WaitWhileNVMBusBusy(); }
/** Sends the given 32-bit absolute address to the target. * * \param[in] AbsoluteAddress Absolute address to send to the target */ static void XMEGANVM_SendAddress(const uint32_t AbsoluteAddress) { /* Send the given 32-bit address to the target, LSB first */ XPROGTarget_SendByte(AbsoluteAddress & 0xFF); XPROGTarget_SendByte(AbsoluteAddress >> 8); XPROGTarget_SendByte(AbsoluteAddress >> 16); XPROGTarget_SendByte(AbsoluteAddress >> 24); }
/** 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; }
/** Removes access to the target's NVM controller and physically disables the target's physical PDI interface. */ void XMEGANVM_DisablePDI(void) { XMEGANVM_WaitWhileNVMBusBusy(); /* Clear the RESET key in the RESET PDI register to allow the XMEGA to run - must perform this until the * change takes effect, as in some cases it takes multiple writes (silicon bug?). */ do { /* Clear reset register */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_RESET_REG); XPROGTarget_SendByte(0x00); /* Read back the reset register, check to see if it took effect */ XPROGTarget_SendByte(PDI_CMD_LDCS | PDI_RESET_REG); } while (XPROGTarget_ReceiveByte() != 0x00); XPROGTarget_DisableTargetPDI(); }
/** 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); }
/** Erases the target's memory space. * * \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 TINYNVM_EraseMemory(const uint8_t EraseCommand, const uint16_t Address) { /* Wait until the NVM controller is no longer busy */ if (!(TINYNVM_WaitWhileNVMControllerBusy())) return false; /* Set the NVM control register to the target memory erase command */ TINYNVM_SendWriteNVMRegister(XPROG_Param_NVMCMDRegAddr); XPROGTarget_SendByte(EraseCommand); /* Write to a high byte location within the target address space to start the erase process */ TINYNVM_SendPointerAddress(Address | 0x0001); XPROGTarget_SendByte(TPI_CMD_SST | TPI_POINTER_INDIRECT); XPROGTarget_SendByte(0x00); /* Wait until the NVM controller is no longer busy */ if (!(TINYNVM_WaitWhileNVMControllerBusy())) return false; return true; }
/** Writes word addressed memory to the target's memory spaces. * * \param[in] WriteAddress Start address to write to within the target's address space * \param[in] WriteBuffer Buffer to source data from * \param[in] WriteLength Total number of bytes to write to the device (must be an integer multiple of 2) * * \return Boolean true if the command sequence complete successfully */ bool TINYNVM_WriteMemory(const uint16_t WriteAddress, uint8_t* WriteBuffer, uint16_t WriteLength) { /* Wait until the NVM controller is no longer busy */ if (!(TINYNVM_WaitWhileNVMControllerBusy())) return false; /* Must have an integer number of words to write - if extra byte, word-align via a dummy high byte */ if (WriteLength & 0x01) WriteBuffer[WriteLength++] = 0xFF; /* Set the NVM control register to the WORD WRITE command for memory writing */ TINYNVM_SendWriteNVMRegister(XPROG_Param_NVMCMDRegAddr); XPROGTarget_SendByte(TINY_NVM_CMD_WORDWRITE); /* Send the address of the location to write to */ TINYNVM_SendPointerAddress(WriteAddress); while (WriteLength) { /* Wait until the NVM controller is no longer busy */ if (!(TINYNVM_WaitWhileNVMControllerBusy())) return false; /* Write the low byte of data to the target */ XPROGTarget_SendByte(TPI_CMD_SST | TPI_POINTER_INDIRECT_PI); XPROGTarget_SendByte(*(WriteBuffer++)); /* Write the high byte of data to the target */ XPROGTarget_SendByte(TPI_CMD_SST | TPI_POINTER_INDIRECT_PI); XPROGTarget_SendByte(*(WriteBuffer++)); /* Need to decrement the write length twice, since we wrote a whole two-byte word */ WriteLength -= 2; } 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 Length of the data to read from the device * * \return Boolean true if the command sequence complete successfully */ bool TINYNVM_ReadMemory(const uint16_t ReadAddress, uint8_t* ReadBuffer, uint16_t ReadSize) { /* Wait until the NVM controller is no longer busy */ if (!(TINYNVM_WaitWhileNVMControllerBusy())) return false; /* Set the NVM control register to the NO OP command for memory reading */ TINYNVM_SendWriteNVMRegister(XPROG_Param_NVMCMDRegAddr); XPROGTarget_SendByte(TINY_NVM_CMD_NOOP); /* Send the address of the location to read from */ TINYNVM_SendPointerAddress(ReadAddress); while (ReadSize-- && TimeoutTicksRemaining) { /* Read the byte of data from the target */ XPROGTarget_SendByte(TPI_CMD_SLD | TPI_POINTER_INDIRECT_PI); *(ReadBuffer++) = XPROGTarget_ReceiveByte(); } return (TimeoutTicksRemaining > 0); }
/** 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); }
/** Busy-waits while the NVM controller is busy performing a NVM operation, such as a FLASH page read. * * \return Boolean true if the NVM controller became ready within the timeout period, false otherwise */ bool TINYNVM_WaitWhileNVMBusBusy(void) { /* Poll the STATUS register to check to see if NVM access has been enabled */ for (;;) { /* Send the SLDCS command to read the TPI STATUS register to see the NVM bus is active */ XPROGTarget_SendByte(TPI_CMD_SLDCS | TPI_STATUS_REG); uint8_t StatusRegister = XPROGTarget_ReceiveByte(); /* We might have timed out waiting for the status register read response, check here */ if (!(TimeoutTicksRemaining)) return false; /* Check the status register read response to see if the NVM bus is enabled */ if (StatusRegister & TPI_STATUS_NVM) return true; } }
/** Busy-waits while the NVM controller is busy performing a NVM operation, such as a FLASH page read or CRC * calculation. * * \return Boolean true if the NVM controller became ready within the timeout period, false otherwise */ bool XMEGANVM_WaitWhileNVMBusBusy(void) { /* Poll the STATUS register to check to see if NVM access has been enabled */ while (TimeoutMSRemaining) { /* Send the LDCS command to read the PDI STATUS register to see the NVM bus is active */ XPROGTarget_SendByte(PDI_CMD_LDCS | PDI_STATUS_REG); uint8_t StatusRegister = XPROGTarget_ReceiveByte(); /* We might have timed out waiting for the status register read response, check here */ if (!(TimeoutMSRemaining)) return false; /* Check the status register read response to see if the NVM bus is enabled */ if (StatusRegister & PDI_STATUS_NVM) { TimeoutMSRemaining = COMMAND_TIMEOUT_MS; return true; } } return false; }
/** Enables the physical PDI interface on the target and enables access to the internal NVM controller. * * \return Boolean true if the PDI interface was enabled successfully, false otherwise */ bool XMEGANVM_EnablePDI(void) { /* Enable PDI programming mode with the attached target */ XPROGTarget_EnableTargetPDI(); /* Store the RESET key into the RESET PDI register to keep the XMEGA in reset */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_RESET_REG); XPROGTarget_SendByte(PDI_RESET_KEY); /* Lower direction change guard time to 32 USART bits */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_CTRL_REG); XPROGTarget_SendByte(0x02); /* Enable access to the XPROG NVM bus by sending the documented NVM access key to the device */ XPROGTarget_SendByte(PDI_CMD_KEY); for (uint8_t i = sizeof(PDI_NVMENABLE_KEY); i > 0; i--) XPROGTarget_SendByte(PDI_NVMENABLE_KEY[i - 1]); /* Wait until the NVM bus becomes active */ return XMEGANVM_WaitWhileNVMBusBusy(); }
/** Handler for the XPROG LEAVE_PROGMODE command to terminate the PDI programming connection with * the attached device. */ static void XPROGProtocol_LeaveXPROGMode(void) { Endpoint_ClearOUT(); Endpoint_SelectEndpoint(AVRISP_DATA_IN_EPNUM); Endpoint_SetEndpointDirection(ENDPOINT_DIR_IN); if (XPROG_SelectedProtocol == XPRG_PROTOCOL_PDI) { XMEGANVM_WaitWhileNVMBusBusy(); /* Clear the RESET key in the RESET PDI register to allow the XMEGA to run */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_RESET_REG); XPROGTarget_SendByte(0x00); /* Do it twice to make sure it takes affect (silicon bug?) */ XPROGTarget_SendByte(PDI_CMD_STCS | PDI_RESET_REG); XPROGTarget_SendByte(0x00); XPROGTarget_DisableTargetPDI(); } else { TINYNVM_WaitWhileNVMBusBusy(); /* Clear the NVMEN bit in the TPI CONTROL register to disable TPI mode */ XPROGTarget_SendByte(TPI_CMD_SSTCS | TPI_CTRL_REG); XPROGTarget_SendByte(0x00); XPROGTarget_DisableTargetTPI(); } Endpoint_Write_Byte(CMD_XPROG); Endpoint_Write_Byte(XPRG_CMD_LEAVE_PROGMODE); Endpoint_Write_Byte(XPRG_ERR_OK); Endpoint_ClearIN(); }
/** 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; }
/** Sends a SOUT command to the target with the specified I/O address, ready for the data byte to be read. * * \param[in] Address 6-bit I/O address to read from in the target's I/O memory space */ static void TINYNVM_SendWriteNVMRegister(const uint8_t Address) { /* The TPI command for reading from the I/O space uses strange addressing, where the I/O address's upper * two bits of the 6-bit address are shifted left once */ XPROGTarget_SendByte(TPI_CMD_SOUT | ((Address & 0x30) << 1) | (Address & 0x0F)); }
/** 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; }