Vivado HLS 提供了 C++ 模板类 hls::stream<>
Vivado HLS 提供了 C++ 模板类 hls::stream<>,用于对流传输数据结构进行建模。
数据流在软件中(以及在测试激励文件中进行 RTL 协同仿真期间)作为无限队列来建模。在 C++ 中对数据流进行仿真
无需满足任意深度。数据流可在函数内部使用,也可在函数接口上使用。内部数据流可作为函数参数来进行传递。
hls::stream数据流在软件中作为无限队列来建模,以及在测试激励文件中进行RTL协同仿真也是作为无限队列来建模。
一、在软件中的hls::stream数据流
1.这里的软件,包括main函数中的测试激励文件,也包括在rtl协同仿真中,测试激励转为的rtl。
不管你是纯C的前仿真,还是C/RTL协同仿真,Testbench(C/C++侧)里的 hls::stream 都被当作一个容量无限的软件队列。它绝不会满,写操作永不阻塞。
这个你在写激励的时候应该有体会,你声明的hls::stream变量,可以容纳一张图像,在你没有指定hls::stream的depth的情况下,可以容纳一个
任意分辨率大小的图像,说明了这个就是一个无限深度的软件队列。
在 C 仿真时,你不用担心测试激励中stream流深度限制,因为它无限大。
C 仿真和协同仿真中,testbench 是软件代码,hls::stream 类在软件侧实现为一个 std::queue 等容器,具有无限容量,从而简化了测试。
2.协同仿真中,如何看待hls::stream
协同仿真中,待测试的DUT设计是内部真实的RTL逻辑,但是在TB中以及TB和DUT之间的接口一侧仍然是
软件流模型。在TB中,你可以一次性往流中灌入一张完整的图像,你不需要担心
DUT有没有准备好接收。
在C仿真阶段,你完全不需要预先定义流的深度,也不需要写代码去处理“流满了怎么办”。因为它是无限深,任何深度需求都能“自动满足”。
这让你可以先聚焦算法逻辑,不用考虑缓冲区的物理限制。
第一层:软件层面设计,抽象为软件无限队列
class stream
{
protected:
std::string _name;
std::deque<__STREAM_T__> _data; // container for the elements
};
hls::stream在c代码层面,可以看出:
hls::stream 在 hls_stream.h 里实际上是对 std::queue 或类似容器的封装,天然具有无限容量。这样做的目的是让软件工程师能用最符合直觉的方式写测试:先产生数据再启动模块,或者边产边消但不必关注满/空条件。
第二层:硬件层面设计,综合指令接管,具体的指定
hls::stream在c代码中被综合后,不再是std::queue,
如果是接口类型,就会是ap_fifo接口,ap_hs接口,或者axi4-stream接口。
如果是内部容器,就会被综合为FIFO,综合指令(例如 #pragma HLS STREAM variable=src_stm depth=4)被替换成特定深度的实际FIFO。指令指定的深度,才决定了硬件里真正的缓冲区大小.
三、软件层面 & 硬件层面的冲突
1.单线程测试平台 vs 并行硬件:HLS 的 C 协同仿真是单线程的。当你调用顶层函数时,程序会停在函数入口,等待它返回。但如果函数里有带握手的 stream 接口,硬件会等着外部给它数据才动。可在单线程仿真里,给 stream 写数据的代码得在函数调用之后才能执行。这就形成了典型的死锁:函数等数据,程序等函数结束去写数据,两者永远等不到对方
2.无“真实”的异步随机握手:ap_hs 或 AXI4-Stream 这类接口需要 valid 和 ready 信号随时按真实世界的节奏变化。但在 C 仿真里,这种“随时”是不存在的,所有操作都按代码顺序一步步来。“随机握手”意味着要同时监测两边状态,这在顺序的 C 模型里模拟不了,所以工具干脆不支持这种强时序验证
四、总结
综上,hls::stream是没法在hls设计中完成随机握手的C仿真和C/RTL协同仿真。
