🎯 圆心 · 目标(每环都从这里出发)
造一台 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 → ㉓多头块入残差流 → ㉔多头块进解码循环 → ㉕整层一块 → ㉖…

本环:把「一层」收进一个引擎

① 目标是什么

还是同一个圆心:一台开机只有一个 AI 的手机,它得一层一层地看上下文。上一环生成主路的注意力子层已经是多头块了(多头 → W_o → 残差 → LN),可一整层还没「长成一块」:注意力块算完,要由核一趟趟把结果读出来、写进 FFN 引擎、再读出来、再写进残差 LN 引擎……一层里核要串 4 次引擎调用(块 LN → FFN → 残差 LN → 残差流回写),层内的中间量全靠核当搬运线。
这一环把整层收进一个引擎:一次 LB_GO,片内自己把「注意力块 → FFN 块 → 残差连接 + LayerNorm」串成一条通路跑完。

② 怎么实现(不脱圆心)

继续「端上紧凑、全定点、每步硬件自洽、只扩不推翻」的主线:
· 新增 rtl/layer_block.sv——一层 = ① 注意力块(复用第 23 环 mh_block:多头 8 lane → W_o → 残差 → LN₁)② FFN 块(复用第 11 环 ffn:LN₁ 低 8bit/lane 直喂 → W1 → ReLU → W2)③ 残差连接 + LayerNorm(复用第 12 环 resid_ln:s = sat8(FFN + LN₁) → LN₂)。中间量片内直连,根本不出片、不经过核;
· 残差流也在片内自流——层跑完那一刻(lb_done),硬件顺手把本层输出写回残差流寄存器堆 XSR 并推进层号。于是「一次调用既跑完整层、又把残差流推一层」,核不必再写 LST_ADV;
· 前 23 圈的引擎一个不删——mh_block / ffn / resid_ln 都还在、单元仿真照跑,老分步链与整层一块逐位一致;
· 只扩不推翻——整层引擎的寄存器开在输出投影页尾部的未占 slot(0x5A8~0x5FC),前 24 圈地址映射一个字不动。

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

新增 rtl/layer_block.sv,rtl/min_bus.sv 挂载并把整层引擎接到 bit10 页内的 0x5A8~0x5FC,重写 circle25/gen_boot.py 的 layer_body:

// layer_block:一条通路串到底(注意力块 → FFN → 残差 LN)
wire mb_start_w  = (st == S_IDLE) && start;      // ① 注意力块起跑
wire ffn_start_w = (st == S_ATTN) && mb_done;    // LN₁ 有效那一拍直接喂 FFN
wire rsn_start_w = (st == S_FFN)  && ffn_done;   // FFN 输出有效那一拍进残差 LN
// LN₁ 与 FFN 输出都片内直连,核一个字不搬
assign ln_bytes[g*8 +: 8] = mb_ln[g*32 +: 8];            // LN₁ → W1
assign fy_bytes[g*8 +: 8] = ffn_y_w[g*32 +: 8];          // FFN → 残差 a

# OS 主路:一层 = 一声 LB_GO + 一次 LB_BUSY
sw   s4, 0x560(x0)    # MB_X = 本层输入残差流(整层的子层输入)
sw   t1, 0x5B8(x0)    # LB_GO:一次调用跑完 注意力块→FFN→残差→LN
poll LB_BUSY
lw   s0, 0x5E0(x0)    # LB_Y0 = 整层输出(四个阶段一次拿齐)
...
# 硬件残差流写回自检:LB 跑完那一刻 XSR 必须就是本层输出
lw   ... 0x0E4(x0)    # XSR lane0
bne  s0, s11, L_bad   # XSR != 整层输出 → 自曝 0xDEADBEEF

整层一块页:0x5A8/0x5AC/0x5B0/0x5B4 FFN 两层权重、0x5B8 LB_GO、0x5BC LB_BUSY、0x5C0~0x5CC LB_A0..A3(注意力块 LN₁)、0x5D0~0x5DC LB_F0..F3(FFN 输出)、0x5E0~0x5EC LB_Y0..Y3(整层输出)、0x5F0~0x5FC LB_S0..S3(注意力块残差和)。

④ 怎么算成(闭环验证)
整层一块 · RESULT=PASS
一次 LB_GO:s=[1,2,13,-1] / LN₁=[-2,-1,7,-3] / FFN=[7,7,7,7] / 整层=[-3,-2,7,-4] · 与老分步链逐位一致 · 屏幕 0xF905FD04 · SCREEN2 = 0x01FE07FD · 单元仿真 + 协同仿真 + 全量回归 35 项全部通过

这一环要证明「整层真的长成一块了」,现场四条硬证据:
· 证据一:一次调用四个阶段全对表——同一份 8 lane、同一份子层输入残差流,一次 LB_GO 拿到 注意力块残差和 [1,2,13,-1]、LN₁ [-2,-1,7,-3]、FFN 输出 [7,7,7,7]、整层输出 [-3,-2,7,-4],逐 lane 断言;
· 证据二:与老分步链逐位一致——OS 现场再跑一遍第 24 圈核手工串的那条路(MB_GO → FFN_START → RSN_START 三次单独调用),LN₁ / FFN 输出 / 整层输出全部逐位相同。结构换了、计算没变(递增不推翻);
· 证据三:残差流写回是硬件干的——每层 LB_GO 之后,残差流 XSR 必须等于整层输出(核一个字没搬);对不上就自曝 0xDEADBEEF;
· 证据四:FFN 真在整层通路上——只换 FFN 第 2 层权重(单位阵),FFN 输出从 [7,7,7,7] 变成 [0,0,7,0]、整层输出跟着变,而 LN₁ 一个字不动(前向通路没有被回改);
· 屏幕值:SCREEN = 0xF905FD04(与第 24 环同值 —— 同一份计算,只是从「核串 4 次调用」换成「整层一个引擎」);SCREEN2 = 0x01FE07FD = 一次调用里 s / LN₁ / FFN / 整层输出 各取 lane0 一个字节的 digest;
· 单元仿真新增 tb_layer_block:独立跑一遍老分步链(mh_block → ffn → resid_ln)与整层一块对表,另有「残差置零 → LN₁ 跟着变」「shift=2 重量化在块内生效」「换 FFN 权重输出变、LN₁ 不动」「同配置复现」五组用例。

⑤ 顺手记下的一课

这一环最容易被忽略、也最容易做错的地方是「一致性」不是「新旧都跑一遍就算数」:新引擎(整层一块)必须在同一份输入下与老路径逐位一致,否则「只扩不推翻」只是一句口号。所以单元仿真和 OS 收尾都专门现场跑老分步链来对表——引擎留着不只是为了兼容,更是为了当参照物。

⑥ 回到目标

本环走完一整圈:目标 → 一台只有一个对话窗口的手机,它得一层层看上下文,而每一层都应该是一个「块」 → 硬件把注意力块、FFN 块、残差归一化串成一个引擎,层内中间量片内直连、残差流片内自流 → OS 主路一层只发一声、只等一次,并用「四阶段对表 + 与老分步链逐位一致 + XSR 自检 + 换 FFN 权重输出变」四条自检钉死 → 单元仿真 + 协同仿真 + 回归 35 项全绿 → 回到目标。芯片的一层 Transformer,从「核手工串的一串调用」正式长成「一块」。下一环让层控制器驱动这块引擎一口气跑完 N 层——整网一块。