-------------------------------------------------------------
-- MSS copyright 2010-2016
--	Filename:  COM5401.VHD
-- Author: Alain Zarembowitch / MSS
--	Version: 12b
--	Date last modified: 7/19/16
-- Inheritance: 	N/A
-- 
-- description:  10/100/1000 MAC. The hardware interface (RGMII or GMII_MII is selected with MII_SELECT.
-- Features include
-- (a) Automatic appending of 32-bit CRC to tx packets. Users don't have to.
-- (b) discarding of rx packets with bad CRC.
-- 
-- Usage: the following KSZ9021RN strapping options MUST be set in the .ucf file
-- pin35 RX_CLK/PHYAD2 pull-down  LEFT_CONNECTOR_A(1),A(19),B(1),B(21)
-- pins32,31,28,27 RXDx/MODEx  pull-up, advertise all modes
-- 	LEFT_CONNECTOR_A(2,4,5,6,21,22,23,24),B(3,4,6,7,23,24,25,26)
-- pin33 RX_DV(RX_CTL)/CLK125_EN	
-- 	pull-down on all ICs. No need for an external 125 MHz clock (not very clean).
-- 	LEFT_CONNECTOR_A(2) pullup, LEFT_CONNECTOR_A(20),B(2),B(22) pull-down
-- pin41 CLK125_NDO/LED_MODE pulldown dual leds, tri-color.
-- 	LEFT_CONNECTOR_A(13,31),_B(14,34)
-- 
-- In addition, place a constraint on the output pins to the PHY to minize the delay spread among the tx pins.
--# place a constraint on the LAN tx signals timing
--NET "LEFT_CONNECTOR_A[7]" TNM = "LAN_TX_OUT";
--NET "LEFT_CONNECTOR_A[8]" TNM = "LAN_TX_OUT";
--NET "LEFT_CONNECTOR_A[9]" TNM = "LAN_TX_OUT";
--NET "LEFT_CONNECTOR_A[10]" TNM = "LAN_TX_OUT";
--NET "LEFT_CONNECTOR_A[11]" TNM = "LAN_TX_OUT";
--NET "LEFT_CONNECTOR_A[12]" TNM = "LAN_TX_OUT";
--TIMEGRP "LAN_TX_OUT" OFFSET = OUT 10.5 ns AFTER "LEFT_CONNECTOR_A[1]";
-- 
-- In addition, place a constraint on the input pins from the PHY to minimize the delay spread among the rx pins
--# place a constraint on the LAN rx signals timing
--NET "LEFT_CONNECTOR_A[2]" TNM = "LAN1_RX_IN";
--NET "LEFT_CONNECTOR_A[3]" TNM = "LAN1_RX_IN";
--NET "LEFT_CONNECTOR_A[4]" TNM = "LAN1_RX_IN";
--NET "LEFT_CONNECTOR_A[5]" TNM = "LAN1_RX_IN";
--NET "LEFT_CONNECTOR_A[6]" TNM = "LAN1_RX_IN";
--TIMEGRP "LAN1_RX_IN" OFFSET = IN 0.5 ns BEFORE "LEFT_CONNECTOR_A<1>";
--
-- The transmit elastic buffer is large enough for 2 maximum size frame. The tx Clear To Send (MAC_TX_CTS)
-- signal is raised when the the MAC is ready to accept one complete frame without interruption.
-- In this case, MAC_TX_CTS may go low while the frame transfer has started, but there is guaranteed
-- space for the entire frame.  
--
-- Usage: to generate a .ngc component, set the XST synthesize properties with Xilinx Specific option / Add I/O buffers off, 
-- then start the Synthesis (no need for "implement design").
--
----------------------------------------------
-- TIMING ISSUE WITH 7-SERIES XILINX FPGA (or any very fast FPGA)
----------------------------------------------
-- As the TXC transmit clock is sent quickly to the LAN PHY, it can arrive too early for the PHY to be able to correct the
-- skew with the TX_CTL and TXD data and control signals. Normally, finding the correct for CLK_SKEWxx is sufficient, 
-- but sometimes not. Therefore another method was added in this case: 
-- 1. set 		INTERNAL_PHY_TX_CLKS_DELAYS: std_logic := '1';
-- 2. provide a reference 125 MHz clock CLK125_0 and a slightly delayed version CLK125_1, as well as a 25 MHz clock CLK25.
-- The phase offset between CLK125_0 and CLK125_1 can be generated by a PLL. For example 30deg phase offset = 0.666ns.

-------------------------------------------
-- Device utilization (GMII, xc6slx16 device)
-------------------------------------------
--Number of Slice Registers
--825/18224, 4%
--Number of Slice LUTs
--1136/9112, 12%
--Number of fully used LUT-FF pairs
--573/1388, 41%
--Number of bonded IOBs
--269/232, 115%
--Number of Block RAM/FIFO
--3/32, 9%
--Number of BUFG/BUFGCTRLs
--3/16, 18%
-------------------------------------------
-- Device utilization (RGMII, xc6slx16 device)
-------------------------------------------
-- Maximum Frequency: 143.486MHz
--Number of Slice Registers
--830/18224,4%
--Number of Slice LUTs
--1139/9112,12%
--Number of fully used LUT-FF pairs
--576/1393,41%
--Number of bonded IOBs
--256/232,110%
--Number of Block RAM/FIFO
--3/32,9%
--Number of BUFG/BUFGCTRLs
--2/16,12%

-- Rev 2 2/28/11 AZ
-- Reduced number of clocks from 3 (processing clock, 125 MHz reference clock from PHY, RX clock from PHY)
-- to two (processing clock, RX clock from PHY). The main reason is that the 125 MHz from the PHY was not clean enough, 
-- nor is it absolutely necessary. Cleaner code. The fewer clocks the better.
--
-- Rev 3 8/4/11 AZ
-- Corrected bug with RX_EOF3
-- 
-- Rev 4 8/18/11 AZ
-- Added initialization for simulation
-- Corrected tx flow control problem when using 10 Mbps.
-- Eliminated false MAC_TX_CTS after small packet
-- Reclocked MAC_TX_CTS output signal with user-side clock.
-- Block transmit path if link_status = '0'. Also reset tx state machine.
-- Flow control issue at 10Mbps in case of collision.
-- 
-- Rev 5 9/16/11 AZ
-- Corrected interface errors when instantiating GMII_MII_WRAPPER.
-- 
-- Rev 7 11/19/11 AZ
-- Major modifications in GMII_MII_WRAPPER to eliminate the need for a 125 MHz clock from the user.
-- The user-supplied clock CLK is now fully independent of the PHY clocks for GMII/MII and RGMII.
-- Changed the link status detection method to a more generic one (read basic status register 1) to work with all PHYs.
-- Added tristate buffer in MII_MI.vhd (otherwise the MDIO signal is mishandled by Xilinx XST when creating
-- a "black box" .ngc file).
--
-- Rev 9 8/11/12 AZ
-- Rationalize the reset strategy. ASYNC_RESET is converted to three synchronous resets, one for each global clock.
-- Added diagnostics and test points
-- Link status is now conditioned on the presence of a RXC clock from the PHY
-- Speed status now detects the absence of RXC clock from the PHY
-- Strengthen start of frame detection (now detecting 4 bytes, previously only 1)
-- +++Corrected problem detecting SOF/EOF when two packets are in rapid succession.
--
-- Rev 11  2/1/14 AZ
-- Corrected problem detecting SOF/EOF when two a short frame immediatly follows a larger frame
-- Introduces an 8-CLK min delay between successive frames
-- Encapsulated block RAM for easier porting to other FPGA types.
-- brought PLATFORM generic parameter to the top
-- changed sensitivity lists to avoid warnings
--
-- 6/22/14 AZ
-- Added resilience to missing Ethernet preamble (observed in a 10 Mbps case with KSZ9021)
-- PHY_CONFIG_CHANGE is no longer ignored if sent while the PHY is in reset
-- 
-- 8/18/14 AZ
-- control both skews registers in KSZ9021
-- errors counters now support concurrent errors (too short AND bad crc for example)
--
-- 9/22/14 AZ
-- Added parameter to support different target PHYs
--
-- Rev 12b 7/19/16 AZ
-- Added generic parameter NO_1000 to advertise or not 1000Mbps capability. Default is yes.

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

entity COM5401 is
	generic (
		MII_SEL: std_logic := '1';   
			-- select which MII interface: 0 = RGMII, 1 = GMII/MII
		PHY_ADDR: std_logic_vector(4 downto 0) := "00100";	
			-- PHY_AD0/1 pulled-down by 1KOhm, PHY_AD2 pulled-up in .ucf file.
		CLK_FREQUENCY: integer := 125;
			-- CLK frequency in MHz. Needed to compute actual delays.
			-- 8 MHz min (otherwise counters sizes must be increased to prevent overflow)
		PLATFORM: integer := 6;
			-- addresses minor differences in I/O primitives
			-- 6 = Xilinx Spartan-6 style IODELAY2, IDDR2, ODDR2
			-- 7 = Xilinx series-7 style IDELAY, IDDR, ODDR
		TARGET_PHY: integer := 2;
		      -- account for subtle differences in PHY configuration (non-standard registers for example)
		      -- 2 = Micrel KSZ9021
		      -- 3 = Micrel KSZ9031 and more standard RGMII PHYs
		NO_1000: std_logic := '0';	
			-- '1' to stop advertising 1000Mbps capabilities during autonegotiations
			-- Use when 125 MHz clockk for GbE is too high for place and route
			-- SCOPE CURRENTLY LIMITED TO KSZ9021
		INTERNAL_PHY_TX_CLKS_DELAYS: std_logic := '0';
		INTERNAL_PHY_RX_CLKS_DELAYS: std_logic := '0';
			-- '0' to implement the delays at the PHY using the CLK_SKEW value (plan A)
			-- '1' to implement ADDITIONAL RXC and TXC clock delays within (plan A+B)
		SIMULATION: std_logic := '0'
			-- during simulation, fake LINK_STATUS = '1'
	);
    Port ( 
		--// CLK, RESET
		CLK: in std_logic;
			-- USER-side GLOBAL CLOCK
		SYNC_RESET: in std_logic;
			-- reset pulse must be > slowest clock period  (>400ns)
			-- synchronous with CLK
		CLK125_0: in std_logic;	-- must be a GLOBAL clock
		CLK125_1: in std_logic;	-- no need for global buffer here
		CLK25: in std_logic;	-- no need for global buffer here
			-- 125 MHz clocks with small (ns) phase offset, 25 MHz clock (50% duty cycle)
			-- Used for TXC delay when external LAN PHY is unable to correct the tx signals timing or when
			-- the FPGA does not have output delay capability (for example Xilinx 7-series HR banks).
			-- Used when INTERNAL_PHY_TX_CLKS_DELAYS = '1' and PLATFORM = 7
		
		--// MAC CONFIGURATION
		-- configuration signals are synchonous with the user-side CLK
		MAC_TX_CONFIG: in std_logic_vector(15 downto 0);
			-- bit 0: (1) Automatic padding of short frames. Requires that auto-CRC insertion be enabled too. 
			--			 (0) Skip padding. User is responsible for adding padding to meet the minimum 60 byte frame size
			-- bit 1: (1) Automatic appending of 32-bit CRC at the end of the frame
			--			 (0) Skip CRC32 insertion. User is responsible for including the frame check sequence
			-- Note: use 0x03 when interfacing with COM-5402 IP/UDP/TCP stack.
		MAC_RX_CONFIG: in std_logic_vector(15 downto 0);
			-- bit 0: (1) promiscuous mode enabled (0) disabled, i.e. destination address is verified for each incoming packet 
			-- bit 1: (1) accept broadcast rx packets (0) reject
			-- bit 2: (1) accept multi-cast rx packets (0) reject
			-- bit 3: (1) filter out the 4-byte CRC field (0) pass along the CRC field.
			-- Note2: use 0x0F when interfacing with COM-5402 IP/UDP/TCP stack.
		MAC_ADDR: in std_logic_vector(47 downto 0);
			-- This network node 48-bit MAC address. The receiver checks incoming packets for a match between 
			-- the destination address field and this MAC address.
			-- The user is responsible for selecting a unique ‘hardware’ address for each instantiation.
			-- Natural bit order: enter x0123456789ab for the MAC address 01:23:45:67:89:ab

		--// PHY CONFIGURATION
		-- configuration signals are synchonous with the user-side CLK.
		PHY_CONFIG_CHANGE: in std_logic;
			-- optional pulse to activate any configuration change below.
			-- Not needed if the default values are acceptable.
		PHY_RESET: in std_logic; 
			-- 1 = PHY software reset (default), 0 = no reset
		SPEED: in std_logic_vector(1 downto 0);
			-- 00 = force 10 Mbps
			-- 01 = force 100 Mbps
			-- 10 = force 1000 Mbps
			-- 11 = auto-negotiation (default)
		DUPLEX: in std_logic;
			-- 1 = full-duplex (default), 0 = half-duplex
		TEST_MODE: in std_logic_vector(1 downto 0);
			-- 00 = normal mode (default)
			-- 01 = loopback mode (at the phy)
			-- 10 = remote loopback
		POWER_DOWN: in std_logic;
			-- software power down mode. 1 = enabled, 0 = disabled (default).
		CLK_SKEW1: in std_logic_vector(15 downto 0);
		CLK_SKEW2: in std_logic_vector(15 downto 0);
			-- Skews passed to the PHY control registers to adjust clock/control/data delays
			-- KSZ9021 PHY: 16-bit values
				-- 1 = RGMII clock and control pad skew
				-- 2 = RGMII rx data pad skew
			-- KSZ9031 PHY: 10-bit values
				-- 1 = Clock pad skew
				-- IEEE 802.3 MMD device address 2 register 8.
				-- bit 4:0  RX_CLK output pad skew control
				-- bit 9:5 GTX_CLK input pad skew control
				-- 2 = unused (future)


		--// USER -> Transmit MAC Interface
		-- 32-bit CRC is automatically appended. User should not supply it.
		-- Synchonous with the user-side CLK
		MAC_TX_DATA: in std_logic_vector(7 downto 0);
			-- MAC reads the data at the rising edge of CLK when MAC_TX_DATA_VALID = '1'
		MAC_TX_DATA_VALID: in std_logic;
			-- data valid
		MAC_TX_EOF: in std_logic;
			-- '1' when sending the last byte in a packet to be transmitted. 
			-- Aligned with MAC_TX_DATA_VALID
		MAC_TX_CTS: out std_logic;
			-- MAC-generated Clear To Send flow control signal, indicating room in the 
			-- tx elastic buffer for a complete maximum size frame 1518B. 
			-- The user should check that this signal is high before deciding to send
			-- sending the next frame. 
			-- Note: MAC_TX_CTS may go low while the frame is transfered in. Ignore it.
		
		--// Receive MAC -> USER Interface
		-- Valid rx packets only: packets with bad CRC or invalid address are discarded.
		-- Synchonous with the user-side CLK
		-- The short-frame padding is included .
		MAC_RX_DATA: out std_logic_vector(7 downto 0);
			-- USER reads the data at the rising edge of CLK when MAC_RX_DATA_VALID = '1'
		MAC_RX_DATA_VALID: out std_logic;
			-- data valid
		MAC_RX_SOF: out std_logic;
			-- '1' when sending the first byte in a received packet. 
			-- Aligned with MAC_RX_DATA_VALID
		MAC_RX_EOF: out std_logic;
			-- '1' when sending the last byte in a received packet. 
			-- Aligned with MAC_RX_DATA_VALID
		MAC_RX_CTS: in std_logic;
			-- User-generated Clear To Send flow control signal. The receive MAC checks that this 
			-- signal is high before sending the next MAC_RX_DATA byte. 
		
		
		
		--// RGMII PHY Interface (when RGMII is enabled. See MII_SEL generic flag above)
		-- Connect directly to FPGA pins
		TXC: out std_logic;  -- tx clock
		TXD: out std_logic_vector(3 downto 0);  -- tx data
		TX_CTL: out std_logic;  -- tx control: combines TX_EN and TX_ER
			-- valid at the falling clock edge
		
		RXC: in std_logic;  -- receive clock
			-- must be delayed by 1.5 to 2.1 ns to prevent glitches
			-- Set the strapping options in the .ucf constraint file.
		RXD: in std_logic_vector(3 downto 0);  
			-- receive data
			-- Set the strapping options in the .ucf constraint file.
		RX_CTL: in std_logic;  -- receive control. combines RX_DV and RX_ER
			-- valid at the falling clock edge
			-- Note: Micrel KSZ9021RN PHY defines these as dual-function signals.
			-- Set the strapping options in the .ucf constraint file.
		RESET_N: out std_logic;
			-- PHY reset
		MCLK: out std_logic;
		MDIO: inout std_logic;  -- (tri-state)
			-- serial interface
		
		--// GMII/MII PHY Interface (when GMII/MII is enabled.  See MII_SEL generic flag above)
		MII_TX_CLK: in std_logic;
			-- MII tx clock from PHY. Continuous clock. (10/100 Mbps only) 
			-- 25 MHz (100 Mbps), or 2.5 MHz (10 Mbps) depending on speed
			-- accuracy: +/- 100ppm (MII)
			-- duty cycle between 35% and 65% inclusive (MII).
		GMII_TX_CLK: out std_logic;
			-- GMII tx clock to PHY. Continuous clock. 125MHz (1000 Mbps only)
			-- 2ns delay inside (user adjustable).
		GMII_MII_TXD: out std_logic_vector(7 downto 0);  -- tx data
			-- tx data (when TX_EN = '1' and TX_ER = '0') or special codes otherwise (carrier extend, 
			-- carrier extend error, transmit error propagation). See 802.3 table 35-1 for definitions.
		GMII_MII_TX_EN: out std_logic;
		GMII_MII_TX_ER: out std_logic;
			-- to deliberately corrupt the contents of the frame (so as to be detected as such by the receiver)
		GMII_MII_CRS: in std_logic;
		GMII_MII_COL: in std_logic;
		
		GMII_MII_RX_CLK: in std_logic;  
			-- continuous receive reference clock recovered by the PHY from the received signal
			-- 125/25/2.5 MHz +/- 50 ppm. 
			-- Duty cycle better than 35%/65% (MII)
			-- 125 MHz must be delayed by 1.5 to 2.1 ns to prevent glitches (TBC. true for RGMII, but for GMII TOO???)
			
		GMII_MII_RXD: in std_logic_vector(7 downto 0);  
			-- rx data. 8-bit when 1000 Mbps. 4-bit nibble (3:0) when 10/100 Mbps.
		GMII_MII_RX_DV: in std_logic;  
		GMII_MII_RX_ER: in std_logic;  
		
	   
		--// PHY status
		SPEED_STATUS: out std_logic_vector(1 downto 0);
			-- RXC clock speed, 00 = 2.5 MHz, 01 = 25 MHz, 10 = 125 MHz, 11 = no RXC clock from the PHY
			-- read from the PHY

		-- The link, duplex status are read from the RXD when RX_CTL is inactive
		-- synchronous with RXCG global clock
		LINK_STATUS: out std_logic;
			-- 0 = link down, 1 = link up
		DUPLEX_STATUS: out std_logic;
			-- 0 = half duplex, 1 = full duplex
		PHY_ID: out std_logic_vector(15 downto 0);
			-- read PHY ID (part of hardware self-test). Correct answer for KSZ9021 is 0x0022
			
		--// DIAGNOSTICS (synchronous with user-supplied clock CLK) 
		N_RX_FRAMES: out  std_logic_vector(15 downto 0);
			-- number of received frames
		N_RX_BAD_CRCS: out  std_logic_vector(15 downto 0);
			-- number of BAD CRCs among the received frames
		N_RX_FRAMES_TOO_SHORT: out  std_logic_vector(15 downto 0);
			-- number of rx frames too short (<64B)
		N_RX_FRAMES_TOO_LONG: out  std_logic_vector(15 downto 0);
			-- number of rx frames too long (>1518B)
		N_RX_WRONG_ADDR: out  std_logic_vector(15 downto 0);
			-- number of rx frames where address does not match (and promiscuous mode is off)
		N_RX_LENGTH_ERRORS: out  std_logic_vector(15 downto 0);
			-- number of rx frames with length field inconsistent with actual rx frame length

		--// TEST POINTS, COMSCOPE TRACES
		CS_CLKG: out std_logic;	-- synchronous clock for the comscope traces
		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;
		CT1: out std_logic;
		CT1_CLK: out std_logic;
		TP: out std_logic_vector(10 downto 1)
		
 );
end entity;

architecture Behavioral of COM5401 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT RESET_TIMER
	GENERIC (
		CLK_FREQUENCY: in integer
	);	
	PORT(
		CLK : IN std_logic;
		RESET_START : IN std_logic;          
		RESET_COMPLETE : OUT std_logic;
		INITIAL_CONFIG_PULSE : OUT std_logic;
		RESET_N : OUT std_logic
		);
	END COMPONENT;

	COMPONENT PHY_CONFIG
	GENERIC (
		PHY_ADDR: std_logic_vector(4 downto 0)
	);	
	PORT(
		SYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		CONFIG_CHANGE : IN std_logic;
		PHY_RESET : IN std_logic;
		SPEED : IN std_logic_vector(1 downto 0);
		DUPLEX : IN std_logic;
		TEST_MODE : IN std_logic_vector(1 downto 0);
		POWER_DOWN : IN std_logic;
		CLK_SKEW1: in std_logic_vector(15 downto 0);
		CLK_SKEW2: in std_logic_vector(15 downto 0);
		SREG_READ_START : IN std_logic;
		SREG_REGAD : IN std_logic_vector(8 downto 0);    
		MDIO : INOUT std_logic;      
		SREG_DATA : OUT std_logic_vector(15 downto 0);
		SREG_SAMPLE_CLK : OUT std_logic;
		MCLK : OUT std_logic;
		LINK_STATUS: out std_logic;  
		TP: out std_logic_vector(10 downto 1)
		);
	END COMPONENT;

	COMPONENT PHY_CONFIG_KSZ9021
	GENERIC (
		PHY_ADDR: std_logic_vector(4 downto 0);
		NO_1000: std_logic	
	);	
	PORT(
		SYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		CONFIG_CHANGE : IN std_logic;
		PHY_RESET : IN std_logic;
		SPEED : IN std_logic_vector(1 downto 0);
		DUPLEX : IN std_logic;
		TEST_MODE : IN std_logic_vector(1 downto 0);
		POWER_DOWN : IN std_logic;
		CLK_SKEW1: in std_logic_vector(15 downto 0);
		CLK_SKEW2: in std_logic_vector(15 downto 0);
		SREG_READ_START : IN std_logic;
		SREG_REGAD : IN std_logic_vector(8 downto 0);    
		MDIO : INOUT std_logic;      
		SREG_DATA : OUT std_logic_vector(15 downto 0);
		SREG_SAMPLE_CLK : OUT std_logic;
		MCLK : OUT std_logic;
		LINK_STATUS: out std_logic;  
		TP: out std_logic_vector(10 downto 1)
		);
	END COMPONENT;

	COMPONENT RGMII_WRAPPER
	GENERIC (
		PLATFORM: integer;
		INTERNAL_PHY_TX_CLKS_DELAYS: std_logic;
		INTERNAL_PHY_RX_CLKS_DELAYS: std_logic;
		CLK_FREQUENCY: in integer
	);	
	PORT(
		SYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		CLK125_0: in std_logic;	
		CLK125_1: in std_logic;	
		CLK25: in std_logic;	
		RXC : IN std_logic;
		RXD : IN std_logic_vector(3 downto 0);
		RX_CTL : IN std_logic;
		MAC_TXD : IN std_logic_vector(7 downto 0);
		MAC_TX_EN : IN std_logic;
		MAC_TX_ER : IN std_logic;
		MAC_TX_SAMPLE_CLK : IN std_logic;
		TX_SPEED : IN std_logic_vector(1 downto 0);          
		TXC : OUT std_logic;
		TXD : OUT std_logic_vector(3 downto 0);
		TX_CTL : OUT std_logic;
		MAC_RXD : OUT std_logic_vector(7 downto 0);
		MAC_RX_SAMPLE_CLK: OUT std_logic;
		MAC_RX_DV : OUT std_logic;
		MAC_RX_ER : OUT std_logic;
		RXCG_OUT : OUT std_logic;
		TXCG_OUT: out std_logic;
		CRS : OUT std_logic;
		COL : OUT std_logic;
		SPEED_STATUS : OUT std_logic_vector(1 downto 0);
		TP: out std_logic_vector(10 downto 1)
		);
	END COMPONENT;
	
	COMPONENT GMII_MII_WRAPPER
	GENERIC (
		CLK_FREQUENCY: in integer
	);	
	PORT(
		SYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		TX_CLK : IN std_logic;
		RX_CLK : IN std_logic;
		RXD : IN std_logic_vector(7 downto 0);
		RX_DV : IN std_logic;
		RX_ER : IN std_logic;
		CRS : IN std_logic;
		COL : IN std_logic;
		MAC_TXD : IN std_logic_vector(7 downto 0);
		MAC_TX_EN : IN std_logic;
		MAC_TX_ER : IN std_logic;
		MAC_TX_SAMPLE_CLK : IN std_logic;
		MAC_TX_SPEED : IN std_logic_vector(1 downto 0);          
		TXCG_OUT: out std_logic;
		GTX_CLK : OUT std_logic;
		TXD : OUT std_logic_vector(7 downto 0);
		TX_EN : OUT std_logic;
		TX_ER : OUT std_logic;
		RXCG_OUT: out std_logic;
		MAC_RXD : OUT std_logic_vector(7 downto 0);
		MAC_RX_DV : OUT std_logic;
		MAC_RX_ER : OUT std_logic;
		MAC_RX_SAMPLE_CLK : OUT std_logic;
		MAC_CRS : OUT std_logic;
		MAC_COL : OUT std_logic;
		SPEED_STATUS : OUT std_logic_vector(1 downto 0)
		);
	END COMPONENT;
	
	COMPONENT CRC32_8B
	PORT(
		SYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		CRC32_IN : IN std_logic_vector(31 downto 0);
		DATA_IN : IN std_logic_vector(7 downto 0);
		SAMPLE_CLK_IN : IN std_logic;          
		CRC32_OUT : OUT std_logic_vector(31 downto 0);
		CRC32_VALID : OUT std_logic
		);
	END COMPONENT;

	COMPONENT BRAM_DP
	GENERIC(
		DATA_WIDTHA: integer;
		ADDR_WIDTHA: integer;
		DATA_WIDTHB: integer;
		ADDR_WIDTHB: integer
	);
	PORT(
	    CLKA   : in  std_logic;
	    WEA    : in  std_logic;
	    ADDRA  : in  std_logic_vector(ADDR_WIDTHA-1 downto 0);
	    DIA   : in  std_logic_vector(DATA_WIDTHA-1 downto 0);
	    DOA  : out std_logic_vector(DATA_WIDTHA-1 downto 0);
	    CLKB   : in  std_logic;
	    WEB    : in  std_logic;
	    ADDRB  : in  std_logic_vector(ADDR_WIDTHB-1 downto 0);
	    DIB   : in  std_logic_vector(DATA_WIDTHB-1 downto 0);
	    DOB  : out std_logic_vector(DATA_WIDTHB-1 downto 0)
		);
	END COMPONENT;

	COMPONENT LFSR11C
	PORT(
		ASYNC_RESET : IN std_logic;
		CLK : IN std_logic;
		BIT_CLK_REQ : IN std_logic;
		SYNC_RESET : IN std_logic;
		SEED : IN std_logic_vector(10 downto 0);          
		LFSR_BIT : OUT std_logic;
		BIT_CLK_OUT : OUT std_logic;
		SOF_OUT : OUT std_logic;
		LFSR_REG_OUT: OUT std_logic_vector(10 downto 0)
		);
	END COMPONENT;
	
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- NOTATIONS: 
-- _E as one-CLK early sample
-- _D as one-CLK delayed sample
-- _D2 as two-CLKs delayed sample

--// CLK & RESETS ---------
signal SYNC_RESETRX: std_logic := '0';
signal RESETRX_FLAG_D: std_logic := '0';
signal SYNC_RESETTX: std_logic := '0';
signal RESETTX_FLAG_D: std_logic := '0';

--// PHY RESET AND CONFIGURATION ----------------------------------------------------------
signal RESET_N_LOCAL: std_logic := '0';
signal PHY_CONFIG_CHANGE_PENDING: std_logic := '0';
signal INITIAL_CONFIG_PULSE: std_logic := '1';
signal PHY_CONFIG_CHANGE_A: std_logic := '0';
signal PHY_RESET_A: std_logic := '0';
signal SPEED_A: std_logic_vector(1 downto 0);
signal DUPLEX_A: std_logic := '0';
signal TEST_MODE_A: std_logic_vector(1 downto 0);
signal POWER_DOWN_A: std_logic := '0';
signal CLK_SKEW1_A: std_logic_vector(15 downto 0);
signal CLK_SKEW2_A: std_logic_vector(15 downto 0);
signal RESET_COMPLETE: std_logic := '0';
signal PHY_IF_WRAPPER_RESET: std_logic := '0';
signal SREG_READ_START: std_logic := '0';
signal SREG_SAMPLE_CLK: std_logic := '0';
signal PHY_ID_LOCAL: std_logic_vector(15 downto 0);
signal LINK_STATUS_FROM_PHY: std_logic := '0';
signal LINK_STATUS_local: std_logic := '0';
signal DUPLEX_STATUS_local: std_logic := '0';
signal SPEED_STATUS_local: std_logic_vector(1 downto 0) := (others => '0');
signal TP_RGMII_WRAPPER: std_logic_vector(10 downto 1) := (others => '0');

--//  PHY INTERFACE: GMII to RGMII CONVERSION ----------------------------------------------------------
signal CRS: std_logic := '0';
signal CRS_D: std_logic := '0';
signal COL: std_logic := '0';
signal MAC_TXD: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_TX_EN: std_logic := '0';
signal MAC_TX_ER: std_logic := '0';
signal MAC_TX_SAMPLE_CLK: std_logic := '0';
signal MAC_RXD: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_RX_DV: std_logic := '0';
signal MAC_RX_ER: std_logic := '0';
signal MAC_RX_SAMPLE_CLK: std_logic := '0';

--//  TX ELASTIC BUFFER ----------------------------------------------------------
signal MAC_TX_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal MAC_TX_WPTR: std_logic_vector(11 downto 0) := (others => '0');
signal MAC_TX_WPTR_D: std_logic_vector(11 downto 0) := (others => '0');
signal MAC_TX_WPTR_D2: std_logic_vector(11 downto 0) := (others => '0');
signal MAC_TX_WPTR_D3: std_logic_vector(11 downto 0) := (others => '0');
signal MAC_TX_WPTR_STABLE: std_logic := '0';
signal MAC_TX_WPTR_STABLE_D: std_logic := '0';
signal TX_COUNTER8: std_logic_vector(2 downto 0) :=(others => '0');
signal MAC_TX_WEA: std_logic_vector(1 downto 0) := (others => '0');
signal MAC_TX_BUF_SIZE: std_logic_vector(11 downto 0) := (others => '0');
signal MAC_TX_RPTR: std_logic_vector(11 downto 0) := (others => '1');
signal MAC_TX_RPTR_D: std_logic_vector(11 downto 0) := (others => '1');
signal MAC_TX_RPTR_CONFIRMED: std_logic_vector(11 downto 0) := (others => '1');
signal MAC_TX_RPTR_CONFIRMED_D: std_logic_vector(11 downto 0) := (others => '1');
type DOBtype is array(integer range 0 to 1) of std_logic_vector(8 downto 0);
signal MAC_TX_DOB: DOBtype;
signal MAC_TX_DATA2: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_TX_EOF2: std_logic := '0';
signal MAC_TX_EOF2_D: std_logic := '0';
signal COMPLETE_TX_FRAMES_INBUF: std_logic_vector(7 downto 0) := x"00";  -- can't have more than 147 frames in a 16k buffer
signal ATLEAST1_COMPLETE_TX_FRAME_INBUF: std_logic := '0';
signal MAC_TX_EOF_TOGGLE: std_logic := '0';
signal MAC_TX_EOF_TOGGLE_D: std_logic := '0';
signal MAC_TX_EOF_TOGGLE_D2: std_logic := '0';
signal MAC_TX_CTS_local: std_logic := '0';

--//-- TX FLOW CONTROL --------------------------------
signal TX_SUCCESS_TOGGLE: std_logic := '0';
signal TX_SUCCESS_TOGGLE_D: std_logic := '0';
signal TX_SUCCESS_TOGGLE_D2: std_logic := '0';
signal MAC_TX_BUF_FREE: std_logic_vector(11 downto 0) := (others => '0');


--// MAC TX STATE MACHINE ----------------------------------------------------------
signal TX_CLKG: std_logic := '0';
signal TX_SPEED: std_logic_vector(1 downto 0) := (others => '0');
signal TX_BYTE_CLK: std_logic := '0';
signal TX_BYTE_CLK_D: std_logic := '0';
signal TX_HALF_BYTE_FLAG: std_logic := '0';
signal IPG: std_logic := '0';
signal IPG_CNTR: std_logic_vector(7 downto 0) := (others => '0');  -- TODO CHECK CONSISTENCY WITH TIMER VALUES
signal TX_EVENT1: std_logic := '0';
signal TX_EVENT2: std_logic := '0';
signal TX_EVENT3: std_logic := '0';
signal TX_STATE: integer range 0 to 15 := 0;
signal TX_BYTE_COUNTER: std_logic_vector(18 downto 0) := (others => '0');  -- large enough for counting 2000 bytes in max size packet
signal TX_BYTE_COUNTER2: std_logic_vector(2 downto 0) := (others => '0');  -- small auxillary byte counter for small fields
signal TX_PREAMBLE: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_TX_DATA4: std_logic_vector(7 downto 0) := (others => '0');
signal RETX_ATTEMPT_COUNTER: std_logic_vector(4 downto 0) := (others => '0'); -- re-transmission attempts counter
signal RAND: std_logic_vector(10 downto 0) := (others => '0');
signal RETX_RANDOM_BKOFF: std_logic_vector(9 downto 0) := (others => '0');
signal TX_SUCCESS: std_logic := '0';
signal TX_EN: std_logic := '0';
signal TX_ER: std_logic := '0';

--//  TX 32-BIT CRC COMPUTATION -------------------------------------------------------
signal TX_CRC32: std_logic_vector(31 downto 0) := (others => '0');
signal TX_CRC32_FLIPPED_INV: std_logic_vector(31 downto 0) := (others => '0');
signal TX_CRC32_RESET: std_logic := '0';
signal TX_FCS: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_TX_SAMPLE3_CLK: std_logic := '0';
signal MAC_TX_DATA3: std_logic_vector(7 downto 0) := (others => '0');

--// MAC RX STATE MACHINE ----------------------------------------------------------
signal RX_CLKG: std_logic := '0';
signal RX_STATE: integer range 0 to 3 := 0;
signal RX_EVENT1: std_logic := '0';
--signal RX_EVENT2: std_logic := '0';
signal RX_EVENT3: std_logic := '0';
signal RX_EVENT4: std_logic := '0';
signal RX_EVENT5: std_logic := '0';
signal RX_EVENT5_TOGGLE: std_logic := '0';
signal RX_EVENT5_TOGGLE_D: std_logic := '0';
signal RX_EVENT5_TOGGLE_D2: std_logic := '0';
signal RX_BYTE_COUNTER: std_logic_vector(18 downto 0):= (others => '0');  -- large enough for counting 2000 bytes in max size packet
signal RX_BYTE_COUNTER_INC: std_logic_vector(18 downto 0):= (others => '0');  -- large enough for counting 2000 bytes in max size packet
signal RX_TOO_SHORT: std_logic := '0';
signal RX_TOO_LONG: std_logic := '0';
signal RX_VALID_ADDR: std_logic := '0';
signal RX_LENGTH_ERR: std_logic := '0';
signal LAST6B: std_logic_vector(47 downto 0) := (others => '0');
signal RX_LENGTH: std_logic_vector(10 downto 0) := (others => '0');
signal RX_LENGTH_TYPEN: std_logic := '0';
signal RX_DIFF: std_logic_vector(11 downto 0) := (others => '0');
signal MAC_RXD_D: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_RX_SAMPLE2_CLK: std_logic := '0';
signal RX_STATE4: integer range 0 to 3 := 0;
signal RX_STATE4_D: integer range 0 to 3 := 0;
signal RX_STATE4_TIMER: integer range 0 to 7 := 0;

--//  RX 32-BIT CRC COMPUTATION -------------------------------------------------------
signal RX_CRC32_RESET: std_logic := '0';
signal RX_CRC32: std_logic_vector(31 downto 0) := (others => '0');
signal RX_CRC32_VALID: std_logic := '0';
--signal RX_BAD_CRC: std_logic := '0';

--// PARSE RX DATA -------------------------------------------------------------------
signal MAC_RXD3: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_RX_SAMPLE3_CLK: std_logic := '0';
--signal MAC_RX_SOF3: std_logic := '0';	-- unused
signal MAC_RX_EOF3A: std_logic := '0';
signal MAC_RX_EOF3B: std_logic := '0';
signal MAC_RX_EOF3B_D: std_logic := '0';
signal MAC_RX_EOF3: std_logic := '0';
signal RX_FRAME_EN3: std_logic := '0';
signal RX_FRAME_TOGGLE3: std_logic := '0';  -- delineates VALID rx frames (does not toggle if rx frame was rejected)

--//  VALID RX FRAME? ----------------------------------------------------------
signal N_RX_FRAMES_local: std_logic_vector(15 downto 0) := (others => '0');
signal N_RX_BAD_CRCS_local: std_logic_vector(15 downto 0) := (others => '0');
signal N_RX_FRAMES_TOO_SHORT_local: std_logic_vector(15 downto 0) := (others => '0');
signal N_RX_FRAMES_TOO_LONG_local: std_logic_vector(15 downto 0) := (others => '0');
signal N_RX_LENGTH_ERRORS_local: std_logic_vector(15 downto 0) := (others => '0');
signal N_RX_WRONG_ADDR_local: std_logic_vector(15 downto 0) := (others => '0');

--//  RX INPUT ELASTIC BUFFER ----------------------------------------------------------
signal RX_EVENT5_TOGGLE2_D: std_logic := '0';
signal RX_EVENT5_TOGGLE2_D2: std_logic := '0';
signal MAC_RX_DIA: std_logic_vector(8 downto 0) := (others => '0');
signal MAC_RX_DOB: std_logic_vector(8 downto 0) := (others => '0');
signal MAC_RX_WPTR: std_logic_vector(10 downto 0) := (others => '0');
signal MAC_RX_WPTR_CONFIRMED: std_logic_vector(10 downto 0) := (others => '0');
signal MAC_RX_WPTR_CONFIRMED_D: std_logic_vector(10 downto 0) := (others => '0');
signal MAC_RX_RPTR: std_logic_vector(10 downto 0) := (others => '1');
signal MAC_RXD4: std_logic_vector(7 downto 0) := (others => '0');
signal MAC_RX_SAMPLE4_CLK: std_logic := '0';
signal MAC_RX_SAMPLE4_CLK_E: std_logic := '0';
signal DISABLE_OUTPUT4: std_logic := '0';
signal RX_FRAME_TOGGLE4: std_logic := '0';
signal RX_FRAME_TOGGLE4_D: std_logic := '0';
signal MAC_RX_SOF4: std_logic := '0';
signal MAC_RX_EOF4: std_logic := '0';
signal MAC_RX_EOF4_D: std_logic := '0';
signal MAC_RX_DATA_VALID_local: std_logic := '0';
signal MAC_RX_DATA_VALID_E: std_logic := '0';
signal MAC_RX_BUF_SIZE: std_logic_vector(10 downto 0) := (others => '0');
signal PHY_CONFIG_TP: std_logic_vector(10 downto 1) := (others => '0');

--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

--// SYNCHRONOUS RESETS ----------------------------------------------------------
-- Create synchronous reset for all three clocks within: RX_CLKG, TX_CLKG, CLK

-- Create synchronous reset for RX_CLKG
-- PHY-supplied RX global clock
SYNC_RESETRX_GEN_001: process(RX_CLKG)
begin	
	if rising_edge(RX_CLKG) then
		RESETRX_FLAG_D <= SYNC_RESET;
		SYNC_RESETRX <= RESETRX_FLAG_D;
	end if;
end process;

-- Create synchronous reset for TX_CLKG
SYNC_RESETTX_GEN_001: process(TX_CLKG)
begin	
	if rising_edge(TX_CLKG) then
		RESETTX_FLAG_D <= SYNC_RESET;
		SYNC_RESETTX <= RESETTX_FLAG_D;
	end if;
end process;

--// PHY RESET AND CONFIGURATION ----------------------------------------------------------
-- First generate a RESET_N pulse 10ms long, then wait 50ms before programming the PHY
-- We cannot assume that the 125 MHz reference clock is present. 


-- PHY reset at power up or SYNC_RESET
-- Generates a 10ms RESET_N pulse followed by a TBD ms delay and a INITIAL_CONFIG_PULSE.
-- The delay between RESET_N de-assertion and config pulse is 100ms because of the RC timeconstant
-- (RESET_N does not go directly to the PHY, it goes through a RC filter first)
Inst_RESET_TIMER: RESET_TIMER 
GENERIC MAP(
	CLK_FREQUENCY => CLK_FREQUENCY	-- user clock frequency in MHz
)
PORT MAP(
	CLK => CLK,	-- user clock, always present
	RESET_START => SYNC_RESET,
	RESET_COMPLETE => RESET_COMPLETE,
	INITIAL_CONFIG_PULSE => INITIAL_CONFIG_PULSE,  -- config pulse 50ms after RESET_N deassertion
	RESET_N => RESET_N_LOCAL
);

RESET_N <= RESET_N_LOCAL;

-- hold the PHY_CONFIG_CHANGE until the intial reset cycle is complete
PHY_CONFIG_CHANGE_HOLD: process(CLK)
begin
	if rising_edge(CLK) then
		if(PHY_CONFIG_CHANGE = '1') then
			PHY_CONFIG_CHANGE_PENDING <= '1';
		elsif(PHY_CONFIG_CHANGE_PENDING = '1') and (RESET_COMPLETE = '1') then
			PHY_CONFIG_CHANGE_PENDING <= '0';
		end if;
	end if;
end process;

-- enact the configuration
PHY_CONFIG_CHANGE_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or ((INITIAL_CONFIG_PULSE = '1') and (PHY_CONFIG_CHANGE_PENDING = '0')) then
			-- A default configuration is loaded automatically after power up. 
			PHY_CONFIG_CHANGE_A <= '1';
			PHY_RESET_A <= '0';	-- no software PHY reset, we just did a hardware reset
			SPEED_A <= "11";		-- auto
			DUPLEX_A <= '1';		
			TEST_MODE_A <= "00";
			POWER_DOWN_A <= '0';
			if(TARGET_PHY = 2) then
			    -- default for KSZ9021 PHY
                CLK_SKEW1_A <= x"E6E3";  --  (INTERNAL_PHY_CLKS_DELAYS => '0')
                CLK_SKEW2_A <= x"6666";
            elsif(TARGET_PHY = 3) then
			     -- default for Micrel KSZ9031 and more standard RGMII PHYs (10 LSbs used)
			     CLK_SKEW1_A <= x"03FF";
			     CLK_SKEW2_A <= x"03FF";
			end if;
		elsif(PHY_CONFIG_CHANGE_PENDING = '1') and (RESET_COMPLETE = '1') then
			-- PHY_CONFIG_CHANGE indicates a user-triggered configuration change.
			PHY_CONFIG_CHANGE_A <= '1';
			PHY_RESET_A <= PHY_RESET;
			SPEED_A <= SPEED;
			DUPLEX_A <= DUPLEX;
			TEST_MODE_A <= TEST_MODE;
			POWER_DOWN_A <= POWER_DOWN;
			CLK_SKEW1_A <= CLK_SKEW1;
			CLK_SKEW2_A <= CLK_SKEW2;
		else
			PHY_CONFIG_CHANGE_A <= '0';
		end if;
	end if;
end process;


-- PHY monitoring and control
PHY_CONFIG_2: if(TARGET_PHY = 2) generate
    -- Micrel KSZ9021 PHY
    Inst_PHY_CONFIG: PHY_CONFIG_KSZ9021 
    GENERIC MAP(
        PHY_ADDR => PHY_ADDR,
		  NO_1000 => NO_1000
    )
    PORT MAP(
        SYNC_RESET => SYNC_RESET,  
        CLK => CLK,
        CONFIG_CHANGE => PHY_CONFIG_CHANGE_A,
        PHY_RESET => PHY_RESET_A,
        SPEED => SPEED_A,
        DUPLEX => DUPLEX_A,
        TEST_MODE => TEST_MODE_A,
        POWER_DOWN => POWER_DOWN_A,
        CLK_SKEW1 => CLK_SKEW1_A,
        CLK_SKEW2 => CLK_SKEW2_A,
        SREG_READ_START => SREG_READ_START,
        SREG_REGAD => "000000010",	-- register 2: PHY Identifier 1
    --	SREG_REGAD => "000000001",	-- register 1: Status word (link status, etc) 
    --	SREG_REGAD => "100000001",	-- register 257: Strap status (extended register)
    --	SREG_REGAD => "100000000",	-- register 256: common control (extended register)
        SREG_DATA => PHY_ID_LOCAL,
        SREG_SAMPLE_CLK => SREG_SAMPLE_CLK,
        LINK_STATUS => LINK_STATUS_FROM_PHY,	-- generic method: read basic status register 1 (works for all PHYs)
        MCLK => MCLK,
        MDIO => MDIO,
        TP => PHY_CONFIG_TP
    );
end generate;
PHY_CONFIG_3: if(TARGET_PHY = 3) generate
    -- Micrel KSZ9031 and more standard RGMII PHYs 
    Inst_PHY_CONFIG: PHY_CONFIG 
    GENERIC MAP(
        PHY_ADDR => PHY_ADDR
    )
    PORT MAP(
        SYNC_RESET => SYNC_RESET,  
        CLK => CLK,
        CONFIG_CHANGE => PHY_CONFIG_CHANGE_A,
        PHY_RESET => PHY_RESET_A,
        SPEED => SPEED_A,
        DUPLEX => DUPLEX_A,
        TEST_MODE => TEST_MODE_A,
        POWER_DOWN => POWER_DOWN_A,
        CLK_SKEW1 => CLK_SKEW1_A,
        CLK_SKEW2 => CLK_SKEW2_A,
        SREG_READ_START => SREG_READ_START,
        SREG_REGAD => "000000010",	-- register 2: PHY Identifier 1
    --	SREG_REGAD => "000000001",	-- register 1: Status word (link status, etc) 
    --	SREG_REGAD => "100000001",	-- register 257: Strap status (extended register)
    --	SREG_REGAD => "100000000",	-- register 256: common control (extended register)
        SREG_DATA => PHY_ID_LOCAL,
        SREG_SAMPLE_CLK => SREG_SAMPLE_CLK,
        LINK_STATUS => LINK_STATUS_FROM_PHY,	-- generic method: read basic status register 1 (works for all PHYs)
        MCLK => MCLK,
        MDIO => MDIO,
        TP => PHY_CONFIG_TP
    );
end generate;



PHY_ID <= PHY_ID_LOCAL;

-- Special cases
LINK_STATUS_local <= '1' when (SIMULATION = '1') else	-- fake link status during simulation
							'0' when (SPEED_STATUS_local = "11") else -- Force link status to '0' when there is no PHY CLK
							LINK_STATUS_FROM_PHY;	-- otherwise, ask the PHY for LINK_STATUS

-- read PHY identification once at power-up or reset (hardware self-test)
PHY_STATUS_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') or (PHY_CONFIG_CHANGE_A = '1') then	-- power-up/reset
			SREG_READ_START <= '1';				-- start asking for status register
		elsif(SREG_SAMPLE_CLK = '1') then
			SREG_READ_START <= '0';
		end if;
	end if;
end process;
--//  PHY INTERFACE: RGMII FORMATTING ----------------------------------------------------------
-- Adjust the TXC and RXC clock 2ns delays within as needed.
PHY_IF_WRAPPER_RESET <= SYNC_RESETRX;	

IF_SEL_001: if(MII_SEL = '0') generate
	Inst_RGMII_WRAPPER: RGMII_WRAPPER 
	GENERIC MAP(
		PLATFORM => PLATFORM,
		INTERNAL_PHY_TX_CLKS_DELAYS => INTERNAL_PHY_TX_CLKS_DELAYS,
		INTERNAL_PHY_RX_CLKS_DELAYS => INTERNAL_PHY_RX_CLKS_DELAYS,
		CLK_FREQUENCY => CLK_FREQUENCY	-- user clock frequency in MHz
	)
	PORT MAP(
		SYNC_RESET => PHY_IF_WRAPPER_RESET,
		CLK => CLK,		-- user-side clock
		CLK125_0 => CLK125_0,
		CLK125_1 => CLK125_1,
		CLK25 => CLK25,
		TXC => TXC,    -- PHY interface -----------------
		TXD => TXD,
		TX_CTL => TX_CTL,
		RXC => RXC,
		RXD => RXD,
		RX_CTL => RX_CTL,
		CRS => CRS,
		COL => COL, 
		MAC_RXD => MAC_RXD,  -- MAC interface -------------
		MAC_RX_SAMPLE_CLK => MAC_RX_SAMPLE_CLK,
		MAC_RX_DV => MAC_RX_DV,
		MAC_RX_ER => MAC_RX_ER,
		RXCG_OUT => RX_CLKG, -- clock from PHY, global (BUFG within)
		TXCG_OUT => TX_CLKG, -- global (BUFG within)
		MAC_TXD => MAC_TXD,  
		MAC_TX_EN => MAC_TX_EN,
		MAC_TX_ER => MAC_TX_ER,
		MAC_TX_SAMPLE_CLK => MAC_TX_SAMPLE_CLK,
		TX_SPEED => TX_SPEED,
		SPEED_STATUS => SPEED_STATUS_local,
		TP => TP_RGMII_WRAPPER
	);
end generate;

-- report to user
LINK_STATUS <= LINK_STATUS_local;
DUPLEX_STATUS <= DUPLEX;	-- fake it (? still searching for a more generic code)
SPEED_STATUS <= SPEED_STATUS_local;

--//  PHY INTERFACE: GMII/MII FORMATTING ----------------------------------------------------------

IF_SEL_002: if(MII_SEL = '1') generate
	Inst_GMII_MII_WRAPPER: GMII_MII_WRAPPER 
	GENERIC MAP(
		CLK_FREQUENCY => CLK_FREQUENCY	-- user clock frequency in MHz
	)
	PORT MAP(
		SYNC_RESET => PHY_IF_WRAPPER_RESET,
		CLK => CLK,		-- user-side clock
		TX_CLK => MII_TX_CLK,  -- PHY interface -----------------
		GTX_CLK => GMII_TX_CLK, 
		TXD => GMII_MII_TXD,
		TX_EN => GMII_MII_TX_EN,
		TX_ER => GMII_MII_TX_ER,
		RX_CLK => GMII_MII_RX_CLK,
		RXD => GMII_MII_RXD,
		RX_DV => GMII_MII_RX_DV,
		RX_ER => GMII_MII_RX_ER,
		CRS => GMII_MII_CRS,
		COL => GMII_MII_COL, 
		MAC_RXD => MAC_RXD,  -- MAC interface -------------
		MAC_RX_DV => MAC_RX_DV,
		MAC_RX_ER => MAC_RX_ER,
		MAC_RX_SAMPLE_CLK => MAC_RX_SAMPLE_CLK,
		RXCG_OUT => RX_CLKG,  -- clock from PHY, global (BUFG within) 
		TXCG_OUT => TX_CLKG,  -- clock from PHY, global (BUFG within), can be same as RXCG (1000) or independent (10/100)
		MAC_TXD => MAC_TXD,
		MAC_TX_EN => MAC_TX_EN,
		MAC_TX_ER => MAC_TX_ER,
		MAC_TX_SAMPLE_CLK => MAC_TX_SAMPLE_CLK,
		MAC_TX_SPEED => TX_SPEED,
		MAC_CRS => CRS,
		MAC_COL => COL,
		SPEED_STATUS => SPEED_STATUS_local
	);
end generate;



--//  TX ELASTIC BUFFER ----------------------------------------------------------
-- The purpose of the elastic buffer is two-fold:
-- (a) a transition between the CLK-synchronous user side, and the RX_CLKG synchronous PHY side
-- (b) storage for Ethernet transmit frames, to absorb traffic peaks, minimize the number of 
-- UDP packets lost at high throughput.
-- The tx elastic buffer is 16Kbits, large enough for TWO complete maximum size 
-- (14addr+1500data+4FCS = 1518B) frames.

-- write pointer management
MAC_TX_WPTR_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			TX_COUNTER8 <= (others => '0');
			MAC_TX_WPTR <= (others => '0');
			MAC_TX_WPTR_D <= (others => '0');
			MAC_TX_WPTR_STABLE <= '0';
		else
			TX_COUNTER8 <= TX_COUNTER8 + 1;

			if(MAC_TX_DATA_VALID = '1') then
				MAC_TX_WPTR <= MAC_TX_WPTR + 1;
			end if;
			
			-- update WPTR_D once every 8 clocks.
			if(TX_COUNTER8 = 7) then
				MAC_TX_WPTR_D <= MAC_TX_WPTR;
			end if;
			
			-- allow WPTR reclocking with another clock, as long as it is away from the transition area
			if(TX_COUNTER8 < 6) then
				MAC_TX_WPTR_STABLE <= '1';
			else 
				MAC_TX_WPTR_STABLE <= '0';
			end if;
		end if;
	end if;
end process;

-- select which RAMBlock to write to.
MAC_TX_WEA(0) <= MAC_TX_DATA_VALID and (not MAC_TX_WPTR(11));
MAC_TX_WEA(1) <= MAC_TX_DATA_VALID and MAC_TX_WPTR(11);

MAC_TX_DIA <=  MAC_TX_EOF & MAC_TX_DATA;

-- No need for initialization
RAMB16_X: for I in 0 to 1 generate
	RAMB16_S9_S9_001: BRAM_DP 
	GENERIC MAP(
		DATA_WIDTHA => 9,		
		ADDR_WIDTHA => 11,
		DATA_WIDTHB => 9,		 
		ADDR_WIDTHB => 11

	)
	PORT MAP(
		CLKA => CLK,
		WEA => MAC_TX_WEA(I),      -- Port A Write Enable Input
		ADDRA => MAC_TX_WPTR(10 downto 0),  -- Port A 11-bit Address Input
		DIA => MAC_TX_DIA,      -- Port A 9-bit Data Input
		DOA => open,
		CLKB => TX_CLKG,
		WEB => '0',
		ADDRB => MAC_TX_RPTR(10 downto 0),  -- Port B 11-bit Address Input
		DIB => "000000000",      -- Port B 9-bit Data Input
		DOB => MAC_TX_DOB(I)      -- Port B 9-bit Data Output
	);
	
end generate;

MAC_TX_DATA2 <= MAC_TX_DOB(conv_integer(MAC_TX_RPTR_D(11)))(7 downto 0);
MAC_TX_EOF2 <= MAC_TX_DOB(conv_integer(MAC_TX_RPTR_D(11)))(8); 

-- TX_CLKG zone. Reclock WPTR
MAC_TX_WPTR_002: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if(SYNC_RESETTX = '1') then
			MAC_TX_WPTR_STABLE_D <= '0';
			MAC_TX_WPTR_D2 <= (others => '0');
			MAC_TX_WPTR_D3 <= (others => '0');
		else
			MAC_TX_WPTR_STABLE_D <= MAC_TX_WPTR_STABLE;
			MAC_TX_WPTR_D2 <= MAC_TX_WPTR_D;
			
			if(MAC_TX_WPTR_STABLE_D = '1') then
				-- WPTR is stable. OK to resample with the RX_CLKG clock.
				MAC_TX_WPTR_D3 <= MAC_TX_WPTR_D2;
			end if;
		end if;
	end if;
end process;

MAC_TX_BUF_SIZE <= MAC_TX_WPTR_D3 + not(MAC_TX_RPTR);
-- occupied tx buffer size for reading purposes (CLKG clock domain)(
-- always lags, could be a bit more, never less.

--//-- TX FLOW CONTROL --------------------------------
-- ask for more input data if there is room for at least 1K more input bytes
-- Never write past the last confirmed read pointer location.

-- read the last confirmed read pointer location and reclock in CLK domain when stable
MAC_TX_CTS_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			TX_SUCCESS_TOGGLE_D <= '0';
			TX_SUCCESS_TOGGLE_D2 <= '0';
			MAC_TX_RPTR_CONFIRMED_D <= (others => '1');
		else
			TX_SUCCESS_TOGGLE_D <= TX_SUCCESS_TOGGLE;
			TX_SUCCESS_TOGGLE_D2 <= TX_SUCCESS_TOGGLE_D;
			if(TX_SUCCESS_TOGGLE_D2 /= TX_SUCCESS_TOGGLE_D) then
				-- shortly after successful packet transmission. 
				MAC_TX_RPTR_CONFIRMED_D <= MAC_TX_RPTR_CONFIRMED;
			end if;
		end if;
	end if;
end process;

-- Compute available room for more tx data
MAC_TX_CTS_002: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_TX_BUF_FREE <= (others => '0');
		else
			MAC_TX_BUF_FREE <= not (MAC_TX_WPTR_D2 + not MAC_TX_RPTR_CONFIRMED_D);
		end if;
	end if;
end process;

-- Is there enough room for a complete max size frame?
-- Don't cut it too close because user interface can flood the buffer very quickly (CLK @ 125 MHz clock)
-- while we compute the buffer size with the possibly much slower RX_CLG (could be 2.5 MHz for 10Mbps).
MAC_TX_CTS_003: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_TX_CTS_local <= '0';	-- reset
		elsif(LINK_STATUS_local = '0') then
			-- don't ask the stack for data if there is no link
			MAC_TX_CTS_local <= '0';	-- reset
		elsif(MAC_TX_BUF_FREE(11) = '0') then
			-- room for less than 2KB. Activate flow control
			MAC_TX_CTS_local <= '0';
		else
			MAC_TX_CTS_local <= '1';
		end if;
	end if;
end process;
MAC_TX_CTS <= MAC_TX_CTS_local;


-- manage read pointer
MAC_TX_RPTR_001: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if(SYNC_RESETTX = '1') then
			MAC_TX_RPTR <= (others => '1');
			MAC_TX_RPTR_D <= (others => '1');
		else
			MAC_TX_RPTR_D <= MAC_TX_RPTR;
			
			if(SYNC_RESETRX = '1') then
				MAC_TX_RPTR <= (others => '1');
			elsif(TX_STATE = 1) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(2 downto 0) <= 1) then
				-- read the first byte(s) in advance (need 2 RX_CLKG to get the data out)
				-- Note: we may temporarily read past the write pointer (by one location) 
				-- but will rewind immediately thereafter
				MAC_TX_RPTR <= MAC_TX_RPTR + 1;
			elsif(TX_STATE = 2) and (TX_EVENT3 = '1') then
				-- we are done reading the packet. rewind the read pointer, as we went past the end of packet.
				MAC_TX_RPTR <= MAC_TX_RPTR - 1;
			elsif(TX_STATE = 2) and (TX_BYTE_CLK = '1') then
				-- read the rest of the packet
				-- forward data from input elastic buffer to RGMII interface
				-- Note: we may temporarily read past the write pointer (by one location) 
				-- but will rewind immediately thereafter
				MAC_TX_RPTR <= MAC_TX_RPTR + 1;
			elsif(TX_STATE = 6) then
				-- collision detected. rewind read pointer to the start of frame.
				MAC_TX_RPTR <= MAC_TX_RPTR_CONFIRMED;
			end if;
		end if;
	end if;
end process;

-- update confirmed read pointer after successful frame transmission
MAC_TX_RPTR_002: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if(SYNC_RESETTX = '1') then
			MAC_TX_RPTR_CONFIRMED <= (others => '1');
			TX_SUCCESS_TOGGLE <= '0';
		elsif(TX_SUCCESS = '1') then
			MAC_TX_RPTR_CONFIRMED <= MAC_TX_RPTR;
			TX_SUCCESS_TOGGLE <= not TX_SUCCESS_TOGGLE;
		end if;
	end if;
end process;

-- How many COMPLETE tx frames are available for transmission in the input elastic buffer?
-- Transmission is triggered by the availability of a COMPLETE frame in the buffer (not just a few frame bytes)
-- It is therefore important to keep track of the number of complete frames.
-- At the elastic buffer input, a new complete frame is detected upon receiving the EOF pulse.
COMPLETE_TX_FRAMES_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_TX_EOF_TOGGLE <= '0';
		elsif(MAC_TX_DATA_VALID = '1') and (MAC_TX_EOF = '1') then
			MAC_TX_EOF_TOGGLE <= not MAC_TX_EOF_TOGGLE;  -- Need toggle signal to generate copy in RX_CLKG clock domain
		end if;
	end if;
end process;


COMPLETE_TX_FRAMES_002: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if(SYNC_RESETTX = '1') then
			MAC_TX_EOF_TOGGLE_D <= '0';
			MAC_TX_EOF_TOGGLE_D2 <= '0';
			COMPLETE_TX_FRAMES_INBUF <= (others => '0');
		else
			MAC_TX_EOF_TOGGLE_D <= MAC_TX_EOF_TOGGLE;	-- reclock in TX_CLKG clock domain (to prevent glitches)
			MAC_TX_EOF_TOGGLE_D2 <= MAC_TX_EOF_TOGGLE_D;

			if(MAC_TX_EOF_TOGGLE_D2 /= MAC_TX_EOF_TOGGLE_D) and (TX_SUCCESS = '0') then
				-- just added another complete frame into the tx buffer (while no successful transmission concurrently)
				COMPLETE_TX_FRAMES_INBUF <= COMPLETE_TX_FRAMES_INBUF + 1;
			elsif(MAC_TX_EOF_TOGGLE_D2 = MAC_TX_EOF_TOGGLE_D) and (TX_SUCCESS = '1') 
					and (ATLEAST1_COMPLETE_TX_FRAME_INBUF = '1') then
				-- a frame was successfully transmitted (and none was added at the very same instant)
				COMPLETE_TX_FRAMES_INBUF <= COMPLETE_TX_FRAMES_INBUF - 1;
			end if;
		end if;
	end if;
end process;

-- Flag to indicate at least one complete tx frame in buffer.
ATLEAST1_COMPLETE_TX_FRAME_INBUF <= '0' when (COMPLETE_TX_FRAMES_INBUF = 0) else '1';


DELAY_EOF2: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if(SYNC_RESETTX = '1') then
			MAC_TX_EOF2_D <= '0';
		elsif(TX_BYTE_CLK = '1') then
			-- delay by one byte
			MAC_TX_EOF2_D <= MAC_TX_EOF2;
		end if;
	end if;
end process;


--// MAC TX STATE MACHINE ----------------------------------------------------------
TX_SPEED <= SPEED_STATUS_local;  -- transmit speed is as auto-negotiated by the rx PHY.

-- Tx timers ------------------------
-- Generate transmit clock. Depends on the tx_speed.
-- Clock is always enabled, even when not transmitting (reason: we need to be
-- able to convey to the RGMII wrapper when to stop, etc).
-- Important distinction between TX_CLK and TX_BYTE_CLK because GMII interface is 4-bit wide
-- for 10/100 Mbps and 8-bit wide for 1000 Mbps.
-- Thus, TX_BYTE_CLK is half the frequency of TX_CLK, but pulses are aligned.
TX_CLK_GEN_001: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if (SYNC_RESETTX = '1') then
			TX_BYTE_CLK <= '0';
			TX_HALF_BYTE_FLAG <= '0';
		elsif (TX_SPEED = "10") then
			-- 1000 Mbps
			TX_BYTE_CLK <= '1';
		elsif (TX_SPEED = "01") or (TX_SPEED = "00") then
			-- 10/100 Mbps. 
			-- divide by two to get the byte clock when 10/100 Mbps
			TX_HALF_BYTE_FLAG <= not TX_HALF_BYTE_FLAG;
			if(TX_HALF_BYTE_FLAG = '1') then
				TX_BYTE_CLK <= '1';
			else
				TX_BYTE_CLK <= '0';
			end if;
		else
			TX_BYTE_CLK <= '0';
		end if;	
	end if;
end process;


-- 96-bit InterPacketGap (Interframe Delay) timer
IPG_001: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if (SYNC_RESETRX = '1') then
			IPG_CNTR <= (others => '0');
			CRS_D <= '0';
		else
			CRS_D <= CRS;  -- reclock with TX_CLKG

			if((CRS_D = '1') and (DUPLEX = '0')) or (TX_EN = '1') or (TX_STATE = 5) then
				-- detected passing packet (half-duplex only) or transmission is in progress 
				-- or carrier extension in progress
				-- Arm InterPacketGap timer
				IPG_CNTR <= x"0C"  ; -- 96 bits = 12 bytes  802.3 section 4.4.2
			elsif(IPG_CNTR > 0) and (TX_BYTE_CLK = '1') then
				-- after end of passing packet, decrement counter downto to zero (InterPacketGap).
				IPG_CNTR <= IPG_CNTR - 1;
			end if;
		end if;
	end if;
end process;
IPG <= '1' when (IPG_CNTR = 0) else '0';  -- '1' last passing packet was more than InterPacketGap ago. OK to start tx.

-- Events ------------------------
-- First tx packet trigger
TX_EVENT1 <= '0' when (ATLEAST1_COMPLETE_TX_FRAME_INBUF = '0') else -- no COMPLETE frame in tx input buffer
				 '0' when (MAC_TX_BUF_SIZE = 0) else -- no data in tx input buffer
				 '0' when (IPG = '0') else -- medium is not clear. need to wait after the InterPacketGap. Deferring on.
				 '0' when (TX_SUCCESS = '1') else  -- don't act too quickly. It takes one RX_CLKG to update the complete_tx_frame_inbuf counter.
				 '0' when (PHY_IF_WRAPPER_RESET = '1') else -- PHY/RGMII wrapper are being reset. Do not start tx.
				 TX_BYTE_CLK;  -- go ahead..start transmitting. align event pulse with TX_BYTE_CLK
				 
-- collision detection, half-duplex mode, within the timeSlot
-- Timeslot is 64 bytes for 10/100 Mbps and 512 bytes for 1000 Mbps, starting at the preamble.
TX_EVENT2 <= '1' when ((COL = '1') and (DUPLEX = '0') and (TX_SPEED = "10") and (TX_BYTE_COUNTER(10 downto 0) < 503)) else
				 '1' when ((COL = '1') and (DUPLEX = '0') and (TX_SPEED(1) = '0') and (TX_BYTE_COUNTER(10 downto 0) < 55)) else
				 '0';
				 
-- end of frame detected at tx buffer output. 
-- Timing depends on the TX_SPEED (because of the delay in reading data from tx buffer output)
TX_EVENT3 <= '1' when (TX_BYTE_CLK = '1') and (MAC_TX_EOF2 = '1') and (TX_SPEED = "10") else
			    '1' when (TX_BYTE_CLK = '1') and (MAC_TX_EOF2_D = '1') and (TX_SPEED(1) = '0') else
				 '0';

-- Tx state machine ------------------------
TX_STATE_GEN_001: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if (SYNC_RESETTX = '1') or (LINK_STATUS_local = '0') then
			TX_STATE <= 0;	-- idle state
			TX_SUCCESS <= '0'; 
			TX_BYTE_CLK_D <= '0';
			RETX_ATTEMPT_COUNTER <= (Others => '0');  -- re-transmission attempts counter
		else

			TX_BYTE_CLK_D <= TX_BYTE_CLK;  -- output byte ready one RX_CLKG later 
			
			if(TX_STATE = 0) then
				TX_SUCCESS <= '0'; 
				RETX_ATTEMPT_COUNTER <= (Others => '0');  -- reset re-transmission attempts counter
				if (TX_EVENT1 = '1') then
					-- start tx packet: send 1st byte of preamble
					TX_STATE <= 1; 
					TX_BYTE_COUNTER2 <= "111"; -- 8-byte preamble + start of frame sequence
				end if;
			elsif(TX_STATE = 1) and (DUPLEX = '0') and (COL = '1')  then
				-- collision sensing while in half-duplex mode. 
				-- The packet header being transmitted is well within the slot time limit.
				TX_STATE <= 6;  -- send jam
				TX_BYTE_COUNTER2 <= "011"; -- jamSize = 32 bits = 4 Bytes
			elsif(TX_STATE = 1) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(2 downto 0) /= 0) then
				-- counting through the preamble + start frame sequence
				TX_BYTE_COUNTER2 <= TX_BYTE_COUNTER2 - 1;
			elsif(TX_STATE = 1) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(2 downto 0) = 0) then
				-- end of preamble. start forwarding data from elastic buffer to RGMII wrapper
				TX_STATE <= 2; 
				TX_BYTE_COUNTER <= (others => '0');
			elsif(TX_STATE = 2) and (TX_EVENT2 = '1') then
				-- collision sensing while in half-duplex mode and within the specified slot time (starting at the preamble)
				TX_STATE <= 6;  -- send jam
				TX_BYTE_COUNTER2 <= "011"; -- jamSize = 32 bits = 4 Bytes
			elsif(TX_STATE = 2) and (TX_BYTE_CLK = '1') and (TX_EVENT3 = '0') then
				-- keep track of the payload byte count (to detect the need for padding)
				TX_BYTE_COUNTER <= TX_BYTE_COUNTER + 1;
			elsif(TX_STATE = 2) and (TX_BYTE_CLK = '1') and (TX_EVENT3 = '1')  then
				-- found end of frame
				TX_BYTE_COUNTER <= TX_BYTE_COUNTER + 1;
				if (TX_BYTE_COUNTER(10 downto 0) < 59) then 
					if (MAC_TX_CONFIG(1 downto 0) = "11") then
						-- frame is too short: payload data does not meet minimum 60-byte size.
						-- user enabled automatic padding and automatic CRC32 insertion
						TX_STATE <= 3;
					else
						-- error: frame is too short. abort.
						TX_STATE <= 10;
					end if;
				elsif (MAC_TX_CONFIG(1) = '1') then
					-- user enabled auto-CRC32 insertion. Start inserting CRC
					TX_STATE <= 4;
					TX_BYTE_COUNTER2 <= "011";	-- 4-byte CRC(FCS)
				elsif (TX_BYTE_COUNTER(10 downto 0) >= 63) then
					-- complete packet (including user-supplied CRC)
					-- Carrier Extension?  Applicable to 1000 Mbps half-duplex
					if(TX_SPEED = "10") and (DUPLEX = '0') and (TX_BYTE_COUNTER(10 downto 0) < 511) then
						-- Carrier extension to slotTime (512 bytes) as per 802.3 Section 4.2.3.4
						TX_STATE <= 5;
					else
						-- we are done here
						TX_STATE <= 0;
						TX_SUCCESS <= '1'; -- completed frame transmission
					end if;
				else
					-- error. frame is too short (< 64 bytes including 4-byte CRC). abort.
					TX_STATE <= 10;
				end if;
			elsif(TX_STATE = 3) and (TX_EVENT2 = '1') then
				-- collision sensing while in half-duplex mode and within the specified slot time (starting at the preamble)
				TX_STATE <= 6;  -- send jam
				TX_BYTE_COUNTER2 <= "011"; -- jamSize = 32 bits = 4 Bytes
			elsif(TX_STATE = 3) and (TX_BYTE_CLK = '1') then
				TX_BYTE_COUNTER <= TX_BYTE_COUNTER + 1;
				if(TX_BYTE_COUNTER(10 downto 0) < 59) then
					-- padding payload field to the minimum size.
					-- keep track of the byte count 
				elsif (MAC_TX_CONFIG(1) = '1') then
					-- Completed padding. User enabled CRC32 insertion. Start inserting CRC
					TX_STATE <= 4;
					TX_BYTE_COUNTER2 <= "011";	-- 4-byte CRC(FCS)
				else
					-- error. Illegal user configuration. auto-pad requires auto-CRC. abort.
					TX_STATE <= 10;
				end if;
			elsif(TX_STATE = 4) and (TX_EVENT2 = '1') then
				-- collision sensing while in half-duplex mode and within the specified slot time (starting at the preamble)
				TX_STATE <= 6;  -- send jam
				TX_BYTE_COUNTER2 <= "011"; -- jamSize = 32 bits = 4 Bytes
			elsif(TX_STATE = 4) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(1 downto 0) /= 0) then
				-- counting through the CRC/FCS sequence
				TX_BYTE_COUNTER2 <= TX_BYTE_COUNTER2 - 1;
				TX_BYTE_COUNTER <= TX_BYTE_COUNTER + 1;
			elsif(TX_STATE = 4) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(1 downto 0) = 0) then
				-- end of CRC/FCS. Packet is now complete. 
				TX_BYTE_COUNTER <= TX_BYTE_COUNTER + 1;
				-- Carrier Extension?  Applicable to 1000 Mbps half-duplex
				if(TX_SPEED = "10") and (DUPLEX = '0') and (TX_BYTE_COUNTER(10 downto 0) < 511) then
					-- Carrier extension to slotTime (512 bytes) as per 802.3 Section 4.2.3.4
					TX_STATE <= 5;
				else
					-- we are done here
					TX_STATE <= 0;
					TX_SUCCESS <= '1'; -- completed frame transmission
				end if;
			elsif(TX_STATE = 5) and (TX_EVENT2 = '1') then
				-- collision sensing while in half-duplex mode and within the specified slot time (starting at the preamble)
				TX_STATE <= 6;  -- send jam
				TX_BYTE_COUNTER2 <= "011"; -- jamSize = 32 bits = 4 Bytes
			elsif(TX_STATE = 5) and (TX_BYTE_CLK = '1') then
				-- Carrier extension
				TX_BYTE_COUNTER <= TX_BYTE_COUNTER + 1;
				if(TX_BYTE_COUNTER(10 downto 0) >= 511) then
					-- met slotTime requirement.
					TX_STATE <= 0;
					TX_SUCCESS <= '1'; -- completed frame transmission
				end if;
			elsif(TX_STATE = 6) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(1 downto 0) /= 0) then
				-- Jam . counting through the 4-byte jam
				TX_BYTE_COUNTER2 <= TX_BYTE_COUNTER2 - 1;
			elsif(TX_STATE = 6) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(1 downto 0) = 0) then
				-- end of Jam

				-- re-transmit?
				if(RETX_ATTEMPT_COUNTER < 16) then
					-- we have not yet reached the attemptLimit
					TX_STATE <= 7;  -- backoff 
					RETX_ATTEMPT_COUNTER <= RETX_ATTEMPT_COUNTER + 1;
					-- set backoff
					if(TX_SPEED = "10") then
						-- 1000 Mbps. Backoff is an integer multiple of slotTime: 
						-- random * slotTime. slotTime = 512 Bytes
						TX_BYTE_COUNTER(8 downto 0) <= (others => '1');
						TX_BYTE_COUNTER(18 downto 9) <= RETX_RANDOM_BKOFF;  -- uniform random variable. range 0 - 1023
					elsif(TX_SPEED(1) = '0') then
						-- 10/100 Mbps. Backoff is an integer multiple of slotTime:
						-- random * slotTime. slotTime = 64 Bytes
						TX_BYTE_COUNTER(5 downto 0) <= (others => '1');
						TX_BYTE_COUNTER(15  downto 6) <= RETX_RANDOM_BKOFF;  -- uniform random variable. range 0 - 1023;
						TX_BYTE_COUNTER(18 downto 16) <= (others => '0');
					end if;
				else
					TX_STATE <= 10;	-- error. could not transmit packet
				end if;
			elsif(TX_STATE = 7) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER /= 0) then
				-- backoff timer
				TX_BYTE_COUNTER <= TX_BYTE_COUNTER - 1;
			elsif(TX_STATE = 7) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER = 0) then
				-- backoff timer expired. try sending again
				-- start tx packet: send 1st byte of preamble
				TX_STATE <= 1; 
				TX_BYTE_COUNTER2 <= "111"; -- 8-byte preamble + start of frame sequence
			end if;
		end if;
	end if;
