-------------------------------------------------------------------------
-- Project: JCPU, a portable 8-bit RISC CPU written in VHDL
-- This file: testbench for system test using Xilinx ML-402

-- Copyright (C) 2007 J. Ahrensfeld

-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.

-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-- Lesser General Public License for more details.

-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

-- For questions and ideas, please contact the author at jens@jayfield.org

-----------------------------------------------------------------------

LIBRARY ieee;
use IEEE.STD_LOGIC_1164.ALL;
USE ieee.numeric_std.ALL;

ENTITY tb_emac_top_jb IS
END tb_emac_top_jb;

ARCHITECTURE behavior OF tb_emac_top_jb IS 

	constant CLK_PERIOD			: time := 10 ns;
	constant MII_CLK_PERIOD		: time := 37.7 ns;
	constant GMII_CLK_PERIOD	: time := 8.0 ns;

	signal CLK					: std_logic := '1';
	signal RST					: std_logic := '1';

	-- Slave
	signal CYC_I				: std_logic := '0';
	signal STB_I				: std_logic := '0';
	signal WE_I					: std_logic := '0';
	signal SEL_I				: unsigned(3 downto 0) := (others => '1');
	signal ACK_O				: std_logic;
	signal INT_O				: std_logic;
	signal MRDY_I				: std_logic := '0';
	signal SRDY_O				: std_logic;
	signal ADDR_I				: unsigned(31 downto 0) := (others => '-');
	signal DAT_I				: unsigned(31 downto 0) := (others => '-');
	signal DAT_O				: unsigned(31 downto 0) := (others => '-');
	signal DAT_O_reg			: unsigned(31 downto 0) := (others => '-');
	signal status_reg			: unsigned(31 downto 0) := (others => '0');
	signal size_reg				: unsigned(31 downto 0) := (others => '0');

	-- MII signals
	signal mii_rx_clk_board		: std_logic := '1';
	signal mii_tx_clk_board		: std_logic := '1';
	signal mii_rx_clk			: std_logic := '1';
	signal mii_tx_clk			: std_logic := '1';
	signal mii_rx				: unsigned(7 downto 0) := (others => '-');
	signal mii_tx				: unsigned(7 downto 0);
	signal mii_rx_dv			: std_logic := '0';
	signal mii_rx_er			: std_logic := '0';
	signal mii_tx_en			: std_logic;
	signal mii_tx_er			: std_logic;
	signal mii_crs				: std_logic := '0';
	signal mii_col				: std_logic := '0';
	signal mii_gtx_clk			: std_logic := '0';

	signal loop_back_en			: std_logic := '0';
	signal gigabit_rx_en		: std_logic := '0';
	signal gigabit_tx_en		: std_logic := '0';
	
	signal pktgen_tx			: unsigned(7 downto 0);
	signal pktgen_tx_en			: std_logic;
	signal pktgen_tx_er			: std_logic;
	signal pktgen_en			: std_logic := '0';
	signal pktgen_gigabit		: std_logic := '0';
	signal pktgen_fcs_en		: std_logic := '0';
	signal dat_o_cnt_en			: std_logic := '0';
	signal dat_o_cnt_rst		: std_logic := '1';
	signal dat_o_cnt			: natural;
	signal pkt_count			: natural;
	
	type emac_action_t is (emac_idle, emac_alloc, emac_write, emac_commit, emac_wait_data, emac_read, emac_free);
	signal emac_action	: emac_action_t;
	
	constant RX_STATUS_REG_ADDR	: unsigned(31 downto 0) := X"0000_0000";
	constant RX_SIZE_REG_ADDR	: unsigned(31 downto 0) := X"0000_0004";
	constant TX_STATUS_REG_ADDR	: unsigned(31 downto 0) := X"0000_0008";
	constant TX_SIZE_REG_ADDR	: unsigned(31 downto 0) := X"0000_000C";
	constant DATA_REG_ADDR		: unsigned(31 downto 0) := X"0000_8000";

BEGIN

