OpenPitonWorkspaceNode
OpenPitonWorkspaceNode, in chia_openpiton/openpiton_workspace.py, drives one OpenPiton checkout on one worker. sims, build, run, and regress call OpenPiton’s sims tool. The other members query or edit the checkout directly.
from chia.base.ChiaFunction import get
from chia_openpiton.openpiton_workspace import OpenPitonWorkspaceNode
with OpenPitonWorkspaceNode("/work/openpiton") as node:
cfg = get(node.configure.chia_remote(x_tiles=1, y_tiles=1))
art = get(node.build.chia_remote(cfg))
res = get(node.run.chia_remote(cfg, "hello_world.c"))
assert res.success
Construction
OpenPitonWorkspaceNode(piton_root: str, *, root_on_remote_worker: bool = False, **kwargs)
piton_root must name an existing local directory. With root_on_remote_worker=True, the node accepts an absolute path that exists only on a remote worker. That mode requires require_colocated=False, and the caller schedules each call. The other keyword arguments go to CHIA’s ColocatedNode:
Argument |
Default |
Effect |
|---|---|---|
|
|
Pin members to an existing placement group |
|
|
Reserve a one-bundle placement group of |
|
|
Bundle to pin to |
|
|
Bundle shape for a self-reserved group |
|
|
Strategy for a self-reserved group |
|
|
Block until the reservation is ready |
|
|
Timeout for that wait |
As a context manager, the node releases a placement group it reserved. node.task_options returns the scheduling options that place an actor, such as a PitonToolServer, on the node’s bundle.
Calling members
Each member declares resources={"openpiton": 1} and has five call forms:
Call |
Behaviour |
|---|---|
|
Runs remotely on the node’s bundle; returns a Ray reference |
|
Same, and returns the value |
|
Runs in the calling process |
|
Returns a handle with extra Ray options and the root still bound |
|
Runs remotely, unpinned |
Members
The signatures below are the class attributes. Instance calls omit piton_root.
sims(piton_root: str, args: str, core: str = "ariane", cwd: str | None = None,
timeout_seconds: int = 3600) -> tuple[int, str, str]
configure(piton_root: str, x_tiles: int = 1, y_tiles: int = 1, core: str = "ariane",
network_config: str = "2dmesh_config",
config_rtl: tuple[str, ...] = ("MINIMAL_MONITORING",),
caches: dict[str, tuple[int, int]] | None = None,
address_map: str | None = None, extra_flags: tuple[str, ...] = (),
sys: str = "manycore", timeout_seconds: int = 300) -> PitonConfig
build(piton_root: str, config: PitonConfig, sim_type: str = "vlt", clean: bool = False,
extra_build_args: tuple[str, ...] = (), timeout_seconds: int = 7200) -> PitonBuildArtifact
run(piton_root: str, config: PitonConfig, test: str, sim_type: str = "vlt",
precompiled: bool = False, asm_diag_root: str | None = None,
finish_mask: str | None = None, rtl_timeout: int | None = None,
max_cycle: int | None = None, extra_run_args: tuple[str, ...] = (),
timeout_seconds: int = 3600) -> PitonRunResult
regress(piton_root: str, config: PitonConfig, tests: tuple[str, ...], group: str = "",
sim_type: str = "vlt", precompiled: bool = False,
asm_diag_root: str | None = None, timeout_seconds: int = 3600) -> PitonRegressResult
put_file(piton_root: str, relpath: str, content: bytes | str) -> str
apply_edits(piton_root: str, edits: dict[str, str], staged: str = "") -> dict
revert_edits(piton_root: str) -> list[str]
collect(piton_root: str, base_dir: str, patterns: tuple[str, ...],
max_bytes_per_file: int | None = None) -> PitonCollectResult
clean(piton_root: str, config: PitonConfig) -> bool
Member |
Behaviour |
|---|---|
|
Runs |
|
Returns a validated PitonConfig with the checkout’s revisions recorded |
|
Builds the model, or returns the cached one |
|
Runs one diag and judges it from the transcript |
|
Calls |
|
Writes |
|
Replaces the content of existing files: |
|
Restores every file |
|
Globs |
|
Removes the configuration’s model directory and returns whether it existed. |
verilator_version_text(root, core="ariane", timeout_seconds=120) -> str is a plain static method. It returns verilator --version as seen inside OpenPiton’s environment and caches successful results per (root, core).
source_fingerprint(root, version_text, timeout_seconds=60) -> str is another plain static method. It returns the SHA-256 hex digest that the build cache checks, described under Build cache below.
configure
A given address_map replaces piton/verif/env/manycore/devices_ariane.xml, the checkout’s single copy of the simulation device map. configure then runs four probes concurrently, and a failed git probe records "":
Probe |
Field |
|---|---|
|
|
|
|
|
|
|
|
It returns the resulting PitonConfig, whose construction raises ValueError on an invalid mesh, core, network, or cache. A caches value of None or an empty mapping selects DEFAULT_CACHES, and a partial mapping keeps the defaults for the caches it leaves out.
build
Rejects a
sim_typeoutsidevlt,vcs,ncv,icv,msm, andrivwithValueError. Onlyvlt(Verilator) is license-free.Refuses an Ariane configuration whose L1D or L1I has more ways than its L1.5 (see Ariane’s way rule). It returns a failed artifact without running
sims:returncode=-1,failure_reason="way_rule", and the lines ofconfig.way_violations()aserrors. A cached model of such a configuration is not served either.Raises
ValueErrorwhengit diff -- piton/verif/env/manycorediffers fromconfig.diff, for example after anotherconfigure(address_map=...)call on the checkout. The check runs only for a configuration that recorded the checkout’s state, a non-emptysource_revordiff, asconfigure()sets. A configuration with both fields empty, such as each one MACE’s loop constructs, builds the checkout as it stands.Returns the cached model, if one exists and the checkout has not changed since it was built.
With
clean=True, or when the cached model is stale, deletes the model’sobj_dirand marker.For
sim_type="vlt", adds--no-timingwhen Verilator is version 5 or later, unless anextra_build_argsentry already containstiming. The version comes fromconfig.verilator_version, or fromverilator_version_text().Runs, in
$PITON_ROOT/build:
sims <config.sims_flags()> -build_id=<config.build_id> -<sim_type>_build [-<sim_type>_build_args=<arg> ...]
success requires exit status 0 and the model binary. A failed build carries the tag build_timeout when its timeout stopped it, else a build_failure_reason tag, or no_model_binary when no tag matches, as when sims exits 0 without a binary.
Build cache
A successful build writes the marker file .mace_build_ok into the model directory. The marker holds config.key and the checkout’s source fingerprint, taken before sims ran. With clean=False, build returns at once, without running sims, when the binary exists and the marker holds the current key and fingerprint. That artifact has reused=True and wall_time_s=0.0. build trusts the marker over the binary, because a worker killed mid-link can leave a truncated binary.
The fingerprint hashes the Verilator version and, for the checkout and its Ariane submodule, three things:
the commit, from
git rev-parse HEAD;the edits to tracked files, from
git diff HEAD;the untracked files that
.gitignoredoes not list, by content, or by size and modification time above 1 MiB.
OpenPiton’s .gitignore lists the build outputs, such as pyHP’s .tmp.v files and everything under build/, so a build leaves the fingerprint of its checkout unchanged. The aws submodule is left out: simulation does not read it, and its diff on a Linux checkout runs to hundreds of megabytes. On a patched checkout the fingerprint takes about 0.1 s.
A build ID covers the configuration, so a patch, an RTL edit, or a new commit leaves it unchanged. When the marker’s fingerprint no longer matches, build logs the checkout or Verilator changed since it was built, deletes the model’s obj_dir and marker, and rebuilds. A marker written before the fingerprint existed holds only the key, so its model rebuilds once.
Edits inside the Ariane submodule’s own submodules, such as corev_apu/rv_plic, change the fingerprint only when they move a submodule commit or make a clean submodule dirty. After such an edit, rebuild with clean=True.
run
For the default sys="manycore", run executes this command in a fresh run directory:
sims <config.sims_flags()> -build_id=<config.build_id> [-precompiled] [-asm_diag_root=<dir>]
-finish_mask=<mask> [-rtl_timeout=<n>] [-max_cycle=<n>] <extra_run_args> -<sim_type>_run <test>
finish_mask defaults to config.finish_mask, one 1 per tile, so a multi-tile run passes only when every hart hits the good trap. precompiled=True makes sims look in $ARIANE_ROOT/tmp/riscv-tests/build for a prebuilt riscv-tests ELF instead of compiling the source. asm_diag_root adds a directory to search for the diag source. For another sys, run drops the manycore options and the test name; pass test selection in extra_run_args.
The verdict comes from parse.sim_verdict() over the full sim.log, or over stdout when there is no sim.log. A call that timed out without a verdict gets "timeout".
Directory layout
$PITON_ROOT/build/ working directory for build() and the sims() member
manycore/ config.sys
mace_<12 hex digits>/ config.build_id (model_dir)
.mace_build_ok success marker
obj_dir/Vcmp_top Verilator model (binary_path)
runs/
<test>-<epoch ms>-<8 hex>/ run_dir, one per run() call
sim.log status.log fake_uart.log mem.image symbol.tbl diag.exe
The run directory name replaces / in the test name with _. For another sys, the model lives under build/<sys>/, and the binary is the single executable V* file in obj_dir.
Timeouts and processes
Member |
Default |
|---|---|
|
3600 |
|
300, per probe |
|
7200 |
|
3600 |
|
3600, per test |
Every sims and verilator --version command runs under bash -lc in its own session. On timeout, the adapter kills the whole process group, keeps the partial output, returns returncode=-1, and appends sims timed out after <N>s to stderr. A launch failure also returns -1, without that marker. On KeyboardInterrupt, the adapter kills the process group and re-raises. The git probes run without a shell and return "" on timeout.
A SIGTERM or SIGKILL that ends the Python process reaches neither path, so each command also starts a watchdog, which checks every 2 s that the process that started it is alive and kills the command’s process group once it has exited. Stopping a batch therefore stops its builds and simulations too.
Environment
Each bash -lc command first unsets LD_LIBRARY_PATH, then exports PITON_ROOT and sources piton/piton_settings.bash. The sparc and pico cores need nothing more. For core="ariane", the prologue sets these variables before sourcing:
Variable |
Value |
|---|---|
|
|
|
The existing value, else |
|
|
|
|
|
|
OpenPiton’s prebuilt configsrch, which sims runs to look up each tool’s version, is a host binary. The Nix dev shell exports an LD_LIBRARY_PATH that holds Nix’s libstdc++, which needs a newer glibc than an Ubuntu or Debian host has. With that path, configsrch fails to load, and sims stops with configsrch returned error code 1. The prologue clears the caller’s value for this reason.
piton_settings.bash puts /usr/bin first on PATH, so after sourcing, the Ariane prologue prepends $RISCV/bin, and every prologue prepends $VERILATOR_ROOT/bin when VERILATOR_ROOT is set. Setting VERILATOR_ROOT therefore selects the Verilator for every core.
Result types
PitonBuildArtifact
Field |
Type |
Meaning |
|---|---|---|
|
|
Exit status 0 and model binary present |
|
|
|
|
|
The configuration built |
|
|
Simulator selector |
|
|
|
|
|
Model binary; |
|
|
Build wall time; |
|
|
Version text behind the |
|
|
|
|
|
Failure tag, |
|
|
|
|
|
Tail of each stream. |
|
|
Error lines from the full output of a failed build, from build_errors, each one that names an undefined name followed by the closest names in its file (with_close_names), or for a |
PitonRunResult
Field |
Type |
Meaning |
|---|---|---|
|
|
|
|
|
|
|
|
Diag name |
|
|
Simulator selector |
|
|
This run’s directory |
|
|
|
|
|
Simulation time stamped on the verdict line |
|
|
|
|
|
|
|
|
Run wall time |
|
|
Tail of |
|
|
Tail of |
|
|
Tail of |
|
|
As in |
|
|
For |
Each tail keeps the last 8000 characters. For the configurations this adapter builds, status.log has no Cyc= field, so cycles stays None and sim_time measures run length. The static method PitonRunResult.decide(returncode, verdict) holds the pass rule.
Other results
Type |
Fields |
|---|---|
|
|
|
|