----------------------------------------------------------------------------------
--
-- Application interface
--
--              ___________ ____ ____ _________ ____ __     __ ____ ____ _________
--  CH1_data    ___________X____X____X_________X____X__ ... __X____X____X_________
--                          ______________     ________     _____________
--  CH1_DV      ___________/              \___/         ...              \________
--                          ____
--  CH1_first   ___________/    \______________________ ... ______________________
--                                                                   ____
--  CH1_last    _______________________________________ ... ________/    \________
--
--
--
--
--
--  CH1_data    ___________X_A__X_B__X_C_______X_D__X__ ... __X____X____X_________
--
--  RAM_Addr    ______ 43 ______X 44 X___ 45 __X 46 X__ ... __X 63 X 64 X_________
--
--
----------------------------------------------------------------------------------
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
library UNISIM;
use UNISIM.VComponents.all;
----------------------------------------------------------------------------------
ENTITY Ethernet_controller_1ch IS
  PORT(
    CH1_clk:            IN  STD_LOGIC;                      -- user (application) clock
    CH1_data:           IN  STD_LOGIC_VECTOR(15 DOWNTO 0);  -- User Data IN
    CH1_DV:             IN  STD_LOGIC;                      -- User Data Valid
    CH1_first:          IN  STD_LOGIC;                      -- First data word
    CH1_last:           IN  STD_LOGIC;                      -- Last data word
    CH1_ready:          OUT STD_LOGIC := '0';               -- "Ready for new data" flag
    CH1_UDP_SRC_port:   IN  STD_LOGIC_VECTOR(15 DOWNTO 0);  -- UDP source port
    CH1_UDP_DST_port:   IN  STD_LOGIC_VECTOR(15 DOWNTO 0);  -- UDP destination port
    CH1_IP_SRC:         IN  STD_LOGIC_VECTOR(31 DOWNTO 0);  -- IP source address
    CH1_IP_DST:         IN  STD_LOGIC_VECTOR(31 DOWNTO 0);  -- IP destination address

    MAT_TX_CLK:         IN  STD_LOGIC;                              -- Ethernet MAC clock (125 MHz)
    MAC_TX_Data:        OUT STD_LOGIC_VECTOR( 7 DOWNTO 0) := X"00"; -- Tx data
    MAC_TX_DV:          OUT STD_LOGIC := '0';                       -- Tx data valid
    MAC_TX_ACK:         IN  STD_LOGIC;                              -- First byte of data accepted, remaining data must be provided

    MAC_RX_CLK:         IN  STD_LOGIC;                      -- clock derived from GMII_RX_CLK
    MAC_RX_DATA:        IN  STD_LOGIC_VECTOR( 7 DOWNTO 0);  -- Rx data
    MAC_RX_DV:          IN  STD_LOGIC;                      -- Rx data valid
    MAC_RX_GOOD_FRAME:  IN  STD_LOGIC;                      -- Asserted when a valid frame was received
    MAC_RX_BAD_FRAME:   IN  STD_LOGIC);                     -- Asserted when an invalid frame was received

