#!/usr/bin/env python3
"""Offline helper for the UART receiver benchmark.

This script documents and checks the cycle math used by the testbench for the
directed scenarios. It is not imported by the Verilog testbench at runtime.
Expected values remain hardcoded in `testbench.v`.
"""

from dataclasses import dataclass

BIT_CYCLES = 16
OUTPUT_OFFSET = 153  # From the cycle before the start bit is first observed.


@dataclass
class Event:
    cycle: int
    kind: str
    value: int | None
    tag: str


def schedule_valid(current_cycle: int, byte: int, tag: str) -> Event:
    return Event(current_cycle + OUTPUT_OFFSET, "valid", byte, tag)


def schedule_error(current_cycle: int, tag: str) -> Event:
    return Event(current_cycle + OUTPUT_OFFSET, "error", None, tag)


def main() -> None:
    cycle = 0
    events: list[Event] = []

    cycle += 2   # reset asserted
    cycle += 2   # post-reset idle
    cycle += 4   # false-start low
    cycle += 20  # false-start recovery

    events.append(schedule_valid(cycle, 0x55, "valid_55"))
    cycle += 10 * BIT_CYCLES + 8

    events.append(schedule_valid(cycle, 0x00, "valid_00"))
    cycle += 10 * BIT_CYCLES

    events.append(schedule_valid(cycle, 0xFF, "valid_ff"))
    cycle += 10 * BIT_CYCLES

    events.append(schedule_valid(cycle, 0xA3, "valid_a3"))
    cycle += 10 * BIT_CYCLES

    events.append(schedule_valid(cycle, 0x3C, "back_to_back_3c"))
    cycle += 10 * BIT_CYCLES

    events.append(schedule_valid(cycle, 0xC7, "back_to_back_c7"))
    cycle += 10 * BIT_CYCLES + 16

    events.append(schedule_error(cycle, "framing_error"))
    cycle += 10 * BIT_CYCLES + 16

    cycle += 5 * BIT_CYCLES
    cycle += 1   # reset during frame
    cycle += 40  # settle time

    print("Directed event summary:")
    for event in events:
        if event.kind == "valid":
            print(f"  cycle {event.cycle:4d}: VALID 0x{event.value:02X} ({event.tag})")
        else:
            print(f"  cycle {event.cycle:4d}: ERROR ({event.tag})")


if __name__ == "__main__":
    main()
