突发传输的前置条件和限制 - 2023.2 简体中文

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

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

突发前置条件

突发即汇总连续的存储器访问请求。这些连续突发必须满足下列前置条件才能成功启动突发最优化:

  • 请求必须全部为读取请求或者全部为写入请求 - 不支持突发读取和写入。
  • 必须按单调递增访问顺序(访问的存储器位置和访问时间两方面都是如此)。不能访问位于先前访问的两处存储器位置之间的存储器位置。
  • 必须为连续存储器 - 两个存储器之间按正序彼此相邻且无间隔或重叠。
  • 读取/写入访问的次数(或突发长度)必须可在发出请求之前确定。这意味着即使在运行时计算得到突发长度,仍必须在发出读取/写入请求之前完成计算。
  • 如果将两个阵列绑定到同一个 M-AXI 端口,那么任意给定时间内,任一方向上最多仅针对一个阵列执行突发。
  • 如果在同一区域的同一捆绑的同一通道上进行同向访问,则所有这些访问都不会出现突发。
  • 突发请求的发起时间与完成时间之间不得存在任何依赖关系问题。
提示: volatile 限定符会阻止往来此变量的突发访问。

由于存储器访问重叠导致的外层循环突发失败

在以下示例中外层循环突发推断将失败,因为循环 L1 的迭代 0 和迭代 1 访问的是阵列中的相同元素 a 和 b。突发推断属于全有或全无类型的最优化,即,该工具不会推断部分突发。它是一种贪婪的算法,会尝试最大程度增加突发长度。自动突发推断将尝试以自下而上的方式来推断突发,即从内层向外层,当任一前置条件无法得到满足时即停止。在以下示例中,突发推断发现重复读取元素 8 时就会停止,因此,在此例中,将推断得到内层突发长度为 9。

L1: for (int i = 0; i < 8; ++i)
    L2: for (int j = 0; j < 9; ++j)
            b[i*8 + j] = a[i*8 + j];

itr 0: |0 1 2 3 4 5 6 7 8|
itr 1: |                8 9 10 11 12 13 14 15 16|

使用 ap_int/ap_uint 类型作为循环归纳变量

由于突发推断取决于循环归纳变量和循环次数,因此使用非原生类型可能阻碍最优化的触发。建议循环归纳变量始终使用无符号的整数类型。

必须至少进入循环一次

在某些情况下,编译器可能无法推断出循环归纳变量最大值从不为零 (0),即,它无法证明始终能够进入循环。在此类情况下,断言语句将有助于编译器完成此推断。

assert (N > 0);
L1: for(int a = 0; a < N; ++a) { … }

阵列上的循环间或循环内依赖关系

如果写入阵列位置,然后在同一迭代或下一次迭代内从该位置读取,可能导致最优化难以对此类阵列依赖关系进行解密。基本上,对于此类案例,最优化将失败,因为它无法保证写入将先于读取发生。

针对存储器的有条件访问

如果执行的存储器访问是有条件访问,则可能导致突发推断算法失败,因为它无法通过条件语句来完成推理。在某些情况下,编译器将简化条件语句,甚至将其移除,但一般建议在存储器访问周围避免使用条件语句。

在从循环调用的函数内部执行 M-AXI 访问

跨函数阵列访问分析并不适合编译器转换(如突发推断)。在此类情况下,用户可以使用 INLINE 编译指示或指令来内联函数,以避免突发失败。

void my_function(hls::stream<T> &out_pkt, int *din, int input_idx) {
    T v;
    v.data = din[input_idx];
    out_pkt.write(v);
}

void my_kernel(hls::stream<T> &out_pkt,
               int            *din,
               int            num_512_bytes,
               int            num_times) {
#pragma HLS INTERFACE mode=m_axi port = din offset=slave bundle=gmem0
#pragma HLS INTERFACE mode=axis port=out_pkt
#pragma HLS INTERFACE mode=s_axilite port=din bundle=control
#pragma HLS INTERFACE mode=s_axilite port=num_512_bytes bundle=control
#pragma HLS INTERFACE mode=s_axilite port=num_times bundle=control
#pragma HLS INTERFACE mode=s_axilite port=return bundle=control

unsigned int idx = 0;
L0: for (int i = 0; i < ntimes; ++i) {
    L1: for (int j = 0; j < num_512_bytes; ++j) {
#pragma HLS PIPELINE
        my_function(out_pkt, din, idx++);
    }
}

由于从调用的函数执行存储器访问,因此导致突发推断将会失败。为了正常完成突发推断,建议用户对访问 M-AXI 存储器的任意此类函数执行内联。

在此示例中,突发推断失败的另一个原因是通过 my_function 中的 din 来访问存储器,这是通过变量 (idx) 来定义的,此变量不属于循环归纳变量 ij 的函数,因此可能并非顺序或单调变量。请勿传递 idx,可改用 (i*num_512_bytes+j)

数据流循环上的流水打拍突发推断

在具有 DATAFLOW 编译指示或指令的循环上不支持突发推断。但数据流循环内的每个进程/任务都具有突发。并且,由于任务可以并行执行,因此不支持在数据流区域内共享 M-AXI 端口。