end process;

-- Tx packet assembly ------------------------
-- generate 7-byte preamble, 1-byte start frame sequence
-- TX_PREAMBLE is aligned with TX_BYTE_CLK_D
PREAMBLE_GEN_001: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if (SYNC_RESETTX = '1') then
			TX_PREAMBLE <= (others => '0');
		elsif(TX_STATE = 1) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(2 downto 0) = 1) then
			TX_PREAMBLE <= "11010101";  -- start frame delimiter (SFD). 
			-- [note: standard shows LSb D0 to the left, MSb D7 to the right, as per serial transmission sequence.]
		elsif(TX_BYTE_CLK = '1') then
			-- new packet or re-transmission
			TX_PREAMBLE <= "01010101";  -- preamble
			-- [note: standard shows LSb to the left, MSb to the right, as per serial transmission sequence.]
		end if;
	end if;
end process;

-- mux 4-byte frame check sequence
-- TX_FCS is aligned with TX_BYTE_CLK_D
FCS_GEN_001: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		-- send MSB first (802.11 Section 3.2.9).
		-- Don't have time to reclock TX_CRC32_FLIPPED_INV(31 downto 24): will be muxed without reclocking.
		if (SYNC_RESETTX = '1') then
			TX_FCS <= (others => '0');
		elsif(TX_STATE = 4) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(1 downto 0) = 3) then
			TX_FCS <= TX_CRC32_FLIPPED_INV(23 downto 16);	
		elsif(TX_STATE = 4) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(1 downto 0) = 2) then
			TX_FCS <= TX_CRC32_FLIPPED_INV(15 downto 8);	
		elsif(TX_STATE = 4) and (TX_BYTE_CLK = '1') and (TX_BYTE_COUNTER2(1 downto 0) = 1) then
			TX_FCS <= TX_CRC32_FLIPPED_INV(7 downto 0);
		end if;
	end if;
