🎯 圆心 · 目标(每环都从这里出发)
造一台 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 化 · ㊻对话回路 → ㊼…

本环:一段之后,还有下一句

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

第 45 环把「一整段生成」收进了一条 npu.generate:一个 prompt 进去,硬件自己跑到停止符,回核一串 token。可手机不是「一问一答就结束」:窗口一直开着,用户说完一句还有下一句,AI 答完一段还要接着答下一段。

在第 45 环里,「一段之后怎么办」还在软件里:核要再发一条 generate、再喂一个输入、再把上下文接上——「接着下一句」没有硬件契约。本环把一场对话(多个回合)也收进一个引擎:一个 CONV_GO,硬件自己跑

回合 0:开新窗口(清空上下文)→ 跑一整段
      → 记下这一回合的 token / 步数 / 末 token
回合 1:输入 = 上一回合的末 token(接话)或 OS 排好的下一句
      → 上一回合的 K/V 还留在环里(同一场对话记得住)
      → …… 直到 N 个回合走完

核从「一段接一段地发」退成「发一场对话、等一场、读整份记录」。

② 实现(做了什么)

新增 rtl/conv_loop.sv(四态:IDLE → TURN → GO → WAIT):

  • 回合边界:ST_TURN 决定这一回合的输入,并在需要时发 kv_clear;ST_GO 给整段引擎一发 gen_start;ST_WAIT 等这一段的 done 收口。
  • 输入规则:回合 0 用 OS 给的 prompts[1:0];之后 mode_carry=1 用上一回合末 token(硬件接话),否则用队列里的第 k 句 prompts[2k+1:2k]。核读回的 CONV_PIN 就是硬件真正用的那个输入。
  • 会话记忆:mode_ctx=1 只在回合 0 清一次上下文,回合之间 K/V 留在环里;mode_ctx=0 每回合开头都清(对照组:每句都当新对话)。
  • 会话记录:每个回合的 token 序列(turn_seq,每步 2bit)、步数(4bit)、末 token(2bit)、整场 transcript(最近 16 个 token)、回合数、总 token 数、清上下文次数,全部在片上。

三处配套改动(都是「扩」不是「改」):

rtl/kv_ring.sv   加 clear 单拍口:两层 K/V 的条数/头指针/累计量全归零(会话真空)。
                 已有设计一律接 1'b0,前 45 圈行为逐位不变。
rtl/layer_net.sv 加 ctx_clear 入、ktot_out 出:主路的 K/V 环其实长在整网引擎里,
                 会话边界要清的是它;累计 K 条数也由它报给核。
rtl/min_bus.sv   新页 0x900~0x948(bit11+bit8,前 45 圈没用过):
                 CONV_PROMPTS / NTURNS / MODE / LAYERS / GO / BUSY /
                 TURNS / TOKENS / SEQ / TLAST / TN / CTXCLR / KVTOT /
                 TSEQ01 / TSEQ23 / PIN / SCREEN21 / SCREEN22。
                 npu.wait 的忙闲并入 conv_busy|conv_go_pulse(一场对话也算 NPU 忙)。

OS 主循环里跑三场,全走「写配置 → 一条 CONV_GO → 轮询 CONV_BUSY → 读整份记录」:

会话配置要证明什么
A2 回合 · ctx=1 · 队列 [0,1]一场两回合;上下文不被清,K/V 累计 == 全部 token
B2 回合 · ctx=0 · 队列 [0,1]每轮清上下文 → 清 2 次、K/V 只剩末回合
C2 回合 · ctx=1 · carry=1回合 1 的输入 = 回合 0 的末 token(3),核从没写过 3
③ 怎么验证(硬证据)

所有既有观察点在那之前已经跑完,SCREEN~SCREEN20 一个没动;对话试验插在全部收尾之后,只新增 SCREEN21/22。

