和上一篇内容Vitis/Vivado HLS 流水线中的存储依赖——解决方法之一中一样,考虑以下函数模块,
void ExampleModule(
hls::stream<t_data> strm_in,
hls::stream<t_data> strm_out
){
#pragma HLS INTERFACE mode=ap_ctrl_none port=return
t_data mem[2*N];
#pragma HLS BIND_STORAGE variable=mem type=ram_2p impl=bram
#pragma HLS ARRAY_PARTITION dim=1 factor=2 type=cyclic variable=mem
/* 数据输入代码段 */
calculation_loop:
for(int i = N - 1; i >= 1; i--)
{
#pragma HLS PIPELINE II=1
mem[i] = function(mem[2*i], mem[2*i + 1]);
}
/* 数据输出代码段 */
}
综合后将由于存在存储依赖而出现II Violation警告,其原因是在流水线计算中当i接近1时出现了数据读取超前于数据写入的情况。
上一篇内容采用的解决方法是引入流水线空转的方式避开循环的存储依赖,该方法会增加整个循环latency。
另一个方法:采用额外的寄存器寄存存储依赖的数据,而非写回存储器。
void ExampleModule(
hls::stream<t_data> strm_in,
hls::stream<t_data> strm_out
){
#pragma HLS INTERFACE mode=ap_ctrl_none port=return
t_data mem[2*N];
#pragma HLS BIND_STORAGE variable=mem type=ram_2p impl=bram
#pragma HLS ARRAY_PARTITION dim=1 factor=2 type=cyclic variable=mem
t_data reg[3];
/* 数据输入代码段 */
ap_uint<1> update_conflict = false;
calculation_loop:
for(int i = N - 1; i >= 1; i--)
{
#pragma HLS PIPELINE II=1
#pragma HLS DEPENDENCE false
t_data src_1 = conflict ? reg[2 * i] : mem[2 * i];
t_data src_2 = mem[2 * i + 1];
t_data res = function(mem[2*i], mem[2*i + 1]); // 假设function()的latency=1
update_conflict(i, conflict);
if(conflict)
reg[i] = res;
else
mem[i] = res;
}
// 将寄存器中的冲突数据写入存储器,非必须
mem[2] = res[2];
/* 数据输出代码段 */
}
其中,update_conflict函数的一种写法为
void update_conflict(
int i,
ap_uint<1> &conflict
){
if(i == 2)
{
conflict = true;
}
else
{
conflict = false;
}
}
即:当i=2时,存储依赖存在,将conflict置位,该次循环的res将写入寄存器,同时下一循环将从寄存器中取值,从而避免存储器的存储依赖。