end process;


TX_EN <= '0' when (TX_STATE = 0) else  -- idle
			'1' when (TX_STATE < 5) else  -- normal transmission
			'1' when (TX_STATE = 6) else  -- 32-bit jam after collision detection
			'0';
			
TX_ER <= '1' when (TX_STATE = 5) else  -- carrier extension
			'0';

-- mux preamble, start frame sequence, data, fcs, etc and forward to GMII tx interface
-- MAC_TX_DATA4 is aligned with TX_BYTE_CLK_D
TX_MUX_001: process(TX_STATE, TX_BYTE_COUNTER2, TX_PREAMBLE, MAC_TX_DATA2, TX_CRC32_FLIPPED_INV, TX_FCS)
begin
	if(TX_STATE = 1) then
		-- 7-byte preamble and 1-byte start frame sequence
		MAC_TX_DATA4 <= TX_PREAMBLE;
	elsif(TX_STATE = 2) then
		-- payload data
		MAC_TX_DATA4 <= MAC_TX_DATA2;
	elsif(TX_STATE = 3) then
		-- padding
		MAC_TX_DATA4 <= x"00";  -- padding with zeros.
	elsif(TX_STATE = 4) and (TX_BYTE_COUNTER2(1 downto 0) = 3) then
		-- Frame Check Sequence
		MAC_TX_DATA4 <= TX_CRC32_FLIPPED_INV(31 downto 24);	-- no time to reclock. need it now
	elsif(TX_STATE = 4) and (TX_BYTE_COUNTER2(1 downto 0) < 3) then
		-- Frame Check Sequence
		MAC_TX_DATA4 <= TX_FCS;
	elsif(TX_STATE = 5) then
		-- carrier extend
		MAC_TX_DATA4 <= x"0F";  
	else
	-- TODO tail end
		MAC_TX_DATA4 <= x"00";
	end if;
