OXYGENHUB
◆ main enhanced-x16-emulator / tests / fetch_vera_rtl.py
5.5 KiB · Python · last changed chore(): initial commit 25 days ago source blame history raw
1
#!/usr/bin/env python3
2
"""Fetch the VERA Verilog that tests/test_vera_pcm.c cites.
3
4
VERA is an FPGA, so the RTL is not a description of the hardware -- it IS the
5
hardware, and it is the oracle for every VERA conformance test here. The tests
6
quote the lines they rest on so a reader need not fetch anything, but the
7
quotes are only trustworthy if they can be checked against the source.
8
9
    python tests/fetch_vera_rtl.py
10
11
Nothing here is committed: it is someone else's repository under its own
12
licence, and it is reference material rather than part of the build. No test
13
depends on it being present.
14
15
Pinned to the tag the emulator reports itself to be, not to a branch. A citation of "top.v:480" is
16
meaningless against a moving target, and the whole point is that a reviewer can
17
check the quoted line. When the pin moves, the quoted lines must be re-checked.
18
19
Use X16Community/vera-module and not fvdhoef/vera-module. The latter is the
20
parent repository, but the two diverged at the v0.7 release in 2019 and are now
21
different chips: the parent has no audio RTL at all -- no audio directory, no
22
PCM, no FIFO, and only eight bus registers against this one's thirty-two. Audio
23
was added for production hardware and exists solely in the fork.
24
"""
25
26
import sys
27
import urllib.request
28
from pathlib import Path
29
30
DEST = Path(__file__).resolve().parent / "vera-rtl"
31
RAW = "https://raw.githubusercontent.com/X16Community/vera-module"
32
33
# The tag matching VERA_VERSION_MAJOR/MINOR/PATCH in src/video.c. Held in step
34
# by tests/check_vera_version.py: if the emulator's declared version changes,
35
# this pin and every cited line number must be re-checked.
36
COMMIT = "45cc1f053376dae12173ea63612820e4d289c0da"  # tag v47.0.2
37
38
# psg.v is pinned to R48 instead, because that is the revision the emulator
39
# implements. vera_psg.c:122-123 XOR the saw and triangle waveforms with the
40
# inverted pulse width, which arrived in the RTL as a8135f32 (2024-10-21) and
41
# shipped in v48.0.1; R47 has the unmodulated forms. The emulator backported it
42
# in #290 on 2024-08-11, two months before the RTL change landed.
43
#
44
# psg.v is the only audio file that differs between the two tags -- audio_fifo.v,
45
# pcm.v and audio.v are byte-identical -- so this is the whole of the split.
46
# See the header of tests/check_vera_version.py for why the emulator is not any
47
# single revision.
48
COMMIT_PSG = "6e8bea68a5a04687149e27b1b7b3726fb01405f4"  # tag v48.0.1
49
50
FILES = (
51
    ("fpga/source/top.v", COMMIT),                  # register decode, incl. 0x1B-0x1D
52
    ("fpga/source/graphics/layer_renderer.v", COMMIT),   # map and tile address arithmetic
53
    ("fpga/source/graphics/sprite_renderer.v", COMMIT),  # sprite collision mask and sprcol_irq
54
    ("fpga/source/palette_ram.mem", COMMIT),        # the default palette, baked into the bitstream
55
    ("fpga/source/audio/audio_fifo.v", COMMIT),     # the PCM FIFO itself
56
    ("fpga/source/audio/pcm.v", COMMIT),            # rate accumulator, sample fetch, volume
57
    ("fpga/source/audio/audio.v", COMMIT),          # PCM and PSG into the DAC
58
    ("fpga/source/audio/psg.v", COMMIT_PSG),        # R48: see above
59
    # The VRAM bandwidth model in src/vera_bandwidth.c rests on these. It claims
60
    # a per-scanline clock window, a bus width, an access cost and a priority
61
    # order, and every one of those numbers is quoted from here.
62
    ("fpga/source/vram_if.v", COMMIT),              # the one VRAM bus: 4-way fixed priority, one access per clock
63
    ("fpga/source/main_ram.v", COMMIT),             # 32k x 32 BRAM array, which is why an access is 4 bytes
64
    ("fpga/source/video/video_vga.v", COMMIT),      # H_TOTAL 800, V_TOTAL 525: the scanline clock window
65
    ("fpga/source/graphics/composer.v", COMMIT),    # line_render_start, so a line is fetched during the one before it
66
    ("fpga/source/graphics/layer_line_buffer.v", COMMIT),  # the double buffer that makes that prefetch possible
67
    ("fpga/source/addr_data.v", COMMIT),            # the $9F23/$9F24 data port and the FX engine behind it
68
)
69
70
71
def manifest():
72
    return "".join(f"{Path(p).name} {ref}\n" for p, ref in FILES)
73
74
75
def get(url, path):
76
    if path.exists():
77
        print(f"  have {path.name}")
78
        return True
79
    path.parent.mkdir(parents=True, exist_ok=True)
80
    try:
81
        with urllib.request.urlopen(url) as r:
82
            data = r.read()
83
    except Exception as e:
84
        print(f"  FAILED {path.name}: {e}", file=sys.stderr)
85
        return False
86
    path.write_bytes(data)
87
    print(f"  got  {path.name} ({len(data)} bytes)")
88
    return True
89
90
91
def main():
92
    print(f"VERA RTL -> {DEST}")
93
    for p, ref in FILES:
94
        print(f"  {Path(p).name:16} {ref[:8]}")
95
96
    # Re-fetch when any pin has moved. Skipping on "file exists" alone would
97
    # quietly serve the previous revision's RTL under the new commit's name,
98
    # and the line numbers cited in the tests would silently stop matching --
99
    # which is exactly the failure this whole oracle exists to prevent.
100
    stamp = DEST / "COMMIT"
101
    want = manifest()
102
    have = stamp.read_text() if stamp.exists() else None
103
    if have is not None and have != want:
104
        print("  pins moved: discarding the fetched copy")
105
        for p, _ in FILES:
106
            (DEST / Path(p).name).unlink(missing_ok=True)
107
        stamp.unlink(missing_ok=True)
108
109
    ok = True
110
    for p, ref in FILES:
111
        # Flattened: the citations name the file, not its path in the repo.
112
        ok &= get(f"{RAW}/{ref}/{p}", DEST / Path(p).name)
113
    if not ok:
114
        return 1
115
    stamp.write_text(want)
116
    return 0
117
118
119
if __name__ == "__main__":
120
    sys.exit(main())