证据结果说明
一场几回合turns = 2一条 CONV_GO 里硬件自己接了第二段
总 token 数7(A)/ 8(C)== 两回合步数之和(硬件自报,核不算)
A 清上下文次数1只在会话开头清,回合之间不清
A 的 K/V 累计7 == 总 token两个回合的 7 条 K/V 都进过环(同一场对话记得住)
B 清上下文次数2每回合开头都清(对照)
B 的 K/V 累计3 == 末回合步数前半段上下文真的被清掉了
A 末回合输入CONV_PIN = 1队列里的第 2 句(硬件自报的「真正用的输入」)
C 末回合输入CONV_PIN = 3回合 0 的末 token;核没写过 3 ——「接着上一句」在硬件里
同一句输入的答案A ≠ B输入都是 1,A 带上下文、B 上下文被清 → 答案不同
接话 vs 队列C ≠ A输入 3 与输入 1 是两句不同的话
主通路SCREEN = 0xF905FD04与第 25~45 圈逐位一致;SCREEN2~20 一个没漂
SCREEN21 = 0x005B000B | SCREEN22 = 0x12470E0F
回合0 序列 0x000B | 回合1 序列 0x005B | 清上下文 1/2 | 回合 2 | token 7 | K/V 7/3 | 答案不同 + 接话 3

单元仿真 tb_conv_loop:用一个「每段固定 3 步」的段引擎替身,钉死六件事——① 两回合队列输入:回合记录、总 token、transcript 拼接全对;② 接话模式:第 2 回合的输入变成第 1 回合末 token(3),输出跟着变;③ ctx=1 一场只清 1 次、ctx=0 清 2 次;④ n_turns=0 当作 1 回合不空转;⑤ 忙时再 start 不重入(一场仍只有 N 段);⑥ clear 归零。

协同仿真 tb_min_soc46:三场会话全跑通,SCREEN21=0x005B000B、SCREEN22=0x12470E0F,SCREEN~SCREEN20 与第 25~45 圈逐位一致。全量回归见文末。

④ 调试中钉住的钉子

其一(清错了环)。第一版把 ctx_clear 只接到 min_bus 的 KV 环、也读那个环的累计数——结果会话的 K/V 累计恒为 0,但生成照样出 token。原因是:第 24 环之后主路的 K/V 环长在 layer_net 里面(每层一份),min_bus 那只环是早期 OS 级注意力试验留下的。会话边界要清的是主路那只环。教训:同一功能在演进中出现过两只环,接线前先确认「主路现在读哪一只」。

其二(打包读数没拆字节)。CONV_KVTOT 是「层0 低字节 | 层1 高字节」,一次读回是 0x0707;第一版直接和 7 比,判失败。加 andi 0xFF 只看层 0 才对。教训:探针是打包值时,读回先拆字段再比。

其三(引擎还没起来,轮询已经结束)。CONV_GO 写完的下一条 lw CONV_BUSY 可能比 conv 引擎看到 start 还早——读到 busy=0,轮询一圈就退出,接着读到 turns=0,自曝在「回合数」。GO 之后补 4 个 nop 再进轮询即稳。教训:「发令 → 轮询」之间必须留出引擎起忙的窗口,与第 36 环 npu.wait 的那道缝是同一颗钉子。

其四(x31 的读回/比较异常)。把 CONV_PIN 读进 t6(x31)再比较,恒不中;换 s9(x25)立即正确,其它逻辑一字未改。这颗核在 x31 的装载/分支路径上还有一颗没钉死的钉子,记下来留给后续环查(现有程序一律避开 x31 做检查)。

其五(首段的「热身」现象,留作下一环的输入)。同一份代码、同一颗采样种子,ctx=1 会话的第一段与 ctx=0 会话的第一段并不逐位相同(回合数、K/V 数都对,但 token 序列不同);连跑两场 ctx=1 又完全复现。说明会话起步的第一段仍受「上一场运行历史」影响,尚未定位到具体寄存器。本环的证据因此以结构量为主(回合记录 / K-V 累计 / 接话输入 / 清上下文次数 / 核搬运),不把「首段逐位相同」当作前提——这颗钉子明确留给下一环。

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

到这一环,软件看一场对话只剩一句话:「这几句,接着聊」。回合的切分、上下文的去留、下一句的输入从哪来、每回合说了什么——全在片内。OS 主循环从「一段接一段」退成「一场接一场」。

对一台手机的意义:对话窗口的语义第一次落到硬件契约上——窗口不关,上下文就在;开新会话,上下文就真空。mode_ctx 那两个模式正好对应产品里的两件事:同一场对话记得住(有记忆),换一个话题/新窗口从零开始(无记忆)。再往上,就是「为别人搭台」时每个服务都能拿到同一份会话上下文的基础。