// Winograd Convolution Engine — F(2x2, 3x3) minimal algorithm
// Reduces 3x3 conv from 9 muls to 4 muls per output pair
module winograd_f2x3 #(parameter DW=16, CH=16)(
    input clk, rst_n, input en, input start,
    // Input tile: 4x4 = 16 elements per channel
    input signed [DW-1:0] tile_in [0:15], // 4x4 input tile
    // Weight: 3x3 = 9 elements per output channel
    input signed [DW-1:0] wt_3x3 [0:8],
    // Output: 2x2 = 4 elements per output channel
    output reg signed [DW-1:0] tile_out [0:3],
    output reg out_valid, output reg done
);
    // Winograd transform matrices for F(2,3):
    // BT = [1  0 -1 0;  0  1  1 0;  0 -1  1 0;  0  1  0 -1]
    // G  = [1 0 0; 1/2 1/2 1/2; 1/2 -1/2 1/2; 0 0 1]
    // AT = [1 1 1 0; 0 1 -1 -1]
    // Step 1: Transform weights g → U = G*g*G^T (4x4, only 4 unique muls per row/col)
    reg signed [DW*2-1:0] u [0:15]; // 4x4 transformed weights
    reg signed [DW*2-1:0] v [0:15]; // 4x4 transformed inputs
    reg signed [DW*2-1:0] m [0:15]; // 4x4 element-wise products
    reg [3:0] state;
    integer i;
    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin state<=0; out_valid<=0; done<=0;
            for(i=0;i<16;i++) begin u[i]<='0; v[i]<='0; m[i]<='0; tile_out[i%4]<='0; end end
        else case(state)
          0: if(start) begin state<=1;
                // Weight transform (precomputed for fixed weights)
                u[0] <= wt_3x3[0];                           // g00
                u[1] <= (wt_3x3[0]+wt_3x3[1]+wt_3x3[2])>>>1; // (g00+g01+g02)/2
                u[2] <= (wt_3x3[0]-wt_3x3[1]+wt_3x3[2])>>>1; // (g00-g01+g02)/2
                u[3] <= wt_3x3[2];                           // g02
                // Rows 1-3 similar pattern (simplified)
                u[4] <= (wt_3x3[3]+wt_3x3[6])>>>1;
                u[5] <= (wt_3x3[3]+wt_3x3[4]+wt_3x3[5]+wt_3x3[6]+wt_3x3[7]+wt_3x3[8])>>>2;
                u[6] <= (wt_3x3[3]-wt_3x3[4]+wt_3x3[5]+wt_3x3[6]-wt_3x3[7]+wt_3x3[8])>>>2;
                u[7] <= (wt_3x3[5]+wt_3x3[8])>>>1;
                u[8] <= (wt_3x3[0]-wt_3x3[6])>>>1;
                u[9] <= (wt_3x3[0]+wt_3x3[1]+wt_3x3[2]-wt_3x3[6]-wt_3x3[7]-wt_3x3[8])>>>2;
                u[10]<= (wt_3x3[0]-wt_3x3[1]+wt_3x3[2]-wt_3x3[6]+wt_3x3[7]-wt_3x3[8])>>>2;
                u[11]<= (wt_3x3[2]-wt_3x3[8])>>>1;
                u[12]<= wt_3x3[6];
                u[13]<= (wt_3x3[6]+wt_3x3[7]+wt_3x3[8])>>>1;
                u[14]<= (wt_3x3[6]-wt_3x3[7]+wt_3x3[8])>>>1;
                u[15]<= wt_3x3[8];
            end
          1: begin state<=2;
                // Input transform: V = BT * d * BT^T
                v[0] <= tile_in[0] - tile_in[2] - tile_in[8] + tile_in[10];
                v[1] <= tile_in[1] + tile_in[2] - tile_in[9] - tile_in[10];
                v[2] <= -tile_in[1] + tile_in[2] + tile_in[9] - tile_in[10];
                v[3] <= tile_in[1] - tile_in[3] - tile_in[9] + tile_in[11];
                v[4] <= tile_in[4] + tile_in[8] - tile_in[6] - tile_in[10];
                v[5] <= tile_in[5] + tile_in[6] + tile_in[9] + tile_in[10];
                v[6] <= -tile_in[5] + tile_in[6] - tile_in[9] + tile_in[10];
                v[7] <= tile_in[5] - tile_in[7] + tile_in[9] - tile_in[11];
                v[8] <= -tile_in[4] + tile_in[6] + tile_in[8] - tile_in[10];
                v[9] <= -tile_in[5] - tile_in[6] + tile_in[9] + tile_in[10];
                v[10]<= tile_in[5] - tile_in[6] - tile_in[9] + tile_in[10];
                v[11]<= -tile_in[5] + tile_in[7] - tile_in[9] + tile_in[11];
                v[12]<= tile_in[4] - tile_in[6];
                v[13]<= tile_in[5] + tile_in[6];
                v[14]<= -tile_in[5] + tile_in[6];
                v[15]<= tile_in[5] - tile_in[7];
            end
          2: begin state<=3;
                // Element-wise multiply: M = U ⊙ V
                for(i=0;i<16;i++) m[i]<=u[i]*v[i];
            end
          3: begin state<=4;
                // Output transform: Y = AT * M * AT^T
                tile_out[0] <= (m[0]+m[1]+m[2]+m[4]+m[5]+m[6]+m[8]+m[9]+m[10]) >>> DW;
                tile_out[1] <= (m[1]-m[2]-m[3]+m[5]-m[6]-m[7]+m[9]-m[10]-m[11]) >>> DW;
                tile_out[2] <= (m[4]+m[5]+m[6]-m[8]-m[9]-m[10]-m[12]-m[13]-m[14]) >>> DW;
                tile_out[3] <= (m[5]-m[6]-m[7]-m[9]+m[10]+m[11]-m[13]+m[14]+m[15]) >>> DW;
                out_valid <= 1;
            end
          4: begin done<=1; out_valid<=0; end
        endcase
    end
endmodule