Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions packages/chip/docs/project/prototype-status-dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ Snapshot: updated after 2026-05-19 PD closure retry and local tool recovery.
| --- | --- | --- | --- |
| docs-and-project-plan | `PASS` | `command_pass` | `none` |
| architecture-docs | `PASS` | `command_pass` | `none` |
| toolchain-fast-path | `PASS` | `tool_available` | `none` |
| toolchain-fast-path | `BLOCK` | `tool_blocker` | `scripts/check_tools.sh && scripts/tool_versions.sh` |
| platform-contract | `PASS` | `command_pass` | `none` |
| linux-boot-prerequisites | `PASS` | `command_pass` | `none` |
| software-bsp | `BLOCK` | `scaffold_only` | `make software-bsp-evidence-check` |
| real-world-release-gates | `PASS` | `command_pass` | `none` |
| rtl-source | `PASS` | `source_present` | `none` |
| synthesis | `PASS` | `generated_artifact` | `none` |
| cocotb | `PASS` | `generated_artifact` | `none` |
| verilator | `PASS` | `generated_artifact` | `none` |
| formal | `PASS` | `generated_artifact` | `none` |
| qemu | `PASS` | `generated_artifact` | `none` |
| renode | `PASS` | `generated_artifact` | `none` |
| synthesis | `BLOCK` | `tool_blocker` | `make synth` |
| cocotb | `BLOCK` | `regen_required` | `make cocotb cocotb-npu cocotb-contract cocotb-cpu` |
| verilator | `BLOCK` | `tool_blocker` | `make verilator` |
| formal | `BLOCK` | `tool_blocker` | `make formal inside Docker/Nix` |
| qemu | `BLOCK` | `tool_blocker` | `make qemu-check` |
| renode | `BLOCK` | `tool_blocker` | `make renode-check` |
| npu-ml-proof | `PASS` | `generated_artifact` | `none` |
| minimum-linux-npu-target | `BLOCK` | `tool_blocker` | `make minimum-linux-npu-target-strict` |
| pd-contract | `PASS` | `command_pass` | `none` |
| product-package | `BLOCK` | `release_blocker` | `close package/FPGA/KiCad/PD/manufacturing release blockers or keep product claim below fabrication` |
| benchmarks | `BLOCK` | `scaffold_only` | `python3 benchmarks/run_benchmarks.py run --metadata benchmarks/metadata/strict-blocked-template.json --strict-missing` |
| release-pipeline | `PASS` | `generated_artifact` | `none` |
| benchmarks | `BLOCK` | `regen_required` | `make benchmarks-dry-run or run the final-macbook-host-smoke benchmark set` |
| release-pipeline | `BLOCK` | `regen_required` | `make tool-versions pipeline-check` |

## Workstream Dashboard

Expand Down
45 changes: 26 additions & 19 deletions packages/chip/scripts/check_chipyard_generated_linux_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ def require(condition: bool, message: str, failures: list[str]) -> None:
failures.append(message)


def load_json(path: Path, failures: list[str]) -> dict:
def load_json(path: Path, failures: list[str], blockers: list[str] | None = None) -> dict:
if not path.is_file():
failures.append(f"missing {rel(path)}")
target = blockers if blockers is not None else failures
target.append(f"missing {rel(path)}")
return {}
try:
data = json.loads(read(path))
Expand All @@ -73,9 +74,10 @@ def mem_region(memmap: dict, name: str) -> tuple[int, int] | None:
return None


def check_dts(failures: list[str]) -> None:
def check_dts(failures: list[str], blockers: list[str]) -> None:
for path in (DTS, GEN_DTS):
require(path.is_file(), f"missing generated DTS: {rel(path)}", failures)
if not path.is_file():
blockers.append(f"missing generated DTS: {rel(path)}")
if not DTS.is_file():
return

Expand Down Expand Up @@ -109,8 +111,8 @@ def check_dts(failures: list[str]) -> None:
)


def check_memmap(failures: list[str]) -> None:
memmap = load_json(MEMMAP, failures)
def check_memmap(failures: list[str], blockers: list[str]) -> None:
memmap = load_json(MEMMAP, failures, blockers)
if not memmap:
return

Expand All @@ -131,15 +133,15 @@ def check_memmap(failures: list[str]) -> None:
)


