Merge pull request #1040 from stefanrueger/partdesc

Developer options to describe parts and extend avrdude.conf syntax
This commit is contained in:
Stefan Rueger 2022-08-02 18:27:42 +01:00 committed by GitHub
commit 7f63632c6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1729 additions and 154 deletions

View File

@ -252,6 +252,9 @@ add_executable(avrdude
main.c main.c
term.c term.c
term.h term.h
developer_opts.c
developer_opts.h
developer_opts_private.h
whereami.c whereami.c
whereami.h whereami.h
"${EXTRA_WINDOWS_RESOURCES}" "${EXTRA_WINDOWS_RESOURCES}"

View File

@ -198,6 +198,9 @@ avrdude_SOURCES = \
main.c \ main.c \
whereami.c \ whereami.c \
whereami.h \ whereami.h \
developer_opts.c \
developer_opts.h \
developer_opts_private.h \
term.c \ term.c \
term.h term.h

View File

@ -12,7 +12,7 @@
# #
# DO NOT MODIFY THIS FILE. Modifications will be overwritten the next # DO NOT MODIFY THIS FILE. Modifications will be overwritten the next
# time a "make install" is run. For user-specific additions, use the # time a "make install" is run. For user-specific additions, use the
# "-C +filename" commandline option. # "-C +filename" command line option.
# #
# Possible entry formats are: # Possible entry formats are:
# #
@ -34,12 +34,12 @@
# rdyled = <num> ; # pin number # rdyled = <num> ; # pin number
# pgmled = <num> ; # pin number # pgmled = <num> ; # pin number
# vfyled = <num> ; # pin number # vfyled = <num> ; # pin number
# usbvid = <hexnum>; # USB VID (Vendor ID) # usbvid = <hexnum> ; # USB VID (Vendor ID)
# usbpid = <hexnum> [, <hexnum> ...] # USB PID (Product ID) (1) # usbpid = <hexnum> [, <hexnum> ...] ; # USB PID (Product ID) (1)
# usbdev = <interface>; # USB interface or other device info # usbdev = <interface> ; # USB interface or other device info
# usbvendor = <vendorname>; # USB Vendor Name # usbvendor = <vendorname> ; # USB Vendor Name
# usbproduct = <productname>; # USB Product Name # usbproduct = <productname> ; # USB Product Name
# usbsn = <serialno>; # USB Serial Number # usbsn = <serialno> ; # USB Serial Number
# hvupdi_support = <num> [, <num>, ... ] ; # UPDI HV Variants Support # hvupdi_support = <num> [, <num>, ... ] ; # UPDI HV Variants Support
# #
# To invert a bit, use = ~ <num>, the spaces are important. # To invert a bit, use = ~ <num>, the spaces are important.
@ -51,29 +51,31 @@
# ; # ;
# #
# part # part
# id = <id> ; # quoted string
# desc = <description> ; # quoted string # desc = <description> ; # quoted string
# id = <id> ; # quoted string
# family_id = <id> ; # quoted string, eg, "megaAVR" or "tinyAVR"
# hvupdi_variant = <num> ; # numeric -1 (n/a) or 0..2
# devicecode = <num> ; # deprecated, use stk500_devcode
# stk500_devcode = <num> ; # numeric
# avr910_devcode = <num> ; # numeric
# has_jtag = <yes/no> ; # part has JTAG i/f # has_jtag = <yes/no> ; # part has JTAG i/f
# has_debugwire = <yes/no> ; # part has debugWire i/f # has_debugwire = <yes/no> ; # part has debugWire i/f
# has_pdi = <yes/no> ; # part has PDI i/f # has_pdi = <yes/no> ; # part has PDI i/f
# has_updi = <yes/no> ; # part has UPDI i/f # has_updi = <yes/no> ; # part has UPDI i/f
# has_tpi = <yes/no> ; # part has TPI i/f # has_tpi = <yes/no> ; # part has TPI i/f
# devicecode = <num> ; # deprecated, use stk500_devcode # is_at90s1200 = <yes/no> ; # AT90S1200 part
# stk500_devcode = <num> ; # numeric # is_avr32 = <yes/no> ; # AVR32 part
# avr910_devcode = <num> ; # numeric
# signature = <num> <num> <num> ; # signature bytes # signature = <num> <num> <num> ; # signature bytes
# usbpid = <num> ; # DFU USB PID # usbpid = <num> ; # DFU USB PID
# chip_erase_delay = <num> ; # micro-seconds # chip_erase_delay = <num> ; # micro-seconds
# reset = dedicated | io; # reset = dedicated | io ;
# retry_pulse = reset | sck; # retry_pulse = reset | sck ;
# pgm_enable = <instruction format> ;
# chip_erase = <instruction format> ;
# chip_erase_delay = <num> ; # chip erase delay (us) # chip_erase_delay = <num> ; # chip erase delay (us)
# # STK500 parameters (parallel programming IO lines) # # STK500 parameters (parallel programming IO lines)
# pagel = <num> ; # pin name in hex, i.e., 0xD7 # pagel = <num> ; # pin name in hex, i.e., 0xD7
# bs2 = <num> ; # pin name in hex, i.e., 0xA0 # bs2 = <num> ; # pin name in hex, i.e., 0xA0
# serial = <yes/no> ; # can use serial downloading # serial = <yes/no> ; # can use serial downloading
# parallel = <yes/no/pseudo>; # can use par. programming # parallel = <yes/no/pseudo> ; # can use par. programming
# # STK500v2 parameters, to be taken from Atmel's XML files # # STK500v2 parameters, to be taken from Atmel's XML files
# timeout = <num> ; # timeout = <num> ;
# stabdelay = <num> ; # stabdelay = <num> ;
@ -85,52 +87,59 @@
# predelay = <num> ; # predelay = <num> ;
# postdelay = <num> ; # postdelay = <num> ;
# pollmethod = <num> ; # pollmethod = <num> ;
# mode = <num> ;
# delay = <num> ;
# blocksize = <num> ;
# readsize = <num> ;
# hvspcmdexedelay = <num> ; # hvspcmdexedelay = <num> ;
# # STK500v2 HV programming parameters, from XML # # STK500v2 HV programming parameters, from XML
# pp_controlstack = <num>, <num>, ...; # PP only # pp_controlstack = <num>, <num>, ... ; # PP only
# hvsp_controlstack = <num>, <num>, ...; # HVSP only # hvsp_controlstack = <num>, <num>, ... ; # HVSP only
# hventerstabdelay = <num>; # flash_instr = <num>, <num>, <num> ;
# progmodedelay = <num>; # PP only # eeprom_instr = <num>, <num>, ... ;
# latchcycles = <num>; # hventerstabdelay = <num> ;
# togglevtg = <num>; # progmodedelay = <num> ; # PP only
# poweroffdelay = <num>; # latchcycles = <num> ;
# resetdelayms = <num>; # togglevtg = <num> ;
# resetdelayus = <num>; # poweroffdelay = <num> ;
# hvleavestabdelay = <num>; # resetdelayms = <num> ;
# resetdelay = <num>; # resetdelayus = <num> ;
# synchcycles = <num>; # HVSP only # hvleavestabdelay = <num> ;
# chiperasepulsewidth = <num>; # PP only # resetdelay = <num> ;
# chiperasepolltimeout = <num>; # synchcycles = <num> ; # HVSP only
# chiperasetime = <num>; # HVSP only # chiperasepulsewidth = <num> ; # PP only
# programfusepulsewidth = <num>; # PP only # chiperasepolltimeout = <num> ;
# programfusepolltimeout = <num>; # chiperasetime = <num> ; # HVSP only
# programlockpulsewidth = <num>; # PP only # programfusepulsewidth = <num> ; # PP only
# programlockpolltimeout = <num>; # programfusepolltimeout = <num> ;
# programlockpulsewidth = <num> ; # PP only
# programlockpolltimeout = <num> ;
# # JTAG ICE mkII parameters, also from XML files # # JTAG ICE mkII parameters, also from XML files
# allowfullpagebitstream = <yes/no> ; # allowfullpagebitstream = <yes/no> ;
# enablepageprogramming = <yes/no> ; # enablepageprogramming = <yes/no> ;
# idr = <num> ; # IO addr of IDR (OCD) reg. # idr = <num> ; # IO addr of IDR (OCD) reg
# rampz = <num> ; # IO addr of RAMPZ reg. # rampz = <num> ; # IO addr of RAMPZ reg
# spmcr = <num> ; # mem addr of SPMC[S]R reg. # spmcr = <num> ; # mem addr of SPMC[S]R reg
# eecr = <num> ; # mem addr of EECR reg. # eecr = <num> ; # mem addr of EECR reg only when != 0x3c
# # (only when != 0x3c) # mcu_base = <num> ;
# is_at90s1200 = <yes/no> ; # AT90S1200 part # nvm_base = <num> ;
# is_avr32 = <yes/no> ; # AVR32 part # ocd_base = <num> ;
# ocdrev = <num> ;
# pgm_enable = <instruction format> ;
# chip_erase = <instruction format> ;
# #
# memory <memtype> # memory <memtype>
# paged = <yes/no> ; # yes / no # paged = <yes/no> ; # yes/no (flash only, do not use for EEPROM)
# offset = <num> ; # memory offset
# size = <num> ; # bytes # size = <num> ; # bytes
# page_size = <num> ; # bytes # page_size = <num> ; # bytes
# num_pages = <num> ; # numeric # num_pages = <num> ; # numeric
# min_write_delay = <num> ; # micro-seconds # min_write_delay = <num> ; # micro-seconds
# max_write_delay = <num> ; # micro-seconds # max_write_delay = <num> ; # micro-seconds
# readback_p1 = <num> ; # byte value # readback = <num> <num> ; # pair of byte values
# readback_p2 = <num> ; # byte value # readback_p1 = <num> ; # byte value (first component)
# pwroff_after_write = <yes/no> ; # yes / no # readback_p2 = <num> ; # byte value (second component)
# pwroff_after_write = <yes/no> ; # yes/no
# mode = <num> ; # STK500 v2 file parameter, to be taken from Atmel's XML files
# delay = <num> ; # "
# blocksize = <num> ; # "
# readsize = <num> ; # "
# read = <instruction format> ; # read = <instruction format> ;
# write = <instruction format> ; # write = <instruction format> ;
# read_lo = <instruction format> ; # read_lo = <instruction format> ;
@ -144,7 +153,8 @@
# ; # ;
# #
# If any of the above parameters are not specified, the default value # If any of the above parameters are not specified, the default value
# of 0 is used for numerics or the empty string ("") for string # of 0 is used for numerics (except for hvupdi_variant and ocdrev,
# where the default value is -1) or the empty string ("") for string
# values. If a required parameter is left empty, AVRDUDE will # values. If a required parameter is left empty, AVRDUDE will
# complain. # complain.
# #
@ -152,7 +162,12 @@
# using the following syntax. In this case specified integer and # using the following syntax. In this case specified integer and
# string values override parameter values from the parent part. New # string values override parameter values from the parent part. New
# memory definitions are added to the definitions inherited from the # memory definitions are added to the definitions inherited from the
# parent. # parent. If, however, a new memory definition refers to an existing
# one of the same name for that part then, from v7.1, the existing
# memory definition is extended, and components overwritten with new
# values. Assigning NULL removes an inherited SPI instruction format,
# memory definition, control stack, eeprom or flash instruction, eg,
# as in memory "efuse" = NULL;
# #
# part parent <id> # quoted string # part parent <id> # quoted string
# id = <id> ; # quoted string # id = <id> ; # quoted string
@ -181,7 +196,7 @@
# #
# INSTRUCTION FORMATS # INSTRUCTION FORMATS
# #
# Instruction formats are specified as a comma seperated list of # Instruction formats are specified as a comma separated list of
# string values containing information (bit specifiers) about each # string values containing information (bit specifiers) about each
# of the 32 bits of the instruction. Bit specifiers may be one of # of the 32 bits of the instruction. Bit specifiers may be one of
# the following formats: # the following formats:
@ -190,10 +205,11 @@
# #
# '0' = the bit is always clear on input as well as output # '0' = the bit is always clear on input as well as output
# #
# 'x' = the bit is ignored on input and output # 'x' = the bit is ignored on input and output and set as 0
# #
# 'a' = the bit is an address bit, the bit-number matches this bit # 'a' = the bit is an address bit; from v 7.1 the bit-number
# specifier's position within the current instruction byte # is set to match the right bit position for the
# instruction to "just work"
# #
# 'aN' = the bit is the Nth address bit, bit-number = N, i.e., a12 # 'aN' = the bit is the Nth address bit, bit-number = N, i.e., a12
# is address bit 12 on input, a0 is address bit 0. # is address bit 12 on input, a0 is address bit 0.
@ -204,9 +220,26 @@
# #
# Each instruction must be composed of 32 bit specifiers. The # Each instruction must be composed of 32 bit specifiers. The
# instruction specification closely follows the instruction data # instruction specification closely follows the instruction data
# provided in Atmel's data sheets for their parts. # provided in Atmel's data sheets for their parts. Note that flash
# addresses always refer to *word* addresses whilst all other
# memory types specify *byte* addresses.
# #
# See below for some examples. # Example for signature read on the ATmega328P:
# read = "0 0 1 1 0 0 0 0", "0 0 0 x x x x x",
# "x x x x x x a1 a0", "o o o o o o o o";
#
# As the address bit numbers in the SPI opcodes are highly
# systematic, they don't really need to be specified. A compact
# version of the format specification neither uses bit-numbers for
# address lines nor spaces. If such a string is longer than 7
# characters, then the characters 0, 1, x, a, i and o will be
# recognised as the corresponding bit, whilst any of the characters
# ., -, _ or / can act as arbitrary visual separators, which are
# ignored. Examples:
#
# loadpage_lo = "0100.0000--000x.xxxx--xxaa.aaaa--iiii.iiii";
#
# loadpage_lo = "0100.0000", "000x.xxxx", "xxaa.aaaa", "iiii.iiii";
# #
# #
# The following are STK500 part device codes to use for the # The following are STK500 part device codes to use for the
@ -11845,6 +11878,8 @@ part parent "m2561"
" a7 x x x x x x x", " a7 x x x x x x x",
" x x x x x x x x"; " x x x x x x x x";
load_ext_addr = NULL;
mode = 0x41; mode = 0x41;
delay = 20; delay = 20;
blocksize = 256; blocksize = 256;

View File

@ -71,6 +71,21 @@ void avr_free_opcode(OPCODE * op)
free(op); free(op);
} }
// returns position 0..31 of highest bit set or INT_MIN if no bit is set
int intlog2(unsigned int n) {
int ret;
if(!n)
return INT_MIN;
for(ret = 0; n >>= 1; ret++)
continue;
return ret;
}
/* /*
* avr_set_bits() * avr_set_bits()
* *
@ -126,6 +141,101 @@ int avr_set_addr(OPCODE * op, unsigned char * cmd, unsigned long addr)
} }
/*
* avr_set_addr_mem()
*
* Set address bits in the specified command based on the memory, opcode and
* address; addr must be a word address for flash or, for all other memories,
* a byte address; returns 0 on success and -1 on error (no memory or no
* opcode) or, if positive, bn+1 where bn is bit number of the highest
* necessary bit that the opcode does not provide.
*/
int avr_set_addr_mem(AVRMEM *mem, int opnum, unsigned char *cmd, unsigned long addr) {
int ret, isflash, lo, hi, memsize, pagesize;
OPCODE *op;
if(!mem)
return -1;
if(!(op = mem->op[opnum]))
return -1;
isflash = !strcmp(mem->desc, "flash"); // ISP parts have only one flash-like memory
memsize = mem->size >> isflash; // word addresses for flash
pagesize = mem->page_size >> isflash;
// compute range lo..hi of needed address bits
switch(opnum) {
case AVR_OP_READ:
case AVR_OP_WRITE:
case AVR_OP_READ_LO:
case AVR_OP_READ_HI:
case AVR_OP_WRITE_LO:
case AVR_OP_WRITE_HI:
lo = 0;
hi = intlog2(memsize-1); // memsize = 1 implies no addr bit is needed
break;
case AVR_OP_LOADPAGE_LO:
case AVR_OP_LOADPAGE_HI:
lo = 0;
hi = intlog2(pagesize-1);
break;
case AVR_OP_LOAD_EXT_ADDR:
lo = 16;
hi = intlog2(memsize-1);
break;
case AVR_OP_WRITEPAGE:
lo = intlog2(pagesize);
hi = intlog2(memsize-1);
break;
case AVR_OP_CHIP_ERASE:
case AVR_OP_PGM_ENABLE:
default:
lo = 0;
hi = -1;
break;
}
// Unless it's load extended address, ISP chips only deal with 16 bit addresses
if(opnum != AVR_OP_LOAD_EXT_ADDR && hi > 15)
hi = 15;
unsigned char avail[32];
memset(avail, 0, sizeof avail);
for(int i=0; i<32; i++) {
if(op->bit[i].type == AVR_CMDBIT_ADDRESS) {
int bitno, j, bit;
unsigned char mask;
bitno = op->bit[i].bitno & 31;
j = 3 - i / 8;
bit = i % 8;
mask = 1 << bit;
avail[bitno] = 1;
// 'a' bit with number outside bit range [lo, hi] is set to 0
if (bitno >= lo && bitno <= hi? (addr >> bitno) & 1: 0)
cmd[j] = cmd[j] | mask;
else
cmd[j] = cmd[j] & ~mask;
}
}
ret = 0;
if(lo >= 0 && hi < 32 && lo <= hi)
for(int bn=lo; bn <= hi; bn++)
if(!avail[bn]) // necessary bit bn misses in opcode
ret = bn+1;
return ret;
}
/* /*
* avr_set_input() * avr_set_input()
* *
@ -239,7 +349,6 @@ static char * bittype(int type)
} }
/*** /***
*** Elementary functions dealing with AVRMEM structures *** Elementary functions dealing with AVRMEM structures
***/ ***/
@ -354,7 +463,6 @@ AVRMEM_ALIAS * avr_dup_memalias(AVRMEM_ALIAS * m)
void avr_free_mem(AVRMEM * m) void avr_free_mem(AVRMEM * m)
{ {
int i;
if (m->buf != NULL) { if (m->buf != NULL) {
free(m->buf); free(m->buf);
m->buf = NULL; m->buf = NULL;
@ -363,7 +471,7 @@ void avr_free_mem(AVRMEM * m)
free(m->tags); free(m->tags);
m->tags = NULL; m->tags = NULL;
} }
for(i=0;i<sizeof(m->op)/sizeof(m->op[0]);i++) for(size_t i=0; i<sizeof(m->op)/sizeof(m->op[0]); i++)
{ {
if (m->op[i] != NULL) if (m->op[i] != NULL)
{ {
@ -379,13 +487,16 @@ void avr_free_memalias(AVRMEM_ALIAS * m)
free(m); free(m);
} }
AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, char * desc) AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, const char * desc)
{ {
AVRMEM_ALIAS * m, * match; AVRMEM_ALIAS * m, * match;
LNODEID ln; LNODEID ln;
int matches; int matches;
int l; int l;
if(!p || !desc || !p->mem_alias)
return NULL;
l = strlen(desc); l = strlen(desc);
matches = 0; matches = 0;
match = NULL; match = NULL;
@ -403,13 +514,16 @@ AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, char * desc)
return NULL; return NULL;
} }
AVRMEM * avr_locate_mem_noalias(AVRPART * p, char * desc) AVRMEM * avr_locate_mem_noalias(AVRPART * p, const char * desc)
{ {
AVRMEM * m, * match; AVRMEM * m, * match;
LNODEID ln; LNODEID ln;
int matches; int matches;
int l; int l;
if(!p || !desc || !p->mem)
return NULL;
l = strlen(desc); l = strlen(desc);
matches = 0; matches = 0;
match = NULL; match = NULL;
@ -428,7 +542,7 @@ AVRMEM * avr_locate_mem_noalias(AVRPART * p, char * desc)
} }
AVRMEM * avr_locate_mem(AVRPART * p, char * desc) AVRMEM * avr_locate_mem(AVRPART * p, const char * desc)
{ {
AVRMEM * m, * match; AVRMEM * m, * match;
AVRMEM_ALIAS * alias; AVRMEM_ALIAS * alias;
@ -436,9 +550,13 @@ AVRMEM * avr_locate_mem(AVRPART * p, char * desc)
int matches; int matches;
int l; int l;
if(!p || !desc)
return NULL;
l = strlen(desc); l = strlen(desc);
matches = 0; matches = 0;
match = NULL; match = NULL;
if(p->mem) {
for (ln=lfirst(p->mem); ln; ln=lnext(ln)) { for (ln=lfirst(p->mem); ln; ln=lnext(ln)) {
m = ldata(ln); m = ldata(ln);
if (strncmp(desc, m->desc, l) == 0) { if (strncmp(desc, m->desc, l) == 0) {
@ -446,6 +564,7 @@ AVRMEM * avr_locate_mem(AVRPART * p, char * desc)
matches++; matches++;
} }
} }
}
if (matches == 1) if (matches == 1)
return match; return match;
@ -476,7 +595,8 @@ AVRMEM_ALIAS * avr_find_memalias(AVRPART * p, AVRMEM * m_orig)
void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p, void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
int type, int verbose) int type, int verbose)
{ {
static unsigned int prev_mem_offset, prev_mem_size; static unsigned int prev_mem_offset;
static int prev_mem_size;
int i, j; int i, j;
char * optr; char * optr;
@ -545,12 +665,10 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
} }
/* /*
* Elementary functions dealing with AVRPART structures * Elementary functions dealing with AVRPART structures
*/ */
AVRPART * avr_new_part(void) AVRPART * avr_new_part(void)
{ {
AVRPART * p; AVRPART * p;
@ -568,6 +686,7 @@ AVRPART * avr_new_part(void)
p->reset_disposition = RESET_DEDICATED; p->reset_disposition = RESET_DEDICATED;
p->retry_pulse = PIN_AVR_SCK; p->retry_pulse = PIN_AVR_SCK;
p->flags = AVRPART_SERIALOK | AVRPART_PARALLELOK | AVRPART_ENABLEPAGEPROGRAMMING; p->flags = AVRPART_SERIALOK | AVRPART_PARALLELOK | AVRPART_ENABLEPAGEPROGRAMMING;
p->parent_id = NULL;
p->config_file = NULL; p->config_file = NULL;
p->lineno = 0; p->lineno = 0;
memset(p->signature, 0xFF, 3); memset(p->signature, 0xFF, 3);
@ -597,7 +716,6 @@ AVRPART * avr_dup_part(AVRPART * d)
p->mem = save; p->mem = save;
p->mem_alias = save2; p->mem_alias = save2;
for (ln=lfirst(d->mem); ln; ln=lnext(ln)) { for (ln=lfirst(d->mem); ln; ln=lnext(ln)) {
AVRMEM *m = ldata(ln); AVRMEM *m = ldata(ln);
AVRMEM *m2 = avr_dup_mem(m); AVRMEM *m2 = avr_dup_mem(m);
@ -625,15 +743,13 @@ AVRPART * avr_dup_part(AVRPART * d)
void avr_free_part(AVRPART * d) void avr_free_part(AVRPART * d)
{ {
int i;
ldestroy_cb(d->mem, (void(*)(void *))avr_free_mem); ldestroy_cb(d->mem, (void(*)(void *))avr_free_mem);
d->mem = NULL; d->mem = NULL;
ldestroy_cb(d->mem_alias, (void(*)(void *))avr_free_memalias); ldestroy_cb(d->mem_alias, (void(*)(void *))avr_free_memalias);
d->mem_alias = NULL; d->mem_alias = NULL;
for(i=0;i<sizeof(d->op)/sizeof(d->op[0]);i++) /* do not free d->parent_id and d->config_file */
{ for(size_t i=0; i<sizeof(d->op)/sizeof(d->op[0]); i++) {
if (d->op[i] != NULL) if (d->op[i] != NULL) {
{
avr_free_opcode(d->op[i]); avr_free_opcode(d->op[i]);
d->op[i] = NULL; d->op[i] = NULL;
} }
@ -641,12 +757,15 @@ int i;
free(d); free(d);
} }
AVRPART * locate_part(LISTID parts, char * partdesc) AVRPART * locate_part(LISTID parts, const char * partdesc)
{ {
LNODEID ln1; LNODEID ln1;
AVRPART * p = NULL; AVRPART * p = NULL;
int found; int found;
if(!parts || !partdesc)
return NULL;
found = 0; found = 0;
for (ln1=lfirst(parts); ln1 && !found; ln1=lnext(ln1)) { for (ln1=lfirst(parts); ln1 && !found; ln1=lnext(ln1)) {
@ -807,3 +926,232 @@ void avr_display(FILE * f, AVRPART * p, const char * prefix, int verbose)
if (buf) if (buf)
free(buf); free(buf);
} }
char cmdbitchar(CMDBIT cb) {
switch(cb.type) {
case AVR_CMDBIT_IGNORE:
return 'x';
case AVR_CMDBIT_VALUE:
return cb.value? '1': '0';
case AVR_CMDBIT_ADDRESS:
return 'a';
case AVR_CMDBIT_INPUT:
return 'i';
case AVR_CMDBIT_OUTPUT:
return 'o';
default:
return '?';
}
}
char *cmdbitstr(CMDBIT cb) {
char space[32];
*space = cmdbitchar(cb);
if(*space == 'a')
sprintf(space+1, "%d", cb.bitno);
else
space[1] = 0;
return strdup(space);
}
const char *opcodename(int opnum) {
switch(opnum) {
case AVR_OP_READ:
return "read";
case AVR_OP_WRITE:
return "write";
case AVR_OP_READ_LO:
return "read_lo";
case AVR_OP_READ_HI:
return "read_hi";
case AVR_OP_WRITE_LO:
return "write_lo";
case AVR_OP_WRITE_HI:
return "write_hi";
case AVR_OP_LOADPAGE_LO:
return "loadpage_lo";
case AVR_OP_LOADPAGE_HI:
return "loadpage_hi";
case AVR_OP_LOAD_EXT_ADDR:
return "load_ext_addr";
case AVR_OP_WRITEPAGE:
return "writepage";
case AVR_OP_CHIP_ERASE:
return "chip_erase";
case AVR_OP_PGM_ENABLE:
return "pgm_enable";
default:
return "???";
}
}
// Unique string representation of an opcode
char *opcode2str(OPCODE *op, int opnum, int detailed) {
char cb, space[1024], *sp = space;
int compact = 1;
if(!op)
return strdup("NULL");
// Can the opcode be printed in a compact way? Only if address bits are systematic.
for(int i=31; i >= 0; i--)
if(op->bit[i].type == AVR_CMDBIT_ADDRESS)
if(i<8 || i>23 || op->bit[i].bitno != (opnum == AVR_OP_LOAD_EXT_ADDR? i+8: i-8))
compact = 0;
if(detailed)
*sp++ = '"';
for(int i=31; i >= 0; i--) {
*sp++ = cb = cmdbitchar(op->bit[i]);
if(compact) {
if(i && i%8 == 0)
*sp++ = '-', *sp++ = '-';
else if(i && i%4 == 0)
*sp++ = '.';
} else {
if(cb == 'a') {
sprintf(sp, "%d", op->bit[i].bitno);
sp += strlen(sp);
}
if(i) {
if(detailed)
*sp++ = ' ';
if(i%8 == 0)
*sp++ = ' ';
}
}
}
if(detailed)
*sp++ = '"';
*sp = 0;
return strdup(space);
}
/*
* Match STRING against the partname pattern PATTERN, returning 1 if it
* matches, 0 if not. NOTE: part_match() is a modified old copy of !fnmatch()
* from the GNU C Library (published under GLP v2). Used for portability.
*/
inline static int fold(int c) {
return (c >= 'A' && c <= 'Z')? c+('a'-'A'): c;
}
int part_match(const char *pattern, const char *string) {
unsigned char c;
const char *p = pattern, *n = string;
if(!*n) // AVRDUDE specialty: empty string never matches
return 0;
while((c = fold(*p++))) {
switch(c) {
case '?':
if(*n == 0)
return 0;
break;
case '\\':
c = fold(*p++);
if(fold(*n) != c)
return 0;
break;
case '*':
for(c = *p++; c == '?' || c == '*'; c = *p++)
if(c == '?' && *n++ == 0)
return 0;
if(c == 0)
return 1;
{
unsigned char c1 = fold(c == '\\'? *p : c); // This char
for(--p; *n; ++n) // Recursively check reminder of string for *
if((c == '[' || fold(*n) == c1) && part_match(p, n) == 1)
return 1;
return 0;
}
case '[':
{
int negate;
if(*n == 0)
return 0;
negate = (*p == '!' || *p == '^');
if(negate)
++p;
c = *p++;
for(;;) {
unsigned char cstart = c, cend = c;
if(c == '\\')
cstart = cend = *p++;
cstart = cend = fold(cstart);
if(c == 0) // [ (unterminated)
return 0;
c = *p++;
c = fold(c);
if(c == '-' && *p != ']') {
cend = *p++;
if(cend == '\\')
cend = *p++;
if(cend == 0)
return 0;
cend = fold(cend);
c = *p++;
}
if(fold(*n) >= cstart && fold(*n) <= cend)
goto matched;
if(c == ']')
break;
}
if(!negate)
return 0;
break;
matched:;
while(c != ']') { // Skip the rest of the [...] that already matched
if(c == 0) // [... (unterminated)
return 0;
c = *p++;
if(c == '\\') // XXX 1003.2d11 is unclear if this is right
++p;
}
if(negate)
return 0;
}
break;
default:
if(c != fold(*n))
return 0;
}
++n;
}
return *n == 0;
}

View File

@ -25,10 +25,12 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <errno.h>
#include "avrdude.h" #include "avrdude.h"
#include "libavrdude.h" #include "libavrdude.h"
#include "config.h" #include "config.h"
#include "developer_opts.h"
#if defined(WIN32) #if defined(WIN32)
#define strtok_r( _s, _sep, _lasts ) \ #define strtok_r( _s, _sep, _lasts ) \
@ -45,11 +47,13 @@ int yywarning(char * errmsg, ...);
static int assign_pin(int pinno, TOKEN * v, int invert); static int assign_pin(int pinno, TOKEN * v, int invert);
static int assign_pin_list(int invert); static int assign_pin_list(int invert);
static int which_opcode(TOKEN * opcode); static int which_opcode(TOKEN * opcode);
static int parse_cmdbits(OPCODE * op); static int parse_cmdbits(OPCODE * op, int opnum);
static int pin_name; static int pin_name;
%} %}
%token K_NULL;
%token K_READ %token K_READ
%token K_WRITE %token K_WRITE
%token K_READ_LO %token K_READ_LO
@ -112,6 +116,7 @@ static int pin_name;
%token K_PSEUDO %token K_PSEUDO
%token K_PWROFF_AFTER_WRITE %token K_PWROFF_AFTER_WRITE
%token K_RDYLED %token K_RDYLED
%token K_READBACK
%token K_READBACK_P1 %token K_READBACK_P1
%token K_READBACK_P2 %token K_READBACK_P2
%token K_READMEM %token K_READMEM
@ -329,6 +334,7 @@ prog_decl :
free_token($3); free_token($3);
YYABORT; YYABORT;
} }
current_prog->parent_id = cache_string($3->value.string);
current_prog->config_file = cache_string(cfg_infile); current_prog->config_file = cache_string(cfg_infile);
current_prog->lineno = cfg_lineno; current_prog->lineno = cfg_lineno;
free_token($3); free_token($3);
@ -420,6 +426,7 @@ part_decl :
free_token($3); free_token($3);
YYABORT; YYABORT;
} }
current_part->parent_id = cache_string($3->value.string);
current_part->config_file = cache_string(cfg_infile); current_part->config_file = cache_string(cfg_infile);
current_part->lineno = cfg_lineno; current_part->lineno = cfg_lineno;
@ -797,6 +804,13 @@ part_parm :
} }
} | } |
K_PP_CONTROLSTACK TKN_EQUAL K_NULL {
{
current_part->ctl_stack_type = CTL_STACK_NONE;
memset(current_part->controlstack, 0, CTL_STACK_SIZE);
}
} |
K_HVSP_CONTROLSTACK TKN_EQUAL num_list { K_HVSP_CONTROLSTACK TKN_EQUAL num_list {
{ {
TOKEN * t; TOKEN * t;
@ -828,6 +842,13 @@ part_parm :
} }
} | } |
K_HVSP_CONTROLSTACK TKN_EQUAL K_NULL {
{
current_part->ctl_stack_type = CTL_STACK_NONE;
memset(current_part->controlstack, 0, CTL_STACK_SIZE);
}
} |
K_FLASH_INSTR TKN_EQUAL num_list { K_FLASH_INSTR TKN_EQUAL num_list {
{ {
TOKEN * t; TOKEN * t;
@ -858,6 +879,12 @@ part_parm :
} }
} | } |
K_FLASH_INSTR TKN_EQUAL K_NULL {
{
memset(current_part->flash_instr, 0, FLASH_INSTR_SIZE);
}
} |
K_EEPROM_INSTR TKN_EQUAL num_list { K_EEPROM_INSTR TKN_EQUAL num_list {
{ {
TOKEN * t; TOKEN * t;
@ -888,6 +915,12 @@ part_parm :
} }
} | } |
K_EEPROM_INSTR TKN_EQUAL K_NULL {
{
memset(current_part->eeprom_instr, 0, EEPROM_INSTR_SIZE);
}
} |
K_CHIP_ERASE_DELAY TKN_EQUAL TKN_NUMBER K_CHIP_ERASE_DELAY TKN_EQUAL TKN_NUMBER
{ {
current_part->chip_erase_delay = $3->value.number; current_part->chip_erase_delay = $3->value.number;
@ -1275,35 +1308,50 @@ part_parm :
*/ */
K_MEMORY TKN_STRING K_MEMORY TKN_STRING
{ { /* select memory for extension or create if not there */
current_mem = avr_new_memtype(); AVRMEM *mem = avr_locate_mem_noalias(current_part, $2->value.string);
if (current_mem == NULL) { if(!mem) {
if(!(mem = avr_new_memtype())) {
yyerror("could not create mem instance"); yyerror("could not create mem instance");
free_token($2); free_token($2);
YYABORT; YYABORT;
} }
strncpy(current_mem->desc, $2->value.string, AVR_MEMDESCLEN - 1); strncpy(mem->desc, $2->value.string, AVR_MEMDESCLEN - 1);
current_mem->desc[AVR_MEMDESCLEN-1] = 0; mem->desc[AVR_MEMDESCLEN-1] = 0;
ladd(current_part->mem, mem);
}
current_mem = mem;
free_token($2); free_token($2);
} }
mem_specs mem_specs
{ {
AVRMEM * existing_mem; if (is_alias) { // alias mem has been already entered
lrmv_d(current_part->mem, current_mem);
avr_free_mem(current_mem);
is_alias = false;
} else { // check all opcodes re necessary address bits
unsigned char cmd[4] = { 0, 0, 0, 0, };
int bn;
existing_mem = avr_locate_mem_noalias(current_part, current_mem->desc); for(int i=0; i<AVR_OP_MAX; i++)
if(current_mem && current_mem->op[i]) {
if((bn = avr_set_addr_mem(current_mem, i, cmd, 0UL)) > 0)
yywarning("%s's %s %s misses a necessary address bit a%d",
current_part->desc, current_mem->desc, opcodename(i), bn-1);
}
}
current_mem = NULL;
} |
K_MEMORY TKN_STRING TKN_EQUAL K_NULL
{
AVRMEM *existing_mem = avr_locate_mem_noalias(current_part, $2->value.string);
if (existing_mem != NULL) { if (existing_mem != NULL) {
lrmv_d(current_part->mem, existing_mem); lrmv_d(current_part->mem, existing_mem);
avr_free_mem(existing_mem); avr_free_mem(existing_mem);
} }
if (is_alias) { free_token($2);
avr_free_mem(current_mem); // alias mem has been already entered below
is_alias = false;
} else {
ladd(current_part->mem, current_mem);
}
current_mem = NULL; current_mem = NULL;
} | } |
opcode TKN_EQUAL string_list { opcode TKN_EQUAL string_list {
{ {
int opnum; int opnum;
@ -1317,13 +1365,27 @@ part_parm :
free_token($1); free_token($1);
YYABORT; YYABORT;
} }
if(0 != parse_cmdbits(op)) YYABORT; if(0 != parse_cmdbits(op, opnum))
YYABORT;
if (current_part->op[opnum] != NULL) { if (current_part->op[opnum] != NULL) {
/*yywarning("operation redefined");*/ /*yywarning("operation redefined");*/
avr_free_opcode(current_part->op[opnum]); avr_free_opcode(current_part->op[opnum]);
} }
current_part->op[opnum] = op; current_part->op[opnum] = op;
free_token($1);
}
} |
opcode TKN_EQUAL K_NULL {
{
int opnum = which_opcode($1);
if(opnum < 0)
YYABORT;
if(current_part->op[opnum] != NULL)
avr_free_opcode(current_part->op[opnum]);
current_part->op[opnum] = NULL;
free_token($1); free_token($1);
} }
} }
@ -1398,6 +1460,14 @@ mem_spec :
free_token($3); free_token($3);
} | } |
K_READBACK TKN_EQUAL TKN_NUMBER TKN_NUMBER
{
current_mem->readback[0] = $3->value.number;
current_mem->readback[1] = $4->value.number;
free_token($3);
free_token($4);
} |
K_READBACK_P1 TKN_EQUAL TKN_NUMBER K_READBACK_P1 TKN_EQUAL TKN_NUMBER
{ {
current_mem->readback[0] = $3->value.number; current_mem->readback[0] = $3->value.number;
@ -1455,13 +1525,27 @@ mem_spec :
free_token($1); free_token($1);
YYABORT; YYABORT;
} }
if(0 != parse_cmdbits(op)) YYABORT; if(0 != parse_cmdbits(op, opnum))
YYABORT;
if (current_mem->op[opnum] != NULL) { if (current_mem->op[opnum] != NULL) {
/*yywarning("operation redefined");*/ /*yywarning("operation redefined");*/
avr_free_opcode(current_mem->op[opnum]); avr_free_opcode(current_mem->op[opnum]);
} }
current_mem->op[opnum] = op; current_mem->op[opnum] = op;
free_token($1);
}
} |
opcode TKN_EQUAL K_NULL {
{
int opnum = which_opcode($1);
if(opnum < 0)
YYABORT;
if(current_mem->op[opnum] != NULL)
avr_free_opcode(current_mem->op[opnum]);
current_mem->op[opnum] = NULL;
free_token($1); free_token($1);
} }
} }
@ -1579,15 +1663,12 @@ static int which_opcode(TOKEN * opcode)
} }
static int parse_cmdbits(OPCODE * op) static int parse_cmdbits(OPCODE * op, int opnum)
{ {
TOKEN * t; TOKEN *t;
int bitno; int bitno;
char ch;
char * e;
char * q;
int len; int len;
char * s, *brkt = NULL; char *s, *brkt = NULL;
int rv = 0; int rv = 0;
bitno = 32; bitno = 32;
@ -1595,9 +1676,17 @@ static int parse_cmdbits(OPCODE * op)
t = lrmv_n(string_list, 1); t = lrmv_n(string_list, 1);
s = strtok_r(t->value.string, " ", &brkt); char *str = t->value.string;
// Compact alternative specification? (eg, "0100.0000--000x.xxxx--xxaa.aaaa--iiii.iiii")
char bit[2] = {0, 0}, *cc = str;
int compact = !strchr(str, ' ') && strlen(str) > 7;
bit[0] = *cc++;
s = !compact? strtok_r(str, " ", &brkt): *bit? bit: NULL;
while (rv == 0 && s != NULL) { while (rv == 0 && s != NULL) {
// Ignore visual grouping characters in compact mode
if(*s != '.' && *s != '-' && *s != '_' && *s !='/')
bitno--; bitno--;
if (bitno < 0) { if (bitno < 0) {
yyerror("too many opcode bits for instruction"); yyerror("too many opcode bits for instruction");
@ -1613,10 +1702,8 @@ static int parse_cmdbits(OPCODE * op)
break; break;
} }
ch = s[0];
if (len == 1) { if (len == 1) {
switch (ch) { switch (*s) {
case '1': case '1':
op->bit[bitno].type = AVR_CMDBIT_VALUE; op->bit[bitno].type = AVR_CMDBIT_VALUE;
op->bit[bitno].value = 1; op->bit[bitno].value = 1;
@ -1635,7 +1722,10 @@ static int parse_cmdbits(OPCODE * op)
case 'a': case 'a':
op->bit[bitno].type = AVR_CMDBIT_ADDRESS; op->bit[bitno].type = AVR_CMDBIT_ADDRESS;
op->bit[bitno].value = 0; op->bit[bitno].value = 0;
op->bit[bitno].bitno = 8*(bitno/8) + bitno % 8; op->bit[bitno].bitno = bitno < 8 || bitno > 23? 0:
opnum == AVR_OP_LOAD_EXT_ADDR? bitno+8: bitno-8; /* correct bit number for lone 'a' */
if(bitno < 8 || bitno > 23)
yywarning("address bits don't normally appear in Bytes 0 or 3 of SPI commands");
break; break;
case 'i': case 'i':
op->bit[bitno].type = AVR_CMDBIT_INPUT; op->bit[bitno].type = AVR_CMDBIT_INPUT;
@ -1647,21 +1737,40 @@ static int parse_cmdbits(OPCODE * op)
op->bit[bitno].value = 0; op->bit[bitno].value = 0;
op->bit[bitno].bitno = bitno % 8; op->bit[bitno].bitno = bitno % 8;
break; break;
case '.':
case '-':
case '_':
case '/':
break;
default : default :
yyerror("invalid bit specifier '%c'", ch); yyerror("invalid bit specifier '%c'", *s);
rv = -1; rv = -1;
break; break;
} }
} }
else { else {
if (ch == 'a') { if (*s == 'a') {
q = &s[1]; int sb, bn;
op->bit[bitno].bitno = strtol(q, &e, 0); char *e, *q;
if ((e == q)||(*e != 0)) {
yyerror("can't parse bit number from \"%s\"", q); q = s+1;
rv = -1; errno = 0;
break; bn = strtol(q, &e, 0); // address line
if (e == q || *e != 0 || errno) {
yywarning("can't parse bit number from a%s", q);
bn = 0;
} }
sb = opnum == AVR_OP_LOAD_EXT_ADDR? bitno+8: bitno-8; // should be this number
if(bitno < 8 || bitno > 23)
yywarning("address bits don't normally appear in Bytes 0 or 3 of SPI commands");
else if((bn & 31) != sb)
yywarning("a%d would normally be expected to be a%d", bn, sb);
else if(bn < 0 || bn > 31)
yywarning("invalid address bit a%d, using a%d", bn, bn & 31);
op->bit[bitno].bitno = bn & 31;
op->bit[bitno].type = AVR_CMDBIT_ADDRESS; op->bit[bitno].type = AVR_CMDBIT_ADDRESS;
op->bit[bitno].value = 0; op->bit[bitno].value = 0;
} }
@ -1672,12 +1781,16 @@ static int parse_cmdbits(OPCODE * op)
} }
} }
s = strtok_r(NULL, " ", &brkt); bit[0] = *cc++;
s = !compact? strtok_r(NULL, " ", &brkt): *bit? bit: NULL;
} /* while */ } /* while */
free_token(t); free_token(t);
} /* while */ } /* while */
if(bitno > 0)
yywarning("too few opcode bits in instruction");
return rv; return rv;
} }

