diff --git a/libraries/io/eth/hdllib.cfg b/libraries/io/eth/hdllib.cfg index 7f3ddcef911dd0a4f87e51ec945339df036cb37a..8c9e731ddc9c4a15fd45b4c995e40d4ec43769bf 100644 --- a/libraries/io/eth/hdllib.cfg +++ b/libraries/io/eth/hdllib.cfg @@ -7,6 +7,7 @@ hdl_lib_technology = synth_files = src/vhdl/eth_pkg.vhd src/vhdl/eth_checksum.vhd + src/vhdl/eth_checksum_10G.vhd src/vhdl/eth_hdr_store.vhd src/vhdl/eth_hdr_status.vhd src/vhdl/eth_hdr_ctrl.vhd @@ -25,6 +26,7 @@ synth_files = test_bench_files = src/vhdl/eth_statistics.vhd tb/vhdl/tb_eth_checksum.vhd + tb/vhdl/tb_eth_checksum_10G.vhd tb/vhdl/tb_eth_crc_ctrl.vhd tb/vhdl/tb_eth_hdr.vhd tb/vhdl/tb_eth.vhd diff --git a/libraries/io/eth/src/vhdl/eth_checksum_10G.vhd b/libraries/io/eth/src/vhdl/eth_checksum_10G.vhd new file mode 100644 index 0000000000000000000000000000000000000000..19e45a8d07443d036b213d5d4c59dd984b8a4db6 --- /dev/null +++ b/libraries/io/eth/src/vhdl/eth_checksum_10G.vhd @@ -0,0 +1,177 @@ +------------------------------------------------------------------------------- +-- +-- 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 dp_lib.dp_stream_pkg.ALL; +USE work.eth_pkg.ALL; + +-- Purpose: +-- Can be used to calculate the checksum for IPv4, ICMP and UDP, provided +-- the correct words are provided between sop and eop. +-- Description: +-- Determine the 16 bit 1-complement checksum according IPv4 to for the valid +-- words between snk_in.sop and snk_in.eop, taking in account snk_in.empty. +-- . For checksum verification the result should be 0 when the words are OK. +-- . For checksum calculation the result should be used at the checksum field +-- in the packet header. +-- Remarks: +-- . At the snk_in.sop the checksum is initialized to 0. +-- . At the snk_in.eop the snk_in.empty LSBytes are padded with 0. +-- . The words do not need to be provided in order, because the checksum is +-- based on addition. +-- . Assume that snk_in.sop and snk_in.eop are only active when snk_in.valid +-- is active. +-- . Assume that between packets so from snk_in.eop to next snk_in.sop the +-- snk_in.valid is inactive and that snk_in.valid is only active for new +-- data. + +ENTITY eth_checksum_10G IS + PORT ( + rst : IN STD_LOGIC; + clk : IN STD_LOGIC; + + snk_in : IN t_dp_sosi; + + checksum : OUT STD_LOGIC_VECTOR(c_halfword_w-1 DOWNTO 0); + checksum_val : OUT STD_LOGIC + ); +END eth_checksum_10G; + + +ARCHITECTURE rtl OF eth_checksum_10G IS + + SIGNAL in_data : STD_LOGIC_VECTOR(c_longword_w-1 DOWNTO 0); + SIGNAL in_valid : STD_LOGIC; + SIGNAL in_sop : STD_LOGIC; + SIGNAL in_eop : STD_LOGIC; + + SIGNAL prev_in_valid : STD_LOGIC; + SIGNAL prev_in_eop : STD_LOGIC; + SIGNAL prev_in_eop_dly : STD_LOGIC; + + SIGNAL word_sum_cin : UNSIGNED(1 DOWNTO 0); -- carry in + SIGNAL word_sum_dat : UNSIGNED(c_halfword_w-1 DOWNTO 0); + SIGNAL word_sum : UNSIGNED(c_halfword_w+1 DOWNTO 0); + SIGNAL nxt_word_sum : UNSIGNED(c_halfword_w+1 DOWNTO 0); + + SIGNAL sum_cin : UNSIGNED(0 DOWNTO 0); -- carry in + SIGNAL sum_dat : UNSIGNED(c_halfword_w-1 DOWNTO 0); + SIGNAL sum : UNSIGNED(c_halfword_w DOWNTO 0); + SIGNAL nxt_sum : UNSIGNED(c_halfword_w DOWNTO 0); + + SIGNAL last_dat : UNSIGNED(c_halfword_w-1 DOWNTO 0); + + SIGNAL i_checksum : STD_LOGIC_VECTOR(checksum'RANGE); + SIGNAL nxt_checksum : STD_LOGIC_VECTOR(checksum'RANGE); + SIGNAL i_checksum_val : STD_LOGIC; + SIGNAL nxt_checksum_val : STD_LOGIC; + + SIGNAL data_p0 : STD_LOGIC_VECTOR(c_halfword_w+1 DOWNTO 0); + SIGNAL data_p1 : STD_LOGIC_VECTOR(c_halfword_w+1 DOWNTO 0); + SIGNAL data_p2 : STD_LOGIC_VECTOR(c_halfword_w+1 DOWNTO 0); + SIGNAL data_p3 : STD_LOGIC_VECTOR(c_halfword_w+1 DOWNTO 0); + +BEGIN + + data_p0 <= "00" & in_data(c_longword_w -1 DOWNTO c_halfword_w * 3); + data_p1 <= "00" & in_data(c_halfword_w*3 -1 DOWNTO c_halfword_w * 2); + data_p2 <= "00" & in_data(c_halfword_w*2 -1 DOWNTO c_halfword_w ); + data_p3 <= "00" & in_data(c_halfword_w -1 DOWNTO 0); + + checksum <= i_checksum; + checksum_val <= i_checksum_val; + + p_clk : PROCESS(rst, clk) + BEGIN + IF rst='1' THEN + prev_in_valid <= '0'; + prev_in_eop <= '0'; + prev_in_eop_dly <= '0'; + word_sum <= (OTHERS=>'0'); + sum <= (OTHERS=>'0'); + i_checksum <= (OTHERS=>'0'); + i_checksum_val <= '0'; + ELSIF rising_edge(clk) THEN + -- input + prev_in_valid <= in_valid; + prev_in_eop <= in_eop; + prev_in_eop_dly <= prev_in_eop; + -- internal + word_sum <= nxt_word_sum; + sum <= nxt_sum; + -- outputs + i_checksum <= nxt_checksum; + i_checksum_val <= nxt_checksum_val; + END IF; + END PROCESS; + + -- Combinatorial sink input + p_zero_empty : PROCESS(snk_in, in_eop) + BEGIN + in_data <= snk_in.data(c_longword_w-1 DOWNTO 0); + IF in_eop='1' THEN + CASE TO_INTEGER(UNSIGNED(snk_in.empty(c_eth_empty_w-1 DOWNTO 0))) IS + WHEN 1 => in_data <= snk_in.data(c_longword_w-1 DOWNTO c_byte_w) & c_slv0( c_byte_w-1 DOWNTO 0); + WHEN 2 => in_data <= snk_in.data(c_longword_w-1 DOWNTO 2*c_byte_w) & c_slv0(2*c_byte_w-1 DOWNTO 0); + WHEN 3 => in_data <= snk_in.data(c_longword_w-1 DOWNTO 3*c_byte_w) & c_slv0(3*c_byte_w-1 DOWNTO 0); + WHEN 4 => in_data <= snk_in.data(c_longword_w-1 DOWNTO 4*c_byte_w) & c_slv0(4*c_byte_w-1 DOWNTO 0); + WHEN 5 => in_data <= snk_in.data(c_longword_w-1 DOWNTO 5*c_byte_w) & c_slv0(5*c_byte_w-1 DOWNTO 0); + WHEN 6 => in_data <= snk_in.data(c_longword_w-1 DOWNTO 6*c_byte_w) & c_slv0(6*c_byte_w-1 DOWNTO 0); + WHEN 7 => in_data <= snk_in.data(c_longword_w-1 DOWNTO 7*c_byte_w) & c_slv0(7*c_byte_w-1 DOWNTO 0); + WHEN OTHERS => NULL; + END CASE; + END IF; + END PROCESS; + + in_valid <= snk_in.valid; + in_sop <= snk_in.sop; + in_eop <= snk_in.eop; + + -- Word sum + word_sum_cin <= (OTHERS => '0') WHEN in_sop='1' ELSE word_sum(c_halfword_w +1 DOWNTO c_halfword_w); + word_sum_dat <= word_sum(c_halfword_w-1 DOWNTO 0); + nxt_word_sum <= UNSIGNED(data_p0) + + UNSIGNED(data_p1) + + UNSIGNED(data_p2) + + UNSIGNED(data_p3) + + word_sum_cin WHEN in_valid='1' ELSE + word_sum; + + -- Accumulated sum + sum_cin(0) <= sum(c_halfword_w); + sum_dat <= sum(c_halfword_w-1 DOWNTO 0); + nxt_sum <= (OTHERS=>'0') WHEN in_sop='1' ELSE + ('0' & sum_dat) + word_sum_dat + sum_cin WHEN prev_in_valid='1' ELSE + sum; + + -- Accumulate the last carry + last_dat <= sum(c_halfword_w-1 DOWNTO 0) + sum_cin; + + -- Checksum is 1-complement of the sum + nxt_checksum <= NOT(STD_LOGIC_VECTOR(last_dat)) WHEN prev_in_eop_dly='1' ELSE i_checksum; + nxt_checksum_val <= '1' WHEN prev_in_eop_dly='1' ELSE + '0' WHEN in_sop='1' ELSE i_checksum_val; + +END rtl; diff --git a/libraries/io/eth/tb/vhdl/tb_eth_checksum_10G.vhd b/libraries/io/eth/tb/vhdl/tb_eth_checksum_10G.vhd new file mode 100644 index 0000000000000000000000000000000000000000..0c6a5a9829d389e7ec71ffe77cdd8bf3a65c4307 --- /dev/null +++ b/libraries/io/eth/tb/vhdl/tb_eth_checksum_10G.vhd @@ -0,0 +1,206 @@ +------------------------------------------------------------------------------- +-- +-- 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 eth_checksum +-- Description: +-- +-- Example from Wiki IPv4. Use Hex 45000030442240008006442e8c7c19acae241e2b +-- (20Bytes IP header) the c_exp_checksum then becomes: +-- +-- 4500 + 0030 + 4422 + 4000 + 8006 + 0000 + 8c7c + 19ac + ae24 + 1e2b = 2BBCF +-- 2 + BBCF = BBD1 = 1011101111010001, the 1'S of sum = 0100010000101110 = 442E +-- +-- Verify that checksum=c_exp_checksum when checksum_val='1' +-- +-- Usage: +-- > as 10 +-- > run -all + +LIBRARY IEEE, common_lib, dp_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE common_lib.common_pkg.ALL; +USE dp_lib.dp_stream_pkg.ALL; +USE work.eth_pkg.ALL; + + +ENTITY tb_eth_checksum_10G IS +END tb_eth_checksum_10G; + + +ARCHITECTURE tb OF tb_eth_checksum_10G IS + + CONSTANT clk_period : TIME := 10 ns; -- 100 MHz + + CONSTANT c_exp_checksum : NATURAL := 16#442E#; + + -- Minimum nof clk cycles between eop and sop + CONSTANT c_checksum_latency : NATURAL := 3; + CONSTANT c_wait_eop_sop : NATURAL := 10; -- >= c_checksum_latency-1; + + SIGNAL tb_end : STD_LOGIC := '0'; + SIGNAL clk : STD_LOGIC := '1'; + SIGNAL rst : STD_LOGIC; + + SIGNAL src_out : t_dp_sosi; + + SIGNAL checksum : STD_LOGIC_VECTOR(c_halfword_w-1 DOWNTO 0); + SIGNAL checksum_val : STD_LOGIC; + +BEGIN + + clk <= NOT clk OR tb_end AFTER clk_period/2; + rst <= '1', '0' AFTER 3*clk_period; + + p_stimuli : PROCESS + BEGIN + src_out.data <= TO_DP_DATA(0); + src_out.valid <= '0'; + src_out.sop <= '0'; + src_out.eop <= '0'; + src_out.empty <= TO_DP_EMPTY(0); + WAIT UNTIL rst='0'; + WAIT UNTIL rising_edge(clk); + + ---------------------------------------------------------------------------- + -- First + ---------------------------------------------------------------------------- + src_out.sop <= '1'; + src_out.valid <= '1'; + src_out.data <= RESIZE_DP_DATA(X"4500003044224000"); + WAIT UNTIL rising_edge(clk); + src_out.sop <= '0'; + src_out.data <= RESIZE_DP_DATA(X"800600008c7c19ac"); + WAIT UNTIL rising_edge(clk); + src_out.eop <= '1'; + src_out.data <= RESIZE_DP_DATA(X"00000000ae241e2b"); + WAIT UNTIL rising_edge(clk); + src_out.data <= (OTHERS=>'0'); + src_out.valid <= '0'; + src_out.eop <= '0'; + -- Wait latency + FOR I IN 0 TO c_wait_eop_sop-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Again with valid low for a few cycles + ---------------------------------------------------------------------------- + src_out.sop <= '1'; + src_out.valid <= '1'; + src_out.data <= RESIZE_DP_DATA(X"4500003044224000"); + WAIT UNTIL rising_edge(clk); + src_out.sop <= '0'; + src_out.data <= RESIZE_DP_DATA(X"800600008c7c19ac"); + + -- pause + src_out.valid <= '0'; + FOR I IN 0 TO 1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + src_out.valid <= '1'; + + WAIT UNTIL rising_edge(clk); + src_out.eop <= '1'; + src_out.data <= RESIZE_DP_DATA(X"00000000ae241e2b"); + WAIT UNTIL rising_edge(clk); + src_out.data <= (OTHERS=>'0'); + src_out.valid <= '0'; + src_out.eop <= '0'; + -- Wait latency + FOR I IN 0 TO c_wait_eop_sop-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Again with carry in within word_sum + ---------------------------------------------------------------------------- + src_out.sop <= '1'; + src_out.valid <= '1'; + src_out.data <= RESIZE_DP_DATA(X"4500003044224000"); +-- WAIT UNTIL rising_edge(clk); +-- src_out.data <= RESIZE_DP_DATA(X"80060000"); +-- WAIT UNTIL rising_edge(clk); +-- src_out.data <= RESIZE_DP_DATA(X"8c7c19ac"); + WAIT UNTIL rising_edge(clk); + src_out.sop <= '0'; + src_out.data <= RESIZE_DP_DATA(X"80068c7c000019ac"); + + -- pause + src_out.valid <= '0'; + FOR I IN 0 TO 0 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + src_out.valid <= '1'; + + WAIT UNTIL rising_edge(clk); + src_out.eop <= '1'; + src_out.data <= RESIZE_DP_DATA(X"00000000ae241e2b"); + WAIT UNTIL rising_edge(clk); + src_out.data <= (OTHERS=>'0'); + src_out.valid <= '0'; + src_out.eop <= '0'; + -- Wait latency + FOR I IN 0 TO c_wait_eop_sop-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + ---------------------------------------------------------------------------- + -- Again with empty = 2 + ---------------------------------------------------------------------------- + src_out.empty <= TO_DP_EMPTY(2); + src_out.sop <= '1'; + src_out.valid <= '1'; + src_out.data <= RESIZE_DP_DATA(X"4500003044224000"); + WAIT UNTIL rising_edge(clk); + src_out.sop <= '0'; + src_out.data <= RESIZE_DP_DATA(X"80061e2b8c7c19ac"); -- overwrite these "0000" with the last 2, now empty, bytes "1e2b", to keep the seem expected result + WAIT UNTIL rising_edge(clk); + src_out.eop <= '1'; + src_out.data <= RESIZE_DP_DATA(X"00000000ae241e2b"); + WAIT UNTIL rising_edge(clk); + src_out.data <= (OTHERS=>'0'); + src_out.valid <= '0'; + src_out.eop <= '0'; + -- Wait latency + FOR I IN 0 TO c_wait_eop_sop-1 LOOP WAIT UNTIL rising_edge(clk); END LOOP; + + tb_end <= '1'; + ASSERT FALSE REPORT "Simulation tb_eth_checksum finished." SEVERITY NOTE; + WAIT; + END PROCESS; + + + p_verify : PROCESS + BEGIN + WAIT UNTIL rising_edge(clk); + IF checksum_val='1' THEN + ASSERT UNSIGNED(checksum)=c_exp_checksum REPORT "Wrong checksum" SEVERITY ERROR; + END IF; + IF tb_end='1' THEN + ASSERT checksum_val='1' REPORT "Checksum is not valid at tb_end" SEVERITY ERROR; + END IF; + END PROCESS; + + u_dut : ENTITY work.eth_checksum_10G + PORT MAP ( + rst => rst, + clk => clk, + + snk_in => src_out, + + checksum => checksum, + checksum_val => checksum_val + ); + +END tb;