read_lock
和 write_lock
与 while(empty) or
while (full)
循环类似,会持续不断尝试获取资源,直至得到资源为止,因此代码执行将停滞直至获取锁定为止。您可以使用 empty()
和 full()
方法来判定缺少可供获取的可用块是否会导致对 read_lock
或 write_lock
执行的调用因此停滞,如以下示例所示。
#include "hls_streamofblocks.h"
void reader(hls::stream_of_blocks<buf> &in1, hls::stream_of_blocks<buf> &in2, int out[M][N], int c) {
for(unsigned j = 0; j < M;) {
if (!in1.empty()) {
hls::read_lock<ppbuf> arr1(in1);
for(unsigned i = 0; i < N; ++i) {
out[j][i] = arr1[N-1-i];
}
j++;
} else if (!in2.empty()) {
hls::read_lock<ppbuf> arr2(in2);
for(unsigned i = 0; i < N; ++i) {
out[j][i] = arr2[N-1-i];
}
j++;
}
}
}
void writer(hls::stream_of_blocks<buf> &out1, hls::stream_of_blocks<buf> &out2, int in[M][N], int d) {
for(unsigned j = 0; j < M; ++j) {
if (d < 2) {
if (!out1.full()) {
hls::write_lock<ppbuf> arr(out1);
for(unsigned i = 0; i < N; ++i) {
arr[N-1-i] = in[j][i];
}
}
} else {
if (!out2.full()) {
hls::write_lock<ppbuf> arr(out2);
for(unsigned i = 0; i < N; ++i) {
arr[N-1-i] = in[j][i];
}
}
}
}
}
void top(int in[M][N], int out[M][N], int c, int d) {
#pragma HLS dataflow
hls::stream_of_blocks<buf, 3> strm1, strm2; // Depth=3
writer(strm1, strm2, in, d);
reader(strm1, strm2, out, c);
}
生产者进程和使用者进程可在其主体的任一范围内执行以下操作。如各示例所示,范围通常为循环,但这并非强制要求。诸如条件等其他范围也同样受支持。受支持操作包括:
- 获取某个块,例如,一个含任意受支持的数据类型的阵列。
- 对于生产者,此阵列将为空,即,根据底层数据类型的构造函数(如有)进行初始化。
- 对于使用者,此阵列将为满(当然不超过生产者对其填充的量,与对于 PIPO 缓冲器的要求相同,称为按需适用完整写入)。
- 将此块视作等同于本地存储器用于读取和写入,端口数量上限不超过根据
BIND_STORAGE
编译指示或指令所分配的最大端口数,此编译指示或指令是专为块的串流指定的,用于指定每一侧各自可看到哪些端口:- 1 个端口表示每一侧均只能访问 1 个端口,最终块串流可将单个双端口存储器用于实现。
- 2 个端口表示每一侧根据调度可使用 1 个或 2 个端口:
- 如果调度器在至少一侧使用 2 个端口,则将不会发生合并
- 如果调度器使用 1 个端口,则可能发生合并
- 如果不指定该编译指示,则调度器将根据当前用于局部阵列的条件来决定。此外:
- 生产者可对其获取的块执行写入和读取
- 使用者则只能对其获取的块执行读取
- 当块从获取此块的范围内退出时,将自动释放。释放后的块:
- 如果由生产者释放块,则此块可供使用者获取。
- 如果由使用者释放块,则此块可供生产者获取并复用,前提是由构造函数(如有)对此块进行重新初始化。此初始化可能导致设计变慢,因此通常不建议这样做。您可使用 __no_ctor__ 属性(如前文 std::complex 所述)防止为阵列元素调用构造函数。
块串流与 PIPO 缓冲器精神上极为相似。对于 PIPO,获取与调用生产者或使用者进程函数相同,而释放则与从该函数返回相同。这意味着:
- PIPO 的握手符合以下描述
- 在使用者一侧为 ap_start/ap_ready,且
- 在生产者一侧为 ap_done/ap_continue。
- 块串流的握手则符合以下描述
- 在生产者有其自己的 read/empty_n,且
- 在使用者一侧有 write/full_n。