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).
This commit is contained in:
Grigori Goronzy 2015-11-24 00:33:22 +01:00
parent f452ca226e
commit 63327e3498
2 changed files with 232 additions and 2 deletions

View File

@ -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)

202
stcgal/ihex.py Normal file
View File

@ -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()