Dependencies: link cores together for re-use
For a long time, productivity gains in hardware designs have been achieved primarily by re-using existing code, a.k.a. IP blocks. Re-use is also engrained into FuseSoC: re-usable hardware design components are packaged into cores and then used in other designs. In FuseSoC (and many other package managers), re-use is achieved by expressing dependencies between FuseSoC cores.
This section explains how dependencies are specified, how they are resolved by FuseSoC, and how they can be constrained.
A dependency example: DualBlinky
We introduced the basic FuseSoC features by creating a reusable core called Blinky.
To illustrate the concept of dependencies in FuseSoC we employ another example: DualBlinky, the “dual-core” version of Blinky.
Again, all source code is available in the FuseSoC source tree in the tests/userguide/dualblinky
directory.
$ tree tests/userguide/dualblinky
tests/userguide/dualblinky
├── data
│ └── nexys_video.xdc
├── dualblinky.core
└── rtl
└── dualblinky.sv
2 directories, 3 files
The core file is shown in full below.
dualblinky.core
, 2x BlinkyCAPI=2:
name: fusesoc:examples:dualblinky:1.0.0
description: DualBlinky, a FuseSoC example with dependencies
filesets:
rtl:
files:
- rtl/dualblinky.sv
file_type: systemVerilogSource
depend:
- fusesoc:examples:blinky
nexys_video:
files:
- data/nexys_video.xdc:
file_type: xdc
targets:
default: &default
filesets:
- rtl
toplevel: dualblinky
synth:
<<: *default
description: Synthesize the design for a Nexys Video FPGA board
default_tool: vivado
filesets_append:
- nexys_video
tools:
vivado:
part: xc7a200tsbg484-1
parameters:
clk_freq_hz: 100000000
parameters:
clk_freq_hz:
datatype: int
description: Frequency of the board clock, in Hz
paramtype: vlogparam
Specifying a dependency
Dependencies in FuseSoC are expressed between a file set and a core.
They are listed in a core file as in the filesets.FILESET_NAME.depend
section.
The example below shows how to create a dependency between the fusesoc:examples:blinky
core and the rtl
file set of the fusesoc:examples:dualblinky:1.0
core.
# Excerpt of dualblinky.core
# ...
filesets:
# ...
rtl:
# ...
depend:
- ">=fusesoc:examples:blinky:1.0"
Note
YAML requires quotation marks for strings with special characters, as they are used in version constraints.
Both single ('
) and double quotes ("
) can be used.
File ordering
File ordering (compilation order) is important in many hardware design projects. The following rules apply.
Files from dependencies are inserted into the file list before the files in the file set where the dependency is declared.
The order in which dependencies are listed in the
depend
section does not imply any ordering. That is, specifyingdepend: [A, B]
does not guarantee that files from coreA
are included before the ones from coreB
. (If such an order is desired, make coreB
depend onA
.)
What happens if a dependency is specified?
Declaring a dependency includes the dependent core in the build.
More specifically, the following sections specified in the default
target of the dependent core are included:
filesets
: File sets to include.hooks
: A list of hooks to execute.generate
: List of generators.parameters
: List of available parameters.vpi
: List of VPI objects.
Notably not included are the tools
, toplevel
, description
, and default_tool
sections of the default
target.
Also, no target other than default
is considered when including a dependency.
Version constraints
Version constraints specify which version of a dependent core can be used, and which versions are incompatible.
Within a core file, version constraints are expressed by prefixing a core name with a version comparison operator. The following version comparison operators are available.
Operator |
Meaning |
Example |
---|---|---|
|
exactly |
|
|
less (lower) than |
|
|
at most (less than or equal to) |
|
|
at least (greater than or equal to) |
|
|
more (higher) than |
|
|
Caret requirement: any version less than the next major version (see below) |
|
|
Tilde requirement: allow updates to the current version (see below) |
|
Notes:
If no operator is specified, then
=
is assumed. So the=
operator is effectively optional.If no version number is given any version is accepted, i.e.
>= 0.0.0
.
Caret requirements
Caret requirements allow semantic versioning-compatible updates to a specified version. An update is allowed if the new version number does not modify the left-most non-zero digit in the major, minor, patch grouping.
Tilde requirements
Tilde requirements specify a minimal version with some ability to update. If you specify a major, minor, and patch version or only a major and minor version, only patch-level changes are allowed. If you only specify a major version, then minor- and patch-level changes are allowed.
Semantic versioning (SemVer)
A common scenario when declaring dependency is the following: “Core B depends on a version of core A which has the same interface as version 1.0.0, but may contain additional bug fixes in the implementation of that interface.” Version numbers and dependencies alone cannot express this relationship, as they are (by default) meaningless. Equally, tools have a very hard time determining such compatibility accurately. Instead, humans are needed to attach meaning to version numbers, and that’s where semantic versioning comes in.
Semantic versioning is a convention that gives meaning to version numbers. Being a convention, semantic versioning is not enforced by tooling, but relies on cooperation and a shared understanding between authors of reusable IP cores. Effectively, semantic versioning allows authors to encode in the version number information such as “this version breaks API compatibility”, “this version is backwards compatible with a certain previous version”, etc.
A detailed explanation of semantic versioning is available at semver.org. The basics, however, are quickly explained. Semantic versioning expects version numbers with three components, MAJOR, MINOR, and PATCH, such as 1.0.3. With this structure in place, follow these guidelines:
Given a version number MAJOR.MINOR.PATCH, increment the:
MAJOR version when you make incompatible API changes,
MINOR version when you add functionality in a backwards compatible manner, and
PATCH version when you make backwards compatible bug fixes.
Additional labels for pre-release and build metadata are available as extensions to the MAJOR.MINOR.PATCH format.