Provide avr_set_addr_mem() to set addresses in SPI opcodes within boundaries

The function avr_set_addr_mem(AVRMEM *mem, int opnum, unsigned char *cmd,
unsigned long addr) is meant to replace avr_set_addr(OPCODE *op, unsigned
char *cmd, unsigned long addr) in future.

avr_set_addr_mem() has more information about the context of the task in that
it knows the memory size, memory page size, whether or not the memory is a
flash memory (which gets words addressees supplied) and, crucially, knows
which SPI operation it is meant to compute the address bits for.

avr_set_addr_mem() first computes the interval of bit numbers that must be
supplied for the SPI command to stand a chance to work. The function only
sets those address bits that are needed. Once all avr_set_addr() function
calls have been replaced by avr_set_addr_mem(), the SPI commands that need an
address can afford to declare in avrdude.conf all 16 address bits in the
middle two bytes of the SPI command. This over-declaration will be corrected
during runtime by avr_set_addr_mem(). One consequence of this is that parts
can inherit smaller or larger memories from parents without the need to use
different SPI codes in avrdude.conf. Another consequence is that
avr_set_addr_mem() can, and does, tell the caller whether vital address bits
were not declared in the SPI opcode. During parsing of avrdude.conf this
might be utilised to generate a corresponding warning. This will uncover
problematic SPI codes in avrdude.conf that in the past went undetected.
This commit is contained in:
Stefan Rueger 2022-07-21 21:42:07 +01:00
parent 55f6765ea5
commit 572849ec2a
2 changed files with 95 additions and 0 deletions

View File

@ -295,6 +295,100 @@ static void checkaddr(int memsize, int pagesize, int opnum, OPCODE *op, AVRPART
} }
/*
* 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;
}
static char *dev_sprintf(const char *fmt, ...) { static char *dev_sprintf(const char *fmt, ...) {
int size = 0; int size = 0;
char *p = NULL; char *p = NULL;

View File

@ -25,6 +25,7 @@ const char *opcodename(int opnum);
char *opcode2str(OPCODE *op, int opnum, int detailed); char *opcode2str(OPCODE *op, int opnum, int detailed);
int opcodecmp(OPCODE *op1, OPCODE *op2, int opnum); int opcodecmp(OPCODE *op1, OPCODE *op2, int opnum);
int intlog2(unsigned int n); int intlog2(unsigned int n);
int avr_set_addr_mem(AVRMEM *mem, int opnum, unsigned char *cmd, unsigned long addr);
int part_match(const char *pattern, const char *string); int part_match(const char *pattern, const char *string);
void dev_output_part_defs(char *partdesc); void dev_output_part_defs(char *partdesc);