突发前置条件
突发即汇总连续的存储器访问请求。这些连续突发必须满足下列前置条件才能成功启动突发最优化:
- 请求必须全部为读取请求或者全部为写入请求 - 不支持突发读取和写入。
- 必须按单调递增访问顺序(访问的存储器位置和访问时间两方面都是如此)。不能访问位于先前访问的两处存储器位置之间的存储器位置。
- 必须为连续存储器 - 两个存储器之间按正序彼此相邻且无间隔或重叠。
- 读取/写入访问的次数(或突发长度)必须可在发出请求之前确定。这意味着即使在运行时计算得到突发长度,仍必须在发出读取/写入请求之前完成计算。
- 如果将两个阵列绑定到同一个 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
) 来定义的,此变量不属于循环归纳变量 i
和 j
的函数,因此可能并非顺序或单调变量。请勿传递 idx
,可改用 (i*num_512_bytes+j)
。
数据流循环上的流水打拍突发推断
在具有 DATAFLOW 编译指示或指令的循环上不支持突发推断。但数据流循环内的每个进程/任务都具有突发。并且,由于任务可以并行执行,因此不支持在数据流区域内共享 M-AXI 端口。