First successful programming

git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1516 81a1dc3b-b13d-400b-aceb-764788c761c2
This commit is contained in:
Dawid Buchwald 2021-12-11 22:22:38 +00:00 committed by Marius Greuel
parent 8f67f9c50b
commit c6902553be
2 changed files with 392 additions and 29 deletions

View File

@ -40,6 +40,11 @@
#include "updi_link.h"
#include "updi_state.h"
#include "updi_readwrite.h"
#include "updi_nvm.h"
#include "updi_constants.h"
static int serialupdi_enter_progmode(PROGRAMMER * pgm);
static int serialupdi_leave_progmode(PROGRAMMER * pgm);
static void serialupdi_setup(PROGRAMMER * pgm)
{
@ -64,7 +69,7 @@ static int serialupdi_open(PROGRAMMER * pgm, char * port)
return updi_link_open(pgm);
}
static int serialupdi_decode_sib(updi_sib_info * sib_info)
static int serialupdi_decode_sib(PROGRAMMER * pgm, updi_sib_info * sib_info)
{
char * str_ptr;
@ -105,12 +110,18 @@ static int serialupdi_decode_sib(updi_sib_info * sib_info)
switch (sib_info->nvm_version) {
case '0':
avrdude_message(MSG_INFO, "%s: NVM type 0: 16-bit, page oriented write\n", progname);
updi_set_nvm_mode(pgm, UPDI_NVM_MODE_V0);
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT);
break;
case '2':
avrdude_message(MSG_INFO, "%s: NVM type 2: 24-bit, word oriented write\n", progname);
updi_set_nvm_mode(pgm, UPDI_NVM_MODE_V2);
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_24BIT);
break;
case '3':
avrdude_message(MSG_INFO, "%s: NVM type 3: 16-bit, page oriented\n", progname);
updi_set_nvm_mode(pgm, UPDI_NVM_MODE_V3);
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT);
break;
default:
avrdude_message(MSG_INFO, "%s: Unsupported NVM type: %c, please update software\n", progname, sib_info->nvm_version);
@ -121,9 +132,240 @@ static int serialupdi_decode_sib(updi_sib_info * sib_info)
static void serialupdi_close(PROGRAMMER * pgm)
{
if (serialupdi_leave_progmode(pgm) < 0) {
avrdude_message(MSG_INFO, "%s: Unable to leave NVM programming mode\n", progname);
}
updi_link_close(pgm);
}
typedef enum {
APPLY_RESET,
RELEASE_RESET
} reset_mode;
static int serialupdi_reset(PROGRAMMER * pgm, reset_mode mode)
{
/*
def reset(self, apply_reset):
"""
Applies or releases an UPDI reset condition
:param apply_reset: True to apply, False to release
"""
if apply_reset:
self.logger.info("Apply reset")
self.readwrite.write_cs(constants.UPDI_ASI_RESET_REQ, constants.UPDI_RESET_REQ_VALUE)
else:
self.logger.info("Release reset")
self.readwrite.write_cs(constants.UPDI_ASI_RESET_REQ, 0x00)
*/
switch (mode) {
case APPLY_RESET:
avrdude_message(MSG_DEBUG, "%s: Sending reset request\n", progname);
return updi_write_cs(pgm, UPDI_ASI_RESET_REQ, UPDI_RESET_REQ_VALUE);
case RELEASE_RESET:
avrdude_message(MSG_DEBUG, "%s: Sending release reset request\n", progname);
return updi_write_cs(pgm, UPDI_ASI_RESET_REQ, 0x00);
}
return -1;
}
static int serialupdi_wait_for_unlock(PROGRAMMER * pgm, unsigned int ms) {
/*
def wait_unlocked(self, timeout_ms):
"""
Waits for the device to be unlocked.
All devices boot up as locked until proven otherwise
:param timeout_ms: number of milliseconds to wait
"""
timeout = Timeout(timeout_ms)
while not timeout.expired():
if not self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS) & (
1 << constants.UPDI_ASI_SYS_STATUS_LOCKSTATUS):
return True
self.logger.error("Timeout waiting for device to unlock")
return False
*/
unsigned long start_time;
unsigned long current_time;
struct timeval tv;
uint8_t status;
gettimeofday (&tv, NULL);
start_time = (tv.tv_sec * 1000000) + tv.tv_usec;
do {
if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &status) >= 0) {
if (!(status & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS))) {
return 0;
}
}
gettimeofday (&tv, NULL);
current_time = (tv.tv_sec * 1000000) + tv.tv_usec;
} while ((current_time - start_time) < (ms * 1000));
avrdude_message(MSG_INFO, "%s: Timeout waiting for device to unlock\n", progname);
return -1;
}
static int serialupdi_in_prog_mode(PROGRAMMER * pgm, uint8_t * in_prog_mode)
{
/*
def in_prog_mode(self):
"""
Checks whether the NVM PROG flag is up
"""
if self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS) & (1 << constants.UPDI_ASI_SYS_STATUS_NVMPROG):
return True
return False
*/
uint8_t value;
int rc;
rc = updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: Read CS operation failed\n", progname);
return rc;
}
if (value & (1 << UPDI_ASI_SYS_STATUS_NVMPROG)) {
*in_prog_mode = 1;
} else {
*in_prog_mode = 0;
}
return 0;
}
static int serialupdi_enter_progmode(PROGRAMMER * pgm)
{
/*
def enter_progmode(self):
"""
Enters into NVM programming mode
"""
# First check if NVM is already enabled
if self.in_prog_mode():
self.logger.info("Already in NVM programming mode")
return True
self.logger.info("Entering NVM programming mode")
# Put in the key
self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_NVM)
# Check key status
key_status = self.readwrite.read_cs(constants.UPDI_ASI_KEY_STATUS)
self.logger.debug("Key status = 0x%02X", key_status)
if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_NVMPROG):
self.logger.error("Key status = 0x%02X", key_status)
raise IOError("Key not accepted")
# Toggle reset
self.reset(apply_reset=True)
self.reset(apply_reset=False)
# And wait for unlock
if not self.wait_unlocked(100):
raise IOError("Failed to enter NVM programming mode: device is locked")
# Check for NVMPROG flag
if not self.in_prog_mode():
raise IOError("Failed to enter NVM programming mode")
self.logger.debug("Now in NVM programming mode")
return True
*/
uint8_t in_prog_mode;
unsigned char buffer[8];
uint8_t key_status;
if (serialupdi_in_prog_mode(pgm, &in_prog_mode) < 0) {
avrdude_message(MSG_INFO, "%s: Checking UPDI NVM prog mode failed\n", progname);
return -1;
}
if (in_prog_mode) {
avrdude_message(MSG_DEBUG, "%s: Already in prog mode\n", progname);
return 0;
}
avrdude_message(MSG_INFO, "%s: Entering NVM programming mode\n", progname);
memcpy(buffer, UPDI_KEY_NVM, sizeof(buffer));
if (updi_write_key(pgm, buffer, UPDI_KEY_64, sizeof(buffer)) < 0) {
avrdude_message(MSG_INFO, "%s: Writing NVM KEY failed\n", progname);
return -1;
}
if (updi_read_cs(pgm, UPDI_ASI_KEY_STATUS, &key_status) < 0) {
avrdude_message(MSG_INFO, "%s: Checking KEY status failed\n", progname);
return -1;
}
avrdude_message(MSG_DEBUG, "%s: Key status: 0x%02X\n", progname, key_status);
if (!(key_status & (1 << UPDI_ASI_KEY_STATUS_NVMPROG))) {
avrdude_message(MSG_INFO, "%s: Key was not accepted\n", progname);
return -1;
}
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
return -1;
}
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
return -1;
}
if (serialupdi_wait_for_unlock(pgm, 100) < 0) {
avrdude_message(MSG_INFO, "%s: Failed to enter NVM programming mode: device is locked\n", progname);
return -1;
}
if (serialupdi_in_prog_mode(pgm, &in_prog_mode) < 0) {
avrdude_message(MSG_INFO, "%s: Checking UPDI NVM prog mode failed\n", progname);
return -1;
}
if (!in_prog_mode) {
avrdude_message(MSG_INFO, "%s: Failed to enter NVM programming mode\n", progname);
return -1;
}
avrdude_message(MSG_DEBUG, "%s: Entered NVM programming mode\n", progname);
return 0;
}
static int serialupdi_leave_progmode(PROGRAMMER * pgm)
{
/*
def leave_progmode(self):
"""
Disables UPDI which releases any keys enabled
"""
self.logger.info("Leaving NVM programming mode")
self.reset(apply_reset=True)
self.reset(apply_reset=False)
self.readwrite.write_cs(constants.UPDI_CS_CTRLB,
(1 << constants.UPDI_CTRLB_UPDIDIS_BIT) | (1 << constants.UPDI_CTRLB_CCDETDIS_BIT))
*/
avrdude_message(MSG_INFO, "%s: Leaving NVM programming mode\n", progname);
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
return -1;
}
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
return -1;
}
return updi_write_cs(pgm, UPDI_CS_CTRLB, (1 << UPDI_CTRLB_UPDIDIS_BIT) | (1 << UPDI_CTRLB_CCDETDIS_BIT));
}
static int serialupdi_initialize(PROGRAMMER * pgm, AVRPART * p)
{
updi_sib_info * sib_info = updi_get_sib_info(pgm);
@ -137,17 +379,15 @@ static int serialupdi_initialize(PROGRAMMER * pgm, AVRPART * p)
avrdude_message(MSG_INFO, "%s: Read SIB operation failed\n", progname);
return -1;
}
if (serialupdi_decode_sib(sib_info) < 0) {
if (serialupdi_decode_sib(pgm, sib_info) < 0) {
avrdude_message(MSG_INFO, "%s: Decode SIB_INFO failed\n", progname);
return -1;
}
updi_set_nvm_mode(pgm, sib_info->nvm_version);
if (sib_info->nvm_version == '2') {
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_24BIT);
} else {
updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT);
if (serialupdi_enter_progmode(pgm) < 0) {
avrdude_message(MSG_INFO, "%s: Unable to enter NVM programming mode\n", progname);
return -1;
}
return 0;
}
@ -185,19 +425,19 @@ static int serialupdi_program_enable(PROGRAMMER * pgm, AVRPART * p)
return -1;
}
static int serialupdi_chip_erase(PROGRAMMER * pgm, AVRPART * p)
{
avrdude_message(MSG_INFO, "%s: error: chip erase not implemented yet\n",
progname);
return -1;
}
static int serialupdi_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
unsigned long addr, unsigned char * value)
{
return updi_read_byte(pgm, mem->offset + addr, value);
}
static int serialupdi_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem,
unsigned long addr, unsigned char value)
{
return updi_write_byte(pgm, mem->offset + addr, value);
}
static int serialupdi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
unsigned int page_size,
unsigned int addr, unsigned int n_bytes)
@ -208,7 +448,8 @@ static int serialupdi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
int read_bytes = 0;
int rc;
while (remaining_bytes > 0) {
rc = updi_read_data(pgm, m->offset + read_offset, m->buf + read_offset, m->readsize);
rc = updi_read_data(pgm, m->offset + read_offset, m->buf + read_offset,
remaining_bytes > m->readsize ? m->readsize : remaining_bytes);
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: Paged load operation failed\n", progname);
return rc;
@ -228,11 +469,133 @@ static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
unsigned int page_size,
unsigned int addr, unsigned int n_bytes)
{
avrdude_message(MSG_INFO, "%s: error: paged write not implemented yet\n",
int rc;
if (serialupdi_enter_progmode(pgm) < 0) {
avrdude_message(MSG_INFO, "%s: Unable to enter NVM programming mode\n", progname);
return -1;
}
if (n_bytes > m->page_size) {
unsigned int write_offset = addr;
unsigned int remaining_bytes = n_bytes;
int write_bytes = 0;
while (remaining_bytes > 0) {
if (strcmp(m->desc, "eeprom")==0) {
rc = updi_nvm_write_eeprom(pgm, p, m->offset + write_offset, m->buf + write_offset,
remaining_bytes > m->page_size ? m->page_size : remaining_bytes);
} else if (strcmp(m->desc, "flash")==0) {
rc = updi_nvm_write_flash(pgm, p, m->offset + write_offset, m->buf + write_offset,
remaining_bytes > m->page_size ? m->page_size : remaining_bytes);
} else {
rc = -1;
}
if (rc < 0) {
avrdude_message(MSG_INFO, "%s: Paged write operation failed\n", progname);
return rc;
} else {
write_bytes+=rc;
write_offset+=m->page_size;
remaining_bytes-=m->page_size;
}
}
return write_bytes;
} else {
if (strcmp(m->desc, "eeprom")==0) {
rc = updi_nvm_write_eeprom(pgm, p, m->offset+addr, m->buf+addr, n_bytes);
} else if (strcmp(m->desc, "flash")==0) {
rc = updi_nvm_write_flash(pgm, p, m->offset+addr, m->buf+addr, n_bytes);
} else {
rc = -1;
}
return rc;
}
}
static int serialupdi_unlock(PROGRAMMER * pgm, AVRPART * p)
{
/*
def unlock(self):
"""
Unlock by chip erase
"""
# Put in the key
self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_CHIPERASE)
# Check key status
key_status = self.readwrite.read_cs(constants.UPDI_ASI_KEY_STATUS)
self.logger.debug("Key status = 0x%02X", key_status)
if not key_status & (1 << constants.UPDI_ASI_KEY_STATUS_CHIPERASE):
raise PymcuprogError("Key not accepted")
# Toggle reset
self.reset(apply_reset=True)
self.reset(apply_reset=False)
# And wait for unlock
if not self.wait_unlocked(500):
raise PymcuprogError("Failed to chip erase using key")
*/
unsigned char buffer[8];
uint8_t key_status;
memcpy(buffer, UPDI_KEY_CHIPERASE, sizeof(buffer));
if (updi_write_key(pgm, buffer, UPDI_KEY_64, sizeof(buffer)) < 0) {
avrdude_message(MSG_INFO, "%s: Writing NVM KEY failed\n", progname);
return -1;
}
if (updi_read_cs(pgm, UPDI_ASI_KEY_STATUS, &key_status) < 0) {
avrdude_message(MSG_INFO, "%s: Checking KEY status failed\n", progname);
return -1;
}
avrdude_message(MSG_DEBUG, "%s: Key status: 0x%02X\n", progname, key_status);
if (!(key_status & (1 << UPDI_ASI_KEY_STATUS_CHIPERASE))) {
avrdude_message(MSG_INFO, "%s: Key not accepted\n", progname);
return -1;
}
if (serialupdi_reset(pgm, APPLY_RESET) < 0) {
avrdude_message(MSG_INFO, "%s: Apply reset operation failed\n", progname);
return -1;
}
if (serialupdi_reset(pgm, RELEASE_RESET) < 0) {
avrdude_message(MSG_INFO, "%s: Release reset operation failed\n", progname);
return -1;
}
return serialupdi_wait_for_unlock(pgm, 500);
}
static int serialupdi_chip_erase(PROGRAMMER * pgm, AVRPART * p)
{
if (serialupdi_enter_progmode(pgm) < 0) {
avrdude_message(MSG_INFO, "%s: Unable to enter NVM programming mode\n", progname);
return -1;
}
if (updi_nvm_chip_erase(pgm, p) < 0) {
avrdude_message(MSG_INFO, "%s: Chip erase failed, device might be locked, attempting unlock now\n", progname);
return serialupdi_unlock(pgm, p);
}
return 0;
}
static int serialupdi_page_erase(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m,
unsigned int baseaddr)
{
avrdude_message(MSG_INFO, "%s: error: page erase not implemented yet\n",
progname);
return -1;
}
void serialupdi_initpgm(PROGRAMMER * pgm)
{
strcpy(pgm->type, "serialupdi");
@ -251,14 +614,16 @@ void serialupdi_initpgm(PROGRAMMER * pgm)
pgm->open = serialupdi_open;
pgm->close = serialupdi_close;
pgm->read_byte = serialupdi_read_byte;
pgm->write_byte = avr_write_byte_default;
pgm->write_byte = serialupdi_write_byte;
/*
* optional functions
*/
pgm->unlock = serialupdi_unlock;
pgm->paged_write = serialupdi_paged_write;
pgm->paged_load = serialupdi_paged_load;
pgm->page_erase = serialupdi_page_erase;
pgm->setup = serialupdi_setup;
pgm->teardown = serialupdi_teardown;

View File

@ -1239,17 +1239,15 @@ int updi_nvm_wait_ready(PROGRAMMER * pgm, AVRPART *p)
gettimeofday (&tv, NULL);
start_time = (tv.tv_sec * 1000000) + tv.tv_usec;
do {
if (updi_read_byte(pgm, p->nvm_base + UPDI_NVMCTRL_STATUS, &status) < 0){
avrdude_message(MSG_INFO, "%s: Status read operation failed\n", progname);
return -1;
}
if (status & (1 << UPDI_NVM_STATUS_WRITE_ERROR)) {
avrdude_message(MSG_INFO, "%s: NVM error\n", progname);
return -1;
}
if (!(status & ((1 << UPDI_NVM_STATUS_EEPROM_BUSY) |
(1 << UPDI_NVM_STATUS_FLASH_BUSY)))) {
return 0;
if (updi_read_byte(pgm, p->nvm_base + UPDI_NVMCTRL_STATUS, &status) >= 0) {
if (status & (1 << UPDI_NVM_STATUS_WRITE_ERROR)) {
avrdude_message(MSG_INFO, "%s: NVM error\n", progname);
return -1;
}
if (!(status & ((1 << UPDI_NVM_STATUS_EEPROM_BUSY) |
(1 << UPDI_NVM_STATUS_FLASH_BUSY)))) {
return 0;
}
}
gettimeofday (&tv, NULL);
current_time = (tv.tv_sec * 1000000) + tv.tv_usec;