Preliminary support for PIC24F chips with tblpag at an odd address; finally updated...
authorthequux <thequux@12e2690d-a6be-4b82-a7b7-67c4a43b65c8>
Mon, 17 Jan 2011 06:36:10 +0000 (06:36 +0000)
committerthequux <thequux@12e2690d-a6be-4b82-a7b7-67c4a43b65c8>
Mon, 17 Jan 2011 06:36:10 +0000 (06:36 +0000)
git-svn-id: https://svn.code.sf.net/p/goodfet/code/trunk@843 12e2690d-a6be-4b82-a7b7-67c4a43b65c8

client/goodfet.pic
firmware/apps/pic/pic.c
firmware/include/pic.h

index 7fb9e9e..40de634 100755 (executable)
@@ -24,6 +24,14 @@ from intelhex import IntelHex
 PICAPP = 0x34
 MONITORAPP = 0x00
 NOK = 0x7E
+
+VERB_CMDLIST = 0x88
+
+# 0x32 is the normal address of the TBLPAG register. On some PICs,
+# though, it appears at 0x54 (see DS39970B). Affected PIC families
+# include: PIC24FJxxx{DA1,DA2,GB2,GA3}xx
+tblpag = 0x32
+
 dev_table = { 0x00EE : "dsPIC33FJ128GP708",
               0x00EF : "dsPIC33FJ128GP710",
               0x080A : "PIC24HJ12GP201",
@@ -44,7 +52,26 @@ dev_table = { 0x00EE : "dsPIC33FJ128GP708",
               0x040C : "PIC24FJ96GA010",
               0x0407 : "PIC24FJ128GA006",
               0x040A : "PIC24FJ128GA008",
-              0x040D : "PIC24FJ128GA010" }
+              0x040D : "PIC24FJ128GA010",
+              0x4109 : "PIC24FJ128DA106",
+              0x410D : "PIC24FJ256DA106",
+              0x410B : "PIC24FJ128DA110",
+              0x410F : "PIC24FJ256DA110",
+              0x4108 : "PIC24FJ128DA206",
+              0x410C : "PIC24FJ256DA206",
+              0x410A : "PIC24FJ128DA210",
+              0x410E : "PIC24FJ256DA210",
+              0x46C0 : "PIC24FJ64GA306",
+              0x46C4 : "PIC24FJ64GA308",
+              0x46C8 : "PIC24FJ64GA310",
+              0x46C2 : "PIC24FJ128GA306",
+              0x46C6 : "PIC24FJ128GA308",
+              0x46CA : "PIC24FJ128GA310",
+              0x4100 : "PIC24FJ128GB206",
+              0x4104 : "PIC24FJ256GB206",
+              0x4102 : "PIC24FJ128GB210",
+              0x4106 : "PIC24FJ256GB210",
+              }
 cfg_table = { 0xF80000 : "FBS",
               0xF80002 : "FSS",
               0xF80004 : "FGS",
@@ -80,6 +107,84 @@ Assumes ICSP session already started."""
         stopICSP()
         exit(-1)
 
+class PicResponseThunk(object):
+    """A holder for a response value from the PIC, eg from a REGOUT
+    command.
+
+    To retrieve the value, simply call the object. If the value has
+    not yet been retrieved, any pending commands in the command buffer
+    will be executed. You can determine whether the response has been
+    recieved by calling the have_response function.
+    
+    Objects of this class should only be created by the PicCmdBuffer
+    class"""
+
+    def __init__(self, cmdbuf):
+        self.holder = cmdbuf
+        self.value = None
+
+    def _set_value(self, value):
+        self.value = value
+    def have_response(self):
+        return self.value is not None
+    def __call__(self):
+        if self.value is None:
+            self.holder.force()
+        assert self.value is not None
+        return self.value
+
+class PicCmdBuffer(object):
+    def __init__(self, client):
+        self.client = client
+        self.buffered_commands = []
+        self.response_buffer = []
+        data = client.writecmd(MONITORAPP, 0xc2)
+        assert data is not None and len(data) is 2
+        # buffer size is the size of the goodfet's recieve buffer, in
+        # instructions
+        self.buffer_size = (ord(data[0]) + (ord(data[1]) << 8)) >> 2
+    def force(self):
+        assert len(self.buffered_commands) <= self.buffer_size
+        command = ''.join(self.buffered_commands)
+        self.buffered_commands = []
+        #print "Running: " + command.encode('hex')
+        data = self.client.writecmd(PICAPP, VERB_CMDLIST,
+                                    len(command), map(ord, command))
+        #print "Recieved: " + data.encode('hex')
+        assert len(data) == (len(self.response_buffer) + 1) * 2
+        orig_data = data
+        checksum = 0
+        for resp in self.response_buffer:
+            wd, data = data[0:2],data[2:]
+            val = ord(wd[0]) + (ord(wd[1]) << 8)
+            checksum += val
+            resp._set_value(val)
+        assert len(data) == 2
+        checksum += ord(data[0]) + (ord(data[1]) << 8)
+        checksum &= 0xffff
+        assert checksum == 0
+        self.response_buffer = []
+    def _add_command(self, command, data):
+        assert command in (0,1)
+        assert (data >> 24) == 0
+        full_cmd = [chr(command)]
+        for i in (0,1,2):
+            full_cmd.append(chr((data >> (8*i)) & 0xff))
+        if len(self.buffered_commands) >= self.buffer_size:
+            self.force()
+        self.buffered_commands.append(''.join(full_cmd))
+    def SIX(self, *insns):
+        """Run a list of instructions"""
+        for insn in insns:
+            self._add_command(0, insn)
+        return self
+    def REGOUT(self):
+        "Read the VISI register. Returns a PicResponseThunk"
+        thunk = PicResponseThunk(self)
+        self.response_buffer.append(thunk)
+        self._add_command(1, 0)
+        return thunk
+    
 def build_instr_stream( cmd_li ):
     """Given a list of instruction words, returns a list of bytes of
 the same, in little endian ordering."""
@@ -148,7 +253,7 @@ Returns an instance of IntelHex corresponding to result.
 Note that we start and stop an ICSP session here! This means an
 existing session will be broken."""
     readpm_cmd_li = [0x200000, # MOV #addr<23:16>, W0
-                     0x880190, # MOV W0, TBLPAG
+                     0x880000 | tblpag << 3, # MOV W0, TBLPAG
                      0x200006, # MOV #addr<15:0>, W6
                      0xEB0380, # CLR W7
                      0x000000, # NOP
@@ -259,7 +364,7 @@ Returns [-1] on failure."""
 #############
 
 if len(sys.argv) == 1:
-    print "Usage: %s verb [objects]\n" % sys.argv[0]
+    print "Usage: %s [-alt_tblpag] verb [objects]\n" % sys.argv[0]
     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]
@@ -275,6 +380,7 @@ if len(sys.argv) == 1:
     print "%s regout" % sys.argv[0]
     print """
 Note: use - for stdout.
+      use -alt_tblpag for PIC24FJxxx{DA1,DA2,GB2,GA3}xx with tblpag at 0x54
 Warning: only formally supports dsPIC33F/PIC24H,
          but read/write flash memory works with PIC24F ...
 """
@@ -285,33 +391,58 @@ client = GoodFET()
 client.verbose = False # Dump activity to terminal
 client.serInit( timeout=10 ) # UART comm timeout (in seconds)
 
+if sys.argv[1] == "-alt_tblpag":
+    del sys.argv[1]
+    tblpag = 0x54
 
 # 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
+    startICSP()
+    cmdbuf = PicCmdBuffer(client)
+    cmdbuf.SIX(0x040200, # GOTO 0x200
+               0x000000, # NOP
+               0x200FF0, # MOV #0xff, W0
+               0x880000 | tblpag << 3, # MOV W0, TBLPAG
+               0x200006, # MOV #0x0000, W6
+               0x207847, # MOV #VISI, W7
+               0x000000, # NOP
+               0xBA0B96, # TBLRDL [W6],[W7]
+               0x000000, # NOP
+               0x000000) # NOP
+    devid = cmdbuf.REGOUT()
+    cmdbuf.SIX(0x200026, # MOV #0x0002, W6
+               0x000000, # NOP
+               0xBA0B96, # TBLRDL [W6],[W7]
+               0x000000, # NOP
+               0x000000) # NOP
+    devidrev = cmdbuf.REGOUT()
+    cmdbuf.SIX(0x200800, # MOV #0x80, W0
+               0x880000 | tblpag << 3, # MOV W0, TBLPAG
+               0x207F00, # MOV #0x07F0, W6
+               0x000000, # NOP
+               0xBA0B96, # TBLRDL [W6],[W7]
+               0x000000, # NOP
+               0x000000) # NOP
+    appid = cmdbuf.REGOUT()
+    print "Application ID:   0x%02x" % appid()
+    print "DEVID:            0x%04x  (%s)" % (
+        devid(), dev_table.get(devid(), "unknown"))
+    print "Revision:  0x%04x" % devidrev()
+    stopICSP()
     #print "\n(Note that -1 indicates failure to read a value.)"
 
+elif sys.argv[1] == "test":
+    startICSP()
+    cmdbuf = PicCmdBuffer(client)
+    cmdbuf.SIX(0x2BEEF6, # MOV #0xBEEF, W6
+               0x000000,
+               0x780B86, # MOV W6, [W7]
+               0x000000, # NOP
+               0x000000) # NOP
+    assert cmdbuf.REGOUT()() == 0xbeef
+    stopICSP()
+    
 elif sys.argv[1] == "reset":
     client.writecmd( PICAPP, 0x87, 0 )
 
@@ -319,7 +450,7 @@ elif sys.argv[1] == "config": # Dump configuration registers
     prep_cmd_li = [0x040200, # GOTO 0x0200
                    0x000000, #
                    0x200F80, # MOV #0x00F8, W0
-                   0x880190, # MOV W0, TBLPAG
+                   0x880000 | tblpag << 3, # MOV W0, TBLPAG
                    0xEB0300, # CLR W6
                    0x207847, # MOV #VISI, W7
                    0x000000] # NOP
@@ -366,7 +497,7 @@ elif sys.argv[1] == "program":
                           0x24001A, # MOV $0x4001, W10
                           0x883B0A, # MOV W10, NVMCON
                           0x200000, # + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
-                          0x880190, # MOV W0, TBLPAG
+                          0x880000 | tblpag << 3, # MOV W0, TBLPAG
                           0x200007] # + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
 
     # Load 4 instructions into write latches
@@ -458,7 +589,7 @@ elif sys.argv[1] == "program":
                      0x24000A, # MOV #0x4000, W10
                      0x883B0A, # MOV W10, NVMCON
                      0x200F80, # MOV #0xF8, W0
-                     0x880190, # MOV W0, TBLPAG
+                     0x880000 | tblpag<< 3, # MOV W0, TBLPAG
                      0x200000 + ((proghex[addr]&0x00ff)<<4), # MOV #new_val<7:0>, W0
                      0xBB0B80, # TBLWTL W0, [W7]
                      0x000000, # NOP
@@ -539,7 +670,7 @@ elif sys.argv[1] == "write":
                 0x24003A, # MOV $0x4003, W10
                 0x883B0A, # MOV W10, NVMCON
                 0x200000 + ((addr>>12)&0xff0),  # MOV #addr<23:16>, W0
-                0x880190, # MOV W0, TBLPAG
+                0x880000 | tblpag << 3, # MOV W0, TBLPAG
                 0x200007 + ((addr&0xffff)<<4), # MOV #addr<15:0>, W7
 
                 # Here we load the instruction into registers W0:W1
@@ -618,7 +749,7 @@ elif sys.argv[1] == "write_config":
                  0x24000A, # MOV #0x4000, W10
                  0x883B0A, # MOV W10, NVMCON
                  0x200F80, # MOV #0xF8, W0
-                 0x880190, # MOV W0, TBLPAG
+                 0x880000 | tblpag << 3, # MOV W0, TBLPAG
                  0x200000 + ((new_val&0xffff)<<4), # MOV #<new_val>, W0
                  0xBB0B80, # TBLWTL W0, [W7]
                  0x000000, # NOP
@@ -648,7 +779,7 @@ elif sys.argv[1] == "read": # Read an address of program memory
     readpm_cmd_li = [0x040200,                         # GOTO 0x0200
                      0x000000,                         #
                      0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
-                     0x880190,                         # MOV W0, TBLPAG
+                     0x880000 | tblpag << 3,                         # MOV W0, TBLPAG
                      0x200006+((addr & 0xffff)<<4),    # MOV #addr<15:0>, W6
                      0xEB0380,                         # CLR W7
                      0xEB0080,                         # CLR W1
@@ -722,7 +853,7 @@ elif sys.argv[1] == "erase": # Bulk (all program memory) or page erase
                         0x24042A, # MOV $0x4042, W10
                         0x883B0A, # MOV W10, NVMCON
                         0x200000+((addr & 0xff0000)>>12), # MOV #addr<23:16>, W0
-                        0x880190, # MOV W0, TBLPAG
+                        0x880000 | tblpag << 3, # MOV W0, TBLPAG
                         0x200001+((addr&0xffff)<<4), # MOV addr<15:0>, W1
                         0x000000, # NOP
                         0xBB0881, # TBLTWL W1, [W1]
index 4e25fea..0c8664e 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: t -*- */
 /*! \file dspic33f.c
 
   \author Scott Livingston
@@ -14,6 +15,8 @@
 
 #include "pic.h"
 
+#define CYCLE_DELAY() delay_ticks(10);
+
 //! Handle a PIC command; currently assumes dsPIC33F/PIC24H
 void pic_handle_fn( uint8_t const app,
                                        uint8_t const verb,
@@ -101,6 +104,10 @@ void pic_handle_fn( uint8_t const app,
                txdata(app,verb,0);
                break;
 
+       case PIC_CMDLIST:
+               pic33f_cmdlist(len); // reply is handled by pic33f_cmdlist
+               break;
+
        default:
                debugstr( "Verb unimplemented in PIC application." );
                txdata(app,NOK,0);
@@ -109,6 +116,23 @@ void pic_handle_fn( uint8_t const app,
        }
 }
 
+void pic33f_transcmd(unsigned char cmd) {
+  int i = 0;
+  DIR_PGD_WR;
+  CLR_PGC;
+  for (i = 0; i < 4; i++) {
+    if (cmd & 0x1)
+      SET_PGD;
+    else
+      CLR_PGD;
+    CYCLE_DELAY();
+    SET_PGC;
+    cmd >>= 1;
+    CYCLE_DELAY();
+    CLR_PGC;
+  }
+  CLR_PGD;
+}
 
 void pic33f_trans8( unsigned char byte )
 {
@@ -117,20 +141,19 @@ void pic33f_trans8( unsigned char byte )
        unsigned int i;
        
        DIR_PGD_WR; // Write mode
-       i = 1;
-       while (i & 0xff) {
-               if (byte & i) {
+       for (i = 0; i < 8; i++) {
+               if (byte & 0x01) {
                        SET_PGD;
                } else {
                        CLR_PGD;
                }
-               delay_ticks(10);
+               CYCLE_DELAY();
                SET_PGC;
-               delay_ticks(10);
+               byte >>= 1;
+               CYCLE_DELAY();
 
                CLR_PGC;
-               delay_ticks(10);
-               i = i << 1;
+               //CYCLE_DELAY();
        }
        CLR_PGD;
        DIR_PGD_RD; // Read mode
@@ -152,16 +175,7 @@ void pic33f_six( unsigned int highb, unsigned int loww )
           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_transcmd(0);
        pic33f_trans16( loww );
        pic33f_trans8( highb );
        DIR_PGD_RD;
@@ -176,28 +190,14 @@ unsigned int pic33f_regout()
        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);
-       }
+       pic33f_transcmd(1);
 
        // Pump clock for 8 cycles, and switch PGD direction to read.
        for (i = 0; i < 7; i++) {
                SET_PGC;
-               delay_ticks(10);
+               CYCLE_DELAY();
                CLR_PGC;
-               delay_ticks(10);
+               CYCLE_DELAY();
        }
        DIR_PGD_RD;
 
@@ -206,24 +206,90 @@ unsigned int pic33f_regout()
           be read) on falling clock edges. */
        for (i = 0; i < 16; i++) {
                SET_PGC;
-               delay_ticks(10);
+               CYCLE_DELAY();
                CLR_PGC;
                result |= READ_PGD << i;
-               delay_ticks(10);
+               CYCLE_DELAY();
        }
-
+#if 1
        /* 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);
+       CYCLE_DELAY();
        CLR_PGC;
-       delay_ticks(10);
+       CYCLE_DELAY();
+#endif
 
        return result;
 }
 
+void pic33f_cmdlist(unsigned int list_len) {
+       /* commands are written as 4-byte little-endian records, where
+          the low 4 bits of first byte contains the command, and the
+          next three bytes contain the data.
+
+          Currently this only supports the SIX and REGOUT
+          instructions.
+
+          SIX instructions return no data. REGOUT instructions return
+          the 16-bit value read as two bytes, lower byte first.
+          
+          The final two bytes of the response are the 2's complement
+          inverse of the sum of the response words. i.e., if the
+          response is correctly recieved, the sum of the words should
+          be 0.
+
+          This is sent when the goodfet is done running the command
+          list, and is ready for further commands.
+       */
+       
+       unsigned char cmd;
+       unsigned int response_word;
+       unsigned int checksum = 0;
+       int response_count;
+       int i;
+       list_len &= ~3; // truncate to multiple of 4 bytes.
+       if (list_len > CMDDATALEN)
+               list_len = CMDDATALEN;
+       response_count = 1;
+       for (i = 0; i < list_len; i += 4) {
+               cmd = cmddata[i];
+               if (cmd == 0)
+                       continue;
+               else if (cmd == 1)
+                       response_count ++;
+               else
+                       goto error;
+       }
+       txhead(PIC, PIC_CMDLIST, response_count << 1);
+
+       for (i = 0; i < list_len; i+= 4) {
+               cmd = cmddata[i];
+               if (cmd == 0) {
+                       // SIX command
+                       pic33f_transcmd(0);
+                       pic33f_trans8(cmddata[i+1]);
+                       pic33f_trans8(cmddata[i+2]);
+                       pic33f_trans8(cmddata[i+3]);
+                       
+               } else if (cmd == 1) {
+                       // REGOUT command
+                       response_word = pic33f_regout();
+                       checksum += response_word;
+                       response_count--;
+                       txword(response_word);
+               }
+       }
+       txword(~checksum + 1);
+       if (response_count != 1)
+               debugstr("Response count wrong");
+       return;
+ error:
+       txdata(PIC, NOK, 0);
+}
 
+/* This should be replaced by pic33f_cmdlist */
 void pic33f_sixlist( unsigned int list_len )
 {
        unsigned int k;
@@ -245,6 +311,7 @@ void pic33f_sixlist( unsigned int list_len )
 }
 
 
+/* This is slated to be replaced by pic33f_cmdlist */
 unsigned int pic33f_getid()
 {
        unsigned int result;
@@ -336,9 +403,9 @@ void pic33f_connect()
        CLR_PGC;
        for (key_low = 0; key_low < 33; key_low++) {
                SET_PGC;
-               delay_us(1);
+               CYCLE_DELAY();
                CLR_PGC;
-               delay_us(1);
+               CYCLE_DELAY();
        }
        DIR_PGD_RD;
 
index e95c054..3bd69f7 100644 (file)
@@ -69,6 +69,9 @@ unsigned int pic33f_regout();
 //! Execute a list of commands on attached dsPIC33F/PIC24H.
 void pic33f_sixlist( unsigned int list_len );
 
+//! Execute a list of ICSP commands on attached PIC
+void pic33f_cmdlist(unsigned int list_len);
+
 //! Start Enhanced ICSP session with dsPIC33F/PIC24H (assumes Programming Executive is present).
 void pic33f_eicsp_connect();
 
@@ -96,6 +99,7 @@ void pic33f_trans8( unsigned char byte );
 #define PIC_SIXLIST33F 0x86 /* Buffers list of instructions to MSP430,
                                                           then executes them over ICSP session
                                                           with target dsPIC33F/PIC24H chip. */
+#define PIC_CMDLIST    0x88 /* Similar to PIC_SIXLIST33F, but includes ICSP command */
 
 #define PIC_RESET33F   0x87 // Reset attached dsPIC33F/PIC24H chip.
 #define PIC_START33F   0x84 // Start ICSP session