流水线中的 PE 对流经的各项传输事务同步执行操作。对于每次 compute()
调用,PE 都将显式启动和停止一次。但当 PE 标记为 FREE_RUNNING
时(如 指南宏 中所述),它将具有如下硬件语义:
- PE 不会根据传输事务或
compute()
调用来启动、停止或复位。它属于 HLS 内核,并具有ap_none
控制接口,如块级控制协议中所述 - 此接口只能包含 AXI4‑Stream 实参或标量输入
- PE 执行数据驱动的操作,并且仅对输入串流码字进行操作,而无法察觉传输事务的有效载荷大小
- 在硬件比特流编程到器件中后,PE 立即开始执行
图 1. 自由运行
上图显示了自由运行的 PE 的图示。此加速器包含 2 个 PE,LdStr
PE 包含全局存储器访问,fsk_incr
则是自由运行的 PE。在 compute()
作用域内,这些 PE 均通过下列两个 AXI4‑Stream 接口来连接:AS
用于将码字从 LdStr
移至 fsk_incr
,XS
则为反馈路径。
此示例的代码如下所示。
class fsk_acc : public VPP_ACC<fsk_acc, NCU>
{
ZERO_COPY(A);
ZERO_COPY(X);
SYS_PORT(A, DDR[0]);
SYS_PORT(X, DDR[0]);
FREE_RUNNING (fsk_incr);
public:
static void compute(DT* A, DT* X, int sz);
static void loadstore(DT* A, DT* X, hls::stream<DT>& AS,
hls::stream<DT>& XS);
static void fsk_incr(hls::stream<DT>& AS, hls::stream<DT>& XS);
};
Void fsk_acc::compute(DT* A, DT* X, int sz)
{
static vpp::stream<DT> AS, XS;
ldst(A, X, sz, AS, XS);
fsk_incr(AS, XS);
}
void fsk_acc::ldst(DT* A, DT* X, int sz, hls::stream<DT>& AS,
hls::stream<DT>& XS)
{
for (int i = 0; i < sz; i++) {
AS.write(A[i]);
}
for (int i = 0; i < sz; i++) {
XS.read(X[i]);
}
}
void fsk_acc::fsk_incr(hls::stream<DT>& AS, hls::stream<DT>& XS)
{
DT val;
AS.read(val);
XS.write(val + 1);
}
LdSt
PE 对 sz
码字执行操作,它可对全局存储器端口 A 和 X 分别执行读取和写入。而自由运行的 PE fsk_incr
则对 sz
不可知,仅对传入 AS
串流上的码字予以响应。
前文所述自由运行的语义能够显著简化自由运行的 PE 的实现,通常这样即可简化 FPGA 的使用和所需的布线资源。它串流流水线设计,其中所含中间 PE 可作为自由运行的 PE,从而仅对输入 AXI4‑Stream 进行操作。
凭借任一流水线组合,只要启用硬件复制(NCU 大于 1),硬件即可包含任意数量的复制流水线,并且 compute()
作业可在可用流水线时隙上运行。这样应用层即可保持简单,并自动运行数据,使其穿过硬件中的多个流水线。