changes to the GUI to allow for better loading and writing as well as tested our...
[goodfet] / client / goodfet.pic
1 #!/usr/bin/env python
2 # PIC client (currently only supports dsPIC33F/PIC24H.)
3 #
4 # Note that a verify operation only considers addresses specified in
5 # the given Intel hex file ($foo.hex).
6
7 # sixfile expects a plain text file, one instruction per line.
8 #
9 #
10 # Scott Livingston  <slivingston at caltech.edu>
11 #
12 # March-June 2010.
13
14
15 import sys
16 import time
17
18 from GoodFET import GoodFET
19 from intelhex import IntelHex
20
21 #############
22 # Some constants (not really immutable...)
23 #############
24 PICAPP = 0x34
25 MONITORAPP = 0x00
26 NOK = 0x7E
27
28 VERB_CMDLIST = 0x88
29
30 # 0x32 is the normal address of the TBLPAG register. On some PICs,
31 # though, it appears at 0x54 (see DS39970B). Affected PIC families
32 # include: PIC24FJxxx{DA1,DA2,GB2,GA3}xx
33 tblpag = 0x32
34
35 WRITE_MAX_POLL_ATTEMPTS = 100
36
37 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
38               0x00EF : "dsPIC33FJ128GP710",
39               0x080A : "PIC24HJ12GP201",
40               0x080B : "PIC24HJ12GP202",
41               0x0444 : "PIC24FJ16GA002",
42               0x044C : "PIC24FJ16GA004",
43               0x0445 : "PIC24FJ32GA002",
44               0x044D : "PIC24FJ32GA004",
45               0x0446 : "PIC24FJ48GA002",
46               0x044E : "PIC24FJ48GA004",
47               0x0447 : "PIC24FJ64GA002",
48               0x044F : "PIC24FJ64GA004",
49               0x0405 : "PIC24FJ64GA006",
50               0x0408 : "PIC24FJ64GA008",
51               0x040B : "PIC24FJ64GA010",
52               0x0406 : "PIC24FJ96GA006",
53               0x0409 : "PIC24FJ96GA008",
54               0x040C : "PIC24FJ96GA010",
55               0x0407 : "PIC24FJ128GA006",
56               0x040A : "PIC24FJ128GA008",
57               0x040D : "PIC24FJ128GA010",
58               0x1000 : "PIC24FJ64GA106",
59               0x1008 : "PIC24FJ128GA106",
60               0x1010 : "PIC24FJ192GA106",
61               0x1018 : "PIC24FJ256GA106",
62               0x1002 : "PIC24FJ64GA108",
63               0x100A : "PIC24FJ128GA108",
64               0x1012 : "PIC24FJ192GA108",
65               0x101A : "PIC24FJ256GA108",
66               0x1006 : "PIC24FJ64GA110",
67               0x100E : "PIC24FJ128GA110",
68               0x1016 : "PIC24FJ192GA110",
69               0x101E : "PIC24FJ256GA110",
70               0x1001 : "PIC24FJ64GB106",
71               0x1009 : "PIC24FJ128GB106",
72               0x1011 : "PIC24FJ192GB106",
73               0x1019 : "PIC24FJ256GB106",
74               0x1003 : "PIC24FJ64GB108",
75               0x100B : "PIC24FJ128GB108",
76               0x1013 : "PIC24FJ192GB108",
77               0x101B : "PIC24FJ256GB108",
78               0x1007 : "PIC24FJ64GB110",
79               0x100F : "PIC24FJ128GB110",
80               0x1017 : "PIC24FJ192GB110",
81               0x101F : "PIC24FJ256GB110",
82               0x4109 : "PIC24FJ128DA106",
83               0x410D : "PIC24FJ256DA106",
84               0x410B : "PIC24FJ128DA110",
85               0x410F : "PIC24FJ256DA110",
86               0x4108 : "PIC24FJ128DA206",
87               0x410C : "PIC24FJ256DA206",
88               0x410A : "PIC24FJ128DA210",
89               0x410E : "PIC24FJ256DA210",
90               0x46C0 : "PIC24FJ64GA306",
91               0x46C4 : "PIC24FJ64GA308",
92               0x46C8 : "PIC24FJ64GA310",
93               0x46C2 : "PIC24FJ128GA306",
94               0x46C6 : "PIC24FJ128GA308",
95               0x46CA : "PIC24FJ128GA310",
96               0x4100 : "PIC24FJ128GB206",
97               0x4104 : "PIC24FJ256GB206",
98               0x4102 : "PIC24FJ128GB210",
99               0x4106 : "PIC24FJ256GB210",
100               }
101 cfg_table = { 0xF80000 : "FBS",
102               0xF80002 : "FSS",
103               0xF80004 : "FGS",
104               0xF80006 : "FOSCSEL",
105               0xF80008 : "FOSC",
106               0xF8000A : "FWDT",
107               0xF8000C : "FPOR",
108               0xF8000E : "FICD",
109               0xF80010 : "FUID0",
110               0xF80012 : "FUID1",
111               0xF80014 : "FUID2",
112               0xF80016 : "FUID3",
113               "width" : 7 } # For pretty printing.
114
115 #cfg_bitmask_table = { 
116
117
118 #############
119 # Functions:
120 #############
121
122 def runlist( cmd_li ):
123     """Load (and execute) a given list of instructions.
124 Assumes ICSP session already started."""
125     cmd_byte_li = build_instr_stream( cmd_li )
126     data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
127     if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
128         print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
129         stopICSP()
130         exit(-1)
131     elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
132         print "Error (in runlist): failed transaction; aborting."
133         stopICSP()
134         exit(-1)
135
136 class PicResponseThunk(object):
137     """A holder for a response value from the PIC, eg from a REGOUT
138     command.
139
140     To retrieve the value, simply call the object. If the value has
141     not yet been retrieved, any pending commands in the command buffer
142     will be executed. You can determine whether the response has been
143     recieved by calling the have_response function.
144     
145     Objects of this class should only be created by the PicCmdBuffer
146     class"""
147
148     def __init__(self, cmdbuf):
149         self.holder = cmdbuf
150         self.value = None
151
152     def _set_value(self, value):
153         self.value = value
154     def have_response(self):
155         return self.value is not None
156     def __call__(self):
157         if self.value is None:
158             self.holder.force()
159         assert self.value is not None
160         return self.value
161
162 class PicCmdBuffer(object):
163     def __init__(self, client):
164         self.client = client
165         self.buffered_commands = []
166         self.response_buffer = []
167         data = client.writecmd(MONITORAPP, 0xc2)
168         assert data is not None and len(data) is 2
169         # buffer size is the size of the goodfet's recieve buffer, in
170         # instructions
171         self.buffer_size = (ord(data[0]) + (ord(data[1]) << 8)) >> 2
172     def force(self):
173         assert len(self.buffered_commands) <= self.buffer_size
174         command = ''.join(self.buffered_commands)
175         self.buffered_commands = []
176         #print "Running: " + command.encode('hex')
177         data = self.client.writecmd(PICAPP, VERB_CMDLIST,
178                                     len(command), map(ord, command))
179         #print "Recieved: " + data.encode('hex')
180         assert len(data) == (len(self.response_buffer) + 1) * 2
181         orig_data = data
182         checksum = 0
183         for resp in self.response_buffer:
184             wd, data = data[0:2],data[2:]
185             val = ord(wd[0]) + (ord(wd[1]) << 8)
186             checksum += val
187             resp._set_value(val)
188         assert len(data) == 2
189         checksum += ord(data[0]) + (ord(data[1]) << 8)
190         checksum &= 0xffff
191         assert checksum == 0
192         self.response_buffer = []
193     def _add_command(self, command, data):
194         assert command in (0,1)
195         assert (data >> 24) == 0
196         full_cmd = [chr(command)]
197         for i in (0,1,2):
198             full_cmd.append(chr((data >> (8*i)) & 0xff))
199         if len(self.buffered_commands) >= self.buffer_size:
200             self.force()
201         self.buffered_commands.append(''.join(full_cmd))
202     def SIX(self, *insns):
203         """Run a list of instructions"""
204         for insn in insns:
205             self._add_command(0, insn)
206         return self
207     def REGOUT(self):
208         "Read the VISI register. Returns a PicResponseThunk"
209         thunk = PicResponseThunk(self)
210         self.response_buffer.append(thunk)
211         self._add_command(1, 0)
212         return thunk
213     
214 def build_instr_stream( cmd_li ):
215     """Given a list of instruction words, returns a list of bytes of
216 the same, in little endian ordering."""
217     cmd_byte_li = []
218     for instr in cmd_li:
219         cmd_byte_li += [instr & 0xff,
220                         (instr >> 8) & 0xff,
221                         (instr >> 16) & 0xff]
222     return cmd_byte_li
223
224 def readreg( reg_num ):
225     """Read contents of a working register (i.e. W0, W1, ..., W15)."""
226     instr = 0x883C20+(reg_num&0xf)
227     client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
228                                        (instr >> 8) & 0xff,
229                                        (instr >> 16) & 0xff] )
230     client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
231     return readVISI()
232
233 def writereg( reg_num, word ):
234     """Write 16-bit word to a working register (i.e., W0, W1, ..., W15)."""
235     instr = 0x200000 + ((word&0xffff)<<4) + (reg_num&0xf)
236     client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
237                                        (instr >> 8) & 0xff,
238                                        (instr >> 16) & 0xff] )
239
240 def readVISI():
241     """Read VISI register; assumes ICSP session already started."""
242     data = client.writecmd( PICAPP, 0x83, 0 )
243     result = ord(data[0])
244     result |= ord(data[1]) << 8
245     return result
246
247 def readNVMCON():
248     """Read NVMCON register; assumes ICSP session already started."""
249     rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
250                     0x883C20, # MOV W0, VISI
251                     0x000000, # NOP
252                     0x000000] # NOP
253     runlist( rd_nvmcon_li )
254     return readVISI()
255
256 def resetPC():
257     """Reset program counter during an active ISCP session."""
258     runlist( [0x040200,   # GOTO 0x0200
259               0x000000] )
260
261 def startICSP():
262     #print "Starting dsPIC33F/PIC24H ICSP session..."
263     data = client.writecmd( PICAPP, 0x84, 0 )
264
265 def stopICSP():
266     #print "Stopping dsPIC33F/PIC24H ICSP session..."
267     data = client.writecmd( PICAPP, 0x85, 0 )
268
269 def dumpVISI():
270     """Read and print VISI register to stdout; assumes ICSP session already started."""
271     print "Reading VISI register..."
272     result = readVISI()
273     print "VISI: 0x%04X" % result
274
275 def dump_pm( start_addr, stop_addr, pretty=False ):
276     """Dump routine, now encapsulated in a function.
277 Returns an instance of IntelHex corresponding to result.
278
279 Note that we start and stop an ICSP session here! This means an
280 existing session will be broken."""
281     readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
282                      0x880000 | tblpag << 3, # MOV W0, TBLPAG
283                      0x200006, # MOV #addr<15:0>, W6
284                      0xEB0380, # CLR W7
285                      0x000000, # NOP
286                      0xBA1B96, # TBLRDL [W6], [W7++]
287                      0x000000, # NOP
288                      0x000000, # NOP
289                      0xBADBB6, # TBLRDH.B [W6++], [W7++]
290                      0x000000, # NOP
291                      0x000000, # NOP
292                      0xBADBD6, # TBLRDH.B [++W6], [W7++]
293                      0x000000, # NOP
294                      0x000000, # NOP
295                      0xBA1BB6, # TBLRDL [W6++], [W7++]
296                      0x000000, # NOP
297                      0x000000, # NOP
298                      0xBA1B96, # TBLRDL [W6], [W7++]
299                      0x000000, # NOP
300                      0x000000, # NOP
301                      0xBADBB6, # TBLRDH.B [W6++], [W7++]
302                      0x000000, # NOP
303                      0x000000, # NOP
304                      0xBADBD6, # TBLRDH.B [++W6], [W7++]
305                      0x000000, # NOP
306                      0x000000, # NOP
307                      0xBA0BB6, # TBLRDL [W6++], [W7]
308                      0x000000, # NOP
309                      0x000000] # NOP
310
311     dumphex = IntelHex()
312
313     startICSP()
314
315     print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
316     # Prep device
317     client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
318     client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
319     client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
320
321     last_highb = -1 # Only set TBLPAG when needed.
322     packed_instr_list = [0,0,0,0,0,0] # For packing 4 (24-bit) instructions in 6 (16-bit) words
323     for addr in range( start_addr&0xfffff8, stop_addr+8, 8 ):
324         if (addr>>16)&0xff != last_highb: 
325             last_highb = (addr>>16)&0xff;
326             specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
327                                 readpm_cmd_li[1],
328                                 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
329         else:
330             specify_addr_cmd = [readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
331         runlist( specify_addr_cmd + readpm_cmd_li[3:] )
332
333         for reg_num in range(6): # Read W0:5, to get packed instructions
334             packed_instr_list[reg_num] = readreg( reg_num )
335         instr_list = words2instr( packed_instr_list )
336
337         for offset in range(4): # Print result
338             if addr+offset*2 < start_addr:
339                 continue
340             if addr+offset*2 > stop_addr:
341                 break
342             dumphex.puts( ((addr+offset*2)&0xffffff)*2,
343                           chr(instr_list[offset]&0xff)
344                           +chr((instr_list[offset]>>8)&0xff)
345                           +chr((instr_list[offset]>>16)&0xff)
346                           +chr(0) )
347             if pretty:
348                 print "0x%06X    0x%06X" % (addr+offset*2,instr_list[offset])
349
350     stopICSP()
351
352     return dumphex
353
354     
355 def instr2words( instr_list ):
356     """Convert a list of 4 24-bit instructions to a list of 6 words
357 (16-bit width).
358
359 Returns [-1] on failure."""
360     if len(instr_list) < 4: # Catch mistakes
361         print "Error in instr2words: operand has less than 4 elements."
362         return [-1]
363     word_list = [0,0,0,0,0,0]
364     for k in [0,1]:
365         word_list[k*3] = instr_list[k*2] & 0xffff
366         word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
367         word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
368         word_list[k*3+2] = instr_list[k*2+1] & 0xffff
369     return word_list
370
371 def words2instr( word_list ):
372     """4 24-bit instructions from a packing in 6 words (16 bit width).
373 This is the inverse of function instr2words.
374
375 Returns [-1] on failure."""
376     if len(word_list) < 6: # Catch mistakes
377         print "Error in words2instr: operand has less than 6 elements."
378         return [-1]
379     instr_list = [0,0,0,0]
380     for k in [0,1]:
381         instr_list[k*2] = word_list[k*3]
382         instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
383         instr_list[k*2+1] = word_list[k*3+2]
384         instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
385     return instr_list
386
387
388 #############
389 # Main entry: 
390 #############
391
392 if len(sys.argv) == 1:
393     print "Usage: %s [-alt_tblpag] verb [objects]\n" % sys.argv[0]
394     print "%s devid" % sys.argv[0]
395     print "%s read 0x$addr" % sys.argv[0]
396     print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
397     print "%s config" % sys.argv[0]
398     print "%s reset" % sys.argv[0]
399     print "%s program $foo.hex" % sys.argv[0]
400     print "%s verify $foo.hex" % sys.argv[0]
401     print "%s write 0x$address 0x$value" % sys.argv[0]
402     print "%s write_config 0x$reg_address (or $reg_name) [$0x0000]" % sys.argv[0]
403     print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
404     print "%s six [instruction]" % sys.argv[0]
405     print "%s sixfile [$foo.txt]" % sys.argv[0]
406     print "%s regout" % sys.argv[0]
407     print """
408 Note: use - for stdout.
409       use -alt_tblpag for PIC24FJxxx{DA1,DA2,GB2,GA3}xx with tblpag at 0x54
410 Warning: only formally supports dsPIC33F/PIC24H,
411          but read/write flash memory works with PIC24F ...
412 """
413     sys.exit()
414
415 # Initialize and open connection to GoodFET
416 client = GoodFET()
417 client.verbose = False # Dump activity to terminal
418 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
419
420 if sys.argv[1] == "-alt_tblpag":
421     del sys.argv[1]
422     tblpag = 0x54
423
424 # Handle each possible PIC verb in turn
425 if sys.argv[1] == "devid": #0x81
426     print "Requesting Application ID, DEVID and hardware revision..."
427     startICSP()
428     cmdbuf = PicCmdBuffer(client)
429     cmdbuf.SIX(0x040200, # GOTO 0x200
430                0x000000, # NOP
431                0x200FF0, # MOV #0xff, W0
432                0x880000 | tblpag << 3, # MOV W0, TBLPAG
433                0x200006, # MOV #0x0000, W6
434                0x207847, # MOV #VISI, W7
435                0x000000, # NOP
436                0xBA0B96, # TBLRDL [W6],[W7]
437                0x000000, # NOP
438                0x000000) # NOP
439     devid = cmdbuf.REGOUT()
440     cmdbuf.SIX(0x200026, # MOV #0x0002, W6
441                0x000000, # NOP
442                0xBA0B96, # TBLRDL [W6],[W7]
443                0x000000, # NOP
444                0x000000) # NOP
445     devidrev = cmdbuf.REGOUT()
446     cmdbuf.SIX(0x200800, # MOV #0x80, W0
447                0x880000 | tblpag << 3, # MOV W0, TBLPAG
448                0x207F00, # MOV #0x07F0, W6
449                0x000000, # NOP
450                0xBA0B96, # TBLRDL [W6],[W7]
451                0x000000, # NOP
452                0x000000) # NOP
453     appid = cmdbuf.REGOUT()
454     print "Application ID:   0x%02x" % appid()
455     print "DEVID:            0x%04x  (%s)" % (
456         devid(), dev_table.get(devid(), "unknown"))
457     print "Revision:  0x%04x" % devidrev()
458     stopICSP()
459     #print "\n(Note that -1 indicates failure to read a value.)"
460
461 elif sys.argv[1] == "test":
462     startICSP()
463     cmdbuf = PicCmdBuffer(client)
464     cmdbuf.SIX(0x2BEEF6, # MOV #0xBEEF, W6
465                0x000000,
466                0x780B86, # MOV W6, [W7]
467                0x000000, # NOP
468                0x000000) # NOP
469     assert cmdbuf.REGOUT()() == 0xbeef
470     stopICSP()
471     
472 elif sys.argv[1] == "reset":
473     client.writecmd( PICAPP, 0x87, 0 )
474
475 elif sys.argv[1] == "config": # Dump configuration registers
476     prep_cmd_li = [0x040200, # GOTO 0x0200
477                    0x000000, #
478                    0x200F80, # MOV #0x00F8, W0
479                    0x880000 | tblpag << 3, # MOV W0, TBLPAG
480                    0xEB0300, # CLR W6
481                    0x207847, # MOV #VISI, W7
482                    0x000000] # NOP
483     rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
484                  0x000000, # NOP
485                  0x000000] # NOP
486     startICSP()
487     print "Dumping configuration registers..."
488     for instr in prep_cmd_li:
489         data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
490                                                   (instr >> 8) & 0xff,
491                                                   (instr >> 16) & 0xff] )
492     for k in range(12): # twelve configuration registers total
493         for instr in rd_cmd_li:
494             data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
495                                                   (instr >> 8) & 0xff,
496                                                   (instr >> 16) & 0xff] )
497         result = readVISI()
498         addr = 0xF80000+k*2
499         if cfg_table.has_key( addr ):
500             print "0x%06X (%s): 0x%02X" % ( addr,
501                                             cfg_table[addr].ljust(cfg_table["width"]),
502                                             result )
503         else:
504             print "0x%06X: 0x%02X" % ( addr, result )
505
506     stopICSP()
507
508 elif sys.argv[1] == "program":
509     if len(sys.argv) != 3:
510         print "Error: an Intel HEX file to load must be given."
511         exit(1)
512
513     try:
514         proghex = IntelHex( sys.argv[2] )
515     except IOError:
516         print "Error while attempting to read from %s" % sys.argv[2]
517         exit(-1)
518     ph_addrs = proghex.addresses()
519
520     # Load starting program memory address
521     wr_pm64_li_setaddr = [0x040200, # GOTO 0x0200
522                           0x000000, #
523                           0x24001A, # MOV $0x4001, W10
524                           0x883B0A, # MOV W10, NVMCON
525                           0x200000, # + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
526                           0x880000 | tblpag << 3, # MOV W0, TBLPAG
527                           0x200007] # + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
528
529     # Load 4 instructions into write latches
530     wr_pm64_li_wrlat = [0xEB0300, # CLR W6
531                         0x000000, # NOP
532                         0xBB0BB6, # TBLWTL [W6++], [W7]
533                         0x000000, # NOP
534                         0x000000, # NOP
535                         0xBBDBB6, # TBLWTH.B [W6++], [W7++]
536                         0x000000, # NOP
537                         0x000000, # NOP
538                         0xBBEBB6, # TBLWTH.B [W6++], [++W7]
539                         0x000000, # NOP
540                         0x000000, # NOP
541                         0xBB1BB6, # TBLWTL [W6++], [W7++]
542                         0x000000, # NOP
543                         0x000000, # NOP
544                         0xBB0BB6, # TBLWTL [W6++], [W7]
545                         0x000000, # NOP
546                         0x000000, # NOP
547                         0xBBDBB6, # TBLWTH.B [W6++], [W7++]
548                         0x000000, # NOP
549                         0x000000, # NOP
550                         0xBBEBB6, # TBLWTH.B [W6++], [++W7]
551                         0x000000, # NOP
552                         0x000000, # NOP
553                         0xBB1BB6, # TBLWTL [W6++], [W7++]
554                         0x000000, # NOP
555                         0x000000] # NOP
556
557     wr_pm64_li_wr = [0xA8E761, # BSET NVMCON, #WR
558                      0x000000, # NOP
559                      0x000000, # NOP
560                      0x000000, # NOP
561                      0x000000] # NOP
562     
563     startICSP()
564     
565     for last_code_addr in reversed(ph_addrs):
566         if (last_code_addr>>1) < 0xf80000:
567             last_row_addr = (last_code_addr>>1) & 0xffff80
568             break
569     for addr in range((ph_addrs[0]>>1)&0xffff80,last_code_addr+2,128):
570         # Prevent crossing addresses where upper byte
571         # (i.e., address<23:16>) changes.
572         #if addr+126 > 0xffff:
573         #    stop_addr = addr | 0xffff
574         #else:
575         #    stop_addr = addr+126
576
577         print "Writing address 0x%06X" % (addr)
578
579         runlist( wr_pm64_li_setaddr[:4]
580                  + [0x200000 + ((addr>>12)&0xff0),
581                     wr_pm64_li_setaddr[5],
582                     0x200007 + ((addr&0xffff)<<4)] )
583                  
584         instr_list = [0,0,0,0]
585         for mid_addr in range(addr,addr+126,8):
586             for offset in range(4):
587                 if (mid_addr+offset*2)<<1 not in ph_addrs:
588                     instr_list[offset] = 0xffffff
589                 else:
590                     instr_list[offset] = proghex[(mid_addr+offset*2)<<1]
591                     instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+1] << 8
592                     instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+2] << 16
593             packed_instr_list = instr2words( instr_list )
594             runlist( [0x200000 + ((packed_instr_list[0]&0xffff)<<4),
595                       0x200001 + ((packed_instr_list[1]&0xffff)<<4),
596                       0x200002 + ((packed_instr_list[2]&0xffff)<<4),
597                       0x200003 + ((packed_instr_list[3]&0xffff)<<4),
598                       0x200004 + ((packed_instr_list[4]&0xffff)<<4),
599                       0x200005 + ((packed_instr_list[5]&0xffff)<<4)]
600                      + wr_pm64_li_wrlat )
601         
602         runlist( wr_pm64_li_wr )
603         attempts = 0
604         status = readNVMCON()
605         while status & 0x8000:
606             attempts += 1
607             #print " Status was 0x%04X for address 0x%06X" % (status, addr)
608             if attempts >= WRITE_MAX_POLL_ATTEMPTS:
609                 print "Error writing to address 0x%06X (Status: 0x%04X)\nTry erase and program again." % (addr, status)
610                 stopICSP()
611                 exit(-1)
612             client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
613             status = readNVMCON()
614
615     # Configuration registers must be treated separately
616     for addr in ph_addrs[ph_addrs.index(last_code_addr):]:
617
618         print "Configuration register 0x%06X" % ( addr )
619   
620         if addr%4 != 0:
621             continue
622         if (addr>>1) > 0xf80017:
623             break
624         wr_cfg_li = [0x040200, # GOTO 0x0200
625                      0x000000, #
626                      0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
627                      0x24000A, # MOV #0x4000, W10
628                      0x883B0A, # MOV W10, NVMCON
629                      0x200F80, # MOV #0xF8, W0
630                      0x880000 | tblpag<< 3, # MOV W0, TBLPAG
631                      0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
632                      0xBB0B80, # TBLWTL W0, [W7]
633                      0x000000, # NOP
634                      0x000000, # NOP
635                      0xA8E761, # BSET NVMCON, #WR
636                      0x000000, # NOP
637                      0x000000, # NOP
638                      0x000000, # NOP
639                      0x000000] # NOP
640         runlist( wr_cfg_li )
641         attempts = 0
642         status = readNVMCON()
643         while status & 0x8000:
644             attempts += 1
645             #print " Status was 0x%04X for address 0x%06X" % (status, addr)
646             if attempts >= WRITE_MAX_POLL_ATTEMPTS:
647                 print "Error writing to address 0x%06X (Status: 0x%04X)\nTry erase and program again." % (addr, status)
648                 stopICSP()
649                 exit(-1)
650             client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
651             status = readNVMCON()
652
653     stopICSP()
654
655
656 elif sys.argv[1] == "verify":
657     if len(sys.argv) != 3:
658         print "Error: an Intel HEX file with which to compare must be given."
659         exit(1)
660
661     try:
662         proghex = IntelHex( sys.argv[2] )
663     except IOError:
664         print "Error while attempting to read from %s" % sys.argv[2]
665         exit(-1)
666
667     print "WARNING: verifying config registers not yet supported."
668
669     for addr in proghex.addresses():
670         if addr % 4 != 0 or (addr>>1) < 0x200:
671             continue
672         #elif first_addr == None:
673         #    first_addr = addr>>1
674         if (addr>>1) >= 0xf80000:
675             break
676         last_addr = addr>>1
677     dumphex = dump_pm( (proghex.addresses()[0]>>1),
678                        last_addr )
679
680     # Step through addresses in HEX file and compare to those in target PIC
681     for addr in proghex.addresses():
682         if addr>>1 >= 0xF80000:
683             break # verifying config registers not yet supported
684         # Compare; fail if mismatch
685         if proghex[addr] != dumphex[addr]:
686             addr &= addr&0xfffffffc
687             found_instr = dumphex[addr]
688             found_instr |= dumphex[addr+1]<<8
689             found_instr |= dumphex[addr+2]<<16
690             exp_instr = proghex[addr]
691             exp_instr |= proghex[addr+1]<<8
692             exp_instr |= proghex[addr+2]<<16
693             print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % ( addr>>1, found_instr, exp_instr )
694             exit(-1)
695
696     print "PASSED" # Be verbose.
697
698
699 elif sys.argv[1] == "write":
700     if len(sys.argv) != 4:
701         print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
702         exit(1)
703     
704     addr = int(float.fromhex(sys.argv[2]))
705     new_instr = int(float.fromhex(sys.argv[3]))
706     #new_instr_li = []
707     #for k in range(4):
708     #    new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
709
710     #new_words = instr2words( new_instr_li )
711         
712     wr_pm_li = [0x040200, # GOTO 0x0200
713                 0x000000, #
714
715                 0x24003A, # MOV $0x4003, W10
716                 0x883B0A, # MOV W10, NVMCON
717                 0x200000 + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
718                 0x880000 | tblpag << 3, # MOV W0, TBLPAG
719                 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
720
721                 # Here we load the instruction into registers W0:W1
722                 0x200000 + ((new_instr&0xffff)<<4),
723                 0x200001 + ((new_instr>>12)&0xff0),
724                 #0x200000 + ((new_words[0]&0xffff)<<4),
725                 #0x200001 + ((new_words[1]&0xffff)<<4),
726                 #0x200002 + ((new_words[2]&0xffff)<<4), 
727                 #0x200003 + ((new_words[3]&0xffff)<<4),
728                 #0x200004 + ((new_words[4]&0xffff)<<4),
729                 #0x200005 + ((new_words[5]&0xffff)<<4),
730
731                 0xEB0300, # CLR W6
732                 0x000000, # NOP
733                 0xBB0BB6, # TBLWTL [W6++], [W7]
734                 0x000000, # NOP
735                 0x000000, # NOP
736                 0xBB8B96, # TBLWTH [W6], [W7]
737                 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
738                 0x000000, # NOP
739                 0x000000, # NOP
740                 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
741                 #0x000000, # NOP
742                 #0x000000, # NOP
743                 #0xBB1BB6, # TBLWTL [W6++], [W7++]
744                 #0x000000, # NOP
745                 #0x000000, # NOP
746                 #0xBB0BB6, # TBLWTL [W6++], [W7]
747                 #0x000000, # NOP
748                 #0x000000, # NOP
749                 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
750                 #0x000000, # NOP
751                 #0x000000, # NOP
752                 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
753                 #0x000000, # NOP
754                 #0x000000, # NOP
755                 #0xBB1BB6, # TBLWTL [W6++], [W7++]
756                 #0x000000, # NOP
757                 #0x000000, # NOP
758                 
759                 0xA8E761, # BSET NVMCON, #WR
760                 0x000000, # NOP
761                 0x000000, # NOP
762                 0x000000, # NOP
763                 0x000000] # NOP
764     
765     startICSP()
766
767     runlist( wr_pm_li )
768     status = readNVMCON()
769     while status & 0x8000:
770         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
771         status = readNVMCON()
772
773     stopICSP()
774
775 elif sys.argv[1] == "write_config":
776     if len(sys.argv) < 3:
777         print "Error: please specify the target config register."
778         exit(1)
779     elif len(sys.argv) == 3:
780         new_val = 0xFF # Assume new value
781     else:
782         new_val = int(float.fromhex(sys.argv[3]))
783     try:
784         reg_addr = int(float.fromhex(sys.argv[2]))
785     except ValueError:
786         if sys.argv[2] not in cfg_table.values():
787             print "Given register, %s, not recognized." % sys.argv[2]
788             exit(1)
789         reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
790     
791     wr_cfg_li = [0x040200, # GOTO 0x0200
792                  0x000000, #
793                  0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
794                  0x24000A, # MOV #0x4000, W10
795                  0x883B0A, # MOV W10, NVMCON
796                  0x200F80, # MOV #0xF8, W0
797                  0x880000 | tblpag << 3, # MOV W0, TBLPAG
798                  0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
799                  0xBB0B80, # TBLWTL W0, [W7]
800                  0x000000, # NOP
801                  0x000000, # NOP
802                  0xA8E761, # BSET NVMCON, #WR
803                  0x000000, # NOP
804                  0x000000, # NOP
805                  0x000000, # NOP
806                  0x000000] # NOP
807
808     startICSP()
809
810     runlist( wr_cfg_li )
811     status = readNVMCON()
812     while status & 0x8000:
813         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
814         status = readNVMCON()
815
816     stopICSP()
817         
818
819 elif sys.argv[1] == "read": # Read an address of program memory
820     startICSP()
821
822     addr = int(float.fromhex(sys.argv[2]))
823
824     readpm_cmd_li = [0x040200,                         # GOTO 0x0200
825                      0x000000,                         #
826                      0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
827                      0x880000 | tblpag << 3,                         # MOV W0, TBLPAG
828                      0x200006+((addr & 0xffff)<<4),    # MOV #addr<15:0>, W6
829                      0xEB0380,                         # CLR W7
830                      0xEB0080,                         # CLR W1
831                      0x000000,                         # NOP
832                      0xBA1B96,                         # TBLRDL [W6], [W7++]
833                      0x000000,                         # NOP
834                      0x000000,                         # NOP
835                      0xBACB96,                         # TBLRDH.B [W6], [W7]
836                      0x000000,                         # NOP
837                      0x000000]                         # NOP
838                      
839     runlist( readpm_cmd_li )
840     loww = readreg(0)  # Read W0, which has lower 16 bits
841     highb = readreg(1) # Read W1, which has high 8 bits
842     result = (highb<<16) | loww
843     
844     print "0x%06X: 0x%06X" % ( addr, result )
845
846     stopICSP()
847
848 elif sys.argv[1] == "dump": # Read section of program memory
849     if len(sys.argv) < 3:
850         print "Error: please specify file in which to dump program memory."
851         exit(1)
852     fname = sys.argv[2]
853
854     if len(sys.argv) == 6:
855         print_term = True
856     else:
857         print_term = False
858         
859     if len(sys.argv) > 4:
860         start_addr = int(float.fromhex(sys.argv[3]))
861         stop_addr  = int(float.fromhex(sys.argv[4]))
862     else:
863         # Assume all of program memory (including unimplemented sections,
864         # which will be read as zeroes.
865         start_addr = 0x0
866         stop_addr = 0xFFFFFE
867
868     dumphex = dump_pm( start_addr, stop_addr, print_term )
869         
870     if not print_term:
871         if fname == "-":
872             dumphex.tofile( sys.stdout, format="hex" )
873         else:
874             dumphex.tofile( fname, format="hex" )
875
876
877 elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
878
879     if len(sys.argv) == 3:
880         addr = int(float.fromhex(sys.argv[2]))
881     else:
882         addr = None
883
884     if addr is None: # Bulk erase (all of program memory)
885         erase_cmd_li = [0x040200, # GOTO 0x0200
886                         0x000000, #
887                         0x2404FA, # MOV $0x404F, W10
888                         0x883B0A, # MOV W10, NVMCON
889                         0xA8E761, # BSET NVMCON, #WR
890                         0x000000, # NOP
891                         0x000000, # NOP
892                         0x000000, # NOP
893                         0x000000] # NOP
894     else: # Page erase
895         print "Erasing page of 0x%06X" % addr
896         erase_cmd_li = [0x040200, # GOTO 0x0200
897                         0x000000, #
898                         0x24042A, # MOV $0x4042, W10
899                         0x883B0A, # MOV W10, NVMCON
900                         0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
901                         0x880000 | tblpag << 3, # MOV W0, TBLPAG
902                         0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
903                         0x000000, # NOP
904                         0xBB0881, # TBLTWL W1, [W1]
905                         0x000000, # NOP
906                         0x000000, # NOP
907                         0xA8E761, # BSET NVMCON, #WR
908                         0x000000, # NOP
909                         0x000000, # NOP
910                         0x000000, # NOP
911                         0x000000] # NOP
912         # Note that page alignment (effectively, bitmask of 0xffff80
913         # applied to given program memory address) seems to be
914         # enforced by hardware.
915
916     startICSP()
917
918     runlist( erase_cmd_li )
919     status = readNVMCON()
920     while status & 0x8000:
921         client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
922         status = readNVMCON()
923
924     stopICSP()
925
926 elif sys.argv[1] == "six": #0x82
927     startICSP()
928     
929     if len(sys.argv) < 3:
930         instr = 0x000000 # Assume nop
931     else:
932         instr = int(float.fromhex(sys.argv[2]))
933     print "Loading 0x%06X for execution..." % instr
934     data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
935                                        (instr >> 8) & 0xff,
936                                        (instr >> 16) & 0xff] )
937     print "Executing (by loading nop; pumping clock)..."
938     data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
939
940     dumpVISI()
941
942     stopICSP()
943
944 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
945     startICSP()
946
947     if len(sys.argv) < 3:
948         print "Warning: no file given; trying cmd.txt..."
949         fname = "cmd.txt"
950     else:
951         fname = sys.argv[2]
952
953     try:
954         instrfile = open( fname, "r" )
955     except:
956         print "Failed to open file %s for reading." % fname
957         exit(-1)
958
959     try:
960         print "Opened file %s. Consecutively reading commmands..." % fname
961         for line in instrfile:
962             words = line.split()
963             if len(words) > 0:
964                 try:
965                     instr = int(float.fromhex(words[0]))
966                 except ValueError:
967                     print "Warning: invalid instruction found at offset %d." % instrfile.tell()
968                     continue
969                 print "Loading 0x%06X for execution..." % instr
970                 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
971                                                    (instr >> 8) & 0xff,
972                                                    (instr >> 16) & 0xff] )
973             #else:
974             #    print "Warning: empty line found at offset %d." % instrfile.tell()
975     except:
976         print "Error: exception caught while reading file %s." % fname
977         instrfile.close()
978         stopICSP()
979         exit(-1)
980
981     instrfile.close()
982     
983     print "Loading nop, pumping clock (to finish execution)..."
984     data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
985     
986     dumpVISI()
987
988     stopICSP()
989
990 elif sys.argv[1] == "regout": #0x83
991     startICSP()
992
993     print "Reading VISI register..."
994     data = client.writecmd( PICAPP, 0x83, 0 )
995     result = ord(data[0])
996     result |= ord(data[1]) << 8
997     print "VISI: 0x%04X" % result
998
999     stopICSP()
1000
1001 else:
1002     print "Unrecognized verb: %s" % sys.argv[1]
1003     sys.exit(-1)
1004