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工程的核心流程和结构关系:

Vitis HLS 工程结构 & 流程 源文件 (src/) adder.cpp adder.h 其他 .cpp/.h 测试文件 (tb/) adder_tb.cpp 测试数据文件 解决方案 (solution1/) C 综合 (syn/) 生成 RTL 代码 C/RTL 协同仿真 (sim/) 验证 RTL 正确性 IP 导出 (impl/) 生成 IP 核 综合报告 (report/) 输出产物 Verilog / VHDL IP 核 (XCI) 综合报告 (RPT) 综合 仿真 导出 一个工程可包含多个解决方案,便于对比不同优化策略

从这张图可以看得很清楚:源文件和测试文件是输入,经过解决方案的处理,最终输出RTL代码和IP核。解决方案是核心,它包含了综合、仿真、实现三个子步骤。

避坑指南:我曾经在同一个工程里建了5个解决方案,分别尝试不同的流水线深度和循环展开因子。结果发现,有时候II优化得特别好,但资源爆了;有时候面积很小,但延迟高得离谱。所以,多方案对比是HLS开发的常态,别指望一次就能找到最优解。

好了,关于Vitis HLS的界面、第一个项目创建和工程结构,咱们就聊到这里。这些东西看似基础,但就像盖房子的地基一样,打不牢后面会很痛苦。你可以在自己的机器上跟着操作一遍,遇到问题很正常,多看看Console里的日志,大部分答案都在那里。