🎯 圆心 · 目标(每环都从这里出发)
造一台 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分离 · ⑯自回归解码 · ⑰连续生成 · ⑱采样 · ⑲KV环形缓冲 · ⑳滑窗注意力 · ㉑多头注意力 · ㉒输出投影 W_o · ㉓多头块入残差流 · ㉔多头块进解码循环 · ㉕整层一块 · ㉖整网一块 · ㉗ISA契约 · ㉘权重内存化 · ㉙权重预取双缓冲 · ㉚权重分块 · ㉛矩阵行块 · ㉜行块自增 · ㉝起始块号可编程 · ㉞段表连扫 · ㉟段表流式取 · ㊱行块矩阵扫 ISA 化 · ㊲描述符链 · ㊳链槽直喂 · ㊴主路走链 · ㊵链预投下层 · ㊶跨趟预投 · ㊷权重池三 bank · ㊸权重池 ISA 化 · ㊹一步生成 ISA 化 · ㊺整段生成 ISA 化 · ㊻对话回路 · ㊼对话 ISA 化 · ㊽会话记忆归档 · ㊾多会话槽位 · ㊿会话槽位 ISA 化 → 51…

本环:让「换一个人说话」也成为一个动作

① 目标(这一环要解决什么)

第 49 环给会话记忆区加了槽位目录,会话号终于能各自住一槽。可「换一场会话」在软件里仍是一串手工流程:open(查/分槽)→ 读基址 → 写 ARC_BASE → 发恢复 → 轮询 → 跑完再发归档 → 读摘要/字数 → commit → 清会话态。一次「切人」要核写六七个寄存器、跑两轮轮询,中间还得自己记住「我现在在哪个会话里」。

手机上的对话窗口切换是最频繁的动作。这一环把整场「开关场」收进一条指令 npu.sess:

  • enter(cmd=1):查/分配槽位 → 把该槽基址交给归档引擎 → 恢复 K/V → 进这场;
  • leave(cmd=2):把当前 K/V 归档到当前槽 → 摘要/字数 commit 回目录 → 退出;
  • switch(cmd=3):先 leave 再 enter(换一个人说话)。

引擎自己记住「现在在哪一场」(cur_sid/cur_slot/cur_base/cur_valid);enter 另一场时自动先把当前这场收好——软件不用再提醒它「先存再取」。

② 实现(做了什么)

新增 rtl/sess_switch.sv(会话开关场引擎),把两个已经验证过的子引擎串起来——槽位目录(第 49 环)与归档引擎(第 48 环):

一条 npu.sess 的片内时序:
  leave  : 摆 cmd/base/sid → 发归档 save → 等 busy 落
           → 取 arc_digest/words → 摆 commit → 发目录 start → 等 busy 落
  enter  : 发目录 open → 等 busy 落 → 取 slot/base
           → 摆 ARC_BASE/sid → 发归档 load → 等 busy 落 → 进这场
  switch : 先 leave(当前),再 enter(目标)

ISA 契约:核升 v15,custom-1 加 funct3=100:npu.sess rs1, rs2——rs1 = 会话号,rs2[1:0] = enter/leave/switch。EX 拍两个操作数直接送进开关场引擎,核一个配置寄存器都不写。

min_bus 新页 0xD00(bit11+bit10+bit8):
0xD00 SX_CMD   0xD04 SX_SID   0xD08 SX_GO   0xD0C SX_RST
0xD10 SCREEN26
读回:0xD20 BUSY   0xD24 DONE   0xD28 OK     0xD2C SLOT
      0xD30 BASE   0xD34 CUR    0xD38 WORDS  0xD3C DIGEST
      0xD40 MISMATCH 0xD44 ENTER 0xD48 LEAVE  0xD4C CORE
      0xD50 MEMOK  0xD54 STATUS 0xD58 ERR    0xD5C ST
npu.rd 新选源 21~29:
  21=槽号 22=基址 23=状态{mem_ok,ok,err,cur_valid}
  24=当前会话号 25=本次字数 26=本次摘要 27=逐条比对不一致数
  28=累计 leave  29=累计 enter

软硬协同的三个接口约定:① 目录只给基址,归档只认基址——两个引擎之间不传内容;② commit 的摘要/字数直接取归档引擎刚算出来的 digest/words,核不搬运;③ 没有旧上下文的会话(空槽/首次开机)不是错误:enter 照常成功,mem_ok=0 表示「这场还没存过东西」,上层当新会话继续。