inst_emac_top_jb : entity work.emac_top_jb
	GENERIC MAP
	(
		f_sysclk	=> 100.0,
		RX_RAM_SIZE	=> 2048,
		TX_RAM_SIZE	=> 2048
	)
	PORT MAP
	(
		CLK_I		=> CLK,
		RST_I		=> RST,
		INT_O		=> INT_O,
		CYC_I		=> CYC_I,
		STB_I		=> STB_I,
		SEL_I		=> SEL_I,
		WE_I		=> WE_I,
		ACK_O		=> ACK_O,
		SRDY_O		=> SRDY_O,
		MRDY_I		=> MRDY_I,
		ADDR_I		=> ADDR_I,
		DAT_I		=> DAT_I,
		DAT_O		=> DAT_O,
		mii_rx_clk	=> mii_rx_clk_board,
		mii_rx_dv	=> mii_rx_dv,
		mii_rx_er	=> mii_rx_er,
		mii_rx		=> mii_rx,
		mii_tx_clk	=> mii_tx_clk_board,
		mii_tx_en	=> mii_tx_en,
		mii_tx_er	=> mii_tx_er,
		mii_tx		=> mii_tx,
		mii_gtx_clk	=> mii_gtx_clk,
		mii_crs		=> mii_crs,
		mii_col		=> mii_col
		
	);

inst_pkt_gen : entity work.pkt_gen
	GENERIC MAP
	(
		f_sysclk		=> 100.0,
		PKT_SIZE_MIN	=> 64,
		PKT_SIZE_MAX	=> 1512
		
	)
	PORT MAP
	(
		clk			=> CLK,
		rst			=> RST,
		en			=> pktgen_en,
		gigabit_en	=> pktgen_gigabit,
		fcs_gen_en	=> pktgen_fcs_en,
		mii_tx_clk	=> mii_tx_clk_board,
		mii_tx_en	=> pktgen_tx_en,
		mii_tx_er	=> pktgen_tx_er,
		mii_tx		=> pktgen_tx
		
	);


CLK_GEN: process
	begin
		wait for CLK_PERIOD/2;
		CLK <= not CLK;
	end process;

MII_CLK_GEN: process
	begin
		wait for MII_CLK_PERIOD/2;
		mii_rx_clk <= not mii_rx_clk;
		mii_tx_clk <= not mii_tx_clk;
	end process;

GMII_CLK_GEN: process
	begin
		wait for GMII_CLK_PERIOD/2;
		mii_gtx_clk <= not mii_gtx_clk;
	end process;

	mii_rx_clk_board <= mii_gtx_clk when gigabit_rx_en = '1' else mii_rx_clk;
	mii_tx_clk_board <= mii_gtx_clk when gigabit_tx_en = '1' else mii_tx_clk;

DAT_O_register:
	process(CLK)
	begin
		if rising_edge(CLK) then
			if ACK_O = '1' then
				DAT_O_reg	<= DAT_O;
			end if;
		end if;
	end process;
	
DAT_O_count_register:
	process(CLK)
	begin
		if rising_edge(CLK) then
			if dat_o_cnt_rst = '1' then
				dat_o_cnt <= 0;
			elsif ACK_O = '1' and dat_o_cnt_en = '1' then
				dat_o_cnt	<= dat_o_cnt + 1;
			end if;
		end if;
	end process;

	mii_rx		<= mii_tx when loop_back_en = '1' else pktgen_tx;
	mii_rx_dv	<= mii_tx_en when loop_back_en = '1' else pktgen_tx_en;
	mii_rx_er	<= mii_tx_er when loop_back_en = '1' else pktgen_tx_er;
	mii_crs		<= '0';
	mii_col		<= '0';
	
