Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
tb_eth.vhd 28.25 KiB
-------------------------------------------------------------------------------
--
-- 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, 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 dp_lib.dp_stream_pkg.ALL;
USE common_lib.eth_layers_pkg.ALL;
USE WORK.eth_pkg.ALL;
USE tech_tse_lib.tech_tse_pkg.ALL;
USE tech_tse_lib.tb_tech_tse_pkg.ALL;


ENTITY tb_eth IS
  -- Test bench control parameters
  GENERIC (
    --   g_data_type = c_tb_tse_data_type_symbols  = 0
    --   g_data_type = c_tb_tse_data_type_counter  = 1
    --   g_data_type = c_tb_tse_data_type_arp      = 2
    --   g_data_type = c_tb_tse_data_type_ping     = 3
    --   g_data_type = c_tb_tse_data_type_udp      = 4
    g_data_type : NATURAL := c_tb_tse_data_type_udp
  );
END tb_eth;


ARCHITECTURE tb OF tb_eth IS

  -- as 10
  -- run 100 us
  
  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_tse_tx_ready_latency;  -- 0, 1 are supported, must match TSE MAC c_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_udp_header_len + c_tb_nof_data; -- nof IP data,
                                          -- also suits ICMP, because c_icmp_header_len = c_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_word_align         : STD_LOGIC_VECTOR(c_eth_word_align_w-1 DOWNTO 0) := TO_UVEC(c_eth_word_align, c_eth_word_align_w);
  CONSTANT c_lcu_src_mac        : STD_LOGIC_VECTOR(c_eth_mac_slv'RANGE) := X"10FA01020300";
  CONSTANT c_dut_src_mac        : STD_LOGIC_VECTOR(c_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_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
  CONSTANT c_dut_ethertype      : NATURAL := sel_a_b(g_data_type-c_tb_tse_data_type_arp, c_eth_type_ip, c_eth_type_arp);
  CONSTANT c_tx_eth_header      : t_eth_header := (word_align => c_word_align,
                                                   dst_mac    => c_dut_src_mac,
                                                   src_mac    => c_lcu_src_mac,
                                                   eth_type   => TO_UVEC(c_dut_ethertype, c_eth_type_w));
  CONSTANT c_exp_eth_header     : t_eth_header := (word_align => c_tx_eth_header.word_align, -- =
                                                   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_ip_total_length + c_tb_ip_nof_data;
                                             -- support only ping protocol or UDP protocol over IP
  CONSTANT c_tb_ip_protocol     : NATURAL := sel_a_b(g_data_type-c_tb_tse_data_type_ping, c_ip_protocol_udp, c_ip_protocol_icmp);
  
  CONSTANT c_tx_ip_header       : t_ip_header := (version         => TO_UVEC(c_ip_version,         c_ip_version_w),
                                                  header_length   => TO_UVEC(c_ip_header_length,   c_ip_header_length_w),
                                                  services        => TO_UVEC(c_ip_services,        c_ip_services_w),
                                                  total_length    => TO_UVEC(c_tb_ip_total_length, c_ip_total_length_w),
                                                  identification  => TO_UVEC(c_ip_identification,  c_ip_identification_w),
                                                  flags           => TO_UVEC(c_ip_flags,           c_ip_flags_w),
                                                  fragment_offset => TO_UVEC(c_ip_fragment_offset, c_ip_fragment_offset_w),
                                                  time_to_live    => TO_UVEC(c_ip_time_to_live,    c_ip_time_to_live_w),
                                                  protocol        => TO_UVEC(c_tb_ip_protocol,     c_ip_protocol_w),
                                                  header_checksum => TO_UVEC(c_ip_header_checksum, c_ip_header_checksum_w),  -- init value (or try 0xEBBD = 60349)
                                                  src_ip_addr     => TO_UVEC(c_lcu_ip_addr,        c_ip_addr_w),
                                                  dst_ip_addr     => TO_UVEC(c_dut_ip_addr,        c_ip_addr_w));
                                                  
  CONSTANT c_exp_ip_header      : t_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_arp_packet := (htype => TO_UVEC(c_arp_htype,        c_arp_htype_w),
                                                   ptype => TO_UVEC(c_arp_ptype,        c_arp_ptype_w),
                                                   hlen  => TO_UVEC(c_arp_hlen,         c_arp_hlen_w),
                                                   plen  => TO_UVEC(c_arp_plen,         c_arp_plen_w),
                                                   oper  => TO_UVEC(c_arp_oper_request, c_arp_oper_w),
                                                   sha   => c_lcu_src_mac,
                                                   spa   => TO_UVEC(c_lcu_ip_addr,      c_ip_addr_w),
                                                   tha   => c_dut_src_mac,
                                                   tpa   => TO_UVEC(c_dut_ip_addr,      c_ip_addr_w));

  CONSTANT c_exp_arp_packet     : t_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_arp_oper_reply, c_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);                     --     /  \
                                                   
  CONSTANT c_arp_htype              : NATURAL := 1;                   -- Hardware type, 1=ethernet
  CONSTANT c_arp_ptype              : NATURAL := c_eth_type_ip;       -- Protocol type, do ARP for IPv4
  CONSTANT c_arp_hlen               : NATURAL := c_eth_mac_addr_len;  -- Hardware length = 6
  CONSTANT c_arp_plen               : NATURAL := c_ip_addr_len;       -- Protocol length = 4
  CONSTANT c_arp_oper               : NATURAL := 1;                   -- Operator, 1=request, (2=reply)
 
  -- . ICMP header
  CONSTANT c_tx_icmp_header      : t_icmp_header := (msg_type => TO_UVEC(c_icmp_msg_type_request, c_icmp_msg_type_w),  -- ping request
                                                     code     => TO_UVEC(c_icmp_code,     c_icmp_code_w),
                                                     checksum => TO_UVEC(c_icmp_checksum, c_icmp_checksum_w),          -- init value
                                                     id       => TO_UVEC(c_icmp_id,       c_icmp_id_w),
                                                     sequence => TO_UVEC(c_icmp_sequence, c_icmp_sequence_w));
  CONSTANT c_exp_icmp_header     : t_icmp_header := (msg_type => TO_UVEC(c_icmp_msg_type_reply, c_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_udp_total_length + c_tb_nof_data;
  CONSTANT c_tx_udp_header       : t_udp_header := (src_port     => TO_UVEC(c_lcu_udp_port,        c_udp_port_w),
                                                    dst_port     => TO_UVEC(c_dut_udp_port_ctrl,   c_udp_port_w),       -- or use c_dut_udp_port_st#
                                                    total_length => TO_UVEC(c_tb_udp_total_length, c_udp_total_length_w),
                                                    checksum     => TO_UVEC(c_udp_checksum,        c_udp_checksum_w));  -- init value

  CONSTANT c_exp_udp_header      : t_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_eth_total_header;  -- transmitted packet header
  SIGNAL exp_total_header    : t_eth_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_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_tse_tx_mac;
  SIGNAL lcu_tx_mac_out      : t_tse_tx_mac;
  SIGNAL lcu_rx_sosi         : t_dp_sosi;
  SIGNAL lcu_rx_siso         : t_dp_siso;
  SIGNAL lcu_rx_mac_out      : t_tse_rx_mac;
  SIGNAL lcu_txp             : STD_LOGIC;
  SIGNAL lcu_rxp             : STD_LOGIC;
  SIGNAL lcu_led             : t_tse_led;

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;
  
  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_tse_setup(c_promis_en, c_tse_tx_fifo_depth, c_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_eth_total_header_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_tb_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_tse_setup(c_promis_en, c_tse_tx_fifo_depth, c_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;

--     proc_tse_tx_packet(tx_total_header,  100, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, lcu_tx_en, lcu_tx_siso, lcu_tx_sosi);
    FOR I IN 0 TO 40 LOOP
      proc_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;
--     proc_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_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_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_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_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_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_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_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_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_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_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);

    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_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_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 work.eth
  GENERIC MAP (
    g_cross_clock_domain => c_cross_clock_domain
  )
  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 interface
    -- . 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,
    -- PHY interface
    eth_txp           => eth_txp,
    eth_rxp           => eth_rxp,
    -- LED interface
    tse_led           => eth_led
  );

  lcu : ENTITY work.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
  );

END tb;