Chipcon mode for sniffing with radio settings
[goodfet] / client / goodfet.pic
1 #!/usr/bin/env python
2 # PIC client (currently only supports dsPIC33F/PIC24H.)
3 #
4 # Note that a verify operation only considers addresses specified in
5 # the given Intel hex file ($foo.hex).
6
7 # sixfile expects a plain text file, one instruction per line.
8 #
9 #
10 # Scott Livingston  <slivingston at caltech.edu>
11 #
12 # March-June 2010.
13
14
15 import sys
16 import time
17
18 from GoodFET import GoodFET
19 from intelhex import IntelHex
20
21 #############
22 # Some constants (not really immutable...)
23 #############
24 PICAPP = 0x34
25 MONITORAPP = 0x00
26 NOK = 0x7E
27 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
28               0x00EF : "dsPIC33FJ128GP710",
29               0x080A : "PIC24HJ12GP201",
30               0x080B : "PIC24HJ12GP202",
31               0x0444 : "PIC24FJ16GA002",
32               0x044C : "PIC24FJ16GA004",
33               0x0445 : "PIC24FJ32GA002",
34               0x044D : "PIC24FJ32GA004",
35               0x0446 : "PIC24FJ48GA002",
36               0x044E : "PIC24FJ48GA004",
37               0x0447 : "PIC24FJ64GA002",
38               0x044F : "PIC24FJ64GA004",
39               0x0405 : "PIC24FJ64GA006",
40               0x0408 : "PIC24FJ64GA008",
41               0x040B : "PIC24FJ64GA010",
42               0x0406 : "PIC24FJ96GA006",
43               0x0409 : "PIC24FJ96GA008",
44               0x040C : "PIC24FJ96GA010",
45               0x0407 : "PIC24FJ128GA006",
46               0x040A : "PIC24FJ128GA008",
47               0x040D : "PIC24FJ128GA010" }
48 cfg_table = { 0xF80000 : "FBS",
49               0xF80002 : "FSS",
50               0xF80004 : "FGS",
51               0xF80006 : "FOSCSEL",
52               0xF80008 : "FOSC",
53               0xF8000A : "FWDT",
54               0xF8000C : "FPOR",
55               0xF8000E : "FICD",
56               0xF80010 : "FUID0",
57               0xF80012 : "FUID1",
58               0xF80014 : "FUID2",
59               0xF80016 : "FUID3",
60               "width" : 7 } # For pretty printing.
61
62 #cfg_bitmask_table = { 
63
64
65 #############
66 # Functions:
67 #############
68
69 def runlist( cmd_li ):
70     """Load (and execute) a given list of instructions.
71 Assumes ICSP session already started."""
72     cmd_byte_li = build_instr_stream( cmd_li )
73     data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
74     if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
75         print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
76         stopICSP()
77         exit(-1)
78     elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
79         print "Error (in runlist): failed transaction; aborting."
80         stopICSP()
81         exit(-1)
82
83 def build_instr_stream( cmd_li ):
84     """Given a list of instruction words, returns a list of bytes of
85 the same, in little endian ordering."""
86     cmd_byte_li = []
87     for instr in cmd_li:
88         cmd_byte_li += [instr & 0xff,
89                         (instr >> 8) & 0xff,
90                         (instr >> 16) & 0xff]
91     return cmd_byte_li
92
93 def readreg( reg_num ):
94     """Read contents of a working register (i.e. W0, W1, ..., W15)."""
95     instr = 0x883C20+(reg_num&0xf)
96     client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
97                                        (instr >> 8) & 0xff,
98                                        (instr >> 16) & 0xff] )
99     client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
100     return readVISI()
101
102 def writereg( reg_num, word ):
103     """Write 16-bit word to a working register (i.e., W0, W1, ..., W15)."""
104     instr = 0x200000 + ((word&0xffff)<<4) + (reg_num&0xf)
105     client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
106                                        (instr >> 8) & 0xff,
107                                        (instr >> 16) & 0xff] )
108
109 def readVISI():
110     """Read VISI register; assumes ICSP session already started."""
111     data = client.writecmd( PICAPP, 0x83, 0 )
112     result = ord(data[0])
113     result |= ord(data[1]) << 8
114     return result
115
116 def readNVMCON():
117     """Read NVMCON register; assumes ICSP session already started."""
118     rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
119                     0x883C20, # MOV W0, VISI
120                     0x000000, # NOP
121                     0x000000] # NOP
122     runlist( rd_nvmcon_li )
123     return readVISI()
124
125 def resetPC():
126     """Reset program counter during an active ISCP session."""
127     runlist( [0x040200,   # GOTO 0x0200
128               0x000000] )
129
130 def startICSP():
131     #print "Starting dsPIC33F/PIC24H ICSP session..."
132     data = client.writecmd( PICAPP, 0x84, 0 )
133
134 def stopICSP():
135     #print "Stopping dsPIC33F/PIC24H ICSP session..."
136     data = client.writecmd( PICAPP, 0x85, 0 )
137
138 def dumpVISI():
139     """Read and print VISI register to stdout; assumes ICSP session already started."""
140     print "Reading VISI register..."
141     result = readVISI()
142     print "VISI: 0x%04X" % result
143
144 def dump_pm( start_addr, stop_addr, pretty=False ):
145     """Dump routine, now encapsulated in a function.
146 Returns an instance of IntelHex corresponding to result.
147
148 Note that we start and stop an ICSP session here! This means an
149 existing session will be broken."""
150     readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
151                      0x880190, # MOV W0, TBLPAG
152                      0x200006, # MOV #addr<15:0>, W6
153                      0xEB0380, # CLR W7
154                      0x000000, # NOP
155                      0xBA1B96, # TBLRDL [W6], [W7++]
156                      0x000000, # NOP
157                      0x000000, # NOP
158                      0xBADBB6, # TBLRDH.B [W6++], [W7++]
159                      0x000000, # NOP
160                      0x000000, # NOP
161                      0xBADBD6, # TBLRDH.B [++W6], [W7++]
162                      0x000000, # NOP
163                      0x000000, # NOP
164                      0xBA1BB6, # TBLRDL [W6++], [W7++]
165                      0x000000, # NOP
166                      0x000000, # NOP
167                      0xBA1B96, # TBLRDL [W6], [W7++]
168                      0x000000, # NOP
169                      0x000000, # NOP
170                      0xBADBB6, # TBLRDH.B [W6++], [W7++]
171                      0x000000, # NOP
172                      0x000000, # NOP
173                      0xBADBD6, # TBLRDH.B [++W6], [W7++]
174                      0x000000, # NOP
175                      0x000000, # NOP
176                      0xBA0BB6, # TBLRDL [W6++], [W7]
177                      0x000000, # NOP
178                      0x000000] # NOP
179
180     dumphex = IntelHex()
181
182     startICSP()
183
184     print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
185     # Prep device
186     client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
187     client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
188     client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
189
190     last_highb = -1 # Only set TBLPAG when needed.
191     packed_instr_list = [0,0,0,0,0,0] # For packing 4 (24-bit) instructions in 6 (16-bit) words
192     for addr in range( start_addr&0xfffff8, stop_addr+8, 8 ):
193         if (addr>>16)&0xff != last_highb: 
194             last_highb = (addr>>16)&0xff;
195             specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
196                                 readpm_cmd_li[1],
197                                 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
198         else:
199             specify_addr_cmd = [readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
200         runlist( specify_addr_cmd + readpm_cmd_li[3:] )
201
202         for reg_num in range(6): # Read W0:5, to get packed instructions
203             packed_instr_list[reg_num] = readreg( reg_num )
204         instr_list = words2instr( packed_instr_list )
205
206         for offset in range(4): # Print result
207             if addr+offset*2 < start_addr:
208                 continue
209             if addr+offset*2 > stop_addr:
210                 break
211             dumphex.puts( ((addr+offset*2)&0xffffff)*2,
212                           chr(instr_list[offset]&0xff)
213                           +chr((instr_list[offset]>>8)&0xff)
214                           +chr((instr_list[offset]>>16)&0xff)
215                           +chr(0) )
216             if pretty:
217                 print "0x%06X    0x%06X" % (addr+offset*2,instr_list[offset])
218
219     stopICSP()
220
221     return dumphex
222
223     
224 def instr2words( instr_list ):
225     """Convert a list of 4 24-bit instructions to a list of 6 words
226 (16-bit width).
227
228 Returns [-1] on failure."""
229     if len(instr_list) < 4: # Catch mistakes
230         print "Error in instr2words: operand has less than 4 elements."
231         return [-1]
232     word_list = [0,0,0,0,0,0]
233     for k in [0,1]:
234         word_list[k*3] = instr_list[k*2] & 0xffff
235         word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
236         word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
237         word_list[k*3+2] = instr_list[k*2+1] & 0xffff
238     return word_list
239
240 def words2instr( word_list ):
241     """4 24-bit instructions from a packing in 6 words (16 bit width).
242 This is the inverse of function instr2words.
243
244 Returns [-1] on failure."""
245     if len(word_list) < 6: # Catch mistakes
246         print "Error in words2instr: operand has less than 6 elements."
247         return [-1]
248     instr_list = [0,0,0,0]
249     for k in [0,1]:
250         instr_list[k*2] = word_list[k*3]
251         instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
252         instr_list[k*2+1] = word_list[k*3+2]
253         instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
254     return instr_list
255
256
257 #############
258 # Main entry: 
259 #############
260
261 if len(sys.argv) == 1:
262     print "Usage: %s verb [objects]\n" % sys.argv[0]
263     print "%s devid" % sys.argv[0]
264     print "%s read 0x$addr" % sys.argv[0]
265     print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
266     print "%s config" % sys.argv[0]
267     print "%s reset" % sys.argv[0]
268     print "%s program $foo.hex" % sys.argv[0]
269     print "%s verify $foo.hex" % sys.argv[0]
270     print "%s write 0x$address 0x$value" % sys.argv[0]
271     print "%s write_config 0x$reg_address (or $reg_name) [$0x0000]" % sys.argv[0]
272     print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
273     print "%s six [instruction]" % sys.argv[0]
274     print "%s sixfile [$foo.txt]" % sys.argv[0]
275     print "%s regout" % sys.argv[0]
276     print """
277 Note: use - for stdout.
278 Warning: only formally supports dsPIC33F/PIC24H,
279          but read/write flash memory works with PIC24F ...
280 """
281     sys.exit()
282
283 # Initialize and open connection to GoodFET
284 client = GoodFET()
285 client.verbose = False # Dump activity to terminal
286 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
287
288
289 # Handle each possible PIC verb in turn
290
291 if sys.argv[1] == "devid": #0x81
292     print "Requesting Application ID, DEVID and hardware revision..."
293     data = client.writecmd( PICAPP, 0x81, 0 )
294     
295     if len(data) > 0:
296         appid = ord(data[0])
297     else:
298         appid = -1
299     if len(data) > 2:
300         devid = ord(data[1]) + (ord(data[2]) << 8)
301     else:
302         devid = -1
303     if len(data) > 4:
304         hwrev = ord(data[3]) + (ord(data[4]) << 8)
305     else:
306         hwrev = -1
307     print "Application ID:   0x%02X" % appid
308     if dev_table.has_key( devid ):
309         print "DEVID:          0x%04X  (%s)" % ( devid, dev_table[devid] )
310     else:
311         print "DEVID:          0x%04X  (unknown)" %  devid
312     print "revision:       0x%04X"% hwrev
313     #print "\n(Note that -1 indicates failure to read a value.)"
314
315 elif sys.argv[1] == "reset":
316     client.writecmd( PICAPP, 0x87, 0 )
317
318 elif sys.argv[1] == "config": # Dump configuration registers
319     prep_cmd_li = [0x040200, # GOTO 0x0200
320                    0x000000, #
321                    0x200F80, # MOV #0x00F8, W0
322                    0x880190, # MOV W0, TBLPAG
323                    0xEB0300, # CLR W6
324                    0x207847, # MOV #VISI, W7
325                    0x000000] # NOP
326     rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
327                  0x000000, # NOP
328                  0x000000] # NOP
329     startICSP()
330     print "Dumping configuration registers..."
331     for instr in prep_cmd_li:
332         data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
333                                                   (instr >> 8) & 0xff,
334                                                   (instr >> 16) & 0xff] )
335     for k in range(12): # twelve configuration registers total
336         for instr in rd_cmd_li:
337             data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
338                                                   (instr >> 8) & 0xff,
339                                                   (instr >> 16) & 0xff] )
340         result = readVISI()
341         addr = 0xF80000+k*2
342         if cfg_table.has_key( addr ):
343             print "0x%06X (%s): 0x%02X" % ( addr,
344                                             cfg_table[addr].ljust(cfg_table["width"]),
345                                             result )
346         else:
347             print "0x%06X: 0x%02X" % ( addr, result )
348
349     stopICSP()
350
351 elif sys.argv[1] == "program":
352     if len(sys.argv) != 3:
353         print "Error: an Intel HEX file to load must be given."
354         exit(1)
355
356     try:
357         proghex = IntelHex( sys.argv[2] )
358     except IOError:
359         print "Error while attempting to read from %s" % sys.argv[2]
360         exit(-1)
361     ph_addrs = proghex.addresses()
362
363     # Load starting program memory address
364     wr_pm64_li_setaddr = [0x040200, # GOTO 0x0200
365                           0x000000, #
366                           0x24001A, # MOV $0x4001, W10
367                           0x883B0A, # MOV W10, NVMCON
368                           0x200000, # + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
369                           0x880190, # MOV W0, TBLPAG
370                           0x200007] # + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
371
372     # Load 4 instructions into write latches
373     wr_pm64_li_wrlat = [0xEB0300, # CLR W6
374                         0x000000, # NOP
375                         0xBB0BB6, # TBLWTL [W6++], [W7]
376                         0x000000, # NOP
377                         0x000000, # NOP
378                         0xBBDBB6, # TBLWTH.B [W6++], [W7++]
379                         0x000000, # NOP
380                         0x000000, # NOP
381                         0xBBEBB6, # TBLWTH.B [W6++], [++W7]
382                         0x000000, # NOP
383                         0x000000, # NOP
384                         0xBB1BB6, # TBLWTL [W6++], [W7++]
385                         0x000000, # NOP
386                         0x000000, # NOP
387                         0xBB0BB6, # TBLWTL [W6++], [W7]
388                         0x000000, # NOP
389                         0x000000, # NOP
390                         0xBBDBB6, # TBLWTH.B [W6++], [W7++]
391                         0x000000, # NOP
392                         0x000000, # NOP
393                         0xBBEBB6, # TBLWTH.B [W6++], [++W7]
394                         0x000000, # NOP
395                         0x000000, # NOP
396                         0xBB1BB6, # TBLWTL [W6++], [W7++]
397                         0x000000, # NOP
398                         0x000000] # NOP
399
400     wr_pm64_li_wr = [0xA8E761, # BSET NVMCON, #WR
401                      0x000000, # NOP
402                      0x000000, # NOP
403                      0x000000, # NOP
404                      0x000000] # NOP
405     
406     startICSP()
407     
408     for last_code_addr in reversed(ph_addrs):
409         if (last_code_addr>>1) < 0xf80000:
410             last_row_addr = (last_code_addr>>1) & 0xffff80
411             break
412     for addr in range((ph_addrs[0]>>1)&0xffff80,last_code_addr+2,128):
413         # Prevent crossing addresses where upper byte
414         # (i.e., address<23:16>) changes.
415         #if addr+126 > 0xffff:
416         #    stop_addr = addr | 0xffff
417         #else:
418         #    stop_addr = addr+126
419
420         runlist( wr_pm64_li_setaddr[:4]
421                  + [0x200000 + ((addr>>12)&0xff0),
422                     wr_pm64_li_setaddr[5],
423                     0x200007 + ((addr&0xffff)<<4)] )
424                  
425         instr_list = [0,0,0,0]
426         for mid_addr in range(addr,addr+126,8):
427             for offset in range(4):
428                 if (mid_addr+offset*2)<<1 not in ph_addrs:
429                     instr_list[offset] = 0xffffff
430                 else:
431                     instr_list[offset] = proghex[(mid_addr+offset*2)<<1]
432                     instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+1] << 8
433                     instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+2] << 16
434             packed_instr_list = instr2words( instr_list )
435             runlist( [0x200000 + ((packed_instr_list[0]&0xffff)<<4),
436                       0x200001 + ((packed_instr_list[1]&0xffff)<<4),
437                       0x200002 + ((packed_instr_list[2]&0xffff)<<4),
438                       0x200003 + ((packed_instr_list[3]&0xffff)<<4),
439                       0x200004 + ((packed_instr_list[4]&0xffff)<<4),
440                       0x200005 + ((packed_instr_list[5]&0xffff)<<4)]
441                      + wr_pm64_li_wrlat )
442         
443         runlist( wr_pm64_li_wr )
444         status = readNVMCON()
445         while status & 0x8000:
446             client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
447             status = readNVMCON()
448
449     # Configuration registers must be treated separately
450     for addr in ph_addrs[ph_addrs.index(last_code_addr):]:
451         if addr%4 != 0:
452             continue
453         if (addr>>1) > 0xf80017:
454             break
455         wr_cfg_li = [0x040200, # GOTO 0x0200
456                      0x000000, #
457                      0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
458                      0x24000A, # MOV #0x4000, W10
459                      0x883B0A, # MOV W10, NVMCON
460                      0x200F80, # MOV #0xF8, W0
461                      0x880190, # MOV W0, TBLPAG
462                      0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
463                      0xBB0B80, # TBLWTL W0, [W7]
464                      0x000000, # NOP
465                      0x000000, # NOP
466                      0xA8E761, # BSET NVMCON, #WR
467                      0x000000, # NOP
468                      0x000000, # NOP
469                      0x000000, # NOP
470                      0x000000] # NOP
471         runlist( wr_cfg_li )
472         status = readNVMCON()
473         while status & 0x8000:
474             client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
475             status = readNVMCON()
476
477     stopICSP()
478
479
480 elif sys.argv[1] == "verify":
481     if len(sys.argv) != 3:
482         print "Error: an Intel HEX file with which to compare must be given."
483         exit(1)
484
485     try:
486         proghex = IntelHex( sys.argv[2] )
487     except IOError:
488         print "Error while attempting to read from %s" % sys.argv[2]
489         exit(-1)
490
491     print "WARNING: verifying config registers not yet supported."
492
493     for addr in proghex.addresses():
494         if addr % 4 != 0 or (addr>>1) < 0x200:
495             continue
496         #elif first_addr == None:
497         #    first_addr = addr>>1
498         if (addr>>1) >= 0xf80000:
499             break
500         last_addr = addr>>1
501     dumphex = dump_pm( (proghex.addresses()[0]>>1),
502                        last_addr )
503
504     # Step through addresses in HEX file and compare to those in target PIC
505     for addr in proghex.addresses():
506         if addr>>1 >= 0xF80000:
507             break # verifying config registers not yet supported
508         # Compare; fail if mismatch
509         if proghex[addr] != dumphex[addr]:
510             addr &= addr&0xfffffffc
511             found_instr = dumphex[addr]
512             found_instr |= dumphex[addr+1]<<8
513             found_instr |= dumphex[addr+2]<<16
514             exp_instr = proghex[addr]
515             exp_instr |= proghex[addr+1]<<8
516             exp_instr |= proghex[addr+2]<<16
517             print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % ( addr>>1, found_instr, exp_instr )
518             exit(-1)
519
520     print "PASSED" # Be verbose.
521
522
523 elif sys.argv[1] == "write":
524     if len(sys.argv) != 4:
525         print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
526         exit(1)
527     
528     addr = int(float.fromhex(sys.argv[2]))
529     new_instr = int(float.fromhex(sys.argv[3]))
530     #new_instr_li = []
531     #for k in range(4):
532     #    new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
533
534     #new_words = instr2words( new_instr_li )
535         
536     wr_pm_li = [0x040200, # GOTO 0x0200
537                 0x000000, #
538
539                 0x24003A, # MOV $0x4003, W10
540                 0x883B0A, # MOV W10, NVMCON
541                 0x200000 + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
542                 0x880190, # MOV W0, TBLPAG
543                 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
544
545                 # Here we load the instruction into registers W0:W1
546                 0x200000 + ((new_instr&0xffff)<<4),
547                 0x200001 + ((new_instr>>12)&0xff0),
548                 #0x200000 + ((new_words[0]&0xffff)<<4),
549                 #0x200001 + ((new_words[1]&0xffff)<<4),
550                 #0x200002 + ((new_words[2]&0xffff)<<4), 
551                 #0x200003 + ((new_words[3]&0xffff)<<4),
552                 #0x200004 + ((new_words[4]&0xffff)<<4),
553                 #0x200005 + ((new_words[5]&0xffff)<<4),
554
555                 0xEB0300, # CLR W6
556                 0x000000, # NOP
557                 0xBB0BB6, # TBLWTL [W6++], [W7]
558                 0x000000, # NOP
559                 0x000000, # NOP
560                 0xBB8B96, # TBLWTH [W6], [W7]
561                 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
562                 0x000000, # NOP
563                 0x000000, # NOP
564                 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
565                 #0x000000, # NOP
566                 #0x000000, # NOP
567                 #0xBB1BB6, # TBLWTL [W6++], [W7++]
568                 #0x000000, # NOP
569                 #0x000000, # NOP
570                 #0xBB0BB6, # TBLWTL [W6++], [W7]
571                 #0x000000, # NOP
572                 #0x000000, # NOP
573                 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
574                 #0x000000, # NOP
575                 #0x000000, # NOP
576                 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
577                 #0x000000, # NOP
578                 #0x000000, # NOP
579                 #0xBB1BB6, # TBLWTL [W6++], [W7++]
580                 #0x000000, # NOP
581                 #0x000000, # NOP
582                 
583                 0xA8E761, # BSET NVMCON, #WR
584                 0x000000, # NOP
585                 0x000000, # NOP
586                 0x000000, # NOP
587                 0x000000] # NOP
588     
589     startICSP()
590
591     runlist( wr_pm_li )
592     status = readNVMCON()
593     while status & 0x8000:
594         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
595         status = readNVMCON()
596
597     stopICSP()
598
599 elif sys.argv[1] == "write_config":
600     if len(sys.argv) < 3:
601         print "Error: please specify the target config register."
602         exit(1)
603     elif len(sys.argv) == 3:
604         new_val = 0xFF # Assume new value
605     else:
606         new_val = int(float.fromhex(sys.argv[3]))
607     try:
608         reg_addr = int(float.fromhex(sys.argv[2]))
609     except ValueError:
610         if sys.argv[2] not in cfg_table.values():
611             print "Given register, %s, not recognized." % sys.argv[2]
612             exit(1)
613         reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
614     
615     wr_cfg_li = [0x040200, # GOTO 0x0200
616                  0x000000, #
617                  0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
618                  0x24000A, # MOV #0x4000, W10
619                  0x883B0A, # MOV W10, NVMCON
620                  0x200F80, # MOV #0xF8, W0
621                  0x880190, # MOV W0, TBLPAG
622                  0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
623                  0xBB0B80, # TBLWTL W0, [W7]
624                  0x000000, # NOP
625                  0x000000, # NOP
626                  0xA8E761, # BSET NVMCON, #WR
627                  0x000000, # NOP
628                  0x000000, # NOP
629                  0x000000, # NOP
630                  0x000000] # NOP
631
632     startICSP()
633
634     runlist( wr_cfg_li )
635     status = readNVMCON()
636     while status & 0x8000:
637         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
638         status = readNVMCON()
639
640     stopICSP()
641         
642
643 elif sys.argv[1] == "read": # Read an address of program memory
644     startICSP()
645
646     addr = int(float.fromhex(sys.argv[2]))
647
648     readpm_cmd_li = [0x040200,                         # GOTO 0x0200
649                      0x000000,                         #
650                      0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
651                      0x880190,                         # MOV W0, TBLPAG
652                      0x200006+((addr & 0xffff)<<4),    # MOV #addr<15:0>, W6
653                      0xEB0380,                         # CLR W7
654                      0xEB0080,                         # CLR W1
655                      0x000000,                         # NOP
656                      0xBA1B96,                         # TBLRDL [W6], [W7++]
657                      0x000000,                         # NOP
658                      0x000000,                         # NOP
659                      0xBACB96,                         # TBLRDH.B [W6], [W7]
660                      0x000000,                         # NOP
661                      0x000000]                         # NOP
662                      
663     runlist( readpm_cmd_li )
664     loww = readreg(0)  # Read W0, which has lower 16 bits
665     highb = readreg(1) # Read W1, which has high 8 bits
666     result = (highb<<16) | loww
667     
668     print "0x%06X: 0x%06X" % ( addr, result )
669
670     stopICSP()
671
672 elif sys.argv[1] == "dump": # Read section of program memory
673     if len(sys.argv) < 3:
674         print "Error: please specify file in which to dump program memory."
675         exit(1)
676     fname = sys.argv[2]
677
678     if len(sys.argv) == 6:
679         print_term = True
680     else:
681         print_term = False
682         
683     if len(sys.argv) > 4:
684         start_addr = int(float.fromhex(sys.argv[3]))
685         stop_addr  = int(float.fromhex(sys.argv[4]))
686     else:
687         # Assume all of program memory (including unimplemented sections,
688         # which will be read as zeroes.
689         start_addr = 0x0
690         stop_addr = 0xFFFFFE
691
692     dumphex = dump_pm( start_addr, stop_addr, print_term )
693         
694     if not print_term:
695         if fname == "-":
696             dumphex.tofile( sys.stdout, format="hex" )
697         else:
698             dumphex.tofile( fname, format="hex" )
699
700
701 elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
702
703     if len(sys.argv) == 3:
704         addr = int(float.fromhex(sys.argv[2]))
705     else:
706         addr = None
707
708     if addr is None: # Bulk erase (all of program memory)
709         erase_cmd_li = [0x040200, # GOTO 0x0200
710                         0x000000, #
711                         0x2404FA, # MOV $0x404F, W10
712                         0x883B0A, # MOV W10, NVMCON
713                         0xA8E761, # BSET NVMCON, #WR
714                         0x000000, # NOP
715                         0x000000, # NOP
716                         0x000000, # NOP
717                         0x000000] # NOP
718     else: # Page erase
719         print "Erasing page of 0x%06X" % addr
720         erase_cmd_li = [0x040200, # GOTO 0x0200
721                         0x000000, #
722                         0x24042A, # MOV $0x4042, W10
723                         0x883B0A, # MOV W10, NVMCON
724                         0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
725                         0x880190, # MOV W0, TBLPAG
726                         0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
727                         0x000000, # NOP
728                         0xBB0881, # TBLTWL W1, [W1]
729                         0x000000, # NOP
730                         0x000000, # NOP
731                         0xA8E761, # BSET NVMCON, #WR
732                         0x000000, # NOP
733                         0x000000, # NOP
734                         0x000000, # NOP
735                         0x000000] # NOP
736         # Note that page alignment (effectively, bitmask of 0xffff80
737         # applied to given program memory address) seems to be
738         # enforced by hardware.
739
740     startICSP()
741
742     runlist( erase_cmd_li )
743     status = readNVMCON()
744     while status & 0x8000:
745         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
746         status = readNVMCON()
747
748     stopICSP()
749
750 elif sys.argv[1] == "six": #0x82
751     startICSP()
752     
753     if len(sys.argv) < 3:
754         instr = 0x000000 # Assume nop
755     else:
756         instr = int(float.fromhex(sys.argv[2]))
757     print "Loading 0x%06X for execution..." % instr
758     data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
759                                        (instr >> 8) & 0xff,
760                                        (instr >> 16) & 0xff] )
761     print "Executing (by loading nop; pumping clock)..."
762     data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
763
764     dumpVISI()
765
766     stopICSP()
767
768 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
769     startICSP()
770
771     if len(sys.argv) < 3:
772         print "Warning: no file given; trying cmd.txt..."
773         fname = "cmd.txt"
774     else:
775         fname = sys.argv[2]
776
777     try:
778         instrfile = open( fname, "r" )
779     except:
780         print "Failed to open file %s for reading." % fname
781         exit(-1)
782
783     try:
784         print "Opened file %s. Consecutively reading commmands..." % fname
785         for line in instrfile:
786             words = line.split()
787             if len(words) > 0:
788                 try:
789                     instr = int(float.fromhex(words[0]))
790                 except ValueError:
791                     print "Warning: invalid instruction found at offset %d." % instrfile.tell()
792                     continue
793                 print "Loading 0x%06X for execution..." % instr
794                 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
795                                                    (instr >> 8) & 0xff,
796                                                    (instr >> 16) & 0xff] )
797             #else:
798             #    print "Warning: empty line found at offset %d." % instrfile.tell()
799     except:
800         print "Error: exception caught while reading file %s." % fname
801         instrfile.close()
802         stopICSP()
803         exit(-1)
804
805     instrfile.close()
806     
807     print "Loading nop, pumping clock (to finish execution)..."
808     data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
809     
810     dumpVISI()
811
812     stopICSP()
813
814 elif sys.argv[1] == "regout": #0x83
815     startICSP()
816
817     print "Reading VISI register..."
818     data = client.writecmd( PICAPP, 0x83, 0 )
819     result = ord(data[0])
820     result |= ord(data[1]) << 8
821     print "VISI: 0x%04X" % result
822
823     stopICSP()
824
825 else:
826     print "Unrecognized verb: %s" % sys.argv[1]
827     sys.exit(-1)
828