Added newly supported boards:
[goodfet] / client / goodfet.bsl
index 949c431..c3f84b1 100755 (executable)
 # Forked by Travis Goodspeed <travis at tnbelt.com> for use with the GoodFET
 # JTAG programmer.
 
+# This WIP is intended to save the info flash when using --fromweb.
+# DONE:
+#  - Save info flash before actionMassErase
+#  - Make actionFromweb use board name to get the right firmware image
+#  - Program saved info after other programming is complete
+#  - Make saveinfo detect missing info (all 0xFF)
+#  - Download image from web before erasing
+# TODO:
+#  - Trim unnecessary BSL unlocks
+#  - Add param to override saveinfo with a provided info.txt
+#  - Figure out better way to saveinfo when -P is required
+#  - Maybe use the best guess from contrib/infos/ when nothing better is provided?
+#  - If saveinfo gets something interesting, request a copy
+#  - Use FTDI Serial Number to archive info text and/or password (dragorn's idea)
+#      /sys/bus/usb-serial/devices/ttyUSB0/../../serial
+
 import sys, time, string, cStringIO, struct
-sys.path.append("/usr/lib/tinyos")
-import serial, os, glob
+#sys.path.append("/usr/lib/tinyos")  #We no longer require TinyOS.
+import serial, os, subprocess, glob
 
 #forked from TinyOS Telos version.
 VERSION = string.split("Revision: 1.39-goodfet-8 ")[1] 
@@ -204,10 +220,26 @@ deviceids = {
 }
 
 #GoodFET firmware
-firmware = {
-    0xf16c: "http://goodfet.sourceforge.net/dist/msp430x1612.hex",
-    0xf26f: "http://goodfet.sourceforge.net/dist/msp430x2618.hex",
-    0xf227: "http://goodfet.sourceforge.net/dist/msp430x2274.hex"
+FIRMWARE_BASEURL = "http://goodfet.sourceforge.net/dist/";
+
+board = None
+BOARDS = {
+    #'apimote': "apimote1",    # .hex not uploaded yet
+    #'apimote1': "apimote1",
+    'facedancer10': "facedancer10",
+    'facedancer11': "facedancer11",
+    'facedancer20': "facedancer20",
+    'facedancer21': "facedancer21",
+    'goodfet11': "goodfet11",
+    'goodfet31': "goodfet31",
+    'goodfet41': "goodfet41",
+    'goodfet42': "goodfet42",
+    'goodthopter10': "goodthopter10",
+    'goodthopter11': "goodthopter11",
+    'telosb': "telosb",
+    'telosbbt': "telosb",
+    'z1': "z1",
+    'zolertiaz1': "z1",
 }
 
 class BSLException(Exception):
@@ -258,9 +290,11 @@ class LowLevel:
     ERR_RX_NAK              = "NAK received (wrong password?)"
     #ERR_CMD_NOT_COMPLETED   = "Command did not send ACK: indicates that it didn't complete correctly"
     ERR_CMD_FAILED          = "Command failed, is not defined or is not allowed"
-    ERR_BSL_SYNC            = "Bootstrap loader synchronization error"
+    ERR_BSL_SYNC            = "Bootstrap loader synchronization error (maybe you need -P)"
     ERR_FRAME_NUMBER        = "Frame sequence number error."
-
+    
+    z1 = 0;
+    
     def calcChecksum(self, data, length):
         """Calculates a checksum of "data"."""
         checksum = 0
@@ -312,8 +346,10 @@ class LowLevel:
             timeout = self.timeout
         )
         if DEBUG: sys.stderr.write("using serial port %r\n" % self.serialport.portstr)
-        self.SetRSTpin()                        #enable power
-        self.SetTESTpin()                       #enable power
+        
+        if not self.z1:
+            self.SetRSTpin()                        #enable power
+            self.SetTESTpin()                       #enable power
         self.serialport.flushInput()
         self.serialport.flushOutput()
 
@@ -523,6 +559,83 @@ class LowLevel:
         self.telosI2CWriteByte( 0x90 | (addr << 1) )
         self.telosI2CWriteByte( cmdbyte )
         self.telosI2CStop()
