Quick Start
- 准备环境:安装 Vivado 2024.2(或更高版本),确保支持 Xilinx Artix-7 / Kintex-7 系列器件。
- 创建工程:在 Vivado 中新建 RTL 工程,选择器件 xc7a100tcsg324-1(Artix-7 典型配置)。
- 添加设计文件:将本文提供的卷积加速器顶层模块(conv_acc_top.v)和 BRAM 包装器(bram_wrapper.v)加入工程。
- 添加约束文件:创建 XDC 文件,定义时钟(100 MHz)、复位(低有效)和输入/输出端口管脚。
- 运行综合:点击 “Run Synthesis”,等待完成,检查无严重警告或错误。
- 运行实现:综合通过后,点击 “Run Implementation”,完成布局布线。
- 生成比特流:实现完成后,点击 “Generate Bitstream”,生成 .bit 文件。
- 上板验证:将比特流下载至 FPGA 板卡(如 Nexys A7-100T),通过串口或 VIO 观察卷积结果与预期匹配。
前置条件与环境
| 项目 | 推荐值 | 说明 | 替代方案 |
|---|---|---|---|
| 器件/板卡 | Xilinx Artix-7 (xc7a100tcsg324-1) | BRAM 资源丰富,适合中小规模卷积加速 | Kintex-7 / Spartan-7 |
| EDA 版本 | Vivado 2024.2 | 支持最新 BRAM 原语与综合优化 | Vivado 2023.1+ |
| 仿真器 | Vivado Simulator (xsim) | 内置于 Vivado,无需额外安装 | ModelSim / Questa |
| 时钟 | 100 MHz 单端时钟 | 由板载晶振提供,经 MMCM 生成 | 50 MHz / 200 MHz(需调整时序约束) |
| 复位 | 低有效异步复位 | 上电后复位所有内部寄存器 | 高有效复位(需修改 RTL) |
| 接口依赖 | UART 或 VIO | 用于读取卷积结果(调试阶段) | AXI4-Stream / 自定义并行接口 |
| 约束文件 | timing.xdc | 定义时钟周期 10 ns,输入/输出延迟 | 默认约束(可能不满足时序) |
目标与验收标准
- 功能点:实现一个基于 BRAM 的 3×3 卷积加速器,支持单通道灰度图像(8位像素)的卷积运算,输出 8 位结果(饱和截断)。
- 性能指标:在 100 MHz 时钟下,处理一幅 64×64 图像(共 4096 像素)的卷积延迟不超过 50 μs(含行缓冲刷新)。
- 资源指标:BRAM 使用量 ≤ 4 个(每个 36 Kb),LUT ≤ 800,FF ≤ 600。
- 验收方式:通过仿真波形验证卷积结果与软件参考(如 Python 脚本)一致;上板后通过 VIO 读取 10 个输出像素值,与仿真结果匹配。
实施步骤
工程结构与关键模块
创建以下文件结构:
conv_accelerator/
├── rtl/
│ ├── conv_acc_top.v # 顶层模块
│ ├── bram_wrapper.v # BRAM 包装器(双端口 BRAM)
│ ├── line_buffer.v # 行缓冲(移位寄存器实现)
│ ├── mac_unit.v # 乘累加单元
│ └── control_fsm.v # 控制状态机
├── sim/
│ ├── tb_conv_acc.v # 测试平台
│ └── input_image.hex # 输入图像数据(十六进制)
├── constraints/
│ └── timing.xdc # 时序约束
└── scripts/
└── run_sim.tcl # 仿真运行脚本逐行说明
- 第 1 行:定义顶层模块 conv_acc_top,包含时钟、复位、输入像素数据、输出像素数据及握手信号。
- 第 2 行:bram_wrapper.v 封装了 Xilinx 的 BRAM 原语(RAMB36E1),实现双端口读写,用于存储卷积核系数。
- 第 3 行:line_buffer.v 使用移位寄存器实现 3 行缓冲,每行 64 像素,采用 BRAM 或分布式 RAM 实现。
- 第 4 行:mac_unit.v 包含 9 个乘法器和 1 个加法树,在 1 个时钟周期内完成 3×3 窗口的乘累加。
- 第 5 行:control_fsm.v 实现状态机,管理行缓冲填充、卷积窗口滑动和输出数据有效信号。
- 第 6-8 行:仿真文件与约束文件,用于验证和上板。
关键 RTL 实现:BRAM 包装器
module bram_wrapper #(
parameter DATA_WIDTH = 8,
parameter ADDR_WIDTH = 4, // 16 个系数(3x3+1 偏置)
parameter INIT_FILE = "coeff.mem"
)(
input wire clk,
input wire we,
input wire [ADDR_WIDTH-1:0] addr_w,
input wire [DATA_WIDTH-1:0] din_w,
input wire [ADDR_WIDTH-1:0] addr_r,
output wire [DATA_WIDTH-1:0] dout_r
);
// 实例化 Xilinx BRAM 原语
(* ram_style = "block" *) reg [DATA_WIDTH-1:0] mem [0:(1<<ADDR_WIDTH)-1];
always @(posedge clk) begin
if (we)
mem[addr_w] <= din_w;
dout_r <= mem[addr_r];
end
endmodule逐行说明
- 第 1 行:模块定义开始,使用参数化设计,DATA_WIDTH 为数据位宽(默认 8 位),ADDR_WIDTH 为地址位宽(默认 4 位,可寻址 16 个系数),INIT_FILE 指定初始化文件。
- 第 2 行:参数 DATA_WIDTH 定义,默认值为 8,表示每个系数占 8 位。
- 第 3 行:参数 ADDR_WIDTH 定义,默认值为 4,对应 2^4=16 个存储单元,用于存放 3×3 卷积核的 9 个系数和 1 个偏置(剩余 6 个保留)。
- 第 4 行:参数 INIT_FILE 定义,指定初始化文件路径,用于在综合/实现时预加载 BRAM 内容。
- 第 5 行:端口列表开始。
- 第 6 行:时钟输入端口 clk,所有操作同步于时钟上升沿。
- 第 7 行:写使能信号 we,高电平有效,控制写入操作。
- 第 8 行:写地址端口 addr_w,宽度由 ADDR_WIDTH 决定,用于指定写入位置。
- 第 9 行:写数据端口 din_w,宽度由 DATA_WIDTH 决定,携带待写入数据。
- 第 10 行:读地址端口 addr_r,用于指定读取位置。
- 第 11 行:读数据输出端口 dout_r,宽度由 DATA_WIDTH 决定,输出读取到的数据。
- 第 12 行:端口列表结束。
- 第 14 行:声明一个二维寄存器数组 mem,用于模拟 BRAM 存储行为。使用 ram_style = “block” 属性,指示综合工具将其推断为块 RAM(BRAM),而非分布式 RAM。
- 第 16 行:always 块开始,敏感列表为时钟上升沿(posedge clk),实现同步读写。
- 第 17 行:条件语句,当 we 为高电平时执行写入操作:将 din_w 写入 mem 的 addr_w 地址处。
- 第 18 行:无条件读取操作:在每个时钟上升沿,将 mem[addr_r] 的值赋给 dout_r。注意:此为同步读,读取结果在下一个时钟周期有效。
- 第 19 行:always 块结束。
- 第 21 行:模块定义结束。
验证结果
完成上述步骤后,通过仿真验证卷积结果。使用提供的测试平台 tb_conv_acc.v 和输入图像数据 input_image.hex,运行仿真脚本 run_sim.tcl。观察输出像素值,与 Python 参考脚本(如使用 NumPy 的卷积函数)计算结果对比,误差应在 1 LSB 以内(因饱和截断引入)。上板后,通过 VIO 读取 10 个输出像素值,应完全匹配仿真结果。
排障指南
- 综合错误:BRAM 未推断——检查代码中是否遗漏 ram_style 属性,或数组声明方式不符合 BRAM 模板(如使用非整数索引)。确保 mem 为二维寄存器数组,且读写操作在同步块内。
- 时序违规——若 100 MHz 时钟下出现建立时间违例,可尝试降低时钟频率至 80 MHz,或优化组合逻辑路径(如乘法器采用流水线结构)。
- 仿真结果不匹配——检查卷积核系数是否正确加载(coeff.mem 文件格式),以及行缓冲的初始状态是否为零。确认控制状态机在图像边界处正确处理(如填充零)。
- 上板无输出——验证复位信号是否有效,时钟是否正常。使用 VIO 观察内部状态机状态,确认是否进入工作状态。
扩展建议
- 支持多通道:将 BRAM 深度扩展为 3×K(K 为输入通道数),每个通道独立存储系数,MAC 单元并行计算后累加。
- 流水线优化:在 MAC 单元内部插入寄存器,将乘法与加法分离为两个流水级,提升时钟频率至 150 MHz 以上。
- 图像尺寸自适应:通过参数化行缓冲深度(LINE_BUF_SIZE),支持不同宽度的图像,但需注意 BRAM 资源消耗随行宽线性增长。
- AXI4-Stream 接口:将输入/输出改为 AXI4-Stream 协议,便于与视频处理 IP 核(如 VDMA)集成。
参考
- Xilinx UG473: 7 Series FPGAs Memory Resources User Guide
- Xilinx UG901: Vivado Design Suite User Guide: Synthesis
- Xilinx UG949: Vivado Design Suite User Guide: Implementation
- 卷积神经网络加速器设计相关论文(如 DianNao 系列)
附录
附录 A:coeff.mem 文件格式示例
@00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F每行以地址起始(@00),后跟 16 个十六进制字节,对应 16 个系数(前 9 个为 3×3 卷积核,第 10 个为偏置,其余保留)。
附录 B:Python 参考脚本
import numpy as np
def conv2d(image, kernel):
h, w = image.shape
kh, kw = kernel.shape
out_h, out_w = h - kh + 1, w - kw + 1
result = np.zeros((out_h, out_w), dtype=np.uint8)
for i in range(out_h):
for j in range(out_w):
window = image[i:i+kh, j:j+kw]
acc = np.sum(window * kernel)
result[i, j] = np.clip(acc, 0, 255)
return result该脚本用于生成参考卷积结果,与 FPGA 输出比对。

评论 0