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