HLS - 数组优化

发布时间 2023-06-14 21:52:22作者: 可达达鸭

1. 空间复杂度

  • 程序的两个衡量指标:时间复杂度(时钟频率)和空间复杂度(资源)。
  • 时间复杂度体现在循环语句,空间复杂度体现在数组上。

2. Partition展开操作

  • 为什么Partition可以提升性能

    • HLS将数组映射到Memory中。如果数组是作为顶层函数的形参,那么就会被综合成memory的接口;如果数组在设计内部,会根据数组大小及优化方式被综合成移位寄存器(大小<1024)/BRAM,LUTRAM,UltraRAM。
    • 存在的问题
      • 当数组作为存储器来实现时,存储器端口的数量会限制对数据的访问,可能导致流水线失败。

      • 假设一数组用单端口RAM实现,功能:扫描数组,依次访问连续三个地址的数据求和;此时每个周期只能进行一次读/写,无法实现流水线。

        • 若想实现吞吐量为1,需要预读取数据,并手动对数据访问流水打拍。
           dout_t array_mem_perform(din_t mem[N]) { 
           
           din_t tmp0, tmp1, tmp2;
           dout_t sum=0;
           int i;
           
           tmp0 = mem[0];
           tmp1 = mem[1];
           SUM_LOOP:for (i = 2; i < N; i++) {
           tmp2 = mem[i];
           sum += tmp2 + tmp1 + tmp0;
           tmp0 = tmp1;
           tmp1 = tmp2;
           }
               
           return sum;
          }
        
      • 即使使用双端口的RAM,每个时钟周期可进行两次访问,

    • 可以不用更改设计,HLS可以使用优化指令:Array Partition、Array Reshape.
  • Array Partition 有3个type可选

    • block、cyclic和complete
      • factor为n,将原数组等分成n个小的数组,每个数组的长度 = 原长度/n。
      • 以一维数组为例,如下图
      #param HLS ARRAY_PARTITION variable=weight_group block factor = 4 dim = 1
      #param HLS ARRAY_PARTITION variable=weight_group cyclic factor = 4 dim = 1
      #param HLS ARRAY_PARTITION variable=weight_group complete dim = 1
      
  • 对多维数组采用Partition展开

3. 数组的映射和重组

  • ARRAY_MAP
    • 将多个较小的数组映射成一个数组,以减少内部资源用量。
    • Horizontal:直接将多个数组拼接在一起,数组的长度等于N+M,数组的宽度为N和M的最大数组宽度。
  • Vertical:对于数组的每一个元素进行拼接,数组的长度等于N/M中最大的长度。
  • 命令介绍:其中variable是被map的数组;instance是map后的数组;mode可选horizontal/vertical;offset仅horizontal可选,int为合并的数组相较于0的偏移量。
    • 注:命令相关例子和用法可以查看Xilinx官网,有较为全面的解释。
    #pragma HLS array_map variable=<name> instance=<instance> <mode> offset=<int>
    //example 将C和D合并成CD
    #pragma HLS array_map variable=C instance=CD vertical
    #pragma HLS array_map variable=D instance=CD vertical
    
  • ARRAY_RESHAPE
    • 两步: