2 # PIC client (currently only supports dsPIC33F/PIC24H.)
4 # Scott Livingston <slivingston at caltech.edu>
12 from GoodFET import GoodFET
13 from intelhex import IntelHex
16 # Some constants (not really immutable...)
21 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
22 0x00EF : "dsPIC33FJ128GP710" }
23 cfg_table = { 0xF80000 : "FBS",
35 "width" : 7 } # For pretty printing.
42 def runlist( cmd_li ):
43 """Load (and execute) a given list of instructions.
44 Assumes ICSP session already started."""
45 cmd_byte_li = build_instr_stream( cmd_li )
46 data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
47 if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
48 print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
51 elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
52 print "Error (in runlist): failed transaction; aborting."
56 def build_instr_stream( cmd_li ):
57 """Given a list of instruction words, returns a list of bytes of
58 the same, in little endian ordering."""
61 cmd_byte_li += [instr & 0xff,
66 def readreg( reg_num ):
67 """Read contents of a working register (i.e. W0, W1, ..., W15)."""
68 instr = 0x883C20+(reg_num&0xf)
69 client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
71 (instr >> 16) & 0xff] )
72 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
76 """Read VISI register; assumes ICSP session already started."""
77 data = client.writecmd( PICAPP, 0x83, 0 )
79 result |= ord(data[1]) << 8
83 """Read NVMCON register; assumes ICSP session already started."""
84 rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
85 0x883C20, # MOV W0, VISI
88 runlist( rd_nvmcon_li )
92 #print "Starting dsPIC33F/PIC24H ICSP session..."
93 data = client.writecmd( PICAPP, 0x84, 0 )
96 #print "Stopping dsPIC33F/PIC24H ICSP session..."
97 data = client.writecmd( PICAPP, 0x85, 0 )
100 """Read and print VISI register to stdout; assumes ICSP session already started."""
101 print "Reading VISI register..."
103 print "VISI: 0x%04X" % result
105 def instr2words( instr_list ):
106 """Convert a list of 4 24-bit instructions to a list of 6 words
109 Returns [-1] on failure."""
110 if len(instr_list) < 4: # Catch mistakes
111 print "Error in instr2words: operand has less than 4 elements."
113 word_list = [0,0,0,0,0,0]
115 word_list[k*3] = instr_list[k*2] & 0xffff
116 word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
117 word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
118 word_list[k*3+2] = instr_list[k*2+1] & 0xffff
121 def words2instr( word_list ):
122 """4 24-bit instructions from a packing in 6 words (16 bit width).
123 This is the inverse of function instr2words.
125 Returns [-1] on failure."""
126 if len(word_list) < 6: # Catch mistakes
127 print "Error in words2instr: operand has less than 6 elements."
129 instr_list = [0,0,0,0]
131 instr_list[k*2] = word_list[k*3]
132 instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
133 instr_list[k*2+1] = word_list[k*3+2]
134 instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
142 if len(sys.argv) == 1:
143 print "Usage: %s verb [objects]" % sys.argv[0]
144 print "Warning: only supports dsPIC33F/PIC24H (maybe)...\n"
145 print "%s devid" % sys.argv[0]
146 print "%s read 0x$addr" % sys.argv[0]
147 print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
148 print "%s config" % sys.argv[0]
149 print "%s program $foo.hex" % sys.argv[0]
150 print "%s verify $foo.hex (NOTE: only addresses in $foo.hex are verified.)" % sys.argv[0]
151 print "%s write $0xADDRESS $0xVALUE" % sys.argv[0]
152 print "%s write_config $REG_NAME [$0x0000]" % sys.argv[0]
153 print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
154 print "%s six [instruction]" % sys.argv[0]
155 print "%s sixfile [$foo.txt] (NOTE: plaintext, one instruction per line.)" % sys.argv[0]
156 print "%s regout" % sys.argv[0]
158 Note: use - for stdout.
162 # Initialize and open connection to GoodFET
164 client.verbose = False # Dump activity to terminal
165 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
168 # Handle each possible PIC verb in turn
170 if sys.argv[1] == "devid": #0x81
171 print "Requesting Application ID, DEVID and hardware revision..."
172 data = client.writecmd( PICAPP, 0x81, 0 )
179 devid = ord(data[1]) + (ord(data[2]) << 8)
183 hwrev = ord(data[3]) + (ord(data[4]) << 8)
186 print "Application ID: 0x%02X" % appid
187 if dev_table.has_key( devid ):
188 print "DEVID: 0x%04X (%s)" % ( devid, dev_table[devid] )
190 print "DEVID: 0x%04X (unknown)" % devid
191 print "revision: 0x%04X"% hwrev
192 #print "\n(Note that -1 indicates failure to read a value.)"
194 elif sys.argv[1] == "config": # Dump configuration registers
197 prep_cmd_li = [0x040200, # GOTO 0x0200
198 0x040200, # GOTO 0x0200
200 0x200F80, # MOV #0x00F8, W0
201 0x880190, # MOV W0, TBLPAG
203 0x207847, # MOV #VISI, W7
205 rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
208 print "Dumping configuration registers..."
209 for instr in prep_cmd_li:
210 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
212 (instr >> 16) & 0xff] )
213 for k in range(12): # twelve configuration registers total
214 for instr in rd_cmd_li:
215 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
217 (instr >> 16) & 0xff] )
220 if cfg_table.has_key( addr ):
221 print "0x%06X (%s): 0x%04X" % ( addr,
222 cfg_table[addr].ljust(cfg_table["width"]),
225 print "0x%06X: 0x%04X" % ( addr, result )
229 elif sys.argv[1] == "program":
230 if len(sys.argv) != 3:
231 print "Error: an Intel HEX file to load must be given."
235 proghex = IntelHex( sys.argv[2] )
237 print "Error while attempting to read from %s" % sys.argv[2]
240 wr_pm_li = [0x040200, # GOTO 0x0200
241 0x040200, # GOTO 0x0200
244 0x24003A, # MOV $0x4003, W10
245 0x883B0A, # MOV W10, NVMCON
247 0x200000,# + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
248 0x880190, # MOV W0, TBLPAG
249 0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
251 # Here we load the instruction into registers W0:W1
252 0x200000,# + ((new_words[0]&0xffff)<<4),
253 0x200001,# + ((new_words[1]&0xffff)<<4),
257 0xBB0BB6, # TBLWTL [W6++], [W7]
260 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
264 0xA8E761, # BSET NVMCON, #WR
272 for addr in proghex.addresses():
273 # Ignore non-aligned steps
277 # Configuration registers must be treated separately
278 if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
279 wr_cfg_li = [0x040200, # GOTO 0x0200
280 0x040200, # GOTO 0x0200
282 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
283 0x24000A, # MOV #0x4000, W10
284 0x883B0A, # MOV W10, NVMCON
285 0x200F80, # MOV #0xF8, W0
286 0x880190, # MOV W0, TBLPAG
287 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
288 0xBB0B80, # TBLWTL W0, [W7]
291 0xA8E761, # BSET NVMCON, #WR
297 status = readNVMCON()
298 while status & 0x8000:
299 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
300 status = readNVMCON()
303 # Build instruction from 3 separate bytes
304 instr = proghex[addr]
305 instr |= proghex[addr+1] << 8
306 instr |= proghex[addr+2] << 16
308 # Change HEX file address to actual program memory address
311 # Program this instruction word
312 addr_highb_cmd = 0x200000 + ((addr_pm>>12)&0xff0)
313 addr_loww_cmd = 0x200007 + ((addr_pm&0xffff)<<4)
314 instr_loww_cmd = 0x200000 + ((instr&0xffff)<<4)
315 instr_highb_cmd = 0x200001 + ((instr>>12)&0xff0)
316 runlist( wr_pm_li[:5] + [addr_highb_cmd] + [wr_pm_li[6]]
318 instr_loww_cmd, instr_highb_cmd]
320 status = readNVMCON()
321 while status & 0x8000:
322 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
323 status = readNVMCON()
328 elif sys.argv[1] == "verify":
329 if len(sys.argv) != 3:
330 print "Error: an Intel HEX file with which to compare must be given."
334 proghex = IntelHex( sys.argv[2] )
336 print "Error while attempting to read from %s" % sys.argv[2]
339 readpm_cmd_li = [0x040200, # GOTO 0x0200
340 0x040200, # GOTO 0x0200
342 0x200000, # MOV #addr_pm<23:16>, W0
343 0x880190, # MOV W0, TBLPAG
344 0x200006, # MOV #addr_pm<15:0>, W6
348 0xBA1B96, # TBLRDL [W6], [W7++]
351 0xBACB96, # TBLRDH.B [W6], [W7]
355 print "WARNING: verifying config registers not yet supported."
359 # Step through addresses in HEX file and compare to those in target PIC
360 for addr in proghex.addresses():
361 # Ignore non-aligned steps
365 # Configuration registers must be treated separately
366 if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
367 # Need to read datasheets to see which bits are masked.
368 # Then add dictionary to handle this.
369 # (Straightforward but tedious.)
372 # Build instruction from 3 separate bytes
373 instr = proghex[addr]
374 instr |= proghex[addr+1] << 8
375 instr |= proghex[addr+2] << 16
377 # Change HEX file address to actual program memory address
380 runlist( readpm_cmd_li[:3] + [readpm_cmd_li[3]+((addr_pm&0xff0000)>>12)]
381 + [readpm_cmd_li[4]] + [readpm_cmd_li[5]+((addr_pm&0xffff)<<4)]
382 + readpm_cmd_li[6:] )
383 loww = readreg(0) # Read W0, which has lower 16 bits
384 highb = readreg(1) # Read W1, which has high 8 bits
385 result = (highb<<16) | loww
387 # Compare; fail if mismatch
389 print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % (addr_pm, result, instr )
395 print "PASSED" # Be verbose.
398 elif sys.argv[1] == "write":
399 if len(sys.argv) != 4:
400 print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
403 addr = int(float.fromhex(sys.argv[2]))
404 new_instr = int(float.fromhex(sys.argv[3]))
407 # new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
409 #new_words = instr2words( new_instr_li )
411 wr_pm_li = [0x040200, # GOTO 0x0200
412 0x040200, # GOTO 0x0200
415 0x24003A, # MOV $0x4003, W10
416 0x883B0A, # MOV W10, NVMCON
417 0x200000 + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
418 0x880190, # MOV W0, TBLPAG
419 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
421 # Here we load the instruction into registers W0:W1
422 0x200000 + ((new_instr&0xffff)<<4),
423 0x200001 + ((new_instr>>12)&0xff0),
424 #0x200000 + ((new_words[0]&0xffff)<<4),
425 #0x200001 + ((new_words[1]&0xffff)<<4),
426 #0x200002 + ((new_words[2]&0xffff)<<4),
427 #0x200003 + ((new_words[3]&0xffff)<<4),
428 #0x200004 + ((new_words[4]&0xffff)<<4),
429 #0x200005 + ((new_words[5]&0xffff)<<4),
433 0xBB0BB6, # TBLWTL [W6++], [W7]
436 0xBB8B96, # TBLWTH [W6], [W7]
437 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
440 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
443 #0xBB1BB6, # TBLWTL [W6++], [W7++]
446 #0xBB0BB6, # TBLWTL [W6++], [W7]
449 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
452 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
455 #0xBB1BB6, # TBLWTL [W6++], [W7++]
459 0xA8E761, # BSET NVMCON, #WR
468 status = readNVMCON()
469 while status & 0x8000:
470 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
471 status = readNVMCON()
475 elif sys.argv[1] == "write_config":
476 if len(sys.argv) < 3:
477 print "Error: please specify the target config register."
479 elif len(sys.argv) == 3:
480 new_val = 0xFF # Assume new value
482 new_val = int(float.fromhex(sys.argv[3]))
483 if sys.argv[2] not in cfg_table.values():
484 print "Given register, %s, not recognized." % sys.argv[2]
486 reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
488 wr_cfg_li = [0x040200, # GOTO 0x0200
489 0x040200, # GOTO 0x0200
491 0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
492 0x24000A, # MOV #0x4000, W10
493 0x883B0A, # MOV W10, NVMCON
494 0x200F80, # MOV #0xF8, W0
495 0x880190, # MOV W0, TBLPAG
496 0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
497 0xBB0B80, # TBLWTL W0, [W7]
500 0xA8E761, # BSET NVMCON, #WR
509 status = readNVMCON()
510 while status & 0x8000:
511 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
512 status = readNVMCON()
517 elif sys.argv[1] == "read": # Read an address of program memory
520 addr = int(float.fromhex(sys.argv[2]))
522 readpm_cmd_li = [0x040200, # GOTO 0x0200
523 0x040200, # GOTO 0x0200
525 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
526 0x880190, # MOV W0, TBLPAG
527 0x200006+((addr & 0xffff)<<4), # MOV #addr<15:0>, W6
531 0xBA1B96, # TBLRDL [W6], [W7++]
534 0xBACB96, # TBLRDH.B [W6], [W7]
538 runlist( readpm_cmd_li )
539 loww = readreg(0) # Read W0, which has lower 16 bits
540 highb = readreg(1) # Read W1, which has high 8 bits
541 result = (highb<<16) | loww
543 print "0x%06X: 0x%06X" % ( addr, result )
547 elif sys.argv[1] == "dump": # Read section of program memory
548 if len(sys.argv) < 3:
549 print "Error: please specify file in which to dump program memory."
553 if len(sys.argv) == 6:
558 if len(sys.argv) > 4:
559 start_addr = int(float.fromhex(sys.argv[3]))
560 stop_addr = int(float.fromhex(sys.argv[4]))
562 # Assume all of program memory (including unimplemented sections,
563 # which will be read as zeroes.
565 stop_addr = 0xFFFFFE #
567 readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
568 0x880190, # MOV W0, TBLPAG
569 0x200006, # MOV #addr<15:0>, W6
573 0xBA1B96, # TBLRDL [W6], [W7++]
576 0xBACB96, # TBLRDH.B [W6], [W7]
584 print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
586 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
587 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
588 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
589 for addr in range( start_addr, stop_addr+2, 2 ):
590 specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
592 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
593 runlist( specify_addr_cmd + readpm_cmd_li[3:] )
594 loww = readreg(0) # Read W0, which has lower 16 bits
595 highb = readreg(1) # Read W1, which has high 8 bits
596 result = (highb<<16) | loww
597 dumphex.puts( (addr&0xffffff)*2,
598 chr(result&0xff)+chr((result>>8)&0xff)+chr((result>>16)&0xff)+chr(0) )
600 print "0x%06X 0x%06X" % (addr,result)
604 dumphex.tofile( sys.stdout, format="hex" )
606 dumphex.tofile( fname, format="hex" )
610 elif sys.argv[1] == "erase": # Bulk erase (all program memory)
612 if len(sys.argv) == 3:
613 addr = int(float.fromhex(sys.argv[2]))
617 if addr is None: # Bulk erase (all of program memory)
618 erase_cmd_li = [0x040200, # GOTO 0x0200
619 0x040200, # GOTO 0x0200
621 0x2404FA, # MOV $0x404F, W10
622 0x883B0A, # MOV W10, NVMCON
623 0xA8E761, # BSET NVMCON, #WR
629 erase_cmd_li = [0x040200, # GOTO 0x0200
630 0x040200, # GOTO 0x0200
632 0x24042A, # MOV $0x4042, W10
633 0x883B0A, # MOV W10, NVMCON
634 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
635 0x880190, # MOV W0, TBLPAG
636 0x200001+((addr&0xffff)<<4), # MOV 0x0, W1
638 0xBB0881, # TBLTWL W1, [W1]
641 0xA8E761, # BSET NVMCON, #WR
646 # Note that page alignment (effectively, bitmask of 0xffff80
647 # applied to given program memory address) seems to be
648 # enforced by hardware.
652 runlist( erase_cmd_li )
653 status = readNVMCON()
654 while status & 0x8000:
655 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
656 status = readNVMCON()
660 elif sys.argv[1] == "six": #0x82
663 if len(sys.argv) < 3:
664 instr = 0x000000 # Assume nop
666 instr = int(float.fromhex(sys.argv[2]))
667 print "Loading 0x%06X for execution..." % instr
668 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
670 (instr >> 16) & 0xff] )
671 print "Executing (by loading nop; pumping clock)..."
672 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
678 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
681 if len(sys.argv) < 3:
682 print "Warning: no file given; trying cmd.txt..."
688 instrfile = open( fname, "r" )
690 print "Failed to open file %s for reading." % fname
694 print "Opened file %s. Consecutively reading commmands..." % fname
695 for line in instrfile:
699 instr = int(float.fromhex(words[0]))
701 print "Warning: invalid instruction found at offset %d." % instrfile.tell()
703 print "Loading 0x%06X for execution..." % instr
704 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
706 (instr >> 16) & 0xff] )
708 # print "Warning: empty line found at offset %d." % instrfile.tell()
710 print "Error: exception caught while reading file %s." % fname
717 print "Loading nop, pumping clock (to finish execution)..."
718 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
724 elif sys.argv[1] == "regout": #0x83
727 print "Reading VISI register..."
728 data = client.writecmd( PICAPP, 0x83, 0 )
729 result = ord(data[0])
730 result |= ord(data[1]) << 8
731 print "VISI: 0x%04X" % result
736 print "Unrecognized verb: %s" % sys.argv[1]