-------------------------------------------------------------
-- MSS copyright 2011-2012
--	Filename:  tbcom5402udptx.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 2
--	Date last modified: 8/7/12
-- Inheritance: 	N/A
--
-- description:  Test bench for the COM-5402.
-- Emphasis is on UDP tx streaming. Generate a PRBS-11 test sequence, segment it and send it through UDP.
-- The LAN receive side is based on reading a stimulus file named "tbcom5402udptx.cap" 
-- (in the form of a Wireshark Libpcap file format, network capture)
-- and passes it to the COM5402 IP stack receive path.
--
-- Sequence: 
-- 1. start segmenting PRBS11 test data into UDP packets
-- 2. upon the 1st packet being ready, an ARP request is sent out to the network to find out the destination MAC address
-- 3. the .cap stimulus file must include an ARP response, otherwise no UDP packet can be sent out
-- 4. UDP packets are sent out
--
-- Tested with NTCPSTREAMS = 1, NUDPTX = 1, NUDPRX = 1 in com5402pkg.vhd
--
-- Test conditions: 
-- our IP address 172.16.1.129, tx port 1234
-- destination IP address 172.16.1.44, rx port 5678
--
-- Adjust the delay after file_open to start reading the stimulus file.
-- If delay is short (1us), we will receive a packet from the destination before we can send an ARP (thus resolving the destination address).
-- If delay is long (50us), we will send one or more ARP requests to resolve the destination MAC address
--
-- Key ISIM waveforms are saved in tbcom5402udptx.wcfg (open with ISIM)
---------------------------------------------------------------
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 tbcom5402udptx IS
END tbcom5402udptx;
 
ARCHITECTURE behavior OF tbcom5402udptx IS 
 
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT TIMER_4US
	GENERIC (
		CLK_FREQUENCY: integer 
	);
	PORT(
		ASYNC_RESET : IN std_logic;
		CLK : IN std_logic;          
		TICK_4US : OUT std_logic;
		TICK_100MS: out std_logic
		);
	END COMPONENT;
 
	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;

	COMPONENT LFSR11P
	PORT(
		CLK : IN std_logic;
		SYNC_RESET : IN std_logic;
		SAMPLE_CLK_OUT_REQ : IN std_logic;          
		DATA_OUT : OUT std_logic_vector(7 downto 0);
		SAMPLE_CLK_OUT : OUT std_logic;
		SOF_OUT : OUT std_logic
		);
	END COMPONENT;

	COMPONENT STREAM_2_PACKETS
	generic (
		NBUFS: integer;
		SIMULATION: std_logic
	);
	PORT(
		ASYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		TICK_4US : IN std_logic;
		STREAM_DATA : IN std_logic_vector(7 downto 0);
		STREAM_DATA_VALID : IN std_logic;
		CTS_IN : IN std_logic;
		ACK_IN : IN std_logic;
		NAK_IN : IN std_logic;          
		STREAM_CTS : OUT std_logic;
		DATA_OUT : OUT std_logic_vector(7 downto 0);
		DATA_VALID_OUT : OUT std_logic;
		SOF_OUT : OUT std_logic;
		EOF_OUT : OUT std_logic;
		TP : OUT std_logic_vector(10 downto 1)
		);
	END COMPONENT;


--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
   signal CLK : std_logic := '0';
   signal ASYNC_RESET : std_logic := '0';
   signal SYNC_RESET : std_logic := '0';
	signal SYNC_RESET_CNTR: std_logic_vector(3 downto 0) := (others => '0');
	signal TICK_4US: 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"AC100181";
		-- 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"0123456789ABCDEF00112233AC100181";
		-- unused
   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_CTS_B: std_logic := '0';
	signal UDP_TX_ACK: std_logic := '0';	
	signal UDP_TX_NAK: 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';
	
	-- PRBS-11 test sequence
	signal PRBS11_DATA: std_logic_vector(7 downto 0) := x"00";
	signal PRBS11_DATA_VALID: std_logic := '0';
	signal PRBS11_SOF: std_logic := '0';
	signal PRBS11_CTS: std_logic := '0';
	signal STREAM_EN: std_logic := '0';


   -- Clock period definitions
   constant CLK_period : time := 8 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 := ".\sim\tbcom5402udptx.cap";	
	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);
	
 
