// retroDE_ps2 — tb_gs_fog_disabled
//
// Proves the FOG_ENABLE=0 compile gate: with gs_stub #(.FOG_ENABLE(1'b0)),
// a PRIM.FGE=1 primitive carrying real per-vertex fog + a nonzero FOGCOL emits
// the RAW (unfogged) post-texture/Gouraud color. Because FOG_ENABLE is a param
// constant, `(FOG_ENABLE && ras_fge)` folds to 0 and each fog mux selects the
// raw color — the exact pre-fog datapath the synthesizer keeps (fog cone,
// including s2_fog_f, pruned). Functional equivalence to the raw color here is
// the observable proof that the fold target is the pre-fog path.

`timescale 1ns/1ps

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

    logic        gif_reg_wr_en; logic [7:0] gif_reg_num; logic [63:0] gif_reg_data;
    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;

    // Fog compiled OUT.
    gs_stub #(.FOG_ENABLE(1'b0)) 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));

    task automatic drive_reg(input logic [7:0] num, input logic [63:0] data);
        @(negedge clk); gif_reg_wr_en=1'b1; gif_reg_num=num; gif_reg_data=data; @(posedge clk);
    endtask
    task automatic drive_idle();
        @(negedge clk); gif_reg_wr_en=1'b0; gif_reg_num=8'd0; gif_reg_data=64'd0; @(posedge clk);
    endtask
    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
    function automatic logic [63:0] rgbaq(input int r, input int g, input int b, input int a);
        return {32'd0, 8'(a), 8'(b), 8'(g), 8'(r)};
    endfunction

    localparam logic [7:0] R_PRIM=8'h00, R_RGBAQ=8'h01, R_XYZF2=8'h04, R_FOGCOL=8'h3D, R_FRAME_1=8'h4C;
    localparam logic [63:0] PRIM_TRI_FGE = 64'd3 | (64'd1<<5);
    localparam logic [63:0] FRAME_1_VAL  = 64'h0000_0000_0001_0000;
    localparam int TC_R=8'hC0, TC_G=8'h80, TC_B=8'h30, TC_A=8'hFF;

    bit          covered [0:15][0:15];
    logic [31:0] cap_c   [0:15][0:15];
    bit cap_armed;
    always_ff @(posedge clk) 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

    int errors, checks;
    task automatic chk_raw(input int x, input int y);
        // With FOG_ENABLE=0, even FGE=1 + FOGCOL!=0 must emit the RAW flat color.
        if (!covered[y][x]) begin $error("[fogoff] (%0d,%0d) not covered", x, y); errors++; end
        else if (cap_c[y][x][7:0]!==TC_R[7:0] || cap_c[y][x][15:8]!==TC_G[7:0] ||
                 cap_c[y][x][23:16]!==TC_B[7:0] || cap_c[y][x][31:24]!==TC_A[7:0]) begin
            $error("[fogoff] (%0d,%0d) got %08x expected RAW (%0d,%0d,%0d,a=%0d) — fog NOT pruned",
                   x, y, cap_c[y][x], TC_R, TC_G, TC_B, TC_A); errors++;
        end else begin
            $display("[fogoff] (%0d,%0d) RAW color (fog pruned) OK", x, y); checks++;
        end
    endtask

    initial begin
        errors=0; checks=0; cap_armed=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
        rst_n=0; gif_reg_wr_en=0; gif_reg_num=0; gif_reg_data=0;
        repeat(4) @(posedge clk); rst_n=1; repeat(2) @(posedge clk);

        // FGE=1 triangle with DISTINCT per-vertex F and a NONZERO FOGCOL: if fog
        // were active these would be visibly blended; with FOG_ENABLE=0 they are not.
        cap_armed=1;
        drive_reg(R_FOGCOL,  64'h00_60_40_20);         // nonzero FOGCOL (would change pixels if fog on)
        drive_reg(R_PRIM,    PRIM_TRI_FGE);
        drive_reg(R_FRAME_1, FRAME_1_VAL);
        drive_reg(R_RGBAQ,   rgbaq(TC_R,TC_G,TC_B,TC_A));
        drive_reg(R_XYZF2,   xyzf2(2,1,8'h10));
        drive_reg(R_RGBAQ,   rgbaq(TC_R,TC_G,TC_B,TC_A));
        drive_reg(R_XYZF2,   xyzf2(13,2,8'hF0));
        drive_reg(R_RGBAQ,   rgbaq(TC_R,TC_G,TC_B,TC_A));
        drive_reg(R_XYZF2,   xyzf2(5,7,8'h80));         // closes
        drive_idle();
        repeat(300) @(posedge clk); cap_armed=0; @(posedge clk);

        chk_raw(7,3); chk_raw(5,4); chk_raw(6,2);
        if (checks==0) begin $error("[fogoff] no interior pixels verified"); errors++; end

        $display("[tb_gs_fog_disabled] checks=%0d errors=%0d", checks, errors);
        if (errors==0) $display("[tb_gs_fog_disabled] PASS");
        else           $display("[tb_gs_fog_disabled] FAIL");
        $finish;
    end
    initial begin #3000000; $error("[tb_gs_fog_disabled] timeout"); $finish; end
endmodule : tb_gs_fog_disabled
