每天推进一圈 · 点进去看当天干成了啥
{标签,段长,起始块号};核只写 RB_TBLS(段表起始字)+ RB_TBLN(段数),发一条 RB_GO,硬件自己发段表读地址(与取权重共用同一条 SRAM 读端口)、解出起点/段长、自增扫完一段,再自读下一条,扫完 n 段才 done · 探针 RB_TBLC(按表扫过几段)/ RB_TBLT(当前段标签——核没写过这个寄存器) · 段表 3 段(起0/2/3)→ acc=[-6,11,3,4] == 整扫、RB_TBLC=3、RB_TBLT=0xE3;改写表第 0 条 → 结果跟着变(表真从内存读);RB_TBLN=2 → 只走前两条 · 单元仿真用 8×8、16 块大矩阵 3 段连扫 == 整扫 · 页 0x3D4~0x3E8 · SCREEN9 0x03E3FAFB · 主通路逐位一致 0xF905FD04 · 回归 53 项sw 写配置 + 一次 sw 发 RB_GO + lw 轮询 RB_BUSY + 四条 lw 读 RB_ACC。本环把它收回 ISA:npu.rbmat rs1, rs2(rs1=激活向量、rs2={段数,段表基址})+ npu.rbmat.s(段表阻塞读,对照)——一条指令内含「清累加器 + 段表流式连扫」;npu.wait 一并覆盖矩阵扫(min_bus 把 rb_busy 并进 npu_busy),npu.rd 选源放宽到 funct7[2:0](4=累加器 lane0 / 5=段数 / 6=块数) · 硬证据:MMIO 开关全关后一条指令 → acc=[-6,11,3,4] == 一次整扫;npu.rd 读回与 MMIO 读回逐位一致;描述符里写段数=2 → 第三条表没走;老路径与指令路径逐位一致 · SCREEN11 0xFAFA0304 · 主通路逐位一致 0xF905FD04 · 回归 57 项{标签,段数,段表基址,权重基址,结果槽,清累加器}——每条说清①这块矩阵住在哪②段表在哪、几段③结果摆哪个槽④要不要清累加器;核只给「链在哪、链多长」,硬件一条条走完,中间不发第二个 start、不读一次累加器 · 新 ISA npu.rbchain / npu.rbchain.s(rs2={链长,链基址}),npu.wait 一并覆盖链,npu.rd sel=7 读回条目数 · 新页 0x750~0x7AC(含 4 槽×4 lane 结果槽)· 硬证据:MMIO 开关全关后一条指令 → 四个槽 = 四块矩阵各自的 W·x([-6,11,3,4]/[3,-2,5,-4]/[2,2,2,2]/[6,-11,-3,-4])、RB_CHNT=0xC3 真从内存读来、改链条目权重基址只有那一槽变、rbchain.s 与流式取逐位一致、老路径逐位一致 · 单元 35 用例 + ISA 契约 + 协同仿真 · SCREEN12 0x04C3FAFC · 主通路逐位一致 0xF905FD04 · 回归 60 项lw 出来、再 sw 进入口。本环把这条搬运也收进硬件:新增 slot_feed 链槽直喂引擎,槽总线片上直连、按每槽 2bit 角色映射(0=Q 1=Q2 2=K 3=V)饱和量化后直送头0 Q / 头1 Q / K 追加口 / V 追加口,喂完那拍顺手把 K/V 追加进本层环 · 新 ISA npu.sfeed rs1(funct3=111,一条指令锁入口+喂槽)· 新页 0x7B0~0x7DC · 硬证据:直喂路径多头跑出结果而核搬运计数 SF_CORE=0;手动搬运路径 SF_CORE=20(16 槽 lane + 4 入口),两条路径多头结果逐位一致;SF_NFEED/SF_ROLE/SF_SRC 由硬件自报(Q←槽2 Q2←槽3 K←槽0 V←槽1);换小激活 x=[1,1,1,1] → 四槽全落 INT4 内可逐位对表 · 单元 + ISA 契约 + 协同仿真 · SCREEN13 0x00041401 · 主通路逐位一致 0xF905FD04 · 回归 63 项{层标签,用途槽,字数,基址},整网引擎等本层权重时把 req 拉高并给出层号,引擎按描述符一条条取字、边取边直投进层权重寄存器,别的层的条目硬件自己跳过——核没参与筛选,只写 WC_MODE/WC_TBLS/WC_N 三个寄存器 · ISA 不加新指令:主路启动仍是 npu.net/npu.wait/npu.rd,换的是「权重从哪来」,解码循环一行未改 · 新页 0x800~0x838(bit11 页)· 硬证据:SCREEN/2/3 与第 25~38 环逐位一致;NET_CHN_WRD=28/趟(2 层×14 字)、NET_CHN_WAIT>0(等的是链不是装载器);链侧 168 字/跳 60 条/走 120 条、末标签 0x01 从内存读来;尾巴 A(链)== B(公式)逐位一致、C(改层0 LN 描述符住处 12→14)≠ A——描述符基址真管着权重;还测了别名(两层共用一张矩阵)· 单元 tb_wei_chain + 协同 tb_min_soc39 · SCREEN14 0x1C3C0107 · 主通路逐位一致 0xF905FD04 · 回归全绿ST_WLD 举着 req 等链把本层 14 个字投完才开算,链整个落在关键路径上(NET_CHN_WAIT)。本环把第 29 环的乒乓预取搬到链上——链的 req/rdy 是电平握手,所以新增一个链取子引擎举着 req 走完一整轮(CP_IDLE→CP_REQ→CP_RLS),算本层时给下层 li_nxt 起一次链取、投进 ~bank_r,走到下层 ST_WLD 常常一拍换 bank 就开跑 · 核只多写一个寄存器 WC_PF(0x83C);WC_PF=0 = 第 39 环老语义,一字未删 · 新探针 NET_CHN_PF/NET_CHN_OVL + 新观察点 SCREEN15(0x848),前 39 圈地址映射一字不改 · 硬证据:等链拍数 阻塞 98 拍 → 预投 52 拍(省 46)、重叠 14 字(下层 14 字全在算本层时投回另一个 bank)、链取 2 次(冷启动+预投)、D 趟(预投)==A 趟(第39环阻塞)==E 趟(WC_PF=0)==F 趟 逐位一致;关预投那几趟两个探针都不再长;改一条描述符预投路径输出跟着变 · 踩实的钉子:① 旧代码写 bank 0 读 bank_r,前面第 29/30 圈把 bank_r 留成 1 → A 趟读到旧字(单测跑不出、整机协同才炸);② 电平握手撤不回半程,必须子引擎举着走完一轮;③ NET_CHN_WAIT 每趟 start 归零,用差值量出 −46→0xD2(差点把省 46 拍读成多等 210 拍);④ SCREEN15 写译码 6'd16 错位到 0x840(只读探针槽),屏幕恒 0 而自检全过 · 单元 tb_chain_pf + 协同 tb_min_soc40 · SCREEN15 0x620E3407 · 主通路逐位一致 0xF905FD04 · 回归全绿li_nxt ≥ layers_r),于是下一趟的第 0 层又要停下来冷启动、等链把 14 个字投完——解码循环一趟接一趟跑(每个 token 一趟),「每趟开头等一次」是最不该留的空转。本环(WC_XP=0x84C)在本趟最后一层还在算的时候就把下一趟首层(层 0)经链投进另一个 bank;下一趟 start 时 ST_WLD 一看就绪标志正指着层 0(ld_ready && ld_layer==0),一拍换 bank 就开跑 · WC_XP=0 = 第 40 环老语义,一字未删 · 新探针 NET_XP_CNT(跨趟预投次数)/ NET_XP_STL(本趟首层等链拍数,每趟 start 归零)+ 新观察点 SCREEN16(0x858),前 40 圈地址映射一字不改 · 硬证据:首层等链 52 拍(G1 冷启动)→ 0 拍(G2 跨趟预投)、跨趟预投 2 次、G2 趟 == A 趟(第 39 环阻塞链)逐位一致、H 趟(WC_XP=0)又回到 52 拍;单元仿真连跑 4 趟 844 < 1000 拍;改一条层 0 描述符(LN 住处 12→14)跨趟预投路径输出跟着变 · 踩实的钉子:① ST_IDLE 的 start 分支开头无条件清 ld_ready,跨趟预投正靠它活着 → 已备好分支进了 ST_WLD、标志却被同一拍清 0,空等到看门狗(缝隙在同一拍的两个赋值之间);② 看门狗上先分清「谁在等谁」(链早已 ent=9/armed=1 回空闲)再修「为什么等不到」 · 单元 tb_chain_xp + 协同 tb_min_soc41 · SCREEN16 0x34000207 · 主通路逐位一致 0xF905FD04 · 回归全绿hit_bank)不靠算,三 bank 怎么环转都不读错;f_seq/lc_seq 用连续序列号跨趟不回绕;消费即腾位;两 bank 切三 bank 时把上一趟已投好的首层认领进池子 · 新寄存器 WC_PF2(0x85C),WC_PF2=0 = 第 41 环老语义一字未删 · 新探针 NET_PF2_CNT/NET_PF2_OVL/NET_BANK_MSK/NET_WL_STL + SCREEN17(0x870),前 41 圈地址映射一字不改 · 硬证据:三 bank vs 两 bank 输出逐位一致 0xFA03FC07、掩码 011→111、4 趟×2 层投递 10 次(每层都被提前投过)、慢内存 3 趟 6332 < 6336(快内存 845 vs 844 持平——诚实口径)、阻塞/公式老路径逐位一致、改层0描述符输出跟着变 · 协同 tb_min_soc42:P0 冷启动允许等、P 趟稳态首层等链 0 拍,SCREEN17=0x07060007,主通路 SCREEN=0xF905FD04/SCREEN2=0x02040404/SCREEN16=0x34000207 与 25~41 环逐位一致,采样 [0,2,0,0,0,3] · 钉子:① rd_bank 无位宽声明(1 bit)→ bank2 截成 0、gamma 用错层;② start 拍 lcnt 还是旧值,得显式拿 0 搜(any_hit_z);③ 跨模式 f_seq=0 vs lc_seq=29 序列错位;④ min_bus 读译码漏 6'd23,WC_PF2 读回恒 0 假失败;⑤ 汇编 s12/s13 不存在、0x868(a7) 偏移超 12bit · 单元 tb_chain_pf2 + 协同 tb_min_soc42 · 回归全绿sw:WC_MODE / WC_TBLS / WC_N / WC_PF / WC_XP / WC_PF2;六个寄存器散在三个页里,顺序错一个就静默变慢。本环把整池配置收进 ISA:核升 v11,开新主 opcode custom-1,一条 npu.pool rs1, rs2——rs1={池深[7:4], 池深2[3], 跨趟预投[2], 预投下层[1], 走链[0]}、rs2={链长[15:8], 链基址[5:0]};EX 拍两个操作数同一拍锁进六个硬件寄存器,核一个配置寄存器都不写(custom-0 前 42 圈编码一位不动)· 新增 POOL_WCORE 计数器量化「核写了几次配置」· 新页 0x874~0x88C:POOL_CFG/POOL_DESC/POOL_N/POOL_WCORE/POOL_RST + SCREEN18,前 42 圈地址映射一字不改 · 硬证据:指令路径 POOL_WCORE=0 vs 老路六条 sw =6,六个寄存器读回逐位一致、POOL_CFG=0x002F/POOL_DESC=0x0A34/POOL_N=1 全由硬件自报(核没写过)、池深 2→1→2 让 WC_PF2 1→0→1(池深真管事)、全零配置六个寄存器全关且 POOL_N 照数、一条指令配出的池跑 Q 趟 == A 趟 0xF905FD04、SCREEN/2/3/17 与 25~42 环逐位一致 · 钉子:① WC_PF(0x83C)/WC_XP(0x84C) 从 R40/41 起「写口有、读口没有」,读回恒 0(两张表都要加);② 0x874(a7) 等偏移超 12bit 有符号,改 a6=0x870 基址;③ 0x2B 漏了 bit2(跨趟预投),配置位错不一定体现在输出上,必须逐位读回;④ 指令脉冲只活一拍,锁存必须在每拍默认块里 · 单元 tb_isa_pool + 协同 tb_min_soc43 · SCREEN18=0x02010007 · 回归全绿sw EMB_TOK → lw EMB_X → npu.net → npu.wait → npu.rd → sw LM_X → sw LM_START → 轮询 → sw SMP_START → 轮询 → sw GEN_STEP_W → 轮询,十几条访存。本环新增 step_gen:一条 npu.step rs1(token), rs2(层数) 在片内按固定顺序跑完 词嵌入查表 → 整网 N 层 → LM 头 → 采样器 → 生成控制器登记,每级「先发单拍 start、再等 busy 落下」,忙闲采样有最小拍数保护(不会把还没忙起来的引擎当成做完)· 核升 v12,custom-1 加 funct3=001,npu.rd 选源放宽到 funct7[3:0](8=这一步的 token、9=步数、10=状态),前 43 圈编码一位不动 · npu.wait 一并覆盖一步生成 · 新页 0x890~0x8B8:STEP_N / STEP_TOKOUT / STEP_STATUS / STEP_CORE / STEP_ST / STEP_RST + SCREEN19,前 43 圈地址映射一字不改 · 硬证据:主路解码循环整段换成六步 npu.step,采样序列 [0,2,0,0,0,3] 与老路径逐位对表全中、token 序列打包 0x0203、STEP_N=6、STEP_CORE=0(六步里核一次没写 EMB_TOK/LM_X/LM_START/SMP_START/GEN_STEP_W/NET_X/NET_LAYERS/NET_GO)、最终残差流 0xF905FD04 与 25~43 环逐位一致、SCREEN2~18 十五个观察点一个没漂 · 钉子:① step_rst_pulse 只写复位分支 → 常高、每拍清空 tok_out/steps(第一步期望 0 恰好假绿,第二步才炸);② 0x8A0 起超 12bit 有符号偏移,须 a6=0x890 基址;③ 老程序隐含 t1==1(WC_START/MHA_START),SCREEN19 算完忘了还原 → 滑窗没启动、第 2 阶段自曝;④ 核的 npu_step_tok 输出与 MMIO reg 同名双驱动 → BLKANDNBLK,须独立 wire + 二选一;⑤ npu.rd 选源仍 3bit 会把 8/9/10 截成 0/1/2 · 单元 tb_step_gen + 协同 tb_min_soc44 · SCREEN19=0x06000203 · 回归全绿npu.step,可一步只是一个字;一句回答是几十步,核还得在软件里循环「发一步 → 等 → 读 token → 喂回输入 → 再看停没停」。本环新增 gen_run 整段生成引擎:一条 npu.generate rs1(prompt), rs2(层数) 在片内自己「跑一步 → 把 token 反馈成下一步输入 → 看生成控制器停没停 → 未停再来一步」,跑到停止符或长度上限才回核;本地另有 MAX_STEPS 兜底,生成控制器配错也不挂死 · 核升 v13,custom-1 加 funct3=010,npu.rd 选源 11~14 = token 序列(每步 2bit)/ 步数 / 状态 / 最后一个 token · 新页 0x8C0~0x8F0:GENRUN_N/SEQ/STATUS/CORE/RUNS/ST/HDIFF + SCREEN20,前 44 圈地址映射一字不改,npu.step 老路径一条不删 · 硬证据:主路六组 npu.step 换成一条指令,token 序列 [0,2,0,0,0,3]→0x0203 与老路径逐位一致、GENRUN_N=6、stop_hit=1(最大步数特意调到 8,停只能来自停止符 3)、GENRUN_CORE=0(整段里核一次没写 EMB_TOK/LM_X/LM_START/SMP_START/GEN_STEP_W/NET_X/NET_LAYERS/NET_GO)、最终残差流 0xF905FD04 与 25~44 环逐位一致、SCREEN2~19 十七个观察点一个没漂 · 钉子:① gen_run 锁了层数却忘了给 step_gen.layers_in 加来路 → 每步 rs2=0、整网一层没跑(输出「有点像」最容易误判成模型问题);② 汇编里 s10 写成 x18(其实是 s2)→ npu.rd 写进 s2、检查读 s10 永远是 0;③ ALU 结果(andi)做分支条件只隔一拍会读到旧值,须两拍;④ 老「多头累计 s9」在整段生成后失效(最后一步两头可能相同),改由硬件粘滞位 GENRUN_HDIFF 记「整段里是否出现过两头不同」 · 单元 tb_gen_run + 协同 tb_min_soc45 · SCREEN20=0x020306E0 · 回归 77 项全绿npu.generate 只能跑「一段」,一段之后怎么办还在软件里:再发一条、再喂一个输入、再把上下文接上。本环新增 conv_loop 对话回路引擎:一条 CONV_GO 跑完一场多回合对话——回合 0 开新窗口(清上下文)→ 整段生成 → 记下本回合 token 序列 / 步数 / 末 token;回合 1 的输入由硬件接上「上一回合的末 token」(carry)或取 OS 队列里的下一句;回合之间 K/V 留在环里(mode_ctx=1,同一场对话记得住)或每轮清空(=0,对照)· kv_ring 加 clear 单拍口做会话真空(已有设计接 0,前 45 圈逐位不变);layer_net 加 ctx_clear/ktot_out(主路 K/V 环长在整网引擎里);新页 0x900~0x948:CONV_TURNS/TOKENS/SEQ/TLAST/TN/CTXCLR/KVTOT/TSEQ01/TSEQ23/PIN + SCREEN21/22 · 硬证据:会话 A(2 回合 ctx=1)turns=2 / tokens=7 / ctxclr=1 / KVTOT=7==全部 token;B(ctx=0)ctxclr=2 / KVTOT=3==末回合步数;A 的 PIN=1(队列第 2 句)、C 的 PIN=3(回合 0 末 token,核没写过 3)——「接着上一句」在硬件里;同一句输入 1,A/B 回合 1 答案不同;SCREEN=0xF905FD04 与 25~45 环逐位一致、SCREEN21=0x005B000B / SCREEN22=0x12470E0F · 钉子:① 主路 K/V 环在 layer_net 不在 min_bus,接错地方累计恒 0;② KVTOT 层0|层1 打包,比较前 andi 0xFF;③ CONV_GO 后立刻读 BUSY 会读到还没起忙,轮询一圈就退出(补 4 nop);④ x31(t6) 读回/比较异常、换 s9 才行;⑤ 会话起步第一段仍受上一场运行历史影响(未定位,留待下一环) · 单元 tb_conv_loop + 协同 tb_min_soc46CONV_GO 跑完,可「开这场对话」仍是核写 5 个配置寄存器(prompts / 回合数 / 模式 / 层数 / GO)。本环把整场对话收进 一条 npu.chat rs1, rs2:rs1=每回合输入队列,rs2={carry, ctx, 层数, 回合数},EX 拍直接送进对话引擎,核一个配置寄存器都不写 · 核升 v14(custom-1 funct3=011),npu.rd 选源加宽到 5bit,新增 15~20 六个读回:整场 token 序列 / 状态(清上下文次数·token 总数·回合数·done·busy)/ 每回合末 token 与步数 / 回合 0~3 各自序列 / 末回合真实输入 · 新增 CHAT_WCORE(0x94C)量化「核写了几次配置」+ SCREEN23(0x950);CONV_RST 一并清计数 · 硬证据:D(一条指令)turns=2 / tokens=8 / ctxclr=1 / PIN=1 / transcript=0x00002E67 / tseq01=0x0267_000B,npu.rd 六个新选源读回整份记录;E(MMIO 5 条 sw)结果与 D 逐位一致;CHAT_WCORE 0 vs 5;SCREEN=0xF905FD04 与 25~46 环逐位一致、SCREEN21/22 未漂 · 钉子:① 选源加宽到 5bit 后常量仍写 4'd16~4'd20,Verilog 静默截成 0~4,六路读回全错;② CONV_RST 忘了清 CHAT_WCORE(A/B/C 跑完是 15)假失败;③ 比等价性前先把 SMP_SEED 回到同一颗;④ 第 46 环「换场次后首段受历史影响」仍在(D 与 A 第二回合不同,但 D==E,ISA 与 MMIO 等价成立) · 协同 tb_min_soc47 · SCREEN23=0x08020500 · 回归全绿kv_archive 会话记忆归档引擎:一场结束,把每层 K/V 按逻辑时间序写进会话记忆区(512 字 SRAM),前面写 8 字表头(SALT / 层数·环深 / K·V 条数 / 摘要 / 会话号 / 载荷字数 / 结束标记);下一场开回合 0 时先清环、再逐条追加回环,然后逐条比对 + 摘要校验,一条不差才 ok=1 · conv_loop 增 mode_mem:回合 0 不清上下文而是等恢复,整场结束自动归档 · 新页 0xA00(CMD/GO/BUSY/OK/WORDS/DIGEST/MISMATCH/MAGIC/KVCNT/SCREEN24) · 硬证据:A 两回合自动归档 16 字、魔术字 SALT、会话号 0x0048;恢复 ok=1 / mismatch=0 / 每层 4 条;参考整网输出 Y_ref=0x05FF04F9,换一份上下文 Y_other=0x0800FAFC(≠),恢复后 Y_res=0x05FF04F9(逐位相同 = 一直没关);会话 C 的 CONV_CTXCLR=0(回合 0 走恢复、没清上下文) · 单元 tb_kv_archive:归档⇄恢复 12 条逐位一致、改坏一字 mismatch=1/ok=0、空会话、坏魔术字不污染环 · 协同 tb_min_soc48:SCREEN=0xF905FD04 / SCREEN2=0x02040404 / SCREEN23=0x08020500 与 25~47 环逐位一致 · 钉子:① 内存/环读使能写成寄存器 → 采样晚一拍、魔术字恒 0(改组合驱动);② save 只写读口没写存口,ARC_MAGIC 恒 0;③ 恢复前必须清环,否则追加进非空环只剩最后 DEPTH 条;④ 摘要不能省——改坏内存后环与内存仍「自洽」,只有摘要会抖;⑤ 借 s4 存 ARC_OK 把收尾残差流覆盖成 1 · SCREEN24=0x10000F04 · 回归全绿sess_slots 会话槽位目录引擎:会话号 → 槽位的映射由硬件维护——open 查/分配、find 只查、commit 落摘要字数、free 只作废指定会话、reset 重建目录;六个数据槽各 64 字,目录住 0x180~0x1BF,空内存自动建目录、满了按轮转换槽 · 归档引擎基址从固定 0x000 改成可编程 ARC_BASE(0xA4C) · 新页 0xC00(CMD/SID/GO/CDIG/CWRD/RST + BUSY/HIT/SLOT/BASE/USED/MAGIC/ERR/STAMP/ESID/EDIG/EWORDS/EVALID) · 硬证据:会话 A 槽 0 / 基址 0,会话 B 槽 1 / 基址 0x40,两场同时住内存;恢复 A Y_A2==Y_A、恢复 B Y_B2==Y_B 且 ≠Y_A2;find A 读回摘要 == commit 摘要;free A 后 A miss、B 仍命中槽 1;未知会话 miss 且 err=0 · 单元 tb_sess_slots(空内存建目录/分槽/find 不改/commit 回读/free 只动一个/空槽复用/六槽轮转/满目录原位命中/reset) · 协同 tb_min_soc49:SCREEN=0xF905FD04 / SCREEN2=0x02040404 / SCREEN23=0x08020500 / SCREEN24=0x10000F04 与 25~48 环逐位一致 · 钉子:① 读表头 h=3 那拍 hdr[3] 正在写,读到旧指针 0(改取 mem_rdata);② free 漏写表头 used,used 涨到 7;③ 满目录换槽仍 +1(替换不算新槽);④ commit 看被 start 清掉的 e_valid,改独立 last_slot/last_valid;⑤ 目录启动撞上第 48 环自动归档,GO 脉冲被互斥挡掉(先等 ARC_BUSY 落);⑥ 归档字数别写死 16(单回合 3 步 = 12 字) · SCREEN25=0x01FF0240 · 回归全绿sess_switch 会话开关场引擎:一条 npu.sess(rs1=会话号、rs2=enter/leave/switch)把两个子引擎串起来——enter 查/分槽→恢复 K/V→进这场;leave 归档→摘要/字数 commit 回目录;换场时自动先把当前这场收好。引擎自己记住「现在在哪一场」(cur_sid/cur_slot/cur_base/cur_valid),没有旧上下文的空槽也能进(mem_ok=0 不是错误) · 核升 v15,custom-1 加 funct3=100;npu.rd 新选源 21~29(槽号/基址/状态/当前会话/字数/摘要/mismatch/leave/enter) · 新页 0xD00(SX_CMD/SID/GO/RST/SCREEN26 + BUSY/OK/SLOT/BASE/CUR/WORDS/DIGEST/MISMATCH/ENTER/LEAVE/CORE/MEMOK/STATUS/ERR/ST) · 硬证据:MMIO 进 B 场 SX_CORE=3;RST 后指令路径进 C(自动 leave B,新槽 0 / 基址 0 / mem_ok=0)→ 对话 → leave(12 字)→ 进 D(槽 2 / 基址 0x80)→ 另一场对话 → leave(digestD≠digestC)→ 进 C(摘要==digestC / mismatch=0)→ 进 D(摘要==digestD)跑整网 Y_D → 进 C 跑整网 Y_C≠Y_D;全程 SX_CORE=0、enter=5/leave=5 · 单元 tb_sess_switch(真 sess_slots+kv_archive+两层 K/V 环模型:取回 12 字逐条对表 / 换场自动 leave / leave 不作废槽 / round-trip 不丢话 / 重复 enter 是 no-op / 空槽可进 / 不在场里 leave 报错) · 协同 tb_min_soc50:SCREEN=0xF905FD04 / SCREEN2=0x02040404 / SCREEN23~25 与 25~49 环逐位一致,SCREEN26=0x050500FF · 钉子:① 4bit 装不下 17 个状态(4'd16→0、4'd17→1,case 重叠,扩 5bit);② 测试台连线名差三个字母,Verilator 隐式生成 1bit 空线、start 恒 0;③ 重复 enter 是 no-op 不拉 busy,轮询 busy 会挂死;④ 状态 nibble 位序算错(0b0101/0b1101 才是对的);⑤ leave 成功忘了置 ok;⑥ 空会话 load 失败不是失败(mem_ok 单独报) · 回归全绿{last_valid, last_slot, last_sid, stamp}(低 8 位仍是累计开启次数,老读回不变),每次 commit 硬件记下这一场;目录新增 last(cmd=6,只查不改)· 开关场新增 boot(cmd=4):问目录 → 命中就取 sid/slot/base → 清片内环 → 恢复 K/V → 进这场;没命中就是首次开机(ok=1 / mem_ok=0 / cur=0),并发一拍 boot_clear 清空片内环 · 核升 v16,custom-1 加 funct3=101:npu.boot 无操作数,核既不传会话号也不扫目录 · 新观察点 0xD64 SX_BOOT / 0xD14 SCREEN27 · 硬证据:运行中是 C、最后提交是 D,一条 boot 恢复到 D(0x62) 而不是 C;状态 0x0D;恢复摘要 == 目录里 D 的摘要;Y_boot == 第 50 环的 Y_D;SX_BOOT=1 且 SX_CORE=0;目录重建后再 boot:ok=1/mem_ok=0/cur=0;空上下文跑整网 Y_fresh ≠ Y_D · 协同 tb_min_soc51:SCREEN=0xF905FD04 / SCREEN2=0x02040404 / SCREEN23~26 与 25~50 环逐位一致,SCREEN27=0x020062FF · 钉子:① miss 只清 cur_valid 忘清 cur_sid,读回还以为是 D(状态要整体清);② 重建目录不等于清片内环,环里残影让「空上下文」假通过(boot_clear 接 ctx_clear);③ 表头第 3 字共用,整字重写会抹掉 last 字段(统一按位域拼);④ cmd 从 2bit 扩 3bit,MMIO/读回/测试台三处都要改,漏一处 boot 被截成 enter · 回归全绿list(cmd=7,只读):扫六个目录项,把活着的会话压成定长 13 字小表写回 0x1C0——[人数, {槽号,sid}, {0,字数}, …],空位写 0,OS 用已有 ARC_PEEK 读、不新开内存口 · 清单按紧凑序号排而非槽号(释放中间槽不留洞),槽号单独放每条高 3 位 · 开关场加 list(cmd=5)转给目录并带回人数,不动当前会话/不搬 K/V/不改目录;空目录 = 0 场 + ok,不是错误 · 核升 v17,custom-1 加 funct3=110:npu.sess.list 无操作数,npu.rd sel=30 读回人数 · 0xC5C SS_LCNT / 0xD18 SCREEN28 · 硬证据:三场 0x71/0x72/0x73(9/4/15 字)→ 一条 list 人数 3,表逐条对:槽0/0x71/9、槽1/0x72/4、槽2/0x73/15;释放 0x72 再 list → 2 条,第二条槽2/0x73,空位读回 0;find 0x71 摘要仍 == commit 摘要;SX_CORE 一位没涨;重建后 list = 0 场 · 单元 tb_sess_list(空目录不建目录/三场/只读/释放/六场/重建),协同 tb_min_soc52:SCREEN=0xF905FD04 / SCREEN2=0x02040404 / SCREEN23~27 与 25~51 环逐位一致,SCREEN28=0x030272FF · 钉子:① 用槽号当清单下标,释放中间槽后第 1 条读到空槽残值(另开紧凑 lidx,「住哪」与「排第几」分开);② 字数在目录项 w3 的高半字,取 [15:0] 读成 kcnt/vcnt;③ 短清单只写前面几条,上次的尾巴被当幽灵会话(定长表全写、空位写 0);④ 空目录被按老逻辑置 err 或顺手建目录(list 必须纯查询) · 回归全绿(本环单元 + 协同 + 相邻 sess_switch / min_soc51)npu.boot 恢复完上一场就进对话,清单要另外发指令,开机第一句话没人负责。本环让一条 npu.boot 串起三件事:恢复上一场 → 数一遍清单 → 排好第一句问候——恢复完不再收场,转去问目录「有几场」,再把人数直送新增的 boot_greet 开机问候合成器 · 问候词表在芯片里:每个短语固定 8 个汉字 = 24 字节 = 6 个 32bit 字(0 场「今天想聊点什么?」、1 场「你有一场没聊完。」……6+ 场封顶「你有六场没聊完。」),定长整行直接落会话记忆区 0x1E0 的对话窗口缓冲区,OS 读 6 个字就上屏、核一个字节不拼 · 六个槽是上限:code=min(cnt,6) 说给人听、cnt_out 如实报原始人数 · 会话记忆区第三个写主,boot 流程严格串行(load → list → greet),优先级 bg > ss > arc · ISA 一个操作数没加(npu.boot 仍无操作数;npu.rd sel=30 在 boot 后读回的就是开机清单人数) · 新页 0xE00(BG_BASE/BG_RST + BG_CNT/CODE/BYTES/SID/RESTORED/W0..W5/BUSY/DONE/SCREEN29) · 硬证据:三场 0x81/0x82/0x83(各 enter+leave 落完整表头)→ 一条 boot:SX_CUR=0x83、SX_MEMOK=1、npu.rd sel=30=3、BG_CNT=3/CODE=3/BYTES=24/SID=0x83/RESTORED=1,BG_W0..W5=「你有三场没聊完。」的 UTF-8 逐位一致;从 0x1E0 读回 6 个字与 BG_W* 逐位相同(这行字已在窗口缓冲区);目录重建后 boot → 0 场说「今天想聊点什么?」;只造 0x84 → 1 场说「你有一场没聊完。」;全程 SX_CORE 一位没涨 · 单元 tb_boot_greet(0~6 场、9 场封顶、换落点、复现、忙时防重入、clear),协同 tb_min_soc53:SCREEN=0xF905FD04 / SCREEN2=0x02040404 / SCREEN23~28 与 25~52 环逐位一致,SCREEN29=0x0303895A · 钉子:① npu.sess 的 rs1/rs2 是寄存器号不是立即数(第一版把 1/2 写进 rs2 字段,核取 x1=0 → cmd=0 → 三条 enter 全 err、used 恒 0;改成 li a0,sid; li a1,cmd; npu.sess a0,a1);② 口播封顶、报告不封顶(code=min(cnt,6),cnt_out 原始值);③ 第三个写主必须排在 list 之后动作,互斥要写在时序里;④ 定长 24 字节才好上屏(变长要找结尾、处理半行) · 回归全绿(本环单元+协同+相邻 sess_switch/sess_list/min_soc51/min_soc52)greet_pick 问候接话引擎:一条 npu.pick 序号 → 硬件自己读清单表 0x1C0(字0=人数,条目 {槽号[31:29],13'b0,sid[15:0]},下一字低半字=字数)→ 解出那一场的会话号 → 排好一句 24 字节 UTF-8「好,接着第 N 场。」写进对话窗口缓冲区 0x1F0 → 再发一拍 enter 脉冲,把号直接送进开关场引擎恢复上下文;核不查表、不取号、不拼字、不发第二条指令 · 清单是紧凑序号(槽号可稀疏),点第 2 场拿到的是「排第 2 的那一场」 · 点 0 / 越界 / 条目 sid=0:ok=0/err=1,不换场、不写问候 · 核升 v18,custom-1 加 funct3=111:npu.pick rs1;npu.rd 选源放宽到 6bit,新增 sel=31/32/33(会话号 / {槽,ok,err} / 字数) · 接话页 0xF00(PK_IDX/GO/BUSY/DONE/OK/ERR/SID/SLOT/WORDS/BYTES/CNT/W0..W5/RST/SCREEN30/PK_CORE);会话记忆区第四个写主,先写问候、后换场,不抢同一个写口 · 硬证据:三场 0x91/0x92/0x93 → boot 恢复 0x93、清单 3 场、Y93;一条 pick 1 → PK_SID=0x91/SLOT=0/WORDS>0/OK=1、SX_CUR=0x91/MEMOK=1、问候「好,接着第一场。」、Y91 ≠ Y93;一条 pick 3 → 回到 0x93、问候「好,接着第三场。」、Y == Y93;一条 pick 7(人数3)→ 只报 err,SX_CUR 不动、SX_ENTER 不涨;全程 PK_CORE=0 / SX_CORE 没涨 · 单元 tb_greet_pick(点1/2/3、越界、空条目、人数由表头决定、复现、clear),协同 tb_min_soc54:SCREEN=0xF905FD04 / SCREEN2~29 与 25~53 环逐位一致,SCREEN30=0x03911F5A · 钉子:① 指令的 rs1 没接进引擎(只接了 MMIO 的 PK_IDX,pick 恒按 0 → 全 err);② 清单表槽号在 [31:29](写成 [18:16] 读回全是 0);③ 同步 SRAM 读要留有效拍(地址+ren 那一拍、下一拍才出数,采样早一拍拿到上一条);④ mv 之后立刻分支会读到旧值,必须隔两条 nop · 回归全绿forget_sess 删掉一场引擎:一条 npu.forget 序号 → 硬件读清单第 N 条(紧凑序号,槽号可稀疏)→ 解出会话号/槽号/字数 → 排 24 字节回执「好,这场删掉了。」落 0x1D0 → 向目录发 free(目录项作废、该槽数据清零、used−1;删到 last 场连 last 记录一起清)→ 再发 list 重排清单(后面的场次自动补位、空位写 0)→ 一拍 forget 脉冲通知开关场:删的若是当前场,当前指针当场清 0(不留指向已释放槽的悬空指针,否则下次 leave 会把已删名单长回来)· 核不查表、不取号、不拼字、不发第二条指令 · 核升 v19,custom-1 八个 funct3 已满,开新主 opcode custom-2(1011011):funct3=000 npu.forget rs1,前 54 圈编码一位不动;npu.rd 新增 sel=34/35/36(剩几场 / 被删的号 / {槽,ok,err})· 删除入口接在接话页后 0xF50,会话记忆区第五个写主 fs > gp > bg > ss > arc,严格串行「先回执、再 free、再 list」· 硬证据:三场 0x91/0x92/0x93、当前 0x93 → 一条 forget 2:FS_SID=0x92/SLOT=1/CNT=2、SS_USED=2、清单变 0x91/0x93、回执逐字对上、跑整网仍 == Y93;一条 pick 2 → 第 2 条已是 0x93(0x92 不在清单里了);一条 forget 2(删当前场)→ SX_CUR=0 / cur_valid=0、used=1;一条 forget 1 → 清单 0 条;空清单点/删只报 err;全程 FS_CORE=0 / SX_CORE、PK_CORE 没涨 · 单元 tb_forget_sess(稀疏槽 0/2/5、删中间补位、删 last、删到空、越界、回执、复现),协同 tb_min_soc55:SCREEN=0xF905FD04 / SCREEN2~30 与 25~54 环逐位一致,SCREEN31=0x0292DD55 · 钉子:① 清单地址算错一位:紧凑序号 j 的条目是 0x1C0+2j−1,别拿「阶段号」当序号推地址(第二场读成第三场);② 槽基址是「槽号×64」,删槽 1 清的是 0x040 不是 0x080;③ 32bit 常数(0x20000092)要 lui+addi 两条拼,分支前一条 nop 不够、要两条;④ 删完必须重排清单,只 free 不 list 会留下「清单里有、目录里没有」的幽灵条目;⑤ 删当前场只清目录不够,开关场的当前指针也要清,否则下次 leave 把已删的场重新归档回来 · 回归全绿rename_sess 改名引擎:一条 npu.rename 序号 → 硬件读清单第 N 条(紧凑序号)→ 解出会话号/槽号/字数 → 把名字寄存器(0x268~0x27C,6 字 = 24 字节 UTF-8)落进该槽名字格(0x200 + 槽号×6)→ 排回执 「好,名字记下了。」 落 0x1D8 → 按当前清单顺序重排整张命名清单(0x240:人数 + 每条 {槽,sid}/字数/名字6字)→ 空槽名字格回收(防新会话继承旧名字)· 核不查表、不取号、不搬名字、不排清单、不发第二条指令 · 核升 v20,custom-2 加 funct3=001 npu.rename rs1;npu.rd 新增 sel=37/38/39/40(会话号 / {槽,ok,err} / 清单人数 / 有名字场数)· 改名页 0xFA0~0xFE8,会话记忆区第六个写主 rn > fs > gp > bg > ss > arc;会话记忆区 512→1024 字,0x000~0x1FF 一字未改,0x200 起才是名字格/命名清单 · 硬证据:重建目录后开三场 0xA1/0xA2/0xA3 → list 三条 → 名字「工作那场」写名字寄存器 → 一条 npu.rename 2:RN_OK=1 / RN_SID=0xA2 / RN_SLOT=1 / RN_CNT=3 / RN_BYTES=24 / RN_NVALID=1、0x206 名字格 6 字逐位、0x1D8 回执逐字、0x240 人数 3 且第 2 条 {槽1,0xA2}/字数/名字;再改第 3 场「查资料」→ RN_NVALID=2、槽 2 名字格 0x20C 落字;非法序号 0/4 只报 err,不写名字格/回执/清单;全程 RN_CORE=0 · 单元 tb_rename_sess(真 sess_slots + 同口 1024 字内存:改名 / 重命名不重复计 / 越界 / 删中间后按紧凑序号重排 / 空槽名字回收 / 复现),协同 tb_min_soc56:SCREEN=0xF905FD04 / SCREEN2~31 与 25~55 环逐位一致,SCREEN32=0x300A2256 · 钉子:① 同步 SRAM 读要「发地址 → 空一拍 → 收数据」三态,少留一拍会读到上一条的字(名字格写成字数、w0 错位);② 核没有会话记忆写口,名字输入只能走寄存器(0x2F0 是内存地址,写不进去,名字全 0);③ 新增写译码要加在对应页块(先加进 0x3F0 页,0x268 的写入静默丢失、nvalid 恒 0);④ 清单条目 {槽,sid} 高位是槽号,比较常数要 lui+addi 拼;⑤ 复位目录不会清轮转指针,开新会话前要 cmd=5 重建 · 回归全绿boot_greet 扩名,槽号由开关场引擎直连(slot / slot_valid,不经核),问候合成器自己发 6 拍读被恢复那一场的名字格(0x200 + 槽号×6,同步 SRAM「发地址→空一拍→收数据」三态),按 UTF-8 字节拼名字、遇 0 截止,后面接固定后缀「,接着聊吧。」(18 字节),空格补齐到 48 字节 = 12 个字,整行落 0x1E0~0x1EB;名字格全 0 → 退回第 53 环按场数那句(24 字节 6 字,词表一字未改);删除一场时该槽名字格 6 字清零——名字跟着会话走,不被下一场继承 · 硬证据:一条 npu.boot 恢复 0xA3(槽 2)→ SX_CUR=0xA3、BG_NAMED=1 / BG_BYTES=48 / BG_SID=0xA3 / BG_SLOT=2,0x1E0~0x1EB 十二字与「查资料,接着聊吧。」逐位相同;一条 npu.forget 3 → FS_OK=1 且 0x20C~0x211 六字全 0;新开无名 0xA4 分到槽 2 再 boot → BG_NAMED=0 / BG_BYTES=24 / BG_CNT=3 / BG_SID=0xA4,0x1E0 起 6 字 == 「你有三场没聊完。」(不继承旧名字)· 问候页 0xE00 新增只读探针 BG_NAMED / BG_W6..W11 / BG_SLOT,SCREEN33 在 0xE5C;会话记忆读口多一个主、写主优先级不变 rn > fs > gp > bg > ss > arc · 单元 tb_boot_greet/tb_forget_sess 复跑通过,协同 tb_min_soc57:SCREEN=0xF905FD04 / SCREEN2~32 与 25~56 环逐位一致,SCREEN33=0x0130A357 · 钉子:① 后缀「,接着聊吧。」是 6 个汉字 = 18 字节,不是 15(少算一个「。」整行错位、第 7 字就对不上);② 名字字节下标是 bi[4:2](24 字节 → 6 字),写成 bi[2:1] 只取到 4 个字;③ 名字格全 0 要退回按场数,否则会说出「,接着聊吧。」这种半句;④ 删除只清槽不清名字格 → 新会话继承旧名字,问候叫错人 · 回归全绿boot_greet 念活清单:新增 all_names 模式,硬件自己读 0x1C0 清单(字0=人数,每条取槽号 [31:29])→ 按槽号读名字格 0x200+槽×6(6 字,同步 SRAM 三态读)→ 按清单顺序拼名字(每场最多 4 个汉字 = 12 字节,名字间补「、」,最多念三场)→ 尾巴诚实:「都在。」或「还有N场。」(中文数字表在芯片里)→ 定长 64 字节 / 16 字落 0x1E0~0x1EF(0x1F0 接话问候不打架);一场名字都没有 → 退回第 57 环单名、再退回第 53 环按场数;all_names=0 与第 57 环逐位一致 · 核不查表、不取号、不拼字 · 新页探针 BG_ALL(0xE60) 与只读 BG_NSPOKE / BG_TOTAL / BG_LEFT,SCREEN34 在 0xE70;会话记忆读口多一个主、写主优先级不变 rn > fs > gp > bg > ss > arc · 硬证据:三场补名后一条 npu.boot → BG_NAMED=1 / BG_BYTES=64 / BG_NSPOKE=3 / BG_TOTAL=3 / BG_LEFT=0,0x1E0~0x1EF 十六字 == 「记一笔、工作那场、家里的事,都在。」;一条 npu.forget 2 删「工作那场」再 boot → 只剩「记一笔、家里的事,都在。」(念活清单,不是旧快照);再开两场并 npu.sess.list、改名 #2「查资料」/#4「跑步计划」→ 清单 4 场只念前三场、BG_LEFT=1,整行 == 「记一笔、查资料、家里的事,还有一场。」 · 单元 tb_boot_greet58(三场全念 / 四场念三场 / 跳过空名 / 无名退回 / 老路 / clear),协同 tb_min_soc58:SCREEN=0xF905FD04 / SCREEN2~33 与 25~57 环逐位一致,SCREEN34=0x03414040 · 钉子:① 分隔符「、」是三字节 UTF-8 E3 80 81,小端 24bit 常量要写 24'h8180E3;② 名字格地址必须用刚从清单条目解出的槽号(等 slot_r 落寄存器再算会读到上一场的名字);③ 单名老路的槽号来自输入端口 slot,不是寄存器 slot_r(否则退回老路读错槽);④ 新开会话后要一条 npu.sess.list 刷新 0x1C0,否则 rename 按旧清单取号 · 回归全绿name_pick 按名字接话引擎:一条 npu.name(无操作数,名字已在 0x268~0x27C)→ 硬件读活清单 0x1C0 → 逐场读名字格 0x200+槽×6(6 字 = 24 字节,同步 SRAM 三态读)→ 24 字节全等比较(半个名字不算:说「工作」不会切到「工作那场」)→ 命中排回执 「好,<名字>,接着聊。」定长 36 字节 / 9 字落 0x230 → 同一条通路把会话号交给开关场引擎(enter)· 核不查表、不比字节、不取号、不拼字、不发第二条指令 · 核升 v21,custom-2 加 funct3=010 npu.name(前 58 圈编码不动);npu.rd 新增 sel=41/42(命中会话号 / {槽,idx,ok,err})· 点名牌与问候同页 0xE80~0xEF0:NP_GO/NP_RST 与只读 NP_OK/ERR/SID/SLOT/IDX/CNT/NCMP/NLEN/BYTES/W0..W8/CORE,SCREEN35=0xEF0;会话记忆写主优先级 np > rn > fs > gp > bg > ss > arc · 硬证据:接着 R58 四场(0xA1/0xA4/0xA5「查资料」/0xA6「跑步计划」)——一条 npu.name 点名「查资料」:NP_OK=1 / NP_SID=0xA5 / NP_NLEN=9 / NP_BYTES=36 / NP_CNT=4,NP_W0..W8 与「好,查资料,接着聊。」逐位相同,SX_CUR 从 0xA6 变成 0xA5;说半个名字「查」→ NP_OK=0 / NP_ERR=1、不换场、0x230 旧回执照旧(屏幕不闪);说清单外的「跑步」→ 同样点不到;说全名「跑步计划」→ NP_SID=0xA6 / NP_NLEN=12,回执逐字对上、SX_CUR 变 0xA6;全程 NP_CORE=0(名字是数据,找名字是硬件)· 单元 tb_name_pick(全名命中/半名不算/命中第三场/不在清单/空名字不扫表/空清单不挂死/可复现)与 tb_riscv_core / tb_rename_sess / tb_boot_greet58 / tb_greet_pick 复跑通过,协同 tb_min_soc59:SCREEN=0xF905FD04 / SCREEN2~34 与 25~58 环逐位一致,SCREEN35=0x02A5A65A · 钉子:① cell 是 Verilog 保留字(config 用),信号名要避开,否则 Verilator 直接报 UNSUPPORTED;② 新引擎的 mem_rdata 忘了接到 sess_rdata → 读回恒 0、表头人数读成 0,一条指令静默变成「空清单」;③ 清单表头/条目的页偏移要按 addr[7:2] 算(NP_CNT 在 0xE9C,不是 0xEA0——0xEA0 是 NCMP);④ 点不到时探针清零是对的(没有新回执),但要另查 0x230 才能证明旧回执没被改 · 回归全绿name_pick 加「没找到」支路:扫完活清单(每条 {槽,sid})→ 逐场读名字格(0x200+槽×6)→ 24 字节仍无一全等时,硬件把 「没找到,是这N场吗?」(N = 活清单场数,中文数字 一/二/三/四)排成 30 字节 UTF-8、空格补到 36 字节 / 9 字,写进与命中回执同一个对话缓冲区 0x230~0x238;answered=1 / ok=0 / err=1,不发换场脉冲、SX_CUR 不动 · 空名字(没说名字)与空清单(没有「这几场」可问)仍只报 err、不回话、不写 0x230——失败有三种,不能混成一种 · 命中路径的回执「好,<名字>,接着聊。」与换场一字未改 · 新只读探针 NP_ANSWERED(0xED4),新观察点 SCREEN36(0xEF4)· 硬证据:R59 四场(0xA1/0xA4/0xA5「查资料」/0xA6「跑步计划」)说清单外的「买菜」→ NP_ANSWERED=1 / NP_ERR=1 / NP_OK=0 / NP_SID=0 / NP_CNT=4 / NP_NCMP=4 / NP_BYTES=36、SX_CUR 仍 0xA6、0x230 九字与「没找到,是四场吗?」逐位相同;半个名字「查」同样回话且不换场(上一环这里是沉默);全名「查资料」照旧命中 0xA5、回执 9 字与 R59 逐位一致;空名字 → answered=0 / ncmp=0、0x230 仍是命中回执;删一场(3 场)再说「买菜」→ 反问变 「没找到,是三场吗?」(场数来自活清单,不是写死);删光 → cnt=0 / answered=0、0x230 保持上一句不闪屏 · 单元 tb_name_pick60 与协同 tb_min_soc60 通过,tb_riscv_core / tb_rename_sess / tb_forget_sess / tb_boot_greet58 / tb_greet_pick / tb_sess_list 与 tb_min_soc58 复跑通过:SCREEN=0xF905FD04 / SCREEN2~35 与 25~59 环逐位一致,SCREEN36=0x0404011E · 钉子:① 探针读的是 NP_W*(0xEAC 起),要证明「0x230 没被改写」必须走归档页窥视口(0x028/0x02C)读内存;② w4 在 0xEBC(0x0B8 是 w3),场数汉字落在 w4 的 byte2;③「三」与「四」只差 w4/w5 各一个字节(0xBA9CE589 vs 0xBA9CE59B);④ 0xA5 在活清单第 2 条不是第 3 条(ncmp=2)· 回归全绿name_pick 在扫表时收名:逐场读名字格(0x200+槽×6)时把首个 0 前的有效字节(≤12 字节)收进片上选项区(最多 3 名 / 42 字节,名字间自动补「、」);一个都不像就回 「没找到:<名1>、<名2>、<名3>等。」,名字没收完加一个「等」字;回执缓冲 9 字 → 15 字(0x230~0x23E,仍不碰 0x240 命名清单);无名清单退回第 60 环原句,空名字 / 空清单仍只报 err、不回话 · 新只读探针 NP_NOPT(0xEF8)、NP_W9~W14(0xED8~0xEEC),新观察点 SCREEN37(0xEFC);ISA 一位没动(还是 npu.name)· 硬证据:四场(0xA1 记一笔 / 0xA5 查资料 / 0xA4 家里的事 / 0xA6 跑步计划)说「买菜」→ NP_NOPT=3 / NP_MORE=1 / NP_BYTES=54 / NP_ANSWERED=1 / NP_SID=0,0x230 与 「没找到:记一笔、查资料、家里的事等。」 逐字相同,SX_CUR 仍 0xA6;接着点刚才念出的第二个名字「查资料」→ 命中 0xA5;删掉「家里的事」再说「买菜」→ 三场名字全念完、没有「等」(51 字节 / more=0);点念出的「跑步计划」→ 命中 0xA6;空名字 / 空清单不回话,0x230 不闪屏;全程 NP_CORE=0 · 单元 tb_name_pick61 与协同 tb_min_soc61 通过,tb_min_soc58 复跑通过:SCREEN=0xF905FD04 / SCREEN2~35 与 25~60 环逐位一致,SCREEN37=0x00030136 · 钉子:① 0xED8 的 addr[7:2] 是 54 不是 22(22 是 0xE58 BG_SLOT,读回会静默串页);② W9~W14 与 W0~W8 之间隔着 NP_CORE/NP_ANSWERED,偏移不能按连续 9 字推;③ 回执变长后 w8 不再固定是 0x20202082(9 字节名字的收尾在 w7),命中回执要按名字长度对表 · 回归全绿name_pick 加「序号」快路:读表头之后、扫名字格之前先认序号,接受 「第N场」「第N」「N」(N = 一..九 或 ASCII 1..9),前缀只认「第」或行首、后缀只认句末的「场」或零/空格填充(所以「第二场会议」这种真名字不会被误判)→ 直接取活清单第 N 条(紧凑序号,槽号可稀疏,与 npu.pick 同口径;不读名字格、不比 24 字节)→ 排 「好,接着第N场。」 24 字节 / 6 字落 0x230 → 同一拍发 enter,把会话号交给开关场引擎 · 越界(四场说「第九场」)不换场、复用第 60 环「没找到,是这N场吗?」(同时置 miss_r,防止 S_WR 写完误 enter);空清单只报 err、不回话;全名命中 / 反问给选项 / 空名字老路逐位不变 · 新只读探针 NP_ORD(0xFEC)、NP_ORDN(0xFF0),新观察点 SCREEN38(0xFF4);ISA 一位没动(还是 npu.name)· 硬证据:四场 0xB1..0xB4(记一笔 / 查资料 / 家里的事 / 跑步计划),说「第二场」→ NP_ORD=1 / NP_ORDN=2 / NP_OK=1 / NP_SID=0xB2 / NP_SLOT=1 / NP_NCMP=0 / NP_BYTES=24 / NP_ANSWERED=1,0x230 与「好,接着第二场。」逐字相同,SX_CUR 切到 0xB2;说「第九场」→ ord1/ordn9/ok0/err1/answered1/ncmp0、36 字节反问、SX_CUR 仍 0xB2(不换场);说全名「查资料」→ ord0 / 命中 0xB2 / ncmp2;说「第二场会议」→ ord0 走比名字路径;全程 NP_CORE=0 · 单元 tb_name_pick62 与协同 tb_min_soc62 通过,tb_name_pick61 / tb_min_soc61 复跑 PASS:SCREEN=0xF905FD04 / SCREEN2~37 与 25~61 环逐位一致,SCREEN38=0x1201B200 · 钉子:① UTF-8 四字常量按字节小端拼(第 = E7 AC AC → 0xE4ACACE7,别把 AC 写成 E7);② 同步 SRAM 读要「发地址 → 空一拍 → 收数据」三态,序号取条目少留一拍会拿到上一条(sid=4);③ 越界只置 answered 不够,S_WR 末尾按 miss_r 决定是否 enter,必须同时置 miss_r;④ 0xFF4 相对 0xE00 是 0x1F4 不是 0x0F4(写成 0x0F4 会静默写到 SCREEN36)· 回归全绿npu.name 认「删掉第N场」「删第N场」(「第」必须有,「场」可省)→ 直取活清单第 N 条(不读名字格、ncmp=0)→ 排 「好,第N场删掉了。」27 字节落 0x230 → 把第 N 条交给删除引擎(fs_start + fs_idx,与 npu.forget 同一条通路,核不发第二条指令)→ 等 free/list 跑完再回 ok,场数用删完的。越界(三场说「删掉第四场」)不删不换场、复用第 60 环「没找到,是这三场吗?」;「删掉第二场会议」这种真名字不算删除;删的正是当前场时 cur_valid 当场归 0 · 新只读探针 NP_DEL(0xF94)、NP_DELDONE(0xFE4),新观察点 SCREEN39(0xFF8);ISA 一位没动 · 硬证据:四场 0xB1..0xB4 说「删掉第二场」→ del1 / ord1 / ordn2 / ok1 / sid0xB2 / slot1 / ncmp0 / 27字节 / DELDONE1 / CNT3,0x230 与「好,第二场删掉了。」逐字相同,SX_STATUS.cur_valid=0;「删掉第四场」→ ok0/err1/answered1/36字节/DELDONE0/CNT 仍 3、一场没删;「删掉第一场」→ 删 0xB1、CNT=2;再说「第二场」= 0xB4、说「家里的事」= 0xB3(ncmp=1)——删除真重排了清单,名字照旧能点;「删掉第二场会议」→ del0 不删 · 单元 tb_name_pick63 与协同 tb_min_soc63 通过:SCREEN=0xF905FD04 / SCREEN2~38 与 25~62 环逐位一致,SCREEN39=0xCA00B203 · 钉子:① 删除出口也在 S_WR 末尾,越界只置 miss_r 不够,必须用 del_r && !miss_r 才不误发 fs_start(否则越界也会删一场);② S_ORDR 的 idx_out 取自 ordn_r,删除支路要同时写 ordn_r,否则 NP_IDX=0;③ SX_STATUS 整字不只有 cur_valid,删当前场后要看 bit0,不能整字比 0;④「删」E588A0、「掉」E68E89 按字节小端拼,「第/场」位置随「掉」在 b6/b9 分两套后缀 · 回归全绿name_pick 在删除之前先读这一场自己的名字格(0x200 + 槽号×6,6 字/24 字节),回执改成 「好,<名字>删掉了。」(6 + 名字字节 + 12,最多 30 字节)落同一个 0x230;名字格空着才退回第 63 环的序号回执 · 新开关 NP_DELNM(0xF9C 写,0=老语义)、只读探针 NP_DELNAME(0xF9C,bit5=named / bit4:0=len)、新观察点 SCREEN40(0xFFC);ISA 一位没动 · 硬证据:剩两场 0xB3 家里的事 / 0xB4 跑步计划,说「删掉第二场」→ 删 0xB4、回「好,跑步计划删掉了。」30 字节、named1/len12/CNT1;接着说「删掉第一场」→ 删 0xB3、回「好,家里的事删掉了。」30 字节、CNT0;两场 w1 = 0xB7E88CBC vs 0xAEE58CBC——没有复用上一场的名字;名字格空着 → named0 / 27 字节 退回序号(单元用例⑧)· 单元 tb_name_pick64 与协同 tb_min_soc64 通过,tb_name_pick63 / tb_min_soc63 复跑 PASS:SCREEN=0xF905FD04 / SCREEN2~39 与 25~63 环逐位一致,SCREEN40=0x800C1E1E · 钉子:① 名字必须在删除引擎 free 槽之前读,删完名字就没了;② 地址不能用本拍才更新的 slot_r,要用条目里的 mem_rdata[31:29](读旧值会读错槽);③ 名字回执长度随名字变(6+nlen+12),不能写死 27;④ 名字格零填充,有效长度取第一个 0 之前(最多 12 字节)· 回归全绿forget_sess 在 free 前把目录项 w1/w2/w3、槽首字、名字 6 字按 13 字写进垃圾记录 0x220(魔术字 "UNDO"),再新增 rtl/undo_del.sv:一条 npu.undo(custom-2 funct3=011,无操作数)读垃圾记录 → 向目录发 restore(cmd=8)把 valid/sid/基址/摘要/w3 原样写回那个槽、used+1 → 名字格 6 字写回 → 槽首字写回 → 清垃圾记录 → list 重排清单。同一场只能撤一次(魔术字清掉,下一场删除覆盖);删除语义(free 只作废目录项、清槽首字)一个字没改 · 硬证据:删第 2 场(0x92/槽2)→ 垃圾记录 13 字逐位对上;一条 undo → 目录项 w0/w1/w2/w3 与删除前逐位一致、used 2→3、名字格/槽首字写回、清单 2→3 且 0x92 回到第 2 条;再撤一次 → ok0/err1 目录一字不动;再删 0x91 再撤 → 垃圾记录被覆盖、撤的是新一场。ISA 测试 tb_isa_undo 程序只有四条指令,一条 npu.undo 端到端跑完、npu.wait 没冻死、SCREEN=0x65 · 单元 tb_undo_del 与 tb_isa_undo 通过;tb_forget_sess / tb_sess_slots / tb_min_soc64 复跑 PASS:SCREEN=0xF905FD04 / SCREEN2~40 与 25~64 环逐位一致 · 钉子:① 槽号字段必须按打包位宽解(slot 在 [23:21],写错成 [20:18] 会撤成槽0);② 同步读必须「发地址→等一拍→收数据」,快照读少一等就拿到上一拍残留;③ cmd=8 要 4bit,3'd8 会截成 0 静默走错分支;④ restored 要进每拍默认块才是单拍脉冲;⑤ 名字回写序号从 wn_i+1 起(wn_i=0 那拍已写 nm0)· 回归全绿undo_del 在 restore + list 成功之后,把删除时留在垃圾记录里的名字 6 字排成 「好,<名字>回来了。」(6+nlen+12,最多 30 字节)落 0x230~0x23E;名字格空着才退回「好,这场回来了。」24 字节;撤回失败一个字不写(不闪屏)· 回执只可能来自垃圾记录:删除后名字格已被 free 清 0,测试台先断言它为 0,再断言回执念出名字 · 新探针页 0x980(UD_BUSY/DONE/OK/ERR/SID/SLOT/CNT/BYTES/NAME/RESTORED),UD_NAME bit5=named / bit4:0=nlen,0x984 SCREEN41;ISA 一位没动(仍 npu.undo)· 硬证据:删 0x92/槽2(跑步计划)→ undo → bytes=30 / named=1 / nlen=12,0x230~0x237 与「好,跑步计划回来了。」逐位相同,0x23F/0x240 没碰;删 0x91/槽0(无名)→ undo → bytes=24 / named=0 / nlen=0,「好,这场回来了。」;再撤 → ok0/err1,回执缓冲区原样不动。ISA 协同 tb_isa_undo66:npu.forget → npu.wait → npu.undo → npu.wait,MMIO 读回 UD_BYTES=30 / UD_NAME=0x2C,SCREEN=0x66,两次 npu.wait 都没冻死 · 单元 tb_undo_del66 与 tb_isa_undo66 通过;tb_undo_del / tb_isa_undo / tb_min_soc64 复跑 PASS:SCREEN=0xF905FD04 与 25~65 环逐位一致 · 钉子:① 名字长度按字节取第一个 0 之前,0x9288E5A1 少写一个 A1 就会让 nlen 变 11、回执整段错位;② 回执 15 字正好写到 0x23E,多写一字就撞 0x23F/0x240;③ 失败分支不能顺手清回执探针——bytes_out 只在成功时更新,屏幕才不闪 · 回归全绿rtl/resume_link.sv 续聊接力引擎:只认撤回引擎的 restored 单拍脉冲 → 等撤回收尾(回执写完)→ 向会话开关场引擎发一条 enter(cmd=1,会话号取撤回解出的那一场)→ 收下 mem_ok/words/cur_sid/cur_valid。进场走的是与 npu.sess enter 完全同一条通路、同一份归档格式(目录查槽 → 归档 K/V 按时间序追加回环 → 当前指针接上),不另造加载逻辑;busy 在 restored 那拍就拉高,覆盖「等撤回 + 进场」整段,npu.wait 中间看不到缝;撤回失败一条 enter 都不发,空场也允许进场但 mem_ok=0 如实报出 · 新探针接在 0x980 撤回页后:UD_RESUME(0x9AC) / UD_MEMOK(0x9B0) / UD_KVW(0x9B4) / UD_CUR(0x9B8) / SCREEN42(0x9BC) / UD_RLERR(0x9C0) / UD_CURV(0x9C4);ISA 一位没动(仍 npu.undo)· 硬证据:三场 0x91/0x92/0x93,0x92 在槽 2、基址 0x80,存着合法 4 条 K + 4 条 V(表头 SALT / 摘要 / 16 字载荷);npu.forget 2 删掉 → npu.undo → UD_MEMOK=1 / UD_KVW=16 / UD_CUR=0x0092、当前场有效;CONV_KVTOT=0x0404(层0|层1 各 4 条,旧上下文真回到片内环);再跑一步 npu.step token=1 layers=2 → CONV_KVTOT=0x0505(下一句接着旧上下文长,不是从 0 开始);归档表头首字写回 SALT、目录项写活、SCREEN=0x67,两次 npu.wait 没冻死 · 单元 tb_resume_link(成功只发一条 enter / 撤回失败不进场 / 空场 mem_ok=0 不假报 / clear 归零)+ 协同 tb_isa_undo67;tb_undo_del66 / tb_undo_del / tb_isa_undo66 / tb_isa_undo / tb_min_soc64 复跑 PASS:SCREEN=0xF905FD04 与 25~66 环逐位一致 · 钉子:① 触发只认 restored 脉冲,认 done 高电平会一次撤回反复接力;② busy 不能等进场才拉高——restored 与开关场启动之间有一拍缝,npu.wait 会在这拍误判空闲;③ enter 的会话号要用撤回引擎解出的 ud_sid,不是垃圾记录原文;④ 空上下文不是错误,但 mem_ok 必须由开关场引擎报、不能软件写死 1 · 回归全绿rtl/resume_speak.sv 先开口引擎:只认续聊继电器的 done 且 ok=1 且 mem_ok=1(真读回旧上下文才开口,空场/恢复失败一个字不说)→ 从刚恢复的这段记忆里取话头(归档摘要低位当起始 token,换一场就变)→ 同一拍把采样器种子装成 0xA5(同一场接回两次说同一句话)→ 自己发一段整段生成(与 npu.generate 同一台引擎、同一份 step/整网/采样通路,不另造解码)→ 一句生成完才落 done;done 保持高电平期间不重复开口,下一次撤回才说第二句 · 忙闲没有缝:resume_link 落忙那一拍 done 才刚为高,npu_busy 加一个组合 pending(接回成功但还没开始开口的那一拍)与 rs_busy 一起把忙闲首尾接上 · 新只读探针页 0xA5C 起:SCREEN43 / RS_SPOKE / RS_SID / RS_SEQ / RS_N / RS_LAST / RS_BUSY / RS_DONE / RS_SEED / RS_CORE / RS_RST / RS_PROMPT;前 67 圈地址映射一字不改,ISA 一位没动(仍 npu.undo)· 硬证据:单元 tb_resume_speak 五用例(空场不开口 / 真接回自己开口 / prompt 取摘要低位 / 同拍 seed=0xA5 / done 高不重复 / 下次接回换话头 / pending 补缝 / clear 归零);ISA 协同 tb_isa_resume68 两场(0x92 摘要 76CA6F0E 低 2 位=2,0x93 摘要 85955090 低 2 位=0)各自 npu.forget → npu.undo → npu.wait → 两场都 spoke=1、RS_CORE=0(核一个生成入口都没写、一条 step/generate 都没发)、话头 A_PROMPT=2 / B_PROMPT=0、第一场说完 CONV_KVTOT=0x0808(接回 4|4,先开口每一步接着旧上下文长)、SCREEN=0x68,两次 npu.wait 没冻死 · 契约更新:tb_isa_undo67 的「恢复后 4|4、再一步 5|5」改为「恢复 4|4 + 先开口 1 步 = 5|5、再一步 6|6」(先开口是新的默认行为);tb_resume_link / tb_isa_undo66 / tb_min_soc64 复跑 PASS,主通路 SCREEN=0xF905FD04 与 25~67 环逐位一致 · 钉子:① 只认 done 且必须 ok && mem_ok——空场也开口会假装记得;② 话头取归档摘要(载荷指纹),不能取软件塞的常数;③ 单拍 gen_start/seed_we 必须进每拍默认块;④ resume_link 落忙与 done 之间有一拍缝,必须用组合 pending 补,不能只把 rs_busy 并进 npu_busy;⑤ 词表/权重为零时生成会退化成全 0 token,必须在协同程序里装回第 27~44 圈那一份嵌入/LM/投影/FFN/LN 权重 · 回归全绿resume_done && ok && mem_ok)后,resume_speak 按会话开关场的 cur_slot 发 6 拍同步读,取名字格 0x200 + 槽×6(6 字 = 24 字节 UTF-8;发地址 → 空一拍 → 收数据)→ 名字长度由硬件数(遇 0 截止、最多 24 字节),后缀 「,咱们接着聊。」(7 汉字 = 21 字节)紧接最后一个名字字节,不留 24 字节空洞 → 定长 48 字节 / 12 字,连写 12 拍落 0x1E0(与开机问候同一个对话窗口,新的覆盖旧的)→ 落屏之后才发同一台整段生成引擎(与 npu.generate 完全同一条通路)· 名字格为空不硬塞称呼:退回「咱们接着聊。」18 字节,named=0 如实报;忙闲覆盖「读名字 → 拼句 → 落屏 → 整段生成」整段,pending 仍补接回落忙那一拍;每次接回只说一次 · 新探针接在第 68 环页后:RS_NAMED(0xA8C) / RS_BYTES(0xA90) / RS_SLOT(0xA94) / RS_LINE0..11(0xA98~0xAC4),屏摘要 SCREEN44(0xAD0) / SCREEN45(0xAD4);前 68 圈地址映射一字不改,ISA 一位没动(仍 npu.undo);会话记忆区第七个写主 rsp > ud > np > rn > fs > gp > bg > ss > arc · 硬证据:单元 tb_resume_speak69(真同步 SRAM 模型)——空场不读名字/不落屏/不开口;槽 2「跑步计划」→ named=1 / bytes=33 / slot=2,0x1E0 十二字与「跑步计划,咱们接着聊。」逐位相同;槽 1 空名字 → 18 字节 / named=0;槽 0「工作」→ 27 字节,后缀紧接第 6 字节无空洞;done 高不重复、clear 归零 · ISA 协同 tb_isa_resume69(真整颗 SoC):0x92 槽 2「跑步计划」/ 0x93 槽 1「工作」,各走 npu.forget → npu.undo → npu.wait → 两场 spoke=1、RS_CORE=0(核一个生成入口都没写)、SCREEN44=0x243(槽 2/33 字节/named1)、SCREEN45=0x137(槽 1/27 字节/named1)、第一次 spoke 快照 0x1E0=「跑步计划,咱们接着聊。」、最终 0x1E0=「工作,咱们接着聊。」、话头 A_PROMPT=2 / B_PROMPT=0、SCREEN=0x69,两次 npu.wait 没冻死 · 回归:tb_isa_resume68 / tb_isa_undo67 / tb_min_soc64 复跑 PASS,主通路 SCREEN=0xF905FD04 与 25~68 环逐位一致;第 68 环单元旧时序断言已被本环取代,留作历史快照 · 钉子:① 名字格读要三态(发地址→空一拍→收数据),少留一拍会把上一条的字当名字;② 后缀是 21 字节(7 汉字)不是 18,少算一个「,」整句错位;③ 名字短必须让后缀紧接最后字节,按 24 字节定长拼会中间冒出 0;④ 探针地址占到 offset 51,屏摘要必须放 52/53(0xAD0/0xAD4),放 0xAC8 会被第 68 环的「核动过手」计数误判;⑤ 单拍 gen_start/seed_we/mem_wen 必须进每拍默认块 · 回归全绿OWNER_BASE=0x280+槽:rename_sess 写完名字格多写一拍归属=sid;forget_sess 清名字前先读归属,把名字 6 字 + 归属一起拍进垃圾记录(13→14 字),删完清归属;undo_del 多读一字归属、名字写回后原样写回归属;resume_speak 读完名字格再读归属,归属 == 这次接回的 sid 才叫名字,对不上就清零名字寄存器、退回「咱们接着聊。」18 字节(named=0 / bind=0)· 新只读探针 RS_OWNER(0xAD8) / RS_BIND(0xADC),屏摘要 SCREEN46(0xAE0);前 69 圈地址映射一字不改,ISA 一位没动(仍 npu.undo) · 硬证据:单元 tb_resume_speak70 —— 槽 2「跑步计划」归属 0x92 → 33 字节/ bind1;槽 3 名字格残留「工作」、归属 0x91,接回 0x93 → 18 字节 / bind0,屏幕是「咱们接着聊。」不是「工作,咱们接着聊。」;槽 0 归属 0x94 → 27 字节/bind1;clear 归零 · ISA 协同 tb_isa_resume70(真整颗 SoC):0x92 槽 2 归属 0x92 / 0x93 槽 3 归属 0x91,各走 npu.forget → npu.undo → npu.wait → 两场 spoke=1、RS_CORE=0、SCREEN44=0x324(槽 3/18 字节/无名)、SCREEN45=0x243(槽 2/33 字节/有名)、SCREEN46=0x01920091({A_bind,A_owner,B_bind,B_owner})、第一次 spoke 快照 0x1E0=「咱们接着聊。」、最终 0x1E0=「跑步计划,咱们接着聊。」、SCREEN=0x70,两次 npu.wait 没冻死 · 回归:tb_resume_speak69 / tb_isa_resume69 / tb_isa_resume68 / tb_isa_undo67 / tb_isa_undo66 / tb_undo_del66 / tb_rename_sess / tb_forget_sess 复跑 PASS;tb_min_soc64 主通路 SCREEN=0xF905FD04 与 25~69 环逐位一致 · 钉子:① 名字格按槽住,光看格子会叫错人,必须验归属;② 删除快照要把归属一起拍,撤回不能伪造归属;③ 归属读也要三态(发地址→空一拍→收数据);④ 对不上时清名字寄存器,不是只把 named 拉低;⑤ 单拍写归属/清归属要进每拍默认块boot_greet 的念清单路径在念名字之前先读名字归属 0x280+槽,与清单条目的 sid 比对:对上才念(nver++),对不上跳过(nskip++),不补「、」、不计入 nspoken;一个名字都没验过就退回按场数问候 · 新只读探针 BG_NVER(0xE74) / BG_NSKIP(0xE70),屏摘要 SCREEN47(0xAE4);前 70 圈地址映射一字不改,ISA 一位没动(仍 npu.boot) · 硬证据:单元 tb_boot_greet71 —— 三场归属全对 → 念全(ver3/skip0);中间一场归属 0xAA ≠ 0xA2 → 跳过,念「记一笔、家里的事,还有一场。」(ver2/skip1/left1);三场全对不上 → 退回「你有三场没聊完。」(ver0/skip3);all_names=0 单名老路逐位不变;clear 归零 · ISA 协同 tb_isa_greet71(真整颗 SoC):目录三场 0xA1/0xA2/0xA3,槽 1「工作那场」归属 0xAA,开机 BG_ALL=1 + 一条 npu.boot → 0x1E0=「记一笔、家里的事,还有一场。」64 字节、BG_NVER=2 / BG_NSKIP=1 / NSPOKE=2 / TOTAL=3 / LEFT=1 / NAMED=1、SCREEN47=0x00122140、SCREEN=0x71,npu.wait 没冻死 · 回归:tb_boot_greet / tb_boot_greet58 / tb_boot_greet71 / tb_min_soc58 / tb_min_soc64 / tb_isa_resume69 / tb_isa_resume70 / tb_resume_speak70 共 8 项 PASS,主通路 SCREEN=0xF905FD04 与 25~70 环逐位一致 · 钉子:① 名字格只说明写过什么,归属才说明属于谁;② 跳过时不能补「、」、不能算 nspoken;③ 归属读同样要三态(发地址→空一拍→收数据);④ 一个名字都没验过要诚实退回按场数,不能念一句空话;⑤ 单拍 mem_ren 必须进每拍默认块all_names=0 的单名问候仍按槽直接读名字格——恢复的那一场也可能是槽复用过的。本环把同一条归属校验搬到单名入口:读名字格之前先读 0x280+槽,与这次恢复的 sid 比对,对上才念名字;对不上就不叫,退回第 53 环「你有三场没聊完。」24 字节,sbind=0 / nskip=1 如实报;sid=0(新开一场)也天然对不上任何归属,不继承旧名字 · 新只读探针 BG_SBIND(0xE7C,低1位=对上、高16位=读到的归属);顺带修掉第 71 环的地址撞名:0xE70 一直归 SCREEN34,BG_NSKIP 挪到 0xE78;屏摘要 SCREEN48(0xAE8);前 71 圈地址映射一字不改,ISA 一位没动(仍 npu.boot) · 硬证据:单元 tb_boot_greet72 —— 槽2「查资料」归属 0xA3 →「查资料,接着聊吧。」48 字节逐位、sbind=1 / ver=1;归属残留 0xAA → 退回「你有三场没聊完。」,「查资料」一个字不出现(sbind=0 / skip=1);sid=0 遇残留归属也不叫;all_names=1 老路仍 ver2/skip1;clear 归零 · ISA 协同 tb_isa_greet72(真整颗 SoC):目录三场,最后一场 0xA3 住 slot2,名字格「查资料」但归属 0xAA;BG_ALL=0 + 一条 npu.boot → 0x1E0=「你有三场没聊完。」24 字节、0x1E1=「天」、BG_NAMED=0 / NVER=0 / NSKIP=1 / SBIND=0 / SOWNER=0x00AA / BYTES=24、SCREEN48=0x00100018、SCREEN=0x72 · 回归:tb_boot_greet / 58 / 71 / 72 / tb_isa_greet71 / tb_min_soc64 全 PASS,主通路 SCREEN=0xF905FD04 与 25~71 环逐位一致 · 钉子:① 单名路径也要三态读归属,不能直接读名字格;② 对不上要退回按场数那句,不是念半句;③ sid=0 不能拿老名字当自己的;④ 0xE70 已被 SCREEN34 占用,新探针必须另找空位;⑤ 探针改成 0xE78 后,第 71 环 ISA 程序要同步改读地址boot_greet 在归属比对通过后看 cnt——cnt>1 → squeue=1 / rem=cnt-1,名字后接「,还有N场在等你。」27 字节;cnt==1 仍走第 57 环「,接着聊吧。」18 字节,老句一字不改;归属对不上仍退回按场数,rem/squeue 归零 · 数字按中文量词写(独立 qnum_byte,第二场是「两场」不是「二场」);名字最长 24 字节 + 27 字节尾巴超过 48,带余场改走第 58 环 64 字节通路(补空格→打包 16 字→连写 16 拍落 0x1E0)· 新探针 rem[3:0]/squeue,屏摘要 SCREEN49(0xAEC)=rem<<20|squeue<<16|sbind<<15|named<<14|bytes;前 72 圈地址映射一字不改,ISA 一位没动(仍 npu.boot) · 硬证据:单元 tb_boot_greet73 六用例——cnt=3→「查资料,还有两场在等你。」64 字节/16 字逐位(rem2/queue1/bind1/ver1);cnt=1→老句 48 字节(rem0/queue0);cnt=2→「还有一场」rem1;cnt=6→「还有五场」rem5;归属残留 0xAA→退回「你有三场没聊完。」24 字节、rem0/queue0、旧名字不出现;clear 归零 · ISA 协同 tb_isa_greet73(真整颗 SoC):目录三场,最后一场 0xA3 住 slot2,名字格「查资料」归属 0x282=0xA3;BG_ALL=0 + 一条 npu.boot → 0x1E0=「查资料,还有两场在等你。」64 字节逐位、BG_NAMED=1 / BYTES=64 / SBIND=1 / SQUEUE=1 / REM=2、SCREEN49=0x0021C040、SCREEN=0x73,旧尾巴「接着聊吧」不出现,npu.wait 没冻死 · 回归:tb_boot_greet / 58 / 71 / 72 / tb_isa_greet72 全 PASS;第 57 环老句的单名用例按新契约把场数设为 1;tb_min_soc64 的 R57 快照同步为带余场整行,复跑 PASS,主通路 SCREEN=0xF905FD04 与 25~72 环逐位一致 · 钉子:① 量词要用「两」不是「二」;② 27 字节尾巴加 24 字节名字装不进 48,必须切 64 字节通路;③ 只有一场时老句不能推翻;④ 归属对不上时余场探针也要归零;⑤ clear 要连 rem/squeue 一起清rem_pick 引擎与 npu.rem(custom-2 funct3=100,0x405B):一条指令读活清单 0x1C0,跳过开关场引擎记着的当前 sid,逐场读名字归属 0x280+槽 验明正身,再读名字格 0x200+槽×6,每场最多取 4 个汉字,按清单顺序排「还有甲、乙、丙等。」;最多念三场,后面还有场就补一个「等」字;整行补空格到 64 字节落 0x1E0(与开机问候同一窗口)· 念不出名字也不沉默:除当前场外没有别的场 →「没有别的场了。」21 字节;有余场但名字没验过/是空格 →「还有N场,没起名字。」30 字节,两者 ok=0/err=1 仍写满整行 · 起句「还有」6 字节、名字间「、」、尾巴「等。」逐字节排;当前场一个字不出现 · 新探针 SCREEN50(0xAF8) 与只读 RP_DIGEST(0xAFC)={nspoken,more,nskip,nself,ntotal,bytes};前 73 圈编码/地址一位不动 · 硬证据:单元 tb_rem_pick74 四用例——五场当前 0xA3 →「还有记一笔、工作那场、跑步计划等。」51 字节+13 空格(spoken3/more1/skip0/self1/others4/left1);四场当前 0xA1 且 slot1 归属残留 0x99 → 跳过「工作那场」,只念「还有查资料、跑步计划。」(spoken2/skip1);只有一场→「没有别的场了。」;三场名字全 0→「还有两场,没起名字。」;clear 全归零 · ISA 协同 tb_isa_greet74(真整颗 SoC,五场目录):npu.boot 恢复 0xA3 落「查资料,还有四场在等你。」→ 一条 npu.rem → 0x1E0=「还有记一笔、工作那场、跑步计划等。」、SCREEN50=0x00701540、SCREEN=0x74,当前场「查资料」整行不出现,第四场只贡献「等」字,npu.wait 没冻死 · 回归 tb_isa_greet73 / tb_riscv_core / tb_min_soc64 全 PASS,主通路 0xF905FD04 与 25~73 环逐位一致 · 钉子:①「还有」起句必须真写进第 0~5 字节,只写名字会从「记一笔」开始;②「等」要扫完清单确认后面还有场才加;③ nothers 必须整表数完,不能念到三场就停;④ 归属/名字格都要三态读(发地址→空一拍→收数据);⑤ 当前 sid 取自开关场引擎,不从操作数猜rem_pick 记住翻页:16 位 pg_mask 按清单条目号标记「已念过 / 已判定不可念」,cont=1 从上次停下的地方续页;第二页起句首「接着还有」,一页仍最多三个名字,页末按 pg_left 补「等。」或「。」;续页已无余场 →「都念完了。」15 字节,不重复任何名字 · 新跨页探针 pg_page/pg_left/pg_tspoke/contused/finished;新问句或换了当前场都从第 0 页重来 · 新 ISA npu.rem.next(custom-2 funct3=101,0x0000505B),新页 0xB00:RP_NEXT/RP_RST2/SCREEN51 + 只读 RP_PAGE/RP_LEFT/RP_TSPOKE/RP_CONTUSED/RP_FINISHED,前 74 圈编码与地址映射一位不动 · 硬证据:单元 tb_rem_pick75 八场当前 0xA1 → 第1页「还有工作那场、查资料、跑步计划等。」(page0/left4/tspoke3)→ 第2页「接着还有读论文、家里的事、学英语等。」(page1/left1/tspoke6)→ 第3页「接着还有买机票。」(page2/left0/tspoke7/finished1)→ 再续「都念完了。」;新问句清零重来;cont=1 但当前场换 0xBB 也重来;一个归属残留 + 一个空名字被标为已处理,页末不误加「等」 · ISA 协同 tb_isa_greet75(真整颗 SoC,五场当前 0xA3):npu.boot 恢复 0xA3 → 一条 npu.rem 第一页 51 字节、SCREEN50=0x00701540 → 一条 npu.rem.next「接着还有读论文。」(page1/left0/tspoke4/cont1/fin1)→ 再一条「都念完了。」15 字节、SCREEN51=0x104C4130、SCREEN=0x75,npu.wait 没冻死 · 回归 tb_rem_pick74 / tb_isa_greet74 / tb_isa_greet73 / tb_riscv_core / tb_boot_greet73 全 PASS,主通路 0xF905FD04 与 25~74 环逐位一致 · 钉子:① 位图按条目号不按槽(槽会复用);② pg_left 只数未处理余场,沿用 nothers-nspoken 会让翻页永远翻不完;③ 尾巴「等」看 pg_left!=0,不是「念满三场」;④ 续页换当前场必须当新问句;⑤ 新页 0xB00 要先于 0x900 分支译码(靠 bit9 区分);⑥ 写 SCREEN50 偏移是 8(a6),4(a6) 是 RP_DONE;⑦ MMIO 脉冲必须每拍默认拉低rem_pick 新增 num_mode,第 0 页「第一个记一笔,第二个工作那场等。」、第 1 页「接着第三个跑步计划,第四个读论文。」;序号是余场序列里的位置(跳过当前场、跳过念不出名字的场,跨页连续编号),带序号一页两位(64 字节窗口的诚实约束)· 每页收口把余场序号表写进 0x2A0:字0=念出的场数、字1..6={5'b0,槽号[2:0],8'b0,会话号[15:0]},按念出的顺序排——念的是谁、点的是谁,同一张表 · name_pick 新增「第N个」识别(个=E4B8AA,必须带「第」):读 0x2A0 取第 N 位 → 回执「好,接着第N个。」24 字节落 0x230 → 同一条 enter 换场;「第N场」仍是活清单序号,两个序号各有权威、不混用(混了会点错人、删错场)· 新写口 0xB20 RP_NUM、只读 0xB24 RP_ORDCNT/0xB28 NP_PER + 0xB30 SCREEN52,前 75 圈地址映射一字不改,ISA 一位没动 · 硬证据:单元 tb_rem_pick76 五场当前 0xA1 → 首页 48 字节、pg_left=2/tspoke=2、表 2 条(0xA2/0xA3)→ 续页 51 字节、left=0/tspoke=4/finished、表 4 条;num_mode=0 首页仍三位、不发表;clear 归零 · ISA 协同 tb_isa_greet76(真整颗 SoC,五场当前 0xA3):RP_NUM=1 → npu.boot 恢复 0xA3 → npu.rem 首页 digest=0x00500540/ORDCNT=2 → npu.rem.next 第二页 51 字节/ORDCNT=4 → 说「第四个」→ NP_PER=1/SX_CUR=0xA5(余场第 4 位,不是清单第 4 条 0xA4)→ 说「第二个」→ SX_CUR=0xA2、0x230=「好,接着第二个。」逐字、SCREEN52=0x04C0A5A2、SCREEN=0x76,npu.wait 没冻死 · 回归 tb_rem_pick74/75、tb_isa_greet74/75、tb_min_soc64、tb_boot_greet73、tb_riscv_core 全 PASS,主通路 0xF905FD04 与 25~75 环逐位一致 · 钉子:① 序号基数已有 tspoke_r,不能再叠本页 nspoken(会数成 1、3、5);②「第二个」的二是 E4BA8C,不能复用「还有两场」的「两」E4B8A4,序数另开 ord_byte;③ 序号表槽号在字 [26:24],按 [18:16] 读恒 0;④「第N个」分支排在 ord_valid 之前,但 ord_valid 的句末条件不会误吃「个」;⑤ 测试台等 done 前要确认 start 已被吃进(done 是电平);⑥ 0xB20 与 0xAF0 同页译码,靠 offset 8 区分name_pick 在「第N个」支路取到号之后多走两步:先读名字归属表 0x280+槽 确认名字属于这个会话号,再读名字格 0x200+槽×6,把回执升级成「好,<名字>,接着聊。」(6+名字+15,与第 59 环全名命中逐字节同一格式)落同一个 0x230,写完再走同一条 enter 换场 · 宁可报序号,绝不叫错人:归属对不上(槽复用旧名字)或名字格为空 → 退回第 76 环的「好,接着第N个。」24 字节,换场照常 · 新开关 0xE88 NP_ORDNM(默认 0,老语义逐位不变)、新只读 0xB2C NP_ORDNAME={named,nlen}、新观察点 0xB34 SCREEN53 · 硬证据:单元 tb_name_pick77 四用例——「第二个」→「好,工作那场,接着聊。」33 字节、named=1/nlen=12、换场 0xA2;「第一个」遇 slot0 归属 0xB7 → 不念旧名字,退回「好,接着第一个。」24 字节、换场 0xA1;开关关掉仍是「好,接着第二个。」;名字格空 → 退回「好,接着第四个。」 · ISA 协同 tb_isa_greet77(真整颗 SoC,四场目录):NP_ORDNM=1 → 说「第二个」→ 回执逐字 33 字节、NP_ORDNAME=0x20、SX_CUR=0xA2 → 说「第一个」→ 归属不符退回「好,接着第一个。」、NP_ORDNAME=0、SX_CUR=0xA1,SCREEN53=0x010CA2A1、SCREEN=0x77 · 回归 tb_isa_greet76(SCREEN52 与回执逐位不变)、tb_name_pick64、tb_isa_greet75 全 PASS · 钉子:① 新状态 30~34 写进 [4:0] 的 st 会回绕成 0~2(仿真表现为刚进名字格就跳回 IDLE),状态机与常量一起加宽到 [5:0];② 归属按槽、名字按槽×6,两个公式不能混;③ 槽号取自序号表字 [26:24];④ 必须先验归属再读名字,否则槽复用会念上一场的名字;⑤ 字节数随名字长度变,落盘仍整 15 字补空格;⑥ 开关默认 0,老程序不写 0xE88 就仍是老回执pick_speak:只看 npu.name 的 done 电平 + per/ord 标志(answered 必真、删除路径不开口),锁下被点的会话号 → 等点选已经发过的那条 enter 真跑完(见 busy 起、再见 busy 落,绝不把上一轮 done 当本轮)→ 只有 ok && mem_ok 才发一段整段生成:话头取刚恢复那场的归档摘要低位、层数 2、种子固定 0xA5(与第 68 环同一套契约),核一个生成入口都不写 · consumed 保证同一场只开口一次,pending 补忙闲首尾的那一拍 · 新开关 0xB38 NP_PKSPK(默认 0,老语义逐位不变)、新探针 0xB3C PKS_SPOKE / 0xB40 PKS_SID / 0xB44 PKS_SEQ / 0xB48 PKS_N / 0xB50 PKS_PROMPT / 0xB54 PKS_CORE、0xB58 PKS_RST、0xB64 SCREEN54;前 77 圈地址映射与 ISA 一位没动 · 硬证据:单元 tb_pick_speak 五用例(序号点选→等进场→摘要低位当话头→单拍 gen_start/seed_we→spoke=1;全名点选不动作;空场 done=1/spoke=0;删除不动作;clear 不重复开口);ISA 协同 tb_isa_greet78(真整颗 SoC,四场目录 + 0x1C0 活清单 + 0x2A0 余场序号表 + slot1=0xA2 归档 4 条 K/V)→ NP_PKSPK=1 + NP_ORDNM=1 + 说「第二个」→ 一条 npu.name 里:换场 0xA2、回执「好,工作那场,接着聊。」、硬件接一句 4 步 seq=0x22、末 token 2、话头 2、PKS_CORE=0、npu.wait 等到这一句说完才返回 → 清探针后说「查资料」全名点选照旧换场 0xA3 但 spoke=0,SCREEN54=0x84A28022、SCREEN=0x78 · 回归 name_pick77 / rem_pick76 / resume_speak70 / isa_greet77 / isa_resume68 全 PASS · 钉子:① pks_ 前缀(第54环已占用 pk_*,重复声明);② 探针复位时若 pick_done 还举着,consumed 必须置 1;③ 目录项 0x190 与活清单 0x1C0 是两张表,格式混用会让 valid=0、整场恢复失败;④ 归档摘要会重算比对,载荷换一个数就 mismatch→ok=0→静默不开口;⑤ gen_run 的 start/prompt/layers 与 sampler seed 四处都要接新来路pick_speak 的触发从「序号点选」拓宽到「任何一次真点中」:新增 en_any 开关(0xB6C NP_ANYSPK,默认 0 = 第78环语义),sel_hit=ok&&answered&&!del&&(per||ord) 始终开口,full_hit=ok&&answered&&!del&&!per&&!ord 只在开关打开时开口;删除路永远不开口、叫错名字(ok=0)不开口;新增 kind_out 探针(0xB70 PKS_KIND,1=序号、2=全名),让「全名真走了同一条路」可被读出 · 生成契约与第78环一字不差:等 name_pick 已发的那条 enter 真跑完(busy 起/落两段式)→ 只有 ok&&mem_ok 才开口 → 话头取恢复的归档摘要低位、2层、种子0xA5、核一个生成入口都不写 · 硬证据:单元 tb_pick_speak79 六用例(en_any=0 全名不动作;en_any=1 全名→等进场→摘要低位2当话头→单拍 gen_start/seed_we→spoke=1/kind=2/sid=0xA4/n=3;序号路仍开口 kind=1;删除不动作;空场 done=1/spoke=0/err=0;clear 不重复);ISA 协同 tb_isa_greet79(真整颗 SoC,四场目录 + slot2=0xA3 也有一份 4 条 K/V 归档)→ NP_PKSPK=1 + NP_ANYSPK=1 + NP_ORDNM=1 + 「查资料」→ 一条 npu.name:换场 0xA3、回执「好,查资料,接着聊。」、硬件自己说一句 4 步 seq=0x22/末 token 2/话头 2/PKS_CORE=0/kind=2 → 清探针再说「第二个」→ 序号路仍开口 sid=0xA2/kind=1,SCREEN55=0xC4A38022、SCREEN=0x79 · 回归 pick_speak79 / pick_speak / name_pick77 / rem_pick76 / resume_speak70 / isa_resume68 / isa_greet77 / isa_greet78 / isa_greet79 / min_soc64 十项全 PASS · 钉子:① 第一次回执会被第二次开口覆盖,测试台须加 rc_captured 只记第一次;② 全名路同样要等 busy 起/落两段式;③ 被点会话必须有真归档,空场 done=1/spoke=0 不假装记得;④ 触发条件必须排除 del;⑤ 单拍 gen_start/seed_we 仍要进每拍默认块;⑥ kind_out 要在认下点选那一拍锁存