From dbcedaa279472080a2585e959a3146f3c5859556 Mon Sep 17 00:00:00 2001 From: scottlivingston Date: Thu, 8 Apr 2010 03:06:22 +0000 Subject: [PATCH] First addition of dsPIC33F/PIC24H chip family programmer! See README under firmware/apps/pic for details. Note that it has only been tested on dsPIC33FJ128GP708 (and x710) chips and lacks some functionality and documentation. Added optional timeout argument to GoodFET client class serInit method. This is passed along to the serial object. To build my dsPIC33F/PIC24H related app, define INSTALL_PIC_APP somewhere near the top of goodfet.c and add apps/pic/dspic33f.o to the firmware Makefile. Added new timing routines that use Timer A to count milliseconds, microseconds and clock ticks. These overshoot by a few instructions, and may need refinements as research demands. Fixed minor typos in comments of a few source files. git-svn-id: https://svn.code.sf.net/p/goodfet/code/trunk@452 12e2690d-a6be-4b82-a7b7-67c4a43b65c8 --- client/GoodFET.py | 71 ++-- client/goodfet.monitor | 2 +- client/goodfet.pic | 782 +++++++++++++++++++++++++++++++++++ firmware/apps/pic/README.txt | 75 ++++ firmware/apps/pic/dspic33f.c | 330 +++++++++++++++ firmware/goodfet.c | 5 + firmware/include/apps.h | 2 + firmware/include/avr.h | 2 +- firmware/include/command.h | 17 + firmware/include/dspic33f.h | 92 +++++ firmware/include/platform.h | 2 +- firmware/lib/command.c | 54 +++ 12 files changed, 1402 insertions(+), 32 deletions(-) create mode 100755 client/goodfet.pic create mode 100644 firmware/apps/pic/README.txt create mode 100644 firmware/apps/pic/dspic33f.c create mode 100644 firmware/include/dspic33f.h diff --git a/client/GoodFET.py b/client/GoodFET.py index 5c38ca9..468b091 100755 --- a/client/GoodFET.py +++ b/client/GoodFET.py @@ -47,6 +47,13 @@ class SymbolTable: class GoodFET: """GoodFET Client Library""" + + besilent=0; + app=0; + verb=0; + count=0; + data=""; + verbose=False GLITCHAPP=0x71; symbols=SymbolTable(); @@ -60,7 +67,7 @@ class GoodFET: return self.symbols.get(name); def timeout(self): print "timeout\n"; - def serInit(self, port=None): + def serInit(self, port=None, timeout=None): """Open the serial port""" if port is None and os.environ.get("GOODFET")!=None: @@ -80,12 +87,10 @@ class GoodFET: port, #9600, 115200, - parity = serial.PARITY_NONE + parity = serial.PARITY_NONE, + timeout=timeout ) - #This might cause problems, but it makes failure graceful. - #self.serialport._timeout = 5; - #Explicitly set RTS and DTR to halt board. self.serialport.setRTS(1); self.serialport.setDTR(1); @@ -116,6 +121,9 @@ class GoodFET: #little endian 16-bit length self.serialport.write(chr(count&0xFF)); self.serialport.write(chr(count>>8)); + + if self.verbose: + print "Tx: ( 0x%02x, 0x%02x, 0x%04x )" % ( app, verb, count ) #print "count=%02x, len(data)=%04x" % (count,len(data)); @@ -128,34 +136,39 @@ class GoodFET: outstr=''.join(data); self.serialport.write(outstr); if not self.besilent: - self.readcmd(); - - besilent=0; - app=0; - verb=0; - count=0; - data=""; + return self.readcmd() + else: + return [] def readcmd(self): """Read a reply from the GoodFET.""" - while 1: - #print "Reading..."; - self.app=ord(self.serialport.read(1)); - #print "APP=%2x" % self.app; - self.verb=ord(self.serialport.read(1)); - #print "VERB=%02x" % self.verb; - self.count=( - ord(self.serialport.read(1)) - +(ord(self.serialport.read(1))<<8) - ); + while 1:#self.serialport.inWaiting(): # Loop while input data is available + try: + #print "Reading..."; + self.app=ord(self.serialport.read(1)); + #print "APP=%2x" % self.app; + self.verb=ord(self.serialport.read(1)); + #print "VERB=%02x" % self.verb; + self.count=( + ord(self.serialport.read(1)) + +(ord(self.serialport.read(1))<<8) + ); + + if self.verbose: + print "Rx: ( 0x%02x, 0x%02x, 0x%04x )" % ( self.app, self.verb, self.count ) - #Debugging string; print, but wait. - if self.app==0xFF and self.verb==0xFF: - print "# DEBUG %s" % self.serialport.read(self.count); - sys.stdout.flush(); - else: - self.data=self.serialport.read(self.count); - return self.data; + #Debugging string; print, but wait. + if self.app==0xFF and self.verb==0xFF: + print "# DEBUG %s" % self.serialport.read(self.count); + sys.stdout.flush(); + return [] + else: + self.data=self.serialport.read(self.count); + return self.data; + except TypeError: + print "Error: waiting for serial read timed out (most likely)." + sys.exit(-1) + #Glitching stuff. def glitchApp(self,app): """Glitch into a device by its application.""" diff --git a/client/goodfet.monitor b/client/goodfet.monitor index fb92528..2ea400a 100755 --- a/client/goodfet.monitor +++ b/client/goodfet.monitor @@ -20,7 +20,7 @@ if(len(sys.argv)==1): print "%s exec '0x35 0x00 0x..'" % sys.argv[0]; sys.exit(); -#Initailize FET and set baud rate +#Initialize FET and set baud rate client=GoodFET(); client.serInit() diff --git a/client/goodfet.pic b/client/goodfet.pic new file mode 100755 index 0000000..66f08a1 --- /dev/null +++ b/client/goodfet.pic @@ -0,0 +1,782 @@ +#!/usr/bin/env python +# PIC client (currently only supports dsPIC33F/PIC24H.) +# +# Scott Livingston +# +# 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 #, 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) + diff --git a/firmware/apps/pic/README.txt b/firmware/apps/pic/README.txt new file mode 100644 index 0000000..ec51f26 --- /dev/null +++ b/firmware/apps/pic/README.txt @@ -0,0 +1,75 @@ +PIC programmer +March, April 2010. + +Scott Livingston + + +Currently targeted at dsPIC33F/PIC24H chip family and tested on + +dsPIC33FJ128GP710, +dsPIC33FJ128GP708 + +(i.e. similar chip, differing in footprint: former is 100-TQFP, latter +is 80-TQFP). + +For the 14-pin GoodFET port (size 2x7), the pinout is +3 -> PGD +5 -> !MCLR +7 -> PGC +2 -> Vcc +9 -> GND + +Programming is done over a protocol similar to 2-wire SPI (i.e., MOSI +and MISO are combined into a single data line), where the GoodFET is +the master. Currently only ICSP is supported, i.e., the programming +executive (similar to a bootstrap loader in other architectures) is +ignored, if present. Note that dsPIC33F/PIC24H chips seem to ship +without the programming executive present. Confer Microchip document +"dsPIC33F/PIC24H Flash Programming Specification" (DS70152G). + +When receiving from (resp. replying to) host, data on the PGD line is +latched (resp. written) by the dsPIC33f/PIC24H chip on the rising edge +of PGC. A statement to this effect appears as a note in section 5.3, +Chapter 5, of the dsPIC33F/PIC24H Flash Programming Spec. + +In the dsPIC33F/PIC24H flash programming spec, there is a typo +regarding verification of the presence of the programming +executive. In Section 5.11, it is stated that an Application ID of +0xBB indicates the programming executive is resident. As of 4 April +2010, the correct App ID is 0xCB, as listed later in the spec +document, Section 7 "Device ID". To dump the device and application +IDs and hardware revision number of an attached dsPIC33F/PIC24H chip, +use the goodfet.pic client: + +$ ./goodfet.pic devid + + +Much more documentation is needed for the programmer and is +forthcoming along with more common tools (e.g. verify programming +results). For now, here are some quick examples. To bulk erase program +memory (this is necessary before programming), + +$ ./goodfet.pic erase + +Then to program the device with a code file, foo.hex, + +$ ./goodfet.pic program foo.hex + + +A quick visual check of results might be a dump of the first few +instruction words. + +$ ./goodfet.pic - 0x200 0x220 pretty + +This command prints (24-bit width) contents of program memory at +addresses 0x200 through 0x220 to stdout. + + +For setting up your development environment, I suggest matt's article +on installing Microchip's GCC port for dsPIC/PIC24 microcontrollers +under GNU/Linux (and almost certainly extends to other Unix-like +environments): + +http://www.electricrock.co.nz/blog/2009/08/installing-microchips-c-compiler-for-pic24-mcus-and-dspic-dscs-c30-on-ubuntu-9-04/ + +The port is called C30 by Microchip. diff --git a/firmware/apps/pic/dspic33f.c b/firmware/apps/pic/dspic33f.c new file mode 100644 index 0000000..b2534b0 --- /dev/null +++ b/firmware/apps/pic/dspic33f.c @@ -0,0 +1,330 @@ +/*! \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; +} diff --git a/firmware/goodfet.c b/firmware/goodfet.c index 4d791f8..f82c5b3 100644 --- a/firmware/goodfet.c +++ b/firmware/goodfet.c @@ -75,6 +75,11 @@ void handle(unsigned char app, case AVR: avrhandle(app,verb,len); break; +#ifdef INSTALL_PIC_APP + case PIC: + pichandle(app,verb,len); + break; +#endif case I2CAPP: i2chandle(app,verb,len); break; diff --git a/firmware/include/apps.h b/firmware/include/apps.h index dc4e536..e8d2463 100644 --- a/firmware/include/apps.h +++ b/firmware/include/apps.h @@ -14,6 +14,8 @@ #define AVR 0x32 #define JTAGARM7TDMI 0x33 +#define PIC 0x34 + #define OCT 0x70 #define GLITCH 0x71 #define PLUGIN 0x72 diff --git a/firmware/include/avr.h b/firmware/include/avr.h index 930ab99..c56d86c 100644 --- a/firmware/include/avr.h +++ b/firmware/include/avr.h @@ -34,7 +34,7 @@ u8 avr_pokeeeprom(u16 adr, u8 val); u8 avr_isready(); //Command codes. -//! Performa chip erase. +//! Perform a chip erase. #define AVR_ERASE 0xF0 //! Fetch RDY/!BSY byte. #define AVR_RDYBSY 0xF1 diff --git a/firmware/include/command.h b/firmware/include/command.h index 0b244ab..dda9b64 100644 --- a/firmware/include/command.h +++ b/firmware/include/command.h @@ -126,6 +126,19 @@ void delay(unsigned int count); void msdelay(unsigned int ms); +//! Prepare Timer A; call before using delay_ms or delay_us. +void prep_timer(); + +//! Delay for specified number of milliseconds (given 16 MHz clock) +void delay_ms( unsigned int ms ); + +//! Delay for specified number of microseconds (given 16 MHz clock) +void delay_us( unsigned int us ); + +//! Delay for specified number of clock ticks (16 MHz clock implies 62.5 ns per tick). +void delay_ticks( unsigned int num_ticks ); + + void monitorhandle(unsigned char, unsigned char, unsigned long); void spihandle(unsigned char, unsigned char, unsigned long); void i2chandle(unsigned char, unsigned char, unsigned long) WEAKDEF; @@ -143,3 +156,7 @@ void avrhandle(unsigned char app, int smartcardhandle(unsigned char app, unsigned char verb, unsigned int len); + +void pichandle( unsigned char app, + unsigned char verb, + unsigned long len ); diff --git a/firmware/include/dspic33f.h b/firmware/include/dspic33f.h new file mode 100644 index 0000000..b6549ea --- /dev/null +++ b/firmware/include/dspic33f.h @@ -0,0 +1,92 @@ +/*! \file dspic33f.h + + \author Scott Livingston + + \brief Definitions for the dsPIC33F programmer application. Note + that the programming for dsPIC33F/PIC24H chips is + non-standard 2-wire SPI; hence, I do not use the existing + GoodFET SPI routines. + + \date March, April 2010 + +*/ + + +/*! Magic, device family specific constants (these are drawn from the + dsPIC33F/PIC24H Flash Programming Specification). Note that the + ICSP key is in bit-reversed order, since it is the only thing that + is sent MSb first (hence, we flip the bit order and use our usual + LSb-first routines). */ +#define ICSP_KEY_LOW 0xC2B2 +#define ICSP_KEY_HIGH 0x8A12 +#define APPID_ADDR 0x8007F0 + +//! I/O (pin level); follows spi.h +#define PGD BIT1 +#define PGC BIT3 +#define MCLR BIT0 + +//Handle bidirectionality of PGD +#define DIR_PGD_RD P5DIR&=~PGD +#define DIR_PGD_WR P5DIR|=PGD + +#define SET_MCLR P5OUT|=MCLR +#define CLR_MCLR P5OUT&=~MCLR +#define SET_PGD P5OUT|=PGD +#define CLR_PGD P5OUT&=~PGD +#define SET_PGC P5OUT|=PGC +#define CLR_PGC P5OUT&=~PGC + +#define READ_PGD (P5IN&PGD?1:0) + + +//! Set pins as appropriate for dsPIC33F/PIC24H +void pic33f_setup(); + +//! Start ICSP with an attached dsPIC33F/PIC24H +void pic33f_connect(); + +/*! Stop ICSP session; maybe not necessary, but cleaner since a + minimum delay is required before dropping MCLR pin after last + transmission to dsPIC33F/PIC24H chip. */ +void pic33f_disconnect(); + +//! ICSP SIX command: execute single command on attached dsPIC33F/PIC24H. +void pic33f_six( unsigned int highb, unsigned int loww ); + +//! ICSP REGOUT command: read contents of VISI register +unsigned int pic33f_regout(); + +//! Execute a list of commands on attached dsPIC33F/PIC24H. +void pic33f_sixlist( unsigned int list_len ); + +//! Start Enhanced ICSP session with dsPIC33F/PIC24H (assumes Programming Executive is present). +void pic33f_eicsp_connect(); + +/*! Get Application ID (u8), Device ID (i.e. DEVID; u16) and hardware + revision (u16). Results are dumped to the host connection (a la + txdata, app is PIC and verb is PIC_DEVID33F). + + This function assumes no ICSP (or Enhanced ICSP) session is currently open! + + Returns number of bytes written to cmddata buffer. +*/ +unsigned int pic33f_getid(); + +//! Write word +void pic33f_trans16( unsigned int word ); + +//! Write byte +void pic33f_trans8( unsigned char byte ); + + +//Command codes +#define PIC_DEVID33F 0x81 //! Read Device and Application ID +#define PIC_SIX33F 0x82 //! ICSP six command; execute instruction on target. +#define PIC_REGOUT33F 0x83 //! Read out VISI register. +#define PIC_SIXLIST33F 0x86 /* Buffers list of instructions to MSP430, + then executes them over ICSP session + with target dsPIC33F/PIC24H chip. */ + +#define PIC_START33F 0x84 // Start ICSP session +#define PIC_STOP33F 0x85 // Stop ICSP (basically, drop !MCLR pin and pause briefly) diff --git a/firmware/include/platform.h b/firmware/include/platform.h index 87b18cc..526c877 100644 --- a/firmware/include/platform.h +++ b/firmware/include/platform.h @@ -25,5 +25,5 @@ void msp430_init_dco(); //LED on P1.0 #define PLEDOUT P1OUT #define PLEDDIR P1DIR -#define PLEDPIN 0x1 +#define PLEDPIN BIT0 diff --git a/firmware/lib/command.c b/firmware/lib/command.c index 415171b..1de6f0a 100644 --- a/firmware/lib/command.c +++ b/firmware/lib/command.c @@ -140,3 +140,57 @@ void msdelay(unsigned int ms){ } //Using TimerA might be cleaner. } + + +/* To better satisfy the somewhat odd timing requirements for + dsPIC33F/PIC24H ICSP programming, and for better control of GoodFET + timing more generally, here are a few delay routines that use Timer A. + + Note that I wrote these referring only to the MSP430x2xx family + manual. Beware on MSP430x1xx chips. Further note that, assuming + some minor errors will be made, I try to err on the side of + delaying slightly longer than requested. */ +void prep_timer() +{ + BCSCTL2 = 0x00; /* In particular, use DCOCLK as SMCLK source with + divider 1. Hence, Timer A ticks with system + clock at 16 MHz. */ + + TACTL = 0x0204; /* Driven by SMCLK; disable Timer A interrupts; + reset timer in case it was previously in use */ +} + +//! Delay for specified number of milliseconds (given 16 MHz clock) +void delay_ms( unsigned int ms ) +{ + // 16000 ticks = 1 ms + TACTL |= 0x20; // Start timer! + while (ms--) { + while (TAR < 16000) + asm( "nop" ); + TACTL = 0x0224; + } + TACTL = 0x0204; // Reset Timer A, till next time +} + +//! Delay for specified number of microseconds (given 16 MHz clock) +void delay_us( unsigned int us ) +{ + // 16 ticks = 1 us + TACTL |= 0x20; // Start timer! + while (us--) { + while (TAR < 16) + asm( "nop" ); + TACTL = 0x0224; + } + TACTL = 0x0204; // Reset Timer A, till next time +} + +//! Delay for specified number of clock ticks (16 MHz clock implies 62.5 ns per tick). +void delay_ticks( unsigned int num_ticks ) +{ + TACTL |= 0x20; // Start timer + while (TAR < num_ticks) + asm( "nop" ); + TACTL = 0x0204; // Reset Timer A, till next time +} -- 2.20.1