hls::task
objects in your code include the header file
hls_task.h.The hls::task
library provides a simpler
way of modeling purely streaming kernels, allowing static instantiation of tasks with
only streaming I/O (hls::stream
or hls::stream_of_blocks
). This reduces the need for checks
for empty stream needed to model concurrent processes in C++.
The following is a simple example that can be found at simple_data_driven on GitHub:
void odds_and_evens(hls::stream<int> &in, hls::stream<int> &out1, hls::stream<int> &out2) {
hls_thread_local hls::stream<int> s1; // channel connecting t1 and t2
hls_thread_local hls::stream<int> s2; // channel connecting t1 and t3
// t1 infinitely runs splitter, with input in and outputs s1 and s2
hls_thread_local hls::task t1(splitter, in, s1, s2);
// t2 infinitely runs function odds, with input s1 and output out1
hls_thread_local hls::task t2(odds, s1, out1);
// t3 infinitely runs function evens, with input s2 and output
hls_thread_local hls::task t3(evens, s2, out2);
}
Notice the top-level function, odds_and_evens
uses streaming input and output interfaces. This is a
purely streaming kernel. The top-level function includes the following:
- s1 and s2 are thread-local streams (
hls_thread_local
) and are used to connect the task-channel tasks t1 and t2. These streams need to be thread-local so that they can be kept alive across top-level calls. - t1, t2, and t3 are the thread-local
hls::task
that execute the functions (splitter
,odds
, andevens
respectively). The tasks run infinitely and just process data on their input streams. No synchronization is needed.
However, this type of model does have some restrictions such as:
- You cannot access non-local memory
- Non-stream data, such as scalar and array variables, must all be local to the processes and cannot be passed as arguments
- A task cannot be nested inside a sequential region
- You must explicitly describe the parallelism in the design by the specification of parallel tasks
The hls::task
objects can be mixed
freely with standard dataflow-style function calls, which can move data in and out of
memories (DRAM and BRAM). Tasks also support splitting channels (hls::split
) to support one-to-many data distributions to build pools of
workers that process streams of data, and merging channels (hls::merge
) to support many-to-one data aggregation.