--------------------------------------------------------------------------------
-- Company: <Name>
--
-- File: MAC_FIR.vhd
-- File history:
--
-- Description		: Implementation of 100-Tap Single MAC  FIR filter using 
--					  Single 18x18 MAC
--
-- Targeted device	: Family	:: SmartFusion2  
-- 					  Die		:: M2S050T
--					  Package	:: 896 FBGA	

--------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_signed.all;
use ieee.std_logic_arith.all;

entity MAC_FIR is
port(	clk				: in std_logic;
		reset_n			: in std_logic;
		Filter_En		: in std_logic;
		Xn_in			: in std_logic_vector(17 downto 0);
		Data_Valid		: in std_logic;
		rdy				: out std_logic;	
		Mac_out			: out std_logic_vector(43 downto 0)	   	   	             	     	     	     	            
         );
end MAC_FIR;


architecture MAC_FIR_ARCH of MAC_FIR is


component mulacc_18x18 is
    -- Port list
    port(
        -- Inputs
        A0        : in  std_logic_vector(17 downto 0);
        A0_ACLR_N : in  std_logic;
        A0_EN     : in  std_logic;
        A0_SCLR_N : in  std_logic;
        B0        : in  std_logic_vector(17 downto 0);
        B0_ACLR_N : in  std_logic;
        B0_EN     : in  std_logic;
        B0_SCLR_N : in  std_logic;
        CLK       : in  std_logic;
        P_ACLR_N  : in  std_logic;
        P_EN      : in  std_logic;
        P_SCLR_N  : in  std_logic;
        -- Outputs
        CDOUT     : out std_logic_vector(43 downto 0);
        P         : out std_logic_vector(43 downto 0)
        );
end component mulacc_18x18;

-- coefficient RAM
component Coef_RAM is
    -- Port list
    port(
        -- Inputs
        A_ADDR : in  std_logic_vector(5 downto 0);
        A_BLK  : in  std_logic;
        B_ADDR : in  std_logic_vector(5 downto 0);
        B_BLK  : in  std_logic;
        CLK    : in  std_logic;
        C_ADDR : in  std_logic_vector(5 downto 0);
        C_BLK  : in  std_logic;
        C_DIN  : in  std_logic_vector(17 downto 0);
        -- Outputs
        A_DOUT : out std_logic_vector(17 downto 0);
        B_DOUT : out std_logic_vector(17 downto 0)
        );
end component Coef_RAM;

component Inp_RAM is
    -- Port list
    port(
        -- Inputs
        A_ADDR : in  std_logic_vector(5 downto 0);
        A_BLK  : in  std_logic;
        B_ADDR : in  std_logic_vector(5 downto 0);
        B_BLK  : in  std_logic;
        CLK    : in  std_logic;
        C_ADDR : in  std_logic_vector(5 downto 0);
        C_BLK  : in  std_logic;
        C_DIN  : in  std_logic_vector(17 downto 0);
        -- Outputs
        A_DOUT : out std_logic_vector(17 downto 0);
        B_DOUT : out std_logic_vector(17 downto 0)
        );
end component Inp_RAM;



-- coefficient RAM
component Coef_RAM1 is
    -- Port list
    port(
        -- Inputs
        A_ADDR : in  std_logic_vector(5 downto 0);
        A_BLK  : in  std_logic;
        B_ADDR : in  std_logic_vector(5 downto 0);
        B_BLK  : in  std_logic;
        CLK    : in  std_logic;
        C_ADDR : in  std_logic_vector(5 downto 0);
        C_BLK  : in  std_logic;
        C_DIN  : in  std_logic_vector(17 downto 0);
        -- Outputs
        A_DOUT : out std_logic_vector(17 downto 0);
        B_DOUT : out std_logic_vector(17 downto 0)
        );
end component Coef_RAM1;

component Inp_RAM1 is
    -- Port list
    port(
        -- Inputs
        A_ADDR : in  std_logic_vector(5 downto 0);
        A_BLK  : in  std_logic;
        B_ADDR : in  std_logic_vector(5 downto 0);
        B_BLK  : in  std_logic;
        CLK    : in  std_logic;
        C_ADDR : in  std_logic_vector(5 downto 0);
        C_BLK  : in  std_logic;
        C_DIN  : in  std_logic_vector(17 downto 0);
        -- Outputs
        A_DOUT : out std_logic_vector(17 downto 0);
        B_DOUT : out std_logic_vector(17 downto 0)
        );
end component Inp_RAM1;

