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