YAOTU INSIGHTS

RERAM带符号乘法:差分对编码、误差预算与标定实践

RERAM带符号乘法:差分对编码、误差预算与标定实践
1. “带符号”三个字才是 RERAM 乘法的第一道坎RERAM阻变存储器阵列做乘法最容易让人上头的部分是“存算一体”这四个字——一次读操作就把整行的乘累加算完能量效率比在存储和计算之间搬数据高出两个量级。但真正在实验室里把板子焊好、把测试程序跑起来的人第一天就会撞上同一个问题权重有正有负而器件的电导永远是正的。RERAM 带符号乘法这件事说穿了就是一场和“非负物理量”搏斗的工程。我刚开始接触这个方向时也天真过觉得负权重无非是减一下软件里w [1.5, -2.3, 0.7]硬件里怎么就不能减。等到把 1T1R 单元测出来的电导曲线摊在屏幕上才明白你手里的可调旋钮只有一个方向从几微西门子转到几十微西门子想让它变成负的物理上根本没有这条路。这篇文章我想讲清楚三件事符号编码的几条技术路线到底怎么选、差分对方案的误差预算怎么算、以及从浮点权重映射到实际电导值的完整标定流程。读者画像我大概摸过——有做器件工艺、想往下游电路走的朋友也有做算法、想把这套东西加速起来的软件背景同学。所以我会尽量把器件物理和电路细节都摆到台面上公式给了、参数给了能照着算。先说结论性的判断RERAM 带符号乘法不是一个“功能”而是一整套符号编码 误差预算 标定补偿的组合工程。任何一环没做扎实最后出来的结果都只是看着像乘法。1.1 器件物理决定了电导天生非负要理解为什么符号这么难处理得回到 RERAM 的开关机制本身。阻变层通常是金属氧化物比如 HfO2、TaOx、AlOx 这一族夹在上下电极之间。加电压之后氧化层里会形成氧空位聚集成的导电细丝conductive filament细丝一接通器件就从高阻态HRS跳到低阻态LRS反向电压让细丝在电极界面处断掉又回到高阻。这个过程的本质是“连接/断开”和“粗细变化”不是“正向/反向”。所以电导 G 的取值范围是 [G_off, G_on]两端都大于零。典型数值我给个参考区间G_on 在几十微西门子量级G_off 在几微西门子甚至更低开关比做到 10 到 100 之间比较常见。注意这个量级差异很大不同工艺、不同材料体系、不同阵列规模实测值可能差一个数量级我后面所有的计算都会明确标注假设值你换成自己的实测数重算就行。模拟计算用的不是二值开关而是中间态。多值电导靠控制限流compliance current或者 SET 的停止电压来实现——细丝越粗电导越大。这里有个关键认知多值态的稳定性远不如二值态。细丝是原子尺度的结构几个氧空位的得失就能让电导跳变几个百分点这是循环间变异cycle-to-cycle variation的主要来源也是后面误差预算里最顽固的一项。注意追求高开关比的工艺路线和追求高线性度、多值稳定的路线优化方向经常是冲突的。做数字存储的器件希望 on/off 比越大越好做模拟计算的器件反而需要把中间态铺开、铺匀。选器件的时候别直接拿存储用的成熟工艺参数来套。1.2 三条符号编码路线的取舍业界处理带符号权重主流的思路可以归成三类我按实际项目里的落地难度排个序。第一类是差分对differential pair也是最常用的。一个权重用两个器件表示W k·(G − G−)。正权重时 G 大 G− 小负权重时反过来。优点是线性度好、直流偏置天然对消代价是面积翻倍而且两个器件的噪声叠加有效位数掉半位。这是我要重点展开的方案。第二类是单端加参考列。每个权重只用一个器件把权重整体平移到非负区间G G_min k·(W W_max)。读的时候用一条不参与计算的参考列提供 G_ref做电流相减。面积省一半但对参考列的匹配度要求极高参考列的漂移会直接变成全局零点漂移属于“省了面积、欠了稳定性”的买卖。第三类是输入侧编码。权重全保持非负把符号转移到输入上用正负脉冲对或者双极性输入电压实现。这条路在某些脉冲神经网络的应用里很好用但如果你要做的是标准的多层感知机推理激活值经过 ReLU 之后本来就非负这套编码反而多此一举。还有一类偏数字的思路用 RERAM 做查找表或者逻辑门按补码逐位算乘法。这条路精度可控但丢掉了模拟域一次读完成乘累加的最大优势我这里不展开。方案面积开销线性度零点稳定性适用场景差分对2 倍好好通用推理加速单端加参考列1 倍中依赖参考列面积敏感、精度要求中低输入侧编码1 倍好好脉冲神经网络、激活非负场景数字位串行1 倍以上优优小批量、高精度需求选型的核心判断点是你要的精度和你能接受的面积。如果目标网络量化到 4 bit 权重就够单端方案加上离线标定完全能撑住如果要求 8 bit 等效精度差分对几乎是唯一现实的选择因为你需要那个共模抑制来压住器件漂移。2. 差分对方案结构细节与误差预算怎么算差分对听起来简单实现起来细节一大堆。我见过不少方案在设计文档里画得很漂亮一上测试台就发现输出全是噪声问题往往出在读通路的虚地没做好或者误差预算根本没算过。2.1 差分结构的电流方程与共模对消先把基本方程摆清楚。一个 N×N 的交叉阵列第 i 行第 j 列的权重由两个 1T1R 单元组成分别接在同一位线的两条支路上共享同一个输入电压 V_j。位线末端接跨阻放大器TIA虚地电位两条支路的电流在这里相减I_i Σ_j V_j · (G_ij − G−_ij)把 G± 拆成共模加差模G G_c ΔG/2G− G_c − ΔG/2那么I_i Σ_j V_j · ΔG_ij共模项 G_c 在相减时对消掉了。这一步非常关键因为共模电流是 Σ V_j · G_c在 N64、V0.1 V、G_c27.5 μS 的条件下这一项能到 176 μA而差分信号本身可能只有几十微安。如果 TIA 不做虚地、不做差分这 176 μA 会直接吃掉 ADC 的动态范围你测到的全是偏置。所以虚地不是“可选的优化”是差分对方案成立的前提。TIA 的反相输入端保持在固定电位通常是读电压的一半左右位线电阻上的压降被钳住同时共模电流在反馈电阻上产生的偏置可以被后级差分放大器消掉。反馈电阻的选择有个简单的算法。最大输出电流 I_max 由最坏情况决定所有输入电压拉满、所有差分电导拉到最大值。取 I_max N · V_max · ΔG_max反馈电阻 R_f 要满足 I_max · R_f 电源轨留出的余量。比如 N64、V_max0.1 V、ΔG_max45 μSI_max ≈ 288 μA如果运放输出摆幅留 1 VR_f 不能超过约 3.4 kΩ。实际取值我一般留 20% 余量取 2.7 kΩ。2.2 有效位宽怎么算一份可以直接套用的预算表这是我认为最该被认真算、却最常被跳过的一步。很多人直接说“我们做到了 6 bit”但问他 6 bit 是怎么来的答不上来。有效位数是噪声预算算出来的不是设计目标喊出来的。先从单器件噪声起算。循环间变异用相对标准差表示实测常见 2% 到 8% 这个范围跟编程策略关系很大。器件间变异device-to-device更大但可以通过逐个标定消除一部分属于“可标定的系统误差”而循环间变异是随机误差标不掉。差分对的噪声要乘以 √2。因为 ΔG G − G−两个器件独立波动方差相加σ_ΔG √(σ_G² σ_G−²) ≈ √2 · σ_G而差分信号的满量程是 ΔG_max G_on − G_off。所以可分辨的级数约等于N_level ≈ (G_on − G_off) / (√2 · σ_G)代入一组典型值G_on 50 μSG_off 5 μS相对标准差 5%取 G_c 27.5 μS 时 σ_G ≈ 1.38 μSσ_ΔG ≈ 1.95 μS N_level ≈ 45 / 1.95 ≈ 23log2(23) ≈ 4.5 bit。这个结果和文献里大量报告的 4 到 6 bit 是吻合的。所以别指望靠差分对做出 8 bit 等效精度除非你在器件层面把变异压到 1% 以内或者上冗余编码、多次采样平均这类系统级手段。参数符号典型值对精度的影响开态电导G_on50 μS决定满量程上限关态电导G_off5 μS决定满量程下限单器件相对变异σ_G/G5%线性拉低有效位数差分噪声σ_ΔG√2·σ_G每增加 1 倍位数减 1等效级数N_level约 23对应 4.5 bitADC 量化噪声—小于 σ_ΔG/2不成为瓶颈即可最后一行值得强调ADC 的量化步长要小于差分噪声否则 ADC 成了瓶颈但也没必要做得很精细因为噪声已经在那儿了ADC 做得再好也提不了位数。这个平衡点很多人搞反花大价钱堆高精度 ADC结果精度被器件变异锁死。2.3 读通路里几个容易翻车的细节读电压的选取是个三方拉锯。电压高信噪比好但会触发读扰动read disturb——读操作本身就在给器件施加应力读多了电导会慢慢漂。电压低扰动小但信号弱、寄生影响大。我实测下来读电压控制在 0.1 V 到 0.2 V 之间是个比较稳的区间具体值要看你的器件 SET 阈值分布一般取 SET 电压的五分之一以下比较安全。选通管的状态要确认。1T1R 结构里字线电压要保证选通管进入深线性区否则管子的导通电阻会和 RERAM 电导串起来读出来的电流是非线性的。这一点在低电导高阻状态下尤其明显因为此时 RERAM 电阻大管子压降小、影响看似不大但一旦电导拉高管子压降上来等效电导就被压低了表现为“大电导端被压缩”直接破坏线性度。实操心得标定的时候一定要单独测一条“纯选通管”的参考支路把管子的导通电阻 R_on 算出来。如果 R_on 跟 RERAM 的等效电阻在同一个量级那你的读电流必须用分压公式修正或者在版图上把管子加宽降低 R_on。我吃过这个亏第一批测试数据全是错的回头查了三天才发现是管子压降。另一个细节是差分对两端的时序匹配。G 和 G− 分属两条支路如果两条支路的寄生电容不一致读脉冲的上升沿会在两端产生不同的响应采样时刻选得不对误差能到 10% 以上。解决办法是版图上严格对称以及采样时刻选在信号完全稳定之后宁可牺牲一点速度。3. 从浮点权重到电导值量化映射与写入标定前面讲的是“怎么表示符号”这一节讲“怎么把算法里的数字真的写进器件”。这一步的工程含量比很多人想的高写不准前面的电路设计全白搭。3.1 映射公式与步长计算假设网络里某层的权重范围是 [−W_max, W_max]量化位数 b差分电导映射成线性关系G G_c k · W G− G_c − k · W其中 G_c (G_on G_off)/2比例系数 k (G_on − G_off) / (2 · W_max)。这么写有个好处W0 时两端电导相等都是 G_c差分输出为零零点自然成立。WW_max 时 G G_on、G− G_off正好用满整个窗口。这就是所谓的“对称映射”。不过对称映射对器件变异不友好因为实际电导不是理想线性的两端容易饱和。另一种做法是“非对称映射”把负权重段和正权重段分别标定成两条查找表代价是标定工作量翻倍。我的建议是器件线性度好偏差 5%就用对称映射否则老老实实做 LUT 分段补偿。量化步长 ΔW W_max / (2^(b−1) − 1)。对应的电导步长是 ΔG 2k·ΔW。这个 ΔG 必须大于差分噪声 σ_ΔG 的一半否则量化就失去意义了写进去也读不准。反过来如果算出 ΔG 远大于 σ_ΔG说明你可以提高量化位数。这条判据我一般用作设计检查项。举个具体例子W_max 1.0b 6则 ΔW ≈ 0.0159k 45 μS / 2 22.5 μS/单位ΔG ≈ 0.714 μS。而前面的 σ_ΔG 1.95 μS明显大于 ΔG 的一半说明 6 bit 量化在这个器件上其实吃不满实际有效位数会被噪声限制在 4.5 bit 左右。想提高就要么加大量程提高 k要么降噪声。这个判断在方案评审阶段就能做出来能省掉大量无用的调试。3.2 写入流程从 Forming 到 Write-Verify写入不是“加个电压就完事”而是一个带反馈的闭环过程。我把完整流程按顺序列出来。第一步是 Forming。新鲜器件处于初始态电阻极高需要一次软击穿建立导电通道。Forming 电压通常比正常 SET 电压高 30% 到 100%限流要设小比如 10 μA避免形成过粗的细丝导致后面写不出高阻。Forming 之后器件才进入可编程状态。第二步是 RESET 到高阻。把目标电导区间的上限先清掉后面从高阻往低阻方向逼近这叫“单向逼近”是提高多值写入成功率的关键策略。因为 SET 过程是渐进的、可控的而 RESET 往往是突变式的先高后低比来回调整靠谱得多。第三步是粗调。用一系列递增幅度的 SET 脉冲每个脉冲后读回电导一旦接近目标就停止。脉冲宽度建议从宽到窄先 100 ns 后 20 ns。步进幅度按指数分布比如电压从 0.8 V 到 1.4 V 分 8 档。第四步是细调加校验。用固定小幅度脉冲反复打每打一次读一次判断是否落在目标电导 ±ΔG/2 窗口内。命中就停超调了就整个 RESET 重来不要在已经写过的状态上做小幅 SET/RESET 拉锯战那会加速器件退化。第五步是验证把整行甚至整个阵列扫一遍记录每个单元的实际电导生成偏差表。这张表后面要做算法侧的补偿或者做模型校准用。整个流程的时间成本要算清楚。假设平均 15 次迭代、每次读脉冲 50 ns 加写脉冲 50 ns单个单元的编程时间在微秒量级。一个 64×64 的阵列有一万多个等效权重单元串行写完就是几十毫秒。这就是为什么量产方案都在推并行写入和批次编程。提示写验证环节的读操作本身也是扰动源。如果校验读得太多会把器件读偏。我的做法是校验读电压取正常工作读电压的一半并且限制每个单元的最大迭代次数超过就直接放弃重来不硬拉。3.3 一次完整标定记录去年我参与过一组 64×64 差分阵列的标定数据大致是这样G_on 实测均值 48 μS标准差 6 μS分布在 32 到 68 μS 之间G_off 均值 5.5 μS标准差 2.1 μS分布明显右偏这很典型高阻态的拖尾现象比低阻态严重得多。变异指标上器件间标准差约 12%循环间约 4.8%。这个组合意味着器件间变异必须在算法侧用逐单元标定消除循环间变异只能靠降低对比度或者多次采样来缓解。针对 4 bit 权重的目标我们算出来的映射系数是 k 21.3 μS步长 ΔG ≈ 1.34 μS和 σ_ΔG ≈ 1.9 μS 基本同量级所以实际有效位数只有 4 位出头。想推到 5 bit就必须把循环间变异压到 3% 以内这在当时的工艺上做不到最后方案降到了 4 bit 精度靠网络本身的冗余把精度损失吸收了。这个例子挺能说明问题带符号乘法的精度天花板是器件给的不是电路给的。电路做得再好也只能逼近那个天花板。4. 阵列级的系统性误差比噪声更麻烦的东西差分对解决了符号问题接下来是三座大山IR Drop、寄生串扰、漂移。这三样有个共同特点——它们是系统性的、可预测的但如果不处理会在整个阵列上产生空间上不均匀的误差比随机噪声更难排查。4.1 IR Drop 的定量估算交叉阵列的金属线不是理想导线。每个交叉点有电流流过线的串联电阻累积起来导致远离驱动点的单元实际看到的电压低于设定值。对 N64 的阵列如果每段线的电阻是 r最坏情况的电压降可以近似写成ΔV ≈ I_total · R_line_eff其中 R_line_eff 约等于 N·r/3这是个常用的经验近似反映的是平均路径长度。取 r 1.5 Ω、N 64则 R_line_eff ≈ 32 Ω。如果一行总电流 100 μA电压降就有 3.2 mV相对于 0.1 V 的读电压是 3.2% 的误差。看着不大但这个误差正比于该列电流总和也就是正比于该点的输入激活强度。也就是说同一列里输入越大的行误差越大——这是一种与数据相关的非线性失真比固定偏置讨厌得多。缓解办法有三条。一是降低线电阻加宽金属线或者用低阻金属层代价是面积。二是降低读电压但会牺牲信噪比。三是采用虚地读通路把位线钳在固定电位这样位线上的压降被钳住主要误差只剩输入侧字线的压降能砍掉一半左右。四是算法侧做补偿把这个误差建模进去在训练时就把畸变考虑进去这是最省硬件成本的办法。4.2 寄生漏电与串扰1T1R 结构本身抑制漏电效果好未选通的行列都是关断的。但寄生电容带来的串扰是躲不掉的。读脉冲到来时位线和字线的交叉电容会把邻近线的信号耦合进来表现为输出上叠加了与数据无关的抖动。处理办法是用差分读出。差分对本身就有一半的共模抑制能力两条支路的耦合往往同相相减后能消掉大半。另外在读脉冲前后做相关双采样correlated double sampling把脉冲前的底噪减掉能再压掉一部分。这两个手段我一般在方案设计阶段就固定下来不等到出问题再补。4.3 漂移与温度RERAM 的保持特性是个慢变量。低阻态在长时间不操作后电导会缓慢下降高阻态可能上升具体方向跟材料和细丝结构有关。测试台上一般看 10 小时到 1000 小时的退化曲线工程上更关心的是“一次编程之后能保持多久不失效”。温度系数也值得说一句。RERAM 的导电细丝是金属性的走的是电子输运温度升高电导下降是正的电阻温度系数这跟半导体正好相反。实测系数大约在 −2000 到 −5000 ppm/K 量级。温度变化 20 K电导就能漂 4% 到 10%直接吃掉一位精度。所以阵列上最好布温度传感器把温度读出来做实时补偿。这个补偿是乘性的用一个缩放因子就行实现很简单收益很明显。5. 常见问题与排查速查表踩过的坑整理成表遇到怪现象的时候可以照着对。这些问题里有一半是电路问题一半是写入流程问题区分的方法很简单看误差是随机的还是可重复的。随机的多半是噪声和时序可重复的多半是偏置和映射。5.1 现象、原因、对策对照现象可能原因排查动作对策输出整体偏向一侧TIA 虚地不准共模对消失效断开输入测静态输出校准 TIA 失调加调零电位器读数比预期低一截且不随位置变选通管压降等效电导被压缩测纯选通管支路电流加宽选通管或分压修正阵列边缘误差大、中心正常IR Drop 引起的空间不均匀扫描不同位置的同一权重虚地读 算法补偿小权重区域读不出差异量化步长小于噪声底算 ΔG 与 σ_ΔG 的比值提高量化步长或加冗余反复读同一单元数值持续变化读扰动降低读电压重测读电压降到 SET 电压 1/5 以下写完之后隔天失效保持特性退化做长时保持测试加温度补偿或用刷新机制同一行的不同列误差方向相反差分对两端匹配差测两端电导分布版图对称性复查逐对标定输出噪声随激活值增大而增大数据相关的线压降在固定权重下扫输入降低输入幅度增加列分段5.2 几个反直觉的坑第一个坑提高读电压不一定提高信噪比。短时间看是提高了长期看器件被读偏了标定表失效你会以为是阵列坏了。我有一次调试连续读了两小时做统计结果数据越来越“好”还高兴了一阵后来重编程才发现全偏了。第二个坑差分对两端不要同时编程。很多人图省事G 和 G− 交替写结果两个器件都处于不稳定状态。正确做法是先写一端到位再写另一端中间不做交替。这样两端的循环间变异相关性低差分噪声接近理想的 √2 倍否则会更大。第三个坑用软件仿真验证过不等于硬件能跑。行为级模型里噪声是独立同分布的实际器件的噪声有空间相关性和时间相关性还有漂移。仿真能跑通的补偿算法到了硬件上经常需要额外的在线校准环节。所以建模阶段就要把非理想因素尽可能建模进去尤其是漂移和温度这两个慢变量。第四个坑直流偏置不会自己消失。即便做了差分只要两端的寄生不对称、TIA 有失调输出就有残余偏置。这个偏置在推理里可能被后面的 ReLU 掩掉一部分但在回归任务里会变成系统性误差。建议每批推理前做一次“零输入校准”把当时的偏置记下来减掉成本很低。6. 先用行为级模型把坑踩一遍再流片最后讲一段建模因为我觉得这是投入产出比最高的环节。RERAM 带符号乘法的验证如果直接上硅片试一次流片几十万周期几个月出问题连排查的手段都有限。用 Python 建一个带非理想因素的行为级模型把误差预算、量化映射、漂移补偿都跑一遍两天能跑出上百组配置的对比能省下大笔冤枉钱。下面这段代码是我实际用过的一个简化版本核心是把差分映射、循环间噪声、量化误差都串起来输出等效的乘累加结果和有效精度。import numpy as np # 器件参数换成你自己的实测值 G_ON 50e-6 # 开态电导 G_OFF 5e-6 # 关态电导 SIGMA_REL 0.048 # 单器件循环间相对标准差 V_READ 0.1 # 读电压 def quantize_weight(w, n_bit): 对称量化返回量化后的权重和步长 w_max np.max(np.abs(w)) step w_max / (2 ** (n_bit - 1) - 1) return np.round(w / step) * step, step def weight_to_conductance(w, w_max): 差分映射返回正负两支电导 k (G_ON - G_OFF) / (2.0 * w_max) gc (G_ON G_OFF) / 2.0 return gc k * w, gc - k * w def read_with_noise(gp, gn, sigma_relSIGMA_REL, rngNone): 加循环间噪声后读出差分电导 rng rng or np.random.default_rng(0) gp_n gp * (1.0 rng.normal(0.0, sigma_rel, gp.shape)) gn_n gn * (1.0 rng.normal(0.0, sigma_rel, gn.shape)) return gp_n - gn_n def enob_from_noise(delta_g_max, sigma_rel, gc): 由噪声底估算等效位数 sigma_dg np.sqrt(2.0) * sigma_rel * gc levels delta_g_max / sigma_dg return np.log2(levels), sigma_dg if __name__ __main__: rng np.random.default_rng(2024) w rng.normal(0.0, 0.4, (64, 64)) # 一层权重 v rng.uniform(0.0, 1.0, 64) # 输入激活非负 gc (G_ON G_OFF) / 2.0 enob, sigma_dg enob_from_noise(G_ON - G_OFF, SIGMA_REL, gc) print(f噪声估算等效位数: {enob:.2f} bit, sigma_dG {sigma_dg*1e6:.2f} uS) for bits in (3, 4, 5, 6): wq, step quantize_weight(w, bits) gp, gn weight_to_conductance(wq, np.max(np.abs(wq))) dg read_with_noise(gp, gn, rngrng) i_out V_READ * (dg v) # 硬件乘累加结果 i_ideal V_READ * ((gp - gn) v) # 理想结果 err np.linalg.norm(i_out - i_ideal) / np.linalg.norm(i_ideal) print(f{bits} bit 量化 - 相对误差 {err*100:.2f}%)跑出来的结果一般是这样3 bit 和 4 bit 的相对误差接近说明噪声才是主导5 bit 开始量化误差继续下降但总误差受噪声限制不再明显改善6 bit 的时候改善几乎为零。这正好对应前面算出的 4.5 bit 天花板。模型的价值就在这里——它能在你动手做版图之前就告诉你“再往上堆位数是浪费面积”。这个模型里还有几件事没建模实际项目里建议补上一是器件间的固定失配用一组固定的偏移量模拟然后跑标定补偿算法二是漂移用一个随时间单调变化的乘性因子三是 IR Drop按行累加电流再反算压降做成迭代。三样都加上之后模型跑出来的误差分布会和实测非常接近我做过对比趋势基本对得上。真正流片之前我一般还会做一件事把模型输出的误差分布喂给网络做一次微调fine-tune看看精度损失能不能被训练吸收掉。很多时候答案是能尤其是卷积层冗余本来就大。这就给了你一个降精度的理由而面积和功耗的收益是实打实的。这套东西我在两个不同规模的阵列上验证过64×64 和 128×128结论一致差分对的符号处理本身不是瓶颈瓶颈永远是器件变异和阵列寄生。把这两样在模型里摸清楚硬件阶段能省掉至少一半的调试时间。