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)
84 """Read VISI register; assumes ICSP session already started."""
85 data = client.writecmd( PICAPP, 0x83, 0 )
87 result |= ord(data[1]) << 8
91 """Read NVMCON register; assumes ICSP session already started."""
92 rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
93 0x883C20, # MOV W0, VISI
96 runlist( rd_nvmcon_li )
100 #print "Starting dsPIC33F/PIC24H ICSP session..."
101 data = client.writecmd( PICAPP, 0x84, 0 )
104 #print "Stopping dsPIC33F/PIC24H ICSP session..."
105 data = client.writecmd( PICAPP, 0x85, 0 )
108 """Read and print VISI register to stdout; assumes ICSP session already started."""
109 print "Reading VISI register..."
111 print "VISI: 0x%04X" % result
113 def instr2words( instr_list ):
114 """Convert a list of 4 24-bit instructions to a list of 6 words
117 Returns [-1] on failure."""
118 if len(instr_list) < 4: # Catch mistakes
119 print "Error in instr2words: operand has less than 4 elements."
121 word_list = [0,0,0,0,0,0]
123 word_list[k*3] = instr_list[k*2] & 0xffff
124 word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
125 word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
126 word_list[k*3+2] = instr_list[k*2+1] & 0xffff
129 def words2instr( word_list ):
130 """4 24-bit instructions from a packing in 6 words (16 bit width).
131 This is the inverse of function instr2words.
133 Returns [-1] on failure."""
134 if len(word_list) < 6: # Catch mistakes
135 print "Error in words2instr: operand has less than 6 elements."
137 instr_list = [0,0,0,0]
139 instr_list[k*2] = word_list[k*3]
140 instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
141 instr_list[k*2+1] = word_list[k*3+2]
142 instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
150 if len(sys.argv) == 1:
151 print "Usage: %s verb [objects]\n" % sys.argv[0]
152 print "%s devid" % sys.argv[0]
153 print "%s read 0x$addr" % sys.argv[0]
154 print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
155 print "%s config" % sys.argv[0]
156 print "%s program $foo.hex" % sys.argv[0]
157 print "%s verify $foo.hex" % sys.argv[0]
158 print "%s write 0x$address 0x$value" % sys.argv[0]
159 print "%s write_config 0x$reg_address (or $reg_name) [$0x0000]" % sys.argv[0]
160 print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
161 print "%s six [instruction]" % sys.argv[0]
162 print "%s sixfile [$foo.txt]" % sys.argv[0]
163 print "%s regout" % sys.argv[0]
165 Note: use - for stdout.
166 Warning: only supports dsPIC33F/PIC24H (maybe)...
170 # Initialize and open connection to GoodFET
172 client.verbose = False # Dump activity to terminal
173 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
176 # Handle each possible PIC verb in turn
178 if sys.argv[1] == "devid": #0x81
179 print "Requesting Application ID, DEVID and hardware revision..."
180 data = client.writecmd( PICAPP, 0x81, 0 )
187 devid = ord(data[1]) + (ord(data[2]) << 8)
191 hwrev = ord(data[3]) + (ord(data[4]) << 8)
194 print "Application ID: 0x%02X" % appid
195 if dev_table.has_key( devid ):
196 print "DEVID: 0x%04X (%s)" % ( devid, dev_table[devid] )
198 print "DEVID: 0x%04X (unknown)" % devid
199 print "revision: 0x%04X"% hwrev
200 #print "\n(Note that -1 indicates failure to read a value.)"
202 elif sys.argv[1] == "config": # Dump configuration registers
203 prep_cmd_li = [0x040200, # GOTO 0x0200
204 0x040200, # GOTO 0x0200
206 0x200F80, # MOV #0x00F8, W0
207 0x880190, # MOV W0, TBLPAG
209 0x207847, # MOV #VISI, W7
211 rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
215 print "Dumping configuration registers..."
216 for instr in prep_cmd_li:
217 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
219 (instr >> 16) & 0xff] )
220 for k in range(12): # twelve configuration registers total
221 for instr in rd_cmd_li:
222 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
224 (instr >> 16) & 0xff] )
227 if cfg_table.has_key( addr ):
228 print "0x%06X (%s): 0x%04X" % ( addr,
229 cfg_table[addr].ljust(cfg_table["width"]),
232 print "0x%06X: 0x%04X" % ( addr, result )
236 elif sys.argv[1] == "program":
237 if len(sys.argv) != 3:
238 print "Error: an Intel HEX file to load must be given."
242 proghex = IntelHex( sys.argv[2] )
244 print "Error while attempting to read from %s" % sys.argv[2]
247 wr_pm_li = [0x040200, # GOTO 0x0200
248 0x040200, # GOTO 0x0200
251 0x24003A, # MOV $0x4003, W10
252 0x883B0A, # MOV W10, NVMCON
254 0x200000,# + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
255 0x880190, # MOV W0, TBLPAG
256 0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
258 # Here we load the instruction into registers W0:W1
259 0x200000,# + ((new_words[0]&0xffff)<<4),
260 0x200001,# + ((new_words[1]&0xffff)<<4),
264 0xBB0BB6, # TBLWTL [W6++], [W7]
267 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
271 0xA8E761, # BSET NVMCON, #WR
279 for addr in proghex.addresses():
280 # Ignore non-aligned steps
284 # Configuration registers must be treated separately
285 if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
286 wr_cfg_li = [0x040200, # GOTO 0x0200
287 0x040200, # GOTO 0x0200
289 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
290 0x24000A, # MOV #0x4000, W10
291 0x883B0A, # MOV W10, NVMCON
292 0x200F80, # MOV #0xF8, W0
293 0x880190, # MOV W0, TBLPAG
294 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
295 0xBB0B80, # TBLWTL W0, [W7]
298 0xA8E761, # BSET NVMCON, #WR
304 status = readNVMCON()
305 while status & 0x8000:
306 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
307 status = readNVMCON()
310 # Build instruction from 3 separate bytes
311 instr = proghex[addr]
312 instr |= proghex[addr+1] << 8
313 instr |= proghex[addr+2] << 16
315 # Change HEX file address to actual program memory address
318 # Program this instruction word
319 addr_highb_cmd = 0x200000 + ((addr_pm>>12)&0xff0)
320 addr_loww_cmd = 0x200007 + ((addr_pm&0xffff)<<4)
321 instr_loww_cmd = 0x200000 + ((instr&0xffff)<<4)
322 instr_highb_cmd = 0x200001 + ((instr>>12)&0xff0)
323 runlist( wr_pm_li[:5] + [addr_highb_cmd] + [wr_pm_li[6]]
325 instr_loww_cmd, instr_highb_cmd]
327 status = readNVMCON()
328 while status & 0x8000:
329 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
330 status = readNVMCON()
335 elif sys.argv[1] == "verify":
336 if len(sys.argv) != 3:
337 print "Error: an Intel HEX file with which to compare must be given."
341 proghex = IntelHex( sys.argv[2] )
343 print "Error while attempting to read from %s" % sys.argv[2]
346 readpm_cmd_li = [0x040200, # GOTO 0x0200
347 0x040200, # GOTO 0x0200
349 0x200000, # MOV #addr_pm<23:16>, W0
350 0x880190, # MOV W0, TBLPAG
351 0x200006, # MOV #addr_pm<15:0>, W6
355 0xBA1B96, # TBLRDL [W6], [W7++]
358 0xBACB96, # TBLRDH.B [W6], [W7]
362 print "WARNING: verifying config registers not yet supported."
366 # Step through addresses in HEX file and compare to those in target PIC
367 for addr in proghex.addresses():
368 # Ignore non-aligned steps
372 # Configuration registers must be treated separately
373 if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
374 # Need to read datasheets to see which bits are masked.
375 # Then add dictionary to handle this.
376 # (Straightforward but tedious.)
379 # Build instruction from 3 separate bytes
380 instr = proghex[addr]
381 instr |= proghex[addr+1] << 8
382 instr |= proghex[addr+2] << 16
384 # Change HEX file address to actual program memory address
387 runlist( readpm_cmd_li[:3] + [readpm_cmd_li[3]+((addr_pm&0xff0000)>>12)]
388 + [readpm_cmd_li[4]] + [readpm_cmd_li[5]+((addr_pm&0xffff)<<4)]
389 + readpm_cmd_li[6:] )
390 loww = readreg(0) # Read W0, which has lower 16 bits
391 highb = readreg(1) # Read W1, which has high 8 bits
392 result = (highb<<16) | loww
394 # Compare; fail if mismatch
396 print "Fail at address 0x%06X: found 0x%06X, expected 0x%06X." % (addr_pm, result, instr )
402 print "PASSED" # Be verbose.
405 elif sys.argv[1] == "write":
406 if len(sys.argv) != 4:
407 print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
410 addr = int(float.fromhex(sys.argv[2]))
411 new_instr = int(float.fromhex(sys.argv[3]))
414 # new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
416 #new_words = instr2words( new_instr_li )
418 wr_pm_li = [0x040200, # GOTO 0x0200
419 0x040200, # GOTO 0x0200
422 0x24003A, # MOV $0x4003, W10
423 0x883B0A, # MOV W10, NVMCON
424 0x200000 + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
425 0x880190, # MOV W0, TBLPAG
426 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
428 # Here we load the instruction into registers W0:W1
429 0x200000 + ((new_instr&0xffff)<<4),
430 0x200001 + ((new_instr>>12)&0xff0),
431 #0x200000 + ((new_words[0]&0xffff)<<4),
432 #0x200001 + ((new_words[1]&0xffff)<<4),
433 #0x200002 + ((new_words[2]&0xffff)<<4),
434 #0x200003 + ((new_words[3]&0xffff)<<4),
435 #0x200004 + ((new_words[4]&0xffff)<<4),
436 #0x200005 + ((new_words[5]&0xffff)<<4),
440 0xBB0BB6, # TBLWTL [W6++], [W7]
443 0xBB8B96, # TBLWTH [W6], [W7]
444 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
447 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
450 #0xBB1BB6, # TBLWTL [W6++], [W7++]
453 #0xBB0BB6, # TBLWTL [W6++], [W7]
456 #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
459 #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
462 #0xBB1BB6, # TBLWTL [W6++], [W7++]
466 0xA8E761, # BSET NVMCON, #WR
475 status = readNVMCON()
476 while status & 0x8000:
477 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
478 status = readNVMCON()
482 elif sys.argv[1] == "write_config":
483 if len(sys.argv) < 3:
484 print "Error: please specify the target config register."
486 elif len(sys.argv) == 3:
487 new_val = 0xFF # Assume new value
489 new_val = int(float.fromhex(sys.argv[3]))
491 reg_addr = int(float.fromhex(sys.argv[2]))
493 if sys.argv[2] not in cfg_table.values():
494 print "Given register, %s, not recognized." % sys.argv[2]
496 reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
498 wr_cfg_li = [0x040200, # GOTO 0x0200
499 0x040200, # GOTO 0x0200
501 0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
502 0x24000A, # MOV #0x4000, W10
503 0x883B0A, # MOV W10, NVMCON
504 0x200F80, # MOV #0xF8, W0
505 0x880190, # MOV W0, TBLPAG
506 0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
507 0xBB0B80, # TBLWTL W0, [W7]
510 0xA8E761, # BSET NVMCON, #WR
519 status = readNVMCON()
520 while status & 0x8000:
521 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
522 status = readNVMCON()
527 elif sys.argv[1] == "read": # Read an address of program memory
530 addr = int(float.fromhex(sys.argv[2]))
532 readpm_cmd_li = [0x040200, # GOTO 0x0200
533 0x040200, # GOTO 0x0200
535 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
536 0x880190, # MOV W0, TBLPAG
537 0x200006+((addr & 0xffff)<<4), # MOV #addr<15:0>, W6
541 0xBA1B96, # TBLRDL [W6], [W7++]
544 0xBACB96, # TBLRDH.B [W6], [W7]
548 runlist( readpm_cmd_li )
549 loww = readreg(0) # Read W0, which has lower 16 bits
550 highb = readreg(1) # Read W1, which has high 8 bits
551 result = (highb<<16) | loww
553 print "0x%06X: 0x%06X" % ( addr, result )
557 elif sys.argv[1] == "dump": # Read section of program memory
558 if len(sys.argv) < 3:
559 print "Error: please specify file in which to dump program memory."
563 if len(sys.argv) == 6:
568 if len(sys.argv) > 4:
569 start_addr = int(float.fromhex(sys.argv[3]))
570 stop_addr = int(float.fromhex(sys.argv[4]))
572 # Assume all of program memory (including unimplemented sections,
573 # which will be read as zeroes.
577 readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
578 0x880190, # MOV W0, TBLPAG
579 0x200006, # MOV #addr<15:0>, W6
582 0xBA1B96, # TBLRDL [W6], [W7++]
585 0xBADBB6, # TBLRDH.B [W6++], [W7++]
588 0xBADBD6, # TBLRDH.B [++W6], [W7++]
591 0xBA1BB6, # TBLRDL [W6++], [W7++]
594 0xBA1B96, # TBLRDL [W6], [W7++]
597 0xBADBB6, # TBLRDH.B [W6++], [W7++]
600 0xBADBD6, # TBLRDH.B [++W6], [W7++]
603 0xBA0BB6, # TBLRDL [W6++], [W7]
611 print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
613 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
614 client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
615 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
617 last_highb = -1 # Only set TBLPAG when needed.
618 packed_instr_list = [0,0,0,0,0,0] # For packing 4 (24-bit) instructions in 6 (16-bit) words
619 for addr in range( start_addr&0xfffff8, stop_addr+8, 8 ):
620 if (addr>>16)&0xff != last_highb:
621 last_highb = (addr>>16)&0xff;
622 specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
624 readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
626 specify_addr_cmd = [readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
627 runlist( specify_addr_cmd + readpm_cmd_li[3:] )
629 for reg_num in range(6): # Read W0:5, to get packed instructions
630 packed_instr_list[reg_num] = readreg( reg_num )
631 instr_list = words2instr( packed_instr_list )
633 for offset in range(4): # Print result
634 if addr+offset*2 < start_addr:
636 if addr+offset*2 > stop_addr:
638 dumphex.puts( ((addr+offset*2)&0xffffff)*2,
639 chr(instr_list[offset]&0xff)
640 +chr((instr_list[offset]>>8)&0xff)
641 +chr((instr_list[offset]>>16)&0xff)
644 print "0x%06X 0x%06X" % (addr+offset*2,instr_list[offset])
648 dumphex.tofile( sys.stdout, format="hex" )
650 dumphex.tofile( fname, format="hex" )
654 elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
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 print "Erasing page of 0x%06X" % addr
674 erase_cmd_li = [0x040200, # GOTO 0x0200
675 0x040200, # GOTO 0x0200
677 0x24042A, # MOV $0x4042, W10
678 0x883B0A, # MOV W10, NVMCON
679 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
680 0x880190, # MOV W0, TBLPAG
681 0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
683 0xBB0881, # TBLTWL W1, [W1]
686 0xA8E761, # BSET NVMCON, #WR
691 # Note that page alignment (effectively, bitmask of 0xffff80
692 # applied to given program memory address) seems to be
693 # enforced by hardware.
697 runlist( erase_cmd_li )
698 status = readNVMCON()
699 while status & 0x8000:
700 client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
701 status = readNVMCON()
705 elif sys.argv[1] == "six": #0x82
708 if len(sys.argv) < 3:
709 instr = 0x000000 # Assume nop
711 instr = int(float.fromhex(sys.argv[2]))
712 print "Loading 0x%06X for execution..." % instr
713 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
715 (instr >> 16) & 0xff] )
716 print "Executing (by loading nop; pumping clock)..."
717 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
723 elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
726 if len(sys.argv) < 3:
727 print "Warning: no file given; trying cmd.txt..."
733 instrfile = open( fname, "r" )
735 print "Failed to open file %s for reading." % fname
739 print "Opened file %s. Consecutively reading commmands..." % fname
740 for line in instrfile:
744 instr = int(float.fromhex(words[0]))
746 print "Warning: invalid instruction found at offset %d." % instrfile.tell()
748 print "Loading 0x%06X for execution..." % instr
749 data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
751 (instr >> 16) & 0xff] )
753 # print "Warning: empty line found at offset %d." % instrfile.tell()
755 print "Error: exception caught while reading file %s." % fname
762 print "Loading nop, pumping clock (to finish execution)..."
763 data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
769 elif sys.argv[1] == "regout": #0x83
772 print "Reading VISI register..."
773 data = client.writecmd( PICAPP, 0x83, 0 )
774 result = ord(data[0])
775 result |= ord(data[1]) << 8
776 print "VISI: 0x%04X" % result
781 print "Unrecognized verb: %s" % sys.argv[1]