1
0

DTC and SCI improvements

This commit is contained in:
Aiden
2026-05-25 14:22:32 +10:00
parent 62d1c3c876
commit 80819448cf
21 changed files with 13823 additions and 86 deletions

214
h8536/timing.py Normal file
View File

@@ -0,0 +1,214 @@
from __future__ import annotations
from .formatting import h16, label_for
from .model import Instruction
def summarize_timing(
instructions: dict[int, Instruction],
labels: dict[int, str] | None = None,
call_graph: dict[str, object] | None = None,
) -> dict[str, list[dict[str, object]]]:
labels = labels or {}
addresses = sorted(instructions)
if not addresses:
return {"blocks": [], "loops": []}
starts = _block_starts(instructions, addresses, labels, call_graph)
blocks = [_summarize_block(start, instructions, labels, starts) for start in sorted(starts)]
loops = _summarize_loops(instructions, labels)
return {
"blocks": [block for block in blocks if block["instruction_count"]],
"loops": loops,
}
def cycle_bounds(cycles: dict[str, object] | None) -> tuple[int, int] | None:
if not cycles:
return None
if "cycles" in cycles:
value = int(cycles["cycles"])
return value, value
if "cycles_min" in cycles and "cycles_max" in cycles:
return int(cycles["cycles_min"]), int(cycles["cycles_max"])
if "not_taken" in cycles and "taken" in cycles:
values = [int(cycles["not_taken"]), int(cycles["taken"])]
return min(values), max(values)
if "trap_not_taken" in cycles and "trap_taken" in cycles:
values = [int(cycles["trap_not_taken"]), int(cycles["trap_taken"])]
return min(values), max(values)
if "false" in cycles and "count_minus_1" in cycles and "taken" in cycles:
values = [int(cycles["false"]), int(cycles["count_minus_1"]), int(cycles["taken"])]
return min(values), max(values)
return None
def _block_starts(
instructions: dict[int, Instruction],
addresses: list[int],
labels: dict[int, str],
call_graph: dict[str, object] | None,
) -> set[int]:
address_set = set(addresses)
starts = {addresses[0]}
starts.update(address for address in labels if address in address_set)
for node in (call_graph or {}).get("nodes", []):
start = int(node["start"])
if start in address_set:
starts.add(start)
for ins in instructions.values():
starts.update(target for target in ins.targets if target in address_set)
if ins.kind in {"branch", "jump", "return", "rte", "sleep", "invalid"}:
next_address = ins.address + max(ins.size, 1)
if next_address in address_set:
starts.add(next_address)
return starts
def _summarize_block(
start: int,
instructions: dict[int, Instruction],
labels: dict[int, str],
starts: set[int],
) -> dict[str, object]:
addresses = []
pc = start
cycles_min = 0
cycles_max = 0
unknown_cycles = 0
terminator: Instruction | None = None
while pc in instructions:
if addresses and pc in starts:
break
ins = instructions[pc]
addresses.append(pc)
bounds = cycle_bounds(ins.cycles)
if bounds is None:
unknown_cycles += 1
else:
cycles_min += bounds[0]
cycles_max += bounds[1]
terminator = ins
next_pc = pc + max(ins.size, 1)
if ins.kind in {"branch", "jump", "return", "rte", "sleep", "invalid"}:
break
if next_pc in starts:
break
pc = next_pc
end = addresses[-1] if addresses else start
return {
"start": start,
"end": end,
"label": labels.get(start, label_for(start)),
"instruction_count": len(addresses),
"cycles_min": cycles_min,
"cycles_max": cycles_max,
"unknown_cycles": unknown_cycles,
"terminator": terminator.text if terminator else "",
"targets": list(terminator.targets if terminator else []),
}
def _summarize_loops(
instructions: dict[int, Instruction],
labels: dict[int, str],
) -> list[dict[str, object]]:
loops: list[dict[str, object]] = []
for ins in sorted(instructions.values(), key=lambda item: item.address):
for target in ins.targets:
if target > ins.address or target not in instructions:
continue
body = [
address
for address in sorted(instructions)
if target <= address <= ins.address
]
if not body:
continue
cycles_min = 0
cycles_max = 0
unknown_cycles = 0
has_call = False
for address in body:
body_ins = instructions[address]
has_call = has_call or body_ins.kind == "call"
bounds = cycle_bounds(body_ins.cycles)
if bounds is None:
unknown_cycles += 1
else:
cycles_min += bounds[0]
cycles_max += bounds[1]
loops.append(
{
"start": target,
"end": ins.address,
"label": labels.get(target, label_for(target)),
"back_edge": ins.address,
"back_edge_text": ins.text,
"instruction_count": len(body),
"cycles_min": cycles_min,
"cycles_max": cycles_max,
"unknown_cycles": unknown_cycles,
"has_call": has_call,
"kind": _loop_kind(ins, has_call),
},
)
return loops
def _loop_kind(instruction: Instruction, has_call: bool) -> str:
if instruction.mnemonic.startswith("SCB/"):
return "counter_delay_loop" if not has_call else "counter_loop"
if instruction.mnemonic in {"BRA", "BRN"}:
return "unconditional_loop" if not has_call else "loop_with_call"
return "delay_loop_candidate" if not has_call else "loop_with_call"
def format_timing_summary(summary: dict[str, list[dict[str, object]]], *, max_items: int = 40) -> list[str]:
lines: list[str] = []
blocks = summary.get("blocks", [])
loops = summary.get("loops", [])
if not blocks and not loops:
return lines
lines.append("; Timing Summary")
if blocks:
lines.append("; Straight-line blocks")
for block in blocks[:max_items]:
lines.append(
"; block "
f"{h16(int(block['start']))}-{h16(int(block['end']))} "
f"{str(block['label']):<20} "
f"ins={block['instruction_count']:<3} "
f"cycles={_cycle_range(block)} "
f"unknown={block['unknown_cycles']}",
)
if len(blocks) > max_items:
lines.append(f"; ... {len(blocks) - max_items} more blocks")
if loops:
lines.append("; Backward-branch loop candidates")
for loop in loops[:max_items]:
lines.append(
"; loop "
f"{h16(int(loop['start']))}-{h16(int(loop['end']))} "
f"{str(loop['label']):<20} "
f"{loop['kind']:<22} "
f"cycles/iteration={_cycle_range(loop)} "
f"back_edge={h16(int(loop['back_edge']))}",
)
if len(loops) > max_items:
lines.append(f"; ... {len(loops) - max_items} more loops")
lines.append("")
return lines
def _cycle_range(item: dict[str, object]) -> str:
minimum = int(item["cycles_min"])
maximum = int(item["cycles_max"])
if minimum == maximum:
return str(minimum)
return f"{minimum}-{maximum}"