Nettle (NETlist Traversal and Topology Layout Explorer) turns elaborated
Verilog and SystemVerilog into a portable .nettle bundle and opens it as an
interactive schematic in a web browser. Use it to understand synthesized RTL
connectivity, navigate or selectively flatten hierarchy, cross-probe source and
schematic objects, and share a reviewable snapshot without sharing a compiler
environment.
Nettle deliberately separates compilation from viewing. The native CLI runs standalone Slang and Yosys with yosys-slang to normalize a design into a versioned ZIP + Protobuf bundle. The React viewer validates and renders that bundle entirely in browser memory. It does not upload the design, call a project API, or need HDL compiler binaries, making Nettle useful for local investigation, design review, and static-site deployment.
The demo builds br_counter.nettle from the manifest-pinned upstream
br_counter.sv design and starts the viewer at http://127.0.0.1:8090. Choose
one of these paths:
| Path | What you need | What runs |
|---|---|---|
| Prebuilt container images | Git, Docker or Podman, and a browser | Published builder and viewer images from GHCR |
| Locally built container images | Git, Docker or Podman, a browser, and this checkout | Builder and viewer images built from your checkout |
| No containers | Git, the native system dependencies, and a browser | The CLI and viewer built directly from your checkout |
Run all commands from the repository root.
First prepare the demo corpus. This clones Bedrock-RTL, sparse-checks out the
full Git SHA in integration_tests/bedrock-rtl/manifest.json, and keeps the
upstream sources under the ignored target/ directory:
scripts/check-design-corpus.py --prepare-only --corpus bedrock-rtl \
--workspace target/design-corporaThese commands pull the published images from GHCR automatically. The builder
image is Linux/amd64 because the pinned standalone Slang release does not
publish a Linux/arm64 archive. The viewer image is multi-platform. Stop the
background viewer afterward with docker stop nettle-viewer-demo or
podman stop nettle-viewer-demo.
Docker Desktop supplies amd64 emulation for the builder. A checkout below your macOS home directory is shared with Docker Desktop by default.
docker run --rm --platform linux/amd64 \
-v "$PWD:/work" -w /work \
ghcr.io/xlsynth/nettle-builder:latest build \
--filelist target/design-corpora/bedrock-rtl/br_counter.f \
--project-root target/design-corpora/bedrock-rtl \
--top br_counter \
--output br_counter.nettle && \
docker run --rm -d \
--name nettle-viewer-demo \
-p 127.0.0.1:8090:8080 \
ghcr.io/xlsynth/nettle-viewer:latest && \
open http://127.0.0.1:8090Use this only when docker --version reports Docker Engine. If it begins with
podman version, use the Podman command below.
docker run --rm --platform linux/amd64 \
--user "$(id -u):$(id -g)" \
-v "$PWD:/work" -w /work \
ghcr.io/xlsynth/nettle-builder:latest build \
--filelist target/design-corpora/bedrock-rtl/br_counter.f \
--project-root target/design-corpora/bedrock-rtl \
--top br_counter \
--output br_counter.nettle && \
docker run --rm -d \
--name nettle-viewer-demo \
-p 127.0.0.1:8090:8080 \
ghcr.io/xlsynth/nettle-viewer:latest && \
xdg-open http://127.0.0.1:8090The fully qualified GHCR image names avoid Podman's short-name registry prompt.
The user namespace mapping lets the container's UID/GID 10001 account write a
bundle owned by the calling host user; :Z supplies the expected SELinux
relabeling.
podman run --rm --platform linux/amd64 \
--userns=keep-id:uid=10001,gid=10001 \
-v "$PWD:/work:Z" -w /work \
ghcr.io/xlsynth/nettle-builder:latest build \
--filelist target/design-corpora/bedrock-rtl/br_counter.f \
--project-root target/design-corpora/bedrock-rtl \
--top br_counter \
--output br_counter.nettle && \
podman run --rm -d \
--name nettle-viewer-demo \
-p 127.0.0.1:8090:8080 \
ghcr.io/xlsynth/nettle-viewer:latest && \
xdg-open http://127.0.0.1:8090Build both images from the checkout, then run the matching command from Option 1 with the local image names in place of the GHCR image names. This exercises local source changes while keeping the compiler and viewer dependencies inside containers.
For Docker Desktop on macOS/arm64:
docker build --platform linux/amd64 \
-f Dockerfile.builder -t nettle-builder:latest .
docker build --platform linux/arm64 \
-f Dockerfile.viewer -t nettle-viewer:latest .For Docker Engine on Linux/amd64:
docker build --platform linux/amd64 \
-f Dockerfile.builder -t nettle-builder:latest .
docker build --platform linux/amd64 \
-f Dockerfile.viewer -t nettle-viewer:latest .For either Docker variant, replace
ghcr.io/xlsynth/nettle-builder:latest with nettle-builder:latest and
ghcr.io/xlsynth/nettle-viewer:latest with nettle-viewer:latest.
For Podman on Linux/amd64:
podman build --platform linux/amd64 \
-f Dockerfile.builder -t localhost/nettle-builder:latest .
podman build --platform linux/amd64 \
-f Dockerfile.viewer -t localhost/nettle-viewer:latest .For Podman, replace the GHCR image names with
localhost/nettle-builder:latest and localhost/nettle-viewer:latest,
respectively. The explicit localhost/ tags avoid Podman's short-name
registry prompt.
This path builds and runs Nettle directly on the host. First install the native system dependencies, then verify the HDL compiler toolchain and build Nettle and its static viewer:
scripts/check-toolchain.sh
cargo build --locked
npm ci
npm run buildBuild the demo bundle and start the viewer:
cargo run --locked -- build \
--filelist target/design-corpora/bedrock-rtl/br_counter.f \
--project-root target/design-corpora/bedrock-rtl \
--top br_counter \
--output br_counter.nettle
cargo run --locked -- view --web-root web/dist --port 8090Open http://127.0.0.1:8090 and choose br_counter.nettle in the file picker.
Press Ctrl-C in the terminal to stop the viewer.
See Option 2 for image
build and run commands matching each supported host. To run the complete test
suite in its known-good toolchain container, build the builder Dockerfile's
test target:
docker build --platform linux/amd64 \
-f Dockerfile.builder --target test -t nettle-test .Native development works on any host where the required compiler tools are available. On Linux/amd64, the versions below match the known-good container. On macOS/arm64 or Linux/arm64, install or build native host versions of Slang and Yosys with yosys-slang; the pinned standalone Slang Linux release is amd64-only. Install:
- Rust 1.88 or newer. This checkout selects and validates Rust 1.95.0 through
rust-toolchain.toml. - standalone Slang 11 or newer. Slang 11.0 is the known-good release. Nettle checks for the command-file, top selection, JSON diagnostics/AST, define, undefine, and parameter capabilities it uses.
- Yosys with a compatible yosys-slang plugin. Nettle does not claim a numeric
Yosys/plugin version range because the two components must be a compatible
pair; it probes
read_slangand every required plugin capability before compiling. The OSS CAD Suite snapshot dated 2026-06-15 is known good. - Node.js 20.19 or later in the 20.x line, 22.12 or later in the 22.x line, or 24 or newer, plus npm to build the web viewer. Node.js 24 with npm 11 is the known-good configuration.
- A modern web browser to use the viewer. Chromium is additionally required to run the browser end-to-end tests.
The Dockerfile.builder and
Dockerfile.viewer pins are the source of truth for
known-good toolchains and build environments. They record the exact Slang
release/source commit and checksums, the checksummed OSS CAD Suite artifact,
and the Rust and Node image versions. Nettle never downloads HDL compilers at
runtime. Run scripts/check-toolchain.sh after installing native compiler
tools to exercise their capabilities end to end.
Install the locked JavaScript dependencies and build the static viewer:
npm ci
npm run buildCargo uses the committed Cargo.lock; use --locked for reproducible native
builds. A typical local development cycle is:
scripts/check-toolchain.sh
cargo build --locked
cargo test --locked
npm test
npm run devThe development viewer is then available at http://127.0.0.1:5173. Use
cargo build --locked --release for an optimized native binary.
Run commands from the repository root unless noted otherwise.
Build the included counter:
cargo run --locked -- build \
--filelist target/design-corpora/bedrock-rtl/br_counter.f \
--project-root target/design-corpora/bedrock-rtl \
--top br_counter \
--output br_counter.nettleThe project root defaults to the filelist's parent. Set a wider root when a
filelist reaches sources through ..; every embedded source must canonicalize
inside that boundary. Nettle embeds only referenced UTF-8 sources up to 4 MiB
and records project-relative paths.
Start the built static viewer and select the bundle in its file picker:
cargo run --locked -- view --web-root web/dist --port 8090Then open http://127.0.0.1:8090. The browser reads the selected file; the
static host never receives it. For frontend development, npm run dev serves
the application at http://127.0.0.1:5173.
Pass a bundle to open it directly:
cargo run --locked -- view br_counter.nettle --web-root web/dist --port 8090This exposes that validated bundle at a fixed, non-cacheable route. Keep the default loopback bind for sensitive designs. A non-loopback bind makes the bundle downloadable by reachable clients and therefore requires the deployment's normal authentication and TLS protections.
render combines build and view while leaving the bundle on disk:
cargo run --locked -- render \
--filelist target/design-corpora/bedrock-rtl/br_counter.f \
--project-root target/design-corpora/bedrock-rtl \
--top br_counter \
--output br_counter.nettle \
--web-root web/dist \
--port 8090Builder overrides are repeatable, raw single-line SystemVerilog expressions:
cargo run --locked -- build \
--filelist project.f \
--project-root "$PWD" \
--top top \
--param WIDTH=16 \
--define SYNTHESIS \
--define NUM_HARTS=4 \
--undefine SIMULATION \
--output design.nettleDuplicate names, empty values, invalid identifiers, and define/undefine
conflicts are rejected before compilation. Add --debug-artifacts only when
raw Yosys JSON, Slang AST JSON, and compiler transcripts are needed; those
artifacts can reveal substantially more project detail.
Larger builds can use YAML:
# build.yaml
filelist: filelists/project.f
project_root: .
top: top
output: design.nettle
parameters:
DEPTH: "32"
WIDTH: "16"
defines:
NUM_HARTS: "4"
SYNTHESIS: null
undefines:
- SIMULATION
debug_artifacts: falsecargo run --locked -- build --config build.yamlYAML paths are relative to the configuration file; command-line paths are
relative to the current directory. YAML may also set slang_bin and
yosys_bin. Scalar CLI values override YAML values, while parameter, define,
and undefine collections are combined. Quote expressions and macro values so
YAML preserves them as strings. Unknown fields are rejected. See
INPUT_CONFIG_FILE_FORMAT.md for the complete schema and
merge behavior.
cargo run --locked -- inspect design.nettle
cargo run --locked -- validate design.nettleinspect prints safe summary metadata. validate checks archive membership,
resource limits, hashes, Protobuf payloads, source indexes, and module
index/body consistency without extracting files. See
NETTLE_FILE_FORMAT.md for the normative schema and
compatibility rules.
The viewer image contains only the static web build and a small file server; it contains no Slang, Yosys, HDL sources, example projects, or default bundle:
docker run --rm -p 127.0.0.1:8090:8080 \
ghcr.io/xlsynth/nettle-viewer:latestIn a shared deployment, place the static site behind the normal authentication and HTTPS boundary. Design bytes selected with the file picker remain in each user's browser.
scripts/check-rust-docs.py
cargo fmt --all --check
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --locked
cargo test --locked
cargo clippy --all-targets --all-features --locked
npm run lint
npm test
npm run build
npm run test:e2e
scripts/check-toolchain.sh
npm run test:designsThe real-toolchain design regressions are opt-in because they require the external HDL compilers. The Development section shows how to run the complete suite in the known-good container environment.
ARCHITECTURE.mddescribes the software architecture, trust boundaries, data flow, and tool design.COMPARISON.mdcompares Nettle's features with other schematic viewers.NETTLE_FILE_FORMAT.mddefines the.nettleinterchange format and its security rules.INPUT_CONFIG_FILE_FORMAT.mddefines the YAML build configuration schema and command-line merge behavior.RESOURCE_LIMITS_FILE_FORMAT.mddocuments the build-time resource-limit policy shared by the native and browser code.
Nettle is licensed under the Apache License 2.0.
Integration tests indirectly use third-party repositories that are referenced by manifests. Third-party sources are not committed into this repository. Nevertheless, manifests identify third-party provenance and applicable license and notice files.