END Ethernet_controller_1ch;
----------------------------------------------------------------------------------
ARCHITECTURE Behavioral OF Ethernet_controller_1ch IS
----------------------------------------------------------------------------------
  -- RAM for Ethernet frame
  COMPONENT Frame_RAM_V5LXT PORT(
    clka:       IN  STD_LOGIC;
    wea:        IN  STD_LOGIC_VECTOR( 0 DOWNTO 0);
    addra:      IN  STD_LOGIC_VECTOR( 9 DOWNTO 0);
    dina:       IN  STD_LOGIC_VECTOR(15 DOWNTO 0);
    clkb:       IN  STD_LOGIC;
    addrb:      IN  STD_LOGIC_VECTOR(10 DOWNTO 0);
    doutb:      OUT STD_LOGIC_VECTOR( 7 DOWNTO 0));
  END COMPONENT;

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

  -- Adresy IP a MAC mohou byt procesorem zmeneny v prubehu vysilani,
  -- proto muze dojit ke ztrate paketu (je lepsi zastavit vysilani)

  --------------------------------------------------------------------------------
  -- common attributes

  -- Ethernet header -------------------------------------------------------------
    --
    --    T 61        PC-359
    --
    --     00           00
    --     1A           19
    --     6B           D1
    --     D2           73
    --     C3           BE
    --     93           28
    -- 

  CONSTANT MAC_DST_1:           STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";
  CONSTANT MAC_DST_2:           STD_LOGIC_VECTOR(7 DOWNTO 0):= X"1A";
  CONSTANT MAC_DST_3:           STD_LOGIC_VECTOR(7 DOWNTO 0):= X"6B";
  CONSTANT MAC_DST_4:           STD_LOGIC_VECTOR(7 DOWNTO 0):= X"D2";
  CONSTANT MAC_DST_5:           STD_LOGIC_VECTOR(7 DOWNTO 0):= X"C3";
  CONSTANT MAC_DST_6:           STD_LOGIC_VECTOR(7 DOWNTO 0):= X"93";

  CONSTANT MAC_SRC_1_o:         STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";
  CONSTANT MAC_SRC_2_o:         STD_LOGIC_VECTOR(7 DOWNTO 0):= X"2A";
  CONSTANT MAC_SRC_3_o:         STD_LOGIC_VECTOR(7 DOWNTO 0):= X"6B";
  CONSTANT MAC_SRC_4_o:         STD_LOGIC_VECTOR(7 DOWNTO 0):= X"D2";
  CONSTANT MAC_SRC_5_o:         STD_LOGIC_VECTOR(7 DOWNTO 0):= X"C3";
  CONSTANT MAC_SRC_6_o:         STD_LOGIC_VECTOR(7 DOWNTO 0):= X"93";

  CONSTANT ETH_type_1:          STD_LOGIC_VECTOR(7 DOWNTO 0):= X"08";
  CONSTANT ETH_type_2:          STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";

  -- IP header -------------------------------------------------------------------
  CONSTANT IP_ver_1:            STD_LOGIC_VECTOR(7 DOWNTO 0):= X"45";
  CONSTANT IP_ver_2:            STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";

  CONSTANT IP_ID_1:             STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";
  CONSTANT IP_ID_2:             STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";

  CONSTANT IP_Flags_1:          STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";
  CONSTANT IP_Flags_2:          STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";

  CONSTANT Time_to_live:        STD_LOGIC_VECTOR(7 DOWNTO 0):= X"70";
  CONSTANT IP_protocol:         STD_LOGIC_VECTOR(7 DOWNTO 0):= X"11";   -- 11h = UDP

  SIGNAL CH1_IP_SRC_1:          STD_LOGIC_VECTOR(7 DOWNTO 0);
  SIGNAL CH1_IP_SRC_2:          STD_LOGIC_VECTOR(7 DOWNTO 0);
  SIGNAL CH1_IP_SRC_3:          STD_LOGIC_VECTOR(7 DOWNTO 0);
  SIGNAL CH1_IP_SRC_4:          STD_LOGIC_VECTOR(7 DOWNTO 0);
  SIGNAL CH1_IP_DST_1:          STD_LOGIC_VECTOR(7 DOWNTO 0);
  SIGNAL CH1_IP_DST_2:          STD_LOGIC_VECTOR(7 DOWNTO 0);
  SIGNAL CH1_IP_DST_3:          STD_LOGIC_VECTOR(7 DOWNTO 0);
  SIGNAL CH1_IP_DST_4:          STD_LOGIC_VECTOR(7 DOWNTO 0);
               
  --------------------------------------------------------------------------------
  -- Channel specific parameters
  SIGNAL CH1_IP_length:         STD_LOGIC_VECTOR(15 DOWNTO 0):= X"0000";
  SIGNAL CH1_IP_CheckSum:       STD_LOGIC_VECTOR(16 DOWNTO 0):= (OTHERS => '0');
  SIGNAL CH1_IP_CS_Carry:       STD_LOGIC_VECTOR(16 DOWNTO 0):= (OTHERS => '0');        -- Carry for  IP CheckSum calculation

  SIGNAL CH1_UDP_length:        STD_LOGIC_VECTOR(15 DOWNTO 0):= X"0000";
  SIGNAL CH1_UDP_CheckSum:      STD_LOGIC_VECTOR(16 DOWNTO 0):= (OTHERS => '0');
  SIGNAL CH1_UDP_CS_Carry:      STD_LOGIC_VECTOR(16 DOWNTO 0):= (OTHERS => '0');        -- Carry for UDP CheckSum calculation

  --------------------------------------------------------------------------------
  -- FIFO interface logic

  -- RAM input
  SIGNAL CH1_MEM_WE:            STD_LOGIC_VECTOR( 0 DOWNTO 0):= "0";
  SIGNAL CH1_MEM_Addr:          STD_LOGIC_VECTOR( 9 DOWNTO 0):= "00" & X"14";
  SIGNAL CH1_MEM_Data:          STD_LOGIC_VECTOR(15 DOWNTO 0):= X"0000";
  SIGNAL CH1_MEM_Data_swap:     STD_LOGIC_VECTOR(15 DOWNTO 0):= X"0000";

  -- RAM output
  SIGNAL Frame_Addr_CH1:        STD_LOGIC_VECTOR(10 DOWNTO 0):= (OTHERS => '0');
  SIGNAL Frame_Data_CH1:        STD_LOGIC_VECTOR( 7 DOWNTO 0):= X"00";
  SIGNAL Frame_Data_CH1_i:      STD_LOGIC_VECTOR( 7 DOWNTO 0):= X"00";

  TYPE t_state_USER2RAM IS (st_wait_for_new_data, st_data_write,
        st_CheckSum_1,
        st_CheckSum_2,
        st_CheckSum_3,
        st_CheckSum_4,
        st_CheckSum_5,
        st_CheckSum_6,
        st_CheckSum_7,
        st_CheckSum_8,
        st_CheckSum_9,
        st_CheckSum_10,
        st_CheckSum_11,
        st_write_13,
        st_write_14,
        st_write_15,
        st_write_16,
        st_write_17,
        st_write_18,
        st_write_19,
        st_write_20,
        st_write_21,
        st_write_22,
        st_wait_for_tx,
        st_write_done);

  SIGNAL state_CH1_wr:              t_state_USER2RAM := st_wait_for_new_data;

  SIGNAL CH1_write_done:            STD_LOGIC:= '0';
  SIGNAL CH1_write_done_shr:        STD_LOGIC_VECTOR(2 DOWNTO 0):= "000";
  SIGNAL CH1_write_done_MAT_TX_CLK:    STD_LOGIC:= '0';
  SIGNAL CH1_write_done_MAT_TX_CLK_d:  STD_LOGIC:= '0';

  SIGNAL CH1_Tx_done:               STD_LOGIC:= '0';
  SIGNAL CH1_Tx_done_shr:           STD_LOGIC_VECTOR(2 DOWNTO 0):= "000";
  SIGNAL CH1_Tx_done_CH1_clk:       STD_LOGIC:= '0';

  -- to count data bytes when reading from RAM (writing to MAC)
  SIGNAL cnt_data_read:             STD_LOGIC_VECTOR(10 DOWNTO 0):= (OTHERS => '0');

  TYPE t_state_RAM2MAC IS (st_check_data_CH1, st_byte_0_CH1, st_wait_ack_CH1, st_tx_data_CH1_1, st_tx_data_CH1_2, st_tx_data_CH1_3);
  SIGNAL tx_MAC_state:              t_state_RAM2MAC := st_check_data_CH1;

  SIGNAL shift_data_out:            STD_LOGIC:= '0';

  CONSTANT Tx_enable:               STD_LOGIC:= '1';

