--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:    12:16:14 10/02/05
-- Design Name:    
-- Module Name:    fir_parallel - Behavioral
-- Project Name:   
-- Target Device:  
-- Tool versions:  
-- Description:
--
-- Dependencies:
-- 
-- Revision:
-- Revision 0.01 - File Created
-- Additional Comments:
-- 
--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.MATH_REAL.ALL;
USE ieee.numeric_std.ALL;

library ieee_proposed;
use ieee_proposed.fixed_float_types.all;
use ieee_proposed.fixed_pkg.all;

library work;
use work.fixed_util_pkg.all;
use work.fir_pkg.all;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity fir_iterative is
Generic
(
	ntaps				: integer := 33;
	nbits_in			: integer := 12;
	nbits_in_frac		: integer := 11;
	nbits_stages		: integer := 13;
	nbits_stages_frac	: integer := 11;
	nbits_out			: integer := 12;
	nbits_out_frac		: integer := 11;
	fir_mode			: fir_iterative_mode_t := normal;
	rounding			: boolean := true;
	saturating			: boolean := true
);
Port
(
	srst			: in std_logic;
	clk				: in std_logic;
	h_din			: in sfixed;
	h_addr_out		: out natural;
	ready			: out std_logic;
	x_valid			: in std_logic;
	x_din			: in sfixed;
	y_dout_valid	: out std_logic;
	y_dout			: out sfixed
);
end fir_iterative;

architecture Behavioral of fir_iterative is

-------------------------------------------------------------------------------
	constant fir_stage_mode : fir_stage_mode_t := transposed;
	constant ntaps_is_even : boolean := (ntaps mod 2) = 0;
	constant h_addr_bits : integer := taps_nbits(ntaps, fir_mode);
	
	subtype in_t is sfixed(shi(nbits_in, nbits_in_frac) downto slo(nbits_in, nbits_in_frac));
	subtype out_t is sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));
	subtype stage_t is sfixed(shi(nbits_stages, nbits_stages_frac) downto slo(nbits_stages, nbits_stages_frac));
	
	type state_t is (init, idle, start, proc, p1, p2, output);
	signal s, sn : state_t;
	signal x_stage, xo, y_stage, y_stage_in : stage_t;
	signal stage_en, cnt_en, new_round : std_logic;
	
	subtype tap_cnt_t is natural range 0 to ntaps-1;
	signal htap_cnt : tap_cnt_t;
	signal xtap_cnt_w : tap_cnt_t;
	signal xtap_cnt_r : tap_cnt_t;
	signal stage_valid, clr_xmem, valid, fir_stage_clr,  fir_stage_en, cnt_reset : std_logic;
	subtype tap_range_t is natural range 0 to ntaps_addr(ntaps, fir_mode)-1;
	signal h_addr : tap_range_t;
	signal xmem_din, xmem_dout : in_t;
	signal xmem_we, y_valid : std_logic;

	type xmem_t is array (0 to ntaps-1) of in_t;
	signal xmem : xmem_t;
	signal y_out_reg : out_t;
	signal y_reg_valid : std_logic;
	
	signal x_stage_vld	: std_logic	:= '1';
	signal y_stage_vld	: std_logic;
	
	-------------------------------------------------------------------------------
