// retroDE_ps2 — tb_ee_biu_mmio // // Chapter 9 targeted TB for ee_biu_mmio_stub. Same semantic shape // as tb_ee_bootstrap_mmio (latched register file, per-byte write // latching, 1-cycle read latency) but a separate module and a // narrower 4 KiB address space. Verifies: // // 1) Reset-init-to-zero for probed offsets. // 2) Write-then-read round-trip returns written value. // 3) Distinct offsets don't collide. // 4) Per-lane byte-enable preservation (SB-through-window, SH- // through-window, and be=0 as no-op). These matter for the // same reason as chapter 8 — the EE's sub-word store // opcodes (SB/SH) can target this window. // // Targets the concrete offset the real BIOS touches: 0x130 (maps // to word index 0x4C inside the 4 KiB window). That specific // offset gets its own case, so if someone later narrows the // decode and accidentally drops that offset, this TB catches it. `timescale 1ns/1ps module tb_ee_biu_mmio; logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; // DUT ports logic reg_wr_en; logic [11:0] reg_wr_addr; logic [31:0] reg_wr_data; logic [3:0] reg_wr_be; logic reg_rd_en; logic [11:0] reg_rd_addr; logic [31:0] reg_rd_data; logic reg_rd_valid; logic ev_valid; trace_pkg::subsys_e ev_subsys; trace_pkg::event_e ev_event; logic [63:0] ev_arg0, ev_arg1, ev_arg2, ev_arg3; logic [31:0] ev_flags; ee_biu_mmio_stub dut ( .clk(clk), .rst_n(rst_n), .reg_wr_en(reg_wr_en), .reg_wr_addr(reg_wr_addr), .reg_wr_data(reg_wr_data), .reg_wr_be(reg_wr_be), .reg_rd_en(reg_rd_en), .reg_rd_addr(reg_rd_addr), .reg_rd_data(reg_rd_data), .reg_rd_valid(reg_rd_valid), .ev_valid(ev_valid), .ev_subsys(ev_subsys), .ev_event(ev_event), .ev_arg0(ev_arg0), .ev_arg1(ev_arg1), .ev_arg2(ev_arg2), .ev_arg3(ev_arg3), .ev_flags(ev_flags) ); int errors = 0; task automatic do_write_be(input logic [11:0] addr, input logic [31:0] data, input logic [3:0] be); @(negedge clk); reg_wr_en = 1'b1; reg_wr_addr = addr; reg_wr_data = data; reg_wr_be = be; @(negedge clk); reg_wr_en = 1'b0; reg_wr_addr = 12'd0; reg_wr_data = 32'd0; reg_wr_be = 4'b0000; endtask task automatic do_write(input logic [11:0] addr, input logic [31:0] data); do_write_be(addr, data, 4'b1111); endtask task automatic do_read(input logic [11:0] addr, output logic [31:0] data_out); @(negedge clk); reg_rd_en = 1'b1; reg_rd_addr = addr; @(negedge clk); reg_rd_en = 1'b0; reg_rd_addr = 12'd0; @(posedge clk); data_out = reg_rd_data; endtask task automatic check(input string tag, input logic [31:0] got, input logic [31:0] exp); if (got !== exp) begin $display("[tb_ee_biu_mmio] FAIL %s got=0x%08h exp=0x%08h", tag, got, exp); errors++; end else begin $display("[tb_ee_biu_mmio] ok %s = 0x%08h", tag, got); end endtask logic [31:0] rd0; initial begin rst_n = 1'b0; reg_wr_en = 1'b0; reg_wr_addr = 12'd0; reg_wr_data = 32'd0; reg_wr_be = 4'b0000; reg_rd_en = 1'b0; reg_rd_addr = 12'd0; repeat (4) @(posedge clk); rst_n = 1'b1; @(posedge clk); // 1) Reset-init: every probed offset reads 0. do_read(12'h000, rd0); check("reset_read_0x000", rd0, 32'd0); do_read(12'h130, rd0); check("reset_read_0x130_bios_touches_here", rd0, 32'd0); do_read(12'hFFC, rd0); check("reset_read_0xFFC", rd0, 32'd0); // 2) Write-then-read at the BIOS-touched offset. Mirrors the // real observed write pattern (cache-control config values). do_write(12'h130, 32'h3202_000F); do_read (12'h130, rd0); check("bios_0x130_writeread", rd0, 32'h3202_000F); // 3) Distinct offsets. do_write(12'h000, 32'hAAAA_AAAA); do_write(12'h140, 32'hBBBB_BBBB); do_write(12'hFFC, 32'hCCCC_CCCC); do_read (12'h000, rd0); check("distinct_0x000", rd0, 32'hAAAA_AAAA); do_read (12'h140, rd0); check("distinct_0x140", rd0, 32'hBBBB_BBBB); do_read (12'hFFC, rd0); check("distinct_0xFFC", rd0, 32'hCCCC_CCCC); do_read (12'h130, rd0); check("prior_wr_preserved", rd0, 32'h3202_000F); // 4) Per-byte enables (SB). do_write(12'h200, 32'h1122_3344); do_write_be(12'h200, 32'h0000_00AA, 4'b0001); do_read(12'h200, rd0); check("sb_lane0", rd0, 32'h1122_33AA); do_write(12'h204, 32'h1122_3344); do_write_be(12'h204, 32'hDD00_0000, 4'b1000); do_read(12'h204, rd0); check("sb_lane3", rd0, 32'hDD22_3344); // 5) Halfword enables (SH). do_write(12'h300, 32'h1122_3344); do_write_be(12'h300, 32'h0000_AABB, 4'b0011); do_read(12'h300, rd0); check("sh_low", rd0, 32'h1122_AABB); do_write(12'h304, 32'h1122_3344); do_write_be(12'h304, 32'hCCDD_0000, 4'b1100); do_read(12'h304, rd0); check("sh_high", rd0, 32'hCCDD_3344); // 6) Zero-be no-op. do_write(12'h400, 32'hDEAD_BEEF); do_write_be(12'h400, 32'hFFFF_FFFF, 4'b0000); do_read(12'h400, rd0); check("be_zero_noop", rd0, 32'hDEAD_BEEF); if (errors == 0) $display("[tb_ee_biu_mmio] PASS"); else $display("[tb_ee_biu_mmio] FAIL errors=%0d", errors); $finish; end initial begin #5_000_000; $display("[tb_ee_biu_mmio] TIMEOUT"); $finish; end endmodule : tb_ee_biu_mmio