// retroDE_ps2 — ee_dmac_ctrl_stub // // Ch287 — EE DMAC global control/status registers at // 0x1000_E000..0x1000_E0FF (256 bytes). NOT the per-channel registers // (those live in dmac_reg_stub at 0x1000_A000+ for channel 2; per- // channel registers for other channels are not modelled yet). // // Surface modelled here (R5900 DMAC global): // offset 0x00 D_CTRL — DMAC enable / cycle-stealing / RELE / etc. // Latched write, read returns last-written. // offset 0x10 D_STAT — Per-channel interrupt status (CIS) + per- // channel interrupt mask (CIM) + stall / MEIS. // Read returns current latch (reset = 0 = no // pending interrupts). Writes are W1C against // the CIS/MEIS half (bits where write_data has // a 1 are cleared); CIM half is NOT W1C — bits // are unconditionally written. Real R5900 // splits the word: bits[15:0] = CIS (W1C), bits // [31:16] = CIM (write). With nothing in the // stub yet setting bits, qbert sees "no // interrupts pending" on every read, which is // exactly the wait-for-quiet pattern its init // loop polls for. // offset 0x20 D_PCR — Per-channel priority + W1C enables. Latched // write, read returns last-written. // offset 0x30 D_SQWC — Stall/skip cycles. Latched. // offset 0x40 D_RBSR — Ring-buffer size. Latched. // offset 0x50 D_RBOR — Ring-buffer base. Latched. // any other offset — write traced + dropped; read returns 0. // // Codex framing: "If the hot PC is truly a D_STAT poll, read-as-zero // may or may not be the right 'ready' value. Let the next run tell us. // If it still loops, the next chapter should decode the branch // condition and choose the exact D_STAT bit semantics, not guess the // whole region." The implementation honors that — every offset has // minimal-sufficient behavior; future chapters can refine specific // bits once a real ELF surfaces a divergence. // // Port interface mirrors the dmac_reg_stub / intc_stub conventions: // reg_wr_en / reg_offset / reg_wr_data : write port // reg_rd_en / reg_offset / reg_rd_data / reg_rd_valid : read port, // 1-cycle latency // trace_pkg::* : ev_* events tagged SUBSYS_DMAC + EV_READ/EV_WRITE // with arg0 = offset, arg1 = data. `timescale 1ns/1ps module ee_dmac_ctrl_stub import trace_pkg::*; ( input logic clk, input logic rst_n, // Write port (single-cycle, shared offset with read). input logic reg_wr_en, input logic [7:0] reg_offset, input logic [31:0] reg_wr_data, // Read port (1-cycle latency). input logic reg_rd_en, output logic [31:0] reg_rd_data, output logic reg_rd_valid, // Trace 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 [7:0] D_CTRL_OFFSET = 8'h00; localparam logic [7:0] D_STAT_OFFSET = 8'h10; localparam logic [7:0] D_PCR_OFFSET = 8'h20; localparam logic [7:0] D_SQWC_OFFSET = 8'h30; localparam logic [7:0] D_RBSR_OFFSET = 8'h40; localparam logic [7:0] D_RBOR_OFFSET = 8'h50; // ------------------------------------------------------------------ // Register file // ------------------------------------------------------------------ logic [31:0] d_ctrl; logic [31:0] d_stat; // CIS in low half (W1C), CIM in high half (W) logic [31:0] d_pcr; logic [31:0] d_sqwc; logic [31:0] d_rbsr; logic [31:0] d_rbor; always_ff @(posedge clk) begin if (!rst_n) begin d_ctrl <= 32'd0; d_stat <= 32'd0; d_pcr <= 32'd0; d_sqwc <= 32'd0; d_rbsr <= 32'd0; d_rbor <= 32'd0; end else if (reg_wr_en) begin unique case (reg_offset) D_CTRL_OFFSET: d_ctrl <= reg_wr_data; D_STAT_OFFSET: begin // W1C on the low half (interrupt-status bits): a 1 // in reg_wr_data clears that bit; a 0 leaves it. // Direct-write on the high half (mask bits). d_stat[15:0] <= d_stat[15:0] & ~reg_wr_data[15:0]; d_stat[31:16] <= reg_wr_data[31:16]; end D_PCR_OFFSET: d_pcr <= reg_wr_data; D_SQWC_OFFSET: d_sqwc <= reg_wr_data; D_RBSR_OFFSET: d_rbsr <= reg_wr_data; D_RBOR_OFFSET: d_rbor <= reg_wr_data; default: ; // unknown offsets: write dropped (traced) endcase end end // Read mux (1-cycle latency to match the stub ecosystem). always_ff @(posedge clk) begin if (!rst_n) begin reg_rd_data <= 32'd0; reg_rd_valid <= 1'b0; end else begin reg_rd_valid <= reg_rd_en; if (reg_rd_en) begin unique case (reg_offset) D_CTRL_OFFSET: reg_rd_data <= d_ctrl; D_STAT_OFFSET: reg_rd_data <= d_stat; D_PCR_OFFSET: reg_rd_data <= d_pcr; D_SQWC_OFFSET: reg_rd_data <= d_sqwc; D_RBSR_OFFSET: reg_rd_data <= d_rbsr; D_RBOR_OFFSET: reg_rd_data <= d_rbor; default: reg_rd_data <= 32'd0; endcase end end end // ------------------------------------------------------------------ // Trace — one event per cycle, write priority over read (consistent // with the rest of the stub ecosystem). // ------------------------------------------------------------------ always_ff @(posedge clk) begin if (!rst_n) begin ev_valid <= 1'b0; ev_subsys <= SUBSYS_DMAC; ev_event <= EV_WRITE; ev_arg0 <= 64'd0; ev_arg1 <= 64'd0; ev_arg2 <= 64'd0; ev_arg3 <= 64'd0; ev_flags <= 32'd0; end else if (reg_wr_en) begin ev_valid <= 1'b1; ev_subsys <= SUBSYS_DMAC; ev_event <= EV_WRITE; ev_arg0 <= {56'd0, reg_offset}; ev_arg1 <= {32'd0, reg_wr_data}; ev_arg2 <= 64'd0; ev_arg3 <= 64'd0; ev_flags <= 32'h0000_0001; // write end else if (reg_rd_en) begin ev_valid <= 1'b1; ev_subsys <= SUBSYS_DMAC; ev_event <= EV_READ; ev_arg0 <= {56'd0, reg_offset}; ev_arg1 <= 64'd0; ev_arg2 <= 64'd0; ev_arg3 <= 64'd0; ev_flags <= 32'd0; end else begin ev_valid <= 1'b0; end end endmodule : ee_dmac_ctrl_stub