diff --git a/libraries/io/eth/tb/vhdl/tb_eth.vhd b/libraries/io/eth/tb/vhdl/tb_eth.vhd
index 9a8005019d277a3372d5766bd14dc1faeba66dce..22387c22d9d90beb359e0e2513ef85bd8ef065a2 100644
--- a/libraries/io/eth/tb/vhdl/tb_eth.vhd
+++ b/libraries/io/eth/tb/vhdl/tb_eth.vhd
@@ -61,6 +61,8 @@ ENTITY tb_eth IS
   GENERIC (
     g_technology_dut : NATURAL := c_tech_select_default;
     g_technology_lcu : NATURAL := c_tech_select_default;
+    g_sim            : BOOLEAN := TRUE;
+    g_sim_level      : NATURAL := 1;      -- when g_sim = TRUE, then 0 = use IP; 1 = use fast serdes model
     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
@@ -81,7 +83,7 @@ ARCHITECTURE tb OF tb_eth 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 cable_delay          : TIME := sel_a_b(g_sim_level=0, 12 ns, 0 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
   
@@ -256,9 +258,11 @@ ARCHITECTURE tb OF tb_eth IS
   SIGNAL lcu_tx_en           : STD_LOGIC := '1';
   SIGNAL lcu_tx_siso         : t_dp_siso;
   SIGNAL lcu_tx_sosi         : t_dp_sosi;
+  SIGNAL lcu_tx_sosi_data    : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
   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_sosi_data    : STD_LOGIC_VECTOR(c_word_w-1 DOWNTO 0);
   SIGNAL lcu_rx_siso         : t_dp_siso;
   SIGNAL lcu_rx_mac_out      : t_tech_tse_rx_mac;
   SIGNAL lcu_txp             : STD_LOGIC;
@@ -421,6 +425,11 @@ BEGIN
   ------------------------------------------------------------------------------
   -- LCU
   ------------------------------------------------------------------------------
+  
+  -- Debug signal to more easily view sosi.data in Wave Window
+  lcu_tx_sosi_data <= lcu_tx_sosi.data(c_word_w-1 DOWNTO 0);
+  lcu_rx_sosi_data <= lcu_rx_sosi.data(c_word_w-1 DOWNTO 0);
+  
   p_lcu_setup : PROCESS
   BEGIN
     lcu_init <= '1';
@@ -535,7 +544,9 @@ BEGIN
   GENERIC MAP (
     g_technology         => g_technology_dut,
     g_cross_clock_domain => c_cross_clock_domain,
-    g_frm_discard_en     => g_frm_discard_en
+    g_frm_discard_en     => g_frm_discard_en,
+    g_sim                => g_sim,
+    g_sim_level          => g_sim_level
   )
   PORT MAP (
     -- Clocks and reset
@@ -570,6 +581,12 @@ BEGIN
   );
 
   lcu : ENTITY tech_tse_lib.tech_tse
+  GENERIC MAP (
+    g_sim          => g_sim,
+    g_sim_level    => g_sim_level,
+    g_sim_tx       => TRUE,
+    g_sim_rx       => TRUE
+  )
   PORT MAP (
     -- Clocks and reset
     mm_rst         => mm_rst,
@@ -619,6 +636,10 @@ BEGIN
       rx_timeout <= rx_timeout + 1;
       IF lcu_rx_sosi.valid='1' THEN
         rx_timeout <= 0;
+      ELSIF rx_pkt_cnt>0 THEN
+        IF tx_pkt_cnt=rx_pkt_cnt + rx_pkt_discarded_cnt + TO_UINT(rx_pkt_flushed_cnt) THEN
+          rx_end <= '1';  -- do not wait for rx_timeout if all expected packets have been received
+        END IF;
       ELSIF rx_timeout>5000 THEN  -- sufficiently large value determined by trial
         rx_end <= '1';
       END IF;
diff --git a/libraries/io/eth/tb/vhdl/tb_tb_eth.vhd b/libraries/io/eth/tb/vhdl/tb_tb_eth.vhd
index 86527014f8420484d39d6edf38b0fb6007c0b2d8..e3339f2fabef00d36a8b3ae8ee3aa645335f6154 100644
--- a/libraries/io/eth/tb/vhdl/tb_tb_eth.vhd
+++ b/libraries/io/eth/tb/vhdl/tb_tb_eth.vhd
@@ -53,6 +53,8 @@ BEGIN
 
 -- g_technology_dut : NATURAL := c_tech_select_default;
 -- g_technology_lcu : NATURAL := c_tech_select_default;
+-- g_sim            : BOOLEAN := FALSE;
+-- g_sim_level      : NATURAL := 0;      -- when g_sim = TRUE, then 0 = use IP; 1 = use fast serdes model
 -- 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
@@ -63,12 +65,13 @@ BEGIN
 -- --   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_eth 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_eth 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_eth 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_eth 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_eth 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_eth GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE,  TRUE, FALSE, c_tb_tech_tse_data_type_udp    ) PORT MAP (tb_end_vec(5));
+  u_use_symbols     : ENTITY work.tb_eth GENERIC MAP (g_technology_dut, c_technology_lcu, FALSE, 0, FALSE, FALSE, FALSE, c_tb_tech_tse_data_type_symbols) PORT MAP (tb_end_vec(0));
+  u_use_counter     : ENTITY work.tb_eth GENERIC MAP (g_technology_dut, c_technology_lcu, FALSE, 0, FALSE, FALSE, FALSE, c_tb_tech_tse_data_type_counter) PORT MAP (tb_end_vec(1));
+  u_use_arp         : ENTITY work.tb_eth GENERIC MAP (g_technology_dut, c_technology_lcu, FALSE, 0,  TRUE, FALSE, FALSE, c_tb_tech_tse_data_type_arp    ) PORT MAP (tb_end_vec(2));
+  u_use_ping        : ENTITY work.tb_eth GENERIC MAP (g_technology_dut, c_technology_lcu, FALSE, 0,  TRUE, FALSE, FALSE, c_tb_tech_tse_data_type_ping   ) PORT MAP (tb_end_vec(3));
+  u_use_udp_0       : ENTITY work.tb_eth GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE, 0,  TRUE, FALSE, FALSE, c_tb_tech_tse_data_type_udp    ) PORT MAP (tb_end_vec(4));
+  u_use_udp_1       : ENTITY work.tb_eth GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE, 1,  TRUE, FALSE, FALSE, c_tb_tech_tse_data_type_udp    ) PORT MAP (tb_end_vec(5));
+  u_use_udp_flush   : ENTITY work.tb_eth GENERIC MAP (g_technology_dut, c_technology_lcu,  TRUE, 1,  TRUE,  TRUE, FALSE, c_tb_tech_tse_data_type_udp    ) PORT MAP (tb_end_vec(6));
   
   tb_end <= '1' WHEN tb_end_vec=c_tb_end_vec ELSE '0';
   
