----------------------------------------------------------------------------------
-- 		Modul pro vysilaci cast linkove vrstvy IEEE802.3 / Ethernet II			  --
----------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

use work.my_constants.all;

entity mac_tx is
PORT (
	-- taktovani 
	TX_CLK: in std_logic;			
	-- GMII 
	GMII_COL: in std_logic;
	GMII_CS: in std_logic;
	GMII_TXD: out std_logic_vector(7 downto 0);
	GMII_TXEN: out std_logic;
	GMII_TXER: out std_logic;

	-- ip vrstva
	TX_DATA: in std_logic_vector(7 downto 0);
	TX_DATA_VLD: in std_logic;
	TX_DATA_ACK: out std_logic;
		
	-- Control / info
	IFG: in std_logic_vector(7 downto 0);
	FULL_DUPLEX: in std_logic;	
	COLLISION: out std_logic;
	
	-- globalni reset 
	RESET: in std_logic
);	
end mac_tx;

architecture Behavioral of mac_tx is

	COMPONENT crc
	PORT(
		DATA_IN : in std_logic_vector (7 downto 0);
		CRC_EN: in std_logic; 
		RESET: in std_logic; 
		CLK: in std_logic;
		CRC_OUT: out std_logic_vector (31 downto 0)
		);
	END COMPONENT;
	
type state_type is (st_idle, st_preamble, st_data, st_crc, st_collision, st_ifg, st_padding, 
						  st_cs, st_ifg_inc);	
						  
signal tx_state, tx_next_state : state_type								:= st_idle; 
-- Pocitadlo bajtu
signal tx_count, tx_count_next: std_logic_vector(5 downto 0) 		:= (others => '0');
signal tx_count_en: std_logic													:= '0';
-- Meziramcova mezera
signal ifg_count, ifg_count_next: std_logic_vector(7 downto 0)		:= (others => '0');
signal ifg_count_en, ifg_count_rst: std_logic							:= '0';
-- Potvrzeni pro ip
signal data_tx_ack_i: std_logic												:= '0';
-- Výstupni signaly 
signal gmii_txd_i: std_logic_vector(7 downto 0)							:= (others => '0');
signal gmii_txen_i, gmii_txer_i: std_logic								:= '0';
-- CRC
signal crc_32: std_logic_vector(31 downto 0)								:= (others => '0');
signal rst_crc, calc_crc: std_logic											:= '0';
-- signaly pro detekce kolize
signal col, cs: std_logic														:= '0';	