------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
BEGIN

-- processing clock
process
begin
	CLK <= '0';
	wait for CLK_period/2;
	CLK <= '1';
	wait for CLK_period/2;
end process;

-- sync reset
process
begin
	if rising_edge(CLK) then
		if(SYNC_RESET_CNTR(3) = '0') then
			SYNC_RESET_CNTR <= SYNC_RESET_CNTR + 1;
		end if;
		if(SYNC_RESET_CNTR = "0111") then
			SYNC_RESET <= '1';
		else
			SYNC_RESET <= '0';
		end if;
	end if;
end process;

TIMER_4US_001: TIMER_4US 
GENERIC MAP(
	CLK_FREQUENCY => 120		-- 120 MHz, as time reference
)
PORT MAP(
	ASYNC_RESET => ASYNC_RESET,
	CLK => CLK,
	TICK_4US => TICK_4US,
	TICK_100MS => open
);

--//-- THROUGHPUT & BER TESTS ----------------------
-- Generate a pseudo-random binary sequence PRBS11 (to compute BER at the receiving end)
LFSR11P_001: LFSR11P PORT MAP(
	CLK => CLK,
	SYNC_RESET => ASYNC_RESET,
	DATA_OUT => PRBS11_DATA,
	SAMPLE_CLK_OUT => PRBS11_DATA_VALID,
	SOF_OUT => PRBS11_SOF,
	SAMPLE_CLK_OUT_REQ => PRBS11_CTS
);
	
-- segment data stream into packets 
STREAM_2_PACKETS_001: STREAM_2_PACKETS 
GENERIC MAP(
	NBUFS => 1,
	SIMULATION => '1'
)
PORT MAP(
	ASYNC_RESET => ASYNC_RESET,
	CLK => CLK,
	TICK_4US => TICK_4US,
	STREAM_DATA => PRBS11_DATA,
	STREAM_DATA_VALID => PRBS11_DATA_VALID,
	STREAM_CTS => PRBS11_CTS,
	DATA_OUT => UDP_TX_DATA,
	DATA_VALID_OUT => UDP_TX_DATA_VALID,
	SOF_OUT => UDP_TX_SOF,
	EOF_OUT => UDP_TX_EOF,
	CTS_IN => UDP_TX_CTS_B,
	ACK_IN => UDP_TX_ACK,
	NAK_IN => UDP_TX_NAK,
	TP => open
);

-- enable stream to packet after a delay (after ARP is complete for example)
process
begin
	STREAM_EN <= '0';
	wait for 30000 ns;
	STREAM_EN <= '1';
	wait;
end process;

UDP_TX_CTS_B <= UDP_TX_CTS and STREAM_EN;

-- 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);
		wait for 500000 ns;	-- start delay. Adjust as needed.
		
		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;
				wait for CLK_period;
			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;
	
	-- test. periodically block CTS for a few us.
	BLOCK_MAC_TX_CTS_TEST: process
	begin
		MAC_TX_CTS <= '1';
		wait for 1000 ns;
		MAC_TX_CTS <= '1';
		wait for 1000 ns;
	end process;
		
	
 
	-- Instantiate the Unit Under Test (UUT)
   uut: COM5402 
	GENERIC MAP(
		CLK_FREQUENCY => 125,	-- 125 MHz clock (8ns simulation period), use for time and frequency
		SIMULATION => '1'
	)
	PORT MAP (
          CLK => CLK,
          ASYNC_RESET => ASYNC_RESET,
			 SYNC_RESET => SYNC_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 => UDP_TX_ACK,
			UDP_TX_NAK => UDP_TX_NAK,
			UDP_TX_DEST_IP_ADDR => x"000000000000000000000000AC10012C",	-- to destination IP recognized in .cap capture file
--			UDP_TX_DEST_IP_ADDR => x"000000000000000000000000AC100180", -- to itself
--			UDP_TX_DEST_IP_ADDR => x"000000000000000000000000AC100155", -- to unknown and inactive destination IP
			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
        );





-- Alternative code 
---- 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;


END;