constant ZERO			: std_logic_vector(7 downto 0):= "00000000";
constant TAPLENGTH		: std_logic_vector(7 downto 0):= "01100100";
constant TAPLENGTH_1	: std_logic_vector(7 downto 0):= "01100011";
constant TAPLENGTH_2	: std_logic_vector(7 downto 0):= "01100010";

constant uSRAMDEPTH		: std_logic_vector(7 downto 0):= "01000000";
constant uSRAMDEPTH_1	: std_logic_vector(7 downto 0):= "00111111";



TYPE State is ( mac_idle,
				mac_Genrdy,
				mac_Loadinp,
				mac_dly0,
				filt_op,
				Op_clr
				);

signal mac_state : State;


  -- Inputs
	signal Transferdone		: std_logic;
	signal rden				: std_logic;	
	signal Initdone			: std_logic;		
		
	signal FiltOp_done		: std_logic;
	signal Data_Valid_dly	: std_logic;

	signal rdy_sig			: std_logic;
	signal rdy_cnt			: std_logic_vector(7 downto 0);

	signal new_inprdaddr	: std_logic_vector(7 downto 0);
	signal inp_rdaddr		: std_logic_vector(7 downto 0);
	signal inp_rdaddr1		: std_logic_vector(7 downto 0);
	signal inp_rdaddr2		: std_logic_vector(7 downto 0);

	signal inp_rddata		: std_logic_vector(17 downto 0);
	signal inp_rddata1		: std_logic_vector(17 downto 0);
	signal inp_rddata2		: std_logic_vector(17 downto 0);

	signal Coef_rdaddr		: std_logic_vector(7 downto 0);
	signal Coef_rdaddr1		: std_logic_vector(7 downto 0);
	signal Coef_rdaddr2		: std_logic_vector(7 downto 0);

	signal Coef_rddata		: std_logic_vector(17 downto 0);
	signal Coef_rddata1		: std_logic_vector(17 downto 0);	
	signal Coef_rddata2		: std_logic_vector(17 downto 0);	

	signal inp_wraddr		: std_logic_vector(7 downto 0);
	signal inp_wraddr1		: std_logic_vector(7 downto 0);
	signal inp_wraddr2		: std_logic_vector(7 downto 0);

	signal inp_wrdata		: std_logic_vector(17 downto 0);
	signal inp_wrdata_dly0	: std_logic_vector(17 downto 0);

	signal Coef0_rden 		: std_logic;
	signal Coef1_rden 		: std_logic;
	signal Inp0_rden 		: std_logic;
	signal Inp1_rden 		: std_logic;

	signal InpCoef0_wren 	: std_logic;
	signal InpCoef1_wren 	: std_logic;

	signal Sel_Coef1 		: std_logic;
	signal Sel_Coef2 		: std_logic;
	signal Sel_Inp1 		: std_logic;
	signal Sel_Inp2 		: std_logic;
	
	
	signal mac_clr			: std_logic;	
	signal clr				: std_logic;
	signal clrsig			: std_logic;
	signal clrsig_0			: std_logic;
	signal clrsig_1			: std_logic;

	signal Yn_out			: std_logic_vector(43 downto 0);

	signal PORTC_ADDR		: std_logic_vector(5 downto 0);
	signal PORTC_DIN		: std_logic_vector(17 downto 0);
	SIGNAL PORTB_Coefaddr 	: std_logic_vector(5 downto 0);
	SIGNAL PORTB_Inpaddr  	: std_logic_vector(5 downto 0);


begin


	process(clk, reset_n)
	begin
	if(reset_n = '0')then
		rdy_sig  <= '0';
	else
		if(rising_edge(clk))then
			if(Transferdone = '1' or mac_state = Op_clr)then
				rdy_sig  <= '0';
			elsif (mac_state = mac_Genrdy or FiltOp_done = '1')then
				rdy_sig  <= '1';
			end if;
		end if;
	end if;	
	end process;

	rdy	<= rdy_sig;

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			rdy_cnt	<= (OTHERS => '0');
		else
			if(rising_edge(clk))then
				if(Transferdone = '1')then
					rdy_cnt	<= (OTHERS => '0');
				elsif(rdy_sig = '1')then	
					rdy_cnt	<= rdy_cnt + '1';
				end if;
			end if;
		end if;
	end process;

-- Generating a signal to indicate the loading of 100 data's into input memory is complete

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			Transferdone	<= '0';
		else
			if(rising_edge(clk))then
				if(rdy_cnt = TAPLENGTH_2 and mac_state = mac_Loadinp)then
					Transferdone	<= '1';	
				else
					Transferdone	<= '0';
				end if;
			end if;
		end if;
	end process;

