Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ The core fully works on Xilinx Spartan 6 family FPGAs only at the moment. It was

The user interface is comprised of two FIFOs with a 8-bit data bus for packet transmission and reception, respectively.

Without FIFO the core is tested on Altera Cyclone III and Lattice ECP5 FPGAs.

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 <https://github.com/yol/ethernet_mac_doc/raw/master/Thesis.pdf>.

Features
Expand All @@ -19,7 +21,9 @@ Finished:
- Filtering received packets by destination MAC address
- MAC address insertion into source address of outgoing packets (only when the first source address byte in the packet stream is `0xFF`)
- **Simple 8 bit wide FIFO user interface** with arbitrary clock domains for packet transmission and reception
- **Media-independent interface (MII)** for 10/100 Mb/s and **gigabit media-independent interface (GMII)** connectivity
- **Media-independent interface (MII)** for 10/100 Mb/s
- **Reduced-Media-independent interface (RMII)** for 100 Mb/s
- **Gigabit-Media-independent interface (GMII)** for 1000 Mb/s
- MII/GMII hardware I/O setup for Xilinx Spartan 6 family FPGAs
- Basic **media-independent interface management (MIIM) interface support** that:
- Configures the PHY auto-negotiation to use full-duplex modes only
Expand Down
254 changes: 254 additions & 0 deletions ethernet_rmii.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
-- This file is part of the ethernet_mac project.
--
-- For the full copyright and license information, please read the
-- LICENSE.md file that was distributed with this source code.

library ieee;
use ieee.std_logic_1164.all;

-- Prebuilt Ethernet MAC without FIFOs

use work.ethernet_types.all;
use work.miim_types.all;

entity ethernet_rmii is
generic(
MIIM_PHY_ADDRESS : t_phy_address := (others => '0');
MIIM_RESET_WAIT_TICKS : natural := 0;
MIIM_POLL_WAIT_TICKS : natural := DEFAULT_POLL_WAIT_TICKS;
MIIM_CLOCK_DIVIDER : positive := 50;
MIIM_DISABLE : boolean := FALSE
);
port(
-- Reset input synchronous to miim_clock_i
reset_i : in std_ulogic;
-- Asynchronous reset output
reset_o : out std_ulogic;

-- MAC address of this station
-- Must not change after reset is deasserted
mac_address_i : in t_mac_address;

-- RMII (Media-independent interface)
rmii_clk_i : in std_ulogic;
rmii_tx_er_o : out std_ulogic;
rmii_tx_en_o : out std_ulogic;
rmii_txd_o : out std_ulogic_vector(1 downto 0);
rmii_rx_er_i : in std_ulogic;
rmii_rx_crs_dv_i : in std_ulogic;
rmii_rxd_i : in std_ulogic_vector(1 downto 0);

-- MII Management Interface
miim_clock_i : in std_ulogic;
mdc_o : out std_ulogic;
mdio_io : inout std_ulogic;
-- Status, synchronous to miim_clock_i
link_up_o : out std_ulogic;

-- TX from client logic
tx_clock_o : out std_ulogic;
-- Asynchronous reset that deasserts synchronously to tx_clock_o
tx_reset_o : out std_ulogic;
tx_enable_i : in std_ulogic;
tx_data_i : in t_ethernet_data;
tx_byte_sent_o : out std_ulogic;
tx_busy_o : out std_ulogic;

-- RX to client logic
rx_clock_o : out std_ulogic;
-- Asynchronous reset that deasserts synchronously to rx_clock_o
rx_reset_o : out std_ulogic;
rx_frame_o : out std_ulogic;
rx_data_o : out t_ethernet_data;
rx_byte_received_o : out std_ulogic;
rx_error_o : out std_ulogic
);
end entity;

architecture rtl of ethernet_rmii is
signal tx_clock : std_ulogic;
signal rx_clock : std_ulogic;

signal reset : std_ulogic := '1';
signal rx_reset : std_ulogic;
signal tx_reset : std_ulogic;

-- Interface between rmii and framing
signal mac_tx_enable : std_ulogic := '0';
signal mac_tx_data : t_ethernet_data;
signal mac_tx_byte_sent : std_ulogic;
signal mac_tx_gap : std_ulogic;
signal mac_rx_frame : std_ulogic;
signal mac_rx_data : t_ethernet_data;
signal mac_rx_byte_received : std_ulogic;
signal mac_rx_error : std_ulogic;

