├── 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 | --------------------------------------------------------------------------------