🎯 圆心 · 目标(每环都从这里出发)
造一台 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 化 · ㊽会话记忆归档 → ㊾…

本环:把「上一场」从片上的一片缓存,变成下一次还认得你的记忆

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

第 46 环让一场对话在片内跑完,第 47 环把整场对话收进一条 npu.chat。可这记忆只活在 K/V 环里:会话一结束、环被写满、或者电源一断,上一场说过的话就没了。

手机不是每次打开都从陌生人开始。用户关掉窗口、过一会儿再打开,AI 该记得刚才聊到哪儿。本环给上下文加一条落内存 / 回片内的通路:

  • 归档(save):把每层 K/V 环里的内容按逻辑时间序写进内存,前面写一张 8 字小表头;
  • 恢复(load):先验表头(魔术字/条数),清空环,再把每条 K/V 按同一顺序追加回环;
  • 恢复后逐条比对:环读回 vs 内存,一条不差 + 摘要一致,才算 ok=1。

于是 OS 只需要说「把上一场接上」,芯片自己把记忆装回来——这就是「下次接着聊」。

② 实现(做了什么)

新增 rtl/kv_archive.sv(会话记忆归档引擎),并把它接进主路:

表头 8 字(base+0..7):
  [0] 魔术字 "SALT" = 0x53414C54
  [1] 版本/层数/环深
  [2] K 条数(层1|层0)   [3] V 条数(层1|层0)
  [4] 载荷摘要             [5] 会话号
  [6] 载荷字数             [7] 结束标记 0xA5A5C3C3
载荷(base+8 起):层0 K → 层0 V → 层1 K → 层1 V,按逻辑时间序
  • 读口借用:归档只在整网空闲时工作,借用 K/V 环现有的读口(组合选择、不新增第三个消费者);追加口与主路 append 合流。
  • 恢复先清环:arc_clear 在 load 的第一拍清空 K/V —— 追加进一个非空的环只会留下最后 DEPTH 条,记忆会错位。
  • 两道完整性尺子:逐条比对(mismatch)+ 表头摘要(digest)。内存被改过一个字时,环与内存仍然「自洽」,只有摘要会抖。
  • 对话引擎交接:conv_loop 新增 mode_mem——回合 0 不清上下文,而是等归档引擎恢复;整场结束自动归档。同一场里回合之间仍由 mode_ctx 管。
  • 会话区:min_bus 内 512 字 × 32bit 的会话记忆区(真芯片上就是 DRAM/Flash 的一个会话快照区),新页 0xA00。
0xA00 ARC_CMD     0xA14 ARC_WORDS    0xA2C ARC_PEEK_DATA   0xA44 SCREEN24
0xA04 ARC_GO      0xA18 ARC_DIGEST   0xA30 ARC_ERR         0xA48 ARC_KVCNT
0xA08 ARC_BUSY    0xA1C ARC_MISMATCH 0xA34 ARC_NENT
0xA0C ARC_DONE    0xA20 ARC_MAGIC    0xA38 ARC_HDRSESS
0xA10 ARC_OK      0xA24 ARC_SESSION  0xA3C ARC_AUTO
③ 怎么验证(硬证据)

协同仿真 tb_min_soc48 在既有全部观察点之后跑四段:

步骤做什么结果
A两回合 [0,1],ctx=1 + mem=1,结束后自动归档ok=1 / 16 字 / 魔术字 SALT / 会话号 0x0048
Ref用 A 留下的上下文跑一趟整网(不采样)Y_ref = 0x05FF04F9
B一回合 prompt=2,ctx=0 + mem=0(清空上下文)Y_other = 0x0800FAFC ≠ Y_ref
L一条 ARC_CMD=2 + ARC_GO 从内存恢复ok=1 / mismatch=0 / 每层 4 条(ARC_KVCNT=0x2424)
Res恢复后同一趟整网Y_res = 0x05FF04F9 == Y_ref
C恢复后再开一场一回合(mem=1)CONV_CTXCLR=0:回合 0 走恢复、没清上下文
SCREEN24 = 0x10000F04
载荷 16 字 | mismatch 0 | ok / 恢复==参考 / 换上下文就变 / 魔术字 四位全 1 | 每层 K 4 条

单元仿真 tb_kv_archive:归档⇄恢复 12 条逐位一致;改坏一个载荷字 → mismatch=1、ok=0;空会话 0 字仍 ok;魔术字被抹 → err=1 且不污染环;换环深/两层独立都覆盖。

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

④ 调试中钉住的钉子

其一(读使能写成寄存器,读回的是上一轮残留)。内存与 K/V 环都在 posedge 采样:mem_ren 若在 S_xRD 用非阻塞置高,真正采样要到再下一拍,而 FSM 下一拍就去收数据——魔术字读回恒 0、表头全是残留。改成组合驱动读地址/读使能,S_xRD → S_xCP 才对齐。教训:「谁在哪一拍采样」要写下来,寄存一拍就是错一拍。

其二(归档只写了读口,没写存口)。save 完成时 ARC_MAGIC 探针读到 0——因为 save 路径从不填 hdr[] 数组,只有 load 才填。归档完成时把刚写进内存的魔术字/会话号同时报给探针,OS 才不用自己去窥视内存。

其三(恢复前必须清环)。第一版直接往现有环里追加 16 条:环深只有 4,追加完只剩最后 4 条(全是层 1 的 V),记忆完全错位。arc_clear 在 load 第一拍清空后才对。

其四(摘要不能省)。改坏内存里的一个字后,恢复出来的环与内存仍然「自洽」——逐条比对 0 条不一致、ok 却是假绿。只有把表头里的摘要与重算的摘要比一次,才抓得住。教训:逐条比对证明「搬得对」,摘要证明「搬的是原来那份」。

其五(s4 是收尾的最终残差流)。归档自检借了 s4 存 ARC_OK,把收尾要写屏幕的 0xF905FD04 覆盖成 1,SCREEN 变成 0x00000001。改用 t6 后恢复。另:mv s3, a0 后必须隔两拍再做分支(第 45 环已记,这次又踩了一次)。

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

到这一环,Salt OS 的「同一个对话窗口」有了场外记忆:窗口关掉再打开,AI 记得上一场说到哪儿;恢复出来的上下文与「一直没关」算出来的整网输出逐位相同——不是「有点像」,是同一份。

对一台手机的意义:记忆是关系的前提。没有记忆的 AI 每次都是陌生人,只能当工具;有记忆的 AI 才能「接着聊」,才谈得上总调度、谈得上为别人搭台时每个服务都接着同一份上下文。再往上,才是多会话、多用户、断电持久化。