From 5ba1f11d1da27ae1ff84f5d2cb8db4b218e30f56 Mon Sep 17 00:00:00 2001 From: stefan Date: Mon, 22 Jun 2026 08:27:41 +0900 Subject: [PATCH] cleaner docs, sim perf --- README.md | 15 +++++++++++---- main.py | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 76f52c7..390a6f1 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ PPSFP (Parallel-Pattern Single-Fault Propagation) style fault simulators and other utility functions for use in various projects. These fault simulators are meant to be correct (ensured by unit-tests) and reasonably fast. +Fault simulation has $O(n^3)$ complexity. Every *fault* is simulated with every *pattern* involving every combinational *gate* in the circuit. We measure performance in terms of `gates * faults * patterns / second`. How fast can we make it? + ## Quick Start This project has submodules. To ensure everything is up-to-date, run @@ -15,9 +17,15 @@ This project manages reproducible programming environments with: - [uv](https://docs.astral.sh/uv/) for managing python environments. - [nix](https://nixos.org) for managing non-python tools and benchmark designs. Follow [this guide](https://librelane.readthedocs.io/en/stable/installation/nix_installation/index.html) or [this guide](https://github.com/fossi-foundation/nix-eda/blob/main/docs/installation.md) to setup [nix-eda](https://github.com/fossi-foundation/nix-eda/tree/main) binary cache to avoid re-building EDA-related tools. -Smoke-test: `uv run pytest` +Run all unit-tests: `uv run pytest` + +Run a naive, baseline fault simulation with 1024 random patterns: `uv run main.py tests/c6288.bench` -For running Jupyter Notebooks in the reproducible programming environment, install a kernelspec with this command: +Same on an 18k-gate circuit: `uv run main.py polito-itc99-b15-sky130` + +`nix develop` makes [quaigh](https://github.com/coloquinte/quaigh) available, another simulator/atpg written in rust. + +To run Jupyter Notebooks in the reproducible programming environment, install a kernelspec: ``` uv run ipython kernel install --user --env VIRTUAL_ENV $(pwd)/.venv --env PATH ${PATH} --name=uv-env ``` @@ -27,8 +35,7 @@ uv run jupyter lab ``` Choose `uv-env` as kernel in Jupyter Lab. -`uv run main.py ` runs a naive, baseline fault simulation. -`nix develop` makes [quaigh](https://github.com/coloquinte/quaigh) available, another simulator/atpg written in rust. + diff --git a/main.py b/main.py index fab81a5..f9568c3 100755 --- a/main.py +++ b/main.py @@ -2,6 +2,7 @@ import argparse import subprocess from pathlib import Path +import time import numpy as np @@ -13,11 +14,11 @@ from fsim.static import LineRoles, FaultSet def main(): - parser = argparse.ArgumentParser(description='Fault Stats') - parser.add_argument('-t', '--tlib', default='SKY130', help=f'techlib of circuit. default: SKY130, available: {sorted(techlib_by_name.keys())}.') - parser.add_argument('-p', '--patterns', default=1024, help='Number of random patterns to simulate.') - parser.add_argument('--seed', type=int, default=42, help='Random seed for reproducibility (default: 42).') - parser.add_argument('circuit', help='gate-level verilog file or nix package to import. See "nix flake show github:s-holst/benchmark-circuits" for available packages.') + parser = argparse.ArgumentParser(description='A basic stuck-at fault simulator.') + parser.add_argument('-t', '--tlib', default='SKY130', help=f'Techlib for verilog circuit. Default: SKY130, available: {sorted(techlib_by_name.keys())}.') + parser.add_argument('-p', '--patterns', default=1024, help='Number of random patterns to simulate. Default: 1024.') + parser.add_argument('--seed', type=int, default=42, help='Random seed for reproducibility. Default: 42.') + parser.add_argument('circuit', help='Gate-level verilog, bench, or nix package to import. See available packages: "nix flake show github:s-holst/benchmark-circuits".') args = parser.parse_args() args.patterns = int(args.patterns) args.tlib = techlib_by_name[args.tlib] @@ -75,6 +76,8 @@ def main(): undetected = set() detected = set() + start_time = time.perf_counter() + with log.progress() as p: for fidx, fault in enumerate(injection_faults): fault_site = fault//2 @@ -91,7 +94,11 @@ def main(): else: detected.add(fault) - log.info(f'detected by simulation: {len(detected)}/{len(injection_faults)} - {len(detected)/len(injection_faults)*100:.1f}%') + sim_time = time.perf_counter() - start_time + log.info(f'fsim time: {sim_time:.2f}s') + sim_performance = stats['comb'] * len(injection_faults) * patterns.shape[1] / sim_time + log.info(f'fsim performance: {sim_performance:.2e} gfp/s') + log.info(f'detected by simulation: {len(detected)}/{len(injection_faults)} - {len(detected)/len(injection_faults)*100:.2f}%') if __name__ == "__main__": main() \ No newline at end of file