end process;

-- signal conditioning for the TX GMII interface
GMII_TX_GEN: process(TX_CLKG)
begin
	if rising_edge(TX_CLKG) then
		if (SYNC_RESETTX = '1') then
			MAC_TXD <= (others => '0');
			MAC_TX_EN <= '0';
			MAC_TX_ER <= '0';
			MAC_TX_SAMPLE_CLK <= '0';
		elsif(TX_BYTE_CLK_D = '1') then
			MAC_TXD <= MAC_TX_DATA4;
			MAC_TX_EN <= TX_EN;
			MAC_TX_ER <= TX_ER;
			MAC_TX_SAMPLE_CLK <= '1';
		else
			MAC_TX_SAMPLE_CLK <= '0';
		end if;
	end if;
end process;

	
-- Tx random backoff ------------------------
-- Use LFSR11 as generator of a uniform random distribution of numbers between 0 and 2047
Inst_LFSR11C: LFSR11C PORT MAP(
	ASYNC_RESET => SYNC_RESETTX,
	CLK => TX_CLKG,
	BIT_CLK_REQ => TX_BYTE_CLK,	-- keep generating new random number
	SYNC_RESET => SYNC_RESETRX,   
	SEED => "00000000001",
	LFSR_BIT => open,
	BIT_CLK_OUT => open,
	SOF_OUT => open,
	LFSR_REG_OUT => RAND
);



