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;