-- Control FSM for storing data's into Coefficient and Input Memories

process(clk, reset_n)
begin
	if(reset_n = '0')then
		mac_state <= mac_idle;
	else
		if(rising_edge(clk))then
			case mac_state is

				when mac_idle 	 => if(Filter_En = '1')then
										mac_state <= mac_Genrdy;
									end if;
				when mac_Genrdy  => if(Data_Valid = '1')then
										mac_state <= mac_Loadinp;
									end if;
				when mac_Loadinp => if(Transferdone = '1')then
										mac_state <= mac_dly0;
									end if;

				when mac_dly0	 => if(Initdone = '1')then
										mac_state <= filt_op;
									end if;
				when filt_op	 =>	if(FiltOp_done = '1')then
										mac_state <= Op_clr;
									end if;
				when Op_clr		 => if(Filter_En = '0')then
										mac_state <= mac_idle;
									else
										mac_state <= filt_op;	
									end if;
				when others 	 => null;	
			end case;
		end if;
	end if;
end process;


--======================================================================================--
--		 Generates address to read coeffcient and input sample each from A,B ports 
--======================================================================================--


	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			Coef_rdaddr	<= (OTHERS => '0');
		else
			if(rising_edge(clk))then
				if(mac_state = filt_op)then	
					if(Coef_rdaddr = TAPLENGTH_1)then
						Coef_rdaddr	<= (OTHERS => '0');
					else
						Coef_rdaddr	<= Coef_rdaddr + '1';
					end if;
				 end if; 
			end if;
		end if;
	end process;

	
-- Genertion of a signal indicates the reading of all Coeffiecients from Coefficient memory is complete

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			FiltOp_done	<= '0';
		else
			if(rising_edge(clk))then
				if(Coef_rdaddr = TAPLENGTH_2)then
					FiltOp_done	<= '1';
				else
					FiltOp_done	<= '0';
				end if;
			end if;
		end if;
	end process;


-- Generation of RD control signal for Input and Coefficient memories

	
	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			Coef0_rden	<= '0';
			Coef1_rden	<= '0';
		else
			if(rising_edge(clk))then
				if(mac_state = filt_op)then
					if((Coef_rdaddr >= uSRAMDEPTH) AND (Coef_rdaddr < TAPLENGTH))then
						Coef0_rden	<= '0';
						Coef1_rden 	<= '1';
					elsif(Coef_rdaddr < uSRAMDEPTH)then
						Coef0_rden	<= '1';
						Coef1_rden 	<= '0';
					end if;	
				end if;
			end if;
		end if;
	end process;


	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			Inp0_rden	<= '0';
			Inp1_rden	<= '0';
		else
			if(rising_edge(clk))then
				if(mac_state = filt_op)then
					if((inp_rdaddr >= uSRAMDEPTH) AND (inp_rdaddr < TAPLENGTH))then
						Inp0_rden	<= '0';
						Inp1_rden 	<= '1';
					elsif(inp_rdaddr < uSRAMDEPTH)then
						Inp0_rden	<= '1';
						Inp1_rden 	<= '0';
					end if;	
				end if;
			end if;
		end if;
	end process;
-- Generation of read address to Coefficent and Input memories

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			Coef_rdaddr1	<= (OTHERS => '0');
			Coef_rdaddr2	<= (OTHERS => '0');
		else
			if(rising_edge(clk))then				
				if((Coef_rdaddr >= uSRAMDEPTH) AND (Coef_rdaddr < TAPLENGTH))then	
					Coef_rdaddr1	<= (OTHERS => '0');
					Coef_rdaddr2	<= Coef_rdaddr - uSRAMDEPTH;
				elsif(Coef_rdaddr < uSRAMDEPTH)then
					Coef_rdaddr1	<= Coef_rdaddr;
					Coef_rdaddr2	<= (OTHERS => '0');
				end if;	
			end if;
		end if;
	end process;

