3、Vitis HLS工具详解:Vitis HLS界面介绍、创建第一个HLS项目、HLS工程结构解析
好,咱们今天正式进入Vitis HLS工具本身。说实话,很多初学者一打开这个工具就懵了——满屏的英文菜单、各种陌生的面板、还有一堆看不懂的配置文件。别急,我当年第一次接触Xilinx的HLS工具时,也花了整整一个下午才搞清楚哪个按钮是干嘛的。今天我就带你把这个工具彻底捋一遍。
3.1 Vitis HLS界面介绍
打开Vitis HLS,你会看到一个典型的Eclipse风格界面。嗯,没错,它底层就是基于Eclipse框架的。如果你用过Vivado或者其它Xilinx工具,会觉得似曾相识。
整个界面主要分为这么几个区域:
- Project Explorer(工程浏览器):左侧,管理你的源文件、测试文件、解决方案等。我习惯把这里当作“文件总指挥部”。
- Console(控制台):底部,显示编译、仿真、综合的实时日志。出错了先看这里,别瞎猜。
- Editor(编辑器):中间,写代码的地方。支持语法高亮,但说实话,我更喜欢在VS Code里写完了再导进来。
- Outline(大纲视图):右侧,显示当前文件的函数、变量结构。快速跳转很好用。
- Flow Navigator(流程导航器):顶部或侧边,引导你完成C仿真、C综合、C/RTL协同仿真、导出IP等步骤。这个对新手特别友好。
小技巧:我个人习惯把Console窗口拉大一点,因为HLS的警告信息特别多,有时候一个warning就暗示了潜在的时序问题。别忽略它们。
还有一个容易被忽略的——Reports(报告)面板。综合完成后,这里会生成详细的性能报告、资源利用率报告、接口报告。我每次综合完第一件事就是看报告,而不是看波形。
3.2 创建第一个HLS项目
光说不练假把式。咱们直接上手创建一个项目。我会用最简单的例子——一个加法器——带你走通整个流程。
3.2.1 新建项目
点击菜单栏 File → New → HLS Project。弹出向导窗口,你需要填几个东西:
- Project name:比如
adder_prj。别用中文,别带空格,这是铁律。 - Location:项目存放路径。我建议单独建一个文件夹,比如
D:/hls_workspace,方便管理。 - Top Function:顶层函数名。这里填
adder。注意,这个函数名必须和你代码里的顶层函数完全一致。
然后点击 Next,进入添加源文件的步骤。你可以选择 Add Files 添加已有的 .c/.cpp 文件,也可以稍后创建。咱们先跳过,直接点 Finish。
注意:我曾经犯过一个低级错误——顶层函数名写错了大小写,结果综合时报错说找不到入口函数。找了半天才发现是大小写问题。所以,这里一定要仔细核对。
3.2.2 编写代码
在Project Explorer里,右键 Source 文件夹,选择 New → Source File,命名为 adder.cpp。然后写入以下代码:
#include <ap_int.h>
typedef ap_uint<8> data_t;
data_t adder(data_t a, data_t b) {
return a + b;
}
这里我用了 ap_uint<8>,这是HLS自定义的任意精度整数类型。为什么不用C自带的 unsigned char?因为在实际项目中,位宽往往不是8、16、32这种标准值,比如你需要一个13位的计数器,用 ap_uint<13> 就非常精准。这也是HLS的一大优势。
3.2.3 添加测试文件
同样,在 Test Bench 文件夹下新建一个 adder_tb.cpp:
#include <iostream>
#include "adder.h"
int main() {
data_t a = 15, b = 27;
data_t result = adder(a, b);
std::cout << "Result: " << result << std::endl;
if (result == 42) {
std::cout << "Test passed!" << std::endl;
return 0;
} else {
std::cout << "Test failed!" << std::endl;
return 1;
}
}
注意,测试文件里要包含顶层函数的头文件。我习惯把函数声明放在 adder.h 里,这样源文件和测试文件都能引用。
3.2.4 运行C仿真
在Flow Navigator里点击 Run C Simulation。如果一切顺利,Console里会打印出 Test passed!。这一步是验证你的C代码功能是否正确。我建议每次修改代码后都跑一遍C仿真,别等到综合完才发现逻辑错了,那太浪费时间。
3.2.5 运行C综合
点击 Run C Synthesis。工具会把你写的C代码转换成RTL(寄存器传输级)代码。这个过程可能需要几秒到几分钟,取决于代码复杂度。综合完成后,会自动弹出综合报告。
你会看到几个关键指标:
- Latency(延迟):从输入到输出需要多少个时钟周期。
- II(Initiation Interval,启动间隔):两次连续输入之间最少间隔多少个时钟周期。
- Resource Utilization(资源利用率):用了多少LUT、FF、DSP、BRAM。
对于这个加法器,延迟应该是1个周期,II也是1。资源嘛,几乎可以忽略不计。
核心要点:C综合是HLS的灵魂。它决定了你的C代码最终会映射成什么样的硬件电路。同样的C代码,不同的综合策略(比如是否流水线、是否展开循环),结果天差地别。这部分我们后面会详细讲。
3.3 HLS工程结构解析
项目创建好了,咱们来看看它的内部结构。在Project Explorer里展开你的工程,你会看到这样的目录树:
adder_prj/
├── src/
│ ├── adder.cpp
│ └── adder.h
├── tb/
│ └── adder_tb.cpp
├── solution1/
│ ├── syn/
│ │ ├── verilog/
│ │ ├── vhdl/
│ │ └── report/
│ ├── sim/
│ │ └── wrap/
│ └── impl/
│ └── ip/
└── .settings/
我来解释一下每个目录的作用:
- src/:存放你的C/C++源文件和头文件。这是你主要工作的地方。
- tb/:测试文件(Test Bench)。注意,测试文件不会被综合,只用于仿真验证。
- solution1/:解决方案。一个工程可以有多个解决方案,每个方案对应不同的综合策略或优化目标。比如solution1追求低延迟,solution2追求小面积。你可以随时切换对比。
- syn/:综合后的输出。里面包含生成的RTL代码(Verilog或VHDL)、综合报告等。
- sim/:仿真相关文件。C/RTL协同仿真时会用到。
- impl/:实现后的IP包。你可以把这个IP导出到Vivado里使用。
我个人觉得,理解这个结构很重要。很多新手遇到问题不知道去哪里看报告,或者找不到生成的RTL代码,就是因为对工程结构不熟悉。
下面我用一张SVG图来展示HLS工程的核心流程和结构关系:
从这张图可以看得很清楚:源文件和测试文件是输入,经过解决方案的处理,最终输出RTL代码和IP核。解决方案是核心,它包含了综合、仿真、实现三个子步骤。
避坑指南:我曾经在同一个工程里建了5个解决方案,分别尝试不同的流水线深度和循环展开因子。结果发现,有时候II优化得特别好,但资源爆了;有时候面积很小,但延迟高得离谱。所以,多方案对比是HLS开发的常态,别指望一次就能找到最优解。
好了,关于Vitis HLS的界面、第一个项目创建和工程结构,咱们就聊到这里。这些东西看似基础,但就像盖房子的地基一样,打不牢后面会很痛苦。你可以在自己的机器上跟着操作一遍,遇到问题很正常,多看看Console里的日志,大部分答案都在那里。