--------------------------------------------------------------------------------
-- 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.utils_pkg.all;
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_semi_parallel is
Generic
(
	ntaps_per_stage		: integer := 32;
	nstages				: integer := 2;
	pipe_latency		: integer := 2;
	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
);
Port
(
	rst			: in std_logic;
	clk			: in std_logic;
	h_addr		: in natural range 0 to nstages*ntaps_per_stage-1;
	h_we		: in std_logic;
	h_in		: in sfixed;
	din_vld		: in std_logic;
	din			: in sfixed;
	dout_vld	: out std_logic;
	start		: in  std_logic;
	rdy			: out std_logic;
	dout		: out sfixed
);
end fir_semi_parallel;

architecture Behavioral of fir_semi_parallel is

	-------------------------------------------------------------------------------
	constant coef_addr_nbits	: natural := NextExpBaseTwo(ntaps_per_stage) + NextExpBaseTwo(nstages);
	
	subtype coef_t is sfixed(shi(nbits_stages, nbits_stages_frac) downto slo(nbits_stages, nbits_stages_frac));
	subtype count_t is natural range 0 to ntaps_per_stage-1;
	type count_array_t is array (nstages-1 downto 0) of count_t;
		
	subtype stage_t is sfixed(shi(nbits_stages, nbits_stages_frac) downto slo(nbits_stages, nbits_stages_frac));
	type stage_array_t is array (0 to nstages) of stage_t;

	signal count_array			: count_array_t;
	signal count0				: count_t;
	signal ce_array				: unsigned(nstages downto 0);
	signal en_array				: unsigned(nstages downto 0);
	signal coef_addr			: unsigned(coef_addr_nbits-1 downto 0);
	
	SIGNAL x			: stage_array_t;
	SIGNAL xo			: stage_array_t;
	SIGNAL y			: stage_array_t;
	SIGNAL accu			: sfixed(shi(nbits_stages, nbits_stages_frac) downto slo(nbits_stages, nbits_stages_frac));

	SIGNAL act			: std_logic := '0';
	SIGNAL start_r		: std_logic := '0';
	signal accu_load	: std_logic;
	signal accu_ld_pipe	: unsigned(nstages+pipe_latency-1 downto 0);
	
	signal din_r 		: sfixed(shi(nbits_in, nbits_in_frac) downto slo(nbits_in, nbits_in_frac));
	signal coef_we 		: unsigned(nstages-1 downto 0);
	
	type coef_array_t is array (0 to nstages-1) of unsigned(nbits_stages-1 downto 0);
	signal coef_din		: unsigned(nbits_stages-1 downto 0);
	signal coef_dout	: coef_array_t;

	-------------------------------------------------------------------------------
