Update urclock programmer
This commit is contained in:
parent
e6c26d8db4
commit
21d93ec8cb
115
src/avrdude.1
115
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,101 @@ 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 and exit.
|
||||||
|
.It Ar showid
|
||||||
|
Show a unique Urclock ID stored in either flash or EEPROM of the MCU and 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 showapp
|
||||||
|
Show the size of the programmed application and exit.
|
||||||
|
.It Ar showstore
|
||||||
|
Show the size of the unused flash between the application and metadata and exit.
|
||||||
|
.It Ar showmeta
|
||||||
|
Show the size of the metadata just below the bootloader and exit.
|
||||||
|
.It Ar showboot
|
||||||
|
Show the size of the bootloader and exit.
|
||||||
|
.It Ar showversion
|
||||||
|
Show bootloader version and capabilities, and exit.
|
||||||
|
.It Ar showvbl
|
||||||
|
Show the vector number and name of the interrupt table vector used by the
|
||||||
|
bootloader for starting the application, and 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 showdate
|
||||||
|
Show the last-modified date of the input file for the flash application
|
||||||
|
and 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, and 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 forcetrim
|
||||||
|
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 -xforcetrim 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 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 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}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
377
src/urclock.c
377
src/urclock.c
|
@ -223,8 +223,9 @@
|
||||||
#define min(a, b) ((a) < (b)? (a): (b))
|
#define min(a, b) ((a) < (b)? (a): (b))
|
||||||
|
|
||||||
static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p);
|
static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p);
|
||||||
static int ur_readEF(const PROGRAMMER *pgm, uint8_t *buf, uint32_t addr, int len, char memtype);
|
static int ur_readEF(const PROGRAMMER *pgm, const AVRPART *p, uint8_t *buf, uint32_t addr, int len,
|
||||||
static int readUrclockID(const PROGRAMMER *pgm);
|
char memchr);
|
||||||
|
static int readUrclockID(const PROGRAMMER *pgm, const AVRPART *p, uint64_t *idp);
|
||||||
static int urclock_send(const PROGRAMMER *pgm, unsigned char *buf, size_t len);
|
static int urclock_send(const PROGRAMMER *pgm, unsigned char *buf, size_t len);
|
||||||
static int urclock_recv(const PROGRAMMER *pgm, unsigned char *buf, size_t len);
|
static int urclock_recv(const PROGRAMMER *pgm, unsigned char *buf, size_t len);
|
||||||
static int urclock_cmd(const PROGRAMMER *pgm, const unsigned char *cmd, unsigned char *res);
|
static int urclock_cmd(const PROGRAMMER *pgm, const unsigned char *cmd, unsigned char *res);
|
||||||
|
@ -260,7 +261,8 @@ typedef struct {
|
||||||
bloptiversion; // Optiboot version as (major<<8) + minor
|
bloptiversion; // Optiboot version as (major<<8) + minor
|
||||||
int32_t blstart; // Bootloader start address, eg, for bootloader write protection
|
int32_t blstart; // Bootloader start address, eg, for bootloader write protection
|
||||||
|
|
||||||
uint64_t urclockID; // Urclock ID read from flash or EEPROM as little endian
|
int idmchr; // Either 'E' or 'F' for the memory where the Urclock ID is located
|
||||||
|
int idaddr; // The address of the Urclock ID
|
||||||
int idlen; // Number 1..8 of Urclock ID bytes (location, see iddesc below)
|
int idlen; // Number 1..8 of Urclock ID bytes (location, see iddesc below)
|
||||||
|
|
||||||
int32_t storestart; // Store (ie, unused flash) start address, same as application size
|
int32_t storestart; // Store (ie, unused flash) start address, same as application size
|
||||||
|
@ -280,9 +282,9 @@ typedef struct {
|
||||||
// Extended parameters for Urclock
|
// Extended parameters for Urclock
|
||||||
int showall, // Show all pieces of info for connected part and exit
|
int showall, // Show all pieces of info for connected part and exit
|
||||||
showid, // ... Urclock ID
|
showid, // ... Urclock ID
|
||||||
showsketch, // ... application size
|
showapp, // ... application size
|
||||||
showstore, // ... store size
|
showstore, // ... store size
|
||||||
showmetadata, // ... metadata size
|
showmeta, // ... metadata size
|
||||||
showboot, // ... bootloader size
|
showboot, // ... bootloader size
|
||||||
showversion, // ... bootloader version and capabilities
|
showversion, // ... bootloader version and capabilities
|
||||||
showvbl, // ... vector bootloader level, vector number and name
|
showvbl, // ... vector bootloader level, vector number and name
|
||||||
|
@ -539,11 +541,39 @@ static void set_date_filename(const PROGRAMMER *pgm, const char *fname) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Put destination address of reset vector jmp or rjmp into addr, return -1 if not an r/jmp
|
||||||
|
static int reset2addr(const unsigned char *opcode, int vecsz, int flashsize, int *addrp) {
|
||||||
|
int op32, addr, rc = 0;
|
||||||
|
uint16_t op16;
|
||||||
|
|
||||||
|
op16 = buf2uint16(opcode); // First word of the jmp or the full rjmp
|
||||||
|
op32 = vecsz == 2? op16: buf2uint32(opcode);
|
||||||
|
|
||||||
|
if(vecsz == 4 && isJmp(op16)) {
|
||||||
|
addr = addr_jmp(op32); // Accept compiler's destination (do not normalise)
|
||||||
|
} else if(isRjmp(op16)) { // rjmp might be generated for larger parts, too
|
||||||
|
addr = dist_rjmp(op16, flashsize);
|
||||||
|
while(addr < 0) // If rjmp was backwards
|
||||||
|
addr += flashsize; // OK for small parts, likely(!) OK if flashsize is a power of 2
|
||||||
|
while(addr > flashsize) // Sanity (should not happen): rjmp jumps over FLASHEND
|
||||||
|
addr -= flashsize;
|
||||||
|
} else
|
||||||
|
rc = -1;
|
||||||
|
|
||||||
|
if(addrp && rc == 0)
|
||||||
|
*addrp = addr;
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Called after the input file has been read for writing or verifying flash
|
// Called after the input file has been read for writing or verifying flash
|
||||||
static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p_unused,
|
static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm,
|
||||||
const AVRMEM *flm, const char *fname, int size) {
|
const char *fname, int size) {
|
||||||
|
|
||||||
int nmdata, maxsize, firstbeg, firstlen;
|
int nmdata, maxsize, firstbeg, firstlen;
|
||||||
|
int vecsz = ur.uP.flashsize <= 8192? 2: 4; // Small parts use rjmp, large parts need 4-byte jmp
|
||||||
|
|
||||||
set_date_filename(pgm, fname);
|
set_date_filename(pgm, fname);
|
||||||
|
|
||||||
|
@ -597,7 +627,6 @@ static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p_unused
|
||||||
Return("input [0x%04x, 0x%04x] overlaps metadata [0x%04x, 0x%04x], consider -xnofilename",
|
Return("input [0x%04x, 0x%04x] overlaps metadata [0x%04x, 0x%04x], consider -xnofilename",
|
||||||
firstbeg, size-1, ur.blstart-nmdata, ur.blstart-1);
|
firstbeg, size-1, ur.blstart-nmdata, ur.blstart-1);
|
||||||
|
|
||||||
int vecsz = ur.uP.flashsize <= 8192? 2: 4; // Small parts use rjmp, large parts need 4-byte jmp
|
|
||||||
bool llcode = firstbeg == 0 && firstlen > ur.uP.ninterrupts*vecsz; // Looks like code
|
bool llcode = firstbeg == 0 && firstlen > ur.uP.ninterrupts*vecsz; // Looks like code
|
||||||
bool llvectors = firstbeg == 0 && firstlen >= ur.uP.ninterrupts*vecsz; // Looks like vector table
|
bool llvectors = firstbeg == 0 && firstlen >= ur.uP.ninterrupts*vecsz; // Looks like vector table
|
||||||
for(int i=0; llvectors && i<ur.uP.ninterrupts*vecsz; i+=vecsz) {
|
for(int i=0; llvectors && i<ur.uP.ninterrupts*vecsz; i+=vecsz) {
|
||||||
|
@ -613,8 +642,8 @@ static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p_unused
|
||||||
if(llcode && llvectors && ur.vblvectornum > 0 && ur.vbllevel) {
|
if(llcode && llvectors && ur.vblvectornum > 0 && ur.vbllevel) {
|
||||||
// From v7.5 patch all levels but for earlier and unknown versions only patch level 1
|
// From v7.5 patch all levels but for earlier and unknown versions only patch level 1
|
||||||
if(ur.blurversion >= 075 || ((ur.blurversion==0 || ur.blurversion >= 072) && ur.vbllevel==1)) {
|
if(ur.blurversion >= 075 || ((ur.blurversion==0 || ur.blurversion >= 072) && ur.vbllevel==1)) {
|
||||||
int appvecloc, reset32;
|
|
||||||
uint16_t reset16;
|
uint16_t reset16;
|
||||||
|
int reset32, appstart, appvecloc;
|
||||||
|
|
||||||
appvecloc = ur.vblvectornum*vecsz; // Location of jump-to-application in vector table
|
appvecloc = ur.vblvectornum*vecsz; // Location of jump-to-application in vector table
|
||||||
reset16 = buf2uint16(flm->buf); // First reset word of to-be-uploaded application
|
reset16 = buf2uint16(flm->buf); // First reset word of to-be-uploaded application
|
||||||
|
@ -631,17 +660,7 @@ static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p_unused
|
||||||
* an error thrown if so.
|
* an error thrown if so.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int appstart;
|
if(reset2addr(flm->buf, vecsz, flm->size, &appstart) < 0) {
|
||||||
|
|
||||||
if(vecsz == 4 && isJmp(reset16)) {
|
|
||||||
appstart = addr_jmp(reset32); // Accept compiler's destination for now (do not normalise)
|
|
||||||
} else if(isRjmp(reset16)) { // rjmp might be generated for larger parts, too
|
|
||||||
appstart = dist_rjmp(reset16, ur.uP.flashsize);
|
|
||||||
while(appstart < 0) // If rjmp was backwards
|
|
||||||
appstart += flm->size; // OK for small parts, likely OK if size is a power of 2
|
|
||||||
while(appstart > flm->size) // Sanity (should not happen): rjmp jumps over FLASHEND
|
|
||||||
appstart -= flm->size;
|
|
||||||
} else {
|
|
||||||
pmsg_warning("not patching input as opcode word %04x at reset is not a%sjmp\n",
|
pmsg_warning("not patching input as opcode word %04x at reset is not a%sjmp\n",
|
||||||
reset16, vecsz==2? "n r": " ");
|
reset16, vecsz==2? "n r": " ");
|
||||||
goto nopatch;
|
goto nopatch;
|
||||||
|
@ -771,7 +790,7 @@ nopatch_nometa:
|
||||||
if(isize < 1 || isize > (int) sizeof spc) // Should not happen
|
if(isize < 1 || isize > (int) sizeof spc) // Should not happen
|
||||||
Return("isize=%d out of range (enlarge spc[] and recompile)", isize);
|
Return("isize=%d out of range (enlarge spc[] and recompile)", isize);
|
||||||
|
|
||||||
if(ur_readEF(pgm, spc, istart, isize, 'F') == 0) { // @@@
|
if(ur_readEF(pgm, p, spc, istart, isize, 'F') == 0) {
|
||||||
for(ai = istart; ai < istart + isize; ai++)
|
for(ai = istart; ai < istart + isize; ai++)
|
||||||
if(!(flm->tags[ai] & TAG_ALLOCATED))
|
if(!(flm->tags[ai] & TAG_ALLOCATED))
|
||||||
flm->buf[ai] = spc[ai-istart];
|
flm->buf[ai] = spc[ai-istart];
|
||||||
|
@ -785,6 +804,24 @@ nopatch_nometa:
|
||||||
}
|
}
|
||||||
ur.done_ce = 0; // From now on can no longer rely on being deleted
|
ur.done_ce = 0; // From now on can no longer rely on being deleted
|
||||||
|
|
||||||
|
// Last, but not least: ensure that vector bootloaders have correct r/jmp at address 0
|
||||||
|
if(ur.blstart && ur.vbllevel==1) {
|
||||||
|
int set=0;
|
||||||
|
for(int i=0; i < vecsz; i++)
|
||||||
|
if(flm->tags[i] & TAG_ALLOCATED)
|
||||||
|
set++;
|
||||||
|
|
||||||
|
if(set && set != vecsz)
|
||||||
|
Return("input overwrites the reset vector partially rendering vector bootloader moot, exiting");
|
||||||
|
|
||||||
|
if(set) {
|
||||||
|
int resetdest;
|
||||||
|
if(reset2addr(flm->buf, vecsz, flm->size, &resetdest) < 0)
|
||||||
|
Return("input does not hold an r/jmp at reset vector rendering vector bootloader moot, exiting");
|
||||||
|
if(resetdest != ur.blstart)
|
||||||
|
Return("input file points reset to 0x%04x instead of vector bootloader at 0x%04x, exiting", resetdest, ur.blstart);
|
||||||
|
}
|
||||||
|
}
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,7 +1098,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||||
ur.blstart = ur.uP.flashsize - ur.xbootsize;
|
ur.blstart = ur.uP.flashsize - ur.xbootsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ur.uP.ninterrupts >= 0)
|
if((int8_t) ur.uP.ninterrupts >= 0) // valid range is 0..127
|
||||||
if(ur.xvectornum < -1 || ur.xvectornum > ur.uP.ninterrupts)
|
if(ur.xvectornum < -1 || ur.xvectornum > ur.uP.ninterrupts)
|
||||||
Return("unknown interrupt vector #%d for vector bootloader -- should be in [-1, %d]",
|
Return("unknown interrupt vector #%d for vector bootloader -- should be in [-1, %d]",
|
||||||
ur.xvectornum, ur.uP.ninterrupts);
|
ur.xvectornum, ur.uP.ninterrupts);
|
||||||
|
@ -1079,7 +1116,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||||
// Sporting chance that we can read top flash to get intell about bootloader
|
// Sporting chance that we can read top flash to get intell about bootloader
|
||||||
if(!ur.urprotocol || (ur.urfeatures & UB_READ_FLASH)) {
|
if(!ur.urprotocol || (ur.urfeatures & UB_READ_FLASH)) {
|
||||||
// Read top 6 bytes from flash memory to obtain extended information about bootloader and type
|
// Read top 6 bytes from flash memory to obtain extended information about bootloader and type
|
||||||
if((rc = ur_readEF(pgm, spc, flm->size-6, 6, 'F')))
|
if((rc = ur_readEF(pgm, p, spc, flm->size-6, 6, 'F')))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
// In a urboot bootloader (v7.2 onwards) these six are as follows
|
// In a urboot bootloader (v7.2 onwards) these six are as follows
|
||||||
|
@ -1134,7 +1171,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||||
int vecsz = ur.uP.flashsize <= 8192? 2: 4;
|
int vecsz = ur.uP.flashsize <= 8192? 2: 4;
|
||||||
|
|
||||||
// Reset vector points to the bootloader and the bootloader has r/jmp to application?
|
// Reset vector points to the bootloader and the bootloader has r/jmp to application?
|
||||||
if((rc = ur_readEF(pgm, spc, 0, 4, 'F')))
|
if((rc = ur_readEF(pgm, p, spc, 0, 4, 'F')))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
uint16_t reset16 = buf2uint16(spc);
|
uint16_t reset16 = buf2uint16(spc);
|
||||||
|
@ -1161,7 +1198,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||||
|
|
||||||
if(ur.blstart) { // Read bootloader to identify jump to vbl vector
|
if(ur.blstart) { // Read bootloader to identify jump to vbl vector
|
||||||
int i, npages, j, n, toend, dist, wasop32, wasjmp, op16;
|
int i, npages, j, n, toend, dist, wasop32, wasjmp, op16;
|
||||||
uint8_t *p;
|
uint8_t *q;
|
||||||
uint16_t opcode;
|
uint16_t opcode;
|
||||||
|
|
||||||
op16 = wasjmp = wasop32 = 0;
|
op16 = wasjmp = wasop32 = 0;
|
||||||
|
@ -1169,10 +1206,10 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
|
||||||
npages = toend/flm->page_size;
|
npages = toend/flm->page_size;
|
||||||
for(i=0; i<npages; i++) {
|
for(i=0; i<npages; i++) {
|
||||||
// Read bootloader page by page
|
// Read bootloader page by page
|
||||||
if((rc = ur_readEF(pgm, spc, ur.blstart+i*flm->page_size, flm->page_size, 'F')))
|
if((rc = ur_readEF(pgm, p, spc, ur.blstart+i*flm->page_size, flm->page_size, 'F')))
|
||||||
return rc;
|
return rc;
|
||||||
for(n=flm->page_size/2, p=spc, j=0; j<n; j++, p+=2, toend-=2) { // Check 16-bit opcodes
|
for(n=flm->page_size/2, q=spc, j=0; j<n; j++, q+=2, toend-=2) { // Check 16-bit opcodes
|
||||||
opcode = buf2uint16(p);
|
opcode = buf2uint16(q);
|
||||||
if(wasjmp) { // Opcode is the word address of the destination
|
if(wasjmp) { // Opcode is the word address of the destination
|
||||||
wasjmp=0;
|
wasjmp=0;
|
||||||
int dest = addr_jmp((opcode<<16) | op16);
|
int dest = addr_jmp((opcode<<16) | op16);
|
||||||
|
@ -1217,17 +1254,15 @@ vblvecfound:
|
||||||
!ur.blstart || ur.uP.bootsize <= 0 || ur.uP.bootsize == flm->size-ur.blstart? 'o':
|
!ur.blstart || ur.uP.bootsize <= 0 || ur.uP.bootsize == flm->size-ur.blstart? 'o':
|
||||||
ur.uP.bootsize > flm->size-ur.blstart? 'O': '-');
|
ur.uP.bootsize > flm->size-ur.blstart? 'O': '-');
|
||||||
|
|
||||||
if((rc = readUrclockID(pgm)) == -1)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
ur.mcode = 0xff;
|
ur.mcode = 0xff;
|
||||||
if(ur.blstart) {
|
if(ur.blstart) {
|
||||||
int nm = nmeta(1, ur.uP.flashsize); // 6 for date + size of store struct + 1 for mcode byte
|
int nm = nmeta(1, ur.uP.flashsize); // 6 for date + size of store struct + 1 for mcode byte
|
||||||
// If want to show properties, examine the bytes below bootloader for metadata
|
// Showing properties mostly requires examining the bytes below bootloader for metadata
|
||||||
if(ur.showall || ur.showid || ur.showsketch || ur.showstore || ur.showmetadata ||
|
if(ur.showall || (ur.showid && *ur.iddesc && *ur.iddesc != 'E') || ur.showapp ||
|
||||||
ur.showboot || ur.showversion || ur.showvbl || ur.showdate || ur.showfilename) {
|
ur.showstore || ur.showmeta || ur.showboot || ur.showversion || ur.showvbl ||
|
||||||
|
ur.showdate || ur.showfilename) {
|
||||||
|
|
||||||
if((rc = ur_readEF(pgm, spc, ur.blstart-nm, nm, 'F')))
|
if((rc = ur_readEF(pgm, p, spc, ur.blstart-nm, nm, 'F')))
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if(spc[nm-1] != 0xff) {
|
if(spc[nm-1] != 0xff) {
|
||||||
|
@ -1260,7 +1295,7 @@ vblvecfound:
|
||||||
ur.hr = hr;
|
ur.hr = hr;
|
||||||
ur.mn = mn;
|
ur.mn = mn;
|
||||||
if(mcode > 1) { // Copy application name over
|
if(mcode > 1) { // Copy application name over
|
||||||
rc = ur_readEF(pgm, spc, ur.blstart-nmeta(mcode, ur.uP.flashsize), mcode, 'F');
|
rc = ur_readEF(pgm, p, spc, ur.blstart-nmeta(mcode, ur.uP.flashsize), mcode, 'F');
|
||||||
if(rc < 0)
|
if(rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
int len = mcode<sizeof ur.filename? mcode: sizeof ur.filename;
|
int len = mcode<sizeof ur.filename? mcode: sizeof ur.filename;
|
||||||
|
@ -1277,27 +1312,33 @@ vblvecfound:
|
||||||
|
|
||||||
// Print and exit when option show... was given
|
// Print and exit when option show... was given
|
||||||
int first=1;
|
int first=1;
|
||||||
int single = !ur.showall && (!!ur.showid + !!ur.showsketch + !!ur.showstore + !!ur.showmetadata +
|
int single = !ur.showall && (!!ur.showid + !!ur.showapp + !!ur.showstore + !!ur.showmeta +
|
||||||
!!ur.showboot + !!ur.showversion + !!ur.showvbl + !!ur.showdate + !!ur.showfilename) == 1;
|
!!ur.showboot + !!ur.showversion + !!ur.showvbl + !!ur.showdate + !!ur.showfilename) == 1;
|
||||||
|
|
||||||
if(ur.showid || ur.showall)
|
if(ur.showid || ur.showall) {
|
||||||
term_out("%0*lx", 2*ur.idlen, ur.urclockID), first=0;
|
uint64_t urclockID;
|
||||||
|
if((rc = readUrclockID(pgm, p, &urclockID)) == -1)
|
||||||
|
return rc;
|
||||||
|
term_out("%0*lx", 2*ur.idlen, urclockID), first=0;
|
||||||
|
}
|
||||||
if(ur.showdate || ur.showall)
|
if(ur.showdate || ur.showall)
|
||||||
term_out(" %04d-%02d-%02d %02d.%02d"+first, ur.yyyy, ur.mm, ur.dd, ur.hr, ur.mn), first=0;
|
term_out(" %04d-%02d-%02d %02d.%02d"+first, ur.yyyy, ur.mm, ur.dd, ur.hr, ur.mn), first=0;
|
||||||
if(ur.showfilename || ur.showall)
|
if(ur.showfilename || ur.showall)
|
||||||
term_out(" %s"+first, *ur.filename? ur.filename: ""), first=0;
|
term_out(" %s"+first, *ur.filename? ur.filename: ""), first=0;
|
||||||
if(ur.showsketch || ur.showall)
|
if(ur.showapp || ur.showall)
|
||||||
term_out(" %s%d"+first, single || *ur.filename? "": "application ", ur.storestart), first=0;
|
term_out(" %s%d"+first, single || *ur.filename? "": "application ", ur.storestart), first=0;
|
||||||
if(ur.showstore || ur.showall)
|
if(ur.showstore || ur.showall)
|
||||||
term_out(" %s%d"+first, single? "": "store ", ur.storesize), first=0;
|
term_out(" %s%d"+first, single? "": "store ", ur.storesize), first=0;
|
||||||
if(ur.showmetadata || ur.showall)
|
if(ur.showmeta || ur.showall)
|
||||||
term_out(" %s%d"+first, single? "": "meta ", nmeta(ur.mcode, ur.uP.flashsize)), first=0;
|
term_out(" %s%d"+first, single? "": "meta ", nmeta(ur.mcode, ur.uP.flashsize)), first=0;
|
||||||
if(ur.showboot || ur.showall)
|
if(ur.showboot || ur.showall)
|
||||||
term_out(" %s%d"+first, single? "": "boot ", ur.blstart? flm->size-ur.blstart: 0), first=0;
|
term_out(" %s%d"+first, single? "": "boot ", ur.blstart? flm->size-ur.blstart: 0), first=0;
|
||||||
if(ur.showversion || ur.showall)
|
if(ur.showversion || ur.showall)
|
||||||
term_out(" %s"+first, ur.desc+(*ur.desc==' ')), first=0;
|
term_out(" %s"+first, ur.desc+(*ur.desc==' ')), first=0;
|
||||||
if(ur.showvbl || (ur.showall && ur.vbllevel))
|
if(ur.showvbl || ur.showall) {
|
||||||
term_out(" vector %d (%s)", ur.vblvectornum, vblvecname(pgm, ur.vblvectornum)), first=0;
|
int vnum = ur.vbllevel? ur.vblvectornum & 0x7f: 0;
|
||||||
|
term_out(" vector %d (%s)"+first, vnum, vblvecname(pgm, vnum)), first=0;
|
||||||
|
}
|
||||||
if(ur.showall)
|
if(ur.showall)
|
||||||
term_out(" %s"+first, ur.uP.name);
|
term_out(" %s"+first, ur.uP.name);
|
||||||
if(!first) {
|
if(!first) {
|
||||||
|
@ -1314,16 +1355,21 @@ alldone:
|
||||||
|
|
||||||
// STK500 section from stk500.c but modified significantly for use with urboot bootloaders
|
// STK500 section from stk500.c but modified significantly for use with urboot bootloaders
|
||||||
|
|
||||||
// STK500v1 load *word* address for flash/eeprom, memtype is 'E'/'F'
|
// STK500v1 load correct address for flash/eeprom, memchr is 'E'/'F'
|
||||||
static int urclock_load_waddr(const PROGRAMMER *pgm, char memtype, unsigned int waddr) {
|
static int urclock_load_baddr(const PROGRAMMER *pgm, const AVRPART *p, char memchr,
|
||||||
unsigned char buf[16];
|
unsigned int baddr) {
|
||||||
unsigned char ext_byte;
|
|
||||||
|
|
||||||
// STK500 protocol: support flash > 64K words with the correct extended-address byte
|
unsigned char buf[16], ext_byte;
|
||||||
if(memtype == 'F' && ur.uP.flashsize > 128*1024) {
|
|
||||||
ext_byte = (waddr >> 16) & 0xff;
|
// For classic parts (think optiboot, avrisp) use word addr, otherwise byte addr (optiboot_x etc)
|
||||||
|
int classic = !(p->prog_modes & (PM_UPDI | PM_PDI | PM_aWire));
|
||||||
|
unsigned int addr = classic? baddr/2: baddr;
|
||||||
|
|
||||||
|
// STK500 protocol: support flash > 64k words/bytes with the correct extended-address byte
|
||||||
|
if(memchr == 'F' && ur.uP.flashsize > (classic? 128*1024: 64*1024)) {
|
||||||
|
ext_byte = (addr >> 16) & 0xff;
|
||||||
if(ext_byte != ur.ext_addr_byte) {
|
if(ext_byte != ur.ext_addr_byte) {
|
||||||
// Either this is the first addr load, or a 64K word boundary is crossed
|
// Either this is the first addr load, or a 64k boundary is crossed
|
||||||
buf[0] = (uint8_t) (Subc_STK_UNIVERSAL_LEXT>>24);
|
buf[0] = (uint8_t) (Subc_STK_UNIVERSAL_LEXT>>24);
|
||||||
buf[1] = (uint8_t) (Subc_STK_UNIVERSAL_LEXT>>16);
|
buf[1] = (uint8_t) (Subc_STK_UNIVERSAL_LEXT>>16);
|
||||||
buf[2] = ext_byte;
|
buf[2] = ext_byte;
|
||||||
|
@ -1334,8 +1380,8 @@ static int urclock_load_waddr(const PROGRAMMER *pgm, char memtype, unsigned int
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[0] = Cmnd_STK_LOAD_ADDRESS;
|
buf[0] = Cmnd_STK_LOAD_ADDRESS;
|
||||||
buf[1] = waddr & 0xff;
|
buf[1] = addr & 0xff;
|
||||||
buf[2] = (waddr >> 8) & 0xff;
|
buf[2] = (addr >> 8) & 0xff;
|
||||||
buf[3] = Sync_CRC_EOP;
|
buf[3] = Sync_CRC_EOP;
|
||||||
|
|
||||||
if(urclock_send(pgm, buf, 4) < 0)
|
if(urclock_send(pgm, buf, 4) < 0)
|
||||||
|
@ -1352,21 +1398,21 @@ static int urclock_load_waddr(const PROGRAMMER *pgm, char memtype, unsigned int
|
||||||
* - mchr is 'F' (flash) or 'E' (EEPROM)
|
* - mchr is 'F' (flash) or 'E' (EEPROM)
|
||||||
* - payload for bytes to write or NULL for read
|
* - payload for bytes to write or NULL for read
|
||||||
*/
|
*/
|
||||||
static int urclock_paged_rdwr(const PROGRAMMER *pgm, char rwop, unsigned int badd,
|
static int urclock_paged_rdwr(const PROGRAMMER *pgm, const AVRPART *part, char rwop,
|
||||||
int len, char mchr, char *payload) {
|
unsigned int badd, int len, char mchr, char *payload) {
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
uint8_t buf[1024 + 5];
|
uint8_t buf[1024 + 5];
|
||||||
|
|
||||||
// STK500v1 only: tell the bootloader which word address should be used by next paged command
|
// STK500v1 only: tell the bootloader which address should be used by next paged command
|
||||||
if(!ur.urprotocol && urclock_load_waddr(pgm, mchr, badd/2) < 0)
|
if(!ur.urprotocol && urclock_load_baddr(pgm, part, mchr, badd) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if(mchr == 'F' && rwop == Cmnd_STK_PROG_PAGE && len != ur.uP.pagesize)
|
if(mchr == 'F' && rwop == Cmnd_STK_PROG_PAGE && len != ur.uP.pagesize)
|
||||||
Return("len %d must be page size %d for paged flash writes", len, ur.uP.pagesize);
|
Return("len %d must be page size %d for paged flash writes", len, ur.uP.pagesize);
|
||||||
|
|
||||||
if(ur.urprotocol) {
|
if(ur.urprotocol) {
|
||||||
uint8_t *p = buf, op =
|
uint8_t *q = buf, op =
|
||||||
mchr == 'F' && rwop == Cmnd_STK_PROG_PAGE? Cmnd_UR_PROG_PAGE_FL:
|
mchr == 'F' && rwop == Cmnd_STK_PROG_PAGE? Cmnd_UR_PROG_PAGE_FL:
|
||||||
mchr == 'E' && rwop == Cmnd_STK_PROG_PAGE? Cmnd_UR_PROG_PAGE_EE:
|
mchr == 'E' && rwop == Cmnd_STK_PROG_PAGE? Cmnd_UR_PROG_PAGE_EE:
|
||||||
mchr == 'F' && rwop == Cmnd_STK_READ_PAGE? Cmnd_UR_READ_PAGE_FL:
|
mchr == 'F' && rwop == Cmnd_STK_READ_PAGE? Cmnd_UR_READ_PAGE_FL:
|
||||||
|
@ -1375,25 +1421,25 @@ static int urclock_paged_rdwr(const PROGRAMMER *pgm, char rwop, unsigned int bad
|
||||||
if(op == 0xff)
|
if(op == 0xff)
|
||||||
Return("command not recognised");
|
Return("command not recognised");
|
||||||
|
|
||||||
*p++ = op;
|
*q++ = op;
|
||||||
*p++ = badd & 0xff;
|
*q++ = badd & 0xff;
|
||||||
*p++ = (badd >> 8) & 0xff;
|
*q++ = (badd >> 8) & 0xff;
|
||||||
// Flash is larger than 64 kBytes, extend address (even for EEPROM)
|
// Flash is larger than 64 kBytes, extend address (even for EEPROM)
|
||||||
if(ur.uP.flashsize > 0x10000)
|
if(ur.uP.flashsize > 0x10000)
|
||||||
*p++ = (badd >> 16) & 0xff;
|
*q++ = (badd >> 16) & 0xff;
|
||||||
|
|
||||||
if(ur.uP.pagesize <= 256) {
|
if(ur.uP.pagesize <= 256) {
|
||||||
if(len > 256)
|
if(len > 256)
|
||||||
Return("urprotocol paged r/w len %d cannot exceed 256", len);
|
Return("urprotocol paged r/w len %d cannot exceed 256", len);
|
||||||
*p++ = len; // len==256 is sent as 0
|
*q++ = len; // len==256 is sent as 0
|
||||||
} else {
|
} else {
|
||||||
int max = ur.uP.pagesize > 256? ur.uP.pagesize: 256;
|
int max = ur.uP.pagesize > 256? ur.uP.pagesize: 256;
|
||||||
if(len > max)
|
if(len > max)
|
||||||
Return("urprotocol paged r/w len %d cannot exceed %d for %s", len, max, ur.uP.name);
|
Return("urprotocol paged r/w len %d cannot exceed %d for %s", len, max, ur.uP.name);
|
||||||
*p++ = len>>8; // Big endian length when needed
|
*q++ = len>>8; // Big endian length when needed
|
||||||
*p++ = len;
|
*q++ = len;
|
||||||
}
|
}
|
||||||
i = p-buf;
|
i = q-buf;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
int max = ur.uP.pagesize > 256? ur.uP.pagesize: 256;
|
int max = ur.uP.pagesize > 256? ur.uP.pagesize: 256;
|
||||||
|
@ -1424,9 +1470,13 @@ static int urclock_paged_rdwr(const PROGRAMMER *pgm, char rwop, unsigned int bad
|
||||||
* Read len bytes at byte address addr of EEPROM (mchr == 'E') or flash (mchr == 'F') from
|
* Read len bytes at byte address addr of EEPROM (mchr == 'E') or flash (mchr == 'F') from
|
||||||
* device fd into buffer buf+1, using extended addressing if needed (extd); returns 0 on success
|
* device fd into buffer buf+1, using extended addressing if needed (extd); returns 0 on success
|
||||||
*/
|
*/
|
||||||
static int ur_readEF(const PROGRAMMER *pgm, uint8_t *buf, uint32_t badd, int len, char mchr) {
|
static int ur_readEF(const PROGRAMMER *pgm, const AVRPART *p, uint8_t *buf, uint32_t badd, int len,
|
||||||
pmsg_debug("ur_readEF(%s, %s, %p, 0x%06x, %d, %c)\n",
|
char mchr) {
|
||||||
pgm? pgm->desc: "?", mchr=='F'? "flash": "eeprom", buf, badd, len, mchr);
|
|
||||||
|
int classic = !(p->prog_modes & (PM_UPDI | PM_PDI | PM_aWire));
|
||||||
|
|
||||||
|
pmsg_debug("ur_readEF(%s, %s, %s, %p, 0x%06x, %d, %c)\n",
|
||||||
|
pgm? ldata(lfirst(pgm->id)): "?", p->desc, mchr=='F'? "flash": "eeprom", buf, badd, len, mchr);
|
||||||
|
|
||||||
if(mchr == 'F' && ur.urprotocol && !(ur.urfeatures & UB_READ_FLASH))
|
if(mchr == 'F' && ur.urprotocol && !(ur.urfeatures & UB_READ_FLASH))
|
||||||
Return("bootloader does not have flash read capability");
|
Return("bootloader does not have flash read capability");
|
||||||
|
@ -1437,8 +1487,8 @@ static int ur_readEF(const PROGRAMMER *pgm, uint8_t *buf, uint32_t badd, int len
|
||||||
if(len < 1 || len > max(ur.uP.pagesize, 256))
|
if(len < 1 || len > max(ur.uP.pagesize, 256))
|
||||||
Return("len %d exceeds range [1, %d]", len, max(ur.uP.pagesize, 256));
|
Return("len %d exceeds range [1, %d]", len, max(ur.uP.pagesize, 256));
|
||||||
|
|
||||||
// Odd byte address under STK500v1 word-address protocol
|
// Odd byte address under word-address protocol for "classic" parts (optiboot, avrisp etc)
|
||||||
int odd = !ur.urprotocol && (badd&1);
|
int odd = !ur.urprotocol && classic && (badd&1);
|
||||||
if(odd) { // Need to read one extra byte
|
if(odd) { // Need to read one extra byte
|
||||||
len++;
|
len++;
|
||||||
badd &= ~1;
|
badd &= ~1;
|
||||||
|
@ -1446,91 +1496,111 @@ static int ur_readEF(const PROGRAMMER *pgm, uint8_t *buf, uint32_t badd, int len
|
||||||
Return("len+1 = %d odd address exceeds range [1, %d]", len, max(ur.uP.pagesize, 256));
|
Return("len+1 = %d odd address exceeds range [1, %d]", len, max(ur.uP.pagesize, 256));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(urclock_paged_rdwr(pgm, Cmnd_STK_READ_PAGE, badd, len, mchr, NULL) < 0)
|
if(urclock_paged_rdwr(pgm, p, Cmnd_STK_READ_PAGE, badd, len, mchr, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return urclock_res_check(pgm, __func__, odd, buf, len-odd);
|
return urclock_res_check(pgm, __func__, odd, buf, len-odd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int readUrclockID(const PROGRAMMER *pgm) {
|
static int parseUrclockID(const PROGRAMMER *pgm) {
|
||||||
|
if(*ur.iddesc) { // User override of ID, eg, -xid=F.-4.2 for penultimate flash word
|
||||||
|
char *idstr = cfg_strdup(__func__, ur.iddesc), *idlenp, *end;
|
||||||
|
unsigned long ad, lg;
|
||||||
|
|
||||||
|
if(!(strchr("EF", *idstr) && idstr[1] == '.')) {
|
||||||
|
pmsg_warning("-xid=%s string must start with E. or F.\n", ur.iddesc);
|
||||||
|
free(idstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(idlenp = strchr(idstr+2, '.'))) {
|
||||||
|
pmsg_warning("-xid=%s string must look like [E|F].<addr>.<len>\n", ur.iddesc);
|
||||||
|
free(idstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
*idlenp++ = 0;
|
||||||
|
ad = strtoul(idstr+2, &end, 0);
|
||||||
|
if(*end || end == idstr+2) {
|
||||||
|
pmsg_warning("cannot parse address %s of -xid=%s\n", idstr+2, ur.iddesc);
|
||||||
|
free(idstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
long sad = *(long *) &ad;
|
||||||
|
if(sad < INT_MIN || sad > INT_MAX) {
|
||||||
|
pmsg_warning("address %s of -xid=%s has implausible size\n", idstr+2, ur.iddesc);
|
||||||
|
free(idstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
lg = strtoul(idlenp, &end, 0);
|
||||||
|
if(*end || end == idlenp) {
|
||||||
|
pmsg_warning("cannot parse length %s of -xid=%s string\n", idlenp, ur.iddesc);
|
||||||
|
free(idstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(!lg || lg > 8) {
|
||||||
|
pmsg_warning("length %s of -xid=%s string must be between 1 and 8\n", idlenp, ur.iddesc);
|
||||||
|
free(idstr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ur.idmchr = *idstr;
|
||||||
|
ur.idaddr = sad;
|
||||||
|
ur.idlen = lg;
|
||||||
|
|
||||||
|
free(idstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int readUrclockID(const PROGRAMMER *pgm, const AVRPART *p, uint64_t *urclockIDp) {
|
||||||
uint8_t spc[16];
|
uint8_t spc[16];
|
||||||
int addr = 256+1; // Location of DS18B20 ID of Urclock boards
|
int mchr, addr, len, size;
|
||||||
int len = 6;
|
|
||||||
char mchr = 'E';
|
|
||||||
|
|
||||||
ur.urclockID = 0;
|
if(ur.idlen)
|
||||||
|
mchr = ur.idmchr, addr = ur.idaddr, len = ur.idlen;
|
||||||
|
else
|
||||||
|
mchr = 'E', addr = 256+1, len = 6; // Default location for unique id on urclock boards
|
||||||
|
|
||||||
// Sanity for small boards
|
*urclockIDp = 0;
|
||||||
if(ur.uP.name && (addr >= ur.uP.eepromsize || addr+len > ur.uP.eepromsize)) {
|
|
||||||
|
// Sanity for small boards in absence of user -xid=... option
|
||||||
|
if(!ur.idlen && (addr >= ur.uP.eepromsize || addr+len > ur.uP.eepromsize)) {
|
||||||
addr = 0;
|
addr = 0;
|
||||||
if(ur.uP.eepromsize < 8)
|
if(ur.uP.eepromsize < 8)
|
||||||
mchr = 'F';
|
mchr = 'F';
|
||||||
}
|
}
|
||||||
|
|
||||||
if(*ur.iddesc) { // User override of ID, eg, -xid=F.-4.2 for penultimate flash word
|
const char *memtype = mchr == 'E'? "eeprom": "flash";
|
||||||
char *idstr = cfg_strdup(__func__, ur.iddesc), *idlen, *end, *memtype;
|
|
||||||
unsigned long ad, lg;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
if(!(strchr("EF", *idstr) && idstr[1] == '.')) {
|
size = mchr == 'F'? ur.uP.flashsize: ur.uP.eepromsize;
|
||||||
pmsg_warning("-xid=%s string must start with E. or F.\n", ur.iddesc);
|
|
||||||
free(idstr);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
memtype = *idstr == 'E'? "EEPROM": "flash";
|
|
||||||
size = *idstr == 'F'? ur.uP.flashsize: ur.uP.eepromsize;
|
|
||||||
|
|
||||||
if(!(idlen = strchr(idstr+2, '.'))) {
|
if(ur.uP.name && size > 0) {
|
||||||
pmsg_warning("-xid=%s string must look like [E|F].<addr>.<len>\n", ur.iddesc);
|
if(addr < 0) // X.-4.4 asks for 4 bytes at top memory
|
||||||
free(idstr);
|
addr += size;
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
*idlen++ = 0;
|
|
||||||
ad = strtoul(idstr+2, &end, 0);
|
|
||||||
if(*end || end == idstr+2) {
|
|
||||||
pmsg_warning("cannot parse address %s of -xid=%s\n", idstr+2, ur.iddesc);
|
|
||||||
free(idstr);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
if(size > 0 && (long) ad < 0)
|
|
||||||
ad += size;
|
|
||||||
if(ur.uP.name && size > 0 && ad >= (unsigned long) size) {
|
|
||||||
pmsg_warning("address %s of -xid=%s string out of %s range [0, 0x%04x]\n",
|
|
||||||
idstr+2, ur.iddesc, memtype, size-1);
|
|
||||||
free(idstr);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
lg = strtoul(idlen, &end, 0);
|
|
||||||
if(*end || end == idlen) {
|
|
||||||
pmsg_warning("cannot parse length %s of -xid=%s string\n", idlen, ur.iddesc);
|
|
||||||
free(idstr);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
if(!lg || lg > 8) {
|
|
||||||
pmsg_warning("length %s of -xid=%s string must be between 1 and 8\n", idlen, ur.iddesc);
|
|
||||||
free(idstr);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
if(ur.uP.name && size > 0 && ad+lg > (unsigned long) size) {
|
|
||||||
pmsg_warning("memory range [0x%04x, 0x%04x] of -xid=%s out of %s range [0, 0x%04x]\n",
|
|
||||||
(int) ad, (int) (ad+lg-1), ur.iddesc, memtype, size-1);
|
|
||||||
free(idstr);
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = ad;
|
if(addr < 0 || addr >= size)
|
||||||
len = lg;
|
Return("effective address %d of -xids=%s string out of %s range [0, 0x%04x]\n",
|
||||||
mchr = *idstr;
|
addr, ur.iddesc, memtype, size-1);
|
||||||
free(idstr);
|
|
||||||
|
if(addr+len > size)
|
||||||
|
Return("memory range [0x%04x, 0x%04x] of -xid=%s out of %s range [0, 0x%04x]\n",
|
||||||
|
addr, addr+len-1, ur.iddesc, memtype, size-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(spc, 0, sizeof spc);
|
memset(spc, 0, sizeof spc);
|
||||||
(void) ur_readEF(pgm, spc, addr, len, mchr);
|
if(mchr == 'E' && !ur.bleepromrw && !ur.xeepromrw)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
if(ur_readEF(pgm, p, spc, addr, len, mchr) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
// Urclock ID
|
// Urclock ID
|
||||||
for(int i = len-1; i >= 0; i--)
|
for(int i = len-1; i >= 0; i--)
|
||||||
ur.urclockID <<= 8, ur.urclockID |= spc[i];
|
*urclockIDp <<= 8, *urclockIDp |= spc[i];
|
||||||
ur.idlen = len;
|
ur.idlen = len;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1681,7 +1751,7 @@ static int urclock_cmd(const PROGRAMMER *pgm, const unsigned char *cmd, unsigned
|
||||||
|
|
||||||
|
|
||||||
// Either emulate chip erase or send appropriate command to bootloader
|
// Either emulate chip erase or send appropriate command to bootloader
|
||||||
static int urclock_chip_erase(const PROGRAMMER *pgm, const AVRPART *p) {
|
static int urclock_chip_erase(const PROGRAMMER *pgm, const AVRPART *p_unused) {
|
||||||
unsigned char buf[16];
|
unsigned char buf[16];
|
||||||
long bak_timeout = serial_recv_timeout;
|
long bak_timeout = serial_recv_timeout;
|
||||||
|
|
||||||
|
@ -1784,8 +1854,6 @@ static void urclock_disable(const PROGRAMMER *pgm) {
|
||||||
|
|
||||||
static int urclock_open(PROGRAMMER *pgm, const char *port) {
|
static int urclock_open(PROGRAMMER *pgm, const char *port) {
|
||||||
union pinfo pinfo;
|
union pinfo pinfo;
|
||||||
int showother = ur.showall || ur.showsketch || ur.showstore || ur.showmetadata || ur.showboot ||
|
|
||||||
ur.showversion || ur.showvbl || ur.showdate || ur.showfilename;
|
|
||||||
|
|
||||||
strcpy(pgm->port, port);
|
strcpy(pgm->port, port);
|
||||||
pinfo.serialinfo.baud = pgm->baudrate? pgm->baudrate: 115200;
|
pinfo.serialinfo.baud = pgm->baudrate? pgm->baudrate: 115200;
|
||||||
|
@ -1809,16 +1877,6 @@ static int urclock_open(PROGRAMMER *pgm, const char *port) {
|
||||||
if(urclock_getsync(pgm) < 0)
|
if(urclock_getsync(pgm) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// Only asking for the urclock ID: find out and exit fast!
|
|
||||||
if(ur.showid && !showother && strchr("EF", *ur.iddesc)) { // Also matches *ur.iddesc == 0
|
|
||||||
ur.xeepromrw = 1; // Pretend can read EEPROM
|
|
||||||
if(readUrclockID(pgm) == -1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
term_out("%0*lx\n", ur.idlen, ur.urclockID);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1832,7 +1890,7 @@ static void urclock_close(PROGRAMMER *pgm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int urclock_paged_write(const PROGRAMMER *pgm, const AVRPART *p_unused, const AVRMEM *m,
|
static int urclock_paged_write(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m,
|
||||||
unsigned int page_size, unsigned int addr, unsigned int n_bytes) {
|
unsigned int page_size, unsigned int addr, unsigned int n_bytes) {
|
||||||
|
|
||||||
int mchr, chunk;
|
int mchr, chunk;
|
||||||
|
@ -1840,8 +1898,8 @@ static int urclock_paged_write(const PROGRAMMER *pgm, const AVRPART *p_unused, c
|
||||||
|
|
||||||
if(n_bytes) {
|
if(n_bytes) {
|
||||||
// Paged writes only valid for flash and eeprom
|
// Paged writes only valid for flash and eeprom
|
||||||
mchr = strcmp(m->desc, "flash") == 0? 'F': 'E';
|
mchr = avr_mem_is_flash_type(m)? 'F': 'E';
|
||||||
if(mchr == 'E' && strcmp(m->desc, "eeprom"))
|
if(mchr == 'E' && !avr_mem_is_eeprom_type(m))
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
n = addr + n_bytes;
|
n = addr + n_bytes;
|
||||||
|
@ -1849,7 +1907,7 @@ static int urclock_paged_write(const PROGRAMMER *pgm, const AVRPART *p_unused, c
|
||||||
for(; addr < n; addr += chunk) {
|
for(; addr < n; addr += chunk) {
|
||||||
chunk = n-addr < page_size? n-addr: page_size;
|
chunk = n-addr < page_size? n-addr: page_size;
|
||||||
|
|
||||||
if(urclock_paged_rdwr(pgm, Cmnd_STK_PROG_PAGE, addr, chunk, mchr, (char *)&m->buf[addr]) < 0)
|
if(urclock_paged_rdwr(pgm, p, Cmnd_STK_PROG_PAGE, addr, chunk, mchr, (char *) m->buf+addr) < 0)
|
||||||
return -3;
|
return -3;
|
||||||
if(urclock_res_check(pgm, __func__, 0, NULL, 0) < 0)
|
if(urclock_res_check(pgm, __func__, 0, NULL, 0) < 0)
|
||||||
return -4;
|
return -4;
|
||||||
|
@ -1860,7 +1918,7 @@ static int urclock_paged_write(const PROGRAMMER *pgm, const AVRPART *p_unused, c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int urclock_paged_load(const PROGRAMMER *pgm, const AVRPART *p_unused, const AVRMEM *m,
|
static int urclock_paged_load(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m,
|
||||||
unsigned int page_size, unsigned int addr, unsigned int n_bytes) {
|
unsigned int page_size, unsigned int addr, unsigned int n_bytes) {
|
||||||
|
|
||||||
int mchr, chunk;
|
int mchr, chunk;
|
||||||
|
@ -1868,15 +1926,15 @@ static int urclock_paged_load(const PROGRAMMER *pgm, const AVRPART *p_unused, co
|
||||||
|
|
||||||
if(n_bytes) {
|
if(n_bytes) {
|
||||||
// Paged reads only valid for flash and eeprom
|
// Paged reads only valid for flash and eeprom
|
||||||
mchr = strcmp(m->desc, "flash") == 0? 'F': 'E';
|
mchr = avr_mem_is_flash_type(m)? 'F': 'E';
|
||||||
if(mchr == 'E' && strcmp(m->desc, "eeprom"))
|
if(mchr == 'E' && !avr_mem_is_eeprom_type(m))
|
||||||
return -2;
|
return -2;
|
||||||
|
|
||||||
n = addr + n_bytes;
|
n = addr + n_bytes;
|
||||||
for(; addr < n; addr += chunk) {
|
for(; addr < n; addr += chunk) {
|
||||||
chunk = n-addr < page_size? n-addr: page_size;
|
chunk = n-addr < page_size? n-addr: page_size;
|
||||||
|
|
||||||
if(urclock_paged_rdwr(pgm, Cmnd_STK_READ_PAGE, addr, chunk, mchr, NULL) < 0)
|
if(urclock_paged_rdwr(pgm, p, Cmnd_STK_READ_PAGE, addr, chunk, mchr, NULL) < 0)
|
||||||
return -3;
|
return -3;
|
||||||
if(urclock_res_check(pgm, __func__, 0, &m->buf[addr], chunk) < 0)
|
if(urclock_res_check(pgm, __func__, 0, &m->buf[addr], chunk) < 0)
|
||||||
return -4;
|
return -4;
|
||||||
|
@ -1928,9 +1986,9 @@ static int urclock_parseextparms(const PROGRAMMER *pgm, LISTID extparms) {
|
||||||
} options[] = {
|
} options[] = {
|
||||||
{"showall", &ur.showall, 0, NULL, 0, "Show all info for connected part and exit"},
|
{"showall", &ur.showall, 0, NULL, 0, "Show all info for connected part and exit"},
|
||||||
{"showid", &ur.showid, 0, NULL, 0, " ... unique Urclock ID"},
|
{"showid", &ur.showid, 0, NULL, 0, " ... unique Urclock ID"},
|
||||||
{"showsketch", &ur.showsketch, 0, NULL, 0, " ... application size"},
|
{"showapp", &ur.showapp, 0, NULL, 0, " ... application size"},
|
||||||
{"showstore", &ur.showstore, 0, NULL, 0, " ... store size"},
|
{"showstore", &ur.showstore, 0, NULL, 0, " ... store size"},
|
||||||
{"showmetadata", &ur.showmetadata, 0, NULL, 0, " ... metadata size"},
|
{"showmeta", &ur.showmeta, 0, NULL, 0, " ... metadata size"},
|
||||||
{"showboot", &ur.showboot, 0, NULL, 0, " ... bootloader size"},
|
{"showboot", &ur.showboot, 0, NULL, 0, " ... bootloader size"},
|
||||||
{"showversion", &ur.showversion, 0, NULL, 0, " ... bootloader version and capabilities"},
|
{"showversion", &ur.showversion, 0, NULL, 0, " ... bootloader version and capabilities"},
|
||||||
{"showvbl", &ur.showvbl, 0, NULL, 0, " ... vector bootloader level, vec # and name"},
|
{"showvbl", &ur.showvbl, 0, NULL, 0, " ... vector bootloader level, vec # and name"},
|
||||||
|
@ -1949,7 +2007,7 @@ static int urclock_parseextparms(const PROGRAMMER *pgm, LISTID extparms) {
|
||||||
{"delay", &ur.delay, 0, NULL, 1, "Add delay [ms] after reset, can be negative"},
|
{"delay", &ur.delay, 0, NULL, 1, "Add delay [ms] after reset, can be negative"},
|
||||||
{"id", NULL, sizeof ur.iddesc, ur.iddesc, 1, "Location of Urclock ID, eg F.12324.6"},
|
{"id", NULL, sizeof ur.iddesc, ur.iddesc, 1, "Location of Urclock ID, eg F.12324.6"},
|
||||||
{"title", NULL, sizeof ur.title, ur.title, 1, "Title used in lieu of a filename when set"},
|
{"title", NULL, sizeof ur.title, ur.title, 1, "Title used in lieu of a filename when set"},
|
||||||
{"?", &help, 0, NULL, 0, "Show this help menu and exit"},
|
{"help", &help, 0, NULL, 0, "Show this help menu and exit"},
|
||||||
};
|
};
|
||||||
|
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
@ -2009,12 +2067,15 @@ static int urclock_parseextparms(const PROGRAMMER *pgm, LISTID extparms) {
|
||||||
msg_error("%s -c %s extended options:\n", progname, (char *) ldata(lfirst(pgm->id)));
|
msg_error("%s -c %s extended options:\n", progname, (char *) ldata(lfirst(pgm->id)));
|
||||||
for(size_t i=0; i<sizeof options/sizeof*options; i++) {
|
for(size_t i=0; i<sizeof options/sizeof*options; i++) {
|
||||||
msg_error(" -x%s%s%*s%s\n", options[i].name, options[i].assign? "=<arg>": "",
|
msg_error(" -x%s%s%*s%s\n", options[i].name, options[i].assign? "=<arg>": "",
|
||||||
max(0, 16-strlen(options[i].name)-(options[i].assign? 6: 0)), "", options[i].help);
|
max(0, 16-(long) strlen(options[i].name)-(options[i].assign? 6: 0)), "", options[i].help);
|
||||||
}
|
}
|
||||||
if(rc == 0)
|
if(rc == 0)
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(parseUrclockID(pgm) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue