第 53 环 · 开机用上清单(AI 说「你有三场没聊完」)
第 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.wait | SX_CUR=0x83、SX_MEMOK=1、npu.rd sel=30=3 |
| 问候 | 读 BG_CNT / CODE / BYTES / SID / RESTORED | 3 / 3 / 24 / 0x83 / 1 |
| 逐字 | 读 BG_W0..W5 | E6A0BDE4 B8E4899C BA9CE589 E8A1B2E6 AEE58A81 8280E38C |
| 上屏 | ARC_PEEK 0x1E0..0x1E5 | 6 个字与 BG_W* 逐位相同(同一行已在窗口缓冲区) |
| 0 场 | 目录重建后再 boot | 问候 = 今天想聊点什么?(code=0/sid=0/restored=0) |
| 1 场 | 造 0x84、leave、再 boot | 问候 = 你有一场没聊完。(cnt=1/code=1) |
| 零配置 | 读 SX_CORE | 全程一位没涨(恢复/清单/问候都没走核写配置) |
主通路未动: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 可以在开机第一句里就提到「你上次问的那件事」「你还没看完的那份资料」——平台上的每一场对话都能被接住,用户不用自己找回入口。从「能列出每一场」,到「开机就会用这份名单」,冷启动第一次变成了有人等你回来的感觉。
开机已经能说「你有三场没聊完」,可用户回一句「先聊第二场」时,AI 还没有把这句话变成一次切场。下一环把问候接上回答:清单里的一场 → 一条 npu.sess 切过去,开口就能接着聊;从「报出名单」,到「点一句就进那场」。