Merge pull request #1171 from stefanrueger/urclock
Provide Urclock programmer
This commit is contained in:
commit
02e02be6f9
|
@ -225,6 +225,9 @@ add_library(libavrdude
|
|||
updi_readwrite.h
|
||||
updi_state.c
|
||||
updi_state.h
|
||||
urclock.c
|
||||
urclock.h
|
||||
urclock_private.h
|
||||
usbasp.c
|
||||
usbasp.h
|
||||
usbdevs.h
|
||||
|
|
|
@ -180,6 +180,9 @@ libavrdude_a_SOURCES = \
|
|||
updi_readwrite.h \
|
||||
updi_nvm.c \
|
||||
updi_nvm.h \
|
||||
urclock.c \
|
||||
urclock.h \
|
||||
urclock_private.h \
|
||||
usbdevs.h \
|
||||
usb_hidapi.c \
|
||||
usb_libusb.c \
|
||||
|
|
33
src/avr.c
33
src/avr.c
|
@ -434,7 +434,7 @@ int avr_read_mem(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, con
|
|||
}
|
||||
if (!failure)
|
||||
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) {
|
||||
|
@ -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.
|
||||
*/
|
||||
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;
|
||||
unsigned char * buf1, * buf2;
|
||||
int vsize;
|
||||
|
@ -1118,13 +1117,33 @@ int avr_verify(const AVRPART * p, const AVRPART * v, const char * memtype, int s
|
|||
size = vsize;
|
||||
}
|
||||
|
||||
int verror = 0, vroerror = 0, maxerrs = verbose >= MSG_DEBUG? size+1: 10;
|
||||
for (i=0; i<size; i++) {
|
||||
if ((b->tags[i] & TAG_ALLOCATED) != 0 && buf1[i] != buf2[i]) {
|
||||
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
|
||||
pmsg_error("verification mismatch, first encountered at addr 0x%04x\n", i);
|
||||
imsg_error("device 0x%02x != input 0x%02x\n", buf1[i], buf2[i]);
|
||||
if(verror < maxerrs) {
|
||||
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;
|
||||
} else {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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) {
|
||||
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) {
|
||||
// Write modified page cont to device; if unsuccessful try bytewise access
|
||||
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
|
||||
* - Otherwise fall back to pgm->write_byte()
|
||||
* - 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
|
||||
*/
|
||||
int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
|
||||
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
|
||||
if(!avr_has_paged_access(pgm, mem))
|
||||
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, "eeprom"), pgm->cp_eeprom, 0 },
|
||||
};
|
||||
int rc;
|
||||
|
||||
if(pgm->chip_erase(pgm, p) < 0)
|
||||
return LIBAVRDUDE_GENERAL_FAILURE;
|
||||
if((rc = pgm->chip_erase(pgm, p)) < 0)
|
||||
return rc;
|
||||
|
||||
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
||||
AVRMEM *mem = mems[i].mem;
|
||||
|
|
120
src/avrdude.1
120
src/avrdude.1
|
@ -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
|
||||
program memory, but no true chip erase can be performed.
|
||||
.Pp
|
||||
The Arduino (which is very similar to the STK500 1.x) is supported via
|
||||
its own programmer type specification ``arduino''. This programmer works for
|
||||
the Arduino Uno Rev3 or any AVR that runs the Optiboot bootloader.
|
||||
Serial bootloaders that run a skeleton of the STK500 1.x protocol are
|
||||
supported via their own programmer type ``arduino''. This programmer works
|
||||
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
|
||||
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
|
||||
|
@ -397,8 +405,8 @@ programming requires the memory be erased to 0xFF beforehand.
|
|||
.Fl A
|
||||
should be used when the programmer hardware, or bootloader
|
||||
software for that matter, does not carry out chip erase and
|
||||
instead handles the memory erase on a page level. The popular
|
||||
Arduino bootloader exhibits this behaviour; for this reason
|
||||
instead handles the memory erase on a page level. Popular
|
||||
Arduino bootloaders exhibit this behaviour; for this reason
|
||||
.Fl A
|
||||
is engaged by default when specifying
|
||||
. Fl c
|
||||
|
@ -432,7 +440,7 @@ contents would exclusively cause bits to be programmed from the value
|
|||
.Ql 1
|
||||
to
|
||||
.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
|
||||
case before programming the cell.
|
||||
.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.
|
||||
Defaults to 10 if not specified.
|
||||
.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
|
||||
.Bl -tag -offset indent -width indent
|
||||
.It Ar reset={cs,aux,aux2}
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
# 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
|
||||
# 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
|
||||
# devicecode = <num> ; # deprecated, use stk500_devcode
|
||||
# stk500_devcode = <num> ; # numeric
|
||||
|
@ -756,6 +756,19 @@ programmer
|
|||
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
|
||||
#------------------------------------------------------------
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
* Atmel AVR8L, AVR8, XMEGA and AVR8X family description of interrupts and more
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
{"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
|
||||
{"LGT8F88P", 227, F_AVR8, {0x1E, 0x93, 0x0F}, 0, 0x02000, 0x040, -1, -1, 0, 0x0200, 4, -1, -1, -1, -1, 0, NULL}, // avrdude
|
||||
{"LGT8F168P", 228, F_AVR8, {0x1E, 0x94, 0x0B}, 0, 0x04000, 0x080, -1, -1, 0, 0x0200, 4, -1, -1, -1, -1, 0, NULL}, // avrdude
|
||||
{"LGT8F328P", 229, F_AVR8, {0x1E, 0x95, 0x0F}, 0, 0x08000, 0x080, -1, -1, 0, 0x0400, 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, 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, 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
|
||||
{"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
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
* meta-author Stefan Rueger <stefan.rueger@urclocks.com>
|
||||
*
|
||||
* 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_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_attiny5 11
|
||||
#define vbu_attiny9 10
|
||||
|
|
|
@ -704,7 +704,7 @@ char *cfg_escape(const char *s) {
|
|||
char buf[50*1024], *d = buf;
|
||||
|
||||
*d++ = '"';
|
||||
for(; *s && d-buf < sizeof buf-7; s++) {
|
||||
for(; *s && d-buf < (long) sizeof buf-7; s++) {
|
||||
switch(*s) {
|
||||
case '\n':
|
||||
*d++ = '\\'; *d++ = 'n';
|
||||
|
@ -855,7 +855,7 @@ void cfg_update_mcuid(AVRPART *part) {
|
|||
return;
|
||||
|
||||
// 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(part->mcuid != (int) uP_table[i].mcuid) {
|
||||
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
|
||||
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) {
|
||||
// Complain unless it can be considered a variant, eg, ATmega32L and ATmega32
|
||||
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) ||
|
||||
flash->size != uP_table[i].flashsize ||
|
||||
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",
|
||||
part->mcuid, uP_table[i].name, sizeof uP_table/sizeof *uP_table);
|
||||
}
|
||||
|
|
|
@ -1152,9 +1152,9 @@ static void dev_pgm_raw(const PROGRAMMER *pgm) {
|
|||
dev_raw_dump(dp.usbproduct, strlen(dp.usbproduct)+1, id, "usbprod", 0);
|
||||
|
||||
// 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);
|
||||
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);
|
||||
|
||||
// Zap address values
|
||||
|
|
|
@ -1860,7 +1860,7 @@ part
|
|||
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
|
||||
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
|
||||
devicecode = <num> ; # deprecated, use stk500_devcode
|
||||
stk500_devcode = <num> ; # numeric
|
||||
|
|
|
@ -580,6 +580,7 @@ const char * pinmask_to_str(const pinmask_t * const pinmask);
|
|||
The target file will be selected at configure time. */
|
||||
|
||||
extern long serial_recv_timeout; /* ms */
|
||||
extern long serial_drain_timeout; /* ms */
|
||||
|
||||
union filedescriptor
|
||||
{
|
||||
|
@ -798,6 +799,7 @@ typedef struct programmer_t {
|
|||
int (*parseextparams) (const struct programmer_t *pgm, const LISTID xparams);
|
||||
void (*setup) (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
|
||||
int (*write_byte_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m,
|
||||
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 (*page_erase_cached)(const struct programmer_t *pgm, const AVRPART *p, const AVRMEM *m,
|
||||
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 (*reset_cache) (const struct programmer_t *pgm, const AVRPART *p);
|
||||
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_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);
|
||||
|
||||
|
|
|
@ -1393,9 +1393,12 @@ int main(int argc, char * argv [])
|
|||
if (uflags & UF_NOWRITE) {
|
||||
pmsg_warning("conflicting -e and -n options specified, NOT erasing chip\n");
|
||||
} else {
|
||||
msg_info("erasing chip\n");
|
||||
pmsg_info("erasing chip\n");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,8 @@ PROGRAMMER *pgm_new(void) {
|
|||
pgm->parseextparams = NULL;
|
||||
pgm->setup = NULL;
|
||||
pgm->teardown = NULL;
|
||||
pgm->readonly = NULL;
|
||||
pgm->flash_readhook = NULL;
|
||||
|
||||
// For allocating "global" memory by the programmer
|
||||
pgm->cookie = NULL;
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "stk500generic.h"
|
||||
#include "stk500v2.h"
|
||||
#include "teensy.h"
|
||||
#include "urclock.h"
|
||||
#include "usbasp.h"
|
||||
#include "usbtiny.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"
|
||||
{"stk600pp", stk600pp_initpgm, stk600pp_desc}, // "STK600PP"
|
||||
{"teensy", teensy_initpgm, teensy_desc}, // "teensy"
|
||||
{"urclock", urclock_initpgm, urclock_desc}, // "Urclock"
|
||||
{"usbasp", usbasp_initpgm, usbasp_desc}, // "usbasp"
|
||||
{"usbtiny", usbtiny_initpgm, usbtiny_desc}, // "USBtiny" or "usbtiny"
|
||||
{"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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "libavrdude.h"
|
||||
|
||||
long serial_recv_timeout = 5000; /* ms */
|
||||
long serial_drain_timeout = 250; /* ms */
|
||||
|
||||
struct baud_mapping {
|
||||
long baud;
|
||||
|
@ -550,7 +551,7 @@ static int ser_drain(const union filedescriptor *fd, int display) {
|
|||
unsigned char buf;
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 250000;
|
||||
timeout.tv_usec = serial_drain_timeout*1000L;
|
||||
|
||||
if (display) {
|
||||
msg_info("drain>");
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "libavrdude.h"
|
||||
|
||||
long serial_recv_timeout = 5000; /* ms */
|
||||
long serial_drain_timeout = 250; /* ms */
|
||||
|
||||
#define W32SERBUFSIZE 1024
|
||||
|
||||
|
@ -635,7 +636,7 @@ static int net_drain(const union filedescriptor *fd, int display) {
|
|||
}
|
||||
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 250000;
|
||||
timeout.tv_usec = serial_drain_timeout*1000L;
|
||||
|
||||
while (1) {
|
||||
FD_ZERO(&rfds);
|
||||
|
@ -712,7 +713,7 @@ static int ser_drain(const union filedescriptor *fd, int display) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
serial_w32SetTimeOut(hComPort,250);
|
||||
serial_w32SetTimeOut(hComPort, serial_drain_timeout);
|
||||
|
||||
if (display) {
|
||||
msg_info("drain>");
|
||||
|
|
53
src/term.c
53
src/term.c
|
@ -187,7 +187,7 @@ static int chardump_line(char *buffer, unsigned char *p, int n, int pad) {
|
|||
unsigned char b[128];
|
||||
|
||||
// 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);
|
||||
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");
|
||||
for (i = 0; i < len + data.bytes_grown; 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);
|
||||
if (rc == -1)
|
||||
imsg_error("%*swrite operation not supported on memory type %s\n", 8, "", mem->desc);
|
||||
werror = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
uint8_t b;
|
||||
rc = pgm->read_byte_cached(pgm, p, mem, addr+i, &b);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (werror)
|
||||
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[]) {
|
||||
term_out("erasing chip ...\n");
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
|
39
src/update.c
39
src/update.c
|
@ -431,7 +431,7 @@ int do_op(const PROGRAMMER *pgm, const AVRPART *p, UPDATE *upd, enum updateflags
|
|||
AVRMEM *mem;
|
||||
int size;
|
||||
int rc;
|
||||
Filestats fs;
|
||||
Filestats fs, fs_patched;
|
||||
|
||||
mem = avr_locate_mem(p, upd->memtype);
|
||||
if (mem == NULL) {
|
||||
|
@ -486,11 +486,10 @@ int do_op(const PROGRAMMER *pgm, const AVRPART *p, UPDATE *upd, enum updateflags
|
|||
return LIBAVRDUDE_GENERAL_FAILURE;
|
||||
}
|
||||
|
||||
size = rc;
|
||||
pmsg_info("reading input file %s for %s%s\n",
|
||||
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;
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// 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
|
||||
pmsg_info("writing %d byte%s %s%s ...\n", fs.nbytes,
|
||||
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)
|
||||
pmsg_notice2("verifying ...\n");
|
||||
|
||||
rc = avr_verify(p, v, upd->memtype, size);
|
||||
rc = avr_verify(pgm, p, v, upd->memtype, size);
|
||||
if (rc < 0) {
|
||||
pmsg_error("verification mismatch\n");
|
||||
pgm->err_led(pgm, ON);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
||||
|
|
@ -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
|
Loading…
Reference in New Issue