+    def writepicROM(self, address, data):
+        ''' Writes data to @address'''
+        for i in range(7,-1,-1):
+            self.picROMclock((address >> i) & 0x01)
+        self.picROMclock(0)
+        recbuf = 0
+        for i in range(7,-1,-1):
+            s = ((data >> i) & 0x01)
+            #print s
+            if i < 1:
+                r = not self.picROMclock(s, True)
+            else:
+                r = not self.picROMclock(s)
+            recbuf = (recbuf << 1) + r
+
+        self.picROMclock(0, True)
+        #k = 1
+        #while not self.serial.getCTS():
+        #    pass 
+        #time.sleep(0.1)
+        return recbuf
+
+    def readpicROM(self, address):
+        ''' reads a byte from @address'''
+        for i in range(7,-1,-1):
+            self.picROMclock((address >> i) & 0x01)
+        self.picROMclock(1)
+        recbuf = 0
+        r = 0
+        for i in range(7,-1,-1):
+            r = self.picROMclock(0)
+            recbuf = (recbuf << 1) + r
+        self.picROMclock(r)
+        #time.sleep(0.1)
+        return recbuf
+        
+    def picROMclock(self, masterout, slow = False):
+        #print "setting masterout to "+str(masterout)
+        self.serialport.setRTS(masterout)
+        self.serialport.setDTR(1)
+        #time.sleep(0.02)
+        self.serialport.setDTR(0)
+        if slow:
+            time.sleep(0.02)
+        return self.serialport.getCTS()
+
+    def picROMfastclock(self, masterout):
+        #print "setting masterout to "+str(masterout)
+        self.serialport.setRTS(masterout)
+        self.serialport.setDTR(1)
+        self.serialport.setDTR(0)
+        time.sleep(0.02)
+        return self.serialport.getCTS()
+
+    def bslResetZ1(self, invokeBSL=0):
+        '''
+        Applies BSL entry sequence on RST/NMI and TEST/VPP pins
+        Parameters:
+            invokeBSL = 1: complete sequence
+            invokeBSL = 0: only RST/NMI pin accessed
+            
+        By now only BSL mode is accessed
+        '''
+        
+        if DEBUG > 1: sys.stderr.write("* bslReset(invokeBSL=%s)\n" % invokeBSL)
+        if invokeBSL:
+            #sys.stderr.write("in Z1 bsl reset...\n")
+            time.sleep(0.1)
+            self.writepicROM(0xFF, 0xFF)
+            time.sleep(0.1)
+            #sys.stderr.write("z1 bsl reset done...\n")
+        else:
+            #sys.stderr.write("in Z1 reset...\n")
+            time.sleep(0.1)
+            self.writepicROM(0xFF, 0xFE)
+            time.sleep(0.1)
+            #sys.stderr.write("z1 reset done...\n")
 
     def telosBReset(self,invokeBSL=0):
         # "BSL entry sequence at dedicated JTAG pins"
@@ -564,6 +677,12 @@ class LowLevel:
         if self.telosI2C:
             self.telosBReset(invokeBSL)
             return
+        
+        if self.z1:
+            if DEBUG > 1: sys.stderr.write("* entering bsl with z1\n")
+            self.bslResetZ1(invokeBSL)
+            return
+    
 
         if DEBUG > 1: sys.stderr.write("* bslReset(invokeBSL=%s)\n" % invokeBSL)
         self.SetRSTpin(1)       #power suply
@@ -574,8 +693,8 @@ class LowLevel:
             self.SetTESTpin(0)
             self.SetRSTpin(0)
             self.SetTESTpin(1)
-
         self.SetRSTpin(0)       #RST  pin: GND
+
         if invokeBSL:
             self.SetTESTpin(1)  #TEST pin: GND
             self.SetTESTpin(0)  #TEST pin: Vcc
@@ -773,6 +892,10 @@ class Memory:
                 sys.stderr.write("ELF section %s at 0x%04x %d bytes\n" % (section.name, section.lma, len(section.data)))
             if len(section.data):
                 self.segments.append( Segment(section.lma, section.data) )
+
+    def loadString(self, startAddr=0, string=None):
+       """fill memory with the contents of a binary chunk of data."""
+       self.segments.append(Segment(startAddr, string));
         
     def loadFile(self, filename):
         """fill memory with the contents of a file. file type is determined from extension"""
@@ -841,7 +964,39 @@ class BootStrapLoader(LowLevel):
         self.data           = None
         self.maxData        = self.MAXDATA
         self.cpu            = None
+       self.info           = None
 
