Skip to content

Commit d43e1ed

Browse files
committed
Add Extended Address resolver parameter to 'write_hex_file' function
New 'ext_addr_mode' parameter responsible for Extended Address records.
1 parent 5c15003 commit d43e1ed

File tree

3 files changed

+378
-22
lines changed

3 files changed

+378
-22
lines changed

docs/manual/part2-5.txt

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ including HEX, bin, or python dictionaries.
66
You can write out HEX data contained in object by method ``.write_hex_file(f)``.
77
Parameter ``f`` should be filename or file-like object.
88
Note that this can include builtins like sys.stdout.
9-
Also you can use the universal tofile.
9+
Also you can use the universal ``tofile``.
1010

1111
To convert data of IntelHex object to HEX8 file format without actually saving it
1212
to disk you can use the builtin StringIO file-like object, e.g.::
@@ -25,10 +25,37 @@ Variable ``hexstr`` will contain a string with the content of a HEX8 file.
2525
You can customize hex file output with following optional arguments
2626
to ``write_hex_file`` call:
2727

28-
* ``write_start_addr`` - you can disable start address record in new hex file;
28+
* ``write_start_addr`` - you can disable start address record in new hex file.
2929
* ``eolstyle`` - you can force ``CRLF`` line endings in new hex file.
3030
* ``byte_count`` - you can control how many bytes should be written to each
3131
data record.
32+
* ``ext_addr_mode`` - you can decide how extended address records should be
33+
resolved and written in new hex file (explained below).
34+
35+
Extended Address records are describing an address in binary space from which
36+
a block of data is being written. Normally without those record we could write
37+
only 64KB of binary data into one single ``.hex`` file. This is because in
38+
one signle data record there are only 2 bytes for address. To have possibility
39+
to write more, Extended Records are needed to increase address resolution.
40+
Currently there are two types of records defined in IntelHex format:
41+
42+
* ``Extended Segment Address`` [02] - you can write up to 1MB of binary data
43+
* ``Extended Linear Address`` [04] - you can write up to 4GB of binary data
44+
45+
There are 4 modes given by ``ext_addr_mode`` parameter to support every type
46+
of memory address resolution:
47+
48+
* ``linear`` - forces to use Extended Linear Address records,
49+
in most of cases this mode is commonly desired (default).
50+
* ``segment`` - forces to use Extended Segment Address records.
51+
* ``none`` - forces to not use any Extended Address records (legacy option).
52+
* ``auto`` - automatically decides which mode to use using last known adress
53+
where data need to be written.
54+
55+
Whenever data overflow for different adres resoution is detected adequat
56+
exception will thrown. Mixing of ``linear`` and ``segment`` records isn't
57+
allowed. There won't be any Extened Address records will written in
58+
any mode if data to write need to be placed in address under 64KB.
3259

3360

3461
Data converters

intelhex/__init__.py

Lines changed: 80 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ def _get_eol_textfile(eolstyle, platform):
544544
raise ValueError("wrong eolstyle %s" % repr(eolstyle))
545545
_get_eol_textfile = staticmethod(_get_eol_textfile)
546546

547-
def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count=16):
547+
def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count=16, ext_addr_mode='linear'):
548548
"""Write data to file f in HEX format.
549549
550550
@param f filename or file-like object for writing
@@ -556,20 +556,55 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
556556
for output file on different platforms.
557557
Supported eol styles: 'native', 'CRLF'.
558558
@param byte_count number of bytes in the data field
559+
@param ext_addr_mode used this to decide which record type to
560+
to write for Extended Address records:
561+
Linear (32-bit addressing) or Segment (20-bit addressing)
562+
or without Extended Address records (16-bit addressing)
563+
Supported modes: 'linear', 'segment', 'none', 'auto'.
559564
"""
560565
if byte_count > 255 or byte_count < 1:
561566
raise ValueError("wrong byte_count value: %s" % byte_count)
562-
fwrite = getattr(f, "write", None)
563-
if fwrite:
564-
fobj = f
565-
fclose = None
566-
else:
567-
fobj = open(f, 'w')
568-
fwrite = fobj.write
569-
fclose = fobj.close
570567

571568
eol = IntelHex._get_eol_textfile(eolstyle, sys.platform)
572569

