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.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
"""Exercise the release gate without TI tools or hardware."""
|
||||
"""Exercise the current board-specific SCI8 gate without TI tools or hardware."""
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
@@ -12,34 +12,35 @@ LIMIT = (0x3FFF - (128 + 1)) * 2
|
||||
|
||||
def image(size):
|
||||
header = struct.pack('<H', 0x08AA) + bytes(16) + struct.pack('<HH', 0, 0x8000)
|
||||
words = (size - 30) // 2
|
||||
return header + struct.pack('<HHH', words, 0, 0x8000) + bytes(words * 2) + bytes(2)
|
||||
blocks = max(1, (size-24+131075)//131076)
|
||||
remaining = (size-24-blocks*6)//2
|
||||
result = bytearray(header)
|
||||
for _ in range(blocks):
|
||||
words = min(65535, remaining)
|
||||
result += struct.pack('<HHH', words, 0, 0x8000) + bytes(words*2)
|
||||
remaining -= words
|
||||
return bytes(result)+bytes(2)
|
||||
|
||||
|
||||
class FirmwareGateTest(unittest.TestCase):
|
||||
def check_image(self, data, success):
|
||||
def check_image(self, data, success, profile="AT25256B"):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
source = Path(directory) / 'candidate.pending'
|
||||
output = Path(directory) / 'firmware.bin'
|
||||
source = Path(directory) / 'candidate.bin'
|
||||
source.write_bytes(data)
|
||||
result = subprocess.run([
|
||||
shutil.which('powershell') or 'pwsh', '-NoProfile', '-ExecutionPolicy', 'Bypass',
|
||||
'-File', str(ROOT / 'check_firmware.ps1'), '-Path', str(source),
|
||||
'-PublishPath', str(output),
|
||||
sys.executable, str(ROOT / 'tests/gas167/check_image.py'),
|
||||
str(source), '--eeprom', profile,
|
||||
], capture_output=True)
|
||||
self.assertEqual(result.returncode == 0, success, result.stderr.decode(errors='replace'))
|
||||
self.assertEqual(output.exists(), success)
|
||||
if success:
|
||||
self.assertEqual(output.read_bytes(), data)
|
||||
self.assertFalse(source.exists())
|
||||
self.assertEqual(result.returncode == 0, success,
|
||||
result.stderr.decode(errors='replace'))
|
||||
self.assertEqual(source.read_bytes(), data)
|
||||
return result.stdout + result.stderr
|
||||
|
||||
def test_exact_boundary_is_accepted(self):
|
||||
self.check_image(image(LIMIT), True)
|
||||
|
||||
def test_34a95bc5_size_is_rejected(self):
|
||||
diagnostic = self.check_image(image(32518), False)
|
||||
self.assertIn(b'overlaps saved parameters by 10 bytes', diagnostic)
|
||||
self.check_image(image(32518), False)
|
||||
|
||||
def test_missing_boot_terminator_is_rejected(self):
|
||||
self.check_image(image(100)[:-2], False)
|
||||
@@ -47,6 +48,15 @@ class FirmwareGateTest(unittest.TestCase):
|
||||
def test_partial_block_is_rejected(self):
|
||||
self.check_image(image(100)[:-10], False)
|
||||
|
||||
def test_m95_capacity_does_not_expand_code_ram(self):
|
||||
diagnostic = self.check_image(image(261884), False, "M95M02")
|
||||
self.assertIn(b'outside code RAM', diagnostic)
|
||||
self.check_image(image(261886), False, "M95M02")
|
||||
|
||||
def test_m95_image_rejected_for_small_part(self):
|
||||
self.check_image(image(32734), True, "M95M02")
|
||||
self.check_image(image(32734), False, "AT25256B")
|
||||
|
||||
def test_wrong_format_is_rejected(self):
|
||||
self.check_image(bytes(100), False)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user