mirror of
https://github.com/wagiminator/ATtiny814-USB-PD-Adapter.git
synced 2025-08-08 12:50:58 +03:00
182 lines
7.5 KiB
Python
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)
|