import CoeffsR2pkg::*;
import CoeffsR4pkg::*;
import CoeffsR8pkg::*;
module FirFilter #(
    parameter DATA_WIDTH = 16,
    parameter COEFF_WIDTH = 16,
    parameter NUM_TAPS = 32,
    parameter RESULT_WIDTH = DATA_WIDTH + COEFF_WIDTH + $clog2(NUM_TAPS) - 1
)
(
    input logic                     Clk_i,
    input logic                     Rst_i,
    input logic [3:0]               DecimFactor_i,               
    input logic [DATA_WIDTH-1:0]    Data_i,
    input logic                     Val_i,

    output logic [RESULT_WIDTH-1:0] Data_o,
    output logic                    Val_o
);
//=======================================================================================
//								REG/WIRE
//=======================================================================================
logic signed [DATA_WIDTH-1:0]   shiftReg        [0:NUM_TAPS-1];
/* MAC */
logic signed [RESULT_WIDTH-1:0] macResult;
/* Coeffs */
logic signed [COEFF_WIDTH-1:0]  coeffs          [0:NUM_TAPS-1];

/* Phase Counter */
logic        [$clog2(8)-1:0]    phaseCounter;

/* 2 Cycle Val_i */
logic                           val2Cycle;

/* Delayed Val_i and Data_i */
logic signed [DATA_WIDTH-1:0]   delayedData;
logic                           delayedVal;
logic                           delayedValR;
//========================================================================================
//								ASSIGNMENTS
//========================================================================================
assign Data_o = macResult;
//========================================================================================
//								CODING
//========================================================================================
/* Delayed Data_i */
always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        delayedData <= '0;
        delayedVal <= 1'b0;
    end
    else begin 
        if (Val_i) begin
            delayedData <= Data_i;
            delayedVal <= Val_i;
        end
        else begin 
            delayedData <= delayedData;
            delayedVal <= 1'b0;
        end
    end
end

// MAC-операция, разделенная на два такта
always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin
        macResult <= '0;
    end
    else begin 
        if (val2Cycle) begin
            if (phaseCounter == 0) begin
                macResult <= '0;
                /* Первая четверть коэффициентов */
                for (int j = 0; j < NUM_TAPS/4; j++) begin
                    logic signed [DATA_WIDTH:0] symSum;
                    symSum = shiftReg[j] + shiftReg[NUM_TAPS-1-j];
                    macResult += symSum * coeffs[j];
                end
            end
            else begin
                /* Вторая четверть коэффициентов */
                for (int j = NUM_TAPS/4; j < NUM_TAPS/2; j++) begin
                    logic signed [DATA_WIDTH:0] symSum;
                    symSum = shiftReg[j] + shiftReg[NUM_TAPS-1-j];
                    macResult += symSum * coeffs[j];
                end
                /* Если количество коэффициентов нечетное, добавляем остальные коэффициенты */
                if (NUM_TAPS % 2 == 1) begin
                    macResult += shiftReg[NUM_TAPS/2] * coeffs[NUM_TAPS/2];
                end
            end
        end
    end
end

/* Shift Register */
always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        shiftReg <= '{default : '0};
    end
    else begin 
        if (delayedVal) begin
            for (int i = 1; i < NUM_TAPS; i++) begin
                shiftReg[i] <= shiftReg[i-1];
            end
            shiftReg[0] <= delayedData;
        end
    end
end

/* Prolong Val_i for two cycles */
always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        val2Cycle <= 1'b0;
    end
    else begin 
        if (Val_i) begin
            val2Cycle <= 1'b1;
        end
        else if (phaseCounter == 1) begin 
            val2Cycle <= 1'b0;
        end
    end
end

/* Phase Counter */
always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        phaseCounter <= '0;
    end
    else begin 
        if (val2Cycle) begin 
            if (phaseCounter == 1) begin 
                phaseCounter <= '0;
            end
            else begin 
                phaseCounter <= phaseCounter + 1;
            end
        end
    end
end

always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        delayedValR <= 1'b0;
    end
    else begin 
        delayedValR <= delayedVal;
    end
end

/* Val_o */
always_ff @(posedge Clk_i) begin 
    if (Rst_i) begin 
        Val_o <= 1'b0;
    end
    else begin 
        Val_o <= delayedValR;
    end
end

/* Choose the right set of coeffs */
always_comb begin
    case (DecimFactor_i) 
        2: coeffs = CoeffsR2pkg::COEFFS;
        4: coeffs = CoeffsR4pkg::COEFFS;
        8: coeffs = CoeffsR8pkg::COEFFS;
        default: coeffs = '{default:'0}; 
    endcase
end
        
endmodule