diff --git a/libraries/technology/tse/sim_tse.vhd b/libraries/technology/tse/sim_tse.vhd
index 9ae20025277303acf57013b3d96e0796e6c639c6..6b6330161751b7bab3de916183559b733f4237de 100644
--- a/libraries/technology/tse/sim_tse.vhd
+++ b/libraries/technology/tse/sim_tse.vhd
@@ -23,9 +23,15 @@
 -- Author:
 -- . Daniel van der Schuur
 -- Purpose:
--- . Drop-in simulation model for tech_tse.vhd.
+-- . Drop-in behavioral simulation model for tech_tse.vhd.
 -- Description:
--- . Basically just a wrapper around sim_tse.
+-- . The simulation model is based on tech_transceiver_lib.sim_transceiver_gx
+--   and is about a factor 4 faster than the IP simulation model.
+-- Remark:
+-- . Default use g_tx_crc=TRUE, to model TSE IP in ETH on UniBoard1, UniBoard2
+-- . Connect eth_txp/eth_rxp directly to host rxp/txp without a TRANSPORT delay,
+--   because the sim_transceiver_gx model requries that both sides of a link
+--   are in phase.
 
 LIBRARY IEEE, common_lib, dp_lib, tech_transceiver_lib;
 USE IEEE.std_logic_1164.ALL;
