🎯 圆心 · 目标(每环都从这里出发)
造一台 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契约 → ㉘权重内存化 → ㉙…

本环:把层权重从「寄存器」搬进「内存」

① 目标是什么

还是同一个圆心:一台开机只有一个 AI 的手机。上一环把「启动整张网」收成了一条 NPU 指令,可层权重还住在寄存器里:开机时核得把每层 4 张投影矩阵(Wq / Wq2 / Wk / Wv)、FFN 两层、LayerNorm 的 gamma/beta 一个 32bit 一个字地 sw 进 ql_w / net_wei 寄存器堆——权重永远占着片内那块小寄存器,也永远长不大。
真芯片不是这样:权重必须住在内存(几 MB 到几 GB),推理时按层流式取进片上小缓冲,用完就走。这一环把层权重搬进权重 SRAM,让整网引擎每层开跑前自己从内存取——软硬协同里的「内存带宽优先于峰值算力」,第一次落到权重上。

② 怎么实现(不脱圆心)

· 权重有地方住:第 3 圈那块权重 SRAM 从 16 字扩到 64 字(0x100~0x1FF 原地址区间,地址映射一个字没动),按「每层 16 字」排布:0/1=Wq、2/3=Wq2、4/5=Wk、6/7=Wv、8/9=W1、10/11=W2、12=gamma、13=beta,层基址 = 层号 × 16 —— 2 层共 32 字;
· 引擎自己取:layer_net 新增一条权重流式取通路(w_addr / w_rd_en / w_rd_data / w_rd_valid)与两个状态 ST_WLD → ST_WLDR:每层开跑前逐字取 16 个字,落进片内「本层权重」小寄存器,取满一层才 ST_PROJ;跑完下一层再取一遍;
· 只扩不推翻:w_src 选择权重来源——1 = 从 SRAM 流式取(本环新增、主路默认),0 = 前 26 圈的寄存器驻留路径(一个字没删、单测照跑);数据面(整网计算)一个门都没动;
· 顺手修掉一颗旧钉子:SRAM 的写译码原先只挡了 bit9、没挡 bit10 —— 写 0x500 页(输出投影页)的 WO_X0 会静默改掉 SRAM 第 0 字。权重真住进内存后这颗钉子会直接毁模型,本环把 bit10 一并挡掉;
· 两个探针:0x2C8 NET_W_CNT(这趟流式取了多少字)、0x2CC NET_W_FIRST(取到的第一个字)、0x2D0 NET_W_DONE,把「权重真从内存来」钉在寄存器上。

③ 本环做了什么(真代码)

rtl/layer_net.sv 新增端口与 WLOAD 状态(每层从内存流式取 16 字):

// layer_net:每层开跑前从 SRAM 流式取权重
ST_WLD:  begin w_rd_en_r <= 1'b1;
               w_addr_r  <= (li * WS) + wcnt;   // 层基址 + 层内序号
               st        <= ST_WLDR; end
ST_WLDR: if (w_rd_valid) begin                  // 同步读:下一拍数据回来
             case (wcnt)
               0: wq_r[31:0]  <= w_rd_data;    // 0/1=Wq
               1: wq_r[63:32] <= w_rd_data;
               ...                              // 2/3=Wq2 … 12=gamma 13=beta
             endcase
             wtot <= wtot + 1;                  // 探针:累计取字数
             if (wcnt == WS-1) st <= ST_PROJ;   // 一层取满 → 开跑本层
             else              st <= ST_WLD;
         end

rtl/min_bus.sv 扩 SRAM 并把它接到引擎,同时修掉 bit10 写穿透:

sram_cache #(.DEPTH(64), .DATA_W(32), .ADDR_W(6)) weight_sram (
    .wr_en(dmem_wen && dmem_addr[8] && !dmem_addr[9] && !dmem_addr[10]),
    .wr_addr(dmem_addr[7:2]), ... );
// 读端口仲裁:整网引擎的流式取优先,其次第 3 圈的老装载 FSM
assign sram_rd_en   = net_w_rd_en | (ld_state == LD_RD);
assign sram_rd_addr = net_w_rd_en ? net_w_addr : 6'd0;

OS 只多两件事:开机时把每层权重也写进 SRAM(sw 到 0x100 + 层号×64 + 字号×4),然后 sw 1, 0x2C4(NET_W_SRC=1)——此后核不再碰权重寄存器。

④ 怎么算成(闭环验证)

单元仿真 tb_layer_net28(引擎 + 行为级权重 SRAM 模型,4 组用例):寄存器路径 y=0xFA03FC07、探针 w_cnt=0;复位后内存路径 同一个 y=0xFA03FC07(逐位一致)、w_cnt=32(2 层×16 字)、首字 = wmem[0]、w_ld_done=1;把 SRAM 里层 1 的 FFN W2 改成单位阵 → 内存路径变 0xF703F90A、寄存器路径仍 0xFA03FC07。

协同仿真 tb_min_soc28(整机:核 → OS → 解码循环 → 收尾八项试验):

探针 / 断言结果说明
NET_W_CNT32每层 16 字 × 2 层,真从 SRAM 流式取
NET_W_FIRST0x01110F01取到的第一个字 = 层 0 的 Wq 低 32bit
NET_W_SRC=0 时 NET_W_CNT0寄存器路径一个字都不从内存取
内存路径 vs 寄存器路径逐位一致同一份计算,只是权重住在哪
改 SRAM 层1 的 W2 → 内存路径[10,-7,3,-9]内存里的权重真在算
同一改动下的寄存器路径[7,-4,3,-6] 不变两个来源确实不同
SCREEN = 0xF905FD04
与第 25 / 26 / 27 环逐位一致 —— 数据面一个字没动,变的只是层权重住在哪

SCREEN2 = 0x02040404(整网 digest,同第 26/27 环)、SCREEN3 = 0x04000204(ISA 契约 digest,同第 27 环)、SCREEN4 = 0x20010707(权重内存化 digest:流式取 32 字 | 首字低 8bit | 内存路径 lane0 | 寄存器路径 lane0 —— 最后两位相等正是「两条权重来源逐位一致」)。全量回归 41 项(新增 layer_net28 与 min_soc28)全部通过。

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

这一环之后,芯片上的权重第一次住在内存里:核不再需要把整张网的权重一个个写进寄存器堆,开机只需把权重摆到内存、把引擎的权重来源拨到内存,剩下的「取谁、什么时候取、取到哪」全由硬件按层自己做。
对「开机只有一个 AI」的手机来说,这是能装下真模型的前提——真模型的权重是几亿到几十亿个数,寄存器装不下,内存装得下;权重住进内存,片内小缓冲只当「本层工作台」,这条路才通到能跑真 DeepSeek 的那一天。