begin

	assert ntaps_per_stage = NextPowerOfTwo(ntaps_per_stage) report "ntaps_per_stage must be of form 2**R!" severity failure;
	assert ntaps_per_stage >= 4 report "ntaps_per_stage must be greater than 4" severity failure;

	x(0)		<= resize(din_r, x(0));
	y(0)		<= to_sfixed(0.0, y(0));

	accu_load	<= accu_ld_pipe(accu_ld_pipe'left);
	start_r		<= act and ce_array(ce_array'left) and not ce_array(ce_array'left-1);
	
din_register:	
	process(clk)
	begin
		if rising_edge(clk) then
			if din_vld = '1' then
				din_r <= din;
			end if;
		end if;
	end process;

accu_load_pipe:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				accu_ld_pipe <= (others => '0');
			else
				accu_ld_pipe <= accu_ld_pipe(accu_ld_pipe'left-1 downto 0) & start_r;
			end if;	
		end if;
	end process;

coef_we_gen:
	process(clk)
	variable i : natural range 0 to nstages-1;
	variable caddr	: unsigned(coef_addr_nbits-1 downto 0);
	variable saddr	: unsigned(NextExpBaseTwo(nstages)-1 downto 0);
	begin
		if rising_edge(clk) then
			caddr		:= to_unsigned(h_addr, coef_addr_nbits);
			saddr		:= caddr(caddr'left downto NextExpBaseTwo(ntaps_per_stage));
			i 			:= to_integer(saddr);
			coef_we		<= (others => '0');
			coef_we(i)	<= '1';
			coef_din	<= unsigned(h_in);
			coef_addr	<= caddr;
		end if;	
	end process;

rdy_logic:	
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				rdy	<= '1';
				act	<= '0';
			elsif start = '1' then
				rdy	<= '0';
				act	<= '1';
			elsif count0 = count_t'high-2 then
				rdy	<= '1';
				act	<= '0';
			end if;
		end if;
	end process;

counter:	
	process(clk)
	variable cnt	: count_t;
	variable enable	: std_logic;
	begin
		if rising_edge(clk) then
			if rst = '1' then
				cnt := count_t'high;
			elsif cnt /= count_t'high then
				cnt := cnt + 1;
			else
				enable	:= '0';
				if start_r = '1' then
					enable	:= '1';
					cnt := 0;
				end if;
			end if;
			count0	<= cnt;
			count_array	<= count_array(count_array'left-1 downto 0) & cnt;
			en_array	<= en_array(en_array'left-1 downto 0) & enable;
		end if;
	end process;

ce_pipe:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				ce_array <= (others => '0');
			else
				ce_array <= ce_array(ce_array'left-1 downto 0) & din_vld;
			end if;
		end if;
	end process;

gen_coef_ram:
	for i in 0 to nstages-1 generate
	signal addr_a : unsigned (NextExpBaseTwo(ntaps_per_stage)-1 downto 0);
	signal addr_b : unsigned (NextExpBaseTwo(ntaps_per_stage)-1 downto 0);
	begin
		addr_a	<= coef_addr(addr_a'length-1 downto 0);
		addr_b <= to_unsigned(count_array(i), addr_b'length);
		inst_coef_ram: entity work.dpram_1w1r2c_ro
		GENERIC MAP 
		(
			addr_width	=> NextExpBaseTwo(ntaps_per_stage),
			data_width	=> nbits_stages
		)
		PORT MAP (
			clk_a	=> clk,
			clk_b	=> clk,
			we_a	=> coef_we(i),
			re_b	=> '1',
			addr_a	=> addr_a,
			addr_b	=> addr_b,
			din_a	=> coef_din,
			dout_b	=> coef_dout(i)
		);
	end generate;
		

gen_stages:
	for i in 0 to nstages-1 generate
	signal h_in : coef_t;
	begin
		h_in <= sfixed(coef_dout(i));
		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
		)
		PORT MAP
		(
			rst			=> rst,
			clk			=> clk,
			ce			=> en_array(i),
			x_in		=> x(i+1),
			y_in		=> y(i),
			h_in		=> h_in,
			x_out		=> xo(i+1),
			y_out		=> y(i+1)
		);
	end generate;
					

gen_delays:
	for i in 0 to nstages-1 generate
	begin
		inst_delay_line : entity work.delay_line(ram_based)
		GENERIC MAP
		(
			nbits		=> nbits_stages,
			nbits_frac	=> nbits_stages_frac,
			ntaps		=> ntaps_per_stage,
			delay_fix	=> 0
		)
		PORT MAP
		(
			rst			=> rst,
			clk			=> clk,
			shift_en	=> ce_array(i),
			delay		=> count_array(i),
			din			=> x(i),
			dout		=> x(i+1)
		);
	end generate;
	
acumulator:
	process(clk)
	begin
		if rising_edge(clk) then
			dout_vld	<= accu_load;
			if rst = '1' then
				dout_vld	<= '0';
			elsif en_array(en_array'left) = '1' then
				if accu_load = '1' then
					dout	<= resize(accu, shi(nbits_out, nbits_out_frac), slo(nbits_out, nbits_out_frac));
					accu 	<= y(nstages);
				else
					accu	<= resize(accu + y(nstages), accu);
				end if;
			end if;
		end if;
	end process;
	
	
end Behavioral;
