阅读导航
- 开场白:为什么这篇论文值得一读
- 背景:稀疏化、卷积与 im2col 的烦恼
- 总体架构:在 CV32E40P 上加装速模块
- CONV2M:把卷积变成稀疏矩阵乘法
- 存储格式与 Gustavson 算法:列主序压缩的妙处
- PE 计算阵列:负载均衡与乘法器分解
- 自定义指令集:K_LOAD、I_CAL 与 WB
- 实验结果:加速比、面积与功耗的账本
- 总结与自测
这一篇在干嘛?
这篇论文(Zhang 等,江南大学,ACM TECS 2025)设计了一颗基于 RISC-V 的处理器,通过三条自定义指令把深度神经网络的卷积推理变成”硬件友好的稀疏矩阵乘法”,让大多数网络的推理时间缩短 70% 以上,能效峰值达到 675 GOPS/W。全文讲清楚四件事:为什么要稀疏化、卷积怎么变成矩阵乘法、硬件怎么负载均衡地并行计算、加速收益与面积功耗代价分别有多大。适合对计算机体系结构、RISC-V 和嵌入式 AI 感兴趣的初学者,只需要懂得卷积和矩阵乘法的基本概念即可阅读。
开场白:为什么这篇论文值得一读
如果你用过嵌入式设备跑神经网络——比如一块 STM32、一块 RISC-V 开发板上的智能摄像头——你一定体会过那种”模型在 GPU 上飞快、搬到开发板上就慢如蜗牛”的挫败感。这篇论文要解决的问题非常具体:在资源受限的边缘设备上,怎么让卷积神经网络(CNN)的推理跑得又快又省电?
论文出自江南大学的一个团队,发表在 ACM Transactions on Embedded Computing Systems(嵌入式计算系统汇刊,TECS)2025 年 10 月刊上。它没有走”造一颗专用加速芯片”的老路,而是选择了一个更聪明的切入点:RISC-V 的模块化指令集扩展机制。RISC-V 是一个开源指令集架构(Instruction Set Architecture,ISA),它的设计哲学是”最小核心 + 可插拔扩展”——就像乐高积木,基础指令集很小,但允许芯片设计者按需添加自定义指令。这给了论文团队一个独特的机会:他们可以在一个标准开源处理器(CV32E40P)内部”开一个洞”,塞进去一套专门为稀疏卷积设计的硬件加速单元,再配三条自定义指令把它暴露给 C 语言程序员。
这条路线的好处是双重的。第一,软件生态兼容:传统专用加速器往往需要专用编程工具甚至汇编语言,而这篇工作的加速单元通过自定义指令直接接入标准 C 编译器工具链,程序员写普通的 C 文件就能用上硬件加速。第二,硬件可复用:作者没有只为卷积定制一个”一次性”电路,而是把卷积统一抽象成稀疏矩阵乘法(sparse matrix multiplication,SpMM),这套硬件对任何稀疏矩阵乘法任务都有效,将来跑别的算法也能复用。
论文的贡献可以浓缩为四点:(1) 设计了带指令融合与高效地址计算单元的 RISC-V 自定义指令集;(2) 提出硬件友好的 CONV2M 结构,把卷积转成稀疏矩阵乘法;(3) 把经典的 Gustavson 稀疏矩阵乘算法引入卷积计算,并设计了配套的低开销稀疏存储格式;(4) 用”列主序 + 元素级并行”解决了 Gustavson 算法固有的负载不均衡问题。
下面这张图是全文的”问题—方案”总览,建议先花一分钟看懂它,后面所有内容都是围绕它展开的。

图 1:现有稀疏矩阵卷积框架存在的问题(左)与本文方案提供的解决路径(右)。(a) 是传统处理器取指开销大、资源利用低效;(b) 是传统 RISC-V 指令集做 MAC 运算的繁琐流程;(c) 是本文在 ISA、算法抽象、处理器结构与硬件设计四个层面的优化。
背景:稀疏化、卷积与 im2col 的烦恼
稀疏化:模型的”减肥手术”
先说背景中的背景:为什么大家都在谈稀疏化(sparsification)?神经网络参数量逐年暴涨,模型越做越深,推理所需的计算量和内存也水涨船高。但研究者很早就发现,大部分网络架构里存在大量冗余——许多权重对最终结果几乎没影响。于是”剪枝”(pruning)技术应运而生:训练时或训练后把不重要的权重直接置零,剪掉的权重比例通常在 20% 到 80% 之间,而精度几乎不掉。
剪枝分为两大流派:
- 非结构化剪枝(unstructured pruning):按重要性指标逐个删除权重,零散地分布在整个矩阵里。优点是剪得狠、稀疏度高、精度损失小;缺点是零元素的分布毫无规律,硬件调度起来很头疼。
- 结构化剪枝(structured pruning):按滤波器(filter)、通道(channel)或块(block)这样的粗粒度结构成块删除。优点是内存访问模式规整、硬件调度友好;缺点是稀疏度上限低,精度损失通常更大。
论文的数据很有说服力:对 AlexNet 用基于幅值的剪枝方法,稀疏度可达 75%,且各层稀疏度差异巨大;对 VGG16,输入特征图的平均稀疏度在 40% 到 60% 之间。换句话说,卷积网络里将近一半的乘法是在算零——这就是巨大的浪费,也是加速的机会。下面三张图分别展示了 AlexNet 的逐层稀疏度、VGG16 的核与特征图稀疏度,以及三种网络在不同稀疏度下的推理表现。

