1. 核心架构:异构计算模型 🧠
-
Host (主机端):运行在服务器 CPU 上,使用标准 C++ 和 XRT 库编写。负责数据准备、内存分配以及调度底层硬件。
-
Kernel (内核端):运行在 U280 FPGA 上,负责实际的并行加速计算。通过 Vitis HLS 工具,可以用 C/C++ 来描述硬件逻辑。
2. Kernel 端开发 (硬件接口映射) ⚡
为了让 C++ 循环变成 U280 上的真实电路,我们使用了 #pragma 编译指示:
-
m_axi(高带宽内存接口) :负责海量数据传输。我们将数组绑定到不同的物理通道(如gmem0,gmem1),让 FPGA 能够同时从 HBM 内存中读写不同数据,实现并行。 -
s_axilite(轻量级控制接口) :负责控制信号和参数传递。我们将指针基地址、标量(如长度N)以及启动/停止信号统一绑定到control寄存器上,供 CPU 读写。
3. Host 端开发 (XRT 控制逻辑) 💻
主机程序扮演着"指挥官"的角色,核心数据流转包括四个步骤:
-
创建 Buffer 📦:在 U280 的 HBM 内存中按字节数分配缓冲区,为数据"占座"。
-
H2D 传输 (Host to Device):通过 PCIe 总线,将待计算的数据从 CPU 内存搬运到 FPGA 的 HBM 中。
-
触发执行 :传递参数并下达执行指令,CPU 通过
wait()指令等待 FPGA 闪电般的计算完成。 -
D2H 传输 (Device to Host):将计算结果从 FPGA 的 HBM 搬运回 CPU 内存,供后续打印或保存。
4. 编译与仿真工作流 🛠️
-
编译产物 :Host 代码被编译为普通的可执行文件(如
host_app),Kernel 代码被编译为硬件配置压缩包(.xclbin)。在终端运行时,将.xclbin作为参数传给host_app来启动整个系统。 -
平台 (Platform) :编译时必须指定目标平台(如
xilinx_u280_...),它包含了板卡的底层硬件拓扑结构。 -
阶梯式开发:
-
sw_emu(软件仿真):秒级编译,完全在 CPU 上运行,专用于快速验证算法逻辑的正确性。 -
hw_emu(硬件仿真):模拟底层硬件时序,用于评估周期和通道拥堵情况。 -
hw(纯硬件):耗时数小时进行综合与布线,生成最终部署到 U280 物理板卡上的文件。
-
异构计算(简单了解)
异构编程生态
学习 XRT (Xilinx Runtime) Native C++ API 是非常明智的选择,它是 AMD/Xilinx
官方目前主推的、比 OpenCL 更轻量且更符合现代 C++ 规范的接口。
以下是系统学习 XRT Native API 的路径和核心知识点:
1. 核心概念对比(从 OpenCL 迁移)
理解 XRT Native API 最快的方法是将其与你当前工程中的 OpenCL 概念进行对比: ┌────────────┬────────────────────────────┬─────────────────────────────────┐
│ 功能 │ OpenCL API (旧) │ XRT Native API (新) │
├────────────┼────────────────────────────┼─────────────────────────────────┤
│ 设备对象 │ cl::Device │ `xrt::device`
│
│ **加载硬... │ cl::Program │ `xrt::uuid` (通过 `device.load_x...
│
│ 内核对象 │ cl::Kernel │ `xrt::kernel`
│
│ 内存缓冲区 │ cl::Buffer │ `xrt::bo` (Buffer Object)
│
│ 数据同步 │ enqueueMigrateMemObjects │ `bo.sync()`
│
│ 执行内核 │ `cl::CommandQueue::enqueu... │ `kernel()` (像函数一样直接调用)
│
└────────────┴────────────────────────────┴─────────────────────────────────┘
2. 最小代码框架 (Hello World)
一个典型的 XRT Native C++ 主机端程序逻辑如下:
cpp
// ==========================================
// 传统 OpenCL API 主机端代码示例 (极度简化版)
// ==========================================
// 1. 获取平台和设备
cl_platform_id platform;
clGetPlatformIDs(1, &platform, NULL);
cl_device_id device;
clGetDeviceIDs(platform, CL_DEVICE_TYPE_ACCELERATOR, 1, &device, NULL);
// 2. 创建上下文和命令队列 (繁琐的初始化)
cl_context context = clCreateContext(0, 1, &device, NULL, NULL, NULL);
cl_command_queue q = clCreateCommandQueue(context, device, 0, NULL);
// 3. 加载 xclbin 硬件比特流并创建 Program (省略了读取文件的几十行代码)
cl_program program = clCreateProgramWithBinary(context, 1, &device, &fileBufSize, (const unsigned char**)&fileBuf, NULL, NULL);
// 4. 创建内核
cl_kernel kernel = clCreateKernel(program, "vadd", NULL);
// 5. 在 FPGA 显存中分配 Buffer
cl_mem buffer_in1 = clCreateBuffer(context, CL_MEM_READ_ONLY, size_in_bytes, NULL, NULL);
cl_mem buffer_in2 = clCreateBuffer(context, CL_MEM_READ_ONLY, size_in_bytes, NULL, NULL);
cl_mem buffer_out = clCreateBuffer(context, CL_MEM_WRITE_ONLY, size_in_bytes, NULL, NULL);
// 6. 逐个设置内核参数 (非常容易出错,写错索引直接崩溃)
clSetKernelArg(kernel, 0, sizeof(cl_mem), &buffer_in1);
clSetKernelArg(kernel, 1, sizeof(cl_mem), &buffer_in2);
clSetKernelArg(kernel, 2, sizeof(cl_mem), &buffer_out);
clSetKernelArg(kernel, 3, sizeof(int), &size);
// 7. 将数据从主机拷贝到 FPGA,并排队执行内核
clEnqueueWriteBuffer(q, buffer_in1, CL_TRUE, 0, size_in_bytes, host_in1, 0, NULL, NULL);
clEnqueueWriteBuffer(q, buffer_in2, CL_TRUE, 0, size_in_bytes, host_in2, 0, NULL, NULL);
clEnqueueTask(q, kernel, 0, NULL, NULL); // 触发执行
// 8. 等待执行完毕并读回结果
clEnqueueReadBuffer(q, buffer_out, CL_TRUE, 0, size_in_bytes, host_out, 0, NULL, NULL);
clFinish(q);
// 9. 手动清理所有资源 (如果中途报错退出,这里没执行就会内存泄漏)
clReleaseMemObject(buffer_in1);
clReleaseMemObject(buffer_in2);
clReleaseMemObject(buffer_out);
clReleaseKernel(kernel);
clReleaseProgram(program);
clReleaseCommandQueue(q);
clReleaseContext(context);
3. 核心学习资源 (必读)
- 官方文档 UG1393 (Vitis 用户指南):
* 重点阅读 "XRT Native API" 章节。这是最权威的参考资料。
- XRT 入门示例 (GitHub):
* 访问 Xilinx/Vitis_Accel_Examples
(https://github.com/Xilinx/Vitis_Accel_Examples)。
* 搜索标注有 xrt_native 的示例(通常在 host/ 目录下可以看到使用了
xrt_kernel.h 而非 xcl2.hpp 的代码)。
- XRT 官方 API 文档:
(https://xilinx.github.io/XRT/master/html/index.html),查看完整的 C++
API 引用手册。
4. 实践建议
* 环境准备:确保你的系统中安装了 xrt-dev 包。在代码中包含头文件
<xrt/xrt_device.h>。
* 编译设置:在 Makefile 中,你需要链接 xrt_coreutil 库。
* 编译选项添加:-I$(XILINX_XRT)/include
* 链接选项添加:-L$(XILINX_XRT)/lib -lxrt_coreutil
* 从模仿开始:你可以试着把你现在工程里
Vitis_workflow/examples/vadd_vmul/host/host.cpp 的 OpenCL 代码改写成 XRT
Native 版本。你会发现代码量会减少 30% 以上,逻辑更清晰。
总结
学习 XRT Native API 的门槛比 OpenCL
低得多,因为它隐藏了大量底层的样板代码。如果你已经理解了"内存申请 -> 数据搬运
-> 启动内核 -> 传回结果"这个流程,只需花 1-2 天 熟悉 xrt::device、xrt::bo 和
xrt::kernel 这几个类即可上手。