重要: 要在代码中使用
hls::task
对象,请包含头文件 hls_task.h。
hls::task
库提供了一种简单的方法用来对数据驱动的进程进行建模,此方法支持对仅含串流 I/O(hls::stream
或 hls::stream_of_blocks
)的任务进行静态例化,这样就不必检查对 C++ 中的并发进程进行建模所必需的空串流。
GitHub 上的 simple_data_driven 提供了如下所示简单示例:
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);
}
请注意,顶层函数 odds_and_evens
使用串流输入和输出接口。这是纯串流内核。顶层函数包含:
- s1 和 s2 均为线程本地的串流 (
hls_thread_local
),并且均用于连接任务通道任务 t1 和 t2。这些串流需作为线程本地的串流,这样在多次顶层调用之间才能保持处于活动状态。 - t1、t2 和 t3 均为线程本地的
hls::task
,分别用于执行splitter
函数、odds
函数和evens
函数。这些任务无限运行,用于处理其输入串流上的数据。无需同步。
但这类仅限串流的模型存在一些限制,如:
- 您无法访问非本地存储器
- 非串流数据(如标量、阵列和指针)可以作为参数来传入,前提是这些端口已通过 STABLE 编译指示声明为稳定端口。当前含
m_axi
接口的顶层指针只能使用offset=off
选项来传递重要: 由于与hls::task
之间不存在同步,因此标量、阵列和指针的读取时间间隔未知。所以编写代码时,需确保代码不依赖于这些端口的读取时机。 - 您必须按并行任务规范来显式描述设计中的并行度
-
hls::task
必须始终在并行上下文中例化,不得嵌套在顺序上下文中- 如果您的顶层函数中使用
hls::task
,那么该顶层函数就会成为并行上下文 - 由于数据流区域是并行上下文,因此您可在其中例化
hls::task
- 在顺序上下文中,如果您调用函数来例化任务,那么:
- 该函数必须是并行上下文关联,如数据流区域
- 必须由并行上下文中的常规数据流进程来生产和耗用任务输入串流和输出串流
- 控制驱动的 TLP 可位于顶层、位于另一个任务驱动的 TLP 内或者位于顺序区域内
- 数据驱动的 TLP 可位于顶层、位于另一个数据驱动的 TLP 内或者嵌套在两个控制驱动的任务之间
- 控制驱动的 TLP 不得位于流水线内,也不得直接位于数据驱动的 TLP 内。在后一种情况下,它必须位于由数据驱动的任务所执行的顺序区域内
- 数据驱动的 TLP 不得直接位于顺序、流水线或控制驱动的 TLP 内
- 顺序区域或流水线只能位于控制或数据驱动的任务主体(而非控制或数据驱动的区域)内
- 如果您的顶层函数中使用
hls::task
对象可与标准数据流样式的函数调用自由混用,这些函数调用可将数据移入和移出存储器(DRAM 和 BRAM)。任务还支持拆分通道 (hls::split
) 与合并通道 (hls::merge
),拆分通道支持一对多数据分发,用于构建工作程序池来处理数据串流,合并通道则支持多对一数据聚集。