第 10 环 · 注意力加权 V(输出)
同一个圆心:一台开机只有一个 AI 的手机。前两环芯片先会「打分」(每个历史字跟当前字有多相关),又会「分权重」(把分数变成一串加起来正好是 1 的归一化权重)。但权重本身还只是数字——真正要用的是:拿这串权重去「加权」每个历史 token 的信息(V,值向量),再求和。这一步就是注意力加权 V,做完它,一次完整的注意力(QK^T → Softmax → ×V)就在芯片上闭合了。本环给芯片造一台加权引擎。
延续「端上算力紧凑、全部定点、不用浮点」的主线:
· 权重是上一环 softmax 产出的 Q8(256 = 1.0);
· V 是 INT4(-8..7),4 个 lane;
· 每个槽位做 w × V[lane] 乘累加(32bit 有符号),全部槽位累完再 (acc + 128) >> 8 四舍五入,把数拉回原来的量级——即 out = Σ wᵢ·Vᵢ / 256。
引擎自己内建遍历:核喊一声 start,硬件从槽位 0 走到 n-1,同时读权重和 V,逐 lane 乘累加。历史有多长,对软件透明。
新增 rtl/attn_av.sv:权重读端口 + V 读端口(双读,同拍取)+ 遍历 FSM——
IDLE → 收 start,清 4 个 lane 累加器 ISSUE → 同一拍发 权重地址 & V 地址 CAP → 收回两者,acc[l] += w × V[l] ROUND → 全部槽位走完,(acc+128)>>8 一次输出 4 个 lane DONE → 拉高 done,y_out 保持有效
min_bus.sv 挂载加权引擎,并给权重 RAM 加了一路读端口、把 KV 读端口改成三方分时复用(算分引擎 > 加权引擎 > 核)。新映射:
0x060 → AV_START 写 1 → 触发加权 V(硬件遍历全部槽位乘累加) 0x064 → AV_OUT0 读注意力输出 lane0(32bit 有符号) 0x068 → AV_OUT1 读 lane1 0x06C → AV_OUT2 读 lane2 0x070 → AV_OUT3 读 lane3 0x074 → AV_BUSY 读加权引擎忙闲(含刚下发那拍,无空窗)
最小系统的语义简化(如实说明):KV Cache 每个槽位只有一个 32bit 载荷,本环把它当作 V 来加权——它同时也是前两环算分用的 K。一个字先承载 K/V,后续圈再把 K/V 拆成双份。
这一环最能看出注意力的意义:第 4 个 token 最相关,拿到 251/256 ≈ 98% 的权重,于是加权求和的结果几乎就等于第 4 个 token 的 V——输出 [4,4,4,4]。换成权重均匀(各 1/4)就得到四个值的平均,也验证了另一条路径。屏幕 = 0x04040404 = 67372036——证明芯片能算出注意力的输出向量。
本环走完一整圈:目标 → AI 要「按相关度取用历史信息」 → 硬件用归一化权重加权 V 并四舍五入输出 → 输出≈最相关槽位 V / 均匀权重取平均 / 负值有符号 / 空上下文全部跑通 → 回到目标。至此一次完整的注意力(QK^T → Softmax → ×V)在芯片上闭合了——大模型心脏里的那块「注意力」,从打分、到分权重、到取用信息,三层都在自研芯片上跑通。下一步,注意力输出要送进前馈网络(FFN 的两个矩阵),继续往一个完整 Transformer 块走。离一台本地跑大模型的手机,又近一圈。
第 11 环:前馈网络 FFN · 用 NPU 算两层——把注意力输出送进 FFN 的两个权重矩阵(先升维、激活、再降维),复用已有的脉动阵列 NPU,让芯片跑通 Transformer 块的第二半。