Manifest (ip.toml) — manifest.py + scaffold.py¶
The IP core manifest: identity, dependencies, source filesets, and build targets.
The analogue of Cargo.toml / package.json / a FuseSoC .core file. Pure module
(parsing uses the stdlib tomllib, no third-party TOML dependency).
- Source: src/hdlpkg/manifest.py, src/hdlpkg/scaffold.py
- Import:
from hdlpkg import Manifest, Dependency, Fileset, Target
The ip.toml format¶
Every core carries an ip.toml at its root:
schema = 1 # optional ip.toml format version (default 1)
[package]
vendor = "acme" # required ─┐
library = "comm" # required ├ VLNV identity
name = "uart" # required │
version = "1.2.0" # required ─┘ (per scheme: SemVer / calver / monotonic / opaque token)
scheme = "semver" # optional version scheme: "semver" (default) | "calver" | "monotonic" | "opaque"
description = "AXI-Lite UART"
license = "Apache-2.0" # SPDX id
authors = ["Jane Doe <jane@acme.com>"]
top = "uart_top" # default top-level unit
keywords = ["uart", "axi"]
[dependencies]
# "vendor:library:name" = "<constraint>"
"acme:common:fifo" = "^1.0.0"
[resolution] # optional; how to handle an incompatible conflict
on-conflict = "fail_on_conflict" # "fail_on_conflict" (default) | "use_latest" | "isolate_namespaces"
[filesets.rtl]
files = ["rtl/uart_top.sv", "rtl/uart_rx.sv"]
type = "systemVerilogSource" # IP-XACT fileType vocabulary
[filesets.tb]
files = ["tb/uart_tb.sv"]
type = "systemVerilogSource"
depend = ["rtl"] # other filesets this one needs
[targets.sim]
toolflow = "verilator" # which backend (see backends.md)
filesets = ["rtl", "tb"] # must reference defined filesets
top = "uart_tb" # overrides package.top for this target
Only [package] (with the four identity keys) is required; everything else is
optional. Unknown fields are ignored. The keys map onto the dataclasses below.
Data model¶
Manifest is a frozen dataclass with these fields:
| Field | Type | Notes |
|---|---|---|
vlnv |
Vlnv |
parsed from the four identity keys |
description, license |
str |
metadata |
authors, keywords |
tuple[str, ...] |
|
top |
str \| None |
default top unit |
dependencies |
tuple[Dependency, ...] |
|
filesets |
dict[str, Fileset] |
keyed by fileset name |
targets |
dict[str, Target] |
keyed by target name |
schema_version |
int |
the ip.toml format version (MANIFEST_SCHEMA_VERSION, default 1) |
version_scheme |
"semver" \| "opaque" |
how this core's versions are interpreted ([package].scheme, default semver) |
conflict_policy |
ConflictPolicy |
how the resolver handles an incompatible conflict ([resolution] on-conflict, default fail_on_conflict) |
ref (property) |
PackageRef |
version-less key |
The optional top-level schema key declares the ip.toml format version (default
1). A manifest written for a newer schema than this hdlpkg understands is
rejected with a clear ManifestError rather than mis-parsed — the migration path the
format needs once it freezes at 1.0. (Note the two distinct keys: top-level schema
is the format version; [package].scheme is the version scheme.)
The [package].scheme key selects the version scheme: semver
(default; valid SemVer), calver (ordered numeric 2024.1, year-as-major),
monotonic (an ordered revision r3, one shared group), or opaque (an
uninterpreted token, pinned exactly). The [resolution] on-conflict
key sets the conflict policy used when an incompatible conflict
arises. Both are validated and an unsupported value raises ManifestError.
Supporting value types:
Dependency—ref: PackageRef+constraint: VersionConstraint.str(dep)renders"vendor:library:name = <constraint>".Fileset—name,files: tuple[str, ...],type: str(default"systemVerilogSource"),depend: tuple[str, ...](other filesets it pulls in — honored by tool-flow generation, see backends). Afilesentry may be a literal path, a glob (rtl/**/*.vhd,**recurses), or a directory (packs every file under it); expansion happens atpack/gentime against the core directory, with matches sorted for deterministic output (the manifest keeps the patterns as written).Target—name,toolflow: str,filesets: tuple[str, ...],top: str | None.
Loading & validation¶
| Constructor | Description |
|---|---|
Manifest.from_str(text) -> Manifest |
Parse from a TOML string. |
Manifest.from_path(path) -> Manifest |
Parse from an ip.toml file. |
Manifest.from_dict(data) -> Manifest |
Validate an already-parsed mapping. |
Validation is strict and the error message always names the offending field:
[package]must exist and carryvendor/library/name/version; the version must be valid SemVer (or, underscheme = "opaque", a valid opaque token) and the segments valid VLNV parts.- Each dependency key must parse as a
PackageRefand its value as aVersionConstraint. - Each fileset must have a
fileslist of strings (each a literal path, a glob, or a directory; expanded atpack/gentime — an entry matching no file is then an error). - Each target must have a
toolflow, and every fileset it references must be defined — a dangling reference is rejected with the list of known filesets.
All failures raise ManifestError (see exceptions).
Scaffolding a starter manifest (scaffold.py, behind hdlpkg init)¶
scaffold.py renders a fresh, valid ip.toml from a few fields. It is pure (no
I/O); the CLI layer owns prompting and writing.
ScaffoldOptions— a frozen value type withvendor/library/name(validated as VLNV segments),version: Version, optionaldescription/license/top(defaults to the core name). Build from strings withScaffoldOptions.create(...)(parses the version; default0.1.0). Properties:effective_top,vlnv.render_manifest(options) -> str— emits a complete manifest with onertlfileset and onesim(Verilator) target. The output round-trips throughManifest, so a freshly scaffolded core passeshdlpkg validateimmediately.
Example¶
from hdlpkg import Manifest
from hdlpkg.scaffold import ScaffoldOptions, render_manifest
text = render_manifest(ScaffoldOptions.create("acme", "comm", "uart"))
m = Manifest.from_str(text)
assert str(m.vlnv) == "acme:comm:uart:0.1.0"
assert "rtl" in m.filesets and "sim" in m.targets