def check_regmaps(failures: list[str]) -> None:
def check_regmaps(failures: list[str], blockers: list[str]) -> None:
required_by_regmap = {
"boot_address": ["bitWidth"],
"clint": ["msip_0", "mtimecmp_0", "mtime_0"],
"plic": ["priority_1", "pending_1", "enables_0", "threshold_0", "claim_complete_0"],
"uart": ["txdata", "rxdata", "txctrl", "rxen", "ie", "ip", "div"],
}
for name, path in REGMAPS.items():
data = load_json(path, failures)
data = load_json(path, failures, blockers)
if not data:
continue
text = json.dumps(data)
Expand All @@ -159,9 +161,9 @@ def bootrom_bytes_from_fir(path: Path) -> bytes:
return bytes(image)


def check_embedded_bootrom_dtb(failures: list[str]) -> None:
require(GEN_FIR.is_file(), f"missing generated FIR: {rel(GEN_FIR)}", failures)
def check_embedded_bootrom_dtb(failures: list[str], blockers: list[str]) -> None:
if not GEN_FIR.is_file():
blockers.append(f"missing generated FIR: {rel(GEN_FIR)}")
return
Comment thread
greptile-apps[bot] marked this conversation as resolved.

image = bootrom_bytes_from_fir(GEN_FIR)
Expand Down Expand Up @@ -202,7 +204,8 @@ def check_embedded_bootrom_dtb(failures: list[str]) -> None:


def check_import_state(failures: list[str], blockers: list[str]) -> None:
require(VERILOG.is_file(), f"missing generated Verilog: {rel(VERILOG)}", failures)
if not VERILOG.is_file():
blockers.append(f"missing generated Verilog: {rel(VERILOG)}")
if VERILOG.is_file():
text = read(VERILOG)
require(
Expand Down Expand Up @@ -240,24 +243,25 @@ def main() -> int:

failures: list[str] = []
blockers: list[str] = []
check_dts(failures)
check_memmap(failures)
check_regmaps(failures)
check_embedded_bootrom_dtb(failures)
check_dts(failures, blockers)
check_memmap(failures, blockers)
check_regmaps(failures, blockers)
check_embedded_bootrom_dtb(failures, blockers)
check_import_state(failures, blockers)

if failures:
print("STATUS: FAIL chipyard.generated_linux_contract")
for failure in failures:
print(f" - {failure}")
if blockers:
print(" blocked follow-up artifacts/evidence:")
for blocker in blockers:
print(f" - {blocker}")
return 1

print(
"STATUS: PASS chipyard.generated_linux_contract - generated DTS/memmap/regmaps expose minimum Linux launch nodes"
)
if blockers:
print(
"STATUS: BLOCKED chipyard.generated_linux_boot - generated AP is not boot-evidence complete:"
"STATUS: BLOCKED chipyard.generated_linux_contract - generated AP artifacts/evidence are not complete:"
)
for blocker in blockers:
print(f" - {blocker}")
Expand All @@ -269,6 +273,9 @@ def main() -> int:
)
return 1 if args.require_boot_evidence else 0
Comment thread
greptile-apps[bot] marked this conversation as resolved.

print(
"STATUS: PASS chipyard.generated_linux_contract - generated DTS/memmap/regmaps expose minimum Linux launch nodes"
)
print("STATUS: PASS chipyard.generated_linux_boot")
return 0

Expand Down
15 changes: 15 additions & 0 deletions packages/chip/scripts/check_minimum_linux_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ def check_evidence(path: Path) -> dict[str, Any]:
"blocked_marker": rel(blocked),
"reason": text.splitlines()[0] if text else "",
}
status_report = path.with_suffix(".json")
report = load_json(status_report)
if report.get("status") == "blocked":
blockers = report.get("blockers")
reason = ""
if isinstance(blockers, list) and blockers:
reason = str(blockers[0])
elif isinstance(report.get("progress"), dict):
reason = str(report["progress"].get("next_step", ""))
return {
"status": "blocked",
"path": rel(path),
"blocked_report": rel(status_report),
"reason": reason or "companion status report is blocked",
}
return {"status": "missing", "path": rel(path), "blocked_marker": rel(blocked)}


Expand Down
29 changes: 25 additions & 4 deletions packages/chip/scripts/run_qemu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,20 +120,41 @@ find_toolchain() {
fi
done

for cc in /opt/homebrew/opt/llvm/bin/clang clang; do
clang_candidates=${RISCV_CLANG_CANDIDATES:-"/opt/homebrew/opt/llvm/bin/clang clang"}
for cc in $clang_candidates; do
if command -v "$cc" >/dev/null 2>&1; then
if "$cc" --target=riscv64-unknown-elf -fuse-ld=lld -x assembler -c /dev/null -o /tmp/eliza-riscv-toolchain-test.o >/dev/null 2>&1; then
rm -f /tmp/eliza-riscv-toolchain-test.o
if clang_can_link_riscv_elf "$cc"; then
printf '%s\n' "$cc"
return 0
fi
rm -f /tmp/eliza-riscv-toolchain-test.o
fi
done

return 1
}

