// gs_prim_list_feeder — Ch330 Brick 1
//
// Runtime primitive-list feeder (minimal). Reads a NORMALIZED combined-TAZ triangle
// list from a small staging RAM and EXPANDS each record into the exact gif_reg_*
// write sequence gs_stub already consumes — reusing the entire proven ingestion
// (vertex window, bbox, attr packing, FIFO/grid, tile renderer). This is NOT GIF A+D
// decode: the format is per-PRIMITIVE records and the feeder knows the fixed
// "combined-TAZ triangle" shape (shared state once, then PRIM + 3 vertices each).
//
// In the Ch330 command-list profile the feeder is the EXCLUSIVE owner of gif_reg_*
// (no arbitration with the GIF unpacker / DMAC). It respects the SAME capacity
// boundary as the baked path: before the prim-completing vertex (vtx2's XYZ2) it
// PAUSES while fifo_full, so a full FIFO stalls the feeder instead of dropping prims.
//
// Staging layout (64-bit words, word-addressed):
//   [0]              : { ..., count[15:0] }   — number of triangle records
//   [1]              : FRAME_1 data           — shared state, emitted once at start
//   [2]              : ALPHA_1 data
//   [3]              : TEST_1  data
//   [4]              : ZBUF_1  data
//   [5]              : TEX0_1  data
//   [6]              : PRIM    data            — re-issued per triangle
//   [7 + 9*i + 0..2] : tri i vtx0 RGBAQ/UV/XYZ2
//   [7 + 9*i + 3..5] : tri i vtx1 RGBAQ/UV/XYZ2
//   [7 + 9*i + 6..8] : tri i vtx2 RGBAQ/UV/XYZ2
//
// Ch402 optional clamp header (word0[34]=1), backward-compatible:
//   [1..5]           : FRAME,ALPHA,TEST,ZBUF,TEX0 as above
//   [6]              : CLAMP_1 data
//   [7]              : PRIM data
//   [8 + 9*i ...]    : triangle records
//
// One `start` pulse plays the whole list; `done` pulses when it finishes.
// Boring on purpose: 2 cycles per emitted register (present addr, then drive).

