// retroDE_ps2 — tb_ee_core_bios_smoke
//
// Sixth EE-core chapter. EE-side mirror of tb_iop_core_bios_smoke. Boots
// the EE core from 0xBFC0_0000 through the shared BIOS ROM path, with
// `ee_core_stub`'s STRICT_UNSUPPORTED mode enabled so the first
// unsupported opcode the BIOS hits halts the core loudly instead of
// silently retiring as a NOP. trap_o / trap_pc_o / trap_instr_o plus
// the retire trace then tell us exactly which opcode the core needs
// to grow next.
//
// The committed TB runs a synthetic BIOS-ish program whose last real
// instruction is deliberately one the core does NOT decode — `BREAK`
// (SPECIAL func 0x0D). BREAK is the MIPS-standard software-trap
// instruction; real BIOS code does not emit it during normal
// execution and it is very unlikely to land in any near-future
// opcode-growth chapter, so it is a stable sentinel for the CI gate.
// (The original sentinel was AND; once AND was added to the core in
// chapter 7 iteration 4 the synthetic program silently stopped
// tripping the trap and the bench timed out — see
// project_ee_core.md for the bench-drift story.)
//
// Swapping in a real BIOS dump:
//   Pass `+BIOS=<hexpath>` on the vvp command line. The TB uses
//   `$readmemh` to populate `u_bios.mem` from the hex file and skips
//   the synthetic preload. Strict pc/instr assertions relax to
//   "something tripped trap_o", so the run is useful iteration bait
//   regardless of which opcode the real BIOS hits first.
//   No Sony BIOS is shipped with the repository
//   (see third_party/LICENSING.md).
//
// BIOS layout (synthetic CI mode — TB preloads via u_bios.mem):
//   0xBFC0_0000   j    0xBFC0_0100
//   0xBFC0_0004   nop (delay slot)
//   0xBFC0_0008..0xBFC0_00FF   default NOP sled (bios_rom_stub fixture)
//   0xBFC0_0100   lui   $1, 0x1000
//   0xBFC0_0104   ori   $1, $1, 0xA000        ; DMAC ch2 base
//   0xBFC0_0108   ori   $2, $0, 0x0200         ; payload-ish base
//   0xBFC0_010C   ori   $3, $0, 0x0004
//   0xBFC0_0110   ori   $4, $0, 0x0001
//   0xBFC0_0114   addiu $6, $1, 0x0040         ; DONE_COUNT addr (unused)
//   0xBFC0_0118   sw    $2, 0x10($1)           ; program MADR (ch2)
//   0xBFC0_011C   sw    $3, 0x20($1)           ; program QWC (ch2)
//   0xBFC0_0120   break                          ; UNSUPPORTED → strict trap
//
// (CHCR is never written, so no DMA actually kicks off; the two SWs
// are there to prove the EE_DMAC write path is still alive even in
// strict-trap teardown.)
//
// Acceptance (synthetic CI mode):
//   - halt_o asserts exactly once
//   - trap_o asserts; trap_pc_o matches the PC of BREAK
//   - trap_instr_o equals the BREAK encoding (0x0000000D)
//   - exactly 11 retires total: reset J + delay NOP + 8 setup + BREAK
//   - the halt retire has flag bit 7 (trap)
//   - no exception vectoring
//   - no DMA_DONE (CHCR never written)
//
// Acceptance (real-BIOS mode, +BIOS=...):
//   - trap_o asserts exactly once, but trap_pc/trap_instr are
//     whatever the image happens to hit first — the TB reports them
//     via the output line so the iteration loop can add opcodes
//     without a separate disassembler.

`timescale 1ns/1ps

module tb_ee_core_bios_smoke;

    // RAM sized for real-BIOS iteration. The synthetic CI mode only
    // uses the first few qwords (bridge preload of payload, then SB
    // writes that never trip CHCR), but the real BIOS copies a large
    // chunk of itself into RAM during init and branches there. With a
    // tiny RAM stub, any PC or data address beyond the stub size
    // aliases back into the same window — the core ends up executing
    // a scrambled mix of overwritten bytes and the "unsupported op"
    // gate stops firing because whatever happens to land at the
    // aliased offset decodes as something our core already supports.
    // 4 MiB is plenty for all BIOS init paths observed so far; real
    // PS2 has 32 MiB.
    localparam int EE_RAM_BYTES   = 4 * 1024 * 1024;
    localparam int EE_RAM_ADDR_W  = $clog2(EE_RAM_BYTES);
    localparam int BIOS_BYTES     = 4 * 1024 * 1024;

    localparam logic [31:0] RESET_VECTOR = 32'hBFC0_0000;
    localparam logic [31:0] BOOTSTRAP_VA = 32'hBFC0_0100;
    localparam int          RESET_SLOT      = 0;
    localparam int          BOOTSTRAP_SLOT0 = 32'h100 >> 2;

    // Expected unsupported instruction: BREAK (SPECIAL func 0x0D).
    // BREAK is specifically designed as a software-trap / debugger
    // breakpoint — real BIOS code does not emit it during normal
    // execution, and it is very unlikely to land in any near-future
    // micro-chapter of opcode growth. That makes it a stable sentinel
    // for the CI-mode "first unsupported op halts the core" gate.
    //   instr = {SPECIAL(6), code(20)=0, func(6)=0x0D} = 0x0000000D
    //
    // (Previous sentinel was AND $5, $2, $3 = 0x00432824, swapped
    // after AND joined the R-type ALU set in chapter 7. See
    // project memory for the Codex-flagged bench-drift story.)
    localparam logic [31:0] EXPECTED_TRAP_INSTR = 32'h0000_000D;
    localparam int TRAP_BOOTSTRAP_OFFSET = 8;
    localparam logic [31:0] EXPECTED_TRAP_PC =
        BOOTSTRAP_VA + (TRAP_BOOTSTRAP_OFFSET << 2);

    // Opcodes
    localparam logic [5:0] OP_SPECIAL = 6'h00;
    localparam logic [5:0] OP_J       = 6'h02;
    localparam logic [5:0] OP_ADDIU   = 6'h09;
    localparam logic [5:0] OP_ORI     = 6'h0D;
    localparam logic [5:0] OP_LUI     = 6'h0F;
    localparam logic [5:0] OP_LW      = 6'h23;
    localparam logic [5:0] OP_SW      = 6'h2B;

    logic clk;
    logic rst_n;

    initial clk = 1'b0;
    always #5 clk = ~clk;

    // ------------------------------------------------------------------
    // EE core — strict mode ON
    // ------------------------------------------------------------------

    logic        core_go;
    logic        core_halt;
    logic [31:0] core_pc;
    logic        core_trap;
    logic [31:0] core_trap_pc;
    logic [31:0] core_trap_instr;

    logic        ee_rd_en;
    logic [31:0] ee_rd_addr;
    logic [31:0] ee_rd_data;
    logic        ee_rd_valid;

    logic        ee_wr_en;
    logic [31:0] ee_wr_addr;
    logic [31:0] ee_wr_data;
    logic [3:0]  ee_wr_be;

    logic        ee_cpu_irq;

    logic                core_ev_valid;
    trace_pkg::subsys_e  core_ev_subsys;
    trace_pkg::event_e   core_ev_event;
    logic [63:0]         core_ev_arg0, core_ev_arg1, core_ev_arg2, core_ev_arg3;
    logic [31:0]         core_ev_flags;

    // Ch49: opt-in exception-entry mode for the alignment trap via
    // a `CH49_ALIGN_EXC` compile-time define (forwarded by the
    // Makefile from `CH49_ALIGN_EXC=1` on the command line). When
    // defined, TRAP_ALIGN_ERROR is driven low so misaligned SW/LW
    // vector to EXC_VECTOR through the standard MIPS path. When
    // NOT defined, the core halts on the first misaligned access
    // (ch47 debug mode), matching the regression baseline.
    //
    // Ch50: when CH49_ALIGN_EXC is set we also model Status.BEV,
    // starting at reset with INIT_BEV=1 so exceptions vector to
    // BEV_EXC_VECTOR (0xBFC0_0380, the R4000 ROM bootstrap common
    // vector). That matches what real BIOS expects during early
    // boot, before its RAM-resident handler is installed and it
    // clears BEV via MTC0 Status.
    // Ch215 — translate `define CH215_JMPBUF_RESTORE` (set by the
    // tb_ee_core_bios_long Makefile target) into the parameter bit
    // passed below. NOT defined for the regression sweep, so the EE
    // core stub keeps its Ch199 minimal behavior there.
`ifdef CH215_JMPBUF_RESTORE
    `define CH215_RESTORE_PARAM 1'b1
`else
    `define CH215_RESTORE_PARAM 1'b0
`endif
`ifdef CH49_ALIGN_EXC
    ee_core_stub #(
        .STRICT_UNSUPPORTED(1'b1),
        .TRAP_ALIGN_ERROR  (1'b0),
        .INIT_BEV          (1'b1),
        .CH215_JMPBUF_RESTORE_ENABLE(`CH215_RESTORE_PARAM)
    ) u_core (
`else
    ee_core_stub #(
        .STRICT_UNSUPPORTED(1'b1),
        .CH215_JMPBUF_RESTORE_ENABLE(`CH215_RESTORE_PARAM)
    ) u_core (
`endif
        .clk(clk), .rst_n(rst_n),
        .go_i(core_go),
        .map_rd_en(ee_rd_en), .map_rd_addr(ee_rd_addr),
        .map_rd_data(ee_rd_data), .map_rd_valid(ee_rd_valid),
        .map_wr_en(ee_wr_en), .map_wr_addr(ee_wr_addr),
        .map_wr_data(ee_wr_data), .map_wr_be(ee_wr_be),
        .cpu_irq(ee_cpu_irq),
        .halt_o(core_halt), .pc_o(core_pc),
        .trap_o(core_trap), .trap_pc_o(core_trap_pc),
        .trap_instr_o(core_trap_instr),
        .ev_valid(core_ev_valid), .ev_subsys(core_ev_subsys),
        .ev_event(core_ev_event),
        .ev_arg0(core_ev_arg0), .ev_arg1(core_ev_arg1),
        .ev_arg2(core_ev_arg2), .ev_arg3(core_ev_arg3),
        .ev_flags(core_ev_flags)
    );

    // ------------------------------------------------------------------
    // EE map + RAM + BIOS + DMAC + INTC
    // (DMAC/INTC present so existing map ports and the chapter-5
    //  interrupt path can reach BIOS code that touches them; synthetic
    //  CI program never actually kicks DMA or takes an exception.)
    // ------------------------------------------------------------------

    logic         bios_rd_en;
    logic [21:0]  bios_rd_addr;
    logic [31:0]  bios_rd_data;
    logic         bios_rd_valid;

    logic         ee_ram_rd_en;
    logic [24:0]  ee_ram_rd_addr;
    logic [127:0] ee_ram_rd_data;
    logic         ee_ram_rd_valid;
    logic         ee_ram_wr_en;
    logic [24:0]  ee_ram_wr_addr;
    logic [127:0] ee_ram_wr_data;
    logic [15:0]  ee_ram_wr_be;
    logic [7:0]   ee_ram_master_id;

    logic         dmac_rd_en;
    logic [31:0]  dmac_rd_addr;
    logic [127:0] dmac_rd_data;
    logic         dmac_rd_valid;

    logic        map_dmac_ch2_wr_en;
    logic [7:0]  map_dmac_ch2_wr_addr;
    logic [31:0] map_dmac_ch2_wr_data;
    logic        map_dmac_ch2_rd_en;
    logic [7:0]  map_dmac_ch2_rd_addr;
    logic [31:0] map_dmac_ch2_rd_data;
    logic        map_dmac_ch2_rd_valid;

    logic [7:0]  dmac_reg_offset_mux;
    assign dmac_reg_offset_mux = map_dmac_ch2_wr_en ? map_dmac_ch2_wr_addr
                                                   : map_dmac_ch2_rd_addr;

    logic        map_intc_wr_en;
    logic [7:0]  map_intc_wr_addr;
    logic [31:0] map_intc_wr_data;
    logic        map_intc_rd_en;
    logic [7:0]  map_intc_rd_addr;
    logic [31:0] map_intc_rd_data;
    logic        map_intc_rd_valid;

    logic [7:0]  intc_reg_addr_mux;
    assign intc_reg_addr_mux = map_intc_wr_en ? map_intc_wr_addr
                                              : map_intc_rd_addr;

    // EE bootstrap MMIO window (0x1F80_0000 - 0x1F80_FFFF). Real stub is wired
    // in this TB so the BIOS no longer reads 0xDEADBEEF from the MCH/SBUS/RDRAM
    // region and launders it into a pointer (chapter 7.99 root cause).
    logic        map_misc_mmio_wr_en;
    logic [15:0] map_misc_mmio_wr_addr;
    logic [31:0] map_misc_mmio_wr_data;
    logic [3:0]  map_misc_mmio_wr_be;
    logic        map_misc_mmio_rd_en;
    logic [15:0] map_misc_mmio_rd_addr;
    logic [31:0] map_misc_mmio_rd_data;
    logic        map_misc_mmio_rd_valid;

    logic                misc_mmio_ev_valid;
    trace_pkg::subsys_e  misc_mmio_ev_subsys;
    trace_pkg::event_e   misc_mmio_ev_event;
    logic [63:0]         misc_mmio_ev_arg0, misc_mmio_ev_arg1;
    logic [63:0]         misc_mmio_ev_arg2, misc_mmio_ev_arg3;
    logic [31:0]         misc_mmio_ev_flags;

    // EE BIU/cache-control window (0xFFFE_0000 - 0xFFFE_0FFF). Chapter 9:
    // closes the next unmapped-write family exposed by the observer
    // after chapter 8 (writes at 0xFFFE_0130 are classic BIU/cache
    // config writes during BIOS boot). Dedicated narrow stub; does NOT
    // fold into the 0x1F80 bootstrap-MMIO region — Codex's explicit
    // call to keep architecturally distinct surfaces distinct.
    logic        map_biu_wr_en;
    logic [11:0] map_biu_wr_addr;
    logic [31:0] map_biu_wr_data;
    logic [3:0]  map_biu_wr_be;
    logic        map_biu_rd_en;
    logic [11:0] map_biu_rd_addr;
    logic [31:0] map_biu_rd_data;
    logic        map_biu_rd_valid;

    logic                biu_ev_valid;
    trace_pkg::subsys_e  biu_ev_subsys;
    trace_pkg::event_e   biu_ev_event;
    logic [63:0]         biu_ev_arg0, biu_ev_arg1;
    logic [63:0]         biu_ev_arg2, biu_ev_arg3;
    logic [31:0]         biu_ev_flags;

    logic                ee_map_ev_valid;
    trace_pkg::subsys_e  ee_map_ev_subsys;
    trace_pkg::event_e   ee_map_ev_event;
    logic [63:0]         ee_map_ev_arg0, ee_map_ev_arg1, ee_map_ev_arg2, ee_map_ev_arg3;
    logic [31:0]         ee_map_ev_flags;

    ee_memory_map_stub
    `ifdef CH70_RAM_ALIAS
    #(
        // Ch70 — coherent RAM-alias mode. Single bidirectional knob
        // covering [0..4MiB): CPU useg writes mirror into ee_ram and
        // CPU kseg0/kseg1 RAM writes mirror into useg_shadow. MMIO/
        // DMAC/INTC/BIU/BIOS regions are de-aliased by the existing
        // decode predicates (RAM eligibility short-circuits there),
        // so Ch33's intent is preserved. Subsumes the Ch64/Ch65
        // narrow knobs.
        .CPU_RAM_ALIAS_EN(1'b1),
        .CPU_RAM_ALIAS_LO(29'h0000_0000),
        .CPU_RAM_ALIAS_HI(29'h0040_0000)
    )
    `else
    `ifdef CH64_USEG_MIRROR
    `define CH64_OR_CH65 1
    `endif
    `ifdef CH65_KSEG_MIRROR
    `define CH64_OR_CH65 1
    `endif
    `ifdef CH64_OR_CH65
    #(
        // Legacy narrow Ch64/Ch65 knobs retained for back-compat with
        // pre-Ch70 invocations. Ch70 supersedes these with a coherent
        // bidirectional alias when CH70_RAM_ALIAS=1.
    `ifdef CH64_USEG_MIRROR
        .CPU_USEG_TO_RAM_MIRROR_EN(1'b1),
        .CPU_USEG_TO_RAM_MIRROR_LO(29'h0004_4000),
        .CPU_USEG_TO_RAM_MIRROR_HI(29'h0004_4700)
    `ifdef CH65_KSEG_MIRROR
        ,
        .CPU_KSEG0_TO_USEG_SHADOW_MIRROR_EN(1'b1),
        .CPU_KSEG0_TO_USEG_SHADOW_MIRROR_LO(29'h001A_4000),
        .CPU_KSEG0_TO_USEG_SHADOW_MIRROR_HI(29'h001A_5000)
    `endif
    `else
    `ifdef CH65_KSEG_MIRROR
        .CPU_KSEG0_TO_USEG_SHADOW_MIRROR_EN(1'b1),
        .CPU_KSEG0_TO_USEG_SHADOW_MIRROR_LO(29'h001A_4000),
        .CPU_KSEG0_TO_USEG_SHADOW_MIRROR_HI(29'h001A_5000)
    `endif
    `endif
    )
    `endif
    `endif
    u_ee_map (
        .clk(clk), .rst_n(rst_n),
        .ee_rd_en(ee_rd_en), .ee_rd_addr(ee_rd_addr),
        .ee_rd_data(ee_rd_data), .ee_rd_valid(ee_rd_valid),
        .ee_wr_en(ee_wr_en), .ee_wr_addr(ee_wr_addr),
        .ee_wr_data(ee_wr_data), .ee_wr_be(ee_wr_be),
        .dmac_rd_en(dmac_rd_en), .dmac_rd_addr(dmac_rd_addr),
        .dmac_rd_data(dmac_rd_data), .dmac_rd_valid(dmac_rd_valid),
        // Ch263 — single EE-map bridge_wr_* port serves both the
        // Ch55 install bridge AND (when CH263_KERNEL_DATA_MUTATE is
        // defined) the Ch262 responder bridge retargeted at the
        // BIOS-polled kernel-data region. The install bridge fires
        // during early boot (~ms scale); the responder bridge fires
        // at 50 ms (mid-treadmill) — they never overlap in time, so
        // a simple priority mux (responder wins when active) is
        // sufficient. The muxed signals are declared below near the
        // install_br_* + Ch263 wires.
        .bridge_wr_en(ee_map_br_wr_en), .bridge_wr_addr(ee_map_br_wr_addr),
        .bridge_wr_data(ee_map_br_wr_data), .bridge_wr_be(ee_map_br_wr_be),
        .bridge_master_id(ee_map_br_master_id),
        .bios_rd_en(bios_rd_en), .bios_rd_addr(bios_rd_addr),
        .bios_rd_data(bios_rd_data), .bios_rd_valid(bios_rd_valid),
        .ram_rd_en(ee_ram_rd_en), .ram_rd_addr(ee_ram_rd_addr),
        .ram_rd_data(ee_ram_rd_data), .ram_rd_valid(ee_ram_rd_valid),
        .ram_wr_en(ee_ram_wr_en), .ram_wr_addr(ee_ram_wr_addr),
        .ram_wr_data(ee_ram_wr_data), .ram_wr_be(ee_ram_wr_be),
        .ram_master_id(ee_ram_master_id),
        .ee_dmac_ch2_wr_en(map_dmac_ch2_wr_en),
        .ee_dmac_ch2_wr_addr(map_dmac_ch2_wr_addr),
        .ee_dmac_ch2_wr_data(map_dmac_ch2_wr_data),
        .ee_dmac_ch2_rd_en(map_dmac_ch2_rd_en),
        .ee_dmac_ch2_rd_addr(map_dmac_ch2_rd_addr),
        .ee_dmac_ch2_rd_data(map_dmac_ch2_rd_data),
        .ee_dmac_ch2_rd_valid(map_dmac_ch2_rd_valid),
        .ee_intc_wr_en(map_intc_wr_en),
        .ee_intc_wr_addr(map_intc_wr_addr),
        .ee_intc_wr_data(map_intc_wr_data),
        .ee_intc_rd_en(map_intc_rd_en),
        .ee_intc_rd_addr(map_intc_rd_addr),
        .ee_intc_rd_data(map_intc_rd_data),
        .ee_intc_rd_valid(map_intc_rd_valid),
        .ee_misc_mmio_wr_en(map_misc_mmio_wr_en),
        .ee_misc_mmio_wr_addr(map_misc_mmio_wr_addr),
        .ee_misc_mmio_wr_data(map_misc_mmio_wr_data),
        .ee_misc_mmio_wr_be(map_misc_mmio_wr_be),
        .ee_misc_mmio_rd_en(map_misc_mmio_rd_en),
        .ee_misc_mmio_rd_addr(map_misc_mmio_rd_addr),
        .ee_misc_mmio_rd_data(map_misc_mmio_rd_data),
        .ee_misc_mmio_rd_valid(map_misc_mmio_rd_valid),
        .ee_biu_wr_en(map_biu_wr_en),
        .ee_biu_wr_addr(map_biu_wr_addr),
        .ee_biu_wr_data(map_biu_wr_data),
        .ee_biu_wr_be(map_biu_wr_be),
        .ee_biu_rd_en(map_biu_rd_en),
        .ee_biu_rd_addr(map_biu_rd_addr),
        .ee_biu_rd_data(map_biu_rd_data),
        .ee_biu_rd_valid(map_biu_rd_valid),
        .ev_valid(ee_map_ev_valid), .ev_subsys(ee_map_ev_subsys),
        .ev_event(ee_map_ev_event),
        .ev_arg0(ee_map_ev_arg0), .ev_arg1(ee_map_ev_arg1),
        .ev_arg2(ee_map_ev_arg2), .ev_arg3(ee_map_ev_arg3),
        .ev_flags(ee_map_ev_flags)
    );

    ee_ram_stub #(.SIZE_BYTES(EE_RAM_BYTES)) u_ee_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(ee_ram_rd_en), .rd_addr(ee_ram_rd_addr[EE_RAM_ADDR_W-1:0]),
        .rd_data(ee_ram_rd_data), .rd_valid(ee_ram_rd_valid),
        .wr_en(ee_ram_wr_en), .wr_addr(ee_ram_wr_addr[EE_RAM_ADDR_W-1:0]),
        .wr_data(ee_ram_wr_data), .wr_be(ee_ram_wr_be),
        .master_id(ee_ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // ------------------------------------------------------------------
    // Ch55 — external install seam. boot_install_agent_stub streams the
    // Ch54 coordinated image (AdES handler at useg 0x80..0x8C plus JR
    // $31/NOP fill through 0x1FC) into EE RAM via the real SIF bridge
    // write path, landing in both ee_ram and useg_shadow (the latter
    // via the bridge→useg_shadow shadowing added in ee_memory_map_stub).
    // Both modules stay instantiated regardless of the CH55_INSTALL
    // plusarg; when not requested, the agent sits in S_IDLE, the
    // bridge never emits, and u_ee_map.bridge_wr_en is 0 just as
    // before.
    // ------------------------------------------------------------------
    logic        install_go;
    logic        install_agent_valid;
    logic [31:0] install_agent_data;
    logic        install_agent_last;
    logic        install_agent_ready;
    logic        install_agent_busy;
    logic        install_agent_done;

    logic                install_agent_ev_valid;
    trace_pkg::subsys_e  install_agent_ev_subsys;
    trace_pkg::event_e   install_agent_ev_event;
    logic [63:0]         install_agent_ev_arg0, install_agent_ev_arg1,
                         install_agent_ev_arg2, install_agent_ev_arg3;
    logic [31:0]         install_agent_ev_flags;

    logic        install_br_wr_en;
    logic [31:0] install_br_wr_addr;
    logic [127:0] install_br_wr_data;
    logic [15:0] install_br_wr_be;
    logic [7:0]  install_br_master_id;
    logic        install_br_last_seen;

    // Ch263 — muxed feed into the EE map's single bridge_wr_* port.
    // Default (no CH263 define): pass install_br_* through unchanged
    // so every pre-Ch263 TB target stays byte-identical. When
    // CH263_KERNEL_DATA_MUTATE is defined, the Ch262 responder block
    // overrides these with a priority mux (responder wins while
    // ch262_eebr_wr_en is high; install_br_* otherwise).
    wire         ee_map_br_wr_en;
    wire [31:0]  ee_map_br_wr_addr;
    wire [127:0] ee_map_br_wr_data;
    wire [15:0]  ee_map_br_wr_be;
    wire [7:0]   ee_map_br_master_id;
`ifndef CH263_KERNEL_DATA_MUTATE
    assign ee_map_br_wr_en     = install_br_wr_en;
    assign ee_map_br_wr_addr   = install_br_wr_addr;
    assign ee_map_br_wr_data   = install_br_wr_data;
    assign ee_map_br_wr_be     = install_br_wr_be;
    assign ee_map_br_master_id = install_br_master_id;
`endif

    boot_install_agent_stub #(
        .TOTAL_WORDS(96),
        .MASTER_ID(8'd6)
    ) u_install_agent (
        .clk(clk), .rst_n(rst_n),
        .go_i(install_go),
        .out_valid(install_agent_valid), .out_data(install_agent_data),
        .out_last(install_agent_last), .out_ready(install_agent_ready),
        .busy_o(install_agent_busy), .done_o(install_agent_done),
        .ev_valid(install_agent_ev_valid),
        .ev_subsys(install_agent_ev_subsys),
        .ev_event(install_agent_ev_event),
        .ev_arg0(install_agent_ev_arg0),
        .ev_arg1(install_agent_ev_arg1),
        .ev_arg2(install_agent_ev_arg2),
        .ev_arg3(install_agent_ev_arg3),
        .ev_flags(install_agent_ev_flags)
    );

    sif_dma_ee_ram_bridge_stub #(
        .DEST_BASE_ADDR(32'h0000_0080),
        .MASTER_ID(8'd5)
    ) u_install_bridge (
        .clk(clk), .rst_n(rst_n),
        .in_valid(install_agent_valid), .in_data(install_agent_data),
        .in_last(install_agent_last), .in_ready(install_agent_ready),
        .bridge_wr_en(install_br_wr_en),
        .bridge_wr_addr(install_br_wr_addr),
        .bridge_wr_data(install_br_wr_data),
        .bridge_wr_be(install_br_wr_be),
        .bridge_master_id(install_br_master_id),
        .last_seen_o(install_br_last_seen)
    );

    // Swap IMAGE_FILE here with a user-supplied Sony BIOS hex dump to
    // run a real-BIOS smoke pass as a bake-in. The runtime-plusarg
    // path below is the preferred mechanism — don't embed paths here.
    bios_rom_stub #(.SIZE_BYTES(BIOS_BYTES)) u_bios (
        .clk(clk), .rst_n(rst_n),
        .rd_en(bios_rd_en), .rd_addr(bios_rd_addr),
        .rd_data(bios_rd_data), .rd_valid(bios_rd_valid),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // DMAC — write-only path exercised; never starts (CHCR not written)
    logic         dmac_ep_valid;
    logic [127:0] dmac_ep_data;
    logic         dmac_ep_last;
    logic         dmac_ep_ready;
    assign dmac_ep_ready = 1'b1;

    logic dmac_irq_completion;

    logic                dmac_ev_valid;
    trace_pkg::subsys_e  dmac_ev_subsys;
    trace_pkg::event_e   dmac_ev_event;
    logic [63:0]         dmac_ev_arg0, dmac_ev_arg1, dmac_ev_arg2, dmac_ev_arg3;
    logic [31:0]         dmac_ev_flags;

    dmac_reg_stub #(.CHANNEL(4'd2), .PATH_ID(4'd2)) u_dmac_ch2 (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(map_dmac_ch2_wr_en),
        .reg_offset(dmac_reg_offset_mux),
        .reg_wr_data(map_dmac_ch2_wr_data),
        .reg_rd_en(map_dmac_ch2_rd_en),
        .reg_rd_data(map_dmac_ch2_rd_data),
        .reg_rd_valid(map_dmac_ch2_rd_valid),
        .mem_rd_en(dmac_rd_en), .mem_rd_addr(dmac_rd_addr),
        .mem_rd_data(dmac_rd_data), .mem_rd_valid(dmac_rd_valid),
        .ep_valid(dmac_ep_valid), .ep_data(dmac_ep_data),
        .ep_last(dmac_ep_last), .ep_ready(dmac_ep_ready),
        .irq_completion_o(dmac_irq_completion),
        .ev_valid(dmac_ev_valid), .ev_subsys(dmac_ev_subsys),
        .ev_event(dmac_ev_event),
        .ev_arg0(dmac_ev_arg0), .ev_arg1(dmac_ev_arg1),
        .ev_arg2(dmac_ev_arg2), .ev_arg3(dmac_ev_arg3),
        .ev_flags(dmac_ev_flags)
    );

    // EE INTC — DMAC ch2 completion → source bit 0 (same as chapter 5).
    // cpu_irq stays low in strict-trap mode because the synthetic
    // program never enables interrupts nor fires DMAC.
    logic [15:0] ee_irq_src;
    assign ee_irq_src = {15'd0, dmac_irq_completion};

    intc_stub #(
        .INTC_STAT_OFFSET(8'h00),
        .INTC_MASK_OFFSET(8'h10)
    ) u_intc (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(map_intc_wr_en), .reg_rd_en(map_intc_rd_en),
        .reg_addr(intc_reg_addr_mux),
        .reg_wr_data(map_intc_wr_data),
        .reg_rd_data(map_intc_rd_data), .reg_rd_valid(map_intc_rd_valid),
        .irq_src(ee_irq_src), .cpu_irq(ee_cpu_irq),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // EE bootstrap MMIO window stub — 64 KiB latched register file at
    // 0x1F80_xxxx. Chapter 8: stops BIOS from laundering 0xDEADBEEF into
    // pointers read from the MCH/SBUS/RDRAM init region.
    //
    // Ch259 — drive iop_intc_inject_src_i from a TB-local reg so the
    // BIOS-long target (or any future variant) can experimentally
    // hold a synthetic IOP INTC source bit asserted via a +plusarg
    // without re-elaborating. Defaults to 16'd0 (no synthetic source);
    // Ch259 Phase 1 runs with this default to test whether proper
    // W1C / mask semantics ALONE break the Ch215 treadmill. Phase 2
    // experiments with `+IOP_INTC_BOOT_SRC=<hex16>` to assert one
    // pending bit if Phase 1 stays in the treadmill.
    logic [15:0] iop_intc_inject_src_q;
    initial begin
        logic [31:0] plusarg_val;
        iop_intc_inject_src_q = 16'd0;
        if ($value$plusargs("IOP_INTC_BOOT_SRC=%h", plusarg_val)) begin
            iop_intc_inject_src_q = plusarg_val[15:0];
            $display("[tb_ee_core_bios_smoke] Ch259 IOP_INTC_BOOT_SRC = 0x%04h (synthetic sticky source on I_STAT)",
                     iop_intc_inject_src_q);
        end
    end

    // Ch262 — responder-driven INTC pulse. When CH262_IOP_RESPONDER is
    // defined, a tiny IOP responder (built from the Ch261 skeleton)
    // runs once mid-treadmill and emits a 1-cycle pulse on
    // `ch262_resp_pulse` when its SIF/EE-RAM landing completes. The
    // pulse ORs into the I_STAT inject path below, where ee_bootstrap_
    // mmio's sticky `(stat_q & ~w1c) | inject` semantics latch the
    // bit until BIOS W1Cs it — exactly the "one timed causal IOP
    // event" Codex's Ch262 framing asked for. Default 0 in non-Ch262
    // builds so the routine BIOS-long target stays byte-identical.
    wire [15:0] ch262_resp_pulse;
`ifndef CH262_IOP_RESPONDER
    assign ch262_resp_pulse = 16'd0;
`endif
    wire [15:0] iop_intc_inject_src_combined = iop_intc_inject_src_q | ch262_resp_pulse;

    ee_bootstrap_mmio_stub u_misc_mmio (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(map_misc_mmio_wr_en),
        .reg_wr_addr(map_misc_mmio_wr_addr),
        .reg_wr_data(map_misc_mmio_wr_data),
        .reg_wr_be(map_misc_mmio_wr_be),
        .reg_rd_en(map_misc_mmio_rd_en),
        .reg_rd_addr(map_misc_mmio_rd_addr),
        .reg_rd_data(map_misc_mmio_rd_data),
        .reg_rd_valid(map_misc_mmio_rd_valid),
        .iop_intc_inject_src_i(iop_intc_inject_src_combined),
        .ev_valid(misc_mmio_ev_valid),
        .ev_subsys(misc_mmio_ev_subsys),
        .ev_event(misc_mmio_ev_event),
        .ev_arg0(misc_mmio_ev_arg0), .ev_arg1(misc_mmio_ev_arg1),
        .ev_arg2(misc_mmio_ev_arg2), .ev_arg3(misc_mmio_ev_arg3),
        .ev_flags(misc_mmio_ev_flags)
    );

    // EE BIU/cache-control stub — 4 KiB latched register file at
    // 0xFFFE_0xxx. Chapter 9: closes the next unmapped-write family.
    ee_biu_mmio_stub u_biu (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(map_biu_wr_en),
        .reg_wr_addr(map_biu_wr_addr),
        .reg_wr_data(map_biu_wr_data),
        .reg_wr_be(map_biu_wr_be),
        .reg_rd_en(map_biu_rd_en),
        .reg_rd_addr(map_biu_rd_addr),
        .reg_rd_data(map_biu_rd_data),
        .reg_rd_valid(map_biu_rd_valid),
        .ev_valid(biu_ev_valid),
        .ev_subsys(biu_ev_subsys),
        .ev_event(biu_ev_event),
        .ev_arg0(biu_ev_arg0), .ev_arg1(biu_ev_arg1),
        .ev_arg2(biu_ev_arg2), .ev_arg3(biu_ev_arg3),
        .ev_flags(biu_ev_flags)
    );

    // ------------------------------------------------------------------
    // Trace sinks
    // ------------------------------------------------------------------

    trace_sink_stub #(.FILENAME("ee_bios_smoke_core.trace"), .SINK_LABEL("ee_core"))
        u_trace_core (.clk(clk), .rst_n(rst_n),
            .ev_valid(core_ev_valid), .ev_subsys(core_ev_subsys),
            .ev_event(core_ev_event), .ev_arg0(core_ev_arg0),
            .ev_arg1(core_ev_arg1), .ev_arg2(core_ev_arg2),
            .ev_arg3(core_ev_arg3), .ev_flags(core_ev_flags));

    trace_sink_stub #(.FILENAME("ee_bios_smoke_map.trace"), .SINK_LABEL("ee_map"))
        u_trace_map (.clk(clk), .rst_n(rst_n),
            .ev_valid(ee_map_ev_valid), .ev_subsys(ee_map_ev_subsys),
            .ev_event(ee_map_ev_event), .ev_arg0(ee_map_ev_arg0),
            .ev_arg1(ee_map_ev_arg1), .ev_arg2(ee_map_ev_arg2),
            .ev_arg3(ee_map_ev_arg3), .ev_flags(ee_map_ev_flags));

    trace_sink_stub #(.FILENAME("ee_bios_smoke_dmac.trace"), .SINK_LABEL("dmac_ch2"))
        u_trace_dmac (.clk(clk), .rst_n(rst_n),
            .ev_valid(dmac_ev_valid), .ev_subsys(dmac_ev_subsys),
            .ev_event(dmac_ev_event), .ev_arg0(dmac_ev_arg0),
            .ev_arg1(dmac_ev_arg1), .ev_arg2(dmac_ev_arg2),
            .ev_arg3(dmac_ev_arg3), .ev_flags(dmac_ev_flags));

    // ------------------------------------------------------------------
    // Observers
    // ------------------------------------------------------------------

    int core_retired_events;
    int core_halt_events;
    int core_trap_events;
    int core_exception_events;
    int dmac_done_events;
    int bios_reads;
    int errors;

    // ------------------------------------------------------------------
    // PC-progress sampler (iteration-14 diagnostic). When the opcode
    // set got deep enough that the BIOS no longer trips an unsupported
    // instruction within the sim window, the timeout needs to say
    // *what kind* of not-trapping it was:
    //   - stuck on one pc        → hang (infinite loop with no advance)
    //   - bouncing in narrow range → polling loop (likely waiting on
    //                                hardware state the core doesn't
    //                                model, e.g. COP0 Count)
    //   - making broad forward progress → just needs more cycles
    // Sample core_pc every PC_SAMPLE_PERIOD cycles into a rolling ring
    // buffer; track running min/max. Print on timeout.
    //
    // Cheap: one always_ff, one ring, one mod. Does not affect any of
    // the existing assertions — it's purely diagnostic output.
    // ------------------------------------------------------------------

    localparam int PC_SAMPLE_PERIOD = 25000;  // ~80 samples in 20 ms window
    localparam int PC_SAMPLE_DEPTH  = 16;

    int          sim_cycle_ctr;
    logic [31:0] pc_min;
    logic [31:0] pc_max;
    logic [31:0] pc_history       [0:PC_SAMPLE_DEPTH-1];
    logic [31:0] pc_instr_history [0:PC_SAMPLE_DEPTH-1];
    int          retired_history  [0:PC_SAMPLE_DEPTH-1];
    // Pointer-register snapshots — added to discriminate "long list"
    // from "cyclic list" or "corrupted pointer" when the pc stays
    // inside a traversal loop. $5 and $6 are the two registers the
    // 0x14B4 loop uses as sentinel and current-node pointers; peeking
    // them at sample time tells us whether $6 is walking forward
    // through plausible addresses, revisiting the same short set of
    // values, or pointing somewhere implausible. Codex-directed.
    logic [31:0] reg_r5_history   [0:PC_SAMPLE_DEPTH-1];
    logic [31:0] reg_r6_history   [0:PC_SAMPLE_DEPTH-1];
    int          pc_sample_count;

    // peek_instr: best-effort decode of the instruction at `addr` by
    // looking through the backing stubs hierarchically. BIOS and EE
    // RAM are both covered; anything else returns 0xDEADBEEF so the
    // reader can tell a miss.
    //
    // This is a simulation-only helper — it peeks at the backing
    // arrays directly, not through the map. Fine for diagnostics.
    function automatic logic [31:0] peek_instr(input logic [31:0] addr);
        logic [28:0] phys;
        logic [21:0] bios_off_words;
        int unsigned ram_qw_idx;
        logic [1:0]  ram_lane;
        logic [19:0] useg_idx;
        logic [31:0] result;
        phys = addr[28:0];
        // Ch179 — useg fetches (top bit of VA = 0) go through
        // useg_shadow_mem in the memory map, NOT ee_ram. The pre-
        // Ch179 version of this peek hit ee_ram for low addresses
        // and reported 0x00000000 for the BIOS-installed handlers
        // in low RAM, which made the wait-loop look like a NOP walk
        // when it's actually executing real code. Route through
        // peek_useg_shadow when VA is in useg space.
        if (addr[31] == 1'b0 && phys[28:22] == 7'd0) begin
            useg_idx = phys[21:2];
            result   = u_ee_map.g_useg_shadow.useg_shadow_mem[useg_idx];
        end else if (phys[28:22] == 7'h7F) begin
            bios_off_words = phys[21:2];
            result = u_bios.mem[bios_off_words];
        end else if (phys[28:25] == 4'h0) begin
            ram_qw_idx = phys[24:4];
            ram_lane   = phys[3:2];
            case (ram_lane)
                2'd0:    result = u_ee_ram.mem[ram_qw_idx][31:0];
                2'd1:    result = u_ee_ram.mem[ram_qw_idx][63:32];
                2'd2:    result = u_ee_ram.mem[ram_qw_idx][95:64];
                2'd3:    result = u_ee_ram.mem[ram_qw_idx][127:96];
                default: result = 32'hDEAD_BEEF;
            endcase
        end else begin
            result = 32'hDEAD_BEEF;
        end
        return result;
    endfunction

    // Ch61: peek the useg_shadow backing directly. The EE CPU reads
    // useg virtual addresses (addr[31]==0, phys[28:22]==0 → first
    // 4 MiB) through useg_shadow_mem, not ee_ram; peek_instr above
    // only peeks ee_ram. For fetch-path diagnostics at pc inside
    // useg we need this second peek.
    function automatic logic [31:0] peek_useg_shadow(input logic [31:0] addr);
        logic [28:0] phys;
        logic [19:0] idx;
        phys = addr[28:0];
        if (phys[28:22] != 7'd0) return 32'hDEADBEEF;
        idx = phys[21:2];
        return u_ee_map.g_useg_shadow.useg_shadow_mem[idx];
    endfunction

    initial begin
        sim_cycle_ctr   = 0;
        pc_min          = 32'hFFFF_FFFF;
        pc_max          = 32'h0000_0000;
        pc_sample_count = 0;
        for (int i = 0; i < PC_SAMPLE_DEPTH; i++) begin
            pc_history[i]       = 32'd0;
            pc_instr_history[i] = 32'd0;
            retired_history[i]  = 0;
            reg_r5_history[i]   = 32'd0;
            reg_r6_history[i]   = 32'd0;
        end
    end

    always_ff @(posedge clk) begin
        if (!rst_n) sim_cycle_ctr <= 0;
        else        sim_cycle_ctr <= sim_cycle_ctr + 1;
    end

    always_ff @(posedge clk) begin
        if (rst_n && ((sim_cycle_ctr % PC_SAMPLE_PERIOD) == 0)) begin
            if (core_pc < pc_min) pc_min <= core_pc;
            if (core_pc > pc_max) pc_max <= core_pc;
            pc_history[pc_sample_count[3:0]]       <= core_pc;
            pc_instr_history[pc_sample_count[3:0]] <= peek_instr(core_pc);
            retired_history[pc_sample_count[3:0]]  <= core_retired_events;
            reg_r5_history[pc_sample_count[3:0]]   <= u_core.regfile[5];
            reg_r6_history[pc_sample_count[3:0]]   <= u_core.regfile[6];
            pc_sample_count <= pc_sample_count + 1;
        end
    end

    // ------------------------------------------------------------------
    // Ch179 — wait-loop autopsy. Maintain a small ring buffer of the
    // most recent EE-side loads (addr/data/cycle/pc). At TIMEOUT we
    // dump the ring so the reader can see exactly which address the
    // wait loop is polling, what value it observed, and which
    // memory-map region the address lives in.
    //
    // No attempt is made to auto-infer the branch's mask/compare
    // semantics — that's left to the human (Codex) reading the
    // emitted instruction window around the loop PC. Cheaper and
    // more honest than guessing.
    // ------------------------------------------------------------------
    localparam int LOAD_RING_DEPTH = 16;
    int          load_ring_count;
    int          load_ring_cycle [0:LOAD_RING_DEPTH-1];
    logic [31:0] load_ring_pc    [0:LOAD_RING_DEPTH-1];
    logic [31:0] load_ring_addr  [0:LOAD_RING_DEPTH-1];
    logic [31:0] load_ring_data  [0:LOAD_RING_DEPTH-1];
    initial begin
        load_ring_count = 0;
        for (int i = 0; i < LOAD_RING_DEPTH; i++) begin
            load_ring_cycle[i] = 0;
            load_ring_pc[i]    = 32'd0;
            load_ring_addr[i]  = 32'd0;
            load_ring_data[i]  = 32'd0;
        end
    end
    // Capture only DATA loads (filter out instruction fetches).
    // The EE core stub uses map_rd_* for both ifetch (S_IFETCH_REQ
    // / S_IFETCH_WAIT) and data load (S_MEM_REQ / S_MEM_RESP).
    // Snapshot map_rd_addr at issue time and stash it; on the
    // valid cycle pair it with the data; only enroll if the state
    // at issue was S_MEM_REQ (= a real data load).
    logic        pend_is_data;
    logic [31:0] pend_addr;
    logic [31:0] pend_pc;
    initial begin
        pend_is_data = 1'b0;
        pend_addr    = 32'd0;
        pend_pc      = 32'd0;
    end
    always_ff @(posedge clk) begin
        if (rst_n) begin
            // At issue cycle, capture state classification + addr.
            // u_core.state == 4'd4 (S_MEM_REQ) means data load.
            if (ee_rd_en && u_core.state == 4'd4) begin
                pend_is_data <= 1'b1;
                pend_addr    <= ee_rd_addr;
                pend_pc      <= core_pc;
            end else if (ee_rd_en) begin
                pend_is_data <= 1'b0;
                pend_addr    <= ee_rd_addr;
                pend_pc      <= core_pc;
            end
            // On valid cycle, enroll the data load if it was one.
            if (ee_rd_valid && pend_is_data) begin
                load_ring_cycle[load_ring_count % LOAD_RING_DEPTH] <= sim_cycle_ctr;
                load_ring_pc   [load_ring_count % LOAD_RING_DEPTH] <= pend_pc;
                load_ring_addr [load_ring_count % LOAD_RING_DEPTH] <= pend_addr;
                load_ring_data [load_ring_count % LOAD_RING_DEPTH] <= ee_rd_data;
                load_ring_count <= load_ring_count + 1;
            end
        end
    end

    // Region classifier — best-effort map of PS2 physical addresses
    // to subsystem names. Covers the regions we KNOW exist or might
    // soon model; everything else falls through to UNKNOWN. The
    // classification is by PHYSICAL address (low 29 bits of the VA),
    // not virtual — so KSEG0 / KSEG1 / KSEG2 / useg all collapse to
    // the same physical bucket.
    function automatic string ch179_region(input logic [31:0] addr);
        logic [28:0] phys;
        phys = addr[28:0];
        // Main RAM (32 MB at physical 0x00000000 — 0x01FFFFFF)
        if (phys[28:25] == 4'h0) return "EE_RAM_32M";
        // Scratchpad (16 KB at 0x70000000)
        if (phys[28:24] == 5'h70 >> 3) return "EE_SCRATCH";
        if (addr[31:14] == 18'h70000 >> 14) return "EE_SCRATCH";
        // BIOS (4 MB at physical 0x1FC00000)
        if (phys[28:22] == 7'h7F) return "BIOS_ROM";
        // PS2 hardware-register windows (all in 0x10000000-0x1FFFFFFF)
        case (phys[27:16])
            12'h000:        return "EE_TIMER";          // 0x10000000-0x1000FFFF
            12'h001:        return "EE_IPU";            // 0x10010000-0x1001FFFF
            12'h002:        return "EE_GIF";            // 0x10020000-0x1002FFFF
            12'h003:        return "EE_VIF0";           // 0x10030000-0x10037FFF
            12'h008:        return "EE_DMAC";           // 0x10008000-0x1000DFFF
            12'h00F:        return "EE_INTC";           // 0x1000F000-0x1000FFFF
            12'h2F0, 12'h2F1,
            12'h2F2, 12'h2F3,
            12'h2F4, 12'h2F5,
            12'h2F6, 12'h2F7: return "EE_DRAM_CTRL";    // 0x1F800000-0x1F87FFFF? bound check
            12'h100:        return "EE_BIU";            // 0x10000000 area variants
            12'hA00, 12'hA01,
            12'hA02, 12'hA03: return "EE_POST_DEBUG";   // 0x1FA00000 POST
            default: ;
        endcase
        // SIF mailbox / DMA: 0x1000F200-0x1000F2FF
        if (phys[28:8] == 21'h1000F2 >> 0) return "EE_SIF_MMIO";
        // VU0/VU1 micro/data: 0x11000000-0x11008FFF
        if (phys[28:16] == 13'h1100) return "EE_VU";
        // GS privileged: 0x12000000-0x120FFFFF
        if (phys[28:20] == 9'h120) return "EE_GS_PRIV";
        // IOP RAM mirror via SBUS at 0x1C000000-0x1FBFFFFF
        if (phys[28:24] == 5'h1C >> 1) return "IOP_RAM_MIRROR";
        return "UNKNOWN";
    endfunction

    // ------------------------------------------------------------------
    // First-unmapped observer (Codex-directed). The map already emits
    // EV_UNMAPPED events with address/master/region, but for
    // root-causing poisoned dataflow we need the *originating
    // core_pc* — i.e. the PC of the instruction that issued the bad
    // request. Map trace events are 1 cycle after the request, but
    // core_pc doesn't advance during S_MEM_REQ→S_MEM_WAIT or
    // S_IFETCH_REQ→S_IFETCH_WAIT, so core_pc at ev_valid time IS the
    // request pc.
    //
    // Captures the first UNMAPPED_DEPTH events with full context,
    // keeps a running total, and prints each new event immediately
    // (bounded by UNMAPPED_DEPTH so we don't spam the log when the
    // BIOS wedges in a DEADBEEF cycle and re-fires millions of times).
    // Prints again in the timeout report for permanence.
    // ------------------------------------------------------------------

    localparam int UNMAPPED_DEPTH = 32;
    int          unmapped_count;
    int          unmapped_cycle  [0:UNMAPPED_DEPTH-1];
    logic [31:0] unmapped_pc     [0:UNMAPPED_DEPTH-1];
    logic        unmapped_is_wr  [0:UNMAPPED_DEPTH-1];
    logic [31:0] unmapped_addr   [0:UNMAPPED_DEPTH-1];
    logic [31:0] unmapped_data   [0:UNMAPPED_DEPTH-1];

    // ------------------------------------------------------------------
    // Chapter 10 diagnostic — lineage around the first unmapped READ.
    // Chapters 8 and 9 absorbed two write-only unmapped families
    // (0x1F80 MMIO, 0xFFFE_0xxx BIU) but the DEADBEEF-poisoned pointer
    // loop still triggers. The smoking-gun event is the first UNMAPPED
    // *read* (addr=0x3084_FFFF at pc=0x000014C4 in the current regression),
    // whose returned DEADBEEF ends up in a register that becomes the
    // base pointer for the self-perpetuating list walk. We want to know
    // WHERE 0x3084_FFFF came from.
    //
    // Capture strategy (Codex-directed, purely TB-side):
    //
    //   A) Dense retire ring. Log every retire's (pc, instruction, and
    //      a snapshot of $2 / $6 just after retire — these are the two
    //      registers the loop reshuffles) into a 32-deep ring. That's
    //      roughly the previous 32 instructions of history at the
    //      moment the first unmapped read fires. The sampler already
    //      owns retired_history but samples every 25000 cycles; this
    //      ring is dense — one entry per retire.
    //
    //   B) First-unmapped-read snapshot. On the FIRST UNMAPPED event
    //      where !wr (i.e. read), decode the instruction at core_pc
    //      into {opcode, rs, rt, rd, imm}, capture live regfile values
    //      for rs and rt, and dump the retire ring newest-first.
    //      That gives an at-a-glance view of:
    //        - what the failing instruction was ("lw $2, 0($6)"),
    //        - what the bad base pointer was ($6's live value),
    //        - what instruction stream set up that value.
    //      Separate from the depth-bounded unmapped_count arrays so
    //      the write-side noise doesn't consume the depth budget.
    // ------------------------------------------------------------------

    localparam int RETIRE_RING_DEPTH = 32;
    int          retire_ring_count;      // monotone, never decreases
    int          retire_ring_head;       // next write slot, wraps
    logic [31:0] retire_ring_pc    [0:RETIRE_RING_DEPTH-1];
    logic [31:0] retire_ring_instr [0:RETIRE_RING_DEPTH-1];
    logic [31:0] retire_ring_r2    [0:RETIRE_RING_DEPTH-1];
    logic [31:0] retire_ring_r6    [0:RETIRE_RING_DEPTH-1];
    int          retire_ring_cyc   [0:RETIRE_RING_DEPTH-1];

    logic        lineage_captured;
    int          lineage_cycle;
    logic [31:0] lineage_pc;
    logic [31:0] lineage_instr;
    logic [5:0]  lineage_opcode;
    logic [4:0]  lineage_rs;
    logic [4:0]  lineage_rt;
    logic [4:0]  lineage_rd;
    logic [15:0] lineage_imm;
    logic [31:0] lineage_rs_val;
    logic [31:0] lineage_rt_val;
    logic [31:0] lineage_rd_val;
    logic [31:0] lineage_effective_addr;
    logic [31:0] lineage_poison_addr;
    logic [31:0] lineage_poison_data;

    // Live decode of the instruction at core_pc, recomputed combinationally.
    // Captured into lineage_* at the edge the first unmapped READ fires.
    // Pure diagnostic — declared at module scope because iverilog 12
    // rejects `automatic` variables inside always_ff.
    logic [31:0] live_pc_instr;
    logic [5:0]  live_opcode;
    logic [4:0]  live_rs_idx;
    logic [4:0]  live_rt_idx;
    logic [4:0]  live_rd_idx;
    logic [15:0] live_imm;
    logic [31:0] live_rs_val;
    logic [31:0] live_rt_val;
    logic [31:0] live_rd_val;
    logic [31:0] live_ea;
    assign live_pc_instr = peek_instr(core_pc);
    assign live_opcode   = live_pc_instr[31:26];
    assign live_rs_idx   = live_pc_instr[25:21];
    assign live_rt_idx   = live_pc_instr[20:16];
    assign live_rd_idx   = live_pc_instr[15:11];
    assign live_imm      = live_pc_instr[15:0];
    assign live_rs_val   = u_core.regfile[live_rs_idx];
    assign live_rt_val   = u_core.regfile[live_rt_idx];
    assign live_rd_val   = u_core.regfile[live_rd_idx];
    assign live_ea       = live_rs_val + {{16{live_imm[15]}}, live_imm};

    // ------------------------------------------------------------------
    // Chapter 11 — register-transition watcher (Codex-directed).
    //
    // The chapter-10 ring pinned the *propagation* chain (addu $6,$2,$0
    // laundering $2's corrupted value into $6). It did NOT pin the
    // *origin* — which retire first wrote a 0x3xxx_xxxx value into $2
    // or $6. The retire ring sampled regfile[i] on the same clock edge
    // the core's own retire was committing, so the "per-entry $2/$6"
    // columns have a 1-cycle ambiguity relative to retired_pc at that
    // same edge. Good for chronology, not for proving "this retire
    // wrote that value."
    //
    // Surgical fix: a dedicated transition detector. Snapshot $2 and
    // $6 one edge apart; when the TB observes the NEW value enter the
    // poison band (0x3000_0000-0x3FFF_FFFF) AND the OLD value was
    // outside that band, latch a rich snapshot of the retire context
    // at that edge. Captures prev/new plus u_core.retired_pc /
    // retired_instr / retired_arg2 / retired_arg3 — the last two
    // carry {EA, map_rd_data} for loads and {rd_idx, alu_result}
    // for ALU ops (see ee_core_stub.sv lines 743-757, 812-813,
    // 830-831). So for a LW that produced the poison, arg2 gives
    // WHERE the data came from and arg3 gives WHAT was loaded.
    //
    // Also sample retired_pc/instr/arg2/arg3 delayed by one clock
    // so that if the NBA ordering makes the "current-edge" retired
    // values stale, we still have the edge-aligned data.
    //
    // Band vs exact match: Codex's call included either exact
    // 0x3084_FFFF or the 0x3xxx_xxxx band. Band is strictly more
    // informative — catches intermediate steps that might produce
    // a different 0x30xx_xxxx or 0x3Axx_xxxx en route.
    // ------------------------------------------------------------------

    logic [31:0] r2_prev_1, r6_prev_1;
    logic [31:0] retired_pc_d1;
    logic [31:0] retired_instr_d1;
    logic [31:0] retired_arg2_d1;
    logic [31:0] retired_arg3_d1;

    always_ff @(posedge clk) begin
        if (rst_n) begin
            r2_prev_1        <= u_core.regfile[2];
            r6_prev_1        <= u_core.regfile[6];
            retired_pc_d1    <= u_core.retired_pc;
            retired_instr_d1 <= u_core.retired_instr;
            retired_arg2_d1  <= u_core.retired_arg2;
            retired_arg3_d1  <= u_core.retired_arg3;
        end
    end

    logic        r2_tx_captured;
    int          r2_tx_cycle;
    logic [31:0] r2_tx_prev;
    logic [31:0] r2_tx_new;
    logic [31:0] r2_tx_retired_pc_now;
    logic [31:0] r2_tx_retired_instr_now;
    logic [31:0] r2_tx_retired_arg2_now;
    logic [31:0] r2_tx_retired_arg3_now;
    logic [31:0] r2_tx_retired_pc_d1;
    logic [31:0] r2_tx_retired_instr_d1;
    logic [31:0] r2_tx_retired_arg2_d1;
    logic [31:0] r2_tx_retired_arg3_d1;

    logic        r6_tx_captured;
    int          r6_tx_cycle;
    logic [31:0] r6_tx_prev;
    logic [31:0] r6_tx_new;
    logic [31:0] r6_tx_retired_pc_now;
    logic [31:0] r6_tx_retired_instr_now;
    logic [31:0] r6_tx_retired_arg2_now;
    logic [31:0] r6_tx_retired_arg3_now;
    logic [31:0] r6_tx_retired_pc_d1;
    logic [31:0] r6_tx_retired_instr_d1;
    logic [31:0] r6_tx_retired_arg2_d1;
    logic [31:0] r6_tx_retired_arg3_d1;

    // Exact-match triggers on the specific poison value 0x3084_FFFF.
    // The band trigger above catches any entry into the 0x3xxx_xxxx
    // range, which picked up legitimate instruction-data loads
    // (e.g. 0x3c1a_bfc0 = LUI $26 as data) during BIOS relocation.
    // Those are expected-and-benign. The exact-match isolates the
    // specific retire that first manufactures the poison value.
    localparam logic [31:0] POISON_VALUE = 32'h3084_FFFF;

    logic        r2_ex_captured;
    int          r2_ex_cycle;
    logic [31:0] r2_ex_retired_pc_now;
    logic [31:0] r2_ex_retired_instr_now;
    logic [31:0] r2_ex_retired_arg2_now;
    logic [31:0] r2_ex_retired_arg3_now;
    logic [31:0] r2_ex_retired_pc_d1;
    logic [31:0] r2_ex_retired_instr_d1;
    logic [31:0] r2_ex_retired_arg2_d1;
    logic [31:0] r2_ex_retired_arg3_d1;

    logic        r6_ex_captured;
    int          r6_ex_cycle;
    logic [31:0] r6_ex_retired_pc_now;
    logic [31:0] r6_ex_retired_instr_now;
    logic [31:0] r6_ex_retired_arg2_now;
    logic [31:0] r6_ex_retired_arg3_now;
    logic [31:0] r6_ex_retired_pc_d1;
    logic [31:0] r6_ex_retired_instr_d1;
    logic [31:0] r6_ex_retired_arg2_d1;
    logic [31:0] r6_ex_retired_arg3_d1;

    // ------------------------------------------------------------------
    // Chapter 12 — RAM-write observer for the poison RAM word
    // (Codex-directed, narrow).
    //
    // Chapter 11 pinned the poison LOAD: `lw $2, 0($6)` at pc=0x14C4
    // with EA=0x80001E28 loaded 0x3084_FFFF. So some EARLIER retired
    // store wrote that value into that exact RAM word. Watch for it.
    //
    // Trigger condition: a retired store (retired_flag_write == 1)
    // with retired_arg2 == 0x80001E28 (the core logs EA in arg2 for
    // stores — see ee_core_stub.sv:828-831) AND retired_arg3 ==
    // 0x3084_FFFF (the rt_val being stored). Exact match so the
    // trigger fires on the specific poison-planting SW, not on any
    // earlier or later unrelated writes to the same word.
    //
    // If the poison value turns out to be built up by multiple sub-
    // word stores instead of a single SW, the exact-match trigger
    // won't fire. In that case chapter 13 would broaden to "any
    // store whose EA falls in the word containing 0x80001E28" —
    // but start narrow per Codex's call.
    //
    // Once captured, the retired_pc pins the specific BIOS
    // instruction that planted the poison, and we can reverse from
    // there to see where 0x3084_FFFF came from (the rs base that
    // produced the EA, and whatever instruction wrote rt before
    // the SW).
    // ------------------------------------------------------------------

    localparam logic [31:0]  POISON_RAM_ADDR      = 32'h8000_1E28;
    localparam logic [28:0]  POISON_RAM_PHYS      = 29'h0_0000_1E28;
    localparam logic [28:2]  POISON_RAM_PHYS_WORD = 27'h0_0000_78A;  // phys[28:2] of 0x1E28
    localparam int           RAMWR_DEPTH          = 8;

    // Narrow trigger — exact SW of POISON_VALUE to POISON_RAM_ADDR.
    logic        ramwr_captured;
    int          ramwr_cycle;
    logic [31:0] ramwr_retired_pc;
    logic [31:0] ramwr_retired_instr;
    logic [31:0] ramwr_retired_arg2;
    logic [31:0] ramwr_retired_arg3;
    logic [5:0]  ramwr_opcode;
    logic [4:0]  ramwr_rs_idx;
    logic [4:0]  ramwr_rt_idx;
    logic [15:0] ramwr_imm;
    logic [31:0] ramwr_rs_val_at_retire;
    logic [31:0] ramwr_rt_val_at_retire;

    // Broad trigger — any retired store whose EA maps (phys-stripped)
    // to the 4-byte word containing POISON_RAM_ADDR. Catches sub-word
    // stores (SB/SH sequences that build the value byte-by-byte) and
    // kseg-alias stores (kseg1 0xA000_1E28 instead of kseg0 0x8000_1E28).
    // Depth 8 so a full SB×4 build-up is preserved with headroom.
    int          ramwr_any_count;
    int          ramwr_any_cycle  [0:RAMWR_DEPTH-1];
    logic [31:0] ramwr_any_pc     [0:RAMWR_DEPTH-1];
    logic [31:0] ramwr_any_instr  [0:RAMWR_DEPTH-1];
    logic [31:0] ramwr_any_ea     [0:RAMWR_DEPTH-1];
    logic [31:0] ramwr_any_data   [0:RAMWR_DEPTH-1];

    // ------------------------------------------------------------------
    // Chapter 13 — control-flow + pointer provenance into the 0x14xx
    // consumer (Codex-directed).
    //
    // Chapter 12 proved the "poison" value is a legitimate code word
    // copied into low RAM by the BIOS. The remaining pathology is that
    // a DIFFERENT routine running at RAM 0x14xx treats that word as a
    // pointer. The high-yield question is no longer "where did
    // 0x3084_FFFF come from" but "how did execution get into the
    // 0x14xx routine at all, and how did $6 end up pointing at
    // 0x80001E28 before the fatal LW?"
    //
    // Two observer families:
    //
    //   A) First control-flow entry into the 0x14xx region. On the
    //      first retire whose pc lies in [0x1400, 0x14FF], capture
    //      (cycle, retired_pc, retired_instr) AND the previous
    //      retire's pc/instr — that's the call/jump site that landed
    //      execution here. Also capture live $2/$5/$6/$31 so we see
    //      the pointer-register state AT routine entry (and $31
    //      reveals the return address, confirming JAL vs JR
    //      semantics).
    //
    //   B) First transitions of $6 / $2 / $5 INTO the phys-word
    //      containing 0x80001E28. Narrow — single 4-byte word match
    //      on phys[28:2], so kseg aliases (useg 0x0000_1E28, kseg0
    //      0x8000_1E28) both count. For each register, capture
    //      prev/new plus retired_pc/instr/arg2/arg3. The $5 trigger
    //      is expected to NEVER fire in the current BIOS run ($5
    //      stayed at the sentinel 0x974 throughout); its absence is
    //      itself diagnostic data — confirms $5 is a sentinel, not
    //      a pointer.
    // ------------------------------------------------------------------

    localparam logic [31:0]  ENTRY_REGION_LOW  = 32'h0000_1400;
    localparam logic [31:0]  ENTRY_REGION_HIGH = 32'h0000_14FF;
    // phys[28:2] of 0x1E28 = 0x0000_1E28 >> 2 = 0x0000_078A
    localparam logic [26:0]  POINTER_PHYS_WORD = 27'h000_078A;

    // Combinational peek of the retire ring slot holding the most
    // recent previous retire. Used by ENTRY14 to show call-site.
    int ring_prev_idx;
    always_comb begin
        ring_prev_idx = (retire_ring_head + RETIRE_RING_DEPTH - 1)
                        % RETIRE_RING_DEPTH;
    end

    // (A) first-entry into 0x14xx
    //
    // Two sources for the "previous retire" context:
    //   - retired_pc_d1 / retired_instr_d1: one-edge-delayed snapshot.
    //     Can be unreliable depending on NBA ordering between the TB
    //     sampler and the DUT's retire bookkeeping — verified in
    //     practice by the data (both "now" and "d1" can read the same
    //     value at the trigger edge).
    //   - retire ring slot (head-1): retire-gated — updated only
    //     when core_ev_valid fires, so slot (head-1) at the trigger
    //     always holds the immediately-previous retire. Authoritative.
    // Capture both and print both; the ring view is the call-site.
    logic        entry14_captured;
    int          entry14_cycle;
    logic [31:0] entry14_retired_pc;
    logic [31:0] entry14_retired_instr;
    logic [31:0] entry14_prev_retired_pc;       // via retired_pc_d1
    logic [31:0] entry14_prev_retired_instr;
    logic [31:0] entry14_ring_prev_pc;          // via retire ring head-1
    logic [31:0] entry14_ring_prev_instr;
    logic [31:0] entry14_r2;
    logic [31:0] entry14_r4;   // a0 — MIPS standard first arg
    logic [31:0] entry14_r5;
    logic [31:0] entry14_r6;
    logic [31:0] entry14_r7;   // a3 — last of the arg-passing regs
    logic [31:0] entry14_r28;  // gp — global pointer
    logic [31:0] entry14_r29;  // sp — stack pointer (if the routine is
                               //      set up as a real callee, it
                               //      should access its arg frame from here)
    logic [31:0] entry14_r30;  // fp — frame pointer
    logic [31:0] entry14_r31;  // ra — return address (already residual
                               //      from earlier ROM JAL per ch13)

    // Chapter 34(a) — ENTRY200 observer. After ch33 de-aliased
    // useg/kseg0, the real BIOS now advances to a trap at
    // retired_pc=0x0000_0200 where fetched bytes decode as opcode 0x2F
    // (CACHE/SDC2, not modeled). That value is a handler pointer
    // (0xBFC529B0) living in the low-RAM dispatch table — the core
    // reached 0x200 via direct branch/jump and fetched data-as-code.
    // This observer captures the single entry: retire ring head-1 and
    // head-2 (so both the delay-slot and the branch/jump site are
    // visible), live register snapshot covering the usual trampoline
    // regs ($8/$9/$26/$27/$31) plus the standard a0..a3 / gp/sp/fp,
    // and the head-2 instruction's opcode class (J/JAL/JR/JALR/other)
    // so we can tell whether the entry is a direct jump, a jr, or
    // looks exception-vector-like (same shape as ENTRY14).
    logic        entry200_captured;
    int          entry200_cycle;
    logic [31:0] entry200_retired_pc;
    logic [31:0] entry200_retired_instr;
    logic [31:0] entry200_prev_retired_pc;     // via retired_pc_d1
    logic [31:0] entry200_prev_retired_instr;
    logic [31:0] entry200_ring_prev_pc;        // head-1
    logic [31:0] entry200_ring_prev_instr;
    int          entry200_ring_prev_cyc;
    logic [31:0] entry200_ring_prev2_pc;       // head-2 (branch/jump site)
    logic [31:0] entry200_ring_prev2_instr;
    int          entry200_ring_prev2_cyc;
    logic [31:0] entry200_r2;
    logic [31:0] entry200_r4;
    logic [31:0] entry200_r5;
    logic [31:0] entry200_r6;
    logic [31:0] entry200_r7;
    logic [31:0] entry200_r8;    // candidate dispatch base / trampoline tmp
    logic [31:0] entry200_r9;    // candidate dispatch index / tmp
    logic [31:0] entry200_r26;   // $k0 — exception scratch (if this came
                                 //      via an exception path)
    logic [31:0] entry200_r27;   // $k1 — exception scratch (same)
    logic [31:0] entry200_r28;
    logic [31:0] entry200_r29;
    logic [31:0] entry200_r30;
    logic [31:0] entry200_r31;   // ra — if JAL/JALR landed here, $31 = pc+8

    // Chapter 35(a) — pc-window runway ring. Captures every retire
    // whose retired_pc lies in [0x1C0, 0x220] up to RUNWAY_DEPTH
    // entries. ENTRY200 showed ring-1/ring-2 (head-1/head-2) are
    // consecutive 0x1F8/0x1FC NOPs — so the originating jump into
    // this zero-filled runway is outside the 32-deep retire ring.
    // This window ring stays focused: only retires in the runway
    // are stored, so 280K+ warmup retires don't flush the buffer.
    // Entry [0] is the originating site (first retire in window);
    // the path to 0x200 then linearly fills [1], [2], ... At the
    // moment of the first window entry we also snapshot
    // retire_ring head-1..head-16 as RUNWAY_PREHIST — the call-site
    // chain that landed us in the runway.
    localparam int RUNWAY_DEPTH        = 64;
    localparam int RUNWAY_PREHIST_DEPTH = 16;
    localparam logic [31:0] RUNWAY_LOW  = 32'h0000_01C0;
    localparam logic [31:0] RUNWAY_HIGH = 32'h0000_0220;
    int          runway_count;
    int          runway_cyc   [0:RUNWAY_DEPTH-1];
    logic [31:0] runway_pc    [0:RUNWAY_DEPTH-1];
    logic [31:0] runway_instr [0:RUNWAY_DEPTH-1];
    logic [31:0] runway_r31   [0:RUNWAY_DEPTH-1];
    logic        runway_prehist_captured;
    int          runway_prehist_cyc   [0:RUNWAY_PREHIST_DEPTH-1];
    logic [31:0] runway_prehist_pc    [0:RUNWAY_PREHIST_DEPTH-1];
    logic [31:0] runway_prehist_instr [0:RUNWAY_PREHIST_DEPTH-1];

    // Chapter 36 — $31 transition-to-runway-$ra observer. The runway
    // evidence showed $31/ra frozen at 0xBFC5220C through 48+
    // consecutive NOP retires — so the originating link instruction
    // (JAL or JALR) is outside any ring we've widened. Instead,
    // trigger directly on the *first* retire edge where $31 changes
    // to the target value. That edge IS the originator.
    //
    // Capture: retired pc/instr/arg2/arg3, decoded rs/rt/rd, live
    // $8/$9 (trampoline context), prev-$31, and ring head-1/head-2.
    // Classify via opcode/func at print time: JAL (direct, target
    // from 26-bit imm), JALR (indirect via rs), or OTHER (explicit
    // mov/add to $31 — unusual but possible).
    localparam logic [31:0] RA_TARGET_VALUE = 32'hBFC5_220C;
    logic        r31_prev_valid;
    logic [31:0] r31_prev;
    logic        r31_to_target_captured;
    int          r31_to_target_cycle;
    logic [31:0] r31_to_target_retired_pc;
    logic [31:0] r31_to_target_retired_instr;
    logic [31:0] r31_to_target_retired_arg2;
    logic [31:0] r31_to_target_retired_arg3;
    logic [31:0] r31_to_target_prev_r31;
    logic [31:0] r31_to_target_r8;
    logic [31:0] r31_to_target_r9;
    logic [4:0]  r31_to_target_rs_idx;
    logic [4:0]  r31_to_target_rt_idx;
    logic [4:0]  r31_to_target_rd_idx;
    logic [31:0] r31_to_target_rs_val;
    logic [31:0] r31_to_target_ring_prev_pc;
    logic [31:0] r31_to_target_ring_prev_instr;
    logic [31:0] r31_to_target_ring_prev2_pc;
    logic [31:0] r31_to_target_ring_prev2_instr;

    // Chapter 37 — ROM-to-low-RAM escape observer. Ch36 pinned the
    // last link-producing JAL (pc=0xBFC52204, target=0xBFC58CC0 in
    // ROM). The actual escape into zero-filled low-RAM happens
    // later, inside/after the 0xBFC58CC0 subroutine, via a
    // non-linking control-flow op (no $31 change between cyc=287080
    // and the first runway retire at cyc=287288). This observer
    // triggers on the first retire AFTER R31_TO_TARGET fired whose
    // pc has top nibble zero (i.e. useg low-RAM). Captures the
    // retire itself, ring head-1/head-2 (delay slot + branch/jump
    // site), live $8/$9/$10/$31, and flags whether $31 is still
    // RA_TARGET_VALUE to reinforce the non-linking-escape reading.
    logic        rom_escape_captured;
    int          rom_escape_cycle;
    logic [31:0] rom_escape_retired_pc;
    logic [31:0] rom_escape_retired_instr;
    logic [31:0] rom_escape_ring_prev_pc;      // head-1 (delay slot)
    logic [31:0] rom_escape_ring_prev_instr;
    int          rom_escape_ring_prev_cyc;
    logic [31:0] rom_escape_ring_prev2_pc;     // head-2 (branch/jump site)
    logic [31:0] rom_escape_ring_prev2_instr;
    int          rom_escape_ring_prev2_cyc;
    logic [31:0] rom_escape_r8;
    logic [31:0] rom_escape_r9;
    logic [31:0] rom_escape_r10;
    logic [31:0] rom_escape_r31;
    logic        rom_escape_r31_unchanged;

    // Chapter 45 — generalized low-RAM escape provenance ring.
    // Ch37's rom_escape_* is a one-shot focused on the first
    // post-JAL escape. Ch44(a) showed there's a second low-RAM
    // escape later in the run that the one-shot misses; this ring
    // records every ROM→low-RAM retire transition after
    // r31_to_target_captured fires, up to ESCAPE_RING_DEPTH
    // entries.
    //
    // Per escape we keep:
    //   - landing pc / instr / cycle
    //   - retire-ring head-1 (delay slot) + head-2 (apparent jump
    //     site) for quick local context
    //   - live $8/$9/$10/$31 at the moment the low-RAM pc retires
    //   - walk-back result: the most recent retire in the ring
    //     whose opcode decodes as J/JAL/JR/JALR (unconditional
    //     control-flow — taken branches can't reach low-RAM from
    //     ROM via a 16-bit offset, so J-family is the only way the
    //     pc can cross), plus the number of ring-hops back from
    //     head-1 so it's obvious whether head-2 already was the
    //     driver or the real site sits further back behind a
    //     stretch of linear-NOP fall-through.
    //   - source-register index (rs_idx) if the walk-back landed
    //     on JR or JALR, so classification at print time can name
    //     "JR $8" / "JR $9" / "JR $10" / "JR $31" / "JALR via $rX"
    //     directly.
    localparam int ESCAPE_RING_DEPTH = 8;
    int          escape_ring_count;
    int          escape_ring_cyc       [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_pc        [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_instr     [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r1_pc     [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r1_instr  [0:ESCAPE_RING_DEPTH-1];
    int          escape_ring_r1_cyc    [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r2_pc     [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r2_instr  [0:ESCAPE_RING_DEPTH-1];
    int          escape_ring_r2_cyc    [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_cf_pc     [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_cf_instr  [0:ESCAPE_RING_DEPTH-1];
    int          escape_ring_cf_cyc    [0:ESCAPE_RING_DEPTH-1];
    int          escape_ring_cf_hops   [0:ESCAPE_RING_DEPTH-1];
    logic        escape_ring_cf_found  [0:ESCAPE_RING_DEPTH-1];
    logic [4:0]  escape_ring_cf_rs     [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r8        [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r9        [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r10       [0:ESCAPE_RING_DEPTH-1];
    logic [31:0] escape_ring_r31       [0:ESCAPE_RING_DEPTH-1];

    // Ch45 — $8 / $9 CHG lineage logs (mirror of the ch36 R10_CHG
    // block). Emits one $display per change across the full run.
    // Kept as support evidence for answering "which register drove
    // the escape?" after the escape ring names its candidate.
    logic        r8_prev_valid;
    logic [31:0] r8_prev;
    logic        r9_prev_valid;
    logic [31:0] r9_prev;

    // Ch48: $3 provenance observer. Ch47's alignment enforcement
    // showed the real-BIOS trap now fires at pc=0xBFC500A4 doing
    // `SW $0, -0x18($3)` with a $3 that produces a misaligned EA.
    // R3_CHG logs every $3 change with retired_pc/instr/arg2/arg3
    // so we can classify the producer (LW EA=arg2 data=arg3, or
    // ADDIU/ORI/LUI build from immediates, or ADDU/OR copy from
    // another register). R3_RING keeps a circular depth-8 record of
    // the most-recent $3 writes so the trap-halt dump can show the
    // last handful of producers, not only the first transition to
    // any one value.
    logic        r3_prev_valid;
    // ------------------------------------------------------------------
    // Ch180 — $1 ($at) provenance ring + ENTRY598 snapshot.
    //
    // The Ch179 autopsy showed the BIOS stuck in a structurally-broken
    // memset at PC=0x000005A8..0x000005B8 — body issues SW/ADDI but the
    // BNE checks $1, which is never modified in the loop. $1 at timeout
    // = 0xBF800000 (the EE BIU/MMIO base), which is not a sensible
    // loop count — strongly suggests $1 was either (a) loaded with the
    // wrong value before the call, or (b) corrupted by an upstream
    // instruction we're silently NOP'ing.
    //
    // This observer captures the last R1_RING_DEPTH writes to $1
    // (cycle + PC + instr + old/new + retired_pc_d1 + instr_d1) so the
    // post-timeout dump shows the provenance chain. Standalone ENTRY598
    // snapshot captures the full register state on the first retire at
    // PC=0x598 so we know whether $1 was already bad on function entry.
    // ------------------------------------------------------------------
    logic [31:0] r1_prev;
    logic        r1_prev_valid;
    localparam int R1_RING_DEPTH = 16;
    int          r1_ring_count;
    int          r1_ring_cyc      [0:R1_RING_DEPTH-1];
    logic [31:0] r1_ring_prev     [0:R1_RING_DEPTH-1];
    logic [31:0] r1_ring_new      [0:R1_RING_DEPTH-1];
    logic [31:0] r1_ring_pc       [0:R1_RING_DEPTH-1];
    logic [31:0] r1_ring_instr    [0:R1_RING_DEPTH-1];
    logic [31:0] r1_ring_pc_d1    [0:R1_RING_DEPTH-1];
    logic [31:0] r1_ring_instr_d1 [0:R1_RING_DEPTH-1];

    logic        entry598_captured;
    int          entry598_count;
    int          entry598_cycle;
    logic [31:0] entry598_r1, entry598_r2, entry598_r3, entry598_r4;
    logic [31:0] entry598_r5, entry598_r6, entry598_r7, entry598_r8;
    logic [31:0] entry598_r29, entry598_r30, entry598_r31;
    logic [31:0] entry598_ring_prev_pc, entry598_ring_prev_instr;
    logic [31:0] entry598_ring_prev2_pc, entry598_ring_prev2_instr;
    logic [31:0] entry598_ring_prev3_pc, entry598_ring_prev3_instr;
    logic [31:0] entry598_ring_prev4_pc, entry598_ring_prev4_instr;

    // Ch188 — BIOS_A0_SYSCALL_SHIM. Sim-only `jr $31; nop` installed
    // at PC=0xA0..0xA4 just before the first BIOS jalr-to-0xA0 fires.
    // PC=0xA0 is the PS2 BIOS internal syscall-trampoline entry
    // (`jalr $10` with $9 = syscall number, observed convention in
    // 30+ BIOS call sites scanned in Ch187). The trampoline itself
    // is never installed by this BIOS image — either it lives in
    // rom1/erom we don't load, or it's set up by an external SoC/IOP
    // path we don't model. The shim treats *every* A0 call as
    // no-op-and-return so the BIOS state machine can advance and we
    // can see the next blocker. Strict: log each call, flag any
    // function number other than 0x44 (the only one observed in our
    // current run).
    logic        ch188_shim_installed;
    logic        ch188_first_a0_logged;
    int          ch188_a0_call_count;

    // Ch193 — halt-point autopsy + exception ring. Per Codex's
    // Ch193 framing: "put a name on the clean halt." Capture the
    // exact instruction + COP0 state + arg regs at the SYSCALL-halt
    // event, and a depth-8 ring of EVERY exception that fires
    // during the run so we can see whether the run took 5 SYSCALLs
    // (one of which halted) or 5 mixed exceptions.
    logic        halt_point_captured;
    int          halt_point_cycle;
    logic [31:0] halt_point_pc;
    logic [31:0] halt_point_instr;
    logic [31:0] halt_point_epc, halt_point_cause, halt_point_status;
    logic [31:0] halt_point_v0, halt_point_v1;
    logic [31:0] halt_point_a0, halt_point_a1, halt_point_a2, halt_point_a3;
    logic [31:0] halt_point_ra;
    // Ch196 — autopsy of the exception context _ReturnFromException(2)
    // is trying to ERET from. BadVAddr is the EA that caused the last
    // AdES; $k0/$k1 are the kernel reservation regs that conventionally
    // point at the saved-context structure in PS2 kernels.
    logic [31:0] halt_point_badvaddr;
    logic [31:0] halt_point_k0, halt_point_k1;
    logic [31:0] halt_point_sp, halt_point_gp;

    localparam int EXC_RING_DEPTH = 8;
    int          exc_ring_count;
    int          exc_ring_cyc   [0:EXC_RING_DEPTH-1];
    logic [31:0] exc_ring_pc    [0:EXC_RING_DEPTH-1];
    logic [31:0] exc_ring_instr [0:EXC_RING_DEPTH-1];
    logic [31:0] exc_ring_cause [0:EXC_RING_DEPTH-1];
    logic [31:0] exc_ring_epc   [0:EXC_RING_DEPTH-1];
    logic [4:0]  exc_ring_excode[0:EXC_RING_DEPTH-1];
    logic        exc_ring_was_halt[0:EXC_RING_DEPTH-1];
    // Ch196 — BadVAddr per exception, so we can see the actual EA
    // each AdES faulted on. Plus a slot for "next retired PC after
    // this exception", which is filled by the post-fault tracker
    // below and tells us whether the handler skipped (EPC+4), retried
    // (EPC), or branched somewhere else (vector address).
    logic [31:0] exc_ring_badvaddr [0:EXC_RING_DEPTH-1];
    logic [31:0] exc_ring_next_pc  [0:EXC_RING_DEPTH-1];
    logic        exc_ring_next_pending [0:EXC_RING_DEPTH-1];
    logic        exc_ring_next_valid   [0:EXC_RING_DEPTH-1];

    // Ch195 — SYSCALL-handoff inspection. The Ch192 clean halt landed
    // at PC=0x8003EEC4 with $v1=0x08, $a0=0x02. To name what the BIOS
    // is asking for, we need (a) the instruction window around the
    // SYSCALL site, (b) the history of writes to the call-contract
    // registers ($v0=$2, $v1=$3, $a0=$4), and (c) the bytes pointed
    // to by $v0 in case it's a table/descriptor address. The three
    // CHG rings below mirror the Ch182/Ch183 R10/R31 ring pattern.
    localparam int CH195_RING_DEPTH = 8;
    logic [31:0] ch195_r2_prev;
    logic        ch195_r2_prev_valid;
    int          ch195_r2_count;
    int          ch195_r2_cyc   [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r2_pv    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r2_nv    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r2_pc    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r2_instr [0:CH195_RING_DEPTH-1];

    logic [31:0] ch195_r3_prev;
    logic        ch195_r3_prev_valid;
    int          ch195_r3_count;
    int          ch195_r3_cyc   [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r3_pv    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r3_nv    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r3_pc    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r3_instr [0:CH195_RING_DEPTH-1];

    logic [31:0] ch195_r4_prev;
    logic        ch195_r4_prev_valid;
    int          ch195_r4_count;
    int          ch195_r4_cyc   [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r4_pv    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r4_nv    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r4_pc    [0:CH195_RING_DEPTH-1];
    logic [31:0] ch195_r4_instr [0:CH195_RING_DEPTH-1];

    // Ch187 — observers for the PC=0xA0 mystery. The Ch186 capture
    // showed the RAM-dispatcher's `jalr $10=0xA0` lands in zero RAM
    // because 0xA0 was wiped by the cache-line clear. Codex Ch187
    // asks: is code EVER installed at 0xA0 after the clear?
    //
    //   (1) low_ram_wr_ring: every store landing in [0x80, 0xC0).
    //   (2) first_pc_a0: one-shot capture of first retire at PC=0xA0.
    //   (3) Snapshot of instr-words at {0x80, 0xA0, 0xC0, 0x500, 0x598}
    //       extended into the existing bad_jalr trigger (PC=0x3003C
    //       is where the jalr $10=0xA0 retires).
    localparam int LOW_RAM_WR_DEPTH = 32;
    int          low_ram_wr_count;
    int          low_ram_wr_cyc  [0:LOW_RAM_WR_DEPTH-1];
    logic [31:0] low_ram_wr_addr [0:LOW_RAM_WR_DEPTH-1];
    logic [31:0] low_ram_wr_data [0:LOW_RAM_WR_DEPTH-1];
    logic [31:0] low_ram_wr_pc   [0:LOW_RAM_WR_DEPTH-1];

    logic        first_pc_a0_captured;
    int          first_pc_a0_cycle;
    logic [31:0] first_pc_a0_instr;
    logic [31:0] first_pc_a0_r31;
    logic [31:0] first_pc_a0_r10;

    logic [31:0] badjalr_peek_080;
    logic [31:0] badjalr_peek_0a0;
    logic [31:0] badjalr_peek_0c0;
    logic [31:0] badjalr_peek_500;
    logic [31:0] badjalr_peek_598;

    // Ch193 — observers. Two: one ring of all exception events
    // (bit 5 of ev_flags) and one one-shot halt capture (bit 3).
    // Ch196 — extended: BadVAddr is captured on the exception cycle;
    // exc_ring_next_pc + _next_pending track the FIRST retire after
    // the exception so we can see whether the handler skipped/retried/
    // branched. Single-pending: we only track the most-recent
    // exception's follow-up retire, which is enough because the next
    // retire after AdES is always the handler entry (or the same PC
    // again if retried).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            exc_ring_count <= 0;
            for (int i = 0; i < EXC_RING_DEPTH; i++) begin
                exc_ring_next_pending[i] <= 1'b0;
                exc_ring_next_valid[i]   <= 1'b0;
                exc_ring_next_pc[i]      <= 32'd0;
                exc_ring_badvaddr[i]     <= 32'd0;
            end
        end else if (core_ev_valid && ((core_ev_flags & 32'h20) != 32'd0)) begin
            exc_ring_cyc     [exc_ring_count % EXC_RING_DEPTH] <= sim_cycle_ctr;
            exc_ring_pc      [exc_ring_count % EXC_RING_DEPTH] <= u_core.retired_pc;
            exc_ring_instr   [exc_ring_count % EXC_RING_DEPTH] <= u_core.retired_instr;
            exc_ring_cause   [exc_ring_count % EXC_RING_DEPTH] <= u_core.cause_word;
            exc_ring_epc     [exc_ring_count % EXC_RING_DEPTH] <= u_core.epc;
            exc_ring_excode  [exc_ring_count % EXC_RING_DEPTH] <= u_core.cause_word[6:2];
            exc_ring_was_halt[exc_ring_count % EXC_RING_DEPTH] <= ((core_ev_flags & 32'h08) != 32'd0);
            exc_ring_badvaddr[exc_ring_count % EXC_RING_DEPTH] <= u_core.badvaddr;
            exc_ring_next_pending[exc_ring_count % EXC_RING_DEPTH] <= 1'b1;
            exc_ring_next_valid  [exc_ring_count % EXC_RING_DEPTH] <= 1'b0;
            exc_ring_count <= exc_ring_count + 1;
        end else if (core_ev_valid) begin
            // Fill any pending "next-PC" slots on the NEXT retire that
            // isn't itself an exception event. There can in principle be
            // more than one pending if back-to-back exceptions fire, but
            // since AdES events in this run are 100s of cycles apart we
            // only ever see one pending at a time.
            for (int j = 0; j < EXC_RING_DEPTH; j++) begin
                if (exc_ring_next_pending[j] && !exc_ring_next_valid[j]) begin
                    exc_ring_next_pc[j]      <= u_core.retired_pc;
                    exc_ring_next_pending[j] <= 1'b0;
                    exc_ring_next_valid[j]   <= 1'b1;
                end
            end
        end
    end

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            halt_point_captured <= 1'b0;
        end else if (!halt_point_captured && core_ev_valid &&
                     ((core_ev_flags & 32'h08) != 32'd0)) begin
            halt_point_captured <= 1'b1;
            halt_point_cycle    <= sim_cycle_ctr;
            halt_point_pc       <= u_core.retired_pc;
            halt_point_instr    <= u_core.retired_instr;
            halt_point_epc      <= u_core.epc;
            halt_point_cause    <= u_core.cause_word;
            halt_point_status   <= u_core.status_word;
            halt_point_v0       <= u_core.regfile[2];
            halt_point_v1       <= u_core.regfile[3];
            halt_point_a0       <= u_core.regfile[4];
            halt_point_a1       <= u_core.regfile[5];
            halt_point_a2       <= u_core.regfile[6];
            halt_point_a3       <= u_core.regfile[7];
            halt_point_ra       <= u_core.regfile[31];
            halt_point_badvaddr <= u_core.badvaddr;
            halt_point_k0       <= u_core.regfile[26];
            halt_point_k1       <= u_core.regfile[27];
            halt_point_sp       <= u_core.regfile[29];
            halt_point_gp       <= u_core.regfile[28];
            $display("[tb_ee_core_bios_smoke] HALT_POINT captured cyc=%0d retired_pc=0x%08h retired_instr=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr);
        end
    end

    // Ch201 — post-RFE progress autopsy. iverilog 12 doesn't support
    // assoc arrays with `logic` keys, so we use fixed-size arrays and
    // a single-PC streak tracker. The streak tracker catches simple
    // spinloops (where one PC repeats consecutively); a small fixed
    // PC-slot table catches a few hot PCs across the run; per-byte
    // arrays cover A0/B0/C0 syscall functions and exception codes.
    int          ch201_post_rfe_retires;
    int          ch201_post_rfe_loads;
    int          ch201_post_rfe_mmio_reads;       // EA >= 0x1F800000
    int          ch201_post_rfe_exceptions;
    int          ch201_post_rfe_a0_calls;
    int          ch201_post_rfe_b0_calls;
    int          ch201_post_rfe_c0_calls;

    // Per-byte fixed-size histograms (256 entries each), zero-init.
    int          ch201_a0_funcs    [0:255];
    int          ch201_b0_funcs    [0:255];
    int          ch201_c0_funcs    [0:255];
    int          ch201_excode_hist [0:31];

    // Streak tracker: how many consecutive retires at one PC.
    logic [31:0] ch201_streak_pc;
    int          ch201_streak_len;
    int          ch201_max_streak_len;
    logic [31:0] ch201_max_streak_pc;

    // Small hot-PC table: 16 slots, LRU-style replacement. Captures
    // multi-PC small loops where the streak tracker misses (e.g. a
    // 4-instr loop body cycling through 4 PCs — each PC's streak is 1
    // but it dominates total retires).
    localparam int CH201_HOTPC_SLOTS = 16;
    logic [31:0] ch201_hot_pc_addr  [0:CH201_HOTPC_SLOTS-1];
    int          ch201_hot_pc_count [0:CH201_HOTPC_SLOTS-1];

    localparam int CH201_LAST_DEPTH = 64;
    int          ch201_last_count;
    int          ch201_last_head;
    logic [31:0] ch201_last_pc    [0:CH201_LAST_DEPTH-1];
    logic [31:0] ch201_last_instr [0:CH201_LAST_DEPTH-1];

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch201_post_rfe_retires    <= 0;
            ch201_post_rfe_loads      <= 0;
            ch201_post_rfe_mmio_reads <= 0;
            ch201_post_rfe_exceptions <= 0;
            ch201_post_rfe_a0_calls   <= 0;
            ch201_post_rfe_b0_calls   <= 0;
            ch201_post_rfe_c0_calls   <= 0;
            ch201_last_count <= 0;
            ch201_last_head  <= 0;
            ch201_streak_pc       <= 32'd0;
            ch201_streak_len      <= 0;
            ch201_max_streak_len  <= 0;
            ch201_max_streak_pc   <= 32'd0;
            for (int i = 0; i < CH201_HOTPC_SLOTS; i++) begin
                ch201_hot_pc_addr [i] <= 32'd0;
                ch201_hot_pc_count[i] <= 0;
            end
            for (int i = 0; i < 256; i++) begin
                ch201_a0_funcs[i] <= 0;
                ch201_b0_funcs[i] <= 0;
                ch201_c0_funcs[i] <= 0;
            end
            for (int i = 0; i < 32; i++) ch201_excode_hist[i] <= 0;
        end else if (ch199_consume_count > 0) begin
            if (core_ev_valid) begin
                int found_slot;
                int lru_slot;
                int lru_count;
                ch201_post_rfe_retires <= ch201_post_rfe_retires + 1;

                // Streak update
                if (u_core.retired_pc == ch201_streak_pc) begin
                    ch201_streak_len <= ch201_streak_len + 1;
                    if (ch201_streak_len + 1 > ch201_max_streak_len) begin
                        ch201_max_streak_len <= ch201_streak_len + 1;
                        ch201_max_streak_pc  <= ch201_streak_pc;
                    end
                end else begin
                    ch201_streak_pc  <= u_core.retired_pc;
                    ch201_streak_len <= 1;
                end

                // Hot-PC table update: find a matching slot, else evict
                // the slot with smallest count. Use a search variable.
                found_slot = -1;
                lru_slot   = 0;
                lru_count  = ch201_hot_pc_count[0];
                for (int j = 0; j < CH201_HOTPC_SLOTS; j++) begin
                    if (found_slot < 0
                            && ch201_hot_pc_addr[j] == u_core.retired_pc
                            && ch201_hot_pc_count[j] > 0)
                        found_slot = j;
                    if (ch201_hot_pc_count[j] < lru_count) begin
                        lru_count = ch201_hot_pc_count[j];
                        lru_slot  = j;
                    end
                end
                if (found_slot >= 0) begin
                    ch201_hot_pc_count[found_slot]
                        <= ch201_hot_pc_count[found_slot] + 1;
                end else begin
                    ch201_hot_pc_addr [lru_slot] <= u_core.retired_pc;
                    ch201_hot_pc_count[lru_slot] <= 1;
                end

                // Last-64 ring
                ch201_last_pc   [ch201_last_head] <= u_core.retired_pc;
                ch201_last_instr[ch201_last_head] <= u_core.retired_instr;
                ch201_last_head <= (ch201_last_head + 1) % CH201_LAST_DEPTH;
                if (ch201_last_count < CH201_LAST_DEPTH)
                    ch201_last_count <= ch201_last_count + 1;

                // A0/B0/C0 dispatcher entries
                if (u_core.retired_pc == 32'h0000_00A0) begin
                    ch201_post_rfe_a0_calls <= ch201_post_rfe_a0_calls + 1;
                    ch201_a0_funcs[u_core.regfile[9][7:0]]
                        <= ch201_a0_funcs[u_core.regfile[9][7:0]] + 1;
                end
                if (u_core.retired_pc == 32'h0000_00B0) begin
                    ch201_post_rfe_b0_calls <= ch201_post_rfe_b0_calls + 1;
                    ch201_b0_funcs[u_core.regfile[9][7:0]]
                        <= ch201_b0_funcs[u_core.regfile[9][7:0]] + 1;
                end
                if (u_core.retired_pc == 32'h0000_00C0) begin
                    ch201_post_rfe_c0_calls <= ch201_post_rfe_c0_calls + 1;
                    ch201_c0_funcs[u_core.regfile[9][7:0]]
                        <= ch201_c0_funcs[u_core.regfile[9][7:0]] + 1;
                end

                if ((core_ev_flags & 32'h20) != 32'd0) begin
                    ch201_post_rfe_exceptions <= ch201_post_rfe_exceptions + 1;
                    ch201_excode_hist[u_core.cause_word[6:2]]
                        <= ch201_excode_hist[u_core.cause_word[6:2]] + 1;
                end
            end

            // Load EA tracking (no per-EA histogram — just totals).
            if (ee_rd_en && u_core.state == 4'd4) begin
                ch201_post_rfe_loads <= ch201_post_rfe_loads + 1;
                if (ee_rd_addr >= 32'h1F80_0000 && ee_rd_addr < 32'h1FC0_0000)
                    ch201_post_rfe_mmio_reads <= ch201_post_rfe_mmio_reads + 1;
            end
        end
    end

    // Ch200 — shared print throttle for the lineage observers. Before
    // Ch199 the BIOS halted at ~10.2M cyc so each per-retire observer
    // (R2_SEED, R3_CHG, R10_CHG, R31_CHG) fired bounded times. After
    // Ch199 the BIOS keeps running for the full watchdog and the
    // observers blow up the log (611 MB in 50 min on tb_ee_core_bios_long).
    // The fix is purely cosmetic: cap each family's $display at the
    // first CH200_PRINT_CAP firings and tally the suppressed count for
    // the end-of-run summary. Ring writes (which feed the authoritative
    // newest-first dumps) keep firing — only the inline prints throttle.
    localparam int CH200_PRINT_CAP = 128;
    int ch200_r2_prints;
    int ch200_r3_prints;
    int ch200_r10_prints;
    int ch200_r31_prints;
    int ch200_suppressed;

    // Ch217 — inspect the longjmp-return path. For each pass through
    // PC=0xBFC52358 (the JAL at the post-setjmp "longjmp-returned"
    // branch), capture $a0/$a1/$a2/$a3/$ra at call entry plus $v0/$v1
    // at PC=0xBFC52360 (= post-delay-slot retire = the call's return
    // landing). Comparison across passes reveals whether the call's
    // input/output mutates (= progress) or stays identical (= static
    // state, treadmill).
    localparam int CH217_MAX = 8;
    int          ch217_count;
    int          ch217_cyc       [0:CH217_MAX-1];
    logic [31:0] ch217_a0_in     [0:CH217_MAX-1];
    logic [31:0] ch217_a1_in     [0:CH217_MAX-1];
    logic [31:0] ch217_a2_in     [0:CH217_MAX-1];
    logic [31:0] ch217_a3_in     [0:CH217_MAX-1];
    logic [31:0] ch217_ra_in     [0:CH217_MAX-1];
    logic [31:0] ch217_v0_before [0:CH217_MAX-1];
    logic [31:0] ch217_v0_after  [0:CH217_MAX-1];
    logic [31:0] ch217_v1_after  [0:CH217_MAX-1];
    int          ch217_pending_idx;
    logic        ch217_pending;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch217_count       <= 0;
            ch217_pending     <= 1'b0;
            ch217_pending_idx <= 0;
        end else if (core_ev_valid) begin
            // Capture JAL entry
            if (u_core.retired_pc == 32'hBFC5_2358
                    && ch217_count < CH217_MAX) begin
                ch217_cyc      [ch217_count] <= sim_cycle_ctr;
                ch217_a0_in    [ch217_count] <= u_core.regfile[4];
                ch217_a1_in    [ch217_count] <= u_core.regfile[5];
                ch217_a2_in    [ch217_count] <= u_core.regfile[6];
                ch217_a3_in    [ch217_count] <= u_core.regfile[7];
                ch217_ra_in    [ch217_count] <= u_core.regfile[31];
                ch217_v0_before[ch217_count] <= u_core.regfile[2];
                ch217_pending_idx <= ch217_count;
                ch217_pending     <= 1'b1;
                ch217_count       <= ch217_count + 1;
            end
            // Capture post-call landing (retire at $ra = 0xBFC52360)
            if (ch217_pending && u_core.retired_pc == 32'hBFC5_2360) begin
                ch217_v0_after[ch217_pending_idx] <= u_core.regfile[2];
                ch217_v1_after[ch217_pending_idx] <= u_core.regfile[3];
                ch217_pending <= 1'b0;
            end
        end
    end

    // Ch260 — Ch218 observer is now opt-in via `define CH259_INTC_DIAG.
    // The arc closed at Ch259: BIOS treadmill requires multi-state
    // IOP/SBUS/kernel activity, not another single-register hardcode.
    // The compact INTC transaction log is preserved here for any
    // future BIOS-mmio probe revival but stays quiet on routine runs
    // (`make tb_ee_core_bios_long` no longer emits Ch218 lines).
    //
    // To re-enable for ad-hoc diagnostics:
    //   make tb_ee_core_bios_long_intc_diag BIOS=...
    // which compiles with `-DCH259_INTC_DIAG`. The plusarg
    // `+IOP_INTC_BOOT_SRC=<hex16>` continues to drive
    // iop_intc_inject_src_i regardless of the diag mode.
`ifdef CH259_INTC_DIAG
    // Ch218 — scoped observer for memory reads inside the Ch217 JAL
    // callee body (the "longjmp-return-handler" reached from
    // PC=0xBFC52358). Ch217's verdict identified that the callee
    // returns identical $v0 across passes — meaning some address it
    // polls returns the same value every pass. This observer captures
    // every EE memory READ whose requesting PC lies in
    // [jal_target, jal_target+0x80), with pass-index attribution from
    // ch217_count, so a post-run comparison can name the static EA.
    //
    // jal_target is decoded dynamically from peek_instr(0xBFC52358):
    //   PC+4 = 0xBFC5235C → upper 4 bits 0xB → target =
    //   {4'hB, instr[25:0], 2'b00}. In synthetic CI mode the JAL is
    //   never reached, peek_instr returns whatever is at that
    //   address (often zero), and the capture stays empty — the
    //   observer is dormant for the regression's hot path.
    //
    // Pure diagnostic add. No production-RTL change, no behavior
    // change to existing assertions, no new regression dependency.
    // jal_target is latched on the FIRST retire at 0xBFC52358 — that
    // is the earliest moment we know BIOS bytes are live (continuous
    // assignment from peek_instr evaluates at time-0 before $readmemh
    // fires, which previously latched 0 and produced a bogus
    // jal_target of 0xB0000000).
    logic [31:0] ch218_jal_word_latched;
    logic        ch218_jal_known;
    wire  [31:0] ch218_jal_target = { 4'hB, ch218_jal_word_latched[25:0], 2'b00 };
    // Initial Ch218 narrowed capture to [jal_target, jal_target+0x80)
    // but the visible callee body just wraps an INNER JAL to
    // 0xbfc4d370 — the actual kernel-poll state lives there or
    // deeper, and the body-window filter excluded all of it. The
    // observer now captures EVERY non-fetch EE memory read AFTER
    // the first JAL fire (= treadmill has started). Instruction
    // fetches are filtered by EA == core_pc; what's left is the
    // signal we care about.
    wire ch218_capture_active = ch218_jal_known;
    wire ch218_is_fetch       = (ee_map_ev_arg0[31:0] == core_pc);

    // Ch259 — tighten the observer to ONLY the IOP INTC pair (0x1F801070
    // I_STAT and 0x1F801074 I_MASK). v7's broad capture surfaced the
    // INTC pair as the dominant cross-pass polling target (56 reads
    // out of 172 total cross-pass captures); per Codex's Ch259 framing
    // the broad fishing-net observer has done its job and we now want
    // a compact transaction log of just these two registers — reads
    // and writes both, with the actual value carried through.
    //
    // Predicate matches the physical IOP INTC EAs exactly. The EE
    // memory map strips kseg bits before emitting the trace event so
    // the seen EA for both 0xBF801070 and 0x1F801070 accesses is
    // 0x1F801070 — verified in the Phase-1 capture data. Earlier
    // versions of this predicate matched on the low 16 bits only and
    // caught false positives from EE-RAM scans whose offsets ended
    // in 0x1070/0x1074.
    wire ch218_is_intc       = (ee_map_ev_arg0[31:0] == 32'h1F80_1070)
                            || (ee_map_ev_arg0[31:0] == 32'h1F80_1074);
    // Module-scope mirror of the live instruction at core_pc so we can
    // bit-select rt[20:16] inside always_ff — iverilog 12 rejects
    // `peek_instr(core_pc)[20:16]` (no bit-select on function return).
    wire [31:0] ch218_live_instr = peek_instr(core_pc);

    // Ch259 — compact INTC transaction buffer. 256 slots is plenty for
    // 8 treadmill passes × ~10 INTC accesses each. Added is_write
    // column so we can render the stream as an ordered R/W log. data
    // column carries actual returned read value for INTC offsets by
    // sampling iop_intc_*_q via hierarchical name (ee_map_ev_arg1 is
    // hardcoded zero for EV_READ events — see Ch258 brief — so we
    // can't trust it for the data column at the map level).
    localparam int CH218_DEPTH = 256;
    int          ch218_count;
    int          ch218_pass_idx [0:CH218_DEPTH-1];
    logic [31:0] ch218_pc       [0:CH218_DEPTH-1];
    logic [31:0] ch218_instr    [0:CH218_DEPTH-1];
    logic [31:0] ch218_ea       [0:CH218_DEPTH-1];
    logic [31:0] ch218_data     [0:CH218_DEPTH-1];
    logic [4:0]  ch218_rt       [0:CH218_DEPTH-1];
    logic        ch218_is_wr_a  [0:CH218_DEPTH-1];

    // Sample the named INTC state via hierarchical name. These are
    // the post-clk values; for a same-cycle read, this captures what
    // the read just received (the read mux uses these state regs).
    wire [31:0] ch218_intc_stat_now = {16'd0,
        u_misc_mmio.iop_intc_stat_q | u_misc_mmio.iop_intc_inject_src_i};
    wire [31:0] ch218_intc_mask_now = {16'd0, u_misc_mmio.iop_intc_mask_q};

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch218_count            <= 0;
            ch218_jal_word_latched <= 32'd0;
            ch218_jal_known        <= 1'b0;
        end else begin
            // Latch jal_target on first JAL fire so peek_instr reads
            // the live BIOS bytes (not the pre-$readmemh zeros).
            if (!ch218_jal_known
                    && core_ev_valid
                    && u_core.retired_pc == 32'hBFC5_2358) begin
                ch218_jal_word_latched <= peek_instr(32'hBFC5_2358);
                ch218_jal_known        <= 1'b1;
            end

            // Ch259 — capture READS to the INTC pair (post-JAL, non-
            // fetch). Data column gets the actual returned value
            // sampled from the named state regs.
            if (ee_map_ev_valid
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ee_map_ev_event  == trace_pkg::EV_READ
                  && ch218_capture_active
                  && !ch218_is_fetch
                  && ch218_is_intc
                  && ch218_count < CH218_DEPTH) begin
                ch218_pass_idx[ch218_count] <= ch217_count;
                ch218_pc      [ch218_count] <= core_pc;
                ch218_instr   [ch218_count] <= ch218_live_instr;
                ch218_ea      [ch218_count] <= ee_map_ev_arg0[31:0];
                ch218_data    [ch218_count] <= (ee_map_ev_arg0[31:0] == 32'h1F80_1070)
                                             ? ch218_intc_stat_now
                                             : ch218_intc_mask_now;
                ch218_rt      [ch218_count] <= ch218_live_instr[20:16];
                ch218_is_wr_a [ch218_count] <= 1'b0;
                ch218_count <= ch218_count + 1;
            end
            // Ch259 — also capture WRITES to the INTC pair so the
            // ordered log shows BIOS's W1C acks and mask writes.
            else if (ee_map_ev_valid
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ee_map_ev_event  == trace_pkg::EV_WRITE
                  && ch218_capture_active
                  && ch218_is_intc
                  && ch218_count < CH218_DEPTH) begin
                ch218_pass_idx[ch218_count] <= ch217_count;
                ch218_pc      [ch218_count] <= core_pc;
                ch218_instr   [ch218_count] <= ch218_live_instr;
                ch218_ea      [ch218_count] <= ee_map_ev_arg0[31:0];
                ch218_data    [ch218_count] <= ee_map_ev_arg1[31:0]; // write data IS carried
                ch218_rt      [ch218_count] <= ch218_live_instr[20:16];
                ch218_is_wr_a [ch218_count] <= 1'b1;
                ch218_count <= ch218_count + 1;
            end  // close capture if/else chain
        end      // close `else begin` (the non-reset branch)
    end
`endif // CH259_INTC_DIAG

    // Ch216 — periodicity/progress classification for the 5 Ch215
    // restores observed in the BIOS-long run. For each restore, snap
    // the post-restore $ra/$sp/$fp/$gp/$v0 + the next 4 retired PCs.
    // Pending flag arms on the consume_syscall RFE event; the NEXT
    // valid retire is the resume target (= the loaded $ra-pc), and
    // 3 more retires after that complete the post-restore trace.
    localparam int CH216_MAX = 8;
    int          ch216_count;
    int          ch216_post_step;
    logic        ch216_pending;
    int          ch216_cyc  [0:CH216_MAX-1];
    logic [31:0] ch216_ra   [0:CH216_MAX-1];
    logic [31:0] ch216_sp   [0:CH216_MAX-1];
    logic [31:0] ch216_fp   [0:CH216_MAX-1];
    logic [31:0] ch216_gp   [0:CH216_MAX-1];
    logic [31:0] ch216_v0   [0:CH216_MAX-1];
    logic [31:0] ch216_pc0  [0:CH216_MAX-1];
    logic [31:0] ch216_pc1  [0:CH216_MAX-1];
    logic [31:0] ch216_pc2  [0:CH216_MAX-1];
    logic [31:0] ch216_pc3  [0:CH216_MAX-1];

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch216_count    <= 0;
            ch216_pending  <= 1'b0;
            ch216_post_step <= 0;
        end else begin
            // Arm on consume_syscall (RFE bit + SYSCALL opcode).
            if (core_ev_valid
                && ((core_ev_flags & 32'h40) != 32'd0)
                && core_ev_arg1[31:26] == 6'h00
                && core_ev_arg1[5:0]   == 6'h0C
                && ch216_count < CH216_MAX) begin
                ch216_pending   <= 1'b1;
                ch216_post_step <= 0;
            end else if (ch216_pending && core_ev_valid) begin
                if (ch216_post_step == 0) begin
                    ch216_cyc[ch216_count] <= sim_cycle_ctr;
                    ch216_ra [ch216_count] <= u_core.regfile[31];
                    ch216_sp [ch216_count] <= u_core.regfile[29];
                    ch216_fp [ch216_count] <= u_core.regfile[30];
                    ch216_gp [ch216_count] <= u_core.regfile[28];
                    ch216_v0 [ch216_count] <= u_core.regfile[2];
                    ch216_pc0[ch216_count] <= u_core.retired_pc;
                end else if (ch216_post_step == 1)
                    ch216_pc1[ch216_count] <= u_core.retired_pc;
                else if (ch216_post_step == 2)
                    ch216_pc2[ch216_count] <= u_core.retired_pc;
                else if (ch216_post_step == 3) begin
                    ch216_pc3[ch216_count] <= u_core.retired_pc;
                    ch216_count   <= ch216_count + 1;
                    ch216_pending <= 1'b0;
                end
                ch216_post_step <= ch216_post_step + 1;
            end
        end
    end

    // Ch214 — decode setjmp call site to find frame-pointer origin.
    // Two one-shot observers + static decode + BIOS-wide pattern scan.
    //
    //   (a) setjmp call (PC=0xBFC4DB50): capture caller PC (= $ra at
    //       entry), $a0/$gp/$sp at entry, total invocation count.
    //   (b) longjmp call (PC=0xBFC4DB8C): same fields. Tells us
    //       whether BIOS ever explicitly calls longjmp (vs the kernel
    //       handler doing it implicitly via syscall #8).
    //   (c) Static decode of 0xBFC52340..0xBFC52360 (the setjmp call
    //       site at PC=0xBFC52348 = $ra-8) so we can see how the
    //       caller computed $a0=0xA000B1E0 — gp-relative, lui+ori, or
    //       lui+addiu pattern.
    //   (d) BIOS-wide pattern search for 0xA000B1E0 constants. Looks
    //       for both `lui ..., 0xA000`+`ori ..., 0xB1E0` and
    //       `lui ..., 0xA001`+`addiu ..., -0x4E20` (signed) and
    //       gp-relative `addiu ..., $gp, -0x5690` patterns.
    logic        ch214_save_seen;
    int          ch214_save_cyc;
    logic [31:0] ch214_save_caller_pc;
    logic [31:0] ch214_save_a0, ch214_save_gp, ch214_save_sp, ch214_save_ra;
    int          ch214_save_count;
    logic        ch214_restore_seen;
    int          ch214_restore_cyc;
    logic [31:0] ch214_restore_caller_pc;
    logic [31:0] ch214_restore_a0, ch214_restore_gp;
    int          ch214_restore_count;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch214_save_seen    <= 1'b0;
            ch214_save_count   <= 0;
            ch214_restore_seen <= 1'b0;
            ch214_restore_count <= 0;
        end else if (core_ev_valid) begin
            if (u_core.retired_pc == 32'hBFC4_DB50) begin
                if (!ch214_save_seen) begin
                    ch214_save_seen      <= 1'b1;
                    ch214_save_cyc       <= sim_cycle_ctr;
                    ch214_save_caller_pc <= u_core.regfile[31];
                    ch214_save_a0        <= u_core.regfile[4];
                    ch214_save_gp        <= u_core.regfile[28];
                    ch214_save_sp        <= u_core.regfile[29];
                    ch214_save_ra        <= u_core.regfile[31];
                end
                ch214_save_count <= ch214_save_count + 1;
            end
            if (u_core.retired_pc == 32'hBFC4_DB8C) begin
                if (!ch214_restore_seen) begin
                    ch214_restore_seen      <= 1'b1;
                    ch214_restore_cyc       <= sim_cycle_ctr;
                    ch214_restore_caller_pc <= u_core.regfile[31];
                    ch214_restore_a0        <= u_core.regfile[4];
                    ch214_restore_gp        <= u_core.regfile[28];
                end
                ch214_restore_count <= ch214_restore_count + 1;
            end
        end
    end

    // Ch213 — investigate frame-pointer source at SYSCALL #8 before
    // implementing restore. Per Codex's guardrail: don't hardcode
    // 0xA000B1E0 until we prove how the kernel handler finds it.
    //
    //   (a) At 2nd SYSCALL #8 retire, scan all 32 GPRs for value
    //       0xA000B1E0 (or its KSEG0 alias 0x8000B1E0) — names the
    //       reg that carries the frame pointer, if any.
    //   (b) Search the existing Ch211 dstore ring (data in stack
    //       range) for stores of 0xA000B1E0 value — but actually
    //       0xA000B1E0 doesn't match stack range; need to scan for
    //       0xA000B1E0 specifically. Use a small one-shot search.
    //   (c) Static-decode 0xBFC4DB30..0xBFC4DB50 — the code just
    //       before the SAVE entry. Whoever JALs into setjmp passes
    //       $a0=0xA000B1E0; the addiu/lui that computes 0xA000B1E0
    //       lives nearby.
    logic        ch213_sc8_seen;
    int          ch213_sc8_cyc;
    logic [31:0] ch213_sc8_regs [0:31];
    int          ch213_sc8_match_reg;   // -1 if none, else 0..31

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch213_sc8_seen <= 1'b0;
            ch213_sc8_match_reg <= -1;
        end else if (!ch213_sc8_seen && core_ev_valid
                     && ((core_ev_flags & 32'h40) != 32'd0)   // RFE bit (Ch199 consume)
                     && ch199_consume_count >= 1) begin
            // Capture on the FIRST RFE consume to study the frame
            // visibility. (We could pick 2nd but earlier means more
            // chances to spot the pointer-bearing register.)
            ch213_sc8_seen <= 1'b1;
            ch213_sc8_cyc  <= sim_cycle_ctr;
            for (int i = 0; i < 32; i++)
                ch213_sc8_regs[i] <= u_core.regfile[i];
            // Live-search: which register holds 0xA000B1E0 (or 0x8000B1E0)?
            // (Use a single iteration loop to record first hit.)
        end
    end

    // Search the store ring (Ch205's ch205_store_*) for any write
    // with data == 0xA000B1E0 — that gives us the PC that wrote the
    // pointer to memory, OR the addiu sequence that computed it.
    // We do this at end-of-run; no observer needed since the ring
    // already exists.

    // Ch212 — field-map confirmation for the context frame at phys
    // 0x0000_B1E0..0x0000_B230 (accessed via KSEG1 = 0xA000_B1E0). For
    // every SW retire whose EA falls in that physical range, record
    // cyc/PC/instr/EA/data + the source GPR index (rt[20:16]) so we
    // can map "offset → which register's value got saved". Then at
    // end-of-run, summarise the field map and dump the BIOS code at
    // PC=0xBFC4DB58 (the save routine — observed in Ch210/Ch211 to be
    // the source PC of the dynamic save) plus PC=0xBFC52350 (= value
    // currently at offset +0x00 — to identify whether that field is
    // a saved-PC, function pointer, or metadata).
    localparam int CH212_FSTORE_DEPTH = 64;
    int          ch212_fstore_count;
    int          ch212_fstore_cyc  [0:CH212_FSTORE_DEPTH-1];
    logic [31:0] ch212_fstore_pc   [0:CH212_FSTORE_DEPTH-1];
    logic [31:0] ch212_fstore_ea   [0:CH212_FSTORE_DEPTH-1];
    logic [31:0] ch212_fstore_data [0:CH212_FSTORE_DEPTH-1];
    logic [31:0] ch212_fstore_instr[0:CH212_FSTORE_DEPTH-1];
    logic [4:0]  ch212_fstore_rt   [0:CH212_FSTORE_DEPTH-1];

    always_ff @(posedge clk) begin
        if (!rst_n) ch212_fstore_count <= 0;
        else if (core_ev_valid
                 && core_ev_arg1[31:26] == 6'h2B           // SW
                 && core_ev_arg2[28:0] >= 29'h000_B1E0
                 && core_ev_arg2[28:0] <  29'h000_B230) begin
            ch212_fstore_cyc  [ch212_fstore_count % CH212_FSTORE_DEPTH] <= sim_cycle_ctr;
            ch212_fstore_pc   [ch212_fstore_count % CH212_FSTORE_DEPTH] <= u_core.retired_pc;
            ch212_fstore_ea   [ch212_fstore_count % CH212_FSTORE_DEPTH] <= core_ev_arg2[31:0];
            ch212_fstore_data [ch212_fstore_count % CH212_FSTORE_DEPTH] <= core_ev_arg3[31:0];
            ch212_fstore_instr[ch212_fstore_count % CH212_FSTORE_DEPTH] <= core_ev_arg1[31:0];
            ch212_fstore_rt   [ch212_fstore_count % CH212_FSTORE_DEPTH] <= core_ev_arg1[20:16];
            ch212_fstore_count <= ch212_fstore_count + 1;
        end
    end

    // Ch211 — final widened context-pattern search before deciding
    // whether to stop the RFE-restore line. Three observers:
    //  (a) `ch211_dstore_*` — every EE store whose DATA falls in the
    //      user-stack range [0x801FFD00, 0x801FFE20]. Wider net than
    //      Ch210's exact-value match; catches any save of a stack
    //      pointer to memory, regardless of which exact value.
    //  (b) `ch211_kseg1_*` — every store to KSEG1 scratch
    //      [0xA0000000, 0xA0010000). Real PS2 may use this region
    //      for kernel-context save (uncached scratch).
    //  (c) `ch211_lowram_*` — every store to phys [0x00000000,
    //      0x00010000) via either KSEG0 or KSEG1. Low-RAM kernel
    //      area where the AdES handler at PC=0x80 lives.
    localparam int CH211_DSTORE_DEPTH = 128;
    int          ch211_dstore_count;
    int          ch211_dstore_cyc  [0:CH211_DSTORE_DEPTH-1];
    logic [31:0] ch211_dstore_pc   [0:CH211_DSTORE_DEPTH-1];
    logic [31:0] ch211_dstore_ea   [0:CH211_DSTORE_DEPTH-1];
    logic [31:0] ch211_dstore_data [0:CH211_DSTORE_DEPTH-1];

    localparam int CH211_REGSTORE_DEPTH = 64;
    int          ch211_kseg1_count;
    int          ch211_kseg1_cyc   [0:CH211_REGSTORE_DEPTH-1];
    logic [31:0] ch211_kseg1_pc    [0:CH211_REGSTORE_DEPTH-1];
    logic [31:0] ch211_kseg1_ea    [0:CH211_REGSTORE_DEPTH-1];
    logic [31:0] ch211_kseg1_data  [0:CH211_REGSTORE_DEPTH-1];

    int          ch211_lowram_count;
    int          ch211_lowram_cyc  [0:CH211_REGSTORE_DEPTH-1];
    logic [31:0] ch211_lowram_pc   [0:CH211_REGSTORE_DEPTH-1];
    logic [31:0] ch211_lowram_ea   [0:CH211_REGSTORE_DEPTH-1];
    logic [31:0] ch211_lowram_data [0:CH211_REGSTORE_DEPTH-1];

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch211_dstore_count <= 0;
            ch211_kseg1_count  <= 0;
            ch211_lowram_count <= 0;
        end else if (ee_wr_en) begin
            if (ee_wr_data >= 32'h801F_FD00 && ee_wr_data <= 32'h801F_FE20) begin
                ch211_dstore_cyc [ch211_dstore_count % CH211_DSTORE_DEPTH] <= sim_cycle_ctr;
                ch211_dstore_pc  [ch211_dstore_count % CH211_DSTORE_DEPTH] <= u_core.retired_pc;
                ch211_dstore_ea  [ch211_dstore_count % CH211_DSTORE_DEPTH] <= ee_wr_addr;
                ch211_dstore_data[ch211_dstore_count % CH211_DSTORE_DEPTH] <= ee_wr_data;
                ch211_dstore_count <= ch211_dstore_count + 1;
            end
            if (ee_wr_addr >= 32'hA000_0000 && ee_wr_addr < 32'hA001_0000) begin
                ch211_kseg1_cyc [ch211_kseg1_count % CH211_REGSTORE_DEPTH] <= sim_cycle_ctr;
                ch211_kseg1_pc  [ch211_kseg1_count % CH211_REGSTORE_DEPTH] <= u_core.retired_pc;
                ch211_kseg1_ea  [ch211_kseg1_count % CH211_REGSTORE_DEPTH] <= ee_wr_addr;
                ch211_kseg1_data[ch211_kseg1_count % CH211_REGSTORE_DEPTH] <= ee_wr_data;
                ch211_kseg1_count <= ch211_kseg1_count + 1;
            end
            if ((ee_wr_addr[28:16] == 13'h0000)
                    && (ee_wr_addr[31:29] == 3'b100
                     || ee_wr_addr[31:29] == 3'b101)) begin
                // Low 64 KB (phys), via KSEG0 (3'b100) or KSEG1 (3'b101)
                ch211_lowram_cyc [ch211_lowram_count % CH211_REGSTORE_DEPTH] <= sim_cycle_ctr;
                ch211_lowram_pc  [ch211_lowram_count % CH211_REGSTORE_DEPTH] <= u_core.retired_pc;
                ch211_lowram_ea  [ch211_lowram_count % CH211_REGSTORE_DEPTH] <= ee_wr_addr;
                ch211_lowram_data[ch211_lowram_count % CH211_REGSTORE_DEPTH] <= ee_wr_data;
                ch211_lowram_count <= ch211_lowram_count + 1;
            end
        end
    end

    // Ch210 — saved-context discovery for SYSCALL #8 ($sp first). Three
    // observers:
    //  (a) Search every EE store whose DATA matches a candidate user-
    //      mode $sp value (0x801FFDD0 from the 2nd outer invocation's
    //      post_sp, plus nearby 0x801FFDD8/0x801FFDC8/0x801FFE00). Any
    //      hit is a candidate save-of-$sp site.
    //  (b) Full GPR snapshot at the FIRST retire of PC=0x00030000 (the
    //      kernel handler entry observed in Ch208/Ch209). Captures all
    //      32 registers so we can see whether $k0/$k1/$a0/$v0/$v1 or
    //      any saved register holds a pointer to a context block.
    //  (c) At the 2nd RFE consume, also dump 32 words around the
    //      current $sp + 32 words at the kernel-fn-ptr table 0x80050FF0
    //      so the user can pattern-match a saved GPR frame.
    localparam int CH210_HIT_DEPTH = 32;
    int          ch210_hit_count;
    int          ch210_hit_cyc  [0:CH210_HIT_DEPTH-1];
    logic [31:0] ch210_hit_pc   [0:CH210_HIT_DEPTH-1];
    logic [31:0] ch210_hit_ea   [0:CH210_HIT_DEPTH-1];
    logic [31:0] ch210_hit_data [0:CH210_HIT_DEPTH-1];

    always_ff @(posedge clk) begin
        if (!rst_n) ch210_hit_count <= 0;
        else if (ee_wr_en
                 && (ee_wr_data == 32'h801F_FDD0
                  || ee_wr_data == 32'h801F_FDD8
                  || ee_wr_data == 32'h801F_FDC8
                  || ee_wr_data == 32'h801F_FE00)) begin
            ch210_hit_cyc [ch210_hit_count % CH210_HIT_DEPTH] <= sim_cycle_ctr;
            ch210_hit_pc  [ch210_hit_count % CH210_HIT_DEPTH] <= u_core.retired_pc;
            ch210_hit_ea  [ch210_hit_count % CH210_HIT_DEPTH] <= ee_wr_addr;
            ch210_hit_data[ch210_hit_count % CH210_HIT_DEPTH] <= ee_wr_data;
            ch210_hit_count <= ch210_hit_count + 1;
        end
    end

    logic        ch210_h0_seen;
    int          ch210_h0_cyc;
    logic [31:0] ch210_h0_prev_retired_pc;
    logic [31:0] ch210_h0_regs [0:31];
    always_ff @(posedge clk) begin
        if (!rst_n) ch210_h0_seen <= 1'b0;
        else if (!ch210_h0_seen && core_ev_valid
                 && u_core.retired_pc == 32'h0003_0000) begin
            ch210_h0_seen <= 1'b1;
            ch210_h0_cyc  <= sim_cycle_ctr;
            // Snapshot the entire regfile so we can scan for pointers
            // to context blocks (kernel save area) at handler entry.
            for (int i = 0; i < 32; i++)
                ch210_h0_regs[i] <= u_core.regfile[i];
            // Capture the immediately-previous retired_pc (= call
            // site / delay slot) for context.
            ch210_h0_prev_retired_pc <= retire_ring_pc
                [(retire_ring_head + RETIRE_RING_DEPTH - 1)
                  % RETIRE_RING_DEPTH];
        end
    end

    // Ch208 — stack-delta ledger for the 2nd outer-function invocation.
    // Arms when ch207_inv_count transitions to 2 (i.e., the 2nd
    // addiu $sp,$sp,-N retire in [0xBFC52A00,0xBFC52B40)) and disarms
    // when the epilog LW at PC=0xBFC52B8C retires its 2nd visit. While
    // armed, every change to $sp is logged with cycle/PC/instr/old/new
    // and a running signed sum is maintained. Verdict classifies the
    // bug as outer_unmatched / callee_drift / delay-slot / non-addiu
    // based on where the −8 byte drift came from.
    localparam int CH208_DEPTH = 256;
    logic        ch208_armed;
    logic        ch208_disarmed;
    logic [31:0] ch208_sp_prev;
    int          ch208_sum;
    int          ch208_change_count;
    int          ch208_cyc          [0:CH208_DEPTH-1];
    logic [31:0] ch208_pc           [0:CH208_DEPTH-1];
    logic [31:0] ch208_instr        [0:CH208_DEPTH-1];
    logic [31:0] ch208_old_sp       [0:CH208_DEPTH-1];
    logic [31:0] ch208_new_sp       [0:CH208_DEPTH-1];
    logic        ch208_in_outer     [0:CH208_DEPTH-1];
    int          ch208_running_sum  [0:CH208_DEPTH-1];

    // Mirror of Ch205's last-LW visit count — used to disarm.
    // ch205_lw_visit_count already exists.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch208_armed        <= 1'b0;
            ch208_disarmed     <= 1'b0;
            ch208_sp_prev      <= 32'd0;
            ch208_sum          <= 0;
            ch208_change_count <= 0;
        end else begin
            // Arm one cycle after ch207_inv_count = 2 (= 2nd outer
            // prolog retired). regfile[29] now holds the post-prolog
            // $sp of the 2nd invocation.
            if (!ch208_armed && ch207_inv_count == 2) begin
                ch208_armed   <= 1'b1;
                ch208_sp_prev <= u_core.regfile[29];
                ch208_sum     <= 0;
            end

            // Disarm when the 2nd visit of the epilog LW retires.
            if (ch208_armed && !ch208_disarmed
                    && ch205_lw_visit_count == 2)
                ch208_disarmed <= 1'b1;

            // Track $sp changes while armed.
            if (ch208_armed && !ch208_disarmed
                    && u_core.regfile[29] !== ch208_sp_prev) begin
                int delta;
                delta = $signed(u_core.regfile[29]) - $signed(ch208_sp_prev);
                ch208_cyc      [ch208_change_count % CH208_DEPTH] <= sim_cycle_ctr;
                ch208_pc       [ch208_change_count % CH208_DEPTH] <= u_core.retired_pc;
                ch208_instr    [ch208_change_count % CH208_DEPTH] <= u_core.retired_instr;
                ch208_old_sp   [ch208_change_count % CH208_DEPTH] <= ch208_sp_prev;
                ch208_new_sp   [ch208_change_count % CH208_DEPTH] <= u_core.regfile[29];
                ch208_in_outer [ch208_change_count % CH208_DEPTH]
                    <= (u_core.retired_pc >= 32'hBFC5_2A00
                        && u_core.retired_pc <  32'hBFC5_2B98);
                ch208_running_sum[ch208_change_count % CH208_DEPTH]
                    <= ch208_sum + delta;
                ch208_sum <= ch208_sum + delta;
                ch208_change_count <= ch208_change_count + 1;
            end

            if (ch208_armed)
                ch208_sp_prev <= u_core.regfile[29];
        end
    end

    // Ch207 — outer-function prolog/SW autopsy. Three observers:
    //  (a) `ch207_slot_*` — ALL writes to 0x801FFDE4 from reset onward
    //      (the exact stack slot Ch205/Ch206 implicated). Depth 16.
    //  (b) Entry-detection ring: every retire of an `addiu $sp,$sp,-N`
    //      whose PC falls in [0xBFC52A00, 0xBFC52B40) marks an outer-
    //      function invocation entry. Captures cyc, entry PC, incoming
    //      $ra, pre/post $sp. Depth 8 (only ever expect 2 in this run).
    //  (c) End-of-run static decode of 0xBFC52A00..0xBFC52B98 so the
    //      user can match the dynamic invocation evidence against the
    //      function's actual prolog/epilog instructions.
    localparam logic [31:0] CH207_TARGET_EA = 32'h801F_FDE4;

    // (a) Slot-watch ring
    localparam int CH207_SLOT_DEPTH = 16;
    int          ch207_slot_count;
    int          ch207_slot_cyc  [0:CH207_SLOT_DEPTH-1];
    logic [31:0] ch207_slot_pc   [0:CH207_SLOT_DEPTH-1];
    logic [31:0] ch207_slot_sp   [0:CH207_SLOT_DEPTH-1];
    logic [31:0] ch207_slot_data [0:CH207_SLOT_DEPTH-1];

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch207_slot_count <= 0;
        end else if (ee_wr_en && ee_wr_addr == CH207_TARGET_EA) begin
            ch207_slot_cyc [ch207_slot_count % CH207_SLOT_DEPTH] <= sim_cycle_ctr;
            ch207_slot_pc  [ch207_slot_count % CH207_SLOT_DEPTH] <= u_core.retired_pc;
            ch207_slot_sp  [ch207_slot_count % CH207_SLOT_DEPTH] <= u_core.regfile[29];
            ch207_slot_data[ch207_slot_count % CH207_SLOT_DEPTH] <= ee_wr_data;
            ch207_slot_count <= ch207_slot_count + 1;
        end
    end

    // (b) Outer-function invocation entries
    localparam int CH207_INV_DEPTH = 8;
    int          ch207_inv_count;
    int          ch207_inv_cyc       [0:CH207_INV_DEPTH-1];
    logic [31:0] ch207_inv_entry_pc  [0:CH207_INV_DEPTH-1];
    logic [31:0] ch207_inv_in_ra     [0:CH207_INV_DEPTH-1];
    logic [31:0] ch207_inv_pre_sp    [0:CH207_INV_DEPTH-1];
    logic [31:0] ch207_inv_post_sp   [0:CH207_INV_DEPTH-1];

    // The prolog signature is `addiu $sp, $sp, -N` with rt=$sp and
    // negative immediate. core_ev_arg1 carries the retired instr;
    // u_core.retired_pc carries the PC. We catch the first such retire
    // per call by simply incrementing the invocation count each time
    // an addiu-sp-neg in range fires.
    always_ff @(posedge clk) begin
        logic [5:0] op;
        logic [4:0] rs, rt;
        logic [15:0] imm;
        if (!rst_n) begin
            ch207_inv_count <= 0;
        end else if (core_ev_valid
                     && u_core.retired_pc >= 32'hBFC5_2A00
                     && u_core.retired_pc <  32'hBFC5_2B40) begin
            op  = core_ev_arg1[31:26];
            rs  = core_ev_arg1[25:21];
            rt  = core_ev_arg1[20:16];
            imm = core_ev_arg1[15:0];
            // addiu $sp, $sp, -N  →  op=0x09, rs=29, rt=29, imm[15]=1
            if (op == 6'h09 && rs == 5'd29 && rt == 5'd29 && imm[15] == 1'b1) begin
                ch207_inv_cyc      [ch207_inv_count % CH207_INV_DEPTH] <= sim_cycle_ctr;
                ch207_inv_entry_pc [ch207_inv_count % CH207_INV_DEPTH] <= u_core.retired_pc;
                ch207_inv_in_ra    [ch207_inv_count % CH207_INV_DEPTH] <= u_core.regfile[31];
                // u_core.regfile[29] at this retire holds the NEW $sp
                // (post-addiu). pre-addiu $sp = new $sp - sext(imm).
                ch207_inv_pre_sp   [ch207_inv_count % CH207_INV_DEPTH]
                    <= u_core.regfile[29] - {{16{imm[15]}}, imm};
                ch207_inv_post_sp  [ch207_inv_count % CH207_INV_DEPTH] <= u_core.regfile[29];
                ch207_inv_count <= ch207_inv_count + 1;
            end
        end
    end

    // Ch206 — $ra lineage from the 2nd RFE consume to the bad
    // epilogue. Activates when ch199_consume_count transitions to 2,
    // then captures every change to $ra (regfile[31]) with the
    // source PC/instruction. Classifies the source per retire as
    // jal / jalr-link / lw / explicit-ALU / unknown. A separate
    // one-shot trigger records the FIRST cycle $ra becomes zero so
    // the verdict can name the corrupting event.
    localparam int CH206_RA_DEPTH = 256;
    logic        ch206_active;
    logic [31:0] ch206_ra_at_2rfe;
    logic        ch206_ra_at_2rfe_captured;
    int          ch206_ra_change_count;
    int          ch206_ra_cyc    [0:CH206_RA_DEPTH-1];
    logic [31:0] ch206_ra_prev   [0:CH206_RA_DEPTH-1];
    logic [31:0] ch206_ra_new    [0:CH206_RA_DEPTH-1];
    logic [31:0] ch206_ra_pc     [0:CH206_RA_DEPTH-1];
    logic [31:0] ch206_ra_instr  [0:CH206_RA_DEPTH-1];
    logic [31:0] ch206_ra_prev_value;
    // First-zero one-shot
    logic        ch206_first_zero_seen;
    int          ch206_first_zero_cyc;
    logic [31:0] ch206_first_zero_pc;
    logic [31:0] ch206_first_zero_instr;
    logic [31:0] ch206_first_zero_prev;
    // First function entry into [0xBFC52A00, 0xBFC52B98) after 2nd RFE
    logic        ch206_func_entry_seen;
    int          ch206_func_entry_cyc;
    logic [31:0] ch206_func_entry_pc;
    logic [31:0] ch206_func_entry_ra_at_entry;
    // First SW $ra into the stack after 2nd RFE
    logic        ch206_prolog_sw_seen;
    int          ch206_prolog_sw_cyc;
    logic [31:0] ch206_prolog_sw_pc;
    logic [31:0] ch206_prolog_sw_addr;
    logic [31:0] ch206_prolog_sw_data;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch206_active              <= 1'b0;
            ch206_ra_at_2rfe_captured <= 1'b0;
            ch206_ra_change_count     <= 0;
            ch206_first_zero_seen     <= 1'b0;
            ch206_func_entry_seen     <= 1'b0;
            ch206_prolog_sw_seen      <= 1'b0;
            ch206_ra_prev_value       <= 32'd0;
        end else begin
            // Activate one cycle after Ch204's second-RFE capture
            // fires (which is the cleanest synchronized signal —
            // ch199_consume_count having transitioned to 2 by then).
            if (!ch206_active && ch204_second_rfe_captured) begin
                ch206_active              <= 1'b1;
                ch206_ra_at_2rfe          <= u_core.regfile[31];
                ch206_ra_at_2rfe_captured <= 1'b1;
                ch206_ra_prev_value       <= u_core.regfile[31];
            end

            if (ch206_active) begin
                // Update prev_value snapshot for next cycle. We log
                // BEFORE updating prev so we see prev→new transition.
                ch206_ra_prev_value <= u_core.regfile[31];

                if (u_core.regfile[31] !== ch206_ra_prev_value) begin
                    ch206_ra_cyc   [ch206_ra_change_count % CH206_RA_DEPTH] <= sim_cycle_ctr;
                    ch206_ra_prev  [ch206_ra_change_count % CH206_RA_DEPTH] <= ch206_ra_prev_value;
                    ch206_ra_new   [ch206_ra_change_count % CH206_RA_DEPTH] <= u_core.regfile[31];
                    ch206_ra_pc    [ch206_ra_change_count % CH206_RA_DEPTH] <= u_core.retired_pc;
                    ch206_ra_instr [ch206_ra_change_count % CH206_RA_DEPTH] <= u_core.retired_instr;
                    ch206_ra_change_count <= ch206_ra_change_count + 1;

                    if (!ch206_first_zero_seen
                            && u_core.regfile[31] == 32'd0) begin
                        ch206_first_zero_seen  <= 1'b1;
                        ch206_first_zero_cyc   <= sim_cycle_ctr;
                        ch206_first_zero_pc    <= u_core.retired_pc;
                        ch206_first_zero_instr <= u_core.retired_instr;
                        ch206_first_zero_prev  <= ch206_ra_prev_value;
                    end
                end

                // First retire into [0xBFC52A00, 0xBFC52B98) =
                // candidate function entry of the function ending at
                // 0xBFC52B94.
                if (!ch206_func_entry_seen && core_ev_valid
                        && u_core.retired_pc >= 32'hBFC5_2A00
                        && u_core.retired_pc <  32'hBFC5_2B98) begin
                    ch206_func_entry_seen      <= 1'b1;
                    ch206_func_entry_cyc       <= sim_cycle_ctr;
                    ch206_func_entry_pc        <= u_core.retired_pc;
                    ch206_func_entry_ra_at_entry <= u_core.regfile[31];
                end

                // First SW with rt=$31 (saved $ra) AFTER ch206_active —
                // candidate prolog save. core_ev_arg1 carries the
                // retired instruction word; for SW, core_ev_arg2 = ea
                // and core_ev_arg3 = the stored data (= $ra).
                if (!ch206_prolog_sw_seen && core_ev_valid
                        && core_ev_arg1[31:26] == 6'h2B
                        && core_ev_arg1[20:16] == 5'd31) begin
                    ch206_prolog_sw_seen <= 1'b1;
                    ch206_prolog_sw_cyc  <= sim_cycle_ctr;
                    ch206_prolog_sw_pc   <= u_core.retired_pc;
                    ch206_prolog_sw_addr <= core_ev_arg2[31:0];
                    ch206_prolog_sw_data <= core_ev_arg3[31:0];
                end
            end
        end
    end

    // Ch205 — return-address provenance for the buggy `jr $ra` at
    // PC=0xBFC52B94 that landed at PC=0 in Ch204. Three observers:
    //   (a) Generic EE store ring (depth 128): every retire-side store
    //       with cyc/EA/data/source_pc. Scanned at end-of-run for the
    //       prolog SW that should have populated $sp+0x1C, plus any
    //       later writes that may have clobbered it.
    //   (b) One-shot capture at the epilog LW (PC=0xBFC52B8C) — grabs
    //       $sp so we can compute the target EA ($sp+0x1C); the loaded
    //       value lands in $ra on the NEXT retire, so the JR observer
    //       below captures the actual return-PC.
    //   (c) One-shot capture at the epilog JR (PC=0xBFC52B94) — grabs
    //       $ra at the moment of branch. If it's 0 → buggy return.
    //   (d) JAL/JALR landing tracker — every retire after a JAL/JALR
    //       captures the source PC + the linked value (= $ra), and a
    //       separate "post-jal landing" snapshot grabs the next PC.
    //       We rotate through a small ring so we can identify the
    //       function-entry path at end-of-run.
    localparam logic [31:0] CH205_LW_PC = 32'hBFC5_2B8C;
    localparam logic [31:0] CH205_JR_PC = 32'hBFC5_2B94;

    localparam int CH205_STORE_DEPTH = 4096;
    int          ch205_store_count;
    int          ch205_store_cyc  [0:CH205_STORE_DEPTH-1];
    logic [31:0] ch205_store_ea   [0:CH205_STORE_DEPTH-1];
    logic [31:0] ch205_store_data [0:CH205_STORE_DEPTH-1];
    logic [31:0] ch205_store_pc   [0:CH205_STORE_DEPTH-1];

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch205_store_count <= 0;
        end else if (ee_wr_en) begin
            ch205_store_cyc [ch205_store_count % CH205_STORE_DEPTH] <= sim_cycle_ctr;
            ch205_store_ea  [ch205_store_count % CH205_STORE_DEPTH] <= ee_wr_addr;
            ch205_store_data[ch205_store_count % CH205_STORE_DEPTH] <= ee_wr_data;
            ch205_store_pc  [ch205_store_count % CH205_STORE_DEPTH] <= u_core.retired_pc;
            ch205_store_count <= ch205_store_count + 1;
        end
    end

    // Ch205 — capture MOST RECENT (not first) epilog LW/JR retires.
    // The first hit had $ra=0xBFC52324 (valid); the trap-causing
    // instance was much later. Track count so the dump can say
    // "this is the Nth visit" and the last fields are the relevant
    // one for the trap-side autopsy.
    logic        ch205_lw_seen;
    int          ch205_lw_cyc;
    logic [31:0] ch205_lw_sp;
    logic [31:0] ch205_lw_target_ea;
    int          ch205_lw_visit_count;
    int          ch205_lw_first_cyc;
    logic [31:0] ch205_lw_first_sp;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch205_lw_seen        <= 1'b0;
            ch205_lw_visit_count <= 0;
            ch205_lw_first_cyc   <= 0;
            ch205_lw_first_sp    <= 32'd0;
        end else if (core_ev_valid
                     && u_core.retired_pc == CH205_LW_PC) begin
            if (!ch205_lw_seen) begin
                ch205_lw_first_cyc <= sim_cycle_ctr;
                ch205_lw_first_sp  <= u_core.regfile[29];
            end
            ch205_lw_seen        <= 1'b1;
            ch205_lw_cyc         <= sim_cycle_ctr;
            ch205_lw_sp          <= u_core.regfile[29];
            ch205_lw_target_ea   <= u_core.regfile[29] + 32'h0000_001C;
            ch205_lw_visit_count <= ch205_lw_visit_count + 1;
        end
    end

    logic        ch205_jr_seen;
    int          ch205_jr_cyc;
    logic [31:0] ch205_jr_ra;
    int          ch205_jr_visit_count;
    logic [31:0] ch205_jr_first_ra;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch205_jr_seen        <= 1'b0;
            ch205_jr_visit_count <= 0;
            ch205_jr_first_ra    <= 32'd0;
        end else if (core_ev_valid
                     && u_core.retired_pc == CH205_JR_PC) begin
            if (!ch205_jr_seen)
                ch205_jr_first_ra <= u_core.regfile[31];
            ch205_jr_seen        <= 1'b1;
            ch205_jr_cyc         <= sim_cycle_ctr;
            ch205_jr_ra          <= u_core.regfile[31];
            ch205_jr_visit_count <= ch205_jr_visit_count + 1;
        end
    end

    // JAL/JALR landing ring (depth 8). On a retire whose retired_instr
    // is JAL (op=0x03) or JALR (op=0/funct=0x09), capture the linked
    // value (= post-retire $ra) and the source PC. The actual TARGET
    // PC is the NEXT retire — we capture that via a one-step-delayed
    // observer below.
    localparam int CH205_JAL_DEPTH = 128;
    int          ch205_jal_count;
    int          ch205_jal_cyc      [0:CH205_JAL_DEPTH-1];
    logic [31:0] ch205_jal_src_pc   [0:CH205_JAL_DEPTH-1];
    logic [31:0] ch205_jal_link     [0:CH205_JAL_DEPTH-1];
    logic [31:0] ch205_jal_target   [0:CH205_JAL_DEPTH-1];
    logic        ch205_jal_pending;
    logic [31:0] ch205_jal_target_word_for_jal;
    always_ff @(posedge clk) begin
        logic [5:0] op, func;
        if (!rst_n) begin
            ch205_jal_count   <= 0;
            ch205_jal_pending <= 1'b0;
        end else if (core_ev_valid) begin
            op   = core_ev_arg1[31:26];
            func = core_ev_arg1[5:0];
            // Fill in the target field of the PREVIOUSLY captured
            // JAL — this retire is the post-delay-slot landing.
            if (ch205_jal_pending) begin
                ch205_jal_target[(ch205_jal_count - 1) % CH205_JAL_DEPTH]
                    <= u_core.retired_pc;
                ch205_jal_pending <= 1'b0;
            end
            // Did this retire LINK ($ra), i.e. JAL / JALR-with-rd=31?
            if (op == 6'h03  // JAL
                || (op == 6'h00 && func == 6'h09)) begin  // JALR
                ch205_jal_cyc   [ch205_jal_count % CH205_JAL_DEPTH] <= sim_cycle_ctr;
                ch205_jal_src_pc[ch205_jal_count % CH205_JAL_DEPTH] <= u_core.retired_pc;
                ch205_jal_link  [ch205_jal_count % CH205_JAL_DEPTH] <= u_core.regfile[31];
                ch205_jal_target[ch205_jal_count % CH205_JAL_DEPTH] <= 32'd0;  // filled next retire
                ch205_jal_count <= ch205_jal_count + 1;
                ch205_jal_pending <= 1'b1;
            end
        end
    end

    // Ch204 — autopsy for the strict-trap landing at PC=0x00000068
    // exposed by the Ch203 BIOS-long run. Three pieces of evidence:
    //   (a) Wide low-RAM write ring covering [0x40, 0xC0) so we can
    //       see whether 0x68 was deliberately installed (and by what
    //       upstream PC) or arrived as stray data.
    //   (b) One-shot snapshot of COP0 + retire ring + low-RAM window
    //       AT THE MOMENT THE STRICT TRAP fires (bit 7 of ev_flags).
    //       The existing Ch193 halt_point capture only fires on bit 3
    //       (halt), so a strict_trap halt path isn't covered there.
    //   (c) Second-`_ReturnFromException(2)` snapshot. Ch199's existing
    //       observer only captures the FIRST consume; if the BIOS
    //       transitions to consume_count==2, capture the new context
    //       so we can compare the two RFE landings.
    localparam int CH204_WRRING_DEPTH = 64;
    int          ch204_wr_count;
    int          ch204_wr_cyc  [0:CH204_WRRING_DEPTH-1];
    logic [31:0] ch204_wr_addr [0:CH204_WRRING_DEPTH-1];
    logic [31:0] ch204_wr_data [0:CH204_WRRING_DEPTH-1];
    logic [31:0] ch204_wr_pc   [0:CH204_WRRING_DEPTH-1];

    always_ff @(posedge clk) begin
        logic [28:0] phys_lo;
        if (!rst_n) begin
            ch204_wr_count <= 0;
        end else if (ee_wr_en) begin
            phys_lo = ee_wr_addr[28:0];
            if (phys_lo >= 29'h000_0040 && phys_lo < 29'h000_00C0) begin
                ch204_wr_cyc [ch204_wr_count % CH204_WRRING_DEPTH] <= sim_cycle_ctr;
                ch204_wr_addr[ch204_wr_count % CH204_WRRING_DEPTH] <= ee_wr_addr;
                ch204_wr_data[ch204_wr_count % CH204_WRRING_DEPTH] <= ee_wr_data;
                ch204_wr_pc  [ch204_wr_count % CH204_WRRING_DEPTH] <= u_core.retired_pc;
                ch204_wr_count <= ch204_wr_count + 1;
            end
        end
    end

    // Strict-trap one-shot snapshot.
    logic        ch204_trap_captured;
    int          ch204_trap_cyc;
    logic [31:0] ch204_trap_pc;
    logic [31:0] ch204_trap_instr;
    logic [31:0] ch204_trap_epc;
    logic [31:0] ch204_trap_cause;
    logic [31:0] ch204_trap_status;
    logic [31:0] ch204_trap_badvaddr;
    logic [31:0] ch204_trap_ra;
    int          ch204_trap_excring_at;
    int          ch204_trap_consume_at;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch204_trap_captured <= 1'b0;
        end else if (!ch204_trap_captured && core_ev_valid
                     && ((core_ev_flags & 32'h80) != 32'd0)) begin
            ch204_trap_captured <= 1'b1;
            ch204_trap_cyc      <= sim_cycle_ctr;
            ch204_trap_pc       <= u_core.retired_pc;
            ch204_trap_instr    <= u_core.retired_instr;
            ch204_trap_epc      <= u_core.epc;
            ch204_trap_cause    <= u_core.cause_word;
            ch204_trap_status   <= u_core.status_word;
            ch204_trap_badvaddr <= u_core.badvaddr;
            ch204_trap_ra       <= u_core.regfile[31];
            ch204_trap_excring_at <= exc_ring_count;
            ch204_trap_consume_at <= ch199_consume_count;
        end
    end

    // Second-RFE consume capture (fires when consume_count transitions 1→2).
    logic        ch204_second_rfe_captured;
    int          ch204_second_rfe_cyc;
    logic [31:0] ch204_second_rfe_pc;
    logic [31:0] ch204_second_rfe_k0;
    logic [31:0] ch204_second_rfe_a0;
    logic [31:0] ch204_second_rfe_ra;
    int          ch204_second_rfe_excring_at;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch204_second_rfe_captured <= 1'b0;
        end else if (!ch204_second_rfe_captured
                     && ch199_consume_count == 1
                     && core_ev_valid
                     && ((core_ev_flags & 32'h40) != 32'd0)
                     && core_ev_arg1[31:26] == 6'h00
                     && core_ev_arg1[5:0]   == 6'h0C) begin
            ch204_second_rfe_captured <= 1'b1;
            ch204_second_rfe_cyc <= sim_cycle_ctr;
            ch204_second_rfe_pc  <= u_core.retired_pc;
            ch204_second_rfe_k0  <= u_core.regfile[26];
            ch204_second_rfe_a0  <= u_core.regfile[4];
            ch204_second_rfe_ra  <= u_core.regfile[31];
            ch204_second_rfe_excring_at <= exc_ring_count;
        end
    end

    // Ch202 — capture the polling-loop contract once. The loop AND
    // instruction at PC=0xBFC4FB28 reads $4 (the mask). Snapshot $4,
    // $3 (timeout counter), $6 (MMIO address) the first time we
    // retire that PC; print the captured contract at end-of-run plus
    // a Ch202 verdict line confirming whether the new MMIO ready
    // stub satisfies (read & mask) != 0.
    logic        ch202_loop_seen;
    int          ch202_first_cyc;
    logic [31:0] ch202_first_mask;     // $4
    logic [31:0] ch202_first_timeout;  // $3
    logic [31:0] ch202_first_mmio_ea;  // $6
    int          ch202_loop_retires_at_first;
    int          ch202_mmio_reads_at_first;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch202_loop_seen <= 1'b0;
        end else if (!ch202_loop_seen && core_ev_valid
                     && u_core.retired_pc == 32'hBFC4_FB28) begin
            ch202_loop_seen     <= 1'b1;
            ch202_first_cyc     <= sim_cycle_ctr;
            ch202_first_mask    <= u_core.regfile[4];
            ch202_first_timeout <= u_core.regfile[3];
            ch202_first_mmio_ea <= u_core.regfile[6];
            ch202_loop_retires_at_first <= ch201_post_rfe_retires;
            ch202_mmio_reads_at_first   <= ch201_post_rfe_mmio_reads;
            $display("[ch202] loop_contract_captured cyc=%0d pc=0x%08h $4(mask)=0x%08h $3(timeout)=0x%08h $6(mmio_ea)=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc,
                     u_core.regfile[4], u_core.regfile[3],
                     u_core.regfile[6]);
        end
    end

    // Ch199 — _ReturnFromException(2) consume observer. Fires when the
    // EE stub retires a SYSCALL with the Ch199 RFE bit set (bit 6 of
    // ev_flags, set by the new syscall_8_a0_2 branch in ee_core_stub).
    // Logs once per consume and never halts execution itself.
    int          ch199_consume_count;
    int          ch199_first_cyc;
    logic [31:0] ch199_first_pc;
    logic [31:0] ch199_first_k0;
    logic [31:0] ch199_first_a0;
    int          ch199_excring_at_consume;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch199_consume_count <= 0;
            ch199_first_cyc     <= 0;
            ch199_first_pc      <= 32'd0;
            ch199_first_k0      <= 32'd0;
            ch199_first_a0      <= 32'd0;
            ch199_excring_at_consume <= 0;
        end else if (core_ev_valid
                     && ((core_ev_flags & 32'h40) != 32'd0)
                     && core_ev_arg1[31:26] == 6'h00
                     && core_ev_arg1[5:0]   == 6'h0C) begin
            if (ch199_consume_count == 0) begin
                ch199_first_cyc <= sim_cycle_ctr;
                ch199_first_pc  <= u_core.retired_pc;
                ch199_first_k0  <= u_core.regfile[26];
                ch199_first_a0  <= u_core.regfile[4];
                ch199_excring_at_consume <= exc_ring_count;
            end
            ch199_consume_count <= ch199_consume_count + 1;
            $display("[ch199] consume_syscall cyc=%0d pc=0x%08h instr=0x%08h $v1=%0d $a0=%0d $k0=0x%08h -> resuming at PC<-$k0 (AdES events so far=%0d)",
                     sim_cycle_ctr, u_core.retired_pc,
                     u_core.retired_instr,
                     u_core.regfile[3], u_core.regfile[4],
                     u_core.regfile[26], exc_ring_count);
        end
    end

    // Ch186 — RAM-dispatcher entry observer. The Ch185 static decode
    // proved Ch184's "patches" were collateral damage from a 4 KiB
    // low-RAM clear, NOT deliberate template patches. So the bug is
    // not the helper; the bug is *why execution reaches RAM
    // dispatcher code at PC=0x00030000-area* after the helper has
    // been wiped (entries 1-22 ran before the clear, entry 23 ran
    // after — and entry 23 came in from PC=0x0003003C).
    //
    // This observer catches the FIRST retire in [0x30000, 0x30040)
    // and snapshots the retire-ring contents + regs at that moment
    // so we can see exactly what control transfer brought the EE
    // into the RAM dispatcher.
    localparam logic [31:0] RAM_DISP_LO = 32'h0003_0000;
    localparam logic [31:0] RAM_DISP_HI = 32'h0003_0040;
    logic        first_ram_disp_captured;
    int          first_ram_disp_cycle;
    logic [31:0] first_ram_disp_pc, first_ram_disp_instr;
    logic [31:0] first_ram_disp_r1, first_ram_disp_r2, first_ram_disp_r3;
    logic [31:0] first_ram_disp_r4, first_ram_disp_r8, first_ram_disp_r10;
    logic [31:0] first_ram_disp_r29, first_ram_disp_r31;
    // Snapshot of the retire_ring at trigger time. Since retire_ring
    // keeps advancing after the trigger, we copy it into a frozen
    // shadow array at the moment of first_ram_disp_captured rising.
    logic [31:0] first_ram_disp_ring_pc    [0:31];
    logic [31:0] first_ram_disp_ring_instr [0:31];
    int          first_ram_disp_ring_cyc   [0:31];
    int          first_ram_disp_ring_head_at_trigger;

    // Writes-to-RAM-dispatcher ring. Captures any store whose target
    // phys address is in [0x30000, 0x30040). Tells us when the
    // BIOS *installed* the RAM-dispatcher code.
    localparam int RAM_DISP_WR_DEPTH = 32;
    int          ram_disp_wr_count;
    int          ram_disp_wr_cyc  [0:RAM_DISP_WR_DEPTH-1];
    logic [31:0] ram_disp_wr_addr [0:RAM_DISP_WR_DEPTH-1];
    logic [31:0] ram_disp_wr_data [0:RAM_DISP_WR_DEPTH-1];
    logic [31:0] ram_disp_wr_pc   [0:RAM_DISP_WR_DEPTH-1];

    // Ch184 — self-modifying-code observer. Hypothesis (β) says
    // PC=0x5B0 in the memset template is patched by callers with a
    // decrement (`addi $1, $1, -1`) before the call and reverted to
    // NOP after. The peek_instr at TIMEOUT shows NOP (final state),
    // so we need a RETIRE-TIME snapshot of the template instructions
    // at every ENTRY598 PLUS a ring of stores that hit the template
    // address range [0x598, 0x5C0).
    //
    // Per Codex Ch184 caution: capture instr words at entry time,
    // not at watchdog timeout (Ch182 proved final RAM peeks lie
    // for code that gets overwritten later).
    //
    // Phys-address filter: writes via useg (0x0xxx), KSEG0
    // (0x80000xxx), or KSEG1 (0xA0000xxx) all share the same
    // phys[28:0] = 0x000005xx. The observer masks the upper bits.
    localparam int PATCH_RING_DEPTH = 32;
    int          patch_ring_count;
    int          patch_ring_cyc  [0:PATCH_RING_DEPTH-1];
    logic [31:0] patch_ring_addr [0:PATCH_RING_DEPTH-1];
    logic [31:0] patch_ring_data [0:PATCH_RING_DEPTH-1];
    logic [3:0]  patch_ring_be   [0:PATCH_RING_DEPTH-1];

    // Per-entry snapshots of the template's five core instructions
    // PLUS the registers we care about. Snapshot indexed by
    // entry598_count BEFORE it increments (i.e., the snapshot for
    // ENTRY598#N lives at index N-1).
    localparam int ENTRY_SNAP_DEPTH = 64;
    int          entry_snap_cyc      [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_r1       [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_r10      [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_r31      [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_instr5a8 [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_instr5ac [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_instr5b0 [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_instr5b4 [0:ENTRY_SNAP_DEPTH-1];
    logic [31:0] entry_snap_instr5b8 [0:ENTRY_SNAP_DEPTH-1];

    // Ch183 — $10 provenance ring (mirror of Ch180 R1 ring), plus a
    // one-shot capture that fires the cycle the bad `jalr $10` at
    // PC=0x0003003C retires. Per Codex Ch183 caution: trust retire-
    // time rings; do NOT rely on peek_instr at timeout (the BIOS
    // re-zeros the RAM-side dispatcher code between the jalr and
    // the watchdog).
    //
    // The existing `r10_prev` is a single-cycle delayed observer
    // used by the rom_escape detector; we add OUR OWN ring with a
    // distinct prev signal so the two don't fight for drivers.
    logic [31:0] r10_chg_prev;
    logic        r10_chg_prev_valid;
    localparam int R10_RING_DEPTH = 16;
    int          r10_ring_count;
    int          r10_ring_cyc      [0:R10_RING_DEPTH-1];
    logic [31:0] r10_ring_prev     [0:R10_RING_DEPTH-1];
    logic [31:0] r10_ring_new      [0:R10_RING_DEPTH-1];
    logic [31:0] r10_ring_pc       [0:R10_RING_DEPTH-1];
    logic [31:0] r10_ring_instr    [0:R10_RING_DEPTH-1];

    // One-shot bad-jalr capture (PC=0x0003003C). Snapshot the regfile
    // + the R10/R1 ring SIZES at trigger so the timeout dump knows
    // which entries to print as "setup window" vs "pre-trigger noise".
    // (Ring contents themselves are dumped from the live arrays.)
    logic        bad_jalr_captured;
    int          bad_jalr_cycle;
    logic [31:0] bad_jalr_r10, bad_jalr_r1, bad_jalr_r8;
    logic [31:0] bad_jalr_r2, bad_jalr_r3, bad_jalr_r4, bad_jalr_r5;
    logic [31:0] bad_jalr_r29, bad_jalr_r31;
    int          bad_jalr_r10_ring_count_at_trigger;
    int          bad_jalr_r1_ring_count_at_trigger;
    int          bad_jalr_r31_ring_count_at_trigger;

    // Ch182 — $31 ($ra) provenance ring. Same shape as Ch180 R1 ring.
    // The trigger of interest: ENTRY598#23 came in with $31=0x00030044
    // (a RAM-side return target where peek_instr is all-zero in both
    // ee_ram and useg_shadow_mem). Find who wrote that value and how —
    // jal/jalr (link semantics), explicit GPR write (lui/ori/addiu),
    // or load from memory. Uses its own local prev signals to avoid
    // colliding with the existing r31_to_target observer's
    // r31_prev / r31_prev_valid drivers.
    logic [31:0] r31_chg_prev;
    logic        r31_chg_prev_valid;
    localparam int R31_RING_DEPTH = 16;
    int          r31_ring_count;
    int          r31_ring_cyc      [0:R31_RING_DEPTH-1];
    logic [31:0] r31_ring_prev     [0:R31_RING_DEPTH-1];
    logic [31:0] r31_ring_new      [0:R31_RING_DEPTH-1];
    logic [31:0] r31_ring_pc       [0:R31_RING_DEPTH-1];
    logic [31:0] r31_ring_instr    [0:R31_RING_DEPTH-1];

    // Ch182 — source-class classifier. Returns one of:
    //   jal / jalr-link  — $31 = PC + 8 (link semantics)
    //   load             — $31 = mem[ea]
    //   lui              — $31 = imm << 16
    //   ori / addiu      — $31 = rs | imm  /  rs + imm
    //   add/or/...       — $31 = ALU result
    //   other            — anything else
    function automatic string r31_source_class(input logic [31:0] instr);
        logic [5:0] op, func;
        logic [4:0] rd;
        op   = instr[31:26];
        func = instr[5:0];
        rd   = instr[15:11];
        if (op == 6'h03) return "jal-link";
        if (op == 6'h00 && func == 6'h09 && rd == 5'd31) return "jalr-link";
        if (op == 6'h00 && func == 6'h09) return "jalr-other-rd";
        if (op == 6'h00 && (func == 6'h20 || func == 6'h21 ||
                            func == 6'h22 || func == 6'h23 ||
                            func == 6'h24 || func == 6'h25 ||
                            func == 6'h26 || func == 6'h27))
            return "alu-rtype";
        if (op == 6'h0F) return "lui-imm";
        if (op == 6'h0D) return "ori-imm";
        if (op == 6'h09) return "addiu-imm";
        if (op == 6'h08) return "addi-imm";
        if (op == 6'h23) return "lw-from-mem";
        if (op == 6'h20) return "lb-from-mem";
        if (op == 6'h21) return "lh-from-mem";
        if (op == 6'h24) return "lbu-from-mem";
        if (op == 6'h25) return "lhu-from-mem";
        return $sformatf("other(op=%02h,func=%02h)", op, func);
    endfunction

    // Ch181 — ENTRY5A8 observer. Fires on every retire at PC=0x5A8
    // where the previous retired PC is NOT in the "expected in-
    // function flow" set {0x5A4 (the addiu right before; first
    // entry from function head), 0x5B8 (the BNE delay slot;
    // normal back-edge inside the loop)}. Any other prev-PC means
    // execution jumped INTO the loop body from somewhere unusual.
    logic        entry5a8_any_captured;
    logic        entry5a8_anomalous_captured;
    int          entry5a8_total_count;
    int          entry5a8_anomalous_count;
    int          entry5a8_cycle;
    logic [31:0] entry5a8_r1, entry5a8_r2, entry5a8_r3, entry5a8_r31;
    logic [31:0] entry5a8_prev_pc, entry5a8_prev_instr;
    logic [31:0] entry5a8_ring_prev2_pc, entry5a8_ring_prev2_instr;
    logic [31:0] entry5a8_ring_prev3_pc, entry5a8_ring_prev3_instr;
    logic [31:0] entry5a8_ring_prev4_pc, entry5a8_ring_prev4_instr;

    logic [31:0] r3_prev;
    localparam int R3_RING_DEPTH = 8;
    int          r3_ring_count;
    int          r3_ring_cyc      [0:R3_RING_DEPTH-1];
    logic [31:0] r3_ring_prev     [0:R3_RING_DEPTH-1];
    logic [31:0] r3_ring_new      [0:R3_RING_DEPTH-1];
    logic [31:0] r3_ring_pc       [0:R3_RING_DEPTH-1];
    logic [31:0] r3_ring_instr    [0:R3_RING_DEPTH-1];
    logic [31:0] r3_ring_arg2     [0:R3_RING_DEPTH-1];
    logic [31:0] r3_ring_arg3     [0:R3_RING_DEPTH-1];

    // Chapter 38 — $10 provenance observer. Ch37's ROM_ESCAPE
    // showed `JR $10` with $10 = 0x000000C0 — the exact landing pc
    // in zero-filled low-RAM. Question now: which instruction wrote
    // 0xC0 into $10? If it's a LW, $10 was loaded from a dispatch
    // table and the table entry is the installation-gap target. If
    // it's computed (ADDIU / LUI+ORI / OR), the logic that *chose*
    // 0xC0 is the candidate bug site.
    //
    // Two observers (same shape as ch36):
    //   R10_CHG: every $10 change emits a line — cumulative lineage.
    //   R10_TO_C0: first-match one-shot on transition to 0xC0.
    //              Captures retired pc/instr/arg2/arg3 + rs/rt/rd
    //              decode + live source register values. Classifier
    //              at print time calls out LW (table load) vs
    //              computed vs move-like vs other.
    localparam logic [31:0] R10_TARGET_VALUE = 32'h0000_00C0;
    logic        r10_prev_valid;
    logic [31:0] r10_prev;
    logic        r10_to_c0_captured;
    int          r10_to_c0_cycle;
    logic [31:0] r10_to_c0_retired_pc;
    logic [31:0] r10_to_c0_retired_instr;
    logic [31:0] r10_to_c0_retired_arg2;
    logic [31:0] r10_to_c0_retired_arg3;
    logic [31:0] r10_to_c0_prev_r10;
    logic [4:0]  r10_to_c0_rs_idx;
    logic [4:0]  r10_to_c0_rt_idx;
    logic [4:0]  r10_to_c0_rd_idx;
    logic [31:0] r10_to_c0_rs_val;
    logic [31:0] r10_to_c0_rt_val;
    logic [31:0] r10_to_c0_ring_prev_pc;
    logic [31:0] r10_to_c0_ring_prev_instr;
    logic [31:0] r10_to_c0_ring_prev2_pc;
    logic [31:0] r10_to_c0_ring_prev2_instr;

    // Chapter 39 — low-RAM write-provenance. Observes the shared
    // 128-bit physical ram_wr_* port on u_ee_ram, which is the
    // single funnel every writer goes through (core SW/SH/SB,
    // DMAC, SIF-EE bridge, TB preload). Two observers:
    //
    //   RAM_C0_EXACT: first qword write with ee_ram_wr_addr[24:4]
    //                 == 0x00C (byte range 0xC0..0xCF — exactly where
    //                 the ch38 trampoline jumps). Captures cycle,
    //                 data128, be16, master_id.
    //
    //   RAM_C0_WINDOW: linear ring depth 32, captures every qword
    //                  write whose qword_idx ∈ [0x00C, 0x01F]
    //                  (byte range 0xC0..0x1FF — the whole
    //                  fall-through runway). If this stays empty,
    //                  we have a definitive install-phase gap.
    //
    // Also capture the concurrent CPU retire (pc/instr) if
    // core_ev_valid is asserting, so cpu-side writes get attributed
    // to the store that caused them.
    localparam int           RAM_C0_WIN_DEPTH = 32;
    localparam logic [20:0]  RAM_C0_QW_IDX    = 21'h00C;       // byte 0xC0 / 16
    localparam logic [20:0]  RAM_WIN_QW_LOW   = 21'h00C;
    localparam logic [20:0]  RAM_WIN_QW_HIGH  = 21'h01F;       // byte 0x1F0 / 16
    logic         ram_c0_exact_captured;
    int           ram_c0_exact_cycle;
    logic [127:0] ram_c0_exact_data;
    logic [15:0]  ram_c0_exact_be;
    logic [7:0]   ram_c0_exact_master;
    logic [31:0]  ram_c0_exact_retired_pc;
    logic [31:0]  ram_c0_exact_retired_instr;
    logic         ram_c0_exact_retired_was_write;
    int           ram_c0_win_count;
    int           ram_c0_win_cyc    [0:RAM_C0_WIN_DEPTH-1];
    logic [20:0]  ram_c0_win_qw_idx [0:RAM_C0_WIN_DEPTH-1];
    logic [15:0]  ram_c0_win_be     [0:RAM_C0_WIN_DEPTH-1];
    logic [127:0] ram_c0_win_data   [0:RAM_C0_WIN_DEPTH-1];
    logic [7:0]   ram_c0_win_master [0:RAM_C0_WIN_DEPTH-1];
    logic [31:0]  ram_c0_win_pc     [0:RAM_C0_WIN_DEPTH-1];
    logic [31:0]  ram_c0_win_instr  [0:RAM_C0_WIN_DEPTH-1];

    // Chapter 39 (part 2) — core-side 32-bit write observer for the
    // same runway window. Taps the CPU's ee_wr_* port BEFORE it
    // splits into kseg0 (ram_wr_*) vs useg-shadow (in-map
    // useg_shadow_mem) backings. ee_wr_addr[28:0] == phys in
    // [0xC0..0x1FF] matches both view prefixes (useg 0x00000XXX,
    // kseg0 0x800000XX, kseg1 0xA00000XX). addr[31:29] is kept
    // verbatim so we can tell which view the writer used.
    // Ch46: re-armed as a circular ring and deepened so late-run
    // writes (e.g. the overwrite of the ch40 stub observed at
    // cyc~293776 in ESCAPE_RING #20) no longer get truncated by the
    // early RAM-clear loop. Walkback fields per entry let us name
    // the CF site that called the routine doing the SW.
    localparam int           CPU_WIN_DEPTH = 128;
    int           cpu_win_count;
    int           cpu_win_cyc     [0:CPU_WIN_DEPTH-1];
    logic [31:0]  cpu_win_addr    [0:CPU_WIN_DEPTH-1];
    logic [31:0]  cpu_win_data    [0:CPU_WIN_DEPTH-1];
    logic [3:0]   cpu_win_be      [0:CPU_WIN_DEPTH-1];
    logic [31:0]  cpu_win_pc      [0:CPU_WIN_DEPTH-1];
    logic [31:0]  cpu_win_instr   [0:CPU_WIN_DEPTH-1];
    // Walkback context per entry (mirror of ch45 escape ring).
    logic [31:0]  cpu_win_cf_pc   [0:CPU_WIN_DEPTH-1];
    logic [31:0]  cpu_win_cf_instr[0:CPU_WIN_DEPTH-1];
    logic         cpu_win_cf_found[0:CPU_WIN_DEPTH-1];
    logic [4:0]   cpu_win_cf_rs   [0:CPU_WIN_DEPTH-1];
    int           cpu_win_write_idx;

    // Ch46: exact-word write triggers on physical bytes 0xC0 and
    // 0xC4 — the first two words of the ch40 stub. First-match
    // one-shots, with walkback context.
    logic         ram_c0w_captured;
    int           ram_c0w_cycle;
    logic [31:0]  ram_c0w_addr;
    logic [31:0]  ram_c0w_data;
    logic [3:0]   ram_c0w_be;
    logic [31:0]  ram_c0w_pc;
    logic [31:0]  ram_c0w_instr;
    logic [31:0]  ram_c0w_cf_pc;
    logic [31:0]  ram_c0w_cf_instr;
    logic         ram_c0w_cf_found;

    logic         ram_c4w_captured;
    int           ram_c4w_cycle;
    logic [31:0]  ram_c4w_addr;
    logic [31:0]  ram_c4w_data;
    logic [3:0]   ram_c4w_be;
    logic [31:0]  ram_c4w_pc;
    logic [31:0]  ram_c4w_instr;
    logic [31:0]  ram_c4w_cf_pc;
    logic [31:0]  ram_c4w_cf_instr;
    logic         ram_c4w_cf_found;

    // Chapter 51 — post-BEV exception-handler window observer.
    // The ch50 real-BIOS smoke showed the BIOS clears Status.BEV
    // at cyc=815 (MTC0 Status=0x10000 at pc=0xBFC021C0), which
    // flips the exception vector target from 0xBFC0_0380 to
    // EXC_VECTOR=0x80. But useg_shadow[0x80] is zero-filled, so
    // the first AdES vectors into nothing. Ch51's question: does
    // the BIOS *ever* install a RAM exception handler at
    // phys 0x80..0xBF after clearing BEV, and if so, when / from
    // where / into which address view (useg vs kseg0)?
    //
    // Two rings:
    //   EXC_W_WIN: every core-side (ee_wr_*) or physical
    //              (ee_ram_wr_*) write into [0x80..0xBF], gated
    //              on Status.BEV==0. Addr carries view info via
    //              top 3 bits. Walkback finds the most-recent
    //              J/JAL/JR/JALR caller.
    //   EXC_FETCH_WIN: every retire whose retired_pc falls in
    //              [0x80..0xBF], gated on Status.BEV==0. If the
    //              BIOS's install populates the handler, later
    //              exception entries should fetch real code
    //              here; if not, we'll see zero-fill fetches.
    //
    // Address-view classifier identical to ch39/ch46:
    //   addr[31:29] == 3'b000 → useg
    //                   3'b100 → kseg0
    //                   3'b101 → kseg1
    //                   3'b110 → kseg2
    //                   3'b111 → kseg3
    //
    // Both rings circular, depth 32. Post-halt summary lists
    // live slots in ascending chronological order.
    localparam int           EXC_WIN_DEPTH    = 32;
    localparam logic [28:0]  EXC_WIN_LOW_PHYS = 29'h0000_0080;
    localparam logic [28:0]  EXC_WIN_HIGH_PHYS= 29'h0000_00BF;
    int          exc_w_win_count;
    int          exc_w_win_cyc     [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_w_win_addr    [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_w_win_data    [0:EXC_WIN_DEPTH-1];
    logic [3:0]  exc_w_win_be      [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_w_win_pc      [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_w_win_instr   [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_w_win_cf_pc   [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_w_win_cf_instr[0:EXC_WIN_DEPTH-1];
    logic        exc_w_win_cf_found[0:EXC_WIN_DEPTH-1];
    logic [4:0]  exc_w_win_cf_rs   [0:EXC_WIN_DEPTH-1];
    int          exc_w_win_write_idx;
    int          exc_f_win_write_idx;

    // Fetch-ring (circular) — retires with pc in the handler
    // window after BEV cleared. Landing instr + live registers
    // tell us whether the fetch sees zero-fill (no install) or
    // real handler code.
    int          exc_f_win_count;
    int          exc_f_win_cyc     [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_f_win_pc      [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_f_win_instr   [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_f_win_r31     [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_f_win_epc     [0:EXC_WIN_DEPTH-1];
    logic [31:0] exc_f_win_badv    [0:EXC_WIN_DEPTH-1];

    // (B) $6 first entry into poison-word
    logic        r6_ptr_captured;
    int          r6_ptr_cycle;
    logic [31:0] r6_ptr_prev;
    logic [31:0] r6_ptr_new;
    logic [31:0] r6_ptr_retired_pc;
    logic [31:0] r6_ptr_retired_instr;
    logic [31:0] r6_ptr_retired_arg2;
    logic [31:0] r6_ptr_retired_arg3;

    // (B) $2 first entry into poison-word
    logic        r2_ptr_captured;
    int          r2_ptr_cycle;
    logic [31:0] r2_ptr_prev;
    logic [31:0] r2_ptr_new;
    logic [31:0] r2_ptr_retired_pc;
    logic [31:0] r2_ptr_retired_instr;
    logic [31:0] r2_ptr_retired_arg2;
    logic [31:0] r2_ptr_retired_arg3;

    // (B) $5 first entry into poison-word
    logic        r5_ptr_captured;
    int          r5_ptr_cycle;
    logic [31:0] r5_ptr_prev;
    logic [31:0] r5_ptr_new;
    logic [31:0] r5_ptr_retired_pc;
    logic [31:0] r5_ptr_retired_instr;
    logic [31:0] r5_ptr_retired_arg2;
    logic [31:0] r5_ptr_retired_arg3;
    logic [31:0] r5_prev_1;
    always_ff @(posedge clk) begin
        if (rst_n) r5_prev_1 <= u_core.regfile[5];
    end

    // ------------------------------------------------------------------
    // Chapter 14 — function-pointer provenance for $8 (Codex-directed).
    //
    // Chapter 13 proved the 0x14xx routine is entered via `jr $8` at
    // pc=0x00A8 with $8 = 0x1478. The highest-yield next question is:
    // who installed 0x1478 into $8 in the first place? If the BIOS
    // intentionally set up 0x1478 as a dispatch target, then the bug
    // is that the routine's *arguments* (notably $2 as a residual
    // counter) are wrong. If $8 itself is stale/corrupted, we have a
    // different class of bug upstream of the dispatch.
    //
    // Exact-match watcher on $8: first transition to 0x0000_1478 with
    // prior value != 0x1478. Captures the same set of fields as
    // chapter 11's r2/r6 triggers — retired_pc/instr/arg2/arg3 at
    // current AND one-edge-delayed, plus decoded rs/rt fields AND
    // live regfile values for those sources. For a LW,
    // arg2 = EA and arg3 = loaded_value (the 0x1478 itself); rs is
    // the base pointer. For an ALU op, arg2 = rd_idx and arg3 =
    // alu_result; rs/rt are the source operands. Either path is
    // directly readable.
    // ------------------------------------------------------------------

    localparam logic [31:0] DISPATCH_TARGET = 32'h0000_1478;

    // ------------------------------------------------------------------
    // Chapter 24 — $8 dispatch-base install observer (Codex-directed).
    //
    // Ch14 showed $8 = 0x1478 is intentionally computed via
    // `addiu $8, $8, 0x5C4` at pc=0x000000A4, starting from
    // prior $8 = 0x0EB4. Ch23 ruled out the low-RAM vector table
    // [0x200..0x3FF] as the source of 0x1478 dispatch. The
    // highest-yield next question: **where did $8 first become
    // 0x0EB4?** That base value has to come from somewhere —
    // typically LUI+ADDIU in ROM, a LW from a table, or a
    // register copy.
    //
    // Same shape as ch14 but with an earlier exact-match value.
    // Captures prev/new, retired_pc/instr/arg2/arg3 at current
    // AND one-edge-delayed edges, and decoded rs/rt live values.
    // ------------------------------------------------------------------

    localparam logic [31:0] DISPATCH_BASE = 32'h0000_0EB4;

    // ------------------------------------------------------------------
    // Chapter 26 — $9 index-selection first-write observer
    // (Codex-directed).
    //
    // Ch25 proved the "array" at RAM[0xE54..] is executable code, not
    // a data table, AND that the effective dispatch math is
    // `0xE54 + index*4 + 0x5C4`. For index = 0x18 (= 24) the target
    // is pc=0x1478 (the broken list-walker). **The choice of
    // $9 = 0x18 at pc=0x5E8 (just before the sll) is the single
    // branching point that directs dispatch into this path.**
    //
    // If $9 = 0x18 is hardcoded / loaded from a constant table, the
    // BIOS intends index 24 and the bug is elsewhere (live-in state
    // for 0x1478). If $9 is computed from an external signal (MMIO
    // read, RAM data that reflects a config), then the bug could be
    // that our simulated state steered $9 to 24 where real HW would
    // have selected a different routine.
    //
    // Same exact-match shape as ch14's R8_INSTALL and ch24's R8_BASE:
    // first-time $9 == 0x18 with prior value != 0x18. Captures
    // prev/new + retired_pc/instr/arg2/arg3 at current AND
    // one-edge-delayed snapshots, plus decoded rs/rt live values.
    // ------------------------------------------------------------------

    localparam logic [31:0] DISPATCH_INDEX = 32'h0000_0018;

    // ------------------------------------------------------------------
    // Chapter 27 — $2-write observer in the [R8_BASE, R8_INSTALL]
    // setup window (Codex-directed).
    //
    // Ch26 ruled out "wrong dispatch index" as the bug class; dispatch
    // to 0x1478 is intentional. Remaining theory: the list-walker's
    // live-in state is wrong, specifically $2 which it treats as
    // list-head. Between R8_BASE (cyc=289085) and R8_INSTALL
    // (cyc=335420) — ~46k cycles — real HW is presumed to set up $2
    // correctly. In our sim, the 0xEE0 scan loop's addiu $2,$2,4 is
    // the dominant writer and leaves $2 = 0x80001E28 (scan residual).
    //
    // If there's a LEGITIMATE setup write in that window (a LW from
    // a well-known RAM slot, or an ADDIU from a meaningful base), it
    // tells us what real HW would have left in $2 before the dispatch.
    // If there are NO such writes, the theory hardens: this path is
    // intentionally picking up register state from somewhere earlier
    // (or relies on a handoff we're missing).
    //
    // Shape: gated change-detector on regfile[2], window-bounded to
    // r8_base_captured && !r8_ex_captured, filtered to exclude the
    // known 0xEE0 scan-body at pc=0x0EE8 (the mechanical incrementer
    // is not the interesting write). Reuses the r6w opcode-class
    // signals from ch22 — those are per-instruction (independent of
    // destination register) so they work for any writer.
    // ------------------------------------------------------------------

    localparam int R2_SETUP_DEPTH = 16;

    // ------------------------------------------------------------------
    // Chapter 28 — scan-exit $k0/$k1 snapshot + $2 gap closer
    // (Codex-directed).
    //
    // Ch27 found no $2 setup write in [R8_BASE, R8_INSTALL]. The
    // remaining leading hypothesis is (iii) MMIO-zero-path for the
    // scan bound — if $k1 (the BNE comparand at pc=0x0EEC) is
    // derived from MMIO-influenced state, real HW would exit the
    // 0xEE0 scan at a different $2 value.
    //
    // Two observers:
    //
    //   SCAN_FINAL_EXIT — first retire at pc=0x00006A70 (the
    //   unambiguous scan-exit target: the `j 0x6A70` at pc=0x0EF4
    //   lands there). Captures $k0, $k1, $2, and the retire ring's
    //   top entries so we can see the exit BNE at pc=0x0EEC and the
    //   J at pc=0x0EF4. If $k0 == $k1 at exit, the scan terminated
    //   on the natural bound. Their values then tell us what the
    //   bound was.
    //
    //   R2_GAP — Codex low closure on ch27: that chapter's observer
    //   was gated [R8_BASE, R8_INSTALL] and intentionally skipped
    //   the 24-cycle [R9_INDEX, R8_BASE] interval. Cheap to close:
    //   count any $2 writes in that gap. Expected: zero.
    // ------------------------------------------------------------------

    localparam logic [31:0] SCAN_EXIT_TARGET = 32'h0000_6A70;

    // ------------------------------------------------------------------
    // Chapter 30 — $k1 (= $27) bound-origin observer (Codex-directed).
    //
    // Ch29 found the relevant scan exit (#1, cyc=335409) uses
    // $k1 = 0x2D24, a DIFFERENT bound from ch28/ch25's well-understood
    // $k1 = 0xF0C. The source of 0x2D24 is unknown. If it's a pure
    // hardcoded ADDIU/LUI+ORI, the second scan is BIOS-deterministic
    // and hypothesis (iii) dies for good. If it's a LW (especially
    // from an MMIO-ish or unmodeled region), hypothesis (iii) gains
    // evidence fast.
    //
    // Same exact-match shape as ch14/ch24/ch26: first transition from
    // `$27 != 0x2D24` to `$27 == 0x2D24`. Captures prev/new plus
    // retired_pc/instr/arg2/arg3 at current-edge AND one-edge-delayed,
    // plus decoded rs/rt live values. For a LOAD, arg2 is the EA and
    // arg3 is the loaded data; for an ALU op, arg2 is rd_idx and
    // arg3 is the alu_result.
    // ------------------------------------------------------------------

    localparam logic [31:0] K1_BOUND_VALUE = 32'h0000_2D24;

    // ------------------------------------------------------------------
    // Chapter 31 — LW-prologue observer at pc=0x1478 (Codex-directed).
    //
    // Ch30 cleanly killed hypothesis (iii) (scan bounds are BIOS-
    // hardcoded). Remaining theories:
    //   (i) pc=0x1478 is a fallback/default handler — uses $2 as-is.
    //   (ii) Real HW gets $2 from an unmodeled source.
    //
    // Direct test of (i): does the routine's prologue self-LOAD $2
    // (or a would-be list-head) from a fixed RAM slot before using
    // it? If yes, we can peek that slot and see what's missing. If no
    // (routine consumes $2 as received), hypothesis (i) hardens and
    // the bug lives in how we hand execution INTO 0x1478 with scan
    // residual rather than whatever the caller SHOULD have prepared.
    //
    // Ch15 already captured pc=0x1478's first 12 retires (ALL kinds,
    // not LW-specific). In that dump, the FIRST load is the fatal
    // `lw $2, 0($6)` at pc=0x14C4 — PROLOGUE[10]. So the evidence
    // already strongly favors hypothesis (i). This chapter is a
    // focused re-confirmation: LW-ONLY capture, deeper (16) so any
    // earlier load via a different base (e.g. `lw $X, offset($4)`
    // or via $gp/$sp) would have been caught if it existed.
    //
    // Per-entry: cycle, pc, instr, rs_idx (base reg), rs_val
    // (live base pointer), rt_idx (dest), EA (arg2), loaded_data
    // (arg3). The rs_idx/rs_val pair is the key: if rs is $gp/$sp/$4
    // (globally-set pointers), the routine is self-loading. If rs
    // is $6 (the scan-residual copy), it's consuming inherited state.
    // ------------------------------------------------------------------

    localparam logic [31:0] LW_PROLOGUE_LOW  = 32'h0000_1478;
    localparam logic [31:0] LW_PROLOGUE_HIGH = 32'h0000_14FF;
    localparam int          LW_PROLOGUE_DEPTH = 16;

    // ------------------------------------------------------------------
    // Chapter 32 — TLB/COP0 write observer (Codex-directed,
    // hypothesis (iv) investigation).
    //
    // Ch31 emerged the TLB-aliasing hypothesis: our map strips
    // phys=addr[28:0], conflating useg 0x0000_1E28 (where ch12 memcpy
    // wrote) with kseg0 0x8000_1E28 (where walker reads). On real
    // MIPS useg is TLB-mapped. Does the BIOS program TLB entries
    // before the bad path? If yes, real HW's useg mapping is
    // determined; our pass-through is still wrong but differently.
    // If no, real HW useg is UNDEFINED at memcpy time — our
    // pass-through aliasing is definitively wrong and useg writes
    // shouldn't land on kseg0 RAM at all.
    //
    // Captures:
    //   - MTC0 (opcode=0x10, rs=0x04): writes to any COP0 register.
    //     We care specifically about EntryHi($10), EntryLo0($2),
    //     EntryLo1($3), Index($0), PageMask($5), Context($4) — the
    //     TLB-programming registers.
    //   - TLBWI (funct=0x02), TLBWR (funct=0x06), TLBP (funct=0x08),
    //     TLBR (funct=0x01): actually commit TLB state. These are
    //     COP0 CO-mode instructions (opcode=0x10, rs=0x10).
    //
    // Gated on !entry14_captured so we see the pre-wedge window
    // without post-wedge noise. Depth 16 is plenty — typical BIOS
    // init sequences do a handful of TLB programs.
    // ------------------------------------------------------------------

    localparam int TLB_COP0_DEPTH = 64;

    int          tlb_cop0_count;
    int          tlb_cop0_cyc    [0:TLB_COP0_DEPTH-1];
    logic [31:0] tlb_cop0_pc     [0:TLB_COP0_DEPTH-1];
    logic [31:0] tlb_cop0_instr  [0:TLB_COP0_DEPTH-1];
    logic [4:0]  tlb_cop0_rt     [0:TLB_COP0_DEPTH-1];  // src GPR for MTC0
    logic [4:0]  tlb_cop0_rd     [0:TLB_COP0_DEPTH-1];  // COP0 reg number for MTC0
    logic [31:0] tlb_cop0_value  [0:TLB_COP0_DEPTH-1];  // for MTC0, the written value
    int          tlb_cop0_kind   [0:TLB_COP0_DEPTH-1];  // 0=MTC0, 1=TLBR, 2=TLBWI, 3=TLBWR, 4=TLBP

    // Classifier combinational signals — per-instruction, independent
    // of register fields.
    logic [5:0] tlbc_op;
    logic [4:0] tlbc_rs;
    logic [5:0] tlbc_funct;
    logic       tlbc_is_mtc0;
    logic       tlbc_is_tlb_co;
    logic       tlbc_is_tlbr;
    logic       tlbc_is_tlbwi;
    logic       tlbc_is_tlbwr;
    logic       tlbc_is_tlbp;
    logic       tlbc_fire;
    int         tlbc_kind;
    assign tlbc_op    = u_core.retired_instr[31:26];
    assign tlbc_rs    = u_core.retired_instr[25:21];
    assign tlbc_funct = u_core.retired_instr[5:0];
    assign tlbc_is_mtc0  = (tlbc_op == 6'h10) && (tlbc_rs == 5'h04);
    assign tlbc_is_tlb_co= (tlbc_op == 6'h10) && (tlbc_rs == 5'h10);
    assign tlbc_is_tlbr  = tlbc_is_tlb_co && (tlbc_funct == 6'h01);
    assign tlbc_is_tlbwi = tlbc_is_tlb_co && (tlbc_funct == 6'h02);
    assign tlbc_is_tlbwr = tlbc_is_tlb_co && (tlbc_funct == 6'h06);
    assign tlbc_is_tlbp  = tlbc_is_tlb_co && (tlbc_funct == 6'h08);
    assign tlbc_fire = tlbc_is_mtc0 || tlbc_is_tlbr || tlbc_is_tlbwi ||
                       tlbc_is_tlbwr || tlbc_is_tlbp;
    assign tlbc_kind = tlbc_is_mtc0  ? 0 :
                       tlbc_is_tlbr  ? 1 :
                       tlbc_is_tlbwi ? 2 :
                       tlbc_is_tlbwr ? 3 :
                       tlbc_is_tlbp  ? 4 : 5;

    int          lw_prologue_count;
    int          lw_prologue_cyc    [0:LW_PROLOGUE_DEPTH-1];
    logic [31:0] lw_prologue_pc     [0:LW_PROLOGUE_DEPTH-1];
    logic [31:0] lw_prologue_instr  [0:LW_PROLOGUE_DEPTH-1];
    logic [4:0]  lw_prologue_rs     [0:LW_PROLOGUE_DEPTH-1];
    logic [4:0]  lw_prologue_rt     [0:LW_PROLOGUE_DEPTH-1];
    logic [31:0] lw_prologue_rs_val [0:LW_PROLOGUE_DEPTH-1];
    logic [31:0] lw_prologue_ea     [0:LW_PROLOGUE_DEPTH-1];
    logic [31:0] lw_prologue_data   [0:LW_PROLOGUE_DEPTH-1];

    logic [31:0] k1_prev_1;
    always_ff @(posedge clk) begin
        if (rst_n) k1_prev_1 <= u_core.regfile[27];
    end

    logic        k1_ex_captured;
    int          k1_ex_cycle;
    logic [31:0] k1_ex_prev;
    logic [31:0] k1_ex_new;
    logic [31:0] k1_ex_retired_pc_now;
    logic [31:0] k1_ex_retired_instr_now;
    logic [31:0] k1_ex_retired_arg2_now;
    logic [31:0] k1_ex_retired_arg3_now;
    logic [31:0] k1_ex_retired_pc_d1;
    logic [31:0] k1_ex_retired_instr_d1;
    logic [31:0] k1_ex_retired_arg2_d1;
    logic [31:0] k1_ex_retired_arg3_d1;
    logic [4:0]  k1_ex_rs_idx;
    logic [4:0]  k1_ex_rt_idx;
    logic [31:0] k1_ex_rs_val;
    logic [31:0] k1_ex_rt_val;

    logic        scan_final_captured;
    int          scan_final_cycle;
    logic [31:0] scan_final_pc;
    logic [31:0] scan_final_k0;
    logic [31:0] scan_final_k1;
    logic [31:0] scan_final_r2;
    logic [31:0] scan_final_ring_prev_pc;     // ring head-1 (delay slot of J)
    logic [31:0] scan_final_ring_prev_instr;
    logic [31:0] scan_final_ring_prev2_pc;    // ring head-2 (the J 0x6A70)
    logic [31:0] scan_final_ring_prev2_instr;

    // R2_GAP: count of $2 writes in [R9_INDEX, R8_BASE]. Captured only
    // if nonzero (normally expected to be 0).
    int          r2_gap_count;
    int          r2_gap_cyc   [0:3];
    logic [31:0] r2_gap_pc    [0:3];
    logic [31:0] r2_gap_instr [0:3];
    logic [31:0] r2_gap_prev  [0:3];
    logic [31:0] r2_gap_new   [0:3];

    // ------------------------------------------------------------------
    // Chapter 29 — LAST scan-exit observer (Codex-directed refinement
    // of ch28's one-shot SCAN_FINAL_EXIT).
    //
    // Ch28 caught the FIRST retire at pc=0x6A70. But ENTRY14 sees
    // $2 = 0x80001E28, while the first exit shows $2 = 0x80000010 —
    // so a LATER scan pass is the one that actually feeds the
    // list-walker. This observer captures ALL retires at pc=0x6A70
    // in a circular ring (depth 8), until ENTRY14 fires. After that,
    // the entries in the ring are the most-recent exits before the
    // list-walker gets entered. The newest ring entry is the one we
    // care about most (the exit whose $2 feeds ENTRY14).
    //
    // Gate on !entry14_captured so post-wedge DEADBEEF-loop passes
    // through pc=0x6A70 (if any) don't pollute.
    // ------------------------------------------------------------------

    localparam int SCAN_EXIT_RING_DEPTH = 8;

    int          scan_last_total;
    int          scan_last_head;
    int          scan_last_cyc   [0:SCAN_EXIT_RING_DEPTH-1];
    logic [31:0] scan_last_k0    [0:SCAN_EXIT_RING_DEPTH-1];
    logic [31:0] scan_last_k1    [0:SCAN_EXIT_RING_DEPTH-1];
    logic [31:0] scan_last_r2    [0:SCAN_EXIT_RING_DEPTH-1];
    logic [31:0] scan_last_ring_prev_pc    [0:SCAN_EXIT_RING_DEPTH-1];
    logic [31:0] scan_last_ring_prev_instr [0:SCAN_EXIT_RING_DEPTH-1];

    int          r2_setup_count;
    int          r2_setup_cyc   [0:R2_SETUP_DEPTH-1];
    logic [31:0] r2_setup_pc    [0:R2_SETUP_DEPTH-1];
    logic [31:0] r2_setup_instr [0:R2_SETUP_DEPTH-1];
    logic [31:0] r2_setup_prev  [0:R2_SETUP_DEPTH-1];
    logic [31:0] r2_setup_new   [0:R2_SETUP_DEPTH-1];
    logic [31:0] r2_setup_arg2  [0:R2_SETUP_DEPTH-1];
    logic [31:0] r2_setup_arg3  [0:R2_SETUP_DEPTH-1];
    int          r2_setup_kind  [0:R2_SETUP_DEPTH-1];

    logic [31:0] r9_prev_1;
    always_ff @(posedge clk) begin
        if (rst_n) r9_prev_1 <= u_core.regfile[9];
    end

    logic        r9_ex_captured;
    int          r9_ex_cycle;
    logic [31:0] r9_ex_prev;
    logic [31:0] r9_ex_new;
    logic [31:0] r9_ex_retired_pc_now;
    logic [31:0] r9_ex_retired_instr_now;
    logic [31:0] r9_ex_retired_arg2_now;
    logic [31:0] r9_ex_retired_arg3_now;
    logic [31:0] r9_ex_retired_pc_d1;
    logic [31:0] r9_ex_retired_instr_d1;
    logic [31:0] r9_ex_retired_arg2_d1;
    logic [31:0] r9_ex_retired_arg3_d1;
    logic [4:0]  r9_ex_rs_idx;
    logic [4:0]  r9_ex_rt_idx;
    logic [31:0] r9_ex_rs_val;
    logic [31:0] r9_ex_rt_val;

    // ------------------------------------------------------------------
    // Chapter 15 — prologue observer (Codex-directed calling-convention
    // audit).
    //
    // Capture the first PROLOGUE_DEPTH retires inside the 0x14xx
    // routine body, with rs/rt field decode AND live source-register
    // values AND retired_arg2/arg3. For a LOAD, arg2 is the EA and
    // arg3 is the loaded word — so if the prologue starts with
    // `lw $X, offset($4)` we'll see EA = $4 + offset directly.
    // For an ALU op, arg2 is rd_idx and arg3 is the result.
    //
    // The point of this chapter: figure out which register the
    // routine actually TREATS as input. If the first few instructions
    // read $4 (standard MIPS a0), the calling convention is normal
    // and $2's residual value isn't the input. If they read $2 / $6
    // directly, the residual-counter theory hardens.
    // ------------------------------------------------------------------

    localparam logic [31:0] PROLOGUE_LOW   = 32'h0000_1478;
    localparam logic [31:0] PROLOGUE_HIGH  = 32'h0000_14FF;
    localparam int          PROLOGUE_DEPTH = 12;

    // ------------------------------------------------------------------
    // Chapter 16 — $2 seed observer (Codex-directed, narrow).
    //
    // Chapter 15 hardened the residual-counter theory: the 0x1478
    // routine treats $2 as a list-head pointer, but at entry $2 is
    // 0x80001E28 — the final value the 0xEE0 loop's
    // `addiu $2, $2, 4` produced. The interesting question is NOT
    // that the loop increments $2 (mechanical and known); it's
    // WHAT SET $2 BEFORE the loop started.
    //
    // Filter: capture every retire that writes $2 EXCEPT the known
    // loop body at pc=0x00000EE8. Detection of "writes $2" uses a
    // change-detector on u_core.regfile[2] with r2_prev_1 (already
    // declared in ch11). Depth 8 entries — enough to hold the seed
    // write and a few prior non-loop writes for context while
    // guaranteeing the seed isn't pushed out by loop noise.
    //
    // Per-entry capture: cycle, retired_pc, retired_instr, prev $2,
    // new $2, retired_arg2, retired_arg3. retired_arg2/arg3 encode
    // (EA, loaded_word) for loads and (rd_idx, alu_result) for ALU —
    // so a seed LW shows us which address the initial $2 value was
    // read from.
    // ------------------------------------------------------------------

    localparam logic [31:0] R2_LOOP_PC   = 32'h0000_0EE8;
    localparam int          R2_SEED_DEPTH = 8;

    // ------------------------------------------------------------------
    // Chapter 17 — control-flow seam audit (Codex-directed).
    //
    // Ch16 localized the $2 seed to a `lui $2, 0x8000` + 0xEE0 scan
    // that marches $2 up to 0x80001E28. Ch13 showed a separate block
    // at pc=0x00A0..0x00AC (addiu $8 / jr $8) dispatches to 0x1478.
    // The open question: is the scan and the dispatch one continuous
    // control-flow path (register reuse without cleanup bug), or do
    // they cross a function boundary (bad handoff / wrong live-in
    // register set)?
    //
    // Three seam triggers:
    //   SCAN_ENTRY     — first retire with pc in [0x0EC0, 0x0EFF].
    //                    Captures the caller (retire-ring head-1) and
    //                    $ra/$sp/$fp/$2 at entry.
    //   SCAN_EXIT      — first retire with pc outside that range,
    //                    AFTER scan entry fired. Captures both the
    //                    exit instruction (retire-ring head-1) and
    //                    the target, plus $ra/$sp/$fp/$2.
    //   DISPATCH_ENTRY — first retire with pc in [0x00A0, 0x00AF].
    //                    Same capture shape.
    //
    // Comparison plan: if $ra/$sp/$fp are identical across all three
    // trigger points, there was no function boundary — the scan and
    // dispatch are one continuous flow and the bug is "scan-cursor
    // value survives into list-walker via $2 because no cleanup."
    // If any of $ra/$sp/$fp change between triggers, there IS a
    // function boundary; the handoff is wrong because the callee
    // inherits $2 it didn't expect.
    // ------------------------------------------------------------------

    localparam logic [31:0] SCAN_REGION_LOW     = 32'h0000_0EC0;
    localparam logic [31:0] SCAN_REGION_HIGH    = 32'h0000_0EFF;
    localparam logic [31:0] DISPATCH_BLOCK_LOW  = 32'h0000_00A0;
    localparam logic [31:0] DISPATCH_BLOCK_HIGH = 32'h0000_00AF;

    // ------------------------------------------------------------------
    // Chapter 18 — MMIO-read -> branch correlator (Codex-directed,
    // sharpened variant of MMIO-zero-path theory).
    //
    // Hypothesis: some MMIO read in our sim returns 0 (stub default)
    // where real HW would return non-zero. The BIOS then takes a BEQ
    // or BNE on that loaded value and heads down a different boot
    // path — specifically, the scan+dispatch+list-walker chain that
    // ch17 proved is one continuous flow. If the BEQ had gone the
    // other way, BIOS would presumably have skipped the wedge-prone
    // chain and reached 0x1478 with valid live-in state (or never
    // reached 0x1478 at all).
    //
    // Per Codex's sharpening: don't log every MMIO read. Log only
    // MMIO reads whose DESTINATION register is consumed by a BEQ or
    // BNE within the next MMIO_LOOKAHEAD retires. That filters the
    // flood down to the reads that actually steer control flow.
    //
    // Per-register pending state, ages across retires, expires after
    // LOOKAHEAD. On BEQ/BNE retire, if rs or rt matches any pending
    // register, capture (load_*, branch_*) as one correlation.
    // Depth 8 correlations — enough to see a pattern without noise.
    //
    // MMIO region = any of our stubbed decode windows. An EA that
    // hits RAM or BIOS is NOT mmio for this purpose.
    // ------------------------------------------------------------------

    localparam int MMIO_LOOKAHEAD     = 16;  // retires (widened for BIOS load->use distance)
    localparam int MMIO_CORREL_DEPTH  = 8;

    // Debug counters: have we seen MMIO reads at all? BEQ/BNE at all?
    int dbg_mmio_reads;
    int dbg_branches;

    // ------------------------------------------------------------------
    // Chapter 19 — first ROM→RAM handoff observer (Codex-directed).
    //
    // Ch18 refuted the MMIO-zero-path theory (0 correlations over
    // 294k MMIO reads and 608k branches). The chain the BIOS runs in
    // RAM is not being steered by a stubbed MMIO value. The highest-
    // yield upstream question: what ROM-side JAL/JALR/J/JR brought
    // execution into RAM in the first place, and what arguments did
    // it hand off?
    //
    // Detector: first retire whose retired_pc is in RAM (phys[28:25]
    // == 4'h0 covers kseg0 0x8000_xxxx, kseg1 0xA000_xxxx, useg
    // 0x0000_xxxx) AND whose previous retire was in BIOS ROM
    // (phys[28:22] == 7'h7F covers 0xBFC0_xxxx and aliases). Use the
    // retire ring for authoritative previous retire(s): head-1 is the
    // delay-slot instruction; head-2 is the JAL/J that caused the
    // handoff. Capture both plus the RAM landing.
    //
    // Registers captured at the handoff edge: $4-$7 (MIPS args),
    // $28/gp, $29/sp, $30/fp, $31/ra. For a JAL, $31 should show the
    // return address (pc_of_JAL + 8). For a JR, $31 is residual. For
    // a J, no link. The register values tell us whether the caller
    // set up a proper call frame or whether this is a tail-jump that
    // inherits context.
    // ------------------------------------------------------------------

    logic        rom2ram_captured;
    int          rom2ram_cycle;
    logic [31:0] rom2ram_land_pc;
    logic [31:0] rom2ram_land_instr;
    logic [31:0] rom2ram_delay_pc;      // ring head-1
    logic [31:0] rom2ram_delay_instr;
    logic [31:0] rom2ram_caller_pc;     // ring head-2 (the JAL/J)
    logic [31:0] rom2ram_caller_instr;
    logic [31:0] rom2ram_r4;
    logic [31:0] rom2ram_r5;
    logic [31:0] rom2ram_r6;
    logic [31:0] rom2ram_r7;
    logic [31:0] rom2ram_r28;
    logic [31:0] rom2ram_r29;
    logic [31:0] rom2ram_r30;
    logic [31:0] rom2ram_r31;
    logic [31:0] rom2ram_r2;

    // ------------------------------------------------------------------
    // Chapter 20 — 0x0500 RAM-routine prologue observer (Codex-directed).
    //
    // Ch19 captured the ROM→RAM handoff: `jalr $31, $3` at
    // pc=0xBFC529E8 landed at pc=0x0500 with clean args $4/$5/$6/$7.
    // $6=0x14BC is a code pointer into the later list-walker — a
    // candidate "continuation pointer". Question: what does the
    // RAM-at-0x0500 routine DO with its arguments?
    //
    // Same shape as ch15's 0x1478 prologue observer. Capture the first
    // PROLOGUE2_DEPTH retires whose pc lies in [0x0500, 0x05FF], with
    // rs/rt decode + live source-register values + retired_arg2/arg3.
    // If the prologue:
    //   - reads $4 / $5  → data-argument calling convention
    //   - reads $6       → consumes the continuation pointer
    //   - reads $7       → uses the kseg1 BIOS pointer
    //   - stores $6 to stack or RAM → saves continuation for later jr
    //   - immediately overwrites $4-$7 → they were just-in-time args
    // ------------------------------------------------------------------

    localparam logic [31:0] PROLOGUE2_LOW   = 32'h0000_0500;
    localparam logic [31:0] PROLOGUE2_HIGH  = 32'h0000_05FF;
    localparam int          PROLOGUE2_DEPTH = 12;

    // ------------------------------------------------------------------
    // Chapter 21 — $6 consumer observer (Codex-directed).
    //
    // $6/a2 = 0x14BC was passed as an argument at the ROM→RAM
    // handoff. Ch20 showed the 0x500 memset ignores it. The question:
    // where is $6 FIRST consumed between the handoff (cyc=262063)
    // and ENTRY14 (where $6 has been overwritten to 0x2FC)?
    //
    // Semantic consumers (priority highest-first):
    //   (1) JR $6 / JALR $6  — definitive continuation use
    //   (2) LW/SW/LB/SB/LH/SH/LHU/LBU with rs=$6 — structure access
    //   (3) ADDU/ADDIU/OR/ORI/AND/ANDI copy rs=$6 or rt=$6 —
    //       preserves $6 into another register
    //   (4) BEQ/BNE/BLEZ/BGTZ/BLTZ/BGEZ with rs=$6 or rt=$6 —
    //       branch comparison
    //
    // Capture up to R6_CONSUMER_DEPTH=8 consumers in time order,
    // classified into category for fast reading. Gate on
    // rom2ram_captured && !entry14_captured to bound the window.
    //
    // A write to $6 that overwrites the handoff value WITHOUT
    // reading it first (rs != 6 and rt != 6) is not captured — that's
    // the "discard" path. But the observer runs before checking reads,
    // so a retire that both reads AND writes $6 (e.g., addu $6, $6, $0)
    // counts as a consumer via rs or rt match.
    // ------------------------------------------------------------------

    localparam int R6_CONSUMER_DEPTH = 8;

    // ------------------------------------------------------------------
    // Chapter 22 — $6 write tracker (Codex-directed).
    //
    // Ch21 proved $6 = 0x14BC was discarded: the first semantic
    // consumer at cycle 283384 saw $6 already down at 0x200+.
    // Question: which exact retire changed $6 from 0x14BC to 0x200?
    //
    // Change-detector on regfile[6] vs r6_prev_1. Gated to
    // rom2ram_captured && r6_consumer_count==0 so only PRE-consumer
    // writes are recorded. After the first consumer fires (the
    // increment loop at pc=0xBFC4FC7C), the ring stops filling.
    //
    // Per-entry: cycle, retired_pc, retired_instr, prev, new,
    // retired_arg2, retired_arg3, plus decoded opcode class
    // (LUI/ADDIU/LW/OTHER). retired_arg2/arg3 for loads give the
    // source address and loaded value directly.
    //
    // Depth 8 — more than enough; typically expect 1-2 writes
    // between the handoff and the first consumer.
    // ------------------------------------------------------------------

    localparam int R6_WRITE_DEPTH = 8;

    // ------------------------------------------------------------------
    // Chapter 23 — low-RAM vector-table write survey (Codex-directed).
    //
    // Ch22 localized the real surviving handoff arg as $7 = 0xBFC529B0,
    // which the ROM loop at pc=0xBFC4FC7C/84 splats wholesale into
    // low-RAM vector-table entries at RAM[0x200..]. The question this
    // chapter answers: does a LATER routine PATCH specific slots in
    // [0x200, 0x3FF] with DIFFERENT values (real handlers) after the
    // initial default fill? If yes, the post-fill patches are the
    // real dispatch table — and one of them may be how we later end
    // up in the 0x14xx list-walker.
    //
    // Observer: every retired store whose EA phys-stripped lies in
    // [0x200, 0x3FF]. Split into two buckets:
    //   - "fill": data == DEFAULT_FILL (= 0xBFC529B0). Just count.
    //   - "patch": data != DEFAULT_FILL. Capture up to 32 with full
    //     context (cycle, pc, instr, EA, data).
    //
    // Phys-stripped comparison so kseg aliases (useg 0x200, kseg0
    // 0x80000200, kseg1 0xA0000200) all land in the observer — the
    // BIOS might use any of them.
    //
    // Watch specifically for patches whose DATA matches known
    // landmarks in the later chain: 0x1478 (list-walker entry),
    // 0x14BC (list-walker BEQ = the 0x14BC the ROM caller once
    // passed in $6!), 0x500 (memset), or any pc in RAM.
    // ------------------------------------------------------------------

    localparam logic [28:0] VEC_TABLE_LOW  = 29'h0_0000_0200;
    localparam logic [28:0] VEC_TABLE_HIGH = 29'h0_0000_03FF;
    localparam logic [31:0] DEFAULT_FILL   = 32'hBFC5_29B0;
    localparam int          VEC_TABLE_DEPTH = 64;  // was 32, widened so
                                                   // all 114 patches (or
                                                   // most) fit.
    // "Interesting" patches: data value points into RAM code range
    // (phys[28:22] == 0, low 4MB of RAM). These are the patches that
    // could dispatch to the 0x1478 list-walker. Filter by phys to
    // also catch kseg0/useg aliases.
    localparam int          VEC_INTR_DEPTH = 16;

    // Three counters split by stored-data:
    //   - zero_fill: data == 0 (the very early [0x200..0x3FF] clear)
    //   - default_fill: data == 0xBFC529B0 (the $7-splat loop)
    //   - real_patch: anything else (captured in ring)
    int          vec_zero_fill_count;
    int          vec_default_fill_count;
    int          vec_patch_count;
    int          vec_patch_cyc   [0:VEC_TABLE_DEPTH-1];
    logic [31:0] vec_patch_pc    [0:VEC_TABLE_DEPTH-1];
    logic [31:0] vec_patch_instr [0:VEC_TABLE_DEPTH-1];
    logic [31:0] vec_patch_ea    [0:VEC_TABLE_DEPTH-1];
    logic [31:0] vec_patch_data  [0:VEC_TABLE_DEPTH-1];

    // Separate ring for patches whose DATA maps into low-RAM code
    // (phys[28:22] == 0 — the first 4 MB of RAM). These are the
    // candidates that could dispatch to pc=0x1478 and friends.
    int          vec_intr_count;
    int          vec_intr_cyc   [0:VEC_INTR_DEPTH-1];
    logic [31:0] vec_intr_pc    [0:VEC_INTR_DEPTH-1];
    logic [31:0] vec_intr_ea    [0:VEC_INTR_DEPTH-1];
    logic [31:0] vec_intr_data  [0:VEC_INTR_DEPTH-1];

    // Combinational range check on retired_arg2 phys-stripped.
    logic        vec_ea_in_range;
    assign vec_ea_in_range = (u_core.retired_arg2[28:0] >= VEC_TABLE_LOW) &&
                             (u_core.retired_arg2[28:0] <= VEC_TABLE_HIGH);

    int          r6_write_count;
    int          r6_write_cyc   [0:R6_WRITE_DEPTH-1];
    logic [31:0] r6_write_pc    [0:R6_WRITE_DEPTH-1];
    logic [31:0] r6_write_instr [0:R6_WRITE_DEPTH-1];
    logic [31:0] r6_write_prev  [0:R6_WRITE_DEPTH-1];
    logic [31:0] r6_write_new   [0:R6_WRITE_DEPTH-1];
    logic [31:0] r6_write_arg2  [0:R6_WRITE_DEPTH-1];
    logic [31:0] r6_write_arg3  [0:R6_WRITE_DEPTH-1];
    // 0=LUI, 1=ADDIU, 2=LW-family, 3=ALU-reg, 4=OTHER
    int          r6_write_kind  [0:R6_WRITE_DEPTH-1];

    // Combinational classifier for retired_instr as a $6 writer.
    // Categories picked per Codex's question ("LW, LUI, ADDIU, or
    // something else?").
    logic [5:0] r6w_op;
    logic [5:0] r6w_funct;
    logic       r6w_is_lui;
    logic       r6w_is_addiu;
    logic       r6w_is_load;
    logic       r6w_is_alu_reg;
    int         r6w_kind;
    assign r6w_op        = u_core.retired_instr[31:26];
    assign r6w_funct     = u_core.retired_instr[5:0];
    assign r6w_is_lui    = (r6w_op == 6'h0F);
    // ADDI (0x08) and ADDIU (0x09) share the immediate-arithmetic
    // role; semantically they're the same for this observer's
    // purposes (trapping-vs-not on overflow is irrelevant when we
    // just want to know "how did $6 get this value"). Codex low on
    // Ch22: group them together.
    assign r6w_is_addiu  = (r6w_op == 6'h08) || (r6w_op == 6'h09);
    assign r6w_is_load   = (r6w_op == 6'h20) || (r6w_op == 6'h21) ||
                           (r6w_op == 6'h23) || (r6w_op == 6'h24) ||
                           (r6w_op == 6'h25);
    assign r6w_is_alu_reg = (r6w_op == 6'h00) &&
                            (r6w_funct >= 6'h20) && (r6w_funct <= 6'h2B);
    assign r6w_kind = r6w_is_lui    ? 0 :
                      r6w_is_addiu  ? 1 :
                      r6w_is_load   ? 2 :
                      r6w_is_alu_reg? 3 : 4;

    int          r6_consumer_count;
    int          r6_consumer_cyc    [0:R6_CONSUMER_DEPTH-1];
    logic [31:0] r6_consumer_pc     [0:R6_CONSUMER_DEPTH-1];
    logic [31:0] r6_consumer_instr  [0:R6_CONSUMER_DEPTH-1];
    logic [31:0] r6_consumer_r6_val [0:R6_CONSUMER_DEPTH-1];
    logic [31:0] r6_consumer_arg2   [0:R6_CONSUMER_DEPTH-1];
    logic [31:0] r6_consumer_arg3   [0:R6_CONSUMER_DEPTH-1];
    // 0=jump, 1=load, 2=store, 3=alu, 4=branch
    int          r6_consumer_kind   [0:R6_CONSUMER_DEPTH-1];

    // Combinational decode of retired_instr for $6-consumer classifier.
    logic [5:0]  r6d_op;
    logic [4:0]  r6d_rs;
    logic [4:0]  r6d_rt;
    logic [5:0]  r6d_funct;
    logic        r6d_rs6, r6d_rt6;
    logic        r6d_is_jump;
    logic        r6d_is_load;
    logic        r6d_is_store;
    logic        r6d_is_alu;
    logic        r6d_is_branch;
    logic        r6d_is_consumer;
    int          r6d_kind;
    assign r6d_op    = u_core.retired_instr[31:26];
    assign r6d_rs    = u_core.retired_instr[25:21];
    assign r6d_rt    = u_core.retired_instr[20:16];
    assign r6d_funct = u_core.retired_instr[5:0];
    assign r6d_rs6   = (r6d_rs == 5'd6);
    assign r6d_rt6   = (r6d_rt == 5'd6);
    // JR $6 or JALR rd,$6: op=0x00 && funct in {0x08, 0x09} && rs=6
    assign r6d_is_jump  = (r6d_op == 6'h00) &&
                          ((r6d_funct == 6'h08) || (r6d_funct == 6'h09)) &&
                          r6d_rs6;
    // LW/LB/LH/LBU/LHU with base=$6
    assign r6d_is_load  = ((r6d_op == 6'h20) || (r6d_op == 6'h21) ||
                           (r6d_op == 6'h23) || (r6d_op == 6'h24) ||
                           (r6d_op == 6'h25)) && r6d_rs6;
    // SW/SH/SB with base=$6 (rs=base, rt=data)
    assign r6d_is_store = ((r6d_op == 6'h28) || (r6d_op == 6'h29) ||
                           (r6d_op == 6'h2B)) && r6d_rs6;
    // R-type ALU (funct 0x20-0x27 add/sub/and/or/xor/nor) or shift with rs=6
    // plus I-type ALU (op 0x08-0x0E, 0x0A, 0x0B) with rs=6
    assign r6d_is_alu   = ((r6d_op == 6'h00) &&
                           (r6d_funct >= 6'h20) && (r6d_funct <= 6'h2B) &&
                           (r6d_rs6 || r6d_rt6)) ||
                          ((r6d_op >= 6'h08) && (r6d_op <= 6'h0E) && r6d_rs6);
    // Branches: BEQ/BNE use rs AND rt; REGIMM/BLEZ/BGTZ use rs only
    assign r6d_is_branch = ((r6d_op == 6'h04) || (r6d_op == 6'h05)) &&
                           (r6d_rs6 || r6d_rt6) ||
                           ((r6d_op == 6'h01) || (r6d_op == 6'h06) ||
                            (r6d_op == 6'h07)) && r6d_rs6;
    assign r6d_is_consumer = r6d_is_jump || r6d_is_load || r6d_is_store ||
                             r6d_is_alu  || r6d_is_branch;
    // Kind priority: jump=0 > load=1 > store=2 > alu=3 > branch=4.
    // Only one category should match for a given instruction; defensive.
    assign r6d_kind = r6d_is_jump   ? 0 :
                      r6d_is_load   ? 1 :
                      r6d_is_store  ? 2 :
                      r6d_is_alu    ? 3 :
                      r6d_is_branch ? 4 : -1;

    int          prologue2_count;
    int          prologue2_cyc    [0:PROLOGUE2_DEPTH-1];
    logic [31:0] prologue2_pc     [0:PROLOGUE2_DEPTH-1];
    logic [31:0] prologue2_instr  [0:PROLOGUE2_DEPTH-1];
    logic [4:0]  prologue2_rs     [0:PROLOGUE2_DEPTH-1];
    logic [4:0]  prologue2_rt     [0:PROLOGUE2_DEPTH-1];
    logic [31:0] prologue2_rs_val [0:PROLOGUE2_DEPTH-1];
    logic [31:0] prologue2_rt_val [0:PROLOGUE2_DEPTH-1];
    logic [31:0] prologue2_arg2   [0:PROLOGUE2_DEPTH-1];
    logic [31:0] prologue2_arg3   [0:PROLOGUE2_DEPTH-1];

    // ring_prev2_idx = head-2 for two-back peek
    int ring_prev2_idx;
    always_comb begin
        ring_prev2_idx = (retire_ring_head + RETIRE_RING_DEPTH - 2)
                         % RETIRE_RING_DEPTH;
    end

    // Phys helpers for ROM / RAM classification.
    function automatic logic is_bios_pc(input logic [31:0] pc);
        return pc[28:22] == 7'h7F;
    endfunction
    function automatic logic is_ram_pc(input logic [31:0] pc);
        return pc[28:25] == 4'h0;
    endfunction

    logic        r_pending_valid     [0:31];
    int          r_pending_age       [0:31];
    int          r_pending_cycle     [0:31];
    logic [31:0] r_pending_load_pc   [0:31];
    logic [31:0] r_pending_load_ea   [0:31];
    logic [31:0] r_pending_load_data [0:31];

    int          mmio_correl_count;
    int          mmio_correl_load_cyc   [0:MMIO_CORREL_DEPTH-1];
    logic [31:0] mmio_correl_load_pc    [0:MMIO_CORREL_DEPTH-1];
    logic [31:0] mmio_correl_load_ea    [0:MMIO_CORREL_DEPTH-1];
    logic [31:0] mmio_correl_load_data  [0:MMIO_CORREL_DEPTH-1];
    int          mmio_correl_reg        [0:MMIO_CORREL_DEPTH-1];
    int          mmio_correl_br_cyc     [0:MMIO_CORREL_DEPTH-1];
    logic [31:0] mmio_correl_br_pc      [0:MMIO_CORREL_DEPTH-1];
    logic [31:0] mmio_correl_br_instr   [0:MMIO_CORREL_DEPTH-1];
    logic [31:0] mmio_correl_br_rs_val  [0:MMIO_CORREL_DEPTH-1];
    logic [31:0] mmio_correl_br_rt_val  [0:MMIO_CORREL_DEPTH-1];
    int          mmio_correl_age        [0:MMIO_CORREL_DEPTH-1];

    // Combinational MMIO-region check on EA. Kseg-stripped phys.
    // Broad definition: anything that isn't BIOS or RAM is treated as
    // "MMIO-ish" for this observer — that catches our four stubbed
    // windows (0x1F80, 0xFFFE_0, DMAC, INTC) AND the unmapped regions
    // (which return 0xDEADBEEF via the map). The latter is exactly
    // the "steered by stubbed value" case Codex is asking about.
    logic [28:0] mmio_ea_phys;
    logic        mmio_ea_in_bios;
    logic        mmio_ea_in_ram;
    logic        mmio_ea_is_mmio;
    assign mmio_ea_phys    = u_core.retired_arg2[28:0];
    assign mmio_ea_in_bios = (mmio_ea_phys[28:22] == 7'h7F);
    assign mmio_ea_in_ram  = (mmio_ea_phys[28:25] == 4'h0);
    assign mmio_ea_is_mmio = !mmio_ea_in_bios && !mmio_ea_in_ram;

    logic        scan_entry_captured;
    int          scan_entry_cycle;
    logic [31:0] scan_entry_pc;
    logic [31:0] scan_entry_instr;
    logic [31:0] scan_entry_caller_pc;
    logic [31:0] scan_entry_caller_instr;
    logic [31:0] scan_entry_ra;
    logic [31:0] scan_entry_sp;
    logic [31:0] scan_entry_fp;
    logic [31:0] scan_entry_r2;

    logic        scan_exit_captured;
    int          scan_exit_cycle;
    logic [31:0] scan_exit_target_pc;
    logic [31:0] scan_exit_target_instr;
    logic [31:0] scan_exit_site_pc;    // last scan-range retire
    logic [31:0] scan_exit_site_instr;
    logic [31:0] scan_exit_ra;
    logic [31:0] scan_exit_sp;
    logic [31:0] scan_exit_fp;
    logic [31:0] scan_exit_r2;

    logic        dispatch_entry_captured;
    int          dispatch_entry_cycle;
    logic [31:0] dispatch_entry_pc;
    logic [31:0] dispatch_entry_instr;
    logic [31:0] dispatch_entry_caller_pc;
    logic [31:0] dispatch_entry_caller_instr;
    logic [31:0] dispatch_entry_ra;
    logic [31:0] dispatch_entry_sp;
    logic [31:0] dispatch_entry_fp;
    logic [31:0] dispatch_entry_r2;

    // Track whether current retired_pc is inside the scan region.
    // Used by SCAN_EXIT trigger to fire on the first retire after
    // scan_entry_captured where pc leaves the range.
    logic scan_in_region;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            scan_in_region <= 1'b0;
        end else if (core_ev_valid) begin
            scan_in_region <= (u_core.retired_pc >= SCAN_REGION_LOW &&
                               u_core.retired_pc <= SCAN_REGION_HIGH);
        end
    end

    // Circular ring — always holds the most recent R2_SEED_DEPTH
    // non-loop writes to $2. `count` is monotone (total writes
    // observed so far, saturating is unnecessary since int is large).
    // `head` is the next slot to fill, wraps mod depth. On timeout
    // we walk (head-1..head-depth) to print newest-first.
    int          r2_seed_count;
    int          r2_seed_head;
    int          r2_seed_cyc   [0:R2_SEED_DEPTH-1];
    logic [31:0] r2_seed_pc    [0:R2_SEED_DEPTH-1];
    logic [31:0] r2_seed_instr [0:R2_SEED_DEPTH-1];
    logic [31:0] r2_seed_prev  [0:R2_SEED_DEPTH-1];
    logic [31:0] r2_seed_new   [0:R2_SEED_DEPTH-1];
    logic [31:0] r2_seed_arg2  [0:R2_SEED_DEPTH-1];
    logic [31:0] r2_seed_arg3  [0:R2_SEED_DEPTH-1];

    int          prologue_count;
    int          prologue_cyc    [0:PROLOGUE_DEPTH-1];
    logic [31:0] prologue_pc     [0:PROLOGUE_DEPTH-1];
    logic [31:0] prologue_instr  [0:PROLOGUE_DEPTH-1];
    logic [4:0]  prologue_rs     [0:PROLOGUE_DEPTH-1];
    logic [4:0]  prologue_rt     [0:PROLOGUE_DEPTH-1];
    logic [31:0] prologue_rs_val [0:PROLOGUE_DEPTH-1];
    logic [31:0] prologue_rt_val [0:PROLOGUE_DEPTH-1];
    logic [31:0] prologue_arg2   [0:PROLOGUE_DEPTH-1];
    logic [31:0] prologue_arg3   [0:PROLOGUE_DEPTH-1];

    logic [31:0] r8_prev_1;
    always_ff @(posedge clk) begin
        if (rst_n) r8_prev_1 <= u_core.regfile[8];
    end

    logic        r8_ex_captured;
    int          r8_ex_cycle;
    logic [31:0] r8_ex_prev;
    logic [31:0] r8_ex_new;
    logic [31:0] r8_ex_retired_pc_now;
    logic [31:0] r8_ex_retired_instr_now;
    logic [31:0] r8_ex_retired_arg2_now;
    logic [31:0] r8_ex_retired_arg3_now;
    logic [31:0] r8_ex_retired_pc_d1;
    logic [31:0] r8_ex_retired_instr_d1;
    logic [31:0] r8_ex_retired_arg2_d1;
    logic [31:0] r8_ex_retired_arg3_d1;
    logic [4:0]  r8_ex_rs_idx;
    logic [4:0]  r8_ex_rt_idx;
    logic [31:0] r8_ex_rs_val;
    logic [31:0] r8_ex_rt_val;

    // Ch24: first-write observer for $8 == DISPATCH_BASE (0x0EB4).
    // Parallel structure to r8_ex_* above.
    logic        r8_base_captured;
    int          r8_base_cycle;
    logic [31:0] r8_base_prev;
    logic [31:0] r8_base_new;
    logic [31:0] r8_base_retired_pc_now;
    logic [31:0] r8_base_retired_instr_now;
    logic [31:0] r8_base_retired_arg2_now;
    logic [31:0] r8_base_retired_arg3_now;
    logic [31:0] r8_base_retired_pc_d1;
    logic [31:0] r8_base_retired_instr_d1;
    logic [31:0] r8_base_retired_arg2_d1;
    logic [31:0] r8_base_retired_arg3_d1;
    logic [4:0]  r8_base_rs_idx;
    logic [4:0]  r8_base_rt_idx;
    logic [31:0] r8_base_rs_val;
    logic [31:0] r8_base_rt_val;

    initial begin
        unmapped_count = 0;
        for (int i = 0; i < UNMAPPED_DEPTH; i++) begin
            unmapped_cycle[i]  = 0;
            unmapped_pc[i]     = 32'd0;
            unmapped_is_wr[i]  = 1'b0;
            unmapped_addr[i]   = 32'd0;
            unmapped_data[i]   = 32'd0;
        end
        retire_ring_count = 0;
        retire_ring_head  = 0;
        for (int i = 0; i < RETIRE_RING_DEPTH; i++) begin
            retire_ring_pc[i]    = 32'd0;
            retire_ring_instr[i] = 32'd0;
            retire_ring_r2[i]    = 32'd0;
            retire_ring_r6[i]    = 32'd0;
            retire_ring_cyc[i]   = 0;
        end
        lineage_captured       = 1'b0;
        lineage_cycle          = 0;
        lineage_pc             = 32'd0;
        lineage_instr          = 32'd0;
        lineage_opcode         = 6'd0;
        lineage_rs             = 5'd0;
        lineage_rt             = 5'd0;
        lineage_rd             = 5'd0;
        lineage_imm            = 16'd0;
        lineage_rs_val         = 32'd0;
        lineage_rt_val         = 32'd0;
        lineage_rd_val         = 32'd0;
        lineage_effective_addr = 32'd0;
        lineage_poison_addr    = 32'd0;
        lineage_poison_data    = 32'd0;

        r2_prev_1        = 32'd0;
        r6_prev_1        = 32'd0;
        retired_pc_d1    = 32'd0;
        retired_instr_d1 = 32'd0;
        retired_arg2_d1  = 32'd0;
        retired_arg3_d1  = 32'd0;

        r2_tx_captured          = 1'b0;
        r2_tx_cycle             = 0;
        r2_tx_prev              = 32'd0;
        r2_tx_new               = 32'd0;
        r2_tx_retired_pc_now    = 32'd0;
        r2_tx_retired_instr_now = 32'd0;
        r2_tx_retired_arg2_now  = 32'd0;
        r2_tx_retired_arg3_now  = 32'd0;
        r2_tx_retired_pc_d1     = 32'd0;
        r2_tx_retired_instr_d1  = 32'd0;
        r2_tx_retired_arg2_d1   = 32'd0;
        r2_tx_retired_arg3_d1   = 32'd0;

        r6_tx_captured          = 1'b0;
        r6_tx_cycle             = 0;
        r6_tx_prev              = 32'd0;
        r6_tx_new               = 32'd0;
        r6_tx_retired_pc_now    = 32'd0;
        r6_tx_retired_instr_now = 32'd0;
        r6_tx_retired_arg2_now  = 32'd0;
        r6_tx_retired_arg3_now  = 32'd0;
        r6_tx_retired_pc_d1     = 32'd0;
        r6_tx_retired_instr_d1  = 32'd0;
        r6_tx_retired_arg2_d1   = 32'd0;
        r6_tx_retired_arg3_d1   = 32'd0;

        r2_ex_captured          = 1'b0;
        r2_ex_cycle             = 0;
        r2_ex_retired_pc_now    = 32'd0;
        r2_ex_retired_instr_now = 32'd0;
        r2_ex_retired_arg2_now  = 32'd0;
        r2_ex_retired_arg3_now  = 32'd0;
        r2_ex_retired_pc_d1     = 32'd0;
        r2_ex_retired_instr_d1  = 32'd0;
        r2_ex_retired_arg2_d1   = 32'd0;
        r2_ex_retired_arg3_d1   = 32'd0;

        r6_ex_captured          = 1'b0;
        r6_ex_cycle             = 0;
        r6_ex_retired_pc_now    = 32'd0;
        r6_ex_retired_instr_now = 32'd0;
        r6_ex_retired_arg2_now  = 32'd0;
        r6_ex_retired_arg3_now  = 32'd0;
        r6_ex_retired_pc_d1     = 32'd0;
        r6_ex_retired_instr_d1  = 32'd0;
        r6_ex_retired_arg2_d1   = 32'd0;
        r6_ex_retired_arg3_d1   = 32'd0;

        ramwr_captured          = 1'b0;
        ramwr_cycle             = 0;
        ramwr_retired_pc        = 32'd0;
        ramwr_retired_instr     = 32'd0;
        ramwr_retired_arg2      = 32'd0;
        ramwr_retired_arg3      = 32'd0;
        ramwr_opcode            = 6'd0;
        ramwr_rs_idx            = 5'd0;
        ramwr_rt_idx            = 5'd0;
        ramwr_imm               = 16'd0;
        ramwr_rs_val_at_retire  = 32'd0;
        ramwr_rt_val_at_retire  = 32'd0;

        ramwr_any_count = 0;
        for (int i = 0; i < RAMWR_DEPTH; i++) begin
            ramwr_any_cycle[i] = 0;
            ramwr_any_pc[i]    = 32'd0;
            ramwr_any_instr[i] = 32'd0;
            ramwr_any_ea[i]    = 32'd0;
            ramwr_any_data[i]  = 32'd0;
        end

        entry14_captured            = 1'b0;
        entry14_cycle               = 0;
        entry14_retired_pc          = 32'd0;
        entry14_retired_instr       = 32'd0;
        entry14_prev_retired_pc     = 32'd0;
        entry14_prev_retired_instr  = 32'd0;
        entry14_ring_prev_pc        = 32'd0;
        entry14_ring_prev_instr     = 32'd0;
        entry14_r2                  = 32'd0;
        entry14_r4                  = 32'd0;
        entry14_r5                  = 32'd0;
        entry14_r6                  = 32'd0;
        entry14_r7                  = 32'd0;
        entry14_r28                 = 32'd0;
        entry14_r29                 = 32'd0;
        entry14_r30                 = 32'd0;
        entry14_r31                 = 32'd0;

        entry200_captured           = 1'b0;
        entry200_cycle              = 0;
        entry200_retired_pc         = 32'd0;
        entry200_retired_instr      = 32'd0;
        entry200_prev_retired_pc    = 32'd0;
        entry200_prev_retired_instr = 32'd0;
        entry200_ring_prev_pc       = 32'd0;
        entry200_ring_prev_instr    = 32'd0;
        entry200_ring_prev_cyc      = 0;
        entry200_ring_prev2_pc      = 32'd0;
        entry200_ring_prev2_instr   = 32'd0;
        entry200_ring_prev2_cyc     = 0;
        entry200_r2                 = 32'd0;
        entry200_r4                 = 32'd0;
        entry200_r5                 = 32'd0;
        entry200_r6                 = 32'd0;
        entry200_r7                 = 32'd0;
        entry200_r8                 = 32'd0;
        entry200_r9                 = 32'd0;
        entry200_r26                = 32'd0;
        entry200_r27                = 32'd0;
        entry200_r28                = 32'd0;
        entry200_r29                = 32'd0;
        entry200_r30                = 32'd0;
        entry200_r31                = 32'd0;

        runway_count              = 0;
        runway_prehist_captured   = 1'b0;
        for (int i = 0; i < RUNWAY_DEPTH; i++) begin
            runway_cyc[i]   = 0;
            runway_pc[i]    = 32'd0;
            runway_instr[i] = 32'd0;
            runway_r31[i]   = 32'd0;
        end
        for (int i = 0; i < RUNWAY_PREHIST_DEPTH; i++) begin
            runway_prehist_cyc[i]   = 0;
            runway_prehist_pc[i]    = 32'd0;
            runway_prehist_instr[i] = 32'd0;
        end

        r31_prev_valid                   = 1'b0;
        r31_prev                         = 32'd0;
        r31_to_target_captured           = 1'b0;
        r31_to_target_cycle              = 0;
        r31_to_target_retired_pc         = 32'd0;
        r31_to_target_retired_instr      = 32'd0;
        r31_to_target_retired_arg2       = 32'd0;
        r31_to_target_retired_arg3       = 32'd0;
        r31_to_target_prev_r31           = 32'd0;
        r31_to_target_r8                 = 32'd0;
        r31_to_target_r9                 = 32'd0;
        r31_to_target_rs_idx             = 5'd0;
        r31_to_target_rt_idx             = 5'd0;
        r31_to_target_rd_idx             = 5'd0;
        r31_to_target_rs_val             = 32'd0;
        r31_to_target_ring_prev_pc       = 32'd0;
        r31_to_target_ring_prev_instr    = 32'd0;
        r31_to_target_ring_prev2_pc      = 32'd0;
        r31_to_target_ring_prev2_instr   = 32'd0;

        rom_escape_captured          = 1'b0;
        rom_escape_cycle             = 0;
        rom_escape_retired_pc        = 32'd0;
        rom_escape_retired_instr     = 32'd0;
        rom_escape_ring_prev_pc      = 32'd0;
        rom_escape_ring_prev_instr   = 32'd0;
        rom_escape_ring_prev_cyc     = 0;
        rom_escape_ring_prev2_pc     = 32'd0;
        rom_escape_ring_prev2_instr  = 32'd0;
        rom_escape_ring_prev2_cyc    = 0;
        rom_escape_r8                = 32'd0;
        rom_escape_r9                = 32'd0;
        rom_escape_r10               = 32'd0;
        rom_escape_r31               = 32'd0;
        rom_escape_r31_unchanged     = 1'b0;

        escape_ring_count = 0;
        for (int i = 0; i < ESCAPE_RING_DEPTH; i++) begin
            escape_ring_cyc[i]      = 0;
            escape_ring_pc[i]       = 32'd0;
            escape_ring_instr[i]    = 32'd0;
            escape_ring_r1_pc[i]    = 32'd0;
            escape_ring_r1_instr[i] = 32'd0;
            escape_ring_r1_cyc[i]   = 0;
            escape_ring_r2_pc[i]    = 32'd0;
            escape_ring_r2_instr[i] = 32'd0;
            escape_ring_r2_cyc[i]   = 0;
            escape_ring_cf_pc[i]    = 32'd0;
            escape_ring_cf_instr[i] = 32'd0;
            escape_ring_cf_cyc[i]   = 0;
            escape_ring_cf_hops[i]  = 0;
            escape_ring_cf_found[i] = 1'b0;
            escape_ring_cf_rs[i]    = 5'd0;
            escape_ring_r8[i]       = 32'd0;
            escape_ring_r9[i]       = 32'd0;
            escape_ring_r10[i]      = 32'd0;
            escape_ring_r31[i]      = 32'd0;
        end

        r8_prev_valid = 1'b0;
        r8_prev       = 32'd0;
        r9_prev_valid = 1'b0;
        r9_prev       = 32'd0;

        // Ch180 — $1 provenance + ENTRY598 init.
        r1_prev_valid = 1'b0;
        r1_prev       = 32'd0;
        r1_ring_count = 0;
        // Ch182 — $31 provenance init.
        r31_chg_prev_valid = 1'b0;
        r31_chg_prev       = 32'd0;
        r31_ring_count = 0;
        // Ch193 — halt-point + exception ring init.
        halt_point_captured = 1'b0;
        halt_point_cycle    = 0;
        halt_point_pc       = 32'd0;
        halt_point_instr    = 32'd0;
        halt_point_epc      = 32'd0;
        halt_point_cause    = 32'd0;
        halt_point_status   = 32'd0;
        halt_point_v0       = 32'd0;
        halt_point_v1       = 32'd0;
        halt_point_a0       = 32'd0;
        halt_point_a1       = 32'd0;
        halt_point_a2       = 32'd0;
        halt_point_a3       = 32'd0;
        halt_point_ra       = 32'd0;
        exc_ring_count      = 0;
        halt_point_badvaddr = 32'd0;
        halt_point_k0       = 32'd0;
        halt_point_k1       = 32'd0;
        halt_point_sp       = 32'd0;
        halt_point_gp       = 32'd0;
        for (int i = 0; i < EXC_RING_DEPTH; i++) begin
            exc_ring_cyc[i]      = 0;
            exc_ring_pc[i]       = 32'd0;
            exc_ring_instr[i]    = 32'd0;
            exc_ring_cause[i]    = 32'd0;
            exc_ring_epc[i]      = 32'd0;
            exc_ring_excode[i]   = 5'd0;
            exc_ring_was_halt[i] = 1'b0;
            exc_ring_badvaddr[i] = 32'd0;
            exc_ring_next_pc[i]      = 32'd0;
            exc_ring_next_pending[i] = 1'b0;
            exc_ring_next_valid[i]   = 1'b0;
        end

        // Ch195 — $v0/$v1/$a0 change rings init.
        ch195_r2_prev = 32'd0; ch195_r2_prev_valid = 1'b0; ch195_r2_count = 0;
        ch195_r3_prev = 32'd0; ch195_r3_prev_valid = 1'b0; ch195_r3_count = 0;
        ch195_r4_prev = 32'd0; ch195_r4_prev_valid = 1'b0; ch195_r4_count = 0;
        for (int i = 0; i < CH195_RING_DEPTH; i++) begin
            ch195_r2_cyc[i]   = 0;
            ch195_r2_pv[i]    = 32'd0;
            ch195_r2_nv[i]    = 32'd0;
            ch195_r2_pc[i]    = 32'd0;
            ch195_r2_instr[i] = 32'd0;
            ch195_r3_cyc[i]   = 0;
            ch195_r3_pv[i]    = 32'd0;
            ch195_r3_nv[i]    = 32'd0;
            ch195_r3_pc[i]    = 32'd0;
            ch195_r3_instr[i] = 32'd0;
            ch195_r4_cyc[i]   = 0;
            ch195_r4_pv[i]    = 32'd0;
            ch195_r4_nv[i]    = 32'd0;
            ch195_r4_pc[i]    = 32'd0;
            ch195_r4_instr[i] = 32'd0;
        end

        // Ch188 — A0 syscall shim flags init.
        ch188_shim_installed   = 1'b0;
        ch188_first_a0_logged  = 1'b0;
        ch188_a0_call_count    = 0;

        // Ch187 — low-RAM write ring + first-PC-0xA0 + bad-jalr peek init.
        low_ram_wr_count   = 0;
        for (int i = 0; i < LOW_RAM_WR_DEPTH; i++) begin
            low_ram_wr_cyc[i]  = 0;
            low_ram_wr_addr[i] = 32'd0;
            low_ram_wr_data[i] = 32'd0;
            low_ram_wr_pc[i]   = 32'd0;
        end
        first_pc_a0_captured = 1'b0;
        first_pc_a0_cycle    = 0;
        first_pc_a0_instr    = 32'd0;
        first_pc_a0_r31      = 32'd0;
        first_pc_a0_r10      = 32'd0;
        badjalr_peek_080     = 32'd0;
        badjalr_peek_0a0     = 32'd0;
        badjalr_peek_0c0     = 32'd0;
        badjalr_peek_500     = 32'd0;
        badjalr_peek_598     = 32'd0;

        // Ch186 — first-RAM-dispatcher-entry + writes-to-RAM init.
        first_ram_disp_captured           = 1'b0;
        first_ram_disp_cycle              = 0;
        first_ram_disp_pc                 = 32'd0;
        first_ram_disp_instr              = 32'd0;
        first_ram_disp_r1                 = 32'd0;
        first_ram_disp_r2                 = 32'd0;
        first_ram_disp_r3                 = 32'd0;
        first_ram_disp_r4                 = 32'd0;
        first_ram_disp_r8                 = 32'd0;
        first_ram_disp_r10                = 32'd0;
        first_ram_disp_r29                = 32'd0;
        first_ram_disp_r31                = 32'd0;
        first_ram_disp_ring_head_at_trigger = 0;
        for (int i = 0; i < 32; i++) begin
            first_ram_disp_ring_pc[i]    = 32'd0;
            first_ram_disp_ring_instr[i] = 32'd0;
            first_ram_disp_ring_cyc[i]   = 0;
        end
        ram_disp_wr_count = 0;
        for (int i = 0; i < RAM_DISP_WR_DEPTH; i++) begin
            ram_disp_wr_cyc[i]  = 0;
            ram_disp_wr_addr[i] = 32'd0;
            ram_disp_wr_data[i] = 32'd0;
            ram_disp_wr_pc[i]   = 32'd0;
        end

        // Ch184 — patch ring + per-entry snapshot init.
        patch_ring_count = 0;
        for (int i = 0; i < PATCH_RING_DEPTH; i++) begin
            patch_ring_cyc[i]  = 0;
            patch_ring_addr[i] = 32'd0;
            patch_ring_data[i] = 32'd0;
            patch_ring_be[i]   = 4'd0;
        end
        for (int i = 0; i < ENTRY_SNAP_DEPTH; i++) begin
            entry_snap_cyc[i]      = 0;
            entry_snap_r1[i]       = 32'd0;
            entry_snap_r10[i]      = 32'd0;
            entry_snap_r31[i]      = 32'd0;
            entry_snap_instr5a8[i] = 32'd0;
            entry_snap_instr5ac[i] = 32'd0;
            entry_snap_instr5b0[i] = 32'd0;
            entry_snap_instr5b4[i] = 32'd0;
            entry_snap_instr5b8[i] = 32'd0;
        end

        // Ch183 — $10 ring + bad-jalr capture init.
        r10_chg_prev_valid = 1'b0;
        r10_chg_prev       = 32'd0;
        r10_ring_count     = 0;
        for (int i = 0; i < R10_RING_DEPTH; i++) begin
            r10_ring_cyc[i]   = 0;
            r10_ring_prev[i]  = 32'd0;
            r10_ring_new[i]   = 32'd0;
            r10_ring_pc[i]    = 32'd0;
            r10_ring_instr[i] = 32'd0;
        end
        bad_jalr_captured                    = 1'b0;
        bad_jalr_cycle                       = 0;
        bad_jalr_r10                         = 32'd0;
        bad_jalr_r1                          = 32'd0;
        bad_jalr_r8                          = 32'd0;
        bad_jalr_r2                          = 32'd0;
        bad_jalr_r3                          = 32'd0;
        bad_jalr_r4                          = 32'd0;
        bad_jalr_r5                          = 32'd0;
        bad_jalr_r29                         = 32'd0;
        bad_jalr_r31                         = 32'd0;
        bad_jalr_r10_ring_count_at_trigger   = 0;
        bad_jalr_r1_ring_count_at_trigger    = 0;
        bad_jalr_r31_ring_count_at_trigger   = 0;
        for (int i = 0; i < R31_RING_DEPTH; i++) begin
            r31_ring_cyc[i]   = 0;
            r31_ring_prev[i]  = 32'd0;
            r31_ring_new[i]   = 32'd0;
            r31_ring_pc[i]    = 32'd0;
            r31_ring_instr[i] = 32'd0;
        end
        for (int i = 0; i < R1_RING_DEPTH; i++) begin
            r1_ring_cyc[i]      = 0;
            r1_ring_prev[i]     = 32'd0;
            r1_ring_new[i]      = 32'd0;
            r1_ring_pc[i]       = 32'd0;
            r1_ring_instr[i]    = 32'd0;
            r1_ring_pc_d1[i]    = 32'd0;
            r1_ring_instr_d1[i] = 32'd0;
        end
        entry598_captured     = 1'b0;
        entry598_count        = 0;
        entry598_cycle        = 0;
        entry598_r1           = 32'd0;
        entry598_r2           = 32'd0;
        entry598_r3           = 32'd0;
        entry598_r4           = 32'd0;
        entry598_r5           = 32'd0;
        entry598_r6           = 32'd0;
        entry598_r7           = 32'd0;
        entry598_r8           = 32'd0;
        entry598_r29          = 32'd0;
        entry598_r30          = 32'd0;
        entry598_r31          = 32'd0;
        entry598_ring_prev_pc    = 32'd0;
        entry598_ring_prev_instr = 32'd0;
        entry598_ring_prev2_pc    = 32'd0;
        entry598_ring_prev2_instr = 32'd0;
        entry598_ring_prev3_pc    = 32'd0;
        entry598_ring_prev3_instr = 32'd0;
        entry598_ring_prev4_pc    = 32'd0;
        entry598_ring_prev4_instr = 32'd0;
        entry5a8_any_captured       = 1'b0;
        entry5a8_anomalous_captured = 1'b0;
        entry5a8_total_count        = 0;
        entry5a8_anomalous_count    = 0;
        entry5a8_cycle              = 0;
        entry5a8_r1                 = 32'd0;
        entry5a8_r2                 = 32'd0;
        entry5a8_r3                 = 32'd0;
        entry5a8_r31                = 32'd0;
        entry5a8_prev_pc            = 32'd0;
        entry5a8_prev_instr         = 32'd0;
        entry5a8_ring_prev2_pc      = 32'd0;
        entry5a8_ring_prev2_instr   = 32'd0;
        entry5a8_ring_prev3_pc      = 32'd0;
        entry5a8_ring_prev3_instr   = 32'd0;
        entry5a8_ring_prev4_pc      = 32'd0;
        entry5a8_ring_prev4_instr   = 32'd0;

        r3_prev_valid = 1'b0;
        r3_prev       = 32'd0;
        r3_ring_count = 0;
        for (int i = 0; i < R3_RING_DEPTH; i++) begin
            r3_ring_cyc[i]   = 0;
            r3_ring_prev[i]  = 32'd0;
            r3_ring_new[i]   = 32'd0;
            r3_ring_pc[i]    = 32'd0;
            r3_ring_instr[i] = 32'd0;
            r3_ring_arg2[i]  = 32'd0;
            r3_ring_arg3[i]  = 32'd0;
        end

        r10_prev_valid                = 1'b0;
        r10_prev                      = 32'd0;
        r10_to_c0_captured            = 1'b0;
        r10_to_c0_cycle               = 0;
        r10_to_c0_retired_pc          = 32'd0;
        r10_to_c0_retired_instr       = 32'd0;
        r10_to_c0_retired_arg2        = 32'd0;
        r10_to_c0_retired_arg3        = 32'd0;
        r10_to_c0_prev_r10            = 32'd0;
        r10_to_c0_rs_idx              = 5'd0;
        r10_to_c0_rt_idx              = 5'd0;
        r10_to_c0_rd_idx              = 5'd0;
        r10_to_c0_rs_val              = 32'd0;
        r10_to_c0_rt_val              = 32'd0;
        r10_to_c0_ring_prev_pc        = 32'd0;
        r10_to_c0_ring_prev_instr     = 32'd0;
        r10_to_c0_ring_prev2_pc       = 32'd0;
        r10_to_c0_ring_prev2_instr    = 32'd0;

        ram_c0_exact_captured         = 1'b0;
        ram_c0_exact_cycle            = 0;
        ram_c0_exact_data             = 128'd0;
        ram_c0_exact_be               = 16'd0;
        ram_c0_exact_master           = 8'd0;
        ram_c0_exact_retired_pc       = 32'd0;
        ram_c0_exact_retired_instr    = 32'd0;
        ram_c0_exact_retired_was_write = 1'b0;
        ram_c0_win_count              = 0;
        for (int i = 0; i < RAM_C0_WIN_DEPTH; i++) begin
            ram_c0_win_cyc[i]    = 0;
            ram_c0_win_qw_idx[i] = 21'd0;
            ram_c0_win_be[i]     = 16'd0;
            ram_c0_win_data[i]   = 128'd0;
            ram_c0_win_master[i] = 8'd0;
            ram_c0_win_pc[i]     = 32'd0;
            ram_c0_win_instr[i]  = 32'd0;
        end

        cpu_win_count     = 0;
        cpu_win_write_idx = 0;
        for (int i = 0; i < CPU_WIN_DEPTH; i++) begin
            cpu_win_cyc[i]      = 0;
            cpu_win_addr[i]     = 32'd0;
            cpu_win_data[i]     = 32'd0;
            cpu_win_be[i]       = 4'd0;
            cpu_win_pc[i]       = 32'd0;
            cpu_win_instr[i]    = 32'd0;
            cpu_win_cf_pc[i]    = 32'd0;
            cpu_win_cf_instr[i] = 32'd0;
            cpu_win_cf_found[i] = 1'b0;
            cpu_win_cf_rs[i]    = 5'd0;
        end

        ram_c0w_captured  = 1'b0;
        ram_c0w_cycle     = 0;
        ram_c0w_addr      = 32'd0;
        ram_c0w_data      = 32'd0;
        ram_c0w_be        = 4'd0;
        ram_c0w_pc        = 32'd0;
        ram_c0w_instr     = 32'd0;
        ram_c0w_cf_pc     = 32'd0;
        ram_c0w_cf_instr  = 32'd0;
        ram_c0w_cf_found  = 1'b0;

        ram_c4w_captured  = 1'b0;
        ram_c4w_cycle     = 0;
        ram_c4w_addr      = 32'd0;
        ram_c4w_data      = 32'd0;
        ram_c4w_be        = 4'd0;
        ram_c4w_pc        = 32'd0;
        ram_c4w_instr     = 32'd0;
        ram_c4w_cf_pc     = 32'd0;
        ram_c4w_cf_instr  = 32'd0;
        ram_c4w_cf_found  = 1'b0;

        exc_w_win_count     = 0;
        exc_w_win_write_idx = 0;
        exc_f_win_count     = 0;
        for (int i = 0; i < EXC_WIN_DEPTH; i++) begin
            exc_w_win_cyc[i]      = 0;
            exc_w_win_addr[i]     = 32'd0;
            exc_w_win_data[i]     = 32'd0;
            exc_w_win_be[i]       = 4'd0;
            exc_w_win_pc[i]       = 32'd0;
            exc_w_win_instr[i]    = 32'd0;
            exc_w_win_cf_pc[i]    = 32'd0;
            exc_w_win_cf_instr[i] = 32'd0;
            exc_w_win_cf_found[i] = 1'b0;
            exc_w_win_cf_rs[i]    = 5'd0;
            exc_f_win_cyc[i]      = 0;
            exc_f_win_pc[i]       = 32'd0;
            exc_f_win_instr[i]    = 32'd0;
            exc_f_win_r31[i]      = 32'd0;
            exc_f_win_epc[i]      = 32'd0;
            exc_f_win_badv[i]     = 32'd0;
        end

        prologue_count = 0;
        for (int i = 0; i < PROLOGUE_DEPTH; i++) begin
            prologue_cyc[i]    = 0;
            prologue_pc[i]     = 32'd0;
            prologue_instr[i]  = 32'd0;
            prologue_rs[i]     = 5'd0;
            prologue_rt[i]     = 5'd0;
            prologue_rs_val[i] = 32'd0;
            prologue_rt_val[i] = 32'd0;
            prologue_arg2[i]   = 32'd0;
            prologue_arg3[i]   = 32'd0;
        end

        r2_seed_count = 0;
        r2_seed_head  = 0;
        for (int i = 0; i < R2_SEED_DEPTH; i++) begin
            r2_seed_cyc[i]   = 0;
            r2_seed_pc[i]    = 32'd0;
            r2_seed_instr[i] = 32'd0;
            r2_seed_prev[i]  = 32'd0;
            r2_seed_new[i]   = 32'd0;
            r2_seed_arg2[i]  = 32'd0;
            r2_seed_arg3[i]  = 32'd0;
        end

        scan_in_region              = 1'b0;
        scan_entry_captured         = 1'b0;
        scan_entry_cycle            = 0;
        scan_entry_pc               = 32'd0;
        scan_entry_instr            = 32'd0;
        scan_entry_caller_pc        = 32'd0;
        scan_entry_caller_instr     = 32'd0;
        scan_entry_ra               = 32'd0;
        scan_entry_sp               = 32'd0;
        scan_entry_fp               = 32'd0;
        scan_entry_r2               = 32'd0;

        scan_exit_captured          = 1'b0;
        scan_exit_cycle             = 0;
        scan_exit_target_pc         = 32'd0;
        scan_exit_target_instr      = 32'd0;
        scan_exit_site_pc           = 32'd0;
        scan_exit_site_instr        = 32'd0;
        scan_exit_ra                = 32'd0;
        scan_exit_sp                = 32'd0;
        scan_exit_fp                = 32'd0;
        scan_exit_r2                = 32'd0;

        dispatch_entry_captured     = 1'b0;
        dispatch_entry_cycle        = 0;
        dispatch_entry_pc           = 32'd0;
        dispatch_entry_instr        = 32'd0;
        dispatch_entry_caller_pc    = 32'd0;
        dispatch_entry_caller_instr = 32'd0;
        dispatch_entry_ra           = 32'd0;
        dispatch_entry_sp           = 32'd0;
        dispatch_entry_fp           = 32'd0;
        dispatch_entry_r2           = 32'd0;

        for (int i = 0; i < 32; i++) begin
            r_pending_valid[i]     = 1'b0;
            r_pending_age[i]       = 0;
            r_pending_cycle[i]     = 0;
            r_pending_load_pc[i]   = 32'd0;
            r_pending_load_ea[i]   = 32'd0;
            r_pending_load_data[i] = 32'd0;
        end

        rom2ram_captured     = 1'b0;
        rom2ram_cycle        = 0;
        rom2ram_land_pc      = 32'd0;
        rom2ram_land_instr   = 32'd0;
        rom2ram_delay_pc     = 32'd0;
        rom2ram_delay_instr  = 32'd0;
        rom2ram_caller_pc    = 32'd0;
        rom2ram_caller_instr = 32'd0;
        rom2ram_r4           = 32'd0;
        rom2ram_r5           = 32'd0;
        rom2ram_r6           = 32'd0;
        rom2ram_r7           = 32'd0;
        rom2ram_r28          = 32'd0;
        rom2ram_r29          = 32'd0;
        rom2ram_r30          = 32'd0;
        rom2ram_r31          = 32'd0;
        rom2ram_r2           = 32'd0;

        prologue2_count = 0;
        for (int i = 0; i < PROLOGUE2_DEPTH; i++) begin
            prologue2_cyc[i]    = 0;
            prologue2_pc[i]     = 32'd0;
            prologue2_instr[i]  = 32'd0;
            prologue2_rs[i]     = 5'd0;
            prologue2_rt[i]     = 5'd0;
            prologue2_rs_val[i] = 32'd0;
            prologue2_rt_val[i] = 32'd0;
            prologue2_arg2[i]   = 32'd0;
            prologue2_arg3[i]   = 32'd0;
        end

        r6_consumer_count = 0;
        for (int i = 0; i < R6_CONSUMER_DEPTH; i++) begin
            r6_consumer_cyc[i]    = 0;
            r6_consumer_pc[i]     = 32'd0;
            r6_consumer_instr[i]  = 32'd0;
            r6_consumer_r6_val[i] = 32'd0;
            r6_consumer_arg2[i]   = 32'd0;
            r6_consumer_arg3[i]   = 32'd0;
            r6_consumer_kind[i]   = 0;
        end

        r6_write_count = 0;
        for (int i = 0; i < R6_WRITE_DEPTH; i++) begin
            r6_write_cyc[i]   = 0;
            r6_write_pc[i]    = 32'd0;
            r6_write_instr[i] = 32'd0;
            r6_write_prev[i]  = 32'd0;
            r6_write_new[i]   = 32'd0;
            r6_write_arg2[i]  = 32'd0;
            r6_write_arg3[i]  = 32'd0;
            r6_write_kind[i]  = 0;
        end

        vec_zero_fill_count    = 0;
        vec_default_fill_count = 0;
        vec_patch_count        = 0;
        for (int i = 0; i < VEC_TABLE_DEPTH; i++) begin
            vec_patch_cyc[i]   = 0;
            vec_patch_pc[i]    = 32'd0;
            vec_patch_instr[i] = 32'd0;
            vec_patch_ea[i]    = 32'd0;
            vec_patch_data[i]  = 32'd0;
        end
        vec_intr_count = 0;
        for (int i = 0; i < VEC_INTR_DEPTH; i++) begin
            vec_intr_cyc[i]  = 0;
            vec_intr_pc[i]   = 32'd0;
            vec_intr_ea[i]   = 32'd0;
            vec_intr_data[i] = 32'd0;
        end
        mmio_correl_count = 0;
        dbg_mmio_reads = 0;
        dbg_branches = 0;
        for (int i = 0; i < MMIO_CORREL_DEPTH; i++) begin
            mmio_correl_load_cyc[i]   = 0;
            mmio_correl_load_pc[i]    = 32'd0;
            mmio_correl_load_ea[i]    = 32'd0;
            mmio_correl_load_data[i]  = 32'd0;
            mmio_correl_reg[i]        = 0;
            mmio_correl_br_cyc[i]     = 0;
            mmio_correl_br_pc[i]      = 32'd0;
            mmio_correl_br_instr[i]   = 32'd0;
            mmio_correl_br_rs_val[i]  = 32'd0;
            mmio_correl_br_rt_val[i]  = 32'd0;
            mmio_correl_age[i]        = 0;
        end

        r6_ptr_captured     = 1'b0;
        r6_ptr_cycle        = 0;
        r6_ptr_prev         = 32'd0;
        r6_ptr_new          = 32'd0;
        r6_ptr_retired_pc   = 32'd0;
        r6_ptr_retired_instr= 32'd0;
        r6_ptr_retired_arg2 = 32'd0;
        r6_ptr_retired_arg3 = 32'd0;

        r2_ptr_captured     = 1'b0;
        r2_ptr_cycle        = 0;
        r2_ptr_prev         = 32'd0;
        r2_ptr_new          = 32'd0;
        r2_ptr_retired_pc   = 32'd0;
        r2_ptr_retired_instr= 32'd0;
        r2_ptr_retired_arg2 = 32'd0;
        r2_ptr_retired_arg3 = 32'd0;

        r5_ptr_captured     = 1'b0;
        r5_ptr_cycle        = 0;
        r5_ptr_prev         = 32'd0;
        r5_ptr_new          = 32'd0;
        r5_ptr_retired_pc   = 32'd0;
        r5_ptr_retired_instr= 32'd0;
        r5_ptr_retired_arg2 = 32'd0;
        r5_ptr_retired_arg3 = 32'd0;
        r5_prev_1           = 32'd0;

        r8_prev_1               = 32'd0;
        r8_ex_captured          = 1'b0;
        r8_ex_cycle             = 0;
        r8_ex_prev              = 32'd0;
        r8_ex_new               = 32'd0;
        r8_ex_retired_pc_now    = 32'd0;
        r8_ex_retired_instr_now = 32'd0;
        r8_ex_retired_arg2_now  = 32'd0;
        r8_ex_retired_arg3_now  = 32'd0;
        r8_ex_retired_pc_d1     = 32'd0;
        r8_ex_retired_instr_d1  = 32'd0;
        r8_ex_retired_arg2_d1   = 32'd0;
        r8_ex_retired_arg3_d1   = 32'd0;
        r8_ex_rs_idx            = 5'd0;
        r8_ex_rt_idx            = 5'd0;
        r8_ex_rs_val            = 32'd0;
        r8_ex_rt_val            = 32'd0;

        r8_base_captured          = 1'b0;
        r8_base_cycle             = 0;
        r8_base_prev              = 32'd0;
        r8_base_new               = 32'd0;
        r8_base_retired_pc_now    = 32'd0;
        r8_base_retired_instr_now = 32'd0;
        r8_base_retired_arg2_now  = 32'd0;
        r8_base_retired_arg3_now  = 32'd0;
        r8_base_retired_pc_d1     = 32'd0;
        r8_base_retired_instr_d1  = 32'd0;
        r8_base_retired_arg2_d1   = 32'd0;
        r8_base_retired_arg3_d1   = 32'd0;
        r8_base_rs_idx            = 5'd0;
        r8_base_rt_idx            = 5'd0;
        r8_base_rs_val            = 32'd0;
        r8_base_rt_val            = 32'd0;

        r9_prev_1                 = 32'd0;
        r9_ex_captured            = 1'b0;
        r9_ex_cycle               = 0;
        r9_ex_prev                = 32'd0;
        r9_ex_new                 = 32'd0;
        r9_ex_retired_pc_now      = 32'd0;
        r9_ex_retired_instr_now   = 32'd0;
        r9_ex_retired_arg2_now    = 32'd0;
        r9_ex_retired_arg3_now    = 32'd0;
        r9_ex_retired_pc_d1       = 32'd0;
        r9_ex_retired_instr_d1    = 32'd0;
        r9_ex_retired_arg2_d1     = 32'd0;
        r9_ex_retired_arg3_d1     = 32'd0;
        r9_ex_rs_idx              = 5'd0;
        r9_ex_rt_idx              = 5'd0;
        r9_ex_rs_val              = 32'd0;
        r9_ex_rt_val              = 32'd0;

        r2_setup_count = 0;
        for (int i = 0; i < R2_SETUP_DEPTH; i++) begin
            r2_setup_cyc[i]   = 0;
            r2_setup_pc[i]    = 32'd0;
            r2_setup_instr[i] = 32'd0;
            r2_setup_prev[i]  = 32'd0;
            r2_setup_new[i]   = 32'd0;
            r2_setup_arg2[i]  = 32'd0;
            r2_setup_arg3[i]  = 32'd0;
            r2_setup_kind[i]  = 0;
        end

        scan_final_captured         = 1'b0;
        scan_final_cycle            = 0;
        scan_final_pc               = 32'd0;
        scan_final_k0               = 32'd0;
        scan_final_k1               = 32'd0;
        scan_final_r2               = 32'd0;
        scan_final_ring_prev_pc     = 32'd0;
        scan_final_ring_prev_instr  = 32'd0;
        scan_final_ring_prev2_pc    = 32'd0;
        scan_final_ring_prev2_instr = 32'd0;

        r2_gap_count = 0;
        for (int i = 0; i < 4; i++) begin
            r2_gap_cyc[i]   = 0;
            r2_gap_pc[i]    = 32'd0;
            r2_gap_instr[i] = 32'd0;
            r2_gap_prev[i]  = 32'd0;
            r2_gap_new[i]   = 32'd0;
        end

        scan_last_total = 0;
        scan_last_head  = 0;
        for (int i = 0; i < SCAN_EXIT_RING_DEPTH; i++) begin
            scan_last_cyc[i]             = 0;
            scan_last_k0[i]              = 32'd0;
            scan_last_k1[i]              = 32'd0;
            scan_last_r2[i]              = 32'd0;
            scan_last_ring_prev_pc[i]    = 32'd0;
            scan_last_ring_prev_instr[i] = 32'd0;
        end

        k1_prev_1                 = 32'd0;
        k1_ex_captured            = 1'b0;
        k1_ex_cycle               = 0;
        k1_ex_prev                = 32'd0;
        k1_ex_new                 = 32'd0;
        k1_ex_retired_pc_now      = 32'd0;
        k1_ex_retired_instr_now   = 32'd0;
        k1_ex_retired_arg2_now    = 32'd0;
        k1_ex_retired_arg3_now    = 32'd0;
        k1_ex_retired_pc_d1       = 32'd0;
        k1_ex_retired_instr_d1    = 32'd0;
        k1_ex_retired_arg2_d1     = 32'd0;
        k1_ex_retired_arg3_d1     = 32'd0;
        k1_ex_rs_idx              = 5'd0;
        k1_ex_rt_idx              = 5'd0;
        k1_ex_rs_val              = 32'd0;
        k1_ex_rt_val              = 32'd0;

        lw_prologue_count = 0;
        for (int i = 0; i < LW_PROLOGUE_DEPTH; i++) begin
            lw_prologue_cyc[i]    = 0;
            lw_prologue_pc[i]     = 32'd0;
            lw_prologue_instr[i]  = 32'd0;
            lw_prologue_rs[i]     = 5'd0;
            lw_prologue_rt[i]     = 5'd0;
            lw_prologue_rs_val[i] = 32'd0;
            lw_prologue_ea[i]     = 32'd0;
            lw_prologue_data[i]   = 32'd0;
        end

        tlb_cop0_count = 0;
        for (int i = 0; i < TLB_COP0_DEPTH; i++) begin
            tlb_cop0_cyc[i]   = 0;
            tlb_cop0_pc[i]    = 32'd0;
            tlb_cop0_instr[i] = 32'd0;
            tlb_cop0_rt[i]    = 5'd0;
            tlb_cop0_rd[i]    = 5'd0;
            tlb_cop0_value[i] = 32'd0;
            tlb_cop0_kind[i]  = 0;
        end
    end

    // Dense retire ring — log every retire. u_core.retired_pc /
    // retired_instr are updated on the same edge core_ev_valid fires,
    // so sampling them here gets the just-retired instruction. $2 and
    // $6 are the loop's working and pointer registers; snapshotting
    // their live regfile values one cycle later would be more
    // accurate, but sampling at retire is simpler and good enough
    // for lineage — the next retire in the ring will show the
    // follow-on state.
    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid) begin
            retire_ring_pc[retire_ring_head]    <= u_core.retired_pc;
            retire_ring_instr[retire_ring_head] <= u_core.retired_instr;
            retire_ring_r2[retire_ring_head]    <= u_core.regfile[2];
            retire_ring_r6[retire_ring_head]    <= u_core.regfile[6];
            retire_ring_cyc[retire_ring_head]   <= sim_cycle_ctr;
            retire_ring_head  <= (retire_ring_head + 1) % RETIRE_RING_DEPTH;
            retire_ring_count <= retire_ring_count + 1;
        end
    end

    // Register-transition triggers. Fire exactly once per register when
    // the register's value FIRST enters the 0x3xxx_xxxx poison band
    // (i.e. the band the DEADBEEF-wedge read address 0x3084_FFFF lives
    // in). The detector compares the value at THIS edge to the value
    // one edge ago; a transition into the band with the prior value
    // outside the band is what we latch. We snapshot two views of the
    // retire context: at the current edge AND at the prior edge, to
    // sidestep any 1-cycle NBA ambiguity about when exactly the
    // regfile commit became visible.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r2_tx_captured <= 1'b0;
            r6_tx_captured <= 1'b0;
            r2_ex_captured <= 1'b0;
            r6_ex_captured <= 1'b0;
        end else begin
            if (!r2_tx_captured &&
                u_core.regfile[2][31:28] == 4'h3 &&
                r2_prev_1[31:28]         != 4'h3) begin
                r2_tx_captured          <= 1'b1;
                r2_tx_cycle             <= sim_cycle_ctr;
                r2_tx_prev              <= r2_prev_1;
                r2_tx_new               <= u_core.regfile[2];
                r2_tx_retired_pc_now    <= u_core.retired_pc;
                r2_tx_retired_instr_now <= u_core.retired_instr;
                r2_tx_retired_arg2_now  <= u_core.retired_arg2;
                r2_tx_retired_arg3_now  <= u_core.retired_arg3;
                r2_tx_retired_pc_d1     <= retired_pc_d1;
                r2_tx_retired_instr_d1  <= retired_instr_d1;
                r2_tx_retired_arg2_d1   <= retired_arg2_d1;
                r2_tx_retired_arg3_d1   <= retired_arg3_d1;
                $display("[tb_ee_core_bios_smoke] TRIG_R2 cycle=%0d prev=0x%08h new=0x%08h",
                         sim_cycle_ctr, r2_prev_1, u_core.regfile[2]);
                $display("[tb_ee_core_bios_smoke] TRIG_R2   now   retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         u_core.retired_pc, u_core.retired_instr,
                         u_core.retired_arg2, u_core.retired_arg3);
                $display("[tb_ee_core_bios_smoke] TRIG_R2   d1    retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         retired_pc_d1, retired_instr_d1,
                         retired_arg2_d1, retired_arg3_d1);
            end
            if (!r6_tx_captured &&
                u_core.regfile[6][31:28] == 4'h3 &&
                r6_prev_1[31:28]         != 4'h3) begin
                r6_tx_captured          <= 1'b1;
                r6_tx_cycle             <= sim_cycle_ctr;
                r6_tx_prev              <= r6_prev_1;
                r6_tx_new               <= u_core.regfile[6];
                r6_tx_retired_pc_now    <= u_core.retired_pc;
                r6_tx_retired_instr_now <= u_core.retired_instr;
                r6_tx_retired_arg2_now  <= u_core.retired_arg2;
                r6_tx_retired_arg3_now  <= u_core.retired_arg3;
                r6_tx_retired_pc_d1     <= retired_pc_d1;
                r6_tx_retired_instr_d1  <= retired_instr_d1;
                r6_tx_retired_arg2_d1   <= retired_arg2_d1;
                r6_tx_retired_arg3_d1   <= retired_arg3_d1;
                $display("[tb_ee_core_bios_smoke] TRIG_R6 cycle=%0d prev=0x%08h new=0x%08h",
                         sim_cycle_ctr, r6_prev_1, u_core.regfile[6]);
                $display("[tb_ee_core_bios_smoke] TRIG_R6   now   retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         u_core.retired_pc, u_core.retired_instr,
                         u_core.retired_arg2, u_core.retired_arg3);
                $display("[tb_ee_core_bios_smoke] TRIG_R6   d1    retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         retired_pc_d1, retired_instr_d1,
                         retired_arg2_d1, retired_arg3_d1);
            end
            if (!r2_ex_captured &&
                u_core.regfile[2] == POISON_VALUE &&
                r2_prev_1         != POISON_VALUE) begin
                r2_ex_captured          <= 1'b1;
                r2_ex_cycle             <= sim_cycle_ctr;
                r2_ex_retired_pc_now    <= u_core.retired_pc;
                r2_ex_retired_instr_now <= u_core.retired_instr;
                r2_ex_retired_arg2_now  <= u_core.retired_arg2;
                r2_ex_retired_arg3_now  <= u_core.retired_arg3;
                r2_ex_retired_pc_d1     <= retired_pc_d1;
                r2_ex_retired_instr_d1  <= retired_instr_d1;
                r2_ex_retired_arg2_d1   <= retired_arg2_d1;
                r2_ex_retired_arg3_d1   <= retired_arg3_d1;
                $display("[tb_ee_core_bios_smoke] EXACT_R2 cycle=%0d prev=0x%08h new=0x%08h",
                         sim_cycle_ctr, r2_prev_1, u_core.regfile[2]);
                $display("[tb_ee_core_bios_smoke] EXACT_R2   now   retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         u_core.retired_pc, u_core.retired_instr,
                         u_core.retired_arg2, u_core.retired_arg3);
                $display("[tb_ee_core_bios_smoke] EXACT_R2   d1    retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         retired_pc_d1, retired_instr_d1,
                         retired_arg2_d1, retired_arg3_d1);
            end
            if (!r6_ex_captured &&
                u_core.regfile[6] == POISON_VALUE &&
                r6_prev_1         != POISON_VALUE) begin
                r6_ex_captured          <= 1'b1;
                r6_ex_cycle             <= sim_cycle_ctr;
                r6_ex_retired_pc_now    <= u_core.retired_pc;
                r6_ex_retired_instr_now <= u_core.retired_instr;
                r6_ex_retired_arg2_now  <= u_core.retired_arg2;
                r6_ex_retired_arg3_now  <= u_core.retired_arg3;
                r6_ex_retired_pc_d1     <= retired_pc_d1;
                r6_ex_retired_instr_d1  <= retired_instr_d1;
                r6_ex_retired_arg2_d1   <= retired_arg2_d1;
                r6_ex_retired_arg3_d1   <= retired_arg3_d1;
                $display("[tb_ee_core_bios_smoke] EXACT_R6 cycle=%0d prev=0x%08h new=0x%08h",
                         sim_cycle_ctr, r6_prev_1, u_core.regfile[6]);
                $display("[tb_ee_core_bios_smoke] EXACT_R6   now   retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         u_core.retired_pc, u_core.retired_instr,
                         u_core.retired_arg2, u_core.retired_arg3);
                $display("[tb_ee_core_bios_smoke] EXACT_R6   d1    retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         retired_pc_d1, retired_instr_d1,
                         retired_arg2_d1, retired_arg3_d1);
            end
        end
    end

    // Chapter 12 — RAM-write observer. Two triggers:
    //
    //   Narrow: the first retired store that targets the exact virtual
    //   address POISON_RAM_ADDR with exact data POISON_VALUE. Fires at
    //   most once. If this never fires, the poison was planted some
    //   other way — sub-word store sequence, kseg-alias store, or
    //   DMA (which doesn't go through core_ev_valid).
    //
    //   Broad: any retired store whose EA, phys-stripped to 29 bits
    //   and compared at word granularity (phys[28:2]), hits the word
    //   containing POISON_RAM_ADDR. Captures up to RAMWR_DEPTH events
    //   so an SB×4 byte-by-byte build-up (or an SH-pair) is fully
    //   visible. Also catches stores via kseg1 (0xA0001E28) aliasing
    //   to the same phys word.
    //
    // Retire-event aligned: use core_ev_valid (one edge per retire)
    // and read the u_core.retired_* bundle — authoritative for the
    // instruction that just retired. arg2=EA, arg3=rt_val per
    // ee_core_stub.sv:828-831.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ramwr_captured  <= 1'b0;
            ramwr_any_count <= 0;
        end else if (core_ev_valid && u_core.retired_flag_write &&
                     u_core.retired_arg2[28:2] == POISON_RAM_PHYS_WORD) begin
            // Broad capture — every store whose phys-word matches the
            // poison word. Depth-bounded; later events are counted but
            // not recorded.
            if (ramwr_any_count < RAMWR_DEPTH) begin
                ramwr_any_cycle[ramwr_any_count] <= sim_cycle_ctr;
                ramwr_any_pc[ramwr_any_count]    <= u_core.retired_pc;
                ramwr_any_instr[ramwr_any_count] <= u_core.retired_instr;
                ramwr_any_ea[ramwr_any_count]    <= u_core.retired_arg2;
                ramwr_any_data[ramwr_any_count]  <= u_core.retired_arg3;
                $display("[tb_ee_core_bios_smoke] RAMWR_ANY[%0d] cycle=%0d pc=0x%08h instr=0x%08h EA=0x%08h data=0x%08h",
                         ramwr_any_count, sim_cycle_ctr, u_core.retired_pc,
                         u_core.retired_instr, u_core.retired_arg2,
                         u_core.retired_arg3);
            end
            ramwr_any_count <= ramwr_any_count + 1;
        end
        // Narrow capture (exact virtual addr + exact value) runs
        // alongside the broad one.
        if (rst_n && !ramwr_captured && core_ev_valid &&
            u_core.retired_flag_write &&
            u_core.retired_arg2 == POISON_RAM_ADDR &&
            u_core.retired_arg3 == POISON_VALUE) begin
            ramwr_captured         <= 1'b1;
            ramwr_cycle            <= sim_cycle_ctr;
            ramwr_retired_pc       <= u_core.retired_pc;
            ramwr_retired_instr    <= u_core.retired_instr;
            ramwr_retired_arg2     <= u_core.retired_arg2;
            ramwr_retired_arg3     <= u_core.retired_arg3;
            ramwr_opcode           <= u_core.retired_instr[31:26];
            ramwr_rs_idx           <= u_core.retired_instr[25:21];
            ramwr_rt_idx           <= u_core.retired_instr[20:16];
            ramwr_imm              <= u_core.retired_instr[15:0];
            ramwr_rs_val_at_retire <= u_core.regfile[u_core.retired_instr[25:21]];
            ramwr_rt_val_at_retire <= u_core.regfile[u_core.retired_instr[20:16]];
            $display("[tb_ee_core_bios_smoke] RAMWR cycle=%0d retired_pc=0x%08h instr=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr);
            $display("[tb_ee_core_bios_smoke] RAMWR   decode op=0x%02h rs=$%0d rt=$%0d imm=0x%04h (EA=arg2=0x%08h, data=arg3=0x%08h)",
                     u_core.retired_instr[31:26],
                     u_core.retired_instr[25:21],
                     u_core.retired_instr[20:16],
                     u_core.retired_instr[15:0],
                     u_core.retired_arg2, u_core.retired_arg3);
            $display("[tb_ee_core_bios_smoke] RAMWR   live rs($%0d)=0x%08h rt($%0d)=0x%08h",
                     u_core.retired_instr[25:21],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[20:16]]);
        end
    end

    // Chapter 13-A: first retire with pc in [0x1400, 0x14FF]. Captures
    // the routine entry plus the previous retire (the branch/jump/call
    // site that landed us there) and the live pointer-register state at
    // entry, including $31 (ra) so the calling convention is obvious.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            entry14_captured <= 1'b0;
        end else if (!entry14_captured && core_ev_valid &&
                     u_core.retired_pc >= ENTRY_REGION_LOW &&
                     u_core.retired_pc <= ENTRY_REGION_HIGH) begin
            // Peek the retire ring slot that holds the previous retire.
            // retire_ring_head is the slot where THIS retire will be
            // written, so head-1 (mod DEPTH) has the immediately-prior
            // retire. The ring is updated in a different always_ff on
            // the same edge, but NBA ordering guarantees we see the
            // PRE-update ring values here. ring_prev_idx is a module-
            // scope signal (not automatic — iverilog 12 rejects
            // `automatic` inside always_ff).
            entry14_captured           <= 1'b1;
            entry14_cycle              <= sim_cycle_ctr;
            entry14_retired_pc         <= u_core.retired_pc;
            entry14_retired_instr      <= u_core.retired_instr;
            entry14_prev_retired_pc    <= retired_pc_d1;
            entry14_prev_retired_instr <= retired_instr_d1;
            entry14_ring_prev_pc       <= retire_ring_pc[ring_prev_idx];
            entry14_ring_prev_instr    <= retire_ring_instr[ring_prev_idx];
            entry14_r2                 <= u_core.regfile[2];
            entry14_r4                 <= u_core.regfile[4];
            entry14_r5                 <= u_core.regfile[5];
            entry14_r6                 <= u_core.regfile[6];
            entry14_r7                 <= u_core.regfile[7];
            entry14_r28                <= u_core.regfile[28];
            entry14_r29                <= u_core.regfile[29];
            entry14_r30                <= u_core.regfile[30];
            entry14_r31                <= u_core.regfile[31];
            $display("[tb_ee_core_bios_smoke] ENTRY14 cycle=%0d retired_pc=0x%08h instr=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr);
            $display("[tb_ee_core_bios_smoke] ENTRY14   prev-d1 retired_pc=0x%08h instr=0x%08h",
                     retired_pc_d1, retired_instr_d1);
            $display("[tb_ee_core_bios_smoke] ENTRY14   prev-ring pc=0x%08h instr=0x%08h (call/jump site, authoritative)",
                     retire_ring_pc[ring_prev_idx],
                     retire_ring_instr[ring_prev_idx]);
            $display("[tb_ee_core_bios_smoke] ENTRY14   args    $2=0x%08h $4/a0=0x%08h $5/a1=0x%08h $6/a2=0x%08h $7/a3=0x%08h",
                     u_core.regfile[2], u_core.regfile[4],
                     u_core.regfile[5], u_core.regfile[6],
                     u_core.regfile[7]);
            $display("[tb_ee_core_bios_smoke] ENTRY14   frame   $28/gp=0x%08h $29/sp=0x%08h $30/fp=0x%08h $31/ra=0x%08h",
                     u_core.regfile[28], u_core.regfile[29],
                     u_core.regfile[30], u_core.regfile[31]);
        end
    end

    // Chapter 34(a) — ENTRY200 observer. Fires on the first retire
    // whose retired_pc == 0x0000_0200. Same shape as ENTRY14: capture
    // d1/ring-head-1/ring-head-2 call-site evidence + live regfile
    // snapshot. Head-2 is included because a taken branch/jump
    // sequence retires as [branch, delay, target], so head-2 is the
    // branch/jump instruction itself and head-1 is the delay slot.
    // The instruction-class classification (prev-site opcode/func)
    // is done at print time to keep the always_ff simple.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            entry200_captured <= 1'b0;
        end else if (!entry200_captured && core_ev_valid &&
                     u_core.retired_pc == 32'h0000_0200) begin
            entry200_captured           <= 1'b1;
            entry200_cycle              <= sim_cycle_ctr;
            entry200_retired_pc         <= u_core.retired_pc;
            entry200_retired_instr      <= u_core.retired_instr;
            entry200_prev_retired_pc    <= retired_pc_d1;
            entry200_prev_retired_instr <= retired_instr_d1;
            entry200_ring_prev_pc       <= retire_ring_pc[ring_prev_idx];
            entry200_ring_prev_instr    <= retire_ring_instr[ring_prev_idx];
            entry200_ring_prev_cyc      <= retire_ring_cyc[ring_prev_idx];
            entry200_ring_prev2_pc      <= retire_ring_pc[ring_prev2_idx];
            entry200_ring_prev2_instr   <= retire_ring_instr[ring_prev2_idx];
            entry200_ring_prev2_cyc     <= retire_ring_cyc[ring_prev2_idx];
            entry200_r2                 <= u_core.regfile[2];
            entry200_r4                 <= u_core.regfile[4];
            entry200_r5                 <= u_core.regfile[5];
            entry200_r6                 <= u_core.regfile[6];
            entry200_r7                 <= u_core.regfile[7];
            entry200_r8                 <= u_core.regfile[8];
            entry200_r9                 <= u_core.regfile[9];
            entry200_r26                <= u_core.regfile[26];
            entry200_r27                <= u_core.regfile[27];
            entry200_r28                <= u_core.regfile[28];
            entry200_r29                <= u_core.regfile[29];
            entry200_r30                <= u_core.regfile[30];
            entry200_r31                <= u_core.regfile[31];
            $display("[tb_ee_core_bios_smoke] ENTRY200 cycle=%0d retired_pc=0x%08h instr=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr);
            $display("[tb_ee_core_bios_smoke] ENTRY200   prev-d1    pc=0x%08h instr=0x%08h",
                     retired_pc_d1, retired_instr_d1);
            $display("[tb_ee_core_bios_smoke] ENTRY200   ring-1     pc=0x%08h instr=0x%08h cyc=%0d (delay slot / immediate predecessor)",
                     retire_ring_pc[ring_prev_idx],
                     retire_ring_instr[ring_prev_idx],
                     retire_ring_cyc[ring_prev_idx]);
            $display("[tb_ee_core_bios_smoke] ENTRY200   ring-2     pc=0x%08h instr=0x%08h cyc=%0d (branch/jump site or earlier)",
                     retire_ring_pc[ring_prev2_idx],
                     retire_ring_instr[ring_prev2_idx],
                     retire_ring_cyc[ring_prev2_idx]);
            $display("[tb_ee_core_bios_smoke] ENTRY200   args       $2=0x%08h $4/a0=0x%08h $5/a1=0x%08h $6/a2=0x%08h $7/a3=0x%08h",
                     u_core.regfile[2], u_core.regfile[4],
                     u_core.regfile[5], u_core.regfile[6],
                     u_core.regfile[7]);
            $display("[tb_ee_core_bios_smoke] ENTRY200   trampoline $8=0x%08h $9=0x%08h $26/k0=0x%08h $27/k1=0x%08h",
                     u_core.regfile[8],  u_core.regfile[9],
                     u_core.regfile[26], u_core.regfile[27]);
            $display("[tb_ee_core_bios_smoke] ENTRY200   frame      $28/gp=0x%08h $29/sp=0x%08h $30/fp=0x%08h $31/ra=0x%08h",
                     u_core.regfile[28], u_core.regfile[29],
                     u_core.regfile[30], u_core.regfile[31]);
        end
    end

    // Chapter 35(a) — runway capture. On every retire whose pc is
    // in [RUNWAY_LOW, RUNWAY_HIGH], append to a depth-bounded linear
    // array. The first such retire also snapshots the retire ring's
    // last RUNWAY_PREHIST_DEPTH entries so we see what preceded the
    // fall-through into the runway. ENTRY200's head-1/head-2 only
    // reach 0x1FC/0x1F8 — both already in-runway NOPs — so the
    // originator is further back, in this prehist snapshot.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            runway_count            <= 0;
            runway_prehist_captured <= 1'b0;
        end else if (core_ev_valid &&
                     u_core.retired_pc >= RUNWAY_LOW &&
                     u_core.retired_pc <= RUNWAY_HIGH) begin
            if (!runway_prehist_captured) begin
                // First arrival: snapshot the retire ring so we see
                // the pre-runway call chain. Newest-first: index 0
                // is head-1 (most-recent retire before this one),
                // index 1 is head-2, ... This is the chronologically-
                // reversed last 16 retires preceding the runway.
                for (int j = 0; j < RUNWAY_PREHIST_DEPTH; j++) begin
                    int idx;
                    idx = (retire_ring_head + RETIRE_RING_DEPTH - 1 - j)
                          % RETIRE_RING_DEPTH;
                    runway_prehist_cyc  [j] <= retire_ring_cyc  [idx];
                    runway_prehist_pc   [j] <= retire_ring_pc   [idx];
                    runway_prehist_instr[j] <= retire_ring_instr[idx];
                end
                runway_prehist_captured <= 1'b1;
                $display("[tb_ee_core_bios_smoke] RUNWAY_FIRST cycle=%0d pc=0x%08h instr=0x%08h (first retire into 0x%04h..0x%04h — the originator)",
                         sim_cycle_ctr, u_core.retired_pc,
                         u_core.retired_instr,
                         RUNWAY_LOW[15:0], RUNWAY_HIGH[15:0]);
            end
            if (runway_count < RUNWAY_DEPTH) begin
                runway_cyc  [runway_count] <= sim_cycle_ctr;
                runway_pc   [runway_count] <= u_core.retired_pc;
                runway_instr[runway_count] <= u_core.retired_instr;
                runway_r31  [runway_count] <= u_core.regfile[31];
            end
            runway_count <= runway_count + 1;
        end
    end

    // Chapter 36 — $31 transition-to-runway-$ra observer. Maintain a
    // 1-cycle delayed $31 so we can detect edges. On the first
    // retire where $31 transitions from != RA_TARGET_VALUE to ==
    // RA_TARGET_VALUE, capture everything about the retiring
    // instruction. r31_prev_valid guards against a spurious capture
    // on the reset-deassertion cycle, when r31_prev is stale 0.
    // Since this edge IS the originating link instruction (JAL,
    // JALR, or rare explicit writer), retired_pc is the call site
    // and retired_instr is the exact encoding — classification then
    // happens at print time via opcode/func.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r31_prev_valid         <= 1'b0;
            r31_prev               <= 32'd0;
            r31_to_target_captured <= 1'b0;
        end else begin
            // Advance the delayed view every posedge so r31_prev
            // always holds the value from the previous cycle.
            r31_prev       <= u_core.regfile[31];
            r31_prev_valid <= 1'b1;

            // Every $31 change emits a R31_CHG line — the cumulative
            // call-stack lineage that led to the RA_TARGET_VALUE
            // capture below. Keeps the JAL/JR tree visible across
            // the 84K-retire real-BIOS run (~30 changes, negligible
            // log noise).
            if (r31_prev_valid && u_core.regfile[31] !== r31_prev) begin
                if (ch200_r31_prints < CH200_PRINT_CAP) begin
                    $display("[tb_ee_core_bios_smoke] R31_CHG cyc=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h retired_instr=0x%08h retired_pc_d1=0x%08h retired_instr_d1=0x%08h",
                             sim_cycle_ctr, r31_prev, u_core.regfile[31],
                             u_core.retired_pc, u_core.retired_instr,
                             retired_pc_d1, retired_instr_d1);
                    ch200_r31_prints <= ch200_r31_prints + 1;
                end else begin
                    ch200_suppressed <= ch200_suppressed + 1;
                end
            end

            // Note: intentionally NOT gated on core_ev_valid. Due to
            // NBA read/write ordering across always_ff blocks,
            // u_core.regfile[31] reflects the JAL's post-commit value
            // on the cycle AFTER the JAL retires — so the retire-
            // event flag fires at the JAL cycle while the regfile
            // transition is visible to the TB a cycle later. We
            // capture the transition cycle and sample the d1-delayed
            // retire snapshot, which still points to the JAL.
            if (!r31_to_target_captured && r31_prev_valid &&
                u_core.regfile[31] == RA_TARGET_VALUE &&
                r31_prev           != RA_TARGET_VALUE) begin
                r31_to_target_captured         <= 1'b1;
                r31_to_target_cycle            <= sim_cycle_ctr;
                r31_to_target_retired_pc       <= u_core.retired_pc;
                r31_to_target_retired_instr    <= u_core.retired_instr;
                r31_to_target_retired_arg2     <= u_core.retired_arg2;
                r31_to_target_retired_arg3     <= u_core.retired_arg3;
                r31_to_target_prev_r31         <= r31_prev;
                r31_to_target_r8               <= u_core.regfile[8];
                r31_to_target_r9               <= u_core.regfile[9];
                r31_to_target_rs_idx           <= u_core.retired_instr[25:21];
                r31_to_target_rt_idx           <= u_core.retired_instr[20:16];
                r31_to_target_rd_idx           <= u_core.retired_instr[15:11];
                // For JALR, the target lives in rs_val at retire.
                // Read the current regfile by the rs field index.
                r31_to_target_rs_val           <= u_core.regfile[u_core.retired_instr[25:21]];
                r31_to_target_ring_prev_pc     <= retire_ring_pc[ring_prev_idx];
                r31_to_target_ring_prev_instr  <= retire_ring_instr[ring_prev_idx];
                r31_to_target_ring_prev2_pc    <= retire_ring_pc[ring_prev2_idx];
                r31_to_target_ring_prev2_instr <= retire_ring_instr[ring_prev2_idx];
                $display("[tb_ee_core_bios_smoke] R31_TO_TARGET cycle=%0d retired_pc=0x%08h retired_instr=0x%08h prev_r31=0x%08h new_r31=0x%08h",
                         sim_cycle_ctr,
                         u_core.retired_pc, u_core.retired_instr,
                         r31_prev, u_core.regfile[31]);
                $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   arg2=0x%08h arg3=0x%08h rs=$%0d rt=$%0d rd=$%0d rs_val=0x%08h",
                         u_core.retired_arg2, u_core.retired_arg3,
                         u_core.retired_instr[25:21],
                         u_core.retired_instr[20:16],
                         u_core.retired_instr[15:11],
                         u_core.regfile[u_core.retired_instr[25:21]]);
                $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   $8=0x%08h $9=0x%08h",
                         u_core.regfile[8], u_core.regfile[9]);
                $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   ring-1 pc=0x%08h instr=0x%08h",
                         retire_ring_pc[ring_prev_idx],
                         retire_ring_instr[ring_prev_idx]);
                $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   ring-2 pc=0x%08h instr=0x%08h",
                         retire_ring_pc[ring_prev2_idx],
                         retire_ring_instr[ring_prev2_idx]);
            end
        end
    end

    // Chapter 37 — ROM-to-low-RAM escape observer. Fires on the
    // first retire AFTER r31_to_target_captured where retired_pc has
    // top nibble zero (useg low-RAM). That is the exact retire
    // marking the escape from ROM execution into the zero-filled
    // low-RAM runway. Ring-1 and ring-2 show the delay slot and
    // branch/jump site that caused the transition. The
    // r31_unchanged flag compares $31 at trigger time vs
    // RA_TARGET_VALUE; true means no $31 write occurred between the
    // pinning JAL (ch36) and this escape, which rules in the
    // "non-linking escape" reading (bare J / JR / branch, not
    // JAL/JALR).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            rom_escape_captured <= 1'b0;
        end else if (!rom_escape_captured && r31_to_target_captured &&
                     core_ev_valid &&
                     u_core.retired_pc[31:28] == 4'h0) begin
            rom_escape_captured         <= 1'b1;
            rom_escape_cycle            <= sim_cycle_ctr;
            rom_escape_retired_pc       <= u_core.retired_pc;
            rom_escape_retired_instr    <= u_core.retired_instr;
            rom_escape_ring_prev_pc     <= retire_ring_pc[ring_prev_idx];
            rom_escape_ring_prev_instr  <= retire_ring_instr[ring_prev_idx];
            rom_escape_ring_prev_cyc    <= retire_ring_cyc[ring_prev_idx];
            rom_escape_ring_prev2_pc    <= retire_ring_pc[ring_prev2_idx];
            rom_escape_ring_prev2_instr <= retire_ring_instr[ring_prev2_idx];
            rom_escape_ring_prev2_cyc   <= retire_ring_cyc[ring_prev2_idx];
            rom_escape_r8               <= u_core.regfile[8];
            rom_escape_r9               <= u_core.regfile[9];
            rom_escape_r10              <= u_core.regfile[10];
            rom_escape_r31              <= u_core.regfile[31];
            rom_escape_r31_unchanged    <= (u_core.regfile[31] == RA_TARGET_VALUE);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE cycle=%0d retired_pc=0x%08h instr=0x%08h (first low-RAM retire after R31_TO_TARGET)",
                     sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   ring-1 pc=0x%08h instr=0x%08h cyc=%0d (delay slot)",
                     retire_ring_pc[ring_prev_idx],
                     retire_ring_instr[ring_prev_idx],
                     retire_ring_cyc[ring_prev_idx]);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   ring-2 pc=0x%08h instr=0x%08h cyc=%0d (branch/jump site)",
                     retire_ring_pc[ring_prev2_idx],
                     retire_ring_instr[ring_prev2_idx],
                     retire_ring_cyc[ring_prev2_idx]);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   trampoline $8=0x%08h $9=0x%08h $10=0x%08h $31=0x%08h r31_unchanged=%0d",
                     u_core.regfile[8], u_core.regfile[9],
                     u_core.regfile[10], u_core.regfile[31],
                     (u_core.regfile[31] == RA_TARGET_VALUE));
        end
    end

    // Chapter 45 — generalized ROM→low-RAM escape ring. Fires on
    // every retire after r31_to_target_captured where retired_pc is
    // in low-RAM (top nibble 0), not just the first. Walks the
    // retire_ring backward from head-1 looking for the most recent
    // J/JAL/JR/JALR (the only ops whose target range can cross from
    // ROM to useg low-RAM — sign-extended 16-bit branch offsets
    // can't reach). For JR/JALR, also records the rs_idx so print-
    // time classification can name the driving register directly.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            escape_ring_count <= 0;
        end else if (r31_to_target_captured && core_ev_valid &&
                     u_core.retired_pc[31:28] == 4'h0 &&
                     // True ROM→low-RAM transition only: previous
                     // retire must have been in ROM (top nibble
                     // non-zero). This excludes the internal
                     // low-RAM NOP walk and the JR $31 delay slot
                     // bounces that otherwise flood the ring.
                     retire_ring_pc[ring_prev_idx][31:28] != 4'h0) begin
            // Circular write: always overwrite the slot indexed by
            // (escape_ring_count % DEPTH). After the ring has filled
            // once we keep the most-recent DEPTH entries so late-
            // run escapes (like the one that leads to the new
            // ENTRY200) stay visible even when early stub bounces
            // would otherwise have filled it.
            escape_ring_write_idx = escape_ring_count % ESCAPE_RING_DEPTH;
            // Walk-back scratch vars. iverilog-12 allows int locals
            // inside always_ff via begin/end, but we keep them
            // module-scope below for simplicity.
            walkback_search(retire_ring_head,
                            walk_cf_found, walk_cf_hops, walk_cf_idx);
            if (walk_cf_found) begin
                escape_ring_cf_pc   [escape_ring_write_idx] <= retire_ring_pc   [walk_cf_idx];
                escape_ring_cf_instr[escape_ring_write_idx] <= retire_ring_instr[walk_cf_idx];
                escape_ring_cf_cyc  [escape_ring_write_idx] <= retire_ring_cyc  [walk_cf_idx];
                escape_ring_cf_hops [escape_ring_write_idx] <= walk_cf_hops;
                escape_ring_cf_found[escape_ring_write_idx] <= 1'b1;
                escape_ring_cf_rs   [escape_ring_write_idx] <= retire_ring_instr[walk_cf_idx][25:21];
            end else begin
                escape_ring_cf_pc   [escape_ring_write_idx] <= 32'hDEAD_BEEF;
                escape_ring_cf_instr[escape_ring_write_idx] <= 32'hDEAD_BEEF;
                escape_ring_cf_cyc  [escape_ring_write_idx] <= 0;
                escape_ring_cf_hops [escape_ring_write_idx] <= 0;
                escape_ring_cf_found[escape_ring_write_idx] <= 1'b0;
                escape_ring_cf_rs   [escape_ring_write_idx] <= 5'd0;
            end

            escape_ring_cyc     [escape_ring_write_idx] <= sim_cycle_ctr;
            escape_ring_pc      [escape_ring_write_idx] <= u_core.retired_pc;
            escape_ring_instr   [escape_ring_write_idx] <= u_core.retired_instr;
            escape_ring_r1_pc   [escape_ring_write_idx] <= retire_ring_pc   [ring_prev_idx];
            escape_ring_r1_instr[escape_ring_write_idx] <= retire_ring_instr[ring_prev_idx];
            escape_ring_r1_cyc  [escape_ring_write_idx] <= retire_ring_cyc  [ring_prev_idx];
            escape_ring_r2_pc   [escape_ring_write_idx] <= retire_ring_pc   [ring_prev2_idx];
            escape_ring_r2_instr[escape_ring_write_idx] <= retire_ring_instr[ring_prev2_idx];
            escape_ring_r2_cyc  [escape_ring_write_idx] <= retire_ring_cyc  [ring_prev2_idx];
            escape_ring_r8      [escape_ring_write_idx] <= u_core.regfile[8];
            escape_ring_r9      [escape_ring_write_idx] <= u_core.regfile[9];
            escape_ring_r10     [escape_ring_write_idx] <= u_core.regfile[10];
            escape_ring_r31     [escape_ring_write_idx] <= u_core.regfile[31];

            escape_ring_count <= escape_ring_count + 1;

            $display("[tb_ee_core_bios_smoke] ESCAPE_RING[#%0d slot=%0d] cyc=%0d landing_pc=0x%08h instr=0x%08h $8=0x%08h $9=0x%08h $10=0x%08h $31=0x%08h",
                     escape_ring_count, escape_ring_write_idx,
                     sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.regfile[8], u_core.regfile[9],
                     u_core.regfile[10], u_core.regfile[31]);
        end
    end

    // Wrap-index scratch for the circular escape ring.
    int escape_ring_write_idx;

    // Ch45 walk-back scratch registers (module scope to avoid
    // iverilog-12 block-local limitations when passed to a task).
    logic        walk_cf_found;
    int          walk_cf_hops;
    int          walk_cf_idx;

    // Walk the retire_ring backward from (head-1) up to
    // RETIRE_RING_DEPTH-1 hops looking for the most recent
    // J/JAL/JR/JALR. Outputs via module-scope regs (iverilog-12
    // procedural "output" args on task are finicky on unpacked
    // arrays — module scope avoids that).
    task automatic walkback_search(input int head,
                                    output logic found,
                                    output int   hops,
                                    output int   slot);
        int  h;
        int  idx;
        logic [31:0] cand;
        logic [5:0]  cop, cfn;
        logic        is_cf;
        found = 1'b0;
        hops  = 0;
        slot  = 0;
        for (h = 1; h < RETIRE_RING_DEPTH; h++) begin
            if (!found) begin
                idx = (head + RETIRE_RING_DEPTH - h) % RETIRE_RING_DEPTH;
                cand = retire_ring_instr[idx];
                cop  = cand[31:26];
                cfn  = cand[5:0];
                is_cf = (cop == 6'h02) || (cop == 6'h03) ||
                        (cop == 6'h00 && (cfn == 6'h08 || cfn == 6'h09));
                if (is_cf) begin
                    found = 1'b1;
                    hops  = h;
                    slot  = idx;
                end
            end
        end
    endtask

    // Chapter 45 — $8 / $9 change-lineage logs. Same idea as ch36
    // R10_CHG: one $display per change across the whole run, used
    // as supporting evidence for ESCAPE_RING findings.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r8_prev       <= 32'd0;
            r8_prev_valid <= 1'b0;
        end else begin
            r8_prev       <= u_core.regfile[8];
            r8_prev_valid <= 1'b1;
            if (r8_prev_valid && u_core.regfile[8] !== r8_prev) begin
                $display("[tb_ee_core_bios_smoke] R8_CHG cyc=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h retired_instr=0x%08h retired_pc_d1=0x%08h retired_instr_d1=0x%08h",
                         sim_cycle_ctr, r8_prev, u_core.regfile[8],
                         u_core.retired_pc, u_core.retired_instr,
                         retired_pc_d1, retired_instr_d1);
            end
        end
    end

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r9_prev       <= 32'd0;
            r9_prev_valid <= 1'b0;
        end else begin
            r9_prev       <= u_core.regfile[9];
            r9_prev_valid <= 1'b1;
            if (r9_prev_valid && u_core.regfile[9] !== r9_prev) begin
                $display("[tb_ee_core_bios_smoke] R9_CHG cyc=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h retired_instr=0x%08h retired_pc_d1=0x%08h retired_instr_d1=0x%08h",
                         sim_cycle_ctr, r9_prev, u_core.regfile[9],
                         u_core.retired_pc, u_core.retired_instr,
                         retired_pc_d1, retired_instr_d1);
            end
        end
    end

    // Ch180 — $1 ($at) provenance ring. Captures every write to $1
    // (depth R1_RING_DEPTH) so the timeout dump can show the chain
    // that led to the 0xBF800000 value. No per-change $display
    // (would be noisy; $1 is a hot assembler-temp).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r1_prev       <= 32'd0;
            r1_prev_valid <= 1'b0;
            r1_ring_count <= 0;
        end else begin
            r1_prev       <= u_core.regfile[1];
            r1_prev_valid <= 1'b1;
            if (r1_prev_valid && u_core.regfile[1] !== r1_prev) begin
                r1_ring_cyc     [r1_ring_count % R1_RING_DEPTH] <= sim_cycle_ctr;
                r1_ring_prev    [r1_ring_count % R1_RING_DEPTH] <= r1_prev;
                r1_ring_new     [r1_ring_count % R1_RING_DEPTH] <= u_core.regfile[1];
                r1_ring_pc      [r1_ring_count % R1_RING_DEPTH] <= u_core.retired_pc;
                r1_ring_instr   [r1_ring_count % R1_RING_DEPTH] <= u_core.retired_instr;
                r1_ring_pc_d1   [r1_ring_count % R1_RING_DEPTH] <= retired_pc_d1;
                r1_ring_instr_d1[r1_ring_count % R1_RING_DEPTH] <= retired_instr_d1;
                r1_ring_count   <= r1_ring_count + 1;
            end
        end
    end

    // Ch183 — $10 provenance ring. Mirror of Ch180 R1 ring. The
    // bad jalr at PC=0x0003003C used `$10` as its target register;
    // we need to know who wrote $10 last (and whether the same
    // setup intended to touch $1).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r10_chg_prev       <= 32'd0;
            r10_chg_prev_valid <= 1'b0;
            r10_ring_count     <= 0;
        end else begin
            r10_chg_prev       <= u_core.regfile[10];
            r10_chg_prev_valid <= 1'b1;
            if (r10_chg_prev_valid && u_core.regfile[10] !== r10_chg_prev) begin
                r10_ring_cyc  [r10_ring_count % R10_RING_DEPTH] <= sim_cycle_ctr;
                r10_ring_prev [r10_ring_count % R10_RING_DEPTH] <= r10_chg_prev;
                r10_ring_new  [r10_ring_count % R10_RING_DEPTH] <= u_core.regfile[10];
                r10_ring_pc   [r10_ring_count % R10_RING_DEPTH] <= u_core.retired_pc;
                r10_ring_instr[r10_ring_count % R10_RING_DEPTH] <= u_core.retired_instr;
                r10_ring_count <= r10_ring_count + 1;
            end
        end
    end

    // Ch183 — bad-jalr one-shot capture. Fires on the cycle the
    // RAM-resident `jalr $10` at PC=0x0003003C retires. Captures
    // the regfile + current ring counts so the timeout dump can
    // present a "this is the state at the moment of the bad call"
    // view that doesn't depend on later peek_instr (RAM at 0x3003C
    // gets zeroed before the watchdog fires).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            bad_jalr_captured <= 1'b0;
        end else if (!bad_jalr_captured && core_ev_valid &&
                     u_core.retired_pc == 32'h0003_003C) begin
            bad_jalr_captured                  <= 1'b1;
            bad_jalr_cycle                     <= sim_cycle_ctr;
            bad_jalr_r10                       <= u_core.regfile[10];
            bad_jalr_r1                        <= u_core.regfile[1];
            bad_jalr_r8                        <= u_core.regfile[8];
            bad_jalr_r2                        <= u_core.regfile[2];
            bad_jalr_r3                        <= u_core.regfile[3];
            bad_jalr_r4                        <= u_core.regfile[4];
            bad_jalr_r5                        <= u_core.regfile[5];
            bad_jalr_r29                       <= u_core.regfile[29];
            bad_jalr_r31                       <= u_core.regfile[31];
            bad_jalr_r10_ring_count_at_trigger <= r10_ring_count;
            bad_jalr_r1_ring_count_at_trigger  <= r1_ring_count;
            bad_jalr_r31_ring_count_at_trigger <= r31_ring_count;
            // Ch187 — snapshot the instr words at the addresses Codex
            // asked about, AT THE MOMENT of the jalr (not at timeout).
            badjalr_peek_080 <= peek_instr(32'h0000_0080);
            badjalr_peek_0a0 <= peek_instr(32'h0000_00A0);
            badjalr_peek_0c0 <= peek_instr(32'h0000_00C0);
            badjalr_peek_500 <= peek_instr(32'h0000_0500);
            badjalr_peek_598 <= peek_instr(32'h0000_0598);
            $display("[tb_ee_core_bios_smoke] BAD_JALR captured cyc=%0d retired_pc=0x%08h retired_instr=0x%08h $10=0x%08h $1=0x%08h $8=0x%08h $31=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr,
                     u_core.regfile[10], u_core.regfile[1],
                     u_core.regfile[8], u_core.regfile[31]);
            $display("[tb_ee_core_bios_smoke] BAD_JALR peek-at-jalr-time: [0x80]=0x%08h [0xA0]=0x%08h [0xC0]=0x%08h [0x500]=0x%08h [0x598]=0x%08h",
                     peek_instr(32'h0000_0080),
                     peek_instr(32'h0000_00A0),
                     peek_instr(32'h0000_00C0),
                     peek_instr(32'h0000_0500),
                     peek_instr(32'h0000_0598));
        end
    end

    // Ch182 — $31 ($ra) provenance ring. Same shape as Ch180 R1
    // ring. ENTRY598#23 came in with $31=0x00030044 and the BIOS
    // has no `jal` at PC=0x3003C (the natural call site for that
    // return address) — both useg_shadow and ee_ram are zero at
    // 0x30040. So $31 must have been set via a non-JAL path. This
    // ring tells us which path.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r31_chg_prev       <= 32'd0;
            r31_chg_prev_valid <= 1'b0;
            r31_ring_count     <= 0;
        end else begin
            r31_chg_prev       <= u_core.regfile[31];
            r31_chg_prev_valid <= 1'b1;
            if (r31_chg_prev_valid && u_core.regfile[31] !== r31_chg_prev) begin
                r31_ring_cyc  [r31_ring_count % R31_RING_DEPTH] <= sim_cycle_ctr;
                r31_ring_prev [r31_ring_count % R31_RING_DEPTH] <= r31_chg_prev;
                r31_ring_new  [r31_ring_count % R31_RING_DEPTH] <= u_core.regfile[31];
                r31_ring_pc   [r31_ring_count % R31_RING_DEPTH] <= u_core.retired_pc;
                r31_ring_instr[r31_ring_count % R31_RING_DEPTH] <= u_core.retired_instr;
                r31_ring_count <= r31_ring_count + 1;
            end
        end
    end

    // Ch195 — $v0 ($2), $v1 ($3), $a0 ($4) provenance rings, same
    // pattern as R10/R31. We need these to trace how the SYSCALL
    // contract was assembled — particularly $v1=0x08 (the syscall
    // number) and $a0=0x02 (its first argument). Each ring records
    // the last CH195_RING_DEPTH writes; at halt time the print block
    // walks them newest-first.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch195_r2_prev <= 32'd0; ch195_r2_prev_valid <= 1'b0; ch195_r2_count <= 0;
            ch195_r3_prev <= 32'd0; ch195_r3_prev_valid <= 1'b0; ch195_r3_count <= 0;
            ch195_r4_prev <= 32'd0; ch195_r4_prev_valid <= 1'b0; ch195_r4_count <= 0;
        end else begin
            ch195_r2_prev <= u_core.regfile[2]; ch195_r2_prev_valid <= 1'b1;
            ch195_r3_prev <= u_core.regfile[3]; ch195_r3_prev_valid <= 1'b1;
            ch195_r4_prev <= u_core.regfile[4]; ch195_r4_prev_valid <= 1'b1;
            if (ch195_r2_prev_valid && u_core.regfile[2] !== ch195_r2_prev) begin
                ch195_r2_cyc  [ch195_r2_count % CH195_RING_DEPTH] <= sim_cycle_ctr;
                ch195_r2_pv   [ch195_r2_count % CH195_RING_DEPTH] <= ch195_r2_prev;
                ch195_r2_nv   [ch195_r2_count % CH195_RING_DEPTH] <= u_core.regfile[2];
                ch195_r2_pc   [ch195_r2_count % CH195_RING_DEPTH] <= u_core.retired_pc;
                ch195_r2_instr[ch195_r2_count % CH195_RING_DEPTH] <= u_core.retired_instr;
                ch195_r2_count <= ch195_r2_count + 1;
            end
            if (ch195_r3_prev_valid && u_core.regfile[3] !== ch195_r3_prev) begin
                ch195_r3_cyc  [ch195_r3_count % CH195_RING_DEPTH] <= sim_cycle_ctr;
                ch195_r3_pv   [ch195_r3_count % CH195_RING_DEPTH] <= ch195_r3_prev;
                ch195_r3_nv   [ch195_r3_count % CH195_RING_DEPTH] <= u_core.regfile[3];
                ch195_r3_pc   [ch195_r3_count % CH195_RING_DEPTH] <= u_core.retired_pc;
                ch195_r3_instr[ch195_r3_count % CH195_RING_DEPTH] <= u_core.retired_instr;
                ch195_r3_count <= ch195_r3_count + 1;
            end
            if (ch195_r4_prev_valid && u_core.regfile[4] !== ch195_r4_prev) begin
                ch195_r4_cyc  [ch195_r4_count % CH195_RING_DEPTH] <= sim_cycle_ctr;
                ch195_r4_pv   [ch195_r4_count % CH195_RING_DEPTH] <= ch195_r4_prev;
                ch195_r4_nv   [ch195_r4_count % CH195_RING_DEPTH] <= u_core.regfile[4];
                ch195_r4_pc   [ch195_r4_count % CH195_RING_DEPTH] <= u_core.retired_pc;
                ch195_r4_instr[ch195_r4_count % CH195_RING_DEPTH] <= u_core.retired_instr;
                ch195_r4_count <= ch195_r4_count + 1;
            end
        end
    end

    // Ch180/181 — ENTRY598 + ENTRY5A8 observers. Ch180 originally
    // captured ENTRY598 ONCE (first entry). Ch181 lifts the
    // first-only guard: every retire at PC=0x598 is logged and
    // the latest snapshot kept. Adds ENTRY5A8 observer with prev-PC
    // filter to catch "unexpected" entries to the loop body — i.e.
    // jumps into 0x5A8 that don't come from the function head's
    // fall-through (0x5A4) or the BNE back-edge (0x5B8).
    int          ring_prev3_idx;
    int          ring_prev4_idx;
    always_comb begin
        ring_prev3_idx = (retire_ring_head + RETIRE_RING_DEPTH - 3) % RETIRE_RING_DEPTH;
        ring_prev4_idx = (retire_ring_head + RETIRE_RING_DEPTH - 4) % RETIRE_RING_DEPTH;
    end

    // Ch186 — first-retire-in-RAM-dispatcher observer. Fires once,
    // on the first retire whose PC lies in [0x30000, 0x30040).
    // Captures the regfile + a frozen shadow of the retire_ring at
    // that exact cycle (since the live ring keeps rotating).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            first_ram_disp_captured <= 1'b0;
        end else if (!first_ram_disp_captured && core_ev_valid &&
                     u_core.retired_pc >= RAM_DISP_LO &&
                     u_core.retired_pc <  RAM_DISP_HI) begin
            first_ram_disp_captured              <= 1'b1;
            first_ram_disp_cycle                 <= sim_cycle_ctr;
            first_ram_disp_pc                    <= u_core.retired_pc;
            first_ram_disp_instr                 <= u_core.retired_instr;
            first_ram_disp_r1                    <= u_core.regfile[1];
            first_ram_disp_r2                    <= u_core.regfile[2];
            first_ram_disp_r3                    <= u_core.regfile[3];
            first_ram_disp_r4                    <= u_core.regfile[4];
            first_ram_disp_r8                    <= u_core.regfile[8];
            first_ram_disp_r10                   <= u_core.regfile[10];
            first_ram_disp_r29                   <= u_core.regfile[29];
            first_ram_disp_r31                   <= u_core.regfile[31];
            first_ram_disp_ring_head_at_trigger  <= retire_ring_head;
            // Freeze the entire retire ring contents at this cycle.
            for (int i = 0; i < 32; i++) begin
                first_ram_disp_ring_pc   [i] <= retire_ring_pc   [i];
                first_ram_disp_ring_instr[i] <= retire_ring_instr[i];
                first_ram_disp_ring_cyc  [i] <= retire_ring_cyc  [i];
            end
            $display("[tb_ee_core_bios_smoke] FIRST_RAM_DISP cyc=%0d pc=0x%08h instr=0x%08h $1=0x%08h $8=0x%08h $10=0x%08h $31=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr,
                     u_core.regfile[1], u_core.regfile[8],
                     u_core.regfile[10], u_core.regfile[31]);
        end
    end

    // Ch188/Ch190 — install A0 syscall shim with polling-restore.
    // The Ch188 version installed only once on bad_jalr_captured, but
    // Ch190 caught a 1-cycle timing race: the EE's fetch for PC=0xA0
    // ran in the SAME cycle as the NBA install, so the fetch read
    // the pre-shim NOP. Fixed: install AT RESET and re-install every
    // cycle the shim is missing (defensive against BIOS clear passes
    // that walk over [0x80, 0xC0) — observed in Ch187). Cost is one
    // NBA write per cycle when the shim is alive — but the value
    // doesn't change so iverilog/SV semantics make this a no-op
    // commit. Functionally: the shim is always present once rst_n
    // releases. ch188_shim_installed becomes "we ever saw the
    // shim alive at least once after rst_n" — for logging.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch188_shim_installed <= 1'b0;
        end else begin
            if (u_ee_map.g_useg_shadow.useg_shadow_mem[8'h28] != 32'h03E00008)
                u_ee_map.g_useg_shadow.useg_shadow_mem[8'h28] <= 32'h03E00008;  // jr $31
            if (u_ee_map.g_useg_shadow.useg_shadow_mem[8'h29] != 32'h00000000)
                u_ee_map.g_useg_shadow.useg_shadow_mem[8'h29] <= 32'h00000000;  // nop
            if (!ch188_shim_installed) begin
                ch188_shim_installed <= 1'b1;
                $display("[tb_ee_core_bios_smoke] Ch188/Ch190 A0_SHIM installed-and-pinned (sim-only) at cyc=%0d: useg_shadow_mem[0xA0]=jr $31, [0xA4]=nop. Polling-restore will defend against BIOS clear passes.",
                         sim_cycle_ctr);
            end
        end
    end

    // Ch190 — one-shot observer for first retire at PC=0x30044 (the
    // post-A0-shim return target). Captures the instruction the EE
    // actually retired there, and at the cycle. If shim worked AND
    // useg_shadow_mem[0xC011] was 0x0806914c (`j 0x001A4530`) at
    // retire-time, this should fire and report exactly that.
    logic        first_pc_30044_captured;
    int          first_pc_30044_cycle;
    logic [31:0] first_pc_30044_instr;
    initial begin
        first_pc_30044_captured = 1'b0;
        first_pc_30044_cycle    = 0;
        first_pc_30044_instr    = 32'd0;
    end
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            first_pc_30044_captured <= 1'b0;
        end else if (!first_pc_30044_captured && core_ev_valid &&
                     u_core.retired_pc == 32'h0003_0044) begin
            first_pc_30044_captured <= 1'b1;
            first_pc_30044_cycle    <= sim_cycle_ctr;
            first_pc_30044_instr    <= u_core.retired_instr;
            $display("[tb_ee_core_bios_smoke] Ch190 FIRST_PC_30044 cyc=%0d instr=0x%08h  %s",
                     sim_cycle_ctr, u_core.retired_instr,
                     mips_mnemonic(u_core.retired_instr));
        end
    end

    // Ch190 — A0(0x44) dataflow autopsy. Fires once on the same cycle
    // bad_jalr_captured rises (cyc 1,732,958 in our run). Dumps memory
    // around each register argument so we can identify A0(0x44) by
    // INSPECTING the data, not by guessing from a syscall table.
    //
    // Per Codex Ch190 caution: no semantic guess yet. Just observe.
    // The args at the call were:
    //   $4 = 0x8004468C   $5 = 0x801A4640   $6 = 0x0001468C   $7 = 0x801FFDA8
    // The hypothesis (β) framing says these look like copy/relocation
    // bookkeeping. $6 = 0x14688 is suspiciously close to the RAM
    // dispatcher's memcpy size (0x14640) and to the offset between
    // the two ptrs ($5 - 0x80190000 = 0x14640).
    logic ch190_dump_done;
    initial ch190_dump_done = 1'b0;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch190_dump_done <= 1'b0;
        end else if (bad_jalr_captured && !ch190_dump_done) begin
            ch190_dump_done <= 1'b1;
            $display("[tb_ee_core_bios_smoke] Ch190 A0(0x44) dataflow autopsy at cyc=%0d:",
                     sim_cycle_ctr);
            // Relation arithmetic.
            $display("[tb_ee_core_bios_smoke] Ch190   relations:");
            $display("[tb_ee_core_bios_smoke] Ch190     $5 - $4              = 0x%08h",
                     u_core.regfile[5] - u_core.regfile[4]);
            $display("[tb_ee_core_bios_smoke] Ch190     $5 - 0x80190000      = 0x%08h  (memcpy DEST range size?)",
                     u_core.regfile[5] - 32'h8019_0000);
            $display("[tb_ee_core_bios_smoke] Ch190     $6 (size?)           = 0x%08h",
                     u_core.regfile[6]);
            $display("[tb_ee_core_bios_smoke] Ch190     $6 - 0x14640         = 0x%08h  (delta from RAM-disp memcpy size)",
                     u_core.regfile[6] - 32'h0001_4640);
            $display("[tb_ee_core_bios_smoke] Ch190     $7 - $5              = 0x%08h",
                     u_core.regfile[7] - u_core.regfile[5]);

            // Memory window around $4 = 0x8004468C (KSEG0 RAM).
            $display("[tb_ee_core_bios_smoke] Ch190   mem around $4=0x%08h (-16..+16 words):",
                     u_core.regfile[4]);
            for (int i = -4; i <= 4; i++) begin
                logic [31:0] a, w;
                a = u_core.regfile[4] + (i*4);
                w = peek_instr(a);
                $display("[tb_ee_core_bios_smoke] Ch190     %s 0x%08h: 0x%08h",
                         (i == 0) ? ">>" : "  ", a, w);
            end

            // Memory window around $5 = 0x801A4640 (KSEG0 RAM).
            $display("[tb_ee_core_bios_smoke] Ch190   mem around $5=0x%08h (-16..+16 words):",
                     u_core.regfile[5]);
            for (int i = -4; i <= 4; i++) begin
                logic [31:0] a, w;
                a = u_core.regfile[5] + (i*4);
                w = peek_instr(a);
                $display("[tb_ee_core_bios_smoke] Ch190     %s 0x%08h: 0x%08h",
                         (i == 0) ? ">>" : "  ", a, w);
            end

            // $6 is probably a size, but also dump it as a potential
            // useg-space pointer in case it's actually a 3rd ptr.
            $display("[tb_ee_core_bios_smoke] Ch190   mem at $6=0x%08h as if useg ptr (-8..+8 words):",
                     u_core.regfile[6]);
            for (int i = -2; i <= 2; i++) begin
                logic [31:0] a, w;
                a = u_core.regfile[6] + (i*4);
                w = peek_instr(a);
                $display("[tb_ee_core_bios_smoke] Ch190     %s 0x%08h: 0x%08h",
                         (i == 0) ? ">>" : "  ", a, w);
            end

            // Memory window around $7 = 0x801FFDA8 (stack ptr / return-slot?).
            $display("[tb_ee_core_bios_smoke] Ch190   mem around $7=0x%08h (-16..+16 words):",
                     u_core.regfile[7]);
            for (int i = -4; i <= 4; i++) begin
                logic [31:0] a, w;
                a = u_core.regfile[7] + (i*4);
                w = peek_instr(a);
                $display("[tb_ee_core_bios_smoke] Ch190     %s 0x%08h: 0x%08h",
                         (i == 0) ? ">>" : "  ", a, w);
            end

            // Sample the RAM-dispatcher's memcpy DEST range
            // [0x80190000, 0x801A4640) — start, middle, end.
            // Ch191: also dump DIRECT u_ee_ram contents at the
            // matching phys offsets so we can tell if the data
            // is in ee_ram (KSEG0 backing) but missing from
            // useg_shadow_mem (the useg-fetch backing).
            $display("[tb_ee_core_bios_smoke] Ch190   sample memcpy DEST range 0x80190000..0x801A4640:");
            $display("[tb_ee_core_bios_smoke] Ch190     (key: peek_instr() routes useg→useg_shadow_mem, kseg0→ee_ram)");
            for (int i = 0; i < 4; i++) begin
                logic [31:0] a, w_peek;
                logic [31:0] w_ee_ram_direct, w_useg_direct;
                int unsigned ram_qw_idx;
                logic [1:0]  ram_lane;
                logic [19:0] useg_idx;
                a = 32'h8019_0000 + (i*4);
                w_peek = peek_instr(a);
                ram_qw_idx = a[24:4];
                ram_lane   = a[3:2];
                case (ram_lane)
                    2'd0: w_ee_ram_direct = u_ee_ram.mem[ram_qw_idx][31:0];
                    2'd1: w_ee_ram_direct = u_ee_ram.mem[ram_qw_idx][63:32];
                    2'd2: w_ee_ram_direct = u_ee_ram.mem[ram_qw_idx][95:64];
                    default: w_ee_ram_direct = u_ee_ram.mem[ram_qw_idx][127:96];
                endcase
                useg_idx = a[21:2];
                w_useg_direct = u_ee_map.g_useg_shadow.useg_shadow_mem[useg_idx];
                $display("[tb_ee_core_bios_smoke] Ch190     [start+%0d]  0x%08h: peek=0x%08h  ee_ram[%0h]=0x%08h  useg_shadow[%0h]=0x%08h",
                         i, a, w_peek, ram_qw_idx, w_ee_ram_direct,
                         useg_idx, w_useg_direct);
            end
            for (int i = 0; i < 4; i++) begin
                logic [31:0] a, w;
                a = 32'h8019_A000 + (i*4);
                w = peek_instr(a);
                $display("[tb_ee_core_bios_smoke] Ch190     [mid+%0d]    0x%08h: 0x%08h",
                         i, a, w);
            end
            for (int i = -4; i < 0; i++) begin
                logic [31:0] a, w;
                a = 32'h801A_4640 + (i*4);
                w = peek_instr(a);
                $display("[tb_ee_core_bios_smoke] Ch190     [end%0d]   0x%08h: 0x%08h",
                         i, a, w);
            end

            // Ch191 — direct ee_ram peek at the post-jump-target PC
            // 0x001A4530 (= 0x80190000 + 0x14530, middle of the
            // memcpy DEST range). The post-A0 `j 0x001A4530` lands
            // here. If u_ee_ram has the data but useg_shadow_mem
            // doesn't, the KSEG0↔useg mirror is broken (the Ch70
            // CPU_RAM_ALIAS_EN parameter is the existing knob).
            $display("[tb_ee_core_bios_smoke] Ch191   POST-JUMP TARGET 0x001A4530 (both backings):");
            for (int i = -2; i <= 4; i++) begin
                logic [31:0] a, w_useg, w_ram;
                int unsigned ram_qw_idx;
                logic [1:0]  ram_lane;
                logic [19:0] useg_idx;
                a = 32'h001A_4530 + (i*4);
                ram_qw_idx = a[24:4];
                ram_lane   = a[3:2];
                case (ram_lane)
                    2'd0: w_ram = u_ee_ram.mem[ram_qw_idx][31:0];
                    2'd1: w_ram = u_ee_ram.mem[ram_qw_idx][63:32];
                    2'd2: w_ram = u_ee_ram.mem[ram_qw_idx][95:64];
                    default: w_ram = u_ee_ram.mem[ram_qw_idx][127:96];
                endcase
                useg_idx = a[21:2];
                w_useg = u_ee_map.g_useg_shadow.useg_shadow_mem[useg_idx];
                $display("[tb_ee_core_bios_smoke] Ch191     %s 0x%08h: ee_ram=0x%08h  useg_shadow=0x%08h",
                         (i == 0) ? ">>" : "  ", a, w_ram, w_useg);
            end

            // Post-return PC region 0x00030044..0x00030080.
            // Ch191: dump BOTH backings here too — confirm whether
            // 0x0806914c lives only in useg_shadow_mem (a BIOS write
            // path that *didn't* mirror to ee_ram) or in both.
            $display("[tb_ee_core_bios_smoke] Ch190/191 post-return PC region 0x30044..0x30080 (both backings):");
            for (int i = 0; i < 16; i++) begin
                logic [31:0] a, w_ram, w_useg;
                int unsigned ram_qw_idx;
                logic [1:0]  ram_lane;
                logic [19:0] useg_idx;
                a = 32'h0003_0044 + (i*4);
                ram_qw_idx = a[24:4];
                ram_lane   = a[3:2];
                case (ram_lane)
                    2'd0: w_ram = u_ee_ram.mem[ram_qw_idx][31:0];
                    2'd1: w_ram = u_ee_ram.mem[ram_qw_idx][63:32];
                    2'd2: w_ram = u_ee_ram.mem[ram_qw_idx][95:64];
                    default: w_ram = u_ee_ram.mem[ram_qw_idx][127:96];
                endcase
                useg_idx = a[21:2];
                w_useg = u_ee_map.g_useg_shadow.useg_shadow_mem[useg_idx];
                $display("[tb_ee_core_bios_smoke] Ch190/191   0x%08h: ee_ram=0x%08h useg_shadow=0x%08h",
                         a, w_ram, w_useg);
            end

            // Ch191 — sample the memcpy SOURCE area 0x80030054 + 16 words.
            // If src is all zero too, the memcpy "successfully" copied
            // zeros and nothing useful was relocated.
            $display("[tb_ee_core_bios_smoke] Ch191 memcpy SOURCE start 0x80030054 (KSEG0):");
            for (int i = 0; i < 8; i++) begin
                logic [31:0] a, w_ram, w_useg;
                int unsigned ram_qw_idx;
                logic [1:0]  ram_lane;
                logic [19:0] useg_idx;
                a = 32'h8003_0054 + (i*4);
                ram_qw_idx = a[24:4];
                ram_lane   = a[3:2];
                case (ram_lane)
                    2'd0: w_ram = u_ee_ram.mem[ram_qw_idx][31:0];
                    2'd1: w_ram = u_ee_ram.mem[ram_qw_idx][63:32];
                    2'd2: w_ram = u_ee_ram.mem[ram_qw_idx][95:64];
                    default: w_ram = u_ee_ram.mem[ram_qw_idx][127:96];
                endcase
                useg_idx = a[21:2];
                w_useg = u_ee_map.g_useg_shadow.useg_shadow_mem[useg_idx];
                $display("[tb_ee_core_bios_smoke] Ch191   0x%08h: ee_ram=0x%08h useg_shadow=0x%08h",
                         a, w_ram, w_useg);
            end
        end
    end

    // Ch188 — A0 syscall observer. Logs every retire at PC=0xA0 with
    // the function number ($9 by BIOS convention), $31 (caller's
    // return address), and the four standard MIPS arg regs $4-$7.
    // Flags $9 != 0x44 (the only function number observed in our
    // current run) for visibility — does NOT trap.
    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid && u_core.retired_pc == 32'h0000_00A0) begin
            ch188_a0_call_count <= ch188_a0_call_count + 1;
            $display("[tb_ee_core_bios_smoke] Ch188 A0_CALL#%0d cyc=%0d $9=0x%08h $4=0x%08h $5=0x%08h $6=0x%08h $7=0x%08h $31=0x%08h %s",
                     ch188_a0_call_count + 1, sim_cycle_ctr,
                     u_core.regfile[9],
                     u_core.regfile[4], u_core.regfile[5],
                     u_core.regfile[6], u_core.regfile[7],
                     u_core.regfile[31],
                     (u_core.regfile[9] == 32'h44) ? "(expected fn 0x44)"
                                                  : "(*** UNEXPECTED FN — would need real handler ***)");
        end
    end

    // Ch187 — writes to [0x80, 0xC0). Catches any install at 0xA0
    // (or surrounding addresses we care about).
    always_ff @(posedge clk) begin
        logic [28:0] phys_lo;
        if (!rst_n) begin
            low_ram_wr_count <= 0;
        end else if (ee_wr_en) begin
            phys_lo = ee_wr_addr[28:0];
            if (phys_lo >= 29'h000_0080 && phys_lo < 29'h000_00C0) begin
                low_ram_wr_cyc [low_ram_wr_count % LOW_RAM_WR_DEPTH] <= sim_cycle_ctr;
                low_ram_wr_addr[low_ram_wr_count % LOW_RAM_WR_DEPTH] <= ee_wr_addr;
                low_ram_wr_data[low_ram_wr_count % LOW_RAM_WR_DEPTH] <= ee_wr_data;
                low_ram_wr_pc  [low_ram_wr_count % LOW_RAM_WR_DEPTH] <= u_core.retired_pc;
                low_ram_wr_count <= low_ram_wr_count + 1;
                $display("[tb_ee_core_bios_smoke] LOW_RAM_WRITE cyc=%0d addr=0x%08h data=0x%08h pc=0x%08h",
                         sim_cycle_ctr, ee_wr_addr, ee_wr_data, u_core.retired_pc);
            end
        end
    end

    // Ch187 — one-shot capture of first retire at PC=0xA0.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            first_pc_a0_captured <= 1'b0;
        end else if (!first_pc_a0_captured && core_ev_valid &&
                     u_core.retired_pc == 32'h0000_00A0) begin
            first_pc_a0_captured <= 1'b1;
            first_pc_a0_cycle    <= sim_cycle_ctr;
            first_pc_a0_instr    <= u_core.retired_instr;
            first_pc_a0_r31      <= u_core.regfile[31];
            first_pc_a0_r10      <= u_core.regfile[10];
            $display("[tb_ee_core_bios_smoke] FIRST_PC_A0 cyc=%0d instr=0x%08h $31=0x%08h $10=0x%08h",
                     sim_cycle_ctr, u_core.retired_instr,
                     u_core.regfile[31], u_core.regfile[10]);
        end
    end

    // Ch186 — writes-to-RAM-dispatcher observer. Capture every store
    // landing in [0x30000, 0x30040). Tells us WHEN the BIOS installed
    // the RAM-dispatcher code (and the PC that did the install).
    always_ff @(posedge clk) begin
        logic [28:0] phys_ram;
        if (!rst_n) begin
            ram_disp_wr_count <= 0;
        end else if (ee_wr_en) begin
            phys_ram = ee_wr_addr[28:0];
            if (phys_ram >= 29'h000_30000 && phys_ram < 29'h000_30040) begin
                ram_disp_wr_cyc [ram_disp_wr_count % RAM_DISP_WR_DEPTH] <= sim_cycle_ctr;
                ram_disp_wr_addr[ram_disp_wr_count % RAM_DISP_WR_DEPTH] <= ee_wr_addr;
                ram_disp_wr_data[ram_disp_wr_count % RAM_DISP_WR_DEPTH] <= ee_wr_data;
                ram_disp_wr_pc  [ram_disp_wr_count % RAM_DISP_WR_DEPTH] <= u_core.retired_pc;
                ram_disp_wr_count <= ram_disp_wr_count + 1;
                $display("[tb_ee_core_bios_smoke] RAM_DISP_WRITE cyc=%0d addr=0x%08h data=0x%08h pc=0x%08h",
                         sim_cycle_ctr, ee_wr_addr, ee_wr_data, u_core.retired_pc);
            end
        end
    end

    // Ch184 — patch-write observer. Every CPU store whose phys
    // address falls in [0x598, 0x5C0) is enrolled in a ring buffer.
    // This catches BIOS self-modifying-code patches of the memset
    // template, regardless of whether the store goes via useg,
    // KSEG0, or KSEG1 (all collapse to phys[28:0] = 0x000005xx).
    always_ff @(posedge clk) begin
        logic [28:0] phys_w;
        if (!rst_n) begin
            patch_ring_count <= 0;
        end else if (ee_wr_en) begin
            phys_w = ee_wr_addr[28:0];
            if (phys_w >= 29'h000_0598 && phys_w < 29'h000_05C0) begin
                patch_ring_cyc [patch_ring_count % PATCH_RING_DEPTH] <= sim_cycle_ctr;
                patch_ring_addr[patch_ring_count % PATCH_RING_DEPTH] <= ee_wr_addr;
                patch_ring_data[patch_ring_count % PATCH_RING_DEPTH] <= ee_wr_data;
                patch_ring_be  [patch_ring_count % PATCH_RING_DEPTH] <= ee_wr_be;
                patch_ring_count <= patch_ring_count + 1;
                $display("[tb_ee_core_bios_smoke] PATCH_WRITE cyc=%0d addr=0x%08h data=0x%08h be=0x%01h pc_now=0x%08h",
                         sim_cycle_ctr, ee_wr_addr, ee_wr_data, ee_wr_be,
                         u_core.retired_pc);
            end
        end
    end

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            entry598_captured <= 1'b0;
            entry598_count    <= 0;
        end else if (core_ev_valid && u_core.retired_pc == 32'h0000_0598) begin
            entry598_captured           <= 1'b1;
            entry598_count              <= entry598_count + 1;
            entry598_cycle              <= sim_cycle_ctr;
            // Ch184 — per-entry snapshot of the template instruction
            // words + registers at the moment of entry. Indexed by
            // the count BEFORE increment.
            if (entry598_count < ENTRY_SNAP_DEPTH) begin
                entry_snap_cyc      [entry598_count] <= sim_cycle_ctr;
                entry_snap_r1       [entry598_count] <= u_core.regfile[1];
                entry_snap_r10      [entry598_count] <= u_core.regfile[10];
                entry_snap_r31      [entry598_count] <= u_core.regfile[31];
                entry_snap_instr5a8 [entry598_count] <= peek_instr(32'h0000_05A8);
                entry_snap_instr5ac [entry598_count] <= peek_instr(32'h0000_05AC);
                entry_snap_instr5b0 [entry598_count] <= peek_instr(32'h0000_05B0);
                entry_snap_instr5b4 [entry598_count] <= peek_instr(32'h0000_05B4);
                entry_snap_instr5b8 [entry598_count] <= peek_instr(32'h0000_05B8);
            end
            entry598_r1                 <= u_core.regfile[1];
            entry598_r2                 <= u_core.regfile[2];
            entry598_r3                 <= u_core.regfile[3];
            entry598_r4                 <= u_core.regfile[4];
            entry598_r5                 <= u_core.regfile[5];
            entry598_r6                 <= u_core.regfile[6];
            entry598_r7                 <= u_core.regfile[7];
            entry598_r8                 <= u_core.regfile[8];
            entry598_r29                <= u_core.regfile[29];
            entry598_r30                <= u_core.regfile[30];
            entry598_r31                <= u_core.regfile[31];
            entry598_ring_prev_pc       <= retire_ring_pc[ring_prev_idx];
            entry598_ring_prev_instr    <= retire_ring_instr[ring_prev_idx];
            entry598_ring_prev2_pc      <= retire_ring_pc[ring_prev2_idx];
            entry598_ring_prev2_instr   <= retire_ring_instr[ring_prev2_idx];
            entry598_ring_prev3_pc      <= retire_ring_pc[ring_prev3_idx];
            entry598_ring_prev3_instr   <= retire_ring_instr[ring_prev3_idx];
            entry598_ring_prev4_pc      <= retire_ring_pc[ring_prev4_idx];
            entry598_ring_prev4_instr   <= retire_ring_instr[ring_prev4_idx];
            $display("[tb_ee_core_bios_smoke] ENTRY598#%0d cycle=%0d $1=0x%08h $2=0x%08h $3=0x%08h $4=0x%08h $5=0x%08h $8=0x%08h $31=0x%08h prev=[0x%08h, 0x%08h, 0x%08h, 0x%08h]",
                     entry598_count + 1,
                     sim_cycle_ctr,
                     u_core.regfile[1], u_core.regfile[2], u_core.regfile[3],
                     u_core.regfile[4], u_core.regfile[5], u_core.regfile[8],
                     u_core.regfile[31],
                     retire_ring_pc[ring_prev_idx],
                     retire_ring_pc[ring_prev2_idx],
                     retire_ring_pc[ring_prev3_idx],
                     retire_ring_pc[ring_prev4_idx]);
        end
    end

    // Ch181 — ENTRY5A8: every retire at the loop-body PC, with
    // anomalous-prev-PC filter. Logging EVERY 0x5A8 retire would
    // be too noisy (the loop hits it ~392k times) — so we only
    // $display when the immediately previous retire was NOT in
    // the "expected in-function flow" set {0x5A4 (function-head
    // fall-through), 0x5B8 (BNE delay slot — back-edge)}. Both
    // total-count and anomalous-count are tracked separately.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            entry5a8_any_captured       <= 1'b0;
            entry5a8_anomalous_captured <= 1'b0;
            entry5a8_total_count        <= 0;
            entry5a8_anomalous_count    <= 0;
        end else if (core_ev_valid && u_core.retired_pc == 32'h0000_05A8) begin
            entry5a8_any_captured <= 1'b1;
            entry5a8_total_count  <= entry5a8_total_count + 1;
            // Anomalous? Prev-PC not in expected set.
            if (retire_ring_pc[ring_prev_idx] != 32'h0000_05A4 &&
                retire_ring_pc[ring_prev_idx] != 32'h0000_05B8) begin
                entry5a8_anomalous_captured <= 1'b1;
                entry5a8_anomalous_count    <= entry5a8_anomalous_count + 1;
                entry5a8_cycle              <= sim_cycle_ctr;
                entry5a8_r1                 <= u_core.regfile[1];
                entry5a8_r2                 <= u_core.regfile[2];
                entry5a8_r3                 <= u_core.regfile[3];
                entry5a8_r31                <= u_core.regfile[31];
                entry5a8_prev_pc            <= retire_ring_pc[ring_prev_idx];
                entry5a8_prev_instr         <= retire_ring_instr[ring_prev_idx];
                entry5a8_ring_prev2_pc      <= retire_ring_pc[ring_prev2_idx];
                entry5a8_ring_prev2_instr   <= retire_ring_instr[ring_prev2_idx];
                entry5a8_ring_prev3_pc      <= retire_ring_pc[ring_prev3_idx];
                entry5a8_ring_prev3_instr   <= retire_ring_instr[ring_prev3_idx];
                entry5a8_ring_prev4_pc      <= retire_ring_pc[ring_prev4_idx];
                entry5a8_ring_prev4_instr   <= retire_ring_instr[ring_prev4_idx];
                $display("[tb_ee_core_bios_smoke] ENTRY5A8_ANOMALOUS#%0d cycle=%0d $1=0x%08h $2=0x%08h $3=0x%08h $31=0x%08h prev_pc=0x%08h prev_instr=0x%08h prev_chain=[0x%08h, 0x%08h, 0x%08h]",
                         entry5a8_anomalous_count + 1,
                         sim_cycle_ctr,
                         u_core.regfile[1], u_core.regfile[2], u_core.regfile[3],
                         u_core.regfile[31],
                         retire_ring_pc[ring_prev_idx],
                         retire_ring_instr[ring_prev_idx],
                         retire_ring_pc[ring_prev2_idx],
                         retire_ring_pc[ring_prev3_idx],
                         retire_ring_pc[ring_prev4_idx]);
            end
        end
    end

    // Chapter 48 — $3 provenance observer. R3_CHG logs every $3
    // change inline; R3_RING keeps a circular depth-8 record of
    // the recent producers so the post-halt dump can show the
    // chain that led into the ch47 unaligned-SW trap at pc=
    // 0xBFC500A4. retired_arg2/arg3 are logged verbatim so the
    // post-halt classifier can distinguish LW (arg2=EA, arg3=loaded
    // value) from ALU builds (arg3=result) and R-type copies
    // (arg2={..,rd_idx}, arg3=result). Same NBA-timing rule as
    // ch36 — do NOT gate on core_ev_valid; transition edge is
    // authoritative.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r3_prev       <= 32'd0;
            r3_prev_valid <= 1'b0;
            r3_ring_count <= 0;
        end else begin
            r3_prev       <= u_core.regfile[3];
            r3_prev_valid <= 1'b1;
            if (r3_prev_valid && u_core.regfile[3] !== r3_prev) begin
                if (ch200_r3_prints < CH200_PRINT_CAP) begin
                    $display("[tb_ee_core_bios_smoke] R3_CHG cyc=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h retired_instr=0x%08h arg2=0x%08h arg3=0x%08h",
                             sim_cycle_ctr, r3_prev, u_core.regfile[3],
                             u_core.retired_pc, u_core.retired_instr,
                             u_core.retired_arg2, u_core.retired_arg3);
                    ch200_r3_prints <= ch200_r3_prints + 1;
                end else begin
                    ch200_suppressed <= ch200_suppressed + 1;
                end
                // Circular write into R3_RING (most-recent 8).
                r3_ring_cyc  [r3_ring_count % R3_RING_DEPTH] <= sim_cycle_ctr;
                r3_ring_prev [r3_ring_count % R3_RING_DEPTH] <= r3_prev;
                r3_ring_new  [r3_ring_count % R3_RING_DEPTH] <= u_core.regfile[3];
                r3_ring_pc   [r3_ring_count % R3_RING_DEPTH] <= u_core.retired_pc;
                r3_ring_instr[r3_ring_count % R3_RING_DEPTH] <= u_core.retired_instr;
                r3_ring_arg2 [r3_ring_count % R3_RING_DEPTH] <= u_core.retired_arg2;
                r3_ring_arg3 [r3_ring_count % R3_RING_DEPTH] <= u_core.retired_arg3;
                r3_ring_count <= r3_ring_count + 1;
            end
        end
    end

    // Chapter 38 — $10 provenance observer. Same shape as the ch36
    // R31_* pair. R10_CHG logs every $10 change inline; R10_TO_C0
    // captures the first transition to R10_TARGET_VALUE = 0xC0.
    // iverilog NBA quirk from ch36 applies: read u_core.regfile[10]
    // on the cycle AFTER the write retires, so NOT gated on
    // core_ev_valid; retired_pc / arg2 / arg3 on that cycle still
    // reflect the writing retire (NBA-latched reg read).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r10_prev_valid     <= 1'b0;
            r10_prev           <= 32'd0;
            r10_to_c0_captured <= 1'b0;
        end else begin
            r10_prev       <= u_core.regfile[10];
            r10_prev_valid <= 1'b1;

            if (r10_prev_valid && u_core.regfile[10] !== r10_prev) begin
                if (ch200_r10_prints < CH200_PRINT_CAP) begin
                    $display("[tb_ee_core_bios_smoke] R10_CHG cyc=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h retired_instr=0x%08h retired_pc_d1=0x%08h retired_instr_d1=0x%08h arg2=0x%08h arg3=0x%08h",
                             sim_cycle_ctr, r10_prev, u_core.regfile[10],
                             u_core.retired_pc, u_core.retired_instr,
                             retired_pc_d1, retired_instr_d1,
                             u_core.retired_arg2, u_core.retired_arg3);
                    ch200_r10_prints <= ch200_r10_prints + 1;
                end else begin
                    ch200_suppressed <= ch200_suppressed + 1;
                end
            end

            if (!r10_to_c0_captured && r10_prev_valid &&
                u_core.regfile[10] == R10_TARGET_VALUE &&
                r10_prev           != R10_TARGET_VALUE) begin
                r10_to_c0_captured         <= 1'b1;
                r10_to_c0_cycle            <= sim_cycle_ctr;
                r10_to_c0_retired_pc       <= u_core.retired_pc;
                r10_to_c0_retired_instr    <= u_core.retired_instr;
                r10_to_c0_retired_arg2     <= u_core.retired_arg2;
                r10_to_c0_retired_arg3     <= u_core.retired_arg3;
                r10_to_c0_prev_r10         <= r10_prev;
                r10_to_c0_rs_idx           <= u_core.retired_instr[25:21];
                r10_to_c0_rt_idx           <= u_core.retired_instr[20:16];
                r10_to_c0_rd_idx           <= u_core.retired_instr[15:11];
                r10_to_c0_rs_val           <= u_core.regfile[u_core.retired_instr[25:21]];
                r10_to_c0_rt_val           <= u_core.regfile[u_core.retired_instr[20:16]];
                r10_to_c0_ring_prev_pc     <= retire_ring_pc[ring_prev_idx];
                r10_to_c0_ring_prev_instr  <= retire_ring_instr[ring_prev_idx];
                r10_to_c0_ring_prev2_pc    <= retire_ring_pc[ring_prev2_idx];
                r10_to_c0_ring_prev2_instr <= retire_ring_instr[ring_prev2_idx];
                $display("[tb_ee_core_bios_smoke] R10_TO_C0 cycle=%0d retired_pc=0x%08h retired_instr=0x%08h prev_r10=0x%08h new_r10=0x%08h arg2=0x%08h arg3=0x%08h",
                         sim_cycle_ctr,
                         u_core.retired_pc, u_core.retired_instr,
                         r10_prev, u_core.regfile[10],
                         u_core.retired_arg2, u_core.retired_arg3);
            end
        end
    end

    // Chapter 39 — low-RAM write-provenance. Taps the shared 128-bit
    // ram_wr_* port so every writer (core, DMAC, bridges) is seen.
    // ee_ram_wr_addr is a 25-bit byte address; qword index =
    // addr[24:4]. 0xC0 / 16 = 0x00C, 0x1F0 / 16 = 0x01F. A qword
    // write whose be16 is non-zero populates up to 16 bytes inside
    // that qword.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ram_c0_exact_captured <= 1'b0;
            ram_c0_win_count      <= 0;
        end else if (ee_ram_wr_en && ee_ram_wr_be != 16'd0) begin
            // Exact-0xC0 one-shot.
            if (!ram_c0_exact_captured &&
                ee_ram_wr_addr[24:4] == RAM_C0_QW_IDX) begin
                ram_c0_exact_captured         <= 1'b1;
                ram_c0_exact_cycle            <= sim_cycle_ctr;
                ram_c0_exact_data             <= ee_ram_wr_data;
                ram_c0_exact_be               <= ee_ram_wr_be;
                ram_c0_exact_master           <= ee_ram_master_id;
                ram_c0_exact_retired_pc       <= u_core.retired_pc;
                ram_c0_exact_retired_instr    <= u_core.retired_instr;
                ram_c0_exact_retired_was_write <= core_ev_valid &&
                                                  u_core.retired_flag_write;
                $display("[tb_ee_core_bios_smoke] RAM_C0_EXACT cyc=%0d qw_idx=0x%03h (byte 0xC0 qword) master=%0d be=0x%04h data=0x%032h",
                         sim_cycle_ctr, ee_ram_wr_addr[24:4],
                         ee_ram_master_id, ee_ram_wr_be,
                         ee_ram_wr_data);
                $display("[tb_ee_core_bios_smoke] RAM_C0_EXACT   cpu-ctx core_ev_valid=%0d retired_pc=0x%08h retired_instr=0x%08h retired_flag_write=%0d",
                         core_ev_valid, u_core.retired_pc,
                         u_core.retired_instr,
                         u_core.retired_flag_write);
            end
            // Window ring (depth-bounded linear array).
            if (ee_ram_wr_addr[24:4] >= RAM_WIN_QW_LOW &&
                ee_ram_wr_addr[24:4] <= RAM_WIN_QW_HIGH &&
                ram_c0_win_count < RAM_C0_WIN_DEPTH) begin
                ram_c0_win_cyc   [ram_c0_win_count] <= sim_cycle_ctr;
                ram_c0_win_qw_idx[ram_c0_win_count] <= ee_ram_wr_addr[24:4];
                ram_c0_win_be    [ram_c0_win_count] <= ee_ram_wr_be;
                ram_c0_win_data  [ram_c0_win_count] <= ee_ram_wr_data;
                ram_c0_win_master[ram_c0_win_count] <= ee_ram_master_id;
                ram_c0_win_pc    [ram_c0_win_count] <= u_core.retired_pc;
                ram_c0_win_instr [ram_c0_win_count] <= u_core.retired_instr;
            end
            if (ee_ram_wr_addr[24:4] >= RAM_WIN_QW_LOW &&
                ee_ram_wr_addr[24:4] <= RAM_WIN_QW_HIGH)
                ram_c0_win_count <= ram_c0_win_count + 1;
        end
    end

    // Chapter 39 (part 2) / Ch46 — CPU-side 32-bit write tap.
    // Catches writes to useg 0x000000XX (which post-ch33 go to the
    // in-map useg_shadow instead of ee_ram) as well as kseg0/kseg1
    // views of the same phys byte range. Any core SW whose phys EA
    // falls in [0xC0, 0x1FF] is recorded.
    //
    // Ch46 changes:
    //   - Circular wrap (CPU_WIN_DEPTH deepened to 128).
    //   - Walkback to the most recent J/JAL/JR/JALR per entry,
    //     reusing the ch45 walkback_search task, so each capture
    //     names the ROM caller of the routine doing the write.
    //   - Separate one-shots for the exact words at phys 0xC0 and
    //     phys 0xC4 (the first two words of the ch40 stub — the
    //     ones known to be overwritten per ch45 ESCAPE_RING #20).
    //     Each exact trigger also uses walkback for caller context
    //     and records which address view (useg / kseg0 / kseg1 /
    //     ...) the write came through.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            cpu_win_count     <= 0;
            cpu_win_write_idx <= 0;
            ram_c0w_captured  <= 1'b0;
            ram_c4w_captured  <= 1'b0;
        end else if (ee_wr_en && ee_wr_be != 4'd0 &&
                     ee_wr_addr[28:0] >= 29'h0000_00C0 &&
                     ee_wr_addr[28:0] <= 29'h0000_01FF) begin
            // Circular CPU_WIN insert.
            cpu_win_write_idx = cpu_win_count % CPU_WIN_DEPTH;
            walkback_search(retire_ring_head,
                            walk_cf_found, walk_cf_hops, walk_cf_idx);
            cpu_win_cyc     [cpu_win_write_idx] <= sim_cycle_ctr;
            cpu_win_addr    [cpu_win_write_idx] <= ee_wr_addr;
            cpu_win_data    [cpu_win_write_idx] <= ee_wr_data;
            cpu_win_be      [cpu_win_write_idx] <= ee_wr_be;
            cpu_win_pc      [cpu_win_write_idx] <= u_core.retired_pc;
            cpu_win_instr   [cpu_win_write_idx] <= u_core.retired_instr;
            if (walk_cf_found) begin
                cpu_win_cf_pc   [cpu_win_write_idx] <= retire_ring_pc   [walk_cf_idx];
                cpu_win_cf_instr[cpu_win_write_idx] <= retire_ring_instr[walk_cf_idx];
                cpu_win_cf_found[cpu_win_write_idx] <= 1'b1;
                cpu_win_cf_rs   [cpu_win_write_idx] <= retire_ring_instr[walk_cf_idx][25:21];
            end else begin
                cpu_win_cf_pc   [cpu_win_write_idx] <= 32'd0;
                cpu_win_cf_instr[cpu_win_write_idx] <= 32'd0;
                cpu_win_cf_found[cpu_win_write_idx] <= 1'b0;
                cpu_win_cf_rs   [cpu_win_write_idx] <= 5'd0;
            end
            cpu_win_count <= cpu_win_count + 1;

            // Exact-word triggers for 0xC0 and 0xC4. Match on
            // phys-word-index (phys byte addr[28:2]) so SH/SB
            // writes that touch any byte inside the word also
            // fire. Gated on `ch40_stub_installed` so the RAM-
            // clear loop's first-write burst is excluded and we
            // see the FIRST post-stub overwrite of the stub
            // instructions.
            if (ch40_stub_installed &&
                !ram_c0w_captured && ee_wr_addr[28:2] == 27'h0000030) begin
                ram_c0w_captured <= 1'b1;
                ram_c0w_cycle    <= sim_cycle_ctr;
                ram_c0w_addr     <= ee_wr_addr;
                ram_c0w_data     <= ee_wr_data;
                ram_c0w_be       <= ee_wr_be;
                ram_c0w_pc       <= u_core.retired_pc;
                ram_c0w_instr    <= u_core.retired_instr;
                if (walk_cf_found) begin
                    ram_c0w_cf_pc    <= retire_ring_pc   [walk_cf_idx];
                    ram_c0w_cf_instr <= retire_ring_instr[walk_cf_idx];
                    ram_c0w_cf_found <= 1'b1;
                end else begin
                    ram_c0w_cf_found <= 1'b0;
                end
                $display("[tb_ee_core_bios_smoke] RAM_C0_WORD cyc=%0d addr=0x%08h (view=%s) be=0x%01h data=0x%08h retired_pc=0x%08h retired_instr=0x%08h",
                         sim_cycle_ctr, ee_wr_addr,
                         (ee_wr_addr[31:29] == 3'b000) ? "useg" :
                         (ee_wr_addr[31:29] == 3'b100) ? "kseg0" :
                         (ee_wr_addr[31:29] == 3'b101) ? "kseg1" :
                         (ee_wr_addr[31:29] == 3'b110) ? "kseg2" :
                         (ee_wr_addr[31:29] == 3'b111) ? "kseg3" : "???",
                         ee_wr_be, ee_wr_data,
                         u_core.retired_pc, u_core.retired_instr);
            end
            if (ch40_stub_installed &&
                !ram_c4w_captured && ee_wr_addr[28:2] == 27'h0000031) begin
                ram_c4w_captured <= 1'b1;
                ram_c4w_cycle    <= sim_cycle_ctr;
                ram_c4w_addr     <= ee_wr_addr;
                ram_c4w_data     <= ee_wr_data;
                ram_c4w_be       <= ee_wr_be;
                ram_c4w_pc       <= u_core.retired_pc;
                ram_c4w_instr    <= u_core.retired_instr;
                if (walk_cf_found) begin
                    ram_c4w_cf_pc    <= retire_ring_pc   [walk_cf_idx];
                    ram_c4w_cf_instr <= retire_ring_instr[walk_cf_idx];
                    ram_c4w_cf_found <= 1'b1;
                end else begin
                    ram_c4w_cf_found <= 1'b0;
                end
                $display("[tb_ee_core_bios_smoke] RAM_C4_WORD cyc=%0d addr=0x%08h (view=%s) be=0x%01h data=0x%08h retired_pc=0x%08h retired_instr=0x%08h",
                         sim_cycle_ctr, ee_wr_addr,
                         (ee_wr_addr[31:29] == 3'b000) ? "useg" :
                         (ee_wr_addr[31:29] == 3'b100) ? "kseg0" :
                         (ee_wr_addr[31:29] == 3'b101) ? "kseg1" :
                         (ee_wr_addr[31:29] == 3'b110) ? "kseg2" :
                         (ee_wr_addr[31:29] == 3'b111) ? "kseg3" : "???",
                         ee_wr_be, ee_wr_data,
                         u_core.retired_pc, u_core.retired_instr);
            end
        end
    end

    // Chapter 51 — EXC_W_WIN / EXC_FETCH_WIN: post-BEV
    // exception-handler window observer. Captures every write into
    // phys [0x80..0xBF] and every retire whose pc lands in that
    // range, BUT only after Status.BEV has been cleared (the
    // moment the BIOS's RAM-resident handler expectation kicks
    // in). Address-view decoded via addr[31:29].
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            exc_w_win_count     <= 0;
            exc_w_win_write_idx <= 0;
        end else if (!u_core.status_bev && ee_wr_en && ee_wr_be != 4'd0 &&
                     ee_wr_addr[28:0] >= EXC_WIN_LOW_PHYS &&
                     ee_wr_addr[28:0] <= EXC_WIN_HIGH_PHYS) begin
            exc_w_win_write_idx = exc_w_win_count % EXC_WIN_DEPTH;
            walkback_search(retire_ring_head,
                            walk_cf_found, walk_cf_hops, walk_cf_idx);
            exc_w_win_cyc     [exc_w_win_write_idx] <= sim_cycle_ctr;
            exc_w_win_addr    [exc_w_win_write_idx] <= ee_wr_addr;
            exc_w_win_data    [exc_w_win_write_idx] <= ee_wr_data;
            exc_w_win_be      [exc_w_win_write_idx] <= ee_wr_be;
            exc_w_win_pc      [exc_w_win_write_idx] <= u_core.retired_pc;
            exc_w_win_instr   [exc_w_win_write_idx] <= u_core.retired_instr;
            if (walk_cf_found) begin
                exc_w_win_cf_pc   [exc_w_win_write_idx] <= retire_ring_pc   [walk_cf_idx];
                exc_w_win_cf_instr[exc_w_win_write_idx] <= retire_ring_instr[walk_cf_idx];
                exc_w_win_cf_found[exc_w_win_write_idx] <= 1'b1;
                exc_w_win_cf_rs   [exc_w_win_write_idx] <= retire_ring_instr[walk_cf_idx][25:21];
            end else begin
                exc_w_win_cf_pc   [exc_w_win_write_idx] <= 32'd0;
                exc_w_win_cf_instr[exc_w_win_write_idx] <= 32'd0;
                exc_w_win_cf_found[exc_w_win_write_idx] <= 1'b0;
                exc_w_win_cf_rs   [exc_w_win_write_idx] <= 5'd0;
            end
            exc_w_win_count <= exc_w_win_count + 1;
            $display("[tb_ee_core_bios_smoke] EXC_W_WIN[#%0d slot=%0d] cyc=%0d addr=0x%08h (view=%s) be=0x%01h data=0x%08h pc=0x%08h instr=0x%08h",
                     exc_w_win_count, exc_w_win_write_idx,
                     sim_cycle_ctr, ee_wr_addr,
                     (ee_wr_addr[31:29] == 3'b000) ? "useg" :
                     (ee_wr_addr[31:29] == 3'b100) ? "kseg0" :
                     (ee_wr_addr[31:29] == 3'b101) ? "kseg1" :
                     (ee_wr_addr[31:29] == 3'b110) ? "kseg2" :
                     (ee_wr_addr[31:29] == 3'b111) ? "kseg3" : "???",
                     ee_wr_be, ee_wr_data,
                     u_core.retired_pc, u_core.retired_instr);
        end
    end

    // Fetch-side observer: every retire whose pc is in the window
    // after BEV cleared. landing_instr shows whether the core
    // read valid handler bytes or zero-fill.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            exc_f_win_count <= 0;
        end else if (!u_core.status_bev && core_ev_valid &&
                     u_core.retired_pc[28:0] >= EXC_WIN_LOW_PHYS &&
                     u_core.retired_pc[28:0] <= EXC_WIN_HIGH_PHYS &&
                     u_core.retired_pc[31:29] == 3'b000) begin
            // Only log useg-path fetches (top nibble 0) since that's
            // the only view where the EE core actually fetches
            // from the handler window — EXC_VECTOR default is
            // useg 0x80. The top-nibble test above filters out
            // stray observer hits on BIOS ROM fetches.
            exc_f_win_write_idx = exc_f_win_count % EXC_WIN_DEPTH;
            exc_f_win_cyc   [exc_f_win_write_idx] <= sim_cycle_ctr;
            exc_f_win_pc    [exc_f_win_write_idx] <= u_core.retired_pc;
            exc_f_win_instr [exc_f_win_write_idx] <= u_core.retired_instr;
            exc_f_win_r31   [exc_f_win_write_idx] <= u_core.regfile[31];
            exc_f_win_epc   [exc_f_win_write_idx] <= u_core.epc;
            exc_f_win_badv  [exc_f_win_write_idx] <= u_core.badvaddr;
            exc_f_win_count <= exc_f_win_count + 1;
        end
    end

    // Chapter 15-B: prologue observer — capture the first
    // PROLOGUE_DEPTH retires whose retired_pc lies within the 0x14xx
    // routine body. rs/rt decode + live source-register values tell
    // us which registers the routine actually reads on entry.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            prologue_count <= 0;
        end else if (core_ev_valid &&
                     u_core.retired_pc >= PROLOGUE_LOW &&
                     u_core.retired_pc <= PROLOGUE_HIGH &&
                     prologue_count < PROLOGUE_DEPTH) begin
            prologue_cyc[prologue_count]    <= sim_cycle_ctr;
            prologue_pc[prologue_count]     <= u_core.retired_pc;
            prologue_instr[prologue_count]  <= u_core.retired_instr;
            prologue_rs[prologue_count]     <= u_core.retired_instr[25:21];
            prologue_rt[prologue_count]     <= u_core.retired_instr[20:16];
            prologue_rs_val[prologue_count] <= u_core.regfile[u_core.retired_instr[25:21]];
            prologue_rt_val[prologue_count] <= u_core.regfile[u_core.retired_instr[20:16]];
            prologue_arg2[prologue_count]   <= u_core.retired_arg2;
            prologue_arg3[prologue_count]   <= u_core.retired_arg3;
            prologue_count                  <= prologue_count + 1;
            $display("[tb_ee_core_bios_smoke] PROLOGUE[%0d] cyc=%0d pc=0x%08h instr=0x%08h rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h arg2=0x%08h arg3=0x%08h",
                     prologue_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.retired_instr[25:21],
                     u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.regfile[u_core.retired_instr[20:16]],
                     u_core.retired_arg2, u_core.retired_arg3);
        end
    end

    // Chapter 20: prologue observer for the RAM-at-0x500 routine (the
    // one the ROM JAL at pc=0xBFC529E8 lands in). Same shape as the
    // 0x14xx prologue.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            prologue2_count <= 0;
        end else if (core_ev_valid &&
                     u_core.retired_pc >= PROLOGUE2_LOW &&
                     u_core.retired_pc <= PROLOGUE2_HIGH &&
                     prologue2_count < PROLOGUE2_DEPTH) begin
            prologue2_cyc[prologue2_count]    <= sim_cycle_ctr;
            prologue2_pc[prologue2_count]     <= u_core.retired_pc;
            prologue2_instr[prologue2_count]  <= u_core.retired_instr;
            prologue2_rs[prologue2_count]     <= u_core.retired_instr[25:21];
            prologue2_rt[prologue2_count]     <= u_core.retired_instr[20:16];
            prologue2_rs_val[prologue2_count] <= u_core.regfile[u_core.retired_instr[25:21]];
            prologue2_rt_val[prologue2_count] <= u_core.regfile[u_core.retired_instr[20:16]];
            prologue2_arg2[prologue2_count]   <= u_core.retired_arg2;
            prologue2_arg3[prologue2_count]   <= u_core.retired_arg3;
            prologue2_count                   <= prologue2_count + 1;
            $display("[tb_ee_core_bios_smoke] PROLOGUE2[%0d] cyc=%0d pc=0x%08h instr=0x%08h rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h arg2=0x%08h arg3=0x%08h",
                     prologue2_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.retired_instr[25:21],
                     u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.regfile[u_core.retired_instr[20:16]],
                     u_core.retired_arg2, u_core.retired_arg3);
        end
    end

    // Chapter 21: $6 consumer observer. Fires on retires that
    // semantically consume $6 (jump / load-or-store base / ALU
    // source / branch source), gated to the window between the
    // ROM→RAM handoff and ENTRY14.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r6_consumer_count <= 0;
        end else if (core_ev_valid &&
                     rom2ram_captured && !entry14_captured &&
                     r6d_is_consumer &&
                     r6_consumer_count < R6_CONSUMER_DEPTH) begin
            r6_consumer_cyc[r6_consumer_count]    <= sim_cycle_ctr;
            r6_consumer_pc[r6_consumer_count]     <= u_core.retired_pc;
            r6_consumer_instr[r6_consumer_count]  <= u_core.retired_instr;
            r6_consumer_r6_val[r6_consumer_count] <= u_core.regfile[6];
            r6_consumer_arg2[r6_consumer_count]   <= u_core.retired_arg2;
            r6_consumer_arg3[r6_consumer_count]   <= u_core.retired_arg3;
            r6_consumer_kind[r6_consumer_count]   <= r6d_kind;
            r6_consumer_count                     <= r6_consumer_count + 1;
            $display("[tb_ee_core_bios_smoke] R6_CONSUMER[%0d] cyc=%0d pc=0x%08h instr=0x%08h kind=%s $6=0x%08h arg2=0x%08h arg3=0x%08h",
                     r6_consumer_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     (r6d_kind == 0) ? "JUMP"   :
                     (r6d_kind == 1) ? "LOAD"   :
                     (r6d_kind == 2) ? "STORE"  :
                     (r6d_kind == 3) ? "ALU"    :
                     (r6d_kind == 4) ? "BRANCH" : "OTHER",
                     u_core.regfile[6], u_core.retired_arg2,
                     u_core.retired_arg3);
        end
    end

    // Chapter 22 — $6 write tracker. Change-detector on regfile[6] vs
    // r6_prev_1. Gated on rom2ram_captured && r6_consumer_count==0 so
    // only PRE-first-consumer writes are recorded. retired_pc/instr
    // are authoritative because they don't advance until the next
    // retire fires (NBA-ordering note established in ch16).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r6_write_count <= 0;
        end else if (rom2ram_captured &&
                     r6_consumer_count == 0 &&
                     u_core.regfile[6] != r6_prev_1 &&
                     r6_write_count < R6_WRITE_DEPTH) begin
            r6_write_cyc[r6_write_count]   <= sim_cycle_ctr;
            r6_write_pc[r6_write_count]    <= u_core.retired_pc;
            r6_write_instr[r6_write_count] <= u_core.retired_instr;
            r6_write_prev[r6_write_count]  <= r6_prev_1;
            r6_write_new[r6_write_count]   <= u_core.regfile[6];
            r6_write_arg2[r6_write_count]  <= u_core.retired_arg2;
            r6_write_arg3[r6_write_count]  <= u_core.retired_arg3;
            r6_write_kind[r6_write_count]  <= r6w_kind;
            r6_write_count                 <= r6_write_count + 1;
            $display("[tb_ee_core_bios_smoke] R6_WRITE[%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h kind=%s arg2=0x%08h arg3=0x%08h",
                     r6_write_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     r6_prev_1, u_core.regfile[6],
                     (r6w_kind == 0) ? "LUI"    :
                     (r6w_kind == 1) ? "ADDI_U":
                     (r6w_kind == 2) ? "LOAD"   :
                     (r6w_kind == 3) ? "ALU-R"  :
                                       "OTHER",
                     u_core.retired_arg2, u_core.retired_arg3);
        end
    end

    // Chapter 23: low-RAM vector-table write survey. Counts fill
    // (data == DEFAULT_FILL) and captures patches (data != fill) with
    // full context. A retire is counted as targeting the table when
    // retired_flag_write is set AND retired_arg2's phys maps into
    // [0x200, 0x3FF].
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            vec_zero_fill_count    <= 0;
            vec_default_fill_count <= 0;
            vec_patch_count        <= 0;
            vec_intr_count         <= 0;
        end else if (core_ev_valid &&
                     u_core.retired_flag_write &&
                     vec_ea_in_range) begin
            if (u_core.retired_arg3 == 32'd0) begin
                vec_zero_fill_count <= vec_zero_fill_count + 1;
            end else if (u_core.retired_arg3 == DEFAULT_FILL) begin
                vec_default_fill_count <= vec_default_fill_count + 1;
            end else begin
                if (vec_patch_count < VEC_TABLE_DEPTH) begin
                    vec_patch_cyc[vec_patch_count]   <= sim_cycle_ctr;
                    vec_patch_pc[vec_patch_count]    <= u_core.retired_pc;
                    vec_patch_instr[vec_patch_count] <= u_core.retired_instr;
                    vec_patch_ea[vec_patch_count]    <= u_core.retired_arg2;
                    vec_patch_data[vec_patch_count]  <= u_core.retired_arg3;
                    $display("[tb_ee_core_bios_smoke] VEC_PATCH[%0d] cyc=%0d pc=0x%08h instr=0x%08h EA=0x%08h data=0x%08h",
                             vec_patch_count, sim_cycle_ctr,
                             u_core.retired_pc, u_core.retired_instr,
                             u_core.retired_arg2, u_core.retired_arg3);
                end
                vec_patch_count <= vec_patch_count + 1;
                // Interesting-patch side-ring: DATA phys maps into the
                // first 4 MB of RAM (phys[28:22] == 0). Those are the
                // ones that could dispatch to a RAM-code routine like
                // pc=0x1478.
                if (u_core.retired_arg3[28:22] == 7'd0 &&
                    vec_intr_count < VEC_INTR_DEPTH) begin
                    vec_intr_cyc[vec_intr_count]  <= sim_cycle_ctr;
                    vec_intr_pc[vec_intr_count]   <= u_core.retired_pc;
                    vec_intr_ea[vec_intr_count]   <= u_core.retired_arg2;
                    vec_intr_data[vec_intr_count] <= u_core.retired_arg3;
                    // NOTE: "candidate" not "confirmed" — we only know
                    // the patched value maps into low physical RAM.
                    // We have NOT proven that RAM address holds
                    // executable handler code (could be a data slot,
                    // a table pointer, or uninitialized RAM).
                    $display("[tb_ee_core_bios_smoke] VEC_INTR[%0d] cyc=%0d pc=0x%08h EA=0x%08h data=0x%08h (candidate RAM dispatch target)",
                             vec_intr_count, sim_cycle_ctr,
                             u_core.retired_pc, u_core.retired_arg2,
                             u_core.retired_arg3);
                    vec_intr_count <= vec_intr_count + 1;
                end
            end
        end
    end

    // Chapter 16 — $2 seed observer.
    // Capture only $2-writes that are NOT the 0xEE0 loop's
    // `addiu $2, $2, 4`. Uses the existing r2_prev_1 as a
    // change-detector.
    //
    // NBA timing: at the edge a retire fires, the core schedules
    // regfile[2] <= new_val via NBA. The TB's Active-region read
    // of u_core.regfile[2] at that edge sees the OLD value (NBA
    // uncommitted). The change becomes visible at the NEXT edge.
    // core_ev_valid is a one-cycle pulse at the retire edge, so
    // gating the change-detector on core_ev_valid would never
    // coincide with the actual observable change. Solution: drop
    // the retire gate. Detect regfile[2] changes on every edge;
    // retired_pc/instr/arg2/arg3 still hold the causing retire's
    // values at edge N+1 because they don't update until the NEXT
    // retire fires (~3 cycles later). Filter on retired_pc !=
    // R2_LOOP_PC to exclude the 0xEE0 loop body, per Codex's
    // narrow framing.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r2_seed_count <= 0;
            r2_seed_head  <= 0;
        end else if (u_core.regfile[2] != r2_prev_1 &&
                     u_core.retired_pc != R2_LOOP_PC) begin
            r2_seed_cyc[r2_seed_head]   <= sim_cycle_ctr;
            r2_seed_pc[r2_seed_head]    <= u_core.retired_pc;
            r2_seed_instr[r2_seed_head] <= u_core.retired_instr;
            r2_seed_prev[r2_seed_head]  <= r2_prev_1;
            r2_seed_new[r2_seed_head]   <= u_core.regfile[2];
            r2_seed_arg2[r2_seed_head]  <= u_core.retired_arg2;
            r2_seed_arg3[r2_seed_head]  <= u_core.retired_arg3;
            r2_seed_head  <= (r2_seed_head + 1) % R2_SEED_DEPTH;
            r2_seed_count <= r2_seed_count + 1;
            // Inline print still fires per event — useful for live
            // watching if the log isn't truncated. Timeout summary
            // does the newest-first walk authoritatively. Ch200 caps
            // this to first CH200_PRINT_CAP firings; the ring writes
            // above remain authoritative.
            if (ch200_r2_prints < CH200_PRINT_CAP) begin
                $display("[tb_ee_core_bios_smoke] R2_SEED#%0d cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h arg2=0x%08h arg3=0x%08h",
                         r2_seed_count, sim_cycle_ctr,
                         u_core.retired_pc, u_core.retired_instr,
                         r2_prev_1, u_core.regfile[2],
                         u_core.retired_arg2, u_core.retired_arg3);
                ch200_r2_prints <= ch200_r2_prints + 1;
            end else begin
                ch200_suppressed <= ch200_suppressed + 1;
            end
        end
    end

    // Chapter 17 control-flow seam triggers.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            scan_entry_captured     <= 1'b0;
            scan_exit_captured      <= 1'b0;
            dispatch_entry_captured <= 1'b0;
        end else if (core_ev_valid) begin
            // SCAN_ENTRY: first retire in the scan range.
            if (!scan_entry_captured &&
                u_core.retired_pc >= SCAN_REGION_LOW &&
                u_core.retired_pc <= SCAN_REGION_HIGH) begin
                scan_entry_captured     <= 1'b1;
                scan_entry_cycle        <= sim_cycle_ctr;
                scan_entry_pc           <= u_core.retired_pc;
                scan_entry_instr        <= u_core.retired_instr;
                scan_entry_caller_pc    <= retire_ring_pc[ring_prev_idx];
                scan_entry_caller_instr <= retire_ring_instr[ring_prev_idx];
                scan_entry_ra           <= u_core.regfile[31];
                scan_entry_sp           <= u_core.regfile[29];
                scan_entry_fp           <= u_core.regfile[30];
                scan_entry_r2           <= u_core.regfile[2];
                $display("[tb_ee_core_bios_smoke] SCAN_ENTRY cyc=%0d pc=0x%08h instr=0x%08h caller pc=0x%08h instr=0x%08h ra=0x%08h sp=0x%08h fp=0x%08h $2=0x%08h",
                         sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr,
                         retire_ring_pc[ring_prev_idx],
                         retire_ring_instr[ring_prev_idx],
                         u_core.regfile[31], u_core.regfile[29],
                         u_core.regfile[30], u_core.regfile[2]);
            end

            // SCAN_EXIT: first retire OUTSIDE the scan range AFTER
            // scan_entry fired AND previous retire was inside (tracked
            // by scan_in_region, which at this edge still holds the
            // previous retire's state pre-NBA-commit).
            if (scan_entry_captured && !scan_exit_captured &&
                scan_in_region &&
                !(u_core.retired_pc >= SCAN_REGION_LOW &&
                  u_core.retired_pc <= SCAN_REGION_HIGH)) begin
                scan_exit_captured     <= 1'b1;
                scan_exit_cycle        <= sim_cycle_ctr;
                scan_exit_target_pc    <= u_core.retired_pc;
                scan_exit_target_instr <= u_core.retired_instr;
                scan_exit_site_pc      <= retire_ring_pc[ring_prev_idx];
                scan_exit_site_instr   <= retire_ring_instr[ring_prev_idx];
                scan_exit_ra           <= u_core.regfile[31];
                scan_exit_sp           <= u_core.regfile[29];
                scan_exit_fp           <= u_core.regfile[30];
                scan_exit_r2           <= u_core.regfile[2];
                $display("[tb_ee_core_bios_smoke] SCAN_EXIT cyc=%0d target pc=0x%08h instr=0x%08h exit-site pc=0x%08h instr=0x%08h ra=0x%08h sp=0x%08h fp=0x%08h $2=0x%08h",
                         sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr,
                         retire_ring_pc[ring_prev_idx],
                         retire_ring_instr[ring_prev_idx],
                         u_core.regfile[31], u_core.regfile[29],
                         u_core.regfile[30], u_core.regfile[2]);
            end

            // ROM→RAM handoff: first retire with current pc in RAM
            // and previous retire (ring head-1) in BIOS ROM. Captures
            // ring head-1 (delay slot) AND head-2 (the JAL/J that
            // caused the handoff), plus argument and frame registers.
            if (!rom2ram_captured &&
                is_ram_pc(u_core.retired_pc) &&
                is_bios_pc(retire_ring_pc[ring_prev_idx])) begin
                rom2ram_captured     <= 1'b1;
                rom2ram_cycle        <= sim_cycle_ctr;
                rom2ram_land_pc      <= u_core.retired_pc;
                rom2ram_land_instr   <= u_core.retired_instr;
                rom2ram_delay_pc     <= retire_ring_pc[ring_prev_idx];
                rom2ram_delay_instr  <= retire_ring_instr[ring_prev_idx];
                rom2ram_caller_pc    <= retire_ring_pc[ring_prev2_idx];
                rom2ram_caller_instr <= retire_ring_instr[ring_prev2_idx];
                rom2ram_r4           <= u_core.regfile[4];
                rom2ram_r5           <= u_core.regfile[5];
                rom2ram_r6           <= u_core.regfile[6];
                rom2ram_r7           <= u_core.regfile[7];
                rom2ram_r28          <= u_core.regfile[28];
                rom2ram_r29          <= u_core.regfile[29];
                rom2ram_r30          <= u_core.regfile[30];
                rom2ram_r31          <= u_core.regfile[31];
                rom2ram_r2           <= u_core.regfile[2];
                $display("[tb_ee_core_bios_smoke] ROM2RAM cyc=%0d land pc=0x%08h instr=0x%08h",
                         sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr);
                $display("[tb_ee_core_bios_smoke] ROM2RAM   delay(ring-1) pc=0x%08h instr=0x%08h",
                         retire_ring_pc[ring_prev_idx],
                         retire_ring_instr[ring_prev_idx]);
                $display("[tb_ee_core_bios_smoke] ROM2RAM   caller(ring-2) pc=0x%08h instr=0x%08h",
                         retire_ring_pc[ring_prev2_idx],
                         retire_ring_instr[ring_prev2_idx]);
                $display("[tb_ee_core_bios_smoke] ROM2RAM   args $2=0x%08h $4/a0=0x%08h $5/a1=0x%08h $6/a2=0x%08h $7/a3=0x%08h",
                         u_core.regfile[2], u_core.regfile[4],
                         u_core.regfile[5], u_core.regfile[6],
                         u_core.regfile[7]);
                $display("[tb_ee_core_bios_smoke] ROM2RAM   frame $28/gp=0x%08h $29/sp=0x%08h $30/fp=0x%08h $31/ra=0x%08h",
                         u_core.regfile[28], u_core.regfile[29],
                         u_core.regfile[30], u_core.regfile[31]);
            end

            // DISPATCH_ENTRY: first retire in the 0x00A0 block.
            if (!dispatch_entry_captured &&
                u_core.retired_pc >= DISPATCH_BLOCK_LOW &&
                u_core.retired_pc <= DISPATCH_BLOCK_HIGH) begin
                dispatch_entry_captured     <= 1'b1;
                dispatch_entry_cycle        <= sim_cycle_ctr;
                dispatch_entry_pc           <= u_core.retired_pc;
                dispatch_entry_instr        <= u_core.retired_instr;
                dispatch_entry_caller_pc    <= retire_ring_pc[ring_prev_idx];
                dispatch_entry_caller_instr <= retire_ring_instr[ring_prev_idx];
                dispatch_entry_ra           <= u_core.regfile[31];
                dispatch_entry_sp           <= u_core.regfile[29];
                dispatch_entry_fp           <= u_core.regfile[30];
                dispatch_entry_r2           <= u_core.regfile[2];
                $display("[tb_ee_core_bios_smoke] DISPATCH_ENTRY cyc=%0d pc=0x%08h instr=0x%08h caller pc=0x%08h instr=0x%08h ra=0x%08h sp=0x%08h fp=0x%08h $2=0x%08h",
                         sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr,
                         retire_ring_pc[ring_prev_idx],
                         retire_ring_instr[ring_prev_idx],
                         u_core.regfile[31], u_core.regfile[29],
                         u_core.regfile[30], u_core.regfile[2]);
            end
        end
    end

    // Chapter 18 — MMIO-read -> branch correlator.
    // Runs every retire. Three phases:
    //   (1) Age all pending entries; expire those >= LOOKAHEAD.
    //   (2) If this retire is a LOAD and EA is MMIO, mark the
    //       destination register (rt_idx for LW/LB/LH/LHU) as
    //       pending. Overwrites any prior pending for that reg.
    //   (3) If this retire is BEQ(0x04) or BNE(0x05), check
    //       whether rs or rt is pending. If yes, capture a
    //       correlation and clear pending for that register.
    // Branches with a single source (BLEZ/BGTZ/BLTZ/BGEZ) not
    // handled — BEQ/BNE are the most common and most load-bearing
    // here.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            mmio_correl_count <= 0;
            for (int i = 0; i < 32; i++) begin
                r_pending_valid[i] <= 1'b0;
                r_pending_age[i]   <= 0;
            end
        end else if (core_ev_valid) begin
            // Phase 1: age + expire.
            for (int i = 0; i < 32; i++) begin
                if (r_pending_valid[i]) begin
                    if (r_pending_age[i] + 1 >= MMIO_LOOKAHEAD)
                        r_pending_valid[i] <= 1'b0;
                    else
                        r_pending_age[i] <= r_pending_age[i] + 1;
                end
            end

            // Phase 2: MMIO load arms destination register.
            if (u_core.retired_flag_read && mmio_ea_is_mmio) begin
                dbg_mmio_reads <= dbg_mmio_reads + 1;
                // LW destination = instr[20:16] (rt field)
                r_pending_valid[u_core.retired_instr[20:16]]     <= 1'b1;
                r_pending_age[u_core.retired_instr[20:16]]       <= 0;
                r_pending_cycle[u_core.retired_instr[20:16]]     <= sim_cycle_ctr;
                r_pending_load_pc[u_core.retired_instr[20:16]]   <= u_core.retired_pc;
                r_pending_load_ea[u_core.retired_instr[20:16]]   <= u_core.retired_arg2;
                r_pending_load_data[u_core.retired_instr[20:16]] <= u_core.retired_arg3;
            end

            // Phase 3: branch consumes pending reg. Gate capture on
            // !entry14_captured — we only want correlations from the
            // PRE-wedge window. Otherwise the DEADBEEF feedback loop
            // at pc=0x14C4/14CC trivially fills the ring with its own
            // poison→branch pattern, which we already know about.
            if (u_core.retired_instr[31:26] == 6'h04 ||
                u_core.retired_instr[31:26] == 6'h05) begin
                dbg_branches <= dbg_branches + 1;
            end
            if ((u_core.retired_instr[31:26] == 6'h04 ||
                 u_core.retired_instr[31:26] == 6'h05) &&
                !entry14_captured &&
                mmio_correl_count < MMIO_CORREL_DEPTH) begin
                // rs = instr[25:21], rt = instr[20:16]. Check each.
                if (r_pending_valid[u_core.retired_instr[25:21]]) begin
                    mmio_correl_load_cyc[mmio_correl_count]   <= r_pending_cycle[u_core.retired_instr[25:21]];
                    mmio_correl_load_pc[mmio_correl_count]    <= r_pending_load_pc[u_core.retired_instr[25:21]];
                    mmio_correl_load_ea[mmio_correl_count]    <= r_pending_load_ea[u_core.retired_instr[25:21]];
                    mmio_correl_load_data[mmio_correl_count]  <= r_pending_load_data[u_core.retired_instr[25:21]];
                    mmio_correl_reg[mmio_correl_count]        <= u_core.retired_instr[25:21];
                    mmio_correl_br_cyc[mmio_correl_count]     <= sim_cycle_ctr;
                    mmio_correl_br_pc[mmio_correl_count]      <= u_core.retired_pc;
                    mmio_correl_br_instr[mmio_correl_count]   <= u_core.retired_instr;
                    mmio_correl_br_rs_val[mmio_correl_count]  <= u_core.regfile[u_core.retired_instr[25:21]];
                    mmio_correl_br_rt_val[mmio_correl_count]  <= u_core.regfile[u_core.retired_instr[20:16]];
                    mmio_correl_age[mmio_correl_count]        <= r_pending_age[u_core.retired_instr[25:21]];
                    mmio_correl_count                         <= mmio_correl_count + 1;
                    r_pending_valid[u_core.retired_instr[25:21]] <= 1'b0;
                    $display("[tb_ee_core_bios_smoke] MMIO_CORREL[%0d] load cyc=%0d pc=0x%08h EA=0x%08h data=0x%08h rt=$%0d -> branch cyc=%0d pc=0x%08h instr=0x%08h rs($%0d)=0x%08h rt($%0d)=0x%08h age=%0d",
                             mmio_correl_count, r_pending_cycle[u_core.retired_instr[25:21]],
                             r_pending_load_pc[u_core.retired_instr[25:21]],
                             r_pending_load_ea[u_core.retired_instr[25:21]],
                             r_pending_load_data[u_core.retired_instr[25:21]],
                             u_core.retired_instr[25:21],
                             sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr,
                             u_core.retired_instr[25:21], u_core.regfile[u_core.retired_instr[25:21]],
                             u_core.retired_instr[20:16], u_core.regfile[u_core.retired_instr[20:16]],
                             r_pending_age[u_core.retired_instr[25:21]]);
                end else if (r_pending_valid[u_core.retired_instr[20:16]] &&
                             mmio_correl_count < MMIO_CORREL_DEPTH) begin
                    mmio_correl_load_cyc[mmio_correl_count]   <= r_pending_cycle[u_core.retired_instr[20:16]];
                    mmio_correl_load_pc[mmio_correl_count]    <= r_pending_load_pc[u_core.retired_instr[20:16]];
                    mmio_correl_load_ea[mmio_correl_count]    <= r_pending_load_ea[u_core.retired_instr[20:16]];
                    mmio_correl_load_data[mmio_correl_count]  <= r_pending_load_data[u_core.retired_instr[20:16]];
                    mmio_correl_reg[mmio_correl_count]        <= u_core.retired_instr[20:16];
                    mmio_correl_br_cyc[mmio_correl_count]     <= sim_cycle_ctr;
                    mmio_correl_br_pc[mmio_correl_count]      <= u_core.retired_pc;
                    mmio_correl_br_instr[mmio_correl_count]   <= u_core.retired_instr;
                    mmio_correl_br_rs_val[mmio_correl_count]  <= u_core.regfile[u_core.retired_instr[25:21]];
                    mmio_correl_br_rt_val[mmio_correl_count]  <= u_core.regfile[u_core.retired_instr[20:16]];
                    mmio_correl_age[mmio_correl_count]        <= r_pending_age[u_core.retired_instr[20:16]];
                    mmio_correl_count                         <= mmio_correl_count + 1;
                    r_pending_valid[u_core.retired_instr[20:16]] <= 1'b0;
                    $display("[tb_ee_core_bios_smoke] MMIO_CORREL[%0d] load cyc=%0d pc=0x%08h EA=0x%08h data=0x%08h rt=$%0d -> branch cyc=%0d pc=0x%08h instr=0x%08h rs($%0d)=0x%08h rt($%0d)=0x%08h age=%0d",
                             mmio_correl_count, r_pending_cycle[u_core.retired_instr[20:16]],
                             r_pending_load_pc[u_core.retired_instr[20:16]],
                             r_pending_load_ea[u_core.retired_instr[20:16]],
                             r_pending_load_data[u_core.retired_instr[20:16]],
                             u_core.retired_instr[20:16],
                             sim_cycle_ctr, u_core.retired_pc, u_core.retired_instr,
                             u_core.retired_instr[25:21], u_core.regfile[u_core.retired_instr[25:21]],
                             u_core.retired_instr[20:16], u_core.regfile[u_core.retired_instr[20:16]],
                             r_pending_age[u_core.retired_instr[20:16]]);
                end
            end
        end
    end

    // Chapter 13-B: first transition of $6 / $2 / $5 into the phys-word
    // containing the poison RAM address. One fire per register. Uses
    // phys[28:2] so kseg0/kseg1/useg aliases of the same RAM word all
    // match. The $5 capture is expected to NEVER fire (sentinel value
    // lives at 0x974, phys-word 0x025D) — its absence is itself
    // diagnostic.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r6_ptr_captured <= 1'b0;
            r2_ptr_captured <= 1'b0;
            r5_ptr_captured <= 1'b0;
        end else begin
            if (!r6_ptr_captured &&
                u_core.regfile[6][28:2] == POINTER_PHYS_WORD &&
                r6_prev_1[28:2]         != POINTER_PHYS_WORD) begin
                r6_ptr_captured      <= 1'b1;
                r6_ptr_cycle         <= sim_cycle_ctr;
                r6_ptr_prev          <= r6_prev_1;
                r6_ptr_new           <= u_core.regfile[6];
                r6_ptr_retired_pc    <= u_core.retired_pc;
                r6_ptr_retired_instr <= u_core.retired_instr;
                r6_ptr_retired_arg2  <= u_core.retired_arg2;
                r6_ptr_retired_arg3  <= u_core.retired_arg3;
                $display("[tb_ee_core_bios_smoke] R6_PTR cycle=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         sim_cycle_ctr, r6_prev_1, u_core.regfile[6],
                         u_core.retired_pc, u_core.retired_instr,
                         u_core.retired_arg2, u_core.retired_arg3);
            end
            if (!r2_ptr_captured &&
                u_core.regfile[2][28:2] == POINTER_PHYS_WORD &&
                r2_prev_1[28:2]         != POINTER_PHYS_WORD) begin
                r2_ptr_captured      <= 1'b1;
                r2_ptr_cycle         <= sim_cycle_ctr;
                r2_ptr_prev          <= r2_prev_1;
                r2_ptr_new           <= u_core.regfile[2];
                r2_ptr_retired_pc    <= u_core.retired_pc;
                r2_ptr_retired_instr <= u_core.retired_instr;
                r2_ptr_retired_arg2  <= u_core.retired_arg2;
                r2_ptr_retired_arg3  <= u_core.retired_arg3;
                $display("[tb_ee_core_bios_smoke] R2_PTR cycle=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         sim_cycle_ctr, r2_prev_1, u_core.regfile[2],
                         u_core.retired_pc, u_core.retired_instr,
                         u_core.retired_arg2, u_core.retired_arg3);
            end
            if (!r5_ptr_captured &&
                u_core.regfile[5][28:2] == POINTER_PHYS_WORD &&
                r5_prev_1[28:2]         != POINTER_PHYS_WORD) begin
                r5_ptr_captured      <= 1'b1;
                r5_ptr_cycle         <= sim_cycle_ctr;
                r5_ptr_prev          <= r5_prev_1;
                r5_ptr_new           <= u_core.regfile[5];
                r5_ptr_retired_pc    <= u_core.retired_pc;
                r5_ptr_retired_instr <= u_core.retired_instr;
                r5_ptr_retired_arg2  <= u_core.retired_arg2;
                r5_ptr_retired_arg3  <= u_core.retired_arg3;
                $display("[tb_ee_core_bios_smoke] R5_PTR cycle=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                         sim_cycle_ctr, r5_prev_1, u_core.regfile[5],
                         u_core.retired_pc, u_core.retired_instr,
                         u_core.retired_arg2, u_core.retired_arg3);
            end
        end
    end

    // Chapter 14 — $8 exact-match dispatch-target install watcher.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r8_ex_captured <= 1'b0;
        end else if (!r8_ex_captured &&
                     u_core.regfile[8] == DISPATCH_TARGET &&
                     r8_prev_1         != DISPATCH_TARGET) begin
            r8_ex_captured          <= 1'b1;
            r8_ex_cycle             <= sim_cycle_ctr;
            r8_ex_prev              <= r8_prev_1;
            r8_ex_new               <= u_core.regfile[8];
            r8_ex_retired_pc_now    <= u_core.retired_pc;
            r8_ex_retired_instr_now <= u_core.retired_instr;
            r8_ex_retired_arg2_now  <= u_core.retired_arg2;
            r8_ex_retired_arg3_now  <= u_core.retired_arg3;
            r8_ex_retired_pc_d1     <= retired_pc_d1;
            r8_ex_retired_instr_d1  <= retired_instr_d1;
            r8_ex_retired_arg2_d1   <= retired_arg2_d1;
            r8_ex_retired_arg3_d1   <= retired_arg3_d1;
            r8_ex_rs_idx            <= u_core.retired_instr[25:21];
            r8_ex_rt_idx            <= u_core.retired_instr[20:16];
            r8_ex_rs_val            <= u_core.regfile[u_core.retired_instr[25:21]];
            r8_ex_rt_val            <= u_core.regfile[u_core.retired_instr[20:16]];
            $display("[tb_ee_core_bios_smoke] R8_INSTALL cycle=%0d prev=0x%08h new=0x%08h (dispatch target install)",
                     sim_cycle_ctr, r8_prev_1, u_core.regfile[8]);
            $display("[tb_ee_core_bios_smoke] R8_INSTALL   now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.retired_arg2, u_core.retired_arg3);
            $display("[tb_ee_core_bios_smoke] R8_INSTALL   d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     retired_pc_d1, retired_instr_d1,
                     retired_arg2_d1, retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] R8_INSTALL   src decoded rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     u_core.retired_instr[25:21], u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.regfile[u_core.retired_instr[20:16]]);
        end
    end

    // Chapter 24 — $8 first-write observer for DISPATCH_BASE = 0x0EB4.
    // One level upstream of ch14's 0x1478 observer. Same shape:
    // exact-match transition from prev!=0xEB4 to new==0xEB4, plus
    // retired_pc/instr/arg2/arg3 at current and one-edge-delayed
    // snapshots, plus decoded rs/rt live values.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r8_base_captured <= 1'b0;
        end else if (!r8_base_captured &&
                     u_core.regfile[8] == DISPATCH_BASE &&
                     r8_prev_1         != DISPATCH_BASE) begin
            r8_base_captured          <= 1'b1;
            r8_base_cycle             <= sim_cycle_ctr;
            r8_base_prev              <= r8_prev_1;
            r8_base_new               <= u_core.regfile[8];
            r8_base_retired_pc_now    <= u_core.retired_pc;
            r8_base_retired_instr_now <= u_core.retired_instr;
            r8_base_retired_arg2_now  <= u_core.retired_arg2;
            r8_base_retired_arg3_now  <= u_core.retired_arg3;
            r8_base_retired_pc_d1     <= retired_pc_d1;
            r8_base_retired_instr_d1  <= retired_instr_d1;
            r8_base_retired_arg2_d1   <= retired_arg2_d1;
            r8_base_retired_arg3_d1   <= retired_arg3_d1;
            r8_base_rs_idx            <= u_core.retired_instr[25:21];
            r8_base_rt_idx            <= u_core.retired_instr[20:16];
            r8_base_rs_val            <= u_core.regfile[u_core.retired_instr[25:21]];
            r8_base_rt_val            <= u_core.regfile[u_core.retired_instr[20:16]];
            $display("[tb_ee_core_bios_smoke] R8_BASE cycle=%0d prev=0x%08h new=0x%08h (dispatch base install)",
                     sim_cycle_ctr, r8_prev_1, u_core.regfile[8]);
            $display("[tb_ee_core_bios_smoke] R8_BASE   now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.retired_arg2, u_core.retired_arg3);
            $display("[tb_ee_core_bios_smoke] R8_BASE   d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     retired_pc_d1, retired_instr_d1,
                     retired_arg2_d1, retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] R8_BASE   src decoded rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     u_core.retired_instr[25:21], u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.regfile[u_core.retired_instr[20:16]]);
            // Chapter 25: peek the array at RAM[0xE54..0xEB4] at the
            // moment R8_BASE fires. peek_instr goes through the RAM
            // backing store (ee_ram_stub.mem via hierarchical ref), so
            // this is a simulation-only read that reflects current
            // RAM contents. Dumps 25 words — the full indexed array
            // up through the selected element at 0xEB4.
            $display("[tb_ee_core_bios_smoke] R8_BASE   array_peek RAM[0x0E54..0x0EB4] (25 words, @cyc=%0d):",
                     sim_cycle_ctr);
            for (int k = 0; k < 25; k++) begin
                $display("[tb_ee_core_bios_smoke] R8_BASE     [0x%04h] = 0x%08h",
                         32'h0E54 + k*4,
                         peek_instr(32'h0000_0E54 + k*4));
            end
        end
    end

    // Chapter 26 — $9 first-write observer for DISPATCH_INDEX (0x18).
    // Same exact-match shape as ch14/ch24. Captures the instruction
    // that first selected index 24 — the choice-point for the
    // dispatch formula base+index*4+0x5C4 = 0x1478.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r9_ex_captured <= 1'b0;
        end else if (!r9_ex_captured &&
                     u_core.regfile[9] == DISPATCH_INDEX &&
                     r9_prev_1         != DISPATCH_INDEX) begin
            r9_ex_captured          <= 1'b1;
            r9_ex_cycle             <= sim_cycle_ctr;
            r9_ex_prev              <= r9_prev_1;
            r9_ex_new               <= u_core.regfile[9];
            r9_ex_retired_pc_now    <= u_core.retired_pc;
            r9_ex_retired_instr_now <= u_core.retired_instr;
            r9_ex_retired_arg2_now  <= u_core.retired_arg2;
            r9_ex_retired_arg3_now  <= u_core.retired_arg3;
            r9_ex_retired_pc_d1     <= retired_pc_d1;
            r9_ex_retired_instr_d1  <= retired_instr_d1;
            r9_ex_retired_arg2_d1   <= retired_arg2_d1;
            r9_ex_retired_arg3_d1   <= retired_arg3_d1;
            r9_ex_rs_idx            <= u_core.retired_instr[25:21];
            r9_ex_rt_idx            <= u_core.retired_instr[20:16];
            r9_ex_rs_val            <= u_core.regfile[u_core.retired_instr[25:21]];
            r9_ex_rt_val            <= u_core.regfile[u_core.retired_instr[20:16]];
            $display("[tb_ee_core_bios_smoke] R9_INDEX cycle=%0d prev=0x%08h new=0x%08h (dispatch index selection)",
                     sim_cycle_ctr, r9_prev_1, u_core.regfile[9]);
            $display("[tb_ee_core_bios_smoke] R9_INDEX   now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.retired_arg2, u_core.retired_arg3);
            $display("[tb_ee_core_bios_smoke] R9_INDEX   d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     retired_pc_d1, retired_instr_d1,
                     retired_arg2_d1, retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] R9_INDEX   src decoded rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     u_core.retired_instr[25:21], u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.regfile[u_core.retired_instr[20:16]]);
        end
    end

    // Chapter 27 — $2 setup-window write tracker. Change-detector on
    // regfile[2] vs r2_prev_1, gated to the dispatch-setup window
    // (r8_base_captured && !r8_ex_captured), filtered to exclude the
    // known 0xEE0 scan-body at R2_LOOP_PC = 0x0EE8. The scan
    // mechanically increments $2 by 4; it's not the interesting
    // write. Any OTHER change to $2 in this window is a candidate
    // legitimate-setup write the list-walker might have been
    // designed to consume.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r2_setup_count <= 0;
        end else if (r8_base_captured && !r8_ex_captured &&
                     u_core.regfile[2] != r2_prev_1 &&
                     u_core.retired_pc != R2_LOOP_PC &&
                     r2_setup_count < R2_SETUP_DEPTH) begin
            r2_setup_cyc[r2_setup_count]   <= sim_cycle_ctr;
            r2_setup_pc[r2_setup_count]    <= u_core.retired_pc;
            r2_setup_instr[r2_setup_count] <= u_core.retired_instr;
            r2_setup_prev[r2_setup_count]  <= r2_prev_1;
            r2_setup_new[r2_setup_count]   <= u_core.regfile[2];
            r2_setup_arg2[r2_setup_count]  <= u_core.retired_arg2;
            r2_setup_arg3[r2_setup_count]  <= u_core.retired_arg3;
            r2_setup_kind[r2_setup_count]  <= r6w_kind;  // reuse ch22 classifier
            r2_setup_count                 <= r2_setup_count + 1;
            $display("[tb_ee_core_bios_smoke] R2_SETUP[%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h kind=%s arg2=0x%08h arg3=0x%08h",
                     r2_setup_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     r2_prev_1, u_core.regfile[2],
                     (r6w_kind == 0) ? "LUI"    :
                     (r6w_kind == 1) ? "ADDI_U":
                     (r6w_kind == 2) ? "LOAD"   :
                     (r6w_kind == 3) ? "ALU-R"  :
                                       "OTHER",
                     u_core.retired_arg2, u_core.retired_arg3);
        end
    end

    // Chapter 28 — SCAN_FINAL_EXIT. First retire at pc=SCAN_EXIT_TARGET
    // (0x00006A70 — the unambiguous J target from pc=0x0EF4). Captures
    // $k0 ($26), $k1 ($27), $2, plus retire-ring head-1 (the delay
    // slot of the J) and head-2 (the J itself at pc=0x0EF4).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            scan_final_captured <= 1'b0;
        end else if (!scan_final_captured && core_ev_valid &&
                     u_core.retired_pc == SCAN_EXIT_TARGET) begin
            scan_final_captured         <= 1'b1;
            scan_final_cycle            <= sim_cycle_ctr;
            scan_final_pc               <= u_core.retired_pc;
            scan_final_k0               <= u_core.regfile[26];
            scan_final_k1               <= u_core.regfile[27];
            scan_final_r2               <= u_core.regfile[2];
            scan_final_ring_prev_pc     <= retire_ring_pc[ring_prev_idx];
            scan_final_ring_prev_instr  <= retire_ring_instr[ring_prev_idx];
            scan_final_ring_prev2_pc    <= retire_ring_pc[ring_prev2_idx];
            scan_final_ring_prev2_instr <= retire_ring_instr[ring_prev2_idx];
            $display("[tb_ee_core_bios_smoke] SCAN_FINAL_EXIT cyc=%0d pc=0x%08h $k0=0x%08h $k1=0x%08h $2=0x%08h",
                     sim_cycle_ctr, u_core.retired_pc,
                     u_core.regfile[26], u_core.regfile[27],
                     u_core.regfile[2]);
            $display("[tb_ee_core_bios_smoke] SCAN_FINAL_EXIT   ring-1 pc=0x%08h instr=0x%08h (delay slot)",
                     retire_ring_pc[ring_prev_idx],
                     retire_ring_instr[ring_prev_idx]);
            $display("[tb_ee_core_bios_smoke] SCAN_FINAL_EXIT   ring-2 pc=0x%08h instr=0x%08h (the J to 0x6A70)",
                     retire_ring_pc[ring_prev2_idx],
                     retire_ring_instr[ring_prev2_idx]);
        end
    end

    // Chapter 28 — R2_GAP: close ch27's scope gap. Observer covers the
    // small [R9_INDEX, R8_BASE] window (~24 cycles) that ch27
    // intentionally did not. Same filter (exclude pc=0x0EE8) and
    // change-detector as ch27.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            r2_gap_count <= 0;
        end else if (r9_ex_captured && !r8_base_captured &&
                     u_core.regfile[2] != r2_prev_1 &&
                     u_core.retired_pc != R2_LOOP_PC &&
                     r2_gap_count < 4) begin
            r2_gap_cyc[r2_gap_count]   <= sim_cycle_ctr;
            r2_gap_pc[r2_gap_count]    <= u_core.retired_pc;
            r2_gap_instr[r2_gap_count] <= u_core.retired_instr;
            r2_gap_prev[r2_gap_count]  <= r2_prev_1;
            r2_gap_new[r2_gap_count]   <= u_core.regfile[2];
            r2_gap_count               <= r2_gap_count + 1;
            $display("[tb_ee_core_bios_smoke] R2_GAP[%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h",
                     r2_gap_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     r2_prev_1, u_core.regfile[2]);
        end
    end

    // Chapter 29 — SCAN_LAST_EXIT ring. Fires on every retire at
    // pc=0x6A70 (the scan exit target) before ENTRY14. Circular
    // depth-8 ring so the latest 8 exits are always resident —
    // newest entry = the exit whose $2 most closely precedes
    // ENTRY14.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            scan_last_total <= 0;
            scan_last_head  <= 0;
        end else if (core_ev_valid && !entry14_captured &&
                     u_core.retired_pc == SCAN_EXIT_TARGET) begin
            scan_last_cyc[scan_last_head]             <= sim_cycle_ctr;
            scan_last_k0[scan_last_head]              <= u_core.regfile[26];
            scan_last_k1[scan_last_head]              <= u_core.regfile[27];
            scan_last_r2[scan_last_head]              <= u_core.regfile[2];
            scan_last_ring_prev_pc[scan_last_head]    <= retire_ring_pc[ring_prev_idx];
            scan_last_ring_prev_instr[scan_last_head] <= retire_ring_instr[ring_prev_idx];
            scan_last_head  <= (scan_last_head + 1) % SCAN_EXIT_RING_DEPTH;
            scan_last_total <= scan_last_total + 1;
            $display("[tb_ee_core_bios_smoke] SCAN_LAST_EXIT#%0d cyc=%0d $k0=0x%08h $k1=0x%08h $2=0x%08h",
                     scan_last_total, sim_cycle_ctr,
                     u_core.regfile[26], u_core.regfile[27],
                     u_core.regfile[2]);
        end
    end

    // Chapter 30 — $k1 bound-origin observer for K1_BOUND_VALUE (0x2D24).
    // Same exact-match shape as ch14/ch24/ch26.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            k1_ex_captured <= 1'b0;
        end else if (!k1_ex_captured &&
                     u_core.regfile[27] == K1_BOUND_VALUE &&
                     k1_prev_1          != K1_BOUND_VALUE) begin
            k1_ex_captured          <= 1'b1;
            k1_ex_cycle             <= sim_cycle_ctr;
            k1_ex_prev              <= k1_prev_1;
            k1_ex_new               <= u_core.regfile[27];
            k1_ex_retired_pc_now    <= u_core.retired_pc;
            k1_ex_retired_instr_now <= u_core.retired_instr;
            k1_ex_retired_arg2_now  <= u_core.retired_arg2;
            k1_ex_retired_arg3_now  <= u_core.retired_arg3;
            k1_ex_retired_pc_d1     <= retired_pc_d1;
            k1_ex_retired_instr_d1  <= retired_instr_d1;
            k1_ex_retired_arg2_d1   <= retired_arg2_d1;
            k1_ex_retired_arg3_d1   <= retired_arg3_d1;
            k1_ex_rs_idx            <= u_core.retired_instr[25:21];
            k1_ex_rt_idx            <= u_core.retired_instr[20:16];
            k1_ex_rs_val            <= u_core.regfile[u_core.retired_instr[25:21]];
            k1_ex_rt_val            <= u_core.regfile[u_core.retired_instr[20:16]];
            $display("[tb_ee_core_bios_smoke] K1_BOUND cycle=%0d prev=0x%08h new=0x%08h (scan #1 bound install)",
                     sim_cycle_ctr, k1_prev_1, u_core.regfile[27]);
            $display("[tb_ee_core_bios_smoke] K1_BOUND   now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.retired_arg2, u_core.retired_arg3);
            $display("[tb_ee_core_bios_smoke] K1_BOUND   d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     retired_pc_d1, retired_instr_d1,
                     retired_arg2_d1, retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] K1_BOUND   src decoded rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     u_core.retired_instr[25:21], u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.regfile[u_core.retired_instr[20:16]]);
        end
    end

    // Chapter 31 — LW-filtered prologue observer at pc=0x1478. Fires
    // only on retired loads (retired_flag_read) whose pc lies in the
    // list-walker body [0x1478, 0x14FF]. Captures base-register idx
    // and live value so we can tell whether the routine self-loads
    // from $gp/$sp/$4 (own input slot) or reads via $6 (inherited
    // scan residual).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            lw_prologue_count <= 0;
        end else if (core_ev_valid &&
                     u_core.retired_flag_read &&
                     u_core.retired_pc >= LW_PROLOGUE_LOW &&
                     u_core.retired_pc <= LW_PROLOGUE_HIGH &&
                     lw_prologue_count < LW_PROLOGUE_DEPTH) begin
            lw_prologue_cyc[lw_prologue_count]    <= sim_cycle_ctr;
            lw_prologue_pc[lw_prologue_count]     <= u_core.retired_pc;
            lw_prologue_instr[lw_prologue_count]  <= u_core.retired_instr;
            lw_prologue_rs[lw_prologue_count]     <= u_core.retired_instr[25:21];
            lw_prologue_rt[lw_prologue_count]     <= u_core.retired_instr[20:16];
            lw_prologue_rs_val[lw_prologue_count] <= u_core.regfile[u_core.retired_instr[25:21]];
            lw_prologue_ea[lw_prologue_count]     <= u_core.retired_arg2;
            lw_prologue_data[lw_prologue_count]   <= u_core.retired_arg3;
            lw_prologue_count                     <= lw_prologue_count + 1;
            $display("[tb_ee_core_bios_smoke] LW_PROLOGUE[%0d] cyc=%0d pc=0x%08h instr=0x%08h rs=$%0d rt=$%0d rs_val=0x%08h EA=0x%08h data=0x%08h",
                     lw_prologue_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     u_core.retired_instr[25:21],
                     u_core.retired_instr[20:16],
                     u_core.regfile[u_core.retired_instr[25:21]],
                     u_core.retired_arg2, u_core.retired_arg3);
        end
    end

    // Chapter 32 — TLB/COP0 write observer. Fires on retire of
    // MTC0, TLBR, TLBWI, TLBWR, TLBP. Gated on !entry14_captured
    // so only pre-wedge instructions land in the ring.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            tlb_cop0_count <= 0;
        end else if (core_ev_valid && !entry14_captured &&
                     tlbc_fire &&
                     tlb_cop0_count < TLB_COP0_DEPTH) begin
            tlb_cop0_cyc[tlb_cop0_count]   <= sim_cycle_ctr;
            tlb_cop0_pc[tlb_cop0_count]    <= u_core.retired_pc;
            tlb_cop0_instr[tlb_cop0_count] <= u_core.retired_instr;
            tlb_cop0_rt[tlb_cop0_count]    <= u_core.retired_instr[20:16];
            tlb_cop0_rd[tlb_cop0_count]    <= u_core.retired_instr[15:11];
            // For MTC0, retired_arg3 contains rt_val (the written value)
            // per ee_core_stub.sv line 747. For TLB ops, arg3 is 0.
            tlb_cop0_value[tlb_cop0_count] <= u_core.retired_arg3;
            tlb_cop0_kind[tlb_cop0_count]  <= tlbc_kind;
            tlb_cop0_count                 <= tlb_cop0_count + 1;
            $display("[tb_ee_core_bios_smoke] TLB_COP0[%0d] cyc=%0d pc=0x%08h instr=0x%08h kind=%s rt=$%0d rd=cop0[%0d] value=0x%08h",
                     tlb_cop0_count, sim_cycle_ctr,
                     u_core.retired_pc, u_core.retired_instr,
                     (tlbc_kind == 0) ? "MTC0"  :
                     (tlbc_kind == 1) ? "TLBR"  :
                     (tlbc_kind == 2) ? "TLBWI" :
                     (tlbc_kind == 3) ? "TLBWR" :
                     (tlbc_kind == 4) ? "TLBP"  : "?",
                     u_core.retired_instr[20:16],
                     u_core.retired_instr[15:11],
                     u_core.retired_arg3);
        end
    end

    always_ff @(posedge clk) begin
        if (rst_n && ee_map_ev_valid &&
            ee_map_ev_subsys == trace_pkg::SUBSYS_MEM &&
            ee_map_ev_event  == trace_pkg::EV_UNMAPPED) begin
            if (unmapped_count < UNMAPPED_DEPTH) begin
                unmapped_cycle[unmapped_count] <= sim_cycle_ctr;
                unmapped_pc[unmapped_count]    <= core_pc;
                unmapped_is_wr[unmapped_count] <= (ee_map_ev_flags & 32'h1) != 32'd0;
                unmapped_addr[unmapped_count]  <= ee_map_ev_arg0[31:0];
                unmapped_data[unmapped_count]  <= ee_map_ev_arg1[31:0];
                $display("[tb_ee_core_bios_smoke] UNMAPPED[%0d] cycle=%0d pc=0x%08h %s addr=0x%08h data=0x%08h",
                         unmapped_count, sim_cycle_ctr, core_pc,
                         ((ee_map_ev_flags & 32'h1) != 32'd0) ? "WR" : "RD",
                         ee_map_ev_arg0[31:0], ee_map_ev_arg1[31:0]);
            end
            unmapped_count <= unmapped_count + 1;

            // Chapter 10: capture lineage for the FIRST unmapped READ.
            // This is the poisoning event — its returned DEADBEEF feeds
            // the self-perpetuating list walk. Snapshot everything we
            // can see at the exact edge the bad read fires, separate
            // from the write-side noise the depth-bounded arrays hold.
            // live_* are combinational peeks defined at module scope
            // (iverilog 12 rejects automatic vars inside always_ff).
            if (!lineage_captured && (ee_map_ev_flags & 32'h1) == 32'd0) begin
                lineage_captured       <= 1'b1;
                lineage_cycle          <= sim_cycle_ctr;
                lineage_pc             <= core_pc;
                lineage_instr          <= live_pc_instr;
                lineage_opcode         <= live_opcode;
                lineage_rs             <= live_rs_idx;
                lineage_rt             <= live_rt_idx;
                lineage_rd             <= live_rd_idx;
                lineage_imm            <= live_imm;
                lineage_rs_val         <= live_rs_val;
                lineage_rt_val         <= live_rt_val;
                lineage_rd_val         <= live_rd_val;
                lineage_effective_addr <= live_ea;
                lineage_poison_addr    <= ee_map_ev_arg0[31:0];
                lineage_poison_data    <= ee_map_ev_arg1[31:0];

                $display("[tb_ee_core_bios_smoke] LINEAGE first-unmapped-RD cycle=%0d pc=0x%08h instr=0x%08h",
                         sim_cycle_ctr, core_pc, live_pc_instr);
                $display("[tb_ee_core_bios_smoke] LINEAGE   decode op=0x%02h rs=$%0d rt=$%0d rd=$%0d imm=0x%04h",
                         live_opcode, live_rs_idx, live_rt_idx, live_rd_idx,
                         live_imm);
                $display("[tb_ee_core_bios_smoke] LINEAGE   rs($%0d)=0x%08h rt($%0d)=0x%08h rd($%0d)=0x%08h",
                         live_rs_idx, live_rs_val, live_rt_idx, live_rt_val,
                         live_rd_idx, live_rd_val);
                $display("[tb_ee_core_bios_smoke] LINEAGE   EA = rs + sx(imm) = 0x%08h (poisoned read addr=0x%08h, data=0x%08h)",
                         live_ea, ee_map_ev_arg0[31:0], ee_map_ev_arg1[31:0]);
                $display("[tb_ee_core_bios_smoke] LINEAGE   retire ring (newest first, up to %0d entries):",
                         RETIRE_RING_DEPTH);
                for (int k = 0; k < RETIRE_RING_DEPTH; k++) begin
                    int idx;
                    // Walk backward from head-1; head was the NEXT slot
                    // to write, so head-1 is the most recent. Bound
                    // iteration by retire_ring_count so we don't print
                    // the zero-init slots if fewer retires have fired.
                    if (k < retire_ring_count) begin
                        idx = (retire_ring_head - 1 - k + RETIRE_RING_DEPTH)
                              % RETIRE_RING_DEPTH;
                        $display("[tb_ee_core_bios_smoke] LINEAGE     ring[-%0d] cyc=%0d pc=0x%08h instr=0x%08h $2=0x%08h $6=0x%08h",
                                 k, retire_ring_cyc[idx], retire_ring_pc[idx],
                                 retire_ring_instr[idx], retire_ring_r2[idx],
                                 retire_ring_r6[idx]);
                    end
                end
            end
        end
    end

    // Opcode histogram (supported retires before trap) — useful when a
    // real BIOS is loaded, to see which ops the core is actually
    // executing before it hits the first unsupported one.
    int op_count_lui;
    int op_count_ori;
    int op_count_addiu;
    int op_count_sw;
    int op_count_lw;
    int op_count_j;
    int op_count_other;

    // Ch194 — opcode-coverage audit. The legacy histogram above buckets
    // only six opcodes; everything else falls into op_count_other, which
    // on a real-BIOS run is ~2.5M instructions of opaque "stuff". The
    // arrays below break that bucket down by top-level opcode (op[31:26]),
    // by SPECIAL funct when op=0, and by REGIMM rt when op=1, so we can
    // see exactly which instruction families the BIOS is exercising.
    // ch194_silent_lat mirrors the core's is_unsupported predicate at
    // each retire — with STRICT_UNSUPPORTED=1 the core is supposed to
    // trap on any unsupported encoding, so ch194_silent_total is
    // expected to be 0 on a clean run; nonzero would be a decode bug.
    int   ch194_retire_total;
    int   ch194_silent_total;
    int   ch194_op_hist     [0:63];
    int   ch194_spec_hist   [0:63];
    int   ch194_regimm_hist [0:31];
    logic ch194_silent_lat;

    initial begin
        core_retired_events   = 0;
        core_halt_events      = 0;
        core_trap_events      = 0;
        core_exception_events = 0;
        dmac_done_events      = 0;
        bios_reads            = 0;
        op_count_lui          = 0;
        op_count_ori          = 0;
        op_count_addiu        = 0;
        op_count_sw           = 0;
        op_count_lw           = 0;
        op_count_j            = 0;
        op_count_other        = 0;
        errors                = 0;
        ch194_retire_total    = 0;
        ch194_silent_total    = 0;
        ch194_silent_lat      = 1'b0;
        for (int ci = 0; ci < 64; ci++) begin
            ch194_op_hist[ci]   = 0;
            ch194_spec_hist[ci] = 0;
        end
        for (int ci = 0; ci < 32; ci++)
            ch194_regimm_hist[ci] = 0;
    end

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid) begin
            core_retired_events <= core_retired_events + 1;
            if ((core_ev_flags & 32'h08) != 32'd0)
                core_halt_events <= core_halt_events + 1;
            if ((core_ev_flags & 32'h20) != 32'd0)
                core_exception_events <= core_exception_events + 1;
            if ((core_ev_flags & 32'h80) != 32'd0)
                core_trap_events <= core_trap_events + 1;

            if ((core_ev_flags & 32'h80) == 32'd0) begin
                case (core_ev_arg1[31:26])
                    OP_LUI:   op_count_lui   <= op_count_lui   + 1;
                    OP_ORI:   op_count_ori   <= op_count_ori   + 1;
                    OP_ADDIU: op_count_addiu <= op_count_addiu + 1;
                    OP_SW:    op_count_sw    <= op_count_sw    + 1;
                    OP_LW:    op_count_lw    <= op_count_lw    + 1;
                    OP_J:     op_count_j     <= op_count_j     + 1;
                    default:  op_count_other <= op_count_other + 1;
                endcase

                // Ch194 — extended per-opcode audit (alongside, not
                // replacing, the six-bucket legacy hist above).
                ch194_retire_total <= ch194_retire_total + 1;
                ch194_op_hist[core_ev_arg1[31:26]]
                    <= ch194_op_hist[core_ev_arg1[31:26]] + 1;
                if (core_ev_arg1[31:26] == 6'h00)
                    ch194_spec_hist[core_ev_arg1[5:0]]
                        <= ch194_spec_hist[core_ev_arg1[5:0]] + 1;
                if (core_ev_arg1[31:26] == 6'h01)
                    ch194_regimm_hist[core_ev_arg1[20:16]]
                        <= ch194_regimm_hist[core_ev_arg1[20:16]] + 1;
                if (ch194_silent_lat)
                    ch194_silent_total <= ch194_silent_total + 1;
            end
        end

        if (rst_n && dmac_ev_valid &&
            dmac_ev_subsys == trace_pkg::SUBSYS_DMAC &&
            dmac_ev_event  == trace_pkg::EV_DMA_DONE)
            dmac_done_events <= dmac_done_events + 1;

        if (rst_n && ee_map_ev_valid &&
            ee_map_ev_subsys == trace_pkg::SUBSYS_MEM &&
            ee_map_ev_event  == trace_pkg::EV_READ &&
            ee_map_ev_arg3[7:0] == 8'd0)                   // REGION_BIOS
            bios_reads <= bios_reads + 1;
    end

    // Ch194 — sample is_unsupported in lock-step with retire_pulse so
    // that when core_ev_valid arrives one cycle later, we can attribute
    // the silent-NOP property to the instruction that actually retired.
    // The latched flag feeds ch194_silent_total above.
    always_ff @(posedge clk) begin
        if (!rst_n)
            ch194_silent_lat <= 1'b0;
        else if (u_core.retire_pulse)
            ch194_silent_lat <= u_core.is_unsupported;
    end

    // ------------------------------------------------------------------
    // Mnemonic helper (same coverage as tb_iop_core_bios_smoke so real-
    // BIOS runs read the same way on both sides).
    // ------------------------------------------------------------------

    function automatic string mips_mnemonic(input logic [31:0] word);
        logic [5:0] op, func;
        logic [4:0] rt_field;

        if (word == 32'd0) return "nop";

        op       = word[31:26];
        func     = word[5:0];
        rt_field = word[20:16];

        if (op == 6'h00) begin
            case (func)
                6'h00: return "sll";
                6'h02: return "srl";
                6'h03: return "sra";
                6'h04: return "sllv";
                6'h06: return "srlv";
                6'h07: return "srav";
                6'h08: return "jr";
                6'h09: return "jalr";
                6'h0C: return "syscall";
                6'h0D: return "break";
                6'h10: return "mfhi";
                6'h11: return "mthi";
                6'h12: return "mflo";
                6'h13: return "mtlo";
                6'h18: return "mult";
                6'h19: return "multu";
                6'h1A: return "div";
                6'h1B: return "divu";
                6'h20: return "add";
                6'h21: return "addu";
                6'h22: return "sub";
                6'h23: return "subu";
                6'h24: return "and";
                6'h25: return "or";
                6'h26: return "xor";
                6'h27: return "nor";
                6'h2A: return "slt";
                6'h2B: return "sltu";
                default: return $sformatf("SPECIAL?(%02h)", func);
            endcase
        end else if (op == 6'h01) begin
            case (rt_field)
                5'h00: return "bltz";
                5'h01: return "bgez";
                5'h10: return "bltzal";
                5'h11: return "bgezal";
                default: return $sformatf("REGIMM?(%02h)", rt_field);
            endcase
        end else if (op == 6'h10) begin
            case (word[25:21])
                5'h00: return "mfc0";
                5'h04: return "mtc0";
                5'h10: begin
                    if (func == 6'h10) return "rfe";
                    return $sformatf("COP0.CO?(func=%02h)", func);
                end
                default: return $sformatf("COP0?(rs=%02h)", word[25:21]);
            endcase
        end
        case (op)
            6'h02: return "j";
            6'h03: return "jal";
            6'h04: return "beq";
            6'h05: return "bne";
            6'h06: return "blez";
            6'h07: return "bgtz";
            6'h08: return "addi";
            6'h09: return "addiu";
            6'h0A: return "slti";
            6'h0B: return "sltiu";
            6'h0C: return "andi";
            6'h0D: return "ori";
            6'h0E: return "xori";
            6'h0F: return "lui";
            6'h20: return "lb";
            6'h21: return "lh";
            6'h22: return "lwl";
            6'h23: return "lw";
            6'h24: return "lbu";
            6'h25: return "lhu";
            6'h26: return "lwr";
            6'h28: return "sb";
            6'h29: return "sh";
            6'h2A: return "swl";
            6'h2B: return "sw";
            6'h2E: return "swr";
            6'h2F: return "cache";  // Ch178
            default: return $sformatf("OP?(%02h)", op);
        endcase
    endfunction

    // ------------------------------------------------------------------
    // Ch194 — opcode-coverage audit helpers. ch194_op_name returns the
    // canonical class name for the top-level opcode (op[31:26]); for
    // op=0/1 the real instruction is determined by funct/rt and is
    // separately bucketed. ch194_spec_funct_name and ch194_regimm_rt_name
    // resolve the sub-encodings. ch194_op_recognized reports whether
    // ee_core_stub will decode that top-level opcode at all (anything
    // unrecognized would have tripped STRICT_UNSUPPORTED before retire).
    // ------------------------------------------------------------------
    function automatic string ch194_op_name(input logic [5:0] op);
        case (op)
            6'h00: return "SPECIAL";
            6'h01: return "REGIMM";
            6'h02: return "j";
            6'h03: return "jal";
            6'h04: return "beq";
            6'h05: return "bne";
            6'h06: return "blez";
            6'h07: return "bgtz";
            6'h08: return "addi";
            6'h09: return "addiu";
            6'h0A: return "slti";
            6'h0B: return "sltiu";
            6'h0C: return "andi";
            6'h0D: return "ori";
            6'h0E: return "xori";
            6'h0F: return "lui";
            6'h10: return "COP0";
            6'h11: return "COP1";
            6'h12: return "COP2";
            6'h13: return "COP3";
            6'h14: return "beql";
            6'h15: return "bnel";
            6'h16: return "blezl";
            6'h17: return "bgtzl";
            6'h18: return "daddi";
            6'h19: return "daddiu";
            6'h1A: return "ldl";
            6'h1B: return "ldr";
            6'h1C: return "MMI";
            6'h1E: return "lq";
            6'h1F: return "sq";
            6'h20: return "lb";
            6'h21: return "lh";
            6'h22: return "lwl";
            6'h23: return "lw";
            6'h24: return "lbu";
            6'h25: return "lhu";
            6'h26: return "lwr";
            6'h27: return "lwu";
            6'h28: return "sb";
            6'h29: return "sh";
            6'h2A: return "swl";
            6'h2B: return "sw";
            6'h2C: return "sdl";
            6'h2D: return "sdr";
            6'h2E: return "swr";
            6'h2F: return "cache";
            6'h30: return "ll";
            6'h31: return "lwc1";
            6'h33: return "pref";
            6'h36: return "lqc2";
            6'h37: return "ld";
            6'h38: return "sc";
            6'h39: return "swc1";
            6'h3E: return "sqc2";
            6'h3F: return "sd";
            default: return $sformatf("op_0x%02h", op);
        endcase
    endfunction

    function automatic string ch194_spec_funct_name(input logic [5:0] f);
        case (f)
            6'h00: return "sll";
            6'h02: return "srl";
            6'h03: return "sra";
            6'h04: return "sllv";
            6'h06: return "srlv";
            6'h07: return "srav";
            6'h08: return "jr";
            6'h09: return "jalr";
            6'h0A: return "movz";
            6'h0B: return "movn";
            6'h0C: return "syscall";
            6'h0D: return "break";
            6'h0F: return "sync";
            6'h10: return "mfhi";
            6'h11: return "mthi";
            6'h12: return "mflo";
            6'h13: return "mtlo";
            6'h14: return "dsllv";
            6'h16: return "dsrlv";
            6'h17: return "dsrav";
            6'h18: return "mult";
            6'h19: return "multu";
            6'h1A: return "div";
            6'h1B: return "divu";
            6'h20: return "add";
            6'h21: return "addu";
            6'h22: return "sub";
            6'h23: return "subu";
            6'h24: return "and";
            6'h25: return "or";
            6'h26: return "xor";
            6'h27: return "nor";
            6'h28: return "mfsa";
            6'h29: return "mtsa";
            6'h2A: return "slt";
            6'h2B: return "sltu";
            6'h2C: return "dadd";
            6'h2D: return "daddu";
            6'h2E: return "dsub";
            6'h2F: return "dsubu";
            6'h34: return "teq";
            6'h38: return "dsll";
            6'h3A: return "dsrl";
            6'h3B: return "dsra";
            6'h3C: return "dsll32";
            6'h3E: return "dsrl32";
            6'h3F: return "dsra32";
            default: return $sformatf("func_0x%02h", f);
        endcase
    endfunction

    function automatic string ch194_regimm_rt_name(input logic [4:0] r);
        case (r)
            5'h00: return "bltz";
            5'h01: return "bgez";
            5'h02: return "bltzl";
            5'h03: return "bgezl";
            5'h10: return "bltzal";
            5'h11: return "bgezal";
            5'h12: return "bltzall";
            5'h13: return "bgezall";
            default: return $sformatf("rt_0x%02h", r);
        endcase
    endfunction

    // ee_core_stub recognizes these top-level opcodes (per its is_nop_class
    // assignment around line 527). Anything else with STRICT_UNSUPPORTED=1
    // traps before retire and therefore should never appear in the audit
    // histogram. If it does, that's a decode bug.
    function automatic bit ch194_op_recognized(input logic [5:0] op);
        case (op)
            6'h00, 6'h01, 6'h02, 6'h03,
            6'h04, 6'h05, 6'h06, 6'h07,
            6'h08, 6'h09, 6'h0A, 6'h0B, 6'h0C, 6'h0D, 6'h0F,
            6'h10,
            6'h20, 6'h21, 6'h23, 6'h24, 6'h25,
            6'h28, 6'h29, 6'h2B,
            6'h2F: return 1'b1;
            default: return 1'b0;
        endcase
    endfunction

    // ------------------------------------------------------------------
    // Ch177 — coarse instruction-class autopsy. Distinct from
    // mips_mnemonic above (which decodes a specific instruction) —
    // this returns a class string useful for "which family of opcodes
    // do we need to wire next?" Covers the EE supersets over MIPS III
    // that mips_mnemonic falls back to "OP?(...)" on.
    // ------------------------------------------------------------------
    function automatic string ch177_class(input logic [31:0] word);
        logic [5:0] op, func;
        logic [4:0] rs;

        op   = word[31:26];
        rs   = word[25:21];
        func = word[5:0];

        case (op)
            6'h00: return (func == 6'h0C || func == 6'h0D) ? "TRAP" : "MIPS_SPECIAL";
            6'h01: return "MIPS_REGIMM";
            6'h02, 6'h03: return "MIPS_J/JAL";
            6'h04, 6'h05, 6'h06, 6'h07: return "MIPS_BRANCH";
            6'h08, 6'h09, 6'h0A, 6'h0B,
            6'h0C, 6'h0D, 6'h0E, 6'h0F: return "MIPS_ALU_IMM";
            6'h10: begin
                case (rs)
                    5'h00: return "COP0_MFC0";
                    5'h04: return "COP0_MTC0";
                    5'h10: return "COP0_CO_TLB";
                    default: return $sformatf("COP0(rs=%0d)", rs);
                endcase
            end
            6'h11: return "EE_COP1_FPU";
            6'h12: return "EE_COP2_VU0";
            6'h13: return "EE_COP3_RESERVED";
            6'h14, 6'h15, 6'h16, 6'h17: return "EE_BRANCH_LIKELY";
            6'h18, 6'h19: return "EE_DADDI/DADDIU";
            6'h1A, 6'h1B: return "EE_LDL/LDR";
            6'h1C: return "EE_MMI";              // MMI / SPECIAL2
            6'h1E: return "EE_LQ";
            6'h1F: return "EE_SQ";
            6'h20, 6'h21, 6'h22, 6'h23,
            6'h24, 6'h25, 6'h26: return "MIPS_LOAD";
            6'h27: return "EE_LWU";
            6'h28, 6'h29, 6'h2A, 6'h2B,
            6'h2E: return "MIPS_STORE";
            6'h2F: return "EE_CACHE";
            6'h30: return "EE_LL/LWC0";
            6'h31: return "EE_LWC1";
            6'h33: return "EE_PREF";
            6'h36: return "EE_LQC2";
            6'h37: return "EE_LD";
            6'h38: return "EE_SC/SWC0";
            6'h39: return "EE_SWC1";
            6'h3E: return "EE_SQC2";
            6'h3F: return "EE_SD";
            default: return $sformatf("UNKNOWN(op=%02h)", op);
        endcase
    endfunction

    // ------------------------------------------------------------------
    // Preload helpers
    // ------------------------------------------------------------------

    task automatic bios_word(input int slot, input logic [31:0] word);
        u_bios.mem[slot] = word;
    endtask

    function automatic logic [31:0] enc_i(input logic [5:0] op,
                                          input int rs, input int rt,
                                          input logic [15:0] imm);
        return {op, rs[4:0], rt[4:0], imm};
    endfunction

    function automatic logic [31:0] enc_j(input logic [31:0] target_addr);
        return {OP_J, target_addr[27:2]};
    endfunction

    function automatic logic [31:0] enc_break();
        // SPECIAL opcode, func=0x0D (BREAK). Code field [25:6] is
        // implementation-defined; we use 0 so the encoded word is
        // 0x0000000D — stable, easy to eyeball.
        return {OP_SPECIAL, 20'd0, 6'h0D};
    endfunction

    task automatic bios_lui(input int slot, input int rt,
                            input logic [15:0] imm);
        bios_word(slot, enc_i(OP_LUI, 0, rt, imm));
    endtask
    task automatic bios_ori(input int slot, input int rt, input int rs,
                            input logic [15:0] imm);
        bios_word(slot, enc_i(OP_ORI, rs, rt, imm));
    endtask
    task automatic bios_addiu(input int slot, input int rt, input int rs,
                              input logic [15:0] imm);
        bios_word(slot, enc_i(OP_ADDIU, rs, rt, imm));
    endtask
    task automatic bios_sw(input int slot, input int rt, input int rs,
                           input logic [15:0] offs);
        bios_word(slot, enc_i(OP_SW, rs, rt, offs));
    endtask
    task automatic bios_nop(input int slot);
        bios_word(slot, 32'd0);
    endtask
    task automatic bios_j(input int slot, input logic [31:0] target_va);
        bios_word(slot, enc_j(target_va));
    endtask
    task automatic bios_break(input int slot);
        bios_word(slot, enc_break());
    endtask

    // ------------------------------------------------------------------
    // Stimulus
    // ------------------------------------------------------------------

    string bios_image_path;
    logic  using_real_bios;

    initial begin
        rst_n      = 1'b0;
        core_go    = 1'b0;
        install_go = 1'b0;

        repeat (4) @(posedge clk);
        rst_n = 1'b1;
        repeat (2) @(posedge clk);

        using_real_bios = $value$plusargs("BIOS=%s", bios_image_path);
        if (using_real_bios) begin
            $display("[tb_ee_core_bios_smoke] loading real BIOS image: %s",
                     bios_image_path);
            $readmemh(bios_image_path, u_bios.mem);
        end else begin
            bios_j  (RESET_SLOT,     BOOTSTRAP_VA);
            bios_nop(RESET_SLOT + 1);                          // delay slot

            bios_lui  (BOOTSTRAP_SLOT0 + 0,  1, 16'h1000);
            bios_ori  (BOOTSTRAP_SLOT0 + 1,  1,  1, 16'hA000); // DMAC ch2 base
            bios_ori  (BOOTSTRAP_SLOT0 + 2,  2,  0, 16'h0200);
            bios_ori  (BOOTSTRAP_SLOT0 + 3,  3,  0, 16'h0004);
            bios_ori  (BOOTSTRAP_SLOT0 + 4,  4,  0, 16'h0001);
            bios_addiu(BOOTSTRAP_SLOT0 + 5,  6,  1, 16'h0040); // DONE_COUNT
            bios_sw   (BOOTSTRAP_SLOT0 + 6,  2,  1, 16'h0010); // MADR = $2
            bios_sw   (BOOTSTRAP_SLOT0 + 7,  3,  1, 16'h0020); // QWC  = $3
            bios_break(BOOTSTRAP_SLOT0 + TRAP_BOOTSTRAP_OFFSET);
                                                               // UNSUPPORTED
        end

        @(negedge clk);
        core_go = 1'b1;
        @(negedge clk);
        core_go = 1'b0;

        while (!core_halt) @(posedge clk);
        repeat (4) @(posedge clk);

        // --------------------------------------------------------------
        // Post-halt reporting (both modes)
        // --------------------------------------------------------------
        $display("[tb_ee_core_bios_smoke] retired=%0d trap=%0d trap_pc=0x%08h trap_instr=0x%08h mnemonic=%s halt=%0d except=%0d done=%0d bios_reads=%0d hist(lui,ori,addiu,sw,lw,j,other)=(%0d,%0d,%0d,%0d,%0d,%0d,%0d) errors=%0d",
                 core_retired_events, core_trap_events,
                 core_trap_pc, core_trap_instr,
                 mips_mnemonic(core_trap_instr),
                 core_halt_events, core_exception_events,
                 dmac_done_events, bios_reads,
                 op_count_lui, op_count_ori, op_count_addiu,
                 op_count_sw, op_count_lw, op_count_j, op_count_other,
                 errors);

        // Ch200 — lineage-observer cap summary (also fires on the
        // happy halt path). Mirrors the same line printed at timeout.
        $display("[ch200] observer_caps r2=%0d r3=%0d r10=%0d r31=%0d (cap=%0d) suppressed=%0d",
                 ch200_r2_prints, ch200_r3_prints,
                 ch200_r10_prints, ch200_r31_prints,
                 CH200_PRINT_CAP, ch200_suppressed);

        // Ch201 — post-RFE progress autopsy at halt (mirrors the
        // timeout-path call). Only fires when the Ch199 consume
        // happened during this run.
        if (ch199_consume_count > 0)
            ch201_print_summary();

        // Ch193 — halt-point autopsy. Print only if the halt fired
        // cleanly (vs strict-trap or timeout). Identifies the
        // exact instruction + COP0 state + argument registers at
        // the moment the EE halted, then dumps the depth-8
        // exception ring so we can see whether the 5 exceptions
        // were 5 SYSCALLs (BIOS made 5 SDK syscalls — last one
        // halted) or a mix of fault classes.
        if (halt_point_captured) begin
            string excode_name;
            case (halt_point_cause[6:2])
                5'd0:  excode_name = "Int (interrupt)";
                5'd1:  excode_name = "Mod (TLB modified)";
                5'd2:  excode_name = "TLBL (TLB load)";
                5'd3:  excode_name = "TLBS (TLB store)";
                5'd4:  excode_name = "AdEL (load adr err)";
                5'd5:  excode_name = "AdES (store adr err)";
                5'd6:  excode_name = "IBE (instr bus err)";
                5'd7:  excode_name = "DBE (data bus err)";
                5'd8:  excode_name = "Sys (syscall)";
                5'd9:  excode_name = "Bp (break)";
                5'd10: excode_name = "RI (reserved instr)";
                5'd11: excode_name = "CpU (coprocessor unusable)";
                5'd12: excode_name = "Ov (arithmetic overflow)";
                5'd13: excode_name = "Tr (trap)";
                default: excode_name = $sformatf("ExcCode=0x%02h", halt_point_cause[6:2]);
            endcase
            $display("[tb_ee_core_bios_smoke] Ch193 HALT_POINT cyc=%0d pc=0x%08h instr=0x%08h mnemonic=%s",
                     halt_point_cycle, halt_point_pc, halt_point_instr,
                     mips_mnemonic(halt_point_instr));
            $display("[tb_ee_core_bios_smoke] Ch193   COP0: EPC=0x%08h Cause=0x%08h (ExcCode=0x%02h = %s) Status=0x%08h",
                     halt_point_epc, halt_point_cause,
                     halt_point_cause[6:2], excode_name,
                     halt_point_status);
            $display("[tb_ee_core_bios_smoke] Ch193   regs: $v0=0x%08h $v1=0x%08h $a0=0x%08h $a1=0x%08h $a2=0x%08h $a3=0x%08h $ra=0x%08h",
                     halt_point_v0, halt_point_v1,
                     halt_point_a0, halt_point_a1,
                     halt_point_a2, halt_point_a3,
                     halt_point_ra);
            $display("[tb_ee_core_bios_smoke] Ch193   exception ring (last %0d events, newest first):",
                     (exc_ring_count < EXC_RING_DEPTH) ? exc_ring_count : EXC_RING_DEPTH);
            for (int i = 0; i < EXC_RING_DEPTH; i++) begin
                int slot;
                if (i < exc_ring_count) begin
                    slot = (exc_ring_count - 1 - i + EXC_RING_DEPTH) % EXC_RING_DEPTH;
                    $display("[tb_ee_core_bios_smoke] Ch193     [%0d] cyc=%0d pc=0x%08h instr=0x%08h excode=0x%02h%s",
                             i, exc_ring_cyc[slot], exc_ring_pc[slot],
                             exc_ring_instr[slot],
                             exc_ring_excode[slot],
                             exc_ring_was_halt[slot] ? "  <-- halted here" : "");
                end
            end
            $display("[tb_ee_core_bios_smoke] Ch193   last_a0_call_count=%0d (Ch188 observer); BIOS made %0d A0 syscalls before halt",
                     ch188_a0_call_count, ch188_a0_call_count);
            // Verdict — derived from the halt INSTRUCTION word, not
            // the COP0 Cause. Our EE stub's syscall halt bypasses
            // the full exception machinery (Cause/EPC aren't
            // updated), so Cause may be stale from an earlier
            // legitimate exception (e.g., a CH49 AdES the BIOS
            // exception handler dealt with).
            if (halt_point_instr[31:26] == 6'h00 && halt_point_instr[5:0] == 6'h0C) begin
                $display("[ch193] verdict=clean_syscall_halt (BIOS issued SYSCALL at pc=0x%08h with $v1=0x%08h $v0=0x%08h; classical BIOS-phase handoff. Stale Cause=0x%02h from earlier exceptions is unrelated.)",
                         halt_point_pc, halt_point_v1, halt_point_v0,
                         halt_point_cause[6:2]);
            end else if (halt_point_instr[31:26] == 6'h00 && halt_point_instr[5:0] == 6'h0D) begin
                $display("[ch193] verdict=break_halt (BREAK at pc=0x%08h — BIOS detected an assertion failure or planted-trap)",
                         halt_point_pc);
            end else begin
                $display("[ch193] verdict=other_halt (halt instruction at pc=0x%08h is 0x%08h = %s, not SYSCALL/BREAK — internal halt path?)",
                         halt_point_pc, halt_point_instr,
                         mips_mnemonic(halt_point_instr));
            end
        end

        // ------------------------------------------------------------------
        // Ch194 — opcode-coverage audit. Walks the per-op / per-SPECIAL-
        // funct / per-REGIMM-rt arrays and prints nonzero buckets. The
        // headline names total retires and silent-NOP retires (expected
        // 0 under STRICT_UNSUPPORTED=1). Each nonzero bucket is tagged
        // [recognized] or [UNSUPPORTED!] so a single grep tells us
        // whether anything slipped past the strict-trap gate.
        // ------------------------------------------------------------------
        begin : ch194_audit_dump
            int nonzero_op, nonzero_spec, nonzero_regimm;
            nonzero_op     = 0;
            nonzero_spec   = 0;
            nonzero_regimm = 0;
            $display("[ch194] OPCODE_AUDIT total_retired=%0d silent_nop_total=%0d (expected 0 under STRICT_UNSUPPORTED=1)",
                     ch194_retire_total, ch194_silent_total);

            $display("[ch194] per-opcode buckets (op[31:26]), nonzero only:");
            for (int i = 0; i < 64; i++) begin
                if (ch194_op_hist[i] > 0) begin
                    nonzero_op = nonzero_op + 1;
                    $display("[ch194]   op=0x%02h count=%0d name=%-8s %s",
                             i[5:0], ch194_op_hist[i],
                             ch194_op_name(i[5:0]),
                             ch194_op_recognized(i[5:0]) ? "[recognized]"
                                                         : "[UNSUPPORTED!]");
                end
            end
            $display("[ch194]   (total %0d distinct top-level opcodes appeared)", nonzero_op);

            if (ch194_op_hist[0] > 0) begin
                $display("[ch194] SPECIAL sub-buckets (op=0, funct=instr[5:0]), nonzero only:");
                for (int i = 0; i < 64; i++) begin
                    if (ch194_spec_hist[i] > 0) begin
                        nonzero_spec = nonzero_spec + 1;
                        $display("[ch194]   func=0x%02h count=%0d name=%s",
                                 i[5:0], ch194_spec_hist[i],
                                 ch194_spec_funct_name(i[5:0]));
                    end
                end
                $display("[ch194]   (total %0d distinct SPECIAL funct values, op=0 count=%0d)",
                         nonzero_spec, ch194_op_hist[0]);
            end

            if (ch194_op_hist[1] > 0) begin
                $display("[ch194] REGIMM sub-buckets (op=1, rt=instr[20:16]), nonzero only:");
                for (int i = 0; i < 32; i++) begin
                    if (ch194_regimm_hist[i] > 0) begin
                        nonzero_regimm = nonzero_regimm + 1;
                        $display("[ch194]   rt=0x%02h count=%0d name=%s",
                                 i[4:0], ch194_regimm_hist[i],
                                 ch194_regimm_rt_name(i[4:0]));
                    end
                end
                $display("[ch194]   (total %0d distinct REGIMM rt values)", nonzero_regimm);
            end
        end

        // ------------------------------------------------------------------
        // Ch195 — SYSCALL handoff inspection. Only fires when the halt
        // is a clean SYSCALL (verdict in Ch193). Dumps:
        //  (a) instruction window 0x8003ED80..0x8003EF20 (around halt PC)
        //  (b) pointer-deref 16 words at $v0
        //  (c) newest-first contents of the $v0/$v1/$a0/$ra rings
        //  (d) PS2 syscall-number cheat-sheet for $v1 if it matches a
        //      known SDK value (purely informational; the BIOS may use
        //      its own private numbering during early boot)
        // ------------------------------------------------------------------
        if (halt_point_captured
                && halt_point_instr[31:26] == 6'h00
                && halt_point_instr[5:0]   == 6'h0C) begin : ch195_handoff_dump
            string sdk_name;
            logic [31:0] win_lo, win_hi, ptr_lo;
            int          ring_show;

            // PS2 SDK syscall map (Sony's published "syscall.h"). The
            // BIOS may issue these directly OR use BIOS-internal numbers
            // that overlap. Treat as a hint, not a definitive label.
            case (halt_point_v1)
                32'd1:  sdk_name = "ResetEE";
                32'd2:  sdk_name = "SetGsCrt";
                32'd4:  sdk_name = "Exit";
                32'd6:  sdk_name = "LoadExecPS2";
                32'd7:  sdk_name = "ExecOSD";
                32'd8:  sdk_name = "_ReturnFromException";   // EE exception epilog
                32'd10: sdk_name = "AddSbusIntcHandler";
                32'd11: sdk_name = "RemoveSbusIntcHandler";
                32'd12: sdk_name = "Interrupt2Iop";
                32'd16: sdk_name = "_GetSyscallHandler";
                32'd20: sdk_name = "CreateThread";
                32'd23: sdk_name = "ExitThread";
                32'd29: sdk_name = "StartThread";
                32'd32: sdk_name = "SleepThread";
                32'd33: sdk_name = "WakeupThread";
                default: sdk_name = "(unknown — possibly BIOS-internal)";
            endcase

            $display("[ch195] HANDOFF inspect at halt PC=0x%08h:",
                     halt_point_pc);
            $display("[ch195]   syscall contract: $v1(=%0d)=0x%08h $v0=0x%08h $a0=0x%08h $a1=0x%08h $a2=0x%08h $a3=0x%08h $ra=0x%08h",
                     halt_point_v1, halt_point_v1,
                     halt_point_v0, halt_point_a0, halt_point_a1,
                     halt_point_a2, halt_point_a3, halt_point_ra);
            $display("[ch195]   $v1 SDK name hint: %s", sdk_name);

            // (a) Instruction window. 0x8003ED80..0x8003EF20 exclusive
            // = 0x1A0 bytes = 104 words. The halt PC (0x8003EEC4)
            // sits 0x144 bytes (81 words) into this window.
            win_lo = 32'h8003_ED80;
            win_hi = 32'h8003_EF20;
            $display("[ch195] INSTR_WINDOW [0x%08h..0x%08h):",
                     win_lo, win_hi);
            for (logic [31:0] a = win_lo; a < win_hi; a += 4) begin
                logic [31:0] w;
                w = peek_instr(a);
                $display("[ch195]   0x%08h: %08h  %s%s",
                         a, w, mips_mnemonic(w),
                         (a == halt_point_pc) ? "   <-- HALT (SYSCALL)" :
                         (a == halt_point_v0) ? "   <-- $v0 points here" : "");
            end

            // (b) Pointer-deref window 16 words at $v0. Sometimes
            // overlaps INSTR_WINDOW; print anyway for clarity. If $v0
            // points outside the [0..0x400000) region we route via
            // peek_instr which returns DEAD_BEEF for out-of-range —
            // good enough to spot a non-pointer.
            ptr_lo = halt_point_v0;
            $display("[ch195] PTR_DEREF $v0=0x%08h, 16 words:", ptr_lo);
            for (int k = 0; k < 16; k++) begin
                logic [31:0] a, w;
                a = ptr_lo + (k * 4);
                w = peek_instr(a);
                $display("[ch195]   0x%08h: %08h  %s",
                         a, w, mips_mnemonic(w));
            end

            // (c) Ring contents — newest first. Each ring captures the
            // last 8 writes to that register; the most recent is the
            // instruction that finalized the syscall contract.
            ring_show = (ch195_r3_count < CH195_RING_DEPTH) ? ch195_r3_count : CH195_RING_DEPTH;
            $display("[ch195] $v1 RING (last %0d writes, newest first):", ring_show);
            for (int i = 0; i < ring_show; i++) begin
                int slot;
                slot = (ch195_r3_count - 1 - i + CH195_RING_DEPTH) % CH195_RING_DEPTH;
                $display("[ch195]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h",
                         i, ch195_r3_cyc[slot],
                         ch195_r3_pc[slot], ch195_r3_instr[slot],
                         ch195_r3_pv[slot], ch195_r3_nv[slot]);
            end

            ring_show = (ch195_r2_count < CH195_RING_DEPTH) ? ch195_r2_count : CH195_RING_DEPTH;
            $display("[ch195] $v0 RING (last %0d writes, newest first):", ring_show);
            for (int i = 0; i < ring_show; i++) begin
                int slot;
                slot = (ch195_r2_count - 1 - i + CH195_RING_DEPTH) % CH195_RING_DEPTH;
                $display("[ch195]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h",
                         i, ch195_r2_cyc[slot],
                         ch195_r2_pc[slot], ch195_r2_instr[slot],
                         ch195_r2_pv[slot], ch195_r2_nv[slot]);
            end

            ring_show = (ch195_r4_count < CH195_RING_DEPTH) ? ch195_r4_count : CH195_RING_DEPTH;
            $display("[ch195] $a0 RING (last %0d writes, newest first):", ring_show);
            for (int i = 0; i < ring_show; i++) begin
                int slot;
                slot = (ch195_r4_count - 1 - i + CH195_RING_DEPTH) % CH195_RING_DEPTH;
                $display("[ch195]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h",
                         i, ch195_r4_cyc[slot],
                         ch195_r4_pc[slot], ch195_r4_instr[slot],
                         ch195_r4_pv[slot], ch195_r4_nv[slot]);
            end

            ring_show = (r31_ring_count < R31_RING_DEPTH) ? r31_ring_count : R31_RING_DEPTH;
            $display("[ch195] $ra RING (last %0d writes, newest first):", ring_show);
            for (int i = 0; i < ring_show; i++) begin
                int slot;
                slot = (r31_ring_count - 1 - i + R31_RING_DEPTH) % R31_RING_DEPTH;
                $display("[ch195]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h",
                         i, r31_ring_cyc[slot],
                         r31_ring_pc[slot], r31_ring_instr[slot],
                         r31_ring_prev[slot], r31_ring_new[slot]);
            end
        end

        // ------------------------------------------------------------------
        // Ch196 — exception-context autopsy. _ReturnFromException(2) is
        // trying to ERET back to some saved context. To understand what
        // it would resume, we need to see:
        //   (a) the live COP0 BadVAddr at halt
        //   (b) where $k0/$k1/$sp/$gp point (kernel-context candidates)
        //   (c) what each AdES exception faulted on AND what PC retired
        //       next (skip = EPC+4, retry = EPC, branch = vector)
        //   (d) memory around three candidate save areas:
        //         0x80050FF0..0x80051010  (the kernel pointer $a0 had
        //                                  before being clobbered to 2)
        //         0x80000000..0x80000200  (general exception vector)
        //         peek at $k0, $k1 if they look like KSEG0 pointers
        // No semantic change yet — just autopsy.
        // ------------------------------------------------------------------
        if (halt_point_captured
                && halt_point_instr[31:26] == 6'h00
                && halt_point_instr[5:0]   == 6'h0C) begin : ch196_context_autopsy
            int ring_n;
            logic [31:0] inferred_resume_pc;

            inferred_resume_pc = halt_point_epc + 4;  // handler has been
                                                      // skipping each AdES

            $display("[ch196] CONTEXT_AUTOPSY for SYSCALL $v1=%0d $a0=%0d at PC=0x%08h",
                     halt_point_v1, halt_point_a0, halt_point_pc);
            $display("[ch196]   live COP0 at halt: EPC=0x%08h Cause=0x%08h Status=0x%08h BadVAddr=0x%08h",
                     halt_point_epc, halt_point_cause,
                     halt_point_status, halt_point_badvaddr);
            $display("[ch196]   inferred resume PC = EPC+4 = 0x%08h (handler has been advancing past each AdES; if it had retried, EPC would be the live one). NOT verified against any in-RAM saved context yet.",
                     inferred_resume_pc);
            $display("[ch196]   kernel-context candidate regs: $k0=0x%08h $k1=0x%08h $sp=0x%08h $gp=0x%08h",
                     halt_point_k0, halt_point_k1,
                     halt_point_sp, halt_point_gp);

            // (c) Extended exception ring with BadVAddr + next-PC.
            ring_n = (exc_ring_count < EXC_RING_DEPTH) ? exc_ring_count : EXC_RING_DEPTH;
            $display("[ch196] EXC_RING extended (last %0d AdES/AdEL events, newest first):", ring_n);
            for (int i = 0; i < ring_n; i++) begin
                int slot;
                logic [31:0] delta;
                string action;
                slot = (exc_ring_count - 1 - i + EXC_RING_DEPTH) % EXC_RING_DEPTH;
                delta = exc_ring_next_valid[slot]
                        ? (exc_ring_next_pc[slot] - exc_ring_pc[slot])
                        : 32'hDEADBEEF;
                if (!exc_ring_next_valid[slot])
                    action = "(no follow-up retire yet)";
                else if (exc_ring_next_pc[slot] == exc_ring_pc[slot])
                    action = "RETRY (same PC)";
                else if (exc_ring_next_pc[slot] == (exc_ring_pc[slot] + 4))
                    action = "SKIP +4";
                else if (exc_ring_next_pc[slot] == 32'h8000_0180
                      || exc_ring_next_pc[slot] == 32'hBFC0_0180)
                    action = "BRANCH to GenEx vector";
                else
                    action = "BRANCH elsewhere (handler entry?)";
                $display("[ch196]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h badvaddr=0x%08h excode=0x%02h next_pc=0x%08h delta=0x%08h -> %s",
                         i, exc_ring_cyc[slot],
                         exc_ring_pc[slot], exc_ring_instr[slot],
                         exc_ring_badvaddr[slot],
                         exc_ring_excode[slot],
                         exc_ring_next_pc[slot], delta, action);
            end

            // (d) Candidate context save areas.
            $display("[ch196] CANDIDATE_CTX 0x80050ff0..0x80051010 ($a0-pre-clobber pointer):");
            for (int k = 0; k < 32; k++) begin
                logic [31:0] a, w;
                a = 32'h8005_0FF0 + (k * 4);
                $display("[ch196]   0x%08h: %08h", a, peek_instr(a));
            end

            $display("[ch196] CANDIDATE_CTX 0x80000000..0x80000200 (GenEx vector area):");
            for (int k = 0; k < 128; k++) begin
                logic [31:0] a, w;
                a = 32'h8000_0000 + (k * 4);
                w = peek_instr(a);
                if (w != 32'd0)  // skip the long zero runs
                    $display("[ch196]   0x%08h: %08h  %s", a, w, mips_mnemonic(w));
            end

            if (halt_point_k0[31] && (halt_point_k0[28:24] < 5'h04)) begin
                $display("[ch196] $k0 deref (16 words at 0x%08h):", halt_point_k0);
                for (int k = 0; k < 16; k++) begin
                    logic [31:0] a;
                    a = halt_point_k0 + (k * 4);
                    $display("[ch196]   0x%08h: %08h", a, peek_instr(a));
                end
            end else begin
                $display("[ch196] $k0=0x%08h does not look like a KSEG0 pointer; skipping deref",
                         halt_point_k0);
            end

            if (halt_point_k1[31] && (halt_point_k1[28:24] < 5'h04)) begin
                $display("[ch196] $k1 deref (16 words at 0x%08h):", halt_point_k1);
                for (int k = 0; k < 16; k++) begin
                    logic [31:0] a;
                    a = halt_point_k1 + (k * 4);
                    $display("[ch196]   0x%08h: %08h", a, peek_instr(a));
                end
            end else begin
                $display("[ch196] $k1=0x%08h does not look like a KSEG0 pointer; skipping deref",
                         halt_point_k1);
            end

            $display("[ch196] VERDICT_PROVISIONAL: _ReturnFromException(2) would ERET to inferred_resume_pc=0x%08h ($a0=2 is BIOS-private discriminator, untranslated). Recommend Ch197 implement SYSCALL #8 as a *report-only* shim that prints the would-be target PC + a named verdict, NOT yet performing the actual state restore.",
                     inferred_resume_pc);
        end

        // ------------------------------------------------------------------
        // Ch197 — report-only SYSCALL #8 handler. Narrow contract per Codex:
        //   - Detect SYSCALL where $v1 == 8.
        //   - Do NOT resume execution (existing halt already does this).
        //   - Print a named verdict on a single greppable line, plus the
        //     three inferred resume-PC candidates the autopsy turned up.
        //   - For any OTHER syscall number, the existing ch193
        //     verdict=clean_syscall_halt path continues unchanged.
        // Acceptance: real BIOS run ends in the named verdict;
        // regression stays 148/148.
        // ------------------------------------------------------------------
        if (halt_point_captured
                && halt_point_instr[31:26] == 6'h00
                && halt_point_instr[5:0]   == 6'h0C
                && halt_point_v1 == 32'd8) begin
            $display("[ch197] verdict=return_from_exception_report_only pc=0x%08h instr=0x%08h $a0=0x%08h epc=0x%08h status=0x%08h cause=0x%08h badvaddr=0x%08h $k0=0x%08h $k1=0x%08h",
                     halt_point_pc, halt_point_instr,
                     halt_point_a0,
                     halt_point_epc, halt_point_status,
                     halt_point_cause, halt_point_badvaddr,
                     halt_point_k0, halt_point_k1);
            $display("[ch197]   resume_candidates: EPC=0x%08h EPC+4=0x%08h $k0=0x%08h (Ch196 found $k0 == EPC+4, suggesting handler already advanced the saved-PC; not yet verified against any in-RAM saved context)",
                     halt_point_epc, halt_point_epc + 32'd4, halt_point_k0);
            $display("[ch197]   context_table_head 0x80050ff0 (8 fn-ptr entries + 3 SBUS regs):");
            for (int k = 0; k < 11; k++) begin
                logic [31:0] a;
                a = 32'h8005_0FF0 + (k * 4);
                $display("[ch197]     [%0d] 0x%08h: 0x%08h", k, a, peek_instr(a));
            end
            $display("[ch197] NOTE: no ERET/RFE performed. SYSCALL state-restore semantics deferred to Ch198+ once saved-context mapping is verified.");
        end

        // ------------------------------------------------------------------
        // Ch199 — _ReturnFromException(2) semantic landing. If the new
        // ee_core_stub syscall_8_a0_2 branch consumed one or more
        // SYSCALLs during the run, ch199_consume_count > 0. Summarize
        // here: how many times we resumed, the first resume's $k0
        // landing pc, and (per Codex's caution) whether the AdES event
        // count grew between the first resume and the eventual halt —
        // an AdES-count regrowth post-resume means status/EXL handling
        // is incomplete (BIOS looped right back into the same fault).
        // ------------------------------------------------------------------
        if (ch199_consume_count > 0) begin
            int adess_after_first_consume;
            adess_after_first_consume = exc_ring_count - ch199_excring_at_consume;
            $display("[ch199] SUMMARY consume_count=%0d first_consume cyc=%0d at pc=0x%08h with $a0=%0d resumed to $k0=0x%08h",
                     ch199_consume_count,
                     ch199_first_cyc, ch199_first_pc,
                     ch199_first_a0, ch199_first_k0);
            $display("[ch199]   AdES exception counter: before-resume=%0d total-at-end=%0d delta-after-first-resume=%0d (delta>0 means BIOS re-faulted after resume — status/EXL handling INCOMPLETE)",
                     ch199_excring_at_consume, exc_ring_count,
                     adess_after_first_consume);
            if (halt_point_captured) begin
                $display("[ch199]   post-resume halt: pc=0x%08h instr=0x%08h mnemonic=%s (this is the NEW halt point after Ch199 resumed past the SYSCALL — Ch197 should NOT have printed since the syscall_8_a0_2 path is no longer terminal)",
                         halt_point_pc, halt_point_instr,
                         mips_mnemonic(halt_point_instr));
            end else begin
                $display("[ch199]   no post-resume halt observed (run timed out or completed without further halt)");
            end
        end

        // ------------------------------------------------------------------
        // Ch198 — saved-context MAPPING for _ReturnFromException(2).
        // We're not implementing semantics yet; we're trying to prove
        // WHERE syscall #8 expects to restore state from. The autopsy
        // produces three pieces of evidence:
        //
        //   (a) Caller-side context window (32 words around 0x8003E3F0)
        //       — shows what the caller computed/set up before the
        //       jal to 0x8003EEC0, including how it selected this
        //       wrapper variant (vs another that might have forced
        //       $a0=1 or 0).
        //
        //   (b) Stack dump at $sp (64 words from 0x801FFD80) — looks
        //       for register-spill / context-frame patterns. A typical
        //       MIPS exception frame has 32 GPRs + lo/hi + Status +
        //       Cause + EPC + BadVAddr = ~38 words and the BIOS
        //       handler probably parked one here.
        //
        //   (c) RAM scan: walk the first 1 MiB of useg_shadow_mem
        //       looking for stored copies of {EPC, EPC+4, Cause,
        //       BadVAddr, $k0, table-base 0x80050FF0}. Any hit is a
        //       saved-context-field candidate; absence is itself
        //       useful (confirms syscall #8 must use live COP0/$k0).
        //
        // Acceptance: produces a concrete context map OR explicit
        // evidence that this variant uses COP0/$k0 (no RAM copy).
        // ------------------------------------------------------------------
        if (halt_point_captured
                && halt_point_instr[31:26] == 6'h00
                && halt_point_instr[5:0]   == 6'h0C
                && halt_point_v1 == 32'd8) begin : ch198_context_mapping
            logic [31:0] win_lo, win_hi, sp_lo;
            int hit_count;

            // (a) Caller-side window. The jal at 0x8003E3F0 lives in
            // the middle of this; bookend ~14 instrs before and
            // ~18 after so we see argument setup AND any post-call
            // continuation.
            win_lo = 32'h8003_E380;
            win_hi = 32'h8003_E430;
            $display("[ch198] CALLER_WINDOW [0x%08h..0x%08h):", win_lo, win_hi);
            for (logic [31:0] a = win_lo; a < win_hi; a += 4) begin
                logic [31:0] w;
                w = peek_instr(a);
                $display("[ch198]   0x%08h: %08h  %s%s",
                         a, w, mips_mnemonic(w),
                         (a == 32'h8003_E3F0) ? "   <-- jal 0x8003EEC0 (the wrapper)" : "");
            end

            // (b) Stack dump. $sp grew downward; the kernel exception
            // handler typically writes a frame just BELOW its initial
            // $sp. Dump 64 words upward from $sp ([sp .. sp+0x100)).
            sp_lo = halt_point_sp;
            $display("[ch198] STACK_DUMP $sp=0x%08h, 64 words upward:", sp_lo);
            for (int k = 0; k < 64; k++) begin
                logic [31:0] a, w;
                string annot;
                a = sp_lo + (k * 4);
                w = peek_instr(a);
                annot = "";
                if (w == halt_point_epc)        annot = "  <-- == EPC";
                else if (w == halt_point_epc+4) annot = "  <-- == EPC+4 (=$k0)";
                else if (w == halt_point_cause) annot = "  <-- == Cause";
                else if (w == halt_point_badvaddr && w != 32'd0) annot = "  <-- == BadVAddr";
                else if (w == 32'h8005_0FF0)    annot = "  <-- == table-base 0x80050FF0";
                else if (w == halt_point_ra)    annot = "  <-- == $ra (0x8003E3F8)";
                $display("[ch198]   sp+0x%03h (0x%08h): %08h%s",
                         k*4, a, w, annot);
            end

            // (c) RAM scan. useg_shadow_mem covers the low 4 MiB,
            // word-addressed (20-bit index). Scan the first 1 MiB
            // = 256 K words = bounded sim cost. Print at most 256
            // hits to avoid log spam. We weight the signal: only
            // EPC/EPC+4 hits are STRONG evidence of a RAM-resident
            // saved context; Cause=0x14 is just the small int 20
            // and produces lots of false positives; table-base hits
            // are usually just kernel metadata.
            begin : ram_scan
                int epc_hits, epc4_hits, cause_hits, tbl_hits;
                epc_hits = 0; epc4_hits = 0; cause_hits = 0; tbl_hits = 0;
                $display("[ch198] RAM_SCAN useg_shadow_mem first 1MiB (256K words) for context-field stored copies:");
                hit_count = 0;
                for (int idx = 0; idx < 256*1024; idx++) begin
                    logic [31:0] w, a;
                    w = u_ee_map.g_useg_shadow.useg_shadow_mem[idx[19:0]];
                    a = idx * 4;
                    if (hit_count < 256 &&
                        (w == halt_point_epc
                         || w == (halt_point_epc + 4)
                         || w == halt_point_cause
                         || w == 32'h8005_0FF0)) begin
                        string tag;
                        if (w == halt_point_epc) begin
                            tag = "EPC (STRONG)";
                            epc_hits = epc_hits + 1;
                        end else if (w == halt_point_epc+4) begin
                            tag = "EPC+4 / $k0 (STRONG)";
                            epc4_hits = epc4_hits + 1;
                        end else if (w == halt_point_cause) begin
                            tag = "Cause(0x14) (weak — could be literal 20)";
                            cause_hits = cause_hits + 1;
                        end else if (w == 32'h8005_0FF0) begin
                            tag = "table-base 0x80050FF0 (kernel metadata)";
                            tbl_hits = tbl_hits + 1;
                        end else tag = "?";
                        $display("[ch198]   HIT 0x%08h: 0x%08h <%s>", a, w, tag);
                        hit_count = hit_count + 1;
                    end
                end
                $display("[ch198] RAM_SCAN totals: EPC=%0d EPC+4=%0d Cause=%0d table-base=%0d (only EPC/EPC+4 are strong evidence)",
                         epc_hits, epc4_hits, cause_hits, tbl_hits);

                // The strong-signal test is "is EPC or EPC+4 stored
                // anywhere in low RAM?". If yes → RAM-resident saved
                // context exists; if no → syscall #8 uses live COP0/$k0.
                if ((epc_hits + epc4_hits) == 0) begin
                    $display("[ch198] VERDICT_MAPPING: NO RAM-resident saved-PC found (EPC and EPC+4 absent from first 1 MiB). _ReturnFromException(2) must use COP0/$k0 directly. Recommend Ch199 implement minimal ERET to $k0 (= EPC+4), matching the existing PC=0x80 handler-skip pattern — SYSCALL #8 is the same semantic delivered via syscall instead of vector-fall-through. Cause=%0d / table-base=%0d hits are coincidental literal-int / kernel-metadata noise, NOT saved-context fields.",
                             cause_hits, tbl_hits);
                end else begin
                    $display("[ch198] VERDICT_MAPPING: EPC/EPC+4 found in RAM (EPC=%0d EPC+4=%0d hits) — RAM-resident saved-context EXISTS. Ch199 should enumerate the surrounding words at each strong hit to identify the full context frame layout before implementing ERET.",
                             epc_hits, epc4_hits);
                end
            end
        end

        // Ch177 — single-line autopsy of the first strict-trap halt.
        // Format pinned by Codex Ch177 contract:
        //   pc=... opcode=... op=0x?? class=<...> rs=$.. rt=$.. rd=$.. funct=0x?? cop=<...>
        // The class string is coarse-grained (instruction family, not
        // specific mnemonic — see ch177_class above). When trap_o
        // never fired (e.g., synthetic SYSCALL-halt mode), the line
        // still prints with the captured 0 values so the harness is
        // observable in both run modes.
        if (core_trap_events > 0) begin
            $display("[ch177] strict_trap pc=0x%08h opcode=0x%08h op=0x%02h class=%s rs=$%0d rt=$%0d rd=$%0d funct=0x%02h cop=%s mnemonic=%s",
                     core_trap_pc, core_trap_instr,
                     core_trap_instr[31:26],
                     ch177_class(core_trap_instr),
                     core_trap_instr[25:21],
                     core_trap_instr[20:16],
                     core_trap_instr[15:11],
                     core_trap_instr[5:0],
                     (core_trap_instr[31:26] == 6'h10) ? "COP0" :
                     (core_trap_instr[31:26] == 6'h11) ? "COP1" :
                     (core_trap_instr[31:26] == 6'h12) ? "COP2" :
                     (core_trap_instr[31:26] == 6'h13) ? "COP3" : "none",
                     mips_mnemonic(core_trap_instr));
        end else begin
            $display("[ch177] strict_trap none (core halted via SYSCALL/clean exit, no unsupported opcode reached)");
        end

        // --------------------------------------------------------------
        // Ch204 — strict-trap autopsy. Walks the captured trap context
        // + last 32 retire_ring entries + low-RAM window 0x40..0xC0 +
        // the [0x40,0xC0) write ring + second-RFE consume context (if
        // any). Verdict line classifies how PC arrived at the trap
        // site (direct fall-through, jump, exception vector, RFE).
        // --------------------------------------------------------------
        if (ch204_trap_captured) begin
            int          ring_n;
            logic [31:0] prev_retired_pc;
            logic [31:0] prev_retired_instr;
            string       arrival_verdict;

            $display("[ch204] STRICT_TRAP_AUTOPSY cyc=%0d pc=0x%08h instr=0x%08h ra=0x%08h",
                     ch204_trap_cyc, ch204_trap_pc,
                     ch204_trap_instr, ch204_trap_ra);
            $display("[ch204]   COP0 at trap: EPC=0x%08h Cause=0x%08h Status=0x%08h BadVAddr=0x%08h",
                     ch204_trap_epc, ch204_trap_cause,
                     ch204_trap_status, ch204_trap_badvaddr);
            $display("[ch204]   exc_ring_count_at_trap=%0d consume_count_at_trap=%0d",
                     ch204_trap_excring_at, ch204_trap_consume_at);

            // (a) Low-RAM window 0x40..0xC0 (32 words = covers the
            // trap site at 0x68 and the GenEx-vector entry at 0x80).
            $display("[ch204] LOW_RAM dump 0x00000040..0x000000C0:");
            for (logic [31:0] a = 32'h0000_0040; a < 32'h0000_00C0; a += 4) begin
                logic [31:0] w;
                w = peek_instr(a);
                $display("[ch204]   0x%08h: %08h  %s%s",
                         a, w, mips_mnemonic(w),
                         (a == ch204_trap_pc) ? "   <-- TRAP SITE" :
                         (a == 32'h0000_0080) ? "   (GenEx vector entry)" : "");
            end

            // (b) [0x40,0xC0) write ring — every store that landed in
            // the inspected window, newest-first. If the trap site
            // 0x68 has a write entry, it was deliberately installed;
            // if not, it's residual from earlier code or never written.
            ring_n = (ch204_wr_count < CH204_WRRING_DEPTH)
                      ? ch204_wr_count : CH204_WRRING_DEPTH;
            $display("[ch204] WRITES_TO_0x40-0xC0 captured %0d (newest first):", ring_n);
            for (int i = 0; i < ring_n; i++) begin
                int slot;
                slot = (ch204_wr_count - 1 - i + CH204_WRRING_DEPTH) % CH204_WRRING_DEPTH;
                $display("[ch204]   [%0d] cyc=%0d addr=0x%08h data=0x%08h source_pc=0x%08h%s",
                         i, ch204_wr_cyc[slot],
                         ch204_wr_addr[slot], ch204_wr_data[slot],
                         ch204_wr_pc[slot],
                         (ch204_wr_addr[slot][28:0] == 29'h0000_0068)
                           ? "  <-- INSTALLED trap-site word" : "");
            end

            // (c) Last 32 retired PCs (retire_ring is depth 32).
            $display("[ch204] LAST_32_RETIRES (newest first):");
            for (int i = 0; i < RETIRE_RING_DEPTH; i++) begin
                int slot;
                slot = (retire_ring_head + RETIRE_RING_DEPTH - 1 - i)
                       % RETIRE_RING_DEPTH;
                $display("[ch204]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h  %s",
                         i, retire_ring_cyc[slot],
                         retire_ring_pc[slot], retire_ring_instr[slot],
                         mips_mnemonic(retire_ring_instr[slot]));
            end

            // (d) Arrival-classifier: examine the retire IMMEDIATELY
            // before the trap. If its pc+4 == trap_pc → fall-through.
            // If it was a J/JAL with target==trap_pc → jump. If
            // trap_pc is the GenEx vector (0x80 on this model) or
            // 0x180 (standard MIPS) → exception. Otherwise → unknown.
            prev_retired_pc    = retire_ring_pc   [(retire_ring_head
                                  + RETIRE_RING_DEPTH - 2) % RETIRE_RING_DEPTH];
            prev_retired_instr = retire_ring_instr[(retire_ring_head
                                  + RETIRE_RING_DEPTH - 2) % RETIRE_RING_DEPTH];
            if (ch204_trap_pc == 32'h0000_0080 || ch204_trap_pc == 32'h8000_0180)
                arrival_verdict = "exception_vector_entry";
            else if (prev_retired_pc + 4 == ch204_trap_pc)
                arrival_verdict = $sformatf("fall_through_from_0x%08h", prev_retired_pc);
            else if (prev_retired_instr[31:26] == 6'h02
                  || prev_retired_instr[31:26] == 6'h03)
                arrival_verdict = $sformatf("direct_jump_from_0x%08h_target_in_prev_instr", prev_retired_pc);
            else if (prev_retired_instr[31:26] == 6'h00
                  && (prev_retired_instr[5:0] == 6'h08
                   || prev_retired_instr[5:0] == 6'h09))
                arrival_verdict = $sformatf("jr_or_jalr_from_0x%08h", prev_retired_pc);
            else
                arrival_verdict = $sformatf("unknown_branch_or_exception_from_0x%08h", prev_retired_pc);
            $display("[ch204] ARRIVAL_VERDICT: %s -> trap_pc=0x%08h",
                     arrival_verdict, ch204_trap_pc);

            // (e) Second-RFE consume context — only if one fired.
            if (ch204_second_rfe_captured) begin
                $display("[ch204] SECOND_RFE_CONSUME captured cyc=%0d pc=0x%08h $a0=0x%08h $k0=0x%08h $ra=0x%08h exc_ring_count_at=%0d",
                         ch204_second_rfe_cyc, ch204_second_rfe_pc,
                         ch204_second_rfe_a0, ch204_second_rfe_k0,
                         ch204_second_rfe_ra, ch204_second_rfe_excring_at);
            end else begin
                $display("[ch204] SECOND_RFE_CONSUME none (consume_count stayed at <= 1)");
            end
        end

        // --------------------------------------------------------------
        // Ch205 — return-address provenance for the epilog at
        // 0xBFC52B8C..0xBFC52B94. Prints the LW/JR captures + the
        // walk of recent stores filtered by target_ea + the last 4
        // JAL/JALR landings + a verdict classifier.
        // --------------------------------------------------------------
        if (ch205_lw_seen) begin
            int          ring_n;
            int          slot_writes;
            int          newest_match_slot;
            logic [31:0] slot_value_seen;
            logic        any_clobber;
            int          jal_show;
            int          last_jal_into_func;
            string       verdict_str;

            $display("[ch205] EPILOG_LW LAST visit (of %0d total): cyc=%0d pc=0x%08h $sp=0x%08h target_ea=0x%08h",
                     ch205_lw_visit_count, ch205_lw_cyc, CH205_LW_PC,
                     ch205_lw_sp, ch205_lw_target_ea);
            if (ch205_lw_visit_count > 1)
                $display("[ch205] EPILOG_LW FIRST visit: cyc=%0d $sp=0x%08h",
                         ch205_lw_first_cyc, ch205_lw_first_sp);
            if (ch205_jr_seen) begin
                $display("[ch205] EPILOG_JR LAST visit (of %0d total): cyc=%0d pc=0x%08h $ra=0x%08h",
                         ch205_jr_visit_count, ch205_jr_cyc, CH205_JR_PC, ch205_jr_ra);
                if (ch205_jr_visit_count > 1)
                    $display("[ch205] EPILOG_JR FIRST visit: $ra=0x%08h (compare against LAST $ra=0x%08h to see if value changed)",
                             ch205_jr_first_ra, ch205_jr_ra);
            end else begin
                $display("[ch205] EPILOG_JR not captured (run halted before reaching 0x%08h?)",
                         CH205_JR_PC);
            end

            // Scan store ring for matches to target_ea (any byte
            // overlap). We track: count of matches, NEWEST matching
            // slot (= the prolog SW if it's the function's matched
            // pair, OR a later clobber), and whether any write came
            // AFTER the prolog (= clobber risk).
            slot_writes      = 0;
            newest_match_slot = -1;
            slot_value_seen  = 32'hDEADBEEF;
            any_clobber      = 1'b0;
            ring_n = (ch205_store_count < CH205_STORE_DEPTH)
                      ? ch205_store_count : CH205_STORE_DEPTH;
            $display("[ch205] STORE_RING_TO_EA writes to target_ea=0x%08h (newest first; only matches printed):",
                     ch205_lw_target_ea);
            for (int i = 0; i < ring_n; i++) begin
                int slot;
                slot = (ch205_store_count - 1 - i + CH205_STORE_DEPTH) % CH205_STORE_DEPTH;
                if (ch205_store_ea[slot] == ch205_lw_target_ea) begin
                    if (newest_match_slot < 0) begin
                        newest_match_slot = slot;
                        slot_value_seen   = ch205_store_data[slot];
                    end else begin
                        any_clobber = 1'b1;
                    end
                    slot_writes = slot_writes + 1;
                    $display("[ch205]   [%0d] cyc=%0d ea=0x%08h data=0x%08h source_pc=0x%08h",
                             i, ch205_store_cyc[slot],
                             ch205_store_ea[slot], ch205_store_data[slot],
                             ch205_store_pc[slot]);
                end
            end
            $display("[ch205]   slot_match_count=%0d newest_match_data=0x%08h",
                     slot_writes, slot_value_seen);

            // JAL/JALR landings — scan all captured entries, print
            // ONLY those whose target lies inside the candidate function
            // range. Linear scan; cheap at depth 128.
            jal_show = (ch205_jal_count < CH205_JAL_DEPTH)
                        ? ch205_jal_count : CH205_JAL_DEPTH;
            $display("[ch205] JAL_LANDINGS scanning %0d captures for target in [0xBFC52A00,0xBFC52B98) (newest first):",
                     jal_show);
            last_jal_into_func = -1;
            for (int i = 0; i < jal_show; i++) begin
                int slot;
                slot = (ch205_jal_count - 1 - i + CH205_JAL_DEPTH) % CH205_JAL_DEPTH;
                if (ch205_jal_target[slot] >= 32'hBFC5_2A00
                        && ch205_jal_target[slot] <  32'hBFC5_2B98) begin
                    if (last_jal_into_func < 0) last_jal_into_func = slot;
                    $display("[ch205]   [%0d] cyc=%0d source_pc=0x%08h link($ra after)=0x%08h target=0x%08h",
                             i, ch205_jal_cyc[slot],
                             ch205_jal_src_pc[slot],
                             ch205_jal_link[slot],
                             ch205_jal_target[slot]);
                end
            end
            if (last_jal_into_func < 0)
                $display("[ch205]   (none — function entry was older than %0d JAL/JALR retires)",
                         CH205_JAL_DEPTH);

            // Verdict classifier. Two strong signals: (1) the CURRENT
            // value at the stack slot (read via peek_instr in the
            // stack window below — but for the verdict we re-read it
            // here), and (2) whether the store-ring caught a matching
            // SW. The verdict combines them.
            begin : verdict_logic
                logic [31:0] slot_now;
                slot_now = peek_instr(ch205_lw_target_ea);

                if (slot_now == 32'd0 && slot_writes == 0) begin
                    // Slot contains 0 and no recent write — strongest
                    // signal of entered_without_link. Either the
                    // prolog (now outside the store-ring window) wrote
                    // 0 because $ra was 0 at function entry, OR the
                    // slot was never written and reset-to-zero stuck.
                    // Both collapse to: at $ra-restore time, the saved
                    // value was 0.
                    verdict_str = $sformatf("entered_without_link (stack slot 0x%08h holds 0x00000000 at the LW; no matching SW in the last %0d stores; the function's prolog SW must have either propagated a zero $ra into the slot OR the slot was never written. Either way the immediate caller of the function ending at 0xBFC52B94 set $ra=0 — failure mode is upstream of this epilog)",
                                            ch205_lw_target_ea, CH205_STORE_DEPTH);
                end else if (slot_now == 32'd0 && slot_writes > 0
                             && ch205_store_data[newest_match_slot] == 32'd0) begin
                    verdict_str = $sformatf("entered_without_link (the prolog SW at source_pc=0x%08h wrote 0x00000000 into the slot — function received $ra=0 from its caller)",
                                            ch205_store_pc[newest_match_slot]);
                end else if (slot_now == 32'd0 && slot_writes > 0
                             && ch205_store_data[newest_match_slot] != 32'd0) begin
                    verdict_str = $sformatf("stack_slot_clobbered (newest captured SW wrote 0x%08h to the slot but it now reads 0 — something overwrote the slot between the prolog and the epilog)",
                                            ch205_store_data[newest_match_slot]);
                end else if (slot_now != 32'd0) begin
                    verdict_str = $sformatf("slot_nonzero_at_inspect (slot=0x%08h at end of run — the offending LW may have read a different EA or our $sp capture is stale)",
                                            slot_now);
                end else begin
                    verdict_str = $sformatf("unclassified (slot_now=0x%08h slot_writes=%0d)",
                                            slot_now, slot_writes);
                end
                $display("[ch205] verdict=%s", verdict_str);
            end

            // Peek the BIOS code right BEFORE the predecessor PC
            // captured in retire_ring[31]. Ch204 showed that the
            // retire immediately before the LW was a delay-slot nop
            // at PC=0xBFC4C200. The branch instruction itself sits 4
            // bytes earlier at PC=0xBFC4C1FC.  If that's a `j 0xBFC52B8C`
            // or a jr targeting the epilog, that's the smoking gun for
            // entry-past-prolog.
            $display("[ch205] PREDECESSOR_BRANCH peek:");
            $display("[ch205]   0xBFC4C1F8: 0x%08h  %s",
                     peek_instr(32'hBFC4_C1F8), mips_mnemonic(peek_instr(32'hBFC4_C1F8)));
            $display("[ch205]   0xBFC4C1FC: 0x%08h  %s  <-- branch?",
                     peek_instr(32'hBFC4_C1FC), mips_mnemonic(peek_instr(32'hBFC4_C1FC)));
            $display("[ch205]   0xBFC4C200: 0x%08h  %s  <-- delay slot",
                     peek_instr(32'hBFC4_C200), mips_mnemonic(peek_instr(32'hBFC4_C200)));
            $display("[ch205]   0xBFC4C204: 0x%08h  %s",
                     peek_instr(32'hBFC4_C204), mips_mnemonic(peek_instr(32'hBFC4_C204)));

            // --------------------------------------------------------
            // Ch206 — $ra lineage from 2nd RFE to bad epilogue. Prints
            // captured $ra changes + first-zero classifier + function-
            // entry + prolog-SW snapshot. Verdict names the first
            // corrupting event per Codex's named list.
            // --------------------------------------------------------
            if (ch206_ra_at_2rfe_captured) begin
                int          n;
                string       ch206_verdict;
                logic [5:0]  fz_op, fz_func;
                logic [4:0]  fz_rd, fz_rt;
                fz_op   = ch206_first_zero_instr[31:26];
                fz_func = ch206_first_zero_instr[5:0];
                fz_rd   = ch206_first_zero_instr[15:11];
                fz_rt   = ch206_first_zero_instr[20:16];

                $display("[ch206] $RA_AT_2ND_RFE = 0x%08h (captured at activation)",
                         ch206_ra_at_2rfe);
                if (ch206_func_entry_seen)
                    $display("[ch206] FUNC_ENTRY first retire in [0xBFC52A00,0xBFC52B98) cyc=%0d pc=0x%08h $ra_at_entry=0x%08h",
                             ch206_func_entry_cyc, ch206_func_entry_pc,
                             ch206_func_entry_ra_at_entry);
                else
                    $display("[ch206] FUNC_ENTRY not observed (function never re-entered after 2nd RFE within sim)");
                if (ch206_prolog_sw_seen)
                    $display("[ch206] PROLOG_SW first SW $ra after 2nd RFE: cyc=%0d pc=0x%08h ea=0x%08h data=0x%08h",
                             ch206_prolog_sw_cyc, ch206_prolog_sw_pc,
                             ch206_prolog_sw_addr, ch206_prolog_sw_data);
                else
                    $display("[ch206] PROLOG_SW none observed (no `sw $31` retire after 2nd RFE)");

                n = (ch206_ra_change_count < CH206_RA_DEPTH)
                     ? ch206_ra_change_count : CH206_RA_DEPTH;
                $display("[ch206] RA_CHANGES total=%0d (showing newest %0d, newest first):",
                         ch206_ra_change_count, n);
                for (int i = 0; i < n; i++) begin
                    int slot;
                    string src_class;
                    logic [5:0] cop;
                    logic [5:0] cfu;
                    logic [4:0] crd, crt;
                    slot = (ch206_ra_change_count - 1 - i + CH206_RA_DEPTH) % CH206_RA_DEPTH;
                    cop = ch206_ra_instr[slot][31:26];
                    cfu = ch206_ra_instr[slot][5:0];
                    crd = ch206_ra_instr[slot][15:11];
                    crt = ch206_ra_instr[slot][20:16];
                    if (cop == 6'h03) src_class = "jal";
                    else if (cop == 6'h00 && cfu == 6'h09 && crd == 5'd31)
                        src_class = "jalr-link";
                    else if (cop == 6'h23 && crt == 5'd31) src_class = "lw";
                    else if (cop == 6'h00 && crd == 5'd31) src_class = "alu-r";
                    else if ((cop == 6'h08 || cop == 6'h09 || cop == 6'h0C
                              || cop == 6'h0D || cop == 6'h0F)
                              && crt == 5'd31) src_class = "alu-i";
                    else src_class = "other";
                    $display("[ch206]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h src=%s",
                             i, ch206_ra_cyc[slot],
                             ch206_ra_pc[slot], ch206_ra_instr[slot],
                             ch206_ra_prev[slot], ch206_ra_new[slot],
                             src_class);
                end

                // First-zero classifier
                if (ch206_ra_at_2rfe == 32'd0) begin
                    ch206_verdict = "ra_zero_preexisting_at_second_rfe ($ra was already 0 at 2nd RFE resume)";
                end else if (!ch206_first_zero_seen) begin
                    ch206_verdict = "ra_never_became_zero_post_2nd_rfe (zero was loaded into $ra at the bad epilog LW — check Ch205 evidence)";
                end else if (fz_op == 6'h23 && fz_rt == 5'd31) begin
                    ch206_verdict = $sformatf("ra_zero_loaded_from_stack (LW $ra,offset($base) at pc=0x%08h loaded 0 from memory; the stack slot it read held 0)",
                                              ch206_first_zero_pc);
                end else if (fz_op == 6'h00 && fz_func == 6'h09 && fz_rd == 5'd31) begin
                    ch206_verdict = $sformatf("ra_zero_from_jalr_bad_link (jalr $31,$rs at pc=0x%08h linked $ra=0 — pc+8 was 0, very unusual)",
                                              ch206_first_zero_pc);
                end else if (fz_op == 6'h00 && fz_rd == 5'd31) begin
                    ch206_verdict = $sformatf("ra_zero_explicit_write (R-type ALU funct=0x%02h at pc=0x%08h wrote 0 to $ra)",
                                              fz_func, ch206_first_zero_pc);
                end else if ((fz_op == 6'h08 || fz_op == 6'h09 || fz_op == 6'h0C
                              || fz_op == 6'h0D || fz_op == 6'h0F)
                              && fz_rt == 5'd31) begin
                    ch206_verdict = $sformatf("ra_zero_explicit_write (I-type op=0x%02h at pc=0x%08h wrote 0 to $ra)",
                                              fz_op, ch206_first_zero_pc);
                end else if (!ch206_func_entry_seen) begin
                    ch206_verdict = $sformatf("ra_zero_from_nonlink_entry (first-zero at pc=0x%08h instr=0x%08h, but no function entry observed — likely a non-linking jump path)",
                                              ch206_first_zero_pc, ch206_first_zero_instr);
                end else begin
                    ch206_verdict = $sformatf("ra_zero_unclassified (first-zero at pc=0x%08h instr=0x%08h)",
                                              ch206_first_zero_pc, ch206_first_zero_instr);
                end
                $display("[ch206] verdict=%s", ch206_verdict);
            end else begin
                $display("[ch206] no_second_rfe_captured (Ch206 inactive — 2nd RFE didn't fire in this run)");
            end

            // --------------------------------------------------------
            // Ch207 — outer-function prolog autopsy. Decomposes the
            // bug into one of five named verdicts using:
            //  - all writes to 0x801FFDE4 from reset
            //  - addiu-sp-neg invocation entries in [0xBFC52A00,
            //    0xBFC52B40), with incoming $ra + pre/post $sp
            //  - static dump of 0xBFC52A00..0xBFC52B98 for context
            // --------------------------------------------------------
            begin : ch207_block
                int          slot_n;
                int          inv_n;
                logic        sw_zero_in_prolog;
                logic        sw_seen_for_target;
                logic [31:0] sw_first_data;
                logic [31:0] sw_newest_data;
                int          sw_after_prolog_count;
                int          prolog_inv_idx;
                string       ch207_verdict;

                slot_n = (ch207_slot_count < CH207_SLOT_DEPTH)
                          ? ch207_slot_count : CH207_SLOT_DEPTH;
                inv_n  = (ch207_inv_count  < CH207_INV_DEPTH)
                          ? ch207_inv_count  : CH207_INV_DEPTH;

                $display("[ch207] TARGET_SLOT_WRITES total=%0d (to 0x%08h, oldest first):",
                         ch207_slot_count, CH207_TARGET_EA);
                sw_first_data       = 32'hDEADBEEF;
                sw_newest_data      = 32'hDEADBEEF;
                sw_after_prolog_count = 0;
                sw_seen_for_target  = 1'b0;
                sw_zero_in_prolog   = 1'b0;
                prolog_inv_idx      = -1;
                // Find the invocation that immediately precedes the
                // first write (= the prolog invocation for that write).
                for (int i = 0; i < slot_n; i++) begin
                    int slot;
                    slot = (ch207_slot_count - slot_n + i) % CH207_SLOT_DEPTH;
                    if (!sw_seen_for_target) begin
                        sw_seen_for_target = 1'b1;
                        sw_first_data      = ch207_slot_data[slot];
                        if (ch207_slot_data[slot] == 32'd0)
                            sw_zero_in_prolog = 1'b1;
                    end else begin
                        sw_after_prolog_count = sw_after_prolog_count + 1;
                    end
                    sw_newest_data = ch207_slot_data[slot];
                    $display("[ch207]   [%0d] cyc=%0d pc=0x%08h $sp=0x%08h data=0x%08h",
                             i, ch207_slot_cyc[slot],
                             ch207_slot_pc [slot],
                             ch207_slot_sp [slot],
                             ch207_slot_data[slot]);
                end
                if (ch207_slot_count == 0)
                    $display("[ch207]   (no writes EVER landed at 0x%08h)",
                             CH207_TARGET_EA);

                $display("[ch207] OUTER_INVOCATIONS total=%0d (oldest first):",
                         ch207_inv_count);
                for (int i = 0; i < inv_n; i++) begin
                    int slot;
                    slot = (ch207_inv_count - inv_n + i) % CH207_INV_DEPTH;
                    $display("[ch207]   [%0d] cyc=%0d entry_pc=0x%08h $ra_in=0x%08h pre_sp=0x%08h post_sp=0x%08h",
                             i, ch207_inv_cyc[slot],
                             ch207_inv_entry_pc[slot],
                             ch207_inv_in_ra[slot],
                             ch207_inv_pre_sp[slot],
                             ch207_inv_post_sp[slot]);
                end

                $display("[ch207] STATIC_DECODE 0xBFC52A00..0xBFC52B98 (outer function range):");
                for (logic [31:0] a = 32'hBFC5_2A00; a < 32'hBFC5_2B98; a += 4) begin
                    logic [31:0] w;
                    w = peek_instr(a);
                    if (w != 32'd0
                            && (a >= 32'hBFC5_2A00 && a <= 32'hBFC5_2A40)) begin
                        // First 17 words = expected prolog region;
                        // always show. Compress later all-zero spans
                        // to keep noise down.
                        $display("[ch207]   0x%08h: %08h  %s",
                                 a, w, mips_mnemonic(w));
                    end else if (a == 32'hBFC5_2B8C
                              || a == 32'hBFC5_2B90
                              || a == 32'hBFC5_2B94
                              || a == 32'hBFC5_2B98) begin
                        $display("[ch207]   0x%08h: %08h  %s%s",
                                 a, w, mips_mnemonic(w),
                                 (a == 32'hBFC5_2B8C) ? "  <-- epilog LW" :
                                 (a == 32'hBFC5_2B94) ? "  <-- epilog JR" : "");
                    end
                end

                // ----- Verdict classifier -----
                // Compute the 2nd invocation's expected_ea (= post_sp +
                // 0x1C). If this differs from the canonical target_ea
                // (= the address the EPILOG LW actually loaded from),
                // the function's $sp drifted between prolog and epilog
                // — a stack-discipline bug, not a saved-value bug.
                begin
                    logic [31:0] inv2_expected_ea;
                    logic [31:0] inv2_actual_lw_ea;
                    int          drift_bytes_signed;
                    if (ch207_inv_count >= 2) begin
                        int inv2_slot;
                        inv2_slot = (ch207_inv_count - 1) % CH207_INV_DEPTH;
                        inv2_expected_ea = ch207_inv_post_sp[inv2_slot] + 32'h0000_001C;
                    end else begin
                        inv2_expected_ea = 32'hDEADBEEF;
                    end
                    inv2_actual_lw_ea  = ch205_lw_target_ea;
                    drift_bytes_signed = inv2_actual_lw_ea - inv2_expected_ea;

                    $display("[ch207] FRAME_DRIFT_CHECK 2nd_invocation post_sp+0x1C = 0x%08h ; epilog LW target_ea = 0x%08h ; drift = %0d bytes",
                             inv2_expected_ea, inv2_actual_lw_ea, drift_bytes_signed);

                    if (ch207_inv_count >= 2 && inv2_expected_ea != inv2_actual_lw_ea) begin
                        ch207_verdict = $sformatf("wrong_sp_on_second_invocation ($sp drifted %0d bytes between prolog (post_sp+0x1C=0x%08h) and epilog ($sp+0x1C=0x%08h); the prolog SW correctly wrote $ra=0x%08h to 0x%08h, but the epilog LW read 0 from the drifted slot 0x%08h. The function's body must contain an unbalanced stack adjustment between the prolog and the epilog.)",
                                                  drift_bytes_signed,
                                                  inv2_expected_ea,
                                                  inv2_actual_lw_ea,
                                                  ch207_inv_in_ra[(ch207_inv_count - 1) % CH207_INV_DEPTH],
                                                  inv2_expected_ea,
                                                  inv2_actual_lw_ea);
                    end else if (!sw_seen_for_target) begin
                        ch207_verdict = $sformatf("prologue_save_skipped (no write EVER hit 0x%08h; the outer function's prolog SW either never ran for the second invocation OR used a different EA)",
                                                  CH207_TARGET_EA);
                    end else if (sw_zero_in_prolog && sw_after_prolog_count == 0) begin
                        ch207_verdict = $sformatf("prologue_saved_zero_ra (only one SW to slot 0x%08h, data=0x00000000 — the prolog stored a zero $ra at function entry)",
                                                  CH207_TARGET_EA);
                    end else if (sw_newest_data == 32'd0 && sw_after_prolog_count > 0) begin
                        ch207_verdict = $sformatf("stack_slot_clobbered_after_save (%0d additional writes after the prolog SW; newest write data=0x00000000)",
                                                  sw_after_prolog_count);
                    end else if (sw_newest_data != 32'd0) begin
                        ch207_verdict = $sformatf("slot_holds_nonzero_but_lw_loaded_zero (final SW wrote 0x%08h; epilog LW loaded 0)",
                                                  sw_newest_data);
                    end else begin
                        ch207_verdict = "ch207_unclassified";
                    end
                    $display("[ch207] verdict=%s", ch207_verdict);
                end
            end

            // --------------------------------------------------------
            // Ch208 — stack-delta ledger for 2nd invocation. Prints
            // each $sp change in chronological order with running sum
            // and outer/callee tagging, then classifies the drift.
            // --------------------------------------------------------
            begin : ch208_block
                int          n;
                int          last_outer_change_idx;
                int          last_callee_balance;
                int          callee_pending_sum;
                int          callee_entry_idx;
                logic        in_callee_now;
                logic [31:0] callee_entry_sp;
                int          imbalance_count;
                logic [31:0] imbalance_first_pc;
                int          imbalance_first_delta;
                string       ch208_verdict;
                logic        delay_slot_offender;
                logic [31:0] delay_slot_pc;

                n = (ch208_change_count < CH208_DEPTH)
                     ? ch208_change_count : CH208_DEPTH;
                $display("[ch208] STACK_DELTA_LEDGER armed=%0d disarmed=%0d total_changes=%0d running_sum=%0d (oldest first)",
                         ch208_armed, ch208_disarmed,
                         ch208_change_count, ch208_sum);
                imbalance_count       = 0;
                imbalance_first_pc    = 32'd0;
                imbalance_first_delta = 0;
                delay_slot_offender   = 1'b0;
                delay_slot_pc         = 32'd0;
                for (int i = 0; i < n; i++) begin
                    int slot;
                    int delta;
                    logic [5:0] op;
                    logic [4:0] rs, rt;
                    logic [15:0] imm;
                    string source_class;
                    slot = (ch208_change_count - n + i) % CH208_DEPTH;
                    delta = $signed(ch208_new_sp[slot]) - $signed(ch208_old_sp[slot]);
                    op  = ch208_instr[slot][31:26];
                    rs  = ch208_instr[slot][25:21];
                    rt  = ch208_instr[slot][20:16];
                    imm = ch208_instr[slot][15:0];
                    if (op == 6'h09 && rs == 5'd29 && rt == 5'd29)
                        source_class = "addiu $sp,$sp,N";
                    else if (op == 6'h08 && rs == 5'd29 && rt == 5'd29)
                        source_class = "addi $sp,$sp,N";
                    else if (op == 6'h00
                              && (ch208_instr[slot][15:11] == 5'd29))
                        source_class = "alu-rd=$sp";
                    else
                        source_class = "other-or-non-addiu";
                    // delay-slot offender heuristic: an in-outer $sp
                    // write whose PC equals last-jal_pc+4 (= delay slot)
                    // would be a delay-slot $sp adjustment — unusual.
                    // (Not a hard filter; just flag for inspection.)
                    $display("[ch208]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h old_sp=0x%08h new_sp=0x%08h delta=%0d running=%0d %s [%s]",
                             i, ch208_cyc[slot], ch208_pc[slot],
                             ch208_instr[slot],
                             ch208_old_sp[slot], ch208_new_sp[slot],
                             delta, ch208_running_sum[slot],
                             ch208_in_outer[slot] ? "OUTER" : "CALLEE",
                             source_class);
                end

                // Static decode of addiu $sp,$sp,N sites in the outer
                // function body (between the prolog at 0xBFC52AFC and
                // the epilog at 0xBFC52B8C). Lists all candidates that
                // could be the missing +8 restore site.
                $display("[ch208] STATIC_DECODE addiu/addi $sp,$sp,N sites in 0xBFC52AFC..0xBFC52B8C:");
                for (logic [31:0] a = 32'hBFC5_2AFC; a < 32'hBFC5_2B8C; a += 4) begin
                    logic [31:0] w;
                    logic [5:0] op;
                    logic [4:0] rs, rt;
                    logic [15:0] imm;
                    int          imm_signed;
                    w = peek_instr(a);
                    op = w[31:26];
                    rs = w[25:21];
                    rt = w[20:16];
                    imm = w[15:0];
                    imm_signed = $signed({{16{imm[15]}}, imm});
                    if ((op == 6'h09 || op == 6'h08)
                            && rs == 5'd29 && rt == 5'd29) begin
                        $display("[ch208]   0x%08h: %08h  addiu $sp,$sp,%0d",
                                 a, w, imm_signed);
                    end
                end

                // Verdict classifier: classify each $sp change as
                // OUTER vs CALLEE, walk to see if the −8 came from an
                // OUTER addiu or a CALLEE that returned with drift.
                last_outer_change_idx = -1;
                callee_pending_sum    = 0;
                in_callee_now         = 1'b0;
                callee_entry_sp       = 32'd0;
                last_callee_balance   = 0;
                for (int i = 0; i < n; i++) begin
                    int slot;
                    int delta;
                    slot = (ch208_change_count - n + i) % CH208_DEPTH;
                    delta = $signed(ch208_new_sp[slot]) - $signed(ch208_old_sp[slot]);
                    if (ch208_in_outer[slot]) begin
                        last_outer_change_idx = i;
                    end
                end
                if (ch208_sum == 0) begin
                    ch208_verdict = "no_drift_detected (running sum is 0 — bug must be elsewhere or the ledger missed an event)";
                end else if (ch208_sum == -8 && last_outer_change_idx >= 0) begin
                    int slot;
                    slot = (ch208_change_count - n + last_outer_change_idx) % CH208_DEPTH;
                    ch208_verdict = $sformatf("outer_unmatched_sp_delta (running_sum=%0d; last OUTER $sp write at pc=0x%08h instr=0x%08h delta=%0d; suspect the outer body has an `addiu $sp,$sp,-8` without a matching `+8` restore before the epilog)",
                                              ch208_sum,
                                              ch208_pc[slot],
                                              ch208_instr[slot],
                                              $signed(ch208_new_sp[slot]) - $signed(ch208_old_sp[slot]));
                end else if (ch208_sum == -8) begin
                    ch208_verdict = $sformatf("callee_returned_with_sp_drift (running_sum=%0d but no OUTER $sp writes in window — drift originated inside a callee that didn't restore its full frame)",
                                              ch208_sum);
                end else begin
                    ch208_verdict = $sformatf("ch208_unclassified (running_sum=%0d, change_count=%0d)",
                                              ch208_sum, ch208_change_count);
                end
                $display("[ch208] verdict=%s", ch208_verdict);
            end

            // --------------------------------------------------------
            // Ch209 — focused autopsy of the kernel/RFE section
            // (entries 10..27 of the SP_LEDGER). Adds: rich annotation
            // of each entry in that window, a static peek at the
            // handler-entry PCs, and a verdict naming whether the bug
            // is missing-RFE-restore vs handler-unbalanced vs
            // by-design-frame-survival vs specific-callee.
            // --------------------------------------------------------
            begin : ch209_block
                int          n;
                int          lo_idx, hi_idx;
                logic [31:0] sp_at_excep_entry;
                logic [31:0] sp_pre_rfe;
                logic [31:0] sp_post_rfe;
                int          handler_alloc_total;
                int          handler_balanced_subcalls;
                int          handler_post_rfe_free;
                string       ch209_verdict;

                n = (ch208_change_count < CH208_DEPTH)
                     ? ch208_change_count : CH208_DEPTH;
                lo_idx = 10;
                hi_idx = (n - 1 < 27) ? (n - 1) : 27;

                $display("[ch209] FOCUSED_DUMP entries [%0d..%0d] of SP_LEDGER (kernel handler + RFE section):",
                         lo_idx, hi_idx);
                sp_at_excep_entry   = 32'd0;
                sp_pre_rfe          = 32'd0;
                sp_post_rfe         = 32'd0;
                handler_alloc_total = 0;
                handler_balanced_subcalls = 0;
                handler_post_rfe_free = 0;
                for (int i = lo_idx; i <= hi_idx && i < n; i++) begin
                    int slot;
                    int delta;
                    string seam_note;
                    slot = (ch208_change_count - n + i) % CH208_DEPTH;
                    delta = $signed(ch208_new_sp[slot]) - $signed(ch208_old_sp[slot]);
                    seam_note = "";
                    if (i == lo_idx) begin
                        seam_note = " <-- handler entry (sp BEFORE handler alloc)";
                        sp_at_excep_entry = ch208_old_sp[slot];
                    end
                    // 2nd RFE consume at cyc=10194370 for the FIRST
                    // consume; the SECOND consume is the one we care
                    // about — its cycle was captured by Ch204 in
                    // ch204_second_rfe_cyc. Tag entries straddling it.
                    if (ch204_second_rfe_captured
                            && ch208_cyc[slot] >= ch204_second_rfe_cyc
                            && (i == lo_idx
                                || ch208_cyc[(ch208_change_count - n + i - 1)
                                            % CH208_DEPTH]
                                   < ch204_second_rfe_cyc)) begin
                        seam_note = $sformatf("%s [2nd RFE consume at cyc=%0d → here marks first $sp change after RFE]",
                                              seam_note, ch204_second_rfe_cyc);
                        sp_pre_rfe  = ch208_old_sp[slot];  // best approximation
                        sp_post_rfe = ch208_old_sp[slot];
                    end
                    $display("[ch209]   [%0d] cyc=%0d pc=0x%08h instr=0x%08h delta=%0d old_sp=0x%08h new_sp=0x%08h running=%0d%s",
                             i, ch208_cyc[slot], ch208_pc[slot],
                             ch208_instr[slot], delta,
                             ch208_old_sp[slot], ch208_new_sp[slot],
                             ch208_running_sum[slot], seam_note);
                    if (delta < 0 && i < 13) handler_alloc_total = handler_alloc_total - delta;
                    if (i == hi_idx && ch208_pc[slot] == 32'hBFC5_01FC)
                        handler_post_rfe_free = delta;
                end

                $display("[ch209] HANDLER_ENTRY_PCs static peek (does the handler save user $sp anywhere?):");
                $display("[ch209]   0x00030000: 0x%08h  %s",
                         peek_instr(32'h0000_0000 + 32'h0003_0000),
                         mips_mnemonic(peek_instr(32'h0003_0000)));
                $display("[ch209]   0x00030004: 0x%08h  %s",
                         peek_instr(32'h0003_0004),
                         mips_mnemonic(peek_instr(32'h0003_0004)));
                $display("[ch209]   0x0003E1A8: 0x%08h  %s",
                         peek_instr(32'h0003_E1A8),
                         mips_mnemonic(peek_instr(32'h0003_E1A8)));
                $display("[ch209]   0x0003E1AC: 0x%08h  %s",
                         peek_instr(32'h0003_E1AC),
                         mips_mnemonic(peek_instr(32'h0003_E1AC)));
                $display("[ch209]   0x8003E334: 0x%08h  %s",
                         peek_instr(32'h8003_E334),
                         mips_mnemonic(peek_instr(32'h8003_E334)));
                $display("[ch209]   0x8003E338: 0x%08h  %s",
                         peek_instr(32'h8003_E338),
                         mips_mnemonic(peek_instr(32'h8003_E338)));
                // Peek 0xBFC501D8 (= $k0 = 2nd-RFE resume target) and
                // the BIOS-ROM continuation to see the resumed code.
                $display("[ch209] RFE_RESUME peek (PC=0xBFC501D8 = $k0):");
                for (logic [31:0] a = 32'hBFC5_01D8; a < 32'hBFC5_0210; a += 4) begin
                    $display("[ch209]   0x%08h: 0x%08h  %s%s",
                             a, peek_instr(a), mips_mnemonic(peek_instr(a)),
                             (a == 32'hBFC5_01FC) ? "  <-- the +56 free at entry [27]" : "");
                end

                $display("[ch209] HANDLER_ARITHMETIC alloc_total_in_[10..12]=%0d, balanced_sub_section_[13..26]_nets_to=0, post_rfe_free_at_[27]=%0d, net_after_section=%0d",
                         handler_alloc_total, handler_post_rfe_free,
                         handler_post_rfe_free - handler_alloc_total);

                // Verdict classifier.
                // - rfe_missing_sp_restore : the handler at 0x00030000
                //   allocated 88 bytes (40+24+24) that survived across
                //   RFE; our Ch199 RFE doesn't restore $sp from a saved
                //   context, so those bytes leak into user-mode stack
                //   and ultimately produce the -8 drift after later
                //   epilog compensation.
                // - handler_unbalanced_frame : the handler's own epilog
                //   should have freed those bytes BEFORE issuing
                //   SYSCALL #8, and didn't — bug is in the handler code.
                // - pre_rfe_frame_survives_by_design : real PS2 uses a
                //   separate kernel stack via $k0/$k1; the handler's
                //   frames live on the kernel stack and never interact
                //   with user $sp. Bug is in our model conflating them.
                // - specific_callee_unbalanced_at_pc=... : a specific
                //   non-handler callee dropped bytes.
                //
                // Distinguishing signal: did the handler at PC=
                // 0x00030000 save the user $sp (to $k1 or to a global)
                // before allocating its frame? If YES → real-PS2 model
                // expects the handler to restore $sp from its save
                // before ERET; our Ch199 RFE doesn't do this →
                // verdict = rfe_missing_sp_restore (or its dual:
                // handler_unbalanced_frame depending on whether the
                // BIOS does the save in the handler or relies on
                // hardware/kernel-stack). If NO save → handler
                // genuinely runs on user $sp, and the frame survival
                // would be a model artifact only if the BIOS handler
                // forgot to free its frames.
                if (handler_alloc_total > 0
                        && handler_post_rfe_free > 0
                        && handler_post_rfe_free < handler_alloc_total) begin
                    ch209_verdict = $sformatf("rfe_missing_sp_restore_or_handler_kernel_stack_convention (handler at PC=0x00030000 allocated %0d bytes during AdES handling, only +%0d was freed post-RFE at PC=0xBFC501FC; the missing %0d bytes are the source of the eventual −8 drift. Likely either: (i) real PS2 handler runs on a separate kernel stack via $k0/$k1 — our model lacks that separation, OR (ii) our Ch199 RFE consume should restore $sp from a saved context that the handler stored at entry. Per Codex's caution we are NOT adding an $sp restore yet — Ch210 should inspect whether handler entry stores $sp anywhere (memory at 0x80000000-area kernel context block, $k0/$k1 register, etc.) before allocating its frame.)",
                                              handler_alloc_total,
                                              handler_post_rfe_free,
                                              handler_alloc_total - handler_post_rfe_free);
                end else begin
                    ch209_verdict = $sformatf("ch209_unclassified (handler_alloc=%0d post_rfe_free=%0d)",
                                              handler_alloc_total, handler_post_rfe_free);
                end
                $display("[ch209] verdict=%s", ch209_verdict);
            end

            // --------------------------------------------------------
            // Ch210 — saved-context discovery for syscall #8 ($sp).
            // Walks the candidate-save hits, prints the handler-entry
            // GPR snapshot, dumps memory regions near current $sp /
            // kernel table / $a0-pointed area at 2nd RFE consume.
            // Verdict: name a concrete context addr/offset OR prove
            // no save was observed.
            // --------------------------------------------------------
            begin : ch210_block
                int          hit_n;
                logic        any_save_found;
                string       ch210_verdict;

                hit_n = (ch210_hit_count < CH210_HIT_DEPTH)
                         ? ch210_hit_count : CH210_HIT_DEPTH;
                any_save_found = (hit_n > 0);

                $display("[ch210] CANDIDATE_SAVE_HITS total=%0d (stores whose data matched a user-mode $sp value, oldest first):",
                         ch210_hit_count);
                for (int i = 0; i < hit_n; i++) begin
                    int slot;
                    slot = (ch210_hit_count - hit_n + i) % CH210_HIT_DEPTH;
                    $display("[ch210]   [%0d] cyc=%0d pc=0x%08h ea=0x%08h data=0x%08h",
                             i, ch210_hit_cyc[slot],
                             ch210_hit_pc[slot],
                             ch210_hit_ea[slot],
                             ch210_hit_data[slot]);
                end
                if (!any_save_found)
                    $display("[ch210]   (no stores observed with data matching 0x801FFDD0/D8/C8/E00 — no saved-$sp found in run)");

                if (ch210_h0_seen) begin
                    $display("[ch210] HANDLER_ENTRY snapshot at first retire of PC=0x00030000 cyc=%0d (previous retired_pc=0x%08h):",
                             ch210_h0_cyc, ch210_h0_prev_retired_pc);
                    $display("[ch210]   $zero=0x%08h $at=0x%08h $v0=0x%08h $v1=0x%08h $a0=0x%08h $a1=0x%08h $a2=0x%08h $a3=0x%08h",
                             ch210_h0_regs[0],  ch210_h0_regs[1],
                             ch210_h0_regs[2],  ch210_h0_regs[3],
                             ch210_h0_regs[4],  ch210_h0_regs[5],
                             ch210_h0_regs[6],  ch210_h0_regs[7]);
                    $display("[ch210]   $t0=0x%08h $t1=0x%08h $t2=0x%08h $t3=0x%08h $t4=0x%08h $t5=0x%08h $t6=0x%08h $t7=0x%08h",
                             ch210_h0_regs[8],  ch210_h0_regs[9],
                             ch210_h0_regs[10], ch210_h0_regs[11],
                             ch210_h0_regs[12], ch210_h0_regs[13],
                             ch210_h0_regs[14], ch210_h0_regs[15]);
                    $display("[ch210]   $s0=0x%08h $s1=0x%08h $s2=0x%08h $s3=0x%08h $s4=0x%08h $s5=0x%08h $s6=0x%08h $s7=0x%08h",
                             ch210_h0_regs[16], ch210_h0_regs[17],
                             ch210_h0_regs[18], ch210_h0_regs[19],
                             ch210_h0_regs[20], ch210_h0_regs[21],
                             ch210_h0_regs[22], ch210_h0_regs[23]);
                    $display("[ch210]   $t8=0x%08h $t9=0x%08h $k0=0x%08h $k1=0x%08h $gp=0x%08h $sp=0x%08h $fp=0x%08h $ra=0x%08h",
                             ch210_h0_regs[24], ch210_h0_regs[25],
                             ch210_h0_regs[26], ch210_h0_regs[27],
                             ch210_h0_regs[28], ch210_h0_regs[29],
                             ch210_h0_regs[30], ch210_h0_regs[31]);
                end else begin
                    $display("[ch210] HANDLER_ENTRY snapshot: PC=0x00030000 never retired (no handler-entry observed)");
                end

                // Memory dumps at 2nd-RFE-consume time. Use stack
                // window at ch205_lw_sp (the trap-causing epilog's $sp
                // — best approximation to "current $sp at RFE consume").
                if (ch204_second_rfe_captured) begin
                    $display("[ch210] STACK_AROUND_LW_SP (16 words at trap-epilog $sp=0x%08h):",
                             ch205_lw_sp);
                    for (int k = 0; k < 16; k++) begin
                        logic [31:0] a, w;
                        a = ch205_lw_sp + (k * 4);
                        w = peek_instr(a);
                        $display("[ch210]   0x%08h: 0x%08h", a, w);
                    end
                    $display("[ch210] KERNEL_TABLE 0x80050FF0..0x80051070 (24 words):");
                    for (int k = 0; k < 24; k++) begin
                        logic [31:0] a, w;
                        a = 32'h8005_0FF0 + (k * 4);
                        w = peek_instr(a);
                        $display("[ch210]   0x%08h: 0x%08h", a, w);
                    end
                end

                // Verdict.
                if (any_save_found) begin
                    int newest_slot;
                    newest_slot = (ch210_hit_count - 1 + CH210_HIT_DEPTH) % CH210_HIT_DEPTH;
                    ch210_verdict = $sformatf("saved_sp_observed_at_ea=0x%08h_from_pc=0x%08h_data=0x%08h (most-recent save-candidate; %0d total hits — verify whether THIS write is part of the kernel's saved-context block by inspecting surrounding memory at run-end. If yes, syscall #8 should LOAD from the same EA to restore $sp; if the hits are unrelated coincidences, treat as no-save-found.)",
                                              ch210_hit_ea[newest_slot],
                                              ch210_hit_pc[newest_slot],
                                              ch210_hit_data[newest_slot],
                                              ch210_hit_count);
                end else begin
                    ch210_verdict = "no_context_write_observed (no store with data=user-$sp value in run; the kernel handler does NOT save user-mode $sp to memory in our observed path. Real PS2 syscall #8 likely relies on a register-based context (e.g., $k0/$k1 holding a saved-context pointer at exception entry that was set BEFORE our ledger arming) OR has GPR-restore semantics tied to a hardware-managed Status.EXL bit. Ch211 should either: (i) instrument earlier to catch the pre-arm save, (ii) model syscall #8 as 'unwind known handler frames' sim shim, or (iii) defer and pick a different next blocker.)";
                end
                $display("[ch210] verdict=%s", ch210_verdict);
            end

            // --------------------------------------------------------
            // Ch211 — widened context-pattern search. Prints all three
            // capture rings + a memory dump at 0xA000B1E0..0xA000B220
            // (= surrounding the Ch210 single hit) at run-end. Verdict
            // is BLUNT: either we found a candidate context frame
            // (multiple stack-shaped values stored to a contiguous
            // region) or we didn't.
            // --------------------------------------------------------
            begin : ch211_block
                int dstore_n, kseg1_n, lowram_n;
                int kseg1_distinct_eas;
                int dstore_distinct_eas;
                int frame_candidate_eas;
                string ch211_verdict;

                dstore_n = (ch211_dstore_count < CH211_DSTORE_DEPTH)
                            ? ch211_dstore_count : CH211_DSTORE_DEPTH;
                kseg1_n  = (ch211_kseg1_count < CH211_REGSTORE_DEPTH)
                            ? ch211_kseg1_count : CH211_REGSTORE_DEPTH;
                lowram_n = (ch211_lowram_count < CH211_REGSTORE_DEPTH)
                            ? ch211_lowram_count : CH211_REGSTORE_DEPTH;

                $display("[ch211] STACK_VALUE_STORES total=%0d (any store with data in [0x801FFD00,0x801FFE20], oldest first):",
                         ch211_dstore_count);
                for (int i = 0; i < dstore_n; i++) begin
                    int slot;
                    slot = (ch211_dstore_count - dstore_n + i) % CH211_DSTORE_DEPTH;
                    $display("[ch211]   [%0d] cyc=%0d pc=0x%08h ea=0x%08h data=0x%08h",
                             i, ch211_dstore_cyc[slot],
                             ch211_dstore_pc[slot],
                             ch211_dstore_ea[slot],
                             ch211_dstore_data[slot]);
                end

                $display("[ch211] KSEG1_SCRATCH_STORES total=%0d (any store to [0xA0000000,0xA0010000), oldest first):",
                         ch211_kseg1_count);
                for (int i = 0; i < kseg1_n; i++) begin
                    int slot;
                    slot = (ch211_kseg1_count - kseg1_n + i) % CH211_REGSTORE_DEPTH;
                    $display("[ch211]   [%0d] cyc=%0d pc=0x%08h ea=0x%08h data=0x%08h",
                             i, ch211_kseg1_cyc[slot],
                             ch211_kseg1_pc[slot],
                             ch211_kseg1_ea[slot],
                             ch211_kseg1_data[slot]);
                end

                $display("[ch211] LOWRAM_STORES total=%0d (any store to phys [0,0x10000) via KSEG0/KSEG1, oldest first):",
                         ch211_lowram_count);
                for (int i = 0; i < lowram_n; i++) begin
                    int slot;
                    slot = (ch211_lowram_count - lowram_n + i) % CH211_REGSTORE_DEPTH;
                    $display("[ch211]   [%0d] cyc=%0d pc=0x%08h ea=0x%08h data=0x%08h",
                             i, ch211_lowram_cyc[slot],
                             ch211_lowram_pc[slot],
                             ch211_lowram_ea[slot],
                             ch211_lowram_data[slot]);
                end

                $display("[ch211] MEMDUMP around Ch210 hit at 0xA000B1E0..0xA000B240:");
                for (logic [31:0] a = 32'hA000_B1E0; a < 32'hA000_B240; a += 4) begin
                    $display("[ch211]   0x%08h: 0x%08h", a, peek_instr(a));
                end

                // Distinct-EA counts as a coarse "frame" indicator.
                // A real saved-context frame would have multiple
                // contiguous EAs written to a small region.
                kseg1_distinct_eas  = ch211_kseg1_count;
                dstore_distinct_eas = ch211_dstore_count;
                frame_candidate_eas = kseg1_distinct_eas + ch211_lowram_count;

                if (ch211_dstore_count > 1 && ch211_kseg1_count > 4) begin
                    ch211_verdict = $sformatf("possible_context_frame_found (%0d stack-value stores and %0d KSEG1-scratch stores observed; inspect the KSEG1_SCRATCH_STORES list for a contiguous-EA cluster with GPR-frame-like contents)",
                                              ch211_dstore_count, ch211_kseg1_count);
                end else if (ch211_dstore_count > 1) begin
                    ch211_verdict = $sformatf("multiple_stack_value_stores_but_scattered (%0d hits — none clustered in a context-frame layout; likely incidental writes of stack-shaped data, NOT a kernel save area)",
                                              ch211_dstore_count);
                end else if (ch211_dstore_count == 1) begin
                    ch211_verdict = $sformatf("only_single_static_stack_save (Ch210's single hit at 0xA000B1E4 is the only stack-value store across the full run — confirms it's a one-time stack-base init, NOT a per-exception context save)");
                end else begin
                    ch211_verdict = "no_observable_software_context (zero stack-value stores in the broadened search; the RFE-restore line should be CLOSED — no software context save exists in this BIOS path, and a guessed unwind shim would be speculative. Recommend pivoting Ch212 to a different blocker.)";
                end
                $display("[ch211] verdict=%s", ch211_verdict);
            end

            // --------------------------------------------------------
            // Ch212 — field-map confirmation. Prints every SW into the
            // 0x???B1E0..0x???B230 frame range with source GPR + EA
            // offset; static-decode around the save routine at
            // PC=0xBFC4DB58 (the source observed in Ch210/Ch211) and
            // around PC=0xBFC52350 (= the value currently at frame
            // offset +0x00 — to identify what kind of pointer that is).
            // --------------------------------------------------------
            begin : ch212_block
                int fstore_n;
                string gpr_name [0:31];
                logic [31:0] field00_val;
                string ch212_verdict;

                // GPR name table for readable output.
                gpr_name[0]="zero"; gpr_name[1]="at";   gpr_name[2]="v0";  gpr_name[3]="v1";
                gpr_name[4]="a0";   gpr_name[5]="a1";   gpr_name[6]="a2";  gpr_name[7]="a3";
                gpr_name[8]="t0";   gpr_name[9]="t1";   gpr_name[10]="t2"; gpr_name[11]="t3";
                gpr_name[12]="t4";  gpr_name[13]="t5";  gpr_name[14]="t6"; gpr_name[15]="t7";
                gpr_name[16]="s0";  gpr_name[17]="s1";  gpr_name[18]="s2"; gpr_name[19]="s3";
                gpr_name[20]="s4";  gpr_name[21]="s5";  gpr_name[22]="s6"; gpr_name[23]="s7";
                gpr_name[24]="t8";  gpr_name[25]="t9";  gpr_name[26]="k0"; gpr_name[27]="k1";
                gpr_name[28]="gp";  gpr_name[29]="sp";  gpr_name[30]="fp"; gpr_name[31]="ra";

                fstore_n = (ch212_fstore_count < CH212_FSTORE_DEPTH)
                            ? ch212_fstore_count : CH212_FSTORE_DEPTH;

                $display("[ch212] FRAME_STORES total=%0d (SW into phys [0x0000B1E0,0x0000B230), oldest first):",
                         ch212_fstore_count);
                for (int i = 0; i < fstore_n; i++) begin
                    int slot;
                    int offset;
                    slot = (ch212_fstore_count - fstore_n + i) % CH212_FSTORE_DEPTH;
                    offset = ch212_fstore_ea[slot][8:0] - 9'h1E0;
                    $display("[ch212]   [%0d] cyc=%0d pc=0x%08h ea=0x%08h offset=+0x%02h source=$%s data=0x%08h",
                             i, ch212_fstore_cyc[slot],
                             ch212_fstore_pc[slot],
                             ch212_fstore_ea[slot],
                             offset[7:0],
                             gpr_name[ch212_fstore_rt[slot]],
                             ch212_fstore_data[slot]);
                end

                $display("[ch212] STATIC_DECODE save routine at PC=0xBFC4DB40..0xBFC4DBA0:");
                for (logic [31:0] a = 32'hBFC4_DB40; a < 32'hBFC4_DBA0; a += 4) begin
                    logic [31:0] w;
                    w = peek_instr(a);
                    $display("[ch212]   0x%08h: 0x%08h  %s%s",
                             a, w, mips_mnemonic(w),
                             (a == 32'hBFC4_DB58) ? "  <-- save site observed dynamically" : "");
                end

                // Peek what's at PC=0xBFC52350 (= value at frame
                // offset +0x00 at run end). If it's executable code,
                // dump 8 instructions to see what kind of code it is.
                field00_val = peek_instr(32'hA000_B1E0);
                $display("[ch212] FIELD+0x00 currently holds 0x%08h; peek 8 words at that PC:",
                         field00_val);
                for (logic [31:0] k = 0; k < 32; k += 4) begin
                    logic [31:0] w;
                    w = peek_instr(field00_val + k);
                    $display("[ch212]   0x%08h: 0x%08h  %s",
                             field00_val + k, w, mips_mnemonic(w));
                end

                // Comparison at 2nd RFE: context+0x00 vs current $k0
                if (ch204_second_rfe_captured) begin
                    $display("[ch212] AT_2ND_RFE: context_field+0x00=0x%08h ; $k0_at_2nd_RFE=0x%08h",
                             field00_val, ch204_second_rfe_k0);
                    if (field00_val == ch204_second_rfe_k0) begin
                        ch212_verdict = "field00_equals_k0_at_rfe (the saved-PC at frame +0x00 matches $k0 at SYSCALL #8 — both point at the same resume target; restore routine could use either)";
                    end else begin
                        ch212_verdict = $sformatf("field00_differs_from_k0 (frame +0x00=0x%08h vs $k0_at_2nd_RFE=0x%08h — they point at DIFFERENT PCs. The +0x00 field is NOT just $k0; it carries an independent saved-PC. Ch213 needs to choose: if the BIOS restore routine LWs PC from +0x00, that becomes the resume target; if it uses $k0 directly, +0x00 is metadata.)",
                                                  field00_val,
                                                  ch204_second_rfe_k0);
                    end
                end else begin
                    ch212_verdict = "no_2nd_rfe_captured";
                end
                $display("[ch212] verdict=%s", ch212_verdict);
            end

            // --------------------------------------------------------
            // Ch213 — frame-pointer source investigation. Reports:
            //  (i) Whether any GPR at SYSCALL #8 retire holds the
            //      jmp_buf address (0xA000B1E0 / 0x8000B1E0).
            //  (ii) Stores observed earlier in the run that landed
            //      values 0xA000B1E0 / 0x8000B1E0 (from Ch205 store
            //      ring — scan at end of run).
            //  (iii) Static decode of 0xBFC4DB30..0xBFC4DB50 (the
            //      bytes immediately before the SAVE entry) and
            //      0xBFC4DB80..0xBFC4DBA0 (after RESTORE entry).
            // The verdict names whether we have proven source vs
            // need the CH213_JMPBUF_FALLBACK label.
            // --------------------------------------------------------
            if (ch213_sc8_seen) begin
                int matched_reg;
                int store_hits;
                int store_ring_n;
                string gpr_name [0:31];
                string ch213_verdict;

                gpr_name[0]="zero"; gpr_name[1]="at";   gpr_name[2]="v0";  gpr_name[3]="v1";
                gpr_name[4]="a0";   gpr_name[5]="a1";   gpr_name[6]="a2";  gpr_name[7]="a3";
                gpr_name[8]="t0";   gpr_name[9]="t1";   gpr_name[10]="t2"; gpr_name[11]="t3";
                gpr_name[12]="t4";  gpr_name[13]="t5";  gpr_name[14]="t6"; gpr_name[15]="t7";
                gpr_name[16]="s0";  gpr_name[17]="s1";  gpr_name[18]="s2"; gpr_name[19]="s3";
                gpr_name[20]="s4";  gpr_name[21]="s5";  gpr_name[22]="s6"; gpr_name[23]="s7";
                gpr_name[24]="t8";  gpr_name[25]="t9";  gpr_name[26]="k0"; gpr_name[27]="k1";
                gpr_name[28]="gp";  gpr_name[29]="sp";  gpr_name[30]="fp"; gpr_name[31]="ra";

                matched_reg = -1;
                for (int i = 0; i < 32; i++) begin
                    if (matched_reg < 0
                            && (ch213_sc8_regs[i] == 32'hA000_B1E0
                             || ch213_sc8_regs[i] == 32'h8000_B1E0))
                        matched_reg = i;
                end

                if (matched_reg >= 0)
                    $display("[ch213] FRAME_PTR_AT_SC8: $%s = 0x%08h (matches jmp_buf address)",
                             gpr_name[matched_reg], ch213_sc8_regs[matched_reg]);
                else
                    $display("[ch213] FRAME_PTR_AT_SC8: NO GPR holds 0xA000B1E0 or 0x8000B1E0 at SYSCALL retire — handler must derive the address from elsewhere (hardcoded, $gp-relative, or a global pointer)");

                // Search the Ch205 store ring for writes of frame-ptr value.
                store_hits = 0;
                store_ring_n = (ch205_store_count < CH205_STORE_DEPTH)
                                ? ch205_store_count : CH205_STORE_DEPTH;
                $display("[ch213] STORE_RING data==frame_ptr scan (last %0d EE stores):",
                         store_ring_n);
                for (int i = 0; i < store_ring_n; i++) begin
                    int slot;
                    slot = (ch205_store_count - 1 - i + CH205_STORE_DEPTH) % CH205_STORE_DEPTH;
                    if (ch205_store_data[slot] == 32'hA000_B1E0
                         || ch205_store_data[slot] == 32'h8000_B1E0) begin
                        store_hits = store_hits + 1;
                        $display("[ch213]   [%0d] cyc=%0d pc=0x%08h ea=0x%08h data=0x%08h",
                                 i, ch205_store_cyc[slot],
                                 ch205_store_pc[slot], ch205_store_ea[slot],
                                 ch205_store_data[slot]);
                    end
                end
                $display("[ch213] STORE_RING matches=%0d", store_hits);

                // Static decode around save/restore entry.
                $display("[ch213] STATIC_DECODE pre-save 0xBFC4DB30..0xBFC4DB50:");
                for (logic [31:0] a = 32'hBFC4_DB30; a < 32'hBFC4_DB50; a += 4) begin
                    $display("[ch213]   0x%08h: 0x%08h  %s",
                             a, peek_instr(a), mips_mnemonic(peek_instr(a)));
                end
                $display("[ch213] STATIC_DECODE post-restore 0xBFC4DB80..0xBFC4DBB0:");
                for (logic [31:0] a = 32'hBFC4_DB80; a < 32'hBFC4_DBB0; a += 4) begin
                    $display("[ch213]   0x%08h: 0x%08h  %s",
                             a, peek_instr(a), mips_mnemonic(peek_instr(a)));
                end

                // Verdict: was the frame pointer found?
                if (matched_reg >= 0) begin
                    ch213_verdict = $sformatf("frame_ptr_source_proven (carried in $%s at SYSCALL retire — Ch213-RTL can read from regfile[%0d])",
                                              gpr_name[matched_reg], matched_reg);
                end else if (store_hits > 0) begin
                    ch213_verdict = $sformatf("frame_ptr_source_via_stored_global (%0d store-hits in ring — the pointer was written to memory; restore would need to LW from that location)",
                                              store_hits);
                end else begin
                    ch213_verdict = "frame_ptr_source_unproven (no GPR holds it at SC8, no stores of the value in the late-run ring; the kernel handler likely derives 0xA000B1E0 via a hardcoded LUI+ORI/ADDIU pair inside the kernel's syscall #8 entry which we don't model. Use CH213_JMPBUF_FALLBACK with explicit label.)";
                end
                $display("[ch213] verdict=%s", ch213_verdict);
            end else begin
                $display("[ch213] no SYSCALL #8 retire captured this run");
            end

            // --------------------------------------------------------
            // Ch214 — decode setjmp call site to find the frame-ptr
            // origin (per Codex: no restore yet; just nail down where
            // the BIOS computed 0xA000B1E0 to begin with).
            // --------------------------------------------------------
            begin : ch214_block
                logic [31:0] caller_pc;
                logic [31:0] call_site_pc;
                int          bios_lui_a000_hits;
                int          bios_lui_a001_hits;
                int          bios_gp_relative_hits;
                int          bios_ori_b1e0_hits;
                int          bios_addiu_negs_hits;
                string       ch214_verdict;

                if (ch214_save_seen) begin
                    caller_pc    = ch214_save_caller_pc;
                    call_site_pc = caller_pc - 32'd8;   // PC of the JAL
                    $display("[ch214] SETJMP_CALLS total=%0d first cyc=%0d caller_link_pc=0x%08h call_site_pc=0x%08h $a0=0x%08h $gp=0x%08h $sp=0x%08h",
                             ch214_save_count,
                             ch214_save_cyc,
                             ch214_save_caller_pc,
                             call_site_pc,
                             ch214_save_a0,
                             ch214_save_gp,
                             ch214_save_sp);

                    $display("[ch214] CALLER_DECODE 0xBFC52340..0xBFC52360 (around expected JAL at 0x%08h):",
                             call_site_pc);
                    for (logic [31:0] a = 32'hBFC5_2340; a < 32'hBFC5_2360; a += 4) begin
                        logic [31:0] w;
                        w = peek_instr(a);
                        $display("[ch214]   0x%08h: 0x%08h  %s%s",
                                 a, w, mips_mnemonic(w),
                                 (a == call_site_pc) ? "  <-- expected jal 0xBFC4DB50" :
                                 (a == call_site_pc - 4) ? "  <-- expected $a0 setup (delay slot precedes JAL on MIPS — actually pre-JAL)" : "");
                    end
                end else begin
                    $display("[ch214] SETJMP_CALLS none observed (PC=0xBFC4DB50 never retired)");
                end

                if (ch214_restore_seen) begin
                    $display("[ch214] LONGJMP_CALLS total=%0d first cyc=%0d caller_link_pc=0x%08h $a0=0x%08h $gp=0x%08h",
                             ch214_restore_count,
                             ch214_restore_cyc,
                             ch214_restore_caller_pc,
                             ch214_restore_a0,
                             ch214_restore_gp);
                end else begin
                    $display("[ch214] LONGJMP_CALLS none observed (PC=0xBFC4DB8C never retired) — BIOS never explicitly called longjmp in this run. Means: any context unwind would have to come from inside syscall #8 itself, NOT via BIOS user-mode control flow.");
                end

                // BIOS-wide pattern scan. The BIOS ROM is 4 MiB = 1M
                // words; iterate u_bios.mem and count specific
                // instruction patterns that could compute 0xA000B1E0.
                bios_lui_a000_hits     = 0;
                bios_lui_a001_hits     = 0;
                bios_gp_relative_hits  = 0;
                bios_ori_b1e0_hits     = 0;
                bios_addiu_negs_hits   = 0;
                for (int idx = 0; idx < (BIOS_BYTES / 4); idx++) begin
                    logic [31:0] w;
                    w = u_bios.mem[idx];
                    // lui $r, 0xA000 (op=0x0F, rs=0, imm=0xA000)
                    if (w[31:26] == 6'h0F && w[25:21] == 5'd0
                            && w[15:0] == 16'hA000)
                        bios_lui_a000_hits = bios_lui_a000_hits + 1;
                    // lui $r, 0xA001
                    if (w[31:26] == 6'h0F && w[25:21] == 5'd0
                            && w[15:0] == 16'hA001)
                        bios_lui_a001_hits = bios_lui_a001_hits + 1;
                    // ori $r, ?, 0xB1E0 (op=0x0D, imm=0xB1E0)
                    if (w[31:26] == 6'h0D && w[15:0] == 16'hB1E0)
                        bios_ori_b1e0_hits = bios_ori_b1e0_hits + 1;
                    // addiu $r, ?, -0x4E20 (op=0x09, imm=0xB1E0 = -0x4E20 sign-extended → wait, 0xB1E0 = -0x4E20? Let's check: -0x4E20 = 0xFFFFB1E0; lower 16 bits = 0xB1E0 ✓)
                    if (w[31:26] == 6'h09 && w[15:0] == 16'hB1E0)
                        bios_addiu_negs_hits = bios_addiu_negs_hits + 1;
                    // addiu $r, $gp, -0x5690 (op=0x09, rs=$28, imm=0xA970)
                    if (w[31:26] == 6'h09 && w[25:21] == 5'd28
                            && w[15:0] == 16'hA970)
                        bios_gp_relative_hits = bios_gp_relative_hits + 1;
                end

                $display("[ch214] BIOS_PATTERN_SCAN (1M words):");
                $display("[ch214]   `lui $r, 0xA000` (= load 0xA0000000): %0d hits",
                         bios_lui_a000_hits);
                $display("[ch214]   `lui $r, 0xA001` (= load 0xA0010000): %0d hits",
                         bios_lui_a001_hits);
                $display("[ch214]   `ori $r, ?, 0xB1E0` (low half of 0xA000B1E0): %0d hits",
                         bios_ori_b1e0_hits);
                $display("[ch214]   `addiu $r, ?, -0x4E20` (imm=0xB1E0): %0d hits",
                         bios_addiu_negs_hits);
                $display("[ch214]   `addiu $r, $gp, -0x5690` (imm=0xA970, where $gp=0xA0010870 ⇒ 0xA000B1E0): %0d hits",
                         bios_gp_relative_hits);

                // Verdict.
                if (ch214_save_seen && ch214_save_a0 == 32'hA000_B1E0) begin
                    if (bios_gp_relative_hits > 0) begin
                        ch214_verdict = $sformatf("frame_ptr_via_gp_relative ($a0=0xA000B1E0 at setjmp entry matches $gp(=0x%08h) - 0x5690; BIOS-wide scan found %0d `addiu $r, $gp, -0x5690` matches — the frame pointer is computed via $gp-relative addressing from a static $gp=0xA0010870 base. Real PS2 syscall #8 handler would use the same $gp-relative computation to find the jmp_buf.)",
                                                  ch214_save_gp,
                                                  bios_gp_relative_hits);
                    end else if (bios_lui_a000_hits > 0 && bios_ori_b1e0_hits > 0) begin
                        ch214_verdict = $sformatf("frame_ptr_via_lui_ori_constant (no $gp-relative match; BIOS-wide scan found %0d `lui ..., 0xA000` and %0d `ori ..., 0xB1E0` candidates — the frame pointer is a hardcoded 32-bit constant assembled via LUI+ORI pair. The kernel handler likely uses the same constant load.)",
                                                  bios_lui_a000_hits,
                                                  bios_ori_b1e0_hits);
                    end else if (bios_lui_a001_hits > 0 && bios_addiu_negs_hits > 0) begin
                        ch214_verdict = $sformatf("frame_ptr_via_lui_addiu_signed (BIOS-wide scan found %0d `lui ..., 0xA001` and %0d `addiu ..., -0x4E20` candidates — frame pointer assembled via LUI+ADDIU(signed) pair.)",
                                                  bios_lui_a001_hits,
                                                  bios_addiu_negs_hits);
                    end else begin
                        ch214_verdict = $sformatf("frame_ptr_origin_indeterminate ($a0=0xA000B1E0 confirmed at setjmp entry but no obvious pattern in BIOS scan)");
                    end
                end else begin
                    ch214_verdict = "no_setjmp_call_observed";
                end
                $display("[ch214] verdict=%s", ch214_verdict);
            end

            // Ch216 — classify the 5-restore pattern. Print task
            // defined below; called from both post-halt and timeout
            // paths so the verdict surfaces regardless of exit mode.
            ch216_print_classification();

            // Ch217 — longjmp-return path inspection (call here so the
            // halt path also sees it; the timeout path mirrors the call).
            if (ch217_count > 0)
                ch217_print_longjmp_path();

            // Ch260 — Ch218 INTC-diagnostic print is opt-in. Routine
            // runs leave it quiet; rebuild with
            // `make tb_ee_core_bios_long_intc_diag` to see the INTC
            // transaction log + verdict.
`ifdef CH259_INTC_DIAG
            ch218_print_callee_reads();
`endif

`ifdef CH264_CALLEE_AUTOPSY
            ch264_print_autopsy();
`endif

`ifdef CH265_HELPER_AUTOPSY
            ch265_print_autopsy();
`endif

`ifdef CH266_DISPATCHER_AUTOPSY
            ch266_print_autopsy();
`endif

`ifdef CH267_GATE_OBSERVER
            ch267_print_observer();
`endif

`ifdef CH268_OUTER_READ_AUTOPSY
            ch268_print_autopsy();
`endif

`ifdef CH269_V0_LINEAGE
            ch269_print_observer();
`endif

            // Final small stack window around $sp at epilog time.
            $display("[ch205] STACK_WINDOW $sp=0x%08h, 16 words upward:",
                     ch205_lw_sp);
            for (int k = 0; k < 16; k++) begin
                logic [31:0] a, w;
                string annot;
                a = ch205_lw_sp + (k * 4);
                w = peek_instr(a);
                annot = (a == ch205_lw_target_ea)
                          ? "  <-- $sp+0x1C = target_ea (saved $ra slot)" : "";
                $display("[ch205]   sp+0x%02h (0x%08h): 0x%08h%s",
                         k*4, a, w, annot);
            end
        end

        // --------------------------------------------------------------
        // Chapter 35(a) — runway + prehist dump (fires in both modes,
        // but only yields evidence in real-BIOS; synthetic bootstrap
        // never touches the 0x1C0..0x220 window).
        // --------------------------------------------------------------
        if (runway_prehist_captured) begin
            $display("[tb_ee_core_bios_smoke] RUNWAY count=%0d (first %0d captured, retires in 0x%04h-0x%04h):",
                     runway_count,
                     (runway_count < RUNWAY_DEPTH) ? runway_count : RUNWAY_DEPTH,
                     RUNWAY_LOW[15:0], RUNWAY_HIGH[15:0]);
            for (int i = 0; i < RUNWAY_DEPTH; i++) begin
                if (i < runway_count)
                    $display("[tb_ee_core_bios_smoke] RUNWAY[%0d] cyc=%0d pc=0x%08h instr=0x%08h $31=0x%08h",
                             i, runway_cyc[i], runway_pc[i],
                             runway_instr[i], runway_r31[i]);
            end
            $display("[tb_ee_core_bios_smoke] RUNWAY_PREHIST newest-first (the %0d retires immediately preceding the first runway arrival — originator site at [0]):",
                     RUNWAY_PREHIST_DEPTH);
            for (int i = 0; i < RUNWAY_PREHIST_DEPTH; i++) begin
                $display("[tb_ee_core_bios_smoke] RUNWAY_PREHIST[%0d] cyc=%0d pc=0x%08h instr=0x%08h",
                         i, runway_prehist_cyc[i],
                         runway_prehist_pc[i],
                         runway_prehist_instr[i]);
            end
        end else begin
            $display("[tb_ee_core_bios_smoke] RUNWAY never entered (no retire in 0x%04h-0x%04h)",
                     RUNWAY_LOW[15:0], RUNWAY_HIGH[15:0]);
        end

        // --------------------------------------------------------------
        // Chapter 36 — classify the $31 transition. Opcode/func
        // decode at print time tells us JAL vs JALR vs anything
        // weirder. For JAL, compute the jump target from the 26-bit
        // immediate (shifted left 2, upper nibble from pc+4). For
        // JALR, the target is whatever rs_val was at retire.
        // --------------------------------------------------------------
        if (r31_to_target_captured) begin
            logic [31:0] jal_target;
            logic [31:0] delay_pc;
            delay_pc   = r31_to_target_retired_pc + 32'd4;
            jal_target = {delay_pc[31:28],
                          r31_to_target_retired_instr[25:0], 2'b00};
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET summary cycle=%0d",
                     r31_to_target_cycle);
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   retired_pc=0x%08h retired_instr=0x%08h prev_r31=0x%08h",
                     r31_to_target_retired_pc,
                     r31_to_target_retired_instr,
                     r31_to_target_prev_r31);
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   kind op=0x%02h func=0x%02h => %s",
                     r31_to_target_retired_instr[31:26],
                     r31_to_target_retired_instr[5:0],
                     (r31_to_target_retired_instr[31:26] == 6'h03) ? "JAL (direct link, target from imm26)" :
                     (r31_to_target_retired_instr[31:26] == 6'h00 &&
                      r31_to_target_retired_instr[5:0]   == 6'h09) ? "JALR (indirect link via rs_val)" :
                     (r31_to_target_retired_instr[31:26] == 6'h00 &&
                      r31_to_target_retired_instr[5:0]   == 6'h08) ? "JR (no-link — $31 write unexpected)" :
                     "OTHER (explicit ALU/mov to $31 — rare)");
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   decoded rs=$%0d rt=$%0d rd=$%0d rs_val=0x%08h imm26=0x%07h",
                     r31_to_target_rs_idx, r31_to_target_rt_idx,
                     r31_to_target_rd_idx, r31_to_target_rs_val,
                     r31_to_target_retired_instr[25:0]);
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   JAL-derived target = 0x%08h  (only meaningful if op=0x03)",
                     jal_target);
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   JALR-target (rs_val) = 0x%08h  (only meaningful if JALR)",
                     r31_to_target_rs_val);
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   trampoline $8=0x%08h $9=0x%08h",
                     r31_to_target_r8, r31_to_target_r9);
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   ring-1 pc=0x%08h instr=0x%08h",
                     r31_to_target_ring_prev_pc,
                     r31_to_target_ring_prev_instr);
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET   ring-2 pc=0x%08h instr=0x%08h",
                     r31_to_target_ring_prev2_pc,
                     r31_to_target_ring_prev2_instr);
        end else begin
            $display("[tb_ee_core_bios_smoke] R31_TO_TARGET never fired ($31 never transitioned to 0x%08h)",
                     RA_TARGET_VALUE);
        end

        // --------------------------------------------------------------
        // Chapter 37 — classify the ROM-escape retire's ring-2
        // (branch/jump site) opcode. Reinforce the non-linking-escape
        // reading by calling out r31_unchanged explicitly.
        // --------------------------------------------------------------
        if (rom_escape_captured) begin
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE summary cycle=%0d",
                     rom_escape_cycle);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   retired_pc=0x%08h retired_instr=0x%08h",
                     rom_escape_retired_pc, rom_escape_retired_instr);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   ring-1 pc=0x%08h instr=0x%08h",
                     rom_escape_ring_prev_pc,
                     rom_escape_ring_prev_instr);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   ring-2 pc=0x%08h instr=0x%08h",
                     rom_escape_ring_prev2_pc,
                     rom_escape_ring_prev2_instr);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   ring-2-kind op=0x%02h func=0x%02h rs=$%0d rt=$%0d rd=$%0d => %s",
                     rom_escape_ring_prev2_instr[31:26],
                     rom_escape_ring_prev2_instr[5:0],
                     rom_escape_ring_prev2_instr[25:21],
                     rom_escape_ring_prev2_instr[20:16],
                     rom_escape_ring_prev2_instr[15:11],
                     (rom_escape_ring_prev2_instr[31:26] == 6'h02) ? "J (direct jump, target from imm26)" :
                     (rom_escape_ring_prev2_instr[31:26] == 6'h03) ? "JAL (direct call — unexpected here, ch36 already captured the last link)" :
                     (rom_escape_ring_prev2_instr[31:26] == 6'h00 &&
                      rom_escape_ring_prev2_instr[5:0]   == 6'h08) ? "JR (indirect jump via rs_val — likely escape via stale register)" :
                     (rom_escape_ring_prev2_instr[31:26] == 6'h00 &&
                      rom_escape_ring_prev2_instr[5:0]   == 6'h09) ? "JALR (indirect call)" :
                     (rom_escape_ring_prev2_instr[31:26] == 6'h04) ? "BEQ (taken)" :
                     (rom_escape_ring_prev2_instr[31:26] == 6'h05) ? "BNE (taken)" :
                     (rom_escape_ring_prev2_instr[31:26] == 6'h06) ? "BLEZ (taken)" :
                     (rom_escape_ring_prev2_instr[31:26] == 6'h07) ? "BGTZ (taken)" :
                     "OTHER (not a branch/jump — exception-vector-like or sequential fall-through)");
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   trampoline $8=0x%08h $9=0x%08h $10=0x%08h $31=0x%08h",
                     rom_escape_r8, rom_escape_r9, rom_escape_r10,
                     rom_escape_r31);
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE   r31_unchanged_since_ch36_JAL=%0d (1 => non-linking escape: no intervening JAL/JALR/explicit-$31-write)",
                     rom_escape_r31_unchanged);
        end else begin
            $display("[tb_ee_core_bios_smoke] ROM_ESCAPE never fired (execution never reached low-RAM pc[31:28]==0 after R31_TO_TARGET)");
        end

        // --------------------------------------------------------------
        // Chapter 45 — full ESCAPE_RING dump. One line per entry
        // showing landing pc/instr, local ring-1/ring-2, the
        // walk-back control-flow site (which can be further back
        // than ring-2 when a fall-through NOP corridor separates
        // the driver from the landing retire), live trampoline
        // regs, and a classification string naming the exact
        // opcode family + source register for JR/JALR.
        // --------------------------------------------------------------
        $display("[tb_ee_core_bios_smoke] ESCAPE_RING total=%0d  most-recent %0d kept (circular; depth=%0d):",
                 escape_ring_count,
                 (escape_ring_count < ESCAPE_RING_DEPTH)
                   ? escape_ring_count : ESCAPE_RING_DEPTH,
                 ESCAPE_RING_DEPTH);
        // Print each live ring slot. A slot is live iff its cycle
        // field is non-zero (we zero-init). Cycles are printed so
        // chronology is obvious even though physical slot order is
        // dictated by the circular write index.
        for (int i = 0; i < ESCAPE_RING_DEPTH; i++) begin
            if (escape_ring_cyc[i] != 0) begin
                $display("[tb_ee_core_bios_smoke] ESCAPE_RING[slot=%0d] landing cyc=%0d pc=0x%08h instr=0x%08h",
                         i, escape_ring_cyc[i],
                         escape_ring_pc[i], escape_ring_instr[i]);
                $display("[tb_ee_core_bios_smoke] ESCAPE_RING[slot=%0d]   ring-1 pc=0x%08h instr=0x%08h cyc=%0d",
                         i, escape_ring_r1_pc[i],
                         escape_ring_r1_instr[i], escape_ring_r1_cyc[i]);
                $display("[tb_ee_core_bios_smoke] ESCAPE_RING[slot=%0d]   ring-2 pc=0x%08h instr=0x%08h cyc=%0d",
                         i, escape_ring_r2_pc[i],
                         escape_ring_r2_instr[i], escape_ring_r2_cyc[i]);
                if (escape_ring_cf_found[i]) begin
                    $display("[tb_ee_core_bios_smoke] ESCAPE_RING[slot=%0d]   walkback pc=0x%08h instr=0x%08h cyc=%0d hops=%0d => %s",
                             i, escape_ring_cf_pc[i],
                             escape_ring_cf_instr[i],
                             escape_ring_cf_cyc[i],
                             escape_ring_cf_hops[i],
                             (escape_ring_cf_instr[i][31:26] == 6'h02) ?
                               "J (direct jump, target from imm26)" :
                             (escape_ring_cf_instr[i][31:26] == 6'h03) ?
                               "JAL (direct link call)" :
                             (escape_ring_cf_instr[i][31:26] == 6'h00 &&
                              escape_ring_cf_instr[i][5:0]   == 6'h08) ?
                               "JR (indirect via rs)" :
                             (escape_ring_cf_instr[i][31:26] == 6'h00 &&
                              escape_ring_cf_instr[i][5:0]   == 6'h09) ?
                               "JALR (indirect link via rs)" :
                               "UNKNOWN-CF");
                    if ((escape_ring_cf_instr[i][31:26] == 6'h00) &&
                        ((escape_ring_cf_instr[i][5:0] == 6'h08) ||
                         (escape_ring_cf_instr[i][5:0] == 6'h09)))
                        $display("[tb_ee_core_bios_smoke] ESCAPE_RING[slot=%0d]   driver-register: $%0d (rs field of JR/JALR)",
                                 i, escape_ring_cf_rs[i]);
                end else begin
                    $display("[tb_ee_core_bios_smoke] ESCAPE_RING[slot=%0d]   walkback: NO J/JAL/JR/JALR found within %0d retire-ring slots",
                             i, RETIRE_RING_DEPTH - 1);
                end
                $display("[tb_ee_core_bios_smoke] ESCAPE_RING[slot=%0d]   regs $8=0x%08h $9=0x%08h $10=0x%08h $31=0x%08h",
                         i, escape_ring_r8[i], escape_ring_r9[i],
                         escape_ring_r10[i], escape_ring_r31[i]);
            end
        end

        // --------------------------------------------------------------
        // Chapter 38 — $10 provenance summary + classifier.
        // Classify the writer: LW (table load — arg2=EA, arg3=value)
        // vs ADDIU / ORI / LUI / OR (computed) vs other. For the LW
        // case, the EA and loaded value in arg2/arg3 tell us exactly
        // which memory word held the 0xC0.
        // --------------------------------------------------------------
        if (r10_to_c0_captured) begin
            $display("[tb_ee_core_bios_smoke] R10_TO_C0 summary cycle=%0d",
                     r10_to_c0_cycle);
            $display("[tb_ee_core_bios_smoke] R10_TO_C0   retired_pc=0x%08h retired_instr=0x%08h prev_r10=0x%08h",
                     r10_to_c0_retired_pc,
                     r10_to_c0_retired_instr,
                     r10_to_c0_prev_r10);
            $display("[tb_ee_core_bios_smoke] R10_TO_C0   kind op=0x%02h func=0x%02h => %s",
                     r10_to_c0_retired_instr[31:26],
                     r10_to_c0_retired_instr[5:0],
                     (r10_to_c0_retired_instr[31:26] == 6'h23) ? "LW (table load; EA=arg2, loaded value=arg3)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h21) ? "LH (halfword load)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h25) ? "LHU (halfword zero-ext load)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h20) ? "LB (byte load)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h24) ? "LBU (byte zero-ext load)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h09) ? "ADDIU (computed: rs + imm16_sx)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h08) ? "ADDI (computed: rs + imm16_sx, overflow ignored here)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h0D) ? "ORI (computed: rs | imm16_zx)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h0F) ? "LUI (computed: imm16 << 16)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h0C) ? "ANDI (computed: rs & imm16_zx)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h00 &&
                      r10_to_c0_retired_instr[5:0]   == 6'h25) ? "OR (computed: rs | rt — could be mov via $0)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h00 &&
                      r10_to_c0_retired_instr[5:0]   == 6'h21) ? "ADDU (computed: rs + rt)" :
                     (r10_to_c0_retired_instr[31:26] == 6'h00 &&
                      r10_to_c0_retired_instr[5:0]   == 6'h23) ? "SUBU (computed: rs - rt)" :
                     "OTHER (unexpected writer; inspect arg2/arg3/rs/rt/rd below)");
            $display("[tb_ee_core_bios_smoke] R10_TO_C0   decoded rs=$%0d rt=$%0d rd=$%0d rs_val=0x%08h rt_val=0x%08h imm16=0x%04h",
                     r10_to_c0_rs_idx, r10_to_c0_rt_idx,
                     r10_to_c0_rd_idx,
                     r10_to_c0_rs_val, r10_to_c0_rt_val,
                     r10_to_c0_retired_instr[15:0]);
            $display("[tb_ee_core_bios_smoke] R10_TO_C0   arg2=0x%08h arg3=0x%08h  (for LW: arg2=EA, arg3=loaded word)",
                     r10_to_c0_retired_arg2,
                     r10_to_c0_retired_arg3);
            $display("[tb_ee_core_bios_smoke] R10_TO_C0   ring-1 pc=0x%08h instr=0x%08h",
                     r10_to_c0_ring_prev_pc,
                     r10_to_c0_ring_prev_instr);
            $display("[tb_ee_core_bios_smoke] R10_TO_C0   ring-2 pc=0x%08h instr=0x%08h",
                     r10_to_c0_ring_prev2_pc,
                     r10_to_c0_ring_prev2_instr);
        end else begin
            $display("[tb_ee_core_bios_smoke] R10_TO_C0 never fired ($10 never transitioned to 0x%08h)",
                     R10_TARGET_VALUE);
        end

        // --------------------------------------------------------------
        // Chapter 39 — RAM[0xC0..0x1FF] write-provenance summary.
        // If RAM_C0_EXACT never fires: definitive install-phase gap
        // at 0xC0. If RAM_C0_WINDOW stays at count=0: same answer for
        // the whole runway — no writer ever populated the handler
        // stub region.
        // Master-id convention: 0=EE_IFETCH (core store path),
        // 1=DMAC, 5=SIF EE-side bridge, 9=bridge TB preload.
        // --------------------------------------------------------------
        if (ram_c0_exact_captured) begin
            $display("[tb_ee_core_bios_smoke] RAM_C0_EXACT summary: FIRST write to qword 0x%03h (byte 0xC0) at cyc=%0d, master=%0d, be=0x%04h",
                     RAM_C0_QW_IDX, ram_c0_exact_cycle,
                     ram_c0_exact_master, ram_c0_exact_be);
            $display("[tb_ee_core_bios_smoke] RAM_C0_EXACT   data128=0x%032h",
                     ram_c0_exact_data);
            $display("[tb_ee_core_bios_smoke] RAM_C0_EXACT   cpu-ctx at write: retired_pc=0x%08h retired_instr=0x%08h retired_was_write=%0d",
                     ram_c0_exact_retired_pc,
                     ram_c0_exact_retired_instr,
                     ram_c0_exact_retired_was_write);
        end else begin
            $display("[tb_ee_core_bios_smoke] RAM_C0_EXACT never fired: NO writer ever populated the qword at byte 0xC0 across the entire run (install-phase gap confirmed for 0xC0 slot).");
        end
        $display("[tb_ee_core_bios_smoke] RAM_C0_WINDOW count=%0d writes into qwords [0x%03h..0x%03h] (byte 0xC0..0x1FF):",
                 ram_c0_win_count, RAM_WIN_QW_LOW, RAM_WIN_QW_HIGH);
        for (int i = 0; i < RAM_C0_WIN_DEPTH; i++) begin
            if (i < ram_c0_win_count)
                $display("[tb_ee_core_bios_smoke] RAM_C0_WINDOW[%0d] cyc=%0d qw_idx=0x%03h master=%0d be=0x%04h data=0x%032h pc=0x%08h instr=0x%08h",
                         i, ram_c0_win_cyc[i], ram_c0_win_qw_idx[i],
                         ram_c0_win_master[i], ram_c0_win_be[i],
                         ram_c0_win_data[i], ram_c0_win_pc[i],
                         ram_c0_win_instr[i]);
        end
        $display("[tb_ee_core_bios_smoke] CPU_WIN total=%0d most-recent %0d kept (circular depth=%0d) — core-side 32-bit writes whose phys EA is in [0xC0..0x1FF]:",
                 cpu_win_count,
                 (cpu_win_count < CPU_WIN_DEPTH)
                   ? cpu_win_count : CPU_WIN_DEPTH,
                 CPU_WIN_DEPTH);
        for (int i = 0; i < CPU_WIN_DEPTH; i++) begin
            if (cpu_win_cyc[i] != 0) begin
                $display("[tb_ee_core_bios_smoke] CPU_WIN[slot=%0d] cyc=%0d addr=0x%08h (view=%s) be=0x%01h data=0x%08h pc=0x%08h instr=0x%08h",
                         i, cpu_win_cyc[i], cpu_win_addr[i],
                         (cpu_win_addr[i][31:29] == 3'b000) ? "useg" :
                         (cpu_win_addr[i][31:29] == 3'b100) ? "kseg0" :
                         (cpu_win_addr[i][31:29] == 3'b101) ? "kseg1" :
                         (cpu_win_addr[i][31:29] == 3'b110) ? "kseg2" :
                         (cpu_win_addr[i][31:29] == 3'b111) ? "kseg3" : "???",
                         cpu_win_be[i], cpu_win_data[i],
                         cpu_win_pc[i], cpu_win_instr[i]);
                if (cpu_win_cf_found[i])
                    $display("[tb_ee_core_bios_smoke] CPU_WIN[slot=%0d]   walkback pc=0x%08h instr=0x%08h rs=$%0d",
                             i, cpu_win_cf_pc[i],
                             cpu_win_cf_instr[i], cpu_win_cf_rs[i]);
            end
        end

        // --------------------------------------------------------------
        // Ch46 exact-word triggers — pin the first write that
        // overwrites the ch40 stub at phys 0xC0 and the delay-slot
        // NOP at phys 0xC4. View field distinguishes BIOS writing
        // via useg vs kseg0/kseg1.
        // --------------------------------------------------------------
        if (ram_c0w_captured) begin
            $display("[tb_ee_core_bios_smoke] RAM_C0_WORD summary cyc=%0d addr=0x%08h (view=%s) be=0x%01h data=0x%08h",
                     ram_c0w_cycle, ram_c0w_addr,
                     (ram_c0w_addr[31:29] == 3'b000) ? "useg" :
                     (ram_c0w_addr[31:29] == 3'b100) ? "kseg0" :
                     (ram_c0w_addr[31:29] == 3'b101) ? "kseg1" :
                     (ram_c0w_addr[31:29] == 3'b110) ? "kseg2" :
                     (ram_c0w_addr[31:29] == 3'b111) ? "kseg3" : "???",
                     ram_c0w_be, ram_c0w_data);
            $display("[tb_ee_core_bios_smoke] RAM_C0_WORD   cpu-ctx retired_pc=0x%08h retired_instr=0x%08h",
                     ram_c0w_pc, ram_c0w_instr);
            if (ram_c0w_cf_found)
                $display("[tb_ee_core_bios_smoke] RAM_C0_WORD   walkback pc=0x%08h instr=0x%08h",
                         ram_c0w_cf_pc, ram_c0w_cf_instr);
        end else begin
            $display("[tb_ee_core_bios_smoke] RAM_C0_WORD never fired (no core-side SW to phys 0xC0 across the run)");
        end
        if (ram_c4w_captured) begin
            $display("[tb_ee_core_bios_smoke] RAM_C4_WORD summary cyc=%0d addr=0x%08h (view=%s) be=0x%01h data=0x%08h",
                     ram_c4w_cycle, ram_c4w_addr,
                     (ram_c4w_addr[31:29] == 3'b000) ? "useg" :
                     (ram_c4w_addr[31:29] == 3'b100) ? "kseg0" :
                     (ram_c4w_addr[31:29] == 3'b101) ? "kseg1" :
                     (ram_c4w_addr[31:29] == 3'b110) ? "kseg2" :
                     (ram_c4w_addr[31:29] == 3'b111) ? "kseg3" : "???",
                     ram_c4w_be, ram_c4w_data);
            $display("[tb_ee_core_bios_smoke] RAM_C4_WORD   cpu-ctx retired_pc=0x%08h retired_instr=0x%08h",
                     ram_c4w_pc, ram_c4w_instr);
            if (ram_c4w_cf_found)
                $display("[tb_ee_core_bios_smoke] RAM_C4_WORD   walkback pc=0x%08h instr=0x%08h",
                         ram_c4w_cf_pc, ram_c4w_cf_instr);
        end else begin
            $display("[tb_ee_core_bios_smoke] RAM_C4_WORD never fired (no core-side SW to phys 0xC4 across the run)");
        end

        // --------------------------------------------------------------
        // Chapter 51 — post-BEV exception-handler window summary.
        // If EXC_W_WIN total=0, no write to phys [0x80..0xBF]
        // ever fired after Status.BEV was cleared — the BIOS
        // does NOT install a RAM exception handler in our sim's
        // current path. If total>0, dump each write with its
        // view classification + walkback caller so we can see
        // who installs what.
        // --------------------------------------------------------------
        $display("[tb_ee_core_bios_smoke] EXC_W_WIN total=%0d most-recent %0d kept (depth=%0d, phys [0x%04h..0x%04h], BEV-gated):",
                 exc_w_win_count,
                 (exc_w_win_count < EXC_WIN_DEPTH)
                   ? exc_w_win_count : EXC_WIN_DEPTH,
                 EXC_WIN_DEPTH,
                 EXC_WIN_LOW_PHYS[15:0], EXC_WIN_HIGH_PHYS[15:0]);
        for (int i = 0; i < EXC_WIN_DEPTH; i++) begin
            if (exc_w_win_cyc[i] != 0) begin
                $display("[tb_ee_core_bios_smoke] EXC_W_WIN[slot=%0d] cyc=%0d addr=0x%08h (view=%s) be=0x%01h data=0x%08h pc=0x%08h instr=0x%08h",
                         i, exc_w_win_cyc[i], exc_w_win_addr[i],
                         (exc_w_win_addr[i][31:29] == 3'b000) ? "useg" :
                         (exc_w_win_addr[i][31:29] == 3'b100) ? "kseg0" :
                         (exc_w_win_addr[i][31:29] == 3'b101) ? "kseg1" :
                         (exc_w_win_addr[i][31:29] == 3'b110) ? "kseg2" :
                         (exc_w_win_addr[i][31:29] == 3'b111) ? "kseg3" : "???",
                         exc_w_win_be[i], exc_w_win_data[i],
                         exc_w_win_pc[i], exc_w_win_instr[i]);
                if (exc_w_win_cf_found[i])
                    $display("[tb_ee_core_bios_smoke] EXC_W_WIN[slot=%0d]   walkback pc=0x%08h instr=0x%08h rs=$%0d",
                             i, exc_w_win_cf_pc[i],
                             exc_w_win_cf_instr[i],
                             exc_w_win_cf_rs[i]);
            end
        end

        $display("[tb_ee_core_bios_smoke] EXC_FETCH_WIN total=%0d most-recent %0d kept (depth=%0d, useg retires with pc in [0x%04h..0x%04h], BEV-gated):",
                 exc_f_win_count,
                 (exc_f_win_count < EXC_WIN_DEPTH)
                   ? exc_f_win_count : EXC_WIN_DEPTH,
                 EXC_WIN_DEPTH,
                 EXC_WIN_LOW_PHYS[15:0], EXC_WIN_HIGH_PHYS[15:0]);
        for (int i = 0; i < EXC_WIN_DEPTH; i++) begin
            if (exc_f_win_cyc[i] != 0) begin
                $display("[tb_ee_core_bios_smoke] EXC_FETCH_WIN[slot=%0d] cyc=%0d pc=0x%08h instr=0x%08h $31=0x%08h EPC=0x%08h BadVAddr=0x%08h",
                         i, exc_f_win_cyc[i],
                         exc_f_win_pc[i], exc_f_win_instr[i],
                         exc_f_win_r31[i],
                         exc_f_win_epc[i], exc_f_win_badv[i]);
            end
        end

        // --------------------------------------------------------------
        // Chapter 48 — $3 lineage summary at trap time. Prints the
        // live final $3, then up to R3_RING_DEPTH most-recent
        // writes with a classification of each producer based on
        // opcode/func (LW = table load; ADDIU/ORI/LUI = immediate
        // build; OR/ADDU/SUBU = register copy / compute).
        // --------------------------------------------------------------
        $display("[tb_ee_core_bios_smoke] R3_FINAL value=0x%08h (at halt/trap)",
                 u_core.regfile[3]);
        $display("[tb_ee_core_bios_smoke] R3_RING total=%0d most-recent %0d kept (circular; depth=%0d):",
                 r3_ring_count,
                 (r3_ring_count < R3_RING_DEPTH)
                   ? r3_ring_count : R3_RING_DEPTH,
                 R3_RING_DEPTH);
        for (int i = 0; i < R3_RING_DEPTH; i++) begin
            if (r3_ring_cyc[i] != 0) begin
                $display("[tb_ee_core_bios_smoke] R3_RING[slot=%0d] cyc=%0d prev=0x%08h new=0x%08h pc=0x%08h instr=0x%08h",
                         i, r3_ring_cyc[i],
                         r3_ring_prev[i], r3_ring_new[i],
                         r3_ring_pc[i], r3_ring_instr[i]);
                $display("[tb_ee_core_bios_smoke] R3_RING[slot=%0d]   kind op=0x%02h func=0x%02h => %s",
                         i,
                         r3_ring_instr[i][31:26],
                         r3_ring_instr[i][5:0],
                         (r3_ring_instr[i][31:26] == 6'h23) ? "LW  (table load; arg2=EA, arg3=loaded value)" :
                         (r3_ring_instr[i][31:26] == 6'h21) ? "LH  (halfword load, sign-extended)" :
                         (r3_ring_instr[i][31:26] == 6'h25) ? "LHU (halfword load, zero-extended)" :
                         (r3_ring_instr[i][31:26] == 6'h20) ? "LB  (byte load, sign-extended)" :
                         (r3_ring_instr[i][31:26] == 6'h24) ? "LBU (byte load, zero-extended)" :
                         (r3_ring_instr[i][31:26] == 6'h09) ? "ADDIU (build: rs + imm16_sx)" :
                         (r3_ring_instr[i][31:26] == 6'h08) ? "ADDI  (build: rs + imm16_sx)" :
                         (r3_ring_instr[i][31:26] == 6'h0D) ? "ORI   (build: rs | imm16_zx)" :
                         (r3_ring_instr[i][31:26] == 6'h0F) ? "LUI   (build: imm16 << 16)" :
                         (r3_ring_instr[i][31:26] == 6'h0C) ? "ANDI  (build: rs & imm16_zx)" :
                         (r3_ring_instr[i][31:26] == 6'h00 &&
                          r3_ring_instr[i][5:0]   == 6'h25) ? "OR   (copy/compute: rs | rt)" :
                         (r3_ring_instr[i][31:26] == 6'h00 &&
                          r3_ring_instr[i][5:0]   == 6'h21) ? "ADDU (copy/compute: rs + rt)" :
                         (r3_ring_instr[i][31:26] == 6'h00 &&
                          r3_ring_instr[i][5:0]   == 6'h23) ? "SUBU (copy/compute: rs - rt)" :
                         (r3_ring_instr[i][31:26] == 6'h00 &&
                          r3_ring_instr[i][5:0]   == 6'h24) ? "AND  (copy/compute: rs & rt)" :
                         (r3_ring_instr[i][31:26] == 6'h00 &&
                          r3_ring_instr[i][5:0]   == 6'h27) ? "NOR  (copy/compute: ~(rs | rt))" :
                         "OTHER (inspect arg2/arg3 manually)");
                $display("[tb_ee_core_bios_smoke] R3_RING[slot=%0d]   arg2=0x%08h arg3=0x%08h",
                         i, r3_ring_arg2[i], r3_ring_arg3[i]);
            end
        end
        if (r3_ring_instr[(r3_ring_count - 1) % R3_RING_DEPTH][31:26] == 6'h23) begin
            logic [31:0] last_ea;
            last_ea = r3_ring_arg2[(r3_ring_count - 1) % R3_RING_DEPTH];
            $display("[tb_ee_core_bios_smoke] R3_LAST_LW_EA = 0x%08h  (low 2 bits = %0d%0d — word-aligned iff 00)",
                     last_ea, last_ea[1], last_ea[0]);
        end

        // --------------------------------------------------------------
        // Ch58 — dispatch-slot profiler dump.
        //
        // Two tables:
        //   (1) per-slot hit counts in [0x90..0x1FC] (word-aligned),
        //       showing every slot that was actually exercised;
        //   (2) SLOT_HIT_RING — newest-first listing of the most
        //       recent SLOT_RING_DEPTH low-RAM retires, with caller
        //       context (prev retire), $v0/$a0-$a3, $9, $ra.
        // --------------------------------------------------------------
        $display("[tb_ee_core_bios_smoke] SLOT_HIT total=%0d (retires with pc in [0x%04h..0x%04h))",
                 slot_hit_total, SLOT_LO[15:0], SLOT_HI[15:0]);
        // Count table — only print non-zero slots to keep the output
        // short. Address reconstructed from index: pc = 0x80 + i*4.
        for (int i = 0; i < SLOT_COUNT_SLOTS; i = i + 1) begin
            if (slot_count[i] > 0) begin
                $display("[tb_ee_core_bios_smoke] SLOT_HIT count[0x%03h] = %0d  (first cyc=%0d last cyc=%0d)",
                         32'h80 + i * 4,
                         slot_count[i],
                         slot_first_cyc[i],
                         slot_last_cyc[i]);
            end
        end
        $display("[tb_ee_core_bios_smoke] SLOT_RING total=%0d most-recent %0d kept (circular; depth=%0d):",
                 slot_ring_count,
                 (slot_ring_count < SLOT_RING_DEPTH)
                    ? slot_ring_count : SLOT_RING_DEPTH,
                 SLOT_RING_DEPTH);
        for (int i = 0; i < SLOT_RING_DEPTH; i = i + 1) begin
            if (slot_ring_cyc[i] != 0) begin
                $display("[tb_ee_core_bios_smoke] SLOT_RING[slot=%0d] cyc=%0d pc=0x%08h instr=0x%08h  prev pc=0x%08h instr=0x%08h cyc=%0d",
                         i, slot_ring_cyc[i],
                         slot_ring_pc[i], slot_ring_instr[i],
                         slot_ring_prev_pc[i], slot_ring_prev_instr[i],
                         slot_ring_prev_cyc[i]);
                $display("[tb_ee_core_bios_smoke] SLOT_RING[slot=%0d]   $2=0x%08h $4=0x%08h $5=0x%08h $6=0x%08h $7=0x%08h $9=0x%08h $31=0x%08h",
                         i,
                         slot_ring_r2[i],  slot_ring_r4[i],
                         slot_ring_r5[i],  slot_ring_r6[i],
                         slot_ring_r7[i],  slot_ring_r9[i],
                         slot_ring_r31[i]);
                // Classify the previous retire for at-a-glance caller
                // shape: JR / JALR / J / JAL / other.
                $display("[tb_ee_core_bios_smoke] SLOT_RING[slot=%0d]   prev kind op=0x%02h func=0x%02h => %s",
                         i,
                         slot_ring_prev_instr[i][31:26],
                         slot_ring_prev_instr[i][5:0],
                         (slot_ring_prev_instr[i][31:26] == 6'h02) ?
                           "J (direct jump)" :
                         (slot_ring_prev_instr[i][31:26] == 6'h03) ?
                           "JAL (direct link call)" :
                         (slot_ring_prev_instr[i][31:26] == 6'h00 &&
                          slot_ring_prev_instr[i][5:0]   == 6'h08) ?
                           "JR (indirect via rs)" :
                         (slot_ring_prev_instr[i][31:26] == 6'h00 &&
                          slot_ring_prev_instr[i][5:0]   == 6'h09) ?
                           "JALR (indirect link via rs)" :
                           "OTHER (likely delay slot / linear fall-through)");
            end
        end

        // --------------------------------------------------------------
        // Ch59 — dispatch-call correlation log dump.
        //
        // Entries are printed in allocation order (seq#). For each
        // dispatch:
        //   seq, cyc, entry (0xB0|0xC0), syscall# ($9), args
        //   ($4-$7), $v0 ($2), return-pc ($31), outcome.
        // Outcomes:
        //   TRAP_400000      — the trap site fired within 4096 retires
        //                      of this call returning — STRONG
        //                      candidate for "call that set up the bad
        //                      branch".
        //   NEXT_DISPATCH    — another 0xB0/0xC0 dispatch started
        //                      within the window (detail=new seq).
        //   LONG_RUN         — the BIOS ran more than 4096 retires of
        //                      normal code after returning.
        //   IN_PROGRESS      — the run ended while the window was
        //                      still open (inspect manually).
        //
        // Codex's hunch: the tail of this list is where the trap-setup
        // lineage lives. Anything flagged *** SETS UP TRAP *** is the
        // top Ch60 investment target.
        // --------------------------------------------------------------
        $display("[tb_ee_core_bios_smoke] DISPATCH_LOG total=%0d (window=%0d retires per entry)",
                 disp_log_count, DISPATCH_WINDOW);
        for (int i = 0; i < DISPATCH_LOG_DEPTH; i = i + 1) begin
            if (disp_log_cyc[i] != 0 || disp_log_entry_pc[i] != 32'd0) begin
                $display("[tb_ee_core_bios_smoke] DISPATCH[seq=%0d] cyc=%0d entry=0x%08h $9(syscall#)=0x%08h $31(ra)=0x%08h",
                         disp_log_seq[i], disp_log_cyc[i],
                         disp_log_entry_pc[i], disp_log_r9[i],
                         disp_log_r31[i]);
                $display("[tb_ee_core_bios_smoke] DISPATCH[seq=%0d]   args: $4=0x%08h $5=0x%08h $6=0x%08h $7=0x%08h  $2(v0)=0x%08h",
                         disp_log_seq[i],
                         disp_log_r4[i], disp_log_r5[i],
                         disp_log_r6[i], disp_log_r7[i],
                         disp_log_r2[i]);
                $display("[tb_ee_core_bios_smoke] DISPATCH[seq=%0d]   outcome=%s cyc=%0d detail=%0d%s",
                         disp_log_seq[i],
                         (disp_log_outcome[i] == OUT_TRAP_400000)   ? "TRAP_400000" :
                         (disp_log_outcome[i] == OUT_NEXT_DISPATCH) ? "NEXT_DISPATCH" :
                         (disp_log_outcome[i] == OUT_LONG_RUN)      ? "LONG_RUN" :
                                                                      "IN_PROGRESS (run ended before window expired)",
                         disp_log_outcome_cyc[i],
                         disp_log_outcome_detail[i],
                         (disp_log_outcome[i] == OUT_TRAP_400000)
                            ? "  *** SETS UP TRAP @ pc=0x00400000 ***" : "");
            end
        end
        // Explicit trap-setup summary so the smoking gun is one-liner greppable.
        begin
            int trap_setup_count;
            trap_setup_count = 0;
            for (int i = 0; i < DISPATCH_LOG_DEPTH; i = i + 1) begin
                if (disp_log_outcome[i] == OUT_TRAP_400000) begin
                    trap_setup_count = trap_setup_count + 1;
                    $display("[tb_ee_core_bios_smoke] DISPATCH_TRAP_SETUP seq=%0d cyc=%0d entry=0x%08h $9=0x%08h $31=0x%08h  <- trap at cyc=%0d",
                             disp_log_seq[i], disp_log_cyc[i],
                             disp_log_entry_pc[i], disp_log_r9[i],
                             disp_log_r31[i], disp_log_outcome_cyc[i]);
                end
            end
            if (trap_setup_count == 0)
                $display("[tb_ee_core_bios_smoke] DISPATCH_TRAP_SETUP none — the 0x00400000 trap is NOT within %0d retires of any 0xB0/0xC0 dispatch; trap-setup lineage is elsewhere (tail of DISPATCH_LOG + late RAM code)",
                         DISPATCH_WINDOW);
        end

        // --------------------------------------------------------------
        // Ch60 — late control-flow provenance for pc=0x00400000 trap.
        //
        // Printed in three parts:
        //   (C) TRAP_SNAPSHOT at pc=0x00400000 (register bank +
        //       immediate predecessor retire).
        //   (B) Register-to-0x00400000 producer captures — which
        //       instruction first wrote 0x00400000 into $8/$10/$26/
        //       $31/$2.
        //   (A) LATE_CF_RING most-recent N control-flow retires, in
        //       chronological order. The tail is the immediate call
        //       chain leading to the trap — look for the JR whose
        //       rs_val = 0x00400000.
        // --------------------------------------------------------------
        if (trap_snapshot_captured) begin
            $display("[tb_ee_core_bios_smoke] TRAP_SNAPSHOT cyc=%0d retires=%0d pc=0x00400000",
                     trap_snapshot_cyc, trap_snapshot_retires);
            $display("[tb_ee_core_bios_smoke] TRAP_SNAPSHOT   regs $2=0x%08h $4=0x%08h $5=0x%08h $6=0x%08h $7=0x%08h",
                     trap_snapshot_r2, trap_snapshot_r4,
                     trap_snapshot_r5, trap_snapshot_r6,
                     trap_snapshot_r7);
            $display("[tb_ee_core_bios_smoke] TRAP_SNAPSHOT   regs $8=0x%08h $9=0x%08h $10=0x%08h $26=0x%08h $27=0x%08h",
                     trap_snapshot_r8, trap_snapshot_r9,
                     trap_snapshot_r10, trap_snapshot_r26,
                     trap_snapshot_r27);
            $display("[tb_ee_core_bios_smoke] TRAP_SNAPSHOT   regs $28=0x%08h $29=0x%08h $30=0x%08h $31=0x%08h",
                     trap_snapshot_r28, trap_snapshot_r29,
                     trap_snapshot_r30, trap_snapshot_r31);
            $display("[tb_ee_core_bios_smoke] TRAP_SNAPSHOT   prev retire pc=0x%08h instr=0x%08h cyc=%0d",
                     trap_snapshot_prev_pc, trap_snapshot_prev_instr,
                     trap_snapshot_prev_cyc);
        end else begin
            $display("[tb_ee_core_bios_smoke] TRAP_SNAPSHOT never fired (no retire with pc=0x00400000 observed)");
        end

        if (r2_to_trap_captured)
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$2] cyc=%0d pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r2_to_trap_cyc, r2_to_trap_pc,
                     r2_to_trap_instr, r2_to_trap_arg2, r2_to_trap_arg3);
        else
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$2] never fired ($2 never became 0x00400000)");

        if (r8_to_trap_captured)
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$8] cyc=%0d pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r8_to_trap_cyc, r8_to_trap_pc,
                     r8_to_trap_instr, r8_to_trap_arg2, r8_to_trap_arg3);
        else
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$8] never fired ($8 never became 0x00400000)");

        if (r10_to_trap_captured)
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$10] cyc=%0d pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r10_to_trap_cyc, r10_to_trap_pc,
                     r10_to_trap_instr, r10_to_trap_arg2, r10_to_trap_arg3);
        else
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$10] never fired ($10 never became 0x00400000)");

        if (r26_to_trap_captured)
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$26] cyc=%0d pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r26_to_trap_cyc, r26_to_trap_pc,
                     r26_to_trap_instr, r26_to_trap_arg2, r26_to_trap_arg3);
        else
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$26] never fired ($26 never became 0x00400000)");

        if (r31_to_trap_captured)
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$31] cyc=%0d pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r31_to_trap_cyc, r31_to_trap_pc,
                     r31_to_trap_instr, r31_to_trap_arg2, r31_to_trap_arg3);
        else
            $display("[tb_ee_core_bios_smoke] REG_TO_TRAP[$31] never fired ($31 never became 0x00400000)");

        // LATE_CF_RING in chronological order. Head points at the next
        // write slot; the oldest live slot is at head (when the ring
        // is full) or slot 0 (before it fills).
        $display("[tb_ee_core_bios_smoke] LATE_CF_RING total=%0d most-recent %0d kept (depth=%0d, activated at retired>=%0d):",
                 cf_ring_count,
                 (cf_ring_count < CF_RING_DEPTH) ? cf_ring_count : CF_RING_DEPTH,
                 CF_RING_DEPTH, CF_ACTIVATE_RETIRED);
        begin
            int base;
            int n;
            int idx;
            n = (cf_ring_count < CF_RING_DEPTH) ? cf_ring_count : CF_RING_DEPTH;
            base = (cf_ring_count < CF_RING_DEPTH) ? 0 : cf_ring_head;
            for (int k = 0; k < n; k = k + 1) begin
                idx = (base + k) % CF_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] LATE_CF_RING[%0d] cyc=%0d retires=%0d pc=0x%08h instr=0x%08h rs=$%0d rs_val=0x%08h $31=0x%08h  %s",
                         k,
                         cf_ring_cyc[idx], cf_ring_retires[idx],
                         cf_ring_pc[idx], cf_ring_instr[idx],
                         cf_ring_rs_idx[idx], cf_ring_rs_val[idx],
                         cf_ring_r31[idx],
                         (cf_ring_instr[idx][31:26] == 6'h02) ? "J" :
                         (cf_ring_instr[idx][31:26] == 6'h03) ? "JAL" :
                         (cf_ring_instr[idx][31:26] == 6'h00 &&
                          cf_ring_instr[idx][5:0]   == 6'h08) ? "JR" :
                         (cf_ring_instr[idx][31:26] == 6'h00 &&
                          cf_ring_instr[idx][5:0]   == 6'h09) ? "JALR" :
                         (cf_ring_instr[idx][31:26] == 6'h04) ? "BEQ" :
                         (cf_ring_instr[idx][31:26] == 6'h05) ? "BNE" :
                         (cf_ring_instr[idx][31:26] == 6'h06) ? "BLEZ" :
                         (cf_ring_instr[idx][31:26] == 6'h07) ? "BGTZ" :
                         (cf_ring_instr[idx][31:26] == 6'h01) ? "REGIMM (BLTZ/BGEZ)" :
                         (cf_ring_instr[idx][31:26] == 6'h10 &&
                          cf_ring_instr[idx][5:0]   == 6'h10) ? "RFE" :
                                                                "OTHER-CF");
            end
        end

        // --------------------------------------------------------------
        // Ch61 — J-target population/provenance answer.
        //
        // Printed in three parts:
        //   (1) one-liner yes/no: was useg_shadow[0x001A4530>>2] ever
        //       non-zero when the trap fired? Plus the current word.
        //   (2) write counters: narrow ([0x1A4000..0x1A5000)) and
        //       broader ([0x1A0000..0x1B0000)) for CPU and bridge.
        //   (3) W1A_RING: every narrow-window write captured, newest
        //       first — writer pc/instr/addr/data/be/master.
        //   (4) peek dumps around pc=0x00030044 (jump site) and
        //       pc=0x001A4530 (intended target), from useg_shadow
        //       (authoritative view for useg-space fetches).
        // --------------------------------------------------------------
        begin
            logic [31:0] tgt_word;
            logic [31:0] tgt_word_plus0;
            logic [31:0] tgt_word_plus4;
            logic [31:0] tgt_word_plus8;
            tgt_word       = peek_useg_shadow(32'h001A_4530);
            tgt_word_plus0 = peek_useg_shadow(32'h001A_4530);
            tgt_word_plus4 = peek_useg_shadow(32'h001A_4534);
            tgt_word_plus8 = peek_useg_shadow(32'h001A_4538);
            $display("[tb_ee_core_bios_smoke] W1A_ANSWER useg_shadow[0x001A4530]=0x%08h [+4]=0x%08h [+8]=0x%08h => J_TARGET_POPULATED=%s",
                     tgt_word_plus0, tgt_word_plus4, tgt_word_plus8,
                     (tgt_word_plus0 != 32'd0 || tgt_word_plus4 != 32'd0 || tgt_word_plus8 != 32'd0)
                       ? "YES (the J target has non-zero bytes -- look for aliasing/overwrite)"
                       : "NO (the J target is all-zero in useg_shadow -- a larger install/copy phase is missing)");
        end
        $display("[tb_ee_core_bios_smoke] W1A_COUNT narrow[0x1A4000..0x1A5000) cpu=%0d bridge=%0d   broad[0x1A0000..0x1B0000) cpu=%0d bridge=%0d",
                 w1a_cpu_narrow, w1a_bridge_narrow,
                 w1a_cpu_broad, w1a_bridge_broad);
        $display("[tb_ee_core_bios_smoke] W1A_RING total=%0d most-recent %0d kept (depth=%0d):",
                 w1a_ring_count,
                 (w1a_ring_count < W1A_RING_DEPTH) ? w1a_ring_count : W1A_RING_DEPTH,
                 W1A_RING_DEPTH);
        for (int i = 0; i < W1A_RING_DEPTH; i = i + 1) begin
            if (w1a_ring_cyc[i] != 0 || w1a_ring_addr[i] != 32'd0) begin
                $display("[tb_ee_core_bios_smoke] W1A_RING[%0d] cyc=%0d master=%s  addr=0x%08h be=0x%04h data_lo64=0x%016h",
                         i, w1a_ring_cyc[i],
                         (w1a_ring_master[i] == 1'b1) ? "bridge" : "cpu   ",
                         w1a_ring_addr[i], w1a_ring_be[i],
                         w1a_ring_data[i][63:0]);
                $display("[tb_ee_core_bios_smoke] W1A_RING[%0d]   writer pc=0x%08h instr=0x%08h",
                         i, w1a_ring_wr_pc[i], w1a_ring_wr_instr[i]);
            end
        end
        // Peek dumps — both views where useful.
        $display("[tb_ee_core_bios_smoke] PEEK @ jump site pc=0x00030040..0x00030060 (useg_shadow view):");
        for (int i = 0; i < 8; i = i + 1) begin
            logic [31:0] a;
            a = 32'h0003_0040 + i * 4;
            $display("[tb_ee_core_bios_smoke] PEEK   [0x%08h] = 0x%08h",
                     a, peek_useg_shadow(a));
        end
        $display("[tb_ee_core_bios_smoke] PEEK @ J target pc=0x001A4520..0x001A4560 (useg_shadow view):");
        for (int i = 0; i < 16; i = i + 1) begin
            logic [31:0] a;
            a = 32'h001A_4520 + i * 4;
            $display("[tb_ee_core_bios_smoke] PEEK   [0x%08h] = 0x%08h",
                     a, peek_useg_shadow(a));
        end

        // --------------------------------------------------------------
        // Ch62 — 0xA0 dispatch ABI profile.
        //
        //   (1) A0_ENTRY: registers + previous CF retire at the 0xA0
        //       entry — the ABI payload the syscall sees.
        //   (2) A0_SETUP: the 32 retires from the retire_ring
        //       immediately BEFORE 0xA0 entry, in chronological order
        //       (oldest first). Shows how $4-$7, $9, $10 were formed.
        //   (3) A0_POST: retires from 0xA0 entry until either pc=
        //       0x00030044 (the J site) or depth exhaustion. Confirms
        //       there is no intervening register setup between return
        //       from the stub and the J 0x001A4530.
        // --------------------------------------------------------------
        if (a0_captured) begin
            $display("[tb_ee_core_bios_smoke] A0_ENTRY cyc=%0d retires=%0d",
                     a0_cyc, a0_retires);
            $display("[tb_ee_core_bios_smoke] A0_ENTRY   syscall#: $9=0x%08h  selector: $10=0x%08h  return: $31=0x%08h",
                     a0_r9, a0_r10, a0_r31);
            $display("[tb_ee_core_bios_smoke] A0_ENTRY   args: $4=0x%08h $5=0x%08h $6=0x%08h $7=0x%08h  $2(v0)=0x%08h",
                     a0_r4, a0_r5, a0_r6, a0_r7, a0_r2);
            $display("[tb_ee_core_bios_smoke] A0_ENTRY   prev CF retire cyc=%0d pc=0x%08h instr=0x%08h",
                     a0_prev_cf_cyc, a0_prev_cf_pc, a0_prev_cf_instr);

            $display("[tb_ee_core_bios_smoke] A0_SETUP 32 retires preceding 0xA0 entry (chronological; tail = JALR $10 and delay slot):");
            begin
                int n;
                int idx;
                n = RETIRE_RING_DEPTH;
                for (int k = 0; k < n; k = k + 1) begin
                    // a0_setup_head at capture was retire_ring_head.
                    // Oldest ring slot is at head (about to be overwritten);
                    // walking forward in k gives chronological order.
                    idx = (a0_setup_head + k) % RETIRE_RING_DEPTH;
                    $display("[tb_ee_core_bios_smoke] A0_SETUP[%0d] cyc=%0d pc=0x%08h instr=0x%08h",
                             k, a0_setup_cyc[idx], a0_setup_pc[idx],
                             a0_setup_instr[idx]);
                end
            end

            $display("[tb_ee_core_bios_smoke] A0_POST %0d retires from 0xA0 entry until pc=0x00030044 (J site) or depth exhaustion:",
                     a0_post_count);
            for (int i = 0; i < A0_POST_DEPTH; i = i + 1) begin
                if (i < a0_post_count) begin
                    $display("[tb_ee_core_bios_smoke] A0_POST[%0d] cyc=%0d pc=0x%08h instr=0x%08h  $2=0x%08h $4=0x%08h $5=0x%08h $31=0x%08h",
                             i, a0_post_cyc[i],
                             a0_post_pc[i], a0_post_instr[i],
                             a0_post_r2[i], a0_post_r4[i],
                             a0_post_r5[i], a0_post_r31[i]);
                end
            end
        end else begin
            $display("[tb_ee_core_bios_smoke] A0_ENTRY never fired (no retire with pc=0x000000A0 observed)");
        end

        // --------------------------------------------------------------
        // Ch63 — memcpy source-region provenance summary.
        //
        // Answers "was phys 0x0004468C ever populated, and from
        // which backing?" in four parts:
        //   (1) SRC_ANSWER one-liner: current values at the source
        //       end-pointer across BOTH views (useg_shadow via
        //       peek_useg_shadow, ee_ram via peek_instr). Same
        //       address, two backings — reveals populated-but-only-
        //       in-one-backing cases.
        //   (2) SRC_COUNT: narrow / broader / widest write counters
        //       for CPU and bridge. Tells us the scope of activity.
        //   (3) SRC_RING: every narrow-window write captured.
        //   (4) SRC_LW: first memcpy LW at pc=0x30014 with EA and
        //       loaded value — what the CPU actually read.
        // --------------------------------------------------------------
        begin
            // Check the full memcpy source span at phys [0x00044000..
            // 0x0004468C) in both backings. Scan at 4-byte stride and
            // report by scope: any non-zero word in useg_shadow, any
            // in ee_ram. Picking a single address (like the end ptr)
            // is misleading because the populated region may sit
            // partway through the span.
            int  useg_shadow_nz_count;
            int  ee_ram_nz_count;
            logic [31:0] a;
            useg_shadow_nz_count = 0;
            ee_ram_nz_count      = 0;
            for (a = 32'h0004_4000; a < 32'h0004_468C; a = a + 32'd4) begin
                if (peek_useg_shadow(a) != 32'd0) useg_shadow_nz_count = useg_shadow_nz_count + 1;
                if (peek_instr(a | 32'h8000_0000) != 32'd0) ee_ram_nz_count = ee_ram_nz_count + 1;
            end
            $display("[tb_ee_core_bios_smoke] SRC_ANSWER memcpy source [0x00044000..0x0004468C) non-zero word counts: useg_shadow=%0d ee_ram=%0d => %s",
                     useg_shadow_nz_count, ee_ram_nz_count,
                     (useg_shadow_nz_count == 0 && ee_ram_nz_count == 0)
                         ? "SOURCE NEVER POPULATED (both backings entirely zero -- upstream install/copy phase is missing)"
                     : (useg_shadow_nz_count != 0 && ee_ram_nz_count == 0)
                         ? "SOURCE POPULATED ONLY IN useg_shadow (memcpy reads via kseg0/ee_ram and sees zero -- useg<->kseg0 visibility gap is the real problem)"
                     : (useg_shadow_nz_count == 0 && ee_ram_nz_count != 0)
                         ? "SOURCE POPULATED ONLY IN ee_ram (useg fetch visibility gap)"
                         : "SOURCE POPULATED IN BOTH backings (aliasing is not the problem; inspect content)");
        end
        $display("[tb_ee_core_bios_smoke] SRC_COUNT narrow[0x44640..0x44700) cpu=%0d bridge=%0d  broad[0x44000..0x45000) cpu=%0d bridge=%0d  widest[0x40000..0x50000) cpu=%0d bridge=%0d",
                 src_cpu_narrow, src_bridge_narrow,
                 src_cpu_broad,  src_bridge_broad,
                 src_cpu_widest, src_bridge_widest);
        $display("[tb_ee_core_bios_smoke] SRC_RING total=%0d most-recent %0d kept (depth=%0d):",
                 src_ring_count,
                 (src_ring_count < SRC_RING_DEPTH) ? src_ring_count : SRC_RING_DEPTH,
                 SRC_RING_DEPTH);
        for (int i = 0; i < SRC_RING_DEPTH; i = i + 1) begin
            if (src_ring_cyc[i] != 0 || src_ring_addr[i] != 32'd0) begin
                $display("[tb_ee_core_bios_smoke] SRC_RING[%0d] cyc=%0d master=%s  addr=0x%08h be=0x%04h data_lo64=0x%016h  writer pc=0x%08h instr=0x%08h",
                         i, src_ring_cyc[i],
                         (src_ring_master[i] == 1'b1) ? "bridge" : "cpu   ",
                         src_ring_addr[i], src_ring_be[i],
                         src_ring_data[i][63:0],
                         src_ring_wr_pc[i], src_ring_wr_instr[i]);
            end
        end
        if (first_lw_captured)
            $display("[tb_ee_core_bios_smoke] SRC_LW cyc=%0d first LW at pc=0x00030014 EA=0x%08h loaded=0x%08h",
                     first_lw_cyc, first_lw_ea, first_lw_loaded);
        else
            $display("[tb_ee_core_bios_smoke] SRC_LW never fired (no memcpy LW at pc=0x00030014 with EA in broader source window)");
        $display("[tb_ee_core_bios_smoke] PEEK @ source pc=0x00044660..0x000446A0 (two views):");
        for (int i = 0; i < 16; i = i + 1) begin
            logic [31:0] a_useg;
            logic [31:0] a_kseg;
            a_useg = 32'h0004_4660 + i * 4;
            a_kseg = 32'h8004_4660 + i * 4;
            $display("[tb_ee_core_bios_smoke] PEEK   useg_shadow[0x%08h]=0x%08h  ee_ram[phys 0x%07h]=0x%08h",
                     a_useg, peek_useg_shadow(a_useg),
                     a_kseg[28:0], peek_instr(a_kseg));
        end

        // --------------------------------------------------------------
        // Ch66 — 0x001A459C trap snapshot + recent memory-access ring
        //         + expanded copied-region peek.
        //
        // Disambiguates unsupported-opcode strict halt (decode gap)
        // from AdES alignment exception (real store fault):
        //   - If ch66_trap_prev_trap_count == final trap count - 1
        //     AND retired_instr decodes as an unsupported-in-core
        //     opcode (e.g., SRLV with op=0 func=6 that ee_core_stub
        //     doesn't implement), the trap is a decode gap.
        //   - If core_exception_events advanced around this pc AND
        //     BadVAddr points at a store's EA nearby, it's an AdES.
        //
        // The expanded peek covers [0x001A4530..0x001A45B0) — 32
        // words of the copied code surrounding the trap site — in
        // both useg_shadow (what the CPU fetches) and ee_ram (what
        // the memcpy wrote). With Ch64+Ch65 mirrors enabled both
        // should match.
        // --------------------------------------------------------------
        if (ch66_trap_captured) begin
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT cyc=%0d retires=%0d pc=0x%08h instr=0x%08h",
                     ch66_trap_cyc, ch66_trap_retires,
                     CH66_TRAP_PC, ch66_trap_instr);
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT   decode: op=0x%02h rs=$%0d rt=$%0d rd=$%0d shamt=%0d func=0x%02h",
                     ch66_trap_instr[31:26],
                     ch66_trap_instr[25:21],
                     ch66_trap_instr[20:16],
                     ch66_trap_instr[15:11],
                     ch66_trap_instr[10:6],
                     ch66_trap_instr[5:0]);
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT   COP0 epc=0x%08h cause.exc_code=0x%02h badvaddr=0x%08h status.bev=%0b",
                     ch66_trap_epc, ch66_trap_cause_exc,
                     ch66_trap_badvaddr, ch66_trap_status_bev);
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT   regs $2=0x%08h $3=0x%08h $4=0x%08h $5=0x%08h",
                     ch66_trap_r2, ch66_trap_r3, ch66_trap_r4, ch66_trap_r5);
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT   regs $6=0x%08h $7=0x%08h $8=0x%08h $9=0x%08h $10=0x%08h",
                     ch66_trap_r6, ch66_trap_r7, ch66_trap_r8,
                     ch66_trap_r9, ch66_trap_r10);
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT   regs $26=0x%08h $27=0x%08h $28=0x%08h $29=0x%08h $30=0x%08h $31=0x%08h",
                     ch66_trap_r26, ch66_trap_r27, ch66_trap_r28,
                     ch66_trap_r29, ch66_trap_r30, ch66_trap_r31);
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT   counts at snapshot: except=%0d trap=%0d  (final: except=%0d trap=%0d)",
                     ch66_trap_prev_except_count, ch66_trap_prev_trap_count,
                     core_exception_events, core_trap_events);
            // Classify: if the retired_instr at this pc looks like an
            // unsupported opcode AND trap fired on this retire, it's
            // a decode gap. SRLV is op=0 func=6.
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT   classification: %s",
                     (ch66_trap_instr[31:26] == 6'h00 &&
                      ch66_trap_instr[5:0]   == 6'h06)
                        ? "SRLV (op=0 func=6) -- check ee_core_stub decode; if strict-unsupported, Ch67 should add SRLV execute"
                     : (ch66_trap_instr[31:26] == 6'h00 &&
                        ch66_trap_instr[5:0]   == 6'h04)
                        ? "SLLV (op=0 func=4) -- shift-left variable; similar decode-gap candidate"
                     : (ch66_trap_instr[31:26] == 6'h00 &&
                        ch66_trap_instr[5:0]   == 6'h07)
                        ? "SRAV (op=0 func=7) -- shift-right arithmetic variable; decode-gap candidate"
                        : "check decode coverage for this opcode");
        end else begin
            $display("[tb_ee_core_bios_smoke] CH66_TRAP_SNAPSHOT never fired (no retire at pc=0x001A459C)");
        end

        $display("[tb_ee_core_bios_smoke] MEM_RING total=%0d most-recent %0d kept (depth=%0d):",
                 mem_ring_count,
                 (mem_ring_count < MEM_RING_DEPTH) ? mem_ring_count : MEM_RING_DEPTH,
                 MEM_RING_DEPTH);
        begin
            int n;
            int base;
            int idx;
            n    = (mem_ring_count < MEM_RING_DEPTH) ? mem_ring_count : MEM_RING_DEPTH;
            base = (mem_ring_count < MEM_RING_DEPTH) ? 0 : mem_ring_head;
            for (int k = 0; k < n; k = k + 1) begin
                idx = (base + k) % MEM_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] MEM_RING[%0d] cyc=%0d pc=0x%08h instr=0x%08h EA=0x%08h data=0x%08h  %s",
                         k, mem_ring_cyc[idx],
                         mem_ring_pc[idx], mem_ring_instr[idx],
                         mem_ring_ea[idx], mem_ring_data[idx],
                         (mem_ring_instr[idx][31:26] == 6'h20) ? "LB"  :
                         (mem_ring_instr[idx][31:26] == 6'h21) ? "LH"  :
                         (mem_ring_instr[idx][31:26] == 6'h22) ? "LWL" :
                         (mem_ring_instr[idx][31:26] == 6'h23) ? "LW"  :
                         (mem_ring_instr[idx][31:26] == 6'h24) ? "LBU" :
                         (mem_ring_instr[idx][31:26] == 6'h25) ? "LHU" :
                         (mem_ring_instr[idx][31:26] == 6'h26) ? "LWR" :
                         (mem_ring_instr[idx][31:26] == 6'h28) ? "SB"  :
                         (mem_ring_instr[idx][31:26] == 6'h29) ? "SH"  :
                         (mem_ring_instr[idx][31:26] == 6'h2A) ? "SWL" :
                         (mem_ring_instr[idx][31:26] == 6'h2B) ? "SW"  :
                         (mem_ring_instr[idx][31:26] == 6'h2E) ? "SWR" :
                                                                 "?");
            end
        end

        $display("[tb_ee_core_bios_smoke] PEEK @ copied region pc=0x001A4530..0x001A45B0 (two views; both should match with Ch64+Ch65 on):");
        for (int i = 0; i < 32; i = i + 1) begin
            logic [31:0] a_useg;
            logic [31:0] a_kseg;
            a_useg = 32'h001A_4530 + i * 4;
            a_kseg = 32'h801A_4530 + i * 4;
            $display("[tb_ee_core_bios_smoke] PEEK   useg_shadow[0x%08h]=0x%08h  ee_ram[phys 0x%07h]=0x%08h%s",
                     a_useg, peek_useg_shadow(a_useg),
                     a_kseg[28:0], peek_instr(a_kseg),
                     (a_useg == CH66_TRAP_PC) ? "  <-- trap site" : "");
        end

        // --------------------------------------------------------------
        // Ch68 — loop characterization summary.
        // --------------------------------------------------------------
        $display("[tb_ee_core_bios_smoke] LOOP_SUMMARY total_in_range=%0d count[0x001A4564]=%0d count[0x001A45A0]=%0d load_count=%0d branch_count=%0d",
                 loop_ring_count, loop_count_4564, loop_count_45a0,
                 loop_load_count, loop_branch_count);
        if (loop_ea_seen) begin
            $display("[tb_ee_core_bios_smoke] LOOP_EA min=0x%08h max=0x%08h span=0x%08h (%0d bytes)",
                     loop_ea_min, loop_ea_max,
                     loop_ea_max - loop_ea_min,
                     loop_ea_max - loop_ea_min);
        end else begin
            $display("[tb_ee_core_bios_smoke] LOOP_EA never observed (no load retires in loop range)");
        end
        if (loop_first_seen) begin
            $display("[tb_ee_core_bios_smoke] LOOP_PROGRESSION $4: first=0x%08h last=0x%08h delta=0x%08h",
                     r4_first, r4_last, r4_last - r4_first);
            $display("[tb_ee_core_bios_smoke] LOOP_PROGRESSION $5: first=0x%08h last=0x%08h delta=0x%08h",
                     r5_first, r5_last, r5_last - r5_first);
            $display("[tb_ee_core_bios_smoke] LOOP_PROGRESSION $6: first=0x%08h last=0x%08h delta=0x%08h",
                     r6_first, r6_last, r6_last - r6_first);
        end
        if (last_branch_seen) begin
            $display("[tb_ee_core_bios_smoke] LOOP_LAST_BRANCH cyc=%0d pc=0x%08h instr=0x%08h op=0x%02h rs=$%0d rt=$%0d  rs_val=0x%08h rt_val=0x%08h",
                     last_branch_cyc, last_branch_pc, last_branch_instr,
                     last_branch_instr[31:26],
                     last_branch_rs_idx, last_branch_rt_idx,
                     last_branch_rs_val, last_branch_rt_val);
        end else begin
            $display("[tb_ee_core_bios_smoke] LOOP_LAST_BRANCH never fired (no branch retires in loop range)");
        end

        `ifndef CH71_LONG_RUN
        $display("[tb_ee_core_bios_smoke] LOOP_RING most-recent %0d entries (newest at end):",
                 (loop_ring_count < LOOP_RING_DEPTH) ? loop_ring_count : LOOP_RING_DEPTH);
        begin
            int n;
            int base;
            int idx;
            n    = (loop_ring_count < LOOP_RING_DEPTH) ? loop_ring_count : LOOP_RING_DEPTH;
            base = (loop_ring_count < LOOP_RING_DEPTH) ? 0 : loop_ring_head;
            for (int k = 0; k < n; k = k + 1) begin
                idx = (base + k) % LOOP_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] LOOP_RING[%0d] cyc=%0d retires=%0d pc=0x%08h instr=0x%08h  $4=0x%08h $5=0x%08h $6=0x%08h  arg2=0x%08h arg3=0x%08h",
                         k,
                         loop_ring_cyc[idx], loop_ring_retires[idx],
                         loop_ring_pc[idx], loop_ring_instr[idx],
                         loop_ring_r4[idx], loop_ring_r5[idx],
                         loop_ring_r6[idx],
                         loop_ring_arg2[idx], loop_ring_arg3[idx]);
            end
        end
        `endif

        // Mode-specific checks
        // --------------------------------------------------------------
        if (using_real_bios) begin
            if (!core_trap) begin
                $error("[tb_ee_core_bios_smoke] real-BIOS run did not hit a strict trap; core halted via %s",
                       (core_halt_events > 0) ? "SYSCALL" : "other");
                errors = errors + 1;
            end
            if (core_trap_events != 1) begin
                $error("[tb_ee_core_bios_smoke] expected exactly 1 trap retire, got %0d",
                       core_trap_events);
                errors = errors + 1;
            end
        end else begin
            if (!core_trap) begin
                $error("[tb_ee_core_bios_smoke] trap_o not asserted at halt");
                errors = errors + 1;
            end
            if (core_trap_pc !== EXPECTED_TRAP_PC) begin
                $error("[tb_ee_core_bios_smoke] trap_pc mismatch: got 0x%08h expected 0x%08h",
                       core_trap_pc, EXPECTED_TRAP_PC);
                errors = errors + 1;
            end
            if (core_trap_instr !== EXPECTED_TRAP_INSTR) begin
                $error("[tb_ee_core_bios_smoke] trap_instr mismatch: got 0x%08h expected 0x%08h",
                       core_trap_instr, EXPECTED_TRAP_INSTR);
                errors = errors + 1;
            end
            if (core_trap_events != 1) begin
                $error("[tb_ee_core_bios_smoke] expected 1 trap retire, got %0d",
                       core_trap_events);
                errors = errors + 1;
            end
            // Reset J + delay NOP + 8 supported bootstrap ops + trap = 11
            if (core_retired_events != 11) begin
                $error("[tb_ee_core_bios_smoke] expected 11 retires total, got %0d",
                       core_retired_events);
                errors = errors + 1;
            end
            if (core_halt_events != 0) begin
                $error("[tb_ee_core_bios_smoke] expected 0 SYSCALL halts, got %0d",
                       core_halt_events);
                errors = errors + 1;
            end
            if (core_exception_events != 0) begin
                $error("[tb_ee_core_bios_smoke] expected 0 exception entries, got %0d",
                       core_exception_events);
                errors = errors + 1;
            end
            if (dmac_done_events != 0) begin
                $error("[tb_ee_core_bios_smoke] DMA should not have completed, got %0d",
                       dmac_done_events);
                errors = errors + 1;
            end
            if (bios_reads < 10) begin
                $error("[tb_ee_core_bios_smoke] expected >=10 BIOS reads, got %0d",
                       bios_reads);
                errors = errors + 1;
            end
        end

        if (errors == 0 &&
            core_trap && core_trap_events == 1 &&
            (using_real_bios ||
             (core_trap_pc        == EXPECTED_TRAP_PC &&
              core_trap_instr     == EXPECTED_TRAP_INSTR &&
              core_retired_events == 11)))
            $display("[tb_ee_core_bios_smoke] PASS");
        else
            $display("[tb_ee_core_bios_smoke] FAIL");

        $finish;
    end

    initial begin
        // Codex-directed extension. 20 ms was enough to rule out a hang
        // (linearly growing retired count at a healthy rate), but not
        // enough to resolve "long list vs. cycle" for the linked-list
        // traversal at 0x14B4. 80 ms gives 4x runway; if the loop
        // still dominates the whole window, the next step is pointer-
        // register snapshots at sample time — not more timeout.
        // Ch70 — main smoke is 1x timeout (bring-up discriminator).
        // Ch71 — `tb_ee_core_bios_long` target compiles with
        // CH71_LONG_RUN defined, bumping the timeout 10x and gating
        // the heavy LOOP_RING per-retire writes (above) to make the
        // patient progress runner practical.
        `ifdef CH71_LONG_RUN
        #800000000;
        `else
        #80000000;
        `endif
        $display("[tb_ee_core_bios_smoke] TIMEOUT pc=0x%08h retired=%0d trap=%0d halt=%0d except=%0d done=%0d bios_reads=%0d",
                 core_pc, core_retired_events, core_trap_events,
                 core_halt_events, core_exception_events,
                 dmac_done_events, bios_reads);
        $display("[tb_ee_core_bios_smoke] TIMEOUT pc_sampler samples=%0d pc_min=0x%08h pc_max=0x%08h range=%0d",
                 pc_sample_count, pc_min, pc_max, pc_max - pc_min);
        // Ring-buffer dump with instruction + retired-count snapshots
        // at each sample. Reader diagnoses:
        //   - pc stuck, retired stuck              → real hang
        //   - pc in narrow range, retired growing  → copy loop or
        //                                             tight algo
        //   - pc in narrow range, retired stuck    → polling wait
        //   - pc walking forward                   → normal progress
        // The instr column says WHAT is running there — a memcpy
        // body reads 0x8cXX/0xacXX (LW/SW), a wait loop reads
        // 0x40XX (MFC0) or a specific BNE back to itself.
        $display("[tb_ee_core_bios_smoke] TIMEOUT pc_history ring (slot | pc | instr | retired | $5 | $6; newest = slot %0d):",
                 (pc_sample_count == 0) ? 0
                     : (pc_sample_count - 1) % PC_SAMPLE_DEPTH);
        for (int i = 0; i < PC_SAMPLE_DEPTH; i++) begin
            if (i < pc_sample_count || pc_sample_count >= PC_SAMPLE_DEPTH)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   slot[%0d] pc=0x%08h instr=0x%08h retired=%0d $5=0x%08h $6=0x%08h",
                         i, pc_history[i], pc_instr_history[i],
                         retired_history[i], reg_r5_history[i], reg_r6_history[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT unmapped total=%0d (first %0d captured):",
                 unmapped_count,
                 (unmapped_count < UNMAPPED_DEPTH) ? unmapped_count : UNMAPPED_DEPTH);
        for (int i = 0; i < UNMAPPED_DEPTH; i++) begin
            if (i < unmapped_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   unmapped[%0d] cycle=%0d pc=0x%08h %s addr=0x%08h data=0x%08h",
                         i, unmapped_cycle[i], unmapped_pc[i],
                         unmapped_is_wr[i] ? "WR" : "RD",
                         unmapped_addr[i], unmapped_data[i]);
        end
        if (lineage_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT lineage first-unmapped-RD cycle=%0d pc=0x%08h instr=0x%08h",
                     lineage_cycle, lineage_pc, lineage_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   decode op=0x%02h rs=$%0d rt=$%0d rd=$%0d imm=0x%04h",
                     lineage_opcode, lineage_rs, lineage_rt, lineage_rd,
                     lineage_imm);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   rs($%0d)=0x%08h rt($%0d)=0x%08h rd($%0d)=0x%08h",
                     lineage_rs, lineage_rs_val, lineage_rt, lineage_rt_val,
                     lineage_rd, lineage_rd_val);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   EA=0x%08h poison_addr=0x%08h poison_data=0x%08h",
                     lineage_effective_addr, lineage_poison_addr,
                     lineage_poison_data);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   retire ring at trigger (newest first, up to %0d):",
                     RETIRE_RING_DEPTH);
            // NOTE: this ring reflects state as of the TIMEOUT, not the
            // trigger — the ring keeps advancing while the DEADBEEF
            // loop spins. Inline trigger print above is the snapshot
            // at the actual moment.
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT lineage never captured (no UNMAPPED read event fired)");
        end
        if (r2_tx_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT trig_r2 cycle=%0d prev=0x%08h new=0x%08h",
                     r2_tx_cycle, r2_tx_prev, r2_tx_new);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r2_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r2_tx_retired_pc_now, r2_tx_retired_instr_now,
                     r2_tx_retired_arg2_now, r2_tx_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r2_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r2_tx_retired_pc_d1, r2_tx_retired_instr_d1,
                     r2_tx_retired_arg2_d1, r2_tx_retired_arg3_d1);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT trig_r2 never fired ($2 never entered 0x3xxx_xxxx band)");
        end
        if (r6_tx_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT trig_r6 cycle=%0d prev=0x%08h new=0x%08h",
                     r6_tx_cycle, r6_tx_prev, r6_tx_new);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r6_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r6_tx_retired_pc_now, r6_tx_retired_instr_now,
                     r6_tx_retired_arg2_now, r6_tx_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r6_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r6_tx_retired_pc_d1, r6_tx_retired_instr_d1,
                     r6_tx_retired_arg2_d1, r6_tx_retired_arg3_d1);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT trig_r6 never fired ($6 never entered 0x3xxx_xxxx band)");
        end
        if (r2_ex_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT exact_r2 cycle=%0d (first $2 == 0x%08h)",
                     r2_ex_cycle, POISON_VALUE);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r2_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r2_ex_retired_pc_now, r2_ex_retired_instr_now,
                     r2_ex_retired_arg2_now, r2_ex_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r2_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r2_ex_retired_pc_d1, r2_ex_retired_instr_d1,
                     r2_ex_retired_arg2_d1, r2_ex_retired_arg3_d1);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT exact_r2 never fired ($2 never became 0x%08h)",
                     POISON_VALUE);
        end
        if (r6_ex_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT exact_r6 cycle=%0d (first $6 == 0x%08h)",
                     r6_ex_cycle, POISON_VALUE);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r6_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r6_ex_retired_pc_now, r6_ex_retired_instr_now,
                     r6_ex_retired_arg2_now, r6_ex_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r6_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r6_ex_retired_pc_d1, r6_ex_retired_instr_d1,
                     r6_ex_retired_arg2_d1, r6_ex_retired_arg3_d1);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT exact_r6 never fired ($6 never became 0x%08h)",
                     POISON_VALUE);
        end
        if (ramwr_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT ramwr cycle=%0d retired_pc=0x%08h instr=0x%08h",
                     ramwr_cycle, ramwr_retired_pc, ramwr_retired_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   ramwr decode op=0x%02h rs=$%0d rt=$%0d imm=0x%04h EA=0x%08h data=0x%08h",
                     ramwr_opcode, ramwr_rs_idx, ramwr_rt_idx, ramwr_imm,
                     ramwr_retired_arg2, ramwr_retired_arg3);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   ramwr rs($%0d)=0x%08h rt($%0d)=0x%08h (at retire)",
                     ramwr_rs_idx, ramwr_rs_val_at_retire,
                     ramwr_rt_idx, ramwr_rt_val_at_retire);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT ramwr never fired (no store SW EA=0x%08h data=0x%08h)",
                     POISON_RAM_ADDR, POISON_VALUE);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT ramwr_any total=%0d (first %0d captured, phys-word match):",
                 ramwr_any_count,
                 (ramwr_any_count < RAMWR_DEPTH) ? ramwr_any_count : RAMWR_DEPTH);
        for (int i = 0; i < RAMWR_DEPTH; i++) begin
            if (i < ramwr_any_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   ramwr_any[%0d] cycle=%0d pc=0x%08h instr=0x%08h EA=0x%08h data=0x%08h",
                         i, ramwr_any_cycle[i], ramwr_any_pc[i],
                         ramwr_any_instr[i], ramwr_any_ea[i],
                         ramwr_any_data[i]);
        end
        if (entry14_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT entry14 cycle=%0d retired_pc=0x%08h instr=0x%08h",
                     entry14_cycle, entry14_retired_pc, entry14_retired_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   prev-d1   retired_pc=0x%08h instr=0x%08h",
                     entry14_prev_retired_pc, entry14_prev_retired_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   prev-ring pc=0x%08h instr=0x%08h (authoritative)",
                     entry14_ring_prev_pc, entry14_ring_prev_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   args    $2=0x%08h $4/a0=0x%08h $5/a1=0x%08h $6/a2=0x%08h $7/a3=0x%08h",
                     entry14_r2, entry14_r4, entry14_r5, entry14_r6, entry14_r7);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   frame   $28/gp=0x%08h $29/sp=0x%08h $30/fp=0x%08h $31/ra=0x%08h",
                     entry14_r28, entry14_r29, entry14_r30, entry14_r31);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT entry14 never fired (execution never entered 0x1400-0x14FF)");
        end
        if (entry200_captured) begin
            // Classify how pc=0x200 was entered by looking at head-2
            // (the branch/jump site itself; head-1 is its delay slot).
            // Decoders: opcode = instr[31:26]; for SPECIAL (opcode=0)
            // func = instr[5:0]. J=0x02, JAL=0x03, JR=SPECIAL+0x08,
            // JALR=SPECIAL+0x09, BEQ=0x04, BNE=0x05, BLEZ=0x06,
            // BGTZ=0x07.
            $display("[tb_ee_core_bios_smoke] TIMEOUT entry200 cycle=%0d retired_pc=0x%08h instr=0x%08h",
                     entry200_cycle, entry200_retired_pc, entry200_retired_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   prev-d1    pc=0x%08h instr=0x%08h",
                     entry200_prev_retired_pc, entry200_prev_retired_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   ring-1     pc=0x%08h instr=0x%08h cyc=%0d (delay slot / predecessor)",
                     entry200_ring_prev_pc, entry200_ring_prev_instr,
                     entry200_ring_prev_cyc);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   ring-2     pc=0x%08h instr=0x%08h cyc=%0d (branch/jump site)",
                     entry200_ring_prev2_pc, entry200_ring_prev2_instr,
                     entry200_ring_prev2_cyc);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   entry-kind ring-2 op=0x%02h func=0x%02h => %s",
                     entry200_ring_prev2_instr[31:26],
                     entry200_ring_prev2_instr[5:0],
                     (entry200_ring_prev2_instr[31:26] == 6'h02) ? "J (direct jump)" :
                     (entry200_ring_prev2_instr[31:26] == 6'h03) ? "JAL (direct call, $31=ra)" :
                     (entry200_ring_prev2_instr[31:26] == 6'h00 &&
                      entry200_ring_prev2_instr[5:0]   == 6'h08) ? "JR (indirect jump via rs)" :
                     (entry200_ring_prev2_instr[31:26] == 6'h00 &&
                      entry200_ring_prev2_instr[5:0]   == 6'h09) ? "JALR (indirect call via rs)" :
                     (entry200_ring_prev2_instr[31:26] == 6'h04) ? "BEQ (taken)" :
                     (entry200_ring_prev2_instr[31:26] == 6'h05) ? "BNE (taken)" :
                     (entry200_ring_prev2_instr[31:26] == 6'h06) ? "BLEZ (taken)" :
                     (entry200_ring_prev2_instr[31:26] == 6'h07) ? "BGTZ (taken)" :
                     "OTHER (not a branch/jump — possible exception-vector-like flow)");
            $display("[tb_ee_core_bios_smoke] TIMEOUT   args       $2=0x%08h $4/a0=0x%08h $5/a1=0x%08h $6/a2=0x%08h $7/a3=0x%08h",
                     entry200_r2, entry200_r4, entry200_r5, entry200_r6, entry200_r7);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   trampoline $8=0x%08h $9=0x%08h $26/k0=0x%08h $27/k1=0x%08h",
                     entry200_r8, entry200_r9, entry200_r26, entry200_r27);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   frame      $28/gp=0x%08h $29/sp=0x%08h $30/fp=0x%08h $31/ra=0x%08h",
                     entry200_r28, entry200_r29, entry200_r30, entry200_r31);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT entry200 never fired (execution never reached pc=0x200)");
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT prologue count=%0d (first %0d captured, retires in 0x%04h-0x%04h):",
                 prologue_count,
                 (prologue_count < PROLOGUE_DEPTH) ? prologue_count : PROLOGUE_DEPTH,
                 PROLOGUE_LOW[15:0], PROLOGUE_HIGH[15:0]);
        for (int i = 0; i < PROLOGUE_DEPTH; i++) begin
            if (i < prologue_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   prologue[%0d] cyc=%0d pc=0x%08h instr=0x%08h rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h arg2=0x%08h arg3=0x%08h",
                         i, prologue_cyc[i], prologue_pc[i],
                         prologue_instr[i], prologue_rs[i],
                         prologue_rt[i], prologue_rs_val[i],
                         prologue_rt_val[i], prologue_arg2[i],
                         prologue_arg3[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT prologue2 count=%0d (first %0d captured, retires in 0x%04h-0x%04h — RAM-at-0x500 routine):",
                 prologue2_count,
                 (prologue2_count < PROLOGUE2_DEPTH) ? prologue2_count : PROLOGUE2_DEPTH,
                 PROLOGUE2_LOW[15:0], PROLOGUE2_HIGH[15:0]);
        for (int i = 0; i < PROLOGUE2_DEPTH; i++) begin
            if (i < prologue2_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   prologue2[%0d] cyc=%0d pc=0x%08h instr=0x%08h rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h arg2=0x%08h arg3=0x%08h",
                         i, prologue2_cyc[i], prologue2_pc[i],
                         prologue2_instr[i], prologue2_rs[i],
                         prologue2_rt[i], prologue2_rs_val[i],
                         prologue2_rt_val[i], prologue2_arg2[i],
                         prologue2_arg3[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT r6_consumer count=%0d (first %0d captured between ROM2RAM and ENTRY14):",
                 r6_consumer_count,
                 (r6_consumer_count < R6_CONSUMER_DEPTH) ? r6_consumer_count : R6_CONSUMER_DEPTH);
        for (int i = 0; i < R6_CONSUMER_DEPTH; i++) begin
            if (i < r6_consumer_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   r6_consumer[%0d] cyc=%0d pc=0x%08h instr=0x%08h kind=%s $6=0x%08h arg2=0x%08h arg3=0x%08h",
                         i, r6_consumer_cyc[i], r6_consumer_pc[i],
                         r6_consumer_instr[i],
                         (r6_consumer_kind[i] == 0) ? "JUMP"   :
                         (r6_consumer_kind[i] == 1) ? "LOAD"   :
                         (r6_consumer_kind[i] == 2) ? "STORE"  :
                         (r6_consumer_kind[i] == 3) ? "ALU"    :
                         (r6_consumer_kind[i] == 4) ? "BRANCH" : "OTHER",
                         r6_consumer_r6_val[i], r6_consumer_arg2[i],
                         r6_consumer_arg3[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT vec_table zero_fill=%0d default_fill=%0d patches=%0d (first %0d patches captured, EA in [0x%03h, 0x%03h], neither 0 nor 0x%08h):",
                 vec_zero_fill_count, vec_default_fill_count, vec_patch_count,
                 (vec_patch_count < VEC_TABLE_DEPTH) ? vec_patch_count : VEC_TABLE_DEPTH,
                 VEC_TABLE_LOW[11:0], VEC_TABLE_HIGH[11:0], DEFAULT_FILL);
        for (int i = 0; i < VEC_TABLE_DEPTH; i++) begin
            if (i < vec_patch_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   vec_patch[%0d] cyc=%0d pc=0x%08h instr=0x%08h EA=0x%08h data=0x%08h",
                         i, vec_patch_cyc[i], vec_patch_pc[i],
                         vec_patch_instr[i], vec_patch_ea[i],
                         vec_patch_data[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT vec_intr count=%0d (first %0d captured — patches whose data is a RAM-code pointer):",
                 vec_intr_count,
                 (vec_intr_count < VEC_INTR_DEPTH) ? vec_intr_count : VEC_INTR_DEPTH);
        for (int i = 0; i < VEC_INTR_DEPTH; i++) begin
            if (i < vec_intr_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   vec_intr[%0d] cyc=%0d pc=0x%08h EA=0x%08h data=0x%08h",
                         i, vec_intr_cyc[i], vec_intr_pc[i],
                         vec_intr_ea[i], vec_intr_data[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT r6_write count=%0d (first %0d captured between ROM2RAM and first-$6-consumer):",
                 r6_write_count,
                 (r6_write_count < R6_WRITE_DEPTH) ? r6_write_count : R6_WRITE_DEPTH);
        for (int i = 0; i < R6_WRITE_DEPTH; i++) begin
            if (i < r6_write_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   r6_write[%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h kind=%s arg2=0x%08h arg3=0x%08h",
                         i, r6_write_cyc[i], r6_write_pc[i],
                         r6_write_instr[i], r6_write_prev[i],
                         r6_write_new[i],
                         (r6_write_kind[i] == 0) ? "LUI"    :
                         (r6_write_kind[i] == 1) ? "ADDI_U":
                         (r6_write_kind[i] == 2) ? "LOAD"   :
                         (r6_write_kind[i] == 3) ? "ALU-R"  :
                                                   "OTHER",
                         r6_write_arg2[i], r6_write_arg3[i]);
        end
        if (rom2ram_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT rom2ram cyc=%0d land pc=0x%08h instr=0x%08h",
                     rom2ram_cycle, rom2ram_land_pc, rom2ram_land_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   delay(ring-1) pc=0x%08h instr=0x%08h",
                     rom2ram_delay_pc, rom2ram_delay_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   caller(ring-2) pc=0x%08h instr=0x%08h",
                     rom2ram_caller_pc, rom2ram_caller_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   args $2=0x%08h $4/a0=0x%08h $5/a1=0x%08h $6/a2=0x%08h $7/a3=0x%08h",
                     rom2ram_r2, rom2ram_r4, rom2ram_r5, rom2ram_r6, rom2ram_r7);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   frame $28/gp=0x%08h $29/sp=0x%08h $30/fp=0x%08h $31/ra=0x%08h",
                     rom2ram_r28, rom2ram_r29, rom2ram_r30, rom2ram_r31);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT rom2ram never fired (execution never crossed BIOS ROM into RAM)");
        end
        if (scan_entry_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT scan_entry cyc=%0d pc=0x%08h instr=0x%08h caller pc=0x%08h instr=0x%08h",
                     scan_entry_cycle, scan_entry_pc, scan_entry_instr,
                     scan_entry_caller_pc, scan_entry_caller_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   scan_entry frame ra=0x%08h sp=0x%08h fp=0x%08h $2=0x%08h",
                     scan_entry_ra, scan_entry_sp, scan_entry_fp, scan_entry_r2);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT scan_entry never fired (no retire in 0x%08h-0x%08h)",
                     SCAN_REGION_LOW, SCAN_REGION_HIGH);
        end
        if (scan_exit_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT scan_exit cyc=%0d target pc=0x%08h instr=0x%08h exit-site pc=0x%08h instr=0x%08h",
                     scan_exit_cycle, scan_exit_target_pc,
                     scan_exit_target_instr, scan_exit_site_pc,
                     scan_exit_site_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   scan_exit frame ra=0x%08h sp=0x%08h fp=0x%08h $2=0x%08h",
                     scan_exit_ra, scan_exit_sp, scan_exit_fp, scan_exit_r2);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT scan_exit never fired");
        end
        if (dispatch_entry_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT dispatch_entry cyc=%0d pc=0x%08h instr=0x%08h caller pc=0x%08h instr=0x%08h",
                     dispatch_entry_cycle, dispatch_entry_pc,
                     dispatch_entry_instr, dispatch_entry_caller_pc,
                     dispatch_entry_caller_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   dispatch_entry frame ra=0x%08h sp=0x%08h fp=0x%08h $2=0x%08h",
                     dispatch_entry_ra, dispatch_entry_sp,
                     dispatch_entry_fp, dispatch_entry_r2);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT dispatch_entry never fired");
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT mmio_correl dbg_mmio_reads=%0d dbg_branches=%0d correl=%0d",
                 dbg_mmio_reads, dbg_branches, mmio_correl_count);
        $display("[tb_ee_core_bios_smoke] TIMEOUT mmio_correl count=%0d (first %0d captured, load->branch within %0d retires):",
                 mmio_correl_count,
                 (mmio_correl_count < MMIO_CORREL_DEPTH) ? mmio_correl_count : MMIO_CORREL_DEPTH,
                 MMIO_LOOKAHEAD);
        for (int i = 0; i < MMIO_CORREL_DEPTH; i++) begin
            if (i < mmio_correl_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   mmio_correl[%0d] load cyc=%0d pc=0x%08h EA=0x%08h data=0x%08h $%0d -> branch cyc=%0d pc=0x%08h instr=0x%08h rs=0x%08h rt=0x%08h age=%0d",
                         i, mmio_correl_load_cyc[i], mmio_correl_load_pc[i],
                         mmio_correl_load_ea[i], mmio_correl_load_data[i],
                         mmio_correl_reg[i],
                         mmio_correl_br_cyc[i], mmio_correl_br_pc[i],
                         mmio_correl_br_instr[i],
                         mmio_correl_br_rs_val[i], mmio_correl_br_rt_val[i],
                         mmio_correl_age[i]);
        end
        if (scan_entry_captured && scan_exit_captured && dispatch_entry_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT seam_delta ra: scan_entry=0x%08h scan_exit=0x%08h dispatch_entry=0x%08h (same? %s)",
                     scan_entry_ra, scan_exit_ra, dispatch_entry_ra,
                     ((scan_entry_ra == scan_exit_ra) &&
                      (scan_exit_ra == dispatch_entry_ra)) ? "YES" : "NO");
            $display("[tb_ee_core_bios_smoke] TIMEOUT seam_delta sp: scan_entry=0x%08h scan_exit=0x%08h dispatch_entry=0x%08h (same? %s)",
                     scan_entry_sp, scan_exit_sp, dispatch_entry_sp,
                     ((scan_entry_sp == scan_exit_sp) &&
                      (scan_exit_sp == dispatch_entry_sp)) ? "YES" : "NO");
            $display("[tb_ee_core_bios_smoke] TIMEOUT seam_delta fp: scan_entry=0x%08h scan_exit=0x%08h dispatch_entry=0x%08h (same? %s)",
                     scan_entry_fp, scan_exit_fp, dispatch_entry_fp,
                     ((scan_entry_fp == scan_exit_fp) &&
                      (scan_exit_fp == dispatch_entry_fp)) ? "YES" : "NO");
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT r2_seed total=%0d (last %0d shown newest-first, $2-writes excluding loop pc=0x%08h):",
                 r2_seed_count,
                 (r2_seed_count < R2_SEED_DEPTH) ? r2_seed_count : R2_SEED_DEPTH,
                 R2_LOOP_PC);
        for (int k = 0; k < R2_SEED_DEPTH; k++) begin
            int idx;
            if (k < r2_seed_count) begin
                idx = (r2_seed_head - 1 - k + R2_SEED_DEPTH) % R2_SEED_DEPTH;
                $display("[tb_ee_core_bios_smoke] TIMEOUT   r2_seed[-%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h arg2=0x%08h arg3=0x%08h",
                         k, r2_seed_cyc[idx], r2_seed_pc[idx],
                         r2_seed_instr[idx], r2_seed_prev[idx],
                         r2_seed_new[idx], r2_seed_arg2[idx],
                         r2_seed_arg3[idx]);
            end
        end
        if (r6_ptr_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r6_ptr cycle=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r6_ptr_cycle, r6_ptr_prev, r6_ptr_new,
                     r6_ptr_retired_pc, r6_ptr_retired_instr,
                     r6_ptr_retired_arg2, r6_ptr_retired_arg3);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r6_ptr never fired ($6 never pointed at phys-word 0x%07h)",
                     {5'd0, POINTER_PHYS_WORD});
        end
        if (r2_ptr_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r2_ptr cycle=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r2_ptr_cycle, r2_ptr_prev, r2_ptr_new,
                     r2_ptr_retired_pc, r2_ptr_retired_instr,
                     r2_ptr_retired_arg2, r2_ptr_retired_arg3);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r2_ptr never fired ($2 never pointed at phys-word 0x%07h)",
                     {5'd0, POINTER_PHYS_WORD});
        end
        if (r5_ptr_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r5_ptr cycle=%0d prev=0x%08h new=0x%08h retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r5_ptr_cycle, r5_ptr_prev, r5_ptr_new,
                     r5_ptr_retired_pc, r5_ptr_retired_instr,
                     r5_ptr_retired_arg2, r5_ptr_retired_arg3);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r5_ptr never fired ($5 never pointed at phys-word 0x%07h; expected — $5 is sentinel not pointer)",
                     {5'd0, POINTER_PHYS_WORD});
        end
        if (r8_ex_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r8_install cycle=%0d prev=0x%08h new=0x%08h (dispatch target)",
                     r8_ex_cycle, r8_ex_prev, r8_ex_new);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r8_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r8_ex_retired_pc_now, r8_ex_retired_instr_now,
                     r8_ex_retired_arg2_now, r8_ex_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r8_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r8_ex_retired_pc_d1, r8_ex_retired_instr_d1,
                     r8_ex_retired_arg2_d1, r8_ex_retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r8_src rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     r8_ex_rs_idx, r8_ex_rt_idx,
                     r8_ex_rs_val, r8_ex_rt_val);
            if (entry14_captured) begin
                $display("[tb_ee_core_bios_smoke] TIMEOUT   correlation: $8 installed at cycle=%0d, JR $8 fired at entry14 cycle=%0d (delta=%0d cycles)",
                         r8_ex_cycle, entry14_cycle,
                         entry14_cycle - r8_ex_cycle);
            end
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r8_install never fired ($8 never became 0x%08h)",
                     DISPATCH_TARGET);
        end
        if (r8_base_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r8_base cycle=%0d prev=0x%08h new=0x%08h (dispatch base)",
                     r8_base_cycle, r8_base_prev, r8_base_new);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r8_base_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r8_base_retired_pc_now, r8_base_retired_instr_now,
                     r8_base_retired_arg2_now, r8_base_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r8_base_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r8_base_retired_pc_d1, r8_base_retired_instr_d1,
                     r8_base_retired_arg2_d1, r8_base_retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r8_base_src rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     r8_base_rs_idx, r8_base_rt_idx,
                     r8_base_rs_val, r8_base_rt_val);
            if (r8_ex_captured) begin
                $display("[tb_ee_core_bios_smoke] TIMEOUT   base→install delta=%0d cycles (base at %0d, install at %0d)",
                         r8_ex_cycle - r8_base_cycle,
                         r8_base_cycle, r8_ex_cycle);
            end
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r8_base never fired ($8 never became 0x%08h)",
                     DISPATCH_BASE);
        end
        if (r9_ex_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r9_index cycle=%0d prev=0x%08h new=0x%08h (dispatch index=%0d)",
                     r9_ex_cycle, r9_ex_prev, r9_ex_new, r9_ex_new[4:0]);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r9_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r9_ex_retired_pc_now, r9_ex_retired_instr_now,
                     r9_ex_retired_arg2_now, r9_ex_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r9_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     r9_ex_retired_pc_d1, r9_ex_retired_instr_d1,
                     r9_ex_retired_arg2_d1, r9_ex_retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   r9_src rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     r9_ex_rs_idx, r9_ex_rt_idx,
                     r9_ex_rs_val, r9_ex_rt_val);
            if (r8_base_captured) begin
                $display("[tb_ee_core_bios_smoke] TIMEOUT   index→base delta=%0d cycles (r9=0x18 at %0d → r8=0xEB4 at %0d)",
                         r8_base_cycle - r9_ex_cycle,
                         r9_ex_cycle, r8_base_cycle);
            end
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT r9_index never fired ($9 never became 0x%08h)",
                     DISPATCH_INDEX);
        end
        if (scan_final_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT scan_final_exit cyc=%0d pc=0x%08h $k0=0x%08h $k1=0x%08h $2=0x%08h",
                     scan_final_cycle, scan_final_pc,
                     scan_final_k0, scan_final_k1, scan_final_r2);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   ring-1 pc=0x%08h instr=0x%08h",
                     scan_final_ring_prev_pc, scan_final_ring_prev_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   ring-2 pc=0x%08h instr=0x%08h",
                     scan_final_ring_prev2_pc, scan_final_ring_prev2_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   $k0==$k1? %s (scan terminated on natural bound if YES)",
                     (scan_final_k0 == scan_final_k1) ? "YES" : "NO");
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT scan_final_exit never fired (no retire at pc=0x%08h)",
                     SCAN_EXIT_TARGET);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT r2_gap count=%0d ($2 writes in [R9_INDEX..R8_BASE], excluding pc=0x%08h):",
                 r2_gap_count, R2_LOOP_PC);
        for (int i = 0; i < 4; i++) begin
            if (i < r2_gap_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   r2_gap[%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h",
                         i, r2_gap_cyc[i], r2_gap_pc[i],
                         r2_gap_instr[i], r2_gap_prev[i], r2_gap_new[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT tlb_cop0 count=%0d (MTC0/TLBR/TLBWI/TLBWR/TLBP retires pre-ENTRY14):",
                 tlb_cop0_count);
        for (int i = 0; i < TLB_COP0_DEPTH; i++) begin
            if (i < tlb_cop0_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   tlb_cop0[%0d] cyc=%0d pc=0x%08h instr=0x%08h kind=%s rt=$%0d rd=cop0[%0d] value=0x%08h",
                         i, tlb_cop0_cyc[i], tlb_cop0_pc[i],
                         tlb_cop0_instr[i],
                         (tlb_cop0_kind[i] == 0) ? "MTC0"  :
                         (tlb_cop0_kind[i] == 1) ? "TLBR"  :
                         (tlb_cop0_kind[i] == 2) ? "TLBWI" :
                         (tlb_cop0_kind[i] == 3) ? "TLBWR" :
                         (tlb_cop0_kind[i] == 4) ? "TLBP"  : "?",
                         tlb_cop0_rt[i], tlb_cop0_rd[i],
                         tlb_cop0_value[i]);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT lw_prologue count=%0d (loads in pc=[0x%04h..0x%04h] list-walker body):",
                 lw_prologue_count,
                 LW_PROLOGUE_LOW[15:0], LW_PROLOGUE_HIGH[15:0]);
        for (int i = 0; i < LW_PROLOGUE_DEPTH; i++) begin
            if (i < lw_prologue_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   lw_prologue[%0d] cyc=%0d pc=0x%08h instr=0x%08h rs=$%0d rt=$%0d rs_val=0x%08h EA=0x%08h data=0x%08h",
                         i, lw_prologue_cyc[i], lw_prologue_pc[i],
                         lw_prologue_instr[i],
                         lw_prologue_rs[i], lw_prologue_rt[i],
                         lw_prologue_rs_val[i],
                         lw_prologue_ea[i], lw_prologue_data[i]);
        end
        if (k1_ex_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT k1_bound cycle=%0d prev=0x%08h new=0x%08h",
                     k1_ex_cycle, k1_ex_prev, k1_ex_new);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   k1_now retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     k1_ex_retired_pc_now, k1_ex_retired_instr_now,
                     k1_ex_retired_arg2_now, k1_ex_retired_arg3_now);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   k1_d1  retired_pc=0x%08h instr=0x%08h arg2=0x%08h arg3=0x%08h",
                     k1_ex_retired_pc_d1, k1_ex_retired_instr_d1,
                     k1_ex_retired_arg2_d1, k1_ex_retired_arg3_d1);
            $display("[tb_ee_core_bios_smoke] TIMEOUT   k1_src rs=$%0d rt=$%0d rs_val=0x%08h rt_val=0x%08h",
                     k1_ex_rs_idx, k1_ex_rt_idx,
                     k1_ex_rs_val, k1_ex_rt_val);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT k1_bound never fired ($27 never became 0x%08h)",
                     K1_BOUND_VALUE);
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT scan_last_exit total=%0d pre-ENTRY14 (last %0d shown newest-first):",
                 scan_last_total,
                 (scan_last_total < SCAN_EXIT_RING_DEPTH) ? scan_last_total : SCAN_EXIT_RING_DEPTH);
        for (int k = 0; k < SCAN_EXIT_RING_DEPTH; k++) begin
            int idx;
            if (k < scan_last_total) begin
                idx = (scan_last_head - 1 - k + SCAN_EXIT_RING_DEPTH) %
                      SCAN_EXIT_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] TIMEOUT   scan_last[-%0d] cyc=%0d $k0=0x%08h $k1=0x%08h $2=0x%08h ring-1=0x%08h:0x%08h $k0==$k1? %s",
                         k, scan_last_cyc[idx],
                         scan_last_k0[idx], scan_last_k1[idx],
                         scan_last_r2[idx],
                         scan_last_ring_prev_pc[idx],
                         scan_last_ring_prev_instr[idx],
                         (scan_last_k0[idx] == scan_last_k1[idx]) ? "YES" : "NO");
            end
        end
        $display("[tb_ee_core_bios_smoke] TIMEOUT r2_setup count=%0d (first %0d captured in [R8_BASE..R8_INSTALL], excluding pc=0x%08h scan loop):",
                 r2_setup_count,
                 (r2_setup_count < R2_SETUP_DEPTH) ? r2_setup_count : R2_SETUP_DEPTH,
                 R2_LOOP_PC);
        for (int i = 0; i < R2_SETUP_DEPTH; i++) begin
            if (i < r2_setup_count)
                $display("[tb_ee_core_bios_smoke] TIMEOUT   r2_setup[%0d] cyc=%0d pc=0x%08h instr=0x%08h prev=0x%08h new=0x%08h kind=%s arg2=0x%08h arg3=0x%08h",
                         i, r2_setup_cyc[i], r2_setup_pc[i],
                         r2_setup_instr[i], r2_setup_prev[i],
                         r2_setup_new[i],
                         (r2_setup_kind[i] == 0) ? "LUI"    :
                         (r2_setup_kind[i] == 1) ? "ADDI_U":
                         (r2_setup_kind[i] == 2) ? "LOAD"   :
                         (r2_setup_kind[i] == 3) ? "ALU-R"  :
                                                   "OTHER",
                         r2_setup_arg2[i], r2_setup_arg3[i]);
        end
        // Chapter 25 final-state snapshot of the indexed array.
        $display("[tb_ee_core_bios_smoke] TIMEOUT array_peek RAM[0x0E54..0x0EB4] (25 words, final state):");
        for (int k = 0; k < 25; k++) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT   [0x%04h] = 0x%08h",
                     32'h0E54 + k*4,
                     peek_instr(32'h0000_0E54 + k*4));
        end

        // Ch68 — loop characterization summary at TIMEOUT (in case
        // post-halt block doesn't fire because the run timed out).
        $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_SUMMARY total_in_range=%0d count[0x001A4564]=%0d count[0x001A45A0]=%0d load_count=%0d branch_count=%0d",
                 loop_ring_count, loop_count_4564, loop_count_45a0,
                 loop_load_count, loop_branch_count);
        if (loop_ea_seen)
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_EA min=0x%08h max=0x%08h span=%0d bytes",
                     loop_ea_min, loop_ea_max,
                     loop_ea_max - loop_ea_min);
        if (loop_first_seen) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_PROGRESSION $4: first=0x%08h last=0x%08h delta=%0d",
                     r4_first, r4_last, $signed(r4_last - r4_first));
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_PROGRESSION $5: first=0x%08h last=0x%08h delta=%0d",
                     r5_first, r5_last, $signed(r5_last - r5_first));
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_PROGRESSION $6: first=0x%08h last=0x%08h delta=%0d",
                     r6_first, r6_last, $signed(r6_last - r6_first));
        end
        if (last_branch_seen)
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_LAST_BRANCH cyc=%0d pc=0x%08h instr=0x%08h op=0x%02h rs=$%0d rt=$%0d  rs_val=0x%08h rt_val=0x%08h",
                     last_branch_cyc, last_branch_pc, last_branch_instr,
                     last_branch_instr[31:26],
                     last_branch_rs_idx, last_branch_rt_idx,
                     last_branch_rs_val, last_branch_rt_val);
        `ifndef CH71_LONG_RUN
        $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_RING tail (most-recent %0d):",
                 (loop_ring_count < LOOP_RING_DEPTH) ? loop_ring_count : LOOP_RING_DEPTH);
        begin
            int n;
            int base;
            int idx;
            n    = (loop_ring_count < LOOP_RING_DEPTH) ? loop_ring_count : LOOP_RING_DEPTH;
            base = (loop_ring_count < LOOP_RING_DEPTH) ? 0 : loop_ring_head;
            for (int k = 0; k < n; k = k + 1) begin
                idx = (base + k) % LOOP_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_RING[%0d] cyc=%0d retires=%0d pc=0x%08h instr=0x%08h  $4=0x%08h $5=0x%08h $6=0x%08h  arg2=0x%08h arg3=0x%08h",
                         k,
                         loop_ring_cyc[idx], loop_ring_retires[idx],
                         loop_ring_pc[idx], loop_ring_instr[idx],
                         loop_ring_r4[idx], loop_ring_r5[idx],
                         loop_ring_r6[idx],
                         loop_ring_arg2[idx], loop_ring_arg3[idx]);
            end
        end
        `endif
        // Wider PEEK in the loop region (extends Ch66's 32-word peek
        // to cover the whole [0x001A4530..0x001A4640) range, both
        // backings, so we can see the back-branch instruction).
        $display("[tb_ee_core_bios_smoke] TIMEOUT PEEK loop body [0x001A4530..0x001A4640) (useg_shadow):");
        for (int k = 0; k < 68; k = k + 1) begin
            logic [31:0] a;
            a = 32'h001A_4530 + k*4;
            $display("[tb_ee_core_bios_smoke] TIMEOUT PEEK   [0x%08h] = 0x%08h",
                     a, peek_useg_shadow(a));
        end

        // Ch69 — exit/no-exit summary + projected rate (timeout path).
        if (loop_exit_captured) begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_EXIT cyc=%0d retires=%0d total_in_range=%0d",
                     loop_exit_cyc, loop_exit_retires,
                     loop_exit_total_in_range);
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_EXIT   exit pc=0x%08h instr=0x%08h",
                     loop_exit_pc, loop_exit_instr);
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_EXIT   regs $2=0x%08h $3=0x%08h $4=0x%08h $5=0x%08h $6=0x%08h",
                     loop_exit_r2, loop_exit_r3, loop_exit_r4,
                     loop_exit_r5, loop_exit_r6);
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_EXIT   regs $7=0x%08h $8=0x%08h $9=0x%08h $10=0x%08h $31=0x%08h",
                     loop_exit_r7, loop_exit_r8, loop_exit_r9,
                     loop_exit_r10, loop_exit_r31);
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_EXIT   EA span at exit: min=0x%08h max=0x%08h (%0d bytes)",
                     loop_exit_ea_min, loop_exit_ea_max,
                     loop_exit_ea_max - loop_exit_ea_min);
            $display("[tb_ee_core_bios_smoke] TIMEOUT POSTEXIT %0d retires after loop exit:",
                     postexit_count);
            for (int k = 0; k < POSTEXIT_DEPTH; k = k + 1) begin
                if (k < postexit_count) begin
                    $display("[tb_ee_core_bios_smoke] TIMEOUT POSTEXIT[%0d] cyc=%0d pc=0x%08h instr=0x%08h  $2=0x%08h $4=0x%08h $5=0x%08h $31=0x%08h  arg2=0x%08h arg3=0x%08h",
                             k, postexit_cyc[k],
                             postexit_pc[k], postexit_instr[k],
                             postexit_r2[k],  postexit_r4[k],
                             postexit_r5[k],  postexit_r31[k],
                             postexit_arg2[k], postexit_arg3[k]);
                end
            end
        end else if (loop_seen) begin
            // Loop never exited within the run — print projected
            // progress rate so we can extrapolate when it would.
            int    bytes_advanced;
            int    retires_in_loop;
            int    cycles_in_loop;
            real   bytes_per_retire;
            real   bytes_per_cycle;
            bytes_advanced  = $signed(r4_last - r4_first);
            retires_in_loop = core_retired_events - loop_first_retires;
            cycles_in_loop  = sim_cycle_ctr      - loop_first_cyc;
            bytes_per_retire = (retires_in_loop > 0)
                                ? real'(bytes_advanced) / real'(retires_in_loop)
                                : 0.0;
            bytes_per_cycle  = (cycles_in_loop > 0)
                                ? real'(bytes_advanced) / real'(cycles_in_loop)
                                : 0.0;
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_NO_EXIT loop never left [0x001A4530..0x001A4640) within %0d retires (%0d cycles); $4 advanced %0d bytes",
                     retires_in_loop, cycles_in_loop, bytes_advanced);
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_NO_EXIT   projected rate: %f bytes/retire   %f bytes/cycle",
                     bytes_per_retire, bytes_per_cycle);
        end else begin
            $display("[tb_ee_core_bios_smoke] TIMEOUT LOOP_EXIT loop never entered (no retire in [0x001A4530..0x001A4640))");
        end

        // --------------------------------------------------------------
        // Ch179 — wait-loop autopsy. Print the instruction window
        // around the current PC (using the Ch179-fixed peek_instr
        // that routes useg fetches through useg_shadow_mem), the
        // load-ring history (last N loads observed on the EE side),
        // a register-file snapshot of the candidate operand regs,
        // and a single [ch179] wait_loop summary line.
        // --------------------------------------------------------------
        begin : ch179_autopsy
            int           latest_idx;
            int           hits_pc, hits_pc_m4;
            int           ring_size;
            logic [31:0]  pc_now;
            logic [31:0]  observed_addr;
            logic [31:0]  observed_data;
            string        observed_region;
            string        verdict;
            pc_now = core_pc;

            $display("[tb_ee_core_bios_smoke] Ch179 wait-loop autopsy:");
            // 16-instruction window around the current PC (PC-32 to PC+28).
            $display("[tb_ee_core_bios_smoke] Ch179   instr window around pc=0x%08h:", pc_now);
            for (int i = -8; i <= 7; i++) begin
                logic [31:0] a, w;
                a = pc_now + (i * 4);
                w = peek_instr(a);
                $display("[tb_ee_core_bios_smoke] Ch179     %s 0x%08h: 0x%08h  %s",
                         (i == 0) ? ">>" : "  ",
                         a, w, mips_mnemonic(w));
            end

            // Most-recent loads observed by the EE (ring buffer).
            ring_size = (load_ring_count < LOAD_RING_DEPTH) ? load_ring_count : LOAD_RING_DEPTH;
            $display("[tb_ee_core_bios_smoke] Ch179   load ring (%0d entries, newest first):",
                     ring_size);
            for (int i = 0; i < ring_size; i++) begin
                int slot;
                slot = (load_ring_count - 1 - i + LOAD_RING_DEPTH) % LOAD_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] Ch179     [%0d] cyc=%0d pc=0x%08h addr=0x%08h data=0x%08h region=%s",
                         i, load_ring_cycle[slot], load_ring_pc[slot],
                         load_ring_addr[slot], load_ring_data[slot],
                         ch179_region(load_ring_addr[slot]));
            end

            // Register snapshot — candidates for the polled-pointer
            // and polled-mask roles, plus the usual call-frame regs.
            $display("[tb_ee_core_bios_smoke] Ch179   regfile snapshot: $1=0x%08h $2=0x%08h $3=0x%08h $4=0x%08h $5=0x%08h $6=0x%08h $7=0x%08h $8=0x%08h $9=0x%08h $10=0x%08h $11=0x%08h $26=0x%08h $27=0x%08h $30=0x%08h $31=0x%08h",
                     u_core.regfile[1],  u_core.regfile[2],  u_core.regfile[3],
                     u_core.regfile[4],  u_core.regfile[5],  u_core.regfile[6],
                     u_core.regfile[7],  u_core.regfile[8],  u_core.regfile[9],
                     u_core.regfile[10], u_core.regfile[11], u_core.regfile[26],
                     u_core.regfile[27], u_core.regfile[30], u_core.regfile[31]);
            // Ch179 — COP0 status / cause / count / EPC. Crucial if
            // the loop is a "spin until interrupt" pattern; we need
            // to see whether interrupts are enabled (Status.IE),
            // which IRQ mask bits are armed, whether Cause shows a
            // pending IRQ, and whether the timer (Count) is moving.
            $display("[tb_ee_core_bios_smoke] Ch179   COP0 snapshot: Status=0x%08h Cause=0x%08h Count=0x%08h EPC=0x%08h",
                     u_core.status_word, u_core.cause_word,
                     u_core.cop0_count, u_core.epc);

            // One-line summary per Codex Ch179 contract. Honest
            // about whether the loop is actually polling memory
            // (recent data load within `RECENT_LOAD_CYCLES` of
            // timeout) vs. spinning without loads (memset / busy-
            // wait / waiting on something other than memory).
            // The pre-filter ring only counts true DATA loads, so
            // a stale newest-load entry means the loop hasn't read
            // memory in a long time.
            begin
                int latest_cyc;
                int latency;
                if (ring_size > 0) begin
                    latest_idx = (load_ring_count - 1 + LOAD_RING_DEPTH) % LOAD_RING_DEPTH;
                    latest_cyc = load_ring_cycle[latest_idx];
                    latency    = sim_cycle_ctr - latest_cyc;
                    if (latency < 1024) begin
                        // Recent data load — call it the polled value.
                        observed_addr   = load_ring_addr[latest_idx];
                        observed_data   = load_ring_data[latest_idx];
                        observed_region = ch179_region(observed_addr);
                        verdict         = (observed_data == 32'd0) ? "zero" :
                                          "nonzero";
                    end else begin
                        observed_addr   = 32'd0;
                        observed_data   = 32'd0;
                        observed_region = "no-recent-load";
                        verdict         = $sformatf("no-data-load-in-last-%0d-cycles", latency);
                    end
                end else begin
                    observed_addr   = 32'd0;
                    observed_data   = 32'd0;
                    observed_region = "no-loads";
                    verdict         = "n/a";
                end
                $display("[ch179] wait_loop pc=0x%08h load_addr=0x%08h observed=0x%08h waiting_for=%s region=%s",
                         pc_now, observed_addr, observed_data,
                         verdict, observed_region);
            end
        end

        // --------------------------------------------------------------
        // Ch180 — $1 ($at) provenance + ENTRY598 snapshot.
        // The Ch179 autopsy showed the broken-memset loop hinges on
        // $1 = 0xBF800000 (the EE BIU/MMIO base — not a sensible
        // counter). Ch180 prints the last R1_RING_DEPTH writes to $1
        // so Codex can see exactly which instruction produced that
        // value, plus the function-entry register snapshot at
        // PC=0x598 so we know whether $1 was already bad on entry.
        // --------------------------------------------------------------
        begin : ch180_autopsy
            int           ring_size;
            int           latest_idx;
            logic [31:0]  last_pc, last_instr, last_old, last_new;
            string        last_class;
            int           last_cyc;
            ring_size = (r1_ring_count < R1_RING_DEPTH)
                            ? r1_ring_count : R1_RING_DEPTH;
            $display("[tb_ee_core_bios_smoke] Ch180 r1 lineage (last %0d writes; newest first):",
                     ring_size);
            for (int i = 0; i < ring_size; i++) begin
                int slot;
                slot = (r1_ring_count - 1 - i + R1_RING_DEPTH) % R1_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] Ch180   [%0d] cyc=%0d pc=0x%08h instr=0x%08h old=0x%08h new=0x%08h mnemonic=%s",
                         i, r1_ring_cyc[slot], r1_ring_pc[slot],
                         r1_ring_instr[slot],
                         r1_ring_prev[slot], r1_ring_new[slot],
                         mips_mnemonic(r1_ring_instr[slot]));
            end

            if (entry598_captured) begin
                $display("[tb_ee_core_bios_smoke] Ch180 entry598 cycle=%0d $1=0x%08h $2=0x%08h $3=0x%08h $4=0x%08h $5=0x%08h $6=0x%08h $7=0x%08h $29=0x%08h $30=0x%08h $31=0x%08h",
                         entry598_cycle, entry598_r1, entry598_r2,
                         entry598_r3, entry598_r4, entry598_r5,
                         entry598_r6, entry598_r7,
                         entry598_r29, entry598_r30, entry598_r31);
                $display("[tb_ee_core_bios_smoke] Ch180 entry598 call-site ring-1 pc=0x%08h instr=0x%08h  ring-2 pc=0x%08h instr=0x%08h",
                         entry598_ring_prev_pc, entry598_ring_prev_instr,
                         entry598_ring_prev2_pc, entry598_ring_prev2_instr);
                // Caller window — instructions around (entry598_r31 - 4).
                if (entry598_r31 >= 32'd4) begin
                    logic [31:0] caller_pc;
                    caller_pc = entry598_r31 - 32'd4;
                    $display("[tb_ee_core_bios_smoke] Ch180 caller window around 0x%08h ($31-4 at entry598):",
                             caller_pc);
                    for (int i = -6; i <= 5; i++) begin
                        logic [31:0] a, w;
                        a = caller_pc + (i * 4);
                        w = peek_instr(a);
                        $display("[tb_ee_core_bios_smoke] Ch180   %s 0x%08h: 0x%08h  %s",
                                 (i == 0) ? ">>" : "  ",
                                 a, w, mips_mnemonic(w));
                    end
                end
            end else begin
                $display("[tb_ee_core_bios_smoke] Ch180 entry598 NEVER CAPTURED (BIOS retired no instruction at PC=0x598; the wait-loop hypothesis from Ch179 may be wrong about which function we're in)");
            end

            // [ch180] r1_lineage summary line per Codex contract.
            if (ring_size > 0) begin
                latest_idx = (r1_ring_count - 1 + R1_RING_DEPTH) % R1_RING_DEPTH;
                last_pc    = r1_ring_pc[latest_idx];
                last_instr = r1_ring_instr[latest_idx];
                last_old   = r1_ring_prev[latest_idx];
                last_new   = r1_ring_new[latest_idx];
                last_cyc   = r1_ring_cyc[latest_idx];
                last_class = mips_mnemonic(last_instr);
                $display("[ch180] r1_lineage last_write_cyc=%0d last_write_pc=0x%08h instr=0x%08h old=0x%08h new=0x%08h mnemonic=%s",
                         last_cyc, last_pc, last_instr,
                         last_old, last_new, last_class);
                // Instruction window around the last $1 write. The
                // typical `la $1, addr` pseudo-op is LUI + ORI/ADDIU
                // back-to-back; if we see LUI then a non-ORI/non-
                // ADDIU instruction NOT touching $1, the address
                // materialization was abandoned mid-way.
                $display("[tb_ee_core_bios_smoke] Ch180 instr window around last $1 write site 0x%08h:",
                         last_pc);
                for (int i = -4; i <= 5; i++) begin
                    logic [31:0] a, w;
                    a = last_pc + (i * 4);
                    w = peek_instr(a);
                    $display("[tb_ee_core_bios_smoke] Ch180   %s 0x%08h: 0x%08h  %s",
                             (i == 0) ? ">>" : "  ",
                             a, w, mips_mnemonic(w));
                end
            end else begin
                $display("[ch180] r1_lineage no writes captured ($1 untouched since reset)");
            end
            if (entry598_captured) begin
                $display("[ch180] entry pc=0x00000598 r1=0x%08h r2=0x%08h r3=0x%08h r4=0x%08h r31=0x%08h (latest of %0d total entries)",
                         entry598_r1, entry598_r2, entry598_r3,
                         entry598_r4, entry598_r31, entry598_count);
            end else begin
                $display("[ch180] entry pc=0x00000598 NEVER REACHED");
            end

            // ----------------------------------------------------------
            // Ch181 — verdict on how the loop body got entered with
            // stale $1. Print the latest ENTRY598 snapshot (which
            // overwrites with every entry), the latest anomalous
            // ENTRY5A8 (if any), and a verdict line.
            // ----------------------------------------------------------
            $display("[tb_ee_core_bios_smoke] Ch181 totals: entry598_count=%0d entry5a8_total=%0d entry5a8_anomalous=%0d",
                     entry598_count, entry5a8_total_count, entry5a8_anomalous_count);
            if (entry598_count > 0) begin
                $display("[tb_ee_core_bios_smoke] Ch181 latest-entry598 cyc=%0d $1=0x%08h $2=0x%08h $3=0x%08h $4=0x%08h $5=0x%08h $8=0x%08h $31=0x%08h",
                         entry598_cycle,
                         entry598_r1, entry598_r2, entry598_r3,
                         entry598_r4, entry598_r5, entry598_r8,
                         entry598_r31);
                $display("[tb_ee_core_bios_smoke] Ch181   prev chain: 0x%08h(%s) 0x%08h(%s) 0x%08h(%s) 0x%08h(%s)",
                         entry598_ring_prev_pc,  mips_mnemonic(entry598_ring_prev_instr),
                         entry598_ring_prev2_pc, mips_mnemonic(entry598_ring_prev2_instr),
                         entry598_ring_prev3_pc, mips_mnemonic(entry598_ring_prev3_instr),
                         entry598_ring_prev4_pc, mips_mnemonic(entry598_ring_prev4_instr));
            end
            if (entry5a8_anomalous_captured) begin
                $display("[tb_ee_core_bios_smoke] Ch181 latest-anomalous-5a8 cyc=%0d $1=0x%08h $2=0x%08h $3=0x%08h $31=0x%08h prev_pc=0x%08h prev_instr=0x%08h(%s)",
                         entry5a8_cycle,
                         entry5a8_r1, entry5a8_r2, entry5a8_r3, entry5a8_r31,
                         entry5a8_prev_pc, entry5a8_prev_instr,
                         mips_mnemonic(entry5a8_prev_instr));
                $display("[tb_ee_core_bios_smoke] Ch181   prev chain: 0x%08h 0x%08h 0x%08h",
                         entry5a8_ring_prev2_pc,
                         entry5a8_ring_prev3_pc,
                         entry5a8_ring_prev4_pc);
            end
            // Verdict line.
            if (entry5a8_anomalous_count == 0) begin
                $display("[ch181] verdict=head_entry (loop body always reached via 0x598 fall-through; entry598_count=%0d, no mid-body jumps to 0x5A8 observed)",
                         entry598_count);
            end else begin
                $display("[ch181] verdict=mid_body_jump (entry5a8_anomalous_count=%0d; latest anomalous prev_pc=0x%08h)",
                         entry5a8_anomalous_count, entry5a8_prev_pc);
            end

            // ----------------------------------------------------------
            // Ch182 — $31 ($ra) provenance ring + dispatcher window.
            // The most-recent write before TIMEOUT should be the one
            // that established $31=0x00030044 for ENTRY598#23. Print
            // the last R31_RING_DEPTH writes with source-class
            // classification (jal/jalr-link vs ALU vs load vs other),
            // then dump the dispatcher 0x500-0x510 with mnemonics so
            // Codex can see exactly what flows in the per-call path.
            // ----------------------------------------------------------
            begin : ch182_autopsy
                int           r31_ring_size;
                int           latest_idx_r31;
                logic [31:0]  last_r31_pc, last_r31_instr, last_r31_old, last_r31_new;
                string        last_r31_class;
                int           last_r31_cyc;
                r31_ring_size = (r31_ring_count < R31_RING_DEPTH)
                                  ? r31_ring_count : R31_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] Ch182 r31 lineage (last %0d writes; newest first):",
                         r31_ring_size);
                for (int i = 0; i < r31_ring_size; i++) begin
                    int slot;
                    slot = (r31_ring_count - 1 - i + R31_RING_DEPTH) % R31_RING_DEPTH;
                    $display("[tb_ee_core_bios_smoke] Ch182   [%0d] cyc=%0d pc=0x%08h instr=0x%08h old=0x%08h new=0x%08h mnemonic=%s source=%s",
                             i, r31_ring_cyc[slot], r31_ring_pc[slot],
                             r31_ring_instr[slot],
                             r31_ring_prev[slot], r31_ring_new[slot],
                             mips_mnemonic(r31_ring_instr[slot]),
                             r31_source_class(r31_ring_instr[slot]));
                end

                // Dispatcher window 0x500-0x510 — all entries to 0x598
                // routed through here per Ch181's prev-chain finding.
                $display("[tb_ee_core_bios_smoke] Ch182 dispatcher window 0x500-0x514:");
                for (int i = 0; i <= 5; i++) begin
                    logic [31:0] a, w;
                    a = 32'h0000_0500 + (i * 4);
                    w = peek_instr(a);
                    $display("[tb_ee_core_bios_smoke] Ch182   0x%08h: 0x%08h  %s  source-if-wrote-r31=%s",
                             a, w, mips_mnemonic(w), r31_source_class(w));
                end

                // [ch182] summary line + decoded detail of the last
                // write. For jal/jalr we print the jump target + the
                // saved link PC. For loads we print EA + value. For
                // immediates we just print the materialized value.
                if (r31_ring_size > 0) begin
                    logic [5:0] op_last, func_last;
                    logic [4:0] rs_last;
                    latest_idx_r31 = (r31_ring_count - 1 + R31_RING_DEPTH) % R31_RING_DEPTH;
                    last_r31_pc    = r31_ring_pc[latest_idx_r31];
                    last_r31_instr = r31_ring_instr[latest_idx_r31];
                    last_r31_old   = r31_ring_prev[latest_idx_r31];
                    last_r31_new   = r31_ring_new[latest_idx_r31];
                    last_r31_cyc   = r31_ring_cyc[latest_idx_r31];
                    last_r31_class = r31_source_class(last_r31_instr);
                    op_last        = last_r31_instr[31:26];
                    func_last      = last_r31_instr[5:0];
                    rs_last        = last_r31_instr[25:21];

                    $display("[ch182] r31_lineage last_write_cyc=%0d last_write_pc=0x%08h instr=0x%08h old=0x%08h new=0x%08h mnemonic=%s source=%s",
                             last_r31_cyc, last_r31_pc, last_r31_instr,
                             last_r31_old, last_r31_new,
                             mips_mnemonic(last_r31_instr), last_r31_class);

                    if (op_last == 6'h03) begin
                        // JAL: target = (PC+4) high4 ++ (instr[25:0] << 2).
                        // Link saved to $31 = PC+8 (typically).
                        logic [31:0] target;
                        target = {last_r31_pc[31:28], last_r31_instr[25:0], 2'b00};
                        $display("[ch182]   detail jal target=0x%08h expected_link=0x%08h",
                                 target, last_r31_pc + 32'd8);
                    end else if (op_last == 6'h00 && func_last == 6'h09) begin
                        // JALR: target = $rs (we don't have access to
                        // the rs value at retire time; report rs index
                        // for grep-ability). Link = PC + 8.
                        $display("[ch182]   detail jalr rs=$%0d expected_link=0x%08h",
                                 rs_last, last_r31_pc + 32'd8);
                    end else if (op_last >= 6'h20 && op_last <= 6'h27) begin
                        $display("[ch182]   detail load opcode=0x%02h rs=$%0d imm=0x%04h (EA = $%0d + imm)",
                                 op_last, rs_last, last_r31_instr[15:0], rs_last);
                    end else if (op_last == 6'h0F) begin
                        $display("[ch182]   detail lui imm=0x%04h → new $31 high16",
                                 last_r31_instr[15:0]);
                    end else if (op_last == 6'h0D || op_last == 6'h09 || op_last == 6'h08) begin
                        $display("[ch182]   detail %s rs=$%0d imm=0x%04h",
                                 mips_mnemonic(last_r31_instr), rs_last,
                                 last_r31_instr[15:0]);
                    end else begin
                        $display("[ch182]   detail (other) op=0x%02h func=0x%02h rs=$%0d rt=$%0d",
                                 op_last, func_last, rs_last,
                                 last_r31_instr[20:16]);
                    end
                end else begin
                    $display("[ch182] r31_lineage no writes captured ($31 untouched since reset)");
                end
            end

            // ----------------------------------------------------------
            // Ch183 — $10 lineage + bad-jalr snapshot. The Ch182
            // result identified `jalr $10` at PC=0x0003003C as the
            // bad call site. Here we print:
            //   - the full R10 ring (last 16 writes to $10),
            //   - the registers captured at the moment of that jalr,
            //   - a "$1 changed in setup window?" verdict comparing
            //     the cycle of the latest $1 write before the jalr
            //     against the cycle of the latest $10 write.
            // ----------------------------------------------------------
            begin : ch183_autopsy
                int r10_ring_size;
                int latest_idx_r10;
                int latest_idx_r1;
                int r10_last_write_cyc;
                int r1_last_write_cyc;
                logic [31:0] r10_last_write_pc, r10_last_write_instr, r10_last_write_old, r10_last_write_new;
                logic [31:0] r1_last_write_pc, r1_last_write_instr, r1_last_write_old, r1_last_write_new;
                string r10_last_source;
                logic r1_changed_after_r10;

                r10_ring_size = (r10_ring_count < R10_RING_DEPTH)
                                  ? r10_ring_count : R10_RING_DEPTH;
                $display("[tb_ee_core_bios_smoke] Ch183 r10 lineage (last %0d writes; newest first):",
                         r10_ring_size);
                for (int i = 0; i < r10_ring_size; i++) begin
                    int slot;
                    slot = (r10_ring_count - 1 - i + R10_RING_DEPTH) % R10_RING_DEPTH;
                    $display("[tb_ee_core_bios_smoke] Ch183   [%0d] cyc=%0d pc=0x%08h instr=0x%08h old=0x%08h new=0x%08h mnemonic=%s source=%s",
                             i, r10_ring_cyc[slot], r10_ring_pc[slot],
                             r10_ring_instr[slot],
                             r10_ring_prev[slot], r10_ring_new[slot],
                             mips_mnemonic(r10_ring_instr[slot]),
                             r31_source_class(r10_ring_instr[slot]));
                end

                if (bad_jalr_captured) begin
                    $display("[tb_ee_core_bios_smoke] Ch183 bad-jalr snapshot: cyc=%0d $10=0x%08h $1=0x%08h $8=0x%08h $31=0x%08h $2=0x%08h $3=0x%08h $4=0x%08h $5=0x%08h $29/sp=0x%08h",
                             bad_jalr_cycle,
                             bad_jalr_r10, bad_jalr_r1, bad_jalr_r8,
                             bad_jalr_r31, bad_jalr_r2, bad_jalr_r3,
                             bad_jalr_r4, bad_jalr_r5, bad_jalr_r29);
                    $display("[tb_ee_core_bios_smoke] Ch183 ring sizes at trigger: r10_ring_count=%0d r1_ring_count=%0d r31_ring_count=%0d",
                             bad_jalr_r10_ring_count_at_trigger,
                             bad_jalr_r1_ring_count_at_trigger,
                             bad_jalr_r31_ring_count_at_trigger);
                end else begin
                    $display("[tb_ee_core_bios_smoke] Ch183 bad-jalr NEVER captured (PC=0x0003003C never retired)");
                end

                // [ch183] r10_lineage + did_$1_change_in_setup summary.
                if (r10_ring_size > 0) begin
                    latest_idx_r10        = (r10_ring_count - 1 + R10_RING_DEPTH) % R10_RING_DEPTH;
                    r10_last_write_cyc    = r10_ring_cyc[latest_idx_r10];
                    r10_last_write_pc     = r10_ring_pc[latest_idx_r10];
                    r10_last_write_instr  = r10_ring_instr[latest_idx_r10];
                    r10_last_write_old    = r10_ring_prev[latest_idx_r10];
                    r10_last_write_new    = r10_ring_new[latest_idx_r10];
                    r10_last_source       = r31_source_class(r10_last_write_instr);
                    $display("[ch183] r10_lineage last_write_cyc=%0d pc=0x%08h instr=0x%08h old=0x%08h new=0x%08h mnemonic=%s source=%s",
                             r10_last_write_cyc, r10_last_write_pc,
                             r10_last_write_instr,
                             r10_last_write_old, r10_last_write_new,
                             mips_mnemonic(r10_last_write_instr), r10_last_source);

                    // Did $1 change AFTER the last $10 write but
                    // BEFORE the bad jalr? If yes, the setup window
                    // DID intend to set $1. If no, the caller skipped
                    // the convention.
                    if (r1_ring_count > 0 && bad_jalr_captured) begin
                        latest_idx_r1     = (r1_ring_count - 1 + R1_RING_DEPTH) % R1_RING_DEPTH;
                        r1_last_write_cyc = r1_ring_cyc[latest_idx_r1];
                        r1_last_write_pc  = r1_ring_pc[latest_idx_r1];
                        r1_last_write_instr = r1_ring_instr[latest_idx_r1];
                        r1_last_write_old = r1_ring_prev[latest_idx_r1];
                        r1_last_write_new = r1_ring_new[latest_idx_r1];
                        r1_changed_after_r10 = (r1_last_write_cyc > r10_last_write_cyc) &&
                                               (r1_last_write_cyc < bad_jalr_cycle);
                        $display("[ch183] r1_setup r1_last_write_cyc=%0d pc=0x%08h instr=0x%08h new=0x%08h r1_changed_in_setup=%s",
                                 r1_last_write_cyc, r1_last_write_pc,
                                 r1_last_write_instr, r1_last_write_new,
                                 r1_changed_after_r10 ? "yes" : "no");
                    end
                end else begin
                    $display("[ch183] r10_lineage no writes captured ($10 untouched since reset)");
                end
            end

            // ----------------------------------------------------------
            // Ch184 — patch-ring dump + ENTRY598 correlation table.
            // Tests hypothesis (β): BIOS callers patch PC=0x5B0 with
            // a decrement instruction (`addi $1, $1, -1` =
            // 0x2021FFFF) before calling and revert to NOP after.
            // If the table shows entries with huge $1 having
            // instr[5B0] != NOP at entry, β is confirmed.
            // ----------------------------------------------------------
            begin : ch184_autopsy
                int patch_ring_size;
                int max_entries;
                int stuck_entry_idx;
                logic any_patch_observed;
                patch_ring_size = (patch_ring_count < PATCH_RING_DEPTH)
                                    ? patch_ring_count : PATCH_RING_DEPTH;
                any_patch_observed = (patch_ring_count > 0);

                $display("[tb_ee_core_bios_smoke] Ch184 patch_ring count=%0d (writes into [0x598, 0x5C0), newest first):",
                         patch_ring_count);
                for (int i = 0; i < patch_ring_size; i++) begin
                    int slot;
                    slot = (patch_ring_count - 1 - i + PATCH_RING_DEPTH) % PATCH_RING_DEPTH;
                    $display("[tb_ee_core_bios_smoke] Ch184   [%0d] cyc=%0d addr=0x%08h data=0x%08h be=0x%01h mnemonic=%s",
                             i, patch_ring_cyc[slot], patch_ring_addr[slot],
                             patch_ring_data[slot], patch_ring_be[slot],
                             mips_mnemonic(patch_ring_data[slot]));
                end

                $display("[tb_ee_core_bios_smoke] Ch184 ENTRY598 correlation (entries 1..%0d; instr at entry-time, NOT timeout):",
                         (entry598_count < ENTRY_SNAP_DEPTH) ? entry598_count : ENTRY_SNAP_DEPTH);
                max_entries = (entry598_count < ENTRY_SNAP_DEPTH) ? entry598_count : ENTRY_SNAP_DEPTH;
                stuck_entry_idx = max_entries - 1;
                for (int i = 0; i < max_entries; i++) begin
                    string status_str;
                    string i5b0_mnem;
                    i5b0_mnem = mips_mnemonic(entry_snap_instr5b0[i]);
                    if (i == stuck_entry_idx)
                        status_str = "STUCK";
                    else
                        status_str = "returned";
                    $display("[tb_ee_core_bios_smoke] Ch184   entry#%0d cyc=%0d $1=0x%08h instr[5A8]=0x%08h instr[5AC]=0x%08h instr[5B0]=0x%08h(%s) instr[5B4]=0x%08h instr[5B8]=0x%08h status=%s",
                             i + 1, entry_snap_cyc[i], entry_snap_r1[i],
                             entry_snap_instr5a8[i], entry_snap_instr5ac[i],
                             entry_snap_instr5b0[i], i5b0_mnem,
                             entry_snap_instr5b4[i], entry_snap_instr5b8[i],
                             status_str);
                end

                // [ch184] verdict
                if (!any_patch_observed) begin
                    $display("[ch184] verdict=no_patches_observed (hypothesis (β) likely FALSE — no stores into [0x598, 0x5C0) the entire run. β predicted BIOS callers patch PC=0x5B0; reality: no callers patched. Different mechanism keeps small/huge-$1 loops bounded — back to (α) or (γ))");
                end else begin
                    // Find any entry where instr[5B0] != NOP at entry time.
                    int patched_count;
                    patched_count = 0;
                    for (int i = 0; i < max_entries; i++) begin
                        if (entry_snap_instr5b0[i] != 32'd0)
                            patched_count = patched_count + 1;
                    end
                    if (patched_count > 0) begin
                        $display("[ch184] verdict=patches_observed (hypothesis (β) supported — %0d/%0d entries had non-NOP instr at 0x5B0 at entry-time; patch_ring has %0d writes total)",
                                 patched_count, max_entries, patch_ring_count);
                    end else begin
                        $display("[ch184] verdict=patches_seen_but_not_at_entry (patches happened (count=%0d) but no entry caught a non-NOP at 0x5B0 — patches may target a different address in [0x598,0x5C0) or revert before entry. β weakened)",
                                 patch_ring_count);
                    end
                end
            end

            // ----------------------------------------------------------
            // Ch186 — RAM-dispatcher entry autopsy. Dumps the frozen
            // retire-ring snapshot, the writes-to-RAM-dispatcher
            // history, and a verdict on the control-transfer event.
            // ----------------------------------------------------------
            begin : ch186_autopsy
                int wr_size;
                int oldest_idx;
                int target_idx;
                wr_size = (ram_disp_wr_count < RAM_DISP_WR_DEPTH)
                            ? ram_disp_wr_count : RAM_DISP_WR_DEPTH;
                $display("[tb_ee_core_bios_smoke] Ch186 ram_disp writes count=%0d (writes into [0x30000, 0x30040), newest first):",
                         ram_disp_wr_count);
                for (int i = 0; i < wr_size; i++) begin
                    int slot;
                    slot = (ram_disp_wr_count - 1 - i + RAM_DISP_WR_DEPTH) % RAM_DISP_WR_DEPTH;
                    $display("[tb_ee_core_bios_smoke] Ch186   [%0d] cyc=%0d addr=0x%08h data=0x%08h pc=0x%08h mnemonic=%s",
                             i, ram_disp_wr_cyc[slot], ram_disp_wr_addr[slot],
                             ram_disp_wr_data[slot], ram_disp_wr_pc[slot],
                             mips_mnemonic(ram_disp_wr_data[slot]));
                end

                if (first_ram_disp_captured) begin
                    $display("[tb_ee_core_bios_smoke] Ch186 first RAM-dispatcher retire cyc=%0d pc=0x%08h instr=0x%08h",
                             first_ram_disp_cycle, first_ram_disp_pc,
                             first_ram_disp_instr);
                    $display("[tb_ee_core_bios_smoke] Ch186   regs at trigger: $1=0x%08h $2=0x%08h $3=0x%08h $4=0x%08h $8=0x%08h $10=0x%08h $29/sp=0x%08h $31/ra=0x%08h",
                             first_ram_disp_r1, first_ram_disp_r2,
                             first_ram_disp_r3, first_ram_disp_r4,
                             first_ram_disp_r8, first_ram_disp_r10,
                             first_ram_disp_r29, first_ram_disp_r31);
                    // Frozen retire-ring dump — oldest to newest. The
                    // ring is circular; ring_head_at_trigger is the
                    // NEXT write slot (so the most recent entry is at
                    // (head-1) mod DEPTH; the oldest is at (head)).
                    $display("[tb_ee_core_bios_smoke] Ch186 frozen retire-ring (oldest first, 32 entries; ring_head_at_trigger=%0d):",
                             first_ram_disp_ring_head_at_trigger);
                    for (int i = 0; i < 32; i++) begin
                        int slot;
                        slot = (first_ram_disp_ring_head_at_trigger + i) % 32;
                        $display("[tb_ee_core_bios_smoke] Ch186   slot[%0d] cyc=%0d pc=0x%08h instr=0x%08h  %s",
                                 i, first_ram_disp_ring_cyc[slot],
                                 first_ram_disp_ring_pc[slot],
                                 first_ram_disp_ring_instr[slot],
                                 mips_mnemonic(first_ram_disp_ring_instr[slot]));
                    end

                    // Heuristic verdict — look at the slot just before
                    // the trigger. If it's a `jr`/`jalr`/`j`/`jal`,
                    // the transfer was a legitimate indirect/direct
                    // jump. If it's an exception vector (PC=0x80...
                    // or 0xBFC00200/0xBFC00380), it was an exception.
                    target_idx = (first_ram_disp_ring_head_at_trigger + 31) % 32;
                    begin
                        logic [31:0] last_pc;
                        logic [31:0] last_instr;
                        logic [5:0]  last_op, last_func;
                        last_pc    = first_ram_disp_ring_pc   [target_idx];
                        last_instr = first_ram_disp_ring_instr[target_idx];
                        last_op    = last_instr[31:26];
                        last_func  = last_instr[5:0];
                        if (last_op == 6'h02)
                            $display("[ch186] verdict=j_direct (last retire was J 0x%08h at pc=0x%08h)",
                                     {last_pc[31:28], last_instr[25:0], 2'b00}, last_pc);
                        else if (last_op == 6'h03)
                            $display("[ch186] verdict=jal_direct (last retire was JAL 0x%08h at pc=0x%08h, link expected 0x%08h)",
                                     {last_pc[31:28], last_instr[25:0], 2'b00}, last_pc, last_pc + 32'd8);
                        else if (last_op == 6'h00 && last_func == 6'h08)
                            $display("[ch186] verdict=jr_indirect (last retire was JR at pc=0x%08h, target = rs at runtime)",
                                     last_pc);
                        else if (last_op == 6'h00 && last_func == 6'h09)
                            $display("[ch186] verdict=jalr_indirect (last retire was JALR at pc=0x%08h, target = rs at runtime)",
                                     last_pc);
                        else if (last_pc >= 32'hBFC0_0000 && last_pc < 32'hBFC4_0000 &&
                                 (first_ram_disp_pc == 32'h0000_0200 || first_ram_disp_pc == 32'h0000_0280))
                            $display("[ch186] verdict=exception_vector (last retire was in BIOS ROM and trigger landed at exception vector 0x%08h)",
                                     first_ram_disp_pc);
                        else
                            $display("[ch186] verdict=other (last retire pc=0x%08h instr=0x%08h mnemonic=%s — neither J/JAL/JR/JALR nor exception vector pattern)",
                                     last_pc, last_instr, mips_mnemonic(last_instr));
                    end
                end else begin
                    $display("[ch186] verdict=never_entered_ram_disp (no retire ever landed in [0x30000, 0x30040))");
                end
            end

            // ----------------------------------------------------------
            // Ch187 — low-RAM-writes ring + first-PC-0xA0 + bad-jalr
            // peek snapshot. Tests whether code is EVER installed at
            // PC=0xA0 after the cache-line clear, and what's there
            // at the moment the jalr fires.
            // ----------------------------------------------------------
            begin : ch187_autopsy
                int wr_size;
                wr_size = (low_ram_wr_count < LOW_RAM_WR_DEPTH)
                            ? low_ram_wr_count : LOW_RAM_WR_DEPTH;
                $display("[tb_ee_core_bios_smoke] Ch187 low_ram writes count=%0d (writes into [0x80, 0xC0), newest first):",
                         low_ram_wr_count);
                for (int i = 0; i < wr_size; i++) begin
                    int slot;
                    slot = (low_ram_wr_count - 1 - i + LOW_RAM_WR_DEPTH) % LOW_RAM_WR_DEPTH;
                    $display("[tb_ee_core_bios_smoke] Ch187   [%0d] cyc=%0d addr=0x%08h data=0x%08h pc=0x%08h mnemonic=%s",
                             i, low_ram_wr_cyc[slot], low_ram_wr_addr[slot],
                             low_ram_wr_data[slot], low_ram_wr_pc[slot],
                             mips_mnemonic(low_ram_wr_data[slot]));
                end

                if (first_pc_a0_captured) begin
                    $display("[tb_ee_core_bios_smoke] Ch187 first retire at PC=0x000000A0: cyc=%0d instr=0x%08h $31=0x%08h $10=0x%08h",
                             first_pc_a0_cycle, first_pc_a0_instr,
                             first_pc_a0_r31, first_pc_a0_r10);
                end else begin
                    $display("[tb_ee_core_bios_smoke] Ch187 PC=0x000000A0 NEVER retired (BIOS jalr $10=0xA0 landed somewhere but PC=0xA0 itself never retired; the EE may have taken an exception or walked off into zero RAM and never circled back)");
                end

                if (bad_jalr_captured) begin
                    $display("[tb_ee_core_bios_smoke] Ch187 bad-jalr peek snapshot (AT cyc=%0d, the moment jalr $10=0xA0 retired):",
                             bad_jalr_cycle);
                    $display("[tb_ee_core_bios_smoke] Ch187   [0x80]=0x%08h  %s",
                             badjalr_peek_080, mips_mnemonic(badjalr_peek_080));
                    $display("[tb_ee_core_bios_smoke] Ch187   [0xA0]=0x%08h  %s   <-- jalr target",
                             badjalr_peek_0a0, mips_mnemonic(badjalr_peek_0a0));
                    $display("[tb_ee_core_bios_smoke] Ch187   [0xC0]=0x%08h  %s",
                             badjalr_peek_0c0, mips_mnemonic(badjalr_peek_0c0));
                    $display("[tb_ee_core_bios_smoke] Ch187   [0x500]=0x%08h  %s",
                             badjalr_peek_500, mips_mnemonic(badjalr_peek_500));
                    $display("[tb_ee_core_bios_smoke] Ch187   [0x598]=0x%08h  %s",
                             badjalr_peek_598, mips_mnemonic(badjalr_peek_598));
                end

                // Verdict per Codex framing.
                if (low_ram_wr_count == 0) begin
                    $display("[ch187] verdict=never_installed (no writes into [0x80, 0xC0) the whole run — BIOS expected something at 0xA0 to come from a path we don't model: hardware event, BIOS code we're skipping, or boot mode we don't honor)");
                end else if (badjalr_peek_0a0 == 32'd0) begin
                    $display("[ch187] verdict=installed_then_cleared (%0d writes seen into [0x80, 0xC0) but at jalr-time [0xA0] reads zero — something cleared it between install and use)",
                             low_ram_wr_count);
                end else begin
                    $display("[ch187] verdict=alive_at_jalr (at jalr-time [0xA0] = 0x%08h = %s; the jalr lands on a real instruction)",
                             badjalr_peek_0a0, mips_mnemonic(badjalr_peek_0a0));
                end
            end

            // Ch181 — instruction window around the latest ENTRY598
            // caller ($31 - 4). For the "bad" entry, this is the
            // RAM-resident caller that didn't set $1 properly.
            // Peek BOTH ee_ram and useg_shadow_mem so we can tell
            // which backing has the code (if any).
            if (entry598_count > 0 && entry598_r31 >= 32'd4) begin
                logic [31:0] caller_pc;
                caller_pc = entry598_r31 - 32'd4;
                $display("[tb_ee_core_bios_smoke] Ch181 latest-caller window around 0x%08h ($31-4 of entry598#%0d):",
                         caller_pc, entry598_count);
                for (int i = -8; i <= 6; i++) begin
                    logic [31:0] a, w_peek, w_useg;
                    int unsigned ram_qw_idx;
                    logic [1:0]  ram_lane;
                    logic [31:0] w_ram;
                    a = caller_pc + (i * 4);
                    w_peek = peek_instr(a);
                    w_useg = peek_useg_shadow(a);
                    ram_qw_idx = a[24:4];
                    ram_lane   = a[3:2];
                    case (ram_lane)
                        2'd0: w_ram = u_ee_ram.mem[ram_qw_idx][31:0];
                        2'd1: w_ram = u_ee_ram.mem[ram_qw_idx][63:32];
                        2'd2: w_ram = u_ee_ram.mem[ram_qw_idx][95:64];
                        default: w_ram = u_ee_ram.mem[ram_qw_idx][127:96];
                    endcase
                    $display("[tb_ee_core_bios_smoke] Ch181   %s 0x%08h: useg=0x%08h ram=0x%08h  %s",
                             (i == 0) ? ">>" : "  ",
                             a, w_useg, w_ram,
                             mips_mnemonic(w_useg != 32'd0 ? w_useg : w_ram));
                end
            end
        end

        // Ch199 — also print the resume summary at timeout, since the
        // post-halt block (where the matching summary lives) is gated
        // on `while (!core_halt)` and never runs if the BIOS no longer
        // halts. consume_count > 0 means the syscall_8_a0_2 branch
        // fired and BIOS resumed past 0xBFC501D8; AdES delta tells us
        // whether the resume itself caused re-faulting.
        if (ch199_consume_count > 0) begin
            int adess_after_first_consume;
            adess_after_first_consume = exc_ring_count - ch199_excring_at_consume;
            $display("[ch199] SUMMARY_AT_TIMEOUT consume_count=%0d first_consume cyc=%0d at pc=0x%08h with $a0=%0d resumed to $k0=0x%08h",
                     ch199_consume_count,
                     ch199_first_cyc, ch199_first_pc,
                     ch199_first_a0, ch199_first_k0);
            $display("[ch199]   AdES counter: before-resume=%0d total-at-end=%0d delta-after-first-resume=%0d (0 = clean resume, BIOS did not re-fault back into the same AdES path)",
                     ch199_excring_at_consume, exc_ring_count,
                     adess_after_first_consume);
            $display("[ch199]   verdict=resumed_past_syscall (no halt within %0d ns watchdog window; BIOS retired past 0xBFC501D8 and kept running — Ch200+ can lengthen runway or land on next halt site)",
                     800_000_000);
        end

        // Ch200 — lineage-observer cap summary. Prints how many
        // R2_SEED / R3_CHG / R10_CHG / R31_CHG inline events were
        // shown (each capped at CH200_PRINT_CAP) and how many were
        // suppressed past the cap. Authoritative content of these
        // observers still flows through the rings, which the regular
        // newest-first dumps walk.
        $display("[ch200] observer_caps r2=%0d r3=%0d r10=%0d r31=%0d (cap=%0d) suppressed=%0d",
                 ch200_r2_prints, ch200_r3_prints,
                 ch200_r10_prints, ch200_r31_prints,
                 CH200_PRINT_CAP, ch200_suppressed);

        // ------------------------------------------------------------
        // Ch201 — post-RFE progress autopsy (timeout path). Same block
        // also fires on the happy halt path below for symmetry.
        // ------------------------------------------------------------
        if (ch199_consume_count > 0) begin : ch201_dump_timeout
            ch201_print_summary();
        end

        // Ch216 — fire from timeout path too so the periodicity
        // classification surfaces when BIOS doesn't halt.
        if (ch216_count > 0)
            ch216_print_classification();

        // Ch217 — longjmp-return path inspection (timeout path).
        if (ch217_count > 0)
            ch217_print_longjmp_path();

        // Ch218 — opt-in INTC-diagnostic print (timeout-path mirror).
        // See Ch260 closeout: routine runs leave this quiet.
`ifdef CH259_INTC_DIAG
        ch218_print_callee_reads();
`endif

`ifdef CH264_CALLEE_AUTOPSY
        ch264_print_autopsy();
`endif

`ifdef CH265_HELPER_AUTOPSY
        ch265_print_autopsy();
`endif

`ifdef CH266_DISPATCHER_AUTOPSY
        ch266_print_autopsy();
`endif

`ifdef CH267_GATE_OBSERVER
        ch267_print_observer();
`endif

`ifdef CH268_OUTER_READ_AUTOPSY
        ch268_print_autopsy();
`endif

`ifdef CH269_V0_LINEAGE
        ch269_print_observer();
`endif

        $error("[tb_ee_core_bios_smoke] timeout");
        $finish;
    end

    // ------------------------------------------------------------------
    // Ch201 — shared print helper. Walks the assoc-array histograms,
    // pulls the top 8 PCs and top 8 load addresses, prints the A0/B0/C0
    // function distribution, exception-by-code distribution, the
    // last-64 retired ring (newest first), and a final classifier
    // verdict. Called from both the timeout and post-halt paths above.
    // ------------------------------------------------------------------
    task automatic ch217_print_longjmp_path();
        int n;
        logic [31:0] jal_word;
        logic [31:0] jal_target;
        logic        all_a0_same;
        logic        all_a1_same;
        logic        all_v0_same;
        string       ch217_verdict;

        n = ch217_count;
        $display("[ch217] LONGJMP_PATH_DECODE 0xBFC52350..0xBFC52390:");
        for (logic [31:0] a = 32'hBFC5_2350; a < 32'hBFC5_2390; a += 4) begin
            logic [31:0] w;
            w = peek_instr(a);
            $display("[ch217]   0x%08h: 0x%08h  %s%s",
                     a, w, mips_mnemonic(w),
                     (a == 32'hBFC5_2358) ? "  <-- JAL (longjmp-return-handler)" :
                     (a == 32'hBFC5_2360) ? "  <-- post-call landing" : "");
        end

        // Decode the JAL target at 0xBFC52358. PC+4 = 0xBFC5235C with
        // top-4 bits = 0xB, so target = {4'hB, instr[25:0], 2'b00}.
        jal_word   = peek_instr(32'hBFC5_2358);
        jal_target = { 4'hB, jal_word[25:0], 2'b00 };
        $display("[ch217] JAL_TARGET decoded from instr=0x%08h: PC=0x%08h",
                 jal_word, jal_target);
        $display("[ch217] CALLED_FUNCTION dump at PC=0x%08h:", jal_target);
        for (logic [31:0] a = jal_target; a < jal_target + 32'h40; a += 4) begin
            $display("[ch217]   0x%08h: 0x%08h  %s",
                     a, peek_instr(a), mips_mnemonic(peek_instr(a)));
        end

        $display("[ch217] CALLER_PASSES total=%0d through PC=0xBFC52358:", n);
        for (int i = 0; i < n; i++) begin
            $display("[ch217]   [%0d] cyc=%0d $a0=0x%08h $a1=0x%08h $a2=0x%08h $a3=0x%08h $ra_pre=0x%08h $v0_pre=0x%08h $v0_post=0x%08h $v1_post=0x%08h",
                     i, ch217_cyc[i],
                     ch217_a0_in[i], ch217_a1_in[i],
                     ch217_a2_in[i], ch217_a3_in[i],
                     ch217_ra_in[i], ch217_v0_before[i],
                     ch217_v0_after[i], ch217_v1_after[i]);
        end

        // Compare across passes.
        all_a0_same = (n > 1);
        all_a1_same = (n > 1);
        all_v0_same = (n > 1);
        for (int i = 1; i < n; i++) begin
            if (ch217_a0_in[i] !== ch217_a0_in[0]) all_a0_same = 1'b0;
            if (ch217_a1_in[i] !== ch217_a1_in[0]) all_a1_same = 1'b0;
            if (ch217_v0_after[i] !== ch217_v0_after[0]) all_v0_same = 1'b0;
        end

        if (n < 2) begin
            ch217_verdict = "insufficient_passes (need >= 2 to classify state mutation)";
        end else if (all_a0_same && all_a1_same && all_v0_same) begin
            ch217_verdict = $sformatf("longjmp_return_repeats_due_to_static_state (across %0d passes, $a0/$a1 inputs and $v0 return value are IDENTICAL — the longjmp-return callee is being asked the same question and returning the same answer each cycle. BIOS has no escape signal from this callee. The callee at PC=0x%08h must be modifying something we're missing, OR the BIOS expects some external state (MMIO ready, timer, or kernel global) to flip between passes — but our model isn't providing it.)",
                                      n, jal_target);
        end else if (all_a0_same && !all_v0_same) begin
            ch217_verdict = $sformatf("callee_missing_side_effect (across %0d passes, inputs $a0/$a1 identical but $v0 differs — callee returns different values, BIOS may be making real progress but never escapes the syscall #8 path. Inspect what state the callee mutates.)",
                                      n);
        end else begin
            ch217_verdict = $sformatf("inputs_vary_across_passes ($a0_consistent=%0d $a1_consistent=%0d $v0_consistent=%0d — caller is feeding different inputs each pass)",
                                      all_a0_same, all_a1_same, all_v0_same);
        end
        $display("[ch217] verdict=%s", ch217_verdict);
    endtask

`ifdef CH259_INTC_DIAG
    task automatic ch218_print_callee_reads();
        // Ch259 reshape — narrow INTC transaction log.
        //
        // The Ch218 observer is now scoped to ONLY the IOP INTC pair
        // (0x1F801070 I_STAT, 0x1F801074 I_MASK). Output is an
        // ordered R/W transaction stream with pass index, PC, instr
        // mnemonic, R/W direction, EA, and data (write data for
        // writes, returned read data sampled from the named state
        // regs for reads). Per Codex's Ch259 framing, the broad
        // fishing-net observer has done its job; this version is the
        // compact log for verifying named-INTC behaviour and the
        // optional sticky-source experiment.
        int n;
        int reads_total;
        int writes_total;
        int stat_reads, stat_writes;
        int mask_reads, mask_writes;

        n = ch218_count;
        $display("[ch218] CH259_INTC_TRANSACTIONS captured=%0d (cap=%0d) — ordered R/W log on IOP INTC pair 0x1F801070 (I_STAT) / 0x1F801074 (I_MASK)",
                 n, CH218_DEPTH);

        if (n == 0) begin
            $display("[ch218] verdict=no_intc_traffic (synthetic mode, BIOS never touched INTC, or capture armed too late)");
        end else begin
        reads_total  = 0;
        writes_total = 0;
        stat_reads   = 0;
        stat_writes  = 0;
        mask_reads   = 0;
        mask_writes  = 0;
        for (int i = 0; i < n; i++) begin
            $display("[ch218]   [%0d] pass=%0d %s pc=0x%08h instr=0x%08h ea=0x%08h data=0x%08h  %s",
                     i, ch218_pass_idx[i],
                     ch218_is_wr_a[i] ? "WR" : "RD",
                     ch218_pc[i], ch218_instr[i],
                     ch218_ea[i], ch218_data[i],
                     mips_mnemonic(ch218_instr[i]));
            if (ch218_is_wr_a[i]) writes_total = writes_total + 1;
            else                  reads_total  = reads_total  + 1;
            if (ch218_ea[i] == 32'h1F80_1070) begin
                if (ch218_is_wr_a[i]) stat_writes = stat_writes + 1;
                else                  stat_reads  = stat_reads  + 1;
            end else if (ch218_ea[i] == 32'h1F80_1074) begin
                if (ch218_is_wr_a[i]) mask_writes = mask_writes + 1;
                else                  mask_reads  = mask_reads  + 1;
            end
        end
        $display("[ch218]   summary: reads=%0d writes=%0d  I_STAT(R=%0d W=%0d)  I_MASK(R=%0d W=%0d)  injected_src=0x%04h",
                 reads_total, writes_total,
                 stat_reads, stat_writes,
                 mask_reads, mask_writes,
                 u_misc_mmio.iop_intc_inject_src_i);
        // Render a final compact verdict line. With no synthetic
        // source asserted, healthy behavior is: BIOS reads I_STAT
        // (gets 0), reads I_MASK (gets whatever it last wrote),
        // writes I_MASK to set up, never sees a pending bit, so
        // the loop persists. Verdict labels:
        //   no_intc_traffic             — captured nothing (above)
        //   intc_quiet                  — captured but every read
        //                                 returns 0 and writes don't
        //                                 leave a sticky bit. Means
        //                                 INTC is "well-behaved but
        //                                 silent" — the treadmill is
        //                                 not breaking on INTC alone.
        //   intc_pending_observed       — at least one I_STAT read
        //                                 returned non-zero (= the
        //                                 sticky synthetic source
        //                                 took effect, or BIOS
        //                                 latched something).
        begin : verdict_block
            logic any_nonzero_stat;
            any_nonzero_stat = 1'b0;
            for (int i = 0; i < n; i++) begin
                if (!ch218_is_wr_a[i]
                        && ch218_ea[i] == 32'h1F80_1070
                        && ch218_data[i] != 32'd0) begin
                    any_nonzero_stat = 1'b1;
                end
            end
            if (any_nonzero_stat)
                $display("[ch218] verdict=intc_pending_observed (I_STAT returned non-zero at least once; either sticky synthetic source or BIOS latched a bit — check log for the offending pass)");
            else if (u_misc_mmio.iop_intc_inject_src_i != 16'd0)
                $display("[ch218] verdict=intc_inject_did_not_propagate (IOP_INTC_BOOT_SRC=0x%04h but no I_STAT read returned non-zero — wiring bug or filter mismatch)",
                         u_misc_mmio.iop_intc_inject_src_i);
            else
                $display("[ch218] verdict=intc_quiet (no synthetic source, no pending bit ever observed — proper W1C/mask semantics alone did NOT break the Ch215 treadmill, suggesting BIOS needs an actual asserted source)");
        end
        end  // close `if (n == 0) begin ... end else begin ... end`
    endtask
`endif // CH259_INTC_DIAG

    task automatic ch216_print_classification();
        int n;
        logic all_pcs_match;
        logic all_sps_match;
        logic all_ras_match;
        int   ra_progress_count;
        int   sp_progress_count;
        string ch216_verdict;
        n = ch216_count;
        $display("[ch216] RESTORE_PASSES total=%0d:", n);
        for (int i = 0; i < n; i++) begin
            $display("[ch216]   [%0d] cyc=%0d $ra=0x%08h $sp=0x%08h $fp=0x%08h $gp=0x%08h $v0=0x%08h post_pcs={0x%08h, 0x%08h, 0x%08h, 0x%08h}",
                     i, ch216_cyc[i],
                     ch216_ra[i], ch216_sp[i],
                     ch216_fp[i], ch216_gp[i], ch216_v0[i],
                     ch216_pc0[i], ch216_pc1[i],
                     ch216_pc2[i], ch216_pc3[i]);
        end
        all_pcs_match = (n > 1);
        all_sps_match = (n > 1);
        all_ras_match = (n > 1);
        ra_progress_count = 0;
        sp_progress_count = 0;
        for (int i = 1; i < n; i++) begin
            if (ch216_pc0[i] !== ch216_pc0[0]
                    || ch216_pc1[i] !== ch216_pc1[0]
                    || ch216_pc2[i] !== ch216_pc2[0]
                    || ch216_pc3[i] !== ch216_pc3[0])
                all_pcs_match = 1'b0;
            if (ch216_sp[i] !== ch216_sp[0]) all_sps_match = 1'b0;
            if (ch216_ra[i] !== ch216_ra[0]) all_ras_match = 1'b0;
            if (ch216_ra[i] !== ch216_ra[i-1]) ra_progress_count = ra_progress_count + 1;
            if (ch216_sp[i] !== ch216_sp[i-1]) sp_progress_count = sp_progress_count + 1;
        end
        $display("[ch216] KERNEL_HANDLER 0x00030000..0x00030040:");
        for (logic [31:0] a = 32'h0003_0000; a < 32'h0003_0040; a += 4) begin
            logic [31:0] w;
            w = peek_instr(a);
            $display("[ch216]   0x%08h: 0x%08h  %s%s",
                     a, w, mips_mnemonic(w),
                     (a == 32'h0003_0020) ? "  <-- TIMEOUT PC" : "");
        end
        if (n < 2) begin
            ch216_verdict = "insufficient_restores (need >= 2 to classify periodicity)";
        end else if (all_pcs_match && all_sps_match && all_ras_match) begin
            ch216_verdict = $sformatf("periodic_kernel_loop_no_progress (all %0d restores share identical $ra/$sp/$fp/$gp and identical first-4 post-PCs — pure treadmill; BIOS is stuck in a syscall #8 / RFE / kernel-handler cycle without state advance. Next missing piece: something must increment between passes for BIOS to escape — possibly an MMIO ready bit, a timer counter, or a flag the kernel handler reads each pass.)",
                                      n);
        end else if (all_pcs_match) begin
            ch216_verdict = $sformatf("periodic_kernel_loop_progressing (%0d restores share identical post-PCs but $ra/$sp/$fp/$gp differ across passes — BIOS is iterating through the same code path with evolving register state; ra_progress=%0d sp_progress=%0d. Real progress.)",
                                      n, ra_progress_count, sp_progress_count);
        end else begin
            ch216_verdict = $sformatf("varying_paths (%0d restores have differing post-PCs — BIOS is taking different code paths each pass, not a loop)",
                                      n);
        end
        $display("[ch216] verdict=%s", ch216_verdict);
    endtask

    task automatic ch201_print_summary();
        // Sort the 16-slot hot-PC table by count descending using a
        // local copy so we can present top-N without disturbing state.
        int          top_pc_count [0:CH201_HOTPC_SLOTS-1];
        logic [31:0] top_pc_addr  [0:CH201_HOTPC_SLOTS-1];
        int          tmp_count;
        logic [31:0] tmp_addr;
        string       verdict_str;
        int          dominant_pc_count;
        logic [31:0] dominant_pc_addr;
        int          mmio_pct;
        int          load_pct;
        int          dispatch_total;
        int          excode_nonzero_count;

        for (int i = 0; i < CH201_HOTPC_SLOTS; i++) begin
            top_pc_count[i] = ch201_hot_pc_count[i];
            top_pc_addr [i] = ch201_hot_pc_addr [i];
        end
        // Bubble-sort descending — N=16 is tiny.
        for (int i = 0; i < CH201_HOTPC_SLOTS; i++) begin
            for (int j = i + 1; j < CH201_HOTPC_SLOTS; j++) begin
                if (top_pc_count[j] > top_pc_count[i]) begin
                    tmp_count = top_pc_count[i];
                    tmp_addr  = top_pc_addr [i];
                    top_pc_count[i] = top_pc_count[j];
                    top_pc_addr [i] = top_pc_addr [j];
                    top_pc_count[j] = tmp_count;
                    top_pc_addr [j] = tmp_addr;
                end
            end
        end

        $display("[ch201] POST_RFE_PROGRESS retires=%0d loads=%0d mmio_reads=%0d exceptions=%0d max_streak_len=%0d max_streak_pc=0x%08h",
                 ch201_post_rfe_retires, ch201_post_rfe_loads,
                 ch201_post_rfe_mmio_reads, ch201_post_rfe_exceptions,
                 ch201_max_streak_len, ch201_max_streak_pc);
        $display("[ch201] dispatch_entries A0=%0d B0=%0d C0=%0d",
                 ch201_post_rfe_a0_calls,
                 ch201_post_rfe_b0_calls,
                 ch201_post_rfe_c0_calls);

        $display("[ch201] hot-PC table (16 slots, sorted by count, post-RFE):");
        for (int i = 0; i < CH201_HOTPC_SLOTS; i++) begin
            if (top_pc_count[i] > 0)
                $display("[ch201]   [%0d] pc=0x%08h count=%0d (%0d%% of retires)",
                         i, top_pc_addr[i], top_pc_count[i],
                         (ch201_post_rfe_retires > 0)
                          ? (top_pc_count[i] * 100 / ch201_post_rfe_retires)
                          : 0);
        end

        dispatch_total = ch201_post_rfe_a0_calls
                       + ch201_post_rfe_b0_calls
                       + ch201_post_rfe_c0_calls;
        if (dispatch_total > 0) begin
            $display("[ch201] A0/B0/C0 syscall function distribution (nonzero):");
            for (int f = 0; f < 256; f++) begin
                if (ch201_a0_funcs[f] > 0)
                    $display("[ch201]   A0[0x%02h] count=%0d", f[7:0], ch201_a0_funcs[f]);
            end
            for (int f = 0; f < 256; f++) begin
                if (ch201_b0_funcs[f] > 0)
                    $display("[ch201]   B0[0x%02h] count=%0d", f[7:0], ch201_b0_funcs[f]);
            end
            for (int f = 0; f < 256; f++) begin
                if (ch201_c0_funcs[f] > 0)
                    $display("[ch201]   C0[0x%02h] count=%0d", f[7:0], ch201_c0_funcs[f]);
            end
        end

        excode_nonzero_count = 0;
        if (ch201_post_rfe_exceptions > 0) begin
            $display("[ch201] exception-code distribution (post-RFE):");
            for (int e = 0; e < 32; e++) begin
                if (ch201_excode_hist[e] > 0) begin
                    excode_nonzero_count = excode_nonzero_count + 1;
                    $display("[ch201]   excode=0x%02h count=%0d", e[4:0], ch201_excode_hist[e]);
                end
            end
        end

        $display("[ch201] last-%0d retired PCs (newest first):", ch201_last_count);
        for (int i = 0; i < ch201_last_count; i++) begin
            int slot;
            slot = (ch201_last_head - 1 - i + CH201_LAST_DEPTH) % CH201_LAST_DEPTH;
            $display("[ch201]   [%0d] pc=0x%08h instr=0x%08h  %s",
                     i, ch201_last_pc[slot], ch201_last_instr[slot],
                     mips_mnemonic(ch201_last_instr[slot]));
        end

        // ----- Verdict classifier -----
        // Compute unique-PC count in the last-64 ring — this is the
        // most reliable tight-loop signature for MIPS code (where
        // consecutive same-PC retires never happen because PC always
        // advances or branches). A small unique count = small loop body.
        begin : last_ring_unique
            logic [31:0] uniq_pcs [0:CH201_LAST_DEPTH-1];
            int uniq_count;
            uniq_count = 0;
            for (int i = 0; i < ch201_last_count; i++) begin
                int slot;
                bit seen;
                slot = (ch201_last_head - 1 - i + CH201_LAST_DEPTH) % CH201_LAST_DEPTH;
                seen = 1'b0;
                for (int j = 0; j < uniq_count; j++)
                    if (uniq_pcs[j] == ch201_last_pc[slot]) seen = 1'b1;
                if (!seen) begin
                    uniq_pcs[uniq_count] = ch201_last_pc[slot];
                    uniq_count = uniq_count + 1;
                end
            end
            $display("[ch201] last-64 unique PCs: %0d (small number = tight loop body)", uniq_count);

            dominant_pc_count = top_pc_count[0];
            dominant_pc_addr  = top_pc_addr [0];
            mmio_pct = (ch201_post_rfe_loads > 0)
                        ? (ch201_post_rfe_mmio_reads * 100 / ch201_post_rfe_loads)
                        : 0;
            load_pct = (ch201_post_rfe_retires > 0 && dominant_pc_count > 0)
                        ? (dominant_pc_count * 100 / ch201_post_rfe_retires)
                        : 0;

            if (ch201_post_rfe_exceptions > 5) begin
                verdict_str = $sformatf("post_rfe_exception_loop (%0d post-RFE exceptions across %0d distinct codes)",
                                        ch201_post_rfe_exceptions, excode_nonzero_count);
            end else if (uniq_count <= 16 && ch201_post_rfe_retires > 100000
                         && mmio_pct > 50 && ch201_post_rfe_mmio_reads > 1000) begin
                verdict_str = $sformatf("post_rfe_mmio_polling (last-64 ring has only %0d unique PCs = tight loop body; %0d MMIO reads = %0d%% of all loads)",
                                        uniq_count, ch201_post_rfe_mmio_reads, mmio_pct);
            end else if (uniq_count <= 16 && ch201_post_rfe_retires > 100000) begin
                verdict_str = $sformatf("post_rfe_idle_poll (last-64 ring has only %0d unique PCs = tight loop body; loads are RAM-side, MMIO=%0d%%)",
                                        uniq_count, mmio_pct);
            end else if (load_pct > 25 && mmio_pct > 50) begin
                verdict_str = $sformatf("post_rfe_mmio_polling (dominant_pc=0x%08h covers %0d%% of retires; %0d%% of loads hit MMIO)",
                                        dominant_pc_addr, load_pct, mmio_pct);
            end else if (ch201_post_rfe_retires > 100000) begin
                verdict_str = $sformatf("post_rfe_progress_no_terminal (BIOS retired %0d insns across %0d unique PCs in last-64; no tight-loop signature)",
                                        ch201_post_rfe_retires, uniq_count);
            end else begin
                verdict_str = $sformatf("post_rfe_unclassified (retires=%0d unique_in_last64=%0d dominant_pc_pct=%0d mmio_pct=%0d)",
                                        ch201_post_rfe_retires, uniq_count,
                                        load_pct, mmio_pct);
            end
            $display("[ch201] verdict=%s", verdict_str);
        end

        // Ch202 — mmio ready-stub effectiveness. The named verdict
        // line always prints; the captured-loop contract prints only
        // if the BIOS actually reached the polling loop (it may not
        // anymore now that the early-init MMIO reads return ready).
        $display("[ch202] mmio_ready_stub addr=0x1f801814 ready_value=0xffffffff mask=0x10000000 (BIOS-observed, captured first run; bit 28). loop_seen_this_run=%0d",
                 ch202_loop_seen);
        if (ch202_loop_seen) begin
            $display("[ch202] loop_contract_run captured: cyc=%0d $4(mask)=0x%08h $3(timeout)=0x%08h $6(ea)=0x%08h",
                     ch202_first_cyc, ch202_first_mask,
                     ch202_first_timeout, ch202_first_mmio_ea);
        end else begin
            $display("[ch202] loop_avoided: BIOS did not reach PC=0xbfc4fb28 this run — the early-path MMIO ready return changed the BIOS branch decision before the polling loop was entered. This is the wider effect of the stub: any code that reads 0x1f801814 sees the ready value, not just the specific loop we autopsied.");
        end
    endtask

    // ------------------------------------------------------------------
    // Chapter 40 — minimal diagnostic preload of a `JR $31; NOP` stub
    // at useg 0xC0, inside the in-map useg_shadow backing. Opt-in via
    // `+CH40_STUB` plusarg so the baseline real-BIOS run still
    // exhibits the un-patched install-gap failure (ch34-39 trap at
    // pc=0x200) and reruns can compare against it.
    //
    // Ch39 confirmed the BIOS clears [0xC0..0x1FF] very early (done
    // by cyc ~2300) and then never re-writes the region — so a
    // pre-boot preload would be overwritten by the clear loop.
    // Instead, we wait until r31_to_target_captured fires (cyc
    // 287080 in the current run — the last JAL in the call chain
    // that leads to the ch38 trampoline at 0xBFC58CC0), which is
    // after all clears and ~10 cycles before the JR $10 at
    // pc=0xBFC58CC4 executes. Writing the stub bytes into
    // useg_shadow_mem via hierarchical reference at that moment
    // makes the core read valid handler bytes when it fetches
    // pc=0x000000C0.
    //
    // Stub:
    //   useg 0xC0: 0x03E00008  = JR $31
    //   useg 0xC4: 0x00000000  = NOP (delay slot)
    // $31 is 0xBFC5220C when the JR $10 fires (ch37), so the stub
    // returns to the BIOS ROM caller cleanly.
    //
    // Usage:
    //   make tb_ee_core_bios_smoke BIOS=/tmp/ps2_bios.hex
    //       -> baseline: traps at pc=0x200 (install-gap failure)
    //   make tb_ee_core_bios_smoke BIOS=/tmp/ps2_bios.hex CH40_STUB=1
    //       -> stub installed: BIOS progresses past the trampoline
    //          (CH40_STUB=1 causes sim/Makefile to forward +CH40_STUB
    //          into the vvp invocation; `make -C sim` only — not a
    //          raw vvp env variable).
    // ------------------------------------------------------------------
    logic ch40_stub_enabled;
    logic ch40_stub_installed;
    initial begin
        ch40_stub_enabled   = $test$plusargs("CH40_STUB");
        ch40_stub_installed = 1'b0;
        if (ch40_stub_enabled) begin
            $display("[tb_ee_core_bios_smoke] CH40_STUB_REQUESTED (waiting for r31_to_target edge)");
            wait (using_real_bios && r31_to_target_captured);
            @(posedge clk);
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000030] = 32'h03E00008; // JR $31 at 0xC0
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000031] = 32'h00000000; // NOP     at 0xC4
            ch40_stub_installed = 1'b1;
            $display("[tb_ee_core_bios_smoke] CH40_STUB_INSTALLED cyc=%0d: useg_shadow[0xC0]=0x03E00008 (JR $31), useg_shadow[0xC4]=0x00000000 (NOP)",
                     sim_cycle_ctr);
        end
    end

    // ------------------------------------------------------------------
    // Chapter 52 — diagnostic preload of a minimal AdES/AdEL handler
    // at useg 0x80..0x8C. Codex-sanctioned two-step plan: prove the
    // "no RAM exception handler installed" diagnosis from ch51 is
    // the next actual blocker BEFORE investing in a full SIF/IOP
    // boot model.
    //
    // Handler (4 instructions) runs after BEV=0 when AdES fires and
    // pc vectors to useg 0x80:
    //   0x80: mfc0  $26, $14      ; $k0 = EPC  (faulting pc)
    //   0x84: addiu $26, $26, 4   ; skip the faulting instruction
    //   0x88: jr    $26            ; return to EPC+4
    //   0x8C: rfe                  ; delay slot — restore IE/KU stack
    //
    // Install timing mirrors CH40_STUB: wait on r31_to_target_captured
    // which fires at cyc ~287K — AFTER all four zero-fill waves
    // captured in ch51 (last at cyc=286998) but BEFORE the first
    // AdES fault would enter the handler region.
    //
    // Opt-in via `+CH52_EXC_STUB` plusarg (forwarded by the Makefile
    // from `CH52_EXC_STUB=1` on the make command line). Default off
    // so pre-ch52 runs stay backward-compatible.
    //
    // Encodings:
    //   MFC0 $26, $14   -> OP_COP0=0x10, rs=MF=0,  rt=26, rd=14  = 0x401A7000
    //   ADDIU $26,$26,4 -> OP_ADDIU=0x09, rs=26,   rt=26, imm=4  = 0x275A0004
    //   JR $26          -> SPECIAL=0x00, rs=26,    func=JR=0x08  = 0x03400008
    //   RFE             -> OP_COP0=0x10, rs=CO=0x10, func=0x10   = 0x42000010
    // ------------------------------------------------------------------
    logic ch52_stub_enabled;
    logic ch52_stub_installed;
    initial begin
        ch52_stub_enabled   = $test$plusargs("CH52_EXC_STUB");
        ch52_stub_installed = 1'b0;
        if (ch52_stub_enabled) begin
            $display("[tb_ee_core_bios_smoke] CH52_STUB_REQUESTED (waiting for r31_to_target edge, then installing AdES handler at useg 0x80..0x8C)");
            wait (using_real_bios && r31_to_target_captured);
            @(posedge clk);
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000020] = 32'h401A7000; // MFC0  $26, $14
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000021] = 32'h275A0004; // ADDIU $26, $26, 4
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000022] = 32'h03400008; // JR    $26
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000023] = 32'h42000010; // RFE  (delay slot)
            ch52_stub_installed = 1'b1;
            $display("[tb_ee_core_bios_smoke] CH52_STUB_INSTALLED cyc=%0d: useg_shadow[0x80..0x8C]={MFC0 $k0,EPC; ADDIU $k0,$k0,4; JR $k0; RFE}",
                     sim_cycle_ctr);
        end
    end

    // ------------------------------------------------------------------
    // Chapter 53 — intermediate-richness exception handler blob.
    // Codex's two-step plan, step "1.5": before committing to a full
    // SIF/IOP install model, test whether a slightly richer handler
    // gets the BIOS *meaningfully* farther than ch52's minimal
    // 4-word stub. If yes → the BIOS cares about install bytes
    // beyond mere presence, SIF install is worth the effort. If
    // no → the BIOS needs specific handler semantics (emulation of
    // the faulting SW), and we should invest in that instead.
    //
    // Handler layout (10 instructions at useg 0x80..0xA4):
    //   0x80: mfc0 $26, $14          ; $k0 = EPC (faulting pc)
    //   0x84: sw   $26, 0xA8($0)     ; save EPC at useg 0xA8
    //   0x88: mfc0 $26, $8           ; $k0 = BadVAddr
    //   0x8C: sw   $26, 0xAC($0)     ; save BadVAddr at useg 0xAC
    //   0x90: mfc0 $26, $13          ; $k0 = Cause
    //   0x94: sw   $26, 0xB0($0)     ; save Cause at useg 0xB0
    //   0x98: mfc0 $26, $14          ; $k0 = EPC (again)
    //   0x9C: addiu $26, $26, 4      ; skip faulting instruction
    //   0xA0: jr   $26                ; return to EPC+4
    //   0xA4: rfe                    ; delay slot — restore Status
    //
    // Save slots at 0xA8..0xB0 are inside the exception-handler
    // window [0x80..0xBF] but outside the handler code area; the
    // BIOS wouldn't normally touch these. The per-exception writes
    // are diagnostic — tb-side observers could later read them.
    //
    // Opt-in via `+CH53_EXC_STUB` plusarg (from `CH53_EXC_STUB=1`).
    // If both CH52 and CH53 are set, last-writer-wins (both
    // initials fire on the same r31_to_target edge). Default is
    // that neither is set, matching pre-ch52 behavior.
    //
    // Encodings (double-checked MIPS R4000 format):
    //   MFC0 $26, $14 = 0x401A7000
    //   MFC0 $26, $8  = 0x401A4000
    //   MFC0 $26, $13 = 0x401A6800
    //   SW   $26, 0xA8($0) = 0xAC1A00A8
    //   SW   $26, 0xAC($0) = 0xAC1A00AC
    //   SW   $26, 0xB0($0) = 0xAC1A00B0
    //   ADDIU $26, $26, 4  = 0x275A0004
    //   JR   $26          = 0x03400008
    //   RFE                = 0x42000010
    // ------------------------------------------------------------------
    // ------------------------------------------------------------------
    // Chapter 54 — atomic coordinated install of useg 0x80..0x1FF.
    // Codex-directed step 2 (before going full SIF/IOP): stamp the
    // entire shared low-RAM surface in ONE phase with a coherent
    // image, not piecemeal slot-by-slot. Tests whether the real
    // blocker is "coordinated install exists" or "handler semantics
    // need to be producer-correct". Opt-in via `CH54_BLOB=1`
    // Makefile var → `+CH54_BLOB` plusarg.
    //
    // Image layout (useg_shadow word indices 0x20..0x7F, covering
    // bytes 0x80..0x1FC = 96 words = 0x180 bytes):
    //
    //   0x80..0x8C  (4 words)  AdES exception handler, same body
    //                           as CH52_EXC_STUB:
    //                             MFC0 $26, $14
    //                             ADDIU $26, $26, 4
    //                             JR    $26
    //                             RFE          (delay slot)
    //   0x90..0x1FC  (92 words) Alternating pairs:
    //                             (even-offset word) JR $31
    //                             (odd-offset word)  NOP
    //                           Any BIOS JR-to-any-slot in this
    //                           range either lands on JR $31
    //                           (returns immediately) or on a NOP
    //                           that walks forward one word to
    //                           the next JR $31. Covers the ch37
    //                           dispatch targets 0xB0 and 0xC0
    //                           uniformly, so the BIOS sees a
    //                           consistent "trampoline returns
    //                           cleanly" contract across the
    //                           whole handler surface.
    //
    // Install is atomic (all 96 words stamped in a single block)
    // and timed on `r31_to_target_captured` (cyc~287080), AFTER
    // ch51's wave-4 zero-fill at cyc=286998 but BEFORE the first
    // AdES fault. Same timing strategy as CH40 / CH52 / CH53.
    //
    // If CH40 / CH52 / CH53 are ALSO set simultaneously, CH54's
    // image wins for the overlapping slots (both 0x80..0x8C and
    // 0xC0 get CH54's content); for the non-overlapping slots the
    // result is whichever stub touched them last. The expected
    // usage is `CH54_BLOB=1` alone (without CH40/CH52/CH53).
    //
    // Success criteria per Codex's Ch54 spec:
    //   - No more 0xB0 / 0xC0 / 0x200 data-as-code falls from the
    //     uninitialized runway.
    //   - BIOS gets meaningfully past the Ch52 (retired=368,475)
    //     and Ch53 (retired=85,616) endpoints.
    //   - If it still fails, the new trap is in genuinely new
    //     territory, not the same install-gap class.
    // ------------------------------------------------------------------
    logic ch54_blob_enabled;
    logic ch54_blob_installed;
    initial begin
        ch54_blob_enabled   = $test$plusargs("CH54_BLOB");
        ch54_blob_installed = 1'b0;
        if (ch54_blob_enabled) begin
            $display("[tb_ee_core_bios_smoke] CH54_BLOB_REQUESTED (waiting for r31_to_target edge, then atomic stamp of useg 0x80..0x1FF)");
            wait (using_real_bios && r31_to_target_captured);
            @(posedge clk);
            // Exception handler at 0x80..0x8C (4 words)
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000020] = 32'h401A7000; // MFC0  $26, $14
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000021] = 32'h275A0004; // ADDIU $26, $26, 4
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000022] = 32'h03400008; // JR    $26
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000023] = 32'h42000010; // RFE   (delay slot)
            // Filler: JR $31 / NOP pairs, 0x90..0x1FC (92 words)
            for (int i = 'h24; i < 'h80; i = i + 2) begin
                u_ee_map.g_useg_shadow.useg_shadow_mem[i]   = 32'h03E00008; // JR $31
                u_ee_map.g_useg_shadow.useg_shadow_mem[i+1] = 32'h00000000; // NOP
            end
            ch54_blob_installed = 1'b1;
            $display("[tb_ee_core_bios_smoke] CH54_BLOB_INSTALLED cyc=%0d: useg 0x80..0x8C = AdES handler, 0x90..0x1FC = (JR $31, NOP) pairs (46 x 8-byte entries)",
                     sim_cycle_ctr);
        end
    end

    // ------------------------------------------------------------------
    // Ch55 — external install via boot_install_agent → SIF EE-RAM
    // bridge → ee_memory_map (bridge_wr_* → useg_shadow shadowing).
    //
    // Install timing: same phase as CH54_BLOB — wait on r31_to_target
    // before pulsing the agent's go_i. Pre-core_go installs get wiped
    // by the BIOS's own startup zero-clearing of [0x80..0xBC] (BIOS
    // writes zeros to the handler region at cyc ~1000, ~2400, ~27K
    // before it faults at ~287K). The architecturally honest window
    // is "after the BIOS finishes its own init, just before it first
    // uses the region" — exactly when Ch54's TB-poke landed.
    //
    // No TB direct poke into useg_shadow_mem for the installed region.
    // The 96-word payload is streamed through the sif_dma_ee_ram_bridge
    // (MASTER_ID=5) to phys 0x80, and the new bridge→useg_shadow
    // shadowing in ee_memory_map_stub mirrors each qword into the
    // useg_shadow backing the CPU reads useg exception entries from.
    // ------------------------------------------------------------------
    logic ch55_install_enabled;
    logic ch55_install_kicked;
    initial begin
        ch55_install_enabled = $test$plusargs("CH55_INSTALL");
        ch55_install_kicked  = 1'b0;
        if (ch55_install_enabled) begin
            $display("[tb_ee_core_bios_smoke] CH55_INSTALL_REQUESTED (waiting for r31_to_target edge, then install-agent streams Ch54 image through SIF bridge)");
            wait (using_real_bios && r31_to_target_captured);
            @(negedge clk);
            install_go          = 1'b1;
            ch55_install_kicked = 1'b1;
            @(negedge clk);
            install_go = 1'b0;
            wait (install_agent_done);
            repeat (8) @(posedge clk);
            $display("[tb_ee_core_bios_smoke] CH55_INSTALL_COMPLETE cyc=%0d: agent_done=%0b bridge_last_seen=%0b (useg 0x80..0x1FF installed via SIF bridge + useg_shadow shadowing)",
                     sim_cycle_ctr, install_agent_done, install_br_last_seen);
        end
    end

    // ------------------------------------------------------------------
    // Ch56 — runtime image override for the install-agent payload.
    //
    // Transport is unchanged: the override runs $readmemh on
    // u_install_agent.payload after the agent's own built-in-image
    // init, before the agent is pulsed (CH55 waits on the
    // r31_to_target edge, well after rst_n asserts). The agent is
    // still instantiated in built-in mode (USE_IMAGE_FILE=0) — the
    // override simply rewrites payload[] bytes before the streaming
    // starts, so neither handshake, timing, nor trace schema change.
    // Opt-in via `+CH56_IMAGE=<abs path>` (Makefile CH56_IMAGE=...).
    //
    // Dependency on CH55_INSTALL is now enforced in code (Ch56 audit
    // low-1): without CH55_INSTALL the agent is never pulsed and the
    // override has no effect, so we warn and skip rather than silently
    // load bytes that will never be streamed.
    //
    // Payload is X-filled before $readmemh for byte-for-byte parity
    // with pure external-mode (Ch56 audit low-2): in that mode the
    // agent skips its built-in Ch54 init entirely when USE_IMAGE_FILE=1,
    // so the array starts as declared (X). A short override file
    // here will now produce the same X tails as it would in pure
    // external mode, so "short file" experiments match across the
    // two entry points.
    // ------------------------------------------------------------------
    string ch56_image_path;
    logic  ch56_image_enabled;
    initial begin
        ch56_image_enabled = $value$plusargs("CH56_IMAGE=%s", ch56_image_path);
        if (ch56_image_enabled) begin
            if (!$test$plusargs("CH55_INSTALL")) begin
                $display("[tb_ee_core_bios_smoke] CH56_IMAGE_IGNORED: +CH56_IMAGE=%s supplied without +CH55_INSTALL; install agent is never pulsed, skipping payload override",
                         ch56_image_path);
            end else begin
                wait (rst_n);
                @(posedge clk);
                // Match pure-external-mode tail semantics: no built-in
                // fallback behind short files.
                for (int i = 0; i < 96; i = i + 1)
                    u_install_agent.payload[i] = 32'hx;
                $display("[tb_ee_core_bios_smoke] CH56_IMAGE_OVERRIDE: reloading u_install_agent.payload from %s (payload X-filled first for pure-external-mode parity)",
                         ch56_image_path);
                $readmemh(ch56_image_path, u_install_agent.payload);
            end
        end
    end

    logic ch53_stub_enabled;
    logic ch53_stub_installed;
    initial begin
        ch53_stub_enabled   = $test$plusargs("CH53_EXC_STUB");
        ch53_stub_installed = 1'b0;
        if (ch53_stub_enabled) begin
            $display("[tb_ee_core_bios_smoke] CH53_STUB_REQUESTED (waiting for r31_to_target edge, then installing 10-word AdES handler at useg 0x80..0xA4)");
            wait (using_real_bios && r31_to_target_captured);
            @(posedge clk);
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000020] = 32'h401A7000; // MFC0  $26, $14
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000021] = 32'hAC1A00A8; // SW    $26, 0xA8($0)
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000022] = 32'h401A4000; // MFC0  $26, $8
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000023] = 32'hAC1A00AC; // SW    $26, 0xAC($0)
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000024] = 32'h401A6800; // MFC0  $26, $13
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000025] = 32'hAC1A00B0; // SW    $26, 0xB0($0)
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000026] = 32'h401A7000; // MFC0  $26, $14
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000027] = 32'h275A0004; // ADDIU $26, $26, 4
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000028] = 32'h03400008; // JR    $26
            u_ee_map.g_useg_shadow.useg_shadow_mem[32'h00000029] = 32'h42000010; // RFE   (delay slot)
            ch53_stub_installed = 1'b1;
            $display("[tb_ee_core_bios_smoke] CH53_STUB_INSTALLED cyc=%0d: useg_shadow[0x80..0xA4] = 10-word handler (saves EPC/BadVAddr/Cause at 0xA8/0xAC/0xB0 then EPC+=4 + RFE)",
                     sim_cycle_ctr);
        end
    end

    // ------------------------------------------------------------------
    // Ch58 — dispatch-slot profiler.
    //
    // Ch57 proved install content is load-bearing (image B's NOP-only
    // runway collapsed progress by ~902K retires). This observer turns
    // the next question — "which slots actually matter?" — into
    // evidence. Every retire whose PC falls in useg [0x90..0x1FF] is
    // captured into a circular ring plus a per-slot count table. Each
    // hit records live regs $2, $4-$7, $9, $31, plus the immediately
    // previous retire from the existing dense retire_ring (ring head
    // minus 1 — one edge back, always authoritative by construction).
    //
    // Scope per Codex Ch58 plan: [0x90..0x1FF] (excludes 0x80..0x8C
    // AdES handler entry — that's already understood from ch52/53/54).
    //
    // Purely observational: no RTL change, no stub modification, no
    // payload change. Ch58 RUNS EXCLUSIVELY WITH THE RETURN-ONLY
    // IMAGE (built-in or ch56_ch54_equivalent.mem). The point is
    // profiling the known-good configuration, not yet experimenting
    // with handler content.
    // ------------------------------------------------------------------
    localparam logic [31:0] SLOT_LO           = 32'h0000_0090;
    localparam logic [31:0] SLOT_HI           = 32'h0000_0200;  // exclusive
    localparam int          SLOT_RING_DEPTH   = 64;
    // 96 covers word-aligned pc in [0x80..0x1FC]; we only populate
    // [0x90..0x1FC] via SLOT_LO/HI gating, but a 96-entry table
    // simplifies the index (slot_idx = pc[8:2] - 'h20).
    localparam int          SLOT_COUNT_SLOTS  = 96;

    int          slot_ring_count;                                       // monotone
    int          slot_ring_head;                                        // next write slot
    int          slot_ring_cyc        [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_pc         [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_instr      [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_r2         [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_r4         [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_r5         [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_r6         [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_r7         [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_r9         [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_r31        [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_prev_pc    [0:SLOT_RING_DEPTH-1];
    logic [31:0] slot_ring_prev_instr [0:SLOT_RING_DEPTH-1];
    int          slot_ring_prev_cyc   [0:SLOT_RING_DEPTH-1];

    int          slot_count      [0:SLOT_COUNT_SLOTS-1];                 // hits per slot
    int          slot_first_cyc  [0:SLOT_COUNT_SLOTS-1];
    int          slot_last_cyc   [0:SLOT_COUNT_SLOTS-1];
    int          slot_hit_total;

    initial begin
        slot_ring_count = 0;
        slot_ring_head  = 0;
        slot_hit_total  = 0;
        for (int i = 0; i < SLOT_RING_DEPTH; i = i + 1) begin
            slot_ring_cyc[i]        = 0;
            slot_ring_pc[i]         = 32'd0;
            slot_ring_instr[i]      = 32'd0;
            slot_ring_r2[i]         = 32'd0;
            slot_ring_r4[i]         = 32'd0;
            slot_ring_r5[i]         = 32'd0;
            slot_ring_r6[i]         = 32'd0;
            slot_ring_r7[i]         = 32'd0;
            slot_ring_r9[i]         = 32'd0;
            slot_ring_r31[i]        = 32'd0;
            slot_ring_prev_pc[i]    = 32'd0;
            slot_ring_prev_instr[i] = 32'd0;
            slot_ring_prev_cyc[i]   = 0;
        end
        for (int i = 0; i < SLOT_COUNT_SLOTS; i = i + 1) begin
            slot_count[i]     = 0;
            slot_first_cyc[i] = -1;
            slot_last_cyc[i]  = -1;
        end
    end

    // Eligibility for the current retire edge. u_core.retired_pc is
    // the PC of the just-retired instruction on the same edge
    // core_ev_valid fires, so sampling it directly here gets the
    // retiring pc at no one-cycle lag.
    logic        slot_hit_this_cycle;
    logic [31:0] slot_pc_now;
    logic [6:0]  slot_idx;                                               // 0..95 index into count[]

    assign slot_pc_now         = u_core.retired_pc;
    assign slot_hit_this_cycle = rst_n && core_ev_valid &&
                                 (slot_pc_now >= SLOT_LO) &&
                                 (slot_pc_now <  SLOT_HI);
    // pc[8:2] is in [0x24..0x7F] for pc in [0x90..0x1FC]. Shift the
    // origin so a pc of 0x80 maps to slot_idx=0 (unused by this
    // observer) and 0x1FC maps to slot_idx=95.
    assign slot_idx            = slot_pc_now[8:2] - 7'h20;

    always_ff @(posedge clk) begin
        if (slot_hit_this_cycle) begin
            slot_ring_cyc[slot_ring_head]        <= sim_cycle_ctr;
            slot_ring_pc[slot_ring_head]         <= slot_pc_now;
            slot_ring_instr[slot_ring_head]      <= u_core.retired_instr;
            slot_ring_r2[slot_ring_head]         <= u_core.regfile[2];
            slot_ring_r4[slot_ring_head]         <= u_core.regfile[4];
            slot_ring_r5[slot_ring_head]         <= u_core.regfile[5];
            slot_ring_r6[slot_ring_head]         <= u_core.regfile[6];
            slot_ring_r7[slot_ring_head]         <= u_core.regfile[7];
            slot_ring_r9[slot_ring_head]         <= u_core.regfile[9];
            slot_ring_r31[slot_ring_head]        <= u_core.regfile[31];
            // Previous retire context via retire_ring head-1. This slot
            // still holds the prior entry since retire_ring is also
            // being written this edge (NBA — its new-head update is
            // visible only at the next edge).
            slot_ring_prev_pc[slot_ring_head]    <= retire_ring_pc[ring_prev_idx];
            slot_ring_prev_instr[slot_ring_head] <= retire_ring_instr[ring_prev_idx];
            slot_ring_prev_cyc[slot_ring_head]   <= retire_ring_cyc[ring_prev_idx];

            slot_ring_head  <= (slot_ring_head + 1) % SLOT_RING_DEPTH;
            slot_ring_count <= slot_ring_count + 1;

            slot_count[slot_idx] <= slot_count[slot_idx] + 1;
            if (slot_first_cyc[slot_idx] < 0)
                slot_first_cyc[slot_idx] <= sim_cycle_ctr;
            slot_last_cyc[slot_idx] <= sim_cycle_ctr;
            slot_hit_total <= slot_hit_total + 1;
        end
    end

    // ------------------------------------------------------------------
    // Ch59 — dispatch-call correlation log.
    //
    // Ch58 showed 0xB0 (14 calls) and 0xC0 (7 calls) are the real
    // syscall-style dispatch entries, with $9 as the call number and
    // $31 as the ROM return address. Ch59 correlates each individual
    // call to what happens AFTER its return: does the BIOS reach the
    // 0x00400000 trap site soon, take an exception, make another low-
    // RAM dispatch, or run a long stretch of normal code?
    //
    // Per-call captures: seq#, cyc, entry_pc (0xB0|0xC0), $9, $4..$7,
    // $2, $31, start-retire count. After allocation, the "active"
    // entry is watched on every retire until one of four outcomes:
    //   TRAP_400000     — retire with pc=0x00400000 (final trap site)
    //   NEXT_DISPATCH   — another 0xB0/0xC0 dispatch starts
    //   LONG_RUN        — DISPATCH_WINDOW retires elapsed without
    //                     either of the above (the BIOS returned into
    //                     ROM/RAM and kept making normal progress)
    //   IN_PROGRESS     — only valid if the run halted while the
    //                     window was still open; inspect manually
    //
    // Codex's hunch: the LAST few dispatches before the trap carry
    // more signal than the full set. The post-halt print is ordered
    // by seq#, with the tail serving as the trap-setup lineup. Any
    // entry whose outcome is TRAP_400000 is flagged — that is the
    // call that set up the bad branch. No RTL change; no other TB
    // semantic change.
    // ------------------------------------------------------------------
    localparam int DISPATCH_LOG_DEPTH = 32;
    localparam int DISPATCH_WINDOW    = 4096;                            // retires

    localparam int OUT_IN_PROGRESS   = 0;
    localparam int OUT_TRAP_400000   = 1;
    localparam int OUT_NEXT_DISPATCH = 2;
    localparam int OUT_LONG_RUN      = 3;

    int          disp_log_count;                                         // monotone
    int          disp_log_head;                                          // next slot
    int          disp_log_cyc             [0:DISPATCH_LOG_DEPTH-1];
    int          disp_log_entry_retires   [0:DISPATCH_LOG_DEPTH-1];
    int          disp_log_seq             [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_entry_pc        [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_r2              [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_r4              [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_r5              [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_r6              [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_r7              [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_r9              [0:DISPATCH_LOG_DEPTH-1];
    logic [31:0] disp_log_r31             [0:DISPATCH_LOG_DEPTH-1];
    int          disp_log_outcome         [0:DISPATCH_LOG_DEPTH-1];
    int          disp_log_outcome_cyc     [0:DISPATCH_LOG_DEPTH-1];
    int          disp_log_outcome_detail  [0:DISPATCH_LOG_DEPTH-1];

    int          disp_active_idx;                                        // -1 = no IN_PROGRESS

    initial begin
        disp_log_count  = 0;
        disp_log_head   = 0;
        disp_active_idx = -1;
        for (int i = 0; i < DISPATCH_LOG_DEPTH; i = i + 1) begin
            disp_log_cyc[i]            = 0;
            disp_log_entry_retires[i]  = 0;
            disp_log_seq[i]            = 0;
            disp_log_entry_pc[i]       = 32'd0;
            disp_log_r2[i]             = 32'd0;
            disp_log_r4[i]             = 32'd0;
            disp_log_r5[i]             = 32'd0;
            disp_log_r6[i]             = 32'd0;
            disp_log_r7[i]             = 32'd0;
            disp_log_r9[i]             = 32'd0;
            disp_log_r31[i]            = 32'd0;
            disp_log_outcome[i]        = OUT_IN_PROGRESS;
            disp_log_outcome_cyc[i]    = 0;
            disp_log_outcome_detail[i] = 0;
        end
    end

    logic is_dispatch_entry;
    assign is_dispatch_entry = slot_hit_this_cycle &&
                               (slot_pc_now == 32'h0000_00B0 ||
                                slot_pc_now == 32'h0000_00C0);

    // ------------------------------------------------------------------
    // Ch60 — late control-flow provenance tracer.
    //
    // Ch59's dispatch log showed the 21 0xB0/0xC0 syscalls all clear
    // within cyc ~307K, but the 0x00400000 trap fires ~700K retires
    // later in autonomous code. Ch60 pinpoints the instruction and
    // register that supplied pc=0x00400000.
    //
    // Three stacked observers:
    //   (A) LATE_CF_RING — a depth-64 circular ring capturing every
    //       retired control-flow instruction (J/JAL/JR/JALR/branch/
    //       REGIMM/RFE) once retired_events >= CF_ACTIVATE_RETIRED.
    //       Keeps the most recent 64 CF retires at halt, so the JR
    //       that jumped to 0x00400000 is guaranteed present if it
    //       fell in the post-activation window.
    //   (B) Register-to-0x00400000 watchers. One-shot per register
    //       ($8, $10, $26, $31, $2). Captures the first retire whose
    //       regfile write makes the register equal to 0x00400000 (the
    //       producer of the bad target, whichever register the JR
    //       reads).
    //   (C) TRAP_SNAPSHOT — one-shot on the first retire with
    //       pc=0x00400000 (the trap retire itself). Captures a wide
    //       register bank ($2, $4-$7, $8-$10, $26-$31) plus the
    //       immediate predecessor retire from retire_ring head-1.
    //
    // Purely observational; no RTL/TB semantic change.
    // ------------------------------------------------------------------

    // (A) LATE_CF_RING
    localparam int CF_RING_DEPTH       = 64;
    localparam int CF_ACTIVATE_RETIRED = 300_000;

    int          cf_ring_count;
    int          cf_ring_head;
    int          cf_ring_cyc     [0:CF_RING_DEPTH-1];
    int          cf_ring_retires [0:CF_RING_DEPTH-1];
    logic [31:0] cf_ring_pc      [0:CF_RING_DEPTH-1];
    logic [31:0] cf_ring_instr   [0:CF_RING_DEPTH-1];
    logic [31:0] cf_ring_rs_val  [0:CF_RING_DEPTH-1];
    logic [4:0]  cf_ring_rs_idx  [0:CF_RING_DEPTH-1];
    logic [31:0] cf_ring_r31     [0:CF_RING_DEPTH-1];

    // Combinational decode of the currently-retiring instruction.
    logic [5:0] cf_op;
    logic [5:0] cf_func;
    logic [4:0] cf_rs;
    logic       cf_is_branch_or_jump;
    assign cf_op   = u_core.retired_instr[31:26];
    assign cf_func = u_core.retired_instr[5:0];
    assign cf_rs   = u_core.retired_instr[25:21];
    assign cf_is_branch_or_jump =
        (cf_op == 6'h02) ||                                              // J
        (cf_op == 6'h03) ||                                              // JAL
        (cf_op == 6'h00 && (cf_func == 6'h08 || cf_func == 6'h09)) ||    // JR/JALR
        (cf_op == 6'h04) ||                                              // BEQ
        (cf_op == 6'h05) ||                                              // BNE
        (cf_op == 6'h06) ||                                              // BLEZ
        (cf_op == 6'h07) ||                                              // BGTZ
        (cf_op == 6'h01) ||                                              // REGIMM (BLTZ/BGEZ)
        (cf_op == 6'h10 && cf_func == 6'h10);                            // RFE

    initial begin
        cf_ring_count = 0;
        cf_ring_head  = 0;
        for (int i = 0; i < CF_RING_DEPTH; i = i + 1) begin
            cf_ring_cyc[i]     = 0;
            cf_ring_retires[i] = 0;
            cf_ring_pc[i]      = 32'd0;
            cf_ring_instr[i]   = 32'd0;
            cf_ring_rs_val[i]  = 32'd0;
            cf_ring_rs_idx[i]  = 5'd0;
            cf_ring_r31[i]     = 32'd0;
        end
    end

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid &&
            core_retired_events >= CF_ACTIVATE_RETIRED &&
            cf_is_branch_or_jump) begin
            cf_ring_cyc[cf_ring_head]     <= sim_cycle_ctr;
            cf_ring_retires[cf_ring_head] <= core_retired_events;
            cf_ring_pc[cf_ring_head]      <= u_core.retired_pc;
            cf_ring_instr[cf_ring_head]   <= u_core.retired_instr;
            cf_ring_rs_val[cf_ring_head]  <= u_core.regfile[cf_rs];
            cf_ring_rs_idx[cf_ring_head]  <= cf_rs;
            cf_ring_r31[cf_ring_head]     <= u_core.regfile[31];
            cf_ring_head  <= (cf_ring_head + 1) % CF_RING_DEPTH;
            cf_ring_count <= cf_ring_count + 1;
        end
    end

    // (B) Register-to-0x00400000 watchers (one-shot per register).
    // Follow the existing R10_TO_C0 pattern: snapshot the regfile one
    // edge behind, detect first transition into the sentinel value.
    localparam logic [31:0] TRAP_TARGET = 32'h0040_0000;

    logic [31:0] ch60_r2_prev;
    logic [31:0] ch60_r8_prev;
    logic [31:0] ch60_r10_prev;
    logic [31:0] ch60_r26_prev;
    logic [31:0] ch60_r31_prev;
    logic        ch60_r2_prev_valid;
    logic        ch60_r8_prev_valid;
    logic        ch60_r10_prev_valid;
    logic        ch60_r26_prev_valid;
    logic        ch60_r31_prev_valid;

    logic        r2_to_trap_captured;
    logic        r8_to_trap_captured;
    logic        r10_to_trap_captured;
    logic        r26_to_trap_captured;
    logic        r31_to_trap_captured;

    int          r2_to_trap_cyc;
    int          r8_to_trap_cyc;
    int          r10_to_trap_cyc;
    int          r26_to_trap_cyc;
    int          r31_to_trap_cyc;
    logic [31:0] r2_to_trap_pc;
    logic [31:0] r8_to_trap_pc;
    logic [31:0] r10_to_trap_pc;
    logic [31:0] r26_to_trap_pc;
    logic [31:0] r31_to_trap_pc;
    logic [31:0] r2_to_trap_instr;
    logic [31:0] r8_to_trap_instr;
    logic [31:0] r10_to_trap_instr;
    logic [31:0] r26_to_trap_instr;
    logic [31:0] r31_to_trap_instr;
    logic [31:0] r2_to_trap_arg2;
    logic [31:0] r8_to_trap_arg2;
    logic [31:0] r10_to_trap_arg2;
    logic [31:0] r26_to_trap_arg2;
    logic [31:0] r31_to_trap_arg2;
    logic [31:0] r2_to_trap_arg3;
    logic [31:0] r8_to_trap_arg3;
    logic [31:0] r10_to_trap_arg3;
    logic [31:0] r26_to_trap_arg3;
    logic [31:0] r31_to_trap_arg3;

    initial begin
        ch60_r2_prev_valid  = 1'b0;
        ch60_r8_prev_valid  = 1'b0;
        ch60_r10_prev_valid = 1'b0;
        ch60_r26_prev_valid = 1'b0;
        ch60_r31_prev_valid = 1'b0;
        r2_to_trap_captured  = 1'b0;
        r8_to_trap_captured  = 1'b0;
        r10_to_trap_captured = 1'b0;
        r26_to_trap_captured = 1'b0;
        r31_to_trap_captured = 1'b0;
    end

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch60_r2_prev_valid  <= 1'b0;
            ch60_r8_prev_valid  <= 1'b0;
            ch60_r10_prev_valid <= 1'b0;
            ch60_r26_prev_valid <= 1'b0;
            ch60_r31_prev_valid <= 1'b0;
        end else begin
            if (!r2_to_trap_captured && ch60_r2_prev_valid &&
                u_core.regfile[2] == TRAP_TARGET && ch60_r2_prev != TRAP_TARGET) begin
                r2_to_trap_captured <= 1'b1;
                r2_to_trap_cyc      <= sim_cycle_ctr;
                r2_to_trap_pc       <= u_core.retired_pc;
                r2_to_trap_instr    <= u_core.retired_instr;
                r2_to_trap_arg2     <= u_core.retired_arg2;
                r2_to_trap_arg3     <= u_core.retired_arg3;
            end
            if (!r8_to_trap_captured && ch60_r8_prev_valid &&
                u_core.regfile[8] == TRAP_TARGET && ch60_r8_prev != TRAP_TARGET) begin
                r8_to_trap_captured <= 1'b1;
                r8_to_trap_cyc      <= sim_cycle_ctr;
                r8_to_trap_pc       <= u_core.retired_pc;
                r8_to_trap_instr    <= u_core.retired_instr;
                r8_to_trap_arg2     <= u_core.retired_arg2;
                r8_to_trap_arg3     <= u_core.retired_arg3;
            end
            if (!r10_to_trap_captured && ch60_r10_prev_valid &&
                u_core.regfile[10] == TRAP_TARGET && ch60_r10_prev != TRAP_TARGET) begin
                r10_to_trap_captured <= 1'b1;
                r10_to_trap_cyc      <= sim_cycle_ctr;
                r10_to_trap_pc       <= u_core.retired_pc;
                r10_to_trap_instr    <= u_core.retired_instr;
                r10_to_trap_arg2     <= u_core.retired_arg2;
                r10_to_trap_arg3     <= u_core.retired_arg3;
            end
            if (!r26_to_trap_captured && ch60_r26_prev_valid &&
                u_core.regfile[26] == TRAP_TARGET && ch60_r26_prev != TRAP_TARGET) begin
                r26_to_trap_captured <= 1'b1;
                r26_to_trap_cyc      <= sim_cycle_ctr;
                r26_to_trap_pc       <= u_core.retired_pc;
                r26_to_trap_instr    <= u_core.retired_instr;
                r26_to_trap_arg2     <= u_core.retired_arg2;
                r26_to_trap_arg3     <= u_core.retired_arg3;
            end
            if (!r31_to_trap_captured && ch60_r31_prev_valid &&
                u_core.regfile[31] == TRAP_TARGET && ch60_r31_prev != TRAP_TARGET) begin
                r31_to_trap_captured <= 1'b1;
                r31_to_trap_cyc      <= sim_cycle_ctr;
                r31_to_trap_pc       <= u_core.retired_pc;
                r31_to_trap_instr    <= u_core.retired_instr;
                r31_to_trap_arg2     <= u_core.retired_arg2;
                r31_to_trap_arg3     <= u_core.retired_arg3;
            end

            ch60_r2_prev        <= u_core.regfile[2];
            ch60_r8_prev        <= u_core.regfile[8];
            ch60_r10_prev       <= u_core.regfile[10];
            ch60_r26_prev       <= u_core.regfile[26];
            ch60_r31_prev       <= u_core.regfile[31];
            ch60_r2_prev_valid  <= 1'b1;
            ch60_r8_prev_valid  <= 1'b1;
            ch60_r10_prev_valid <= 1'b1;
            ch60_r26_prev_valid <= 1'b1;
            ch60_r31_prev_valid <= 1'b1;
        end
    end

    // ------------------------------------------------------------------
    // Ch61 — population/provenance observer for the J-target region at
    // 0x001A4530 (and its neighborhood). Ch60 pinned the trap cause to
    // a 618K-retire NOP walk starting at J 0x001A4530; Codex's Ch61
    // question is "was that region ever populated in this run?" If
    // yes, something overwrote or aliased it; if no, there's a missing
    // install/copy phase targeting it.
    //
    // Two-scope write monitor, tapping the existing CPU (ee_wr_*) and
    // install-bridge (install_br_wr_*) ports at TB scope:
    //   - narrow [0x001A4000, 0x001A5000): ring of up to
    //     W1A_RING_DEPTH entries with writer pc/instr/addr/data/be/
    //     master — enough resolution to see who touched the target
    //     region and what they wrote.
    //   - broader [0x001A0000, 0x001B0000): CPU and bridge monotonic
    //     counters. Answers "any writes near the target at all?"
    //
    // Purely observational.
    // ------------------------------------------------------------------
    localparam logic [28:0] W1A_NARROW_LO = 29'h1A_4000;
    localparam logic [28:0] W1A_NARROW_HI = 29'h1A_5000;                 // exclusive
    localparam logic [28:0] W1A_BROAD_LO  = 29'h1A_0000;
    localparam logic [28:0] W1A_BROAD_HI  = 29'h1B_0000;                 // exclusive
    localparam int          W1A_RING_DEPTH = 32;

    int           w1a_ring_count;
    int           w1a_ring_head;
    int           w1a_ring_cyc       [0:W1A_RING_DEPTH-1];
    logic [31:0]  w1a_ring_wr_pc     [0:W1A_RING_DEPTH-1];
    logic [31:0]  w1a_ring_wr_instr  [0:W1A_RING_DEPTH-1];
    logic [31:0]  w1a_ring_addr      [0:W1A_RING_DEPTH-1];
    logic [127:0] w1a_ring_data      [0:W1A_RING_DEPTH-1];
    logic [15:0]  w1a_ring_be        [0:W1A_RING_DEPTH-1];
    logic         w1a_ring_master    [0:W1A_RING_DEPTH-1];               // 0=CPU, 1=bridge

    int w1a_cpu_narrow;
    int w1a_bridge_narrow;
    int w1a_cpu_broad;
    int w1a_bridge_broad;

    initial begin
        w1a_ring_count    = 0;
        w1a_ring_head     = 0;
        w1a_cpu_narrow    = 0;
        w1a_bridge_narrow = 0;
        w1a_cpu_broad     = 0;
        w1a_bridge_broad  = 0;
        for (int i = 0; i < W1A_RING_DEPTH; i = i + 1) begin
            w1a_ring_cyc[i]      = 0;
            w1a_ring_wr_pc[i]    = 32'd0;
            w1a_ring_wr_instr[i] = 32'd0;
            w1a_ring_addr[i]     = 32'd0;
            w1a_ring_data[i]     = 128'd0;
            w1a_ring_be[i]       = 16'd0;
            w1a_ring_master[i]   = 1'b0;
        end
    end

    logic [28:0] ch61_cpu_phys;
    logic [28:0] ch61_bridge_phys;
    assign ch61_cpu_phys    = ee_wr_addr[28:0];
    assign ch61_bridge_phys = install_br_wr_addr[28:0];

    always_ff @(posedge clk) begin
        if (rst_n) begin
            // CPU writes
            if (ee_wr_en) begin
                if (ch61_cpu_phys >= W1A_BROAD_LO && ch61_cpu_phys < W1A_BROAD_HI)
                    w1a_cpu_broad <= w1a_cpu_broad + 1;
                if (ch61_cpu_phys >= W1A_NARROW_LO && ch61_cpu_phys < W1A_NARROW_HI) begin
                    w1a_cpu_narrow <= w1a_cpu_narrow + 1;
                    w1a_ring_cyc[w1a_ring_head]      <= sim_cycle_ctr;
                    w1a_ring_wr_pc[w1a_ring_head]    <= u_core.retired_pc;
                    w1a_ring_wr_instr[w1a_ring_head] <= u_core.retired_instr;
                    w1a_ring_addr[w1a_ring_head]     <= ee_wr_addr;
                    w1a_ring_data[w1a_ring_head]     <= {96'd0, ee_wr_data};
                    w1a_ring_be[w1a_ring_head]       <= {12'd0, ee_wr_be};
                    w1a_ring_master[w1a_ring_head]   <= 1'b0;
                    w1a_ring_head  <= (w1a_ring_head + 1) % W1A_RING_DEPTH;
                    w1a_ring_count <= w1a_ring_count + 1;
                end
            end
            // Install-bridge writes
            if (install_br_wr_en) begin
                if (ch61_bridge_phys >= W1A_BROAD_LO && ch61_bridge_phys < W1A_BROAD_HI)
                    w1a_bridge_broad <= w1a_bridge_broad + 1;
                if (ch61_bridge_phys >= W1A_NARROW_LO && ch61_bridge_phys < W1A_NARROW_HI) begin
                    w1a_bridge_narrow <= w1a_bridge_narrow + 1;
                    w1a_ring_cyc[w1a_ring_head]      <= sim_cycle_ctr;
                    w1a_ring_wr_pc[w1a_ring_head]    <= u_core.retired_pc;
                    w1a_ring_wr_instr[w1a_ring_head] <= u_core.retired_instr;
                    w1a_ring_addr[w1a_ring_head]     <= install_br_wr_addr;
                    w1a_ring_data[w1a_ring_head]     <= install_br_wr_data;
                    w1a_ring_be[w1a_ring_head]       <= install_br_wr_be;
                    w1a_ring_master[w1a_ring_head]   <= 1'b1;
                    w1a_ring_head  <= (w1a_ring_head + 1) % W1A_RING_DEPTH;
                    w1a_ring_count <= w1a_ring_count + 1;
                end
            end
        end
    end

    // ------------------------------------------------------------------
    // Ch62 — focused 0xA0 dispatch profile.
    //
    // Ch61 identified the missing install as syscall $9=0x44 via entry
    // 0xA0 (distinct from the 0xB0/0xC0 family Ch58/59 characterized).
    // Codex's Ch62 scope: profile the 0xA0 call the same way, and
    // capture the ABI evidence needed before writing a targeted stub.
    //
    // One-shot, because Ch58 showed 0xA0 fires exactly once in the
    // full run (cyc 1,268,799). Captures:
    //   - entry state: $2, $4-$7, $9, $10, $31 at the 0xA0 retire;
    //   - immediate previous CF retire (caller context from the
    //     ring, same helper as SLOT_RING);
    //   - 32 retires BEFORE entry: snapshot of retire_ring at the
    //     moment of capture. Shows how $4-$7, $9, $10 were formed in
    //     the caller. Frozen on 0xA0 entry and printed at halt.
    //   - up to 16 retires AFTER entry, ending at the J at pc=
    //     0x00030044. Confirms no intervening register setup between
    //     return-from-syscall and the J into 0x001A4530.
    //
    // Purely observational; no RTL change.
    // ------------------------------------------------------------------
    localparam int A0_POST_DEPTH  = 16;
    localparam logic [31:0] A0_POST_STOP_PC = 32'h0003_0044;

    logic        a0_captured;
    int          a0_cyc;
    int          a0_retires;
    logic [31:0] a0_r2, a0_r4, a0_r5, a0_r6, a0_r7, a0_r9, a0_r10, a0_r31;
    logic [31:0] a0_prev_cf_pc, a0_prev_cf_instr;
    int          a0_prev_cf_cyc;

    logic [31:0] a0_setup_pc    [0:RETIRE_RING_DEPTH-1];
    logic [31:0] a0_setup_instr [0:RETIRE_RING_DEPTH-1];
    int          a0_setup_cyc   [0:RETIRE_RING_DEPTH-1];
    int          a0_setup_head;                                          // for ordering at print time

    logic        a0_post_active;
    int          a0_post_count;
    int          a0_post_cyc    [0:A0_POST_DEPTH-1];
    logic [31:0] a0_post_pc     [0:A0_POST_DEPTH-1];
    logic [31:0] a0_post_instr  [0:A0_POST_DEPTH-1];
    logic [31:0] a0_post_r4     [0:A0_POST_DEPTH-1];
    logic [31:0] a0_post_r5     [0:A0_POST_DEPTH-1];
    logic [31:0] a0_post_r2     [0:A0_POST_DEPTH-1];
    logic [31:0] a0_post_r31    [0:A0_POST_DEPTH-1];

    int a0_snap_k;

    initial begin
        a0_captured    = 1'b0;
        a0_post_active = 1'b0;
        a0_post_count  = 0;
        a0_setup_head  = 0;
        for (int i = 0; i < RETIRE_RING_DEPTH; i = i + 1) begin
            a0_setup_pc[i]    = 32'd0;
            a0_setup_instr[i] = 32'd0;
            a0_setup_cyc[i]   = 0;
        end
        for (int i = 0; i < A0_POST_DEPTH; i = i + 1) begin
            a0_post_cyc[i]   = 0;
            a0_post_pc[i]    = 32'd0;
            a0_post_instr[i] = 32'd0;
            a0_post_r4[i]    = 32'd0;
            a0_post_r5[i]    = 32'd0;
            a0_post_r2[i]    = 32'd0;
            a0_post_r31[i]   = 32'd0;
        end
    end

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid) begin
            // One-shot 0xA0 entry capture
            if (!a0_captured && u_core.retired_pc == 32'h0000_00A0) begin
                a0_captured      <= 1'b1;
                a0_cyc           <= sim_cycle_ctr;
                a0_retires       <= core_retired_events;
                a0_r2            <= u_core.regfile[2];
                a0_r4            <= u_core.regfile[4];
                a0_r5            <= u_core.regfile[5];
                a0_r6            <= u_core.regfile[6];
                a0_r7            <= u_core.regfile[7];
                a0_r9            <= u_core.regfile[9];
                a0_r10           <= u_core.regfile[10];
                a0_r31           <= u_core.regfile[31];
                a0_prev_cf_pc    <= retire_ring_pc[ring_prev_idx];
                a0_prev_cf_instr <= retire_ring_instr[ring_prev_idx];
                a0_prev_cf_cyc   <= retire_ring_cyc[ring_prev_idx];
                a0_setup_head    <= retire_ring_head;

                // Snapshot the retire ring into the setup arrays. This
                // fires exactly once, on the 0xA0 entry edge. The
                // retire_ring content at this moment is the last 32
                // retires ending with the JALR's delay slot — the
                // span that set up $4-$7/$9/$10.
                for (a0_snap_k = 0; a0_snap_k < RETIRE_RING_DEPTH; a0_snap_k = a0_snap_k + 1) begin
                    a0_setup_pc[a0_snap_k]    <= retire_ring_pc[a0_snap_k];
                    a0_setup_instr[a0_snap_k] <= retire_ring_instr[a0_snap_k];
                    a0_setup_cyc[a0_snap_k]   <= retire_ring_cyc[a0_snap_k];
                end

                a0_post_active             <= 1'b1;
                a0_post_count              <= 1;
                a0_post_cyc[0]             <= sim_cycle_ctr;
                a0_post_pc[0]              <= u_core.retired_pc;
                a0_post_instr[0]           <= u_core.retired_instr;
                a0_post_r4[0]              <= u_core.regfile[4];
                a0_post_r5[0]              <= u_core.regfile[5];
                a0_post_r2[0]              <= u_core.regfile[2];
                a0_post_r31[0]             <= u_core.regfile[31];
            end
            // Post-entry capture until stop pc or depth exhaustion
            else if (a0_post_active && a0_post_count < A0_POST_DEPTH) begin
                a0_post_cyc[a0_post_count]   <= sim_cycle_ctr;
                a0_post_pc[a0_post_count]    <= u_core.retired_pc;
                a0_post_instr[a0_post_count] <= u_core.retired_instr;
                a0_post_r4[a0_post_count]    <= u_core.regfile[4];
                a0_post_r5[a0_post_count]    <= u_core.regfile[5];
                a0_post_r2[a0_post_count]    <= u_core.regfile[2];
                a0_post_r31[a0_post_count]   <= u_core.regfile[31];
                a0_post_count <= a0_post_count + 1;
                if (u_core.retired_pc == A0_POST_STOP_PC)
                    a0_post_active <= 1'b0;
            end
        end
    end

    // ------------------------------------------------------------------
    // Ch63 — memcpy source-region provenance for phys 0x0004468C.
    //
    // Ch62 pinned the trap root to two stacked problems: the memcpy
    // source at phys 0x0004468C was all-zero (so the copy wrote zeros
    // into the destination), and kseg0→useg aliasing may hide even a
    // populated ee_ram image from the later useg J-fetch.
    //
    // Codex's Ch63 scope answers ONLY the first question: was the
    // source buffer ever populated, and in which backing?  If no
    // writer appears at all, the gap is upstream (external install,
    // decompression, BIOS phase we haven't reached). If populated
    // only in ee_ram and not useg_shadow, the visibility model is
    // the next fix. We don't decide aliasing before knowing whether
    // bytes exist.
    //
    // Three-scope write monitor:
    //   narrow  [0x0004_4640..0x0004_4700) — 192 bytes covering the
    //     full 16-word memcpy span that A0_PROFILE showed ($4 end
    //     ptr = 0x8004468C, copied 64 bytes backwards).
    //   broader [0x0004_4000..0x0004_5000) — 4 KiB around it.
    //   widest  [0x0004_0000..0x0005_0000) — 64 KiB fallback if the
    //     narrower windows are empty.
    //
    // Plus first-LW capture at pc=0x00030014 (the memcpy's LW). On
    // the first retire there whose EA (retired_arg2) falls in the
    // broader source window, snapshot arg2/arg3 — that's exactly
    // the byte the CPU loaded and from which address.
    //
    // Purely observational; no RTL change.
    // ------------------------------------------------------------------
    localparam logic [28:0] SRC_NARROW_LO  = 29'h0004_4640;
    localparam logic [28:0] SRC_NARROW_HI  = 29'h0004_4700;              // exclusive
    localparam logic [28:0] SRC_BROAD_LO   = 29'h0004_4000;
    localparam logic [28:0] SRC_BROAD_HI   = 29'h0004_5000;              // exclusive
    localparam logic [28:0] SRC_WIDEST_LO  = 29'h0004_0000;
    localparam logic [28:0] SRC_WIDEST_HI  = 29'h0005_0000;              // exclusive
    localparam int          SRC_RING_DEPTH = 32;
    localparam logic [31:0] MEMCPY_LW_PC   = 32'h0003_0014;

    int           src_ring_count;
    int           src_ring_head;
    int           src_ring_cyc       [0:SRC_RING_DEPTH-1];
    logic [31:0]  src_ring_wr_pc     [0:SRC_RING_DEPTH-1];
    logic [31:0]  src_ring_wr_instr  [0:SRC_RING_DEPTH-1];
    logic [31:0]  src_ring_addr      [0:SRC_RING_DEPTH-1];
    logic [127:0] src_ring_data      [0:SRC_RING_DEPTH-1];
    logic [15:0]  src_ring_be        [0:SRC_RING_DEPTH-1];
    logic         src_ring_master    [0:SRC_RING_DEPTH-1];               // 0=CPU, 1=bridge

    int src_cpu_narrow,  src_bridge_narrow;
    int src_cpu_broad,   src_bridge_broad;
    int src_cpu_widest,  src_bridge_widest;

    logic        first_lw_captured;
    int          first_lw_cyc;
    logic [31:0] first_lw_ea;
    logic [31:0] first_lw_loaded;

    initial begin
        src_ring_count = 0;
        src_ring_head  = 0;
        src_cpu_narrow    = 0;  src_bridge_narrow = 0;
        src_cpu_broad     = 0;  src_bridge_broad  = 0;
        src_cpu_widest    = 0;  src_bridge_widest = 0;
        first_lw_captured = 1'b0;
        for (int i = 0; i < SRC_RING_DEPTH; i = i + 1) begin
            src_ring_cyc[i]      = 0;
            src_ring_wr_pc[i]    = 32'd0;
            src_ring_wr_instr[i] = 32'd0;
            src_ring_addr[i]     = 32'd0;
            src_ring_data[i]     = 128'd0;
            src_ring_be[i]       = 16'd0;
            src_ring_master[i]   = 1'b0;
        end
    end

    logic [28:0] ch63_cpu_phys;
    logic [28:0] ch63_bridge_phys;
    assign ch63_cpu_phys    = ee_wr_addr[28:0];
    assign ch63_bridge_phys = install_br_wr_addr[28:0];

    always_ff @(posedge clk) begin
        if (rst_n) begin
            // CPU writes
            if (ee_wr_en) begin
                if (ch63_cpu_phys >= SRC_WIDEST_LO && ch63_cpu_phys < SRC_WIDEST_HI)
                    src_cpu_widest <= src_cpu_widest + 1;
                if (ch63_cpu_phys >= SRC_BROAD_LO && ch63_cpu_phys < SRC_BROAD_HI)
                    src_cpu_broad <= src_cpu_broad + 1;
                if (ch63_cpu_phys >= SRC_NARROW_LO && ch63_cpu_phys < SRC_NARROW_HI) begin
                    src_cpu_narrow <= src_cpu_narrow + 1;
                    src_ring_cyc[src_ring_head]      <= sim_cycle_ctr;
                    src_ring_wr_pc[src_ring_head]    <= u_core.retired_pc;
                    src_ring_wr_instr[src_ring_head] <= u_core.retired_instr;
                    src_ring_addr[src_ring_head]     <= ee_wr_addr;
                    src_ring_data[src_ring_head]     <= {96'd0, ee_wr_data};
                    src_ring_be[src_ring_head]       <= {12'd0, ee_wr_be};
                    src_ring_master[src_ring_head]   <= 1'b0;
                    src_ring_head  <= (src_ring_head + 1) % SRC_RING_DEPTH;
                    src_ring_count <= src_ring_count + 1;
                end
            end
            // Install-bridge writes
            if (install_br_wr_en) begin
                if (ch63_bridge_phys >= SRC_WIDEST_LO && ch63_bridge_phys < SRC_WIDEST_HI)
                    src_bridge_widest <= src_bridge_widest + 1;
                if (ch63_bridge_phys >= SRC_BROAD_LO && ch63_bridge_phys < SRC_BROAD_HI)
                    src_bridge_broad <= src_bridge_broad + 1;
                if (ch63_bridge_phys >= SRC_NARROW_LO && ch63_bridge_phys < SRC_NARROW_HI) begin
                    src_bridge_narrow <= src_bridge_narrow + 1;
                    src_ring_cyc[src_ring_head]      <= sim_cycle_ctr;
                    src_ring_wr_pc[src_ring_head]    <= u_core.retired_pc;
                    src_ring_wr_instr[src_ring_head] <= u_core.retired_instr;
                    src_ring_addr[src_ring_head]     <= install_br_wr_addr;
                    src_ring_data[src_ring_head]     <= install_br_wr_data;
                    src_ring_be[src_ring_head]       <= install_br_wr_be;
                    src_ring_master[src_ring_head]   <= 1'b1;
                    src_ring_head  <= (src_ring_head + 1) % SRC_RING_DEPTH;
                    src_ring_count <= src_ring_count + 1;
                end
            end
            // First memcpy-LW capture: retire at pc=0x30014 (the LW)
            // whose EA (retired_arg2) is in the broader source window.
            // That's the first real observation of what the CPU saw.
            if (core_ev_valid && !first_lw_captured &&
                u_core.retired_pc == MEMCPY_LW_PC &&
                u_core.retired_arg2[28:0] >= SRC_BROAD_LO &&
                u_core.retired_arg2[28:0] <  SRC_BROAD_HI) begin
                first_lw_captured <= 1'b1;
                first_lw_cyc      <= sim_cycle_ctr;
                first_lw_ea       <= u_core.retired_arg2;
                first_lw_loaded   <= u_core.retired_arg3;
            end
        end
    end

    // ------------------------------------------------------------------
    // Ch66 — focused trap snapshot at the new post-Ch65 trap site,
    // pc=0x001A459C, plus a recent load/store ring. Ch65 broke through
    // the empty-useg/kseg visibility wall; now we're 27 instructions
    // into the copied code and hitting a new failure. Codex wants to
    // disambiguate: is the trapping word itself unsupported (SRLV
    // strict halt → decode gap), or is an AdES exception firing on a
    // nearby store (alignment fault on a real memory op)?
    //
    // Captures:
    //   - first retire at pc=0x001A459C: instr, COP0 state (EPC,
    //     Cause.ExcCode, BadVAddr, Status.BEV), wide register bank
    //     ($2-$10, $26-$31), prior exception count so we can tell
    //     whether the core entered an exception at this site.
    //   - depth-16 recent load/store ring: every retire whose opcode
    //     is LB/LBU/LH/LHU/LW/LWL/LWR or SB/SH/SW/SWL/SWR. Records
    //     cyc, pc, instr, retired_arg2 (EA) and retired_arg3 (data).
    //     The last few entries before the trap show whether any load
    //     or store near 0x001A459C had a misaligned EA.
    // ------------------------------------------------------------------
    localparam logic [31:0] CH66_TRAP_PC   = 32'h001A_459C;
    localparam int          MEM_RING_DEPTH = 16;

    logic        ch66_trap_captured;
    int          ch66_trap_cyc;
    int          ch66_trap_retires;
    logic [31:0] ch66_trap_instr;
    logic [31:0] ch66_trap_epc;
    logic [4:0]  ch66_trap_cause_exc;
    logic [31:0] ch66_trap_badvaddr;
    logic        ch66_trap_status_bev;
    logic [31:0] ch66_trap_r2, ch66_trap_r3, ch66_trap_r4, ch66_trap_r5;
    logic [31:0] ch66_trap_r6, ch66_trap_r7, ch66_trap_r8, ch66_trap_r9;
    logic [31:0] ch66_trap_r10;
    logic [31:0] ch66_trap_r26, ch66_trap_r27, ch66_trap_r28, ch66_trap_r29;
    logic [31:0] ch66_trap_r30, ch66_trap_r31;
    int          ch66_trap_prev_except_count;
    int          ch66_trap_prev_trap_count;

    // Recent-memory ring
    int          mem_ring_count;
    int          mem_ring_head;
    int          mem_ring_cyc   [0:MEM_RING_DEPTH-1];
    logic [31:0] mem_ring_pc    [0:MEM_RING_DEPTH-1];
    logic [31:0] mem_ring_instr [0:MEM_RING_DEPTH-1];
    logic [31:0] mem_ring_ea    [0:MEM_RING_DEPTH-1];
    logic [31:0] mem_ring_data  [0:MEM_RING_DEPTH-1];

    // Combinational load/store detection
    logic [5:0]  ch66_retired_op;
    logic        ch66_is_load_store;
    assign ch66_retired_op = u_core.retired_instr[31:26];
    assign ch66_is_load_store =
        (ch66_retired_op == 6'h20) ||                                    // LB
        (ch66_retired_op == 6'h21) ||                                    // LH
        (ch66_retired_op == 6'h22) ||                                    // LWL
        (ch66_retired_op == 6'h23) ||                                    // LW
        (ch66_retired_op == 6'h24) ||                                    // LBU
        (ch66_retired_op == 6'h25) ||                                    // LHU
        (ch66_retired_op == 6'h26) ||                                    // LWR
        (ch66_retired_op == 6'h28) ||                                    // SB
        (ch66_retired_op == 6'h29) ||                                    // SH
        (ch66_retired_op == 6'h2A) ||                                    // SWL
        (ch66_retired_op == 6'h2B) ||                                    // SW
        (ch66_retired_op == 6'h2E);                                      // SWR

    initial begin
        ch66_trap_captured = 1'b0;
        mem_ring_count     = 0;
        mem_ring_head      = 0;
        for (int i = 0; i < MEM_RING_DEPTH; i = i + 1) begin
            mem_ring_cyc[i]   = 0;
            mem_ring_pc[i]    = 32'd0;
            mem_ring_instr[i] = 32'd0;
            mem_ring_ea[i]    = 32'd0;
            mem_ring_data[i]  = 32'd0;
        end
    end

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid) begin
            // Trap snapshot at the focused pc
            if (!ch66_trap_captured && u_core.retired_pc == CH66_TRAP_PC) begin
                ch66_trap_captured           <= 1'b1;
                ch66_trap_cyc                <= sim_cycle_ctr;
                ch66_trap_retires            <= core_retired_events;
                ch66_trap_instr              <= u_core.retired_instr;
                ch66_trap_epc                <= u_core.epc;
                ch66_trap_cause_exc          <= u_core.cause_exc_code;
                ch66_trap_badvaddr           <= u_core.badvaddr;
                ch66_trap_status_bev         <= u_core.status_bev;
                ch66_trap_r2                 <= u_core.regfile[2];
                ch66_trap_r3                 <= u_core.regfile[3];
                ch66_trap_r4                 <= u_core.regfile[4];
                ch66_trap_r5                 <= u_core.regfile[5];
                ch66_trap_r6                 <= u_core.regfile[6];
                ch66_trap_r7                 <= u_core.regfile[7];
                ch66_trap_r8                 <= u_core.regfile[8];
                ch66_trap_r9                 <= u_core.regfile[9];
                ch66_trap_r10                <= u_core.regfile[10];
                ch66_trap_r26                <= u_core.regfile[26];
                ch66_trap_r27                <= u_core.regfile[27];
                ch66_trap_r28                <= u_core.regfile[28];
                ch66_trap_r29                <= u_core.regfile[29];
                ch66_trap_r30                <= u_core.regfile[30];
                ch66_trap_r31                <= u_core.regfile[31];
                ch66_trap_prev_except_count  <= core_exception_events;
                ch66_trap_prev_trap_count    <= core_trap_events;
            end

            // Recent load/store capture
            if (ch66_is_load_store) begin
                mem_ring_cyc[mem_ring_head]   <= sim_cycle_ctr;
                mem_ring_pc[mem_ring_head]    <= u_core.retired_pc;
                mem_ring_instr[mem_ring_head] <= u_core.retired_instr;
                mem_ring_ea[mem_ring_head]    <= u_core.retired_arg2;
                mem_ring_data[mem_ring_head]  <= u_core.retired_arg3;
                mem_ring_head  <= (mem_ring_head + 1) % MEM_RING_DEPTH;
                mem_ring_count <= mem_ring_count + 1;
            end
        end
    end

    // ------------------------------------------------------------------
    // Ch68 — loop characterization observer for the post-Ch67 BIOS
    // loop in the copied-code region. The Ch67 timeout report showed
    // pc cycling between 0x001A4564 and 0x001A45A0 (both LBU loads)
    // with $5 advancing — that suggests a load-driven scan, not a
    // wedge. Codex's question: is the loop healthy long-running BIOS
    // work, or another missing-state loop? This observer answers
    // "are pointers/counters progressing and across what EA range?".
    //
    // Captures (no RTL change):
    //   - LOOP_RING (depth 32): every retire whose pc is in
    //     [0x001A4530..0x001A45C0). Records cyc, retire count, pc,
    //     instr, $2-$10, $31, retired_arg2 (EA for loads/stores),
    //     retired_arg3 (loaded value).
    //   - per-pc visit counts for 0x001A4564 and 0x001A45A0 (the
    //     two pcs Codex called out).
    //   - load EA min/max + load count for loads inside the loop
    //     range. Span answers: is the scan a few bytes in place
    //     (wedge) or megabytes wide (real work)?
    //   - first/last snapshot of $4/$5/$6 to reveal pointer
    //     progression and direction.
    //   - last-branch snapshot: pc/instr + rs/rt regfile values at
    //     the most recent BEQ/BNE/BLEZ/BGTZ/REGIMM retire inside
    //     the loop range. Tells us what the back-edge condition
    //     evaluated to and which registers it reads.
    // ------------------------------------------------------------------
    localparam logic [31:0] LOOP_PC_LO     = 32'h001A_4530;
    localparam logic [31:0] LOOP_PC_HI     = 32'h001A_4640;              // exclusive (covers back-branch at 0x001A45FC)
    localparam logic [31:0] LOOP_PC_4564   = 32'h001A_4564;
    localparam logic [31:0] LOOP_PC_45A0   = 32'h001A_45A0;
    localparam int          LOOP_RING_DEPTH = 32;

    int          loop_ring_count;
    int          loop_ring_head;
    int          loop_ring_cyc      [0:LOOP_RING_DEPTH-1];
    int          loop_ring_retires  [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_pc       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_instr    [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r2       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r3       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r4       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r5       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r6       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r7       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r8       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r9       [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r10      [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_r31      [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_arg2     [0:LOOP_RING_DEPTH-1];
    logic [31:0] loop_ring_arg3     [0:LOOP_RING_DEPTH-1];

    int loop_count_4564;
    int loop_count_45a0;
    int loop_load_count;

    logic [31:0] loop_ea_min;
    logic [31:0] loop_ea_max;
    logic        loop_ea_seen;

    logic        loop_first_seen;
    logic [31:0] r4_first, r4_last;
    logic [31:0] r5_first, r5_last;
    logic [31:0] r6_first, r6_last;

    int          loop_branch_count;
    logic        last_branch_seen;
    int          last_branch_cyc;
    logic [31:0] last_branch_pc, last_branch_instr;
    logic [4:0]  last_branch_rs_idx, last_branch_rt_idx;
    logic [31:0] last_branch_rs_val, last_branch_rt_val;

    // Ch69 — loop entry/exit and projected-rate state.
    //   loop_seen        — armed once we've seen the first in-range retire.
    //   loop_first_cyc/retires — captured at first in-range retire so we
    //                            can compute progress rate at end-of-run.
    //   loop_exit_*      — one-shot snapshot at the first OUT-of-range
    //                      retire after the loop was seen.
    //   postexit_*       — depth-32 ring of the 32 retires AFTER the
    //                      exit, so we see what the BIOS does next.
    logic        loop_seen;
    logic        loop_first_seen_cyc_lat;
    int          loop_first_cyc;
    int          loop_first_retires;

    logic        loop_exit_captured;
    int          loop_exit_cyc;
    int          loop_exit_retires;
    logic [31:0] loop_exit_pc, loop_exit_instr;
    logic [31:0] loop_exit_r2, loop_exit_r3, loop_exit_r4, loop_exit_r5;
    logic [31:0] loop_exit_r6, loop_exit_r7, loop_exit_r8, loop_exit_r9;
    logic [31:0] loop_exit_r10, loop_exit_r31;
    logic [31:0] loop_exit_ea_min, loop_exit_ea_max;
    int          loop_exit_total_in_range;

    localparam int POSTEXIT_DEPTH = 32;
    int          postexit_count;
    int          postexit_cyc       [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_pc        [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_instr     [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_r2        [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_r4        [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_r5        [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_r31       [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_arg2      [0:POSTEXIT_DEPTH-1];
    logic [31:0] postexit_arg3      [0:POSTEXIT_DEPTH-1];

    initial begin
        loop_ring_count    = 0;
        loop_ring_head     = 0;
        loop_count_4564    = 0;
        loop_count_45a0    = 0;
        loop_load_count    = 0;
        loop_ea_seen       = 1'b0;
        loop_first_seen    = 1'b0;
        loop_branch_count  = 0;
        last_branch_seen   = 1'b0;
        loop_seen          = 1'b0;
        loop_first_seen_cyc_lat = 1'b0;
        loop_exit_captured = 1'b0;
        postexit_count     = 0;
        for (int i = 0; i < POSTEXIT_DEPTH; i = i + 1) begin
            postexit_cyc[i]   = 0;
            postexit_pc[i]    = 32'd0;
            postexit_instr[i] = 32'd0;
            postexit_r2[i]    = 32'd0;
            postexit_r4[i]    = 32'd0;
            postexit_r5[i]    = 32'd0;
            postexit_r31[i]   = 32'd0;
            postexit_arg2[i]  = 32'd0;
            postexit_arg3[i]  = 32'd0;
        end
        for (int i = 0; i < LOOP_RING_DEPTH; i = i + 1) begin
            loop_ring_cyc[i]     = 0;
            loop_ring_retires[i] = 0;
            loop_ring_pc[i]      = 32'd0;
            loop_ring_instr[i]   = 32'd0;
            loop_ring_r2[i]      = 32'd0;
            loop_ring_r3[i]      = 32'd0;
            loop_ring_r4[i]      = 32'd0;
            loop_ring_r5[i]      = 32'd0;
            loop_ring_r6[i]      = 32'd0;
            loop_ring_r7[i]      = 32'd0;
            loop_ring_r8[i]      = 32'd0;
            loop_ring_r9[i]      = 32'd0;
            loop_ring_r10[i]     = 32'd0;
            loop_ring_r31[i]     = 32'd0;
            loop_ring_arg2[i]    = 32'd0;
            loop_ring_arg3[i]    = 32'd0;
        end
    end

    logic        loop_pc_in_range;
    logic [5:0]  ch68_op;
    logic [5:0]  ch68_func;
    logic        ch68_is_load;
    logic        ch68_is_branch;
    assign loop_pc_in_range = (u_core.retired_pc >= LOOP_PC_LO) &&
                              (u_core.retired_pc <  LOOP_PC_HI);
    assign ch68_op   = u_core.retired_instr[31:26];
    assign ch68_func = u_core.retired_instr[5:0];
    assign ch68_is_load =
        (ch68_op == 6'h20) || (ch68_op == 6'h21) ||
        (ch68_op == 6'h23) || (ch68_op == 6'h24) ||
        (ch68_op == 6'h25);
    assign ch68_is_branch =
        (ch68_op == 6'h01) || (ch68_op == 6'h04) ||
        (ch68_op == 6'h05) || (ch68_op == 6'h06) ||
        (ch68_op == 6'h07);

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid && loop_pc_in_range) begin
            // Ch71: gate the depth-32 per-field LOOP_RING update — the
            // single biggest observer overhead in the long run. The
            // long-run target only needs LOOP_SUMMARY counts, EA min/
            // max, progression deltas, and last-branch snapshot, all
            // of which are kept below.
            `ifndef CH71_LONG_RUN
            loop_ring_cyc[loop_ring_head]      <= sim_cycle_ctr;
            loop_ring_retires[loop_ring_head]  <= core_retired_events;
            loop_ring_pc[loop_ring_head]       <= u_core.retired_pc;
            loop_ring_instr[loop_ring_head]    <= u_core.retired_instr;
            loop_ring_r2[loop_ring_head]       <= u_core.regfile[2];
            loop_ring_r3[loop_ring_head]       <= u_core.regfile[3];
            loop_ring_r4[loop_ring_head]       <= u_core.regfile[4];
            loop_ring_r5[loop_ring_head]       <= u_core.regfile[5];
            loop_ring_r6[loop_ring_head]       <= u_core.regfile[6];
            loop_ring_r7[loop_ring_head]       <= u_core.regfile[7];
            loop_ring_r8[loop_ring_head]       <= u_core.regfile[8];
            loop_ring_r9[loop_ring_head]       <= u_core.regfile[9];
            loop_ring_r10[loop_ring_head]      <= u_core.regfile[10];
            loop_ring_r31[loop_ring_head]      <= u_core.regfile[31];
            loop_ring_arg2[loop_ring_head]     <= u_core.retired_arg2;
            loop_ring_arg3[loop_ring_head]     <= u_core.retired_arg3;
            loop_ring_head  <= (loop_ring_head + 1) % LOOP_RING_DEPTH;
            `endif
            loop_ring_count <= loop_ring_count + 1;

            if (u_core.retired_pc == LOOP_PC_4564)
                loop_count_4564 <= loop_count_4564 + 1;
            if (u_core.retired_pc == LOOP_PC_45A0)
                loop_count_45a0 <= loop_count_45a0 + 1;

            if (!loop_first_seen) begin
                loop_first_seen <= 1'b1;
                r4_first        <= u_core.regfile[4];
                r5_first        <= u_core.regfile[5];
                r6_first        <= u_core.regfile[6];
            end
            r4_last <= u_core.regfile[4];
            r5_last <= u_core.regfile[5];
            r6_last <= u_core.regfile[6];

            if (ch68_is_load) begin
                loop_load_count <= loop_load_count + 1;
                if (!loop_ea_seen) begin
                    loop_ea_seen <= 1'b1;
                    loop_ea_min  <= u_core.retired_arg2;
                    loop_ea_max  <= u_core.retired_arg2;
                end else begin
                    if (u_core.retired_arg2 < loop_ea_min)
                        loop_ea_min <= u_core.retired_arg2;
                    if (u_core.retired_arg2 > loop_ea_max)
                        loop_ea_max <= u_core.retired_arg2;
                end
            end

            if (ch68_is_branch) begin
                loop_branch_count   <= loop_branch_count + 1;
                last_branch_seen    <= 1'b1;
                last_branch_cyc     <= sim_cycle_ctr;
                last_branch_pc      <= u_core.retired_pc;
                last_branch_instr   <= u_core.retired_instr;
                last_branch_rs_idx  <= u_core.retired_instr[25:21];
                last_branch_rt_idx  <= u_core.retired_instr[20:16];
                last_branch_rs_val  <= u_core.regfile[u_core.retired_instr[25:21]];
                last_branch_rt_val  <= u_core.regfile[u_core.retired_instr[20:16]];
            end

            // Ch69 — first-in-range latch
            loop_seen <= 1'b1;
            if (!loop_first_seen_cyc_lat) begin
                loop_first_seen_cyc_lat <= 1'b1;
                loop_first_cyc          <= sim_cycle_ctr;
                loop_first_retires      <= core_retired_events;
            end
        end

        // Ch69 — exit + post-exit ring. Conditions evaluated outside
        // loop_pc_in_range guard so we catch the first OUT-of-range
        // retire after the loop has been seen.
        if (rst_n && core_ev_valid && loop_seen && !loop_pc_in_range) begin
            if (!loop_exit_captured) begin
                loop_exit_captured       <= 1'b1;
                loop_exit_cyc            <= sim_cycle_ctr;
                loop_exit_retires        <= core_retired_events;
                loop_exit_pc             <= u_core.retired_pc;
                loop_exit_instr          <= u_core.retired_instr;
                loop_exit_r2             <= u_core.regfile[2];
                loop_exit_r3             <= u_core.regfile[3];
                loop_exit_r4             <= u_core.regfile[4];
                loop_exit_r5             <= u_core.regfile[5];
                loop_exit_r6             <= u_core.regfile[6];
                loop_exit_r7             <= u_core.regfile[7];
                loop_exit_r8             <= u_core.regfile[8];
                loop_exit_r9             <= u_core.regfile[9];
                loop_exit_r10            <= u_core.regfile[10];
                loop_exit_r31            <= u_core.regfile[31];
                loop_exit_ea_min         <= loop_ea_min;
                loop_exit_ea_max         <= loop_ea_max;
                loop_exit_total_in_range <= loop_ring_count;
            end else if (postexit_count < POSTEXIT_DEPTH) begin
                postexit_cyc[postexit_count]   <= sim_cycle_ctr;
                postexit_pc[postexit_count]    <= u_core.retired_pc;
                postexit_instr[postexit_count] <= u_core.retired_instr;
                postexit_r2[postexit_count]    <= u_core.regfile[2];
                postexit_r4[postexit_count]    <= u_core.regfile[4];
                postexit_r5[postexit_count]    <= u_core.regfile[5];
                postexit_r31[postexit_count]   <= u_core.regfile[31];
                postexit_arg2[postexit_count]  <= u_core.retired_arg2;
                postexit_arg3[postexit_count]  <= u_core.retired_arg3;
                postexit_count                 <= postexit_count + 1;
            end
        end
    end

    // (C) TRAP_SNAPSHOT at first retire with pc == 0x00400000
    logic        trap_snapshot_captured;
    int          trap_snapshot_cyc;
    int          trap_snapshot_retires;
    logic [31:0] trap_snapshot_r2;
    logic [31:0] trap_snapshot_r4;
    logic [31:0] trap_snapshot_r5;
    logic [31:0] trap_snapshot_r6;
    logic [31:0] trap_snapshot_r7;
    logic [31:0] trap_snapshot_r8;
    logic [31:0] trap_snapshot_r9;
    logic [31:0] trap_snapshot_r10;
    logic [31:0] trap_snapshot_r26;
    logic [31:0] trap_snapshot_r27;
    logic [31:0] trap_snapshot_r28;
    logic [31:0] trap_snapshot_r29;
    logic [31:0] trap_snapshot_r30;
    logic [31:0] trap_snapshot_r31;
    logic [31:0] trap_snapshot_prev_pc;
    logic [31:0] trap_snapshot_prev_instr;
    int          trap_snapshot_prev_cyc;

    initial trap_snapshot_captured = 1'b0;

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid &&
            u_core.retired_pc == TRAP_TARGET &&
            !trap_snapshot_captured) begin
            trap_snapshot_captured   <= 1'b1;
            trap_snapshot_cyc        <= sim_cycle_ctr;
            trap_snapshot_retires    <= core_retired_events;
            trap_snapshot_r2         <= u_core.regfile[2];
            trap_snapshot_r4         <= u_core.regfile[4];
            trap_snapshot_r5         <= u_core.regfile[5];
            trap_snapshot_r6         <= u_core.regfile[6];
            trap_snapshot_r7         <= u_core.regfile[7];
            trap_snapshot_r8         <= u_core.regfile[8];
            trap_snapshot_r9         <= u_core.regfile[9];
            trap_snapshot_r10        <= u_core.regfile[10];
            trap_snapshot_r26        <= u_core.regfile[26];
            trap_snapshot_r27        <= u_core.regfile[27];
            trap_snapshot_r28        <= u_core.regfile[28];
            trap_snapshot_r29        <= u_core.regfile[29];
            trap_snapshot_r30        <= u_core.regfile[30];
            trap_snapshot_r31        <= u_core.regfile[31];
            trap_snapshot_prev_pc    <= retire_ring_pc[ring_prev_idx];
            trap_snapshot_prev_instr <= retire_ring_instr[ring_prev_idx];
            trap_snapshot_prev_cyc   <= retire_ring_cyc[ring_prev_idx];
        end
    end

    always_ff @(posedge clk) begin
        if (rst_n && core_ev_valid) begin
            // --- Outcome-update phase on the currently-active entry ---
            if (disp_active_idx >= 0) begin
                if (u_core.retired_pc == 32'h0040_0000) begin
                    disp_log_outcome[disp_active_idx]     <= OUT_TRAP_400000;
                    disp_log_outcome_cyc[disp_active_idx] <= sim_cycle_ctr;
                    disp_active_idx                        <= -1;
                end else if (is_dispatch_entry) begin
                    // New dispatch starts this cycle — previous rolls up
                    // to NEXT_DISPATCH with detail = new seq (the slot
                    // below will assign disp_log_count to the new entry
                    // before incrementing).
                    disp_log_outcome[disp_active_idx]        <= OUT_NEXT_DISPATCH;
                    disp_log_outcome_cyc[disp_active_idx]    <= sim_cycle_ctr;
                    disp_log_outcome_detail[disp_active_idx] <= disp_log_count;
                    // disp_active_idx will be overwritten in the
                    // allocation block below.
                end else if ((core_retired_events - disp_log_entry_retires[disp_active_idx]) >= DISPATCH_WINDOW) begin
                    disp_log_outcome[disp_active_idx]     <= OUT_LONG_RUN;
                    disp_log_outcome_cyc[disp_active_idx] <= sim_cycle_ctr;
                    disp_active_idx                        <= -1;
                end
            end

            // --- New-dispatch allocation phase ---
            if (is_dispatch_entry) begin
                disp_log_cyc[disp_log_head]             <= sim_cycle_ctr;
                disp_log_entry_retires[disp_log_head]   <= core_retired_events;
                disp_log_seq[disp_log_head]             <= disp_log_count;
                disp_log_entry_pc[disp_log_head]        <= slot_pc_now;
                disp_log_r2[disp_log_head]              <= u_core.regfile[2];
                disp_log_r4[disp_log_head]              <= u_core.regfile[4];
                disp_log_r5[disp_log_head]              <= u_core.regfile[5];
                disp_log_r6[disp_log_head]              <= u_core.regfile[6];
                disp_log_r7[disp_log_head]              <= u_core.regfile[7];
                disp_log_r9[disp_log_head]              <= u_core.regfile[9];
                disp_log_r31[disp_log_head]             <= u_core.regfile[31];
                disp_log_outcome[disp_log_head]         <= OUT_IN_PROGRESS;
                disp_log_outcome_cyc[disp_log_head]     <= 0;
                disp_log_outcome_detail[disp_log_head]  <= 0;

                disp_active_idx <= disp_log_head;
                disp_log_head   <= (disp_log_head + 1) % DISPATCH_LOG_DEPTH;
                disp_log_count  <= disp_log_count + 1;
            end
        end
    end

`ifdef CH264_CALLEE_AUTOPSY
    // ==============================================================
    // Ch264 — narrow callee-read autopsy. Codex's framing: scope the
    // observer to the longjmp-return-handler callee body
    // (0xBFC52984..0xBFC52A04 — the JAL target Ch217 already decoded),
    // capture every NON-FETCH data read across >=2 passes, sample the
    // ACTUAL returned value from u_ee_map.ee_rd_data (the trace
    // event's ev_arg1 is hardcoded zero for EV_READ — known since
    // Ch258), and tag with the region via ev_arg3.
    //
    // Verdicts:
    //   callee_static_mmio_gate_found   — repeated stable read in EE MMIO
    //   callee_static_ram_gate_found    — repeated stable read in EE RAM
    //   callee_no_data_reads            — captured zero non-fetch reads
    //   callee_reads_vary_but_flow_static — reads observed but data varies
    // ==============================================================

    localparam logic [31:0] CH264_CALLEE_LO = 32'hBFC5_2984;
    localparam logic [31:0] CH264_CALLEE_HI = 32'hBFC5_2A04;

    localparam int CH264_DEPTH = 512;
    int          ch264_count;
    int          ch264_pass_idx [0:CH264_DEPTH-1];
    logic [31:0] ch264_pc       [0:CH264_DEPTH-1];
    logic [31:0] ch264_ea       [0:CH264_DEPTH-1];
    logic [31:0] ch264_data     [0:CH264_DEPTH-1];
    logic [7:0]  ch264_region   [0:CH264_DEPTH-1];

    wire ch264_pc_in_callee = (core_pc >= CH264_CALLEE_LO)
                           && (core_pc <  CH264_CALLEE_HI);
    wire ch264_is_fetch     = (ee_map_ev_arg0[31:0] == core_pc);

    // Sample the live ee_rd_data on the cycle the EV_READ event fires
    // (= the cycle the data arrives at the core's input). Trace event
    // fires one cycle after the request; on that cycle ee_rd_data is
    // the multiplexed response from ram_rd_data / bios_rd_data /
    // intc_rd_data / etc. depending on the region of the request.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch264_count <= 0;
        end else if (ee_map_ev_valid
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ee_map_ev_event  == trace_pkg::EV_READ
                  && ch264_pc_in_callee
                  && !ch264_is_fetch
                  && ch264_count < CH264_DEPTH) begin
            ch264_pass_idx[ch264_count] <= ch217_count;
            ch264_pc      [ch264_count] <= core_pc;
            ch264_ea      [ch264_count] <= ee_map_ev_arg0[31:0];
            ch264_data    [ch264_count] <= ee_rd_data;
            ch264_region  [ch264_count] <= ee_map_ev_arg3[7:0];
            ch264_count <= ch264_count + 1;
        end
    end

    task automatic ch264_region_name(input logic [7:0] r,
                                     output string s);
        case (r)
            8'd0:    s = "BIOS_ROM";
            8'd1:    s = "EE_RAM";
            8'd7:    s = "EE_DMAC";
            8'd8:    s = "EE_INTC";
            8'd9:    s = "EE_MISC_MMIO";
            8'd10:   s = "EE_BIU";
            8'd11:   s = "USEG_SHADOW";
            8'd12:   s = "EE_GS_PRIV";
            8'hFF:   s = "UNMAPPED";
            default: s = $sformatf("REGION_%0d", r);
        endcase
    endtask

    task automatic ch264_print_autopsy();
        // Print full ordered transaction stream + repeated-stable-reads
        // table + verdict.
        int n;
        string rname;
        // Build a uniqueness table over EA — count per EA, track
        // pass-set coverage, track all observed data values to
        // determine "static" vs "varies."
        // Max distinct EAs bounded by CH264_DEPTH.
        int          uniq_count;
        logic [31:0] uniq_ea       [0:CH264_DEPTH-1];
        int          uniq_hits     [0:CH264_DEPTH-1];
        int          uniq_passes   [0:CH264_DEPTH-1];   // bitmask of which Ch217 passes saw this EA
        logic [31:0] uniq_first_d  [0:CH264_DEPTH-1];
        logic        uniq_data_var [0:CH264_DEPTH-1];
        logic [7:0]  uniq_region   [0:CH264_DEPTH-1];
        int          j;
        logic        found;
        // Verdict accumulators
        logic [31:0] stable_addr;
        logic [31:0] stable_data;
        logic [7:0]  stable_region;
        int          stable_hits;
        logic        stable_found;
        string       verdict;

        n = ch264_count;
        $display("[ch264] CALLEE_BODY_DATA_READS scope=0x%08h..0x%08h captured=%0d (cap=%0d)",
                 CH264_CALLEE_LO, CH264_CALLEE_HI, n, CH264_DEPTH);

        if (n == 0) begin
            $display("[ch264] verdict=callee_no_data_reads (the callee body issues no non-fetch reads; gate must be elsewhere or the callee is a no-op)");
        end else begin
            for (int i = 0; i < n; i++) begin
                ch264_region_name(ch264_region[i], rname);
                $display("[ch264]   [%0d] pass=%0d pc=0x%08h ea=0x%08h data=0x%08h region=%s",
                         i, ch264_pass_idx[i], ch264_pc[i], ch264_ea[i],
                         ch264_data[i], rname);
            end

            // Build dedup table.
            uniq_count = 0;
            for (int i = 0; i < n; i++) begin
                found = 1'b0;
                for (j = 0; j < uniq_count; j++) begin
                    if (uniq_ea[j] === ch264_ea[i]) begin
                        found = 1'b1;
                        uniq_hits[j]   = uniq_hits[j] + 1;
                        uniq_passes[j] = uniq_passes[j] | (1 << ch264_pass_idx[i]);
                        if (ch264_data[i] !== uniq_first_d[j])
                            uniq_data_var[j] = 1'b1;
                    end
                end
                if (!found) begin
                    uniq_ea[uniq_count]       = ch264_ea[i];
                    uniq_hits[uniq_count]     = 1;
                    uniq_passes[uniq_count]   = (1 << ch264_pass_idx[i]);
                    uniq_first_d[uniq_count]  = ch264_data[i];
                    uniq_data_var[uniq_count] = 1'b0;
                    uniq_region[uniq_count]   = ch264_region[i];
                    uniq_count                = uniq_count + 1;
                end
            end

            // Print the dedup table — show every distinct EA seen.
            $display("[ch264] TOP_DISTINCT_EAs (count=%0d):", uniq_count);
            for (int i = 0; i < uniq_count; i++) begin
                ch264_region_name(uniq_region[i], rname);
                $display("[ch264]   ea=0x%08h  hits=%0d  passes=0x%04h  data=0x%08h  data_varies=%0b  region=%s",
                         uniq_ea[i], uniq_hits[i], uniq_passes[i],
                         uniq_first_d[i], uniq_data_var[i], rname);
            end

            // Pick the "stable" reader: appears in >=2 passes AND data
            // doesn't vary. Tie-break: highest hits.
            stable_found  = 1'b0;
            stable_hits   = 0;
            stable_addr   = 32'd0;
            stable_data   = 32'd0;
            stable_region = 8'hFF;
            for (int i = 0; i < uniq_count; i++) begin
                int npasses;
                npasses = 0;
                for (int b = 0; b < 16; b++)
                    if ((uniq_passes[i] >> b) & 1) npasses = npasses + 1;
                if (npasses >= 2 && !uniq_data_var[i] && uniq_hits[i] > stable_hits) begin
                    stable_found  = 1'b1;
                    stable_addr   = uniq_ea[i];
                    stable_data   = uniq_first_d[i];
                    stable_region = uniq_region[i];
                    stable_hits   = uniq_hits[i];
                end
            end

            if (!stable_found) begin
                verdict = $sformatf("callee_reads_vary_but_flow_static (captured %0d reads across %0d distinct EAs — every multi-pass EA has varying data; the callee is reading SOMETHING but no single EA holds a stable value the gate could lock onto)",
                                    n, uniq_count);
            end else begin
                ch264_region_name(stable_region, rname);
                case (stable_region)
                    8'd1, 8'd11: begin  // EE_RAM, USEG_SHADOW
                        verdict = $sformatf("callee_static_ram_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits / multiple passes — region=%s — this is the polled gate, Ch265 should model what writes here)",
                                            stable_addr, stable_data, stable_hits, rname);
                    end
                    8'd7, 8'd8, 8'd9, 8'd10, 8'd12: begin  // any MMIO
                        verdict = $sformatf("callee_static_mmio_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits / multiple passes — region=%s — this is the polled gate, Ch265 should model the device behind it)",
                                            stable_addr, stable_data, stable_hits, rname);
                    end
                    8'd0: begin  // BIOS_ROM
                        verdict = $sformatf("callee_static_ram_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=BIOS_ROM — likely a BIOS-internal constant the callee uses as data, not a polled state. Treat as: callee reads only constants.)",
                                            stable_addr, stable_data, stable_hits);
                    end
                    default: begin
                        verdict = $sformatf("callee_static_mmio_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=%s)",
                                            stable_addr, stable_data, stable_hits, rname);
                    end
                endcase
            end
            $display("[ch264] verdict=%s", verdict);
        end
    endtask
`endif // CH264_CALLEE_AUTOPSY

`ifdef CH265_HELPER_AUTOPSY
    // ==============================================================
    // Ch265 — helper-body autopsy (one frame deeper than Ch264). The
    // Ch264 result showed the longjmp-return callee at
    // 0xBFC52984..0xBFC52A04 is a pure thunk to 0xBFC4D370(0x0F).
    // Ch265 re-aims the same observation pattern at the helper:
    //
    //   data_reads — non-fetch reads in the helper PC window, with
    //     EA + returned value + region tag (same as Ch264)
    //   cf_outs    — JAL/J/JR/JALR retires inside the helper, with
    //     statically-decoded target for JAL/J (dynamic target left 0)
    //   pass_io    — per-helper-invocation $a0 at entry + $v0/$v1
    //     captured at exit (when execution returns to the callee at
    //     0xBFC52998, the instruction after the JAL)
    //
    // Verdicts:
    //   helper_static_mmio_gate_found   — repeated stable read in EE MMIO
    //   helper_static_ram_gate_found    — repeated stable read in EE RAM
    //   helper_no_data_reads            — captured zero non-fetch reads
    //   helper_is_thunk                 — no/only-stack reads + one JAL out
    //   helper_reads_vary_but_flow_static — reads observed but data varies
    // ==============================================================

    localparam logic [31:0] CH265_HELPER_LO     = 32'hBFC4_D370;
    localparam logic [31:0] CH265_HELPER_HI     = 32'hBFC4_D470;
    localparam logic [31:0] CH265_CALLEE_RETURN = 32'hBFC5_2998; // PC after Ch264 callee's JAL

    localparam int CH265_DEPTH = 512;
    int          ch265_count;
    int          ch265_pass_idx [0:CH265_DEPTH-1];
    logic [31:0] ch265_pc       [0:CH265_DEPTH-1];
    logic [31:0] ch265_ea       [0:CH265_DEPTH-1];
    logic [31:0] ch265_data     [0:CH265_DEPTH-1];
    logic [7:0]  ch265_region   [0:CH265_DEPTH-1];

    wire ch265_pc_in_helper = (core_pc >= CH265_HELPER_LO)
                           && (core_pc <  CH265_HELPER_HI);
    wire ch265_is_fetch     = (ee_map_ev_arg0[31:0] == core_pc);

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch265_count <= 0;
        end else if (ee_map_ev_valid
                  && ee_map_ev_event  == trace_pkg::EV_READ
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ch265_pc_in_helper
                  && !ch265_is_fetch
                  && ch265_count < CH265_DEPTH) begin
            ch265_pass_idx[ch265_count] <= ch217_count;
            ch265_pc      [ch265_count] <= core_pc;
            ch265_ea      [ch265_count] <= ee_map_ev_arg0[31:0];
            ch265_data    [ch265_count] <= ee_rd_data;
            ch265_region  [ch265_count] <= ee_map_ev_arg3[7:0];
            ch265_count <= ch265_count + 1;
        end
    end

    // Per-invocation $a0-in / $v0-out / $v1-out capture. Mirrors the
    // Ch217 pending-pair pattern: entry on retire of 0xBFC4D370, exit
    // when control returns to 0xBFC52998 (the callee's post-JAL
    // instruction).
    localparam int CH265_PASSES = 16;
    int          ch265_pass_count;
    int          ch265_pass_cyc      [0:CH265_PASSES-1];
    logic [31:0] ch265_pass_a0_in    [0:CH265_PASSES-1];
    logic [31:0] ch265_pass_ra_in    [0:CH265_PASSES-1];
    logic [31:0] ch265_pass_v0_post  [0:CH265_PASSES-1];
    logic [31:0] ch265_pass_v1_post  [0:CH265_PASSES-1];
    int          ch265_pass_pending_idx;
    logic        ch265_pass_pending;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch265_pass_count       <= 0;
            ch265_pass_pending     <= 1'b0;
            ch265_pass_pending_idx <= 0;
        end else if (core_ev_valid) begin
            if (u_core.retired_pc == CH265_HELPER_LO
                    && ch265_pass_count < CH265_PASSES) begin
                ch265_pass_cyc  [ch265_pass_count] <= sim_cycle_ctr;
                ch265_pass_a0_in[ch265_pass_count] <= u_core.regfile[4];
                ch265_pass_ra_in[ch265_pass_count] <= u_core.regfile[31];
                ch265_pass_pending_idx <= ch265_pass_count;
                ch265_pass_pending     <= 1'b1;
                ch265_pass_count       <= ch265_pass_count + 1;
            end
            if (ch265_pass_pending
                    && u_core.retired_pc == CH265_CALLEE_RETURN) begin
                ch265_pass_v0_post[ch265_pass_pending_idx] <= u_core.regfile[2];
                ch265_pass_v1_post[ch265_pass_pending_idx] <= u_core.regfile[3];
                ch265_pass_pending <= 1'b0;
            end
        end
    end

    // Control-flow capture: retires of JAL/J/JR/JALR whose retired_pc
    // lies inside the helper window. peek_instr is pure (no side
    // effects) so it can be invoked at capture time.
    localparam int CH265_CF_DEPTH = 64;
    int          ch265_cf_count;
    int          ch265_cf_pass    [0:CH265_CF_DEPTH-1];
    logic [31:0] ch265_cf_pc      [0:CH265_CF_DEPTH-1];
    logic [31:0] ch265_cf_instr   [0:CH265_CF_DEPTH-1];
    logic [31:0] ch265_cf_target  [0:CH265_CF_DEPTH-1]; // static target for J/JAL; 0 for JR/JALR

    function automatic logic ch265_is_cf_opcode(input logic [31:0] instr);
        logic [5:0] op, funct;
        op    = instr[31:26];
        funct = instr[5:0];
        ch265_is_cf_opcode = (op == 6'b000010)                       // J
                          || (op == 6'b000011)                       // JAL
                          || (op == 6'b000000 && funct == 6'b001000) // SPECIAL/JR
                          || (op == 6'b000000 && funct == 6'b001001);// SPECIAL/JALR
    endfunction

    function automatic logic [31:0] ch265_jtarget(input logic [31:0] pc,
                                                  input logic [31:0] instr);
        // J/JAL: target = {(pc+4)[31:28], instr[25:0], 2'b00}
        logic [31:0] dslot;
        dslot = pc + 32'd4;
        ch265_jtarget = { dslot[31:28], instr[25:0], 2'b00 };
    endfunction

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch265_cf_count <= 0;
        end else if (core_ev_valid
                  && u_core.retired_pc >= CH265_HELPER_LO
                  && u_core.retired_pc <  CH265_HELPER_HI
                  && ch265_is_cf_opcode(peek_instr(u_core.retired_pc))
                  && ch265_cf_count < CH265_CF_DEPTH) begin
            logic [31:0] ri;
            logic [5:0]  op;
            ri = peek_instr(u_core.retired_pc);
            op = ri[31:26];
            ch265_cf_pass [ch265_cf_count] <= ch265_pass_count;
            ch265_cf_pc   [ch265_cf_count] <= u_core.retired_pc;
            ch265_cf_instr[ch265_cf_count] <= ri;
            if (op == 6'b000010 || op == 6'b000011)
                ch265_cf_target[ch265_cf_count] <= ch265_jtarget(u_core.retired_pc, ri);
            else
                ch265_cf_target[ch265_cf_count] <= 32'h0;
            ch265_cf_count <= ch265_cf_count + 1;
        end
    end

    task automatic ch265_region_name(input logic [7:0] r,
                                     output string name);
        case (r)
            8'd0:  name = "BIOS_ROM";
            8'd1:  name = "EE_RAM";
            8'd2:  name = "EE_DMAC";
            8'd3:  name = "EE_INTC";
            8'd4:  name = "EE_MISC_MMIO";
            8'd5:  name = "EE_BIU";
            8'd6:  name = "USEG_SHADOW";
            8'd7:  name = "EE_GS_PRIV";
            8'd8:  name = "UNMAPPED";
            default: name = $sformatf("R%0d", r);
        endcase
    endtask

    function automatic string ch265_cf_mnemonic(input logic [31:0] instr);
        logic [5:0] op, funct;
        op    = instr[31:26];
        funct = instr[5:0];
        if (op == 6'b000010)                                 return "j";
        else if (op == 6'b000011)                            return "jal";
        else if (op == 6'b000000 && funct == 6'b001000)      return "jr";
        else if (op == 6'b000000 && funct == 6'b001001)      return "jalr";
        else                                                 return "?";
    endfunction

    task automatic ch265_print_autopsy();
        int n;
        string rname;
        int    uniq_count;
        logic [31:0] uniq_ea       [0:CH265_DEPTH-1];
        int          uniq_hits     [0:CH265_DEPTH-1];
        int          uniq_passes   [0:CH265_DEPTH-1];
        logic [31:0] uniq_first_d  [0:CH265_DEPTH-1];
        logic        uniq_data_var [0:CH265_DEPTH-1];
        logic [7:0]  uniq_region   [0:CH265_DEPTH-1];
        int          j;
        logic        found;
        logic [31:0] stable_addr;
        logic [31:0] stable_data;
        logic [7:0]  stable_region;
        int          stable_hits;
        logic        stable_found;
        string       verdict;
        string       mnem;
        int          jal_out_count;
        logic [31:0] last_jal_target;
        logic        any_stack_only_reads;
        int          non_stack_read_count;

        // Pass IO table — name what was asked + what was answered.
        $display("[ch265] HELPER_PASSES scope=0x%08h count=%0d (cap=%0d):",
                 CH265_HELPER_LO, ch265_pass_count, CH265_PASSES);
        for (int i = 0; i < ch265_pass_count; i++) begin
            $display("[ch265]   [%0d] cyc=%0d $a0_in=0x%08h $ra_in=0x%08h $v0_post=0x%08h $v1_post=0x%08h",
                     i, ch265_pass_cyc[i], ch265_pass_a0_in[i],
                     ch265_pass_ra_in[i], ch265_pass_v0_post[i],
                     ch265_pass_v1_post[i]);
        end

        // Control-flow table — calls / jumps that leave or branch
        // within the helper. JAL targets to OUTSIDE the helper window
        // are flagged explicitly.
        jal_out_count   = 0;
        last_jal_target = 32'd0;
        $display("[ch265] HELPER_CONTROL_FLOW captured=%0d (cap=%0d):",
                 ch265_cf_count, CH265_CF_DEPTH);
        for (int i = 0; i < ch265_cf_count; i++) begin
            string note;
            mnem = ch265_cf_mnemonic(ch265_cf_instr[i]);
            note = "";
            if (mnem == "jal" || mnem == "j") begin
                if (ch265_cf_target[i] <  CH265_HELPER_LO
                 || ch265_cf_target[i] >= CH265_HELPER_HI) begin
                    note = "  <-- LEAVES helper";
                    if (mnem == "jal") begin
                        jal_out_count = jal_out_count + 1;
                        last_jal_target = ch265_cf_target[i];
                    end
                end
            end
            $display("[ch265]   [%0d] pass=%0d pc=0x%08h instr=0x%08h %s target=0x%08h%s",
                     i, ch265_cf_pass[i], ch265_cf_pc[i],
                     ch265_cf_instr[i], mnem, ch265_cf_target[i], note);
        end

        // Data-read autopsy (mirror of Ch264).
        n = ch265_count;
        $display("[ch265] HELPER_BODY_DATA_READS scope=0x%08h..0x%08h captured=%0d (cap=%0d)",
                 CH265_HELPER_LO, CH265_HELPER_HI, n, CH265_DEPTH);

        if (n == 0) begin
            verdict = "helper_no_data_reads";
            if (jal_out_count == 1) begin
                verdict = $sformatf("helper_is_thunk (no non-fetch data reads in body; one JAL out to 0x%08h — recurse autopsy one more frame)",
                                    last_jal_target);
            end else if (jal_out_count == 0) begin
                verdict = "helper_no_data_reads (helper computes from registers only — check what populates $a0=0x0F and whether $v0 actually depends on memory)";
            end
            $display("[ch265] verdict=%s", verdict);
        end else begin
            for (int i = 0; i < n; i++) begin
                ch265_region_name(ch265_region[i], rname);
                $display("[ch265]   [%0d] pass=%0d pc=0x%08h ea=0x%08h data=0x%08h region=%s",
                         i, ch265_pass_idx[i], ch265_pc[i], ch265_ea[i],
                         ch265_data[i], rname);
            end

            // Build dedup table.
            uniq_count = 0;
            for (int i = 0; i < n; i++) begin
                found = 1'b0;
                for (j = 0; j < uniq_count; j++) begin
                    if (uniq_ea[j] === ch265_ea[i]) begin
                        found = 1'b1;
                        uniq_hits[j]   = uniq_hits[j] + 1;
                        uniq_passes[j] = uniq_passes[j] | (1 << ch265_pass_idx[i]);
                        if (ch265_data[i] !== uniq_first_d[j])
                            uniq_data_var[j] = 1'b1;
                    end
                end
                if (!found) begin
                    uniq_ea[uniq_count]       = ch265_ea[i];
                    uniq_hits[uniq_count]     = 1;
                    uniq_passes[uniq_count]   = (1 << ch265_pass_idx[i]);
                    uniq_first_d[uniq_count]  = ch265_data[i];
                    uniq_data_var[uniq_count] = 1'b0;
                    uniq_region[uniq_count]   = ch265_region[i];
                    uniq_count                = uniq_count + 1;
                end
            end

            $display("[ch265] TOP_DISTINCT_EAs (count=%0d):", uniq_count);
            for (int i = 0; i < uniq_count; i++) begin
                ch265_region_name(uniq_region[i], rname);
                $display("[ch265]   ea=0x%08h  hits=%0d  passes=0x%04h  data=0x%08h  data_varies=%0b  region=%s",
                         uniq_ea[i], uniq_hits[i], uniq_passes[i],
                         uniq_first_d[i], uniq_data_var[i], rname);
            end

            // Classify "stack-only" — every distinct EA looks like a
            // stack location (matches $sp neighborhood of an entry).
            // We approximate by checking if EA is within 0x40 of any
            // captured $a0_in's $sp companion. Simpler: if ALL reads
            // are EE_RAM and all EAs are within 0x40 of each other and
            // 0x40 of the first, treat as stack-only.
            non_stack_read_count = 0;
            for (int i = 0; i < uniq_count; i++) begin
                logic [31:0] delta;
                delta = (uniq_ea[i] > uniq_ea[0])
                            ? (uniq_ea[i] - uniq_ea[0])
                            : (uniq_ea[0] - uniq_ea[i]);
                if (delta > 32'h40 || uniq_region[i] != 8'd1) begin
                    non_stack_read_count = non_stack_read_count + 1;
                end
            end
            any_stack_only_reads = (non_stack_read_count == 0);

            // Pick the "stable" reader: appears in >=2 passes AND data
            // doesn't vary. Tie-break: highest hits.
            stable_found  = 1'b0;
            stable_hits   = 0;
            stable_addr   = 32'd0;
            stable_data   = 32'd0;
            stable_region = 8'hFF;
            for (int i = 0; i < uniq_count; i++) begin
                int npasses;
                npasses = 0;
                for (int b = 0; b < 16; b++)
                    if ((uniq_passes[i] >> b) & 1) npasses = npasses + 1;
                if (npasses >= 2 && !uniq_data_var[i] && uniq_hits[i] > stable_hits) begin
                    stable_found  = 1'b1;
                    stable_addr   = uniq_ea[i];
                    stable_data   = uniq_first_d[i];
                    stable_region = uniq_region[i];
                    stable_hits   = uniq_hits[i];
                end
            end

            if (stable_found) begin
                ch265_region_name(stable_region, rname);
                case (stable_region)
                    8'd1, 8'd6: begin
                        verdict = $sformatf("helper_static_ram_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=%s — Ch266 should model what writes here)",
                                            stable_addr, stable_data, stable_hits, rname);
                    end
                    8'd2, 8'd3, 8'd4, 8'd5, 8'd7: begin
                        verdict = $sformatf("helper_static_mmio_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=%s — Ch266 should model the device behind it)",
                                            stable_addr, stable_data, stable_hits, rname);
                    end
                    8'd0: begin
                        verdict = $sformatf("helper_static_ram_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=BIOS_ROM — likely a BIOS constant, not a polled state)",
                                            stable_addr, stable_data, stable_hits);
                    end
                    default: begin
                        verdict = $sformatf("helper_static_mmio_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=%s)",
                                            stable_addr, stable_data, stable_hits, rname);
                    end
                endcase
            end else if (any_stack_only_reads && jal_out_count == 1) begin
                verdict = $sformatf("helper_is_thunk (%0d reads observed but they cluster within 0x40 of each other in EE_RAM (looks like stack frame); exactly one JAL out to 0x%08h — recurse autopsy)",
                                    n, last_jal_target);
            end else if (any_stack_only_reads) begin
                verdict = $sformatf("helper_is_thunk (%0d reads in stack neighborhood; %0d JAL outs — multi-call helper, pick the most-frequent JAL target for Ch266)",
                                    n, jal_out_count);
            end else begin
                verdict = $sformatf("helper_reads_vary_but_flow_static (captured %0d reads across %0d distinct EAs — multi-pass data varies; helper reads something but no single EA holds a stable polled value)",
                                    n, uniq_count);
            end
            $display("[ch265] verdict=%s", verdict);
        end
    endtask
`endif // CH265_HELPER_AUTOPSY

`ifdef CH266_DISPATCHER_AUTOPSY
    // ==============================================================
    // Ch266 — dispatcher autopsy (one frame deeper than Ch265). The
    // helper at 0xBFC4D370 is a thunk to 0xBFC4F320; Ch266 scopes the
    // observer to 0xBFC4F320..0xBFC4F520 (0x200 = 128 instructions —
    // wider than Ch264/265 because this is suspected to be the real
    // dispatcher body, not another wrapper).
    //
    // Upgrades over Ch265:
    //   - Capture WRITES as well as reads (selector-dispatched code
    //     may write a progress code, ready bit, or table pointer).
    //   - Snapshot $sp at entry (not just $a0/$ra/$v0/$v1), so the
    //     stack-frame filter has a known $sp to test against.
    //   - Per-selector grouping in the print task.
    //   - Stack-filter heuristic: discount EAs whose returned value
    //     equals any captured $ra_in (saved-$ra reload), and EAs
    //     within $sp_in..$sp_in+0x40 for any captured pass.
    //   - Selector-table detection: if read EAs scale linearly with
    //     $a0_in (EA_b - EA_a = ($a0_b - $a0_a) * K), it's a
    //     function-pointer-table lookup.
    //
    // Verdicts:
    //   dispatcher_static_mmio_gate_found    — non-stack stable read in MMIO
    //   dispatcher_static_ram_gate_found     — non-stack stable read in EE_RAM/BIOS_ROM
    //   dispatcher_selector_table_found      — read EAs scale linearly with $a0
    //   dispatcher_is_thunk                  — only stack reads + one JAL out
    //   dispatcher_no_nonstack_reads         — all reads are stack-frame-shaped
    // ==============================================================

    localparam logic [31:0] CH266_DISP_LO     = 32'hBFC4_F320;
    localparam logic [31:0] CH266_DISP_HI     = 32'hBFC4_F520;
    localparam logic [31:0] CH266_HELPER_BACK = 32'hBFC4_D388; // helper's post-JAL PC

    // ----- per-invocation entry/exit snapshots -----
    localparam int CH266_PASSES = 32;
    int          ch266_pass_count;
    int          ch266_pass_cyc      [0:CH266_PASSES-1];
    logic [31:0] ch266_pass_a0_in    [0:CH266_PASSES-1];
    logic [31:0] ch266_pass_ra_in    [0:CH266_PASSES-1];
    logic [31:0] ch266_pass_sp_in    [0:CH266_PASSES-1];
    logic [31:0] ch266_pass_v0_post  [0:CH266_PASSES-1];
    logic [31:0] ch266_pass_v1_post  [0:CH266_PASSES-1];
    int          ch266_pass_pending_idx;
    logic        ch266_pass_pending;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch266_pass_count       <= 0;
            ch266_pass_pending     <= 1'b0;
            ch266_pass_pending_idx <= 0;
        end else if (core_ev_valid) begin
            if (u_core.retired_pc == CH266_DISP_LO
                    && ch266_pass_count < CH266_PASSES) begin
                ch266_pass_cyc  [ch266_pass_count] <= sim_cycle_ctr;
                ch266_pass_a0_in[ch266_pass_count] <= u_core.regfile[4];
                ch266_pass_ra_in[ch266_pass_count] <= u_core.regfile[31];
                ch266_pass_sp_in[ch266_pass_count] <= u_core.regfile[29];
                ch266_pass_pending_idx <= ch266_pass_count;
                ch266_pass_pending     <= 1'b1;
                ch266_pass_count       <= ch266_pass_count + 1;
            end
            if (ch266_pass_pending
                    && u_core.retired_pc == CH266_HELPER_BACK) begin
                ch266_pass_v0_post[ch266_pass_pending_idx] <= u_core.regfile[2];
                ch266_pass_v1_post[ch266_pass_pending_idx] <= u_core.regfile[3];
                ch266_pass_pending <= 1'b0;
            end
        end
    end

    // ----- data access capture (R + W) -----
    localparam int CH266_DEPTH = 1024;
    int          ch266_count;
    int          ch266_pass_idx [0:CH266_DEPTH-1];
    logic [31:0] ch266_pc       [0:CH266_DEPTH-1];
    logic [31:0] ch266_ea       [0:CH266_DEPTH-1];
    logic [31:0] ch266_data     [0:CH266_DEPTH-1];
    logic [7:0]  ch266_region   [0:CH266_DEPTH-1];
    logic        ch266_is_wr    [0:CH266_DEPTH-1];

    wire ch266_pc_in_disp = (core_pc >= CH266_DISP_LO)
                         && (core_pc <  CH266_DISP_HI);
    wire ch266_ev_is_fetch_read = (ee_map_ev_event == trace_pkg::EV_READ)
                               && (ee_map_ev_arg0[31:0] == core_pc);
    wire ch266_ev_is_rw         = (ee_map_ev_event == trace_pkg::EV_READ)
                               || (ee_map_ev_event == trace_pkg::EV_WRITE);

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch266_count <= 0;
        end else if (ee_map_ev_valid
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ch266_ev_is_rw
                  && ch266_pc_in_disp
                  && !ch266_ev_is_fetch_read
                  && ch266_count < CH266_DEPTH) begin
            ch266_pass_idx[ch266_count] <= (ch266_pass_count == 0)
                                            ? 0
                                            : (ch266_pass_count - 1);
            ch266_pc      [ch266_count] <= core_pc;
            ch266_ea      [ch266_count] <= ee_map_ev_arg0[31:0];
            ch266_data    [ch266_count] <= (ee_map_ev_event == trace_pkg::EV_WRITE)
                                            ? ee_map_ev_arg1[31:0]
                                            : ee_rd_data;
            ch266_region  [ch266_count] <= ee_map_ev_arg3[7:0];
            ch266_is_wr   [ch266_count] <= (ee_map_ev_event == trace_pkg::EV_WRITE);
            ch266_count <= ch266_count + 1;
        end
    end

    // ----- control-flow capture -----
    localparam int CH266_CF_DEPTH = 256;
    int          ch266_cf_count;
    int          ch266_cf_pass    [0:CH266_CF_DEPTH-1];
    logic [31:0] ch266_cf_pc      [0:CH266_CF_DEPTH-1];
    logic [31:0] ch266_cf_instr   [0:CH266_CF_DEPTH-1];
    logic [31:0] ch266_cf_target  [0:CH266_CF_DEPTH-1];

    function automatic logic ch266_is_cf_opcode(input logic [31:0] instr);
        logic [5:0] op, funct;
        op    = instr[31:26];
        funct = instr[5:0];
        ch266_is_cf_opcode = (op == 6'b000010)
                          || (op == 6'b000011)
                          || (op == 6'b000000 && funct == 6'b001000)
                          || (op == 6'b000000 && funct == 6'b001001);
    endfunction

    function automatic logic [31:0] ch266_jtarget(input logic [31:0] pc,
                                                  input logic [31:0] instr);
        logic [31:0] dslot;
        dslot = pc + 32'd4;
        ch266_jtarget = { dslot[31:28], instr[25:0], 2'b00 };
    endfunction

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch266_cf_count <= 0;
        end else if (core_ev_valid
                  && u_core.retired_pc >= CH266_DISP_LO
                  && u_core.retired_pc <  CH266_DISP_HI
                  && ch266_is_cf_opcode(peek_instr(u_core.retired_pc))
                  && ch266_cf_count < CH266_CF_DEPTH) begin
            logic [31:0] ri;
            logic [5:0]  op;
            ri = peek_instr(u_core.retired_pc);
            op = ri[31:26];
            ch266_cf_pass [ch266_cf_count] <= (ch266_pass_count == 0) ? 0 : ch266_pass_count - 1;
            ch266_cf_pc   [ch266_cf_count] <= u_core.retired_pc;
            ch266_cf_instr[ch266_cf_count] <= ri;
            if (op == 6'b000010 || op == 6'b000011)
                ch266_cf_target[ch266_cf_count] <= ch266_jtarget(u_core.retired_pc, ri);
            else
                ch266_cf_target[ch266_cf_count] <= 32'h0;
            ch266_cf_count <= ch266_cf_count + 1;
        end
    end

    task automatic ch266_region_name(input logic [7:0] r,
                                     output string name);
        case (r)
            8'd0:  name = "BIOS_ROM";
            8'd1:  name = "EE_RAM";
            8'd2:  name = "EE_DMAC";
            8'd3:  name = "EE_INTC";
            8'd4:  name = "EE_MISC_MMIO";
            8'd5:  name = "EE_BIU";
            8'd6:  name = "USEG_SHADOW";
            8'd7:  name = "EE_GS_PRIV";
            8'd8:  name = "UNMAPPED";
            default: name = $sformatf("R%0d", r);
        endcase
    endtask

    function automatic string ch266_cf_mnemonic(input logic [31:0] instr);
        logic [5:0] op, funct;
        op    = instr[31:26];
        funct = instr[5:0];
        if (op == 6'b000010)                            return "j";
        else if (op == 6'b000011)                       return "jal";
        else if (op == 6'b000000 && funct == 6'b001000) return "jr";
        else if (op == 6'b000000 && funct == 6'b001001) return "jalr";
        else                                            return "?";
    endfunction

    // Stack-shape heuristics: EA looks like a stack slot if it lies
    // within $sp..$sp+0x40 for any captured invocation. Returned
    // value looks like a saved-$ra reload if it matches any captured
    // $ra_in.
    function automatic logic ch266_ea_is_stack(input logic [31:0] ea);
        logic res;
        res = 1'b0;
        for (int i = 0; i < ch266_pass_count; i++) begin
            if (ea >= ch266_pass_sp_in[i] && ea < ch266_pass_sp_in[i] + 32'h40)
                res = 1'b1;
        end
        ch266_ea_is_stack = res;
    endfunction

    function automatic logic ch266_value_is_ra_reload(input logic [31:0] v);
        logic res;
        res = 1'b0;
        for (int i = 0; i < ch266_pass_count; i++) begin
            if (v === ch266_pass_ra_in[i]) res = 1'b1;
        end
        ch266_value_is_ra_reload = res;
    endfunction

    task automatic ch266_print_autopsy();
        int n_acc;
        int n_cf;
        string rname;
        // Dedup over EA.
        int    uniq_count;
        logic [31:0] uniq_ea       [0:CH266_DEPTH-1];
        int          uniq_reads    [0:CH266_DEPTH-1];
        int          uniq_writes   [0:CH266_DEPTH-1];
        int          uniq_passes   [0:CH266_DEPTH-1];
        logic [31:0] uniq_first_d  [0:CH266_DEPTH-1];
        logic        uniq_data_var [0:CH266_DEPTH-1];
        logic [7:0]  uniq_region   [0:CH266_DEPTH-1];
        logic        uniq_stack    [0:CH266_DEPTH-1];
        logic        uniq_ra_re    [0:CH266_DEPTH-1];
        int          j;
        logic        found;
        // Selector-table detection
        logic        sel_table_found;
        logic [31:0] sel_table_base;
        int          sel_table_K;
        // Gate candidate (non-stack, non-ra-reload, stable across >=2 passes)
        logic        stable_found;
        logic [31:0] stable_ea, stable_data;
        logic [7:0]  stable_region;
        int          stable_hits;
        // JAL-out count
        int          jal_out_count;
        logic [31:0] last_jal_target;
        string       verdict;

        n_acc = ch266_count;
        n_cf  = ch266_cf_count;

        $display("[ch266] DISPATCHER_PASSES scope=0x%08h..0x%08h count=%0d (cap=%0d):",
                 CH266_DISP_LO, CH266_DISP_HI, ch266_pass_count, CH266_PASSES);
        for (int i = 0; i < ch266_pass_count; i++) begin
            $display("[ch266]   [%0d] cyc=%0d $a0_in=0x%08h $ra_in=0x%08h $sp_in=0x%08h $v0_post=0x%08h $v1_post=0x%08h",
                     i, ch266_pass_cyc[i], ch266_pass_a0_in[i],
                     ch266_pass_ra_in[i], ch266_pass_sp_in[i],
                     ch266_pass_v0_post[i], ch266_pass_v1_post[i]);
        end

        // Control flow
        jal_out_count   = 0;
        last_jal_target = 32'd0;
        $display("[ch266] DISPATCHER_CONTROL_FLOW captured=%0d (cap=%0d):",
                 n_cf, CH266_CF_DEPTH);
        for (int i = 0; i < n_cf; i++) begin
            string mnem, note;
            mnem = ch266_cf_mnemonic(ch266_cf_instr[i]);
            note = "";
            if ((mnem == "jal" || mnem == "j") &&
                (ch266_cf_target[i] < CH266_DISP_LO ||
                 ch266_cf_target[i] >= CH266_DISP_HI)) begin
                note = "  <-- LEAVES dispatcher";
                if (mnem == "jal") begin
                    jal_out_count   = jal_out_count + 1;
                    last_jal_target = ch266_cf_target[i];
                end
            end
            $display("[ch266]   [%0d] pass=%0d pc=0x%08h instr=0x%08h %s target=0x%08h%s",
                     i, ch266_cf_pass[i], ch266_cf_pc[i],
                     ch266_cf_instr[i], mnem, ch266_cf_target[i], note);
        end

        // Data access stream (per-event, with selector lookup + R/W tag)
        $display("[ch266] DISPATCHER_DATA_ACCESS captured=%0d (cap=%0d):",
                 n_acc, CH266_DEPTH);
        for (int i = 0; i < n_acc; i++) begin
            logic [31:0] sel;
            string       rw_str;
            sel = (ch266_pass_idx[i] < ch266_pass_count)
                    ? ch266_pass_a0_in[ch266_pass_idx[i]]
                    : 32'hxxxxxxxx;
            rw_str = ch266_is_wr[i] ? "W" : "R";
            ch266_region_name(ch266_region[i], rname);
            $display("[ch266]   [%0d] pass=%0d sel=0x%08h pc=0x%08h %s ea=0x%08h data=0x%08h region=%s",
                     i, ch266_pass_idx[i], sel, ch266_pc[i],
                     rw_str, ch266_ea[i], ch266_data[i], rname);
        end

        // Build dedup table.
        uniq_count = 0;
        for (int i = 0; i < n_acc; i++) begin
            found = 1'b0;
            for (j = 0; j < uniq_count; j++) begin
                if (uniq_ea[j] === ch266_ea[i]) begin
                    found = 1'b1;
                    if (ch266_is_wr[i]) uniq_writes[j] = uniq_writes[j] + 1;
                    else                uniq_reads [j] = uniq_reads [j] + 1;
                    uniq_passes[j] = uniq_passes[j] | (1 << ch266_pass_idx[i]);
                    if (!ch266_is_wr[i] && ch266_data[i] !== uniq_first_d[j])
                        uniq_data_var[j] = 1'b1;
                end
            end
            if (!found) begin
                uniq_ea[uniq_count]       = ch266_ea[i];
                uniq_reads [uniq_count]   = ch266_is_wr[i] ? 0 : 1;
                uniq_writes[uniq_count]   = ch266_is_wr[i] ? 1 : 0;
                uniq_passes[uniq_count]   = (1 << ch266_pass_idx[i]);
                uniq_first_d[uniq_count]  = ch266_data[i];
                uniq_data_var[uniq_count] = 1'b0;
                uniq_region[uniq_count]   = ch266_region[i];
                uniq_stack[uniq_count]    = ch266_ea_is_stack(ch266_ea[i]);
                uniq_ra_re[uniq_count]    = (!ch266_is_wr[i]) && ch266_value_is_ra_reload(ch266_data[i]);
                uniq_count                = uniq_count + 1;
            end
        end

        // Dedup print, with stack/ra flags
        $display("[ch266] TOP_DISTINCT_EAs (count=%0d):", uniq_count);
        for (int i = 0; i < uniq_count; i++) begin
            ch266_region_name(uniq_region[i], rname);
            $display("[ch266]   ea=0x%08h  R=%0d W=%0d  passes=0x%04h  first_d=0x%08h  data_varies=%0b  region=%s  stack=%0b  ra_reload=%0b",
                     uniq_ea[i], uniq_reads[i], uniq_writes[i],
                     uniq_passes[i], uniq_first_d[i], uniq_data_var[i],
                     rname, uniq_stack[i], uniq_ra_re[i]);
        end

        // Selector-table detection: among READ events (uniq_writes==0),
        // find any pair (a,b) where EA_b - EA_a == ($a0_in_b - $a0_in_a) * K
        // for K in {1,2,4,8}. To keep it simple, attribute each unique
        // EA to the selector of its FIRST occurrence.
        sel_table_found = 1'b0;
        sel_table_base  = 32'd0;
        sel_table_K     = 0;
        for (int ai = 0; ai < uniq_count && !sel_table_found; ai++) begin
            for (int bi = ai + 1; bi < uniq_count && !sel_table_found; bi++) begin
                if (uniq_writes[ai] == 0 && uniq_writes[bi] == 0
                    && !uniq_stack[ai] && !uniq_stack[bi]) begin
                    // Find a captured pass that hit ai's EA, and one for bi
                    logic [31:0] sel_a, sel_b;
                    logic        got_a, got_b;
                    got_a = 1'b0; got_b = 1'b0;
                    sel_a = 32'd0; sel_b = 32'd0;
                    for (int k = 0; k < n_acc; k++) begin
                        if (!ch266_is_wr[k] && ch266_ea[k] === uniq_ea[ai] && !got_a) begin
                            if (ch266_pass_idx[k] < ch266_pass_count) begin
                                sel_a = ch266_pass_a0_in[ch266_pass_idx[k]];
                                got_a = 1'b1;
                            end
                        end
                        if (!ch266_is_wr[k] && ch266_ea[k] === uniq_ea[bi] && !got_b) begin
                            if (ch266_pass_idx[k] < ch266_pass_count) begin
                                sel_b = ch266_pass_a0_in[ch266_pass_idx[k]];
                                got_b = 1'b1;
                            end
                        end
                    end
                    if (got_a && got_b && sel_a !== sel_b) begin
                        logic [31:0] dea;
                        logic [31:0] dsel;
                        dea  = (uniq_ea[bi] > uniq_ea[ai]) ? (uniq_ea[bi] - uniq_ea[ai]) : (uniq_ea[ai] - uniq_ea[bi]);
                        dsel = (sel_b > sel_a) ? (sel_b - sel_a) : (sel_a - sel_b);
                        if (dsel != 0) begin
                            if (dea == dsel * 1)      begin sel_table_K = 1; sel_table_found = 1'b1; end
                            else if (dea == dsel * 2) begin sel_table_K = 2; sel_table_found = 1'b1; end
                            else if (dea == dsel * 4) begin sel_table_K = 4; sel_table_found = 1'b1; end
                            else if (dea == dsel * 8) begin sel_table_K = 8; sel_table_found = 1'b1; end
                            if (sel_table_found) begin
                                // base = smaller_ea - smaller_sel * K
                                if (uniq_ea[ai] <= uniq_ea[bi])
                                    sel_table_base = uniq_ea[ai] - sel_a * sel_table_K;
                                else
                                    sel_table_base = uniq_ea[bi] - sel_b * sel_table_K;
                            end
                        end
                    end
                end
            end
        end

        // Gate candidate: stable read, NOT stack, NOT ra-reload, ≥2 passes
        stable_found  = 1'b0;
        stable_hits   = 0;
        stable_ea     = 32'd0;
        stable_data   = 32'd0;
        stable_region = 8'hFF;
        for (int i = 0; i < uniq_count; i++) begin
            int npasses;
            npasses = 0;
            for (int b = 0; b < 16; b++)
                if ((uniq_passes[i] >> b) & 1) npasses = npasses + 1;
            if (uniq_writes[i] == 0
                && !uniq_data_var[i]
                && !uniq_stack[i]
                && !uniq_ra_re[i]
                && npasses >= 2
                && uniq_reads[i] > stable_hits) begin
                stable_found  = 1'b1;
                stable_ea     = uniq_ea[i];
                stable_data   = uniq_first_d[i];
                stable_region = uniq_region[i];
                stable_hits   = uniq_reads[i];
            end
        end

        // Verdict precedence:
        //   1. selector_table_found  (most informative)
        //   2. static_mmio_gate_found / static_ram_gate_found
        //   3. is_thunk
        //   4. no_nonstack_reads
        if (sel_table_found) begin
            verdict = $sformatf("dispatcher_selector_table_found (base=0x%08h, K=%0d — reads scale as base + $a0 * K — this is a function-pointer / data table; Ch267 should model what populates this table, OR find what reads the resulting pointer)",
                                sel_table_base, sel_table_K);
        end else if (stable_found) begin
            ch266_region_name(stable_region, rname);
            case (stable_region)
                8'd1, 8'd6, 8'd0: begin
                    verdict = $sformatf("dispatcher_static_ram_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=%s — Ch267 should model what writes here)",
                                        stable_ea, stable_data, stable_hits, rname);
                end
                8'd2, 8'd3, 8'd4, 8'd5, 8'd7: begin
                    verdict = $sformatf("dispatcher_static_mmio_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=%s — Ch267 should model the device behind it)",
                                        stable_ea, stable_data, stable_hits, rname);
                end
                default: begin
                    verdict = $sformatf("dispatcher_static_mmio_gate_found (EA=0x%08h returns identical 0x%08h across %0d hits — region=%s)",
                                        stable_ea, stable_data, stable_hits, rname);
                end
            endcase
        end else begin
            int non_stack_reads;
            non_stack_reads = 0;
            for (int i = 0; i < uniq_count; i++) begin
                if (uniq_writes[i] == 0 && !uniq_stack[i] && !uniq_ra_re[i])
                    non_stack_reads = non_stack_reads + 1;
            end
            if (non_stack_reads == 0 && jal_out_count == 1) begin
                verdict = $sformatf("dispatcher_is_thunk (no non-stack reads; one JAL out to 0x%08h — recurse autopsy)",
                                    last_jal_target);
            end else if (non_stack_reads == 0) begin
                verdict = $sformatf("dispatcher_no_nonstack_reads (%0d reads observed but all are stack-frame-shaped or saved-$ra reloads; %0d JAL outs — pick the most-frequent target or look at register dependencies)",
                                    n_acc, jal_out_count);
            end else begin
                verdict = $sformatf("dispatcher_reads_vary_but_flow_static (captured %0d reads across %0d distinct EAs; %0d non-stack reads but none stable across passes)",
                                    n_acc, uniq_count, non_stack_reads);
            end
        end
        $display("[ch266] verdict=%s", verdict);
    endtask
`endif // CH266_DISPATCHER_AUTOPSY

`ifdef CH267_GATE_OBSERVER
    // ==============================================================
    // Ch267 Phase 1 — passive observer for the kernel-global gate
    // STORAGE at physical 0x0000A8C8 (found by Ch266 as the address
    // the dispatcher chain clears+returns). Phase 1 is observation-
    // only: no stubs, no TB pokes. The point is to distinguish three
    // roles for accesses to this EA across the BIOS-long run:
    //
    //   clearer  — a WRITE of 0 (specifically from the Ch266
    //              dispatcher window 0xBFC4F320..0xBFC4F520)
    //   writer   — a WRITE of any non-zero value, from anywhere
    //   poller   — a READ from anywhere
    //
    // Aliases accepted (all three are kseg/kuseg views of phys
    // 0x000A8C8):
    //   0x0000A8C8 (kuseg)
    //   0x8000A8C8 (kseg0 cached)
    //   0xA000A8C8 (kseg1 uncached)
    //
    // Verdicts:
    //   gate_only_cleared_never_polled  — clearers exist, no polls
    //   gate_polled_zero_no_writer      — polls of 0, no non-zero writer
    //   gate_nonzero_writer_found       — a non-zero writer exists
    //   gate_alias_mismatch             — pollers and writers use
    //                                     disjoint aliases (write
    //                                     doesn't reach poller)
    //   gate_value_changes_but_treadmill — writes happen but Ch217
    //                                     treadmill metrics unchanged
    //                                     (left as Codex's call,
    //                                     emitted only on direct hint)
    // ==============================================================

    localparam logic [28:0] CH267_PHYS_TARGET    = 29'h000_A8C8;
    localparam logic [31:0] CH267_DISPATCHER_LO  = 32'hBFC4_F320;
    localparam logic [31:0] CH267_DISPATCHER_HI  = 32'hBFC4_F520;

    localparam int CH267_DEPTH = 1024;
    int          ch267_count;
    int          ch267_cyc        [0:CH267_DEPTH-1];
    int          ch267_pass       [0:CH267_DEPTH-1];
    logic [31:0] ch267_pc         [0:CH267_DEPTH-1];
    logic [31:0] ch267_ea         [0:CH267_DEPTH-1];
    logic [31:0] ch267_data       [0:CH267_DEPTH-1];
    logic        ch267_is_wr      [0:CH267_DEPTH-1];
    logic        ch267_in_disp    [0:CH267_DEPTH-1];
    logic        ch267_post_clear [0:CH267_DEPTH-1];

    logic        ch267_have_clear;

    wire ch267_ea_matches  = (ee_map_ev_arg0[28:0] == CH267_PHYS_TARGET);
    wire ch267_is_rw       = (ee_map_ev_event == trace_pkg::EV_READ)
                          || (ee_map_ev_event == trace_pkg::EV_WRITE);
    wire ch267_pc_in_disp  = (core_pc >= CH267_DISPATCHER_LO)
                          && (core_pc <  CH267_DISPATCHER_HI);
    wire ch267_is_disp_clr = (ee_map_ev_event == trace_pkg::EV_WRITE)
                          && (ee_map_ev_arg1[31:0] == 32'h0)
                          && ch267_pc_in_disp;

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch267_count      <= 0;
            ch267_have_clear <= 1'b0;
        end else if (ee_map_ev_valid
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ch267_is_rw
                  && ch267_ea_matches
                  && ch267_count < CH267_DEPTH) begin
            ch267_cyc       [ch267_count] <= sim_cycle_ctr;
            ch267_pass      [ch267_count] <= ch217_count;
            ch267_pc        [ch267_count] <= core_pc;
            ch267_ea        [ch267_count] <= ee_map_ev_arg0[31:0];
            ch267_data      [ch267_count] <= (ee_map_ev_event == trace_pkg::EV_WRITE)
                                              ? ee_map_ev_arg1[31:0]
                                              : ee_rd_data;
            ch267_is_wr     [ch267_count] <= (ee_map_ev_event == trace_pkg::EV_WRITE);
            ch267_in_disp   [ch267_count] <= ch267_pc_in_disp;
            ch267_post_clear[ch267_count] <= ch267_have_clear;
            ch267_count <= ch267_count + 1;
            if (ch267_is_disp_clr)
                ch267_have_clear <= 1'b1;
        end
    end

    task automatic ch267_role_name(input logic is_wr,
                                   input logic [31:0] data,
                                   input logic in_disp,
                                   output string role);
        if (is_wr) begin
            if (data == 32'h0) begin
                if (in_disp) role = "CLEARER(disp)";
                else         role = "CLEARER(other)";
            end else begin
                role = "WRITER";
            end
        end else begin
            role = "POLLER";
        end
    endtask

    task automatic ch267_alias_label(input logic [31:0] ea,
                                     output string lab);
        case (ea[31:28])
            4'h0: lab = "kuseg";
            4'h8: lab = "kseg0";
            4'hA: lab = "kseg1";
            default: lab = $sformatf("a%h", ea[31:28]);
        endcase
    endtask

    task automatic ch267_print_observer();
        int n;
        int clearer_disp_count, clearer_other_count;
        int writer_count, poller_count;
        int writer_pre_clear, writer_post_clear;
        int poller_pre_clear, poller_post_clear;
        int alias_kuseg, alias_kseg0, alias_kseg1, alias_other;
        // Poller-side alias bitset; writer-side alias bitset.
        // Bit 0 = kuseg, bit 1 = kseg0, bit 2 = kseg1.
        int poller_alias_mask, writer_alias_mask;
        int dc_per_pass [0:31];
        int suppressed_total;
        string role, alab;
        string verdict;

        n = ch267_count;
        clearer_disp_count = 0; clearer_other_count = 0;
        writer_count = 0; poller_count = 0;
        writer_pre_clear = 0; writer_post_clear = 0;
        poller_pre_clear = 0; poller_post_clear = 0;
        alias_kuseg = 0; alias_kseg0 = 0; alias_kseg1 = 0; alias_other = 0;
        poller_alias_mask = 0; writer_alias_mask = 0;
        for (int i = 0; i < 32; i++) dc_per_pass[i] = 0;
        suppressed_total = 0;

        $display("[ch267] GATE_OBSERVER phys_target=0x0000A8C8 (aliases kuseg/kseg0/kseg1) captured=%0d (cap=%0d)",
                 n, CH267_DEPTH);

        // Stream — dispatcher clears beyond the first per pass are
        // counted but not displayed (avoid 69+ identical lines).
        $display("[ch267] STREAM:");
        for (int i = 0; i < n; i++) begin
            logic skip;
            int   p;
            skip = 1'b0;
            if (ch267_is_wr[i] && ch267_data[i] == 32'h0 && ch267_in_disp[i]) begin
                p = (ch267_pass[i] < 32) ? ch267_pass[i] : 31;
                dc_per_pass[p] = dc_per_pass[p] + 1;
                if (dc_per_pass[p] > 1) begin
                    suppressed_total = suppressed_total + 1;
                    skip = 1'b1;
                end
            end
            if (!skip) begin
                ch267_role_name(ch267_is_wr[i], ch267_data[i], ch267_in_disp[i], role);
                ch267_alias_label(ch267_ea[i], alab);
                $display("[ch267]   [%0d] cyc=%0d pass=%0d %s pc=0x%08h ea=0x%08h(%s) data=0x%08h post_clear=%0b",
                         i, ch267_cyc[i], ch267_pass[i], role, ch267_pc[i],
                         ch267_ea[i], alab, ch267_data[i], ch267_post_clear[i]);
            end
        end
        if (suppressed_total > 0)
            $display("[ch267]   ... %0d additional dispatcher clears suppressed (kept first-per-pass; see SUMMARY for total count)",
                     suppressed_total);

        // Counts + alias breakdown
        for (int i = 0; i < n; i++) begin
            int abit;
            case (ch267_ea[i][31:28])
                4'h0: begin alias_kuseg = alias_kuseg + 1; abit = 0; end
                4'h8: begin alias_kseg0 = alias_kseg0 + 1; abit = 1; end
                4'hA: begin alias_kseg1 = alias_kseg1 + 1; abit = 2; end
                default: begin alias_other = alias_other + 1; abit = 3; end
            endcase
            if (ch267_is_wr[i]) begin
                if (ch267_data[i] == 32'h0) begin
                    if (ch267_in_disp[i]) clearer_disp_count = clearer_disp_count + 1;
                    else                  clearer_other_count = clearer_other_count + 1;
                end else begin
                    writer_count = writer_count + 1;
                    if (ch267_post_clear[i]) writer_post_clear = writer_post_clear + 1;
                    else                     writer_pre_clear  = writer_pre_clear  + 1;
                    if (abit < 3) writer_alias_mask = writer_alias_mask | (1 << abit);
                end
            end else begin
                poller_count = poller_count + 1;
                if (ch267_post_clear[i]) poller_post_clear = poller_post_clear + 1;
                else                     poller_pre_clear  = poller_pre_clear  + 1;
                if (abit < 3) poller_alias_mask = poller_alias_mask | (1 << abit);
            end
        end

        $display("[ch267] SUMMARY:");
        $display("[ch267]   clearer(dispatcher) = %0d", clearer_disp_count);
        $display("[ch267]   clearer(other)      = %0d", clearer_other_count);
        $display("[ch267]   writer(non-zero)    = %0d  (pre-clear=%0d  post-clear=%0d)",
                 writer_count, writer_pre_clear, writer_post_clear);
        $display("[ch267]   poller(read)        = %0d  (pre-clear=%0d  post-clear=%0d)",
                 poller_count, poller_pre_clear, poller_post_clear);
        $display("[ch267]   alias_kuseg(0x000…) = %0d", alias_kuseg);
        $display("[ch267]   alias_kseg0(0x800…) = %0d", alias_kseg0);
        $display("[ch267]   alias_kseg1(0xA00…) = %0d", alias_kseg1);
        if (alias_other > 0)
            $display("[ch267]   alias_other         = %0d", alias_other);

        // Verdict
        if (poller_count > 0 && writer_count > 0
                && (poller_alias_mask & writer_alias_mask) == 0) begin
            verdict = $sformatf("gate_alias_mismatch (pollers used alias-mask=0x%01h, writers used alias-mask=0x%01h — disjoint; the non-zero writes never reach the polling read because our model isn't aliasing kuseg/kseg0/kseg1 to the same phys word)",
                                poller_alias_mask, writer_alias_mask);
        end else if (writer_count > 0) begin
            verdict = $sformatf("gate_nonzero_writer_found (writer_count=%0d, post-clear=%0d — at least one non-zero write hits this EA in our model; Ch268 should characterize the writer PC + timing and check whether it's the missing protocol piece)",
                                writer_count, writer_post_clear);
        end else if (poller_count > 0) begin
            verdict = $sformatf("gate_polled_zero_no_writer (poller_count=%0d reads of 0; %0d dispatcher clears observed; NO non-zero writer was captured — confirms Ch266's read: the gate is polled but nothing in our model sets it. Ch268 Phase 2 (single controlled post-clear poke) is the next step.)",
                                poller_count, clearer_disp_count);
        end else if (clearer_disp_count > 0) begin
            verdict = $sformatf("gate_only_cleared_never_polled (the dispatcher writes 0 here %0d times but NOTHING reads this EA — the longjmp chain returns the pointer as $v0 but the caller uses it as a write target / table base / scratch, not as a polled value. The treadmill must be gating on a DIFFERENT address; revisit what the Ch217 outer caller does with $v0=0xA000A8C8.)",
                                clearer_disp_count);
        end else begin
            verdict = $sformatf("gate_no_traffic_at_all (zero accesses captured at phys 0x000A8C8 — the model may be routing this EA somewhere we're not watching, OR the dispatcher's writes aren't reaching the EE map. Re-check the trace event plumbing.)");
        end

        $display("[ch267] verdict=%s", verdict);
    endtask
`endif // CH267_GATE_OBSERVER

`ifdef CH268_OUTER_READ_AUTOPSY
    // ==============================================================
    // Ch268 — widened read autopsy for the longjmp-return OUTER
    // CALLER body (PC 0xBFC52340..0xBFC52400). Ch267 ruled out
    // 0xA000A8C8 as a polled gate; Ch268 looks for whatever the
    // OUTER caller does poll across passes. Captures non-fetch
    // READS only. Buckets by alias-normalized phys EA (low 29 bits)
    // so kuseg/kseg0/kseg1 views of the same word collapse.
    //
    // Per bucket reports: hits, PCs (up to 4), per-pass values
    // (passes 0..15), data_varies flag, region, stack flag, alias
    // mask.
    //
    // Stack filter: captures $sp at the JAL at 0xBFC52358 (one
    // value per Ch217 pass); EA is "stack" if in [$sp..$sp+0x60)
    // for the corresponding pass.
    //
    // Verdicts:
    //   outer_static_mmio_gate_found        — non-stack stable read in MMIO
    //   outer_static_ram_gate_found         — non-stack stable read in EE_RAM/BIOS_ROM
    //   outer_only_stack_reads              — all reads are stack-frame-shaped
    //   outer_no_reads                      — zero captures
    //   outer_reads_vary_but_flow_static    — reads observed, none stable
    // ==============================================================

    localparam logic [31:0] CH268_OUTER_LO = 32'hBFC5_2340;
    localparam logic [31:0] CH268_OUTER_HI = 32'hBFC5_2400;
    localparam logic [31:0] CH268_OUTER_JAL = 32'hBFC5_2358;

    localparam int CH268_DEPTH    = 1024;
    localparam int CH268_PASSES   = 16;
    localparam int CH268_PC_TRACK = 4;

    int          ch268_count;
    int          ch268_pass    [0:CH268_DEPTH-1];
    logic [31:0] ch268_pc      [0:CH268_DEPTH-1];
    logic [31:0] ch268_ea      [0:CH268_DEPTH-1];
    logic [28:0] ch268_phys    [0:CH268_DEPTH-1];
    logic [31:0] ch268_data    [0:CH268_DEPTH-1];
    logic [7:0]  ch268_region  [0:CH268_DEPTH-1];

    // $sp captured at the JAL at 0xBFC52358, one per Ch217 pass.
    logic [31:0] ch268_sp_per_pass [0:CH268_PASSES-1];
    logic        ch268_sp_seen     [0:CH268_PASSES-1];

    wire ch268_pc_in_outer = (core_pc >= CH268_OUTER_LO)
                          && (core_pc <  CH268_OUTER_HI);
    wire ch268_is_fetch    = (ee_map_ev_arg0[31:0] == core_pc);

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch268_count <= 0;
            for (int p = 0; p < CH268_PASSES; p++)
                ch268_sp_seen[p] <= 1'b0;
        end else begin
            // Capture $sp at the JAL retire, indexed by ch217_count
            // (the JAL retire fires BEFORE ch217_count is bumped at
            // this same retire, so the slot equals ch217_count).
            if (core_ev_valid
                    && u_core.retired_pc == CH268_OUTER_JAL
                    && ch217_count < CH268_PASSES) begin
                ch268_sp_per_pass[ch217_count] <= u_core.regfile[29];
                ch268_sp_seen    [ch217_count] <= 1'b1;
            end
            // Capture every non-fetch read inside the outer caller body.
            if (ee_map_ev_valid
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ee_map_ev_event  == trace_pkg::EV_READ
                  && ch268_pc_in_outer
                  && !ch268_is_fetch
                  && ch268_count < CH268_DEPTH) begin
                ch268_pass  [ch268_count] <= ch217_count;
                ch268_pc    [ch268_count] <= core_pc;
                ch268_ea    [ch268_count] <= ee_map_ev_arg0[31:0];
                ch268_phys  [ch268_count] <= ee_map_ev_arg0[28:0];
                ch268_data  [ch268_count] <= ee_rd_data;
                ch268_region[ch268_count] <= ee_map_ev_arg3[7:0];
                ch268_count <= ch268_count + 1;
            end
        end
    end

    function automatic logic ch268_ea_is_stack(input logic [31:0] ea,
                                               input int pass);
        logic res;
        logic [31:0] sp;
        res = 1'b0;
        if (pass < CH268_PASSES && ch268_sp_seen[pass]) begin
            sp = ch268_sp_per_pass[pass];
            if (ea >= sp && ea < sp + 32'h60)
                res = 1'b1;
        end
        ch268_ea_is_stack = res;
    endfunction

    task automatic ch268_region_name(input logic [7:0] r,
                                     output string name);
        case (r)
            8'd0:  name = "BIOS_ROM";
            8'd1:  name = "EE_RAM";
            8'd2:  name = "EE_DMAC";
            8'd3:  name = "EE_INTC";
            8'd4:  name = "EE_MISC_MMIO";
            8'd5:  name = "EE_BIU";
            8'd6:  name = "USEG_SHADOW";
            8'd7:  name = "EE_GS_PRIV";
            8'd8:  name = "UNMAPPED";
            default: name = $sformatf("R%0d", r);
        endcase
    endtask

    task automatic ch268_print_autopsy();
        int n;
        int uniq_count;
        // Use flat arrays to keep iverilog happy. Bucket index b runs
        // 0..uniq_count-1; per-bucket per-slot uses bucket*SLOTS + k.
        logic [28:0] uniq_phys     [0:CH268_DEPTH-1];
        int          uniq_hits     [0:CH268_DEPTH-1];
        int          uniq_passes   [0:CH268_DEPTH-1];
        logic [31:0] uniq_first_d  [0:CH268_DEPTH-1];
        logic        uniq_data_var [0:CH268_DEPTH-1];
        logic [7:0]  uniq_region   [0:CH268_DEPTH-1];
        logic        uniq_stack    [0:CH268_DEPTH-1];
        int          uniq_alias_m  [0:CH268_DEPTH-1];
        int          uniq_pc_cnt   [0:CH268_DEPTH-1];
        logic [31:0] uniq_pcs      [0:CH268_DEPTH*CH268_PC_TRACK-1];
        logic [31:0] uniq_vals     [0:CH268_DEPTH*CH268_PASSES-1];
        logic        uniq_vals_set [0:CH268_DEPTH*CH268_PASSES-1];

        int   j;
        logic found;
        int   non_stack_count;
        // Verdict
        int          gate_idx;
        logic [28:0] gate_phys;
        logic [31:0] gate_data;
        logic [7:0]  gate_region;
        int          gate_hits;
        logic        gate_found;
        string verdict;
        string rname;
        string pc_list;
        string vals_str;
        string alias_str;

        n = ch268_count;
        uniq_count = 0;

        $display("[ch268] OUTER_READS scope=0x%08h..0x%08h captured=%0d (cap=%0d)",
                 CH268_OUTER_LO, CH268_OUTER_HI, n, CH268_DEPTH);

        // Stream + bucketing pass.
        for (int i = 0; i < n; i++) begin
            logic is_stack;
            is_stack = ch268_ea_is_stack(ch268_ea[i], ch268_pass[i]);
            ch268_region_name(ch268_region[i], rname);
            $display("[ch268]   [%0d] pass=%0d pc=0x%08h ea=0x%08h phys=0x%07h data=0x%08h region=%s stack=%0b",
                     i, ch268_pass[i], ch268_pc[i], ch268_ea[i],
                     ch268_phys[i], ch268_data[i], rname, is_stack);

            // Bucket by alias-normalized phys.
            found = 1'b0;
            for (j = 0; j < uniq_count; j++) begin
                if (uniq_phys[j] === ch268_phys[i]) begin
                    found = 1'b1;
                    uniq_hits[j]   = uniq_hits[j] + 1;
                    uniq_passes[j] = uniq_passes[j] | (1 << ch268_pass[i]);
                    if (ch268_data[i] !== uniq_first_d[j]) uniq_data_var[j] = 1'b1;
                    // Track up to CH268_PC_TRACK unique PCs.
                    begin
                        logic pc_found;
                        pc_found = 1'b0;
                        for (int k = 0; k < uniq_pc_cnt[j]; k++)
                            if (uniq_pcs[j*CH268_PC_TRACK + k] === ch268_pc[i])
                                pc_found = 1'b1;
                        if (!pc_found && uniq_pc_cnt[j] < CH268_PC_TRACK) begin
                            uniq_pcs[j*CH268_PC_TRACK + uniq_pc_cnt[j]] = ch268_pc[i];
                            uniq_pc_cnt[j] = uniq_pc_cnt[j] + 1;
                        end
                    end
                    // Per-pass values
                    if (ch268_pass[i] < CH268_PASSES) begin
                        uniq_vals    [j*CH268_PASSES + ch268_pass[i]] = ch268_data[i];
                        uniq_vals_set[j*CH268_PASSES + ch268_pass[i]] = 1'b1;
                    end
                    // Alias mask
                    case (ch268_ea[i][31:28])
                        4'h0: uniq_alias_m[j] = uniq_alias_m[j] | 1;
                        4'h8: uniq_alias_m[j] = uniq_alias_m[j] | 2;
                        4'hA: uniq_alias_m[j] = uniq_alias_m[j] | 4;
                        default: ;
                    endcase
                end
            end
            if (!found) begin
                uniq_phys    [uniq_count] = ch268_phys[i];
                uniq_hits    [uniq_count] = 1;
                uniq_passes  [uniq_count] = (1 << ch268_pass[i]);
                uniq_first_d [uniq_count] = ch268_data[i];
                uniq_data_var[uniq_count] = 1'b0;
                uniq_region  [uniq_count] = ch268_region[i];
                uniq_stack   [uniq_count] = is_stack;
                uniq_pcs     [uniq_count*CH268_PC_TRACK + 0] = ch268_pc[i];
                uniq_pc_cnt  [uniq_count] = 1;
                for (int k = 0; k < CH268_PASSES; k++) begin
                    uniq_vals    [uniq_count*CH268_PASSES + k] = 32'd0;
                    uniq_vals_set[uniq_count*CH268_PASSES + k] = 1'b0;
                end
                if (ch268_pass[i] < CH268_PASSES) begin
                    uniq_vals    [uniq_count*CH268_PASSES + ch268_pass[i]] = ch268_data[i];
                    uniq_vals_set[uniq_count*CH268_PASSES + ch268_pass[i]] = 1'b1;
                end
                case (ch268_ea[i][31:28])
                    4'h0: uniq_alias_m[uniq_count] = 1;
                    4'h8: uniq_alias_m[uniq_count] = 2;
                    4'hA: uniq_alias_m[uniq_count] = 4;
                    default: uniq_alias_m[uniq_count] = 0;
                endcase
                uniq_count = uniq_count + 1;
            end
        end

        // Per-bucket report.
        $display("[ch268] DISTINCT_PHYS_EAs (count=%0d):", uniq_count);
        for (int i = 0; i < uniq_count; i++) begin
            pc_list = "";
            for (int k = 0; k < uniq_pc_cnt[i]; k++) begin
                if (k == 0)
                    pc_list = $sformatf("0x%08h", uniq_pcs[i*CH268_PC_TRACK + k]);
                else
                    pc_list = $sformatf("%s,0x%08h", pc_list, uniq_pcs[i*CH268_PC_TRACK + k]);
            end
            vals_str = "";
            for (int p = 0; p < CH268_PASSES; p++) begin
                if (uniq_vals_set[i*CH268_PASSES + p]) begin
                    if (vals_str == "")
                        vals_str = $sformatf("p%0d=0x%08h", p, uniq_vals[i*CH268_PASSES + p]);
                    else
                        vals_str = $sformatf("%s p%0d=0x%08h", vals_str, p, uniq_vals[i*CH268_PASSES + p]);
                end
            end
            alias_str = "";
            if (uniq_alias_m[i] & 1) alias_str = {alias_str, "kuseg "};
            if (uniq_alias_m[i] & 2) alias_str = {alias_str, "kseg0 "};
            if (uniq_alias_m[i] & 4) alias_str = {alias_str, "kseg1 "};
            ch268_region_name(uniq_region[i], rname);
            $display("[ch268]   phys=0x%07h hits=%0d passes=0x%04h region=%s stack=%0b alias=%s pcs=[%s]",
                     uniq_phys[i], uniq_hits[i], uniq_passes[i],
                     rname, uniq_stack[i], alias_str, pc_list);
            $display("[ch268]     values: %s  data_varies=%0b",
                     vals_str, uniq_data_var[i]);
        end

        // Find a non-stack gate: stable data across >=2 treadmill passes
        // (passes 1..15, excluding pass 0 init noise).
        gate_found  = 1'b0;
        gate_hits   = 0;
        gate_idx    = 0;
        gate_phys   = 29'd0;
        gate_data   = 32'd0;
        gate_region = 8'hFF;
        non_stack_count = 0;
        for (int i = 0; i < uniq_count; i++) begin
            if (!uniq_stack[i]) non_stack_count = non_stack_count + 1;
        end
        for (int i = 0; i < uniq_count; i++) begin
            int npasses_steady;
            npasses_steady = 0;
            for (int b = 1; b < CH268_PASSES; b++)
                if ((uniq_passes[i] >> b) & 1) npasses_steady = npasses_steady + 1;
            if (!uniq_stack[i] && !uniq_data_var[i]
                && npasses_steady >= 2
                && uniq_hits[i] > gate_hits) begin
                gate_found  = 1'b1;
                gate_idx    = i;
                gate_hits   = uniq_hits[i];
                gate_phys   = uniq_phys[i];
                gate_data   = uniq_first_d[i];
                gate_region = uniq_region[i];
            end
        end

        if (n == 0) begin
            verdict = "outer_no_reads (the outer caller body emits no non-fetch reads at all — every Ch217 pass goes JAL → return without touching memory directly here. The gate must live OUTSIDE this window — try widening to 0xBFC52300..0xBFC52500 or look at the syscall/RFE return path.)";
        end else if (gate_found) begin
            ch268_region_name(gate_region, rname);
            case (gate_region)
                8'd1, 8'd0, 8'd6: verdict = $sformatf("outer_static_ram_gate_found (phys=0x%07h returns identical 0x%08h across %0d hits in %0d steady-state passes — region=%s — Ch269 should model what writes here)",
                                                       gate_phys, gate_data, gate_hits,
                                                       (uniq_passes[gate_idx] >> 1), rname);
                8'd2, 8'd3, 8'd4, 8'd5, 8'd7: verdict = $sformatf("outer_static_mmio_gate_found (phys=0x%07h returns identical 0x%08h across %0d hits — region=%s — Ch269 should model the device behind it)",
                                                       gate_phys, gate_data, gate_hits, rname);
                default: verdict = $sformatf("outer_static_gate_found (phys=0x%07h data=0x%08h hits=%0d region=%s)",
                                                       gate_phys, gate_data, gate_hits, rname);
            endcase
        end else if (non_stack_count == 0) begin
            verdict = $sformatf("outer_only_stack_reads (%0d reads observed, all within $sp..$sp+0x60 of their pass — outer caller doesn't poll any kernel-global directly; the gate is somewhere else again)",
                                n);
        end else begin
            verdict = $sformatf("outer_reads_vary_but_flow_static (%0d reads / %0d distinct phys EAs / %0d non-stack — no single bucket has stable data across >=2 treadmill passes. Read set is real but doesn't have a stable polled value, so either the gate value DOES change with each pass (the actual signal) or the gate is in a region we haven't widened to yet)",
                                n, uniq_count, non_stack_count);
        end

        $display("[ch268] verdict=%s", verdict);
    endtask
`endif // CH268_OUTER_READ_AUTOPSY

`ifdef CH269_V0_LINEAGE
    // ==============================================================
    // Ch269 — $v0 lineage check (Codex's hard-stop chapter).
    //
    // The Ch217 outer caller's BEQ at 0xBFC52350 tests $v0. Per the
    // Ch217 verdict, $v0 = 0x00000001 every pass — so the branch
    // falls through to the JAL chain. Codex's framing: this $v0=1
    // is almost certainly committed by the Ch215 jmp_buf restore
    // shim in ee_core_stub.sv:1280 (line: `regfile[2] <= 32'd1;`),
    // NOT by any hidden BIOS load. This chapter proves it.
    //
    // Mechanism (v2, per feedback_observer_design_for_lineage.md):
    // LIVE LATCH that overwrites every cycle u_core.regfile[2]
    // changes (no fill array, no depth, no overflow risk). At each
    // retire of PC=0xBFC52350 (the BEQ), snapshot the latch. The
    // snapshot answers: "what was the LAST $v0 writer before this
    // BEQ retired?" Includes FSM state at the PREVIOUS cycle (the
    // state that DROVE the write). State == S_CH215_WAIT (4'd9)
    // means the Ch215 restore did it. State == S_EXECUTE / S_MEM_*
    // means a real instruction did it.
    //
    // v1 used a fill-from-boot array that overflowed during the
    // pre-treadmill init before any Ch215 commit landed — wasted a
    // build+run. The latch+trigger design has zero such failure
    // mode.
    //
    // Pre-print self-check (counters): total $v0 changes,
    // pass>=1 changes, latch_armed flag, BEQ retire count. A
    // misarmed observer fails its own assertions immediately.
    //
    // If the BEQ retire count is zero, our shim is bypassing the
    // BEQ entirely (sets PC=$ra past the
    // BEQ).
    //
    // Verdict (expected):
    //   v0_set_by_ch215_restore  — confirm shim is the producer →
    //     close the post-Ch215 thunk-chain search; next chapter
    //     should either model real IOP/SBUS state or step back to
    //     hardware deliverables.
    //   v0_set_by_normal_retire  — surprise: a real BIOS instruction
    //     produces $v0=1, so the upstream search has merit.
    //   v0_set_by_mixed          — both, needs finer attribution.
    // ==============================================================

    // Redesigned per feedback_observer_design_for_lineage.md:
    // LIVE LATCH that updates every cycle $v0 changes, PRINTED at the
    // BEQ trigger. No fill-from-boot array — depth doesn't apply.
    //
    // Counters: total $v0 writes, writes seen in steady state, BEQ
    // retire hits. If a BEQ retire fires before any $v0 write in the
    // same pass, the latch holds whatever the prior pass left.

    // The live latch — refreshed every cycle u_core.regfile[2]
    // changes. Held until the next change. Read at BEQ trigger.
    logic [31:0] ch269_lat_v0_prev;
    logic [31:0] ch269_lat_v0_new;
    int          ch269_lat_cyc;
    int          ch269_lat_pass;
    logic [31:0] ch269_lat_pc;
    logic [3:0]  ch269_lat_state_d1;

    // Helpers + counters.
    logic [31:0] ch269_v0_last;
    logic [3:0]  ch269_state_d1;
    int          ch269_total_changes;       // every $v0 write since reset
    int          ch269_changes_pass1plus;   // writes during ch217_count>=1
    logic        ch269_latch_armed;         // set on first change

    // BEQ-retire trigger: print the latch state and increment a
    // counter. Bounded to first 16 retires for output sanity.
    localparam int CH269_BEQ_CAP = 16;
    int          ch269_beq_count;
    int          ch269_beq_pass     [0:CH269_BEQ_CAP-1];
    logic [31:0] ch269_beq_v0       [0:CH269_BEQ_CAP-1];
    // What the latch held at each BEQ trigger:
    logic [3:0]  ch269_beq_latch_st [0:CH269_BEQ_CAP-1];
    logic [31:0] ch269_beq_latch_pc [0:CH269_BEQ_CAP-1];
    logic [31:0] ch269_beq_latch_v0 [0:CH269_BEQ_CAP-1];
    int          ch269_beq_latch_cy [0:CH269_BEQ_CAP-1];
    logic        ch269_beq_latch_ar [0:CH269_BEQ_CAP-1];

    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch269_v0_last           <= 32'd0;
            ch269_state_d1          <= 4'd0;
            ch269_total_changes     <= 0;
            ch269_changes_pass1plus <= 0;
            ch269_latch_armed       <= 1'b0;
            ch269_beq_count         <= 0;
            ch269_lat_v0_prev       <= 32'd0;
            ch269_lat_v0_new        <= 32'd0;
            ch269_lat_cyc           <= 0;
            ch269_lat_pass          <= 0;
            ch269_lat_pc            <= 32'd0;
            ch269_lat_state_d1      <= 4'd0;
        end else begin
            // Lag state by one cycle so latched state reflects the
            // FSM state that DROVE the write (the prior cycle's
            // S_CH215_WAIT if it was a Ch215 commit; the prior cycle's
            // S_EXECUTE / S_MEM_WAIT if a real instruction).
            ch269_state_d1 <= u_core.state;

            if (u_core.regfile[2] !== ch269_v0_last) begin
                // Live latch — overwrite on every change. No depth.
                ch269_lat_v0_prev  <= ch269_v0_last;
                ch269_lat_v0_new   <= u_core.regfile[2];
                ch269_lat_cyc      <= sim_cycle_ctr;
                ch269_lat_pass     <= ch217_count;
                ch269_lat_pc       <= u_core.retired_pc;
                ch269_lat_state_d1 <= ch269_state_d1;
                ch269_latch_armed  <= 1'b1;
                ch269_total_changes <= ch269_total_changes + 1;
                if (ch217_count >= 1)
                    ch269_changes_pass1plus <= ch269_changes_pass1plus + 1;
            end
            ch269_v0_last <= u_core.regfile[2];

            // BEQ retire trigger: snapshot the latch.
            if (core_ev_valid
                    && u_core.retired_pc == 32'hBFC5_2350
                    && ch269_beq_count < CH269_BEQ_CAP) begin
                ch269_beq_pass    [ch269_beq_count] <= ch217_count;
                ch269_beq_v0      [ch269_beq_count] <= u_core.regfile[2];
                ch269_beq_latch_st[ch269_beq_count] <= ch269_lat_state_d1;
                ch269_beq_latch_pc[ch269_beq_count] <= ch269_lat_pc;
                ch269_beq_latch_v0[ch269_beq_count] <= ch269_lat_v0_new;
                ch269_beq_latch_cy[ch269_beq_count] <= ch269_lat_cyc;
                ch269_beq_latch_ar[ch269_beq_count] <= ch269_latch_armed;
                ch269_beq_count <= ch269_beq_count + 1;
            end
        end
    end

    task automatic ch269_state_name(input logic [3:0] s, output string name);
        case (s)
            4'd0:  name = "IDLE";
            4'd1:  name = "IFETCH_REQ";
            4'd2:  name = "IFETCH_WAIT";
            4'd3:  name = "EXECUTE";
            4'd4:  name = "MEM_REQ";
            4'd5:  name = "MEM_WAIT";
            4'd6:  name = "MEM_WRITE";
            4'd7:  name = "HALT";
            4'd8:  name = "CH215_REQ";
            4'd9:  name = "CH215_WAIT";
            default: name = $sformatf("S%0d", s);
        endcase
    endtask

    task automatic ch269_print_observer();
        int  beq_v0_one_pass1plus;
        int  beq_v0_one_pass1plus_from_ch215;
        int  beq_v0_one_pass1plus_from_normal;
        string state_name;
        string source;
        string verdict;

        beq_v0_one_pass1plus = 0;
        beq_v0_one_pass1plus_from_ch215 = 0;
        beq_v0_one_pass1plus_from_normal = 0;

        // ---- Pre-print self-check (no fill array, but report
        // counters so a misarmed observer fails fast). ----
        $display("[ch269] V0_LINEAGE counters:");
        $display("[ch269]   total $v0 changes since reset = %0d", ch269_total_changes);
        $display("[ch269]   $v0 changes in passes >= 1    = %0d", ch269_changes_pass1plus);
        $display("[ch269]   latch armed                   = %0b", ch269_latch_armed);
        $display("[ch269]   BEQ@0xBFC52350 retire_count   = %0d (cap=%0d)",
                 ch269_beq_count, CH269_BEQ_CAP);

        // ---- Per-BEQ-retire print of the latch state at that
        // moment. THIS is the lineage answer Codex asked for: "last
        // $v0 writer before PC=0xBFC52350 in each pass." ----
        $display("[ch269] LATCH_AT_BEQ:");
        for (int i = 0; i < ch269_beq_count; i++) begin
            ch269_state_name(ch269_beq_latch_st[i], state_name);
            source = (ch269_beq_latch_st[i] == 4'd9) ? "CH215_RESTORE" : "normal_retire";
            $display("[ch269]   [%0d] pass=%0d $v0_at_BEQ=0x%08h  last_writer: cyc=%0d state_d1=%s pc=0x%08h v0=0x%08h armed=%0b  source=%s",
                     i, ch269_beq_pass[i], ch269_beq_v0[i],
                     ch269_beq_latch_cy[i], state_name,
                     ch269_beq_latch_pc[i], ch269_beq_latch_v0[i],
                     ch269_beq_latch_ar[i], source);
            if (ch269_beq_pass[i] >= 1 && ch269_beq_v0[i] == 32'd1) begin
                beq_v0_one_pass1plus = beq_v0_one_pass1plus + 1;
                if (ch269_beq_latch_st[i] == 4'd9)
                    beq_v0_one_pass1plus_from_ch215 = beq_v0_one_pass1plus_from_ch215 + 1;
                else
                    beq_v0_one_pass1plus_from_normal = beq_v0_one_pass1plus_from_normal + 1;
            end
        end

        $display("[ch269] SUMMARY (steady-state, pass>=1):");
        $display("[ch269]   BEQ retires with $v0=1            = %0d", beq_v0_one_pass1plus);
        $display("[ch269]     ... last writer from CH215      = %0d", beq_v0_one_pass1plus_from_ch215);
        $display("[ch269]     ... last writer from normal     = %0d", beq_v0_one_pass1plus_from_normal);

        // ---- Verdict — hard stop per Codex's framing ----
        if (ch269_total_changes == 0) begin
            verdict = "v0_observer_misarmed (no $v0 writes captured anywhere — instrumentation bug, fix before any further run)";
        end else if (ch269_beq_count == 0) begin
            verdict = "v0_no_beq_retires (PC=0xBFC52350 was never retired — our shim must be jumping past it. Inspect Ch215 restore PC target.)";
        end else if (beq_v0_one_pass1plus == 0) begin
            verdict = "v0_no_steady_state_beq (BEQ retires happened only in pass=0; the treadmill is NOT cycling through PC=0xBFC52350. Re-check Ch217 vs Ch269 capture predicates.)";
        end else if (beq_v0_one_pass1plus_from_normal == 0) begin
            verdict = $sformatf("v0_set_by_ch215_restore (every steady-state BEQ retire saw $v0=1 set by CH215_WAIT — count=%0d. CONFIRMS Codex's hypothesis: the treadmill BEQ is an artifact of our Ch215 shim, NOT a hidden BIOS load. HARD STOP: the post-Ch215 thunk-chain search Ch264..Ch268 is closed as a shim artifact. Next substantive work: model a recurring IOP/SBUS responder with explicit state, OR step back to hardware-facing deliverables. All conclusions after Ch215 should be labeled \"under jmp_buf fallback semantics.\")",
                                beq_v0_one_pass1plus_from_ch215);
        end else if (beq_v0_one_pass1plus_from_ch215 == 0) begin
            verdict = $sformatf("v0_set_by_normal_retire (every steady-state BEQ retire saw $v0=1 set by a NORMAL instruction (state != CH215_WAIT), count=%0d. The shim is NOT the producer; the upstream search has merit. Continue widening Ch268 backwards.)",
                                beq_v0_one_pass1plus_from_normal);
        end else begin
            verdict = $sformatf("v0_set_by_mixed (steady-state BEQ retires split: CH215=%0d, normal=%0d. Inspect per-pass last-writer PCs above to identify which is which.)",
                                beq_v0_one_pass1plus_from_ch215, beq_v0_one_pass1plus_from_normal);
        end
        $display("[ch269] verdict=%s", verdict);
    endtask
`endif // CH269_V0_LINEAGE

`ifdef CH262_IOP_RESPONDER
    // ==============================================================
    // Ch262 — synthetic IOP responder skeleton (gated). Composes the
    // Ch261-proven chain (iop_exec_stub + iop_memory_map_stub +
    // iop_ram_stub + iop_dmac_reg_stub + iop-side intc_stub +
    // sif_dma_ee_ram_bridge_stub + dedicated ee_ram_stub landing)
    // and converts the bridge's last_seen rising edge into a 1-cycle
    // pulse that the wrapper-level `ch262_resp_pulse` wire feeds into
    // ee_bootstrap_mmio's iop_intc_inject_src_i path.
    //
    // The landing ee_ram_stub is SEPARATE from the BIOS-long EE RAM;
    // the responder's qword payload is irrelevant to BIOS behaviour
    // here, only the resulting INTC pulse matters.
    //
    // The responder fires ONCE per test run, at ~50 ms sim time —
    // late enough that BIOS has reached the Ch215 treadmill, early
    // enough to leave plenty of headroom inside the long-run 800 ms
    // watchdog. Acceptance per Codex's Ch262 framing:
    //   - INTC pending appears after responder activity?
    //   - BIOS consumes/clears it?
    //   - Does treadmill pass / retire / hot-PC pattern change vs
    //     the Ch260 baseline (intc_quiet, retired=24,029,051,
    //     Ch217 passes=8)?
    // ==============================================================

    localparam int                CH262_RAM_BYTES   = 4 * 1024;
    localparam int                CH262_RAM_ADDR_W  = $clog2(CH262_RAM_BYTES);
    localparam int                CH262_EE_BYTES    = 1024 * 1024;
    localparam int                CH262_EE_ADDR_W   = $clog2(CH262_EE_BYTES);
    localparam logic [31:0]       CH262_SCRIPT_BASE = 32'h0000_0400;
    localparam logic [31:0]       CH262_PAYLOAD_BASE= 32'h0000_0200;
    // Ch263 overrides Ch262's isolated landing target with a real
    // BIOS-polled kernel-data offset. The Ch218 v5 capture surfaced
    // PC=0x00030014 scanning 0x80030000..0x80033ff0 every pass (4074
    // reads in pass=1 alone, all returning zero); 0x80030200 is a
    // mid-range slot in that scan, qword-aligned, low risk of being
    // BIOS code or stack. Phys offset = 0x00030200 (kseg0 strips
    // 0x80000000); the EE map's bridge_wr_addr expects phys.
`ifdef CH263_KERNEL_DATA_MUTATE
    localparam logic [31:0]       CH262_EE_LANDING  = 32'h0003_0200;
`else
    localparam logic [31:0]       CH262_EE_LANDING  = 32'h0008_0000;
`endif

    // Responder-side opcodes — must match iop_exec_stub.
    localparam logic [31:0] CH262_OP_HALT     = 32'h0000_0000;
    localparam logic [31:0] CH262_OP_WRITE    = 32'h0000_0001;
    localparam logic [31:0] CH262_OP_READ     = 32'h0000_0002;
    localparam logic [31:0] CH262_OP_WAIT_IRQ = 32'h0000_0003;

    // Architectural addresses reached by the responder script.
    localparam logic [31:0] CH262_DMAC_MADR = 32'h1F80_1520;
    localparam logic [31:0] CH262_DMAC_BCR  = 32'h1F80_1524;
    localparam logic [31:0] CH262_DMAC_CHCR = 32'h1F80_1528;
    localparam logic [31:0] CH262_INTC_STAT = 32'h1F80_1070;
    localparam logic [31:0] CH262_INTC_MASK = 32'h1F80_1074;

    // ----- IOP responder wires -----
    logic        ch262_exec_go;
    logic        ch262_exec_halt;
    logic [7:0]  ch262_exec_pc;

    logic        ch262_cpu_rd_en;
    logic [31:0] ch262_cpu_rd_addr;
    logic [31:0] ch262_cpu_rd_data;
    logic        ch262_cpu_rd_valid;
    logic        ch262_cpu_wr_en;
    logic [31:0] ch262_cpu_wr_addr;
    logic [31:0] ch262_cpu_wr_data;
    logic [3:0]  ch262_cpu_wr_be;

    logic        ch262_iop_cpu_irq;

    logic        ch262_iop_ram_rd_en;
    logic [20:0] ch262_iop_ram_rd_addr;
    logic [31:0] ch262_iop_ram_rd_data;
    logic        ch262_iop_ram_rd_valid;
    logic        ch262_iop_ram_wr_en;
    logic [20:0] ch262_iop_ram_wr_addr;
    logic [31:0] ch262_iop_ram_wr_data;
    logic [3:0]  ch262_iop_ram_wr_be;
    logic [7:0]  ch262_iop_ram_master_id;

    logic        ch262_map_dmac_rd_en;
    logic [3:0]  ch262_map_dmac_rd_addr;
    logic [31:0] ch262_map_dmac_rd_data;
    logic        ch262_map_dmac_rd_valid;
    logic        ch262_map_dmac_wr_en;
    logic [3:0]  ch262_map_dmac_wr_addr;
    logic [31:0] ch262_map_dmac_wr_data;

    logic        ch262_map_intc_rd_en;
    logic [7:0]  ch262_map_intc_rd_addr;
    logic [31:0] ch262_map_intc_rd_data;
    logic        ch262_map_intc_rd_valid;
    logic        ch262_map_intc_wr_en;
    logic [7:0]  ch262_map_intc_wr_addr;
    logic [31:0] ch262_map_intc_wr_data;

    logic        ch262_dma_rd_en;
    logic [31:0] ch262_dma_rd_addr;
    logic [7:0]  ch262_dma_master_id;
    logic [31:0] ch262_dma_rd_data;
    logic        ch262_dma_rd_valid;

    logic        ch262_tb_br_wr_en;
    logic [31:0] ch262_tb_br_wr_addr;
    logic [31:0] ch262_tb_br_wr_data;
    logic [3:0]  ch262_tb_br_wr_be;

    logic        ch262_ep_valid;
    logic [31:0] ch262_ep_data;
    logic        ch262_ep_last;
    logic        ch262_ep_ready;
    logic        ch262_dmac_irq;
    logic        ch262_dmac_busy;
    logic [31:0] ch262_dmac_done_count;

    wire         ch262_eebr_wr_en;
    wire [31:0]  ch262_eebr_wr_addr;
    wire [127:0] ch262_eebr_wr_data;
    wire [15:0]  ch262_eebr_wr_be;
    wire [7:0]   ch262_eebr_master_id;
    wire         ch262_eebr_last_seen;

    iop_exec_stub #(.SCRIPT_BASE(CH262_SCRIPT_BASE)) u_ch262_exec (
        .clk(clk), .rst_n(rst_n),
        .go_i(ch262_exec_go),
        .map_rd_en(ch262_cpu_rd_en), .map_rd_addr(ch262_cpu_rd_addr),
        .map_rd_data(ch262_cpu_rd_data), .map_rd_valid(ch262_cpu_rd_valid),
        .map_wr_en(ch262_cpu_wr_en), .map_wr_addr(ch262_cpu_wr_addr),
        .map_wr_data(ch262_cpu_wr_data), .map_wr_be(ch262_cpu_wr_be),
        .cpu_irq(ch262_iop_cpu_irq),
        .halt_o(ch262_exec_halt), .pc_o(ch262_exec_pc),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    iop_memory_map_stub u_ch262_iop_map (
        .clk(clk), .rst_n(rst_n),
        .iop_rd_en(ch262_cpu_rd_en), .iop_rd_addr(ch262_cpu_rd_addr),
        .iop_rd_data(ch262_cpu_rd_data), .iop_rd_valid(ch262_cpu_rd_valid),
        .iop_wr_en(ch262_cpu_wr_en), .iop_wr_addr(ch262_cpu_wr_addr),
        .iop_wr_data(ch262_cpu_wr_data), .iop_wr_be(ch262_cpu_wr_be),
        .master_id(8'd2),
        .bridge_wr_en(ch262_tb_br_wr_en), .bridge_wr_addr(ch262_tb_br_wr_addr),
        .bridge_wr_data(ch262_tb_br_wr_data), .bridge_wr_be(ch262_tb_br_wr_be),
        .bridge_master_id(8'd0),
        .dma_rd_en(ch262_dma_rd_en), .dma_rd_addr(ch262_dma_rd_addr),
        .dma_master_id(ch262_dma_master_id),
        .dma_rd_data(ch262_dma_rd_data), .dma_rd_valid(ch262_dma_rd_valid),
        .sif_rd_en(), .sif_rd_addr(),
        .sif_rd_data(32'd0), .sif_rd_valid(1'b0),
        .sif_wr_en(), .sif_wr_addr(), .sif_wr_data(),
        .iop_dmac_rd_en(ch262_map_dmac_rd_en), .iop_dmac_rd_addr(ch262_map_dmac_rd_addr),
        .iop_dmac_rd_data(ch262_map_dmac_rd_data), .iop_dmac_rd_valid(ch262_map_dmac_rd_valid),
        .iop_dmac_wr_en(ch262_map_dmac_wr_en), .iop_dmac_wr_addr(ch262_map_dmac_wr_addr),
        .iop_dmac_wr_data(ch262_map_dmac_wr_data),
        .iop_intc_rd_en(ch262_map_intc_rd_en), .iop_intc_rd_addr(ch262_map_intc_rd_addr),
        .iop_intc_rd_data(ch262_map_intc_rd_data), .iop_intc_rd_valid(ch262_map_intc_rd_valid),
        .iop_intc_wr_en(ch262_map_intc_wr_en), .iop_intc_wr_addr(ch262_map_intc_wr_addr),
        .iop_intc_wr_data(ch262_map_intc_wr_data),
        .input_p1(32'd0), .input_p2(32'd0),
        .bios_rd_en(), .bios_rd_addr(),
        .bios_rd_data(32'd0), .bios_rd_valid(1'b0),
        .ram_rd_en(ch262_iop_ram_rd_en), .ram_rd_addr(ch262_iop_ram_rd_addr),
        .ram_rd_data(ch262_iop_ram_rd_data), .ram_rd_valid(ch262_iop_ram_rd_valid),
        .ram_wr_en(ch262_iop_ram_wr_en), .ram_wr_addr(ch262_iop_ram_wr_addr),
        .ram_wr_data(ch262_iop_ram_wr_data), .ram_wr_be(ch262_iop_ram_wr_be),
        .ram_master_id(ch262_iop_ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    iop_ram_stub #(.SIZE_BYTES(CH262_RAM_BYTES)) u_ch262_iop_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(ch262_iop_ram_rd_en),
        .rd_addr(ch262_iop_ram_rd_addr[CH262_RAM_ADDR_W-1:0]),
        .rd_data(ch262_iop_ram_rd_data), .rd_valid(ch262_iop_ram_rd_valid),
        .wr_en(ch262_iop_ram_wr_en),
        .wr_addr(ch262_iop_ram_wr_addr[CH262_RAM_ADDR_W-1:0]),
        .wr_data(ch262_iop_ram_wr_data), .wr_be(ch262_iop_ram_wr_be),
        .master_id(ch262_iop_ram_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    iop_dmac_reg_stub u_ch262_dmac (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(ch262_map_dmac_wr_en), .reg_rd_en(ch262_map_dmac_rd_en),
        .reg_offset(ch262_map_dmac_wr_en ? ch262_map_dmac_wr_addr : ch262_map_dmac_rd_addr),
        .reg_wr_data(ch262_map_dmac_wr_data),
        .reg_rd_data(ch262_map_dmac_rd_data), .reg_rd_valid(ch262_map_dmac_rd_valid),
        .mem_rd_en(ch262_dma_rd_en), .mem_rd_addr(ch262_dma_rd_addr),
        .mem_master_id(ch262_dma_master_id),
        .mem_rd_data(ch262_dma_rd_data), .mem_rd_valid(ch262_dma_rd_valid),
        .ep_valid(ch262_ep_valid), .ep_data(ch262_ep_data), .ep_last(ch262_ep_last),
        .ep_ready(ch262_ep_ready),
        .irq_completion_o(ch262_dmac_irq),
        .busy_o(ch262_dmac_busy), .done_count_o(ch262_dmac_done_count),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    sif_dma_ee_ram_bridge_stub #(
        .DEST_BASE_ADDR(CH262_EE_LANDING),
        .MASTER_ID(8'd5)
    ) u_ch262_ee_bridge (
        .clk(clk), .rst_n(rst_n),
        .in_valid(ch262_ep_valid), .in_data(ch262_ep_data), .in_last(ch262_ep_last),
        .in_ready(ch262_ep_ready),
        .bridge_wr_en(ch262_eebr_wr_en), .bridge_wr_addr(ch262_eebr_wr_addr),
        .bridge_wr_data(ch262_eebr_wr_data), .bridge_wr_be(ch262_eebr_wr_be),
        .bridge_master_id(ch262_eebr_master_id),
        .last_seen_o(ch262_eebr_last_seen),
        .rewind_i(1'b0)
    );

    // Ch262 isolated landing buffer — instantiated only when Ch263 is
    // NOT defined. Under Ch263, the responder bridge writes are
    // muxed into the BIOS-long shared u_ee_ram via the EE map's
    // bridge_wr_* port instead (see the wrapper-scope priority mux
    // below).
`ifndef CH263_KERNEL_DATA_MUTATE
    ee_ram_stub #(.SIZE_BYTES(CH262_EE_BYTES)) u_ch262_ee_ram (
        .clk(clk), .rst_n(rst_n),
        .rd_en(1'b0), .rd_addr({CH262_EE_ADDR_W{1'b0}}),
        .rd_data(), .rd_valid(),
        .wr_en(ch262_eebr_wr_en),
        .wr_addr(ch262_eebr_wr_addr[CH262_EE_ADDR_W-1:0]),
        .wr_data(ch262_eebr_wr_data), .wr_be(ch262_eebr_wr_be),
        .master_id(ch262_eebr_master_id),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );
`endif // !CH263_KERNEL_DATA_MUTATE — u_ch262_ee_ram only exists in pure-Ch262 mode

    // IOP INTC for the responder's WAIT_IRQ semantics.
    logic [15:0] ch262_iop_irq_src;
    assign ch262_iop_irq_src = {15'd0, ch262_dmac_irq};

    intc_stub #(
        .INTC_STAT_OFFSET(8'h70),
        .INTC_MASK_OFFSET(8'h74)
    ) u_ch262_iop_intc (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(ch262_map_intc_wr_en), .reg_rd_en(ch262_map_intc_rd_en),
        .reg_addr(ch262_map_intc_wr_en ? ch262_map_intc_wr_addr : ch262_map_intc_rd_addr),
        .reg_wr_data(ch262_map_intc_wr_data),
        .reg_rd_data(ch262_map_intc_rd_data), .reg_rd_valid(ch262_map_intc_rd_valid),
        .irq_src(ch262_iop_irq_src), .cpu_irq(ch262_iop_cpu_irq),
        .ev_valid(), .ev_subsys(), .ev_event(),
        .ev_arg0(), .ev_arg1(), .ev_arg2(), .ev_arg3(), .ev_flags()
    );

    // -------- Convert bridge.last_seen_o (level-held) into a 1-cycle
    // rising-edge pulse, and route it to the wrapper-level
    // ch262_resp_pulse wire that OR's into iop_intc_inject_src_i.
    logic        ch262_last_seen_q;
    logic        ch262_pulse_q;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch262_last_seen_q <= 1'b0;
            ch262_pulse_q     <= 1'b0;
        end else begin
            ch262_last_seen_q <= ch262_eebr_last_seen;
            ch262_pulse_q     <= ch262_eebr_last_seen && !ch262_last_seen_q;
        end
    end
    assign ch262_resp_pulse = {15'd0, ch262_pulse_q};

    // -------- Preload helpers + delayed go_i pulse. The exec script
    // matches the Ch261 standalone TB exactly.
    task automatic ch262_bridge_write(input logic [31:0] addr,
                                      input logic [31:0] data);
        @(negedge clk);
        ch262_tb_br_wr_en   = 1'b1;
        ch262_tb_br_wr_addr = addr;
        ch262_tb_br_wr_data = data;
        ch262_tb_br_wr_be   = 4'b1111;
        @(negedge clk);
        ch262_tb_br_wr_en   = 1'b0;
        ch262_tb_br_wr_addr = 32'd0;
        ch262_tb_br_wr_data = 32'd0;
        ch262_tb_br_wr_be   = 4'd0;
    endtask

    task automatic ch262_emit_op(input int slot,
                                 input logic [31:0] opcode,
                                 input logic [31:0] addr_or_tgt,
                                 input logic [31:0] data_or_exp);
        logic [31:0] base;
        base = CH262_SCRIPT_BASE + (slot << 4);
        ch262_bridge_write(base + 32'd0,  opcode);
        ch262_bridge_write(base + 32'd4,  addr_or_tgt);
        ch262_bridge_write(base + 32'd8,  data_or_exp);
        ch262_bridge_write(base + 32'd12, 32'd0);
    endtask

    initial begin
        ch262_exec_go        = 1'b0;
        ch262_tb_br_wr_en    = 1'b0;
        ch262_tb_br_wr_addr  = 32'd0;
        ch262_tb_br_wr_data  = 32'd0;
        ch262_tb_br_wr_be    = 4'd0;

        // Wait for the main TB reset deassert + a bit of slack, then
        // preload the responder's IOP RAM (payload + script).
        @(posedge rst_n);
        repeat (8) @(posedge clk);

        ch262_bridge_write(CH262_PAYLOAD_BASE + 32'h00, 32'hDEAD_BEEF);
        ch262_bridge_write(CH262_PAYLOAD_BASE + 32'h04, 32'hC0FF_EE00);
        ch262_bridge_write(CH262_PAYLOAD_BASE + 32'h08, 32'h1234_5678);
        ch262_bridge_write(CH262_PAYLOAD_BASE + 32'h0C, 32'hCAFE_F00D);

        ch262_emit_op(0, CH262_OP_WRITE,    CH262_INTC_MASK, 32'h0000_0001);
        ch262_emit_op(1, CH262_OP_WRITE,    CH262_DMAC_MADR, CH262_PAYLOAD_BASE);
        ch262_emit_op(2, CH262_OP_WRITE,    CH262_DMAC_BCR,  32'h0000_0004);
        ch262_emit_op(3, CH262_OP_WRITE,    CH262_DMAC_CHCR, 32'h0000_0001);
        ch262_emit_op(4, CH262_OP_WAIT_IRQ, 32'd0,           32'd0);
        ch262_emit_op(5, CH262_OP_WRITE,    CH262_INTC_STAT, 32'h0000_0001);
        ch262_emit_op(6, CH262_OP_READ,     32'h1F80_152C,   32'd0);
        ch262_emit_op(7, CH262_OP_HALT,     32'd0,           32'd0);

        // Delay until ~50 ms sim time so BIOS has reached the Ch215
        // treadmill (the Ch217 verdict at Ch259 saw 8 passes across
        // the ~800 ms watchdog window, so passes happen at roughly
        // 100 ms intervals — 50 ms lands the pulse comfortably
        // between passes).
        #50_000_000;

        $display("[tb_ee_core_bios_smoke] Ch262 responder go_i pulse at t=%0t (BIOS expected mid-treadmill)", $time);
        @(negedge clk);
        ch262_exec_go = 1'b1;
        @(negedge clk);
        ch262_exec_go = 1'b0;

        // Observers (passive). Print when the responder halts and when
        // the last_seen pulse propagates.
        @(posedge ch262_exec_halt);
        $display("[tb_ee_core_bios_smoke] Ch262 responder halted at t=%0t (dmac_done_count=%0d)",
                 $time, ch262_dmac_done_count);
    end

    always_ff @(posedge clk) begin
        if (rst_n && ch262_pulse_q) begin
            $display("[tb_ee_core_bios_smoke] Ch262 responder pulse fired at t=%0t (1-cycle, injects bit 0 into ee bootstrap I_STAT)",
                     $time);
        end
    end

`ifdef CH263_KERNEL_DATA_MUTATE
    // --------------------------------------------------------------
    // Ch263 — retarget the responder's DMA into the BIOS-polled
    // kernel-data scan range. The Ch262 bridge_wr_* outputs are now
    // muxed (priority: responder wins while ch262_eebr_wr_en is
    // high) into the EE map's bridge_wr_* port. The Ch262 INTC pulse
    // path is unchanged — the bridge's last_seen rising edge still
    // injects I_STAT[0] — so we now produce BOTH events (interrupt
    // + EE-RAM mutation) on the same responder pass.
    //
    // No new RTL. The mux below is the only wiring change vs Ch262.
    // The bridge bridge_wr_data is 128 bits; we keep ee_map's 128-bit
    // bridge_wr_data shape so the qword writes one slot in the BIOS
    // EE RAM.
    assign ee_map_br_wr_en     = install_br_wr_en | ch262_eebr_wr_en;
    assign ee_map_br_wr_addr   = ch262_eebr_wr_en ? ch262_eebr_wr_addr   : install_br_wr_addr;
    assign ee_map_br_wr_data   = ch262_eebr_wr_en ? ch262_eebr_wr_data   : install_br_wr_data;
    assign ee_map_br_wr_be     = ch262_eebr_wr_en ? ch262_eebr_wr_be     : install_br_wr_be;
    assign ee_map_br_master_id = ch262_eebr_wr_en ? ch262_eebr_master_id : install_br_master_id;

    // --------- Observer: baseline + write + post-DMA BIOS reads ---
    // The Ch263 target lives at virtual 0x80030200 (kseg0) → phys
    // 0x00030200 → ee_ram qword index 0x30200/16 = 0x3020.
    localparam logic [31:0] CH263_TARGET_VIRT = 32'h8003_0200;
    localparam logic [31:0] CH263_TARGET_PHYS = 32'h0003_0200;
    localparam int          CH263_TARGET_QWIDX = 32'h3020;  // /16

    logic        ch263_baseline_captured;
    logic [127:0] ch263_baseline_qword;
    logic        ch263_wr_observed;
    logic [127:0] ch263_wr_value;
    int          ch263_bios_post_reads;
    logic        ch263_seen_mutated_data;

    initial begin
        ch263_baseline_captured = 1'b0;
        ch263_baseline_qword    = 128'd0;
        ch263_wr_observed       = 1'b0;
        ch263_wr_value          = 128'd0;
        ch263_bios_post_reads   = 0;
        ch263_seen_mutated_data = 1'b0;

        // Wait until reset settles + a bit, then snap baseline
        // (BIOS hasn't written here yet at this point).
        @(posedge rst_n);
        repeat (16) @(posedge clk);
        ch263_baseline_qword     = u_ee_ram.mem[CH263_TARGET_QWIDX];
        ch263_baseline_captured  = 1'b1;
        $display("[tb_ee_core_bios_smoke] Ch263 baseline @ EE-virt 0x%08h (qword idx 0x%04h) = 0x%032h",
                 CH263_TARGET_VIRT, CH263_TARGET_QWIDX, ch263_baseline_qword);
    end

    // Latch the responder's write value when bridge_wr_en pulses at
    // our target address. The bridge address is phys; check it.
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch263_wr_observed <= 1'b0;
            ch263_wr_value    <= 128'd0;
        end else if (ch262_eebr_wr_en
                  && ch262_eebr_wr_addr == CH263_TARGET_PHYS
                  && !ch263_wr_observed) begin
            ch263_wr_observed <= 1'b1;
            ch263_wr_value    <= ch262_eebr_wr_data;
            $display("[tb_ee_core_bios_smoke] Ch263 responder wrote 0x%032h to EE-phys 0x%08h at t=%0t",
                     ch262_eebr_wr_data, ch262_eebr_wr_addr, $time);
        end
    end

    // Track BIOS reads at our target qword AFTER the responder write.
    // The Ch218 capture showed BIOS scanning the kernel-data range
    // every pass with PC=0x00030014 walking words. We count any read
    // from EE-virt 0x80030200..0x8003020F (= phys 0x00030200..0F)
    // after ch263_wr_observed has latched, and notice if any of
    // them comes back non-zero (i.e. BIOS sees the mutation).
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            ch263_bios_post_reads   <= 0;
            ch263_seen_mutated_data <= 1'b0;
        end else if (ch263_wr_observed
                  && ee_map_ev_valid
                  && ee_map_ev_subsys == trace_pkg::SUBSYS_MEM
                  && ee_map_ev_event  == trace_pkg::EV_READ
                  && (ee_map_ev_arg0[31:0] >= CH263_TARGET_PHYS)
                  && (ee_map_ev_arg0[31:0] <  CH263_TARGET_PHYS + 32'h10)) begin
            ch263_bios_post_reads <= ch263_bios_post_reads + 1;
            // ev_arg1 hardcoded zero for EE map EV_READ — but we can
            // sample the live ee_ram word at the address directly.
            // Just flag that BIOS touched the slot at all; "mutated
            // data observed" is implied by the address-and-time gate.
            ch263_seen_mutated_data <= 1'b1;
        end
    end

    // Final Ch263 verdict — printed on timeout / halt path.
    final begin
        string verdict;
        $display("[tb_ee_core_bios_smoke] Ch263 summary:");
        $display("[tb_ee_core_bios_smoke]   baseline_captured  = %0b  baseline_qword = 0x%032h",
                 ch263_baseline_captured, ch263_baseline_qword);
        $display("[tb_ee_core_bios_smoke]   wr_observed        = %0b  wr_value       = 0x%032h",
                 ch263_wr_observed, ch263_wr_value);
        $display("[tb_ee_core_bios_smoke]   bios_post_reads    = %0d  seen_mutation  = %0b",
                 ch263_bios_post_reads, ch263_seen_mutated_data);
        if (!ch263_wr_observed) begin
            verdict = "responder_did_not_write_target (Ch263 wiring bug — check bridge mux)";
        end else if (ch263_bios_post_reads == 0) begin
            verdict = "kernel_mutation_unobserved (responder wrote but BIOS never read 0x80030200..0F afterwards — wrong slot, or BIOS exited its scan)";
        end else begin
            // The Ch217 verdict elsewhere in the log tells whether the
            // treadmill state changed; here we just label the reads.
            verdict = "kernel_mutation_observed (BIOS read the slot after responder wrote; cross-check Ch217 verdict for flow change)";
        end
        $display("[tb_ee_core_bios_smoke] Ch263 verdict_class = %s", verdict);
        $display("[tb_ee_core_bios_smoke]   (final flow change is determined by Ch217 verdict + retire-count delta vs Ch260 baseline)");
    end
`endif // CH263_KERNEL_DATA_MUTATE
`endif // CH262_IOP_RESPONDER

endmodule : tb_ee_core_bios_smoke
