Targets¶
How Booley learns your design. Every Booley Flow builds from a FuseSoC .core
Target, so a .core file is the second mandatory piece of a Project next to
booley.toml. The booley-setup skill writes both; this page
explains what they mean.
Design description (.core)¶
The design itself is described outside booley.toml: how it builds lives in
one or more FuseSoC CAPI2 .core files (CAPI2 reference),
and what to verify in .booley/tests.toml (see Simulation tests).
Is FuseSoC mandatory? Yes: every Booley Flow builds from a resolvable
.coreTarget. A.coreis usually a mechanical restatement of the build you already have (a filelist plus a toplevel); for the one thing it can't express, a per-test non-RTL build step like compiling a test's firmware, declare Pre-sim commands instead of abandoning the flow.
A .core describes how the design builds: source files (filesets), top
modules (toplevel), typed parameters/defines, and build targets:
CAPI=2:
name: ::my_project:0
filesets:
rtl:
files:
- rtl/pkg.sv: {file_type: systemVerilogSource}
- rtl/dut.sv: {file_type: systemVerilogSource}
tb:
files:
- tb/tb_dut.sv: {file_type: systemVerilogSource}
tags: [tb] # required on the testbench fileset
fw: # non-RTL data the TB reads ($readmemh etc.)
files:
- fw/firmware.hex: {file_type: user, copyto: firmware.hex}
parameters:
WIDTH: {datatype: int, paramtype: vlogparam, default: 16}
targets:
sim: # target name == the --target value
flow: sim
flow_options: {tool: verilator} # verilator | icarus
filesets: [rtl, tb, fw]
toplevel: tb_dut # a sim target's toplevel is its TB top
parameters: [WIDTH]
lint:
flow: lint
flow_options: {tool: verilator}
filesets: [rtl]
toplevel: dut
synth:
flow: generic
flow_options: {tool: yosys, arch: xilinx} # arch is edalize plumbing Booley
filesets: [rtl] # ignores; the OpenROAD engine
toplevel: dut # drives its own PDK/target
A few conventions worth calling out in that example:
- Tracing needs no project-owned helper source. The Icarus trace overlay supplies the packaged dump module when the Target does not already provide one, then roots it for the trace run. Existing project-supplied modules remain supported and are reused. Verilator relies on the Target's trace-capable C++ simulation harness and does not need the SystemVerilog dump module.
- A
file_type: userfile withcopyto:stages a non-RTL data file (a$readmemhimage, a vectors file) into the build tree at the name the TB opens. Before the simulator starts, Booley exposes that declared relative path in the configuredrun_cwd; the temporary entry is removed after the run. An identical file already present there is preserved, while a different file at the same path is an input-setup error rather than being overwritten. Doctor warns about differing declared inputs at the Project Root or a literalrun_cwd, naming the Target, source and destination. Rename thecopytodestination, remove the checked-in copy, or choose a literalrun_cwdwithout the collision. Identical files and templatedrun_cwdproduce no collision warning. A missing declared$readmemhdestination is a staging/infrastructure error; an undeclared path is amissing_inputdesign failure with a fileset hint. flow_options.arch(and any other Edalize-only knob) is plumbing Booley passes through to the toolchain. The built-in synth path drives its own PDK/target via the OpenROAD engine and ignoresarch.
Fileset file types¶
Set file_type on a fileset as the default for its files, or on a file entry
to override it. Booley treats untyped files as user.
The built-in Flows consume these types. Spell them exactly as shown:
file_type |
Use for |
|---|---|
verilogSource |
Verilog RTL and testbenches for lint and sim; non-testbench RTL for synth and fpga. |
systemVerilogSource |
SystemVerilog RTL and testbenches for the same Flows. |
vlt |
Verilator control (waiver) files for lint and Target-based Verilator simulation builds. See the Verilator recipe. |
cSource, cppSource |
C/C++ sources of a Verilator simulation harness. |
veribleLintRules |
Verible rules file; select at most one. |
veribleLintWaiver |
Verible waiver files; select any number. See the Verible recipe. |
SDC |
ASIC timing constraints for physical synth; the type is uppercase. See ASIC synthesis. |
xdc |
Vivado FPGA constraints; name the file *.xdc. See FPGA implementation. |
user |
Non-RTL inputs such as firmware, vectors, and cocotb Python modules. copyto: sets the staged path; see the example above and Cocotb Targets. |
- Give headers the same type as their sources and mark them
is_include_file: true. HDL headers (.vh/.svh) then supply include paths instead of compiling separately. - Keep
SDCandxdcfiles outside testbench-tagged filesets. - Use the unversioned names
verilogSourceandsystemVerilogSource. Some consumers accept version-suffixed names, but simulation reuse and coverage helpers match the unversioned names exactly.
Every Flow except fpga keeps declared types; for lint and simulation, FuseSoC
passes them to the selected EDA tool. The fpga Flow rebuilds file types from
filenames and selects RTL by suffix alone, so give FPGA RTL a lowercase .v
or .sv suffix. The xdc type is matched case-insensitively, but a file
keeps it only if its name also has an XDC suffix.
Edalize support alone does not make a type an input to every Booley Flow. These types are recognized only in narrow places:
systemCSource,uhdm: the pinned Edalize Verilator node accepts them, but that alone does not make SystemC or UHDM execution supported; see the EDA-tool matrix.tclSource: mapped from the.tclsuffix, but the built-in synth and FPGA recipes have no general Tcl fileset hook.vhdlSource,vhdlSource-2008: classified as sources by simulation coverage, but Booley has no supported VHDL simulation path.
Target authoring¶
Name project-owned Targets <axis>_<subject> using the Booley Flow axis:
| Axis | Driven by | Example |
|---|---|---|
sim_ |
sim |
sim_smoke |
lint_ |
lint |
lint_style |
synth_ |
synth |
synth_timing |
fpga_ |
fpga |
fpga_board |
The axis is needed because CAPI2 has no synthesis flow: synth and FPGA Targets
can both resolve as generic. A bare axis such as lint is fine when there is
only one Target for it; the Elaboration Check reuses a sim Target. Targets with an asic_ prefix run; booley doctor recommends synth_.
Use default: only when another core depends on this one; Booley does not show
it as a selectable Target. Vendored upstream cores keep their original names.
Renaming a Target also requires updating its tests.toml section, Doctor
metadata references, explicit callers, and Goals. For Python testbenches, see
Cocotb Targets.
Explicit lint Targets require verilator, verible, or veriblelint in
flow_options.tool; unsupported or missing EDA tools are excluded from
booley targets --for lint, and explicit Doctor lint selections report incompatibility.
Verilator lint Target¶
To waive Verilator warnings, declare a .vlt control file with
file_type: vlt and select its fileset on the lint Target. The type must be
exactly vlt: an untyped .vlt file, or a guessed type such as
verilatorConfig, does not apply the waiver.
This core sits beside the rtl/ and lint/ directories; file paths are
relative to the .core file:
CAPI=2:
name: acme:ip:top:1.0
filesets:
rtl:
files: [rtl/top.sv]
file_type: systemVerilogSource
lint_config:
files:
- lint/waivers.vlt: {file_type: vlt}
targets:
lint_structural:
flow: lint
flow_options:
tool: verilator
verilator_options: [-Wall]
filesets: [rtl, lint_config]
toplevel: top
-Wall is optional: it adds lint warnings to Verilator's default set and is
independent of the waiver.
lint/waivers.vlt holds the required control-file directive, then the warning
to waive:
`verilator_config
lint_off -rule WIDTHTRUNC -file "*top.sv"
This waives WIDTHTRUNC in source paths ending in top.sv, including the
FuseSoC build-tree copy; other warning classes stay enabled. Make the pattern
specific to your sources; -lines narrows a waiver further (see Verilator's
control-file reference).
Run inside the Sandbox:
booley flow lint --target lint_structural
- Edalize places
vltfiles before HDL inputs, so file-specific rules apply when those inputs are read. - Add
lint_configto other Verilator Targets only where the same waiver is intended. --mode elab-only-standaloneprobes (see Elaboration Check) use their own HDL-only inputs and ignore Target waiver filesets.
Verible lint Target¶
Use canonical verible for a style Target that selects RTL, its rules, and optional
waivers:
CAPI=2:
name: acme:ip:top:1.0
filesets:
rtl:
files: [rtl/top.sv]
file_type: systemVerilogSource
lint_config:
files:
- lint/rules.cfg: {file_type: veribleLintRules}
- lint/waivers.txt: {file_type: veribleLintWaiver}
targets:
lint_style:
flow: lint
flow_options: {tool: verible}
filesets: [rtl, lint_config]
toplevel: top
These are existing Project source files at paths relative to the .core file,
selected through its filesets. Supply at most one rules file; waiver files are optional.
Cocotb Targets (Python testbenches)¶
A Cocotb Target is an ordinary sim Target whose testbench is a cocotb Python
module instead of HDL. It needs nothing beyond the built-in flow: declare cocotb_module in the
Target's flow options and Booley takes it from there.
filesets:
rtl:
files:
- rtl/counter.sv: {file_type: systemVerilogSource}
tb:
files:
- tb/test_counter.py: {file_type: user, copyto: test_counter.py}
# multi-file TBs: copyto preserves the package layout in the build root
- tb/helpers/util.py: {file_type: user, copyto: helpers/util.py}
tags: [tb] # required, same as an SV testbench fileset
targets:
sim_cocotb:
flow: sim
flow_options:
tool: icarus # icarus | verilator
cocotb_module: test_counter # THIS is what makes it a Cocotb Target
iverilog_options: [-g2012] # SystemVerilog sources need -g2012
timescale: 1ns/1ps
filesets: [rtl, tb]
toplevel: counter # what the Python TB attaches to
# .booley/tests.toml
[sim_cocotb]
tests = ["test_reset", "test_basic"] # the @cocotb.test() FUNCTION names
# no `select` — see below
What differs from an SV Target:
toplevelis whatever the Python testbench attaches to. For a simple design that is the DUT itself: cocotb'sduthandle is the toplevel, and there is no HDL wrapper. But a design whose ports are SystemVerilog interfaces needs one: cocotb's BFMs bind to interface instances (AxiStreamBus.from_entity(dut.s_axis_tx)), and an instance has to be instantiated somewhere. Such projects ship a thin HDL wrapper that declares the interfaces and instantiates the DUT, and the wrapper is thetoplevel(commonly named after the test module, e.g.toplevel: test_taxi_eth_mac_10galongsidecocotb_module: test_taxi_eth_mac_10g). Both shapes work as-is; pointtoplevelat the module the testbench actually drives.tests.tomllists cocotb test-function names, and must not declare aselecttemplate, since selection is an env-var filter Booley builds (COCOTB_TEST_FILTER, orTESTCASEon cocotb 1.x, auto-detected). Aselecton a Cocotb Target is a setup-time error.skipworks unchanged.- Seeds and Test Variants. Booley seeds a Cocotb Target through cocotb's
own seed variable, so
tests.tomlmust not declare aseedtemplate for it, and its Test Variants carryenvonly. Tests that differ in seed or in Variant environment run in separate simulator processes; see Seeds. - No sentinels (see pass/fail sentinels):
a missing or truncated
results.xmlis inconclusive, never a pass. RTL assertion output is still scanned. - One test module per Target. A cocotb Target names exactly one
cocotb_module, so a second test module is a second Target — this is a mechanical consequence of the.core, not a style preference, and it is the rule that wins over any "one Target per intent" advice you may read elsewhere. That advice applies to classic HDL testbenches, where one sentinel-scored TB toplevel usually covers a whole intent and splitting it buys nothing. A project with eight cocotb modules therefore has eight sim Targets. If it also has build variants (a-Dset per hardware flavour), the Targets multiply: variants × modules. That is fine up to a point — the Targets are a handful of.corelines each andbooley targetsstays readable — but it is worth deciding which variants earn a full module sweep rather than generating the whole matrix. A common shape is: every module on the default variant, plus the one or two modules that actually exercise the varying behaviour on the other variants. Goals name Targets explicitly, so an unenumerated combination is simply one nobody gates on. - Tracing goes through a VCD. Booley converts it to FST;
--trace-fstis rejected. See Tracing a Cocotb Target. - Image-provisioned only, Icarus or Verilator. Commercial simulators are out of scope for Cocotb Targets.
- Tests generated by
TestFactory/generate_testsor@cocotb.parametrizeare fine: list the rendered IDs intests.toml(e.g.run_test_tx/ifg=12), not the generating function's name.booley doctordefers checking these names toresults.xmlat run time.
Tracing a Cocotb Target¶
booley flow sim --target <target> --trace works on a Cocotb Target with no
trace setup in the .core. The cocotb run owns a VCD dump: the simulator writes
dump.vcd in the run directory, and after the run Booley converts it to the FST
that B-Wave reads, then removes the raw VCD. A failed conversion keeps the VCD,
as trace.vcd, with its diagnostics in the Target's simulation work directory.
As on any Target, a header-only or otherwise unqueryable waveform is rejected.
Booley drives the dump itself, so [flows.sim].trace_args has no effect here:
- Icarus gets
+trace, which fires Booley's dump module, the same one the trace overlay supplies to an SV Target. - Verilator gets
--trace --trace-file dump.vcd, the flags of the C++ harness cocotb supplies.
What verilator_options may carry on a Verilator Cocotb Target, and what a
traced run then does:
| Trace flag | Result |
|---|---|
| none | Accepted. Booley adds --trace to the traced build. |
--trace or --trace-vcd |
Accepted. This is a VCD trace recipe; Booley keeps the options as written. |
--trace-fst |
Rejected: this is a native FST recipe, and cocotb's harness is driven for a VCD. |
--trace-saif |
Rejected, as on every Verilator Target. |
Booley refuses a traced run of a --trace-fst Cocotb Target with:
Cocotb Target '<target>' requests native FST tracing, but the Cocotb run-half currently owns a VCD dump; use a VCD trace recipe for this Target
Remove --trace-fst, or replace it with --trace, to fix it. A
-DVM_TRACE_FMT_FST define must go with it, since Booley rejects that define
without --trace-fst.
Flat and vendored repos¶
- Flat single-file repos (one
picorv32.vat the root) need nortl/+tb/restructure. Classification comes from the project's mandatory.core(as always): a fileset listing the root-level file (e.g.files: [picorv32.v]) is classified by its exact path rather than a parent directory, so the single file is treated as RTL with no directory layout. TB files use the same fileset mechanism carryingtags: [tb]. - Vendored cores you don't want Booley/FuseSoC to discover (a bundled SoC's
example cores) are quarantined with a
FUSESOC_IGNOREmarker file in the directory. Booley's.corescanner skips any directory carrying one, exactly as FuseSoC's own scanner does. - Recursive symlinks are directory links back to a containing ancestor
(for example
src/a/lib/self -> ../../..). FuseSoC discovery follows directory links, so following these would repeatedly scan the same tree. Doctor reports one failure per resolved ancestor, with a link count and a few paths; runbooley doctor --verbosefor every affected path. PlaceFUSESOC_IGNOREin each link's containing directory, or an enclosing subtree you can exclude: it hides all cores in that subtree, so avoid excluding wanted cores at the link's target. Local untracked markers leave vendored source files unchanged, but nested untracked markers are not copied into Goal or baseline worktrees; commit them if repository changes are acceptable. To keep the vendored repository unchanged across worktrees, use Stealth isolation (enabled = trueandignore_native_cores = true) with authored cores in.booley/cores/; this excludes native discovery entirely while reading the original RTL. - Git submodules work, but Goal Mode does not clone them. See Submodules below.
- Multi-core repos (a FuseSoC-native project shipping tens or hundreds of
.corefiles: ibex has 208, with alinttarget in 54 of them) work with no[fusesoc]config at all. Sharing a Target name across cores (manylints, manysims) is normal FuseSoC, not an error; Booley identifies a Target by its(VLNV, name)pair — VLNV being FuseSoC's Vendor:Library:Name:Version coordinate for a core — and only asks you to disambiguate when a bare name is genuinely contested.
Submodules¶
Goal Mode and baseline-relative Flows run in isolated git worktrees. Booley
does not run git submodule update there: doing so may re-clone from a private
SSH URL that the Sandbox cannot reach.
After the worktree's branch or the Flow's baseline ref is selected, Booley
reads that revision's exact gitlinks and reconstructs each submodule from the
initialized repository at the same path in the main Project. It transfers
the pinned commit's local Git objects into a standalone detached repository;
it does not copy a working tree, share a .git pointer, configure a remote, or
consult the URL in .gitmodules. Recursive submodules use the same rule at
each level. Consequently, SSH-only URLs are harmless once the source hierarchy
is initialized locally.
What this asks of you, once, on the host:
git submodule update --init --recursive
# If .booley is a standalone Git repository:
git -C .booley submodule update --init --recursive
Every selected submodule must be present, clean, and non-shallow in its owning RTL repository or Paired Checkout before Goal entry or a baseline Flow starts. Its local object database must also contain the complete object closure for the destination's pinned commit. Worktree setup hard-errors when one of these preconditions is not met, for example:
submodule <path> not found in source Project; initialize it firstsubmodule <path> is dirtysubmodule <path> is shallow; fetch its complete history firstlocal objects for submodule <path> are incomplete
The destination gitlink is authoritative: if the main Project currently has a newer submodule checkout, Booley still materializes the historical pin. A matching clean destination is accepted on resume. If a later recursive step fails, repositories created by that attempt are rolled back; pre-existing content is never removed.
To copy only some of the submodules (a big docs or FPGA-board submodule nothing builds against), list the ones you want:
[submodules]
paths = ["deps/bus_pkg", "deps/axi_verif"]
When paths is absent, all top-level gitlinks in the selected destination
revision are materialized. An explicit empty list selects none. A non-empty
list is an allowlist intersected with that revision's top-level gitlinks;
anything omitted remains an empty gitlink directory in the worktree. Nested
gitlinks below a selected entry are always reconstructed recursively.
For a separately paired .booley repository, the allowlist above
selects gitlinks in the outer repository. Booley also materializes every
top-level gitlink recorded by the Paired Checkout, recursively and
offline, because that repository can own acceptance controls and build inputs.
Source: docs/user/TARGETS.md on GitHub