FPGA赛道突击(三)-HLS编程学习笔记
FPGA赛道突击(三)-HLS编程学习笔记

FPGA赛道突击(三)-HLS编程学习笔记

一、使用HLS开发有别于C语言的经验及注意事项

1、编程注意事项

  • 使用适合HLS的C++子集:并非所有C/C++语法都适合HLS。避免使用动态内存分配(如malloc、new)、递归函数、系统调用等。
  • 数据类型:使用固定大小的数据类型,如int8_tint16_tint32_t,或者使用HLS提供的任意精度数据类型,如ap_int<8>ap_fixed<16,8>,这些可以节省资源。
  • 数组映射:数组通常被映射到BRAM或URAM。考虑数组的分块和重组以优化内存访问。
  • 函数内联:小函数通常会被内联,但也可以显式控制内联。

2、 优化技巧

  • 流水线:对循环和函数使用流水线,以提高吞吐量。
  • 循环展开:对于小循环,可以完全展开以增加并行性,但会消耗更多资源。
  • 数组分区:将大数组分割成多个小数组,以便并行访问。
  • 数据流:允许函数内的多个操作并行执行,只要它们之间没有数据依赖。
  • 依赖关系:注意循环携带依赖和内存依赖,这些会限制并行性。

3、编程约束和限制

(1).不支持的语言特性

// 以下在HLS中通常不支持或有限制:
// 1. 动态内存分配
int *arr = new int[100];  // 不支持

// 2. 递归函数</em>
int factorial(int n) {     // 有限制
    if(n <= 1) return 1;
    return n * factorial(n-1);
}

// 3. 系统调用
printf();  // 仅用于调试,不会生成硬件
FILE *fp = fopen();  // 不支持

// 4. 虚函数和多态
class Base {
    virtual void func();  // 有限制
};

(2).必须使用固定大小数组

// 传统C
void process(int *arr, int size) {  // 动态大小,HLS不友好
    for(int i = 0; i < size; i++) {
        // ...
    }
}

// HLS推荐
#define ARRAY_SIZE 1024
void process(int arr[ARRAY_SIZE]) {  // 固定大小,HLS友好
    #pragma HLS PIPELINE
    for(int i = 0; i < ARRAY_SIZE; i++) {
        // ...
    }
}

4、 关键优化技术

(1).流水线 (Pipelining)

// 无流水线 - 性能差
for(int i = 0; i < N; i++) {
    // 每个迭代需要多个周期
    result[i] = complex_operation(input[i]);
}

// 有流水线 - 高性能
for(int i = 0; i < N; i++) {
    #pragma HLS PIPELINE II=1  // 每个时钟周期开始一个新迭代
    result[i] = complex_operation(input[i]);
}

(2).循环展开 (Loop Unrolling)

// 完全展开
#pragma HLS UNROLL
for(int i = 0; i < 4; i++) {
    sum += a[i] * b[i];
}

// 部分展开
#pragma HLS UNROLL factor=2
for(int i = 0; i < 8; i++) {
    sum += a[i] * b[i];
}

(3).数组分区 (Array Partitioning)

int buffer[1024];
// 完全分区 - 每个元素独立访问
#pragma HLS ARRAY_PARTITION variable=buffer complete

// 块分区 - 分成多个块
#pragma HLS ARRAY_PARTITION variable=buffer block factor=4

// 循环分区 - 按访问模式分区 
#pragma HLS ARRAY_PARTITION variable=buffer cyclic factor=4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注