-------------------------------------------------------------------------------
-- Title      : FLAC_DECODER
-------------------------------------------------------------------------------
-- File       : FLAC_DECODER.vhd
-- Author     : Sathishkumar Donthu
--              Sathish.Donthu@microsemi.com
-- Company    : Microsemi Corporation
-- Device     : SmartFusion 
-- Standard   : VHDL
-------------------------------------------------------------------------------
-- Description: LPC decoder with a custom-made APB3 interface.
--The parameters required to send from Cortex-M3 are:
--address of warm-up samples
--address of coefficients
--address of residual
--LPC shift
--length of the frame
-------------------------------------------------------------------------------
-- Copyright (c) 2011   Actel Corporation
--                      All rights reserved.
-------------------------------------------------------------------------------
library   IEEE;
use       IEEE.std_logic_1164.all;
use       IEEE.std_logic_unsigned.all;

entity LPC is
port ( 
       PCLK                    : in  std_logic;
       PRESET                  : in  std_logic;
       P_VALID                 : in  std_logic;       
       
       --APB3 slave signals 
       
       SPENABLE                : in  std_logic;
       SPSEL                   : in  std_logic;
       SPWRITE                 : in  std_logic;
       SPWDATA                 : in  std_logic_vector(31 downto 0);
       SPADDR                  : in  std_logic_vector(19 downto 0);
       SPRDATA                 : out std_logic_vector(31 downto 0);
       SPREADY,SPSLVERR        : out std_logic;

       --APB3 master signals

       MPENABLE                : out std_logic;
       MPSEL                   : out std_logic;
       MPWRITE                 : out std_logic;
       MPWDATA                 : out std_logic_vector(31 downto 0);
       MPADDR                  : out std_logic_vector(31 downto 0);
       MPRDATA                 : in  std_logic_vector(31 downto 0);
       MPREADY,PSLVERR         : in  std_logic;

       --Signals for conncting to GPIO's of MSS

       START                   : in  std_logic;
       STOP                    : out std_logic
       );
      end LPC;

architecture behav of LPC is

component   multi16
 port   (
            DataA :     in    std_logic_vector(15 downto 0 ) ;
            DataB :     in    std_logic_vector(13 downto 0 ) ;
            Mult  :     out   std_logic_vector(29 downto 0 ) 
            ) ;
 end   component ;

component shifter_r 
 port   (
           INPUT    :   in    std_logic_vector(29 downto 0);
           CLK      :   in    std_logic;
           SHIFT    :   in    std_logic_vector(3 downto 0);
           OUTPUT   :   out   std_logic_vector(29 downto 0)
           );
end component;

type array6    is array (4  downto  0) of std_logic_vector(31  downto   0 ) ;
type array8_32 is array (7  downto  0) of std_logic_vector(15  downto   0 ) ;
type array8_14 is array (7  downto  0) of std_logic_vector(13  downto   0 ) ;
type state_type is (srl2,srl3,st4,st3,st2,delay,idle,ssetup,saccess,rmsetup,
                    rmaccess,msetup,maccess,shift,mac,st1);
signal state       : state_type;
signal rx_data     : array6;
signal data        : array8_32;
signal coeff       : array8_14;
signal read        : std_logic;
signal s_start     : std_logic;
signal length      : std_logic_vector(11 downto 0);
signal m_re        : std_logic_vector(29 downto 0);
signal re          : std_logic_vector(29 downto 0);
signal s_re        : std_logic_vector(29 downto 0);
signal sample      : std_logic_vector(29 downto 0);
signal res         : std_logic_vector(29 downto 0);
signal residue     : std_logic_vector(29 downto 0);
signal out_sample  : std_logic_vector(29 downto 0);
signal wsample     : std_logic_vector(31 downto 0);
signal d1          : std_logic_vector(15 downto 0);
signal c1          : std_logic_vector(13 downto 0);
constant one       : std_logic_vector(31 downto 0):=x"00000004";
begin

