3. HLS工具链入门:典型HLS工具(Vivado HLS/Vitis HLS)安装与工作流程
好,咱们今天聊聊HLS工具链。说白了,就是怎么把C/C++代码变成硬件电路的那一套家伙事儿。
我刚开始接触HLS那会儿,心里也犯嘀咕:这玩意儿靠谱吗?后来在几个项目里摸爬滚打,才慢慢摸清了门道。今天我就把Vivado HLS和Vitis HLS的安装流程、工作套路,掰开了揉碎了讲给你听。
3.1 工具概览:Vivado HLS vs Vitis HLS
先搞清楚这两个工具的关系。Vivado HLS是Xilinx早期的HLS工具,后来被整合进了Vitis统一开发平台。说白了,Vitis HLS就是Vivado HLS的升级版。
| 特性 | Vivado HLS | Vitis HLS |
|---|---|---|
| 推出时间 | 2012年左右 | 2019年 |
| 集成环境 | 独立GUI | Vitis统一平台 |
| 语言支持 | C/C++/SystemC | C/C++/SystemC |
| 优化指令 | #pragma HLS | #pragma HLS + 新特性 |
| 调试能力 | 基础波形查看 | 增强的协同仿真 |
我个人习惯用Vitis HLS,毕竟新工具对C++17的支持更好,调试起来也顺手。但如果你还在维护老项目,Vivado HLS也完全够用。
3.2 安装流程:别踩这些坑
安装这事儿,看着简单,其实门道不少。我曾经在一台Ubuntu 18.04上折腾了整整一下午,就为了搞定一个库依赖问题。
3.2.1 下载与解压
去Xilinx官网下载Vitis统一安装包。注意,这个包很大,大概20GB左右。下载完解压:
tar -xzvf Xilinx_Vitis_2023.1_0522_1443.tar.gz
cd Xilinx_Vitis_2023.1_0522_1443
3.2.2 安装依赖库
这一步最容易出问题。我建议你提前装好这些库:
sudo apt-get update
sudo apt-get install libncurses5 libncurses5-dev libncursesw5
sudo apt-get install libtinfo5 libtinfo-dev
sudo apt-get install libc6-i386 lib32stdc++6 lib32gcc-s1
嗯,这里要注意,不同Ubuntu版本可能缺不同的库。如果安装过程中报错,别慌,看错误信息缺什么就装什么。
3.2.3 运行安装脚本
./xsetup
图形化界面会弹出来。选择Vitis HLS组件就行,不用全装。全装的话,硬盘空间至少留100GB。
3.3 工作流程:从C到RTL的奇幻之旅
安装好了,咱们看看HLS的工作流程。说白了就三步:写代码、加约束、看结果。
核心流程:C/C++源码 → 综合(Synthesis) → 调度(Scheduling) → 绑定(Binding) → RTL输出
我画了张图,帮你理清这个流程:
3.4 实战:跑通第一个HLS工程
光说不练假把式。咱们写个简单的FIR滤波器,看看整个流程怎么走。
3.4.1 编写C代码
// fir.c
#include <ap_int.h>
#include <hls_stream.h>
#define N 11
void fir(hls::stream<ap_int<16>> &in,
hls::stream<ap_int<16>> &out,
const ap_int<16> coeff[N]) {
#pragma HLS INTERFACE axis port=in
#pragma HLS INTERFACE axis port=out
#pragma HLS INTERFACE s_axilite port=coeff
static ap_int<16> shift_reg[N];
#pragma HLS ARRAY_PARTITION variable=shift_reg complete dim=1
ap_int<16> acc = 0;
ap_int<16> data_in;
in.read(data_in);
for (int i = N-1; i > 0; i--) {
#pragma HLS UNROLL
shift_reg[i] = shift_reg[i-1];
}
shift_reg[0] = data_in;
for (int i = 0; i < N; i++) {
#pragma HLS PIPELINE
acc += shift_reg[i] * coeff[i];
}
out.write(acc);
}
这段代码里,我用了几个关键的pragma指令。你想想看,#pragma HLS PIPELINE 就是告诉工具:这个循环要流水线化,每个时钟周期处理一次乘法累加。
3.4.2 编写测试激励
// tb_fir.cpp
#include <iostream>
#include <hls_stream.h>
#include <ap_int.h>
int main() {
hls::stream<ap_int<16>> in, out;
ap_int<16> coeff[N] = {1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1};
// 输入测试数据
for (int i = 0; i < 100; i++) {
in.write(i);
fir(in, out, coeff);
std::cout << "out[" << i << "] = " << out.read() << std::endl;
}
return 0;
}
3.4.3 运行综合
在Vitis HLS GUI里,新建工程,添加fir.c和tb_fir.cpp。然后点击「C Synthesis」按钮。工具会开始综合,生成调度结果和RTL代码。
3.5 常见问题与避坑指南
做HLS开发,有些坑是绕不开的。我把自己踩过的坑列出来,你遇到了直接对照着查。
- 指针别名问题:C代码里如果用了指针,HLS工具可能无法判断它们是否指向同一块内存。解决办法是用
#pragma HLS INTERFACE明确指定接口类型。 - 循环边界不确定:HLS要求循环边界在编译时确定。如果用了变量作为循环边界,工具会报错。我建议用常量或者
#pragma HLS LOOP_TRIPCOUNT给出估计值。 - 数组综合成BRAM还是LUT:默认情况下,小数组会综合成LUT,大数组会综合成BRAM。如果你想强制用BRAM,可以用
#pragma HLS RESOURCE variable=my_array core=RAM_1P。
#pragma HLS LATENCY 指定了乘法器的延迟,问题就解决了。
3.6 小结
嗯,今天的内容就这些。Vivado HLS和Vitis HLS的安装其实不难,关键是理解工作流程。从C代码到RTL,中间经历了综合、调度、绑定三个阶段。每个阶段都有对应的优化指令,用好了事半功倍。
我个人觉得,HLS工具链最妙的地方在于:你可以用软件思维写硬件,但又能通过优化指令控制硬件的细节。说白了,就是「既要又要」——既要开发效率,又要硬件性能。
好了,去装个Vitis HLS试试吧。有问题随时回来翻翻这篇笔记。