Skip to content
Open
3 changes: 2 additions & 1 deletion api/steps/bebop/p2e/02_buildbitstream_event.step.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ async def handler(input_data: dict, ctx: FlowContext) -> None:
os.makedirs(build_dir, exist_ok=True)

build_cmd = (
f"nix develop --ignore-environment --keep HOME --keep ALL_PROXY -c "
f"nix develop --ignore-env --keep-env-var HOME --keep-env-var ALL_PROXY "
f"--keep-env-var CARGO_TARGET_DIR -c "
f"cargo run --release --features p2e -- build p2e "
f"--rtl-dir=\"{vsrc_dir}\" "
f"--out-dir=\"{build_dir}\""
Expand Down
10 changes: 10 additions & 0 deletions api/steps/bebop/p2e/03_runworkload_api.step.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ async def handler(request: ApiRequest, ctx: FlowContext) -> ApiResponse:
"message": "image and bitstream parameters are required",
},
)
if body.get("diff", False) and not body.get("golden-elf"):
return ApiResponse(
status=400,
body={
"success": False,
"failure": True,
"returncode": 400,
"message": "--diff requires --golden-elf <path>",
},
)

await ctx.enqueue({
"topic": "bebop.p2e.runworkload",
Expand Down
97 changes: 88 additions & 9 deletions api/steps/bebop/p2e/03_runworkload_event.step.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import glob
import os
import re
import shlex
import sys
from datetime import datetime
from pathlib import Path

from motia import FlowContext, queue

Expand Down Expand Up @@ -51,6 +53,41 @@ def case_uses_multi_fpga(build_dir: str) -> bool:
return len([path for path in part_dirs if os.path.isdir(path)]) > 1


def runtime_build_command(bbdir: str, chip: str, diff: bool, vsrc_dir: str, build_dir: str) -> tuple[str, str]:
manifest = Path(bbdir) / "bebop" / "Cargo.toml"
features = ["p2e"]
if diff:
manifest = Path(bbdir) / "examples" / "chips" / chip / "generated" / "bebop" / "Cargo.toml"
features.extend(["bemu", "difftest"])
command = shlex.join(
[
"env",
"BEBOP_P2E_RUNTIME_ONLY=1",
"BEBOP_P2E_REBUILD_RUNTIME=1",
f"VSRC_PATH={vsrc_dir}",
f"OUT_PATH={build_dir}",
"cargo",
"run",
"--release",
"--manifest-path",
str(manifest),
"--bin",
"bebop",
"--features",
",".join(features),
"--",
"build",
"p2e",
"--rtl-dir",
vsrc_dir,
"--out-dir",
build_dir,
*(["--diff"] if diff else []),
]
)
return command, str(manifest.parent)


async def handler(input_data: dict, ctx: FlowContext) -> None:
origin_tid = get_origin_trace_id(input_data, ctx)
try:
Expand All @@ -70,6 +107,27 @@ async def handler(input_data: dict, ctx: FlowContext) -> None:
bitstream = input_data.get("bitstream", "")
multi_fpga = bool(input_data.get("multi-fpga", False))
wave = bool(input_data.get("wave", False))
diff = bool(input_data.get("diff", False))
golden_elf = input_data.get("golden-elf", "")
golden_pk = bool(input_data.get("golden-pk", False))
if diff and not golden_elf:
ctx.logger.error("--diff requires --golden-elf <path>")
await check_result(
ctx, 1, continue_run=False,
extra_fields={"error": "missing_golden_elf"},
trace_id=origin_tid,
)
return
if diff and not os.path.isfile(golden_elf):
ctx.logger.error(f"BEMU golden ELF not found: {golden_elf}")
await check_result(
ctx, 1, continue_run=False,
extra_fields={"error": "golden_elf_not_found", "golden_elf": golden_elf},
trace_id=origin_tid,
)
return
if diff:
golden_elf = os.path.abspath(golden_elf)
if "wave_start" in input_data:
ctx.logger.error("invalid parameter: --wave_start (use --wave-start)")
await check_result(
Expand Down Expand Up @@ -134,27 +192,42 @@ async def handler(input_data: dict, ctx: FlowContext) -> None:

# Rebuild the VVAC host runtime in the bitstream case. The bitstream is
# deliberately left in place, so runtime/DPIC changes never trigger FPGA synthesis.
runtime_cmd = (
f"env BEBOP_P2E_RUNTIME_ONLY=1 BEBOP_P2E_REBUILD_RUNTIME=1 "
f"cargo run --release --features p2e -- build p2e "
f"--rtl-dir=\"{vsrc_dir}\" "
f"--out-dir=\"{build_dir}\""
)
runtime_cmd, runtime_cwd = runtime_build_command(bbdir, chip, diff, vsrc_dir, build_dir)
runtime_env = {**os.environ.copy(), **bebop_cargo_env(bbdir, chip)}
if diff:
hpec_home = "/home/x-epic/hpe-24.12.01.s008"
runtime_env["BEBOP_BEMU_P2E_ABI"] = "1"
runtime_env["BEBOP_BEMU_CC"] = os.path.join(
hpec_home, "tools", "gcc-8.3.0", "gcc-8.3.0", "bin", "gcc"
)
runtime_env["BEBOP_BEMU_CXX"] = os.path.join(
hpec_home, "tools", "gcc-8.3.0", "gcc-8.3.0", "bin", "g++"
)
runtime_env["BEBOP_BEMU_DTC"] = os.path.join(bbdir, "result", "bin", "dtc")
runtime_env["CARGO_TARGET_DIR"] = os.path.join(bebop_dir, "target", f"{chip}-p2e-diff")
compiler_libs = [
os.path.join(hpec_home, "tools", "gcc-8.3.0", "gmp-6.2.1", "lib"),
os.path.join(hpec_home, "tools", "gcc-8.3.0", "mpfr-4.1.0", "lib"),
os.path.join(hpec_home, "tools", "gcc-8.3.0", "mpc-1.2.1", "lib"),
]
runtime_env["BEBOP_BEMU_COMPILER_LIBRARY_PATH"] = ":".join(compiler_libs)
ctx.logger.info("Preparing bebop p2e runtime for the selected bitstream ...")
runtime_result = await stream_run_logger_async(
cmd=runtime_cmd,
logger=ctx.logger,
cwd=bebop_dir,
cwd=runtime_cwd,
stdout_prefix="bebop p2e runtime",
stderr_prefix="bebop p2e runtime",
env={**os.environ.copy(), **bebop_cargo_env(bbdir, chip)},
env=runtime_env,
)
runtime_lib_dir = os.path.join(build_dir, "vvacDir", "runtimeDir", "lib", "lib_arm")
rtcfg_path = os.path.join(build_dir, "vvacDir", "runtimeDir", "rtcfg")
libvctb_path = os.path.join(runtime_lib_dir, "libvCtb.so")
libstdcxx_path = os.path.join(runtime_lib_dir, "libstdc++.so.6")
bebop_p2e_path = os.path.join(build_dir, "bebop-p2e")
runtime_artifacts = (rtcfg_path, libvctb_path, libstdcxx_path, bebop_p2e_path)
runtime_artifacts = [rtcfg_path, libvctb_path, libstdcxx_path, bebop_p2e_path]
if diff:
runtime_artifacts.append(os.path.join(build_dir, "libriscv.so"))
if runtime_result.returncode != 0 or not all(os.path.isfile(path) for path in runtime_artifacts):
missing = [path for path in runtime_artifacts if not os.path.isfile(path)]
if missing:
Expand Down Expand Up @@ -189,6 +262,10 @@ async def handler(input_data: dict, ctx: FlowContext) -> None:
run_cmd += " --wave"
if wave_start is not None:
run_cmd += f" --wave-start=\"{wave_start}\""
if diff:
run_cmd += f" --diff --golden-elf={shlex.quote(golden_elf)}"
if golden_pk:
run_cmd += " --golden-pk"
for trace_name in ("itrace", "mtrace", "pmctrace", "ctrace", "banktrace"):
if input_data.get(trace_name, False):
run_cmd += f" --{trace_name}"
Expand Down Expand Up @@ -221,6 +298,8 @@ async def handler(input_data: dict, ctx: FlowContext) -> None:
"log_dir": run_log,
"bdb_trace": os.path.join(run_log, "bdb.ndjson"),
"uart_log": os.path.join(run_log, "uart.log"),
"bank_diff": os.path.join(run_log, "bank_diff.ndjson") if diff else None,
"diff": diff,
"timestamp": timestamp,
},
trace_id=origin_tid,
Expand Down
4 changes: 4 additions & 0 deletions api/steps/config/01_install_event.step.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ async def handler(input_data: dict, ctx: FlowContext) -> None:
bemu = step4.install_bemu(chip, bbdir, gen)
ctx.logger.info(f"Installed bemu crate: {bemu}")

bebop = step4.install_bebop(gen)
ctx.logger.info(f"Installed bebop crate: {bebop}")

# C. TO Workload BUILD SYSTEM
workload = step4.install_workload(chip, bbdir, chip_name, gen)
ctx.logger.info(f"Installed workload defs: {workload}")
Expand All @@ -136,6 +139,7 @@ async def handler(input_data: dict, ctx: FlowContext) -> None:
"chip": chip_name,
"arch": str(arch),
"bemu": str(bemu),
"bebop": str(bebop),
"workload": str(workload),
}
)
Expand Down
53 changes: 53 additions & 0 deletions api/steps/config/scripts/4_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,59 @@ def install_bemu(chip: pb.Chip, bbdir: Path, gen: Path) -> Path:
return bemu / "Cargo.toml"


