第 8 环 · 注意力 QKT(用 KV 算分)
同一个圆心:一台开机只有一个 AI 的手机。上一环把历史 K/V 驻留到片上,AI 有了「记忆」;但记忆躺着没用——它得被「用起来」。注意力第一步:当前这个字(查询 Q)要回看历史每一个字(钥匙 K),算一个「相关性分数」。分数越高,说明当前这个字越该关注那个历史字。本环就是给芯片造一台「算分引擎」。
第 4/5 环已把权重和激活都压成 INT4;第 7 环历史 K/V 已驻留片上。本环顺着这条「内存带宽优先、端上算力紧凑」的主线,做 Q·KT 点积。关键是软硬协同的分工:上下文有多长不该由软件去数。所以把「遍历历史」这件事做进硬件——核只喊一声「开始算分」,引擎自己从槽位 0 走到最后一个,算完全部分数并报出最相关的那个(最大分数 + 槽位号)。上下文长度对软件透明。
新增 rtl/attn_qk.sv:内建遍历 FSM(IDLE→ISSUE→CAP→DONE),逐槽位从 KV Cache 读 K,与锁存的 Q 做逐 lane 点积(INT8×INT8→INT16),硬件比较取最大分数与 argmax 槽位。挂进 min_bus.sv,映射 5 个寄存器,并给 KV 读端口加「引擎/核分时复用」仲裁:
0x034 → ATTN_Q 查询向量 Q(4×INT4 打包) 0x038 → ATTN_START 写 1 → 触发引擎遍历全部 KV 算 QK^T 0x03C → ATTN_SCORE 读最大分数 best_score 0x040 → ATTN_IDX 读 argmax 槽位 best_idx(哪个历史字最相关) 0x044 → ATTN_BUSY 读引擎忙闲
circle08/boot.s:预填充 4 条 K(含一条负值验证 INT4 有符号),下发 Q=[1,2,3,4],触发算分,读回分数与槽位。
如果只是「逐 lane 复制」而不是真点积,四个槽位分数会一模一样;现在是 10/20/30/−10 逐槽不同,说明跨 lane 乘累加真的算对了;argmax 选中最高分槽位 2,负值槽位被正确排除。屏幕 = (2<<8)|30 = 0x21E = 542——证明芯片能「用记忆算分」,注意力第一步落在硅上。
本环走完一整圈:目标 → AI 回看历史要先算「谁最相关」 → 用 KV 算 Q·KT 的硬件算分引擎落地 → 打分/argmax/有符号校验跑通 → 回到目标。AI 的注意力从「记忆躺着」走到「主动打分」,而打分这件事由芯片自动完成,软件不必数上下文。离一台本地跑大模型的手机,又近一圈。
第 9 环:Softmax · 分数归一化——把这一串原始分数变成 0~1 的注意力权重(可做近似/定点除法),让「相关度」变成能用来加权求和 V 的权重。