Files
AMI/scripts/verify_as_status_firmware.py

87 lines
4.4 KiB
Python

"""Verify actual ARM CAN -> AS/VCU display -> UART code with mocked HAL I/O."""
import argparse
import hashlib
import json
from pathlib import Path
from verify_wheel_speed_firmware import Firmware, ROOT
from send_vcu_status_can import build_payload
def verify(path):
checks=[]
f=Firmware(path)
def states():
return list(f.uc.mem_read(f.symbols['vcu_display_state'],4))
def flush():
f.uc.mem_write(f.symbols['vcu_display_dirty'],b'\x0f')
f.commands.clear()
f.drain()
expected=[f'{prefix}={value}' for prefix,value in zip(
('current_task','lock_state','ebs_state','as_state'),states())]
assert all(cmd in f.commands for cmd in expected),(expected,f.commands)
assert states()==[7,0,0,6]
flush()
checks.append('boot: all four unknown; complete UART commands end in real FF FF FF')
for port in (1,2):
for task in range(7):
for as_state in range(6):
for flags in range(16):
f.now=1000
f.receive(build_payload(task,task&1,(task+1)&1,flags,255,as_state),port,ident=0x183)
expected=[task if flags&1 else 7,
(1 if task&1 else 2) if flags&2 else 0,
(1 if (task+1)&1 else 2) if flags&4 else 0,
as_state if flags&8 else 6]
assert states()==expected,(port,task,as_state,flags,states())
flush()
checks.append(f'CAN{port}: all 7 tasks x 6 AS states x 16 independent validity combinations; UART checked')
for flags in range(8):
f.receive(build_payload(2,1,1,15,0,4),ident=0x183)
f.receive(build_payload(2,1,1,flags,0,version=1),ident=0x183)
assert states()[3]==6
flush()
checks.append('legacy v1 always clears previous v2 AS to unknown; all 8 legacy flags accepted')
baseline=build_payload(2,1,1,15,0,1)
malformed=[]
for index,value in [(0,7),(1,2),(2,2),(3,16),(5,6),(5,255),(6,1),(7,0),(7,3)]:
b=bytearray(baseline);b[index]=value;malformed.append((bytes(b),{}))
b=bytearray(baseline);b[3]=7;b[5]=6;malformed.append((bytes(b),{}))
for index,value in [(3,8),(5,1),(6,1)]:
b=bytearray(build_payload(2,1,1,7,0,version=1));b[index]=value;malformed.append((bytes(b),{}))
for opts in ({'dlc':7},{'dlc':9},{'id_type':0x40000000},{'remote':0x20000000},{'fd':0x00200000}):
malformed.append((baseline,opts))
for port in (1,2):
for data,opts in malformed:
f.now=2000;f.receive(baseline,port,ident=0x183)
before=states();f.now=2200;f.receive(data,port,ident=0x183,**opts)
assert states()==before
assert f.read_u32('vcu_status_last_rx_ms')==2000
f.service(2501);assert states()==[7,0,0,6]
checks.append('both ports: malformed DLC/version/enum/reserved/extended/remote/FD rejected without timeout renewal')
for start,boundary,expired in ((0,500,501),(0xFFFFFF00,244,245)):
f=Firmware(path)
f.now=start;f.receive(baseline,ident=0x183)
f.service(boundary);assert states()==[2,1,1,1]
f.service(expired);assert states()==[7,0,0,6]
if f.now < 800: f.now=800
flush()
checks.append('timeout at >500 ms, tick zero and wrap verified; all four unknown transmitted')
f=Firmware(path);f.now=1000;f.receive(baseline,ident=0x183)
f.now=1400;f.receive(bytes(4),ident=0x6A2)
f.service(1501);assert states()==[7,0,0,6]
f.now=1502;f.receive(baseline,ident=0x183);flush();assert states()==[2,1,1,1]
f.now=6502;f.receive(baseline,ident=0x183)
f.commands.clear();f.service();f.drain()
assert 'as_state=1' in f.commands
checks.append('other CAN cannot renew AS; same-value recovery and periodic resync UART work')
return dict(elf=str(path.relative_to(ROOT)),sha256=hashlib.sha256(path.read_bytes()).hexdigest(),
result='passed',checks=checks,evidence='ARM emulation with mocked HAL; no physical CAN/HMI')
if __name__=='__main__':
p=argparse.ArgumentParser(description=__doc__)
p.add_argument('--output',type=Path)
a=p.parse_args()
result=[verify(ROOT/f'firmware/build/{mode}/AMI_Controller.elf') for mode in ('Debug','Release')]
text=json.dumps(result,ensure_ascii=False,indent=2)
if a.output:a.output.parent.mkdir(parents=True,exist_ok=True);a.output.write_text(text+'\n',encoding='utf-8')
print(text)