#!/usr/bin/python2 ## This is a quick and dirty BSL client by Travis Goodspeed for the ## CC430F6137, written out of frustration when another tool generated ## bad checksums. It is free for any use, provided you tip your ## bartender. import serial, time, sys, argparse; class BSL: def __init__(self, port): print("Opening %s" % port); self.serial=serial.Serial(port, baudrate=9600, parity=serial.PARITY_EVEN, stopbits=serial.STOPBITS_ONE, timeout=1); #Low level functions first. def setTST(self,level): """Sets the TST pin.""" self.serial.setRTS(level) time.sleep(0.01); def setRST(self,level): """Sets the !RST pin.""" self.serial.setDTR(level); time.sleep(0.01); def enter_bsl(self): """Activates the bootloader by the sequence in SLAU319N.""" self.setRST(False) self.setTST(False) time.sleep(0.250) self.setRST(True) #Minimum two rising edges are required, but it helps to have three or more. self.setTST(False) self.setTST(True) self.setTST(False) self.setTST(True) self.setTST(False) self.setTST(True) self.setTST(False) self.setTST(True) self.setRST(False) self.setTST(False) time.sleep(0.250) self.serial.flushInput(); def reset(self): """Exits the BSL by resetting the chip.""" self.setTST(True) self.setRST(True); self.setRST(False); self.setRST(True); def crc(self,msg): """Returns a two-byte string of the checksum of a message.""" crc=0xFFFF #msg should already include header bytes. for char in msg: byte=ord(char) x=((crc>>8)^byte)&0xFF; x^=x>>4; crc=(crc<<8)^(x<<12)^(x<<5)^x; return chr(crc&0xFF)+""+chr((crc>>8)&0xFF); def transact(self,msg): """Sends a message, wrapped with a prefix and checksum. Result's wrapper is stripped.""" #Send the message. length=len(msg); ll=chr(length&0xFF); lh=chr((length>>8)&0xFF); crc=self.crc(msg); self.serial.write("\x80"+ll+lh+msg+crc); #Get the reply. reply=self.serial.read(1); if len(reply)!=1: print "Error, missing reply."; sys.exit(1); elif ord(reply[0])==0x00: #Success eighty=ord(self.serial.read(1)); ll=ord(self.serial.read(1)[0]); lh=ord(self.serial.read(1)[0]); length=ll|(lh<<8); rep=self.serial.read(length); crc=self.serial.read(2); assert(crc==self.crc(rep)); return rep; else: print "Error 0x%02x." % ord(reply[0]); #Not sure whether data is coming, so grab a chunk just in case. self.serial.read(10); def unlock(self, password="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"): """Unlocks the bootloader, optionally with a password.""" #Password must be 32 bytes. assert(len(password)==32); resp=self.transact("\x11"+password); assert(len(resp)==2); assert(resp[0]=='\x3b'); code=ord(resp[1]); if code==0x05: print "Incorrect password. Flash was erased." return self.unlock(); elif code==0x00: #success return True; else: print "Unexpected password core message 0x%02x."%code; return False; def version(self): """Gets the BSL version and related bytes.""" resp=self.transact("\x19"); assert(len(resp)==5); vendor=ord(resp[1]); # 0 for TI interpreter=ord(resp[2]); api=ord(resp[3]); # 0 for flash, 30 for sram, 80 for restricted cmd set peripheral=ord(resp[4]); return vendor, interpreter, api, peripheral; def masserase(self): """Bulk erases the device.""" resp=self.transact("\x15"); assert resp=="\x3b\x00" def read(self,adr,length=32): """Dumps memory from the given address.""" al=chr(adr&0xFF) am=chr((adr>>8)&0xFF) ah=chr((adr>>16)&0xFF) ll=chr(length&0xFF) lh=chr((length>>8)&0xFF) resp=self.transact("\x18"+al+am+ah+ll+lh) if resp[0]=="\x3b": print "Error: You need to unlock before reading." assert(resp[0]=="\x3a"); return resp[1:] def write(self,adr,data=""): """Writes memory to the given address.""" al=chr(adr&0xFF) am=chr((adr>>8)&0xFF) ah=chr((adr>>16)&0xFF) resp=self.transact("\x10"+al+am+ah+data) return resp[1:] MAXLEN=256; #Maximum bytes per read request. def readbulk(self,adr,length): """Reads a large volume from the target, in multiple transactions.""" i=adr; buf=""; while i