Dependency Resolver Documentation
Script: routertl_core/dependency_resolver.py
(backward-compat proxy at tools/project-manager/dependency_resolver.py)
1. Overview
The dependency_resolver.py is a critical component of the RouteRTL SDK. Its primary purpose is to scan VHDL and Verilog source files, identify their dependencies, and produce a sorted compilation list that satisfies VHDL's strict "compile-before-use" requirement.
It powers:
- Smart Linting (
make linting): To lint only necessary files in correct order. - Non-Project Mode (
make npm-synthesis): To generate independent checkpoints. - Hierarchy Visualization (
make hierarchy): To print dependency trees.
2. Algorithm: Topological Sort
The resolver uses a Post-Order Traversal (Depth-First Search) to generate a valid topological sort of the dependency graph.
Why Post-Order?
A simpler "DFS Pre-Order + Reversal" approach fails when shared dependencies are visited early in the traversal but needed by multiple parents.
Correct Logic (Implemented):
- Initialize empty
pathslist. - For a given node (Entity/Module):
a. Recursively resolve all its Children (Dependencies).
b. Append the Current Node to the
pathslist. - Deduplicate the list keeping the last occurrence (though with true Post-Order, strict set-based deduplication is usually sufficient if cycles are handled).
Result: A list where every dependency appears before the file that uses it.
3. Parsing Strategy
The resolver uses Regex to parse files without a full compiler frontend. This is fast but relies on coding conventions.
VHDL Detection
| Feature | Regex Pattern | Notes |
|---|---|---|
| Entity Definition | (?i)entity\s+(\w+)\s+is | Case-insensitive. |
| Package Definition | (?i)package\s+(\w+)\s+is | Excludes package body. |
| Entity Instantiation | (?P<label>\w+)\s*:\s*entity\s+(?P<lib>\w+)\.(?P<ent>\w+) | Matches direct entity usage: label : entity lib.name. |
| Package Usage | use\s+(?P<lib>\w+)\.(?P<pkg>\w+)_pkg\.all | Expects package names to end in _pkg. |
Verilog/SV Detection
| Feature | Regex Pattern | Notes |
|---|---|---|
| Module Definition | ^\s*module\s+(\w+)\b | |
| Include | `include\s+\"([^\"]+)\" | Treated as a file dependency (header). |
| Import | import\s+(\w+):: | Unit/Package import. |
4. Hierarchy Detection (Forest)
The resolver can operate in Forest Mode (--forest), where it:
- Builds a full graph of all discovered units.
- Identifies Roots (Nodes with In-Degree 0).
- Treats these Roots as "Top Level Candidates".
- Generates independent dependency trees for each Root.
This allows make linting to process multiple independent IP blocks or top-level modules in a single pass without redundant compilation.
5. CLI Reference
python3 dependency_resolver.py [OPTIONS]
| Flag | Description |
|---|---|
--src DIR [DIR ...] | Source directories to scan (default: src, sim, ip if they exist) |
--top ENTITY | Top-level entity/module name |
--list | Print space-separated ordered file list for --top |
--list-libs | Print space-separated lib:path pairs for --top |
--forest | Output JSON with full project forest (trees + orphans) |
--sort-pkgs | Print topologically sorted list of all packages |
--discover-libs | Auto-discover VHDL library memberships from source (JSON) |
Path Output Contract
All output paths are absolute (resolved via Path.resolve()). This
applies to --list, --list-libs, --sort-pkgs, and --forest JSON.
Consumers that store relative paths (e.g. libraries.json) must normalize
before comparing against resolver output. See
LINTER_PIPELINE.md §3 for details.
6. Integration with Smart Linter
The resolver is invoked by smart_linter.py in three places:
--sort-pkgs— Phase 1 (PRECOMPILE): produces a topologically sorted list of all packages. Custom library files are filtered out before PRECOMPILE.--discover-libs— Phase 1.5 (COMPILE_LIB): auto-discovers which files belong to custom libraries by scanningentity lib.Xinstantiations anduse lib.pkg.allclauses. Results are merged with any manual entries fromlibraries.json. Library membership is fully automatic — no manual configuration required.--top --list— Phase 1.5 also uses this per-entity to get the correct compilation order within each library. Phase 2 (LINT) uses the file list for each hierarchy.
See LINTER_PIPELINE.md for the full pipeline reference.
Auto-Discovery Algorithm
discover_libraries() scans two internal maps:
entity_lib_map— populated fromentity lib.Xinstantiationspackage_lib_map— populated fromuse lib.pkg.allclauses
For each non-standard library found (ieee, std, work, unisim,
unimacro are excluded), it looks up the file path via entity_map
or package_map and returns {lib_name: [absolute_file_paths]}.
7. Known Limitations
- VHDL Component Instantiation: The resolver primarily looks for
entity work.nameorentity lib.name. Old-stylecomponentdeclaration + instantiation is not fully tracked unless the component name exactly matches the entity name (heuristic map). - Generates: Dependencies inside complex
generatestatements are usually found (regex parses everything), but conditional inclusion is not evaluated. The resolver assumes all potential dependencies are needed. - Package Naming: VHDL packages are matched by the broad pattern
use <lib>.<name>.all. The resolver checks both<name>and<name>_pkgagainstpackage_map, supporting both the_pkgsuffix convention and prefix conventions (e.g. Open Logic'solo_base_pkg_math).