HLS 流传输库hls::stream

流传输数据是一种数据传输形式, 其中数据样本从第一个样本开始按顺序发送。流传输不需要地址管理。

Vivado HLS 提供了 C++ 模板类 hls::stream<>, 用于对流传输数据结构进行建模。使用 hls::stream<> 类实现的流传输具有如下属性。

  • 在 C 语言代码中, hls::stream<> 的行为与无限深度的 FIFO 相似。无需定义 hls::stream<> 的大小。
  • 按顺序对其执行读取和写入。即, 从 hls::stream<> 读取数据之后, 无法再次对其进行读取。
  • 顶层接口上的 hls::stream<> 默认情况下使用 ap_fifo 接口来实现。
  • 设计内部的 hls::stream<> 作为 FIFO 来实现, 深度为 2。最优化指令 STREAM 用于更改此默认大小

注释: hls::stream 类应始终在函数之间作为 C++ 参考实参来进行传递。例如, &my_stream。
重要提示! hls::stream 类仅限在 C++ 设计中使用。不支持流传输数组。

C 语言建模与 RTL 实现

数据流在软件中(以及在测试激励文件中进行 RTL 协同仿真期间) 作为无限队列来建模。在 C++ 中对数据流进行仿真
无需满足任意深度。数据流可在函数内部使用, 也可在函数接口上使用。内部数据流可作为函数参数来进行传递。
数据流仅限在基于 C++ 的设计中才能使用。每个 hls::stream<> 对象都仅限单一进程写入, 且仅限单一进程读取。
如果在顶层接口上使用 hls::stream, 默认在 RTL 中会将其作为 FIFO 接口 (ap_fifo) 来实现, 但可选择将其作为
握手接口 (ap_hs) 或作为 AXI-Stream 接口 (axis) 来实现。
如果在设计函数中使用 hls::stream 并将其综合到硬件中, 那么会将其作为 FIFO 来实现, 且默认深度为 2。在某些
情况下(如使用内插时), 可能必须增大 FIFO 深度以确保 FIFO 可保存硬件生成的所有元素。如果无法确保 FIFO 大
小足以保存硬件生成的所有数据样本, 则可能导致设计停滞(可见于 C/RTL 协同仿真和硬件实现中) 。 FIFO 深度可使
用 STREAM 指令搭配 depth 选项来进行调整。在示例设计 hls_stream 中提供了相关示例。

重要提示! 确保 hls::stream 变量在默认非 DATAFLOW 区域内使用时的大小设置正确。

如果使用 hls::stream 在不同任务(子函数或循环) 间传输数据, 应考量在不同任务间存在数据流传输的DATAFLOW 区域内实现这些任务。默认(非 DATAFLOW) 行为是完成每一项任务后再开始下一项任务, 在此情况下必须确保用于实现 hls::stream 变量的 FIFO 大小足以保存生产者任务所生成的所有数据采样。不增大
hls::stream 变量的大小会导致如下错误:

ERROR: [XFORM 203-733] An internal stream xxxx.xxxx.V.user.V' with default size is used in a non-dataflow region, 
which may result in deadlock. Please consider to resize the stream using the directive 'set_directive_stream' or the 'HLS stream' pragma

此错误表示在非 DATAFLOW 区域内(默认 FIFO 深度为 2), 其大小可能不足以保存生产者任务写入 FIFO 的所有数
据采样。

全局和局部流传输

流传输可定义为局部或全局。局部流传输始终作为内部 FIFO 来实现。全局流传输可作为内部 FIFO 或端口来实现:

  • 仅读取或者仅写入的全局定义的流传输推断为顶层 RTL 块的外部端口。
  • 支持读取和写入的全局定义的流传输(位于顶层函数下的层级内) 作为内部 FIFO 来实现。

全局作用域内定义的流传输遵循的规则与任何其它全局变量相同

Using HLS Streams

使用 hls::stream<> 对象, 包含 hls_stream.h 头文件。数据对象流传输是通过指定类型和变量名称来定义的。

给定指定为 hls::stream 的流传输的情况下, 类型 T 可以是:

  • 任何 C++ 原生数据类型
  • Vivado HLS 任意精度类型(例如, ap_int<> 或 ap_ufixed<>)
  • 用户定义的结构体struct(包含以上任意类型)

注释: 用户定义的通用类(或结构), 所含方法(成员函数) 不应用作为流传输变量的类型 (T)
Note: General user-defined classes (or structures) that contain methods (member functions) should not be used as the type (T) for a stream variable

stream命名为可选操作,给定以下 2 个流传输的情况下:
stream bytestr_in1;
stream bytestr_in2(“input_stream2”);

命名的好处:容易在警告信息中定位问题。
有关流传输中剩余元素的任何警告均按如下方式报告, 其中明确标明与 bytetr_in2 的消息:

WARNING: Hls::stream 'hls::stream.1' contains leftover data,which may result in RTL simulation hanging.
WARNING: Hls::stream 'input_stream2' contains leftover data, which may result in RTL simulation hanging.

函数传入或传出的流传输必须按引用传递, 如以下示例所示:

void stream_function (
	hls::stream<uint8_t> &strm_out,
	hls::stream<uint8_t> &strm_in,
	uint16_t strm_len
)

Vivado HLS 支持阻塞访问方式和非阻塞访问方式。

  • 非阻塞访问只能作为 FIFO 接口来实现。
  • 作为 ap_fifo 端口实现并以 AXI4-Stream 资源定义的流传输端口不得使用非阻塞访问。

阻塞读写/Blocking Reads and Writes

