//====================================================================
// stencil_buffer.v - 模板缓冲器
// 第22课：模板测试与操作
//====================================================================
module stencil_buffer #(
    parameter ADDR_WIDTH = 20,
    parameter STENCIL_WIDTH = 8,
    parameter COLOR_WIDTH = 24
)(
    input  wire                          clk, rst_n,
    input  wire                          frag_valid,
    input  wire [ADDR_WIDTH-1:0]         frag_addr,
    input  wire [COLOR_WIDTH-1:0]        frag_color,
    input  wire [STENCIL_WIDTH-1:0]      ref_value,
    input  wire [STENCIL_WIDTH-1:0]      mask,
    input  wire [2:0]                    stencil_func,   // 0=NEVER,1=LESS,...,7=ALWAYS
    input  wire [2:0]                    stencil_pass_op, // 操作: 0=KEEP,1=ZERO,2=REPLACE,3=INCR,4=DECR
    output reg                           frag_ready,
    output reg                           write_valid,
    output reg  [ADDR_WIDTH-1:0]         write_addr,
    output reg  [COLOR_WIDTH-1:0]        write_color,
    output reg                           stencil_passed
);
/* verilator lint_off WIDTHEXPAND */
/* verilator lint_off WIDTHTRUNC */
/* verilator lint_off CASEOVERLAP */
/* verilator lint_off CMPCONST */
/* verilator lint_off UNSIGNED */
/* verilator lint_off WIDTHCONCAT */

    reg [STENCIL_WIDTH-1:0] stencil_buf [0:255];
    reg [STENCIL_WIDTH-1:0] stored_stencil;
    reg [STENCIL_WIDTH-1:0] masked_ref, masked_stencil;
    reg test_pass;
    assign masked_ref = ref_value & mask;
    assign masked_stencil = stored_stencil & mask;
    always @(*) begin
        case (stencil_func)
            3'd0: test_pass = 1'b0;
            3'd1: test_pass = (masked_ref < masked_stencil);
            3'd2: test_pass = (masked_ref == masked_stencil);
            3'd3: test_pass = (masked_ref <= masked_stencil);
            3'd4: test_pass = (masked_ref > masked_stencil);
            3'd5: test_pass = (masked_ref != masked_stencil);
            3'd6: test_pass = (masked_ref >= masked_stencil);
            3'd7: test_pass = 1'b1;
            default: test_pass = 1'b1;
        endcase
    end
    localparam S_READ=2'd0, S_TEST=2'd1, S_UPDATE=2'd2, S_OUT=2'd3;
    reg [1:0] state;
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin state<=S_READ; frag_ready<=1; write_valid<=0; stencil_passed<=0; end
        else begin
            write_valid <= 0; stencil_passed <= 0;
            case (state)
                S_READ: begin
                    frag_ready <= 1;
                    if (frag_valid) begin
                        frag_ready <= 0;
                        stored_stencil <= stencil_buf[frag_addr[7:0]];
                        state <= S_TEST;
                    end
                end
                S_TEST: begin
                    stencil_passed <= test_pass;
                    state <= S_UPDATE;
                end
                S_UPDATE: begin
                    case (stencil_pass_op)
                        3'd0: ; // KEEP
                        3'd1: stencil_buf[frag_addr[7:0]] <= 8'd0; // ZERO
                        3'd2: stencil_buf[frag_addr[7:0]] <= ref_value; // REPLACE
                        3'd3: stencil_buf[frag_addr[7:0]] <= stored_stencil + 8'd1; // INCR
                        3'd4: stencil_buf[frag_addr[7:0]] <= stored_stencil - 8'd1; // DECR
                        default: ;
                    endcase
                    state <= S_OUT;
                end
                S_OUT: begin
                    if (test_pass) begin
                        write_valid <= 1;
                        write_addr <= frag_addr;
                        write_color <= frag_color;
                    end
                    state <= S_READ;
                end
                default: state <= S_READ;
            endcase
        end
    end
endmodule