def install_bebop(gen: Path) -> Path:
bebop = gen.parent.parent / "generated" / "bebop"
bebop.mkdir(parents=True, exist_ok=True)
manifest = bebop / "Cargo.toml"
manifest.write_text(
"""[workspace]
resolver = "2"

[package]
name = "bebop"
version = "0.1.0"
edition = "2021"
build = "../../../../../bebop/build.rs"

[[bin]]
name = "bebop"
path = "../../../../../bebop/src/main.rs"

[features]
default = []
verilator = ["dep:bebop-verilator"]
p2e = ["dep:bebop-p2e"]
bemu = ["dep:bebop-bemu"]
difftest = ["bebop-bemu/difftest"]

[dependencies]
bebop-verilator = { path = "../../../../../bebop/src/nodes/verilator", optional = true }
bebop-p2e = { path = "../../../../../bebop/src/nodes/p2e", optional = true }
bebop-bemu = { path = "../../configs/generated/bemu", optional = true }
bebop-dasm = { path = "../../../../../bebop/src/nodes/lib/dasm" }
bebop-bank-hash = { path = "../../../../../bebop/src/nodes/lib/bank-hash" }
bebop-bemu-profile = { path = "../../../../../bebop/src/nodes/lib/bemu-profile" }
bebop-fd-redirect = { path = "../../../../../bebop/src/nodes/lib/fd-redirect" }
bebop-rtl-trace = { path = "../../../../../bebop/src/nodes/lib/rtl-trace" }
bebop-uart = { path = "../../../../../bebop/src/nodes/lib/uart" }
clap = { version = "4", features = ["derive"] }
libc = "0.2"
log = "0.4"
env_logger = "0.11"
nix = { version = "0.29", features = ["fs", "mman", "signal", "process"] }
toml = "0.8"
ctrlc = "3"
camino = "1.1"
snafu = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
duct = "0.13"
""",
encoding="utf-8",
)
return manifest


