第三章 FPGA开发环境搭建:Vivado安装与配置、Vivado HLS/Vitis HLS入门、第一个HLS工程:矩阵乘法加速

好,咱们进入实战环节了。前面两章聊了CT重建的原理和为什么选FPGA,这一章咱们得把家伙事儿备齐——把开发环境搭起来,跑通第一个HLS工程。

说实话,环境搭建这事儿,看着简单,但坑不少。我见过太多同学卡在安装这一步,折腾一整天连个灯都没点亮。所以这一章我会把细节掰开揉碎了讲,尤其是那些容易踩坑的地方。

3.1 Vivado安装与配置

Vivado是Xilinx(现在叫AMD了)的FPGA开发套件。版本很多,从2018到2024都有。我个人建议用2020.1或2021.1,这两个版本比较稳定,网上资料也多。

3.1.1 下载与安装

去官网下载Vivado HLx版本,大概20-30GB。嗯,你没看错,就是这么夸张。所以建议提前准备好网络环境,别用手机热点。

安装时注意几点:

  • 选择版本:选Vivado HL WebPACK就够了,除非你要用Virtex UltraScale+这种大芯片
  • 组件选择:一定要勾上Vivado HLS(或者Vitis HLS,看版本)
  • 安装路径:别用中文路径!别用空格!我吃过这个亏,编译报错查了半天才发现是路径问题
  • License:WebPACK版本不需要额外license,但如果你要用更高端的器件,得申请免费license
我的小技巧:安装时把杀毒软件关了,不然安装到一半可能被拦截。另外,安装目录建议放在SSD上,编译速度会快不少。

3.1.2 环境变量配置

安装完后,需要配置环境变量。Windows下比较简单:

# 添加系统环境变量
XILINX_VIVADO = C:\Xilinx\Vivado\2021.1
PATH = %XILINX_VIVADO%\bin;%PATH%

Linux下呢,我习惯在.bashrc里加一行:

source /tools/Xilinx/Vivado/2021.1/settings64.sh

为什么要配环境变量?说白了就是让系统能找到vivado、vitis_hls这些命令。不然每次都要跑到安装目录下去执行,太麻烦了。

3.1.3 验证安装

打开终端或命令行,输入:

vivado -version

如果能看到版本信息,恭喜你,安装成功了。如果报错说找不到命令,八成是环境变量没配好。

3.2 Vivado HLS / Vitis HLS入门

好,接下来聊聊HLS。HLS全称是High-Level Synthesis,说白了就是让你用C/C++写FPGA逻辑,不用去手写Verilog。

你可能会问:为什么要用HLS?直接写Verilog不香吗?

我的看法是:对于CT重建这种算法密集型的任务,用HLS开发效率高得多。你想想看,一个矩阵乘法用Verilog写要几百行,用HLS几十行就搞定了。而且HLS做优化、迭代也快。

不过HLS也有它的局限性。我在项目中遇到过,有些时序敏感的逻辑,HLS生成的硬件效率不如手写Verilog。所以我的建议是:算法逻辑用HLS,接口和时序控制用手写Verilog,两者结合才是王道。

3.2.1 Vivado HLS vs Vitis HLS

这两个名字容易搞混。简单说:

特性 Vivado HLS Vitis HLS
所属套件 Vivado Vitis统一平台
支持语言 C/C++ C/C++,OpenCL
适用版本 2019.2及之前 2020.1及之后
使用场景 传统FPGA开发 异构计算、AI加速

如果你用的是2020.1之后的版本,建议直接用Vitis HLS。功能更强,而且和Vitis分析工具集成得更好。

3.2.2 HLS的基本工作流程

HLS的开发流程其实挺直观的:

  1. 写C代码:用C/C++描述算法
  2. 加pragma:用#pragma HLS告诉工具怎么优化
  3. C仿真:验证功能正确性
  4. C综合:把C代码转成RTL
  5. 分析报告:看延迟、吞吐量、资源占用
  6. 导出IP:生成IP核供Vivado使用

嗯,这里要注意:C仿真通过了不代表综合出来的硬件没问题。我遇到过C仿真结果正确,但综合后硬件跑出来不对的情况。所以一定要做RTL仿真和上板验证。

3.3 第一个HLS工程:矩阵乘法加速

理论说完了,咱们动手吧。第一个工程选矩阵乘法,原因很简单:

  • CT重建里到处都是矩阵运算
  • 矩阵乘法是典型的计算密集型任务,适合展示HLS的优化能力
  • 代码简单,容易理解

3.3.1 创建工程

打开Vitis HLS,按以下步骤操作:

  1. 点击Create New Project
  2. 输入工程名:matrix_mult_hls
  3. 选择顶层函数:matrix_mult
  4. 选择器件:我习惯用xc7z020clg484-1(Zynq-7020),CT重建常用这个
  5. 点击Finish
