From 572849ec2af20416c118248b0ce95d4bb70929be Mon Sep 17 00:00:00 2001 From: Stefan Rueger Date: Thu, 21 Jul 2022 21:42:07 +0100 Subject: [PATCH] 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. --- src/developer_opts.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/developer_opts.h | 1 + 2 files changed, 95 insertions(+) diff --git a/src/developer_opts.c b/src/developer_opts.c index cdc40a21..2f50eda6 100644 --- a/src/developer_opts.c +++ b/src/developer_opts.c @@ -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, ...) { int size = 0; char *p = NULL; diff --git a/src/developer_opts.h b/src/developer_opts.h index 00c13005..d112f517 100644 --- a/src/developer_opts.h +++ b/src/developer_opts.h @@ -25,6 +25,7 @@ const char *opcodename(int opnum); char *opcode2str(OPCODE *op, int opnum, int detailed); int opcodecmp(OPCODE *op1, OPCODE *op2, int opnum); 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); void dev_output_part_defs(char *partdesc);