-- limit the random number range depending on the number of transmission attempts
-- see 802.3 standard section 4.2.3.2.5 for details.
RETX_RAND_BKOFF_GEN: process(RAND, RETX_ATTEMPT_COUNTER)
begin
	case RETX_ATTEMPT_COUNTER is
		when "00000" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0000000001";  -- first attempt: r = 0,1	
		when "00001" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0000000011";  -- second attempt: r = 0-3
		when "00010" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0000000111";  -- third attempt: r = 0-7	
		when "00011" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0000001111";  -- etc	
		when "00100" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0000011111";  	
		when "00101" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0000111111";  	
		when "00110" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0001111111";  
		when "00111" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0011111111";  	
		when "01000" => RETX_RANDOM_BKOFF <= RAND(9 downto 0) and "0111111111";  	
		when others => RETX_RANDOM_BKOFF <= RAND(9 downto 0);   -- cap range to r = 0-1023  	
	end case;
end process;
		

--//  TX 32-BIT CRC COMPUTATION -------------------------------------------------------
-- 802.3 section 3.2.9: 
-- protected fields: payload data + padding (excludes preamble and start of frame sequence)
MAC_TX_DATA3 <= MAC_TX_DATA2 when (TX_STATE = 2) else x"00";  -- padding with zeros
MAC_TX_SAMPLE3_CLK <= TX_BYTE_CLK_D when ((TX_STATE = 2) or (TX_STATE = 3)) else '0'; 

