🎯 圆心 · 目标(每环都从这里出发)
造一台 Salt OS 手机:基于 DeepSeek 的 AI 操作系统,iPhone 4S 外形(含底部圆形 Home 键),开机只有一个 AI(无桌面、无图标),软硬协同设计(OS + 芯片 + 主板一起做),为别人搭台、让别人在平台上赚钱。

🧭 完整框架(同心圆)

①文字闭环 → ②技术深入 → ③挂SRAM → ④权重INT4 → ⑤激活INT4 → ⑥NPU.STORE → ⑦KV Cache → ⑧注意力QK^T → ⑨Softmax → ⑩加权V → ⑪前馈FFN → ⑫残差·LayerNorm → ⑬多层堆叠 → ⑭QKV投影 → ⑮K/V分离 → ⑯自回归解码 → ⑰连续生成 → ⑱采样 → ⑲…

本环:让芯片「抽签」说话,不只挑最大的那个字

① 目标是什么

同一个圆心:一台开机只有一个 AI 的手机。上一环芯片已经能从残差流吐出下一个 token,并且一轮轮往下写、写到停止符才停——「说话」这件事在芯片上闭合了。但每一轮它都是 argmax:只取分数最大的那个字。问题在于,这等于把分数的分布抹平了——第二高分的候选永远没机会,同一段开头永远写出同一句话,那不是在说话,是在复读。真模型是按分数的分布抽签:温度高时谁都可能被抽到(话说得活),温度低时几乎只取最大(话说得准);Top-K 则先砍掉长尾里那些几乎不可能的候选,只在最有希望的 K 个里抽,免得抽到荒谬的字。这一环把「抽签」做成片上的采样器——同一个开头能写出不一样的句子。

② 怎么实现(不脱圆心)

延续「端上紧凑、全定点、每步硬件自洽」的主线,新增一个引擎,接在上一环那一页里:
· 采样器(sampler)——输入 LM 头的 logits,输出采样出的 token:温度把 logits 右移 temp_shift 位(移得越多分布越平,候选越容易被翻盘);Top-K 让硬件同时找出最高分与次高分两个候选,只有两者差距 margin ≤ 1 才允许抽签,否则稳取 top-1(分布太尖,没得选);抽签用片上 LFSR(8bit xorshift),每采样一次走一步;
· 软硬协同分工——「分数怎么算」(LM 头投影)是引擎的活;「在分数上怎么选」(温度 + Top-K + 抽签)也是硬件的活;核只负责把 LM 头的结果送进采样器、把采样出的字登记进生成循环;
· 只扩不推翻——寄存器接在第十六圈页内(0x464 起,同 bit10 守卫),前 17 圈的地址映射一个字不动,解码循环的其余部分完全不变。

③ 本环做了什么(真代码)

新增 rtl/sampler.sv:片上采样器(温度 + Top-2 候选 + LFSR 抽签)。

scaled[t]  = logits[t] >>> temp_shift        # 温度:右移压平分布(有符号)
top1/top2  = 最高分 / 次高分候选(并列取小下标,保证确定性)
margin     = top1 - top2
flip_en    = (top_k >= 2) && (margin <= 1)    # 分布太尖就不抽,稳取 top-1
coin       = lfsr[0]                          # 抽签:看随机位
token      = flip_en ? (coin ? top2 : top1) : top1
lfsr_next  = {lfsr[0]^lfsr[2]^lfsr[3]^lfsr[4], lfsr[7:1]}   # 每采样一次走一步

min_bus.sv 挂载并新映射(仍在 LM 头 / 词嵌入页内,0x464 起):

