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