process(clk, reset_n)
	begin
		if (reset_n = '0') then
			inp_rdaddr1		<= (OTHERS => '0');
			inp_rdaddr2		<= (OTHERS => '0');
		else
			if(rising_edge(clk))then				
				if((inp_rdaddr >= uSRAMDEPTH) AND (inp_rdaddr < TAPLENGTH))then	
					inp_rdaddr1		<= (OTHERS => '0');
					inp_rdaddr2		<= inp_rdaddr - uSRAMDEPTH;
				elsif(inp_rdaddr < uSRAMDEPTH)then
					inp_rdaddr1		<= inp_rdaddr;
					inp_rdaddr2		<= (OTHERS => '0');
				end if;	
			end if;
		end if;
	end process;


	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			inp_rdaddr	<= (others => '0');
		else
			if(rising_edge(clk))then
				if(mac_state = Op_clr)then
					inp_rdaddr	<= new_inprdaddr;
				elsif(mac_state = filt_op)then
					if(inp_rdaddr = TAPLENGTH_1)then
						inp_rdaddr	<= (others => '0');
					else
						inp_rdaddr	<= inp_rdaddr  + '1';
					end if;
				end if;
			end if;
		end if;
	end process;



	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			new_inprdaddr 	<= (OTHERS => '0');
		else
			if(rising_edge(clk))then	
				if(FiltOp_done = '1')then
					if(new_inprdaddr = TAPLENGTH)then
						new_inprdaddr 	<= (others => '0');
					else
						new_inprdaddr	<= new_inprdaddr  + '1';
					end if;
				end if;
			end if;
		end if;
	end process;



-- Latching the input on when valid data is present

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			inp_wrdata	 <=(OTHERS => '0');
		else
			if(rising_edge(clk))then
				if(Data_Valid = '1')then
					inp_wrdata	 <= Xn_in;
				end if;
			end if;
		end if;
	end process;


-- Generation of Write address to  Input memories for storing input samples
	
	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			inp_wraddr	 <= (OTHERS => '0');
		else
			if(rising_edge(clk))then
--				if(mac_state = Op_clr)then
--					inp_wraddr	<= new_inprdaddr;
--				else
					if(inp_wraddr = TAPLENGTH)then
						inp_wraddr	<= (OTHERS => '0');
					elsif(Data_Valid_dly = '1')then
						inp_wraddr	<= inp_wraddr + '1';
					end if;
				--end if;			
			end if;
		end if;
	end process;



	-- Generation of write address

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			inp_wraddr1	 <= (OTHERS => '0');
			inp_wraddr2	 <= (OTHERS => '0');
		else
			if(rising_edge(clk))then
				if(inp_wraddr >= ZERO AND  inp_wraddr < uSRAMDEPTH)then
					inp_wraddr1	<= inp_wraddr;
					inp_wraddr2	<= (OTHERS => '0');
				elsif((inp_wraddr >= uSRAMDEPTH) AND (inp_wraddr < TAPLENGTH))then
					inp_wraddr2	<= inp_wraddr - uSRAMDEPTH;
					inp_wraddr1	<= (others => '0');
				end if;
			end if;
		end if;
	end process;

	
-- Generation of WR control signal for Input and Coefficient memories

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			InpCoef0_wren	<= '0';
			InpCoef1_wren	<= '1';	
		else
			if(rising_edge(clk))then
				if(inp_wraddr >= ZERO AND  inp_wraddr < uSRAMDEPTH)then
					InpCoef0_wren	<= Data_Valid_dly;
					InpCoef1_wren	<= '0';
				elsif((inp_wraddr >= uSRAMDEPTH) AND (inp_wraddr < TAPLENGTH))then
					InpCoef0_wren	<= '0';
					InpCoef1_wren	<= Data_Valid_dly;
				else
					InpCoef0_wren	<= '0';
					InpCoef1_wren	<= '0';
				end if;
			end if;
		end if;
	end process;

	-- store the input data to input memories

	process(clk, reset_n)
	begin
		if (reset_n = '0') then
			inp_wrdata_dly0	 <= (others =>  '0');

		else
			if(rising_edge(clk))then
				inp_wrdata_dly0	 <= inp_wrdata;
			end if;
		end if;
	end process;

	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			Data_Valid_dly	 <= '0';
		else
			if(rising_edge(clk))then
				Data_Valid_dly	 <= Data_Valid;
			end if;
		end if;
	end process;


	Initdone <= not(Data_Valid) and Data_Valid_dly;

	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			rden <= '0';
		else
			if(rising_edge(clk))then
				if(Initdone = '1')then
					rden <= '1';
				end if;
			end if;
		end if;
	end process;


U0: mulacc_18x18
	port map(
        -- Inputs
        A0        => inp_rddata,
        A0_ACLR_N => reset_n,
        A0_EN     => '1',
        A0_SCLR_N => '1',
        B0        => Coef_rddata,
        B0_ACLR_N => reset_n,
        B0_EN     => '1',
        B0_SCLR_N => '1',
        CLK       => CLK,
        P_ACLR_N  => reset_n,
        P_EN      => '1',
        P_SCLR_N  => mac_clr,
        -- Outputs
        CDOUT     => OPEN,
        P         => Yn_out
        );

