--- /dev/null
+#!/usr/bin/env python
+# PIC client (currently only supports dsPIC33F/PIC24H.)
+#
+# Scott Livingston <slivingston at caltech.edu>
+#
+# March, April 2010.
+
+
+import sys
+
+from GoodFET import GoodFET
+from intelhex import IntelHex
+
+#############
+# Some constants (not really immutable...)
+#############
+PICAPP = 0x34
+MONITORAPP = 0x00
+NOK = 0x7E
+dev_table = { 0x00EE : "dsPIC33FJ128GP708",
+ 0x00EF : "dsPIC33FJ128GP710" }
+cfg_table = { 0xF80000 : "FBS",
+ 0xF80002 : "FSS",
+ 0xF80004 : "FGS",
+ 0xF80006 : "FOSCSEL",
+ 0xF80008 : "FOSC",
+ 0xF8000A : "FWDT",
+ 0xF8000C : "FPOR",
+ 0xF8000E : "FICD",
+ 0xF80010 : "FUID0",
+ 0xF80012 : "FUID1",
+ 0xF80014 : "FUID2",
+ 0xF80016 : "FUID3",
+ "width" : 7 } # For pretty printing.
+
+
+#############
+# Functions:
+#############
+
+def runlist( cmd_li ):
+ """Load (and execute) a given list of instructions.
+Assumes ICSP session already started."""
+ cmd_byte_li = build_instr_stream( cmd_li )
+ data = client.writecmd( PICAPP, 0x86, len(cmd_byte_li), cmd_byte_li )
+ if client.app == PICAPP and client.verb == 0x86 and client.count != len(cmd_byte_li):
+ print "Error: incomplete execution of sixlist.\nOnly %d instructions run." % (client.count/3)
+ stopICSP()
+ exit(-1)
+ elif client.app == 0xff and client.verb == 0xff: # GoodFET debugstr
+ print "Error (in runlist): failed transaction; aborting."
+ stopICSP()
+ exit(-1)
+
+def build_instr_stream( cmd_li ):
+ """Given a list of instruction words, returns a list of bytes of
+the same, in little endian ordering."""
+ cmd_byte_li = []
+ for instr in cmd_li:
+ cmd_byte_li += [instr & 0xff,
+ (instr >> 8) & 0xff,
+ (instr >> 16) & 0xff]
+ return cmd_byte_li
+
+def readreg( reg_num ):
+ """Read contents of a working register (i.e. W0, W1, ..., W15)."""
+ instr = 0x883C20+(reg_num&0xf)
+ client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
+ (instr >> 8) & 0xff,
+ (instr >> 16) & 0xff] )
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
+ return readVISI()
+
+def readVISI():
+ """Read VISI register; assumes ICSP session already started."""
+ data = client.writecmd( PICAPP, 0x83, 0 )
+ result = ord(data[0])
+ result |= ord(data[1]) << 8
+ return result
+
+def readNVMCON():
+ """Read NVMCON register; assumes ICSP session already started."""
+ rd_nvmcon_li = [0x803B00, # MOV NVMCON, W0
+ 0x883C20, # MOV W0, VISI
+ 0x000000, # NOP
+ 0x000000] # NOP
+ runlist( rd_nvmcon_li )
+ return readVISI()
+
+def startICSP():
+ #print "Starting dsPIC33F/PIC24H ICSP session..."
+ data = client.writecmd( PICAPP, 0x84, 0 )
+
+def stopICSP():
+ #print "Stopping dsPIC33F/PIC24H ICSP session..."
+ data = client.writecmd( PICAPP, 0x85, 0 )
+
+def dumpVISI():
+ """Read and print VISI register to stdout; assumes ICSP session already started."""
+ print "Reading VISI register..."
+ result = readVISI()
+ print "VISI: 0x%04X" % result
+
+def instr2words( instr_list ):
+ """Convert a list of 4 24-bit instructions to a list of 6 words
+(16-bit width).
+
+Returns [-1] on failure."""
+ if len(instr_list) < 4: # Catch mistakes
+ print "Error in instr2words: operand has less than 4 elements."
+ return [-1]
+ word_list = [0,0,0,0,0,0]
+ for k in [0,1]:
+ word_list[k*3] = instr_list[k*2] & 0xffff
+ word_list[k*3+1] = (instr_list[k*2]>>16)&0xff
+ word_list[k*3+1] |= (instr_list[k*2+1]>>8)&0xff00
+ word_list[k*3+2] = instr_list[k*2+1] & 0xffff
+ return word_list
+
+def words2instr( word_list ):
+ """4 24-bit instructions from a packing in 6 words (16 bit width).
+This is the inverse of function instr2words.
+
+Returns [-1] on failure."""
+ if len(word_list) < 6: # Catch mistakes
+ print "Error in words2instr: operand has less than 6 elements."
+ return [-1]
+ instr_list = [0,0,0,0]
+ for k in [0,1]:
+ instr_list[k*2] = word_list[k*3]
+ instr_list[k*2] |= (word_list[k*3+1]&0xff)<<16
+ instr_list[k*2+1] = word_list[k*3+2]
+ instr_list[k*2+1] |= (word_list[k*3+1]&0xff00)<<8
+ return instr_list
+
+
+#############
+# Main entry:
+#############
+
+if len(sys.argv) == 1:
+ print "Usage: %s verb [objects]" % sys.argv[0]
+ print "Warning: only supports dsPIC33F/PIC24H (maybe)...\n"
+ print "%s devid" % sys.argv[0]
+ print "%s read 0x$addr" % sys.argv[0]
+ print "%s dump $foo.hex [0x$start 0x$stop] [pretty]" % sys.argv[0]
+ print "%s config" % sys.argv[0]
+ print "%s program $foo.hex" % sys.argv[0]
+ print "%s write $0xADDRESS $0xVALUE" % sys.argv[0]
+ print "%s write_config $REG_NAME [$0x0000]" % sys.argv[0]
+ print "%s erase [0x$page]" % sys.argv[0] # bulk or page erase
+ print "%s six [instruction]" % sys.argv[0]
+ print "%s sixfile [foo.txt] (NOTE: plaintext, one instruction per line.)" % sys.argv[0]
+ print "%s regout" % sys.argv[0]
+ print """
+Note: use - for stdout.
+"""
+ sys.exit()
+
+# Initialize and open connection to GoodFET
+client = GoodFET()
+client.verbose = False # Dump activity to terminal
+client.serInit( timeout=10 ) # UART comm timeout (in seconds)
+
+
+# Handle each possible PIC verb in turn
+
+if sys.argv[1] == "devid": #0x81
+ print "Requesting Application ID, DEVID and hardware revision..."
+ data = client.writecmd( PICAPP, 0x81, 0 )
+
+ if len(data) > 0:
+ appid = ord(data[0])
+ else:
+ appid = -1
+ if len(data) > 2:
+ devid = ord(data[1]) + (ord(data[2]) << 8)
+ else:
+ devid = -1
+ if len(data) > 4:
+ hwrev = ord(data[3]) + (ord(data[4]) << 8)
+ else:
+ hwrev = -1
+ print "Application ID: 0x%02X" % appid
+ if dev_table.has_key( devid ):
+ print "DEVID: 0x%04X (%s)" % ( devid, dev_table[devid] )
+ else:
+ print "DEVID: 0x%04X (unknown)" % devid
+ print "revision: 0x%04X"% hwrev
+ #print "\n(Note that -1 indicates failure to read a value.)"
+
+elif sys.argv[1] == "config": # Dump configuration registers
+ startICSP()
+
+ prep_cmd_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+ 0x200F80, # MOV #0x00F8, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0xEB0300, # CLR W6
+ 0x207847, # MOV #VISI, W7
+ 0x000000] # NOP
+ rd_cmd_li = [0xBA0BB6, # TBLRDL [W6++], [W7]
+ 0x000000, # NOP
+ 0x000000] # NOP
+ print "Dumping configuration registers..."
+ for instr in prep_cmd_li:
+ data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
+ (instr >> 8) & 0xff,
+ (instr >> 16) & 0xff] )
+ for k in range(12): # twelve configuration registers total
+ for instr in rd_cmd_li:
+ data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
+ (instr >> 8) & 0xff,
+ (instr >> 16) & 0xff] )
+ result = readVISI()
+ addr = 0xF80000+k*2
+ if cfg_table.has_key( addr ):
+ print "0x%06X (%s): 0x%04X" % ( addr,
+ cfg_table[addr].ljust(cfg_table["width"]),
+ result )
+ else:
+ print "0x%06X: 0x%04X" % ( addr, result )
+
+ stopICSP()
+
+elif sys.argv[1] == "program":
+ if len(sys.argv) != 3:
+ print "Error: an Intel HEX file to load must be given."
+ exit(1)
+
+ try:
+ proghex = IntelHex( sys.argv[2] )
+ except IOError:
+ print "Error while attempting to read from %s" % sys.argv[2]
+ exit(-1)
+
+ wr_pm_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+
+ 0x24003A, # MOV $0x4003, W10
+ 0x883B0A, # MOV W10, NVMCON
+
+ 0x200000,# + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
+
+ # Here we load the instruction into registers W0:W1
+ 0x200000,# + ((new_words[0]&0xffff)<<4),
+ 0x200001,# + ((new_words[1]&0xffff)<<4),
+
+ 0xEB0300, # CLR W6
+ 0x000000, # NOP
+ 0xBB0BB6, # TBLWTL [W6++], [W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+
+ startICSP()
+
+ for addr in proghex.addresses():
+ # Ignore non-aligned steps
+ if addr % 4 != 0:
+ continue
+
+ # Configuration registers must be treated separately
+ if (addr>>1) >= 0xf80000 and (addr>>1) <= 0xf80017:
+ wr_cfg_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+ 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
+ 0x24000A, # MOV #0x4000, W10
+ 0x883B0A, # MOV W10, NVMCON
+ 0x200F80, # MOV #0xF8, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
+ 0xBB0B80, # TBLWTL W0, [W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+ runlist( wr_cfg_li )
+ status = readNVMCON()
+ while status & 0x8000:
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
+ status = readNVMCON()
+ continue
+
+ # Build instruction from 3 separate bytes
+ instr = proghex[addr]
+ instr |= proghex[addr+1] << 8
+ instr |= proghex[addr+2] << 16
+
+ # Change HEX file address to actual program memory address
+ addr_pm = addr >> 1
+
+ # Program this instruction word
+ addr_highb_cmd = 0x200000 + ((addr_pm>>12)&0xff0)
+ addr_loww_cmd = 0x200007 + ((addr_pm&0xffff)<<4)
+ instr_loww_cmd = 0x200000 + ((instr&0xffff)<<4)
+ instr_highb_cmd = 0x200001 + ((instr>>12)&0xff0)
+ runlist( wr_pm_li[:5] + [addr_highb_cmd] + [wr_pm_li[6]]
+ + [addr_loww_cmd,
+ instr_loww_cmd, instr_highb_cmd]
+ + wr_pm_li[10:] )
+ status = readNVMCON()
+ while status & 0x8000:
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
+ status = readNVMCON()
+
+ stopICSP()
+ exit(0)
+
+
+ #####################
+ # TESTING
+ # Generic code to write 4 instruction words to program memory.
+ # This is filled in as we step through given code values to write.
+ # Remainder (of total number of instruction words divided by 64)
+ # is then handled specially.
+ #
+ # Note that we do not erase flash memory before programming here!
+ # The user must do so herself.
+ wr_pm64_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+
+ 0x24001A, # MOV $0x4001, W10
+ 0x883B0A, # MOV W10, NVMCON
+
+ 0x200000,# + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200007,# + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
+
+ # Here we load the 4 instructions into registers W0:W5
+ 0x200000,# + ((new_words[0]&0xffff)<<4),
+ 0x200001,# + ((new_words[1]&0xffff)<<4),
+ 0x200002,# + ((new_words[2]&0xffff)<<4),
+ 0x200003,# + ((new_words[3]&0xffff)<<4),
+ 0x200004,# + ((new_words[4]&0xffff)<<4),
+ 0x200005,# + ((new_words[5]&0xffff)<<4),
+
+ 0xEB0300, # CLR W6
+ 0x000000, # NOP
+ 0xBB0BB6, # TBLWTL [W6++], [W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBB1BB6, # TBLWTL [W6++], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBB0BB6, # TBLWTL [W6++], [W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBBDBB6, # TBLWTH.B [W6++], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBBEBB6, # TBLWTH.B [W6++], [++W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBB1BB6, # TBLWTL [W6++], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+ ph_addrs = proghex.addresses()
+ for r in range(len(ph_addrs)): # Find last non-configuration register address
+ if ph_addrs[len(ph_addrs)-r-1]/2 < 0xF80000:
+ break
+
+ for addr in range(0,ph_addrs[len(ph_addrs)-r-1]/2+128,128):
+ addr_highb_cmd = 0x200000 + ((addr>>12)&0xff0)
+ addr_loww_cmd = 0x200007 + ((addr&0xffff)<<4)
+ instr_li = []
+ for k in range(4):
+ instr_li.append( proghex[addr*2+k*4] )
+ instr_li[-1] |= proghex[addr*2+k*4+1] << 8
+ instr_li[-1] |= proghex[addr*2+k*4+2] << 16
+ packed_instr_li = instr2words( instr_li )
+ words_cmd = [0x200000+k+((packed_instr_li[k]&0xffff)<<4) for k in range(6)]
+ runlist( wr_pm64_li[:5] + [addr_highb_cmd] + [wr_pm64_li[6]]
+ + [addr_loww_cmd] + words_cmd
+ + wr_pm64_li[14:] )
+ status = readNVMCON()
+ while status & 0x8000:
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
+ status = readNVMCON()
+ if r > 0:
+ for addr in ph_addrs[len(ph_addrs)-r:]:
+ if addr % 4 != 0:
+ continue
+ print "0x%06X <-- 0x%06X" % (addr/2, proghex[addr])
+ wr_cfg_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+ 0x200007 + ((addr<<3)&0xffff0), # MOV #addr<15:0>, W7
+ 0x24000A, # MOV #0x4000, W10
+ 0x883B0A, # MOV W10, NVMCON
+ 0x200F80, # MOV #0xF8, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
+ 0xBB0B80, # TBLWTL W0, [W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+ runlist( wr_cfg_li )
+ status = readNVMCON()
+ while status & 0x8000:
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
+ status = readNVMCON()
+
+ stopICSP()
+ exit(0)
+#####################
+
+
+elif sys.argv[1] == "write":
+ if len(sys.argv) != 4:
+ print "Error: an address (in program memory) and\n value (or instruction) to write must be given."
+ exit(1)
+
+ addr = int(float.fromhex(sys.argv[2]))
+ new_instr = int(float.fromhex(sys.argv[3]))
+ #new_instr_li = []
+ #for k in range(4):
+ # new_instr_li.append( int(float.fromhex(sys.argv[k+3])) )
+
+ #new_words = instr2words( new_instr_li )
+
+ wr_pm_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+
+ 0x24003A, # MOV $0x4003, W10
+ 0x883B0A, # MOV W10, NVMCON
+ 0x200000 + ((addr>>12)&0xff0), # MOV #addr<23:16>, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
+
+ # Here we load the instruction into registers W0:W1
+ 0x200000 + ((new_instr&0xffff)<<4),
+ 0x200001 + ((new_instr>>12)&0xff0),
+ #0x200000 + ((new_words[0]&0xffff)<<4),
+ #0x200001 + ((new_words[1]&0xffff)<<4),
+ #0x200002 + ((new_words[2]&0xffff)<<4),
+ #0x200003 + ((new_words[3]&0xffff)<<4),
+ #0x200004 + ((new_words[4]&0xffff)<<4),
+ #0x200005 + ((new_words[5]&0xffff)<<4),
+
+ 0xEB0300, # CLR W6
+ 0x000000, # NOP
+ 0xBB0BB6, # TBLWTL [W6++], [W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBB8B96, # TBLWTH [W6], [W7]
+ #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
+ #0x000000, # NOP
+ #0x000000, # NOP
+ #0xBB1BB6, # TBLWTL [W6++], [W7++]
+ #0x000000, # NOP
+ #0x000000, # NOP
+ #0xBB0BB6, # TBLWTL [W6++], [W7]
+ #0x000000, # NOP
+ #0x000000, # NOP
+ #0xBBDBB6, # TBLWTH.B [W6++], [W7++]
+ #0x000000, # NOP
+ #0x000000, # NOP
+ #0xBBEBB6, # TBLWTH.B [W6++], [++W7]
+ #0x000000, # NOP
+ #0x000000, # NOP
+ #0xBB1BB6, # TBLWTL [W6++], [W7++]
+ #0x000000, # NOP
+ #0x000000, # NOP
+
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+
+ startICSP()
+
+ runlist( wr_pm_li )
+ status = readNVMCON()
+ while status & 0x8000:
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
+ status = readNVMCON()
+
+ stopICSP()
+
+elif sys.argv[1] == "write_config":
+ if len(sys.argv) < 3:
+ print "Error: please specify the target config register."
+ exit(1)
+ elif len(sys.argv) == 3:
+ new_val = 0xFF # Assume new value
+ else:
+ new_val = int(float.fromhex(sys.argv[3]))
+ if sys.argv[2] not in cfg_table.values():
+ print "Given register, %s, not recognized." % sys.argv[2]
+ exit(1)
+ reg_addr = cfg_table.keys()[cfg_table.values().index(sys.argv[2])]
+
+ wr_cfg_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+ 0x200007 + ((reg_addr&0xffff)<<4), # MOV #addr<15:0>, W7
+ 0x24000A, # MOV #0x4000, W10
+ 0x883B0A, # MOV W10, NVMCON
+ 0x200F80, # MOV #0xF8, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
+ 0xBB0B80, # TBLWTL W0, [W7]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+
+ startICSP()
+
+ runlist( wr_cfg_li )
+ status = readNVMCON()
+ while status & 0x8000:
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
+ status = readNVMCON()
+
+ stopICSP()
+
+
+elif sys.argv[1] == "read": # Read an address of program memory
+ startICSP()
+
+ addr = int(float.fromhex(sys.argv[2]))
+
+ readpm_cmd_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+ 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200006+((addr & 0xffff)<<4), # MOV #addr<15:0>, W6
+ 0xEB0380, # CLR W7
+ 0xEB0080, # CLR W1
+ 0x000000, # NOP
+ 0xBA1B96, # TBLRDL [W6], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBACB96, # TBLRDH.B [W6], [W7]
+ 0x000000, # NOP
+ 0x000000] # NOP
+
+ runlist( readpm_cmd_li )
+ loww = readreg(0) # Read W0, which has lower 16 bits
+ highb = readreg(1) # Read W1, which has high 8 bits
+ result = (highb<<16) | loww
+
+ print "0x%06X: 0x%06X" % ( addr, result )
+
+ stopICSP()
+
+elif sys.argv[1] == "dump": # Read section of program memory
+ if len(sys.argv) < 3:
+ print "Error: please specify file in which to dump program memory."
+ exit(1)
+ fname = sys.argv[2]
+
+ if len(sys.argv) == 6:
+ print_term = True
+ else:
+ print_term = False
+
+ if len(sys.argv) > 4:
+ start_addr = int(float.fromhex(sys.argv[3]))
+ stop_addr = int(float.fromhex(sys.argv[4]))
+ else:
+ # Assume all of program memory (including unimplemented sections,
+ # which will be read as zeroes.
+ start_addr = 0x0
+ stop_addr = 0xFFFFFE #
+
+ readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200006, # MOV #addr<15:0>, W6
+ 0xEB0380, # CLR W7
+ 0xEB0080, # CLR W1
+ 0x000000, # NOP
+ 0xBA1B96, # TBLRDL [W6], [W7++]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xBACB96, # TBLRDH.B [W6], [W7]
+ 0x000000, # NOP
+ 0x000000] # NOP
+
+ dumphex = IntelHex()
+
+ startICSP()
+
+ print "Reading program memory 0x%06X:0x%06X ..." % ( start_addr, stop_addr )
+ # Prep device
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x02,0x04] ) #GOTO 0x200 (reset)
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) #NOP (pump clock)
+ for addr in range( start_addr, stop_addr+2, 2 ):
+ specify_addr_cmd = [readpm_cmd_li[0] + ((addr & 0xff0000)>>12),
+ readpm_cmd_li[1],
+ readpm_cmd_li[2] + ((addr & 0xffff)<<4)]
+ runlist( specify_addr_cmd + readpm_cmd_li[3:] )
+ loww = readreg(0) # Read W0, which has lower 16 bits
+ highb = readreg(1) # Read W1, which has high 8 bits
+ result = (highb<<16) | loww
+ dumphex.puts( (addr&0xffffff)*2,
+ chr(result&0xff)+chr((result>>8)&0xff)+chr((result>>16)&0xff)+chr(0) )
+ if print_term:
+ print "0x%06X 0x%06X" % (addr,result)
+
+ if not print_term:
+ if fname == "-":
+ dumphex.tofile( sys.stdout, format="hex" )
+ else:
+ dumphex.tofile( fname, format="hex" )
+
+ stopICSP()
+
+elif sys.argv[1] == "erase": # Bulk erase (all program memory)
+
+ if len(sys.argv) == 3:
+ addr = int(float.fromhex(sys.argv[2]))
+ else:
+ addr = None
+
+ if addr is None: # Bulk erase (all of program memory)
+ erase_cmd_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+ 0x2404FA, # MOV $0x404F, W10
+ 0x883B0A, # MOV W10, NVMCON
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+ else: # Page erase
+ erase_cmd_li = [0x040200, # GOTO 0x0200
+ 0x040200, # GOTO 0x0200
+ 0x000000, # NOP
+ 0x24042A, # MOV $0x4042, W10
+ 0x883B0A, # MOV W10, NVMCON
+ 0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
+ 0x880190, # MOV W0, TBLPAG
+ 0x200001+((addr&0xffff)<<4), # MOV 0x0, W1
+ 0x000000, # NOP
+ 0xBB0881, # TBLTWL W1, [W1]
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0xA8E761, # BSET NVMCON, #WR
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000, # NOP
+ 0x000000] # NOP
+ # Note that page alignment (effectively, bitmask of 0xffff80
+ # applied to given program memory address) seems to be
+ # enforced by hardware.
+
+ startICSP()
+
+ runlist( erase_cmd_li )
+ status = readNVMCON()
+ while status & 0x8000:
+ client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] ) # NOP (pump clock)
+ status = readNVMCON()
+
+ stopICSP()
+
+elif sys.argv[1] == "six": #0x82
+ startICSP()
+
+ if len(sys.argv) < 3:
+ instr = 0x000000 # Assume nop
+ else:
+ instr = int(float.fromhex(sys.argv[2]))
+ print "Loading 0x%06X for execution..." % instr
+ data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
+ (instr >> 8) & 0xff,
+ (instr >> 16) & 0xff] )
+ print "Executing (by loading nop; pumping clock)..."
+ data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
+
+ dumpVISI()
+
+ stopICSP()
+
+elif sys.argv[1] == "sixfile": #0x82 (repeated for each instruction in file)
+ startICSP()
+
+ if len(sys.argv) < 3:
+ print "Warning: no file given; trying cmd.txt..."
+ fname = "cmd.txt"
+ else:
+ fname = sys.argv[2]
+
+ try:
+ instrfile = open( fname, "r" )
+ except:
+ print "Failed to open file %s for reading." % fname
+ exit(-1)
+
+ try:
+ print "Opened file %s. Consecutively reading commmands..." % fname
+ for line in instrfile:
+ words = line.split()
+ if len(words) > 0:
+ try:
+ instr = int(float.fromhex(words[0]))
+ except ValueError:
+ print "Warning: invalid instruction found at offset %d." % instrfile.tell()
+ continue
+ print "Loading 0x%06X for execution..." % instr
+ data = client.writecmd( PICAPP, 0x82, 3, [instr & 0xff,
+ (instr >> 8) & 0xff,
+ (instr >> 16) & 0xff] )
+ #else:
+ # print "Warning: empty line found at offset %d." % instrfile.tell()
+ except:
+ print "Error: exception caught while reading file %s." % fname
+ instrfile.close()
+ stopICSP()
+ exit(-1)
+
+ instrfile.close()
+
+ print "Loading nop, pumping clock (to finish execution)..."
+ data = client.writecmd( PICAPP, 0x82, 3, [0x00,0x00,0x00] )
+
+ dumpVISI()
+
+ stopICSP()
+
+elif sys.argv[1] == "regout": #0x83
+ startICSP()
+
+ print "Reading VISI register..."
+ data = client.writecmd( PICAPP, 0x83, 0 )
+ result = ord(data[0])
+ result |= ord(data[1]) << 8
+ print "VISI: 0x%04X" % result
+
+ stopICSP()
+
+else:
+ print "Unrecognized verb: %s" % sys.argv[1]
+ sys.exit(-1)
+
--- /dev/null
+/*! \file dspic33f.c
+
+ \author Scott Livingston
+
+ \brief dsPIC33F programmer application for the GoodFET. Structure
+ and style is somewhat modeled after avr.c
+
+ \date March, April 2010
+*/
+
+
+#include "apps.h"
+#include "platform.h"
+#include "command.h"
+
+#include "dspic33f.h"
+
+
+void pic33f_setup()
+{
+ // Initialize pins; do NOT begin transaction.
+ P5DIR |= PGC|MCLR;
+ P5REN &= ~(PGC|PGD|MCLR);
+ DIR_PGD_WR; // Initially PGD in write mode
+
+ SET_MCLR;
+ CLR_PGC;
+ CLR_PGD;
+
+ prep_timer(); // What better time than now?
+}
+
+
+//! Handle a PIC command; currently assumes dsPIC33F/PIC24H
+void pichandle( unsigned char app,
+ unsigned char verb,
+ unsigned long len )
+{
+ unsigned int nb; // Number of bytes
+ unsigned int highb, loww; // Used for ICSP commands
+
+ switch (verb) {
+
+ case PIC_DEVID33F:
+ nb = pic33f_getid();
+ txdata(app,verb,nb);
+ break;
+
+ case PIC_SIX33F:
+ loww = *cmddata;
+ loww |= (*(cmddata+1)) << 8;
+ highb = *(cmddata+2);
+ pic33f_six( highb, loww );
+ txdata(app,verb,0);
+ break;
+
+ case PIC_SIXLIST33F:
+ pic33f_sixlist( len ); // Reply to host is handled by pic33f_sixlist.
+ break;
+
+ case PIC_REGOUT33F:
+ loww = pic33f_regout();
+ *cmddata = loww & 0xff;
+ *(cmddata+1) = loww >> 8;
+ txdata(app,verb,2);
+ break;
+
+ case PIC_START33F:
+ pic33f_connect();
+ txdata(app,verb,0);
+ break;
+
+ case PIC_STOP33F:
+ pic33f_disconnect();
+ txdata(app,verb,0);
+ break;
+
+ default:
+ debugstr( "Verb unimplemented in PIC application." );
+ txdata(app,NOK,0);
+ break;
+
+ }
+}
+
+
+void pic33f_trans8( unsigned char byte )
+{
+ /* We only twiddle the PGD and PGC lines.
+ MCLR is assumed to be in the correct state. */
+ unsigned int i;
+
+ DIR_PGD_WR; // Write mode
+ i = 1;
+ while (i & 0xff) {
+ if (byte & i) {
+ SET_PGD;
+ } else {
+ CLR_PGD;
+ }
+ delay_ticks(10);
+ SET_PGC;
+ delay_ticks(10);
+
+ CLR_PGC;
+ delay_ticks(10);
+ i = i << 1;
+ }
+ CLR_PGD;
+ DIR_PGD_RD; // Read mode
+}
+
+void pic33f_trans16( unsigned int word )
+{
+ pic33f_trans8( word & 0xff );
+ pic33f_trans8( word >> 8 );
+}
+
+
+void pic33f_six( unsigned int highb, unsigned int loww )
+{
+ /* dsPIC33F/PIC24H instructions have width 24 bits, so we use the
+ lower 8 bits of highb and (all 16 bits of) loww to form the
+ instruction.
+
+ Shift in the instruction. Note that it does not execute until
+ the next 4 clock cycles (which also corresponds to a command
+ receipt time). */
+ unsigned int i;
+ DIR_PGD_WR;
+ CLR_PGD;
+ CLR_PGC;
+ for (i = 0; i < 4; i++) {
+ SET_PGC;
+ delay_ticks(10);
+ CLR_PGC;
+ delay_ticks(10);
+ }
+ pic33f_trans16( loww );
+ pic33f_trans8( highb );
+ DIR_PGD_RD;
+}
+
+
+unsigned int pic33f_regout()
+{
+ unsigned int i;
+ unsigned int result = 0x0000;
+
+ DIR_PGD_WR;
+
+ // Shift in command (REGOUT: 0001b).
+ SET_PGD;
+ delay_ticks(10);
+ SET_PGC;
+ delay_ticks(10);
+ CLR_PGC;
+ delay_ticks(10);
+
+ CLR_PGD;
+ delay_ticks(10);
+ for (i = 0; i < 3; i++) {
+ SET_PGC;
+ delay_ticks(10);
+ CLR_PGC;
+ delay_ticks(10);
+ }
+
+ // Pump clock for 8 cycles, and switch PGD direction to read.
+ for (i = 0; i < 7; i++) {
+ SET_PGC;
+ delay_ticks(10);
+ CLR_PGC;
+ delay_ticks(10);
+ }
+ DIR_PGD_RD;
+
+ /* Now read VISI register (LSb first, as usual).
+ Note that when reading from attached device, data is valid (to
+ be read) on falling clock edges. */
+ for (i = 0; i < 16; i++) {
+ SET_PGC;
+ delay_ticks(10);
+ CLR_PGC;
+ result |= READ_PGD << i;
+ delay_ticks(10);
+ }
+
+ /* One last tick apparently is needed here, at least by the
+ dsPIC33FJ128GP708 chip that I am working with. Note that this
+ is not in the flash programming specs. */
+ SET_PGC;
+ delay_ticks(10);
+ CLR_PGC;
+ delay_ticks(10);
+
+ return result;
+}
+
+
+void pic33f_sixlist( unsigned int list_len )
+{
+ unsigned int k;
+ unsigned int instr_loww;
+
+ // Bound to Rx buffer size.
+ if (list_len > CMDDATALEN)
+ list_len = CMDDATALEN;
+
+ // Run each instruction!
+ for (k = 0; k < list_len-2; k+=3) {
+ instr_loww = *(cmddata+k);
+ instr_loww |= (*(cmddata+k+1)) << 8;
+ pic33f_six( *(cmddata+k+2), instr_loww );
+ }
+
+ // Reply with total number of bytes used from Rx buffer.
+ txdata( PIC, PIC_SIXLIST33F, k );
+}
+
+
+unsigned int pic33f_getid()
+{
+ unsigned int result;
+ unsigned int nb = 0;
+
+ pic33f_connect();
+
+ // Read application ID.
+ pic33f_six( 0x04, 0x0200 ); // goto 0x200 (i.e. reset)
+ pic33f_six( 0x04, 0x0200 ); // goto 0x200 (i.e. reset)
+ pic33f_six( 0x00, 0x0000 ); // nop
+ pic33f_six( 0x20, 0x0800 ); // mov #0x80, W0
+ pic33f_six( 0x88, 0x0190 ); // mov W0, TBLPAG
+ pic33f_six( 0x20, 0x7F00 ); // mov #0x7F0, W0
+ pic33f_six( 0x20, 0x7841 ); // mov #VISI, W1
+ pic33f_six( 0x00, 0x0000 ); // nop
+ pic33f_six( 0xBA, 0x0890 ); // TBLRDL [W0], [W1]
+ pic33f_six( 0x00, 0x0000 ); // nop
+ pic33f_six( 0x00, 0x0000 ); // nop
+ result = pic33f_regout();
+ *cmddata = result & 0xff;
+ nb += 1;
+
+ // Read DEVID.
+ pic33f_six( 0x20, 0x0FF0 ); // mov #0xFF, W0
+ pic33f_six( 0x88, 0x0190 ); // mov W0, TBLPAG
+ pic33f_six( 0xEB, 0x0000 ); // clr W0
+ pic33f_six( 0x00, 0x0000 ); // nop
+ pic33f_six( 0xBA, 0x08B0 ); // TBLRDL [W0++], [W1]
+ pic33f_six( 0x00, 0x0000 ); // nop
+ pic33f_six( 0x00, 0x0000 ); // nop
+ result = pic33f_regout();
+ *(cmddata+1) = result & 0xff;
+ *(cmddata+2) = result >> 8;
+ nb += 2;
+
+ // Read hardware revision.
+ pic33f_six( 0xBA, 0x0890 ); // TBLRDL [W0++], [W1]
+ pic33f_six( 0x00, 0x0000 ); // nop
+ pic33f_six( 0x00, 0x0000 ); // nop
+ result = pic33f_regout();
+ *(cmddata+3) = result & 0xff;
+ *(cmddata+4) = result >> 8;
+ nb += 2;
+
+ pic33f_disconnect();
+
+ return nb;
+}
+
+
+void pic33f_connect()
+{
+ unsigned int key_low;
+ unsigned int key_high;
+
+ key_low = ICSP_KEY_LOW;
+ key_high = ICSP_KEY_HIGH;
+
+ pic33f_setup();
+
+ CLR_PGC;
+ delay_us(1);
+
+ CLR_MCLR;
+ delay_ms(3);
+ SET_MCLR;
+ delay_us(200);
+ CLR_MCLR;
+ delay_us(10);
+
+ // Enter ICSP key
+ pic33f_trans8( key_low & 0xff );
+ key_low = key_low >> 8;
+ pic33f_trans8( key_low & 0xff );
+ pic33f_trans8( key_high & 0xff );
+ key_high = key_high >> 8;
+ pic33f_trans8( key_high & 0xff );
+
+ delay_us(1);
+ SET_MCLR; // ...and pull MCLR pin back up.
+ delay_ms(25); // Now wait about 25 ms (required per spec!).
+
+ /* The first ICSP command must be a SIX, and further, 9 bits are
+ required before the instruction (to be executed), rather than
+ the typical 4 bits. Thus, to simplify code, I simply load a nop
+ here; hence 33 bits are shifted into the dsPIC33F/PIC24H. */
+ DIR_PGD_WR;
+ CLR_PGD;
+ CLR_PGC;
+ for (key_low = 0; key_low < 33; key_low++) {
+ SET_PGC;
+ delay_us(1);
+ CLR_PGC;
+ delay_us(1);
+ }
+ DIR_PGD_RD;
+
+}
+
+
+void pic33f_disconnect()
+{
+ DIR_PGD_WR;
+ CLR_PGD;
+ CLR_PGC;
+ delay_ms(10);
+ CLR_MCLR;
+}