2 # PIC client (currently only supports dsPIC33F/PIC24H.)
4 # Note that a verify operation only considers addresses specified in
5 # the given Intel hex file ($foo.hex).
7 # sixfile expects a plain text file, one instruction per line.
10 # Scott Livingston <slivingston at caltech.edu>
18 from GoodFET import GoodFET
19 from intelhex import IntelHex
22 # Some constants (not really immutable...)
27 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
28 0x00EF : "dsPIC33FJ128GP710",
29 0x080A : "PIC24HJ12GP201" }
30 cfg_table = { 0xF80000 : "FBS",
42 "width" : 7 } # For pretty printing.
44 #cfg_bitmask_table = {
51 def runlist( cmd_li ):
52 """Load (and execute) a given list of instructions.
53 Assumes ICSP session already started."""
54 cmd_byte_li = build_instr_stream( cmd_li )
55 data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
56 if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
57 print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
60 elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
61 print "Error (in runlist): failed transaction; aborting."
65 def build_instr_stream( cmd_li ):
66 """Given a list of instruction words, returns a list of bytes of
67 the same, in little endian ordering."""
70 cmd_byte_li += [instr & 0xff,
75 def readreg( reg_num ):
76 """Read contents of a working register (i.e. W0, W1, ..., W15)."""
77 instr = 0x883C20+(reg_num&0xf)
78 client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
80 (instr >> 16) & 0xff] )
81 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
84 def writereg( reg_num, word ):
85 """Write 16-bit word to a working register (i.e., W0, W1, ..., W15)."""
86 instr = 0x200000 + ((word&0xffff)<<4) + (reg_num&0xf)
87 client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
89 (instr >> 16) & 0xff] )
92 """Read VISI register; assumes ICSP session already started."""
93 data = client.writecmd( PICAPP, 0x83, 0 )
95 result |= ord(data[1]) << 8
99 """Read NVMCON register; assumes ICSP session already started."""
100 rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
101 0x883C20, # MOV W0, VISI
104 runlist( rd_nvmcon_li )
108 """Reset program counter during an active ISCP session."""
109 runlist( [0x040200, # GOTO 0x0200
113 #print "Starting dsPIC33F/PIC24H ICSP session..."
114 data = client.writecmd( PICAPP, 0x84, 0 )
117 #print "Stopping dsPIC33F/PIC24H ICSP session..."
118 data = client.writecmd( PICAPP, 0x85, 0 )
121 """Read and print VISI register to stdout; assumes ICSP session already started."""
122 print "Reading VISI register..."
124 print "VISI: 0x%04X" % result
126 def dump_pm( start_addr, stop_addr, pretty=False ):
127 """Dump routine, now encapsulated in a function.
128 Returns an instance of IntelHex corresponding to result.
130 Note that we start and stop an ICSP session here! This means an
131 existing session will be broken."""
132 readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
133 0x880190, # MOV W0, TBLPAG
134 0x200006, # MOV #addr<15:0>, W6
137 0xBA1B96, # TBLRDL [W6], [W7++]
140 0xBADBB6, # TBLRDH.B [W6++], [W7++]
143 0xBADBD6, # TBLRDH.B [++W6], [W7++]
146 0xBA1BB6, # TBLRDL [W6++], [W7++]
149 0xBA1B96, # TBLRDL [W6], [W7++]
152 0xBADBB6, # TBLRDH.B [W6++], [W7++]
155 0xBADBD6, # TBLRDH.B [++W6], [W7++]
158 0xBA0BB6, # TBLRDL [W6++], [W7]
166 print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
168 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
169 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
170 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
172 last_highb = -1 # Only set TBLPAG when needed.
173 packed_instr_list = [0,0,0,0,0,0] # For packing 4 (24-bit) instructions in 6 (16-bit) words
174 for addr in range( start_addr&0xfffff8, stop_addr+8, 8 ):
175 if (addr>>16)&0xff != last_highb:
176 last_highb = (addr>>16)&0xff;
177 specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
179 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
181 specify_addr_cmd = [readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
182 runlist( specify_addr_cmd + readpm_cmd_li[3:] )
184 for reg_num in range(6): # Read W0:5, to get packed instructions
185 packed_instr_list[reg_num] = readreg( reg_num )
186 instr_list = words2instr( packed_instr_list )
188 for offset in range(4): # Print result
189 if addr+offset*2 < start_addr:
191 if addr+offset*2 > stop_addr:
193 dumphex.puts( ((addr+offset*2)&0xffffff)*2,
194 chr(instr_list[offset]&0xff)
195 +chr((instr_list[offset]>>8)&0xff)
196 +chr((instr_list[offset]>>16)&0xff)
199 print "0x%06X 0x%06X" % (addr+offset*2,instr_list[offset])
206 def instr2words( instr_list ):
207 """Convert a list of 4 24-bit instructions to a list of 6 words
210 Returns [-1] on failure."""
211 if len(instr_list) < 4: # Catch mistakes
212 print "Error in instr2words: operand has less than 4 elements."
214 word_list = [0,0,0,0,0,0]
216 word_list[k*3] = instr_list[k*2] & 0xffff
217 word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
218 word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
219 word_list[k*3+2] = instr_list[k*2+1] & 0xffff
222 def words2instr( word_list ):
223 """4 24-bit instructions from a packing in 6 words (16 bit width).
224 This is the inverse of function instr2words.
226 Returns [-1] on failure."""
227 if len(word_list) < 6: # Catch mistakes
228 print "Error in words2instr: operand has less than 6 elements."
230 instr_list = [0,0,0,0]
232 instr_list[k*2] = word_list[k*3]
233 instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
234 instr_list[k*2+1] = word_list[k*3+2]
235 instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
243 if len(sys.argv) == 1:
244 print "Usage: %s verb [objects]\n" % sys.argv[0]
245 print "%s devid" % sys.argv[0]
246 print "%s read 0x$addr" % sys.argv[0]
247 print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
248 print "%s config" % sys.argv[0]
249 print "%s program $foo.hex" % sys.argv[0]
250 print "%s verify $foo.hex" % sys.argv[0]
251 print "%s write 0x$address 0x$value" % sys.argv[0]
252 print "%s write_config 0x$reg_address (or $reg_name) [$0x0000]" % sys.argv[0]
253 print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
254 print "%s six [instruction]" % sys.argv[0]
255 print "%s sixfile [$foo.txt]" % sys.argv[0]
256 print "%s regout" % sys.argv[0]
258 Note: use - for stdout.
259 Warning: only supports dsPIC33F/PIC24H (maybe)...
263 # Initialize and open connection to GoodFET
265 client.verbose = False # Dump activity to terminal
266 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
269 # Handle each possible PIC verb in turn
271 if sys.argv[1] == "devid": #0x81
272 print "Requesting Application ID, DEVID and hardware revision..."
273 data = client.writecmd( PICAPP, 0x81, 0 )
280 devid = ord(data[1]) + (ord(data[2]) << 8)
284 hwrev = ord(data[3]) + (ord(data[4]) << 8)
287 print "Application ID: 0x%02X" % appid
288 if dev_table.has_key( devid ):
289 print "DEVID: 0x%04X (%s)" % ( devid, dev_table[devid] )
291 print "DEVID: 0x%04X (unknown)" % devid
292 print "revision: 0x%04X"% hwrev
293 #print "\n(Note that -1 indicates failure to read a value.)"
295 elif sys.argv[1] == "config": # Dump configuration registers
296 prep_cmd_li = [0x040200, # GOTO 0x0200
298 0x200F80, # MOV #0x00F8, W0
299 0x880190, # MOV W0, TBLPAG
301 0x207847, # MOV #VISI, W7
303 rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
307 print "Dumping configuration registers..."
308 for instr in prep_cmd_li:
309 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
311 (instr >> 16) & 0xff] )
312 for k in range(12): # twelve configuration registers total
313 for instr in rd_cmd_li:
314 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
316 (instr >> 16) & 0xff] )
319 if cfg_table.has_key( addr ):
320 print "0x%06X (%s): 0x%02X" % ( addr,
321 cfg_table[addr].ljust(cfg_table["width"]),
324 print "0x%06X: 0x%02X" % ( addr, result )
328 elif sys.argv[1] == "program":
329 if len(sys.argv) != 3:
330 print "Error: an Intel HEX file to load must be given."
334 proghex = IntelHex( sys.argv[2] )
336 print "Error while attempting to read from %s" % sys.argv[2]
338 ph_addrs = proghex.addresses()
340 # Load starting program memory address
341 wr_pm64_li_setaddr = [0x040200, # GOTO 0x0200
343 0x24001A, # MOV $0x4001, W10
344 0x883B0A, # MOV W10, NVMCON
345 0x200000, # + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
346 0x880190, # MOV W0, TBLPAG
347 0x200007] # + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
349 # Load 4 instructions into write latches
350 wr_pm64_li_wrlat = [0xEB0300, # CLR W6
352 0xBB0BB6, # TBLWTL [W6++], [W7]
355 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
358 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
361 0xBB1BB6, # TBLWTL [W6++], [W7++]
364 0xBB0BB6, # TBLWTL [W6++], [W7]
367 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
370 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
373 0xBB1BB6, # TBLWTL [W6++], [W7++]
377 wr_pm64_li_wr = [0xA8E761, # BSET NVMCON, #WR
385 for last_code_addr in reversed(ph_addrs):
386 if (last_code_addr>>1) < 0xf80000:
387 last_row_addr = (last_code_addr>>1) & 0xffff80
389 for addr in range((ph_addrs[0]>>1)&0xffff80,last_code_addr+2,128):
390 # Prevent crossing addresses where upper byte
391 # (i.e., address<23:16>) changes.
392 #if addr+126 > 0xffff:
393 # stop_addr = addr | 0xffff
395 # stop_addr = addr+126
397 runlist( wr_pm64_li_setaddr[:4]
398 + [0x200000 + ((addr>>12)&0xff0),
399 wr_pm64_li_setaddr[5],
400 0x200007 + ((addr&0xffff)<<4)] )
402 instr_list = [0,0,0,0]
403 for mid_addr in range(addr,addr+126,8):
404 for offset in range(4):
405 if (mid_addr+offset*2)<<1 not in ph_addrs:
406 instr_list[offset] = 0xffffff
408 instr_list[offset] = proghex[(mid_addr+offset*2)<<1]
409 instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+1] << 8
410 instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+2] << 16
411 packed_instr_list = instr2words( instr_list )
412 runlist( [0x200000 + ((packed_instr_list[0]&0xffff)<<4),
413 0x200001 + ((packed_instr_list[1]&0xffff)<<4),
414 0x200002 + ((packed_instr_list[2]&0xffff)<<4),
415 0x200003 + ((packed_instr_list[3]&0xffff)<<4),
416 0x200004 + ((packed_instr_list[4]&0xffff)<<4),
417 0x200005 + ((packed_instr_list[5]&0xffff)<<4)]
420 runlist( wr_pm64_li_wr )
421 status = readNVMCON()
422 while status & 0x8000:
423 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
424 status = readNVMCON()
426 # Configuration registers must be treated separately
427 for addr in ph_addrs[ph_addrs.index(last_code_addr):]:
430 if (addr>>1) > 0xf80017:
432 wr_cfg_li = [0x040200, # GOTO 0x0200
434 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
435 0x24000A, # MOV #0x4000, W10
436 0x883B0A, # MOV W10, NVMCON
437 0x200F80, # MOV #0xF8, W0
438 0x880190, # MOV W0, TBLPAG
439 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
440 0xBB0B80, # TBLWTL W0, [W7]
443 0xA8E761, # BSET NVMCON, #WR
449 status = readNVMCON()
450 while status & 0x8000:
451 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
452 status = readNVMCON()
457 elif sys.argv[1] == "verify":
458 if len(sys.argv) != 3:
459 print "Error: an Intel HEX file with which to compare must be given."
463 proghex = IntelHex( sys.argv[2] )
465 print "Error while attempting to read from %s" % sys.argv[2]
468 print "WARNING: verifying config registers not yet supported."
470 for addr in proghex.addresses():
471 if addr % 4 != 0 or (addr>>1) < 0x200:
473 #elif first_addr == None:
474 # first_addr = addr>>1
475 if (addr>>1) >= 0xf80000:
478 dumphex = dump_pm( (proghex.addresses()[0]>>1),
481 # Step through addresses in HEX file and compare to those in target PIC
482 for addr in proghex.addresses():
483 if addr>>1 >= 0xF80000:
484 break # verifying config registers not yet supported
485 # Compare; fail if mismatch
486 if proghex[addr] != dumphex[addr]:
487 addr &= addr&0xfffffffc
488 found_instr = dumphex[addr]
489 found_instr |= dumphex[addr+1]<<8
490 found_instr |= dumphex[addr+2]<<16
491 exp_instr = proghex[addr]
492 exp_instr |= proghex[addr+1]<<8
493 exp_instr |= proghex[addr+2]<<16
494 print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % ( addr>>1, found_instr, exp_instr )
497 print "PASSED" # Be verbose.
500 elif sys.argv[1] == "write":
501 if len(sys.argv) != 4:
502 print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
505 addr = int(float.fromhex(sys.argv[2]))
506 new_instr = int(float.fromhex(sys.argv[3]))
509 # new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
511 #new_words = instr2words( new_instr_li )
513 wr_pm_li = [0x040200, # GOTO 0x0200
516 0x24003A, # MOV $0x4003, W10
517 0x883B0A, # MOV W10, NVMCON
518 0x200000 + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
519 0x880190, # MOV W0, TBLPAG
520 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
522 # Here we load the instruction into registers W0:W1
523 0x200000 + ((new_instr&0xffff)<<4),
524 0x200001 + ((new_instr>>12)&0xff0),
525 #0x200000 + ((new_words[0]&0xffff)<<4),
526 #0x200001 + ((new_words[1]&0xffff)<<4),
527 #0x200002 + ((new_words[2]&0xffff)<<4),
528 #0x200003 + ((new_words[3]&0xffff)<<4),
529 #0x200004 + ((new_words[4]&0xffff)<<4),
530 #0x200005 + ((new_words[5]&0xffff)<<4),
534 0xBB0BB6, # TBLWTL [W6++], [W7]
537 0xBB8B96, # TBLWTH [W6], [W7]
538 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
541 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
544 #0xBB1BB6, # TBLWTL [W6++], [W7++]
547 #0xBB0BB6, # TBLWTL [W6++], [W7]
550 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
553 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
556 #0xBB1BB6, # TBLWTL [W6++], [W7++]
560 0xA8E761, # BSET NVMCON, #WR
569 status = readNVMCON()
570 while status & 0x8000:
571 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
572 status = readNVMCON()
576 elif sys.argv[1] == "write_config":
577 if len(sys.argv) < 3:
578 print "Error: please specify the target config register."
580 elif len(sys.argv) == 3:
581 new_val = 0xFF # Assume new value
583 new_val = int(float.fromhex(sys.argv[3]))
585 reg_addr = int(float.fromhex(sys.argv[2]))
587 if sys.argv[2] not in cfg_table.values():
588 print "Given register, %s, not recognized." % sys.argv[2]
590 reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
592 wr_cfg_li = [0x040200, # GOTO 0x0200
594 0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
595 0x24000A, # MOV #0x4000, W10
596 0x883B0A, # MOV W10, NVMCON
597 0x200F80, # MOV #0xF8, W0
598 0x880190, # MOV W0, TBLPAG
599 0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
600 0xBB0B80, # TBLWTL W0, [W7]
603 0xA8E761, # BSET NVMCON, #WR
612 status = readNVMCON()
613 while status & 0x8000:
614 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
615 status = readNVMCON()
620 elif sys.argv[1] == "read": # Read an address of program memory
623 addr = int(float.fromhex(sys.argv[2]))
625 readpm_cmd_li = [0x040200, # GOTO 0x0200
627 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
628 0x880190, # MOV W0, TBLPAG
629 0x200006+((addr & 0xffff)<<4), # MOV #addr<15:0>, W6
633 0xBA1B96, # TBLRDL [W6], [W7++]
636 0xBACB96, # TBLRDH.B [W6], [W7]
640 runlist( readpm_cmd_li )
641 loww = readreg(0) # Read W0, which has lower 16 bits
642 highb = readreg(1) # Read W1, which has high 8 bits
643 result = (highb<<16) | loww
645 print "0x%06X: 0x%06X" % ( addr, result )
649 elif sys.argv[1] == "dump": # Read section of program memory
650 if len(sys.argv) < 3:
651 print "Error: please specify file in which to dump program memory."
655 if len(sys.argv) == 6:
660 if len(sys.argv) > 4:
661 start_addr = int(float.fromhex(sys.argv[3]))
662 stop_addr = int(float.fromhex(sys.argv[4]))
664 # Assume all of program memory (including unimplemented sections,
665 # which will be read as zeroes.
669 dumphex = dump_pm( start_addr, stop_addr, print_term )
673 dumphex.tofile( sys.stdout, format="hex" )
675 dumphex.tofile( fname, format="hex" )
678 elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
680 if len(sys.argv) == 3:
681 addr = int(float.fromhex(sys.argv[2]))
685 if addr is None: # Bulk erase (all of program memory)
686 erase_cmd_li = [0x040200, # GOTO 0x0200
688 0x2404FA, # MOV $0x404F, W10
689 0x883B0A, # MOV W10, NVMCON
690 0xA8E761, # BSET NVMCON, #WR
696 print "Erasing page of 0x%06X" % addr
697 erase_cmd_li = [0x040200, # GOTO 0x0200
699 0x24042A, # MOV $0x4042, W10
700 0x883B0A, # MOV W10, NVMCON
701 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
702 0x880190, # MOV W0, TBLPAG
703 0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
705 0xBB0881, # TBLTWL W1, [W1]
708 0xA8E761, # BSET NVMCON, #WR
713 # Note that page alignment (effectively, bitmask of 0xffff80
714 # applied to given program memory address) seems to be
715 # enforced by hardware.
719 runlist( erase_cmd_li )
720 status = readNVMCON()
721 while status & 0x8000:
722 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
723 status = readNVMCON()
727 elif sys.argv[1] == "six": #0x82
730 if len(sys.argv) < 3:
731 instr = 0x000000 # Assume nop
733 instr = int(float.fromhex(sys.argv[2]))
734 print "Loading 0x%06X for execution..." % instr
735 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
737 (instr >> 16) & 0xff] )
738 print "Executing (by loading nop; pumping clock)..."
739 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
745 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
748 if len(sys.argv) < 3:
749 print "Warning: no file given; trying cmd.txt..."
755 instrfile = open( fname, "r" )
757 print "Failed to open file %s for reading." % fname
761 print "Opened file %s. Consecutively reading commmands..." % fname
762 for line in instrfile:
766 instr = int(float.fromhex(words[0]))
768 print "Warning: invalid instruction found at offset %d." % instrfile.tell()
770 print "Loading 0x%06X for execution..." % instr
771 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
773 (instr >> 16) & 0xff] )
775 # print "Warning: empty line found at offset %d." % instrfile.tell()
777 print "Error: exception caught while reading file %s." % fname
784 print "Loading nop, pumping clock (to finish execution)..."
785 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
791 elif sys.argv[1] == "regout": #0x83
794 print "Reading VISI register..."
795 data = client.writecmd( PICAPP, 0x83, 0 )
796 result = ord(data[0])
797 result |= ord(data[1]) << 8
798 print "VISI: 0x%04X" % result
803 print "Unrecognized verb: %s" % sys.argv[1]