917
src/developer_opts.c Normal file
View File

@ -0,0 +1,917 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2022, Stefan Rueger <smr@theblueorange.space>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* $Id$ */
/*
* Code to program an Atmel AVR device through one of the supported
* programmers.
*
* For parallel port connected programmers, the pin definitions can be
* changed via a config file. See the config file for instructions on
* how to add a programmer definition.
*
*/
#include "ac_cfg.h"
#include <stdio.h>
#include <stdlib.h>
#include <whereami.h>
#include <stdarg.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include "avrdude.h"
#include "libavrdude.h"
#include "developer_opts.h"
#include "developer_opts_private.h"
// return 0 if op code would encode (essentially) the same SPI command
static int opcodecmp(OPCODE *op1, OPCODE *op2, int opnum) {
char *opstr1, *opstr2, *p;
int cmp;
if(!op1 && !op2)
return 0;
if(!op1 || !op2)
return op1? -1: 1;
opstr1 = opcode2str(op1, opnum, 1);
opstr2 = opcode2str(op2, opnum, 1);
if(!opstr1 || !opstr2) {
dev_info("%s: out of memory\n", progname);
exit(1);
}
// don't care x and 0 are functionally equivalent
for(p=opstr1; *p; p++)
if(*p == 'x')
*p = '0';
for(p=opstr2; *p; p++)
if(*p == 'x')
*p = '0';
cmp = strcmp(opstr1, opstr2);
free(opstr1);
free(opstr2);
return cmp;
}
static void printopcode(AVRPART *p, const char *d, OPCODE *op, int opnum) {
unsigned char cmd[4];
int i;
if(op) {
memset(cmd, 0, sizeof cmd);
avr_set_bits(op, cmd);
dev_info(".op\t%s\t%s\t%s\t0x%02x%02x%02x%02x\t", p->desc, d, opcodename(opnum), cmd[0], cmd[1], cmd[2], cmd[3]);
for(i=31; i >= 0; i--) {
dev_info("%c", cmdbitchar(op->bit[i]));
if(i%8 == 0)
dev_info("%c", i? '\t': '\n');
}
}
}
static void printallopcodes(AVRPART *p, const char *d, OPCODE **opa) {
for(int i=0; i<AVR_OP_MAX; i++)
printopcode(p, d, opa[i], i);
}
// mnemonic characterisation of flags
static char *parttype(AVRPART *p) {
static char type[1024];
switch(p->flags & (AVRPART_HAS_PDI | AVRPART_AVR32 | AVRPART_HAS_TPI | AVRPART_HAS_UPDI)) {
case 0: strcpy(type, "ISP"); break;
case AVRPART_HAS_PDI: strcpy(type, "PDI"); break;
case AVRPART_AVR32: strcpy(type, "AVR32"); break;
case AVRPART_HAS_TPI: strcpy(type, "TPI"); break;
case AVRPART_HAS_UPDI: strcpy(type, "UPDI"); break;
default: strcpy(type, "UNKNOWN"); break;
}
if((p->flags & AVRPART_SERIALOK) == 0)
strcat(type, "|NOTSERIAL");
if((p->flags & AVRPART_PARALLELOK) == 0)
strcat(type, "|NOTPARALLEL");
if(p->flags & AVRPART_PSEUDOPARALLEL)
strcat(type, "|PSEUDOPARALLEL");
if(p->flags & AVRPART_IS_AT90S1200)
strcat(type, "|IS_AT90S1200");
if(p->flags & AVRPART_HAS_DW)
strcat(type, "|DW");
if(p->flags & AVRPART_HAS_JTAG)
strcat(type, "|JTAG");
if(p->flags & AVRPART_ALLOWFULLPAGEBITSTREAM)
strcat(type, "|PAGEBITSTREAM");
if((p->flags & AVRPART_ENABLEPAGEPROGRAMMING) == 0)
strcat(type, "|NOPAGEPROGRAMMING");
return type;
}
// check whether address bits are where they should be in ISP commands
static void checkaddr(int memsize, int pagesize, int opnum, OPCODE *op, AVRPART *p, AVRMEM *m) {
int i, lo, hi;
const char *opstr = opcodename(opnum);
lo = intlog2(pagesize);
hi = intlog2(memsize-1);
// address bits should be between positions lo and hi (and fall in line), outside should be 0 or don't care
for(i=0; i<16; i++) { // ISP programming only deals with 16-bit addresses (words for flash, bytes for eeprom)
if(i < lo || i > hi) {
if(op->bit[i+8].type != AVR_CMDBIT_IGNORE && !(op->bit[i+8].type == AVR_CMDBIT_VALUE && op->bit[i+8].value == 0)) {
char *cbs = cmdbitstr(op->bit[i+8]);
dev_info(".cmderr\t%s\t%s-%s\tbit %d outside addressable space should be x or 0 but is %s\n", p->desc, m->desc, opstr, i+8, cbs? cbs: "NULL");
if(cbs)
free(cbs);
}
} else {
if(op->bit[i+8].type != AVR_CMDBIT_ADDRESS)
dev_info(".cmderr\t%s\t%s-%s\tbit %d is %c but should be a\n", p->desc, m->desc, opstr, i+8, cmdbitchar(op->bit[i+8]));
else if(op->bit[i+8].bitno != i)
dev_info(".cmderr\t%s\t%s-%s\tbit %d inconsistent: a%d specified as a%d\n", p->desc, m->desc, opstr, i+8, i, op->bit[i+8].bitno);
}
}
for(i=0; i<32; i++) // command bits 8..23 should not contain address bits
if((i<8 || i>23) && op->bit[i].type == AVR_CMDBIT_ADDRESS)
dev_info(".cmderr\t%s\t%s-%s\tbit %d contains a%d which it shouldn't\n", p->desc, m->desc, opstr, i, op->bit[i].bitno);
}
static char *dev_sprintf(const char *fmt, ...) {
int size = 0;
char *p = NULL;
va_list ap;
// compute size
va_start(ap, fmt);
size = vsnprintf(p, size, fmt, ap);
va_end(ap);
if(size < 0)
return NULL;
size++; // for temrinating '\0'
if(!(p = malloc(size)))
return NULL;
va_start(ap, fmt);
size = vsnprintf(p, size, fmt, ap);
va_end(ap);
if(size < 0) {
free(p);
return NULL;
}
return p;
}
static int dev_nprinted;
int dev_message(int msglvl, const char *fmt, ...) {
va_list ap;
int rc = 0;
if(verbose >= msglvl) {
va_start(ap, fmt);
rc = vfprintf(stderr, fmt, ap);
va_end(ap);
if(rc > 0)
dev_nprinted += rc;
}
return rc;
}
static int dev_part_strct_entry(bool tsv, char *col0, char *col1, char *col2, const char *name, char *cont) {
const char *n = name? name: "name_error";
const char *c = cont? cont: "cont_error";
if(tsv) { // tab separated values
if(col0) {
dev_info("%s\t", col0);
if(col1) {
dev_info("%s\t", col1);
if(col2) {
dev_info("%s\t", col2);
}
}
}
dev_info("%s\t%s\n", n, c);
} else { // grammar conform
int indent = col2 && strcmp(col2, "part");
printf("%*s%-*s = %s;\n", indent? 8: 4, "", indent? 15: 19, n, c);
}
if(cont)
free(cont);
return 1;
}
static const char *dev_controlstack_name(AVRPART *p) {
return
p->ctl_stack_type == CTL_STACK_PP? "pp_controlstack":
p->ctl_stack_type == CTL_STACK_HVSP? "hvsp_controlstack":
p->ctl_stack_type == CTL_STACK_NONE? "NULL":
"unknown_controlstack";
}
static void dev_stack_out(bool tsv, AVRPART *p, const char *name, unsigned char *stack, int ns) {
if(!strcmp(name, "NULL")) {
name = "pp_controlstack";
ns = 0;
}
if(tsv)
dev_info(".pt\t%s\t%s\t", p->desc, name);
else
dev_info(" %-19s =%s", name, ns <=8? " ": "");
if(ns <= 0)
dev_info(tsv? "NULL\n": "NULL;\n");
else
for(int i=0; i<ns; i++)
dev_info("%s0x%02x%s", !tsv && ns > 8 && i%8 == 0? "\n ": "", stack[i], i+1<ns? ", ": tsv? "\n": ";\n");
}
// order in which memories are processed, runtime adds unknown ones (but there shouldn't be any)
static const char *mem_order[100] = {
"eeprom", "flash", "application", "apptable",
"boot", "lfuse", "hfuse", "efuse",
"fuse", "fuse0", "wdtcfg", "fuse1",
"bodcfg", "fuse2", "osccfg", "fuse3",
"fuse4", "tcd0cfg", "fuse5", "syscfg0",
"fuse6", "syscfg1", "fuse7", "append",
"codesize", "fuse8", "fuse9", "bootend",
"bootsize", "fuses", "lock", "lockbits",
"tempsense", "signature", "prodsig", "sernum",
"calibration", "osccal16", "osccal20", "osc16err",
"osc20err", "usersig", "userrow", "data",
};
static void add_mem_order(const char *str) {
for(size_t i=0; i < sizeof mem_order/sizeof *mem_order; i++) {
if(mem_order[i] && !strcmp(mem_order[i], str))
return;
if(!mem_order[i]) {
mem_order[i] = strdup(str);
return;
}
}
dev_info("%s: mem_order[] under-dimensioned in developer_opts.c; increase and recompile\n", progname);
exit(1);
}
static int intcmp(int a, int b) {
return a-b;
}
// deep copies for comparison and raw output
typedef struct {
AVRMEM base;
OPCODE ops[AVR_OP_MAX];
} AVRMEMdeep;
static int avrmem_deep_copy(AVRMEMdeep *d, AVRMEM *m) {
size_t len;
d->base = *m;
// zap all bytes beyond terminating nul of desc array
len = strlen(m->desc)+1;
if(len < sizeof m->desc)
memset(d->base.desc + len, 0, sizeof m->desc - len);
// zap address values
d->base.buf = NULL;
d->base.tags = NULL;
for(int i=0; i<AVR_OP_MAX; i++)
d->base.op[i] = NULL;
// copy over the SPI operations themselves
memset(d->base.op, 0, sizeof d->base.op);
memset(d->ops, 0, sizeof d->ops);
for(size_t i=0; i<sizeof d->ops/sizeof *d->ops; i++)
if(m->op[i])
d->ops[i] = *m->op[i];
return 0;
}
static int memorycmp(AVRMEM *m1, AVRMEM *m2) {
AVRMEMdeep dm1, dm2;
if(!m1 && !m2)
return 0;
if(!m1 || !m2)
return m1? -1: 1;
avrmem_deep_copy(&dm1, m1);
avrmem_deep_copy(&dm2, m2);
return memcmp(&dm1, &dm2, sizeof dm1);
}
typedef struct {
AVRPART base;
OPCODE ops[AVR_OP_MAX];
AVRMEMdeep mems[40];
} AVRPARTdeep;
static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) {
AVRMEM *m;
size_t len, di;
memset(d, 0, sizeof *d);
d->base = *p;
d->base.parent_id = NULL;
d->base.config_file = NULL;
d->base.lineno = 0;
// zap all bytes beyond terminating nul of desc, id and family_id array
len = strlen(p->desc);
if(len < sizeof p->desc)
memset(d->base.desc + len, 0, sizeof p->desc - len);
len = strlen(p->family_id);
if(len < sizeof p->family_id)
memset(d->base.family_id + len, 0, sizeof p->family_id - len);
len = strlen(p->id);
if(len < sizeof p->id)
memset(d->base.id + len, 0, sizeof p->id - len);
// zap address values
d->base.mem = NULL;
d->base.mem_alias = NULL;
for(int i=0; i<AVR_OP_MAX; i++)
d->base.op[i] = NULL;
// copy over the SPI operations
memset(d->base.op, 0, sizeof d->base.op);
memset(d->ops, 0, sizeof d->ops);
for(int i=0; i<AVR_OP_MAX; i++)
if(p->op[i])
d->ops[i] = *p->op[i];
// fill in all memories we got in defined order
di = 0;
for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) {
m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL;
if(m) {
if(di >= sizeof d->mems/sizeof *d->mems) {
avrdude_message(MSG_INFO, "%s: ran out of mems[] space, increase size in AVRMEMdeep of developer_opts.c and recompile\n", progname);
exit(1);
}
avrmem_deep_copy(d->mems+di, m);
di++;
}
}
return di;
}
static char txtchar(unsigned char in) {
in &= 0x7f;
return in == ' '? '_': in > ' ' && in < 0x7f? in: '.';
}
static void dev_raw_dump(unsigned char *p, int nbytes, const char *name, const char *sub, int idx) {
unsigned char *end = p+nbytes;
int n = ((end - p) + 15)/16;
for(int i=0; i<n; i++, p += 16) {
dev_info("%s\t%s\t%02x%04x: ", name, sub, idx, i*16);
for(int j=0; j<16; j++)
dev_info("%02x", p+i*16+j<end? p[i*16+j]: 0);
dev_info(" ");
for(int j=0; j<16; j++)
dev_info("%c", txtchar(p+i*16+j<end? p[i*16+j]: 0));
dev_info("\n");
}
}
static void dev_part_raw(AVRPART *part) {
AVRPARTdeep dp;
int di = avrpart_deep_copy(&dp, part);
dev_raw_dump((unsigned char *) &dp.base, sizeof dp.base, part->desc, "part", 0);
dev_raw_dump((unsigned char *) &dp.ops, sizeof dp.ops, part->desc, "ops", 1);
for(int i=0; i<di; i++)
dev_raw_dump((unsigned char *) (dp.mems+i), sizeof dp.mems[i], part->desc, dp.mems[i].base.desc, i+2);
}
static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) {
if(!tsv) {
dev_info("#------------------------------------------------------------\n");
dev_info("# %s\n", p->desc);
dev_info("#------------------------------------------------------------\n");
if(p->parent_id)
dev_info("\npart parent \"%s\"\n", p->parent_id);
else
dev_info("\npart\n");
}
_if_partout(strcmp, "\"%s\"", desc);
_if_partout(strcmp, "\"%s\"", id);
_if_partout(strcmp, "\"%s\"", family_id);
_if_partout(intcmp, "%d", hvupdi_variant);
_if_partout(intcmp, "0x%02x", stk500_devcode);
_if_partout(intcmp, "0x%02x", avr910_devcode);
_if_partout(intcmp, "%d", chip_erase_delay);
_if_partout(intcmp, "0x%02x", pagel);
_if_partout(intcmp, "0x%02x", bs2);
_if_n_partout_str(memcmp, sizeof p->signature, dev_sprintf("0x%02x 0x%02x 0x%02x", p->signature[0], p->signature[1], p->signature[2]), signature);
_if_partout(intcmp, "0x%04x", usbpid);
if(!base || base->reset_disposition != p->reset_disposition)
_partout_str(strdup(p->reset_disposition == RESET_DEDICATED? "dedicated": p->reset_disposition == RESET_IO? "io": "unknown"), reset);
_if_partout_str(intcmp, strdup(p->retry_pulse == PIN_AVR_RESET? "reset": p->retry_pulse == PIN_AVR_SCK? "sck": "unknown"), retry_pulse);
if(!base || base->flags != p->flags) {
if(tsv) {
_partout("0x%04x", flags);
} else {
_if_flagout(AVRPART_HAS_JTAG, has_jtag);
_if_flagout(AVRPART_HAS_DW, has_debugwire);
_if_flagout(AVRPART_HAS_PDI, has_pdi);
_if_flagout(AVRPART_HAS_UPDI, has_updi);
_if_flagout(AVRPART_HAS_TPI, has_tpi);
_if_flagout(AVRPART_IS_AT90S1200, is_at90s1200);
_if_flagout(AVRPART_AVR32, is_avr32);
_if_flagout(AVRPART_ALLOWFULLPAGEBITSTREAM, allowfullpagebitstream);
_if_flagout(AVRPART_ENABLEPAGEPROGRAMMING, enablepageprogramming);
_if_flagout(AVRPART_SERIALOK, serial);
if(!base || (base->flags & (AVRPART_PARALLELOK | AVRPART_PSEUDOPARALLEL)) != (p->flags & (AVRPART_PARALLELOK | AVRPART_PSEUDOPARALLEL))) {
int par = p->flags & (AVRPART_PARALLELOK | AVRPART_PSEUDOPARALLEL);
_partout_str(strdup(par == 0? "no": par == AVRPART_PSEUDOPARALLEL? "unknown": AVRPART_PARALLELOK? "yes": "pseudo"), parallel);
}
}
}
_if_partout(intcmp, "%d", timeout);
_if_partout(intcmp, "%d", stabdelay);
_if_partout(intcmp, "%d", cmdexedelay);
_if_partout(intcmp, "%d", synchloops);
_if_partout(intcmp, "%d", bytedelay);
_if_partout(intcmp, "%d", pollindex);
_if_partout(intcmp, "0x%02x", pollvalue);
_if_partout(intcmp, "%d", predelay);
_if_partout(intcmp, "%d", postdelay);
_if_partout(intcmp, "%d", pollmethod);
if(!base && p->ctl_stack_type != CTL_STACK_NONE)
dev_stack_out(tsv, p, dev_controlstack_name(p), p->controlstack, CTL_STACK_SIZE);
// @@@ may need to remove controlstack and set p->ctl_stack_type to CTL_STACK_NONE if base has controlstack?
if(base && (p->ctl_stack_type != base->ctl_stack_type || memcmp(base->controlstack, p->controlstack, sizeof base->controlstack)))
dev_stack_out(tsv, p, dev_controlstack_name(p), p->controlstack, CTL_STACK_SIZE);
if(!base || memcmp(base->flash_instr, p->flash_instr, sizeof base->flash_instr))
dev_stack_out(tsv, p, "flash_instr", p->flash_instr, FLASH_INSTR_SIZE);
if(!base || memcmp(base->eeprom_instr, p->eeprom_instr, sizeof base->eeprom_instr))
dev_stack_out(tsv, p, "eeprom_instr", p->eeprom_instr, EEPROM_INSTR_SIZE);
_if_partout(intcmp, "%d", hventerstabdelay);
_if_partout(intcmp, "%d", progmodedelay);
_if_partout(intcmp, "%d", latchcycles);
_if_partout(intcmp, "%d", togglevtg);
_if_partout(intcmp, "%d", poweroffdelay);
_if_partout(intcmp, "%d", resetdelayms);
_if_partout(intcmp, "%d", resetdelayus);
_if_partout(intcmp, "%d", hvleavestabdelay);
_if_partout(intcmp, "%d", resetdelay);
_if_partout(intcmp, "%d", chiperasepulsewidth);
_if_partout(intcmp, "%d", chiperasepolltimeout);
_if_partout(intcmp, "%d", chiperasetime);
_if_partout(intcmp, "%d", programfusepulsewidth);
_if_partout(intcmp, "%d", programfusepolltimeout);
_if_partout(intcmp, "%d", programlockpulsewidth);
_if_partout(intcmp, "%d", programlockpolltimeout);
_if_partout(intcmp, "%d", synchcycles);
_if_partout(intcmp, "%d", hvspcmdexedelay);
_if_partout(intcmp, "0x%02x", idr);
_if_partout(intcmp, "0x%02x", rampz);
_if_partout(intcmp, "0x%02x", spmcr);
_if_partout(intcmp, "0x%02x", eecr); // why is eecr an unsigned short?
_if_partout(intcmp, "0x%04x", mcu_base);
_if_partout(intcmp, "0x%04x", nvm_base);
_if_partout(intcmp, "0x%04x", ocd_base);
_if_partout(intcmp, "%d", ocdrev);
for(int i=0; i < AVR_OP_MAX; i++)
if(!base || opcodecmp(p->op[i], base->op[i], i))
dev_part_strct_entry(tsv, ".ptop", p->desc, "part", opcodename(i), opcode2str(p->op[i], i, !tsv));
for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) {
AVRMEM *m, *bm;
m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL;
bm = base && base->mem? avr_locate_mem(base, mem_order[mi]): NULL;
if(!m && bm && !tsv)
dev_info("\n memory \"%s\" = NULL;\n", bm->desc);
if(!m)
continue;
if(base && !bm)
bm = avr_new_memtype();
if(!tsv) {
if(!memorycmp(bm, m)) // same memory bit for bit, no need to instantiate
continue;
dev_info("\n memory \"%s\"\n", m->desc);
}
_if_memout_yn(paged);
_if_memout(intcmp, m->size > 8192? "0x%x": "%d", size);
_if_memout(intcmp, "%d", page_size);
_if_memout(intcmp, "%d", num_pages); // why can AVRDUDE not compute this?
_if_memout(intcmp, "0x%x", offset);
_if_memout(intcmp, "%d", min_write_delay);
_if_memout(intcmp, "%d", max_write_delay);
_if_memout_yn(pwroff_after_write);
_if_n_memout_str(memcmp, 2, dev_sprintf("0x%02x 0x%02x", m->readback[0], m->readback[1]), readback);
_if_memout(intcmp, "%d", mode);
_if_memout(intcmp, "%d", delay);
_if_memout(intcmp, "%d", blocksize);
_if_memout(intcmp, "%d", readsize);
_if_memout(intcmp, "%d", pollindex);
for(int i=0; i < AVR_OP_MAX; i++)
if(!bm || opcodecmp(bm->op[i], m->op[i], i))
dev_part_strct_entry(tsv, ".ptmmop", p->desc, m->desc, opcodename(i), opcode2str(m->op[i], i, !tsv));
if(!tsv)
dev_info(" ;\n");
for(LNODEID lnm=lfirst(p->mem_alias); lnm; lnm=lnext(lnm)) {
AVRMEM_ALIAS *ma = ldata(lnm);
if(ma->aliased_mem && !strcmp(ma->aliased_mem->desc, m->desc)) {
if(tsv)
dev_info(".ptmm\t%s\t%s\talias\t%s\n", p->desc, ma->desc, m->desc);
else
dev_info("\n memory \"%s\"\n alias \"%s\";\n ;\n", ma->desc, m->desc);
}
}
}
if(!tsv)
dev_info(";\n");
}
// -p */[cdosw*]
void dev_output_part_defs(char *partdesc) {
bool cmdok, waits, opspi, descs, strct, cmpst, raw, all, tsv;
char *flags;
int nprinted;
AVRPART *nullpart = avr_new_part();
if((flags = strchr(partdesc, '/')))
*flags++ = 0;
if(!flags && !strcmp(partdesc, "*")) // treat -p * as if it was -p */*
flags = "*";
if(!*flags || !strchr("cdosSrw*t", *flags)) {
dev_info("%s: flags for developer option -p <wildcard>/<flags> not recognised\n", progname);
dev_info(
"Wildcard examples (these need protecting in the shell through quoting):\n"
" * all known parts\n"
" ATtiny10 just this part\n"
" *32[0-9] matches ATmega329, ATmega325 and ATmega328\n"
" *32? matches ATmega329, ATmega32A, ATmega325 and ATmega328\n"
"Flags (one or more of the characters below):\n"
" c check and report errors in address bits of SPI commands\n"
" d description of core part features\n"
" o opcodes for SPI programming parts and memories\n"
" S show entries of avrdude.conf parts with all values\n"
" s show entries of avrdude.conf parts with necessary values\n"
" r show entries of avrdude.conf parts as raw dump\n"
" w wd_... constants for ISP parts\n"
" * all of the above except s\n"
" t use tab separated values as much as possible\n"
"Examples:\n"
" $ avrdude -p ATmega328P/s\n"
" $ avrdude -p m328*/st | grep chip_erase_delay\n"
" avrdude -p*/r | sort\n"
"Notes:\n"
" -p * is the same as -p */*\n"
" This help message is printed using any unrecognised flag, eg, -p/h\n"
" Leaving no space after -p can be an OK substitute for quoting in shells\n"
" /s and /S outputs are designed to be used as input in avrdude.conf\n"
" Sorted /r output should stay invariant when rearranging avrdude.conf\n"
" The /c, /o and /w flags are less generic and may be removed sometime\n"
" These options are just to help development, so not further documented\n"
);
return;
}
// redirect stderr to stdout
fflush(stderr); fflush(stdout); dup2(1, 2);
all = *flags == '*';
cmdok = all || !!strchr(flags, 'c');
descs = all || !!strchr(flags, 'd');
opspi = all || !!strchr(flags, 'o');
waits = all || !!strchr(flags, 'w');
strct = all || !!strchr(flags, 'S');
raw = all || !!strchr(flags, 'r');
cmpst = !!strchr(flags, 's');
tsv = !!strchr(flags, 't');
// go through all memories and add them to the memory order list
for(LNODEID ln1 = lfirst(part_list); ln1; ln1 = lnext(ln1)) {
AVRPART *p = ldata(ln1);
if(p->mem)
for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm))
add_mem_order(((AVRMEM *) ldata(lnm))->desc);
// same for aliased memories (though probably not needed)
if(p->mem_alias)
for(LNODEID lnm=lfirst(p->mem_alias); lnm; lnm=lnext(lnm))
add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc);
}
nprinted = dev_nprinted;
for(LNODEID ln1 = lfirst(part_list); ln1; ln1 = lnext(ln1)) {
AVRPART *p = ldata(ln1);
int flashsize, flashoffset, flashpagesize, eepromsize , eepromoffset, eeprompagesize;
if(!descs || tsv)
if(dev_nprinted > nprinted) {
dev_info("\n");
nprinted = dev_nprinted;
}
if(!part_match(partdesc, p->desc) && !part_match(partdesc, p->id))
continue;
if(strct || cmpst)
dev_part_strct(p, tsv, !cmpst? NULL: p->parent_id? locate_part(part_list, p->parent_id): nullpart);
if(raw)
dev_part_raw(p);
// identify core flash and eeprom parameters
flashsize = flashoffset = flashpagesize = eepromsize = eepromoffset = eeprompagesize = 0;
if(p->mem) {
for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm)) {
AVRMEM *m = ldata(lnm);
if(!flashsize && 0==strcmp(m->desc, "flash")) {
flashsize = m->size;
flashpagesize = m->page_size;
flashoffset = m->offset;
}
if(!eepromsize && 0==strcmp(m->desc, "eeprom")) {
eepromsize = m->size;
eepromoffset = m->offset;
eeprompagesize = m->page_size;
}
}
}
// "real" entries don't seem to have a space in their desc (a bit hackey)
if(flashsize && !strchr(p->desc, ' ')) {
int ok, nfuses;
AVRMEM *m;
OPCODE *oc;
ok = 2047;
nfuses = 0;
if(!p->op[AVR_OP_PGM_ENABLE])
ok &= ~DEV_SPI_EN_CE_SIG;
if(!p->op[AVR_OP_CHIP_ERASE])
ok &= ~DEV_SPI_EN_CE_SIG;
if((m = avr_locate_mem(p, "flash"))) {
if((oc = m->op[AVR_OP_LOAD_EXT_ADDR])) {
// @@@ to do: check whether address is put at lsb of third byte
} else
ok &= ~DEV_SPI_LOAD_EXT_ADDR;
if((oc = m->op[AVR_OP_READ_HI])) {
if(cmdok)
checkaddr(m->size>>1, 1, AVR_OP_READ_HI, oc, p, m);
} else
ok &= ~DEV_SPI_PROGMEM;
if((oc = m->op[AVR_OP_READ_LO])) {
if(cmdok)
checkaddr(m->size>>1, 1, AVR_OP_READ_LO, oc, p, m);
} else
ok &= ~DEV_SPI_PROGMEM;
if((oc = m->op[AVR_OP_WRITE_HI])) {
if(cmdok)
checkaddr(m->size>>1, 1, AVR_OP_WRITE_HI, oc, p, m);
} else
ok &= ~DEV_SPI_PROGMEM;
if((oc = m->op[AVR_OP_WRITE_LO])) {
if(cmdok)
checkaddr(m->size>>1, 1, AVR_OP_WRITE_LO, oc, p, m);
} else
ok &= ~DEV_SPI_PROGMEM;
if((oc = m->op[AVR_OP_LOADPAGE_HI])) {
if(cmdok)
checkaddr(m->page_size>>1, 1, AVR_OP_LOADPAGE_HI, oc, p, m);
} else
ok &= ~DEV_SPI_PROGMEM_PAGED;
if((oc = m->op[AVR_OP_LOADPAGE_LO])) {
if(cmdok)
checkaddr(m->page_size>>1, 1, AVR_OP_LOADPAGE_LO, oc, p, m);
} else
ok &= ~DEV_SPI_PROGMEM_PAGED;
if((oc = m->op[AVR_OP_WRITEPAGE])) {
if(cmdok)
checkaddr(m->size>>1, m->page_size>>1, AVR_OP_WRITEPAGE, oc, p, m);
} else
ok &= ~DEV_SPI_PROGMEM_PAGED;
} else
ok &= ~(DEV_SPI_PROGMEM_PAGED | DEV_SPI_PROGMEM);
if((m = avr_locate_mem(p, "eeprom"))) {
if((oc = m->op[AVR_OP_READ])) {
if(cmdok)
checkaddr(m->size, 1, AVR_OP_READ, oc, p, m);
} else
ok &= ~DEV_SPI_EEPROM;
if((oc = m->op[AVR_OP_WRITE])) {
if(cmdok)
checkaddr(m->size, 1, AVR_OP_WRITE, oc, p, m);
} else
ok &= ~DEV_SPI_EEPROM;
if((oc = m->op[AVR_OP_LOADPAGE_LO])) {
if(cmdok)
checkaddr(m->page_size, 1, AVR_OP_LOADPAGE_LO, oc, p, m);
} else
ok &= ~DEV_SPI_EEPROM_PAGED;
if((oc = m->op[AVR_OP_WRITEPAGE])) {
if(cmdok)
checkaddr(m->size, m->page_size, AVR_OP_WRITEPAGE, oc, p, m);
} else
ok &= ~DEV_SPI_EEPROM_PAGED;
} else
ok &= ~(DEV_SPI_EEPROM_PAGED | DEV_SPI_EEPROM);
if((m = avr_locate_mem(p, "signature")) && (oc = m->op[AVR_OP_READ])) {
if(cmdok)
checkaddr(m->size, 1, AVR_OP_READ, oc, p, m);
} else
ok &= ~DEV_SPI_EN_CE_SIG;
if((m = avr_locate_mem(p, "calibration")) && (oc = m->op[AVR_OP_READ])) {
if(cmdok)
checkaddr(m->size, 1, AVR_OP_READ, oc, p, m);
} else
ok &= ~DEV_SPI_CALIBRATION;
// actually, some AT90S... parts cannot read, only write lock bits :-0
if( ! ((m = avr_locate_mem(p, "lock")) && m->op[AVR_OP_WRITE]))
ok &= ~DEV_SPI_LOCK;
if(((m = avr_locate_mem(p, "fuse")) || (m = avr_locate_mem(p, "lfuse"))) && m->op[AVR_OP_READ] && m->op[AVR_OP_WRITE])
nfuses++;
else
ok &= ~DEV_SPI_LFUSE;
if((m = avr_locate_mem(p, "hfuse")) && m->op[AVR_OP_READ] && m->op[AVR_OP_WRITE])
nfuses++;
else
ok &= ~DEV_SPI_HFUSE;
if((m = avr_locate_mem(p, "efuse")) && m->op[AVR_OP_READ] && m->op[AVR_OP_WRITE])
nfuses++;
else
ok &= ~DEV_SPI_EFUSE;
if(descs) {
int len = 16-strlen(p->desc);
dev_info("%s '%s' =>%*s [0x%02X, 0x%02X, 0x%02X, 0x%08x, 0x%05x, 0x%03x, 0x%06x, 0x%04x, 0x%03x, %d, 0x%03x, 0x%04x, '%s'], # %s %d\n",
tsv || all? ".desc": " ",
p->desc, len > 0? len: 0, "",
p->signature[0], p->signature[1], p->signature[2],
flashoffset, flashsize, flashpagesize,
eepromoffset, eepromsize, eeprompagesize,
nfuses,
ok,
p->flags,
parttype(p),
p->config_file, p->lineno
);
}
}
if(opspi) {
printallopcodes(p, "part", p->op);
if(p->mem) {
for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm)) {
AVRMEM *m = ldata(lnm);
if(m)
printallopcodes(p, m->desc, m->op);
}
}
}
// print wait delays for AVR family parts
if(waits) {
if(!(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI | AVRPART_HAS_TPI | AVRPART_AVR32)))
dev_info(".wd_chip_erase %.3f ms %s\n", p->chip_erase_delay/1000.0, p->desc);
if(p->mem) {
for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm)) {
AVRMEM *m = ldata(lnm);
// write delays not needed for read-only calibration and signature memories
if(strcmp(m->desc, "calibration") && strcmp(m->desc, "signature")) {
if(!(p->flags & (AVRPART_HAS_PDI | AVRPART_HAS_UPDI | AVRPART_HAS_TPI | AVRPART_AVR32))) {
if(m->min_write_delay == m->max_write_delay)
dev_info(".wd_%s %.3f ms %s\n", m->desc, m->min_write_delay/1000.0, p->desc);
else {
dev_info(".wd_min_%s %.3f ms %s\n", m->desc, m->min_write_delay/1000.0, p->desc);
dev_info(".wd_max_%s %.3f ms %s\n", m->desc, m->max_write_delay/1000.0, p->desc);
}
}
}
}
}
}
}
}

