第 65 环 · 删除可撤回(删错还能救回来)
第 55 环把「这场不用留了」收进一条 npu.forget:目录项作废、槽首字清零、名字格清零、清单重排、当前指针清掉——删得干净利落。
可删错了呢?软件手里一个字都没留。用户只能说「我刚才删错了」,救不回来。一个只有一个对话窗口的手机,删除是危险动作,必须给用户一条后悔路。
这一环让删除之前先留一份现场,再给一条 npu.undo:删错还能救回来。
删除先留快照。第 55 环的删除引擎在 free 之前多走一小段:读该槽目录项的基址/摘要/w3、槽首字、名字 6 字,按固定 13 字写进垃圾记录 0x220:
0x220 魔术字 "UNDO"(0x554E444F)
0x221 {8'b0, 槽号[2:0], 5'b0, 会话号[15:0]}
0x222 原基址(槽号 × 64)
0x223 原摘要
0x224 原 w3(字数 / kv 计数)
0x225 字数
0x226~0x22B 名字 6 字(24 字节)
0x22C 槽首字(free 会清掉的那一个字)
撤回走目录自己的语义。新增 rtl/undo_del.sv:一条 npu.undo 读垃圾记录 → 向槽位目录发 restore(cmd=8,目录把 valid/sid/基址/摘要/w3 原样写回那个槽、used + 1)→ 名字格 6 字写回(0x200 + 槽号×6)→ 槽首字写回 → 清垃圾记录魔术字 → 再发 list(cmd=7)重排清单。
只撤一次。魔术字清掉后同一场不能再撤;下一场删除会覆盖垃圾记录。这样「后来住进同一槽的新会话」不会被误认成被删的那一场。
软硬接口。ISA 新增 npu.undo(custom-2,funct3=011,无操作数);垃圾记录落点 0x220(名字格 0x200 之后、命名清单 0x240 之前);目录新增 cmd=8 restore。删除语义(free 只作废目录项、清槽首字)一个字没改。
单元仿真 tb_undo_del(配真 forget_sess + 真 sess_slots):
used 3→2、清单 3→2;垃圾记录 13 字逐位对上(魔术字 / 槽号 / 基址 0x080 / 摘要 / w3 / 16 字 / 名字 / 0xDEAD0092);w0/w1/w2/w3 与删除前逐位一致、used 2→3、名字格 6 字写回、槽首字写回、清单 2→3 且 0x92 回到第 2 条;ok=0 / err=1,目录/清单一字不动(同一场只能撤一次);ISA 协同仿真 tb_isa_undo:程序只有四条——npu.undo / npu.wait / addi t0,0x65 / sw t0,SCREEN。测试台在复位前把「上一次删除留下的现场」摆进会话记忆区,一条指令跑完:ok=1 / sid=0x92 / slot=2 / cnt=3,目录项、名字格、槽首字、清单逐位复原,垃圾记录清掉,npu.wait 没冻死,程序继续走到 SCREEN=0x65。
主通路未动:tb_min_soc64 复跑 RESULT=PASS,SCREEN=0xF905FD04、SCREEN2~40 与第 25~64 环逐位一致。旧的 tb_forget_sess 与 tb_sess_slots 也复跑通过(新增快照写不碰它们检查的任何地址)。
只有一个对话窗口的手机,用户和 AI 的关系是「说话就把事办了」。可越是这样,越需要一条后悔路:说「删掉第二场」,AI 回「好,跑步计划删掉了。」;如果说错了,一句「撤回」,那一场连同名字、上下文、清单位置一起回来。
这一环把「后悔」也做进了接口契约:删除时芯片自己留现场,撤回时芯片自己走目录语义,OS 不需要在应用层偷偷备份一份会话。软硬协同的分工仍然是——目录说什么是活的,芯片负责把现场收好、原样还回去。
撤回回执。这一环能救回来了,可救回来时一句话都没说——用户只看到清单里多了一场,不知道救回的是哪一场。下一环让撤回也念名字:「好,跑步计划回来了。」