library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;


entity rgmii_tx is
    Port ( clk         : in STD_LOGIC;
           clk90       : in STD_LOGIC;
           phy_ready   : in STD_LOGIC;

           data        : in STD_LOGIC_VECTOR (7 downto 0);
           data_valid  : in STD_LOGIC;
           data_enable : in STD_LOGIC;
           data_error  : in STD_LOGIC;
           
           eth_txck    : out STD_LOGIC_VECTOR(0 downto 0);
           eth_txctl   : out STD_LOGIC_VECTOR(0 downto 0);
           eth_txd     : out STD_LOGIC_VECTOR (3 downto 0));
end rgmii_tx;

architecture Behavioral of rgmii_tx is

	 component my_ddio_out IS
		PORT
		(
			datain_h		: IN STD_LOGIC_VECTOR (0 DOWNTO 0);
			datain_l		: IN STD_LOGIC_VECTOR (0 DOWNTO 0);
			outclock		: IN STD_LOGIC ;
			dataout		: OUT STD_LOGIC_VECTOR (0 DOWNTO 0)
		);
	end component my_ddio_out;
	
	component tx_ddio_out IS
	PORT
	(
		datain_h		: IN STD_LOGIC_VECTOR (3 DOWNTO 0);
		datain_l		: IN STD_LOGIC_VECTOR (3 DOWNTO 0);
		outclock		: IN STD_LOGIC ;
		dataout		: OUT STD_LOGIC_VECTOR (3 DOWNTO 0)
	);
	end component tx_ddio_out;
	
    signal enable_count        : unsigned(6 downto 0) := (others => '0');

    signal enable_frequency    : unsigned(6 downto 0) := (others => '1');
    signal times_3             : unsigned(8 downto 0) := (others => '0');
    signal first_quarter       : unsigned(6 downto 0) := (others => '0');
    signal second_quarter      : unsigned(6 downto 0) := (others => '0');
    signal third_quarter       : unsigned(6 downto 0) := (others => '0');

    signal dout                : STD_LOGIC_VECTOR (7 downto 0) := (others => '0');
    signal doutctl             : STD_LOGIC_VECTOR (1 downto 0) := (others => '0');
    signal doutclk             : STD_LOGIC_VECTOR (1 downto 0) := (others => '0');
    signal hold_data           : STD_LOGIC_VECTOR (7 downto 0);
    signal hold_valid          : STD_LOGIC;
    signal hold_error          : STD_LOGIC;
    signal ok_to_send          : STD_LOGIC := '0';
    signal tx_ready            : STD_LOGIC;
    signal tx_ready_meta       : STD_LOGIC;
	 
	 signal a,b,c,d				 : std_logic_vector(0 downto 0) := (others=> '0');

    ATTRIBUTE IOB : STRING ;
    ATTRIBUTE IOB OF dout    : signal IS "TRUE";
    ATTRIBUTE IOB OF doutctl : signal IS "TRUE";
begin

    times_3 <= ("0" & enable_frequency & "0") + ("00" & enable_frequency);  
    -------------------------------------------------
    -- Map the data and control signals so that they
    -- can be sent via the DDR registers
    -------------------------------------------------
