Example Test Bench - 2021.2 English

Vitis High-Level Synthesis User Guide (UG1399)

Document ID
UG1399
ft:locale
English (United States)
Release Date
2021-12-15
Version
2021.2 English

Xilinx recommends that you separate the top-level function for synthesis from the test bench, and that you use header files. The following code example shows a design in which the top-level function for the HLS project, hier_func, calls two sub-functions:

  • sumsub_func performs addition and subtraction.
  • shift_func performs shift.

The data types are defined in the header file (hier_func.h). The code for the function follows:

#include "hier_func.h"

int sumsub_func(din_t *in1, din_t *in2, dint_t *outSum, dint_t *outSub)
{
 *outSum = *in1 + *in2;
 *outSub = *in1 - *in2;
}

int shift_func(dint_t *in1, dint_t *in2, dout_t *outA, dout_t *outB)
{
 *outA = *in1 >> 1;
 *outB = *in2 >> 2;
}

void hier_func(din_t A, din_t B, dout_t *C, dout_t *D)
{
 dint_t apb, amb;

 sumsub_func(&A,&B,&apb,&amb);
 shift_func(&apb,&amb,C,D);
}

As shown, the top-level function can contain multiple sub-functions. There can only be one top-level function for synthesis. To synthesize multiple functions, group them as sub-functions of a single top-level function.

The header file (hier_func.h), shown below, demonstrates how to use macros and how typedef statements can make the code more portable and readable.

Tip: Arbitrary Precision (AP) Data Types discusses arbitrary precision data types, and how the typedef statement allows the types and therefore the bit-widths of the variables to be refined for both area and performance improvements in the final FPGA implementation.
#ifndef _HIER_FUNC_H_
#define _HIER_FUNC_H_

#include <stdio.h>

#define NUM_TRANS 40

typedef int din_t;
typedef int dint_t;
typedef int dout_t;

void hier_func(din_t A, din_t B, dout_t *C, dout_t *D);

#endif

The header file above includes some #define statements, such as NUM_TRANS, that are not required by the hier_func function, but are provided for the test bench, which also includes the same header file.

The following code defines a test bench for the hier_func design:

#include "hier_func.h"

int main() {
 // Data storage
 int a[NUM_TRANS], b[NUM_TRANS];
 int c_expected[NUM_TRANS], d_expected[NUM_TRANS];
 int c[NUM_TRANS], d[NUM_TRANS];

  //Function data (to/from function)
 int a_actual, b_actual;
 int c_actual, d_actual;

  // Misc
 int retval=0, i, i_trans, tmp;
 FILE *fp;

 // Load input data from files
 fp=fopen(tb_data/inA.dat,r);
 for (i=0; i<NUM_TRANS; i++){
 fscanf(fp, %d, &tmp);
 a[i] = tmp;
 } 
 fclose(fp);

 fp=fopen(tb_data/inB.dat,r);
 for (i=0; i<NUM_TRANS; i++){
 fscanf(fp, %d, &tmp);
 b[i] = tmp;
 } 
 fclose(fp);

 // Execute the function multiple times (multiple transactions)
 for(i_trans=0; i_trans<NUM_TRANS-1; i_trans++){

 //Apply next data values
 a_actual = a[i_trans];
 b_actual = b[i_trans];

  hier_func(a_actual, b_actual, &c_actual, &d_actual);

 //Store outputs
 c[i_trans] = c_actual;
 d[i_trans] = d_actual;
 }

 // Load expected output data from files
 fp=fopen(tb_data/outC.golden.dat,r);
 for (i=0; i<NUM_TRANS; i++){
 fscanf(fp, %d, &tmp);
 c_expected[i] = tmp;
 } 
 fclose(fp);

 fp=fopen(tb_data/outD.golden.dat,r);
 for (i=0; i<NUM_TRANS; i++){
 fscanf(fp, %d, &tmp);
 d_expected[i] = tmp;
 } 
 fclose(fp);

 // Check outputs against expected
 for (i = 0; i < NUM_TRANS-1; ++i) {
 if(c[i] != c_expected[i]){
 retval = 1;
 }
 if(d[i] != d_expected[i]){
 retval = 1;
 }
 }

 // Print Results
 if(retval == 0){
 printf(    *** *** *** *** \n); 
 printf(    Results are good \n); 
 printf(    *** *** *** *** \n); 
 } else {
 printf(    *** *** *** *** \n); 
 printf(    Mismatch: retval=%d \n, retval); 
 printf(    *** *** *** *** \n); 
 }

 // Return 0 if outputs are corre
 return retval;
}