Research-Stack/4-Infrastructure/hardware/sparkle/tangnano9k/monitor_uart.py

84 lines
2.8 KiB
Python
Executable file

#!/usr/bin/env python3
import argparse
import os
import select
import sys
import termios
import time
import tty
BAUD_FLAGS = {
9600: termios.B9600,
19200: termios.B19200,
38400: termios.B38400,
57600: termios.B57600,
115200: termios.B115200,
}
def configure(fd: int, baud: int) -> list[int]:
old = termios.tcgetattr(fd)
tty.setraw(fd)
attrs = termios.tcgetattr(fd)
baud_flag = BAUD_FLAGS[baud]
attrs[4] = baud_flag
attrs[5] = baud_flag
attrs[2] |= termios.CLOCAL | termios.CREAD
attrs[2] &= ~termios.CSTOPB
attrs[2] &= ~termios.PARENB
attrs[2] &= ~termios.CSIZE
attrs[2] |= termios.CS8
attrs[6][termios.VMIN] = 0
attrs[6][termios.VTIME] = 0
termios.tcsetattr(fd, termios.TCSANOW, attrs)
return old
def decode(byte: int) -> str:
tag = byte >> 4
payload = byte & 0x0F
if tag == 0x5:
return f"0x{byte:02x} tag=state state={payload}"
if tag == 0x6:
audio_mode = (payload >> 3) & 1
emit = (payload >> 2) & 1
handle_k = payload & 0x03
return f"0x{byte:02x} tag=meta mode={'audio' if audio_mode else 'manual'} emit={bool(emit)} handleK={handle_k}"
return f"0x{byte:02x} tag=0x{tag:x} low=0x{payload:x}"
def main() -> int:
parser = argparse.ArgumentParser(description="Monitor Tang Nano 9K Sparkle Phi/S3C UART telemetry.")
parser.add_argument("port", nargs="?", default="/dev/ttyUSB1")
parser.add_argument("--baud", type=int, default=115200, choices=sorted(BAUD_FLAGS))
parser.add_argument("--seconds", type=float, default=0.0, help="Stop after N seconds; 0 means run forever.")
parser.add_argument("--expect-state-tag", action="store_true", help="Fail unless state-tagged 0x5N frames arrive.")
parser.add_argument("--min-frames", type=int, default=1, help="Minimum expected frames when checking telemetry.")
args = parser.parse_args()
fd = os.open(args.port, os.O_RDONLY | os.O_NOCTTY | os.O_NONBLOCK)
old_attrs = configure(fd, args.baud)
deadline = None if args.seconds <= 0 else time.monotonic() + args.seconds
matched = 0
print(f"listening on {args.port} at {args.baud} baud; press the FPGA user button")
try:
while deadline is None or time.monotonic() < deadline:
ready, _, _ = select.select([fd], [], [], 0.25)
if not ready:
continue
data = os.read(fd, 256)
for byte in data:
if byte >> 4 == 0x5:
matched += 1
print(decode(byte), flush=True)
finally:
termios.tcsetattr(fd, termios.TCSANOW, old_attrs)
os.close(fd)
if args.expect_state_tag and matched < args.min_frames:
print(f"expected at least {args.min_frames} state-tag frame(s), saw {matched}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
sys.exit(main())