U1: Coef_RAM 
    -- Port list
    port map(
        -- Inputs
        A_ADDR 			=> Coef_rdaddr1(5 downto 0),
		A_BLK			=> Coef0_rden,
        B_ADDR 			=> PORTB_Coefaddr,
		B_BLK 			=> '0',
        CLK    			=> clk,
        C_ADDR 			=> PORTC_ADDR,
		C_BLK			=> '0',	
        C_DIN  			=> PORTC_DIN,
        -- Outputs
        A_DOUT 			=> Coef_rddata1,
        B_DOUT 			=> OPEN
        );

U2: Inp_RAM
    -- Port list
    port map(
        -- Inputs
        A_ADDR 			=> inp_rdaddr1(5 downto 0),
		A_BLK			=> Inp0_rden,
        B_ADDR 			=> PORTB_Inpaddr,
		B_BLK 			=> '0',
		CLK				=> clk,
        C_ADDR 			=> inp_wraddr1(5 downto 0),
		C_BLK			=> InpCoef0_wren,--InpCoef0_wren_dly
        C_DIN  			=> inp_wrdata_dly0,--inp_wrdata_dly
        -- Outputs
        A_DOUT 			=> inp_rddata1,
        B_DOUT 			=> OPEN
        );



U1_1: Coef_RAM1 
     --Port list
    port map(
         --Inputs
        A_ADDR 			=> Coef_rdaddr2(5 downto 0),
		A_BLK			=> Coef1_rden,
        B_ADDR 			=> PORTB_Coefaddr,
		B_BLK 			=> '0',
        CLK    			=> clk,
        C_ADDR 			=> PORTC_ADDR,
		C_BLK			=> '0',	
        C_DIN  			=> PORTC_DIN,
         --Outputs
        A_DOUT 			=> Coef_rddata2,
        B_DOUT 			=> OPEN
        );

U2_1: Inp_RAM1
     --Port list
    port map(
         --Inputs
        A_ADDR 			=> inp_rdaddr2(5 downto 0),
		A_BLK			=> Inp1_rden,
        B_ADDR 			=> PORTB_Inpaddr,
		B_BLK 			=> '0',
		CLK				=> clk,
        C_ADDR 			=> inp_wraddr2(5 downto 0),
		C_BLK			=> InpCoef1_wren,--InpCoef1_wren_dly	
        C_DIN  			=> inp_wrdata_dly0,--inp_wrdata_dly
         --Outputs
        A_DOUT 			=> inp_rddata2,
        B_DOUT 			=> OPEN
        );


-----	
	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			Sel_Coef1	<= '0';
			Sel_Coef2	<= '0';
			Sel_Inp1	<= '0';
			Sel_Inp2	<= '0';
		else
			if(rising_edge(clk))then
				Sel_Coef1	<= Coef0_rden;
				Sel_Coef2	<= Coef1_rden;

				Sel_Inp1	<= Inp0_rden;
				Sel_Inp2	<= Inp1_rden;
			end if;
		end if;
	end process;


	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			Coef_rddata <= (others => '0');
		else
			if(rising_edge(clk))then
				if(Sel_Coef1 = '1')then
					Coef_rddata <= Coef_rddata1;
				elsif(Sel_Coef2 = '1')then
					Coef_rddata <= Coef_rddata2;
				end if;
			end if;
		end if;
	end process;

	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			inp_rddata	<= (others => '0');
		else
			if(rising_edge(clk))then
				if(Sel_Inp1 = '1')then
					inp_rddata 	<= inp_rddata1;
				elsif(Sel_Inp2 = '1')then
					inp_rddata 	<= inp_rddata2;
				end if;
			end if;
		end if;
	end process;

----  Clears the mathblock output after calculating each FIR output.

	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			clrsig <= '0';
		else
			if(rising_edge(clk))then
				if(mac_state = Op_clr or mac_state = mac_dly0)then
					clrsig <= '1';
				else
					clrsig <= '0';	
				end if;
			end if;
		end if;
	end process;

	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			clrsig_1  <= '0';
			clrsig_0  <= '0';
			clr		  <= '0';
		else
			if(rising_edge(clk))then
				clrsig_0 <= clrsig;
				clrsig_1 <= clrsig_0;	
				clr 	 <= clrsig_1;
			end if;
		end if;
	end process;

	mac_clr <= not(clr);

--- FIR output

	process(clk, reset_n)
	begin
		if(reset_n = '0') then
			Mac_out	 <= (others => '0');
		else
			if(rising_edge(clk))then
				if(clr = '1')then
				  Mac_out <= Yn_out;
				end if;
			end if;
		end if;
	end process;


end MAC_FIR_ARCH;   




