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 dump_pm( start_addr, stop_addr, pretty=False ):
126 """Dump routine, now encapsulated in a function.
127 Returns an instance of IntelHex corresponding to result.
129 Note that we start and stop an ICSP session here! This means an
130 existing session will be broken."""
131 readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
132 0x880190, # MOV W0, TBLPAG
133 0x200006, # MOV #addr<15:0>, W6
136 0xBA1B96, # TBLRDL [W6], [W7++]
139 0xBADBB6, # TBLRDH.B [W6++], [W7++]
142 0xBADBD6, # TBLRDH.B [++W6], [W7++]
145 0xBA1BB6, # TBLRDL [W6++], [W7++]
148 0xBA1B96, # TBLRDL [W6], [W7++]
151 0xBADBB6, # TBLRDH.B [W6++], [W7++]
154 0xBADBD6, # TBLRDH.B [++W6], [W7++]
157 0xBA0BB6, # TBLRDL [W6++], [W7]
165 print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
167 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
168 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
169 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
171 last_highb = -1 # Only set TBLPAG when needed.
172 packed_instr_list = [0,0,0,0,0,0] # For packing 4 (24-bit) instructions in 6 (16-bit) words
173 for addr in range( start_addr&0xfffff8, stop_addr+8, 8 ):
174 if (addr>>16)&0xff != last_highb:
175 last_highb = (addr>>16)&0xff;
176 specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
178 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
180 specify_addr_cmd = [readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
181 runlist( specify_addr_cmd + readpm_cmd_li[3:] )
183 for reg_num in range(6): # Read W0:5, to get packed instructions
184 packed_instr_list[reg_num] = readreg( reg_num )
185 instr_list = words2instr( packed_instr_list )
187 for offset in range(4): # Print result
188 if addr+offset*2 < start_addr:
190 if addr+offset*2 > stop_addr:
192 dumphex.puts( ((addr+offset*2)&0xffffff)*2,
193 chr(instr_list[offset]&0xff)
194 +chr((instr_list[offset]>>8)&0xff)
195 +chr((instr_list[offset]>>16)&0xff)
198 print "0x%06X 0x%06X" % (addr+offset*2,instr_list[offset])
205 def instr2words( instr_list ):
206 """Convert a list of 4 24-bit instructions to a list of 6 words
209 Returns [-1] on failure."""
210 if len(instr_list) < 4: # Catch mistakes
211 print "Error in instr2words: operand has less than 4 elements."
213 word_list = [0,0,0,0,0,0]
215 word_list[k*3] = instr_list[k*2] & 0xffff
216 word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
217 word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
218 word_list[k*3+2] = instr_list[k*2+1] & 0xffff
221 def words2instr( word_list ):
222 """4 24-bit instructions from a packing in 6 words (16 bit width).
223 This is the inverse of function instr2words.
225 Returns [-1] on failure."""
226 if len(word_list) < 6: # Catch mistakes
227 print "Error in words2instr: operand has less than 6 elements."
229 instr_list = [0,0,0,0]
231 instr_list[k*2] = word_list[k*3]
232 instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
233 instr_list[k*2+1] = word_list[k*3+2]
234 instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
242 if len(sys.argv) == 1:
243 print "Usage: %s verb [objects]\n" % sys.argv[0]
244 print "%s devid" % sys.argv[0]
245 print "%s read 0x$addr" % sys.argv[0]
246 print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
247 print "%s config" % sys.argv[0]
248 print "%s program $foo.hex" % sys.argv[0]
249 print "%s verify $foo.hex" % sys.argv[0]
250 print "%s write 0x$address 0x$value" % sys.argv[0]
251 print "%s write_config 0x$reg_address (or $reg_name) [$0x0000]" % sys.argv[0]
252 print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
253 print "%s six [instruction]" % sys.argv[0]
254 print "%s sixfile [$foo.txt]" % sys.argv[0]
255 print "%s regout" % sys.argv[0]
257 Note: use - for stdout.
258 Warning: only supports dsPIC33F/PIC24H (maybe)...
262 # Initialize and open connection to GoodFET
264 client.verbose = False # Dump activity to terminal
265 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
268 # Handle each possible PIC verb in turn
270 if sys.argv[1] == "devid": #0x81
271 print "Requesting Application ID, DEVID and hardware revision..."
272 data = client.writecmd( PICAPP, 0x81, 0 )
279 devid = ord(data[1]) + (ord(data[2]) << 8)
283 hwrev = ord(data[3]) + (ord(data[4]) << 8)
286 print "Application ID: 0x%02X" % appid
287 if dev_table.has_key( devid ):
288 print "DEVID: 0x%04X (%s)" % ( devid, dev_table[devid] )
290 print "DEVID: 0x%04X (unknown)" % devid
291 print "revision: 0x%04X"% hwrev
292 #print "\n(Note that -1 indicates failure to read a value.)"
294 elif sys.argv[1] == "config": # Dump configuration registers
295 prep_cmd_li = [0x040200, # GOTO 0x0200
297 0x200F80, # MOV #0x00F8, W0
298 0x880190, # MOV W0, TBLPAG
300 0x207847, # MOV #VISI, W7
302 rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
306 print "Dumping configuration registers..."
307 for instr in prep_cmd_li:
308 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
310 (instr >> 16) & 0xff] )
311 for k in range(12): # twelve configuration registers total
312 for instr in rd_cmd_li:
313 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
315 (instr >> 16) & 0xff] )
318 if cfg_table.has_key( addr ):
319 print "0x%06X (%s): 0x%02X" % ( addr,
320 cfg_table[addr].ljust(cfg_table["width"]),
323 print "0x%06X: 0x%02X" % ( addr, result )
327 elif sys.argv[1] == "program":
328 if len(sys.argv) != 3:
329 print "Error: an Intel HEX file to load must be given."
333 proghex = IntelHex( sys.argv[2] )
335 print "Error while attempting to read from %s" % sys.argv[2]
337 ph_addrs = proghex.addresses()
339 # Load starting program memory address
340 wr_pm64_li_setaddr = [0x040200, # GOTO 0x0200
342 0x24001A, # MOV $0x4001, W10
343 0x883B0A, # MOV W10, NVMCON
344 0x200000, # + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
345 0x880190, # MOV W0, TBLPAG
346 0x200007] # + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
348 # Load 4 instructions into write latches
349 wr_pm64_li_wrlat = [0xEB0300, # CLR W6
351 0xBB0BB6, # TBLWTL [W6++], [W7]
354 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
357 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
360 0xBB1BB6, # TBLWTL [W6++], [W7++]
363 0xBB0BB6, # TBLWTL [W6++], [W7]
366 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
369 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
372 0xBB1BB6, # TBLWTL [W6++], [W7++]
376 wr_pm64_li_wr = [0xA8E761, # BSET NVMCON, #WR
384 for last_code_addr in reversed(ph_addrs):
385 if (last_code_addr>>1) < 0xf80000:
386 last_row_addr = (last_code_addr>>1) & 0xffff80
388 for addr in range((ph_addrs[0]>>1)&0xffff80,last_code_addr+2,128):
389 # Prevent crossing addresses where upper byte
390 # (i.e., address<23:16>) changes.
391 #if addr+126 > 0xffff:
392 # stop_addr = addr | 0xffff
394 # stop_addr = addr+126
396 runlist( wr_pm64_li_setaddr[:4]
397 + [0x200000 + ((addr>>12)&0xff0),
398 wr_pm64_li_setaddr[5],
399 0x200007 + ((addr&0xffff)<<4)] )
401 instr_list = [0,0,0,0]
402 for mid_addr in range(addr,addr+126,8):
403 for offset in range(4):
404 if (mid_addr+offset*2)<<1 not in ph_addrs:
405 instr_list[offset] = 0xffffff
407 instr_list[offset] = proghex[(mid_addr+offset*2)<<1]
408 instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+1] << 8
409 instr_list[offset] |= proghex[((mid_addr+offset*2)<<1)+2] << 16
410 packed_instr_list = instr2words( instr_list )
411 runlist( [0x200000 + ((packed_instr_list[0]&0xffff)<<4),
412 0x200001 + ((packed_instr_list[1]&0xffff)<<4),
413 0x200002 + ((packed_instr_list[2]&0xffff)<<4),
414 0x200003 + ((packed_instr_list[3]&0xffff)<<4),
415 0x200004 + ((packed_instr_list[4]&0xffff)<<4),
416 0x200005 + ((packed_instr_list[5]&0xffff)<<4)]
419 runlist( wr_pm64_li_wr )
420 status = readNVMCON()
421 while status & 0x8000:
422 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
423 status = readNVMCON()
425 # Configuration registers must be treated separately
426 for addr in ph_addrs[ph_addrs.index(last_code_addr):]:
429 if (addr>>1) > 0xf80017:
431 wr_cfg_li = [0x040200, # GOTO 0x0200
433 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
434 0x24000A, # MOV #0x4000, W10
435 0x883B0A, # MOV W10, NVMCON
436 0x200F80, # MOV #0xF8, W0
437 0x880190, # MOV W0, TBLPAG
438 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
439 0xBB0B80, # TBLWTL W0, [W7]
442 0xA8E761, # BSET NVMCON, #WR
448 status = readNVMCON()
449 while status & 0x8000:
450 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
451 status = readNVMCON()
456 elif sys.argv[1] == "verify":
457 if len(sys.argv) != 3:
458 print "Error: an Intel HEX file with which to compare must be given."
462 proghex = IntelHex( sys.argv[2] )
464 print "Error while attempting to read from %s" % sys.argv[2]
467 print "WARNING: verifying config registers not yet supported."
469 for addr in proghex.addresses():
470 if addr % 4 != 0 or (addr>>1) < 0x200:
472 #elif first_addr == None:
473 # first_addr = addr>>1
474 if (addr>>1) >= 0xf80000:
477 dumphex = dump_pm( (proghex.addresses()[0]>>1),
480 # Step through addresses in HEX file and compare to those in target PIC
481 for addr in proghex.addresses():
482 if addr>>1 >= 0xF80000:
483 break # verifying config registers not yet supported
484 # Compare; fail if mismatch
485 if proghex[addr] != dumphex[addr]:
486 addr &= addr&0xfffffffc
487 found_instr = dumphex[addr]
488 found_instr |= dumphex[addr+1]<<8
489 found_instr |= dumphex[addr+2]<<16
490 exp_instr = proghex[addr]
491 exp_instr |= proghex[addr+1]<<8
492 exp_instr |= proghex[addr+2]<<16
493 print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % ( addr>>1, found_instr, exp_instr )
496 print "PASSED" # Be verbose.
499 elif sys.argv[1] == "write":
500 if len(sys.argv) != 4:
501 print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
504 addr = int(float.fromhex(sys.argv[2]))
505 new_instr = int(float.fromhex(sys.argv[3]))
508 # new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
510 #new_words = instr2words( new_instr_li )
512 wr_pm_li = [0x040200, # GOTO 0x0200
515 0x24003A, # MOV $0x4003, W10
516 0x883B0A, # MOV W10, NVMCON
517 0x200000 + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
518 0x880190, # MOV W0, TBLPAG
519 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
521 # Here we load the instruction into registers W0:W1
522 0x200000 + ((new_instr&0xffff)<<4),
523 0x200001 + ((new_instr>>12)&0xff0),
524 #0x200000 + ((new_words[0]&0xffff)<<4),
525 #0x200001 + ((new_words[1]&0xffff)<<4),
526 #0x200002 + ((new_words[2]&0xffff)<<4),
527 #0x200003 + ((new_words[3]&0xffff)<<4),
528 #0x200004 + ((new_words[4]&0xffff)<<4),
529 #0x200005 + ((new_words[5]&0xffff)<<4),
533 0xBB0BB6, # TBLWTL [W6++], [W7]
536 0xBB8B96, # TBLWTH [W6], [W7]
537 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
540 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
543 #0xBB1BB6, # TBLWTL [W6++], [W7++]
546 #0xBB0BB6, # TBLWTL [W6++], [W7]
549 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
552 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
555 #0xBB1BB6, # TBLWTL [W6++], [W7++]
559 0xA8E761, # BSET NVMCON, #WR
568 status = readNVMCON()
569 while status & 0x8000:
570 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
571 status = readNVMCON()
575 elif sys.argv[1] == "write_config":
576 if len(sys.argv) < 3:
577 print "Error: please specify the target config register."
579 elif len(sys.argv) == 3:
580 new_val = 0xFF # Assume new value
582 new_val = int(float.fromhex(sys.argv[3]))
584 reg_addr = int(float.fromhex(sys.argv[2]))
586 if sys.argv[2] not in cfg_table.values():
587 print "Given register, %s, not recognized." % sys.argv[2]
589 reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
591 wr_cfg_li = [0x040200, # GOTO 0x0200
593 0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
594 0x24000A, # MOV #0x4000, W10
595 0x883B0A, # MOV W10, NVMCON
596 0x200F80, # MOV #0xF8, W0
597 0x880190, # MOV W0, TBLPAG
598 0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
599 0xBB0B80, # TBLWTL W0, [W7]
602 0xA8E761, # BSET NVMCON, #WR
611 status = readNVMCON()
612 while status & 0x8000:
613 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
614 status = readNVMCON()
619 elif sys.argv[1] == "read": # Read an address of program memory
622 addr = int(float.fromhex(sys.argv[2]))
624 readpm_cmd_li = [0x040200, # GOTO 0x0200
626 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
627 0x880190, # MOV W0, TBLPAG
628 0x200006+((addr & 0xffff)<<4), # MOV #addr<15:0>, W6
632 0xBA1B96, # TBLRDL [W6], [W7++]
635 0xBACB96, # TBLRDH.B [W6], [W7]
639 runlist( readpm_cmd_li )
640 loww = readreg(0) # Read W0, which has lower 16 bits
641 highb = readreg(1) # Read W1, which has high 8 bits
642 result = (highb<<16) | loww
644 print "0x%06X: 0x%06X" % ( addr, result )
648 elif sys.argv[1] == "dump": # Read section of program memory
649 if len(sys.argv) < 3:
650 print "Error: please specify file in which to dump program memory."
654 if len(sys.argv) == 6:
659 if len(sys.argv) > 4:
660 start_addr = int(float.fromhex(sys.argv[3]))
661 stop_addr = int(float.fromhex(sys.argv[4]))
663 # Assume all of program memory (including unimplemented sections,
664 # which will be read as zeroes.
668 dumphex = dump_pm( start_addr, stop_addr, print_term )
672 dumphex.tofile( sys.stdout, format="hex" )
674 dumphex.tofile( fname, format="hex" )
677 elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
679 if len(sys.argv) == 3:
680 addr = int(float.fromhex(sys.argv[2]))
684 if addr is None: # Bulk erase (all of program memory)
685 erase_cmd_li = [0x040200, # GOTO 0x0200
687 0x2404FA, # MOV $0x404F, W10
688 0x883B0A, # MOV W10, NVMCON
689 0xA8E761, # BSET NVMCON, #WR
695 print "Erasing page of 0x%06X" % addr
696 erase_cmd_li = [0x040200, # GOTO 0x0200
698 0x24042A, # MOV $0x4042, W10
699 0x883B0A, # MOV W10, NVMCON
700 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
701 0x880190, # MOV W0, TBLPAG
702 0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
704 0xBB0881, # TBLTWL W1, [W1]
707 0xA8E761, # BSET NVMCON, #WR
712 # Note that page alignment (effectively, bitmask of 0xffff80
713 # applied to given program memory address) seems to be
714 # enforced by hardware.
718 runlist( erase_cmd_li )
719 status = readNVMCON()
720 while status & 0x8000:
721 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
722 status = readNVMCON()
726 elif sys.argv[1] == "six": #0x82
729 if len(sys.argv) < 3:
730 instr = 0x000000 # Assume nop
732 instr = int(float.fromhex(sys.argv[2]))
733 print "Loading 0x%06X for execution..." % instr
734 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
736 (instr >> 16) & 0xff] )
737 print "Executing (by loading nop; pumping clock)..."
738 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
744 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
747 if len(sys.argv) < 3:
748 print "Warning: no file given; trying cmd.txt..."
754 instrfile = open( fname, "r" )
756 print "Failed to open file %s for reading." % fname
760 print "Opened file %s. Consecutively reading commmands..." % fname
761 for line in instrfile:
765 instr = int(float.fromhex(words[0]))
767 print "Warning: invalid instruction found at offset %d." % instrfile.tell()
769 print "Loading 0x%06X for execution..." % instr
770 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
772 (instr >> 16) & 0xff] )
774 # print "Warning: empty line found at offset %d." % instrfile.tell()
776 print "Error: exception caught while reading file %s." % fname
783 print "Loading nop, pumping clock (to finish execution)..."
784 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
790 elif sys.argv[1] == "regout": #0x83
793 print "Reading VISI register..."
794 data = client.writecmd( PICAPP, 0x83, 0 )
795 result = ord(data[0])
796 result |= ord(data[1]) << 8
797 print "VISI: 0x%04X" % result
802 print "Unrecognized verb: %s" % sys.argv[1]