// retroDE_ps2 — ee_memory_map_stub // // EE-visible address decode. Wave 2.7 revision added a DMAC read-master // port and a RAM region that routes to ee_ram_stub. Wave 3 reverse- // direction addition: a bridge write-master port for SIF egress landing // (128-bit), mirroring the IOP map's bridge port but in the EE→RAM // write direction. EE-core chapter 3 addition: a CPU-side EE DMAC // channel-2 (GIF) register write port, decoded at phys[28:12] == // 17'h1_000A (i.e. 0x1000_A000-0x1000_AFFF, 4 KiB per-channel window). // Only ch2 is decoded — other EE DMAC channels are intentionally not // routed yet. Mirrors the IOP map's single-channel ch9 decode pattern. // EE-core chapter 4 addition: a read counterpart at the same decode. // The DMAC response joins the existing EE CPU read pipeline alongside // BIOS and EE RAM, 1-cycle latency, so software can poll CHCR.start // or DONE_COUNT instead of burning fixed delay slots. EE-core chapter // 5 addition: an EE INTC register window at phys[28:12] == 17'h1_000F // (0x1000_F000-0x1000_FFFF) with both read and write, so the EE core // can program INTC_MASK, observe INTC_STAT, and ack via W1C — mirrors // iop_memory_map_stub's IOP INTC decode but at the EE physical slot. // Chapter 8 addition (first machine-state bring-up): an "EE bootstrap // MMIO" window at phys[28:16] == 13'h1F80 (0x1F80_0000-0x1F80_FFFF, // 64 KiB) covering MCH / SBUS / RDRAM-init registers the BIOS probes // very early. Without decode, CPU reads there returned 0xDEADBEEF and // that poison eventually wedged the core in a self-referential list // walk. Routed to ee_bootstrap_mmio_stub with latched read/write // semantics so read/modify/write sequences stay self-consistent. // // Contract refs: // docs/stub_module_plan.md (Wave 1, item 3) // docs/wave25_memory_backed_dma_plan.md (DMAC as memory client) // docs/wave26_multi_beat_dma_plan.md (multi-beat transfers) // docs/contracts/memory.md // // Address semantics: // - EE fetch / CPU data port (32-bit data, CPU-visible virtual address): // kseg0/kseg1 aliasing is modelled via `phys = ee_addr[28:0]` for // both reads and writes. Regions decoded off the stripped phys: // BIOS window phys[28:22] == 7'h7F (reads only) // EE RAM window phys[28:25] == 4'h0 (reads and writes) // EE DMAC ch2 (GIF) phys[28:12] == 17'h1_000A (reads and writes) // → offset phys[7:0] passes to dmac_reg_stub // (CHCR=0x00, MADR=0x10, QWC=0x20, TADR=0x30, // DONE_COUNT=0x40) // EE INTC phys[28:12] == 17'h1_000F (reads and writes) // → offset phys[7:0] passes to intc_stub // (INTC_STAT=0x00, INTC_MASK=0x10) // EE bootstrap MMIO phys[28:16] == 13'h1F80 (reads and writes) // → offset phys[15:0] passes to // ee_bootstrap_mmio_stub (latched reg file) // Anything else on this port is UNMAPPED. // - DMAC read port (128-bit data): treats its address as a physical bus // address directly. No kseg strip. RAM window decoded at // dmac_addr[31:25] == 7'b0 (i.e., 0x00000000 - 0x01FFFFFF, 32 MiB). // - Bridge write port (128-bit data): SIF egress bridge lands qwords // at physical addresses in the same RAM window (bridge_wr_addr // [31:25] == 7'b0). No kseg strip; writes bypass CPU side entirely. // Caller drives its own master_id for trace attribution (convention: // 5 = SIF EE-side bridge). // // 32-bit ↔ 128-bit bridging for CPU RAM access: // EE RAM is 128-bit qword-addressable. A 32-bit CPU read selects one // of four lanes by ee_rd_addr[3:2] on the response side. A 32-bit CPU // write broadcasts ee_wr_data into the matching lane of the 128-bit // write bus, with ram_wr_be[15:0] masked so only the four bytes of // that lane are enabled (other lanes pass through as no-op). This is // the same technique the sif_dma_ee_ram_bridge uses to build a qword // from streamed 32-bit beats, but per-access instead of accumulated. // // Arbitration (for the shared 128-bit ram_rd / ram_wr ports): // Reads: CPU wins over DMAC on same-cycle collision. // Writes: CPU wins over bridge on same-cycle collision. // TBs sequence masters in time; priority is defensive — on a real // collision the loser's response data is undefined, documented not // guarded. Mirrors the IOP map's CPU-vs-bridge write arbitration. // // Region ids (shared with ee_ram_stub and bios_rom_stub): // 0 BIOS // 1 EE_RAM // 7 EE_DMAC (chapter 3 addition; avoids collision with IOP region ids // 2..6 used by iop_memory_map_stub) // 8 EE_INTC (chapter 5 addition) // 9 EE_MISC_MMIO (chapter 8 addition; 0x1F80_xxxx bootstrap window) // 10 EE_BIU (chapter 9 addition; 0xFFFE_0xxx window) // 11 USEG_SHADOW (ch34 addition; first 4 MiB of useg, disjoint // backing from kseg0 RAM after the ch33 de-aliasing) // 0xFF UNMAPPED // // Master ids: // 0 = EE_IFETCH // 1 = DMAC // 5 = SIF EE-side bridge (reverse-direction landing) // // Trace semantics: // Map-layer events describe request routing (what was asked for, where it // was sent). Arg1 is intentionally 0 when the request is routed to a // backing store that will emit its own delivery event. `ee_ram_stub` // emits the delivery MEM READ with the returned data — the two events // together show the full ownership hop. // // Latency assumption (Wave 2.7): // This map assumes fixed one-cycle backing-store latency. `bios_rd_valid` // and `ram_rd_valid` are not consulted by the response pipeline — the // map asserts `*_rd_valid` one cycle after the request unconditionally. // All Wave 2.5/2.7 backing stubs honour that contract. If a later backing // store introduces wait states, the map must grow proper response // handshaking (tracking outstanding requests and gating `*_rd_valid` on // the downstream `_rd_valid`). Flagged for the next memory-side expansion. // // Trace payload schemas: // MAP.MEM READ arg0=addr arg1=0 arg2=master_id arg3=region_id // MAP.MEM UNMAPPED arg0=addr arg1=0xDEADBEEF arg2=master_id arg3=0xFF // (Wave 1 EE-write UNMAPPED unchanged: flags bit 0 indicates write) `timescale 1ns/1ps module ee_memory_map_stub import trace_pkg::*; #( // Ch64 — narrow visibility experiment. When enabled, CPU useg // stores whose phys falls in [CPU_USEG_TO_RAM_MIRROR_LO, // CPU_USEG_TO_RAM_MIRROR_HI) are mirrored into ee_ram via the // shared RAM write path, IN ADDITION to the existing write into // useg_shadow_mem. This restores a narrow slice of the kseg0<-> // useg aliasing that Ch33's separate-backings simplification // removed. Kept off by default; enabled by TB parameter override // (CH64_USEG_MIRROR=1 in the bios-smoke build). Ch63 proved the // BIOS writes memcpy source code via useg stores, so the memcpy // at pc=0x30014 reading via kseg0 sees zero. A narrow mirror // restores visibility for the affected address range only. parameter bit CPU_USEG_TO_RAM_MIRROR_EN = 1'b0, parameter logic [28:0] CPU_USEG_TO_RAM_MIRROR_LO = 29'h0004_4000, parameter logic [28:0] CPU_USEG_TO_RAM_MIRROR_HI = 29'h0004_4700, // Ch65 — symmetric narrow mirror for the destination direction. // When enabled, CPU kseg0/kseg1 stores that hit the RAM decode // AND whose phys lies in [CPU_KSEG0_TO_USEG_SHADOW_MIRROR_LO, // _HI) are ALSO committed to useg_shadow_mem via the existing // byte-enable write path, in addition to the normal ee_ram // write. This restores the second half of kseg0<->useg aliasing // for the memcpy destination region. Default off; enabled by TB // parameter override (CH65_KSEG_MIRROR=1 in the bios-smoke // build). Ch64 proved this BIOS pattern: build code via useg, // copy via kseg0, execute via useg. parameter bit CPU_KSEG0_TO_USEG_SHADOW_MIRROR_EN = 1'b0, parameter logic [28:0] CPU_KSEG0_TO_USEG_SHADOW_MIRROR_LO = 29'h001A_4000, parameter logic [28:0] CPU_KSEG0_TO_USEG_SHADOW_MIRROR_HI = 29'h001A_5000, // Ch70 — coherent RAM-alias mode. Promotes the two narrow Ch64/ // Ch65 mirror experiments into a single bidirectional aliasing // policy for the [LO, HI) phys window. When enabled, CPU writes // anywhere in the window mirror into the OTHER backing // (useg→ee_ram and kseg→useg_shadow), restoring the kseg0↔useg // aliasing real PS2 hardware has via the TLB while keeping // MMIO/DMAC/INTC/BIU/BIOS regions de-aliased per Ch33's intent // (those decode predicates short-circuit RAM eligibility, so // the alias never mirrors into them). Default off for // regression compatibility. Default window covers the first // 4 MiB — wide enough for the BIOS's RAM-resident copied code // (Ch64..Ch69 evidence) without touching kseg0 RAM beyond the // useg_shadow's 4 MiB span. parameter bit CPU_RAM_ALIAS_EN = 1'b0, parameter logic [28:0] CPU_RAM_ALIAS_LO = 29'h0000_0000, parameter logic [28:0] CPU_RAM_ALIAS_HI = 29'h0040_0000, // Ch145 — useg-shadow backing size. Default 1M words = 4 MiB // matches the original ch34 contract (useg first 4 MiB has its // own backing, disjoint from kseg0 RAM). For hardware builds // where useg is unused (e.g., the Ch123 PSMCT32 raster demo // runs the bootlet entirely from BIOS at 0xBFC0_0000 and the // GIF payload from RAM at phys 0x100 — no useg traffic at all), // override this to a small value (e.g., 1024 words = 4 KiB) to // shrink the inferred BRAM footprint by ~1024×. No address // decode changes; the index `ee_rd_phys[21:2]` is naturally // bounded by the smaller array on smaller sizes (any access // beyond returns whatever the synth tool maps; for the Ch123 // demo no such access ever happens). Keep the default to leave // all existing TBs byte-identical. parameter int unsigned USEG_SHADOW_WORDS_PARAM = 1024 * 1024, // Ch296 — useg-shadow backing enable. Default 1 = current behavior // EXACTLY (the `useg_shadow_mem` array and all its read/write logic // are instantiated; every existing BIOS/qbert/useg sim is byte- // identical). When 0, the array and its read/write logic are fully // removed from elaboration (wrapped in a generate-if), so the ~33k // flip-flops Quartus bit-blasts the array into on the board build // disappear. With the shadow disabled, useg READS return the // unmapped value (0xDEADBEEF via the existing read-mux fall-through, // because `ee_rd_is_useg_shadow` is forced to 0) and useg WRITES are // dropped (no-op). Every other region (BIOS, EE-RAM, GS-priv MMIO, // DMAC, INTC, BIU, bootstrap MMIO) is unchanged. The board PSMCT32 // raster demo never issues useg traffic (bootlet runs from BIOS at // 0xBFC0_0000, GIF payload from EE-RAM via DMAC), so disabling the // shadow on the board path is free. ONLY the board/synth top sets 0; // sim TBs and the BIOS/qbert path keep the default 1. parameter bit USEG_SHADOW_ENABLE = 1'b1 ) ( input logic clk, input logic rst_n, // ------------------------------------------------------------------ // EE fetch port (32-bit data, virtual address with kseg aliasing) // ------------------------------------------------------------------ input logic ee_rd_en, input logic [31:0] ee_rd_addr, output logic [31:0] ee_rd_data, output logic ee_rd_valid, input logic ee_wr_en, input logic [31:0] ee_wr_addr, input logic [31:0] ee_wr_data, input logic [3:0] ee_wr_be, // ------------------------------------------------------------------ // DMAC read port (128-bit data, physical address) // ------------------------------------------------------------------ input logic dmac_rd_en, input logic [31:0] dmac_rd_addr, output logic [127:0] dmac_rd_data, output logic dmac_rd_valid, // ------------------------------------------------------------------ // Bridge write port (Wave 3 reverse-direction landing). 128-bit data, // physical address. Caller provides its own master_id. Only EE RAM // region is mapped from this port in current scope. // ------------------------------------------------------------------ input logic bridge_wr_en, input logic [31:0] bridge_wr_addr, input logic [127:0] bridge_wr_data, input logic [15:0] bridge_wr_be, input logic [7:0] bridge_master_id, // ------------------------------------------------------------------ // Downstream to bios_rom_stub (EE fetch path) // ------------------------------------------------------------------ output logic bios_rd_en, output logic [21:0] bios_rd_addr, input logic [31:0] bios_rd_data, input logic bios_rd_valid, // ------------------------------------------------------------------ // Downstream to ee_ram_stub (DMAC read path). // Address is presented as a full-width RAM-local offset; consumers may // truncate to match their own backing-store address width. // ------------------------------------------------------------------ output logic ram_rd_en, output logic [24:0] ram_rd_addr, input logic [127:0] ram_rd_data, input logic ram_rd_valid, // ------------------------------------------------------------------ // Downstream RAM write port (Wave 3). Driven by bridge_wr_*; no // other master writes to EE RAM through this map in current scope. // ------------------------------------------------------------------ output logic ram_wr_en, output logic [24:0] ram_wr_addr, output logic [127:0] ram_wr_data, output logic [15:0] ram_wr_be, output logic [7:0] ram_master_id, // ------------------------------------------------------------------ // Downstream EE DMAC ch2 (GIF) register write port (chapter 3). // Fires when the CPU writes within 0x1000_A000-0x1000_AFFF. 8-bit // offset matches dmac_reg_stub's register surface (CHCR/MADR/QWC/ // TADR at 0x00/0x10/0x20/0x30). // ------------------------------------------------------------------ output logic ee_dmac_ch2_wr_en, output logic [7:0] ee_dmac_ch2_wr_addr, output logic [31:0] ee_dmac_ch2_wr_data, // ------------------------------------------------------------------ // Downstream EE DMAC ch2 (GIF) register read port (chapter 4). // 1-cycle latency mirrors BIOS and EE RAM reads. reg_rd_valid is // not consulted by the response pipeline — the map asserts // ee_rd_valid one cycle after the CPU request unconditionally, // same convention as the other backing stubs. // ------------------------------------------------------------------ output logic ee_dmac_ch2_rd_en, output logic [7:0] ee_dmac_ch2_rd_addr, input logic [31:0] ee_dmac_ch2_rd_data, input logic ee_dmac_ch2_rd_valid, // ------------------------------------------------------------------ // Downstream EE INTC register ports (chapter 5). Same shape as the // DMAC register ports: 8-bit shared offset surface, write-then- // read response pipeline joins the EE CPU mux. Offsets INTC_STAT // at 0x00 and INTC_MASK at 0x10 match intc_stub's default. // ------------------------------------------------------------------ output logic ee_intc_wr_en, output logic [7:0] ee_intc_wr_addr, output logic [31:0] ee_intc_wr_data, output logic ee_intc_rd_en, output logic [7:0] ee_intc_rd_addr, input logic [31:0] ee_intc_rd_data, input logic ee_intc_rd_valid, // ------------------------------------------------------------------ // Downstream EE bootstrap-MMIO ports (chapter 8). 16-bit window // offset (0x1F80_0000-0x1F80_FFFF, 64 KiB), 1-cycle read latency // matching the rest of the stub ecosystem. Routed to // ee_bootstrap_mmio_stub which latches writes and returns the // last-written value on read (so BIOS read/modify/write sequences // stay self-consistent instead of laundering 0xDEADBEEF into // data structures). // ------------------------------------------------------------------ output logic ee_misc_mmio_wr_en, output logic [15:0] ee_misc_mmio_wr_addr, output logic [31:0] ee_misc_mmio_wr_data, output logic [3:0] ee_misc_mmio_wr_be, output logic ee_misc_mmio_rd_en, output logic [15:0] ee_misc_mmio_rd_addr, input logic [31:0] ee_misc_mmio_rd_data, input logic ee_misc_mmio_rd_valid, // ------------------------------------------------------------------ // Ch112 — GS privileged-MMIO window (0x1200_0000-0x1200_FFFF, // 64 KiB). Routed to ee_gs_priv_bridge_stub, which accumulates // 32-bit half-writes per 8-byte slot and fires a 64-bit // gs_stub.reg_wr_* pulse on each EE half-write. The map // decode is symmetric with the misc-MMIO path: top 13 bits of // the 29-bit physical address tag the window. Read side is // not modelled at this scope (the Ch111 bridge is write-only, // matching the limited read coverage of the GS priv block). // TBs that don't model GS-priv MMIO can leave these ports // unconnected (named-port instantiation tolerates omitted // outputs). // ------------------------------------------------------------------ output logic ee_gs_priv_wr_en, output logic [15:0] ee_gs_priv_wr_addr, output logic [31:0] ee_gs_priv_wr_data, output logic [3:0] ee_gs_priv_wr_be, // ------------------------------------------------------------------ // EE BIU / cache-control window at 0xFFFE_0000-0xFFFE_0FFF (4 KiB). // Narrow, dedicated stub — kept architecturally separate from the // 0x1F80_xxxx bootstrap MMIO window so each decode surface has its // own region tag and its own evidence trail. // ------------------------------------------------------------------ output logic ee_biu_wr_en, output logic [11:0] ee_biu_wr_addr, output logic [31:0] ee_biu_wr_data, output logic [3:0] ee_biu_wr_be, output logic ee_biu_rd_en, output logic [11:0] ee_biu_rd_addr, input logic [31:0] ee_biu_rd_data, input logic ee_biu_rd_valid, // ------------------------------------------------------------------ // Trace (shared; priority EE > DMAC on collision) // ------------------------------------------------------------------ output logic ev_valid, output subsys_e ev_subsys, output event_e ev_event, output logic [63:0] ev_arg0, output logic [63:0] ev_arg1, output logic [63:0] ev_arg2, output logic [63:0] ev_arg3, output logic [31:0] ev_flags ); localparam logic [63:0] REGION_BIOS = 64'd0; localparam logic [63:0] REGION_EE_RAM = 64'd1; localparam logic [63:0] REGION_EE_DMAC = 64'd7; localparam logic [63:0] REGION_EE_INTC = 64'd8; localparam logic [63:0] REGION_EE_MISC_MMIO = 64'd9; localparam logic [63:0] REGION_EE_BIU = 64'd10; // Ch33: dedicated tag for the useg-shadow backing (first 4 MiB of // useg, disjoint from kseg0 RAM). Kept as a separate region so // trace/post-run analysis can distinguish shadow hits from real // ee_ram hits. localparam logic [63:0] REGION_USEG_SHADOW = 64'd11; // Ch112: GS-privileged-MMIO window (0x1200_0000-0x1200_FFFF). // Routed to ee_gs_priv_bridge_stub. localparam logic [63:0] REGION_EE_GS_PRIV = 64'd12; // Ch287: EE DMAC global control/status registers at 0x1000_E000. // Hosts D_CTRL/D_STAT/D_PCR/D_SQWC/D_RBSR/D_RBOR. Instantiated // INTERNALLY (ee_dmac_ctrl_stub) so existing TBs don't need to // route new ports. localparam logic [63:0] REGION_EE_DMAC_CTRL = 64'd13; // Ch288: EE DMAC per-channel register surfaces for channels NOT // covered by a dedicated transfer-FSM stub. ch0 VIF0, ch1 VIF1, // ch3 IPU_FROM, ch4 IPU_TO, ch5 SIF0 — each a 4 KiB window at // 0x1000_8000 + N*0x1000. Latched CHCR/MADR/QWC/TADR only; no // FSM. ch2 GIF stays on its dedicated external port. localparam logic [63:0] REGION_EE_DMAC_PASSIVE = 64'd14; localparam logic [63:0] REGION_UNMAPPED = 64'hFF; localparam logic [63:0] MASTER_EE_IFETCH = 64'd0; // EE CPU (fetch + data) localparam logic [63:0] MASTER_DMAC = 64'd1; // EE DMAC channel-2 (GIF) MMIO base — only channel decoded in this // chapter. phys[28:12] == EE_DMAC_CH2_BASE[28:12] identifies the // 4 KiB per-channel register window at 0x1000_A000. localparam logic [28:0] EE_DMAC_CH2_BASE = 29'h1000_A000; // EE INTC MMIO base at 0x1000_F000. localparam logic [28:0] EE_INTC_BASE = 29'h1000_F000; // Ch287 — EE DMAC global control/status base at 0x1000_E000 // (D_CTRL/D_STAT/D_PCR/D_SQWC/D_RBSR/D_RBOR). Window is 4 KiB to // match the discrimination pattern of the existing DMAC ch2 / INTC // bases; the live registers occupy only the first 256 bytes // (8-bit reg_offset), the remainder is silent within the window // (writes dropped + traced, reads return 0). localparam logic [28:0] EE_DMAC_CTRL_BASE = 29'h1000_E000; // EE bootstrap-MMIO window covers the full 0x1F80_0000-0x1F80_FFFF // range (64 KiB). Top 13 bits of the 29-bit physical address are // the discriminator — see ee_bootstrap_mmio_stub.sv for the // per-register latching semantics inside. localparam logic [12:0] EE_MISC_MMIO_TAG = 13'h1F80; // Ch112: GS privileged-MMIO window (PMODE/SMODE/DISPFB/DISPLAY/ // BGCOLOR/HWREG/etc.) at 0x1200_0000-0x1200_FFFF (64 KiB). // Routed to ee_gs_priv_bridge_stub. localparam logic [12:0] EE_GS_PRIV_TAG = 13'h1200; // EE BIU/cache-control window at 0xFFFE_0000-0xFFFE_0FFF (4 KiB, // phys 0x1FFE_0000-0x1FFE_0FFF after kseg1-strip). Narrow by design // — chapter 9 covers a single architecturally-distinct surface the // real BIOS touches for BIU/cache config. See ee_biu_mmio_stub.sv. localparam logic [16:0] EE_BIU_MMIO_TAG = 17'h1FFE0; // ------------------------------------------------------------------ // Decode: EE CPU side (kseg-stripped), bridge side (physical), // DMAC side (physical). // ------------------------------------------------------------------ logic [28:0] ee_rd_phys; logic [28:0] ee_wr_phys; logic ee_rd_is_bios; logic ee_rd_is_ram; logic ee_rd_is_dmac_ch2; logic ee_rd_is_dmac_ctrl; // Ch287 logic ee_rd_is_dmac_passive; // Ch288 (ch0/1/3/4/5) logic ee_rd_is_intc; logic ee_rd_is_misc_mmio; logic ee_rd_is_biu; logic ee_wr_is_ram; logic ee_wr_is_dmac_ch2; logic ee_wr_is_dmac_ctrl; // Ch287 logic ee_wr_is_dmac_passive; // Ch288 logic ee_wr_is_intc; logic ee_wr_is_misc_mmio; logic ee_wr_is_biu; logic ee_wr_is_gs_priv; logic [21:0] ee_bios_offset; logic [24:0] ee_rd_ram_offset; logic [24:0] ee_wr_ram_offset; // ------------------------------------------------------------------ // Chapter 33 — useg de-aliasing experiment. Prior behavior: phys = // addr[28:0] strip treated useg (0x0000_0000-0x7FFF_FFFF) and kseg0 // (0x8000_0000-0x9FFF_FFFF) as the same physical word. Real MIPS // has useg TLB-mapped, so that alias is only "correct" when the // BIOS has programmed a TLB entry. Ch32 proved BIOS issues ZERO // TLBWI/TLBWR/TLBP/TLBR pre-ENTRY14 — so real HW's useg is // unmapped in that window, and our alias is too permissive. // // Experimental fix: route useg accesses whose phys lies in // [0, 4 MiB) to a dedicated in-map shadow backing, DISJOINT from // the kseg0/kseg1 RAM. Keeps useg reads/writes self-consistent // (unlike a zero/sink model) so any genuine useg-internal work // the BIOS does still behaves, but de-aliases the ch12 memcpy // from the ch31 walker's kseg0 read. // // 4 MiB shadow = 1M × 32-bit words. Big but cheap in sim. // USEG = addr[31]==0 (0x0000_0000..0x7FFF_FFFF) // kseg0/kseg1/kseg2/3 keep their existing phys[28:0]-strip // behavior. // ------------------------------------------------------------------ logic ee_rd_is_useg; logic ee_wr_is_useg; logic ee_rd_is_useg_shadow; logic ee_wr_is_useg_shadow; assign ee_rd_phys = ee_rd_addr[28:0]; assign ee_wr_phys = ee_wr_addr[28:0]; assign ee_rd_is_useg = (ee_rd_addr[31] == 1'b0); assign ee_wr_is_useg = (ee_wr_addr[31] == 1'b0); // Shadow covers first 4 MiB: phys[28:22] == 0 → phys in [0, 0x3FFFFF]. // That's enough for the low-RAM init region the BIOS touches via // useg (ch12 memcpy at phys 0x1E28, ch23 table ops at phys // 0x200..0x3FF, etc.). Accesses beyond 4 MiB to useg fall through // to existing decode (will mostly land UNMAPPED). // When USEG_SHADOW_ENABLE=0 the shadow decode is forced low so useg // reads fall through to the 0xDEADBEEF unmapped mux arm and useg // writes are dropped (the write logic below is also generate-removed). // ee_rd_is_useg / ee_wr_is_useg are NOT gated — they still exclude // useg from the RAM decode (`!ee_*_is_useg`), preserving the Ch33 // de-aliasing intent regardless of this knob. assign ee_rd_is_useg_shadow = USEG_SHADOW_ENABLE && ee_rd_is_useg && (ee_rd_phys[28:22] == 7'd0); // Forward-decl: cpu_rd_hit_ram is declared further down (line ~593). // Used inside the ram-rd arbitration block and in the DMAC // response pipeline (Ch115 — see comment block below). assign ee_wr_is_useg_shadow = USEG_SHADOW_ENABLE && ee_wr_is_useg && (ee_wr_phys[28:22] == 7'd0); assign ee_rd_is_bios = (ee_rd_phys[28:22] == 7'h7F); // Exclude useg from RAM decode only — the TLB-aliasing wedge is // specifically about useg-store-to-RAM conflating with // kseg0-load-from-RAM. Useg aliases of MMIO/DMAC/INTC/BIU windows // remain routed as before; the existing test benches exercise // those via useg addressing (e.g. synthetic DMAC program at // 0x1000A010 with addr[31]=0) and must keep working. assign ee_rd_is_ram = !ee_rd_is_useg && (ee_rd_phys[28:25] == 4'h0); assign ee_rd_is_dmac_ch2 = (ee_rd_phys[28:12] == EE_DMAC_CH2_BASE[28:12]); assign ee_rd_is_dmac_ctrl = (ee_rd_phys[28:12] == EE_DMAC_CTRL_BASE[28:12]); // Ch287 // Ch288 — passive per-channel surface: ch0/1/3/4/5. Match the // 0x1000_8xxx..0x1000_Dxxx span, excluding 0x1000_Axxx (ch2 GIF // has its own dedicated stub via ee_dmac_ch2_* ports). assign ee_rd_is_dmac_passive = (ee_rd_phys[28:16] == 13'h1000) && ((ee_rd_phys[15:12] == 4'h8) || (ee_rd_phys[15:12] == 4'h9) || (ee_rd_phys[15:12] == 4'hB) || (ee_rd_phys[15:12] == 4'hC) || (ee_rd_phys[15:12] == 4'hD)); assign ee_rd_is_intc = (ee_rd_phys[28:12] == EE_INTC_BASE[28:12]); assign ee_rd_is_misc_mmio = (ee_rd_phys[28:16] == EE_MISC_MMIO_TAG); assign ee_rd_is_biu = (ee_rd_phys[28:12] == EE_BIU_MMIO_TAG); assign ee_wr_is_ram = !ee_wr_is_useg && (ee_wr_phys[28:25] == 4'h0); assign ee_wr_is_dmac_ch2 = (ee_wr_phys[28:12] == EE_DMAC_CH2_BASE[28:12]); assign ee_wr_is_dmac_ctrl = (ee_wr_phys[28:12] == EE_DMAC_CTRL_BASE[28:12]); // Ch287 // Ch288 — passive per-channel write decode (mirrors read side). assign ee_wr_is_dmac_passive = (ee_wr_phys[28:16] == 13'h1000) && ((ee_wr_phys[15:12] == 4'h8) || (ee_wr_phys[15:12] == 4'h9) || (ee_wr_phys[15:12] == 4'hB) || (ee_wr_phys[15:12] == 4'hC) || (ee_wr_phys[15:12] == 4'hD)); assign ee_wr_is_intc = (ee_wr_phys[28:12] == EE_INTC_BASE[28:12]); assign ee_wr_is_misc_mmio = (ee_wr_phys[28:16] == EE_MISC_MMIO_TAG); assign ee_wr_is_biu = (ee_wr_phys[28:12] == EE_BIU_MMIO_TAG); assign ee_wr_is_gs_priv = (ee_wr_phys[28:16] == EE_GS_PRIV_TAG); assign ee_bios_offset = ee_rd_phys[21:0]; assign ee_rd_ram_offset = ee_rd_phys[24:0]; assign ee_wr_ram_offset = ee_wr_phys[24:0]; // ------------------------------------------------------------------ // Bridge RAM decode (general — not useg-shadow specific, referenced // by the RAM-write arbitration and trace below). Kept at module // scope so it survives when the useg-shadow backing is generate- // removed (USEG_SHADOW_ENABLE=0). // ------------------------------------------------------------------ logic bridge_is_ram; logic [24:0] bridge_ram_offset; assign bridge_is_ram = (bridge_wr_addr[31:25] == 7'd0); assign bridge_ram_offset = bridge_wr_addr[24:0]; // ------------------------------------------------------------------ // Useg-shadow read response. Declared at module scope because the EE // CPU read mux references `useg_shadow_rd_data` unconditionally. The // procedural driver lives inside the generate-if below — when the // shadow is disabled the else-arm ties this to 0 and the mux never // selects it anyway (ee_rd_was_useg_shadow stays 0). // ------------------------------------------------------------------ logic [31:0] useg_shadow_rd_data; // ------------------------------------------------------------------ // Useg-shadow backing: 4 MiB = 1M words. Separate from kseg0/kseg1 // RAM. 1-cycle read latency, per-byte write-enable. // // Ch296 — the entire backing array plus its CPU-read, CPU-write, and // bridge-write logic is wrapped in a generate-if on USEG_SHADOW_ENABLE. // When disabled (board/synth build), the ~33k flip-flops Quartus bit- // blasts `useg_shadow_mem` into are fully removed: useg reads return // the unmapped value via the read mux (ee_rd_is_useg_shadow is forced // 0), useg writes are dropped, and `useg_shadow_rd_data` is tied 0. // ------------------------------------------------------------------ generate if (USEG_SHADOW_ENABLE) begin : g_useg_shadow localparam int USEG_SHADOW_WORDS = USEG_SHADOW_WORDS_PARAM; // Ch145 — parameter-driven (default 1M = 4 MiB) logic [31:0] useg_shadow_mem [0:USEG_SHADOW_WORDS-1]; initial begin for (int i = 0; i < USEG_SHADOW_WORDS; i++) useg_shadow_mem[i] = 32'd0; end logic [19:0] useg_rd_idx; logic [19:0] useg_wr_idx; assign useg_rd_idx = ee_rd_phys[21:2]; assign useg_wr_idx = ee_wr_phys[21:2]; logic useg_shadow_rd_valid; logic [31:0] useg_shadow_rd_data_q; always_ff @(posedge clk) begin if (!rst_n) begin useg_shadow_rd_valid <= 1'b0; useg_shadow_rd_data_q <= 32'd0; end else begin useg_shadow_rd_valid <= ee_rd_en && ee_rd_is_useg_shadow; if (ee_rd_en && ee_rd_is_useg_shadow) useg_shadow_rd_data_q <= useg_shadow_mem[useg_rd_idx]; end end assign useg_shadow_rd_data = useg_shadow_rd_data_q; // Ch65 — CPU kseg0 store mirror hit. Parallels the Ch64 // useg->ram mirror in the opposite direction. Disjoint from // ee_wr_is_useg_shadow by construction (kseg0 writes set // ee_wr_is_ram, not ee_wr_is_useg_shadow). useg_wr_idx already // extracts ee_wr_phys[21:2], which is the right word index for // both useg-stripped and kseg0-stripped phys in the first 4 MiB. // Ch70 unified alias also covers the kseg→useg_shadow direction // for any CPU RAM write whose phys is in [CPU_RAM_ALIAS_LO, // CPU_RAM_ALIAS_HI). Subsumes the Ch65 narrow knob when enabled. logic cpu_wr_kseg_alias_hit; logic cpu_wr_kseg_shadow_mirror_hit; assign cpu_wr_kseg_alias_hit = CPU_RAM_ALIAS_EN && ee_wr_en && ee_wr_is_ram && (ee_wr_phys >= CPU_RAM_ALIAS_LO) && (ee_wr_phys < CPU_RAM_ALIAS_HI); assign cpu_wr_kseg_shadow_mirror_hit = cpu_wr_kseg_alias_hit || (CPU_KSEG0_TO_USEG_SHADOW_MIRROR_EN && ee_wr_en && ee_wr_is_ram && (ee_wr_phys >= CPU_KSEG0_TO_USEG_SHADOW_MIRROR_LO) && (ee_wr_phys < CPU_KSEG0_TO_USEG_SHADOW_MIRROR_HI)); always_ff @(posedge clk) begin if (rst_n && ee_wr_en && (ee_wr_is_useg_shadow || cpu_wr_kseg_shadow_mirror_hit)) begin if (ee_wr_be[0]) useg_shadow_mem[useg_wr_idx][ 7: 0] <= ee_wr_data[ 7: 0]; if (ee_wr_be[1]) useg_shadow_mem[useg_wr_idx][15: 8] <= ee_wr_data[15: 8]; if (ee_wr_be[2]) useg_shadow_mem[useg_wr_idx][23:16] <= ee_wr_data[23:16]; if (ee_wr_be[3]) useg_shadow_mem[useg_wr_idx][31:24] <= ee_wr_data[31:24]; end end // Ch55: producer-side install seam. Bridge writes whose byte address // falls in the narrow coordinated-install window [0x80..0x1FF] also // shadow into useg_shadow_mem, so the EE CPU (which reads useg // exception-entry words via the shadow backing) observes bytes a // real external producer (SIF/IOP or BootROM/CDVD handoff) deposits // into physical RAM. ee_ram is still written unchanged through // bridge_wr_hit_ram below — this is purely additive mirroring. // // Narrowed per Ch55 Codex audit finding 1: earlier draft shadowed // all 4 MiB of low RAM, which would let unrelated future bridge // writes become visible through useg reads and muddy diagnostics. // The shared-surface install the BIOS needs lives only in // [0x80..0x1FF]; anything else goes through the regular // bridge→ee_ram path, disjoint from useg_shadow, matching the // Ch33 de-aliasing intent. If a future chapter needs a wider // mirror, raise BRIDGE_USEG_SHADOW_END here explicitly. localparam logic [31:0] BRIDGE_USEG_SHADOW_BASE = 32'h0000_0080; localparam logic [31:0] BRIDGE_USEG_SHADOW_END = 32'h0000_0200; logic bridge_wr_in_install_window; logic bridge_wr_is_useg_shadow; logic [19:0] bridge_useg_wr_idx0; logic [19:0] bridge_useg_wr_idx1; logic [19:0] bridge_useg_wr_idx2; logic [19:0] bridge_useg_wr_idx3; assign bridge_wr_in_install_window = (bridge_wr_addr >= BRIDGE_USEG_SHADOW_BASE) && (bridge_wr_addr < BRIDGE_USEG_SHADOW_END); assign bridge_wr_is_useg_shadow = bridge_wr_en && bridge_is_ram && bridge_wr_in_install_window; assign bridge_useg_wr_idx0 = bridge_wr_addr[21:2]; assign bridge_useg_wr_idx1 = bridge_wr_addr[21:2] + 20'd1; assign bridge_useg_wr_idx2 = bridge_wr_addr[21:2] + 20'd2; assign bridge_useg_wr_idx3 = bridge_wr_addr[21:2] + 20'd3; always_ff @(posedge clk) begin if (rst_n && bridge_wr_is_useg_shadow) begin if (bridge_wr_be[ 0]) useg_shadow_mem[bridge_useg_wr_idx0][ 7: 0] <= bridge_wr_data[ 7: 0]; if (bridge_wr_be[ 1]) useg_shadow_mem[bridge_useg_wr_idx0][15: 8] <= bridge_wr_data[ 15: 8]; if (bridge_wr_be[ 2]) useg_shadow_mem[bridge_useg_wr_idx0][23:16] <= bridge_wr_data[ 23: 16]; if (bridge_wr_be[ 3]) useg_shadow_mem[bridge_useg_wr_idx0][31:24] <= bridge_wr_data[ 31: 24]; if (bridge_wr_be[ 4]) useg_shadow_mem[bridge_useg_wr_idx1][ 7: 0] <= bridge_wr_data[ 39: 32]; if (bridge_wr_be[ 5]) useg_shadow_mem[bridge_useg_wr_idx1][15: 8] <= bridge_wr_data[ 47: 40]; if (bridge_wr_be[ 6]) useg_shadow_mem[bridge_useg_wr_idx1][23:16] <= bridge_wr_data[ 55: 48]; if (bridge_wr_be[ 7]) useg_shadow_mem[bridge_useg_wr_idx1][31:24] <= bridge_wr_data[ 63: 56]; if (bridge_wr_be[ 8]) useg_shadow_mem[bridge_useg_wr_idx2][ 7: 0] <= bridge_wr_data[ 71: 64]; if (bridge_wr_be[ 9]) useg_shadow_mem[bridge_useg_wr_idx2][15: 8] <= bridge_wr_data[ 79: 72]; if (bridge_wr_be[10]) useg_shadow_mem[bridge_useg_wr_idx2][23:16] <= bridge_wr_data[ 87: 80]; if (bridge_wr_be[11]) useg_shadow_mem[bridge_useg_wr_idx2][31:24] <= bridge_wr_data[ 95: 88]; if (bridge_wr_be[12]) useg_shadow_mem[bridge_useg_wr_idx3][ 7: 0] <= bridge_wr_data[103: 96]; if (bridge_wr_be[13]) useg_shadow_mem[bridge_useg_wr_idx3][15: 8] <= bridge_wr_data[111:104]; if (bridge_wr_be[14]) useg_shadow_mem[bridge_useg_wr_idx3][23:16] <= bridge_wr_data[119:112]; if (bridge_wr_be[15]) useg_shadow_mem[bridge_useg_wr_idx3][31:24] <= bridge_wr_data[127:120]; end end end else begin : g_no_useg_shadow // USEG_SHADOW_ENABLE=0 — no backing array, no read/write logic. // useg reads fall through to the 0xDEADBEEF unmapped mux arm // (ee_rd_is_useg_shadow is forced 0); useg writes are dropped. assign useg_shadow_rd_data = 32'd0; end endgenerate logic dmac_is_ram; logic [24:0] dmac_ram_offset; assign dmac_is_ram = (dmac_rd_addr[31:25] == 7'd0); assign dmac_ram_offset = dmac_rd_addr[24:0]; // ------------------------------------------------------------------ // BIOS routing (CPU fetch only — BIOS is read-only from every port) // ------------------------------------------------------------------ assign bios_rd_en = ee_rd_en && ee_rd_is_bios; assign bios_rd_addr = ee_bios_offset; // ------------------------------------------------------------------ // Shared 128-bit RAM read port. CPU wins over DMAC on collision. // CPU 32-bit read selects a lane from the 128-bit qword on the // response side. // ------------------------------------------------------------------ logic cpu_rd_hit_ram; logic dmac_rd_hit_ram; assign cpu_rd_hit_ram = ee_rd_en && ee_rd_is_ram; assign dmac_rd_hit_ram = dmac_rd_en && dmac_is_ram; // ------------------------------------------------------------------ // Ch115 — CPU vs DMAC RAM-read arbitration with DMAC retry. // // PRE-Ch115 BUG: when both `cpu_rd_hit_ram` and // `dmac_rd_hit_ram` fired on the same cycle, the map gave // CPU priority on `ram_rd_addr` but ALSO returned `dmac_rd_valid` // next cycle as if the DMAC had been serviced — so the DMAC // saw the CPU's data instead of its own. With a RAM-resident // EE bootlet busy-fetching while DMAC streamed a payload from // the same RAM, this silently corrupted the DMAC payload // (reproducer: tb_ee_ram_cpu_dmac_contention). // // FIX: keep CPU priority on the RAM bus, but when the DMAC's // request is contended out, BUFFER the DMAC's address into a // 1-deep retry slot here in the map. On the next cycle that // is NOT cpu-rd-hit-ram, replay the buffered DMAC read. The // DMAC's `mem_rd_en` is a one-shot pulse and `dmac_reg_stub` // waits in S_FETCH_WAIT for `mem_rd_valid`, so deferring the // valid response by one (or more) cycles is safe — the DMAC // simply waits longer before advancing to S_ACTIVE_SEND. The // CPU's path is unchanged: its request always fires this cycle. // ------------------------------------------------------------------ logic dmac_rd_buf_q; logic [24:0] dmac_rd_buf_addr_q; logic dmac_rd_serviced_now; // CPU priority remains absolute. DMAC fires when CPU isn't // hitting RAM AND there's a request to service (either fresh // this cycle or buffered from a prior contended cycle). assign dmac_rd_serviced_now = !cpu_rd_hit_ram && (dmac_rd_buf_q || dmac_rd_hit_ram); assign ram_rd_en = cpu_rd_hit_ram || dmac_rd_serviced_now; assign ram_rd_addr = cpu_rd_hit_ram ? ee_rd_ram_offset : dmac_rd_buf_q ? dmac_rd_buf_addr_q : dmac_ram_offset; always_ff @(posedge clk) begin if (!rst_n) begin dmac_rd_buf_q <= 1'b0; dmac_rd_buf_addr_q <= '0; end else begin // Set buffer on a fresh contention cycle (DMAC issued a // request that lost to the CPU). Only latch when the // buffer is empty — the DMAC won't issue a new request // while one is outstanding (it waits in S_FETCH_WAIT), // so we never need to enqueue more than one. if (!dmac_rd_buf_q && cpu_rd_hit_ram && dmac_rd_hit_ram) begin dmac_rd_buf_q <= 1'b1; dmac_rd_buf_addr_q <= dmac_ram_offset; end // Clear buffer the cycle the buffered request actually // gets the bus. else if (dmac_rd_buf_q && dmac_rd_serviced_now) begin dmac_rd_buf_q <= 1'b0; end end end // ------------------------------------------------------------------ // Shared 128-bit RAM write port. CPU wins over bridge on collision. // CPU 32-bit data expands to a 128-bit lane; ram_wr_be masks so // only the four bytes of that lane are enabled. // ------------------------------------------------------------------ logic cpu_wr_hit_ram; logic bridge_wr_hit_ram; assign cpu_wr_hit_ram = ee_wr_en && ee_wr_is_ram; assign bridge_wr_hit_ram = bridge_wr_en && bridge_is_ram; // Ch64 useg-mirror hit: the CPU write is targeting a useg address // whose phys falls in the parameterized window. When enabled, the // same data also lands in ee_ram (lane-expanded below). Disjoint // from cpu_wr_hit_ram because ee_wr_is_useg and ee_wr_is_ram are // mutually exclusive by construction. // // Ch70 unified alias: the same write also mirrors into ee_ram if // CPU_RAM_ALIAS_EN is set and the phys is in [CPU_RAM_ALIAS_LO, // CPU_RAM_ALIAS_HI). This subsumes the Ch64 narrow knob when // enabled. logic cpu_wr_useg_mirror_hit; logic cpu_wr_useg_alias_hit; assign cpu_wr_useg_alias_hit = CPU_RAM_ALIAS_EN && ee_wr_en && ee_wr_is_useg_shadow && (ee_wr_phys >= CPU_RAM_ALIAS_LO) && (ee_wr_phys < CPU_RAM_ALIAS_HI); assign cpu_wr_useg_mirror_hit = cpu_wr_useg_alias_hit || (CPU_USEG_TO_RAM_MIRROR_EN && ee_wr_en && ee_wr_is_useg_shadow && (ee_wr_phys >= CPU_USEG_TO_RAM_MIRROR_LO) && (ee_wr_phys < CPU_USEG_TO_RAM_MIRROR_HI)); logic cpu_wr_to_ram_any; assign cpu_wr_to_ram_any = cpu_wr_hit_ram || cpu_wr_useg_mirror_hit; logic [1:0] cpu_wr_lane; logic [127:0] cpu_wr_data_exp; logic [15:0] cpu_wr_be_exp; assign cpu_wr_lane = ee_wr_addr[3:2]; always_comb begin cpu_wr_data_exp = 128'd0; cpu_wr_be_exp = 16'd0; case (cpu_wr_lane) 2'd0: begin cpu_wr_data_exp[31:0] = ee_wr_data; cpu_wr_be_exp[3:0] = ee_wr_be; end 2'd1: begin cpu_wr_data_exp[63:32] = ee_wr_data; cpu_wr_be_exp[7:4] = ee_wr_be; end 2'd2: begin cpu_wr_data_exp[95:64] = ee_wr_data; cpu_wr_be_exp[11:8] = ee_wr_be; end 2'd3: begin cpu_wr_data_exp[127:96] = ee_wr_data; cpu_wr_be_exp[15:12] = ee_wr_be; end endcase end assign ram_wr_en = cpu_wr_to_ram_any || bridge_wr_hit_ram; // cpu_wr_hit_ram and cpu_wr_useg_mirror_hit are mutually // exclusive; ee_wr_phys[24:0] is the same extraction as // ee_wr_ram_offset, so one address expression covers both CPU // cases. assign ram_wr_addr = cpu_wr_to_ram_any ? ee_wr_phys[24:0] : bridge_ram_offset; assign ram_wr_data = cpu_wr_to_ram_any ? cpu_wr_data_exp : bridge_wr_data; assign ram_wr_be = cpu_wr_to_ram_any ? cpu_wr_be_exp : bridge_wr_be; assign ram_master_id = cpu_wr_to_ram_any ? 8'd0 : bridge_master_id; // ------------------------------------------------------------------ // EE DMAC ch2 register write port (chapter 3, write-only) // ------------------------------------------------------------------ assign ee_dmac_ch2_wr_en = ee_wr_en && ee_wr_is_dmac_ch2; assign ee_dmac_ch2_wr_addr = ee_wr_phys[7:0]; assign ee_dmac_ch2_wr_data = ee_wr_data; assign ee_dmac_ch2_rd_en = ee_rd_en && ee_rd_is_dmac_ch2; assign ee_dmac_ch2_rd_addr = ee_rd_phys[7:0]; assign ee_intc_wr_en = ee_wr_en && ee_wr_is_intc; assign ee_intc_wr_addr = ee_wr_phys[7:0]; assign ee_intc_wr_data = ee_wr_data; assign ee_intc_rd_en = ee_rd_en && ee_rd_is_intc; assign ee_intc_rd_addr = ee_rd_phys[7:0]; // ------------------------------------------------------------------ // Ch287 — EE DMAC global control/status stub, instantiated // INTERNALLY so existing TBs don't need to route new ports. // Surface: D_CTRL/D_STAT/D_PCR/D_SQWC/D_RBSR/D_RBOR in the // 0x1000_E000-0x1000_E0FF window (broader 4 KiB window decoded by // EE_DMAC_CTRL_BASE[28:12] match; the live registers occupy the // first 256 bytes via reg_offset = ee_*_phys[7:0]). // ------------------------------------------------------------------ logic ee_dmac_ctrl_wr_en; logic [7:0] ee_dmac_ctrl_wr_addr; logic [31:0] ee_dmac_ctrl_wr_data; logic ee_dmac_ctrl_rd_en; logic [7:0] ee_dmac_ctrl_rd_addr; logic [31:0] ee_dmac_ctrl_rd_data; logic ee_dmac_ctrl_rd_valid; // Trace from the inner stub is currently unwired (the outer // map already emits one trace event per CPU access via the // EV_READ/EV_WRITE arms below; merging two trace sources here // would require a mux that doesn't exist yet). Future chapters // that need DMAC-level retire detail can plumb these into // trace_sink_stub directly from the TB hierarchy. logic ee_dmac_ctrl_ev_valid; trace_pkg::subsys_e ee_dmac_ctrl_ev_subsys; trace_pkg::event_e ee_dmac_ctrl_ev_event; logic [63:0] ee_dmac_ctrl_ev_arg0; logic [63:0] ee_dmac_ctrl_ev_arg1; logic [63:0] ee_dmac_ctrl_ev_arg2; logic [63:0] ee_dmac_ctrl_ev_arg3; logic [31:0] ee_dmac_ctrl_ev_flags; assign ee_dmac_ctrl_wr_en = ee_wr_en && ee_wr_is_dmac_ctrl; assign ee_dmac_ctrl_wr_addr = ee_wr_phys[7:0]; assign ee_dmac_ctrl_wr_data = ee_wr_data; assign ee_dmac_ctrl_rd_en = ee_rd_en && ee_rd_is_dmac_ctrl; assign ee_dmac_ctrl_rd_addr = ee_rd_phys[7:0]; ee_dmac_ctrl_stub u_dmac_ctrl ( .clk(clk), .rst_n(rst_n), .reg_wr_en(ee_dmac_ctrl_wr_en), .reg_offset(ee_dmac_ctrl_wr_en ? ee_dmac_ctrl_wr_addr : ee_dmac_ctrl_rd_addr), .reg_wr_data(ee_dmac_ctrl_wr_data), .reg_rd_en(ee_dmac_ctrl_rd_en), .reg_rd_data(ee_dmac_ctrl_rd_data), .reg_rd_valid(ee_dmac_ctrl_rd_valid), .ev_valid(ee_dmac_ctrl_ev_valid), .ev_subsys(ee_dmac_ctrl_ev_subsys), .ev_event(ee_dmac_ctrl_ev_event), .ev_arg0(ee_dmac_ctrl_ev_arg0), .ev_arg1(ee_dmac_ctrl_ev_arg1), .ev_arg2(ee_dmac_ctrl_ev_arg2), .ev_arg3(ee_dmac_ctrl_ev_arg3), .ev_flags(ee_dmac_ctrl_ev_flags) ); // ------------------------------------------------------------------ // Ch288 — Passive per-channel surface (ch0/1/3/4/5). Hosts // CHCR/MADR/QWC/TADR per channel; latched, no FSM, no D_STAT // side effects. Same internal-instance pattern as Ch287. // ------------------------------------------------------------------ logic ee_dmac_passive_wr_en; logic [15:0] ee_dmac_passive_wr_addr; logic [31:0] ee_dmac_passive_wr_data; logic ee_dmac_passive_rd_en; logic [15:0] ee_dmac_passive_rd_addr; logic [31:0] ee_dmac_passive_rd_data; logic ee_dmac_passive_rd_valid; logic ee_dmac_passive_ev_valid; trace_pkg::subsys_e ee_dmac_passive_ev_subsys; trace_pkg::event_e ee_dmac_passive_ev_event; logic [63:0] ee_dmac_passive_ev_arg0; logic [63:0] ee_dmac_passive_ev_arg1; logic [63:0] ee_dmac_passive_ev_arg2; logic [63:0] ee_dmac_passive_ev_arg3; logic [31:0] ee_dmac_passive_ev_flags; assign ee_dmac_passive_wr_en = ee_wr_en && ee_wr_is_dmac_passive; assign ee_dmac_passive_wr_addr = ee_wr_phys[15:0]; assign ee_dmac_passive_wr_data = ee_wr_data; assign ee_dmac_passive_rd_en = ee_rd_en && ee_rd_is_dmac_passive; assign ee_dmac_passive_rd_addr = ee_rd_phys[15:0]; ee_dmac_passive_chan_stub u_dmac_passive ( .clk(clk), .rst_n(rst_n), .reg_wr_en(ee_dmac_passive_wr_en), .chan_addr(ee_dmac_passive_wr_en ? ee_dmac_passive_wr_addr : ee_dmac_passive_rd_addr), .reg_wr_data(ee_dmac_passive_wr_data), .reg_rd_en(ee_dmac_passive_rd_en), .reg_rd_data(ee_dmac_passive_rd_data), .reg_rd_valid(ee_dmac_passive_rd_valid), .ev_valid(ee_dmac_passive_ev_valid), .ev_subsys(ee_dmac_passive_ev_subsys), .ev_event(ee_dmac_passive_ev_event), .ev_arg0(ee_dmac_passive_ev_arg0), .ev_arg1(ee_dmac_passive_ev_arg1), .ev_arg2(ee_dmac_passive_ev_arg2), .ev_arg3(ee_dmac_passive_ev_arg3), .ev_flags(ee_dmac_passive_ev_flags) ); assign ee_misc_mmio_wr_en = ee_wr_en && ee_wr_is_misc_mmio; assign ee_misc_mmio_wr_addr = ee_wr_phys[15:0]; assign ee_misc_mmio_wr_data = ee_wr_data; assign ee_misc_mmio_wr_be = ee_wr_be; // Ch112 — GS privileged-MMIO write routing. assign ee_gs_priv_wr_en = ee_wr_en && ee_wr_is_gs_priv; assign ee_gs_priv_wr_addr = ee_wr_phys[15:0]; assign ee_gs_priv_wr_data = ee_wr_data; assign ee_gs_priv_wr_be = ee_wr_be; assign ee_misc_mmio_rd_en = ee_rd_en && ee_rd_is_misc_mmio; assign ee_misc_mmio_rd_addr = ee_rd_phys[15:0]; assign ee_biu_wr_en = ee_wr_en && ee_wr_is_biu; assign ee_biu_wr_addr = ee_wr_phys[11:0]; assign ee_biu_wr_data = ee_wr_data; assign ee_biu_wr_be = ee_wr_be; assign ee_biu_rd_en = ee_rd_en && ee_rd_is_biu; assign ee_biu_rd_addr = ee_rd_phys[11:0]; // ------------------------------------------------------------------ // EE CPU read response pipeline (one-cycle latency, matches the rest // of the stub ecosystem). Latch which region answered plus the lane // index for RAM reads. // ------------------------------------------------------------------ logic ee_rd_pending; logic ee_rd_was_bios; logic ee_rd_was_ram; logic ee_rd_was_dmac_ch2; logic ee_rd_was_dmac_ctrl; // Ch287 logic ee_rd_was_dmac_passive; // Ch288 logic ee_rd_was_intc; logic ee_rd_was_misc_mmio; logic ee_rd_was_biu; logic ee_rd_was_useg_shadow; logic [1:0] ee_rd_ram_lane; always_ff @(posedge clk) begin if (!rst_n) begin ee_rd_pending <= 1'b0; ee_rd_was_bios <= 1'b0; ee_rd_was_ram <= 1'b0; ee_rd_was_dmac_ch2 <= 1'b0; ee_rd_was_dmac_ctrl <= 1'b0; ee_rd_was_dmac_passive <= 1'b0; ee_rd_was_intc <= 1'b0; ee_rd_was_misc_mmio <= 1'b0; ee_rd_was_biu <= 1'b0; ee_rd_was_useg_shadow <= 1'b0; ee_rd_ram_lane <= 2'd0; end else begin ee_rd_pending <= ee_rd_en; if (ee_rd_en) begin ee_rd_was_bios <= ee_rd_is_bios; ee_rd_was_ram <= ee_rd_is_ram; ee_rd_was_dmac_ch2 <= ee_rd_is_dmac_ch2; ee_rd_was_dmac_ctrl <= ee_rd_is_dmac_ctrl; // Ch287 ee_rd_was_dmac_passive <= ee_rd_is_dmac_passive; // Ch288 ee_rd_was_intc <= ee_rd_is_intc; ee_rd_was_misc_mmio <= ee_rd_is_misc_mmio; ee_rd_was_biu <= ee_rd_is_biu; ee_rd_was_useg_shadow <= ee_rd_is_useg_shadow; ee_rd_ram_lane <= ee_rd_addr[3:2]; end end end logic [31:0] ee_rd_ram_slice; always_comb begin case (ee_rd_ram_lane) 2'd0: ee_rd_ram_slice = ram_rd_data[31:0]; 2'd1: ee_rd_ram_slice = ram_rd_data[63:32]; 2'd2: ee_rd_ram_slice = ram_rd_data[95:64]; 2'd3: ee_rd_ram_slice = ram_rd_data[127:96]; default: ee_rd_ram_slice = 32'd0; endcase end assign ee_rd_valid = ee_rd_pending; assign ee_rd_data = ee_rd_was_bios ? bios_rd_data : ee_rd_was_ram ? ee_rd_ram_slice : ee_rd_was_useg_shadow ? useg_shadow_rd_data : ee_rd_was_dmac_ch2 ? ee_dmac_ch2_rd_data : ee_rd_was_dmac_ctrl ? ee_dmac_ctrl_rd_data // Ch287 : ee_rd_was_dmac_passive ? ee_dmac_passive_rd_data // Ch288 : ee_rd_was_intc ? ee_intc_rd_data : ee_rd_was_misc_mmio ? ee_misc_mmio_rd_data : ee_rd_was_biu ? ee_biu_rd_data : 32'hDEADBEEF; // ------------------------------------------------------------------ // DMAC read response pipeline (128-bit). Ch115 — `dmac_rd_pending` // now sets ONLY when the DMAC's read actually hits the RAM bus // this cycle (`dmac_rd_serviced_now`), so a contended-out request // doesn't return false data with the CPU's contents (the // pre-Ch115 corruption bug). For non-RAM dmac_rd_en hits (other // dmac_is_* targets), the existing behavior is preserved. // ------------------------------------------------------------------ logic dmac_rd_pending; logic dmac_rd_was_ram; logic dmac_rd_actually_serviced; // The DMAC's pending response fires when: // (a) it actually got the RAM bus this cycle (RAM target), OR // (b) its request hit a non-RAM target (dmac_rd_en && !dmac_is_ram — // no contention possible there). // Otherwise it stays in S_FETCH_WAIT, waiting for the buffered // replay (RAM case) or the original request (non-RAM case). assign dmac_rd_actually_serviced = dmac_rd_serviced_now || (dmac_rd_en && !dmac_is_ram); always_ff @(posedge clk) begin if (!rst_n) begin dmac_rd_pending <= 1'b0; dmac_rd_was_ram <= 1'b0; end else begin dmac_rd_pending <= dmac_rd_actually_serviced; if (dmac_rd_actually_serviced) dmac_rd_was_ram <= dmac_rd_serviced_now; end end assign dmac_rd_valid = dmac_rd_pending; assign dmac_rd_data = dmac_rd_was_ram ? ram_rd_data : 128'hDEAD_BEEF_DEAD_BEEF_DEAD_BEEF_DEAD_BEEF; // ------------------------------------------------------------------ // Trace emission — one event per cycle, priority: // EE read > EE write > DMAC read // Two paths are unlikely to be active simultaneously in Wave 2.7 // testbenches (each TB exercises a single master); priority here is // defensive. // ------------------------------------------------------------------ always_ff @(posedge clk) begin if (!rst_n) begin ev_valid <= 1'b0; ev_subsys <= SUBSYS_MEM; ev_event <= EV_READ; ev_arg0 <= 64'd0; ev_arg1 <= 64'd0; ev_arg2 <= 64'd0; ev_arg3 <= 64'd0; ev_flags <= 32'd0; end else if (ee_rd_en) begin ev_valid <= 1'b1; ev_subsys <= SUBSYS_MEM; if (ee_rd_is_bios) begin ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_BIOS; end else if (ee_rd_is_ram) begin ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_EE_RAM; end else if (ee_rd_is_dmac_ch2) begin ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_EE_DMAC; end else if (ee_rd_is_dmac_ctrl) begin // Ch287 ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_EE_DMAC_CTRL; end else if (ee_rd_is_dmac_passive) begin // Ch288 ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_EE_DMAC_PASSIVE; end else if (ee_rd_is_intc) begin ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_EE_INTC; end else if (ee_rd_is_misc_mmio) begin ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_EE_MISC_MMIO; end else if (ee_rd_is_biu) begin ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_EE_BIU; end else if (ee_rd_is_useg_shadow) begin // Ch34: useg-shadow hits get their own region tag so // post-run analysis can distinguish them from kseg0 // RAM hits. Pre-ch33 code conflated the two via the // phys=addr[28:0] strip; the shadow is now a separate // 4 MiB backing. ev_event <= EV_READ; ev_arg1 <= 64'd0; ev_arg3 <= REGION_USEG_SHADOW; end else begin ev_event <= EV_UNMAPPED; ev_arg1 <= 64'hDEADBEEF; ev_arg3 <= REGION_UNMAPPED; end ev_arg0 <= {32'd0, ee_rd_addr}; ev_arg2 <= MASTER_EE_IFETCH; ev_flags <= 32'd0; end else if (ee_wr_en) begin ev_valid <= 1'b1; ev_subsys <= SUBSYS_MEM; if (ee_wr_is_ram) begin ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_RAM; end else if (ee_wr_is_dmac_ch2) begin ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_DMAC; end else if (ee_wr_is_dmac_ctrl) begin // Ch287 ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_DMAC_CTRL; end else if (ee_wr_is_dmac_passive) begin // Ch288 ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_DMAC_PASSIVE; end else if (ee_wr_is_intc) begin ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_INTC; end else if (ee_wr_is_misc_mmio) begin ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_MISC_MMIO; end else if (ee_wr_is_biu) begin ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_BIU; end else if (ee_wr_is_gs_priv) begin // Ch112: GS-privileged window (0x1200_xxxx) routed // to ee_gs_priv_bridge_stub. Without this branch a // valid 0x1200_xxxx write would surface as // EV_UNMAPPED at the trace. ev_event <= EV_WRITE; ev_arg3 <= REGION_EE_GS_PRIV; end else if (ee_wr_is_useg_shadow) begin // Ch34: dedicated useg-shadow tag (see read-side note). ev_event <= EV_WRITE; ev_arg3 <= REGION_USEG_SHADOW; end else begin ev_event <= EV_UNMAPPED; ev_arg3 <= REGION_UNMAPPED; end ev_arg0 <= {32'd0, ee_wr_addr}; ev_arg1 <= {32'd0, ee_wr_data}; ev_arg2 <= MASTER_EE_IFETCH; ev_flags <= 32'h0000_0001; end else if (dmac_rd_en) begin ev_valid <= 1'b1; ev_subsys <= SUBSYS_MEM; ev_event <= dmac_is_ram ? EV_READ : EV_UNMAPPED; ev_arg0 <= {32'd0, dmac_rd_addr}; ev_arg1 <= dmac_is_ram ? 64'd0 : 64'hDEADBEEF; ev_arg2 <= MASTER_DMAC; ev_arg3 <= dmac_is_ram ? REGION_EE_RAM : REGION_UNMAPPED; ev_flags <= 32'd0; end else if (bridge_wr_en) begin ev_valid <= 1'b1; ev_subsys <= SUBSYS_MEM; ev_event <= bridge_is_ram ? EV_WRITE : EV_UNMAPPED; ev_arg0 <= {32'd0, bridge_wr_addr}; ev_arg1 <= bridge_wr_data[63:0]; ev_arg2 <= {56'd0, bridge_master_id}; ev_arg3 <= bridge_is_ram ? REGION_EE_RAM : REGION_UNMAPPED; ev_flags <= 32'h0000_0001; end else begin ev_valid <= 1'b0; end end endmodule : ee_memory_map_stub