24
src/developer_opts.h Normal file
View File

@ -0,0 +1,24 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2022, Stefan Rueger <smr@theblueorange.space>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef developer_opts_h
#define developer_opts_h
void dev_output_part_defs(char *partdesc);
#endif

View File

@ -0,0 +1,116 @@
/*
* avrdude - A Downloader/Uploader for AVR device programmers
* Copyright (C) 2022, Stefan Rueger <smr@theblueorange.space>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef developer_opts_private_h
#define developer_opts_private_h
#define DEV_SPI_EN_CE_SIG 1
#define DEV_SPI_PROGMEM 2
#define DEV_SPI_PROGMEM_PAGED 4
#define DEV_SPI_LOAD_EXT_ADDR 8
#define DEV_SPI_EEPROM 16
#define DEV_SPI_EEPROM_PAGED 32
#define DEV_SPI_LOCK 64
#define DEV_SPI_CALIBRATION 128
#define DEV_SPI_LFUSE 256
#define DEV_SPI_HFUSE 512
#define DEV_SPI_EFUSE 1024
static int dev_message(int msglvl, const char *fmt, ...);
#ifndef DEV_INFO
#define DEV_INFO MSG_INFO
#endif
#ifndef DEV_NOTICE
#define DEV_NOTICE MSG_NOTICE
#endif
#ifndef DEV_NOTICE
#define DEV_NOTICE2 MSG_NOTICE2
#endif
#define dev_info(...) dev_message(DEV_INFO, __VA_ARGS__)
#define dev_notice(...) dev_message(DEV_NOTICE, __VA_ARGS__)
#define dev_notice2(...) dev_message(DEV_NOTICE2, __VA_ARGS__)
#define _partout(fmt, component) \
dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component))
#define _if_partout(cmp, fmt, component) do { \
if(!base || cmp(base->component, p->component)) \
dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component)); \
} while(0)
#define _if_n_partout(cmp, n, fmt, component) do { \
if(!base || cmp(base->component, p->component, n)) \
dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, dev_sprintf(fmt, p->component)); \
} while(0)
#define _partout_str(result, component) \
dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result)
#define _if_partout_str(cmp, result, component) do { \
if(!base || cmp(base->component, p->component)) \
dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result); \
} while(0)
#define _if_n_partout_str(cmp, n, result, component) do { \
if(!base || cmp(base->component, p->component, n)) \
dev_part_strct_entry(tsv, ".pt", p->desc, NULL, #component, result); \
} while(0)
#define _memout(fmt, component) \
dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, dev_sprintf(fmt, m->component))
#define _if_memout(cmp, fmt, component) do { \
if(!bm || cmp(bm->component, m->component)) \
dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, dev_sprintf(fmt, m->component)); \
} while(0)
#define _memout_str(result, component) \
dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result)
#define _if_n_memout_str(cmp, n, result, component) do { \
if(!bm || cmp(bm->component, m->component, n)) \
dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, result); \
} while(0)
#define _memout_yn(component) \
dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, strdup(m->component? "yes": "no"))
#define _if_memout_yn(component) do { \
if(!bm || bm->component != m->component) \
dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, #component, strdup(m->component? "yes": "no")); \
} while(0)
#define _flagout(mask, name) \
_partout_str(strdup(p->flags & (mask)? "yes": "no"), name)
#define _if_flagout(mask, name) do { \
if(!base || (base->flags & (mask)) != (p->flags & (mask))) \
_partout_str(strdup(p->flags & (mask)? "yes": "no"), name); \
} while(0)
#define _cmderr(result, component) \
dev_part_strct_entry(tsv, ".cmderr", p->desc, m->desc, #component, result)
#endif

View File

@ -57,12 +57,9 @@ SIGN [+-]
%% %%
#{SIGN}*{DIGIT}+ { yylval = number(yytext); return TKN_NUMBER; } {SIGN}?{DIGIT}+ { yylval = number(yytext); return TKN_NUMBER; }
#{SIGN}*{DIGIT}+"."{DIGIT}* { yylval = number_real(yytext); return TKN_NUMBER_REAL; } {SIGN}?{DIGIT}+"."{DIGIT}* { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
#{SIGN}*"."{DIGIT}* { yylval = number_real(yytext); return TKN_NUMBER_REAL; } {SIGN}?"."{DIGIT}+ { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
{DIGIT}+ { yylval = number(yytext); return TKN_NUMBER; }
{DIGIT}+"."{DIGIT}* { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
"."{DIGIT}+ { yylval = number_real(yytext); return TKN_NUMBER_REAL; }
"\"" { string_buf_ptr = string_buf; BEGIN(strng); } "\"" { string_buf_ptr = string_buf; BEGIN(strng); }
@ -179,6 +176,7 @@ miso { yylval=NULL; return K_MISO; }
mode { yylval=NULL; return K_MODE; } mode { yylval=NULL; return K_MODE; }
mosi { yylval=NULL; return K_MOSI; } mosi { yylval=NULL; return K_MOSI; }
no { yylval=new_token(K_NO); return K_NO; } no { yylval=new_token(K_NO); return K_NO; }
NULL { yylval=NULL; return K_NULL; }
num_banks { yylval=NULL; return K_NUM_PAGES; } num_banks { yylval=NULL; return K_NUM_PAGES; }
num_pages { yylval=NULL; return K_NUM_PAGES; } num_pages { yylval=NULL; return K_NUM_PAGES; }
nvm_base { yylval=NULL; return K_NVM_BASE; } nvm_base { yylval=NULL; return K_NVM_BASE; }
@ -213,6 +211,7 @@ rdyled { yylval=NULL; return K_RDYLED; }
read { yylval=new_token(K_READ); return K_READ; } read { yylval=new_token(K_READ); return K_READ; }
read_hi { yylval=new_token(K_READ_HI); return K_READ_HI; } read_hi { yylval=new_token(K_READ_HI); return K_READ_HI; }
read_lo { yylval=new_token(K_READ_LO); return K_READ_LO; } read_lo { yylval=new_token(K_READ_LO); return K_READ_LO; }
readback { yylval=NULL; return K_READBACK; }
readback_p1 { yylval=NULL; return K_READBACK_P1; } readback_p1 { yylval=NULL; return K_READBACK_P1; }
readback_p2 { yylval=NULL; return K_READBACK_P2; } readback_p2 { yylval=NULL; return K_READBACK_P2; }
readsize { yylval=NULL; return K_READSIZE; } readsize { yylval=NULL; return K_READSIZE; }

View File

@ -183,6 +183,7 @@ typedef struct opcode {
} OPCODE; } OPCODE;
/* Any changes here, please also reflect in dev_part_strct() of developer_opts.c */
#define AVRPART_SERIALOK 0x0001 /* part supports serial programming */ #define AVRPART_SERIALOK 0x0001 /* part supports serial programming */
#define AVRPART_PARALLELOK 0x0002 /* part supports parallel programming */ #define AVRPART_PARALLELOK 0x0002 /* part supports parallel programming */
#define AVRPART_PSEUDOPARALLEL 0x0004 /* part has pseudo parallel support */ #define AVRPART_PSEUDOPARALLEL 0x0004 /* part has pseudo parallel support */
@ -212,9 +213,11 @@ typedef struct opcode {
#define TAG_ALLOCATED 1 /* memory byte is allocated */ #define TAG_ALLOCATED 1 /* memory byte is allocated */
/* Any changes here, please also reflect in dev_part_strct() of developer_opts.c */
typedef struct avrpart { typedef struct avrpart {
char desc[AVR_DESCLEN]; /* long part name */ char desc[AVR_DESCLEN]; /* long part name */
char id[AVR_IDLEN]; /* short part name */ char id[AVR_IDLEN]; /* short part name */
char * parent_id; /* parent id if set, for -p.../s */
char family_id[AVR_FAMILYIDLEN+1]; /* family id in the SIB (avr8x) */ char family_id[AVR_FAMILYIDLEN+1]; /* family id in the SIB (avr8x) */
int hvupdi_variant; /* HV pulse on UPDI pin, no pin or RESET pin */ int hvupdi_variant; /* HV pulse on UPDI pin, no pin or RESET pin */
int stk500_devcode; /* stk500 device code */ int stk500_devcode; /* stk500 device code */
@ -317,14 +320,22 @@ typedef struct avrmem_alias {
extern "C" { extern "C" {
#endif #endif
int intlog2(unsigned int n);
/* Functions for OPCODE structures */ /* Functions for OPCODE structures */
OPCODE * avr_new_opcode(void); OPCODE * avr_new_opcode(void);
void avr_free_opcode(OPCODE * op); void avr_free_opcode(OPCODE * op);
int avr_set_bits(OPCODE * op, unsigned char * cmd); int avr_set_bits(OPCODE * op, unsigned char * cmd);
int avr_set_addr(OPCODE * op, unsigned char * cmd, unsigned long addr); int avr_set_addr(OPCODE * op, unsigned char * cmd, unsigned long addr);
int avr_set_addr_mem(AVRMEM *mem, int opnum, unsigned char *cmd, unsigned long addr);
int avr_set_input(OPCODE * op, unsigned char * cmd, unsigned char data); int avr_set_input(OPCODE * op, unsigned char * cmd, unsigned char data);
int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data); int avr_get_output(OPCODE * op, unsigned char * res, unsigned char * data);
int avr_get_output_index(OPCODE * op); int avr_get_output_index(OPCODE * op);
char cmdbitchar(CMDBIT cb);
char *cmdbitstr(CMDBIT cb);
const char *opcodename(int opnum);
char *opcode2str(OPCODE *op, int opnum, int detailed);
/* Functions for AVRMEM structures */ /* Functions for AVRMEM structures */
AVRMEM * avr_new_memtype(void); AVRMEM * avr_new_memtype(void);
@ -333,9 +344,9 @@ int avr_initmem(AVRPART * p);
AVRMEM * avr_dup_mem(AVRMEM * m); AVRMEM * avr_dup_mem(AVRMEM * m);
void avr_free_mem(AVRMEM * m); void avr_free_mem(AVRMEM * m);
void avr_free_memalias(AVRMEM_ALIAS * m); void avr_free_memalias(AVRMEM_ALIAS * m);
AVRMEM * avr_locate_mem(AVRPART * p, char * desc); AVRMEM * avr_locate_mem(AVRPART * p, const char * desc);
AVRMEM * avr_locate_mem_noalias(AVRPART * p, char * desc); AVRMEM * avr_locate_mem_noalias(AVRPART * p, const char * desc);
AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, char * desc); AVRMEM_ALIAS * avr_locate_memalias(AVRPART * p, const char * desc);
AVRMEM_ALIAS * avr_find_memalias(AVRPART * p, AVRMEM * m_orig); AVRMEM_ALIAS * avr_find_memalias(AVRPART * p, AVRMEM * m_orig);
void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p, void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
int type, int verbose); int type, int verbose);
@ -344,7 +355,7 @@ void avr_mem_display(const char * prefix, FILE * f, AVRMEM * m, AVRPART * p,
AVRPART * avr_new_part(void); AVRPART * avr_new_part(void);
AVRPART * avr_dup_part(AVRPART * d); AVRPART * avr_dup_part(AVRPART * d);
void avr_free_part(AVRPART * d); void avr_free_part(AVRPART * d);
AVRPART * locate_part(LISTID parts, char * partdesc); AVRPART * locate_part(LISTID parts, const char * partdesc);
AVRPART * locate_part_by_avr910_devcode(LISTID parts, int devcode); AVRPART * locate_part_by_avr910_devcode(LISTID parts, int devcode);
AVRPART * locate_part_by_signature(LISTID parts, unsigned char * sig, AVRPART * locate_part_by_signature(LISTID parts, unsigned char * sig,
int sigsize); int sigsize);
@ -356,6 +367,8 @@ typedef void (*walk_avrparts_cb)(const char *name, const char *desc,
void walk_avrparts(LISTID avrparts, walk_avrparts_cb cb, void *cookie); void walk_avrparts(LISTID avrparts, walk_avrparts_cb cb, void *cookie);
void sort_avrparts(LISTID avrparts); void sort_avrparts(LISTID avrparts);
int part_match(const char *pattern, const char *string);
int compare_memory_masked(AVRMEM * m, uint8_t buf1, uint8_t buf2); int compare_memory_masked(AVRMEM * m, uint8_t buf1, uint8_t buf2);
#ifdef __cplusplus #ifdef __cplusplus
@ -659,6 +672,7 @@ typedef struct programmer_t {
char desc[PGM_DESCLEN]; char desc[PGM_DESCLEN];
char type[PGM_TYPELEN]; char type[PGM_TYPELEN];
char port[PGM_PORTLEN]; char port[PGM_PORTLEN];
char *parent_id;
void (*initpgm)(struct programmer_t * pgm); void (*initpgm)(struct programmer_t * pgm);
unsigned int pinno[N_PINS]; unsigned int pinno[N_PINS];
struct pindef_t pin[N_PINS]; struct pindef_t pin[N_PINS];

View File

@ -50,7 +50,7 @@
#include "libavrdude.h" #include "libavrdude.h"
#include "term.h" #include "term.h"
#include "developer_opts.h"
/* Get VERSION from ac_cfg.h */ /* Get VERSION from ac_cfg.h */
char * version = VERSION; char * version = VERSION;
@ -761,6 +761,12 @@ int main(int argc, char * argv [])
avrdude_message(MSG_NOTICE, "\n"); avrdude_message(MSG_NOTICE, "\n");
// developer option -p <wildcard>/[*codws] prints various aspects of part descriptions and exits
if(partdesc && (strcmp(partdesc, "*") == 0 || strchr(partdesc, '/'))) {
dev_output_part_defs(partdesc);
exit(1);
}
if (partdesc) { if (partdesc) {
if (strcmp(partdesc, "?") == 0) { if (strcmp(partdesc, "?") == 0) {
avrdude_message(MSG_INFO, "\n"); avrdude_message(MSG_INFO, "\n");

View File

@ -79,6 +79,7 @@ PROGRAMMER * pgm_new(void)
pgm->usbpid = lcreat(NULL, 0); pgm->usbpid = lcreat(NULL, 0);
pgm->desc[0] = 0; pgm->desc[0] = 0;
pgm->type[0] = 0; pgm->type[0] = 0;
pgm->parent_id = NULL;
pgm->config_file = NULL; pgm->config_file = NULL;
pgm->lineno = 0; pgm->lineno = 0;
pgm->baudrate = 0; pgm->baudrate = 0;
@ -145,11 +146,8 @@ void pgm_free(PROGRAMMER * const p)
ldestroy_cb(p->usbpid, free); ldestroy_cb(p->usbpid, free);
p->id = NULL; p->id = NULL;
p->usbpid = NULL; p->usbpid = NULL;
/* this is done by pgm_teardown, but usually cookie is not set to NULL */ /* do not free p->parent_id nor p->config_file */
/* if (p->cookie !=NULL) { /* p->cookie is freed by pgm_teardown */
free(p->cookie);
p->cookie = NULL;
}*/
free(p); free(p);
} }
@ -169,7 +167,6 @@ PROGRAMMER * pgm_dup(const PROGRAMMER * const src)
pgm->id = lcreat(NULL, 0); pgm->id = lcreat(NULL, 0);
pgm->usbpid = lcreat(NULL, 0); pgm->usbpid = lcreat(NULL, 0);
for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) { for (ln = lfirst(src->usbpid); ln; ln = lnext(ln)) {
int *ip = malloc(sizeof(int)); int *ip = malloc(sizeof(int));
if (ip == NULL) { if (ip == NULL) {