Files
Balsam_167/tests/gas167/check_image.py
Andrey 5d9818291b feat: add M95M02-only GAS and EEPROM profiles
Expose the existing registers through Modbus and service CAN using one GAS provider only in M95M02 builds.
Include the EEPROM layout/driver, LOAD protection and calibration, firmware identity and shared CCS build workflow.
Add transport/profile and SCI8 validation tests, documentation and the checked M95M02 firmware image.
2026-10-09 16:17:37 +03:00

49 lines
2.0 KiB
Python

"""Validate the PM35 RAM-load SCI8 image against the selected EEPROM profile."""
import argparse
from pathlib import Path
import struct
def check_image(path, eeprom):
data = path.read_bytes()
limit = {"AT25256B": 32508, "M95M02": 261884}[eeprom]
if not 24 <= len(data) <= limit:
raise ValueError(f"Image size {len(data)} outside 24..{limit}")
if struct.unpack_from("<H", data)[0] != 0x08AA:
raise ValueError("Not a SCI8 boot image")
high, low = struct.unpack_from("<HH", data, 18)
entry = (high << 16) | low
position, blocks = 22, []
while True:
if position + 2 > len(data):
raise ValueError("Missing boot terminator")
words = struct.unpack_from("<H", data, position)[0]
position += 2
if not words:
if position != len(data):
raise ValueError("Trailing bytes after boot terminator")
break
if position + 4 + words * 2 > len(data):
raise ValueError("Truncated boot block")
high, low = struct.unpack_from("<HH", data, position)
address = (high << 16) | low
end = address + words
if not 0x8000 <= address < end <= 0xD000:
raise ValueError(f"Boot destination {address:#x}..{end:#x} outside code RAM")
if any(address < old_end and old_start < end for old_start, old_end in blocks):
raise ValueError("Overlapping boot blocks")
blocks.append((address, end))
position += 4 + words * 2
if not any(start <= entry < end for start, end in blocks):
raise ValueError("Entry point not loaded by the boot table")
return len(data), len(blocks), limit - len(data)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("image", type=Path)
parser.add_argument("--eeprom", choices=("AT25256B", "M95M02"), required=True)
args = parser.parse_args()
size, blocks, free = check_image(args.image, args.eeprom)
print(f"PASS SCI8 {args.eeprom}: {size} bytes, {blocks} RAM blocks, {free} bytes free")