def install_workload(chip: pb.Chip, bbdir: Path, name: str, gen: Path) -> Path:
if not chip.profiles:
raise ValueError(f"chip {name}: no compiler profiles")
Expand Down
2 changes: 1 addition & 1 deletion api/steps/mill/scripts/mill.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def elaborate_cmd(main: str, config: str, out: str, *, seq_mem: bool = False) ->
f" --repl-seq-mem --repl-seq-mem-file={shlex.quote(os.path.join(out, 'mems.conf'))}"
)
return (
f"mill -i __.test.runMain {main} {config} "
f"flock /tmp/buckyball-mill.lock mill -i __.test.runMain {main} {config} "
"--disable-annotation-unknown --strip-debug-info -O=debug "
f"--split-verilog -o={shlex.quote(out)}{extra}"
)
53 changes: 28 additions & 25 deletions api/steps/uvm/scripts/uvm_common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
import re
import shlex
import sys
import tomllib
Expand All @@ -12,22 +13,16 @@
from utils.path import get_buckyball_path, log_dir
from utils.stream_run import stream_run_logger

config_scripts = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "config", "scripts")
)
sys.path.insert(0, config_scripts)


def load_chip(bbdir: str, chip: str):
config_scripts = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "config", "scripts")
)
if config_scripts not in sys.path:
sys.path.insert(0, config_scripts)
try:
import chip_pb2
except ImportError as e:
raise FileNotFoundError(
f"missing {os.path.join(config_scripts, 'chip_pb2.py')}; run bbdev config --install"
) from e
import chip_pb2

