Function Instantiation - 2023.2 English

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
UG1399
Release Date
2023-12-18
Version
2023.2 English

Function instantiation is an optimization technique that has the area benefits of maintaining the function hierarchy but provides an additional powerful option: performing targeted local optimizations on specific instances of a function. This can simplify the control logic around the function call and potentially improve latency and throughput.

The FUNCTION_INSTANTIATE pragma or directive exploits the fact that some inputs to a function may be a constant value when the function is called and uses this to both simplify the surrounding control structures and produce smaller more optimized function blocks. This is best explained by example as shown in the following code.

char func(char inval, char incr) {
#pragma HLS INLINE OFF
#pragma HLS FUNCTION_INSTANTIATE variable=incr
 return inval + incr;
}
 
void top(char inval1, char inval2, char inval3,
 char *outval1, char *outval2, char *outval3)
{
 *outval1 = func(inval1,   0);
 *outval2 = func(inval2,   1);
 *outval3 = func(inval3, 100);
}
Tip: The Vitis HLS tool automatically decomposes (or inlines) small functions into higher-level calling functions. Using the INLINE pragma with the OFF option can be used to prevent this automatic inlining.

It is clear that function func has been written to perform three exclusive operations (depending on the value of incr). Each instance of function func is implemented in an identical manner. While this is great for function reuse and area optimization, it also means that the control logic inside the function must be more complex to account for the two exclusive operations. Refer to Vitis-HLS-Introductory-Examples/Pipelining/Functions/function_instantiate for the full version of this example.

The FUNCTION_INSTANTIATE optimization allows each instance to be independently optimized, reducing the functionality and area. After FUNCTION_INSTANTIATE optimization, the code above can effectively be transformed to have two separate functions, each optimized for different possible values of mode, as shown:

void func1() {
  // code segment 1
}

void func2() {
  // code segment 2
}

If the function is used at different levels of hierarchy such that function sharing is difficult without extensive inlining or code modifications, function instantiation can provide the best means of improving area: many small locally optimized copies are better than many large copies that cannot be shared.