图 2(左):AlexNet 基于幅值剪枝后各层的稀疏度分布,整体可达 75%,但层间差异明显。

图 2(中):VGG16 各层卷积核与输入特征图的稀疏度,输入特征图平均在 40%–60% 之间。

图 2(右):三种网络在不同稀疏度下的推理性能。稀疏化对结果与精度影响很小,推理速度随稀疏度提高而加快,但在传统处理器上,稀疏化引入的管理开销常常抵消收益。
但这里有个”但是”:在传统处理器上,稀疏化的收益经常被管理开销吃掉。跳过零元素需要额外的判断、索引和查表,省下来的计算可能还不够付这些”管理费”。所以稀疏化要真正落地,必须靠专门的硬件架构——这正是本文的出发点。
传统卷积为什么慢
再来看卷积本身。一个标准的二维卷积至少是五层嵌套循环:输出通道、输出高度、输出宽度、核高度、核宽度,核心计算是乘累加(Multiply-Accumulate,MAC)。用公式表达就是:
论文用的传统卷积伪代码如下(算法 1):
ALGORITHM 1: Traditional Convolution
1: for c = 0 to 1 do
2: for i = offset to inputSize - offset - 1 do
3: for j = offset to inputSize - offset - 1 do
4: sum = 0
5: for m = 0 to kernelSize - 1 do
6: for n = 0 to kernelSize - 1 do
7: sum += input[(i + m - offset), (j + n - offset)] × kernel[m, n]
8: end for
9: end for
10: output[i, j] = sum
11: end for
12: end for
13: end for为了提升速度,很多研究把卷积改写成通用矩阵乘法(General Matrix Multiplication,GEMM)来算——最著名的就是 im2col 方法:把滑窗扫过的每个感受野展开成矩阵的一列,卷积就变成了规整的矩阵乘。但 im2col 有个致命伤:滑窗重叠导致输入数据被大量重复拷贝,内存访问量约为原始数据的 9 倍(对 3×3 核而言)。特征图一大,存储就成了性能瓶颈。
更宏观地看,DNN 推理 90% 以上的时间花在 MAC 运算和频繁的内存访问上。而在传统 RISC-V 指令集上,一次 MAC 需要执行 4 条加载指令、2 条乘法指令、1 条加法指令和 1 条写回指令——光取指和访存的开销就把计算淹没了。指令之间并行度又低,卷积算起来自然慢。
和现有工作的区别
学术界已经有很多 DNN 加速方案,论文在第二部分做了对比,这里挑几个有代表性的:
- HybridDNN(DAC’20)和 Eyeriss(JSSC’16):传统专用加速器的代表,性能强,但依赖专用硬件和专用工具链,改一次设计要重新做 RTL 综合,灵活性差。
- XpulpNN(DATE’20):同样是 RISC-V 指令集扩展的思路,但面向的是低精度 SIMD 运算(量化网络),没有处理稀疏问题。本文的指令集则同时加速稀疏卷积推理,并支持稀疏矩阵乘法的硬件复用。
- RI5CY-Accel(TC’24):用 Winograd 算法减少乘法次数,与本文”稀疏化 + 自定义计算流程”的路线完全不同。
- SamurAI(VLSI’20):把深度学习模型与 RISC-V 平台整合的另一种尝试。
一句话总结本文的差异化定位:不是最快的加速器,而是最灵活、最省电的可编程稀疏加速方案——单核轻量设计,C 语言可编程,为边缘系统而生。
总体架构:在 CV32E40P 上加装速模块
看懂了动机,我们来拆硬件。本文的处理器基座是 CV32E40P——一个来自 OpenHW Group 的开源 32 位 RISC-V 处理器,四段流水线(取指、译码、执行、写回),在嵌入式领域用得非常多。作者的做法不是推倒重来,而是”外科手术式”地改造了三个部位:
- 执行段(execute stage):插入一个加速模块,负责卷积和稀疏稠密矩阵乘法。这是本文的核心。
- 译码模块(decode):改掉,让它能识别新的自定义指令。
- 写回段的 LSU(Load-Store Unit,加载存储单元):重新设计,支持地址计算和多周期读写控制。
加速模块内部又分三块(对应后面图 6(c)):
- 控制单元(control unit):里面有一个同步调度器(synchronization scheduling unit),在并行计算时充当总指挥,协调整个数据流。
- 数据处理单元(data processing unit):包含矩阵压缩单元(MCU,Matrix Compression Unit)和 CONV2M 单元,负责在计算前把数据”备好菜”——压缩核矩阵、切分特征图。
- 计算处理单元(computation processing unit):真正的 PE 阵列,做乘法与加法。
这套架构最精妙的地方在于分工:CV32E40P 原有的流水线照常跑普通程序,只有遇到自定义指令时,工作才”下沉”到加速模块。这就好比一家普通餐厅后厨里加了一台专业烤炉——日常炒菜流程不变,遇到要烤的东西就交给烤炉,烤炉自己有温控和定时,厨师不用守着。

