From 6f86ce7057dd7cd1b491e8f09501258822d2ea74 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Tue, 23 May 2017 17:06:09 +0200 Subject: Import j1demo --- j1demo/firmware/intelhex.py | 643 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 j1demo/firmware/intelhex.py (limited to 'j1demo/firmware/intelhex.py') diff --git a/j1demo/firmware/intelhex.py b/j1demo/firmware/intelhex.py new file mode 100644 index 0000000..ecf8b28 --- /dev/null +++ b/j1demo/firmware/intelhex.py @@ -0,0 +1,643 @@ +#!/usr/bin/python + +# Copyright (c) 2005-2007, Alexander Belchenko +# All rights reserved. +# +# Redistribution and use in source and binary forms, +# with or without modification, are permitted provided +# that the following conditions are met: +# +# * Redistributions of source code must retain +# the above copyright notice, this list of conditions +# and the following disclaimer. +# * 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. +# * Neither the name of the +# nor the names of its contributors may be used to endorse +# or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. + +'''Intel HEX file format reader and converter. + +This script also may be used as hex2bin convertor utility. + +@author Alexander Belchenko (bialix AT ukr net) +@version 0.8.6 +@date 2007/04/26 +''' + + +__docformat__ = "javadoc" + + +from array import array +from binascii import hexlify, unhexlify + + +class IntelHex: + ''' Intel HEX file reader. ''' + + def __init__(self, fname): + ''' Constructor. + @param fname file name of HEX file or file object. + ''' + #public members + self.Error = None + self.AddrOverlap = None + self.padding = 0x0FF + # Start Address + self.start_addr = None + + # private members + self._fname = fname + self._buf = {} + self._readed = False + self._eof = False + self._offset = 0 + + def readfile(self): + ''' Read file into internal buffer. + @return True if successful. + ''' + if self._readed: + return True + + if not hasattr(self._fname, "read"): + f = file(self._fname, "rU") + fclose = f.close + else: + f = self._fname + fclose = None + + self._offset = 0 + self._eof = False + + result = True + + for s in f: + if not self.decode_record(s): + result = False + break + + if self._eof: + break + + if fclose: + fclose() + + self._readed = result + return result + + def decode_record(self, s): + ''' Decode one record of HEX file. + @param s line with HEX record. + @return True if line decode OK, or this is not HEX line. + False if this is invalid HEX line or checksum error. + ''' + s = s.rstrip('\r\n') + if not s: + return True # empty line + + if s[0] == ':': + try: + bin = array('B', unhexlify(s[1:])) + except TypeError: + # this might be raised by unhexlify when odd hexascii digits + self.Error = "Odd hexascii digits" + return False + length = len(bin) + if length < 5: + self.Error = "Too short line" + return False + else: + return True # first char must be ':' + + record_length = bin[0] + + if length != (5 + record_length): + self.Error = "Invalid line length" + return False + + addr = bin[1]*256 + bin[2] + + record_type = bin[3] + if not (0 <= record_type <= 5): + self.Error = "Invalid type of record: %d" % record_type + return False + + crc = sum(bin) + crc &= 0x0FF + if crc != 0: + self.Error = "Invalid crc" + return False + + if record_type == 0: + # data record + addr += self._offset + for i in xrange(4, 4+record_length): + if not self._buf.get(addr, None) is None: + self.AddrOverlap = addr + self._buf[addr] = bin[i] + addr += 1 # FIXME: addr should be wrapped on 64K boundary + + elif record_type == 1: + # end of file record + if record_length != 0: + self.Error = "Bad End-of-File Record" + return False + self._eof = True + + elif record_type == 2: + # Extended 8086 Segment Record + if record_length != 2 or addr != 0: + self.Error = "Bad Extended 8086 Segment Record" + return False + self._offset = (bin[4]*256 + bin[5]) * 16 + + elif record_type == 4: + # Extended Linear Address Record + if record_length != 2 or addr != 0: + self.Error = "Bad Extended Linear Address Record" + return False + self._offset = (bin[4]*256 + bin[5]) * 65536 + + elif record_type == 3: + # Start Segment Address Record + if record_length != 4 or addr != 0: + self.Error = "Bad Start Segment Address Record" + return False + if self.start_addr: + self.Error = "Start Address Record appears twice" + return False + self.start_addr = {'CS': bin[4]*256 + bin[5], + 'IP': bin[6]*256 + bin[7], + } + + elif record_type == 5: + # Start Linear Address Record + if record_length != 4 or addr != 0: + self.Error = "Bad Start Linear Address Record" + return False + if self.start_addr: + self.Error = "Start Address Record appears twice" + return False + self.start_addr = {'EIP': (bin[4]*16777216 + + bin[5]*65536 + + bin[6]*256 + + bin[7]), + } + + return True + + def _get_start_end(self, start=None, end=None): + """Return default values for start and end if they are None + """ + if start is None: + start = min(self._buf.keys()) + if end is None: + end = max(self._buf.keys()) + if start > end: + start, end = end, start + return start, end + + def tobinarray(self, start=None, end=None, pad=None): + ''' Convert to binary form. + @param start start address of output bytes. + @param end end address of output bytes. + @param pad fill empty spaces with this value + (if None used self.padding). + @return array of unsigned char data. + ''' + if pad is None: + pad = self.padding + + bin = array('B') + + if self._buf == {}: + return bin + + start, end = self._get_start_end(start, end) + + for i in xrange(start, end+1): + bin.append(self._buf.get(i, pad)) + + return bin + + def tobinstr(self, start=None, end=None, pad=0xFF): + ''' Convert to binary form. + @param start start address of output bytes. + @param end end address of output bytes. + @param pad fill empty spaces with this value + (if None used self.padding). + @return string of binary data. + ''' + return self.tobinarray(start, end, pad).tostring() + + def tobinfile(self, fobj, start=None, end=None, pad=0xFF): + '''Convert to binary and write to file. + + @param fobj file name or file object for writing output bytes. + @param start start address of output bytes. + @param end end address of output bytes. + @param pad fill empty spaces with this value + (if None used self.padding). + ''' + if not hasattr(fobj, "write"): + fobj = file(fobj, "wb") + fclose = fobj.close + else: + fclose = None + + fobj.write(self.tobinstr(start, end, pad)) + + if fclose: + fclose() + + def minaddr(self): + ''' Get minimal address of HEX content. ''' + aa = self._buf.keys() + if aa == []: + return 0 + else: + return min(aa) + + def maxaddr(self): + ''' Get maximal address of HEX content. ''' + aa = self._buf.keys() + if aa == []: + return 0 + else: + return max(aa) + + def __getitem__(self, addr): + ''' Get byte from address. + @param addr address of byte. + @return byte if address exists in HEX file, or self.padding + if no data found. + ''' + return self._buf.get(addr, self.padding) + + def __setitem__(self, addr, byte): + self._buf[addr] = byte + + def writefile(self, f, write_start_addr=True): + """Write data to file f in HEX format. + + @param f filename or file-like object for writing + @param write_start_addr enable or disable writing start address + record to file (enabled by default). + If there is no start address nothing + will be written. + + @return True if successful. + """ + fwrite = getattr(f, "write", None) + if fwrite: + fobj = f + fclose = None + else: + fobj = file(f, 'w') + fwrite = fobj.write + fclose = fobj.close + + # start address record if any + if self.start_addr and write_start_addr: + keys = self.start_addr.keys() + keys.sort() + bin = array('B', '\0'*9) + if keys == ['CS','IP']: + # Start Segment Address Record + bin[0] = 4 # reclen + bin[1] = 0 # offset msb + bin[2] = 0 # offset lsb + bin[3] = 3 # rectyp + cs = self.start_addr['CS'] + bin[4] = (cs >> 8) & 0x0FF + bin[5] = cs & 0x0FF + ip = self.start_addr['IP'] + bin[6] = (ip >> 8) & 0x0FF + bin[7] = ip & 0x0FF + bin[8] = (-sum(bin)) & 0x0FF # chksum + fwrite(':') + fwrite(hexlify(bin.tostring()).upper()) + fwrite('\n') + elif keys == ['EIP']: + # Start Linear Address Record + bin[0] = 4 # reclen + bin[1] = 0 # offset msb + bin[2] = 0 # offset lsb + bin[3] = 5 # rectyp + eip = self.start_addr['EIP'] + bin[4] = (eip >> 24) & 0x0FF + bin[5] = (eip >> 16) & 0x0FF + bin[6] = (eip >> 8) & 0x0FF + bin[7] = eip & 0x0FF + bin[8] = (-sum(bin)) & 0x0FF # chksum + fwrite(':') + fwrite(hexlify(bin.tostring()).upper()) + fwrite('\n') + else: + self.Error = ('Invalid start address value: %r' + % self.start_addr) + return False + + # data + minaddr = IntelHex.minaddr(self) + maxaddr = IntelHex.maxaddr(self) + if maxaddr > 65535: + offset = (minaddr/65536)*65536 + else: + offset = None + + while True: + if offset != None: + # emit 32-bit offset record + high_ofs = offset / 65536 + offset_record = ":02000004%04X" % high_ofs + bytes = divmod(high_ofs, 256) + csum = 2 + 4 + bytes[0] + bytes[1] + csum = (-csum) & 0x0FF + offset_record += "%02X\n" % csum + + ofs = offset + if (ofs + 65536) > maxaddr: + rng = xrange(maxaddr - ofs + 1) + else: + rng = xrange(65536) + else: + ofs = 0 + offset_record = '' + rng = xrange(maxaddr + 1) + + csum = 0 + k = 0 + record = "" + for addr in rng: + byte = self._buf.get(ofs+addr, None) + if byte != None: + if k == 0: + # optionally offset record + fobj.write(offset_record) + offset_record = '' + # start data record + record += "%04X00" % addr + bytes = divmod(addr, 256) + csum = bytes[0] + bytes[1] + + k += 1 + # continue data in record + record += "%02X" % byte + csum += byte + + # check for length of record + if k < 16: + continue + + if k != 0: + # close record + csum += k + csum = (-csum) & 0x0FF + record += "%02X" % csum + fobj.write(":%02X%s\n" % (k, record)) + # cleanup + csum = 0 + k = 0 + record = "" + else: + if k != 0: + # close record + csum += k + csum = (-csum) & 0x0FF + record += "%02X" % csum + fobj.write(":%02X%s\n" % (k, record)) + + # advance offset + if offset is None: + break + + offset += 65536 + if offset > maxaddr: + break + + # end-of-file record + fobj.write(":00000001FF\n") + if fclose: + fclose() + + return True +#/IntelHex + + +class IntelHex16bit(IntelHex): + """Access to data as 16-bit words.""" + + def __init__(self, source): + """Construct class from HEX file + or from instance of ordinary IntelHex class. + + @param source file name of HEX file or file object + or instance of ordinary IntelHex class + """ + if isinstance(source, IntelHex): + # from ihex8 + self.Error = source.Error + self.AddrOverlap = source.AddrOverlap + self.padding = source.padding + + # private members + self._fname = source._fname + self._buf = source._buf + self._readed = source._readed + self._eof = source._eof + self._offset = source._offset + else: + IntelHex.__init__(self, source) + + if self.padding == 0x0FF: + self.padding = 0x0FFFF + + def __getitem__(self, addr16): + """Get 16-bit word from address. + Raise error if found only one byte from pair. + + @param addr16 address of word (addr8 = 2 * addr16). + @return word if bytes exists in HEX file, or self.padding + if no data found. + """ + addr1 = addr16 * 2 + addr2 = addr1 + 1 + byte1 = self._buf.get(addr1, None) + byte2 = self._buf.get(addr2, None) + + if byte1 != None and byte2 != None: + return byte1 | (byte2 << 8) # low endian + + if byte1 == None and byte2 == None: + return self.padding + + raise Exception, 'Bad access in 16-bit mode (not enough data)' + + def __setitem__(self, addr16, word): + addr_byte = addr16 * 2 + bytes = divmod(word, 256) + self._buf[addr_byte] = bytes[1] + self._buf[addr_byte+1] = bytes[0] + + def minaddr(self): + '''Get minimal address of HEX content in 16-bit mode.''' + aa = self._buf.keys() + if aa == []: + return 0 + else: + return min(aa)/2 + + def maxaddr(self): + '''Get maximal address of HEX content in 16-bit mode.''' + aa = self._buf.keys() + if aa == []: + return 0 + else: + return max(aa)/2 + +#/class IntelHex16bit + + +def hex2bin(fin, fout, start=None, end=None, size=None, pad=0xFF): + """Hex-to-Bin convertor engine. + @return 0 if all OK + + @param fin input hex file (filename or file-like object) + @param fout output bin file (filename or file-like object) + @param start start of address range (optional) + @param end end of address range (optional) + @param size size of resulting file (in bytes) (optional) + @param pad padding byte (optional) + """ + h = IntelHex(fin) + if not h.readfile(): + print "Bad HEX file" + return 1 + + # start, end, size + if size != None and size != 0: + if end == None: + if start == None: + start = h.minaddr() + end = start + size - 1 + else: + if (end+1) >= size: + start = end + 1 - size + else: + start = 0 + + try: + h.tobinfile(fout, start, end, pad) + except IOError: + print "Could not write to file: %s" % fout + return 1 + + return 0 +#/def hex2bin + + +if __name__ == '__main__': + import getopt + import os + import sys + + usage = '''Hex2Bin python converting utility. +Usage: + python intelhex.py [options] file.hex [out.bin] + +Arguments: + file.hex name of hex file to processing. + out.bin name of output file. + If omitted then output write to file.bin. + +Options: + -h, --help this help message. + -p, --pad=FF pad byte for empty spaces (ascii hex value). + -r, --range=START:END specify address range for writing output + (ascii hex value). + Range can be in form 'START:' or ':END'. + -l, --length=NNNN, + -s, --size=NNNN size of output (decimal value). +''' + + pad = 0xFF + start = None + end = None + size = None + + try: + opts, args = getopt.getopt(sys.argv[1:], "hp:r:l:s:", + ["help", "pad=", "range=", + "length=", "size="]) + + for o, a in opts: + if o in ("-h", "--help"): + print usage + sys.exit(0) + elif o in ("-p", "--pad"): + try: + pad = int(a, 16) & 0x0FF + except: + raise getopt.GetoptError, 'Bad pad value' + elif o in ("-r", "--range"): + try: + l = a.split(":") + if l[0] != '': + start = int(l[0], 16) + if l[1] != '': + end = int(l[1], 16) + except: + raise getopt.GetoptError, 'Bad range value(s)' + elif o in ("-l", "--lenght", "-s", "--size"): + try: + size = int(a, 10) + except: + raise getopt.GetoptError, 'Bad size value' + + if start != None and end != None and size != None: + raise getopt.GetoptError, 'Cannot specify START:END and SIZE simultaneously' + + if not args: + raise getopt.GetoptError, 'Hex file is not specified' + + if len(args) > 2: + raise getopt.GetoptError, 'Too many arguments' + + except getopt.GetoptError, msg: + print msg + print usage + sys.exit(2) + + fin = args[0] + if len(args) == 1: + import os.path + name, ext = os.path.splitext(fin) + fout = name + ".bin" + else: + fout = args[1] + + if not os.path.isfile(fin): + print "File not found" + sys.exit(1) + + sys.exit(hex2bin(fin, fout, start, end, size, pad)) -- cgit v1.2.3