summaryrefslogtreecommitdiff
path: root/appl/cmd/avr/burn.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/avr/burn.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/avr/burn.b')
-rw-r--r--appl/cmd/avr/burn.b859
1 files changed, 859 insertions, 0 deletions
diff --git a/appl/cmd/avr/burn.b b/appl/cmd/avr/burn.b
new file mode 100644
index 00000000..d1004cd1
--- /dev/null
+++ b/appl/cmd/avr/burn.b
@@ -0,0 +1,859 @@
+implement Burn;
+
+include "sys.m";
+ sys: Sys;
+
+include "draw.m";
+
+include "bufio.m";
+ bufio: Bufio;
+ Iobuf: import bufio;
+
+include "timers.m";
+ timers: Timers;
+ Timer: import timers;
+
+include "string.m";
+ str: String;
+
+include "arg.m";
+
+Burn: module
+{
+ init: fn(nil: ref Draw->Context, nil: list of string);
+};
+
+Avr: adt {
+ id: int;
+ rev: int;
+ flashsize: int;
+ eepromsize: int;
+ fusebytes: int;
+ lockbytes: int;
+ serfprog: int; # serial fuse programming support
+ serlprog: int; # serial lockbit programming support
+ serflread: int; # serial fuse/lockbit reading support
+ commonlfr: int; # lockbits and fuses are combined
+ sermemprog: int; # serial memory programming support
+ pagesize: int;
+ eeprompagesize: int;
+ selftimed: int; # all instructions are self-timed
+ fullpar: int; # part has full parallel interface
+ polling: int; # polling can be used during SPI access
+ fpoll: int; # flash poll value
+ epoll1: int; # eeprom poll value 1
+ epoll2: int; # eeprom poll value 2
+ name: string;
+ signalpagel: int; # posn of PAGEL signal (16rD7 by default)
+ signalbs2: int; # posn of BS2 signal (16rA0 by default)
+};
+
+F, T: con iota;
+ATMEGA128: con 16rB2; # 128k devices
+
+avrs: array of Avr = array[] of {
+ (ATMEGA128, 1, 131072, 4096, 3, 1, T, T, T, F, T, 256, 8, T, T, T, 16rFF, 16rFF, 16rFF, "ATmega128", 16rD7, 16rA0),
+};
+
+sfd: ref Sys->FD;
+cfd: ref Sys->FD;
+rd: ref Rd;
+mib510 := 1;
+
+Rd: adt {
+ c: chan of array of byte;
+ pid: int;
+ fd: ref Sys->FD;
+ buf: array of byte;
+ new: fn(fd: ref Sys->FD): ref Rd;
+ read: fn(r: self ref Rd, ms: int): array of byte;
+ readn: fn(r: self ref Rd, n: int, ms: int): array of byte;
+ flush: fn(r: self ref Rd);
+ stop: fn(r: self ref Rd);
+ reader: fn(r: self ref Rd, c: chan of int);
+};
+
+debug := 0;
+verify := 0;
+erase := 1;
+ignore := 0;
+
+init(nil: ref Draw->Context, args: list of string)
+{
+ sys = load Sys Sys->PATH;
+ bufio = ckl(load Bufio Bufio->PATH, Bufio->PATH);
+ str = ckl(load String String->PATH, String->PATH);
+ timers = ckl(load Timers Timers->PATH, Timers->PATH);
+
+ serial := "/dev/eia0";
+ fuseext := -1;
+ fuselow := -1;
+ fusehigh := -1;
+ arg := ckl(load Arg Arg->PATH, Arg->PATH);
+ arg->init(args);
+ arg->setusage("burn [-rD] [-d serialdev] file.out");
+ while((o := arg->opt()) != 0)
+ case o {
+ 'D' => debug++;
+ 'e' => erase = 0;
+ 'r' => verify = 1;
+ 'd' => serial = arg->earg();
+ 'i' => ignore = 1;
+ 'E' => fuseext = fuseval(arg->earg());
+ 'L' => fuselow = fuseval(arg->earg());
+ 'H' => fusehigh = fuseval(arg->earg());
+ * => arg->usage();
+ }
+ args = arg->argv();
+ if(len args != 1)
+ arg->usage();
+ arg = nil;
+
+ sfile := hd args;
+ fd := bufio->open(sfile, Sys->OREAD);
+ if(fd == nil)
+ err(sys->sprint("can't open %s: %r", sfile));
+
+ timers->init(2);
+ sfd = sys->open(serial, Sys->ORDWR);
+ if(sfd == nil)
+ err(sys->sprint("can't open %s: %r", "/dev/eia0"));
+ cfd = sys->open(serial+"ctl", Sys->ORDWR);
+ sys->fprint(cfd, "f");
+ sys->fprint(cfd, "b115200");
+ sys->fprint(cfd, "i8");
+# sys->fprint(cfd, "f\nb115200\ni8");
+ rd = Rd.new(sfd);
+
+ initialise();
+ if(fuseext >= 0 || fuselow >= 0 || fusehigh >= 0){
+ if(fuselow >= 0 && (fuselow & 16rF) == 0)
+ err("don't program external clock");
+ if(fuseext >= 0 && (fuseext & (1<<0)) == 0)
+ err("don't program ATmega103 compatibility");
+ if(fusehigh >= 0 && (fusehigh & (1<<7)) == 0)
+ err("don't program OCDEN=0");
+ if(fusehigh >= 0 && writefusehigh(fusehigh) >= 0)
+ sys->print("set fuse high=%.2ux\n", fusehigh);
+ if(fuselow >= 0 && writefuselow(fuselow) >= 0)
+ sys->print("set fuse low=%.2ux\n", fuselow);
+ if(fuseext >= 0 && writefuseext(fuseext) >= 0)
+ sys->print("set fuse ext=%.2ux\n", fuseext);
+ shutdown();
+ exit;
+ }
+
+ if(!verify && erase){
+ chiperase();
+ sys->print("Erased flash\n");
+ }
+
+ totbytes := 0;
+ while((l := fd.gets('\n')) != nil){
+ (c, addr, data) := sdecode(l);
+ if(c >= '1' && c <= '3'){
+ if(verify){
+ fdata := readflashdata(addr, len data);
+ if(!eq(fdata, data))
+ sys->print("mismatch: %d::%d at %4.4ux\n", len data, len fdata, addr);
+ }else if(writeflashdata(addr, data) != len data)
+ err("failed to program device");
+ totbytes += len data;
+ } else if(c == '0')
+ sys->print("title: %q\n", string data);
+ }
+ if(!verify){
+ flushpage();
+ sys->print("Programmed %ud (0x%4.4ux) bytes\n", totbytes, totbytes);
+ }
+
+ shutdown();
+}
+
+ckl[T](m: T, s: string): T
+{
+ if(m == nil)
+ err(sys->sprint("can't load %s: %r", s));
+ return m;
+}
+
+fuseval(s: string): int
+{
+ (n, t) := str->toint(s, 16);
+ if(t != nil || n < 0 || n > 255)
+ err("illegal fuse value");
+ return n;
+}
+
+cache: (int, array of byte);
+
+readflashdata(addr: int, nbytes: int): array of byte
+{
+ data := array[nbytes] of byte;
+ ia := addr;
+ ea := addr+nbytes;
+ while(addr < ea){
+ (ca, cd) := cache;
+ if(addr >= ca && addr < ca+len cd){
+ n := nbytes;
+ o := addr-ca;
+ if(o+n > len cd)
+ n = len cd - o;
+ if(addr-ia+n > len data)
+ n = len data - (addr-ia);
+ data[addr-ia:] = cd[o:o+n];
+ addr += n;
+ }else{
+ ca = addr & ~16rFF;
+ cd = readflashpage(ca, 16r100);
+ cache = (ca, cd);
+ }
+ }
+ return data;
+}
+
+writeflashdata(addr: int, data: array of byte): int
+{
+ pagesize := avrs[0].pagesize;
+ ia := addr;
+ ea := addr+len data;
+ while(addr < ea){
+ (ca, cd) := cache;
+ if(addr >= ca && addr < ca+len cd){
+ n := len data;
+ o := addr-ca;
+ if(o+n > len cd)
+ n = len cd - o;
+ cd[o:] = data[0:n];
+ addr += n;
+ data = data[n:];
+ }else{
+ if(flushpage() < 0)
+ break;
+ cache = (addr & ~16rFF, array[pagesize] of {* => byte 16rFF});
+ }
+ }
+ return addr-ia;
+}
+
+flushpage(): int
+{
+ (ca, cd) := cache;
+ if(len cd == 0)
+ return 0;
+ cache = (0, nil);
+ if(writeflashpage(ca, cd) != len cd)
+ return -1;
+ return len cd;
+}
+
+shutdown()
+{
+# setisp(0);
+ if(rd != nil){
+ rd.stop();
+ rd = nil;
+ }
+ if(timers != nil)
+ timers->shutdown();
+}
+
+err(s: string)
+{
+ sys->fprint(sys->fildes(2), "burn: %s\n", s);
+ shutdown();
+ raise "fail:error";
+}
+
+dump(a: array of byte): string
+{
+ s := sys->sprint("[%d]", len a);
+ for(i := 0; i < len a; i++)
+ s += sys->sprint(" %.2ux", int a[i]);
+ return s;
+}
+
+initialise()
+{
+ if(mib510){
+ # MIB510-specific: switch rs232 to STK500
+ for(i:=0; i<8; i++){
+ setisp0(1);
+ sys->sleep(10);
+ rd.flush();
+ if(setisp(1))
+ break;
+ }
+ if(!setisp(1))
+ err("no response from programmer");
+ }
+ resync();
+ resync();
+ if(!mib510){
+ r := rpc(array[] of {Cmd_STK_GET_SIGN_ON}, 7);
+ if(r != nil)
+ sys->print("got: %q\n", string r);
+ }
+ r := readsig();
+ if(len r > 0 && r[0] != byte 16rFF)
+ sys->print("sig: %s\n", dump(r));
+ (min, maj) := version();
+ sys->print("Firmware version: %s.%s\n", min, maj);
+ setdevice(avrs[0]);
+ pgmon();
+ r = readsig();
+ sys->print("sig: %s\n", dump(r));
+ pgmoff();
+ if(len r < 3 || r[0] != byte 16r1e || r[1] != byte 16r97 || r[2] != byte 16r02)
+ if(!ignore)
+ err("unlikely response: check connections");
+
+ # could set voltages here...
+ sys->print("fuses: h=%.2ux l=%.2ux e=%.2ux\n", readfusehigh(), readfuselow(), readfuseext());
+}
+
+resync()
+{
+ for(i := 0; i < 8; i++){
+ rd.flush();
+ r := rpc(array[] of {Cmd_STK_GET_SYNC}, 0);
+ if(r != nil)
+ return;
+ }
+ err("lost sync with programmer");
+}
+
+getparam(p: byte): int
+{
+ r := rpc(array[] of {Cmd_STK_GET_PARAMETER, p}, 1);
+ if(len r > 0)
+ return int r[0];
+ return -1;
+}
+
+version(): (string, string)
+{
+ maj := getparam(Parm_STK_SW_MAJOR);
+ min := getparam(Parm_STK_SW_MINOR);
+ if(mib510)
+ return (sys->sprint("%c", maj), sys->sprint("%c", min));
+ return (sys->sprint("%d", maj), sys->sprint("%d", min));
+}
+
+eq(a, b: array of byte): int
+{
+ if(len a != len b)
+ return 0;
+ for(i := 0; i < len a; i++)
+ if(a[i] != b[i])
+ return 0;
+ return 1;
+}
+
+#
+# Motorola S records
+#
+
+badsrec(s: string)
+{
+ err("bad S record: "+s);
+}
+
+hexc(c: int): int
+{
+ if(c >= '0' && c <= '9')
+ return c-'0';
+ if(c >= 'a' && c <= 'f')
+ return c-'a'+10;
+ if(c >= 'A' && c <= 'F')
+ return c-'A'+10;
+ return -1;
+}
+
+g8(s: string): int
+{
+ if(len s >= 2){
+ c0 := hexc(s[0]);
+ c1 := hexc(s[1]);
+ if(c0 >= 0 && c1 >= 0)
+ return (c0<<4) | c1;
+ }
+ return -1;
+}
+
+# S d len
+sdecode(s: string): (int, int, array of byte)
+{
+ while(len s > 0 && (s[len s-1] == '\r' || s[len s-1] == '\n'))
+ s = s[0:len s-1];
+ if(len s < 4 || s[0] != 'S')
+ badsrec(s);
+ l := g8(s[2:4]);
+ if(l < 0)
+ badsrec("length: "+s);
+ if(2*l != len s - 4)
+ badsrec("length: "+s);
+ csum := l;
+ na := 2;
+ if(s[1] >= '1' && s[1] <= '3')
+ na = s[1]-'1'+2;
+ addr := 0;
+ for(i:=0; i<na; i++){
+ b := g8(s[4+i*2:]);
+ if(b < 0)
+ badsrec(s);
+ csum += b;
+ addr = (addr << 8) | b;
+ }
+ case s[1] {
+ '0' or # used as segment name (seems to be srec file name with TinyOS)
+ '1' to '3' or # data
+ '5' or # plot so far
+ '7' to '9' => # end/start address
+ ;
+ * =>
+ badsrec("type: "+s);
+ }
+ data := array[l-na-1] of byte;
+ for(i = 0; i < len data; i++){
+ c := g8(s[4+(na+i)*2:]);
+ csum += c;
+ data[i] = byte c;
+ }
+ v := g8(s[4+l*2-2:]);
+ csum += v;
+ if((csum & 16rFF) != 16rFF)
+ badsrec("checksum: "+s);
+ return (s[1], addr, data);
+}
+
+#
+# serial port
+#
+
+Rd.new(fd: ref Sys->FD): ref Rd
+{
+ r := ref Rd(chan[4] of array of byte, 0, fd, nil);
+ c := chan of int;
+ spawn r.reader(c);
+ <-c;
+ return r;
+}
+
+Rd.reader(r: self ref Rd, c: chan of int)
+{
+ r.pid = sys->pctl(0, nil);
+ c <-= 1;
+ for(;;){
+ buf := array[258] of byte;
+ n := sys->read(r.fd, buf, len buf);
+ if(n <= 0){
+ r.pid = 0;
+ err(sys->sprint("read error: %r"));
+ }
+ if(debug)
+ sys->print("<- %s\n", dump(buf[0:n]));
+ r.c <-= buf[0:n];
+ }
+}
+
+Rd.read(r: self ref Rd, ms: int): array of byte
+{
+ if((a := r.buf) != nil){
+ r.buf = nil;
+ return a;
+ }
+ t := Timer.start(ms);
+ alt{
+ a = <-r.c =>
+ t.stop();
+ Acc:
+ for(;;){
+ sys->sleep(5);
+ alt{
+ b := <-r.c =>
+ if(b == nil)
+ break Acc;
+ a = cat(a, b);
+ * =>
+ break Acc;
+ }
+ }
+ return a;
+ <-t.timeout =>
+ return nil;
+ }
+}
+
+Rd.readn(r: self ref Rd, n: int, ms: int): array of byte
+{
+ a: array of byte;
+
+ while((need := n - len a) > 0){
+ b := r.read(ms);
+ if(b == nil)
+ break;
+ if(len b > need){
+ r.buf = b[need:];
+ b = b[0:need];
+ }
+ a = cat(a, b);
+ }
+ return a;
+}
+
+Rd.flush(r: self ref Rd)
+{
+ r.buf = nil;
+ sys->sleep(5);
+ for(;;){
+ alt{
+ <-r.c =>
+ ;
+ * =>
+ return;
+ }
+ }
+}
+
+Rd.stop(r: self ref Rd)
+{
+ pid := r.pid;
+ if(pid){
+ fd := sys->open("#p/"+string pid+"/ctl", Sys->OWRITE);
+ if(fd != nil)
+ sys->fprint(fd, "kill");
+ }
+}
+
+cat(a, b: array of byte): array of byte
+{
+ if(len b == 0)
+ return a;
+ if(len a == 0)
+ return b;
+ c := array[len a + len b] of byte;
+ c[0:] = a;
+ c[len a:] = b;
+ return c;
+}
+
+#
+# STK500 communication protocol
+#
+
+STK_SIGN_ON_MESSAGE: con "AVR STK"; # Sign on string for Cmd_STK_GET_SIGN_ON
+
+# Responses
+
+Resp_STK_OK: con byte 16r10;
+Resp_STK_FAILED: con byte 16r11;
+Resp_STK_UNKNOWN: con byte 16r12;
+Resp_STK_NODEVICE: con byte 16r13;
+Resp_STK_INSYNC: con byte 16r14;
+Resp_STK_NOSYNC: con byte 16r15;
+
+Resp_ADC_CHANNEL_ERROR: con byte 16r16;
+Resp_ADC_MEASURE_OK: con byte 16r17;
+Resp_PWM_CHANNEL_ERROR: con byte 16r18;
+Resp_PWM_ADJUST_OK: con byte 16r19;
+
+# Special constants
+
+Sync_CRC_EOP: con byte 16r20;
+
+# Commands
+
+Cmd_STK_GET_SYNC: con byte 16r30;
+Cmd_STK_GET_SIGN_ON: con byte 16r31;
+
+Cmd_STK_SET_PARAMETER: con byte 16r40;
+Cmd_STK_GET_PARAMETER: con byte 16r41;
+Cmd_STK_SET_DEVICE: con byte 16r42;
+Cmd_STK_SET_DEVICE_EXT: con byte 16r45;
+
+Cmd_STK_ENTER_PROGMODE: con byte 16r50;
+Cmd_STK_LEAVE_PROGMODE: con byte 16r51;
+Cmd_STK_CHIP_ERASE: con byte 16r52;
+Cmd_STK_CHECK_AUTOINC: con byte 16r53;
+Cmd_STK_LOAD_ADDRESS: con byte 16r55;
+Cmd_STK_UNIVERSAL: con byte 16r56;
+Cmd_STK_UNIVERSAL_MULTI: con byte 16r57;
+
+Cmd_STK_PROG_FLASH: con byte 16r60;
+Cmd_STK_PROG_DATA: con byte 16r61;
+Cmd_STK_PROG_FUSE: con byte 16r62;
+Cmd_STK_PROG_LOCK: con byte 16r63;
+Cmd_STK_PROG_PAGE: con byte 16r64;
+Cmd_STK_PROG_FUSE_EXT: con byte 16r65;
+
+Cmd_STK_READ_FLASH: con byte 16r70;
+Cmd_STK_READ_DATA: con byte 16r71;
+Cmd_STK_READ_FUSE: con byte 16r72;
+Cmd_STK_READ_LOCK: con byte 16r73;
+Cmd_STK_READ_PAGE: con byte 16r74;
+Cmd_STK_READ_SIGN: con byte 16r75;
+Cmd_STK_READ_OSCCAL: con byte 16r76;
+Cmd_STK_READ_FUSE_EXT: con byte 16r77;
+Cmd_STK_READ_OSCCAL_EXT: con byte 16r78;
+
+# Parameter constants
+
+Parm_STK_HW_VER: con byte 16r80; # ' ' - R
+Parm_STK_SW_MAJOR: con byte 16r81; # ' ' - R
+Parm_STK_SW_MINOR: con byte 16r82; # ' ' - R
+Parm_STK_LEDS: con byte 16r83; # ' ' - R/W
+Parm_STK_VTARGET: con byte 16r84; # ' ' - R/W
+Parm_STK_VADJUST: con byte 16r85; # ' ' - R/W
+Parm_STK_OSC_PSCALE: con byte 16r86; # ' ' - R/W
+Parm_STK_OSC_CMATCH: con byte 16r87; # ' ' - R/W
+Parm_STK_RESET_DURATION: con byte 16r88; # ' ' - R/W
+Parm_STK_SCK_DURATION: con byte 16r89; # ' ' - R/W
+
+Parm_STK_BUFSIZEL: con byte 16r90; # ' ' - R/W, Range {0..255}
+Parm_STK_BUFSIZEH: con byte 16r91; # ' ' - R/W, Range {0..255}
+Parm_STK_DEVICE: con byte 16r92; # ' ' - R/W, Range {0..255}
+Parm_STK_PROGMODE: con byte 16r93; # ' ' - 'P' or 'S'
+Parm_STK_PARAMODE: con byte 16r94; # ' ' - TRUE or FALSE
+Parm_STK_POLLING: con byte 16r95; # ' ' - TRUE or FALSE
+Parm_STK_SELFTIMED: con byte 16r96; # ' ' - TRUE or FALSE
+
+# status bits
+
+Stat_STK_INSYNC: con byte 16r01; # INSYNC status bit, '1' - INSYNC
+Stat_STK_PROGMODE: con byte 16r02; # Programming mode, '1' - PROGMODE
+Stat_STK_STANDALONE: con byte 16r04; # Standalone mode, '1' - SM mode
+Stat_STK_RESET: con byte 16r08; # RESET button, '1' - Pushed
+Stat_STK_PROGRAM: con byte 16r10; # Program button, ' 1' - Pushed
+Stat_STK_LEDG: con byte 16r20; # Green LED status, '1' - Lit
+Stat_STK_LEDR: con byte 16r40; # Red LED status, '1' - Lit
+Stat_STK_LEDBLINK: con byte 16r80; # LED blink ON/OFF, '1' - Blink
+
+ispmode := array[] of {byte 16rAA, byte 16r55, byte 16r55, byte 16rAA, byte 16r17, byte 16r51, byte 16r31, byte 16r13, byte 0}; # last byte is 1 to switch isp on 0 to switch off
+
+ck(r: array of byte)
+{
+ if(r == nil)
+ err("programming failed");
+}
+
+pgmon()
+{
+ ck(rpc(array[] of {Cmd_STK_ENTER_PROGMODE}, 0));
+}
+
+pgmoff()
+{
+ ck(rpc(array[] of {Cmd_STK_LEAVE_PROGMODE}, 0));
+}
+
+setisp0(on: int)
+{
+ rd.flush();
+ buf := array[len ispmode] of byte;
+ buf[0:] = ispmode;
+ buf[8] = byte on;
+ sys->write(sfd, buf, len buf);
+}
+
+setisp(on: int): int
+{
+ rd.flush();
+ buf := array[len ispmode] of byte;
+ buf[0:] = ispmode;
+ buf[8] = byte on;
+ r := send(buf, 2);
+ return len r == 2 && ok(r);
+}
+
+readsig(): array of byte
+{
+ r := send(array[] of {Cmd_STK_READ_SIGN, Sync_CRC_EOP}, 5);
+ # doesn't behave as documented in AVR061: it repeats the command bytes instead
+ if(len r != 5 || r[0] != Cmd_STK_READ_SIGN || r[4] != Sync_CRC_EOP){
+ sys->fprint(sys->fildes(2), "bad reply %s\n", dump(r));
+ return nil;
+ }
+ return r[1:len r-1]; # trim proto bytes
+}
+
+pgrpc(a: array of byte, repn: int): array of byte
+{
+ pgmon();
+ r := rpc(a, repn);
+ pgmoff();
+ return r;
+}
+
+eop := array[] of {Sync_CRC_EOP};
+
+rpc(a: array of byte, repn: int): array of byte
+{
+ r := send(cat(a, eop), repn+2);
+ if(!ok(r)){
+ if(len r >= 2 && r[0] == Resp_STK_INSYNC && r[len r-1] == Resp_STK_NODEVICE)
+ err("internal error: programming parameters not correctly set");
+ if(len r >= 1 && r[0] == Resp_STK_NOSYNC)
+ err("lost synchronisation");
+ sys->fprint(sys->fildes(2), "bad reply %s\n", dump(r));
+ return nil;
+ }
+ return r[1:len r-1]; # trim sync bytes
+}
+
+send(a: array of byte, repn: int): array of byte
+{
+ if(debug)
+ sys->print("-> %s\n", dump(a));
+ if(sys->write(sfd, a, len a) != len a)
+ err(sys->sprint("write error: %r"));
+ return rd.readn(repn, 2000);
+}
+
+ok(r: array of byte): int
+{
+ return len r >= 2 && r[0] == Resp_STK_INSYNC && r[len r -1] == Resp_STK_OK;
+}
+
+universal(req: array of byte): int
+{
+ r := pgrpc(cat(array[] of {Cmd_STK_UNIVERSAL}, req), 1);
+ if(r == nil)
+ return -1;
+ return int r[0];
+}
+
+setdevice(d: Avr)
+{
+ b := array[] of {
+ Cmd_STK_SET_DEVICE,
+ byte d.id,
+ byte d.rev,
+ byte 0, # prog type (CHECK)
+ byte d.fullpar,
+ byte d.polling,
+ byte d.selftimed,
+ byte d.lockbytes,
+ byte d.fusebytes,
+ byte d.fpoll,
+ byte d.fpoll,
+ byte d.epoll1,
+ byte d.epoll2,
+ byte (d.pagesize >> 8), byte d.pagesize,
+ byte (d.eepromsize>>8), byte d.eepromsize,
+ byte (d.flashsize>>24), byte (d.flashsize>>16), byte (d.flashsize>>8), byte d.flashsize
+ };
+ ck(rpc(b, 0));
+ if(mib510)
+ return;
+ b = array[] of {
+ Cmd_STK_SET_DEVICE_EXT,
+ byte 4,
+ byte d.eeprompagesize,
+ byte d.signalpagel,
+ byte d.signalbs2,
+ byte 0 # ResetDisable
+ };
+ ck(rpc(b, 0));
+}
+
+chiperase()
+{
+ ck(pgrpc(array[] of {Cmd_STK_CHIP_ERASE}, 0));
+}
+
+readfuselow(): int
+{
+ return universal(array[] of {byte 16r50, byte 0, byte 0, byte 0});
+}
+
+readfusehigh(): int
+{
+ return universal(array[] of {byte 16r58, byte 8, byte 0, byte 0});
+}
+
+readfuseext(): int
+{
+ return universal(array[] of {byte 16r50, byte 8, byte 0, byte 0});
+}
+
+readlockfuse(): int
+{
+ return universal(array[] of {byte 16r58, byte 0, byte 0, byte 0});
+}
+
+readflashpage(addr: int, nb: int): array of byte
+{
+ return readmem('F', addr/2, nb);
+}
+
+readeeprompage(addr: int, nb: int): array of byte
+{
+ return readmem('E', addr, nb);
+}
+
+readmem(memtype: int, addr: int, nb: int): array of byte
+{
+ if(nb > 256)
+ nb = 256;
+ pgmon();
+ r := rpc(array[] of {Cmd_STK_LOAD_ADDRESS, byte addr, byte (addr>>8)}, 0);
+ if(r != nil){
+ r = send(array[] of {Cmd_STK_READ_PAGE, byte (nb>>8), byte nb, byte memtype, Sync_CRC_EOP}, nb+2);
+ l := len r;
+ # AVR601 says last byte should be Resp_STK_OK but it's not, at least on MIB; check for both
+ if(l >= 2 && r[0] == Resp_STK_INSYNC && (r[l-1] == Resp_STK_INSYNC || r[l-1] == Resp_STK_OK))
+ r = r[1:l-1]; # trim framing bytes
+ else{
+ sys->print("bad reply: %s\n", dump(r));
+ r = nil;
+ }
+ if(len r < nb)
+ sys->print("short [%d@%4.4ux]\n", nb, addr);
+ }
+ pgmoff();
+ return r;
+}
+
+writeflashpage(addr: int, data: array of byte): int
+{
+ return writemem('F', addr/2, data);
+}
+
+writeeeprompage(addr: int, data: array of byte): int
+{
+ return writemem('E', addr, data);
+}
+
+writemem(memtype: int, addr: int, data: array of byte): int
+{
+ nb := len data;
+ if(nb > 256){
+ nb = 256;
+ data = data[0:nb];
+ }
+ pgmon();
+ r := rpc(array[] of {Cmd_STK_LOAD_ADDRESS, byte addr, byte (addr>>8)}, 0);
+ if(r != nil){
+ r = rpc(cat(array[] of {Cmd_STK_PROG_PAGE, byte (nb>>8), byte nb, byte memtype},data), 0);
+ if(r == nil)
+ nb = -1;
+ }
+ pgmoff();
+ return nb;
+}
+
+writefuseext(v: int): int
+{
+ return universal(array[] of {byte 16rAC, byte 16rA4, byte 16rFF, byte v});
+}
+
+writefuselow(v: int): int
+{
+ return universal(array[] of {byte 16rAC, byte 16rA0, byte 16rFF, byte v});
+}
+
+writefusehigh(v: int): int
+{
+ return universal(array[] of {byte 16rAC, byte 16rA8, byte 16rFF, byte v});
+}