path = Path(bbdir) / "examples" / "chips" / chip / "configs" / "generated" / "chip.pb"
if not path.is_file():
raise FileNotFoundError(f"missing {path}; run bbdev config --install")
msg = chip_pb2.Chip()
msg.ParseFromString(path.read_bytes())
if not msg.name or not msg.cores:
Expand All @@ -36,15 +31,7 @@ def load_chip(bbdir: str, chip: str):


def ball_domain(chip):
d0 = chip.cores[0].balldomain
key = [(m.ball_id, m.ball_dir, m.in_bw, m.out_bw) for m in d0.mappings]
isa = [(e.mnemonic, e.funct7, e.bid) for e in d0.isa]
for core in chip.cores[1:]:
k = [(m.ball_id, m.ball_dir, m.in_bw, m.out_bw) for m in core.balldomain.mappings]
i = [(e.mnemonic, e.funct7, e.bid) for e in core.balldomain.isa]
if k != key or i != isa:
raise ValueError("chip.pb cores have different balldomains")
return d0
return chip.cores[0].balldomain


def selected_mappings(domain, ball: str | None):
Expand All @@ -70,13 +57,23 @@ def vcs_defines(domain, mapping, bank_entries: int):
return defs


def smatmul_accumulator_filename(rtl_dir: Path) -> str:
text = (rtl_dir / "SMatMulUnit.sv").read_text()
modules = re.findall(r"\b(accumulator_\d+x\d+)\s+accumulator_ext\s*\(", text)
if len(modules) != 1:
raise ValueError(f"expected one SMatMul accumulator instance, found {modules!r}")
return f"{modules[0]}.sv"


def _filelist(
verify_dir: Path, ball_dir: str, uvm_rel: str, rtl_rel: str, sim_dir: Path
verify_dir: Path, ball_dir: str, uvm_rel: str, rtl_rel: str, rtl_dir: Path, sim_dir: Path
) -> str:
src = verify_dir / "filelists" / f"{ball_dir}_ball.f"
dst = sim_dir / f"{ball_dir}_ball.f"
sim_dir.mkdir(parents=True, exist_ok=True)
text = src.read_text()
if "@SMATMUL_ACCUMULATOR@" in text:
text = text.replace("@SMATMUL_ACCUMULATOR@", smatmul_accumulator_filename(rtl_dir))
dst.write_text(text.replace("@UVM@", uvm_rel).replace("@RTL@", rtl_rel))
return str(dst.relative_to(verify_dir))

Expand All @@ -90,7 +87,7 @@ def build_ball(bbdir: str, chip_name: str, mill_cfg: str, domain, mapping, ctx)
sim_dir = verify_dir / "build" / chip_name
uvm_rel = os.path.relpath(Path(bbdir) / "verify" / "uvm", verify_dir)
rtl_rel = os.path.relpath(rtl_dir, verify_dir)
flist = _filelist(verify_dir, ball, uvm_rel, rtl_rel, sim_dir)
flist = _filelist(verify_dir, ball, uvm_rel, rtl_rel, rtl_dir, sim_dir)
cargo = (
f"nix develop {shlex.quote(str(Path(bbdir) / 'verify'))} --command "
f"cargo build --manifest-path {shlex.quote(str(casegen))}"
Expand Down Expand Up @@ -180,7 +177,13 @@ def run_chip(bbdir: str, chip: str, ball: str | None, ctx, do_run: bool) -> dict
msg = load_chip(bbdir, chip)
domain = ball_domain(msg)
mill_cfg = msg.mill.verilator_config
maps = selected_mappings(domain, ball)
maps = [
m
for m in selected_mappings(domain, ball)
if (Path(bbdir) / "examples" / "balls" / m.ball_dir / "verify" / "filelists" / f"{m.ball_dir}_ball.f").is_file()
]
if ball is not None and not maps:
raise ValueError(f"UVM filelist missing for ball {ball!r}")
ran = []
covs = []
run_root = None
Expand Down
Loading
Loading