When using the Vitis HLS design flow, it is time consuming to synthesize an improperly coded C function and then analyze the implementation details to determine why the function does not perform as expected. Therefore, the first step in high-level synthesis should be to validate that the C function is correct, before generating RTL code, by performing simulation using a well written test bench. Writing a good test bench can greatly increase your productivity, as C functions execute in orders of magnitude faster than RTL simulations. Using C to develop and validate the algorithm before synthesis is much faster than developing and debugging RTL code.
Vitis HLS uses the test bench to compile and execute the C simulation. During the compilation process, you can select the Launch Debugger option to open a full C-debug environment, which enables you to more closely analyze the C simulation. Vitis HLS also uses the test bench to verify the RTL output of synthesis as described in C/RTL Co-Simulation in Vitis HLS.
The test bench includes the main()
function,
as well as any needed sub-functions that are not in the hierarchy of the top-level function
designated for synthesis by Vitis HLS. The main
function verifies that the top-level function for synthesis is correct by providing stimuli
and calling the function for synthesis, and by consuming and validating its output.
The following code shows the important features of a self-checking test bench, as an example:
int main () {
//Esablish an initial return value. 0 = success
int ret=0;
// Call any preliminary functions required to prepare input for the test.
…
…// Call the top-level function multiple times, passing input stimuli as needed.
for(i=0; i<NUM_TRANS; i++){
top_func(input, output);
}
// Capture the output results of the function, write to a file
…
// Compare the results of the function against expected results
ret = system("diff --brief -w output.dat output.golden.dat");
if (ret != 0) {
printf("Test failed !!!\n");
ret=1;
} else {
printf("Test passed !\n");
}
…
return ret;
}
The test bench should execute the top-level function for multiple transactions, allowing many different data values to be applied and verified. The test bench is only as good as the variety of tests it performs. In addition, your test bench must provide multiple transactions if you want to calculate II during RTL simulation as described in C/RTL Co-Simulation in Vitis HLS.
This self-checking test bench compares the results of the function, output.dat, against known good results in output.golden.dat. This is just one example of a self-checking test bench. There are many ways to validate your top-level function, and you must code your test bench as appropriate to your code.
In the Vitis HLS design flow, the return value of function
main()
indicates the following:
- Zero: Results are correct.
- Non-zero value: Results are incorrect.
The test bench can return any non-zero value. A complex test bench can return different values depending on the type of failure. If the test bench returns a non-zero value after C simulation or C/RTL co-simulation, Vitis HLS reports an error and simulation fails.
main()
function, it is recommended that you constrain the return
value to an 8-bit range for portability and safety.Of course, the results of simulation are only as good as the test bench you provide. You are responsible for ensuring that the test bench returns the correct result. If the test bench returns zero, Vitis HLS indicates that the simulation has passed, regardless of what occurred during simulation.