Updated NVM controller code to latest pymcuprog

This commit is contained in:
Dawid Buchwald
2024-01-11 20:56:13 +01:00
parent 2358893974
commit c33ec9c51b
3 changed files with 244 additions and 190 deletions

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}