🎯 圆心 · 目标(每环都从这里出发)
造一台 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 开机续聊 → 52…

本环:开机只有一个 AI,而它记得你们上次说到哪儿

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

第 50 环把「进哪一场会话」收进了一条 npu.sess。可开机是另一回事:OS 只知道用户要接着上次聊,却不知道上次是哪一场——会话号住在目录里,重启后没人记得。软件要么自己扫六个槽位、比时间戳,要么在别处再存一份「最后一场」的副本,两份记录一旦不同就恢复错人。

这一环把「最后一场」交给目录自己记:每次 commit(一场真正存好)时,硬件把这一场的会话号/槽号写进目录表头;开机一条 npu.boot 直接问目录,取回那场并恢复 K/V。首次开机(目录为空或刚重建)不是错误——照常新开一场。

语义关键:boot 恢复的是「最后一次提交的会话」,不是「当前正在用的会话」。仿真里特意让运行中的是 C、最后提交的是 D——boot 必须回到 D。

② 实现(做了什么)

目录表头多记一件事(不搬地址)。表头第 3 字原来只放 8bit「累计开启次数」,现在打包成:

0x188 表头[2] = { last_valid, last_slot[2:0], 4'b0,
                   last_sid[15:0], stamp[7:0] }

低 8 位仍是 stamp(老读回逐位不变),高位记住最后一次 commit 的会话。这样既没有移动目录项地址(0x190 起仍是六个槽的目录项),也没有破坏第 49/50 环的读回格式。

目录新增一条命令 last(cmd=6):只报「上次提交的是谁 / 住哪槽 / 基址多少」,不分配、不改目录;表头坏掉或从没提交过就返回 miss,不置 err。

开关场引擎新增 boot(cmd=4):

boot:
  问目录 last
    ├─ 命中 → 取 sid/slot/base → 清片内环 → 归档 load → 进这场
    └─ 没命中 → ok=1 / mem_ok=0 / cur=0(首次开机)
              → 发一拍 boot_clear 清空片内 K/V 环

ISA:核升 v16,custom-1 加 funct3=101:npu.boot(无操作数)。EX 拍一个脉冲进开关场引擎;会话号、槽位、基址全由硬件自己问、自己取——核既不传号,也不扫目录。

新观察点:
0xD64 SX_BOOT   累计开机续聊次数(MMIO 读)
0xD14 SCREEN27  本环自检 digest
npu.rd 仍用 21~29:24=当前会话号 26=本次摘要 23=状态 21=槽号 22=基址

软硬协同的约定:① 目录是唯一的「会话索引」,OS 不再另存一份「最后一场」;② boot 只读目录、不改目录;③ 首次开机与「恢复失败」严格区分——前者 ok=1/mem_ok=0,后者 err=1;④ 没有旧上下文时硬件自己清环,避免把上一场的 K/V 残影当成新会话的记忆。

③ 怎么验证(硬证据)

协同仿真 tb_min_soc51 在第 50 环全部流程跑完后(当前会话 C、最后提交 D)继续:

步骤做什么结果
find DMMIO 问目录:D 的摘要取到 digestD(≠0)
boot一条 npu.bootcur=0x62(D),不是运行中的 C(0x61)
状态读 npu.rd sel=230x0D = mem_ok1/ok1/err0/cur_valid1
摘要读 npu.rd sel=26== digestD(上下文一字不差)
续聊恢复后跑整网Y_boot == 第 50 环的 Y_D(接着上次的输出)
零配置读 SX_BOOT / SX_CORE1 / 0(核一个入口寄存器都没写)
首次开机目录重建 → 再 bootok=1 / mem_ok=0 / cur=0,不是错误
对照空上下文跑整网Y_fresh ≠ Y_D(恢复的上下文真在影响输出)
SCREEN27 = 0x020062FF
boot 2 次 | 恢复的会话号 0x62 | 八条证据位全 1

主通路未动:SCREEN=0xF905FD04、SCREEN2=0x02040404、SCREEN23~26 与第 25~50 环逐位一致。全量回归通过。

④ 调试中钉住的钉子

其一(miss 不能只清 valid)。首次开机路径只把 cur_valid 置 0,cur_sid 还留着上一场的 0x62。软件读「当前会话号」会以为还在 D。miss 必须把 cur_sid/cur_slot/cur_base 一起清干净——状态是一个整体,不能只改一半。

其二(清目录不等于清片内环)。目录重建后 boot 返回 miss,但片上 K/V 环里还留着 D 的上下文;接着跑整网,输出与 Y_D 一模一样,自检把它当「恢复成功」放过去了。修法:boot miss 发一拍 boot_clear,接到 layer_net.ctx_clear 与 K/V 环的 clear——内存里的目录和片上的状态是两份,重建目录必须同时清环。这也正好暴露了原来「空上下文」对照用例是假通过。

其三(表头字不能整字重写)。stamp 与「最后一场」共用第 3 字:分配新槽时如果直接写 {24'b0, stamp+1},就会把 last 字段抹掉。所有写表头的地方统一按打包格式拼,且读回时低 8 位解成 stamp——共用字要先定好位域,再改所有写口。

其四(cmd 从 2bit 到 3bit)。开关场原来只有 enter/leave/switch 三个命令,cmd 是 [1:0];boot 是第 4 个。扩宽到 [2:0] 时,MMIO 写入、读回、测试台三处都要跟着改,漏一处就把 boot 截成 0(变成 enter)。

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

到这一环,Salt OS 的手机开机真的可以「只有一个 AI」:没有桌面、没有图标,也没有「请选择继续哪场对话」的列表——开机一条 npu.boot,AI 自己回到你们上次说话的地方,接着上一句。用户感知到的不是「恢复会话」,而是这个 AI 一直记得。

对平台的意义:会话是别人服务长上来的挂点。AI 记得上次聊到哪、找过谁、办到哪一步,第三方服务才可能在同一个对话窗口里被接着调用,而不是每次从零开始。这一环把「记忆」从软件的一份副本,变成硬件目录里的一条确定记录——重启、断电、换场都不会认错人。