图 3:本文的 RISC-V 核心整体架构。在 CV32E40P 四段流水线的基础上,执行段嵌入加速模块,译码器扩展支持自定义指令,写回段重新设计了 LSU 以支持地址计算与多周期读写。
被重新设计的 LSU:一次写回一整批
值得单独讲讲 LSU 的改造,因为它解决了一个容易被初学者忽视的问题:写回也是开销。传统 LSU 每次只写回一个元素,一个矩阵算完要写回无数次。本文的设计里,地址控制单元(ACU,Address Calculation Unit)可以一次性写回整个计算结果,大幅减少写回指令的数量。
具体机制是”计数器 + 握手”(counter + handshake),如图 5 所示。当译码器解码 WB 指令并收到 ready 信号后,LSU 把要写回的数据拍数装入内部计数器,然后进入写回阶段:每个时钟周期发一次写请求,把写数据和写使能驱动到下游存储接口;存储接口通过握手返回 ready 响应;只有当写请求和 ready 同时为真,计数器才减一、发起下一次写。计数器归零、最后一个写信号确认后,LSU 在同一周期输出完成信号,反馈给流水线控制单元,流水线立刻取指执行下一条指令。
这个设计的巧妙之处在于:它天然适应存储系统可变的响应延迟——存储慢了就自动等(握手不通就不减计数),不需要额外的暂停或轮询,也不需要程序员插入一堆显式的写回指令。流水线吞吐量因此显著提升。

图 5:自定义写回指令的执行时间示意。“计数器 + 握手”机制在可变延迟下精确追踪写回完成时机,完成信号直接触发流水线取下一条指令。
CONV2M:把卷积变成稀疏矩阵乘法
现在进入本文的第一个核心技术:CONV2M(Convolution to Matrix,卷积转矩阵)单元。
im2col 的老大难问题
前面说过,im2col 方法把卷积展开成矩阵乘,代价是滑窗重叠造成的数据冗余拷贝——内存访问量约为原始数据的 9 倍。举个直观的例子:3×3 的核在 4×4 的输入上滑动,每个 2×2 的角落区域会被不同的滑窗重复读取。数据一重复,就要重复从内存搬、重复占存储空间。特征图越大,这个开销越离谱。
CONV2M 的两招:切分 + 核稀疏化
CONV2M 的思路是把输入特征图切分与卷积核稀疏化结合起来,直接在硬件里完成转换。论文给了一个具体例子(图 4):
- 输入是单通道 4×4 特征图,卷积核是 3×3。
- 第一步:把 4×4 输入特征图切成两个 3×4 矩阵——注意中间两行被两个矩阵复用,不需要重复加载,这就消除了 im2col 式的冗余数据搬运。
- 第二步:给 3×3 卷积核边缘补零(zero-padding),让它变成一个稀疏矩阵。补出来的零元素天然参与稀疏化:既然矩阵里大量元素是零,就只需存非零元素。
这两招合起来的效果:与传统的 im2col 相比,冗余内存访问减少约 35%,在处理大特征图时优势尤其明显。同时核的零填充加速了稀疏化进程,存储空间和计算负载双双下降。

