Merge pull request #1171 from stefanrueger/urclock

Provide Urclock programmer
This commit is contained in:
Stefan Rueger 2022-11-22 01:13:57 +00:00 committed by GitHub
commit 02e02be6f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 3001 additions and 73 deletions

View File

@ -225,6 +225,9 @@ add_library(libavrdude
updi_readwrite.h updi_readwrite.h
updi_state.c updi_state.c
updi_state.h updi_state.h
urclock.c
urclock.h
urclock_private.h
usbasp.c usbasp.c
usbasp.h usbasp.h
usbdevs.h usbdevs.h

View File

@ -180,6 +180,9 @@ libavrdude_a_SOURCES = \
updi_readwrite.h \ updi_readwrite.h \
updi_nvm.c \ updi_nvm.c \
updi_nvm.h \ updi_nvm.h \
urclock.c \
urclock.h \
urclock_private.h \
usbdevs.h \ usbdevs.h \
usb_hidapi.c \ usb_hidapi.c \
usb_libusb.c \ usb_libusb.c \

View File

@ -434,7 +434,7 @@ int avr_read_mem(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, con
} }
if (!failure) if (!failure)
return avr_mem_hiaddr(mem); return avr_mem_hiaddr(mem);
/* else: fall back to byte-at-a-time write, for historical reasons */ /* else: fall back to byte-at-a-time read, for historical reasons */
} }
if (strcmp(mem->desc, "signature") == 0) { if (strcmp(mem->desc, "signature") == 0) {
@ -1088,8 +1088,7 @@ int compare_memory_masked(AVRMEM * m, uint8_t b1, uint8_t b2) {
* *
* Return the number of bytes verified, or -1 if they don't match. * Return the number of bytes verified, or -1 if they don't match.
*/ */
int avr_verify(const AVRPART * p, const AVRPART * v, const char * memtype, int size) int avr_verify(const PROGRAMMER *pgm, const AVRPART *p, const AVRPART *v, const char *memtype, int size) {
{
int i; int i;
unsigned char * buf1, * buf2; unsigned char * buf1, * buf2;
int vsize; int vsize;
@ -1118,13 +1117,33 @@ int avr_verify(const AVRPART * p, const AVRPART * v, const char * memtype, int s
size = vsize; size = vsize;
} }
int verror = 0, vroerror = 0, maxerrs = verbose >= MSG_DEBUG? size+1: 10;
for (i=0; i<size; i++) { for (i=0; i<size; i++) {
if ((b->tags[i] & TAG_ALLOCATED) != 0 && buf1[i] != buf2[i]) { if ((b->tags[i] & TAG_ALLOCATED) != 0 && buf1[i] != buf2[i]) {
uint8_t bitmask = get_fuse_bitmask(a); uint8_t bitmask = get_fuse_bitmask(a);
if((buf1[i] & bitmask) != (buf2[i] & bitmask)) { if(pgm->readonly && pgm->readonly(pgm, p, a, i)) {
if(quell_progress < 2) {
if(vroerror < 10) {
if(!(verror + vroerror))
pmsg_warning("verification mismatch%s\n",
avr_mem_is_flash_type(a)? " in r/o areas, expected for vectors and/or bootloader": "");
imsg_warning("device 0x%02x != input 0x%02x at addr 0x%04x (read only location)\n",
buf1[i], buf2[i], i);
} else if(vroerror == 10)
imsg_warning("suppressing further mismatches in read-only areas\n");
}
vroerror++;
} else if((buf1[i] & bitmask) != (buf2[i] & bitmask)) {
// Mismatch is not just in unused bits // Mismatch is not just in unused bits
pmsg_error("verification mismatch, first encountered at addr 0x%04x\n", i); if(verror < maxerrs) {
imsg_error("device 0x%02x != input 0x%02x\n", buf1[i], buf2[i]); if(!(verror + vroerror))
pmsg_warning("verification mismatch\n");
imsg_error("device 0x%02x != input 0x%02x at addr 0x%04x (error)\n", buf1[i], buf2[i], i);
} else if(verror == maxerrs) {
imsg_warning("suppressing further verification errors\n");
}
verror++;
if(verbose < 1)
return -1; return -1;
} else { } else {
// Mismatch is only in unused bits // Mismatch is only in unused bits
@ -1143,7 +1162,7 @@ int avr_verify(const AVRPART * p, const AVRPART * v, const char * memtype, int s
} }
} }
return size; return verror? -1: size;
} }

View File

@ -204,23 +204,6 @@ int avr_is_and(const unsigned char *s1, const unsigned char *s2, const unsigned
} }
static int initCache(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p) {
AVRMEM *basemem = avr_locate_mem(p, cp == pgm->cp_flash? "flash": "eeprom");
if(!basemem || !avr_has_paged_access(pgm, basemem))
return LIBAVRDUDE_GENERAL_FAILURE;
cp->size = basemem->size;
cp->page_size = basemem->page_size;
cp->offset = basemem->offset;
cp->cont = cfg_malloc("initCache()", cp->size);
cp->copy = cfg_malloc("initCache()", cp->size);
cp->iscached = cfg_malloc("initCache()", cp->size/cp->page_size);
return LIBAVRDUDE_SUCCESS;
}
static int cacheAddress(int addr, const AVR_Cache *cp, const AVRMEM *mem) { static int cacheAddress(int addr, const AVR_Cache *cp, const AVRMEM *mem) {
int cacheaddr = addr + (int) (mem->offset - cp->offset); int cacheaddr = addr + (int) (mem->offset - cp->offset);
@ -261,6 +244,29 @@ static int loadCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p,
} }
static int initCache(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p) {
AVRMEM *basemem = avr_locate_mem(p, cp == pgm->cp_flash? "flash": "eeprom");
if(!basemem || !avr_has_paged_access(pgm, basemem))
return LIBAVRDUDE_GENERAL_FAILURE;
cp->size = basemem->size;
cp->page_size = basemem->page_size;
cp->offset = basemem->offset;
cp->cont = cfg_malloc("initCache()", cp->size);
cp->copy = cfg_malloc("initCache()", cp->size);
cp->iscached = cfg_malloc("initCache()", cp->size/cp->page_size);
if((pgm->prog_modes & PM_SPM) && avr_mem_is_flash_type(basemem)) { // Could be vector bootloader
// Caching the vector page gives control to the progammer that then can patch the reset vector
if(loadCachePage(cp, pgm, p, basemem, 0, 0, 0) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
}
return LIBAVRDUDE_SUCCESS;
}
static int writeCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int base, int nlOnErr) { static int writeCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int base, int nlOnErr) {
// Write modified page cont to device; if unsuccessful try bytewise access // Write modified page cont to device; if unsuccessful try bytewise access
if(avr_write_page_default(pgm, p, mem, base, cp->cont + base) < 0) { if(avr_write_page_default(pgm, p, mem, base, cp->cont + base) < 0) {
@ -597,11 +603,15 @@ int avr_read_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *
* - Used if paged routines available and if memory is EEPROM or flash * - Used if paged routines available and if memory is EEPROM or flash
* - Otherwise fall back to pgm->write_byte() * - Otherwise fall back to pgm->write_byte()
* - Out of memory addr: synchronise cache with device and return whether successful * - Out of memory addr: synchronise cache with device and return whether successful
* - If programmer indicates a readonly spot, return LIBAVRDUDE_SOFTFAIL
* - Cache is automagically created and initialised if needed * - Cache is automagically created and initialised if needed
*/ */
int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
unsigned long addr, unsigned char data) { unsigned long addr, unsigned char data) {
if(pgm->readonly && pgm->readonly(pgm, p, mem, addr))
return LIBAVRDUDE_SOFTFAIL;
// Use pgm->write_byte() if not EEPROM/flash or no paged access // Use pgm->write_byte() if not EEPROM/flash or no paged access
if(!avr_has_paged_access(pgm, mem)) if(!avr_has_paged_access(pgm, mem))
return fallback_write_byte(pgm, p, mem, addr, data); return fallback_write_byte(pgm, p, mem, addr, data);
@ -636,9 +646,10 @@ int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p) {
{ avr_locate_mem(p, "flash"), pgm->cp_flash, 1 }, { avr_locate_mem(p, "flash"), pgm->cp_flash, 1 },
{ avr_locate_mem(p, "eeprom"), pgm->cp_eeprom, 0 }, { avr_locate_mem(p, "eeprom"), pgm->cp_eeprom, 0 },
}; };
int rc;
if(pgm->chip_erase(pgm, p) < 0) if((rc = pgm->chip_erase(pgm, p)) < 0)
return LIBAVRDUDE_GENERAL_FAILURE; return rc;
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) { for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
AVRMEM *mem = mems[i].mem; AVRMEM *mem = mems[i].mem;

View File

@ -147,9 +147,17 @@ programming mode. The programmer type is ``wiring''. Note that the -D option
will likely be required in this case, because the bootloader will rewrite the will likely be required in this case, because the bootloader will rewrite the
program memory, but no true chip erase can be performed. program memory, but no true chip erase can be performed.
.Pp .Pp
The Arduino (which is very similar to the STK500 1.x) is supported via Serial bootloaders that run a skeleton of the STK500 1.x protocol are
its own programmer type specification ``arduino''. This programmer works for supported via their own programmer type ``arduino''. This programmer works
the Arduino Uno Rev3 or any AVR that runs the Optiboot bootloader. for the Arduino Uno Rev3 or any AVR that runs the Optiboot bootloader.
.Pp
Urprotocol is a leaner version of the STK500 1.x protocol that is designed
to be backwards compatible with STK500 v1.x, and allows bootloaders to be
much smaller, eg, as implemented in the urboot project
https://github.com/stefanrueger/urboot. The programmer type ``urclock''
caters for these urboot programmers. Owing to its backward compatibility,
any bootloader that can be served by the arduino programmer can normally
also be served by the urclock programmer.
.Pp .Pp
The BusPirate is a versatile tool that can also be used as an AVR programmer. The BusPirate is a versatile tool that can also be used as an AVR programmer.
A single BusPirate can be connected to up to 3 independent AVRs. See A single BusPirate can be connected to up to 3 independent AVRs. See
@ -397,8 +405,8 @@ programming requires the memory be erased to 0xFF beforehand.
.Fl A .Fl A
should be used when the programmer hardware, or bootloader should be used when the programmer hardware, or bootloader
software for that matter, does not carry out chip erase and software for that matter, does not carry out chip erase and
instead handles the memory erase on a page level. The popular instead handles the memory erase on a page level. Popular
Arduino bootloader exhibits this behaviour; for this reason Arduino bootloaders exhibit this behaviour; for this reason
.Fl A .Fl A
is engaged by default when specifying is engaged by default when specifying
. Fl c . Fl c
@ -432,7 +440,7 @@ contents would exclusively cause bits to be programmed from the value
.Ql 1 .Ql 1
to to
.Ql 0 . .Ql 0 .
Note that in order to reprogram EERPOM cells, no explicit prior chip Note that in order to reprogram EEPROM cells, no explicit prior chip
erase is required since the MCU provides an auto-erase cycle in that erase is required since the MCU provides an auto-erase cycle in that
case before programming the cell. case before programming the cell.
.It Xo Fl E Ar exitspec Ns .It Xo Fl E Ar exitspec Ns
@ -1141,6 +1149,106 @@ programmer creates errors during initial sequence.
Specify how many connection retry attemps to perform before exiting. Specify how many connection retry attemps to perform before exiting.
Defaults to 10 if not specified. Defaults to 10 if not specified.
.El .El
.It Ar Urclock
.Bl -tag -offset indent -width indent
.It Ar showall
Show all info for the connected part, then exit. The -xshow... options
below can be used to assemble a bespoke response consisting of a subset
(or only one item) of all available relevant information about the
connected part and bootloader.
.It Ar showid
Show a unique Urclock ID stored in either flash or EEPROM of the MCU, then exit.
.It Ar id=<E|F>.<addr>.<len>
Historically, the Urclock ID was a six-byte unique little-endian number
stored in Urclock boards at EEPROM address 257. The location of this
number can be set by the -xid=<E|F>.<addr>.<len> extended parameter. E
stands for EEPROM and F stands for flash. A negative address addr counts
from the end of EEPROM and flash, respectively. The length len of the
Urclock ID can be between 1 and 8 bytes.
.It Ar showdate
Show the last-modified date of the input file for the flash application,
then exit. If the input file was stdin, the date will be that of the
programming.
.It Ar showfilename
Show the input filename (or title) of the last flash writing session, then exit.
.It Ar title=<string>
When set, <string> will be used in lieu of the input filename. The maximum
string length for the title/filename field is 254 bytes including
terminating nul.
.It Ar showapp
Show the size of the programmed application, then exit.
.It Ar showstore
Show the size of the unused flash between the application and metadata, then exit.
.It Ar showmeta
Show the size of the metadata just below the bootloader, then exit.
.It Ar showboot
Show the size of the bootloader, then exit.
.It Ar showversion
Show bootloader version and capabilities, then exit.
.It Ar showvector
Show the vector number and name of the interrupt table vector used by the
bootloader for starting the application, then exit. For hardware-supported
bootloaders this will be vector 0 (Reset), and for vector bootloaders this
will be any other vector number of the interrupt vector table or the slot
just behind the vector table with the name VBL_ADDITIONAL_VECTOR.
.It Ar showpart
Show the part for which the bootloader was compiled, then exit.
.It Ar bootsize=<size>
Manual override for bootloader size. Urboot bootloaders put the number of used
bootloader pages into a table at the top of flash, so the urclock programmer can
look up the bootloader size itself. In backward-compatibility mode, when programming
via other bootloaders, this option can be used to tell the programmer the
size, and therefore the location, of the bootloader.
.It Ar vectornum=<arg>
Manual override for vector number. Urboot bootloaders put the vector
number used by a vector bootloader into a table at the top of flash, so
this option is normally not needed for urboot bootloaders. However, it is
useful in backward-compatibility mode (or when the urboot bootloader does
not offer flash read). Specifying a vector number in these circumstances
implies a vector bootloader whilst the default assumption would be a
hardware-supported bootloader.
.It Ar eepromrw
Manual override for asserting EEPROM read/write capability. Not normally
needed for urboot bootloaders, but useful for in backward-compatibility
mode if the bootloader offers EEPROM read/write.
.It Ar emulate_ce
If an urboot bootloader does not offer a chip erase command it will tell
the urclock programmer so during handshake. In this case the urclock
programmer emulates a chip erase, if warranted by user command line
options, by filling the remainder of unused flash below the bootloader
with 0xff. If this option is specified, the urclock programmer will assume
that the bootloader cannot erase the chip itself. The option is useful
for backwards-compatible bootloaders that do not implement chip erase.
.It Ar restore
Upload unchanged flash input files and trim below the bootloader if
needed. This is most useful when one has a backup of the full flash and
wants to play that back onto the device. No metadata are written in this
case and no vector patching happens either if it is a vector bootloader.
However, for vector bootloaders, even under the option -xrestore an
input file will not be uploaded for which the reset vector does not point
to the vector bootloader. This is to avoid writing an input file to the
device that would render the vector bootloader not functional as it would
not be reached after reset.
.It Ar initstore
On writing to flash fill the store space between the flash application and
the metadata section with 0xff.
.It Ar nofilename
On writing to flash do not store the application input filename (nor a title).
.It Ar nodate
On writing to flash do not store the application input filename (nor a
title) and no date either.
.It Ar nometadata
On writing to flash do not store any metadata. The full flash below the
bootloader is available for the application. In particular, no data store
frame is programmed.
.It Ar delay=<n>
Add a <n> ms delay after reset. This can be useful if a board takes a
particularly long time to exit from external reset. <n> can be negative,
in which case the default 80 ms delay after issuing reset will be
shortened accordingly.
.It Ar help
Show this help menu and exit
.El
.It Ar buspirate .It Ar buspirate
.Bl -tag -offset indent -width indent .Bl -tag -offset indent -width indent
.It Ar reset={cs,aux,aux2} .It Ar reset={cs,aux,aux2}

View File

@ -71,7 +71,7 @@
# prog_modes = PM_<i/f> {| PM_<i/f>} # interfaces, eg, PM_SPM|PM_ISP|PM_HVPP|PM_debugWIRE # prog_modes = PM_<i/f> {| PM_<i/f>} # interfaces, eg, PM_SPM|PM_ISP|PM_HVPP|PM_debugWIRE
# mcuid = <num>; # unique id in 0..2039 for 8-bit AVRs # mcuid = <num>; # unique id in 0..2039 for 8-bit AVRs
# n_interrupts = <num>; # number of interrupts, used for vector bootloaders # n_interrupts = <num>; # number of interrupts, used for vector bootloaders
# n_page_erase = <num>; # if set, number of pages erased during NVM erase # n_page_erase = <num>; # if set, number of pages erased during SPM erase
# hvupdi_variant = <num> ; # numeric -1 (n/a) or 0..2 # hvupdi_variant = <num> ; # numeric -1 (n/a) or 0..2
# devicecode = <num> ; # deprecated, use stk500_devcode # devicecode = <num> ; # deprecated, use stk500_devcode
# stk500_devcode = <num> ; # numeric # stk500_devcode = <num> ; # numeric
@ -756,6 +756,19 @@ programmer
connection_type = serial; connection_type = serial;
; ;
#------------------------------------------------------------
# urclock
#------------------------------------------------------------
# See https://github.com/stefanrueger/urboot
programmer
id = "urclock";
desc = "Urclock programmer for urboot bootloaders using urprotocol";
type = "urclock";
prog_modes = PM_SPM;
connection_type = serial;
;
#------------------------------------------------------------ #------------------------------------------------------------
# xbee # xbee
#------------------------------------------------------------ #------------------------------------------------------------

View File

@ -6,10 +6,10 @@
* Atmel AVR8L, AVR8, XMEGA and AVR8X family description of interrupts and more * Atmel AVR8L, AVR8, XMEGA and AVR8X family description of interrupts and more
* *
* published under GNU General Public License, version 3 (GPL-3.0) * published under GNU General Public License, version 3 (GPL-3.0)
* meta-author: Stefan Rueger <stefan.rueger@urclocks.com> * meta-author Stefan Rueger <stefan.rueger@urclocks.com>
* *
* v 1.1 * v 1.1
* 30.08.2022 * 20.11.2022
* *
*/ */
@ -256,9 +256,9 @@ const uPcore_t uP_table[] = { // Value of -1 typically means unknown
{"ATA8515", 224, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42, vtab_ata8515}, // atdf {"ATA8515", 224, F_AVR8, {0x1E, 0x95, 0x63}, -1, -1, -1, 0, 0, 0, 0x0400, 16, 0x0200, 0x0400, 1, 1, 42, vtab_ata8515}, // atdf
{"ATA664251", 225, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20, vtab_attiny167}, // atdf, avr-gcc 12.2.0 {"ATA664251", 225, F_AVR8, {0x1E, 0x94, 0x87}, 0, 0x04000, 0x080, 0, 0, 0, 0x0200, 4, 0x0100, 0x0200, 3, 1, 20, vtab_attiny167}, // atdf, avr-gcc 12.2.0
{"M3000", 226, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x1000, 0x1000, -1, -1, 0, NULL}, // avr-gcc 12.2.0 {"M3000", 226, F_AVR8, {0xff, -1, -1}, 0, 0x10000, -1, -1, -1, -1, -1, -1, 0x1000, 0x1000, -1, -1, 0, NULL}, // avr-gcc 12.2.0
{"LGT8F88P", 227, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, -1, -1, 0, 0x0200, 4, -1, -1, -1, -1, 0, NULL}, // avrdude {"LGT8F88P", 227, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26, vtab_atmega328p}, // avrdude, from ATmega88
{"LGT8F168P", 228, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, -1, -1, 0, 0x0200, 4, -1, -1, -1, -1, 0, NULL}, // avrdude {"LGT8F168P", 228, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, 4, 0x0100, 0, 0x0200, 4, 0x0100, 0x0400, 3, 1, 26, vtab_atmega328p}, // avrdude, from ATmega168P
{"LGT8F328P", 229, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, -1, -1, 0, 0x0400, 4, -1, -1, -1, -1, 0, NULL}, // avrdude {"LGT8F328P", 229, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, 4, 0x0200, 0, 0x0400, 4, 0x0100, 0x0800, 3, 1, 26, vtab_atmega328p}, // avrdude, from ATmega328P
{"ATxmega8E5", 230, F_XMEGA, {0x1E, 0x93, 0x41}, 0, 0x02800, 0x080, 1, 0x0800, 0, 0x0200, 32, 0x2000, 0x0400, 7, 1, 43, vtab_atxmega32e5}, // atdf, avr-gcc 12.2.0, avrdude {"ATxmega8E5", 230, F_XMEGA, {0x1E, 0x93, 0x41}, 0, 0x02800, 0x080, 1, 0x0800, 0, 0x0200, 32, 0x2000, 0x0400, 7, 1, 43, vtab_atxmega32e5}, // atdf, avr-gcc 12.2.0, avrdude
{"ATxmega16A4", 231, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 94, vtab_atxmega32a4}, // atdf, avr-gcc 12.2.0, avrdude {"ATxmega16A4", 231, F_XMEGA, {0x1E, 0x94, 0x41}, 0, 0x05000, 0x100, 1, 0x1000, 0, 0x0400, 32, 0x2000, 0x0800, 6, 1, 94, vtab_atxmega32a4}, // atdf, avr-gcc 12.2.0, avrdude

View File

@ -9,7 +9,7 @@
* meta-author Stefan Rueger <stefan.rueger@urclocks.com> * meta-author Stefan Rueger <stefan.rueger@urclocks.com>
* *
* v 1.1 * v 1.1
* 30.08.2022 * 20.11.2022
* *
*/ */
@ -770,7 +770,7 @@ typedef struct { // Value of -1 typically means unknown
#define vts_avr128da64 64 #define vts_avr128da64 64
#define vts_avr128db64 65 #define vts_avr128db64 65
// Suggested vector bootloader interrupt number (first unused vector or, failing that, slot just above vector table) // Suggested vector bootloader interrupt: first unused vector or slot just above vector table
#define vbu_attiny4 10 #define vbu_attiny4 10
#define vbu_attiny5 11 #define vbu_attiny5 11
#define vbu_attiny9 10 #define vbu_attiny9 10

View File

@ -704,7 +704,7 @@ char *cfg_escape(const char *s) {
char buf[50*1024], *d = buf; char buf[50*1024], *d = buf;
*d++ = '"'; *d++ = '"';
for(; *s && d-buf < sizeof buf-7; s++) { for(; *s && d-buf < (long) sizeof buf-7; s++) {
switch(*s) { switch(*s) {
case '\n': case '\n':
*d++ = '\\'; *d++ = 'n'; *d++ = '\\'; *d++ = 'n';
@ -855,7 +855,7 @@ void cfg_update_mcuid(AVRPART *part) {
return; return;
// Find an entry that shares the same name, overwrite mcuid with known, existing mcuid // Find an entry that shares the same name, overwrite mcuid with known, existing mcuid
for(int i=0; i < sizeof uP_table/sizeof *uP_table; i++) { for(size_t i=0; i < sizeof uP_table/sizeof *uP_table; i++) {
if(strcasecmp(part->desc, uP_table[i].name) == 0) { if(strcasecmp(part->desc, uP_table[i].name) == 0) {
if(part->mcuid != (int) uP_table[i].mcuid) { if(part->mcuid != (int) uP_table[i].mcuid) {
if(part->mcuid >= 0 && verbose >= MSG_DEBUG) if(part->mcuid >= 0 && verbose >= MSG_DEBUG)
@ -867,7 +867,7 @@ void cfg_update_mcuid(AVRPART *part) {
} }
// None have the same name: an entry with part->mcuid might be an error // None have the same name: an entry with part->mcuid might be an error
for(int i=0; i < sizeof uP_table/sizeof *uP_table; i++) for(size_t i=0; i < sizeof uP_table/sizeof *uP_table; i++)
if(part->mcuid == (int) uP_table[i].mcuid) { if(part->mcuid == (int) uP_table[i].mcuid) {
// Complain unless it can be considered a variant, eg, ATmega32L and ATmega32 // Complain unless it can be considered a variant, eg, ATmega32L and ATmega32
AVRMEM *flash = avr_locate_mem(part, "flash"); AVRMEM *flash = avr_locate_mem(part, "flash");
@ -876,7 +876,7 @@ void cfg_update_mcuid(AVRPART *part) {
if(strncasecmp(part->desc, uP_table[i].name, l1 < l2? l1: l2) || if(strncasecmp(part->desc, uP_table[i].name, l1 < l2? l1: l2) ||
flash->size != uP_table[i].flashsize || flash->size != uP_table[i].flashsize ||
flash->page_size != uP_table[i].pagesize || flash->page_size != uP_table[i].pagesize ||
part->n_interrupts != uP_table[i].ninterrupts) part->n_interrupts != (int8_t) uP_table[i].ninterrupts)
yywarning("mcuid %d is reserved for %s, use a free number >= %d", yywarning("mcuid %d is reserved for %s, use a free number >= %d",
part->mcuid, uP_table[i].name, sizeof uP_table/sizeof *uP_table); part->mcuid, uP_table[i].name, sizeof uP_table/sizeof *uP_table);
} }

View File

@ -1152,9 +1152,9 @@ static void dev_pgm_raw(const PROGRAMMER *pgm) {
dev_raw_dump(dp.usbproduct, strlen(dp.usbproduct)+1, id, "usbprod", 0); dev_raw_dump(dp.usbproduct, strlen(dp.usbproduct)+1, id, "usbprod", 0);
// Zap all bytes beyond terminating nul of desc, type and port array // Zap all bytes beyond terminating nul of desc, type and port array
if((len = strlen(dp.type)+1) < sizeof dp.type) if((len = (int) strlen(dp.type)+1) < (int) sizeof dp.type)
memset(dp.type + len, 0, sizeof dp.type - len); memset(dp.type + len, 0, sizeof dp.type - len);
if((len = strlen(dp.port)+1) < sizeof dp.port) if((len = (int) strlen(dp.port)+1) < (int) sizeof dp.port)
memset(dp.port + len, 0, sizeof dp.port - len); memset(dp.port + len, 0, sizeof dp.port - len);
// Zap address values // Zap address values

View File

@ -1860,7 +1860,7 @@ part
prog_modes = PM_<i/f> @{| PM_<i/f>@} # interfaces, eg, PM_SPM|PM_ISP|PM_HVPP|PM_debugWIRE prog_modes = PM_<i/f> @{| PM_<i/f>@} # interfaces, eg, PM_SPM|PM_ISP|PM_HVPP|PM_debugWIRE
mcuid = <num>; # unique id in 0..2039 for 8-bit AVRs mcuid = <num>; # unique id in 0..2039 for 8-bit AVRs
n_interrupts = <num>; # number of interrupts, used for vector bootloaders n_interrupts = <num>; # number of interrupts, used for vector bootloaders
n_page_erase = <num>; # if set, number of pages erased during NVM erase n_page_erase = <num>; # if set, number of pages erased during SPM erase
hvupdi_variant = <num> ; # numeric -1 (n/a) or 0..2 hvupdi_variant = <num> ; # numeric -1 (n/a) or 0..2
devicecode = <num> ; # deprecated, use stk500_devcode devicecode = <num> ; # deprecated, use stk500_devcode
stk500_devcode = <num> ; # numeric stk500_devcode = <num> ; # numeric

View File

@ -580,6 +580,7 @@ const char * pinmask_to_str(const pinmask_t * const pinmask);
The target file will be selected at configure time. */ The target file will be selected at configure time. */
extern long serial_recv_timeout; /* ms */ extern long serial_recv_timeout; /* ms */
extern long serial_drain_timeout; /* ms */
union filedescriptor union filedescriptor
{ {
@ -798,6 +799,7 @@ typedef struct programmer_t {
int (*parseextparams) (const struct programmer_t *pgm, const LISTID xparams); int (*parseextparams) (const struct programmer_t *pgm, const LISTID xparams);
void (*setup) (struct programmer_t *pgm); void (*setup) (struct programmer_t *pgm);
void (*teardown) (struct programmer_t *pgm); void (*teardown) (struct programmer_t *pgm);
int (*flash_readhook) (const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *flm, const char *fname, int size);
// Cached r/w API for terminal reads/writes // Cached r/w API for terminal reads/writes
int (*write_byte_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m, int (*write_byte_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m,
unsigned long addr, unsigned char value); unsigned long addr, unsigned char value);
@ -806,6 +808,8 @@ typedef struct programmer_t {
int (*chip_erase_cached)(const struct programmer_t *pgm, const AVRPART *p); int (*chip_erase_cached)(const struct programmer_t *pgm, const AVRPART *p);
int (*page_erase_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m, int (*page_erase_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m,
unsigned int baseaddr); unsigned int baseaddr);
int (*readonly) (const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m,
unsigned int addr);
int (*flush_cache) (const struct programmer_t *pgm, const AVRPART *p); int (*flush_cache) (const struct programmer_t *pgm, const AVRPART *p);
int (*reset_cache) (const struct programmer_t *pgm, const AVRPART *p); int (*reset_cache) (const struct programmer_t *pgm, const AVRPART *p);
AVR_Cache *cp_flash, *cp_eeprom; AVR_Cache *cp_flash, *cp_eeprom;
@ -885,7 +889,7 @@ int avr_write(const PROGRAMMER *pgm, const AVRPART *p, const char *memtype, int
int avr_signature(const PROGRAMMER *pgm, const AVRPART *p); int avr_signature(const PROGRAMMER *pgm, const AVRPART *p);
int avr_verify(const AVRPART * p, const AVRPART * v, const char * memtype, int size); int avr_verify(const PROGRAMMER *pgm, const AVRPART *p, const AVRPART *v, const char *m, int size);
int avr_get_cycle_count(const PROGRAMMER *pgm, const AVRPART *p, int *cycles); int avr_get_cycle_count(const PROGRAMMER *pgm, const AVRPART *p, int *cycles);

View File

@ -1393,9 +1393,12 @@ int main(int argc, char * argv [])
if (uflags & UF_NOWRITE) { if (uflags & UF_NOWRITE) {
pmsg_warning("conflicting -e and -n options specified, NOT erasing chip\n"); pmsg_warning("conflicting -e and -n options specified, NOT erasing chip\n");
} else { } else {
msg_info("erasing chip\n"); pmsg_info("erasing chip\n");
exitrc = avr_chip_erase(pgm, p); exitrc = avr_chip_erase(pgm, p);
if(exitrc) if(exitrc == LIBAVRDUDE_SOFTFAIL) {
imsg_info("delaying chip erase until first -U upload to flash\n");
exitrc = 1;
} else if(exitrc)
goto main_exit; goto main_exit;
} }
} }

View File

@ -154,6 +154,8 @@ PROGRAMMER *pgm_new(void) {
pgm->parseextparams = NULL; pgm->parseextparams = NULL;
pgm->setup = NULL; pgm->setup = NULL;
pgm->teardown = NULL; pgm->teardown = NULL;
pgm->readonly = NULL;
pgm->flash_readhook = NULL;
// For allocating "global" memory by the programmer // For allocating "global" memory by the programmer
pgm->cookie = NULL; pgm->cookie = NULL;

View File

@ -51,6 +51,7 @@
#include "stk500generic.h" #include "stk500generic.h"
#include "stk500v2.h" #include "stk500v2.h"
#include "teensy.h" #include "teensy.h"
#include "urclock.h"
#include "usbasp.h" #include "usbasp.h"
#include "usbtiny.h" #include "usbtiny.h"
#include "wiring.h" #include "wiring.h"
@ -102,6 +103,7 @@ const PROGRAMMER_TYPE programmers_types[] = { // Name(s) the programmers call th
{"stk600hvsp", stk600hvsp_initpgm, stk600hvsp_desc}, // "STK600HVSP" {"stk600hvsp", stk600hvsp_initpgm, stk600hvsp_desc}, // "STK600HVSP"
{"stk600pp", stk600pp_initpgm, stk600pp_desc}, // "STK600PP" {"stk600pp", stk600pp_initpgm, stk600pp_desc}, // "STK600PP"
{"teensy", teensy_initpgm, teensy_desc}, // "teensy" {"teensy", teensy_initpgm, teensy_desc}, // "teensy"
{"urclock", urclock_initpgm, urclock_desc}, // "Urclock"
{"usbasp", usbasp_initpgm, usbasp_desc}, // "usbasp" {"usbasp", usbasp_initpgm, usbasp_desc}, // "usbasp"
{"usbtiny", usbtiny_initpgm, usbtiny_desc}, // "USBtiny" or "usbtiny" {"usbtiny", usbtiny_initpgm, usbtiny_desc}, // "USBtiny" or "usbtiny"
{"wiring", wiring_initpgm, wiring_desc}, // "Wiring" {"wiring", wiring_initpgm, wiring_desc}, // "Wiring"
@ -171,5 +173,3 @@ void walk_programmer_types(walk_programmer_types_cb cb, void *cookie)
cb(p->id, p->desc, cookie); cb(p->id, p->desc, cookie);
} }
} }

View File

@ -52,6 +52,7 @@
#include "libavrdude.h" #include "libavrdude.h"
long serial_recv_timeout = 5000; /* ms */ long serial_recv_timeout = 5000; /* ms */
long serial_drain_timeout = 250; /* ms */
struct baud_mapping { struct baud_mapping {
long baud; long baud;
@ -550,7 +551,7 @@ static int ser_drain(const union filedescriptor *fd, int display) {
unsigned char buf; unsigned char buf;
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 250000; timeout.tv_usec = serial_drain_timeout*1000L;
if (display) { if (display) {
msg_info("drain>"); msg_info("drain>");

View File

@ -37,6 +37,7 @@
#include "libavrdude.h" #include "libavrdude.h"
long serial_recv_timeout = 5000; /* ms */ long serial_recv_timeout = 5000; /* ms */
long serial_drain_timeout = 250; /* ms */
#define W32SERBUFSIZE 1024 #define W32SERBUFSIZE 1024
@ -635,7 +636,7 @@ static int net_drain(const union filedescriptor *fd, int display) {
} }
timeout.tv_sec = 0; timeout.tv_sec = 0;
timeout.tv_usec = 250000; timeout.tv_usec = serial_drain_timeout*1000L;
while (1) { while (1) {
FD_ZERO(&rfds); FD_ZERO(&rfds);
@ -712,7 +713,7 @@ static int ser_drain(const union filedescriptor *fd, int display) {
return -1; return -1;
} }
serial_w32SetTimeOut(hComPort,250); serial_w32SetTimeOut(hComPort, serial_drain_timeout);
if (display) { if (display) {
msg_info("drain>"); msg_info("drain>");

View File

@ -187,7 +187,7 @@ static int chardump_line(char *buffer, unsigned char *p, int n, int pad) {
unsigned char b[128]; unsigned char b[128];
// Sanity check // Sanity check
n = n < 1? 1: n > sizeof b? sizeof b: n; n = n < 1? 1: n > (int) sizeof b? (int) sizeof b: n;
memcpy(b, p, n); memcpy(b, p, n);
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
@ -664,19 +664,21 @@ static int cmd_write(PROGRAMMER *pgm, AVRPART *p, int argc, char *argv[]) {
report_progress(0, 1, avr_has_paged_access(pgm, mem)? "Caching": "Writing"); report_progress(0, 1, avr_has_paged_access(pgm, mem)? "Caching": "Writing");
for (i = 0; i < len + data.bytes_grown; i++) { for (i = 0; i < len + data.bytes_grown; i++) {
int rc = pgm->write_byte_cached(pgm, p, mem, addr+i, buf[i]); int rc = pgm->write_byte_cached(pgm, p, mem, addr+i, buf[i]);
if (rc) { if (rc == LIBAVRDUDE_SOFTFAIL) {
pmsg_warning("(write) programmer write protects %s address 0x%04x\n", mem->desc, addr+i);
} else if(rc) {
pmsg_error("(write) error writing 0x%02x at 0x%05lx, rc=%d\n", buf[i], (long) addr+i, (int) rc); pmsg_error("(write) error writing 0x%02x at 0x%05lx, rc=%d\n", buf[i], (long) addr+i, (int) rc);
if (rc == -1) if (rc == -1)
imsg_error("%*swrite operation not supported on memory type %s\n", 8, "", mem->desc); imsg_error("%*swrite operation not supported on memory type %s\n", 8, "", mem->desc);
werror = true; werror = true;
} } else {
uint8_t b; uint8_t b;
rc = pgm->read_byte_cached(pgm, p, mem, addr+i, &b); rc = pgm->read_byte_cached(pgm, p, mem, addr+i, &b);
if (b != buf[i]) { if (b != buf[i]) {
pmsg_error("(write) error writing 0x%02x at 0x%05lx cell=0x%02x\n", buf[i], (long) addr+i, b); pmsg_error("(write) verification error writing 0x%02x at 0x%05lx cell=0x%02x\n", buf[i], (long) addr+i, b);
werror = true; werror = true;
} }
}
if (werror) if (werror)
pgm->err_led(pgm, ON); pgm->err_led(pgm, ON);
@ -755,8 +757,47 @@ static int cmd_send(PROGRAMMER *pgm, AVRPART *p, int argc, char *argv[]) {
static int cmd_erase(PROGRAMMER *pgm, AVRPART *p, int argc, char *argv[]) { static int cmd_erase(PROGRAMMER *pgm, AVRPART *p, int argc, char *argv[]) {
term_out("erasing chip ...\n"); term_out("erasing chip ...\n");
// Erase chip and clear cache // Erase chip and clear cache
pgm->chip_erase_cached(pgm, p); int rc = pgm->chip_erase_cached(pgm, p);
if(rc == LIBAVRDUDE_SOFTFAIL) {
pmsg_info("(erase) emulating chip erase by writing 0xff to flash ");
AVRMEM *flm = avr_locate_mem(p, "flash");
if(!flm) {
msg_error("but flash not defined for part %s?\n", p->desc);
return -1;
}
int addr, beg = 0, end = flm->size-1;
if(pgm->readonly) {
for(addr=beg; addr < flm->size; addr++)
if(!pgm->readonly(pgm, p, flm, addr)) {
beg = addr;
break;
}
if(addr >= flm->size) {
msg_info("but all flash is write protected\n");
return 0;
}
for(addr=end; addr >= 0; addr--)
if(!pgm->readonly(pgm, p, flm, addr)) {
end = addr;
break;
}
}
msg_info("[0x%04x, 0x%04x]; undo with abort\n", beg, end);
for(int addr=beg; addr <= end; addr++)
if(!pgm->readonly || !pgm->readonly(pgm, p, flm, addr))
if(pgm->write_byte_cached(pgm, p, flm, addr, 0xff) == -1)
return -1;
return 0;
}
if(rc) {
pmsg_error("(erase) programmer %s failed erasing the chip\n", (char *) ldata(lfirst(pgm->id)));
return -1;
}
return 0; return 0;
} }

View File

@ -431,7 +431,7 @@ int do_op(const PROGRAMMER *pgm, const AVRPART *p, UPDATE *upd, enum updateflags
AVRMEM *mem; AVRMEM *mem;
int size; int size;
int rc; int rc;
Filestats fs; Filestats fs, fs_patched;
mem = avr_locate_mem(p, upd->memtype); mem = avr_locate_mem(p, upd->memtype);
if (mem == NULL) { if (mem == NULL) {
@ -486,11 +486,10 @@ int do_op(const PROGRAMMER *pgm, const AVRPART *p, UPDATE *upd, enum updateflags
return LIBAVRDUDE_GENERAL_FAILURE; return LIBAVRDUDE_GENERAL_FAILURE;
} }
size = rc;
pmsg_info("reading input file %s for %s%s\n", pmsg_info("reading input file %s for %s%s\n",
update_inname(upd->filename), mem->desc, alias_mem_desc); update_inname(upd->filename), mem->desc, alias_mem_desc);
if(memstats(p, upd->memtype, size, &fs) < 0) if(memstats(p, upd->memtype, rc, &fs) < 0)
return LIBAVRDUDE_GENERAL_FAILURE; return LIBAVRDUDE_GENERAL_FAILURE;
imsg_info("with %d byte%s in %d section%s within %s\n", imsg_info("with %d byte%s in %d section%s within %s\n",
@ -507,6 +506,38 @@ int do_op(const PROGRAMMER *pgm, const AVRPART *p, UPDATE *upd, enum updateflags
msg_info("\n"); msg_info("\n");
} }
// Patch flash input, eg, for vector bootloaders
if(pgm->flash_readhook) {
AVRMEM *mem = avr_locate_mem(p, upd->memtype);
if(mem && !strcmp(mem->desc, "flash")) {
rc = pgm->flash_readhook(pgm, p, mem, upd->filename, rc);
if (rc < 0) {
pmsg_notice("readhook for file %s failed\n", update_inname(upd->filename));
return LIBAVRDUDE_GENERAL_FAILURE;
}
if(memstats(p, upd->memtype, rc, &fs_patched) < 0)
return LIBAVRDUDE_GENERAL_FAILURE;
if(memcmp(&fs_patched, &fs, sizeof fs)) {
pmsg_info("preparing flash input for device%s\n",
pgm->prog_modes & PM_SPM? " bootloader": "");
imsg_notice2("with %d byte%s in %d section%s within %s\n",
fs_patched.nbytes, update_plural(fs_patched.nbytes),
fs_patched.nsections, update_plural(fs_patched.nsections),
update_interval(fs_patched.firstaddr, fs_patched.lastaddr));
if(mem->page_size > 1) {
imsg_notice2("using %d page%s and %d pad byte%s",
fs_patched.npages, update_plural(fs_patched.npages),
fs_patched.nfill, update_plural(fs_patched.nfill));
if(fs_patched.ntrailing)
msg_notice2(", and %d trailing 0xff byte%s",
fs_patched.ntrailing, update_plural(fs_patched.ntrailing));
msg_notice2("\n");
}
}
}
}
size = rc;
// Write the buffer contents to the selected memory type // Write the buffer contents to the selected memory type
pmsg_info("writing %d byte%s %s%s ...\n", fs.nbytes, pmsg_info("writing %d byte%s %s%s ...\n", fs.nbytes,
update_plural(fs.nbytes), mem->desc, alias_mem_desc); update_plural(fs.nbytes), mem->desc, alias_mem_desc);
@ -585,7 +616,7 @@ int do_op(const PROGRAMMER *pgm, const AVRPART *p, UPDATE *upd, enum updateflags
if (quell_progress < 2) if (quell_progress < 2)
pmsg_notice2("verifying ...\n"); pmsg_notice2("verifying ...\n");
rc = avr_verify(p, v, upd->memtype, size); rc = avr_verify(pgm, p, v, upd->memtype, size);
if (rc < 0) { if (rc < 0) {
pmsg_error("verification mismatch\n"); pmsg_error("verification mismatch\n");
pgm->err_led(pgm, ON); pgm->err_led(pgm, ON);

2503
src/urclock.c Normal file

File diff suppressed because it is too large Load Diff

29
src/urclock.h Normal file
View File

@ -0,0 +1,29 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2022, Stefan Rueger <stefan.rueger@urclocks.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
/* $Id$ */
#ifndef urclock_h__
#define urclock_h__
extern const char urclock_desc[];
void urclock_initpgm (PROGRAMMER *pgm);
#endif

156
src/urclock_private.h Normal file
View File

@ -0,0 +1,156 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2022, Stefan Rueger <stefan.rueger@urclocks.com>
*
* 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, see <http://www.gnu.org/licenses/>.
*/
/* $Id$ */
#ifndef urclock_private_h__
#define urclock_private_h__
#include "avrintel.h"
// EEPROM or flash cache for bytewise access
typedef struct {
int base, size;
char *page, *copy;
} Cache;
// STK500v1 protocol constants
#define Resp_STK_OK 0x10
#define Resp_STK_INSYNC 0x14
#define Sync_CRC_EOP 0x20
#define Cmnd_STK_GET_SYNC 0x30
#define Cmnd_STK_ENTER_PROGMODE 0x50
#define Cmnd_STK_LEAVE_PROGMODE 0x51
#define Cmnd_STK_CHIP_ERASE 0x52
#define Cmnd_STK_LOAD_ADDRESS 0x55
#define Cmnd_STK_UNIVERSAL 0x56
#define Cmnd_STK_PROG_PAGE 0x64
#define Cmnd_STK_READ_PAGE 0x74
#define Cmnd_STK_READ_SIGN 0x75
// Urprotocol command extensions to STK500v1
#define Cmnd_UR_PROG_PAGE_EE 0x00
#define Cmnd_UR_READ_PAGE_EE 0x01
#define Cmnd_UR_PROG_PAGE_FL 0x02
#define Cmnd_UR_READ_PAGE_FL 0x03
// STK_UNIVERSAL commands for backward compatibility
#define Subc_STK_UNIVERSAL_LEXT 0x4d000000u // Load extended address
#define Subc_STK_UNIVERSAL_CE 0xac800000u // Chip erase
// Urboot protocol side channel info about MCU id and 5 binary bootloader features
// Number of differnt MCU ids
#define UB_N_MCU 2040 // MCU id 0..2039
// 5 bootloader features
#define UB_RESERVED_1 1
#define UB_RESERVED_2 2
#define UB_READ_FLASH 4 // Bootloader can read flash
#define UB_FLASH_LL_NOR 8 // Bootloader flash programming looks like a NOR memory
#define UB_CHIP_ERASE 16 // Bootloader has a flash-only chip erase that protects itself
#define UB_INFO(ub_features, ub_mcuid) (ub_features*UB_N_MCU + ub_mcuid)
#define UB_FEATURES(ub_info) ((uint16_t)(ub_info)/UB_N_MCU)
#define UB_MCUID(ub_info) ((uint16_t)(ub_info)%UB_N_MCU)
/*
* Urboot layout of top six bytes
*
* FLASHEND-5: numblpags, only from v7.5: 1 byte number 1..127 of bootloader flash pages
* FLASHEND-4: vblvecnum, only from v7.5: 1 byte vector number 1..127 for vector bootloader
* FLASHEND-3: 2 byte rjmp opcode to bootloader pgm_write_page(sram, flash) or ret opcode
* FLASHEND-1: capability byte of bootloader
* FLASHEND-0: version number of bootloader: 5 msb = major version, 3 lsb = minor version
*/
// Capability byte of bootloader from version 7.2 onwards
#define UR_PGMWRITEPAGE 128 // pgm_write_page() can be called from application at FLASHEND+1-4
#define UR_AUTOBAUD 128 // Bootloader has autobaud detection (from v7.7)
#define UR_EEPROM 64 // EEPROM read/write support
#define UR_URPROTOCOL 32 // Bootloader uses urprotocol that requires avrdude -c urclock
#define UR_DUAL 16 // Dual boot
#define UR_VBLMASK 12 // Vector bootloader bits
#define UR_VBLPATCHVERIFY 12 // Patch reset/interrupt vectors and show original ones on verify
#define UR_VBLPATCH 8 // Patch reset/interrupt vectors only (expect an error on verify)
#define UR_VBL 4 // Merely start application via interrupt vector instead of reset
#define UR_NO_VBL 0 // Not a vector bootloader, must set fuses to HW bootloader support
#define UR_PROTECTME 2 // Bootloader safeguards against overwriting itself
#define UR_RESETFLAGS 1 // Load reset flags into register R2 before starting application
#define UR_HAS_CE 1 // Bootloader has Chip Erase (from v7.7)
#define verbyte_cv(capver) ((uint8_t) ((uint16_t) (capver) >> 8))
#define hascapbyte_cv(capver) ({ uint8_t _vh = verbyte_cv(capver); _vh >= 072 && _vh != 0xff; })
#define hasextendedv_cv(capver) ({ uint8_t _vh = verbyte_cv(capver); _vh >= 075 && _vh != 0xff; })
#define capabilities_cv(capver) ({ uint16_t _vc = capver; \
(uint8_t) (hascapbyte_cv(_vc)? _vc&0xff: 0); })
#define vblvecnum_cv(capver) ({ uint16_t _vc = capver; \
(uint8_t) (hasextendedv_cv(_vc)? pgm_read_b1(FLASHEND-4): 0); })
#define numblpages_cv(capver) ({ uint16_t _vc = capver; \
(uint8_t) (hasextendedv_cv(_vc)? pgm_read_b1(FLASHEND-5): 0); })
#define blurversion_cv(capver) ({ uint8_t _vh = verbyte_cv(capver); \
(uint8_t) (_vh >= 072 && _vh != 0xff? _vh: 0); })
#define vercapis(capver, mask) ({ uint16_t _vi = capver; !!(capabilities_cv(_vi) & (mask)); })
#define ispgmwritepage_cv(capver) vercapis(capver, UR_PGMWRITEPAGE) // up to v7.6
#define isautobaud_cv(capver) vercapis(capver, UR_AUTOBAUD) // from v7.7
#define iseeprom_cv(capver) vercapis(capver, UR_EEPROM)
#define isurprotocol_cv(capver) vercapis(capver, UR_URPROTOCOL)
#define isdual_cv(capver) vercapis(capver, UR_DUAL)
#define isvectorbl_cv(capver) vercapis(capver, UR_VBLMASK)
#define isprotectme_cv(capver) vercapis(capver, UR_PROTECTME)
#define isresetflags_cv(capver) vercapis(capver, UR_RESETFLAGS) // up to v7.6
#define ishas_ce_cv(capver) vercapis(capver, UR_HAS_CE) // from v7.7
// Capability bits incl position
#define pgmwritepage_bit_cap(cap) ((cap) & UR_PGMWRITEPAGE) // up to v7.6
#define autibaud_bit_cap(cap) ((cap) & UR_AUTOBAUD) // from v7.7
#define eeprom_bit_cap(cap) ((cap) & UR_EEPROM)
#define dual_bit_cap(cap) ((cap) & UR_DUAL)
#define vector_bits_cap(cap) ((cap) & UR_VBLMASK))
#define protectme_bit_cap(cap) ((cap) & UR_PROTECTME)
#define urprotocol_bit_cap(cap) ((cap) & UR_URPROTOCOL)
#define resetflags_bit_cap(cap) ((cap) & UR_RESETFLAGS) // up to v7.6
#define has_ce_bit_cap(cap) ((cap) & UR_HAS_CE) // from v7.7
// Boolean capabilities
#define ispgmwritepage_cap(cap) (!!((cap) & UR_PGMWRITEPAGE)) // up to v7.6
#define isautobaud_cap(cap) (!!((cap) & UR_AUTOBAUD)) // from v7.7
#define iseeprom_cap(cap) (!!((cap) & UR_EEPROM))
#define isdual_cap(cap) (!!((cap) & UR_DUAL))
#define isvectorbl_cap(cap) (!!((cap) & UR_VBLMASK)))
#define isprotectme_cap(cap) (!!((cap) & UR_PROTECTME))
#define isurprotocol_cap(cap) (!!((cap) & UR_URPROTOCOL))
#define isresetflags_cap(cap) (!!((cap) & UR_RESETFLAGS)) // up to v7.6
#define ishas_ce_cap(cap) (!!((cap) & UR_HAS_CE)) // from v7.7
// Capability levels 0, 1, 2 or 3
#define vectorbl_level_cap(cap) (((cap) & UR_VBLMASK)/UR_VBL)
#endif