@@ -37,6 +43,7 @@ USE work.tech_tse_pkg.ALL;
 ENTITY sim_tse IS 
   GENERIC(
     g_tx         : BOOLEAN;
+    g_tx_crc     : BOOLEAN := TRUE;  -- model append CRC by TSE MAC, CRC value = 0
     g_rx         : BOOLEAN
   );      
   PORT(
@@ -83,11 +90,26 @@ ARCHITECTURE str OF sim_tse IS
   SIGNAL tx_snk_rst : STD_LOGIC;
   SIGNAL rx_src_rst : STD_LOGIC;
 
-  SIGNAL sim_transceiver_gx_tx_snk_in_arr  : t_dp_sosi_arr(0 DOWNTO 0);
-  SIGNAL sim_transceiver_gx_tx_snk_out_arr : t_dp_siso_arr(0 DOWNTO 0);
+  SIGNAL tx_fifo_sosi  : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL tx_fifo_siso  : t_dp_siso := c_dp_siso_hold;
+  
+  TYPE t_reg IS RECORD
+    crc_sosi : t_dp_sosi;
+    crc_cnt  : NATURAL RANGE 0 TO 4;
+  END RECORD;
+  
+  SIGNAL crc_siso      : t_dp_siso := c_dp_siso_hold;
+  SIGNAL r             : t_reg;
+  SIGNAL nxt_r         : t_reg;
 
-  SIGNAL sim_transceiver_gx_rx_src_out_arr : t_dp_sosi_arr(0 DOWNTO 0);
-  SIGNAL sim_transceiver_gx_rx_src_in_arr  : t_dp_siso_arr(0 DOWNTO 0);
+  SIGNAL tx_pkt_sosi   : t_dp_sosi := c_dp_sosi_rst;
+  SIGNAL tx_pkt_siso   : t_dp_siso := c_dp_siso_hold;
+  
+  SIGNAL gx_tx_snk_in_arr  : t_dp_sosi_arr(0 DOWNTO 0);
+  SIGNAL gx_tx_snk_out_arr : t_dp_siso_arr(0 DOWNTO 0);
+
+  SIGNAL gx_rx_src_out_arr : t_dp_sosi_arr(0 DOWNTO 0);
+  SIGNAL gx_rx_src_in_arr  : t_dp_siso_arr(0 DOWNTO 0);
 
 BEGIN
 
@@ -123,10 +145,95 @@ BEGIN
     snk_in      => tx_snk_in,
     snk_out     => tx_snk_out,
 
-    src_out     => sim_transceiver_gx_tx_snk_in_arr(0),
-    src_in      => sim_transceiver_gx_tx_snk_out_arr(0)
+    src_out     => tx_fifo_sosi,
+    src_in      => tx_fifo_siso
   );        
 