process(clk)
    begin
        if rising_edge(clk) then
            first_quarter  <= "00" & enable_frequency(enable_frequency'high downto 2);
            second_quarter <= "0"  & enable_frequency(enable_frequency'high downto 1);
            third_quarter  <= times_3(times_3'high downto 2);
            if data_enable = '1' then
                enable_frequency <= enable_count+1;
                enable_count <= (others => '0');                
            elsif  enable_count /= "1111111" then
                enable_count <= enable_count + 1;
            end if;

            if data_enable = '1' then
                hold_data <= data;
                hold_valid <= data_valid;
                hold_error <= data_error;
                if enable_frequency = 1 then
                    -- Double data rate transfer at full frequency
                    dout(3 downto 0) <= data(3 downto 0); 
                    dout(7 downto 4) <= data(7 downto 4); 
                    doutctl(0) <= ok_to_send and data_valid;
                    doutctl(1) <= ok_to_send and (data_valid XOR data_error);
                    doutclk(0) <= '1';
                    doutclk(1) <= '0';
                else
                    -- Send the low nibble
                    dout(3 downto 0) <= data(3 downto 0); 
                    dout(7 downto 4) <= data(3 downto 0); 
                    doutctl(0) <= ok_to_send and data_valid;
                    doutctl(1) <= ok_to_send and data_valid;
                    doutclk(0) <= '1';
                    doutclk(1) <= '1';
                end if;
            elsif enable_count = first_quarter-1  then
                if enable_frequency(1) = '1' then
                    -- Send the high nibble and valid signal for the last half of this cycle
                    doutctl(1) <= ok_to_send and (hold_valid XOR hold_error);
                    doutclk(1) <= '0';
                else        
                    doutctl(0) <= ok_to_send and (hold_valid XOR hold_error);
                    doutctl(1) <= ok_to_send and (hold_valid XOR hold_error);
                    doutclk(0) <= '0';
                    doutclk(1) <= '0';
                end if;
            elsif enable_count = first_quarter  then
                doutctl(0) <= ok_to_send and (hold_valid XOR hold_error);
                doutctl(1) <= ok_to_send and (hold_valid XOR hold_error);
                doutclk(0) <= '0';
                doutclk(1) <= '0';
            elsif enable_count = second_quarter-1  then
                dout(3 downto 0) <= hold_data(7 downto 4); 
                dout(7 downto 4) <= hold_data(7 downto 4); 
               -- Send the high nibble and valid signal for the last half of this cycle
                doutclk(0) <= '1';        
                doutclk(1) <= '1';        
                doutctl(0) <= ok_to_send and hold_valid;
                doutctl(1) <= ok_to_send and hold_valid;
            elsif enable_count = third_quarter-1  then
                if enable_frequency(1) = '1' then
                    doutctl(1) <= ok_to_send and (hold_valid XOR hold_error);
                    doutclk(1) <= '0';
                else        
                    doutctl(0) <= ok_to_send and (hold_valid XOR hold_error);
                    doutctl(1) <= ok_to_send and (hold_valid XOR hold_error);
                    doutclk(0) <= '0';
                    doutclk(1) <= '0';
                end if;
            elsif enable_count = third_quarter  then
                doutclk(0) <= '0';        
                doutclk(1) <= '0';        
                doutctl(0) <= ok_to_send and (hold_valid XOR hold_error);
                doutctl(1) <= ok_to_send and (hold_valid XOR hold_error);
            end if;
        end if; 
    end process;

   ----------------------------------------------------
   -- DDR output registers 
   ----------------------------------------------------
	--clocking
	a(0) <= doutctl(0);
	b(0) <= doutctl(1);
	c(0) <= doutclk(0);
	d(0) <= doutclk(1);
	
	out_tx_ctl: my_ddio_out 
			port map
			(
				datain_h		=> a,
				datain_l		=> b,
				outclock		=> clk,
				dataout		=> eth_txctl
			);
	out_tx_clk: my_ddio_out 
			port map
			(
				datain_h		=> c,
				datain_l		=> d,
				outclock		=> clk90,
				dataout		=> eth_txck
			);
	--data
	out_tx_data: tx_ddio_out 
	PORT MAP
	(
		datain_h		=> dout(3 downto 0),
		datain_l		=> dout(7 downto 4),
		outclock		=> clk,
		dataout		=> eth_txd
	);

    
monitor_reset_state: process(clk)
    begin
       if rising_edge(clk) then
          tx_ready      <= tx_ready_meta;
          tx_ready_meta <= phy_ready;
          if tx_ready = '1' and data_valid = '0' and data_enable = '1' then
             ok_to_send    <= '1';
          end if;
       end if;
    end process;

end Behavioral;