Implementing HLS Variants
This guide explains how to implement HLS backend variants for FINN hardware layers.
Overview
HLS variants generate C++ code that calls finn-hlslib template functions. The generated code is synthesized by Vitis HLS to create RTL IP blocks.
Location: src/finn/custom_op/fpgadataflow/hls/<layer>_hls.py
Inherits from: Base layer + HLSBackend
Key responsibility: Populate the code_gen_dict dictionary with code fragments that are assembled into a complete HLS C++ file.
Code Generation Dictionary
The HLS backend infrastructure uses a template-based system. Your HLS variant populates self.code_gen_dict with code fragments that are substituted into templates defined in src/finn/custom_op/fpgadataflow/templates.py.
Templates:
ipgen_template- Used for IP generation (synthesis)docompute_template- Used for cppsim (C++ simulation)docompute_template_timeout- Used for cppsim with freerunning HLS operations
Required Methods
Method |
Template Key |
Purpose |
|---|---|---|
|
|
Add #include directives for finn-hlslib headers |
|
|
Define constants for HLS template parameters (#define statements) |
|
|
Generate main function call to finn-hlslib template |
|
|
Generate function signature with stream parameters |
|
|
Add HLS interface pragmas (axis ports, control protocol) |
Optional Methods
generate_params(model, path)- Generate weight/threshold parameter files (needed for MVAU, VVAU, Thresholding)
Reference implementation: See src/finn/custom_op/fpgadataflow/hls/layernorm_hls.py for a complete example.
HLS-Specific Node Attributes
The HLSBackend base class defines node attributes that control code generation:
cpp_interface
Purpose: Determines how data is packed into HLS streams
Values:
- "packed" (default) - Data packed as ap_uint<width> bit vectors
- "hls_vector" - Data as HLS vector types (hls::vector)
Effect: Influences how $STREAMDECLARATIONS$ and data reading/writing are generated
Recommendation: Use "hls_vector" for new HLS components going forward.
hls_style
Purpose: Determines execution style for HLS operations
Values:
- "ifm_aware" (default) - Kernel knows the input feature map size and when all outputs are computed
- "freerunning" - Free-running execution with timeout-based control
Effect:
- Selects which cppsim template is used (docompute_template vs docompute_template_timeout)
- Changes stream declarations and output handling
Note: These attributes are set during hardware layer conversion (convert_to_hw_layers transformations). When implementing a new HLS layer, you will need to set these attributes appropriately.
Generated Code Structure
The code generation produces a C++ file with this structure:
// From $GLOBALS$
#include "layernorm.hpp"
// From $DEFINES$
#define N 128
#define SIMD 16
// From $BLACKBOXFUNCTION$ and $PRAGMAS$
void LayerNorm_0(
hls::stream<ap_uint<512>> &in0,
hls::stream<ap_uint<512>> &out)
{
#pragma HLS INTERFACE axis port=in0
#pragma HLS INTERFACE axis port=out
#pragma HLS INTERFACE ap_ctrl_none port=return
// From $DOCOMPUTE$
layernorm<N, SIMD>(in0, weights, out);
}
This file is then synthesized by Vitis HLS to create an RTL IP block.
Adding to FINN
After implementing your HLS variant:
Add import to
src/finn/custom_op/fpgadataflow/hls/__init__.pyUpdate SpecializeLayers if needed
Add tests in
tests/fpgadataflow/
See Also
CustomOp Class Hierarchy - CustomOp class hierarchy
Implementing RTL Variants - Implementing RTL variants
SpecializeLayers: HLS vs RTL Selection - HLS vs RTL selection
HDL_STYLE_GUIDE.md - HLS coding conventions
finn-hlslib - HLS template library