Templating
This page is the C++ realization of component parameterization; the concept — what HwParam and
HwConst are, and when to use each — is Hardware Components: Parameterization.
Here we cover how each lowers into generated code.
How HwParam lowers
A HwParam value reaches codegen as an HwParamValue — an
int that also carries its .param_name. That dual nature lets the generator emit either a literal
value or a template-parameter name, depending on where it lands:
- Top kernel signature → literal width. Top-level kernels are emitted concrete (no
template <int …>block) so Vitis HLS can attach the AXI interfaces. The port widths inkernel_signature(comp)come from_stream_template_arg, which always emits the literal integer from the endpoint’sbitwidth(the variant’sHwParamValue). So aStreamIFMasterdeclared without_bw = 64becomeshls::stream<streamutils::axi4s_word<64>>&in the top. .tpphook → template parameter. A hook that takes a stream argument is emitted as a templated function (hook_signature(method, template_params=…)names its stream argsWORD_BWand so on), so its stub is written to a.tpprather than a.cpp—HlsCodegenStepselects the extension per hook. Emitting it as.tppkeeps the template definition visible through the generated header’s include path while the impl file stays sticky across rebuilds.poly’sevaluatehook takess_in/m_out, so it lands inpoly_evaluate_impl.tpp; a hook with no stream argument (likesimp_fun’scompute) is concrete and lands in a plain.cpp.
The decision between the two is HwParamValue.param_name: SynthContext.cpp_param(name) returns the
template-parameter name for a HwParam field, or repr(value) for a plain literal.
How HwConst lowers
A HwConst is a class-level structural constant intended to
emit as a C++ static constexpr T name = value;. This emission is currently deferred (a follow-up
phase): discover_hw_const(cls) already surfaces the fields, but the generator does not yet write the
static constexpr lines. In the meantime a HwConst shapes generated structure indirectly through the
Python values it feeds (e.g. a static array extent that sizes a buffer).
param_supports — emitting variant kernels
param_supports turns one component class into multiple
concrete kernel tops. Each variant key maps to a dict of HwParam overrides; codegen emits
<cpp_kernel_name>_<key> for each, alongside the default <cpp_kernel_name>.
@dataclass
class VarKernel(FreeRunMod):
cpp_kernel_name: ClassVar[str | None] = "var_kernel"
param_supports: ClassVar[dict] = {"bw64": {"in_bw": 64}, "bw128": {"in_bw": 128}}
in_bw: HwParam[int] = 32
# ... endpoints declared with bitwidth=self.in_bw
emits var_kernel, var_kernel_bw64, and var_kernel_bw128 — three concrete tops whose ports carry
literal widths of 32, 64 and 128, with no template <...> block on any of them.
No example in this repo uses
param_supports, so there is no worked reference to read — the snippet above is synthetic (though its output is verified). Where you have seen per-configuration kernels, as inexamples/vmac’sgen/ob8_q0_o0_m16/tree, they come from a different mechanism: the build script generates a source set per configuration rather than one class declaring its variants. Reach forparam_supportswhen one component should ship several fixed-width tops; reach for a build sweep when the configurations differ by more thanHwParamvalues.
The mechanism: _iter_variants(comp_class) first validates with
validate_param_supports, then yields the default variant
(suffix "") followed by one instance per param_supports entry — each built through the normal
__init__ with the overrides applied (no immutability bypass). For each, kernel_signature(comp,
variant_suffix=key) names the top <base>_<key> and fills its ports with that instance’s concrete
HwParamValue widths. This is the path to take when hardware integration needs concrete top
signatures rather than a single templated top.
API
HwParamValue— int +.param_name; drives template-name-vs-literal.kernel_signature(comp, variant_suffix="")— the concrete top signature; appends_<suffix>for variants.param_supports/validate_param_supports— the variant map and its validation.HlsCodegenStep— selects.cppvs.tppper hook.
Quick reference
- Top kernels are concrete —
HwParambecomes a literal width in the signature. - Hand-written hooks are templated —
HwParambecomes a.tpptemplate parameter. HwConst→static constexpris deferred; the value still shapes structure via Python.param_supports→ one concrete<kernel>_<key>top per variant (default always emitted).- The concept and the
HwParamvsHwConstframing are in Parameterization.