From 63327e349851f080bf54d28850caa28f0a858d03 Mon Sep 17 00:00:00 2001 From: Grigori Goronzy Date: Tue, 24 Nov 2015 00:33:22 +0100 Subject: [PATCH] frontend: add Intel HEX support Add Intel HEX support with the help of the IHex library. IHex needed some modifications to make it work with Python 3. Intel HEX files are autodetected, based on file extension (.hex, .ihx or .ihex). --- stcgal/frontend.py | 32 ++++++- stcgal/ihex.py | 202 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 stcgal/ihex.py diff --git a/stcgal/frontend.py b/stcgal/frontend.py index 3979102..f3ac224 100644 --- a/stcgal/frontend.py +++ b/stcgal/frontend.py @@ -25,6 +25,7 @@ import argparse import stcgal from stcgal.utils import Utils, BaudType from stcgal.protocols import * +from stcgal.ihex import IHex class StcGal: """STC ISP flash tool frontend""" @@ -55,11 +56,31 @@ class StcGal: except ValueError as e: raise NameError("invalid option '%s' (%s)" % (kv[0], e)) + def load_file_auto(self, fileobj): + """Load file with Intel Hex autodetection.""" + + fname = fileobj.name.lower() + if (fname.endswith(".hex") or fname.endswith(".ihx") or + fname.endswith(".ihex")): + try: + hexfile = IHex.read(fileobj) + binary = hexfile.extract_data() + print("%d bytes (Intel HEX)" %len(binary)) + return binary + except ValueError as e: + raise IOError("invalid Intel HEX file (%s)" %e) + else: + binary = fileobj.read() + print("%d bytes (Binary)" %len(binary)) + return binary + def program_mcu(self): code_size = self.protocol.model.code ee_size = self.protocol.model.eeprom - bindata = self.opts.code_binary.read() + print("Loading flash: ", end="") + sys.stdout.flush() + bindata = self.load_file_auto(self.opts.code_binary) # warn if it overflows if len(bindata) > code_size: @@ -70,7 +91,9 @@ class StcGal: # add eeprom data if supplied if self.opts.eeprom_binary: - eedata = self.opts.eeprom_binary.read() + print("Loading EEPROM: ", end="") + sys.stdout.flush() + eedata = self.load_file_auto(self.opts.eeprom_binary) if len(eedata) > ee_size: print("WARNING: eeprom_binary truncated!", file=sys.stderr) eedata = eedata[0:ee_size] @@ -116,6 +139,11 @@ class StcGal: else: self.protocol.disconnect() return 0 + except IOError as e: + sys.stdout.flush(); + print("I/O error: %s" % e, file=sys.stderr) + self.protocol.disconnect() + return 1 except NameError as e: sys.stdout.flush(); print("Option error: %s" % e, file=sys.stderr) diff --git a/stcgal/ihex.py b/stcgal/ihex.py new file mode 100644 index 0000000..0154dd4 --- /dev/null +++ b/stcgal/ihex.py @@ -0,0 +1,202 @@ +# IHex by Kier Davis, modified for Python 3 +# Public Domain +# https://github.com/kierdavis/IHex + +import struct +import codecs + +class IHex(object): + @classmethod + def read(cls, lines): + ihex = cls() + + segbase = 0 + for line in lines: + line = line.strip() + if not line: continue + + t, a, d = ihex.parse_line(line) + if t == 0x00: + ihex.insert_data(segbase + a, d) + + elif t == 0x01: + break # Should we check for garbage after this? + + elif t == 0x02: + ihex.set_mode(16) + segbase = struct.unpack(">H", d[0:2])[0] << 4 + + elif t == 0x03: + ihex.set_mode(16) + + cs, ip = struct.unpack(">2H", d[0:2]) + ihex.set_start((cs, ip)) + + elif t == 0x04: + ihex.set_mode(32) + segbase = struct.unpack(">H", d[0:2])[0] << 16 + + elif t == 0x05: + ihex.set_mode(32) + ihex.set_start(struct.unpack(">I", d[0:4])[0]) + + else: + raise ValueError("Invalid type byte") + + return ihex + + @classmethod + def read_file(cls, fname): + f = open(fname, "rb") + ihex = cls.read(f) + f.close() + return ihex + + def __init__(self): + self.areas = {} + self.start = None + self.mode = 8 + self.row_bytes = 16 + + def set_row_bytes(self, row_bytes): + """Set output hex file row width (bytes represented per row).""" + if row_bytes < 1 or row_bytes > 0xff: + raise ValueError("Value out of range: (%r)" % row_bytes) + self.row_bytes = row_bytes + + def extract_data(self, start=None, end=None): + if start is None: + start = 0 + + if end is None: + end = 0 + result = bytes() + + for addr, data in self.areas.items(): + if addr >= start: + end = max(end, addr + len(data)) + result = result[:start] + data[start-addr:end-addr] + result[end:] + + return result + + else: + result = bytes() + + for addr, data in self.areas.items(): + if addr >= start and addr < end: + result = result[:start] + data[start-addr:end-addr] + result[end:] + + return result + + def set_start(self, start=None): + self.start = start + + def set_mode(self, mode): + self.mode = mode + + def get_area(self, addr): + for start, data in self.areas.items(): + end = start + len(data) + if addr >= start and addr <= end: + return start + + return None + + def insert_data(self, istart, idata): + iend = istart + len(idata) + + area = self.get_area(istart) + if area is None: + self.areas[istart] = idata + + else: + data = self.areas[area] + # istart - iend + len(idata) + len(data) + self.areas[area] = data[:istart-area] + idata + data[iend-area:] + + def calc_checksum(self, bytes): + total = sum(bytes) + return (-total) & 0xFF + + def parse_line(self, rawline): + if rawline[0:1] != b":": + raise ValueError("Invalid line start character (%r)" % rawline[0]) + + try: + #line = rawline[1:].decode("hex") + line = codecs.decode(rawline[1:], "hex_codec") + except: + raise ValueError("Invalid hex data") + + length, addr, type = struct.unpack(">BHB", line[:4]) + + dataend = length + 4 + data = line[4:dataend] + + #~ print line[dataend:dataend + 2], repr(line) + cs1 = line[dataend] + cs2 = self.calc_checksum(line[:dataend]) + + if cs1 != cs2: + raise ValueError("Checksums do not match") + + return (type, addr, data) + + def make_line(self, type, addr, data): + line = struct.pack(">BHB", len(data), addr, type) + line += data + line += chr(self.calc_checksum(line)) + #~ return ":" + line.encode("hex") + return ":" + line.encode("hex").upper() + "\r\n" + + def write(self): + output = "" + + for start, data in sorted(self.areas.items()): + i = 0 + segbase = 0 + + while i < len(data): + chunk = data[i:i + self.row_bytes] + + addr = start + newsegbase = segbase + + if self.mode == 8: + addr = addr & 0xFFFF + + elif self.mode == 16: + t = addr & 0xFFFF + newsegbase = (addr - t) >> 4 + addr = t + + if newsegbase != segbase: + output += self.make_line(0x02, 0, struct.pack(">H", newsegbase)) + segbase = newsegbase + + elif self.mode == 32: + newsegbase = addr >> 16 + addr = addr & 0xFFFF + + if newsegbase != segbase: + output += self.make_line(0x04, 0, struct.pack(">H", newsegbase)) + segbase = newsegbase + + output += self.make_line(0x00, addr, chunk) + + i += self.row_bytes + start += self.row_bytes + + if self.start is not None: + if self.mode == 16: + output += self.make_line(0x03, 0, struct.pack(">2H", self.start[0], self.start[1])) + elif self.mode == 32: + output += self.make_line(0x05, 0, struct.pack(">I", self.start)) + + output += self.make_line(0x01, 0, "") + return output + + def write_file(self, fname): + f = open(fname, "w") + f.write(self.write()) + f.close()