From b6e72dce4cc3604e38ff6469f4b48f1872cddbb3 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Wed, 8 Dec 2021 10:09:52 +0000 Subject: [PATCH 01/12] Implemented basic serial code refactoring for upcoming SerialUPDI implementation git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serial_refactoring@1511 81a1dc3b-b13d-400b-aceb-764788c761c2 --- arduino.c | 3 ++- avr910.c | 3 ++- buspirate.c | 3 ++- butterfly.c | 3 ++- jtagmkI.c | 9 +++++---- jtagmkII.c | 23 +++++++++++++++-------- libavrdude.h | 31 ++++++++++++++++++++++++++++--- ser_posix.c | 28 +++++++++++++++++++++------- ser_win32.c | 44 ++++++++++++++++++++++++++++++++++++++------ stk500.c | 3 ++- stk500v2.c | 17 ++++++++++------- wiring.c | 3 ++- xbee.c | 12 +++++++----- 13 files changed, 136 insertions(+), 46 deletions(-) diff --git a/arduino.c b/arduino.c index 566f56ab..dbaafef2 100644 --- a/arduino.c +++ b/arduino.c @@ -84,7 +84,8 @@ static int arduino_open(PROGRAMMER * pgm, char * port) { union pinfo pinfo; strcpy(pgm->port, port); - pinfo.baud = pgm->baudrate? pgm->baudrate: 115200; + pinfo.serialinfo.baud = pgm->baudrate? pgm->baudrate: 115200; + pinfo.serialinfo.cflags = SERIAL_8N1; if (serial_open(port, pinfo, &pgm->fd)==-1) { return -1; } diff --git a/avr910.c b/avr910.c index bcc71d60..3e14fdc1 100644 --- a/avr910.c +++ b/avr910.c @@ -370,7 +370,8 @@ static int avr910_open(PROGRAMMER * pgm, char * port) } strcpy(pgm->port, port); - pinfo.baud = pgm->baudrate; + pinfo.serialinfo.baud = pgm->baudrate; + pinfo.serialinfo.cflags = SERIAL_8N1; if (serial_open(port, pinfo, &pgm->fd)==-1) { return -1; } diff --git a/buspirate.c b/buspirate.c index 90b50845..f2a6d9ec 100644 --- a/buspirate.c +++ b/buspirate.c @@ -427,7 +427,8 @@ static int buspirate_open(struct programmer_t *pgm, char * port) if(pgm->baudrate == 0) pgm->baudrate = 115200; - pinfo.baud = pgm->baudrate; + pinfo.serialinfo.baud = pgm->baudrate; + pinfo.serialinfo.cflags = SERIAL_8N1; strcpy(pgm->port, port); if (serial_open(port, pinfo, &pgm->fd)==-1) { return -1; diff --git a/butterfly.c b/butterfly.c index de9a3175..1d5fafdf 100644 --- a/butterfly.c +++ b/butterfly.c @@ -391,7 +391,8 @@ static int butterfly_open(PROGRAMMER * pgm, char * port) if(pgm->baudrate == 0) { pgm->baudrate = 19200; } - pinfo.baud = pgm->baudrate; + pinfo.serialinfo.baud = pgm->baudrate; + pinfo.serialinfo.cflags = SERIAL_8N1; if (serial_open(port, pinfo, &pgm->fd)==-1) { return -1; } diff --git a/jtagmkI.c b/jtagmkI.c index 2a5f27e4..fc5f3f70 100644 --- a/jtagmkI.c +++ b/jtagmkI.c @@ -553,7 +553,7 @@ static int jtagmkI_initialize(PROGRAMMER * pgm, AVRPART * p) progname, pgm->baudrate); if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) { PDATA(pgm)->initial_baudrate = pgm->baudrate; /* don't adjust again later */ - serial_setspeed(&pgm->fd, pgm->baudrate); + serial_setparams(&pgm->fd, pgm->baudrate, SERIAL_8N1); } } } @@ -648,9 +648,10 @@ static int jtagmkI_open(PROGRAMMER * pgm, char * port) for (i = 0; i < sizeof(baudtab) / sizeof(baudtab[0]); i++) { union pinfo pinfo; - pinfo.baud = baudtab[i].baud; + pinfo.serialinfo.baud = baudtab[i].baud; + pinfo.serialinfo.cflags = SERIAL_8N1; avrdude_message(MSG_NOTICE2, "%s: jtagmkI_open(): trying to sync at baud rate %ld:\n", - progname, pinfo.baud); + progname, pinfo.serialinfo.baud); if (serial_open(port, pinfo, &pgm->fd)==-1) { return -1; } @@ -697,7 +698,7 @@ static void jtagmkI_close(PROGRAMMER * pgm) "trying to set baudrate to %d\n", progname, PDATA(pgm)->initial_baudrate); if (jtagmkI_setparm(pgm, PARM_BITRATE, b) == 0) { - serial_setspeed(&pgm->fd, pgm->baudrate); + serial_setparams(&pgm->fd, pgm->baudrate, SERIAL_8N1); } } } diff --git a/jtagmkII.c b/jtagmkII.c index df10f9f9..be6a4ed6 100644 --- a/jtagmkII.c +++ b/jtagmkII.c @@ -1312,7 +1312,7 @@ static int jtagmkII_initialize(PROGRAMMER * pgm, AVRPART * p) "trying to set baudrate to %d\n", progname, pgm->baudrate); if (jtagmkII_setparm(pgm, PAR_BAUD_RATE, &b) == 0) - serial_setspeed(&pgm->fd, pgm->baudrate); + serial_setparams(&pgm->fd, pgm->baudrate, SERIAL_8N1); } } if ((pgm->flag & PGM_FL_IS_JTAG) && pgm->bitclock != 0.0) { @@ -1490,7 +1490,8 @@ static int jtagmkII_open(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -1542,7 +1543,8 @@ static int jtagmkII_open_dw(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -1594,7 +1596,8 @@ static int jtagmkII_open_pdi(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -1647,7 +1650,8 @@ static int jtagmkII_dragon_open(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -1700,7 +1704,8 @@ static int jtagmkII_dragon_open_dw(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -1753,7 +1758,8 @@ static int jtagmkII_dragon_open_pdi(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -3330,7 +3336,8 @@ static int jtagmkII_open32(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines diff --git a/libavrdude.h b/libavrdude.h index 2431a218..0d1bf3a3 100644 --- a/libavrdude.h +++ b/libavrdude.h @@ -530,9 +530,34 @@ union filedescriptor } usb; }; +#define SERIAL_CS5 0x0000 +#define SERIAL_CS6 0x0001 +#define SERIAL_CS7 0x0002 +#define SERIAL_CS8 0x0004 + +#define SERIAL_NO_CSTOPB 0x0000 +#define SERIAL_CSTOPB 0x0008 + +#define SERIAL_NO_CREAD 0x0000 +#define SERIAL_CREAD 0x0010 + +#define SERIAL_NO_PARITY 0x0000 +#define SERIAL_PARENB 0x0020 +#define SERIAL_PARODD 0x0040 + +#define SERIAL_NO_CLOCAL 0x0000 +#define SERIAL_CLOCAL 0x0080 + +#define SERIAL_8N1 (SERIAL_CS8 | SERIAL_NO_CSTOPB | SERIAL_CREAD | SERIAL_NO_PARITY | SERIAL_CLOCAL) +#define SERIAL_8E1 (SERIAL_CS8 | SERIAL_NO_CSTOPB | SERIAL_CREAD | SERIAL_PARENB | SERIAL_CLOCAL) +#define SERIAL_8E2 (SERIAL_CS8 | SERIAL_CSTOPB | SERIAL_CREAD | SERIAL_PARENB | SERIAL_CLOCAL) + union pinfo { - long baud; + struct { + long baud; + unsigned long cflags; + } serialinfo; struct { unsigned short vid; @@ -548,7 +573,7 @@ struct serial_device { // open should return -1 on error, other values on success int (*open)(char * port, union pinfo pinfo, union filedescriptor *fd); - int (*setspeed)(union filedescriptor *fd, long baud); + int (*setparams)(union filedescriptor *fd, long baud, unsigned long cflags); void (*close)(union filedescriptor *fd); int (*send)(union filedescriptor *fd, const unsigned char * buf, size_t buflen); @@ -570,7 +595,7 @@ extern struct serial_device avrdoper_serdev; extern struct serial_device usbhid_serdev; #define serial_open (serdev->open) -#define serial_setspeed (serdev->setspeed) +#define serial_setparams (serdev->setparams) #define serial_close (serdev->close) #define serial_send (serdev->send) #define serial_recv (serdev->recv) diff --git a/ser_posix.c b/ser_posix.c index 2f40d0e4..3d8e15a1 100644 --- a/ser_posix.c +++ b/ser_posix.c @@ -55,6 +55,8 @@ struct baud_mapping { /* There are a lot more baud rates we could handle, but what's the point? */ static struct baud_mapping baud_lookup_table [] = { + { 300, B300 }, + { 600, B600 }, { 1200, B1200 }, { 2400, B2400 }, { 4800, B4800 }, @@ -96,7 +98,20 @@ static speed_t serial_baud_lookup(long baud) return baud; } -static int ser_setspeed(union filedescriptor *fd, long baud) +static tcflag_t translate_flags(unsigned long cflags) +{ + return ((cflags & SERIAL_CS5) ? CS5 : 0) | + ((cflags & SERIAL_CS6) ? CS6 : 0) | + ((cflags & SERIAL_CS7) ? CS7 : 0) | + ((cflags & SERIAL_CS8) ? CS8 : 0) | + ((cflags & SERIAL_CSTOPB) ? CSTOPB : 0) | + ((cflags & SERIAL_CREAD) ? CREAD : 0) | + ((cflags & (SERIAL_PARENB | SERIAL_PARODD)) ? PARENB : 0) | + ((cflags & SERIAL_PARODD) ? PARODD : 0) | + ((cflags & SERIAL_CLOCAL) ? CLOCAL : 0) ; +} + +static int ser_setparams(union filedescriptor *fd, long baud, unsigned long cflags) { int rc; struct termios termios; @@ -110,7 +125,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud) */ rc = tcgetattr(fd->ifd, &termios); if (rc < 0) { - avrdude_message(MSG_INFO, "%s: ser_setspeed(): tcgetattr() failed", + avrdude_message(MSG_INFO, "%s: ser_setparams(): tcgetattr() failed", progname); return -errno; } @@ -125,7 +140,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud) termios.c_iflag = IGNBRK; termios.c_oflag = 0; termios.c_lflag = 0; - termios.c_cflag = (CS8 | CREAD | CLOCAL); + termios.c_cflag = translate_flags(cflags); termios.c_cc[VMIN] = 1; termios.c_cc[VTIME] = 0; @@ -134,7 +149,7 @@ static int ser_setspeed(union filedescriptor *fd, long baud) rc = tcsetattr(fd->ifd, TCSANOW, &termios); if (rc < 0) { - avrdude_message(MSG_INFO, "%s: ser_setspeed(): tcsetattr() failed\n", + avrdude_message(MSG_INFO, "%s: ser_setparams(): tcsetattr() failed\n", progname); return -errno; } @@ -298,7 +313,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp) /* * set serial line attributes */ - rc = ser_setspeed(fdp, pinfo.baud); + rc = ser_setparams(fdp, pinfo.serialinfo.baud, pinfo.serialinfo.cflags); if (rc) { avrdude_message(MSG_INFO, "%s: ser_open(): can't set attributes for device \"%s\": %s\n", progname, port, strerror(-rc)); @@ -308,7 +323,6 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp) return 0; } - static void ser_close(union filedescriptor *fd) { /* @@ -501,7 +515,7 @@ static int ser_drain(union filedescriptor *fd, int display) struct serial_device serial_serdev = { .open = ser_open, - .setspeed = ser_setspeed, + .setparams = ser_setparams, .close = ser_close, .send = ser_send, .recv = ser_recv, diff --git a/ser_win32.c b/ser_win32.c index 5fc17b1b..25412cf0 100644 --- a/ser_win32.c +++ b/ser_win32.c @@ -54,6 +54,8 @@ static unsigned char serial_over_ethernet = 0; /* HANDLE hComPort=INVALID_HANDLE_VALUE; */ static struct baud_mapping baud_lookup_table [] = { + { 300, CBR_300 }, + { 600, CBR_600 }, { 1200, CBR_1200 }, { 2400, CBR_2400 }, { 4800, CBR_4800 }, @@ -97,7 +99,7 @@ static BOOL serial_w32SetTimeOut(HANDLE hComPort, DWORD timeout) // in ms return SetCommTimeouts(hComPort, &ctmo); } -static int ser_setspeed(union filedescriptor *fd, long baud) +static int ser_setparams(union filedescriptor *fd, long baud, unsigned long cflags) { if (serial_over_ethernet) { return -ENOTTY; @@ -111,9 +113,39 @@ static int ser_setspeed(union filedescriptor *fd, long baud) dcb.fBinary = 1; dcb.fDtrControl = DTR_CONTROL_DISABLE; dcb.fRtsControl = RTS_CONTROL_DISABLE; - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; + switch ((cflags & (SERIAL_CS5 | SERIAL_CS6 | SERIAL_CS7 | SERIAL_CS8))) { + case SERIAL_CS5: + dcb.ByteSize = 5; + break; + case SERIAL_CS6: + dcb.ByteSize = 6; + break; + case SERIAL_CS7: + dcb.ByteSize = 7; + break; + case SERIAL_CS8: + dcb.ByteSize = 8; + break; + } + switch ((cflags & (SERIAL_NO_PARITY | SERIAL_PARENB | SERIAL_PARODD))) { + case SERIAL_NO_PARITY: + dcb.Parity = NOPARITY; + break; + case SERIAL_PARENB: + dcb.Parity = EVENPARITY; + break; + case SERIAL_PARODD: + dcb.Parity = ODDPARITY; + break; + } + switch ((cflags & (SERIAL_NO_CSTOPB | SERIAL_CSTOPB))) { + case SERIAL_NO_CSTOPB: + dcb.StopBits = ONESTOPBIT; + break; + case SERIAL_CSTOPB: + dcb.StopBits = TWOSTOPBITS; + break; + } if (!SetCommState(hComPort, &dcb)) return -1; @@ -283,7 +315,7 @@ static int ser_open(char * port, union pinfo pinfo, union filedescriptor *fdp) } fdp->pfd = (void *)hComPort; - if (ser_setspeed(fdp, pinfo.baud) != 0) + if (ser_setparams(fdp, pinfo.serialinfo.baud, pinfo.serialinfo.cflags) != 0) { CloseHandle(hComPort); avrdude_message(MSG_INFO, "%s: ser_open(): can't set com-state for \"%s\"\n", @@ -770,7 +802,7 @@ static int ser_drain(union filedescriptor *fd, int display) struct serial_device serial_serdev = { .open = ser_open, - .setspeed = ser_setspeed, + .setparams = ser_setparams, .close = ser_close, .send = ser_send, .recv = ser_recv, diff --git a/stk500.c b/stk500.c index ebb22a27..88944afc 100644 --- a/stk500.c +++ b/stk500.c @@ -657,7 +657,8 @@ static int stk500_open(PROGRAMMER * pgm, char * port) { union pinfo pinfo; strcpy(pgm->port, port); - pinfo.baud = pgm->baudrate? pgm->baudrate: 115200; + pinfo.serialinfo.baud = pgm->baudrate? pgm->baudrate: 115200; + pinfo.serialinfo.cflags = SERIAL_8N1; if (serial_open(port, pinfo, &pgm->fd)==-1) { return -1; } diff --git a/stk500v2.c b/stk500v2.c index 4687ad6b..f6c5b1ca 100644 --- a/stk500v2.c +++ b/stk500v2.c @@ -1603,12 +1603,12 @@ static void stk500v2_enable(PROGRAMMER * pgm) static int stk500v2_open(PROGRAMMER * pgm, char * port) { - union pinfo pinfo = { .baud = 115200 }; + union pinfo pinfo = { .serialinfo.baud = 115200, .serialinfo.cflags = SERIAL_8N1 }; DEBUG("STK500V2: stk500v2_open()\n"); if (pgm->baudrate) - pinfo.baud = pgm->baudrate; + pinfo.serialinfo.baud = pgm->baudrate; PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN; @@ -1671,12 +1671,12 @@ static int stk500v2_open(PROGRAMMER * pgm, char * port) static int stk600_open(PROGRAMMER * pgm, char * port) { - union pinfo pinfo = { .baud = 115200 }; + union pinfo pinfo = { .serialinfo.baud = 115200, .serialinfo.cflags = SERIAL_8N1 }; DEBUG("STK500V2: stk600_open()\n"); if (pgm->baudrate) - pinfo.baud = pgm->baudrate; + pinfo.serialinfo.baud = pgm->baudrate; PDATA(pgm)->pgmtype = PGMTYPE_UNKNOWN; @@ -3392,7 +3392,8 @@ static int stk500v2_jtagmkII_open(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -3503,7 +3504,8 @@ static int stk500v2_dragon_isp_open(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines @@ -3581,7 +3583,8 @@ static int stk500v2_dragon_hv_open(PROGRAMMER * pgm, char * port) * a higher baud rate, we switch to it later on, after establishing * the connection with the ICE. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; + pinfo.serialinfo.cflags = SERIAL_8N1; /* * If the port name starts with "usb", divert the serial routines diff --git a/wiring.c b/wiring.c index 1dc4d6a6..c0a68055 100644 --- a/wiring.c +++ b/wiring.c @@ -150,7 +150,8 @@ static int wiring_open(PROGRAMMER * pgm, char * port) union pinfo pinfo; strcpy(pgm->port, port); - pinfo.baud = pgm->baudrate ? pgm->baudrate: 115200; + pinfo.serialinfo.baud = pgm->baudrate ? pgm->baudrate: 115200; + pinfo.serialinfo.cflags = SERIAL_8N1; serial_open(port, pinfo, &pgm->fd); /* If we have a snoozetime, then we wait and do NOT toggle DTR/RTS */ diff --git a/xbee.c b/xbee.c index 454dc6de..1f974e55 100644 --- a/xbee.c +++ b/xbee.c @@ -1198,7 +1198,7 @@ static int xbeedev_open(char *port, union pinfo pinfo, (unsigned int)xbs->xbee_address[6], (unsigned int)xbs->xbee_address[7]); - if (pinfo.baud) { + if (pinfo.serialinfo.baud) { /* * User supplied the correct baud rate. */ @@ -1222,7 +1222,7 @@ static int xbeedev_open(char *port, union pinfo pinfo, * plugged in. The doubled clock rate means a doubled serial * rate. Double 9600 baud == 19200 baud. */ - pinfo.baud = 19200; + pinfo.serialinfo.baud = 19200; } else { /* * In normal mode, default to 9600. @@ -1234,10 +1234,11 @@ static int xbeedev_open(char *port, union pinfo pinfo, * XBee baud rate we should select. The baud rate of the AVR * device is irrelevant. */ - pinfo.baud = 9600; + pinfo.serialinfo.baud = 9600; } + pinfo.serialinfo.cflags = SERIAL_8N1; - avrdude_message(MSG_NOTICE, "%s: Baud %ld\n", progname, (long)pinfo.baud); + avrdude_message(MSG_NOTICE, "%s: Baud %ld\n", progname, (long)pinfo.serialinfo.baud); { const int rc = xbs->serialDevice->open(tty, pinfo, @@ -1640,7 +1641,8 @@ static int xbee_open(PROGRAMMER *pgm, char *port) { union pinfo pinfo; strcpy(pgm->port, port); - pinfo.baud = pgm->baudrate; + pinfo.serialinfo.baud = pgm->baudrate; + pinfo.serialinfo.cflags = SERIAL_8N1; /* Wireless is lossier than normal serial */ serial_recv_timeout = 1000; From 748bee8ecf6bec2640415903dcc4643180063f4f Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Wed, 8 Dec 2021 12:36:58 +0000 Subject: [PATCH 02/12] Basic read operations implemented git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1513 81a1dc3b-b13d-400b-aceb-764788c761c2 --- Makefile.am | 11 + avrdude.conf.in | 7 + avrdude/serialupdi.c | 253 ++++++++ avrdude/serialupdi.h | 43 ++ avrdude/updi_constants.h | 156 +++++ avrdude/updi_link.c | 825 ++++++++++++++++++++++++ avrdude/updi_link.h | 58 ++ avrdude/updi_nvm.c | 1277 ++++++++++++++++++++++++++++++++++++++ avrdude/updi_nvm.h | 51 ++ avrdude/updi_readwrite.c | 320 ++++++++++ avrdude/updi_readwrite.h | 51 ++ avrdude/updi_state.c | 55 ++ avrdude/updi_state.h | 85 +++ pgm_type.c | 2 + 14 files changed, 3194 insertions(+) create mode 100644 avrdude/serialupdi.c create mode 100644 avrdude/serialupdi.h create mode 100644 avrdude/updi_constants.h create mode 100644 avrdude/updi_link.c create mode 100644 avrdude/updi_link.h create mode 100644 avrdude/updi_nvm.c create mode 100644 avrdude/updi_nvm.h create mode 100644 avrdude/updi_readwrite.c create mode 100644 avrdude/updi_readwrite.h create mode 100644 avrdude/updi_state.c create mode 100644 avrdude/updi_state.h diff --git a/Makefile.am b/Makefile.am index 2b812d17..ffe7ca90 100644 --- a/Makefile.am +++ b/Makefile.am @@ -183,6 +183,17 @@ libavrdude_a_SOURCES = \ tpi.h \ usbasp.c \ usbasp.h \ + serialupdi.c \ + serialupdi.h \ + updi_constants.h \ + updi_link.c \ + updi_link.h \ + updi_state.c \ + updi_state.h \ + updi_readwrite.c \ + updi_readwrite.h \ + updi_nvm.c \ + updi_nvm.h \ usbdevs.h \ usb_hidapi.c \ usb_libusb.c \ diff --git a/avrdude.conf.in b/avrdude.conf.in index 5d1a7013..837c9f54 100644 --- a/avrdude.conf.in +++ b/avrdude.conf.in @@ -613,6 +613,13 @@ programmer reset = 3; # TMS 7 ; +programmer + id = "serialupdi"; + desc = "SerialUPDI"; + type = "serialupdi"; + connection_type = serial; +; + programmer id = "avrisp"; desc = "Atmel AVR ISP"; diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c new file mode 100644 index 00000000..0f4e70c1 --- /dev/null +++ b/avrdude/serialupdi.c @@ -0,0 +1,253 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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$ */ + +/* + * Interface to the SerialUPDI programmer. + * + * 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 "serialupdi.h" +#include "updi_link.h" +#include "updi_state.h" +#include "updi_readwrite.h" + +static void serialupdi_setup(PROGRAMMER * pgm) +{ + if ((pgm->cookie = malloc(sizeof(updi_state))) == 0) { + avrdude_message(MSG_INFO, + "%s: serialupdi_setup(): Out of memory allocating private data\n", + progname); + exit(1); + } + memset(pgm->cookie, 0, sizeof(updi_state)); + updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT); +} + +static void serialupdi_teardown(PROGRAMMER * pgm) +{ + free(pgm->cookie); +} + +static int serialupdi_open(PROGRAMMER * pgm, char * port) +{ + strcpy(pgm->port, port); + return updi_link_open(pgm); +} + +static int serialupdi_decode_sib(updi_sib_info * sib_info) +{ + char * str_ptr; + + sib_info->sib_string[SIB_INFO_STRING_LENGTH]=0; + avrdude_message(MSG_DEBUG, "%s: Received SIB: [%s]\n", progname, sib_info->sib_string); + memset(sib_info->family_string, 0, SIB_INFO_FAMILY_LENGTH+1); + memset(sib_info->nvm_string, 0, SIB_INFO_NVM_LENGTH+1); + memset(sib_info->debug_string, 0, SIB_INFO_DEBUG_LENGTH+1); + memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1); + memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1); + memset(sib_info->extra_string, 0, SIB_INFO_EXTRA_LENGTH+1); + + memcpy(sib_info->family_string, sib_info->sib_string, SIB_INFO_FAMILY_LENGTH); + memcpy(sib_info->nvm_string, sib_info->sib_string + 8, SIB_INFO_NVM_LENGTH); + memcpy(sib_info->debug_string, sib_info->sib_string + 11, SIB_INFO_DEBUG_LENGTH); + memcpy(sib_info->pdi_string, sib_info->sib_string + 15, SIB_INFO_PDI_LENGTH); + strcpy(sib_info->extra_string, (char *)sib_info->sib_string + 19); + + str_ptr = strstr(sib_info->nvm_string, ":"); + if (!str_ptr) { + avrdude_message(MSG_INFO, "%s: Incorrect format of NVM string\n", progname); + return -1; + } + sib_info->nvm_version = *(str_ptr+1); + + str_ptr = strstr(sib_info->debug_string, ":"); + if (!str_ptr) { + avrdude_message(MSG_INFO, "%s: Incorrect format of DEBUG string\n", progname); + return -1; + } + sib_info->debug_version = *(str_ptr+1); + + avrdude_message(MSG_DEBUG, "%s: Device family ID: %s\n", progname, sib_info->family_string); + avrdude_message(MSG_DEBUG, "%s: NVM interface: %s\n", progname, sib_info->nvm_string); + avrdude_message(MSG_DEBUG, "%s: Debug interface: %s\n", progname, sib_info->debug_string); + avrdude_message(MSG_DEBUG, "%s: PDI oscillator: %s\n", progname, sib_info->pdi_string); + avrdude_message(MSG_DEBUG, "%s: Extra information: %s\n", progname, sib_info->extra_string); + switch (sib_info->nvm_version) { + case '0': + avrdude_message(MSG_INFO, "%s: NVM type 0: 16-bit, page oriented write\n", progname); + break; + case '2': + avrdude_message(MSG_INFO, "%s: NVM type 2: 24-bit, word oriented write\n", progname); + break; + case '3': + avrdude_message(MSG_INFO, "%s: NVM type 3: 16-bit, page oriented\n", progname); + break; + default: + avrdude_message(MSG_INFO, "%s: Unsupported NVM type: %c, please update software\n", progname, sib_info->nvm_version); + return -1; + } + return 0; +} + +static void serialupdi_close(PROGRAMMER * pgm) +{ + updi_link_close(pgm); +} + +static int serialupdi_initialize(PROGRAMMER * pgm, AVRPART * p) +{ + updi_sib_info * sib_info = updi_get_sib_info(pgm); + + if (updi_link_init(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname); + return -1; + } + avrdude_message(MSG_INFO, "%s: UPDI link initialization OK\n", progname); + if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) { + avrdude_message(MSG_INFO, "%s: Read SIB operation failed\n", progname); + return -1; + } + if (serialupdi_decode_sib(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); + } + + return 0; +} + +static void serialupdi_disable(PROGRAMMER * pgm) +{ + /* Do nothing. */ + + return; +} + +static void serialupdi_enable(PROGRAMMER * pgm) +{ + /* Do nothing. */ + + return; +} + +static void serialupdi_display(PROGRAMMER * pgm, const char * p) +{ + return; +} + +static int serialupdi_cmd(PROGRAMMER * pgm, const unsigned char * cmd, + unsigned char * res) +{ + avrdude_message(MSG_INFO, "%s: error: cmd %s[%s] not implemented yet\n", + progname, cmd, res); + return -1; +} + +static int serialupdi_program_enable(PROGRAMMER * pgm, AVRPART * p) +{ + avrdude_message(MSG_INFO, "%s: error: program enable not implemented yet\n", + progname); + 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) +{ +// avrdude_message(MSG_INFO, "%s: error: read byte not implemented yet\n", +// progname); + return updi_read_byte(pgm, mem->offset + addr, value); +// return -1; +} + +static int serialupdi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + unsigned int page_size, + unsigned int addr, unsigned int n_bytes) +{ + avrdude_message(MSG_INFO, "%s: error: paged load not implemented yet\n", + progname); + return -1; +} + +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", + progname); + return -1; +} + +void serialupdi_initpgm(PROGRAMMER * pgm) +{ + strcpy(pgm->type, "serialupdi"); + + /* + * mandatory functions + */ + + pgm->initialize = serialupdi_initialize; + pgm->display = serialupdi_display; + pgm->enable = serialupdi_enable; + pgm->disable = serialupdi_disable; + pgm->program_enable = serialupdi_program_enable; + pgm->chip_erase = serialupdi_chip_erase; + pgm->cmd = serialupdi_cmd; + pgm->open = serialupdi_open; + pgm->close = serialupdi_close; + pgm->read_byte = serialupdi_read_byte; + pgm->write_byte = avr_write_byte_default; + + /* + * optional functions + */ + + pgm->paged_write = serialupdi_paged_write; + pgm->paged_load = serialupdi_paged_load; + pgm->setup = serialupdi_setup; + pgm->teardown = serialupdi_teardown; + +} + +const char serialupdi_desc[] = "Driver for SerialUPDI programmers"; diff --git a/avrdude/serialupdi.h b/avrdude/serialupdi.h new file mode 100644 index 00000000..ff7270d5 --- /dev/null +++ b/avrdude/serialupdi.h @@ -0,0 +1,43 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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 serialupdi_h +#define serialupdi_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char serialupdi_desc[]; +void serialupdi_initpgm (PROGRAMMER * pgm); + +#ifdef __cplusplus +} +#endif + +#endif /* serialupdi_h */ diff --git a/avrdude/updi_constants.h b/avrdude/updi_constants.h new file mode 100644 index 00000000..ff8a446f --- /dev/null +++ b/avrdude/updi_constants.h @@ -0,0 +1,156 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_constants_h +#define updi_constants_h + +#define UPDI_BREAK 0x00 + +#define UPDI_LDS 0x00 +#define UPDI_STS 0x40 +#define UPDI_LD 0x20 +#define UPDI_ST 0x60 +#define UPDI_LDCS 0x80 +#define UPDI_STCS 0xC0 +#define UPDI_REPEAT 0xA0 +#define UPDI_KEY 0xE0 + +#define UPDI_PTR 0x00 +#define UPDI_PTR_INC 0x04 +#define UPDI_PTR_ADDRESS 0x08 + +#define UPDI_ADDRESS_8 0x00 +#define UPDI_ADDRESS_16 0x04 +#define UPDI_ADDRESS_24 0x08 + +#define UPDI_DATA_8 0x00 +#define UPDI_DATA_16 0x01 +#define UPDI_DATA_24 0x02 + +#define UPDI_KEY_SIB 0x04 +#define UPDI_KEY_KEY 0x00 + +#define UPDI_KEY_64 0x00 +#define UPDI_KEY_128 0x01 +#define UPDI_KEY_256 0x02 + +#define UPDI_SIB_8BYTES UPDI_KEY_64 +#define UPDI_SIB_16BYTES UPDI_KEY_128 +#define UPDI_SIB_32BYTES UPDI_KEY_256 + +#define UPDI_REPEAT_BYTE 0x00 +#define UPDI_REPEAT_WORD 0x01 + +#define UPDI_PHY_SYNC 0x55 +#define UPDI_PHY_ACK 0x40 + +#define UPDI_MAX_REPEAT_SIZE (0xFF+1) // Repeat counter of 1-byte, with off-by-one counting + +//# CS and ASI Register Address map +#define UPDI_CS_STATUSA 0x00 +#define UPDI_CS_STATUSB 0x01 +#define UPDI_CS_CTRLA 0x02 +#define UPDI_CS_CTRLB 0x03 +#define UPDI_ASI_KEY_STATUS 0x07 +#define UPDI_ASI_RESET_REQ 0x08 +#define UPDI_ASI_CTRLA 0x09 +#define UPDI_ASI_SYS_CTRLA 0x0A +#define UPDI_ASI_SYS_STATUS 0x0B +#define UPDI_ASI_CRC_STATUS 0x0C + +#define UPDI_CTRLA_IBDLY_BIT 7 +#define UPDI_CTRLB_CCDETDIS_BIT 3 +#define UPDI_CTRLB_UPDIDIS_BIT 2 + +#define UPDI_KEY_NVM "NVMProg " +#define UPDI_KEY_CHIPERASE "NVMErase" +#define UPDI_KEY_UROW "NVMUs&te" + +#define UPDI_ASI_STATUSA_REVID 4 +#define UPDI_ASI_STATUSB_PESIG 0 + +#define UPDI_ASI_KEY_STATUS_CHIPERASE 3 +#define UPDI_ASI_KEY_STATUS_NVMPROG 4 +#define UPDI_ASI_KEY_STATUS_UROWWRITE 5 + +#define UPDI_ASI_SYS_STATUS_RSTSYS 5 +#define UPDI_ASI_SYS_STATUS_INSLEEP 4 +#define UPDI_ASI_SYS_STATUS_NVMPROG 3 +#define UPDI_ASI_SYS_STATUS_UROWPROG 2 +#define UPDI_ASI_SYS_STATUS_LOCKSTATUS 0 + +#define UPDI_ASI_SYS_CTRLA_UROW_FINAL 1 + +#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/avrdude/updi_link.c b/avrdude/updi_link.c new file mode 100644 index 00000000..33c3da1d --- /dev/null +++ b/avrdude/updi_link.c @@ -0,0 +1,825 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_link.h" +#include "updi_constants.h" +#include "updi_state.h" + +#include + +void msleep(int tms) +{ + struct timeval tv; + tv.tv_sec = tms / 1000; + tv.tv_usec = (tms % 1000) * 1000; + select (0, NULL, NULL, NULL, &tv); +} + +static int updi_physical_open(PROGRAMMER* pgm, int baudrate, unsigned long cflags) +{ + serial_recv_timeout = 100; + union pinfo pinfo; + + pinfo.serialinfo.baud = baudrate; + pinfo.serialinfo.cflags = cflags; + + avrdude_message(MSG_DEBUG, "%s: Opening serial port...\n", progname); + + if (serial_open(pgm->port, pinfo, &pgm->fd)==-1) { + + avrdude_message(MSG_INFO, "%s: Serial port open failed!\n", progname); + return -1; + } + + /* + * drain any extraneous input + */ + serial_drain(&pgm->fd, 0); + + return 0; +} + +static void updi_physical_close(PROGRAMMER* pgm) +{ + serial_close(&pgm->fd); + pgm->fd.ifd = -1; +} + +static int updi_physical_send(PROGRAMMER * pgm, unsigned char * buf, size_t len) +{ + size_t i; + int rv; + + avrdude_message(MSG_DEBUG, "%s: Sending %lu bytes [", progname, len); + for (i=0; ifd, buf, len); + serial_recv(&pgm->fd, buf, len); + return rv; +} + +static int updi_physical_recv(PROGRAMMER * pgm, unsigned char * buf, size_t len) +{ + size_t i; + int rv; + + rv = serial_recv(&pgm->fd, buf, len); + if (rv < 0) { + avrdude_message(MSG_DEBUG, + "%s: serialupdi_recv(): programmer is not responding\n", + progname); + return -1; + } + + avrdude_message(MSG_DEBUG, "%s: Received %lu bytes [", progname, len); + for (i=0; ifd, buffer, 1); + serial_recv(&pgm->fd, buffer, 1); + + msleep(100); + + buffer[0] = UPDI_BREAK; + + serial_send(&pgm->fd, buffer, 1); + serial_recv(&pgm->fd, buffer, 1); + + updi_physical_close(pgm); + + return updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, SERIAL_8E2); +} + +int updi_physical_sib(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size) +{ +/* + def sib(self): + """ + System information block is just a string coming back from a SIB command + """ + self.send([ + constants.UPDI_PHY_SYNC, + constants.UPDI_KEY | constants.UPDI_KEY_SIB | constants.UPDI_SIB_32BYTES]) + return self.ser.readline() +*/ + unsigned char send_buffer[2]; + + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_KEY | UPDI_KEY_SIB | UPDI_SIB_32BYTES; + + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_INFO, "%s: SIB request send failed\n", progname); + return -1; + } + + return updi_physical_recv(pgm, buffer, size); +} + +int updi_link_open(PROGRAMMER * pgm) +{ + return updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, + (SERIAL_CS8 | SERIAL_CSTOPB | SERIAL_CREAD | SERIAL_PARENB | SERIAL_CLOCAL)); +} + +void updi_link_close(PROGRAMMER * pgm) +{ + updi_physical_close(pgm); +} + +static int updi_link_init_session_parameters(PROGRAMMER * pgm) +{ +/* + def _init_session_parameters(self): + """ + Set the inter-byte delay bit and disable collision detection + """ + self.stcs(constants.UPDI_CS_CTRLB, 1 << constants.UPDI_CTRLB_CCDETDIS_BIT) + self.stcs(constants.UPDI_CS_CTRLA, 1 << constants.UPDI_CTRLA_IBDLY_BIT) +*/ + if (updi_link_stcs(pgm, UPDI_CS_CTRLB, 1 << UPDI_CTRLB_CCDETDIS_BIT) < 0) { + return -1; + } + + if (updi_link_stcs(pgm, UPDI_CS_CTRLA, 1 << UPDI_CTRLA_IBDLY_BIT) < 0) { + return -1; + } + + return 0; +} + +static int updi_link_check(PROGRAMMER * pgm) +{ +/* + def _check_datalink(self): + """ + Check UPDI by loading CS STATUSA + """ + try: + if self.ldcs(constants.UPDI_CS_STATUSA) != 0: + self.logger.info("UPDI init OK") + return True + except PymcuprogError: + self.logger.warning("Check failed") + return False + self.logger.info("UPDI not OK - reinitialisation required") + return False +*/ + int result; + uint8_t value; + result = updi_link_ldcs(pgm, UPDI_CS_STATUSA, &value); + if (result < 0) { + avrdude_message(MSG_DEBUG, "%s: Check failed\n", progname); + return -1; + } else { + if (value > 0) { + avrdude_message(MSG_DEBUG, "%s: UDPI init OK\n", progname); + return 0; + } else { + avrdude_message(MSG_DEBUG, "%s: UDPI not OK - reinitialisation required\n", progname); + return -1; + } + } +} + + +int updi_link_init(PROGRAMMER * pgm) +{ +/* + def init_datalink(self): + """ + Init DL layer + """ + self._init_session_parameters() + # Check + if not self._check_datalink(): + # Send double break if all is not well, and re-check + self.updi_phy.send_double_break() + self._init_session_parameters() + if not self._check_datalink(): + raise PymcuprogError("UPDI initialisation failed") +*/ + if (updi_link_init_session_parameters(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: Session initialisation failed\n", progname); + return -1; + } + + if (updi_link_check(pgm) < 0) { + avrdude_message(MSG_DEBUG, "%s: Datalink not active, resetting...\n", progname); + if (updi_physical_send_double_break(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: Datalink initialisation failed\n", progname); + return -1; + } + if (updi_link_init_session_parameters(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: Session initialisation failed\n", progname); + return -1; + } + if (updi_link_check(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: Restoring datalink failed\n", progname); + return -1; + } + } + return 0; +} + +int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value) +{ +/* + def ldcs(self, address): + """ + Load data from Control/Status space + + :param address: address to load + """ + self.logger.debug("LDCS from 0x%02X", address) + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LDCS | (address & 0x0F)]) + response = self.updi_phy.receive(self.LDCS_RESPONSE_BYTES) + numbytes_received = len(response) + if numbytes_received != self.LDCS_RESPONSE_BYTES: + raise PymcuprogError("Unexpected number of bytes in response: " + "{} byte(s) expected {} byte(s)".format(numbytes_received, self.LDCS_RESPONSE_BYTES)) + + return response[0] +*/ + unsigned char buffer[2]; + int result; + avrdude_message(MSG_DEBUG, "%s: LDCS from 0x%02X\n", progname, address); + buffer[0]=UPDI_PHY_SYNC; + buffer[1]=UPDI_LDCS | (address & 0x0F); + if (updi_physical_send(pgm, buffer, 2) < 0) { + avrdude_message(MSG_INFO, "%s: LDCS send operation failed\n", progname); + return -1; + } + result = updi_physical_recv(pgm, buffer, 1); + if (result != 1) { + if (result >= 0) { + avrdude_message(MSG_INFO, "%s: Incorrect response size, received %d instead of %d bytes\n", progname, result, 1); + } + return -1; + } + * value = buffer[0]; + return 0; +} + +int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value) +{ +/* + def stcs(self, address, value): + """ + Store a value to Control/Status space + + :param address: address to store to + :param value: value to write + """ + self.logger.debug("STCS to 0x%02X", address) + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_STCS | (address & 0x0F), value]) +*/ + unsigned char buffer[3]; + avrdude_message(MSG_DEBUG, "%s: STCS 0x%02X to address 0x%02X\n", progname, value, address); + buffer[0] = UPDI_PHY_SYNC; + buffer[1] = UPDI_STCS | (address & 0x0F); + buffer[2] = value; + return updi_physical_send(pgm, buffer, 3); +} + +int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def ld_ptr_inc(self, size): + """ + Loads a number of bytes from the pointer location with pointer post-increment + + :param size: number of bytes to load + :return: values read + """ + self.logger.debug("LD8 from ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC | + constants.UPDI_DATA_8]) + return self.updi_phy.receive(size) +*/ + unsigned char send_buffer[2]; + avrdude_message(MSG_DEBUG, "%s: LD8 from ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_8; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_INFO, "%s: LD_PTR_INC send operation failed\n", progname); + return -1; + } + return updi_physical_recv(pgm, buffer, size); +} + +int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words) +{ +/* + def ld_ptr_inc16(self, words): + """ + Load a 16-bit word value from the pointer location with pointer post-increment + + :param words: number of words to load + :return: values read + """ + self.logger.debug("LD16 from ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC | + constants.UPDI_DATA_16]) + return self.updi_phy.receive(words << 1) +*/ + unsigned char send_buffer[2]; + avrdude_message(MSG_DEBUG, "%s: LD16 from ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_16; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_INFO, "%s: LD_PTR_INC send operation failed\n", progname); + return -1; + } + return updi_physical_recv(pgm, buffer, words << 2); +} + +int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def st_ptr_inc(self, data): + """ + Store data to the pointer location with pointer post-increment + + :param data: data to store + """ + self.logger.debug("ST8 to *ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_8, + data[0]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("ACK error with st_ptr_inc") + + num = 1 + while num < len(data): + self.updi_phy.send([data[num]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st_ptr_inc") + num += 1 +*/ + unsigned char send_buffer[3]; + unsigned char recv_buffer[1]; + int response; + int num = 1; + avrdude_message(MSG_DEBUG, "%s: ST8 to *ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_8; + send_buffer[2] = buffer[0]; + if (updi_physical_send(pgm, send_buffer, 3) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR_INC send operation failed\n", progname); + return -1; + } + + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_INFO, "%s: ACK was expected but not received\n", progname); + return -1; + } + + while (num < size) { + send_buffer[0]=buffer[num]; + if (updi_physical_send(pgm, send_buffer, 1) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR_INC data send operation failed\n", progname); + return -1; + } + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_INFO, "%s: Data ACK was expected but not received\n", progname); + return -1; + } + num++; + } + + return 0; +} + +int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words) +{ +/* + def st_ptr_inc16(self, data): + """ + Store a 16-bit word value to the pointer location with pointer post-increment + + :param data: data to store + """ + self.logger.debug("ST16 to *ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | + constants.UPDI_DATA_16, data[0], data[1]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("ACK error with st_ptr_inc16") + + num = 2 + while num < len(data): + self.updi_phy.send([data[num], data[num + 1]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st_ptr_inc16") + num += 2 +*/ + unsigned char send_buffer[4]; + unsigned char recv_buffer[1]; + int response; + int num = 2; + avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16; + send_buffer[2] = buffer[0]; + send_buffer[3] = buffer[1]; + if (updi_physical_send(pgm, send_buffer, 4) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR_INC16 send operation failed\n", progname); + return -1; + } + + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_INFO, "%s: ACK was expected but not received\n", progname); + return -1; + } + + while (num < words) { + send_buffer[0]=buffer[num]; + send_buffer[1]=buffer[num+1]; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR_INC data send operation failed\n", progname); + return -1; + } + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_INFO, "%s: Data ACK was expected but not received\n", progname); + return -1; + } + num+=2; + } + + return 0; +} + +int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats) +{ +/* + def repeat(self, repeats): + """ + Store a value to the repeat counter + + :param repeats: number of repeats requested + """ + self.logger.debug("Repeat %d", repeats) + if (repeats - 1) > constants.UPDI_MAX_REPEAT_SIZE: + self.logger.error("Invalid repeat count of %d", repeats) + raise Exception("Invalid repeat count!") + repeats -= 1 + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, + repeats & 0xFF]) +*/ + unsigned char buffer[3]; + avrdude_message(MSG_DEBUG, "%s: Repeat %d\n", progname, repeats); + if ((repeats - 1) > UPDI_MAX_REPEAT_SIZE) { + avrdude_message(MSG_INFO, "%s: Invalid repeat count of %d\n", progname, repeats); + return -1; + } + repeats-=1; + buffer[0] = UPDI_PHY_SYNC; + buffer[1] = UPDI_REPEAT | UPDI_REPEAT_BYTE; + buffer[2] = repeats & 0xFF; + return updi_physical_send(pgm, buffer, 3); +} + +int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def read_sib(self): + """ + Read the SIB + """ + return self.updi_phy.sib() +*/ + return updi_physical_sib(pgm, buffer, size); +} + +int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size) +{ +/* + def key(self, size, key): + """ + Write a key + + :param size: size of key (0=64B, 1=128B, 2=256B) + :param key: key value + """ + self.logger.debug("Writing key") + if len(key) != 8 << size: + raise PymcuprogError("Invalid KEY length!") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_KEY | constants.UPDI_KEY_KEY | size]) + self.updi_phy.send(list(reversed(list(key)))) +*/ + unsigned char send_buffer[2]; + unsigned char reversed_key[256]; + int index; + avrdude_message(MSG_DEBUG, "%s: UPDI writing key\n", progname); + if (size != (8 << size_type)) { + avrdude_message(MSG_INFO, "%s: Invalid key length\n", progname); + return -1; + } + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_KEY | UPDI_KEY_KEY | size_type; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI key send message failed\n", progname); + return -1; + } + /* reverse key contents */ + for (index=0; index> 8) & 0xFF, (address >> 16) & 0xFF]) + return self.updi_phy.receive(1)[0] +*/ + unsigned char send_buffer[5]; + unsigned char recv_buffer[1]; + avrdude_message(MSG_DEBUG, "%s: LD from 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LDS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_INFO, "%s: LD operation send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_INFO, "%s: LD operation recv failed\n", progname); + return -1; + } + * value = recv_buffer[0]; + return 0; +} + +int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value) +{ +/* + def ld16(self, address): + """ + Load a 16-bit word directly from a 24-bit address + + :param address: address to load from + :return: values read + """ + self.logger.info("LD from 0x{0:06X}".format(address)) + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + return self.updi_phy.receive(2) +*/ + unsigned char send_buffer[5]; + unsigned char recv_buffer[2]; + avrdude_message(MSG_DEBUG, "%s: LD16 from 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LDS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_INFO, "%s: LD16 operation send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 2) < 0) { + avrdude_message(MSG_INFO, "%s: LD16 operation recv failed\n", progname); + return -1; + } + * value = (recv_buffer[0] << 8 | recv_buffer[1]); + return 0; +} + +static int updi_link_st_data_phase(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size) +{ +/* + def _st_data_phase(self, values): + """ + Performs data phase of transaction: + * receive ACK + * send data + + :param values: bytearray of value(s) to send + """ + response = self.updi_phy.receive(1) + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st") + + self.updi_phy.send(values) + response = self.updi_phy.receive(1) + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st") +*/ + unsigned char recv_buffer[1]; + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI data phase recv failed on first ACK\n", progname); + return -1; + } + if (recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_INFO, "%s: UPDI data phase expected first ACK\n", progname); + return -1; + } + if (updi_physical_send(pgm, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI data phase send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI data phase recv failed on second ACK\n", progname); + return -1; + } + if (recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_INFO, "%s: UPDI data phase expected second ACK\n", progname); + return -1; + } + return 0; +} + +int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value) +{ +/* + def st(self, address, value): + """ + Store a single byte value directly to a 24-bit address + + :param address: address to write to + :param value: value to write + """ + self.logger.info("ST to 0x{0:06X}".format(address)) + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_8, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + return self._st_data_phase([value & 0xFF]) +*/ + unsigned char send_buffer[5]; + avrdude_message(MSG_DEBUG, "%s: ST to 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_STS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_INFO, "%s: ST operation send failed\n", progname); + return -1; + } + send_buffer[0] = value; + return updi_link_st_data_phase(pgm, send_buffer, 1); +} + +int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value) +{ +/* + def st16(self, address, value): + """ + Store a 16-bit word value directly to a 24-bit address + + :param address: address to write to + :param value: value to write + """ + self.logger.info("ST to 0x{0:06X}".format(address)) + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + return self._st_data_phase([value & 0xFF, (value >> 8) & 0xFF]) +*/ + unsigned char send_buffer[5]; + avrdude_message(MSG_DEBUG, "%s: ST16 to 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_STS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_INFO, "%s: ST16 operation send failed\n", progname); + return -1; + } + send_buffer[0] = value & 0xFF; + send_buffer[1] = (value >> 8) & 0xFF; + return updi_link_st_data_phase(pgm, send_buffer, 2); +} + +int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address) +{ +/* + def st_ptr(self, address): + """ + Set the pointer location + + :param address: address to write + """ + self.logger.info("ST to ptr") + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_ADDRESS | constants.UPDI_DATA_24, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + response = self.updi_phy.receive(1) + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st_ptr") +*/ + unsigned char send_buffer[5]; + unsigned char recv_buffer[1]; + avrdude_message(MSG_DEBUG, "%s: ST_PTR to 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_STS | UPDI_ST | UPDI_PTR_ADDRESS | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_DATA_24 : UPDI_DATA_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR operation send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI ST_PTR recv failed on ACK\n", progname); + return -1; + } + if (recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_INFO, "%s: UPDI ST_PTR expected ACK\n", progname); + return -1; + } + return 0; +} diff --git a/avrdude/updi_link.h b/avrdude/updi_link.h new file mode 100644 index 00000000..e6848b0d --- /dev/null +++ b/avrdude/updi_link.h @@ -0,0 +1,58 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_link_h +#define updi_link_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_link_open(PROGRAMMER * pgm); +void updi_link_close(PROGRAMMER * pgm); +int updi_link_init(PROGRAMMER * pgm); +int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value); +int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value); +int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); +int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); +int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats); +int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size); +int updi_link_ld(PROGRAMMER * pgm, uint32_t address, uint8_t * value); +int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value); +int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value); +int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value); +int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_link_h */ diff --git a/avrdude/updi_nvm.c b/avrdude/updi_nvm.c new file mode 100644 index 00000000..df8cc02c --- /dev/null +++ b/avrdude/updi_nvm.c @@ -0,0 +1,1277 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_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(PROGRAMMER * pgm, 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 +*/ + avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_flash_page_V0(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) < 0) { + avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_eeprom_V0(PROGRAMMER * pgm, 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") +*/ + avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_user_row_V0(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase user row\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Load NVM address\n", progname); + if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRL, address & 0xFF) < 0) { + avrdude_message(MSG_INFO, "%s: Write ADDRL operation failed\n", progname); + return -1; + } + if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRH, (address >> 8) & 0xFF) < 0) { + avrdude_message(MSG_INFO, "%s: Write ADDRH operation failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Load fuse data\n", progname); + if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_DATAL, value & 0xFF) < 0) { + avrdude_message(MSG_INFO, "%s: Write DATAL operation failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Execute fuse write\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE) < 0) { + avrdude_message(MSG_INFO, "%s: Write fuse operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_write_V0(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear page buffer\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) < 0) { + avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + } + avrdude_message(MSG_DEBUG, "%s: Committing data\n", progname); + if (nvm_command == USE_DEFAULT_COMMAND) { + nvm_command = UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE; + } + if (updi_nvm_command(pgm, p, nvm_command) < 0) { + avrdude_message(MSG_INFO, "%s: Commit data command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_chip_erase_V2(PROGRAMMER * pgm, 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 +*/ + avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_flash_page_V2(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_eeprom_V2(PROGRAMMER * pgm, 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) +*/ + avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_user_row_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, + uint16_t size, access_mode mode); + +static int nvm_write_flash_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: NVM EEPROM erase/write command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Clear NVM command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_write_fuse_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: NVM write command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE) < 0) { + avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Clear NVM command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_chip_erase_V3(PROGRAMMER * pgm, 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 +*/ + avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_flash_page_V3(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_eeprom_V3(PROGRAMMER * pgm, 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") +*/ + avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_user_row_V3(PROGRAMMER * pgm, 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) +*/ + avrdude_message(MSG_DEBUG, "%s: Erase user row at address 0x%06X\n", progname, address); + + return nvm_erase_flash_page_V3(pgm, p, address); +} + +static int nvm_write_V3(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, + uint16_t size, access_mode mode, uint8_t nvm_command); + +static int nvm_write_flash_V3(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear page buffer\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR) < 0) { + avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + } + avrdude_message(MSG_DEBUG, "%s: Committing data\n", progname); + if (nvm_command == USE_DEFAULT_COMMAND) { + nvm_command = UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE; + } + if (updi_nvm_command(pgm, p, nvm_command) < 0) { + avrdude_message(MSG_INFO, "%s: Commit data command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + + +int updi_nvm_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_chip_erase_V0(pgm, p); + case UPDI_NVM_MODE_V2: + return nvm_chip_erase_V2(pgm, p); + case UPDI_NVM_MODE_V3: + return nvm_chip_erase_V3(pgm, p); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_erase_flash_page(PROGRAMMER * pgm, AVRPART *p, uint32_t address) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_erase_flash_page_V0(pgm, p, address); + case UPDI_NVM_MODE_V2: + return nvm_erase_flash_page_V2(pgm, p, address); + case UPDI_NVM_MODE_V3: + return nvm_erase_flash_page_V3(pgm, p, address); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_erase_eeprom(PROGRAMMER * pgm, AVRPART *p) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_erase_eeprom_V0(pgm, p); + case UPDI_NVM_MODE_V2: + return nvm_erase_eeprom_V2(pgm, p); + case UPDI_NVM_MODE_V3: + return nvm_erase_eeprom_V3(pgm, p); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_erase_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_erase_user_row_V0(pgm, p, address, size); + case UPDI_NVM_MODE_V2: + return nvm_erase_user_row_V2(pgm, p, address, size); + case UPDI_NVM_MODE_V3: + return nvm_erase_user_row_V3(pgm, p, address, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_flash(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_write_flash_V0(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V2: + return nvm_write_flash_V2(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V3: + return nvm_write_flash_V3(pgm, p, address, buffer, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return 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); + case UPDI_NVM_MODE_V3: + return nvm_write_user_row_V3(pgm, p, address, buffer, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_eeprom(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_write_eeprom_V0(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V2: + return nvm_write_eeprom_V2(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V3: + return nvm_write_eeprom_V3(pgm, p, address, buffer, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_fuse(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint8_t value) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_write_fuse_V0(pgm, p, address, value); + case UPDI_NVM_MODE_V2: + return nvm_write_fuse_V2(pgm, p, address, value); + case UPDI_NVM_MODE_V3: + return nvm_write_fuse_V3(pgm, p, address, value); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_wait_ready(PROGRAMMER * pgm, 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; + struct timeval tv; + uint8_t status; + 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; + } + gettimeofday (&tv, NULL); + current_time = (tv.tv_sec * 1000000) + tv.tv_usec; + } while ((current_time - start_time) < 10000000); + + avrdude_message(MSG_INFO, "%s: Wait NVM ready timed out\n", progname); + return -1; +} + +int updi_nvm_command(PROGRAMMER * pgm, 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) +*/ + avrdude_message(MSG_DEBUG, "%s: NVMCMD %d executing\n", progname, command); + + return updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_CTRLA, command); +} diff --git a/avrdude/updi_nvm.h b/avrdude/updi_nvm.h new file mode 100644 index 00000000..c0c1544e --- /dev/null +++ b/avrdude/updi_nvm.h @@ -0,0 +1,51 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_h +#define updi_nvm_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_nvm_chip_erase(PROGRAMMER * pgm, AVRPART * p); +int updi_nvm_erase_flash_page(PROGRAMMER * pgm, AVRPART *p, uint32_t address); +int updi_nvm_erase_eeprom(PROGRAMMER * pgm, AVRPART *p); +int updi_nvm_erase_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint16_t size); +int updi_nvm_write_flash(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); +int updi_nvm_write_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); +int updi_nvm_write_eeprom(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); +int updi_nvm_write_fuse(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint8_t value); +int updi_nvm_wait_ready(PROGRAMMER * pgm, AVRPART *p); +int updi_nvm_command(PROGRAMMER * pgm, AVRPART *p, uint8_t command); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_nvm_h */ diff --git a/avrdude/updi_readwrite.c b/avrdude/updi_readwrite.c new file mode 100644 index 00000000..f36015b8 --- /dev/null +++ b/avrdude/updi_readwrite.c @@ -0,0 +1,320 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_constants.h" +#include "updi_link.h" +#include "updi_readwrite.h" + +int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value) +{ +/* + def read_cs(self, address): + """ + Read from Control/Status space + + :param address: address (index) to read + :return: value read + """ + return self.datalink.ldcs(address) +*/ + return updi_link_ldcs(pgm, address, value); +} + +int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value) +{ +/* + def write_cs(self, address, value): + """ + Write to Control/Status space + + :param address: address (index) to write + :param value: 8-bit value to write + """ + return self.datalink.stcs(address, value) +*/ + return updi_link_stcs(pgm, address, value); +} + +int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size) +{ +/* + def write_key(self, size, key): + """ + Write a KEY into UPDI + + :param size: size of key to send + :param key: key value + """ + return self.datalink.key(size, key) +*/ + return updi_link_key(pgm, buffer, size_type, size); +} + +int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def read_sib(self): + """ + Read the SIB from UPDI + + :return: SIB string (bytearray) read + """ + return self.datalink.read_sib() +*/ + return updi_link_read_sib(pgm, buffer, size); +} + +int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value) +{ +/* + def read_byte(self, address): + """ + Read a single byte from UPDI + + :param address: address to read from + :return: value read + """ + return self.datalink.ld(address) +*/ + return updi_link_ld(pgm, address, value); +} + +int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value) +{ +/* + def write_byte(self, address, value): + """ + Writes a single byte to UPDI + + :param address: address to write to + :param value: value to write + """ + return self.datalink.st(address, value) +*/ + return updi_link_st(pgm, address, value); +} + +int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def read_data(self, address, size): + """ + Reads a number of bytes of data from UPDI + + :param address: address to write to + :param size: number of bytes to read + """ + self.logger.debug("Reading %d bytes from 0x%04X", size, address) + # Range check + if size > constants.UPDI_MAX_REPEAT_SIZE: + raise PymcuprogError("Cant read that many bytes in one go") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + if size > 1: + self.datalink.repeat(size) + + # Do the read(s) + return self.datalink.ld_ptr_inc(size) +*/ + avrdude_message(MSG_DEBUG, "%s: Reading %d bytes from 0x%06X", progname, size, address); + + if (size > UPDI_MAX_REPEAT_SIZE) { + avrdude_message(MSG_INFO, "%s: Can't read that many bytes in one go\n", progname); + return -1; + } + + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); + return -1; + } + + if (size > 1) { + if (updi_link_repeat(pgm, size) < 0) { + avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); + return -1; + } + } + return updi_link_ld_ptr_inc(pgm, buffer, size); +} + +int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def write_data(self, address, data): + """ + Writes a number of bytes to memory + + :param address: address to write to + :param data: data to write + """ + # Special case of 1 byte + if len(data) == 1: + return self.datalink.st(address, data[0]) + # Special case of 2 byte + if len(data) == 2: + self.datalink.st(address, data[0]) + return self.datalink.st(address + 1, data[1]) + + # Range check + if len(data) > constants.UPDI_MAX_REPEAT_SIZE: + raise PymcuprogError("Invalid length") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + self.datalink.repeat(len(data)) + return self.datalink.st_ptr_inc(data) +*/ + if (size == 1) { + return updi_link_st(pgm, address, buffer[0]); + } + if (size == 2) { + if (updi_link_st(pgm, address, buffer[0]) < 0) { + avrdude_message(MSG_INFO, "%s: ST operation failed\n", progname); + return -1; + } + return updi_link_st(pgm, address+1, buffer[1]); + } + if (size > UPDI_MAX_REPEAT_SIZE) { + avrdude_message(MSG_INFO, "%s: Invalid length\n", progname); + return -1; + } + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); + return -1; + } + if (updi_link_repeat(pgm, size) < 0) { + avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); + return -1; + } + return updi_link_st_ptr_inc(pgm, buffer, size); +} + +int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def read_data_words(self, address, words): + """ + Reads a number of words of data from UPDI + + :param address: address to write to + :param words: number of words to read + """ + self.logger.debug("Reading %d words from 0x%04X", words, address) + + # Range check + if words > constants.UPDI_MAX_REPEAT_SIZE >> 1: + raise PymcuprogError("Cant read that many words in one go") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + if words > 1: + self.datalink.repeat(words) + + # Do the read + return self.datalink.ld_ptr_inc16(words) +*/ + avrdude_message(MSG_DEBUG, "%s: Reading %d words from 0x%06X", progname, size, address); + + if (size > (UPDI_MAX_REPEAT_SIZE >> 1)) { + avrdude_message(MSG_INFO, "%s: Can't read that many words in one go\n", progname); + return -1; + } + + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); + return -1; + } + + if (size > 1) { + if (updi_link_repeat(pgm, size) < 0) { + avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); + return -1; + } + } + return updi_link_ld_ptr_inc16(pgm, buffer, size); +} + +int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def write_data_words(self, address, data): + """ + Writes a number of words to memory + + :param address: address to write to + :param data: data to write + """ + # Special-case of 1 word + if len(data) == 2: + value = data[0] + (data[1] << 8) + return self.datalink.st16(address, value) + + # Range check + if len(data) > constants.UPDI_MAX_REPEAT_SIZE << 1: + raise PymcuprogError("Invalid length") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + self.datalink.repeat(len(data) >> 1) + return self.datalink.st_ptr_inc16(data) +*/ + if (size == 2) { + return updi_link_st16(pgm, address, buffer[0] + (buffer[1] << 8)); + } + if (size > UPDI_MAX_REPEAT_SIZE << 1) { + avrdude_message(MSG_INFO, "%s: Invalid length\n", progname); + return -1; + } + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); + return -1; + } + if (updi_link_repeat(pgm, size >> 1) < 0) { + avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); + return -1; + } + return updi_link_st_ptr_inc16(pgm, buffer, size); +} diff --git a/avrdude/updi_readwrite.h b/avrdude/updi_readwrite.h new file mode 100644 index 00000000..9519d179 --- /dev/null +++ b/avrdude/updi_readwrite.h @@ -0,0 +1,51 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_readwrite_h +#define updi_readwrite_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value); +int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value); +int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size); +int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value); +int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value); +int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); +int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); +int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); +int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_readwrite_h */ diff --git a/avrdude/updi_state.c b/avrdude/updi_state.c new file mode 100644 index 00000000..63d80f46 --- /dev/null +++ b/avrdude/updi_state.c @@ -0,0 +1,55 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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 "libavrdude.h" +#include "updi_state.h" + +updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm) +{ + return &((updi_state *)(pgm->cookie))->sib_info; +} + +updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm) +{ + return ((updi_state *)(pgm->cookie))->datalink_mode; +} + +void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode) +{ + ((updi_state *)(pgm->cookie))->datalink_mode = mode; +} + +updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm) +{ + return ((updi_state *)(pgm->cookie))->nvm_mode; +} + +void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode) +{ + ((updi_state *)(pgm->cookie))->nvm_mode = mode; +} diff --git a/avrdude/updi_state.h b/avrdude/updi_state.h new file mode 100644 index 00000000..d4e39d4c --- /dev/null +++ b/avrdude/updi_state.h @@ -0,0 +1,85 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_state_h +#define updi_state_h + +#include "libavrdude.h" + +typedef enum +{ + UPDI_LINK_MODE_16BIT, + UPDI_LINK_MODE_24BIT +} updi_datalink_mode; + +typedef enum +{ + UPDI_NVM_MODE_V0, + UPDI_NVM_MODE_V2, + UPDI_NVM_MODE_V3 +} updi_nvm_mode; + +#define SIB_INFO_STRING_LENGTH 32 +#define SIB_INFO_FAMILY_LENGTH 8 +#define SIB_INFO_NVM_LENGTH 3 +#define SIB_INFO_DEBUG_LENGTH 3 +#define SIB_INFO_PDI_LENGTH 4 +#define SIB_INFO_EXTRA_LENGTH 20 + +typedef struct +{ + unsigned char sib_string[SIB_INFO_STRING_LENGTH+1]; + char family_string[SIB_INFO_FAMILY_LENGTH+1]; + char nvm_string[SIB_INFO_NVM_LENGTH+1]; + char debug_string[SIB_INFO_DEBUG_LENGTH+1]; + char pdi_string[SIB_INFO_PDI_LENGTH+1]; + char extra_string[SIB_INFO_EXTRA_LENGTH+1]; + char nvm_version; + char debug_version; +} updi_sib_info; + +typedef struct +{ + updi_sib_info sib_info; + updi_datalink_mode datalink_mode; + updi_nvm_mode nvm_mode; +} updi_state; + +#ifdef __cplusplus +extern "C" { +#endif + +updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm); +updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm); +void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode); +updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm); +void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_state_h */ diff --git a/pgm_type.c b/pgm_type.c index 9f28f441..becf7bdd 100644 --- a/pgm_type.c +++ b/pgm_type.c @@ -45,6 +45,7 @@ #include "pickit2.h" #include "ppi.h" #include "serbb.h" +#include "serialupdi.h" #include "stk500.h" #include "stk500generic.h" #include "stk500v2.h" @@ -87,6 +88,7 @@ const PROGRAMMER_TYPE programmers_types[] = { {"par", par_initpgm, par_desc}, {"pickit2", pickit2_initpgm, pickit2_desc}, {"serbb", serbb_initpgm, serbb_desc}, + {"serialupdi", serialupdi_initpgm, serialupdi_desc}, {"stk500", stk500_initpgm, stk500_desc}, {"stk500generic", stk500generic_initpgm, stk500generic_desc}, {"stk500v2", stk500v2_initpgm, stk500v2_desc}, From 8f67f9c50bb2899a679f1724ea78e479f38ab491 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Wed, 8 Dec 2021 14:18:21 +0000 Subject: [PATCH 03/12] Implemented byte and page read operations git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1514 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/serialupdi.c | 26 ++++++++++++++++++++------ avrdude/updi_readwrite.c | 2 +- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c index 0f4e70c1..5da521ea 100644 --- a/avrdude/serialupdi.c +++ b/avrdude/serialupdi.c @@ -195,19 +195,33 @@ static int serialupdi_chip_erase(PROGRAMMER * pgm, AVRPART * p) static int serialupdi_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, unsigned long addr, unsigned char * value) { -// avrdude_message(MSG_INFO, "%s: error: read byte not implemented yet\n", -// progname); return updi_read_byte(pgm, mem->offset + addr, value); -// return -1; } static int serialupdi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned int page_size, unsigned int addr, unsigned int n_bytes) { - avrdude_message(MSG_INFO, "%s: error: paged load not implemented yet\n", - progname); - return -1; + if (n_bytes > m->readsize) { + unsigned int read_offset = addr; + unsigned int remaining_bytes = n_bytes; + 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); + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: Paged load operation failed\n", progname); + return rc; + } else { + read_bytes+=rc; + read_offset+=m->readsize; + remaining_bytes-=m->readsize; + } + } + return read_bytes; + } else { + return updi_read_data(pgm, m->offset + addr, m->buf, n_bytes); + } } static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, diff --git a/avrdude/updi_readwrite.c b/avrdude/updi_readwrite.c index f36015b8..eecfb7f0 100644 --- a/avrdude/updi_readwrite.c +++ b/avrdude/updi_readwrite.c @@ -153,7 +153,7 @@ int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_ # Do the read(s) return self.datalink.ld_ptr_inc(size) */ - avrdude_message(MSG_DEBUG, "%s: Reading %d bytes from 0x%06X", progname, size, address); + avrdude_message(MSG_DEBUG, "%s: Reading %d bytes from 0x%06X\n", progname, size, address); if (size > UPDI_MAX_REPEAT_SIZE) { avrdude_message(MSG_INFO, "%s: Can't read that many bytes in one go\n", progname); From c6902553be5a102091a626a4b154278b696cceb7 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Sat, 11 Dec 2021 22:22:38 +0000 Subject: [PATCH 04/12] First successful programming git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1516 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/serialupdi.c | 401 +++++++++++++++++++++++++++++++++++++++++-- avrdude/updi_nvm.c | 20 +-- 2 files changed, 392 insertions(+), 29 deletions(-) diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c index 5da521ea..2db77875 100644 --- a/avrdude/serialupdi.c +++ b/avrdude/serialupdi.c @@ -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; diff --git a/avrdude/updi_nvm.c b/avrdude/updi_nvm.c index df8cc02c..f3d899a9 100644 --- a/avrdude/updi_nvm.c +++ b/avrdude/updi_nvm.c @@ -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; From e941d4d3f1796f7bca429c5ea8c7e2337304c21a Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Sun, 12 Dec 2021 20:00:23 +0000 Subject: [PATCH 05/12] Implemented faster flash programming method git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1517 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/serialupdi.c | 2 + avrdude/updi_link.c | 103 +++++++++++++++++++++++++++++++++++++++ avrdude/updi_link.h | 1 + avrdude/updi_readwrite.c | 6 +-- 4 files changed, 107 insertions(+), 5 deletions(-) diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c index 2db77875..98b09b32 100644 --- a/avrdude/serialupdi.c +++ b/avrdude/serialupdi.c @@ -489,6 +489,7 @@ static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, 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 { + avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes); rc = -1; } @@ -508,6 +509,7 @@ static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, } else if (strcmp(m->desc, "flash")==0) { rc = updi_nvm_write_flash(pgm, p, m->offset+addr, m->buf+addr, n_bytes); } else { + avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes); rc = -1; } return rc; diff --git a/avrdude/updi_link.c b/avrdude/updi_link.c index 33c3da1d..a3852bfe 100644 --- a/avrdude/updi_link.c +++ b/avrdude/updi_link.c @@ -526,6 +526,109 @@ int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t wo return 0; } +int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize) { +/* + def st_ptr_inc16_RSD(self, data, blocksize): + """ + Store a 16-bit word value to the pointer location with pointer post-increment + :param data: data to store + :blocksize: max number of bytes being sent -1 for all. + Warning: This does not strictly honor blocksize for values < 6 + We always glob together the STCS(RSD) and REP commands. + But this should pose no problems for compatibility, because your serial adapter can't deal with 6b chunks, + none of pymcuprog would work! + """ + self.logger.debug("ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d", len(data), blocksize) + + #for performance we glob everything together into one USB transfer.... + repnumber= ((len(data) >> 1) -1) + data = [*data, *[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x06]] + + if blocksize == -1 : + # Send whole thing at once stcs + repeat + st + (data + stcs) + blocksize = 3 + 3 + 2 + len(data) + num = 0 + firstpacket = [] + if blocksize < 10 : + # very small block size - we send pair of 2-byte commands first. + firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x0E], + *[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)]] + data = [*[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC |constants.UPDI_DATA_16], *data] + num = 0 + else: + firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA , 0x0E], + *[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)], + *[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_16], + *data[:blocksize - 8]] + num = blocksize - 8 + self.updi_phy.send( firstpacket ) + + # if finite block size, this is used. + while num < len(data): + data_slice = data[num:num+blocksize] + self.updi_phy.send(data_slice) + num += len(data_slice) +*/ + avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d\n", progname, words * 2, blocksize); + + unsigned int temp_buffer_size = 3 + 3 + 2 + (words * 2) + 3; + unsigned int num=0; + unsigned char* temp_buffer = malloc(temp_buffer_size); + + if (temp_buffer == 0) { + avrdude_message(MSG_INFO, "%s: Allocating temporary buffer failed\n", progname); + return -1; + } + + if (blocksize == -1) { + blocksize = temp_buffer_size; + } + + temp_buffer[0] = UPDI_PHY_SYNC; + temp_buffer[1] = UPDI_STCS | UPDI_CS_CTRLA; + temp_buffer[2] = 0x0E; + temp_buffer[3] = UPDI_PHY_SYNC; + temp_buffer[4] = UPDI_REPEAT | UPDI_REPEAT_BYTE; + temp_buffer[5] = (words - 1) & 0xFF; + temp_buffer[6] = UPDI_PHY_SYNC; + temp_buffer[7] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16; + + memcpy(temp_buffer + 8, buffer, words * 2); + + temp_buffer[temp_buffer_size-3] = UPDI_PHY_SYNC; + temp_buffer[temp_buffer_size-2] = UPDI_STCS | UPDI_CS_CTRLA; + temp_buffer[temp_buffer_size-1] = 0x06; + + if (blocksize < 10) { + if (updi_physical_send(pgm, temp_buffer, 6) < 0) { + avrdude_message(MSG_INFO, "%s: Failed to send first package\n", progname); + free(temp_buffer); + return -1; + } + num = 6; + } + + while (num < temp_buffer_size) { + int next_package_size; + + if (num + blocksize > temp_buffer_size) { + next_package_size = temp_buffer_size - num; + } else { + next_package_size = blocksize; + } + + if (updi_physical_send(pgm, temp_buffer + num, next_package_size) < 0) { + avrdude_message(MSG_INFO, "%s: Failed to send package\n", progname); + free(temp_buffer); + return -1; + } + + num+=next_package_size; + } + free(temp_buffer); + return 0; +} + int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats) { /* diff --git a/avrdude/updi_link.h b/avrdude/updi_link.h index e6848b0d..5b5e5bda 100644 --- a/avrdude/updi_link.h +++ b/avrdude/updi_link.h @@ -42,6 +42,7 @@ int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); +int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize); int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats); int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size); diff --git a/avrdude/updi_readwrite.c b/avrdude/updi_readwrite.c index eecfb7f0..e304a510 100644 --- a/avrdude/updi_readwrite.c +++ b/avrdude/updi_readwrite.c @@ -312,9 +312,5 @@ int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); return -1; } - if (updi_link_repeat(pgm, size >> 1) < 0) { - avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); - return -1; - } - return updi_link_st_ptr_inc16(pgm, buffer, size); + return updi_link_st_ptr_inc16_RSD(pgm, buffer, size >> 1, -1); } From 9ff14b7a4211ad114f126b8347a45106a5127fd8 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Mon, 13 Dec 2021 08:49:29 +0000 Subject: [PATCH 06/12] Fix candidate for issue with atmega4809 git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1518 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/serialupdi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c index 98b09b32..2aa0252a 100644 --- a/avrdude/serialupdi.c +++ b/avrdude/serialupdi.c @@ -461,7 +461,7 @@ static int serialupdi_paged_load(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, } return read_bytes; } else { - return updi_read_data(pgm, m->offset + addr, m->buf, n_bytes); + return updi_read_data(pgm, m->offset + addr, m->buf + addr, n_bytes); } } From 189f829c3f5b6d19c5a493a119543d1f9c262935 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Mon, 13 Dec 2021 10:59:46 +0000 Subject: [PATCH 07/12] Fix candidate for write fuse operation git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1519 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/serialupdi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c index 2aa0252a..15ee3275 100644 --- a/avrdude/serialupdi.c +++ b/avrdude/serialupdi.c @@ -434,6 +434,9 @@ static int serialupdi_read_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, static int serialupdi_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, unsigned long addr, unsigned char value) { + if (strstr(mem->desc, "fuse") != 0) { + return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); + } return updi_write_byte(pgm, mem->offset + addr, value); } From dc846ba7e864b9bf6218559ff21bb2e0ea3b4844 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Mon, 13 Dec 2021 11:11:27 +0000 Subject: [PATCH 08/12] Fix candidate for EEPROM writing issue git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1520 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/serialupdi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c index 15ee3275..4b3a32a9 100644 --- a/avrdude/serialupdi.c +++ b/avrdude/serialupdi.c @@ -437,6 +437,11 @@ static int serialupdi_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, if (strstr(mem->desc, "fuse") != 0) { return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); } + if (strcmp(mem->desc, "eeprom") == 0) { + unsigned char buffer[1]; + buffer[0]=value; + return updi_nvm_write_eeprom(pgm, p, mem->offset + addr, buffer, 1); + } return updi_write_byte(pgm, mem->offset + addr, value); } From c3100763cb8ca6e9bf743e95f5e1bf27474fbdc1 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Wed, 15 Dec 2021 14:00:14 +0000 Subject: [PATCH 09/12] Implemented lockbits programming and forced chip erase procedure git-svn-id: svn://svn.savannah.nongnu.org/avrdude/branches/serialupdi@1522 81a1dc3b-b13d-400b-aceb-764788c761c2 --- avrdude/serialupdi.c | 70 +++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c index 4b3a32a9..62a9d44c 100644 --- a/avrdude/serialupdi.c +++ b/avrdude/serialupdi.c @@ -238,6 +238,8 @@ static int serialupdi_in_prog_mode(PROGRAMMER * pgm, uint8_t * in_prog_mode) return 0; } +static int serialupdi_unlock(PROGRAMMER * pgm, AVRPART * p); + static int serialupdi_enter_progmode(PROGRAMMER * pgm) { /* @@ -283,8 +285,20 @@ def enter_progmode(self): 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; + avrdude_message(MSG_INFO, "%s: Checking UPDI NVM prog mode failed, attempting reset\n", progname); + if (serialupdi_leave_progmode(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: Unable to leave progmode\n", progname); + return -1; + } + if (updi_link_init(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname); + return -1; + } + avrdude_message(MSG_INFO, "%s: UPDI link initialization OK\n", progname); + if (serialupdi_in_prog_mode(pgm, &in_prog_mode) < 0) { + avrdude_message(MSG_INFO, "%s: Checking UPDI NVM prog mode failed again, exiting\n", progname); + return -1; + } } if (in_prog_mode) { avrdude_message(MSG_DEBUG, "%s: Already in prog mode\n", progname); @@ -321,7 +335,16 @@ def enter_progmode(self): 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 (!ovsigck) { + return -1; + } + if (serialupdi_unlock(pgm, 0x00) < 0) { + return -1; + } + if (updi_link_init(pgm) < 0) { + return -1; + } + return serialupdi_enter_progmode(pgm); } if (serialupdi_in_prog_mode(pgm, &in_prog_mode) < 0) { @@ -335,6 +358,18 @@ def enter_progmode(self): } avrdude_message(MSG_DEBUG, "%s: Entered NVM programming mode\n", progname); + + updi_sib_info * sib_info = updi_get_sib_info(pgm); + + if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) { + avrdude_message(MSG_INFO, "%s: Read SIB operation failed\n", progname); + return -1; + } + if (serialupdi_decode_sib(pgm, sib_info) < 0) { + avrdude_message(MSG_INFO, "%s: Decode SIB_INFO failed\n", progname); + return -1; + } + return 0; } @@ -368,21 +403,11 @@ static int serialupdi_leave_progmode(PROGRAMMER * pgm) static int serialupdi_initialize(PROGRAMMER * pgm, AVRPART * p) { - updi_sib_info * sib_info = updi_get_sib_info(pgm); - if (updi_link_init(pgm) < 0) { avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname); return -1; } avrdude_message(MSG_INFO, "%s: UPDI link initialization OK\n", progname); - if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) { - avrdude_message(MSG_INFO, "%s: Read SIB operation failed\n", progname); - return -1; - } - if (serialupdi_decode_sib(pgm, sib_info) < 0) { - avrdude_message(MSG_INFO, "%s: Decode SIB_INFO failed\n", progname); - return -1; - } if (serialupdi_enter_progmode(pgm) < 0) { avrdude_message(MSG_INFO, "%s: Unable to enter NVM programming mode\n", progname); return -1; @@ -437,11 +462,19 @@ static int serialupdi_write_byte(PROGRAMMER * pgm, AVRPART * p, AVRMEM * mem, if (strstr(mem->desc, "fuse") != 0) { return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); } + if (strcmp(mem->desc, "lock") == 0) { + return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); + } if (strcmp(mem->desc, "eeprom") == 0) { unsigned char buffer[1]; buffer[0]=value; return updi_nvm_write_eeprom(pgm, p, mem->offset + addr, buffer, 1); } + if (strcmp(mem->desc, "flash") == 0) { + unsigned char buffer[1]; + buffer[0]=value; + return updi_nvm_write_flash(pgm, p, mem->offset + addr, buffer, 1); + } return updi_write_byte(pgm, mem->offset + addr, value); } @@ -478,12 +511,6 @@ static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, unsigned int addr, unsigned int n_bytes) { 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; @@ -585,11 +612,6 @@ static int serialupdi_unlock(PROGRAMMER * pgm, AVRPART * p) 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); From d1dddad8969ce56837d81baa8e950722fe14c3d0 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Sat, 18 Dec 2021 16:47:37 +0100 Subject: [PATCH 10/12] Added support for writing USERROW memory --- .gitignore | 39 ++ avrdude.conf.in | 7 + serialupdi.c | 965 +++++++++++++++++++++++++++++++++++ serialupdi.h | 43 ++ updi_constants.h | 156 ++++++ updi_link.c | 930 +++++++++++++++++++++++++++++++++ updi_link.h | 59 +++ updi_nvm.c | 1275 ++++++++++++++++++++++++++++++++++++++++++++++ updi_nvm.h | 51 ++ updi_readwrite.c | 316 ++++++++++++ updi_readwrite.h | 51 ++ updi_state.c | 55 ++ updi_state.h | 85 ++++ 13 files changed, 4032 insertions(+) create mode 100644 .gitignore create mode 100644 serialupdi.c create mode 100644 serialupdi.h create mode 100644 updi_constants.h create mode 100644 updi_link.c create mode 100644 updi_link.h create mode 100644 updi_nvm.c create mode 100644 updi_nvm.h create mode 100644 updi_readwrite.c create mode 100644 updi_readwrite.h create mode 100644 updi_state.c create mode 100644 updi_state.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..bb11dcce --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +avrdude +libavrdude.a +libavrdude.la +Makefile +Makefile.in +*.o +*.lo +libtool +ltmain.sh +.deps/ +.libs/ +INSTALL +ac_cfg.h +ac_cfg.h.in +aclocal.m4 +autom4te.cache/ +avrdude.conf +avrdude.conf.tmp +avrdude.spec +compile +config.guess +config.log +config.status +config.sub +config_gram.c +config_gram.h +configure +configure~ +depcomp +doc/mdate-sh +doc/texinfo.tex +install-sh +lexer.c +missing +stamp-h1 +windows/.deps/ +ylwrap +m4/ +*.bin diff --git a/avrdude.conf.in b/avrdude.conf.in index 837c9f54..46b2da67 100644 --- a/avrdude.conf.in +++ b/avrdude.conf.in @@ -16562,6 +16562,13 @@ part readsize = 0x4; ; + memory "userrow" + size = 0x20; + offset = 0x1080; + page_size = 0x20; + readsize = 0x20; + ; + memory "data" # SRAM, only used to supply the offset offset = 0x1000000; diff --git a/serialupdi.c b/serialupdi.c new file mode 100644 index 00000000..839d7d81 --- /dev/null +++ b/serialupdi.c @@ -0,0 +1,965 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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$ */ + +/* + * Interface to the SerialUPDI programmer. + * + * 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 "serialupdi.h" +#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) +{ + if ((pgm->cookie = malloc(sizeof(updi_state))) == 0) { + avrdude_message(MSG_INFO, + "%s: serialupdi_setup(): Out of memory allocating private data\n", + progname); + exit(1); + } + memset(pgm->cookie, 0, sizeof(updi_state)); + updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT); +} + +static void serialupdi_teardown(PROGRAMMER * pgm) +{ + free(pgm->cookie); +} + +static int serialupdi_open(PROGRAMMER * pgm, char * port) +{ + strcpy(pgm->port, port); + return updi_link_open(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_reset_connection(PROGRAMMER * pgm) +{ + 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_link_init(pgm); +} + +static int serialupdi_decode_sib(PROGRAMMER * pgm, updi_sib_info * sib_info) +{ + char * str_ptr; + + sib_info->sib_string[SIB_INFO_STRING_LENGTH]=0; + avrdude_message(MSG_DEBUG, "%s: Received SIB: [%s]\n", progname, sib_info->sib_string); + memset(sib_info->family_string, 0, SIB_INFO_FAMILY_LENGTH+1); + memset(sib_info->nvm_string, 0, SIB_INFO_NVM_LENGTH+1); + memset(sib_info->debug_string, 0, SIB_INFO_DEBUG_LENGTH+1); + memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1); + memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1); + memset(sib_info->extra_string, 0, SIB_INFO_EXTRA_LENGTH+1); + + memcpy(sib_info->family_string, sib_info->sib_string, SIB_INFO_FAMILY_LENGTH); + memcpy(sib_info->nvm_string, sib_info->sib_string + 8, SIB_INFO_NVM_LENGTH); + memcpy(sib_info->debug_string, sib_info->sib_string + 11, SIB_INFO_DEBUG_LENGTH); + memcpy(sib_info->pdi_string, sib_info->sib_string + 15, SIB_INFO_PDI_LENGTH); + strcpy(sib_info->extra_string, (char *)sib_info->sib_string + 19); + + str_ptr = strstr(sib_info->nvm_string, ":"); + if (!str_ptr) { + avrdude_message(MSG_INFO, "%s: Incorrect format of NVM string\n", progname); + return -1; + } + sib_info->nvm_version = *(str_ptr+1); + + str_ptr = strstr(sib_info->debug_string, ":"); + if (!str_ptr) { + avrdude_message(MSG_INFO, "%s: Incorrect format of DEBUG string\n", progname); + return -1; + } + sib_info->debug_version = *(str_ptr+1); + + avrdude_message(MSG_DEBUG, "%s: Device family ID: %s\n", progname, sib_info->family_string); + avrdude_message(MSG_DEBUG, "%s: NVM interface: %s\n", progname, sib_info->nvm_string); + avrdude_message(MSG_DEBUG, "%s: Debug interface: %s\n", progname, sib_info->debug_string); + avrdude_message(MSG_DEBUG, "%s: PDI oscillator: %s\n", progname, sib_info->pdi_string); + avrdude_message(MSG_DEBUG, "%s: Extra information: %s\n", progname, sib_info->extra_string); + 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); + return -1; + } + return 0; +} + +static void serialupdi_close(PROGRAMMER * pgm) +{ + avrdude_message(MSG_INFO, "%s: Leaving NVM programming mode\n", progname); + + if (serialupdi_leave_progmode(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: Unable to leave NVM programming mode\n", progname); + } + updi_link_close(pgm); +} + +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; +} + +typedef enum { + WAIT_FOR_UROW_LOW, + WAIT_FOR_UROW_HIGH +} urow_wait_mode; + +static int serialupdi_wait_for_urow(PROGRAMMER * pgm, unsigned int ms, urow_wait_mode mode) { +/* + def wait_urow_prog(self, timeout_ms, wait_for_high): + """ + Waits for the device to be in user row write mode + User row is writeable on a locked device using this mechanism + + :param timeout_ms: number of milliseconds to wait + :param wait_for_high: set True to wait for bit to go high; False to wait for low + """ + timeout = Timeout(timeout_ms) + + while not timeout.expired(): + status = self.readwrite.read_cs(constants.UPDI_ASI_SYS_STATUS) + if wait_for_high: + if status & (1 << constants.UPDI_ASI_SYS_STATUS_UROWPROG): + return True + else: + if not status & (1 << constants.UPDI_ASI_SYS_STATUS_UROWPROG): + return True + + self.logger.error("Timeout waiting for device to enter UROW WRITE mode") + 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 (mode == WAIT_FOR_UROW_HIGH) { + if (status & (1 << UPDI_ASI_SYS_STATUS_UROWPROG)) { + return 0; + } + } else { + if (!(status & (1 << UPDI_ASI_SYS_STATUS_UROWPROG))) { + 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 complete UROW WRITE\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; + } + + 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)) +*/ + 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_write_userrow(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + unsigned int page_size, + unsigned int addr, unsigned int n_bytes) +{ + /* + def write_user_row_locked_device(self, address, data): + """ + Writes data to the user row when the device is locked, using a key. + """ + # Put in the key + self.readwrite.write_key(constants.UPDI_KEY_64, constants.UPDI_KEY_UROW) + + # 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_UROWWRITE): + raise PymcuprogError("Key not accepted") + + # Toggle reset + self.reset(apply_reset=True) + self.reset(apply_reset=False) + + # Wait for mode to be entered + if not self.wait_urow_prog(500, wait_for_high=True): + raise PymcuprogError("Failed to enter urow write mode using key") + + # At this point we can write one 'page' to the device, and have it transfered into the user row + # Transfer data + self.readwrite.write_data(address, data) + + # Finalize + self.readwrite.write_cs(constants.UPDI_ASI_SYS_CTRLA, + (1 << constants.UPDI_ASI_SYS_CTRLA_UROW_FINAL) | + (1 << constants.UPDI_CTRLB_CCDETDIS_BIT)) + + # Wait for mode to be exited + if not self.wait_urow_prog(500, wait_for_high=False): + # Toggle reset + self.reset(apply_reset=True) + self.reset(apply_reset=False) + raise PymcuprogError("Failed to exit urow write mode") + + # Clear status + self.readwrite.write_cs(constants.UPDI_ASI_KEY_STATUS, + (1 << constants.UPDI_ASI_KEY_STATUS_UROWWRITE) | + (1 << constants.UPDI_CTRLB_CCDETDIS_BIT)) + + # Toggle reset + self.reset(apply_reset=True) + self.reset(apply_reset=False) + */ + unsigned char buffer[8]; + uint8_t key_status; + + memcpy(buffer, UPDI_KEY_UROW, sizeof(buffer)); + if (updi_write_key(pgm, buffer, UPDI_KEY_64, sizeof(buffer)) < 0) { + avrdude_message(MSG_INFO, "%s: Writing USERROW 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_UROWWRITE))) { + 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_urow(pgm, 500, WAIT_FOR_UROW_HIGH) < 0) { + avrdude_message(MSG_INFO, "%s: Failed to enter USERROW programming mode\n", progname); + return -1; + } + + if (updi_write_data(pgm, m->offset+addr, m->buf + addr, n_bytes) < 0) { + avrdude_message(MSG_INFO, "%s: Writing USER ROW failed\n", progname); + return -1; + } + + if (updi_write_cs(pgm, UPDI_ASI_SYS_CTRLA, (1 << UPDI_ASI_SYS_CTRLA_UROW_FINAL) | + (1 << UPDI_CTRLB_CCDETDIS_BIT)) < 0) { + avrdude_message(MSG_INFO, "%s: Failed trying to commit user row write\n", progname); + return -1; + } + + if (serialupdi_wait_for_urow(pgm, 500, WAIT_FOR_UROW_LOW) < 0) { + avrdude_message(MSG_DEBUG, "%s: Failed to exit USERROW 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; + } + } + + if (updi_write_cs(pgm, UPDI_ASI_KEY_STATUS, (1 << UPDI_ASI_KEY_STATUS_UROWWRITE) | + (1 << UPDI_CTRLB_CCDETDIS_BIT)) < 0) { + avrdude_message(MSG_INFO, "%s: Failed trying to complete user row write\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; + } + + serialupdi_reset_connection(pgm); + + serialupdi_enter_progmode(pgm); + + return 0; +} + +static int serialupdi_initialize(PROGRAMMER * pgm, AVRPART * p) +{ + uint8_t value; + uint8_t reset_link_required=0; + + if (updi_link_init(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname); + return -1; + } + avrdude_message(MSG_INFO, "%s: UPDI link initialization OK\n", progname); + + if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) { + + /* let's try reset the connection */ + if (!serialupdi_reset_connection(pgm)) { + return -1; + } + + if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) { + avrdude_message(MSG_INFO, "%s: Read CS operation during initialization failed\n", progname); + return -1; + } + } + if (value & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS)) { + avrdude_message(MSG_INFO, "%s: Device is locked\n", progname); + } + if (value & (1 << UPDI_ASI_SYS_STATUS_UROWPROG)) { + avrdude_message(MSG_INFO, "%s: Device in USER ROW programming state, leaving programming mode\n", progname); + reset_link_required = 1; + } + if (value & (1 << UPDI_ASI_SYS_STATUS_NVMPROG)) { + avrdude_message(MSG_INFO, "%s: Device in NVM programming state, leaving programming mode\n", progname); + reset_link_required = 1; + } + if (value & (1 << UPDI_ASI_SYS_STATUS_INSLEEP)) { + avrdude_message(MSG_INFO, "%s: Device is in SLEEP mode\n", progname); + } + if (value & (1 << UPDI_ASI_SYS_STATUS_RSTSYS)) { + avrdude_message(MSG_INFO, "%s: Device in reset status, trying to release it\n", progname); + if (serialupdi_reset(pgm, RELEASE_RESET)<0) { + return -1; + } + } + if (reset_link_required) { + if (serialupdi_reset_connection(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI link reset failed\n", progname); + return -1; + } + } + + updi_sib_info * sib_info = updi_get_sib_info(pgm); + + if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) { + /* this should never happen, let's try to reset connection and try again */ + if (serialupdi_reset_connection(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: SerialUPDI reset connection failed\n", progname); + return -1; + } + if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) { + avrdude_message(MSG_INFO, "%s: Read SIB operation failed\n", progname); + return -1; + } + } + if (serialupdi_decode_sib(pgm, sib_info) < 0) { + avrdude_message(MSG_INFO, "%s: Decode SIB_INFO failed\n", progname); + return -1; + } + + if (updi_link_init(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname); + return -1; + } + + avrdude_message(MSG_INFO, "%s: Entering NVM programming mode\n", progname); + /* try, but ignore failure */ + serialupdi_enter_progmode(pgm); + + return 0; +} + +static void serialupdi_disable(PROGRAMMER * pgm) +{ + /* Do nothing. */ + + return; +} + +static void serialupdi_enable(PROGRAMMER * pgm) +{ + /* Do nothing. */ + + return; +} + +static void serialupdi_display(PROGRAMMER * pgm, const char * p) +{ + return; +} + +static int serialupdi_cmd(PROGRAMMER * pgm, const unsigned char * cmd, + unsigned char * res) +{ + avrdude_message(MSG_INFO, "%s: error: cmd %s[%s] not implemented yet\n", + progname, cmd, res); + return -1; +} + +static int serialupdi_program_enable(PROGRAMMER * pgm, AVRPART * p) +{ + avrdude_message(MSG_INFO, "%s: error: program enable 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) +{ + if (strstr(mem->desc, "fuse") != 0) { + return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); + } + if (strcmp(mem->desc, "lock") == 0) { + return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); + } + if (strcmp(mem->desc, "eeprom") == 0) { + unsigned char buffer[1]; + buffer[0]=value; + return updi_nvm_write_eeprom(pgm, p, mem->offset + addr, buffer, 1); + } + if (strcmp(mem->desc, "flash") == 0) { + unsigned char buffer[1]; + buffer[0]=value; + return updi_nvm_write_flash(pgm, p, mem->offset + addr, buffer, 1); + } + 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) +{ + if (n_bytes > m->readsize) { + unsigned int read_offset = addr; + unsigned int remaining_bytes = n_bytes; + int read_bytes = 0; + int rc; + while (remaining_bytes > 0) { + 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; + } else { + read_bytes+=rc; + read_offset+=m->readsize; + remaining_bytes-=m->readsize; + } + } + return read_bytes; + } else { + return updi_read_data(pgm, m->offset + addr, m->buf + addr, n_bytes); + } +} + +static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, + unsigned int page_size, + unsigned int addr, unsigned int n_bytes) +{ + int rc; + 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 if (strcmp(m->desc, "userrow")==0) { + rc = serialupdi_write_userrow(pgm, p, m, page_size, write_offset, + remaining_bytes > m->page_size ? m->page_size : remaining_bytes); + } else if (strcmp(m->desc, "fuses")==0) { + avrdude_message(MSG_DEBUG, "%s: Page write operation requested for fuses, falling back to byte-level write\n", progname); + return -1; + } else { + avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes); + 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 if (strcmp(m->desc, "userrow")==0) { + rc = serialupdi_write_userrow(pgm, p, m, page_size, addr, n_bytes); + } else if (strcmp(m->desc, "fuses")==0) { + avrdude_message(MSG_DEBUG, "%s: Page write operation requested for fuses, falling back to byte-level write\n", progname); + rc = -1; + } else { + avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes); + 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; + } + + if (serialupdi_wait_for_unlock(pgm, 500) < 0) { + avrdude_message(MSG_INFO, "%s: Waiting for unlock failed\n", progname); + return -1; + } + + if (updi_link_init(pgm) < 0) { + avrdude_message(MSG_INFO, "%s: UPDI link reinitialization failed\n", progname); + return -1; + } + + return serialupdi_enter_progmode(pgm); +} + +static int serialupdi_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + uint8_t value; + + if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) { + avrdude_message(MSG_INFO, "%s: Read CS operation during chip erase failed\n", progname); + return -1; + } + + if (value & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS)) { + avrdude_message(MSG_INFO, "%s: Device is locked\n", progname); + if (ovsigck) { + avrdude_message(MSG_INFO, "%s: Attempting device erase\n", progname); + return serialupdi_unlock(pgm, p); + } + } else { + return updi_nvm_chip_erase(pgm, p); + } + return -1; +} + +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; +} + +static int serialupdi_read_signature(PROGRAMMER * pgm, AVRPART *p, AVRMEM *m) { + + uint8_t value; + + if (updi_read_cs(pgm, UPDI_ASI_SYS_STATUS, &value)<0) { + avrdude_message(MSG_INFO, "%s: Read CS operation during signature read failed\n", progname); + return -1; + } + + if (value & (1 << UPDI_ASI_SYS_STATUS_LOCKSTATUS)) { + m->buf[0]=0x00; + m->buf[1]=0x00; + m->buf[2]=0x00; + } else { + updi_read_byte(pgm, m->offset + 0, m->buf); + updi_read_byte(pgm, m->offset + 1, m->buf+1); + updi_read_byte(pgm, m->offset + 2, m->buf+2); + } + + return 3; +} + +static int serialupdi_read_sib(PROGRAMMER * pgm, AVRPART *p, char *sib) { + + updi_sib_info * sib_info = updi_get_sib_info(pgm); + + memcpy(sib, sib_info->sib_string, 32); + + return 0; +} + + +void serialupdi_initpgm(PROGRAMMER * pgm) +{ + strcpy(pgm->type, "serialupdi"); + + /* + * mandatory functions + */ + + pgm->initialize = serialupdi_initialize; + pgm->display = serialupdi_display; + pgm->enable = serialupdi_enable; + pgm->disable = serialupdi_disable; + pgm->program_enable = serialupdi_program_enable; + pgm->chip_erase = serialupdi_chip_erase; + pgm->cmd = serialupdi_cmd; + pgm->open = serialupdi_open; + pgm->close = serialupdi_close; + pgm->read_byte = serialupdi_read_byte; + pgm->write_byte = serialupdi_write_byte; + + /* + * optional functions + */ + + pgm->unlock = serialupdi_unlock; + pgm->paged_write = serialupdi_paged_write; + pgm->read_sig_bytes = serialupdi_read_signature; + pgm->read_sib = serialupdi_read_sib; + pgm->paged_load = serialupdi_paged_load; + pgm->page_erase = serialupdi_page_erase; + pgm->setup = serialupdi_setup; + pgm->teardown = serialupdi_teardown; + +} + +const char serialupdi_desc[] = "Driver for SerialUPDI programmers"; diff --git a/serialupdi.h b/serialupdi.h new file mode 100644 index 00000000..ff7270d5 --- /dev/null +++ b/serialupdi.h @@ -0,0 +1,43 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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 serialupdi_h +#define serialupdi_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const char serialupdi_desc[]; +void serialupdi_initpgm (PROGRAMMER * pgm); + +#ifdef __cplusplus +} +#endif + +#endif /* serialupdi_h */ diff --git a/updi_constants.h b/updi_constants.h new file mode 100644 index 00000000..ff8a446f --- /dev/null +++ b/updi_constants.h @@ -0,0 +1,156 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_constants_h +#define updi_constants_h + +#define UPDI_BREAK 0x00 + +#define UPDI_LDS 0x00 +#define UPDI_STS 0x40 +#define UPDI_LD 0x20 +#define UPDI_ST 0x60 +#define UPDI_LDCS 0x80 +#define UPDI_STCS 0xC0 +#define UPDI_REPEAT 0xA0 +#define UPDI_KEY 0xE0 + +#define UPDI_PTR 0x00 +#define UPDI_PTR_INC 0x04 +#define UPDI_PTR_ADDRESS 0x08 + +#define UPDI_ADDRESS_8 0x00 +#define UPDI_ADDRESS_16 0x04 +#define UPDI_ADDRESS_24 0x08 + +#define UPDI_DATA_8 0x00 +#define UPDI_DATA_16 0x01 +#define UPDI_DATA_24 0x02 + +#define UPDI_KEY_SIB 0x04 +#define UPDI_KEY_KEY 0x00 + +#define UPDI_KEY_64 0x00 +#define UPDI_KEY_128 0x01 +#define UPDI_KEY_256 0x02 + +#define UPDI_SIB_8BYTES UPDI_KEY_64 +#define UPDI_SIB_16BYTES UPDI_KEY_128 +#define UPDI_SIB_32BYTES UPDI_KEY_256 + +#define UPDI_REPEAT_BYTE 0x00 +#define UPDI_REPEAT_WORD 0x01 + +#define UPDI_PHY_SYNC 0x55 +#define UPDI_PHY_ACK 0x40 + +#define UPDI_MAX_REPEAT_SIZE (0xFF+1) // Repeat counter of 1-byte, with off-by-one counting + +//# CS and ASI Register Address map +#define UPDI_CS_STATUSA 0x00 +#define UPDI_CS_STATUSB 0x01 +#define UPDI_CS_CTRLA 0x02 +#define UPDI_CS_CTRLB 0x03 +#define UPDI_ASI_KEY_STATUS 0x07 +#define UPDI_ASI_RESET_REQ 0x08 +#define UPDI_ASI_CTRLA 0x09 +#define UPDI_ASI_SYS_CTRLA 0x0A +#define UPDI_ASI_SYS_STATUS 0x0B +#define UPDI_ASI_CRC_STATUS 0x0C + +#define UPDI_CTRLA_IBDLY_BIT 7 +#define UPDI_CTRLB_CCDETDIS_BIT 3 +#define UPDI_CTRLB_UPDIDIS_BIT 2 + +#define UPDI_KEY_NVM "NVMProg " +#define UPDI_KEY_CHIPERASE "NVMErase" +#define UPDI_KEY_UROW "NVMUs&te" + +#define UPDI_ASI_STATUSA_REVID 4 +#define UPDI_ASI_STATUSB_PESIG 0 + +#define UPDI_ASI_KEY_STATUS_CHIPERASE 3 +#define UPDI_ASI_KEY_STATUS_NVMPROG 4 +#define UPDI_ASI_KEY_STATUS_UROWWRITE 5 + +#define UPDI_ASI_SYS_STATUS_RSTSYS 5 +#define UPDI_ASI_SYS_STATUS_INSLEEP 4 +#define UPDI_ASI_SYS_STATUS_NVMPROG 3 +#define UPDI_ASI_SYS_STATUS_UROWPROG 2 +#define UPDI_ASI_SYS_STATUS_LOCKSTATUS 0 + +#define UPDI_ASI_SYS_CTRLA_UROW_FINAL 1 + +#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/updi_link.c b/updi_link.c new file mode 100644 index 00000000..b1af2b87 --- /dev/null +++ b/updi_link.c @@ -0,0 +1,930 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_link.h" +#include "updi_constants.h" +#include "updi_state.h" + +#include + +void msleep(int tms) +{ + struct timeval tv; + tv.tv_sec = tms / 1000; + tv.tv_usec = (tms % 1000) * 1000; + select (0, NULL, NULL, NULL, &tv); +} + +static int updi_physical_open(PROGRAMMER* pgm, int baudrate, unsigned long cflags) +{ + serial_recv_timeout = 100; + union pinfo pinfo; + + pinfo.serialinfo.baud = baudrate; + pinfo.serialinfo.cflags = cflags; + + avrdude_message(MSG_DEBUG, "%s: Opening serial port...\n", progname); + + if (serial_open(pgm->port, pinfo, &pgm->fd)==-1) { + + avrdude_message(MSG_DEBUG, "%s: Serial port open failed!\n", progname); + return -1; + } + + /* + * drain any extraneous input + */ + serial_drain(&pgm->fd, 0); + + return 0; +} + +static void updi_physical_close(PROGRAMMER* pgm) +{ + serial_close(&pgm->fd); + pgm->fd.ifd = -1; +} + +static int updi_physical_send(PROGRAMMER * pgm, unsigned char * buf, size_t len) +{ + size_t i; + int rv; + + avrdude_message(MSG_DEBUG, "%s: Sending %lu bytes [", progname, len); + for (i=0; ifd, buf, len); + serial_recv(&pgm->fd, buf, len); + return rv; +} + +static int updi_physical_recv(PROGRAMMER * pgm, unsigned char * buf, size_t len) +{ + size_t i; + int rv; + + rv = serial_recv(&pgm->fd, buf, len); + if (rv < 0) { + avrdude_message(MSG_DEBUG, + "%s: serialupdi_recv(): programmer is not responding\n", + progname); + return -1; + } + + avrdude_message(MSG_DEBUG, "%s: Received %lu bytes [", progname, len); + for (i=0; ifd, buffer, 1); + serial_recv(&pgm->fd, buffer, 1); + + msleep(100); + + buffer[0] = UPDI_BREAK; + + serial_send(&pgm->fd, buffer, 1); + serial_recv(&pgm->fd, buffer, 1); + + updi_physical_close(pgm); + + return updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, SERIAL_8E2); +} + +int updi_physical_sib(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size) +{ +/* + def sib(self): + """ + System information block is just a string coming back from a SIB command + """ + self.send([ + constants.UPDI_PHY_SYNC, + constants.UPDI_KEY | constants.UPDI_KEY_SIB | constants.UPDI_SIB_32BYTES]) + return self.ser.readline() +*/ + unsigned char send_buffer[2]; + + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_KEY | UPDI_KEY_SIB | UPDI_SIB_32BYTES; + + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_DEBUG, "%s: SIB request send failed\n", progname); + return -1; + } + + return updi_physical_recv(pgm, buffer, size); +} + +int updi_link_open(PROGRAMMER * pgm) +{ + if (updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, SERIAL_8E2) < 0) { + return -1; + } + return updi_physical_send_double_break(pgm); +} + +void updi_link_close(PROGRAMMER * pgm) +{ + updi_physical_close(pgm); +} + +static int updi_link_init_session_parameters(PROGRAMMER * pgm) +{ +/* + def _init_session_parameters(self): + """ + Set the inter-byte delay bit and disable collision detection + """ + self.stcs(constants.UPDI_CS_CTRLB, 1 << constants.UPDI_CTRLB_CCDETDIS_BIT) + self.stcs(constants.UPDI_CS_CTRLA, 1 << constants.UPDI_CTRLA_IBDLY_BIT) +*/ + if (updi_link_stcs(pgm, UPDI_CS_CTRLB, 1 << UPDI_CTRLB_CCDETDIS_BIT) < 0) { + return -1; + } + + if (updi_link_stcs(pgm, UPDI_CS_CTRLA, 1 << UPDI_CTRLA_IBDLY_BIT) < 0) { + return -1; + } + + return 0; +} + +static int updi_link_check(PROGRAMMER * pgm) +{ +/* + def _check_datalink(self): + """ + Check UPDI by loading CS STATUSA + """ + try: + if self.ldcs(constants.UPDI_CS_STATUSA) != 0: + self.logger.info("UPDI init OK") + return True + except PymcuprogError: + self.logger.warning("Check failed") + return False + self.logger.info("UPDI not OK - reinitialisation required") + return False +*/ + int result; + uint8_t value; + result = updi_link_ldcs(pgm, UPDI_CS_STATUSA, &value); + if (result < 0) { + avrdude_message(MSG_DEBUG, "%s: Check failed\n", progname); + return -1; + } else { + if (value > 0) { + avrdude_message(MSG_DEBUG, "%s: UDPI init OK\n", progname); + return 0; + } else { + avrdude_message(MSG_DEBUG, "%s: UDPI not OK - reinitialisation required\n", progname); + return -1; + } + } +} + + +int updi_link_init(PROGRAMMER * pgm) +{ +/* + def init_datalink(self): + """ + Init DL layer + """ + self._init_session_parameters() + # Check + if not self._check_datalink(): + # Send double break if all is not well, and re-check + self.updi_phy.send_double_break() + self._init_session_parameters() + if not self._check_datalink(): + raise PymcuprogError("UPDI initialisation failed") +*/ + if (updi_link_init_session_parameters(pgm) < 0) { + avrdude_message(MSG_DEBUG, "%s: Session initialisation failed\n", progname); + return -1; + } + + if (updi_link_check(pgm) < 0) { + avrdude_message(MSG_DEBUG, "%s: Datalink not active, resetting...\n", progname); + if (updi_physical_send_double_break(pgm) < 0) { + avrdude_message(MSG_DEBUG, "%s: Datalink initialisation failed\n", progname); + return -1; + } + if (updi_link_init_session_parameters(pgm) < 0) { + avrdude_message(MSG_DEBUG, "%s: Session initialisation failed\n", progname); + return -1; + } + if (updi_link_check(pgm) < 0) { + avrdude_message(MSG_DEBUG, "%s: Restoring datalink failed\n", progname); + return -1; + } + } + return 0; +} + +int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value) +{ +/* + def ldcs(self, address): + """ + Load data from Control/Status space + + :param address: address to load + """ + self.logger.debug("LDCS from 0x%02X", address) + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LDCS | (address & 0x0F)]) + response = self.updi_phy.receive(self.LDCS_RESPONSE_BYTES) + numbytes_received = len(response) + if numbytes_received != self.LDCS_RESPONSE_BYTES: + raise PymcuprogError("Unexpected number of bytes in response: " + "{} byte(s) expected {} byte(s)".format(numbytes_received, self.LDCS_RESPONSE_BYTES)) + + return response[0] +*/ + unsigned char buffer[2]; + int result; + avrdude_message(MSG_DEBUG, "%s: LDCS from 0x%02X\n", progname, address); + buffer[0]=UPDI_PHY_SYNC; + buffer[1]=UPDI_LDCS | (address & 0x0F); + if (updi_physical_send(pgm, buffer, 2) < 0) { + avrdude_message(MSG_DEBUG, "%s: LDCS send operation failed\n", progname); + return -1; + } + result = updi_physical_recv(pgm, buffer, 1); + if (result != 1) { + if (result >= 0) { + avrdude_message(MSG_DEBUG, "%s: Incorrect response size, received %d instead of %d bytes\n", progname, result, 1); + } + return -1; + } + * value = buffer[0]; + return 0; +} + +int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value) +{ +/* + def stcs(self, address, value): + """ + Store a value to Control/Status space + + :param address: address to store to + :param value: value to write + """ + self.logger.debug("STCS to 0x%02X", address) + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_STCS | (address & 0x0F), value]) +*/ + unsigned char buffer[3]; + avrdude_message(MSG_DEBUG, "%s: STCS 0x%02X to address 0x%02X\n", progname, value, address); + buffer[0] = UPDI_PHY_SYNC; + buffer[1] = UPDI_STCS | (address & 0x0F); + buffer[2] = value; + return updi_physical_send(pgm, buffer, 3); +} + +int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def ld_ptr_inc(self, size): + """ + Loads a number of bytes from the pointer location with pointer post-increment + + :param size: number of bytes to load + :return: values read + """ + self.logger.debug("LD8 from ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC | + constants.UPDI_DATA_8]) + return self.updi_phy.receive(size) +*/ + unsigned char send_buffer[2]; + avrdude_message(MSG_DEBUG, "%s: LD8 from ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_8; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_DEBUG, "%s: LD_PTR_INC send operation failed\n", progname); + return -1; + } + return updi_physical_recv(pgm, buffer, size); +} + +int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words) +{ +/* + def ld_ptr_inc16(self, words): + """ + Load a 16-bit word value from the pointer location with pointer post-increment + + :param words: number of words to load + :return: values read + """ + self.logger.debug("LD16 from ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC | + constants.UPDI_DATA_16]) + return self.updi_phy.receive(words << 1) +*/ + unsigned char send_buffer[2]; + avrdude_message(MSG_DEBUG, "%s: LD16 from ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_16; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_DEBUG, "%s: LD_PTR_INC send operation failed\n", progname); + return -1; + } + return updi_physical_recv(pgm, buffer, words << 2); +} + +int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def st_ptr_inc(self, data): + """ + Store data to the pointer location with pointer post-increment + + :param data: data to store + """ + self.logger.debug("ST8 to *ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_8, + data[0]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("ACK error with st_ptr_inc") + + num = 1 + while num < len(data): + self.updi_phy.send([data[num]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st_ptr_inc") + num += 1 +*/ + unsigned char send_buffer[3]; + unsigned char recv_buffer[1]; + int response; + int num = 1; + avrdude_message(MSG_DEBUG, "%s: ST8 to *ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_8; + send_buffer[2] = buffer[0]; + if (updi_physical_send(pgm, send_buffer, 3) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC send operation failed\n", progname); + return -1; + } + + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_DEBUG, "%s: ACK was expected but not received\n", progname); + return -1; + } + + while (num < size) { + send_buffer[0]=buffer[num]; + if (updi_physical_send(pgm, send_buffer, 1) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC data send operation failed\n", progname); + return -1; + } + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_DEBUG, "%s: Data ACK was expected but not received\n", progname); + return -1; + } + num++; + } + + return 0; +} + +int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words) +{ +/* + def st_ptr_inc16(self, data): + """ + Store a 16-bit word value to the pointer location with pointer post-increment + + :param data: data to store + """ + self.logger.debug("ST16 to *ptr++") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | + constants.UPDI_DATA_16, data[0], data[1]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("ACK error with st_ptr_inc16") + + num = 2 + while num < len(data): + self.updi_phy.send([data[num], data[num + 1]]) + response = self.updi_phy.receive(1) + + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st_ptr_inc16") + num += 2 +*/ + unsigned char send_buffer[4]; + unsigned char recv_buffer[1]; + int response; + int num = 2; + avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++\n", progname); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16; + send_buffer[2] = buffer[0]; + send_buffer[3] = buffer[1]; + if (updi_physical_send(pgm, send_buffer, 4) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC16 send operation failed\n", progname); + return -1; + } + + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_DEBUG, "%s: ACK was expected but not received\n", progname); + return -1; + } + + while (num < words) { + send_buffer[0]=buffer[num]; + send_buffer[1]=buffer[num+1]; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR_INC data send operation failed\n", progname); + return -1; + } + response = updi_physical_recv(pgm, recv_buffer, 1); + + if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_DEBUG, "%s: Data ACK was expected but not received\n", progname); + return -1; + } + num+=2; + } + + return 0; +} + +int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize) { +/* + def st_ptr_inc16_RSD(self, data, blocksize): + """ + Store a 16-bit word value to the pointer location with pointer post-increment + :param data: data to store + :blocksize: max number of bytes being sent -1 for all. + Warning: This does not strictly honor blocksize for values < 6 + We always glob together the STCS(RSD) and REP commands. + But this should pose no problems for compatibility, because your serial adapter can't deal with 6b chunks, + none of pymcuprog would work! + """ + self.logger.debug("ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d", len(data), blocksize) + + #for performance we glob everything together into one USB transfer.... + repnumber= ((len(data) >> 1) -1) + data = [*data, *[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x06]] + + if blocksize == -1 : + # Send whole thing at once stcs + repeat + st + (data + stcs) + blocksize = 3 + 3 + 2 + len(data) + num = 0 + firstpacket = [] + if blocksize < 10 : + # very small block size - we send pair of 2-byte commands first. + firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x0E], + *[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)]] + data = [*[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC |constants.UPDI_DATA_16], *data] + num = 0 + else: + firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA , 0x0E], + *[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)], + *[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_16], + *data[:blocksize - 8]] + num = blocksize - 8 + self.updi_phy.send( firstpacket ) + + # if finite block size, this is used. + while num < len(data): + data_slice = data[num:num+blocksize] + self.updi_phy.send(data_slice) + num += len(data_slice) +*/ + avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d\n", progname, words * 2, blocksize); + + unsigned int temp_buffer_size = 3 + 3 + 2 + (words * 2) + 3; + unsigned int num=0; + unsigned char* temp_buffer = malloc(temp_buffer_size); + + if (temp_buffer == 0) { + avrdude_message(MSG_DEBUG, "%s: Allocating temporary buffer failed\n", progname); + return -1; + } + + if (blocksize == -1) { + blocksize = temp_buffer_size; + } + + temp_buffer[0] = UPDI_PHY_SYNC; + temp_buffer[1] = UPDI_STCS | UPDI_CS_CTRLA; + temp_buffer[2] = 0x0E; + temp_buffer[3] = UPDI_PHY_SYNC; + temp_buffer[4] = UPDI_REPEAT | UPDI_REPEAT_BYTE; + temp_buffer[5] = (words - 1) & 0xFF; + temp_buffer[6] = UPDI_PHY_SYNC; + temp_buffer[7] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16; + + memcpy(temp_buffer + 8, buffer, words * 2); + + temp_buffer[temp_buffer_size-3] = UPDI_PHY_SYNC; + temp_buffer[temp_buffer_size-2] = UPDI_STCS | UPDI_CS_CTRLA; + temp_buffer[temp_buffer_size-1] = 0x06; + + if (blocksize < 10) { + if (updi_physical_send(pgm, temp_buffer, 6) < 0) { + avrdude_message(MSG_DEBUG, "%s: Failed to send first package\n", progname); + free(temp_buffer); + return -1; + } + num = 6; + } + + while (num < temp_buffer_size) { + int next_package_size; + + if (num + blocksize > temp_buffer_size) { + next_package_size = temp_buffer_size - num; + } else { + next_package_size = blocksize; + } + + if (updi_physical_send(pgm, temp_buffer + num, next_package_size) < 0) { + avrdude_message(MSG_DEBUG, "%s: Failed to send package\n", progname); + free(temp_buffer); + return -1; + } + + num+=next_package_size; + } + free(temp_buffer); + return 0; +} + +int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats) +{ +/* + def repeat(self, repeats): + """ + Store a value to the repeat counter + + :param repeats: number of repeats requested + """ + self.logger.debug("Repeat %d", repeats) + if (repeats - 1) > constants.UPDI_MAX_REPEAT_SIZE: + self.logger.error("Invalid repeat count of %d", repeats) + raise Exception("Invalid repeat count!") + repeats -= 1 + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, + repeats & 0xFF]) +*/ + unsigned char buffer[3]; + avrdude_message(MSG_DEBUG, "%s: Repeat %d\n", progname, repeats); + if ((repeats - 1) > UPDI_MAX_REPEAT_SIZE) { + avrdude_message(MSG_DEBUG, "%s: Invalid repeat count of %d\n", progname, repeats); + return -1; + } + repeats-=1; + buffer[0] = UPDI_PHY_SYNC; + buffer[1] = UPDI_REPEAT | UPDI_REPEAT_BYTE; + buffer[2] = repeats & 0xFF; + return updi_physical_send(pgm, buffer, 3); +} + +int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def read_sib(self): + """ + Read the SIB + """ + return self.updi_phy.sib() +*/ + return updi_physical_sib(pgm, buffer, size); +} + +int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size) +{ +/* + def key(self, size, key): + """ + Write a key + + :param size: size of key (0=64B, 1=128B, 2=256B) + :param key: key value + """ + self.logger.debug("Writing key") + if len(key) != 8 << size: + raise PymcuprogError("Invalid KEY length!") + self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_KEY | constants.UPDI_KEY_KEY | size]) + self.updi_phy.send(list(reversed(list(key)))) +*/ + unsigned char send_buffer[2]; + unsigned char reversed_key[256]; + int index; + avrdude_message(MSG_DEBUG, "%s: UPDI writing key\n", progname); + if (size != (8 << size_type)) { + avrdude_message(MSG_DEBUG, "%s: Invalid key length\n", progname); + return -1; + } + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_KEY | UPDI_KEY_KEY | size_type; + if (updi_physical_send(pgm, send_buffer, 2) < 0) { + avrdude_message(MSG_DEBUG, "%s: UPDI key send message failed\n", progname); + return -1; + } + /* reverse key contents */ + for (index=0; index> 8) & 0xFF, (address >> 16) & 0xFF]) + return self.updi_phy.receive(1)[0] +*/ + unsigned char send_buffer[5]; + unsigned char recv_buffer[1]; + avrdude_message(MSG_DEBUG, "%s: LD from 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LDS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_DEBUG, "%s: LD operation send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_DEBUG, "%s: LD operation recv failed\n", progname); + return -1; + } + * value = recv_buffer[0]; + return 0; +} + +int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value) +{ +/* + def ld16(self, address): + """ + Load a 16-bit word directly from a 24-bit address + + :param address: address to load from + :return: values read + """ + self.logger.info("LD from 0x{0:06X}".format(address)) + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + return self.updi_phy.receive(2) +*/ + unsigned char send_buffer[5]; + unsigned char recv_buffer[2]; + avrdude_message(MSG_DEBUG, "%s: LD16 from 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_LDS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_DEBUG, "%s: LD16 operation send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 2) < 0) { + avrdude_message(MSG_DEBUG, "%s: LD16 operation recv failed\n", progname); + return -1; + } + * value = (recv_buffer[0] << 8 | recv_buffer[1]); + return 0; +} + +static int updi_link_st_data_phase(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size) +{ +/* + def _st_data_phase(self, values): + """ + Performs data phase of transaction: + * receive ACK + * send data + + :param values: bytearray of value(s) to send + """ + response = self.updi_phy.receive(1) + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st") + + self.updi_phy.send(values) + response = self.updi_phy.receive(1) + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st") +*/ + unsigned char recv_buffer[1]; + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_DEBUG, "%s: UPDI data phase recv failed on first ACK\n", progname); + return -1; + } + if (recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_DEBUG, "%s: UPDI data phase expected first ACK\n", progname); + return -1; + } + if (updi_physical_send(pgm, buffer, size) < 0) { + avrdude_message(MSG_DEBUG, "%s: UPDI data phase send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_DEBUG, "%s: UPDI data phase recv failed on second ACK\n", progname); + return -1; + } + if (recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_DEBUG, "%s: UPDI data phase expected second ACK\n", progname); + return -1; + } + return 0; +} + +int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value) +{ +/* + def st(self, address, value): + """ + Store a single byte value directly to a 24-bit address + + :param address: address to write to + :param value: value to write + """ + self.logger.info("ST to 0x{0:06X}".format(address)) + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_8, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + return self._st_data_phase([value & 0xFF]) +*/ + unsigned char send_buffer[5]; + avrdude_message(MSG_DEBUG, "%s: ST to 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_STS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST operation send failed\n", progname); + return -1; + } + send_buffer[0] = value; + return updi_link_st_data_phase(pgm, send_buffer, 1); +} + +int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value) +{ +/* + def st16(self, address, value): + """ + Store a 16-bit word value directly to a 24-bit address + + :param address: address to write to + :param value: value to write + """ + self.logger.info("ST to 0x{0:06X}".format(address)) + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + return self._st_data_phase([value & 0xFF, (value >> 8) & 0xFF]) +*/ + unsigned char send_buffer[5]; + avrdude_message(MSG_DEBUG, "%s: ST16 to 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_STS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST16 operation send failed\n", progname); + return -1; + } + send_buffer[0] = value & 0xFF; + send_buffer[1] = (value >> 8) & 0xFF; + return updi_link_st_data_phase(pgm, send_buffer, 2); +} + +int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address) +{ +/* + def st_ptr(self, address): + """ + Set the pointer location + + :param address: address to write + """ + self.logger.info("ST to ptr") + self.updi_phy.send( + [constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_ADDRESS | constants.UPDI_DATA_24, + address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) + response = self.updi_phy.receive(1) + if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: + raise PymcuprogError("Error with st_ptr") +*/ + unsigned char send_buffer[5]; + unsigned char recv_buffer[1]; + avrdude_message(MSG_DEBUG, "%s: ST_PTR to 0x%06X\n", progname, address); + send_buffer[0] = UPDI_PHY_SYNC; + send_buffer[1] = UPDI_STS | UPDI_ST | UPDI_PTR_ADDRESS | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_DATA_24 : UPDI_DATA_16); + send_buffer[2] = address & 0xFF; + send_buffer[3] = (address >> 8) & 0xFF; + send_buffer[4] = (address >> 16) & 0xFF; + if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR operation send failed\n", progname); + return -1; + } + if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { + avrdude_message(MSG_DEBUG, "%s: UPDI ST_PTR recv failed on ACK\n", progname); + return -1; + } + if (recv_buffer[0] != UPDI_PHY_ACK) { + avrdude_message(MSG_DEBUG, "%s: UPDI ST_PTR expected ACK\n", progname); + return -1; + } + return 0; +} diff --git a/updi_link.h b/updi_link.h new file mode 100644 index 00000000..5b5e5bda --- /dev/null +++ b/updi_link.h @@ -0,0 +1,59 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_link_h +#define updi_link_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_link_open(PROGRAMMER * pgm); +void updi_link_close(PROGRAMMER * pgm); +int updi_link_init(PROGRAMMER * pgm); +int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value); +int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value); +int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); +int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); +int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize); +int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats); +int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size); +int updi_link_ld(PROGRAMMER * pgm, uint32_t address, uint8_t * value); +int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value); +int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value); +int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value); +int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_link_h */ diff --git a/updi_nvm.c b/updi_nvm.c new file mode 100644 index 00000000..f3d899a9 --- /dev/null +++ b/updi_nvm.c @@ -0,0 +1,1275 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_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(PROGRAMMER * pgm, 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 +*/ + avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_flash_page_V0(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) < 0) { + avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_eeprom_V0(PROGRAMMER * pgm, 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") +*/ + avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_user_row_V0(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase user row\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Load NVM address\n", progname); + if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRL, address & 0xFF) < 0) { + avrdude_message(MSG_INFO, "%s: Write ADDRL operation failed\n", progname); + return -1; + } + if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRH, (address >> 8) & 0xFF) < 0) { + avrdude_message(MSG_INFO, "%s: Write ADDRH operation failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Load fuse data\n", progname); + if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_DATAL, value & 0xFF) < 0) { + avrdude_message(MSG_INFO, "%s: Write DATAL operation failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Execute fuse write\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE) < 0) { + avrdude_message(MSG_INFO, "%s: Write fuse operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_write_V0(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear page buffer\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) < 0) { + avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + } + avrdude_message(MSG_DEBUG, "%s: Committing data\n", progname); + if (nvm_command == USE_DEFAULT_COMMAND) { + nvm_command = UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE; + } + if (updi_nvm_command(pgm, p, nvm_command) < 0) { + avrdude_message(MSG_INFO, "%s: Commit data command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_chip_erase_V2(PROGRAMMER * pgm, 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 +*/ + avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_flash_page_V2(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_eeprom_V2(PROGRAMMER * pgm, 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) +*/ + avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_user_row_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, + uint16_t size, access_mode mode); + +static int nvm_write_flash_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: NVM EEPROM erase/write command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Clear NVM command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_write_fuse_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: NVM write command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE) < 0) { + avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Clear NVM command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_chip_erase_V3(PROGRAMMER * pgm, 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 +*/ + avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_flash_page_V3(PROGRAMMER * pgm, 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]; + avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + data[0] = 0xFF; + if (updi_write_data(pgm, address, data, 1) < 0) { + avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_eeprom_V3(PROGRAMMER * pgm, 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") +*/ + avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { + avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + +static int nvm_erase_user_row_V3(PROGRAMMER * pgm, 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) +*/ + avrdude_message(MSG_DEBUG, "%s: Erase user row at address 0x%06X\n", progname, address); + + return nvm_erase_flash_page_V3(pgm, p, address); +} + +static int nvm_write_V3(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, + uint16_t size, access_mode mode, uint8_t nvm_command); + +static int nvm_write_flash_V3(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + avrdude_message(MSG_DEBUG, "%s: Clear page buffer\n", progname); + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR) < 0) { + avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (mode == USE_WORD_ACCESS) { + if (updi_write_data_words(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); + return -1; + } + } else { + if (updi_write_data(pgm, address, buffer, size) < 0) { + avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); + return -1; + } + } + avrdude_message(MSG_DEBUG, "%s: Committing data\n", progname); + if (nvm_command == USE_DEFAULT_COMMAND) { + nvm_command = UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE; + } + if (updi_nvm_command(pgm, p, nvm_command) < 0) { + avrdude_message(MSG_INFO, "%s: Commit data command failed\n", progname); + return -1; + } + if (updi_nvm_wait_ready(pgm, p) < 0) { + avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); + return -1; + } + if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { + avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); + return -1; + } + return 0; +} + + +int updi_nvm_chip_erase(PROGRAMMER * pgm, AVRPART * p) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_chip_erase_V0(pgm, p); + case UPDI_NVM_MODE_V2: + return nvm_chip_erase_V2(pgm, p); + case UPDI_NVM_MODE_V3: + return nvm_chip_erase_V3(pgm, p); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_erase_flash_page(PROGRAMMER * pgm, AVRPART *p, uint32_t address) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_erase_flash_page_V0(pgm, p, address); + case UPDI_NVM_MODE_V2: + return nvm_erase_flash_page_V2(pgm, p, address); + case UPDI_NVM_MODE_V3: + return nvm_erase_flash_page_V3(pgm, p, address); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_erase_eeprom(PROGRAMMER * pgm, AVRPART *p) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_erase_eeprom_V0(pgm, p); + case UPDI_NVM_MODE_V2: + return nvm_erase_eeprom_V2(pgm, p); + case UPDI_NVM_MODE_V3: + return nvm_erase_eeprom_V3(pgm, p); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_erase_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_erase_user_row_V0(pgm, p, address, size); + case UPDI_NVM_MODE_V2: + return nvm_erase_user_row_V2(pgm, p, address, size); + case UPDI_NVM_MODE_V3: + return nvm_erase_user_row_V3(pgm, p, address, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_flash(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_write_flash_V0(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V2: + return nvm_write_flash_V2(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V3: + return nvm_write_flash_V3(pgm, p, address, buffer, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return 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); + case UPDI_NVM_MODE_V3: + return nvm_write_user_row_V3(pgm, p, address, buffer, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_eeprom(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_write_eeprom_V0(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V2: + return nvm_write_eeprom_V2(pgm, p, address, buffer, size); + case UPDI_NVM_MODE_V3: + return nvm_write_eeprom_V3(pgm, p, address, buffer, size); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_write_fuse(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint8_t value) +{ + switch(updi_get_nvm_mode(pgm)) + { + case UPDI_NVM_MODE_V0: + return nvm_write_fuse_V0(pgm, p, address, value); + case UPDI_NVM_MODE_V2: + return nvm_write_fuse_V2(pgm, p, address, value); + case UPDI_NVM_MODE_V3: + return nvm_write_fuse_V3(pgm, p, address, value); + default: + avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); + return -1; + } +} + +int updi_nvm_wait_ready(PROGRAMMER * pgm, 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; + struct timeval tv; + uint8_t status; + 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) { + 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; + } while ((current_time - start_time) < 10000000); + + avrdude_message(MSG_INFO, "%s: Wait NVM ready timed out\n", progname); + return -1; +} + +int updi_nvm_command(PROGRAMMER * pgm, 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) +*/ + avrdude_message(MSG_DEBUG, "%s: NVMCMD %d executing\n", progname, command); + + return updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_CTRLA, command); +} diff --git a/updi_nvm.h b/updi_nvm.h new file mode 100644 index 00000000..c0c1544e --- /dev/null +++ b/updi_nvm.h @@ -0,0 +1,51 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_h +#define updi_nvm_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_nvm_chip_erase(PROGRAMMER * pgm, AVRPART * p); +int updi_nvm_erase_flash_page(PROGRAMMER * pgm, AVRPART *p, uint32_t address); +int updi_nvm_erase_eeprom(PROGRAMMER * pgm, AVRPART *p); +int updi_nvm_erase_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint16_t size); +int updi_nvm_write_flash(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); +int updi_nvm_write_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); +int updi_nvm_write_eeprom(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); +int updi_nvm_write_fuse(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint8_t value); +int updi_nvm_wait_ready(PROGRAMMER * pgm, AVRPART *p); +int updi_nvm_command(PROGRAMMER * pgm, AVRPART *p, uint8_t command); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_nvm_h */ diff --git a/updi_readwrite.c b/updi_readwrite.c new file mode 100644 index 00000000..3ee19cb0 --- /dev/null +++ b/updi_readwrite.c @@ -0,0 +1,316 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_constants.h" +#include "updi_link.h" +#include "updi_readwrite.h" + +int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value) +{ +/* + def read_cs(self, address): + """ + Read from Control/Status space + + :param address: address (index) to read + :return: value read + """ + return self.datalink.ldcs(address) +*/ + return updi_link_ldcs(pgm, address, value); +} + +int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value) +{ +/* + def write_cs(self, address, value): + """ + Write to Control/Status space + + :param address: address (index) to write + :param value: 8-bit value to write + """ + return self.datalink.stcs(address, value) +*/ + return updi_link_stcs(pgm, address, value); +} + +int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size) +{ +/* + def write_key(self, size, key): + """ + Write a KEY into UPDI + + :param size: size of key to send + :param key: key value + """ + return self.datalink.key(size, key) +*/ + return updi_link_key(pgm, buffer, size_type, size); +} + +int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) +{ +/* + def read_sib(self): + """ + Read the SIB from UPDI + + :return: SIB string (bytearray) read + """ + return self.datalink.read_sib() +*/ + return updi_link_read_sib(pgm, buffer, size); +} + +int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value) +{ +/* + def read_byte(self, address): + """ + Read a single byte from UPDI + + :param address: address to read from + :return: value read + """ + return self.datalink.ld(address) +*/ + return updi_link_ld(pgm, address, value); +} + +int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value) +{ +/* + def write_byte(self, address, value): + """ + Writes a single byte to UPDI + + :param address: address to write to + :param value: value to write + """ + return self.datalink.st(address, value) +*/ + return updi_link_st(pgm, address, value); +} + +int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def read_data(self, address, size): + """ + Reads a number of bytes of data from UPDI + + :param address: address to write to + :param size: number of bytes to read + """ + self.logger.debug("Reading %d bytes from 0x%04X", size, address) + # Range check + if size > constants.UPDI_MAX_REPEAT_SIZE: + raise PymcuprogError("Cant read that many bytes in one go") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + if size > 1: + self.datalink.repeat(size) + + # Do the read(s) + return self.datalink.ld_ptr_inc(size) +*/ + avrdude_message(MSG_DEBUG, "%s: Reading %d bytes from 0x%06X\n", progname, size, address); + + if (size > UPDI_MAX_REPEAT_SIZE) { + avrdude_message(MSG_DEBUG, "%s: Can't read that many bytes in one go\n", progname); + return -1; + } + + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname); + return -1; + } + + if (size > 1) { + if (updi_link_repeat(pgm, size) < 0) { + avrdude_message(MSG_DEBUG, "%s: Repeat operation failed\n", progname); + return -1; + } + } + return updi_link_ld_ptr_inc(pgm, buffer, size); +} + +int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def write_data(self, address, data): + """ + Writes a number of bytes to memory + + :param address: address to write to + :param data: data to write + """ + # Special case of 1 byte + if len(data) == 1: + return self.datalink.st(address, data[0]) + # Special case of 2 byte + if len(data) == 2: + self.datalink.st(address, data[0]) + return self.datalink.st(address + 1, data[1]) + + # Range check + if len(data) > constants.UPDI_MAX_REPEAT_SIZE: + raise PymcuprogError("Invalid length") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + self.datalink.repeat(len(data)) + return self.datalink.st_ptr_inc(data) +*/ + if (size == 1) { + return updi_link_st(pgm, address, buffer[0]); + } + if (size == 2) { + if (updi_link_st(pgm, address, buffer[0]) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST operation failed\n", progname); + return -1; + } + return updi_link_st(pgm, address+1, buffer[1]); + } + if (size > UPDI_MAX_REPEAT_SIZE) { + avrdude_message(MSG_DEBUG, "%s: Invalid length\n", progname); + return -1; + } + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname); + return -1; + } + if (updi_link_repeat(pgm, size) < 0) { + avrdude_message(MSG_DEBUG, "%s: Repeat operation failed\n", progname); + return -1; + } + return updi_link_st_ptr_inc(pgm, buffer, size); +} + +int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def read_data_words(self, address, words): + """ + Reads a number of words of data from UPDI + + :param address: address to write to + :param words: number of words to read + """ + self.logger.debug("Reading %d words from 0x%04X", words, address) + + # Range check + if words > constants.UPDI_MAX_REPEAT_SIZE >> 1: + raise PymcuprogError("Cant read that many words in one go") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + if words > 1: + self.datalink.repeat(words) + + # Do the read + return self.datalink.ld_ptr_inc16(words) +*/ + avrdude_message(MSG_DEBUG, "%s: Reading %d words from 0x%06X", progname, size, address); + + if (size > (UPDI_MAX_REPEAT_SIZE >> 1)) { + avrdude_message(MSG_DEBUG, "%s: Can't read that many words in one go\n", progname); + return -1; + } + + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname); + return -1; + } + + if (size > 1) { + if (updi_link_repeat(pgm, size) < 0) { + avrdude_message(MSG_DEBUG, "%s: Repeat operation failed\n", progname); + return -1; + } + } + return updi_link_ld_ptr_inc16(pgm, buffer, size); +} + +int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) +{ +/* + def write_data_words(self, address, data): + """ + Writes a number of words to memory + + :param address: address to write to + :param data: data to write + """ + # Special-case of 1 word + if len(data) == 2: + value = data[0] + (data[1] << 8) + return self.datalink.st16(address, value) + + # Range check + if len(data) > constants.UPDI_MAX_REPEAT_SIZE << 1: + raise PymcuprogError("Invalid length") + + # Store the address + self.datalink.st_ptr(address) + + # Fire up the repeat + self.datalink.repeat(len(data) >> 1) + return self.datalink.st_ptr_inc16(data) +*/ + if (size == 2) { + return updi_link_st16(pgm, address, buffer[0] + (buffer[1] << 8)); + } + if (size > UPDI_MAX_REPEAT_SIZE << 1) { + avrdude_message(MSG_DEBUG, "%s: Invalid length\n", progname); + return -1; + } + if (updi_link_st_ptr(pgm, address) < 0) { + avrdude_message(MSG_DEBUG, "%s: ST_PTR operation failed\n", progname); + return -1; + } + return updi_link_st_ptr_inc16_RSD(pgm, buffer, size >> 1, -1); +} diff --git a/updi_readwrite.h b/updi_readwrite.h new file mode 100644 index 00000000..9519d179 --- /dev/null +++ b/updi_readwrite.h @@ -0,0 +1,51 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_readwrite_h +#define updi_readwrite_h + +#include "libavrdude.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value); +int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value); +int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size); +int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); +int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value); +int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value); +int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); +int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); +int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); +int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_readwrite_h */ diff --git a/updi_state.c b/updi_state.c new file mode 100644 index 00000000..63d80f46 --- /dev/null +++ b/updi_state.c @@ -0,0 +1,55 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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 "libavrdude.h" +#include "updi_state.h" + +updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm) +{ + return &((updi_state *)(pgm->cookie))->sib_info; +} + +updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm) +{ + return ((updi_state *)(pgm->cookie))->datalink_mode; +} + +void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode) +{ + ((updi_state *)(pgm->cookie))->datalink_mode = mode; +} + +updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm) +{ + return ((updi_state *)(pgm->cookie))->nvm_mode; +} + +void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode) +{ + ((updi_state *)(pgm->cookie))->nvm_mode = mode; +} diff --git a/updi_state.h b/updi_state.h new file mode 100644 index 00000000..d4e39d4c --- /dev/null +++ b/updi_state.h @@ -0,0 +1,85 @@ +/* + * avrdude - A Downloader/Uploader for AVR device programmers + * Copyright (C) 2021 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_state_h +#define updi_state_h + +#include "libavrdude.h" + +typedef enum +{ + UPDI_LINK_MODE_16BIT, + UPDI_LINK_MODE_24BIT +} updi_datalink_mode; + +typedef enum +{ + UPDI_NVM_MODE_V0, + UPDI_NVM_MODE_V2, + UPDI_NVM_MODE_V3 +} updi_nvm_mode; + +#define SIB_INFO_STRING_LENGTH 32 +#define SIB_INFO_FAMILY_LENGTH 8 +#define SIB_INFO_NVM_LENGTH 3 +#define SIB_INFO_DEBUG_LENGTH 3 +#define SIB_INFO_PDI_LENGTH 4 +#define SIB_INFO_EXTRA_LENGTH 20 + +typedef struct +{ + unsigned char sib_string[SIB_INFO_STRING_LENGTH+1]; + char family_string[SIB_INFO_FAMILY_LENGTH+1]; + char nvm_string[SIB_INFO_NVM_LENGTH+1]; + char debug_string[SIB_INFO_DEBUG_LENGTH+1]; + char pdi_string[SIB_INFO_PDI_LENGTH+1]; + char extra_string[SIB_INFO_EXTRA_LENGTH+1]; + char nvm_version; + char debug_version; +} updi_sib_info; + +typedef struct +{ + updi_sib_info sib_info; + updi_datalink_mode datalink_mode; + updi_nvm_mode nvm_mode; +} updi_state; + +#ifdef __cplusplus +extern "C" { +#endif + +updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm); +updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm); +void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode); +updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm); +void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode); + +#ifdef __cplusplus +} +#endif + +#endif /* updi_state_h */ From 0bb1b758a4640c399cf72d9c5764a6e4db7beda9 Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Tue, 21 Dec 2021 21:30:31 +0100 Subject: [PATCH 11/12] Removed files from old location --- avrdude/serialupdi.c | 664 -------------------- avrdude/serialupdi.h | 43 -- avrdude/updi_constants.h | 156 ----- avrdude/updi_link.c | 928 --------------------------- avrdude/updi_link.h | 59 -- avrdude/updi_nvm.c | 1275 -------------------------------------- avrdude/updi_nvm.h | 51 -- avrdude/updi_readwrite.c | 316 ---------- avrdude/updi_readwrite.h | 51 -- avrdude/updi_state.c | 55 -- avrdude/updi_state.h | 85 --- 11 files changed, 3683 deletions(-) delete mode 100644 avrdude/serialupdi.c delete mode 100644 avrdude/serialupdi.h delete mode 100644 avrdude/updi_constants.h delete mode 100644 avrdude/updi_link.c delete mode 100644 avrdude/updi_link.h delete mode 100644 avrdude/updi_nvm.c delete mode 100644 avrdude/updi_nvm.h delete mode 100644 avrdude/updi_readwrite.c delete mode 100644 avrdude/updi_readwrite.h delete mode 100644 avrdude/updi_state.c delete mode 100644 avrdude/updi_state.h diff --git a/avrdude/serialupdi.c b/avrdude/serialupdi.c deleted file mode 100644 index 62a9d44c..00000000 --- a/avrdude/serialupdi.c +++ /dev/null @@ -1,664 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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$ */ - -/* - * Interface to the SerialUPDI programmer. - * - * 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 "serialupdi.h" -#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) -{ - if ((pgm->cookie = malloc(sizeof(updi_state))) == 0) { - avrdude_message(MSG_INFO, - "%s: serialupdi_setup(): Out of memory allocating private data\n", - progname); - exit(1); - } - memset(pgm->cookie, 0, sizeof(updi_state)); - updi_set_datalink_mode(pgm, UPDI_LINK_MODE_16BIT); -} - -static void serialupdi_teardown(PROGRAMMER * pgm) -{ - free(pgm->cookie); -} - -static int serialupdi_open(PROGRAMMER * pgm, char * port) -{ - strcpy(pgm->port, port); - return updi_link_open(pgm); -} - -static int serialupdi_decode_sib(PROGRAMMER * pgm, updi_sib_info * sib_info) -{ - char * str_ptr; - - sib_info->sib_string[SIB_INFO_STRING_LENGTH]=0; - avrdude_message(MSG_DEBUG, "%s: Received SIB: [%s]\n", progname, sib_info->sib_string); - memset(sib_info->family_string, 0, SIB_INFO_FAMILY_LENGTH+1); - memset(sib_info->nvm_string, 0, SIB_INFO_NVM_LENGTH+1); - memset(sib_info->debug_string, 0, SIB_INFO_DEBUG_LENGTH+1); - memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1); - memset(sib_info->pdi_string, 0, SIB_INFO_PDI_LENGTH+1); - memset(sib_info->extra_string, 0, SIB_INFO_EXTRA_LENGTH+1); - - memcpy(sib_info->family_string, sib_info->sib_string, SIB_INFO_FAMILY_LENGTH); - memcpy(sib_info->nvm_string, sib_info->sib_string + 8, SIB_INFO_NVM_LENGTH); - memcpy(sib_info->debug_string, sib_info->sib_string + 11, SIB_INFO_DEBUG_LENGTH); - memcpy(sib_info->pdi_string, sib_info->sib_string + 15, SIB_INFO_PDI_LENGTH); - strcpy(sib_info->extra_string, (char *)sib_info->sib_string + 19); - - str_ptr = strstr(sib_info->nvm_string, ":"); - if (!str_ptr) { - avrdude_message(MSG_INFO, "%s: Incorrect format of NVM string\n", progname); - return -1; - } - sib_info->nvm_version = *(str_ptr+1); - - str_ptr = strstr(sib_info->debug_string, ":"); - if (!str_ptr) { - avrdude_message(MSG_INFO, "%s: Incorrect format of DEBUG string\n", progname); - return -1; - } - sib_info->debug_version = *(str_ptr+1); - - avrdude_message(MSG_DEBUG, "%s: Device family ID: %s\n", progname, sib_info->family_string); - avrdude_message(MSG_DEBUG, "%s: NVM interface: %s\n", progname, sib_info->nvm_string); - avrdude_message(MSG_DEBUG, "%s: Debug interface: %s\n", progname, sib_info->debug_string); - avrdude_message(MSG_DEBUG, "%s: PDI oscillator: %s\n", progname, sib_info->pdi_string); - avrdude_message(MSG_DEBUG, "%s: Extra information: %s\n", progname, sib_info->extra_string); - 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); - return -1; - } - return 0; -} - -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_unlock(PROGRAMMER * pgm, AVRPART * p); - -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, attempting reset\n", progname); - if (serialupdi_leave_progmode(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: Unable to leave progmode\n", progname); - return -1; - } - if (updi_link_init(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname); - return -1; - } - avrdude_message(MSG_INFO, "%s: UPDI link initialization OK\n", progname); - if (serialupdi_in_prog_mode(pgm, &in_prog_mode) < 0) { - avrdude_message(MSG_INFO, "%s: Checking UPDI NVM prog mode failed again, exiting\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); - if (!ovsigck) { - return -1; - } - if (serialupdi_unlock(pgm, 0x00) < 0) { - return -1; - } - if (updi_link_init(pgm) < 0) { - return -1; - } - return serialupdi_enter_progmode(pgm); - } - - 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); - - updi_sib_info * sib_info = updi_get_sib_info(pgm); - - if (updi_read_sib(pgm, sib_info->sib_string, 32) < 0) { - avrdude_message(MSG_INFO, "%s: Read SIB operation failed\n", progname); - return -1; - } - if (serialupdi_decode_sib(pgm, sib_info) < 0) { - avrdude_message(MSG_INFO, "%s: Decode SIB_INFO failed\n", progname); - return -1; - } - - 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) -{ - if (updi_link_init(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: UPDI link initialization failed\n", progname); - return -1; - } - avrdude_message(MSG_INFO, "%s: UPDI link initialization OK\n", progname); - if (serialupdi_enter_progmode(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: Unable to enter NVM programming mode\n", progname); - return -1; - } - - return 0; -} - -static void serialupdi_disable(PROGRAMMER * pgm) -{ - /* Do nothing. */ - - return; -} - -static void serialupdi_enable(PROGRAMMER * pgm) -{ - /* Do nothing. */ - - return; -} - -static void serialupdi_display(PROGRAMMER * pgm, const char * p) -{ - return; -} - -static int serialupdi_cmd(PROGRAMMER * pgm, const unsigned char * cmd, - unsigned char * res) -{ - avrdude_message(MSG_INFO, "%s: error: cmd %s[%s] not implemented yet\n", - progname, cmd, res); - return -1; -} - -static int serialupdi_program_enable(PROGRAMMER * pgm, AVRPART * p) -{ - avrdude_message(MSG_INFO, "%s: error: program enable 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) -{ - if (strstr(mem->desc, "fuse") != 0) { - return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); - } - if (strcmp(mem->desc, "lock") == 0) { - return updi_nvm_write_fuse(pgm, p, mem->offset + addr, value); - } - if (strcmp(mem->desc, "eeprom") == 0) { - unsigned char buffer[1]; - buffer[0]=value; - return updi_nvm_write_eeprom(pgm, p, mem->offset + addr, buffer, 1); - } - if (strcmp(mem->desc, "flash") == 0) { - unsigned char buffer[1]; - buffer[0]=value; - return updi_nvm_write_flash(pgm, p, mem->offset + addr, buffer, 1); - } - 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) -{ - if (n_bytes > m->readsize) { - unsigned int read_offset = addr; - unsigned int remaining_bytes = n_bytes; - int read_bytes = 0; - int rc; - while (remaining_bytes > 0) { - 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; - } else { - read_bytes+=rc; - read_offset+=m->readsize; - remaining_bytes-=m->readsize; - } - } - return read_bytes; - } else { - return updi_read_data(pgm, m->offset + addr, m->buf + addr, n_bytes); - } -} - -static int serialupdi_paged_write(PROGRAMMER * pgm, AVRPART * p, AVRMEM * m, - unsigned int page_size, - unsigned int addr, unsigned int n_bytes) -{ - int rc; - 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 { - avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes); - 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 { - avrdude_message(MSG_INFO, "%s: Invalid memory type: <%s:%d>, 0x%06X, %d (0x%04X)\n", progname, m->desc, page_size, addr, n_bytes, n_bytes); - 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 (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"); - - /* - * mandatory functions - */ - - pgm->initialize = serialupdi_initialize; - pgm->display = serialupdi_display; - pgm->enable = serialupdi_enable; - pgm->disable = serialupdi_disable; - pgm->program_enable = serialupdi_program_enable; - pgm->chip_erase = serialupdi_chip_erase; - pgm->cmd = serialupdi_cmd; - pgm->open = serialupdi_open; - pgm->close = serialupdi_close; - pgm->read_byte = serialupdi_read_byte; - 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; - -} - -const char serialupdi_desc[] = "Driver for SerialUPDI programmers"; diff --git a/avrdude/serialupdi.h b/avrdude/serialupdi.h deleted file mode 100644 index ff7270d5..00000000 --- a/avrdude/serialupdi.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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 serialupdi_h -#define serialupdi_h - -#include "libavrdude.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern const char serialupdi_desc[]; -void serialupdi_initpgm (PROGRAMMER * pgm); - -#ifdef __cplusplus -} -#endif - -#endif /* serialupdi_h */ diff --git a/avrdude/updi_constants.h b/avrdude/updi_constants.h deleted file mode 100644 index ff8a446f..00000000 --- a/avrdude/updi_constants.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_constants_h -#define updi_constants_h - -#define UPDI_BREAK 0x00 - -#define UPDI_LDS 0x00 -#define UPDI_STS 0x40 -#define UPDI_LD 0x20 -#define UPDI_ST 0x60 -#define UPDI_LDCS 0x80 -#define UPDI_STCS 0xC0 -#define UPDI_REPEAT 0xA0 -#define UPDI_KEY 0xE0 - -#define UPDI_PTR 0x00 -#define UPDI_PTR_INC 0x04 -#define UPDI_PTR_ADDRESS 0x08 - -#define UPDI_ADDRESS_8 0x00 -#define UPDI_ADDRESS_16 0x04 -#define UPDI_ADDRESS_24 0x08 - -#define UPDI_DATA_8 0x00 -#define UPDI_DATA_16 0x01 -#define UPDI_DATA_24 0x02 - -#define UPDI_KEY_SIB 0x04 -#define UPDI_KEY_KEY 0x00 - -#define UPDI_KEY_64 0x00 -#define UPDI_KEY_128 0x01 -#define UPDI_KEY_256 0x02 - -#define UPDI_SIB_8BYTES UPDI_KEY_64 -#define UPDI_SIB_16BYTES UPDI_KEY_128 -#define UPDI_SIB_32BYTES UPDI_KEY_256 - -#define UPDI_REPEAT_BYTE 0x00 -#define UPDI_REPEAT_WORD 0x01 - -#define UPDI_PHY_SYNC 0x55 -#define UPDI_PHY_ACK 0x40 - -#define UPDI_MAX_REPEAT_SIZE (0xFF+1) // Repeat counter of 1-byte, with off-by-one counting - -//# CS and ASI Register Address map -#define UPDI_CS_STATUSA 0x00 -#define UPDI_CS_STATUSB 0x01 -#define UPDI_CS_CTRLA 0x02 -#define UPDI_CS_CTRLB 0x03 -#define UPDI_ASI_KEY_STATUS 0x07 -#define UPDI_ASI_RESET_REQ 0x08 -#define UPDI_ASI_CTRLA 0x09 -#define UPDI_ASI_SYS_CTRLA 0x0A -#define UPDI_ASI_SYS_STATUS 0x0B -#define UPDI_ASI_CRC_STATUS 0x0C - -#define UPDI_CTRLA_IBDLY_BIT 7 -#define UPDI_CTRLB_CCDETDIS_BIT 3 -#define UPDI_CTRLB_UPDIDIS_BIT 2 - -#define UPDI_KEY_NVM "NVMProg " -#define UPDI_KEY_CHIPERASE "NVMErase" -#define UPDI_KEY_UROW "NVMUs&te" - -#define UPDI_ASI_STATUSA_REVID 4 -#define UPDI_ASI_STATUSB_PESIG 0 - -#define UPDI_ASI_KEY_STATUS_CHIPERASE 3 -#define UPDI_ASI_KEY_STATUS_NVMPROG 4 -#define UPDI_ASI_KEY_STATUS_UROWWRITE 5 - -#define UPDI_ASI_SYS_STATUS_RSTSYS 5 -#define UPDI_ASI_SYS_STATUS_INSLEEP 4 -#define UPDI_ASI_SYS_STATUS_NVMPROG 3 -#define UPDI_ASI_SYS_STATUS_UROWPROG 2 -#define UPDI_ASI_SYS_STATUS_LOCKSTATUS 0 - -#define UPDI_ASI_SYS_CTRLA_UROW_FINAL 1 - -#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/avrdude/updi_link.c b/avrdude/updi_link.c deleted file mode 100644 index a3852bfe..00000000 --- a/avrdude/updi_link.c +++ /dev/null @@ -1,928 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_link.h" -#include "updi_constants.h" -#include "updi_state.h" - -#include - -void msleep(int tms) -{ - struct timeval tv; - tv.tv_sec = tms / 1000; - tv.tv_usec = (tms % 1000) * 1000; - select (0, NULL, NULL, NULL, &tv); -} - -static int updi_physical_open(PROGRAMMER* pgm, int baudrate, unsigned long cflags) -{ - serial_recv_timeout = 100; - union pinfo pinfo; - - pinfo.serialinfo.baud = baudrate; - pinfo.serialinfo.cflags = cflags; - - avrdude_message(MSG_DEBUG, "%s: Opening serial port...\n", progname); - - if (serial_open(pgm->port, pinfo, &pgm->fd)==-1) { - - avrdude_message(MSG_INFO, "%s: Serial port open failed!\n", progname); - return -1; - } - - /* - * drain any extraneous input - */ - serial_drain(&pgm->fd, 0); - - return 0; -} - -static void updi_physical_close(PROGRAMMER* pgm) -{ - serial_close(&pgm->fd); - pgm->fd.ifd = -1; -} - -static int updi_physical_send(PROGRAMMER * pgm, unsigned char * buf, size_t len) -{ - size_t i; - int rv; - - avrdude_message(MSG_DEBUG, "%s: Sending %lu bytes [", progname, len); - for (i=0; ifd, buf, len); - serial_recv(&pgm->fd, buf, len); - return rv; -} - -static int updi_physical_recv(PROGRAMMER * pgm, unsigned char * buf, size_t len) -{ - size_t i; - int rv; - - rv = serial_recv(&pgm->fd, buf, len); - if (rv < 0) { - avrdude_message(MSG_DEBUG, - "%s: serialupdi_recv(): programmer is not responding\n", - progname); - return -1; - } - - avrdude_message(MSG_DEBUG, "%s: Received %lu bytes [", progname, len); - for (i=0; ifd, buffer, 1); - serial_recv(&pgm->fd, buffer, 1); - - msleep(100); - - buffer[0] = UPDI_BREAK; - - serial_send(&pgm->fd, buffer, 1); - serial_recv(&pgm->fd, buffer, 1); - - updi_physical_close(pgm); - - return updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, SERIAL_8E2); -} - -int updi_physical_sib(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size) -{ -/* - def sib(self): - """ - System information block is just a string coming back from a SIB command - """ - self.send([ - constants.UPDI_PHY_SYNC, - constants.UPDI_KEY | constants.UPDI_KEY_SIB | constants.UPDI_SIB_32BYTES]) - return self.ser.readline() -*/ - unsigned char send_buffer[2]; - - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_KEY | UPDI_KEY_SIB | UPDI_SIB_32BYTES; - - if (updi_physical_send(pgm, send_buffer, 2) < 0) { - avrdude_message(MSG_INFO, "%s: SIB request send failed\n", progname); - return -1; - } - - return updi_physical_recv(pgm, buffer, size); -} - -int updi_link_open(PROGRAMMER * pgm) -{ - return updi_physical_open(pgm, pgm->baudrate? pgm->baudrate: 115200, - (SERIAL_CS8 | SERIAL_CSTOPB | SERIAL_CREAD | SERIAL_PARENB | SERIAL_CLOCAL)); -} - -void updi_link_close(PROGRAMMER * pgm) -{ - updi_physical_close(pgm); -} - -static int updi_link_init_session_parameters(PROGRAMMER * pgm) -{ -/* - def _init_session_parameters(self): - """ - Set the inter-byte delay bit and disable collision detection - """ - self.stcs(constants.UPDI_CS_CTRLB, 1 << constants.UPDI_CTRLB_CCDETDIS_BIT) - self.stcs(constants.UPDI_CS_CTRLA, 1 << constants.UPDI_CTRLA_IBDLY_BIT) -*/ - if (updi_link_stcs(pgm, UPDI_CS_CTRLB, 1 << UPDI_CTRLB_CCDETDIS_BIT) < 0) { - return -1; - } - - if (updi_link_stcs(pgm, UPDI_CS_CTRLA, 1 << UPDI_CTRLA_IBDLY_BIT) < 0) { - return -1; - } - - return 0; -} - -static int updi_link_check(PROGRAMMER * pgm) -{ -/* - def _check_datalink(self): - """ - Check UPDI by loading CS STATUSA - """ - try: - if self.ldcs(constants.UPDI_CS_STATUSA) != 0: - self.logger.info("UPDI init OK") - return True - except PymcuprogError: - self.logger.warning("Check failed") - return False - self.logger.info("UPDI not OK - reinitialisation required") - return False -*/ - int result; - uint8_t value; - result = updi_link_ldcs(pgm, UPDI_CS_STATUSA, &value); - if (result < 0) { - avrdude_message(MSG_DEBUG, "%s: Check failed\n", progname); - return -1; - } else { - if (value > 0) { - avrdude_message(MSG_DEBUG, "%s: UDPI init OK\n", progname); - return 0; - } else { - avrdude_message(MSG_DEBUG, "%s: UDPI not OK - reinitialisation required\n", progname); - return -1; - } - } -} - - -int updi_link_init(PROGRAMMER * pgm) -{ -/* - def init_datalink(self): - """ - Init DL layer - """ - self._init_session_parameters() - # Check - if not self._check_datalink(): - # Send double break if all is not well, and re-check - self.updi_phy.send_double_break() - self._init_session_parameters() - if not self._check_datalink(): - raise PymcuprogError("UPDI initialisation failed") -*/ - if (updi_link_init_session_parameters(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: Session initialisation failed\n", progname); - return -1; - } - - if (updi_link_check(pgm) < 0) { - avrdude_message(MSG_DEBUG, "%s: Datalink not active, resetting...\n", progname); - if (updi_physical_send_double_break(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: Datalink initialisation failed\n", progname); - return -1; - } - if (updi_link_init_session_parameters(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: Session initialisation failed\n", progname); - return -1; - } - if (updi_link_check(pgm) < 0) { - avrdude_message(MSG_INFO, "%s: Restoring datalink failed\n", progname); - return -1; - } - } - return 0; -} - -int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value) -{ -/* - def ldcs(self, address): - """ - Load data from Control/Status space - - :param address: address to load - """ - self.logger.debug("LDCS from 0x%02X", address) - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LDCS | (address & 0x0F)]) - response = self.updi_phy.receive(self.LDCS_RESPONSE_BYTES) - numbytes_received = len(response) - if numbytes_received != self.LDCS_RESPONSE_BYTES: - raise PymcuprogError("Unexpected number of bytes in response: " - "{} byte(s) expected {} byte(s)".format(numbytes_received, self.LDCS_RESPONSE_BYTES)) - - return response[0] -*/ - unsigned char buffer[2]; - int result; - avrdude_message(MSG_DEBUG, "%s: LDCS from 0x%02X\n", progname, address); - buffer[0]=UPDI_PHY_SYNC; - buffer[1]=UPDI_LDCS | (address & 0x0F); - if (updi_physical_send(pgm, buffer, 2) < 0) { - avrdude_message(MSG_INFO, "%s: LDCS send operation failed\n", progname); - return -1; - } - result = updi_physical_recv(pgm, buffer, 1); - if (result != 1) { - if (result >= 0) { - avrdude_message(MSG_INFO, "%s: Incorrect response size, received %d instead of %d bytes\n", progname, result, 1); - } - return -1; - } - * value = buffer[0]; - return 0; -} - -int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value) -{ -/* - def stcs(self, address, value): - """ - Store a value to Control/Status space - - :param address: address to store to - :param value: value to write - """ - self.logger.debug("STCS to 0x%02X", address) - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_STCS | (address & 0x0F), value]) -*/ - unsigned char buffer[3]; - avrdude_message(MSG_DEBUG, "%s: STCS 0x%02X to address 0x%02X\n", progname, value, address); - buffer[0] = UPDI_PHY_SYNC; - buffer[1] = UPDI_STCS | (address & 0x0F); - buffer[2] = value; - return updi_physical_send(pgm, buffer, 3); -} - -int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) -{ -/* - def ld_ptr_inc(self, size): - """ - Loads a number of bytes from the pointer location with pointer post-increment - - :param size: number of bytes to load - :return: values read - """ - self.logger.debug("LD8 from ptr++") - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC | - constants.UPDI_DATA_8]) - return self.updi_phy.receive(size) -*/ - unsigned char send_buffer[2]; - avrdude_message(MSG_DEBUG, "%s: LD8 from ptr++\n", progname); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_8; - if (updi_physical_send(pgm, send_buffer, 2) < 0) { - avrdude_message(MSG_INFO, "%s: LD_PTR_INC send operation failed\n", progname); - return -1; - } - return updi_physical_recv(pgm, buffer, size); -} - -int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words) -{ -/* - def ld_ptr_inc16(self, words): - """ - Load a 16-bit word value from the pointer location with pointer post-increment - - :param words: number of words to load - :return: values read - """ - self.logger.debug("LD16 from ptr++") - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_LD | constants.UPDI_PTR_INC | - constants.UPDI_DATA_16]) - return self.updi_phy.receive(words << 1) -*/ - unsigned char send_buffer[2]; - avrdude_message(MSG_DEBUG, "%s: LD16 from ptr++\n", progname); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_LD | UPDI_PTR_INC | UPDI_DATA_16; - if (updi_physical_send(pgm, send_buffer, 2) < 0) { - avrdude_message(MSG_INFO, "%s: LD_PTR_INC send operation failed\n", progname); - return -1; - } - return updi_physical_recv(pgm, buffer, words << 2); -} - -int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) -{ -/* - def st_ptr_inc(self, data): - """ - Store data to the pointer location with pointer post-increment - - :param data: data to store - """ - self.logger.debug("ST8 to *ptr++") - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_8, - data[0]]) - response = self.updi_phy.receive(1) - - if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: - raise PymcuprogError("ACK error with st_ptr_inc") - - num = 1 - while num < len(data): - self.updi_phy.send([data[num]]) - response = self.updi_phy.receive(1) - - if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: - raise PymcuprogError("Error with st_ptr_inc") - num += 1 -*/ - unsigned char send_buffer[3]; - unsigned char recv_buffer[1]; - int response; - int num = 1; - avrdude_message(MSG_DEBUG, "%s: ST8 to *ptr++\n", progname); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_8; - send_buffer[2] = buffer[0]; - if (updi_physical_send(pgm, send_buffer, 3) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR_INC send operation failed\n", progname); - return -1; - } - - response = updi_physical_recv(pgm, recv_buffer, 1); - - if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { - avrdude_message(MSG_INFO, "%s: ACK was expected but not received\n", progname); - return -1; - } - - while (num < size) { - send_buffer[0]=buffer[num]; - if (updi_physical_send(pgm, send_buffer, 1) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR_INC data send operation failed\n", progname); - return -1; - } - response = updi_physical_recv(pgm, recv_buffer, 1); - - if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { - avrdude_message(MSG_INFO, "%s: Data ACK was expected but not received\n", progname); - return -1; - } - num++; - } - - return 0; -} - -int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words) -{ -/* - def st_ptr_inc16(self, data): - """ - Store a 16-bit word value to the pointer location with pointer post-increment - - :param data: data to store - """ - self.logger.debug("ST16 to *ptr++") - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | - constants.UPDI_DATA_16, data[0], data[1]]) - response = self.updi_phy.receive(1) - - if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: - raise PymcuprogError("ACK error with st_ptr_inc16") - - num = 2 - while num < len(data): - self.updi_phy.send([data[num], data[num + 1]]) - response = self.updi_phy.receive(1) - - if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: - raise PymcuprogError("Error with st_ptr_inc16") - num += 2 -*/ - unsigned char send_buffer[4]; - unsigned char recv_buffer[1]; - int response; - int num = 2; - avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++\n", progname); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16; - send_buffer[2] = buffer[0]; - send_buffer[3] = buffer[1]; - if (updi_physical_send(pgm, send_buffer, 4) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR_INC16 send operation failed\n", progname); - return -1; - } - - response = updi_physical_recv(pgm, recv_buffer, 1); - - if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { - avrdude_message(MSG_INFO, "%s: ACK was expected but not received\n", progname); - return -1; - } - - while (num < words) { - send_buffer[0]=buffer[num]; - send_buffer[1]=buffer[num+1]; - if (updi_physical_send(pgm, send_buffer, 2) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR_INC data send operation failed\n", progname); - return -1; - } - response = updi_physical_recv(pgm, recv_buffer, 1); - - if (response != 1 || recv_buffer[0] != UPDI_PHY_ACK) { - avrdude_message(MSG_INFO, "%s: Data ACK was expected but not received\n", progname); - return -1; - } - num+=2; - } - - return 0; -} - -int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize) { -/* - def st_ptr_inc16_RSD(self, data, blocksize): - """ - Store a 16-bit word value to the pointer location with pointer post-increment - :param data: data to store - :blocksize: max number of bytes being sent -1 for all. - Warning: This does not strictly honor blocksize for values < 6 - We always glob together the STCS(RSD) and REP commands. - But this should pose no problems for compatibility, because your serial adapter can't deal with 6b chunks, - none of pymcuprog would work! - """ - self.logger.debug("ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d", len(data), blocksize) - - #for performance we glob everything together into one USB transfer.... - repnumber= ((len(data) >> 1) -1) - data = [*data, *[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x06]] - - if blocksize == -1 : - # Send whole thing at once stcs + repeat + st + (data + stcs) - blocksize = 3 + 3 + 2 + len(data) - num = 0 - firstpacket = [] - if blocksize < 10 : - # very small block size - we send pair of 2-byte commands first. - firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA, 0x0E], - *[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)]] - data = [*[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC |constants.UPDI_DATA_16], *data] - num = 0 - else: - firstpacket = [*[constants.UPDI_PHY_SYNC, constants.UPDI_STCS | constants.UPDI_CS_CTRLA , 0x0E], - *[constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, (repnumber & 0xFF)], - *[constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_INC | constants.UPDI_DATA_16], - *data[:blocksize - 8]] - num = blocksize - 8 - self.updi_phy.send( firstpacket ) - - # if finite block size, this is used. - while num < len(data): - data_slice = data[num:num+blocksize] - self.updi_phy.send(data_slice) - num += len(data_slice) -*/ - avrdude_message(MSG_DEBUG, "%s: ST16 to *ptr++ with RSD, data length: 0x%03X in blocks of: %d\n", progname, words * 2, blocksize); - - unsigned int temp_buffer_size = 3 + 3 + 2 + (words * 2) + 3; - unsigned int num=0; - unsigned char* temp_buffer = malloc(temp_buffer_size); - - if (temp_buffer == 0) { - avrdude_message(MSG_INFO, "%s: Allocating temporary buffer failed\n", progname); - return -1; - } - - if (blocksize == -1) { - blocksize = temp_buffer_size; - } - - temp_buffer[0] = UPDI_PHY_SYNC; - temp_buffer[1] = UPDI_STCS | UPDI_CS_CTRLA; - temp_buffer[2] = 0x0E; - temp_buffer[3] = UPDI_PHY_SYNC; - temp_buffer[4] = UPDI_REPEAT | UPDI_REPEAT_BYTE; - temp_buffer[5] = (words - 1) & 0xFF; - temp_buffer[6] = UPDI_PHY_SYNC; - temp_buffer[7] = UPDI_ST | UPDI_PTR_INC | UPDI_DATA_16; - - memcpy(temp_buffer + 8, buffer, words * 2); - - temp_buffer[temp_buffer_size-3] = UPDI_PHY_SYNC; - temp_buffer[temp_buffer_size-2] = UPDI_STCS | UPDI_CS_CTRLA; - temp_buffer[temp_buffer_size-1] = 0x06; - - if (blocksize < 10) { - if (updi_physical_send(pgm, temp_buffer, 6) < 0) { - avrdude_message(MSG_INFO, "%s: Failed to send first package\n", progname); - free(temp_buffer); - return -1; - } - num = 6; - } - - while (num < temp_buffer_size) { - int next_package_size; - - if (num + blocksize > temp_buffer_size) { - next_package_size = temp_buffer_size - num; - } else { - next_package_size = blocksize; - } - - if (updi_physical_send(pgm, temp_buffer + num, next_package_size) < 0) { - avrdude_message(MSG_INFO, "%s: Failed to send package\n", progname); - free(temp_buffer); - return -1; - } - - num+=next_package_size; - } - free(temp_buffer); - return 0; -} - -int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats) -{ -/* - def repeat(self, repeats): - """ - Store a value to the repeat counter - - :param repeats: number of repeats requested - """ - self.logger.debug("Repeat %d", repeats) - if (repeats - 1) > constants.UPDI_MAX_REPEAT_SIZE: - self.logger.error("Invalid repeat count of %d", repeats) - raise Exception("Invalid repeat count!") - repeats -= 1 - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_REPEAT | constants.UPDI_REPEAT_BYTE, - repeats & 0xFF]) -*/ - unsigned char buffer[3]; - avrdude_message(MSG_DEBUG, "%s: Repeat %d\n", progname, repeats); - if ((repeats - 1) > UPDI_MAX_REPEAT_SIZE) { - avrdude_message(MSG_INFO, "%s: Invalid repeat count of %d\n", progname, repeats); - return -1; - } - repeats-=1; - buffer[0] = UPDI_PHY_SYNC; - buffer[1] = UPDI_REPEAT | UPDI_REPEAT_BYTE; - buffer[2] = repeats & 0xFF; - return updi_physical_send(pgm, buffer, 3); -} - -int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) -{ -/* - def read_sib(self): - """ - Read the SIB - """ - return self.updi_phy.sib() -*/ - return updi_physical_sib(pgm, buffer, size); -} - -int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size) -{ -/* - def key(self, size, key): - """ - Write a key - - :param size: size of key (0=64B, 1=128B, 2=256B) - :param key: key value - """ - self.logger.debug("Writing key") - if len(key) != 8 << size: - raise PymcuprogError("Invalid KEY length!") - self.updi_phy.send([constants.UPDI_PHY_SYNC, constants.UPDI_KEY | constants.UPDI_KEY_KEY | size]) - self.updi_phy.send(list(reversed(list(key)))) -*/ - unsigned char send_buffer[2]; - unsigned char reversed_key[256]; - int index; - avrdude_message(MSG_DEBUG, "%s: UPDI writing key\n", progname); - if (size != (8 << size_type)) { - avrdude_message(MSG_INFO, "%s: Invalid key length\n", progname); - return -1; - } - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_KEY | UPDI_KEY_KEY | size_type; - if (updi_physical_send(pgm, send_buffer, 2) < 0) { - avrdude_message(MSG_INFO, "%s: UPDI key send message failed\n", progname); - return -1; - } - /* reverse key contents */ - for (index=0; index> 8) & 0xFF, (address >> 16) & 0xFF]) - return self.updi_phy.receive(1)[0] -*/ - unsigned char send_buffer[5]; - unsigned char recv_buffer[1]; - avrdude_message(MSG_DEBUG, "%s: LD from 0x%06X\n", progname, address); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_LDS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); - send_buffer[2] = address & 0xFF; - send_buffer[3] = (address >> 8) & 0xFF; - send_buffer[4] = (address >> 16) & 0xFF; - if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { - avrdude_message(MSG_INFO, "%s: LD operation send failed\n", progname); - return -1; - } - if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { - avrdude_message(MSG_INFO, "%s: LD operation recv failed\n", progname); - return -1; - } - * value = recv_buffer[0]; - return 0; -} - -int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value) -{ -/* - def ld16(self, address): - """ - Load a 16-bit word directly from a 24-bit address - - :param address: address to load from - :return: values read - """ - self.logger.info("LD from 0x{0:06X}".format(address)) - self.updi_phy.send( - [constants.UPDI_PHY_SYNC, constants.UPDI_LDS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16, - address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) - return self.updi_phy.receive(2) -*/ - unsigned char send_buffer[5]; - unsigned char recv_buffer[2]; - avrdude_message(MSG_DEBUG, "%s: LD16 from 0x%06X\n", progname, address); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_LDS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); - send_buffer[2] = address & 0xFF; - send_buffer[3] = (address >> 8) & 0xFF; - send_buffer[4] = (address >> 16) & 0xFF; - if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { - avrdude_message(MSG_INFO, "%s: LD16 operation send failed\n", progname); - return -1; - } - if (updi_physical_recv(pgm, recv_buffer, 2) < 0) { - avrdude_message(MSG_INFO, "%s: LD16 operation recv failed\n", progname); - return -1; - } - * value = (recv_buffer[0] << 8 | recv_buffer[1]); - return 0; -} - -static int updi_link_st_data_phase(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size) -{ -/* - def _st_data_phase(self, values): - """ - Performs data phase of transaction: - * receive ACK - * send data - - :param values: bytearray of value(s) to send - """ - response = self.updi_phy.receive(1) - if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: - raise PymcuprogError("Error with st") - - self.updi_phy.send(values) - response = self.updi_phy.receive(1) - if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: - raise PymcuprogError("Error with st") -*/ - unsigned char recv_buffer[1]; - if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { - avrdude_message(MSG_INFO, "%s: UPDI data phase recv failed on first ACK\n", progname); - return -1; - } - if (recv_buffer[0] != UPDI_PHY_ACK) { - avrdude_message(MSG_INFO, "%s: UPDI data phase expected first ACK\n", progname); - return -1; - } - if (updi_physical_send(pgm, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: UPDI data phase send failed\n", progname); - return -1; - } - if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { - avrdude_message(MSG_INFO, "%s: UPDI data phase recv failed on second ACK\n", progname); - return -1; - } - if (recv_buffer[0] != UPDI_PHY_ACK) { - avrdude_message(MSG_INFO, "%s: UPDI data phase expected second ACK\n", progname); - return -1; - } - return 0; -} - -int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value) -{ -/* - def st(self, address, value): - """ - Store a single byte value directly to a 24-bit address - - :param address: address to write to - :param value: value to write - """ - self.logger.info("ST to 0x{0:06X}".format(address)) - self.updi_phy.send( - [constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_8, - address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) - return self._st_data_phase([value & 0xFF]) -*/ - unsigned char send_buffer[5]; - avrdude_message(MSG_DEBUG, "%s: ST to 0x%06X\n", progname, address); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_STS | UPDI_DATA_8 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); - send_buffer[2] = address & 0xFF; - send_buffer[3] = (address >> 8) & 0xFF; - send_buffer[4] = (address >> 16) & 0xFF; - if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { - avrdude_message(MSG_INFO, "%s: ST operation send failed\n", progname); - return -1; - } - send_buffer[0] = value; - return updi_link_st_data_phase(pgm, send_buffer, 1); -} - -int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value) -{ -/* - def st16(self, address, value): - """ - Store a 16-bit word value directly to a 24-bit address - - :param address: address to write to - :param value: value to write - """ - self.logger.info("ST to 0x{0:06X}".format(address)) - self.updi_phy.send( - [constants.UPDI_PHY_SYNC, constants.UPDI_STS | constants.UPDI_ADDRESS_24 | constants.UPDI_DATA_16, - address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) - return self._st_data_phase([value & 0xFF, (value >> 8) & 0xFF]) -*/ - unsigned char send_buffer[5]; - avrdude_message(MSG_DEBUG, "%s: ST16 to 0x%06X\n", progname, address); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_STS | UPDI_DATA_16 | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_ADDRESS_24 : UPDI_ADDRESS_16); - send_buffer[2] = address & 0xFF; - send_buffer[3] = (address >> 8) & 0xFF; - send_buffer[4] = (address >> 16) & 0xFF; - if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { - avrdude_message(MSG_INFO, "%s: ST16 operation send failed\n", progname); - return -1; - } - send_buffer[0] = value & 0xFF; - send_buffer[1] = (value >> 8) & 0xFF; - return updi_link_st_data_phase(pgm, send_buffer, 2); -} - -int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address) -{ -/* - def st_ptr(self, address): - """ - Set the pointer location - - :param address: address to write - """ - self.logger.info("ST to ptr") - self.updi_phy.send( - [constants.UPDI_PHY_SYNC, constants.UPDI_ST | constants.UPDI_PTR_ADDRESS | constants.UPDI_DATA_24, - address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF]) - response = self.updi_phy.receive(1) - if len(response) != 1 or response[0] != constants.UPDI_PHY_ACK: - raise PymcuprogError("Error with st_ptr") -*/ - unsigned char send_buffer[5]; - unsigned char recv_buffer[1]; - avrdude_message(MSG_DEBUG, "%s: ST_PTR to 0x%06X\n", progname, address); - send_buffer[0] = UPDI_PHY_SYNC; - send_buffer[1] = UPDI_STS | UPDI_ST | UPDI_PTR_ADDRESS | (updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? UPDI_DATA_24 : UPDI_DATA_16); - send_buffer[2] = address & 0xFF; - send_buffer[3] = (address >> 8) & 0xFF; - send_buffer[4] = (address >> 16) & 0xFF; - if (updi_physical_send(pgm, send_buffer, updi_get_datalink_mode(pgm) == UPDI_LINK_MODE_24BIT ? 5 : 4) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR operation send failed\n", progname); - return -1; - } - if (updi_physical_recv(pgm, recv_buffer, 1) < 0) { - avrdude_message(MSG_INFO, "%s: UPDI ST_PTR recv failed on ACK\n", progname); - return -1; - } - if (recv_buffer[0] != UPDI_PHY_ACK) { - avrdude_message(MSG_INFO, "%s: UPDI ST_PTR expected ACK\n", progname); - return -1; - } - return 0; -} diff --git a/avrdude/updi_link.h b/avrdude/updi_link.h deleted file mode 100644 index 5b5e5bda..00000000 --- a/avrdude/updi_link.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_link_h -#define updi_link_h - -#include "libavrdude.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int updi_link_open(PROGRAMMER * pgm); -void updi_link_close(PROGRAMMER * pgm); -int updi_link_init(PROGRAMMER * pgm); -int updi_link_ldcs(PROGRAMMER * pgm, uint8_t address, uint8_t * value); -int updi_link_stcs(PROGRAMMER * pgm, uint8_t address, uint8_t value); -int updi_link_ld_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); -int updi_link_ld_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); -int updi_link_st_ptr_inc(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); -int updi_link_st_ptr_inc16(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words); -int updi_link_st_ptr_inc16_RSD(PROGRAMMER * pgm, unsigned char * buffer, uint16_t words, int blocksize); -int updi_link_repeat(PROGRAMMER * pgm, uint16_t repeats); -int updi_link_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); -int updi_link_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size); -int updi_link_ld(PROGRAMMER * pgm, uint32_t address, uint8_t * value); -int updi_link_ld16(PROGRAMMER * pgm, uint32_t address, uint16_t * value); -int updi_link_st(PROGRAMMER * pgm, uint32_t address, uint8_t value); -int updi_link_st16(PROGRAMMER * pgm, uint32_t address, uint16_t value); -int updi_link_st_ptr(PROGRAMMER * pgm, uint32_t address); - -#ifdef __cplusplus -} -#endif - -#endif /* updi_link_h */ diff --git a/avrdude/updi_nvm.c b/avrdude/updi_nvm.c deleted file mode 100644 index f3d899a9..00000000 --- a/avrdude/updi_nvm.c +++ /dev/null @@ -1,1275 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_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(PROGRAMMER * pgm, 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 -*/ - avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { - avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_flash_page_V0(PROGRAMMER * pgm, 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]; - avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - data[0] = 0xFF; - if (updi_write_data(pgm, address, data, 1) < 0) { - avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_PAGE) < 0) { - avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_eeprom_V0(PROGRAMMER * pgm, 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") -*/ - avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_ERASE_EEPROM) < 0) { - avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_user_row_V0(PROGRAMMER * pgm, 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]; - avrdude_message(MSG_DEBUG, "%s: Erase user row\n", progname); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - 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) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Load NVM address\n", progname); - if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRL, address & 0xFF) < 0) { - avrdude_message(MSG_INFO, "%s: Write ADDRL operation failed\n", progname); - return -1; - } - if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_ADDRH, (address >> 8) & 0xFF) < 0) { - avrdude_message(MSG_INFO, "%s: Write ADDRH operation failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Load fuse data\n", progname); - if (updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_DATAL, value & 0xFF) < 0) { - avrdude_message(MSG_INFO, "%s: Write DATAL operation failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Execute fuse write\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_WRITE_FUSE) < 0) { - avrdude_message(MSG_INFO, "%s: Write fuse operation failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_write_V0(PROGRAMMER * pgm, 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) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Clear page buffer\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V0_NVMCTRL_CTRLA_PAGE_BUFFER_CLR) < 0) { - avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (mode == USE_WORD_ACCESS) { - if (updi_write_data_words(pgm, address, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); - return -1; - } - } else { - if (updi_write_data(pgm, address, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); - return -1; - } - } - avrdude_message(MSG_DEBUG, "%s: Committing data\n", progname); - if (nvm_command == USE_DEFAULT_COMMAND) { - nvm_command = UPDI_V0_NVMCTRL_CTRLA_WRITE_PAGE; - } - if (updi_nvm_command(pgm, p, nvm_command) < 0) { - avrdude_message(MSG_INFO, "%s: Commit data command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_chip_erase_V2(PROGRAMMER * pgm, 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 -*/ - avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { - avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_flash_page_V2(PROGRAMMER * pgm, 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]; - avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - data[0] = 0xFF; - if (updi_write_data(pgm, address, data, 1) < 0) { - avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { - avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_eeprom_V2(PROGRAMMER * pgm, 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) -*/ - avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { - avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { - avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_user_row_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, - uint16_t size, access_mode mode); - -static int nvm_write_flash_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: NVM EEPROM erase/write command\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_EEPROM_ERASE_WRITE) < 0) { - avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); - return -1; - } - if (updi_write_data(pgm, address, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { - avrdude_message(MSG_INFO, "%s: Clear NVM command failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_write_fuse_V2(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: NVM write command\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_FLASH_WRITE) < 0) { - avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); - return -1; - } - if (mode == USE_WORD_ACCESS) { - if (updi_write_data_words(pgm, address, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); - return -1; - } - } else { - if (updi_write_data(pgm, address, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); - return -1; - } - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Clear NVM command\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V2_NVMCTRL_CTRLA_NOCMD) < 0) { - avrdude_message(MSG_INFO, "%s: Clear NVM command failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_chip_erase_V3(PROGRAMMER * pgm, 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 -*/ - avrdude_message(MSG_DEBUG, "%s: Chip erase using NVM CTRL\n", progname); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_CHIP_ERASE) < 0) { - avrdude_message(MSG_INFO, "%s: Chip erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_flash_page_V3(PROGRAMMER * pgm, 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]; - avrdude_message(MSG_DEBUG, "%s: Erase flash page at address 0x%06X\n", progname, address); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - data[0] = 0xFF; - if (updi_write_data(pgm, address, data, 1) < 0) { - avrdude_message(MSG_INFO, "%s: Dummy write operation failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_ERASE) < 0) { - avrdude_message(MSG_INFO, "%s: Flash page erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_eeprom_V3(PROGRAMMER * pgm, 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") -*/ - avrdude_message(MSG_DEBUG, "%s: Erase EEPROM\n", progname); - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_EEPROM_ERASE) < 0) { - avrdude_message(MSG_INFO, "%s: EEPROM erase command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); - return -1; - } - return 0; -} - -static int nvm_erase_user_row_V3(PROGRAMMER * pgm, 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) -*/ - avrdude_message(MSG_DEBUG, "%s: Erase user row at address 0x%06X\n", progname, address); - - return nvm_erase_flash_page_V3(pgm, p, address); -} - -static int nvm_write_V3(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, - uint16_t size, access_mode mode, uint8_t nvm_command); - -static int nvm_write_flash_V3(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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(PROGRAMMER * pgm, 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) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - avrdude_message(MSG_DEBUG, "%s: Clear page buffer\n", progname); - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_BUFFER_CLEAR) < 0) { - avrdude_message(MSG_INFO, "%s: Clear page operation failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (mode == USE_WORD_ACCESS) { - if (updi_write_data_words(pgm, address, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: Write data words operation failed\n", progname); - return -1; - } - } else { - if (updi_write_data(pgm, address, buffer, size) < 0) { - avrdude_message(MSG_INFO, "%s: Write data operation failed\n", progname); - return -1; - } - } - avrdude_message(MSG_DEBUG, "%s: Committing data\n", progname); - if (nvm_command == USE_DEFAULT_COMMAND) { - nvm_command = UPDI_V3_NVMCTRL_CTRLA_FLASH_PAGE_WRITE; - } - if (updi_nvm_command(pgm, p, nvm_command) < 0) { - avrdude_message(MSG_INFO, "%s: Commit data command failed\n", progname); - return -1; - } - if (updi_nvm_wait_ready(pgm, p) < 0) { - avrdude_message(MSG_INFO, "%s: Wait for ready chip failed\n", progname); - return -1; - } - if (updi_nvm_command(pgm, p, UPDI_V3_NVMCTRL_CTRLA_NOCMD) < 0) { - avrdude_message(MSG_INFO, "%s: Sending empty command failed\n", progname); - return -1; - } - return 0; -} - - -int updi_nvm_chip_erase(PROGRAMMER * pgm, AVRPART * p) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return nvm_chip_erase_V0(pgm, p); - case UPDI_NVM_MODE_V2: - return nvm_chip_erase_V2(pgm, p); - case UPDI_NVM_MODE_V3: - return nvm_chip_erase_V3(pgm, p); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_erase_flash_page(PROGRAMMER * pgm, AVRPART *p, uint32_t address) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return nvm_erase_flash_page_V0(pgm, p, address); - case UPDI_NVM_MODE_V2: - return nvm_erase_flash_page_V2(pgm, p, address); - case UPDI_NVM_MODE_V3: - return nvm_erase_flash_page_V3(pgm, p, address); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_erase_eeprom(PROGRAMMER * pgm, AVRPART *p) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return nvm_erase_eeprom_V0(pgm, p); - case UPDI_NVM_MODE_V2: - return nvm_erase_eeprom_V2(pgm, p); - case UPDI_NVM_MODE_V3: - return nvm_erase_eeprom_V3(pgm, p); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_erase_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint16_t size) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return nvm_erase_user_row_V0(pgm, p, address, size); - case UPDI_NVM_MODE_V2: - return nvm_erase_user_row_V2(pgm, p, address, size); - case UPDI_NVM_MODE_V3: - return nvm_erase_user_row_V3(pgm, p, address, size); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_write_flash(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return nvm_write_flash_V0(pgm, p, address, buffer, size); - case UPDI_NVM_MODE_V2: - return nvm_write_flash_V2(pgm, p, address, buffer, size); - case UPDI_NVM_MODE_V3: - return nvm_write_flash_V3(pgm, p, address, buffer, size); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_write_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return 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); - case UPDI_NVM_MODE_V3: - return nvm_write_user_row_V3(pgm, p, address, buffer, size); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_write_eeprom(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return nvm_write_eeprom_V0(pgm, p, address, buffer, size); - case UPDI_NVM_MODE_V2: - return nvm_write_eeprom_V2(pgm, p, address, buffer, size); - case UPDI_NVM_MODE_V3: - return nvm_write_eeprom_V3(pgm, p, address, buffer, size); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_write_fuse(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint8_t value) -{ - switch(updi_get_nvm_mode(pgm)) - { - case UPDI_NVM_MODE_V0: - return nvm_write_fuse_V0(pgm, p, address, value); - case UPDI_NVM_MODE_V2: - return nvm_write_fuse_V2(pgm, p, address, value); - case UPDI_NVM_MODE_V3: - return nvm_write_fuse_V3(pgm, p, address, value); - default: - avrdude_message(MSG_INFO, "%s: Invalid NVM Mode %d\n", progname, updi_get_nvm_mode(pgm)); - return -1; - } -} - -int updi_nvm_wait_ready(PROGRAMMER * pgm, 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; - struct timeval tv; - uint8_t status; - 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) { - 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; - } while ((current_time - start_time) < 10000000); - - avrdude_message(MSG_INFO, "%s: Wait NVM ready timed out\n", progname); - return -1; -} - -int updi_nvm_command(PROGRAMMER * pgm, 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) -*/ - avrdude_message(MSG_DEBUG, "%s: NVMCMD %d executing\n", progname, command); - - return updi_write_byte(pgm, p->nvm_base + UPDI_NVMCTRL_CTRLA, command); -} diff --git a/avrdude/updi_nvm.h b/avrdude/updi_nvm.h deleted file mode 100644 index c0c1544e..00000000 --- a/avrdude/updi_nvm.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_h -#define updi_nvm_h - -#include "libavrdude.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int updi_nvm_chip_erase(PROGRAMMER * pgm, AVRPART * p); -int updi_nvm_erase_flash_page(PROGRAMMER * pgm, AVRPART *p, uint32_t address); -int updi_nvm_erase_eeprom(PROGRAMMER * pgm, AVRPART *p); -int updi_nvm_erase_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint16_t size); -int updi_nvm_write_flash(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); -int updi_nvm_write_user_row(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); -int updi_nvm_write_eeprom(PROGRAMMER * pgm, AVRPART *p, uint32_t address, unsigned char * buffer, uint16_t size); -int updi_nvm_write_fuse(PROGRAMMER * pgm, AVRPART *p, uint32_t address, uint8_t value); -int updi_nvm_wait_ready(PROGRAMMER * pgm, AVRPART *p); -int updi_nvm_command(PROGRAMMER * pgm, AVRPART *p, uint8_t command); - -#ifdef __cplusplus -} -#endif - -#endif /* updi_nvm_h */ diff --git a/avrdude/updi_readwrite.c b/avrdude/updi_readwrite.c deleted file mode 100644 index e304a510..00000000 --- a/avrdude/updi_readwrite.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_constants.h" -#include "updi_link.h" -#include "updi_readwrite.h" - -int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value) -{ -/* - def read_cs(self, address): - """ - Read from Control/Status space - - :param address: address (index) to read - :return: value read - """ - return self.datalink.ldcs(address) -*/ - return updi_link_ldcs(pgm, address, value); -} - -int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value) -{ -/* - def write_cs(self, address, value): - """ - Write to Control/Status space - - :param address: address (index) to write - :param value: 8-bit value to write - """ - return self.datalink.stcs(address, value) -*/ - return updi_link_stcs(pgm, address, value); -} - -int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size) -{ -/* - def write_key(self, size, key): - """ - Write a KEY into UPDI - - :param size: size of key to send - :param key: key value - """ - return self.datalink.key(size, key) -*/ - return updi_link_key(pgm, buffer, size_type, size); -} - -int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size) -{ -/* - def read_sib(self): - """ - Read the SIB from UPDI - - :return: SIB string (bytearray) read - """ - return self.datalink.read_sib() -*/ - return updi_link_read_sib(pgm, buffer, size); -} - -int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value) -{ -/* - def read_byte(self, address): - """ - Read a single byte from UPDI - - :param address: address to read from - :return: value read - """ - return self.datalink.ld(address) -*/ - return updi_link_ld(pgm, address, value); -} - -int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value) -{ -/* - def write_byte(self, address, value): - """ - Writes a single byte to UPDI - - :param address: address to write to - :param value: value to write - """ - return self.datalink.st(address, value) -*/ - return updi_link_st(pgm, address, value); -} - -int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) -{ -/* - def read_data(self, address, size): - """ - Reads a number of bytes of data from UPDI - - :param address: address to write to - :param size: number of bytes to read - """ - self.logger.debug("Reading %d bytes from 0x%04X", size, address) - # Range check - if size > constants.UPDI_MAX_REPEAT_SIZE: - raise PymcuprogError("Cant read that many bytes in one go") - - # Store the address - self.datalink.st_ptr(address) - - # Fire up the repeat - if size > 1: - self.datalink.repeat(size) - - # Do the read(s) - return self.datalink.ld_ptr_inc(size) -*/ - avrdude_message(MSG_DEBUG, "%s: Reading %d bytes from 0x%06X\n", progname, size, address); - - if (size > UPDI_MAX_REPEAT_SIZE) { - avrdude_message(MSG_INFO, "%s: Can't read that many bytes in one go\n", progname); - return -1; - } - - if (updi_link_st_ptr(pgm, address) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); - return -1; - } - - if (size > 1) { - if (updi_link_repeat(pgm, size) < 0) { - avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); - return -1; - } - } - return updi_link_ld_ptr_inc(pgm, buffer, size); -} - -int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) -{ -/* - def write_data(self, address, data): - """ - Writes a number of bytes to memory - - :param address: address to write to - :param data: data to write - """ - # Special case of 1 byte - if len(data) == 1: - return self.datalink.st(address, data[0]) - # Special case of 2 byte - if len(data) == 2: - self.datalink.st(address, data[0]) - return self.datalink.st(address + 1, data[1]) - - # Range check - if len(data) > constants.UPDI_MAX_REPEAT_SIZE: - raise PymcuprogError("Invalid length") - - # Store the address - self.datalink.st_ptr(address) - - # Fire up the repeat - self.datalink.repeat(len(data)) - return self.datalink.st_ptr_inc(data) -*/ - if (size == 1) { - return updi_link_st(pgm, address, buffer[0]); - } - if (size == 2) { - if (updi_link_st(pgm, address, buffer[0]) < 0) { - avrdude_message(MSG_INFO, "%s: ST operation failed\n", progname); - return -1; - } - return updi_link_st(pgm, address+1, buffer[1]); - } - if (size > UPDI_MAX_REPEAT_SIZE) { - avrdude_message(MSG_INFO, "%s: Invalid length\n", progname); - return -1; - } - if (updi_link_st_ptr(pgm, address) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); - return -1; - } - if (updi_link_repeat(pgm, size) < 0) { - avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); - return -1; - } - return updi_link_st_ptr_inc(pgm, buffer, size); -} - -int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) -{ -/* - def read_data_words(self, address, words): - """ - Reads a number of words of data from UPDI - - :param address: address to write to - :param words: number of words to read - """ - self.logger.debug("Reading %d words from 0x%04X", words, address) - - # Range check - if words > constants.UPDI_MAX_REPEAT_SIZE >> 1: - raise PymcuprogError("Cant read that many words in one go") - - # Store the address - self.datalink.st_ptr(address) - - # Fire up the repeat - if words > 1: - self.datalink.repeat(words) - - # Do the read - return self.datalink.ld_ptr_inc16(words) -*/ - avrdude_message(MSG_DEBUG, "%s: Reading %d words from 0x%06X", progname, size, address); - - if (size > (UPDI_MAX_REPEAT_SIZE >> 1)) { - avrdude_message(MSG_INFO, "%s: Can't read that many words in one go\n", progname); - return -1; - } - - if (updi_link_st_ptr(pgm, address) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); - return -1; - } - - if (size > 1) { - if (updi_link_repeat(pgm, size) < 0) { - avrdude_message(MSG_INFO, "%s: Repeat operation failed\n", progname); - return -1; - } - } - return updi_link_ld_ptr_inc16(pgm, buffer, size); -} - -int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size) -{ -/* - def write_data_words(self, address, data): - """ - Writes a number of words to memory - - :param address: address to write to - :param data: data to write - """ - # Special-case of 1 word - if len(data) == 2: - value = data[0] + (data[1] << 8) - return self.datalink.st16(address, value) - - # Range check - if len(data) > constants.UPDI_MAX_REPEAT_SIZE << 1: - raise PymcuprogError("Invalid length") - - # Store the address - self.datalink.st_ptr(address) - - # Fire up the repeat - self.datalink.repeat(len(data) >> 1) - return self.datalink.st_ptr_inc16(data) -*/ - if (size == 2) { - return updi_link_st16(pgm, address, buffer[0] + (buffer[1] << 8)); - } - if (size > UPDI_MAX_REPEAT_SIZE << 1) { - avrdude_message(MSG_INFO, "%s: Invalid length\n", progname); - return -1; - } - if (updi_link_st_ptr(pgm, address) < 0) { - avrdude_message(MSG_INFO, "%s: ST_PTR operation failed\n", progname); - return -1; - } - return updi_link_st_ptr_inc16_RSD(pgm, buffer, size >> 1, -1); -} diff --git a/avrdude/updi_readwrite.h b/avrdude/updi_readwrite.h deleted file mode 100644 index 9519d179..00000000 --- a/avrdude/updi_readwrite.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_readwrite_h -#define updi_readwrite_h - -#include "libavrdude.h" - -#ifdef __cplusplus -extern "C" { -#endif - -int updi_read_cs(PROGRAMMER * pgm, uint8_t address, uint8_t * value); -int updi_write_cs(PROGRAMMER * pgm, uint8_t address, uint8_t value); -int updi_write_key(PROGRAMMER * pgm, unsigned char * buffer, uint8_t size_type, uint16_t size); -int updi_read_sib(PROGRAMMER * pgm, unsigned char * buffer, uint16_t size); -int updi_read_byte(PROGRAMMER * pgm, uint32_t address, uint8_t * value); -int updi_write_byte(PROGRAMMER * pgm, uint32_t address, uint8_t value); -int updi_read_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); -int updi_write_data(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); -int updi_read_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); -int updi_write_data_words(PROGRAMMER * pgm, uint32_t address, uint8_t * buffer, uint16_t size); - -#ifdef __cplusplus -} -#endif - -#endif /* updi_readwrite_h */ diff --git a/avrdude/updi_state.c b/avrdude/updi_state.c deleted file mode 100644 index 63d80f46..00000000 --- a/avrdude/updi_state.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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 "libavrdude.h" -#include "updi_state.h" - -updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm) -{ - return &((updi_state *)(pgm->cookie))->sib_info; -} - -updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm) -{ - return ((updi_state *)(pgm->cookie))->datalink_mode; -} - -void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode) -{ - ((updi_state *)(pgm->cookie))->datalink_mode = mode; -} - -updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm) -{ - return ((updi_state *)(pgm->cookie))->nvm_mode; -} - -void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode) -{ - ((updi_state *)(pgm->cookie))->nvm_mode = mode; -} diff --git a/avrdude/updi_state.h b/avrdude/updi_state.h deleted file mode 100644 index d4e39d4c..00000000 --- a/avrdude/updi_state.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * avrdude - A Downloader/Uploader for AVR device programmers - * Copyright (C) 2021 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_state_h -#define updi_state_h - -#include "libavrdude.h" - -typedef enum -{ - UPDI_LINK_MODE_16BIT, - UPDI_LINK_MODE_24BIT -} updi_datalink_mode; - -typedef enum -{ - UPDI_NVM_MODE_V0, - UPDI_NVM_MODE_V2, - UPDI_NVM_MODE_V3 -} updi_nvm_mode; - -#define SIB_INFO_STRING_LENGTH 32 -#define SIB_INFO_FAMILY_LENGTH 8 -#define SIB_INFO_NVM_LENGTH 3 -#define SIB_INFO_DEBUG_LENGTH 3 -#define SIB_INFO_PDI_LENGTH 4 -#define SIB_INFO_EXTRA_LENGTH 20 - -typedef struct -{ - unsigned char sib_string[SIB_INFO_STRING_LENGTH+1]; - char family_string[SIB_INFO_FAMILY_LENGTH+1]; - char nvm_string[SIB_INFO_NVM_LENGTH+1]; - char debug_string[SIB_INFO_DEBUG_LENGTH+1]; - char pdi_string[SIB_INFO_PDI_LENGTH+1]; - char extra_string[SIB_INFO_EXTRA_LENGTH+1]; - char nvm_version; - char debug_version; -} updi_sib_info; - -typedef struct -{ - updi_sib_info sib_info; - updi_datalink_mode datalink_mode; - updi_nvm_mode nvm_mode; -} updi_state; - -#ifdef __cplusplus -extern "C" { -#endif - -updi_sib_info* updi_get_sib_info(PROGRAMMER * pgm); -updi_datalink_mode updi_get_datalink_mode(PROGRAMMER * pgm); -void updi_set_datalink_mode(PROGRAMMER * pgm, updi_datalink_mode mode); -updi_nvm_mode updi_get_nvm_mode(PROGRAMMER * pgm); -void updi_set_nvm_mode(PROGRAMMER * pgm, updi_nvm_mode mode); - -#ifdef __cplusplus -} -#endif - -#endif /* updi_state_h */ From c093b21a67a98bba761941a5e19506233ded0aee Mon Sep 17 00:00:00 2001 From: Dawid Buchwald Date: Tue, 21 Dec 2021 21:45:36 +0100 Subject: [PATCH 12/12] Moved SerialUPDI sources to correct location --- serialupdi.c => src/serialupdi.c | 0 serialupdi.h => src/serialupdi.h | 0 updi_constants.h => src/updi_constants.h | 0 updi_link.c => src/updi_link.c | 0 updi_link.h => src/updi_link.h | 0 updi_nvm.c => src/updi_nvm.c | 0 updi_nvm.h => src/updi_nvm.h | 0 updi_readwrite.c => src/updi_readwrite.c | 0 updi_readwrite.h => src/updi_readwrite.h | 0 updi_state.c => src/updi_state.c | 0 updi_state.h => src/updi_state.h | 0 11 files changed, 0 insertions(+), 0 deletions(-) rename serialupdi.c => src/serialupdi.c (100%) rename serialupdi.h => src/serialupdi.h (100%) rename updi_constants.h => src/updi_constants.h (100%) rename updi_link.c => src/updi_link.c (100%) rename updi_link.h => src/updi_link.h (100%) rename updi_nvm.c => src/updi_nvm.c (100%) rename updi_nvm.h => src/updi_nvm.h (100%) rename updi_readwrite.c => src/updi_readwrite.c (100%) rename updi_readwrite.h => src/updi_readwrite.h (100%) rename updi_state.c => src/updi_state.c (100%) rename updi_state.h => src/updi_state.h (100%) diff --git a/serialupdi.c b/src/serialupdi.c similarity index 100% rename from serialupdi.c rename to src/serialupdi.c diff --git a/serialupdi.h b/src/serialupdi.h similarity index 100% rename from serialupdi.h rename to src/serialupdi.h diff --git a/updi_constants.h b/src/updi_constants.h similarity index 100% rename from updi_constants.h rename to src/updi_constants.h diff --git a/updi_link.c b/src/updi_link.c similarity index 100% rename from updi_link.c rename to src/updi_link.c diff --git a/updi_link.h b/src/updi_link.h similarity index 100% rename from updi_link.h rename to src/updi_link.h diff --git a/updi_nvm.c b/src/updi_nvm.c similarity index 100% rename from updi_nvm.c rename to src/updi_nvm.c diff --git a/updi_nvm.h b/src/updi_nvm.h similarity index 100% rename from updi_nvm.h rename to src/updi_nvm.h diff --git a/updi_readwrite.c b/src/updi_readwrite.c similarity index 100% rename from updi_readwrite.c rename to src/updi_readwrite.c diff --git a/updi_readwrite.h b/src/updi_readwrite.h similarity index 100% rename from updi_readwrite.h rename to src/updi_readwrite.h diff --git a/updi_state.c b/src/updi_state.c similarity index 100% rename from updi_state.c rename to src/updi_state.c diff --git a/updi_state.h b/src/updi_state.h similarity index 100% rename from updi_state.h rename to src/updi_state.h