🎯 圆心 · 目标(每环都从这里出发)
造一台 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 会话清单 · 53 开机用上清单 → 54…

本环:从「列得出名单」到「开机第一句话」

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

第 51 环让一条 npu.boot 把「上一次聊到哪一场」恢复回来;第 52 环让一条 npu.sess.list 把「这儿有哪几场」列出来。可两件事在开机时还是各走各的:boot 恢复完就进对话,清单要另外发一条指令;更关键的是开机第一句话没人负责。

Salt OS 开机没有桌面、没有图标,只有 AI 一个对话窗口。第一帧不该是图标墙,也不该是空白——它应该是一句话:「你有三场没聊完。」这句话里同时有「清单」(三场)和「话头」(没聊完),正好就是这一环要补的那一层。

本环让一条 npu.boot 把三件事串起来:恢复上一场 → 数一遍清单 → 排好第一句问候。问候语由硬件从芯片里的词表排出来,直接落进对话窗口缓冲区;核一个字节都不拼。

② 实现(做了什么)

开机流程接上清单。sess_switch 的 boot 状态机改了三步:恢复 K/V 成功后不再直接收场,而是转到 list 问目录「有几场」;清单回来后再把人数交给问候合成器。首次开机(目录为空)也走同一条路——没有旧场不是错误,照样数清单(0 场)、照样说第一句话。

新增 boot_greet 开机问候合成器。词表在芯片里,每个短语固定 8 个汉字 = 24 字节 = 6 个 32bit 字,定长、可整块落内存:

0 场:今天想聊点什么?
1 场:你有一场没聊完。
2 场:你有两场没聊完。
3 场:你有三场没聊完。
4 场:你有四场没聊完。
5 场:你有五场没聊完。
6+ :你有六场没聊完。   (六个槽就是上限)

落点:会话记忆区 0x1E0 ~ 0x1E5(对话窗口缓冲区首行)
出口:code(0=新的一天,1..6=几场待聊)、bytes=24
      cnt_out / sid_out / restored_out(这一场是不是真从内存恢复的)

一个引擎,三个写主。会话记忆区原来由「目录」和「归档」共用读写口(两者从不同时动作);本环把问候语加进去,优先级 bg > ss > arc。问候只在 boot 流程的 list 之后动作,此时目录、归档都空闲,三者天然互斥。

ISA 一个操作数都没加。npu.boot 仍然无操作数;npu.rd sel=30 在 boot 之后读回的,就是这一次开机里清单的人数(原来只有单独发 npu.sess.list 才有)。新页 0xE00:BG_BASE / BG_RST,读回 BG_CNT / BG_CODE / BG_BYTES / BG_SID / BG_RESTORED / BG_W0..W5 / BG_BUSY / BG_DONE / SCREEN29。

软硬协同的约定:① 开机第一句话是「OS 的第一帧」,不该等模型跑完再生成——短语表进芯片,开机即有;② 清单是这句话的唯一输入,人数直连、不经核;③ 问候语定长 24 字节落内存,OS 不需要猜结尾;④ 核不拼字、不搬字——同一把尺子(SF_CORE / POOL_WCORE / SX_CORE)继续量到这一环。

③ 怎么验证(硬证据)

协同仿真 tb_min_soc53 在第 52 环全部流程跑完后继续:

步骤做什么结果
造三场0x81 / 0x82 / 0x83 各进一场、各 leave 一次(空上下文也落完整表头)目录 used=3,最后提交 0x83
开机一条 npu.boot + npu.waitSX_CUR=0x83、SX_MEMOK=1、npu.rd sel=30=3
问候读 BG_CNT / CODE / BYTES / SID / RESTORED3 / 3 / 24 / 0x83 / 1
逐字读 BG_W0..W5E6A0BDE4 B8E4899C BA9CE589 E8A1B2E6 AEE58A81 8280E38C
上屏ARC_PEEK 0x1E0..0x1E56 个字与 BG_W* 逐位相同(同一行已在窗口缓冲区)
0 场目录重建后再 boot问候 = 今天想聊点什么?(code=0/sid=0/restored=0)
1 场造 0x84、leave、再 boot问候 = 你有一场没聊完。(cnt=1/code=1)
零配置读 SX_CORE全程一位没涨(恢复/清单/问候都没走核写配置)
SCREEN29 = 0x0303895A
三场人数 3 | code 3 | w2 低字节 0x89(「三」的第三字节) | 证据 0x5A

主通路未动:SCREEN=0xF905FD04、SCREEN2=0x02040404、SCREEN23~28 与第 25~52 环逐位一致。单元测试 tb_boot_greet 覆盖 0/1/2/3/4/5/6 场、9 场封顶、换落点、复现、忙时防重入、clear 归零;相邻的 sess_switch / sess_list / min_soc51 / min_soc52 一并复跑通过。

④ 调试中钉住的钉子

其一(npu.sess 的两个操作数是寄存器号,不是立即数)。第一版写 sess(rs1=A1, rs2=1),以为第二个字段就是「1=enter」。核里 npu_sess_cfg = ex_opB——它取的是 rs2 这个寄存器的值。因为 x1 恒为 0,三条 enter 全被当成 cmd=0,目录 engine 置 err、一条也没进去;SX_ENTER/SX_LEAVE 原地不动,used 一直是 0。修法:li a0, sid; li a1, 1; npu.sess a0, a1——先看清操作数从哪来,再写编码。

其二(口播封顶,报告不封顶)。六个槽是上限,第 7 场起问候只能说「六场」,但 cnt_out 要如实报告原始人数(9 场就是 9)。第一版把 code 直接写成 cnt,9 场时口播成了「九场」;改成 code = min(cnt, 6)、cnt_out = cnt——「说给人听的」和「报给系统看的」可以是两个数。

其三(第三个写主必须排在后面动作)。会话记忆区原本只有目录/归档两个写主,靠「从不同时动作」省掉仲裁。问候是第三个写主,如果它在 list 还没写完 0x1C0 时就动手,会把清单表踩掉。修法:boot 流程严格串行(load → list → greet),并给内存口加优先级;「互斥」要写在时序里,不能只写在注释里。

其四(定长才好上屏)。问候语没有用「变长字符串 + 结束符」:变长就要 OS 逐字节找结尾,还得处理半行。8 个汉字、24 字节、6 个字,写满即一行——OS 读 6 个字就能上屏,硬件也只需 6 拍。

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

Salt OS 手机开机只有一个 AI。用户按下电源键,看到的第一个东西不是桌面、不是图标,而是 AI 说的一句话:「你有三场没聊完。」——这一句话背后,芯片刚刚恢复了他上次的上下文、数清了所有会话、把「三」这个数字排进了句子里。

对平台的意义:会话清单不只是给系统看的调试表,它是AI 的开场白。第三方服务接进来以后,AI 可以在开机第一句里就提到「你上次问的那件事」「你还没看完的那份资料」——平台上的每一场对话都能被接住,用户不用自己找回入口。从「能列出每一场」,到「开机就会用这份名单」,冷启动第一次变成了有人等你回来的感觉。