// retroDE_ps2 — tb_gs_feeder_fog_e2e
//
// END-TO-END GS per-vertex FOG through the runtime feeder: a staging list with
// PRIM.FGE=1 drives gs_prim_list_feeder, whose gif_reg_* stream feeds gs_stub.
// Proves the full data path — the feeder tags the vertex commit XYZF2 (reg 0x04)
// carrying the per-vertex fog byte, and gs_stub applies the fog blend at emit.
//
// One untextured flat Gouraud triangle, flat per-vertex F, FOGCOL left at the
// RTL default 0 (black) — the SH3 scene's value. So the emitted RGB must equal
// (C * F) >> 8 per channel (fog toward black), alpha unchanged, at interior px.

`timescale 1ns/1ps

module tb_gs_feeder_fog_e2e;
    localparam int STG_ADDR_W = 12;

    logic clk; logic rst_n;
    initial clk = 1'b0; always #5 clk = ~clk;

    // feeder <-> staging + gs_stub
    logic                   start, busy, done;
    logic [15:0]            records_emitted;
    logic [31:0]            fifo_wait_cycles;
    logic [STG_ADDR_W-1:0]  stg_rd_addr;
    logic [63:0]            stg_rd_data;
    logic                   gif_reg_wr_en;
    logic [7:0]             gif_reg_num;
    logic [63:0]            gif_reg_data;

    // staging RAM
    logic [63:0] stg [0:127];
    always_ff @(posedge clk) stg_rd_data <= stg[stg_rd_addr[6:0]];

    // gs_stub outputs we watch
    logic [7:0]  bg_r, bg_g, bg_b;
    logic [63:0] prim_q, rgbaq_q, xyz2_q, xyzf2_q, frame_1_q, zbuf_1_q;
    logic        prim_complete; logic [31:0] prim_complete_count;
    logic [63:0] prim_v0_q, prim_v1_q, prim_v2_q, prim_color_q;
    logic [63:0] prim_color_v0_q, prim_color_v1_q, prim_color_v2_q;
    trace_pkg::vertex_t prim_v0_decoded_q, prim_v1_decoded_q, prim_v2_decoded_q;
    trace_pkg::color_t  prim_v0_color_decoded_q, prim_v1_color_decoded_q, prim_v2_color_decoded_q;
    logic        pixel_emit; logic [31:0] pixel_emit_count;
    logic [11:0] pixel_x_q, pixel_y_q; logic [63:0] pixel_color_q;
    logic [8:0]  pixel_fbp_q; logic [5:0] pixel_fbw_q, pixel_psm_q; logic [31:0] pixel_fb_addr_q;
    logic        raster_pixel_emit; logic [31:0] raster_pixel_emit_count;
    logic [11:0] raster_pixel_x_q, raster_pixel_y_q; logic [63:0] raster_pixel_color_q;
    logic [31:0] raster_pixel_fb_addr_q; logic [3:0] raster_pixel_be_q;
    logic [31:0] raster_pixel_mask_q; logic [5:0] raster_pixel_psm_q;
    logic        raster_active, raster_overflow, raster_fifo_full, raster_degenerate;
    logic        tex_rd_en; logic [31:0] tex_rd_addr;
    logic        fb_rd_en;  logic [31:0] fb_rd_addr;
    logic        z_rd_en;   logic [31:0] z_rd_addr;
    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;

    gs_prim_list_feeder #(.STG_ADDR_W(STG_ADDR_W)) u_feeder (
        .clk(clk), .rst_n(rst_n), .start(start), .busy(busy), .done(done),
        .records_emitted(records_emitted), .fifo_wait_cycles(fifo_wait_cycles),
        .stg_rd_addr(stg_rd_addr), .stg_rd_data(stg_rd_data),
        .fifo_full(raster_fifo_full),
        .gif_reg_wr_en(gif_reg_wr_en), .gif_reg_num(gif_reg_num), .gif_reg_data(gif_reg_data));

    gs_stub u_gs (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(1'b0), .reg_wr_addr(16'd0), .reg_wr_data(64'd0),
        .gif_reg_wr_en(gif_reg_wr_en), .gif_reg_num(gif_reg_num), .gif_reg_data(gif_reg_data),
        .bg_r(bg_r), .bg_g(bg_g), .bg_b(bg_b),
        .prim_q(prim_q), .rgbaq_q(rgbaq_q), .xyz2_q(xyz2_q), .xyzf2_q(xyzf2_q),
        .frame_1_q(frame_1_q), .zbuf_1_q(zbuf_1_q),
        .prim_complete(prim_complete), .prim_complete_count(prim_complete_count),
        .prim_v0_q(prim_v0_q), .prim_v1_q(prim_v1_q), .prim_v2_q(prim_v2_q),
        .prim_color_q(prim_color_q),
        .prim_color_v0_q(prim_color_v0_q), .prim_color_v1_q(prim_color_v1_q), .prim_color_v2_q(prim_color_v2_q),
        .prim_v0_decoded_q(prim_v0_decoded_q), .prim_v1_decoded_q(prim_v1_decoded_q), .prim_v2_decoded_q(prim_v2_decoded_q),
        .prim_v0_color_decoded_q(prim_v0_color_decoded_q), .prim_v1_color_decoded_q(prim_v1_color_decoded_q), .prim_v2_color_decoded_q(prim_v2_color_decoded_q),
        .pixel_emit(pixel_emit), .pixel_emit_count(pixel_emit_count),
        .pixel_x_q(pixel_x_q), .pixel_y_q(pixel_y_q), .pixel_color_q(pixel_color_q),
        .pixel_fbp_q(pixel_fbp_q), .pixel_fbw_q(pixel_fbw_q), .pixel_psm_q(pixel_psm_q), .pixel_fb_addr_q(pixel_fb_addr_q),
        .raster_pixel_emit(raster_pixel_emit), .raster_pixel_emit_count(raster_pixel_emit_count),
        .raster_pixel_x_q(raster_pixel_x_q), .raster_pixel_y_q(raster_pixel_y_q), .raster_pixel_color_q(raster_pixel_color_q),
        .raster_pixel_fb_addr_q(raster_pixel_fb_addr_q), .raster_pixel_be_q(raster_pixel_be_q),
        .raster_pixel_mask_q(raster_pixel_mask_q), .raster_pixel_psm_q(raster_pixel_psm_q),
        .raster_active(raster_active), .raster_overflow(raster_overflow),
        .raster_fifo_full(raster_fifo_full), .raster_degenerate(raster_degenerate),
        .tex_rd_en(tex_rd_en), .tex_rd_addr(tex_rd_addr), .tex_rd_data(32'd0),
        .fb_rd_en(fb_rd_en), .fb_rd_addr(fb_rd_addr), .fb_rd_data(32'd0),
        .z_rd_en(z_rd_en), .z_rd_addr(z_rd_addr), .z_rd_data(32'd0),
        .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));

    // flat triangle color + fog
    localparam int CR=8'hC0, CG=8'h80, CB=8'h40, CA=8'hFF, FF=8'h80;
    function automatic logic [63:0] xyzf2(input int x, input int y, input int f);
        return {8'(f), 24'd0, 12'(y), 4'd0, 12'(x), 4'd0};
    endfunction

    // capture emitted pixels
    bit          covered [0:15][0:15];
    logic [31:0] cap_c   [0:15][0:15];
    bit cap_armed;
    always_ff @(posedge clk) begin
        if (rst_n && cap_armed && raster_pixel_emit
            && raster_pixel_x_q < 16 && raster_pixel_y_q < 16) begin
            covered[raster_pixel_y_q][raster_pixel_x_q] <= 1'b1;
            cap_c  [raster_pixel_y_q][raster_pixel_x_q] <= raster_pixel_color_q[31:0];
        end
    end

    int errors, checks;
    function automatic int fogblack(input int c, input int f); return (c*f) >> 8; endfunction
    task automatic chk(input int x, input int y);
        int er, eg, eb;
        er=fogblack(CR,FF); eg=fogblack(CG,FF); eb=fogblack(CB,FF);
        if (!covered[y][x]) begin $error("[e2e] (%0d,%0d) not covered", x, y); errors++; end
        else if (cap_c[y][x][7:0]!==er[7:0] || cap_c[y][x][15:8]!==eg[7:0] ||
                 cap_c[y][x][23:16]!==eb[7:0] || cap_c[y][x][31:24]!==CA[7:0]) begin
            $error("[e2e] (%0d,%0d) got %08x expected fog-black (R=%0d G=%0d B=%0d A=%0d)",
                   x, y, cap_c[y][x], er, eg, eb, CA); errors++;
        end else begin
            $display("[e2e] (%0d,%0d) got (%0d,%0d,%0d,a=%0d) fog-black EXACT OK",
                     x, y, cap_c[y][x][7:0], cap_c[y][x][15:8], cap_c[y][x][23:16], cap_c[y][x][31:24]);
            checks++;
        end
    endtask

    initial begin
        errors=0; checks=0; cap_armed=0; start=0;
        for (int y=0;y<16;y++) for (int x=0;x<16;x++) begin covered[y][x]=0; cap_c[y][x]=0; end
        for (int i=0;i<128;i++) stg[i]=64'd0;
        // staging: 1 tri, shared state, PRIM+FGE, 3 vertices (RGBAQ/UV/XYZF2)
        stg[0]=64'd1;
        stg[1]=64'h0000_0000_0001_0000;                 // FRAME FBW=1 PSMCT32
        stg[2]=64'd0; stg[3]=64'd0; stg[4]=64'd0; stg[5]=64'd0;   // ALPHA/TEST/ZBUF/TEX0
        stg[6]=64'd3 | (64'h1<<5);                       // PRIM: TRI + FGE (TME=0)
        // v0
        stg[7]={32'd0, 8'(CA), 8'(CB), 8'(CG), 8'(CR)}; stg[8]=64'd0; stg[9]=xyzf2(2,2,FF);
        // v1
        stg[10]={32'd0, 8'(CA), 8'(CB), 8'(CG), 8'(CR)}; stg[11]=64'd0; stg[12]=xyzf2(12,3,FF);
        // v2 (closes)
        stg[13]={32'd0, 8'(CA), 8'(CB), 8'(CG), 8'(CR)}; stg[14]=64'd0; stg[15]=xyzf2(4,9,FF);

        rst_n=0; repeat(4) @(posedge clk); rst_n=1; repeat(2) @(posedge clk);
        cap_armed=1;
        @(negedge clk); start=1; @(negedge clk); start=0;
        wait (done==1'b1);
        repeat (400) @(posedge clk);
        cap_armed=0; @(posedge clk);

        if (raster_pixel_emit_count == 0) begin
            $error("[e2e] feeder->gs_stub rendered 0 pixels"); errors++;
        end
        // interior pixels of the triangle
        chk(5,4); chk(4,5); chk(6,4);
        if (checks == 0) begin $error("[e2e] no interior pixels verified"); errors++; end

        $display("[tb_gs_feeder_fog_e2e] emit_count=%0d checks=%0d errors=%0d",
                 raster_pixel_emit_count, checks, errors);
        if (errors==0) $display("[tb_gs_feeder_fog_e2e] PASS");
        else           $display("[tb_gs_feeder_fog_e2e] FAIL");
        $finish;
    end

    initial begin #3000000; $error("[tb_gs_feeder_fog_e2e] timeout"); $finish; end
endmodule : tb_gs_feeder_fog_e2e