TX_CRC32_RESET <= '1' when (TX_STATE = 1) else 
						'1' when (SYNC_RESETTX = '1') else
						'0';  -- reset CRC2 during the packet preamble (covers the re-transmission case)

-- latency 1 TX_CLKG
TX_CRC32_8B: CRC32_8B PORT MAP(
	SYNC_RESET => TX_CRC32_RESET,
	CLK => TX_CLKG,
	CRC32_IN => TX_CRC32,  -- feedback previous iteration
	DATA_IN => MAC_TX_DATA3,	
	SAMPLE_CLK_IN => MAC_TX_SAMPLE3_CLK,
	CRC32_OUT => TX_CRC32,
	CRC32_VALID => open
);

-- flip LSb<->MSb and invert
TX_CRC32_002: process(TX_CRC32)
begin
	for I in 0 to 7 loop
		TX_CRC32_FLIPPED_INV(I) <= not TX_CRC32(7 - I);
		TX_CRC32_FLIPPED_INV(I + 8) <= not TX_CRC32(15 - I);
		TX_CRC32_FLIPPED_INV(I + 16) <= not TX_CRC32(23 - I);
		TX_CRC32_FLIPPED_INV(I + 24) <= not TX_CRC32(31 - I);
	end loop;
end process;

--// MAC RX STATE MACHINE ----------------------------------------------------------
-- remember the last rx byte
RX_DELAY_001: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if (SYNC_RESETRX = '1') then
			MAC_RXD_D <= (others => '0');
			LAST6B <= (others => '0');
		elsif (RX_EVENT4 = '1') then
			-- valid byte
			MAC_RXD_D <= MAC_RXD;
			-- remember the last 6 bytes (used during preamble and address field)
			-- shift-in the last 6 bytes (efficient when decoding address field or length/type field)
			-- MSB (47 downto 40) is received first.
			LAST6B(47 downto 8) <= LAST6B(39 downto 0);
			LAST6B(7 downto 0) <= MAC_RXD_D;
		end if;
	end if;
