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 arguments select
and result_out
are run-time parameters.
#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
void simple_param(input_buffer<int32> &in, output_buffer<int32> &out, int32 select, int32 &result_out);
#endif
int8
, int16
, int32
, int64
, uint8
, uint16
,
uint32
, uint64
, cint16
, cint32
, float
.filter_with_array_param
function.#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
void filter_with_array_param(input_buffer<cint16> & in, output_buffer<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 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, one
output
port and one inout
port. The first
argument to appear in the argument list, in[0]
, is
an input buffer. The second argument is an output buffer. The third argument is a
run-time parameter (it is not a buffer or stream type) and is inferred as an input
parameter, in[1]
, because it is passed by value.
The fourth argument is a run-time parameter and is inferred as an
inout
parameter, inout[0]
,
because it is passed by reference.
in
, out
, and inout
for a kernel belong to their own categories, and
all start from index 0 in the graph.In the following graph definition, a simple_param
kernel is instantiated and buffers are
connected to in[0]
and out[0]
(the input and output buffers of the kernel). The input
run-time parameter is connected to the graph input
port, select_value
, and the inout
run-time
parameter is connected to the graph inout
port, result_out
.
class parameterGraph : public graph {
private:
kernel first;
public:
input_port select_value;
input_port in;
output_port out;
inout_port result_out;
parameterGraph() {
first = kernel::create(simple_param);
connect(in, first.in[0]);
connect(first.out[0], out);
connect<parameter>(select_value, first.in[1]);//default sync rtp input
connect<parameter>(first.inout[0], result_out);//default async rtp output
}
};
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(in, first.in[0]);
connect(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 execute. In this graph example, the kernel only executes when three conditions are met:
- A valid buffer of 32 bytes of input data is available
- An empty buffer 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 execute 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.inout[1]), param_port);