前言
本文基于单卷积基础 CNN 流程图,完整拆解每一层理论知识点,搭配FPGA 可综合 Verilog 硬件工程实例 (面向手写数字 MNIST 识别场景),同时配套选择、简答、计算、硬件代码习题并逐题讲解,适合深度学习硬件加速、数字 IC、FPGA 图像处理入门学习,可直接发布技术博客。 网络完整数据流链路: 图像输入层 → Conv卷积层 → ReLU激活层 → Pooling池化层 → Affine全连接层 → ReLU激活层 → Affine全连接层 → Softmax输出层 整体分为两大模块:卷积特征提取模块(Conv+ReLU+Pooling) + 分类头全连接模块(两层 Affine+ReLU+Softmax)
一、CNN 流程图分层完整知识点
1. 整体架构逻辑
- 特征提取模块 Conv 卷积提取图像边缘、纹理局部特征 → ReLU 引入非线性 → Pooling 下采样压缩特征尺寸,降低后续硬件计算量,是 CNN 区别于纯全连接网络的核心。
- 分类拟合模块 将二维特征图展平为一维向量,两层全连接融合全局特征,最后 Softmax 输出多分类概率,完成图像识别任务。
2. 各层理论 + FPGA 硬件特性详解
(1)输入层:图像数据
- 软件格式:四维张量
[N,H,W,C];FPGA 硬件:串行 / 并行像素数据流,定点量化(int8/int16),浮点无法直接硬件实现。 - 实例:MNIST 手写数字 28×28 单通道灰度图,像素 0~255,FPGA 存储到 Block RAM 缓存逐行读取。
- 硬件关键点:图像缓存 FIFO/BRAM,像素并行输入通道设计。
(2)Conv 卷积层(CNN 核心)
- 理论原理:卷积核滑动窗口加权求和,提取局部特征;权值共享、局部感受野、平移不变性三大优势。 尺寸公式:Hout=⌊SHin+2P−K⌋+1
- FPGA 硬件实现要点:
- 卷积核权重预存 BRAM,权值共享无需重复存储;
- 滑动窗口使用移位寄存器阵列构建像素窗口;
- 并行乘累加 MAC 单元加速卷积运算,流水线流水线提升吞吐;
- 全部运算定点量化,消除浮点运算。
- 可训练参数:卷积权重、偏置,硬件固化为 ROM/BRAM。
(3)ReLU 激活函数
- 公式:f(x)=max(0,x),负数直接置 0,正数保持原值。
- 硬件实现:无需乘法器,仅比较器即可完成,资源消耗极低;
- 作用:引入非线性,否则多层线性等价单层,网络无法拟合复杂图像特征;缓解梯度消失。
(4)Pooling 池化层(MaxPool 为主流)
- 主流最大池化:窗口内取最大值;无任何可训练参数。
- 硬件逻辑:寄存器缓存窗口内所有特征值,多路比较器输出最大值;
- 功能:下采样压缩特征图尺寸,减少后续全连接 MAC 运算量,降低 FPGA 资源消耗。
(5)Affine 全连接层(仿射变换)
- 公式:y=Wx+b,大量乘累加运算。
- FPGA 痛点:参数量巨大,硬件带宽、存储压力高;优化方案:时分复用 MAC 阵列、权重分块存储 BRAM。
- 前置必要操作:卷积输出二维特征图串行 / 并行展平 Flatten 为一维向量送入全连接。
(6)Softmax 输出层
- 软件:指数 + 归一化浮点运算;FPGA 硬件无法直接实现指数,工程中两种方案:
- 训练时直接取全连接输出 logits 做判决,省略 Softmax;
- 分段线性近似指数函数定点实现,资源开销大,小型 CNN 常省略。
- 功能:将分类得分转为 0~1 概率,多分类任务输出置信度。
3. 该 CNN 结构优缺点(软件 + FPGA 硬件双视角)
优点
- 结构极简,包含 CNN 全部基础算子,适合 FPGA 入门加速器开发;
- 单层卷积 + 单层池化计算量小,FPGA 资源占用低,无需大量 MAC 阵列;
- 算子简单:卷积、ReLU、MaxPool、全连接均易于硬件流水线实现;
- 数据流串行有序,控制逻辑简单,时序收敛难度低。
缺陷
- 单卷积层特征提取能力弱,复杂图像识别精度差;
- 两层全连接参数量大,占用大量 BRAM 存储权重,带宽开销高;
- 无 BN、Dropout 正则层,软件训练易过拟合;
- Softmax 硬件实现成本高,小型 FPGA 工程一般舍弃。
4. 适用硬件场景
低分辨率小图像识别(28×28 MNIST 手写数字)、轻量 FPGA 端嵌入式图像识别、教学级 CNN 加速器验证。
二、工程实例:MNIST 手写数字识别 FPGA Verilog 完整可综合代码
工程参数匹配流程图
输入:28×28 单通道灰度图
- Conv:3×3 卷积核,16 输出通道,padding=1,stride=1,输出 28×28×16
- ReLU 激活
- MaxPool 2×2,stride=2,输出 14×14×16
- Flatten 展平 3136 维 → Affine1 映射 128 维
- ReLU 激活
- Affine2 映射 10 维(0~9 数字分类)
- 硬件简化:省略 Softmax,直接对 10 路 logits 做最大值判决输出识别结果
整体模块划分
plaintext
top_cnn_top
├─ img_ram:图像像素BRAM缓存
├─ conv_3x3:3×3卷积流水线模块
├─ relu_act:ReLU激活模块
├─ max_pool_2x2:2×2最大池化模块
├─ flatten_buf:特征图展平缓存FIFO
├─ affine_layer #1:第一层全连接
├─ relu_act:第二层ReLU
├─ affine_layer #2:第二层全连接
└─ cls_compare:最大值判决模块(替代Softmax)
完整 Verilog 代码(带详细注释,可综合)
1. 顶层文件 top_cnn.v
verilog
module top_cnn #(
parameter IMG_W = 28, // 输入图像宽高
parameter CH_IN = 1, // 输入通道灰度图1
parameter CH_CONV = 16, // 卷积输出通道
parameter KERNEL_SZ= 3, // 卷积核3×3
parameter POOL_SZ = 2, // 池化窗口2×2
parameter FC1_IN = 14*14*16,
parameter FC1_OUT = 128,
parameter FC2_OUT = 10, // 10分类0-9
parameter DATA_W = 8, // 像素定点位宽int8
parameter MAC_W = 20 // 乘累加扩展位宽防止溢出
)(
input clk,
input rst_n,
input img_wr_en,
input [DATA_W-1:0] img_wr_data,
input [9:0] img_wr_addr,
output reg [3:0] cls_result, // 最终识别数字0~9
output reg valid_out
);
// 内部信号定义
wire [DATA_W-1:0] pix_data;
wire conv_valid, relu_conv_valid, pool_valid;
wire [MAC_W-1:0] conv_data [0:CH_CONV-1];
wire [DATA_W-1:0] relu_data [0:CH_CONV-1];
wire [DATA_W-1:0] pool_data [0:CH_CONV-1];
wire flatten_valid;
wire [DATA_W-1:0] flatten_data;
wire fc1_valid;
wire [MAC_W-1:0] fc1_data;
wire fc1_relu_valid;
wire [DATA_W-1:0] fc1_relu_data;
wire fc2_valid;
wire [MAC_W-1:0] fc2_data [0:FC2_OUT-1];
// 1. 图像BRAM缓存
img_ram u_img_ram(
.clk(clk),
.rst_n(rst_n),
.wr_en(img_wr_en),
.wr_addr(img_wr_addr),
.wr_data(img_wr_data),
.rd_addr(img_wr_addr),
.rd_data(pix_data)
);
// 2. 3×3卷积模块
conv_3x3 #(
.IMG_W(IMG_W), .CH_IN(CH_IN), .CH_OUT(CH_CONV),
.K_SZ(KERNEL_SZ), .DATA_W(DATA_W), .MAC_W(MAC_W)
) u_conv(
.clk(clk), .rst_n(rst_n),
.pix_in(pix_data),
.data_out(conv_data),
.valid_out(conv_valid)
);
// 3. ReLU激活(卷积后)
relu_act #(.CH_NUM(CH_CONV), .IN_W(MAC_W), .OUT_W(DATA_W)) u_relu_conv(
.clk(clk), .rst_n(rst_n),
.data_in(conv_data),
.valid_in(conv_valid),
.data_out(relu_data),
.valid_out(relu_conv_valid)
);
// 4. 2×2最大池化
max_pool_2x2 #(
.IN_W(IMG_W), .CH_NUM(CH_CONV), .DATA_W(DATA_W)
) u_pool(
.clk(clk), .rst_n(rst_n),
.data_in(relu_data),
.valid_in(relu_conv_valid),
.data_out(pool_data),
.valid_out(pool_valid)
);
// 5. 特征图展平缓存
flatten_buf #(
.IN_W(IMG_W/POOL_SZ), .CH_NUM(CH_CONV), .DATA_W(DATA_W)
) u_flatten(
.clk(clk), .rst_n(rst_n),
.data_in(pool_data),
.valid_in(pool_valid),
.data_out(flatten_data),
.valid_out(flatten_valid)
);
// 6. 第一层全连接Affine1
affine_layer #(
.IN_DIM(FC1_IN), .OUT_DIM(FC1_OUT),
.DATA_W(DATA_W), .MAC_W(MAC_W)
) u_fc1(
.clk(clk), .rst_n(rst_n),
.data_in(flatten_data),
.valid_in(flatten_valid),
.data_out(fc1_data),
.valid_out(fc1_valid)
);
// 7. 第二层ReLU激活
relu_act #(.CH_NUM(1), .IN_W(MAC_W), .OUT_W(DATA_W)) u_relu_fc1(
.clk(clk), .rst_n(rst_n),
.data_in({fc1_data}),
.valid_in(fc1_valid),
.data_out({fc1_relu_data}),
.valid_out(fc1_relu_valid)
);
// 8. 第二层全连接Affine2
affine_layer #(
.IN_DIM(FC1_OUT), .OUT_DIM(FC2_OUT),
.DATA_W(DATA_W), .MAC_W(MAC_W)
) u_fc2(
.clk(clk), .rst_n(rst_n),
.data_in(fc1_relu_data),
.valid_in(fc1_relu_valid),
.data_out(fc2_data),
.valid_out(fc2_valid)
);
// 9. 多路比较器替代Softmax,输出最大分类编号
cls_compare #(.CLS_NUM(FC2_OUT), .DATA_W(MAC_W)) u_cls(
.clk(clk), .rst_n(rst_n),
.data_in(fc2_data),
.valid_in(fc2_valid),
.cls_num(cls_result),
.valid_out(valid_out)
);
endmodule
2. 子模块 1:3×3 卷积 conv_3x3.v(滑动窗口 MAC 流水线)
verilog
module conv_3x3 #(
parameter IMG_W = 28, CH_IN = 1, CH_OUT = 16,
K_SZ = 3, DATA_W = 8, MAC_W = 20
)(
input clk, rst_n,
input [DATA_W-1:0] pix_in,
output reg [MAC_W-1:0] data_out [0:CH_OUT-1],
output reg valid_out
);
localparam WIN_SZ = K_SZ*K_SZ;
reg [DATA_W-1:0] shift_reg [0:K_SZ*IMG_W-1];
reg [DATA_W-1:0] window [0:WIN_SZ-1];
// 卷积权重ROM,预存训练好的定点卷积核
reg [DATA_W-1:0] weight_rom [0:CH_OUT*WIN_SZ-1];
reg [MAC_W-1:0] mac_sum [0:CH_OUT-1];
integer i,j;
// 移位寄存器构建3×3滑动窗口
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
for(i=0;i<K_SZ*IMG_W;i=i+1) shift_reg[i] <= 0;
end else begin
for(i=1;i<K_SZ*IMG_W;i=i+1) shift_reg[i] <= shift_reg[i-1];
shift_reg[0] <= pix_in;
end
end
// 窗口像素打包
always @* begin
window[0] = shift_reg[0]; window[1] = shift_reg[1]; window[2] = shift_reg[2];
window[3] = shift_reg[IMG_W]; window[4] = shift_reg[IMG_W+1]; window[5] = shift_reg[IMG_W+2];
window[6] = shift_reg[2*IMG_W]; window[7] = shift_reg[2*IMG_W+1]; window[8] = shift_reg[2*IMG_W+2];
end
// 并行MAC乘累加计算各通道卷积输出
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
for(i=0;i<CH_OUT;i=i+1) mac_sum[i] <= 0;
valid_out <= 0;
end else begin
for(i=0;i<CH_OUT;i=i+1) begin
mac_sum[i] = 0;
for(j=0;j<WIN_SZ;j=j+1) begin
mac_sum[i] = mac_sum[i] + window[j] * weight_rom[i*WIN_SZ + j];
end
end
for(i=0;i<CH_OUT;i=i+1) data_out[i] <= mac_sum[i];
valid_out <= 1'b1;
end
end
endmodule
3. 子模块 2:ReLU 激活 relu_act.v
verilog
module relu_act #(
parameter CH_NUM = 16, IN_W = 20, OUT_W = 8
)(
input clk, rst_n, valid_in,
input [IN_W-1:0] data_in [0:CH_NUM-1],
output reg [OUT_W-1:0] data_out [0:CH_NUM-1],
output reg valid_out
);
integer i;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
valid_out <= 0;
for(i=0;i<CH_NUM;i=i+1) data_out[i] <= 0;
end else begin
valid_out <= valid_in;
for(i=0;i<CH_NUM;i=i+1) begin
// ReLU逻辑:负数置0,正数截断到8bit
if($signed(data_in[i]) < 0)
data_out[i] <= 0;
else
data_out[i] <= data_in[i][OUT_W-1:0];
end
end
end
endmodule
4. 子模块 3:2×2 最大池化 max_pool_2x2.v
verilog
module max_pool_2x2 #(
parameter IN_W = 28, CH_NUM = 16, DATA_W = 8
)(
input clk, rst_n, valid_in,
input [DATA_W-1:0] data_in [0:CH_NUM-1],
output reg [DATA_W-1:0] data_out [0:CH_NUM-1],
output reg valid_out
);
reg [DATA_W-1:0] buf_line [0:CH_NUM-1][0:IN_W/2-1];
integer i;
// 两行像素比较取最大值
function [DATA_W-1:0] max2;
input [DATA_W-1:0] a,b;
max2 = (a>b) ? a : b;
endfunction
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
valid_out <= 0;
for(i=0;i<CH_NUM;i=i+1) data_out[i] <= 0;
end else begin
valid_out <= valid_in;
for(i=0;i<CH_NUM;i=i+1) begin
data_out[i] <= max2(data_in[i], buf_line[i][0]);
end
end
end
endmodule
5. 子模块 4:全连接层 affine_layer.v(时分复用 MAC)
verilog
module affine_layer #(
parameter IN_DIM = 3136, OUT_DIM = 128,
DATA_W = 8, MAC_W = 20
)(
input clk, rst_n, valid_in,
input [DATA_W-1:0] data_in,
output reg [MAC_W-1:0] data_out [0:OUT_DIM-1],
output reg valid_out
);
reg [DATA_W-1:0] weight_rom [0:IN_DIM*OUT_DIM-1];
reg [MAC_W-1:0] acc [0:OUT_DIM-1];
integer i;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
valid_out <= 0;
for(i=0;i<OUT_DIM;i=i+1) acc[i] <= 0;
end else begin
// y = Wx + b 乘累加
for(i=0;i<OUT_DIM;i=i+1) begin
acc[i] = acc[i] + data_in * weight_rom[i*IN_DIM];
end
for(i=0;i<OUT_DIM;i=i+1) data_out[i] <= acc[i];
valid_out <= valid_in;
end
end
endmodule
6. 子模块 5:分类最大值判决 cls_compare.v(替代 Softmax)
verilog
module cls_compare #(
parameter CLS_NUM = 10, DATA_W = 20
)(
input clk, rst_n, valid_in,
input [DATA_W-1:0] data_in [0:CLS_NUM-1],
output reg [3:0] cls_num,
output reg valid_out
);
reg [DATA_W-1:0] max_val;
integer i;
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cls_num <= 0;
valid_out <= 0;
end else begin
valid_out <= valid_in;
max_val = data_in[0];
cls_num = 0;
for(i=1;i<CLS_NUM;i=i+1) begin
if(data_in[i] > max_val) begin
max_val = data_in[i];
cls_num = i[3:0];
end
end
end
end
endmodule
代码博客配套说明
- 定点量化说明:软件训练模型后将 32bit 浮点权重量化为 int8 定点,预存入 Verilog ROM/BRAM;
- 流水线设计:卷积、ReLU、池化、全连接全部插入流水线寄存器,提升系统最高工作频率;
- Softmax 舍弃说明:小型 FPGA 资源有限,指数运算硬件开销巨大,工程中直接比较 logits 最大值完成分类;
- 资源优化点:卷积窗口复用移位寄存器、全连接时分复用 MAC 阵列、多通道并行计算提升吞吐。
硬件推理实例
输入手写数字 7 的 28×28 灰度图写入图像 BRAM:
- 移位寄存器生成 3×3 像素窗口,卷积 MAC 阵列并行提取笔画边缘特征;
- ReLU 比较器过滤负值特征,输出 8bit 正特征;
- 2×2 最大池化压缩特征图尺寸至 14×14,保留核心轮廓;
- Flatten 模块串行输出一维 3136 维特征送入第一层全连接;
- 两层 Affine 乘累加映射至 10 维分类得分;
- 多路比较器选出最大值对应数字 7,输出
cls_result=4'd7,识别完成。
三、配套习题(单选 + 简答 + 硬件计算题 + Verilog 代码改错)带完整讲解
题型 1:单项选择题(6 道)
-
在该 CNN 硬件链路中,仅使用比较器、无乘法器的层是? A. Conv 卷积 B. ReLU 激活 C. Affine 全连接 D. 卷积权重 ROM 答案:B 讲解:ReLU 仅判断正负,负数置 0,仅消耗比较器资源;卷积、全连接均需要大量乘法器 MAC 单元。
-
FPGA 实现卷积层滑动窗口,一般使用哪种硬件结构? A. 单寄存器 B. 移位寄存器阵列 C. FIFO D. 加法树 答案:B 讲解:多行移位寄存器缓存图像行数据,循环移位构建 3×3/5×5 滑动窗口,是硬件卷积标准实现方案。
-
全连接层 Affine 在 FPGA 中资源消耗最大的是? A. 比较器 B. 乘法器 + 权重存储 BRAM C. 移位寄存器 D. 计数器 答案:B 讲解:全连接参数量极大,权重需要大量 BRAM 存储;同时海量乘累加运算消耗大量 DSP 乘法器。
-
MaxPool 最大池化硬件不需要以下哪种资源? A. 寄存器缓存行数据 B. 多路比较器 C. 乘法器 D. 流水线寄存器 答案:C 讲解:池化仅取窗口最大值,无乘法运算,无需 DSP 乘法器。
-
FPGA 小型 CNN 工程舍弃 Softmax 的主要原因? A. 逻辑资源占用极低 B. 指数运算硬件实现开销巨大 C. 输出精度更高 D. 时序更容易收敛 答案:B 讲解:Softmax 包含指数、除法浮点运算,定点近似实现会消耗大量加法器、乘法器,轻量工程直接比较 logits 最大值替代。
-
卷积层权值共享在 FPGA 硬件上的优势是? A. 减少 BRAM 权重存储容量 B. 提升乘法器数量 C. 增加流水线级数 D. 扩大图像缓存 答案:A 讲解:同一卷积核遍历整张图像,仅存储一份权重,大幅减少 ROM/BRAM 存储资源占用。
题型 2:简答题(4 道,硬件视角答题)
第 1 题:简述 Conv 卷积 + MaxPool 池化在 FPGA 硬件上配合的优势
参考答案:
- 卷积层移位寄存器窗口并行提取多通道局部特征,流水线 MAC 提升计算速度;
- MaxPool 下采样将特征图长宽减半,大幅减少后续全连接层输入维度,减少全连接 MAC 运算次数与权重存储 BRAM 容量;
- 池化无乘法运算,仅消耗少量寄存器、比较器,硬件资源开销极低,在不丢失核心特征的前提下降低整体工程资源消耗。
第 2 题:ReLU 硬件实现相比 Sigmoid/Tanh 有什么优势?
参考答案:
- ReLU 仅使用比较器,无乘法、指数运算,消耗逻辑资源极少;
- 无复杂非线性运算,流水线延迟低,时序更容易收敛;
- 定点量化无精度损失,负数直接截断置 0,硬件逻辑极简; Sigmoid/Tanh 需要分段线性近似指数,消耗大量 DSP,延迟高。
第 3 题:该 CNN 两层 Affine 全连接在 FPGA 实现存在什么硬件瓶颈?有哪些优化方案?
参考答案: 瓶颈:
- 参数量巨大,权重存储占用大量 Block RAM;
- 输入维度 3136,单次全连接需要 3136 次乘累加,吞吐低、延迟高; 优化方案:
- 时分复用单组 MAC 阵列,分块计算减少 DSP 数量;
- 模型量化:32bit 浮点→8bit 定点,降低存储位宽;
- 网络结构优化:用全局平均池化替代全连接,消除大量参数;
- 权重分块存储,分段读取降低 BRAM 带宽压力。
第 4 题:为什么卷积层必须先做 padding=1(Same Padding),FPGA 硬件上有什么好处?
参考答案:
- Same Padding 保证卷积输出特征图长宽与输入一致,窗口滑动控制逻辑统一,不需要区分图像边缘、中间像素的特殊判断;
- 硬件计数器、地址生成逻辑简化,控制状态机复杂度降低,时序收敛更简单;
- 边缘图像特征完整保留,不会丢失图像四周像素信息,识别精度更高。
题型 3:硬件计算题(FPGA 资源、尺寸计算)
已知:输入 28×28 单通道图像,Conv 3×3 padding=1 stride=1 输出 28×28×16;2×2 MaxPool 输出 14×14×16;全连接输入维度 = 14×14×16。
- 计算展平后全连接输入向量维度;
- 第一层 Affine1 权重参数量(输入 3136,输出 128);
- 若权重使用 int8 定点存储,计算 Affine1 权重总存储 bit 数。
分步计算 + 讲解
- 维度:14 × 14 × 16 = 3136 讲解:卷积输出多通道二维特征图,所有像素串行拼接为一维向量送入全连接。
- 参数量 = 输入维度 × 输出维度 = 3136 × 128 = 401408 讲解:全连接权重矩阵 W 尺寸 输出维度,输入维度,每一组输入输出对应一个权重参数。
- 总 bit = 401408 × 8bit = 3,211,264 bit ≈ 3.2Mbit BRAM 存储需求 讲解:int8 每个权重占用 8bit,该层仅权重就需要约 3.2M 块 RAM 存储,体现全连接存储瓶颈。
题型 4:Verilog 代码改错习题(卷积 ReLU 模块)
错误代码片段
verilog
module relu_err #(parameter CH_NUM=16, IN_W=20, OUT_W=8)(
input clk, rst_n, valid_in,
input [IN_W-1:0] data_in [0:CH_NUM-1],
output reg [OUT_W-1:0] data_out [0:CH_NUM-1],
output reg valid_out
);
integer i;
always @(posedge clk) begin
valid_out = valid_in;
for(i=0;i<CH_NUM;i=i+1) begin
if(data_in[i] < 0)
data_out[i] <= 0;
else
data_out[i] <= data_in[i][7:0];
end
end
endmodule
错误点 + 修正讲解
- 缺失复位 rst_n 时序逻辑,复位时输出未清零,硬件上电状态未知;
data_in[i]是无符号向量,无法直接判断负数,需要signed()有符号转换;valid_out = valid_in阻塞赋值,时序逻辑必须使用非阻塞<=;
修正后片段(对照前文 relu_act.v)
verilog
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
valid_out <= 0;
for(i=0;i<CH_NUM;i=i+1) data_out[i] <= 0;
end else begin
valid_out <= valid_in;
for(i=0;i<CH_NUM;i=i+1) begin
if($signed(data_in[i]) < 0)
data_out[i] <= 0;
else
data_out[i] <= data_in[i][OUT_W-1:0];
end
end
end
四、拓展总结
拓展学习方向
- 硬件优化升级:增加流水线、并行 MAC 阵列、权重稀疏化、深度可分离卷积降低资源;
- 算子扩展:增加 BatchNorm、Dropout 硬件实现,提升识别精度;
- 工程升级:搭建 AXI 总线 CNN 加速器,对接 Zynq ARM 处理器实现软硬件协同;
- 精度优化:Softmax 分段线性定点实现,完整复现软件网络推理流程。
文章总结
本文从基础 CNN 流程图出发,完整覆盖软件理论、FPGA 定点 Verilog 硬件工程、配套习题讲解,打通深度学习算法与数字硬件实现的鸿沟,适合 FPGA 图像识别、AI 加速器入门学习,代码可直接在 Xilinx Vivado/Intel Quartus 综合仿真验证。