diff --git a/libraries/io/eth/hdllib.cfg b/libraries/io/eth/hdllib.cfg
index 445b27d5158cf1b8159b91d1e4ef6ddf36af65ff..2c2d0d2e75764aaa9141fd42a142a327023bcca8 100644
--- a/libraries/io/eth/hdllib.cfg
+++ b/libraries/io/eth/hdllib.cfg
@@ -8,18 +8,18 @@ build_synth_dir =
 synth_files =
     src/vhdl/eth_pkg.vhd
     $UNB/Firmware/modules/tse/src/vhdl/eth_checksum.vhd
-    $UNB/Firmware/modules/tse/src/vhdl/eth_hdr_store.vhd
-    $UNB/Firmware/modules/tse/src/vhdl/eth_hdr_status.vhd
-    $UNB/Firmware/modules/tse/src/vhdl/eth_hdr_ctrl.vhd
+    eth_hdr_store.vhd
+    eth_hdr_status.vhd
+    eth_hdr_ctrl.vhd
     $UNB/Firmware/modules/tse/src/vhdl/eth_hdr.vhd
     $UNB/Firmware/modules/tse/src/vhdl/eth_crc_ctrl.vhd
     $UNB/Firmware/modules/tse/src/vhdl/eth_crc_word.vhd
     $UNB/Firmware/modules/tse/src/vhdl/eth_mm_registers.vhd
-    $UNB/Firmware/modules/tse/src/vhdl/eth_mm_reg_frame.vhd
+    eth_mm_reg_frame.vhd
     $UNB/Firmware/modules/tse/src/vhdl/eth_udp_channel.vhd
     $UNB/Firmware/modules/tse/src/vhdl/eth_buffer.vhd
-    $UNB/Firmware/modules/tse/src/vhdl/eth_control.vhd
-    $UNB/Firmware/modules/tse/src/vhdl/eth_ihl_to_20.vhd
+    eth_control.vhd
+    eth_ihl_to_20.vhd
     src/vhdl/eth.vhd
     
     src/vhdl/avs_eth.vhd
@@ -32,4 +32,4 @@ test_bench_files =
     tb/vhdl/tb_eth.vhd
     tb/vhdl/tb_tb_eth.vhd
     tb/vhdl/tb_eth_udp_offload.vhd
