Hardware modules and Flows

Every Waveflow design starts from a hardware module — a HwModule: a SimObj with typed ports and a behavior, the single source of truth for a hardware block. The Hardware modules page is the foundation — what a module is, the three things that define it, and the taxonomy of kinds. This index then covers the flows.

A flow is the end-to-end recipe for taking a module from its Python specification to a verified hardware realization — which build steps run, in what order, producing which artifacts, and how the result is checked. There are two, and they split on one axis: the DUT.

The two flows

Sequential (host-activated) — the DUT is a control-driven kernel (ap_ctrl_hs + s_axilite) that the host launches and waits on. Because it has a start/done handshake, Vitis can drive it directly in C-simulation and C/RTL co-simulation, so the testbench is an ordinary sequential int main() (a SeqTB). Toy example throughout: simp_fun (examples/regmap/simp_fun.py). Targets: control_driven_kernel + sequential_vitis_tb.

Concurrent (free-running) — the DUT is a free-running kernel (ap_ctrl_none): one hls::task for a leaf, one per child for a composite, wired by internal channels. It has no start/done handshake, so Vitis co-sim refuses it; verification instead drives the elaborated RTL cycle-by-cycle through an XSI BFM. Toy example throughout: mem_copy (examples/mem_copy/). Targets: composite_kernel + sequential_xsi_tb.

A third path — the full system on the fabric (an FPGA bitstream via Vivado IPI, no testbench, host software drives it) — is future work; it is not one of the two simulation flows above.

Two targets that are not flows

The targets above are per-graph: a DUT plus its testbench. There are two more, and both ask a per-module question:

xsi_bfm_modelcan this one module be realized as a pre-written cycle model beside a top? That is the realization of a module that lies outside the cut, and the peer of composite_kernel (inside it). A module answers it by declaring a bfm_model() hook, just as a module inside the cut declares kernel_task().

rtl_modulecan this one module be realized as hand-written Verilog beside the kernel? The third member of the same family, for the parts of a design that have no expression inside a Vitis kernel at all — a memory shared by two concurrent accessors is the worked case. A module answers it by declaring an rtl_module() hook naming a .v that already exists.

Neither is a row in the table, because the cut is a property of the build, not of the class: the same module is inside the DUT in one synthesis, hand-written RTL beside it in another, and a testbench model in a third, with nothing about the module changed. See Hardware modules for that axis, and check(mod, "<target>") for the per-module answer.

See also


Table of contents

  • Hardware modules - The foundation both flows build on. A HwModule is a SimObj with typed ports and a behavior — the single source of truth for a hardware block, both the model you simulate and the source for a generated kernel. It is defined by three things: its endpoints, how it is wired, and what it does. The taxonomy then sorts the kinds and hands off to a flow: a plain HwModule is a simulation-only model, HostActivated maps to the sequential flow, and FreeRunMod (a leaf, or a composite of sub-components) maps to the concurrent flow. Kind is only one of three axes: kind is how the body is invoked (a class fact), hooks are how it is realized (kernel_task / bfm_model), and the cut decides which hook applies (a build choice).
  • Sequential (host-activated) - Flow 1 — a control-driven (ap_ctrl_hs + s_axilite) kernel the host launches and waits on, driven by a sequential Vitis testbench Vitis runs in C-simulation and C/RTL co-simulation. The concept here; the full worked walkthrough is the regmap example.
  • Concurrent (free-running) - Flow 2 — a free-running (ap_ctrl_none) kernel or composite Vitis cannot co-simulate, verified by driving the elaborated RTL cycle-by-cycle through a concurrent XSI BFM. The concept here; the full worked walkthrough is the mem_copy example.
  • Full system, on hardware - Flow 4 — the assembled multi-block system taken all the way to a bitstream: blocks exported as IP, wired in Vivado IPI, and run on the FPGA with the host CPU as the driver. No simulation. Future work on the RFSoC bring-up path.
  • Parameterization - Parameterizing a HwModule (both flows). The family is one axis — when does the value bind — with four points: HwConst at class definition, HwParam at build (so distinct values mean distinct artifacts), DynParam at init/pre-sim, and a regmap register at runtime. The last two share one artifact across every value. param_supports declares a set of HwParam values to emit as kernel variants; its C++ realization is comp_codegen/templating.