图 4:CONV2M 方法的全过程。(a) 结合零元素构造权重矩阵;(b) 输入特征图矩阵的复用过程;(c) 卷积核补零形成稀疏矩阵;(d) 用 Gustavson 计算进行矩阵展开。
常见坑
很多人第一次读到这里会以为”切分 + 补零”改变了计算结果——其实没有。切分只是把 4×4 特征图重新组织成两个 3×4 矩阵,中间两行复用;补零只是在核的边缘加零,零乘任何数还是零。数学上结果与原卷积完全一致,变的只是数据组织和访问方式。另一个常见误解是认为补零”增加”了计算量:恰恰相反,补零产生的稀疏结构让硬件可以跳过这些零,存储和计算反而都省了。
存储格式与 Gustavson 算法:列主序压缩的妙处
卷积变成稀疏矩阵乘法之后,下一个问题是:稀疏矩阵怎么存?
为什么不用 CSR 和 CSC
教科书画风的标准答案是 CSR(Compressed Sparse Row,按行压缩)或 CSC(Compressed Sparse Column,按列压缩)。但论文指出,这两种格式在元素级并行的计算环境里都有先天缺陷:
- CSR/CSC 需要**维护一个与矩阵尺寸相当的查找表(lookup table)**来追踪”最近一次读取的非零元素位置”。这个大查找表既吃内存,查询又复杂,严重拖慢访问速度。
- 在本文的元素级并行架构和 Gustavson 式数据流下,CSR 把一整行的非零元素打包在一起,硬件调度器只能按行分派任务——当各行稀疏度差异大时,负载严重不均。
- CSC 虽然按列组织,但一列内的所有非零元素必须顺序读取,无法把一列拆开分给多个 PE 并行执行。
一句话:CSR 和 CSC 的存储模式天然限制了处理粒度,不适合细粒度的元素级并行。
列主序压缩格式:val、row、col 三个数组
论文提出的方案是一个列主序稀疏矩阵压缩格式(column-major sparse matrix compression format),用三个数组存矩阵:
- val:存非零元素的值;
- row:存非零元素的行索引;
- col:存非零元素的列索引。
它与 CSR/CSC 的关键区别是:每个非零元素的位置由坐标显式给出,不需要额外的大查找表。数据访问快,查询过程简单。而且非零元素按列顺序存储——同一列的非零元素在 val 数组中连续排列,访问更连续,数据利用率和计算效率都更高。每个 PE 可以独立并发地处理不同的非零元素,避免了负载不均和同步问题。
与之配套,稠密特征图采用行主序(row-major)存储,每行作为连续块存放。这有两个好处:处理器可以一次把整行向量直接加载进处理单元,降低访问延迟;更重要的是,行主序与 Gustavson 算法”逐行处理”的计算模型天然契合——算法按行处理、重点处理非零元素,一次操作处理一整行,把频繁的访存和索引操作压到最低。
Gustavson 算法:稀疏乘稠密的主力
现在引出本文的核心算法:Gustavson 算法。它是一种逐行处理(row-wise)的稀疏矩阵乘法算法,利用稀疏矩阵的低秩特性,只处理非零元素,完全跳过零元素的乘法和加法,从而减少不必要的计算和访存。它的另一个优点是增量式构建结果矩阵:每处理一个非零元素就累加一次,逐步构造最终输出,避免生成巨大的中间结果矩阵。论文的算法 2 给出了它的伪代码:
ALGORITHM 2: Gustavson Sparse-Dense Matrix Multiplication
1: for i = 0 to i = num_of_rows_of_A - 1 do
2: load values[i, :]
3: load col_idx[i, :]
4: NNZ = number of non-zeros in values[i, :]
5: for j = 0 to NNZ-1 do
6: col = col_idx[i, j]
7: load B[col, :]
8: s0 = values[i, j]
9: C[i, :] += s0 × B[col, :]
10: end for
11: store C[i, :]
12: end for行级并行的坑:负载不均衡
但 Gustavson 算法直接并行化会踩一个大坑。按行并行时,非零元素在各行之间的分布往往很不均匀——有的行密密麻麻全是非零元素,有的行几乎全是零。结果是:部分 PE 超载,另一部分 PE 早早算完在空等同步。整个系统的速度被最慢的 PE 拖住。图 6(b) 里的例子就是如此:PE1、PE2、PE3 只分到少量非零元素,PE0 却要处理一大堆,整体性能被 PE0 的长尾卡死。
解药:列主序元素级并行
论文的解法是列主序元素级并行(column-major element-level parallelism):不再按行分派,而是把稀疏矩阵的所有非零元素按列主序均匀撒到多个 PE 上,每个 PE 处理大约相同数量的非零元素。算法 3 是优化后的版本:
ALGORITHM 3: Optimized Gustavson Sparse-Dense Matrix Multiplication
1: for p = 0 to NUM_PE - 1 do
2: initialise C(p) ← 0
3: end for
4: for e in A do
5: Scheduler dispatches e to PE
6: load B[col, :]
7: C(p)[row, :] += val × B[col, :]
8: end for
9: synchronize all PEs
10: store C(p) to its block of C图 6(b) 的例子里,四个 PE 各处理三个非零元素,工作量基本拉平。把任务粒度细化到”单个元素”级别后,PE 之间的依赖减少,同步开销随之下降。更妙的是,配合这套策略,Gustavson 算法可以让目标行在 PE 的寄存器里驻留一段时间做部分和(partial sum),不需要跨 PE 的广播或协议网络——数据通路简化了,功耗也降了。
通关标准
读到这里,你应该能不看书回答三个问题:(1) CSR/CSC 为什么不适合元素级并行?(2) 本文的列主序格式靠哪三个数组存稀疏矩阵,靠什么省掉了查找表?(3) Gustavson 算法按行并行时为什么会负载不均,列主序元素级并行是怎么解决的?三问全答上来,这一节的精髓你就吃透了。
PE 计算阵列:负载均衡与乘法器分解
上面讲了”算法层面”怎么把任务分匀,这一节讲”硬件层面”怎么把计算做快做省。图 6 是这一节的主角。

