import argparse from collections import defaultdict, deque import json import os from pathlib import Path import struct import subprocess import sys import tempfile from types import SimpleNamespace REPO = Path(__file__).resolve().parents[2] sys.path.insert(0, str(REPO / "validation/tools/pim/pimcomp/correctness/synchronization")) import global_memory as sync def read_events(artifact): streams = {} for path in artifact.glob("core_*.pim"): core = int(path.stem.split("_")[1]) registers, events = {}, [] data = path.read_bytes() assert data[:4] == b"PIMB" for pc, (op, rd, rs, flags, arg, selection, offset, size) in enumerate( struct.iter_unpack(" 1 if len({reader[0] for reader in readers}) > 1: stalled_reader = readers[-1] for reader in readers: assert reaches(writer, reader), ("missing READY", writer, reader) if iteration < 2: assert reaches(reader, stores[iteration + 1, address, size]), ( "unsafe overwrite", reader, address) assert shared, "fixture must exercise a publication with multiple physical readers" return shared, stalled_reader def run_functional(artifact, inputs, references, outputs, out, policy, seed, reader=None): out.mkdir() command = [str(sync.RUST_BINARY), "--folder", str(artifact), "--output", str(out / "output.bin"), "--dump", sync.compare.build_dump_ranges(artifact / "config.json", outputs), "--mode", "throughput", "--batch-size", "8", "--input-dir", str(inputs[0].parent), "--batch-output-dir", str(out / "iterations"), "--diagnostic-schedule-policy", policy, "--diagnostic-schedule-seed", str(seed)] if reader: command += ["--diagnostic-target-stall", f"{reader[0] + 1}:{reader[2]}:1:10000"] result = subprocess.run(command, capture_output=True, text=True, timeout=120) assert result.returncode == 0, result.stdout + result.stderr for index, reference in enumerate(references[:8]): result = sync.compare.compare_simulator_outputs( out / "iterations" / f"output_{index:06d}.bin", outputs, reference, threshold=1e-3, rtol=1e-5) assert result.passed, result def main(): parser = argparse.ArgumentParser() parser.add_argument("--compiler", required=True) args = parser.parse_args() with tempfile.TemporaryDirectory(prefix="raptor-publication-") as temporary: root = Path(temporary) model = REPO / "validation/networks/pimcomp_models/vgg8/vgg8-mnist-reconstructed.onnx" options = SimpleNamespace(timeout=120, threshold=1e-3, rtol=1e-5) samples, inputs, _, _ = sync.make_inputs(model, 8, 0, root) references = sync.make_references(model, samples, root, options) _, outputs = sync.compare.onnx_io(model) for arch, cores, weights in (("arch-a", 168, 96), ("arch-b", 138, 128)): out = root / arch out.mkdir() command = [args.compiler, str(model), "-o", str(out / "model"), "--maccel=PIM", "--EmitPimCodegen", "--crossbar-size=128", f"--crossbar-count={weights}", f"--core-count={cores}", "--pipeline=4", f"--pim-target-config={sync.CONFIG_ROOT / arch / 'throughput_config_1000ms.json'}", "--mlir-disable-threading", "--pim-emit-json", "--pim-detect-communication-deadlock"] compiled = subprocess.run(command, capture_output=True, text=True, timeout=120, env=dict(os.environ, OMP_NUM_THREADS="1")) assert compiled.returncode == 0, compiled.stdout + compiled.stderr artifact = out / "pim" streams = read_events(artifact) shared, reader = check_lifetimes(streams) for policy, seed in (("greedy", 0), ("randomized", 1), ("randomized", 7)): run_functional(artifact, inputs, references, outputs, out / f"{policy}-{seed}", policy, seed) run_functional(artifact, inputs, references, outputs, out / "stalled", "randomized", 11, reader) print(f"{arch}: {shared} shared publications; three-iteration lifetime proof; eight inputs; randomized and stalled execution passed") if __name__ == "__main__": main()