考虑以下函数模块,
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]);
}
/* 数据输出代码段 */
}
仅当function()的latency=0时,calculation_loop能够实现II=1的流水线,否则综合后将由于存在存储依赖而出现II Violation警告,导致II不为1:
不难发现,其原因是在流水线计算中当i接近1时出现了数据读取超前于数据写入的情况,这也是高级语言和RTL语言所体现出的不同的计算特性。
Vitis HLS中并未提供类似于实现流水线冲刷的函数,为解决此问题,考虑添加流水线的运行控制信号run,
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
/* 数据输入代码段 */
ap_uint<1> run = true;
short int dont_run_cnt = 0;
calculation_loop:
for(int i = N - 1; i >= 1;)
{
#pragma HLS PIPELINE II=1
#pragma HLS DEPENDENCE false
if(run)
{
mem[i] = function(mem[2*i], mem[2*i + 1]); // 假设function()的latency=1
}
update_run(i, run, dont_run_cnt);
}
/* 数据输出代码段 */
}
当function()的latency不为0时,信号run在出现存储依赖的循环条件下置false,例如在上述样例中latency=1,在i = 1、2、3时出现存储依赖问题,通过设置run信号在i=1时增加两轮空转的循环,即可避免存储依赖。
函数update_run的一种写法:
void update_run(
int &i,
ap_uint<1> &run,
short_int &dont_run_cnt
){
if(i == 2)
{
if(run)
{
run = false;
dont_run_cnt = 1;
}
else
{
if(dont_run_cnt != 0)
dont_run_cnt--;
else
run = true;
}
}
else
{
i--;
}
}