mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat(tiny): implement R2-P7 clock and reset lifecycle
Integrate the Tiny RCU, normalized REF24 control, external-input profiles, single PLL backend, SYS/MEM/PCLK clocks, CPU-only WFI gating, peripheral lifecycle control and retained debug behavior. Keep CPU and all 32 main SRAM macros on SYS and the six private Crypto macros on PCLK. Add the handwritten RCU register/HAL contract, RS_EBUSY, bounded SDK operations and focused firmware. Preserve shared-IP defaults and the ICS55/PLL SAFE24 boot and explicit IHP130/no-PLL compatibility profiles. Use APB/AXI domain bridges and source-owned response holders to preserve backpressured responses, complete aborted bursts and prevent stale replay. Verify production DMA source-reset drain and common XPI endpoint reset with extracted integration bindings and an independent scoreboard. Add source-bound macro/clock audits, SAFE24 observational STA constraints and commercial-adapter host checks. No P8 functionality or operating-point qualification is included. See TINY-R2-P7 in docs/ip/tiny-soc.md. Validation: 54 P7 directed tests pass, including both simulators, ten source-reset ratio runs and two expected-negative drain mutations. RTL format/style, clock/reset inventory, Ruff and both Tiny PDK PR dry-runs pass. Retained 06-00 synthesis and strict netlist boots pass for both PDKs; STA remains negative and observational. Source comparison confirms only the follow-up tests changed after that backend campaign. Evidence: build/ics55-tiny-2026-10-14-06-00-ff4b5cef6eb5/meta/ tiny-r2-p7-source-reset/summary.json. Earlier firmware, HAL and compatibility results and the non-clean full-suite attempt retain their original attribution. Full Icarus firmware is deferred to final Tiny qualification; this commit does not constitute human phase acceptance or physical signoff.
This commit is contained in:
84 files changed
+6913
-360
No files matched your search
@@ -16,6 +16,8 @@ CDC_PRIMITIVES = {
|
||||
"async_fifo",
|
||||
"async_reqack",
|
||||
"axi4_async_bridge",
|
||||
"tiny_axi4_async_bridge",
|
||||
"tiny_apb_async_bridge",
|
||||
"cdc_2phase",
|
||||
"cdc_fifo",
|
||||
"cdc_sync",
|
||||
@@ -129,6 +131,21 @@ def validate(document_path: Path, root: Path) -> None:
|
||||
raw_crossings = document.get("crossings")
|
||||
if not isinstance(raw_crossings, list):
|
||||
raise ValueError("crossings must be a list")
|
||||
generated = document.get("generated_apb_crossings")
|
||||
if generated is not None:
|
||||
if (document.get("clock_contract") != "TINY-R2-P7" or
|
||||
generated.get("generator") != "scripts/generate_tiny.py" or
|
||||
generated.get("primitive") != "tiny_apb_async_bridge" or
|
||||
generated.get("source") != "system"):
|
||||
raise ValueError("unrecognized generated APB crossing contract")
|
||||
expected = {"aon": ["clint", "archinfo"], "memory": ["xpi"],
|
||||
"peripheral": ["gpio", "uart0", "timer0", "timer1", "i2c0", "dma",
|
||||
"pwm", "rtc", "wdg", "rng", "crc", "ws2812", "crypto", "i2s"]}
|
||||
if (generated.get("destinations") != expected or
|
||||
generated.get("same_clock_isolation") != ["sdio"] or
|
||||
generated.get("native_system") != ["sysctrl", "sram"] or
|
||||
not set(expected).issubset(names)):
|
||||
raise ValueError("P7 generated APB domain assignment mismatch")
|
||||
crossing_names: set[str] = set()
|
||||
for index, value in enumerate(raw_crossings):
|
||||
field = f"crossings[{index}]"
|
||||
|
||||
@@ -16,6 +16,9 @@ from scripts.rtl.generate_pin_map import generate as generate_pin_map # noqa: E
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
|
||||
TINY = ROOT / "rtl/tiny"
|
||||
RCU_TARGETS = {"gpio": 1, "uart0": 2, "i2c0": 3, "timer0": 4, "timer1": 5,
|
||||
"pwm": 6, "i2s": 7, "sdio": 8, "dma": 9, "ws2812": 10,
|
||||
"rng": 11, "crc": 12, "crypto": 13}
|
||||
|
||||
|
||||
def read_topology(path: Path, memory_map: Path) -> dict:
|
||||
@@ -77,9 +80,9 @@ def read_topology(path: Path, memory_map: Path) -> dict:
|
||||
if document.get("private_crypto") != {
|
||||
"banks": 6, "bank_bytes": 4096,
|
||||
"macro_instance": "u_soc.u_crypto.u_storage.gen_bank[{bank}].gen_macro.u_sram.u_mem",
|
||||
"clock_domain": "system", "logical_domain": "PCLK (SAFE24 alias)",
|
||||
"clock_domain": "peripheral", "logical_domain": "PCLK",
|
||||
}:
|
||||
raise ValueError("Tiny P6 requires six private Crypto banks on SAFE24 PCLK")
|
||||
raise ValueError("Tiny requires six private Crypto banks on PCLK")
|
||||
return document
|
||||
|
||||
|
||||
@@ -111,7 +114,7 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
|
||||
board.append("wire [31:0] s_gpio = {" +
|
||||
", ".join(f"s_gpio_{pin}" for pin in reversed(range(32))) + "};\n")
|
||||
atomic_write(output / "rtl/tiny_tb_gpio.svh", "\n".join(board))
|
||||
irq = [comment, "always_comb begin", " s_irq = '0;"]
|
||||
irq = [comment, "always_comb begin", " s_irq_async = '0;"]
|
||||
header = ["#ifndef RETROSOC_GENERATED_IRQ_METADATA_H",
|
||||
"#define RETROSOC_GENERATED_IRQ_METADATA_H", "#include <stdint.h>",
|
||||
"#define RS_SOC_IRQ_VECTOR_WIDTH UINT32_C(32)",
|
||||
@@ -119,7 +122,7 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
|
||||
f"#define RS_SOC_ALLOCATED_IRQ_COUNT UINT32_C({len(document['interrupts']) - 2})"]
|
||||
for item in document["interrupts"]:
|
||||
bit, name = item["core_bit"], item["name"].upper()
|
||||
irq.append(f" s_irq[{bit}] = {item['signal']};")
|
||||
irq.append(f" s_irq_async[{bit}] = {item['signal']};")
|
||||
header.append(f"#define RS_SOC_IRQ_{name} UINT32_C({bit})")
|
||||
if bit >= 2:
|
||||
header.append(f"#define RS_SOC_EXT_IRQ_{name} UINT32_C({bit - 2})")
|
||||
@@ -164,13 +167,51 @@ def generate_bindings(document: dict, output: Path, pdk: str = "IHP130",
|
||||
reset_assignments = []
|
||||
for index, target in enumerate(document["apb_targets"]):
|
||||
name = target["name"]
|
||||
clock = ("s_ref_clk" if name in ("clint", "archinfo") else
|
||||
"s_backend_mem_clk" if name == "xpi" else
|
||||
"s_exec_clk" if name in ("sysctrl", "sram", "sdio") else "s_backend_pclk")
|
||||
target_bit = RCU_TARGETS.get(name)
|
||||
ip_clock = f"s_{name}_clk" if target_bit is not None else clock
|
||||
if target_bit is not None:
|
||||
declarations.append(f"logic {ip_clock};")
|
||||
declarations.append(f"logic s_{name}_rst_n;")
|
||||
declarations.append(f"apb4_if u_{name}_apb4_if (.pclk(clk_i), .presetn(s_{name}_rst_n));")
|
||||
reset_assignments.append(f"assign s_{name}_rst_n = s_leaf_rst_n[{index + 1}];")
|
||||
bridges.append(f"assign s_target_rst_n[{index + 5}] = s_{name}_rst_n;")
|
||||
declarations.append(f"apb4_if u_{name}_apb4_if (.pclk({ip_clock}), .presetn(s_{name}_rst_n));")
|
||||
if target_bit is None:
|
||||
reset_assignments.append(f"assign s_{name}_rst_n = s_leaf_rst_n[{index + 1}];")
|
||||
else:
|
||||
reset_assignments.append(f"(* keep_hierarchy = 1 *) tiny_peripheral_leaf u_{name}_lifecycle ("
|
||||
f".clk_i({clock}), .rst_n_i(s_leaf_rst_n[{index + 1}]), "
|
||||
f".gate_i(s_gate_request[{target_bit}]), "
|
||||
f".reset_i(s_local_reset[{target_bit}]), "
|
||||
f".clk_o({ip_clock}), .rst_n_o(s_{name}_rst_n), "
|
||||
f".gated_o(s_actual_gate[{target_bit}]), "
|
||||
f".held_o(s_reset_held[{target_bit}]));")
|
||||
extra_ready = " && s_i2s_reset_ready" if name == "i2s" else ""
|
||||
if name == "dma":
|
||||
# CDC-ready outputs are observed in SYS, matching APB readiness.
|
||||
extra_ready = " && s_dma_ready_sys && s_dma_idle_sys"
|
||||
reset_assignments.append(f"assign s_reset_ready[{target_bit}] = s_{name}_rst_n && "
|
||||
f"s_apb_cdc_ready[{index}]{extra_ready};")
|
||||
reset_assignments.append(f"assign s_leaf_clk[{index + 1}] = {clock};")
|
||||
bridges.append(f"assign s_target_rst_n[{index + 5}] = s_fabric_rst_n;")
|
||||
crossed = clock != "s_exec_clk" or target_bit is not None
|
||||
endpoint = f"u_{name}_sys_apb4_if" if crossed else f"u_{name}_apb4_if"
|
||||
bridges.append(f"tiny_axi42apb4 u_{name}_bridge ("
|
||||
f".clk_i(clk_i), .rst_n_i(s_{name}_rst_n), "
|
||||
f".axi4(u_targets_axi4_if[{index + 5}]), .apb4(u_{name}_apb4_if));")
|
||||
f".clk_i(s_exec_clk), .rst_n_i(s_fabric_rst_n), "
|
||||
f".axi4(u_targets_axi4_if[{index + 5}]), .apb4({endpoint}));")
|
||||
if crossed:
|
||||
disabled = f"s_disabled_sys[{target_bit}]" if target_bit is not None else "1'b0"
|
||||
declarations.append(f"apb4_if {endpoint} (.pclk(s_exec_clk), .presetn(s_fabric_rst_n));")
|
||||
bridges.append(f"tiny_apb_async_bridge u_{name}_cfg_cdc ("
|
||||
f".src_clk_i(s_exec_clk), .src_rst_n_i(s_fabric_rst_n), "
|
||||
f".dst_clk_i({ip_clock}), .dst_rst_n_i(s_{name}_rst_n), "
|
||||
f".disabled_i({disabled}), "
|
||||
f".idle_o(s_apb_cdc_idle[{index}]), "
|
||||
f".ready_o(s_apb_cdc_ready[{index}]), "
|
||||
f".src_apb4({endpoint}), .dst_apb4(u_{name}_apb4_if));")
|
||||
else:
|
||||
bridges.append(f"assign s_apb_cdc_idle[{index}] = 1'b1;")
|
||||
bridges.append(f"assign s_apb_cdc_ready[{index}] = 1'b1;")
|
||||
decode.append(f"if ({' || '.join(f'`SOC_ADDR_IS_{r}(addr_i)' for r in target['regions'])}) "
|
||||
f"return 5'd{index + 5};")
|
||||
atomic_write(output / "rtl/tiny_apb_interfaces.svh", "\n".join(declarations + reset_assignments) + "\n")
|
||||
|
||||
@@ -43,6 +43,7 @@ TEST_SOURCES = (
|
||||
"crt/src/hal/fabric_monitor.c",
|
||||
"crt/src/hal/sysctrl.c",
|
||||
"crt/src/hal/clock.c",
|
||||
"crt/src/hal/rcu.c",
|
||||
"crt/src/hal/resource.c",
|
||||
"crt/src/hal/extension.c",
|
||||
"crt/src/hal/user_ip.c",
|
||||
@@ -122,6 +123,7 @@ def main() -> int:
|
||||
"-DRS_NPU_TEST_MMIO",
|
||||
"-DRS_FABRIC_MONITOR_TEST_MMIO",
|
||||
"-DRS_RESOURCE_TEST_MMIO",
|
||||
"-DRS_TINY_RCU_TEST_MMIO",
|
||||
"-I",
|
||||
str(memory_map_root / "include"),
|
||||
"-I",
|
||||
|
||||
@@ -114,10 +114,12 @@ def verify_locked_views(root: Path) -> dict:
|
||||
return verified
|
||||
|
||||
|
||||
def inspect_netlist(document: dict) -> dict:
|
||||
def expand_netlist(document: dict) -> tuple:
|
||||
"""Resolve hierarchy aliases without assigning a product clock contract."""
|
||||
modules = document["modules"]
|
||||
top = "retrosoc_tiny_asic"
|
||||
leaves = []
|
||||
boundaries = {}
|
||||
aliases = {}
|
||||
|
||||
def canonical(bit):
|
||||
@@ -159,6 +161,7 @@ def inspect_netlist(document: dict) -> dict:
|
||||
path = f"{scope}/{name}"
|
||||
attrs = modules.get(kind, {}).get("attributes", {})
|
||||
if kind in modules and not int(str(attrs.get("blackbox", "0")), 2):
|
||||
boundaries[path] = {"path": path, "type": kind, "ports": ports}
|
||||
visit(kind, path, ports)
|
||||
else:
|
||||
directions = cell.get("port_directions", {
|
||||
@@ -168,9 +171,15 @@ def inspect_netlist(document: dict) -> dict:
|
||||
"directions": directions})
|
||||
|
||||
visit(top, top, {})
|
||||
for c in leaves:
|
||||
for c in [*leaves, *boundaries.values()]:
|
||||
for port, bits in c["ports"].items():
|
||||
c["ports"][port] = [canonical(bit) for bit in bits]
|
||||
return modules, top, leaves, canonical, boundaries
|
||||
|
||||
|
||||
def inspect_netlist(document: dict) -> dict:
|
||||
# Keep the original P1 parked-PLL/direct-SYS contract fail-closed.
|
||||
modules, top, leaves, canonical, _ = expand_netlist(document)
|
||||
clock = modules[top]["cells"]["u_clock_buffer"]["connections"]["clk_o"]
|
||||
if len(clock) != 1 or not isinstance(clock[0], int):
|
||||
raise ValueError("missing live SYS clock")
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
#!/usr/bin/env python3
|
||||
"""P7 structural clock/macro evidence; separate from the frozen P1 audit."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
from scripts import tiny_r2_baseline as b # noqa: E402
|
||||
from scripts.config_key import variant_id # noqa: E402
|
||||
from scripts.tiny_ics55_platform import expand_netlist, inspect_clock_source # noqa: E402
|
||||
|
||||
PHASE = "TINY-R2-P7"
|
||||
RAM_TYPES = {"ICS55": ("ics55_ecos_sram_1024x32_m8", "CLK"),
|
||||
"IHP130": ("RM_IHPSG13_1P_1024x32_c2_bm_bist", "A_CLK")}
|
||||
|
||||
|
||||
def inspect_netlist(document: dict, pdk: str) -> dict:
|
||||
modules, top, leaves, canonical, boundaries = expand_netlist(document)
|
||||
|
||||
def boundary(suffix: str, kind: str) -> dict:
|
||||
found = [cell for name, cell in boundaries.items()
|
||||
if name.replace("/", ".").endswith("." + suffix)
|
||||
and cell["type"].split("$")[0] == kind]
|
||||
if len(found) != 1:
|
||||
raise ValueError(f"missing or ambiguous P7 clock boundary: {suffix}")
|
||||
return found[0]
|
||||
|
||||
def pin(cell: dict, name: str) -> str:
|
||||
bits = cell["ports"].get(name, [])
|
||||
if len(bits) != 1 or bits[0] in ("0", "1", "x", "z"):
|
||||
raise ValueError(f"missing/constant P7 clock: {cell['path']}/{name}")
|
||||
return bits[0]
|
||||
|
||||
root = boundary("u_soc.u_clock_backend.u_sys_mux", "safe_clock_mux")
|
||||
memory = boundary("u_soc.u_clock_backend.u_mem_mux", "safe_clock_mux")
|
||||
peripheral = boundary("u_soc.u_clock_backend.u_pclk_mux", "safe_clock_mux")
|
||||
cpu_gate = boundary("u_soc.u_cpu.u_cpu_gate", "tiny_clock_gate")
|
||||
crypto_gate = boundary("u_soc.u_crypto_lifecycle", "tiny_peripheral_leaf")
|
||||
sys_clock = pin(root, "clk_o")
|
||||
pclk = pin(peripheral, "clk_o")
|
||||
if pin(cpu_gate, "clk_i") != sys_clock or pin(memory, "clk0_i") != sys_clock:
|
||||
raise ValueError("CPU gate/MEM source is not common SYS")
|
||||
if pin(crypto_gate, "clk_i") != pclk:
|
||||
raise ValueError("private Crypto storage gate is not sourced from PCLK")
|
||||
cpu_clock = pin(cpu_gate, "clk_o")
|
||||
crypto_clock = pin(crypto_gate, "clk_o")
|
||||
ram_type, ram_pin = RAM_TYPES[pdk]
|
||||
macros = [cell for cell in leaves if cell["type"] == ram_type]
|
||||
main_names = {f"u_soc.u_sram.gen_group[{g}].u_group.gen_bank[{n}].u_ram.u_mem"
|
||||
for g in range(4) for n in range(8)}
|
||||
crypto_names = {f"u_soc.u_crypto.u_storage.gen_bank[{n}].gen_macro.u_sram.u_mem"
|
||||
for n in range(6)}
|
||||
observed = set()
|
||||
for cell in macros:
|
||||
name = next((name for name in main_names | crypto_names
|
||||
if cell["path"].replace("/", ".").endswith("." + name)), None)
|
||||
if name is None or name in observed:
|
||||
raise ValueError("extra, duplicated or renamed physical SRAM macro")
|
||||
observed.add(name)
|
||||
expected_clock = sys_clock if name in main_names else crypto_clock
|
||||
if pin(cell, ram_pin) != expected_clock:
|
||||
raise ValueError(f"physical SRAM clock mismatch: {name}")
|
||||
if len(macros) != 38 or observed != main_names | crypto_names:
|
||||
raise ValueError("P7 requires 32 main SRAM plus six private Crypto macros")
|
||||
cpu = [cell for cell in leaves if "u_hazard3_cpu_2port" in cell["path"]
|
||||
and ("CK" in cell["ports"] or "CKN" in cell["ports"] or "CLK" in cell["ports"])]
|
||||
if not cpu:
|
||||
raise ValueError("missing CPU clock endpoints")
|
||||
for cell in cpu:
|
||||
for name in ("CK", "CKN", "CLK"):
|
||||
if name in cell["ports"] and pin(cell, name) not in (sys_clock, cpu_clock):
|
||||
raise ValueError("CPU has an unapproved clock or a hidden divider")
|
||||
pll = [cell for cell in leaves if cell["type"] == "PLL_TOP"]
|
||||
if pdk == "ICS55":
|
||||
if len(pll) != 1 or pin(pll[0], "CKOUT1") != pin(root, "clk1_i"):
|
||||
raise ValueError("P7 requires one PLL CKOUT1 connected to the SYS mux")
|
||||
if pin(pll[0], "REFCLK") != pin(root, "clk0_i"):
|
||||
raise ValueError("PLL reference and safe SYS source differ")
|
||||
constants = {"0": 0, "1": 1}
|
||||
for cell in leaves:
|
||||
if cell["type"] in ("TIELOH7R", "TIEHIH7R"):
|
||||
for bit in cell["ports"]["Z"]:
|
||||
constants[bit] = int(cell["type"] == "TIEHIH7R")
|
||||
if pll[0]["ports"].get("EN", ["x"])[0] in constants:
|
||||
raise ValueError("P7 PLL enable must be controlled, not tied on/off")
|
||||
external = modules[top]["ports"]["extclk_i_pad"]["bits"]
|
||||
if len(external) != 1:
|
||||
raise ValueError("missing external reference input")
|
||||
inspect_clock_source(leaves, pin(root, "clk0_i"),
|
||||
canonical(f"{top}:{external[0]}"), constants)
|
||||
elif pll:
|
||||
raise ValueError("no-PLL compatibility instantiated a PLL")
|
||||
return {"system_clock": sys_clock, "memory_clock": pin(memory, "clk_o"),
|
||||
"peripheral_clock": pclk, "cpu_clock": cpu_clock,
|
||||
"main_macros": 32, "private_crypto_macros": 6, "pll_macros": len(pll),
|
||||
"cpu_clocked_cells": len(cpu), "macro_bindings": macros,
|
||||
"clock_boundaries": [root, memory, peripheral, cpu_gate, crypto_gate],
|
||||
"qualification": "structural domain binding only; no physical operating-point qualification"}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("action", choices=("audit-netlist", "verify-netlist", "capture-source", "verify-source"))
|
||||
parser.add_argument("--variant-root", type=Path, required=True)
|
||||
parser.add_argument("--netlist", type=Path)
|
||||
parser.add_argument("--config", type=Path)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
if args.action in ("capture-source", "verify-source"):
|
||||
inputs = b.source_inputs(ROOT, "ICS55") | b.source_inputs(ROOT, "IHP130")
|
||||
for folder in ("tests", "configs/ci"):
|
||||
for path in (ROOT / folder).rglob("*"):
|
||||
if path.is_file() and path.suffix in (".py", ".sv", ".svh", ".c", ".S", ".ld", ".mk"):
|
||||
inputs[str(path.relative_to(ROOT))] = b.sha256(path)
|
||||
contract = b.artifact(ROOT / "docs/ip/tiny-soc.md")
|
||||
if args.action == "capture-source":
|
||||
if args.output.exists():
|
||||
raise ValueError("source evidence already exists; use a new attempt instead of overwriting")
|
||||
b.dump(args.output, {"phase": PHASE, "repository": b.git_info(ROOT),
|
||||
"specification": contract, "source_inputs": inputs,
|
||||
"source_digest": b.digest_map(inputs), "tools": b.tools_identity(ROOT),
|
||||
"snapshot": b.snapshot(ROOT, args.output.parent / "source", inputs)})
|
||||
else:
|
||||
recorded = b.read_json(args.output)
|
||||
if (recorded.get("phase") != PHASE or recorded.get("source_inputs") != inputs or
|
||||
recorded.get("specification") != contract):
|
||||
raise ValueError("P7 sources changed after the retained source snapshot")
|
||||
print(f"{PHASE} {args.action}: PASS")
|
||||
return 0
|
||||
if args.netlist is None or args.config is None:
|
||||
raise ValueError("netlist actions require --netlist and --config")
|
||||
manifest = b.configuration(args.variant_root)
|
||||
pdk = manifest["configuration"]["PDK"]
|
||||
config = dict(line.split("=", 1) for line in args.config.read_text().splitlines() if "=" in line)
|
||||
for key, value in {"SOC": "TINY", "PDK": pdk,
|
||||
"HAVE_PLL": "YES" if pdk == "ICS55" else "NO",
|
||||
"HAVE_SRAM_MACRO": "YES", "SRAM_SIZE_KIB": "128",
|
||||
"TOP_DESIGN": "retrosoc_tiny_asic", "PERIOD_PS": "41667"}.items():
|
||||
if config.get(key) != value:
|
||||
raise ValueError(f"P7 consumed configuration mismatch: {key}")
|
||||
values = [f"{k}={v}" for k, v in manifest["configuration"].items()
|
||||
if k not in ("SIMU", "SYNTH", "SYNTH_RECIPE", "STA")]
|
||||
expected = variant_id(manifest["profile"], values,
|
||||
ROOT / "dependencies/dependencies.lock.json",
|
||||
"2000-01-01-00-00").rsplit("-", 1)[1]
|
||||
if config.get("CONFIG_DIGEST") != expected or args.variant_root.name.rsplit("-", 1)[1] != expected:
|
||||
raise ValueError("P7 consumed configuration digest mismatch")
|
||||
paths = (args.netlist, Path(str(args.netlist) + ".json"), args.config)
|
||||
if args.action == "audit-netlist" and not paths[1].is_file():
|
||||
# A mapped Verilog export is also a valid audit input. Serialize it
|
||||
# without optimization or remapping; retain the conversion result.
|
||||
# This supports the pre-P7 IHP130 recipe which did not emit JSON.
|
||||
flow = b.read_json(args.variant_root / "syn/yosys/result-synth.json")
|
||||
if flow.get("status") != "passed" or flow.get("exit_code") != 0:
|
||||
raise ValueError("cannot derive audit JSON from an unsuccessful synthesis")
|
||||
b.execute(ROOT, args.output.parent, "p7-netlist-json",
|
||||
["timeout", "--foreground", "120s", "yosys", "-Q", "-T", "-p",
|
||||
f'read_verilog "{paths[0]}"; write_json "{paths[1]}"'], quiet=True)
|
||||
inputs = [b.artifact(path) for path in paths]
|
||||
auditors = [b.artifact(ROOT / "scripts" / name) for name in
|
||||
("tiny_r2_p7.py", "tiny_ics55_platform.py", "tiny_r2_baseline.py", "config_key.py")]
|
||||
if args.action == "audit-netlist":
|
||||
result = {"phase": PHASE, "status": "passed", "pdk": pdk,
|
||||
"audit_scripts": auditors, "inputs": inputs,
|
||||
"configuration": manifest["configuration"], "config_digest": expected,
|
||||
"binding": inspect_netlist(json.loads(paths[1].read_text()), pdk)}
|
||||
b.dump(args.output, result)
|
||||
else:
|
||||
result = b.read_json(args.output)
|
||||
if (result.get("status") != "passed" or result.get("phase") != PHASE or
|
||||
result.get("audit_scripts") != auditors or result.get("config_digest") != expected or
|
||||
result.get("inputs") != inputs):
|
||||
raise ValueError("P7 structural evidence is missing, stale, or not passed")
|
||||
except (OSError, ValueError, KeyError) as error:
|
||||
print(f"{PHASE}: {error}", file=sys.stderr)
|
||||
return 1
|
||||
print(f"{PHASE} {args.action}: PASS (structural evidence only)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in new issue
Block a user