Update urclock programmer

This commit is contained in:
Stefan Rueger 2022-11-07 01:26:47 +00:00
parent e6c26d8db4
commit 21d93ec8cb
No known key found for this signature in database
GPG Key ID: B0B4F1FD86B1EC55
3 changed files with 334 additions and 170 deletions

View File

@ -147,9 +147,17 @@ programming mode. The programmer type is ``wiring''. Note that the -D option
will likely be required in this case, because the bootloader will rewrite the
program memory, but no true chip erase can be performed.
.Pp
The Arduino (which is very similar to the STK500 1.x) is supported via
its own programmer type specification ``arduino''. This programmer works for
the Arduino Uno Rev3 or any AVR that runs the Optiboot bootloader.
Serial bootloaders that run a skeleton of the STK500 1.x protocol are
supported via their own programmer type ``arduino''. This programmer works
for the Arduino Uno Rev3 or any AVR that runs the Optiboot bootloader.
.Pp
Urprotocol is a leaner version of the STK500 1.x protocol that is designed
to be backwards compatible with STK500 v1.x, and allows bootloaders to be
much smaller, eg, as implemented in the urboot project
https://github.com/stefanrueger/urboot. The programmer type ``urclock''
caters for these urboot programmers. Owing to its backward compatibility,
any bootloader that can be served by the arduino programmer can normally
also be served by the urclock programmer.
.Pp
The BusPirate is a versatile tool that can also be used as an AVR programmer.
A single BusPirate can be connected to up to 3 independent AVRs. See
@ -397,8 +405,8 @@ programming requires the memory be erased to 0xFF beforehand.
.Fl A
should be used when the programmer hardware, or bootloader
software for that matter, does not carry out chip erase and
instead handles the memory erase on a page level. The popular
Arduino bootloader exhibits this behaviour; for this reason
instead handles the memory erase on a page level. Popular
Arduino bootloaders exhibit this behaviour; for this reason
.Fl A
is engaged by default when specifying
. Fl c
@ -432,7 +440,7 @@ contents would exclusively cause bits to be programmed from the value
.Ql 1
to
.Ql 0 .
Note that in order to reprogram EERPOM cells, no explicit prior chip
Note that in order to reprogram EEPROM cells, no explicit prior chip
erase is required since the MCU provides an auto-erase cycle in that
case before programming the cell.
.It Xo Fl E Ar exitspec Ns
@ -1141,6 +1149,101 @@ programmer creates errors during initial sequence.
Specify how many connection retry attemps to perform before exiting.
Defaults to 10 if not specified.
.El
.It Ar Urclock
.Bl -tag -offset indent -width indent
.It Ar showall
Show all info for the connected part 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
.Bl -tag -offset indent -width indent
.It Ar reset={cs,aux,aux2}

View File

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

View File

