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 cfg_table = { 0xF80000 : "FBS",
41 "width" : 7 } # For pretty printing.
43 #cfg_bitmask_table = {
50 def runlist( cmd_li ):
51 """Load (and execute) a given list of instructions.
52 Assumes ICSP session already started."""
53 cmd_byte_li = build_instr_stream( cmd_li )
54 data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
55 if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
56 print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
59 elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
60 print "Error (in runlist): failed transaction; aborting."
64 def build_instr_stream( cmd_li ):
65 """Given a list of instruction words, returns a list of bytes of
66 the same, in little endian ordering."""
69 cmd_byte_li += [instr & 0xff,
74 def readreg( reg_num ):
75 """Read contents of a working register (i.e. W0, W1, ..., W15)."""
76 instr = 0x883C20+(reg_num&0xf)
77 client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
79 (instr >> 16) & 0xff] )
80 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
83 def writereg( reg_num, word ):
84 """Write 16-bit word to a working register (i.e., W0, W1, ..., W15)."""
85 instr = 0x200000 + ((word&0xffff)<<4) + (reg_num&0xf)
86 client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
88 (instr >> 16) & 0xff] )
91 """Read VISI register; assumes ICSP session already started."""
92 data = client.writecmd( PICAPP, 0x83, 0 )
94 result |= ord(data[1]) << 8
98 """Read NVMCON register; assumes ICSP session already started."""
99 rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
100 0x883C20, # MOV W0, VISI
103 runlist( rd_nvmcon_li )
107 """Reset program counter during an active ISCP session."""
108 runlist( [0x040200, # GOTO 0x0200
112 #print "Starting dsPIC33F/PIC24H ICSP session..."
113 data = client.writecmd( PICAPP, 0x84, 0 )
116 #print "Stopping dsPIC33F/PIC24H ICSP session..."
117 data = client.writecmd( PICAPP, 0x85, 0 )
120 """Read and print VISI register to stdout; assumes ICSP session already started."""
121 print "Reading VISI register..."
123 print "VISI: 0x%04X" % result
125 def instr2words( instr_list ):
126 """Convert a list of 4 24-bit instructions to a list of 6 words
129 Returns [-1] on failure."""
130 if len(instr_list) < 4: # Catch mistakes
131 print "Error in instr2words: operand has less than 4 elements."
133 word_list = [0,0,0,0,0,0]
135 word_list[k*3] = instr_list[k*2] & 0xffff
136 word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
137 word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
138 word_list[k*3+2] = instr_list[k*2+1] & 0xffff
141 def words2instr( word_list ):
142 """4 24-bit instructions from a packing in 6 words (16 bit width).
143 This is the inverse of function instr2words.
145 Returns [-1] on failure."""
146 if len(word_list) < 6: # Catch mistakes
147 print "Error in words2instr: operand has less than 6 elements."
149 instr_list = [0,0,0,0]
151 instr_list[k*2] = word_list[k*3]
152 instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
153 instr_list[k*2+1] = word_list[k*3+2]
154 instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
162 if len(sys.argv) == 1:
163 print "Usage: %s verb [objects]\n" % sys.argv[0]
164 print "%s devid" % sys.argv[0]
165 print "%s read 0x$addr" % sys.argv[0]
166 print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
167 print "%s config" % sys.argv[0]
168 print "%s program $foo.hex" % sys.argv[0]
169 print "%s verify $foo.hex" % sys.argv[0]
170 print "%s write 0x$address 0x$value" % sys.argv[0]
171 print "%s write_config 0x$reg_address (or $reg_name) [$0x0000]" % sys.argv[0]
172 print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
173 print "%s six [instruction]" % sys.argv[0]
174 print "%s sixfile [$foo.txt]" % sys.argv[0]
175 print "%s regout" % sys.argv[0]
177 Note: use - for stdout.
178 Warning: only supports dsPIC33F/PIC24H (maybe)...
182 # Initialize and open connection to GoodFET
184 client.verbose = False # Dump activity to terminal
185 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
188 # Handle each possible PIC verb in turn
190 if sys.argv[1] == "devid": #0x81
191 print "Requesting Application ID, DEVID and hardware revision..."
192 data = client.writecmd( PICAPP, 0x81, 0 )
199 devid = ord(data[1]) + (ord(data[2]) << 8)
203 hwrev = ord(data[3]) + (ord(data[4]) << 8)
206 print "Application ID: 0x%02X" % appid
207 if dev_table.has_key( devid ):
208 print "DEVID: 0x%04X (%s)" % ( devid, dev_table[devid] )
210 print "DEVID: 0x%04X (unknown)" % devid
211 print "revision: 0x%04X"% hwrev
212 #print "\n(Note that -1 indicates failure to read a value.)"
214 elif sys.argv[1] == "config": # Dump configuration registers
215 prep_cmd_li = [0x040200, # GOTO 0x0200
217 0x200F80, # MOV #0x00F8, W0
218 0x880190, # MOV W0, TBLPAG
220 0x207847, # MOV #VISI, W7
222 rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
226 print "Dumping configuration registers..."
227 for instr in prep_cmd_li:
228 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
230 (instr >> 16) & 0xff] )
231 for k in range(12): # twelve configuration registers total
232 for instr in rd_cmd_li:
233 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
235 (instr >> 16) & 0xff] )
238 if cfg_table.has_key( addr ):
239 print "0x%06X (%s): 0x%02X" % ( addr,
240 cfg_table[addr].ljust(cfg_table["width"]),
243 print "0x%06X: 0x%02X" % ( addr, result )
247 elif sys.argv[1] == "program":
248 if len(sys.argv) != 3:
249 print "Error: an Intel HEX file to load must be given."
253 proghex = IntelHex( sys.argv[2] )
255 print "Error while attempting to read from %s" % sys.argv[2]
257 ph_addrs = proghex.addresses()
259 # Load starting program memory address
260 wr_pm64_li_setaddr = [0x040200, # GOTO 0x0200
262 0x24001A, # MOV $0x4001, W10
263 0x883B0A, # MOV W10, NVMCON
264 0x200000, # + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
265 0x880190, # MOV W0, TBLPAG
266 0x200007] # + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
268 # Load 4 instructions into write latches
269 wr_pm64_li_wrlat = [0xEB0300, # CLR W6
271 0xBB0BB6, # TBLWTL [W6++], [W7]
274 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
277 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
280 0xBB1BB6, # TBLWTL [W6++], [W7++]
283 0xBB0BB6, # TBLWTL [W6++], [W7]
286 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
289 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
292 0xBB1BB6, # TBLWTL [W6++], [W7++]
296 wr_pm64_li_wr = [0xA8E761, # BSET NVMCON, #WR
304 for last_code_addr in reversed(ph_addrs):
305 if (last_code_addr>>1) < 0xf80000:
306 last_row_addr = (last_code_addr>>1) & 0xffff80
308 for addr in range((ph_addrs[0]>>1)&0xffff80,last_code_addr+2,128):
309 # Prevent crossing addresses where upper byte
310 # (i.e., address<23:16>) changes.
311 #if addr+126 > 0xffff:
312 # stop_addr = addr | 0xffff
314 # stop_addr = addr+126
316 runlist( wr_pm64_li_setaddr[:4]
317 + [0x200000 + ((addr>>12)&0xff0),
318 wr_pm64_li_setaddr[5],
319 0x200007 + ((addr&0xffff)<<4)] )
321 instr_list = [0,0,0,0]
322 for mid_addr in range(addr,addr+126,8):
323 for offset in range(4):
324 if (mid_addr+offset*2)<<1 not in ph_addrs:
325 instr_list[offset] = 0xffffff
327 instr_list[offset] = proghex[(mid_addr+offset*2)<<1]
328 instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+1] << 8
329 instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+2] << 16
330 packed_instr_list = instr2words( instr_list )
331 runlist( [0x200000 + ((packed_instr_list[0]&0xffff)<<4),
332 0x200001 + ((packed_instr_list[1]&0xffff)<<4),
333 0x200002 + ((packed_instr_list[2]&0xffff)<<4),
334 0x200003 + ((packed_instr_list[3]&0xffff)<<4),
335 0x200004 + ((packed_instr_list[4]&0xffff)<<4),
336 0x200005 + ((packed_instr_list[5]&0xffff)<<4)]
339 runlist( wr_pm64_li_wr )
340 status = readNVMCON()
341 while status & 0x8000:
342 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
343 status = readNVMCON()
345 # Configuration registers must be treated separately
346 for addr in ph_addrs[ph_addrs.index(last_code_addr):]:
349 if (addr>>1) > 0xf80017:
351 wr_cfg_li = [0x040200, # GOTO 0x0200
353 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
354 0x24000A, # MOV #0x4000, W10
355 0x883B0A, # MOV W10, NVMCON
356 0x200F80, # MOV #0xF8, W0
357 0x880190, # MOV W0, TBLPAG
358 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
359 0xBB0B80, # TBLWTL W0, [W7]
362 0xA8E761, # BSET NVMCON, #WR
368 status = readNVMCON()
369 while status & 0x8000:
370 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
371 status = readNVMCON()
376 elif sys.argv[1] == "verify":
377 if len(sys.argv) != 3:
378 print "Error: an Intel HEX file with which to compare must be given."
382 proghex = IntelHex( sys.argv[2] )
384 print "Error while attempting to read from %s" % sys.argv[2]
387 readpm_cmd_li = [0x040200, # GOTO 0x0200
389 0x200000, # MOV #addr_pm<23:16>, W0
390 0x880190, # MOV W0, TBLPAG
391 0x200006, # MOV #addr_pm<15:0>, W6
395 0xBA1B96, # TBLRDL [W6], [W7++]
398 0xBACB96, # TBLRDH.B [W6], [W7]
402 print "WARNING: verifying config registers not yet supported."
406 # Step through addresses in HEX file and compare to those in target PIC
407 for addr in proghex.addresses():
408 # Ignore non-aligned steps
412 # Configuration registers must be treated separately
413 if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
414 # Need to read datasheets to see which bits are masked.
415 # Then add dictionary to handle this.
416 # (Straightforward but tedious.)
419 # Build instruction from 3 separate bytes
420 instr = proghex[addr]
421 instr |= proghex[addr+1] << 8
422 instr |= proghex[addr+2] << 16
424 # Change HEX file address to actual program memory address
427 runlist( readpm_cmd_li[:2] + [readpm_cmd_li[2]+((addr_pm&0xff0000)>>12)]
428 + [readpm_cmd_li[3]] + [readpm_cmd_li[4]+((addr_pm&0xffff)<<4)]
429 + readpm_cmd_li[5:] )
430 loww = readreg(0) # Read W0, which has lower 16 bits
431 highb = readreg(1) # Read W1, which has high 8 bits
432 result = (highb<<16) | loww
434 # Compare; fail if mismatch
436 print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % (addr_pm, result, instr )
442 print "PASSED" # Be verbose.
445 elif sys.argv[1] == "write":
446 if len(sys.argv) != 4:
447 print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
450 addr = int(float.fromhex(sys.argv[2]))
451 new_instr = int(float.fromhex(sys.argv[3]))
454 # new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
456 #new_words = instr2words( new_instr_li )
458 wr_pm_li = [0x040200, # GOTO 0x0200
461 0x24003A, # MOV $0x4003, W10
462 0x883B0A, # MOV W10, NVMCON
463 0x200000 + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
464 0x880190, # MOV W0, TBLPAG
465 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
467 # Here we load the instruction into registers W0:W1
468 0x200000 + ((new_instr&0xffff)<<4),
469 0x200001 + ((new_instr>>12)&0xff0),
470 #0x200000 + ((new_words[0]&0xffff)<<4),
471 #0x200001 + ((new_words[1]&0xffff)<<4),
472 #0x200002 + ((new_words[2]&0xffff)<<4),
473 #0x200003 + ((new_words[3]&0xffff)<<4),
474 #0x200004 + ((new_words[4]&0xffff)<<4),
475 #0x200005 + ((new_words[5]&0xffff)<<4),
479 0xBB0BB6, # TBLWTL [W6++], [W7]
482 0xBB8B96, # TBLWTH [W6], [W7]
483 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
486 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
489 #0xBB1BB6, # TBLWTL [W6++], [W7++]
492 #0xBB0BB6, # TBLWTL [W6++], [W7]
495 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
498 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
501 #0xBB1BB6, # TBLWTL [W6++], [W7++]
505 0xA8E761, # BSET NVMCON, #WR
514 status = readNVMCON()
515 while status & 0x8000:
516 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
517 status = readNVMCON()
521 elif sys.argv[1] == "write_config":
522 if len(sys.argv) < 3:
523 print "Error: please specify the target config register."
525 elif len(sys.argv) == 3:
526 new_val = 0xFF # Assume new value
528 new_val = int(float.fromhex(sys.argv[3]))
530 reg_addr = int(float.fromhex(sys.argv[2]))
532 if sys.argv[2] not in cfg_table.values():
533 print "Given register, %s, not recognized." % sys.argv[2]
535 reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
537 wr_cfg_li = [0x040200, # GOTO 0x0200
539 0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
540 0x24000A, # MOV #0x4000, W10
541 0x883B0A, # MOV W10, NVMCON
542 0x200F80, # MOV #0xF8, W0
543 0x880190, # MOV W0, TBLPAG
544 0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
545 0xBB0B80, # TBLWTL W0, [W7]
548 0xA8E761, # BSET NVMCON, #WR
557 status = readNVMCON()
558 while status & 0x8000:
559 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
560 status = readNVMCON()
565 elif sys.argv[1] == "read": # Read an address of program memory
568 addr = int(float.fromhex(sys.argv[2]))
570 readpm_cmd_li = [0x040200, # GOTO 0x0200
572 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
573 0x880190, # MOV W0, TBLPAG
574 0x200006+((addr & 0xffff)<<4), # MOV #addr<15:0>, W6
578 0xBA1B96, # TBLRDL [W6], [W7++]
581 0xBACB96, # TBLRDH.B [W6], [W7]
585 runlist( readpm_cmd_li )
586 loww = readreg(0) # Read W0, which has lower 16 bits
587 highb = readreg(1) # Read W1, which has high 8 bits
588 result = (highb<<16) | loww
590 print "0x%06X: 0x%06X" % ( addr, result )
594 elif sys.argv[1] == "dump": # Read section of program memory
595 if len(sys.argv) < 3:
596 print "Error: please specify file in which to dump program memory."
600 if len(sys.argv) == 6:
605 if len(sys.argv) > 4:
606 start_addr = int(float.fromhex(sys.argv[3]))
607 stop_addr = int(float.fromhex(sys.argv[4]))
609 # Assume all of program memory (including unimplemented sections,
610 # which will be read as zeroes.
614 readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
615 0x880190, # MOV W0, TBLPAG
616 0x200006, # MOV #addr<15:0>, W6
619 0xBA1B96, # TBLRDL [W6], [W7++]
622 0xBADBB6, # TBLRDH.B [W6++], [W7++]
625 0xBADBD6, # TBLRDH.B [++W6], [W7++]
628 0xBA1BB6, # TBLRDL [W6++], [W7++]
631 0xBA1B96, # TBLRDL [W6], [W7++]
634 0xBADBB6, # TBLRDH.B [W6++], [W7++]
637 0xBADBD6, # TBLRDH.B [++W6], [W7++]
640 0xBA0BB6, # TBLRDL [W6++], [W7]
648 print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
650 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
651 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
652 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
654 last_highb = -1 # Only set TBLPAG when needed.
655 packed_instr_list = [0,0,0,0,0,0] # For packing 4 (24-bit) instructions in 6 (16-bit) words
656 for addr in range( start_addr&0xfffff8, stop_addr+8, 8 ):
657 if (addr>>16)&0xff != last_highb:
658 last_highb = (addr>>16)&0xff;
659 specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
661 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
663 specify_addr_cmd = [readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
664 runlist( specify_addr_cmd + readpm_cmd_li[3:] )
666 for reg_num in range(6): # Read W0:5, to get packed instructions
667 packed_instr_list[reg_num] = readreg( reg_num )
668 instr_list = words2instr( packed_instr_list )
670 for offset in range(4): # Print result
671 if addr+offset*2 < start_addr:
673 if addr+offset*2 > stop_addr:
675 dumphex.puts( ((addr+offset*2)&0xffffff)*2,
676 chr(instr_list[offset]&0xff)
677 +chr((instr_list[offset]>>8)&0xff)
678 +chr((instr_list[offset]>>16)&0xff)
681 print "0x%06X 0x%06X" % (addr+offset*2,instr_list[offset])
685 dumphex.tofile( sys.stdout, format="hex" )
687 dumphex.tofile( fname, format="hex" )
691 elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
693 if len(sys.argv) == 3:
694 addr = int(float.fromhex(sys.argv[2]))
698 if addr is None: # Bulk erase (all of program memory)
699 erase_cmd_li = [0x040200, # GOTO 0x0200
701 0x2404FA, # MOV $0x404F, W10
702 0x883B0A, # MOV W10, NVMCON
703 0xA8E761, # BSET NVMCON, #WR
709 print "Erasing page of 0x%06X" % addr
710 erase_cmd_li = [0x040200, # GOTO 0x0200
712 0x24042A, # MOV $0x4042, W10
713 0x883B0A, # MOV W10, NVMCON
714 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
715 0x880190, # MOV W0, TBLPAG
716 0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
718 0xBB0881, # TBLTWL W1, [W1]
721 0xA8E761, # BSET NVMCON, #WR
726 # Note that page alignment (effectively, bitmask of 0xffff80
727 # applied to given program memory address) seems to be
728 # enforced by hardware.
732 runlist( erase_cmd_li )
733 status = readNVMCON()
734 while status & 0x8000:
735 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
736 status = readNVMCON()
740 elif sys.argv[1] == "six": #0x82
743 if len(sys.argv) < 3:
744 instr = 0x000000 # Assume nop
746 instr = int(float.fromhex(sys.argv[2]))
747 print "Loading 0x%06X for execution..." % instr
748 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
750 (instr >> 16) & 0xff] )
751 print "Executing (by loading nop; pumping clock)..."
752 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
758 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
761 if len(sys.argv) < 3:
762 print "Warning: no file given; trying cmd.txt..."
768 instrfile = open( fname, "r" )
770 print "Failed to open file %s for reading." % fname
774 print "Opened file %s. Consecutively reading commmands..." % fname
775 for line in instrfile:
779 instr = int(float.fromhex(words[0]))
781 print "Warning: invalid instruction found at offset %d." % instrfile.tell()
783 print "Loading 0x%06X for execution..." % instr
784 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
786 (instr >> 16) & 0xff] )
788 # print "Warning: empty line found at offset %d." % instrfile.tell()
790 print "Error: exception caught while reading file %s." % fname
797 print "Loading nop, pumping clock (to finish execution)..."
798 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
804 elif sys.argv[1] == "regout": #0x83
807 print "Reading VISI register..."
808 data = client.writecmd( PICAPP, 0x83, 0 )
809 result = ord(data[0])
810 result |= ord(data[1]) << 8
811 print "VISI: 0x%04X" % result
816 print "Unrecognized verb: %s" % sys.argv[1]