begin
	
	h_addr_out <= h_addr;
	x_stage <= resize(xmem_dout, x_stage, fixed_wrap, fixed_truncate);
	y_stage_in	<= to_sfixed(0, y_stage_in) when new_round = '1' else y_stage;
	
	----------------------------------------
	process (clk)
	begin
	if rising_edge(clk) then
		y_reg_valid <= '0';
		if srst = '1' then
			y_out_reg <= to_sfixed(0, y_out_reg);
		elsif y_valid = '1' then
			y_out_reg <=  resize(y_stage, y_out_reg);
			y_reg_valid <= '1';
		end if;
	end if;
	end process;

	process (clk)
	begin
	if rising_edge(clk) then
		y_dout_valid <= '0';
		if srst = '1' then
			y_dout <= to_sfixed(0, sproto(nbits_out, nbits_out_frac));
		elsif y_reg_valid = '1' then
			y_dout <= resize(y_out_reg, sproto(nbits_out, nbits_out_frac), fixed_wrap, fixed_round);
			y_dout_valid <= '1';
		end if;
	end if;
	end process;

	----------------------------------------
	proc_xmem_rw: process (clk)
	begin
	if rising_edge(clk) then
		if xmem_we = '1' then
			xmem(xtap_cnt_w) <= xmem_din;
		end if;
		xmem_dout <= xmem(xtap_cnt_r);
	end if;
	end process;

	----------------------------------------
	proc_xmem_dinmux: process(new_round, clr_xmem, x_din, xmem_dout)
	begin
		xmem_din <= to_sfixed(0, xmem_din);
		if new_round = '1' and clr_xmem = '0' then
			xmem_din <= sfixed(x_din);
		end if;
	end process;

	----------------------------------------
	proc_xmem_pointer: process(clk, new_round ,clr_xmem)
	begin
		xmem_we <= clr_xmem or new_round;
		if rising_edge(clk) then
			if srst = '1' then
				xtap_cnt_w <= tap_cnt_t'high; 
			elsif xmem_we = '1' then
				if xtap_cnt_w /= tap_cnt_t'low then
					xtap_cnt_w <= xtap_cnt_w - 1;
				else
					xtap_cnt_w <= tap_cnt_t'high;
				end if;	
			end if;	
		end if;	
	end process;

	----------------------------------------
	proc_h_addr: process(htap_cnt)
	begin
		if fir_mode = symmetric then
			if ntaps_is_even then 
				if htap_cnt > ntaps/2-1 then
					h_addr <= ntaps - htap_cnt - 1;
				else
					h_addr <= htap_cnt;
				end if;
			else	
				if htap_cnt > (ntaps-1)/2 then
					h_addr <= ntaps - htap_cnt - 1;
				else
					h_addr <= htap_cnt;
				end if;
			end if;	
		else	
			h_addr <= htap_cnt;
		end if;	
	end process;

	----------------------------------------
	proc_tap_counter: process(cnt_reset, clk, htap_cnt, xtap_cnt_r, xtap_cnt_w, cnt_en)
	begin
		if rising_edge(clk) then
			if cnt_reset = '1' then
				htap_cnt <= tap_cnt_t'low; 
				xtap_cnt_r <= xtap_cnt_w; 
			elsif cnt_en = '1' then
				if htap_cnt /= tap_cnt_t'high then
					htap_cnt <= htap_cnt + 1;
				else
					htap_cnt <= tap_cnt_t'low; 
				end if;	
				if xtap_cnt_r /= tap_cnt_t'high then
					xtap_cnt_r <= xtap_cnt_r + 1;
				else
					xtap_cnt_r <= tap_cnt_t'low; 
				end if;	
			end if;	
		end if;	
	end process;

	----------------------------------------
	proc_valid_out: process(srst, clk, valid, new_round)
	variable val_pipe : unsigned(4 downto 0);
	variable ctrl_pipe : unsigned(2 downto 0);

	begin
		if rising_edge(clk) then
			if srst = '1' then
				ctrl_pipe := (others => '0');
				fir_stage_en <= '0';
				fir_stage_clr <= '0';
			else	
				ctrl_pipe := ctrl_pipe(ctrl_pipe'left-1 downto ctrl_pipe'right) & new_round;
				fir_stage_en <= stage_en;
				fir_stage_clr <= ctrl_pipe(2);
			end if;	
		end if;
		cnt_reset <= new_round;
	end process;

	----------------------------------------
	proc_fsm: process(s, x_valid, htap_cnt, xtap_cnt_w)
	begin
		clr_xmem <= '0';
		new_round <= '0';
		cnt_en <= '0';
		ready <= '0';
		valid <= '0';
		sn <= s;
		stage_en <= '0';
		y_valid <= '0';
		
		case s is
			when init =>
			clr_xmem <= '1';
			if xtap_cnt_w = tap_cnt_t'low then
				sn <= idle;
			end if;
			
			when idle =>
			ready <= '1';
			if x_valid = '1' then
				sn <= start;
			end if;	
			
			when start =>
				new_round <= '1';
				sn <= proc;

			when proc =>
				stage_en <= '1';
				cnt_en <= '1';
				if htap_cnt = tap_cnt_t'high then
					sn <= p1;
				end if;

			when p1 =>
				sn <= p2;
				
			when p2 =>
				sn <= output;
				ready <= '1';

			when output =>
				sn <= idle;
				y_valid <= '1';
				ready <= '1';
				if x_valid = '1' then
					sn <= start;
				end if;	

			when others => null;
		end case;
	end process;	

	proc_fsm_next: process(srst, clk, sn)
	begin
		if rising_edge(clk) then
			if srst = '1' then
				s <= init;
			else
				s <= sn;
			end if;
		end if;
	end process;	
	
	----------------------------------------
		inst_fir_stage: entity work.fir_stage_sys
		GENERIC MAP
		(
			nbits_in		=> nbits_stages,
			nbits_in_frac	=> nbits_stages_frac,
			nbits_out		=> nbits_stages,
			nbits_out_frac	=> nbits_stages_frac,
			pipe_latency	=> 2
		)
		PORT MAP
		(
			rst			=> srst,
			ce			=> x_stage_vld,
			clk			=> clk,
			x_in		=> x_stage,
			y_in		=> y_stage_in,
			h_in		=> h_din,
			x_out		=> xo,
			y_out		=> y_stage,
			vld_out		=> y_stage_vld
		);

	----------------------------------------
	-- Finished instantiation
	----------------------------------------
	
end Behavioral;
