EMualtor adjustments
This commit is contained in:
@@ -26,9 +26,14 @@ AUTONOMOUS_TX_REPORT_LABEL = "loc_BB43"
|
||||
MAIN_REPORT_GATE_ENTRY = 0x3FD3
|
||||
MAIN_REPORT_GATE_CALL = 0x3FEB
|
||||
SESSION_GATE_ENTRY = 0x3FEF
|
||||
IDLE_REPORT_GATE_ENTRY = 0x4046
|
||||
IDLE_REPORT_QUEUE_WRITE = 0x4067
|
||||
IDLE_REPORT_GATE_END = 0x4070
|
||||
QUEUE_REPORT_ENTRY = 0xBAF2
|
||||
RESEND_GATE_ENTRY = 0xBE9E
|
||||
PERIODIC_RESEND_ENTRY = 0xBED5
|
||||
FRT1_OCIA_ENTRY = 0xBEEA
|
||||
FRT2_OCIA_ENTRY = 0xBF23
|
||||
INDEX_DECODER_ADDRESS = 0x622B
|
||||
INDEX_DECODER_LABEL = "loc_622B"
|
||||
CHECKSUM_SEED = 0x5A
|
||||
@@ -82,6 +87,8 @@ STATE_VARIABLES = {
|
||||
0xF9B5: "event_queue_write_or_pending_cursor_candidate",
|
||||
0xF9B9: "event_queue_base_or_current_slot_candidate",
|
||||
0xF9C0: "serial_tx_busy_timer_candidate",
|
||||
0xF9C4: "idle_heartbeat_gate_countdown_candidate",
|
||||
0xF9C5: "rx_session_timeout_candidate",
|
||||
0xF9C6: "autonomous_report_period_timer_candidate",
|
||||
0xF9C8: "autonomous_report_resend_countdown_candidate",
|
||||
}
|
||||
@@ -132,6 +139,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
|
||||
"gate_queue_model": None,
|
||||
"tx_report_model": None,
|
||||
"periodic_resend_model": None,
|
||||
"timer_interrupt_model": None,
|
||||
"confidence": "low",
|
||||
"confidence_score": 0.0,
|
||||
"caveat": "No protocol semantics are emitted without both RX and TX serial reconstruction candidates.",
|
||||
@@ -148,6 +156,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
|
||||
gate_queue_model = _gate_queue_model(ordered, commands)
|
||||
tx_report_model = _tx_report_model(ordered, responses)
|
||||
periodic_resend_model = _periodic_resend_model(ordered, responses)
|
||||
timer_interrupt_model = _timer_interrupt_model(ordered)
|
||||
evidence = _top_level_evidence(ordered, dispatch, responses, rx_candidate, tx_candidate)
|
||||
|
||||
confidence_score = _confidence_score(frame_supported, dispatch, responses, commands)
|
||||
@@ -202,6 +211,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
|
||||
"gate_queue_model": gate_queue_model,
|
||||
"tx_report_model": tx_report_model,
|
||||
"periodic_resend_model": periodic_resend_model,
|
||||
"timer_interrupt_model": timer_interrupt_model,
|
||||
"evidence": evidence,
|
||||
}
|
||||
return {
|
||||
@@ -222,6 +232,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
|
||||
"gate_queue_model": protocol["gate_queue_model"],
|
||||
"tx_report_model": protocol["tx_report_model"],
|
||||
"periodic_resend_model": protocol["periodic_resend_model"],
|
||||
"timer_interrupt_model": protocol["timer_interrupt_model"],
|
||||
"confidence": protocol["confidence"],
|
||||
"confidence_score": protocol["confidence_score"],
|
||||
"caveat": protocol["caveat"],
|
||||
@@ -1308,10 +1319,17 @@ def _logical_table_map_candidates(ordered: list[JsonObject]) -> list[JsonObject]
|
||||
|
||||
def _state_variable_candidates(ordered: list[JsonObject]) -> list[JsonObject]:
|
||||
candidates: list[JsonObject] = []
|
||||
state_regions = [
|
||||
(SERIAL_HANDLER_START, SERIAL_HANDLER_END),
|
||||
(IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END),
|
||||
(0x40E0, 0x40E0),
|
||||
(FRT1_OCIA_ENTRY, 0xBF08),
|
||||
(FRT2_OCIA_ENTRY, 0xBF37),
|
||||
]
|
||||
serial_region = [
|
||||
ins
|
||||
for ins in ordered
|
||||
if SERIAL_HANDLER_START <= int(ins.get("address", -1)) <= SERIAL_HANDLER_END
|
||||
if any(start <= int(ins.get("address", -1)) <= end for start, end in state_regions)
|
||||
]
|
||||
if not any(
|
||||
_has_ref_in_range(ins, min(STATE_VARIABLES), max(STATE_VARIABLES))
|
||||
@@ -1366,8 +1384,8 @@ def _state_variable_candidates(ordered: list[JsonObject]) -> list[JsonObject]:
|
||||
"evidence_addresses_hex": _hlist(evidence),
|
||||
"confidence": "candidate-medium",
|
||||
"caveat": (
|
||||
"Role is inferred from references in the serial handler region and remains "
|
||||
"a state-variable candidate."
|
||||
"Role is inferred from references in serial handler, gate, and timer regions "
|
||||
"and remains a state-variable candidate."
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -1621,6 +1639,7 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
|
||||
evidence = _dedupe_ints(
|
||||
_addresses_in_ranges(ordered, [(MAIN_REPORT_GATE_ENTRY, MAIN_REPORT_GATE_CALL)], MAIN_REPORT_GATE_ENTRY, MAIN_REPORT_GATE_CALL)
|
||||
+ _addresses_in_ranges(ordered, [(SESSION_GATE_ENTRY, 0x4007)], SESSION_GATE_ENTRY, 0x4007)
|
||||
+ _addresses_in_ranges(ordered, [(IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END)], IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END)
|
||||
+ _addresses_in_ranges(ordered, [(QUEUE_REPORT_ENTRY, AUTONOMOUS_TX_REPORT_CALL)], QUEUE_REPORT_ENTRY, AUTONOMOUS_TX_REPORT_CALL)
|
||||
+ _addresses_in_ranges(ordered, [(RESEND_GATE_ENTRY, PERIODIC_RESEND_ENTRY)], RESEND_GATE_ENTRY, PERIODIC_RESEND_ENTRY)
|
||||
)
|
||||
@@ -1655,6 +1674,26 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
|
||||
MAIN_REPORT_GATE_CALL,
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "idle_heartbeat_report_may_enqueue",
|
||||
"entry_label": "loc_4046",
|
||||
"target_label": "loc_4067",
|
||||
"condition_candidate": (
|
||||
"F9C4 == 0 && ((FAA5.bit7 == 0) || (F9C3 == 0)) && F9B0 == F9B5"
|
||||
),
|
||||
"summary": (
|
||||
"Idle/default report gate; when the FRT2 countdown clears and the queue is "
|
||||
"empty, loc_4046 can enqueue H'00FF for the later loc_BAF2 -> loc_BA26 send path."
|
||||
),
|
||||
"state_addresses_hex": [_h16(0xF9C4), _h16(0xFAA5), _h16(0xF9C3), _h16(0xF9B0), _h16(0xF9B5)],
|
||||
"enqueued_report_candidate_hex": _h16(0x00FF),
|
||||
"evidence_addresses": _addresses_in_ranges(
|
||||
ordered,
|
||||
[(IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END)],
|
||||
IDLE_REPORT_GATE_ENTRY,
|
||||
IDLE_REPORT_GATE_END,
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "queue_has_pending_report",
|
||||
"entry_label": "loc_BAF2",
|
||||
@@ -1705,6 +1744,20 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
|
||||
0x4007,
|
||||
),
|
||||
},
|
||||
{
|
||||
"name": "idle_heartbeat_gate_initial_delay_loaded",
|
||||
"summary": "Startup/init loads F9C4 with H'14 before the first idle/default report can be queued.",
|
||||
"state_addresses_hex": [_h16(0xF9C4)],
|
||||
"reload_value_hex": _h16(0x14, width=2),
|
||||
"evidence_addresses": _state_immediate_evidence(ordered, 0xF9C4, 0x14),
|
||||
},
|
||||
{
|
||||
"name": "idle_heartbeat_gate_post_send_delay_loaded",
|
||||
"summary": "loc_BA26 reloads F9C4 with H'07 after each send, matching the observed heartbeat spacing.",
|
||||
"state_addresses_hex": [_h16(0xF9C4)],
|
||||
"reload_value_hex": _h16(0x07, width=2),
|
||||
"evidence_addresses": _state_immediate_evidence(ordered, 0xF9C4, 0x07),
|
||||
},
|
||||
{
|
||||
"name": "host_ack_can_advance_queue",
|
||||
"summary": "Commands 0x05/0x06 are modeled as acknowledgement paths that can clear pending state or advance F9B5.",
|
||||
@@ -1729,6 +1782,111 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
|
||||
}
|
||||
|
||||
|
||||
def _timer_interrupt_model(ordered: list[JsonObject]) -> JsonObject | None:
|
||||
present_addresses = {int(ins["address"]) for ins in ordered if isinstance(ins.get("address"), int)}
|
||||
frt1_evidence = _dedupe_ints(
|
||||
_addresses_in_ranges(ordered, [(FRT1_OCIA_ENTRY, 0xBF08)], FRT1_OCIA_ENTRY, 0xBF08)
|
||||
)
|
||||
frt2_evidence = _dedupe_ints(
|
||||
_addresses_in_ranges(ordered, [(FRT2_OCIA_ENTRY, 0xBF37)], FRT2_OCIA_ENTRY, 0xBF37)
|
||||
)
|
||||
sources: list[JsonObject] = []
|
||||
if frt1_evidence:
|
||||
frt1_counters = [
|
||||
counter
|
||||
for counter in (
|
||||
_timer_counter(0xF9C0, "tx_report_gate_counter_candidate", "candidate gate counter used before entering the report builder.", 0xBEF4),
|
||||
_timer_counter(0xF9C1, "rx_interbyte_timeout_candidate", "candidate RX interbyte timeout counter.", 0xBEFE),
|
||||
_timer_counter(0xF9C6, "periodic_resend_cadence_counter_candidate", "candidate periodic resend/heartbeat cadence counter.", 0xBF08),
|
||||
)
|
||||
if int(counter["evidence_address"]) in present_addresses
|
||||
]
|
||||
sources.append(
|
||||
{
|
||||
"source": "FRT1 OCIA",
|
||||
"vector_address_hex": _h16(0x0062),
|
||||
"handler_address": FRT1_OCIA_ENTRY,
|
||||
"handler_address_hex": _h16(FRT1_OCIA_ENTRY),
|
||||
"summary": "Candidate periodic tick ISR for serial busy, interbyte, and resend counters.",
|
||||
"counters": frt1_counters,
|
||||
"evidence_addresses": frt1_evidence,
|
||||
"evidence_addresses_hex": _hlist(frt1_evidence),
|
||||
}
|
||||
)
|
||||
if frt2_evidence:
|
||||
frt2_counters = [
|
||||
counter
|
||||
for counter in (
|
||||
_timer_counter(0xF9C4, "idle_heartbeat_gate_countdown_candidate", "candidate idle/default report enqueue countdown.", 0xBF2D),
|
||||
_timer_counter(0xF9C5, "rx_session_timeout_candidate", "candidate RX/session maintenance timeout counter.", 0xBF37),
|
||||
)
|
||||
if int(counter["evidence_address"]) in present_addresses
|
||||
]
|
||||
sources.append(
|
||||
{
|
||||
"source": "FRT2 OCIA",
|
||||
"vector_address_hex": _h16(0x006A),
|
||||
"handler_address": FRT2_OCIA_ENTRY,
|
||||
"handler_address_hex": _h16(FRT2_OCIA_ENTRY),
|
||||
"summary": "Candidate periodic tick ISR for idle heartbeat/report and RX session counters.",
|
||||
"clock_select": "CKS1=1 CKS0=0 => phi/32",
|
||||
"ocra_value_hex": "H'7A12",
|
||||
"manual_reference": "Manual/0900766b802125d0.md:12038 FRT CKS1/CKS0 clock select",
|
||||
"counters": frt2_counters,
|
||||
"evidence_addresses": frt2_evidence,
|
||||
"evidence_addresses_hex": _hlist(frt2_evidence),
|
||||
}
|
||||
)
|
||||
if not sources:
|
||||
return None
|
||||
|
||||
counters = _dedupe_timer_counters(
|
||||
counter
|
||||
for source in sources
|
||||
for counter in source.get("counters", [])
|
||||
if isinstance(counter, dict)
|
||||
)
|
||||
evidence = _dedupe_ints(
|
||||
addr
|
||||
for source in sources
|
||||
for addr in source.get("evidence_addresses", [])
|
||||
if isinstance(addr, int)
|
||||
)
|
||||
return {
|
||||
"kind": "timer_interrupt_model_candidate",
|
||||
"source": " / ".join(str(source["source"]) for source in sources),
|
||||
"summary": "FRT compare-match handlers decrement serial gate, timeout, and cadence counters.",
|
||||
"sources": sources,
|
||||
"counters": counters,
|
||||
"evidence_addresses": evidence,
|
||||
"evidence_addresses_hex": _hlist(evidence),
|
||||
"confidence": "candidate-medium",
|
||||
}
|
||||
|
||||
|
||||
def _timer_counter(address: int, name: str, role: str, evidence_address: int) -> JsonObject:
|
||||
return {
|
||||
"address": address,
|
||||
"address_hex": _h16(address),
|
||||
"name_candidate": name,
|
||||
"role": role,
|
||||
"evidence_address": evidence_address,
|
||||
"evidence_address_hex": _h16(evidence_address),
|
||||
}
|
||||
|
||||
|
||||
def _dedupe_timer_counters(counters: Iterable[JsonObject]) -> list[JsonObject]:
|
||||
output = []
|
||||
seen: set[int] = set()
|
||||
for counter in counters:
|
||||
address = counter.get("address")
|
||||
if not isinstance(address, int) or address in seen:
|
||||
continue
|
||||
seen.add(address)
|
||||
output.append(counter)
|
||||
return output
|
||||
|
||||
|
||||
def _tx_report_model(ordered: list[JsonObject], responses: list[JsonObject]) -> JsonObject | None:
|
||||
report_responses = [
|
||||
response for response in responses
|
||||
|
||||
Reference in New Issue
Block a user