570+
addresses = dict_keys(self._buf)
571+
addresses.sort()
572+
addr_len = len(addresses)
573+
minaddr = addresses[0] if addr_len else 0
574+
maxaddr = addresses[-1] if addr_len else 0
575+
576+
# make parameter case-insensitive
577+
ext_addr_mode = ext_addr_mode.lower()
578+
# resolve extended address type
579+
if ext_addr_mode == 'linear':
580+
# enforces Extended Linear Address record type (default)
581+
extaddr_rectyp = 4
582+
elif ext_addr_mode == 'segment':
583+
# enforces Extended Segment Address record type
584+
extaddr_rectyp = 2
585+
elif ext_addr_mode == 'none':
586+
# enforces no Extended Address records format
587+
extaddr_rectyp = 0
588+
elif ext_addr_mode == 'auto':
589+
# Extended Address record type is resolved by Max Address
590+
if maxaddr > 0x0FFFFF:
591+
extaddr_rectyp = 4
592+
else: # 1MB sapcing is max for Segement
593+
extaddr_rectyp = 2
594+
else:
595+
raise ValueError('ext_addr_mode should be one of:'
596+
' "linear", "segment", "none", "auto";'
597+
' got %r instead' % ext_addr_mode)
598+
599+
# check max address with resolved format
600+
if addr_len:
601+
if extaddr_rectyp == 4 and maxaddr > 0xFFFFFFFF:
602+
raise LinearSpacingOverflowError(overflwd_len=(maxaddr-0xFFFFFFFF))
603+
elif extaddr_rectyp == 2 and maxaddr > 0x0FFFFF:
604+
raise SegmentSpacingOverflowError(overflwd_len=(maxaddr-0x0FFFFF))
605+
elif extaddr_rectyp == 0 and maxaddr > 0xFFFF:
606+
raise BasicSpacingOverflowError(overflwd_len=(maxaddr-0xFFFF))
607+
573608
# Translation table for uppercasing hex ascii string.
574609
# timeit shows that using hexstr.translate(table)
575610
# is faster than hexstr.upper():
@@ -581,6 +616,15 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
581616
# Python 2
582617
table = ''.join(chr(i).upper() for i in range_g(256))
583618

619+
fwrite = getattr(f, "write", None)
620+
if fwrite:
621+
fobj = f
622+
fclose = None
623+
else:
624+
fobj = open(f, 'w')
625+
fwrite = fobj.write
626+
fclose = fobj.close
627+
584628
# start address record if any
585629
if self.start_addr and write_start_addr:
586630
keys = dict_keys(self.start_addr)
@@ -623,19 +667,13 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
623667
raise InvalidStartAddressValueError(start_addr=self.start_addr)
624668

625669
# data
626-
addresses = dict_keys(self._buf)
627-
addresses.sort()
628-
addr_len = len(addresses)
629670
if addr_len:
630-
minaddr = addresses[0]
631-
maxaddr = addresses[-1]
632-
633671
if maxaddr > 65535:
634672
need_offset_record = True
635673
else:
636674
need_offset_record = False
637-
high_ofs = 0
638675

676+
high_ofs = 0
639677
cur_addr = minaddr
640678
cur_ix = 0
641679

@@ -645,9 +683,15 @@ def write_hex_file(self, f, write_start_addr=True, eolstyle='native', byte_count
645683
bin[0] = 2 # reclen
646684
bin[1] = 0 # offset msb
647685
bin[2] = 0 # offset lsb
648-
bin[3] = 4 # rectyp
649-
high_ofs = int(cur_addr>>16)
686+
bin[3] = extaddr_rectyp # rectyp
687+
688+
if extaddr_rectyp == 4:
689+
high_ofs = int(cur_addr>>16)
690+
else: # extaddr_rectyp == 2:
691+
# 0x00X0000 => 0xX000
692+
high_ofs = int((cur_addr & 0x00F0000) >> 4)
650693
b = divmod(high_ofs, 256)
694+
651695
bin[4] = b[0] # msb of high_ofs
652696
bin[5] = b[1] # lsb of high_ofs
653697
bin[6] = (-sum(bin)) & 0x0FF # chksum
@@ -1094,7 +1138,7 @@ def bin2hex(fin, fout, offset=0):
10941138
return 1
10951139

10961140
try:
1097-
h.tofile(fout, format='hex')
1141+
h.write_hex_file(fout)
10981142
except IOError:
10991143
e = sys.exc_info()[1] # current exception
11001144
txt = "ERROR: Could not write to file: %s: %s" % (fout, str(e))
@@ -1279,6 +1323,10 @@ def ascii_hex_to_int(ascii):
12791323
# BadAccess16bit - not enough data to read 16 bit value (deprecated, see NotEnoughDataError)
12801324
# NotEnoughDataError - not enough data to read N contiguous bytes
12811325
# EmptyIntelHexError - requested operation cannot be performed with empty object
1326+
# OverflowError - data overflow general error
1327+
# LinearSpacingOverflowError - 32-bit address spacing data overflow
1328+
# SegmentSpacingOverflowError - 20-bit address spacing data overflow
1329+
# BasicSpacingOverflowError - 16-bit address spacing data overflow
12821330

12831331
class IntelHexError(Exception):
12841332
'''Base Exception class for IntelHex module'''
@@ -1368,3 +1416,16 @@ class BadAccess16bit(NotEnoughDataError):
13681416

13691417
class EmptyIntelHexError(IntelHexError):
13701418
_fmt = "Requested operation cannot be executed with empty object"
1419+
1420+
1421+
class OverflowError(IntelHexError):
1422+
_fmt = 'Base class for overflowed data'
1423+
1424+
class LinearSpacingOverflowError(OverflowError):
1425+
_fmt = 'Data overflow Linear (32-bit) address spacing. Overflowed data: %(overflwd_len)d bytes'
1426+
1427+
class SegmentSpacingOverflowError(OverflowError):
1428+
_fmt = 'Data overflow Segment (20-bit) address spacing. Overflowed data: %(overflwd_len)d bytes'
1429+
1430+
class BasicSpacingOverflowError(OverflowError):
1431+
_fmt = 'Data overflow 16-bit address spacing. Overflowed data: %(overflwd_len)d bytes'

0 commit comments

Comments
 (0)