-------------------------------------------------------------
-- MSS copyright 2011
--	Filename:  TBCOM5402RX.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 1
--	Date last modified: 4/28/11
-- Inheritance: 	N/A
--
-- description:  Test bench for the COM-5402.
-- Reads a stimulus file named "input.cap" [Must be in the same directory as the ISE project]
-- (in the form of a Wireshark Libpcap file format, network capture)
-- and passes it to the COM5402 IP stack receive path.
--

---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.com5402pkg.all;	-- defines global types, number of TCP streams, etc
 
ENTITY tbcom5402rx IS
END tbcom5402rx;
 
ARCHITECTURE behavior OF tbcom5402rx IS 
 
    -- Component Declaration for the Unit Under Test (UUT)
 
	COMPONENT COM5402
	GENERIC (
		CLK_FREQUENCY: integer;
		SIMULATION: std_logic
	);	
	PORT(
		CLK : IN std_logic;
		ASYNC_RESET : IN std_logic;
		MAC_ADDR : IN std_logic_vector(47 downto 0);
		IPv4_ADDR : IN std_logic_vector(31 downto 0);
		IPv6_ADDR : IN std_logic_vector(127 downto 0);
		SUBNET_MASK: in std_logic_vector(31 downto 0);
		GATEWAY_IP_ADDR: in std_logic_vector(31 downto 0);
		MAC_TX_CTS : IN std_logic;
		MAC_RX_DATA : IN std_logic_vector(7 downto 0);
		MAC_RX_DATA_VALID : IN std_logic;
		MAC_RX_SOF : IN std_logic;
		MAC_RX_EOF : IN std_logic;          
		MAC_TX_DATA : OUT std_logic_vector(7 downto 0);
		MAC_TX_DATA_VALID : OUT std_logic;
		MAC_TX_EOF : OUT std_logic;
		UDP_RX_DATA: out std_logic_vector(7 downto 0);
		UDP_RX_DATA_VALID: out std_logic;
		UDP_RX_SOF: out std_logic;	
		UDP_RX_EOF: out std_logic;	
		UDP_RX_DEST_PORT_NO: in std_logic_vector(15 downto 0);
		UDP_TX_DATA : IN std_logic_vector(7 downto 0);
		UDP_TX_DATA_VALID : IN std_logic;
		UDP_TX_SOF : IN std_logic;
		UDP_TX_EOF : IN std_logic;
		UDP_TX_CTS: out std_logic;	
		UDP_TX_ACK: out std_logic;	
		UDP_TX_NAK: out std_logic;	
		UDP_TX_DEST_IP_ADDR: in std_logic_vector(127 downto 0);
		UDP_TX_DEST_PORT_NO: in std_logic_vector(15 downto 0);
		UDP_TX_SOURCE_PORT_NO: in std_logic_vector(15 downto 0);
		TCP_RX_DATA: out SLV8xNTCPSTREAMStype;
		TCP_RX_DATA_VALID: out std_logic_vector((NTCPSTREAMS-1) downto 0);
		TCP_RX_RTS: out std_logic;
		TCP_RX_CTS: in std_logic;
		TCP_TX_DATA: in SLV8xNTCPSTREAMStype;
		TCP_TX_DATA_VALID: in std_logic_vector((NTCPSTREAMS-1) downto 0);
		TCP_TX_CTS: out std_logic_vector((NTCPSTREAMS-1) downto 0);	
		CS1 : OUT std_logic_vector(7 downto 0);
		CS1_CLK : OUT std_logic;
		CS2 : OUT std_logic_vector(7 downto 0);
		CS2_CLK : OUT std_logic;
		TP : OUT std_logic_vector(10 downto 1)
		);
	END COMPONENT;
    

   --Inputs
   signal CLK : std_logic := '0';
   signal ASYNC_RESET : std_logic := '0';
   signal MAC_ADDR : std_logic_vector(47 downto 0) := x"000102030405";
		-- must match Wireshark capture file
   signal IPv4_ADDR : std_logic_vector(31 downto 0) := x"AC100180";
		-- must match Wireshark capture file
   signal GATEWAY_IP_ADDR : std_logic_vector(31 downto 0) := x"AC100103";
		-- must match Wireshark capture file
   signal IPv6_ADDR : std_logic_vector(127 downto 0) := x"0123456789ABCDEF00112233AC100180";
   signal MAC_TX_CTS : std_logic := '1';
   signal MAC_RX_DATA : std_logic_vector(7 downto 0) := (others => '0');
   signal MAC_RX_DATA_VALID : std_logic := '0';
   signal MAC_RX_SOF : std_logic := '0';
   signal MAC_RX_EOF : std_logic := '0';
   signal MAC_RX_DATA0 : std_logic_vector(7 downto 0) := (others => '0');
   signal MAC_RX_DATA_VALID0 : std_logic := '0';
   signal MAC_RX_SOF0 : std_logic := '0';
   signal MAC_RX_EOF0 : std_logic := '0';
	signal TCP_TX_DATA: SLV8xNTCPSTREAMStype := (others => (others => '0'));
	signal TCP_TX_DATA_VALID: std_logic_vector((NTCPSTREAMS-1) downto 0) := (others => '0');

 	--Outputs
   signal MAC_TX_DATA : std_logic_vector(7 downto 0);
   signal MAC_TX_DATA_VALID : std_logic;
   signal MAC_TX_EOF : std_logic;
   signal CS1 : std_logic_vector(7 downto 0);
   signal CS1_CLK : std_logic;
   signal CS2 : std_logic_vector(7 downto 0);
   signal CS2_CLK : std_logic;
   signal TP : std_logic_vector(10 downto 1);
	signal TCP_RX_CTS_E: std_logic;
	signal TCP_RX_CTS: std_logic;
	signal TCP_TX_CTS: std_logic_vector((NTCPSTREAMS-1) downto 0);
	signal TCP_TX_DATA_E: SLV8xNTCPSTREAMStype;
	signal TCP_TX_DATA_VALID_E: std_logic_vector((NTCPSTREAMS-1) downto 0);
	
	-- udp tx
	signal UDP_TX_DATA: std_logic_vector(7 downto 0) := x"00";
	signal UDP_TX_DATA_VALID: std_logic := '0';
	signal UDP_TX_SOF: std_logic := '0';
	signal UDP_TX_EOF: std_logic := '0';
	signal UDP_TX_CTS: std_logic := '0';
	signal UDP_TX_TRIGGER_TOGGLE: std_logic := '0';
	signal UDP_TX_TRIGGER_TOGGLE_D: std_logic := '0';
	signal UDP_TX_TRIGGER_TOGGLE_D2: std_logic := '0';


   -- Clock period definitions
   constant CLK_period : time := 10 ns;
	
	constant SIM_LENGTH: integer := 100000;	-- simulation length expressed in number of bytes read from the libpcap stimulus file
	
	type char_file is file of character; -- one byte each
	file libpcap_file: char_file;
	constant file_name: string := "input.cap";	-- file must be located in same directory as this testbench.
	signal byteCount1: std_logic_vector(7 downto 0) := x"00";
	signal byteCount2: std_logic_vector(15 downto 0) := x"0000";
	signal messageLength: std_logic_vector(31 downto 0) := x"00000000";