0x464 → SMP_TEMP    温度(logits 右移位数,0=原始温度)
0x468 → SMP_TOPK    Top-K(1=只取最大;2=最高+次高,可抽签)
0x46C → SMP_SEED    写 LFSR 种子(单拍装载,复位 0xA5)
0x470 → SMP_START   写 1 → 采样一次(锁结果 + LFSR 前进一步)
0x474 → SMP_SRC     logits 源:0=核写入的 SMP_L0..3;1=直接取 LM 头
0x478 → SMP_TOKEN   读采样出的 token(不只 argmax)
0x47C → SMP_T1      读最高分候选号   0x480 → SMP_T2  次高分候选号
0x484 → SMP_MARGIN  读 top1-top2 分差 0x488 → SMP_FLIP 本次是否翻到 top-2
0x48C → SMP_FLIPS   读累计翻盘次数  0x490 → SMP_LFSR  当前 LFSR 状态
0x494 → SMP_BUSY    读采样器忙闲(含刚下发那拍,无空窗)
0x498~0x4A4 → SMP_L0..L3  核写入的 logits(备选源)

最小 OS 只改「吐 token」这一步,其余解码循环原样不动:

SMP_TEMP=2、SMP_TOPK=2、SMP_SEED=0xA5、SMP_SRC=1(直接取 LM 头 logits)
L_gen_body:  ... 两层块 → LM_X → LM_START → 读 LM logits
             → SMP_START(温度右移 + Top-2 + LFSR 抽签)
             → 读 SMP_TOKEN = 采样出的字(不再读 LM_TOKEN)
             → GEN_STEP_W 登记 → 未 done 就 j L_gen_loop
④ 怎么算成(闭环验证)
采样序列 [0, 2, 0, 2] · 第 1 步采样出 0(argmax 会给 2)· RESULT=PASS
最后残差流 [-6,-5,3,7] → 屏幕 0xFA03FB07 · SCREEN2 = 0x02040200(flips=2, count=4, tok_out=2, stop_hit=0)· 单元仿真 + 协同仿真 + 全量回归 22 项全部通过

这一环最直观的是「同样的开头,采样和贪心给出的字不一样」:
· 温度 shift = 2、Top-K = 2、种子 0xA5、停止符 3、最大步数 4、prompt = token 0;
· 第 1 步:残差流 → LM 头 logits [3,-2,4,-1]。温度右移 2 位 → [0,-1,1,-1],top1 = 2(分 1)、top2 = 0(分 0),差距只有 1 → 允许抽签;LFSR 首步最低位 = 1 → 抽到 0。上一环的 argmax 会给 2——这就是「不只取最大」的硬证据;
· 第 2 步:LFSR 走一步后最低位 = 0 → 落回 top1 = 2;
· 第 3 步:logits [7,-3,-12,8],argmax 明明是 3,但温度把它压成 [1,-1,-3,2],top1 = 3(分 2)、top2 = 0(分 1),差距又是 1 → 抽签再次翻盘到 0。温度真的改变了结果;
· 第 4 步:回到 2;4 步走完到长度上限而停(这段没吐停止符),SCREEN2 里 flips=2(第 1、3 步各翻一次盘);
· 屏幕 SCREEN = 0xFA03FB07 = 4194564871(最后一步残差流),SCREEN2 = 0x02040200,与软件参考模型逐位一致;单元仿真还覆盖了「Top-K=1 关掉抽签永远取最大」「分数拉开就不抽」「换种子改变抽签结果」「翻盘计数」四种情形。

⑤ 回到目标

本环走完一整圈:目标 → 一个开机只有一个对话窗口的手机,它说话得像人——同一句话开头能给出不同的说法,而不是复读机 → 硬件在 LM 头与生成控制器之间插入采样器(温度右移 + Top-K 两候选 + LFSR 抽签),OS 把「吐 token」从 argmax 改成采样,解码循环原样保留 → 采样序列与软件参考模型逐位一致、与贪心结果明显不同,单元仿真 + 协同仿真 + 回归 22 项全绿 → 回到目标。这块芯片第一次不是「只挑最大的字」,而是按温度与 Top-K 抽签说话。离一个会说话、不像复读机的 AI 手机,又近一圈。下一环接上更完整的一步:现在 KV 只放得下 4 个 token(长度一到就停),下一步把 KV 做成环形缓冲——写满了就绕回去覆盖最老的,长序列不再溢出,才能真的聊下去。