第 32 环 · 行块自增(一次启动扫完整个矩阵)
还是同一个圆心:一台开机只有一个 AI 的手机。上一环把块号做成「核可写的寄存器」,但跑法仍是软件驱动——4 块矩阵,核要写 4 次块号、发 4 次 RB_GO,还得自己记住下一个块号是几。
真 NPU 的一块权重有几百上千个行块,每块一次 MMIO = 核(软件)成了矩阵扫描的瓶颈。真做法是硬件自己数:给一次启动,引擎把块号自增着扫完整个矩阵,累加器一路长到最后一块,扫完才报 done。这一环把「核喂每块」升级成「核发一声、硬件扫完」。
· 自增开关:新增寄存器 0x3B4 RB_AUTO——写 1 = 块号由硬件自增;
· 一次启动扫完:auto_mode=1 时一条 RB_GO 即可,硬件把块号从 0 自增到 NB-1,每块照旧取权重 → 算部分积 → 累加进跨块 acc,中间不再要核发 start;最后一块算完才 busy=0 / done=1(软件只见「一次启动、一次完成」);
· 块计数探针:新增 blkcnt_out / 0x3B8 RB_BLKC = 自上次 RB_CLR 以来硬件真正处理过的块数(自增是否真扫完的硬证据);
· 只扩不推翻:auto_mode=0 = 第 31 环老语义(核写块号、核发每块 start),一个字没删,两条路径必须逐位一致;第 31 环的 RB_CLR 现在把块计数一并归零;
· 探针:0x3A0 RB_BLKO(自增停在的块号)、0x3B8 RB_BLKC(扫过块数)、0x3BC SCREEN7。前 31 圈地址映射一字不改。
改 rtl/row_blk.sv(新增 auto_mode 输入 + blkcnt_out 探针 + ST_COMP 里的自增续跑),新增 tb/tb_row_blk32.sv(单元仿真);min_bus 挂 0x3B4 RB_AUTO / 0x3B8 RB_BLKC / 0x3BC SCREEN7 并新增第七观察点 screen7_out;circle32/gen_boot32.py 生成最小 OS(第 32 环试验段);tb/tb_min_soc32.sv 协同仿真。
// rtl/row_blk.sv(节选):一块算完,块号自己加 1,接着取下一块(不放手、不叫软件)
ST_COMP: begin
for (n0 = 0; n0 < RB; n0 = n0 + 1) begin
part_out[n0*ACC_W +: ACC_W] <= partial_c[n0];
acc_out[(r0_i + n0)*ACC_W +: ACC_W] <=
acc_out[(r0_i + n0)*ACC_W +: ACC_W] + partial_c[n0];
end
blkcnt_out <= blkcnt_r + 8'd1; // 又处理完一块
if (auto_r && more_blk) begin // 还有块 → 自增续跑
blk_r <= blk_r + 1'b1; // 块号硬件自增
rbase_out <= nxt_r0; cbase_out <= nxt_c0;
f_i <= 2'd0; st <= ST_FETCH;
end else begin
busy <= 1'b0; done <= 1'b1; st <= ST_IDLE; // 扫完才放手
end
end
测试矩阵 W = [1,2,-1,0 / 0,1,1,-2 / -1,0,2,1 / 2,-1,0,1]、x = [3,-2,5,-4],手算 y = W·x = [-6,11,3,4]。最小 OS 在芯片上真跑(整段只说了一次 start):
| 动作 | 结果 | 说明 |
|---|---|---|
| RB_AUTO=1,一次 RB_GO | acc = [-6, 11, 3, 4] | == W·x(第 31 环这里要发 4 次 start) |
| 读 RB_BLKC | 4 | 硬件自己数到 4 块(探针) |
| 读 RB_BLKO | 3 | 块号加到 NB-1 才停 |
| RB_AUTO=0 老路径(4 次 start) | [-6, 11, 3, 4] | 两条路径逐位一致(递增不推翻) |
| RB_CLR | acc=[0,0,0,0]、块计数 0 | 累加器与块计数一起归零 |
单元仿真 tb_row_blk32 7 组用例通过(含自增途中逐块快照:acc 从 [-1,-2,0,0] → [-6,11,0,0] → 整行,全在同一次 start 之内;稀疏激活 x=[2,0,3,0] 逐 lane 对表);协同仿真 tb_min_soc32:SCREEN = 0xF905FD04、SCREEN2 = 0x02040404、SCREEN3 = 0x04000204、SCREEN4 = 0x10200707、SCREEN5 = 0x04081807、SCREEN6 = 0x0402FFFA 与第 31 环逐位一致(本环只给行块引擎加了个自增开关,主通路一个字没动);采样序列 [0,2,0,0,0,3]、第 6 步吐停止符 3 而停。全量回归 49 项全绿。
从「核一块块喂」走到「硬件自己数块号、一次启动扫完整个矩阵」——这正是软件与 NPU 的分工线:核只需要说「算这个矩阵」,剩下几百上千个行块由硬件流式走完,核不必为每块再插一笔 MMIO。芯片离「跑真 DeepSeek 权重」又近一层:层的权重再大,也只是硬件自己一块块数过去、累加器一格格长起来。
现在自增只能从块 0 扫到底。真芯片上要能把大矩阵分段扫描:核给一个起始块号与块数,硬件从任意一块起自增,扫到哪儿停由寄存器说了算——大矩阵因此能一次扫一段、几段拼完整层,配合预取把下一段提前取进来。下一环:起始块号可编程 · 自增从任意块起(大矩阵分段扫)。