From 5b3d7fadcf177daa1c4f28b3a35d924c70d66cf9 Mon Sep 17 00:00:00 2001 From: PeterKietzmann Date: Tue, 30 Aug 2016 18:41:59 +0200 Subject: [PATCH 1/2] dist/tools: update serial boot loader script --- dist/tools/cc2538-bsl/cc2538-bsl.py | 590 +++++++++++++++++++++++----- 1 file changed, 488 insertions(+), 102 deletions(-) mode change 100755 => 100644 dist/tools/cc2538-bsl/cc2538-bsl.py diff --git a/dist/tools/cc2538-bsl/cc2538-bsl.py b/dist/tools/cc2538-bsl/cc2538-bsl.py old mode 100755 new mode 100644 index ec3d3a3e3eb1..1adb1256cae3 --- a/dist/tools/cc2538-bsl/cc2538-bsl.py +++ b/dist/tools/cc2538-bsl/cc2538-bsl.py @@ -29,7 +29,7 @@ # Implementation based on stm32loader by Ivan A-R -# Serial boot loader over UART for CC2538 +# Serial boot loader over UART for CC13xx / CC2538 / CC26xx # Based on the info found in TI's swru333a.pdf (spma029.pdf) # # Bootloader only starts if no valid image is found or if boot loader @@ -48,9 +48,22 @@ import subprocess import struct import binascii +import traceback + +try: + import magic + have_magic = True +except ImportError: + have_magic = False + +try: + from intelhex import IntelHex + have_hex_support = True +except ImportError: + have_hex_support = False #version -VERSION_STRING = "1.0" +VERSION_STRING = "2.1" # Verbose level QUIET = 5 @@ -93,12 +106,91 @@ def mdebug(level, message, attr='\n'): COMMAND_RET_INVALID_ADR = 0x43 COMMAND_RET_FLASH_FAIL = 0x44 -ADDR_IEEE_ADDRESS_SECONDARY = 0x0027ffcc - class CmdException(Exception): pass +class FirmwareFile(object): + HEX_FILE_EXTENSIONS = ('hex', 'ihx', 'ihex') + + def __init__(self, path): + """ + Read a firmware file and store its data ready for device programming. + + This class will try to guess the file type if python-magic is available. + + If python-magic indicates a plain text file, and if IntelHex is + available, then the file will be treated as one of Intel HEX format. + + In all other cases, the file will be treated as a raw binary file. + + In both cases, the file's contents are stored in bytes for subsequent + usage to program a device or to perform a crc check. + + Parameters: + path -- A str with the path to the firmware file. + + Attributes: + bytes: A bytearray with firmware contents ready to send to the device + """ + self._crc32 = None + firmware_is_hex = False + + if have_magic: + file_type = bytearray(magic.from_file(path, True)) + + #from_file() returns bytes with PY3, str with PY2. This comparison + #will be True in both cases""" + if file_type == b'text/plain': + firmware_is_hex = True + mdebug(5, "Firmware file: Intel Hex") + elif file_type == b'application/octet-stream': + mdebug(5, "Firmware file: Raw Binary") + else: + error_str = "Could not determine firmware type. Magic " \ + "indicates '%s'" % (file_type) + raise CmdException(error_str) + else: + if os.path.splitext(path)[1][1:] in self.HEX_FILE_EXTENSIONS: + firmware_is_hex = True + mdebug(5, "Your firmware looks like an Intel Hex file") + else: + mdebug(5, "Cannot auto-detect firmware filetype: Assuming .bin") + + mdebug(10, "For more solid firmware type auto-detection, install " + "python-magic.") + mdebug(10, "Please see the readme for more details.") + + if firmware_is_hex: + if have_hex_support: + self.bytes = bytearray(IntelHex(path).tobinarray()) + return + else: + error_str = "Firmware is Intel Hex, but the IntelHex library " \ + "could not be imported.\n" \ + "Install IntelHex in site-packages or program " \ + "your device with a raw binary (.bin) file.\n" \ + "Please see the readme for more details." + raise CmdException(error_str) + + with open(path, 'rb') as f: + self.bytes = bytearray(f.read()) + + def crc32(self): + """ + Return the crc32 checksum of the firmware image + + Return: + The firmware's CRC32, ready for comparison with the CRC + returned by the ROM bootloader's COMMAND_CRC32 + """ + if self._crc32 is None: + self._crc32 = binascii.crc32(bytearray(self.bytes)) & 0xffffffff + + return self._crc32 + class CommandInterface(object): + ACK_BYTE = 0xCC + NACK_BYTE = 0x33 def open(self, aport='/dev/tty.usbserial-000013FAB', abaudrate=500000): self.sp = serial.Serial( port=aport, @@ -111,40 +203,66 @@ def open(self, aport='/dev/tty.usbserial-000013FAB', abaudrate=500000): timeout=0.5 # set a timeout value, None for waiting forever ) - # Use the DTR and RTS lines to control !RESET and the bootloader pin. + def invoke_bootloader(self, dtr_active_high=False, inverted=False): + # Use the DTR and RTS lines to control bootloader and the !RESET pin. # This can automatically invoke the bootloader without the user # having to toggle any pins. + # + # If inverted is False (default): # DTR: connected to the bootloader pin # RTS: connected to !RESET - self.sp.setDTR(1) - self.sp.setRTS(0) - self.sp.setRTS(1) - self.sp.setRTS(0) - self.sp.setDTR(0) + # If inverted is True, pin connections are the other way round + if inverted: + set_bootloader_pin = self.sp.setRTS + set_reset_pin = self.sp.setDTR + else: + set_bootloader_pin = self.sp.setDTR + set_reset_pin = self.sp.setRTS + + set_bootloader_pin(1 if not dtr_active_high else 0) + set_reset_pin(0) + set_reset_pin(1) + set_reset_pin(0) + time.sleep(0.002) # Make sure the pin is still asserted when the chip + # comes out of reset. This fixes an issue where there + # wasn't enough delay here on Mac. + set_bootloader_pin(0 if not dtr_active_high else 1) + + # Some boards have a co-processor that detects this sequence here and + # then drives the main chip's BSL enable and !RESET pins. Depending on + # board design and co-processor behaviour, the !RESET pin may get + # asserted after we have finished the sequence here. In this case, we + # need a small delay so as to avoid trying to talk to main chip before + # it has actually entered its bootloader mode. + # + # See contiki-os/contiki#1533 + time.sleep(0.1) def close(self): self.sp.close() - def _wait_for_ack(self, info="", timeout=0): + def _wait_for_ack(self, info = "", timeout = 1): stop = time.time() + timeout - got = None - while not got: - got = self._read(2) + got = bytearray(2) + while got[-2] != 00 or got[-1] not in (CommandInterface.ACK_BYTE, + CommandInterface.NACK_BYTE): + got += self._read(1) if time.time() > stop: - break + raise CmdException("Timeout waiting for ACK/NACK after '%s'" + % (info,)) - if not got: - mdebug(10, "No response to %s" % info) - return 0 + # Our bytearray's length is: 2 initial bytes + 2 bytes for the ACK/NACK + # plus a possible N-4 additional (buffered) bytes + mdebug(10, "Got %d additional bytes before ACK/NACK" % (len(got) - 4,)) # wait for ask - ask = got[1] + ask = got[-1] - if ask == 0xCC: + if ask == CommandInterface.ACK_BYTE: # ACK return 1 - elif ask == 0x33: + elif ask == CommandInterface.NACK_BYTE: # NACK mdebug(10, "Target replied with a NACK during %s" % info) return 0 @@ -172,33 +290,46 @@ def _calc_checks(self, cmd, addr, size): +cmd) &0xFF) - def _write(self, data): + def _write(self, data, is_retry=False): if PY3: if type(data) == int: - self.sp.write(bytes([data])) + assert data < 256 + goal = 1 + written = self.sp.write(bytes([data])) elif type(data) == bytes or type(data) == bytearray: - self.sp.write(data) + goal = len(data) + written = self.sp.write(data) + else: + raise CmdException("Internal Error. Bad data type: {}".format(type(data))) else: if type(data) == int: - self.sp.write(chr(data)) + assert data < 256 + goal = 1 + written = self.sp.write(chr(data)) else: - self.sp.write(data) + goal = len(data) + written = self.sp.write(data) + if written < goal: + mdebug(10, "*** Only wrote {} of target {} bytes".format(written, goal)) + if is_retry and written == 0: + raise CmdException("Failed to write data on the serial bus") + mdebug(10, "*** Retrying write for remainder") + if type(data) == int: + return self._write(data, is_retry=True) + else: + return self._write(data[written:], is_retry=True) def _read(self, length): - got = self.sp.read(length) - if PY3: - return got - else: - return [ord(x) for x in got] + return bytearray(self.sp.read(length)) def sendAck(self): - self._write(chr(0x00)) + self._write(0x00) self._write(0xCC) return def sendNAck(self): - self._write(chr(0x00)) - self._write(chr(0x33)) + self._write(0x00) + self._write(0x33) return @@ -215,7 +346,7 @@ def receivePacket(self): size = got[0] #rcv size chks = got[1] #rcv checksum - data = self._read(size-2) # rcv data + data = bytearray(self._read(size - 2)) # rcv data mdebug(10, "*** received %x bytes" % size) if chks == sum(data)&0xFF: @@ -235,7 +366,7 @@ def sendSynch(self): mdebug(10, "*** sending synch sequence") self._write(cmd) # send U self._write(cmd) # send U - return self._wait_for_ack("Synch (0x55 0x55)") + return self._wait_for_ack("Synch (0x55 0x55)", 2) def checkLastCmd(self): stat = self.cmdGetStatus() @@ -246,11 +377,11 @@ def checkLastCmd(self): mdebug(10, "Command Successful") return 1 else: - stat_str = RETURN_CMD_STRS.get(stat, None) + stat_str = RETURN_CMD_STRS.get(stat[0], None) if stat_str is None: - mdebug(0, 'Warning: unrecognized status returned 0x%x' % stat) + mdebug(0, 'Warning: unrecognized status returned 0x%x' % stat[0]) else: - mdebug(0, "Target returned: 0x%x, %s" % (stat, stat_str)) + mdebug(0, "Target returned: 0x%x, %s" % (stat[0], stat_str)) return 0 @@ -291,6 +422,7 @@ def cmdGetChipId(self): version = self.receivePacket() # 4 byte answ, the 2 LSB hold chip ID if self.checkLastCmd(): assert len(version) == 4, "Unreasonable chip id: %s" % repr(version) + mdebug(10, " Version 0x%02X%02X%02X%02X" % tuple(version)) chip_id = (version[2] << 8) | version[3] return chip_id else: @@ -348,6 +480,18 @@ def cmdEraseMemory(self, addr, size): if self._wait_for_ack("Erase memory (0x26)",10): return self.checkLastCmd() + def cmdBankErase(self): + cmd = 0x2C + lng = 3 + + self._write(lng) # send length + self._write(cmd) # send checksum + self._write(cmd) # send cmd + + mdebug(10, "*** Bank Erase command(0x2C)") + if self._wait_for_ack("Bank Erase (0x2C)",10): + return self.checkLastCmd() + def cmdCRC32(self, addr, size): cmd=0x27 lng=11 @@ -364,6 +508,23 @@ def cmdCRC32(self, addr, size): if self.checkLastCmd(): return self._decode_addr(crc[3],crc[2],crc[1],crc[0]) + def cmdCRC32CC26xx(self, addr, size): + cmd = 0x27 + lng = 15 + + self._write(lng) # send length + self._write(self._calc_checks(cmd, addr, size)) # send checksum + self._write(cmd) # send cmd + self._write(self._encode_addr(addr)) # send addr + self._write(self._encode_addr(size)) # send size + self._write(self._encode_addr(0x00000000)) # send number of reads + + mdebug(10, "*** CRC32 command(0x27)") + if self._wait_for_ack("Get CRC32 (0x27)", 1): + crc=self.receivePacket() + if self.checkLastCmd(): + return self._decode_addr(crc[3], crc[2], crc[1], crc[0]) + def cmdDownload(self, addr, size): cmd=0x21 lng=11 @@ -411,6 +572,23 @@ def cmdMemRead(self, addr): # untested if self.checkLastCmd(): return data # self._decode_addr(ord(data[3]),ord(data[2]),ord(data[1]),ord(data[0])) + def cmdMemReadCC26xx(self, addr): + cmd = 0x2A + lng = 9 + + self._write(lng) # send length + self._write(self._calc_checks(cmd, addr, 2)) # send checksum + self._write(cmd) # send cmd + self._write(self._encode_addr(addr)) # send addr + self._write(1) # send width, 4 bytes + self._write(1) # send number of reads + + mdebug(10, "*** Mem Read (0x2A)") + if self._wait_for_ack("Mem Read (0x2A)", 1): + data = self.receivePacket() + if self.checkLastCmd(): + return data + def cmdMemWrite(self, addr, data, width): # untested # TODO: check width for 1 or 4 and data size cmd=0x2B @@ -433,22 +611,19 @@ def cmdMemWrite(self, addr, data, width): # untested def writeMemory(self, addr, data): lng = len(data) trsf_size = 248 # amount of data bytes transferred per packet (theory: max 252 + 3) - if PY3: - empty_packet = b'\xff'*trsf_size # empty packet (filled with 0xFF) - else: - empty_packet = [255]*trsf_size # empty packet (filled with 0xFF) + empty_packet = bytearray((0xFF,) * trsf_size) # Boot loader enable check # TODO: implement check for all chip sizes & take into account partial firmware uploads if (lng == 524288): #check if file is for 512K model if not ((data[524247] & (1 << 4)) >> 4): #check the boot loader enable bit (only for 512K model) - if not query_yes_no("The boot loader backdoor is not enabled "\ + if not ( conf['force'] or query_yes_no("The boot loader backdoor is not enabled "\ "in the firmware you are about to write to the target. "\ "You will NOT be able to reprogram the target using this tool if you continue! "\ - "Do you want to continue?","no"): + "Do you want to continue?","no") ): raise Exception('Aborted by user.') - mdebug(5, "Writing %(lng)d bytes starting at address 0x%(addr)X" % + mdebug(5, "Writing %(lng)d bytes starting at address 0x%(addr)08X" % { 'lng': lng, 'addr': addr}) offs = 0 @@ -459,8 +634,7 @@ def writeMemory(self, addr, data): if addr_set != 1: self.cmdDownload(addr,lng) #set starting address if not set addr_set = 1 - - mdebug(5, " Write %(len)d bytes at 0x%(addr)X" % {'addr': addr, 'len': trsf_size}, '\r') + mdebug(5, " Write %(len)d bytes at 0x%(addr)08X" % {'addr': addr, 'len': trsf_size}, '\r') sys.stdout.flush() self.cmdSendData(data[offs:offs+trsf_size]) # send next data packet @@ -471,10 +645,203 @@ def writeMemory(self, addr, data): addr = addr + trsf_size lng = lng - trsf_size - mdebug(5, "Write %(len)d bytes at 0x%(addr)X" % {'addr': addr, 'len': lng}, '\r') + mdebug(5, "Write %(len)d bytes at 0x%(addr)08X" % {'addr': addr, 'len': lng}) self.cmdDownload(addr,lng) return self.cmdSendData(data[offs:offs+lng]) # send last data packet +class Chip(object): + def __init__(self, command_interface): + self.command_interface = command_interface + + # Some defaults. The child can override. + self.flash_start_addr = 0x00000000 + self.has_cmd_set_xosc = False + + def crc(self, address, size): + return getattr(self.command_interface, self.crc_cmd)(address, size) + + def disable_bootloader(self): + if not (conf['force'] or query_yes_no("Disabling the bootloader will prevent you from "\ + "using this script until you re-enable the bootloader "\ + "using JTAG. Do you want to continue?", "no")): + raise Exception('Aborted by user.') + + if PY3: + pattern = struct.pack('> 4 + if 0 < self.size <= 4: + self.size *= 0x20000 # in bytes + else: + self.size = 0x10000 # in bytes + self.bootloader_address = self.flash_start_addr + self.size - ccfg_len + + sram = (((model[2] << 8) | model[3]) & 0x380) >> 7 + sram = (2 - sram) << 3 if sram <= 1 else 32 # in KB + + pg = self.command_interface.cmdMemRead(FLASH_CTRL_DIECFG2) + pg_major = (pg[2] & 0xF0) >> 4 + if pg_major == 0: + pg_major = 1 + pg_minor = pg[2] & 0x0F + + ti_oui = bytearray([0x00, 0x12, 0x4B]) + ieee_addr = self.command_interface.cmdMemRead(addr_ieee_address_primary) + ieee_addr_end = self.command_interface.cmdMemRead(addr_ieee_address_primary + 4) + if ieee_addr[:3] == ti_oui: + ieee_addr += ieee_addr_end + else: + ieee_addr = ieee_addr_end + ieee_addr + + mdebug(5, "CC2538 PG%d.%d: %dKB Flash, %dKB SRAM, CCFG at 0x%08X" + % (pg_major, pg_minor, self.size >> 10, sram, + self.bootloader_address)) + mdebug(5, "Primary IEEE Address: %s" % (':'.join('%02X' % x for x in ieee_addr))) + + def erase(self): + mdebug(5, "Erasing %s bytes starting at address 0x%08X" % (self.size, self.flash_start_addr)) + return self.command_interface.cmdEraseMemory(self.flash_start_addr, self.size) + + def read_memory(self, addr): + # CC2538's COMMAND_MEMORY_READ sends each 4-byte number in inverted + # byte order compared to what's written on the device + data = self.command_interface.cmdMemRead(addr) + return bytearray([data[x] for x in range(3, -1, -1)]) + +class CC26xx(Chip): + # Class constants + MISC_CONF_1 = 0x500010A0 + PROTO_MASK_BLE = 0x01 + PROTO_MASK_IEEE = 0x04 + PROTO_MASK_BOTH = 0x05 + + def __init__(self, command_interface): + super(CC26xx, self).__init__(command_interface) + self.bootloader_dis_val = 0x00000000 + self.crc_cmd = "cmdCRC32CC26xx" + + ICEPICK_DEVICE_ID = 0x50001318 + FCFG_USER_ID = 0x50001294 + PRCM_RAMHWOPT = 0x40082250 + FLASH_SIZE = 0x4003002C + addr_ieee_address_primary = 0x500012F0 + ccfg_len = 88 + ieee_address_secondary_offset = 0x20 + bootloader_dis_offset = 0x30 + sram = "Unknown" + + # Determine CC13xx vs CC26xx via ICEPICK_DEVICE_ID::WAFER_ID and store + # PG revision + device_id = self.command_interface.cmdMemReadCC26xx(ICEPICK_DEVICE_ID) + wafer_id = (((device_id[3] & 0x0F) << 16) + + (device_id[2] << 8) + + (device_id[1] & 0xF0)) >> 4 + pg_rev = (device_id[3] & 0xF0) >> 4 + + # Read FCFG1_USER_ID to get the package and supported protocols + user_id = self.command_interface.cmdMemReadCC26xx(FCFG_USER_ID) + package = {0x00: '4x4mm', 0x01: '5x5mm', 0x02: '7x7mm'}.get(user_id[2] & 0x03, "Unknown") + protocols = user_id[1] >> 4 + + # We can now detect the exact device + if wafer_id == 0xB99A: + chip = self._identify_cc26xx(pg_rev, protocols) + elif wafer_id == 0xB9BE: + chip = self._identify_cc13xx(pg_rev, protocols) + + # Read flash size, calculate and store bootloader disable address + self.size = self.command_interface.cmdMemReadCC26xx(FLASH_SIZE)[0] * 4096 + self.bootloader_address = self.size - ccfg_len + bootloader_dis_offset + self.addr_ieee_address_secondary = self.size - ccfg_len + ieee_address_secondary_offset + + # RAM size + ramhwopt_size = self.command_interface.cmdMemReadCC26xx(PRCM_RAMHWOPT)[0] & 3 + if ramhwopt_size == 3: + sram = "20KB" + elif ramhwopt_size == 2: + sram = "16KB" + else: + sram = "Unknown" + + # Primary IEEE address. Stored with the MSB at the high address + ieee_addr = self.command_interface.cmdMemReadCC26xx(addr_ieee_address_primary + 4)[::-1] + ieee_addr += self.command_interface.cmdMemReadCC26xx(addr_ieee_address_primary)[::-1] + + mdebug(5, "%s (%s): %dKB Flash, %s SRAM, CCFG.BL_CONFIG at 0x%08X" + % (chip, package, self.size >> 10, sram, + self.bootloader_address)) + mdebug(5, "Primary IEEE Address: %s" % (':'.join('%02X' % x for x in ieee_addr))) + + def _identify_cc26xx(self, pg, protocols): + chips_dict = { + CC26xx.PROTO_MASK_IEEE: 'CC2630', + CC26xx.PROTO_MASK_BLE: 'CC2640', + CC26xx.PROTO_MASK_BOTH: 'CC2650', + } + + chip_str = chips_dict.get(protocols & CC26xx.PROTO_MASK_BOTH, "Unknown") + + if pg == 1: + pg_str = "PG1.0" + elif pg == 3: + pg_str = "PG2.0" + elif pg == 7: + pg_str = "PG2.1" + elif pg == 8: + rev_minor = self.command_interface.cmdMemReadCC26xx(CC26xx.MISC_CONF_1)[0] + if rev_minor == 0xFF: + rev_minor = 0x00 + pg_str = "PG2.%d" % (2 + rev_minor,) + + return "%s %s" % (chip_str, pg_str) + + def _identify_cc13xx(self, pg, protocols): + chip_str = "CC1310" + if protocols & CC26xx.PROTO_MASK_IEEE == CC26xx.PROTO_MASK_IEEE: + chip_str = "CC1350" + + if pg == 0: + pg_str = "PG1.0" + elif pg == 2: + rev_minor = self.command_interface.cmdMemReadCC26xx(CC26xx.MISC_CONF_1)[0] + if rev_minor == 0xFF: + rev_minor = 0x00 + pg_str = "PG2.%d" % (rev_minor,) + + return "%s %s" % (chip_str, pg_str) + + def erase(self): + mdebug(5, "Erasing all main bank flash sectors") + return self.command_interface.cmdBankErase() + + def read_memory(self, addr): + # CC26xx COMMAND_MEMORY_READ returns contents in the same order as + # they are stored on the device + return self.command_interface.cmdMemReadCC26xx(addr) + def query_yes_no(question, default="yes"): valid = {"yes":True, "y":True, "ye":True, "no":False, "n":False} @@ -535,10 +902,11 @@ def print_version(): print('%s %s' % (sys.argv[0], version)) def usage(): - print("""Usage: %s [-hqVewvr] [-l length] [-p port] [-b baud] [-a addr] [-i addr] [file.bin] - -h This help + print("""Usage: %s [-DhqVfewvr] [-l length] [-p port] [-b baud] [-a addr] [-i addr] [--bootloader-active-high] [--bootloader-invert-lines] [file.bin] + -h, --help This help -q Quiet -V Verbose + -f Force operation(s) without asking any questions -e Erase (full) -w Write -v Verify (CRC32 check) @@ -548,6 +916,9 @@ def usage(): -b baud Baud speed (default: 500000) -a addr Target address -i, --ieee-address addr Set the secondary 64 bit IEEE address + --bootloader-active-high Use active high signals to enter bootloader + --bootloader-invert-lines Inverts the use of RTS and DTR to enter bootloader + -D, --disable-bootloader After finishing, disable the bootloader --version Print script version Examples: @@ -556,22 +927,14 @@ def usage(): """ % (sys.argv[0],sys.argv[0],sys.argv[0])) -def read(filename): - """Read the file to be programmed and turn it into a binary""" - with open(filename, 'rb') as f: - bytes = f.read() - if PY3: - return bytes - else: - return [ord(x) for x in bytes] - if __name__ == "__main__": conf = { 'port': 'auto', 'baud': 500000, 'force_speed' : 0, - 'address': 0x00200000, + 'address': None, + 'force': 0, 'erase': 0, 'write': 0, 'verify': 0, @@ -579,12 +942,15 @@ def read(filename): 'len': 0x80000, 'fname':'', 'ieee_address': 0, + 'bootloader_active_high': False, + 'bootloader_invert_lines' : False, + 'disable-bootloader': 0 } # http://www.python.org/doc/2.5.2/lib/module-getopt.html try: - opts, args = getopt.getopt(sys.argv[1:], "hqVewvrp:b:a:l:i:", ['ieee-address=', 'version']) + opts, args = getopt.getopt(sys.argv[1:], "DhqVfewvrp:b:a:l:i:", ['help', 'ieee-address=', 'disable-bootloader', 'bootloader-active-high', 'bootloader-invert-lines', 'version']) except getopt.GetoptError as err: # print help information and exit: print(str(err)) # will print something like "option -a not recognized" @@ -596,9 +962,11 @@ def read(filename): QUIET = 10 elif o == '-q': QUIET = 0 - elif o == '-h': + elif o == '-h' or o == '--help': usage() sys.exit(0) + elif o == '-f': + conf['force'] = 1 elif o == '-e': conf['erase'] = 1 elif o == '-w': @@ -618,6 +986,12 @@ def read(filename): conf['len'] = eval(a) elif o == '-i' or o == '--ieee-address': conf['ieee_address'] = str(a) + elif o == '--bootloader-active-high': + conf['bootloader_active_high'] = True + elif o == '--bootloader-invert-lines': + conf['bootloader_invert_lines'] = True + elif o == '-D' or o == '--disable-bootloader': + conf['disable-bootloader'] = 1 elif o == '--version': print_version() sys.exit(0) @@ -633,23 +1007,27 @@ def read(filename): raise Exception('No file path given.') if conf['write'] and conf['read']: - if not query_yes_no("You are reading and writing to the same file. This will overwrite your input file. "\ - "Do you want to continue?","no"): + if not ( conf['force'] or query_yes_no("You are reading and writing to the same file. This will overwrite your input file. "\ + "Do you want to continue?","no") ): raise Exception('Aborted by user.') if conf['erase'] and conf['read'] and not conf['write']: - if not query_yes_no("You are about to erase your target before reading. "\ - "Do you want to continue?","no"): + if not ( conf['force'] or query_yes_no("You are about to erase your target before reading. "\ + "Do you want to continue?","no") ): raise Exception('Aborted by user.') if conf['read'] and not conf['write'] and conf['verify']: raise Exception('Verify after read not implemented.') + if conf['len'] < 0: + raise Exception('Length must be positive but %d was provided' + % (conf['len'],)) + # Try and find the port automatically if conf['port'] == 'auto': ports = [] # Get a list of all USB-like names in /dev - for name in ['tty.usbserial', 'ttyUSB', 'tty.usbmodem']: + for name in ['tty.usbserial', 'ttyUSB', 'tty.usbmodem', 'tty.SLAB_USBtoUART']: ports.extend(glob.glob('/dev/%s*' % name)) ports = sorted(ports) @@ -662,29 +1040,18 @@ def read(filename): cmd = CommandInterface() cmd.open(conf['port'], conf['baud']) + cmd.invoke_bootloader(conf['bootloader_active_high'], conf['bootloader_invert_lines']) mdebug(5, "Opening port %(port)s, baud %(baud)d" % {'port':conf['port'], 'baud':conf['baud']}) if conf['write'] or conf['verify']: mdebug(5, "Reading data from %s" % args[0]) - data = read(args[0]) + firmware = FirmwareFile(args[0]) mdebug(5, "Connecting to target...") if not cmd.sendSynch(): raise CmdException("Can't connect to target. Ensure boot loader is started. (no answer on synch sequence)") - if conf['force_speed'] != 1: - if cmd.cmdSetXOsc(): #switch to external clock source - cmd.close() - conf['baud']=1000000 - cmd.open(conf['port'], conf['baud']) - mdebug(6, "Opening port %(port)s, baud %(baud)d" % {'port':conf['port'],'baud':conf['baud']}) - mdebug(6, "Reconnecting to target at higher speed...") - if (cmd.sendSynch()!=1): - raise CmdException("Can't connect to target after clock source switch. (Check external crystal)") - else: - raise CmdException("Can't switch target to external clock source. (Try forcing speed)") - # if (cmd.cmdPing() != 1): # raise CmdException("Can't connect to target. Ensure boot loader is started. (no answer on ping command)") @@ -692,24 +1059,38 @@ def read(filename): chip_id_str = CHIP_ID_STRS.get(chip_id, None) if chip_id_str is None: - mdebug(0, 'Warning: unrecognized chip ID 0x%x' % chip_id) + mdebug(10, ' Unrecognized chip ID. Trying CC13xx/CC26xx') + device = CC26xx(cmd) else: - mdebug(5, " Target id 0x%x, %s" % (chip_id, chip_id_str)) + mdebug(10, " Target id 0x%x, %s" % (chip_id, chip_id_str)) + device = CC2538(cmd) - if conf['erase']: - # we only do full erase for now (CC2538) - address = 0x00200000 #flash start addr for cc2538 - size = 0x80000 #total flash size cc2538 - mdebug(5, "Erasing %s bytes starting at address 0x%x" % (size, address)) + # Choose a good default address unless the user specified -a + if conf['address'] is None: + conf['address'] = device.flash_start_addr - if cmd.cmdEraseMemory(address, size): + if conf['force_speed'] != 1 and device.has_cmd_set_xosc: + if cmd.cmdSetXOsc(): #switch to external clock source + cmd.close() + conf['baud'] = 1000000 + cmd.open(conf['port'], conf['baud']) + mdebug(6, "Opening port %(port)s, baud %(baud)d" % {'port':conf['port'], 'baud':conf['baud']}) + mdebug(6, "Reconnecting to target at higher speed...") + if (cmd.sendSynch() != 1): + raise CmdException("Can't connect to target after clock source switch. (Check external crystal)") + else: + raise CmdException("Can't switch target to external clock source. (Try forcing speed)") + + if conf['erase']: + # we only do full erase for now + if device.erase(): mdebug(5, " Erase done") else: raise CmdException("Erase failed") if conf['write']: # TODO: check if boot loader back-door is open, need to read flash size first to get address - if cmd.writeMemory(conf['address'], data): + if cmd.writeMemory(conf['address'], firmware.bytes): mdebug(5, " Write done ") else: raise CmdException("Write failed ") @@ -717,8 +1098,8 @@ def read(filename): if conf['verify']: mdebug(5,"Verifying by comparing CRC32 calculations.") - crc_local = (binascii.crc32(bytearray(data))& 0xffffffff) - crc_target = cmd.cmdCRC32(conf['address'],len(data)) #CRC of target will change according to length input file + crc_local = firmware.crc32() + crc_target = device.crc(conf['address'], len(firmware.bytes)) #CRC of target will change according to length input file if crc_local == crc_target: mdebug(5, " Verified (match: 0x%08x)" % crc_local) @@ -735,27 +1116,32 @@ def read(filename): mdebug(5, "Setting IEEE address to %s" % (':'.join(['%02x' % ord(b) for b in struct.pack('>Q', ieee_addr)]))) ieee_addr_bytes = [ord(b) for b in struct.pack('> 2): + rdata = device.read_memory(conf['address'] + (i * 4)) #reading 4 bytes at a time + mdebug(5, " 0x%x: 0x%02x%02x%02x%02x" % (conf['address'] + (i * 4), rdata[0], rdata[1], rdata[2], rdata[3]), '\r') + f.write(rdata) + f.close() mdebug(5, " Read done ") + if conf['disable-bootloader']: + device.disable_bootloader() + cmd.cmdReset() except Exception as err: + if QUIET >= 10: + traceback.print_exc() exit('ERROR: %s' % str(err)) From 5eafb071a6e9c044bbe54ae8ee5667b00aaf02e6 Mon Sep 17 00:00:00 2001 From: PeterKietzmann Date: Tue, 30 Aug 2016 21:39:39 +0200 Subject: [PATCH 2/2] dist/tools/cc2538-bsl: enforce python2 --- boards/cc2538dk/Makefile.include | 2 +- boards/openmote-cc2538/Makefile.include | 2 +- boards/remote-common/Makefile.include | 2 +- dist/tools/cc2538-bsl/cc2538-bsl.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 dist/tools/cc2538-bsl/cc2538-bsl.py diff --git a/boards/cc2538dk/Makefile.include b/boards/cc2538dk/Makefile.include index e88621674189..303db8c6b526 100644 --- a/boards/cc2538dk/Makefile.include +++ b/boards/cc2538dk/Makefile.include @@ -20,7 +20,7 @@ export RESET = $(RIOTBOARD)/$(BOARD)/dist/reset.sh export PROGRAMMER ?= cc2538-bsl ifeq ($(PROGRAMMER),cc2538-bsl) - export FLASHER = python $(RIOTBASE)/dist/tools/cc2538-bsl/cc2538-bsl.py + export FLASHER = $(RIOTBASE)/dist/tools/cc2538-bsl/cc2538-bsl.py export FFLAGS = -p "$(PORT)" -e -w -v $(HEXFILE) else ifeq ($(PROGRAMMER),jlink) export FLASHER = $(RIOTBOARD)/$(BOARD)/dist/flash.sh diff --git a/boards/openmote-cc2538/Makefile.include b/boards/openmote-cc2538/Makefile.include index 5a55a6909f1b..ad56683272fc 100644 --- a/boards/openmote-cc2538/Makefile.include +++ b/boards/openmote-cc2538/Makefile.include @@ -19,7 +19,7 @@ ifeq ($(PROGRAMMER),jlink) else export OFLAGS = -O binary export HEXFILE = $(ELFFILE:.elf=.bin) - export FLASHER = python $(RIOTBASE)/dist/tools/cc2538-bsl/cc2538-bsl.py + export FLASHER = $(RIOTBASE)/dist/tools/cc2538-bsl/cc2538-bsl.py export FFLAGS = -p "$(PORT)" -e -w -v -b 460800 $(HEXFILE) endif diff --git a/boards/remote-common/Makefile.include b/boards/remote-common/Makefile.include index 7fe111e51e2a..7ae0d4cb28ee 100644 --- a/boards/remote-common/Makefile.include +++ b/boards/remote-common/Makefile.include @@ -12,7 +12,7 @@ ifeq ($(PROGRAMMER),cc2538-bsl) else ifeq ($(OS),Darwin) PORT_BSL ?= $(PORT_DARWIN) endif - export FLASHER = python $(RIOTBASE)/dist/tools/cc2538-bsl/cc2538-bsl.py + export FLASHER = $(RIOTBASE)/dist/tools/cc2538-bsl/cc2538-bsl.py export FFLAGS = -p "$(PORT_BSL)" -e -w -v -b 115200 $(HEXFILE) else ifeq ($(PROGRAMMER),jlink) export FLASHER = $(RIOTBOARD)/$(BOARD)/dist/flash.sh diff --git a/dist/tools/cc2538-bsl/cc2538-bsl.py b/dist/tools/cc2538-bsl/cc2538-bsl.py old mode 100644 new mode 100755 index 1adb1256cae3..31ecb74cd35a --- a/dist/tools/cc2538-bsl/cc2538-bsl.py +++ b/dist/tools/cc2538-bsl/cc2538-bsl.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python2 # Copyright (c) 2014, Jelmer Tiete . # All rights reserved.