C6-Reticulum-ASM/tests/log/test_log_str.py
DeFiDude 7c80281244 log_init + log_str: verified
log_init is a single ret today; the ts source lands when clock_get_freq
is wired and log_event needs a real ms-since-boot value. The function
exists now so log_event can call it without forward dependency.

log_str tail-calls uart_tx_bytes — no frame, ra threads through. The
indirection exists so log_event and downstream primitives don't couple
to the underlying TX implementation; a future IRQ-driven uart_tx_bytes
swap stays local to one file.
2026-05-01 22:46:56 -06:00

38 lines
1.3 KiB
Python

"""Tests for src/log/log_str.S — tail-call into uart_tx_bytes."""
from __future__ import annotations
import re
import pytest
from harness import build
@pytest.fixture(scope="module")
def artifacts() -> build.BuildArtifacts:
return build.build("qemu-virt")
def test_function_exists(artifacts: build.BuildArtifacts) -> None:
assert build.symbol_address(artifacts.elf, "log_str") > 0
def test_tail_calls_uart_tx_bytes(artifacts: build.BuildArtifacts) -> None:
"""Body is a single tail-call: auipc/jalr to uart_tx_bytes (no frame
setup, no ra save). The target symbol comment must reference
uart_tx_bytes."""
body = build.objdump_disassemble(artifacts.elf, symbol="log_str")
instrs = [line for line in body.splitlines() if re.match(r"^\s*[0-9a-f]{8}:", line)]
# `tail uart_tx_bytes` → auipc t1, ... ; jalr zero, off(t1) (or jr).
# Compressed forms are also possible (`c.j`, `c.jr`).
assert "uart_tx_bytes" in body, body
# No stack adjust: no `addi sp, sp, …` should appear.
for line in instrs:
assert not re.search(r"\baddi\s+sp", line), line
def test_no_callee_saved_writes(artifacts: build.BuildArtifacts) -> None:
body = build.objdump_disassemble(artifacts.elf, symbol="log_str")
for match in re.findall(r"\b(s\d+)\b,", body):
pytest.fail(f"log_str writes callee-saved {match}: {body}")