mirror of
https://github.com/ratspeak/C6-Reticulum-ASM
synced 2026-08-12 18:07:18 -04:00
Word-by-word copy from .data's LMA (__data_load_start) to its VMA (__data_start..__data_end). On qemu-virt LMA == VMA so the loop is identity; on the C6 it pulls flash → SRAM. Same asm shape, target- selected behaviour driven entirely by the linker script. toolchain/qemu-virt.ld: defines __data_load_start = LOADADDR(.data) so the symbol resolves at link time on either target. Static disassembly checks: three auipc/addi pairs for the bounds, matched lw/sw word pair, two increments per iteration, ret, no callee-saved writes.
62 lines
2.7 KiB
Python
62 lines
2.7 KiB
Python
"""Tests for src/boot/_init_data.S.
|
|
|
|
Static disassembly checks (per @verify: kat-only). On qemu-virt the
|
|
function is semantically identity (LMA == VMA), but the asm shape — load,
|
|
store, increment-both, branch — is the same as on the C6 where it
|
|
performs a real flash → SRAM copy. The tests assert the shape; the
|
|
integration check (a non-zero .data initialiser observed at runtime)
|
|
lands when _main wires up the boot chain.
|
|
"""
|
|
|
|
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_loads_three_bounds(artifacts: build.BuildArtifacts) -> None:
|
|
"""Three la pairs at the head: __data_start, __data_end, __data_load_start."""
|
|
body = build.objdump_disassemble(artifacts.elf, symbol="_init_data")
|
|
instrs = [line for line in body.splitlines() if re.match(r"^\s*[0-9a-f]{8}:", line)]
|
|
assert len(instrs) >= 9, body
|
|
for i, reg in enumerate(("t0", "t1", "t2")):
|
|
assert "auipc" in instrs[2 * i] and f"{reg}," in instrs[2 * i], instrs[2 * i]
|
|
assert "addi" in instrs[2 * i + 1] and f"{reg}," in instrs[2 * i + 1], instrs[2 * i + 1]
|
|
|
|
|
|
def test_load_store_word_pair(artifacts: build.BuildArtifacts) -> None:
|
|
body = build.objdump_disassemble(artifacts.elf, symbol="_init_data")
|
|
# The word load is from the LMA cursor (t2), the word store goes to the
|
|
# VMA cursor (t0). Match the canonical forms; objdump may compress.
|
|
assert re.search(r"\blw\b\s+t3,0\(t2\)", body), body
|
|
assert re.search(r"\bsw\b\s+t3,0\(t0\)", body), body
|
|
|
|
|
|
def test_loop_advances_both_cursors(artifacts: build.BuildArtifacts) -> None:
|
|
body = build.objdump_disassemble(artifacts.elf, symbol="_init_data")
|
|
# Two `addi … 4` increments per iteration — one for t0, one for t2.
|
|
addi_count = len(re.findall(r"\baddi\s+(t0|t2),", body)) + \
|
|
len(re.findall(r"\baddi\s+(t0|t2),", body))
|
|
# The la prologue also uses addi for low-12-bit fixups; subtract the
|
|
# known three la pairs (3 addi). Increments contribute 2 more.
|
|
addi_total = len(re.findall(r"\baddi\b", body))
|
|
assert addi_total >= 5, body # 3 (la fixups) + 2 (increments)
|
|
|
|
|
|
def test_function_returns(artifacts: build.BuildArtifacts) -> None:
|
|
body = build.objdump_disassemble(artifacts.elf, symbol="_init_data")
|
|
assert "ret" in body or "jalr\tzero" in body, body
|
|
|
|
|
|
def test_does_not_touch_callee_saved(artifacts: build.BuildArtifacts) -> None:
|
|
body = build.objdump_disassemble(artifacts.elf, symbol="_init_data")
|
|
for match in re.findall(r"\b(s\d+)\b,", body):
|
|
pytest.fail(f"_init_data writes callee-saved register {match}: {body}")
|