end process;

-- Rx events ------------------------
-- new packet. RX_DV is asserted and detected start of frame delimiter (SFD)
-- Note 6/22/14: in some 10 Mbps cases, the x555555555555 preamble is missing. We therefore 
-- just detect the SFD 0xD5.
RX_EVENT1 <= '1' when (MAC_RX_SAMPLE_CLK = '1') and (MAC_RX_DV = '1') and (MAC_RX_ER = '0') 
							and (MAC_RXD_D = x"D5") and ((LAST6B(23 downto 0) = x"555555") or (SPEED_STATUS_local = "00")) else '0';	
-- false carrier indication  (TODO: what for???)  xE for 10/1000, x0E for 1000 Mbps
--RX_EVENT2 <= '1' when (MAC_RX_SAMPLE_CLK = '1') and (MAC_RX_DV = '0') and (MAC_RX_ER = '1') 
--							and (MAC_RXD(3 downto 0) = x"E")  else '0';	

-- end of frame delimiter
RX_EVENT3 <= '1' when (MAC_RX_SAMPLE_CLK = '1') and (MAC_RX_DV = '0') else
				 '0';
				 
-- valid frame byte (data, padding, crc)
RX_EVENT4 <= '1' when (MAC_RX_SAMPLE_CLK = '1') and (MAC_RX_DV = '1') and (MAC_RX_ER = '0') else
				 '0';
				
-- frame complete, all checks complete
RX_EVENT5 <= MAC_RX_EOF3B_D;



-- Rx state machine ------------------------
RX_BYTE_COUNTER_INC <= RX_BYTE_COUNTER + 1;

RX_STATE_GEN_001: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if (SYNC_RESETRX = '1') then
			RX_STATE <= 0;
			RX_BYTE_COUNTER <= (others => '0');
		elsif(RX_STATE = 0) and (RX_EVENT1 = '1') then
			-- RX_DV is asserted and detected start of frame delimiter (SFD)
			-- Note: the preamble could be full, partial or entirely missing.
			RX_STATE <= 1;
			RX_BYTE_COUNTER <= (others => '0');
		elsif(RX_STATE = 1) and (RX_EVENT3 = '1') then
			-- end of frame delimiter
			RX_STATE <= 2;
		elsif(RX_STATE = 1) and (RX_EVENT4 = '1') then
			-- count bytes within frame
			RX_BYTE_COUNTER <= RX_BYTE_COUNTER_INC;
		elsif(RX_STATE = 2) and (RX_EVENT5 = '1') then
			-- frame complete, all checks complete
			RX_STATE <= 0;
		end if;
	end if;
end process;

-- Assess whether rx frame is too short (collision) or too long?
-- ready at RX_STATE 2
RX_TOO_SHORT_GEN: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if (SYNC_RESETRX = '1') then
			RX_TOO_SHORT <= '0';
			RX_TOO_LONG <= '0';
		elsif(RX_STATE = 1) and (RX_EVENT3 = '1') then
			-- end of frame delimiter
			if(RX_BYTE_COUNTER_INC(18 downto 6) = 0) then  -- < 64 bytes
				-- too short 6+6+2+46+4 = 64
				RX_TOO_SHORT <= '1';
				RX_TOO_LONG <= '0';
			elsif(RX_BYTE_COUNTER_INC(18 downto 11) /= 0) or 
				(RX_BYTE_COUNTER_INC(10 downto 0)> 1518) then  -- > 1518 bytes
				-- too long. 6+6+2+1500+4 = 1418
				RX_TOO_SHORT <= '0';
				RX_TOO_LONG <= '1';
			else
				RX_TOO_SHORT <= '0';
				RX_TOO_LONG <= '0';
			end if;
		end if;
	end if;
end process;

-- Destination address check
ADDR_CHECK_GEN: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if (SYNC_RESETRX = '1') then
			RX_VALID_ADDR <= '0';
		elsif(MAC_RX_CONFIG(0) = '1') then
			-- promiscuous mode. No destination address check
			RX_VALID_ADDR <= '1';
		elsif(RX_STATE = 1) and (RX_EVENT4 = '1') and (RX_BYTE_COUNTER = 6) then
			-- end of destination address field. Check address
			if(LAST6B = MAC_ADDR) then
				-- destination address matches
				RX_VALID_ADDR <= '1';
			elsif (LAST6B = x"FFFFFFFFFFFF") and (MAC_RX_CONFIG(1) = '1') then
				-- accepts broadcast packets with the broadcast destination address FF:FF:FF:FF:FF:FF. 
				RX_VALID_ADDR <= '1';
			elsif (LAST6B(42) = '1') and (MAC_RX_CONFIG(2) = '1') then
				-- accept multicast packets with the multicast bit set in the destination address. 
				-- '1' in the LSb of the first address byte.
				RX_VALID_ADDR <= '1';
		   else
				RX_VALID_ADDR <= '0';
			end if;
		end if;
	end if;
end process;

-- Length/type field check
LENGTH_CHECK_GEN: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if (SYNC_RESETRX = '1') then
			RX_LENGTH_TYPEN <= '0';  
		elsif(RX_STATE = 0) and (RX_EVENT1 = '1') then -- new 8/25/14
			-- assume type field by default at the start of frame
			RX_LENGTH_TYPEN <= '0';  -- length/type field represents a type. ignore the length value.
		elsif(RX_STATE = 1) and (RX_EVENT4 = '1') and (RX_BYTE_COUNTER = 14) then
			-- end of length/type field
			if(LAST6B(15 downto 11) = 0) and (LAST6B(10 downto 0) <= 1500) then
				-- this field is interpreted as "Length" = client data field size
				-- MSB first (802.3 section 3.2.6)
				RX_LENGTH <= LAST6B(10 downto 0);  
				RX_LENGTH_TYPEN <= '1';  -- length/type field represents a length
			else
				RX_LENGTH_TYPEN <= '0';  -- length/type field represents a type. ignore the length value.
			end if;
		end if;
	end if;
end process;

-- compute the difference between RX_BYTE_COUNTER and RX_LENGTH (meaningless, but help minimize gates)
RX_DIFF <= RX_BYTE_COUNTER(11 downto 0) - ('0' & RX_LENGTH);

-- Length field consistency with actual rx frame length. Check if the length/type field is 'length'
RX_LENGTH_ERR_GEN: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if (SYNC_RESETRX = '1') then
			RX_LENGTH_ERR <= '0'; 
		elsif(RX_LENGTH_TYPEN = '0') then
			-- type field. No explicit length info. Can't validate actual length.
			RX_LENGTH_ERR <= '0'; 
		elsif(MAC_RX_EOF3B = '1') then
			if(RX_LENGTH <= 46) then
				-- short rx frame is padded to the minimum size of 60 bytes + 4 CRC
				if(RX_BYTE_COUNTER = 63) then
					-- correct answer.
					RX_LENGTH_ERR <= '0'; 
				else
					-- inconsistency
					RX_LENGTH_ERR <= '1'; 
				end if;
			else
				-- normal size frame. no pad.
				if(RX_DIFF = 17) then
					-- correct answer.
					RX_LENGTH_ERR <= '0'; 
				else
					-- inconsistency
					RX_LENGTH_ERR <= '1'; 
				end if;
			end if;
		end if;
	end if;
end process;



--//  RX 32-BIT CRC COMPUTATION -------------------------------------------------------
-- 802.3 section 3.2.9: 
-- protected fields: payload data + optional pad + CRC (excludes preamble and start of frame sequence)
MAC_RX_SAMPLE2_CLK <= '1' when (RX_STATE = 1) and (MAC_RX_SAMPLE_CLK = '1')  else '0'; 

RX_CRC32_RESET <= '1' when (RX_STATE = 0) else 
						'1' when (SYNC_RESETRX = '1') else
						'0';  -- reset CRC2 during the packet preamble 

-- latency 1 RX_CLKG
RX_CRC32_8B: CRC32_8B PORT MAP(
	SYNC_RESET => RX_CRC32_RESET,
	CLK => RX_CLKG,
	CRC32_IN => RX_CRC32,  -- feedback previous iteration
	DATA_IN => MAC_RXD_D,	
	SAMPLE_CLK_IN => MAC_RX_SAMPLE2_CLK,
	CRC32_OUT => RX_CRC32,
	CRC32_VALID => RX_CRC32_VALID
);

---- assess whether the frame check sequence is valid
---- ready one RX_CLKG after the start of RX_STATE 2
--RX_BAD_CRC_GEN: process(RX_CLKG)
--begin
--	if rising_edge(RX_CLKG) then
--		if(SYNC_RESETRX = '1') then
--			RX_BAD_CRC <= '0';
--		elsif(RX_STATE = 2) then
--			-- end of frame delimiter
--			RX_BAD_CRC <= not RX_CRC32_VALID;
--		end if;
--	end if;
--end process;

--// PARSE RX DATA -------------------------------------------------------------------
-- Delay data by 1 byte (otherwise we will only know about EOF AFTER the last byte is received)
MAC_RXD3 <= MAC_RXD_D;

-- SOF
--MAC_RX_SOF3 <= MAC_RX_SAMPLE_CLK when (RX_STATE = 1) and (RX_BYTE_COUNTER = 0) else '0';

-- EOF based on the length field (does not include pad nor CRC). Meaningless when the type/length field 
-- is used as type. 
MAC_RX_EOF3A <= '1' when (RX_STATE = 1) and (RX_EVENT4 = '1') and (RX_DIFF = 13) else '0';

-- EOF based on the RX_DV deassertion.
MAC_RX_EOF3B <= '1' when (RX_STATE = 1) and (RX_EVENT3 = '1') else '0';

MAC_RX_EOF3 <=  MAC_RX_EOF3B when (RX_LENGTH_TYPEN = '0') else MAC_RX_EOF3A;
MAC_RX_SAMPLE3_CLK <= MAC_RX_SAMPLE_CLK and RX_FRAME_EN3;

RX_FILTEROUT_001: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if (SYNC_RESETRX = '1') then
			RX_FRAME_EN3 <= '0';
		elsif(RX_STATE = 0) and (RX_EVENT1 = '1') then
			RX_FRAME_EN3 <= '1';
		elsif(MAC_RX_EOF3 = '1') then
			RX_FRAME_EN3 <= '0';
		end if;
	end if;
end process;
		
