HLS入门到上板联调全流程 - 第1节

一、HLS概述:到底什么是HLS?

HLS,全称是High-Level Synthesis,中文叫高层次综合。

说白了,就是让你用C/C++写硬件逻辑。

你可能会问:「用C写硬件?靠谱吗?」

嗯,我刚开始接触HLS时也是这个反应。记得2015年我第一次在项目里尝试用HLS做图像处理加速,当时心里直打鼓——这玩意儿综合出来的电路能跑吗?结果呢?不仅跑通了,时序还比手写RTL好不少。

HLS的本质,是把C/C++描述的算法行为,自动转换成RTL级的硬件描述(Verilog/VHDL)。它帮你做了三件事:

  • 调度(Scheduling):决定每个操作在哪个时钟周期执行
  • 绑定(Binding):把操作映射到具体的硬件资源(LUT、DSP、BRAM)
  • 控制逻辑生成:自动产生状态机来控制数据流

你想想看,传统RTL设计里,这些全得你手动搞定。一个简单的FIR滤波器,RTL写下来少说几百行,还要反复仿真验证时序。用HLS呢?几十行C代码,加几个pragma指令,完事。

核心观点:HLS不是要取代RTL,而是让算法工程师和硬件工程师之间的沟通成本降到最低。你负责算法实现,工具负责硬件映射。

二、HLS vs 传统RTL设计:谁更香?

这个问题我经常被问到。直接上对比表吧:

对比维度 HLS 传统RTL
开发效率 高,C仿真秒级迭代 低,仿真+综合耗时
调试难度 低,C调试工具成熟 高,波形分析费时
性能控制 中等,依赖工具优化 精细,完全可控
资源利用率 良好,但可能冗余 优秀,可极致优化
复用性 高,C代码跨平台 低,需重写适配
学习曲线 平缓,C语言基础即可 陡峭,需掌握硬件思维

我个人习惯是这么分的:

  • 算法密集型的模块(图像处理、信号处理、神经网络)→ 用HLS
  • 接口时序敏感的模块(DDR控制器、高速SerDes)→ 用手写RTL
  • 控制逻辑复杂的模块(状态机、协议解析)→ 看情况,我倾向RTL

我的经验:别把HLS当万能药。我在一个项目中用HLS做PCIe DMA控制器,结果折腾了两周,综合出来的逻辑面积比手写大了3倍。后来老老实实换回Verilog,一天搞定。

三、开发环境搭建:Vivado HLS vs Vitis HLS

现在Xilinx主推的是Vitis HLS,但很多老项目还在用Vivado HLS。我建议你直接上Vitis HLS 2023.1以上版本,功能更全,bug更少。

3.1 工具安装步骤

  1. 下载安装包:从Xilinx官网下载Vitis统一安装包(约20GB)
  2. 选择组件:勾选Vitis HLS、Vivado、XRT(运行时)
  3. 安装路径:不要有中文和空格,我习惯装到 C:\Xilinx\Vitis\2023.1
  4. 设置环境变量:
# Windows
set PATH=C:\Xilinx\Vitis\2023.1\bin;%PATH%
set XILINX_HLS=C:\Xilinx\Vitis\2023.1

# Linux
export PATH=/tools/Xilinx/Vitis/2023.1/bin:$PATH
export XILINX_HLS=/tools/Xilinx/Vitis/2023.1

3.2 License配置(避坑指南)

我曾经因为License问题折腾了一整天,最后发现是环境变量没配对。

注意:Vitis HLS需要单独的HLS License,和Vivado的License不通用!

配置步骤:

  • 将License文件放到 C:\Xilinx\licenses 目录
  • 设置环境变量 XILINXD_LICENSE_FILE 指向该文件
  • 或者用 lmutil lmstat -a 检查License服务器状态

四、开发流程总览:一张图说清楚

下面这张SVG图,是我自己总结的HLS开发全流程。你看一遍就能记住:

HLS开发全流程 步骤1:C/C++设计 编写算法代码 步骤2:C仿真验证 验证功能正确性 步骤3:HLS综合 生成RTL代码 步骤4:分析 性能/资源报告 不满足要求?优化代码 步骤5:RTL协同仿真 验证RTL行为 步骤6:IP封装导出 生成IP核 步骤7:Vivado集成 综合、布局布线

这个流程我走了不下50遍。最关键的环节其实是步骤2的C仿真和步骤4的分析。很多人急着往下走,结果综合出来的电路性能不达标,又得回头改代码。

我的建议:在C仿真阶段多花点时间,把testbench写全。我一般会写三种测试:功能测试、边界测试、性能测试。这样后面综合出来的RTL基本一次过。

五、第一个HLS工程:从零开始

光说不练假把式。我们来搭一个最简单的HLS工程:

// 文件名:add.cpp
#include "hls_stream.h"

void add(hls::stream<int> &in1, hls::stream<int> &in2, hls::stream<int> &out) {
    #pragma HLS INTERFACE axis port=in1
    #pragma HLS INTERFACE axis port=in2
    #pragma HLS INTERFACE axis port=out
    
    int a = in1.read();
    int b = in2.read();
    out.write(a + b);
}

这段代码实现了一个简单的加法器。注意看那几个 #pragma HLS INTERFACE 指令,它们告诉工具:这些端口用AXIS协议。

综合之后,你会看到类似这样的报告:

指标数值
Latency(延迟)2个时钟周期
II(初始化间隔)1个时钟周期
LUT使用12个
FF使用8个

嗯,这个结果还不错。对于这么简单的逻辑,手写RTL大概也就这个水平。

记住:HLS不是魔法。你写的C代码质量,直接决定了综合出来的硬件质量。好的C代码 + 合适的pragma = 接近手写RTL的性能。

好了,这一章的内容就到这里。工具装好了,流程也清楚了,下一章我们开始实战——写一个真正的图像处理加速器。

可可精品资料库