process(PCLK,PRESET,START)
variable args_read :integer range 4 downto 0;
variable warmup_samples,coeffs,i_mul:integer range 8 downto 0;
begin
 
 if(START='0' or PRESET='0') then
    state<=idle;
    read<='1';
    length<=x"001";
    STOP<='0';
    args_read:=0;
    warmup_samples:=0;
    coeffs:=0;
    MPADDR<=x"00000000";
    MPWDATA<=x"00000000";    
 else

  if (PCLK'event and PCLK = '1') then
         
      case state is

        --***************************************************************************
        --APB Slave states
        --***************************************************************************

        when idle         => if(SPSEL='1' and SPENABLE='0') then
                             state<=ssetup;                       
                             end if;
        when ssetup       => if(SPSEL='1' and SPENABLE='1'and SPWRITE='1' and SPADDR=x"50100") then                        
                             SPREADY<='1';
                             state<=saccess;
                             elsif(SPWRITE='0') then
                             SPREADY<='0';
                             state<=saccess;
                             end if;
        when saccess      => if(SPWRITE='1') then
                             SPREADY<='1';
                             rx_data(args_read)<=SPWDATA;
                              if(args_read=4) then                         
                              state<=msetup;
                              else
                              state<=ssetup;
                              args_read:=args_read+1;
                              end if;
                             elsif(SPWRITE='0') then                        
                             state<=idle;
                             SPREADY<='1';
                             end if;
        
        --***************************************************************************
        --APB Master states for reading coefficients and warm-up samples
        --***************************************************************************

        
        when msetup      => if(P_VALID='0') then                             
                             SPREADY<='0';                     
                             if(coeffs=8) then                  
                             state<=rmsetup;
                             else
                             state<=maccess;                             
                              if(warmup_samples=8) then
                              MPADDR<=rx_data(1);                              
                              else
                              MPADDR<=rx_data(0);                              
                              end if;
                             end if; 
                             else
                             MPADDR<=x"00000000";
                             MPWDATA<=x"00000000";
                             state<=msetup;                            
                             end if;                                                  
        when maccess      => if(P_VALID='0') then 
                             if(MPREADY='1') then                              
                               if(warmup_samples=8) then
                               coeff(coeffs)<=MPRDATA(13 downto 0);
                               coeffs:=coeffs+1;
                               rx_data(1)<=rx_data(1)+one;
                               else
                               data(7-warmup_samples)<=MPRDATA(15 downto 0);
                               warmup_samples:= warmup_samples+1;
                               rx_data(0)<=rx_data(0)+one;
                               end if;  
                              state<=msetup;
                              else
                              state<=maccess;
                              end if;  
                              else
                              MPADDR<=x"00000000";
                              MPWDATA<=x"00000000";
                              state<=msetup;
                              end if;                            
         
         --***************************************************************************
         --APB Master states
         --***************************************************************************
                 
         when rmsetup    => if(P_VALID='0') then                             
                             SPREADY<='0';
                             state<=rmaccess;
                             if(read='1') then                             
                             MPADDR<=rx_data(2);        
                             else                             
                             MPADDR<= rx_data(0);
                             MPWDATA<=wsample;
                             end if;  
                             else
                             MPADDR<=x"00000000";
                             MPWDATA<=x"00000000";
                             state<=rmsetup;                                                       
                             end if; 
         when rmaccess    => if(P_VALID='0') then
                              if(MPREADY='1') then                                    
                                if(read = '0') then
                                    if(length=rx_data(4)(11 downto 0)) then
                                    state<=idle;STOP<='1';
                                    else
                                    state<=rmsetup;
                                    length<=length+x"001";
                                    end if;
                                rx_data(0)<=rx_data(0)+one;                           
                                read<='1';                           
                                else
                                state<= delay;
                                re<="000000000000000000000000000000";
                                d1<=data(0);
                                c1<=coeff(0);
                                residue<=MPRDATA(29 downto 0);
                                i_mul:=1;
                                read<='0';
                                rx_data(2) <= rx_data(2) + one ;
                                end if;

                              else
                              state<=rmaccess;
                              end if; 
                              else                             
                             MPADDR<=x"00000000";
                             MPWDATA<=x"00000000";
                             state<=rmsetup;                              
                              end if;

        --***************************************************************************
        --Predicting the next Sample
        --***************************************************************************

        when  mac         => re<=re+m_re;
                             if(i_mul=8) then
                             state<=st3;
                             else
                             d1<=data(i_mul);
                             c1<=coeff(i_mul);
                             i_mul:=i_mul+1;
                             state<=delay;
                             end if;                                                  
        when  delay       => state<=st1;
                             MPADDR<=x"00000000";
                             MPWDATA<=x"00000000";
        when  st1         => state<=mac;            
        when  st3         => s_re<=re;
                             s_start<='1';
                             state<=srl2;        
        when  srl2        => state<=srl3;
        when  srl3        => state<=st4;
                             s_start<='0';
        when  st4         => sample<=residue+res;   
                             state<=shift;                                 
        when  shift       => data(7)<=data(6);
                             data(6)<=data(5);
                             data(5)<=data(4);
                             data(4)<=data(3);
                             data(3)<=data(2);
                             data(2)<=data(1);
                             data(1)<=data(0);
                             data(0)<=sample(15 downto 0);
                             out_sample<=sample;
                             state<=rmsetup;                     
        when others       => state<=idle;

     end case;
   end if;
 end if;
end process;

--******************************************************************************
-- Registering MPSEL and MPENABLE
--******************************************************************************

sel1:process(PCLK)
begin

if (PCLK'event and PCLK = '1') then 
      if(state = rmaccess or state = maccess) and P_VALID='0' then     
        MPSEL<='1';
        MPENABLE<='1';
      elsif(state = msetup or state = rmsetup) and P_VALID='0' then       
        MPSEL<='1';
        MPENABLE<='0';
      else       
        MPSEL<='0';
        MPENABLE<='0';
      end if;
end if;

end process sel1;
--******************************************************************************
-- Registering MPWRITE
--******************************************************************************
pwrite:process(PCLK)
begin
if(PCLK'event and PCLK='1') then
    if(state = rmsetup and read='0') and P_VALID='0' then
      MPWRITE <= '1';
    elsif(state=rmaccess and read='0') and P_VALID='0' then
      MPWRITE<='1';
    else    
      MPWRITE <= '0';
    end if;
end if;
end process pwrite;

mult_i   : multi16 port  map (d1,c1,m_re) ;
shifter_0: shifter_r port map (s_re,s_start,rx_data(3)(3 downto 0),res);
wsample<=out_sample(29)&out_sample(29)&out_sample;

end behav;