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