Look-up Tables - 2020.2 English

Versal ACAP AI Engine Programming Environment User Guide (UG1076)

Document ID
UG1076
Release Date
2020-11-24
Version
2020.2 English

Static File-scoped Tables

Kernel functions can use private, read-only data structures that are accessed as file-scoped variables. The compiler allocates a limited amount of static heap space for such data. As an example, consider the following header file (user_parameter.h):

#ifndef USER_PARAMETER_H
#define USER_PARAMETER_H

#include <adf.h>

static int32 lutarray[8] = {1,2,3,4,5,6,0,0} ; 

#endif

This header file can be included in the kernel source file and the look-up table can be accessed inside a kernel function directly. The static modifier ensures that the array definition is local to this file. The AI Engine compiler then allocates this array in static heap space for the processor where this kernel is used.

#include "user_parameter.h"
void simple_lut(input_window_cint16 * in, output_window_cint16 * out){
  v4cint32 tmp; 
  v4cacc48 acc;
  v32cint16 coeffs; 

  upd_w(coeffs, 0, lutarray);
  window_readincr(in, tmp);
  acc = mul4(tmp 0, 0x3210, 1, coeffs, 0, 0x0000, 1);
  acc = mac4(acc, tmp, 2, 0x3210, 1, coeffs, 2, 0x0000, 1) ;
  acc = mac4(acc, tmp, 4, 0x3210, 1, coeffs, 4, 0x0000, 1) ;
  window_writeincr(out, srs(acc) ) ;
  }

Global Graph-scoped Tables

While the previous example only includes an eight entry look-up table accessed as a global variable, many other algorithms require much larger look-up tables. Because AI Engine local memory is at a premium, it is much more efficient for the AI Engine compiler to manage the look-up table explicitly for specific kernels than to leave a large amount of stack or heap space on every processor. Such tables should not be declared static in the kernel header file.

#ifndef USER_PARAMETER_H
#define USER_PARAMETER_H

#include <adf.h>

int32 lutarray[8] = {1,2,3,4,5,6,0,0} ; 

#endif

The kernel source continues to include the header file and use the table as before. But, now you must declare this table as extern in the graph class header and use the parameter::array(…) function to create a parameter object explicitly in the graph. You also need to attach this parameter object to the kernel as shown in the following code:

#include <adf.h>
extern int32 lutarray[8];
class simple_lut_graph : public graph {
public:
  kernel k;
  parameter p;

  simple_lut_graph() {
    k = kernel::create(simple);
    p = parameter::array(lutarray);
    connect<>(p,k);
    ...
  }
}

Including this explicit specification of the look-up table in the graph description ensures that the compiler is aware of the requirement to reserve a suitably sized piece of memory for the look-up table when it allocates memory for kernel input and output buffers.

Shared Graph-scoped Tables

Sometimes, the same table definition is used in multiple kernels. Because the AI Engine architecture is a distributed address-space architecture, each processor binary image that executes such a kernel needs to have that table defined in its own local memory. To get the correct graph linkage spread across multiple processors, you must declare the tables as extern within the kernel source file as well as the graph class definition file. Then, the actual table definition needs to be specified in a separate header file that is attached as a property to the kernel as shown below.

#include <adf.h>
extern int32 lutarray[8];
class simple_lut_graph : public adf::graph {
public:
  kernel k;
  parameter p;

  simple_lut_graph() {
    k = kernel::create(simple);
    p = parameter::array(lutarray);
    connect<>(p,k);

    std::vector<std::string> myheaders;
    myheaders.push_back("./user_parameter.h")
    headers(k) = myheaders;
    ...
  }
}

This ensures that the header file that defines the table is included in the final binary link wherever this kernel is used without causing re-definition errors.