// retroDE_ps2 — tb_top_psmct32_alpha_blend_demo (Brick 2a)
//
// TOP-LEVEL alpha-blend (transparency) demo TB for the BRAM BOARD
// VARIANT. Proves the synthesizable FLAT alpha-blended SPRITE path
// renders end-to-end through `top_psmct32_raster_demo_bram` — the
// variant the de25_nano board top instantiates — exactly as a board
// load would SHOW on HDMI.
//
//   EE bootlet (bios_alpha.mem) + GIF payload (payload_alpha.mem):
//     U1 PACKED  -> opaque BG SPRITE (ABE=0) solid blue, full 16x8.
//     U2 PACKED  -> semi-transparent overlay SPRITE (PRIM.ABE=1,
//                   source-over ALPHA_1, RGBAQ.A=0x40, red) over x[0..7].
//
// The overlay pixel is a read-MODIFY-write against the framebuffer:
// the GS reads the BG pixel back through vram_bram_stub.read2 (the same
// spare port the texel fetch uses — mutually exclusive because a flat
// blend never textures) and blends source-over.
//
//   Source-over Cv = ((Cs - Cd) * As) >> 7 + Cd, As=0x40 (=64):
//     overlap (x in [0..7]) -> (0x7F, 0x00, 0x60)
//     non-overlap (x in [8..15]) -> pure BG blue (0x00, 0x00, 0xC0)
//
// Verification is via the PCRTC scanout RGB (the same r/g/b/de a board
// wires to a video PHY). The overlap is asserted to be NEITHER the pure
// source (0xFF,0x00,0x00) NOR the pure dest (0x00,0x00,0xC0) — proving
// real blending happened, not a flat copy.

`timescale 1ns/1ps

module tb_top_psmct32_alpha_blend_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;

    top_psmct32_raster_demo_bram #(
        .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),
        .joy_a_pressed_i(1'b0),
        .joy_b_pressed_i(1'b0)
    );

    // ----- Expected colors -----
    // Background (opaque) blue, overlay (red, As=0x40), source-over blend.
    localparam logic [7:0] BG_R = 8'h00, BG_G = 8'h00, BG_B = 8'hC0;
    localparam logic [7:0] OV_R = 8'hFF, OV_G = 8'h00, OV_B = 8'h00;
    // Blend = ((Cs - Cd) * 64) >> 7 + Cd, per channel:
    //   R: (255-0)*64>>7 + 0   = 127 = 0x7F
    //   G: (0-0)              = 0   = 0x00
    //   B: (0-192)*64>>7 + 192 = 96  = 0x60
    localparam logic [7:0] BL_R = 8'h7F, BL_G = 8'h00, BL_B = 8'h60;

    // ----- Frame capture (delayed counters: BRAM read is registered) -----
    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

    logic [31:0] hcnt_d;
    logic [31:0] vcnt_d;
    always_ff @(posedge clk) begin
        if (!rst_n) begin
            hcnt_d <= 32'd0;
            vcnt_d <= 32'd0;
        end else begin
            hcnt_d <= 32'(dut.u_pcrtc.hcnt);
            vcnt_d <= 32'(dut.u_pcrtc.vcnt);
        end
    end

    always_ff @(posedge clk) begin
        if (rst_n && capture_armed && de
            && (vcnt_d < V_ACTIVE) && (hcnt_d < H_ACTIVE)) begin
            cap_r [vcnt_d][hcnt_d] <= r;
            cap_g [vcnt_d][hcnt_d] <= g;
            cap_b [vcnt_d][hcnt_d] <= b;
            cap_de[vcnt_d][hcnt_d] <= 1'b1;
        end
    end

    int errors;
    int blend_pixels_ok;
    int bg_pixels_ok;
    initial begin
        errors = 0;
        blend_pixels_ok = 0;
        bg_pixels_ok = 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;

        // --- Overlap region (x in [0..7]): must equal the source-over blend
        //     of overlay-red over BG-blue, AND be neither pure src nor dest. ---
        for (int y = 0; y < 8; y++) begin
            for (int x = 0; x < 8; x++) begin
                if (!cap_de[y][x]) begin
                    $error("blend (%0d,%0d) DE never asserted", x, y);
                    errors = errors + 1;
                end else if (cap_r[y][x] !== BL_R || cap_g[y][x] !== BL_G || cap_b[y][x] !== BL_B) begin
                    $error("[blend] (%0d,%0d) got (%02x,%02x,%02x) expected (%02x,%02x,%02x)",
                           x, y, cap_r[y][x], cap_g[y][x], cap_b[y][x], BL_R, BL_G, BL_B);
                    errors = errors + 1;
                end else begin
                    blend_pixels_ok = blend_pixels_ok + 1;
                end
                // Guard: blending actually happened (not a flat copy of
                // either operand).
                if (cap_r[y][x] === OV_R && cap_g[y][x] === OV_G && cap_b[y][x] === OV_B) begin
                    $error("[blend] (%0d,%0d) is PURE SOURCE — no blend happened", x, y);
                    errors = errors + 1;
                end
                if (cap_r[y][x] === BG_R && cap_g[y][x] === BG_G && cap_b[y][x] === BG_B) begin
                    $error("[blend] (%0d,%0d) is PURE DEST — overlay never drew", x, y);
                    errors = errors + 1;
                end
            end
        end

        // --- Non-overlap region (x in [8..15]): pure BG blue (overlay
        //     does not cover it). ---
        for (int y = 0; y < 8; y++) begin
            for (int x = 8; x < 16; x++) begin
                if (!cap_de[y][x]) begin
                    $error("bg (%0d,%0d) DE never asserted", x, y);
                    errors = errors + 1;
                end else if (cap_r[y][x] !== BG_R || cap_g[y][x] !== BG_G || cap_b[y][x] !== BG_B) begin
                    $error("[bg] (%0d,%0d) got (%02x,%02x,%02x) expected (%02x,%02x,%02x)",
                           x, y, cap_r[y][x], cap_g[y][x], cap_b[y][x], BG_R, BG_G, BG_B);
                    errors = errors + 1;
                end else begin
                    bg_pixels_ok = bg_pixels_ok + 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_alpha_blend_demo] blend_ok=%0d/64 bg_ok=%0d/64 raster_emits=%0d errors=%0d",
                 blend_pixels_ok, bg_pixels_ok,
                 dut.u_gs.raster_pixel_emit_count, errors);
        if (errors == 0)
            $display("[tb_top_psmct32_alpha_blend_demo] PASS");
        else
            $display("[tb_top_psmct32_alpha_blend_demo] FAIL");
        $finish;
    end

    initial begin
        #20000000;
        $error("[tb_top_psmct32_alpha_blend_demo] TIMEOUT");
        $finish;
    end

endmodule : tb_top_psmct32_alpha_blend_demo
