电路设计新人
我最近刚做完一个图像锐化的IP,也折腾过这些优化。核心思路是让数据流动起来,别堵着。
首先,别一上来就加指令。先用HLS的Analysis视图,看看综合报告里的Timing和Latency。重点看最内层循环的II是不是1,如果不是,就说明有依赖导致没法每个时钟都启动新一次迭代。这时候用pipeline指令(一般是加在内层循环)强制流水,工具会想办法打破依赖(比如多副本寄存器),但前提是你的算法逻辑允许。
对于图像处理,通常是双层循环遍历像素。如果直接在最内层循环pipeline,吞吐是上去了,但每次计算一个像素可能需要周围的像素(比如3x3窗口),这就会产生依赖。经典做法是使用“行缓冲”(line buffer)而不是整个图像数组。你可以把行缓冲定义成多个独立的行(比如3行),然后对每个行数组进行partition complete(完全分割)成单个寄存器。这样HLS就会用寄存器实现每行,访问延迟极低,才能配合上内层循环的pipeline节奏。这就是array partition的典型用法。
至于dataflow,用在函数级或任务级流水。比如你的算法可以分成“读取像素”、“核心计算”、“写出结果”三个子函数。在顶层函数里,用dataflow指令,HLS会用FIFO把它们连接起来,形成生产者-消费者流水线,让三个部分同时处理不同像素的数据。这能极大提升整体吞吐,但要注意子函数间的数据通道(FIFO深度)要设对,不然容易死锁或性能下降。
一个简单的对比:优化前,两层循环,直接综合,II可能等于像素计算所需周期(比如10),延迟是整个图像的行数列数10。优化后,内层循环pipeline II=1,使用行缓冲且partition,顶层可能再加dataflow,理想情况下可以每个时钟输出一个像素结果,吞吐率就是时钟频率本身。
最后提醒,指令不是越多越好。乱加pipeline可能导致面积暴涨(因为要复制逻辑)。先优化最关键的瓶颈循环,看报告,再迭代。HLS的优化是个试错和平衡的过程。
