// retroDE_ps2 — ee_gs_priv_bridge_stub (Ch111) // // Bridges 32-bit EE-MMIO writes targeting the GS privileged- // register window at 0x1200_0000 into the 64-bit gs_stub.reg_wr_* // port. Real PS2 driver code reaches PMODE / DISPFB1 / DISPLAY1 // (etc.) via 64-bit MIPS `sd` instructions; the EE microarch // breaks each `sd` into a pair of 32-bit `sw` operations to the // low+high halves of the 8-byte register slot. This bridge does // the inverse — it watches the 32-bit EE write stream, latches a // 64-bit shadow per 8-byte slot, and fires a gs_stub.reg_wr_* // pulse on EVERY half-write with the running 64-bit shadow value. // // Scope: // - One shared 64-bit shadow + an offset[15:3] tag identifying // the currently-tracked 8-byte slot. Sequential writes to the // SAME slot accumulate (low first, then high → final shadow // has both halves correct on the second fire). Switching // slots resets the shadow to zero so partial-half writes to // a fresh slot don't carry stale data from a different reg. // - Each EE half-write fires a gs_stub.reg_wr_* pulse with the // 8-byte-aligned offset (`{ee_wr_addr[15:3], 3'b000}`) and // the FULL 64-bit shadow. Single-half writes (e.g. PMODE // where only the low byte matters) work because the high // half stays zero and gs_stub's latch sees the right value. // - 32-bit EE write width (matches ee_memory_map_stub's // ee_wr_*-port surface). **Full-word writes only**: // `ee_wr_be` MUST be 4'b1111 on every accepted write. Byte- // lane merging into the 64-bit shadow is intentionally NOT // modelled here — control-plane GS registers (PMODE/ // DISPFB1/DISPLAY1/etc.) are always written as full 32-bit // halves of an `sd`, and constraining the contract keeps the // shadow + commit logic small. A simulation-time `$error` // fires if a non-full be is presented; a future chapter can // widen the bridge to per-byte merge if/when a real driver // pattern needs sub-word writes here. // // Wiring contract (TB-level for Ch111): // ee_wr_en ← TB EE-MMIO write strobe at 0x12000000+offset // ee_wr_addr ← 16-bit offset within the GS priv window (= EE // phys addr [15:0]; the upper EE-window decode // lives in the test bench / memory map) // ee_wr_data ← 32-bit EE data (one of two halves of a 64-bit // GS register) // ee_wr_be ← 4-bit per-byte enable (typically 4'b1111) // // The bridge does NOT participate in EE reads. The gs_stub // privileged-register port is write-only at this scope, matching // the limited read coverage of the GS priv block in the rest of // the design. `timescale 1ns/1ps module ee_gs_priv_bridge_stub ( input logic clk, input logic rst_n, // EE-MMIO write port (32-bit data, 16-bit offset within // 0x1200_0000 window). input logic ee_wr_en, input logic [15:0] ee_wr_addr, input logic [31:0] ee_wr_data, input logic [3:0] ee_wr_be, // gs_stub privileged-register port (16-bit offset, 64-bit data). output logic gs_reg_wr_en, output logic [15:0] gs_reg_wr_addr, output logic [63:0] gs_reg_wr_data ); // Shared 64-bit shadow + the 13-bit offset[15:3] tag of the // currently-tracked slot. Resets to zero on rst_n or on a // switch to a different slot. logic [63:0] shadow; logic [12:0] shadow_tag; logic shadow_valid; logic [12:0] cur_tag; logic cur_is_high; logic [63:0] new_shadow; assign cur_tag = ee_wr_addr[15:3]; assign cur_is_high = ee_wr_addr[2]; always_comb begin logic [63:0] base_shadow; // If the EE write hits the same 8-byte slot we're already // tracking, merge into the existing shadow. Otherwise start // a fresh shadow at zero (the un-touched half stays 0 — that's // safe for the demo where we always write the half that // matters first; high-only writes are not used in this TB // family). base_shadow = (shadow_valid && shadow_tag == cur_tag) ? shadow : 64'd0; if (cur_is_high) new_shadow = {ee_wr_data, base_shadow[31:0]}; else new_shadow = {base_shadow[63:32], ee_wr_data}; end always_ff @(posedge clk) begin if (!rst_n) begin shadow <= 64'd0; shadow_tag <= 13'd0; shadow_valid <= 1'b0; gs_reg_wr_en <= 1'b0; gs_reg_wr_addr <= 16'd0; gs_reg_wr_data <= 64'd0; end else begin gs_reg_wr_en <= 1'b0; if (ee_wr_en) begin // Contract: full-word writes only. Sub-word // (per-byte) merging into the 64-bit shadow is // out of scope at Ch111. Catch contract violations // loudly so a future driver pattern that needs // byte-lane writes is forced to widen the bridge. if (ee_wr_be !== 4'b1111) begin $error("ee_gs_priv_bridge_stub: ee_wr_be=%b — only 4'b1111 supported (full-word writes); offset=0x%04x data=0x%08h", ee_wr_be, ee_wr_addr, ee_wr_data); end shadow <= new_shadow; shadow_tag <= cur_tag; shadow_valid <= 1'b1; gs_reg_wr_en <= 1'b1; gs_reg_wr_addr <= {cur_tag, 3'b000}; gs_reg_wr_data <= new_shadow; end end end endmodule : ee_gs_priv_bridge_stub