任务与数据流 - 2023.2 简体中文

Vitis 高层次综合用户指南 (UG1399)

Document ID
UG1399
Release Date
2023-12-18
Version
2023.2 简体中文

hls::task 还支持对数据流区域内部的任务进行定义。数据流区域允许将访问外部阵列的进程定义映射到来自设计层级上层的 M_AXI、标量或 PIPO 实参。这就需要由 #pragma HLS dataflow 语句来识别数据流进程,通过 ap_ctrl_chain 来同步这些进程,并由这些进程从任何非串流 C++ 数据结构读取数据,将数据作为 hls::streamhls::stream_of_blocks 通道进行输出,以便连接到 hls::tasks。随后,任务即可输出由其他数据流进程所读取的串流,并将这些串流写入 M_AXI、标量或 PIPO 实参。必须先由进程或区域生成任务的输入串流,然后声明这些任务,最后由这些进程或区域耗用其输出串流。

重要: 由于 hls::task 对象无法读取或写入 M_AXI、标量或 PIPO 实参,数据流进程必须在 hls::tasks 上读取或写入这些接口并写入或读取串流通道,如下所示。

以下示例演示了任务与数据流进程的结合。顶层函数 (top-func) 是数据流区域,可定义顺序函数 write_out()read_in(),以及 hls::task 对象和 hls::stream 通道。

#include "hls_task.h"

// This is an I/O dataflow process
void write_out(int* out, int n, hls::stream<int> &s2) {
  for (int i=0; i<n; i++)
    out[i] = s2.read();
}
// This is an I/O dataflow process
void read_in(int* in, int n, hls::stream<int> &s1) {
  for (int i=0; i<n; i++)
    s1.write(in[i]);
}
// This is an hls::task body
void func1(hls::stream<int> &s1, hls::stream<int> &s3) {
  // No while(1) needed! This will be a task
  s3.write(... + s1.read());
}
// This is an hls::task body
void func2(hls::stream<int> &s3, hls::stream<int> &s2) {
  // No while(1) needed! This will be a task
  s2.write(... * s3.read());
}
// This could legally be at the top of the design hierarchy
void top-func(int *in, int *out, int n) {
#pragma HLS dataflow
  hls_thread_local hls::stream<int> sk3;
  hls_thread_local hls::stream<int> sk1;
  hls_thread_local hls::stream<int> sk2;
 
  read_in(in, n, sk1);                              // can access stream, scalar or array; calling order matters
  hls_thread_local hls::task t2(func2, sk3, sk2);   // can access only stream; instance order does not matter
  hls_thread_local hls::task t1(func1, sk1, sk3);   // can access only stream; instance order does not matter
  write_out(out, n, sk2);                           // can access stream, scalar or array; calling order matters
}

#pragma HLS DATAFLOW 对于这两个顺序函数而言是必需的,但 hls::task 对象并不需要它。Vitis HLS 将在内部自动拆分 top-func,包括将常规数据流进程和 hls::task 进程拆分为两个数据流区域:

  1. 其中一个数据流区域使用的是 ap_ctrl_chain,其中包含常规数据流进程(如 read_in()write_out())且顺序与这些进程在 C++ 代码中显示的顺序相同,此外还包含对以下 ap_ctrl_none 区域的调用
  2. 另一个数据流区域使用的是 ap_ctrl_none,其中包含任务与通道。

因此,您可在 Vitis HLS GUI 的“Dataflow Viewer”(数据流查看器)中看到两个层次构成的层级。