Parameter Inference
If an integer scalar value appears in the formal arguments of a kernel
function, then that parameter becomes a run-time parameter. In the following
example, the argument select
is a run-time
parameter.
#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
void simple_param(input_window_cint16 * in, output_window_cint16 * out, int select);
#endif
int8, int16, int32, int64, uint8, uint16, uint32, uint64,
cint16, cint32, float, cfloat
.filter_with_array_param
function.#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
void filter_with_array_param(input_window_cint16 * in, output_window_cint16 * out, const int32 (&coefficients)[32]);
#endif
Implicit ports are inferred for each parameter in the function argument, including the array parameters. The following table describes the type of port inferred for each function argument.
Formal Parameter | Port Class |
---|---|
T | Input |
const T | Input |
T & | Inout |
const T & | Input |
const T (&)[ …] | Input |
T(&)[…] | Inout |
From the table, you can see that when the AI Engine cannot make externally visible changes to the function parameter, an input port is inferred. When the formal parameter is passed by value, a copy is made, so changes to that copy are not externally visible. When a parameter is passed with a const qualifier, the parameter cannot be written, so these are also treated as input ports.
When the AI Engine kernel is passed a parameter reference and it is able to modify it, an inout port is inferred and can be used to pass parameters between AI Engine kernels or to allow reading back of results from the control processor.
graph::read()
. The
inout port cannot be updated by
graph::update()
.arg
list, once as an input and once as an inout, for example,
kernel_function(int32 foo_in, int32
&foo_out)
.Parameter Hookup
Both input and inout run-time parameter ports can be
connected to corresponding hierarchical ports in their enclosing graph. This is the
mechanism that parameters are exposed for run-time modification. In the following
graph, an instance is created of the previously defined simple_param
kernel. This kernel has two input ports and one output
port. The first argument to appear in the argument list, in[0]
, is
an input window. The second argument is an output window. The third argument is a
run-time parameter (it is not a window or stream type) and is inferred as an input
parameter, in[1]
, because it is passed by value.
In the following graph definition, a simple_param
kernel is instantiated and windows are
connected to in[0]
and out[0]
(the input and
output windows of the kernel). The run-time parameter is connected to the graph
input port, select_value
.
class parameterGraph : public graph {
private:
kernel first;
public:
input_port select_value;
input_port in;
output_port out;
parameterGraph() {
first = kernel::create(simple_param);
connect< window <32> >(in, first.in[0]);
connect< window <32> >(first.out[0], out);
connect<parameter>(select_value, first.in[1]);
}
};
An array parameter can be hooked up in the same way. The compiler automatically allocates space for the array data so that it is accessible from the processor where this kernel gets mapped.
class arrayParameterGraph : public graph {
private:
kernel first;
public:
input_port coeffs;
input_port in;
output_port out;
arrayParameterGraph() {
first = kernel::create(filter_with_array_param);
connect< window <32> >(in, first.in[0]);
connect< window <32> >(first.out[0], out);
connect<parameter>(coeffs, first.in[1]);
}
};
Input Parameter Synchronization
The default behavior for input run-time parameters ports is triggering behavior. This means that the parameter plays a part in the rules that determine when a kernel could fire. In this graph example, the kernel only fires when three conditions are met:
- A valid window of 32 bytes of input data is available
- An empty window of 32 bytes is available for the output data
- A write to the input parameter takes place
In triggering mode, a single write to the input parameter allows the kernel to fire once, setting the input parameter value on every individual kernel call.
There is an alternative mode to allow input kernels parameters to be set asynchronously. To specify that parameters update asynchronously, use the async modifier when connecting a port.
connect<parameter>(param_port, async(first.in[1]));
When a kernel port is designated as asynchronous, it no longer plays a role in the firing rules for the kernel. When the parameter is written once, the value is observed in subsequent firings. At any time, the PS can write a new value for the run-time parameter. That value is observed on the next and any subsequent kernel firing.
Inout Parameter Synchronization
The default behavior for inout run-time parameters ports is asynchronous behavior. This means that the parameter can be read back by the controlling processor or another kernel, but the producer kernel execution is not affected. For synchronous behavior from the inout parameter where the kernel blocks until the parameter value is read out on each invocation of the kernel, you can use a sync modifier when connecting the inout port to the enclosing graph as follows.
connect<parameter>(sync(first.out[1]), param_port);