2 # PIC client (currently only supports dsPIC33F/PIC24H.)
4 # Scott Livingston <slivingston at caltech.edu>
11 from GoodFET import GoodFET
12 from intelhex import IntelHex
15 # Some constants (not really immutable...)
20 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
21 0x00EF : "dsPIC33FJ128GP710" }
22 cfg_table = { 0xF80000 : "FBS",
34 "width" : 7 } # For pretty printing.
41 def runlist( cmd_li ):
42 """Load (and execute) a given list of instructions.
43 Assumes ICSP session already started."""
44 cmd_byte_li = build_instr_stream( cmd_li )
45 data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
46 if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
47 print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
50 elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
51 print "Error (in runlist): failed transaction; aborting."
55 def build_instr_stream( cmd_li ):
56 """Given a list of instruction words, returns a list of bytes of
57 the same, in little endian ordering."""
60 cmd_byte_li += [instr & 0xff,
65 def readreg( reg_num ):
66 """Read contents of a working register (i.e. W0, W1, ..., W15)."""
67 instr = 0x883C20+(reg_num&0xf)
68 client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
70 (instr >> 16) & 0xff] )
71 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
75 """Read VISI register; assumes ICSP session already started."""
76 data = client.writecmd( PICAPP, 0x83, 0 )
78 result |= ord(data[1]) << 8
82 """Read NVMCON register; assumes ICSP session already started."""
83 rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
84 0x883C20, # MOV W0, VISI
87 runlist( rd_nvmcon_li )
91 #print "Starting dsPIC33F/PIC24H ICSP session..."
92 data = client.writecmd( PICAPP, 0x84, 0 )
95 #print "Stopping dsPIC33F/PIC24H ICSP session..."
96 data = client.writecmd( PICAPP, 0x85, 0 )
99 """Read and print VISI register to stdout; assumes ICSP session already started."""
100 print "Reading VISI register..."
102 print "VISI: 0x%04X" % result
104 def instr2words( instr_list ):
105 """Convert a list of 4 24-bit instructions to a list of 6 words
108 Returns [-1] on failure."""
109 if len(instr_list) < 4: # Catch mistakes
110 print "Error in instr2words: operand has less than 4 elements."
112 word_list = [0,0,0,0,0,0]
114 word_list[k*3] = instr_list[k*2] & 0xffff
115 word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
116 word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
117 word_list[k*3+2] = instr_list[k*2+1] & 0xffff
120 def words2instr( word_list ):
121 """4 24-bit instructions from a packing in 6 words (16 bit width).
122 This is the inverse of function instr2words.
124 Returns [-1] on failure."""
125 if len(word_list) < 6: # Catch mistakes
126 print "Error in words2instr: operand has less than 6 elements."
128 instr_list = [0,0,0,0]
130 instr_list[k*2] = word_list[k*3]
131 instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
132 instr_list[k*2+1] = word_list[k*3+2]
133 instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
141 if len(sys.argv) == 1:
142 print "Usage: %s verb [objects]" % sys.argv[0]
143 print "Warning: only supports dsPIC33F/PIC24H (maybe)...\n"
144 print "%s devid" % sys.argv[0]
145 print "%s read 0x$addr" % sys.argv[0]
146 print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
147 print "%s config" % sys.argv[0]
148 print "%s program $foo.hex" % sys.argv[0]
149 print "%s write $0xADDRESS $0xVALUE" % sys.argv[0]
150 print "%s write_config $REG_NAME [$0x0000]" % sys.argv[0]
151 print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
152 print "%s six [instruction]" % sys.argv[0]
153 print "%s sixfile [foo.txt] (NOTE: plaintext, one instruction per line.)" % sys.argv[0]
154 print "%s regout" % sys.argv[0]
156 Note: use - for stdout.
160 # Initialize and open connection to GoodFET
162 client.verbose = False # Dump activity to terminal
163 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
166 # Handle each possible PIC verb in turn
168 if sys.argv[1] == "devid": #0x81
169 print "Requesting Application ID, DEVID and hardware revision..."
170 data = client.writecmd( PICAPP, 0x81, 0 )
177 devid = ord(data[1]) + (ord(data[2]) << 8)
181 hwrev = ord(data[3]) + (ord(data[4]) << 8)
184 print "Application ID: 0x%02X" % appid
185 if dev_table.has_key( devid ):
186 print "DEVID: 0x%04X (%s)" % ( devid, dev_table[devid] )
188 print "DEVID: 0x%04X (unknown)" % devid
189 print "revision: 0x%04X"% hwrev
190 #print "\n(Note that -1 indicates failure to read a value.)"
192 elif sys.argv[1] == "config": # Dump configuration registers
195 prep_cmd_li = [0x040200, # GOTO 0x0200
196 0x040200, # GOTO 0x0200
198 0x200F80, # MOV #0x00F8, W0
199 0x880190, # MOV W0, TBLPAG
201 0x207847, # MOV #VISI, W7
203 rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
206 print "Dumping configuration registers..."
207 for instr in prep_cmd_li:
208 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
210 (instr >> 16) & 0xff] )
211 for k in range(12): # twelve configuration registers total
212 for instr in rd_cmd_li:
213 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
215 (instr >> 16) & 0xff] )
218 if cfg_table.has_key( addr ):
219 print "0x%06X (%s): 0x%04X" % ( addr,
220 cfg_table[addr].ljust(cfg_table["width"]),
223 print "0x%06X: 0x%04X" % ( addr, result )
227 elif sys.argv[1] == "program":
228 if len(sys.argv) != 3:
229 print "Error: an Intel HEX file to load must be given."
233 proghex = IntelHex( sys.argv[2] )
235 print "Error while attempting to read from %s" % sys.argv[2]
238 wr_pm_li = [0x040200, # GOTO 0x0200
239 0x040200, # GOTO 0x0200
242 0x24003A, # MOV $0x4003, W10
243 0x883B0A, # MOV W10, NVMCON
245 0x200000,# + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
246 0x880190, # MOV W0, TBLPAG
247 0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
249 # Here we load the instruction into registers W0:W1
250 0x200000,# + ((new_words[0]&0xffff)<<4),
251 0x200001,# + ((new_words[1]&0xffff)<<4),
255 0xBB0BB6, # TBLWTL [W6++], [W7]
258 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
262 0xA8E761, # BSET NVMCON, #WR
270 for addr in proghex.addresses():
271 # Ignore non-aligned steps
275 # Configuration registers must be treated separately
276 if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
277 wr_cfg_li = [0x040200, # GOTO 0x0200
278 0x040200, # GOTO 0x0200
280 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
281 0x24000A, # MOV #0x4000, W10
282 0x883B0A, # MOV W10, NVMCON
283 0x200F80, # MOV #0xF8, W0
284 0x880190, # MOV W0, TBLPAG
285 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
286 0xBB0B80, # TBLWTL W0, [W7]
289 0xA8E761, # BSET NVMCON, #WR
295 status = readNVMCON()
296 while status & 0x8000:
297 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
298 status = readNVMCON()
301 # Build instruction from 3 separate bytes
302 instr = proghex[addr]
303 instr |= proghex[addr+1] << 8
304 instr |= proghex[addr+2] << 16
306 # Change HEX file address to actual program memory address
309 # Program this instruction word
310 addr_highb_cmd = 0x200000 + ((addr_pm>>12)&0xff0)
311 addr_loww_cmd = 0x200007 + ((addr_pm&0xffff)<<4)
312 instr_loww_cmd = 0x200000 + ((instr&0xffff)<<4)
313 instr_highb_cmd = 0x200001 + ((instr>>12)&0xff0)
314 runlist( wr_pm_li[:5] + [addr_highb_cmd] + [wr_pm_li[6]]
316 instr_loww_cmd, instr_highb_cmd]
318 status = readNVMCON()
319 while status & 0x8000:
320 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
321 status = readNVMCON()
327 #####################
329 # Generic code to write 4 instruction words to program memory.
330 # This is filled in as we step through given code values to write.
331 # Remainder (of total number of instruction words divided by 64)
332 # is then handled specially.
334 # Note that we do not erase flash memory before programming here!
335 # The user must do so herself.
336 wr_pm64_li = [0x040200, # GOTO 0x0200
337 0x040200, # GOTO 0x0200
340 0x24001A, # MOV $0x4001, W10
341 0x883B0A, # MOV W10, NVMCON
343 0x200000,# + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
344 0x880190, # MOV W0, TBLPAG
345 0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
347 # Here we load the 4 instructions into registers W0:W5
348 0x200000,# + ((new_words[0]&0xffff)<<4),
349 0x200001,# + ((new_words[1]&0xffff)<<4),
350 0x200002,# + ((new_words[2]&0xffff)<<4),
351 0x200003,# + ((new_words[3]&0xffff)<<4),
352 0x200004,# + ((new_words[4]&0xffff)<<4),
353 0x200005,# + ((new_words[5]&0xffff)<<4),
357 0xBB0BB6, # TBLWTL [W6++], [W7]
360 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
363 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
366 0xBB1BB6, # TBLWTL [W6++], [W7++]
369 0xBB0BB6, # TBLWTL [W6++], [W7]
372 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
375 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
378 0xBB1BB6, # TBLWTL [W6++], [W7++]
382 0xA8E761, # BSET NVMCON, #WR
387 ph_addrs = proghex.addresses()
388 for r in range(len(ph_addrs)): # Find last non-configuration register address
389 if ph_addrs[len(ph_addrs)-r-1]/2 < 0xF80000:
392 for addr in range(0,ph_addrs[len(ph_addrs)-r-1]/2+128,128):
393 addr_highb_cmd = 0x200000 + ((addr>>12)&0xff0)
394 addr_loww_cmd = 0x200007 + ((addr&0xffff)<<4)
397 instr_li.append( proghex[addr*2+k*4] )
398 instr_li[-1] |= proghex[addr*2+k*4+1] << 8
399 instr_li[-1] |= proghex[addr*2+k*4+2] << 16
400 packed_instr_li = instr2words( instr_li )
401 words_cmd = [0x200000+k+((packed_instr_li[k]&0xffff)<<4) for k in range(6)]
402 runlist( wr_pm64_li[:5] + [addr_highb_cmd] + [wr_pm64_li[6]]
403 + [addr_loww_cmd] + words_cmd
405 status = readNVMCON()
406 while status & 0x8000:
407 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
408 status = readNVMCON()
410 for addr in ph_addrs[len(ph_addrs)-r:]:
413 print "0x%06X <-- 0x%06X" % (addr/2, proghex[addr])
414 wr_cfg_li = [0x040200, # GOTO 0x0200
415 0x040200, # GOTO 0x0200
417 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
418 0x24000A, # MOV #0x4000, W10
419 0x883B0A, # MOV W10, NVMCON
420 0x200F80, # MOV #0xF8, W0
421 0x880190, # MOV W0, TBLPAG
422 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
423 0xBB0B80, # TBLWTL W0, [W7]
426 0xA8E761, # BSET NVMCON, #WR
432 status = readNVMCON()
433 while status & 0x8000:
434 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
435 status = readNVMCON()
439 #####################
442 elif sys.argv[1] == "write":
443 if len(sys.argv) != 4:
444 print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
447 addr = int(float.fromhex(sys.argv[2]))
448 new_instr = int(float.fromhex(sys.argv[3]))
451 # new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
453 #new_words = instr2words( new_instr_li )
455 wr_pm_li = [0x040200, # GOTO 0x0200
456 0x040200, # GOTO 0x0200
459 0x24003A, # MOV $0x4003, W10
460 0x883B0A, # MOV W10, NVMCON
461 0x200000 + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
462 0x880190, # MOV W0, TBLPAG
463 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
465 # Here we load the instruction into registers W0:W1
466 0x200000 + ((new_instr&0xffff)<<4),
467 0x200001 + ((new_instr>>12)&0xff0),
468 #0x200000 + ((new_words[0]&0xffff)<<4),
469 #0x200001 + ((new_words[1]&0xffff)<<4),
470 #0x200002 + ((new_words[2]&0xffff)<<4),
471 #0x200003 + ((new_words[3]&0xffff)<<4),
472 #0x200004 + ((new_words[4]&0xffff)<<4),
473 #0x200005 + ((new_words[5]&0xffff)<<4),
477 0xBB0BB6, # TBLWTL [W6++], [W7]
480 0xBB8B96, # TBLWTH [W6], [W7]
481 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
484 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
487 #0xBB1BB6, # TBLWTL [W6++], [W7++]
490 #0xBB0BB6, # TBLWTL [W6++], [W7]
493 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
496 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
499 #0xBB1BB6, # TBLWTL [W6++], [W7++]
503 0xA8E761, # BSET NVMCON, #WR
512 status = readNVMCON()
513 while status & 0x8000:
514 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
515 status = readNVMCON()
519 elif sys.argv[1] == "write_config":
520 if len(sys.argv) < 3:
521 print "Error: please specify the target config register."
523 elif len(sys.argv) == 3:
524 new_val = 0xFF # Assume new value
526 new_val = int(float.fromhex(sys.argv[3]))
527 if sys.argv[2] not in cfg_table.values():
528 print "Given register, %s, not recognized." % sys.argv[2]
530 reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
532 wr_cfg_li = [0x040200, # GOTO 0x0200
533 0x040200, # GOTO 0x0200
535 0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
536 0x24000A, # MOV #0x4000, W10
537 0x883B0A, # MOV W10, NVMCON
538 0x200F80, # MOV #0xF8, W0
539 0x880190, # MOV W0, TBLPAG
540 0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
541 0xBB0B80, # TBLWTL W0, [W7]
544 0xA8E761, # BSET NVMCON, #WR
553 status = readNVMCON()
554 while status & 0x8000:
555 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
556 status = readNVMCON()
561 elif sys.argv[1] == "read": # Read an address of program memory
564 addr = int(float.fromhex(sys.argv[2]))
566 readpm_cmd_li = [0x040200, # GOTO 0x0200
567 0x040200, # GOTO 0x0200
569 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
570 0x880190, # MOV W0, TBLPAG
571 0x200006+((addr & 0xffff)<<4), # MOV #addr<15:0>, W6
575 0xBA1B96, # TBLRDL [W6], [W7++]
578 0xBACB96, # TBLRDH.B [W6], [W7]
582 runlist( readpm_cmd_li )
583 loww = readreg(0) # Read W0, which has lower 16 bits
584 highb = readreg(1) # Read W1, which has high 8 bits
585 result = (highb<<16) | loww
587 print "0x%06X: 0x%06X" % ( addr, result )
591 elif sys.argv[1] == "dump": # Read section of program memory
592 if len(sys.argv) < 3:
593 print "Error: please specify file in which to dump program memory."
597 if len(sys.argv) == 6:
602 if len(sys.argv) > 4:
603 start_addr = int(float.fromhex(sys.argv[3]))
604 stop_addr = int(float.fromhex(sys.argv[4]))
606 # Assume all of program memory (including unimplemented sections,
607 # which will be read as zeroes.
609 stop_addr = 0xFFFFFE #
611 readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
612 0x880190, # MOV W0, TBLPAG
613 0x200006, # MOV #addr<15:0>, W6
617 0xBA1B96, # TBLRDL [W6], [W7++]
620 0xBACB96, # TBLRDH.B [W6], [W7]
628 print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
630 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
631 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
632 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
633 for addr in range( start_addr, stop_addr+2, 2 ):
634 specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
636 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
637 runlist( specify_addr_cmd + readpm_cmd_li[3:] )
638 loww = readreg(0) # Read W0, which has lower 16 bits
639 highb = readreg(1) # Read W1, which has high 8 bits
640 result = (highb<<16) | loww
641 dumphex.puts( (addr&0xffffff)*2,
642 chr(result&0xff)+chr((result>>8)&0xff)+chr((result>>16)&0xff)+chr(0) )
644 print "0x%06X 0x%06X" % (addr,result)
648 dumphex.tofile( sys.stdout, format="hex" )
650 dumphex.tofile( fname, format="hex" )
654 elif sys.argv[1] == "erase": # Bulk erase (all program memory)
656 if len(sys.argv) == 3:
657 addr = int(float.fromhex(sys.argv[2]))
661 if addr is None: # Bulk erase (all of program memory)
662 erase_cmd_li = [0x040200, # GOTO 0x0200
663 0x040200, # GOTO 0x0200
665 0x2404FA, # MOV $0x404F, W10
666 0x883B0A, # MOV W10, NVMCON
667 0xA8E761, # BSET NVMCON, #WR
673 erase_cmd_li = [0x040200, # GOTO 0x0200
674 0x040200, # GOTO 0x0200
676 0x24042A, # MOV $0x4042, W10
677 0x883B0A, # MOV W10, NVMCON
678 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
679 0x880190, # MOV W0, TBLPAG
680 0x200001+((addr&0xffff)<<4), # MOV 0x0, W1
682 0xBB0881, # TBLTWL W1, [W1]
685 0xA8E761, # BSET NVMCON, #WR
690 # Note that page alignment (effectively, bitmask of 0xffff80
691 # applied to given program memory address) seems to be
692 # enforced by hardware.
696 runlist( erase_cmd_li )
697 status = readNVMCON()
698 while status & 0x8000:
699 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
700 status = readNVMCON()
704 elif sys.argv[1] == "six": #0x82
707 if len(sys.argv) < 3:
708 instr = 0x000000 # Assume nop
710 instr = int(float.fromhex(sys.argv[2]))
711 print "Loading 0x%06X for execution..." % instr
712 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
714 (instr >> 16) & 0xff] )
715 print "Executing (by loading nop; pumping clock)..."
716 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
722 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
725 if len(sys.argv) < 3:
726 print "Warning: no file given; trying cmd.txt..."
732 instrfile = open( fname, "r" )
734 print "Failed to open file %s for reading." % fname
738 print "Opened file %s. Consecutively reading commmands..." % fname
739 for line in instrfile:
743 instr = int(float.fromhex(words[0]))
745 print "Warning: invalid instruction found at offset %d." % instrfile.tell()
747 print "Loading 0x%06X for execution..." % instr
748 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
750 (instr >> 16) & 0xff] )
752 # print "Warning: empty line found at offset %d." % instrfile.tell()
754 print "Error: exception caught while reading file %s." % fname
761 print "Loading nop, pumping clock (to finish execution)..."
762 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
768 elif sys.argv[1] == "regout": #0x83
771 print "Reading VISI register..."
772 data = client.writecmd( PICAPP, 0x83, 0 )
773 result = ord(data[0])
774 result |= ord(data[1]) << 8
775 print "VISI: 0x%04X" % result
780 print "Unrecognized verb: %s" % sys.argv[1]