图 6:加速单元的详细架构。(a) 乘法器分解与动态移位方法;(b) 列优先元素级并行策略;(c) 加速单元的详细内部结构,包含控制单元、数据处理单元与计算处理单元。
元素分配:广播 + 近似均分
硬件上,压缩后的稀疏矩阵数据要按乘法处理单元(MulPE)的数量合理分配。实现上采用广播(broadcasting)技术:把列主序压缩格式存储的非零元素均匀广播分发给所有 MulPE,保证每个 MulPE 处理的非零元素数量大致相同,从硬件层面杜绝负载不均造成的性能瓶颈。
数据预取与行向量复用
稠密矩阵这边,数据处理依据 Gustavson 算法的天然需求做预取:一次性把整行向量加载进 MulPE,而不是每次计算都替换行向量。硬件会比较”待处理非零元素的列索引”与”上次处理元素的列索引”,据此判断是否需要替换数据。得益于列主序压缩格式(同一列的非零元素连续存放),稠密矩阵行向量的替换频率被大幅压低,数据局部性和复用率双双提升。
乘法器分解:不用大乘法器也能乘
接下来是硬件设计里最”抠门”也最聪明的一招。传统乘法器在位宽大的时候非常吃资源。本文不用传统乘法器,而是把乘数分解成小因子(7、5、3、1),用移位和加法完成乘法——也就是”乘法器分解 + 动态移位”(图 6(a))。例如乘以 7 等价于”左移 3 位减自己”,乘以 5 等价于”左移 2 位加自己”。这大幅降低了对乘法器硬件的依赖,显著降低了逻辑单元的复杂度和功耗。后面你会看到,这直接体现在 FPGA 资源报告里:DSP 数量零增长。
同步调度器与加法树
并行乘法完成后,各 PE 产出的部分结果需要一个”汇合”过程。本文设计了一个同步调度器:并行乘法单元完成的部分结果先暂时存入各自的队列,调度器根据第二阶段并行加法单元的状态,把缓冲的结果转移给加法单元计算,尽量压缩同步等待时间。
第二阶段用的是加法树(addition tree)结构。传统的顺序加法是一步步累加,每次加法都要等前一次完成,是线性的;加法树把加法操作分层组织,让计算在对数时间内完成——就像淘汰赛,第一轮 8 个数两两相加得 4 个,第二轮得 2 个,最后一轮出冠军。这能快速汇聚各 PE 的部分计算结果,生成最终输出矩阵。
整个数据计算的完整流程如图 7 所示。

图 7:单次计算的详细数据流。压缩核数据与切分特征图数据进入 PE 计算单元,经并行乘法、队列缓冲、同步调度、加法树汇聚,最终产出结果。
自定义指令集:K_LOAD、I_CAL 与 WB
硬件就绪,接下来看软件怎么”够得着”这些硬件。RISC-V 的指令集有六种基本格式:R 型(寄存器到寄存器)、I 型(短立即数与加载)、S 型(存储)、B 型(条件分支)、U 型(长立即数)、J 型(无条件跳转)。本文基于 R 型格式设计了三条专用指令,统一使用 opcode 0x77,具体功能由 funct7 和 funct3 字段的取值决定:两条负责加载数据和稀疏计算,一条负责写回计算结果。图 8(a) 展示了这三条指令的编码与功能说明。

图 8:(a) 定制的 RISC-V 自定义指令编码与说明;(b) 硬件执行时间的测量结果。
三条指令各自的分工
- K_LOAD:接收两个参数,RS1 是卷积核的起始地址,RS2 是卷积核的尺寸。该指令把核数据加载进来后,立即执行稀疏化和压缩,按列主序压缩格式(Column-major Compress Format)存好,为后续计算做好准备。对应硬件里的矩阵压缩单元(MCU)。
- I_CAL:同样两个参数,RS1 是输入特征图的起始地址,RS2 是特征图尺寸。特征图数据先送入 CONV2M 单元做重排(切分),然后与之前加载的卷积核数据一起执行基于 Gustavson 的计算,完成卷积操作。
- WB:执行卷积计算结果的写回,只接收一个参数——写回基地址。它是一条多周期指令,执行时间取决于卷积核与输入特征图的乘积规模。前面讲过的”计数器 + 握手”机制就服务于这条指令。
注意这套指令设计的一个精妙之处:指令数与问题规模解耦。无论矩阵多大、卷积多少通道,程序员只需要循环调用这三条指令,地址偏移由地址计算单元(ACU)自动算,写回由 WB 一次搞定。传统写法里,问题规模每翻一倍,指令数跟着翻倍;在这里,指令数保持稳定,计算开销不会随矩阵维度或通道数增长而膨胀。
元素级并行分配的数学
在 K_LOAD 的加载过程中,MCU 会遍历整个稀疏矩阵的元素,每遇到一个非零元素,除了按列主序压缩格式存储外,还同步递增一个非零元素计数器(NNZ_CNT)。一次扫描、零额外通路,就得到了全局非零元素总数 。分发元素到 PE 时,只需用这个计数器和综合阶段固定的 PE 数量做简单的位运算。设 PE 数量 ,分配公式为:
翻译成人话:把 N 个非零元素分成 P 份,每份至少 base 个,前 rem 个 PE 各再多拿 1 个。这样任意两个 PE 的任务量之差不超过 1,用简单的移位与位与操作就实现了理论上最小的静态负载方差——完全不涉及除法或浮点运算,硬件成本几乎为零。
完整执行流程
把三条指令串起来,一次完整卷积的执行流程是(对应图 8(b)):
- 自定义卷积指令被译码后进入加速单元,通过 LSU 从内存取回核数据;
- 处理器激活 MCU,对卷积核做稀疏化和列主序压缩;
- 特征图通过 LSU 从内存的输入通道取回,激活 CONV2M 单元把数据切成两段;
- 压缩后的核数据与切分后的特征图一起送入 PE 计算单元处理;
- ACU 全程保持激活,自动计算地址偏移;
- 循环迭代直到整个计算完成,WB 指令一次性把结果写回。
常见坑
初学者容易把这三条指令理解成”三条独立的加速指令”,以为可以随意穿插使用。实际上它们是有状态依赖的流水协作:K_LOAD 必须先于 I_CAL 执行(核要先压缩好),I_CAL 产生的结果要由 WB 统一写回,三者通过加速模块内部的状态寄存器衔接——参数地址一旦读入,状态寄存器会随数据加载、卷积计算等操作自动递增,通过比较状态变化前后的值来推进整个计算流程。如果你的 C 代码里调用顺序不对,硬件状态机就会乱掉。
实验结果:加速比、面积与功耗的账本
技术讲完了,现在来算账。所有加速到底值不值?论文的实验部分给出了相当完整的答案。先看实验框架(图 9):
- 用 PyTorch 训练各种神经网络,得到权重参数的精度,把训练好的权重嵌入网络结构、写进一个 .c 文件;
- 设计好自定义指令集并验证后,把这些指令集成进 RISC-V 工具链,通过链接器把上述文件整合成可执行文件(ELF);
- 硬件开发用 Xilinx Vivado 做 FPGA 开发、用 Synopsys Design Compiler 做 ASIC 设计,完善处理器核心的 RTL 描述;
- 所有数据和生成的 ELF 交给 Verilator(周期精确仿真器)做系统级性能评估。
其中步骤 (2)(3) 每次测试只需做一次,后续迭代只需重复 (1) 和 (4)。