注意:顶层函数名要和代码里的函数名一致,不然综合时会报错。我曾经因为大小写问题折腾了半小时。

3.3.2 编写代码

在Source里新建一个文件,叫matrix_mult.cpp:

#include <hls_stream.h>
#include "ap_int.h"

#define N 4

void matrix_mult(int A[N][N], int B[N][N], int C[N][N]) {
    #pragma HLS INTERFACE s_axilite port=return
    #pragma HLS INTERFACE bram port=A
    #pragma HLS INTERFACE bram port=B
    #pragma HLS INTERFACE bram port=C

    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            #pragma HLS PIPELINE II=1
            int sum = 0;
            for (int k = 0; k < N; k++) {
                sum += A[i][k] * B[k][j];
            }
            C[i][j] = sum;
        }
    }
}

这段代码看着简单,但有几个关键点:

  • #pragma HLS PIPELINE II=1:告诉工具尽量让循环流水线化,理想情况下每个时钟周期输出一个结果
  • INTERFACE:定义端口类型,这里用BRAM接口,方便和Zynq的PS端通信
  • ap_int.h:Xilinx的任意精度整数库,比标准int更灵活

3.3.3 编写测试激励

再建一个testbench文件,叫tb_matrix_mult.cpp:

#include <iostream>
#include "matrix_mult.cpp"

int main() {
    int A[N][N] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16}};
    int B[N][N] = {{1,0,0,0}, {0,1,0,0}, {0,0,1,0}, {0,0,0,1}};
    int C[N][N] = {0};

    matrix_mult(A, B, C);

    // 验证结果
    bool pass = true;
    for (int i = 0; i < N; i++) {
        for (int j = 0; j < N; j++) {
            if (C[i][j] != A[i][j]) {
                pass = false;
                std::cout << "Mismatch at [" << i << "][" << j << "]" << std::endl;
            }
        }
    }

    if (pass) {
        std::cout << "Test passed!" << std::endl;
        return 0;
    } else {
        std::cout << "Test failed!" << std::endl;
        return 1;
    }
}

这里我用单位矩阵做测试,因为任何矩阵乘以单位矩阵结果不变,验证起来方便。

3.3.4 运行C仿真

在Vitis HLS界面里:

  1. 右键点击工程,选择Run C Simulation
  2. 在弹出的对话框里,选择tb_matrix_mult.cpp作为testbench
  3. 点击OK

如果看到"Test passed!",说明功能正确。如果报错,检查一下代码有没有拼写错误。

3.3.5 综合与优化

C仿真通过后,点击Solution → Run C Synthesis。等几分钟,工具会生成综合报告。

报告里重点关注几个指标:

  • Latency:从输入到输出需要多少个时钟周期
  • II (Initiation Interval):多久能处理一组新数据
  • BRAM/DSP/FF/LUT:资源占用情况

对于4x4矩阵乘法,不加优化时延迟大概在几十个周期。如果加上PIPELINE和UNROLL优化,可以降到十几个周期。我在CT重建项目里,把矩阵乘法从64x64优化到4x4分块处理,延迟从几千周期降到了几百周期,效果很明显。

优化小贴士:如果资源够用,可以用#pragma HLS UNROLL factor=4把内层循环展开,并行度更高。但要注意,展开会消耗更多DSP和LUT资源。

3.3.6 导出IP核

综合通过后,点击Export RTL,选择IP Catalog格式。这样生成的IP核就可以在Vivado里直接调用了。

导出时注意:

  • 选择IP-XACT格式,兼容性最好
  • 勾上"Evaluate with Vivado"可以自动打开Vivado检查
  • IP核的名字和版本号要规范,方便后续管理

3.4 常见问题与避坑指南

最后,分享几个我踩过的坑:

  • 综合时间太长:如果工程比较大,可以先做C仿真,确认逻辑正确后再综合。别每次改一行代码就综合一次,太浪费时间。
  • 资源爆了:CT重建的算法往往很复杂,资源很容易超标。我的经验是先做资源评估,再决定优化策略。
  • 时序不收敛:如果综合后时序不满足,可以试试降低时钟频率,或者用PIPELINE优化关键路径。
  • 仿真和实际不一致:这个最头疼。我建议在C仿真通过后,一定要做RTL协同仿真(Co-simulation),确保RTL行为和C代码一致。

好了,这一章的内容就到这里。环境搭好了,第一个HLS工程也跑通了,下一章咱们开始真正进入CT重建的核心算法——滤波反投影(FBP)的FPGA实现。

记住,HLS只是工具,真正重要的是理解算法和硬件架构。多动手,多思考,你也能成为FPGA加速的高手。