ISSCC 2026的论文合集一放出来,我第一件事就是把目录按方向分堆,然后挑出自己最关心的那几篇,从摘要开始一页页啃。今年这届里,Session 15的第8篇——行内习惯记作15.8——是我花时间最多的一篇。这篇论文学习的过程其实很能说明问题:一个电路从业者要怎么把一篇顶会论文真正吃透,绝不是扫一遍摘要、记住几个漂亮数字就算完,而是要把背后的设计取舍、指标算法、验证方法全部拆开。15.8做的是一颗面向边缘端神经网络推理的SRAM存内计算宏(Compute-in-Memory,下面简称CIM),它最抓眼的地方是在保住可用推理精度的同时,把能效比推到了一个比较激进的位置。这篇我前后读了三遍,又自己动手搭了简化模型验证,下面把整个过程里真正有价值的东西整理出来。
1. 先把这篇论文的坐标定下来
读顶会论文最容易犯的错,是拿到手就直接钻进电路图里,结果看到一半发现自己不知道作者到底在和谁比、比什么。我的习惯是先给论文定坐标:它在哪个session、这个session是什么调性、它解决的问题属于哪个层次。坐标定准了,后面读细节才不会迷路。
1.1 Session 15这个区意味着什么
ISSCC的session编号本身不直接等于方向,但同类工作会被聚在相邻的几个session里。我拿到目录之后,通常会把Session 14到16这几篇放在一起横向扫一遍,看看今年这个方向整体的风向。从我扫下来的感觉是,Session 15这一带集中了一批偏"数字与计算密集型"的工作:有面向机器学习加速的、有做新型存储阵列的、也有做近存/存内计算的。把15.8放进这个语境里看,它的定位就很清楚了——它不是一颗追求绝对峰值算力的芯片,而是冲着"每瓦能干多少有效运算"这个指标去的。
这一步为什么要先做?因为同一篇论文,如果你不知道它的邻居是谁,你就没法判断它的创新是"领先半步"还是"另辟蹊径"。我见过太多人读完一篇论文只会背数字,问他这篇比去年同类型的强在哪,答不上来。横向扫一遍session就是为了避免这种情况。
1.2 从标题关键词反推它要解决的真问题
15.8的标题里几个关键词值得逐个抠:CIM、精度、能效、边缘。这四个词连起来就是一条完整的故事线。边缘端意味着供电和散热都受限,不能靠堆功耗换性能;CIM意味着把乘累加运算放进存储阵列里做,省掉数据在存储和计算单元之间来回搬运的开销;精度意味着模拟域的存内计算天生有噪声、有工艺偏差,要把它做到网络能用的精度是需要硬功夫的;能效则是对前面三点的综合考核。
把这四个词拆开之后,论文要解决的"真问题"就浮现了:存内计算最大的理论优势是省掉数据搬运,但模拟计算引入的误差会让精度掉下来,一旦为了精度去加校准电路,能效优势又被吃掉一部分。所以这篇论文的核心矛盾,就是在精度和能效之间找那个平衡点。你带着这个矛盾去读它的电路,很多设计细节就突然说得通了。
1.3 这篇论文的读者画像
不是所有人都需要精读这篇。我自己的判断是,如果你在做这几类事情,读它性价比很高:一是做边缘AI芯片架构的,想知道CIM路线现在实际能做到什么水平;二是做SRAM阵列和模拟前端电路的,想学怎么处理阵列里的非理想效应;三是做量化算法和硬件协同的,想看硬件误差怎么反馈到网络精度上。反过来,如果你完全不碰模拟电路,只想了解AI加速器的大框架,那读摘要和几张总图就够了,硬啃电路细节会事倍功半。
2. 15.8的整体架构拆解
定完坐标就进入正文。我读架构图有个固定的套路:先看数据从哪进、从哪出,再看中间算了什么,最后看控制信号怎么协调。这个顺序能把一张复杂的框图拆成几条独立的数据流,读起来轻松很多。
2.1 系统级框图与数据通路
15.8的系统框图大致可以分成三块:输入缓冲与量化接口、CIM宏阵列本身、以及输出端的累加与反量化通路。数据流是这样的:网络某一层的激活值经过量化后,按位拆分送入宏阵列;权重则以另一种形式预存在阵列里;两者在阵列内部完成模拟域乘累加;结果出来之后经过模数转换、数字累加、再反量化,送到下一层。
这里有个细节值得注意:输入为什么要按位拆分。这是因为模拟存内计算通常一次只能处理很窄的位宽,多位输入必须拆成若干个单比特或双比特的分量,分多次算再加权求和。这个拆分策略直接决定了吞吐和能效,是整篇论文的骨架之一。我读到这里的时候专门停下来算了一下拆分的次数和它带来的额外开销,这个后面在指标章节会展开。
2.2 存内计算宏的电路骨架
宏阵列这一块是重头戏。本质上它是一块改造过的SRAM:标准的6T位单元负责存储,另外增加了参与计算的通路。计算时,字线被激活,位线上的电压或电流会因为存储值和输入的不同而产生差异,读出电路再把这个差异转成数字量。
它的关键改动在于读出和计算是复用的。传统SRAM读出只关心0和1,而这里读出的是多个单元共同作用的结果,是一个模拟量。这就要求读出电路有更高的线性度和更低的失调。我读这一节的时候反复对照了它的时序图,确认了阵列一次计算需要几个时钟周期、什么时候激活字线、什么时候采样。这些时序细节是复现时最容易出错的地方,值得逐拍去抠。
2.3 三个关键设计取舍的来龙去脉
这篇论文有三处取舍我认为是精华,值得单独拎出来讲。
第一处是阵列规模的选择。阵列开得越大,一次算的行数越多,理论上能效越高,但位线越长、寄生电容越大,读出信号越弱、越容易被噪声淹没。作者没有一味堆大,而是选了一个在信号强度和能效之间折中的规模。
第二处是模拟计算的位宽。位宽越高,单次能算的精度越好,但需要的参考电平越多、校准越复杂。作者走了一条混合路线:低位宽在模拟域粗算,高位宽靠数字域补足,把模拟硬件的负担降下来。
第三处是校准策略。模拟电路有工艺偏差,不做校准精度根本不够用,但全芯片校准又太贵。作者采用的是一种分布式的、只针对关键路径的校准,把开销控制在可接受范围。这三处取舍我建议每个读者都自己在纸上推一遍,推完你对"为什么这么设计"的理解会上一个台阶。
3. 指标背后的门道
顶会论文的数字都很好看,但数字怎么来的、怎么比、含不含水分,是判断一篇论文价值的关键。这一章我把15.8的几类核心指标一个个拆开,讲清楚计算口径和背后的隐藏成本。
3.1 能效比不是标个数字那么简单
CIM论文最常报的指标是TOPS/W,也就是每瓦能完成多少万亿次运算。这个数字的计算口径有好几种,差别能有好几倍。
第一种口径是"阵列内计算能效":只算乘累加在阵列里那部分消耗的能量,把ADC、数字累加、接口全部排除在外。这个数字最漂亮,但也最不真实。
第二种口径是"宏级能效":把ADC和宏内的外围电路算进去,但不算芯片级的接口和缓存。
第三种是"系统级能效":把整个芯片的功耗都摊进去。
15.8报的主要是宏级的数字,这在CIM领域属于比较诚实的做法。我在读的时候特意去它的功耗分解图里核对了:ADC占了多大比例、阵列占了多少、外围占了多少。核对下来发现ADC是大头,这其实也印证了当前CIM领域公认的瓶颈——模拟计算的能效优势,很大一部分被模数转换吃掉了。理解了这一点,你就明白为什么这篇论文要花那么多篇幅去优化ADC和降低转换次数。
3.2 精度损失的来源与量化方式
精度这块,论文一般会报网络级的准确率,比如在某个数据集上掉了多少个百分点。但硬件误差是怎么传到网络准确率上的,中间链条值得理清。
硬件端主要有几类误差:一是存储单元的器件偏差,导致权重实际值和理想值有出入;二是读出通路的失调和噪声,让计算结果随机抖动;三是ADC的量化误差;四是有限位宽带来的截断误差。这四类误差在模拟域叠加,最后体现为网络某一层输出的扰动。
判断一篇CIM论文的精度报告是否可信,我会看两点:一是它测误差时用的是实测芯片还是仿真模型;二是它有没有把误差建模进网络后重新评估准确率,还是只报一个电路指标就说"精度足够"。15.8在这一点上做得比较扎实,它给出了误差注入网络后的准确率曲线,这比单纯报一个信噪比有说服力得多。
3.3 面积与吞吐的隐藏成本
面积和能效往往是一对矛盾。CIM为了省数据搬运,会在阵列里加额外的计算和读出电路,这会增加单元面积。一个有意思的对比是:同样容量的标准SRAM和CIM阵列,CIM的密度通常会低一些。这个密度损失算不算划算,取决于它省下来的搬运功耗有没有超过面积增加带来的成本。
吞吐这块同理。论文报的吞吐通常是理想情况下的峰值,实际网络跑起来会因为层间串行、数据依赖、接口带宽等因素打折扣。我读的时候专门找了它有没有报端到端跑一个完整网络的实际吞吐,如果只报了宏的峰值吞吐,那对系统设计者的参考价值就要打个折。
| 指标类型 | 常见口径 | 需要注意的隐藏成本 |
|---|---|---|
| 能效比 TOPS/W | 阵列内 / 宏级 / 系统级 | ADC功耗占比、外围电路是否计入 |
| 推理精度 | 电路指标 / 网络准确率 | 是否做了误差注入网络的重新评估 |
| 阵列密度 | 单元面积 / 有效容量 | 额外计算电路带来的面积开销 |
| 吞吐 | 峰值 / 端到端 | 层间串行、接口带宽的限制 |
4. 想复现或验证这篇论文,我会从哪几刀切进去
光读不练,理解永远是浮的。读完15.8之后我花了两个晚上搭了一个简化模型,把它的核心机制跑了一遍。下面把我验证的路径分享出来,你可以按这个顺序上手,从最简单的一刀开始。
4.1 环境与模型准备
不需要真的去流片,也不需要完整的工艺库。第一步我用Python先把网络层的量化和误差注入建模跑通,目的是搞明白误差到底怎么影响准确率。第二步用简化的行为级模型模拟阵列计算,目的是验证拆分和累加的逻辑对不对。第三步才考虑用SPICE去仿单元级的读出。
先跑Python这一步有个好处:你能在几小时内就得到"误差多大时准确率会崩"这个直观感受,而这个感受是你判断论文指标是否可信的基准。下面是误差注入建模的一个简化片段:
import numpy as np def quantize(x, bits): scale = 2 ** bits - 1 return np.round(np.clip(x, 0, 1) * scale) / scale def inject_weight_error(weights, sigma): # sigma 表示器件偏差的相对标准差 noise = np.random.normal(0, sigma, weights.shape) return weights * (1 + noise) def inject_read_noise(activations, sigma): # 读出通路的随机抖动 noise = np.random.normal(0, sigma, activations.shape) return activations + noise # 假设某层权重和激活 w = np.random.uniform(-1, 1, (256, 256)) a = np.random.uniform(0, 1, (256,)) for sigma_w in [0.001, 0.005, 0.01, 0.02]: w_err = inject_weight_error(w, sigma_w) a_err = quantize(inject_read_noise(a, 0.005), 4) y = w_err @ a_err print(f"weight sigma={sigma_w}, output energy={np.sum(y**2):.2f}")这段代码很粗,但能让你快速感受到不同误差水平下输出能量的变化。等你把这个跑通,再去读论文里的精度章节,会有一种"原来它说的是这个感觉"的清晰感。
4.2 从单元到宏的行为级仿真
第二步是行为级仿真。这里的关键是把阵列的并行计算正确表达出来。CIM阵列的特点是多个单元同时作用在一条位线上,输出是它们贡献的叠加。我用一个简化的sigmoid型读出函数来近似位线上的电压响应:
def array_compute(weights_row, inputs, bitline_gain=1.0): # weights_row: 一行单元存储的权重 # inputs: 对应的单比特或多比特输入 contributions = weights_row * inputs raw = np.sum(contributions) * bitline_gain # 简化的非线性读出响应 return raw / (1 + np.abs(raw)) # 模拟一次多位输入的拆分累加 w_row = np.random.choice([-1, 1], 64) in_val = 0.7 # 一个被量化的输入 bits = [(in_val >> i) & 1 for i in range(4)] # 4位拆分 result = 0 for i, b in enumerate(bits): partial = array_compute(w_row, np.full(64, b)) result += partial * (2 ** i) print("拆分累加结果:", result)跑这段你就会发现一个坑:位拆分的顺序和权重必须对应正确,否则结果全错。这个坑我在复现的时候第一遍就踩了,调了半天才发现是位序反了。论文里这种细节通常一笔带过,但复现时正是这些地方最容易出错。
4.3 系统级联仿的注意点
第三步才是把前面两步拼起来做端到端联仿。到这一步,我建议不要一上来就搭完整网络,先用一层的规模跑通链路:量化、拆分、阵列计算、读出、累加、反量化。链路跑通了再往上叠层。联仿里最容易被忽略的是数据在模拟域和数字域之间来回转换的边界条件——什么时候是模拟量、什么时候已经变成数字量、缩放因子在哪一步应用,这些必须在仿真里明确标出来,否则结果对不上你都不知道错在哪。
5. 学习过程中踩到的坑与排查清单
最后这部分是我读文献和动手验证时攒下来的经验,尤其是那些论文不会写、但实际操作绕不开的细节。
5.1 常见疑惑与排查速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 复现精度远低于论文 | 误差模型太理想或太悲观 | 先对齐误差量级,再逐步逼近论文设定 |
| 能效数字对不上 | 计算口径不一致 | 确认是阵列级、宏级还是系统级 |
| 输出结果符号反了 | 位拆分顺序或权重编码错误 | 检查权重和输入的编码约定 |
| 阵列输出饱和 | 位线增益过高或单元数过多 | 调低增益或减少并行单元数 |
| 联仿结果漂移 | 模拟数字边界缩放因子错位 | 标出每一步的数值定义域 |
5.2 几条我自己总结的读论文心得
第一条,先读图和表,再读正文。顶会论文的图和表是作者压缩出来的精华,一张标注清楚的架构图能顶好几段文字。我通常是先把所有图翻一遍,脑子里有个轮廓,再回头读文字补细节。
第二条,遇到漂亮数字先问口径。任何报出来的指标,先问它是怎么算的、算了哪些部分、和谁比。很多看起来碾压的数字,换个口径就平平无奇了。
第三条,把"为什么"写在旁边。读每个设计点时,强迫自己用一句话写出作者为什么这么选。写不出来的地方,说明你还没读懂,得回去重看。
第四条,动手算一遍关键参数。论文里的位宽、阵列规模、拆分次数这些参数,自己按公式推一遍,往往能发现一些作者没明说的约束条件,也能判断这个方案在你的场景里能不能用。
第五条,注意论文没说的部分。一篇论文的局限往往藏在它避而不谈的地方:没报端到端吞吐、没报最坏工艺角、没报温度变化下的表现。这些空白恰恰是你在实际项目里最需要自己去补的。
把这五条用在15.8上,我对它的判断是:这是一篇在精度和能效平衡上做得比较扎实的CIM工作,它的价值不在于某一个指标有多炸裂,而在于它把模拟计算的误差处理得比较务实,给出了从电路到网络准确率的完整链条。如果你在做类似方向,它的校准策略和位拆分方案是可以直接借鉴的;如果你只是了解趋势,记住它解决的核心矛盾就够了。我自己在复现它阵列计算逻辑时踩的位序坑,还有读指标时先问口径的习惯,都是从这篇里实打实磨出来的经验,希望能帮你少走点弯路。