An IP packager and dependency manager for HDL (Verilog/VHDL/SystemVerilog) design reuse.
Project description
HDL IP Packager
A package manager and dependency resolver for HDL IP cores — bringing the
ergonomics of pip / npm / cargo / docker pull to Verilog, VHDL, and
SystemVerilog design reuse. Built in Python 3.11+.
Project status: pre-alpha / foundation. The versioning, identity (VLNV), and manifest layers are implemented and tested; resolution, packaging, and registries are designed and stubbed. See docs/progress_tracker.md for exactly what is done versus planned, and docs/architecture.md for the design.
Why
Hardware teams re-use IP constantly, but sharing it is still mostly manual: copy files, hand-track versions, hope the dependency you vendored matches the one your colleague used. Software solved this with package managers. The mature HDL attempts (FuseSoC, Bender, Orbit, IP-XACT/IEEE 1685, vendor catalogs) each got part of the way. This project distills the state of the art into one tool — see the research write-up in docs/research/state_of_the_art.md.
Features
Implemented today:
- VLNV identity — cores are named
vendor:library:name:version(the IP-XACT convention), so names are globally meaningful and collision-resistant. - Semantic versioning — full SemVer 2.0.0 parsing/precedence plus a
constraint grammar (
^,~,>=,<, ranges,*) for dependency specs. - Manifest (
ip.toml) — a TOML manifest per core declaring identity, metadata, filesets, dependencies, and build targets. - Dependency resolver — backtracking, newest-compatible resolution that unifies
SemVer-compatible dependents (Cargo-style) and applies a configurable
[resolution] on-conflictpolicy to incompatible conflicts (fail_on_conflict/use_latest/isolate_namespaces); scheme-aware (semver/calver/monotonic/opaque), pre-release-aware. Underisolate_namespaces,genname-mangles coexisting SystemVerilog/VHDL packages so two versions build together. - Lockfile (
ip.lock) — a deterministic, verifiable record of a resolve (exact VLNVs + source + SHA-256), written byhdlpkg resolve. - Content-addressed cache + registries — a SHA-256-keyed local cache with
verify-on-read, fed by local-directory, HTTP, and OCI registry backends behind one
--registrylocation (a path,http(s)://, oroci://);hdlpkg installresolves and fetches dependencies into it. - Private, self-hosted distribution — publish/consume cores over an internal HTTP server
or any OCI registry (Harbor, Artifactory, Nexus, GitLab, Zot, ECR/ACR) without going
public;
hdlpkg loginstores per-host credentials (a direct bearer token, or a username+secret that drives the OCI token-exchange used by managed registries; adocker loginis reused), so a team shares IP inside its network. A deterministic.ipkgartifact backspack, append-onlypublish(withyank), andpull(by VLNV). - CLI (
hdlpkg) — all commands are implemented:info,validate,init,add,resolve,install,pack,publish,pull,yank,login,logout,gen,tree,export-ipxact.
Designed and on the roadmap (see the progress tracker):
- A Git-backed registry channel.
- Tool-flow generation straight from a registry (it builds from local/extracted sources today).
Requirements
| Dependency | Version |
|---|---|
| Python | 3.11+ (uses stdlib tomllib) |
| OS | Windows, Linux, macOS (pure Python) |
| Runtime deps | none yet (kept minimal by design) |
Install
# From a clone, install in editable mode with the dev/test toolchain:
python -m pip install -e ".[dev]"
This puts the hdlpkg command on your PATH and installs pytest, ruff, mypy, and
pre-commit. Enable the local git hooks (ruff + mypy on commit) once with:
pre-commit install
Usage
hdlpkg --help # show all commands
hdlpkg init --vendor acme --library comm --name uart # scaffold a starter ip.toml
hdlpkg info ip.toml # print the parsed identity, deps, filesets, targets
hdlpkg validate ip.toml # parse + validate a manifest (exit 0 if OK)
hdlpkg resolve ip.toml --search ../cores # resolve deps to a deterministic ip.lock
hdlpkg install ip.toml --search ../cores # resolve + fetch deps into the cache
hdlpkg pack ip.toml # build a distributable .ipkg
hdlpkg pack ip.toml --sbom --search ../cores # also emit a CycloneDX SBOM
hdlpkg publish ip.toml --registry ../reg # publish into a local registry
hdlpkg pull acme:common:fifo:1.0.0 --registry ../reg --output ./fifo
hdlpkg gen sim ip.toml --search ../cores # generate Verilator/Vivado inputs for a target
hdlpkg tree ip.toml --search ../cores # print the resolved dependency graph
hdlpkg export-ipxact ip.toml # export an IP-XACT (IEEE 1685) component XML
python -m hdl_ip_packager info # same CLI, invoked as a module
# Private, self-hosted registry (HTTP or OCI) -- log in once, then publish/consume:
hdlpkg login oci://harbor.corp.local/ip # stores a per-host bearer token
hdlpkg publish ip.toml --registry oci://harbor.corp.local/ip
hdlpkg resolve ip.toml --registry oci://harbor.corp.local/ip # nothing leaves your network
There is also a full hdlpkg(1) man page (commands, the typical producer/consumer
workflow, files, registries, examples). View it from a checkout with man ./man/hdlpkg.1;
see man/README.md to install it so man hdlpkg works system-wide.
A minimal ip.toml:
[package]
vendor = "acme"
library = "comm"
name = "uart"
version = "1.2.0"
[dependencies]
"acme:common:fifo" = "^1.0.0"
[filesets.rtl]
files = ["rtl/uart_top.sv"]
type = "systemVerilogSource"
[targets.sim]
toolflow = "verilator"
filesets = ["rtl"]
top = "uart_top"
Optional keys extend this: [package].scheme selects the version scheme — semver
(default), calver (2024.1, year-as-major), monotonic (r3), or opaque
(non-SemVer tokens pinned exactly) — and [resolution] on-conflict = "..."
(fail_on_conflict default / use_latest / isolate_namespaces, also a
--on-conflict flag) for how an incompatible version conflict is handled. See
docs/modules/manifest.md and
resolver.md.
Two complete, working cores live under examples/ — a FIFO
(acme:common:fifo) and a UART (acme:comm:uart) that depends on it:
hdlpkg info examples/uart/ip.toml
hdlpkg validate examples/fifo/ip.toml
hdlpkg resolve examples/uart/ip.toml --search examples # writes examples/uart/ip.lock
Tests
The suite uses pytest with a scalable, marker-based layout and a foldable summary report. From the repo root:
pytest # run everything (with the local summary)
pytest -m unit # only fast unit tests
pytest -m "not integration" # skip filesystem/integration tests
pytest --cov=hdl_ip_packager --cov-report=term-missing # with coverage
# Produce the JUnit XML + the rendered Markdown report (what CI shows):
pytest --junitxml=test-results.xml
python scripts/render_test_summary.py --title "Test results"
See tests/README.md for how the suite is organized and how to add new test modules. CI runs the suite on every push/PR and renders the summary into the GitHub Actions run page.
Documentation
Full technical documentation lives in docs/ and is published
as a site at https://germanbravolopez.github.io/hdl-ip-packager/ (built from
docs/ by .github/workflows/docs.yml on every
push to main). To preview it locally:
pip install -e ".[docs]"
mkdocs serve
| Document | Description |
|---|---|
| User guide | Start here if you are new — what the tool does and a hands-on walkthrough |
| Module manual | Per-module reference + the full hdlpkg command reference |
| AI agent instructions | Start here if you are an AI agent or new contributor — briefing, file map, rules |
| Architecture | Module map, manifest/lockfile design, data flow, roadmap |
| State of the art | Research survey of package managers (pip/npm/cargo/docker) and HDL tools (FuseSoC, IP-XACT, Orbit, Bender) |
| Progress tracker | What is done, in progress, and planned |
| Quick-find index | Every file, concept, and topic |
Development workflow
Day-to-day work lands on develop, the working branch; main is the protected
release line, updated only at release time. No PR is needed for normal work —
only a release goes through a PR (see Releasing).
- Work on
develop(or a short-livedfeature//fix//docs/branch you merge into it).mainis off-limits for direct commits (ruleset "main": no direct commits/pushes, no force-push, no deletion). - Implement with tests. Keep docs in sync as you go — run the
/update-docschecklist (docs/progress_tracker.md,docs/architecture.md,docs/INDEX.md, and this README if user-visible behaviour changed). - Make the gates green before committing:
pytest,ruff check .,ruff format --check .,mypy. The pre-commit hooks (pre-commit install) run ruff + mypy on each commit so these are caught locally before CI. - Commit to
developand push. CI runs on the push. No PR — the accumulateddevelopdiff is reviewed at the next release.
A release is the one flow that uses a PR (develop → main); the agent reviews it
with /code-review and merges it (see Releasing). A human gate applies only when
the agent cannot safely decide on its own — the 1.0.0 stability sign-off, a
security-sensitive or hard-to-reverse change, or anything explicitly reserved.
Releasing
Releases are tag-driven, and the X.Y.Z tag must sit on the merge commit on
main — so a release goes through the same PR flow, not a direct push:
- On a
release/X.Y.Zbranch cut offdevelop, bump the version in bothpyproject.tomlandsrc/hdl_ip_packager/__init__.py, record the release indocs/progress_tracker.md, and make the gates green. - Open a PR into
main, review it (/code-review) and merge with a merge commit (the agent owns this — see the workflow above; the1.0.0sign-off is the one release that needs explicit human go-ahead). Fast-forwarddevelopto the mergedmainafterwards so the working branch carries the release commit. - On the updated
main, create and push the bareX.Y.Ztag (novprefix)..github/workflows/release.ymlthen builds the wheel + sdist, publishes to PyPI via OIDC trusted publishing, and creates a GitHub Release for the tag (a short summary from thedocs/progress_tracker.mdentry plus a link to the PyPI page, with the wheel + sdist attached). A guard (scripts/check_release_version.py) fails the run if the tag and the packaged version disagree, so the tag is the single source of truth for the published version.
(One-time: register the repo as a PyPI trusted publisher and create the pypi
environment.) The /release agent command in .claude/commands/ automates the
mechanics (bump both version files, run the gates, prepare the release PR, review +
merge it, then tag main and watch Actions + PyPI to green).
See docs/ai_agent_instructions.md for the full agent obligations and coding conventions.
License
MIT © 2026 German Bravo Lopez.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file hdl_ip_packager-0.10.0.tar.gz.
File metadata
- Download URL: hdl_ip_packager-0.10.0.tar.gz
- Upload date:
- Size: 203.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f45e1446a697b33c16d4f41db320e03cd905347ea08c1d0e6baea330f02d61b6
|
|
| MD5 |
297db803d4dd44aa3658e7d6ad278a31
|
|
| BLAKE2b-256 |
e4d030b762f61215cc09bab1b97d53186d9149a7f72cdf9706f7493841aede81
|
Provenance
The following attestation bundles were made for hdl_ip_packager-0.10.0.tar.gz:
Publisher:
release.yml on germanbravolopez/hdl-ip-packager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hdl_ip_packager-0.10.0.tar.gz -
Subject digest:
f45e1446a697b33c16d4f41db320e03cd905347ea08c1d0e6baea330f02d61b6 - Sigstore transparency entry: 1809472040
- Sigstore integration time:
-
Permalink:
germanbravolopez/hdl-ip-packager@3156ddafd071d79379bf491630828e63aebababf -
Branch / Tag:
refs/tags/0.10.0 - Owner: https://github.com/germanbravolopez
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3156ddafd071d79379bf491630828e63aebababf -
Trigger Event:
push
-
Statement type:
File details
Details for the file hdl_ip_packager-0.10.0-py3-none-any.whl.
File metadata
- Download URL: hdl_ip_packager-0.10.0-py3-none-any.whl
- Upload date:
- Size: 84.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
108f1577d040f3c19532135b8b2f1c0c8bc5cc1cf4405ed5f7ed8ae30d659177
|
|
| MD5 |
1fc4d9aa2257a4a3893ed5ba2c8ff135
|
|
| BLAKE2b-256 |
7a87cc8db360d0f69ae0ea6c23f663259f71608d82f93d5a5c2a1e754cf16176
|
Provenance
The following attestation bundles were made for hdl_ip_packager-0.10.0-py3-none-any.whl:
Publisher:
release.yml on germanbravolopez/hdl-ip-packager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
hdl_ip_packager-0.10.0-py3-none-any.whl -
Subject digest:
108f1577d040f3c19532135b8b2f1c0c8bc5cc1cf4405ed5f7ed8ae30d659177 - Sigstore transparency entry: 1809472048
- Sigstore integration time:
-
Permalink:
germanbravolopez/hdl-ip-packager@3156ddafd071d79379bf491630828e63aebababf -
Branch / Tag:
refs/tags/0.10.0 - Owner: https://github.com/germanbravolopez
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3156ddafd071d79379bf491630828e63aebababf -
Trigger Event:
push
-
Statement type: