///////////////////////////////////////////////////////////////////////////////////////////////////
// Company: Microsemi
//
// File: LMS_ALGO.v
// File history:
//      1.0 : Date:28/10/2013 :Initial Release
//      <Revision number>: <Date>: <Comments>
//      <Revision number>: <Date>: <Comments>
//
// Description: Computes error signal,correction factor and filter coefficients. Sends filter 
//coefficients serially to FIR filter
//
// 
//
// Targeted device: <Family::Igloo2> <Die::M2GL010> <Package::484 FBGA>
// Author: Applications Engineering
//
/////////////////////////////////////////////////////////////////////////////////////////////////// 


`timescale 1ns / 100ps

module LMS_ALGO(
CLK,
ARST_N,
ENABLE,
OUTPUT_ENABLE,
MUL_ENABLE,
ADD_ENABLE,
UPDATE_ENABLE,
INPUT_SIGNAL,
DELAYED_SIGNAL,
FIR_OUT_SIGNAL,
FILTER_COEF_OUT,
ERROR,
SEND_ENABLE,
COEF_DONE,
COUNT
);



input CLK;
input ARST_N;
input ENABLE;
input OUTPUT_ENABLE;
input MUL_ENABLE;
input ADD_ENABLE;
input UPDATE_ENABLE;
input [4:0] COUNT;
input signed [15:0] INPUT_SIGNAL ;
input signed [15:0] FIR_OUT_SIGNAL;

output SEND_ENABLE;
output signed [15:0] FILTER_COEF_OUT;
output signed [15:0] ERROR;
output signed [15:0] DELAYED_SIGNAL;
output COEF_DONE;

wire [4:0] COUNT;

reg NEW_SAMPLE;
reg SEND_ENABLE;
reg   [4:0] coef;
reg COEF_DONE;
reg [4:0] mul_index;
reg [4:0] add_index;
reg  signed  [15:0] FILTER_COEF_OUT;
reg  signed [15:0] input_reg[15:0];
reg  signed [15:0] filter_coefficient_temp[15:0];
reg  signed [31:0]correction_factor[15:0];
reg signed [15:0]desired_delay0;        
reg signed [15:0]ERROR;

assign DELAYED_SIGNAL = input_reg[0];   // Input to the Core FIR Filter

////////////////////////////////////////////////////////////////////////////////////////
 // compute ERROR when OUTPUT_ENABLE signal is high

always @ (posedge CLK or negedge ARST_N)     
  begin
    if(ARST_N == 1'b0)
      begin
        ERROR <= 16'b0;
      end
  else
    begin
      if(OUTPUT_ENABLE)
        ERROR <= desired_delay0 - FIR_OUT_SIGNAL;
    end

  end
 
//////////////////////////////////////////////////////////////////////////////////////
 // Input Shift register shifts the 16 bit input signal when ever ENABLE signalis high

always @ (posedge CLK or negedge ARST_N )     
  begin
    if(ARST_N == 1'b0)
      begin 
        input_reg[0] <= 16'b0;
        input_reg[1] <= 16'b0;
        input_reg[2] <= 16'b0;
        input_reg[3] <= 16'b0;
        input_reg[4] <= 16'b0;
        input_reg[5] <= 16'b0;
        input_reg[6] <= 16'b0;
        input_reg[7] <= 16'b0;
        desired_delay0 <= 16'b0;
      end
    else
      begin
        if(ENABLE)
          begin
            desired_delay0 <= INPUT_SIGNAL;   
            input_reg[0] <= desired_delay0;  
            input_reg[1] <= input_reg[0];
            input_reg[2] <= input_reg[1]; 
            input_reg[3] <= input_reg[2];
            input_reg[4] <= input_reg[3];
            input_reg[5] <= input_reg[4];
            input_reg[6] <= input_reg[5];
            input_reg[7] <= input_reg[6];
          end
        else 
          desired_delay0 <= desired_delay0;       
      end 
  end

/////////////////////////////////////////////////////////////////////////////////////
// index variables used to compute  correction factors,filter coefficients

always @ (posedge CLK or negedge ARST_N)     
  begin
    if(ARST_N == 1'b0)
      begin
        mul_index <= 5'b00000;
        add_index <= 5'b00000;
        coef      <= 5'b00000;
      end
    else
      begin
        mul_index <= COUNT;
        add_index <= mul_index;
        coef  <= add_index;
      end
  end

///////////////////////////////////////////////////////////////////////////////////
// computing coorection factor 

always @ (posedge CLK or negedge ARST_N)           
  begin
    if(ARST_N == 1'b0)
      begin
        correction_factor[0] <= 32'b0;
        correction_factor[1] <= 32'b0;
        correction_factor[2] <= 32'b0;
        correction_factor[3] <= 32'b0;
        correction_factor[4] <= 32'b0;
        correction_factor[5] <= 32'b0;
        correction_factor[6] <= 32'b0;
        correction_factor[7] <= 32'b0;
      end
    else 
      begin
        if(MUL_ENABLE) 
          correction_factor[mul_index] <= ERROR * input_reg[mul_index];      
      end
  end

////////////////////////////////////////////////////////////////////////////////////
 // truncation and filter weight update
always @ (posedge CLK or negedge ARST_N  )        
  begin
    if(ARST_N == 1'b0)
      begin
        filter_coefficient_temp[0] <= 16'b0;
        filter_coefficient_temp[1] <= 16'b0;
        filter_coefficient_temp[2] <= 16'b0;
        filter_coefficient_temp[3] <= 16'b0;
        filter_coefficient_temp[4] <= 16'b0;
        filter_coefficient_temp[5] <= 16'b0;
        filter_coefficient_temp[6] <= 16'b0;
        filter_coefficient_temp[7] <= 16'b0;
      end
    else 
      begin
        if(ADD_ENABLE)
          filter_coefficient_temp[add_index]  <= filter_coefficient_temp[add_index]+correction_factor[add_index][28:13] +  {1'b0,correction_factor[add_index][28]};       
      end
  end

//////////////////////////////////////////////////////////////////////////////////////////////
// sending the filter weights serially to FIR filter
always @ (posedge CLK or negedge ARST_N)   
  begin
    if(ARST_N == 1'b0)
      begin
        FILTER_COEF_OUT  <= 16'b0;
        COEF_DONE <= 1'b0;
        SEND_ENABLE <= 1'b0;
      end
    else
      begin
        if( UPDATE_ENABLE )
          begin
            FILTER_COEF_OUT <= filter_coefficient_temp[coef];
            SEND_ENABLE <= 1'b1;
          end
        else
          SEND_ENABLE <= 1'b0;
      end   
  end

endmodule