图 9:实验框架。PyTorch 训练模型 → 权重嵌入 C 文件 → 集成自定义指令的工具链生成 ELF → Vivado/Design Compiler 完成硬件开发 → Verilator 周期级仿真评估。
矩阵乘法:规模越大,加速越猛
本文的硬件阵列并行执行 4 个乘数和 4 个被乘数,所以以 4×4 矩阵为基本单元;更大的矩阵乘法先切成 4×4 子矩阵再迭代执行。实验测试了不同 K 值(K = 16、32、64、128、256、512)下、三种随机稀疏度区间(30%–50%、50%–60%、60%–80%)的矩阵乘法性能,对比 CV32E40P(RV32IM 基线)与本文处理器。

图 10(a):随机稀疏度 30%–50% 下不同规模矩阵乘法的加速效果评估。

图 10(b):随机稀疏度 50%–60% 下不同规模矩阵乘法的加速效果评估。

图 10(c):随机稀疏度 60%–80% 下不同规模矩阵乘法的加速效果评估。
结果呈现清晰的规律:本文处理器在各种规模下的时钟周期数都显著少于 CV32E40P,且矩阵尺寸 K 越大、加速效果越明显。当矩阵规模达到 512、稀疏度在 60%–80% 时,时钟周期加速比高达 70 倍。原因也很直观:矩阵越大,传统 RV32IM 指令集的读写操作越频繁,大量时钟周期耗在访存上——而这恰恰是本文硬件绕开的开销。
卷积:GOPS 与 stride 的关系
卷积性能用 4×4 输入特征图加 3×3 卷积核、不同通道数来评估。规律与矩阵乘法类似:通道数越多加速比越高;而且由于卷积包含大量矩阵乘法,卷积的整体加速效率比单次矩阵乘法更高——这要归功于 CONV2M 专用转换硬件。卷积加速的峰值 GOPS 达到 2.12。论文还测了 stride 大于 1 的情况:stride 增大后滑窗步长变大、输出特征图变小、计算量下降,同样输入尺寸下 stride 从 1 变 2,GOPS 从 2.12 降到 0.92。
整网推理:三个经典网络的检验
接着上真实网络:ResNet18、VGG16 和 MobileNetV2,逐层统计时钟周期。

图 11(a):ResNet18 各卷积层在 CV32E40P 与本文处理器上的时钟周期对比。

图 11(b):VGG16 各卷积层在 CV32E40P 与本文处理器上的时钟周期对比。

图 11(c):MobileNetV2 各卷积层在 CV32E40P 与本文处理器上的时钟周期对比。