+  no_tx_crc : IF NOT g_tx_crc GENERATE
+    gx_tx_snk_in_arr(0) <= tx_fifo_sosi;
+    tx_fifo_siso        <= gx_tx_snk_out_arr(0);
+  END GENERATE;
+  
+  gen_tx_crc : IF g_tx_crc GENERATE
+    -----------------------------------------------------------------------------
+    -- Model Tx CRC by appending four zero octets at end of Tx packet
+    -----------------------------------------------------------------------------
+    --
+    -- The p_crc_comb implementation is based on the following timing diagram:
+    --                        _   _   _   _   _   _   _   _   _
+    --   tr_clk             _| |_| |_| |_| |_| |_| |_| |_| |_| 
+    --                      ___________________________________
+    --   tx_fifo_siso.ready 
+    --                      _________
+    --   tx_fifo_sosi.valid          |_________________________
+    --                            ___
+    --   tx_fifo_sosi.eop   _____|   |_________________________
+    --                      _____                 _____________
+    --   crc_siso.ready          |_______________|
+    --                                _______________
+    --   crc_sosi.valid     _________|               |_________  
+    --                                            ___
+    --   crc_sosi.eop       _____________________|   |_________
+    --                      
+    --   crc_cnt             | 0 | 0 | 1 | 2 | 3 | 0 | 0 | 0 | 
+    --                      _________________________
+    --   tx_pkt_sosi.valid                           |_________
+    --                                            ___
+    --   tx_pkt_sosi.eop    _____________________|   |_________
+    --                         
+   
+    tx_fifo_siso.ready <= tx_pkt_siso.ready AND crc_siso.ready;
+    
+    p_tx_pkt_sosi : PROCESS(tx_fifo_sosi, r)
+    BEGIN
+      -- start with tx_fifo_sosi packet
+      tx_pkt_sosi <= tx_fifo_sosi;
+      -- append CRC = 0 at end of tx_fifo_sosi packet
+      IF r.crc_sosi.valid='1' THEN
+        tx_pkt_sosi.data <= TO_DP_DATA(0);
+      END IF;
+      tx_pkt_sosi.valid <= tx_fifo_sosi.valid OR r.crc_sosi.valid;
+      tx_pkt_sosi.eop   <= r.crc_sosi.eop;
+    END PROCESS;
+    
+    p_crc_comb : PROCESS(tx_fifo_sosi, r)
+      VARIABLE v : t_reg;
+    BEGIN
+      crc_siso.ready <= '1';
+      v := r;
+      v.crc_sosi.valid := '0';
+      v.crc_sosi.eop := '0';
+      IF tx_fifo_sosi.eop='1' THEN
+        crc_siso.ready <= '0';
+        v.crc_sosi.valid := '1';
+        v.crc_cnt := 1;
+      END IF;
+      IF r.crc_cnt>0 THEN
+        crc_siso.ready <= '0';
+        v.crc_sosi.valid := '1';
+        v.crc_cnt := r.crc_cnt + 1;
+      END IF;
+      IF r.crc_cnt=3 THEN
+        v.crc_sosi.eop := '1';
+        v.crc_cnt := 0;
+      END IF;
+      nxt_r <= v;
+    END PROCESS;
+  
+    p_crc_reg : PROCESS(tr_rst, tr_clk)
+    BEGIN
+      IF tr_rst='1' THEN
+        r <= (c_dp_sosi_rst, 0);
+      ELSIF rising_edge(tr_clk) THEN
+        r <= nxt_r;    
+      END IF;
+    END PROCESS;
+    
+    gx_tx_snk_in_arr(0) <= tx_pkt_sosi;
+    tx_pkt_siso         <= gx_tx_snk_out_arr(0);
+    
+  END GENERATE;
+  
   -------------------------------------------------------------------------------
   -- Transceiver sim model
   -- . Inside this model, tr_clk = tx_clk = rx_clk. We're using its output 
@@ -148,13 +255,13 @@ BEGIN
     tx_clk(0)       => tr_clk,
     tx_rst(0)       => tr_rst,
 
-    tx_sosi_arr     => sim_transceiver_gx_tx_snk_in_arr,
-    tx_siso_arr     => sim_transceiver_gx_tx_snk_out_arr,
+    tx_sosi_arr     => gx_tx_snk_in_arr,
+    tx_siso_arr     => gx_tx_snk_out_arr,
     tx_dataout(0)   => eth_txp,
 
     rx_datain(0)    => eth_rxp,
-    rx_sosi_arr     => sim_transceiver_gx_rx_src_out_arr,
-    rx_siso_arr     => sim_transceiver_gx_rx_src_in_arr
+    rx_sosi_arr     => gx_rx_src_out_arr,
+    rx_siso_arr     => gx_rx_src_in_arr
   );  
 
   -------------------------------------------------------------------------------
@@ -186,8 +293,8 @@ BEGIN
     rd_rst      => rx_src_rst,
     rd_clk      => rx_src_clk,
 
-    snk_in      => sim_transceiver_gx_rx_src_out_arr(0),
-    snk_out     => sim_transceiver_gx_rx_src_in_arr(0),
+    snk_in      => gx_rx_src_out_arr(0),
+    snk_out     => gx_rx_src_in_arr(0),
 
     src_out     => rx_src_out,
     src_in      => rx_src_in
diff --git a/libraries/technology/tse/tb_tech_tse.vhd b/libraries/technology/tse/tb_tech_tse.vhd
index 70e680667963218ca7ba41abaed4edbf96dae500..ce074ce5673c8b798bdc26a7f934adb1e5cebddd 100644
--- a/libraries/technology/tse/tb_tech_tse.vhd
+++ b/libraries/technology/tse/tb_tech_tse.vhd
@@ -48,7 +48,13 @@ ENTITY tb_tech_tse IS
     g_technology : NATURAL := c_tech_select_default;
     --   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  : NATURAL := c_tb_tech_tse_data_type_symbols
