目录
Vivado HLS是Xilinx公司推出的一款高层次综合工具,用于将C、C++或OpenCL代码转换为可综合的RTL代码。以下是一些常见的Vivado HLS优化指令详解。
1.内核优化(Kernel Optimization)
PIPELINE:该指令用于对函数或循环进行流水线操作。作用于函数时,可使函数的多个实例在不同的时钟周期内并行执行,从而提高吞吐量。作用于循环时,将循环体中的操作分解到多个时钟周期中执行,允许下一次循环的部分操作在前一次循环尚未完全结束时就开始,大大提高了循环的执行速度。例如在一个数据处理的循环中,使用#pragma HLS PIPELINE可以让数据在流水线中逐个处理,每个时钟周期都能处理新的数据。当作用于循环时有个rewind选项,若设置为true,则在循环结束后会自动将流水线复位到初始状态,以便下一次循环使用相同的硬件资源。
使用方法:
#pragma HLS PIPELINE II=<int> enable_flush=<bool> rewind=<bool>
INLINE:用于去除函数的层次化,将被调用函数的代码直接嵌入到调用函数中。对于小函数,Vivado HLS通常会自动进行内联处理,这样可以减少函数调用的开销,改善资源消耗,因为内联后不再需要调用函数的相关逻辑,如保存和恢复寄存器等操作。如果不希望某个函数自动内联,可以使用INLINE -off选项来禁止自动内联。
使用方法:
#pragma HLS INLINE off|recursive
ALLOCATION:定义了函数和相应的RTL module之间的关系,主要用于在相同函数被多次调用时,使能创建多个实例,让这些实例能够并行执行,从而提高系统的性能。例如,在一个有多个数据处理任务的系统中,每个任务都可以通过ALLOCATION创建独立的实例,并行处理不同的数据,提高整体的数据处理速度。
使用方法:
#pragma HLS ALLOCATION instances=<name> limit=<int> function
2.循环优化(Loop Optimization)
UNROLL:将循环体展开为并行操作,通过复制循环体中的电路来实现多个循环迭代的同时执行,从而减少循环的执行时间,提高吞吐量。可以使用factor选项来控制循环被复制成几个并行的部分。例如,#pragma HLS UNROLL factor=4表示将循环展开为 4 个并行的部分,每个部分处理不同的数据,原本需要多个时钟周期完成的循环,现在可以在较少的时钟周期内完成。不过,过度展开可能会导致资源消耗增加。
使用方法:
#pragma HLS UNROLL factor=<N> skip_exit_check=<bool>
LOOP_MERGE:用于将顺序的循环合并在一起。在默认情况下,for 循环会创建额外的状态机,而额外的状态机会占用额外的时钟周期以及额外的资源。通过LOOP_MERGE将相邻的循环合并,可以减少状态机的数量,从而在一定程度上降低延迟,提高性能。例如,有两个相邻的循环,它们操作的数据相关且可以合并为一个循环进行处理,使用LOOP_MERGE指令就可以将它们合并,减少总的时钟周期数。
使用方法:
#pragma HLS LOOP_TRIPCOUNT min=<int> max=<int> avg=<int>
3.数组优化(Array Optimization)
ARRAY_PARTITION:用于将数组分割成不同的部分,以提高数组的访问效率和并行性。有三种分割方式,分别是block、cyclic和complete。
block:将数组按照指定的块大小进行分割,每个块可以独立访问,适用于需要对数组进行分块处理的情况,例如在矩阵运算中,将矩阵按块分割后可以并行处理不同的块。
cyclic:将数组元素循环地分配到不同的块中,分割为指定块数,这种方式可以实现更均匀的数据分布,提高数组的并行访问能力,常用于需要对数组进行随机访问或流水线操作的场景。
complete:将数组的每个元素都分割成独立的部分,每个元素都可以单独进行访问和操作,适用于需要对数组元素进行高度并行处理的情况,但可能会消耗较多的资源。
使用方法:
#pragma HLS ARRAY_PARTITION variable=<name> type=<type> factor=<int> dim=<int>
ARRAY_RESHAPE:将数组的分割与纵向的映射结合在一起,它可以改变数组的结构和布局,以适应硬件实现的需求,提高数据的吞吐量。例如,在一些数据处理算法中,需要将二维数组重新排列成一维数组,或者将数组的维度进行调整,以便更好地利用硬件资源进行并行处理。
使用方法:
#pragma HLS ARRAY_RESHAPE variable=<name> type=<type> factor=<int> dim=<int>
4.其他优化指令
DATAFLOW:可以作用于函数和循环,是一种乒乓操作的方式,它允许顺序执行的任务(如函数、循环等)在数据准备好时就开始执行,而不必等待前一个任务完全完成,从而实现任务之间的交叠执行,提高系统的吞吐量,降低延迟。例如,在一个数据处理流程中,有多个函数依次对数据进行处理,使用DATAFLOW可以让每个函数在有输入数据时就开始处理,而不需要等待上一个函数处理完所有数据,实现数据的流水式处理。
使用方法:
#pragma HLS DATAFLOW disable_start_propagation=<bool>
LATENCY:用于指定完成函数、循环或区域的延迟。通过明确指定延迟要求,可以让编译器根据这个约束来优化设计,例如调整流水线的级数、资源的分配等,以满足特定的延迟指标。例如,对于一个实时性要求较高的系统,需要限制某个函数的执行时间,就可以使用LATENCY指令来指定最大允许的延迟时间,让编译器生成满足该要求的硬件设计。
使用方法:
#pragma HLS LATENCY min=<int> max=<int>
此外,Vivado HLS还提供了一些其他的优化指令,如INTERFACE指令用于将数组具体化为RAM 或者FIFO接口;RESOURCE指令可将数组具体化为单个或双管脚RAM,并能规定RAM的 latency 等。这些优化指令可以根据具体的设计需求和性能目标来灵活使用,以实现高效的硬件设计。在使用Vivado HLS优化指令时,需要综合考虑设计的性能、资源消耗、延迟等多个方面的因素,通过不断地试验和调整,找到最适合具体应用场景的优化方案。