Vivado HLS

Vivado HLS

社蕙 498 2022-12-13

这个玩意是他妈真垃圾,谁用这个东西做设计谁傻逼

编译参数

HLS关于设置GCC编译选项的问题

在project设置里改,但不是去改运行参数,而是去改具体文件的参数。

Select the matrixmul_test.cpp in the files list window and click the Edit CFLAG… button, type
-DHW_COSIM, and click OK. (This defines a compiler flag that will be used later.)

定点化

Vitis 高层次综合用户指南 (UG1399),挺好懂的这一章,特别是HDL正儿八经手动写过定点化的,不难。

最好把定义写在头文件里,一起改方便,因为定点数debug看不到值,非常恶心。

typedef ap_fixed<6, 2> w1_t;
typedef ap_fixed<6, 2> w2_t;
typedef ap_fixed<6, 1> b1_t;
typedef ap_fixed<6, 1> b2_t;
// 下面用来debug!
//  typedef float w1_t;
//  typedef float w2_t;
//  typedef float b1_t;
//  typedef float b2_t;

HLS for循环优化

HLS for循环优化这篇文章讲的还可以,DATAFLOW稍微看懂了一些。

DATAFLOW

说回DATAFLOW这个东西,Vitis 高层次综合用户指南 (UG1399)给出了一个编程范式,相当麻烦这个东西,我没有完全搞懂,顶着一大堆warning在用吧反正。

注意:内联一大堆循环可能会直接error,要么不让他内联,要么想个办法把循环pipeline。

PIPELINE

如果要pipeline,就要注意两件事:第一,变量必须能够匹配操作吞吐量,比如array一般用RAM存,多存储带宽低,那要PARTITION,能把变量取出来;第二,循环必须能够流水。

// 错误的写法,这样FOR_HIDDEN_2循环在不停地处理同一个hidden元素
// 前后产生依赖,编译器无法正常流水
    FOR_HIDDEN_1: for (int i = 0; i < HIDDEN_LAYER; i++) {
        FOR_HIDDEN_2: for (int j = 0; j < IMAGE_SIZE; j++) {
            hidden[i] += input_image[j] * w1[j][i];
        }
    }
// 正确的写法,这样FOR_HIDDEN_2循环在处理不同hidden元素
// 因为hidden有32个元素,而一次循环3个cycle
// 保证第二次处理同一个hidden元素时,前一次处理必定完成
    FOR_HIDDEN_2: for (int j = 0; j < IMAGE_SIZE; j++) {
        FOR_HIDDEN_1: for (int i = 0; i < HIDDEN_LAYER; i++) {
            hidden[i] += input_image[j] * w1[j][i];
        }
    }

概念

interval这个东西其实也还是不能说完全懂了,参考关于多功能单元流水线的延迟(latency)与启动间隔(Initiation interval)

延迟(latency):生成结果的指令与使用结果的指令之间的周期数。(在没有发生RAW冒险的情况下)。关于latency我的理解是,该指令执行之后,需要等待多久该指令才能产生可以使用的结果,被其他指令所使用。举个例子,浮点加法单元的latency为3,意味着如果有一条浮点加法指令,那么在不发生RAW的情况下,最快也要在3个时钟周期后,其他指令才能使用刚才这条浮点加法指令的运算结果。
启动间隔(Initiation interval):在发出两个给定类型的操作之间必须间隔的周期数。关于Initiation interval我的理解是:它有点像Throughput吞吐量,也就是在发射了一条这样的指令后,需要多少个时钟周期才能够再次发射一条这样的指令。