检查满的块和空的块 - 2023.2 简体中文

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

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

read_lockwrite_lockwhile(empty) or while (full) 循环类似,会持续不断尝试获取资源,直至得到资源为止,因此代码执行将停滞直至获取锁定为止。您可以使用 empty()full() 方法来判定缺少可供获取的可用块是否会导致对 read_lockwrite_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。