begin
	
	----------------------------------------------------------------------	
	-- CRC modul ---------------------------------------------------------
	Inst_crc: crc PORT MAP(
		CLK => TX_CLK,
		DATA_IN => TX_DATA,
		CRC_EN => calc_crc,
		CRC_OUT => crc_32,
		RESET => rst_crc
	);
	
	----------------------------------------------------------------------	
	-- Pocitani bajtu ----------------------------------------------------
	tx_count_next <= (others => '0') when tx_count_en = '0' else 
							tx_count when tx_count = 60 else
							tx_count + 1;
	
	----------------------------------------------------------------------
	-- IFG = nastavitelna mezera mezi ramci ------------------------------
	ifg_count_next <= (others => '0') when ifg_count_rst = '1' else
							 ifg_count + 1 when ifg_count_en = '1' else
							 ifg_count;
	
	----------------------------------------------------------------------
	-- SEKVENCNI CAST ----------------------------------------------------
	----------------------------------------------------------------------
	process (TX_CLK)
	begin
		if rising_edge(TX_CLK) then 
			if reset = '1' then
				tx_state <= st_idle;
			else
				tx_state  <= tx_next_state;				
				-- Citace 
				tx_count  <= tx_count_next;
				ifg_count <= ifg_count_next;
				
				-- reg. vystup GMII
				GMII_TXD <= gmii_txd_i;
				GMII_TXEN <= gmii_txen_i;
				GMII_TXER <= gmii_txer_i;
				-- potvrzovaci signal pro ip vrstvu
				TX_DATA_ACK <= data_tx_ack_i;	
				
			end if;
		end if;
	end process;
	
	-- Potlaceni koliznich signalu pri plnem duplexu
	col <= (not FULL_DUPLEX) and GMII_COL;
	cs <= (not FULL_DUPLEX) and GMII_CS;

	----------------------------------------------------------------------
	-- KOMBINACNI CAST ---------------------------------------------------
	----------------------------------------------------------------------
	mac_tx: process (tx_state, col, cs, ifg_count, tx_count, crc_32, TX_DATA, TX_DATA_VLD, IFG)
	begin
		calc_crc <= '0';
		rst_crc <= '0';
		tx_count_en <= '0';
		gmii_txd_i <= x"00";
		gmii_txen_i <= '0';
		gmii_txer_i <= '0';
		data_tx_ack_i <= '0';
		COLLISION <= '0';
		ifg_count_rst <= '0';
		ifg_count_en <= '0';
		tx_next_state <= st_idle;
	
		case (tx_state) is
			
			----------------------------------------------------------------------			
			-- Necinnost ---------------------------------------------------------
			when st_idle => 
				tx_next_state <= st_idle;
				rst_crc <= '1';
				ifg_count_rst <= '1';
				if TX_DATA_VLD = '1' then 
					tx_next_state <= st_preamble; 
					if cs = '1' then tx_next_state <= st_cs; end if; -- CSMA algoritmus
				end if;
			
			----------------------------------------------------------------------						
			-- Vytvoreni synchronizacni sekvence + start sekvence ----------------
			when st_preamble =>
				tx_next_state <= st_preamble;
				tx_count_en <= '1';
				gmii_txd_i <= x"55";
				gmii_txen_i <= '1';
				if col = '1' then -- detekce kolize
					tx_next_state <= st_collision;
					tx_count_en <= '0';
				else
					if tx_count(2 downto 0) = "111" then 
						gmii_txd_i <= x"D5"; 
						tx_next_state <= st_data; 	
						tx_count_en <= '0'; 
						data_tx_ack_i <= '1';  
					end if;
				end if;			
			
			----------------------------------------------------------------------				
			-- Odesilani ramce ---------------------------------------------------
			when st_data =>
				tx_count_en <= '1';
				tx_next_state <= st_data;
				gmii_txd_i <= TX_DATA;
				gmii_txen_i <= '1';
				calc_crc <= '1';				
				if col = '1' then -- detekce kolize
					tx_next_state <= st_collision;
					tx_count_en <= '0';
				else
					if (tx_count < 60) and (TX_DATA_VLD = '0') then -- Jumbo frame povolen
						tx_next_state <= st_padding; 
						gmii_txd_i <= x"00";
					elsif TX_DATA_VLD = '0' then  
						tx_next_state <= st_crc; 
						gmii_txd_i <= crc_32(7 downto 0); 
						calc_crc <= '0'; 
						tx_count_en <= '0';
					end if;
				end if;			
			
			----------------------------------------------------------------------			
			-- minimalni delka ramce je 60B -> doplneni 0x00 ---------------------
			when st_padding =>
				tx_count_en <= '1';
				gmii_txd_i <= x"00";
				gmii_txen_i <= '1';
				calc_crc <= '1';
				tx_next_state <= st_padding;			
				if col = '1' then -- detekce kolize
					tx_next_state <= st_collision;
					tx_count_en <= '0';
				else
					if tx_count = 60 then 
						tx_next_state <= st_crc; 
						gmii_txd_i <= crc_32(7 downto 0); 
						calc_crc <= '0'; 
						tx_count_en <= '0'; 
					end if;
				end if;			
			
			----------------------------------------------------------------------
			-- Doplneni ramce o CRC32 --------------------------------------------
			when st_crc =>
				tx_next_state <= st_crc;
				gmii_txen_i <= '1';
				tx_count_en <= '1';								
				if col = '1' then -- detekce kolize
					tx_next_state <= st_collision;
					tx_count_en <= '0';
				else
					case tx_count(1 downto 0) is 
						when "00"=> gmii_txd_i <= crc_32(15 downto 8);
						when "01"=> gmii_txd_i <= crc_32(23 downto 16);
						when "10"=> gmii_txd_i <= crc_32(31 downto 24); tx_next_state <= st_ifg; tx_count_en <= '0';
						when others => 
					end case;
				end if;
			
			----------------------------------------------------------------------
			-- po uvolneni media cekat na ifg ------------------------------------
			when st_cs =>
				tx_next_state <= st_cs;
				if cs = '0' then tx_next_state <= st_ifg; end if;	
			
			----------------------------------------------------------------------
			-- Nastala kolize ----------------------------------------------------
			when st_collision =>
				tx_next_state <= st_collision;
				tx_count_en <= '1';
				gmii_txer_i <= '1';
				COLLISION <= '1';
				-- cekat nahodnou dobu (nastavena pevna doba)
				if tx_count = 60 then tx_next_state <= st_idle; end if;
			
			----------------------------------------------------------------------
			-- Zakladni mezera mezi pakety dlouha 12B ----------------------------
			when st_ifg =>	
				tx_next_state <= st_ifg;
				tx_count_en <= '1';
				if tx_count > 10 then tx_next_state <= st_ifg_inc; end if;
			
			----------------------------------------------------------------------
			-- Do mezery zapocitat nastavene ifg ---------------------------------
			when st_ifg_inc =>					
				ifg_count_en <= '1';
				tx_next_state <= st_ifg;
				if ifg_count = IFG then tx_next_state <= st_idle; end if;
	
		end case;
	end process;

end Behavioral;

