diff --git a/.github/workflows/packaging-consistency.yml b/.github/workflows/packaging-consistency.yml
new file mode 100644
index 000000000..b63800c00
--- /dev/null
+++ b/.github/workflows/packaging-consistency.yml
@@ -0,0 +1,21 @@
+name: Packaging support consistency
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: Set up Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: "3.11"
+
+ - name: Check Python, wheel, and documentation support declarations
+ run: python3 ci/check_python_support_sync.py
diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml
index b11edde75..10823d8ef 100644
--- a/.github/workflows/wheels.yml
+++ b/.github/workflows/wheels.yml
@@ -188,11 +188,9 @@ jobs:
eval "$(brew shellenv)"
brew install -q boost
- name: Install cibuildwheel (and delvewheel for Windows)
- run: python -m pip install cibuildwheel==4.1.0 delvewheel
+ run: python -m pip install cibuildwheel==4.2.1 delvewheel
- name: Build wheels
env:
- # Python 3.15 remains in beta and is not selected without this opt-in.
- CIBW_ENABLE: cpython-prerelease
CIBW_ENVIRONMENT: SKLEARN_SKIP_NETWORK_TESTS=1
CIBW_ENVIRONMENT_MACOS: >-
SKLEARN_SKIP_NETWORK_TESTS=1
@@ -202,6 +200,7 @@ jobs:
CIBW_BUILD: cp${{ matrix.python }}-${{ matrix.platform_id }}
CIBW_ARCHS: 'all'
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_image }}
+ CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux_image }}
CIBW_MANYLINUX_I686_IMAGE: ${{ matrix.manylinux_image }}
CIBW_BEFORE_ALL_WINDOWS: >
pwsh -NoProfile -NonInteractive -ExecutionPolicy Bypass
diff --git a/README.md b/README.md
index ed89c090f..3ad5607e9 100644
--- a/README.md
+++ b/README.md
@@ -14,9 +14,10 @@ https://colab.research.google.com/github/najaeda/naja/blob/main/tutorials/notebo
## What is Naja?
-Naja is an open source EDA framework for loading, elaborating, analyzing, optimizing, and transforming hardware designs from RTL SystemVerilog through structural netlists. It is usable from Python or C++.
+Naja is an open source EDA framework for loading, elaborating, analyzing, optimizing, and transforming hardware designs from SystemVerilog and VHDL RTL through structural netlists. It is usable from Python or C++.
- **SV/Verilog frontend** — parse Verilog and elaborate SystemVerilog RTL into a browsable design model
+- **VHDL frontend (Beta)** — parse and elaborate supported VHDL RTL into the same design model
- **Netlist analysis** — hierarchy, connectivity, equipotentials
- **Logic optimization** — dead logic elimination, constant propagation
- **ECO transformations** — direct netlist editing
@@ -28,7 +29,8 @@ The diagram below shows how formats, frontends, APIs, and companion tools
integrate around Naja's C++ netlist engine:
- **Design inputs** — SystemVerilog is parsed and elaborated through
- [`slang`](https://github.com/MikePopoloski/slang); gate-level Verilog can be
+ [`slang`](https://github.com/MikePopoloski/slang); VHDL is parsed and elaborated
+ through the **naja-vhdl** frontend (Beta); gate-level Verilog can be
loaded and emitted; and Liberty plus the
[Python primitive libraries](./src/najaeda/najaeda/primitives/) provide cell
and primitive models.
@@ -57,8 +59,10 @@ config:
---
flowchart LR
sv["`**SystemVerilog**`"] ==> slang(["`**slang**
SystemVerilog Frontend`"])
+ vhdl["`**VHDL**`"] ==> naja-vhdl["`**naja-vhdl**
VHDL Frontend (Beta)`"]
verilog["`**gate-level verilog**`"] ==> naja-verilog["`**naja-verilog**
gate verilog Parser`"]
naja-verilog ==> naja["`**naja C++ · netlist engine**
hierarchy · buses
bit-level nets & terms · connectivity
Primitive Functional Models`"]
+ naja-vhdl =="`**Elaboration**`"==> naja
najaif@{ label: "**naja-if**
Logical View Interchange Format
Cap'n Proto" } <==> naja
slang =="`**Elaboration**`"==> naja
naja <==> najaeda["`**najaeda**
Python API`"]
@@ -69,24 +73,27 @@ flowchart LR
pythonlibs["`**Python libraries**
naja representation`"] ==> naja
sv@{ shape: disk}
+ vhdl@{ shape: disk}
verilog@{ shape: disk}
naja@{ shape: rounded}
najaif@{ shape: disk}
lib@{ shape: disk}
pythonlibs@{ shape: disk}
- sv:::input
- slang:::frontend
- naja-verilog:::frontend
- verilog:::input
- naja:::core
- najaif:::input
- najaeda:::api
- kf:::tool
- kfm:::tool
- ns:::tool
- scope:::tool
- lib:::input
- pythonlibs:::input
+ sv:::input
+ vhdl:::input
+ slang:::frontend
+ naja-verilog:::frontend
+ naja-vhdl:::frontend
+ verilog:::input
+ naja:::core
+ najaif:::input
+ najaeda:::api
+ kf:::tool
+ kfm:::tool
+ ns:::tool
+ scope:::tool
+ lib:::input
+ pythonlibs:::input
classDef input fill:#eef2ff,stroke:#6366f1,color:#1e1b4b,stroke-width:2px
classDef frontend fill:#fff7ed,stroke:#f97316,color:#431407,stroke-width:2px
classDef core fill:#fef2f2,stroke:#ef4444,color:#450a0a,stroke-width:3px
@@ -112,11 +119,17 @@ The best entry point is the [`najaeda`](https://pypi.org/project/najaeda/) Pytho
pip install najaeda
```
+Requires Python 3.10 or later. Pre-built wheels are published for:
+
+- Linux x86_64 and AArch64 (`manylinux_2_28`; glibc 2.28 or later)
+- macOS Apple Silicon (arm64), macOS 11 or later
+- Windows x86_64
+
Full documentation: [najaeda.readthedocs.io](https://najaeda.readthedocs.io/en/latest/)
For AI-assisted design exploration, [`naja-scope`](https://github.com/najaeda/naja-scope)
is a najaeda-based MCP server that gives MCP-compatible assistants a precise,
-structured view of elaborated SystemVerilog designs. Instead of pasting large
+structured view of elaborated SystemVerilog or VHDL designs. Instead of pasting large
RTL files into chat, agents can ask targeted questions — what drives a signal,
what is inside a module, where a net comes from — and get small, exact answers
with file-and-line references.
diff --git a/ci/check_python_support_sync.py b/ci/check_python_support_sync.py
new file mode 100755
index 000000000..f4b89098d
--- /dev/null
+++ b/ci/check_python_support_sync.py
@@ -0,0 +1,331 @@
+#!/usr/bin/env python3
+# SPDX-FileCopyrightText: 2026 The Naja authors
+#
+# SPDX-License-Identifier: Apache-2.0
+
+"""Check that Python and wheel support declarations stay synchronized.
+
+``pyproject.toml`` is authoritative for supported Python versions. The wheel
+workflow is authoritative for binary platforms. Public documentation repeats
+both for users, so this check makes any drift an actionable CI failure.
+"""
+
+import argparse
+import re
+import sys
+import tomllib
+from collections import defaultdict
+from pathlib import Path
+
+
+PYPROJECT = Path("pyproject.toml")
+WHEEL_WORKFLOW = Path(".github/workflows/wheels.yml")
+DOCUMENTATION = (
+ Path("README.md"),
+ Path("src/najaeda/README.rst"),
+ Path("src/najaeda/najaeda/docs/source/introduction.rst"),
+)
+
+# Markers are intentionally user-facing terms rather than documentation
+# formatting, so the same check works for Markdown and reStructuredText.
+PLATFORM_MARKERS = {
+ "manylinux_x86_64": "Linux x86_64",
+ "manylinux_aarch64": "AArch64",
+ "macosx_arm64": "macOS Apple Silicon (arm64)",
+ "win_amd64": "Windows x86_64",
+}
+
+
+def version_key(version: str) -> tuple[int, ...]:
+ return tuple(int(part) for part in version.split("."))
+
+
+def display_version(version: str) -> str:
+ parts = version.split(".")
+ while len(parts) > 1 and parts[-1] == "0":
+ parts.pop()
+ return ".".join(parts)
+
+
+def version_from_matrix_code(code: str) -> tuple[str, bool] | None:
+ match = re.fullmatch(r"(\d)(\d+)(t?)", code)
+ if not match:
+ return None
+ major, minor, free_threaded = match.groups()
+ return f"{major}.{int(minor)}", bool(free_threaded)
+
+
+def parse_minimum_python(specifier: str) -> str:
+ match = re.search(r"(?:^|,)\s*>=\s*(\d+\.\d+)(?:\.\d+)?(?:\s*|,|$)", specifier)
+ if not match:
+ raise ValueError(
+ "[project].requires-python must contain a >= major.minor lower bound "
+ f"that this check can validate; found {specifier!r}"
+ )
+ return match.group(1)
+
+
+def load_project_support(root: Path) -> tuple[str, set[str]]:
+ with (root / PYPROJECT).open("rb") as stream:
+ project = tomllib.load(stream)["project"]
+
+ minimum = parse_minimum_python(project["requires-python"])
+ versions = set()
+ for classifier in project.get("classifiers", []):
+ match = re.fullmatch(r"Programming Language :: Python :: (\d+)\.(\d+)", classifier)
+ if match:
+ versions.add(f"{match.group(1)}.{int(match.group(2))}")
+ return minimum, versions
+
+
+def parse_matrix_entries(root: Path) -> list[dict[str, str]]:
+ lines = (root / WHEEL_WORKFLOW).read_text(encoding="utf-8").splitlines()
+ in_build_wheels = False
+ in_include = False
+ current: dict[str, str] | None = None
+ entries: list[dict[str, str]] = []
+
+ for line in lines:
+ if line == " build_wheels:":
+ in_build_wheels = True
+ continue
+ if not in_build_wheels:
+ continue
+ if line.startswith(" ") and not line.startswith(" ") and line != " build_wheels:":
+ break
+ if line == " include:":
+ in_include = True
+ continue
+ if not in_include:
+ continue
+ if line == " steps:":
+ break
+
+ entry_match = re.fullmatch(r"\s+- os:\s*(\S+)\s*", line)
+ if entry_match:
+ if current is not None:
+ entries.append(current)
+ current = {"os": entry_match.group(1).strip("'\"")}
+ continue
+
+ field_match = re.fullmatch(r"\s+(python|platform_id|manylinux_image):\s*(\S+)\s*", line)
+ if current is not None and field_match:
+ current[field_match.group(1)] = field_match.group(2).strip("'\"")
+
+ if current is not None:
+ entries.append(current)
+ return entries
+
+
+def documented_support(path: Path) -> tuple[str | None, set[str], str]:
+ text = path.read_text(encoding="utf-8")
+ match = re.search(r"Requires Python (\d+\.\d+) or later\.", text)
+ if not match:
+ return None, set(), ""
+
+ # The support list immediately follows the requirement. Limiting the scan
+ # avoids unrelated platform mentions elsewhere in a README.
+ support_block = text[match.start() : match.start() + 600]
+ platforms = {
+ platform
+ for platform, marker in PLATFORM_MARKERS.items()
+ if marker in support_block
+ }
+ return match.group(1), platforms, support_block
+
+
+def check(root: Path) -> list[str]:
+ failures: list[str] = []
+ minimum, classifier_versions = load_project_support(root)
+ entries = parse_matrix_entries(root)
+ workflow_text = (root / WHEEL_WORKFLOW).read_text(encoding="utf-8")
+
+ if not classifier_versions:
+ failures.append("pyproject.toml has no minor-version Python classifiers")
+ return failures
+
+ oldest_classifier = min(classifier_versions, key=version_key)
+ if minimum != oldest_classifier:
+ failures.append(
+ f"pyproject.toml requires-python starts at {minimum}, but the oldest "
+ f"Python classifier is {oldest_classifier}"
+ )
+
+ coverage: dict[str, set[str]] = defaultdict(set)
+ free_threaded_versions: set[str] = set()
+ linux_images: set[str] = set()
+ for entry in entries:
+ missing = {"python", "platform_id"} - entry.keys()
+ if missing:
+ failures.append(
+ "wheel matrix entry is missing "
+ f"{', '.join(sorted(missing))}: {entry}"
+ )
+ continue
+
+ parsed = version_from_matrix_code(entry["python"])
+ if parsed is None:
+ failures.append(f"unrecognized wheel matrix Python value: {entry['python']!r}")
+ continue
+ version, free_threaded = parsed
+ if free_threaded:
+ free_threaded_versions.add(version)
+ continue
+
+ platform = entry["platform_id"]
+ coverage[version].add(platform)
+ if platform.startswith("manylinux_"):
+ if "manylinux_image" in entry:
+ linux_images.add(entry["manylinux_image"])
+ else:
+ failures.append(
+ f"Python {version} {platform} has no manylinux_image in the matrix"
+ )
+
+ matrix_versions = set(coverage)
+ if matrix_versions != classifier_versions:
+ missing_from_matrix = classifier_versions - matrix_versions
+ missing_from_classifiers = matrix_versions - classifier_versions
+ if missing_from_matrix:
+ failures.append(
+ "Python classifiers without regular wheel jobs: "
+ + ", ".join(sorted(missing_from_matrix, key=version_key))
+ )
+ if missing_from_classifiers:
+ failures.append(
+ "regular wheel jobs without Python classifiers: "
+ + ", ".join(sorted(missing_from_classifiers, key=version_key))
+ )
+
+ unsupported_free_threaded = free_threaded_versions - classifier_versions
+ if unsupported_free_threaded:
+ failures.append(
+ "free-threaded wheel jobs without corresponding Python classifiers: "
+ + ", ".join(sorted(unsupported_free_threaded, key=version_key))
+ )
+
+ matrix_platforms = set().union(*coverage.values()) if coverage else set()
+ unknown_platforms = matrix_platforms - PLATFORM_MARKERS.keys()
+ if unknown_platforms:
+ failures.append(
+ "wheel platforms need documentation marker mappings in "
+ "ci/check_python_support_sync.py: "
+ + ", ".join(sorted(unknown_platforms))
+ )
+
+ for version in sorted(classifier_versions & matrix_versions, key=version_key):
+ if coverage[version] != matrix_platforms:
+ missing = matrix_platforms - coverage[version]
+ extra = coverage[version] - matrix_platforms
+ details = []
+ if missing:
+ details.append("missing " + ", ".join(sorted(missing)))
+ if extra:
+ details.append("unexpected " + ", ".join(sorted(extra)))
+ failures.append(f"Python {version} wheel coverage differs: {'; '.join(details)}")
+
+ if len(linux_images) > 1:
+ failures.append(
+ "documentation assumes one manylinux baseline, but the matrix uses: "
+ + ", ".join(sorted(linux_images))
+ )
+
+ for platform in sorted(matrix_platforms):
+ if platform.startswith("manylinux_"):
+ architecture = platform.removeprefix("manylinux_").upper()
+ variable = f"CIBW_MANYLINUX_{architecture}_IMAGE"
+ if not re.search(rf"^\s+{re.escape(variable)}:", workflow_text, re.MULTILINE):
+ failures.append(
+ f"{WHEEL_WORKFLOW}: does not forward matrix.manylinux_image via {variable}"
+ )
+
+ macos_targets = set(
+ re.findall(r"\bMACOSX_DEPLOYMENT_TARGET=(\d+(?:\.\d+)*)", workflow_text)
+ )
+ if "macosx_arm64" in matrix_platforms and len(macos_targets) != 1:
+ failures.append(
+ f"{WHEEL_WORKFLOW}: expected one MACOSX_DEPLOYMENT_TARGET, found "
+ + (", ".join(sorted(macos_targets)) if macos_targets else "none")
+ )
+
+ for relative_path in DOCUMENTATION:
+ documented_minimum, documented_platforms, support_block = documented_support(
+ root / relative_path
+ )
+ if documented_minimum is None:
+ failures.append(
+ f"{relative_path}: missing 'Requires Python X.Y or later.' support block"
+ )
+ continue
+ if documented_minimum != minimum:
+ failures.append(
+ f"{relative_path}: documents Python {documented_minimum} or later, "
+ f"but pyproject.toml requires {minimum} or later"
+ )
+ if documented_platforms != matrix_platforms:
+ missing = matrix_platforms - documented_platforms
+ extra = documented_platforms - matrix_platforms
+ details = []
+ if missing:
+ details.append("missing " + ", ".join(sorted(missing)))
+ if extra:
+ details.append("unexpected " + ", ".join(sorted(extra)))
+ failures.append(
+ f"{relative_path}: platform documentation differs: {'; '.join(details)}"
+ )
+
+ if linux_images:
+ manylinux_image = next(iter(linux_images))
+ if manylinux_image not in support_block:
+ failures.append(f"{relative_path}: does not document {manylinux_image}")
+ baseline = re.fullmatch(r"manylinux_(\d+)_(\d+)", manylinux_image)
+ if baseline:
+ glibc_marker = f"glibc {baseline.group(1)}.{baseline.group(2)} or later"
+ if glibc_marker not in support_block:
+ failures.append(f"{relative_path}: does not document {glibc_marker}")
+
+ if len(macos_targets) == 1:
+ macos_target = display_version(next(iter(macos_targets)))
+ macos_marker = f"macOS {macos_target} or later"
+ if macos_marker not in support_block:
+ failures.append(f"{relative_path}: does not document {macos_marker}")
+
+ return failures
+
+
+def main() -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ "--root",
+ type=Path,
+ default=Path(__file__).resolve().parent.parent,
+ help="repository root (defaults to the parent of this script's directory)",
+ )
+ args = parser.parse_args()
+
+ try:
+ failures = check(args.root.resolve())
+ except (KeyError, OSError, tomllib.TOMLDecodeError, ValueError) as error:
+ print(f"Packaging support consistency check ERROR: {error}", file=sys.stderr)
+ return 2
+
+ if failures:
+ print("Packaging support consistency check FAILED:", file=sys.stderr)
+ for failure in failures:
+ print(f" - {failure}", file=sys.stderr)
+ return 1
+
+ minimum, versions = load_project_support(args.root.resolve())
+ entries = parse_matrix_entries(args.root.resolve())
+ platforms = sorted({entry["platform_id"] for entry in entries if "platform_id" in entry})
+ print(
+ "Python support is synchronized: "
+ f">={minimum}; classifiers {', '.join(sorted(versions, key=version_key))}"
+ )
+ print("Wheel platforms are synchronized: " + ", ".join(platforms))
+ print(f"Documentation is synchronized: {len(DOCUMENTATION)} files")
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/pyproject.toml b/pyproject.toml
index 3e63e23c3..65eae3279 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -52,6 +52,8 @@ regex = 'NAJA_VERSION\s*\{\s*"(?P[^"]+)"\s*\}'
[project.urls]
Homepage = "https://github.com/najaeda/naja"
+Repository = "https://github.com/najaeda/naja"
+Issues = "https://github.com/najaeda/naja/issues"
[tool.cibuildwheel]
build-verbosity = 1
diff --git a/src/core/NajaVersion.h.in b/src/core/NajaVersion.h.in
index 1b1eb5fb9..429a10ab8 100644
--- a/src/core/NajaVersion.h.in
+++ b/src/core/NajaVersion.h.in
@@ -26,7 +26,7 @@ inline std::string getVersionPart(const std::string& version, int index) {
} // namespace detail
-const std::string NAJA_VERSION { "0.7.24" };
+const std::string NAJA_VERSION { "0.7.25" };
const std::string NAJA_MAJOR { detail::getVersionPart(NAJA_VERSION, 0) };
const std::string NAJA_MINOR { detail::getVersionPart(NAJA_VERSION, 1) };
const std::string NAJA_PATCH { detail::getVersionPart(NAJA_VERSION, 2) };
diff --git a/src/najaeda/README.rst b/src/najaeda/README.rst
index 2b6bae4d3..bb103078d 100644
--- a/src/najaeda/README.rst
+++ b/src/najaeda/README.rst
@@ -58,7 +58,11 @@ Installation
pip install najaeda
-Requires Python 3.10+. Wheels are published for Linux, macOS, and Windows.
+Requires Python 3.10 or later. Pre-built wheels are published for:
+
+* Linux x86_64 and AArch64 (``manylinux_2_28``; glibc 2.28 or later)
+* macOS Apple Silicon (arm64), macOS 11 or later
+* Windows x86_64
Tutorials
---------
diff --git a/src/najaeda/najaeda/docs/.readthedocs.yaml b/src/najaeda/najaeda/docs/.readthedocs.yaml
index 51de279b2..b737eef2c 100644
--- a/src/najaeda/najaeda/docs/.readthedocs.yaml
+++ b/src/najaeda/najaeda/docs/.readthedocs.yaml
@@ -10,9 +10,27 @@ version: 2
# Set the OS, Python version and other tools you might need
build:
- os: ubuntu-22.04
+ os: ubuntu-24.04
+ apt_packages:
+ - cmake
+ - ninja-build
+ - libboost-dev
+ - libtbb-dev
+ - capnproto
+ - libcapnp-dev
+ - zlib1g-dev
+ - bison
+ - flex
tools:
python: "3.12"
+ jobs:
+ post_install:
+ # Build this revision, including the extension used by autodoc.
+ - python -m pip install . --config-settings=cmake.define.PREGENERATED_PARSER_SOURCES=OFF --config-settings=build.tool-args=-j2
+
+submodules:
+ include: all
+ recursive: true
# Build documentation in the "docs/" directory with Sphinx
sphinx:
diff --git a/src/najaeda/najaeda/docs/source/conf.py b/src/najaeda/najaeda/docs/source/conf.py
index 4d3dda40d..332b0fb3f 100644
--- a/src/najaeda/najaeda/docs/source/conf.py
+++ b/src/najaeda/najaeda/docs/source/conf.py
@@ -10,8 +10,9 @@
import re
import sys
-# Add the src directory to sys.path
-sys.path.insert(0, os.path.abspath('../../../'))
+# Prefer the installed package (including its compiled extension). Fall back
+# to the source package for local builds with the extension on PYTHONPATH.
+sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..')))
def read_naja_release():
@@ -43,15 +44,16 @@ def read_naja_release():
try:
from najaeda import naja as raw_naja
- sys.modules.setdefault("najaeda.naja", raw_naja)
- tags.add("raw_naja_available")
- autodoc_mock_imports = []
-except Exception:
- autodoc_mock_imports = ["naja", "najaeda.naja"]
+except Exception as error:
+ raise RuntimeError(
+ "The documentation requires the compiled najaeda extension. "
+ "Install this checkout with pip, or add the matching CMake Python "
+ "extension directory to PYTHONPATH before running Sphinx."
+ ) from error
+sys.modules.setdefault("najaeda.naja", raw_naja)
templates_path = ['_templates']
exclude_patterns = []
-suppress_warnings = ["autodoc.mocked_object"]
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
diff --git a/src/najaeda/najaeda/docs/source/introduction.rst b/src/najaeda/najaeda/docs/source/introduction.rst
index 2f53dbe00..71fa1b41f 100644
--- a/src/najaeda/najaeda/docs/source/introduction.rst
+++ b/src/najaeda/najaeda/docs/source/introduction.rst
@@ -53,7 +53,11 @@ Installation
pip install najaeda
-Requires Python 3.10 or later.
+Requires Python 3.10 or later. Pre-built wheels are published for:
+
+* Linux x86_64 and AArch64 (``manylinux_2_28``; glibc 2.28 or later)
+* macOS Apple Silicon (arm64), macOS 11 or later
+* Windows x86_64
Bug Reports
-----------
diff --git a/src/najaeda/najaeda/docs/source/raw_api.rst b/src/najaeda/najaeda/docs/source/raw_api.rst
index b2b988690..d1ebf5531 100644
--- a/src/najaeda/najaeda/docs/source/raw_api.rst
+++ b/src/najaeda/najaeda/docs/source/raw_api.rst
@@ -237,36 +237,38 @@ semantic source of truth.
* - Object
- Main public methods and values
+ * - :class:`najaeda.naja.SNLParameter`
+ - ``create_decimal``, ``create_binary``, ``create_boolean``, ``create_string`` (optional default value), ``hasDefaultValue``, ``getValue``, ``getName``, ``getDesign``, ``destroy``
* - :class:`najaeda.naja.NLUniverse`
- - ``create``, ``destroy``, ``get``, ``getDB``, ``getTopDB``, ``setTopDB``, ``getTopDesign``, ``setTopDesign``, ``getUserDBs``, ``getSNLDesign``, ``getObject``, ``applyDLE``, ``applyConstantPropagation``, ``getMaxFanout``, ``getMaxLogicLevel``
+ - :meth:`create `, :meth:`destroy `, :meth:`get `, :meth:`getDB `, :meth:`getTopDB `, :meth:`setTopDB `, :meth:`getTopDesign `, :meth:`setTopDesign `, :meth:`getUserDBs `, :meth:`getSNLDesign `, :meth:`getObject `, :meth:`applyDLE `, :meth:`applyConstantPropagation `, :meth:`getMaxFanout `, :meth:`getMaxLogicLevel `
* - :class:`najaeda.naja.NLDB`
- - ``create``, ``destroy``, ``getID``, ``getNLID``, ``isTopDB``, ``getLibraries``, ``getLibrary``, ``getGlobalLibraries``, ``getPrimitiveLibraries``, ``getTopDesign``, ``loadVerilog``, ``loadSystemVerilog``, ``loadVHDL``, ``loadLibertyPrimitives``, ``loadNajaIF``, ``dumpNajaIF``, ``dumpVerilog``
+ - :meth:`create `, :meth:`destroy `, :meth:`getID `, :meth:`getNLID `, :meth:`isTopDB `, :meth:`getLibraries `, :meth:`getLibrary `, :meth:`getGlobalLibraries `, :meth:`getPrimitiveLibraries `, :meth:`getTopDesign `, :meth:`loadVerilog `, :meth:`loadSystemVerilog `, :meth:`loadVHDL `, :meth:`loadLibertyPrimitives `, :meth:`loadNajaIF `, :meth:`dumpNajaIF `, :meth:`dumpVerilog `
* - :class:`najaeda.naja.NLLibrary`
- - ``create``, ``createPrimitives``, ``getDB``, ``getID``, ``getNLID``, ``getName``, ``setName``, ``isStandard``, ``isPrimitives``, ``getSNLDesign``, ``getSNLDesigns``, ``getLibrary``
+ - :meth:`create `, :meth:`createPrimitives `, :meth:`getDB `, :meth:`getID `, :meth:`getNLID `, :meth:`getName `, :meth:`setName `, :meth:`isStandard `, :meth:`isPrimitives `, :meth:`getSNLDesign `, :meth:`getSNLDesigns `, :meth:`getLibrary `, :meth:`getLibraries `
* - :class:`najaeda.naja.SNLDesign`
- - ``create``, ``createPrimitive``, ``clone``, ``destroy``, ``getName``, ``setName``, ``getDB``, ``getLibrary``, ``getID``, ``getNLID``, ``getRevisionCount``, ``getTerms``, ``getTerm``, ``getTermByID``, ``getScalarTerms``, ``getBusTerms``, ``getBundleTerms``, ``getNets``, ``getNet``, ``getScalarNets``, ``getBusNets``, ``getInstances``, ``getInstance``, ``getInstanceByID``, ``getInstanceByIDList``, ``getParameters``, ``getParameter``, ``addCombinatorialArcs``, ``addInputsToClockArcs``, ``addClockToOutputsArcs``, ``setTimingModelParameter``, ``getCombinatorialInputs``, ``getCombinatorialOutputs``, ``getClockRelatedInputs``, ``getClockRelatedOutputs``, ``getInputRelatedClocks``, ``getOutputRelatedClocks``, ``getClockTerms``, ``getAsyncResetTerms``, ``getAsyncSetTerms``, ``getSyncResetTerms``, ``getSyncSetTerms``, ``getDataInputTerms``, ``getOutputTerms``, ``setTruthTable``, ``setTruthTableFromParameter``, ``setTruthTables``, ``getTruthTable``, ``getTruthTableByOutputID``, ``isConst0``, ``isConst1``, ``isConst``, ``isBuf``, ``isInv``, ``isAnd``, ``isNand``, ``isOr``, ``isNor``, ``isXor``, ``isXnor``, ``isMux``, ``dumpVerilog``, ``dumpFullDotFile``, ``dumpContextDotFile``
+ - :meth:`create `, :meth:`createPrimitive `, :meth:`clone `, :meth:`destroy `, :meth:`getName `, :meth:`setName `, :meth:`getDB `, :meth:`getLibrary `, :meth:`getID `, :meth:`getNLID `, :meth:`getRevisionCount `, :meth:`getTerms `, :meth:`getTerm `, :meth:`getTermByID `, :meth:`getScalarTerms `, :meth:`getBusTerms `, :meth:`getBundleTerms `, :meth:`getNets `, :meth:`getNet `, :meth:`getScalarNets `, :meth:`getBusNets `, :meth:`getInstances `, :meth:`getInstance `, :meth:`getInstanceByID `, :meth:`getInstanceByIDList `, :meth:`getParameters `, :meth:`getParameter `, :meth:`addCombinatorialArcs `, :meth:`addInputsToClockArcs `, :meth:`addClockToOutputsArcs `, :meth:`setTimingModelParameter `, :meth:`getCombinatorialInputs `, :meth:`getCombinatorialOutputs `, :meth:`getClockRelatedInputs `, :meth:`getClockRelatedOutputs `, :meth:`getInputRelatedClocks `, :meth:`getOutputRelatedClocks `, :meth:`getClockTerms `, :meth:`getAsyncResetTerms `, :meth:`getAsyncSetTerms `, :meth:`getSyncResetTerms `, :meth:`getSyncSetTerms `, :meth:`getDataInputTerms `, :meth:`getOutputTerms `, :meth:`setTruthTable `, :meth:`setTruthTableFromParameter `, :meth:`setTruthTables `, :meth:`getTruthTable `, :meth:`getTruthTableByOutputID `, :meth:`isConst0 `, :meth:`isConst1 `, :meth:`isConst `, :meth:`isBuf `, :meth:`isInv `, :meth:`isAnd `, :meth:`isNand `, :meth:`isOr `, :meth:`isNor `, :meth:`isXor `, :meth:`isXnor `, :meth:`isMux `, :meth:`dumpVerilog `, :meth:`dumpFullDotFile `, :meth:`dumpContextDotFile `
* - :class:`najaeda.naja.SNLInstance`
- - ``create``, ``destroy``, ``getName``, ``setName``, ``getID``, ``getNLID``, ``getDesign``, ``getModel``, ``getInstTerm``, ``getInstTerms``, ``getInstParameter``, ``getInstParameters``, ``getCombinatorialInputs``, ``getCombinatorialOutputs``, ``getClockRelatedInputs``, ``getClockRelatedOutputs``, ``getInputRelatedClocks``, ``getOutputRelatedClocks``
+ - :meth:`create `, :meth:`destroy `, :meth:`getName `, :meth:`setName `, :meth:`getID `, :meth:`getNLID `, :meth:`getDesign `, :meth:`getModel `, :meth:`getInstTerm `, :meth:`getInstTerms `, :meth:`getInstParameter `, :meth:`getInstParameters `, :meth:`getCombinatorialInputs `, :meth:`getCombinatorialOutputs `, :meth:`getClockRelatedInputs `, :meth:`getClockRelatedOutputs `, :meth:`getInputRelatedClocks `, :meth:`getOutputRelatedClocks `
* - :class:`najaeda.naja.SNLTerm` and term subclasses
- - ``Direction``, ``getName``, ``setName``, ``getDirection``, ``getDesign``, ``getNet``, ``setNet``, ``getBits``, ``getWidth``, ``getNLID``, ``getSourceLoc``, ``hasSourceLoc``; bit terms also expose ``setRole``, ``getRole``, ``getResetActiveLevel``, ``is_clock``, ``is_async_reset``, ``is_async_set``, ``is_sync_reset``, ``is_sync_set``, ``is_reset``, ``is_enable``, ``is_data``, ``is_data_input``, ``is_data_output``
+ - :class:`Direction `, :meth:`getName `, :meth:`setName `, :meth:`getDirection `, :meth:`getDesign `, :meth:`getNet `, :meth:`setNet `, :meth:`getBits `, :meth:`getWidth `, :meth:`getNLID `, :meth:`getSourceLoc `, :meth:`hasSourceLoc `; bit terms also expose :meth:`setRole `, :meth:`getRole `, :meth:`getResetActiveLevel `, :meth:`is_clock `, :meth:`is_async_reset `, :meth:`is_async_set `, :meth:`is_sync_reset `, :meth:`is_sync_set `, :meth:`is_reset `, :meth:`is_enable `, :meth:`is_data `, :meth:`is_data_input `, :meth:`is_data_output `
* - :class:`najaeda.naja.SNLNet` and net subclasses
- - ``Type``, ``getName``, ``setName``, ``getDesign``, ``getBits``, ``getWidth``, ``getType``, ``setType``, ``getTypeAsString``, ``isConstant``, ``isConstant0``, ``isConstant1``, ``getComponents``, ``getInstTerms``, ``getBitTerms``
+ - :class:`Type `, :meth:`getName `, :meth:`setName `, :meth:`getDesign `, :meth:`getBits `, :meth:`getWidth `, :meth:`getType `, :meth:`setType `, :meth:`getTypeAsString `, :meth:`isConstant `, :meth:`isConstant0 `, :meth:`isConstant1 `, :meth:`getComponents `, :meth:`getInstTerms `, :meth:`getBitTerms `
* - :class:`najaeda.naja.SNLInstTerm`
- - ``getInstance``, ``getBitTerm``, ``getNet``, ``setNet``, ``getDirection``, ``getRole``, ``getResetActiveLevel``, ``is_clock``, ``is_async_reset``, ``is_async_set``, ``is_sync_reset``, ``is_sync_set``, ``is_reset``, ``is_enable``, ``is_data``, ``is_data_input``, ``is_data_output``
+ - :meth:`getInstance `, :meth:`getBitTerm `, :meth:`getNet `, :meth:`setNet `, :meth:`getDirection `, :meth:`getRole `, :meth:`getResetActiveLevel `, :meth:`is_clock `, :meth:`is_async_reset `, :meth:`is_async_set `, :meth:`is_sync_reset `, :meth:`is_sync_set `, :meth:`is_reset `, :meth:`is_enable `, :meth:`is_data `, :meth:`is_data_input `, :meth:`is_data_output `
* - :class:`najaeda.naja.SNLTermRole`
- - ``Clock``, ``DataInput``, ``DataOutput``, ``AsyncReset``, ``AsyncSet``, ``SyncReset``, ``SyncSet``, ``Enable``, ``ScanInput``, ``ScanEnable``, ``MemoryReadAddress``, ``MemoryReadData``, ``MemoryWriteAddress``, ``MemoryWriteData``, ``MemoryWriteEnable``, ``Other``
+ - :attr:`Clock `, :attr:`DataInput `, :attr:`DataOutput `, :attr:`AsyncReset `, :attr:`AsyncSet `, :attr:`SyncReset `, :attr:`SyncSet `, :attr:`Enable `, :attr:`ScanInput `, :attr:`ScanEnable `, :attr:`MemoryReadAddress `, :attr:`MemoryReadData `, :attr:`MemoryWriteAddress `, :attr:`MemoryWriteData `, :attr:`MemoryWriteEnable `, :attr:`Other `
* - :class:`najaeda.naja.SNLActiveLevel`
- - ``High``, ``Low``, ``NA``
+ - :attr:`High `, :attr:`Low `, :attr:`NA `
* - :class:`najaeda.naja.SNLPath`
- - ``empty``, ``size``, ``getInstances``, ``getInstanceIDs``, ``getHeadInstance``, ``getTailInstance``, ``getHeadPath``, ``getTailPath``, ``getDesign``, ``getModel``
+ - :meth:`empty `, :meth:`size `, :meth:`getInstances `, :meth:`getInstanceIDs `, :meth:`getHeadInstance `, :meth:`getTailInstance `, :meth:`getHeadPath `, :meth:`getTailPath `, :meth:`getDesign `, :meth:`getModel `
* - :class:`najaeda.naja.SNLOccurrence`
- - ``isInstanceOccurrence``, ``getPath``, ``getInstance``, ``getInstTerm``, ``getNetComponent``, ``getDesign``
+ - :meth:`isInstanceOccurrence `, :meth:`getPath `, :meth:`getInstance `, :meth:`getInstTerm `, :meth:`getNetComponent `, :meth:`getDesign `
* - :class:`najaeda.naja.SNLEquipotential`
- - ``Mode``, ``getTerms``, ``getInstTermOccurrences``, ``isConst0``, ``isConst1``, ``dumpDotFile``
+ - :class:`Mode `, :meth:`getTerms `, :meth:`getInstTermOccurrences `, :meth:`isConst0 `, :meth:`isConst1 `, :meth:`dumpDotFile `
* - :class:`najaeda.naja.NLID`
- - ``from_string``, ``toTuple``, ``getType``, ``getDBID``, ``getLibraryID``, ``getDesignID``, ``getDesignObjectID``, ``getInstanceID``, ``getBit``, ``isDesign``, ``isInstance``, ``isNet``, ``isTerm``
+ - :meth:`from_string `, :meth:`toTuple `, :meth:`getType `, :meth:`getDBID `, :meth:`getLibraryID `, :meth:`getDesignID `, :meth:`getDesignObjectID `, :meth:`getInstanceID `, :meth:`getBit `, :meth:`isDesign `, :meth:`isInstance `, :meth:`isNet `, :meth:`isTerm `
* - :class:`najaeda.naja.LogicCone`
- - ``FanIn``, ``FanOut``, ``getDirection``, ``getRoot``, ``getNodes``, ``getLeaves``, ``getNodeCount`` and snake_case aliases
+ - :attr:`FanIn `, :attr:`FanOut `, :meth:`getDirection `, :meth:`getRoot `, :meth:`getNodes `, :meth:`getLeaves `, :meth:`getNodeCount ` and snake_case aliases
* - Module functions
- :func:`najaeda.naja.getVersion`, :func:`najaeda.naja.getGitHash`,
:func:`najaeda.naja.snapshot_manifest`,
@@ -277,39 +279,39 @@ semantic source of truth.
:func:`najaeda.naja.logCritical` plus the SystemVerilog intent helpers
listed above
+
Detailed raw reference
----------------------
-When Sphinx can import the compiled extension, the following section exposes
-clickable class and method anchors generated from the raw binding itself.
-Documentation builders that do not have the binary available show the static
-expert reference above.
-
-.. only:: raw_naja_available
+The following class and method references are generated from the compiled
+extension. Building this documentation requires an importable extension from
+the same checkout.
- .. py:currentmodule:: najaeda.naja
+.. py:module:: najaeda.naja
- Core database objects
- ~~~~~~~~~~~~~~~~~~~~~
+Core database objects
+~~~~~~~~~~~~~~~~~~~~~
- .. autosummary::
+.. autosummary::
- NLUniverse
- NLDB
- NLLibrary
- SNLDesign
+ NLUniverse
+ NLDB
+ NLLibrary
+ SNLDesign
- .. autoclass:: najaeda.naja.NLUniverse
- :members:
- :undoc-members:
+.. autoclass:: najaeda.naja.NLUniverse
+ :members:
+ :undoc-members:
+ :inherited-members:
- .. autoclass:: najaeda.naja.NLDB
- :members:
- :undoc-members:
+.. autoclass:: najaeda.naja.NLDB
+ :members:
+ :undoc-members:
+ :inherited-members:
- ``NLDB.loadSystemVerilog`` writes incremental frontend diagnostics to
- ``naja_sv_diagnostics.log`` by default. Pass ``diagnostics_report_path=None``
- to disable the report file and keep diagnostics console-only.
+``NLDB.loadSystemVerilog`` writes incremental frontend diagnostics to
+``naja_sv_diagnostics.log`` by default. Pass ``diagnostics_report_path=None``
+to disable the report file and keep diagnostics console-only.
Both ``NLDB.loadSystemVerilog`` and ``NLDB.loadVHDL`` accept
``library="DESIGN"`` to select a root ``NLLibrary`` in this database. Missing
@@ -485,226 +487,275 @@ expert reference above.
specific ``SystemVerilog*`` exception classes and expose structured
diagnostic details where available.
- .. autoclass:: najaeda.naja.NLLibrary
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLDesign
- :members:
- :undoc-members:
-
- Design objects
- ~~~~~~~~~~~~~~
-
- .. autosummary::
-
- SNLInstance
- SNLTerm
- SNLScalarTerm
- SNLBusTerm
- SNLBundleTerm
- SNLBitTerm
- SNLBusTermBit
- SNLNet
- SNLScalarNet
- SNLBusNet
- SNLBitNet
- SNLInstTerm
- SNLInstParameter
- SNLParameter
- SNLAttribute
-
- .. autoclass:: najaeda.naja.SNLInstance
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLTerm
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLScalarTerm
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLBusTerm
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLBundleTerm
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLBitTerm
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLBusTermBit
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLNet
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLScalarNet
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLBusNet
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLBitNet
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLInstTerm
- :members:
- :undoc-members:
- :inherited-members:
-
- .. autoclass:: najaeda.naja.SNLInstParameter
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLParameter
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLAttribute
- :members:
- :undoc-members:
-
- Hierarchy and connectivity
- ~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- .. autosummary::
-
- SNLPath
- SNLOccurrence
- SNLEquipotential
- SNLNetComponent
- NLID
- LogicCone
-
- .. autoclass:: najaeda.naja.SNLPath
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLOccurrence
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLEquipotential
- :members:
- :undoc-members:
-
- Pass ``mode=SNLEquipotential.Mode.TraverseAssigns`` to the constructor
- to cross Assign instances and omit their instance terminals. The
- default is ``SNLEquipotential.Mode.Standard``.
-
- .. autoclass:: najaeda.naja.SNLNetComponent
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.NLID
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.LogicCone
- :members:
- :undoc-members:
-
- Enum-like values
- ~~~~~~~~~~~~~~~~
-
- .. autosummary::
-
- SNLTerm.Direction
- SNLNet.Type
- SNLEquipotential.Mode
- SNLTermRole
- SNLActiveLevel
-
- .. autoclass:: najaeda.naja.SNLTerm.Direction
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLNet.Type
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLEquipotential.Mode
- :members:
- :undoc-members:
-
- ``SNLNet.Type.AssignX`` and ``SNLNet.Type.AssignZ`` represent structural
- four-state constants. Use ``SNLNet.isConstantX()`` and
- ``SNLNet.isConstantZ()`` to distinguish them; ``isConstant()`` accepts all
- four assign values while the zero and one predicates remain binary-only.
-
- .. autoclass:: najaeda.naja.SNLTermRole
- :members:
- :undoc-members:
-
- .. autoclass:: najaeda.naja.SNLActiveLevel
- :members:
- :undoc-members:
-
- Module functions
- ~~~~~~~~~~~~~~~~
-
- .. autosummary::
-
- getVersion
- getGitHash
- snapshot_manifest
- setLogLevel
- addLogFile
- clearLogSinks
- installLoggingHandler
- log
- logInfo
- logWarn
- logCritical
- intent_available
- live_compilation
- ast_symbol_of
- snl_objects_of
- intent_parameters_of
- intent_type_of
- intent_package_member
-
- .. autofunction:: najaeda.naja.getVersion
- .. autofunction:: najaeda.naja.getGitHash
-
- ``snapshot_manifest(path)`` reads only a snapshot's ``snl.mf`` manifest;
- it does not load the Cap'n Proto payload or create an ``NLUniverse``. It
- returns the schema version and the Naja producer version / Git hash. At
- present, loading a snapshot requires an exact match of both producer
- values with the reader build; regenerate snapshots after changing builds.
-
- .. autofunction:: najaeda.naja.snapshot_manifest
- .. autofunction:: najaeda.naja.setLogLevel
- .. autofunction:: najaeda.naja.addLogFile
- .. autofunction:: najaeda.naja.clearLogSinks
- .. autofunction:: najaeda.naja.installLoggingHandler
- .. autofunction:: najaeda.naja.log
- .. autofunction:: najaeda.naja.logInfo
- .. autofunction:: najaeda.naja.logWarn
- .. autofunction:: najaeda.naja.logCritical
- .. autofunction:: najaeda.naja.intent_available
- .. autofunction:: najaeda.naja.live_compilation
- .. autofunction:: najaeda.naja.ast_symbol_of
- .. autofunction:: najaeda.naja.snl_objects_of
- .. autofunction:: najaeda.naja.intent_parameters_of
- .. autofunction:: najaeda.naja.intent_type_of
- .. autofunction:: najaeda.naja.intent_package_member
+.. autoclass:: najaeda.naja.NLLibrary
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLDesign
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+Design objects
+~~~~~~~~~~~~~~
+
+.. autosummary::
+
+ SNLInstance
+ SNLTerm
+ SNLScalarTerm
+ SNLBusTerm
+ SNLBundleTerm
+ SNLBitTerm
+ SNLBusTermBit
+ SNLNet
+ SNLScalarNet
+ SNLBusNet
+ SNLBitNet
+ SNLInstTerm
+ SNLInstParameter
+ SNLParameter
+ SNLAttribute
+
+.. autoclass:: najaeda.naja.SNLInstance
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLTerm
+ :exclude-members: Direction
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLScalarTerm
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLBusTerm
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLBundleTerm
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLBitTerm
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLBusTermBit
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLNet
+ :exclude-members: Type
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLScalarNet
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLBusNet
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLBitNet
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLInstTerm
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLInstParameter
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+Use the raw API to declare model parameters and set instance values; these
+operations do not currently have high-level ``najaeda.netlist`` wrappers.
+The final value argument of ``create_decimal(design, name[, value])``,
+``create_string(design, name[, value])``,
+``create_boolean(design, name[, value])``, and
+``create_binary(design, name, size[, value])`` is optional. Omitting it creates
+a required parameter with no default. The binary creator retains its existing
+``size`` argument.
+
+.. code-block:: python
+
+ width = naja.SNLParameter.create_decimal(model, "WIDTH")
+ assert not width.hasDefaultValue()
+ naja.SNLInstParameter.create(instance, width, "8")
+
+``hasDefaultValue()`` distinguishes an absent default from an explicit zero,
+false, or empty string. ``getValue()`` returns the default as a string, or
+``None`` if there is none. Instance values are obtained separately with
+``SNLInstParameter.getValue()``. Cloning and NajaIF snapshots preserve default
+presence, including empty string defaults.
+
+Instances may be constructed before their required values are attached. Verilog
+export rejects an instance missing a required value, and always emits supplied
+values for required parameters. The current Verilog writer cannot emit a model
+declaration without a default and reports an error for such declarations;
+it does not emit SystemVerilog parameter port lists. A model supplied externally
+may still be instantiated when its declaration is excluded from the export.
+Parameter-derived truth tables require a model default. Changing an instance
+parameter does not re-elaborate the model's ports or topology.
+
+.. autoclass:: najaeda.naja.SNLParameter
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLAttribute
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+Hierarchy and connectivity
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. autosummary::
+
+ SNLPath
+ SNLOccurrence
+ SNLEquipotential
+ SNLNetComponent
+ NLID
+ LogicCone
+
+.. autoclass:: najaeda.naja.SNLPath
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLOccurrence
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLEquipotential
+ :exclude-members: Mode
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+ Pass ``mode=SNLEquipotential.Mode.TraverseAssigns`` to the constructor
+ to cross Assign instances and omit their instance terminals. The
+ default is ``SNLEquipotential.Mode.Standard``.
+
+.. autoclass:: najaeda.naja.SNLNetComponent
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.NLID
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.LogicCone
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+Enum-like values
+~~~~~~~~~~~~~~~~
+
+.. autosummary::
+
+ SNLTerm.Direction
+ SNLNet.Type
+ SNLEquipotential.Mode
+ SNLTermRole
+ SNLActiveLevel
+
+.. autoclass:: najaeda.naja.SNLTerm.Direction
+ :members:
+ :undoc-members:
+
+.. autoclass:: najaeda.naja.SNLNet.Type
+ :members:
+ :undoc-members:
+
+.. autoclass:: najaeda.naja.SNLEquipotential.Mode
+ :members:
+ :undoc-members:
+
+``SNLNet.Type.AssignX`` and ``SNLNet.Type.AssignZ`` represent structural
+four-state constants. Use ``SNLNet.isConstantX()`` and
+``SNLNet.isConstantZ()`` to distinguish them; ``isConstant()`` accepts all
+four assign values while the zero and one predicates remain binary-only.
+
+.. autoclass:: najaeda.naja.SNLTermRole
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+.. autoclass:: najaeda.naja.SNLActiveLevel
+ :members:
+ :undoc-members:
+ :inherited-members:
+
+Module functions
+~~~~~~~~~~~~~~~~
+
+.. autosummary::
+
+ getVersion
+ getGitHash
+ snapshot_manifest
+ setLogLevel
+ addLogFile
+ clearLogSinks
+ installLoggingHandler
+ log
+ logInfo
+ logWarn
+ logCritical
+ intent_available
+ live_compilation
+ ast_symbol_of
+ snl_objects_of
+ intent_parameters_of
+ intent_type_of
+ intent_package_member
+
+.. autofunction:: najaeda.naja.getVersion
+.. autofunction:: najaeda.naja.getGitHash
+
+``snapshot_manifest(path)`` reads only a snapshot's ``snl.mf`` manifest;
+it does not load the Cap'n Proto payload or create an ``NLUniverse``. It
+returns the schema version and the Naja producer version / Git hash. At
+present, loading a snapshot requires an exact match of both producer
+values with the reader build; regenerate snapshots after changing builds.
+
+.. autofunction:: najaeda.naja.snapshot_manifest
+.. autofunction:: najaeda.naja.setLogLevel
+.. autofunction:: najaeda.naja.addLogFile
+.. autofunction:: najaeda.naja.clearLogSinks
+.. autofunction:: najaeda.naja.installLoggingHandler
+.. autofunction:: najaeda.naja.log
+.. autofunction:: najaeda.naja.logInfo
+.. autofunction:: najaeda.naja.logWarn
+.. autofunction:: najaeda.naja.logCritical
+.. autofunction:: najaeda.naja.intent_available
+.. autofunction:: najaeda.naja.live_compilation
+.. autofunction:: najaeda.naja.ast_symbol_of
+.. autofunction:: najaeda.naja.snl_objects_of
+.. autofunction:: najaeda.naja.intent_parameters_of
+.. autofunction:: najaeda.naja.intent_type_of
+.. autofunction:: najaeda.naja.intent_package_member
diff --git a/src/nl/formats/verilog/backend/SNLVRLDumper.cpp b/src/nl/formats/verilog/backend/SNLVRLDumper.cpp
index b57882245..39adfb923 100644
--- a/src/nl/formats/verilog/backend/SNLVRLDumper.cpp
+++ b/src/nl/formats/verilog/backend/SNLVRLDumper.cpp
@@ -235,6 +235,9 @@ bool shouldDumpInstParameter(
const naja::NL::SNLInstance* instance,
const naja::NL::SNLInstParameter* instParameter) {
const auto* parameter = instParameter->getParameter();
+ if (not parameter->hasDefaultValue()) {
+ return true;
+ }
if (naja::NL::NLDB0::isMemory(instance->getModel()) &&
parameter->getName() == naja::NL::NLName("INIT") &&
isZeroVerilogIntegerLiteral(instParameter->getValue())) {
@@ -1766,6 +1769,14 @@ bool SNLVRLDumper::dumpInstance(
const SNLInstance* instance,
std::ostream& o,
DesignInsideAnonymousNaming& naming) {
+ for (auto parameter: instance->getModel()->getParameters()) {
+ if (not parameter->hasDefaultValue() &&
+ not instance->getInstParameter(parameter->getName())) {
+ throw SNLVRLDumperException(
+ "Missing value for required parameter " + parameter->getName().getString() +
+ " on instance " + instance->getString());
+ }
+ }
if (NLDB0::isMux2(instance->getModel())) {
emitNajaMux2Model_ = true;
emitNajaPrimitiveModels_ = true;
@@ -2214,6 +2225,12 @@ void SNLVRLDumper::dumpTermAssigns(const SNLDesign* design, std::ostream& o) {
}
void SNLVRLDumper::dumpParameter(const SNLParameter* parameter, std::ostream& o) {
+ if (not parameter->hasDefaultValue()) {
+ throw SNLVRLDumperException(
+ "Cannot emit required parameter " + parameter->getName().getString() +
+ " in design " + parameter->getDesign()->getString() +
+ ": Verilog parameter declarations require a default value");
+ }
o << "parameter " << parameter->getName().getString() << " = ";
if (parameter->getType()==SNLParameter::Type::String) {
o << "\"" << parameter->getValue() << "\"";
diff --git a/src/nl/netlist/serialization/capnp/SNLCapnPInterface.cpp b/src/nl/netlist/serialization/capnp/SNLCapnPInterface.cpp
index 9b140372e..21830a528 100644
--- a/src/nl/netlist/serialization/capnp/SNLCapnPInterface.cpp
+++ b/src/nl/netlist/serialization/capnp/SNLCapnPInterface.cpp
@@ -134,7 +134,10 @@ void dumpParameter(
const SNLParameter* snlParameter) {
parameter.setName(snlParameter->getName().getString());
parameter.setType(SNLtoCapNpParameterType(snlParameter->getType()));
- parameter.setValue(snlParameter->getValue());
+ // A null Text pointer denotes no default; setValue("") preserves an empty default.
+ if (snlParameter->hasDefaultValue()) {
+ parameter.setValue(snlParameter->getValue());
+ }
}
DesignType SNLtoCapNpDesignType(SNLDesign::Type type) {
@@ -340,8 +343,11 @@ void loadDesignParameter(
const SNLDesignInterface::Parameter::Reader& parameter) {
auto name = parameter.getName();
auto type = parameter.getType();
- auto value = parameter.getValue();
- SNLParameter::create(design, NLName(name), CapnPtoSNLParameterType(type), value);
+ if (parameter.hasValue()) {
+ SNLParameter::create(design, NLName(name), CapnPtoSNLParameterType(type), parameter.getValue());
+ } else {
+ SNLParameter::create(design, NLName(name), CapnPtoSNLParameterType(type));
+ }
}
void loadDesignInterface(
diff --git a/src/nl/netlist/snl/SNLParameter.cpp b/src/nl/netlist/snl/SNLParameter.cpp
index fa41d3787..390a5a7b1 100644
--- a/src/nl/netlist/snl/SNLParameter.cpp
+++ b/src/nl/netlist/snl/SNLParameter.cpp
@@ -28,7 +28,7 @@ std::string SNLParameter::Type::getString() const {
}
//LCOV_EXCL_STOP
-SNLParameter::SNLParameter(SNLDesign* design, const NLName& name, Type type, const std::string& value):
+SNLParameter::SNLParameter(SNLDesign* design, const NLName& name, Type type, std::optional value):
design_(design), name_(name), type_(type), value_(value)
{}
@@ -39,6 +39,20 @@ SNLParameter* SNLParameter::create(SNLDesign* design, const NLName& name, Type t
return parameter;
}
+SNLParameter* SNLParameter::create(SNLDesign* design, const NLName& name, Type type) {
+ preCreate(design, name);
+ auto parameter = new SNLParameter(design, name, type, std::nullopt);
+ parameter->postCreate();
+ return parameter;
+}
+
+std::string SNLParameter::getValue() const {
+ if (not hasDefaultValue()) {
+ throw NLException("SNLParameter " + getName().getString() + " has no default value");
+ }
+ return *value_;
+}
+
void SNLParameter::postCreate() {
design_->addParameter(this);
}
@@ -63,7 +77,7 @@ bool SNLParameter::deepCompare(const SNLParameter* other, std::string& reason) c
if (getName() != other->getName()) {
return false;
}
- if (getValue() not_eq other->getValue()) {
+ if (getType() != other->getType() || value_ != other->value_) {
return false;
}
return true;
@@ -87,7 +101,7 @@ std::string SNLParameter::getDescription() const {
stream << "<" << std::string(getTypeName());
stream << " " + getType().getString();
stream << " " + getName().getString();
- stream << " " << getValue();
+ stream << " " << (hasDefaultValue() ? getValue() : "");
stream << ">";
return stream.str();
}
diff --git a/src/nl/netlist/snl/SNLParameter.h b/src/nl/netlist/snl/SNLParameter.h
index ff7251924..9649d6079 100644
--- a/src/nl/netlist/snl/SNLParameter.h
+++ b/src/nl/netlist/snl/SNLParameter.h
@@ -5,6 +5,7 @@
#pragma once
#include
+#include
#include "NLName.h"
#include "NajaObject.h"
@@ -45,11 +46,17 @@ class SNLParameter : public NajaObject {
*/
static SNLParameter* create(SNLDesign* design, const NLName& name, Type type, const std::string& value);
+ /// \brief Create a required parameter without a default value.
+ static SNLParameter* create(SNLDesign* design, const NLName& name, Type type);
+
/// \brief Destroy this SNLParameter.
void destroy();
/// \return this SNLParameter name.
NLName getName() const { return name_; }
- std::string getValue() const { return value_; }
+ /// \return whether this parameter has a default (including an empty string).
+ bool hasDefaultValue() const { return value_.has_value(); }
+ /// \return the default value; throws NLException if no default exists.
+ std::string getValue() const;
Type getType() const { return type_; }
/// \return this SNLParameter owning SNLDesign.
SNLDesign* getDesign() const { return design_; }
@@ -71,7 +78,7 @@ class SNLParameter : public NajaObject {
};
bool deepCompare(const SNLParameter* other, std::string& reason) const;
private:
- SNLParameter(SNLDesign* design, const NLName& name, Type type, const std::string& value);
+ SNLParameter(SNLDesign* design, const NLName& name, Type type, std::optional value);
static void preCreate(SNLDesign* design, const NLName& name);
void postCreate();
void destroyFromDesign();
@@ -79,7 +86,7 @@ class SNLParameter : public NajaObject {
SNLDesign* design_ { nullptr };
NLName name_ {};
Type type_ { Type::Decimal };
- std::string value_ {};
+ std::optional value_ {};
boost::intrusive::set_member_hook<> designParametersHook_ {};
};
diff --git a/src/nl/python/naja_wrapping/PyNLLibrary.cpp b/src/nl/python/naja_wrapping/PyNLLibrary.cpp
index ce30a3c70..64894e52b 100644
--- a/src/nl/python/naja_wrapping/PyNLLibrary.cpp
+++ b/src/nl/python/naja_wrapping/PyNLLibrary.cpp
@@ -6,6 +6,7 @@
#include "PyInterface.h"
#include "PyNLDB.h"
+#include "PyNLLibraries.h"
#include "PySNLDesign.h"
#include "PySNLDesigns.h"
@@ -83,6 +84,7 @@ GetBoolAttribute(NLLibrary, isPrimitives)
SetNameMethod(NLLibrary)
GetNameMethod(NLLibrary)
+GetContainerMethod(NLLibrary, NLLibrary*, NLLibraries, Libraries)
GetContainerMethod(NLLibrary, SNLDesign*, SNLDesigns, SNLDesigns)
DBoDeallocMethod(NLLibrary)
@@ -111,6 +113,8 @@ PyMethodDef PyNLLibrary_Methods[] = {
"get Parent DB."},
{ "getLibrary", (PyCFunction)PyNLLibrary_getLibrary, METH_VARARGS,
"retrieve a NLLibrary."},
+ { "getLibraries", (PyCFunction)PyNLLibrary_getLibraries, METH_NOARGS,
+ "iterate over the child NLLibraries."},
{ "getSNLDesign", (PyCFunction)PyNLLibrary_getSNLDesign, METH_O,
"retrieve a SNLDesign."},
{ "getSNLDesigns", (PyCFunction)PyNLLibrary_getSNLDesigns, METH_NOARGS,
diff --git a/src/nl/python/naja_wrapping/PySNLParameter.cpp b/src/nl/python/naja_wrapping/PySNLParameter.cpp
index cfae7ba76..6b2b869b9 100644
--- a/src/nl/python/naja_wrapping/PySNLParameter.cpp
+++ b/src/nl/python/naja_wrapping/PySNLParameter.cpp
@@ -18,17 +18,18 @@ static PyObject* PySNLParameter_createString(PyObject*, PyObject* args) {
PyObject* arg0 = nullptr;
const char* arg1 = nullptr;
const char* arg2 = nullptr;
- if (not PyArg_ParseTuple(args, "Oss:SNLParameter.createString", &arg0, &arg1, &arg2)) {
+ if (not PyArg_ParseTuple(args, "Os|s:SNLParameter.createString", &arg0, &arg1, &arg2)) {
setError("malformed SNLParameter string value creation method");
return nullptr;
}
NLName name = NLName(arg1);
- std::string value = arg2;
SNLParameter* parameter = nullptr;
TRY
if (IsPySNLDesign(arg0)) {
- parameter = SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::String, value);
+ parameter = arg2
+ ? SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::String, arg2)
+ : SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::String);
} else {
setError("SNLParameter create accepts SNLDesign as first argument");
return nullptr;
@@ -41,7 +42,7 @@ static PyObject* PySNLParameter_createDecimal(PyObject*, PyObject* args) {
PyObject* arg0 = nullptr;
const char* arg1 = nullptr;
int value = 0;
- if (not PyArg_ParseTuple(args, "Osi:SNLParameter.createDecimal", &arg0, &arg1, &value)) {
+ if (not PyArg_ParseTuple(args, "Os|i:SNLParameter.createDecimal", &arg0, &arg1, &value)) {
setError("malformed SNLParameter int value creation method");
return nullptr;
}
@@ -50,7 +51,9 @@ static PyObject* PySNLParameter_createDecimal(PyObject*, PyObject* args) {
SNLParameter* parameter = nullptr;
TRY
if (IsPySNLDesign(arg0)) {
- parameter = SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Decimal, std::to_string(value));
+ parameter = PyTuple_Size(args) > 2
+ ? SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Decimal, std::to_string(value))
+ : SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Decimal);
} else {
setError("SNLParameter create accepts SNLDesign as first argument");
return nullptr;
@@ -64,7 +67,7 @@ static PyObject* PySNLParameter_createBinary(PyObject*, PyObject* args) {
const char* arg1 = nullptr;
int size = 0;
int value = 0;
- if (not PyArg_ParseTuple(args, "Osii:SNLParameter.createBinary", &arg0, &arg1, &size, &value)) {
+ if (not PyArg_ParseTuple(args, "Osi|i:SNLParameter.createBinary", &arg0, &arg1, &size, &value)) {
setError("malformed SNLParameter binary value creation method");
return nullptr;
}
@@ -73,7 +76,9 @@ static PyObject* PySNLParameter_createBinary(PyObject*, PyObject* args) {
SNLParameter* parameter = nullptr;
TRY
if (IsPySNLDesign(arg0)) {
- parameter = SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Binary, std::to_string(value));
+ parameter = PyTuple_Size(args) > 3
+ ? SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Binary, std::to_string(value))
+ : SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Binary);
} else {
setError("SNLParameter create accepts SNLDesign as first argument");
return nullptr;
@@ -86,7 +91,7 @@ static PyObject* PySNLParameter_createBoolean(PyObject*, PyObject* args) {
PyObject* arg0 = nullptr;
const char* arg1 = nullptr;
int value = 0;
- if (not PyArg_ParseTuple(args, "Osp:SNLParameter.createBoolean", &arg0, &arg1, &value)) {
+ if (not PyArg_ParseTuple(args, "Os|p:SNLParameter.createBoolean", &arg0, &arg1, &value)) {
setError("malformed SNLParameter boolean value creation method");
return nullptr;
}
@@ -95,7 +100,9 @@ static PyObject* PySNLParameter_createBoolean(PyObject*, PyObject* args) {
SNLParameter* parameter = nullptr;
TRY
if (IsPySNLDesign(arg0)) {
- parameter = SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Boolean, std::to_string(value));
+ parameter = PyTuple_Size(args) > 2
+ ? SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Boolean, std::to_string(value))
+ : SNLParameter::create(PYSNLDesign_O(arg0), name, SNLParameter::Type::Boolean);
} else {
setError("SNLParameter create accepts SNLDesign as first argument");
return nullptr;
@@ -104,6 +111,25 @@ static PyObject* PySNLParameter_createBoolean(PyObject*, PyObject* args) {
return PySNLParameter_Link(parameter);
}
+static PyObject* PySNLParameter_hasDefaultValue(PySNLParameter* self) {
+ METHOD_HEAD("PySNLParameter.hasDefaultValue()")
+ return PyBool_FromLong(selfObject->hasDefaultValue());
+}
+
+static PyObject* PySNLParameter_getValue(PySNLParameter* self) {
+ METHOD_HEAD("PySNLParameter.getValue()")
+ if (not selfObject->hasDefaultValue()) {
+ Py_RETURN_NONE;
+ }
+ TRY
+ return PyUnicode_FromString(selfObject->getValue().c_str());
+ // The missing-default case is handled above; only defensive exceptions remain.
+ // LCOV_EXCL_START
+ NLCATCH
+ return nullptr;
+ // LCOV_EXCL_STOP
+}
+
GetNameMethod(SNLParameter)
GetObjectMethod(SNLParameter, SNLDesign, getDesign)
@@ -111,13 +137,17 @@ DBoDestroyAttribute(PySNLParameter_destroy, PySNLParameter)
PyMethodDef PySNLParameter_Methods[] = {
{ "create_string", (PyCFunction)PySNLParameter_createString, METH_VARARGS|METH_STATIC,
- "SNLParameter string value creator"},
+ "SNLParameter string value creator; omit the final value argument for no default"},
{ "create_decimal", (PyCFunction)PySNLParameter_createDecimal, METH_VARARGS|METH_STATIC,
- "SNLParameter int value creator"},
+ "SNLParameter int value creator; omit the final value argument for no default"},
{ "create_binary", (PyCFunction)PySNLParameter_createBinary, METH_VARARGS|METH_STATIC,
- "SNLParameter binary value creator"},
+ "SNLParameter binary value creator; omit the final value argument for no default"},
{ "create_boolean", (PyCFunction)PySNLParameter_createBoolean, METH_VARARGS|METH_STATIC,
- "SNLParameter boolean value creator"},
+ "SNLParameter boolean value creator; omit the final value argument for no default"},
+ { "hasDefaultValue", (PyCFunction)PySNLParameter_hasDefaultValue, METH_NOARGS,
+ "Return whether this parameter has a default value."},
+ { "getValue", (PyCFunction)PySNLParameter_getValue, METH_NOARGS,
+ "Return the default as a string, or None if no default exists."},
{ "getName", (PyCFunction)PySNLParameter_getName, METH_NOARGS,
"get SNLParameter name"},
{ "getDesign", (PyCFunction)PySNLParameter_getDesign, METH_NOARGS,
@@ -133,4 +163,4 @@ DBoLinkCreateMethod(SNLParameter)
PyTypeNLObjectWithoutNLIDLinkPyType(SNLParameter)
PyTypeObjectDefinitions(SNLParameter)
-}
\ No newline at end of file
+}
diff --git a/test/nl/formats/verilog/backend/SNLVRLDumperTestParameters.cpp b/test/nl/formats/verilog/backend/SNLVRLDumperTestParameters.cpp
index fc8de2798..2ba4418dc 100644
--- a/test/nl/formats/verilog/backend/SNLVRLDumperTestParameters.cpp
+++ b/test/nl/formats/verilog/backend/SNLVRLDumperTestParameters.cpp
@@ -721,3 +721,23 @@ TEST_F(SNLVRLDumperTestParameters, testWideMuxAndMemoryPrimitiveFileDump) {
EXPECT_EQ(1u, countSubstring(primitiveDump, "module naja_dffssne #("));
EXPECT_EQ(1u, countSubstring(primitiveDump, "module naja_divmod #("));
}
+
+TEST_F(SNLVRLDumperTestParameters, requiredParameterDeclarationRejected) {
+ SNLParameter::create(top_, NLName("WIDTH"), SNLParameter::Type::Decimal);
+ SNLVRLDumper dumper;
+ std::ostringstream out;
+ EXPECT_THROW(dumper.dumpDesign(top_, out), SNLVRLDumperException);
+}
+
+TEST_F(SNLVRLDumperTestParameters, requiredInstanceParameter) {
+ auto parameter = SNLParameter::create(model_, NLName("TEXT"), SNLParameter::Type::String);
+ auto instance = SNLInstance::create(top_, model_, NLName("inst"));
+ SNLVRLDumper dumper;
+ dumper.setDumpHierarchy(false);
+ std::ostringstream missing;
+ EXPECT_THROW(dumper.dumpDesign(top_, missing), SNLVRLDumperException);
+ SNLInstParameter::create(instance, parameter, "");
+ std::ostringstream out;
+ EXPECT_NO_THROW(dumper.dumpDesign(top_, out));
+ EXPECT_NE(std::string::npos, out.str().find(".TEXT(\"\")"));
+}
diff --git a/test/nl/python/naja_wrapping/test_nldb.py b/test/nl/python/naja_wrapping/test_nldb.py
index c84532081..33ed61b82 100644
--- a/test/nl/python/naja_wrapping/test_nldb.py
+++ b/test/nl/python/naja_wrapping/test_nldb.py
@@ -7,6 +7,7 @@
import shutil
import tempfile
import unittest
+import warnings
import zipfile
import naja
import faulthandler
@@ -104,6 +105,14 @@ def testVHDL(self):
self.assertEqual(db.getTopDesign(), design)
self.assertTrue(db.isTopDB())
+ def testVHDLBetaWarningAsError(self):
+ db = naja.NLDB.create(naja.NLUniverse.get())
+ with warnings.catch_warnings():
+ warnings.simplefilter("error", RuntimeWarning)
+ with self.assertRaisesRegex(RuntimeWarning, "VHDL parser is in Beta mode"):
+ db.loadVHDL("input.vhd")
+ self.assertEqual(len(list(db.getLibraries())), 0)
+
def testVHDLWarningReport(self):
db = naja.NLDB.create(naja.NLUniverse.get())
with tempfile.TemporaryDirectory() as directory:
diff --git a/test/nl/python/naja_wrapping/test_nllibrary.py b/test/nl/python/naja_wrapping/test_nllibrary.py
index e78f128a5..f9d7a1aaf 100644
--- a/test/nl/python/naja_wrapping/test_nllibrary.py
+++ b/test/nl/python/naja_wrapping/test_nllibrary.py
@@ -35,6 +35,7 @@ def test(self):
self.assertFalse(lib1.isPrimitives())
self.assertListEqual([lib1], list(db.getLibraries()))
self.assertListEqual([], list(db.getPrimitiveLibraries()))
+ self.assertListEqual([], list(lib1.getLibraries()))
lib1.setName('RENAMEDLIB1')
self.assertIsNone(db.getLibrary("LIB1"))
@@ -48,11 +49,14 @@ def test(self):
self.assertEqual(lib2, lib1.getLibrary("LIB2"))
self.assertTrue(lib2.isStandard())
self.assertFalse(lib2.isPrimitives())
+ self.assertListEqual([lib2], list(lib1.getLibraries()))
self.assertListEqual([lib1, lib2], list(db.getGlobalLibraries()))
#create anonymous sublib
anonajaib = naja.NLLibrary.create(lib1)
self.assertIsNotNone(anonajaib)
+ self.assertListEqual([lib2, anonajaib], list(lib1.getLibraries()))
+ self.assertListEqual([], list(lib2.getLibraries()))
def testErrors(self):
u = naja.NLUniverse.get()
diff --git a/test/nl/python/naja_wrapping/test_snldesign.py b/test/nl/python/naja_wrapping/test_snldesign.py
index 83d573e4f..05a2e03e4 100644
--- a/test/nl/python/naja_wrapping/test_snldesign.py
+++ b/test/nl/python/naja_wrapping/test_snldesign.py
@@ -227,6 +227,33 @@ def testCompare(self):
self.assertGreater(design1, design0)
self.assertGreaterEqual(design1, design0)
+ def testRequiredParameters(self):
+ design = naja.SNLDesign.create(self.lib, "MODEL")
+ top = naja.SNLDesign.create(self.lib, "TOP")
+ instance = naja.SNLInstance.create(top, design, "inst")
+ cases = [
+ (naja.SNLParameter.create_decimal, (), 0, "0"),
+ (naja.SNLParameter.create_binary, (8,), 0, "0"),
+ (naja.SNLParameter.create_boolean, (), False, "0"),
+ (naja.SNLParameter.create_string, (), "", ""),
+ ]
+ for index, (creator, args, default, expected) in enumerate(cases):
+ name = "P" + str(index)
+ required = creator(design, name, *args)
+ self.assertFalse(required.hasDefaultValue())
+ self.assertIsNone(required.getValue())
+ actual = naja.SNLInstParameter.create(instance, required, expected)
+ self.assertEqual(expected, actual.getValue())
+ supplied = creator(design, name + "_DEFAULT", *args, default)
+ self.assertTrue(supplied.hasDefaultValue())
+ self.assertEqual(expected, supplied.getValue())
+ with self.assertRaises(RuntimeError):
+ creator(design, name, *args)
+ clone = design.clone("CLONE")
+ self.assertFalse(clone.getParameter("P3").hasDefaultValue())
+ self.assertTrue(clone.getParameter("P3_DEFAULT").hasDefaultValue())
+ self.assertEqual("", clone.getParameter("P3_DEFAULT").getValue())
+
def testParameters(self):
self.assertIsNotNone(self.lib)
design = naja.SNLDesign.create(self.lib, "DESIGN")
diff --git a/test/nl/snl/kernel/SNLParameterTest.cpp b/test/nl/snl/kernel/SNLParameterTest.cpp
index 081c67835..aba7eddad 100644
--- a/test/nl/snl/kernel/SNLParameterTest.cpp
+++ b/test/nl/snl/kernel/SNLParameterTest.cpp
@@ -113,3 +113,28 @@ TEST_F(SNLParameterTest, testInstanceParameterCreationError) {
SNLInstParameter::create(instance, param, "ERROR"),
NLException);
}
+
+TEST_F(SNLParameterTest, requiredParameters) {
+ auto parameter = SNLParameter::create(design_, NLName("WIDTH"), SNLParameter::Type::Decimal);
+ EXPECT_FALSE(parameter->hasDefaultValue());
+ EXPECT_THROW(parameter->getValue(), NLException);
+ EXPECT_NE(std::string::npos, parameter->getDescription().find("no default"));
+ EXPECT_THROW(SNLParameter::create(design_, NLName("WIDTH"), SNLParameter::Type::Decimal), NLException);
+
+ auto empty = SNLParameter::create(design_, NLName("TEXT"), SNLParameter::Type::String, "");
+ EXPECT_TRUE(empty->hasDefaultValue());
+ EXPECT_EQ("", empty->getValue());
+ auto clone = design_->clone(NLName("clone"));
+ std::string reason;
+ EXPECT_TRUE(parameter->deepCompare(clone->getParameter(NLName("WIDTH")), reason));
+ EXPECT_FALSE(clone->getParameter(NLName("WIDTH"))->hasDefaultValue());
+ EXPECT_TRUE(clone->getParameter(NLName("TEXT"))->hasDefaultValue());
+ EXPECT_EQ("", clone->getParameter(NLName("TEXT"))->getValue());
+
+ auto other = SNLDesign::create(designsLib_);
+ auto requiredText = SNLParameter::create(other, NLName("TEXT"), SNLParameter::Type::String);
+ EXPECT_FALSE(empty->deepCompare(requiredText, reason));
+ auto instance = SNLInstance::create(other, design_);
+ auto actual = SNLInstParameter::create(instance, parameter, "8");
+ EXPECT_EQ("8", actual->getValue());
+}
diff --git a/test/nl/snl/serialization/capnp/SNLCapnPTest0.cpp b/test/nl/snl/serialization/capnp/SNLCapnPTest0.cpp
index 9b051b914..9e50ec8f7 100644
--- a/test/nl/snl/serialization/capnp/SNLCapnPTest0.cpp
+++ b/test/nl/snl/serialization/capnp/SNLCapnPTest0.cpp
@@ -387,3 +387,23 @@ TEST_F(SNLCapNpTest0, incompatibleManifestSchemaThrows) {
::testing::HasSubstr("producer naja version test-producer, git hash test-hash"));
}
}
+
+TEST_F(SNLCapNpTest0, requiredParameterRoundTrip) {
+ auto model = db_->getLibrary(NLName("MYLIB"))->getSNLDesign(NLName("model0"));
+ auto required = SNLParameter::create(model, NLName("REQUIRED"), SNLParameter::Type::String);
+ SNLParameter::create(model, NLName("EMPTY"), SNLParameter::Type::String, "");
+ auto instance = db_->getTopDesign()->getInstance(NLName("instance1"));
+ SNLInstParameter::create(instance, required, "");
+ auto outPath = std::filesystem::path(SNL_CAPNP_TEST_PATH) / "requiredParameterRoundTrip.snl";
+ SNLCapnP::dump(db_, outPath);
+ db_->setID(2);
+ auto loaded = SNLCapnP::load(outPath);
+ std::string reason;
+ EXPECT_TRUE(db_->deepCompare(loaded, reason)) << reason;
+ auto loadedModel = loaded->getLibrary(NLName("MYLIB"))->getSNLDesign(NLName("model0"));
+ EXPECT_FALSE(loadedModel->getParameter(NLName("REQUIRED"))->hasDefaultValue());
+ EXPECT_TRUE(loadedModel->getParameter(NLName("EMPTY"))->hasDefaultValue());
+ EXPECT_EQ("", loadedModel->getParameter(NLName("EMPTY"))->getValue());
+ EXPECT_EQ("", loaded->getTopDesign()->getInstance(NLName("instance1"))
+ ->getInstParameter(NLName("REQUIRED"))->getValue());
+}