summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2013-07-10 01:29:55 (GMT)
committerChristian Pointner <equinox@spreadspace.org>2013-07-10 01:29:55 (GMT)
commit2beeddebe44c8d5997b1999c651f28a36155cf07 (patch)
tree6196c7469bcdbe96ae7c639e7316c6930753b708
parent9b62390cca48c5a364970e9b48f7f72873fb65b4 (diff)
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
-rw-r--r--bootloader/Makefile2
-rwxr-xr-xbootloader/downloader.py88
-rw-r--r--bootloader/ihexpic.py405
3 files changed, 442 insertions, 53 deletions
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 <equinox@spreadspace.org>
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+#*********************************************************************
+# 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'