-- Master
STIMULUS: process

	procedure emac_tx_reset_100mbps  is
	begin
	
		gigabit_tx_en	<= '0';
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= X"A000_0000";
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= TX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		WE_I	<= '0';

	end procedure emac_tx_reset_100mbps;

	procedure emac_rx_reset_100mbps  is
	begin
	
		gigabit_rx_en	<= '0';
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= X"A000_0000";
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= RX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		WE_I	<= '0';

	end procedure emac_rx_reset_100mbps;

	procedure emac_tx_reset_1000mbps  is
	begin
	
		gigabit_tx_en	<= '1';
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= X"E000_0000";
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= TX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		WE_I	<= '0';

	end procedure emac_tx_reset_1000mbps;

	procedure emac_rx_reset_1000mbps  is
	begin
	
		gigabit_rx_en	<= '1';
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= X"E000_0000";
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= RX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		WE_I	<= '0';

	end procedure emac_rx_reset_1000mbps;

	procedure emac_alloc (size : natural) is
	begin
	
		-- TX-ALLOCATION
		-- set TX-size
		emac_action	<= emac_alloc;
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= to_unsigned(size, 32);
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= TX_SIZE_REG_ADDR;

	check_alloc_ok:
		while(true) loop

			-- set alloc request
			wait until rising_edge(CLK) and SRDY_O = '1';
			WE_I	<= '0';
			STB_I	<= '1';
			ADDR_I	<= TX_STATUS_REG_ADDR;
			wait until rising_edge(CLK) and SRDY_O = '1';
			STB_I	<= '0';
			wait until rising_edge(CLK) and ACK_O = '1';
			status_reg	<= DAT_O;
			wait until rising_edge(CLK) and SRDY_O = '1';
			DAT_I	<= X"0002_0000" or (not X"0001_0000" and status_reg);
			STB_I	<= '1';
			WE_I	<= '1';
			ADDR_I	<= TX_STATUS_REG_ADDR;

		check_bsy:
			while (true) loop
				wait until rising_edge(CLK) and SRDY_O = '1';
				WE_I	<= '0';
				STB_I	<= '1';
				ADDR_I	<= TX_STATUS_REG_ADDR;
				wait until rising_edge(CLK) and SRDY_O = '1';
				STB_I	<= '0';
				wait until rising_edge(CLK) and ACK_O = '1';
				status_reg	<= DAT_O;
				wait until rising_edge(CLK);
				if status_reg(17) = '0' then
					exit check_bsy;
				end if;
			end loop;
			if status_reg(16) = '1' then
				exit check_alloc_ok;
			end if;
		end loop;
		CYC_I	<= '0';
		
		emac_action	<= emac_idle;

	end procedure emac_alloc;
	
	procedure emac_commit  is
	begin
	
		-- RX- DEALLOCATION
		emac_action	<= emac_commit;

		-- set free request
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		WE_I	<= '0';
		STB_I	<= '1';
		ADDR_I	<= TX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		wait until rising_edge(CLK) and ACK_O = '1';
		status_reg	<= DAT_O;
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= X"0001_0000" or (not X"0002_0000" and status_reg);
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= TX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		WE_I	<= '0';

		emac_action	<= emac_idle;

	end procedure emac_commit;

	procedure emac_write (size : natural) is
	variable data : unsigned (7 downto 0);
	variable num_words : natural;
	begin

		-- FILL TX data
		emac_action	<= emac_write;
		if (size mod 4) = 0 then
			num_words	:= size/4;
		else
			num_words	:= size/4 + 1;
		end if;
		
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		data	:= X"00";
		ADDR_I	<= DATA_REG_ADDR;
		WE_I	<= '1';
		for i in 1 to num_words loop
			STB_I	<= '1';
			DAT_I(7 downto 0)	<= data;
			data				:= data + 1;
			DAT_I(15 downto 8)	<= data;
			data				:= data + 1;
			DAT_I(23 downto 16)	<= data;
			data				:= data + 1;
			DAT_I(31 downto 24)	<= data;
			data				:= data + 1;
			wait until rising_edge(CLK) and SRDY_O = '1';
		end loop;

		STB_I	<= '0';
		wait until rising_edge(CLK) and SRDY_O = '1';

 		emac_commit;

		emac_action	<= emac_idle;

	end procedure emac_write;

	procedure emac_read (size_in : natural) is
	variable data : unsigned (7 downto 0);
	variable size, num_words : natural;
	begin

		emac_action	<= emac_wait_data;

		-- check if data available
	check_data_avail:
		while (true) loop
			CYC_I	<= '1';
			wait until rising_edge(CLK) and SRDY_O = '1';
			WE_I	<= '0';
			STB_I	<= '1';
			ADDR_I	<= RX_STATUS_REG_ADDR;
			wait until rising_edge(CLK) and SRDY_O = '1';
			STB_I	<= '0';
			wait until rising_edge(CLK) and ACK_O = '1';
			status_reg	<= DAT_O;
			wait until rising_edge(CLK);
			if status_reg(17) = '1' and status_reg(16) = '1' then
				exit check_data_avail;
			end if;
		end loop;

	read_size:
		dat_o_cnt_rst <= '1';
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		WE_I	<= '0';
		STB_I	<= '1';
		ADDR_I	<= RX_SIZE_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		wait until rising_edge(CLK) and ACK_O = '1';
		size_reg	<= DAT_O;
		wait until rising_edge(CLK);

		-- set packet request
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		WE_I	<= '0';
		STB_I	<= '1';
		ADDR_I	<= RX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		wait until rising_edge(CLK) and ACK_O = '1';
		status_reg	<= DAT_O;
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= X"0002_0000" or (not X"0001_0000" and status_reg);
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= RX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		WE_I	<= '0';

		emac_action	<= emac_read;
		-- Read RX data
		size := size_in;
		if (size_in = 0) then
			size	:= to_integer(size_reg(15 downto 0));
		end if;
		
		if (size mod 4) = 0 then
			num_words	:= size/4;
		else
			num_words	:= size/4 + 1;
		end if;
		
		dat_o_cnt_rst <= '0';
		dat_o_cnt_en <= '1';
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		ADDR_I	<= DATA_REG_ADDR;
		WE_I	<= '0';
		STB_I	<= '1';
		for i in 1 to num_words loop
			wait until rising_edge(CLK) and SRDY_O = '1';
		end loop;
		STB_I	<= '0';
		wait until rising_edge(CLK) and dat_o_cnt = num_words;
		dat_o_cnt_en <= '0';
		emac_action	<= emac_idle;

	end procedure emac_read;

	procedure emac_free  is
	begin
	
		-- RX- DEALLOCATION
		emac_action	<= emac_free;

		-- set free request
		CYC_I	<= '1';
		wait until rising_edge(CLK) and SRDY_O = '1';
		WE_I	<= '0';
		STB_I	<= '1';
		ADDR_I	<= RX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		wait until rising_edge(CLK) and ACK_O = '1';
		status_reg	<= DAT_O;
		wait until rising_edge(CLK) and SRDY_O = '1';
		DAT_I	<= X"0001_0000" or (not X"0002_0000" and status_reg);
		STB_I	<= '1';
		WE_I	<= '1';
		ADDR_I	<= RX_STATUS_REG_ADDR;
		wait until rising_edge(CLK) and SRDY_O = '1';
		STB_I	<= '0';
		WE_I	<= '0';

		emac_action	<= emac_idle;

	end procedure emac_free;

	begin

		-- 100 Mbps
		wait for 6*MII_CLK_PERIOD;
		RST <= '0';
		wait for 60*CLK_PERIOD;
		emac_rx_reset_100mbps;
		emac_tx_reset_100mbps;
		pktgen_gigabit	<= '0';
		pktgen_fcs_en	<= '1';
		wait for 60*CLK_PERIOD;
		
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);

		for i in 1 to 1 loop
			for j in 1 to 10 loop
		 		emac_alloc (72);
		 		emac_write (72);
			end loop;
			wait for 1 ms;
		end loop;
		
		wait for 60*CLK_PERIOD;

		wait until rising_edge(mii_tx_clk_board) and mii_tx_en = '0';
		
		loop_back_en	<= '1';
		
 		emac_alloc (74);
 		emac_write (74);
 
 		emac_alloc (105);
 		emac_write (105);

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

 		emac_alloc (103);
 		emac_write (103);

		emac_read (0);
		emac_free;

 		emac_alloc (71);
 		emac_write (71);
 
		emac_read (0);
		emac_free;

 		emac_alloc (82);
 		emac_write (82);

 		emac_alloc (86);
 		emac_write (86);

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

 		emac_alloc (77);
 		emac_write (77);

 		emac_alloc (99);
 		emac_write (99);

 		emac_alloc (65);
 		emac_write (65);

 		emac_alloc (321);
 		emac_write (321);

		emac_read (0);
		emac_free;

 		emac_alloc (1111);
 		emac_write (1111);

		emac_read (0);
		emac_free;

 		emac_alloc (73);
 		emac_write (73);

 		emac_alloc (107);
 		emac_write (107);

 		emac_alloc (83);
 		emac_write (83);

		emac_read (0);
		emac_free;

 		emac_alloc (72);
 		emac_write (72);

 		emac_alloc (1003);
 		emac_write (1003);

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		wait for 60*CLK_PERIOD;

		loop_back_en	<= '0';
		pktgen_en		<= '1';
		pkt_count		<= 0;
		for i in 1 to 500 loop
			wait for 1 us;
			emac_read (0);
			emac_free;
			pkt_count	<= pkt_count + 1;
		end loop;
		pktgen_en		<= '0';
		emac_read (0);
		emac_free;
			

		-- 1000 MBps
		RST <= '1';
		wait for 6*MII_CLK_PERIOD;
		RST <= '0';
		wait for 60*CLK_PERIOD;
		emac_rx_reset_1000mbps;
		emac_tx_reset_1000mbps;
		pktgen_gigabit	<= '1';
		pktgen_fcs_en	<= '1';
		wait for 60*CLK_PERIOD;
		
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);
 		emac_alloc (1004);

		for i in 1 to 1 loop
			for j in 1 to 10 loop
		 		emac_alloc (72);
		 		emac_write (72);
			end loop;
			wait for 1 ms;
		end loop;
		
		wait for 60*CLK_PERIOD;

		wait until rising_edge(mii_tx_clk_board) and mii_tx_en = '0';
		
		loop_back_en	<= '1';
		
 		emac_alloc (74);
 		emac_write (74);
 
 		emac_alloc (105);
 		emac_write (105);

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

 		emac_alloc (103);
 		emac_write (103);

		emac_read (0);
		emac_free;

 		emac_alloc (71);
 		emac_write (71);
 
		emac_read (0);
		emac_free;

 		emac_alloc (82);
 		emac_write (82);

 		emac_alloc (86);
 		emac_write (86);

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

 		emac_alloc (77);
 		emac_write (77);

 		emac_alloc (99);
 		emac_write (99);

 		emac_alloc (65);
 		emac_write (65);

 		emac_alloc (321);
 		emac_write (321);

		emac_read (0);
		emac_free;

 		emac_alloc (1111);
 		emac_write (1111);

		emac_read (0);
		emac_free;

 		emac_alloc (73);
 		emac_write (73);

 		emac_alloc (107);
 		emac_write (107);

 		emac_alloc (83);
 		emac_write (83);

		emac_read (0);
		emac_free;

 		emac_alloc (72);
 		emac_write (72);

 		emac_alloc (1003);
 		emac_write (1003);

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		emac_read (0);
		emac_free;

		wait for 60*CLK_PERIOD;

		loop_back_en	<= '0';
		pktgen_en		<= '1';
		pkt_count		<= 0;
		for i in 1 to 500 loop
			wait for 1 us;
			emac_read (0);
			emac_free;
			pkt_count	<= pkt_count + 1;
		end loop;
		pktgen_en		<= '0';
		emac_read (0);
		emac_free;
			
		wait; 
	
	end process;
	
END;
