---------------------------------------------------------------
-- FIR Digital Filter Design (DSP example)
-- tested by Weijun Zhang, 04/2001
--
-- VHDL Data-Flow modeling
-- KEYWORD:
-- generate, array, range, constant and subtype
---------------------------------------------------------------

library IEEE; -- declare the library
use IEEE.std_logic_1164.all;
use IEEE.std_logic_arith.all;

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

entity FIR_filter is
  port (
    rst            : in std_logic;
    clk            : in std_logic;
    coef_ld, start : in std_logic;
    o_enable       : in std_logic;
    bypass         : in std_logic;
    Yn_in, Xn_in   : in std_logic_vector(15 downto 0);
    Xn_out         : out std_logic_vector(3 downto 0);
    Yn_out         : out std_logic_vector(15 downto 0)
  );
end FIR_filter;

architecture BEH of FIR_filter is

  constant K : integer := 4; -- circuit has four stages

  -- use type and subtype to define the complex signals

  subtype bit4 is std_logic_vector(3 downto 0);
  subtype bit8 is std_logic_vector(7 downto 0);
  subtype bit16 is std_logic_vector(15 downto 0);

  type klx4 is array (K downto 0) of bit4;
  type kx8 is array (K - 1 downto 0) of bit8;
  type klx8 is array (K downto 0) of bit8;
  type kx16 is array (K - 1 downto 0) of bit16;
  type klx16 is array (K downto 0) of bit16;

  -- define signal in type of arrays
  signal REG1, REG2, COEF : klx4;
  signal MULT8            : kx8;
  signal MULT16           : kx16;
  signal SUM              : klx16;
  signal Xn_tmp           : bit4;
  signal Yn_tmp           : bit16;

begin

  -- initialize the first stage

  REG2(K) <= Xn_in when (start = '1')
else (REG2(K)'range       => '0');
  REG1(K) <= (REG1(K)'range => '0');
  SUM(K)  <= Yn_in;
  COEF(K) <= Xn_in;

  -- start the computation, use generate to obtain the
  -- multiple stages

  gen8 : for j in K - 1 downto 0 generate

    stages : process (rst, clk)
    begin

      if (rst = '0') then
        REG1(j)   <= (REG1(j)'range   => '0');
        REG2(j)   <= (REG2(j)'range   => '0');
        COEF(j)   <= (COEF(j)'range   => '0');
        MULT16(j) <= (MULT16(j)'range => '0');
        SUM(j)    <= (SUM(j)'range    => '0');
      elsif (clk'event and clk = '1') then
        REG1(j) <= REG2(j + 1);
        REG2(j) <= REG1(j);
        if (coef_ld = '1') then
          COEF(j) <= COEF(j + 1);
        end if;
        MULT8(j)              <= signed(REG1(j)) * signed(COEF(j));
        MULT16(j)(7 downto 0) <= MULT8(j);
        if (MULT8(j)(7) = '0') then
          MULT16(j)(15 downto 8) <= "00000000";
        else
          MULT16(j)(15 downto 8) <= "11111111";
        end if;
        SUM(j) <= signed(MULT16(j)) + signed(SUM(j + 1));
      end if;

    end process;

  end generate;

  -- control the outputs by concurrent statements

  Xn_tmp <= Xn_in when bypass = '1' else
    REG2(0) when coef_ld = '0' else
    COEF(0);

  Yn_tmp <= Yn_in when bypass = '1' else
    SUM(0);

  Xn_out <= Xn_tmp when o_enable = '0' else
    (Xn_out'range => 'Z');

  Yn_out <= Yn_tmp when o_enable = '0' else
    (Yn_out'range => 'Z');

end BEH;
