diff --git a/libraries/technology/tse/hdllib.cfg b/libraries/technology/tse/hdllib.cfg index fce238d38f245af8e05a0fe07a79077a1b67a2fd..4f94c143b87dc34c5d00cc81a0d0f4f985b040d9 100644 --- a/libraries/technology/tse/hdllib.cfg +++ b/libraries/technology/tse/hdllib.cfg @@ -12,3 +12,5 @@ synth_files = tech_tse.vhd test_bench_files = + tb_tech_tse_pkg.vhd + tb_tech_tse.vhd diff --git a/libraries/technology/tse/tb_tech_tse.vhd b/libraries/technology/tse/tb_tech_tse.vhd new file mode 100644 index 0000000000000000000000000000000000000000..05be5548b2ae4e99c42fba3738c6d76fedbae80b --- /dev/null +++ b/libraries/technology/tse/tb_tech_tse.vhd @@ -0,0 +1,246 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2009 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE WORK.tse_pkg.ALL; +USE WORK.tb_tse_pkg.ALL; + + +ENTITY tb_tse 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 : NATURAL := c_tb_tse_data_type_symbols + ); +END tb_tse; + + +ARCHITECTURE tb OF tb_tse IS + + -- as 10 + -- run 50 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_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 + + 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_dst_mac : STD_LOGIC_VECTOR(c_eth_mac_slv'RANGE) := X"10FA01020300"; + CONSTANT c_src_mac : STD_LOGIC_VECTOR(c_eth_mac_slv'RANGE) := X"123456789ABC"; -- = 12-34-56-78-9A-BC + CONSTANT c_ethertype : STD_LOGIC_VECTOR(c_eth_type_slv'RANGE) := X"10FA"; + CONSTANT c_etherlen : STD_LOGIC_VECTOR(c_eth_type_slv'RANGE) := "0000000000010000"; + + -- Packet headers + CONSTANT c_eth_header_loopback : t_eth_header := (c_word_align, c_src_mac, c_src_mac, c_ethertype); + CONSTANT c_eth_header_etherlen : t_eth_header := (c_word_align, c_src_mac, c_src_mac, c_etherlen); + + SIGNAL total_header_loopback : t_eth_total_header; + SIGNAL total_header_etherlen : t_eth_total_header; + + -- 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 mm_clk : STD_LOGIC; -- memory-mapped bus clock + SIGNAL mm_rst : STD_LOGIC; -- reset synchronous with mm_clk + + -- TSE MAC control interface + SIGNAL mm_init : STD_LOGIC := '1'; + SIGNAL mm_miso : t_mem_miso; + SIGNAL mm_mosi : t_mem_mosi; + + SIGNAL mm_psc_access : STD_LOGIC; + + -- TSE MAC transmit interface + -- . The tb is the ST source + SIGNAL tx_en : STD_LOGIC := '1'; + SIGNAL tx_siso : t_dp_siso; + SIGNAL tx_sosi : t_dp_sosi; + -- . MAC specific + SIGNAL tx_mac_in : t_tse_tx_mac; + SIGNAL tx_mac_out : t_tse_tx_mac; + + -- TSE MAC receive interface + -- . The tb is the ST sink + SIGNAL rx_sosi : t_dp_sosi; + SIGNAL rx_siso : t_dp_siso; + -- . MAC specific + SIGNAL rx_mac_out : t_tse_rx_mac; + + -- TSE PHY interface + SIGNAL eth_txp : STD_LOGIC; + SIGNAL eth_rxp : STD_LOGIC; + + SIGNAL tse_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; + + -- Use signal to leave unused fields 'X' + total_header_loopback.eth <= c_eth_header_loopback; + total_header_etherlen.eth <= c_eth_header_etherlen; + + p_mm_setup : PROCESS + BEGIN + mm_init <= '1'; + mm_mosi.wr <= '0'; + mm_mosi.rd <= '0'; + + -- reset release + mm_rst <= '1'; + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP; + mm_rst <= '0'; + FOR I IN 0 TO 9 LOOP WAIT UNTIL rising_edge(mm_clk); END LOOP; + + proc_tse_setup(c_promis_en, c_tse_tx_fifo_depth, c_tse_rx_fifo_depth, c_tx_ready_latency, + c_src_mac, mm_psc_access, + mm_clk, mm_miso, mm_mosi); + mm_init <= '0'; + WAIT; + END PROCESS; + + + p_ff_transmitter : PROCESS + BEGIN + -- . Avalon ST + tx_sosi.data <= (OTHERS=>'0'); + tx_sosi.valid <= '0'; + tx_sosi.sop <= '0'; + tx_sosi.eop <= '0'; + tx_sosi.empty <= (OTHERS=>'0'); + tx_sosi.err <= (OTHERS=>'0'); + -- . MAC specific + tx_mac_in.crc_fwd <= '0'; -- when '0' then TSE MAC generates the TX CRC field + + WHILE mm_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; + + -- Loopback txp->rxp so DST_MAC = c_src_mac to send to itself + + -- TX frame: + -- . I=0 is empty payload, so only 4 words of the ETH header with 46 padding zeros, so empty = 2 + -- . For I=1 to 46 the payload length remains 46 with padding zeros, so empty = 2 + -- . For I>46 the payload length is I and empty = 4 - (I mod 4) + +-- proc_tse_tx_packet(total_header_etherlen, 16, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 16, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 16, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); + FOR I IN 0 TO 59 LOOP + proc_tse_tx_packet(total_header_loopback, I, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); + END LOOP; +-- proc_tse_tx_packet(total_header_loopback, 100, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 101, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 102, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 103, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1499, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); -- verify st empty +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1500, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); +-- proc_tse_tx_packet(total_header_loopback, 1501, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); -- verify c_eth_payload_max +-- proc_tse_tx_packet(total_header_loopback, 100, g_data_type, c_tx_ready_latency, c_nof_tx_not_valid, st_clk, tx_en, tx_siso, tx_sosi); + + WAIT; + END PROCESS; + + + p_ff_receiver : PROCESS + BEGIN + -- . Avalon ST + rx_siso.ready <= '0'; + + WHILE mm_init/='0' LOOP + WAIT UNTIL rising_edge(st_clk); + END LOOP; + + -- Receive forever + WHILE TRUE LOOP + proc_tse_rx_packet(total_header_loopback, g_data_type, st_clk, rx_sosi, rx_siso); + END LOOP; + + WAIT; + END PROCESS; + + + dut : ENTITY work.tse -- uses stratix4 architecture tse_sgmii_lvds + 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 => mm_mosi, + mm_sla_out => mm_miso, + + -- MAC transmit interface + -- . ST sink + tx_snk_in => tx_sosi, + tx_snk_out => tx_siso, + -- . MAC specific + tx_mac_in => tx_mac_in, + tx_mac_out => tx_mac_out, + + -- MAC receive interface + -- . ST Source + rx_src_in => rx_siso, + rx_src_out => rx_sosi, + -- . MAC specific + rx_mac_out => rx_mac_out, + + -- PHY interface + eth_txp => eth_txp, + eth_rxp => eth_rxp, + + tse_led => tse_led + ); + + -- Loopback + eth_rxp <= TRANSPORT eth_txp AFTER cable_delay; + +END tb; diff --git a/libraries/technology/tse/tb_tech_tse_pkg.vhd b/libraries/technology/tse/tb_tech_tse_pkg.vhd new file mode 100644 index 0000000000000000000000000000000000000000..e7f2133cb884e45b18f405194a302a5aa5bfdcec --- /dev/null +++ b/libraries/technology/tse/tb_tech_tse_pkg.vhd @@ -0,0 +1,439 @@ +------------------------------------------------------------------------------- +-- +-- Copyright (C) 2010 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- JIVE (Joint Institute for VLBI in Europe) <http://www.jive.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU General Public License as published by +-- the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU General Public License for more details. +-- +-- You should have received a copy of the GNU General Public License +-- along with this program. If not, see <http://www.gnu.org/licenses/>. +-- +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_mem_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE dp_lib.tb_dp_pkg.ALL; +USE common_lib.eth_layers_pkg.ALL; +USE WORK.tse_pkg.ALL; + + +PACKAGE tb_tse_pkg IS + + -- Test bench supported packet data types + CONSTANT c_tb_tse_data_type_symbols : NATURAL := 0; + CONSTANT c_tb_tse_data_type_counter : NATURAL := 1; + CONSTANT c_tb_tse_data_type_arp : NATURAL := 2; + CONSTANT c_tb_tse_data_type_ping : NATURAL := 3; -- over IP/ICMP + CONSTANT c_tb_tse_data_type_udp : NATURAL := 4; -- over IP + + FUNCTION func_tb_tse_header_size(data_type : NATURAL) RETURN NATURAL; -- raw ethernet: 4 header words, protocol ethernet: 11 header words + + -- Configure the TSE MAC + PROCEDURE proc_tse_setup(CONSTANT c_promis_en : IN BOOLEAN; + CONSTANT c_tse_tx_fifo_depth : IN NATURAL; + CONSTANT c_tse_rx_fifo_depth : IN NATURAL; + CONSTANT c_tx_ready_latency : IN NATURAL; + CONSTANT src_mac : IN STD_LOGIC_VECTOR(c_eth_mac_slv'RANGE); + SIGNAL psc_access : OUT STD_LOGIC; + SIGNAL mm_clk : IN STD_LOGIC; + SIGNAL mm_miso : IN t_mem_miso; + SIGNAL mm_mosi : OUT t_mem_mosi); + + PROCEDURE proc_tse_tx_packet(CONSTANT total_header : IN t_eth_total_header; + CONSTANT data_len : IN NATURAL; -- in symbols = octets = bytes + CONSTANT c_data_type : IN NATURAL; -- c_tb_tse_data_type_* + CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_nof_not_valid : IN NATURAL; -- when > 0 then pull tx valid low for c_nof_not_valid beats during tx + SIGNAL ff_clk : IN STD_LOGIC; + SIGNAL ff_en : IN STD_LOGIC; -- similar purpose as c_nof_not_valid, but not used so pass on signal '1' + SIGNAL ff_src_in : IN t_dp_siso; + SIGNAL ff_src_out : OUT t_dp_sosi); + + -- Receive and verify packet from the TSE MAC + PROCEDURE proc_tse_rx_packet(CONSTANT total_header : IN t_eth_total_header; + CONSTANT c_data_type : IN NATURAL; -- c_tb_tse_data_type_* + SIGNAL ff_clk : IN STD_LOGIC; + SIGNAL ff_snk_in : IN t_dp_sosi; + SIGNAL ff_snk_out : OUT t_dp_siso); + +END tb_tse_pkg; + + +PACKAGE BODY tb_tse_pkg IS + + -- LOCAL ITEMS --------------------------------------------------------------- + CONSTANT c_nof_eth_beats : NATURAL := c_eth_nof_words; -- nof words in eth part of the header + CONSTANT c_nof_hdr_beats : NATURAL := c_eth_total_header_nof_words; -- nof words in the total header + + + -- GLOBAL ITEMS -------------------------------------------------------------- + + FUNCTION func_tb_tse_header_size(data_type : NATURAL) RETURN NATURAL IS + BEGIN + CASE data_type IS + WHEN c_tb_tse_data_type_symbols => RETURN c_eth_nof_words; + WHEN c_tb_tse_data_type_counter => RETURN c_eth_nof_words; + WHEN OTHERS => NULL; + END CASE; + RETURN c_eth_total_header_nof_words; + END func_tb_tse_header_size; + + ------------------------------------------------------------------------------ + -- MM bus + ------------------------------------------------------------------------------ + + -- Use default word addressing for MAC registers according to table 4.8, 4.9 + -- Use halfword addressing for PCS register to match table 4.17 + FUNCTION func_map_pcs_addr(pcs_addr : NATURAL) RETURN NATURAL IS + BEGIN + RETURN pcs_addr * 2 + c_tse_byte_addr_pcs_offset; + END func_map_pcs_addr; + + + ------------------------------------------------------------------------------ + -- Stream + ------------------------------------------------------------------------------ + + -- Configure the TSE MAC + -- . The src_mac[47:0] = 0x123456789ABC for MAC address 12-34-56-78-9A-BC + PROCEDURE proc_tse_setup(CONSTANT c_promis_en : IN BOOLEAN; + CONSTANT c_tse_tx_fifo_depth : IN NATURAL; + CONSTANT c_tse_rx_fifo_depth : IN NATURAL; + CONSTANT c_tx_ready_latency : IN NATURAL; + CONSTANT src_mac : IN STD_LOGIC_VECTOR(c_eth_mac_slv'RANGE); + SIGNAL psc_access : OUT STD_LOGIC; + SIGNAL mm_clk : IN STD_LOGIC; + SIGNAL mm_miso : IN t_mem_miso; + SIGNAL mm_mosi : OUT t_mem_mosi) IS + CONSTANT c_mac0 : INTEGER := TO_SINT(hton(src_mac(47 DOWNTO 16), 4)); + CONSTANT c_mac1 : INTEGER := TO_SINT(hton(src_mac(15 DOWNTO 0), 2)); + BEGIN + -- PSC control + psc_access <= '1'; + proc_mem_mm_bus_rd(func_map_pcs_addr(16#22#), mm_clk, mm_miso, mm_mosi); -- REV --> 0x0901 + proc_mem_mm_bus_wr(func_map_pcs_addr(16#28#), 16#0008#, mm_clk, mm_miso, mm_mosi); -- IF_MODE <-- Force 1GbE, no autonegatiation + proc_mem_mm_bus_rd(func_map_pcs_addr(16#00#), mm_clk, mm_miso, mm_mosi); -- CONTROL --> 0x1140 + proc_mem_mm_bus_rd(func_map_pcs_addr(16#02#), mm_clk, mm_miso, mm_mosi); -- STATUS --> 0x000D + proc_mem_mm_bus_wr(func_map_pcs_addr(16#00#), 16#0140#, mm_clk, mm_miso, mm_mosi); -- CONTROL <-- Auto negotiate disable + psc_access <= '0'; + + -- MAC control + proc_mem_mm_bus_rd(16#000#, mm_clk, mm_miso, mm_mosi); -- REV --> CUST_VERSION & 0x0901 + IF c_promis_en=FALSE THEN + proc_mem_mm_bus_wr(16#008#, 16#0100004B#, mm_clk, mm_miso, mm_mosi); + ELSE + proc_mem_mm_bus_wr(16#008#, 16#0100005B#, mm_clk, mm_miso, mm_mosi); + END IF; + -- COMMAND_CONFIG <-- + -- Only the bits relevant to UniBoard are explained here, others are 0 + -- [ 0] = TX_ENA = 1, enable tx datapath + -- [ 1] = RX_ENA = 1, enable rx datapath + -- [ 2] = XON_GEN = 0 + -- [ 3] = ETH_SPEED = 1, enable 1GbE operation + -- [ 4] = PROMIS_EN = 0, when 1 then receive all frames + -- [ 5] = PAD_EN = 0, when 1 enable receive padding removal (requires ethertype=payload length) + -- [ 6] = CRC_FWD = 1, enable receive CRC forward + -- [ 7] = PAUSE_FWD = 0 + -- [ 8] = PAUSE_IGNORE = 0 + -- [ 9] = TX_ADDR_INS = 0, when 1 then MAX overwrites tx SRC MAC with mac_0,1 or one of the supplemental mac + -- [ 10] = HD_ENA = 0 + -- [ 11] = EXCESS_COL = 0 + -- [ 12] = LATE_COL = 0 + -- [ 13] = SW_RESET = 0, when 1 MAC disables tx and rx, clear statistics and flushes receive FIFO + -- [ 14] = MHAS_SEL = 0, select multicast address resolutions hash-code mode + -- [ 15] = LOOP_ENA = 0 + -- [18-16] = TX_ADDR_SEL[2:0] = 000, TX_ADDR_INS insert mac_0,1 or one of the supplemental mac + -- [ 19] = MAGIC_EN = 0 + -- [ 20] = SLEEP = 0 + -- [ 21] = WAKEUP = 0 + -- [ 22] = XOFF_GEN = 0 + -- [ 23] = CNT_FRM_ENA = 0 + -- [ 24] = NO_LGTH_CHECK = 1, when 0 then check payload length of received frames (requires ethertype=payload length) + -- [ 25] = ENA_10 = 0 + -- [ 26] = RX_ERR_DISC = 0, when 1 then discard erroneous frames (requires store and forward mode, so rx_section_full=0) + -- when 0 then pass on with rx_err[0]=1 + -- [ 27] = DISABLE_RD_TIMEOUT = 0 + -- [30-28] = RSVD = 000 + -- [ 31] = CNT_RESET = 0, when 1 clear statistics + proc_mem_mm_bus_wr(16#00C#, c_mac0, mm_clk, mm_miso, mm_mosi); -- MAC_0 + proc_mem_mm_bus_wr(16#010#, c_mac1, mm_clk, mm_miso, mm_mosi); -- MAC_1 <-- SRC_MAC = 12-34-56-78-9A-BC + proc_mem_mm_bus_wr(16#05C#, 16#0000000C#, mm_clk, mm_miso, mm_mosi); -- TX_IPG_LENGTH <-- interpacket gap = 12 + --proc_mem_mm_bus_wr(16#014#, 16#000005EE#, mm_clk, mm_miso, mm_mosi); -- FRM_LENGTH <-- receive max frame length = 1518 + proc_mem_mm_bus_wr(16#014#, 16#0000233A#, mm_clk, mm_miso, mm_mosi); -- FRM_LENGTH <-- receive max frame length = 9018 + + -- FIFO legenda: + -- . Tx section full = There is enough data in the FIFO to start reading it, when 0 then store and forward. + -- . Rx section full = There is enough data in the FIFO to start reading it, when 0 then store and forward. + -- . Tx section empty = There is not much empty space anymore in the FIFO, warn user via ff_tx_septy + -- . Rx section empty = There is not much empty space anymore in the FIFO, inform remote device via XOFF flow control + -- . Tx almost full = Assert ff_tx_a_full and deassert ff_tx_rdy. Furthermore TX_ALMOST_FULL = c_tx_ready_latency+3, + -- so choose 3 for zero tx ready latency + -- . Rx almost full = Assert ff_rx_a_full and if the user is not ready ff_rx_rdy then: + -- --> break off the reception with an error to avoid FIFO overflow + -- . Tx almost empty = Assert ff_tx_a_empty and if the FIFO does not contain a eop yet then: + -- --> break off the transmission with an error to avoid FIFO underflow + -- . Rx almost empty = Assert ff_rx_a_empty + -- Typical FIFO values: + -- . TX_SECTION_FULL = 16 > 8 = TX_ALMOST_EMPTY + -- . RX_SECTION_FULL = 16 > 8 = RX_ALMOST_EMPTY + -- . TX_SECTION_EMPTY = D-16 < D-3 = Tx FIFO depth - TX_ALMOST_FULL + -- . RX_SECTION_EMPTY = D-16 < D-8 = Rx FIFO depth - RX_ALMOST_FULL + -- . c_tse_tx_fifo_depth = 1 M9K = 256*32b = 1k * 8b is sufficient when the Tx user respects ff_tx_rdy, to store a complete + -- ETH packet would require 1518 byte, so 2 M9K = 2k * 8b + -- . c_tse_rx_fifo_depth = 1 M9K = 256*32b = 1k * 8b is sufficient when the Rx user ff_rx_rdy is sufficiently active + proc_mem_mm_bus_wr(16#01C#, c_tse_rx_fifo_depth-16, mm_clk, mm_miso, mm_mosi); -- RX_SECTION_EMPTY <-- default FIFO depth - 16, >3 + proc_mem_mm_bus_wr(16#020#, 16, mm_clk, mm_miso, mm_mosi); -- RX_SECTION_FULL <-- default 16 + proc_mem_mm_bus_wr(16#024#, c_tse_tx_fifo_depth-16, mm_clk, mm_miso, mm_mosi); -- TX_SECTION_EMPTY <-- default FIFO depth - 16, >3 + proc_mem_mm_bus_wr(16#028#, 16, mm_clk, mm_miso, mm_mosi); -- TX_SECTION_FULL <-- default 16, >~ 8 otherwise no tx + proc_mem_mm_bus_wr(16#02C#, 8, mm_clk, mm_miso, mm_mosi); -- RX_ALMOST_EMPTY <-- default 8 + proc_mem_mm_bus_wr(16#030#, 8, mm_clk, mm_miso, mm_mosi); -- RX_ALMOST_FULL <-- default 8 + proc_mem_mm_bus_wr(16#034#, 8, mm_clk, mm_miso, mm_mosi); -- TX_ALMOST_EMPTY <-- default 8 + proc_mem_mm_bus_wr(16#038#, c_tx_ready_latency+3, mm_clk, mm_miso, mm_mosi); -- TX_ALMOST_FULL <-- default 3 + + proc_mem_mm_bus_rd(16#0E8#, mm_clk, mm_miso, mm_mosi); -- TX_CMD_STAT --> 0x00040000 : [18]=1 TX_SHIFT16, [17]=0 OMIT_CRC + proc_mem_mm_bus_rd(16#0EC#, mm_clk, mm_miso, mm_mosi); -- RX_CMD_STAT --> 0x02000000 : [25]=1 RX_SHIFT16 + + WAIT UNTIL rising_edge(mm_clk); + END proc_tse_setup; + + + -- Transmit user packet + -- . Use word aligned payload data, so with half word inserted before the 14 byte header + -- . Packets can be send immediately after eachother so new sop directly after last eop + -- . The word rate is controlled by respecting ready from the MAC + PROCEDURE proc_tse_tx_packet(CONSTANT total_header : IN t_eth_total_header; + CONSTANT data_len : IN NATURAL; -- in symbols = octets = bytes + CONSTANT c_data_type : IN NATURAL; -- c_tb_tse_data_type_* + CONSTANT c_ready_latency : IN NATURAL; -- 0, 1 are supported by proc_dp_stream_ready_latency() + CONSTANT c_nof_not_valid : IN NATURAL; -- when > 0 then pull tx valid low for c_nof_not_valid beats during tx + SIGNAL ff_clk : IN STD_LOGIC; + SIGNAL ff_en : IN STD_LOGIC; -- similar purpose as c_nof_not_valid, but not used so pass on signal '1' + SIGNAL ff_src_in : IN t_dp_siso; + SIGNAL ff_src_out : OUT t_dp_sosi) IS + CONSTANT c_eth_header : t_eth_header := total_header.eth; + CONSTANT c_arp_packet : t_eth_total_header_arr := func_eth_arp_total_header( total_header.eth, total_header.arp); + CONSTANT c_icmp_header : t_eth_total_header_arr := func_eth_icmp_total_header(total_header.eth, total_header.ip, total_header.icmp); + CONSTANT c_udp_header : t_eth_total_header_arr := func_eth_udp_total_header( total_header.eth, total_header.ip, total_header.udp); + + CONSTANT c_mod : NATURAL := data_len MOD c_tse_symbols_per_beat; + CONSTANT c_nof_data_beats : NATURAL := data_len / c_tse_symbols_per_beat + sel_a_b(c_mod, 1, 0); + CONSTANT c_empty : NATURAL := sel_a_b(c_mod, c_tse_symbols_per_beat - c_mod, 0); + VARIABLE v_sym : UNSIGNED(c_tse_symbol_w-1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE v_num : UNSIGNED(c_tse_data_w-1 DOWNTO 0) := (OTHERS=>'0'); + BEGIN + ff_src_out.empty <= TO_DP_EMPTY(0); + ---------------------------------------------------------------------------- + -- ETH Header + -- . sop + ff_src_out.data <= RESIZE_DP_DATA(c_udp_header(0)); -- all arp, icmp and udp contain the same eth header, so it is ok to use c_udp_header + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '1', '0', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + ff_src_out.data <= RESIZE_DP_DATA(c_udp_header(1)); -- prepare data before loop, so proc_dp_stream_ready_latency can be called at start of the loops + FOR I IN 2 TO c_nof_eth_beats-1 LOOP + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '0', '0', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + ff_src_out.data <= RESIZE_DP_DATA(c_udp_header(I)); + END LOOP; + ---------------------------------------------------------------------------- + -- ETH higher layer headers + IF c_data_type=c_tb_tse_data_type_arp THEN + FOR I IN c_nof_eth_beats TO c_nof_hdr_beats-2 LOOP + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '0', '0', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + ff_src_out.data <= RESIZE_DP_DATA(c_arp_packet(I)); + END LOOP; + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '0', '0', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + -- . eop + ff_src_out.data <= RESIZE_DP_DATA(c_arp_packet(c_nof_hdr_beats-1)); + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '0', '1', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + ELSIF c_data_type=c_tb_tse_data_type_ping OR c_data_type=c_tb_tse_data_type_udp THEN + FOR I IN c_nof_eth_beats TO c_nof_hdr_beats-1 LOOP + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '0', '0', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + CASE c_data_type IS + WHEN c_tb_tse_data_type_ping => ff_src_out.data <= RESIZE_DP_DATA(c_icmp_header(I)); + WHEN c_tb_tse_data_type_udp => ff_src_out.data <= RESIZE_DP_DATA(c_udp_header(I)); + WHEN OTHERS => NULL; + END CASE; + END LOOP; + END IF; + ---------------------------------------------------------------------------- + -- Data + IF c_data_type/=c_tb_tse_data_type_arp THEN + FOR I IN 0 TO c_nof_data_beats-1 LOOP + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '0', '0', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + CASE c_data_type IS + WHEN c_tb_tse_data_type_counter => + -- data : X"00000001", X"00000002", X"00000003", etc + v_num := v_num + 1; + ff_src_out.data <= RESIZE_DP_DATA(STD_LOGIC_VECTOR(v_num)); + WHEN OTHERS => + -- data : X"01020304", X"05060708", X"090A0B0C", etc + FOR J IN c_tse_symbols_per_beat-1 DOWNTO 0 LOOP + v_sym := v_sym + 1; + ff_src_out.data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) <= STD_LOGIC_VECTOR(v_sym); + END LOOP; + END CASE; + -- tb : pull valid low for some time during the middle of the payload + IF c_nof_not_valid > 0 AND I=c_nof_data_beats/2 THEN + ff_src_out.valid <= '0'; + FOR I IN 0 TO c_nof_not_valid LOOP WAIT UNTIL rising_edge(ff_clk); END LOOP; + ff_src_out.valid <= '1'; + END IF; + END LOOP; + -------------------------------------------------------------------------- + -- Last data + IF c_empty > 0 THEN + -- Overwrite empty data + ff_src_out.empty <= TO_DP_EMPTY(c_empty); + FOR J IN c_empty-1 DOWNTO 0 LOOP + ff_src_out.data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) <= (OTHERS=>'0'); + END LOOP; + END IF; + -- . eop + proc_dp_stream_ready_latency(c_ready_latency, ff_clk, ff_src_in.ready, ff_en, '0', '1', '0', '1', ff_src_out.sync, ff_src_out.valid, ff_src_out.sop, ff_src_out.eop); + END IF; + ---------------------------------------------------------------------------- + -- Initialize for next tx packet + ff_src_out.data <= TO_DP_DATA(0); + ff_src_out.valid <= '0'; + ff_src_out.eop <= '0'; + ff_src_out.empty <= TO_DP_EMPTY(0); + END proc_tse_tx_packet; + + + -- Receive packet + -- . Use word aligned payload data, so with half word inserted before the 14 byte header + -- . Packets can be always be received, assume the user application is always ready + -- . The CRC32 is also passed on to the user at eop. + -- . Note that when empty/=0 then the CRC32 is not word aligned, so therefore use prev_data to be able + -- to handle part of last data word in case empty/=0 at eop + PROCEDURE proc_tse_rx_packet(CONSTANT total_header : IN t_eth_total_header; + CONSTANT c_data_type : IN NATURAL; -- c_tb_tse_data_type_* + SIGNAL ff_clk : IN STD_LOGIC; + SIGNAL ff_snk_in : IN t_dp_sosi; + SIGNAL ff_snk_out : OUT t_dp_siso) IS + CONSTANT c_eth_header : t_eth_header := total_header.eth; + CONSTANT c_arp_packet : t_eth_total_header_arr := func_eth_arp_total_header( total_header.eth, total_header.arp); + CONSTANT c_icmp_header : t_eth_total_header_arr := func_eth_icmp_total_header(total_header.eth, total_header.ip, total_header.icmp); + CONSTANT c_udp_header : t_eth_total_header_arr := func_eth_udp_total_header( total_header.eth, total_header.ip, total_header.udp); + VARIABLE v_sym : UNSIGNED(c_tse_symbol_w-1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE v_num : UNSIGNED(c_tse_data_w-1 DOWNTO 0) := (OTHERS=>'0'); + VARIABLE v_empty : NATURAL; + VARIABLE v_first : BOOLEAN := TRUE; + VARIABLE v_data : STD_LOGIC_VECTOR(c_tse_data_w-1 DOWNTO 0); + VARIABLE v_prev_data : STD_LOGIC_VECTOR(c_tse_data_w-1 DOWNTO 0); + BEGIN + -- Keep ff_rx_snk_out.ready='1' and ff_rx_snk_out.xon='1' all the time + ff_snk_out <= c_dp_siso_rdy; + ---------------------------------------------------------------------------- + -- Verify ETH Header + -- . wait for sop + proc_dp_stream_valid_sop(ff_clk, ff_snk_in.valid, ff_snk_in.sop); + ASSERT ff_snk_in.data(31 DOWNTO 16) = X"0000" REPORT "RX: Wrong ETH alignment half word not zero" SEVERITY ERROR; + ASSERT ff_snk_in.data(15 DOWNTO 0) = c_eth_header.dst_mac(47 DOWNTO 32) REPORT "RX: Wrong ETH dst_mac_addr(47 DOWNTO 32)" SEVERITY ERROR; + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + ASSERT ff_snk_in.data(31 DOWNTO 0) = c_eth_header.dst_mac(31 DOWNTO 0) REPORT "RX: Wrong ETH dst_mac_addr(31 DOWNTO 0)" SEVERITY ERROR; + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + ASSERT ff_snk_in.data(31 DOWNTO 0) = c_eth_header.src_mac(47 DOWNTO 16) REPORT "RX: Wrong ETH src_mac_addr(47 DOWNTO 16)" SEVERITY ERROR; + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + ASSERT ff_snk_in.data(31 DOWNTO 16) = c_eth_header.src_mac(15 DOWNTO 0) REPORT "RX: Wrong ETH src_mac_addr(15 DOWNTO 0)" SEVERITY ERROR; + ASSERT ff_snk_in.data(15 DOWNTO 0) = c_eth_header.eth_type REPORT "RX: Wrong ETH ethertype" SEVERITY ERROR; + ---------------------------------------------------------------------------- + -- Verify ETH higher layer headers + IF c_data_type=c_tb_tse_data_type_arp THEN + FOR I IN c_nof_eth_beats TO c_nof_hdr_beats-1 LOOP + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + ASSERT ff_snk_in.data(31 DOWNTO 0) = c_arp_packet(I) REPORT "RX: Wrong ARP response word" SEVERITY ERROR; + END LOOP; + -- . continue to eop + WHILE ff_snk_in.eop /= '1' LOOP + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + END LOOP; + ELSIF c_data_type=c_tb_tse_data_type_ping OR c_data_type=c_tb_tse_data_type_udp THEN + FOR I IN c_nof_eth_beats TO c_nof_hdr_beats-1 LOOP + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + IF I/=c_ip_header_checksum_wi THEN -- do not verify tx ip header checksum + CASE c_data_type IS + WHEN c_tb_tse_data_type_ping => ASSERT ff_snk_in.data(31 DOWNTO 0) = c_icmp_header(I) REPORT "RX: Wrong IP/ICMP = PING response word" SEVERITY ERROR; + WHEN c_tb_tse_data_type_udp => ASSERT ff_snk_in.data(31 DOWNTO 0) = c_udp_header(I) REPORT "RX: Wrong IP/UDP response word" SEVERITY ERROR; + WHEN OTHERS => NULL; + END CASE; + END IF; + END LOOP; + END IF; + ---------------------------------------------------------------------------- + -- Verify DATA + IF c_data_type/=c_tb_tse_data_type_arp THEN + -- . continue to eop + v_first := TRUE; + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + WHILE ff_snk_in.eop /= '1' LOOP + v_prev_data := v_data; + v_data := ff_snk_in.data(c_tse_data_w-1 DOWNTO 0); + IF v_first = FALSE THEN + CASE c_data_type IS + WHEN c_tb_tse_data_type_counter => + -- data : X"00000001", X"00000002", X"00000003", etc + v_num := v_num + 1; + IF UNSIGNED(v_prev_data)/=0 THEN -- do not verify zero padding + ASSERT UNSIGNED(v_prev_data) = v_num REPORT "RX: Wrong data word" SEVERITY ERROR; + END IF; + WHEN OTHERS => + -- data : X"01020304", X"05060708", X"090A0B0C", etc + FOR J IN c_tse_symbols_per_beat-1 DOWNTO 0 LOOP + v_sym := v_sym + 1; + IF UNSIGNED(v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w))/=0 THEN -- do not verify zero padding + ASSERT UNSIGNED(v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w)) = v_sym REPORT "RX: Wrong data symbol" SEVERITY ERROR; + END IF; + END LOOP; + END CASE; + END IF; + v_first := FALSE; + proc_dp_stream_valid(ff_clk, ff_snk_in.valid); + END LOOP; + -------------------------------------------------------------------------- + -- Verify last DATA and CRC32 if empty/=0 else the last word is only the CRC32 + v_prev_data := v_data; + v_data := ff_snk_in.data(c_tse_data_w-1 DOWNTO 0); + v_empty := TO_INTEGER(UNSIGNED(ff_snk_in.empty(c_tse_empty_w-1 DOWNTO 0))); + IF v_empty > 0 THEN + FOR J IN v_empty-1 DOWNTO 0 LOOP + v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) := (OTHERS=>'0'); + END LOOP; + CASE c_data_type IS + WHEN c_tb_tse_data_type_counter => + -- data : X"00000001", X"00000002", X"00000003", etc + v_num := v_num + 1; + FOR J IN v_empty-1 DOWNTO 0 LOOP + v_num((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w) := (OTHERS=>'0'); -- force CRC32 symbols in last data word to 0 + END LOOP; + IF UNSIGNED(v_prev_data)/=0 THEN -- do not verify zero padding + ASSERT UNSIGNED(v_prev_data) = v_num REPORT "RX: Wrong empty data word" SEVERITY ERROR; + END IF; + WHEN OTHERS => + -- data : X"01020304", X"05060708", X"090A0B0C", etc + FOR J IN c_tse_symbols_per_beat-1 DOWNTO v_empty LOOP -- ignore CRC32 symbols in last data word + v_sym := v_sym + 1; + IF UNSIGNED(v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w))/=0 THEN -- do not verify zero padding + ASSERT UNSIGNED(v_prev_data((J+1)*c_tse_symbol_w-1 DOWNTO J*c_tse_symbol_w)) = v_sym REPORT "RX: Wrong empty data symbol" SEVERITY ERROR; + END IF; + END LOOP; + END CASE; + END IF; + END IF; + -- No verify on CRC32 word + END proc_tse_rx_packet; + +END tb_tse_pkg;