-    $UNB/Firmware/modules/tse/tb/vhdl/tb_eth_ihl_to_20.vhd
+    tb/vhdl/tb_eth_ihl_to_20.vhd
diff --git a/libraries/io/eth/src/vhdl/eth_control.vhd b/libraries/io/eth/src/vhdl/eth_control.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..912840e7f78ed2d4c1232ef36f56d641e01b86f9
--- /dev/null
+++ b/libraries/io/eth/src/vhdl/eth_control.vhd
@@ -0,0 +1,366 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE common_lib.eth_layers_pkg.ALL;
+USE work.eth_pkg.ALL;
+
+ENTITY eth_control IS
+  PORT (
+    -- Clocks and reset
+    rst               : IN  STD_LOGIC;
+    clk               : IN  STD_LOGIC;
+    
+    -- Control registers
+    reg_config        : IN  t_eth_mm_reg_config;
+    reg_control       : IN  t_eth_mm_reg_control;
+    reg_continue_wr   : IN  STD_LOGIC;     -- used to know that control reg has been written and ETH module can continue from TX_PENDING state
+    reg_status        : OUT t_eth_mm_reg_status;
+    reg_status_wr     : IN  STD_LOGIC;     -- used to know that status reg has been read and can be cleared to remove the sla_interrupt
+    
+    -- Streaming sink Rx frame
+    -- . The rcv_rd, rcv_ack and rcv_done act instead of rcv_out.ready to have rcv_out ready per frame instead of per data word
+    rcv_rd            : OUT STD_LOGIC;
+    rcv_ack           : IN  STD_LOGIC;
+    rcv_done          : IN  STD_LOGIC;
+    rcv_in            : IN  t_dp_sosi;  -- packet memory is always ready for packet data
+    rcv_hdr_words     : IN  t_eth_total_header_arr;
+    rcv_hdr_status    : IN  t_eth_hdr_status;
+    
+    -- Streaming source Tx frame
+    xmt_in            : IN  t_dp_siso;
+    xmt_out           : OUT t_dp_sosi;
+    
+    -- MM frame memory    
+    mem_in            : OUT t_mem_mosi;
+    mem_out           : IN  t_mem_miso
+  );
+END eth_control;
+
+
+ARCHITECTURE rtl OF eth_control IS
+
+  -- Internal source ready latency of this component
+  CONSTANT c_this_src_latency : NATURAL := 1;  -- xmt_in, xmt_out
+  
+  TYPE t_state_enum IS (
+    s_idle,
+    s_rx_request,
+    s_rx_frame,
+    s_hdr_response,
+    s_tx_pending,
+    s_tx_frame,
+    s_tx_done
+  );
+  
+  SIGNAL nxt_rcv_rd           : STD_LOGIC;
+  SIGNAL rcv_in_eop_dly       : STD_LOGIC;
+  
+  SIGNAL hdr_response         : t_eth_total_header_arr;
+  SIGNAL nxt_hdr_response     : t_eth_total_header_arr;
+  SIGNAL hdr_word_cnt         : NATURAL RANGE 0 TO c_eth_total_header_nof_words := c_eth_total_header_nof_words;
+  SIGNAL nxt_hdr_word_cnt     : NATURAL;
+  SIGNAL hdr_en               : STD_LOGIC;
+  SIGNAL nxt_hdr_en           : STD_LOGIC;
+  SIGNAL hdr_done             : STD_LOGIC;
+  SIGNAL nxt_hdr_done         : STD_LOGIC;
+  
+  SIGNAL tx_insert            : STD_LOGIC;
+  SIGNAL nxt_tx_insert        : STD_LOGIC;
+  
+  SIGNAL i_reg_status         : t_eth_mm_reg_status;
+  SIGNAL nxt_reg_status       : t_eth_mm_reg_status;
+  
+  SIGNAL xmt_start            : STD_LOGIC;
+  SIGNAL nxt_xmt_start        : STD_LOGIC;
+  SIGNAL xmt_en               : STD_LOGIC;
+  SIGNAL nxt_xmt_en           : STD_LOGIC;
+  SIGNAL xmt_done             : STD_LOGIC;
+  SIGNAL nxt_xmt_done         : STD_LOGIC;
+  SIGNAL xmt_word_cnt         : NATURAL RANGE 0 TO c_eth_frame_nof_words;
+  SIGNAL nxt_xmt_word_cnt     : NATURAL;
+  SIGNAL tx_nof_words         : STD_LOGIC_VECTOR(c_eth_frame_nof_words_w-1 DOWNTO 0);
+  SIGNAL nxt_tx_nof_words     : STD_LOGIC_VECTOR(c_eth_frame_nof_words_w-1 DOWNTO 0);
+  SIGNAL tx_empty             : STD_LOGIC_VECTOR(c_eth_empty_w-1 DOWNTO 0);
+  SIGNAL nxt_tx_empty         : STD_LOGIC_VECTOR(c_eth_empty_w-1 DOWNTO 0);
+  
+  SIGNAL i_mem_in             : t_mem_mosi;
+  SIGNAL nxt_mem_in           : t_mem_mosi;
+  SIGNAL rd_val               : STD_LOGIC_VECTOR(0 TO c_mem_ram_rd_latency);  -- use [0] to combinatorially store rd (= rd_en)
+  SIGNAL nxt_rd_val           : STD_LOGIC_VECTOR(1 TO c_mem_ram_rd_latency);
+  SIGNAL rd_sop               : STD_LOGIC_VECTOR(    rd_val'RANGE);
+  SIGNAL nxt_rd_sop           : STD_LOGIC_VECTOR(nxt_rd_val'RANGE);
+  SIGNAL rd_eop               : STD_LOGIC_VECTOR(    rd_val'RANGE);
+  SIGNAL nxt_rd_eop           : STD_LOGIC_VECTOR(nxt_rd_val'RANGE);
+  
+  SIGNAL xmt_siso             : t_dp_siso;
+  SIGNAL xmt_sosi             : t_dp_sosi;
+  
+  SIGNAL i_xmt_out            : t_dp_sosi;
+    
+  -- ST->MM state machine
+  SIGNAL state                : t_state_enum;
+  SIGNAL nxt_state            : t_state_enum;
+  
+BEGIN
+
+  reg_status <= i_reg_status;
+  mem_in     <= i_mem_in;
+  xmt_out    <= i_xmt_out;
+  
+  p_clk : PROCESS(rst, clk)
+  BEGIN
+    IF rst='1' THEN
+      rcv_rd                   <= '0';
+      rcv_in_eop_dly           <= '0';
+      hdr_word_cnt             <= c_eth_total_header_nof_words;
+      hdr_en                   <= '0';
+      hdr_done                 <= '0';
+      tx_insert                <= '0';
+      xmt_start                <= '0';
+      i_reg_status             <= c_eth_mm_reg_status_rst;
+      tx_nof_words             <= (OTHERS=>'0');
+      tx_empty                 <= (OTHERS=>'0');
+      xmt_word_cnt             <= 0;
+      xmt_en                   <= '0';
+      xmt_done                 <= '0';
+      i_mem_in                 <= c_mem_mosi_rst;
+      rd_val(nxt_rd_val'RANGE) <= (OTHERS=>'0');
+      rd_sop(nxt_rd_val'RANGE) <= (OTHERS=>'0');
+      rd_eop(nxt_rd_val'RANGE) <= (OTHERS=>'0');
+      state                    <= s_idle;
+    ELSIF rising_edge(clk) THEN
+      rcv_rd                   <= nxt_rcv_rd;
+      rcv_in_eop_dly           <= rcv_in.eop;
+      hdr_response             <= nxt_hdr_response;
+      hdr_word_cnt             <= nxt_hdr_word_cnt;
+      hdr_en                   <= nxt_hdr_en;
+      hdr_done                 <= nxt_hdr_done;
+      tx_insert                <= nxt_tx_insert;
+      xmt_start                <= nxt_xmt_start;
+      i_reg_status             <= nxt_reg_status;
+      tx_nof_words             <= nxt_tx_nof_words;
+      tx_empty                 <= nxt_tx_empty;
+      xmt_word_cnt             <= nxt_xmt_word_cnt;
+      xmt_en                   <= nxt_xmt_en;
+      xmt_done                 <= nxt_xmt_done;
+      i_mem_in                 <= nxt_mem_in;
+      rd_val(nxt_rd_val'RANGE) <= nxt_rd_val;
+      rd_sop(nxt_rd_val'RANGE) <= nxt_rd_sop;
+      rd_eop(nxt_rd_val'RANGE) <= nxt_rd_eop;
+      state                    <= nxt_state;
+    END IF;
+  END PROCESS;
+  
+  ------------------------------------------------------------------------------
+  -- Response header
+  ------------------------------------------------------------------------------
+  
+  p_hdr_response : PROCESS(rcv_hdr_words, rcv_hdr_status, reg_config)
+  BEGIN
+    nxt_hdr_response   <= func_eth_response_header(     rcv_hdr_words, reg_config.mac_address);
+    IF rcv_hdr_status.is_arp='1' THEN
+      nxt_hdr_response <= func_eth_arp_response_header( rcv_hdr_words, reg_config.mac_address, reg_config.ip_address);
+    ELSIF rcv_hdr_status.is_icmp='1' THEN
+      nxt_hdr_response <= func_eth_icmp_response_header(rcv_hdr_words, reg_config.mac_address);
+    ELSIF rcv_hdr_status.is_udp='1' THEN
+      nxt_hdr_response <= func_eth_udp_response_header( rcv_hdr_words, reg_config.mac_address);
+    ELSIF rcv_hdr_status.is_ip='1' THEN
+      -- If it is another protocol over IP than ICMP or UDP, then prepare only IP response
+      nxt_hdr_response <= func_eth_ip_response_header(  rcv_hdr_words, reg_config.mac_address);
+    END IF;
+  END PROCESS;
+  
+  ------------------------------------------------------------------------------
+  -- Frame memory access
+  ------------------------------------------------------------------------------
+  
+  p_mem_access : PROCESS(i_mem_in, rcv_in, hdr_en, hdr_word_cnt, hdr_response, xmt_en, xmt_siso, xmt_word_cnt)
+  BEGIN
+    nxt_mem_in.address <= i_mem_in.address;
+    nxt_mem_in.wrdata  <= i_mem_in.wrdata;
+    nxt_mem_in.wr      <= '0';
+    nxt_mem_in.rd      <= '0';
+    nxt_hdr_word_cnt   <= 0;
+    nxt_xmt_word_cnt   <= 0;
+    -- Rx frame
+    IF rcv_in.valid='1' THEN
+      nxt_mem_in.address <= INCR_UVEC(i_mem_in.address, 1);
+      nxt_mem_in.wrdata  <= RESIZE_MEM_DATA(rcv_in.data);
+      nxt_mem_in.wr      <= '1';
+    END IF;
+    IF rcv_in.sop='1' THEN
+      nxt_mem_in.address <= TO_MEM_ADDRESS(c_eth_ram_rx_offset);  -- rx buffer starts at address 0
+    END IF;
+    -- Prepare the Tx header
+    IF hdr_en='1' THEN                                               -- tx buffer starts at address 380
+      nxt_mem_in.address <= TO_MEM_ADDRESS(c_eth_ram_tx_offset + hdr_word_cnt);
+      nxt_mem_in.wrdata  <= RESIZE_MEM_DATA(hdr_response(hdr_word_cnt));
+      nxt_mem_in.wr      <= '1';
+      nxt_hdr_word_cnt   <= hdr_word_cnt+1;
+    END IF;
+    -- Tx frame
+    IF xmt_en='1' THEN
+      nxt_mem_in.address <= TO_MEM_ADDRESS(c_eth_ram_tx_offset + xmt_word_cnt);
+      nxt_xmt_word_cnt   <= xmt_word_cnt;
+      IF xmt_siso.ready='1' THEN
+        nxt_mem_in.rd    <= '1';
+        nxt_xmt_word_cnt <= xmt_word_cnt+1;  -- rd_en side counter
+      END IF;
+    END IF;
+  END PROCESS;
+  
+    
+  ------------------------------------------------------------------------------
+  -- Tx frame output control
+  ------------------------------------------------------------------------------
+  
+  rd_val(0) <= i_mem_in.rd;
+  rd_sop(0) <= i_mem_in.rd WHEN xmt_word_cnt=1                      ELSE '0';
+  rd_eop(0) <= i_mem_in.rd WHEN xmt_word_cnt=UNSIGNED(tx_nof_words) ELSE '0';
+  
+  -- rd_val side after read latency
+  nxt_rd_val <= rd_val(0 TO rd_val'HIGH-1);
+  nxt_rd_sop <= rd_sop(0 TO rd_val'HIGH-1);
+  nxt_rd_eop <= rd_eop(0 TO rd_val'HIGH-1);
+
+  xmt_sosi.data  <= RESIZE_DP_DATA(mem_out.rddata);
+  xmt_sosi.valid <= rd_val(rd_val'HIGH);
+  xmt_sosi.sop   <= rd_sop(rd_val'HIGH);
+  xmt_sosi.eop   <= rd_eop(rd_val'HIGH);
+  xmt_sosi.empty <= RESIZE_DP_EMPTY(tx_empty);
+  
+  -- adapt mem read latency to xmt ready latency
+  u_xmt_out_adapter : ENTITY dp_lib.dp_latency_adapter
+  GENERIC MAP (
+    g_in_latency   => c_mem_ram_rd_latency+1,  -- = 2 + 1 latency for xmt_siso.ready -> nxt_mem_in.rd
+    g_out_latency  => c_this_src_latency       -- = 1
+  )
+  PORT MAP (
+    rst     => rst,
+    clk     => clk,
+    -- ST sink
+    snk_out => xmt_siso,
+    snk_in  => xmt_sosi,
+    -- ST source
+    src_in  => xmt_in,
+    src_out => i_xmt_out
+  );
+  
+  nxt_xmt_done <= i_xmt_out.eop;
+
+  ------------------------------------------------------------------------------
+  -- Master-slave handshake control via status register
+  ------------------------------------------------------------------------------
+  
+  -- Gather Rx and Tx status into the status register
+  p_reg_status : PROCESS(i_reg_status, reg_status_wr, rcv_in, rcv_in_eop_dly, i_mem_in, tx_insert, hdr_done, xmt_done)
+  BEGIN
+    nxt_reg_status <= i_reg_status;
+    -- MM write access to the status address clears the status register, which removes the interrupt
+    -- using write instead of read access to remove the interrupt allows for polling reg_status
+    IF reg_status_wr='1'    THEN nxt_reg_status          <= c_eth_mm_reg_status_rst; END IF;
+    -- Events that set the status register
+    IF rcv_in.eop='1'       THEN nxt_reg_status.rx_empty <= rcv_in.empty(c_eth_empty_w-1 DOWNTO 0); END IF;
+    IF rcv_in_eop_dly='1'   THEN
+      nxt_reg_status.rx_nof_words                                     <= (OTHERS=>'0');
+      nxt_reg_status.rx_nof_words(c_eth_frame_nof_words_w-1 DOWNTO 0) <= INCR_UVEC(i_mem_in.address(c_eth_frame_nof_words_w-1 DOWNTO 0), 1);
+    END IF;
+    IF tx_insert='1'        THEN nxt_reg_status.tx_avail <= '1'; END IF;
+    IF hdr_done='1'         THEN nxt_reg_status.rx_avail <= '1'; END IF;
+    IF xmt_done='1'         THEN nxt_reg_status.tx_done  <= '1'; END IF;
+  END PROCESS;
+  
+  -- Capture the control register tx fields for a new transmit
+  nxt_tx_nof_words <= reg_control.tx_nof_words(c_eth_frame_nof_words_w-1 DOWNTO 0) WHEN xmt_start='1' ELSE tx_nof_words;
+  nxt_tx_empty     <= reg_control.tx_empty                                         WHEN xmt_start='1' ELSE tx_empty;
+  
+  
+  ------------------------------------------------------------------------------
+  -- State machine
+  ------------------------------------------------------------------------------
+  
+  p_state : PROCESS(state, reg_control, reg_continue_wr, rcv_in, rcv_ack, rcv_done, hdr_word_cnt, xmt_siso, xmt_word_cnt, tx_nof_words, xmt_done)
+  BEGIN
+    nxt_rcv_rd     <= '0';  -- read frame start pulse
+    nxt_hdr_en     <= '0';  -- prepare response header enable
+    nxt_hdr_done   <= '0';  -- prepare response header done
+    nxt_tx_insert  <= '0';  -- accept tx insert request
+    nxt_xmt_start  <= '0';  -- write frame start pulse
+    nxt_xmt_en     <= '0';  -- write frame enable
+    
+    nxt_state <= state;
+    CASE state IS
+      WHEN s_idle =>
+        IF reg_control.tx_request='1' THEN   -- MM master requests to insert an extra tx frame
+          nxt_tx_insert <= '1';
+          nxt_state <= s_tx_pending;
+        END IF;
+        IF reg_control.rx_en='1' THEN    -- MM master enables default Rx-Tx operation so request a new rx frame
+          nxt_rcv_rd <= '1';
+          nxt_state <= s_rx_request;
+        END IF;
+      WHEN s_rx_request =>
+        IF rcv_in.sop='1' THEN              -- busy receiving a new frame (best check for sop, so no need to check rcv_busy, rcv_err in eth_rx_frame)
+          nxt_state <= s_rx_frame;
+        ELSIF rcv_ack='1' THEN              -- rcv ack with no rcv sop, so there was no rx frame pending, try request again
+          nxt_state <= s_idle;
+        END IF;
+      WHEN s_rx_frame =>
+        IF rcv_done='1' THEN                -- frame received
+          nxt_state <= s_hdr_response;
+        END IF;
+      WHEN s_hdr_response =>                -- prepare tx header for rx response
+        IF hdr_word_cnt < c_eth_total_header_nof_words-1 THEN
+          nxt_hdr_en <= '1';
+        ELSE
+          nxt_hdr_done <= '1';              -- new frame received and response prepared
+          nxt_state <= s_tx_pending;
+        END IF;
+      WHEN s_tx_pending =>                  -- wait for MM master
+        IF reg_continue_wr='1' THEN      -- MM master done
+          IF reg_control.tx_en='1' THEN  -- continue with response tx frame
+            nxt_xmt_start <= '1';
+            nxt_state <= s_tx_frame;
+          ELSE                              -- do not response tx frame
+            nxt_state <= s_idle;
+          END IF;
+        END IF;
+      WHEN s_tx_frame =>
+        IF xmt_siso.ready='0' OR xmt_word_cnt < UNSIGNED(tx_nof_words)-1 THEN
+          nxt_xmt_en <= '1';
+        ELSE
+          nxt_state <= s_tx_done;           -- frame transmitted
+        END IF;
+      WHEN OTHERS => -- s_tx_done
+        IF xmt_done='1' THEN
+          nxt_state <= s_idle;              -- frame transmitted, to be safe wait for xmt_done
+        END IF;
+    END CASE;
+  END PROCESS;
+  
+END rtl;
diff --git a/libraries/io/eth/src/vhdl/eth_hdr_ctrl.vhd b/libraries/io/eth/src/vhdl/eth_hdr_ctrl.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..55260a2ca594acd1b827a66dc718349c7b42fe56
--- /dev/null
+++ b/libraries/io/eth/src/vhdl/eth_hdr_ctrl.vhd
@@ -0,0 +1,212 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE common_lib.eth_layers_pkg.ALL;
+USE work.eth_pkg.ALL;
+
+-- Description:
+-- . for IP frames replace IP header checksum with the calculated value
+-- . discard frames shorter than 11 words
+-- . frame discard input to optionally discard a frame based on some header criteria
+
+ENTITY eth_hdr_ctrl IS
+  PORT (
+    -- Clocks and reset
+    rst             : IN  STD_LOGIC;
+    clk             : IN  STD_LOGIC;
+    
+    -- Stored header
+    hdr_words       : IN  t_eth_total_header_arr;
+    hdr_status      : IN  t_eth_hdr_status;
+    
+    frm_discard     : IN  STD_LOGIC := '0';
+    
+    -- ST interface
+    snk_in_word_cnt : IN  NATURAL RANGE 0 TO c_eth_total_header_nof_words;  -- snk_in hdr word count
+    snk_in          : IN  t_dp_sosi;
+    snk_out         : OUT t_dp_siso;
+    src_in          : IN  t_dp_siso;
+    src_out         : OUT t_dp_sosi
+  );
+END eth_hdr_ctrl;
+
+
+ARCHITECTURE rtl OF eth_hdr_ctrl IS
+
+  -- Internal sink ready latency and source ready latency of this component
+  CONSTANT c_this_snk_latency : NATURAL := 1;
+  CONSTANT c_this_src_latency : NATURAL := 1;
+    
+  TYPE t_state_enum IS (
+    s_idle,
+    s_store_header,
+    s_src_header_sop,
+    s_src_header,
+    s_src_data
+  );
+  
+  SIGNAL state               : t_state_enum;
+  SIGNAL nxt_state           : t_state_enum;
+  
+  -- Sink
+  SIGNAL snk_in_eop_hold     : STD_LOGIC;
+  SIGNAL nxt_snk_in_eop_hold : STD_LOGIC;
+  
+  -- Source
+  SIGNAL next_src_out        : t_dp_sosi;
+  SIGNAL i_src_out           : t_dp_sosi;
+  SIGNAL nxt_src_out         : t_dp_sosi;
+  SIGNAL src_word_cnt        : NATURAL RANGE 0 TO c_eth_total_header_nof_words;
+  SIGNAL nxt_src_word_cnt    : NATURAL;
+  
+BEGIN
+
+  src_out <= i_src_out;
+  
+  p_reg : PROCESS(rst, clk)
+  BEGIN
+    IF rst='1' THEN
+      state           <= s_idle;
+      snk_in_eop_hold <= '0';
+      src_word_cnt    <= 0;
+      i_src_out       <= c_dp_sosi_rst;
+    ELSIF rising_edge(clk) THEN
+      state           <= nxt_state;
+      snk_in_eop_hold <= nxt_snk_in_eop_hold;
+      src_word_cnt    <= nxt_src_word_cnt;
+      i_src_out       <= nxt_src_out;
+    END IF;
+  END PROCESS;
+  
+  
+  ------------------------------------------------------------------------------
+  -- Hold the sink input for source output
+  ------------------------------------------------------------------------------
+  
+  u_snk_in : ENTITY dp_lib.dp_hold_input
+  PORT MAP (
+    rst           => rst,
+    clk           => clk,
+    -- ST sink
+    snk_out       => OPEN,
+    snk_in        => snk_in,
+    -- ST source
+    src_in        => src_in,  -- must not use snk_out.ready here to avoid wrong first payload data
+    next_src_out  => next_src_out,
+    pend_src_out  => OPEN,
+    src_out_reg   => i_src_out
+  );  
+    
+  ------------------------------------------------------------------------------
+  -- Control state machine for the source output
+  ------------------------------------------------------------------------------
+  
+  -- Hold snk_in.eop, because snk_in packet can only have a header of exactly 11 words (e.g. ARP).
+  -- Discard snk_in packets that are shorter than 11 words (e.g. ???), for both Rx and Tx shorter frames are not useful.
+  nxt_snk_in_eop_hold <= '0' WHEN snk_in.sop='1' ELSE
+                         '1' WHEN snk_in.eop='1' ELSE snk_in_eop_hold;
+  
+  p_state : PROCESS(state, src_word_cnt, i_src_out, snk_in, snk_in_word_cnt, frm_discard, src_in, hdr_words, hdr_status, snk_in_eop_hold, next_src_out)
+  BEGIN
+    snk_out.ready     <= '1';           -- default accept sink input
+    nxt_state         <= state;
+    nxt_src_word_cnt  <= src_word_cnt;
+    nxt_src_out       <= i_src_out;
+    nxt_src_out.valid <= '0';
+    nxt_src_out.sop   <= '0';
+    nxt_src_out.eop   <= '0';
+    CASE state IS
+      WHEN s_idle =>
+        -- wait for new frame, then store the frame header and no output yet
+        IF snk_in.sop='1' THEN
+          nxt_src_word_cnt <= 0;
+          nxt_state        <= s_store_header;
+        END IF;
+      WHEN s_store_header =>
+        -- stop the sink when the whole header has arrived or when a eop has arrived
+        IF snk_in.valid='1' AND snk_in_word_cnt=c_eth_total_header_nof_words-1 THEN
+          snk_out.ready <= '0';
+          nxt_state <= s_src_header_sop;
+        ELSIF snk_in.eop='1' THEN
+          -- discard not supported packets shorter than 11 words
+          snk_out.ready <= '0';
+          nxt_state <= s_idle;
+        END IF;
+      WHEN s_src_header_sop =>
+        -- stop the input and output the (modified) header, first the sop
+        snk_out.ready <= '0';
+        IF frm_discard='1' THEN
+          -- discard this frame because of optional external reasons
+          nxt_state <= s_idle;
+        ELSIF src_in.ready='1' THEN
+          nxt_src_out.data  <= RESIZE_DP_DATA(hdr_words(src_word_cnt));
+          nxt_src_out.valid <= '1';
+          nxt_src_out.sop   <= '1';
+          nxt_src_out.eop   <= '0';
+          nxt_src_word_cnt  <= src_word_cnt+1;
+          nxt_state <= s_src_header;
+        END IF;
+      WHEN s_src_header =>
+        -- stop the input and output the rest of the (modified) header
+        snk_out.ready <= '0';
+        IF src_in.ready='1' THEN
+          nxt_src_out.data  <= RESIZE_DP_DATA(hdr_words(src_word_cnt));
+          nxt_src_out.valid <= '1';
+          nxt_src_out.sop   <= '0';
+          nxt_src_out.eop   <= '0';
+          nxt_src_word_cnt  <= src_word_cnt+1;
+          -- Replace IP header checksum with the calculated checksum from hdr_status.ip_checksum:
+          -- . for Rx: the verified checksum, so will be 0 when OK or != when the received frame has an IP header error.
+          -- . for Tx: the calculated checksum for the Tx IP header.
+          IF hdr_status.is_ip='1' AND src_word_cnt=c_ip_header_checksum_wi THEN
+            nxt_src_out.data(c_ip_header_checksum_w-1 DOWNTO 0) <= hdr_status.ip_checksum;
+          END IF;
+          IF src_word_cnt=c_eth_total_header_nof_words-1 THEN
+            IF snk_in_eop_hold='1' THEN
+              -- header only packet
+              nxt_src_out.eop <= '1';
+              nxt_state <= s_idle;
+            ELSE
+              -- packet with payload
+              nxt_state <= s_src_data;
+            END IF;
+          END IF;
+        END IF;
+      WHEN OTHERS =>  -- s_src_data
+        -- continue the input payload and output it
+        snk_out.ready <= src_in.ready;
+        nxt_src_out   <= next_src_out;
+        IF next_src_out.eop='1' THEN
+          nxt_state <= s_idle;
+        END IF;
+    END CASE;
+    
+    -- Pass on frame level flow control
+    snk_out.xon <= src_in.xon;
+  END PROCESS;
+
+END rtl;
diff --git a/libraries/io/eth/src/vhdl/eth_hdr_status.vhd b/libraries/io/eth/src/vhdl/eth_hdr_status.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..d729f14b85f887f344d14649e54a3c4a45846f51
--- /dev/null
+++ b/libraries/io/eth/src/vhdl/eth_hdr_status.vhd
@@ -0,0 +1,144 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE common_lib.eth_layers_pkg.ALL;
+USE work.eth_pkg.ALL;
+
+-- Purpose:
+--   Determine the Ethernet header status.
+-- Description:
+--   Determines all relevant information from the Ethernet header after every
+--   asserted sop.
+-- Remark:
+-- . g_ip_header_checksum_calculate:
+--   = TRUE, then the IP header checksum is calculated for hdr_status
+--   = FALSE, then the IP header checksum is read for hdr_status
+
+ENTITY eth_hdr_status IS
+  GENERIC (
+    g_ip_header_checksum_calculate : BOOLEAN := TRUE
+  );
+  PORT (
+    rst            : IN  STD_LOGIC;
+    clk            : IN  STD_LOGIC;
+    
+    -- Total header
+    hdr_words      : IN  t_eth_total_header_arr;
+    hdr_words_val  : IN  STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1);
+    hdr_fields     : IN  t_eth_total_header;
+    hdr_fields_val : IN  STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1);
+    hdr_data       : IN  STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
+    hdr_data_val   : IN  STD_LOGIC;
+    
+    -- Header status
+    hdr_status     : OUT t_eth_hdr_status
+  );
+END eth_hdr_status;
+
+
+ARCHITECTURE rtl OF eth_hdr_status IS
+
+  SIGNAL i_hdr_status        : t_eth_hdr_status;
+  SIGNAL nxt_hdr_status      : t_eth_hdr_status;
+  
+  SIGNAL ip_header           : t_dp_sosi;
+  SIGNAL calc_checksum       : STD_LOGIC_VECTOR(c_halfword_w-1 DOWNTO 0);
+  SIGNAL calc_checksum_val   : STD_LOGIC;
+  SIGNAL ip_checksum         : STD_LOGIC_VECTOR(c_halfword_w-1 DOWNTO 0);
+  SIGNAL ip_checksum_val     : STD_LOGIC;
+  
+BEGIN
+
+  hdr_status <= i_hdr_status;
+  
+  p_clk : PROCESS(rst, clk)
+  BEGIN
+    IF rst='1' THEN
+      i_hdr_status <= c_eth_hdr_status_rst;
+    ELSIF rising_edge(clk) THEN
+      i_hdr_status <= nxt_hdr_status;
+    END IF;
+  END PROCESS;
+  
+  ------------------------------------------------------------------------------
+  -- Packet type
+  nxt_hdr_status.is_arp  <= '1' WHEN UNSIGNED(hdr_fields.eth.eth_type)=c_eth_type_arp     AND hdr_fields_val(c_eth_type_wi)='1' ELSE '0';
+  nxt_hdr_status.is_ip   <= '1' WHEN UNSIGNED(hdr_fields.eth.eth_type)=c_eth_type_ip      AND hdr_fields_val(c_eth_type_wi)='1' ELSE '0';
+  nxt_hdr_status.is_icmp <= '1' WHEN UNSIGNED(hdr_fields.ip.protocol)=c_ip_protocol_icmp  AND hdr_fields_val(c_ip_protocol_wi)='1'  AND i_hdr_status.is_ip='1'  ELSE '0';
+  nxt_hdr_status.is_udp  <= '1' WHEN UNSIGNED(hdr_fields.ip.protocol)=c_ip_protocol_udp   AND hdr_fields_val(c_ip_protocol_wi)='1'  AND i_hdr_status.is_ip='1'  ELSE '0';
+  nxt_hdr_status.is_dhcp <= '1' WHEN UNSIGNED(hdr_fields.udp.dst_port)=c_udp_port_dhcp_in AND hdr_fields_val(c_udp_dst_port_wi)='1' AND i_hdr_status.is_udp='1' ELSE '0';
+  
+  ------------------------------------------------------------------------------
+  -- IP Header checksum
+  ip_header.data  <= RESIZE_dp_data(hdr_data);
+  ip_header.valid <= '1' WHEN UNSIGNED(hdr_words_val(c_ip_lo_wi TO c_ip_hi_wi))/=0 ELSE '0';
+  ip_header.sop   <= hdr_words_val(c_ip_lo_wi);
+  ip_header.eop   <= hdr_words_val(c_ip_hi_wi);
+  ip_header.empty <= (OTHERS=>'0');
+  
+  -- Calculate the IP header checksum for the packet header
+  u_calc_checksum : ENTITY work.eth_checksum
+  PORT MAP (
+    rst           => rst,
+    clk           => clk,
+    snk_in        => ip_header,
+    checksum      => calc_checksum,
+    checksum_val  => calc_checksum_val
+  );
+  
+  -- Use calculated IP header checksum
+  gen_calc : IF g_ip_header_checksum_calculate=TRUE GENERATE
+    ip_checksum     <= calc_checksum;
+    ip_checksum_val <= calc_checksum_val;
+  END GENERATE;
+  
+  -- Use IP header checksum field read from the frame header
+  gen_read : IF g_ip_header_checksum_calculate=FALSE GENERATE
+    ip_checksum     <= hdr_fields.ip.header_checksum;
+    ip_checksum_val <= calc_checksum_val;              -- use u_calc_checksum only for valid control
+  END GENERATE;
+  
+  -- If it is an IP packet then update the hdr_status IP checksum fields, else leave them 0
+  p_hdr_status_ip_checksum : PROCESS(i_hdr_status, ip_checksum, ip_checksum_val)
+  BEGIN
+    nxt_hdr_status.ip_checksum_val   <= '0';
+    nxt_hdr_status.ip_checksum       <= (OTHERS=>'0');
+    nxt_hdr_status.ip_checksum_is_ok <= '0';
+    IF i_hdr_status.is_ip='1' AND ip_checksum_val='1' THEN
+      nxt_hdr_status.ip_checksum_val <= '1';
+      nxt_hdr_status.ip_checksum     <= ip_checksum;
+      IF UNSIGNED(ip_checksum)=0 THEN
+        nxt_hdr_status.ip_checksum_is_ok <= '1';
+      END IF;
+    END IF;
+  END PROCESS;
+    
+  ------------------------------------------------------------------------------
+  -- UDP port number
+  nxt_hdr_status.udp_port <= hdr_fields.udp.dst_port;
+  
+END rtl;
diff --git a/libraries/io/eth/src/vhdl/eth_hdr_store.vhd b/libraries/io/eth/src/vhdl/eth_hdr_store.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..8bd70fdd4445d42306bb2180f0f990354786c749
--- /dev/null
+++ b/libraries/io/eth/src/vhdl/eth_hdr_store.vhd
@@ -0,0 +1,156 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE common_lib.eth_layers_pkg.ALL;
+USE work.eth_pkg.ALL;
+
+-- Purpose:
+--   Provide the total Ethernet header information for the user to decide upon.
+-- Description:
+--   Extract the first c_eth_total_header_nof_words = 11 words from an Ethernet
+--   packet and make them available via as a word array and as records fields.
+--   The component has three output formats:
+--   1) hdr_words, the 11 word header store
+--   2) hdr_data, the currently active header word
+--   3) hdr_fields, combinatorial mapping of the 11 word header store to 
+--      various Ethernet packet header fields.
+--   Which format to use depends on the application. The logic for words or
+--   header fields that are not used, will get optimized away during synthesis.
+--   The snk_in word count is also output for the header words. The first valid
+--   word marked by the snk_in.sop has count 0, the last header word has count
+--   10 and the payload words have count 11.
+-- Remarks:
+-- . This component acts as a stream monitor, therefore it does not output a
+--   snk_out.ready.
+-- . The word indices from eth_pkg together with hdr_words_val[] can be used
+--   to determine which word of the Ethernet header words is valid.
+-- . The word indices from eth_pkg together with hdr_fields_val[] can be used
+--   to determine which fields of the Ethernet header records are valid. The
+--   difference with hdr_words_val is that hdr_words_val is a single cycle 
+--   pulse, whereas hdr_fields_val keeps its state until the next sop.
+--
+
+ENTITY eth_hdr_store IS
+  PORT (
+    rst            : IN  STD_LOGIC;
+    clk            : IN  STD_LOGIC;
+    
+    -- Streaming Sink
+    snk_in          : IN  t_dp_sosi;
+    snk_in_word_cnt : OUT NATURAL RANGE 0 TO c_eth_total_header_nof_words;
+    
+    -- Total header
+    hdr_words      : OUT t_eth_total_header_arr;
+    hdr_words_val  : OUT STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1);
+    -- Combinatorial map of the 11 header words on to the Ethernet header records
+    hdr_fields     : OUT t_eth_total_header;
+    hdr_fields_val : OUT STD_LOGIC_VECTOR(0 TO c_eth_total_header_nof_words-1);
+    -- Support also outputting only the currently valid header data
+    hdr_data       : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
+    hdr_data_val   : OUT STD_LOGIC
+  );
+END eth_hdr_store;
+
+
+ARCHITECTURE rtl OF eth_hdr_store IS
+
+  SIGNAL word_cnt           : NATURAL RANGE 0 TO c_eth_total_header_nof_words;
+  SIGNAL nxt_word_cnt       : NATURAL;
+  
+  SIGNAL i_hdr_words        : t_eth_total_header_arr := (OTHERS=>(OTHERS=>'0'));  -- init to avoid numeric_std warning, no need to rst
+  SIGNAL nxt_hdr_words      : t_eth_total_header_arr;
+  SIGNAL nxt_hdr_words_val  : STD_LOGIC_VECTOR(hdr_fields_val'RANGE);
+  SIGNAL i_hdr_fields_val   : STD_LOGIC_VECTOR(hdr_fields_val'RANGE);
+  SIGNAL nxt_hdr_fields_val : STD_LOGIC_VECTOR(hdr_fields_val'RANGE);
+  SIGNAL i_hdr_data         : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0) := (OTHERS=>'0');  -- init to avoid numeric_std warning, no need to rst
+  SIGNAL nxt_hdr_data       : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
+  SIGNAL nxt_hdr_data_val   : STD_LOGIC;
+  
+BEGIN
+
+  snk_in_word_cnt <= word_cnt;
+  
+  hdr_words      <= i_hdr_words;
+  hdr_fields_val <= i_hdr_fields_val;
+  hdr_data       <= i_hdr_data;
+  
+  p_clk : PROCESS(rst, clk)
+  BEGIN
+    IF rst='1' THEN
+      -- input
+      -- internal
+      word_cnt         <= 0;
+      -- output
+      hdr_words_val    <= (OTHERS=>'0');
+      i_hdr_fields_val <= (OTHERS=>'0');
+      hdr_data_val     <= '0';
+    ELSIF rising_edge(clk) THEN
+      -- input
+      -- internal
+      word_cnt         <= nxt_word_cnt;
+      -- output
+      i_hdr_words      <= nxt_hdr_words;      -- stored array of 11 words
+      hdr_words_val    <= nxt_hdr_words_val;
+      i_hdr_fields_val <= nxt_hdr_fields_val;
+      i_hdr_data       <= nxt_hdr_data;       -- current word
+      hdr_data_val     <= nxt_hdr_data_val;
+    END IF;
+  END PROCESS;
+  
+  -- Extract the 11 words from the Ethernet header
+  p_words : PROCESS(word_cnt, i_hdr_words, i_hdr_fields_val, i_hdr_data, snk_in)
+  BEGIN
+    nxt_word_cnt       <= word_cnt;
+    nxt_hdr_words      <= i_hdr_words;
+    nxt_hdr_words_val  <= (OTHERS=>'0');
+    nxt_hdr_fields_val <= i_hdr_fields_val;
+    nxt_hdr_data       <= i_hdr_data;
+    nxt_hdr_data_val   <= '0';
+    IF snk_in.sop='1' THEN
+      nxt_hdr_fields_val <= (OTHERS=>'0');
+    END IF;
+    IF snk_in.valid='1' AND word_cnt < c_eth_total_header_nof_words THEN
+      -- new Ethernet header is arriving
+      nxt_word_cnt                 <= word_cnt + 1;
+      nxt_hdr_words(     word_cnt) <= snk_in.data(c_eth_data_w-1 DOWNTO 0);
+      nxt_hdr_words_val( word_cnt) <= '1';
+      nxt_hdr_fields_val(word_cnt) <= '1';
+      nxt_hdr_data                 <= snk_in.data(c_eth_data_w-1 DOWNTO 0);
+      nxt_hdr_data_val             <= '1';
+    END IF;
+    IF snk_in.eop='1' THEN
+      nxt_word_cnt <= 0;
+    END IF;
+  END PROCESS;
+  
+  -- Combinatorial map of the total header on to the Ethernet header records
+  hdr_fields.eth  <= func_eth_map_eth_header( i_hdr_words);
+  hdr_fields.arp  <= func_eth_map_arp_packet( i_hdr_words);
+  hdr_fields.ip   <= func_eth_map_ip_header(  i_hdr_words);
+  hdr_fields.icmp <= func_eth_map_icmp_header(i_hdr_words);
+  hdr_fields.udp  <= func_eth_map_udp_header( i_hdr_words);
+  
+END rtl;
diff --git a/libraries/io/eth/src/vhdl/eth_ihl_to_20.vhd b/libraries/io/eth/src/vhdl/eth_ihl_to_20.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..edaae16bb9a6f33ce938e3910725b704b3fcd60f
--- /dev/null
+++ b/libraries/io/eth/src/vhdl/eth_ihl_to_20.vhd
@@ -0,0 +1,216 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program IS free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program IS distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE common_lib.eth_layers_pkg.ALL;
+USE work.eth_pkg.ALL;
+
+-- Purpose:
+-- Description:
+--   Strip IPv4 headers to 20 bytes
+--   Set ihl to 5 (5 * 4 = 20 bytes)
+--   Ajust IPv4 Packet Total Length field
+--   IP Header Checksum is set to 0x0000 and should be ignored
+--   The checksum could be updated using https://tools.ietf.org/html/rfc1624
+-- Remarks:
+--  . c_this_snk_latency = 1
+--  . c_this_src_latency = 1
+
+  ------------------------------------------------------------------------------
+  -- IPv4 Packet
+  --
+  --  0       3 4     7 8            15 16   18 19                        31  wi
+  -- |----------------------------------------------------------------------|
+  -- | Version |  ihl  |    Services    |    IPv4 Packet Total Length       |  4
+  -- |----------------------------------------------------------------------|
+  -- |       Identification             | Flags |    Fragment Offset        |  5
+  -- |----------------------------------------------------------------------|
+  -- |     TTL         |    Protocol    |      Header Checksum              |  6
+  -- |----------------------------------------------------------------------|
+  -- |              Source IP Address                                       |  7
+  -- |----------------------------------------------------------------------|
+  -- |              Destination IP Address                                  |  8
+  -- |----------------------------------------------------------------------|
+  -- |              Options ...                                             |
+  -- |----------------------------------------------------------------------|
+
+
+ENTITY eth_ihl_to_20 IS
+  GENERIC (
+    incoming_ihl   : NATURAL := 20
+  );
+  PORT (
+    rst            : IN  STD_LOGIC;
+    clk            : IN  STD_LOGIC;
+
+    -- Streaming Sink
+    snk_in         : IN  t_dp_sosi;
+    snk_out        : OUT t_dp_siso;
+    
+    -- Streaming Source
+    src_in         : IN  t_dp_siso;
+    src_out        : OUT t_dp_sosi
+  );
+END eth_ihl_to_20;
+
+
+ARCHITECTURE rtl OF eth_ihl_to_20 IS
+
+  SIGNAL i_src_out        : t_dp_sosi;
+  
+  TYPE state_type IS (Idle, Eth_DestMAC0, Eth_DestMAC1, Eth_SrcMAC0, Eth_SrcMAC1, IPv4_lengths, IPv4_ID, IPv4_TTL, IPv4_SrcIP, IPv4_DestIP, IPv4_Options, IPv4_Payload);
+  SIGNAL state   : state_type;
+  
+  SIGNAL ihl : unsigned(c_ip_header_length_w-1 downto 0);
+  
+BEGIN
+
+  src_out <= i_src_out;
+  --i_src_out <= snk_in;
+
+  -- Pass on frame level flow control
+  snk_out.xon <= src_in.xon;
+  
+  -- No change in ready latency, c_this_snk_latency = c_this_src_latency
+  snk_out.ready <= src_in.ready;
+  
+
+  PROCESS(clk, rst)
+  BEGIN
+    IF rst = '1' THEN 
+      state <= Idle;
+      ihl   <= TO_UNSIGNED(0,ihl'LENGTH);
+    ELSIF (rising_edge(clk)) THEN
+      i_src_out <= snk_in;
+      CASE state IS
+        WHEN Idle=>
+          IF snk_in.sop = '1' and snk_in.valid = '1' THEN 
+            state <= Eth_DestMAC0;
+          END IF;
+          
+        WHEN Eth_DestMAC0=>
+          IF snk_in.valid = '1' THEN 
+            state <= Eth_DestMAC1;
+          END IF;
+          
+        
+        WHEN Eth_DestMAC1  =>
+          IF snk_in.valid = '1' THEN 
+            state <= Eth_SrcMAC0;
+          END IF;
+          
+        
+        WHEN Eth_SrcMAC0  =>
+          IF snk_in.valid = '1' THEN 
+            IF snk_in.data(c_eth_type_slv'RANGE) = TO_UVEC(c_eth_type_ip,c_eth_type_w) THEN -- if IPv4 frame
+              state <= Eth_SrcMAC1;
+            ELSE
+              state <= Idle;
+            END IF;
+          END IF;
+          
+        
+        WHEN Eth_SrcMAC1  =>
+          IF snk_in.valid = '1' THEN
+            IF snk_in.data(27 downto 24) = TO_UVEC(c_ip_header_length,c_ip_header_length_w) THEN  -- if ihl = 20, nothing to do
+              state <= Idle;
+            ELSE
+              state <= IPv4_lengths;
+              i_src_out.data(27 downto 24) <= TO_UVEC(c_ip_header_length ,c_ip_header_length_w);  -- make IHL = 20
+              i_src_out.data(c_ip_total_length_w-1 downto 0) <= std_logic_vector(unsigned(snk_in.data(c_ip_total_length_w-1 downto 0))
+                                                                                 - ((unsigned(snk_in.data(27 downto 24))
+                                                                                    - to_unsigned(c_ip_header_length ,c_ip_header_length_w)
+                                                                                    ) & "00"
+                                                                                   )
+                                                                                );                -- correct Total Length
+              ihl <= unsigned(snk_in.data(27 downto 24));  -- save IHL
+            END IF;
+          END IF;
+        
+        
+        WHEN IPv4_lengths  =>
+          IF snk_in.valid = '1' THEN
+            state <= IPv4_ID;
+            ihl <= ihl - 1;
+          END IF;
+      
+      
+        WHEN IPv4_ID  =>
+          IF snk_in.valid = '1' THEN
+            state <= IPv4_TTL;
+            i_src_out.data(c_ip_header_checksum_w-1 downto 0) <= TO_UVEC(0,c_ip_header_checksum_w);
+            ihl <= ihl - 1;
+          END IF;
+          
+        
+        WHEN IPv4_TTL  =>
+          IF snk_in.valid = '1' THEN
+            state <= IPv4_SrcIP;
+            ihl <= ihl - 1;
+          END IF;
+          
+        
+        WHEN IPv4_SrcIP  =>
+          IF snk_in.valid = '1' THEN
+            state <= IPv4_DestIP;
+            ihl <= ihl - 1;
+          END IF;
+          
+        
+        WHEN IPv4_DestIP  =>
+          IF snk_in.valid = '1' THEN
+            i_src_out.valid <= '0';    -- do not allow option words to get through this module
+            state <= IPv4_Options;
+            ihl <= ihl - 1;
+            IF ihl = 2 THEN
+              state <= IPv4_Payload;
+            END IF;
+          END IF;
+          
+        WHEN IPv4_Options  =>
+          IF snk_in.valid = '1' THEN
+            i_src_out.valid <= '0';    -- do not allow option words to get through this module
+            ihl <= ihl - 1;
+            IF ihl = 2 THEN
+              state <= IPv4_Payload;
+            END IF;
+          END IF;
+          
+        
+        WHEN IPv4_Payload =>
+          IF snk_in.eop = '1' and snk_in.valid = '1' THEN
+            state <= Idle;
+          END IF;
+          
+        WHEN others =>
+          state <= Idle;
+      END CASE;
+    END IF;
+  end PROCESS    ;
+
+  
+
+END rtl;
diff --git a/libraries/io/eth/src/vhdl/eth_mm_reg_frame.vhd b/libraries/io/eth/src/vhdl/eth_mm_reg_frame.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..2c2d537dc3f692c6b52a6b9a6e1dec65638e46d4
--- /dev/null
+++ b/libraries/io/eth/src/vhdl/eth_mm_reg_frame.vhd
@@ -0,0 +1,74 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.eth_layers_pkg.ALL;
+USE work.eth_pkg.ALL;
+
+ENTITY eth_mm_reg_frame IS
+  PORT (
+    -- Clocks and reset
+    rst             : IN  STD_LOGIC;  -- reset synchronous with clk
+    clk             : IN  STD_LOGIC;  -- packet stream clock
+    
+    -- Inputs need for the frame register
+    hdr_fields      : IN  t_eth_total_header;                         -- Header info
+    hdr_status      : IN  t_eth_hdr_status;                           -- Header info
+    erc_word        : IN  STD_LOGIC_VECTOR(c_eth_data_w-1 DOWNTO 0);  -- Extracted CRC word (enumerate: 0=OK, >0 AND odd = Error)
+    reg_config      : IN  t_eth_mm_reg_config;                        -- Config setting
+    
+    -- Frame register
+    reg_frame       : OUT t_eth_mm_reg_frame    
+  );
+END eth_mm_reg_frame;
+
+
+ARCHITECTURE str OF eth_mm_reg_frame IS
+
+  SIGNAL nxt_reg_frame : t_eth_mm_reg_frame;
+  
+BEGIN
+
+  p_reg : PROCESS(rst, clk)
+  BEGIN
+    IF rst='1' THEN
+      reg_frame <= c_eth_mm_reg_frame_rst;
+    ELSIF rising_edge(clk) THEN
+      reg_frame <= nxt_reg_frame;
+    END IF;
+  END PROCESS;
+  
+  nxt_reg_frame.is_dhcp           <= hdr_status.is_dhcp;
+  nxt_reg_frame.is_udp_ctrl_port  <= '1' WHEN hdr_status.is_udp='1' AND UNSIGNED(hdr_status.udp_port)=UNSIGNED(reg_config.udp_port) ELSE '0';
+  nxt_reg_frame.is_udp            <= hdr_status.is_udp;
+  nxt_reg_frame.is_icmp           <= hdr_status.is_icmp;
+  nxt_reg_frame.ip_address_match  <= '1' WHEN UNSIGNED(hdr_fields.ip.dst_ip_addr)=UNSIGNED(reg_config.ip_address) ELSE '0';
+  nxt_reg_frame.ip_checksum_is_ok <= hdr_status.ip_checksum_is_ok;
+  nxt_reg_frame.is_ip             <= hdr_status.is_ip;
+  nxt_reg_frame.is_arp            <= hdr_status.is_arp;
+  nxt_reg_frame.mac_address_match <= '1' WHEN UNSIGNED(hdr_fields.eth.dst_mac)=UNSIGNED(reg_config.mac_address) ELSE '0';
+  nxt_reg_frame.eth_mac_error     <= erc_word(c_eth_error_w-1 DOWNTO 0);
+  
+END str;
diff --git a/libraries/io/eth/tb/vhdl/tb_eth_ihl_to_20.vhd b/libraries/io/eth/tb/vhdl/tb_eth_ihl_to_20.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..683990729ab5873bde46737ae101119070b8864c
--- /dev/null
+++ b/libraries/io/eth/tb/vhdl/tb_eth_ihl_to_20.vhd
@@ -0,0 +1,225 @@
+-------------------------------------------------------------------------------
+--
+-- Copyright (C) 2010
+-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/>
+-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/>
+-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands
+--
+-- This program is free software: you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation, either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+-------------------------------------------------------------------------------
+
+LIBRARY IEEE, common_lib, dp_lib;
+USE IEEE.std_logic_1164.ALL;
+USE IEEE.numeric_std.ALL;
+USE common_lib.common_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE common_lib.eth_layers_pkg.ALL;
+USE work.eth_pkg.ALL;
+
+
+ENTITY tb_eth_IHL_to_20 IS
+END tb_eth_IHL_to_20;
+
+
+ARCHITECTURE tb OF tb_eth_IHL_to_20 IS
+
+  CONSTANT clk_period : TIME := 5 ns;  -- 100 MHz
+
+
+  SIGNAL clk            : STD_LOGIC := '0';
+  SIGNAL rst            : STD_LOGIC;
+
+
+  SIGNAL snk_in         : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL snk_out        : t_dp_siso;
+  
+  SIGNAL src_in         : t_dp_siso := c_dp_siso_rdy;
+  SIGNAL src_out        : t_dp_sosi;
+
+  TYPE int_arr is array (natural range <>) of integer;
+  CONSTANT c_IHL_to_test : int_arr(1 to 11) := (5,6,7,8,9,10,11,12,13,14,15);
+  CONSTANT c_len_to_test : int_arr(1 to 5) := (0,1,16,20,3000);
+  
+
+  PROCEDURE gen_eth_frame (constant IHL             : natural;
+                           constant UDP_payload_len : natural;
+                           signal   clk             : in std_logic;
+                           signal   snk_in          : inout t_dp_sosi) is
+  BEGIN
+    snk_in.sop   <= '1';
+    snk_in.valid <= '1';
+    snk_in.data  <= RESIZE_DP_DATA(X"0000FFFF");  -- Eth header
+    WAIT UNTIL rising_edge(clk);
+    snk_in.sop   <= '0';
+    snk_in.data  <= RESIZE_DP_DATA(X"FFFFFFFF");
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(X"10FA0004");
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(X"00000800");
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(X"4" &        -- IPv4 header 
+                                   TO_UVEC(IHL,c_ip_header_length_w) &
+                                   X"00" &
+                                   TO_UVEC((IHL+UDP_payload_len+2)*4,c_ip_total_length_w));
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(X"00004000");
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(X"80110000");
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(X"0A0B0001");
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(X"0A0B00FF");
+    WAIT UNTIL rising_edge(clk);
+
+    FOR I IN 6 TO IHL LOOP
+      snk_in.data  <= RESIZE_DP_DATA(X"BAD000" & TO_UVEC(I,c_ip_header_length_w));  -- optionnal option word
+      WAIT UNTIL rising_edge(clk);
+    END LOOP;
+    
+    snk_in.data  <= RESIZE_DP_DATA(X"10FA10FA");  -- UDP header
+    WAIT UNTIL rising_edge(clk);
+    snk_in.data  <= RESIZE_DP_DATA(TO_UVEC((UDP_payload_len+2)*4,c_udp_total_length_w) & X"0000");
+    WAIT UNTIL rising_edge(clk);
+    
+    FOR I IN 0 TO UDP_payload_len-1 LOOP                  -- UDP payload
+      snk_in.data  <= RESIZE_DP_DATA(X"BEEF" & TO_UVEC(I,16));  
+      WAIT UNTIL rising_edge(clk);
+    END LOOP;
+
+    snk_in.data  <= RESIZE_DP_DATA(X"CCCCCCCC");  -- Eth CRC
+    snk_in.eop   <= '1';
+    WAIT UNTIL rising_edge(clk);
+
+    snk_in.data  <= (OTHERS=>'0');
+    snk_in.valid <= '0';
+    snk_in.eop   <= '0';
+    WAIT UNTIL rising_edge(clk);
+  END PROCEDURE gen_eth_frame;
+
+  PROCEDURE check_eth_frame ( constant UDP_payload_len : natural;
+                              signal   clk             : in std_logic;
+                              signal   src_out         : in t_dp_sosi) is
+    CONSTANT c_IHL : natural := 5;
+  BEGIN
+    
+     -- Eth header
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1' and src_out.sop = '1';
+    ASSERT src_out.data(31 downto 0) = X"0000FFFF" REPORT "Wrong word align and Destination MAC"       SEVERITY FAILURE; 
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"FFFFFFFF" REPORT "Wrong Destination MAC"                      SEVERITY FAILURE;
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"10FA0004" REPORT "Wrong Source MAC"                           SEVERITY FAILURE;
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"00000800" REPORT "Wrong Source MAC and EtherType"             SEVERITY FAILURE;
+    
+    -- IPv4 header
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"4" &
+                                       TO_UVEC(c_IHL,c_ip_header_length_w) &
+                                       X"00" &
+                                       TO_UVEC((c_IHL+UDP_payload_len+2)*4,c_ip_total_length_w)
+                                                   REPORT "Wrong Version / IHL / Total Length"         SEVERITY FAILURE;
+    
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"00004000" REPORT "Wrong identification / Flags / Frag Offset" SEVERITY FAILURE;
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"80110000" REPORT "Wrong TTL / Protocol / Checksum"            SEVERITY FAILURE;
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"0A0B0001" REPORT "Wrong Source IP"                            SEVERITY FAILURE;
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = X"0A0B00FF" REPORT "Wrong Dest IP"                              SEVERITY FAILURE;
+    -- No options Here
+
+    -- UDP header
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';                                                                 
+    ASSERT src_out.data(31 downto 0) = X"10FA10FA" REPORT "Wrong UDP ports"                            SEVERITY FAILURE;
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+    ASSERT src_out.data(31 downto 0) = TO_UVEC((UDP_payload_len+2)*4,c_udp_total_length_w) &
+                                       X"0000" 
+                                                   REPORT "Wrong UDP length / CRC"                     SEVERITY FAILURE;
+    -- UDP payload
+    FOR I IN 0 TO UDP_payload_len-1 LOOP
+      WAIT UNTIL falling_edge(clk) and src_out.valid = '1';
+      ASSERT src_out.data(31 downto 0) = X"BEEF" & TO_UVEC(I,16) REPORT "Wrong UDP Payload"            SEVERITY FAILURE;
+--      ASSERT src_out.data(31 downto 0) = X"BEEF" & TO_UVEC(I,16) REPORT "Wrong UDP Payload: 0xBEEF" & TO_UVEC(I,16)'IMAGE                SEVERITY FAILURE;
+    END LOOP;
+
+    -- Eth CRC
+    WAIT UNTIL falling_edge(clk) and src_out.valid = '1' and src_out.eop   <= '1';
+    ASSERT src_out.data(31 downto 0) = X"CCCCCCCC" REPORT "Wrong Eth CRC"                              SEVERITY FAILURE;
+
+  END PROCEDURE check_eth_frame;
+  
+  
+BEGIN
+
+  clk <= NOT clk AFTER clk_period/2;
+  rst <= '1', '0' AFTER clk_period*7;
+  
+  
+  gen_frame: PROCESS  
+  BEGIN
+    WAIT UNTIL rst='0';
+
+    FOR len_n in c_len_to_test'RANGE LOOP
+      FOR IHL_n in c_IHL_to_test'RANGE LOOP
+        WAIT FOR 50 ns; WAIT UNTIL rising_edge(clk);
+        gen_eth_frame (c_IHL_to_test(IHL_n),
+                       c_len_to_test(len_n),
+                       clk,snk_in);
+      END LOOP;
+    END LOOP;
+    
+    wait for 1 ms;
+    ASSERT FALSE REPORT "ERROR: Processing was too long.  DUT is stuck" SEVERITY FAILURE;
+    WAIT;
+  END PROCESS;
+
+  
+  dut : ENTITY work.eth_IHL_to_20
+  GENERIC MAP (
+    incoming_IHL => 24
+  )
+  PORT MAP (
+    rst         => rst,
+    clk         => clk,
+
+    -- Streaming Sink
+    snk_in      => snk_in,
+    snk_out     => snk_out,
+    
+    -- Streaming Source
+    src_in      => src_in,
+    src_out     => src_out
+  );
+  
+  
+  check_frame: PROCESS  
+  BEGIN
+
+    FOR len_n in c_len_to_test'RANGE LOOP
+      FOR IHL_n in c_IHL_to_test'RANGE LOOP
+        check_eth_frame (
+                         c_len_to_test(len_n),
+                         clk,src_out);
+      END LOOP;
+    END LOOP;
+    
+    WAIT for 1 us;
+    ASSERT FALSE REPORT "Simulation finished: NO ERRORS" SEVERITY FAILURE;
+    WAIT;
+  END PROCESS;
+
+END tb;