--	signal byteRead: std_logic_vector(7 downto 0);
	
 
BEGIN

-- read and decode a libpcap file
	process is
		variable c : character;
		variable byteRead: std_logic_vector(7 downto 0) := x"00";
		
	begin
		file_open(libpcap_file, file_name, read_mode);
		
		for i in 0 to SIM_LENGTH loop
			if(not endfile(libpcap_file)) then
				read(libpcap_file, c);
			else
				c := 'z';	-- continue simulation past the end of the stimulus file.
			end if;
			byteRead := std_logic_vector(to_unsigned(character'pos(c),8));
			if(byteCount1 < 24) then
				-- global header
				byteCount1 <= byteCount1 + 1;
				byteCount2 <= (others => '0');
				wait for 0.01ns;
			else
				-- packet
				if(byteCount2 < 4) then
					-- timestamp seconds
					MAC_RX_EOF0 <= '0';	-- clear EOF flag
					MAC_RX_DATA_VALID0 <= '0';
					byteCount2 <= byteCount2 + 1;
				elsif(byteCount2 < 8) then
					-- timetamp microseconds
					byteCount2 <= byteCount2 + 1;
				elsif(byteCount2 < 12) then
					-- packet length
					messageLength(23 downto 0) <= messageLength(31 downto 8);
					messageLength(31 downto 24) <= std_logic_vector(to_unsigned(character'pos(c),8));	-- LSB first
					byteCount2 <= byteCount2 + 1;
				elsif(byteCount2 < 16) then
					-- actual packet length on network
					byteCount2 <= byteCount2 + 1;
				else
					-- packet data
					MAC_RX_DATA0 <= byteRead;
					if(byteCount2 = 16) then
						MAC_RX_SOF0 <= '1';
						MAC_RX_DATA_VALID0 <= '1';
						byteCount2 <= byteCount2 + 1;
					elsif(byteCount2 = messageLength + 15) then
						-- reached end of packet, reset counter
						MAC_RX_EOF0 <= '1';
						byteCount2 <= (others => '0');	
					else
						MAC_RX_SOF0 <= '0';
						byteCount2 <= byteCount2 + 1;
					end if;
				end if;
				CLK <= '0';
				wait for CLK_period/2;
				CLK <= '1';
				wait for CLK_period/2;
			end if;
			
		end loop;
		
	end process;
	
	-- reclock data for clean interface
	RECLOCK_001: process(CLK)
	begin
		if rising_edge(CLK) then
			MAC_RX_DATA <= MAC_RX_DATA0;
			MAC_RX_DATA_VALID <= MAC_RX_DATA_VALID0;
			MAC_RX_SOF <= MAC_RX_SOF0;
			MAC_RX_EOF <= MAC_RX_EOF0;
		end if;
	end process;
	
 
	-- Instantiate the Unit Under Test (UUT)
   uut: COM5402 
	GENERIC MAP(
		CLK_FREQUENCY => 100,	-- 100 MHz clock (10ns simulation period), use for time and frequency
		SIMULATION => '1'
	)
	PORT MAP (
          CLK => CLK,
          ASYNC_RESET => ASYNC_RESET,
			 MAC_ADDR => MAC_ADDR,
			 IPv4_ADDR => IPv4_ADDR,
			 IPv6_ADDR => IPv6_ADDR,
			 SUBNET_MASK => x"FFFFFF00",
			 GATEWAY_IP_ADDR => GATEWAY_IP_ADDR,
          MAC_TX_DATA => MAC_TX_DATA,
          MAC_TX_DATA_VALID => MAC_TX_DATA_VALID,
          MAC_TX_EOF => MAC_TX_EOF,
          MAC_TX_CTS => MAC_TX_CTS,
          MAC_RX_DATA => MAC_RX_DATA,
          MAC_RX_DATA_VALID => MAC_RX_DATA_VALID,
          MAC_RX_SOF => MAC_RX_SOF,
          MAC_RX_EOF => MAC_RX_EOF,
			 -- UDP rx
			 UDP_RX_DATA => open,
			 UDP_RX_DATA_VALID => open,
			 UDP_RX_SOF => open,
			 UDP_RX_EOF => open,
			 UDP_RX_DEST_PORT_NO => x"0089",
			-- UDP tx
			UDP_TX_DATA => UDP_TX_DATA,
			UDP_TX_DATA_VALID => UDP_TX_DATA_VALID,
			UDP_TX_SOF => UDP_TX_SOF,
			UDP_TX_EOF => UDP_TX_EOF,
			UDP_TX_CTS => UDP_TX_CTS,
			UDP_TX_ACK => open,
			UDP_TX_NAK => open,
			UDP_TX_DEST_IP_ADDR => x"000000000000000000000000AC10015f",
			UDP_TX_DEST_PORT_NO => x"1234",
			UDP_TX_SOURCE_PORT_NO => x"5678",
			-- TCP rx
			 TCP_RX_DATA => open,
			 TCP_RX_DATA_VALID => open,
			 TCP_RX_RTS => open,
			 TCP_TX_DATA => TCP_TX_DATA,
			 TCP_TX_DATA_VALID => TCP_TX_DATA_VALID,
			 TCP_TX_CTS => TCP_TX_CTS,
			 TCP_RX_CTS => TCP_RX_CTS,
          CS1 => CS1,
          CS1_CLK => CS1_CLK,
          CS2 => CS2,
          CS2_CLK => CS2_CLK,
          TP => TP
        );


-- send periodic messages
UDP_TX_send: process
begin
	UDP_TX_TRIGGER_TOGGLE <= not UDP_TX_TRIGGER_TOGGLE;
	wait for 5000ns;
end process;
	

-- test code to send a UDP frame packet once every ... 
UDP_TX_testsim: process(CLK, UDP_TX_TRIGGER_TOGGLE)
begin
	if rising_edge(CLK) then
		UDP_TX_TRIGGER_TOGGLE_D <= UDP_TX_TRIGGER_TOGGLE;
		UDP_TX_TRIGGER_TOGGLE_D2 <= UDP_TX_TRIGGER_TOGGLE_D;
		
		if(UDP_TX_CTS = '1') then
			if(UDP_TX_TRIGGER_TOGGLE_D2 /=  UDP_TX_TRIGGER_TOGGLE_D) then	-- periodic pulse
				UDP_TX_DATA_VALID <= '1';
				UDP_TX_SOF <= '1';
				UDP_TX_EOF <= '0';
				UDP_TX_DATA <= x"00";
			elsif(UDP_TX_DATA > x"10") then
				-- end of udp frame
				UDP_TX_EOF <= '0';
				UDP_TX_DATA_VALID <= '0';
				UDP_TX_SOF <= '0';
				UDP_TX_EOF <= '0';
			else
				UDP_TX_DATA <= UDP_TX_DATA + 1;
				UDP_TX_SOF <= '0';
				if(UDP_TX_DATA = x"10") then
					UDP_TX_EOF <= '1';
				else
					UDP_TX_EOF <= '0';
				end if;
			end if;
		else
			UDP_TX_DATA_VALID <= '0';
			UDP_TX_SOF <= '0';
			UDP_TX_EOF <= '0';
		end if;
	end if;
end process;


process
begin
	TCP_RX_CTS_E <= '0';
	wait for 50 ns;
	TCP_RX_CTS_E <= '1';	-- read stream#1
	wait for 10 ns;
end process;


process
begin
	TCP_TX_DATA_E(1) <= x"00";
	TCP_TX_DATA_VALID_E <= "00";
	wait for 10 ns;
--	wait for 5000 ns;
	TCP_TX_DATA_E(1) <= x"40";
	TCP_TX_DATA_VALID_E <= "10";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"39";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"39";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"39";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"41";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"42";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"43";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"0D";
	wait for 10 ns;
	TCP_TX_DATA_E(1) <= x"0A";
	wait for 10 ns;
	TCP_TX_DATA_VALID_E <= "00";
	wait;
end process;	

-- data stream is TCP stream #0
-- tx throughput test on stream #0
LAN_TX_GEN: process(CLK)
begin
	if rising_edge(CLK) then
		if(TCP_TX_CTS(0) = '1') then
			TCP_TX_DATA(0) <= TCP_TX_DATA(0) + 1;
			TCP_TX_DATA_VALID(0) <= '1';
		else
			TCP_TX_DATA_VALID(0) <= '0';
		end if;
	end if;
end process;



-- make input signals clock synchronous
RECLOCK_002: process(CLK, TCP_RX_CTS_E, TCP_TX_DATA_E)
begin
	if rising_edge(CLK) then
		TCP_RX_CTS <= TCP_RX_CTS_E;
		TCP_TX_DATA(1) <= TCP_TX_DATA_E(1);
		TCP_TX_DATA_VALID(1) <= TCP_TX_DATA_VALID_E(1);
	end if;
end process;

END;
