From 9c8a34465a3aae28563c7cc2efead5d954af7e4f Mon Sep 17 00:00:00 2001 From: Andrea Zoppi Date: Wed, 14 Feb 2024 21:37:06 +0100 Subject: [PATCH] Added Microchip SQTP parsing + minor fixes Microchip SQTP derives from Interl HEX, but it isn't properly supported because it addresses by 2 or 4 bytes per word, instead of direct bytes. Microchip SQTP will likely become a separate format in the future. --- src/hexrec/formats/ihex.py | 2 +- src/hexrec/formats/mos.py | 2 +- src/hexrec/formats/raw.py | 2 +- src/hexrec/formats/srec.py | 2 +- src/hexrec/formats/titxt.py | 5 ++- src/hexrec/formats/xtek.py | 2 +- tests/test_formats_ihex.py | 40 +++++++++++++++++++ .../microchip_auxmem_dsPIC33EP256MU806.hex | 7 ++++ .../microchip_bootmem_PIC32MX110F016B.hex | 7 ++++ .../microchip_eeprom_PIC12F1840.hex | 7 ++++ .../microchip_eeprom_PIC18F1220.hex | 7 ++++ .../microchip_progmem_PIC18F1220.hex | 6 +++ .../microchip_progmem_PIC32MX360F512L.hex | 7 ++++ .../microchip_userid_PIC12F1501.hex | 7 ++++ .../microchip_userid_PIC32MX360F512L.hex | 7 ++++ tests/test_formats_mos.py | 2 + tests/test_formats_xtek.py | 1 + 17 files changed, 107 insertions(+), 6 deletions(-) create mode 100644 tests/test_formats_ihex/microchip_auxmem_dsPIC33EP256MU806.hex create mode 100644 tests/test_formats_ihex/microchip_bootmem_PIC32MX110F016B.hex create mode 100644 tests/test_formats_ihex/microchip_eeprom_PIC12F1840.hex create mode 100644 tests/test_formats_ihex/microchip_eeprom_PIC18F1220.hex create mode 100644 tests/test_formats_ihex/microchip_progmem_PIC18F1220.hex create mode 100644 tests/test_formats_ihex/microchip_progmem_PIC32MX360F512L.hex create mode 100644 tests/test_formats_ihex/microchip_userid_PIC12F1501.hex create mode 100644 tests/test_formats_ihex/microchip_userid_PIC32MX360F512L.hex diff --git a/src/hexrec/formats/ihex.py b/src/hexrec/formats/ihex.py index 0264f24..28dbf90 100644 --- a/src/hexrec/formats/ihex.py +++ b/src/hexrec/formats/ihex.py @@ -754,7 +754,7 @@ def validate_records( >>> from hexrec import IhexFile >>> records = [IhexFile.Record.create_data(123, b'abc')] >>> file = IhexFile.from_records(records) - >>> file.validate_records() + >>> _ = file.validate_records() Traceback (most recent call last): ... ValueError: missing end of file record diff --git a/src/hexrec/formats/mos.py b/src/hexrec/formats/mos.py index 402fcdb..25160b0 100644 --- a/src/hexrec/formats/mos.py +++ b/src/hexrec/formats/mos.py @@ -568,7 +568,7 @@ def validate_records( >>> from hexrec import MosFile >>> records = [MosFile.Record.create_data(123, b'abc')] >>> file = MosFile.from_records(records) - >>> file.validate_records() + >>> _ = file.validate_records() Traceback (most recent call last): ... ValueError: missing end of file record diff --git a/src/hexrec/formats/raw.py b/src/hexrec/formats/raw.py index 38675e7..6a444eb 100644 --- a/src/hexrec/formats/raw.py +++ b/src/hexrec/formats/raw.py @@ -344,7 +344,7 @@ def validate_records( >>> from hexrec import RawFile >>> records = [RawFile.Record.create_data(123, b'abc')] >>> file = RawFile.from_records(records) - >>> file.validate_records() + >>> _ = file.validate_records() Traceback (most recent call last): ... ValueError: first record address not zero diff --git a/src/hexrec/formats/srec.py b/src/hexrec/formats/srec.py index fff860e..8c66626 100644 --- a/src/hexrec/formats/srec.py +++ b/src/hexrec/formats/srec.py @@ -1095,7 +1095,7 @@ def validate_records( >>> from hexrec import SrecFile >>> records = [SrecFile.Record.create_data(123, b'abc')] >>> file = SrecFile.from_records(records) - >>> file.validate_records() + >>> _ = file.validate_records() Traceback (most recent call last): ... ValueError: missing start record diff --git a/src/hexrec/formats/titxt.py b/src/hexrec/formats/titxt.py index 2476d0a..ff93494 100644 --- a/src/hexrec/formats/titxt.py +++ b/src/hexrec/formats/titxt.py @@ -35,6 +35,7 @@ from typing import Any from typing import Mapping from typing import Optional +from typing import Sequence from typing import Type from typing import TypeVar from typing import cast as _cast @@ -396,6 +397,8 @@ def validate( class TiTxtFile(BaseFile): r"""Texas Instruments TI-TXT file object.""" + FILE_EXT: Sequence[str] = ['.txt'] + Record: Type[TiTxtRecord] = TiTxtRecord @classmethod @@ -537,7 +540,7 @@ def validate_records( >>> from hexrec import TiTxtFile >>> records = [TiTxtFile.Record.create_data(456, b'abc')] >>> file = TiTxtFile.from_records(records) - >>> file.validate_records() + >>> _ = file.validate_records() Traceback (most recent call last): ... ValueError: missing end of file record diff --git a/src/hexrec/formats/xtek.py b/src/hexrec/formats/xtek.py index f74bd97..ca0a7d0 100644 --- a/src/hexrec/formats/xtek.py +++ b/src/hexrec/formats/xtek.py @@ -676,7 +676,7 @@ def validate_records( >>> from hexrec import XtekFile >>> records = [XtekFile.Record.create_data(123, b'abc')] >>> file = XtekFile.from_records(records) - >>> file.validate_records() + >>> _ = file.validate_records() Traceback (most recent call last): ... ValueError: missing end of file record diff --git a/tests/test_formats_ihex.py b/tests/test_formats_ihex.py index e08d09e..ee5a980 100644 --- a/tests/test_formats_ihex.py +++ b/tests/test_formats_ihex.py @@ -375,6 +375,27 @@ def test_parse_syntax(self): record = IhexRecord.parse(line) record.validate() + # https://developerhelp.microchip.com/xwiki/bin/view/software-tools/ipe/sqtp-file-format-specification/example/ + def test_parse_microchip_sqtp(self): + lines = [ + b':02000004740086\r\n', + b':04000000BF7087ED59\r\n', + b':0400000043BD7F3449\r\n', + b':00000001FF\r\n', + ] + records = [ + IhexRecord(ELA, count=0x02, address=0x0000, checksum=0x86, data=b'\x74\x00'), + IhexRecord(DATA, count=0x04, address=0x0000, checksum=0x59, data=b'\xBF\x70\x87\xED'), + IhexRecord(DATA, count=0x04, address=0x0000, checksum=0x49, data=b'\x43\xBD\x7F\x34'), + IhexRecord(EOF, count=0x00, address=0x0000, checksum=0xFF, data=b''), + ] + for line, expected in zip(lines, records): + actual = IhexRecord.parse(line) + actual.validate() + expected = _cast(IhexRecord, expected) + expected.validate() + assert actual == expected + # https://en.wikipedia.org/wiki/Intel_HEX#Record_types def test_parse_wikipedia(self): lines = [ @@ -789,6 +810,25 @@ def test_parse_errors(self): with io.BytesIO(buffer) as stream: IhexFile.parse(stream, ignore_errors=False) + # https://developerhelp.microchip.com/xwiki/bin/view/software-tools/ipe/sqtp-file-format-specification/examples/ + # FIXME: Microchip SQTP addresses are word-/dword-based --> add 'addressing' meta & property to apply/update records + def test_parse_file_microchip_sqtp(self, datapath): + filenames = [ + 'microchip_auxmem_dsPIC33EP256MU806.hex', + 'microchip_bootmem_PIC32MX110F016B.hex', + 'microchip_eeprom_PIC12F1840.hex', + 'microchip_eeprom_PIC18F1220.hex', + 'microchip_progmem_PIC18F1220.hex', + 'microchip_progmem_PIC32MX360F512L.hex', + 'microchip_userid_PIC12F1501.hex', # fixed checksums + 'microchip_userid_PIC32MX360F512L.hex', + ] + for filename in filenames: + path = str(datapath / filename) + with open(path, 'rb') as stream: + file = IhexFile.parse(stream) + file.validate_records() + # https://en.wikipedia.org/wiki/Intel_HEX#File_example def test_parse_file_wikipedia(self, datapath): path = str(datapath / 'wikipedia.hex') diff --git a/tests/test_formats_ihex/microchip_auxmem_dsPIC33EP256MU806.hex b/tests/test_formats_ihex/microchip_auxmem_dsPIC33EP256MU806.hex new file mode 100644 index 0000000..c40560b --- /dev/null +++ b/tests/test_formats_ihex/microchip_auxmem_dsPIC33EP256MU806.hex @@ -0,0 +1,7 @@ +:0200000401FFFA +:0400000000000000FC +:0400000001000000FB +:0400000002000000FA +:0400000003000000F9 +:0400000004000000F8 +:00000001FF diff --git a/tests/test_formats_ihex/microchip_bootmem_PIC32MX110F016B.hex b/tests/test_formats_ihex/microchip_bootmem_PIC32MX110F016B.hex new file mode 100644 index 0000000..f4ef244 --- /dev/null +++ b/tests/test_formats_ihex/microchip_bootmem_PIC32MX110F016B.hex @@ -0,0 +1,7 @@ +:020000047F007B +:0400000039268EC748 +:040000001FB777E2CD +:04000000031E7E3D20 +:04000000D56F64E272 +:04000000F993C2A707 +:00000001FF diff --git a/tests/test_formats_ihex/microchip_eeprom_PIC12F1840.hex b/tests/test_formats_ihex/microchip_eeprom_PIC12F1840.hex new file mode 100644 index 0000000..f0b34f1 --- /dev/null +++ b/tests/test_formats_ihex/microchip_eeprom_PIC12F1840.hex @@ -0,0 +1,7 @@ +:020000040000FA +:02F0000000000E +:02F0000001000D +:02F0000002000C +:02F0000003000B +:02F0000004000A +:00000001FF diff --git a/tests/test_formats_ihex/microchip_eeprom_PIC18F1220.hex b/tests/test_formats_ihex/microchip_eeprom_PIC18F1220.hex new file mode 100644 index 0000000..a7716dc --- /dev/null +++ b/tests/test_formats_ihex/microchip_eeprom_PIC18F1220.hex @@ -0,0 +1,7 @@ +:0200000400F00A +:020000000000FE +:020000000100FD +:020000000200FC +:020000000300FB +:020000000400FA +:00000001FF diff --git a/tests/test_formats_ihex/microchip_progmem_PIC18F1220.hex b/tests/test_formats_ihex/microchip_progmem_PIC18F1220.hex new file mode 100644 index 0000000..0646bc4 --- /dev/null +++ b/tests/test_formats_ihex/microchip_progmem_PIC18F1220.hex @@ -0,0 +1,6 @@ +:04000000000C000CE4 +:04000000010C000CE3 +:04000000020C000CE2 +:04000000030C000CE1 +:04000000040C000CE0 +:00000001FF diff --git a/tests/test_formats_ihex/microchip_progmem_PIC32MX360F512L.hex b/tests/test_formats_ihex/microchip_progmem_PIC32MX360F512L.hex new file mode 100644 index 0000000..8710c9b --- /dev/null +++ b/tests/test_formats_ihex/microchip_progmem_PIC32MX360F512L.hex @@ -0,0 +1,7 @@ +:02000004740086 +:0400000000000000FC +:0400000001000000FB +:0400000002000000FA +:0400000003000000F9 +:0400000004000000F8 +:00000001FF diff --git a/tests/test_formats_ihex/microchip_userid_PIC12F1501.hex b/tests/test_formats_ihex/microchip_userid_PIC12F1501.hex new file mode 100644 index 0000000..cf9a798 --- /dev/null +++ b/tests/test_formats_ihex/microchip_userid_PIC12F1501.hex @@ -0,0 +1,7 @@ +:020000040001F9 +:040000007E34CF3447 +:040000009034C5343F +:040000000B34113478 +:04000000F234F334AF +:040000001C34683410 +:00000001FF diff --git a/tests/test_formats_ihex/microchip_userid_PIC32MX360F512L.hex b/tests/test_formats_ihex/microchip_userid_PIC32MX360F512L.hex new file mode 100644 index 0000000..d949df3 --- /dev/null +++ b/tests/test_formats_ihex/microchip_userid_PIC32MX360F512L.hex @@ -0,0 +1,7 @@ +:020000047F007B +:04BFC000000000007D +:04BFC000010000007C +:04BFC000020000007B +:04BFC000030000007A +:04BFC0000400000079 +:00000001FF diff --git a/tests/test_formats_mos.py b/tests/test_formats_mos.py index a756156..3e616a9 100644 --- a/tests/test_formats_mos.py +++ b/tests/test_formats_mos.py @@ -682,12 +682,14 @@ def test_validate_records_data_order(self): MosRecord.create_eof(2)] file = MosFile.from_records(records) file.validate_records(data_ordering=True) + file.validate_records(data_ordering=False) records = [MosRecord.create_data(10, b'xyz'), MosRecord.create_data(14, b'abc'), MosRecord.create_eof(2)] file = MosFile.from_records(records) file.validate_records(data_ordering=True) + file.validate_records(data_ordering=False) def test_validate_records_raises_records(self): file = MosFile() diff --git a/tests/test_formats_xtek.py b/tests/test_formats_xtek.py index 89c6dde..9e7bac9 100644 --- a/tests/test_formats_xtek.py +++ b/tests/test_formats_xtek.py @@ -855,3 +855,4 @@ def test_validate_records_start_within_data(self): file = XtekFile.from_records(records) assert file.startaddr == 0x1234 file.validate_records(start_within_data=True) + file.validate_records(start_within_data=False)