----------------------------------------------------------------------------------
BEGIN
----------------------------------------------------------------------------------

  CH1_UDP_CS_Carry <= (X"0000" & CH1_UDP_CheckSum(16));         -- Carry for UDP CheckSum calculation
  CH1_IP_CS_Carry  <= (X"0000" &  CH1_IP_CheckSum(16));         -- Carry for  IP CheckSum calculation

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

  CH1_IP_SRC_1 <= CH1_IP_SRC(31 DOWNTO 24);     CH1_IP_DST_1 <= CH1_IP_DST(31 DOWNTO 24);
  CH1_IP_SRC_2 <= CH1_IP_SRC(23 DOWNTO 16);     CH1_IP_DST_2 <= CH1_IP_DST(23 DOWNTO 16);
  CH1_IP_SRC_3 <= CH1_IP_SRC(15 DOWNTO  8);     CH1_IP_DST_3 <= CH1_IP_DST(15 DOWNTO  8);
  CH1_IP_SRC_4 <= CH1_IP_SRC( 7 DOWNTO  0);     CH1_IP_DST_4 <= CH1_IP_DST( 7 DOWNTO  0);
                                                                     
  --------------------------------------------------------------------------------
  wr_to_RAM_CH1: PROCESS(CH1_clk) BEGIN
    IF rising_edge(CH1_clk) THEN

      CH1_ready <= '0';                         -- default assignment

      CASE state_CH1_wr IS

        WHEN st_wait_for_new_data =>            -- waiting for "Start of frame"
                CH1_ready <= '1';
                CH1_write_done <= '0';
                IF CH1_first = '1' THEN
                  CH1_MEM_Addr <= CH1_MEM_Addr + 1;       -- increment address
                  CH1_MEM_WE <= "1";
                  CH1_MEM_Data <= CH1_data;
                  state_CH1_wr <= st_data_write;
                  CH1_UDP_CheckSum <= '0' & CH1_data;
                END IF;

        WHEN st_data_write =>                   -- write data to RAM until CH1_last is received
                IF CH1_DV = '1' THEN
                  CH1_MEM_Addr <= CH1_MEM_Addr + 1;       -- increment address
                  CH1_MEM_WE <= "1";
                  CH1_MEM_Data <= CH1_data;
                  CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + CH1_data;
                ELSE
                  CH1_MEM_WE <= "0";
                END IF;

                IF CH1_last = '1' THEN
                  state_CH1_wr <= st_CheckSum_1;
                  CH1_UDP_length(10 DOWNTO 0) <= (CH1_MEM_Addr & '0') - CONV_STD_LOGIC_VECTOR(30,10);
                        -- data length is:      (2 * CH1_MEM_Addr) + 2 - 42 + 8
                        --   +  2 = last 2 bytes to write
                        --   - 42 = address offset
                        --   +  8 = UDP header length
                  CH1_IP_length(10 DOWNTO 0) <= (CH1_MEM_Addr & '0') - CONV_STD_LOGIC_VECTOR(10,10);
                        -- data length is:      (2 * CH1_MEM_Addr) + 2 - 42 + 28
                        --   +  2 = last 2 bytes to write
                        --   - 42 = address offset
                        --   + 28 = IP + UDP header length
                END IF;

        -- all data received, calculate UDP checksum
        WHEN st_CheckSum_1 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & (CH1_IP_SRC_1 & CH1_IP_SRC_2));
                                  CH1_IP_CheckSum  <= '0' & X"0014";               -- 20 (h14) is length of IP header (added to total length)
                                  CH1_MEM_Addr <= "0000000000"; CH1_MEM_WE <= "1";
                                  CH1_MEM_Data <= MAC_DST_1 & MAC_DST_2;
                                  state_CH1_wr <= st_CheckSum_2;

        WHEN st_CheckSum_2 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & (CH1_IP_SRC_3 & CH1_IP_SRC_4));
                                  CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (IP_ver_1 & IP_ver_2);
                                  CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                                  CH1_MEM_Data <= MAC_DST_3 & MAC_DST_4;
                                  state_CH1_wr <= st_CheckSum_3;

        WHEN st_CheckSum_3 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & (CH1_IP_DST_1 & CH1_IP_DST_2));
                                  CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + CH1_UDP_length;
                                  CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                                  CH1_MEM_Data <= MAC_DST_5 & MAC_DST_6;
                                  state_CH1_wr <= st_CheckSum_4;

        WHEN st_CheckSum_4 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & (CH1_IP_DST_3 & CH1_IP_DST_4));
                                  CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (IP_ID_1 & IP_ID_2);
                                  CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                                  CH1_MEM_Data <= MAC_SRC_1_o & MAC_SRC_2_o;
                                  state_CH1_wr <= st_CheckSum_5;

        WHEN st_CheckSum_5 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & (X"00" & IP_protocol));
                                  CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (IP_Flags_1 & IP_Flags_2);
                                  CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                                  CH1_MEM_Data <= MAC_SRC_3_o & MAC_SRC_4_o;
                                  state_CH1_wr <= st_CheckSum_6;

        WHEN st_CheckSum_6 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & CH1_UDP_length);
                                  CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (Time_to_live & IP_protocol);
                                  CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                                  CH1_MEM_Data <= MAC_SRC_5_o & MAC_SRC_6_o;
                                  state_CH1_wr <= st_CheckSum_7;

        WHEN st_CheckSum_7 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & CH1_UDP_length);
                              CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (CH1_IP_SRC_1 & CH1_IP_SRC_2);
                              CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                              CH1_MEM_Data <= ETH_type_1 & ETH_type_2;
                              state_CH1_wr <= st_CheckSum_8;

        WHEN st_CheckSum_8 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & CH1_UDP_SRC_port);
                              CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (CH1_IP_SRC_3 & CH1_IP_SRC_4);
                              CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                              CH1_MEM_Data <= IP_ver_1 & IP_ver_2;
                              state_CH1_wr <= st_CheckSum_9;

        WHEN st_CheckSum_9 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0) + ('0' & CH1_UDP_DST_port);
                              CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (CH1_IP_DST_1 & CH1_IP_DST_2);
                              CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                              CH1_MEM_Data <= CH1_IP_length;
                              state_CH1_wr <= st_CheckSum_10;

        WHEN st_CheckSum_10 => CH1_UDP_CheckSum <= CH1_UDP_CS_Carry + CH1_UDP_CheckSum(15 DOWNTO 0);
                               CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0) + (CH1_IP_DST_3 & CH1_IP_DST_4);
                               CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                               CH1_MEM_Data <= IP_ID_1 & IP_ID_2;
                               state_CH1_wr <= st_CheckSum_11;

        WHEN st_CheckSum_11 => CH1_UDP_CheckSum <= NOT CH1_UDP_CheckSum;
                               CH1_IP_CheckSum <= CH1_IP_CS_Carry + CH1_IP_CheckSum(15 DOWNTO 0);
                               CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                               CH1_MEM_Data <= IP_Flags_1 & IP_Flags_2;
                               state_CH1_wr <= st_write_13;

        WHEN st_write_13 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_IP_CheckSum <= NOT CH1_IP_CheckSum;
                            CH1_MEM_Data <= Time_to_live & IP_protocol;
                            state_CH1_wr <= st_write_14;

        WHEN st_write_14 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_IP_CheckSum(15 DOWNTO 0);
                            state_CH1_wr <= st_write_15;

        WHEN st_write_15 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_IP_SRC_1 & CH1_IP_SRC_2;
                            state_CH1_wr <= st_write_16;

        WHEN st_write_16 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_IP_SRC_3 & CH1_IP_SRC_4;
                            state_CH1_wr <= st_write_17;

        WHEN st_write_17 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_IP_DST_1 & CH1_IP_DST_2;
                            state_CH1_wr <= st_write_18;

        WHEN st_write_18 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_IP_DST_3 & CH1_IP_DST_4;
                            state_CH1_wr <= st_write_19;

        WHEN st_write_19 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_UDP_SRC_port;
                            state_CH1_wr <= st_write_20;

        WHEN st_write_20 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_UDP_DST_port;
                            state_CH1_wr <= st_write_21;

        WHEN st_write_21 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_UDP_length;
                            state_CH1_wr <= st_write_22;

        WHEN st_write_22 => CH1_MEM_Addr <= CH1_MEM_Addr + 1; CH1_MEM_WE <= "1";
                            CH1_MEM_Data <= CH1_UDP_CheckSum(15 DOWNTO 0);
                            state_CH1_wr <= st_write_done;
                            CH1_write_done <= '1';

        WHEN st_write_done => CH1_MEM_WE <= "0";
                              CH1_MEM_Addr <= ("00" & X"14");      -- prepare pointer for new data
                              IF CH1_Tx_done_CH1_clk = '0' THEN
                                state_CH1_wr <= st_wait_for_tx;
                              END IF;

        WHEN st_wait_for_tx => IF CH1_Tx_done_CH1_clk = '1' THEN
                                 state_CH1_wr <= st_wait_for_new_data;
                               END IF;

      END CASE;

    END IF;
  END PROCESS wr_to_RAM_CH1;


  --------------------------------------------------------------------------------
  -- interdomain signal synchronization
  --------------------------------------------------------------------------------

  -- get CHx_Tx_done to CHx_clk domain

  PROCESS(CH1_Tx_done, CH1_clk) BEGIN
    IF CH1_Tx_done = '1' THEN
      CH1_Tx_done_shr <= (OTHERS => '1');
    ELSIF rising_edge(CH1_clk) THEN
      CH1_Tx_done_shr <= CH1_Tx_done_shr(CH1_Tx_done_shr'HIGH-1 DOWNTO 0) & '0';
    END IF;
  END PROCESS;

  PROCESS(CH1_clk) BEGIN
    IF rising_edge(CH1_clk) THEN
      CH1_Tx_done_CH1_clk <= CH1_Tx_done_shr(CH1_Tx_done_shr'HIGH);
    END IF;
  END PROCESS;

  --------------------------------------------------------------------------------
  -- get CHx_write_done to MAT_TX_CLK domain

  PROCESS(CH1_write_done, MAT_TX_CLK) BEGIN
    IF CH1_write_done = '1' THEN
      CH1_write_done_shr <= (OTHERS => '1');
    ELSIF rising_edge(MAT_TX_CLK) THEN
      CH1_write_done_shr <= CH1_write_done_shr(CH1_write_done_shr'HIGH-1 DOWNTO 0) & '0';
    END IF;
  END PROCESS;

  PROCESS(MAT_TX_CLK) BEGIN
    IF rising_edge(MAT_TX_CLK) THEN
      CH1_write_done_MAT_TX_CLK <= CH1_write_done_shr(CH1_write_done_shr'HIGH);
    END IF;
  END PROCESS;

----------------------------------------------------------------------------------
-- Memory read process (MAC input)

-- st_watit_for_MAC_ready_CH3, st_wait_for_ack_CH3, st_data_out_1_CH3, st_data_out_2_CH3, st_data_out_3_CH3,

  PROCESS(MAT_TX_CLK) BEGIN
    IF rising_edge(MAT_TX_CLK) THEN

      -- rising edge of "CHx_write_done_MAT_TX_CLK" signal
      CH1_write_done_MAT_TX_CLK_d <= CH1_write_done_MAT_TX_CLK;

      IF CH1_write_done_MAT_TX_CLK_d = '0' AND CH1_write_done_MAT_TX_CLK = '1' THEN CH1_Tx_done <= '0'; END IF;

        CASE tx_MAC_state IS

          WHEN st_check_data_CH1 => MAC_TX_DV <= '0';
                                    shift_data_out <= '0';
                                    MAC_TX_Data <= Frame_data_CH1;              -- Byte 0

                                    IF Tx_enable = '1' THEN     -- state machine is locked here if Tx is disabled

                                      IF CH1_write_done_MAT_TX_CLK = '1' THEN       -- CH1 ready?
                                        tx_MAC_state <= st_byte_0_CH1;
                                        Frame_Addr_CH1 <= Frame_Addr_CH1 + 1;
                                      END IF;                                       -- If not, stay in current state

                                    END IF;

          WHEN st_byte_0_CH1     => tx_MAC_state <= st_wait_ack_CH1;
                                    MAC_TX_DV <= '1';
                                    shift_data_out <= '1';
                                    Frame_Addr_CH1 <= Frame_Addr_CH1 + 1;

          WHEN st_wait_ack_CH1   => shift_data_out <= '0';
                                    IF MAC_TX_ACK = '1' THEN
                                      tx_MAC_state <= st_tx_data_CH1_1;
                                      Frame_Addr_CH1 <= Frame_Addr_CH1 + 1;
                                      MAC_TX_Data <= Frame_Data_CH1_i;          -- Byte 1
                                    END IF;

          -- load length of data payload (to send proper number of bytes)
          WHEN st_tx_data_CH1_1  => IF Frame_Addr_CH1 = X"27" THEN
                                      tx_MAC_state <= st_tx_data_CH1_2;
                                      cnt_data_read(10 DOWNTO 8) <= Frame_Data_CH1(2 DOWNTO 0);        -- higher byte of packet length
                                    END IF;
                                    MAC_TX_Data <= Frame_Data_CH1;
                                    Frame_Addr_CH1 <= Frame_Addr_CH1 + 1;

          WHEN st_tx_data_CH1_2  => tx_MAC_state <= st_tx_data_CH1_3;
                                    cnt_data_read(7 DOWNTO 0) <= Frame_Data_CH1;                       -- lower byte of packet length
                                    MAC_TX_Data <= Frame_Data_CH1;
                                    Frame_Addr_CH1 <= Frame_Addr_CH1 + 1;

          WHEN st_tx_data_CH1_3  => Frame_Addr_CH1 <= Frame_Addr_CH1 + 1;
                                    MAC_TX_Data <= Frame_Data_CH1;
                                    cnt_data_read <= cnt_data_read - 1;
                                    IF cnt_data_read = ("000" & X"06") THEN      -- last byte of data
                                      MAC_TX_DV <= '0';
                                      CH1_Tx_done <= '1';
                                    END IF;
                                    IF CH1_write_done_MAT_TX_CLK = '0' THEN
                                      tx_MAC_state <= st_check_data_CH1;
                                      Frame_Addr_CH1 <= (OTHERS => '0');
                                    END IF;

        END CASE;

    END IF;
  END PROCESS;

----------------------------------------------------------------------------------
  -- output data shift register
  PROCESS(MAT_TX_CLK) BEGIN
    IF rising_edge(MAT_TX_CLK) THEN
      IF shift_data_out = '1' THEN
        Frame_Data_CH1_i <= Frame_Data_CH1;
      END IF;
    END IF;
  END PROCESS;

----------------------------------------------------------------------------------
  -- Ethernet frame RAMs (one RAM per channel)

  Frame_RAM_CH1 : Frame_RAM_V5LXT PORT MAP(
    clka    => CH1_clk,
    wea     => CH1_MEM_WE,
    addra   => CH1_MEM_Addr,
    dina    => CH1_MEM_Data_swap,
    clkb    => MAT_TX_CLK,
    addrb   => Frame_Addr_CH1,
    doutb   => Frame_Data_CH1);

  CH1_MEM_Data_swap <= CH1_MEM_Data(7 DOWNTO 0) & CH1_MEM_Data(15 DOWNTO 8);

----------------------------------------------------------------------------------
END Behavioral;
----------------------------------------------------------------------------------
