// retroDE_ps2 — bios_rom_stub // // Simulation stub for the 4 MiB BIOS ROM window. Gives Milestone B a // deterministic instruction source before the rest of the memory system // exists. // // Contract refs: // docs/stub_module_plan.md (Wave 1, item 2) // docs/contracts/memory.md (memory owns BIOS storage/visibility) // docs/contracts/iop.md (IOP owns BIOS behavior — NOT here) // docs/decisions/0002-bios-policy.md (real BIOS + narrow stubs; this stub // is the storage adapter, not firmware // behavior, and needs no stub-policy // tracking) // // Backing store: // - If IMAGE_FILE is a non-empty string, `$readmemh` loads it at // elaboration. Caller is responsible for supplying a hex image produced // from a user-supplied BIOS dump. No BIOS image is shipped with this // repository (see third_party/LICENSING.md). // - If IMAGE_FILE is empty (default), a synthetic fixture is generated: // mem[word_i] = 32'h00000000 (MIPS NOP: sll $0, $0, 0) // Rationale: straight-line valid MIPS so the fixture is a legitimate // execution target for any future emulator comparison. This aligns // with sim/golden/trace_compare_spec.md ("first comparison target"). // Earlier versions used 32'hBFC00000 | word_index for trace-distinct // inspection, but the spec explicitly rules out fixtures whose words // are not a sensible execution target. // // Interface: // - Byte-addressed within the 4 MiB window. The lower 2 bits of rd_addr // are ignored (word-aligned fetch). Upstream address decode is owned // by ee_memory_map_stub; this block does not validate the window itself. // - One-cycle read latency: rd_en pulses on cycle N, rd_data/rd_valid // present on cycle N+1. // - Each completed read emits a MEM.READ trace event. // // Trace payload schema (per stub plan): // MEM READ arg0=addr arg1=data arg2=master arg3=region // master: 0=EE_IFETCH (only source wired in Wave 1) // region: 0=BIOS `timescale 1ns/1ps module bios_rom_stub import trace_pkg::*; #( parameter int SIZE_BYTES = 4 * 1024 * 1024, parameter string IMAGE_FILE = "" ) ( input logic clk, input logic rst_n, input logic rd_en, input logic [$clog2(SIZE_BYTES)-1:0] rd_addr, output logic [31:0] rd_data, output logic rd_valid, 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 int WORD_COUNT = SIZE_BYTES / 4; localparam int ADDR_WIDTH = $clog2(SIZE_BYTES); localparam int WORD_INDEX_WIDTH = ADDR_WIDTH - 2; logic [31:0] mem [0:WORD_COUNT-1]; initial begin if (IMAGE_FILE != "") begin $display("[bios_rom_stub] loading image: %0s", IMAGE_FILE); $readmemh(IMAGE_FILE, mem); end else begin for (int i = 0; i < WORD_COUNT; i++) begin mem[i] = 32'h00000000; // MIPS NOP end $display("[bios_rom_stub] synthetic NOP sled loaded (%0d words)", WORD_COUNT); end end logic [WORD_INDEX_WIDTH-1:0] word_index; assign word_index = rd_addr[ADDR_WIDTH-1:2]; always_ff @(posedge clk) begin if (!rst_n) begin rd_data <= 32'd0; rd_valid <= 1'b0; 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 begin rd_valid <= rd_en; if (rd_en) begin rd_data <= mem[word_index]; ev_valid <= 1'b1; ev_subsys <= SUBSYS_MEM; ev_event <= EV_READ; ev_arg0 <= {{(64-ADDR_WIDTH){1'b0}}, rd_addr}; ev_arg1 <= {32'd0, mem[word_index]}; ev_arg2 <= 64'd0; // master: EE_IFETCH ev_arg3 <= 64'd0; // region: BIOS ev_flags <= 32'd0; end else begin ev_valid <= 1'b0; end end end endmodule : bios_rom_stub