serial improvements
This commit is contained in:
@@ -30,6 +30,7 @@ from .serial_reconstruction import (
|
||||
serial_reconstruction_json_payload,
|
||||
serial_reconstruction_metadata_for_instruction,
|
||||
)
|
||||
from .serial_semantics import analyze_serial_semantics
|
||||
from .symbols import symbol_for_address
|
||||
from .tables import IO_REGISTERS
|
||||
from .timing import format_timing_summary
|
||||
@@ -474,6 +475,7 @@ def write_json(
|
||||
for ins in (instructions[addr] for addr in sorted(instructions))
|
||||
],
|
||||
}
|
||||
payload["serial_semantics"] = analyze_serial_semantics(payload)
|
||||
path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
||||
|
||||
|
||||
|
||||
@@ -2,10 +2,13 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from .serial_semantics import analyze_serial_semantics
|
||||
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
|
||||
@@ -17,6 +20,7 @@ class SerialPseudocodeOptions:
|
||||
include_evidence: bool = True
|
||||
include_manual: bool = True
|
||||
include_board: bool = True
|
||||
include_semantics: bool = True
|
||||
|
||||
|
||||
def generate_serial_pseudocode(
|
||||
@@ -28,6 +32,7 @@ def generate_serial_pseudocode(
|
||||
opts = options or SerialPseudocodeOptions()
|
||||
tx_candidate = _find_candidate(payload, "candidate_sci1_tx_frame")
|
||||
rx_candidate = _find_candidate(payload, "candidate_sci1_rx_frame")
|
||||
serial_semantics = analyze_serial_semantics(payload) if opts.include_semantics else None
|
||||
|
||||
lines: list[str] = []
|
||||
lines.extend(_file_header(source_name, tx_candidate, rx_candidate))
|
||||
@@ -36,6 +41,8 @@ def generate_serial_pseudocode(
|
||||
if opts.include_manual:
|
||||
lines.extend(_manual_reference_lines(payload))
|
||||
lines.extend(_declarations(tx_candidate, rx_candidate))
|
||||
if opts.include_semantics:
|
||||
lines.extend(_semantics_lines(serial_semantics, opts))
|
||||
|
||||
emitted = False
|
||||
if opts.include_tx and tx_candidate:
|
||||
@@ -96,6 +103,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
parser.add_argument("--no-evidence", action="store_true", help="omit evidence-address comments")
|
||||
parser.add_argument("--no-manual", action="store_true", help="omit manual-reference comments")
|
||||
parser.add_argument("--no-board", action="store_true", help="omit board/MAX202 comments")
|
||||
parser.add_argument("--no-semantics", action="store_true", help="omit candidate command/field semantics")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
options = SerialPseudocodeOptions(
|
||||
@@ -104,6 +112,7 @@ def main(argv: list[str] | None = None) -> int:
|
||||
include_evidence=not args.no_evidence,
|
||||
include_manual=not args.no_manual,
|
||||
include_board=not args.no_board,
|
||||
include_semantics=not args.no_semantics,
|
||||
)
|
||||
write_serial_pseudocode(args.input, args.out, options)
|
||||
print(f"wrote {args.out}")
|
||||
@@ -256,6 +265,135 @@ def _declarations(tx_candidate: JsonObject | None, rx_candidate: JsonObject | No
|
||||
return lines
|
||||
|
||||
|
||||
def _semantics_lines(
|
||||
analysis: JsonObject | None,
|
||||
opts: SerialPseudocodeOptions,
|
||||
) -> list[str]:
|
||||
if not isinstance(analysis, dict):
|
||||
return []
|
||||
protocols = analysis.get("protocol_semantics")
|
||||
if not isinstance(protocols, list) or not protocols:
|
||||
return []
|
||||
protocol = protocols[0]
|
||||
if not isinstance(protocol, dict):
|
||||
return []
|
||||
|
||||
lines: list[str] = ["/* Candidate Protocol Semantics"]
|
||||
lines.append(
|
||||
f" * confidence: {protocol.get('confidence', 'unknown')} "
|
||||
f"({protocol.get('confidence_score', 'n/a')})",
|
||||
)
|
||||
caveat = str(protocol.get("caveat") or "").strip()
|
||||
if caveat:
|
||||
lines.append(f" * caveat: {_comment_text(caveat)}")
|
||||
|
||||
layout = protocol.get("byte_layout")
|
||||
if isinstance(layout, list) and layout:
|
||||
lines.append(" * byte layout:")
|
||||
for item in layout:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
offset = item.get("offset", "?")
|
||||
name = item.get("name_candidate", "byte")
|
||||
semantic = item.get("semantic", "")
|
||||
confidence = item.get("confidence", "unknown")
|
||||
lines.append(f" * - byte{offset}: {name} ({confidence}) - {_comment_text(str(semantic))}")
|
||||
|
||||
dispatch = protocol.get("command_dispatch")
|
||||
if isinstance(dispatch, dict):
|
||||
values = ", ".join(str(value) for value in dispatch.get("command_values_hex", []))
|
||||
lines.append(
|
||||
" * dispatch: command_low3 = RX_FRAME(0) & 0x07"
|
||||
+ (f"; observed {values}" if values else ""),
|
||||
)
|
||||
if opts.include_evidence:
|
||||
lines.append(f" * dispatch evidence: {_hex_join(dispatch.get('evidence_addresses_hex'))}")
|
||||
|
||||
index_decoder = protocol.get("index_decoder")
|
||||
if isinstance(index_decoder, dict):
|
||||
lines.append(
|
||||
" * index decoder: RX[1:2] -> logical index via "
|
||||
f"{index_decoder.get('label', 'loc_622B')} ({index_decoder.get('confidence', 'unknown')})",
|
||||
)
|
||||
|
||||
commands = [item for item in protocol.get("commands", []) if isinstance(item, dict)]
|
||||
if commands:
|
||||
lines.append(" * command candidates:")
|
||||
for command in commands:
|
||||
value = command.get("command_value_hex", "??")
|
||||
name = command.get("name_candidate", "unknown")
|
||||
summary = _comment_text(str(command.get("summary") or ""))
|
||||
handler = command.get("handler_start_hex") or "multiple"
|
||||
responses = ", ".join(str(item) for item in command.get("response_candidates", [])) or "none"
|
||||
lines.append(f" * - {value} {name}: {summary}; handler {handler}; responses {responses}")
|
||||
lines.append(" */")
|
||||
lines.append("")
|
||||
|
||||
lines.extend(
|
||||
[
|
||||
"static u8 sci1_rx_candidate_command(void)",
|
||||
"{",
|
||||
" return (u8)(RX_FRAME(0) & 0x07u);",
|
||||
"}",
|
||||
"",
|
||||
"static u16 sci1_rx_candidate_value(void)",
|
||||
"{",
|
||||
" return (u16)(((u16)RX_FRAME(3) << 8) | RX_FRAME(4));",
|
||||
"}",
|
||||
"",
|
||||
"static u16 sci1_rx_candidate_logical_index(void)",
|
||||
"{",
|
||||
" u8 page = RX_FRAME(1);",
|
||||
" u8 offset = RX_FRAME(2);",
|
||||
"",
|
||||
" if (page == 0u && offset <= 0x7Fu) {",
|
||||
" return offset;",
|
||||
" }",
|
||||
" if (page == 1u) {",
|
||||
" return (u16)(0x0080u + offset);",
|
||||
" }",
|
||||
" if (page == 2u && offset <= 0x7Fu) {",
|
||||
" return (u16)(0x0180u + offset);",
|
||||
" }",
|
||||
" return 0x01FFu;",
|
||||
"}",
|
||||
"",
|
||||
"void sci1_process_candidate_protocol_command(void)",
|
||||
"{",
|
||||
" u8 command = sci1_rx_candidate_command();",
|
||||
" u16 logical_index = sci1_rx_candidate_logical_index();",
|
||||
" u16 value = sci1_rx_candidate_value();",
|
||||
"",
|
||||
" switch (command) {",
|
||||
],
|
||||
)
|
||||
for command in commands:
|
||||
value = command.get("command_value")
|
||||
if not isinstance(value, int):
|
||||
continue
|
||||
name = _safe_identifier(str(command.get("name_candidate") or f"command_{value:02X}"))
|
||||
summary = _comment_text(str(command.get("summary") or "candidate command semantics unknown"))
|
||||
evidence = _hex_join(command.get("evidence_addresses_hex"))
|
||||
lines.append(f" case 0x{value:02X}u:")
|
||||
lines.append(f" /* {name}: {summary}")
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f" * evidence: {evidence}")
|
||||
lines.append(" */")
|
||||
lines.append(f" candidate_{name}(logical_index, value);")
|
||||
lines.append(" break;")
|
||||
lines.extend(
|
||||
[
|
||||
" default:",
|
||||
" candidate_unknown_command(command, logical_index, value);",
|
||||
" break;",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
],
|
||||
)
|
||||
return lines
|
||||
|
||||
|
||||
def _tx_functions(candidate: JsonObject, opts: SerialPseudocodeOptions) -> list[str]:
|
||||
length = _int_field(candidate, "frame_length", 6)
|
||||
seed = _int_field(candidate, "checksum_seed", 0x5A)
|
||||
@@ -473,5 +611,21 @@ def _dedupe(items: list[str]) -> list[str]:
|
||||
return output
|
||||
|
||||
|
||||
def _hex_join(value: object) -> str:
|
||||
if not isinstance(value, list):
|
||||
return ""
|
||||
return ", ".join(str(item) for item in value)
|
||||
|
||||
|
||||
def _safe_identifier(value: str) -> str:
|
||||
cleaned = re.sub(r"[^0-9A-Za-z_]", "_", value.strip())
|
||||
cleaned = re.sub(r"_+", "_", cleaned).strip("_")
|
||||
if not cleaned:
|
||||
return "unknown"
|
||||
if cleaned[0].isdigit():
|
||||
return "_" + cleaned
|
||||
return cleaned
|
||||
|
||||
|
||||
def _comment_text(text: str) -> str:
|
||||
return text.replace("*/", "* /").replace("\r", " ").replace("\n", " ")
|
||||
|
||||
1262
h8536/serial_semantics.py
Normal file
1262
h8536/serial_semantics.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user