Files
ATtiny814-USB-PD-Adapter/software/tools/pymcuprog/libs/pymcuprog/hexfileutils.py
2022-09-11 11:42:08 +02:00

182 lines
7.5 KiB
Python

"""
Module providing read and write functionality towards hex files with data intended for target device memories
"""
import copy
import os
from array import array
from collections import namedtuple
from intelhex import IntelHex
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path # python 2 backport
from .deviceinfo.deviceinfokeys import DeviceMemoryInfoKeys, DeviceInfoKeys
def write_memories_to_hex(filename, memory_segments):
"""
Write a collection of memory segments to a hex file
Each segment will be written from relative offset 0 (i.e. start of each memory segment)
:param filename: Name/path of hex file to write to
:param memory_segments: list of namedtuples with two fields: data and memory_info. data contains a
byte array of raw data bytes and memory_info is a dictionary with memory information as defined
in deviceinfo.deviceinfo.DeviceMemoryInfo.
"""
hexfile = IntelHex()
for memory_segment in memory_segments:
_add_data_to_hex(hexfile, memory_segment.data, memory_segment.memory_info)
_write_hex_to_file(hexfile, filename)
def write_memory_to_hex(filename, memory_segment, offset):
"""
Write one memory segment to a hex file with data starting at relative offset given by offset parameter.
:param filename: Name/path of hex file to write to
:param memory_segment: namedtuple with two fields: data and memory_info. data contains a byte array
of raw data bytes and memory_info is a dictionary with memory information as defined in
deviceinfo.deviceinfo.DeviceMemoryInfo).
:param offset: Relative offset for the data within the memory segment
"""
hexfile = IntelHex()
_add_data_to_hex(hexfile, memory_segment.data, memory_segment.memory_info, offset)
_write_hex_to_file(hexfile, filename)
def read_memories_from_hex(filename, device_memory_info):
"""
Read the content of a hexfile
:param filename: Name/path of hex file to read from
:param device_memory_info: DeviceMemoryInfo instance for the device the hex file is intended for
:returns: list of namedtuples with three fields: data, offset and memory_info. data contains a byte array
of raw data bytes, offset is the start address within the memory the data starts at and memory_info
is a dictionary with the memory info as defined in pymcuprog.deviceinfo.deviceinfo
"""
hexfile = IntelHex()
hexfile.fromfile(filename, format='hex')
memory_segments = []
for segment in hexfile.segments():
start = segment[0]
stop = segment[1]
subsegment_start = start
subsegment_stop = start
while subsegment_stop < stop:
current_memory_info = device_memory_info.memory_info_by_address(subsegment_start,
DeviceMemoryInfoKeys.HEXFILE_ADDRESS,
DeviceMemoryInfoKeys.HEXFILE_SIZE)
if current_memory_info is None:
raise IndexError(
"Hexfile contains data at hex address 0x{:X} which is outside any memory".format(subsegment_start))
current_hexfile_address = current_memory_info[DeviceMemoryInfoKeys.HEXFILE_ADDRESS]
current_hexfile_size = current_memory_info[DeviceMemoryInfoKeys.HEXFILE_SIZE]
subsegment_stop = current_hexfile_address + current_hexfile_size
if stop < subsegment_stop:
# Reached end of segment
subsegment_stop = stop
memory_tuple = namedtuple('MemorySegment', 'data offset memory_info')
data = hexfile.tobinarray(start=subsegment_start, end=subsegment_stop - 1)
current_size = current_memory_info[DeviceMemoryInfoKeys.SIZE]
if current_hexfile_size == current_size*2:
# There are phantom bytes in the hexfile (PIC16 EEPROM), so every 2nd byte should be removed
data = remove_phantom_bytes(data)
memory_tuple.data = data
memory_tuple.memory_info = current_memory_info
memory_tuple.offset = subsegment_start - current_hexfile_address
memory_segments.append(copy.deepcopy(memory_tuple))
subsegment_start = subsegment_stop
return memory_segments
def verify_flash_from_hex(hex_filename, backend, max_read_chunk=None):
"""
Verify the contents of flash against a hex-file
:param filename: Name/path of hex-file to verify
:param device_memory_info: DeviceMemoryInfo instance for the device the hex file should be verified against
:param backend: Reference to the Backend class of pymcuprog
:returns: Boolean value indicating success or failure of the operation
"""
hexfile = IntelHex(hex_filename)
segments = hexfile.segments()
for i in range(len(segments)):
segment_data = []
for j in range(segments[i][1]-segments[i][0]):
segment_data.append(hexfile[segments[i][0]+j])
verify_status = backend.verify_memory(segment_data, 'flash', segments[i][0], max_read_chunk=max_read_chunk)
if verify_status is False:
return False
return True
def remove_phantom_bytes(data):
"""
Remove every 2nd byte from the data
"""
data_stripped = []
for index in range(0, len(data), 2):
data_stripped.append(data[index])
# Make a bin array out of the data list to be consistent with the data format of
# the data fetched directly from the hex file
data_stripped_binarray = array('B')
data_stripped_binarray.fromlist(data_stripped)
return data_stripped_binarray
def _add_data_to_hex(intelhex, data, memory_info, offset=0):
"""
Add given data starting at relative index offset to IntelHex instance intelhex
:param intelhex: IntelHex object
:param data: raw data bytes
:param memory_info: memory info as provided by pymcuprog.deviceinfo.deviceinfo
:param offset: relative offset within the memory
"""
hexfile_address_key = DeviceMemoryInfoKeys.HEXFILE_ADDRESS
hexfile_size_key = DeviceMemoryInfoKeys.HEXFILE_SIZE
size_key = DeviceMemoryInfoKeys.SIZE
name = memory_info[DeviceInfoKeys.NAME]
if offset+len(data) > memory_info[hexfile_size_key]:
raise IndexError(
"Attempting to write outside boundary of {} memory ({} bytes starting at offset {})".format(name,
len(data),
offset))
hex_offset = memory_info[hexfile_address_key] + offset
if memory_info[hexfile_size_key] == memory_info[size_key]*2:
# Hex file should contain one phantom byte per data byte in the hex file (PIC16 EEPROM)
for i, dat in enumerate(data):
intelhex[i*2 + hex_offset] = data[i]
intelhex[i*2 + 1 + hex_offset] = 0 & 0xFF
else:
for i, dat in enumerate(data):
intelhex[i + hex_offset] = dat
def _write_hex_to_file(intelhex, filename):
"""
Write intelhex object to file.
Directories will be created if path does not exist
:param intelhex: IntelHex instance
:param filename: Name/path to write intelhex object to
"""
directory = os.path.dirname(filename)
if directory != '' and not os.path.exists(directory):
Path(directory).mkdir(exist_ok=True, parents=True)
intelhex.write_hex_file(filename)