`timescale 1ns/1ps

module gs_prim_list_feeder #(
    parameter int STG_ADDR_W = 12
) (
    input  logic                   clk,
    input  logic                   rst_n,

    input  logic                   start,
    output logic                   busy,
    output logic                   done,
    // Ch330 Brick 3 — observability counters (latched per list, cleared at start).
    output logic [15:0]            records_emitted,   // primitives whose final XYZ2 was emitted
    output logic [31:0]            fifo_wait_cycles,   // cycles paused at a completing kick under fifo_full

    output logic [STG_ADDR_W-1:0]  stg_rd_addr,
    input  logic [63:0]            stg_rd_data,

    input  logic                   fifo_full,

    output logic                   gif_reg_wr_en,
    output logic [7:0]             gif_reg_num,
    output logic [63:0]            gif_reg_data
);

    localparam logic [7:0] REG_PRIM    = 8'h00;
    localparam logic [7:0] REG_RGBAQ   = 8'h01;
    localparam logic [7:0] REG_ST      = 8'h02;   // Ch342 — perspective ST (S/T) for FST=0 tris
    localparam logic [7:0] REG_UV      = 8'h03;
    // GS per-vertex FOG — XYZF2 vertex commit (reg 0x04). When the record's
    // PRIM has FGE=1, the completing vertex is tagged XYZF2 so gs_stub decodes
    // the fog byte (F=[63:56]) + 24-bit Z (Z=[55:32]); FGE=0 keeps XYZ2 (0x05).
    localparam logic [7:0] REG_XYZF2   = 8'h04;
    localparam logic [7:0] REG_XYZ2    = 8'h05;
    localparam logic [7:0] REG_TEX0_1  = 8'h06;
    localparam logic [7:0] REG_ALPHA_1 = 8'h42;
    localparam logic [7:0] REG_TEST_1  = 8'h47;
    localparam logic [7:0] REG_CLAMP_1 = 8'h48;
    localparam logic [7:0] REG_FRAME_1 = 8'h4C;
    localparam logic [7:0] REG_ZBUF_1  = 8'h4E;

    localparam int OFF_COUNT = 0;
    localparam int OFF_FRAME = 1;   // FRAME,ALPHA,TEST,ZBUF,TEX0,PRIM = words 1..6
    localparam int OFF_TRIS  = 7;
    localparam int WORDS_PER_TRI = 9;

    // Header registers (loaded once).
    logic [15:0] tri_count;
    logic [63:0] hdr_q [0:6];        // legacy q[5]=PRIM; extended q[5]=CLAMP, q[6]=PRIM
    // setup-emit index -> GIF reg num (iverilog-12: no unpacked localparam array).
    function automatic logic [7:0] hdr_reg_num(input logic [2:0] i);
        unique case (i)
            3'd0:    hdr_reg_num = REG_FRAME_1;
            3'd1:    hdr_reg_num = REG_ALPHA_1;
            3'd2:    hdr_reg_num = REG_TEST_1;
            3'd3:    hdr_reg_num = REG_ZBUF_1;
            3'd4:    hdr_reg_num = REG_TEX0_1;
            default: hdr_reg_num = REG_CLAMP_1;
        endcase
    endfunction

    typedef enum logic [3:0] {
        S_IDLE,
        S_HDR_RD, S_HDR_LD,          // read legacy words 0..6 or extended words 0..7
        S_SETUP,                     // emit FRAME/ALPHA/TEST/ZBUF/TEX0[/CLAMP] from hdr_q
        S_PRIM,                      // emit the selected legacy/extended PRIM word
        S_VTX_RD, S_VTX_EMIT,        // walk the 9 vertex words of the current tri
        S_AFTER_TRIS,                // Ch334 — tris done; start rects if any, else done
        S_RECT_RD, S_RECT_LD,        // Ch334 — read a rect's 3 words (color, corner0, corner1)
        S_RECT_EMIT,                 // Ch334 — emit the 20-step 2-triangle expansion of one rect
        S_DONE
    } state_t;

    localparam int WORDS_PER_RECT = 3;          // Ch334 — color + corner0(XYZ2) + corner1(XYZ2)

    state_t      state;
    logic [3:0]  hdr_i;              // 0..6 legacy or 0..7 extended header-word index
    logic [2:0]  setup_i;            // 0..4 legacy or 0..5 extended setup-emit index
    logic [15:0] tri_idx;            // 0..tri_count-1
    logic [3:0]  vtx_word;           // 0..8 within a tri

    // Ch334 — native rectangle records (one record -> two colored triangles, expanded HERE).
    logic [15:0] rect_count;         // count[31:16]
    logic [15:0] rect_idx;           // 0..rect_count-1
    logic [1:0]  rect_word;          // 0..2 read index within a rect record
    logic [63:0] rect_color;         // RGBAQ for both triangles
    logic [63:0] rect_c0, rect_c1;   // the two opposite corners (XYZ2-packed)
    logic [4:0]  rect_emit;          // 0..19 emit step

    // Ch342 — PERSPECTIVE format flag (word0[32]). 0 = legacy RGBAQ/UV/XYZ2 per vertex (byte-exact).
    // 1 = RGBAQ/ST/XYZ2: the middle vertex word is emitted as REG_ST (host packs S_fp[23:0]/T_fp[55:32],
    // 24-bit FRAC=12) and RGBAQ carries Q_fp[55:32]; PRIM (hdr_q[5]) must be FST=0. Same 9 words/tri,
    // same 27-tri cap. Rects are not allowed in this format (rect_count forced 0 at header load).
    logic perspective_mode;
    // Ch345a — SPRITE format flag (word0[33]). 1 = each primitive is a SPRITE record: 2 vertices x
    // (RGBAQ, UV, XYZ2) = 6 words, vs a TRI's 3 vertices = 9 words. PRIM (hdr_q[5]) carries SPRITE+TME+ABE
    // and gs_stub kicks on the 2nd XYZ2 per the PRIM type. Affine UV only (perspective_mode forced 0 with
    // it); rects forced off. Same shared-state setup (FRAME/ALPHA/TEST/ZBUF/TEX0/PRIM). Narrow grammar:
    // PSMCT32 dest+tex, UV affine, ABE source-over, TCC texel alpha — the Ch344-proven subset.
    logic sprite_mode;
    logic clamp_header_mode;
    wire [3:0] header_last = clamp_header_mode ? 4'd7 : 4'd6;
    wire [STG_ADDR_W-1:0] tris_base = clamp_header_mode ? STG_ADDR_W'(8) : STG_ADDR_W'(OFF_TRIS);
    wire [63:0] prim_q = clamp_header_mode ? hdr_q[6] : hdr_q[5];
    wire [4:0] words_per_prim = sprite_mode ? 5'd6 : 5'd9;   // staging words per primitive
    wire [3:0] last_vtx_word  = sprite_mode ? 4'd5 : 4'd8;   // final XYZ2 of the primitive (the kick)
    // GS per-vertex FOG — the record's PRIM.FGE (bit 5) selects the vertex
    // commit register: FGE=1 → XYZF2 (0x04, carries the per-vertex fog byte),
    // FGE=0 → XYZ2 (0x05) exactly as before. prim_q flows to gs_stub with its
    // FGE bit intact (S_PRIM emit, unmasked), so gs_stub's ras_fge tracks it.
    wire prim_fge = prim_q[5];
    logic [7:0] vtx_reg_num;
    always_comb unique case (vtx_word % 3)
        2'd0:    vtx_reg_num = REG_RGBAQ;
        2'd1:    vtx_reg_num = perspective_mode ? REG_ST : REG_UV;
        default: vtx_reg_num = prim_fge ? REG_XYZF2 : REG_XYZ2;
    endcase
    wire vtx_completing = (vtx_word == last_vtx_word);   // final XYZ2 = the FIFO push / kick

    // Ch334 — corner fields (XYZ2 layout: x=[15:4], y=[31:20], z=[63:32]) + a packer.
    wire [11:0] rx0 = rect_c0[15:4];  wire [11:0] ry0 = rect_c0[31:20];
    wire [11:0] rx1 = rect_c1[15:4];  wire [11:0] ry1 = rect_c1[31:20];
    wire [31:0] rz  = rect_c0[63:32];
    function automatic logic [63:0] mk_xyz2(input logic [11:0] x, input logic [11:0] y, input logic [31:0] z);
        mk_xyz2 = {z, y, 4'd0, x, 4'd0};
    endfunction
    // 20-step expansion: [PRIM, (RGBAQ,UV,XYZ2)x3] x2. Two tris cover the quad (x0,y0)-(x1,y1).
    logic [7:0]  rect_reg;
    logic [63:0] rect_dat;
    always_comb begin
        unique case (rect_emit)
            5'd0, 5'd10:                                  rect_reg = REG_PRIM;
            5'd1,5'd4,5'd7,5'd11,5'd14,5'd17:             rect_reg = REG_RGBAQ;
            5'd2,5'd5,5'd8,5'd12,5'd15,5'd18:             rect_reg = REG_UV;
            default:                                      rect_reg = REG_XYZ2;  // 3,6,9,13,16,19
        endcase
        unique case (rect_emit)
            5'd0, 5'd10:                                  rect_dat = prim_q;          // PRIM
            5'd1,5'd4,5'd7,5'd11,5'd14,5'd17:             rect_dat = rect_color;      // RGBAQ
            5'd2,5'd5,5'd8,5'd12,5'd15,5'd18:             rect_dat = 64'd0;           // UV (uniform texture)
            5'd3:    rect_dat = mk_xyz2(rx0, ry0, rz);    // tri1 v0
            5'd6:    rect_dat = mk_xyz2(rx1, ry0, rz);    // tri1 v1
            5'd9:    rect_dat = mk_xyz2(rx0, ry1, rz);    // tri1 v2 (completes tri1)
            5'd13:   rect_dat = mk_xyz2(rx1, ry0, rz);    // tri2 v0
            5'd16:   rect_dat = mk_xyz2(rx0, ry1, rz);    // tri2 v1
            default: rect_dat = mk_xyz2(rx1, ry1, rz);    // tri2 v2 (idx 19, completes the rect)
        endcase
    end
    wire rect_completing = (rect_emit == 5'd9) || (rect_emit == 5'd19);  // the two FIFO pushes

    assign busy = (state != S_IDLE) && (state != S_DONE);

    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            state <= S_IDLE; tri_count <= 0; hdr_i <= 0; setup_i <= 0; perspective_mode <= 1'b0; sprite_mode <= 1'b0; clamp_header_mode <= 1'b0;
            tri_idx <= 0; vtx_word <= 0; stg_rd_addr <= '0;
            gif_reg_wr_en <= 1'b0; gif_reg_num <= 8'd0; gif_reg_data <= 64'd0; done <= 1'b0;
            records_emitted <= 16'd0; fifo_wait_cycles <= 32'd0;
            rect_count <= 0; rect_idx <= 0; rect_word <= 0; rect_emit <= 0;
            rect_color <= 64'd0; rect_c0 <= 64'd0; rect_c1 <= 64'd0;
            for (int k=0;k<7;k++) hdr_q[k] <= 64'd0;
        end else begin
            gif_reg_wr_en <= 1'b0;
            done          <= 1'b0;

            unique case (state)
                S_IDLE: if (start) begin
                    hdr_i <= 4'd0; stg_rd_addr <= STG_ADDR_W'(OFF_COUNT); state <= S_HDR_RD;
                    records_emitted <= 16'd0; fifo_wait_cycles <= 32'd0;   // clear per list
                end

                // ---- header load: words 0..6 (count + 6 shared-state words) ----
                S_HDR_RD: state <= S_HDR_LD;            // addr presented; data next cycle
                S_HDR_LD: begin
                    if (hdr_i == 4'd0) begin
                        tri_count  <= stg_rd_data[15:0];
                        // Ch342 — word0[32] = perspective format flag; rects are not allowed with it
                        // (force rect_count 0 so the rect-expansion path can never run in this format).
                        perspective_mode <= stg_rd_data[32];
                        sprite_mode      <= stg_rd_data[33];   // Ch345a
                        clamp_header_mode <= stg_rd_data[34];   // Ch402 optional CLAMP_1 header
                        // rects disallowed with the perspective OR sprite format.
                        rect_count <= (stg_rd_data[32] || stg_rd_data[33]) ? 16'd0 : stg_rd_data[31:16];
                    end
                    else               hdr_q[hdr_i-4'd1] <= stg_rd_data;
                    if (hdr_i == header_last) begin     // legacy count+6 words, or extended count+7
                        setup_i <= 3'd0; state <= S_SETUP;
                    end else begin
                        hdr_i <= hdr_i + 4'd1;
                        stg_rd_addr <= STG_ADDR_W'(OFF_COUNT) + STG_ADDR_W'(hdr_i + 4'd1);
                        state <= S_HDR_RD;
                    end
                end

                // ---- emit shared state once (from hdr_q, no staging read) ----
                S_SETUP: begin
                    gif_reg_wr_en <= 1'b1;
                    gif_reg_num   <= hdr_reg_num(setup_i);
                    gif_reg_data  <= hdr_q[setup_i];
                    if (setup_i == (clamp_header_mode ? 3'd5 : 3'd4)) begin
                        tri_idx <= 16'd0;
                        state   <= (tri_count == 16'd0) ? S_AFTER_TRIS : S_PRIM;
                    end else begin
                        setup_i <= setup_i + 3'd1;
                    end
                end

                // ---- per triangle: PRIM, then 9 vertex words ----
                S_PRIM: begin
                    gif_reg_wr_en <= 1'b1; gif_reg_num <= REG_PRIM; gif_reg_data <= prim_q;
                    vtx_word      <= 4'd0;
                    stg_rd_addr   <= tris_base + STG_ADDR_W'(tri_idx * words_per_prim);
                    state         <= S_VTX_RD;
                end
                S_VTX_RD: state <= S_VTX_EMIT;          // vert-word addr presented; data next cycle
                S_VTX_EMIT: begin
                    if (vtx_completing && fifo_full) begin
                        // pause: hold addr/data, do not emit, until the FIFO drains a slot
                        fifo_wait_cycles <= fifo_wait_cycles + 32'd1;
                        state <= S_VTX_EMIT;
                    end else begin
                        gif_reg_wr_en <= 1'b1; gif_reg_num <= vtx_reg_num; gif_reg_data <= stg_rd_data;
                        if (vtx_word == last_vtx_word) begin
                            records_emitted <= records_emitted + 16'd1;   // a primitive's final XYZ2 emitted
                            if (tri_idx + 16'd1 == tri_count) state <= S_AFTER_TRIS;
                            else begin tri_idx <= tri_idx + 16'd1; state <= S_PRIM; end
                        end else begin
                            vtx_word    <= vtx_word + 4'd1;
                            stg_rd_addr <= stg_rd_addr + STG_ADDR_W'(1);
                            state       <= S_VTX_RD;
                        end
                    end
                end

                // ---- Ch334 — native rectangles: each record = 3 words, expands to 2 tris ----
                S_AFTER_TRIS: begin
                    if (rect_count != 16'd0) begin
                        rect_idx    <= 16'd0; rect_word <= 2'd0;
                        stg_rd_addr <= tris_base + STG_ADDR_W'(tri_count * words_per_prim);
                        state       <= S_RECT_RD;
                    end else state <= S_DONE;
                end
                S_RECT_RD: state <= S_RECT_LD;          // rect-word addr presented; data next cycle
                S_RECT_LD: begin
                    unique case (rect_word)
                        2'd0:    rect_color <= stg_rd_data;
                        2'd1:    rect_c0    <= stg_rd_data;
                        default: rect_c1    <= stg_rd_data;
                    endcase
                    stg_rd_addr <= stg_rd_addr + STG_ADDR_W'(1);   // advance through every rect word
                    if (rect_word == 2'd2) begin rect_word <= 2'd0; rect_emit <= 5'd0; state <= S_RECT_EMIT; end
                    else                   begin rect_word <= rect_word + 2'd1; state <= S_RECT_RD; end
                end
                S_RECT_EMIT: begin
                    if (rect_completing && fifo_full) begin
                        fifo_wait_cycles <= fifo_wait_cycles + 32'd1;   // pause at a completing XYZ2
                        state <= S_RECT_EMIT;
                    end else begin
                        gif_reg_wr_en <= 1'b1; gif_reg_num <= rect_reg; gif_reg_data <= rect_dat;
                        if (rect_completing) records_emitted <= records_emitted + 16'd1;  // one tri done
                        if (rect_emit == 5'd19) begin                  // whole rect emitted (2 tris)
                            if (rect_idx + 16'd1 == rect_count) state <= S_DONE;
                            else begin rect_idx <= rect_idx + 16'd1; state <= S_RECT_RD; end  // addr already at next base
                        end else rect_emit <= rect_emit + 5'd1;
                    end
                end

                S_DONE: begin done <= 1'b1; state <= S_IDLE; end
                default: state <= S_IDLE;
            endcase
        end
    end

endmodule : gs_prim_list_feeder
