From 2beeddebe44c8d5997b1999c651f28a36155cf07 Mon Sep 17 00:00:00 2001 From: Christian Pointner Date: Wed, 10 Jul 2013 01:29:55 +0000 Subject: drop requirment for intelhex library added minimal hex file handling class git-svn-id: https://svn.spreadspace.org/pic/trunk@65 a09c6847-51d9-44de-8ef2-e725cf50f3c7 --- bootloader/Makefile | 2 +- bootloader/downloader.py | 88 +++++----- bootloader/ihexpic.py | 405 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+), 53 deletions(-) create mode 100644 bootloader/ihexpic.py diff --git a/bootloader/Makefile b/bootloader/Makefile index 370a652..fb91150 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -28,4 +28,4 @@ all: $(MAKE) -f Makefile-$(TARGET) $@ %: - $(MAKE) -f Makefile-$(TARGET) $@ \ No newline at end of file + $(MAKE) -f Makefile-$(TARGET) $@ diff --git a/bootloader/downloader.py b/bootloader/downloader.py index 459e0d2..260ff7e 100755 --- a/bootloader/downloader.py +++ b/bootloader/downloader.py @@ -29,7 +29,7 @@ VERSION_MIN = 1 ### HEX File Magic def load_hex(file): - from intelhex import IntelHex + from ihexpic import IHexPic fin = file if fin == '-': @@ -38,58 +38,41 @@ def load_hex(file): print >> sys.stderr, "ERROR: File not found: %s" % fin sys.exit(1) - codedata = {} - ih = IntelHex(fin) - for a in ih.addresses(): - if a/2 not in codedata.keys(): - codedata[a/2] = 0 - if a%2 == 0: - codedata[a/2] += ih[a] - else: - codedata[a/2] += (ih[a] << 8) + hexdata = IHexPic() + hexdata.load_from_file(fin) + return hexdata - return codedata +# TODO: re-add this as soon as write_hex_file works +# def write_hex(file, hexdata): +# fout = file +# if fout == '-': +# fout = sys.stdout -def write_hex(file, codedata): - from intelhex import IntelHex +# hexdata.write_hex_file(fout) - fin = file - if fin == '-': - fin = sys.stdout - - hexdata = {} - for a in codedata: - hexdata[2*a] = codedata[a] & 0xFF - hexdata[2*a+1] = codedata[a] >> 8 - - ih = IntelHex() - ih.fromdict(hexdata) - ih.write_hex_file(fin) - - -def get_lowest_flash_addr(codedata, fss): - lowest_code_addr = sorted(codedata.keys())[0] +def get_lowest_flash_addr(hexdata, fss): + lowest_code_addr = hexdata.get_lowest_addr() return lowest_code_addr - (lowest_code_addr%fss) -def get_highest_flash_addr(codedata, fss): - highest_code_addr = sorted(codedata.keys())[-1] +def get_highest_flash_addr(hexdata, fss): + highest_code_addr = hexdata.get_highest_addr() return highest_code_addr + (fss - highest_code_addr%fss) -def create_flash_image(codedata, fss, sa, ea): +def create_flash_image(hexdata, fss, sa, ea): img = ( sa, [0xFFFF]*(ea-sa) ) - for a,d in codedata.items(): + for a,d in hexdata.items(): if a < ea: img[1][a-sa] = d return img -def create_flash_segments(codedata, fs, fss): - sa = get_lowest_flash_addr(codedata, fss) - ea = get_highest_flash_addr(codedata, fss) +def create_flash_segments(hexdata, fs, fss): + sa = get_lowest_flash_addr(hexdata, fss) + ea = get_highest_flash_addr(hexdata, fss) if ea >= fs: print >> sys.stderr, "WARNING: the hex file contains data after end of flash, these words will be ignored" ea = fs - img = create_flash_image(codedata, fss, sa, ea) + img = create_flash_image(hexdata, fss, sa, ea) for i in xrange(0, ea, fss): slice = tuple(img[1][i:i+fss]) if not all( (elem == 0xFFFF) for elem in slice): @@ -234,27 +217,28 @@ def boot(dev, id, file): cmd_boot(dev) def write_flash(dev, id, file): - codedata = load_hex(file) - for segment in create_flash_segments(codedata, id['fs'], id['fss']): + hexdata = load_hex(file) + for segment in create_flash_segments(hexdata, id['fs'], id['fss']): # print >> sys.stderr, "%05X: %s" % (segment[0], ''.join('%04X '%i for i in segment[1])) cmd_write_flash_segment(dev, id, segment[0], segment[1]) -def read_flash(dev, id, file): - codedata = {} - for addr in xrange(0, id['fs'], id['fss']): - data = cmd_read_flash_segment(dev, id, addr) -# print >> sys.stderr, "%05X: %s" % (addr, ''.join('%04X '%i for i in data)) - a = addr - for d in data: - codedata[a] = d - a += 1 +# TODO: re-add this as soon as write_hex_file works +# def read_flash(dev, id, file): +# codedata = {} +# for addr in xrange(0, id['fs'], id['fss']): +# data = cmd_read_flash_segment(dev, id, addr) +# # print >> sys.stderr, "%05X: %s" % (addr, ''.join('%04X '%i for i in data)) +# a = addr +# for d in data: +# codedata[a] = d +# a += 1 - write_hex(file, codedata) +# write_hex(file, codedata) def verify_flash(dev, id, file): - codedata = load_hex(file) + hexdata = load_hex(file) err = 0 - for segment in create_flash_segments(codedata, id['fs'], id['fss']): + for segment in create_flash_segments(hexdata, id['fs'], id['fss']): flashdata = cmd_read_flash_segment(dev, id, segment[0]) for file,flash in zip(segment[1] , flashdata): if flash == 0x3FFF: @@ -276,7 +260,7 @@ def verify_flash(dev, id, file): commands = { 'boot': boot, 'write': write_flash, - 'read': read_flash, +# 'read': read_flash, # TODO: re-add this as soon as write_hex_file works 'verify': verify_flash } diff --git a/bootloader/ihexpic.py b/bootloader/ihexpic.py new file mode 100644 index 0000000..d6978de --- /dev/null +++ b/bootloader/ihexpic.py @@ -0,0 +1,405 @@ +# spreadspace pic utils +# +# +# Copyright (C) 2011 Christian Pointner +# +# This file is part of spreadspace pic utils. +# +# spreadspace pic utils is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# any later version. +# +# spreadspace pic utils is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with spreadspace pic utils. If not, see . +# +#********************************************************************* +# This is based on IntelHex 1.4 +# which can be found at: http://www.bialix.com/intelhex/ +# +# +# Copyright (c) 2005-2012, 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 author 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 writer for PIC Microcontroller.''' + + +from array import array +from binascii import hexlify, unhexlify +from bisect import bisect_right +import sys + + +class IHexPic(object): + ''' Intel HEX file reader for PIC Microcontroller. ''' + + def __init__(self): + # private members + self._buf = {} + self._offset = 0 + + def _decode_record(self, s, line=0): + '''Decode one record of HEX file. + + @param s line with HEX record. + @param line line number (for error messages). + + @raise EndOfFile if EOF record encountered. + ''' + s = s.rstrip('\r\n') + if not s: + return # empty line + + if s[0] == ':': + try: + bin = array('B', unhexlify(str(s[1:]))) + except (TypeError, ValueError): + # this might be raised by unhexlify when odd hexascii digits + raise HexRecordError(line=line) + length = len(bin) + if length < 5: + raise HexRecordError(line=line) + else: + raise HexRecordError(line=line) + + record_length = bin[0] + if length != (5 + record_length): + raise RecordLengthError(line=line) + if record_length%2 != 0: + raise RecordLengthError(line=line) + + addr = bin[1]*256 + bin[2] + if addr%2 != 0: + raise RecordAlignmentError(line=line) + + record_type = bin[3] + if not (0 <= record_type <= 5): + raise RecordTypeError(line=line) + + crc = sum(bin) + crc &= 0x0FF + if crc != 0: + raise RecordChecksumError(line=line) + + if record_type == 0: + # data record + addr += self._offset + for i in xrange(4, 4+record_length, 2): + addr16 = addr/2 + if not self._buf.get(addr16, None) is None: + raise AddressOverlapError(address=addr16, line=line) + self._buf[addr16] = bin[i] + (bin[i+1]<<8) + addr += 2 # FIXME: addr should be wrapped + # BUT after 02 record (at 64K boundary) + # and after 04 record (at 4G boundary) + + elif record_type == 1: + # end of file record + if record_length != 0: + raise EOFRecordError(line=line) + raise _EndOfFile + + elif record_type == 2: + # Extended 8086 Segment Record + if record_length != 2 or addr != 0: + raise ExtendedSegmentAddressRecordError(line=line) + self._offset = (bin[4]*256 + bin[5]) * 16 + + elif record_type == 4: + # Extended Linear Address Record + if record_length != 2 or addr != 0: + raise ExtendedLinearAddressRecordError(line=line) + self._offset = (bin[4]*256 + bin[5]) * 65536 + + else: + raise UnsupportedRecordTypeError(type=record_type, line=line) + + + def load_from_file(self, fobj): + """Load hex file into internal buffer. + + @param fobj file name or file-like object + """ + if getattr(fobj, "read", None) is None: + fobj = open(fobj, "r") + fclose = fobj.close + else: + fclose = None + + line = 0 + + try: + decode = self._decode_record + try: + for s in fobj: + line += 1 + decode(s, line) + except _EndOfFile: + pass + finally: + if fclose: + fclose() + + def load_from_dict(self, dikt): + """Load data from dictionary. Dictionary should contain int keys + representing addresses. Values should be the data to be stored in + those addresses in unsigned char form (i.e. not strings). + + The contents of the dict will be merged with this object and will + overwrite any conflicts. + """ + s = dikt.copy() + for k in s.keys(): + if type(k) not in (int, long) or k < 0: + raise ValueError('Source dictionary should have only int keys') + self._buf.update(s) + + # TODO: fix this so it works with 16bit data + # def write_to_file(self, f): + # """Write data to file f in HEX format. + + # @param f filename or file-like object for writing + # """ + # fwrite = getattr(f, "write", None) + # if fwrite: + # fobj = f + # fclose = None + # else: + # fobj = file(f, 'w') + # fwrite = fobj.write + # fclose = fobj.close + + # # Translation table for uppercasing hex ascii string. + # # timeit shows that using hexstr.translate(table) + # # is faster than hexstr.upper(): + # # 0.452ms vs. 0.652ms (translate vs. upper) + # if sys.version_info[0] >= 3: + # table = bytes(range(256)).upper() + # else: + # table = ''.join(chr(i).upper() for i in range(256)) + + + # # data + # addresses = self._buf.keys() + # addresses.sort() + # addr_len = len(addresses) + # if addr_len: + # minaddr = addresses[0] + # maxaddr = addresses[-1] + + # if maxaddr > 65535: + # need_offset_record = True + # else: + # need_offset_record = False + # high_ofs = 0 + + # cur_addr = minaddr + # cur_ix = 0 + + # while cur_addr <= maxaddr: + # if need_offset_record: + # bin = array('B', str('\0'*7)) + # bin[0] = 2 # reclen + # bin[1] = 0 # offset msb + # bin[2] = 0 # offset lsb + # bin[3] = 4 # rectyp + # high_ofs = int(cur_addr>>16) + # b = divmod(high_ofs, 256) + # bin[4] = b[0] # msb of high_ofs + # bin[5] = b[1] # lsb of high_ofs + # bin[6] = (-sum(bin)) & 0x0FF # chksum + # fwrite(':' + + # str(hexlify(bin.tostring()).translate(table)) + + # '\n') + + # while True: + # # produce one record + # low_addr = cur_addr & 0x0FFFF + # # chain_len off by 1 + # chain_len = min(15, 65535-low_addr, maxaddr-cur_addr) + + # # search continuous chain + # stop_addr = cur_addr + chain_len + # if chain_len: + # ix = bisect_right(addresses, stop_addr, + # cur_ix, + # min(cur_ix+chain_len+1, addr_len)) + # chain_len = ix - cur_ix # real chain_len + # # there could be small holes in the chain + # # but we will catch them by try-except later + # # so for big continuous files we will work + # # at maximum possible speed + # else: + # chain_len = 1 # real chain_len + + # bin = array('B', str('\0'*(5+chain_len))) + # b = divmod(low_addr, 256) + # bin[1] = b[0] # msb of low_addr + # bin[2] = b[1] # lsb of low_addr + # bin[3] = 0 # rectype + # try: # if there is small holes we'll catch them + # for i in range(chain_len): + # bin[4+i] = self._buf[cur_addr+i] + # except KeyError: + # # we catch a hole so we should shrink the chain + # chain_len = i + # bin = bin[:5+i] + # bin[0] = chain_len + # bin[4+chain_len] = (-sum(bin)) & 0x0FF # chksum + # fwrite(':' + + # str(hexlify(bin.tostring()).translate(table)) + + # '\n') + + # # adjust cur_addr/cur_ix + # cur_ix += chain_len + # if cur_ix < addr_len: + # cur_addr = addresses[cur_ix] + # else: + # cur_addr = maxaddr + 1 + # break + # high_addr = int(cur_addr>>16) + # if high_addr > high_ofs: + # break + + # # end-of-file record + # fwrite(":00000001FF\n") + # if fclose: + # fclose() + + def get_lowest_addr(self): + return sorted(self._buf.keys())[0] + + def get_highest_addr(self): + return sorted(self._buf.keys())[-1] + + def items(self): + return self._buf.items() + + def __getitem__(self, addr): + t = type(addr) + if t in (int, long): + if addr < 0: + raise TypeError('Address should be >= 0.') + return self._buf(addr, 0xFFFF) + else: + raise TypeError('Address has unsupported type: %s' % t) + + +## +# IHexPic Errors Hierarchy: +# +# IHexPicError - basic error +# HexReaderError - general hex reader error +# AddressOverlapError - data for the same address overlap +# HexRecordError - hex record decoder base error +# RecordLengthError - record has invalid length +# RecordTypeError - record has invalid type (RECTYP) +# RecordChecksumError - record checksum mismatch +# EOFRecordError - invalid EOF record (type 01) +# ExtendedAddressRecordError - extended address record base error +# ExtendedSegmentAddressRecordError - invalid extended segment address record (type 02) +# ExtendedLinearAddressRecordError - invalid extended linear address record (type 04) +# UnsupportedRecordError - invalid/unsupported record type +# _EndOfFile - it's not real error, used internally by hex reader as signal that EOF record found + +class IHexPicError(Exception): + '''Base Exception class for IHexPic module''' + + _fmt = 'IHexPic base error' #: format string + + def __init__(self, msg=None, **kw): + """Initialize the Exception with the given message. + """ + self.msg = msg + for key, value in kw.items(): + setattr(self, key, value) + + def __str__(self): + """Return the message in this Exception.""" + if self.msg: + return self.msg + try: + return self._fmt % self.__dict__ + except (NameError, ValueError, KeyError), e: + return 'Unprintable exception %s: %s' \ + % (repr(e), str(e)) + +class _EndOfFile(IHexPicError): + """Used for internal needs only.""" + _fmt = 'EOF record reached -- signal to stop read file' + + +class HexReaderError(IHexPicError): + _fmt = 'Hex reader base error' + +class AddressOverlapError(HexReaderError): + _fmt = 'Hex file has data overlap at address 0x%(address)X on line %(line)d' + + +class HexRecordError(HexReaderError): + _fmt = 'Hex file contains invalid record at line %(line)d' + + +class RecordLengthError(HexRecordError): + _fmt = 'Record at line %(line)d has invalid length' + +class RecordAlignmentError(HexRecordError): + _fmt = 'Record at line %(line)d has invalid adress (not 16bit aligned)' + +class RecordTypeError(HexRecordError): + _fmt = 'Record at line %(line)d has invalid record type' + +class RecordChecksumError(HexRecordError): + _fmt = 'Record at line %(line)d has invalid checksum' + +class EOFRecordError(HexRecordError): + _fmt = 'File has invalid End-of-File record' + +class UnsupportedRecordError(HexRecordError): + _fmt = 'File has unsupported record type %(type)d at line %(line)d' + + +class ExtendedAddressRecordError(HexRecordError): + _fmt = 'Base class for extended address exceptions' + +class ExtendedSegmentAddressRecordError(ExtendedAddressRecordError): + _fmt = 'Invalid Extended Segment Address Record at line %(line)d' + +class ExtendedLinearAddressRecordError(ExtendedAddressRecordError): + _fmt = 'Invalid Extended Linear Address Record at line %(line)d' -- cgit v1.2.3