/* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2022, Stefan Rueger * * 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 . */ /* $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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "avrdude.h" #include "libavrdude.h" #include "config.h" #include "developer_opts.h" #include "developer_opts_private.h" // Inject part parameters into a semi-automated rewrite of avrdude.conf // - Add entries to the tables below; they get written on -p*/si or -c*/si // - Use the output in a new avrdude.conf // - Output again with -p*/s or -c*/s (no /i) and use that for final avrdude.conf // - Remove entries from below tables static struct { const char *pgmid, *var, *value; } pgminj[] = { // Add triples here, eg, {"stk500v2", "prog_modes", "PM_TPI|PM_ISP"}, {NULL, NULL, NULL}, }; static struct { const char *mcu, *var, *value; } ptinj[] = { // Add triples here, eg, {"ATmega328P", "mcuid", "999"}, {NULL, NULL, NULL}, }; static struct { const char *mcu, *mem, *var, *value; } meminj[] = { // Add quadruples here, eg, {"ATmega328P", "flash", "page_size", "128"}, {NULL, NULL, NULL, NULL}, }; // Return 0 if op code would encode (essentially) the same SPI command static int opcodecmp(const OPCODE *op1, const 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(const AVRPART *p, const char *d, const 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(const AVRPART *p, const char *d, OPCODE * const *opa) { for(int i=0; iflags no longer used for programming modes, use p->prog_modes * remove this comment in 2023 static char *prog_modes_str_flags(const AVRPART *p) { static char type[1024]; *type = 0; if(!(p->flags & AVRPART_HAS_TPI) // TPI devices don't have the SPM opcode && strcmp(p->id, "t4") // Nor have these early ones && strcmp(p->id, "t5") && strcmp(p->id, "t9") && strcmp(p->id, "t10") && strcmp(p->id, "t11") && strcmp(p->id, "t12") && strcmp(p->id, "t15") && strcmp(p->id, "t20") && strcmp(p->id, "t26") && strcmp(p->id, "t28") && strcmp(p->id, "t40")) strcpy(type, "PM_SPM"); switch(p->flags & (AVRPART_HAS_PDI | AVRPART_AVR32 | AVRPART_HAS_TPI | AVRPART_HAS_UPDI)) { case AVRPART_HAS_TPI: // AVR8L family strcat(type, "|PM_TPI"); break; case 0: // AVR8 family, "classic" parts if(p->flags & AVRPART_SERIALOK) // ATmega406 has no ISP strcat(type, "|PM_ISP"); break; case AVRPART_HAS_PDI: // AVR8_XMEGA family strcat(type, "|PM_PDI"); break; case AVRPART_HAS_UPDI: // AVR8X family strcat(type, "|PM_UPDI"); break; case AVRPART_AVR32: // AVR32 family strcat(type, "|PM_aWire"); break; default: strcat(type, "|PM_UNKNOWN"); } switch(p->ctl_stack_type) { case CTL_STACK_PP: strcat(type, "|PM_HVPP"); break; case CTL_STACK_HVSP: strcat(type, "|PM_HVSP"); break; default: break; } if(p->flags & AVRPART_HAS_DW) strcat(type, "|PM_debugWIRE"); if(p->flags & AVRPART_HAS_JTAG) strcat(type, "|PM_JTAG"); return type + (*type == '|'); } * */ static char *prog_modes_str(int pm) { static char type[1024]; strcpy(type, "0"); if(pm & PM_SPM) strcat(type, " | PM_SPM"); if(pm & PM_TPI) strcat(type, " | PM_TPI"); if(pm & PM_ISP) strcat(type, " | PM_ISP"); if(pm & PM_PDI) strcat(type, " | PM_PDI"); if(pm & PM_UPDI) strcat(type, " | PM_UPDI"); if(pm & PM_HVSP) strcat(type, " | PM_HVSP"); if(pm & PM_HVPP) strcat(type, " | PM_HVPP"); if(pm & PM_debugWIRE) strcat(type, " | PM_debugWIRE"); if(pm & PM_JTAG) strcat(type, " | PM_JTAG"); if(pm & PM_JTAGmkI) strcat(type, " | PM_JTAGmkI"); if(pm & PM_XMEGAJTAG) strcat(type, " | PM_XMEGAJTAG"); if(pm & PM_AVR32JTAG) strcat(type, " | PM_AVR32JTAG"); if(pm & PM_aWire) strcat(type, " | PM_aWire"); return type + (type[1] == 0? 0: 4); } // Check whether address bits are where they should be in ISP commands static void checkaddr(int memsize, int pagesize, int opnum, const OPCODE *op, const AVRPART *p, const 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 cfg_strdup("dev_sprintf()", ""); size++; // For terminating '\0' p = cfg_malloc("dev_sprintf()", size); va_start(ap, fmt); size = vsnprintf(p, size, fmt, ap); va_end(ap); if(size < 0) *p = 0; 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(stdout, fmt, ap); va_end(ap); if(rc > 0) dev_nprinted += rc; } return rc; } // Any of the strings in the list contains subs as substring? int dev_has_subsstr_comms(const LISTID comms, const char *subs) { if(comms) for(LNODEID ln=lfirst(comms); ln; ln=lnext(ln)) if(strstr((char *) ldata(ln), subs)) return 1; return 0; } // Print a chained list of strings void dev_print_comment(const LISTID comms) { if(comms) for(LNODEID ln=lfirst(comms); ln; ln=lnext(ln)) dev_info("%s", (char *) ldata(ln)); } // Conditional output of part, memory or programmer's comments field static void dev_cout(const LISTID comms, const char *name, int rhs, int elself) { COMMENT *cp; if((cp = locate_comment(comms, name, rhs))) dev_print_comment(cp->comms); else if(elself) dev_info("\n"); } // Print part->comments, mem->comments or pgm->comments (for debugging) void dev_print_kw_comments(const LISTID comms) { if(comms) for(LNODEID ln=lfirst(comms); ln; ln=lnext(ln)) { COMMENT *n = ldata(ln); if(n && n->comms) { dev_info(">>> %s %c\n", n->kw, n->rhs? '>': '<'); dev_print_comment(n->comms); } } } // Ideally all assignment outputs run via this function static int dev_part_strct_entry(bool tsv, // Print as spreadsheet? const char *col0, const char *col1, const char *col2, // Descriptors of item const char *name, char *cont, const LISTID comms) { // Name, contents and comments 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"); dev_cout(comms, n, 0, 0); // Print comments before the line dev_info("%*s%-*s = %s;", indent? 8: 4, "", indent? 18: 22, n, c); dev_cout(comms, n, 1, 1); // Print comments on rhs } if(cont) free(cont); return 1; } static const char *dev_controlstack_name(const 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, const AVRPART *p, const char *name, const 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_cout(p->comments, name, 0, 0); dev_info(" %-22s =%s", name, ns <=8? " ": ""); } if(ns <= 0) dev_info(tsv? "NULL\n": "NULL;"); else for(int i=0; i 8 && i%8 == 0? "\n ": " ", stack[i], i+1comments, name, 1, 1); } static int intcmp(int a, int b) { return a-b; } // Deep copies for comparison and raw output typedef struct { char descbuf[32]; AVRMEM base; OPCODE ops[AVR_OP_MAX]; } AVRMEMdeep; static int avrmem_deep_copy(AVRMEMdeep *d, const AVRMEM *m) { d->base = *m; // Note memory desc (name, really) is limited to 31 char here memset(d->descbuf, 0, sizeof d->descbuf); strncpy(d->descbuf, m->desc, sizeof d->descbuf-1); // Zap address values d->base.comments = NULL; d->base.buf = NULL; d->base.tags = NULL; d->base.desc = NULL; for(int i=0; ibase.op[i] = NULL; // Copy over the SPI operations themselves memset(d->ops, 0, sizeof d->ops); for(size_t i=0; iop[i]) d->ops[i] = *m->op[i]; return 0; } static int memorycmp(const AVRMEM *m1, const 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 { char descbuf[64]; char idbuf[32]; char family_idbuf[16]; AVRPART base; OPCODE ops[AVR_OP_MAX]; AVRMEMdeep mems[40]; } AVRPARTdeep; // Return memory iff its desc matches str exactly static AVRMEM *dev_locate_mem(const AVRPART *p, const char *str) { AVRMEM *m = p->mem? avr_locate_mem_noalias(p, str): NULL; return m && strcmp(m->desc, str) == 0? m: NULL; } static int avrpart_deep_copy(AVRPARTdeep *d, const AVRPART *p) { AVRMEM *m; size_t di; memset(d, 0, sizeof *d); d->base = *p; d->base.comments = NULL; d->base.parent_id = NULL; d->base.config_file = NULL; d->base.lineno = 0; // Copy over desc, id, and family_id memset(d->descbuf, 0, sizeof d->descbuf); strncpy(d->descbuf, p->desc, sizeof d->descbuf-1); memset(d->idbuf, 0, sizeof d->idbuf); strncpy(d->idbuf, p->id, sizeof d->idbuf-1); memset(d->family_idbuf, 0, sizeof d->family_idbuf); strncpy(d->family_idbuf, p->family_id, sizeof d->family_idbuf-1); // Zap address values d->base.desc = NULL; d->base.id = NULL; d->base.family_id = NULL; d->base.mem = NULL; d->base.mem_alias = NULL; for(int i=0; ibase.op[i] = NULL; // Copy over all used SPI operations memset(d->ops, 0, sizeof d->ops); for(int i=0; iop[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 avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) { m = dev_locate_mem(p, avr_mem_order[mi]); if(m) { if(di >= sizeof d->mems/sizeof *d->mems) { pmsg_error("ran out of mems[] space, increase size in AVRMEMdeep of developer_opts.c and recompile\n"); exit(1); } avrmem_deep_copy(d->mems+di, m); di++; } } return di; } static char txtchar(unsigned char in) { in &= 0x7f; return in == 0? '.': in > ' ' && in < 0x7f? in: '_'; } static void dev_raw_dump(const void *v, int nbytes, const char *name, const char *sub, int idx) { const unsigned char *p = v; int n = (nbytes + 31)/32; for(int i=0; idesc, "part", 0); for(int i=0; idesc, opsnm("part", i), 1); for(int i=0; idesc, nm, i+2); dev_raw_dump(&dp.mems[i].base, sizeof dp.mems[i].base, part->desc, nm, i+2); for(int j=0; jdesc, opsnm(nm, j), i+2); } } static void dev_part_strct(const AVRPART *p, bool tsv, const AVRPART *base, bool injct) { char *descstr = cfg_escape(p->desc); COMMENT *cp; if(!tsv) { const char *del = "#------------------------------------------------------------"; cp = locate_comment(p->comments, "*", 0); if(!cp || !dev_has_subsstr_comms(cp->comms, del)) { dev_info("%s\n", del); dev_info("# %.*s\n", strlen(descstr)-2, descstr+1); // Remove double quotes dev_info("%s\n\n", del); } if(cp) dev_print_comment(cp->comms); if(p->parent_id && *p->parent_id) dev_info("part parent \"%s\"\n", p->parent_id); else dev_info("part\n"); } _if_partout_str(strcmp, descstr, desc); _if_partout_str(strcmp, cfg_escape(p->id), id); _if_partout_str(strcmp, cfg_escape(p->family_id), family_id); _if_partout_str(intcmp, cfg_strdup("dev_part_strct()", prog_modes_str(p->prog_modes)), prog_modes); _if_partout(intcmp, "%d", mcuid); _if_partout(intcmp, "%d", n_interrupts); _if_partout(intcmp, "%d", n_page_erase); _if_partout(intcmp, "%d", n_boot_sections); _if_partout(intcmp, "%d", boot_section_size); _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(cfg_strdup("dev_part_strct()", p->reset_disposition == RESET_DEDICATED? "dedicated": p->reset_disposition == RESET_IO? "io": "unknown"), reset); _if_partout_str(intcmp, cfg_strdup("dev_part_strct()", 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_IS_AT90S1200, is_at90s1200); _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(cfg_strdup("dev_part_strct()", 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); _if_partout(intcmp, "0x%02x", eind); _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); _if_partout(intcmp, "0x%02x", autobaud_sync); 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), p->comments); for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) { AVRMEM *m, *bm; m = dev_locate_mem(p, avr_mem_order[mi]); bm = base? dev_locate_mem(base, avr_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_cout(m->comments, "*", 0, 1); dev_info(" 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); _if_memout(intcmp, "%d", n_word_writes); _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), m->comments); if(injct) for(size_t i=0; idesc) == 0 && strcmp(meminj[i].mem, m->desc) == 0) dev_part_strct_entry(tsv, ".ptmm", p->desc, m->desc, meminj[i].var, cfg_strdup("meminj", meminj[i].value), NULL); if(!tsv) { dev_cout(m->comments, ";", 0, 0); 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(injct) for(size_t i=0; idesc) == 0) dev_part_strct_entry(tsv, ".pt", p->desc, NULL, ptinj[i].var, cfg_strdup("ptinj", ptinj[i].value), NULL); if(!tsv) { dev_cout(p->comments, ";", 0, 0); dev_info(";\n"); } } void dev_output_pgm_part(int dev_opt_c, const char *programmer, int dev_opt_p, const char *partdesc) { if(dev_opt_c == 2 && dev_opt_p == 2) { char *p; dev_print_comment(cfg_get_prologue()); dev_info("default_programmer = %s;\n", p = cfg_escape(default_programmer)); free(p); dev_info("default_parallel = %s;\n", p = cfg_escape(default_parallel)); free(p); dev_info("default_serial = %s;\n", p = cfg_escape(default_serial)); free(p); dev_info("default_spi = %s;\n", p = cfg_escape(default_spi)); free(p); dev_info("\n#\n# PROGRAMMER DEFINITIONS\n#\n\n"); } if(dev_opt_c) dev_output_pgm_defs(cfg_strdup("main()", programmer)); if(dev_opt_p == 2 && dev_opt_c) dev_info("\n"); if(dev_opt_p == 2) dev_info("#\n# PART DEFINITIONS\n#\n"); if(dev_opt_p) dev_output_part_defs(cfg_strdup("main()", partdesc)); } // -p */[dASsrcow*ti] void dev_output_part_defs(char *partdesc) { bool cmdok, waits, opspi, descs, astrc, strct, cmpst, injct, 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 */s flags = "s"; if(!*flags || !strchr("cdoASsrw*ti", *flags)) { dev_info("%s: flags for developer option -p / 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" " d description of core part features\n" " A show entries of avrdude.conf parts with all values\n" " S show entries of avrdude.conf parts with necessary values\n" " s show short entries of avrdude.conf parts using parent\n" " r show entries of avrdude.conf parts as raw dump\n" " c check and report errors in address bits of SPI commands\n" " o opcodes for SPI programming parts and memories\n" " w wd_... constants for ISP parts\n" " * all of the above except s and S\n" " t use tab separated values as much as possible\n" " i inject assignments from source code table\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 */s\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, /S and /A 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; } all = *flags == '*'; cmdok = all || !!strchr(flags, 'c'); descs = all || !!strchr(flags, 'd'); opspi = all || !!strchr(flags, 'o'); waits = all || !!strchr(flags, 'w'); astrc = all || !!strchr(flags, 'A'); raw = all || !!strchr(flags, 'r'); strct = !!strchr(flags, 'S'); cmpst = !!strchr(flags, 's'); tsv = !!strchr(flags, 't'); injct = !!strchr(flags, 'i'); // 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)) avr_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)) avr_add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc); } if((nprinted = dev_nprinted)) { dev_info("\n"); 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(astrc || strct || cmpst) dev_part_strct(p, tsv, astrc? NULL: strct? nullpart: p->parent_id && *p->parent_id? locate_part(part_list, p->parent_id): nullpart, injct); 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, prog_modes_str(p->prog_modes), 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->prog_modes & PM_ISP) 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->prog_modes & PM_ISP) { 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); } } } } } } } } static void dev_pgm_raw(const PROGRAMMER *pgm) { PROGRAMMER dp; int len, idx; char *id = ldata(lfirst(pgm->id)); LNODEID ln; memcpy(&dp, pgm, sizeof dp); // Dump id, usbpid and hvupdi_support lists for(idx=0, ln=lfirst(dp.id); ln; ln=lnext(ln)) dev_raw_dump(ldata(ln), strlen(ldata(ln))+1, id, "id", idx++); for(idx=0, ln=lfirst(dp.usbpid); ln; ln=lnext(ln)) dev_raw_dump(ldata(ln), sizeof(int), id, "usbpid", idx++); for(idx=0, ln=lfirst(dp.hvupdi_support); ln; ln=lnext(ln)) dev_raw_dump(ldata(ln), sizeof(int), id, "hvupdi_", idx++); if(dp.desc) dev_raw_dump(dp.desc, strlen(dp.desc)+1, id, "desc", 0); // Dump cache_string values if(dp.usbdev && *dp.usbdev) dev_raw_dump(dp.usbdev, strlen(dp.usbdev)+1, id, "usbdev", 0); if(dp.usbsn && *dp.usbsn) dev_raw_dump(dp.usbsn, strlen(dp.usbsn)+1, id, "usbsn", 0); if(dp.usbvendor && *dp.usbvendor) dev_raw_dump(dp.usbvendor, strlen(dp.usbvendor)+1, id, "usbvend", 0); if(dp.usbproduct && *dp.usbproduct) dev_raw_dump(dp.usbproduct, strlen(dp.usbproduct)+1, id, "usbprod", 0); // Zap all bytes beyond terminating nul of desc, type and port array if((len = (int) strlen(dp.type)+1) < (int) sizeof dp.type) memset(dp.type + len, 0, sizeof dp.type - len); if((len = (int) strlen(dp.port)+1) < (int) sizeof dp.port) memset(dp.port + len, 0, sizeof dp.port - len); // Zap address values dp.desc = NULL; dp.id = NULL; dp.comments = NULL; dp.parent_id = NULL; dp.initpgm = NULL; dp.usbpid = NULL; dp.usbdev = NULL; dp.usbsn = NULL; dp.usbvendor = NULL; dp.usbproduct = NULL; dp.hvupdi_support = NULL; // Only dump contents of PROGRAMMER struct up to and excluding the fd component dev_raw_dump((char *) &dp, offsetof(PROGRAMMER, fd), id, "pgm", 0); } static const char *connstr(conntype_t conntype) { switch(conntype) { case CONNTYPE_PARALLEL: return "parallel"; case CONNTYPE_SERIAL: return "serial"; case CONNTYPE_USB: return "usb"; case CONNTYPE_SPI: return "spi"; default: return ""; } } static char *dev_usbpid_liststr(const PROGRAMMER *pgm) { char spc[1024]; int firstid = 1; spc[0] = 0; if(pgm->usbpid) for(LNODEID ln=lfirst(pgm->usbpid); ln; ln=lnext(ln)) { if(strlen(spc) > sizeof spc - 20) break; if(!firstid) strcat(spc, ", "); firstid = 0; sprintf(spc + strlen(spc), "0x%04x", *(unsigned int *) ldata(ln)); } return cfg_strdup(__func__, *spc? spc: "NULL"); } static char *dev_hvupdi_support_liststr(const PROGRAMMER *pgm) { char spc[1024]; int firstid = 1; spc[0] = 0; if(pgm->hvupdi_support) for(LNODEID ln=lfirst(pgm->hvupdi_support); ln; ln=lnext(ln)) { if(strlen(spc) > sizeof spc - 20) break; if(!firstid) strcat(spc, ", "); firstid = 0; sprintf(spc + strlen(spc), "%d", *(unsigned int *) ldata(ln)); } return cfg_strdup(__func__, *spc? spc: "NULL"); } static void dev_pgm_strct(const PROGRAMMER *pgm, bool tsv, const PROGRAMMER *base, bool injct) { char *id = ldata(lfirst(pgm->id)); LNODEID ln; COMMENT *cp; int firstid; if(!tsv) { const char *del = "#------------------------------------------------------------"; cp = locate_comment(pgm->comments, "*", 0); if(!cp || !dev_has_subsstr_comms(cp->comms, del)) { dev_info("%s\n# ", del); for(firstid=1, ln=lfirst(pgm->id); ln; ln=lnext(ln)) { if(!firstid) dev_info("/"); firstid = 0; dev_info("%s", ldata(ln)); } dev_info("\n%s\n\n", del); } if(cp) dev_print_comment(cp->comms); if(pgm->parent_id && *pgm->parent_id) dev_info("programmer parent \"%s\"\n", pgm->parent_id); else dev_info("programmer\n"); } if(tsv) dev_info(".prog\t%s\tid\t", id); else { dev_cout(pgm->comments, "id", 0, 0); dev_info(" %-22s = ", "id"); } for(firstid=1, ln=lfirst(pgm->id); ln; ln=lnext(ln)) { if(!firstid) dev_info(", "); firstid = 0; char *str = cfg_escape(ldata(ln)); dev_info("%s", str); free(str); } if(tsv) dev_info("\n"); else { dev_info(";"); dev_cout(pgm->comments, "id", 1, 1); } _if_pgmout_str(strcmp, cfg_escape(pgm->desc), desc); if(!base || base->initpgm != pgm->initpgm) _pgmout_fmt("type", "\"%s\"", locate_programmer_type_id(pgm->initpgm)); _if_pgmout_str(intcmp, cfg_strdup("dev_pgm_strct()", prog_modes_str(pgm->prog_modes)), prog_modes); if(!base || base->conntype != pgm->conntype) _pgmout_fmt("connection_type", "%s", connstr(pgm->conntype)); _if_pgmout(intcmp, "%d", baudrate); _if_pgmout(intcmp, "0x%04x", usbvid); char *pgmstr = dev_usbpid_liststr(pgm); int show = !base; if(base) { char *basestr = dev_usbpid_liststr(base); show = strcmp(basestr, pgmstr) != 0; free(basestr); } if(show) dev_part_strct_entry(tsv, ".prog", id, NULL, "usbpid", pgmstr, pgm->comments); else // dev_part_strct_entry() frees pgmstr free(pgmstr); _if_pgmout_str(strcmp, cfg_escape(pgm->usbdev), usbdev); _if_pgmout_str(strcmp, cfg_escape(pgm->usbsn), usbsn); _if_pgmout_str(strcmp, cfg_escape(pgm->usbvendor), usbvendor); _if_pgmout_str(strcmp, cfg_escape(pgm->usbproduct), usbproduct); for(int i=0; ipin+i); char *bstr = base? pins_to_strdup(base->pin+i): NULL; if(!base || strcmp(bstr, str)) _pgmout_fmt(avr_pin_lcname(i), "%s", str); free(str); if(bstr) free(bstr); } pgmstr = dev_hvupdi_support_liststr(pgm); show = !base; if(base) { char *basestr = dev_hvupdi_support_liststr(base); show = strcmp(basestr, pgmstr) != 0; free(basestr); } if(show) dev_part_strct_entry(tsv, ".prog", id, NULL, "hvupdi_support", pgmstr, pgm->comments); else // dev_part_strct_entry() frees pgmstr free(pgmstr); if(injct) for(size_t i=0; iid); ln; ln=lnext(ln)) if(strcmp(pgminj[i].pgmid, ldata(ln)) == 0) dev_part_strct_entry(tsv, ".prog", ldata(ln), NULL, pgminj[i].var, cfg_strdup("pgminj", pgminj[i].value), NULL); if(!tsv) { dev_cout(pgm->comments, ";", 0, 0); dev_info(";\n"); } } // -c */[ASsrti] void dev_output_pgm_defs(char *pgmid) { bool astrc, strct, cmpst, raw, tsv, injct; char *flags; int nprinted; PROGRAMMER *nullpgm = pgm_new(); if((flags = strchr(pgmid, '/'))) *flags++ = 0; if(!flags && !strcmp(pgmid, "*")) // Treat -c * as if it was -c */s flags = "s"; if(!*flags || !strchr("ASsrti", *flags)) { dev_info("%s: flags for developer option -c / not recognised\n", progname); dev_info( "Wildcard examples (these need protecting in the shell through quoting):\n" " * all known programmers\n" " avrftdi just this programmer\n" " jtag*pdi matches jtag2pdi, jtag3pdi, jtag3updi and jtag2updi\n" " jtag?pdi matches jtag2pdi and jtag3pdi\n" "Flags (one or more of the characters below):\n" " A show entries of avrdude.conf programmers with all values\n" " S show entries of avrdude.conf programmers with necessary values\n" " s show short entries of avrdude.conf programmers using parent\n" " r show entries of avrdude.conf programmers as raw dump\n" " t use tab separated values as much as possible\n" " i inject assignments from source code table\n" "Examples:\n" " $ avrdude -c usbasp/s\n" " $ avrdude -c */st | grep baudrate\n" " $ avrdude -c */r | sort\n" "Notes:\n" " -c * is the same as -c */s\n" " This help message is printed using any unrecognised flag, eg, -c/h\n" " Leaving no space after -c can be an OK substitute for quoting in shells\n" " /s, /S and /A outputs are designed to be used as input in avrdude.conf\n" " Sorted /r output should stay invariant when rearranging avrdude.conf\n" " These options are just to help development, so not further documented\n" ); return; } astrc = !!strchr(flags, 'A'); strct = !!strchr(flags, 'S'); cmpst = !!strchr(flags, 's'); raw = !!strchr(flags, 'r'); tsv = !!strchr(flags, 't'); injct = !!strchr(flags, 'i'); nprinted = dev_nprinted; LNODEID ln1, ln2; for(ln1=lfirst(programmers); ln1; ln1=lnext(ln1)) { PROGRAMMER *pgm = ldata(ln1); int matched = 0; for(ln2=lfirst(pgm->id); ln2; ln2=lnext(ln2)) { if(part_match(pgmid, ldata(ln2))) { matched = 1; break; } } if(!matched) continue; if(dev_nprinted > nprinted) { dev_info("\n"); nprinted = dev_nprinted; } if(astrc || strct || cmpst) dev_pgm_strct(pgm, tsv, astrc? NULL: strct? nullpgm: pgm->parent_id && *pgm->parent_id? locate_programmer(programmers, pgm->parent_id): nullpgm, injct); if(raw) dev_pgm_raw(pgm); } }