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_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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
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)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
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
|
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}
|
||||||
|
|
|
@ -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
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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>");
|
||||||
|
|
|
@ -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>");
|
||||||
|
|
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];
|
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;
|
||||||
}
|
}
|
||||||
|
|
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;
|
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);
|
||||||
|
|
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