+    def fetchinfo(self):
+        data=self.uploadData(0x1000,256);
+       return data;
+
+    def dumpinfo(self):
+       data = self.fetchinfo();
+        hex="@1000\n";
+        for c in data:
+            hex+=("%02x "%ord(c));
+        hex+="\nq\n";
+        print hex;
+       return data;
+
+    def saveinfo(self):
+       sys.stderr.write('Checking for info flash...')
+       sys.stderr.flush()
+       data = self.fetchinfo();
+       good_data = False;
+       for c in data:
+           if ord(c) is not 255:
+               good_data=True;
+               break;
+       if good_data:
+           sys.stderr.write('  Saved!\n')
+           sys.stderr.flush()
+           self.info = Memory();
+           self.info.loadString(0x1000,data);
+       else:
+           sys.stderr.write('  None.\n')
+           sys.stderr.write('Look at contrib/infos/README.txt for better performance.\n')
+           sys.stderr.flush()
 
     def preparePatch(self):
         """prepare to download patch"""
@@ -1136,22 +1291,33 @@ class BootStrapLoader(LowLevel):
             sys.stderr.flush()
         else:
             raise BSLException, "programming without data not possible"
+
+    def prepareFromweb(self):
+        """Grab GoodFET firmware from the web."""
+        url="%s%s.hex" % (FIRMWARE_BASEURL, self.board);
+        print "Grabbing %s firmware from %s" % (self.board, url);
+        fn="/tmp/.%s.hex" % self.board
+       try:
+           subprocess.call(['curl', '-sS', url, '-o', fn])
+       except OSError:
+           print "Failed to run curl, trying wget"
+           try:
+               subprocess.call(['wget', '-nv', url, '-O', fn])
+           except OSError:
+               print "Failed to fetch firmware.  Maybe you need to install curl or wget?"
+               sys.exit()
+
     def actionFromweb(self):
-        """Grab GoodFET firmware from the web, then flash it."""
-        print "Grabbing %x firmware." % self.dev_id;
-        print "%s" % firmware[self.dev_id];
-        fn="/tmp/.goodfet.hex"
-        os.system("curl %s >%s" % (firmware[self.dev_id],fn))
-        
+        """Flash the GoodFET firmware which has been downloaded in an earlier step."""
+        fn="/tmp/.%s.hex" % self.board
         fw=Memory(fn);
-        #fw.loadIhex(open(fn,"rb"));
         
         sys.stderr.write("Program ...\n")
         sys.stderr.flush()
         self.programData(fw, self.ACTION_PROGRAM | self.ACTION_VERIFY)
         sys.stderr.write("%i bytes programmed.\n" % self.byteCtr)
         sys.stderr.flush()
-        
+
     def actionVerify(self):
         """Verify programmed data"""
         if self.data is not None:
@@ -1242,7 +1408,7 @@ General options:
   -P, --password=file   Specify a file with the interrupt vectors that
                         are used as password. This can be any file that
                         has previously been used to program the device.
-                        (e.g. -P INT_VECT.TXT).
+                        (e.g. -P INT_VECT.TXT or -P goodfet.hex).
   -f, --framesize=num   Max. number of data bytes within one transmitted
                         frame (16 to 240 in steps of 16) (e.g. -f 240).
   -m, --erasecycles=num Number of mass erase cycles (default is 1). Some
@@ -1283,15 +1449,20 @@ General options:
                         --swap-reset-test, and --telos-latch
   --telosb              Implies options --swap-reset-test, --telos-i2c,
                         --no-BSL-download, and --speed=38400
+  --apimote             Implies --swap-reset-test
   --goodfet10
   --goodfet20
   --goodfet30
+  --goodthopter         Same as GF30.
   --tmote               Identical operation to --telosb
+  --z1                  Bootstrap a Z1
   --no-BSL-download     Do not download replacement BSL (disable automatic)
   --force-BSL-download  Download replacement BSL even if not needed (the one
                         in the device would have the required features)
   --slow                Add delays when operating the conrol pins. Useful if
                         the pins/circuit has high capacitance.
+  --dumpinfo           Print the info flash timing data so you can save a copy
+                        for later.  You must provide -P if flash is not        empty.
 
 Program Flow Specifiers:
   -e, --masserase       Mass Erase (clear all flash memory)
@@ -1361,6 +1532,7 @@ def hexify(line, bytes, width=16):
 #Main:
 def main(itest=1):
     global DEBUG
+    global board
     import getopt
     filetype    = None
     filename    = None