clang_can_link_riscv_elf() {
cc=$1
test_base="${TMPDIR:-/tmp}/eliza-riscv-toolchain-test.$$"
test_src="${test_base}.S"
test_elf="${test_base}.elf"
cat >"$test_src" <<'EOF'
.section .text
.globl _start
_start:
j _start
EOF
if "$cc" --target=riscv64-unknown-elf -fuse-ld=lld \
-nostdlib -nostartfiles -ffreestanding \
-march=rv64imac -mabi=lp64 \
-x assembler "$test_src" -o "$test_elf" >/dev/null 2>&1; then
rm -f "$test_src" "$test_elf"
return 0
fi
rm -f "$test_src" "$test_elf"
return 1
}

explain_toolchain_blocker() {
cat <<EOF
BLOCKED: no RISC-V ELF toolchain found on PATH.
Expand Down
39 changes: 39 additions & 0 deletions packages/chip/scripts/test_qemu_smoke_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import json
import os
import shutil
import stat
import subprocess
import tempfile
Expand All @@ -28,6 +29,14 @@ def write_executable(path: Path, text: str) -> None:
path.chmod(path.stat().st_mode | stat.S_IXUSR)


def link_host_tools(bindir: Path, *names: str) -> None:
for name in names:
host_tool = shutil.which(name)
if host_tool is None:
raise AssertionError(f"missing host tool required by test: {name}")
(bindir / name).symlink_to(host_tool)


def run_check(env: dict[str, str]) -> subprocess.CompletedProcess[str]:
merged = os.environ.copy()
merged.update(env)
Expand Down Expand Up @@ -100,6 +109,35 @@ def test_build_failure_is_fail() -> None:
assert_contains(result.stdout, "STATUS: FAIL qemu.build")


def test_autodetected_clang_without_lld_is_blocked() -> None:
with tempfile.TemporaryDirectory() as td:
bindir = Path(td)
link_host_tools(bindir, "cat", "dirname", "grep", "mkdir", "rm", "rmdir", "sh")
clang = bindir / "clang"
write_executable(
clang,
"#!/bin/sh\n"
'case "$*" in\n'
" *-fuse-ld=lld*) echo 'clang: error: invalid linker name in argument' >&2; exit 1 ;;\n"
" *) exit 0 ;;\n"
"esac\n",
)
result = run_check(
{
"PATH": str(bindir),
"RISCV_CC": "",
"RISCV_CLANG_CANDIDATES": "clang",
"REQUIRE_QEMU": "0",
}
)
if result.returncode != 0:
raise AssertionError(
f"expected missing lld to be non-strict BLOCKED, got {result.returncode}\n{result.stdout}"
)
assert_contains(result.stdout, "STATUS: PASS qemu.semantic")
assert_contains(result.stdout, "STATUS: BLOCKED qemu.build")

Comment thread
greptile-apps[bot] marked this conversation as resolved.

def test_fake_toolchain_and_qemu_pass() -> None:
QEMU_ELF.unlink(missing_ok=True)
with tempfile.TemporaryDirectory() as td:
Expand Down Expand Up @@ -275,6 +313,7 @@ def main() -> int:
test_missing_toolchain_is_non_strict_blocked,
test_missing_toolchain_is_strict_blocked,
test_build_failure_is_fail,
test_autodetected_clang_without_lld_is_blocked,
test_fake_toolchain_and_qemu_pass,
test_os_boot_check_blocks_without_payloads,
test_os_boot_check_fails_on_kernel_panic,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ source_ids:
dut: e1_linux_soc_contract
scope: >
Dry-run coverage targets for future AI-assisted cocotb stimulus proposals
against interrupt-controller, CLINT placeholder, and reset-domain edges.
against interrupt-controller, future CLINT, and reset-domain edges.
Candidate tests are not evidence until accepted into source and passing the
named cocotb gates.
required_followup_gates:
Expand Down Expand Up @@ -34,6 +34,6 @@ bins:
- verify/cocotb/test_reset_domain_cleanup.py::reset_clears_handshake_signals
- id: clint_future_timer_irq_contract
description: Future CLINT timer interrupt contract must elaborate even while skipped.
required_observation: skipped scaffold remains discoverable until CLINT lands in the SoC contract
required_observation: fail-closed skipped contract remains discoverable until CLINT lands in the SoC contract
existing_reference:
- verify/cocotb/test_clint_timer_irq.py::clint_timer_irq_fires_when_mtime_ge_mtimecmp
Loading