图 11(d):三个网络整体加速效果评估,总时钟周期分别减少 63%(ResNet18)、68%(VGG16)、72%(MobileNetV2)。
数据非常有说服力:各卷积层的时钟周期加速比约为 ResNet18 达 60 倍、VGG16 达 63 倍、MobileNetV2 达 71 倍。整体推理的总时钟周期分别下降 63%、68%、72%。MobileNetV2 表现最好的原因是它大量使用深度可分离卷积(depthwise separable convolution),这种结构在本文的处理器架构中加速效率格外突出。另一个有趣的观察:参数量更大的 VGG16 比 ResNet18 加速更明显——说明网络参数规模越大,这套自定义指令的优势越突出。
FPGA 资源账单
加速不是免费的。表 1 给出了两颗处理器在 Xilinx ZCU3EG 平台、100 MHz 下的 FPGA 资源对比:
| 资源 | CV32E40P | Our_Work | 增幅 |
|---|---|---|---|
| 时钟频率 | 100 MHz | 100 MHz | - |
| LUT | 5,452 | 20,277 | 271% |
| FF | 2,632 | 3,821 | 45% |
| DSP | 5 | 5 | 0% |
| IO | 5 | 6 | 20% |
| BUFG | 3 | 12 | 75% |
| 静态功耗 | 0.115 W | 0.210 W | 82% |
| 动态功耗 | 0.144 W | 0.195 W | 35% |
| 总功耗 | 0.259 W | 0.405 W | 56% |
三个值得注意的细节:LUT 暴涨 271%,因为复杂的控制逻辑要用查找表实现;DSP 零增长——这正是乘法器分解与动态移位技术的功劳,乘法不靠 DSP 硬核,代价是转嫁到了 LUT 上;FF 增长 45%,主要分配给乘法单元的流水线级,用额外寄存器存中间数据保证流水线高效运转。功耗方面,静态功耗涨 82%(逻辑和存储单元复杂度提高),动态功耗涨 35%(控制信号频繁翻转、逻辑活动率上升),总功耗涨 56%。
ASIC 资源账单
ASIC 综合用 SMIC CMOS LL 55nm 1.2V 工艺、TT 角(Typical corner),表 2 是综合报告:
| 资源 | CV32E40P | Our_Work | 增幅 |
|---|---|---|---|
| 时钟频率 | 300 MHz | 300 MHz | - |
| 端口数 | 8,720 | 20,149 | 131% |
| 线网数 | 23,197 | 97,659 | 320% |
| 单元数 | 19,085 | 86,247 | 352% |
| 核心单元面积 | 0.0466 mm² | 0.1718 mm² | 268% |
| 总单元面积 | 2.94 mm² | 3.06 mm² | 4.08% |
| 内部功耗 | 2.22 mW | 2.72 mW | 22.5% |
| 翻转功耗 | 0.27 mW | 0.33 mW | 22.2% |
| 漏电功耗 | 80.2 µW | 91.2 µW | 13.7% |
| 总功耗 | 2.57 mW | 3.14 mW | 22.1% |
注意一个容易误读的数据:核心单元面积涨了 268%,但总单元面积只涨了 4.08%(从 2.94 mm² 到 3.06 mm²)。因为整个芯片的面积基数大,加速模块只是其中一小块。功耗的增长则温和得多:总功耗只涨 22.1%,换来的是 60 倍以上的能效提升——这笔账显然是划算的。
横向对比:能效之王
最后和业界已发表的处理器/加速器同台竞技(表 3):
| 处理器 | JSSC’16 | JSSC’22 | TC’24 | DAC’20 | AICAS’24 | TCAD’25 | ESSCIRC’21 | Our_Work |
|---|---|---|---|---|---|---|---|---|
| 架构 | 加速器 | 加速器 | 处理器 | 加速器 | 处理器 | 处理器 | 处理器 | 处理器 |
| 工艺/器件 | CMOS 65nm | CMOS 22nm | CMOS 90nm | PYNQ Z1 | CMOS 55nm | CMOS 22nm | CMOS 65nm | CMOS 55nm |
| 面积 | 12.25 mm² | 12 mm² | 6.44 mm² | - | 0.58 mm² | - | 10 mm² | 3.06 mm² |
| 功耗 | 140 mW | 25.4 mW | 5.8 mW | 2.6 W | 2.4 mW | 43.38 mW | 49.5 mW | 3.14 mW |
| 时钟频率 | 200 MHz | 450 MHz | 100 MHz | - | 200 MHz | 800 MHz | 205 MHz | 300 MHz |
| 性能 | 33.6 GOPS | 15.6 GOPS | 0.225 GOPS | 83.3 GOPS | 0.131 GOPS | 5.232 GOPS | 15 GOPS | 2.12 GOPS |
| 面积效率 | 2.74 GOPS/mm² | 1.3 GOPS/mm² | 0.034 GOPS/mm² | - | 0.225 GOPS/mm² | - | 1.5 GOPS/mm² | 0.69 GOPS/mm² |
| 能效 | 240 GOPS/W | 614 GOPS/W | 38.79 GOPS/W | 32.0 GOPS/W | 54.42 GOPS/W | 120.87 GOPS/W | 303 GOPS/W | 675 GOPS/W |
这组数据要分三个维度读:
- 能效(本文的王牌):300 MHz、1.2 V 供电下达到 675 GOPS/W,超过所有列出的基线。相比 JSSC’22 的 614 GOPS/W(10 核设计 + MRAM 低功耗保持),本文用单核设计做到相当甚至更高的效率,复杂度低得多;更是基线 RISC-V 核 CV32E40P(10.92 GOPS/W)的 60 倍以上。
- 性能(诚实的短板):2.12 GOPS 不及 JSSC’16(33.6 GOPS)、JSSC’22(15.6 GOPS)、DAC’20(83.3 GOPS)等专用加速器——但那些是针对峰值吞吐优化的多核或专用架构,功耗和设计复杂度也完全不是一个量级(JSSC’16 功耗 140 mW,是本文的 44 倍;TCAD’25 功耗超过本文 16 倍)。
- 面积效率(居中):0.69 GOPS/mm²,优于 TC’24 和 AICAS’24,不及为峰值性能优化或用先进工艺的作品。
一句话总结定位:性能、面积效率与能效的平衡三角。JSSC’22 和 TCAD’25 追求高频或多核吞吐,代价是高功耗与高复杂度;本文单核 RISC-V 强调轻量、省电、软件可编程,更适合资源受限的嵌入式系统,稀疏计算架构也正好回应了未来边缘设备网络部署的演进需求。
通关标准
你应该能凭记忆说出这组关键数字:大多数网络整体推理时间减少 70% 以上;矩阵规模 512、稀疏度 60%–80% 时周期加速比 70 倍;能效 675 GOPS/W,是基线 CV32E40P 的 60 余倍;代价是 FPGA 上 LUT 涨 271%(DSP 零增长)、ASIC 上总功耗涨 22.1%。能把”收益”和”代价”两边都数出来,才算真正读懂了这篇论文的实验设计。
总结与自测
回顾全文,这篇论文给嵌入式 AI 加速提供了一个”教科书级”的完整闭环:
- 指令集层:基于 RISC-V 模块化扩展机制,用三条 R 型自定义指令(opcode 0x77)把稀疏卷积暴露给 C 程序员,指令数与问题规模解耦;
- 算法层:CONV2M 把卷积转成稀疏矩阵乘法(比 im2col 省约 35% 冗余访存),Gustavson 算法做稀疏乘稠密,列主序元素级并行解决负载不均;
- 存储层:val/row/col 三数组的列主序压缩格式省掉大查找表,稠密侧行主序存储配合整行加载与数据复用;
- 硬件层:MCU + CONV2M + PE 阵列 + 同步调度器 + 加法树 + 乘法器分解 + “计数器 + 握手”式 LSU,每一环都在削减取指、访存和同步开销。
最终成绩单:FPGA 验证功能,55nm CMOS 综合,大多数网络推理时间减少 70% 以上,能效 675 GOPS/W。它证明了在 RISC-V 生态里,“自定义指令 + 稀疏化 + 算法硬件协同设计”是一条切实可行的边缘 AI 加速路径。
自测:五个问题检验你有没有真懂
1. 为什么 CSR 和 CSC 格式在本文的架构中不适用? CSR/CSC 需要维护一个与矩阵尺寸相当的大查找表来追踪非零元素位置,既耗内存又拖慢查询;CSR 把整行非零元素打包,硬件只能按行分派任务,行间稀疏度差异大会导致负载严重不均;CSC 中一列的非零元素必须顺序读取,无法拆分给多个 PE 并行执行。两者都限制了处理粒度,不适合元素级并行。
2. CONV2M 相比传统 im2col 省在哪里? im2col 滑窗重叠导致数据冗余拷贝,内存访问约为原始数据的 9 倍。CONV2M 把 4×4 特征图切成两个 3×4 矩阵并复用中间两行,同时对卷积核补零形成稀疏矩阵,冗余内存访问减少约 35%,且存储和计算负载随稀疏化同步下降。
3. 列主序元素级并行是如何解决 Gustavson 算法负载不均衡问题的? 原始 Gustavson 按行并行,非零元素在各行分布不均导致部分 PE 超载、部分空转,系统被最慢的 PE 拖住。列主序方案把所有非零元素按列主序均匀分配到各 PE(每个 PE 约 3 个元素 vs 原来有的 PE 分不到),并用 NNZ_CNT 计数器和位运算实现任意两 PE 任务量之差不超过 1 的理论最优静态分配,任务粒度细化到单个元素后 PE 间依赖和同步开销也随之减少。
4. 为什么 DSP 数量零增长却能把乘法做出来?LUT 又为什么暴涨? 因为乘法采用”分解 + 动态移位”策略:乘数被分解为 7、5、3、1 等小因子,用移位和加法实现乘法,完全不占用 DSP 硬核。但移位加法网络和复杂控制逻辑需要大量查找表实现,所以 LUT 增长 271%——本质上是用 LUT 换 DSP 的资源置换。
5. 这篇工作牺牲了什么换来了 675 GOPS/W 的能效?它适合什么场景? 牺牲的是峰值性能和面积效率:2.12 GOPS 远低于 JSSC’16(33.6)、DAC’20(83.3)等专用加速器;ASIC 总功耗涨 22.1%、核心面积涨 268%(总单元面积仅涨 4.08%)。它适合的正是这种取舍的应用场景——资源受限、功耗敏感的边缘嵌入式系统,用单核可编程设计换取足够用的性能(整体推理提速 70%+)、极致能效和 C 语言级软件生态兼容性。
如果你对 RISC-V 自定义指令扩展或稀疏计算感兴趣,这篇论文的参考文献里还有两篇值得顺藤摸瓜:Eyeriss(JSSC’16)是能效架构设计的经典范本,Gamma(ASPLOS’21)则展示了 Gustavson 算法在通用稀疏矩阵乘加速中的更多玩法。