@ -223,8 +223,9 @@
#define min(a, b) ((a) < (b)? (a): (b))
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 readUrclockID(const PROGRAMMER *pgm);
static int ur_readEF(const PROGRAMMER *pgm, const AVRPART *p, uint8_t *buf, uint32_t addr, int len,
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_recv(const PROGRAMMER *pgm, unsigned char *buf, size_t len);
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
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)
int32_t storestart; // Store (ie, unused flash) start address, same as application size
@ -280,9 +282,9 @@ typedef struct {
// Extended parameters for Urclock
int showall, // Show all pieces of info for connected part and exit
showid, // ... Urclock ID
showsketch, // ... application size
showapp, // ... application size
showstore, // ... store size
showmetadata, // ... metadata size
showmeta, // ... metadata size
showboot, // ... bootloader size
showversion, // ... bootloader version and capabilities
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
static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p_unused,
const AVRMEM *flm, const char *fname, int size) {
static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *flm,
const char *fname, int size) {
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);
@ -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",
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 llvectors = firstbeg == 0 && firstlen >= ur.uP.ninterrupts*vecsz; // Looks like vector table
for(int i=0; llvectors && i<ur.uP.ninterrupts*vecsz; i+=vecsz) {
@ -613,11 +642,11 @@ static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p_unused
if(llcode && llvectors && ur.vblvectornum > 0 && ur.vbllevel) {
// 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)) {
int appvecloc, reset32;
uint16_t reset16;
int reset32, appstart, appvecloc;
appvecloc = ur.vblvectornum*vecsz; // Location of jump-to-application in vector table
reset16 = buf2uint16(flm->buf); // First reset word of to-be-uploaded application
appvecloc = ur.vblvectornum*vecsz; // Location of jump-to-application in vector table
reset16 = buf2uint16(flm->buf); // First reset word of to-be-uploaded application
reset32 = vecsz == 2? reset16: buf2uint32(flm->buf);
/*
@ -631,17 +660,7 @@ static int urclock_flash_readhook(const PROGRAMMER *pgm, const AVRPART *p_unused
* an error thrown if so.
*/
int appstart;
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 {
if(reset2addr(flm->buf, vecsz, flm->size, &appstart) < 0) {
pmsg_warning("not patching input as opcode word %04x at reset is not a%sjmp\n",
reset16, vecsz==2? "n r": " ");
goto nopatch;
@ -771,7 +790,7 @@ nopatch_nometa:
if(isize < 1 || isize > (int) sizeof spc) // Should not happen
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++)
if(!(flm->tags[ai] & TAG_ALLOCATED))
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
// 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;
}
@ -1061,7 +1098,7 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
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)
Return("unknown interrupt vector #%d for vector bootloader -- should be in [-1, %d]",
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
if(!ur.urprotocol || (ur.urfeatures & UB_READ_FLASH)) {
// 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;
// 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;
// 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;
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
int i, npages, j, n, toend, dist, wasop32, wasjmp, op16;
uint8_t *p;
uint8_t *q;
uint16_t opcode;
op16 = wasjmp = wasop32 = 0;
@ -1169,10 +1206,10 @@ static int ur_initstruct(const PROGRAMMER *pgm, const AVRPART *p) {
npages = toend/flm->page_size;
for(i=0; i<npages; i++) {
// 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;
for(n=flm->page_size/2, p=spc, j=0; j<n; j++, p+=2, toend-=2) { // Check 16-bit opcodes
opcode = buf2uint16(p);
for(n=flm->page_size/2, q=spc, j=0; j<n; j++, q+=2, toend-=2) { // Check 16-bit opcodes
opcode = buf2uint16(q);
if(wasjmp) { // Opcode is the word address of the destination
wasjmp=0;
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.uP.bootsize > flm->size-ur.blstart? 'O': '-');
if((rc = readUrclockID(pgm)) == -1)
return rc;
ur.mcode = 0xff;
if(ur.blstart) {
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
if(ur.showall || ur.showid || ur.showsketch || ur.showstore || ur.showmetadata ||
ur.showboot || ur.showversion || ur.showvbl || ur.showdate || ur.showfilename) {
// Showing properties mostly requires examining the bytes below bootloader for metadata
if(ur.showall || (ur.showid && *ur.iddesc && *ur.iddesc != 'E') || ur.showapp ||
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;
if(spc[nm-1] != 0xff) {
@ -1260,7 +1295,7 @@ vblvecfound:
ur.hr = hr;
ur.mn = mn;
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)
return rc;
int len = mcode<sizeof ur.filename? mcode: sizeof ur.filename;
@ -1277,27 +1312,33 @@ vblvecfound:
// Print and exit when option show... was given
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;
if(ur.showid || ur.showall)
term_out("%0*lx", 2*ur.idlen, ur.urclockID), first=0;
if(ur.showid || ur.showall) {
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)
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)
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;
if(ur.showstore || ur.showall)
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;
if(ur.showboot || ur.showall)
term_out(" %s%d"+first, single? "": "boot ", ur.blstart? flm->size-ur.blstart: 0), first=0;
if(ur.showversion || ur.showall)
term_out(" %s"+first, ur.desc+(*ur.desc==' ')), first=0;
if(ur.showvbl || (ur.showall && ur.vbllevel))
term_out(" vector %d (%s)", ur.vblvectornum, vblvecname(pgm, ur.vblvectornum)), first=0;
if(ur.showvbl || ur.showall) {
int vnum = ur.vbllevel? ur.vblvectornum & 0x7f: 0;
term_out(" vector %d (%s)"+first, vnum, vblvecname(pgm, vnum)), first=0;
}
if(ur.showall)
term_out(" %s"+first, ur.uP.name);
if(!first) {
@ -1314,16 +1355,21 @@ alldone:
// STK500 section from stk500.c but modified significantly for use with urboot bootloaders
// STK500v1 load *word* address for flash/eeprom, memtype is 'E'/'F'
static int urclock_load_waddr(const PROGRAMMER *pgm, char memtype, unsigned int waddr) {
unsigned char buf[16];
unsigned char ext_byte;
// STK500v1 load correct address for flash/eeprom, memchr is 'E'/'F'
static int urclock_load_baddr(const PROGRAMMER *pgm, const AVRPART *p, char memchr,
unsigned int baddr) {
// STK500 protocol: support flash > 64K words with the correct extended-address byte
if(memtype == 'F' && ur.uP.flashsize > 128*1024) {
ext_byte = (waddr >> 16) & 0xff;
unsigned char buf[16], ext_byte;
// 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) {
// 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[1] = (uint8_t) (Subc_STK_UNIVERSAL_LEXT>>16);
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[1] = waddr & 0xff;
buf[2] = (waddr >> 8) & 0xff;
buf[1] = addr & 0xff;
buf[2] = (addr >> 8) & 0xff;
buf[3] = Sync_CRC_EOP;
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)
* - payload for bytes to write or NULL for read
*/
static int urclock_paged_rdwr(const PROGRAMMER *pgm, char rwop, unsigned int badd,
int len, char mchr, char *payload) {
static int urclock_paged_rdwr(const PROGRAMMER *pgm, const AVRPART *part, char rwop,
unsigned int badd, int len, char mchr, char *payload) {
int i;
uint8_t buf[1024 + 5];
// STK500v1 only: tell the bootloader which word address should be used by next paged command
if(!ur.urprotocol && urclock_load_waddr(pgm, mchr, badd/2) < 0)
// STK500v1 only: tell the bootloader which address should be used by next paged command
if(!ur.urprotocol && urclock_load_baddr(pgm, part, mchr, badd) < 0)
return -1;
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);
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 == 'E' && rwop == Cmnd_STK_PROG_PAGE? Cmnd_UR_PROG_PAGE_EE:
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)
Return("command not recognised");
*p++ = op;
*p++ = badd & 0xff;
*p++ = (badd >> 8) & 0xff;
*q++ = op;
*q++ = badd & 0xff;
*q++ = (badd >> 8) & 0xff;
// Flash is larger than 64 kBytes, extend address (even for EEPROM)
if(ur.uP.flashsize > 0x10000)
*p++ = (badd >> 16) & 0xff;
*q++ = (badd >> 16) & 0xff;
if(ur.uP.pagesize <= 256) {
if(len > 256)
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 {
int max = ur.uP.pagesize > 256? ur.uP.pagesize: 256;
if(len > max)
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
*p++ = len;
*q++ = len>>8; // Big endian length when needed
*q++ = len;
}
i = p-buf;
i = q-buf;
} else {
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
* 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) {
pmsg_debug("ur_readEF(%s, %s, %p, 0x%06x, %d, %c)\n",
pgm? pgm->desc: "?", mchr=='F'? "flash": "eeprom", buf, badd, len, mchr);
static int ur_readEF(const PROGRAMMER *pgm, const AVRPART *p, uint8_t *buf, uint32_t badd, int len,
char 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))
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))
Return("len %d exceeds range [1, %d]", len, max(ur.uP.pagesize, 256));
// Odd byte address under STK500v1 word-address protocol
int odd = !ur.urprotocol && (badd&1);
// Odd byte address under word-address protocol for "classic" parts (optiboot, avrisp etc)
int odd = !ur.urprotocol && classic && (badd&1);
if(odd) { // Need to read one extra byte
len++;
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));
}
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 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];
int addr = 256+1; // Location of DS18B20 ID of Urclock boards
int len = 6;
char mchr = 'E';
int mchr, addr, len, size;
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
if(ur.uP.name && (addr >= ur.uP.eepromsize || addr+len > ur.uP.eepromsize)) {
*urclockIDp = 0;
// Sanity for small boards in absence of user -xid=... option
if(!ur.idlen && (addr >= ur.uP.eepromsize || addr+len > ur.uP.eepromsize)) {
addr = 0;
if(ur.uP.eepromsize < 8)
mchr = 'F';
}
if(*ur.iddesc) { // User override of ID, eg, -xid=F.-4.2 for penultimate flash word
char *idstr = cfg_strdup(__func__, ur.iddesc), *idlen, *end, *memtype;
unsigned long ad, lg;
int size;
const char *memtype = mchr == 'E'? "eeprom": "flash";
if(!(strchr("EF", *idstr) && idstr[1] == '.')) {
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;
size = mchr == 'F'? ur.uP.flashsize: ur.uP.eepromsize;
if(!(idlen = strchr(idstr+2, '.'))) {
pmsg_warning("-xid=%s string must look like [E|F].<addr>.<len>\n", ur.iddesc);
free(idstr);
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;
}
if(ur.uP.name && size > 0) {
if(addr < 0) // X.-4.4 asks for 4 bytes at top memory
addr += size;
addr = ad;
len = lg;
mchr = *idstr;
free(idstr);
if(addr < 0 || addr >= size)
Return("effective address %d of -xids=%s string out of %s range [0, 0x%04x]\n",
addr, ur.iddesc, memtype, size-1);
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);
(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
for(int i = len-1; i >= 0; i--)
ur.urclockID <<= 8, ur.urclockID |= spc[i];
*urclockIDp <<= 8, *urclockIDp |= spc[i];
ur.idlen = len;
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
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];
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) {
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);
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)
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;
}
@ -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) {
int mchr, chunk;
@ -1840,8 +1898,8 @@ static int urclock_paged_write(const PROGRAMMER *pgm, const AVRPART *p_unused, c
if(n_bytes) {
// Paged writes only valid for flash and eeprom
mchr = strcmp(m->desc, "flash") == 0? 'F': 'E';
if(mchr == 'E' && strcmp(m->desc, "eeprom"))
mchr = avr_mem_is_flash_type(m)? 'F': 'E';
if(mchr == 'E' && !avr_mem_is_eeprom_type(m))
return -2;
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) {
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;
if(urclock_res_check(pgm, __func__, 0, NULL, 0) < 0)
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) {
int mchr, chunk;
@ -1868,15 +1926,15 @@ static int urclock_paged_load(const PROGRAMMER *pgm, const AVRPART *p_unused, co
if(n_bytes) {
// Paged reads only valid for flash and eeprom
mchr = strcmp(m->desc, "flash") == 0? 'F': 'E';
if(mchr == 'E' && strcmp(m->desc, "eeprom"))
mchr = avr_mem_is_flash_type(m)? 'F': 'E';
if(mchr == 'E' && !avr_mem_is_eeprom_type(m))
return -2;
n = addr + n_bytes;
for(; addr < n; addr += chunk) {
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;
if(urclock_res_check(pgm, __func__, 0, &m->buf[addr], chunk) < 0)
return -4;
@ -1928,9 +1986,9 @@ static int urclock_parseextparms(const PROGRAMMER *pgm, LISTID extparms) {
} options[] = {
{"showall", &ur.showall, 0, NULL, 0, "Show all info for connected part and exit"},
{"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"},
{"showmetadata", &ur.showmetadata, 0, NULL, 0, " ... metadata size"},
{"showmeta", &ur.showmeta, 0, NULL, 0, " ... metadata size"},
{"showboot", &ur.showboot, 0, NULL, 0, " ... bootloader size"},
{"showversion", &ur.showversion, 0, NULL, 0, " ... bootloader version and capabilities"},
{"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"},
{"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"},
{"?", &help, 0, NULL, 0, "Show this help menu and exit"},
{"help", &help, 0, NULL, 0, "Show this help menu and exit"},
};
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)));
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>": "",
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)
exit(0);
}
if(parseUrclockID(pgm) < 0)
return -1;
return rc;
}