在FPGA加速器开发中,数据流的高效处理往往是性能优化的关键所在。Vitis HLS提供的hls::stream作为硬件数据流的C++抽象,其接口配置直接影响最终生成的RTL电路性能与资源利用率。本文将从一个实际图像处理模块出发,深入剖析AXI-Stream与ap_fifo两种接口的配置差异、实现原理及工程实践中的典型问题解决方案。
两种接口在信号组成、握手机制和适用场景上存在本质差异:
| 特性 | AXI-Stream | ap_fifo |
|---|---|---|
| 协议复杂度 | 支持多通道、数据包传输 | 简单点对点传输 |
| 信号组成 | TDATA/TKEEP/TLAST等标准信号 | 仅有数据线+空满标志 |
| 吞吐量潜力 | 支持突发传输和高带宽应用 | 适合稳定速率的数据流 |
| 资源消耗 | 接口逻辑较复杂 | 实现极为轻量 |
| 典型应用场景 | 跨模块通信、DMA传输 | 内部流水线数据传递 |
提示:选择接口时需权衡协议开销与功能需求,AXI-Stream适合异构系统间通信,而ap_fifo更适合模块内部数据传递。
两种接口在代码层面的配置方式截然不同:
AXI-Stream配置示例:
cpp复制#include "ap_axi_sdata.h"
typedef ap_axiu<32, 0, 0, 0> axis_pkt;
void process_image(
hls::stream<axis_pkt> &input,
hls::stream<axis_pkt> &output) {
#pragma HLS INTERFACE axis port=input
#pragma HLS INTERFACE axis port=output
// 处理逻辑...
}
ap_fifo配置示例:
cpp复制void data_filter(
hls::stream<uint16_t> &raw_data,
hls::stream<uint16_t> &filtered_data) {
#pragma HLS INTERFACE ap_fifo port=raw_data
#pragma HLS INTERFACE ap_fifo port=filtered_data
// 过滤逻辑...
}
关键区别点:
hls::stream在硬件中实现为FIFO时,深度配置直接影响系统吞吐量和资源占用:
cpp复制// 显式指定深度为16的stream
hls::stream<float, 16> data_stream;
// 通过指令动态设置深度
#pragma HLS STREAM variable=data_stream depth=32
深度计算经验公式:
code复制所需深度 = 生产者最大突发写入量 - 消费者同期处理量 + 安全余量
典型场景建议值:
当FIFO深度配置不当时,综合报告会显示以下警告:
code复制WARNING: [XFORM-203] Dataflow checking...
Potential stall at 'data_stream' due to insufficient depth
硬件仿真中可能表现为:
以32位数据宽度为例,生成的RTL接口信号如下:
| C++层面 | RTL信号 | 方向 | 描述 |
|---|---|---|---|
| stream.read() | TVALID | 输入 | 数据有效标志 |
| TREADY | 输出 | 接收准备标志 | |
| TDATA[31:0] | 输入 | 主数据通道 | |
| TLAST | 输入 | 包结束标志 | |
| TKEEP[3:0] | 输入 | 字节有效指示 |
调试技巧:
#pragma HLS INTERFACE axis register_mode=both可改善时序相同数据宽度的ap_fifo接口信号明显更简单:
| C++层面 | RTL信号 | 方向 | 描述 |
|---|---|---|---|
| stream.read() | fifo_rd_en | 输出 | 读使能 |
| fifo_dout | 输入 | 数据输出 | |
| fifo_empty | 输入 | 空状态标志 | |
| stream.write() | fifo_wr_en | 输出 | 写使能 |
| fifo_din | 输出 | 数据输入 | |
| fifo_full | 输入 | 满状态标志 |
注意:ap_fifo接口不支持突发传输,每次读写只能处理单个数据
我们构建一个完整的图像处理流水线,展示混合接口的使用:
code复制AXI-Stream输入 → 色彩转换(ap_fifo) → 滤波处理(ap_fifo) → AXI-Stream输出
关键实现代码:
cpp复制// 色彩转换模块
void color_convert(
hls::stream<rgb_pixel> &in_fifo,
hls::stream<yuv_pixel> &out_fifo) {
#pragma HLS INTERFACE ap_fifo port=in_fifo
#pragma HLS INTERFACE ap_fifo port=out_fifo
#pragma HLS PIPELINE II=1
// 转换逻辑...
}
// 顶层集成
void image_pipeline(
hls::stream<axis_pkt> &axi_in,
hls::stream<axis_pkt> &axi_out) {
#pragma HLS INTERFACE axis port=axi_in
#pragma HLS INTERFACE axis port=axi_out
#pragma HLS DATAFLOW
hls::stream<rgb_pixel, 8> color_fifo;
hls::stream<yuv_pixel, 8> filter_fifo;
axi_to_fifo(axi_in, color_fifo);
color_convert(color_fifo, filter_fifo);
filter_chain(filter_fifo, axi_out);
}
在实际项目中,我们通过以下手段提升吞吐量:
cpp复制#pragma HLS INTERFACE axis port=axi_out bundle=OUTPUT
#pragma HLS INTERFACE axis port=axi_in bundle=INPUT
cpp复制typedef ap_axiu<128, 0, 0, 0> wide_axis_pkt; // 128位总线提升带宽
cpp复制#pragma HLS PIPELINE II=1 rewind
#pragma HLS occurrence cycle=2
经过实测,在Xilinx Alveo U200卡上,优化后的设计可实现: