// retroDE_ps2 — tb_gs_stub
//
// Milestone A slice: verifies gs_stub's BGCOLOR latch, non-BGCOLOR
// privileged-register logging, stable-state holding, and trace emission.
// Does not instantiate platform_video_stub (covered separately where a full
// Milestone A test is warranted).
//
// Plan ref: docs/stub_module_plan.md (Initial testbench plan).

`timescale 1ns/1ps

module tb_gs_stub;

    logic clk;
    logic rst_n;

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

    // ------------------------------------------------------------------
    // DUT and trace sink
    // ------------------------------------------------------------------

    logic        reg_wr_en;
    logic [15:0] reg_wr_addr;
    logic [63:0] reg_wr_data;

    logic [7:0]  bg_r, bg_g, bg_b;

    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_stub u_gs (
        .clk(clk), .rst_n(rst_n),
        .reg_wr_en(reg_wr_en), .reg_wr_addr(reg_wr_addr), .reg_wr_data(reg_wr_data),
        .gif_reg_wr_en(1'b0), .gif_reg_num(8'd0), .gif_reg_data(64'd0),
        .prim_q(), .rgbaq_q(), .xyz2_q(), .xyzf2_q(), .frame_1_q(), .zbuf_1_q(),
        .prim_complete(), .prim_complete_count(),
        .prim_v0_q(), .prim_v1_q(), .prim_v2_q(),
        .prim_color_q(),
        .prim_color_v0_q(), .prim_color_v1_q(), .prim_color_v2_q(),
        .prim_v0_decoded_q(), .prim_v1_decoded_q(), .prim_v2_decoded_q(),
        .prim_v0_color_decoded_q(), .prim_v1_color_decoded_q(), .prim_v2_color_decoded_q(),
        .pixel_emit(), .pixel_emit_count(),
        .pixel_x_q(), .pixel_y_q(),
        .pixel_color_q(),
        .pixel_fbp_q(), .pixel_fbw_q(), .pixel_psm_q(),
        .pixel_fb_addr_q(),
        .raster_pixel_emit(), .raster_pixel_emit_count(),
        .raster_pixel_x_q(), .raster_pixel_y_q(),
        .raster_pixel_color_q(),
        .raster_pixel_fb_addr_q(),
        .raster_active(), .raster_overflow(),
        .raster_degenerate(),
        .bg_r(bg_r), .bg_g(bg_g), .bg_b(bg_b),
        .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)
    );

    trace_sink_stub #(.FILENAME("gs.trace"), .SINK_LABEL("gs")) u_trace_gs (
        .clk(clk), .rst_n(rst_n),
        .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)
    );

    // ------------------------------------------------------------------
    // Checkers
    // ------------------------------------------------------------------

    int bgcolor_events;
    int mode_events;
    int errors;

    initial begin
        bgcolor_events = 0;
        mode_events    = 0;
        errors         = 0;
    end

    always_ff @(posedge clk) begin
        if (rst_n && ev_valid) begin
            if (ev_event == trace_pkg::EV_BGCOLOR) bgcolor_events <= bgcolor_events + 1;
            if (ev_event == trace_pkg::EV_MODE)    mode_events    <= mode_events + 1;
        end
    end

    task automatic write_reg(input logic [15:0] offset, input logic [63:0] data);
        @(negedge clk);
        reg_wr_en   = 1'b1;
        reg_wr_addr = offset;
        reg_wr_data = data;
        @(negedge clk);
        reg_wr_en   = 1'b0;
        reg_wr_addr = 16'd0;
        reg_wr_data = 64'd0;
    endtask

    task automatic expect_bg(input logic [7:0] r, input logic [7:0] g, input logic [7:0] b,
                             input string label);
        if (bg_r !== r || bg_g !== g || bg_b !== b) begin
            $error("[tb_gs_stub] %s: bg=(%02h,%02h,%02h) expected=(%02h,%02h,%02h)",
                   label, bg_r, bg_g, bg_b, r, g, b);
            errors = errors + 1;
        end
    endtask

    // ------------------------------------------------------------------
    // Stimulus
    // ------------------------------------------------------------------

    initial begin
        rst_n       = 1'b0;
        reg_wr_en   = 1'b0;
        reg_wr_addr = 16'd0;
        reg_wr_data = 64'd0;

        repeat (4) @(posedge clk);
        rst_n = 1'b1;
        repeat (2) @(posedge clk);

        // Default reset value should be mid-grey.
        expect_bg(8'h40, 8'h40, 8'h40, "post-reset default");

        // Write BGCOLOR = (R=0xAA, G=0xBB, B=0xCC).
        write_reg(16'h00E0, 64'h0000_0000_00AA_BBCC);
        repeat (2) @(posedge clk);
        expect_bg(8'hAA, 8'hBB, 8'hCC, "after BGCOLOR write");

        // Stable-state: color should hold for many cycles without additional writes.
        repeat (20) @(posedge clk);
        expect_bg(8'hAA, 8'hBB, 8'hCC, "stable hold");

        // Write to a non-BGCOLOR privileged register (PMODE at offset 0x000) —
        // should log EV_MODE but leave BGCOLOR untouched.
        write_reg(16'h0000, 64'h0000_0000_0000_0001);
        repeat (2) @(posedge clk);
        expect_bg(8'hAA, 8'hBB, 8'hCC, "after PMODE write");

        // Overwrite BGCOLOR with a second value.
        write_reg(16'h00E0, 64'h0000_0000_0011_2233);
        repeat (2) @(posedge clk);
        expect_bg(8'h11, 8'h22, 8'h33, "after second BGCOLOR write");

        // ------------------------------------------------------------------
        $display("[tb_gs_stub] bgcolor_events=%0d mode_events=%0d errors=%0d",
                 bgcolor_events, mode_events, errors);

        if (bgcolor_events < 2)
            $error("[tb_gs_stub] expected at least 2 BGCOLOR events, got %0d", bgcolor_events);
        if (mode_events < 1)
            $error("[tb_gs_stub] expected at least 1 MODE event, got %0d", mode_events);

        if (errors == 0 && bgcolor_events >= 2 && mode_events >= 1)
            $display("[tb_gs_stub] PASS");
        else
            $display("[tb_gs_stub] FAIL");

        $finish;
    end

    initial begin
        #100000;
        $error("[tb_gs_stub] timeout");
        $finish;
    end

endmodule : tb_gs_stub
