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