+    g_data_type  : NATURAL := c_tb_tech_tse_data_type_symbols;
+    g_sim        : BOOLEAN := TRUE;
+    g_sim_level  : NATURAL := 1;    -- 0 = use IP; 1 = use fast serdes model;
+    g_tb_end     : BOOLEAN := TRUE  -- when TRUE then tb_end ends this simulation, else a higher multi-testbench will end the simulation
+  );
+  PORT (
+    tb_end : OUT STD_LOGIC
   );
 END tb_tech_tse;
 
@@ -60,7 +66,7 @@ ARCHITECTURE tb OF tb_tech_tse 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 cable_delay          : TIME := sel_a_b(g_sim_level=0, 12 ns, 0 ns);
 
   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
@@ -84,7 +90,6 @@ ARCHITECTURE tb OF tb_tech_tse IS
   
   -- Clocks and reset
   SIGNAL rx_end            : STD_LOGIC := '0';
-  SIGNAL tb_end            : STD_LOGIC := '0';
   SIGNAL eth_clk           : STD_LOGIC := '0';  -- tse reference clock
   SIGNAL sys_clk           : STD_LOGIC := '0';  -- system clock
   SIGNAL st_clk            : STD_LOGIC;         -- stream clock
@@ -214,7 +219,9 @@ BEGIN
   dut : ENTITY work.tech_tse
   GENERIC MAP (
     g_technology => g_technology,
-    g_ETH_PHY    => "LVDS" -- "LVDS" (default): uses LVDS IOs for ctrl_unb_common, "XCVR": uses tranceiver PHY
+    g_ETH_PHY    => "LVDS",  -- "LVDS" (default): uses LVDS IOs for ctrl_unb_common, "XCVR": uses tranceiver PHY
+    g_sim        => g_sim,
+    g_sim_level  => g_sim_level     -- 0 = use IP; 1 = use fast serdes model;
   )
   PORT MAP (
     -- Clocks and reset
@@ -259,6 +266,7 @@ BEGIN
   
   p_verify : PROCESS
   BEGIN
+    tb_end <= '0';
     WAIT UNTIL rx_end='1';
     -- Verify that all transmitted packets have been received
     IF tx_pkt_cnt=0 THEN
@@ -269,14 +277,13 @@ BEGIN
       REPORT "Not all transmitted packets were received." SEVERITY ERROR;
     END IF;
     tb_end <= '1';
-    WAIT;
-  END PROCESS;
-  
-  p_tb_end : PROCESS  
-  BEGIN
-    WAIT UNTIL tb_end='1';
+    
     WAIT FOR 1 ns;
-    REPORT "Simulation finished." SEVERITY FAILURE;
+    IF g_tb_end=FALSE THEN
+      REPORT "Tb simulation finished." SEVERITY NOTE;
+    ELSE
+      REPORT "Tb simulation finished." SEVERITY FAILURE;
+    END IF;
     WAIT;
   END PROCESS;
   
diff --git a/libraries/technology/tse/tech_tse.vhd b/libraries/technology/tse/tech_tse.vhd
index 2afb13310bedc7f45b639377ae86dce758164165..8f9ef2a1969d0ad8969015e3149699b40944e757 100644
--- a/libraries/technology/tse/tech_tse.vhd
+++ b/libraries/technology/tse/tech_tse.vhd
@@ -34,7 +34,7 @@ ENTITY tech_tse IS
     g_technology   : NATURAL := c_tech_select_default;
     g_ETH_PHY      : STRING  := "LVDS"; -- "LVDS" (default): uses LVDS IOs for ctrl_unb_common, "XCVR": uses tranceiver PHY
     g_sim          : BOOLEAN := FALSE;
-    g_sim_level    : NATURAL := 0;     -- 0 = use IP; 1 = use fast serdes model;
+    g_sim_level    : NATURAL := 0;     -- 0 = use IP model (equivalent to g_sim = FALSE); 1 = use fast serdes model;
     g_sim_tx       : BOOLEAN := TRUE;
     g_sim_rx       : BOOLEAN := TRUE
   );