--------------------------------------------------
-- MemoryFir.vhd
-- by Toshio Iwata at digitalfilter.com 2004/09/09

library IEEE;
  use IEEE.std_logic_1164.all;
  use IEEE.std_logic_arith.all;

entity MemoryFir is
   Port (
          RST_N : In std_logic;
          MCLK : In std_logic;
          FSCLK : In std_logic;
          XIN : In std_logic_vector(15 downto 0);
          YOUT : Out std_logic_vector(15 downto 0) );
end MemoryFir;

architecture RTL of MemoryFir is

  component Rom
  Port (
          CLK : In std_logic;
          ADDR : In std_logic_vector(7 downto 0);
          DATA : Out std_logic_vector(15 downto 0) );
  end component;

  component Ram
  Port (
          CLK : In std_logic;
          WR_N : In std_logic;
          WRADDR : In std_logic_vector(7 downto 0);
          RDADDR : In std_logic_vector(7 downto 0);
          WRDATA : In std_logic_vector(15 downto 0);
          RDDATA : Out std_logic_vector(15 downto 0) );
  end component;

  signal Xn : std_logic_vector(15 downto 0);
  signal Hn : std_logic_vector(15 downto 0);
  signal HnXn : signed(31 downto 0);
  signal HnXn_20b : signed(19 downto 0);
  signal XIN_sig : signed(15 downto 0);
  signal ADDOUT_dly : signed(21 downto 0);
  signal ADDOUT : signed(21 downto 0);
  signal counter8b : unsigned(7 downto 0);
  signal counter8b_fs : unsigned(7 downto 0);
  signal rdaddr_sig : unsigned(7 downto 0);
  signal ADDR : std_logic_vector(7 downto 0);
  signal RDADDR : std_logic_vector(7 downto 0);
  signal WRADDR : std_logic_vector(7 downto 0);
  signal MuxXn : signed(15 downto 0);
  signal MuxHn : signed(15 downto 0);
  signal fsclk_dly1 : std_logic;
  signal ADDOUT_dly_clr : std_logic;
  signal WR_N : std_logic;

begin -- MemoryFir
-------------------------------------------------------------------
  WR_N <= not FSCLK;
  
-------------------------------------------------------------------
gen_ADDR : process( counter8b )
begin
  for i in 7 downto 0 loop
    ADDR(i) <= counter8b(i);
  end loop;
end process gen_ADDR;

-------------------------------------------------------------------
  rdaddr_sig <= counter8b + counter8b_fs + 1;

-------------------------------------------------------------------
gen_RDADDR : process( rdaddr_sig )
begin
  for i in 7 downto 0 loop
    RDADDR(i) <= rdaddr_sig(i);
  end loop;
end process gen_RDADDR;

-------------------------------------------------------------------
gen_WRADDR : process( counter8b_fs )
begin
  for i in 7 downto 0 loop
    WRADDR(i) <= counter8b_fs(i);
  end loop;
end process gen_WRADDR;

-------------------------------------------------------------------
Rom_1 : Rom port map (
          CLK => MCLK,
          ADDR => ADDR,
          DATA => Hn );

-------------------------------------------------------------------
Ram_1 : Ram port map (
          CLK => MCLK,
          WR_N => WR_N,
          WRADDR => WRADDR,
          RDADDR => RDADDR,
          WRDATA => XIN,
          RDDATA => Xn );

-------------------------------------------------------------------
gen_fsclk_dly : process( RST_N, MCLK )
begin
  if( RST_N = '0' ) then
    fsclk_dly1 <= '0';
  elsif( MCLK'event and MCLK = '1' ) then
    fsclk_dly1 <= FSCLK;
  end if;
end process gen_fsclk_dly;

-------------------------------------------------------------------
  ADDOUT_dly_clr <= fsclk_dly1;
              
-------------------------------------------------------------------
gen_counter8b : process( RST_N, MCLK )
begin
  if( RST_N = '0' ) then
    counter8b <= (others => '0');
  elsif( MCLK'event and MCLK = '1' ) then
    if( FSCLK = '1' ) then 
      counter8b <= (others => '0');
    else 
      counter8b <= counter8b + 1; -- up counter
    end if;
  end if;
end process gen_counter8b;

-------------------------------------------------------------------
gen_counter8b_fs : process( RST_N, MCLK )
begin
  if( RST_N = '0' ) then
    counter8b_fs <= (others => '1');
  elsif( MCLK'event and MCLK = '1' ) then
    if( FSCLK = '1' ) then 
      counter8b_fs <= counter8b_fs - 1; -- down counter
    end if;
  end if;
end process gen_counter8b_fs;

-------------------------------------------------------------------
gen_MuxXn : process( Xn )
begin
  for i in 15 downto 0 loop
    MuxXn(i) <= Xn(i);
  end loop;
end process gen_MuxXn;

-------------------------------------------------------------------
gen_MuxHn : process( Hn )
begin
  for i in 15 downto 0 loop
    MuxHn(i) <= Hn(i);
  end loop;
end process gen_MuxHn;
  
-------------------------------------------------------------------
  HnXn <= MuxXn * MuxHn;

-------------------------------------------------------------------
gen_XIN_sig : process( XIN )
begin
  for i in 15 downto 0 loop
    XIN_sig(i) <= XIN(i);
  end loop;
end process gen_XIN_sig;

----------------------------------------------------
gen_HnXn_20b: process( HnXn )
begin
  for k in 19 downto 0 loop
    HnXn_20b(k) <= HnXn(k+11);
  end loop;
end process gen_HnXn_20b;

----------------------------------------------------
  ADDOUT <= HnXn_20b + ADDOUT_dly;

----------------------------------------------------
gen_ADDOUT_dly : process( RST_N, MCLK )
variable tmpint : signed(21 downto 0);
begin
  if( RST_N = '0' ) then
    ADDOUT_dly <= (others => '0');
  elsif( MCLK'event and MCLK = '1' ) then
    if( ADDOUT_dly_clr = '1' ) then 
      ADDOUT_dly <= (others => '0');
    else
      ADDOUT_dly <= ADDOUT;
    end if;
  end if;
end process gen_ADDOUT_dly;

-------------------------------------------------------------------
gen_YOUT : process( RST_N, MCLK )
begin
  if( RST_N = '0' ) then
    YOUT <= (others => '0');
  elsif( MCLK'event and MCLK = '1' ) then
    if( ADDOUT_dly_clr = '1' ) then 
      if( ADDOUT_dly(21) = '0' and -- positive value
         ( ADDOUT_dly(20) = '1' 
        or ADDOUT_dly(19) = '1' ) ) then -- overflow
        YOUT <= "0111111111111111";
      elsif( ADDOUT_dly(21) = '1' and -- negative value
         ( ADDOUT_dly(20) = '0' 
        or ADDOUT_dly(19) = '0' ) ) then -- underflow
        YOUT <= "1000000000000000";
      else 
        for i in 15 downto 0 loop
          YOUT(i) <= ADDOUT_dly(i+4);
        end loop;
      end if;
    end if;
  end if;
end process gen_YOUT;

end; -- MemoryFir