@@ -1371,7 +1543,7 @@ def main(itest=1):
     wait        = 0     #wait at the end
     goaddr      = None
     bsl         = BootStrapLoader()
-    toinit      = []
+    deviceinit  = []
     todo        = []
     startaddr   = None
     size        = 2
@@ -1385,32 +1557,7 @@ def main(itest=1):
     
     bsl.invertRST = 1
     bsl.invertTEST = itest
-    
-    if comPort is None and os.environ.get("GOODFET")!=None:
-        glob_list = glob.glob(os.environ.get("GOODFET"));
-        if len(glob_list) > 0:
-            comPort = glob_list[0];
-    if comPort is None:
-        glob_list = glob.glob("/dev/tty.usbserial*");
-        if len(glob_list) > 0:
-            comPort = glob_list[0];
-    if comPort is None:
-        glob_list = glob.glob("/dev/ttyUSB*");
-        if len(glob_list) > 0:
-            comPort = glob_list[0];
-    if os.name=='nt':
-            from scanwin32 import winScan;
-            scan=winScan();
-            for order,comport,desc,hwid in sorted(scan.comports()):
-                try:
-                    if hwid.index('FTDI')==0:
-                        comPort=comport;
-                        #print "Using FTDI port %s" % port
-                except:
-                    #Do nothing.
-                    a=1;
-    sys.stderr.write("MSP430 Bootstrap Loader Version: %s\n" % VERSION)
-
+   
     try:
         opts, args = getopt.getopt(sys.argv[1:],
             "hc:P:wf:m:eEpvrg:UDudsxbITNB:S:V14",
@@ -1423,7 +1570,9 @@ def main(itest=1):
              "swap-reset-test", "telos-latch", "telos-i2c", "telos", "telosb",
              "tmote","no-BSL-download", "force-BSL-download", "slow",
              "dumpivt", "dumpinfo", "fromweb",
-             "goodfet30", "goodfet20", "goodfet10"
+             "goodfet40", "goodfet30",  "goodthopter", "goodfet20", "goodfet10",
+             "z1",
+             "nhbadge", "nhbadgeb", "goodfet"
             ]
         )
     except getopt.GetoptError:
@@ -1478,13 +1627,14 @@ def main(itest=1):
             sys.stderr.write( "Number of mass erase cycles set to %i.\n" % meraseCycles)
             bsl.meraseCycles = meraseCycles
         elif o in ("-e", "--masserase"):
-            toinit.append(bsl.actionMassErase)        #Erase Flash
+            deviceinit.append(bsl.actionMassErase)        #Erase Flash
         elif o in ("-E", "--erasecheck"):
-            toinit.append(bsl.actionEraseCheck)       #Erase Check (by file)
+            deviceinit.append(bsl.actionEraseCheck)       #Erase Check (by file)
         elif o in ("-p", "--program"):
             todo.append(bsl.actionProgram)          #Program file
         elif o in ("--fromweb"):
-            toinit.append(bsl.actionMassErase)        #Erase Flash
+            deviceinit.append(bsl.prepareFromweb)         #Download firmware
+            deviceinit.append(bsl.actionMassErase)        #Erase Flash
             todo.append(bsl.actionFromweb)          #Program GoodFET code
         elif o in ("-v", "--verify"):
             todo.append(bsl.actionVerify)           #Verify file
@@ -1570,9 +1720,20 @@ def main(itest=1):
         elif o in ("--goodfet20", ):
             bsl.invertRST = 1
             bsl.invertTEST = 1
-        elif o in ("--goodfet30", ):
+        elif o in ("--goodfet30", "--goodfet31", "--goodthopter" ):
             bsl.invertRST = 1
             bsl.invertTEST = 0
+        elif o in ("--goodfet40", ):
+            bsl.invertRST = 1
+            bsl.invertTEST = 1
+        elif o in ("--goodfet", ):
+            bsl.invertRST = 1
+            bsl.invertTEST = 1
+        elif o in ("--apimote",):
+            bsl.swapRSTTEST = 1;
+        elif o in ("--nhbadge", "--nhbadgeb" ):
+            bsl.invertRST = 1
+            bsl.invertTEST = 1
         elif o in ("--telosb", ):
             bsl.swapRSTTEST = 1
             bsl.telosI2C = 1
@@ -1583,6 +1744,9 @@ def main(itest=1):
             bsl.telosI2C = 1
             mayuseBSL = 0
             speed = 38400
