diff --git a/libraries/io/eth1g/hdllib.cfg b/libraries/io/eth1g/hdllib.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..13c93e084190c6198604956a18240952209b920b
--- /dev/null
+++ b/libraries/io/eth1g/hdllib.cfg
@@ -0,0 +1,26 @@
+hdl_lib_name = eth1g
+hdl_library_clause_name = eth1g_lib
+hdl_lib_uses_synth = dp common eth
+hdl_lib_uses_sim = 
+hdl_lib_technology = 
+
+synth_files =
+    src/vhdl/eth1g.vhd
+    
+test_bench_files = 
+    tb/vhdl/tb_eth1g.vhd
+    tb/vhdl/tb_tb_eth1g.vhd
+
+regression_test_vhdl = 
+#    tb/vhdl/tb_eth_checksum.vhd
+#    tb/vhdl/tb_eth_crc_ctrl.vhd
+#    tb/vhdl/tb_eth_hdr.vhd
+#    tb/vhdl/tb_eth_udp_offload.vhd
+#    tb/vhdl/tb_eth_ihl_to_20.vhd
+#    tb/vhdl/tb_tb_eth.vhd
+
+
+[modelsim_project_file]
+
+
+[quartus_project_file]
diff --git a/libraries/io/eth1g/src/vhdl/eth1g.vhd b/libraries/io/eth1g/src/vhdl/eth1g.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..d2c754399e82486a78337abf50506dd5ba79587e
--- /dev/null
+++ b/libraries/io/eth1g/src/vhdl/eth1g.vhd
@@ -0,0 +1,668 @@
+-------------------------------------------------------------------------------
+--
+-- 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/>.
+--
+-------------------------------------------------------------------------------
+
+-- Purpose:
+--   Provide Ethernet control access to a node and some UDP ports for streaming
+--   data.
+-- Description:
+--   Connect the 1GbE TSE to the microprocessor and to streaming UDP ports. The
+--   packets for the streaming channels are directed based on the UDP port
+--   number and all other packets are transfered to the default control channel.
+
+LIBRARY IEEE, common_lib, technology_lib, dp_lib, eth_lib, tech_tse_lib;
+USE IEEE.std_logic_1164.ALL;
+USE common_lib.common_pkg.ALL;
+USE common_lib.common_mem_pkg.ALL;
+USE common_lib.common_network_layers_pkg.ALL;
+USE common_lib.common_network_total_header_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE tech_tse_lib.tech_tse_pkg.ALL;
+USE eth_lib.eth_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+
+ENTITY eth1g IS
+  GENERIC (
+    g_technology         : NATURAL := c_tech_select_default;
+    g_init_ip_address    : STD_LOGIC_VECTOR(c_network_ip_addr_w-1 DOWNTO 0) := X"00000000"; -- 0.0.0.0
+    g_cross_clock_domain : BOOLEAN := TRUE;    -- use FALSE when mm_clk and st_clk are the same, else use TRUE to cross the clock domain
+    g_ETH_PHY            : STRING  := "LVDS";  -- "LVDS" (default): uses LVDS IOs for ctrl_unb_common, "XCVR": uses tranceiver PHY
+    g_ihl20              : BOOLEAN := FALSE;
+    g_frm_discard_en     : BOOLEAN := FALSE     -- when TRUE discard frame types that would otherwise have to be discarded by the Nios MM master
+  );
+  PORT (
+    -- Clocks and reset
+    mm_rst             : IN  STD_LOGIC;  -- reset synchronous with mm_clk
+    mm_clk             : IN  STD_LOGIC;  -- memory-mapped bus clock
+    eth_clk            : IN  STD_LOGIC;  -- ethernet phy reference clock
+    st_rst             : IN  STD_LOGIC;  -- reset synchronous with st_clk
+    st_clk             : IN  STD_LOGIC;  -- packet stream clock
+    
+    cal_rec_clk        : IN  STD_LOGIC := '0';  -- Calibration & reconfig clock when using XCVR
+    
+    -- UDP transmit interface
+    udp_tx_snk_in_arr  : IN  t_dp_sosi_arr(c_eth_nof_udp_ports-1 DOWNTO 0) := (OTHERS=> c_dp_sosi_rst);  -- ST sinks, default not valid if not used
+    udp_tx_snk_out_arr : OUT t_dp_siso_arr(c_eth_nof_udp_ports-1 DOWNTO 0);
+    -- UDP receive interface
+    udp_rx_src_in_arr  : IN  t_dp_siso_arr(c_eth_nof_udp_ports-1 DOWNTO 0) := (OTHERS=> c_dp_siso_rdy);  -- ST sources, default ready if not used
+    udp_rx_src_out_arr : OUT t_dp_sosi_arr(c_eth_nof_udp_ports-1 DOWNTO 0);
+
+    -- Memory Mapped Slaves
+    tse_sla_in         : IN  t_mem_mosi;  -- ETH TSE MAC registers
+    tse_sla_out        : OUT t_mem_miso;
+    reg_sla_in         : IN  t_mem_mosi;  -- ETH control and status registers
+    reg_sla_out        : OUT t_mem_miso;
+    reg_sla_interrupt  : OUT STD_LOGIC;   -- Interrupt
+    ram_sla_in         : IN  t_mem_mosi;  -- ETH rx frame and tx frame memory
+    ram_sla_out        : OUT t_mem_miso;
+
+    -- Monitoring
+    rx_flushed_frm_cnt : OUT STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);  -- only used in simulation, because it will get optimized away in synthesis
+
+    -- PHY interface
+    eth_txp            : OUT STD_LOGIC;
+    eth_rxp            : IN  STD_LOGIC;
+
+    -- LED interface
+    tse_led            : OUT t_tech_tse_led
+  );
+END eth1g;
+
+
+ARCHITECTURE str OF eth1g IS
+
+  ------------------------------------------------------------------------------
+  -- ETH Rx packet buffer and Tx packet buffer
+  ------------------------------------------------------------------------------
+  
+  -- Use MM bus data width = c_word_w = 32
+  CONSTANT c_mm_ram  : t_c_mem := (latency  => c_mem_ram_rd_latency,
+                                   adr_w    => c_eth_ram_addr_w,
+                                   dat_w    => c_word_w,
+                                   nof_dat  => c_eth_ram_nof_words,
+                                   init_sl  => '0');
+                                   
+  SIGNAL mem_in         : t_mem_mosi;  -- big endian on ST and TSE MAC network side
+  SIGNAL mem_out        : t_mem_miso;  -- big endian on ST and TSE MAC network side
+  SIGNAL mem_in_endian  : t_mem_mosi;  -- keep big endian on MM side
+  SIGNAL mem_out_endian : t_mem_miso;  -- keep big endian on MM side
+                                   
+  ------------------------------------------------------------------------------
+  -- ETH stream
+  ------------------------------------------------------------------------------
+  
+  -- Multiplex - demultiplex
+  CONSTANT c_mux_nof_ports    : NATURAL := 1 + c_eth_nof_udp_ports; -- One for control + nof UDP ports
+  CONSTANT c_demux_nof_ports  : NATURAL := c_mux_nof_ports;
+  CONSTANT c_demux_combined   : BOOLEAN := FALSE;  -- when TRUE then all downstream sinks must be ready, when FALSE then only the
+                                                   -- selected sink needs to be ready (see dp_demux for more explanation).
+  -- All Rx (so UDP off-load and other ETH traffic)
+  SIGNAL rx_adapt_siso        : t_dp_siso;
+  SIGNAL rx_adapt_sosi        : t_dp_sosi;
+  
+  SIGNAL rx_crc_siso          : t_dp_siso;
+  SIGNAL rx_crc_sosi          : t_dp_sosi;
+  
+  SIGNAL rx_ihl20_siso        : t_dp_siso;
+  SIGNAL rx_ihl20_sosi        : t_dp_sosi;
+  
+  SIGNAL rx_hdr_siso          : t_dp_siso;
+  SIGNAL rx_hdr_sosi          : t_dp_sosi;
+  SIGNAL rx_hdr_status          : t_eth_hdr_status;
+  SIGNAL rx_hdr_status_complete : STD_LOGIC;
+  
+  -- Rx demux
+  SIGNAL rx_channel_siso      : t_dp_siso;
+  SIGNAL rx_channel_sosi      : t_dp_sosi;
+  
+  SIGNAL demux_siso_arr       : t_dp_siso_arr(0 TO c_mux_nof_ports-1);
+  SIGNAL demux_sosi_arr       : t_dp_sosi_arr(0 TO c_mux_nof_ports-1);
+  
+  -- ETH Rx
+  SIGNAL eth_rx_siso          : t_dp_siso;
+  SIGNAL eth_rx_sosi          : t_dp_sosi;
+  
+  SIGNAL rx_frm_discard         : STD_LOGIC;
+  SIGNAL rx_frm_discard_val     : STD_LOGIC;
+  SIGNAL rx_eth_discard         : STD_LOGIC;
+  SIGNAL rx_eth_discard_val     : STD_LOGIC;
+
+  SIGNAL rx_frame_rd            : STD_LOGIC;
+  SIGNAL rx_frame_ack           : STD_LOGIC;
+  SIGNAL rx_frame_done          : STD_LOGIC;
+  SIGNAL rx_frame_sosi          : t_dp_sosi;
+  SIGNAL rx_frame_hdr_words_arr : t_network_total_header_32b_arr;
+  SIGNAL rx_frame_hdr_fields    : t_network_total_header;
+  SIGNAL rx_frame_hdr_status    : t_eth_hdr_status;
+  SIGNAL rx_frame_crc_word      : STD_LOGIC_VECTOR(c_eth_data_w-1 DOWNTO 0);
+  
+  -- ETH Tx
+  SIGNAL eth_tx_siso          : t_dp_siso;
+  SIGNAL eth_tx_sosi          : t_dp_sosi;
+  
+  -- Tx mux
+  SIGNAL mux_siso_arr         : t_dp_siso_arr(0 TO c_mux_nof_ports-1);
+  SIGNAL mux_sosi_arr         : t_dp_sosi_arr(0 TO c_mux_nof_ports-1);
+  
+  -- All Tx (so UDP off-load and other ETH traffic)
+  SIGNAL tx_mux_siso          : t_dp_siso;
+  SIGNAL tx_mux_sosi          : t_dp_sosi;
+  
+  SIGNAL tx_hdr_siso          : t_dp_siso;
+  SIGNAL tx_hdr_sosi          : t_dp_sosi;
+  
+  ------------------------------------------------------------------------------
+  -- MM registers (in st_clk domain)
+  ------------------------------------------------------------------------------
+  
+  -- . write/read back
+  SIGNAL reg_demux            : t_eth_mm_reg_demux;
+  SIGNAL reg_config           : t_eth_mm_reg_config;
+  SIGNAL reg_control          : t_eth_mm_reg_control;
+  SIGNAL reg_continue_wr      : STD_LOGIC;
+  -- . read only
+  SIGNAL reg_frame            : t_eth_mm_reg_frame;
+  SIGNAL reg_status           : t_eth_mm_reg_status;
+  SIGNAL reg_status_wr        : STD_LOGIC;
+  
+  ------------------------------------------------------------------------------
+  -- TSE MAC
+  ------------------------------------------------------------------------------
+  -- . MAC Transmit Stream
+  SIGNAL tse_tx_siso        : t_dp_siso;
+  SIGNAL tse_tx_sosi        : t_dp_sosi;
+  -- . MAC specific
+  SIGNAL tse_tx_mac_in      : t_tech_tse_tx_mac;
+  SIGNAL tse_tx_mac_out     : t_tech_tse_tx_mac;
+  -- . MAC Receive Stream
+  SIGNAL tse_rx_sosi        : t_dp_sosi;
+  SIGNAL tse_rx_siso        : t_dp_siso;
+  -- . MAC specific
+  SIGNAL tse_rx_mac_out     : t_tech_tse_rx_mac;
+
+BEGIN
+
+  ------------------------------------------------------------------------------
+  -- MM registers
+  ------------------------------------------------------------------------------
+  
+  u_mm_registers : ENTITY eth_lib.eth_mm_registers
+  GENERIC MAP (
+    g_cross_clock_domain => g_cross_clock_domain,
+    g_init_ip_address    => g_init_ip_address
+  )
+  PORT MAP (
+    -- Clocks and reset
+    mm_rst             => mm_rst,
+    mm_clk             => mm_clk,
+    st_rst             => st_rst,
+    st_clk             => st_clk,
+    -- Memory Mapped Slave
+    sla_in             => reg_sla_in,
+    sla_out            => reg_sla_out,
+    sla_interrupt      => reg_sla_interrupt,
+    -- MM registers in st_clk domain
+    -- . write/read back
+    st_reg_demux       => reg_demux,
+    st_reg_config      => reg_config,
+    st_reg_control     => reg_control,
+    st_reg_continue_wr => reg_continue_wr,
+    -- . read only
+    st_reg_frame       => reg_frame,
+    st_reg_status      => reg_status,
+    st_reg_status_wr   => reg_status_wr
+  );
+
+  -- Packet buffer
+  u_mm_ram : ENTITY common_lib.common_ram_crw_crw
+  GENERIC MAP (
+    g_technology => g_technology,
+    g_ram        => c_mm_ram
+  )
+  PORT MAP (
+    rst_a     => mm_rst,
+    clk_a     => mm_clk,
+    wr_en_a   => ram_sla_in.wr,
+    adr_a     => ram_sla_in.address(c_mm_ram.adr_w-1 DOWNTO 0),
+    wr_dat_a  => ram_sla_in.wrdata(c_mm_ram.dat_w-1 DOWNTO 0),
+    rd_en_a   => ram_sla_in.rd,
+    rd_dat_a  => ram_sla_out.rddata(c_mm_ram.dat_w-1 DOWNTO 0),
+    rd_val_a  => ram_sla_out.rdval,
+    rst_b     => st_rst,
+    clk_b     => st_clk,
+    wr_en_b   => mem_in_endian.wr,
+    adr_b     => mem_in_endian.address(c_mm_ram.adr_w-1 DOWNTO 0),
+    wr_dat_b  => mem_in_endian.wrdata(c_mm_ram.dat_w-1 DOWNTO 0),
+    rd_en_b   => mem_in_endian.rd,
+    rd_dat_b  => mem_out_endian.rddata(c_mm_ram.dat_w-1 DOWNTO 0),
+    rd_val_b  => mem_out_endian.rdval
+  );
+  
+  -- The Rx, Tx packet buffer is big-endian
+  mem_in_endian <= func_mem_swap_endianess(mem_in, c_word_sz);
+  mem_out       <= func_mem_swap_endianess(mem_out_endian, c_word_sz);
+  
+  
+  ------------------------------------------------------------------------------
+  -- RX : Adapt the TSE RX source ready latency from 2 to 1
+  ------------------------------------------------------------------------------
+  
+  u_adapt : ENTITY dp_lib.dp_latency_adapter
+  GENERIC MAP (
+    g_in_latency  => c_eth_rx_ready_latency,  -- = 2
+    g_out_latency => c_eth_ready_latency      -- = 1
+  )
+  PORT MAP (
+    rst     => st_rst,
+    clk     => st_clk,
+    -- ST sink
+    snk_out => tse_rx_siso,
+    snk_in  => tse_rx_sosi,
+    -- ST source
+    src_in  => rx_adapt_siso,
+    src_out => rx_adapt_sosi
+  );
+     
+  ------------------------------------------------------------------------------
+  -- RX : Replace the CRC word with the stream error field from the TSE MAC
+  ------------------------------------------------------------------------------
+  
+  u_crc_ctrl : ENTITY eth_lib.eth_crc_ctrl
+  PORT MAP (
+    rst            => st_rst,
+    clk            => st_clk,
+
+    -- Streaming Sink
+    snk_in_err     => rx_adapt_sosi.err(c_tech_tse_error_w-1 DOWNTO 0),  -- preserve error field from TSE MAC stream
+    snk_in         => rx_adapt_sosi,
+    snk_out        => rx_adapt_siso,
+    
+    -- Streaming Source
+    src_in         => rx_crc_siso,
+    src_out        => rx_crc_sosi,        -- replaced CRC word by snk_in_err, so CRC word /=0 indicates any TSE error
+    src_out_err    => OPEN                -- flag snk_in_err/=0 at src_out.eop
+  );
+  
+  ------------------------------------------------------------------------------
+  -- RX : Strip the option words from the IPv4 header
+  ------------------------------------------------------------------------------
+  gen_ihl20: IF g_ihl20 GENERATE
+    u_ihl20 : ENTITY eth_lib.eth_ihl_to_20
+    PORT MAP (
+      rst            => st_rst,
+      clk            => st_clk,
+
+      -- Streaming Sink
+      snk_in         => rx_crc_sosi,
+      snk_out        => rx_crc_siso,
+      
+      -- Streaming Source
+      src_in         => rx_ihl20_siso,
+      src_out        => rx_ihl20_sosi
+    );
+  END GENERATE;
+  
+  no_ihl20: IF not g_ihl20 GENERATE
+    rx_ihl20_sosi <= rx_crc_sosi;
+    rx_crc_siso   <= rx_ihl20_siso;
+  END GENERATE;
+  
+  
+  ------------------------------------------------------------------------------
+  -- RX : For IP verify IP header checksum
+  ------------------------------------------------------------------------------
+  
+  u_rx_frame : ENTITY eth_lib.eth_hdr
+  GENERIC MAP (
+    g_header_store_and_forward     => TRUE,
+    g_ip_header_checksum_calculate => TRUE
+  )
+  PORT MAP (
+    -- Clocks and reset
+    rst             => st_rst,
+    clk             => st_clk,
+    
+    -- Streaming Sink
+    snk_in          => rx_ihl20_sosi,
+    snk_out         => rx_ihl20_siso,
+    
+    -- Streaming Source
+    src_in          => rx_hdr_siso,
+    src_out         => rx_hdr_sosi,
+
+    -- Frame control
+    frm_discard     => rx_eth_discard,
+    frm_discard_val => rx_eth_discard_val,
+    
+    -- Header info
+    hdr_status          => rx_hdr_status,
+    hdr_status_complete => rx_hdr_status_complete
+  );
+
+  rx_eth_discard     <= rx_frm_discard      WHEN g_frm_discard_en=TRUE ELSE '0';
+  rx_eth_discard_val <= rx_frm_discard_val  WHEN g_frm_discard_en=TRUE ELSE '1';
+
+  u_frm_discard : ENTITY eth_lib.eth_frm_discard
+  GENERIC MAP (
+    g_support_dhcp       => TRUE,
+    g_support_udp_onload => FALSE
+  )
+  PORT MAP (
+    -- Clocks and reset
+    rst             => st_rst,
+    clk             => st_clk,
+    
+    -- MM control
+    reg_config      => reg_config,
+    reg_demux       => reg_demux,
+    
+    -- ST info
+    hdr_status          => rx_hdr_status,
+    hdr_status_complete => rx_hdr_status_complete,
+    
+    -- Frame discard decision
+    frm_discard     => rx_frm_discard,
+    frm_discard_val => rx_frm_discard_val
+  );
+  
+  
+  ------------------------------------------------------------------------------
+  -- Demux the UDP off-load traffic and the keep the other ETH traffic
+  ------------------------------------------------------------------------------
+
+  -- Put UDP off-load traffic on channel > 0
+  -- Put other ETH    traffic on channel = 0 for further internal processing
+  u_udp_channel : ENTITY eth_lib.eth_udp_channel
+  PORT MAP (
+    -- Clocks and reset
+    rst            => st_rst,
+    clk            => st_clk,
+    
+    -- Streaming Sink
+    snk_in         => rx_hdr_sosi,
+    snk_out        => rx_hdr_siso,
+    
+    -- Streaming Source with channel field
+    src_in         => rx_channel_siso,
+    src_out        => rx_channel_sosi,
+    
+    -- Demux control
+    reg_demux      => reg_demux,
+    hdr_status     => rx_hdr_status
+  );
+  
+  -- Demultiplex channel 0 for internal handling and the other channels > 0 for external UDP off-load.
+  u_rx_demux : ENTITY dp_lib.dp_demux
+  GENERIC MAP (
+    g_nof_output    => c_demux_nof_ports,
+    g_combined      => c_demux_combined
+  )
+  PORT MAP (
+    rst         => st_rst,
+    clk         => st_clk,
+    -- ST sinks
+    snk_out     => rx_channel_siso,
+    snk_in      => rx_channel_sosi,
+    -- ST source
+    src_in_arr  => demux_siso_arr,
+    src_out_arr => demux_sosi_arr
+  );
+    
+  -- Fixed local ETH port
+  eth_rx_sosi       <= demux_sosi_arr(0);
+  demux_siso_arr(0) <= eth_rx_siso;
+
+  -- UDP offload ports
+  gen_udp_rx_demux : FOR i IN 1 TO c_eth_nof_udp_ports GENERATE
+    udp_rx_src_out_arr(i-1) <= demux_sosi_arr(i);
+    demux_siso_arr(i)       <= udp_rx_src_in_arr(i-1);
+  END GENERATE;
+ 
+  
+  ------------------------------------------------------------------------------
+  -- ETH RX frame buffer
+  ------------------------------------------------------------------------------
+  
+  u_rx_buffer : ENTITY eth_lib.eth_buffer
+  GENERIC MAP (
+    g_technology   => g_technology
+  )
+  PORT MAP (
+    -- Clocks and reset
+    rst             => st_rst,
+    clk             => st_clk,
+    
+    -- Streaming Sink
+    snk_in          => eth_rx_sosi,
+    snk_out         => eth_rx_siso,
+    
+    -- Streaming Source
+    -- . The src_rd, src_ack and src_done act instead of src_in.ready to have src_in ready per frame instead of per data word
+    src_rd          => rx_frame_rd,    -- request frame pulse
+    src_ack         => rx_frame_ack,   -- acknowledge request
+    src_done        => rx_frame_done,  -- signal frame received
+    src_out         => rx_frame_sosi,
+    
+    -- Monitoring
+    flushed_frm_cnt => rx_flushed_frm_cnt
+  );  
+
+    
+  ------------------------------------------------------------------------------
+  -- ETH RX frame monitor
+  ------------------------------------------------------------------------------
+  
+  -- Extract total header
+  u_rx_hdr_info : ENTITY eth_lib.eth_hdr
+  GENERIC MAP (
+    g_header_store_and_forward     => FALSE,
+    g_ip_header_checksum_calculate => FALSE
+  )
+  PORT MAP (
+    -- Clocks and reset
+    rst             => st_rst,
+    clk             => st_clk,
+    
+    -- Streaming Sink
+    snk_in          => rx_frame_sosi,
+    
+    -- Header info
+    hdr_words_arr   => rx_frame_hdr_words_arr,
+    hdr_fields      => rx_frame_hdr_fields,
+    hdr_status      => rx_frame_hdr_status
+  );
+  
+  -- Extract CRC word (enumerate: 0=OK, >0 AND odd = Error)
+  u_rx_crc_word : ENTITY eth_lib.eth_crc_word
+  PORT MAP (
+    rst            => st_rst,
+    clk            => st_clk,
+    
+    -- Streaming Sink
+    snk_in         => rx_frame_sosi,
+    
+    -- CRC word
+    crc_word       => rx_frame_crc_word,
+    crc_word_val   => OPEN
+  );
+  
+  u_mm_reg_frame : ENTITY eth_lib.eth_mm_reg_frame
+  PORT MAP (
+    -- Clocks and reset
+    rst             => st_rst,
+    clk             => st_clk,
+    
+    -- Inputs need for the frame register
+    hdr_fields      => rx_frame_hdr_fields,
+    hdr_status      => rx_frame_hdr_status,
+    erc_word        => rx_frame_crc_word,
+    reg_config      => reg_config,
+    
+    -- Frame register
+    reg_frame       => reg_frame
+  );
+
+    
+  ------------------------------------------------------------------------------
+  -- ETH Control
+  ------------------------------------------------------------------------------
+  
+  u_control : ENTITY eth_lib.eth_control
+  PORT MAP (
+    -- Clocks and reset
+    rst               => st_rst,
+    clk               => st_clk,
+    
+    -- Control register
+    reg_config        => reg_config,
+    reg_control       => reg_control,
+    reg_continue_wr   => reg_continue_wr,
+    reg_status        => reg_status,
+    reg_status_wr     => reg_status_wr,
+        
+    -- Streaming sink Rx frame
+    rcv_rd            => rx_frame_rd,
+    rcv_ack           => rx_frame_ack,
+    rcv_done          => rx_frame_done,
+    rcv_in            => rx_frame_sosi,
+    rcv_hdr_words_arr => rx_frame_hdr_words_arr,
+    rcv_hdr_status    => rx_frame_hdr_status,
+
+    -- Streaming source Tx frame
+    xmt_in            => eth_tx_siso,
+    xmt_out           => eth_tx_sosi,
+    
+    -- MM frame memory    
+    mem_in            => mem_in,
+    mem_out           => mem_out
+  );
+  
+    
+  ------------------------------------------------------------------------------
+  -- TX : Mux UDP
+  ------------------------------------------------------------------------------
+   
+  -- Fixed local ETH
+  eth_tx_siso     <= mux_siso_arr(0);
+  mux_sosi_arr(0) <= eth_tx_sosi;
+
+  -- UDP offload ports
+  gen_udp_tx_mux : FOR i IN 1 TO c_eth_nof_udp_ports GENERATE
+    udp_tx_snk_out_arr(i-1) <= mux_siso_arr(i);
+    mux_sosi_arr(i)         <= udp_tx_snk_in_arr(i-1);
+  END GENERATE;
+  
+  -- Multiplex the two input streams on to the single ETH stream
+  u_tx_mux : ENTITY dp_lib.dp_mux
+  GENERIC MAP (
+    g_technology      => g_technology,
+    g_data_w          => c_eth_data_w,
+    g_empty_w         => c_eth_empty_w,
+    g_in_channel_w    => 1,
+    g_error_w         => 1,
+    g_use_empty       => TRUE,
+    g_use_in_channel  => FALSE,
+    g_use_error       => FALSE,
+    g_nof_input       => c_mux_nof_ports,
+    g_use_fifo        => FALSE,
+    g_fifo_size       => array_init(1024, c_mux_nof_ports),  -- input FIFOs are not used, but generic must match g_nof_input
+    g_fifo_fill       => array_init(   0, c_mux_nof_ports)   -- input FIFOs are not used, but generic must match g_nof_input
+  )
+  PORT MAP (
+    rst         => st_rst,
+    clk         => st_clk,
+    -- ST sinks
+    snk_out_arr => mux_siso_arr,     -- OUT = request to upstream ST source
+    snk_in_arr  => mux_sosi_arr,
+    -- ST source
+    src_in      => tx_mux_siso,  -- IN  = request from downstream ST sink
+    src_out     => tx_mux_sosi
+  );  
+  
+  ------------------------------------------------------------------------------
+  -- TX : For IP insert IP header checksum
+  ------------------------------------------------------------------------------
+  
+  u_tx_frame : ENTITY eth_lib.eth_hdr
+  GENERIC MAP (
+    g_header_store_and_forward     => TRUE,
+    g_ip_header_checksum_calculate => TRUE
+  )
+  PORT MAP (
+    -- Clocks and reset
+    rst             => st_rst,
+    clk             => st_clk,
+    
+    -- Streaming Sink
+    snk_in          => tx_mux_sosi,
+    snk_out         => tx_mux_siso,
+    
+    -- Streaming Source
+    src_in          => tx_hdr_siso,
+    src_out         => tx_hdr_sosi
+  );
+  
+  
+  ------------------------------------------------------------------------------
+  -- TSE MAC
+  ------------------------------------------------------------------------------
+  tx_hdr_siso <= tse_tx_siso;
+  tse_tx_sosi <= func_dp_stream_error_set(tx_hdr_sosi, 0);   -- set err field (value 0 for OK)
+  
+  tse_tx_mac_in.crc_fwd <= '0';  -- when '0' then TSE MAC generates the TX CRC field
+
+  u_tech_tse : ENTITY tech_tse_lib.tech_tse
+  GENERIC MAP (
+    g_technology   => g_technology,
+    g_ETH_PHY      => g_ETH_PHY
+  )
+  PORT MAP (
+    -- Clocks and reset
+    mm_rst         => mm_rst,
+    mm_clk         => mm_clk,
+    eth_clk        => eth_clk,
+    tx_snk_clk     => st_clk,
+    rx_src_clk     => st_clk,
+    cal_rec_clk    => cal_rec_clk,
+    -- Memory Mapped Slave
+    mm_sla_in      => tse_sla_in,
+    mm_sla_out     => tse_sla_out,
+    -- MAC transmit interface
+    -- . ST sink
+    tx_snk_in      => tse_tx_sosi,
+    tx_snk_out     => tse_tx_siso,
+    -- . MAC specific
+    tx_mac_in      => tse_tx_mac_in,
+    tx_mac_out     => tse_tx_mac_out,  -- OPEN
+    -- MAC receive interface
+    -- . ST Source
+    rx_src_in      => tse_rx_siso,
+    rx_src_out     => tse_rx_sosi,
+    -- . MAC specific
+    rx_mac_out     => tse_rx_mac_out,
+    -- PHY interface
+    eth_txp        => eth_txp,
+    eth_rxp        => eth_rxp,
+    -- LED interface
+    tse_led        => tse_led
+  );
+  
+END str;
diff --git a/libraries/io/eth1g/tb/vhdl/tb_eth1g.vhd b/libraries/io/eth1g/tb/vhdl/tb_eth1g.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..8b82839c23f6c180e818b305f8f8738a1c1e712d
--- /dev/null
+++ b/libraries/io/eth1g/tb/vhdl/tb_eth1g.vhd
@@ -0,0 +1,657 @@
+-------------------------------------------------------------------------------
+--
+-- 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/>.
+--
+-------------------------------------------------------------------------------
+
+-- Purpose: Testbench for eth1g
+-- Description:
+--
+--   The p_lcu_transmitter transmits packets and the p_eth_control loops them
+--   back to p_lcu_receiver:
+--
+--                     -------    -------
+--                 /---|     |<---|     |<--- p_lcu_transmitter
+--   p_eth_control |   | DUT |    | LCU |
+--                 \-->|(ETH)|--->|(TSE)|---> p_lcu_receiver
+--                     -------    -------
+--
+--   The tb is self checking based on:
+--   . proc_tech_tse_rx_packet() for expected header and data type
+--   . tx_pkt_cnt=rx_pkt_cnt > 0 must be true at the tb_end.
+-- Usage:
+--   > as 10
+--   > run -all
+
+
+LIBRARY IEEE, common_lib, dp_lib, technology_lib, eth_lib, tech_tse_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 common_lib.tb_common_mem_pkg.ALL;
+USE common_lib.common_network_layers_pkg.ALL;
+USE common_lib.common_network_total_header_pkg.ALL;
+USE dp_lib.dp_stream_pkg.ALL;
+USE technology_lib.technology_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+USE tech_tse_lib.tech_tse_pkg.ALL;
+USE tech_tse_lib.tb_tech_tse_pkg.ALL;
+USE eth_lib.eth_pkg.ALL;
+
+
+ENTITY tb_eth1g IS
+  -- Test bench control parameters
+  GENERIC (
+    g_technology_dut : NATURAL := c_tech_select_default;
+    g_technology_lcu : NATURAL := c_tech_select_default;
+    g_frm_discard_en : BOOLEAN := FALSE;  -- when TRUE discard frame types that would otherwise have to be discarded by the Nios MM master
+    g_flush_test_en  : BOOLEAN := FALSE;  -- when TRUE send many large frames to enforce flush in eth_buffer
+    g_tb_end         : BOOLEAN := TRUE;   -- when TRUE then tb_end ends this simulation, else a higher multi-testbench will end the simulation
+    --   g_data_type = c_tb_tech_tse_data_type_symbols  = 0
+    --   g_data_type = c_tb_tech_tse_data_type_counter  = 1
+    --   g_data_type = c_tb_tech_tse_data_type_arp      = 2
+    --   g_data_type = c_tb_tech_tse_data_type_ping     = 3
+    --   g_data_type = c_tb_tech_tse_data_type_udp      = 4
+    g_data_type : NATURAL := c_tb_tech_tse_data_type_udp
+  );
+  PORT (
+    tb_end : OUT STD_LOGIC
+  );
+END tb_eth1g;
+
+
+ARCHITECTURE tb OF tb_eth1g IS
+
+  CONSTANT sys_clk_period       : TIME := 10 ns;  -- 100 MHz
+  CONSTANT eth_clk_period       : TIME :=  8 ns;  -- 125 MHz
+  CONSTANT cable_delay          : TIME := 12 ns;
+
+  CONSTANT c_cross_clock_domain : BOOLEAN := TRUE;  -- use FALSE when mm_clk and st_clk are the same, else use TRUE to cross the clock domain
+  
+  -- TSE constants
+  CONSTANT c_promis_en          : BOOLEAN := FALSE;
+                                                 
+  CONSTANT c_tx_ready_latency   : NATURAL := c_tech_tse_tx_ready_latency;  -- 0, 1 are supported, must match TSE MAC c_tech_tse_tx_ready_latency
+  CONSTANT c_nof_tx_not_valid   : NATURAL := 0;  -- when > 0 then pull tx valid low for c_nof_tx_not_valid beats during tx
+  
+  -- Payload user data
+  CONSTANT c_tb_nof_data        : NATURAL := 0;  -- nof UDP user data, nof ping padding data
+  CONSTANT c_tb_ip_nof_data     : NATURAL := c_network_udp_header_len + c_tb_nof_data; -- nof IP data,
+                                          -- also suits ICMP, because c_network_icmp_header_len = c_network_udp_header_len
+  CONSTANT c_tb_reply_payload   : BOOLEAN := TRUE;  -- TRUE copy rx payload into response payload, else header only (e.g. for ARP)
+
+  -- Packet headers
+  -- . Ethernet header
+  CONSTANT c_lcu_src_mac        : STD_LOGIC_VECTOR(c_network_eth_mac_slv'RANGE) := X"10FA01020300";
+  CONSTANT c_dut_src_mac        : STD_LOGIC_VECTOR(c_network_eth_mac_slv'RANGE) := X"123456789ABC";  -- = 12-34-56-78-9A-BC
+  CONSTANT c_dut_src_mac_hi     : NATURAL := TO_UINT(c_dut_src_mac(c_network_eth_mac_addr_w-1 DOWNTO c_word_w));
+  CONSTANT c_dut_src_mac_lo     : NATURAL := TO_UINT(c_dut_src_mac(                c_word_w-1 DOWNTO        0));
+  -- support only ARP and IPv4 over ETH
+  --                                                             symbols   counter               ARP=0x806               IP=0x800               IP=0x800
+  CONSTANT c_dut_ethertype      : NATURAL := sel_n(g_data_type, 16#07F0#, 16#07F1#, c_network_eth_type_arp, c_network_eth_type_ip, c_network_eth_type_ip);
+  
+  CONSTANT c_tx_eth_header      : t_network_eth_header := (dst_mac    => c_dut_src_mac,
+                                                           src_mac    => c_lcu_src_mac,
+                                                           eth_type   => TO_UVEC(c_dut_ethertype, c_network_eth_type_w));
+  CONSTANT c_discard_eth_header : t_network_eth_header := (dst_mac    => c_dut_src_mac,
+                                                           src_mac    => c_lcu_src_mac,
+                                                           eth_type   => TO_UVEC(16#07F0#, c_network_eth_type_w));
+  CONSTANT c_exp_eth_header     : t_network_eth_header := (dst_mac    => c_tx_eth_header.src_mac,    -- \/
+                                                           src_mac    => c_tx_eth_header.dst_mac,    -- /\
+                                                           eth_type   => c_tx_eth_header.eth_type);  -- =
+                                                   
+  -- . IP header
+  CONSTANT c_lcu_ip_addr        : NATURAL := 16#05060708#;  -- = 05:06:07:08
+  CONSTANT c_dut_ip_addr        : NATURAL := 16#01020304#;
+  CONSTANT c_tb_ip_total_length : NATURAL := c_network_ip_total_length + c_tb_ip_nof_data;
+  
+  -- support only ping protocol or UDP protocol over IP
+  --                                                          symbols counter  ARP                      ping=1                     UDP=17
+  CONSTANT c_tb_ip_protocol     : NATURAL := sel_n(g_data_type,    13,     14,  15, c_network_ip_protocol_icmp, c_network_ip_protocol_udp);
+  
+  CONSTANT c_tx_ip_header       : t_network_ip_header := (version         => TO_UVEC(c_network_ip_version,         c_network_ip_version_w),
+                                                          header_length   => TO_UVEC(c_network_ip_header_length,   c_network_ip_header_length_w),
+                                                          services        => TO_UVEC(c_network_ip_services,        c_network_ip_services_w),
+                                                          total_length    => TO_UVEC(c_tb_ip_total_length,         c_network_ip_total_length_w),
+                                                          identification  => TO_UVEC(c_network_ip_identification,  c_network_ip_identification_w),
+                                                          flags           => TO_UVEC(c_network_ip_flags,           c_network_ip_flags_w),
+                                                          fragment_offset => TO_UVEC(c_network_ip_fragment_offset, c_network_ip_fragment_offset_w),
+                                                          time_to_live    => TO_UVEC(c_network_ip_time_to_live,    c_network_ip_time_to_live_w),
+                                                          protocol        => TO_UVEC(c_tb_ip_protocol,             c_network_ip_protocol_w),
+                                                          header_checksum => TO_UVEC(c_network_ip_header_checksum, c_network_ip_header_checksum_w),  -- init value (or try 0xEBBD = 60349)
+                                                          src_ip_addr     => TO_UVEC(c_lcu_ip_addr,                c_network_ip_addr_w),
+                                                          dst_ip_addr     => TO_UVEC(c_dut_ip_addr,                c_network_ip_addr_w));
+                                                  
+  CONSTANT c_exp_ip_header      : t_network_ip_header := (version         => c_tx_ip_header.version,          -- =
+                                                          header_length   => c_tx_ip_header.header_length,    -- =
+                                                          services        => c_tx_ip_header.services,         -- =
+                                                          total_length    => c_tx_ip_header.total_length,     -- =
+                                                          identification  => c_tx_ip_header.identification,   -- =
+                                                          flags           => c_tx_ip_header.flags,            -- =
+                                                          fragment_offset => c_tx_ip_header.fragment_offset,  -- =
+                                                          time_to_live    => c_tx_ip_header.time_to_live,     -- =
+                                                          protocol        => c_tx_ip_header.protocol,         -- =
+                                                          header_checksum => c_tx_ip_header.header_checksum,  -- init value
+                                                          src_ip_addr     => c_tx_ip_header.dst_ip_addr,      -- \/
+                                                          dst_ip_addr     => c_tx_ip_header.src_ip_addr);     -- /\
+                                                  
+  -- . ARP packet
+  CONSTANT c_tx_arp_packet      : t_network_arp_packet := (htype => TO_UVEC(c_network_arp_htype,        c_network_arp_htype_w),
+                                                           ptype => TO_UVEC(c_network_arp_ptype,        c_network_arp_ptype_w),
+                                                           hlen  => TO_UVEC(c_network_arp_hlen,         c_network_arp_hlen_w),
+                                                           plen  => TO_UVEC(c_network_arp_plen,         c_network_arp_plen_w),
+                                                           oper  => TO_UVEC(c_network_arp_oper_request, c_network_arp_oper_w),
+                                                           sha   => c_lcu_src_mac,
+                                                           spa   => TO_UVEC(c_lcu_ip_addr,              c_network_ip_addr_w),
+                                                           tha   => c_dut_src_mac,
+                                                           tpa   => TO_UVEC(c_dut_ip_addr,              c_network_ip_addr_w));
+
+  CONSTANT c_exp_arp_packet     : t_network_arp_packet := (htype => c_tx_arp_packet.htype,
+                                                           ptype => c_tx_arp_packet.ptype,
+                                                           hlen  => c_tx_arp_packet.hlen,
+                                                           plen  => c_tx_arp_packet.plen,
+                                                           oper  => TO_UVEC(c_network_arp_oper_reply, c_network_arp_oper_w),  -- reply
+                                                           sha   => c_tx_arp_packet.tha,                      --  \/
+                                                           spa   => c_tx_arp_packet.tpa,                      --  /\  \/
+                                                           tha   => c_tx_arp_packet.sha,                      -- /  \ /\ 
+                                                           tpa   => c_tx_arp_packet.spa);                     --     /  \
+                                                   
+  -- . ICMP header
+  CONSTANT c_tx_icmp_header      : t_network_icmp_header := (msg_type => TO_UVEC(c_network_icmp_msg_type_request, c_network_icmp_msg_type_w),  -- ping request
+                                                             code     => TO_UVEC(c_network_icmp_code,             c_network_icmp_code_w),
+                                                             checksum => TO_UVEC(c_network_icmp_checksum,         c_network_icmp_checksum_w),  -- init value
+                                                             id       => TO_UVEC(c_network_icmp_id,               c_network_icmp_id_w),
+                                                             sequence => TO_UVEC(c_network_icmp_sequence,         c_network_icmp_sequence_w));
+  CONSTANT c_exp_icmp_header     : t_network_icmp_header := (msg_type => TO_UVEC(c_network_icmp_msg_type_reply,   c_network_icmp_msg_type_w),  -- ping reply
+                                                             code     => c_tx_icmp_header.code,
+                                                             checksum => c_tx_icmp_header.checksum,          -- init value
+                                                             id       => c_tx_icmp_header.id,
+                                                             sequence => c_tx_icmp_header.sequence);
+  
+  -- . UDP header
+  CONSTANT c_dut_udp_port_ctrl   : NATURAL := 11;                  -- ETH demux UDP for control
+  CONSTANT c_dut_udp_port_st0    : NATURAL := 57;                  -- ETH demux UDP port 0
+  CONSTANT c_dut_udp_port_st1    : NATURAL := 58;                  -- ETH demux UDP port 1
+  CONSTANT c_dut_udp_port_st2    : NATURAL := 59;                  -- ETH demux UDP port 2
+  CONSTANT c_dut_udp_port_en     : NATURAL := 16#10000#;           -- ETH demux UDP port enable bit 16
+  CONSTANT c_lcu_udp_port        : NATURAL := 10;                  -- UDP port used for src_port
+  CONSTANT c_dut_udp_port_st     : NATURAL := c_dut_udp_port_st0;  -- UDP port used for dst_port
+  CONSTANT c_tb_udp_total_length : NATURAL := c_network_udp_total_length + c_tb_nof_data;
+  CONSTANT c_tx_udp_header       : t_network_udp_header := (src_port     => TO_UVEC(c_lcu_udp_port,         c_network_udp_port_w),
+                                                            dst_port     => TO_UVEC(c_dut_udp_port_ctrl,    c_network_udp_port_w),       -- or use c_dut_udp_port_st#
+                                                            total_length => TO_UVEC(c_tb_udp_total_length,  c_network_udp_total_length_w),
+                                                            checksum     => TO_UVEC(c_network_udp_checksum, c_network_udp_checksum_w));  -- init value
+
+  CONSTANT c_exp_udp_header      : t_network_udp_header := (src_port     => c_tx_udp_header.dst_port,      -- \/
+                                                            dst_port     => c_tx_udp_header.src_port,      -- /\
+                                                            total_length => c_tx_udp_header.total_length,  -- =
+                                                            checksum     => c_tx_udp_header.checksum);     -- init value
+
+  SIGNAL tx_total_header     : t_network_total_header;  -- transmitted packet header
+  SIGNAL discard_total_header: t_network_total_header;  -- transmitted packet header for to be discarded packet
+  SIGNAL exp_total_header    : t_network_total_header;  -- expected received packet header
+  
+  -- ETH control
+  CONSTANT c_dut_control_rx_en   : NATURAL := 2**c_eth_mm_reg_control_bi.rx_en;
+  CONSTANT c_dut_control_tx_en   : NATURAL := 2**c_eth_mm_reg_control_bi.tx_en;
+    
+  -- Clocks and reset
+  SIGNAL eth_clk             : STD_LOGIC := '0';  -- tse reference clock
+  SIGNAL sys_clk             : STD_LOGIC := '0';  -- system clock
+  SIGNAL st_clk              : STD_LOGIC;         -- stream clock
+  SIGNAL st_rst              : STD_LOGIC := '1';  -- reset synchronous with st_clk
+  SIGNAL mm_clk              : STD_LOGIC;         -- memory-mapped bus clock
+  SIGNAL mm_rst              : STD_LOGIC := '1';  -- reset synchronous with mm_clk
+  
+  -- ETH TSE interface
+  SIGNAL dut_tse_init        : STD_LOGIC := '1';
+  SIGNAL dut_eth_init        : STD_LOGIC := '1';
+  SIGNAL eth_tse_miso        : t_mem_miso;
+  SIGNAL eth_tse_mosi        : t_mem_mosi;
+  SIGNAL eth_psc_access      : STD_LOGIC;
+  SIGNAL eth_txp             : STD_LOGIC;
+  SIGNAL eth_rxp             : STD_LOGIC;
+  SIGNAL eth_led             : t_tech_tse_led;
+  
+  -- ETH MM registers interface
+  SIGNAL eth_reg_miso        : t_mem_miso;
+  SIGNAL eth_reg_mosi        : t_mem_mosi;
+  SIGNAL eth_reg_interrupt   : STD_LOGIC;
+  
+  SIGNAL eth_mm_reg_control  : t_eth_mm_reg_control;
+  SIGNAL eth_mm_reg_status   : t_eth_mm_reg_status;
+  
+  SIGNAL eth_ram_miso        : t_mem_miso;
+  SIGNAL eth_ram_mosi        : t_mem_mosi;
+  
+  -- ETH UDP data path interface
+  SIGNAL udp_tx_sosi_arr     : t_dp_sosi_arr(c_eth_nof_udp_ports-1 DOWNTO 0);
+  SIGNAL udp_tx_siso_arr     : t_dp_siso_arr(c_eth_nof_udp_ports-1 DOWNTO 0);
+  SIGNAL udp_rx_siso_arr     : t_dp_siso_arr(c_eth_nof_udp_ports-1 DOWNTO 0);
+  SIGNAL udp_rx_sosi_arr     : t_dp_sosi_arr(c_eth_nof_udp_ports-1 DOWNTO 0);
+  
+  
+  -- LCU TSE interface
+  SIGNAL lcu_init            : STD_LOGIC := '1';
+  SIGNAL lcu_tse_miso        : t_mem_miso;
+  SIGNAL lcu_tse_mosi        : t_mem_mosi;
+  SIGNAL lcu_psc_access      : STD_LOGIC;
+  SIGNAL lcu_tx_en           : STD_LOGIC := '1';
+  SIGNAL lcu_tx_siso         : t_dp_siso;
+  SIGNAL lcu_tx_sosi         : t_dp_sosi;
+  SIGNAL lcu_tx_mac_in       : t_tech_tse_tx_mac;
+  SIGNAL lcu_tx_mac_out      : t_tech_tse_tx_mac;
+  SIGNAL lcu_rx_sosi         : t_dp_sosi;
+  SIGNAL lcu_rx_siso         : t_dp_siso;
+  SIGNAL lcu_rx_mac_out      : t_tech_tse_rx_mac;
+  SIGNAL lcu_txp             : STD_LOGIC;
+  SIGNAL lcu_rxp             : STD_LOGIC;
+  SIGNAL lcu_led             : t_tech_tse_led;
+
+  -- Verification
+  SIGNAL tx_end              : STD_LOGIC := '0';
+  SIGNAL rx_end              : STD_LOGIC := '0';
+  SIGNAL rx_timeout          : NATURAL := 0;
+  SIGNAL tx_pkt_cnt          : NATURAL := 0;
+  SIGNAL rx_pkt_cnt          : NATURAL := 0;
+  SIGNAL rx_pkt_discarded_cnt: NATURAL := 0;
+  SIGNAL rx_pkt_flushed_cnt  : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);  
+  
+BEGIN
+
+  -- run 50 us
+  
+  eth_clk <= NOT eth_clk AFTER eth_clk_period/2;  -- TSE reference clock
+  sys_clk <= NOT sys_clk AFTER sys_clk_period/2;  -- System clock
+
+  mm_clk  <= sys_clk;
+  st_clk  <= sys_clk;
+
+  p_reset : PROCESS
+  BEGIN
+    -- reset release
+    st_rst <= '1';
+    mm_rst <= '1';
+    FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+    mm_rst <= '0';
+    WAIT UNTIL rising_edge(st_clk);
+    st_rst <= '0';
+    FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+    WAIT;
+  END PROCESS;
+  
+  -- Use signal to leave unused fields 'X'
+  tx_total_header.eth  <= c_tx_eth_header;
+  tx_total_header.arp  <= c_tx_arp_packet;
+  tx_total_header.ip   <= c_tx_ip_header;
+  tx_total_header.icmp <= c_tx_icmp_header;
+  tx_total_header.udp  <= c_tx_udp_header;
+  
+  discard_total_header.eth  <= c_discard_eth_header;
+  discard_total_header.arp  <= c_tx_arp_packet;
+  discard_total_header.ip   <= c_tx_ip_header;
+  discard_total_header.icmp <= c_tx_icmp_header;
+  discard_total_header.udp  <= c_tx_udp_header;
+  
+  exp_total_header.eth  <= c_exp_eth_header;
+  exp_total_header.arp  <= c_exp_arp_packet;
+  exp_total_header.ip   <= c_exp_ip_header;
+  exp_total_header.icmp <= c_exp_icmp_header;
+  exp_total_header.udp  <= c_exp_udp_header;
+  
+  ------------------------------------------------------------------------------
+  -- DUT
+  ------------------------------------------------------------------------------
+  p_tse_setup : PROCESS
+  BEGIN
+    dut_tse_init <= '1';
+    eth_tse_mosi.wr <= '0';
+    eth_tse_mosi.rd <= '0';
+    -- Wait for ETH init
+    WHILE dut_eth_init='1' LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+    -- Setup the TSE MAC
+    proc_tech_tse_setup(g_technology_dut,
+                        c_promis_en, c_tech_tse_tx_fifo_depth, c_tech_tse_rx_fifo_depth, c_tx_ready_latency,
+                        c_dut_src_mac, eth_psc_access,
+                        mm_clk, eth_tse_miso, eth_tse_mosi);
+    dut_tse_init <= '0';
+    WAIT;
+  END PROCESS;
+  
+  
+  p_eth_control : PROCESS
+    VARIABLE v_eth_control_word : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
+  BEGIN
+    -- ETH setup
+    dut_eth_init <= '1';
+    eth_reg_mosi.wr <= '0';
+    eth_reg_mosi.rd <= '0';
+    eth_ram_mosi.address <= (OTHERS=>'0');
+    eth_ram_mosi.wr      <= '0';
+    eth_ram_mosi.rd      <= '0';
+    
+    -- Wait for reset release
+    WHILE mm_rst='1' LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+    
+    -- Setup the DEMUX UDP
+    proc_mem_mm_bus_wr(c_eth_reg_demux_wi+0, c_dut_udp_port_en+c_dut_udp_port_st0, mm_clk, eth_reg_miso, eth_reg_mosi);  -- UDP port stream 0
+    proc_mem_mm_bus_wr(c_eth_reg_demux_wi+1, c_dut_udp_port_en+c_dut_udp_port_st1, mm_clk, eth_reg_miso, eth_reg_mosi);  -- UDP port stream 1
+    proc_mem_mm_bus_wr(c_eth_reg_demux_wi+2, c_dut_udp_port_en+c_dut_udp_port_st2, mm_clk, eth_reg_miso, eth_reg_mosi);  -- UDP port stream 2
+    proc_mem_mm_bus_rd(c_eth_reg_demux_wi+0,                                       mm_clk, eth_reg_miso, eth_reg_mosi);
+    proc_mem_mm_bus_rd(c_eth_reg_demux_wi+1,                                       mm_clk, eth_reg_miso, eth_reg_mosi);
+    proc_mem_mm_bus_rd(c_eth_reg_demux_wi+2,                                       mm_clk, eth_reg_miso, eth_reg_mosi);
+    
+    -- Setup the RX config
+    proc_mem_mm_bus_wr(c_eth_reg_config_wi+0, c_dut_src_mac_lo,                    mm_clk, eth_reg_miso, eth_reg_mosi);  -- control MAC address lo word
+    proc_mem_mm_bus_wr(c_eth_reg_config_wi+1, c_dut_src_mac_hi,                    mm_clk, eth_reg_miso, eth_reg_mosi);  -- control MAC address hi halfword
+    proc_mem_mm_bus_wr(c_eth_reg_config_wi+2, c_dut_ip_addr,                       mm_clk, eth_reg_miso, eth_reg_mosi);  -- control IP address
+    proc_mem_mm_bus_wr(c_eth_reg_config_wi+3, c_dut_udp_port_ctrl,                 mm_clk, eth_reg_miso, eth_reg_mosi);  -- control UDP port
+    -- Enable RX
+    proc_mem_mm_bus_wr(c_eth_reg_control_wi+0, c_dut_control_rx_en,                mm_clk, eth_reg_miso, eth_reg_mosi);  -- control rx en
+    dut_eth_init <= '0';
+    
+    -- Wait for TSE init
+    WHILE dut_tse_init='1' LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+    
+    -- Response control
+    WHILE TRUE LOOP
+      eth_mm_reg_status  <= c_eth_mm_reg_status_rst;
+      eth_mm_reg_control <= c_eth_mm_reg_control_rst;
+      -- wait for rx_avail interrupt
+      IF eth_reg_interrupt='1' THEN
+        -- read status register to read the status
+        proc_mem_mm_bus_rd(c_eth_reg_status_wi+0, mm_clk, eth_reg_miso, eth_reg_mosi);  -- read result available in eth_mm_reg_status
+        proc_mem_mm_bus_rd_latency(c_mem_reg_rd_latency, mm_clk);
+        eth_mm_reg_status <= func_eth_mm_reg_status(eth_reg_miso.rddata);
+        WAIT UNTIL rising_edge(mm_clk);
+        -- write status register to acknowledge the interrupt
+        proc_mem_mm_bus_wr(c_eth_reg_status_wi+0, 0, mm_clk, eth_reg_miso, eth_reg_mosi);  -- void value
+        -- prepare control register for response
+        IF c_tb_reply_payload=TRUE THEN
+          eth_mm_reg_control.tx_nof_words <= INCR_UVEC(eth_mm_reg_status.rx_nof_words, -1);  -- -1 to skip the CRC word for the response
+          eth_mm_reg_control.tx_empty     <= eth_mm_reg_status.rx_empty;
+        ELSE
+          eth_mm_reg_control.tx_nof_words <= TO_UVEC(c_network_total_header_32b_nof_words, c_eth_max_frame_nof_words_w);
+          eth_mm_reg_control.tx_empty     <= TO_UVEC(0, c_eth_empty_w);
+        END IF;
+        eth_mm_reg_control.tx_en <= '1';
+        eth_mm_reg_control.rx_en <= '1';
+        WAIT UNTIL rising_edge(mm_clk);
+        -- wait for interrupt removal due to status register read access
+        WHILE eth_reg_interrupt='1' LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+        -- write control register to enable tx
+        IF c_tb_reply_payload=TRUE THEN
+          -- . copy the received payload to the response payload (overwrite part of the default response header in case of raw ETH)
+          FOR I IN func_tech_tse_header_size(g_data_type) TO TO_UINT(eth_mm_reg_control.tx_nof_words)-1 LOOP
+            proc_mem_mm_bus_rd(c_eth_ram_rx_offset+I, mm_clk, eth_ram_miso, eth_ram_mosi);
+            proc_mem_mm_bus_rd_latency(c_mem_ram_rd_latency, mm_clk);
+            proc_mem_mm_bus_wr(c_eth_ram_tx_offset+I, TO_SINT(eth_ram_miso.rddata(c_word_w-1 DOWNTO 0)), mm_clk, eth_ram_miso, eth_ram_mosi);
+          END LOOP;
+        --ELSE
+          -- . only reply header
+        END IF;
+        v_eth_control_word := func_eth_mm_reg_control(eth_mm_reg_control);
+        proc_mem_mm_bus_wr(c_eth_reg_control_wi+0, TO_UINT(v_eth_control_word),  mm_clk, eth_reg_miso, eth_reg_mosi);
+        -- write continue register to make the ETH module continue
+        proc_mem_mm_bus_wr(c_eth_reg_continue_wi, 0, mm_clk, eth_reg_miso, eth_reg_mosi);  -- void value
+      END IF;
+      WAIT UNTIL rising_edge(mm_clk);
+    END LOOP;
+    
+    WAIT;
+  END PROCESS;
+
+  ------------------------------------------------------------------------------
+  -- LCU
+  ------------------------------------------------------------------------------
+  p_lcu_setup : PROCESS
+  BEGIN
+    lcu_init <= '1';
+    lcu_tse_mosi.wr <= '0';
+    lcu_tse_mosi.rd <= '0';
+    -- Wait for reset release
+    WHILE mm_rst='1' LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+    -- Setup the LCU TSE MAC
+    proc_tech_tse_setup(g_technology_lcu,
+                        c_promis_en, c_tech_tse_tx_fifo_depth, c_tech_tse_rx_fifo_depth, c_tx_ready_latency,
+                        c_lcu_src_mac, lcu_psc_access,
+                        mm_clk, lcu_tse_miso, lcu_tse_mosi);
+    -- Wait for DUT init done
+    WHILE dut_tse_init/='0' LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP;
+    lcu_init <= '0';
+    WAIT;
+  END PROCESS;
+  
+  p_lcu_transmitter : PROCESS
+  BEGIN
+    -- . Avalon ST
+    lcu_tx_sosi.data  <= (OTHERS=>'0');
+    lcu_tx_sosi.valid <= '0';
+    lcu_tx_sosi.sop   <= '0';
+    lcu_tx_sosi.eop   <= '0';
+    lcu_tx_sosi.empty <= (OTHERS=>'0');
+    lcu_tx_sosi.err   <= (OTHERS=>'0');
+    -- . MAC specific
+    lcu_tx_mac_in.crc_fwd <= '0';  -- when '0' then TSE MAC generates the TX CRC field
+
+    WHILE lcu_init/='0' LOOP WAIT UNTIL rising_edge(st_clk); END LOOP;
+    FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(st_clk); END LOOP;
+
+    FOR I IN 0 TO 40 LOOP
+      proc_tech_tse_tx_packet(tx_total_header, I, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      --FOR J IN 0 TO 9 LOOP WAIT UNTIL rising_edge(st_clk); END LOOP;
+    END LOOP;
+    
+    IF g_frm_discard_en=TRUE THEN
+      -- Insert a counter data packet that should be discarded
+      proc_tech_tse_tx_packet(discard_total_header, 13, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      rx_pkt_discarded_cnt <= rx_pkt_discarded_cnt + 1;
+      -- Send another packet that should be received
+      proc_tech_tse_tx_packet(tx_total_header,  14, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+    END IF;
+    
+    IF g_flush_test_en=TRUE THEN
+      proc_tech_tse_tx_packet(tx_total_header, 1496, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1497, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1498, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1499, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header,    0, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header,    1, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+      proc_tech_tse_tx_packet(tx_total_header,    2, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+    END IF;
+    
+--     proc_tech_tse_tx_packet(tx_total_header,  104, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header,  105, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header, 1472, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header,  101, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header, 1000, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header,  102, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header, 1000, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header,  103, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header,  104, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+--     proc_tech_tse_tx_packet(tx_total_header,  105, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
+
+    tx_end <= '1';
+    WAIT;
+  END PROCESS;
+
+  
+  p_lcu_receiver : PROCESS
+  BEGIN
+    -- . Avalon ST
+    lcu_rx_siso <= c_dp_siso_hold;
+
+    WHILE lcu_init/='0' LOOP WAIT UNTIL rising_edge(st_clk); END LOOP;
+
+    -- Verification of multiple rx packets is only supported when all packets
+    -- are of the same g_data_type, because the rx process can only support
+    -- one expected result. The proc_tech_tse_rx_packet does not (yet) interpret the
+    -- actually received packet, it relies on the preset expected total_header.
+    
+    -- Receive forever
+    WHILE TRUE LOOP
+      proc_tech_tse_rx_packet(exp_total_header, g_data_type, st_clk, lcu_rx_sosi, lcu_rx_siso);
+    END LOOP;
+
+    WAIT;
+  END PROCESS;
+
+  -- Wire ethernet cable between lcu and dut
+  eth_rxp <= TRANSPORT lcu_txp AFTER cable_delay;
+  lcu_rxp <= TRANSPORT eth_txp AFTER cable_delay;
+  
+  gen_udp_rx_siso_rdy: FOR i IN 0 TO c_eth_nof_udp_ports-1 GENERATE
+    udp_rx_siso_arr(i).ready <= '1';
+  END GENERATE;
+  
+  dut : ENTITY eth_lib.eth
+  GENERIC MAP (
+    g_technology         => g_technology_dut,
+    g_cross_clock_domain => c_cross_clock_domain,
+    g_frm_discard_en     => g_frm_discard_en
+  )
+  PORT MAP (
+    -- Clocks and reset
+    mm_rst            => mm_rst,
+    mm_clk            => mm_clk,
+    eth_clk           => eth_clk,
+    st_rst            => st_rst,
+    st_clk            => st_clk,
+    -- UDP transmit interfaceg_frm_discard_en
+    -- . ST sink
+    udp_tx_snk_in_arr  => udp_tx_sosi_arr,
+    udp_tx_snk_out_arr => udp_tx_siso_arr,
+    -- UDP receive interface
+    -- . ST source
+    udp_rx_src_in_arr  => udp_rx_siso_arr,
+    udp_rx_src_out_arr => udp_rx_sosi_arr,
+    -- Control Memory Mapped Slaves
+    tse_sla_in        => eth_tse_mosi,
+    tse_sla_out       => eth_tse_miso,
+    reg_sla_in        => eth_reg_mosi,
+    reg_sla_out       => eth_reg_miso,
+    reg_sla_interrupt => eth_reg_interrupt,
+    ram_sla_in        => eth_ram_mosi,
+    ram_sla_out       => eth_ram_miso,
+    -- Monitoring
+    rx_flushed_frm_cnt => rx_pkt_flushed_cnt,
+    -- PHY interface
+    eth_txp           => eth_txp,
+    eth_rxp           => eth_rxp,
+    -- LED interface
+    tse_led           => eth_led
+  );
+
+  lcu : ENTITY tech_tse_lib.tech_tse
+  PORT MAP (
+    -- Clocks and reset
+    mm_rst         => mm_rst,
+    mm_clk         => mm_clk,
+    eth_clk        => eth_clk,
+    tx_snk_clk     => st_clk,
+    rx_src_clk     => st_clk,
+    
+    -- Memory Mapped Slave
+    mm_sla_in      => lcu_tse_mosi,
+    mm_sla_out     => lcu_tse_miso,
+    
+    -- MAC transmit interface
+    -- . ST sink
+    tx_snk_in      => lcu_tx_sosi,
+    tx_snk_out     => lcu_tx_siso,
+    -- . MAC specific
+    tx_mac_in      => lcu_tx_mac_in,
+    tx_mac_out     => lcu_tx_mac_out,
+    
+    -- MAC receive interface
+    -- . ST Source
+    rx_src_in      => lcu_rx_siso,
+    rx_src_out     => lcu_rx_sosi,
+    -- . MAC specific
+    rx_mac_out     => lcu_rx_mac_out,
+
+    -- PHY interface
+    eth_txp        => lcu_txp,
+    eth_rxp        => lcu_rxp,
+
+    tse_led        => lcu_led
+  );
+
+  -- Verification
+  tx_pkt_cnt <= tx_pkt_cnt + 1 WHEN lcu_tx_sosi.sop='1' AND rising_edge(st_clk);
+  rx_pkt_cnt <= rx_pkt_cnt + 1 WHEN lcu_rx_sosi.eop='1' AND rising_edge(st_clk);
+
+  p_rx_end : PROCESS
+  BEGIN
+    rx_end <= '0';
+    WAIT UNTIL tx_end='1';
+    
+    -- use timeout since tx_end or last received packet to determine rx_end
+    rx_timeout <= 0;
+    WHILE rx_end='0' LOOP
+      rx_timeout <= rx_timeout + 1;
+      IF lcu_rx_sosi.valid='1' THEN
+        rx_timeout <= 0;
+      ELSIF rx_timeout>5000 THEN  -- sufficiently large value determined by trial
+        rx_end <= '1';
+      END IF;
+      WAIT UNTIL rising_edge(st_clk);
+    END LOOP;
+    
+    --WAIT FOR 10 us;
+    --rx_end <= '1';
+    WAIT;
+  END PROCESS;
+    
+  p_tb_end : PROCESS  
+  BEGIN
+    tb_end <= '0';
+    WAIT UNTIL rx_end='1';
+    
+    -- Verify that all transmitted packets have been received
+    IF tx_pkt_cnt=0 THEN
+      REPORT "No packets were transmitted." SEVERITY ERROR;
+    ELSIF rx_pkt_cnt=0 THEN
+      REPORT "No packets were received." SEVERITY ERROR;
+    ELSIF tx_pkt_cnt/=rx_pkt_cnt + rx_pkt_discarded_cnt + TO_UINT(rx_pkt_flushed_cnt) THEN
+      REPORT "Not all transmitted packets were received." SEVERITY ERROR;
+    END IF;
+    
+    WAIT FOR 1 us;
+    tb_end <= '1';
+    IF g_tb_end=FALSE THEN
+      REPORT "Tb simulation finished." SEVERITY NOTE;
+    ELSE
+      REPORT "Tb simulation finished." SEVERITY FAILURE;
+    END IF;
+    WAIT;
+  END PROCESS;
+  
+END tb;
diff --git a/libraries/io/eth1g/tb/vhdl/tb_tb_eth1g.vhd b/libraries/io/eth1g/tb/vhdl/tb_tb_eth1g.vhd
new file mode 100644
index 0000000000000000000000000000000000000000..1e81e5738c852bd9667f58dd33c0aee7abc4a0ed
--- /dev/null
+++ b/libraries/io/eth1g/tb/vhdl/tb_tb_eth1g.vhd
@@ -0,0 +1,82 @@
+-------------------------------------------------------------------------------
+--
+-- 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/>.
+--
+-------------------------------------------------------------------------------
+
+-- Purpose: Multi-testbench for eth1g
+-- Description:
+--   Verify eth1g for different data types
+-- Usage:
+--   > as 3
+--   > run -all
+
+LIBRARY IEEE, technology_lib, tech_tse_lib;
+USE IEEE.std_logic_1164.ALL;
+USE technology_lib.technology_pkg.ALL;
+USE technology_lib.technology_select_pkg.ALL;
+USE tech_tse_lib.tb_tech_tse_pkg.ALL;
+
+
+ENTITY tb_tb_eth1g IS
+  GENERIC (
+    g_technology_dut : NATURAL := c_tech_select_default
+  );
+END tb_tb_eth1g;
+
+
+ARCHITECTURE tb OF tb_tb_eth1g IS
+
+  CONSTANT c_technology_lcu : NATURAL := c_tech_select_default;
+  
+  CONSTANT c_tb_end_vec : STD_LOGIC_VECTOR(15 DOWNTO 0) := (OTHERS=>'1');
+  SIGNAL   tb_end_vec   : STD_LOGIC_VECTOR(15 DOWNTO 0) := c_tb_end_vec;  -- sufficiently long to fit all tb instances
+  SIGNAL   tb_end       : STD_LOGIC := '0';
+  
+BEGIN
+
+-- g_technology_dut : NATURAL := c_tech_select_default;
+-- g_technology_lcu : NATURAL := c_tech_select_default;
+-- g_frm_discard_en : BOOLEAN := TRUE;   -- when TRUE discard frame types that would otherwise have to be discarded by the Nios MM master
+-- g_flush_test_en  : BOOLEAN := FALSE;  -- when TRUE send many large frames to enforce flush in eth_buffer
+-- g_tb_end         : BOOLEAN := TRUE;   -- when TRUE then tb_end ends this simulation, else a higher multi-testbench will end the simulation
+-- --   g_data_type = c_tb_tech_tse_data_type_symbols  = 0
+-- --   g_data_type = c_tb_tech_tse_data_type_counter  = 1
+-- --   g_data_type = c_tb_tech_tse_data_type_arp      = 2
+-- --   g_data_type = c_tb_tech_tse_data_type_ping     = 3
+-- --   g_data_type = c_tb_tech_tse_data_type_udp      = 4
+-- g_data_type : NATURAL := c_tb_tech_tse_data_type_udp
+  
+  u_use_symbols     : ENTITY work.tb_eth1g GENERIC MAP (g_technology_dut, c_technology_lcu, FALSE, FALSE, FALSE, c_tb_tech_tse_data_type_symbols) PORT MAP (tb_end_vec(0));
+  u_use_counter     : ENTITY work.tb_eth1g GENERIC MAP (g_technology_dut, c_technology_lcu, FALSE, FALSE, FALSE, c_tb_tech_tse_data_type_counter) PORT MAP (tb_end_vec(1));
+  u_use_arp         : ENTITY work.tb_eth1g GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE, FALSE, FALSE, c_tb_tech_tse_data_type_arp    ) PORT MAP (tb_end_vec(2));
+  u_use_ping        : ENTITY work.tb_eth1g GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE, FALSE, FALSE, c_tb_tech_tse_data_type_ping   ) PORT MAP (tb_end_vec(3));
+  u_use_udp         : ENTITY work.tb_eth1g GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE, FALSE, FALSE, c_tb_tech_tse_data_type_udp    ) PORT MAP (tb_end_vec(4));
+  u_use_udp_flush   : ENTITY work.tb_eth1g GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE,  TRUE, FALSE, c_tb_tech_tse_data_type_udp    ) PORT MAP (tb_end_vec(5));
+  
+  tb_end <= '1' WHEN tb_end_vec=c_tb_end_vec ELSE '0';
+  
+  p_tb_end : PROCESS
+  BEGIN
+    WAIT UNTIL tb_end='1';
+    WAIT FOR 1 ns;
+    REPORT "Multi tb simulation finished." SEVERITY FAILURE;
+    WAIT;
+  END PROCESS;
+END tb;