// retroDE_ps2 — tb_top_psmct32_tile_cap_overflow_demo (Ch315)
//
// CAPACITY-CEILING / GRACEFUL-CLAMP proof. The SAME 6-deep-in-t0 capacity payload
// (payload_tile_cap), but a DEPTH-4 FIFO (TILE_FIFO_DEPTH=4, the OLD size) with
// TILE_PRIM_COUNT=4. The scene wants 6 prims in tile t0, but a depth-4 FIFO can only
// batch 4: bin_occ_max_r CLAMPS at 4 (the capacity ceiling — vs occ_max=6 at depth 8
// in tb_top_psmct32_tile_cap_demo), and the clamped 4-prim batch still renders ALL 16
// tiles and the core halts (graceful degradation, no hang/crash).
//
// This is the realistic capacity limit for the BATCHED tile path: the render triggers
// at TILE_PRIM_COUNT and DRAINS the FIFO, so excess prims are clamped (not push-dropped)
// — raster_overflow (the streaming-path push-while-full FLAG, counted by the new
// raster_overflow_count_r) does not fire here. The pair {this, cap_demo} shows the
// resource/behaviour difference between depth 4 and depth 8 directly.
//
// PROOFS:
//   CEILING  : bin_occ_max_r == 4 (depth-4 FIFO clamped the 6-prim t0 cluster to 4).
//   GRACEFUL : all 16 tiles cleared (256 each) + 16*256 flush — the clamped batch
//              rendered fully; core_halt asserted (no hang).
//   DIAG     : the overflow counters are wired + readable (raster_overflow_count_r,
//              bin_overflow_r reported).

`timescale 1ns/1ps

module tb_top_psmct32_tile_cap_overflow_demo;

    localparam int H_ACTIVE = 64, V_ACTIVE = 64;
    localparam int TP_CLEAR = 1;

    logic clk, 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, dma_done_seen, frame_seen, raster_overflow;
    logic        frame_toggle, dma_done_toggle;

    top_psmct32_raster_demo_bram #(
        .H_ACTIVE(H_ACTIVE), .V_ACTIVE(V_ACTIVE),
        .VRAM_BYTES(32*1024), .PSMCT32_SWIZZLE(1'b0),
        .COMBINED_TAZ(1'b1), .TILE_LOCAL(1'b1), .TILE_COLS(4), .TILE_ROWS(4),
        .TILE_MULTIPRIM(1'b1), .TILE_PRIM_COUNT(4), .TILE_FIFO_DEPTH(4),
        .BIN_BUFFER_ENABLE(1'b1)
    ) 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)
    );

    int clear_cw [0:15];
    int flush_emits;
    initial begin for(int t=0;t<16;t++) clear_cw[t]=0; flush_emits=0; end
    always_ff @(posedge clk) if (rst_n) begin
        if (dut.u_gs.tile_phase==TP_CLEAR && dut.u_gs.tile_color_we) begin
            int t; t=int'(dut.u_gs.tile_row_r)*4 + int'(dut.u_gs.tile_col_r);
            if (t>=0 && t<16) clear_cw[t] <= clear_cw[t] + 1;
        end
        if (dut.u_gs.raster_pixel_emit) flush_emits <= flush_emits + 1;
    end

    int errors, occ_max, ovf_count; logic ovf;

    initial begin
        errors=0;
        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);
        // Fixed settle (no raster_active edge wait): under overflow the dropped-prim
        // pushes + degraded render can perturb raster_active timing, so just wait a
        // generous window for the surviving 4-prim batch to clear+render+flush all 16
        // tiles, then sample the (stable) overflow flags + clear/flush counters.
        repeat(120000) @(posedge clk);
        occ_max   = int'(dut.u_gs.bin_occ_max_r);
        ovf_count = int'(dut.u_gs.raster_overflow_count_r);
        ovf       = dut.u_gs.raster_overflow;

        // CEILING — depth-4 FIFO clamps the 6-prim t0 cluster to exactly 4.
        if (occ_max != 4)    begin $error("[ovf] bin_occ_max=%0d (want 4 — depth-4 ceiling)", occ_max); errors++; end
        // GRACEFUL — the clamped 4-prim batch still renders all 16 tiles, no hang.
        for (int t=0;t<16;t++)
            if (clear_cw[t] != 256) begin $error("[ovf] tile %0d cleared %0d (want 256 — render did not complete)", t, clear_cw[t]); errors++; end
        if (flush_emits != 16*256) begin $error("[ovf] flush emitted %0d (want %0d)", flush_emits, 16*256); errors++; end
        if (!core_halt)      begin $error("[ovf] core_halt low (hang)"); errors++; end

        $display("[tb_top_psmct32_tile_cap_overflow_demo] ovf=%0b ovf_count=%0d occ_max=%0d flush=%0d errors=%0d",
                 ovf, ovf_count, occ_max, flush_emits, errors);
        if (errors==0) $display("[tb_top_psmct32_tile_cap_overflow_demo] PASS");
        else           $display("[tb_top_psmct32_tile_cap_overflow_demo] FAIL");
        $finish;
    end

    initial begin #600000000; $error("[tb_top_psmct32_tile_cap_overflow_demo] TIMEOUT"); $finish; end
endmodule : tb_top_psmct32_tile_cap_overflow_demo
