LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;
USE IEEE.MATH_REAL.ALL;
use std.textio.all; --  Imports the standard textio package.

use work.emac_types.all;
use work.utils_pkg.all;

ENTITY pkt_gen IS
	Generic
	(
		f_sysclk		: real		:= 100.0;
		TX_RAM_SIZE		: natural	:= 2048;
		PKT_SIZE_MIN	: natural	:= 64;
		PKT_SIZE_MAX	: natural	:= 1518
		
	);
	Port
	(
		clk				: in  STD_LOGIC;
		rst				: in  STD_LOGIC;
		en				: in  STD_LOGIC;
		gigabit_en		: in  STD_LOGIC;
		fcs_gen_en		: in  STD_LOGIC;
		mii_tx_clk		: in  STD_LOGIC;
		mii_tx_en		: out STD_LOGIC;
		mii_tx_er		: out STD_LOGIC;
		mii_tx			: out unsigned(7 downto 0)
		
	);
END pkt_gen;

ARCHITECTURE behavior OF pkt_gen IS

	subtype word_ptr_t is unsigned(15 downto 0);

	signal pkt_data			: unsigned(31 downto 0);
	signal pkt_nbytes		: word_ptr_t := X"0040";
	signal pkt_nwords		: word_ptr_t := X"0010";
	
	signal fill_size		: word_ptr_t;
	signal fill_cnt			: word_ptr_t;
	signal fill_rdy			: std_logic;
	signal fill_set			: std_logic;
	signal fill_en			: std_logic;

	signal tx_packets		: natural;
	signal tx_bytes			: natural;
	
	signal tx_ctrl_in		: tx_ctrl_in_t;
	signal tx_ctrl_out		: tx_ctrl_out_t;
	signal tx_din			: unsigned(31 downto 0);
	signal tx_din_vld		: std_logic;
	
	type host_state_t is (host_init, host_idle, host_alloc, host_setup, host_arm, host_fill, host_commit);
	signal host_s, host_sn : host_state_t;
	
begin

------------------------------------------------------------------
host_state_next:
	process(clk)
	begin
		if rising_edge(clk) then
			if rst = '1' then
				host_s	<= host_init;
			else
				host_s	<= host_sn;
			end if;
		end if;
	end process;	

host_state:
	process(host_s, tx_ctrl_out, pkt_nbytes, fill_rdy, en, gigabit_en, fcs_gen_en)
	begin
	
		tx_ctrl_in.pkt_alloc_en		<= '0';
		tx_ctrl_in.pkt_commit_en	<= '0';
		tx_ctrl_in.reset			<= '0';
		tx_ctrl_in.Gbps_en			<= gigabit_en;
		tx_ctrl_in.fcs_gen_en		<= fcs_gen_en;
		tx_ctrl_in.tx_size			<= pkt_nbytes;
		fill_set					<= '0';
		fill_en						<= '0';
		
		host_sn						<= host_s;
		
		case host_s is
		
		when host_init =>
			if tx_ctrl_out.reset_busy = '0' then
				host_sn 		<= host_idle;
			end if;
		
		when host_idle =>
			if tx_ctrl_out.pkt_alloc_req = '0' and en = '1' then
				tx_ctrl_in.pkt_alloc_en		<= '1';
				host_sn		<= host_alloc;
			end if;

		when host_alloc =>
			if tx_ctrl_out.pkt_alloc_req = '0' then
				if tx_ctrl_out.pkt_armed = '1' then
					host_sn		<= host_setup;
				else
					host_sn 	<= host_idle;
				end if;
			end if;

		when host_setup =>
			fill_set		<= '1';
			host_sn			<= host_fill;

		when host_fill =>
			fill_en			<= '1';
			if fill_rdy = '1' then
				host_sn		<= host_commit;
			end if;
		
		when host_commit =>
			tx_ctrl_in.pkt_commit_en	<= '1';
			host_sn			<= host_idle;

		when others => 
			host_sn			<= host_idle;
		end case;

	end process;

------------------------------------------------------------------
pkt_generator:
	process(clk)
	variable size		: word_ptr_t;
	variable krand		: real;
	variable seed1		: integer;
	variable seed2		: integer;
	
	begin
		if rising_edge(clk) then
			if rst = '1' then
				size		:= to_unsigned(PKT_SIZE_MIN, word_ptr_t'length);
				seed1		:= 31101970;
				seed2		:= 12586901;
				tx_packets	<= 0;
				tx_bytes	<= 0;
			elsif tx_ctrl_in.pkt_commit_en = '1' then
				uniform(seed1, seed2, krand);
				size		:= to_unsigned(PKT_SIZE_MIN + natural(real(PKT_SIZE_MAX-PKT_SIZE_MIN)*krand), word_ptr_t'length);
				tx_packets	<= tx_packets + 1;
				tx_bytes	<= tx_bytes + to_integer(pkt_nbytes);
			end if;
			pkt_nbytes	<= size;
			if size(1 downto 0) = 0 then
				pkt_nwords	<= "00" & size(word_ptr_t'left downto 2);
			else	
				pkt_nwords	<= "00" & size(word_ptr_t'left downto 2) + 1;
			end if;
		end if;
	end process;

------------------------------------------------------------------
pkt_data_gen:
	process(clk)
	variable data : unsigned(7 downto 0);
	begin
		if rising_edge(clk) then
			tx_din_vld	<= fill_en;
			if fill_set = '1' then
				data	:= X"00";
				tx_din	<= X"03020100";
			elsif fill_en = '1' then
				tx_din(7 downto 0)		<= data;
				data					:= data + 1;
				tx_din(15 downto 8)		<= data;
				data					:= data + 1;
				tx_din(23 downto 16)	<= data;
				data					:= data + 1;
				tx_din(31 downto 24)	<= data;
				data					:= data + 1;
			end if;
		end if;	
			
	end process;	

------------------------------------------------------------------
pkt_data_counter:
	process(clk)
	begin
		if rising_edge(clk) then
			if fill_set = '1' then
				fill_size		<= (others => '0');
				fill_rdy		<= '0';
				fill_cnt 		<= pkt_nwords;
			elsif fill_en = '1' then
				if fill_cnt /= 0 then
					fill_cnt	<= fill_cnt - 1;
					fill_size	<= fill_size + 1;
				else
					fill_rdy	<= '1';
				end if;
			end if;
		end if;	
			
	end process;	
	
------------------------------------------------------------------
inst_emac_tx : entity work.emac_tx
	GENERIC MAP
	(
		f_sysclk	=> f_sysclk,
		RAM_SIZE	=> TX_RAM_SIZE
	)
	PORT MAP
	(
		clk			=> clk,
		rst			=> rst,
		din_vld		=> tx_din_vld,
		din			=> tx_din,
		ctrl_in		=> tx_ctrl_in,
		ctrl_out	=> tx_ctrl_out,
		mii_tx_clk	=> mii_tx_clk,
		mii_tx_en	=> mii_tx_en,
		mii_tx_er	=> mii_tx_er,
		mii_tx		=> mii_tx
		
	);


end behavior;
