├── xilinx
├── test
│ ├── test_configuration_spartan6.vhd
│ ├── test_wrapper_spartan6.vhd
│ ├── test_instance_spartan6.ucf
│ └── test_instance_spartan6.vhd
├── output_buffer.vhd
├── single_signal_synchronizer_spartan6.vhd
├── input_buffer.vhd
├── tx_fifo.vhd
├── fixed_input_delay.vhd
├── ipcore_dir
│ ├── ethernet_mac_tx_fifo_xilinx.xise
│ └── ethernet_mac_tx_fifo_xilinx.xco
└── mii_gmii_io_spartan6.vhd
├── generic
└── single_signal_synchronizer_simple.vhd
├── single_signal_synchronizer.vhd
├── miim_types.vhd
├── .gitignore
├── Makefile
├── ethernet_types.vhd
├── mii_gmii_io.vhd
├── test
├── test_common.vhd
└── test_instance.vhd
├── LICENSE.md
├── crc32.vhd
├── reset_generator.vhd
├── framing_common.vhd
├── crc.vhd
├── utility.vhd
├── crc32_tb.vhd
├── tx_fifo_adapter.vhd
├── miim.vhd
├── mii_gmii.vhd
├── ethernet_with_fifos.vhd
├── miim_control.vhd
├── ethernet.vhd
├── miim_registers.vhd
├── README.md
├── framing.vhd
├── rx_fifo.vhd
└── ethernet_mac_tb.vhd
/xilinx/test/test_configuration_spartan6.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Configuration for using the testbench in post-synthesis simulation
7 | -- Start work.post_synthesis_spartan6 in your simulator of choice
8 |
9 | configuration post_synthesis_spartan6 of ethernet_mac_tb is
10 | for behavioral
11 | for all : test_instance
12 | use entity work.test_wrapper_spartan6;
13 | end for;
14 | end for;
15 | end configuration;
--------------------------------------------------------------------------------
/generic/single_signal_synchronizer_simple.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Simple two-FF synchronizer
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | architecture simple of single_signal_synchronizer is
12 | signal signal_tmp : std_ulogic := '0';
13 | begin
14 | process(clock_target_i, preset_i)
15 | begin
16 | if preset_i = '1' then
17 | signal_tmp <= '1';
18 | signal_o <= '1';
19 | elsif rising_edge(clock_target_i) then
20 | signal_tmp <= signal_i;
21 | signal_o <= signal_tmp;
22 | end if;
23 | end process;
24 | end architecture;
25 |
--------------------------------------------------------------------------------
/single_signal_synchronizer.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Synchronize a single bit from an arbitrary clock domain
7 | -- into the clock_target domain
8 |
9 | library ieee;
10 | use ieee.std_logic_1164.all;
11 |
12 | entity single_signal_synchronizer is
13 | port(
14 | clock_target_i : in std_ulogic;
15 | -- Asynchronous preset of the output and synchronizer flip-flops
16 | preset_i : in std_ulogic := '0';
17 | -- Asynchronous signal input
18 | signal_i : in std_ulogic;
19 | -- Synchronous signal output
20 | signal_o : out std_ulogic
21 | );
22 | end entity;
--------------------------------------------------------------------------------
/miim_types.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- MIIM types
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | package miim_types is
13 | subtype t_register_address is unsigned(4 downto 0);
14 | subtype t_phy_address is unsigned(4 downto 0);
15 | subtype t_data is std_ulogic_vector(15 downto 0);
16 |
17 | function to_register_address(c : natural) return t_register_address;
18 |
19 | constant DEFAULT_POLL_WAIT_TICKS : natural := 10000000;
20 | end package;
21 |
22 | package body miim_types is
23 | function to_register_address(c : natural) return t_register_address is
24 | begin
25 | return to_unsigned(c, t_register_address'length);
26 | end function;
27 | end package body;
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.tcl
3 | .Xil/
4 | .settings/
5 | _ngo/
6 | _xmsgs/
7 | *.log
8 | *.xmsgs
9 | *.cmd
10 | tmp/
11 | iseconfig/
12 | *.html
13 | isim
14 | netgen/
15 | *.rpt
16 | planAhead_run_*/
17 | smartxplorer_results/
18 | smartxplorer_setup/
19 | xst/
20 | xilinxsim.ini
21 | xlnx_auto_0_xdb/
22 | *.xml
23 | *.xrpt
24 | *.bgn
25 | *.bit
26 | *.bld
27 | *.cmd_log
28 | *.drc
29 | *.lpc
30 | *.lso
31 | *.ncd
32 | *.ngc
33 | *.ngd
34 | *.ngr
35 | *.pad
36 | *.par
37 | *.psr
38 | *.pcf
39 | *.prj
40 | *.ptwx
41 | *.stx
42 | *.sty
43 | *.syr
44 | *.twr
45 | *.twx
46 | *.unroutes
47 | *.ut
48 | *.xdl
49 | *.xpi
50 | *.xst
51 | *.xwbt
52 | *.wdb
53 | *.exe
54 | *.map
55 | *.mrp
56 | *.ngm
57 | *.csv
58 | *_pad.txt
59 | *.debug
60 | *.gise
61 |
62 | **/ipcore_dir/*.vhd
63 | **/ipcore_dir/*.txt
64 | **/ipcore_dir/*.asy
65 | **/ipcore_dir/*.sym
66 | **/ipcore_dir/*.vho
67 | **/ipcore_dir/*.ncf
68 | **/ipcore_dir/*.cgp
69 | **/ipcore_dir/*.xci
70 | **/ipcore_dir/*/
71 |
72 | .project
73 | *.wcfg
74 | ghdl/
75 | ethernet_mac_tb
76 |
77 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Set ISE_DIR to ISE installation directory. Example: .../14.7/ISE_DS/ISE
2 |
3 | OPTS=--std=93c -g --ieee=synopsys -fexplicit --warn-no-vital-generic
4 | # --no-vital-checks
5 |
6 | all:
7 | ghdl -i --work=ethernet_mac --workdir=ghdl $(OPTS) *.vhd xilinx/*.vhd test/*.vhd xilinx/ipcore_dir/ethernet_mac_tx_fifo_xilinx.vhd
8 | ghdl -m --work=ethernet_mac --workdir=ghdl -Pghdl/unisim -Pghdl/xilinxcorelib $(OPTS) ethernet_mac_tb
9 |
10 | check: all
11 | ghdl -r --work=ethernet_mac --workdir=ghdl -Pghdl/unisim -Pghdl/xilinxcorelib $(OPTS) ethernet_mac_tb --ieee-asserts=disable $(CHECK_OPTS)
12 |
13 | prepare:
14 | mkdir -p ghdl/unisim ghdl/xilinxcorelib #ghdl/simprim
15 | ghdl -i --work=unisim --workdir=ghdl/unisim $(OPTS) $(ISE_DIR)/vhdl/src/unisims/*.vhd
16 | ghdl -i --work=unisim --workdir=ghdl/unisim $(OPTS) $(ISE_DIR)/vhdl/src/unisims/primitive/*.vhd
17 | ghdl -i --work=XilinxCoreLib --workdir=ghdl/xilinxcorelib $(OPTS) $(ISE_DIR)/vhdl/src/XilinxCoreLib/*.vhd
18 | #ghdl -i --work=simprim --workdir=ghdl/simprim $(OPTS) $(ISE_DIR)/vhdl/src/simprims/simprim_Vcomponents.vhd $(ISE_DIR)/vhdl/src/simprims/simprim_Vpackage.vhd $(ISE_DIR)/vhdl/src/simprims/primitive/other/*.vhd
19 |
--------------------------------------------------------------------------------
/ethernet_types.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | library ieee;
7 | use ieee.std_logic_1164.all;
8 |
9 | package ethernet_types is
10 | -- One Ethernet interface byte
11 | subtype t_ethernet_data is std_ulogic_vector(7 downto 0);
12 | -- Ethernet speed, values defined below
13 | subtype t_ethernet_speed is std_ulogic_vector(1 downto 0);
14 | -- Ethernet MAC layer address
15 | constant MAC_ADDRESS_BYTES : positive := 6;
16 | subtype t_mac_address is std_ulogic_vector((MAC_ADDRESS_BYTES * 8 - 1) downto 0);
17 | -- Use utility.reverse_bytes to convert from the canoncial form to the internal representation
18 | -- Example: signal m : t_mac_address := reverse_bytes(x"04AA19BCDE10");
19 | -- m then represents the canoncial address 04-AA-19-BC-DE-10
20 | -- Broadcast address
21 | constant BROADCAST_MAC_ADDRESS : t_mac_address := x"FFFFFFFFFFFF";
22 |
23 | -- Speed constants
24 | constant SPEED_1000MBPS : t_ethernet_speed := "10";
25 | constant SPEED_100MBPS : t_ethernet_speed := "01";
26 | constant SPEED_10MBPS : t_ethernet_speed := "00";
27 | constant SPEED_UNSPECIFIED : t_ethernet_speed := "11";
28 | end package;
--------------------------------------------------------------------------------
/xilinx/output_buffer.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Configurable output buffer forced into the IO block
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | library unisim;
12 | use unisim.vcomponents.all;
13 |
14 | entity output_buffer is
15 | port(
16 | -- Connect to pad or OBUF
17 | pad_o : out std_ulogic;
18 | -- Connect to user logic
19 | buffer_i : in std_ulogic;
20 |
21 | -- Capture clock
22 | clock_i : in std_ulogic
23 | );
24 | end entity;
25 |
26 | architecture spartan_6 of output_buffer is
27 | -- Force putting input Flip-Flop into IOB so it doesn't end up in a normal logic tile
28 | -- which would ruin the timing.
29 | attribute iob : string;
30 | attribute iob of FDRE_inst : label is "FORCE";
31 | begin
32 | FDRE_inst : FDRE
33 | generic map(
34 | INIT => '0') -- Initial value of register ('0' or '1')
35 | port map(
36 | Q => pad_o, -- Data output
37 | C => clock_i, -- Clock input
38 | CE => '1', -- Clock enable input
39 | R => '0', -- Synchronous reset input
40 | D => buffer_i -- Data input
41 | );
42 | end architecture;
43 |
--------------------------------------------------------------------------------
/mii_gmii_io.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Device-specific IO setup needed for communicating with the PHY
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | use work.ethernet_types.all;
12 |
13 | entity mii_gmii_io is
14 | port(
15 | -- 125 MHz clock input (exact requirements can vary by implementation)
16 | -- Spartan 6: clock should be unbuffered
17 | clock_125_i : in std_ulogic;
18 |
19 | -- RX and TX clocks
20 | clock_tx_o : out std_ulogic;
21 | clock_rx_o : out std_ulogic;
22 |
23 | -- Speed selection for clock switch
24 | speed_select_i : in t_ethernet_speed;
25 |
26 | -- Signals connected directly to external ports
27 | -- MII
28 | mii_tx_clk_i : in std_ulogic;
29 | mii_tx_en_o : out std_ulogic;
30 | mii_txd_o : out t_ethernet_data;
31 | mii_rx_clk_i : in std_ulogic;
32 | mii_rx_er_i : in std_ulogic;
33 | mii_rx_dv_i : in std_ulogic;
34 | mii_rxd_i : in t_ethernet_data;
35 |
36 | -- GMII
37 | gmii_gtx_clk_o : out std_ulogic;
38 |
39 | -- Signals connected to the mii_gmii module
40 | int_mii_tx_en_i : in std_ulogic;
41 | int_mii_txd_i : in t_ethernet_data;
42 | int_mii_rx_er_o : out std_ulogic;
43 | int_mii_rx_dv_o : out std_ulogic;
44 | int_mii_rxd_o : out t_ethernet_data
45 | );
46 | end entity;
47 |
48 |
--------------------------------------------------------------------------------
/test/test_common.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Some types used in the test user application and the testbench
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | use work.ethernet_types.all;
12 | use work.utility.all;
13 |
14 | package test_common is
15 | type t_test_mode is (
16 | -- Do nothing
17 | TEST_NOTHING,
18 | -- Loop all RX frames back to TX identically
19 | TEST_LOOPBACK,
20 | -- Transmit frames with each size from 1 to 59 bytes to see if they are padded correctly
21 | TEST_TX_PADDING
22 | );
23 |
24 | constant TEST_MAC_ADDRESS : t_mac_address := reverse_bytes(x"000102030405");
25 |
26 | component test_instance
27 | port(clock_125_i : in std_ulogic;
28 | user_clock_i : in std_ulogic;
29 | reset_i : in std_ulogic;
30 |
31 | mii_tx_clk_i : in std_ulogic;
32 | mii_tx_er_o : out std_ulogic;
33 | mii_tx_en_o : out std_ulogic;
34 | mii_txd_o : out std_ulogic_vector(7 downto 0);
35 | mii_rx_clk_i : in std_ulogic;
36 | mii_rx_er_i : in std_ulogic;
37 | mii_rx_dv_i : in std_ulogic;
38 | mii_rxd_i : in std_ulogic_vector(7 downto 0);
39 |
40 | gmii_gtx_clk_o : out std_ulogic;
41 |
42 | rgmii_tx_ctl_o : out std_ulogic;
43 | rgmii_rx_ctl_i : in std_ulogic;
44 |
45 | speed_override_i : in t_ethernet_speed;
46 |
47 | test_mode_i : in t_test_mode);
48 | end component;
49 | end package;
50 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Philipp Kerling
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of ethernet\_mac nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | * Neither the source code, nor any derivative product, may be used for military
19 | purposes.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/crc32.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Utility functions for CRC32/Ethernet frame check sequence calculation
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | use work.crc.all;
12 |
13 | package crc32 is
14 | -- As defined in IEEE 802.3 clause 3.2.9
15 | -- x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1
16 | constant CRC32_POLYNOMIAL : std_ulogic_vector(32 downto 0) := (
17 | 32 | 26 | 23 | 22 | 16 | 12 | 11 | 10 | 8 | 7 | 5 | 4 | 2 | 1 | 0 => '1',
18 | others => '0'
19 | );
20 |
21 | -- CRC32 value type
22 | subtype t_crc32 is std_ulogic_vector(31 downto 0);
23 |
24 | -- Value that remains as CRC value when incoming data including the original FCS is piped through update_crc32
25 | -- and the FCS is correct.
26 | -- Usually this would be zero, but the inversion of the FCS in clause 3.2.9 e changes it to this magic value.
27 | constant CRC32_POSTINVERT_MAGIC : t_crc32 := X"C704DD7B";
28 |
29 | -- Update CRC32 old_crc by one bit (input)
30 | function update_crc32(old_crc : t_crc32; input : std_ulogic) return t_crc32;
31 | -- Update CRC32 old_crc by an arbitrary number of bits (input)
32 | function update_crc32(old_crc : t_crc32; input : std_ulogic_vector) return t_crc32;
33 |
34 | constant CRC32_BYTES : positive := (t_crc32'length / 8);
35 | end package;
36 |
37 | package body crc32 is
38 | function update_crc32(old_crc : t_crc32; input : std_ulogic) return t_crc32 is
39 | begin
40 | return update_crc(old_crc, input, CRC32_POLYNOMIAL);
41 | end function;
42 |
43 | function update_crc32(old_crc : t_crc32; input : std_ulogic_vector) return t_crc32 is
44 | begin
45 | return update_crc(old_crc, input, CRC32_POLYNOMIAL);
46 | end function;
47 | end package body;
48 |
--------------------------------------------------------------------------------
/reset_generator.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Monitor the speed and issue a core-wide reset if it changes
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | use work.ethernet_types.all;
12 |
13 | entity reset_generator is
14 | generic(
15 | -- Number of clock_i ticks reset should get asserted for
16 | RESET_TICKS : positive := 1000
17 | );
18 | port(
19 | clock_i : in std_ulogic;
20 | -- Speed signal synchronous to clock_i
21 | speed_i : in t_ethernet_speed;
22 |
23 | -- Asynchronous reset input for this logic
24 | -- Do NOT connect reset_i and reset_o anywhere in the design
25 | reset_i : in std_ulogic;
26 | -- Reset output
27 | -- Is also asserted whenever reset_i is asserted
28 | reset_o : out std_ulogic
29 | );
30 | end entity;
31 |
32 | architecture rtl of reset_generator is
33 | type t_state is (
34 | WATCH,
35 | RESET
36 | );
37 | signal state : t_state := WATCH;
38 | signal reset_counter : integer range 0 to RESET_TICKS;
39 |
40 | signal last_speed : t_ethernet_speed;
41 | begin
42 | speed_watch : process(reset_i, clock_i)
43 | begin
44 | if reset_i = '1' then
45 | last_speed <= SPEED_UNSPECIFIED;
46 | state <= WATCH;
47 | reset_o <= '1';
48 | elsif rising_edge(clock_i) then
49 | reset_o <= '0';
50 |
51 | case state is
52 | when WATCH =>
53 | null;
54 | when RESET =>
55 | reset_o <= '1';
56 | if reset_counter = RESET_TICKS then
57 | state <= WATCH;
58 | else
59 | reset_counter <= reset_counter + 1;
60 | end if;
61 | end case;
62 |
63 | if speed_i /= last_speed then
64 | -- Speed was changed
65 | state <= RESET;
66 | -- Always reset counter
67 | reset_counter <= 0;
68 | end if;
69 |
70 | last_speed <= speed_i;
71 | end if;
72 | end process;
73 |
74 | end architecture;
75 |
--------------------------------------------------------------------------------
/xilinx/single_signal_synchronizer_spartan6.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Two-FF synchronizer that uses constraints to prohibit XST from
7 | -- optimizing it away
8 |
9 | library ieee;
10 | use ieee.std_logic_1164.all;
11 |
12 | library unisim;
13 | use unisim.vcomponents.all;
14 |
15 | architecture spartan6 of single_signal_synchronizer is
16 | signal signal_tmp : std_ulogic := '0';
17 |
18 | -- Constrain registers
19 | attribute ASYNC_REG : string;
20 | attribute ASYNC_REG of FDPE_tmp_inst : label is "TRUE";
21 | attribute ASYNC_REG of FDPE_out_inst : label is "TRUE";
22 | -- Do not allow conversion into a shift register
23 | attribute shreg_extract : string;
24 | attribute shreg_extract of signal_tmp : signal is "no";
25 | -- Do not allow register balancing
26 | attribute register_balancing : string;
27 | attribute register_balancing of signal_tmp : signal is "no";
28 | --attribute register_balancing of signal_i : signal is "no";
29 | --attribute register_balancing of signal_o : signal is "no";
30 | begin
31 | FDPE_tmp_inst : FDPE
32 | generic map(
33 | INIT => '1') -- Initial value of register ('0' or '1')
34 | port map(
35 | Q => signal_tmp, -- Data output
36 | C => clock_target_i, -- Clock input
37 | CE => '1', -- Clock enable input
38 | PRE => preset_i, -- Asynchronous preset input
39 | D => signal_i -- Data input
40 | );
41 |
42 | FDPE_out_inst : FDPE
43 | generic map(
44 | INIT => '1') -- Initial value of register ('0' or '1')
45 | port map(
46 | Q => signal_o, -- Data output
47 | C => clock_target_i, -- Clock input
48 | CE => '1', -- Clock enable input
49 | PRE => preset_i, -- Asynchronous preset input
50 | D => signal_tmp -- Data input
51 | );
52 |
53 | end architecture;
--------------------------------------------------------------------------------
/xilinx/input_buffer.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Configurable input buffer forced into the IO block
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | library unisim;
12 | use unisim.vcomponents.all;
13 |
14 | entity input_buffer is
15 | generic(
16 | -- If TRUE, fixed_input_delay is inserted between the pad and the flip-flop
17 | HAS_DELAY : boolean := FALSE;
18 | IDELAY_VALUE : natural range 0 to 255 := 0
19 | );
20 | port(
21 | -- Connect to pad or IBUF
22 | pad_i : in std_ulogic;
23 | -- Connect to user logic
24 | buffer_o : out std_ulogic;
25 |
26 | -- Capture clock
27 | clock_i : in std_ulogic
28 | );
29 | end entity;
30 |
31 | architecture spartan_6 of input_buffer is
32 | signal delayed : std_ulogic := '0';
33 |
34 | -- Force putting input Flip-Flop into IOB so it doesn't end up in a normal logic tile
35 | -- which would ruin the timing.
36 | attribute iob : string;
37 | attribute iob of FDRE_inst : label is "FORCE";
38 | begin
39 | -- When delay activated: Instantiate IODELAY2 and then capture its output
40 | delay_gen : if HAS_DELAY = TRUE generate
41 | fixed_input_delay_inst : entity work.fixed_input_delay
42 | generic map(
43 | IDELAY_VALUE => IDELAY_VALUE
44 | )
45 | port map(
46 | pad_i => pad_i,
47 | delayed_o => delayed
48 | );
49 | end generate;
50 |
51 | -- When delay deactivated: Directly capture the signal
52 | no_delay_gen : if HAS_DELAY = FALSE generate
53 | delayed <= pad_i;
54 | end generate;
55 |
56 | FDRE_inst : FDRE
57 | generic map(
58 | INIT => '0') -- Initial value of register ('0' or '1')
59 | port map(
60 | Q => buffer_o, -- Data output
61 | C => clock_i, -- Clock input
62 | CE => '1', -- Clock enable input
63 | R => '0', -- Synchronous reset input
64 | D => delayed -- Data input
65 | );
66 |
67 | end architecture;
68 |
--------------------------------------------------------------------------------
/framing_common.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Common definitions pertaining to the structure of Ethernet frames
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 | use work.crc32.all;
14 | use work.utility.all;
15 |
16 | package framing_common is
17 |
18 | -- Preamble/SFD data in IEEE 802.3 clauses 4.2.5 and 4.2.6 is denoted LSB first, so they appear reversed here
19 | constant PREAMBLE_DATA : t_ethernet_data := "01010101";
20 | --constant PREAMBLE_LENGTH : positive := 7;
21 | constant START_FRAME_DELIMITER_DATA : t_ethernet_data := "11010101";
22 | constant PADDING_DATA : t_ethernet_data := "00000000";
23 |
24 | -- Data is counted from the end of the SFD to the beginning of the frame check sequence, exclusive
25 | constant MIN_FRAME_DATA_BYTES : positive := 46 + 2 + 6 + 6; -- bytes
26 | constant MAX_FRAME_DATA_BYTES : positive := 1500 + 2 + 6 + 6; -- bytes
27 |
28 | constant INTERPACKET_GAP_BYTES : positive := 12; -- bytes
29 |
30 | -- 11 bits are sufficient for 2048 bytes, Ethernet can only have 1518
31 | constant PACKET_LENGTH_BITS : positive := 11;
32 | constant MAX_PACKET_LENGTH : positive := (2 ** PACKET_LENGTH_BITS) - 1;
33 | subtype t_packet_length is unsigned((PACKET_LENGTH_BITS - 1) downto 0);
34 |
35 | -- Get a specific byte out of the given CRC32 suitable for transmission as Ethernet FCS
36 | function fcs_output_byte(fcs : t_crc32; byte : integer) return t_ethernet_data;
37 | end package;
38 |
39 | package body framing_common is
40 | function fcs_output_byte(fcs : t_crc32; byte : integer) return t_ethernet_data is
41 | variable reversed : t_crc32;
42 | variable out_byte : t_ethernet_data;
43 | variable inverted : t_ethernet_data;
44 | begin
45 | -- Reverse and invert the whole CRC32, then get the needed byte out
46 | reversed := reverse_vector(fcs);
47 | out_byte := reversed((((byte + 1) * 8) - 1) downto byte * 8);
48 | inverted := not out_byte;
49 | return inverted;
50 | end function;
51 | end package body;
52 |
--------------------------------------------------------------------------------
/crc.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Utility functions for CRC calculation
7 | -- Inspired by "Automatic Generation of Parallel CRC Circuits" by Michael Sprachmann
8 |
9 | library ieee;
10 | use ieee.std_logic_1164.all;
11 | use ieee.numeric_std.all;
12 |
13 | use work.utility.all;
14 |
15 | package crc is
16 | -- Update CRC old_crc by one bit (input) using a given polynomial
17 | function update_crc(old_crc : std_ulogic_vector; input : std_ulogic; polynomial : std_ulogic_vector) return std_ulogic_vector;
18 | -- Update CRC old_crc by an arbitrary number of bits (input) using a given polynomial
19 | function update_crc(old_crc : std_ulogic_vector; input : std_ulogic_vector; polynomial : std_ulogic_vector) return std_ulogic_vector;
20 | end package;
21 |
22 | package body crc is
23 | function update_crc(old_crc : std_ulogic_vector; input : std_ulogic; polynomial : std_ulogic_vector) return std_ulogic_vector is
24 | variable new_crc : std_ulogic_vector(old_crc'range);
25 | variable feedback : std_ulogic;
26 | begin
27 | assert not old_crc'ascending report "CRC argument must have descending range";
28 | -- Simple calculation with LFSR
29 | new_crc := old_crc;
30 | feedback := new_crc(new_crc'high) xor input;
31 |
32 | new_crc := std_ulogic_vector(unsigned(new_crc) sll 1);
33 | if (feedback = '1') then
34 | new_crc := new_crc xor polynomial(polynomial'high - 1 downto 0);
35 | end if;
36 |
37 | return new_crc;
38 | end function;
39 |
40 | -- Let the synthesizer figure out how to compute the checksum in parallel
41 | -- for any number of bits
42 | function update_crc(old_crc : std_ulogic_vector; input : std_ulogic_vector; polynomial : std_ulogic_vector) return std_ulogic_vector is
43 | variable new_crc : std_ulogic_vector(old_crc'range);
44 | begin
45 | assert not old_crc'ascending report "CRC argument must have descending range";
46 | assert not input'ascending report "Input argument must have descending range";
47 | new_crc := old_crc;
48 |
49 | -- Start with LSB
50 | for i in input'low to input'high loop
51 | new_crc := update_crc(new_crc, input(i), polynomial);
52 | end loop;
53 |
54 | return new_crc;
55 | end function;
56 |
57 | end package body;
58 |
--------------------------------------------------------------------------------
/xilinx/tx_fifo.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Combination of tx_fifo_adapter with a Xilinx core generator FIFO
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 |
14 | entity tx_fifo is
15 | port(
16 | clock_i : in std_ulogic;
17 |
18 | data_i : in t_ethernet_data;
19 | wr_en_i : in std_ulogic;
20 | full_o : out std_ulogic;
21 |
22 | -- Interface to framing layer
23 | mac_tx_reset_i : in std_ulogic;
24 | mac_tx_clock_i : in std_ulogic;
25 | mac_tx_enable_o : out std_ulogic;
26 | mac_tx_data_o : out t_ethernet_data;
27 | mac_tx_byte_sent_i : in std_ulogic;
28 | mac_tx_busy_i : in std_ulogic
29 | );
30 | end entity;
31 |
32 | architecture rtl of tx_fifo is
33 | signal rd_en : std_ulogic := '0';
34 | signal data_out : std_logic_vector(t_ethernet_data'range);
35 | signal empty : std_ulogic := '1';
36 | -- Intermediate signal needed for conversion between std_logic_vector and std_ulogic_vector
37 | signal read_count_lv : std_logic_vector(11 downto 0);
38 | signal read_count : unsigned(11 downto 0);
39 |
40 | begin
41 | -- Convert type
42 | read_count <= unsigned(read_count_lv);
43 |
44 | tx_data_fifo_inst : entity work.ethernet_mac_tx_fifo_xilinx
45 | port map(
46 | rst => mac_tx_reset_i,
47 | wr_clk => clock_i,
48 | rd_clk => mac_tx_clock_i,
49 | din => std_logic_vector(data_i),
50 | wr_en => wr_en_i,
51 | rd_en => rd_en,
52 | dout => data_out,
53 | full => full_o,
54 | empty => empty,
55 | rd_data_count => read_count_lv
56 | );
57 |
58 | tx_fifo_adapter_inst : entity work.tx_fifo_adapter
59 | port map(
60 | mac_tx_reset_i => mac_tx_reset_i,
61 | mac_tx_clock_i => mac_tx_clock_i,
62 | mac_tx_enable_o => mac_tx_enable_o,
63 | mac_tx_data_o => mac_tx_data_o,
64 | mac_tx_byte_sent_i => mac_tx_byte_sent_i,
65 | mac_tx_busy_i => mac_tx_busy_i,
66 | rd_en_o => rd_en,
67 | data_i => std_ulogic_vector(data_out),
68 | empty_i => empty,
69 | read_count_i => read_count
70 | );
71 |
72 | end architecture;
73 |
--------------------------------------------------------------------------------
/xilinx/test/test_wrapper_spartan6.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Convert std_logic_vector ports of test_instance_spartan6 (changed on compilation
7 | -- by the Xilinx toolchain) to std_ulogic_vector
8 |
9 | library ieee;
10 | use ieee.std_logic_1164.all;
11 | use ieee.numeric_std.all;
12 |
13 | use work.ethernet_types.all;
14 | use work.test_common.all;
15 |
16 | entity test_wrapper_spartan6 is
17 | port(
18 | clock_125_i : in std_ulogic;
19 | user_clock_i : in std_ulogic;
20 | reset_i : in std_ulogic;
21 |
22 | mii_tx_clk_i : in std_ulogic;
23 | mii_tx_er_o : out std_ulogic;
24 | mii_tx_en_o : out std_ulogic;
25 | mii_txd_o : out std_ulogic_vector(7 downto 0);
26 | mii_rx_clk_i : in std_ulogic;
27 | mii_rx_er_i : in std_ulogic;
28 | mii_rx_dv_i : in std_ulogic;
29 | mii_rxd_i : in std_ulogic_vector(7 downto 0);
30 |
31 | gmii_gtx_clk_o : out std_ulogic;
32 |
33 | rgmii_tx_ctl_o : out std_ulogic;
34 | rgmii_rx_ctl_i : in std_ulogic;
35 |
36 | speed_override_i : in t_ethernet_speed;
37 | test_mode_i : in t_test_mode
38 | );
39 | end entity;
40 |
41 | architecture structure of test_wrapper_spartan6 is
42 | signal mii_txd : std_logic_vector(7 downto 0);
43 | signal test_mode : std_logic_vector(1 downto 0);
44 | begin
45 | mii_txd_o <= std_ulogic_vector(mii_txd);
46 | -- Convert to std_logic_vector for the interface
47 | with test_mode_i select test_mode <=
48 | "01" when TEST_LOOPBACK,
49 | "10" when TEST_TX_PADDING,
50 | "00" when others;
51 |
52 | test_instance_inst : entity work.test_instance_spartan6
53 | port map(
54 | clock_125_i => clock_125_i,
55 | user_clock_i => user_clock_i,
56 | reset_i => reset_i,
57 | mii_tx_clk_i => mii_tx_clk_i,
58 | mii_tx_er_o => mii_tx_er_o,
59 | mii_tx_en_o => mii_tx_en_o,
60 | mii_txd_o => mii_txd,
61 | mii_rx_clk_i => mii_rx_clk_i,
62 | mii_rx_er_i => mii_rx_er_i,
63 | mii_rx_dv_i => mii_rx_dv_i,
64 | mii_rxd_i => std_logic_vector(mii_rxd_i),
65 | gmii_gtx_clk_o => gmii_gtx_clk_o,
66 | rgmii_tx_ctl_o => rgmii_tx_ctl_o,
67 | rgmii_rx_ctl_i => rgmii_rx_ctl_i,
68 | speed_override_i => std_logic_vector(speed_override_i),
69 | test_mode_i => test_mode
70 | );
71 |
72 | end architecture;
73 |
74 |
--------------------------------------------------------------------------------
/utility.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Utility functions
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | package utility is
12 | -- Return the reverse of the given vector
13 | function reverse_vector(vec : in std_ulogic_vector) return std_ulogic_vector;
14 | -- Return a vector with the bytes in opposite order but the content of the bytes unchanged (e.g. for big/little endian conversion)
15 | function reverse_bytes(vec : in std_ulogic_vector) return std_ulogic_vector;
16 | -- Extract a byte out of a vector
17 | function extract_byte(vec : in std_ulogic_vector; byteno : in natural) return std_ulogic_vector;
18 | -- Set a byte in a vector
19 | procedure set_byte(vec : inout std_ulogic_vector; byteno : in natural; value : in std_ulogic_vector(7 downto 0));
20 | end package;
21 |
22 | package body utility is
23 | function reverse_vector(vec : in std_ulogic_vector) return std_ulogic_vector is
24 | variable result : std_ulogic_vector(vec'range);
25 | alias rev_vec : std_ulogic_vector(vec'reverse_range) is vec;
26 | begin
27 | for i in rev_vec'range loop
28 | result(i) := rev_vec(i);
29 | end loop;
30 | return result;
31 | end function;
32 |
33 | function reverse_bytes(vec : in std_ulogic_vector) return std_ulogic_vector is
34 | variable result : std_ulogic_vector(vec'range);
35 | begin
36 | assert vec'length mod 8 = 0 report "Vector length must be a multiple of 8 for byte reversal" severity failure;
37 | assert vec'low = 0 report "Vector must start at 0 for byte reversal" severity failure;
38 | for byte in 0 to vec'high / 8 loop
39 | set_byte(result, vec'high / 8 - byte, extract_byte(vec, byte));
40 | end loop;
41 | return result;
42 | end function;
43 |
44 | function extract_byte(vec : in std_ulogic_vector; byteno : in natural) return std_ulogic_vector is
45 | begin
46 | -- Support both vector directions
47 | if vec'ascending then
48 | return vec(byteno * 8 to (byteno + 1) * 8 - 1);
49 | else
50 | return vec((byteno + 1) * 8 - 1 downto byteno * 8);
51 | end if;
52 | end function;
53 |
54 | procedure set_byte(vec : inout std_ulogic_vector; byteno : in natural; value : in std_ulogic_vector(7 downto 0)) is
55 | begin
56 | -- Support both vector directions
57 | if vec'ascending then
58 | vec(byteno * 8 to (byteno + 1) * 8 - 1) := value;
59 | else
60 | vec((byteno + 1) * 8 - 1 downto byteno * 8) := value;
61 | end if;
62 | end procedure;
63 | end package body;
64 |
--------------------------------------------------------------------------------
/xilinx/fixed_input_delay.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Apply a fixed delay to an input pin using IODELAY2
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | library unisim;
12 | use unisim.vcomponents.all;
13 |
14 | entity fixed_input_delay is
15 | generic(
16 | IDELAY_VALUE : natural range 0 to 255 := 0
17 | );
18 | port(
19 | pad_i : in std_ulogic;
20 | delayed_o : out std_ulogic
21 | );
22 | end entity;
23 |
24 | architecture spartan_6 of fixed_input_delay is
25 | begin
26 | mii_rx_dv_IODELAY2_inst : IODELAY2
27 | generic map(
28 | COUNTER_WRAPAROUND => "WRAPAROUND", -- "STAY_AT_LIMIT" or "WRAPAROUND"
29 | DATA_RATE => "SDR", -- "SDR" or "DDR"
30 | DELAY_SRC => "IDATAIN", -- "IO", "ODATAIN" or "IDATAIN"
31 | IDELAY2_VALUE => 0, -- Delay value when IDELAY_MODE="PCI" (0-255)
32 | IDELAY_MODE => "NORMAL", -- "NORMAL" or "PCI"
33 | IDELAY_TYPE => "FIXED", -- "FIXED", "DEFAULT", "VARIABLE_FROM_ZERO", "VARIABLE_FROM_HALF_MAX"
34 | -- or "DIFF_PHASE_DETECTOR"
35 | IDELAY_VALUE => IDELAY_VALUE, -- Amount of taps for fixed input delay (0-255)
36 | ODELAY_VALUE => 0, -- Amount of taps fixed output delay (0-255)
37 | SERDES_MODE => "NONE", -- "NONE", "MASTER" or "SLAVE"
38 | SIM_TAPDELAY_VALUE => 75 -- Per tap delay used for simulation in ps
39 | )
40 | port map(
41 | BUSY => open, -- 1-bit output: Busy output after CAL
42 | DATAOUT => delayed_o, -- 1-bit output: Delayed data output to ISERDES/input register
43 | DATAOUT2 => open, -- 1-bit output: Delayed data output to general FPGA fabric
44 | DOUT => open, -- 1-bit output: Delayed data output
45 | TOUT => open, -- 1-bit output: Delayed 3-state output
46 | CAL => '0', -- 1-bit input: Initiate calibration input
47 | CE => '0', -- 1-bit input: Enable INC input
48 | CLK => '0', -- 1-bit input: Clock input
49 | IDATAIN => pad_i, -- 1-bit input: Data input (connect to top-level port or I/O buffer)
50 | INC => '0', -- 1-bit input: Increment / decrement input
51 | IOCLK0 => '0', -- 1-bit input: Input from the I/O clock network
52 | IOCLK1 => '0', -- 1-bit input: Input from the I/O clock network
53 | ODATAIN => '0', -- 1-bit input: Output data input from output register or OSERDES2.
54 | RST => '0', -- 1-bit input: Reset to zero or 1/2 of total delay period
55 | T => '1' -- 1-bit input: 3-state input signal
56 | );
57 |
58 | end architecture;
59 |
60 |
--------------------------------------------------------------------------------
/xilinx/test/test_instance_spartan6.ucf:
--------------------------------------------------------------------------------
1 | # Timing
2 | NET "clock_125_i" TNM_NET = "clock_125_i";
3 | TIMESPEC TS_clock_125_in = PERIOD "clock_125_i" 125 MHz HIGH 50% INPUT_JITTER 80 ps;
4 |
5 | NET "mii_tx_er_o" TNM = "gmii_out";
6 | NET "mii_txd_o[0]" TNM = "gmii_out";
7 | NET "mii_txd_o[1]" TNM = "gmii_out";
8 | NET "mii_txd_o[2]" TNM = "gmii_out";
9 | NET "mii_txd_o[3]" TNM = "gmii_out";
10 | NET "mii_txd_o[4]" TNM = "gmii_out";
11 | NET "mii_txd_o[5]" TNM = "gmii_out";
12 | NET "mii_txd_o[6]" TNM = "gmii_out";
13 | NET "mii_txd_o[7]" TNM = "gmii_out";
14 | NET "mii_tx_en_o" TNM = "gmii_out";
15 | NET "gmii_gtx_clk_o" TNM = "gmii_out";
16 | NET "mii_rx_er_i" TNM = "mii_in";
17 | NET "mii_rx_dv_i" TNM = "mii_in";
18 | NET "mii_rxd_i[7]" TNM = "mii_in";
19 | NET "mii_rxd_i[6]" TNM = "mii_in";
20 | NET "mii_rxd_i[5]" TNM = "mii_in";
21 | NET "mii_rxd_i[4]" TNM = "mii_in";
22 | NET "mii_rxd_i[3]" TNM = "mii_in";
23 | NET "mii_rxd_i[1]" TNM = "mii_in";
24 | NET "mii_rxd_i[0]" TNM = "mii_in";
25 | NET "mii_rxd_i[2]" TNM = "mii_in";
26 | # 25 ns constraint is for MII only
27 | # REFERENCE_PIN constraint is for GMII bus skew analysis
28 | TIMEGRP "gmii_out" OFFSET = OUT 25 ns AFTER "mii_tx_clk_i" REFERENCE_PIN "gmii_gtx_clk_o";
29 |
30 | NET "mii_tx_clk_i" TNM_NET = mii_tx_clk_i;
31 | TIMESPEC TS_mii_tx_clk_i = PERIOD "mii_tx_clk_i" 40 ns HIGH 35%;
32 | NET "mii_rx_clk_i" TNM_NET = mii_rx_clk_i;
33 | TIMESPEC TS_mii_rx_clk_i = PERIOD "mii_rx_clk_i" 125 MHz HIGH 33%;
34 | #TIMEGRP "mii_in" OFFSET = IN 2.5 ns VALID 3 ns BEFORE "mii_rx_clk_i" RISING;
35 | TIMEGRP "mii_in" OFFSET = IN 2 ns VALID 2 ns BEFORE "mii_rx_clk_i" RISING;
36 |
37 | # Pins
38 | NET "clock_125_i" LOC = AA12 | IOSTANDARD = LVCMOS33;
39 | #NET "mdc_o" LOC = AA2 | IOSTANDARD = LVCMOS33;
40 | #NET "mdio_io" LOC = AB3 | IOSTANDARD = LVCMOS33;
41 |
42 | NET "gmii_gtx_clk_o" LOC = R11 | IOSTANDARD = LVCMOS33;
43 | NET "mii_tx_en_o" LOC = AB16 | IOSTANDARD = LVCMOS33;
44 | NET "mii_tx_er_o" LOC = AB18 | IOSTANDARD = LVCMOS33;
45 |
46 | NET "mii_rxd_i[0]" LOC = Y3 | IOSTANDARD = LVCMOS33;
47 | NET "mii_rxd_i[1]" LOC = W8 | IOSTANDARD = LVCMOS33;
48 | NET "mii_rxd_i[2]" LOC = W4 | IOSTANDARD = LVCMOS33;
49 | NET "mii_rxd_i[3]" LOC = U9 | IOSTANDARD = LVCMOS33;
50 | NET "mii_rxd_i[4]" LOC = V7 | IOSTANDARD = LVCMOS33;
51 | NET "mii_rxd_i[5]" LOC = V5 | IOSTANDARD = LVCMOS33;
52 | NET "mii_rxd_i[6]" LOC = W9 | IOSTANDARD = LVCMOS33;
53 | NET "mii_rxd_i[7]" LOC = U6 | IOSTANDARD = LVCMOS33;
54 | NET "mii_txd_o[0]" LOC = AA18 | IOSTANDARD = LVCMOS33;
55 | NET "mii_txd_o[1]" LOC = AB14 | IOSTANDARD = LVCMOS33;
56 | NET "mii_txd_o[2]" LOC = AA16 | IOSTANDARD = LVCMOS33;
57 | NET "mii_txd_o[3]" LOC = W14 | IOSTANDARD = LVCMOS33;
58 | NET "mii_txd_o[4]" LOC = T16 | IOSTANDARD = LVCMOS33;
59 | NET "mii_txd_o[5]" LOC = Y14 | IOSTANDARD = LVCMOS33;
60 | NET "mii_txd_o[6]" LOC = V15 | IOSTANDARD = LVCMOS33;
61 | NET "mii_txd_o[7]" LOC = AA14 | IOSTANDARD = LVCMOS33;
62 | NET "mii_rx_clk_i" LOC = Y11 | IOSTANDARD = LVCMOS33;
63 | NET "mii_tx_clk_i" LOC = W12 | IOSTANDARD = LVCMOS33;
64 | NET "mii_rx_er_i" LOC = Y8 | IOSTANDARD = LVCMOS33;
65 | NET "mii_rx_dv_i" LOC = Y4 | IOSTANDARD = LVCMOS33;
66 |
--------------------------------------------------------------------------------
/xilinx/ipcore_dir/ethernet_mac_tx_fifo_xilinx.xise:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/crc32_tb.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Simple testbench for playing around with the CRC calculation code
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.crc32.all;
13 | use work.utility.all;
14 |
15 | entity crc32_tb is
16 | end entity;
17 |
18 | architecture behavioral of crc32_tb is
19 |
20 | -- "Known good" function for comparison
21 | -- polynomial: (0 1 2 4 5 7 8 10 11 12 16 22 23 26 32)
22 | -- data width: 8
23 | -- convention: the first serial bit is D[0]
24 | function NEXTCRC32_D8(DATA : std_ulogic_vector(7 downto 0);
25 | CRC : std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
26 | variable D : std_ulogic_vector(7 downto 0);
27 | variable C : std_ulogic_vector(31 downto 0);
28 | variable NEWCRC : std_ulogic_vector(31 downto 0);
29 |
30 | begin
31 | D := DATA;
32 | C := CRC;
33 | NewCRC(0) := C(24) xor C(30) xor D(1) xor D(7);
34 | NewCRC(1) := C(25) xor C(31) xor D(0) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
35 | NewCRC(2) := C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
36 | NewCRC(3) := C(27) xor D(4) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
37 | NewCRC(4) := C(28) xor D(3) xor C(27) xor D(4) xor C(26) xor D(5) xor C(24) xor C(30) xor D(1) xor D(7);
38 | NewCRC(5) := C(29) xor D(2) xor C(28) xor D(3) xor C(27) xor D(4) xor C(25) xor C(31) xor D(0) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
39 | NewCRC(6) := C(30) xor D(1) xor C(29) xor D(2) xor C(28) xor D(3) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
40 | NewCRC(7) := C(31) xor D(0) xor C(29) xor D(2) xor C(27) xor D(4) xor C(26) xor D(5) xor C(24) xor D(7);
41 | NewCRC(8) := C(0) xor C(28) xor D(3) xor C(27) xor D(4) xor C(25) xor D(6) xor C(24) xor D(7);
42 | NewCRC(9) := C(1) xor C(29) xor D(2) xor C(28) xor D(3) xor C(26) xor D(5) xor C(25) xor D(6);
43 | NewCRC(10) := C(2) xor C(29) xor D(2) xor C(27) xor D(4) xor C(26) xor D(5) xor C(24) xor D(7);
44 | NewCRC(11) := C(3) xor C(28) xor D(3) xor C(27) xor D(4) xor C(25) xor D(6) xor C(24) xor D(7);
45 | NewCRC(12) := C(4) xor C(29) xor D(2) xor C(28) xor D(3) xor C(26) xor D(5) xor C(25) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
46 | NewCRC(13) := C(5) xor C(30) xor D(1) xor C(29) xor D(2) xor C(27) xor D(4) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
47 | NewCRC(14) := C(6) xor C(31) xor D(0) xor C(30) xor D(1) xor C(28) xor D(3) xor C(27) xor D(4) xor C(26) xor D(5);
48 | NewCRC(15) := C(7) xor C(31) xor D(0) xor C(29) xor D(2) xor C(28) xor D(3) xor C(27) xor D(4);
49 | NewCRC(16) := C(8) xor C(29) xor D(2) xor C(28) xor D(3) xor C(24) xor D(7);
50 | NewCRC(17) := C(9) xor C(30) xor D(1) xor C(29) xor D(2) xor C(25) xor D(6);
51 | NewCRC(18) := C(10) xor C(31) xor D(0) xor C(30) xor D(1) xor C(26) xor D(5);
52 | NewCRC(19) := C(11) xor C(31) xor D(0) xor C(27) xor D(4);
53 | NewCRC(20) := C(12) xor C(28) xor D(3);
54 | NewCRC(21) := C(13) xor C(29) xor D(2);
55 | NewCRC(22) := C(14) xor C(24) xor D(7);
56 | NewCRC(23) := C(15) xor C(25) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
57 | NewCRC(24) := C(16) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
58 | NewCRC(25) := C(17) xor C(27) xor D(4) xor C(26) xor D(5);
59 | NewCRC(26) := C(18) xor C(28) xor D(3) xor C(27) xor D(4) xor C(24) xor C(30) xor D(1) xor D(7);
60 | NewCRC(27) := C(19) xor C(29) xor D(2) xor C(28) xor D(3) xor C(25) xor C(31) xor D(0) xor D(6);
61 | NewCRC(28) := C(20) xor C(30) xor D(1) xor C(29) xor D(2) xor C(26) xor D(5);
62 | NewCRC(29) := C(21) xor C(31) xor D(0) xor C(30) xor D(1) xor C(27) xor D(4);
63 | NewCRC(30) := C(22) xor C(31) xor D(0) xor C(28) xor D(3);
64 | NewCRC(31) := C(23) xor C(29) xor D(2);
65 |
66 | return NEWCRC;
67 | end NEXTCRC32_D8;
68 |
69 | -- Signals as isim cannot trace variables
70 | signal crc : t_crc32;
71 | signal comparison_crc : t_crc32;
72 | signal data : std_ulogic_vector(7 downto 0);
73 |
74 | constant WAIT_PERIOD : time := 40 ns;
75 | begin
76 | test_crc32 : process
77 | variable saved_crc : t_crc32;
78 | begin
79 | crc <= (others => '1');
80 | comparison_crc <= (others => '1');
81 | data <= (others => '0');
82 | wait for WAIT_PERIOD;
83 |
84 | for cnt in 0 to 10 loop
85 | crc <= update_crc32(crc, data);
86 | comparison_crc <= NEXTCRC32_D8(data, crc);
87 | if cnt >= 7 then
88 | data <= (others => '0');
89 | else
90 | data <= std_ulogic_vector(to_unsigned(cnt + 1, 8));
91 | end if;
92 | wait for WAIT_PERIOD;
93 | if crc /= comparison_crc then
94 | report "CRC mismatch" severity note;
95 | end if;
96 | end loop;
97 |
98 | saved_crc := not reverse_vector(crc);
99 |
100 | wait for 100 ns;
101 |
102 | for j in 0 to 3 loop
103 | crc <= update_crc32(crc, saved_crc(((j + 1) * 8) - 1 downto j * 8));
104 | comparison_crc <= NEXTCRC32_D8(saved_crc(((j + 1) * 8) - 1 downto j * 8), crc);
105 | wait for WAIT_PERIOD;
106 | end loop;
107 |
108 | --crc <= reverse_vector(crc);
109 |
110 | wait for WAIT_PERIOD;
111 |
112 | if crc /= X"C704dd7B" then
113 | report "Final CRC wrong" severity note;
114 | end if;
115 |
116 | wait;
117 | end process;
118 |
119 | end architecture;
120 |
121 |
--------------------------------------------------------------------------------
/tx_fifo_adapter.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Read packets from a TX FIFO and send them to framing
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 |
14 | entity tx_fifo_adapter is
15 | port(
16 | -- Interface to framing layer
17 | mac_tx_reset_i : in std_ulogic;
18 | mac_tx_clock_i : in std_ulogic;
19 | mac_tx_enable_o : out std_ulogic;
20 | mac_tx_data_o : out t_ethernet_data;
21 | mac_tx_byte_sent_i : in std_ulogic;
22 | mac_tx_busy_i : in std_ulogic;
23 |
24 | -- FIFO interface
25 | rd_en_o : out std_ulogic;
26 | data_i : in t_ethernet_data;
27 | empty_i : in std_ulogic;
28 | read_count_i : in unsigned
29 | );
30 | end entity;
31 |
32 | architecture rtl of tx_fifo_adapter is
33 | type t_state is (
34 | READ_SIZE_HIGH,
35 | WAIT_READ_SIZE_LOW,
36 | READ_SIZE_LOW,
37 | WAIT_DATA_COUNT1,
38 | WAIT_DATA_COUNT2,
39 | WAIT_PACKET,
40 | WAIT_DATA_READ,
41 | READ_DATA,
42 | SEND_DATA
43 | );
44 |
45 | constant TX_PACKET_SIZE_BITS : positive := 12;
46 | constant TX_MAX_PACKET_SIZE : positive := ((2 ** TX_PACKET_SIZE_BITS) - 1);
47 |
48 | signal state : t_state := READ_SIZE_HIGH;
49 | signal remaining_packet_size : unsigned(TX_PACKET_SIZE_BITS - 1 downto 0);
50 | signal next_data : t_ethernet_data := (others => '0');
51 | signal rd_en : std_ulogic := '0';
52 |
53 | begin
54 | rd_en_o <= rd_en;
55 |
56 | send_proc : process(mac_tx_reset_i, mac_tx_clock_i)
57 | begin
58 | if mac_tx_reset_i = '1' then
59 | state <= READ_SIZE_HIGH;
60 | rd_en <= '0';
61 | mac_tx_enable_o <= '0';
62 | elsif rising_edge(mac_tx_clock_i) then
63 | rd_en <= '0';
64 | mac_tx_enable_o <= '0';
65 |
66 | case state is
67 | when READ_SIZE_HIGH =>
68 | -- Wait for FIFO nonempty
69 | if empty_i = '0' then
70 | -- Read packet size high byte
71 | remaining_packet_size(TX_PACKET_SIZE_BITS - 1 downto 8) <= unsigned(data_i(TX_PACKET_SIZE_BITS - 1 - 8 downto 0));
72 | -- Move FIFO to next byte
73 | rd_en <= '1';
74 | state <= WAIT_READ_SIZE_LOW;
75 | end if;
76 | when WAIT_READ_SIZE_LOW =>
77 | -- Wait for EMPTY flag and data to update
78 | state <= READ_SIZE_LOW;
79 | when READ_SIZE_LOW =>
80 | -- Wait for FIFO nonempty
81 | if empty_i = '0' then
82 | -- Read packet size low byte
83 | remaining_packet_size(7 downto 0) <= unsigned(data_i);
84 | -- Move FIFO to next byte
85 | rd_en <= '1';
86 | state <= WAIT_DATA_COUNT1;
87 | end if;
88 | when WAIT_DATA_COUNT1 =>
89 | -- The FIFO read data count can over-report for up to two clock cycles according
90 | -- to XILINX documentation. Make sure this doesn't happen here.
91 | state <= WAIT_DATA_COUNT2;
92 | when WAIT_DATA_COUNT2 =>
93 | state <= WAIT_PACKET;
94 | when WAIT_PACKET =>
95 | -- Check for obviously wrong frame size to avoid lock-up
96 | if remaining_packet_size = 0 then
97 | -- Try again
98 | state <= READ_SIZE_HIGH;
99 | else
100 | -- Wait for all data available and TX idle
101 | if read_count_i >= remaining_packet_size and mac_tx_busy_i = '0' then
102 | -- Remember the first byte
103 | mac_tx_data_o <= data_i;
104 | -- Start transmission already, delay through framing is long enough to not miss the first tx_byte_sent
105 | mac_tx_enable_o <= '1';
106 | -- Move FIFO on to the second byte
107 | rd_en <= '1';
108 | if remaining_packet_size = 1 then
109 | -- No data to prefetch
110 | state <= SEND_DATA;
111 | else
112 | -- Prefetch data
113 | state <= WAIT_DATA_READ;
114 | end if;
115 | end if;
116 | end if;
117 | when WAIT_DATA_READ =>
118 | -- Third byte
119 | rd_en <= '1';
120 | state <= READ_DATA;
121 | mac_tx_enable_o <= '1';
122 | when READ_DATA =>
123 | next_data <= data_i;
124 | state <= SEND_DATA;
125 | mac_tx_enable_o <= '1';
126 | when SEND_DATA =>
127 | mac_tx_enable_o <= '1';
128 | if mac_tx_byte_sent_i = '1' then
129 | if remaining_packet_size = 1 then
130 | -- This was the last byte
131 | mac_tx_enable_o <= '0';
132 | state <= READ_SIZE_HIGH;
133 | else
134 | if rd_en = '1' then
135 | -- The buffer is exhausted if we've supplied its value
136 | -- in the previous clock cycle, now supply data directly from the FIFO
137 | mac_tx_data_o <= data_i;
138 | else
139 | -- Pass the buffered byte on
140 | mac_tx_data_o <= next_data;
141 | next_data <= data_i;
142 | end if;
143 | -- Get one byte out of FIFO
144 | if remaining_packet_size >= 3 then
145 | rd_en <= '1';
146 | end if;
147 | remaining_packet_size <= remaining_packet_size - 1;
148 | end if;
149 | end if;
150 | end case;
151 | end if;
152 | end process;
153 |
154 | end architecture;
155 |
--------------------------------------------------------------------------------
/xilinx/test/test_instance_spartan6.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Instantiate test_mirror and add Spartan 6-specific clock buffering
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 | use work.test_common.all;
14 |
15 | library unisim;
16 | use unisim.vcomponents.all;
17 |
18 | entity test_instance_spartan6 is
19 | port(
20 | clock_125_i : in std_ulogic;
21 | user_clock_i : in std_ulogic;
22 | reset_i : in std_ulogic;
23 |
24 | mii_tx_clk_i : in std_ulogic;
25 | mii_tx_er_o : out std_ulogic;
26 | mii_tx_en_o : out std_ulogic;
27 | mii_txd_o : out std_ulogic_vector(7 downto 0);
28 | mii_rx_clk_i : in std_ulogic;
29 | mii_rx_er_i : in std_ulogic;
30 | mii_rx_dv_i : in std_ulogic;
31 | mii_rxd_i : in std_ulogic_vector(7 downto 0);
32 |
33 | gmii_gtx_clk_o : out std_ulogic;
34 |
35 | rgmii_tx_ctl_o : out std_ulogic;
36 | rgmii_rx_ctl_i : in std_ulogic;
37 |
38 | speed_override_i : in t_ethernet_speed;
39 |
40 | test_mode_i : in std_ulogic_vector(1 downto 0)
41 | );
42 | end entity;
43 |
44 | architecture rtl of test_instance_spartan6 is
45 | signal clock_125 : std_ulogic;
46 | signal clock_125_unbuffered : std_ulogic;
47 | signal locked : std_ulogic;
48 | signal reset : std_ulogic;
49 | signal test_mode : t_test_mode;
50 | begin
51 | reset <= reset_i or (not locked);
52 | with test_mode_i select test_mode <=
53 | TEST_LOOPBACK when "01",
54 | TEST_TX_PADDING when "10",
55 | TEST_NOTHING when others;
56 |
57 | clock_125_BUFG_inst : BUFG
58 | port map(
59 | O => clock_125, -- 1-bit output: Clock buffer output
60 | I => clock_125_unbuffered -- 1-bit input: Clock buffer input
61 | );
62 |
63 | DCM_SP_inst : DCM_SP
64 | generic map(
65 | CLKDV_DIVIDE => 5.0, -- CLKDV divide value
66 | -- (1.5,2,2.5,3,3.5,4,4.5,5,5.5,6,6.5,7,7.5,8,9,10,11,12,13,14,15,16).
67 | CLKFX_DIVIDE => 5, -- Divide value on CLKFX outputs - D - (1-32)
68 | CLKFX_MULTIPLY => 2, -- Multiply value on CLKFX outputs - M - (2-32)
69 | CLKIN_DIVIDE_BY_2 => FALSE, -- CLKIN divide by two (TRUE/FALSE)
70 | CLKIN_PERIOD => 8.0, -- Input clock period specified in nS
71 | CLKOUT_PHASE_SHIFT => "NONE", -- Output phase shift (NONE, FIXED, VARIABLE)
72 | CLK_FEEDBACK => "1X", -- Feedback source (NONE, 1X, 2X)
73 | DESKEW_ADJUST => "SYSTEM_SYNCHRONOUS", -- SYSTEM_SYNCHRNOUS or SOURCE_SYNCHRONOUS
74 | DFS_FREQUENCY_MODE => "LOW", -- Unsupported - Do not change value
75 | DLL_FREQUENCY_MODE => "LOW", -- Unsupported - Do not change value
76 | DSS_MODE => "NONE", -- Unsupported - Do not change value
77 | DUTY_CYCLE_CORRECTION => TRUE, -- Unsupported - Do not change value
78 | FACTORY_JF => X"c080", -- Unsupported - Do not change value
79 | PHASE_SHIFT => 0, -- Amount of fixed phase shift (-255 to 255)
80 | STARTUP_WAIT => FALSE -- Delay config DONE until DCM_SP LOCKED (TRUE/FALSE)
81 | )
82 | port map(
83 | CLK0 => clock_125_unbuffered, -- 1-bit output: 0 degree clock output
84 | CLK180 => open, -- 1-bit output: 180 degree clock output
85 | CLK270 => open, -- 1-bit output: 270 degree clock output
86 | CLK2X => open, -- 1-bit output: 2X clock frequency clock output
87 | CLK2X180 => open, -- 1-bit output: 2X clock frequency, 180 degree clock output
88 | CLK90 => open, -- 1-bit output: 90 degree clock output
89 | CLKDV => open, -- 1-bit output: Divided clock output
90 | CLKFX => open, -- 1-bit output: Digital Frequency Synthesizer output (DFS)
91 | CLKFX180 => open, -- 1-bit output: 180 degree CLKFX output
92 | LOCKED => locked, -- 1-bit output: DCM_SP Lock Output
93 | PSDONE => open, -- 1-bit output: Phase shift done output
94 | STATUS => open, -- 8-bit output: DCM_SP status output
95 | CLKFB => clock_125_unbuffered, -- 1-bit input: Clock feedback input
96 | CLKIN => clock_125_i, -- 1-bit input: Clock input
97 | DSSEN => '0', -- 1-bit input: Unsupported, specify to GND.
98 | PSCLK => '0', -- 1-bit input: Phase shift clock input
99 | PSEN => '0', -- 1-bit input: Phase shift enable
100 | PSINCDEC => '0', -- 1-bit input: Phase shift increment/decrement input
101 | RST => '0' -- 1-bit input: Active high reset input
102 | );
103 |
104 | test_instance_inst : entity work.test_instance
105 | port map(
106 | clock_125_i => clock_125_unbuffered,
107 | user_clock_i => clock_125,
108 | reset_i => reset,
109 | mii_tx_clk_i => mii_tx_clk_i,
110 | mii_tx_er_o => mii_tx_er_o,
111 | mii_tx_en_o => mii_tx_en_o,
112 | mii_txd_o => mii_txd_o,
113 | mii_rx_clk_i => mii_rx_clk_i,
114 | mii_rx_er_i => mii_rx_er_i,
115 | mii_rx_dv_i => mii_rx_dv_i,
116 | mii_rxd_i => mii_rxd_i,
117 | gmii_gtx_clk_o => gmii_gtx_clk_o,
118 | rgmii_tx_ctl_o => rgmii_tx_ctl_o,
119 | rgmii_rx_ctl_i => rgmii_rx_ctl_i,
120 | speed_override_i => speed_override_i,
121 | test_mode_i => test_mode
122 | );
123 |
124 | end architecture;
125 |
126 |
--------------------------------------------------------------------------------
/test/test_instance.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- ethernet_with_fifos wrapped with an application that just loops all incoming packets back
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.utility.all;
13 | use work.ethernet_types.all;
14 | use work.framing_common.all;
15 | use work.test_common.all;
16 |
17 | entity test_instance is
18 | port(
19 | clock_125_i : in std_ulogic;
20 | -- Clock used for FIFOs and MIIM
21 | user_clock_i : in std_ulogic;
22 | reset_i : in std_ulogic;
23 |
24 | mii_tx_clk_i : in std_ulogic;
25 | mii_tx_er_o : out std_ulogic;
26 | mii_tx_en_o : out std_ulogic;
27 | mii_txd_o : out std_ulogic_vector(7 downto 0);
28 | mii_rx_clk_i : in std_ulogic;
29 | mii_rx_er_i : in std_ulogic;
30 | mii_rx_dv_i : in std_ulogic;
31 | mii_rxd_i : in std_ulogic_vector(7 downto 0);
32 |
33 | gmii_gtx_clk_o : out std_ulogic;
34 |
35 | rgmii_tx_ctl_o : out std_ulogic;
36 | rgmii_rx_ctl_i : in std_ulogic;
37 |
38 | speed_override_i : in t_ethernet_speed;
39 |
40 | -- Test type
41 | test_mode_i : in t_test_mode
42 | );
43 | end entity;
44 |
45 | architecture rtl of test_instance is
46 | signal rx_empty : std_ulogic;
47 | signal tx_full : std_ulogic;
48 |
49 | signal mac_address : t_mac_address;
50 |
51 | -- Connected to ethernet_with_fifos via mux on test_mode_i
52 | signal rx_rd_en : std_ulogic := '0';
53 | signal rx_data : t_ethernet_data;
54 | signal tx_data : t_ethernet_data;
55 | signal tx_wr_en : std_ulogic := '0';
56 |
57 | -- Signals in TEST_LOOPBACK
58 | signal rx_rd_en_loopback : std_ulogic := '0';
59 | signal tx_data_loopback : t_ethernet_data;
60 | signal tx_wr_en_loopback : std_ulogic := '0';
61 | -- Signals in other test modes
62 | signal rx_rd_en_sync : std_ulogic := '0';
63 | signal tx_data_sync : t_ethernet_data;
64 | signal tx_wr_en_sync : std_ulogic := '0';
65 |
66 | type t_tx_padding_state is (
67 | TX_PAD_SIZE_HI,
68 | TX_PAD_SIZE_LO,
69 | TX_PAD_DATA,
70 | TX_PAD_DONE
71 | );
72 | signal tx_padding_state : t_tx_padding_state := TX_PAD_SIZE_HI;
73 | signal tx_packet_byte : integer range 0 to MIN_FRAME_DATA_BYTES;
74 | signal tx_packet_size : integer range 0 to MIN_FRAME_DATA_BYTES;
75 | signal tx_reset : std_ulogic;
76 |
77 | begin
78 | mac_address <= TEST_MAC_ADDRESS;
79 |
80 | -- Process for mirroring packets from the RX FIFO to the TX FIFO
81 | -- Asynchronous to avoid complicated buffering on full/empty conditions
82 | tx_data_loopback <= rx_data;
83 | tx_wr_en_loopback <= not rx_empty and not tx_full;
84 | rx_rd_en_loopback <= not rx_empty and not tx_full;
85 |
86 | -- Mux for FIFO user signals
87 | with test_mode_i select rx_rd_en <=
88 | rx_rd_en_loopback when TEST_LOOPBACK,
89 | rx_rd_en_sync when TEST_TX_PADDING,
90 | '0' when others;
91 | with test_mode_i select tx_wr_en <=
92 | tx_wr_en_loopback when TEST_LOOPBACK,
93 | tx_wr_en_sync when TEST_TX_PADDING,
94 | '0' when others;
95 | with test_mode_i select tx_data <=
96 | tx_data_loopback when TEST_LOOPBACK,
97 | tx_data_sync when others;
98 |
99 | ethernet_with_fifos_inst : entity work.ethernet_with_fifos
100 | generic map(
101 | MIIM_DISABLE => TRUE
102 | )
103 | port map(
104 | clock_125_i => clock_125_i,
105 | reset_i => reset_i,
106 | mac_address_i => mac_address,
107 | mii_tx_clk_i => mii_tx_clk_i,
108 | mii_tx_er_o => mii_tx_er_o,
109 | mii_tx_en_o => mii_tx_en_o,
110 | mii_txd_o => mii_txd_o,
111 | mii_rx_clk_i => mii_rx_clk_i,
112 | mii_rx_er_i => mii_rx_er_i,
113 | mii_rx_dv_i => mii_rx_dv_i,
114 | mii_rxd_i => mii_rxd_i,
115 | gmii_gtx_clk_o => gmii_gtx_clk_o,
116 | rgmii_tx_ctl_o => rgmii_tx_ctl_o,
117 | rgmii_rx_ctl_i => rgmii_rx_ctl_i,
118 | miim_clock_i => user_clock_i,
119 | speed_override_i => speed_override_i,
120 | rx_clock_i => user_clock_i,
121 | rx_empty_o => rx_empty,
122 | rx_rd_en_i => rx_rd_en,
123 | rx_data_o => rx_data,
124 | tx_clock_i => user_clock_i,
125 | tx_data_i => tx_data,
126 | tx_wr_en_i => tx_wr_en,
127 | tx_full_o => tx_full,
128 | tx_reset_o => tx_reset
129 | );
130 |
131 | -- Process for synchronous test modes
132 | -- Currently only TEST_TX_PADDING
133 | test_proc : process(user_clock_i)
134 | begin
135 | if rising_edge(user_clock_i) then
136 | rx_rd_en_sync <= '0';
137 |
138 | if test_mode_i = TEST_TX_PADDING and tx_reset = '0' then
139 | tx_wr_en_sync <= '1';
140 | case tx_padding_state is
141 | when TX_PAD_SIZE_HI =>
142 | tx_data_sync <= (others => '0');
143 | tx_padding_state <= TX_PAD_SIZE_LO;
144 | when TX_PAD_SIZE_LO =>
145 | tx_data_sync <= t_ethernet_data(to_unsigned(tx_packet_size, 8));
146 | tx_padding_state <= TX_PAD_DATA;
147 | tx_packet_byte <= 0;
148 | when TX_PAD_DATA =>
149 | tx_data_sync <= t_ethernet_data(to_unsigned(tx_packet_byte + 1, 8));
150 | if tx_packet_byte = tx_packet_size - 1 then
151 | if tx_packet_size = MIN_FRAME_DATA_BYTES - 1 then
152 | -- Last size (59)
153 | tx_padding_state <= TX_PAD_DONE;
154 | else
155 | -- Last byte of the current packet
156 | tx_packet_size <= tx_packet_size + 1;
157 | tx_padding_state <= TX_PAD_SIZE_HI;
158 | end if;
159 | else
160 | tx_packet_byte <= tx_packet_byte + 1;
161 | end if;
162 | when TX_PAD_DONE =>
163 | tx_wr_en_sync <= '0';
164 | end case;
165 | else
166 | -- Reset
167 | tx_padding_state <= TX_PAD_SIZE_HI;
168 | tx_packet_size <= 1;
169 | tx_wr_en_sync <= '0';
170 | end if;
171 | end if;
172 | end process;
173 |
174 | end architecture;
175 |
--------------------------------------------------------------------------------
/miim.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Basic MIIM transaction functionality: read and write registers
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | use work.miim_types.all;
12 | use work.utility.all;
13 |
14 | -- MII Management Interface compliant to IEEE 802.3 clause 22
15 | entity miim is
16 | generic(
17 | -- Resulting clock frequency fclock / clock_divider has to be below 2.5 MHz for IEEE conformance
18 | -- Use only even numbers for 50% duty cycle of MDC
19 | CLOCK_DIVIDER : integer range 8 to 1000 := 50
20 | );
21 | port(
22 | -- Synchronous active-high reset
23 | reset_i : in std_ulogic;
24 | clock_i : in std_ulogic;
25 |
26 | -- Transaction data
27 | register_address_i : in t_register_address;
28 | phy_address_i : in t_phy_address := (others => '0');
29 | -- Output data read from the PHY when wr_en_i was low when the transaction was started
30 | data_read_o : out t_data;
31 | -- Input data to write to the PHY when wr_en_i is high when the transaction starts
32 | data_write_i : in t_data;
33 | -- Request transaction
34 | -- Must stay asserted until the transaction has completed
35 | req_i : in std_ulogic;
36 | -- Transaction has completed
37 | -- Deasserted after the user deasserts req_i
38 | ack_o : out std_ulogic;
39 | -- Transaction direction:
40 | -- Low: read register from PHY
41 | -- High: write register to PHY
42 | wr_en_i : in std_ulogic;
43 |
44 | -- MIIM interface: connect to top-level pins
45 | mdc_o : out std_ulogic;
46 | mdio_io : inout std_ulogic
47 | );
48 | end entity;
49 |
50 | architecture rtl of miim is
51 | type t_miim_txrx_state is (
52 | IDLE,
53 | TX_COMMAND,
54 | RX_TURNAROUND_Z,
55 | RX_TURNAROUND_Z_READLOW,
56 | RX_DATA,
57 | TX_TURNAROUND_HIGH,
58 | TX_TURNAROUND_LOW,
59 | TX_DATA,
60 | DONE
61 | );
62 |
63 | signal state : t_miim_txrx_state := IDLE;
64 |
65 | -- Operation type as defined by the standard
66 | subtype t_operation_type is std_ulogic_vector(1 downto 0);
67 |
68 | constant PREAMBLE_LENGTH : natural := 32;
69 | -- The frame format as described in IEEE 802.3 clause 22.2.4.5 is LSB first, so the constants appear reversed here
70 | constant START_OF_FRAME : std_ulogic_vector(1 downto 0) := "10";
71 | constant OPERATION_READ : t_operation_type := "01";
72 | constant OPERATION_WRITE : t_operation_type := "10";
73 | -- Total length of a command on the interface
74 | constant COMMAND_LENGTH : natural := PREAMBLE_LENGTH + START_OF_FRAME'length + t_operation_type'length + t_phy_address'length + t_register_address'length;
75 |
76 | signal operation_code : t_operation_type;
77 | -- Complete prebuffered command to send out
78 | signal command : std_ulogic_vector(COMMAND_LENGTH - 1 downto 0);
79 | -- Number of the command bit currently being sent
80 | signal command_bit_position : integer range 0 to COMMAND_LENGTH;
81 | -- Number of the data bit currently being sent
82 | signal data_bit_position : integer range 0 to t_data'length;
83 |
84 | signal clock_divide_counter : integer range 0 to CLOCK_DIVIDER;
85 |
86 | -- Bit order:
87 | -- PHYAD/REGAD/DATA: MSB first
88 |
89 | begin
90 |
91 | -- Disable clock when idle, apply division otherwise
92 | mdc_o <= '1' when ((state /= IDLE) and (state /= DONE) and clock_divide_counter >= (CLOCK_DIVIDER / 2)) else '0';
93 |
94 | with wr_en_i select operation_code <=
95 | OPERATION_WRITE when '1',
96 | OPERATION_READ when others;
97 |
98 | -- Build command data array
99 | command(PREAMBLE_LENGTH - 1 downto 0) <= (others => '1');
100 | command(command'high(1) downto PREAMBLE_LENGTH) <= reverse_vector(std_ulogic_vector(register_address_i)) & reverse_vector(std_ulogic_vector(phy_address_i)) & operation_code & START_OF_FRAME;
101 |
102 | output : process(state, command_bit_position, data_bit_position, command, data_write_i) is
103 | begin
104 | ack_o <= '0';
105 | mdio_io <= 'Z';
106 | case state is
107 | when IDLE =>
108 | null;
109 | when TX_COMMAND =>
110 | mdio_io <= command(command_bit_position);
111 | when RX_TURNAROUND_Z =>
112 | null;
113 | when RX_TURNAROUND_Z_READLOW =>
114 | null;
115 | when RX_DATA =>
116 | null;
117 | when TX_TURNAROUND_HIGH =>
118 | mdio_io <= '1';
119 | when TX_TURNAROUND_LOW =>
120 | mdio_io <= '0';
121 | when TX_DATA =>
122 | mdio_io <= data_write_i(data_bit_position);
123 | when DONE =>
124 | ack_o <= '1';
125 | end case;
126 | end process;
127 |
128 | rx : process(clock_i) is
129 | begin
130 | -- Synchronize to rising as in the FSM process
131 | if rising_edge(clock_i) then
132 | -- and read just before rising (divided) MDC edge
133 | -- / 2 - 1
134 | if state = RX_DATA and (clock_divide_counter = (CLOCK_DIVIDER / 4)) then
135 | data_read_o(data_bit_position) <= mdio_io;
136 | end if;
137 | end if;
138 | end process;
139 |
140 | fsm : process(clock_i) is
141 | begin
142 | if rising_edge(clock_i) then
143 | if reset_i = '1' then
144 | state <= IDLE;
145 | clock_divide_counter <= 0;
146 | else
147 | if clock_divide_counter = CLOCK_DIVIDER - 1 then
148 | clock_divide_counter <= 0;
149 | else
150 | clock_divide_counter <= clock_divide_counter + 1;
151 | end if;
152 |
153 | -- Run the FSM on the falling divided MDC edge
154 | if (clock_divide_counter = 0) then
155 | case state is
156 | when IDLE =>
157 | command_bit_position <= 0;
158 | -- start at MSB
159 | data_bit_position <= t_data'length - 1;
160 |
161 | if req_i = '1' then
162 | state <= TX_COMMAND;
163 | end if;
164 |
165 | when TX_COMMAND =>
166 | command_bit_position <= command_bit_position + 1;
167 | if command_bit_position = COMMAND_LENGTH - 1 then
168 | if wr_en_i = '0' then
169 | state <= RX_TURNAROUND_Z;
170 | else
171 | state <= TX_TURNAROUND_HIGH;
172 | end if;
173 | end if;
174 |
175 | when RX_TURNAROUND_Z =>
176 | state <= RX_TURNAROUND_Z_READLOW;
177 | when RX_TURNAROUND_Z_READLOW =>
178 | state <= RX_DATA;
179 | when TX_TURNAROUND_HIGH =>
180 | state <= TX_TURNAROUND_LOW;
181 | when TX_TURNAROUND_LOW =>
182 | state <= TX_DATA;
183 |
184 | when RX_DATA | TX_DATA =>
185 | if data_bit_position = 0 then
186 | state <= DONE;
187 | else
188 | data_bit_position <= data_bit_position - 1;
189 | end if;
190 |
191 | when DONE =>
192 | if req_i = '0' then
193 | state <= IDLE;
194 | end if;
195 | end case;
196 | end if;
197 | end if;
198 | end if;
199 | end process;
200 |
201 | end architecture;
202 |
--------------------------------------------------------------------------------
/mii_gmii.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Adaption layer for data transfer with MII and GMII
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.framing_common.all;
13 | use work.ethernet_types.all;
14 |
15 | entity mii_gmii is
16 | port(
17 | tx_reset_i : in std_ulogic;
18 | tx_clock_i : in std_ulogic;
19 | rx_reset_i : in std_ulogic;
20 | rx_clock_i : in std_ulogic;
21 |
22 | -- MII (Media-independent interface)
23 | mii_tx_en_o : out std_ulogic;
24 | mii_txd_o : out std_ulogic_vector(7 downto 0);
25 | mii_rx_er_i : in std_ulogic;
26 | mii_rx_dv_i : in std_ulogic;
27 | mii_rxd_i : in std_ulogic_vector(7 downto 0);
28 |
29 | -- RGMII (Reduced pin count gigabit media-independent interface)
30 | -- Leave open if RGMII is not used
31 | rgmii_tx_ctl_o : out std_ulogic;
32 | rgmii_rx_ctl_i : in std_ulogic;
33 | -- Other pins:
34 | -- mii_gtx_clk_o is TXC
35 | -- mii_txd_o[3:0] is TD[3:0]
36 | -- mii_rx_clk_i is RXC
37 | -- mii_rxd_i[3:0] is RD[3:0]
38 |
39 | -- Interface control signals
40 | -- Must stay stable after tx_reset_i or rx_reset_i is deasserted
41 | speed_select_i : in t_ethernet_speed;
42 |
43 | -- TX/RX control
44 | -- TX signals synchronous to tx_clock
45 | tx_enable_i : in std_ulogic;
46 | -- When asserted together with tx_enable_i, tx_byte_sent_o works as normal, but no data is actually
47 | -- put onto the media-independent interface (for IPG transmission)
48 | tx_gap_i : in std_ulogic;
49 | tx_data_i : in t_ethernet_data;
50 | -- Put next data byte on tx_data_i when asserted
51 | tx_byte_sent_o : out std_ulogic;
52 |
53 | -- RX signals synchronous to rx_clock
54 | -- Asserted as long as one continuous frame is being received
55 | rx_frame_o : out std_ulogic;
56 | -- Valid when rx_byte_received_o is asserted
57 | rx_data_o : out t_ethernet_data;
58 | rx_byte_received_o : out std_ulogic;
59 | rx_error_o : out std_ulogic
60 | );
61 | end entity;
62 |
63 | architecture rtl of mii_gmii is
64 | -- Transmission
65 | type t_mii_gmii_tx_state is (
66 | TX_INIT,
67 | TX_GMII,
68 | TX_MII_LO_QUAD,
69 | TX_MII_HI_QUAD
70 | );
71 | signal tx_state : t_mii_gmii_tx_state := TX_INIT;
72 |
73 | -- Reception
74 | type t_mii_gmii_rx_state is (
75 | RX_INIT,
76 | RX_GMII,
77 | RX_MII_LO_QUAD,
78 | RX_MII_HI_QUAD
79 | );
80 | signal rx_state : t_mii_gmii_rx_state := RX_INIT;
81 |
82 | begin
83 |
84 | -- TX FSM is split into this synchronous process and the output process for tx_byte_sent_o
85 | -- A strictly one-process FSM is impractical for MII transmission: Wait states would be needed
86 | -- to correctly generate tx_byte_sent_o for GMII.
87 | mii_gmii_tx_sync : process(tx_reset_i, tx_clock_i)
88 | begin
89 | -- Use asynchronous reset, clock_tx is not guaranteed to be running during system initialization
90 | if tx_reset_i = '1' then
91 | tx_state <= TX_INIT;
92 | mii_tx_en_o <= '0';
93 | elsif rising_edge(tx_clock_i) then
94 | mii_tx_en_o <= '0';
95 | mii_txd_o <= (others => '0');
96 |
97 | case tx_state is
98 | when TX_INIT =>
99 | case speed_select_i is
100 | when SPEED_1000MBPS =>
101 | tx_state <= TX_GMII;
102 | when others =>
103 | tx_state <= TX_MII_LO_QUAD;
104 | end case;
105 | when TX_GMII =>
106 | -- GMII is very simple: Pass data through when
107 | -- tx_enable_i is asserted
108 | mii_tx_en_o <= tx_enable_i and not tx_gap_i;
109 | mii_txd_o <= tx_data_i;
110 | when TX_MII_LO_QUAD =>
111 | mii_tx_en_o <= tx_enable_i and not tx_gap_i;
112 | mii_txd_o <= "0000" & tx_data_i(3 downto 0);
113 | if tx_enable_i = '1' then
114 | -- Advance to high quad only when data was actually sent
115 | tx_state <= TX_MII_HI_QUAD;
116 | end if;
117 | when TX_MII_HI_QUAD =>
118 | -- tx_enable_i is not considered, a full byte always has to be sent
119 | mii_tx_en_o <= not tx_gap_i;
120 | mii_txd_o <= "0000" & tx_data_i(7 downto 4);
121 | tx_state <= TX_MII_LO_QUAD;
122 | end case;
123 | end if;
124 | end process;
125 |
126 | -- TX output process
127 | -- Generates only the tx_byte_sent_o output
128 | mii_gmii_tx_output : process(tx_state, tx_enable_i, speed_select_i)
129 | begin
130 | -- Default output value
131 | tx_byte_sent_o <= '0';
132 |
133 | case tx_state is
134 | when TX_INIT =>
135 | -- Look ahead to have tx_byte_sent already set in the TX_GMII clock cycle
136 | if tx_enable_i = '1' and speed_select_i = SPEED_1000MBPS then
137 | tx_byte_sent_o <= '1';
138 | end if;
139 | when TX_GMII =>
140 | -- Look ahead again
141 | if tx_enable_i = '0' then
142 | tx_byte_sent_o <= '0';
143 | else
144 | tx_byte_sent_o <= '1';
145 | end if;
146 | when TX_MII_LO_QUAD =>
147 | null;
148 | when TX_MII_HI_QUAD =>
149 | -- MII is simpler, no look-ahead needed
150 | tx_byte_sent_o <= '1';
151 | end case;
152 | end process;
153 |
154 | -- MII/GMII packet reception
155 | mii_gmii_rx_fsm : process(rx_clock_i, rx_reset_i)
156 | begin
157 | if rx_reset_i = '1' then
158 | rx_state <= RX_INIT;
159 | rx_byte_received_o <= '0';
160 | elsif rising_edge(rx_clock_i) then
161 | -- Default output values
162 | rx_frame_o <= '0';
163 | rx_byte_received_o <= '0';
164 | rx_error_o <= '0';
165 |
166 | if rx_state /= RX_INIT then
167 | -- Hand indicators through
168 | rx_error_o <= mii_rx_er_i;
169 | rx_frame_o <= mii_rx_dv_i;
170 | end if;
171 |
172 | case rx_state is
173 | when RX_INIT =>
174 | -- Wait for a pause in reception
175 | if mii_rx_dv_i = '0' then
176 | case speed_select_i is
177 | when SPEED_1000MBPS =>
178 | rx_state <= RX_GMII;
179 | when others =>
180 | rx_state <= RX_MII_LO_QUAD;
181 | end case;
182 | end if;
183 | when RX_GMII =>
184 | -- Just pass the data through
185 | rx_data_o <= mii_rxd_i;
186 | rx_byte_received_o <= mii_rx_dv_i;
187 | when RX_MII_LO_QUAD =>
188 | -- Wait until start of reception
189 | if mii_rx_dv_i = '1' then
190 | rx_state <= RX_MII_HI_QUAD;
191 | end if;
192 | -- Capture low quad
193 | rx_data_o(3 downto 0) <= mii_rxd_i(3 downto 0);
194 | when RX_MII_HI_QUAD =>
195 | -- Capture high quad and mark it valid
196 | rx_data_o(7 downto 4) <= mii_rxd_i(3 downto 0);
197 | rx_byte_received_o <= '1';
198 | rx_frame_o <= '1';
199 | if mii_rx_dv_i = '0' then
200 | -- Frame ended prematurely on a half-byte
201 | rx_error_o <= '1';
202 | end if;
203 | rx_state <= RX_MII_LO_QUAD;
204 | end case;
205 | end if;
206 | end process;
207 |
208 | end architecture;
209 |
--------------------------------------------------------------------------------
/xilinx/mii_gmii_io_spartan6.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- IO structure for MII/GMII on Xilinx Spartan-6 devices
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | library unisim;
12 | use unisim.vcomponents.all;
13 |
14 | architecture spartan_6 of mii_gmii_io is
15 | signal gmii_active : std_ulogic := '0';
16 | signal clock_tx : std_ulogic := '0';
17 | signal clock_tx_inv : std_ulogic := '1';
18 | signal clock_mii_rx_io : std_ulogic := '0';
19 | signal clock_mii_rx_div : std_ulogic;
20 |
21 | -- IDELAY_VALUE applied to the inputs using IODELAY2
22 | -- This will need fine-tuning depending on the exact device and the location of the IO pins
23 | -- The constraints file can be used to override this default value in the fixed_input_delay
24 | -- instances if needed
25 | constant MII_RX_INPUT_DELAY : natural := 10;
26 | signal clock_mii_rx_ibufg : std_ulogic;
27 |
28 | begin
29 | clock_tx_o <= clock_tx;
30 | -- Inverter is absorbed into the IOB FF clock inputs
31 | clock_tx_inv <= not clock_tx;
32 |
33 | -- speed_select_i must be registered so no hazards can reach the BUFGMUX
34 | with speed_select_i select gmii_active <=
35 | '1' when SPEED_1000MBPS,
36 | '0' when others;
37 |
38 | -- Switch between 125 Mhz reference clock and MII_TX_CLK for TX process and register clocking
39 | -- depending on mode of operation
40 | -- Asynchronous clock switch-over is required: the MII TX_CLK might not be running any more when
41 | -- switching to GMII. This means that glitches can occur on the clock and the complete MAC has to
42 | -- be reset after a speed change.
43 | clock_tx_BUFGMUX_inst : BUFGMUX
44 | generic map(
45 | CLK_SEL_TYPE => "ASYNC" -- Glitchles ("SYNC") or fast ("ASYNC") clock switch-over
46 | )
47 | port map(
48 | O => clock_tx, -- 1-bit output: Clock buffer output
49 | I0 => mii_tx_clk_i, -- 1-bit input: Clock buffer input (S=0)
50 | I1 => clock_125_i, -- 1-bit input: Clock buffer input (S=1)
51 | S => gmii_active -- 1-bit input: Clock buffer select
52 | );
53 |
54 | -- Output clock only when running GMII to reduce switching noise
55 | -- and avoid outputting a useless 25 MHz clock in MII mode.
56 | -- Invert clock so that the output values toggle at the falling edge (as seen from the PHY)
57 | -- and are valid when the clock rises.
58 | -- The inverse clock is generated by simple inversion. This is not a problem since inverters
59 | -- are integrated into the ODDR2 clock inputs (no LUT necessary).
60 | -- Clock enable is not synchronous to clock_tx here, but that shouldn't matter as it
61 | -- switches only very seldomly. If the setup/hold time is violated, only one or two clock cyles
62 | -- should be missed.
63 | ODDR2_inst : ODDR2
64 | generic map(
65 | DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1"
66 | INIT => '0', -- Sets initial state of the Q output to '0' or '1'
67 | SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
68 | port map(
69 | Q => gmii_gtx_clk_o, -- 1-bit output data
70 | C0 => clock_tx_inv, -- 1-bit clock input
71 | C1 => clock_tx, -- 1-bit clock input
72 | CE => gmii_active, -- 1-bit clock enable input
73 | D0 => '1', -- 1-bit data input (associated with C0)
74 | D1 => '0', -- 1-bit data input (associated with C1)
75 | R => '0', -- 1-bit reset input
76 | S => '0' -- 1-bit set input
77 | );
78 |
79 | -- Use FDRE in IOB for output pins to guarantee delay characteristics are identical to the GTX_CLK output.
80 | -- The registers need to be clocked by clock_tx and not clock_125. Metastability would
81 | -- ensure otherwise since the TX state machine is clocked by clock_tx and clock_tx/clock_125
82 | -- have no defined phase relationship.
83 | mii_tx_en_buffer_inst : entity work.output_buffer
84 | port map(
85 | pad_o => mii_tx_en_o,
86 | buffer_i => int_mii_tx_en_i,
87 | clock_i => clock_tx
88 | );
89 |
90 | mii_txd_buffer_generate : for i in mii_txd_o'range generate
91 | mii_txd_buffer_inst : entity work.output_buffer
92 | port map(
93 | pad_o => mii_txd_o(i),
94 | buffer_i => int_mii_txd_i(i),
95 | clock_i => clock_tx
96 | );
97 | end generate;
98 |
99 | -- Inserting a delay into the clock path should theoretically allow fine-tuning
100 | -- of the clock/data offset, but the timing analyzer doesn't like it as very big
101 | -- IDELAY_VALUE values are necessary that exhibit strong variations. Maybe it works anyway. Try if
102 | -- the current method fails.
103 | -- mii_rx_clk_delay_inst : entity work.fixed_input_delay
104 | -- generic map (
105 | -- IDELAY_VALUE => 0
106 | -- )
107 | -- port map (
108 | -- pin_i => mii_rx_clk_i,
109 | -- delayed_o => mii_rx_clk_delayed
110 | -- );
111 |
112 | IBUFG_inst : IBUFG
113 | generic map(
114 | IBUF_LOW_PWR => FALSE, -- Low power (TRUE) vs. performance (FALSE) setting for referenced I/O standards
115 | IOSTANDARD => "LVCMOS33")
116 | port map(
117 | O => clock_mii_rx_ibufg, -- Clock buffer output
118 | I => mii_rx_clk_i -- Clock buffer input (connect directly to top-level port)
119 | );
120 |
121 | -- Use of a PLL or DCM for RX_CLK is not possible! The clock frequency in 10 Mbps mode (2.5 MHz)
122 | -- is below the minimum input frequency of both the PLL and DCM block.
123 | mii_rx_clk_BUFIO2_inst : BUFIO2
124 | generic map(
125 | DIVIDE => 1, -- DIVCLK divider (1,3-8)
126 | DIVIDE_BYPASS => TRUE, -- Bypass the divider circuitry (TRUE/FALSE)
127 | I_INVERT => FALSE, -- Invert clock (TRUE/FALSE)
128 | USE_DOUBLER => FALSE -- Use doubler circuitry (TRUE/FALSE)
129 | )
130 | port map(
131 | DIVCLK => clock_mii_rx_div, -- 1-bit output: Divided clock output
132 | IOCLK => clock_mii_rx_io, -- 1-bit output: I/O output clock
133 | SERDESSTROBE => open, -- 1-bit output: Output SERDES strobe (connect to ISERDES2/OSERDES2)
134 | I => clock_mii_rx_ibufg -- 1-bit input: Clock input (connect to IBUFG)
135 | );
136 |
137 | mii_rx_clk_BUFG_inst : BUFG
138 | port map(
139 | O => clock_rx_o, -- 1-bit output: Clock buffer output
140 | I => clock_mii_rx_div -- 1-bit input: Clock buffer input
141 | );
142 |
143 | mii_rx_dv_buffer_inst : entity work.input_buffer
144 | generic map(
145 | HAS_DELAY => TRUE,
146 | IDELAY_VALUE => MII_RX_INPUT_DELAY
147 | )
148 | port map(
149 | pad_i => mii_rx_dv_i,
150 | buffer_o => int_mii_rx_dv_o,
151 | clock_i => clock_mii_rx_io
152 | );
153 |
154 | mii_rx_er_buffer_inst : entity work.input_buffer
155 | generic map(
156 | HAS_DELAY => TRUE,
157 | IDELAY_VALUE => MII_RX_INPUT_DELAY
158 | )
159 | port map(
160 | pad_i => mii_rx_er_i,
161 | buffer_o => int_mii_rx_er_o,
162 | clock_i => clock_mii_rx_io
163 | );
164 |
165 | mii_rxd_buffer_generate : for i in mii_rxd_i'range generate
166 | mii_rxd_buffer_inst : entity work.input_buffer
167 | generic map(
168 | HAS_DELAY => TRUE,
169 | IDELAY_VALUE => MII_RX_INPUT_DELAY
170 | )
171 | port map(
172 | pad_i => mii_rxd_i(i),
173 | buffer_o => int_mii_rxd_o(i),
174 | clock_i => clock_mii_rx_io
175 | );
176 | end generate;
177 |
178 | end architecture;
179 |
--------------------------------------------------------------------------------
/ethernet_with_fifos.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Prebuilt Ethernet MAC with FIFOs connected
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 |
11 | use work.ethernet_types.all;
12 | use work.miim_types.all;
13 |
14 | entity ethernet_with_fifos is
15 | generic(
16 | MIIM_PHY_ADDRESS : t_phy_address := (others => '0');
17 | MIIM_RESET_WAIT_TICKS : natural := 0;
18 | MIIM_POLL_WAIT_TICKS : natural := DEFAULT_POLL_WAIT_TICKS;
19 | -- See comment in miim for values
20 | -- Default is fine for 125 MHz MIIM clock
21 | MIIM_CLOCK_DIVIDER : positive := 50;
22 | MIIM_DISABLE : boolean := FALSE;
23 |
24 | -- See comment in rx_fifo for values
25 | RX_FIFO_SIZE_BITS : positive := 12
26 | );
27 | port(
28 | -- Unbuffered 125 MHz clock input
29 | clock_125_i : in std_ulogic;
30 | -- Asynchronous reset
31 | reset_i : in std_ulogic;
32 | -- MAC address of this station
33 | -- Must not change after reset is deasserted
34 | mac_address_i : in t_mac_address;
35 |
36 | -- MII (Media-independent interface)
37 | mii_tx_clk_i : in std_ulogic;
38 | mii_tx_er_o : out std_ulogic;
39 | mii_tx_en_o : out std_ulogic;
40 | mii_txd_o : out std_ulogic_vector(7 downto 0);
41 | mii_rx_clk_i : in std_ulogic;
42 | mii_rx_er_i : in std_ulogic;
43 | mii_rx_dv_i : in std_ulogic;
44 | mii_rxd_i : in std_ulogic_vector(7 downto 0);
45 |
46 | -- GMII (Gigabit media-independent interface)
47 | gmii_gtx_clk_o : out std_ulogic;
48 |
49 | -- RGMII (Reduced pin count gigabit media-independent interface)
50 | rgmii_tx_ctl_o : out std_ulogic;
51 | rgmii_rx_ctl_i : in std_ulogic;
52 |
53 | -- MII Management Interface
54 | -- Clock, can be identical to clock_125_i
55 | -- If not, adjust MIIM_CLOCK_DIVIDER accordingly
56 | miim_clock_i : in std_ulogic;
57 | mdc_o : out std_ulogic;
58 | mdio_io : inout std_ulogic;
59 | -- Status, synchronous to miim_clock_i
60 | link_up_o : out std_ulogic;
61 | speed_o : out t_ethernet_speed;
62 | -- Also synchronous to miim_clock_i if used!
63 | speed_override_i : in t_ethernet_speed := SPEED_UNSPECIFIED;
64 |
65 | -- TX FIFO
66 | tx_clock_i : in std_ulogic;
67 | -- Synchronous reset
68 | -- When asserted, the content of the buffer was lost.
69 | -- When full is deasserted the next time, a packet size must be written.
70 | -- The data of the packet previously being written is not available anymore then.
71 | tx_reset_o : out std_ulogic;
72 | tx_data_i : in t_ethernet_data;
73 | tx_wr_en_i : in std_ulogic;
74 | tx_full_o : out std_ulogic;
75 |
76 | -- RX FIFO
77 | rx_clock_i : in std_ulogic;
78 | -- Synchronous reset
79 | -- When asserted, the content of the buffer was lost.
80 | -- When empty is deasserted the next time, a packet size must be read out.
81 | -- The data of the packet previously being read out is not available anymore then.
82 | rx_reset_o : out std_ulogic;
83 | rx_empty_o : out std_ulogic;
84 | rx_rd_en_i : in std_ulogic;
85 | rx_data_o : out t_ethernet_data
86 | );
87 | end entity;
88 |
89 | architecture rtl of ethernet_with_fifos is
90 | signal mac_reset : std_ulogic := '1';
91 |
92 | signal mac_tx_reset : std_ulogic := '1';
93 | signal mac_tx_clock : std_ulogic;
94 | signal mac_tx_enable : std_ulogic := '0';
95 | signal mac_tx_data : t_ethernet_data;
96 | signal mac_tx_byte_sent : std_ulogic;
97 | signal mac_tx_busy : std_ulogic;
98 | signal mac_tx_busy_int : std_ulogic;
99 | signal mac_rx_reset : std_ulogic := '1';
100 | signal mac_rx_clock : std_ulogic;
101 | signal mac_rx_frame : std_ulogic;
102 | signal mac_rx_data : t_ethernet_data;
103 | signal mac_rx_byte_received : std_ulogic;
104 | signal mac_rx_error : std_ulogic;
105 |
106 | begin
107 |
108 | -- Needed for correct simulation of the inter-packet gap
109 | -- Without any delay, tx_fifo_adapter would see the tx_busy indication too early
110 | -- This generally applies to all signals, but the behavior of the other ones
111 | -- does not cause simulation mismatches.
112 | mac_tx_busy <= transport mac_tx_busy_int after 1 ns;
113 |
114 | -- Synchronize user resets
115 | sync_tx_reset_inst : entity work.single_signal_synchronizer
116 | port map(
117 | clock_target_i => tx_clock_i,
118 | signal_i => mac_reset,
119 | signal_o => tx_reset_o
120 | );
121 |
122 | sync_rx_reset_inst : entity work.single_signal_synchronizer
123 | port map(
124 | clock_target_i => rx_clock_i,
125 | signal_i => mac_reset,
126 | signal_o => rx_reset_o
127 | );
128 |
129 | ethernet_inst : entity work.ethernet
130 | generic map(
131 | MIIM_PHY_ADDRESS => MIIM_PHY_ADDRESS,
132 | MIIM_RESET_WAIT_TICKS => MIIM_RESET_WAIT_TICKS,
133 | MIIM_POLL_WAIT_TICKS => MIIM_POLL_WAIT_TICKS,
134 | MIIM_CLOCK_DIVIDER => MIIM_CLOCK_DIVIDER,
135 | MIIM_DISABLE => MIIM_DISABLE
136 | )
137 | port map(
138 | clock_125_i => clock_125_i,
139 | reset_i => reset_i,
140 | reset_o => mac_reset,
141 | mac_address_i => mac_address_i,
142 | mii_tx_clk_i => mii_tx_clk_i,
143 | mii_tx_er_o => mii_tx_er_o,
144 | mii_tx_en_o => mii_tx_en_o,
145 | mii_txd_o => mii_txd_o,
146 | mii_rx_clk_i => mii_rx_clk_i,
147 | mii_rx_er_i => mii_rx_er_i,
148 | mii_rx_dv_i => mii_rx_dv_i,
149 | mii_rxd_i => mii_rxd_i,
150 | gmii_gtx_clk_o => gmii_gtx_clk_o,
151 | rgmii_tx_ctl_o => rgmii_tx_ctl_o,
152 | rgmii_rx_ctl_i => rgmii_rx_ctl_i,
153 | miim_clock_i => miim_clock_i,
154 | mdc_o => mdc_o,
155 | mdio_io => mdio_io,
156 | tx_reset_o => mac_tx_reset,
157 | tx_clock_o => mac_tx_clock,
158 | tx_enable_i => mac_tx_enable,
159 | tx_data_i => mac_tx_data,
160 | tx_byte_sent_o => mac_tx_byte_sent,
161 | tx_busy_o => mac_tx_busy_int,
162 | rx_reset_o => mac_rx_reset,
163 | rx_clock_o => mac_rx_clock,
164 | rx_frame_o => mac_rx_frame,
165 | rx_data_o => mac_rx_data,
166 | rx_byte_received_o => mac_rx_byte_received,
167 | rx_error_o => mac_rx_error,
168 | link_up_o => link_up_o,
169 | speed_o => speed_o,
170 | speed_override_i => speed_override_i
171 | );
172 |
173 | rx_fifo_inst : entity work.rx_fifo
174 | generic map(
175 | MEMORY_SIZE_BITS => RX_FIFO_SIZE_BITS
176 | )
177 | port map(
178 | clock_i => rx_clock_i,
179 | mac_rx_reset_i => mac_rx_reset,
180 | mac_rx_clock_i => mac_rx_clock,
181 | mac_rx_frame_i => mac_rx_frame,
182 | mac_rx_data_i => mac_rx_data,
183 | mac_rx_byte_received_i => mac_rx_byte_received,
184 | mac_rx_error_i => mac_rx_error,
185 | empty_o => rx_empty_o,
186 | rd_en_i => rx_rd_en_i,
187 | data_o => rx_data_o
188 | );
189 |
190 | tx_fifo_inst : entity work.tx_fifo
191 | port map(
192 | clock_i => tx_clock_i,
193 | data_i => tx_data_i,
194 | wr_en_i => tx_wr_en_i,
195 | full_o => tx_full_o,
196 | mac_tx_reset_i => mac_tx_reset,
197 | mac_tx_clock_i => mac_tx_clock,
198 | mac_tx_enable_o => mac_tx_enable,
199 | mac_tx_data_o => mac_tx_data,
200 | mac_tx_byte_sent_i => mac_tx_byte_sent,
201 | mac_tx_busy_i => mac_tx_busy
202 | );
203 |
204 | end architecture;
205 |
206 |
--------------------------------------------------------------------------------
/xilinx/ipcore_dir/ethernet_mac_tx_fifo_xilinx.xco:
--------------------------------------------------------------------------------
1 | ##############################################################
2 | #
3 | # Xilinx Core Generator version 14.7
4 | # Date: Tue Jul 28 23:05:54 2015
5 | #
6 | ##############################################################
7 | #
8 | # This file contains the customisation parameters for a
9 | # Xilinx CORE Generator IP GUI. It is strongly recommended
10 | # that you do not manually alter this file as it may cause
11 | # unexpected and unsupported behavior.
12 | #
13 | ##############################################################
14 | #
15 | # Generated from component: xilinx.com:ip:fifo_generator:9.3
16 | #
17 | ##############################################################
18 | #
19 | # BEGIN Project Options
20 | SET addpads = false
21 | SET asysymbol = true
22 | SET busformat = BusFormatAngleBracketNotRipped
23 | SET createndf = false
24 | SET designentry = VHDL
25 | SET device = xc6slx45
26 | SET devicefamily = spartan6
27 | SET flowvendor = Other
28 | SET formalverification = false
29 | SET foundationsym = false
30 | SET implementationfiletype = Ngc
31 | SET package = fgg484
32 | SET removerpms = false
33 | SET simulationfiles = Behavioral
34 | SET speedgrade = -2
35 | SET verilogsim = false
36 | SET vhdlsim = true
37 | # END Project Options
38 | # BEGIN Select
39 | SELECT FIFO_Generator xilinx.com:ip:fifo_generator:9.3
40 | # END Select
41 | # BEGIN Parameters
42 | CSET add_ngc_constraint_axi=false
43 | CSET almost_empty_flag=false
44 | CSET almost_full_flag=false
45 | CSET aruser_width=1
46 | CSET awuser_width=1
47 | CSET axi_address_width=32
48 | CSET axi_data_width=64
49 | CSET axi_type=AXI4_Stream
50 | CSET axis_type=FIFO
51 | CSET buser_width=1
52 | CSET clock_enable_type=Slave_Interface_Clock_Enable
53 | CSET clock_type_axi=Common_Clock
54 | CSET component_name=ethernet_mac_tx_fifo_xilinx
55 | CSET data_count=false
56 | CSET data_count_width=11
57 | CSET disable_timing_violations=false
58 | CSET disable_timing_violations_axi=false
59 | CSET dout_reset_value=0
60 | CSET empty_threshold_assert_value=4
61 | CSET empty_threshold_assert_value_axis=1022
62 | CSET empty_threshold_assert_value_rach=1022
63 | CSET empty_threshold_assert_value_rdch=1022
64 | CSET empty_threshold_assert_value_wach=1022
65 | CSET empty_threshold_assert_value_wdch=1022
66 | CSET empty_threshold_assert_value_wrch=1022
67 | CSET empty_threshold_negate_value=5
68 | CSET enable_aruser=false
69 | CSET enable_awuser=false
70 | CSET enable_buser=false
71 | CSET enable_common_overflow=false
72 | CSET enable_common_underflow=false
73 | CSET enable_data_counts_axis=false
74 | CSET enable_data_counts_rach=false
75 | CSET enable_data_counts_rdch=false
76 | CSET enable_data_counts_wach=false
77 | CSET enable_data_counts_wdch=false
78 | CSET enable_data_counts_wrch=false
79 | CSET enable_ecc=false
80 | CSET enable_ecc_axis=false
81 | CSET enable_ecc_rach=false
82 | CSET enable_ecc_rdch=false
83 | CSET enable_ecc_wach=false
84 | CSET enable_ecc_wdch=false
85 | CSET enable_ecc_wrch=false
86 | CSET enable_read_channel=false
87 | CSET enable_read_pointer_increment_by2=false
88 | CSET enable_reset_synchronization=true
89 | CSET enable_ruser=false
90 | CSET enable_tdata=false
91 | CSET enable_tdest=false
92 | CSET enable_tid=false
93 | CSET enable_tkeep=false
94 | CSET enable_tlast=false
95 | CSET enable_tready=true
96 | CSET enable_tstrobe=false
97 | CSET enable_tuser=false
98 | CSET enable_write_channel=false
99 | CSET enable_wuser=false
100 | CSET fifo_application_type_axis=Data_FIFO
101 | CSET fifo_application_type_rach=Data_FIFO
102 | CSET fifo_application_type_rdch=Data_FIFO
103 | CSET fifo_application_type_wach=Data_FIFO
104 | CSET fifo_application_type_wdch=Data_FIFO
105 | CSET fifo_application_type_wrch=Data_FIFO
106 | CSET fifo_implementation=Independent_Clocks_Block_RAM
107 | CSET fifo_implementation_axis=Common_Clock_Block_RAM
108 | CSET fifo_implementation_rach=Common_Clock_Block_RAM
109 | CSET fifo_implementation_rdch=Common_Clock_Block_RAM
110 | CSET fifo_implementation_wach=Common_Clock_Block_RAM
111 | CSET fifo_implementation_wdch=Common_Clock_Block_RAM
112 | CSET fifo_implementation_wrch=Common_Clock_Block_RAM
113 | CSET full_flags_reset_value=1
114 | CSET full_threshold_assert_value=2047
115 | CSET full_threshold_assert_value_axis=1023
116 | CSET full_threshold_assert_value_rach=1023
117 | CSET full_threshold_assert_value_rdch=1023
118 | CSET full_threshold_assert_value_wach=1023
119 | CSET full_threshold_assert_value_wdch=1023
120 | CSET full_threshold_assert_value_wrch=1023
121 | CSET full_threshold_negate_value=2046
122 | CSET id_width=4
123 | CSET inject_dbit_error=false
124 | CSET inject_dbit_error_axis=false
125 | CSET inject_dbit_error_rach=false
126 | CSET inject_dbit_error_rdch=false
127 | CSET inject_dbit_error_wach=false
128 | CSET inject_dbit_error_wdch=false
129 | CSET inject_dbit_error_wrch=false
130 | CSET inject_sbit_error=false
131 | CSET inject_sbit_error_axis=false
132 | CSET inject_sbit_error_rach=false
133 | CSET inject_sbit_error_rdch=false
134 | CSET inject_sbit_error_wach=false
135 | CSET inject_sbit_error_wdch=false
136 | CSET inject_sbit_error_wrch=false
137 | CSET input_data_width=8
138 | CSET input_depth=2048
139 | CSET input_depth_axis=1024
140 | CSET input_depth_rach=16
141 | CSET input_depth_rdch=1024
142 | CSET input_depth_wach=16
143 | CSET input_depth_wdch=1024
144 | CSET input_depth_wrch=16
145 | CSET interface_type=Native
146 | CSET output_data_width=8
147 | CSET output_depth=2048
148 | CSET overflow_flag=false
149 | CSET overflow_flag_axi=false
150 | CSET overflow_sense=Active_High
151 | CSET overflow_sense_axi=Active_High
152 | CSET performance_options=First_Word_Fall_Through
153 | CSET programmable_empty_type=No_Programmable_Empty_Threshold
154 | CSET programmable_empty_type_axis=No_Programmable_Empty_Threshold
155 | CSET programmable_empty_type_rach=No_Programmable_Empty_Threshold
156 | CSET programmable_empty_type_rdch=No_Programmable_Empty_Threshold
157 | CSET programmable_empty_type_wach=No_Programmable_Empty_Threshold
158 | CSET programmable_empty_type_wdch=No_Programmable_Empty_Threshold
159 | CSET programmable_empty_type_wrch=No_Programmable_Empty_Threshold
160 | CSET programmable_full_type=No_Programmable_Full_Threshold
161 | CSET programmable_full_type_axis=No_Programmable_Full_Threshold
162 | CSET programmable_full_type_rach=No_Programmable_Full_Threshold
163 | CSET programmable_full_type_rdch=No_Programmable_Full_Threshold
164 | CSET programmable_full_type_wach=No_Programmable_Full_Threshold
165 | CSET programmable_full_type_wdch=No_Programmable_Full_Threshold
166 | CSET programmable_full_type_wrch=No_Programmable_Full_Threshold
167 | CSET rach_type=FIFO
168 | CSET rdch_type=FIFO
169 | CSET read_clock_frequency=1
170 | CSET read_data_count=true
171 | CSET read_data_count_width=12
172 | CSET register_slice_mode_axis=Fully_Registered
173 | CSET register_slice_mode_rach=Fully_Registered
174 | CSET register_slice_mode_rdch=Fully_Registered
175 | CSET register_slice_mode_wach=Fully_Registered
176 | CSET register_slice_mode_wdch=Fully_Registered
177 | CSET register_slice_mode_wrch=Fully_Registered
178 | CSET reset_pin=true
179 | CSET reset_type=Asynchronous_Reset
180 | CSET ruser_width=1
181 | CSET synchronization_stages=2
182 | CSET synchronization_stages_axi=2
183 | CSET tdata_width=64
184 | CSET tdest_width=4
185 | CSET tid_width=8
186 | CSET tkeep_width=4
187 | CSET tstrb_width=4
188 | CSET tuser_width=4
189 | CSET underflow_flag=false
190 | CSET underflow_flag_axi=false
191 | CSET underflow_sense=Active_High
192 | CSET underflow_sense_axi=Active_High
193 | CSET use_clock_enable=false
194 | CSET use_dout_reset=true
195 | CSET use_embedded_registers=false
196 | CSET use_extra_logic=true
197 | CSET valid_flag=false
198 | CSET valid_sense=Active_High
199 | CSET wach_type=FIFO
200 | CSET wdch_type=FIFO
201 | CSET wrch_type=FIFO
202 | CSET write_acknowledge_flag=false
203 | CSET write_acknowledge_sense=Active_High
204 | CSET write_clock_frequency=1
205 | CSET write_data_count=false
206 | CSET write_data_count_width=12
207 | CSET wuser_width=1
208 | # END Parameters
209 | # BEGIN Extra information
210 | MISC pkg_timestamp=2012-11-19T12:39:56Z
211 | # END Extra information
212 | GENERATE
213 | # CRC: 190bae02
214 |
--------------------------------------------------------------------------------
/miim_control.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | library ieee;
7 | use ieee.std_logic_1164.all;
8 | use ieee.numeric_std.all;
9 |
10 | use work.ethernet_types.all;
11 | use work.miim_types.all;
12 | use work.miim_registers.all;
13 |
14 | entity miim_control is
15 | generic(
16 | -- Ticks to wait before writing any registers after reset low
17 | RESET_WAIT_TICKS : natural := 0;
18 |
19 | -- Ticks to wait between polling the status register
20 | POLL_WAIT_TICKS : natural := DEFAULT_POLL_WAIT_TICKS;
21 |
22 | -- Activate debug output
23 | DEBUG_OUTPUT : boolean := FALSE
24 |
25 | -- Example for Marvell PHY 88E1111 and 125 MHz MIIM clock:
26 | -- RESET_WAIT_TICKS => 1250000 (10 ms at 125 MHz, minimum: 5 ms)
27 | );
28 | port(
29 | reset_i : in std_ulogic;
30 | clock_i : in std_ulogic;
31 |
32 | miim_register_address_o : out t_register_address;
33 | miim_data_read_i : in t_data;
34 | miim_data_write_o : out t_data;
35 | miim_req_o : out std_ulogic;
36 | miim_ack_i : in std_ulogic;
37 | miim_we_o : out std_ulogic;
38 |
39 | speed_o : out t_ethernet_speed;
40 | link_up_o : out std_ulogic;
41 |
42 | -- Only used if DEBUG_OUTPUT is TRUE
43 | debug_fifo_we_o : out std_ulogic;
44 | debug_fifo_write_data_o : out std_ulogic_vector(7 downto 0)
45 | );
46 | end entity;
47 |
48 | architecture rtl of miim_control is
49 | signal register_address : t_register_address;
50 |
51 | type t_state is (
52 | RESET_WAIT,
53 | WRITE_AUTONEG,
54 | WRITE_GIGABIT_AUTONEG,
55 | WRITE_SOFTRESET,
56 | WAIT_POLL,
57 | READ_STATUS,
58 | READ_SPEED_10_100,
59 | READ_SPEED_1000,
60 | DEBUG_START,
61 | DEBUG_WRITE_REGAD,
62 | DEBUG_WRITE_BYTE1,
63 | DEBUG_WRITE_BYTE2,
64 | WAIT_ACK_LOW,
65 | DEBUG_DONE
66 | );
67 | signal state : t_state := RESET_WAIT;
68 | signal after_ack_state : t_state;
69 |
70 | -- Initial register write contents
71 |
72 | -- Reset the PHY
73 | constant control_register_reset : t_control_register := (
74 | reset => '1',
75 | loopback => '0',
76 | speed_10_100 => '0',
77 | speed_1000 => '1',
78 | auto_negotiation_enable => '1',
79 | power_down => '0',
80 | isolate => '0',
81 | restart_auto_negotiation => '0',
82 | duplex_mode => '1',
83 | enable_collision_test => '0',
84 | unidirectional_enable => '0'
85 | );
86 |
87 | -- Activate only full-duplex 10/100
88 | constant auto_negotiation_set_fd : t_auto_negotiation_advertisement_register_802_3 := (
89 | next_page => '0',
90 | remote_fault => '0',
91 | extended_next_page => '0',
92 | asymmetric_pause => '0',
93 | pause => '0',
94 | advertise_100base_t4 => '0',
95 | advertise_100base_tx_fd => '1',
96 | advertise_100base_tx_hd => '0',
97 | advertise_10base_t_fd => '1',
98 | advertise_10base_t_hd => '0'
99 | );
100 |
101 | -- Activate only full-duplex 1000
102 | constant master_slave_set_fd : t_master_slave_control_register := (
103 | test_mode_bits => "000",
104 | master_slave_manual_config_enable => '0',
105 | master_slave_manual_config_value => '0',
106 | port_type_is_multiport => '0',
107 | advertise_1000base_t_fd => '1',
108 | advertise_1000base_t_hd => '0'
109 | );
110 |
111 | signal init_done : boolean := FALSE;
112 | signal reset_wait_counter : natural range 0 to RESET_WAIT_TICKS + 1;
113 | signal poll_wait_counter : natural range 0 to POLL_WAIT_TICKS + 1;
114 |
115 | signal lp_supports_10 : std_ulogic;
116 | signal lp_supports_100 : std_ulogic;
117 |
118 | begin
119 | miim_register_address_o <= register_address;
120 |
121 | fsm : process(clock_i)
122 | begin
123 | if rising_edge(clock_i) then
124 | -- Default values
125 | miim_req_o <= '0';
126 | debug_fifo_we_o <= '0';
127 |
128 | if reset_i = '1' then
129 | state <= RESET_WAIT;
130 | link_up_o <= '0';
131 | speed_o <= SPEED_UNSPECIFIED;
132 | reset_wait_counter <= 0;
133 | poll_wait_counter <= 0;
134 | else
135 | miim_we_o <= '0';
136 | case state is
137 | -- Initialization
138 | when RESET_WAIT =>
139 | -- Keep in mind that zero is a valid (and the default) value for RESET_WAIT_TICKS
140 | if reset_wait_counter = RESET_WAIT_TICKS then
141 | state <= WRITE_AUTONEG;
142 | end if;
143 | reset_wait_counter <= reset_wait_counter + 1;
144 |
145 | when WRITE_AUTONEG =>
146 | -- Advertise 100 MBit/10 MBit full-duplex, no PAUSE support
147 | register_address <= AUTONEG_ADVERTISEMENT_REG;
148 | miim_data_write_o <= auto_negotiation_advertisement_register_802_3_to_data(auto_negotiation_set_fd);
149 | miim_req_o <= '1';
150 | miim_we_o <= '1';
151 | if miim_ack_i = '1' then
152 | miim_req_o <= '0';
153 | state <= WAIT_ACK_LOW;
154 | after_ack_state <= WRITE_GIGABIT_AUTONEG;
155 | end if;
156 | when WRITE_GIGABIT_AUTONEG =>
157 | -- Advertise 1000 MBit full-duplex
158 | register_address <= MASTERSLAVE_CTRL_REG;
159 | miim_data_write_o <= master_slave_control_register_to_data(master_slave_set_fd);
160 | miim_req_o <= '1';
161 | miim_we_o <= '1';
162 | if miim_ack_i = '1' then
163 | miim_req_o <= '0';
164 | state <= WAIT_ACK_LOW;
165 | after_ack_state <= WRITE_SOFTRESET;
166 | end if;
167 | when WRITE_SOFTRESET =>
168 | -- Reset the PHY to apply the autonegotiation values
169 | register_address <= CONTROL_REG;
170 | miim_data_write_o <= control_register_to_data(control_register_reset);
171 | miim_req_o <= '1';
172 | miim_we_o <= '1';
173 | if miim_ack_i = '1' then
174 | miim_req_o <= '0';
175 | init_done <= TRUE;
176 | state <= WAIT_ACK_LOW;
177 | after_ack_state <= WAIT_POLL;
178 | end if;
179 |
180 | -- State polling
181 |
182 | when WAIT_POLL =>
183 | -- Don't poll continuously to reduce unnecessary switching noise
184 | poll_wait_counter <= poll_wait_counter + 1;
185 | if poll_wait_counter = POLL_WAIT_TICKS then
186 | poll_wait_counter <= 0;
187 | state <= READ_STATUS;
188 | end if;
189 | when READ_STATUS =>
190 | -- Read status register
191 | register_address <= STATUS_REG;
192 | miim_req_o <= '1';
193 | if miim_ack_i = '1' then
194 | -- Link is up when the link status indicator and auto-negotiation is OK
195 | link_up_o <= data_to_status_register(miim_data_read_i).link_status and data_to_status_register(miim_data_read_i).auto_negotiation_complete;
196 | miim_req_o <= '0';
197 | state <= WAIT_ACK_LOW;
198 | after_ack_state <= READ_SPEED_10_100;
199 | end if;
200 | when READ_SPEED_10_100 =>
201 | -- Read link partner ability register
202 | register_address <= AUTONEG_LP_BASEPAGEABILITY_REG;
203 | miim_req_o <= '1';
204 | if miim_ack_i = '1' then
205 | miim_req_o <= '0';
206 | lp_supports_10 <= data_to_auto_negotiation_lp_base_page_ability_register(miim_data_read_i).can_10base_t_fd;
207 | lp_supports_100 <= data_to_auto_negotiation_lp_base_page_ability_register(miim_data_read_i).can_100base_tx_fd;
208 | state <= WAIT_ACK_LOW;
209 | after_ack_state <= READ_SPEED_1000;
210 | end if;
211 | when READ_SPEED_1000 =>
212 | register_address <= MASTERSLAVE_STATUS_REG;
213 | miim_req_o <= '1';
214 | if miim_ack_i = '1' then
215 | miim_req_o <= '0';
216 | -- Detect highest supported data rate
217 | if data_to_master_slave_status_register(miim_data_read_i).lp_1000base_t_fd = '1' then
218 | speed_o <= SPEED_1000MBPS;
219 | elsif lp_supports_100 = '1' then
220 | speed_o <= SPEED_100MBPS;
221 | elsif lp_supports_10 = '1' then
222 | speed_o <= SPEED_10MBPS;
223 | else
224 | -- Nothing is supported
225 | speed_o <= SPEED_UNSPECIFIED;
226 | end if;
227 |
228 | state <= WAIT_ACK_LOW;
229 | register_address <= (others => '0');
230 | if DEBUG_OUTPUT = TRUE then
231 | after_ack_state <= DEBUG_START;
232 | else
233 | after_ack_state <= WAIT_POLL;
234 | end if;
235 | end if;
236 |
237 | -- Debug states
238 |
239 | when DEBUG_START =>
240 | if miim_ack_i = '1' then
241 | state <= DEBUG_WRITE_REGAD;
242 | debug_fifo_we_o <= '1';
243 | debug_fifo_write_data_o <= "000" & std_ulogic_vector(register_address);
244 | else
245 | miim_req_o <= '1';
246 | end if;
247 | when DEBUG_WRITE_REGAD =>
248 | debug_fifo_we_o <= '1';
249 | debug_fifo_write_data_o <= miim_data_read_i(15 downto 8);
250 | state <= DEBUG_WRITE_BYTE1;
251 | when DEBUG_WRITE_BYTE1 =>
252 | debug_fifo_we_o <= '1';
253 | debug_fifo_write_data_o <= miim_data_read_i(7 downto 0);
254 | state <= DEBUG_WRITE_BYTE2;
255 | when DEBUG_WRITE_BYTE2 =>
256 | if register_address = "11111" then
257 | register_address <= (others => '0');
258 | state <= DEBUG_DONE;
259 | else
260 | register_address <= register_address + 1;
261 | after_ack_state <= DEBUG_START;
262 | state <= WAIT_ACK_LOW;
263 | end if;
264 | when DEBUG_DONE =>
265 | --state <= WAIT_DEBUG_START;
266 | state <= WAIT_POLL;
267 |
268 | -- Auxiliary state
269 | when WAIT_ACK_LOW =>
270 | if miim_ack_i = '0' then
271 | state <= after_ack_state;
272 | end if;
273 | end case;
274 | end if;
275 | end if;
276 | end process;
277 |
278 | end architecture;
279 |
280 |
--------------------------------------------------------------------------------
/ethernet.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | library ieee;
7 | use ieee.std_logic_1164.all;
8 |
9 | -- Prebuilt Ethernet MAC without FIFOs
10 |
11 | use work.ethernet_types.all;
12 | use work.miim_types.all;
13 |
14 | entity ethernet is
15 | generic(
16 | MIIM_PHY_ADDRESS : t_phy_address := (others => '0');
17 | MIIM_RESET_WAIT_TICKS : natural := 0;
18 | MIIM_POLL_WAIT_TICKS : natural := DEFAULT_POLL_WAIT_TICKS;
19 | MIIM_CLOCK_DIVIDER : positive := 50;
20 | -- You need to supply the current speed via speed_override when MIIM is disabled
21 | MIIM_DISABLE : boolean := FALSE
22 | );
23 | port(
24 | clock_125_i : in std_ulogic;
25 | -- Reset input synchronous to miim_clock_i
26 | reset_i : in std_ulogic;
27 | -- Asynchronous reset output
28 | -- Reset may be asserted when the speed changes to get the system
29 | -- back to a defined state (glitches might occur on the clock)
30 | reset_o : out std_ulogic;
31 |
32 | -- MAC address of this station
33 | -- Must not change after reset is deasserted
34 | mac_address_i : in t_mac_address;
35 |
36 | -- MII (Media-independent interface)
37 | mii_tx_clk_i : in std_ulogic;
38 | mii_tx_er_o : out std_ulogic;
39 | mii_tx_en_o : out std_ulogic;
40 | mii_txd_o : out std_ulogic_vector(7 downto 0);
41 | mii_rx_clk_i : in std_ulogic;
42 | mii_rx_er_i : in std_ulogic;
43 | mii_rx_dv_i : in std_ulogic;
44 | mii_rxd_i : in std_ulogic_vector(7 downto 0);
45 |
46 | -- GMII (Gigabit media-independent interface)
47 | gmii_gtx_clk_o : out std_ulogic;
48 |
49 | -- RGMII (Reduced pin count gigabit media-independent interface)
50 | rgmii_tx_ctl_o : out std_ulogic;
51 | rgmii_rx_ctl_i : in std_ulogic;
52 |
53 | -- MII Management Interface
54 | miim_clock_i : in std_ulogic;
55 | mdc_o : out std_ulogic;
56 | mdio_io : inout std_ulogic;
57 | -- Status, synchronous to miim_clock_i
58 | link_up_o : out std_ulogic;
59 | speed_o : out t_ethernet_speed;
60 | -- Also synchronous to miim_clock_i if used!
61 | speed_override_i : in t_ethernet_speed := SPEED_UNSPECIFIED;
62 |
63 | -- TX from client logic
64 | tx_clock_o : out std_ulogic;
65 | -- Asynchronous reset that deasserts synchronously to tx_clock_o
66 | tx_reset_o : out std_ulogic;
67 | tx_enable_i : in std_ulogic;
68 | tx_data_i : in t_ethernet_data;
69 | tx_byte_sent_o : out std_ulogic;
70 | tx_busy_o : out std_ulogic;
71 |
72 | -- RX to client logic
73 | rx_clock_o : out std_ulogic;
74 | -- Asynchronous reset that deasserts synchronously to rx_clock_o
75 | rx_reset_o : out std_ulogic;
76 | rx_frame_o : out std_ulogic;
77 | rx_data_o : out t_ethernet_data;
78 | rx_byte_received_o : out std_ulogic;
79 | rx_error_o : out std_ulogic
80 | );
81 | end entity;
82 |
83 | architecture rtl of ethernet is
84 | signal tx_clock : std_ulogic;
85 | signal rx_clock : std_ulogic;
86 |
87 | signal reset : std_ulogic := '1';
88 | signal rx_reset : std_ulogic;
89 | signal tx_reset : std_ulogic;
90 |
91 | -- Interface between mii_gmii and framing
92 | signal mac_tx_enable : std_ulogic := '0';
93 | signal mac_tx_data : t_ethernet_data;
94 | signal mac_tx_byte_sent : std_ulogic;
95 | signal mac_tx_gap : std_ulogic;
96 | signal mac_rx_frame : std_ulogic;
97 | signal mac_rx_data : t_ethernet_data;
98 | signal mac_rx_byte_received : std_ulogic;
99 | signal mac_rx_error : std_ulogic;
100 |
101 | -- Internal MII bus between mii_gmii and mii_gmii_io
102 | signal int_mii_tx_en : std_ulogic;
103 | signal int_mii_txd : std_ulogic_vector(7 downto 0);
104 | signal int_mii_rx_er : std_ulogic;
105 | signal int_mii_rx_dv : std_ulogic;
106 | signal int_mii_rxd : std_ulogic_vector(7 downto 0);
107 |
108 | -- MIIM interconnection signals
109 | signal miim_register_address : t_register_address;
110 | signal miim_phy_address_sig : t_phy_address;
111 | signal miim_data_read : t_data;
112 | signal miim_data_write : t_data;
113 | signal miim_req : std_ulogic;
114 | signal miim_ack : std_ulogic;
115 | signal miim_wr_en : std_ulogic;
116 | signal miim_speed : t_ethernet_speed;
117 | signal speed : t_ethernet_speed;
118 | signal link_up : std_ulogic;
119 | begin
120 | reset_o <= reset;
121 | rx_reset_o <= rx_reset;
122 | tx_reset_o <= tx_reset;
123 | tx_clock_o <= tx_clock;
124 | rx_clock_o <= rx_clock;
125 |
126 | link_up_o <= link_up;
127 | speed_o <= speed;
128 | miim_phy_address_sig <= MIIM_PHY_ADDRESS;
129 | -- Errors are never transmitted in full-duplex mode
130 | mii_tx_er_o <= '0';
131 |
132 | with speed_override_i select speed <=
133 | miim_speed when SPEED_UNSPECIFIED,
134 | speed_override_i when others;
135 |
136 | -- Generate MAC reset if necessary
137 | reset_generator_inst : entity work.reset_generator
138 | port map(
139 | clock_i => miim_clock_i,
140 | speed_i => speed,
141 | reset_i => reset_i,
142 | reset_o => reset
143 | );
144 |
145 | -- Bring reset into RX and TX clock domains, using:
146 | -- * Asynchronous assertion of reset to guarantee resetting even when the MII clock is not running
147 | -- * Synchronous deassertion of reset to guarantee meeting the reset recovery time of the flip flops
148 | sync_rx_reset_inst : entity work.single_signal_synchronizer
149 | port map(
150 | clock_target_i => rx_clock,
151 | preset_i => reset,
152 | signal_i => '0',
153 | signal_o => rx_reset
154 | );
155 |
156 | sync_tx_reset_inst : entity work.single_signal_synchronizer
157 | port map(
158 | clock_target_i => tx_clock,
159 | preset_i => reset,
160 | signal_i => '0',
161 | signal_o => tx_reset
162 | );
163 |
164 | mii_gmii_inst : entity work.mii_gmii
165 | port map(
166 | rx_reset_i => rx_reset,
167 | rx_clock_i => rx_clock,
168 | tx_reset_i => tx_reset,
169 | tx_clock_i => tx_clock,
170 |
171 | -- MII (Media-independent interface)
172 | mii_tx_en_o => int_mii_tx_en,
173 | mii_txd_o => int_mii_txd,
174 | mii_rx_er_i => int_mii_rx_er,
175 | mii_rx_dv_i => int_mii_rx_dv,
176 | mii_rxd_i => int_mii_rxd,
177 |
178 | -- RGMII (Reduced pin count gigabit media-independent interface)
179 | rgmii_tx_ctl_o => open,
180 | rgmii_rx_ctl_i => '0',
181 |
182 | -- Interface control signals
183 | speed_select_i => speed,
184 | tx_enable_i => mac_tx_enable,
185 | tx_gap_i => mac_tx_gap,
186 | tx_data_i => mac_tx_data,
187 | tx_byte_sent_o => mac_tx_byte_sent,
188 | rx_frame_o => mac_rx_frame,
189 | rx_data_o => mac_rx_data,
190 | rx_byte_received_o => mac_rx_byte_received,
191 | rx_error_o => mac_rx_error
192 | );
193 |
194 | mii_gmii_io_inst : entity work.mii_gmii_io
195 | port map(
196 | clock_125_i => clock_125_i,
197 | clock_tx_o => tx_clock,
198 | clock_rx_o => rx_clock,
199 | speed_select_i => speed,
200 | mii_tx_clk_i => mii_tx_clk_i,
201 | mii_tx_en_o => mii_tx_en_o,
202 | mii_txd_o => mii_txd_o,
203 | mii_rx_clk_i => mii_rx_clk_i,
204 | mii_rx_er_i => mii_rx_er_i,
205 | mii_rx_dv_i => mii_rx_dv_i,
206 | mii_rxd_i => mii_rxd_i,
207 | gmii_gtx_clk_o => gmii_gtx_clk_o,
208 | int_mii_tx_en_i => int_mii_tx_en,
209 | int_mii_txd_i => int_mii_txd,
210 | int_mii_rx_er_o => int_mii_rx_er,
211 | int_mii_rx_dv_o => int_mii_rx_dv,
212 | int_mii_rxd_o => int_mii_rxd
213 | );
214 |
215 | framing_inst : entity work.framing
216 | port map(
217 | rx_reset_i => rx_reset,
218 | tx_clock_i => tx_clock,
219 | tx_reset_i => tx_reset,
220 | rx_clock_i => rx_clock,
221 | mac_address_i => mac_address_i,
222 | tx_enable_i => tx_enable_i,
223 | tx_data_i => tx_data_i,
224 | tx_byte_sent_o => tx_byte_sent_o,
225 | tx_busy_o => tx_busy_o,
226 | rx_frame_o => rx_frame_o,
227 | rx_data_o => rx_data_o,
228 | rx_byte_received_o => rx_byte_received_o,
229 | rx_error_o => rx_error_o,
230 | mii_tx_enable_o => mac_tx_enable,
231 | mii_tx_gap_o => mac_tx_gap,
232 | mii_tx_data_o => mac_tx_data,
233 | mii_tx_byte_sent_i => mac_tx_byte_sent,
234 | mii_rx_frame_i => mac_rx_frame,
235 | mii_rx_data_i => mac_rx_data,
236 | mii_rx_byte_received_i => mac_rx_byte_received,
237 | mii_rx_error_i => mac_rx_error
238 | );
239 |
240 | miim_gen : if MIIM_DISABLE = FALSE generate
241 | miim_inst : entity work.miim
242 | generic map(
243 | CLOCK_DIVIDER => MIIM_CLOCK_DIVIDER
244 | )
245 | port map(
246 | reset_i => reset_i,
247 | clock_i => miim_clock_i,
248 | register_address_i => miim_register_address,
249 | phy_address_i => miim_phy_address_sig,
250 | data_read_o => miim_data_read,
251 | data_write_i => miim_data_write,
252 | req_i => miim_req,
253 | ack_o => miim_ack,
254 | wr_en_i => miim_wr_en,
255 | mdc_o => mdc_o,
256 | mdio_io => mdio_io
257 | );
258 |
259 | miim_control_inst : entity work.miim_control
260 | generic map(
261 | RESET_WAIT_TICKS => MIIM_RESET_WAIT_TICKS,
262 | POLL_WAIT_TICKS => MIIM_POLL_WAIT_TICKS,
263 | DEBUG_OUTPUT => FALSE
264 | )
265 | port map(
266 | reset_i => reset_i,
267 | clock_i => miim_clock_i,
268 | miim_register_address_o => miim_register_address,
269 | miim_data_read_i => miim_data_read,
270 | miim_data_write_o => miim_data_write,
271 | miim_req_o => miim_req,
272 | miim_ack_i => miim_ack,
273 | miim_we_o => miim_wr_en,
274 | link_up_o => link_up,
275 | speed_o => miim_speed,
276 | debug_fifo_we_o => open,
277 | debug_fifo_write_data_o => open
278 | );
279 | end generate;
280 | end architecture;
281 |
282 |
--------------------------------------------------------------------------------
/miim_registers.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- MIIM register definitions
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 | use work.miim_types.all;
14 |
15 | package miim_registers is
16 | -- Register numbers
17 | constant CONTROL_REG : t_register_address := to_register_address(0);
18 | constant STATUS_REG : t_register_address := to_register_address(1);
19 | constant PHY_ID1_REG : t_register_address := to_register_address(2);
20 | constant PHY_ID2_REG : t_register_address := to_register_address(3);
21 | constant AUTONEG_ADVERTISEMENT_REG : t_register_address := to_register_address(4);
22 | constant AUTONEG_LP_BASEPAGEABILITY_REG : t_register_address := to_register_address(5);
23 | constant AUTONEG_EXPANSION_REG : t_register_address := to_register_address(6);
24 | constant AUTONEG_NEXTPAGETX_REG : t_register_address := to_register_address(7);
25 | constant AUTONEG_LP_NEXTPAGERECV_REG : t_register_address := to_register_address(8);
26 | constant MASTERSLAVE_CTRL_REG : t_register_address := to_register_address(9);
27 | constant MASTERSLAVE_STATUS_REG : t_register_address := to_register_address(10);
28 | constant PSE_CONTROL_REG : t_register_address := to_register_address(11);
29 | constant PSE_STATUS_REG : t_register_address := to_register_address(12);
30 | constant MMD_ACCESSCONTROL_REG : t_register_address := to_register_address(13);
31 | constant MMD_ACCESSADDRESSDATA_REG : t_register_address := to_register_address(14);
32 | constant EXTENDED_STATUS_REG : t_register_address := to_register_address(15);
33 | constant VENDOR_SPECIFIC_REG_BASE : t_register_address := to_register_address(16);
34 |
35 | -- Register contents of selected registers
36 | -- See Ethernet specification for the meaning of the fields
37 | type t_control_register is record
38 | reset : std_ulogic;
39 | loopback : std_ulogic;
40 | speed_10_100 : std_ulogic;
41 | speed_1000 : std_ulogic;
42 | auto_negotiation_enable : std_ulogic;
43 | power_down : std_ulogic;
44 | isolate : std_ulogic;
45 | restart_auto_negotiation : std_ulogic;
46 | duplex_mode : std_ulogic;
47 | enable_collision_test : std_ulogic;
48 | unidirectional_enable : std_ulogic;
49 | end record;
50 |
51 | type t_status_register is record
52 | can_100base_t4 : std_ulogic;
53 | can_100base_x_fd : std_ulogic;
54 | can_100base_x_hd : std_ulogic;
55 | can_10mbps_fd : std_ulogic;
56 | can_10mbps_hd : std_ulogic;
57 | can_100base_t2_fd : std_ulogic;
58 | can_100base_t2_hd : std_ulogic;
59 | extended_status : std_ulogic;
60 | undirectional_ability : std_ulogic;
61 | mf_preamble_suppression : std_ulogic;
62 | auto_negotiation_complete : std_ulogic;
63 | remote_fault : std_ulogic;
64 | auto_negotiation_ability : std_ulogic;
65 | link_status : std_ulogic;
66 | jabber_detect : std_ulogic;
67 | extended_capability : std_ulogic;
68 | end record;
69 |
70 | type t_auto_negotiation_advertisement_register_802_3 is record
71 | next_page : std_ulogic;
72 | remote_fault : std_ulogic;
73 | extended_next_page : std_ulogic;
74 | asymmetric_pause : std_ulogic;
75 | pause : std_ulogic;
76 | advertise_100base_t4 : std_ulogic;
77 | advertise_100base_tx_fd : std_ulogic;
78 | advertise_100base_tx_hd : std_ulogic;
79 | advertise_10base_t_fd : std_ulogic;
80 | advertise_10base_t_hd : std_ulogic;
81 | end record;
82 |
83 | type auto_negotiation_lp_base_page_ability_register_802_3_t is record
84 | next_page : std_ulogic;
85 | acknowledge : std_ulogic;
86 | remote_fault : std_ulogic;
87 | extended_next_page : std_ulogic;
88 | asymmetric_pause : std_ulogic;
89 | pause : std_ulogic;
90 | can_100base_t4 : std_ulogic;
91 | can_100base_tx_fd : std_ulogic;
92 | can_100base_tx_hd : std_ulogic;
93 | can_10base_t_fd : std_ulogic;
94 | can_10base_t_hd : std_ulogic;
95 | end record;
96 |
97 | type t_master_slave_control_register is record
98 | test_mode_bits : std_ulogic_vector(2 downto 0);
99 | master_slave_manual_config_enable : std_ulogic;
100 | master_slave_manual_config_value : std_ulogic;
101 | port_type_is_multiport : std_ulogic;
102 | advertise_1000base_t_fd : std_ulogic;
103 | advertise_1000base_t_hd : std_ulogic;
104 | end record;
105 |
106 | type t_master_slave_status_register is record
107 | master_slave_config_fault : std_ulogic;
108 | master_slave_config_resolution : std_ulogic;
109 | local_receiver_status : std_ulogic;
110 | remote_receiver_status : std_ulogic;
111 | lp_1000base_t_fd : std_ulogic;
112 | lp_1000base_t_hd : std_ulogic;
113 | idle_error_count : unsigned(7 downto 0);
114 | end record;
115 |
116 | constant AUTO_NEGOTATION_802_3_SELECTOR : std_ulogic_vector(4 downto 0) := "00001";
117 |
118 | -- Selected conversion functions
119 | -- Register records to 16-bit data values
120 | function control_register_to_data(reg : in t_control_register) return t_data;
121 | function auto_negotiation_advertisement_register_802_3_to_data(reg : in t_auto_negotiation_advertisement_register_802_3) return t_data;
122 | function master_slave_control_register_to_data(reg : in t_master_slave_control_register) return t_data;
123 |
124 | -- 16-bit data values to registers
125 | function data_to_status_register(data : in t_data) return t_status_register;
126 | function data_to_master_slave_status_register(data : in t_data) return t_master_slave_status_register;
127 | function data_to_auto_negotiation_lp_base_page_ability_register(data : in t_data) return auto_negotiation_lp_base_page_ability_register_802_3_t;
128 |
129 | end package;
130 |
131 | package body miim_registers is
132 | function control_register_to_data(reg : in t_control_register) return t_data is
133 | variable data : t_data;
134 | begin
135 | data := (
136 | 15 => reg.reset,
137 | 14 => reg.loopback,
138 | 13 => reg.speed_10_100,
139 | 12 => reg.auto_negotiation_enable,
140 | 11 => reg.power_down,
141 | 10 => reg.isolate,
142 | 9 => reg.restart_auto_negotiation,
143 | 8 => reg.duplex_mode,
144 | 7 => reg.enable_collision_test,
145 | 6 => reg.speed_1000,
146 | 5 => reg.unidirectional_enable,
147 | others => '0'
148 | );
149 | return data;
150 | end function;
151 |
152 | function auto_negotiation_advertisement_register_802_3_to_data(reg : in t_auto_negotiation_advertisement_register_802_3) return t_data is
153 | variable data : t_data;
154 | begin
155 | data := (
156 | 15 => reg.next_page,
157 | 13 => reg.remote_fault,
158 | 12 => reg.extended_next_page,
159 | 11 => reg.asymmetric_pause,
160 | 10 => reg.pause,
161 | 9 => reg.advertise_100base_t4,
162 | 8 => reg.advertise_100base_tx_fd,
163 | 7 => reg.advertise_100base_tx_hd,
164 | 6 => reg.advertise_10base_t_fd,
165 | 5 => reg.advertise_10base_t_hd,
166 | others => '0'
167 | );
168 | data(4 downto 0) := AUTO_NEGOTATION_802_3_SELECTOR;
169 | return data;
170 | end function;
171 |
172 | function master_slave_control_register_to_data(reg : in t_master_slave_control_register) return t_data is
173 | variable data : t_data;
174 | begin
175 | data := (
176 | 12 => reg.master_slave_manual_config_enable,
177 | 11 => reg.master_slave_manual_config_value,
178 | 10 => reg.port_type_is_multiport,
179 | 9 => reg.advertise_1000base_t_fd,
180 | 8 => reg.advertise_1000base_t_hd,
181 | others => '0'
182 | );
183 | data(15 downto 13) := reg.test_mode_bits;
184 | return data;
185 | end function;
186 |
187 | function data_to_status_register(data : in t_data) return t_status_register is
188 | variable status : t_status_register;
189 | begin
190 | status := (
191 | can_100base_t4 => data(15),
192 | can_100base_x_fd => data(14),
193 | can_100base_x_hd => data(13),
194 | can_10mbps_fd => data(12),
195 | can_10mbps_hd => data(11),
196 | can_100base_t2_fd => data(10),
197 | can_100base_t2_hd => data(9),
198 | extended_status => data(8),
199 | undirectional_ability => data(7),
200 | mf_preamble_suppression => data(6),
201 | auto_negotiation_complete => data(5),
202 | remote_fault => data(4),
203 | auto_negotiation_ability => data(3),
204 | link_status => data(2),
205 | jabber_detect => data(1),
206 | extended_capability => data(0)
207 | );
208 | return status;
209 | end function;
210 |
211 | function data_to_master_slave_status_register(data : in t_data) return t_master_slave_status_register is
212 | variable status : t_master_slave_status_register;
213 | begin
214 | status := (
215 | master_slave_config_fault => data(15),
216 | master_slave_config_resolution => data(14),
217 | local_receiver_status => data(13),
218 | remote_receiver_status => data(12),
219 | lp_1000base_t_fd => data(11),
220 | lp_1000base_t_hd => data(10),
221 | idle_error_count => unsigned(data(7 downto 0))
222 | );
223 | return status;
224 | end function;
225 |
226 | function data_to_auto_negotiation_lp_base_page_ability_register(data : in t_data) return auto_negotiation_lp_base_page_ability_register_802_3_t is
227 | variable ability : auto_negotiation_lp_base_page_ability_register_802_3_t;
228 | begin
229 | ability := (
230 | next_page => data(15),
231 | acknowledge => data(14),
232 | remote_fault => data(13),
233 | extended_next_page => data(12),
234 | asymmetric_pause => data(11),
235 | pause => data(10),
236 | can_100base_t4 => data(9),
237 | can_100base_tx_fd => data(8),
238 | can_100base_tx_hd => data(7),
239 | can_10base_t_fd => data(6),
240 | can_10base_t_hd => data(5)
241 | );
242 | return ability;
243 | end function;
244 |
245 | end package body;
246 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 10/100/1000 VHDL Ethernet MAC
2 |
3 | This tri-mode full-duplex Ethernet MAC sublayer was developed in VHDL as an alternative to both commercial and free implementations for usage on FPGAs. Its main distinction is the focus on simplicity both in the external user interface and internal operation. Only essential Ethernet functionality is supported.
4 |
5 | The core fully works on Xilinx Spartan 6 family FPGAs only at the moment. It was verified on hardware on a Trenz Electronic GmbH TE0600 GigaBee micromodule with a TE0603 baseboard.
6 |
7 | The user interface is comprised of two FIFOs with a 8-bit data bus for packet transmission and reception, respectively.
8 |
9 | This page is a short overview of the features and usage of the MAC. More information on the design and implementation can be found in the design document at .
10 |
11 | Features
12 | -------
13 | Finished:
14 |
15 | - **Full-duplex IEEE 802.3 communication** on copper at 10 (10BASE-T), 100 (100BASE-TX), and 1000 (1000BASE-T) Mb/s physical link speed
16 | - Achievable **data rate very close to theoretical maximum**
17 | - Generation of Ethernet preamble and frame check sequence on transmission
18 | - Verification of received packets (preamble, size, frame check sequence)
19 | - Filtering received packets by destination MAC address
20 | - MAC address insertion into source address of outgoing packets (only when the first source address byte in the packet stream is `0xFF`)
21 | - **Simple 8 bit wide FIFO user interface** with arbitrary clock domains for packet transmission and reception
22 | - **Media-independent interface (MII)** for 10/100 Mb/s and **gigabit media-independent interface (GMII)** connectivity
23 | - MII/GMII hardware I/O setup for Xilinx Spartan 6 family FPGAs
24 | - Basic **media-independent interface management (MIIM) interface support** that:
25 | - Configures the PHY auto-negotiation to use full-duplex modes only
26 | - Polls the current PHY link state and speed by reading the status and auto-negotiation registers
27 |
28 | Decisively not supported:
29 |
30 | - CSMA/CD (which effectively means **no half-duplex connections**)
31 | - Flow control with PAUSE frames
32 | - Energy-efficient Ethernet
33 | - Multicast reception filter
34 | - On-chip buses (can be integrated with custom code if needed)
35 | - Runtime configuration of core features and parameters
36 | - Manual link speed configuration via MIIM
37 | - Packet counters/statistics
38 |
39 | Ideas for the future:
40 |
41 | - Replacement of the TX FIFO that is currently generated by the Xilinx core generator with a custom VHDL implementation
42 | - Reduced gigabit media-independent interface (RGMII) support
43 |
44 | Preparation
45 | -------
46 | Using the core currently requires an installation of Xilinx ISE 14.7 (WebPack will work) for the TX FIFO generation.
47 | In preparation for both running the testbench and using the MAC in a project, please follow these steps:
48 |
49 | - Open the project file `ethernet_mac.xise` in the ISE project navigator
50 | - Select the root node "xc6slx45-2fgg484" in the hierarchy view
51 | - Run the "Regenerate All Cores" process under "Design Utilities"
52 |
53 | Verifying the design
54 | -------
55 | The source code includes the self-checking testbench entity `ethernet_mac_tb`.
56 |
57 | If you have [GHDL](http://ghdl.free.fr/) and make installed, you can start a basic functional verification in a behavioral simulation of the core by simply running
58 |
59 | $ make prepare ISE_DIR=/path/to/14.7/ISE_DS/ISE
60 | $ make check
61 | If everything works as expected, the last output line should read `MAC functional check ended OK`. Note that by default only a reduced set of tests is performed, you can modify the `TEST_THOROUGH` generic in `ethernet_mac_tb.vhd` to run the full test suite.
62 | ModelSim also works fine when you have the Xilinx libraries correctly imported. ISim will not unfortunately as it needs excessive amounts of system RAM when running the testbench.
63 |
64 | Post-synthesis verification is also supported with the XC6SLX45-2FGG484 FPGA as sample target device. To get the simulation model, select the `test_instance_spartan6` top module in the ISE hierarchy view and run the "Generate Post-Place & Route Simulation Model" process under "Implement Design" / "Place & Route" (or Post-Translate/Post-Map if desired). It was only tested in ModelSim, but other simulators are expected to work as well. You only need the following files for post-synthesis simulation (and a correctly linked Xilinx ISE `simprim` VHDL library):
65 |
66 | - `crc.vhd`
67 | - `crc32.vhd`
68 | - `ethernet_mac_tb.vhd`
69 | - `ethernet_types.vhd`
70 | - `framing_common.vhd`
71 | - `test/test_common.vhd`
72 | - `utility.vhd`
73 | - `xilinx/test/test_configuration_spartan6.vhd`
74 | - `xilinx/test/test_wrapper_spartan6.vhd`
75 | - `netgen/par/test_instance_spartan6_timesim.vhd`
76 |
77 | Start the `work.post_synthesis_spartan6` configuration then (*not* `work.ethernet_mac_tb`). When using ModelSim, you must set the time resolution to ps or finer. You might also want to set the `TEST_MII_SETUPHOLD` generic to `TRUE` for additional setup/hold time simulation. If you use the SDF file at `netgen/par/test_instance_spartan6_timesim.sdf` for delay modeling, apply it to the region `/ethernet_mac_inst/test_instance_inst`.
78 |
79 | To test the design on actual hardware, follow the instructions in the [ethernet\_mac\_test](https://github.com/yol/ethernet_mac_test) (benchmark) or [Chips-Demo](https://github.com/yol/Chips-Demo) (example webserver user application) project.
80 |
81 | **Please note that while this design includes a testbench for most functionality and is proven in practice, it is still the result of a Bachelor's thesis and not a professional product. As such, all usage is at your own responsibility. This is especially true if you use the source code or any derivative product to operate nuclear facilities, life support, or other mission critical applications where human life or property may be at stake or endangered. See also the [license and disclaimer](LICENSE.md).**
82 |
83 | Using the MAC
84 | -------------
85 | Basic project setup with Xilinx ISE 14.7:
86 |
87 | - Open your own project in the ISE project navigator
88 | - Open the Libraries panel
89 | - Right-click and select "New VHDL Library"
90 | - Enter `ethernet_mac` as the "New VHDL Library Name" and select the folder you cloned this repository to as "Library Files Location"
91 | - Click "OK" in the dialog and the one popping up directly after it
92 | - Right-click on the newly added library, select "Add Source", and add the following files from the repository (always click "OK" in the "Adding Source Files" dialog without changing anything):
93 | - All `.vhd` files in the `xilinx` directory
94 | - `xilinx/ipcore_dir/ethernet_mac_tx_fifo_xilinx.xco`
95 | - Add the timing constraints found in `xilinx/test/test_instance_spartan6.ucf` to your own constraints file. You might need to adapt the pin names to your design.
96 |
97 | The entity you will usually want to instantiate is `ethernet_with_fifos`. It includes all modules relevant for Ethernet operation and FIFOs for packet TX and RX. You need to include the following code in the head of your VHDL file to use the custom data types:
98 |
99 | library ethernet_mac;
100 | use ethernet_mac.ethernet_types.all;
101 |
102 | The ports are as follows:
103 |
104 | Port | Function
105 | -------- | ---
106 | `clock_125_i` | 125 MHz *unbuffered* reference clock for GMII TX operation
107 | `reset_i` | Active-high asynchronous reset for the whole core
108 | `mac_address_i` | MAC address of the local entity, should not change after `reset_i` is deasserted
109 | `mii_tx_clk_i` | MII `TX_CLK`
110 | `mii_tx_er_o` | MII `TX_ER`
111 | `mii_tx_en_o` | MII `TX_EN`
112 | `mii_txd_o` | MII `TXD`
113 | `mii_rx_clk_i` | MII `RX_CLK`
114 | `mii_rx_er_i` | MII `RX_ER`
115 | `mii_rx_dv_i` | MII `RX_DV`
116 | `mii_rxd_i` | MII `RXD`
117 | `gmii_gtx_clk_o` | GMII `GTX_CLK`
118 | `miim_clock_i` | Clock used for MIIM operation
119 | `mdc_o` | MIIM `MDC`
120 | `mdio_io` | MIIM `MDIO`
121 | `link_up_o` | Link status indicator synchronous to `miim_clock_i`
122 | `speed_o` | Link speed indicator synchronous to `miim_clock_i`
123 | `speed_override_i` | Overrides the speed detected via MIIM if supplied, must be synchronous to `miim_clock_i`
124 | `tx_clock_i` | TX FIFO clock
125 | `tx_reset_o` | Synchronous reset for all logic using the TX FIFO
126 | `tx_data_i` | TX FIFO data
127 | `tx_wr_en_i` | TX FIFO write enable
128 | `tx_full_o` | TX FIFO full indication
129 | `rx_clock_i` | RX FIFO clock
130 | `rx_reset_o` | Synchronous reset for all logic using the RX FIFO
131 | `rx_empty_o` | RX FIFO empty indication
132 | `rx_rd_en_i` | RX FIFO read enable
133 | `rx_data_o` | RX FIFO data
134 |
135 | The generics:
136 |
137 | Generic | Function
138 | ------- | ------
139 | `MIIM_PHY_ADDRESS` | MIIM address of the PHY (refer to its data sheet to see how this is configured on the PHY side)
140 | `MIIM_RESET_WAIT_TICKS ` | Number of `miim_clock_i` cycles to wait until accessing registers after reset is deasserted
141 | `MIIM_POLL_WAIT_TICKS` | Number of `miim_clock_i` cycles to wait between polling the status and auto-negotiation registers
142 | `MIIM_CLOCK_DIVIDER` | Clock divider between `miim_clock_i` and `mdc_o`. Make sure that the frequency of `mdc_o` is at most 2.5 MHz for IEEE 802.3 compatibility.
143 | `MIIM_DISABLE` | Completely disable MIIM functionality when `TRUE`. You *have* to supply the current link speed on `speed_override_i` then!
144 | `RX_FIFO_SIZE_BITS` | Set the size of the RX FIFO in powers of 2 by overriding the number of size bits
145 |
146 | Connect all MII, GMII, and MIIM pins directly to the pads. `miim_clock_i`, `rx_clock_i` and `tx_clock_i` can be identical if desired. The default values for `MIIM_CLOCK_DIVIDER` and `MIIM_POLL_WAIT_TICKS` are calculated for a MIIM clock of 125 MHz.
147 |
148 | Both the TX and the RX FIFO have a simple communication scheme. A packet unit on the FIFO interface consists of: 2 bytes of size information, most significant byte first, and then exactly as many bytes of data as were indicated. The data includes all Ethernet MAC headers (destination address, source address, and length/type) in the exact same order as defined in the standard, but not the frame check sequence trailer. The exact timing follows the interface of first-word-fall-through FIFOs generated by the Xilinx core generator but is generally not different from a "normal" FIFO interface. Refer to the FIFO generator user guide for details.
149 | You can take a look at the [Chips-Demo file `chips_mac_adaptor.vhd`](https://github.com/yol/Chips-Demo/blob/master/source/chips_mac_adaptor.vhd) for an example of how an application might use the core.
150 |
151 | Note that you might have to override the default input delays on the GMII RX path to your device in the constraints file e.g. using PlanAhead. It is expected that the strict GMII timing constraints can not be met completely under all circumstances.
152 |
153 | > Written with [StackEdit](https://stackedit.io/).
154 |
--------------------------------------------------------------------------------
/framing.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- MAC sublayer functionality (en-/decapsulation, FCS, IPG)
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 | use work.framing_common.all;
14 | use work.crc32.all;
15 | use work.utility.all;
16 |
17 | entity framing is
18 | port(
19 | tx_reset_i : in std_ulogic;
20 | tx_clock_i : in std_ulogic;
21 | rx_reset_i : in std_ulogic;
22 | rx_clock_i : in std_ulogic;
23 |
24 | -- MAC address of this station
25 | -- Must not change after either reset is deasserted
26 | -- Used for
27 | -- * dropping received packets when the destination address differs from both this one and the broadcast address
28 | -- * inserting as source address in transmitted packets when the first byte of the source address in the data stream is all-ones
29 | mac_address_i : in t_mac_address;
30 |
31 | -- For details on the signals, see the port list of mii_gmii
32 |
33 | -- TX from client logic
34 | -- The length/type field is considered part of the data!
35 | -- It is not interpreted by the framing layer at all.
36 | tx_enable_i : in std_ulogic;
37 | tx_data_i : in t_ethernet_data;
38 | tx_byte_sent_o : out std_ulogic;
39 | -- Do not start new frames while asserted
40 | -- (continuing the previous one is alright)
41 | tx_busy_o : out std_ulogic;
42 |
43 | -- RX to client logic
44 | rx_frame_o : out std_ulogic;
45 | rx_data_o : out t_ethernet_data;
46 | rx_byte_received_o : out std_ulogic;
47 | rx_error_o : out std_ulogic;
48 |
49 | -- TX to MII
50 | mii_tx_enable_o : out std_ulogic;
51 | mii_tx_data_o : out t_ethernet_data;
52 | mii_tx_byte_sent_i : in std_ulogic;
53 | mii_tx_gap_o : out std_ulogic;
54 |
55 | -- RX from MII
56 | mii_rx_frame_i : in std_ulogic;
57 | mii_rx_data_i : in t_ethernet_data;
58 | mii_rx_byte_received_i : in std_ulogic;
59 | mii_rx_error_i : in std_ulogic
60 | );
61 | end entity;
62 |
63 | architecture rtl of framing is
64 | -- Transmission
65 | type t_tx_state is (
66 | TX_IDLE,
67 | -- TX_PREMABLE1 is not needed: first preamble byte is transmitted directly in TX_IDLE when start of transmission
68 | -- is detected.
69 | TX_PREAMBLE2,
70 | TX_PREAMBLE3,
71 | TX_PREAMBLE4,
72 | TX_PREAMBLE5,
73 | TX_PREAMBLE6,
74 | TX_PREAMBLE7,
75 | TX_START_FRAME_DELIMITER,
76 | TX_CLIENT_DATA_WAIT_SOURCE_ADDRESS,
77 | TX_SOURCE_ADDRESS,
78 | TX_CLIENT_DATA,
79 | TX_PAD,
80 | TX_FRAME_CHECK_SEQUENCE2,
81 | TX_FRAME_CHECK_SEQUENCE3,
82 | TX_FRAME_CHECK_SEQUENCE4,
83 | TX_INTERPACKET_GAP
84 | );
85 |
86 | signal tx_state : t_tx_state := TX_IDLE;
87 | signal tx_frame_check_sequence : t_crc32;
88 | signal tx_padding_required : natural range 0 to MIN_FRAME_DATA_BYTES + 4 + 1 := 0;
89 | signal tx_interpacket_gap_counter : integer range 0 to INTERPACKET_GAP_BYTES;
90 | signal tx_mac_address_byte : integer range 0 to MAC_ADDRESS_BYTES;
91 |
92 | -- Reception
93 | type t_rx_state is (
94 | RX_WAIT_START_FRAME_DELIMITER,
95 | RX_DATA,
96 | RX_ERROR,
97 | RX_SKIP_FRAME
98 | );
99 |
100 | signal rx_state : t_rx_state := RX_WAIT_START_FRAME_DELIMITER;
101 | signal rx_frame_check_sequence : t_crc32;
102 | subtype t_rx_frame_size is natural range 0 to MAX_FRAME_DATA_BYTES + CRC32_BYTES + 1;
103 | signal rx_frame_size : t_rx_frame_size;
104 | signal rx_is_group_address : std_ulogic;
105 | signal rx_mac_address_byte : integer range 0 to MAC_ADDRESS_BYTES;
106 |
107 | begin
108 | -- Pass mii_tx_byte_sent_i through directly as long as data is being transmitted
109 | -- to avoid having to prefetch data in the synchronous process
110 | tx_byte_sent_o <= '1' when ((tx_state = TX_CLIENT_DATA or tx_state = TX_CLIENT_DATA_WAIT_SOURCE_ADDRESS or tx_state = TX_SOURCE_ADDRESS) and mii_tx_byte_sent_i = '1') else '0';
111 |
112 | -- Transmission state machine
113 | tx_fsm_sync : process(tx_reset_i, tx_clock_i)
114 | variable update_fcs : boolean;
115 | variable data_out : t_ethernet_data;
116 | begin
117 | if tx_reset_i = '1' then
118 | tx_state <= TX_IDLE;
119 | mii_tx_enable_o <= '0';
120 | tx_busy_o <= '1';
121 | elsif rising_edge(tx_clock_i) then
122 | mii_tx_enable_o <= '0';
123 | tx_busy_o <= '0';
124 | if tx_state = TX_IDLE then
125 | if tx_enable_i = '1' then
126 | -- Jump straight into preamble to save a clock cycle of latency
127 | tx_state <= TX_PREAMBLE2;
128 | mii_tx_data_o <= PREAMBLE_DATA;
129 | mii_tx_enable_o <= '1';
130 | mii_tx_gap_o <= '0';
131 | tx_busy_o <= '1';
132 | end if;
133 | else
134 | -- Keep TX enable and busy asserted at all times
135 | mii_tx_enable_o <= '1';
136 | tx_busy_o <= '1';
137 |
138 | -- Use mii_tx_byte_sent_i as clock enable
139 | if mii_tx_byte_sent_i = '1' then
140 | mii_tx_gap_o <= '0';
141 | data_out := (others => '0');
142 | update_fcs := FALSE;
143 |
144 | case tx_state is
145 | when TX_IDLE =>
146 | -- Handled above, cannot happen here
147 | null;
148 | when TX_PREAMBLE2 | TX_PREAMBLE3 | TX_PREAMBLE4 | TX_PREAMBLE5 | TX_PREAMBLE6 =>
149 | tx_state <= t_tx_state'succ(tx_state);
150 | data_out := PREAMBLE_DATA;
151 | when TX_PREAMBLE7 =>
152 | tx_state <= TX_START_FRAME_DELIMITER;
153 | data_out := PREAMBLE_DATA;
154 | when TX_START_FRAME_DELIMITER =>
155 | tx_state <= TX_CLIENT_DATA_WAIT_SOURCE_ADDRESS;
156 | data_out := START_FRAME_DELIMITER_DATA;
157 | -- Load padding register
158 | tx_padding_required <= MIN_FRAME_DATA_BYTES;
159 | -- Load FCS
160 | -- Initial value is 0xFFFFFFFF which is equivalent to inverting the first 32 bits of the frame
161 | -- as required in clause 3.2.9 a
162 | tx_frame_check_sequence <= (others => '1');
163 | -- Load MAC address counter
164 | tx_mac_address_byte <= 0;
165 | when TX_CLIENT_DATA_WAIT_SOURCE_ADDRESS =>
166 | data_out := tx_data_i;
167 | update_fcs := TRUE;
168 | -- Skip destination address
169 | if tx_mac_address_byte < MAC_ADDRESS_BYTES then
170 | tx_mac_address_byte <= tx_mac_address_byte + 1;
171 | else
172 | -- All-ones means that we should insert the source address here
173 | if tx_data_i = x"FF" then
174 | tx_state <= TX_SOURCE_ADDRESS;
175 | -- Override client data with first source address byte
176 | data_out := extract_byte(mac_address_i, 0);
177 | -- Second byte is to be sent in next cycle
178 | tx_mac_address_byte <= 1;
179 | else
180 | -- Transmit as usual, skip TX_SOURCE_ADDRESS
181 | tx_state <= TX_CLIENT_DATA;
182 | end if;
183 | end if;
184 |
185 | -- Bail out from here if transmission was aborted
186 | -- Note that this should not happen under normal circumstances as the
187 | -- Ethernet frame would be far too short.
188 | if tx_enable_i = '0' then
189 | tx_state <= TX_PAD;
190 | data_out := PADDING_DATA;
191 | end if;
192 | when TX_SOURCE_ADDRESS =>
193 | data_out := extract_byte(mac_address_i, tx_mac_address_byte);
194 | update_fcs := TRUE;
195 | if tx_mac_address_byte < MAC_ADDRESS_BYTES - 1 then
196 | tx_mac_address_byte <= tx_mac_address_byte + 1;
197 | else
198 | -- Address completely sent when tx_mac_address_byte reaches 5
199 | -- Pass on client data again in next cycle
200 | tx_state <= TX_CLIENT_DATA;
201 | end if;
202 | when TX_CLIENT_DATA =>
203 | data_out := tx_data_i;
204 | update_fcs := TRUE;
205 | if tx_enable_i = '0' then
206 | -- No more user data was available, next value has to be sent
207 | -- in this clock cycle already
208 | if tx_padding_required = 0 then
209 | -- Send FCS byte 1 now, byte 2 in next cycle
210 | tx_state <= TX_FRAME_CHECK_SEQUENCE2;
211 | data_out := fcs_output_byte(tx_frame_check_sequence, 0);
212 | update_fcs := FALSE;
213 | else
214 | tx_state <= TX_PAD;
215 | data_out := PADDING_DATA;
216 | end if;
217 | end if;
218 | when TX_PAD =>
219 | data_out := PADDING_DATA;
220 | update_fcs := TRUE;
221 | if tx_padding_required = 0 then
222 | -- When required=0, previous one was the last one -> send FCS
223 | tx_state <= TX_FRAME_CHECK_SEQUENCE2;
224 | data_out := fcs_output_byte(tx_frame_check_sequence, 0);
225 | update_fcs := FALSE;
226 | end if;
227 | when TX_FRAME_CHECK_SEQUENCE2 =>
228 | tx_state <= t_tx_state'succ(tx_state);
229 | data_out := fcs_output_byte(tx_frame_check_sequence, 1);
230 | when TX_FRAME_CHECK_SEQUENCE3 =>
231 | tx_state <= t_tx_state'succ(tx_state);
232 | data_out := fcs_output_byte(tx_frame_check_sequence, 2);
233 | when TX_FRAME_CHECK_SEQUENCE4 =>
234 | tx_state <= TX_INTERPACKET_GAP;
235 | data_out := fcs_output_byte(tx_frame_check_sequence, 3);
236 | -- Load IPG counter with initial value
237 | tx_interpacket_gap_counter <= 0;
238 | when TX_INTERPACKET_GAP =>
239 | -- Only state where the MAC is still busy but no data is actually sent
240 | mii_tx_gap_o <= '1';
241 | if tx_interpacket_gap_counter = INTERPACKET_GAP_BYTES - 1 then
242 | -- Last IPG byte is transmitted in this cycle
243 | tx_state <= TX_IDLE;
244 | else
245 | tx_interpacket_gap_counter <= tx_interpacket_gap_counter + 1;
246 | end if;
247 | end case;
248 |
249 | mii_tx_data_o <= data_out;
250 | if update_fcs then
251 | tx_frame_check_sequence <= update_crc32(tx_frame_check_sequence, data_out);
252 | end if;
253 |
254 | if tx_state = TX_CLIENT_DATA_WAIT_SOURCE_ADDRESS or tx_state = TX_SOURCE_ADDRESS or tx_state = TX_CLIENT_DATA or tx_state = TX_PAD then
255 | -- Decrement required padding
256 | if tx_padding_required > 0 then
257 | tx_padding_required <= tx_padding_required - 1;
258 | end if;
259 | end if;
260 | end if;
261 | end if;
262 | end if;
263 | end process;
264 |
265 | -- Reception state machine
266 | rx_fsm_sync : process(rx_reset_i, rx_clock_i)
267 | begin
268 | if rx_reset_i = '1' then
269 | rx_state <= RX_WAIT_START_FRAME_DELIMITER;
270 | elsif rising_edge(rx_clock_i) then
271 | rx_error_o <= '0';
272 | rx_data_o <= mii_rx_data_i;
273 | rx_byte_received_o <= '0';
274 | rx_frame_o <= '0';
275 |
276 | case rx_state is
277 | when RX_WAIT_START_FRAME_DELIMITER =>
278 | -- Reset MAC address detection
279 | rx_mac_address_byte <= 0;
280 | rx_is_group_address <= '1';
281 | -- Reset frame size and FCS
282 | rx_frame_size <= 0;
283 | -- Initial value is 0xFFFFFFFF which is equivalent to inverting the first 32 bits of the frame
284 | -- as required in clause 3.2.9 a
285 | rx_frame_check_sequence <= (others => '1');
286 |
287 | if mii_rx_frame_i = '1' then
288 | if mii_rx_byte_received_i = '1' then
289 | case mii_rx_data_i is
290 | when START_FRAME_DELIMITER_DATA =>
291 | rx_state <= RX_DATA;
292 | when PREAMBLE_DATA =>
293 | -- Do nothing, wait for end of preamble
294 | null;
295 | when others =>
296 | -- The frame needs to be thrown away, but there is no need to
297 | -- inform the higher layer since nothing of value was actually "received" anyway.
298 | rx_state <= RX_SKIP_FRAME;
299 | end case;
300 | end if;
301 | if mii_rx_error_i = '1' then
302 | -- Same here
303 | rx_state <= RX_SKIP_FRAME;
304 | end if;
305 | end if;
306 | when RX_DATA =>
307 | rx_frame_o <= '1';
308 | rx_byte_received_o <= mii_rx_byte_received_i;
309 | if mii_rx_frame_i = '0' then
310 | rx_state <= RX_WAIT_START_FRAME_DELIMITER;
311 | -- Remaining FCS after parsing whole packet + FCS needs to be a specific value
312 | if mii_rx_error_i = '1' or rx_frame_check_sequence /= CRC32_POSTINVERT_MAGIC or rx_frame_size < MIN_FRAME_DATA_BYTES + CRC32_BYTES or rx_frame_size > MAX_FRAME_DATA_BYTES + CRC32_BYTES then
313 | rx_error_o <= '1';
314 | end if;
315 | else
316 | if mii_rx_byte_received_i = '1' then
317 | -- Update FCS check
318 | rx_frame_check_sequence <= update_crc32(rx_frame_check_sequence, mii_rx_data_i);
319 | -- Increase frame size
320 | if rx_frame_size < t_rx_frame_size'high then
321 | rx_frame_size <= rx_frame_size + 1;
322 | end if;
323 | -- Check destination MAC address (first 6 bytes of packet)
324 | if rx_mac_address_byte < MAC_ADDRESS_BYTES then
325 | -- First byte determines whether the address is an individual or group address
326 | if rx_mac_address_byte = 0 then
327 | if mii_rx_data_i(0) = '0' then
328 | -- LSB of the address is zero: packet is destined for an individual entity
329 | rx_is_group_address <= '0';
330 | -- Check first address byte
331 | if mii_rx_data_i /= extract_byte(mac_address_i, rx_mac_address_byte) then
332 | -- Packet is not destined for us -> drop it
333 | rx_state <= RX_ERROR;
334 | end if;
335 | end if;
336 | -- If not: It is a group address packet -> do not drop it and do not check the address further
337 | elsif rx_is_group_address = '0' then
338 | -- Check other MAC address bytes only if we know it doesn't have a group destination address
339 | if mii_rx_data_i /= extract_byte(mac_address_i, rx_mac_address_byte) then
340 | -- Packet is not destined for us -> drop it
341 | rx_state <= RX_ERROR;
342 | end if;
343 | end if;
344 |
345 | rx_mac_address_byte <= rx_mac_address_byte + 1;
346 | end if;
347 | end if;
348 | if mii_rx_error_i = '1' then
349 | -- Skip the rest of the frame and tell the higher layer
350 | rx_state <= RX_ERROR;
351 | end if;
352 | end if;
353 | when RX_SKIP_FRAME =>
354 | -- Skip the currently receiving frame without signaling the higher layer
355 | if mii_rx_frame_i = '0' then
356 | rx_state <= RX_WAIT_START_FRAME_DELIMITER;
357 | end if;
358 | when RX_ERROR =>
359 | -- Skip the currently receiving frame and signal the higher layer
360 | rx_frame_o <= '1';
361 | rx_error_o <= '1';
362 | if mii_rx_frame_i = '0' then
363 | rx_state <= RX_WAIT_START_FRAME_DELIMITER;
364 | end if;
365 | end case;
366 | end if;
367 | end process;
368 |
369 | end architecture;
370 |
--------------------------------------------------------------------------------
/rx_fifo.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Storage for packet reception with FIFO user interface
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 | use work.crc32.all;
14 |
15 | entity rx_fifo is
16 | generic(
17 | -- Bits to use for the size of the memory
18 | -- The total size is then 2^(MEMORY_SIZE_BITS)
19 | -- The generic is given as bit count because only sizes
20 | -- that are a power of two are supported
21 | MEMORY_SIZE_BITS : positive := 12
22 | );
23 | port(
24 | clock_i : in std_ulogic;
25 |
26 | mac_rx_reset_i : in std_ulogic;
27 | mac_rx_clock_i : in std_ulogic;
28 | mac_rx_frame_i : in std_ulogic;
29 | mac_rx_data_i : in t_ethernet_data;
30 | mac_rx_byte_received_i : in std_ulogic;
31 | mac_rx_error_i : in std_ulogic;
32 |
33 | empty_o : out std_ulogic;
34 | rd_en_i : in std_ulogic;
35 | data_o : out t_ethernet_data
36 | );
37 | end entity;
38 |
39 | architecture rtl of rx_fifo is
40 | type t_write_state is (
41 | WRITE_WAIT,
42 | WRITE_PACKET,
43 | WRITE_LENGTH_HIGH,
44 | WRITE_LENGTH_LOW,
45 | WRITE_PACKET_VALID,
46 | WRITE_PACKET_INVALID,
47 | WRITE_SKIP_FRAME
48 | );
49 | signal write_state : t_write_state := WRITE_PACKET_INVALID;
50 |
51 | type t_read_state is (
52 | READ_WAIT_PACKET,
53 | READ_WAIT_ACK,
54 | READ_LENGTH_LOW,
55 | READ_PACKET
56 | );
57 | signal read_state : t_read_state := READ_WAIT_PACKET;
58 |
59 | constant MEMORY_SIZE : positive := 2 ** MEMORY_SIZE_BITS;
60 |
61 | constant FLAG_BYTES : positive := 1;
62 | constant PACKET_LENGTH_BYTES : positive := 2;
63 |
64 | type t_memory is array (0 to (MEMORY_SIZE - 1)) of t_ethernet_data;
65 |
66 | -- Use unsigned here instead of simple integer variables to guarantee identical behavior in behavioral and post-translate simulation
67 | -- Integer variables will wrap around only on reaching 2^32 in behavioral simulation, unsigned will always wrap around correctly.
68 | subtype t_memory_address is unsigned((MEMORY_SIZE_BITS - 1) downto 0);
69 | -- Ethernet frames have to be smaller than ~1500 bytes, so 11 bits will suffice for the packet length
70 | subtype t_packet_length is unsigned(10 downto 0);
71 |
72 | signal memory : t_memory;
73 |
74 | -- Signals in write side clock domain
75 | signal write_start_address : t_memory_address;
76 | signal write_address : t_memory_address;
77 | signal write_packet_length : t_packet_length;
78 | signal write_safe_address : t_memory_address;
79 | signal write_update_safe_address_req : std_ulogic;
80 | signal write_update_safe_address_ack : std_ulogic;
81 | signal write_reset_read_side : std_ulogic;
82 |
83 | -- Signals in read side clock domain
84 | signal read_reset : std_ulogic := '1';
85 | signal read_data : t_ethernet_data;
86 | signal read_address : t_memory_address;
87 | -- Address of the last byte that is still part of the currently processed frame in the read process
88 | signal read_end_address : t_memory_address;
89 | signal read_packet_length_high : unsigned(2 downto 0);
90 | signal read_update_safe_address_req : std_ulogic;
91 | signal read_update_safe_address_ack : std_ulogic;
92 |
93 | -- Signals crossing clock domains
94 | signal update_safe_address : t_memory_address;
95 |
96 | -- Counts the elements in [a, b) in a ring buffer of given size
97 | -- a and b should have the same range
98 | function pointer_difference(a : unsigned; b : unsigned; size : positive) return unsigned is
99 | -- Make sure the result has a matching range
100 | variable result : unsigned(a'range);
101 | begin
102 | if b >= a then
103 | result := b - a;
104 | else
105 | result := size - a + b;
106 | end if;
107 | return result;
108 | end function;
109 | begin
110 | data_o <= read_data;
111 |
112 | sync_req_inst : entity work.single_signal_synchronizer
113 | port map(
114 | clock_target_i => mac_rx_clock_i,
115 | signal_i => read_update_safe_address_req,
116 | signal_o => write_update_safe_address_req
117 | );
118 |
119 | sync_ack_inst : entity work.single_signal_synchronizer
120 | port map(
121 | clock_target_i => clock_i,
122 | signal_i => write_update_safe_address_ack,
123 | signal_o => read_update_safe_address_ack
124 | );
125 |
126 | sync_reset_inst : entity work.single_signal_synchronizer
127 | port map(
128 | clock_target_i => clock_i,
129 | signal_i => write_reset_read_side,
130 | signal_o => read_reset
131 | );
132 |
133 | write_get_safe_address : process(mac_rx_reset_i, mac_rx_clock_i)
134 | begin
135 | if mac_rx_reset_i = '1' then
136 | write_update_safe_address_ack <= '0';
137 | write_safe_address <= (others => '1');
138 | elsif rising_edge(mac_rx_clock_i) then
139 | -- Data is read when the read process changes the level on write_update_safe_address_req
140 | if write_update_safe_address_req /= write_update_safe_address_ack then
141 | -- Read new safe address
142 | write_safe_address <= update_safe_address;
143 | -- Signal read process that the change was detected
144 | write_update_safe_address_ack <= not write_update_safe_address_ack;
145 | end if;
146 | end if;
147 | end process;
148 |
149 | write_memory : process(mac_rx_reset_i, mac_rx_clock_i)
150 | variable write_address_now : t_memory_address;
151 | variable write_data : t_ethernet_data;
152 | variable write_enable : boolean;
153 | variable packet_length_calculated : t_packet_length;
154 | variable enough_room : boolean;
155 | begin
156 | if mac_rx_reset_i = '1' then
157 | write_state <= WRITE_PACKET_INVALID;
158 | write_start_address <= (others => '0');
159 | write_address <= (others => '0');
160 | write_reset_read_side <= '1';
161 | elsif rising_edge(mac_rx_clock_i) then
162 | -- Default values for variables so no storage is inferred
163 | write_address_now := write_address;
164 | write_enable := FALSE;
165 | write_data := (others => '0');
166 | packet_length_calculated := (others => '0');
167 | enough_room := TRUE;
168 |
169 | case write_state is
170 | when WRITE_WAIT =>
171 | -- First byte has definitely been written invalid, read side can go into action now
172 | write_reset_read_side <= '0';
173 | if mac_rx_frame_i = '1' then
174 | if mac_rx_error_i = '1' then
175 | write_state <= WRITE_SKIP_FRAME;
176 | else
177 | if mac_rx_byte_received_i = '1' then
178 | -- Check whether the 3 header bytes can be written safely to the buffer afterwards
179 | for i in 0 to 2 loop
180 | if write_start_address + i = write_safe_address then
181 | enough_room := FALSE;
182 | end if;
183 | end loop;
184 |
185 | if enough_room then
186 | -- Write first byte
187 | -- Leave 3 bytes room for data validity indication and packet length
188 | write_address_now := write_start_address + PACKET_LENGTH_BYTES + FLAG_BYTES;
189 | write_data := mac_rx_data_i;
190 | write_enable := TRUE;
191 | write_address <= write_address_now + 1;
192 |
193 | write_state <= WRITE_PACKET;
194 | else
195 | write_state <= WRITE_SKIP_FRAME;
196 | end if;
197 | end if;
198 | end if;
199 | end if;
200 | when WRITE_PACKET =>
201 | if mac_rx_error_i = '1' then
202 | write_state <= WRITE_SKIP_FRAME;
203 | end if;
204 | -- Calculate packet length
205 | -- Resize as packet_length'length can be greater than memory_address_t'length for small memories
206 | packet_length_calculated := resize(pointer_difference(write_start_address, write_address, MEMORY_SIZE), packet_length_calculated'length);
207 |
208 | if mac_rx_frame_i = '1' then
209 | if mac_rx_byte_received_i = '1' then
210 | -- Write data
211 | write_data := mac_rx_data_i;
212 | write_enable := TRUE;
213 | write_address <= write_address + 1;
214 | end if;
215 |
216 | -- Packet length will overflow after this clock cycle
217 | if packet_length_calculated = (packet_length_calculated'range => '1') then
218 | -- Throw frame away
219 | write_state <= WRITE_SKIP_FRAME;
220 | end if;
221 |
222 | -- Buffer memory will overflow after this clock cycle
223 | -- This is always an error as at least one byte must be available even
224 | -- after the frame has ended to mark the next packet invalid in the buffer.
225 | if write_address = write_safe_address then
226 | -- Throw frame away
227 | write_state <= WRITE_SKIP_FRAME;
228 | end if;
229 | -- Error flag is irrelevant if rx_frame_i is low already
230 | else
231 | if packet_length_calculated <= (CRC32_BYTES + PACKET_LENGTH_BYTES + FLAG_BYTES) then
232 | -- Frame is way too short, ignore
233 | write_state <= WRITE_WAIT;
234 | end if;
235 | packet_length_calculated := packet_length_calculated - CRC32_BYTES - PACKET_LENGTH_BYTES - FLAG_BYTES;
236 | write_packet_length <= packet_length_calculated;
237 |
238 | -- Write next packet invalid before doing anything else
239 | -- Read process could read past this packet faster than we can flag the following packet invalid otherwise
240 | -- for very low network speed/system clock speed ratios
241 | write_address_now := write_start_address + PACKET_LENGTH_BYTES + FLAG_BYTES + to_integer(packet_length_calculated);
242 | write_data := (others => '0');
243 | write_enable := TRUE;
244 |
245 | -- Write data length low byte next
246 | write_state <= WRITE_LENGTH_LOW;
247 | end if;
248 | when WRITE_LENGTH_LOW =>
249 | -- Write length low byte
250 | write_address_now := write_start_address + 2;
251 | write_data := std_ulogic_vector(write_packet_length(7 downto 0));
252 | write_enable := TRUE;
253 |
254 | -- Write high byte next
255 | write_state <= WRITE_LENGTH_HIGH;
256 | when WRITE_LENGTH_HIGH =>
257 | -- Write length high byte
258 | write_address_now := write_start_address + 1;
259 | write_data := "00000" & std_ulogic_vector(write_packet_length(10 downto 8));
260 | write_enable := TRUE;
261 |
262 | -- Write packet validity indicator next
263 | write_state <= WRITE_PACKET_VALID;
264 | when WRITE_PACKET_VALID =>
265 | write_address_now := write_start_address;
266 | write_data := (others => '1'); -- mark valid
267 | write_enable := TRUE;
268 |
269 | -- Packet received correctly and completely, move start pointer for next packet ->
270 | -- Move write pointer past the data of the current frame (excluding FCS) so the next
271 | -- packet can be written
272 | -- The FCS will get overwritten as it is not needed for operation of the higher layers.
273 | write_start_address <= write_start_address + PACKET_LENGTH_BYTES + FLAG_BYTES + to_integer(write_packet_length);
274 | write_state <= WRITE_WAIT;
275 | when WRITE_PACKET_INVALID =>
276 | -- Mark current/first packet as invalid
277 | -- This cannot be merged into the WRITE_WAIT state, as WRITE_WAIT needs to write an incoming data byte
278 | -- into the buffer immediately.
279 | write_address_now := write_start_address;
280 | write_data := (others => '0');
281 | write_enable := TRUE;
282 | write_state <= WRITE_WAIT;
283 | when WRITE_SKIP_FRAME =>
284 | if mac_rx_frame_i = '0' then
285 | report "Skipped frame" severity note;
286 | write_state <= WRITE_WAIT;
287 | end if;
288 | end case;
289 |
290 | if write_enable then
291 | memory(to_integer(write_address_now)) <= write_data;
292 | end if;
293 | end if;
294 | end process;
295 |
296 | read_memory : process(clock_i)
297 | variable read_address_now : t_memory_address;
298 | begin
299 | if rising_edge(clock_i) then
300 | -- Default output value
301 | empty_o <= '1';
302 |
303 | -- Default variable value to avoid storage
304 | read_address_now := read_address;
305 |
306 | if read_reset = '1' then
307 | read_state <= READ_WAIT_PACKET;
308 | read_address <= (others => '0');
309 | read_end_address <= (others => '0');
310 | read_data <= (others => '0');
311 | read_update_safe_address_req <= '0';
312 | else
313 | case read_state is
314 | when READ_WAIT_PACKET =>
315 | -- Wait until data is ready, nobody is trying to get data out (in case the previous state was READ_PACKET)
316 | -- to prevent read overruns, and the write FSM has ack'ed the update request
317 | if read_data = (read_data'range => '1') and rd_en_i = '0' and read_update_safe_address_req = read_update_safe_address_ack then
318 | read_state <= READ_WAIT_ACK;
319 | -- Advance to address high byte
320 | read_address_now := read_address + 1;
321 | -- Tell the receiver that something is here
322 | empty_o <= '1';
323 | end if;
324 | when READ_WAIT_ACK =>
325 | empty_o <= '0';
326 |
327 | if rd_en_i = '1' then
328 | -- Read address high byte
329 | read_address_now := read_address + 1;
330 | read_state <= READ_LENGTH_LOW;
331 | read_packet_length_high <= unsigned(read_data(2 downto 0));
332 | end if;
333 | when READ_LENGTH_LOW =>
334 | empty_o <= '0';
335 | if rd_en_i = '1' then
336 | -- Read address low byte
337 | read_address_now := read_address + 1;
338 | read_state <= READ_PACKET;
339 | -- The end address is the address of the last byte that is still valid, so one byte
340 | -- needs to be subtracted.
341 | read_end_address <= read_address_now + to_integer(read_packet_length_high & unsigned(read_data)) - 1;
342 | end if;
343 | when READ_PACKET =>
344 | empty_o <= '0';
345 |
346 | if read_address = read_end_address then
347 | -- Wait for the last byte to be read out
348 | if rd_en_i = '1' then
349 | -- Guarantee that rx_empty_o is high for at least one clock cycle (needed so that the
350 | -- receiver can sense the end of the packet)
351 | empty_o <= '1';
352 | read_state <= READ_WAIT_PACKET;
353 | -- Update safe address for write process
354 | update_safe_address <= read_end_address;
355 | read_update_safe_address_req <= not read_update_safe_address_req;
356 | end if;
357 | end if;
358 |
359 | if rd_en_i = '1' then
360 | -- If this was the last byte: go to the first byte following this packet (-> header of the next packet)
361 | -- No need to skip the FCS here, this is already taken care of in the write process.
362 | read_address_now := read_address + 1;
363 | end if;
364 | end case;
365 |
366 | -- Read data at new address in this clock cycle,
367 | -- save the value to the signal for the next clock cycle.
368 | read_data <= memory(to_integer(read_address_now));
369 | read_address <= read_address_now;
370 | end if;
371 | end if;
372 | end process;
373 |
374 | end architecture;
375 |
--------------------------------------------------------------------------------
/ethernet_mac_tb.vhd:
--------------------------------------------------------------------------------
1 | -- This file is part of the ethernet_mac project.
2 | --
3 | -- For the full copyright and license information, please read the
4 | -- LICENSE.md file that was distributed with this source code.
5 |
6 | -- Self-checking testbench for the complete ethernet_mac (excluding MIIM)
7 |
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | use work.ethernet_types.all;
13 | use work.framing_common.all;
14 | use work.utility.all;
15 | use work.crc32.all;
16 | use work.test_common.all;
17 |
18 | entity ethernet_mac_tb is
19 | generic(
20 | -- Test configuration
21 | -- Setting to TRUE enables test of all packet sizes from 1 to 1528
22 | TEST_THOROUGH : boolean := FALSE;
23 | -- Enforce GMII setup/hold times
24 | TEST_MII_SETUPHOLD : boolean := FALSE;
25 | -- Print debug information such as all sent/received data bytes
26 | VERBOSE : boolean := FALSE
27 | );
28 | end entity;
29 |
30 | architecture behavioral of ethernet_mac_tb is
31 | -- ethernet_with_fifos signals
32 | signal clock_125 : std_ulogic := '0';
33 | signal reset : std_ulogic := '1';
34 | signal mii_tx_clk : std_ulogic := '0';
35 | signal mii_tx_er : std_ulogic := '0';
36 | signal mii_tx_en : std_ulogic := '0';
37 | signal mii_txd : std_ulogic_vector(7 downto 0) := (others => '0');
38 | signal mii_rx_clk : std_ulogic := '0';
39 | signal mii_rx_er : std_ulogic := '0';
40 | signal mii_rx_dv : std_ulogic := '0';
41 | signal mii_rxd : std_ulogic_vector(7 downto 0) := (others => '0');
42 | signal gmii_gtx_clk : std_ulogic := '0';
43 | signal user_clock : std_ulogic := '0';
44 |
45 | -- Testbench signals
46 | signal run : boolean := TRUE;
47 |
48 | constant MAX_PACKETS_IN_TRANSACTION : integer := 10;
49 |
50 | -- Data array length is a bit on the large side so we can send jumbo frames
51 | -- When debugging problems with waveform viewers (or generally using iSim), try smaller values to lessen
52 | -- the burden on the simulator. Note that not all test cases will run then.
53 | type t_packet_data is array (0 to 10000) of t_ethernet_data;
54 | --type t_packet_data is array (0 to 1050) of t_ethernet_data;
55 | --type t_packet_data is array (0 to 70) of t_ethernet_data;
56 | type t_packet_transaction is record
57 | valid : boolean;
58 | data : t_packet_data;
59 | size : integer;
60 | end record;
61 | type t_packet_buffer is array (0 to MAX_PACKETS_IN_TRANSACTION - 1) of t_packet_transaction;
62 |
63 | signal speed_override : t_ethernet_speed := SPEED_1000MBPS;
64 | signal send_packet_req : boolean := FALSE;
65 | signal send_packet_ack : boolean := FALSE;
66 | signal send_corrupt_data : boolean := FALSE;
67 | signal send_packet_buffer : t_packet_buffer;
68 |
69 | signal receive_packet_req : boolean := FALSE;
70 | signal receive_packet_ack : boolean := FALSE;
71 | signal receive_packet_buffer : t_packet_buffer;
72 | signal receive_packet_count_expected : integer := 0;
73 | signal receive_ipg_duration_bits : integer;
74 |
75 | signal test_mode : t_test_mode := TEST_LOOPBACK;
76 |
77 | -- Timing definitions
78 | constant clock_125_period : time := 8 ns;
79 | constant clock_25_period : time := 40 ns;
80 | constant clock_2_5_period : time := 400 ns;
81 | constant mii_rx_setup : time := 2 ns;
82 | constant mii_rx_hold : time := 0 ns;
83 | constant mii_tx_setup : time := 2.5 ns;
84 |
85 | -- Functions
86 |
87 | impure function mii_rx_clk_period return time is
88 | begin
89 | case speed_override is
90 | when SPEED_10MBPS =>
91 | return clock_2_5_period;
92 | when SPEED_100MBPS =>
93 | return clock_25_period;
94 | when others =>
95 | return clock_125_period;
96 | end case;
97 | end function;
98 |
99 | -- Compare two packet transactions
100 | -- When respect_address is set, it is taken into account that the
101 | -- source address of the received packet will be different and needs to
102 | -- match the address of the MAC test instance. The second arguments must be
103 | -- the received packet then.
104 | function compare_packet_transactions(left, right : in t_packet_transaction; respect_address : boolean := FALSE) return boolean is
105 | variable data_begin : integer := 0;
106 | begin
107 | if left.valid /= right.valid then
108 | report "Transaction validity state mismatch" severity note;
109 | return FALSE;
110 | elsif left.valid = TRUE then
111 | -- Both are valid
112 | -- Check size
113 | if left.size /= right.size then
114 | report "Transaction size mismatch" severity note;
115 | return FALSE;
116 | end if;
117 | -- Check data
118 | if respect_address = TRUE then
119 | -- Verify that the destination address is untouched
120 | for i in 0 to MAC_ADDRESS_BYTES - 1 loop
121 | if left.data(i) /= right.data(i) then
122 | report "Transaction destination address mismatch at index " & integer'image(i) severity note;
123 | return FALSE;
124 | end if;
125 | end loop;
126 | -- Compare the source address with the constant
127 | for i in 0 to MAC_ADDRESS_BYTES - 1 loop
128 | if right.data(MAC_ADDRESS_BYTES + i) /= extract_byte(TEST_MAC_ADDRESS, i) then
129 | report "Transaction source address mismatch at index " & integer'image(i) severity note;
130 | return FALSE;
131 | end if;
132 | end loop;
133 | -- Start normal verification after the addresses
134 | data_begin := 2 * MAC_ADDRESS_BYTES;
135 | end if;
136 | -- Compare rest of data (or all if respect_address is FALSE)
137 | for i in data_begin to left.size - 1 loop
138 | if left.data(i) /= right.data(i) then
139 | report "Transaction data mismatch at index " & integer'image(i) severity note;
140 | return FALSE;
141 | end if;
142 | end loop;
143 | -- All good
144 | return TRUE;
145 | else
146 | -- Both are invalid, no further check necessary
147 | -- Data does not matter
148 | return TRUE;
149 | end if;
150 | end function;
151 |
152 | function "="(left, right : in t_packet_transaction) return boolean is
153 | begin
154 | return compare_packet_transactions(left, right, FALSE);
155 | end function;
156 |
157 | -- Compare two packet transaction buffers
158 | -- When respect_address is set, it is taken into account that the
159 | -- source address of the received packets will be different and needs to
160 | -- match the address of the MAC test instance. The second arguments must be
161 | -- the received packets then.
162 | function compare_packet_buffers(left, right : in t_packet_buffer; respect_address : boolean := FALSE) return boolean is
163 | begin
164 | for i in t_packet_buffer'range loop
165 | -- Stop when both elements are invalid (end reached)
166 | exit when (not left(i).valid) and (not right(i).valid);
167 | -- Compare elements
168 | if not compare_packet_transactions(left(i), right(i), respect_address) then
169 | report "Mismatch in buffer element " & integer'image(i) severity note;
170 | return FALSE;
171 | end if;
172 | end loop;
173 | return TRUE;
174 | end function;
175 |
176 | function "="(left, right : in t_packet_buffer) return boolean is
177 | begin
178 | return compare_packet_buffers(left, right, FALSE);
179 | end function;
180 |
181 | -- "Known good" CRC32 function for comparison from chips example project
182 | -- polynomial: (0 1 2 4 5 7 8 10 11 12 16 22 23 26 32)
183 | -- data width: 8
184 | -- convention: the first serial bit is D[0]
185 | function NEXTCRC32_D8(DATA : std_ulogic_vector(7 downto 0);
186 | CRC : std_ulogic_vector(31 downto 0)) return std_ulogic_vector is
187 | variable D : std_ulogic_vector(7 downto 0);
188 | variable C : std_ulogic_vector(31 downto 0);
189 | variable NEWCRC : std_ulogic_vector(31 downto 0);
190 |
191 | begin
192 | D := DATA;
193 | C := CRC;
194 | NewCRC(0) := C(24) xor C(30) xor D(1) xor D(7);
195 | NewCRC(1) := C(25) xor C(31) xor D(0) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
196 | NewCRC(2) := C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
197 | NewCRC(3) := C(27) xor D(4) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
198 | NewCRC(4) := C(28) xor D(3) xor C(27) xor D(4) xor C(26) xor D(5) xor C(24) xor C(30) xor D(1) xor D(7);
199 | NewCRC(5) := C(29) xor D(2) xor C(28) xor D(3) xor C(27) xor D(4) xor C(25) xor C(31) xor D(0) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
200 | NewCRC(6) := C(30) xor D(1) xor C(29) xor D(2) xor C(28) xor D(3) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
201 | NewCRC(7) := C(31) xor D(0) xor C(29) xor D(2) xor C(27) xor D(4) xor C(26) xor D(5) xor C(24) xor D(7);
202 | NewCRC(8) := C(0) xor C(28) xor D(3) xor C(27) xor D(4) xor C(25) xor D(6) xor C(24) xor D(7);
203 | NewCRC(9) := C(1) xor C(29) xor D(2) xor C(28) xor D(3) xor C(26) xor D(5) xor C(25) xor D(6);
204 | NewCRC(10) := C(2) xor C(29) xor D(2) xor C(27) xor D(4) xor C(26) xor D(5) xor C(24) xor D(7);
205 | NewCRC(11) := C(3) xor C(28) xor D(3) xor C(27) xor D(4) xor C(25) xor D(6) xor C(24) xor D(7);
206 | NewCRC(12) := C(4) xor C(29) xor D(2) xor C(28) xor D(3) xor C(26) xor D(5) xor C(25) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
207 | NewCRC(13) := C(5) xor C(30) xor D(1) xor C(29) xor D(2) xor C(27) xor D(4) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
208 | NewCRC(14) := C(6) xor C(31) xor D(0) xor C(30) xor D(1) xor C(28) xor D(3) xor C(27) xor D(4) xor C(26) xor D(5);
209 | NewCRC(15) := C(7) xor C(31) xor D(0) xor C(29) xor D(2) xor C(28) xor D(3) xor C(27) xor D(4);
210 | NewCRC(16) := C(8) xor C(29) xor D(2) xor C(28) xor D(3) xor C(24) xor D(7);
211 | NewCRC(17) := C(9) xor C(30) xor D(1) xor C(29) xor D(2) xor C(25) xor D(6);
212 | NewCRC(18) := C(10) xor C(31) xor D(0) xor C(30) xor D(1) xor C(26) xor D(5);
213 | NewCRC(19) := C(11) xor C(31) xor D(0) xor C(27) xor D(4);
214 | NewCRC(20) := C(12) xor C(28) xor D(3);
215 | NewCRC(21) := C(13) xor C(29) xor D(2);
216 | NewCRC(22) := C(14) xor C(24) xor D(7);
217 | NewCRC(23) := C(15) xor C(25) xor D(6) xor C(24) xor C(30) xor D(1) xor D(7);
218 | NewCRC(24) := C(16) xor C(26) xor D(5) xor C(25) xor C(31) xor D(0) xor D(6);
219 | NewCRC(25) := C(17) xor C(27) xor D(4) xor C(26) xor D(5);
220 | NewCRC(26) := C(18) xor C(28) xor D(3) xor C(27) xor D(4) xor C(24) xor C(30) xor D(1) xor D(7);
221 | NewCRC(27) := C(19) xor C(29) xor D(2) xor C(28) xor D(3) xor C(25) xor C(31) xor D(0) xor D(6);
222 | NewCRC(28) := C(20) xor C(30) xor D(1) xor C(29) xor D(2) xor C(26) xor D(5);
223 | NewCRC(29) := C(21) xor C(31) xor D(0) xor C(30) xor D(1) xor C(27) xor D(4);
224 | NewCRC(30) := C(22) xor C(31) xor D(0) xor C(28) xor D(3);
225 | NewCRC(31) := C(23) xor C(29) xor D(2);
226 |
227 | return NEWCRC;
228 | end function;
229 |
230 | -- Copy MAC address (or similar) from a concatenated vector into byte units
231 | procedure copy_to_buffer_packet(source : in std_ulogic_vector; signal destination : inout t_packet_buffer; transaction : in integer) is
232 | begin
233 | for i in 0 to source'high / 8 loop
234 | destination(transaction).data(i) <= extract_byte(source, i);
235 | end loop;
236 | end procedure;
237 |
238 | -- Copy MAC address (or similar) from byte units into a concatenated vector
239 | procedure copy_from_buffer_packet(source : in t_packet_buffer; transaction : in integer; destination : inout std_ulogic_vector) is
240 | begin
241 | for i in 0 to destination'high / 8 loop
242 | destination(((i + 1) * 8) - 1 downto (i * 8)) := source(transaction).data(i);
243 | end loop;
244 | end procedure;
245 |
246 | begin
247 | -- Be aware of simulation mismatch because of delta-delay issues here
248 | user_clock <= clock_125;
249 |
250 | -- Instantiate component
251 | ethernet_mac_inst : test_instance
252 | port map(
253 | clock_125_i => clock_125,
254 | user_clock_i => user_clock,
255 | reset_i => reset,
256 | mii_tx_clk_i => mii_tx_clk,
257 | mii_tx_er_o => mii_tx_er,
258 | mii_tx_en_o => mii_tx_en,
259 | mii_txd_o => mii_txd,
260 | mii_rx_clk_i => mii_rx_clk,
261 | mii_rx_er_i => mii_rx_er,
262 | mii_rx_dv_i => mii_rx_dv,
263 | mii_rxd_i => mii_rxd,
264 | gmii_gtx_clk_o => gmii_gtx_clk,
265 | rgmii_tx_ctl_o => open,
266 | rgmii_rx_ctl_i => '0',
267 | speed_override_i => speed_override,
268 | test_mode_i => test_mode
269 | );
270 |
271 | -- Generate clocks
272 | clock_125_process : process
273 | begin
274 | if not run then
275 | wait until run;
276 | end if;
277 | clock_125 <= not clock_125;
278 | wait for clock_125_period / 2;
279 | end process;
280 |
281 | mii_tx_clk_process : process
282 | begin
283 | if not run then
284 | wait until run;
285 | end if;
286 | case speed_override is
287 | when SPEED_10MBPS =>
288 | mii_tx_clk <= not mii_tx_clk;
289 | wait for clock_2_5_period / 2;
290 | when SPEED_100MBPS =>
291 | mii_tx_clk <= not mii_tx_clk;
292 | wait for clock_25_period / 2;
293 | when others =>
294 | -- MII TX_CLK is inactive in 1 Gbps mode
295 | wait until ((speed_override = SPEED_10MBPS) or (speed_override = SPEED_100MBPS));
296 | end case;
297 | end process;
298 |
299 | -- Process for stimulating the MII RX interface
300 | packet_send_process : process is
301 | procedure mii_rx_cycle(data : in std_ulogic_vector(7 downto 0) := "XXXXXXXX";
302 | dv : in std_ulogic := '1';
303 | er : in std_ulogic := '0') is
304 | begin
305 | if TEST_MII_SETUPHOLD then
306 | -- Setup/hold time simulation is only useful in post-synthesis simulation
307 | mii_rx_clk <= '0';
308 | wait for (mii_rx_clk_period / 2) - mii_rx_setup;
309 | mii_rx_dv <= dv;
310 | mii_rx_er <= er;
311 | mii_rxd <= data;
312 | wait for mii_rx_setup;
313 | mii_rx_clk <= '1';
314 | wait for mii_rx_hold;
315 | mii_rxd <= (others => 'X');
316 | mii_rx_dv <= 'X';
317 | mii_rx_er <= 'X';
318 | wait for (mii_rx_clk_period / 2) - mii_rx_hold;
319 | else
320 | mii_rx_clk <= '0';
321 | mii_rx_dv <= dv;
322 | mii_rx_er <= er;
323 | mii_rxd <= data;
324 | wait for mii_rx_clk_period / 2;
325 | mii_rx_clk <= '1';
326 | wait for mii_rx_clk_period / 2;
327 | end if;
328 | end procedure;
329 |
330 | procedure mii_rx_put(
331 | data : in std_ulogic_vector(7 downto 0) := "XXXXXXXX";
332 | dv : in std_ulogic := '1';
333 | er : in std_ulogic := '0') is
334 | begin
335 | if VERBOSE and data /= "XXXXXXXX" then
336 | report "Send: " & integer'image(to_integer(unsigned(data)));
337 | end if;
338 | if speed_override = SPEED_1000MBPS then
339 | mii_rx_cycle(data, dv, er);
340 | else
341 | mii_rx_cycle("XXXX" & data(3 downto 0), dv, er);
342 | mii_rx_cycle("XXXX" & data(7 downto 4), dv, er);
343 | end if;
344 | end procedure;
345 |
346 | procedure mii_rx_toggle is
347 | begin
348 | mii_rx_put(dv => '0', er => '0', data => open);
349 | end procedure;
350 |
351 | variable fcs : t_crc32;
352 | begin
353 | while not send_packet_req loop
354 | mii_rx_toggle;
355 | if not run then
356 | wait until run;
357 | end if;
358 | end loop;
359 |
360 | for packet_i in send_packet_buffer'range loop
361 | -- Stop at first invalid packet
362 | exit when not send_packet_buffer(packet_i).valid;
363 |
364 | -- Preamble
365 | for i in 0 to 3 loop
366 | mii_rx_put(PREAMBLE_DATA);
367 | end loop;
368 | -- SFD
369 | mii_rx_put(START_FRAME_DELIMITER_DATA);
370 | -- Data
371 | fcs := (others => '1');
372 | for i in 0 to send_packet_buffer(packet_i).size - 1 loop
373 | if send_corrupt_data then
374 | mii_rx_put(send_packet_buffer(packet_i).data(i) and "11011111");
375 | else
376 | mii_rx_put(send_packet_buffer(packet_i).data(i));
377 | end if;
378 | fcs := NEXTCRC32_D8(send_packet_buffer(packet_i).data(i), fcs);
379 | end loop;
380 | -- FCS
381 | mii_rx_put(fcs_output_byte(fcs, 0));
382 | mii_rx_put(fcs_output_byte(fcs, 1));
383 | mii_rx_put(fcs_output_byte(fcs, 2));
384 | mii_rx_put(fcs_output_byte(fcs, 3));
385 | -- IFG
386 | for i in 0 to 11 loop
387 | mii_rx_toggle;
388 | end loop;
389 | end loop;
390 |
391 | send_packet_ack <= TRUE;
392 | --report "Done sending" severity note;
393 | while send_packet_req loop
394 | mii_rx_toggle;
395 | end loop;
396 | send_packet_ack <= FALSE;
397 | end process;
398 |
399 | -- Process for reading the MII TX interface into a packet buffer
400 | packet_receive_process : process is
401 | variable current_byte : integer;
402 | variable data : t_ethernet_data;
403 | variable fcs : t_crc32;
404 | variable ipg_count_bits : integer;
405 |
406 | procedure wait_clk is
407 | begin
408 | case speed_override is
409 | when SPEED_10MBPS | SPEED_100MBPS =>
410 | wait until rising_edge(mii_tx_clk);
411 | when others =>
412 | wait until rising_edge(gmii_gtx_clk);
413 | end case;
414 | assert mii_tx_er = '0' report "MII transmission error flag is set" severity failure;
415 | if TEST_MII_SETUPHOLD then
416 | assert mii_tx_en'last_event > mii_tx_setup report "Setup time violated on TX_EN" severity failure;
417 | assert mii_txd'last_event > mii_tx_setup report "Setup time violated on TXD" severity failure;
418 | end if;
419 | end procedure;
420 |
421 | procedure read_byte(output_byte : inout t_ethernet_data) is
422 | variable tx_was_enabled : std_ulogic;
423 | begin
424 | tx_was_enabled := mii_tx_en;
425 | case speed_override is
426 | when SPEED_10MBPS | SPEED_100MBPS =>
427 | output_byte(3 downto 0) := mii_txd(3 downto 0);
428 | wait_clk;
429 | assert mii_tx_en = '1' report "Frame transmission ended between byte boundaries" severity failure;
430 | output_byte(7 downto 4) := mii_txd(3 downto 0);
431 | wait_clk;
432 | when others =>
433 | output_byte := mii_txd;
434 | wait_clk;
435 | end case;
436 | if tx_was_enabled = '1' and VERBOSE then
437 | report "Rcv data: " & integer'image(to_integer(unsigned(output_byte)));
438 | end if;
439 | end procedure;
440 |
441 | begin
442 | wait until receive_packet_req;
443 |
444 | --report "Start receiver" severity note;
445 |
446 | for i in receive_packet_buffer'range loop
447 | receive_packet_buffer(i).valid <= FALSE;
448 | end loop;
449 |
450 | packet_loop : for current_packet_i in 0 to receive_packet_count_expected - 1 loop
451 | current_byte := 0;
452 | ipg_count_bits := 0;
453 | -- Wait for beginning of frame
454 | loop
455 | -- Assuming tx_en is deasserted now, this is already the first cycle of the IPG
456 | case speed_override is
457 | when SPEED_10MBPS | SPEED_100MBPS =>
458 | ipg_count_bits := ipg_count_bits + 4;
459 | when others =>
460 | ipg_count_bits := ipg_count_bits + 8;
461 | end case;
462 | wait_clk;
463 | -- Allow receive cancellation
464 | exit packet_loop when not receive_packet_req;
465 | exit when mii_tx_en = '1';
466 | end loop;
467 |
468 | -- Measure IPG duration between last two packets when multiple packets are received
469 | if current_packet_i /= 0 then
470 | assert ipg_count_bits >= INTERPACKET_GAP_BYTES * 8 report "Inter-packet gap too short" severity failure;
471 | receive_ipg_duration_bits <= ipg_count_bits;
472 | end if;
473 |
474 | --report "Start packet reception" severity note;
475 |
476 | for i in 0 to 6 loop
477 | read_byte(data);
478 | assert data = PREAMBLE_DATA and mii_tx_en = '1' report "Packet did not start with correct preamble data" severity failure;
479 | end loop;
480 |
481 | read_byte(data);
482 | assert data = START_FRAME_DELIMITER_DATA and mii_tx_en = '1' report "Packet did not start with correct preamble data or start frame delimiter" severity failure;
483 |
484 | fcs := (others => '1');
485 |
486 | loop
487 | read_byte(data);
488 | receive_packet_buffer(current_packet_i).data(current_byte) <= data;
489 | current_byte := current_byte + 1;
490 | assert current_byte <= t_packet_data'high report "Transmitted packet is too long (size now " & integer'image(current_byte) & ")" severity failure;
491 | fcs := NEXTCRC32_D8(data, fcs);
492 |
493 | -- Exit after frame end
494 | exit when mii_tx_en = '0';
495 | end loop;
496 |
497 | -- Subtract FCS size
498 | current_byte := current_byte - CRC32_BYTES;
499 | if VERBOSE then
500 | report "Rcv size: " & integer'image(current_byte);
501 | end if;
502 | assert current_byte >= MIN_FRAME_DATA_BYTES report "Transmitted packet is too short" severity failure;
503 | -- Check FCS
504 | assert fcs = CRC32_POSTINVERT_MAGIC report "FCS of transmitted packet did not match contents" severity failure;
505 |
506 | receive_packet_buffer(current_packet_i).size <= current_byte;
507 | receive_packet_buffer(current_packet_i).valid <= TRUE;
508 | --report "Received packet index " & integer'image(current_packet_i) & " size " & integer'image(current_byte) severity note;
509 | --assert current_packet_i < receive_packet_buffer'high report "Too many packets were transmitted";
510 | end loop;
511 |
512 | if receive_packet_req then
513 | receive_packet_ack <= TRUE;
514 | wait until not receive_packet_req;
515 | receive_packet_ack <= FALSE;
516 | end if;
517 |
518 | --report "Stop receiver" severity note;
519 | end process;
520 |
521 | -- Main test process
522 | test_process : process is
523 | -- Activate the sender and wait for it to complete
524 | procedure do_send is
525 | begin
526 | send_packet_req <= TRUE;
527 | wait until send_packet_ack;
528 | send_packet_req <= FALSE;
529 | wait until not send_packet_ack;
530 | end procedure;
531 |
532 | -- Activate the receiver and wait for it to complete
533 | procedure do_receive is
534 | begin
535 | receive_packet_req <= TRUE;
536 | wait until receive_packet_ack;
537 | receive_packet_req <= FALSE;
538 | wait until not receive_packet_ack;
539 | end procedure;
540 |
541 | -- Activate the sender an receiver and wait for both to complete
542 | procedure do_send_receive is
543 | begin
544 | send_packet_req <= TRUE;
545 | receive_packet_req <= TRUE;
546 | wait until send_packet_ack and receive_packet_ack;
547 | send_packet_req <= FALSE;
548 | receive_packet_req <= FALSE;
549 | wait until (not send_packet_ack) and (not receive_packet_ack);
550 | end procedure;
551 |
552 | -- Send a packet, receive the mirror packet and check for equality
553 | procedure test_one_size(size : in integer) is
554 | begin
555 | report "Check single frame loopback size " & integer'image(size) severity note;
556 | send_packet_buffer(0).size <= size;
557 | do_send_receive;
558 | assert compare_packet_buffers(send_packet_buffer, receive_packet_buffer, TRUE) report "Packet loopback resulted in different packets" severity failure;
559 | end procedure;
560 |
561 | -- Send a packet, check that nothing comes back
562 | procedure test_send_broken is
563 | variable send_corrupt_data_backup : boolean;
564 | variable destination_address_backup : t_mac_address;
565 | begin
566 | -- Enable receiver
567 | receive_packet_req <= TRUE;
568 |
569 | do_send;
570 | -- Give it some time to send the packet back if it went through
571 | wait for mii_rx_clk_period * 3000;
572 | -- Nothing should have been received
573 | assert not receive_packet_buffer(0).valid report "Invalid packet was answered by the MAC" severity failure;
574 | -- Now send an OK packet to check that it hasn't deadlocked somewhere
575 | send_corrupt_data_backup := send_corrupt_data;
576 | send_corrupt_data <= FALSE;
577 | copy_from_buffer_packet(send_packet_buffer, 0, destination_address_backup);
578 | -- Make sure the packet doesn't get dropped because of a non-matching address
579 | copy_to_buffer_packet(TEST_MAC_ADDRESS, send_packet_buffer, 0);
580 | test_one_size(100);
581 | send_corrupt_data <= send_corrupt_data_backup;
582 | copy_to_buffer_packet(destination_address_backup, send_packet_buffer, 0);
583 | end procedure;
584 |
585 | procedure test_broken_size(size : in integer) is
586 | begin
587 | report "Check single broken frame is not looped back size " & integer'image(size) severity note;
588 | send_packet_buffer(0).size <= size;
589 | test_send_broken;
590 | end procedure;
591 |
592 | procedure set_test_mode(new_test_mode : in t_test_mode) is
593 | begin
594 | wait until falling_edge(user_clock);
595 | test_mode <= new_test_mode;
596 | end procedure;
597 |
598 | procedure test_one_speed is
599 | variable verify_packet_buffer : t_packet_buffer;
600 | begin
601 | set_test_mode(TEST_LOOPBACK);
602 |
603 | send_packet_buffer(0).valid <= TRUE;
604 | send_packet_buffer(1).valid <= FALSE;
605 | receive_packet_count_expected <= 1;
606 |
607 | if TRUE then
608 | -- Tests for packets that should pass
609 | if TEST_THOROUGH then
610 | for size in MIN_FRAME_DATA_BYTES to MAX_FRAME_DATA_BYTES loop
611 | test_one_size(size);
612 | end loop;
613 | else
614 | -- Test just a few sizes
615 | test_one_size(MIN_FRAME_DATA_BYTES);
616 | test_one_size(MIN_FRAME_DATA_BYTES + 1);
617 | test_one_size(1000);
618 | test_one_size(MAX_FRAME_DATA_BYTES - 1);
619 | test_one_size(MAX_FRAME_DATA_BYTES);
620 | end if;
621 |
622 | -- Test broadcast MAC address instead of unicast
623 | copy_to_buffer_packet(BROADCAST_MAC_ADDRESS, send_packet_buffer, 0);
624 | report "Check broadcast MAC destination address:" severity note;
625 | test_one_size(100);
626 | -- Test multicast address
627 | copy_to_buffer_packet(TEST_MAC_ADDRESS, send_packet_buffer, 0);
628 | -- Set group address bit
629 | send_packet_buffer(0).data(0)(0) <= '1';
630 | -- Make sure the rest is different from the test MAC address
631 | send_packet_buffer(0).data(1) <= not send_packet_buffer(0).data(1);
632 | report "Check multicast MAC destination address:" severity note;
633 | test_one_size(100);
634 | -- Copy entity address back for further tests
635 | copy_to_buffer_packet(TEST_MAC_ADDRESS, send_packet_buffer, 0);
636 | end if;
637 |
638 | -- Tests for packets that should get dropped
639 | if TRUE then
640 | if TEST_THOROUGH then
641 | -- Test runt frames
642 | for size in 1 to MIN_FRAME_DATA_BYTES - 1 loop
643 | test_broken_size(size);
644 | end loop;
645 | -- Test jumbo frames
646 | for size in MAX_FRAME_DATA_BYTES + 1 to MAX_FRAME_DATA_BYTES + 10 loop
647 | test_broken_size(size);
648 | end loop;
649 | else
650 | -- Test runt frames
651 | test_broken_size(1);
652 | test_broken_size(2);
653 | test_broken_size(3);
654 | test_broken_size(MIN_FRAME_DATA_BYTES - 2);
655 | test_broken_size(MIN_FRAME_DATA_BYTES - 1);
656 | -- Test jumbo frames
657 | test_broken_size(MAX_FRAME_DATA_BYTES + 1);
658 | test_broken_size(MAX_FRAME_DATA_BYTES + 2);
659 | end if;
660 | -- Test size that overflows 11 bits of size information in FIFOs
661 | test_broken_size(2 ** 11 + 70);
662 | -- Test size that is greater than total RX buffer size
663 | test_broken_size(9999);
664 | -- Test different destination MAC address
665 | for b in 0 to MAC_ADDRESS_BYTES - 1 loop
666 | -- Make sure byte position b does not match
667 | send_packet_buffer(0).data(b)(5) <= not send_packet_buffer(0).data(b)(5);
668 | report "Check MAC destination address mismatch at position " & integer'image(b) & ":" severity note;
669 | test_broken_size(100);
670 | -- Restore original address
671 | copy_to_buffer_packet(TEST_MAC_ADDRESS, send_packet_buffer, 0);
672 | end loop;
673 | -- Test wrong FCS
674 | report "Check single broken frame is not looped back size 100 bad FCS" severity note;
675 | send_packet_buffer(0).size <= 100;
676 | send_corrupt_data <= TRUE;
677 | test_send_broken;
678 | send_corrupt_data <= FALSE;
679 | -- Disable receiver
680 | receive_packet_req <= FALSE;
681 | wait for mii_rx_clk_period * 2;
682 | end if;
683 |
684 | if TRUE then
685 | -- Check RX FIFO overflow
686 | for i in 0 to 7 loop
687 | send_packet_buffer(i).valid <= TRUE;
688 | send_packet_buffer(i).size <= 1024;
689 | end loop;
690 | -- Suspend FIFO reader
691 | set_test_mode(TEST_NOTHING);
692 |
693 | report "Check RX FIFO overrun: Fill FIFO" severity note;
694 | -- Fill FIFO
695 | do_send;
696 | -- Enable receiver
697 | receive_packet_req <= TRUE;
698 | -- One more than really expected (3)
699 | receive_packet_count_expected <= 4;
700 | -- Resume FIFO reader
701 | set_test_mode(TEST_LOOPBACK);
702 | report "Check RX FIFO overrun: Receive mirrored packets" severity note;
703 | -- Wait for 3rd packet received
704 | wait until receive_packet_buffer(2).valid;
705 | -- Give it some more time to potentially receive another packet
706 | wait for mii_rx_clk_period * 3000;
707 | assert not receive_packet_ack report "Too many packets were received" severity failure;
708 | -- Disable receiver
709 | receive_packet_req <= FALSE;
710 | wait for mii_rx_clk_period * 2;
711 | -- Check IPG duration
712 | report "IPG duration: " & integer'image(receive_ipg_duration_bits) & " bits" severity note;
713 | assert receive_ipg_duration_bits < 20 * 8 report "Received interpacket gap is too long" severity failure;
714 | -- Validate packets that went through
715 | for i in 0 to 2 loop
716 | assert compare_packet_transactions(send_packet_buffer(i), receive_packet_buffer(i), TRUE) report "Packet loopback resulted in different packets" severity failure;
717 | end loop;
718 | -- Check that normal reception is now working again
719 | receive_packet_count_expected <= 1;
720 | send_packet_buffer(1).valid <= FALSE;
721 | test_one_size(100);
722 | end if;
723 |
724 | if TRUE then
725 | -- Check TX padding
726 | -- Fill verification data initially
727 | for packet_i in verify_packet_buffer'range loop
728 | verify_packet_buffer(packet_i).valid := FALSE;
729 | end loop;
730 | verify_packet_buffer(0).valid := TRUE;
731 | verify_packet_buffer(0).size := MIN_FRAME_DATA_BYTES;
732 | -- Start transmission
733 | set_test_mode(TEST_TX_PADDING);
734 | for size in 1 to 59 loop
735 | report "Check TX padding size " & integer'image(size) severity note;
736 | -- Fill verification data
737 | for i in 0 to size - 1 loop
738 | verify_packet_buffer(0).data(i) := t_ethernet_data(to_unsigned(i + 1, 8));
739 | end loop;
740 | for i in size to MIN_FRAME_DATA_BYTES - 1 loop
741 | verify_packet_buffer(0).data(i) := PADDING_DATA;
742 | end loop;
743 | -- Receive frame
744 | do_receive;
745 | -- Verify contents
746 | -- Do not check the MAC address: Auto-insertion does not take place in this test
747 | assert compare_packet_buffers(verify_packet_buffer, receive_packet_buffer, FALSE) report "Padded TX message does not have expected size and content" severity failure;
748 | end loop;
749 |
750 | -- Stop TX padding test to prevent unwanted packets being sent after a speed change
751 | set_test_mode(TEST_NOTHING);
752 | end if;
753 |
754 | -- Check for correct FIFO function when it is filled up exactly to the last byte?
755 | end procedure;
756 | begin
757 | report "MAC functional check starting" severity note;
758 | if TEST_MII_SETUPHOLD then
759 | report "Testing MII setup/hold times" severity note;
760 | end if;
761 |
762 | reset <= '1';
763 | speed_override <= SPEED_1000MBPS;
764 | wait for 100 ns;
765 | reset <= '0';
766 | wait for 10 us;
767 |
768 | for packet_i in send_packet_buffer'range loop
769 | -- Destination address
770 | copy_to_buffer_packet(TEST_MAC_ADDRESS, send_packet_buffer, packet_i);
771 | -- Source address
772 | for i in MAC_ADDRESS_BYTES to 2 * MAC_ADDRESS_BYTES - 1 loop
773 | -- Destination and source address
774 | send_packet_buffer(packet_i).data(i) <= x"FF";
775 | end loop;
776 | for i in 12 to t_packet_data'high loop
777 | send_packet_buffer(packet_i).data(i) <= std_ulogic_vector(to_unsigned((i + 7 + packet_i) mod 256, 8));
778 | end loop;
779 | end loop;
780 |
781 | report "Testing speed: 1 Gbps" severity note;
782 | test_one_speed;
783 |
784 | -- Transition on user_clock falling because speed_override
785 | -- must be synchronous to miim_clock (which is equal to user_clock here)
786 | wait until falling_edge(user_clock);
787 | speed_override <= SPEED_100MBPS;
788 | wait for 10 us;
789 | report "Testing speed: 100 Mbps" severity note;
790 | test_one_speed;
791 |
792 | wait until falling_edge(user_clock);
793 | speed_override <= SPEED_10MBPS;
794 | wait for 10 us;
795 | report "Testing speed: 10 Mbps" severity note;
796 | test_one_speed;
797 |
798 | report "MAC functional check ended OK" severity note;
799 | -- Stop simulation
800 | run <= FALSE;
801 | wait;
802 | end process;
803 |
804 | -- Detect when the MAC is not answering
805 | watchdog : process is
806 | begin
807 | wait for 1 ms;
808 | if not run then
809 | wait until run;
810 | end if;
811 | if receive_packet_req and receive_packet_req'last_event > 20 ms then
812 | report "Expected number of frames could not be received within specified timeframe" severity failure;
813 | end if;
814 | end process;
815 |
816 | end architecture;
817 |
--------------------------------------------------------------------------------