5a7ab02de3c34678f6841d2ab0c43744bcfa7f40
[goodfet] / client / GoodFET.py
1 #!/usr/bin/env python
2 # GoodFET Client Library
3
4 # (C) 2009 Travis Goodspeed <travis at radiantmachines.com>
5 #
6 # This code is ugly as sin, for bootstrapping the firmware only.
7 # Rewrite cleanly as soon as is convenient.
8
9 import sys, time, string, cStringIO, struct, glob, serial, os;
10
11
12 class GoodFET:
13     def __init__(self, *args, **kargs):
14         self.data=[0];
15     def timeout(self):
16         print "timeout\n";
17     def serInit(self, port=None):
18         """Open the serial port"""
19         
20         if port is None and os.environ.get("GOODFET")!=None:
21             glob_list = glob.glob(os.environ.get("GOODFET"));
22             if len(glob_list) > 0:
23                 port = glob_list[0];
24         if port is None:
25             glob_list = glob.glob("/dev/tty.usbserial*");
26             if len(glob_list) > 0:
27                 port = glob_list[0];
28         if port is None:
29             glob_list = glob.glob("/dev/ttyUSB*");
30             if len(glob_list) > 0:
31                 port = glob_list[0];
32         
33         self.serialport = serial.Serial(
34             port,
35             #9600,
36             115200,
37             parity = serial.PARITY_NONE
38             )
39         #Drop DTR, which is !RST, low to begin the app.
40         self.serialport.setDTR(0);
41         self.serialport.flushInput()
42         self.serialport.flushOutput()
43         
44         #Read and handle the initial command.
45         #time.sleep(1);
46         self.readcmd(); #Read the first command.
47         if(self.verb!=0x7F):
48             print "Verb %02x is wrong.  Incorrect firmware?" % self.verb;
49         #print "Connected."
50     def writecmd(self, app, verb, count, data=[], blocks=1):
51         """Write a command and some data to the GoodFET."""
52         self.serialport.write(chr(app));
53         self.serialport.write(chr(verb));
54         self.serialport.write(chr(count));
55         #print "count=%02x, len(data)=%04x" % (count,len(data));
56         if count!=0:
57             for d in data:
58                 self.serialport.write(chr(d));
59         
60         self.readcmd(blocks);  #Uncomment this later, to ensure a response.
61     def readcmd(self,blocks=1):
62         """Read a reply from the GoodFET."""
63         self.app=ord(self.serialport.read(1));
64         self.verb=ord(self.serialport.read(1));
65         self.count=ord(self.serialport.read(1));
66         self.data=self.serialport.read(self.count*blocks);
67         return self.data;
68         #print "READ %02x %02x %02x " % (self.app, self.verb, self.count);
69         
70     #Monitor stuff
71     def peekbyte(self,address):
72         """Read a byte of memory from the monitor."""
73         self.data=[address&0xff,address>>8];
74         self.writecmd(0,0x02,2,self.data);
75         #self.readcmd();
76         return ord(self.data[0]);
77     def peekword(self,address):
78         """Read a word of memory from the monitor."""
79         return self.peekbyte(address)+(self.peekbyte(address+1)<<8);
80     def pokebyte(self,address,value):
81         """Set a byte of memory by the monitor."""
82         self.data=[address&0xff,address>>8,value];
83         self.writecmd(0,0x03,3,self.data);
84         return ord(self.data[0]);
85     def dumpmem(self,begin,end):
86         i=begin;
87         while i<end:
88             print "%04x %04x" % (i, self.peekword(i));
89             i+=2;
90     def monitor_ram_pattern(self):
91         """Overwrite all of RAM with 0xBEEF."""
92         self.writecmd(0,0x90,0,self.data);
93         return;
94     def monitor_ram_depth(self):
95         """Determine how many bytes of RAM are unused by looking for 0xBEEF.."""
96         self.writecmd(0,0x91,0,self.data);
97         return ord(self.data[0])+(ord(self.data[1])<<8);
98     
99     #Baud rates.
100     baudrates=[115200, 
101                9600,
102                19200,
103                38400,
104                57600,
105                115200];
106     def setBaud(self,baud):
107         """Change the baud rate.  TODO fix this."""
108         rates=self.baudrates;
109         self.data=[baud];
110         print "Changing FET baud."
111         self.serialport.write(chr(0x00));
112         self.serialport.write(chr(0x80));
113         self.serialport.write(chr(1));
114         self.serialport.write(chr(baud));
115         
116         print "Changed host baud."
117         self.serialport.setBaudrate(rates[baud]);
118         time.sleep(1);
119         self.serialport.flushInput()
120         self.serialport.flushOutput()
121         
122         print "Baud is now %i." % rates[baud];
123         return;
124     def readbyte(self):
125         return ord(self.serialport.read(1));
126     def findbaud(self):
127         for r in self.baudrates:
128             print "\nTrying %i" % r;
129             self.serialport.setBaudrate(r);
130             #time.sleep(1);
131             self.serialport.flushInput()
132             self.serialport.flushOutput()
133             
134             for i in range(1,10):
135                 self.readbyte();
136             
137             print "Read %02x %02x %02x %02x" % (
138                 self.readbyte(),self.readbyte(),self.readbyte(),self.readbyte());
139     def monitortest(self):
140         """Self-test several functions through the monitor."""
141         print "Performing monitor self-test.";
142         
143         if self.peekword(0x0c00)!=0x0c04 and self.peekword(0x0c00)!=0x0c06:
144             print "ERROR Fetched wrong value from 0x0c04.";
145         self.pokebyte(0x0021,0); #Drop LED
146         if self.peekbyte(0x0021)!=0:
147             print "ERROR, P1OUT not cleared.";
148         self.pokebyte(0x0021,1); #Light LED
149         
150         print "Self-test complete.";
151     
152     def SPIsetup(self):
153         """Moved the FET into the SPI application."""
154         self.writecmd(0x01,0x10,0,self.data); #SPI/SETUP
155     
156         
157     def SPItrans8(self,byte):
158         """Read and write 8 bits by SPI."""
159         data=self.SPItrans([byte]);
160         return ord(data[0]);
161     
162     def SPItrans(self,data):
163         """Exchange data by SPI."""
164         self.data=data;
165         self.writecmd(0x01,0x00,len(data),data);
166         return self.data;
167     
168     JEDECmanufacturers={0xFF: "MISSING",
169                         0xEF: "Winbond",
170                         0xC2: "MXIC",
171                         0x20: "Numonyx/ST"
172                         };
173
174     JEDECdevices={0xFFFFFF: "MISSING",
175                   0xEF3014: "W25X80L",
176                   0xEF3013: "W25X40L",
177                   0xEF3012: "W25X20L",
178                   0xEF3011: "W25X10L",
179                   0xC22014: "MX25L8005",
180                   0xC22013: "MX25L4005",
181                   0x204011: "M45PE10"
182                   };
183     JEDECsizes={0x14: 0x100000,
184                 0x13: 0x080000,
185                 0x12: 0x040000,
186                 0x11: 0x020000}
187     JEDECsize=0;
188     def SPIjedec(self):
189         """Grab an SPI Flash ROM's JEDEC bytes."""
190         data=[0x9f, 0, 0, 0];
191         data=self.SPItrans(data);
192         #print "Manufacturer: %02x\nType: %02x\nCapacity: %02x" % (ord(data[1]),ord(data[2]),ord(data[3]));
193         self.JEDECmanufacturer=ord(data[1]);
194         self.JEDECtype=ord(data[2]);
195         self.JEDECcapacity=ord(data[3]);
196         self.JEDECsize=self.JEDECsizes.get(self.JEDECcapacity);
197         if self.JEDECsize==None:
198             self.JEDECsize=0;
199         self.JEDECdevice=(ord(data[1])<<16)+(ord(data[2])<<8)+ord(data[3]);
200         return data;
201     def SPIpeek(self,adr):
202         """Grab a byte from an SPI Flash ROM."""
203         data=[0x03,
204               (adr&0xFF0000)>>16,
205               (adr&0xFF00)>>8,
206               adr&0xFF,
207               0];
208         self.SPItrans(data);
209         return ord(self.data[4]);
210 #     def SPIpeekblock(self,adr):
211 #         """Grab a block from an SPI Flash ROM.  Block size is unknown"""
212 #         data=[(adr&0xFF0000)>>16,
213 #               (adr&0xFF00)>>8,
214 #               adr&0xFF];
215         
216 #         self.writecmd(0x01,0x02,3,data);
217 #         return self.data;
218     def SPIpeekblock(self,adr,blocks=1):
219         """Grab a few block from an SPI Flash ROM.  Block size is unknown"""
220         data=[(adr&0xFF0000)>>16,
221               (adr&0xFF00)>>8,
222               adr&0xFF,
223               blocks];
224         
225         self.writecmd(0x01,0x02,4,data,blocks);
226         return self.data;
227     
228     def SPIpokebyte(self,adr,val):
229         self.SPIpokebytes(adr,[val]);
230     def SPIpokebytes(self,adr,data):
231         #self.SPIwriteenable();
232         adranddata=[(adr&0xFF0000)>>16,
233               (adr&0xFF00)>>8,
234               adr&0xFF
235               ]+data;
236         self.writecmd(0x01,0x03,
237                       len(adranddata),adranddata);
238         
239     def SPIchiperase(self):
240         """Mass erase an SPI Flash ROM."""
241         self.writecmd(0x01,0x81,0,[]);
242     def SPIwriteenable(self):
243         """SPI Flash Write Enable"""
244         data=[0x06];
245         self.SPItrans(data);
246         
247     def SPIjedecmanstr(self):
248         """Grab the JEDEC manufacturer string.  Call after SPIjedec()."""
249         man=self.JEDECmanufacturers.get(self.JEDECmanufacturer)
250         if man==0:
251             man="UNKNOWN";
252         return man;
253     
254     def SPIjedecstr(self):
255         """Grab the JEDEC manufacturer string.  Call after SPIjedec()."""
256         man=self.JEDECmanufacturers.get(self.JEDECmanufacturer);
257         if man==0:
258             man="UNKNOWN";
259         device=self.JEDECdevices.get(self.JEDECdevice);
260         if device==0:
261             device="???"
262         return "%s %s" % (man,device);
263     def MSP430setup(self):
264         """Move the FET into the MSP430 JTAG application."""
265         print "Initializing MSP430.";
266         self.writecmd(0x11,0x10,0,self.data);
267
268     def I2Csetup(self):
269         """Move the FET into the I2C application."""
270         self.writecmd(0x02,0x10,0,self.data); #SPI/SETUP
271     def I2Cstart(self):
272         """Start an I2C transaction."""
273         self.writecmd(0x02,0x20,0,self.data); #SPI/SETUP
274     def I2Cstop(self):
275         """Stop an I2C transaction."""
276         self.writecmd(0x02,0x21,0,self.data); #SPI/SETUP
277     def I2Cread(self,len=1):
278         """Read len bytes by I2C."""
279         self.writecmd(0x02,0x00,1,[len]); #SPI/SETUP
280         return self.data;
281     def I2Cwrite(self,bytes):
282         """Write bytes by I2C."""
283         self.writecmd(0x02,0x01,len(bytes),bytes); #SPI/SETUP
284         return ord(self.data[0]);
285     def CCsetup(self):
286         """Move the FET into the CC2430/CC2530 application."""
287         #print "Initializing Chipcon.";
288         self.writecmd(0x30,0x10,0,self.data);
289     def CCrd_config(self):
290         """Read the config register of a Chipcon."""
291         self.writecmd(0x30,0x82,0,self.data);
292         return ord(self.data[0]);
293     def CCwr_config(self,config):
294         """Write the config register of a Chipcon."""
295         self.writecmd(0x30,0x81,1,[config&0xFF]);
296     
297     CCversions={0x0100:"CC1110",
298                 0x8500:"CC2430",
299                 0x8900:"CC2431",
300                 0x8100:"CC2510",
301                 0x9100:"CC2511",
302                 0xFF00:"CCmissing"};
303     def CCidentstr(self):
304         ident=self.CCident();
305         chip=self.CCversions.get(ident&0xFF00);
306         return "%s/r%02x" % (chip, ident&0xFF); 
307     def CCident(self):
308         """Get a chipcon's ID."""
309         self.writecmd(0x30,0x8B,0,None);
310         chip=ord(self.data[0]);
311         rev=ord(self.data[1]);
312         return (chip<<8)+rev;
313     def CCgetPC(self):
314         """Get a chipcon's PC."""
315         self.writecmd(0x30,0x83,0,None);
316         hi=ord(self.data[0]);
317         lo=ord(self.data[1]);
318         return (hi<<8)+lo;
319     def CCdebuginstr(self,instr):
320         self.writecmd(0x30,0x88,len(instr),instr);
321         return ord(self.data[0]);
322     def MSP430peek(self,adr):
323         """Read the contents of memory at an address."""
324         self.data=[adr&0xff, (adr&0xff00)>>8];
325         self.writecmd(0x11,0x02,2,self.data);
326         return ord(self.data[0])+(ord(self.data[1])<<8);
327     def CCpeekcodebyte(self,adr):
328         """Read the contents of code memory at an address."""
329         self.data=[adr&0xff, (adr&0xff00)>>8];
330         self.writecmd(0x30,0x90,2,self.data);
331         return ord(self.data[0]);
332     def CCpeekdatabyte(self,adr):
333         """Read the contents of data memory at an address."""
334         self.data=[adr&0xff, (adr&0xff00)>>8];
335         self.writecmd(0x30,0x91, 2, self.data);
336         return ord(self.data[0]);
337     def CCpokedatabyte(self,adr,val):
338         """Write a byte to data memory."""
339         self.data=[adr&0xff, (adr&0xff00)>>8, val];
340         self.writecmd(0x30, 0x92, 3, self.data);
341         return ord(self.data[0]);
342     def CCchiperase(self):
343         """Erase all of the target's memory."""
344         self.writecmd(0x30,0x80,0,None);
345     def CCstatus(self):
346         """Check the status."""
347         self.writecmd(0x30,0x84,0,None);
348         return ord(self.data[0])
349     CCstatusbits={0x80 : "erased",
350                   0x40 : "pcon_idle",
351                   0x20 : "halted",
352                   0x10 : "pm0",
353                   0x08 : "halted",
354                   0x04 : "locked",
355                   0x02 : "oscstable",
356                   0x01 : "overflow"};
357     def CCstatusstr(self):
358         """Check the status as a string."""
359         status=self.CCstatus();
360         str="";
361         i=1;
362         while i<0x100:
363             if(status&i):
364                 str="%s %s" %(self.CCstatusbits[i],str);
365             i*=2;
366         return str;
367     def MSP430poke(self,adr,val):
368         """Read the contents of memory at an address."""
369         self.data=[adr&0xff, (adr&0xff00)>>8, val&0xff, (val&0xff00)>>8];
370         self.writecmd(0x11,0x03,4,self.data);
371         return;# ord(self.data[0])+(ord(self.data[1])<<8);
372     def MSP430start(self):
373         """Start debugging."""
374         self.writecmd(0x11,0x20,0,self.data);
375         ident=self.MSP430ident();
376         print "Target identifies as %04x." % ident;
377     
378     def CCstart(self):
379         """Start debugging."""
380         self.writecmd(0x30,0x20,0,self.data);
381         ident=self.CCidentstr();
382         print "Target identifies as %s." % ident;
383         #print "Status: %s." % self.CCstatusstr();
384         self.CCreleasecpu();
385         self.CChaltcpu();
386         #print "Status: %s." % self.CCstatusstr();
387         
388     def CCstop(self):
389         """Stop debugging."""
390         self.writecmd(0x30,0x21,0,self.data);
391     def CCstep_instr(self):
392         """Step one instruction."""
393         self.writecmd(0x30,0x89,0,self.data);
394     def MSP430stop(self):
395         """Stop debugging."""
396         self.writecmd(0x11,0x21,0,self.data);
397     def MSP430haltcpu(self):
398         """Halt the CPU."""
399         self.writecmd(0x11,0xA0,0,self.data);
400     def MSP430releasecpu(self):
401         """Resume the CPU."""
402         self.writecmd(0x11,0xA1,0,self.data);
403     def CChaltcpu(self):
404         """Halt the CPU."""
405         self.writecmd(0x30,0x86,0,self.data);
406     def CCreleasecpu(self):
407         """Resume the CPU."""
408         self.writecmd(0x30,0x87,0,self.data);
409     def MSP430shiftir8(self,ins):
410         """Shift the 8-bit Instruction Register."""
411         data=[ins];
412         self.writecmd(0x11,0x80,1,data);
413         return ord(self.data[0]);
414     def MSP430shiftdr16(self,dat):
415         """Shift the 16-bit Data Register."""
416         data=[dat&0xFF,(dat&0xFF00)>>8];
417         self.writecmd(0x11,0x81,2,data);
418         return ord(self.data[0])#+(ord(self.data[1])<<8);
419     def MSP430setinstrfetch(self):
420         """Set the instruction fetch mode."""
421         self.writecmd(0x11,0xC1,0,self.data);
422         return self.data[0];
423     def MSP430ident(self):
424         """Grab self-identification word from 0x0FF0 as big endian."""
425         i=self.MSP430peek(0x0ff0);
426         return ((i&0xFF00)>>8)+((i&0xFF)<<8)
427     def MSP430test(self):
428         """Test MSP430 JTAG.  Requires that a chip be attached."""
429         if self.MSP430ident()==0xffff:
430             print "Is anything connected?";
431         print "Testing RAM.";
432         temp=self.MSP430peek(0x0200);
433         self.MSP430poke(0x0200,0xdead);
434         if(self.MSP430peek(0x0200)!=0xdead):
435             print "Poke of 0x0200 did not set to 0xDEAD properly.";
436             return;
437         self.MSP430poke(0x0200,temp); #restore old value.
438     def MSP430flashtest(self):
439         self.MSP430masserase();
440         i=0x2500;
441         while(i<0xFFFF):
442             if(self.MSP430peek(i)!=0xFFFF):
443                 print "ERROR: Unerased flash at %04x."%i;
444             self.MSP430writeflash(i,0xDEAD);
445             i+=2;
446     def MSP430masserase(self):
447         """Erase MSP430 flash memory."""
448         self.writecmd(0x11,0xE3,0,None);
449     def MSP430writeflash(self,adr,val):
450         """Write a word of flash memory."""
451         if(self.MSP430peek(adr)!=0xFFFF):
452             print "FLASH ERROR: %04x not clear." % adr;
453         data=[adr&0xFF,(adr&0xFF00)>>8,val&0xFF,(val&0xFF00)>>8];
454         self.writecmd(0x11,0xE1,4,data);
455         rval=ord(self.data[0])+(ord(self.data[1])<<8);
456         if(val!=rval):
457             print "FLASH WRITE ERROR AT %04x.  Found %04x, wrote %04x." % (adr,rval,val);
458             
459     def MSP430dumpbsl(self):
460         self.MSP430dumpmem(0xC00,0xfff);
461     def MSP430dumpallmem(self):
462         self.MSP430dumpmem(0x200,0xffff);
463     def MSP430dumpmem(self,begin,end):
464         i=begin;
465         while i<end:
466             print "%04x %04x" % (i, self.MSP430peek(i));
467             i+=2;
468     def CCtest(self):
469         self.CCreleasecpu();
470         self.CChaltcpu();
471         #print "Status: %s" % self.CCstatusstr();
472         
473         #Grab ident three times, should be equal.
474         ident1=self.CCident();
475         ident2=self.CCident();
476         ident3=self.CCident();
477         if(ident1!=ident2 or ident2!=ident3):
478             print "Error, repeated ident attempts unequal."
479             print "%04x, %04x, %04x" % (ident1, ident2, ident3);
480         
481         #Single step, printing PC.
482         print "Tracing execution at startup."
483         for i in range(1,15):
484             pc=self.CCgetPC();
485             byte=self.CCpeekcodebyte(i);
486             #print "PC=%04x, %02x" % (pc, byte);
487             self.CCstep_instr();
488         
489         print "Verifying that debugging a NOP doesn't affect the PC."
490         for i in range(1,15):
491             pc=self.CCgetPC();
492             self.CCdebuginstr([0x00]);
493             if(pc!=self.CCgetPC()):
494                 print "ERROR: PC changed during CCdebuginstr([NOP])!";
495         
496         
497         #print "Status: %s." % self.CCstatusstr();
498         #Exit debugger
499         self.CCstop();
500         print "Done.";