--  tb_intersect.vhd (Intersect testbench)
--  'intersect' ray-triangle intersection soft core
--
--  Created by Wenzel Jakob on 8/29/06.
--  Copyright 2006. All rights reserved.

library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.techmap.all;
use work.usb_iface_pkg.all;
use work.ddr_memctrl_pkg.all;
use work.intersect_pkg.all;
use work.common.all;
use work.tb_scene_pkg.all;
use work.cache_pkg.all;
use work.fplib_pkg.all;

entity tb_intersect is
	generic (
		PACKET_SIZE : natural := 16
	);
end tb_intersect;

architecture behav of tb_intersect is
	component ddr is
		port (
			Dq 		: inout std_logic_vector(15 downto 0);
			Dqs		: inout std_logic_vector(1 downto 0);
			Addr	: in std_logic_vector(12 downto 0);
			Ba		: in std_logic_vector(1 downto 0);
			Clk		: in std_logic;
			Clk_n	: in std_logic;
			Cke		: in std_logic;
			Cs_n	: in std_logic;
			Ras_n	: in std_logic;
			Cas_n	: in std_logic;
			We_n	: in std_logic;
			Dm		: in std_logic_vector(1 downto 0)
		);
	end component ddr;

	type cmdType is (
		LOAD_MR,
		AUTOREFRESH,
		PRECHARGE,
		ACTIVE,
		WRITE,
		READ,
		NOP,
		UNDEF
	);

	-- Clock
	signal clk0, clk90, rst	: std_logic := '0';
	signal clk180, clk270	: std_logic := '0';
	signal ifclk			: std_logic := '0';
	signal oled_ack			: std_logic := '0';
	-- DDR signals
	signal ddr_clk			: std_logic := '0';
	signal ddr_clk_n		: std_logic := '0';
	signal ddr_dq			: std_logic_vector(15 downto 0) := (others => 'Z');
	signal ddr_dqs			: std_logic_vector(1 downto 0) := "ZZ";
	signal ddr_addr			: std_logic_vector(12 downto 0) := (others => '0');
	signal ddr_bank			: std_logic_vector(1 downto 0) := (others => '0');
	signal ddr_dm			: std_logic_vector(1 downto 0) := (others => '0');
	signal ddr_ras_n		: std_logic := 'Z';
	signal ddr_cas_n		: std_logic := 'Z';
	signal ddr_we_n			: std_logic := 'Z';
	signal ddr_cke			: std_logic := 'Z';
	signal ddr_cs_n			: std_logic := 'Z';
	signal cmd				: cmdType;
	-- DDR / Controller->Host signals
	signal h_ack_o			: std_logic;
	signal h_rdAck_o		: std_logic;
	signal h_earlyAck_o		: std_logic;
	signal h_op_i			: std_logic_vector(1 downto 0);
	signal h_addr_i			: std_logic_vector(22 downto 0);
	signal h_data_i			: std_logic_vector(31 downto 0);
	signal h_data_o			: std_logic_vector(31 downto 0);
	-- Control port (input)
	signal ctrl_i			: usb_ctrl_i_t;
	-- Control port (output)
	signal ctrl_o			: usb_ctrl_o_t;
	-- Communication
	signal fifo_o_do		: std_logic_vector(31 downto 0);
	signal fifo_i_di		: std_logic_vector(31 downto 0) := (others => '0');
	signal fifo_i_wren		: std_logic := '0';
	signal fifo_i_full		: std_logic := '0';
	signal fifo_o_empty		: std_logic := '0';
	signal fifo_o_rden		: std_logic := '0';