对hls::stream<> 对象的基本访问为阻塞读取/写入。这些访问是使用类方法完成的。这些方法会停止(阻止) 对空的流传输 FIFO 的读操作以及对满的流传输 FIFO 的写操作, 直至相应的ap_hs 接口协议的流传输完成握手为止。

Blocking Write Methods

// Usage of void write(const T & wdata)
hls::stream my_stream;
int src_var = 42;
my_stream.write(src_var); // or my_stream << src_var;

“<<”运算符已重载, 可按相似方式应用于 C++ 流传输(例如, iostreams 和 filestreams) 的流插入运算符。要写入的
hls::stream<> 对象将作为左侧实参提供, 而变量则作为右侧实参写入。

Blocking Read Methods

// Usage of void read(T &rdata)
hls::stream my_stream;
int dst_var;
my_stream.read(dst_var); //or int dst_var = my_stream.read(); //or my_stream >> dst_var;
或者, 通过将流传输赋值(例如, 使用 = 或 +=) 给左侧对象, 可读取流传输中的下一个对象。
或者,“>>”运算符已重载, 支持以相似方式用于 C++ 流传输(例如, iostreams 和 filestreams) 的流提取运算符。

Non-Blocking Reads and Writes

非阻塞读写方法同样可用。这些方法允许在空的流传输上尝试读取或者尝试写入已满的流传输时仍可继续执行。
这些方法会返回布尔值以指示访问状态(true 表示成功, false 表示失败) 。此外还包含其它方法用于测试
hls::stream<> 流传输的状态。
重要提示! 仅在使用 ap_fifo 协议的接口上支持非阻塞方法。更确切地说, AXI-Stream 标准和赛灵思 ap_hs I/O 协议不支持非阻塞访问。
在 C 语言仿真期间, 流传输大小无限。因此, 无法对 C 语言仿真验证流传输是否已满。只能在 RTL 仿真期间已定义
FIFO 大小(默认大小 1。或者通过 STREAM 指令定义的任意大小) 的情况下验证这些方法。
重要提示! 如果设计指定为使用块级 I/O 协议 ap_ctrl_none 且设计包含采用非阻塞行为的任意 hls::stream 变量, 则无法保证完成 C/RTL 协同仿真。

Non-Blocking Writes

此方法会尝试将 src_var 变量推送到 my_stream 流传输, 如果成功, 则返回布尔值 true。否则返回 false, 队列
不受影响。
// Usage of void write_nb(const T & wdata)
hls::stream my_stream;
int src_var = 42;
if (my_stream.write_nb(src_var)) {
// Perform standard operations

} else {
// Write did not occur
return;
}

Fullness Test

bool full(void)
仅当 hls::stream<> 对象已满时返回 true。
// Usage of bool full(void)
hls::stream my_stream;
int src_var = 42;
bool stream_full;
stream_full = my_stream.full();

Non-Blocking Read

bool read_nb(T & rdata)
此方法会尝试从流传输中读取值, 成功则返回 true。否则返回 false, 队列不受影响。

Emptiness Test

bool empty(void)
如果 hls::stream<> 为空, 则返回 true。
// Usage of bool empty(void)
hls::stream my_stream;
int dst_var;
bool stream_empty;
stream_empty = my_stream.empty();

控制 RTL FIFO 深度

对于使用流传输数据的大部分设计, 默认 RTL FIFO 深度 2 足矣。每次仅处理一个采样的流传输数据。
对于执行实现所需的 FIFO 深度大于 2 的多速率设计, 必须判定(并使用 STREAM 指令设置) 完成 RTL 仿真所需的深
度。如果 FIFO 深度不足, RTL 协同仿真将停滞。
由于在 GUI 指令窗格内无法查看流传输对象, 因此在该窗格内无法直接应用 STREAM 指令。
对于包含已声明的 hls::stream<> 对象(或者实参列表中已使用或包含该对象) 的函数, 右键单击并执行以下操作:
• 选中 STREAM 指令。
• 使用流传输变量的名称手动填充 variable 字段。
或者可以:
• 在 directives.tcl 文件中手动指定 STREAM 指令, 或
• 在 source 中添加该指令作为编译指示。

C/RTL 协同仿真支持

Vivado HLS C/RTL 协同仿真功能不支持在顶层接口中包含 hls::stream<> 成员的结构或类。 Vivado HLS 支持对这些结构或类进行综合。(不能协仿,但可综合)

typedef struct {
hls::stream<uint8_t> a;
hls::stream<uint16_t> b;
} strm_strct_t;
void dut_top(strm_strct_t indata, strm_strct_t outdata) { }

上述限制适用于顶层函数实参和全局声明的对象。如果使用流传输结构体进行综合, 则必须使用外部 RTL 仿真器和用户创建的 HDL 测试激励文件来验证设计。对于仅含内部链接的 hls::stream<> 对象并无此类限制。

知识点

  • 重要提示! hls::stream 类仅限在 C++ 设计中使用。不支持流传输数组。

  • Vivado HLS C/RTL 协同仿真功能不支持在顶层接口中包含 hls::stream<> 成员的结构或类。 Vivado HLS 支持对这些结构或类进行综合。(不能协仿,但可综合)

  • 重要提示! 仅在使用 ap_fifo 协议的接口上支持非阻塞方法。更确切地说, AXI-Stream 标准和赛灵思 ap_hs I/O 协议不支持非阻塞访问。

  • 重要提示! 如果设计指定为使用块级 I/O 协议 ap_ctrl_none 且设计包含采用非阻塞行为的任意 hls::stream 变量, 则无法保证完成 C/RTL 协同仿真。

你可能感兴趣的:(hls,stream)