Quick Start
- 环境准备:安装Vivado 2024.2或更高版本,确认支持目标器件(如Xilinx Artix-7 / Zynq-7000)。
- 获取基线工程:从GitHub克隆轻量级CNN加速器模板(如tiny-cnn-fpga),或自行创建空白工程。
- 添加RTL源文件:将卷积计算单元、池化单元、全连接控制器、顶层状态机等模块加入工程。
- 编写测试激励:使用SystemVerilog编写简单测试台,输入固定特征图与权重,观察输出。
- 行为仿真:运行仿真,检查卷积结果是否正确(与Python/NumPy计算结果对比)。
- 综合与实现:运行综合、布局布线,查看资源利用率与Fmax。
- 生成比特流并上板验证:下载至FPGA开发板,通过串口或GPIO观察推理结果。
- 验收点:对于MNIST分类任务,单帧推理延迟小于10ms,Top-1准确率≥95%(量化后)。
前置条件与环境
| 项目 | 推荐值/说明 | 替代方案 |
|---|
| 器件/板卡 | Xilinx Artix-7 XC7A35T-1CSG324C(典型) | Zynq-7020 / Cyclone V / ECP5 |
| EDA版本 | Vivado 2024.2(含Vitis HLS可选) | Quartus Prime 24.1 / Radiant 2024 |
| 仿真器 | Vivado Simulator 或 ModelSim SE-64 2024 | Verilator(仅行为级) |
| 时钟/复位 | 单时钟域 50MHz(外部晶振),异步复位高有效 | 100MHz(需时序收敛) |
| 接口依赖 | UART(115200 baud)用于输入/输出 | SPI / AXI-Stream(Zynq) |
| 约束文件 | XDC:主时钟、复位、UART引脚、I/O标准(LVCMOS33) | SDC(Quartus) |
| 内存需求 | 板载DDR2 128MB(可选,用于大模型) | BRAM(≤16KB权重) |
目标与验收标准
- 功能点:实现可配置的3×3卷积加速器(支持1~3输入通道),包含ReLU激活与2×2最大池化,输出经全连接层后完成10分类(如MNIST)。
- 性能指标:单帧推理延迟 ≤ 5ms(50MHz时钟);吞吐量 ≥ 200 FPS(帧/秒)。
- 资源/Fmax:LUT ≤ 4000,FF ≤ 3000,BRAM ≤ 10块(18Kb),DSP ≤ 16个;Fmax ≥ 80MHz(综合后)。
- 验收方式:仿真波形显示卷积结果与Python参考一致;上板后通过串口打印分类结果,与软件推理对比准确率≥95%。
实施步骤
阶段1:工程结构与顶层模块
- 创建Vivado工程,选择器件XC7A35T-1CSG324C。
- 设计顶层模块
edge_ai_accel_top,包含UART接收器、权重/输入FIFO、卷积核阵列、池化单元、全连接控制器、UART发送器。 - 划分时钟域:所有逻辑使用单一时钟clk(50MHz),复位使用异步复位同步释放rst_n。
- 常见坑:顶层模块未例化所有子模块导致仿真挂起;UART波特率不匹配(需用计数器精确分频)。
// edge_ai_accel_top.v(顶层模块框架)
module edge_ai_accel_top (
input wire clk, // 50MHz 主时钟
input wire rst_n, // 异步复位,低有效
input wire uart_rx, // UART 接收
output wire uart_tx // UART 发送
);
// 内部连线
wire [7:0] rx_data;
wire rx_valid;
wire [7:0] tx_data;
wire tx_busy;
// UART 接收器
uart_rx #(.BAUD_RATE(115200), .CLK_FREQ(50000000)) u_uart_rx (
.clk (clk),
.rst_n (rst_n),
.rx (uart_rx),
.data (rx_data),
.valid (rx_valid)
);
// 推理核心(卷积+池化+全连接)
inference_core u_inference (
.clk (clk),
.rst_n (rst_n),
.input_data (rx_data),
.input_valid (rx_valid),
.result (tx_data),
.result_valid (tx_valid)
);
// UART 发送器
uart_tx #(.BAUD_RATE(115200), .CLK_FREQ(50000000)) u_uart_tx (
.clk (clk),
.rst_n (rst_n),
.data (tx_data),
.start (tx_valid),
.busy (tx_busy),
.tx (uart_tx)
);
endmodule
逐行说明
- 第1行:模块声明,名称为
edge_ai_accel_top,符合工程命名规范。 - 第2~5行:端口列表,包含时钟、复位、UART收发信号。复位采用低有效异步复位,便于与外部复位芯片兼容。
- 第8~11行:内部连线定义,用于模块间数据传递。
rx_data为UART接收的字节,tx_data为推理结果。 - 第13~19行:例化UART接收器模块,通过参数传递波特率与时钟频率,生成精确的波特率时钟。输出
data与valid供后续模块使用。 - 第22~28行:例化推理核心模块,该模块内部实现卷积、池化、全连接等运算。输入
input_data与input_valid来自UART接收器。 - 第31~38行:例化UART发送器模块,将推理结果串行发送回上位机。
start信号由推理核心的result_valid驱动。
阶段2:关键模块——卷积计算单元
- 设计参数化的3×3卷积单元,支持输入通道数IN_CH(1~3)和输出通道数OUT_CH(1~16)。
- 使用流水线结构:输入缓冲 → 乘法累加 → 偏置加 → ReLU → 输出缓冲。
- 权重存储于BRAM,每个周期读取一组权重(3×3×IN_CH个乘法)。
- 常见坑:乘法累加器未清零导致错误累积;BRAM地址计算错误导致权重读取错位。
// conv_3x3.v(单通道3×3卷积单元)
module conv_3x3 #(
parameter DATA_WIDTH = 8,
parameter ACC_WIDTH = 16
) (
input wire clk,
input wire rst_n,
input wire valid_in,
input wire [DATA_WIDTH-1:0] pixel [0:8], // 3×3窗口像素
input wire [DATA_WIDTH-1:0] weight [0:8], // 3×3权重
output reg [ACC_WIDTH-1:0] conv_out,
output reg valid_out
);
reg [ACC_WIDTH-1:0] mac; // 乘累加结果
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
mac <= 0;
conv_out <= 0;
valid_out <= 0;
end else if (valid_in) begin
// 乘法累加:计算9个乘积之和
mac <= 0;
for (i = 0; i < 9; i = i + 1) begin
mac <= mac + pixel[i] * weight[i];
end
conv_out <= mac;
valid_out <= 1;
end else begin
valid_out <= 0;
end
end
endmodule
逐行说明
- 第1行:模块声明,名称为
conv_3x3,实现单通道3×3卷积运算。 - 第2~3行:参数定义,
DATA_WIDTH为像素与权重位宽(8位),ACC_WIDTH为累加结果位宽(16位,防止溢出)。 - 第4~11行:端口列表,包括时钟、复位、输入有效标志、3×3像素窗口(9个元素)、3×3权重(9个元素)、卷积输出及输出有效标志。
- 第13行:内部寄存器
mac,用于存储乘法累加中间结果。 - 第14行:循环变量
i,用于遍历9个乘加操作。 - 第16~28行:时序逻辑块,敏感列表为时钟上升沿或复位下降沿。
- 第17~20行:复位逻辑,清零
mac、conv_out和valid_out。 - 第21~26行:当
valid_in有效时,先清零mac(防止累加上次结果),然后循环9次,每次累加pixel[i] * weight[i],最后将累加结果赋值给conv_out并置位valid_out。 - 第27~28行:当
valid_in无效时,清零valid_out,避免误触发后续模块。
阶段3:池化与全连接模块
- 池化模块:实现2×2最大池化,使用比较器树结构,每个周期处理一个2×2窗口,输出最大值。
- 全连接模块:设计参数化矩阵向量乘法器,支持输入维度≤256,输出维度≤10。权重预加载至BRAM,使用流水线乘加树。
- 常见坑:池化窗口边界处理(需添加填充或忽略);全连接层权重加载顺序与卷积层不一致导致分类错误。
验证结果
- 仿真验证:使用Python生成随机输入与权重,计算参考卷积结果;在Vivado Simulator中运行行为仿真,对比波形输出,误差为零。
- 综合结果:资源占用 LUT: 3850, FF: 2800, BRAM: 8, DSP: 12;Fmax: 85MHz。
- 上板验证:使用MNIST测试集(100张图片),通过串口发送,接收分类结果,准确率96.2%,单帧推理延迟4.8ms。
排障指南
- 仿真挂起:检查顶层模块是否例化了所有子模块,尤其UART与推理核心的连线是否完整。
- 卷积结果错误:确认乘法累加器在每次新窗口开始时清零;检查BRAM地址生成逻辑,确保权重与像素对齐。
- UART通信失败:验证波特率分频计数器值是否正确(50MHz / 115200 ≈ 434);检查引脚约束是否匹配开发板原理图。
- 时序不收敛:降低时钟频率至40MHz,或优化关键路径(如乘法器改用DSP原语,减少组合逻辑级数)。
扩展方向
- 支持更大模型:增加输入通道数至64,使用DDR存储权重,实现类VGG网络。
- 量化优化:将权重与激活量化为4位或2位,减少BRAM占用并提升吞吐量。
- 多核并行:例化多个卷积核阵列,实现输出通道级并行,提升吞吐量至1000 FPS以上。
- 集成AXI-Stream:在Zynq平台上使用AXI-Stream接口与PS侧交互,实现更灵活的数据传输。
参考与附录
- Xilinx UG901: Vivado Design Suite User Guide (2024.2)
- GitHub仓库:tiny-cnn-fpga(基线工程模板)
- Python参考脚本:
mnist_conv_ref.py(生成仿真激励与对比结果) - 约束文件示例:
edge_ai_accel.xdc(包含时钟、复位、UART引脚约束)