begin
	ifclk <= not ifclk after 10.4 ns; -- 48Mhz
	clk0 <= not clk0 after 5 ns;
	clk90 <= clk0 after 2.5 ns;
	clk180 <= clk90 after 2.5 ns;
	clk270 <= clk180 after 2.5 ns;
	oled_ack <= '0';

	-- Pipelined DDR SDRAM controller and
	-- I/O pads to MT46V16M16FG-6 DDR SDRAM
	ddr_wrp: ddr_wrapper generic map (
		tech => virtex4,
		TIME_INITIAL => 10_000,
		TIME_tRFSHROW => 100
	) port map (
		clk0 => clk0,
		cclk => clk270,
		dqsclk => clk90,
		ddrclk => clk90,
		rst => rst,
		ddr_clk => ddr_clk,
		ddr_clk_n => ddr_clk_n,
		ddr_cke => ddr_cke,
		ddr_cs_n => ddr_cs_n,
		ddr_dqs => ddr_dqs,
		ddr_dq => ddr_dq,
		ddr_dm => ddr_dm,
		ddr_bank => ddr_bank,
		ddr_addr => ddr_addr,
		ddr_ras_n => ddr_ras_n,
		ddr_cas_n => ddr_cas_n,
		ddr_we_n => ddr_we_n,
		h_addr_i => h_addr_i,
		h_data_i => h_data_i,
		h_data_o => h_data_o,
		h_op_i => h_op_i,
		h_rdAck_o => h_rdAck_o,
		h_earlyAck_o => h_earlyAck_o,
		h_ack_o => h_ack_o
	);

	-- Instantiate Intersect
	i_inst: intersect_bd generic map (
		PACKET_SIZE => PACKET_SIZE
	) port map (
		clk0 => clk0,
		rst => rst,
		ctrl_i => ctrl_i,
		ctrl_o => ctrl_o,
		oled_ack => oled_ack,
		ddr_ack => h_ack_o,
		ddr_earlyAck => h_earlyAck_o,
		ddr_rdAck => h_rdAck_o,
		ddr_op => h_op_i,
		ddr_addr => h_addr_i,
		ddr_do => h_data_i,
		ddr_di => h_data_o	
	);

	-- Input FIFO (Host -> FPGA)
	fifo_i: fifo32 generic map (
		tech => virtex4
	) port map (
		rst => rst,
		empty => ctrl_o.fifo_i_empty,
		full => fifo_i_full,
		rdclk => clk0,
		rden => ctrl_i.fifo_i_rden,
		do => ctrl_o.fifo_i_do,
		wrclk => ifclk,
		wren => fifo_i_wren,
		di => fifo_i_di
	);

	-- Output FIFO (FPGA -> Host)
	fifo_o: fifo32 generic map (
		tech => virtex4
	) port map (
		rst => rst,
		empty => fifo_o_empty,
		full => ctrl_o.fifo_o_full,
		rdclk => ifclk,
		rden => fifo_o_rden,
		do => fifo_o_do,
		wrclk => clk0,
		wren => ctrl_i.fifo_o_wren,
		di => ctrl_i.fifo_o_di
	);

	ddr_model: ddr port map (
		Dq => ddr_dq,
		Dqs => ddr_dqs,
		Addr => ddr_addr,
		Ba => ddr_bank,
		Clk => ddr_clk,
		Clk_n => ddr_clk_n,
		Cke => ddr_cke,
		Cs_n => ddr_cs_n,
		Ras_n => ddr_ras_n,
		Cas_n => ddr_cas_n,
		We_n => ddr_we_n,
		Dm => ddr_dm
	);

	main: process is
		procedure write32(data: vec32) is
		begin
--			message("FIFO: Writing " & to_string_hex(data));
			fifo_i_di <= data;
			fifo_i_wren <= '1';
			wait until rising_edge(ifclk);
			wait for 1 ns;
			fifo_i_wren <= '0';
			fifo_i_di <= (others => '0');
		end procedure write32;

		procedure check32(expected: vec32) is
		begin
--			message("FIFO: Verifying " & to_string_hex(expected));
			if fifo_o_empty = '1' then
				wait until fifo_o_empty = '0';
			end if;
			tb_check(fifo_o_do, expected);
			fifo_o_rden <= '1';
			wait until rising_edge(ifclk);
			wait for 1 ns;
			fifo_o_rden <= '0';
		end procedure check32;

		procedure check_empty is
		begin
			if fifo_o_empty = '0' or ctrl_o.fifo_i_empty = '0' then
				tb_error("ERROR: FIFOs not empty after operation!");
			end if;
		end procedure check_empty;

		variable temp_req  : rcache_i_t;
		variable temp_resp : rcache_o_t;
		variable temp_data : std_logic_vector(31 downto 0);
begin
		rst <= '1';
		wait for 20 ns;
		rst <= '0';
	
		wait for 1 us;
		-- Query the version
		write32(X"10000000");
		check32(X"494E5411");

		-- Wait until DDR is ready
		wait for 20 us;

		for i in 0 to 15 loop
			temp_req.addr := std_logic_vector(to_unsigned(i*16, 23));
			temp_resp := cache_lookup(temp_req);
			message("In iteration " & to_string(i) & " of 15");
	
			temp_data := X"20000000";
			write32(std_logic_vector(unsigned(temp_data) + (i * 16)));
			for j in 0 to 15 loop
				write32(
					X"00" & temp_resp.block_data((j+1) * 24 - 1 downto j * 24)
				);
			end loop;
			wait for 1 us;
			check_empty;
		end loop;

		for i in 1 to 16 loop
			message("In iteration " & to_string(i) & " of 16");
			write32(X"50000042");
			write32(X"bf627931");
			write32(X"3f800053");
			write32(X"3f2743a0");
			write32(X"be0c2617");
			write32(X"bf498e94");
			write32(X"3f19e4b7");
			write32(X"60000000");
			check32(X"00000042");
			check32(X"00000000");
			check32(X"00000000");
			check32(X"00000000");
		end loop;

		tb_report("");
		wait;
	end process main;

	decode : process (ddr_ras_n, ddr_cas_n, ddr_we_n)
		variable command : std_logic_vector(2 downto 0);
	begin
		command := ddr_ras_n & ddr_cas_n & ddr_we_n;
		case command is
			when "000" =>
				cmd <= LOAD_MR;
			when "001" =>
				cmd <= AUTOREFRESH;
			when "010" =>
				cmd <= PRECHARGE;
			when "011" =>
				cmd <= ACTIVE;
			when "100" =>
				cmd <= WRITE;
			when "101" =>
				cmd <= READ;
			when "111" =>
				cmd <= NOP;
			when others =>
				cmd <= UNDEF;
		end case;  
	end process;
end behav;
