Dependencies¶
astli provides built-in dependency management designed specifically for hardware designs. It fetches packages from Git repositories or local directories, resolves compatible versions, and records exact snapshots in an astli.lock lockfile.
[dependencies]
common_cells = { git = "https://github.com/pulp-platform/common_cells.git", version = "1.38" }
idma = { git = "https://github.com/pulp-platform/iDMA.git", rev = "main" }
tech = { path = "../tech" }
Specifying dependency sources¶
gitwithversion: Resolves semver requirements (e.g.^1.38or1.38.0) by selecting the highest matchingvX.Y.Ztag available in the remote repository.gitwithrev: Pins to an explicit Git branch, tag name, or commit SHA.path: Points to a local directory relative to the current package root. Ideal for monorepos or in-tree development.
Each dependency is treated as an independent astli package. If a dependency includes its own astli.toml, its source boundaries, features, and transitives are respected; otherwise, standard default conventions apply. Code quality findings (linting and checks) originating from third-party dependencies are ignored by default.
Version resolution and pinning¶
astli requires a unified dependency graph: exactly one version of any given package is selected for the entire design.
If two dependencies require mutually incompatible versions of the same package, astli raises a version conflict error.
Resolving version conflicts with pinned
If two upstream packages specify conflicting requirements, the root package can force a specific version using [resolve] pinned:
[resolve]
pinned = ["common_cells"]
When a package is pinned, the root's requirement takes absolute precedence. astli will select the root package's chosen version and downgrade conflicting upstream requirements to warnings rather than fatal errors.
Lockfiles (astli.lock)¶
When dependencies are resolved, the exact commits and revisions chosen are written to astli.lock next to your astli.toml.
# Re-resolve all dependencies to latest compatible versions and update the lockfile
astli update
# Update only a single specific dependency
astli update common_cells
Commit your lockfile
Always check astli.lock into source control. Future builds will reproduce the exact same commit revisions across machines without re-querying Git remotes.
Visualizing the dependency tree¶
Run astli tree to inspect the resolved dependency hierarchy and see exactly which packages pull in each dependency:
$ astli tree
soc
├── axi 0.39.6
│ └── common_cells 1.37.0
└── common_cells 1.37.0
- Revisions: Packages tracked by branch or commit hash display their short Git commit SHA instead of a version tag.
- Local & patched checkouts: Dependencies resolved from a local
pathor activeastli patchdisplay their source directory path. - Deduplication: When a shared package appears multiple times in the tree, its downstream dependencies are listed only upon its first occurrence and marked with
(*)on subsequent occurrences to keep output concise. - Described packages: Upstream dependencies configured via
[described.NAME](see below) are annotated with(described).
Enforcing lockfiles in CI¶
In automated CI environments, you want to guarantee that dependencies are never updated implicitly. Pass the --locked flag to any command:
astli check --locked --top soc_top
If astli.lock is missing or out of sync with astli.toml, astli exits with an error instead of modifying the lockfile.
The shared cache¶
To save disk space and network bandwidth, astli downloads and checks out dependencies into a single shared cache on your machine, accessible across all your local projects.
# Pre-fetch all dependencies into cache (useful for offline work or container caching)
astli fetch
# Print the resolved filesystem path of a dependency
astli path common_cells
# Show the location of the local cache directory
astli cache dir
# Clear the cached repositories
astli cache clean
The default cache directory is located at:
- Linux:
$XDG_CACHE_HOME/astli(typically~/.cache/astli) - macOS:
~/Library/Caches/astli - Windows:
%LOCALAPPDATA%\astli
You can customize this location by setting the ASTLI_CACHE_DIR environment variable.
Read-only cache protection
Cached dependencies are marked read-only to prevent accidental edits from leaking across projects. Never manually modify files inside the cache; if you need to alter or customize a dependency's module, use overrides in your project, or work on it in a clone with astli patch.
Describing unmanifested dependencies ([described])¶
When consuming a third-party dependency that does not yet have an astli.toml, astli falls back to sensible package defaults: all files in the repository are scanned as sources, and headers are exported exclusively from the include/ directory.
However, many real-world IPs bundle conflicting files for different EDA flows side-by-side (for example, FPGA vs. ASIC primitives), or locate their header files in non-standard directories. Without a manifest, this can cause duplicate symbol collisions or missing include files.
To support unmanifested upstream repositories cleanly, you can provide temporary package descriptions in your root astli.toml using [described.NAME]:
[described.tech_cells_generic]
exclude = ["src/fpga/**"]
[described.idma]
include-dirs = ["src/include", "target/rtl/include"]
A [described.NAME] block accepts the same sources, include-dirs, and exclude keys that the dependency would declare in its own [package] manifest table, evaluated relative to the dependency's root directory. Because descriptions only filter file discovery and include paths, they never alter version resolution or lockfile checksums.
Guardrails for temporary descriptions¶
[described] is intended as a temporary stopgap while upstream packages adopt astli. To prevent stale configurations from lingering silently, astli provides clear guardrails:
- Visibility: Every run that resolves dependencies reports which packages are currently described (suppressed with
-q), andastli treetags them with(described). - Automatic upstream handoff: As soon as the upstream repository adds its own
astli.toml,astliautomatically uses the upstream manifest and issues a warning prompting you to remove the redundant[described]block. - Validation:
astliemits warnings if you describe a package that is not part of your design, or if anexcludepattern does not match any files in the dependency checkout. - Root-only scope: Only descriptions in your root project's
astli.tomlare evaluated. Downstream libraries cannot impose file descriptions on parent designs.
Upstreaming a manifest
To resolve the issue permanently for the entire community, propose an astli.toml upstream in the dependency's repository. You can use astli patch to test your [package] configuration in a local checkout before submitting a pull request.
Working on a dependency in place¶
When iterating on hardware IP alongside a top-level SoC or testbench, you often need to make and test edits in a dependency without publishing premature Git tags or making temporary edits to your manifest.
astli patch (aliased as astli clone) lets you temporarily override any dependency with a local working directory:
# Clone common_cells at its locked commit into .astli/patch/common_cells and redirect builds to it
astli patch common_cells
# Or point the dependency at an existing local checkout (or clone it there if the path doesn't exist)
astli patch common_cells ../common_cells
# Remove the patch and revert to the locked remote dependency (your local clone is preserved)
astli patch --remove common_cells
How local patches work¶
When a dependency is patched:
- Instant offline clone: If the target directory does not exist,
astliclones it instantly from your local cache mirror at the exact commit pinned inastli.lock. The clone's Gitoriginis automatically configured to the upstream repository URL, ready for committing and pushing. - Transitive discovery: The patched directory's own
astli.tomlorBender.ymlis parsed in place, so any new dependencies or headers you introduce in the IP block are resolved seamlessly into the top-level design. - Zero manifest pollution: Patches are stored locally in
.astli/patch.toml. The.astlidirectory automatically ignores itself via an internal.gitignore, guaranteeing that local development overrides are never accidentally committed to source control. - CI safety: While a patch is active,
astli.lockis treated as strictly read-only to preserve reproducible states for team members. In addition,--lockedandastli updatefail with an error to prevent accidental CI or production builds against uncommitted local patches.
Freezing changes with astli snapshot¶
Once you have tested your modifications in the patched repository, committed them, and pushed them to your remote Git branch, use astli snapshot to update your root project:
astli snapshot common_cells
astli snapshot performs the following automated steps:
- Validates Git state: Checks that the patched repository has no uncommitted changes and verifies that the current
HEADcommit has been pushed to the remote repository so collaborators can fetch it. - Updates
astli.toml: Replaces the dependency's existing version requirement inastli.tomlwith an exactrev = "<commit-hash>"pointing to the new commit. - Pins when necessary: If other transitive packages depend on version tags for this IP,
astli snapshotautomatically adds the package to[resolve] pinned, ensuring your exact commit takes precedence without causing version conflict errors. - Removes the patch: Cleans up the local override in
.astli/patch.tomland updatesastli.lockto the new commit, leaving your local cloned directory untouched on disk.
Migrating Bender projects
If your root project's dependencies come from its Bender.yml, because it has no astli.toml or one without a [dependencies] table, astli snapshot changes nothing. It prints the commit to require; write it into your Bender.yml by hand, then drop the patch with astli patch --remove.