From 2358893974b93e0f8bb8adff2b7a2b3bc02f795b Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Tue, 9 Jan 2024 18:24:28 +0100 Subject: [PATCH] Split current NVM code into three separate modules --- src/CMakeLists.txt | 6 + src/Makefile.am | 6 + src/updi_constants.h | 47 -- src/updi_nvm.c | 1149 ++---------------------------------------- src/updi_nvm_v0.c | 501 ++++++++++++++++++ src/updi_nvm_v0.h | 51 ++ src/updi_nvm_v2.c | 459 +++++++++++++++++ src/updi_nvm_v2.h | 51 ++ src/updi_nvm_v3.c | 459 +++++++++++++++++ src/updi_nvm_v3.h | 51 ++ 10 files changed, 1635 insertions(+), 1145 deletions(-) create mode 100644 src/updi_nvm_v0.c create mode 100644 src/updi_nvm_v0.h create mode 100644 src/updi_nvm_v2.c create mode 100644 src/updi_nvm_v2.h create mode 100644 src/updi_nvm_v3.c create mode 100644 src/updi_nvm_v3.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6be05e1a..66309608 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -237,6 +237,12 @@ add_library(libavrdude updi_link.h updi_nvm.c updi_nvm.h + updi_nvm_v0.c + updi_nvm_v0.h + updi_nvm_v2.c + updi_nvm_v2.h + updi_nvm_v3.c + updi_nvm_v3.h updi_readwrite.c updi_readwrite.h updi_state.c diff --git a/src/Makefile.am b/src/Makefile.am index 11471781..775568f2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -192,6 +192,12 @@ libavrdude_a_SOURCES = \ updi_readwrite.h \ updi_nvm.c \ updi_nvm.h \ + updi_nvm_v0.c \ + updi_nvm_v0.h \ + updi_nvm_v2.c \ + updi_nvm_v2.h \ + updi_nvm_v3.c \ + updi_nvm_v3.h \ urclock.c \ urclock.h \ urclock_hash.h \ diff --git a/src/updi_constants.h b/src/updi_constants.h index ff8a446f..c9e8e1a0 100644 --- a/src/updi_constants.h +++ b/src/updi_constants.h @@ -106,51 +106,4 @@ #define UPDI_RESET_REQ_VALUE 0x59 -// FLASH CONTROLLER -#define UPDI_NVMCTRL_CTRLA 0x00 -#define UPDI_NVMCTRL_CTRLB 0x01 -#define UPDI_NVMCTRL_STATUS 0x02 -#define UPDI_NVMCTRL_INTCTRL 0x03 -#define UPDI_NVMCTRL_INTFLAGS 0x04 -#define UPDI_NVMCTRL_DATAL 0x06 -#define UPDI_NVMCTRL_DATAH 0x07 -#define UPDI_NVMCTRL_ADDRL 0x08 -#define UPDI_NVMCTRL_ADDRH 0x09 - -// NVMCTRL v0 CTRLA -#define UPDI_V0_NVMCTRL_CTRLA_NOP 0x00 -#define UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE 0x01 -#define UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE 0x02 -#define UPDI_V0_NVMCTRL_CTRLA_ERASE_WRITE_PAGE 0x03 -#define UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR 0x04 -#define UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE 0x05 -#define UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM 0x06 -#define UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE 0x07 - -// NVMCTRL v2 CTRLA -#define UPDI_V2_NVMCTRL_CTRLA_NOCMD 0x00 -#define UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE 0x02 -#define UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE 0x08 -#define UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE 0x13 -#define UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE 0x20 -#define UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE 0x30 - -// NVMCTRL v3 CTRLA -#define UPDI_V3_NVMCTRL_CTRLA_NOCMD 0x00 -#define UPDI_V3_NVMCTRL_CTRLA_NOP 0x01 -#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE 0x04 -#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE_WRITE 0x05 -#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE 0x08 -#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR 0x0F -#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_WRITE 0x14 -#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE_WRITE 0x15 -#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE 0x17 -#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_BUFFER_CLEAR 0x1F -#define UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE 0x20 -#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE 0x30 - -#define UPDI_NVM_STATUS_WRITE_ERROR 2 -#define UPDI_NVM_STATUS_EEPROM_BUSY 1 -#define UPDI_NVM_STATUS_FLASH_BUSY 0 - #endif /* updi_constants_h */ diff --git a/src/updi_nvm.c b/src/updi_nvm.c index a853b6c2..c2fc697b 100644 --- a/src/updi_nvm.c +++ b/src/updi_nvm.c @@ -35,1037 +35,20 @@ #include "avrdude.h" #include "libavrdude.h" #include "updi_nvm.h" +#include "updi_nvm_v0.h" +#include "updi_nvm_v2.h" +#include "updi_nvm_v3.h" #include "updi_state.h" -#include "updi_constants.h" -#include "updi_readwrite.h" - -typedef enum -{ - DONT_USE_WORD_ACCESS, - USE_WORD_ACCESS -} access_mode; - -#define USE_DEFAULT_COMMAND 0xFF - -static int nvm_chip_erase_V0(const PROGRAMMER *pgm, const AVRPART *p) { -/* - def chip_erase(self): - """ - 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 - """ - self.logger.info("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") - - # Erase - self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE) - - # And wait for it - if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready after chip erase") - - 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"); - return -1; - } - if (updi_nvm_command(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"); - return -1; - } - return 0; -} - -static int nvm_erase_flash_page_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address) { -/* - def erase_flash_page(self, address): - """ - Erasing single flash page using the NVM controller (v0) - - :param address: Start address of page to erase - :type address: int - """ - self.logger.info("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") - - # Dummy write - self.readwrite.write_data(address, [0xFF]) - - # Erase - self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) - - # And wait for it - if not self.wait_nvm_ready(): - raise IOError("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"); - return -1; - } - data[0] = 0xFF; - if (updi_write_data(pgm, address, data, 1) < 0) { - pmsg_error("dummy write operation failed\n"); - return -1; - } - if (updi_nvm_command(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"); - return -1; - } - return 0; -} - -static int nvm_erase_eeprom_V0(const PROGRAMMER *pgm, const AVRPART *p) { -/* - def erase_eeprom(self): - """ - Erase EEPROM memory only (v0) - """ - self.logger.info("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") - - # Erase - self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM) - - # And wait for it - if not self.wait_nvm_ready(): - 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"); - return -1; - } - if (updi_nvm_command(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"); - return -1; - } - return 0; -} - -static int nvm_erase_user_row_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size) { -/* - def erase_user_row(self, address, size): - """ - Erase User Row memory only (v0) - - :param address: Start address of user row - :type address: int - """ - self.logger.info("Erase user row") - - # 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 user row erase") - - # On this NVM version user row is implemented as EEPROM - # When erasing single EEPROM pages a dummy write is needed for each location to be erased - for offset in range(size): - self.readwrite.write_data(address+offset, [0xFF]) - - # Erase - self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) - - # And wait for it - if not self.wait_nvm_ready(): - raise IOError("Timeout waiting for NVM controller to be ready after user row erase") -*/ - 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"); - return -1; - } - data[0]=0xFF; - for (offset = 0; offset> 8) & 0xFF) - - # Write data - self.logger.debug("Load fuse data") - self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_DATAL, data[0] & 0xFF) - - # Execute - self.logger.debug("Execute fuse write") - self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE) - - 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"); - return -1; - } - pmsg_debug("load NVM address\n"); - if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRL, address & 0xFF) < 0) { - pmsg_error("UPDI write ADDRL operation failed\n"); - return -1; - } - if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRH, (address >> 8) & 0xFF) < 0) { - pmsg_error("write ADDRH operation failed\n"); - return -1; - } - pmsg_debug("load fuse data\n"); - if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_DATAL, value & 0xFF) < 0) { - pmsg_error("write DATAL operation failed\n"); - return -1; - } - pmsg_debug("execute fuse write\n"); - if (updi_nvm_command(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"); - return -1; - } - return 0; -} - -static int nvm_write_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, - uint16_t size, access_mode mode, uint8_t nvm_command) -{ -/* - def write_nvm(self, address, data, use_word_access, nvmcommand=constants.UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE): - """ - Writes a page of data to NVM (v0) - - By default the PAGE_WRITE command is used, which - requires that the page is already erased. - By default word access is used (flash) - - :param address: address to write to - :param data: data to write - :param use_word_access: write whole words? - :param nvmcommand: command to use for commit - """ - - # Check that NVM controller is ready - if not self.wait_nvm_ready(): - raise PymcuprogError("Timeout waiting for NVM controller to be ready before page buffer clear") - - # Clear the page buffer - self.logger.debug("Clear page buffer") - self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) - - # Wait for NVM controller to be ready - if not self.wait_nvm_ready(): - raise PymcuprogError("Timeout waiting for NVM controller to be ready after page buffer clear") - - # Load the page buffer by writing directly to location - if use_word_access: - self.readwrite.write_data_words(address, data) - else: - self.readwrite.write_data(address, data) - - # Write the page to NVM, maybe erase first - self.logger.debug("Committing data") - self.execute_nvm_command(nvmcommand) - - # Wait for NVM controller to be ready again - 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"); - return -1; - } - pmsg_debug("clear page buffer\n"); - if (updi_nvm_command(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"); - return -1; - } - if (mode == USE_WORD_ACCESS) { - if (updi_write_data_words(pgm, address, buffer, size) < 0) { - pmsg_error("write data words operation failed\n"); - return -1; - } - } else { - if (updi_write_data(pgm, address, buffer, size) < 0) { - pmsg_error("write data operation failed\n"); - return -1; - } - } - pmsg_debug("committing data\n"); - if (nvm_command == USE_DEFAULT_COMMAND) { - nvm_command = UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE; - } - if (updi_nvm_command(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"); - return -1; - } - return 0; -} - -static int 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 - """ - self.logger.info("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") - - # Erase - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_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") - - 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"); - return -1; - } - if (updi_nvm_command(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"); - return -1; - } - return 0; -} - -static int nvm_erase_flash_page_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address) { -/* - def erase_flash_page(self, address): - """ - Erasing single flash page using the NVM controller (v1) - - :param address: Start address of page to erase - :type address: int - """ - self.logger.info("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") - - # Erase command - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_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") - - # Remove command from NVM controller - self.logger.debug("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) -*/ - 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"); - return -1; - } - data[0] = 0xFF; - if (updi_write_data(pgm, address, data, 1) < 0) { - 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"); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); - return -1; - } - return 0; -} - -static int nvm_erase_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p) { -/* - def erase_eeprom(self): - """ - Erase EEPROM memory only (v1) - """ - self.logger.info("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") - - # Erase - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_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") - - # Remove command from NVM controller - self.logger.debug("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) -*/ - pmsg_debug("erase EEPROM\n"); - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); - return -1; - } - if (updi_nvm_command(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"); - 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"); - return -1; - } - return 0; -} - -static int nvm_erase_user_row_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size) { -/* - def erase_user_row(self, address, size): - """ - Erase User Row memory only (v1) - - :param address: Start address of user row - :type address: int - """ - # size is not used for this NVM version - _dummy = size - # On this NVM version user row is implemented as flash - return self.erase_flash_page(address) -*/ - return nvm_erase_flash_page_V2(pgm, p, address); -} - -static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, - uint16_t size, access_mode mode); - -static int nvm_write_flash_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { -/* - def write_flash(self, address, data): - """ - Writes data to flash (v1) - - :param address: address to write to - :param data: data to write - """ - return self.write_nvm(address, data, use_word_access=True) -*/ - return nvm_write_V2(pgm, p, address, buffer, size, USE_WORD_ACCESS); -} - -static int nvm_write_user_row_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { -/* - def write_user_row(self, address, data): - """ - Writes data to user row (v1) - - :param address: address to write to - :param data: data to write - """ - # On this NVM variant user row is implemented as Flash - return self.write_nvm(address, data, use_word_access=False) -*/ - return nvm_write_V2(pgm, p, address, buffer, size, DONT_USE_WORD_ACCESS); -} - -static int nvm_write_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { -/* - def write_eeprom(self, address, data): - """ - Writes data to NVM (EEPROM) - - :param address: address to write to - :param data: data to write - """ - nvm_command = constants.UPDI_V2_NVMCTRL_CTRLA_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") - - # Write the command to the NVM controller - self.logger.info("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") - - # Remove command from NVM controller - self.logger.info("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) -*/ - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() 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) { - pmsg_error("EEPROM erase command failed\n"); - return -1; - } - if (updi_write_data(pgm, address, buffer, size) < 0) { - 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"); - 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"); - return -1; - } - return 0; -} - -static int nvm_write_fuse_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint8_t value) { -/* - def write_fuse(self, address, data): - """ - Writes one fuse value - V1 fuses are EEPROM-based - - :param address: address to write to - :param data: data to write - """ - return self.write_eeprom(address, data) -*/ - unsigned char buffer[1]; - buffer[0]=value; - return nvm_write_eeprom_V2(pgm, p, address, buffer, 1); -} - -static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, - uint16_t size, access_mode mode) -{ -/* - def write_nvm(self, address, data, use_word_access): - """ - Writes data to NVM (version 1) - This version of the NVM block has no page buffer, so words are written directly. - - :param address: address to write to - :param data: data to write - :param use_word_access: write in whole words? - """ - nvm_command = constants.UPDI_V2_NVMCTRL_CTRLA_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") - - # Write the command to the NVM controller - self.logger.info("NVM write command") - self.execute_nvm_command(nvm_command) - - # Write the data - if use_word_access: - self.readwrite.write_data_words(address, data) - else: - 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") - - # Remove command from NVM controller - self.logger.info("Clear NVM command") - self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) -*/ - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("updi_nvm_wait_ready() failed\n"); - return -1; - } - pmsg_debug("NVM write command\n"); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE) < 0) { - pmsg_error("clear page operation failed\n"); - return -1; - } - if (mode == USE_WORD_ACCESS) { - if (updi_write_data_words(pgm, address, buffer, size) < 0) { - pmsg_error("write data words operation failed\n"); - return -1; - } - } else { - if (updi_write_data(pgm, address, buffer, size) < 0) { - pmsg_error("write data operation failed\n"); - return -1; - } - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - pmsg_error("wait for ready chip 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"); - return -1; - } - return 0; -} - -static int nvm_chip_erase_V3(const PROGRAMMER *pgm, const AVRPART *p) { -/* - def chip_erase(self): - """ - 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 - """ - self.logger.info("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") - - # Erase - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) - - # And wait for it - status = self.wait_nvm_ready() - - # Remove command - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) - - if not status: - raise IOError("Timeout waiting for NVM controller to be ready after chip erase") - - 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"); - return -1; - } - if (updi_nvm_command(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"); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("sending empty command failed\n"); - return -1; - } - return 0; -} - -static int nvm_erase_flash_page_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address) { -/* - def erase_flash_page(self, address): - """ - Erasing single flash page using the NVM controller (v3) - - :param address: Start address of page to erase - :type address: int - """ - self.logger.info("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") - - # Dummy write - self.readwrite.write_data(address, [0xFF]) - - # Erase - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) - - # And wait for it - status = self.wait_nvm_ready() - - # Remove command - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) - - if not status: - raise IOError("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"); - return -1; - } - data[0] = 0xFF; - if (updi_write_data(pgm, address, data, 1) < 0) { - pmsg_error("dummy write operation failed\n"); - return -1; - } - if (updi_nvm_command(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"); - return -1; - } - return 0; -} - -static int nvm_erase_eeprom_V3(const PROGRAMMER *pgm, const AVRPART *p) { -/* - def erase_eeprom(self): - """ - Erase EEPROM memory only - """ - self.logger.info("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") - - # Erase - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE) - - # And wait for it - status = self.wait_nvm_ready() - - # Remove command - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) - - if not status: - 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"); - return -1; - } - if (updi_nvm_command(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"); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("sending empty command failed\n"); - return -1; - } - return 0; -} - -static int nvm_erase_user_row_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size) { -/* - def erase_user_row(self, address, size): - """ - Erase User Row memory only - - :param address: Start address of user row - :type address: int - """ - self.logger.info("Erase user row") - - # On this NVM version user row is implemented as FLASH - return self.erase_flash_page(self, address) -*/ - pmsg_debug("erase user row at address 0x%06X\n", address); - - return nvm_erase_flash_page_V3(pgm, p, address); -} - -static int nvm_write_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, - uint16_t size, access_mode mode, uint8_t nvm_command); - -static int nvm_write_flash_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { -/* - def write_flash(self, address, data): - """ - Writes data to flash (v3) - - :param address: address to write to - :param data: data to write - """ - return self.write_nvm(address, data, use_word_access=True) -*/ - return nvm_write_V3(pgm, p, address, buffer, size, USE_WORD_ACCESS, USE_DEFAULT_COMMAND); -} - -static int nvm_write_user_row_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { -/* - def write_user_row(self, address, data): - """ - Writes data to user row (v3) - - :param address: address to write to - :param data: data to write - """ - # On this NVM variant user row is implemented as FLASH - return self.write_nvm(address, data, use_word_access=True) -*/ - return nvm_write_V3(pgm, p, address, buffer, size, USE_WORD_ACCESS, USE_DEFAULT_COMMAND); -} - -static int nvm_write_eeprom_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { -/* - def write_eeprom(self, address, data): - """ - Write data to EEPROM (v3) - - :param address: address to write to - :param data: data to write - """ - return self.write_nvm(address, data, use_word_access=False, - nvmcommand=constants.UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE_WRITE) -*/ - return nvm_write_V3(pgm, p, address, buffer, size, DONT_USE_WORD_ACCESS, UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE_WRITE); -} - -static int nvm_write_fuse_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint8_t value) { -/* - def write_fuse(self, address, data): - """ - Writes one fuse value (v3) - - :param address: address to write to - :param data: data to write - """ - return self.write_eeprom(address, data) -*/ - unsigned char buffer[1]; - buffer[0] = value; - return nvm_write_eeprom_V3(pgm, p, address, buffer, 1); -} - -static int nvm_write_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, - uint16_t size, access_mode mode, uint8_t nvm_command) -{ -/* - def write_nvm(self, address, data, use_word_access, nvmcommand=constants.UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE): - """ - Writes a page of data to NVM (v3) - - By default the PAGE_WRITE command is used, which - requires that the page is already erased. - By default word access is used (flash) - - :param address: address to write to - :param data: data to write - :param use_word_access: write whole words? - :param nvmcommand: command to use for commit - """ - - # Check that NVM controller is ready - if not self.wait_nvm_ready(): - raise PymcuprogError("Timeout waiting for NVM controller to be ready before page buffer clear") - - # Clear the page buffer - self.logger.debug("Clear page buffer") - self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR) - - # Wait for NVM controller to be ready - if not self.wait_nvm_ready(): - raise PymcuprogError("Timeout waiting for NVM controller to be ready after page buffer clear") - - # Load the page buffer by writing directly to location - if use_word_access: - self.readwrite.write_data_words(address, data) - else: - self.readwrite.write_data(address, data) - - # Write the page to NVM, maybe erase first - self.logger.debug("Committing data") - self.execute_nvm_command(nvmcommand) - - # Wait for NVM controller to be ready again - if not self.wait_nvm_ready(): - raise PymcuprogError("Timeout waiting for NVM controller to be ready after page write") - - # 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"); - return -1; - } - pmsg_debug("clear page buffer\n"); - if (updi_nvm_command(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"); - return -1; - } - if (mode == USE_WORD_ACCESS) { - if (updi_write_data_words(pgm, address, buffer, size) < 0) { - pmsg_error("write data words operation failed\n"); - return -1; - } - } else { - if (updi_write_data(pgm, address, buffer, size) < 0) { - pmsg_error("write data operation failed\n"); - return -1; - } - } - pmsg_debug("committing data\n"); - if (nvm_command == USE_DEFAULT_COMMAND) { - nvm_command = UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE; - } - if (updi_nvm_command(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"); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - pmsg_error("sending empty command failed\n"); - return -1; - } - return 0; -} - int updi_nvm_chip_erase(const PROGRAMMER *pgm, const AVRPART *p) { switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_chip_erase_V0(pgm, p); + return updi_nvm_chip_erase_V0(pgm, p); case UPDI_NVM_MODE_V2: - return nvm_chip_erase_V2(pgm, p); + return updi_nvm_chip_erase_V2(pgm, p); case UPDI_NVM_MODE_V3: - return nvm_chip_erase_V3(pgm, p); + return updi_nvm_chip_erase_V3(pgm, p); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1076,11 +59,11 @@ int updi_nvm_erase_flash_page(const PROGRAMMER *pgm, const AVRPART *p, uint32_t switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_erase_flash_page_V0(pgm, p, address); + return updi_nvm_erase_flash_page_V0(pgm, p, address); case UPDI_NVM_MODE_V2: - return nvm_erase_flash_page_V2(pgm, p, address); + return updi_nvm_erase_flash_page_V2(pgm, p, address); case UPDI_NVM_MODE_V3: - return nvm_erase_flash_page_V3(pgm, p, address); + return updi_nvm_erase_flash_page_V3(pgm, p, address); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1091,11 +74,11 @@ int updi_nvm_erase_eeprom(const PROGRAMMER *pgm, const AVRPART *p) { switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_erase_eeprom_V0(pgm, p); + return updi_nvm_erase_eeprom_V0(pgm, p); case UPDI_NVM_MODE_V2: - return nvm_erase_eeprom_V2(pgm, p); + return updi_nvm_erase_eeprom_V2(pgm, p); case UPDI_NVM_MODE_V3: - return nvm_erase_eeprom_V3(pgm, p); + return updi_nvm_erase_eeprom_V3(pgm, p); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1106,11 +89,11 @@ int updi_nvm_erase_user_row(const PROGRAMMER *pgm, const AVRPART *p, uint32_t ad switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_erase_user_row_V0(pgm, p, address, size); + return updi_nvm_erase_user_row_V0(pgm, p, address, size); case UPDI_NVM_MODE_V2: - return nvm_erase_user_row_V2(pgm, p, address, size); + return updi_nvm_erase_user_row_V2(pgm, p, address, size); case UPDI_NVM_MODE_V3: - return nvm_erase_user_row_V3(pgm, p, address, size); + return updi_nvm_erase_user_row_V3(pgm, p, address, size); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1121,11 +104,11 @@ int updi_nvm_write_flash(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addre switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_write_flash_V0(pgm, p, address, buffer, size); + return updi_nvm_write_flash_V0(pgm, p, address, buffer, size); case UPDI_NVM_MODE_V2: - return nvm_write_flash_V2(pgm, p, address, buffer, size); + return updi_nvm_write_flash_V2(pgm, p, address, buffer, size); case UPDI_NVM_MODE_V3: - return nvm_write_flash_V3(pgm, p, address, buffer, size); + return updi_nvm_write_flash_V3(pgm, p, address, buffer, size); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1136,11 +119,11 @@ int updi_nvm_write_user_row(const PROGRAMMER *pgm, const AVRPART *p, uint32_t ad switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_write_user_row_V0(pgm, p, address, buffer, size); + return updi_nvm_write_user_row_V0(pgm, p, address, buffer, size); case UPDI_NVM_MODE_V2: - return nvm_write_user_row_V2(pgm, p, address, buffer, size); + return updi_nvm_write_user_row_V2(pgm, p, address, buffer, size); case UPDI_NVM_MODE_V3: - return nvm_write_user_row_V3(pgm, p, address, buffer, size); + return updi_nvm_write_user_row_V3(pgm, p, address, buffer, size); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1151,11 +134,11 @@ int updi_nvm_write_eeprom(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addr switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_write_eeprom_V0(pgm, p, address, buffer, size); + return updi_nvm_write_eeprom_V0(pgm, p, address, buffer, size); case UPDI_NVM_MODE_V2: - return nvm_write_eeprom_V2(pgm, p, address, buffer, size); + return updi_nvm_write_eeprom_V2(pgm, p, address, buffer, size); case UPDI_NVM_MODE_V3: - return nvm_write_eeprom_V3(pgm, p, address, buffer, size); + return updi_nvm_write_eeprom_V3(pgm, p, address, buffer, size); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1166,11 +149,11 @@ int updi_nvm_write_fuse(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres switch(updi_get_nvm_mode(pgm)) { case UPDI_NVM_MODE_V0: - return nvm_write_fuse_V0(pgm, p, address, value); + return updi_nvm_write_fuse_V0(pgm, p, address, value); case UPDI_NVM_MODE_V2: - return nvm_write_fuse_V2(pgm, p, address, value); + return updi_nvm_write_fuse_V2(pgm, p, address, value); case UPDI_NVM_MODE_V3: - return nvm_write_fuse_V3(pgm, p, address, value); + return updi_nvm_write_fuse_V3(pgm, p, address, value); default: pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); return -1; @@ -1178,61 +161,31 @@ int updi_nvm_write_fuse(const PROGRAMMER *pgm, const AVRPART *p, uint32_t addres } int updi_nvm_wait_ready(const PROGRAMMER *pgm, const AVRPART *p) { -/* - def wait_nvm_ready(self): - """ - Waits for the NVM controller to be ready - """ - timeout = Timeout(10000) # 10 sec timeout, just to be sure - - 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_NVM_STATUS_WRITE_ERROR): - self.logger.error("NVM error") - return False - - if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) | - (1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)): - return True - - self.logger.error("Wait NVM ready timed out") - return False -*/ - unsigned long start_time; - unsigned long current_time; - uint8_t status; - start_time = avr_ustimestamp(); - do { - if (updi_read_byte(pgm, p->nvm_base + UPDI_NVMCTRL_STATUS, &status) >= 0) { - if (status & (1 << UPDI_NVM_STATUS_WRITE_ERROR)) { - pmsg_error("unable to write NVM status\n"); - return -1; - } - if (!(status & ((1 << UPDI_NVM_STATUS_EEPROM_BUSY) | - (1 << UPDI_NVM_STATUS_FLASH_BUSY)))) { - return 0; - } - } - current_time = avr_ustimestamp(); - } while ((current_time - start_time) < 10000000); - - pmsg_error("wait NVM ready timed out\n"); - return -1; + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return updi_nvm_wait_ready_V0(pgm, p); + case UPDI_NVM_MODE_V2: + return updi_nvm_wait_ready_V2(pgm, p); + case UPDI_NVM_MODE_V3: + return updi_nvm_wait_ready_V3(pgm, p); + default: + pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); + return -1; + } } int updi_nvm_command(const PROGRAMMER *pgm, const AVRPART *p, uint8_t command) { -/* - def execute_nvm_command(self, command): - """ - Executes an NVM COMMAND on the NVM CTRL - - :param command: command to execute - """ - self.logger.debug("NVMCMD %d executing", command) - return self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_CTRLA, command) -*/ - pmsg_debug("NVMCMD %d executing\n", command); - - return updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_CTRLA, command); + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return updi_nvm_command_V0(pgm, p, command); + case UPDI_NVM_MODE_V2: + return updi_nvm_command_V2(pgm, p, command); + case UPDI_NVM_MODE_V3: + return updi_nvm_command_V3(pgm, p, command); + default: + pmsg_error("invalid NVM Mode %d\n", updi_get_nvm_mode(pgm)); + return -1; + } } diff --git a/src/updi_nvm_v0.c b/src/updi_nvm_v0.c new file mode 100644 index 00000000..4ec1d1fd --- /dev/null +++ b/src/updi_nvm_v0.c @@ -0,0 +1,501 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2024 Dawid Buchwald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* + * Based on pymcuprog + * See https://github.com/microchip-pic-avr-tools/pymcuprog + */ + +#include "ac_cfg.h" +#include +#include +#include +#include +#include +#include + +#include "avrdude.h" +#include "libavrdude.h" +#include "updi_nvm.h" +#include "updi_nvm_v0.h" +#include "updi_state.h" +#include "updi_constants.h" +#include "updi_readwrite.h" + +// NVMCTRL v0 REGISTERS +#define UPDI_V0_NVMCTRL_CTRLA 0x00 +#define UPDI_V0_NVMCTRL_CTRLB 0x01 +#define UPDI_V0_NVMCTRL_STATUS 0x02 +#define UPDI_V0_NVMCTRL_INTCTRL 0x03 +#define UPDI_V0_NVMCTRL_INTFLAGS 0x04 +#define UPDI_V0_NVMCTRL_DATAL 0x06 +#define UPDI_V0_NVMCTRL_DATAH 0x07 +#define UPDI_V0_NVMCTRL_ADDRL 0x08 +#define UPDI_V0_NVMCTRL_ADDRH 0x09 + +// NVMCTRL v0 CTRLA +#define UPDI_V0_NVMCTRL_CTRLA_NOP 0x00 +#define UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE 0x01 +#define UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE 0x02 +#define UPDI_V0_NVMCTRL_CTRLA_ERASE_WRITE_PAGE 0x03 +#define UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR 0x04 +#define UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE 0x05 +#define UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM 0x06 +#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 USE_DEFAULT_COMMAND 0xFF + +typedef enum +{ + DONT_USE_WORD_ACCESS, + USE_WORD_ACCESS +} access_mode; + + +int updi_nvm_chip_erase_V0(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def chip_erase(self): + """ + 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 + """ + self.logger.info("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") + + # Erase + self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE) + + # And wait for it + if not self.wait_nvm_ready(): + raise IOError("Timeout waiting for NVM controller to be ready after chip erase") + + 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"); + return -1; + } + if (updi_nvm_command(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"); + return -1; + } + return 0; +} + +int updi_nvm_erase_flash_page_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address) { +/* + def erase_flash_page(self, address): + """ + Erasing single flash page using the NVM controller (v0) + + :param address: Start address of page to erase + :type address: int + """ + self.logger.info("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") + + # Dummy write + self.readwrite.write_data(address, [0xFF]) + + # Erase + self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) + + # And wait for it + if not self.wait_nvm_ready(): + raise IOError("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"); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + pmsg_error("dummy write operation failed\n"); + return -1; + } + if (updi_nvm_command(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"); + return -1; + } + return 0; +} + +int updi_nvm_erase_eeprom_V0(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def erase_eeprom(self): + """ + Erase EEPROM memory only (v0) + """ + self.logger.info("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") + + # Erase + self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM) + + # And wait for it + if not self.wait_nvm_ready(): + 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"); + return -1; + } + if (updi_nvm_command(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"); + return -1; + } + return 0; +} + +int updi_nvm_erase_user_row_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size) { +/* + def erase_user_row(self, address, size): + """ + Erase User Row memory only (v0) + + :param address: Start address of user row + :type address: int + """ + self.logger.info("Erase user row") + + # 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 user row erase") + + # On this NVM version user row is implemented as EEPROM + # When erasing single EEPROM pages a dummy write is needed for each location to be erased + for offset in range(size): + self.readwrite.write_data(address+offset, [0xFF]) + + # Erase + self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) + + # And wait for it + if not self.wait_nvm_ready(): + raise IOError("Timeout waiting for NVM controller to be ready after user row erase") +*/ + 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"); + return -1; + } + data[0]=0xFF; + for (offset = 0; offset> 8) & 0xFF) + + # Write data + self.logger.debug("Load fuse data") + self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_DATAL, data[0] & 0xFF) + + # Execute + self.logger.debug("Execute fuse write") + self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE) + + 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"); + return -1; + } + pmsg_debug("load NVM address\n"); + if (updi_write_byte(pgm, p->nvm_base + UPDI_V0_NVMCTRL_ADDRL, address & 0xFF) < 0) { + pmsg_error("UPDI write ADDRL operation failed\n"); + return -1; + } + if (updi_write_byte(pgm, p->nvm_base + UPDI_V0_NVMCTRL_ADDRH, (address >> 8) & 0xFF) < 0) { + pmsg_error("write ADDRH operation failed\n"); + return -1; + } + pmsg_debug("load fuse data\n"); + if (updi_write_byte(pgm, p->nvm_base + UPDI_V0_NVMCTRL_DATAL, value & 0xFF) < 0) { + pmsg_error("write DATAL operation failed\n"); + return -1; + } + pmsg_debug("execute fuse write\n"); + if (updi_nvm_command(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"); + return -1; + } + return 0; +} + +static int nvm_write_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, + uint16_t size, access_mode mode, uint8_t nvm_command) +{ +/* + def write_nvm(self, address, data, use_word_access, nvmcommand=constants.UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE): + """ + Writes a page of data to NVM (v0) + + By default the PAGE_WRITE command is used, which + requires that the page is already erased. + By default word access is used (flash) + + :param address: address to write to + :param data: data to write + :param use_word_access: write whole words? + :param nvmcommand: command to use for commit + """ + + # Check that NVM controller is ready + if not self.wait_nvm_ready(): + raise PymcuprogError("Timeout waiting for NVM controller to be ready before page buffer clear") + + # Clear the page buffer + self.logger.debug("Clear page buffer") + self.execute_nvm_command(constants.UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) + + # Wait for NVM controller to be ready + if not self.wait_nvm_ready(): + raise PymcuprogError("Timeout waiting for NVM controller to be ready after page buffer clear") + + # Load the page buffer by writing directly to location + if use_word_access: + self.readwrite.write_data_words(address, data) + else: + self.readwrite.write_data(address, data) + + # Write the page to NVM, maybe erase first + self.logger.debug("Committing data") + self.execute_nvm_command(nvmcommand) + + # Wait for NVM controller to be ready again + 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"); + return -1; + } + pmsg_debug("clear page buffer\n"); + if (updi_nvm_command(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"); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + pmsg_error("write data words operation failed\n"); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + pmsg_error("write data operation failed\n"); + return -1; + } + } + pmsg_debug("committing data\n"); + if (nvm_command == USE_DEFAULT_COMMAND) { + nvm_command = UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE; + } + if (updi_nvm_command(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"); + return -1; + } + return 0; +} + +int updi_nvm_wait_ready_V0(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def wait_nvm_ready(self): + """ + Waits for the NVM controller to be ready + """ + timeout = Timeout(10000) # 10 sec timeout, just to be sure + + 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 + + if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) | + (1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)): + return True + + self.logger.error("Wait NVM ready timed out") + return False +*/ + unsigned long start_time; + unsigned long current_time; + uint8_t status; + 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)) { + 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)))) { + return 0; + } + } + current_time = avr_ustimestamp(); + } while ((current_time - start_time) < 10000000); + + pmsg_error("wait NVM ready timed out\n"); + return -1; +} + +int updi_nvm_command_V0(const PROGRAMMER *pgm, const AVRPART *p, uint8_t command) { +/* + def execute_nvm_command(self, command): + """ + Executes an NVM COMMAND on the NVM CTRL + + :param command: command to execute + """ + self.logger.debug("NVMCMD %d executing", command) + return self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_CTRLA, command) +*/ + pmsg_debug("NVMCMD %d executing\n", command); + + return updi_write_byte(pgm, p->nvm_base + UPDI_V0_NVMCTRL_CTRLA, command); +} diff --git a/src/updi_nvm_v0.h b/src/updi_nvm_v0.h new file mode 100644 index 00000000..8aed500c --- /dev/null +++ b/src/updi_nvm_v0.h @@ -0,0 +1,51 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2024 Dawid Buchwald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* + * Based on pymcuprog + * See https://github.com/microchip-pic-avr-tools/pymcuprog + */ + +#ifndef updi_nvm_v0_h +#define updi_nvm_v0_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_nvm_chip_erase_V0(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_erase_flash_page_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address); +int updi_nvm_erase_eeprom_V0(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_erase_user_row_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size); +int updi_nvm_write_flash_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_user_row_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_eeprom_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_fuse_V0(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint8_t value); +int updi_nvm_wait_ready_V0(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_command_V0(const PROGRAMMER *pgm, const AVRPART *p, uint8_t command); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_nvm_v0_h */ diff --git a/src/updi_nvm_v2.c b/src/updi_nvm_v2.c new file mode 100644 index 00000000..0d2b2897 --- /dev/null +++ b/src/updi_nvm_v2.c @@ -0,0 +1,459 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2024 Dawid Buchwald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* + * Based on pymcuprog + * See https://github.com/microchip-pic-avr-tools/pymcuprog + */ + +#include "ac_cfg.h" +#include +#include +#include +#include +#include +#include + +#include "avrdude.h" +#include "libavrdude.h" +#include "updi_nvm.h" +#include "updi_nvm_v2.h" +#include "updi_state.h" +#include "updi_constants.h" +#include "updi_readwrite.h" + +// NVMCTRL v2 REGISTERS +#define UPDI_V2_NVMCTRL_CTRLA 0x00 +#define UPDI_V2_NVMCTRL_CTRLB 0x01 +#define UPDI_V2_NVMCTRL_STATUS 0x02 +#define UPDI_V2_NVMCTRL_INTCTRL 0x03 +#define UPDI_V2_NVMCTRL_INTFLAGS 0x04 +#define UPDI_V2_NVMCTRL_DATAL 0x06 +#define UPDI_V2_NVMCTRL_DATAH 0x07 +#define UPDI_V2_NVMCTRL_ADDR0 0x08 +#define UPDI_V2_NVMCTRL_ADDR1 0x09 +#define UPDI_V2_NVMCTRL_ADDR2 0x0a +#define UPDI_V2_NVMCTRL_ADDR3 0x0b + +// NVMCTRL v2 CTRLA +#define UPDI_V2_NVMCTRL_CTRLA_NOCMD 0x00 +#define UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE 0x02 +#define UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE 0x08 +#define UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE 0x13 +#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 USE_DEFAULT_COMMAND 0xFF + +typedef enum +{ + DONT_USE_WORD_ACCESS, + USE_WORD_ACCESS +} access_mode; + +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 + """ + self.logger.info("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") + + # Erase + self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_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") + + 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"); + return -1; + } + if (updi_nvm_command(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"); + return -1; + } + return 0; +} + +int updi_nvm_erase_flash_page_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address) { +/* + def erase_flash_page(self, address): + """ + Erasing single flash page using the NVM controller (v1) + + :param address: Start address of page to erase + :type address: int + """ + self.logger.info("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") + + # Erase command + self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_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") + + # Remove command from NVM controller + self.logger.debug("Clear NVM command") + self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) +*/ + 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"); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + 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"); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready() failed\n"); + return -1; + } + return 0; +} + +int updi_nvm_erase_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def erase_eeprom(self): + """ + Erase EEPROM memory only (v1) + """ + self.logger.info("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") + + # Erase + self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_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") + + # Remove command from NVM controller + self.logger.debug("Clear NVM command") + self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) +*/ + pmsg_debug("erase EEPROM\n"); + if (updi_nvm_wait_ready(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready() failed\n"); + return -1; + } + if (updi_nvm_command(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"); + 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"); + return -1; + } + return 0; +} + +int updi_nvm_erase_user_row_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size) { +/* + def erase_user_row(self, address, size): + """ + Erase User Row memory only (v1) + + :param address: Start address of user row + :type address: int + """ + # size is not used for this NVM version + _dummy = size + # On this NVM version user row is implemented as flash + return self.erase_flash_page(address) +*/ + return updi_nvm_erase_flash_page_V2(pgm, p, address); +} + +static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, + uint16_t size, access_mode mode); + +int updi_nvm_write_flash_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { +/* + def write_flash(self, address, data): + """ + Writes data to flash (v1) + + :param address: address to write to + :param data: data to write + """ + return self.write_nvm(address, data, use_word_access=True) +*/ + return nvm_write_V2(pgm, p, address, buffer, size, USE_WORD_ACCESS); +} + +int updi_nvm_write_user_row_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { +/* + def write_user_row(self, address, data): + """ + Writes data to user row (v1) + + :param address: address to write to + :param data: data to write + """ + # On this NVM variant user row is implemented as Flash + return self.write_nvm(address, data, use_word_access=False) +*/ + return nvm_write_V2(pgm, p, address, buffer, size, DONT_USE_WORD_ACCESS); +} + +int updi_nvm_write_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { +/* + def write_eeprom(self, address, data): + """ + Writes data to NVM (EEPROM) + + :param address: address to write to + :param data: data to write + """ + nvm_command = constants.UPDI_V2_NVMCTRL_CTRLA_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") + + # Write the command to the NVM controller + self.logger.info("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") + + # Remove command from NVM controller + self.logger.info("Clear NVM command") + self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) +*/ + if (updi_nvm_wait_ready(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready() 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) { + pmsg_error("EEPROM erase command failed\n"); + return -1; + } + if (updi_write_data(pgm, address, buffer, size) < 0) { + 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"); + 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"); + return -1; + } + return 0; +} + +int updi_nvm_write_fuse_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint8_t value) { +/* + def write_fuse(self, address, data): + """ + Writes one fuse value + V1 fuses are EEPROM-based + + :param address: address to write to + :param data: data to write + """ + return self.write_eeprom(address, data) +*/ + unsigned char buffer[1]; + buffer[0]=value; + return updi_nvm_write_eeprom_V2(pgm, p, address, buffer, 1); +} + +static int nvm_write_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, + uint16_t size, access_mode mode) +{ +/* + def write_nvm(self, address, data, use_word_access): + """ + Writes data to NVM (version 1) + This version of the NVM block has no page buffer, so words are written directly. + + :param address: address to write to + :param data: data to write + :param use_word_access: write in whole words? + """ + nvm_command = constants.UPDI_V2_NVMCTRL_CTRLA_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") + + # Write the command to the NVM controller + self.logger.info("NVM write command") + self.execute_nvm_command(nvm_command) + + # Write the data + if use_word_access: + self.readwrite.write_data_words(address, data) + else: + 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") + + # Remove command from NVM controller + self.logger.info("Clear NVM command") + self.execute_nvm_command(constants.UPDI_V2_NVMCTRL_CTRLA_NOCMD) +*/ + if (updi_nvm_wait_ready(pgm, p) < 0) { + pmsg_error("updi_nvm_wait_ready() failed\n"); + return -1; + } + pmsg_debug("NVM write command\n"); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE) < 0) { + pmsg_error("clear page operation failed\n"); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + pmsg_error("write data words operation failed\n"); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + pmsg_error("write data operation failed\n"); + return -1; + } + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + pmsg_error("wait for ready chip 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"); + return -1; + } + return 0; +} + +int updi_nvm_wait_ready_V2(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def wait_nvm_ready(self): + """ + Waits for the NVM controller to be ready + """ + timeout = Timeout(10000) # 10 sec timeout, just to be sure + + 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 + + if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) | + (1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)): + return True + + self.logger.error("Wait NVM ready timed out") + return False +*/ + unsigned long start_time; + unsigned long current_time; + uint8_t status; + 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"); + return -1; + } + if (!(status & ((1 << UPDI_V2_NVM_STATUS_EEPROM_BUSY) | + (1 << UPDI_V2_NVM_STATUS_FLASH_BUSY)))) { + return 0; + } + } + current_time = avr_ustimestamp(); + } while ((current_time - start_time) < 10000000); + + pmsg_error("wait NVM ready timed out\n"); + return -1; +} + +int updi_nvm_command_V2(const PROGRAMMER *pgm, const AVRPART *p, uint8_t command) { +/* + def execute_nvm_command(self, command): + """ + Executes an NVM COMMAND on the NVM CTRL + + :param command: command to execute + """ + self.logger.debug("NVMCMD %d executing", command) + return self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_CTRLA, command) +*/ + pmsg_debug("NVMCMD %d executing\n", command); + + return updi_write_byte(pgm, p->nvm_base + UPDI_V2_NVMCTRL_CTRLA, command); +} diff --git a/src/updi_nvm_v2.h b/src/updi_nvm_v2.h new file mode 100644 index 00000000..89248ba4 --- /dev/null +++ b/src/updi_nvm_v2.h @@ -0,0 +1,51 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2024 Dawid Buchwald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* + * Based on pymcuprog + * See https://github.com/microchip-pic-avr-tools/pymcuprog + */ + +#ifndef updi_nvm_v2_h +#define updi_nvm_v2_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_nvm_chip_erase_V2(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_erase_flash_page_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address); +int updi_nvm_erase_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_erase_user_row_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size); +int updi_nvm_write_flash_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_user_row_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_eeprom_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_fuse_V2(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint8_t value); +int updi_nvm_wait_ready_V2(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_command_V2(const PROGRAMMER *pgm, const AVRPART *p, uint8_t command); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_nvm_v2_h */ diff --git a/src/updi_nvm_v3.c b/src/updi_nvm_v3.c new file mode 100644 index 00000000..e11051fc --- /dev/null +++ b/src/updi_nvm_v3.c @@ -0,0 +1,459 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2024 Dawid Buchwald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* + * Based on pymcuprog + * See https://github.com/microchip-pic-avr-tools/pymcuprog + */ + +#include "ac_cfg.h" +#include +#include +#include +#include +#include +#include + +#include "avrdude.h" +#include "libavrdude.h" +#include "updi_nvm.h" +#include "updi_nvm_v3.h" +#include "updi_state.h" +#include "updi_constants.h" +#include "updi_readwrite.h" + +// NVMCTRL v3 REGISTERS +#define UPDI_V3_NVMCTRL_CTRLA 0x00 +#define UPDI_V3_NVMCTRL_CTRLB 0x01 +#define UPDI_V3_NVMCTRL_INTCTRL 0x04 +#define UPDI_V3_NVMCTRL_INTFLAGS 0x05 +#define UPDI_V3_NVMCTRL_STATUS 0x06 +#define UPDI_V3_NVMCTRL_DATAL 0x08 +#define UPDI_V3_NVMCTRL_DATAH 0x09 +#define UPDI_V3_NVMCTRL_ADDR0 0x0c +#define UPDI_V3_NVMCTRL_ADDR1 0x0d +#define UPDI_V3_NVMCTRL_ADDR2 0x0e +#define UPDI_V3_NVMCTRL_ADDR3 0x0f + +// NVMCTRL v3 CTRLA +#define UPDI_V3_NVMCTRL_CTRLA_NOCMD 0x00 +#define UPDI_V3_NVMCTRL_CTRLA_NOP 0x01 +#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE 0x04 +#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE_WRITE 0x05 +#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE 0x08 +#define UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR 0x0F +#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_WRITE 0x14 +#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE_WRITE 0x15 +#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE 0x17 +#define UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_BUFFER_CLEAR 0x1F +#define UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE 0x20 +#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 USE_DEFAULT_COMMAND 0xFF + +typedef enum +{ + DONT_USE_WORD_ACCESS, + USE_WORD_ACCESS +} access_mode; + +int updi_nvm_chip_erase_V3(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def chip_erase(self): + """ + 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 + """ + self.logger.info("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") + + # Erase + self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) + + # And wait for it + status = self.wait_nvm_ready() + + # Remove command + self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) + + if not status: + raise IOError("Timeout waiting for NVM controller to be ready after chip erase") + + 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"); + return -1; + } + if (updi_nvm_command(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"); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("sending empty command failed\n"); + return -1; + } + return 0; +} + +int updi_nvm_erase_flash_page_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address) { +/* + def erase_flash_page(self, address): + """ + Erasing single flash page using the NVM controller (v3) + + :param address: Start address of page to erase + :type address: int + """ + self.logger.info("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") + + # Dummy write + self.readwrite.write_data(address, [0xFF]) + + # Erase + self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) + + # And wait for it + status = self.wait_nvm_ready() + + # Remove command + self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) + + if not status: + raise IOError("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"); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + pmsg_error("dummy write operation failed\n"); + return -1; + } + if (updi_nvm_command(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"); + return -1; + } + return 0; +} + +int updi_nvm_erase_eeprom_V3(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def erase_eeprom(self): + """ + Erase EEPROM memory only + """ + self.logger.info("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") + + # Erase + self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE) + + # And wait for it + status = self.wait_nvm_ready() + + # Remove command + self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_NOCMD) + + if not status: + 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"); + return -1; + } + if (updi_nvm_command(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"); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("sending empty command failed\n"); + return -1; + } + return 0; +} + +int updi_nvm_erase_user_row_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size) { +/* + def erase_user_row(self, address, size): + """ + Erase User Row memory only + + :param address: Start address of user row + :type address: int + """ + self.logger.info("Erase user row") + + # On this NVM version user row is implemented as FLASH + return self.erase_flash_page(self, address) +*/ + pmsg_debug("erase user row at address 0x%06X\n", address); + + return updi_nvm_erase_flash_page_V3(pgm, p, address); +} + +static int nvm_write_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, + uint16_t size, access_mode mode, uint8_t nvm_command); + +int updi_nvm_write_flash_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { +/* + def write_flash(self, address, data): + """ + Writes data to flash (v3) + + :param address: address to write to + :param data: data to write + """ + return self.write_nvm(address, data, use_word_access=True) +*/ + return nvm_write_V3(pgm, p, address, buffer, size, USE_WORD_ACCESS, USE_DEFAULT_COMMAND); +} + +int updi_nvm_write_user_row_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { +/* + def write_user_row(self, address, data): + """ + Writes data to user row (v3) + + :param address: address to write to + :param data: data to write + """ + # On this NVM variant user row is implemented as FLASH + return self.write_nvm(address, data, use_word_access=True) +*/ + return nvm_write_V3(pgm, p, address, buffer, size, USE_WORD_ACCESS, USE_DEFAULT_COMMAND); +} + +int updi_nvm_write_eeprom_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size) { +/* + def write_eeprom(self, address, data): + """ + Write data to EEPROM (v3) + + :param address: address to write to + :param data: data to write + """ + return self.write_nvm(address, data, use_word_access=False, + nvmcommand=constants.UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE_WRITE) +*/ + return nvm_write_V3(pgm, p, address, buffer, size, DONT_USE_WORD_ACCESS, UPDI_V3_NVMCTRL_CTRLA_EEPROM_PAGE_ERASE_WRITE); +} + +int updi_nvm_write_fuse_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint8_t value) { +/* + def write_fuse(self, address, data): + """ + Writes one fuse value (v3) + + :param address: address to write to + :param data: data to write + """ + return self.write_eeprom(address, data) +*/ + unsigned char buffer[1]; + buffer[0] = value; + return updi_nvm_write_eeprom_V3(pgm, p, address, buffer, 1); +} + +static int nvm_write_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, + uint16_t size, access_mode mode, uint8_t nvm_command) +{ +/* + def write_nvm(self, address, data, use_word_access, nvmcommand=constants.UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE): + """ + Writes a page of data to NVM (v3) + + By default the PAGE_WRITE command is used, which + requires that the page is already erased. + By default word access is used (flash) + + :param address: address to write to + :param data: data to write + :param use_word_access: write whole words? + :param nvmcommand: command to use for commit + """ + + # Check that NVM controller is ready + if not self.wait_nvm_ready(): + raise PymcuprogError("Timeout waiting for NVM controller to be ready before page buffer clear") + + # Clear the page buffer + self.logger.debug("Clear page buffer") + self.execute_nvm_command(constants.UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR) + + # Wait for NVM controller to be ready + if not self.wait_nvm_ready(): + raise PymcuprogError("Timeout waiting for NVM controller to be ready after page buffer clear") + + # Load the page buffer by writing directly to location + if use_word_access: + self.readwrite.write_data_words(address, data) + else: + self.readwrite.write_data(address, data) + + # Write the page to NVM, maybe erase first + self.logger.debug("Committing data") + self.execute_nvm_command(nvmcommand) + + # Wait for NVM controller to be ready again + if not self.wait_nvm_ready(): + raise PymcuprogError("Timeout waiting for NVM controller to be ready after page write") + + # 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"); + return -1; + } + pmsg_debug("clear page buffer\n"); + if (updi_nvm_command(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"); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + pmsg_error("write data words operation failed\n"); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + pmsg_error("write data operation failed\n"); + return -1; + } + } + pmsg_debug("committing data\n"); + if (nvm_command == USE_DEFAULT_COMMAND) { + nvm_command = UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE; + } + if (updi_nvm_command(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"); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + pmsg_error("sending empty command failed\n"); + return -1; + } + return 0; +} + + +int updi_nvm_wait_ready_V3(const PROGRAMMER *pgm, const AVRPART *p) { +/* + def wait_nvm_ready(self): + """ + Waits for the NVM controller to be ready + """ + timeout = Timeout(10000) # 10 sec timeout, just to be sure + + 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 + + if not status & ((1 << constants.UPDI_NVM_STATUS_EEPROM_BUSY) | + (1 << constants.UPDI_NVM_STATUS_FLASH_BUSY)): + return True + + self.logger.error("Wait NVM ready timed out") + return False +*/ + unsigned long start_time; + unsigned long current_time; + uint8_t status; + 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"); + return -1; + } + if (!(status & ((1 << UPDI_V3_NVM_STATUS_EEPROM_BUSY) | + (1 << UPDI_V3_NVM_STATUS_FLASH_BUSY)))) { + return 0; + } + } + current_time = avr_ustimestamp(); + } while ((current_time - start_time) < 10000000); + + pmsg_error("wait NVM ready timed out\n"); + return -1; +} + +int updi_nvm_command_V3(const PROGRAMMER *pgm, const AVRPART *p, uint8_t command) { +/* + def execute_nvm_command(self, command): + """ + Executes an NVM COMMAND on the NVM CTRL + + :param command: command to execute + """ + self.logger.debug("NVMCMD %d executing", command) + return self.readwrite.write_byte(self.device.nvmctrl_address + constants.UPDI_NVMCTRL_CTRLA, command) +*/ + pmsg_debug("NVMCMD %d executing\n", command); + + return updi_write_byte(pgm, p->nvm_base + UPDI_V3_NVMCTRL_CTRLA, command); +} diff --git a/src/updi_nvm_v3.h b/src/updi_nvm_v3.h new file mode 100644 index 00000000..49461d2d --- /dev/null +++ b/src/updi_nvm_v3.h @@ -0,0 +1,51 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2024 Dawid Buchwald + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* $Id$ */ + +/* + * Based on pymcuprog + * See https://github.com/microchip-pic-avr-tools/pymcuprog + */ + +#ifndef updi_nvm_v3_h +#define updi_nvm_v3_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_nvm_chip_erase_V3(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_erase_flash_page_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address); +int updi_nvm_erase_eeprom_V3(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_erase_user_row_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint16_t size); +int updi_nvm_write_flash_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_user_row_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_eeprom_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, unsigned char *buffer, uint16_t size); +int updi_nvm_write_fuse_V3(const PROGRAMMER *pgm, const AVRPART *p, uint32_t address, uint8_t value); +int updi_nvm_wait_ready_V3(const PROGRAMMER *pgm, const AVRPART *p); +int updi_nvm_command_V3(const PROGRAMMER *pgm, const AVRPART *p, uint8_t command); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_nvm_v3_h */