第 51 环 · 开机续聊(一条 npu.boot 恢复上次会话)
第 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 D | MMIO 问目录:D 的摘要 | 取到 digestD(≠0) |
| boot | 一条 npu.boot | cur=0x62(D),不是运行中的 C(0x61) |
| 状态 | 读 npu.rd sel=23 | 0x0D = mem_ok1/ok1/err0/cur_valid1 |
| 摘要 | 读 npu.rd sel=26 | == digestD(上下文一字不差) |
| 续聊 | 恢复后跑整网 | Y_boot == 第 50 环的 Y_D(接着上次的输出) |
| 零配置 | 读 SX_BOOT / SX_CORE | 1 / 0(核一个入口寄存器都没写) |
| 首次开机 | 目录重建 → 再 boot | ok=1 / mem_ok=0 / cur=0,不是错误 |
| 对照 | 空上下文跑整网 | Y_fresh ≠ Y_D(恢复的上下文真在影响输出) |
主通路未动: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 记得上次聊到哪、找过谁、办到哪一步,第三方服务才可能在同一个对话窗口里被接着调用,而不是每次从零开始。这一环把「记忆」从软件的一份副本,变成硬件目录里的一条确定记录——重启、断电、换场都不会认错人。
现在只会续最后一场,可用户可能同时开着「工作」「家里」「查资料」。下一环给目录加一条 npu.sess.list:一条指令扫出所有还活着的会话(号/槽/摘要/字数),让 AI 能说「你有三场没聊完」——从记得上一场,到记得每一场。