🎯 圆心 · 目标(每环都从这里出发)
造一台 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 化 → ㊲描述符链 → ㊳链槽直喂 → ㊴…

本环:扫描收进了指令,搬运呢?

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

上一环把「一整层权重」收成一条 npu.rbchain:内存里排一串矩阵描述符,硬件一条条走完,整层的结论同时留在片上四个结果槽里。可槽还在扫描引擎里、注意力子层在另一头——核得把四个槽一个个 lw 读出来、再一个个 sw 写进注意力子层的入口寄存器(MHA_Q0/MHA_Q1、K/V 追加口)。

也就是说:「扫描」收进了指令,「搬运」还留在软件手里。硬件已经算完、就在片上,却要绕一圈经核的手再送回去。这一环把这条搬运也收进硬件——链扫完即用,核不搬运。

② 实现(做了什么)

新增 rtl/slot_feed.sv 链槽直喂引擎:输入是 row_blk 的结果槽总线(512bit = 4 槽 × 4 lane × 32bit,片上直连,不经核),按角色映射把每个槽分流到它该去的入口,并做饱和量化(8bit → [-128,127]、4bit → [-8,7]):

slot0/1/2/3 ──(每槽 2bit 角色)──▶  0=头0 Q   1=头1 Q2   2=K 追加口   3=V 追加口
                 sat8 / sat4 后直连;喂完那一拍顺手把 K/V 追加进本层环

ISA 契约(核升 v10):npu.sfeed rs1(funct3=111)——一条指令:锁住「入口切到链槽」+ 把四个槽喂出去。核一个字节不搬:不读一个槽、不写一个入口。硬件的探针还会自报:本次喂了几种角色(SF_NFEED)、角色映射表(SF_ROLE)、每个入口由哪一槽喂来(SF_SRC)——这些字段核从没写过,对得上就说明布线真是映射驱动的。

同时加了一个「核搬运计数」SF_CORE:核每读一个槽 lane、每写一个注意力入口,计数 +1。直喂路径应为 0,老路径(核自己搬)应为一个正数——这就是「核不搬运」可量化的硬证据。新页 0x7B0~0x7DC,前 37 圈地址映射一字不改。

③ 怎么验证(硬证据)
证据结果说明
直喂路径(npu.sfeed,核零搬运)多头头0 best=24、头1 best=0xFFF4多头在直喂进来的 Q/K/V 上真跑出了结果
SF_CORE(核搬运计数)直喂 0一个槽都没读、一个入口寄存器都没写
SF_NFEED / SF_ROLE / SF_SRC4 / 0x4E / 0x4EQ←槽2、Q2←槽3、K←槽0、V←槽1,全由硬件自报
手动搬运路径(核自己读槽、自己写入口)SF_CORE = 2016 个槽 lane + 四个入口:搬运真的落在核手里
两条路径的多头结果逐位一致结果一样,差别只在「谁搬」——0 vs 20
换小激活重扫同一条链四个槽 = [[2,0,2,2],[1,1,1,1],[4,4,4,4],[-2,0,-2,-2]]槽值全落在 INT4 范围内,两条路径可逐位对表
SCREEN13 = 0x00041401
直喂核搬运 0 | SF_NFEED 4 | 手动核搬运 0x14(20) | 头0 输出 lane0 1

单元仿真 tb_slot_feed:角色映射(默认 0xE4 与换表 0x1B,Q/V 真的换槽)、来源探针逐位、nfeed 在「两槽同角色」时只算一次(4 → 3)、两端饱和(200 → 127、-300 → -128;4bit → 7 / -8)、同配置复现逐位一致、全零槽输出全零。

ISA 单元仿真 tb_isa_sfeed 只验契约本身:四块矩阵与段表写进 SRAM,一条 npu.rbchain 扫完 → 一条 npu.sfeed → SF_Q4/SF_Q24/SF_K4/SF_V4 对表全中、SF_CORE=0、换映射表 Q 与 V 互换、MMIO 老路径(SF_MODE+SF_GO)与指令路径逐位一致;全通 SCREEN = 0x0000E5E5。

协同仿真 tb_min_soc38:SCREEN = 0xF905FD04 与第 25~37 环逐位一致(本环只加一条直喂通路,计算面一个字没动);采样序列仍是 [0,2,0,0,0,3]。全量回归 63 项全绿。

④ 调试中钉住的钉子

其一:脉冲寄存器要在「每拍默认」里拉低。新加的直喂脉冲我只在复位分支里清了,没在每拍默认里清——脉冲一直挂高,忙闲永远不为空,npu.wait 把流水线冻在那里,仿真跑到超时。链启动脉冲当年也是这样写的,照抄容易漏。

其二:计数要在「每拍」那块里数读、不能在复位块里数。「核搬运计数」第一版被放进了复位分支,于是永远是 0——看上去还挺像「核零搬运」,其实是个假绿。挪进每拍默认块之后,手动路径才如实报出 20。假绿比红更危险。

其三:两条来路不能重复触发。start 一开始写成「脉冲 | 指令执行拍」,同一个 npu.sfeed 会喂两次(K/V 也多追加一条)。统一成只认那一个脉冲寄存器,指令和 MMIO 两条来路都往它写脉冲。

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

从「一条指令扫完一整层权重」走到「一条指令扫完、并且直接接进注意力子层」:链扫出来的四个槽不再绕经核的手,片上直连进多头块与 K/V 环。核在这一步上从「整层搬运的声明者」再退一步——连搬都不用声明了,只写一条指令,剩下的交给布线。

这正是软硬协同最实在的一层:凡是数据本来就在片上、而且去处是固定的,就不该让它回核里绕一圈。回头做整网时,一层的权重会从「核把每个矩阵搬进寄存器」变成「一条链扫完、槽直接喂下去」——核退回成那个只说话不搬东西的人。