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