--//  VALID RX FRAME? ----------------------------------------------------------
-- Is the rx frame valid? If so, confirm the wptr location.
MAC_RX_VALID_001: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then

		if(SYNC_RESETRX = '1') then
			RX_EVENT5_TOGGLE <= '0';
			MAC_RX_WPTR_CONFIRMED <= (others => '0');
			MAC_RX_EOF3B_D <= '0';
			N_RX_FRAMES_local <= (others => '0');
			N_RX_BAD_CRCS_local <= (others => '0');
			N_RX_FRAMES_TOO_SHORT_local <= (others => '0');
			N_RX_FRAMES_TOO_LONG_local <= (others => '0');
			N_RX_WRONG_ADDR_local <= (others => '0');
			N_RX_LENGTH_ERRORS_local <= (others => '0');
		else 
			-- wait one more CLK period until all validity checks are complete
			MAC_RX_EOF3B_D <= MAC_RX_EOF3B;

			if(RX_EVENT5 = '1') then
				-- (same as RX_STATE = 2) frame complete, all checks complete
				N_RX_FRAMES_local <= N_RX_FRAMES_local + 1;
				RX_EVENT5_TOGGLE <= not RX_EVENT5_TOGGLE;
				
				if(RX_CRC32_VALID = '0') then
					-- BAD_CRC
					N_RX_BAD_CRCS_local <= N_RX_BAD_CRCS_local + 1;
				end if;
				
				if(RX_TOO_SHORT = '1') then
					-- frame is too short (<64B)
					N_RX_FRAMES_TOO_SHORT_local <= N_RX_FRAMES_TOO_SHORT_local + 1;
				end if;
				
				if(RX_TOO_LONG = '1') then
					-- frame is too long (>1518B)
					N_RX_FRAMES_TOO_LONG_local <= N_RX_FRAMES_TOO_LONG_local + 1;
				end if;
				
				if(RX_VALID_ADDR = '0') then
					-- address does not match (and promiscuous mode is off)
					N_RX_WRONG_ADDR_local <= N_RX_WRONG_ADDR_local + 1;
				end if;
				
				if(RX_LENGTH_ERR = '1') then
					-- length field is inconsistent with actual rx frame length
					N_RX_LENGTH_ERRORS_local <= N_RX_LENGTH_ERRORS_local + 1;
				end if;
				
				if (RX_CRC32_VALID = '1') and (RX_TOO_SHORT = '0') and (RX_TOO_LONG = '0') and (RX_VALID_ADDR = '1')
				and (RX_LENGTH_ERR = '0') then 
					-- passed all checks
					RX_FRAME_TOGGLE3 <= not RX_FRAME_TOGGLE3; -- delineates VALID rx frames (does not toggle if rx frame was rejected)
					-- update confirmed value for MAC_RX_WPTR
					if(MAC_RX_CONFIG(3) = '1') then
						-- filter out 4-byte CRC-32
						MAC_RX_WPTR_CONFIRMED <= MAC_RX_WPTR - 4;
					else
						-- include 4-byte CRC-32
						MAC_RX_WPTR_CONFIRMED <= MAC_RX_WPTR;
					end if;
				end if;
				
			end if;
		end if;
	end if;
end process;

-- to DIAGNOSTICS output (reclock with user CLK)
MAC_RX_VALID_002: process(CLK)
begin
	if rising_edge(CLK) then
		RX_EVENT5_TOGGLE_D <= RX_EVENT5_TOGGLE;
		RX_EVENT5_TOGGLE_D2 <= RX_EVENT5_TOGGLE_D;
		
		if(RX_EVENT5_TOGGLE_D /= RX_EVENT5_TOGGLE_D2) then
			-- diagnostics were just updated. Safe to reclock with CLK now.
			N_RX_FRAMES <= N_RX_FRAMES_local;
			N_RX_BAD_CRCS <= N_RX_BAD_CRCS_local;
			N_RX_FRAMES_TOO_SHORT <= N_RX_FRAMES_TOO_SHORT_local;
			N_RX_FRAMES_TOO_LONG <= N_RX_FRAMES_TOO_LONG_local;
			N_RX_WRONG_ADDR <= N_RX_WRONG_ADDR_local;
			N_RX_LENGTH_ERRORS <= N_RX_LENGTH_ERRORS_local;
		end if;
	end if;
end process;

--//  RX INPUT ELASTIC BUFFER ----------------------------------------------------------
-- The purpose of the elastic buffer is two-fold:
-- (a) a transition between the RX_CLKG synchronous PHY side and the CLK-synchronous user side.
-- (b) storage for receive packets, to absorb traffic peaks, minimize the number of 
-- UDP packets lost at high throughput.
-- The rx elastic buffer is 16Kbits, large enough for a complete maximum size (14addr+1500data+4FCS = 1518B) frame.

-- write pointer management
MAC_RX_WPTR_001: process(RX_CLKG)
begin
	if rising_edge(RX_CLKG) then
		if(SYNC_RESETRX = '1') then
			MAC_RX_WPTR <= (others => '0');
		else
			if(RX_STATE = 0) then
				-- re-position the write pointer (same or rewind if previous frame was invalid
				MAC_RX_WPTR <= MAC_RX_WPTR_CONFIRMED;
			elsif(MAC_RX_SAMPLE3_CLK = '1') then
				MAC_RX_WPTR <= MAC_RX_WPTR + 1;
			end if;
		end if;
	end if;
end process;

-- No need for initialization
-- 18Kbit buffer(s) 
MAC_RX_DIA <=  RX_FRAME_TOGGLE3 & MAC_RXD3;
RAMB16_S9_S9_002: BRAM_DP 
GENERIC MAP(
	DATA_WIDTHA => 9,		
	ADDR_WIDTHA => 11,
	DATA_WIDTHB => 9,		 
	ADDR_WIDTHB => 11

)
PORT MAP(
	CLKA => RX_CLKG,
	WEA => MAC_RX_SAMPLE3_CLK,      -- Port A Write Enable Input
	ADDRA => MAC_RX_WPTR,  -- Port A 11-bit Address Input
	DIA => MAC_RX_DIA,      -- Port A 9-bit Data Input
	DOA => open,
	CLKB => CLK,
	WEB => '0',
	ADDRB => MAC_RX_RPTR,  -- Port B 11-bit Address Input
	DIB => "000000000",      -- Port B 9-bit Data Input
	DOB => MAC_RX_DOB      -- Port B 9-bit Data Output
);
MAC_RXD4 <= MAC_RX_DOB(7 downto 0);
RX_FRAME_TOGGLE4 <= MAC_RX_DOB(8);

-- CLK zone. Reclock MAC_RX_WPTR_CONFIRMED when it is safe to do so
MAC_RX_WPTR_002: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_RX_WPTR_CONFIRMED_D <= (others => '0');
			RX_EVENT5_TOGGLE2_D <= '0';
			RX_EVENT5_TOGGLE2_D2 <= '0';
		else
			RX_EVENT5_TOGGLE2_D <= RX_EVENT5_TOGGLE;
			RX_EVENT5_TOGGLE2_D2 <= RX_EVENT5_TOGGLE2_D;
			if(RX_EVENT5_TOGGLE2_D /= RX_EVENT5_TOGGLE2_D2) then
				-- MAC_RX_WPTR_CONFIRMED is stable. OK to resample with the RX_CLKG clock.
				MAC_RX_WPTR_CONFIRMED_D <= MAC_RX_WPTR_CONFIRMED;
			end if;
		end if;
	end if;
end process;

MAC_RX_BUF_SIZE <= MAC_RX_WPTR_CONFIRMED_D + not(MAC_RX_RPTR);
-- occupied tx buffer size

-- manage read pointer
MAC_RX_RPTR_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_RX_RPTR <= (others => '1');
			MAC_RX_SAMPLE4_CLK_E <= '0';
			MAC_RX_SAMPLE4_CLK <= '0';
		else
			MAC_RX_SAMPLE4_CLK <= MAC_RX_SAMPLE4_CLK_E;  -- it takes one CLK to read data from the RAMB

			if((RX_STATE4 = 0) or (RX_STATE4 = 1)) and (MAC_RX_CTS = '1') and (MAC_RX_BUF_SIZE /= 0) then
				-- user requests data and the buffer is not empty
				MAC_RX_RPTR <= MAC_RX_RPTR + 1;
				MAC_RX_SAMPLE4_CLK_E <= '1';
			elsif(RX_STATE4 = 2) then
				-- rewind read pointer to the end of the last frame
				MAC_RX_RPTR <= MAC_RX_RPTR - 3;
				MAC_RX_SAMPLE4_CLK_E <= '0';
			else
				MAC_RX_SAMPLE4_CLK_E <= '0';
			end if;
		end if;
	end if;
end process;

-- state machine to introduce a separation of at least 8 CLKs between two successive frames
-- why 8? because the Ethernet header (7FFs + 1 SFD) was stripped off before.
-- It also makes life easier for subsequent processing (IP stack etc).
MAC_RX_RPTR_002: process(CLK)
begin
	if rising_edge(CLK) then
		RX_STATE4_D <= RX_STATE4;
		
		if(SYNC_RESET = '1') then
			RX_STATE4 <= 0;
		elsif	(RX_STATE4 = 0) and (MAC_RX_CTS = '1') and (MAC_RX_BUF_SIZE /= 0) then
			-- about to read the start of frame (1 CLK later because of RAMB latency)
			RX_STATE4 <= 1;
		elsif	(RX_STATE4 = 1) and (MAC_RX_BUF_SIZE = 0) then
			-- empty buffer means end of frame (1 CLK later because of RAMB latency)
			-- start timer to separate output frames
			RX_STATE4 <= 3;
			RX_STATE4_TIMER <= 5;	-- use 5 for a gap of 8 CLKs between successive output frames
		elsif	(RX_STATE4 = 1) and (MAC_RX_SOF4 = '0') and (RX_FRAME_TOGGLE4 /= RX_FRAME_TOGGLE4_D) then
			-- toggle means start of next frame = past the end of the current frame
			-- design: to not act on the first frame byte (endless loop)
			-- rewind the read pointer
			RX_STATE4 <= 2;
		elsif	(RX_STATE4 = 2) then
			-- start timer to separate output frames
			RX_STATE4 <= 3;
			RX_STATE4_TIMER <= 5;	-- use 5 for a gap of 8 CLKs between successive output frames
		elsif (RX_STATE4 = 3) then
			if(RX_STATE4_TIMER = 0) then
				-- ready to read the next frame in the rx elastic buffer
				RX_STATE4 <= 0;
			else
				RX_STATE4_TIMER <= RX_STATE4_TIMER - 1;
			end if;
		end if;
	end if;
end process;

-- cut output enable tail off
DISABLE_OUTPUT4 <= '1' when (RX_STATE4_D = 1) and (MAC_RX_SOF4 = '0') and (RX_FRAME_TOGGLE4 /= RX_FRAME_TOGGLE4_D) else
				'1' when (RX_STATE4_D = 2) else
				'0';
				
-- reconstruct a SOF aligned with the first output byte
SOF_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_RX_SOF4 <= '0';
		elsif(RX_STATE4 = 1) and (RX_STATE4_D = 0)then
			MAC_RX_SOF4 <= '1';
		else
			MAC_RX_SOF4 <= '0';
		end if;
	end if;
end process;

RX_FRAME_TOGGLE4_D_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(MAC_RX_SOF4 = '1') then
			RX_FRAME_TOGGLE4_D <= RX_FRAME_TOGGLE4;
		end if;
	end if;
end process;

-- reconstruct an EOF aligned with the last output byte
EOF_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_RX_EOF4 <= '0';
		elsif(MAC_RX_SAMPLE4_CLK_E = '1') and  (MAC_RX_BUF_SIZE = 0)then
			MAC_RX_EOF4 <= '1';
		else
			MAC_RX_EOF4 <= '0';
		end if;
	end if;
end process;


-- output to user. Need to delay 1 CLK because the EOF information is late
MAC_RX_DATA_VALID_E <= MAC_RX_SAMPLE4_CLK and (not DISABLE_OUTPUT4);
RX_OUTPUT_RECLOCK_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(SYNC_RESET = '1') then
			MAC_RX_DATA <= (others => '0');
			MAC_RX_DATA_VALID_local <= '0';
			MAC_RX_SOF <= '0';
			MAC_RX_EOF4_D <= '0';
		else
			MAC_RX_DATA <= MAC_RXD4;
			MAC_RX_DATA_VALID_local <= MAC_RX_DATA_VALID_E;
			MAC_RX_SOF <= MAC_RX_SOF4;
			MAC_RX_EOF4_D <= MAC_RX_EOF4;
		end if;
	end if;
end process;

MAC_RX_DATA_VALID <= MAC_RX_DATA_VALID_local;	
MAC_RX_EOF <= '1' when (MAC_RX_DATA_VALID_local = '1') and (MAC_RX_DATA_VALID_E = '0') else
					MAC_RX_EOF4_D;


--// TEST POINTS -------------------------
---- transmit side
--TP(1) <= MAC_TX_DATA_VALID;	-- tx data from user
--TP(2) <= MAC_TX_CTS_local;	-- tx flow control to user
-- test test test
TP(1) <= SPEED_STATUS_local(0);
TP(2) <= SPEED_STATUS_local(1);
TP(3) <= TX_EVENT1;	-- start tx to PHY
TP(4) <= TX_EVENT2;	-- collision
TP(5) <= TX_EVENT3;	-- done sending frame to PHY

-- receive side
RECLOCK_00X: process(RX_CLKG) 
begin
	if rising_edge(RX_CLKG) then
		TP(6) <= RX_EVENT1;	-- new packet
		TP(7) <= RX_EVENT3;	-- frame delimiter
		TP(8) <= RX_EVENT4;	-- valid frame byte
		TP(9) <= RX_EVENT5;	-- frame complete
		TP(10) <= RX_EVENT5 and not RX_CRC32_VALID;	-- invalid frame CRC
	end if;
end process;

--// COMSCOPE TRACES ------------------------
CS_CLKG <= RX_CLKG;
CS1 <= MAC_RXD;	-- data
CS1_CLK <= RX_EVENT4;	-- valid byte
CS2 <= MAC_RXD_D;
CS2_CLK <= MAC_RX_SAMPLE2_CLK;
CT1 <= RX_EVENT5 and not RX_CRC32_VALID;	-- invalid frame CRC
CT1_CLK <= '1';
end Behavioral;