③ 怎么验证(硬证据)

协同仿真 tb_min_soc50 在既有全部观察点之后跑完整的「开关场」流程:

步骤做什么结果
M-BMMIO 三条 sw 进 B 场(第 49 环存过)cur=0x52 / 槽1 / 基址0x40 / SX_CORE=3
RST清计数core=0 / enter=0 / leave=0,会话状态保留
E0一条 npu.sess 进新会话 C(0x61)自动 leave 了 B(leave=1);新槽 0 / 基址 0 / mem_ok=0
C跑一场对话(prompt=0)→ 一条 leave C归档 12 字 → commit → digestC
E2一条 enter 新会话 D(0x62)槽 2 / 基址 0x80 / mem_ok=0
D另一场对话(prompt=2)→ 一条 leave DdigestD ≠ digestC
E4一条 enter Cmem_ok=1 / mismatch=0 / 摘要 == digestC
E5一条 enter D → 跑整网摘要 == digestD → Y_D
E6一条 enter C → 跑整网Y_C ≠ Y_D(恢复出的上下文真的影响输出)
收尾核写入口次数SX_CORE=0 / enter=5 / leave=5
SCREEN26 = 0x050500FF
enter 5 次 | leave 5 次 | 核写入口 0 次 | 八条证据位全 1

单元仿真 tb_sess_switch 用真的 sess_slots + kv_archive + 一个两层 K/V 环模型对表:一条 enter 取回 12 字且环内容逐条等于参考;换场自动 leave 前一场;leave 后目录不作废;再 enter 上下文原样回来;重复 enter 同一场是 no-op;新会话空槽也能进;switch 一条命令换人;不在场里时 leave 明确 err=1。

主通路未动:SCREEN=0xF905FD04、SCREEN2=0x02040404、SCREEN23~25 与第 25~49 环逐位一致。

④ 调试中钉住的钉子

其一(4bit 装不下 17 个状态)。开关场有 18 个状态,状态寄存器写成 [3:0],4'd16 被截成 0、4'd17 截成 1,S_LOADD/S_FIN 直接和 S_IDLE/S_ARC 撞在一起(Verilator 报 CASEOVERLAP)。扩到 5bit。教训:状态数超过 15 就必须先看位宽——这类截断不会报错,只会让 FSM 走错门。

其二(测试台连线名差了三个字母)。DUT 端口接的是 sxs_ss_start,测试台 mux 里写的是 sx_ss_start。Verilog 默认允许隐式网线,于是悄悄生成一根 1bit 的空线,start 恒 0——引擎收到一条「没有 start 的指令」,测试台却在等 busy。教训:开 default_nettype none,或至少把「没驱动的线」当错误查。

其三(不拉 busy 的成功也是一次成功)。重复 enter 同一场是 no-op:done 置起、busy 从没拉高。测试台按「先等 busy 起、再等 busy 落」轮询,直接挂死。软件侧同理——不能假设每个命令都会忙,要看 done/状态位。

其四(状态 nibble 的位序算错)。状态是 {mem_ok, ok, err, cur_valid}:新会话是 0b0101=5、恢复会话是 0b1101=0xD。第一版按 0b0011/0x0B 断言,程序在自检里自曝 0xDEAD0084/0xDEAD0088。教训:打包位序要写在纸上再算十六进制。

其五(leave 成功却忘了置 ok)。leave 分支只置了 err,成功路径没人把 ok 拉高——状态读回 ok=0,上层会以为退出失败。补上成功分支。

其六(空会话的 load 失败不是失败)。新槽的表头是全 0,归档 load 会返回 ok=0/err=1。如果把「恢复到空记忆」当错误,第一次开机就崩。enter 只看目录是否给出槽位;mem_ok 单独报告「这场有没有旧上下文」。

⑤ 回到目标(这环让目标前进了多少)

到这一环,Salt OS 的「同一个对话窗口」不仅能分区记忆,还能一句话换人:一条 npu.sess 把「问目录、取基址、存取上下文、记住现在在哪一场」全收进硬件;换场时自动把上一场收好,核一个入口寄存器都不用写(SX_CORE=0)。

对一台手机的意义:用户点开另一个会话,不该是一串系统调用和轮询——它就该是一个动作。会话切换变成一条指令之后,OS 的主循环可以短到「读一句话 → npu.chat → 显示」,中间不再有会话管理的胶水代码。再往上,是开机自动接着上次那场(npu.boot)、跨设备同步、断电持久化。