-- Internal MII bus between mii_gmii and mii_gmii_io
signal int_rmii_tx_en : std_ulogic;
signal int_rmii_txd : std_ulogic_vector(1 downto 0);
signal int_rmii_rx_er : std_ulogic;
signal int_rmii_rx_crs_dv : std_ulogic;
signal int_rmii_rxd : std_ulogic_vector(1 downto 0);

-- MIIM interconnection signals
signal miim_register_address : t_register_address;
signal miim_phy_address_sig : t_phy_address;
signal miim_data_read : t_data;
signal miim_data_write : t_data;
signal miim_req : std_ulogic;
signal miim_ack : std_ulogic;
signal miim_wr_en : std_ulogic;
signal miim_speed : t_ethernet_speed;
signal speed : t_ethernet_speed;
signal link_up : std_ulogic;
begin
reset_o <= reset;
rx_reset_o <= rx_reset;
tx_reset_o <= tx_reset;
tx_clock_o <= tx_clock;
rx_clock_o <= rx_clock;

link_up_o <= link_up;
miim_phy_address_sig <= MIIM_PHY_ADDRESS;
-- Errors are never transmitted in full-duplex mode
rmii_tx_er_o <= '0';

speed <= SPEED_100MBPS; -- set to fixed 100MBit for RMII mode

-- Generate MAC reset if necessary
reset_generator_inst : entity work.reset_generator
port map(
clock_i => miim_clock_i,
speed_i => speed,
reset_i => reset_i,
reset_o => reset
);

-- Bring reset into RX and TX clock domains, using:
-- * Asynchronous assertion of reset to guarantee resetting even when the MII clock is not running
-- * Synchronous deassertion of reset to guarantee meeting the reset recovery time of the flip flops
sync_rx_reset_inst : entity work.single_signal_synchronizer
port map(
clock_target_i => rx_clock,
preset_i => reset,
signal_i => '0',
signal_o => rx_reset
);

sync_tx_reset_inst : entity work.single_signal_synchronizer
port map(
clock_target_i => tx_clock,
preset_i => reset,
signal_i => '0',
signal_o => tx_reset
);

rmii_inst : entity work.rmii
port map(
rx_reset_i => rx_reset,
rx_clock_i => rx_clock,
tx_reset_i => tx_reset,
tx_clock_i => tx_clock,

-- MII (Media-independent interface)
rmii_tx_en_o => int_rmii_tx_en,
rmii_txd_o => int_rmii_txd,
rmii_rx_er_i => int_rmii_rx_er,
rmii_rx_crs_dv_i => int_rmii_rx_crs_dv,
rmii_rxd_i => int_rmii_rxd,

-- Interface control signals
tx_enable_i => mac_tx_enable,
tx_gap_i => mac_tx_gap,
tx_data_i => mac_tx_data,
tx_byte_sent_o => mac_tx_byte_sent,
rx_frame_o => mac_rx_frame,
rx_data_o => mac_rx_data,
rx_byte_received_o => mac_rx_byte_received,
rx_error_o => mac_rx_error
);

rmii_io_inst : entity work.rmii_io
port map(
clock_tx_o => tx_clock,
clock_rx_o => rx_clock,
rmii_clk_i => rmii_clk_i,
rmii_tx_en_o => rmii_tx_en_o,
rmii_txd_o => rmii_txd_o,
rmii_rx_er_i => rmii_rx_er_i,
rmii_rx_crs_dv_i => rmii_rx_crs_dv_i,
rmii_rxd_i => rmii_rxd_i,
int_rmii_tx_en_i => int_rmii_tx_en,
int_rmii_txd_i => int_rmii_txd,
int_rmii_rx_er_o => int_rmii_rx_er,
int_rmii_rx_crs_dv_o => int_rmii_rx_crs_dv,
int_rmii_rxd_o => int_rmii_rxd
);

