From c33ec9c51bde08cb5cc3542d578670ade92f8ff2 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Thu, 11 Jan 2024 20:56:13 +0100 Subject: [PATCH] Updated NVM controller code to latest pymcuprog --- src/updi_nvm_v0.c | 79 ++++++++-------- src/updi_nvm_v2.c | 225 +++++++++++++++++++++++++++------------------- src/updi_nvm_v3.c | 130 +++++++++++++++------------ 3 files changed, 244 insertions(+), 190 deletions(-) diff --git a/src/updi_nvm_v0.c b/src/updi_nvm_v0.c index 4ec1d1fd..ca332cd0 100644 --- a/src/updi_nvm_v0.c +++ b/src/updi_nvm_v0.c @@ -34,7 +34,6 @@ #include "avrdude.h" #include "libavrdude.h" -#include "updi_nvm.h" #include "updi_nvm_v0.h" #include "updi_state.h" #include "updi_constants.h" @@ -62,9 +61,9 @@ #define UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE 0x07 // NVMCTRL STATUS -#define UPDI_V0_NVM_STATUS_WRITE_ERROR 2 -#define UPDI_V0_NVM_STATUS_EEPROM_BUSY 1 -#define UPDI_V0_NVM_STATUS_FLASH_BUSY 0 +#define UPDI_V0_NVM_STATUS_WRITE_ERROR_BIT 2 +#define UPDI_V0_NVM_STATUS_EEPROM_BUSY_BIT 1 +#define UPDI_V0_NVM_STATUS_FLASH_BUSY_BIT 0 #define USE_DEFAULT_COMMAND 0xFF @@ -100,16 +99,16 @@ int updi_nvm_chip_erase_V0(const PROGRAMMER *pgm, const AVRPART *p) { return True */ pmsg_debug("Chip erase using NVM CTRL\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + if (updi_nvm_command_V0(pgm, p, UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { pmsg_error("UPDI chip erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } return 0; @@ -142,8 +141,8 @@ int updi_nvm_erase_flash_page_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32 */ unsigned char data[1]; pmsg_debug("erase flash page at address 0x%06X\n", address); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } data[0] = 0xFF; @@ -151,12 +150,12 @@ int updi_nvm_erase_flash_page_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32 pmsg_error("dummy write operation failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) < 0) { + if (updi_nvm_command_V0(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) < 0) { pmsg_error("UPDI flash page erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } return 0; @@ -182,16 +181,16 @@ int updi_nvm_erase_eeprom_V0(const PROGRAMMER *pgm, const AVRPART *p) { raise IOError("Timeout waiting for NVM controller to be ready after EEPROM erase") */ pmsg_debug("erase EEPROM\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM) < 0) { + if (updi_nvm_command_V0(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM) < 0) { pmsg_error("UPDI EEPROM erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } return 0; @@ -227,8 +226,8 @@ int updi_nvm_erase_user_row_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t uint16_t offset; unsigned char data[1]; pmsg_debug("erase user row\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } data[0]=0xFF; @@ -239,12 +238,12 @@ int updi_nvm_erase_user_row_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t return -1; } } - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) < 0) { + if (updi_nvm_command_V0(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) < 0) { pmsg_error("erase page operation failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } return 0; @@ -327,8 +326,8 @@ int updi_nvm_write_fuse_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t add if not self.wait_nvm_ready(): raise PymcuprogError("Timeout waiting for NVM controller to be ready after fuse write") */ - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } pmsg_debug("load NVM address\n"); @@ -346,12 +345,12 @@ int updi_nvm_write_fuse_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t add return -1; } pmsg_debug("execute fuse write\n"); - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE) < 0) { + if (updi_nvm_command_V0(pgm, p, UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE) < 0) { pmsg_error("write fuse operation failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } return 0; @@ -401,17 +400,17 @@ static int nvm_write_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres if not self.wait_nvm_ready(): raise PymcuprogError("Timeout waiting for NVM controller to be ready after page write") */ - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } pmsg_debug("clear page buffer\n"); - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) < 0) { + if (updi_nvm_command_V0(pgm, p, UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) < 0) { pmsg_error("clear page operation failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } if (mode == USE_WORD_ACCESS) { @@ -429,12 +428,12 @@ static int nvm_write_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres if (nvm_command == USE_DEFAULT_COMMAND) { nvm_command = UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE; } - if (updi_nvm_command(pgm, p, nvm_command) < 0) { + if (updi_nvm_command_V0(pgm, p, nvm_command) < 0) { pmsg_error("commit data command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V0(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V0() failed\n"); return -1; } return 0; @@ -468,12 +467,12 @@ int updi_nvm_wait_ready_V0(const PROGRAMMER *pgm, const AVRPART *p) { start_time = avr_ustimestamp(); do { if (updi_read_byte(pgm, p->nvm_base + UPDI_V0_NVMCTRL_STATUS, &status) >= 0) { - if (status & (1 << UPDI_V0_NVM_STATUS_WRITE_ERROR)) { + if (status & (1 << UPDI_V0_NVM_STATUS_WRITE_ERROR_BIT)) { pmsg_error("unable to write NVM status\n"); return -1; } - if (!(status & ((1 << UPDI_V0_NVM_STATUS_EEPROM_BUSY) | - (1 << UPDI_V0_NVM_STATUS_FLASH_BUSY)))) { + if (!(status & ((1 << UPDI_V0_NVM_STATUS_EEPROM_BUSY_BIT) | + (1 << UPDI_V0_NVM_STATUS_FLASH_BUSY_BIT)))) { return 0; } } diff --git a/src/updi_nvm_v2.c b/src/updi_nvm_v2.c index 0d2b2897..e358c13f 100644 --- a/src/updi_nvm_v2.c +++ b/src/updi_nvm_v2.c @@ -34,7 +34,6 @@ #include "avrdude.h" #include "libavrdude.h" -#include "updi_nvm.h" #include "updi_nvm_v2.h" #include "updi_state.h" #include "updi_constants.h" @@ -55,16 +54,20 @@ // NVMCTRL v2 CTRLA #define UPDI_V2_NVMCTRL_CTRLA_NOCMD 0x00 +#define UPDI_V2_NVMCTRL_CTRLA_NOOP 0x01 #define UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE 0x02 #define UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE 0x08 +#define UPDI_V2_NVMCTRL_CTRLA_EEPROM_WRITE 0x12 #define UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE 0x13 +#define UPDI_V2_NVMCTRL_CTRLA_EEPROM_BYTE_ERASE 0x18 #define UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE 0x20 #define UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE 0x30 // NVMCTRL STATUS -#define UPDI_V2_NVM_STATUS_WRITE_ERROR 2 -#define UPDI_V2_NVM_STATUS_EEPROM_BUSY 1 -#define UPDI_V2_NVM_STATUS_FLASH_BUSY 0 +#define UPDI_V2_NVM_STATUS_WRITE_ERROR_MASK 0x30 +#define UPDI_V2_NVM_STATUS_WRITE_ERROR_BIT 4 +#define UPDI_V2_NVM_STATUS_EEPROM_BUSY_BIT 1 +#define UPDI_V2_NVM_STATUS_FLASH_BUSY_BIT 0 #define USE_DEFAULT_COMMAND 0xFF @@ -79,35 +82,45 @@ int updi_nvm_chip_erase_V2(const PROGRAMMER *pgm, const AVRPART *p) { def chip_erase(self): """ Does a chip erase using the NVM controller - Note that on locked devices this it not possible - and the ERASE KEY has to be used instead + + Note that on locked devices this it not possible and the ERASE KEY has to be used instead """ - self.logger.info("Chip erase using NVM CTRL") + self.logger.debug("Chip erase using NVM CTRL") # Wait until NVM CTRL is ready to erase if not self.wait_nvm_ready(): - raise Exception("Timeout waiting for NVM controller to be ready before chip erase") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready before chip erase") # Erase - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE) + self.execute_nvm_command(self.NVMCMD_CHIP_ERASE) # And wait for it - if not self.wait_nvm_ready(): - raise Exception("Timeout waiting for NVM controller to be ready after chip erase") + status = self.wait_nvm_ready() - return True + # Remove command from NVM controller + self.logger.debug("Clear NVM command") + self.execute_nvm_command(self.NVMCMD_NOCMD) + if not status: + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready after chip erase") */ + int status; pmsg_debug("chip erase using NVM CTRL\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V2(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { pmsg_error("chip erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + status = updi_nvm_wait_ready_V2(pgm, p); + pmsg_debug("clear NVM command\n"); + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("command buffer erase failed\n"); + return -1; + } + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } return 0; @@ -117,35 +130,41 @@ int updi_nvm_erase_flash_page_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32 /* def erase_flash_page(self, address): """ - Erasing single flash page using the NVM controller (v1) + Erasing single flash page using the NVM controller :param address: Start address of page to erase :type address: int """ - self.logger.info("Erase flash page at address 0x%08X", address) + self.logger.debug("Erase flash page at address 0x%08X", address) # Wait until NVM CTRL is ready to erase if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready before flash page erase") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready before flash page erase") # Erase command - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) + self.execute_nvm_command(self.NVMCMD_FLASH_PAGE_ERASE) # Dummy write self.readwrite.write_data(address, [0xFF]) # And wait for it - if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready after flash page erase") + status = self.wait_nvm_ready() # Remove command from NVM controller self.logger.debug("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) + self.execute_nvm_command(self.NVMCMD_NOCMD) + if not status: + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready after flash page erase") */ unsigned char data[1]; - pmsg_debug("erase flash page at address 0x%06X\n", address); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + int status; + pmsg_debug("erase flash page at address 0x%08X\n", address); + if (updi_nvm_wait_ready_V2(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); + return -1; + } + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { + pmsg_error("flash page erase command failed\n"); return -1; } data[0] = 0xFF; @@ -153,12 +172,14 @@ int updi_nvm_erase_flash_page_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32 pmsg_error("dummy write operation failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { - pmsg_error("flash page erase command failed\n"); + status = updi_nvm_wait_ready_V2(pgm, p); + pmsg_debug("clear NVM command\n"); + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("command buffer erase failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } return 0; @@ -168,41 +189,44 @@ int updi_nvm_erase_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p) { /* def erase_eeprom(self): """ - Erase EEPROM memory only (v1) + Erase EEPROM memory only """ - self.logger.info("Erase EEPROM") + self.logger.debug("Erase EEPROM") # Wait until NVM CTRL is ready to erase if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready before EEPROM erase") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready before EEPROM erase") # Erase - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE) + self.execute_nvm_command(self.NVMCMD_EEPROM_ERASE) # And wait for it - if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready after EEPROM erase") + status = self.wait_nvm_ready() # Remove command from NVM controller self.logger.debug("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) + self.execute_nvm_command(self.NVMCMD_NOCMD) + if not status: + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready after EEPROM erase") */ + int status; pmsg_debug("erase EEPROM\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V2(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { pmsg_error("EEPROM erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + status = updi_nvm_wait_ready_V2(pgm, p); + pmsg_debug("clear NVM command\n"); + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("command buffer erase failed\n"); return -1; } - pmsg_debug("clear NVM command\n"); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("sending empty command failed\n"); + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } return 0; @@ -264,35 +288,39 @@ int updi_nvm_write_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t a Writes data to NVM (EEPROM) :param address: address to write to + :type address: int :param data: data to write + :type data: list of bytes """ - nvm_command = constants.UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE + nvm_command = self.NVMCMD_EEPROM_ERASE_WRITE # Check that NVM controller is ready if not self.wait_nvm_ready(): - raise Exception("Timeout waiting for NVM ready before command write") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM ready before command write") # Write the command to the NVM controller - self.logger.info("NVM EEPROM erase/write command") + self.logger.debug("NVM EEPROM erase/write command") self.execute_nvm_command(nvm_command) # Write the data self.readwrite.write_data(address, data) # Wait for NVM controller to be ready again - if not self.wait_nvm_ready(): - raise Exception("Timeout waiting for NVM ready after data write") + status = self.wait_nvm_ready() # Remove command from NVM controller - self.logger.info("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) + self.logger.debug("Clear NVM command") + self.execute_nvm_command(self.NVMCMD_NOCMD) + if not status: + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM ready after data write") */ - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + int status; + if (updi_nvm_wait_ready_V2(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } pmsg_debug("NVM EEPROM erase/write command\n"); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE) < 0) { + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE) < 0) { pmsg_error("EEPROM erase command failed\n"); return -1; } @@ -300,13 +328,14 @@ int updi_nvm_write_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t a pmsg_error("write data operation failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + status = updi_nvm_wait_ready_V2(pgm, p); + pmsg_debug("clear NVM command\n"); + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("command buffer erase failed\n"); return -1; } - pmsg_debug("clear NVM command\n"); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("clear NVM command failed\n"); + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } return 0; @@ -333,23 +362,29 @@ static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres uint16_t size, access_mode mode) { /* - def write_nvm(self, address, data, use_word_access): + def write_nvm(self, address, data, use_word_access=True): """ - Writes data to NVM (version 1) + Writes data to NVM. + This version of the NVM block has no page buffer, so words are written directly. :param address: address to write to + :type address: int :param data: data to write - :param use_word_access: write in whole words? + :type data: list of bytes + :param use_word_access: True for 16-bit writes (eg: flash) + :type use_word_access: bool, defaults to True + :raises: PymcuprogSerialUpdiNvmTimeout if a timeout occurred + :raises: PymcuprogSerialUpdiNvmError if an error condition is encountered """ - nvm_command = constants.UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE + nvm_command = self.NVMCMD_FLASH_WRITE # Check that NVM controller is ready if not self.wait_nvm_ready(): - raise Exception("Timeout waiting for NVM controller to be ready before page buffer clear") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready before page buffer clear") # Write the command to the NVM controller - self.logger.info("NVM write command") + self.logger.debug("NVM write command") self.execute_nvm_command(nvm_command) # Write the data @@ -359,19 +394,21 @@ static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres self.readwrite.write_data(address, data) # Wait for NVM controller to be ready again - if not self.wait_nvm_ready(): - raise Exception("Timeout waiting for NVM controller to be ready after data write") + status = self.wait_nvm_ready() # Remove command from NVM controller - self.logger.info("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) + self.logger.debug("Clear NVM command") + self.execute_nvm_command(self.NVMCMD_NOCMD) + if not status: + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready after data write") */ - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + int status; + if (updi_nvm_wait_ready_V2(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } pmsg_debug("NVM write command\n"); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE) < 0) { + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE) < 0) { pmsg_error("clear page operation failed\n"); return -1; } @@ -386,13 +423,14 @@ static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres return -1; } } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("wait for ready chip failed\n"); + status = updi_nvm_wait_ready_V2(pgm, p); + pmsg_debug("clear NVM command\n"); + if (updi_nvm_command_V2(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("command buffer erase failed\n"); return -1; } - pmsg_debug("clear NVM command\n"); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("clear NVM command failed\n"); + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V2() failed\n"); return -1; } return 0; @@ -400,21 +438,26 @@ static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres int updi_nvm_wait_ready_V2(const PROGRAMMER *pgm, const AVRPART *p) { /* - def wait_nvm_ready(self): + def wait_nvm_ready(self, timeout_ms=100): """ Waits for the NVM controller to be ready + + :param timeout_ms: Timeout period in milliseconds + :type timeout_ms: int, defaults to 100 + :returns: True if 'ready', False if timeout occurred before ready + :rtype: bool + :raises: PymcuprogSerialUpdiNvmError if an error condition is encountered """ - timeout = Timeout(10000) # 10 sec timeout, just to be sure + timeout = Timeout(timeout_ms) self.logger.debug("Wait NVM ready") while not timeout.expired(): - status = self.readwrite.read_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_STATUS) - if status & (1 << constants.UPDI_V0_NVM_STATUS_WRITE_ERROR): - self.logger.error("NVM error") - return False + status = self.readwrite.read_byte(self.device.nvmctrl_address + self.NVMCTRL_STATUS) + if status & self.STATUS_WRITE_ERROR_bm: + self.logger.error("NVM error (%d)", status >> self.STATUS_WRITE_ERROR_bp) + raise PymcuprogSerialUpdiNvmError(msg="NVM error", code=(status >> self.STATUS_WRITE_ERROR_bp)) - if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) | - (1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)): + if not status & ((1 << self.STATUS_EEPROM_BUSY_bp) | (1 << self.STATUS_FLASH_BUSY_bp)): return True self.logger.error("Wait NVM ready timed out") @@ -426,12 +469,12 @@ int updi_nvm_wait_ready_V2(const PROGRAMMER *pgm, const AVRPART *p) { start_time = avr_ustimestamp(); do { if (updi_read_byte(pgm, p->nvm_base + UPDI_V2_NVMCTRL_STATUS, &status) >= 0) { - if (status & (1 << UPDI_V2_NVM_STATUS_WRITE_ERROR)) { - pmsg_error("unable to write NVM status\n"); + if (status & UPDI_V2_NVM_STATUS_WRITE_ERROR_MASK) { + pmsg_error("unable to write NVM status, error %d\n", status >> UPDI_V2_NVM_STATUS_WRITE_ERROR_BIT); return -1; } - if (!(status & ((1 << UPDI_V2_NVM_STATUS_EEPROM_BUSY) | - (1 << UPDI_V2_NVM_STATUS_FLASH_BUSY)))) { + if (!(status & ((1 << UPDI_V2_NVM_STATUS_EEPROM_BUSY_BIT) | + (1 << UPDI_V2_NVM_STATUS_FLASH_BUSY_BIT)))) { return 0; } } diff --git a/src/updi_nvm_v3.c b/src/updi_nvm_v3.c index e11051fc..9100f2bc 100644 --- a/src/updi_nvm_v3.c +++ b/src/updi_nvm_v3.c @@ -34,7 +34,6 @@ #include "avrdude.h" #include "libavrdude.h" -#include "updi_nvm.h" #include "updi_nvm_v3.h" #include "updi_state.h" #include "updi_constants.h" @@ -68,9 +67,10 @@ #define UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE 0x30 // NVMCTRL STATUS -#define UPDI_V3_NVM_STATUS_WRITE_ERROR 2 -#define UPDI_V3_NVM_STATUS_EEPROM_BUSY 1 -#define UPDI_V3_NVM_STATUS_FLASH_BUSY 0 +#define UPDI_V3_NVM_STATUS_WRITE_ERROR_MASK 0x70 +#define UPDI_V3_NVM_STATUS_WRITE_ERROR_BIT 2 +#define UPDI_V3_NVM_STATUS_EEPROM_BUSY_BIT 1 +#define UPDI_V3_NVM_STATUS_FLASH_BUSY_BIT 0 #define USE_DEFAULT_COMMAND 0xFF @@ -86,44 +86,43 @@ int updi_nvm_chip_erase_V3(const PROGRAMMER *pgm, const AVRPART *p) { """ Does a chip erase using the NVM controller - Note that on locked devices this is not possible - and the ERASE KEY has to be used instead, see the unlock method + Note that on locked devices this is not possible and the ERASE KEY has to be used instead, see the unlock method """ - self.logger.info("Chip erase using NVM CTRL") + self.logger.debug("Chip erase using NVM CTRL") # Wait until NVM CTRL is ready to erase if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready before chip erase") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready before chip erase") # Erase - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) + self.execute_nvm_command(self.NVMCMD_CHIP_ERASE) # And wait for it status = self.wait_nvm_ready() # Remove command - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) + self.execute_nvm_command(self.NVMCMD_NOCMD) if not status: - raise IOError("Timeout waiting for NVM controller to be ready after chip erase") - - return True + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready after chip erase") */ + int status; pmsg_debug("Chip erase using NVM CTRL\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V3(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { pmsg_error("chip erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + status = updi_nvm_wait_ready_V3(pgm, p); + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("sending empty command failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("sending empty command failed\n"); + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } return 0; @@ -133,36 +132,37 @@ int updi_nvm_erase_flash_page_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32 /* def erase_flash_page(self, address): """ - Erasing single flash page using the NVM controller (v3) + Erasing single flash page using the NVM controller :param address: Start address of page to erase :type address: int """ - self.logger.info("Erase flash page at address 0x%08X", address) + self.logger.debug("Erase flash page at address 0x%08X", address) # Wait until NVM CTRL is ready to erase if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready before flash page erase") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready before flash page erase") # Dummy write self.readwrite.write_data(address, [0xFF]) # Erase - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) + self.execute_nvm_command(self.NVMCMD_FLASH_PAGE_ERASE) # And wait for it status = self.wait_nvm_ready() # Remove command - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) + self.execute_nvm_command(self.NVMCMD_NOCMD) if not status: - raise IOError("Timeout waiting for NVM controller to be ready after flash page erase") + raise PymcuprogSerialUpdiNvmTimeout("Timeout waiting for NVM controller to be ready after flash page erase") */ + int status; unsigned char data[1]; pmsg_debug("erase flash page at address 0x%06X\n", address); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V3(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } data[0] = 0xFF; @@ -170,12 +170,17 @@ int updi_nvm_erase_flash_page_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32 pmsg_error("dummy write operation failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { pmsg_error("flash page erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + status = updi_nvm_wait_ready_V3(pgm, p); + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("sending empty command failed\n"); + return -1; + } + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } return 0; @@ -205,21 +210,23 @@ int updi_nvm_erase_eeprom_V3(const PROGRAMMER *pgm, const AVRPART *p) { if not status: raise IOError("Timeout waiting for NVM controller to be ready after EEPROM erase") */ + int status; pmsg_debug("erase EEPROM\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V3(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { pmsg_error("EEPROM erase command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + status = updi_nvm_wait_ready_V3(pgm, p); + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("sending empty command failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("sending empty command failed\n"); + if (status < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } return 0; @@ -354,17 +361,17 @@ static int nvm_write_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres # Remove command self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) */ - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V3(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } pmsg_debug("clear page buffer\n"); - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR) < 0) { + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR) < 0) { pmsg_error("clear page operation failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V3(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } if (mode == USE_WORD_ACCESS) { @@ -382,15 +389,15 @@ static int nvm_write_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres if (nvm_command == USE_DEFAULT_COMMAND) { nvm_command = UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE; } - if (updi_nvm_command(pgm, p, nvm_command) < 0) { + if (updi_nvm_command_V3(pgm, p, nvm_command) < 0) { pmsg_error("commit data command failed\n"); return -1; } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); + if (updi_nvm_wait_ready_V3(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready_V3() failed\n"); return -1; } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + if (updi_nvm_command_V3(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { pmsg_error("sending empty command failed\n"); return -1; } @@ -400,21 +407,26 @@ static int nvm_write_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres int updi_nvm_wait_ready_V3(const PROGRAMMER *pgm, const AVRPART *p) { /* - def wait_nvm_ready(self): + def wait_nvm_ready(self, timeout_ms=100): """ Waits for the NVM controller to be ready + + :param timeout_ms: Timeout period in milliseconds + :type timeout_ms: int, defaults to 100 + :returns: True if 'ready', False if timeout occurred before ready + :rtype: bool + :raises: PymcuprogSerialUpdiNvmError if an error condition is encountered """ - timeout = Timeout(10000) # 10 sec timeout, just to be sure + timeout = Timeout(timeout_ms) self.logger.debug("Wait NVM ready") while not timeout.expired(): - status = self.readwrite.read_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_STATUS) - if status & (1 << constants.UPDI_V0_NVM_STATUS_WRITE_ERROR): - self.logger.error("NVM error") - return False + status = self.readwrite.read_byte(self.device.nvmctrl_address + self.NVMCTRL_STATUS) + if status & self.STATUS_WRITE_ERROR_bm: + self.logger.error("NVM error (%d)", status >> self.STATUS_WRITE_ERROR_bp) + raise PymcuprogSerialUpdiNvmError(msg="NVM error", code=(status >> self.STATUS_WRITE_ERROR_bp)) - if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) | - (1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)): + if not status & ((1 << self.STATUS_EEPROM_BUSY_bp) | (1 << self.STATUS_FLASH_BUSY_bp)): return True self.logger.error("Wait NVM ready timed out") @@ -426,12 +438,12 @@ int updi_nvm_wait_ready_V3(const PROGRAMMER *pgm, const AVRPART *p) { start_time = avr_ustimestamp(); do { if (updi_read_byte(pgm, p->nvm_base + UPDI_V3_NVMCTRL_STATUS, &status) >= 0) { - if (status & (1 << UPDI_V3_NVM_STATUS_WRITE_ERROR)) { - pmsg_error("unable to write NVM status\n"); + if (status & UPDI_V3_NVM_STATUS_WRITE_ERROR_MASK) { + pmsg_error("unable to write NVM status, error code %d\n", status >> UPDI_V3_NVM_STATUS_WRITE_ERROR_BIT); return -1; } - if (!(status & ((1 << UPDI_V3_NVM_STATUS_EEPROM_BUSY) | - (1 << UPDI_V3_NVM_STATUS_FLASH_BUSY)))) { + if (!(status & ((1 << UPDI_V3_NVM_STATUS_EEPROM_BUSY_BIT) | + (1 << UPDI_V3_NVM_STATUS_FLASH_BUSY_BIT)))) { return 0; } }