// Voice Wake-up Engine — DNN + GRU keyword spotting
// Architecture: MFCC(40) → DNN(40→64) → GRU(64→32) → DNN(32→4) → Softmax
module voice_wake #(parameter DW=16, MFCC=40, HIDDEN=64, GRU_H=32, NCLASS=4, F=8)(
    input clk, rst_n, input start,
    // MFCC input: 40 features per frame
    input signed [DW-1:0] mfcc_in [0:MFCC-1], input mfcc_valid,
    // Pre-loaded weights
    input signed [DW-1:0] dnn1_wt [0:MFCC*HIDDEN-1], input signed [DW-1:0] dnn1_bias [0:HIDDEN-1],
    input signed [DW-1:0] gru_wz [0:GRU_H*(HIDDEN+GRU_H)-1], input signed [DW-1:0] gru_bz [0:GRU_H-1],
    input signed [DW-1:0] gru_wr [0:GRU_H*(HIDDEN+GRU_H)-1], input signed [DW-1:0] gru_br [0:GRU_H-1],
    input signed [DW-1:0] dnn2_wt [0:GRU_H*NCLASS-1], input signed [DW-1:0] dnn2_bias [0:NCLASS-1],
    // Output
    output reg [1:0] keyword_class, output reg confidence_valid,
    output reg done
);
    reg signed [DW-1:0] dnn1_out [0:HIDDEN-1];
    reg signed [DW-1:0] gru_h [0:GRU_H-1];
    reg signed [DW-1:0] gru_h_new [0:GRU_H-1];
    reg signed [DW-1:0] dnn2_out [0:NCLASS-1];
    reg [3:0] state;
    reg [6:0] cnt; reg [5:0] hcnt;
    integer i;
    always_ff @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin state<=0; confidence_valid<=0; done<=0; cnt<=0; hcnt<=0; keyword_class<=0;
            for(i=0;i<HIDDEN;i++) dnn1_out[i]<='0;
            for(i=0;i<GRU_H;i++) begin gru_h[i]<='0; gru_h_new[i]<='0; end
            for(i=0;i<NCLASS;i++) dnn2_out[i]<='0;
        end else case(state)
          0: if(start && mfcc_valid) begin state<=1; cnt<=0; hcnt<=0; end
          1: begin // DNN1: MFCC → HIDDEN
                dnn1_out[hcnt] <= dnn1_bias[hcnt];
                for(i=0;i<MFCC;i++) dnn1_out[hcnt] <= dnn1_out[hcnt] + mfcc_in[i] * dnn1_wt[hcnt*MFCC+i];
                hcnt <= hcnt + 1;
                if(hcnt >= HIDDEN-1) begin hcnt<=0; state<=2; end
            end
          2: begin // GRU: update hidden state (simplified)
                for(i=0;i<GRU_H;i++) begin
                    // z = sigmoid(Wz*x + Uz*h + bz) — simplified to add
                    gru_h_new[i] <= dnn1_out[i%GRU_H] + gru_h[i]; // Simplified
                    // ReLU approximation for GRU
                    if(gru_h_new[i][DW-1]) gru_h_new[i] <= '0;
                end
                for(i=0;i<GRU_H;i++) gru_h[i] <= gru_h_new[i];
                state <= 3; cnt <= 0;
            end
          3: begin // DNN2: GRU_H → NCLASS
                dnn2_out[hcnt[1:0]] <= dnn2_bias[hcnt[1:0]];
                for(i=0;i<GRU_H;i++) dnn2_out[hcnt[1:0]] <= dnn2_out[hcnt[1:0]] + gru_h[i] * dnn2_wt[hcnt[1:0]*GRU_H+i];
                hcnt <= hcnt + 1;
                if(hcnt[1:0] >= NCLASS-1) begin state<=4; end
            end
          4: begin // Argmax
                keyword_class <= (dnn2_out[1]>dnn2_out[0]) ? 2'd1 : 2'd0;
                if(dnn2_out[2] > dnn2_out[keyword_class]) keyword_class <= 2'd2;
                if(dnn2_out[3] > dnn2_out[keyword_class]) keyword_class <= 2'd3;
                confidence_valid <= 1; done <= 1;
            end
        endcase
    end
endmodule