From 12581837917c1935a57f5b42f330e2fdc4137c73 Mon Sep 17 00:00:00 2001 From: Jelmer Tiete <jelmer@tiete.be> Date: Tue, 23 Sep 2014 18:49:18 -0400 Subject: [PATCH] Add the latest cc2538-bsl script from https://github.com/JelmerT/cc2538-bsl/commit/f3701a0 --- boards/cc2538dk/dist/cc2538-bsl.py | 761 +++++++++++++++++++++++++++++ 1 file changed, 761 insertions(+) create mode 100755 boards/cc2538dk/dist/cc2538-bsl.py diff --git a/boards/cc2538dk/dist/cc2538-bsl.py b/boards/cc2538dk/dist/cc2538-bsl.py new file mode 100755 index 0000000000..ec3d3a3e3e --- /dev/null +++ b/boards/cc2538dk/dist/cc2538-bsl.py @@ -0,0 +1,761 @@ +#!/usr/bin/env python + +# Copyright (c) 2014, Jelmer Tiete <jelmer@tiete.be>. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote +# products derived from this software without specific prior +# written permission. + +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Implementation based on stm32loader by Ivan A-R <ivan@tuxotronic.org> + +# Serial boot loader over UART for CC2538 +# 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 +# backdoor is enabled. +# Make sure you don't lock yourself out!! (enable backdoor in your firmware) +# More info at https://github.com/JelmerT/cc2538-bsl + +from __future__ import print_function +from subprocess import Popen, PIPE + +import sys, getopt +import glob +import time +import tempfile +import os +import subprocess +import struct +import binascii + +#version +VERSION_STRING = "1.0" + +# Verbose level +QUIET = 5 + +# Check which version of Python is running +PY3 = sys.version_info >= (3,0) + +try: + import serial +except ImportError: + print('{} requires the Python serial library'.format(sys.argv[0])) + print('Please install it with one of the following:') + print('') + if PY3: + print(' Ubuntu: sudo apt-get install python3-serial') + print(' Mac: sudo port install py34-serial') + else: + print(' Ubuntu: sudo apt-get install python-serial') + print(' Mac: sudo port install py-serial') + sys.exit(1) + + +def mdebug(level, message, attr='\n'): + if QUIET >= level: + print(message, end=attr, file=sys.stderr) + +# Takes chip IDs (obtained via Get ID command) to human-readable names +CHIP_ID_STRS = {0xb964: 'CC2538'} + +RETURN_CMD_STRS = {0x40: 'Success', + 0x41: 'Unknown command', + 0x42: 'Invalid command', + 0x43: 'Invalid address', + 0x44: 'Flash fail' + } + +COMMAND_RET_SUCCESS = 0x40 +COMMAND_RET_UNKNOWN_CMD = 0x41 +COMMAND_RET_INVALID_CMD = 0x42 +COMMAND_RET_INVALID_ADR = 0x43 +COMMAND_RET_FLASH_FAIL = 0x44 + +ADDR_IEEE_ADDRESS_SECONDARY = 0x0027ffcc + +class CmdException(Exception): + pass + +class CommandInterface(object): + def open(self, aport='/dev/tty.usbserial-000013FAB', abaudrate=500000): + self.sp = serial.Serial( + port=aport, + baudrate=abaudrate, # baudrate + bytesize=8, # number of databits + parity=serial.PARITY_NONE, + stopbits=1, + xonxoff=0, # enable software flow control + rtscts=0, # disable RTS/CTS flow control + timeout=0.5 # set a timeout value, None for waiting forever + ) + + # Use the DTR and RTS lines to control !RESET and the bootloader pin. + # This can automatically invoke the bootloader without the user + # having to toggle any pins. + # 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) + + def close(self): + self.sp.close() + + + def _wait_for_ack(self, info="", timeout=0): + stop = time.time() + timeout + got = None + while not got: + got = self._read(2) + if time.time() > stop: + break + + if not got: + mdebug(10, "No response to %s" % info) + return 0 + + # wait for ask + ask = got[1] + + if ask == 0xCC: + # ACK + return 1 + elif ask == 0x33: + # NACK + mdebug(10, "Target replied with a NACK during %s" % info) + return 0 + + # Unknown response + mdebug(10, "Unrecognised response 0x%x to %s" % (ask, info)) + return 0 + + def _encode_addr(self, addr): + byte3 = (addr >> 0) & 0xFF + byte2 = (addr >> 8) & 0xFF + byte1 = (addr >> 16) & 0xFF + byte0 = (addr >> 24) & 0xFF + if PY3: + return bytes([byte0, byte1, byte2, byte3]) + else: + return (chr(byte0) + chr(byte1) + chr(byte2) + chr(byte3)) + + def _decode_addr(self, byte0, byte1, byte2, byte3): + return ((byte3 << 24) | (byte2 << 16) | (byte1 << 8) | (byte0 << 0)) + + def _calc_checks(self, cmd, addr, size): + return ((sum(bytearray(self._encode_addr(addr))) + +sum(bytearray(self._encode_addr(size))) + +cmd) + &0xFF) + + def _write(self, data): + if PY3: + if type(data) == int: + self.sp.write(bytes([data])) + elif type(data) == bytes or type(data) == bytearray: + self.sp.write(data) + else: + if type(data) == int: + self.sp.write(chr(data)) + else: + self.sp.write(data) + + def _read(self, length): + got = self.sp.read(length) + if PY3: + return got + else: + return [ord(x) for x in got] + + def sendAck(self): + self._write(chr(0x00)) + self._write(0xCC) + return + + def sendNAck(self): + self._write(chr(0x00)) + self._write(chr(0x33)) + return + + + def receivePacket(self): + # stop = time.time() + 5 + # got = None + # while not got: + got = self._read(2) + # if time.time() > stop: + # break + + # if not got: + # raise CmdException("No response to %s" % info) + + size = got[0] #rcv size + chks = got[1] #rcv checksum + data = self._read(size-2) # rcv data + + mdebug(10, "*** received %x bytes" % size) + if chks == sum(data)&0xFF: + self.sendAck() + return data + else: + self.sendNAck() + #TODO: retry receiving! + raise CmdException("Received packet checksum error") + return 0 + + def sendSynch(self): + cmd = 0x55 + + self.sp.flushInput() #flush serial input buffer for first ACK reception + + mdebug(10, "*** sending synch sequence") + self._write(cmd) # send U + self._write(cmd) # send U + return self._wait_for_ack("Synch (0x55 0x55)") + + def checkLastCmd(self): + stat = self.cmdGetStatus() + if not (stat): + raise CmdException("No response from target on status request. (Did you disable the bootloader?)") + + if stat[0] == COMMAND_RET_SUCCESS: + mdebug(10, "Command Successful") + return 1 + else: + stat_str = RETURN_CMD_STRS.get(stat, None) + if stat_str is None: + mdebug(0, 'Warning: unrecognized status returned 0x%x' % stat) + else: + mdebug(0, "Target returned: 0x%x, %s" % (stat, stat_str)) + return 0 + + + def cmdPing(self): + cmd = 0x20 + lng = 3 + + self._write(lng) # send size + self._write(cmd) # send checksum + self._write(cmd) # send data + + mdebug(10, "*** Ping command (0x20)") + if self._wait_for_ack("Ping (0x20)"): + return self.checkLastCmd() + + def cmdReset(self): + cmd = 0x25 + lng = 3 + + self._write(lng) # send size + self._write(cmd) # send checksum + self._write(cmd) # send data + + mdebug(10, "*** Reset command (0x25)") + if self._wait_for_ack("Reset (0x25)"): + return 1 + + def cmdGetChipId(self): + cmd = 0x28 + lng = 3 + + self._write(lng) # send size + self._write(cmd) # send checksum + self._write(cmd) # send data + + mdebug(10, "*** GetChipId command (0x28)") + if self._wait_for_ack("Get ChipID (0x28)"): + 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) + chip_id = (version[2] << 8) | version[3] + return chip_id + else: + raise CmdException("GetChipID (0x28) failed") + + def cmdGetStatus(self): + cmd = 0x23 + lng = 3 + + self._write(lng) # send size + self._write(cmd) # send checksum + self._write(cmd) # send data + + mdebug(10, "*** GetStatus command (0x23)") + if self._wait_for_ack("Get Status (0x23)"): + stat = self.receivePacket() + return stat + + def cmdSetXOsc(self): + cmd = 0x29 + lng = 3 + + self._write(lng) # send size + self._write(cmd) # send checksum + self._write(cmd) # send data + + mdebug(10, "*** SetXOsc command (0x29)") + if self._wait_for_ack("SetXOsc (0x29)"): + return 1 + # UART speed (needs) to be changed! + + def cmdRun(self, addr): + cmd=0x22 + lng=7 + + self._write(lng) # send length + self._write(self._calc_checks(cmd,addr,0)) # send checksum + self._write(cmd) # send cmd + self._write(self._encode_addr(addr)) # send addr + + mdebug(10, "*** Run command(0x22)") + return 1 + + def cmdEraseMemory(self, addr, size): + cmd=0x26 + lng=11 + + 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 + + mdebug(10, "*** Erase command(0x26)") + if self._wait_for_ack("Erase memory (0x26)",10): + return self.checkLastCmd() + + def cmdCRC32(self, addr, size): + cmd=0x27 + lng=11 + + 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 + + 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 + + if (size % 4) != 0: # check for invalid data lengths + raise Exception('Invalid data size: %i. Size must be a multiple of 4.' % size) + + 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 + + mdebug(10, "*** Download command (0x21)") + if self._wait_for_ack("Download (0x21)",2): + return self.checkLastCmd() + + def cmdSendData(self, data): + cmd=0x24 + lng=len(data)+3 + # TODO: check total size of data!! max 252 bytes! + + self._write(lng) # send size + self._write((sum(bytearray(data))+cmd)&0xFF) # send checksum + self._write(cmd) # send cmd + self._write(bytearray(data)) # send data + + mdebug(10, "*** Send Data (0x24)") + if self._wait_for_ack("Send data (0x24)",10): + return self.checkLastCmd() + + def cmdMemRead(self, addr): # untested + cmd=0x2A + lng=8 + + self._write(lng) # send length + self._write(self._calc_checks(cmd,addr,4)) # send checksum + self._write(cmd) # send cmd + self._write(self._encode_addr(addr)) # send addr + self._write(4) # send width, 4 bytes + + mdebug(10, "*** Mem Read (0x2A)") + if self._wait_for_ack("Mem Read (0x2A)",1): + data = self.receivePacket() + if self.checkLastCmd(): + return data # self._decode_addr(ord(data[3]),ord(data[2]),ord(data[1]),ord(data[0])) + + def cmdMemWrite(self, addr, data, width): # untested + # TODO: check width for 1 or 4 and data size + cmd=0x2B + lng=10 + + self._write(lng) # send length + self._write(self._calc_checks(cmd,addr,0)) # send checksum + self._write(cmd) # send cmd + self._write(self._encode_addr(addr)) # send addr + self._write(bytearray(data)) # send data + self._write(width) # send width, 4 bytes + + mdebug(10, "*** Mem write (0x2B)") + if self._wait_for_ack("Mem Write (0x2B)",2): + return checkLastCmd() + + +# Complex commands section + + 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) + + # 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 "\ + "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"): + raise Exception('Aborted by user.') + + mdebug(5, "Writing %(lng)d bytes starting at address 0x%(addr)X" % + { 'lng': lng, 'addr': addr}) + + offs = 0 + addr_set = 0 + + while lng > trsf_size: #check if amount of remaining data is less then packet size + if data[offs:offs+trsf_size] != empty_packet: #skip packets filled with 0xFF + 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') + sys.stdout.flush() + + self.cmdSendData(data[offs:offs+trsf_size]) # send next data packet + else: # skipped packet, address needs to be set + addr_set = 0 + + offs = offs + trsf_size + addr = addr + trsf_size + lng = lng - trsf_size + + mdebug(5, "Write %(len)d bytes at 0x%(addr)X" % {'addr': addr, 'len': lng}, '\r') + self.cmdDownload(addr,lng) + return self.cmdSendData(data[offs:offs+lng]) # send last data packet + +def query_yes_no(question, default="yes"): + valid = {"yes":True, "y":True, "ye":True, + "no":False, "n":False} + if default == None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while True: + sys.stdout.write(question + prompt) + if PY3: + choice = input().lower() + else: + choice = raw_input().lower() + if default is not None and choice == '': + return valid[default] + elif choice in valid: + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' "\ + "(or 'y' or 'n').\n") + +# Convert the entered IEEE address into an integer +def parse_ieee_address (inaddr): + try: + return int(inaddr, 16) + except ValueError: + # inaddr is not a hex string, look for other formats + if ':' in inaddr: + bytes = inaddr.split(':') + elif '-' in inaddr: + bytes = inaddr.split('-') + if len(bytes) != 8: + raise ValueError("Supplied IEEE address does not contain 8 bytes") + addr = 0 + for i,b in zip(range(8), bytes): + try: + addr += int(b, 16) << (56-(i*8)) + except ValueError: + raise ValueError("IEEE address contains invalid bytes") + return addr + +def print_version(): + # Get the version using "git describe". + try: + p = Popen(['git', 'describe', '--tags', '--match', '[0-9]*'], + stdout=PIPE, stderr=PIPE) + p.stderr.close() + line = p.stdout.readlines()[0] + version = line.strip() + except: + # We're not in a git repo, or git failed, use fixed version string. + version = VERSION_STRING + 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 + -q Quiet + -V Verbose + -e Erase (full) + -w Write + -v Verify (CRC32 check) + -r Read + -l length Length of read + -p port Serial port (default: first USB-like port in /dev) + -b baud Baud speed (default: 500000) + -a addr Target address + -i, --ieee-address addr Set the secondary 64 bit IEEE address + --version Print script version + +Examples: + ./%s -e -w -v example/main.bin + ./%s -e -w -v --ieee-address 00:12:4b:aa:bb:cc:dd:ee example/main.bin + + """ % (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, + 'erase': 0, + 'write': 0, + 'verify': 0, + 'read': 0, + 'len': 0x80000, + 'fname':'', + 'ieee_address': 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']) + except getopt.GetoptError as err: + # print help information and exit: + print(str(err)) # will print something like "option -a not recognized" + usage() + sys.exit(2) + + for o, a in opts: + if o == '-V': + QUIET = 10 + elif o == '-q': + QUIET = 0 + elif o == '-h': + usage() + sys.exit(0) + elif o == '-e': + conf['erase'] = 1 + elif o == '-w': + conf['write'] = 1 + elif o == '-v': + conf['verify'] = 1 + elif o == '-r': + conf['read'] = 1 + elif o == '-p': + conf['port'] = a + elif o == '-b': + conf['baud'] = eval(a) + conf['force_speed'] = 1 + elif o == '-a': + conf['address'] = eval(a) + elif o == '-l': + conf['len'] = eval(a) + elif o == '-i' or o == '--ieee-address': + conf['ieee_address'] = str(a) + elif o == '--version': + print_version() + sys.exit(0) + else: + assert False, "Unhandled option" + + try: + # Sanity checks + if conf['write'] or conf['read'] or conf['verify']: # check for input/output file + try: + args[0] + except: + 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"): + 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"): + raise Exception('Aborted by user.') + + if conf['read'] and not conf['write'] and conf['verify']: + raise Exception('Verify after read not implemented.') + + # 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']: + ports.extend(glob.glob('/dev/%s*' % name)) + + ports = sorted(ports) + + if ports: + # Found something - take it + conf['port'] = ports[0] + else: + raise Exception('No serial port found.') + + cmd = CommandInterface() + cmd.open(conf['port'], conf['baud']) + 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]) + + 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)") + + chip_id = cmd.cmdGetChipId() + 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) + else: + mdebug(5, " Target id 0x%x, %s" % (chip_id, chip_id_str)) + + 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)) + + if cmd.cmdEraseMemory(address, size): + 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): + mdebug(5, " Write done ") + else: + raise CmdException("Write failed ") + + 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 + + if crc_local == crc_target: + mdebug(5, " Verified (match: 0x%08x)" % crc_local) + else: + cmd.cmdReset() + raise Exception("NO CRC32 match: Local = 0x%x, Target = 0x%x" % (crc_local,crc_target)) + + if conf['ieee_address'] != 0: + ieee_addr = parse_ieee_address(conf['ieee_address']) + if PY3: + mdebug(5, "Setting IEEE address to %s" % (':'.join(['%02x' % b for b in struct.pack('>Q', ieee_addr)]))) + ieee_addr_bytes = struct.pack('<Q', ieee_addr) + else: + 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('<Q', ieee_addr)] + + if cmd.writeMemory(ADDR_IEEE_ADDRESS_SECONDARY, ieee_addr_bytes): + mdebug(5, " Set address done ") + else: + raise CmdException("Set address failed ") + + if conf['read']: + length = conf['len'] + if length < 4: # reading 4 bytes at a time + length = 4 + else: + length = length + (length % 4) + + mdebug(5, "Reading %s bytes starting at address 0x%x" % (length, conf['address'])) + f = file(args[0], 'w').close() #delete previous file + for i in range(0,(length/4)): + rdata = cmd.cmdMemRead(conf['address']+(i*4)) #reading 4 bytes at a time + mdebug(5, " 0x%x: 0x%02x%02x%02x%02x" % (conf['address']+(i*4), ord(rdata[3]), ord(rdata[2]), ord(rdata[1]), ord(rdata[0])), '\r') + file(args[0], 'ab').write(''.join(reversed(rdata))) + mdebug(5, " Read done ") + + cmd.cmdReset() + + except Exception as err: + exit('ERROR: %s' % str(err)) -- GitLab