framing_inst : entity work.framing
port map(
rx_reset_i => rx_reset,
tx_clock_i => tx_clock,
tx_reset_i => tx_reset,
rx_clock_i => rx_clock,
mac_address_i => mac_address_i,
tx_enable_i => tx_enable_i,
tx_data_i => tx_data_i,
tx_byte_sent_o => tx_byte_sent_o,
tx_busy_o => tx_busy_o,
rx_frame_o => rx_frame_o,
rx_data_o => rx_data_o,
rx_byte_received_o => rx_byte_received_o,
rx_error_o => rx_error_o,
mii_tx_enable_o => mac_tx_enable,
mii_tx_gap_o => mac_tx_gap,
mii_tx_data_o => mac_tx_data,
mii_tx_byte_sent_i => mac_tx_byte_sent,
mii_rx_frame_i => mac_rx_frame,
mii_rx_data_i => mac_rx_data,
mii_rx_byte_received_i => mac_rx_byte_received,
mii_rx_error_i => mac_rx_error
);

miim_gen : if MIIM_DISABLE = FALSE generate
miim_inst : entity work.miim
generic map(
CLOCK_DIVIDER => MIIM_CLOCK_DIVIDER
)
port map(
reset_i => reset_i,
clock_i => miim_clock_i,
register_address_i => miim_register_address,
phy_address_i => miim_phy_address_sig,
data_read_o => miim_data_read,
data_write_i => miim_data_write,
req_i => miim_req,
ack_o => miim_ack,
wr_en_i => miim_wr_en,
mdc_o => mdc_o,
mdio_io => mdio_io
);

miim_control_inst : entity work.miim_control
generic map(
RESET_WAIT_TICKS => MIIM_RESET_WAIT_TICKS,
POLL_WAIT_TICKS => MIIM_POLL_WAIT_TICKS,
DEBUG_OUTPUT => FALSE
)
port map(
reset_i => reset_i,
clock_i => miim_clock_i,
miim_register_address_o => miim_register_address,
miim_data_read_i => miim_data_read,
miim_data_write_o => miim_data_write,
miim_req_o => miim_req,
miim_ack_i => miim_ack,
miim_we_o => miim_wr_en,
link_up_o => link_up,
speed_o => miim_speed,
debug_fifo_we_o => open,
debug_fifo_write_data_o => open
);
end generate;
end architecture;

23 changes: 19 additions & 4 deletions framing.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,21 @@ begin
when TX_IDLE =>
-- Handled above, cannot happen here
null;
when TX_PREAMBLE2 | TX_PREAMBLE3 | TX_PREAMBLE4 | TX_PREAMBLE5 | TX_PREAMBLE6 =>
tx_state <= t_tx_state'succ(tx_state);
when TX_PREAMBLE2 =>
--tx_state <= t_tx_state'succ(tx_state); -- Xilinx ISE14.7 does not synthesize this line
tx_state <= TX_PREAMBLE3;
data_out := PREAMBLE_DATA;
when TX_PREAMBLE3 =>
tx_state <= TX_PREAMBLE4;
data_out := PREAMBLE_DATA;
when TX_PREAMBLE4 =>
tx_state <= TX_PREAMBLE5;
data_out := PREAMBLE_DATA;
when TX_PREAMBLE5 =>
tx_state <= TX_PREAMBLE6;
data_out := PREAMBLE_DATA;
when TX_PREAMBLE6 =>
tx_state <= TX_PREAMBLE7;
data_out := PREAMBLE_DATA;
when TX_PREAMBLE7 =>
tx_state <= TX_START_FRAME_DELIMITER;
Expand Down Expand Up @@ -225,10 +238,12 @@ begin
update_fcs := FALSE;
end if;
when TX_FRAME_CHECK_SEQUENCE2 =>
tx_state <= t_tx_state'succ(tx_state);
--tx_state <= t_tx_state'succ(tx_state); -- ISE14.7 does not synthesize this
tx_state <= TX_FRAME_CHECK_SEQUENCE3;
data_out := fcs_output_byte(tx_frame_check_sequence, 1);
when TX_FRAME_CHECK_SEQUENCE3 =>
tx_state <= t_tx_state'succ(tx_state);
--tx_state <= t_tx_state'succ(tx_state); -- ISE14.7 does not synthesize this
tx_state <= TX_FRAME_CHECK_SEQUENCE4;
data_out := fcs_output_byte(tx_frame_check_sequence, 2);
when TX_FRAME_CHECK_SEQUENCE4 =>
tx_state <= TX_INTERPACKET_GAP;
Expand Down
Loading