Writing core files
A core is described in a core description file, or core file.
Core files are written in YAML syntax and follow the FuseSoC’s own CAPI (version 2) schema, which describes the structure of core files (e.g. which keys and values are allowed where). Don’t worry: using FuseSoC neither requires a full understanding of YAML, nor an up-front knowledge of CAPI. However, some key facts about YAML are important.
Things one should know about YAML
Whitespace matters (as in Python): indentation is used to group settings together to form a hierarchy. The exact amount of whitespace used for indentation does not matter; typically two or four spaces are used.
Think of a YAML file as a hierarchical, typed data structure. There are lists, dictionaries (key/value pairs), integers, strings, etc.
YAML syntax provides multiple ways to describe the same structure. It does not matter to FuseSoC which syntax variant is used. For example, a list of items can be written in the following two, semantically identical ways.
[ "item1", "item2" ]
is semantically identical to
- "item1" - "item2"
The same is true for dictionaries (key/value pairs).
{ key1: "value1", key2: "value2" }
is semantically identical to
key1: "value1" key2: "value2"
In most cases, the longer (second) form is preferred, as it is easier to make changes while keeping the diff easy to read.
We recommend always adding double quotes around strings used as value. In most cases, strings in YAML do not need to be surrounded by (single or double) quotation marks. However, the exact rules when quoting is needed are not easily summarized without in-depth understanding of the YAML language syntax. At minimum quotation marks are required when strings start with an exclamation mark. Always using double quotes avoids having to even think about the exact rules.
Examples:
recommended: "quotedvalue" required: "!tool_verilator (something)" possible: unquotedvalue
For a quick introduction into most of YAML’s features have a look at Learn YAML in Y minutes. The full YAML 1.2 specification is available at yaml.org (it’s not an easy read, though).
An example: the blinky core
The following sections explain how to add FuseSoC support to a hardware project.
The code is taken from an example design in the FuseSoC source tree in the tests/userguide/blinky directory.
The design consists of two SystemVerilog files, a testbench, a Xilinx constraint file (with pin mappings for a Nexys Video FPGA board), and finally, the FuseSoC core file.
$ tree tests/userguide/blinky/
tests/userguide/blinky/
├── blinky.core
├── data
│ └── nexys_video.xdc
├── rtl
│ ├── blinky.sv
│ └── macros.svh
└── tb
└── blinky_tb.sv
3 directories, 5 files
To get started, here’s the full blinky.core file.
The following sections will refer back to this example to discuss it in detail.
blinky.core, an exemplary core file 1CAPI=2:
2name: fusesoc:examples:blinky:1.0.0
3description: Blinky, a FuseSoC example core
4
5filesets:
6 rtl:
7 files:
8 - rtl/blinky.sv
9 - rtl/macros.svh:
10 is_include_file: true
11 file_type: systemVerilogSource
12
13 tb:
14 files:
15 - tb/blinky_tb.sv
16 file_type: systemVerilogSource
17
18 nexys_video:
19 files:
20 # YAML short form, see rtl/macros.svh above for the longer form.
21 - data/nexys_video.xdc: {file_type: xdc}
22
23targets:
24 # The "default" target is special in FuseSoC and used in dependencies.
25 # The "&default" is a YAML anchor referenced later.
26 default: &default
27 filesets:
28 - rtl
29 toplevel: blinky
30 parameters:
31 - clk_freq_hz
32
33 # The "sim" target simulates the design. (It could have any name.)
34 sim:
35 # Copy all key/value pairs from the "default" target.
36 <<: *default
37 description: Simulate the design
38 default_tool: icarus
39 filesets_append:
40 - tb
41 toplevel: blinky_tb
42 tools:
43 icarus:
44 iverilog_options:
45 - -g2012 # Use SystemVerilog-2012
46 modelsim:
47 vlog_options:
48 - -timescale=1ns/1ns
49 parameters:
50 - pulses=10
51
52 # The "synth" target synthesizes the design. (It could have any name.)
53 synth:
54 <<: *default
55 description: Synthesize the design for a Nexys Video FPGA board
56 default_tool: vivado
57 filesets_append:
58 - nexys_video
59 tools:
60 vivado:
61 part: xc7a200tsbg484-1
62 parameters:
63 - clk_freq_hz=100000000
64
65parameters:
66 clk_freq_hz:
67 datatype : int
68 description : Frequency of the board clock, in Hz
69 paramtype : vlogparam
70 pulses:
71 datatype : int
72 description : Number of pulses to run in testbench
73 paramtype : vlogparam
Naming the core file
The core file can have any name, but it must end in .core.
It is recommended to choose a file name matching the core name, as discussed below.
The first line: CAPI=2
A core file always starts with the line CAPI=2.
No other content (including comments) is allowed before this line, as FuseSoC uses this line to differentiate between different versions of the CAPI schema.
Only CAPI version 2 is specified at the moment.
The core name, version, and description
Each core has a name, given in the name key.
Core names can be freely chosen, but need to follow a common structure called VLNV.
VLNV stands the four parts of a core name, which are separated by colon (:): Vendor, Library, Name, and Version.
Version numbers should be three numbers in the form major.minor.patch and follow semantic versioning (SemVer).
Cores can also have a description, given in the description key.
A description is optional, but recommended.
name: fusesoc:examples:blinky:1.0.0
description: Blinky, a FuseSoC example core
In this example, the vendor is fusesoc, the library is examples, and the name of the core is blinky.
The version is set to 1.0.0.
Specifying source files
A core typically consists of one or multiple source files.
Source files are grouped into file sets under the filesets key.
FuseSoC does neither mandate a specific grouping, nor naming of file sets. It is common to use one file set for RTL (design) files, and one for testbench files.
The following example shows a single file set, rtl, with a set of common keys.
filesets:
rtl:
files:
- rtl/blinky.sv
- rtl/macros.svh:
is_include_file: true
file_type: systemVerilogSource
For each named file set, several keys are supported:
files: An ordered list of source files. The list of source files is ordered: the files will be passed to the tool in exactly the given order. This is important, for example, in SystemVerilog, where packages need to be compiled before they can be used by subsequent source files.file_type: The default file type for all files in thefileslist.depend: Dependencies on other cores. Dependencies are explained in depth at Dependencies: link cores together for re-use.
Source files
For local cores, source files are resolved relative to the location of the core file and must be stored in the same directory as the core file, or in a subdirectory of it. For remote cores, file names are typically relative to the repository or archive root.
Source file names cannot be absolute paths, or start with ../.
Optionally, source files can have attributes; the file macros.svh is an example of that.
When specifying attributes, end the file name with a colon (:), and specify attributes as key-value pairs below it.
(Alternatively, the equivalent short form syntax can be used, e.g. macros.svh: {is_include_file: true}.)
The most common attributes are:
is_include_file: The file is an include file. In Verilog and C/C++, this means the file is not passed to the tool directly, but instead the file is included by another source file. FuseSoC ensures that the tool finds the include file, e.g. by passing an appropriate include path to the tool.file_type: Override the default file type of the fileset for this particular file.
Refer to the CAPI2 reference documentation for more details.
File types
A file type describes the type of source file. FuseSoC does not use this information itself, but passes it on to tool backends which then configure the tool appropriately depending on the file type encountered.
Commonly used file types are:
verilogSource: Verilog source code, up to Verilog-2001. Files ending in.vor.vhshould use this type.systemVerilogSource: SystemVerilog source code (design and test code). Files ending in.svor.svhshould use this type.vhdlSource: VHDL source code. Files ending in.vhdor.vhdlshould use this file type.
Refer to the CAPI2 reference documentation for more details.
Targets
A target can be seen as something you would like to do with the source code in the core: synthesize it, simulate it, lint it.
Targets are specified as dictionaries under the targets top-level key.
targets:
# The "default" target is special in FuseSoC and used in dependencies.
# The "&default" is a YAML anchor referenced later.
default: &default
filesets:
- rtl
toplevel: blinky
parameters:
- clk_freq_hz
# The "sim" target simulates the design. (It could have any name.)
sim:
# Copy all key/value pairs from the "default" target.
<<: *default
description: Simulate the design
default_tool: icarus
filesets_append:
- tb
toplevel: blinky_tb
tools:
icarus:
iverilog_options:
- -g2012 # Use SystemVerilog-2012
modelsim:
vlog_options:
- -timescale=1ns/1ns
parameters:
- pulses=10
# The "synth" target synthesizes the design. (It could have any name.)
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
The blinky example shown above defines three targets: the default target, a sim target to simulate the design, and a synth target to synthesize it.
Many designs also define a lint target to run static analysis jobs.
The sim and synth targets are optional and could have had any name.
The default target is special and required.
Within a target
Within each target block multiple keys determine what the target does. The most common keys are:
filesets: An ordered list of file sets (source files) included in the target.description(optional): A description of the target.toplevel(optional): The name of the design toplevel. (For advanced scenarios it is possible to specify a list of multiple toplevels instead of just a single one.)default_tool(optional): The default tool to be used to build the target. The tool can also be set or overridden through a FuseSoC command-line argument.tools(optional): Tool-specific settings, grouped by tool name.parameters(optional): Parameters (Verilog parameters and defines, VHDL generics, etc.) to be passed to the design, or forced to a certain value.
The filesets_append key is part of an inheritance schema and explained further in section Inheritance and the default target.
The default target
The default target is the only required target.
It serves two purposes:
The
defaulttarget is used if no other target is explicitly selected when running FuseSoC.The contents of the
defaulttarget are used if the core is used as dependency (described in detail in Dependencies: link cores together for re-use).
All reusable code in the core should go into the default target: RTL files, lint waivers, reusable constraints, etc.
Inheritance and the default target
Importantly, other targets in the same core do not inherit the contents of the default target automatically.
To achieve such inheritance behavior, FuseSoC provides a flexible inheritance mechanism, based on YAML anchors/references, YAML << merge operator, and a FuseSoC-specific list append feature.
The blinky.core shows the recommended template to inherit configuration between targets.
Add
&defaultafter thedefault:text. This defines a YAML anchor nameddefault, which can be referenced later in the file.Add a line
<<: *defaultto the target where you want to inherit fromdefault(thesimandsynthtargets in the example code). This line will effectively “copy over” all configuration under thedefaulttarget.
As always with inheritance the interesting questions are around overriding behavior.
Settings (keys) given in the target which inherits from
defaultoverride the keys indefault. For example, thetoplevelkey in thesimtarget is overridden to betb. Note that no merging of setting data structure is performed.For settings which are lists, for example the
filesetskey, FuseSoC provides a way to combine lists by adding_appendto the name of the key.This behavior is best explained by example. The
filesetslist in thedefaulttarget consists of a single item,rtl. Thesimtarget wants to append the itemtb(a file set with testbench files) to the list. To do so, it specifies the specialfilesets_appendkey with a partial list. When evaluating the core file, FuseSoC appends the contents ofsim.fileset_appendat the end ofdefault.filesetto form a list with two items:rtl, andtb. The same behavior works for all lists in core files.
Signed core files
The fusesoc cli tool can produce a cryptographic signature for a core (currenly using ssh keys). The signature is stored in a separate file named as the signed core but with .sig appended.
For verifying signatures a file called a trustfile is used. The trustfile contains a list of trusted users and their public ssh keys, one per line:
$ cat tests/signature_files/trustfiles/trustfile_1ed_and_2
user1@example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFL1l8BB+EnUUkwMtMMqv1HFw8q2SZ7ERuGcFYt8VtqL
user2@example.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP6w+b8Xue0Jo/Hskd9B+Ttr6g9rq99qbZV/01q+FgED
The location of this file can be set in the [main] section of the fusesoc.conf file:
[main]
ssh-trustfile = /home/anders/FuseSoCdb/git/fusesoc/tests/signature_files/trustfiles/trustfile
This setting can also be overridden using the command line option --ssh-trustfile.
The signing process is accomplished with the core sign command, which takes a core name and a path to a private ssh key as arguments.
The signing status of a core is shown in the core list and core show commands in the fusesoc cli tool.