+        elif o in ("--z1", ):
+            bsl.z1 = 1
+            speed = 38400
         elif o in ("--no-BSL-download", ):
             mayuseBSL = 0
         elif o in ("--force-BSL-download", ):
@@ -1625,6 +1789,58 @@ def main(itest=1):
         sys.stderr.write("Warning: option --reset ignored as --upload is specified!\n")
         reset = 0
 
+
+    if os.environ.get("board")==None:
+      if board==None:
+       print "Board not specified.  Defaulting to goodfet41.";
+       raw_input("Press Ctrl+C to cancel, or Enter to continue.");
+       board='goodfet41';
+      bsl.board=board;
+    else:
+      bsl.board=None;
+      try:
+       bsl.board=BOARDS[os.environ.get("board").lower()];
+      except:
+        pass;
+    if bsl.board==None:
+      print "Unknown board specified.  Try goodfet41, facedancer11, or similar.";
+      sys.exit(2);
+      
+    if bsl.board=='telosb':
+        bsl.swapRSTTEST = 1
+        bsl.telosI2C = 1
+        mayuseBSL = 0
+    if bsl.board=='z1':
+        bsl.z1 = 1
+    if bsl.board=='apimote':
+        bsl.swapRSTTEST = 1;
+    
+    
+    if comPort is None and os.environ.get("GOODFET")!=None:
+        glob_list = glob.glob(os.environ.get("GOODFET"));
+        if len(glob_list) > 0:
+            comPort = glob_list[0];
+    if comPort is None:
+        glob_list = glob.glob("/dev/tty.usbserial*");
+        if len(glob_list) > 0:
+            comPort = glob_list[0];
+    if comPort is None:
+        glob_list = glob.glob("/dev/ttyUSB*");
+        if len(glob_list) > 0:
+            comPort = glob_list[0];
+    if os.name=='nt':
+            from scanwin32 import winScan;
+            scan=winScan();
+            for order,comport,desc,hwid in sorted(scan.comports()):
+                try:
+                    if hwid.index('FTDI')==0:
+                        comPort=comport;
+                        #print "Using FTDI port %s" % port
+                except:
+                    #Do nothing.
+                    a=1;
+    sys.stderr.write("MSP430 Bootstrap Loader Version: %s\n" % VERSION)
+
     sys.stderr.flush()
     
     #prepare data to download
@@ -1652,12 +1868,16 @@ def main(itest=1):
 
     bsl.comInit(comPort)                            #init port
 
+    if bsl.actionMassErase in deviceinit:
+        bsl.actionStartBSL()
+       bsl.saveinfo()
+
     #initialization list
-    if toinit:  #erase and erase check
+    if deviceinit:  #erase and erase check
         if DEBUG: sys.stderr.write("Preparing device ...\n")
         #bsl.actionStartBSL(usepatch=0, adjsp=0)     #no workarounds needed
         #if speed: bsl.actionChangeBaudrate(speed)   #change baud rate as fast as possible
-        for f in toinit: f()
+        for f in deviceinit: f()
 
     if todo or goaddr or startaddr:
         if DEBUG: sys.stderr.write("Actions ...\n")
@@ -1682,6 +1902,11 @@ def main(itest=1):
                     sys.stderr.write("   %r\n" % f)
         for f in todo: f()                          #work through todo list
 
+    if bsl.info is not None:
+       sys.stderr.write('Programming info flash...\n')
+       sys.stderr.flush()
+       bsl.programData(bsl.info, bsl.ACTION_PROGRAM)
+
     if reset:                                       #reset device first if desired
         bsl.actionReset()
     if dumpivt:
@@ -1692,13 +1917,13 @@ def main(itest=1):
             hex+=("%02x "%ord(c));
         print hex;
     if dumpinfo:
-        bsl.txPasswd(); #default pass
-        data=bsl.uploadData(0x1000,256);
-        hex="@1000\n";
-        for c in data:
-            hex+=("%02x "%ord(c));
-        hex+="\nq\n";
-        print hex;
+       # I don't know what bslreset is all about, but if it is enabled and
+       # the wrong password is provided, the chip gets erased.
+       reset = True
+       if not bsl.passwd:
+           reset = False
+        bsl.actionStartBSL(bslreset=reset)
+        bsl.dumpinfo()
     
     if goaddr is not None:                          #start user programm at specified address
         bsl.actionRun(goaddr)                       #load PC and execute