// retroDE_ps2 — tb_top_psmct32_textured_demo (Brick 1) // // TOP-LEVEL textured-sprite demo TB. Proves the synthesizable textured // SPRITE path renders end-to-end through the REAL hardware top // (top_psmct32_raster_demo), exactly as a board load would: // // EE bootlet (bios_textured.mem) configures DISPFB1/DISPLAY1/PMODE // and kicks DMAC ch2 to stream the GIF payload (payload_textured.mem): // U1 BITBLT/TRX -> uploads an 8x8 PSMCT32 texture to VRAM (DBP=8) // U2 IMAGE -> 16 qwords = the 64 texels // U3/U4 PACKED -> PRIM(SPRITE+TME) + TEX0 + UV + XYZ2: a textured // 8x8 sprite at screen (0,0)..(7,7) // U5 PACKED -> a FLAT green control sprite at (8,0)..(15,7) // // The top is built with PSMCT32_SWIZZLE=0 so the LINEAR gs_texel_addr // fetch and the BITBLT upload share one VRAM layout (v1 scope; the // swizzle reconciliation is the gs_stub TODO, out of scope here). // // Verification is via the PCRTC scanout RGB (the same r/g/b/de a board // wires to a video PHY) — so a PASS means the board would SHOW the // texture: // - Textured region x in [0..7]: scanout pixel == the uploaded texel // for (x,y). Texel(x,y) ABGR = {A=FF, B=y, G=x, R=0x40}, and PCRTC // PSMCT32 emits r=texel[7:0]=0x40, g=texel[15:8]=x, b=texel[23:16]=y. // - Flat region x in [8..15]: scanout pixel == the flat green color // (proves the TME=0 path still works alongside texturing). // - The textured region must NOT be uniform (it's a real sampled // gradient, not a flat fill) — guards against "texturing silently // collapsed to flat." `timescale 1ns/1ps module tb_top_psmct32_textured_demo; localparam int H_ACTIVE = 16; localparam int V_ACTIVE = 8; logic clk; logic rst_n; initial clk = 1'b0; always #5 clk = ~clk; logic core_go; logic [7:0] r, g, b; logic hsync, vsync, de; logic core_halt; logic dma_done_seen; logic frame_seen; logic raster_overflow; logic frame_toggle; logic dma_done_toggle; // PSMCT32_SWIZZLE=0 so texture-upload (BITBLT) and texel-fetch // (gs_texel_addr, linear) share one VRAM layout. top_psmct32_raster_demo #( .H_ACTIVE (H_ACTIVE), .V_ACTIVE (V_ACTIVE), .PSMCT32_SWIZZLE(1'b0) ) dut ( .clk(clk), .rst_n(rst_n), .core_go(core_go), .r(r), .g(g), .b(b), .hsync(hsync), .vsync(vsync), .de(de), .core_halt(core_halt), .dma_done_seen(dma_done_seen), .frame_seen(frame_seen), .raster_overflow(raster_overflow), .frame_toggle(frame_toggle), .dma_done_toggle(dma_done_toggle) ); // ----- Expected texel/scanout for the textured region ----- // Bold 2x2-quadrant texture (mirrors bake.py tex_demo_texel): // (x<4,y<4)=RED (x>=4,y<4)=GREEN (x<4,y>=4)=BLUE (x>=4,y>=4)=YELLOW // PCRTC PSMCT32 -> r=texel[7:0], g=texel[15:8], b=texel[23:16]. function automatic logic [7:0] tex_exp_r(input int x, input int y); return ((x<4)==(y<4)) ? 8'hFF : 8'h00; endfunction function automatic logic [7:0] tex_exp_g(input int x, input int y); return (x>=4) ? 8'hFF : 8'h00; endfunction function automatic logic [7:0] tex_exp_b(input int x, input int y); return ((x<4)&&(y>=4)) ? 8'hFF : 8'h00; endfunction // Flat control sprite color: rgbaq_data(0x20, 0xC0, 0x40). localparam logic [7:0] FLAT_R = 8'h20; localparam logic [7:0] FLAT_G = 8'hC0; localparam logic [7:0] FLAT_B = 8'h40; // ----- Frame capture ----- logic [7:0] cap_r [0:V_ACTIVE-1][0:H_ACTIVE-1]; logic [7:0] cap_g [0:V_ACTIVE-1][0:H_ACTIVE-1]; logic [7:0] cap_b [0:V_ACTIVE-1][0:H_ACTIVE-1]; logic cap_de[0:V_ACTIVE-1][0:H_ACTIVE-1]; bit capture_armed; initial begin for (int y = 0; y < V_ACTIVE; y++) for (int x = 0; x < H_ACTIVE; x++) begin cap_r[y][x] = 8'd0; cap_g[y][x] = 8'd0; cap_b[y][x] = 8'd0; cap_de[y][x] = 1'b0; end capture_armed = 1'b0; end always_ff @(posedge clk) begin if (rst_n && capture_armed && de && (dut.u_pcrtc.vcnt < V_ACTIVE) && (dut.u_pcrtc.hcnt < H_ACTIVE)) begin cap_r [dut.u_pcrtc.vcnt][dut.u_pcrtc.hcnt] <= r; cap_g [dut.u_pcrtc.vcnt][dut.u_pcrtc.hcnt] <= g; cap_b [dut.u_pcrtc.vcnt][dut.u_pcrtc.hcnt] <= b; cap_de[dut.u_pcrtc.vcnt][dut.u_pcrtc.hcnt] <= 1'b1; end end int errors; int tex_pixels_ok; int distinct_tex_values; logic [7:0] first_tex_g; initial begin errors = 0; tex_pixels_ok = 0; distinct_tex_values = 0; end initial begin rst_n = 1'b0; core_go = 1'b0; repeat (4) @(posedge clk); rst_n = 1'b1; repeat (8) @(posedge clk); @(negedge clk); core_go = 1'b1; @(negedge clk); core_go = 1'b0; wait (core_halt == 1'b1); repeat (4) @(posedge clk); wait (dma_done_seen == 1'b1); repeat (10) @(posedge clk); if (dut.xfer_busy == 1'b1) wait (dut.xfer_busy == 1'b0); if (dut.u_gs.raster_active == 1'b1) wait (dut.u_gs.raster_active == 1'b0); repeat (10) @(posedge clk); @(posedge dut.u_pcrtc.end_of_frame); @(posedge clk); capture_armed = 1'b1; @(posedge dut.u_pcrtc.end_of_frame); @(posedge clk); capture_armed = 1'b0; // --- Textured region (x in [0..7]) must match the uploaded texel. --- for (int y = 0; y < 8; y++) begin for (int x = 0; x < 8; x++) begin logic [7:0] er, eg, eb; er = tex_exp_r(x, y); eg = tex_exp_g(x, y); eb = tex_exp_b(x, y); if (!cap_de[y][x]) begin $error("textured (%0d,%0d) DE never asserted", x, y); errors = errors + 1; end else if (cap_r[y][x] !== er || cap_g[y][x] !== eg || cap_b[y][x] !== eb) begin $error("[textured] (%0d,%0d) got (%02x,%02x,%02x) expected (%02x,%02x,%02x)", x, y, cap_r[y][x], cap_g[y][x], cap_b[y][x], er, eg, eb); errors = errors + 1; end else begin tex_pixels_ok = tex_pixels_ok + 1; end end end // --- The textured region must be a real gradient, not flat. --- // (Guards against "texturing collapsed to a single texel / flat // color".) Count how many distinct G values (= sampled u) appear // across row 0. first_tex_g = cap_g[0][0]; for (int x = 1; x < 8; x++) if (cap_g[0][x] !== first_tex_g) distinct_tex_values = distinct_tex_values + 1; if (distinct_tex_values == 0) begin $error("textured region is uniform (G all = %02x) — texturing collapsed to flat", first_tex_g); errors = errors + 1; end // --- Flat control region (x in [8..15]) must be the flat color. --- for (int y = 0; y < 8; y++) begin for (int x = 8; x < 16; x++) begin if (!cap_de[y][x]) begin $error("flat (%0d,%0d) DE never asserted", x, y); errors = errors + 1; end else if (cap_r[y][x] !== FLAT_R || cap_g[y][x] !== FLAT_G || cap_b[y][x] !== FLAT_B) begin $error("[flat] (%0d,%0d) got (%02x,%02x,%02x) expected (%02x,%02x,%02x)", x, y, cap_r[y][x], cap_g[y][x], cap_b[y][x], FLAT_R, FLAT_G, FLAT_B); errors = errors + 1; end end end // --- Status bundle. --- if (!core_halt) begin $error("core_halt low at end"); errors = errors + 1; end if (!dma_done_seen) begin $error("dma_done_seen never latched"); errors = errors + 1; end if (!frame_seen) begin $error("frame_seen never latched"); errors = errors + 1; end if (raster_overflow) begin $error("raster_overflow set"); errors = errors + 1; end $display("[tb_top_psmct32_textured_demo] tex_ok=%0d/64 gradient_distinct=%0d raster_emits=%0d errors=%0d", tex_pixels_ok, distinct_tex_values, dut.u_gs.raster_pixel_emit_count, errors); if (errors == 0) $display("[tb_top_psmct32_textured_demo] PASS"); else $display("[tb_top_psmct32_textured_demo] FAIL"); $finish; end initial begin #20000000; $error("[tb_top_psmct32_textured_demo] TIMEOUT"); $finish; end endmodule : tb_top_psmct32_textured_demo