--------------------------------------------------------------------------------
-- Company: 
-- Engineer:
--
-- Create Date:   17:16:42 13.05.2007
-- Design Name:   tb_fir_stage
-- Module Name:   tb_fir_stage.vhd
-- Project Name:  fir_stage
-- Target Device:  
-- Tool versions:  
-- Description:   
-- 
--------------------------------------------------------------------------------
LIBRARY ieee;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.MATH_REAL.ALL;
USE ieee.numeric_std.ALL;
use std.textio.all; --  Imports the standard textio package.

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.filter_pkg.all;
use work.fir_pkg.all;

use work.PCK_FIO.all;

ENTITY tb_fir_semi_parallel IS
	Generic
	(
		ntaps_per_stage		: integer := 32;
		nstages				: integer := 2;
		nbits_in			: integer := 32;
		nbits_in_frac		: integer := 30;
		nbits_stages		: integer := 32;
		nbits_stages_frac	: integer := 30;
		nbits_out			: integer := 32;
		nbits_out_frac		: integer := 30
	);
END tb_fir_semi_parallel;

ARCHITECTURE behavior OF tb_fir_semi_parallel IS 

	-------------------------------------------------------------------------------
	--Constants
	constant PERIOD : time := 10 ns;
	constant nsamples : integer := 32;
	constant M		 : integer := 1;

	--Inputs
	SIGNAL clk			: std_logic := '0';
	SIGNAL rst			: std_logic := '1';
	SIGNAL din_vld		: std_logic := '0';
	SIGNAL d_in			: sfixed(shi(nbits_in, nbits_in_frac) downto slo(nbits_in, nbits_in_frac));
	signal h_addr		: natural := 0;
	SIGNAL h_we			: std_logic := '0';
	SIGNAL h_in			: sfixed(shi(nbits_stages, nbits_stages_frac) downto slo(nbits_stages, nbits_stages_frac));
	SIGNAL start		: std_logic := '0';

	--Outputs
	SIGNAL d_out		: sfixed(shi(nbits_out, nbits_out_frac) downto slo(nbits_out, nbits_out_frac));
	SIGNAL dout_vld		: std_logic;
	SIGNAL rdy			: std_logic;

	SIGNAL fileout_enable   : std_logic := '1';
	
	
	SIGNAL xi, yo : real := 0.0;

	CONSTANT ntaps	: natural := nstages*ntaps_per_stage;
	type h_mem_t is array (0 to ntaps-1) of sfixed(shi(nbits_stages, nbits_stages_frac) downto slo(nbits_stages, nbits_stages_frac));

	function to_h_mem(coef_real : real_array_t; ntaps : integer; proto : sfixed) return h_mem_t is
	variable res : h_mem_t;
	begin
		for i in 0 to ntaps-1 loop
			res(i) := to_sfixed(coef_real(i), proto, fixed_wrap, fixed_round);
		end loop;
		
		return res;
	end to_h_mem;	
	
	CONSTANT coeffs : real_array_t(0 to ntaps-1) := FilterCoef_Lowpass(ntaps, 0.1, 4.0);
	CONSTANT h_mem : h_mem_t := to_h_mem(coeffs, ntaps, h_in);
	
	-- file I/O
	subtype sample_t is integer range -32768 to 32767;
	type file_t is file of sample_t;

	signal x_input : real_array_t(0 to nsamples-1) :=
	(
		0.0,	
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		1.0,
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0,		
		0.0
		
	);
	
	-------------------------------------------------------------------------------
BEGIN

	xi <= to_real(d_in);
	yo <= to_real(d_out);
	
	----------------------------------------
	-- Instantiate the Unit Under Test (UUT)
	----------------------------------------
	uut : entity work.fir_semi_parallel
	GENERIC MAP
	(
		ntaps_per_stage		=> ntaps_per_stage,
		nstages				=> nstages,
		pipe_latency		=> 2,
		nbits_in			=> nbits_in,
		nbits_in_frac		=> nbits_in_frac,
		nbits_stages		=> nbits_stages,
		nbits_stages_frac	=> nbits_stages_frac,
		nbits_out			=> nbits_out,
		nbits_out_frac		=> nbits_out_frac
	)
	PORT MAP
	(
		rst			=> rst,
		clk			=> clk,
		h_addr		=> h_addr,
		h_we		=> h_we,
		h_in		=> h_in,
		din_vld		=> din_vld,
		din			=> d_in,
		dout_vld	=> dout_vld,
		rdy			=> rdy,
		start		=> start,
		dout		=> d_out
	);
	

	----------------------------------------
	-- Finished instantiation
	----------------------------------------
	tb_clk : PROCESS
	BEGIN
		clk <= not clk;
		wait for PERIOD/2;
	END PROCESS;

	tb : PROCESS
	file fi : file_t open read_mode is "wav_in.dat";
	variable si : sample_t;
	variable remain : integer;
	variable j : integer;
	BEGIN

		-- Wait 100 ns for global reset to finish
		wait for 4*PERIOD;
		rst <= '0';
		------------------------------------------
		wait for 2*PERIOD;
		
		-- write coeffs
		for i in 0 to ntaps-1 loop
			wait until rising_edge(clk);
			h_addr	<= i;
			h_in 	<= h_mem(i);
			h_we	<= '1';
		end loop;
		wait until rising_edge(clk);
--		h_addr	<= 0;
--		h_in 	<= to_sfixed(0.25, h_in);
--		h_we	<= '1';
--		wait until rising_edge(clk);
		h_we	<= '0';

		-- Write zeros
		remain := ntaps_per_stage*nstages;
		j := 0;
		while remain /= 0 loop
			wait until rising_edge(clk) and rdy = '1';
			for i in 0 to M-1 loop
				d_in <= to_sfixed(0.0, d_in);
				din_vld <= '1';
				j := j + 1;
				remain := remain - 1;
				wait until rising_edge(clk);
			end loop;
			din_vld <= '0';
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
			start <= '1';
			wait until rising_edge(clk);
			start <= '0';
		end loop;

		------------------------------------------
		-- Write data
		remain := nsamples;
		j := 0;
		while remain /= 0 loop
			wait until rising_edge(clk) and rdy = '1';
			for i in 0 to M-1 loop
				d_in <= to_sfixed(x_input(j), d_in);
				din_vld <= '1';
				j := j + 1;
				remain := remain - 1;
				wait until rising_edge(clk);
			end loop;
			din_vld <= '0';
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
			start <= '1';
			wait until rising_edge(clk);
			start <= '0';
		end loop;

		-- Write zeros
		remain := 100;
		j := 0;
		while remain /= 0 loop
			wait until rising_edge(clk) and rdy = '1';
			for i in 0 to M-1 loop
				d_in <= to_sfixed(0.0, d_in);
				din_vld <= '1';
				j := j + 1;
				remain := remain - 1;
				wait until rising_edge(clk);
			end loop;
			din_vld <= '0';
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
--			wait until rising_edge(clk);
			start <= '1';
			wait until rising_edge(clk);
			start <= '0';
		end loop;

		assert false report "Test finished" severity error;
		wait; 
	END PROCESS;
	
	tb_fo : PROCESS(clk, fileout_enable)
	file fo : file_t open write_mode is "wav_out.dat";
	variable so : sample_t;

	BEGIN
		if rising_edge(clk) and fileout_enable = '1' then
			if dout_vld = '1' then
				so := to_integer(d_out);
				write(fo, so);
			end if;	
		end if;

	END PROCESS;
	
END;
