Compare commits

...

4 Commits

5 changed files with 348 additions and 53 deletions

View File

@ -32,7 +32,7 @@ architecture bench of cocotb_top_hwitl is
-- Generic test bench
signal bench_ready : std_logic := '0';
begin
top_mac_test_inst : entity top_hwitl (mac) -- TODO: I haven't managed to get configurations working with cocotb yet, so changing this line changes the test case :(
top_mac_test_inst : entity top_hwitl(eth) -- TODO: I haven't managed to get configurations working with cocotb yet, so changing this line changes the test case :(
port map(
clk => clk,
rst_n => rst_n,

View File

@ -44,11 +44,6 @@ architecture mac of top_hwitl is
constant F_CLK : integer := 50000000;
constant F_CLK_PHY : integer := 140000000;
constant LED_BLINK : boolean_vector(led_n'range) := (
6 downto 2 => true,
others => false
);
constant ETH_CONFIG : configuration_t := (
mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33")
);
@ -57,8 +52,6 @@ architecture mac of top_hwitl is
signal clk_phy : std_logic;
signal phy_pll_lock : std_logic;
signal led_sig : std_logic_vector(led_n'range);
signal phy_out : phy_out_t;
signal phy_in : phy_in_t;
@ -70,9 +63,6 @@ architecture mac of top_hwitl is
constant BYTE_CNT_MAX : integer := 100;
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
signal button_n_sync : std_logic_vector(button_n'range);
signal button : std_logic_vector(button_n'range);
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
signal tmo : integer range 0 to TMO_MAX;
begin
@ -118,42 +108,6 @@ begin
end if;
end process reset_sync;
ledcon_inst : entity work.ledcon
generic map(
F_CLK => F_CLK,
BLINK => LED_BLINK
)
port map(
clk => clk,
rst => rst,
sig => led_sig,
led_n => led_n
);
led_sig <= (
0 => phy_pll_lock,
1 => phy_out.carrier_detect,
2 => phy_out.rx_active,
3 => phy_out.rx_error,
4 => mac_out.rx_mac_crc_ok,
5 => mac_out.rx_mac_crc_error,
6 => mac_out.tx_active,
7 => '0'
);
synchronizer_inst : entity trashernet.synchronizer
generic map(
SIZE => 2
)
port map(
clk => clk,
rst => rst,
data_in => button_n(0),
data_out => button_n_sync(0)
);
button <= not button_n_sync;
receiver : process(clk, rst) is
begin
if rst then
@ -174,7 +128,7 @@ begin
end if;
end if;
if (tmo = 0) or (button(0) = '1') then
if tmo = 0 then
state <= TX;
byte_cnt <= BYTE_CNT_MAX;
mac_in.tx_header.mac_destination <= (others => x"FF");
@ -187,7 +141,7 @@ begin
if mac_out.tx_mac_data_ack then
if byte_cnt = 1 then
mac_in.tx_mac_data_en <= '0';
state <= IDLE;
state <= IDLE;
else
byte_cnt <= byte_cnt - 1;
end if;
@ -203,3 +157,127 @@ begin
debug_data(1) <= tx_n;
end architecture mac;
-- -------------------------------------------------------------------------- --
-- top_hwitl (eth)
-- -------------------------------------------------------------------------- --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
library trashernet;
use trashernet.trashernet_pkg.all;
architecture eth of top_hwitl is
component pll0
port(
CLK : in std_logic;
CLKOP : out std_logic;
LOCK : out std_logic
);
end component pll0;
constant F_CLK : integer := 50000000;
constant F_CLK_PHY : integer := 140000000;
constant ETH_CONFIG : configuration_t := (
mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33")
);
signal rst : std_logic;
signal clk_phy : std_logic;
signal phy_pll_lock : std_logic;
signal phy_out : phy_out_t;
signal phy_in : phy_in_t;
signal mac_out : mac_out_t;
signal mac_in : mac_in_t;
constant PROT_ARP : integer := 0;
constant PROT_IP : integer := 1;
constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := (PROT_ARP => ETHERNET_II_PROTOCOL_ARP, PROT_IP => ETHERNET_II_PROTOCOL_IP);
signal ethernet_i_out : ethernet_i_out_t;
signal ethernet_i_in : ethernet_i_in_t;
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
begin
trashernet_eth_inst : entity trashernet.trashernet_eth
generic map(
ETHERNET_II_PROTOCOLS => ETHERNET_II_PROTOCOLS
)
port map(
clk => clk,
rst => rst,
mac_out => mac_out,
mac_in => mac_in,
config => ETH_CONFIG,
ethernet_i_out => ethernet_i_out,
ethernet_i_in => ethernet_i_in,
ethernet_ii_out => ethernet_ii_out,
ethernet_ii_in => ethernet_ii_in
);
trashernet_mac_inst : entity trashernet.trashernet_mac
port map(
clk => clk,
rst => rst,
phy_out => phy_out,
phy_in => phy_in,
mac_out => mac_out,
mac_in => mac_in
);
pll0_inst : pll0
port map(
CLK => clk,
CLKOP => clk_phy,
LOCK => phy_pll_lock
);
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
generic map(
F_CLK => F_CLK,
F_CLK_PHY => F_CLK_PHY
)
port map(
clk => clk,
phy_clk => clk_phy,
rst => rst,
phy_out => phy_out,
phy_in => phy_in,
rx_p => rx_p,
tx_p => tx_p,
tx_n => tx_n
);
reset_sync : process(clk, rst_n) is
begin
if (rst_n = '0') then
rst <= '1';
elsif (rising_edge(clk)) then
rst <= '0';
end if;
end process reset_sync;
receiver : process(clk, rst) is
begin
if rst then
elsif rising_edge(clk) then
if ethernet_i_out.rx_header_rcv then
report "RX ETH-I: " & to_string(to_integer(ethernet_i_out.rx_length));
end if;
if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then
report "RX ARP";
end if;
if (ethernet_ii_out(PROT_IP).rx_header_rcv) then
report "RX IP";
end if;
end if;
end process receiver;
end architecture eth;

View File

@ -52,10 +52,6 @@ architecture rtl of top_mac_test is
others => false
);
type configuration_t is record
mac_address : mac_addr_t;
end record configuration_t;
constant ETH_CONFIG : configuration_t := (
mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33")
);

View File

@ -0,0 +1,170 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- trashernet_eth.vhd : Ethernet OSI Layer 2, Data Link, ETH I/II Ethertype
-- Implements arbitration of different ethertypes.
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.trashernet_pkg.all;
entity trashernet_eth is
generic(
ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ETHERNET_II_PROTOCOLS_NONE
);
port(
-- Global
clk : in std_logic; -- Global clock
rst : in std_logic; -- Asynchronous reset
-- MAC application interface
mac_out : in mac_out_t; -- MAC application IF (out of MAC)
mac_in : out mac_in_t; -- MAC application IF (into MAC)
-- Configuration
config : in configuration_t; -- Global Trashernet configuration
-- Ethernet 802.3 application interface
ethernet_i_out : out ethernet_i_out_t; -- Ethernet 802.3 IF (out from MAC)
ethernet_i_in : in ethernet_i_in_t := ETHERNET_I_IN_UNUSED; -- Ethernet 802.3 IF (out from MAC)
-- Ethernet II application interface
ethernet_ii_out : out ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); -- Ethernet II IF (out from MAC)
ethernet_ii_in : in ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range) := (others => ETHERNET_II_IN_UNUSED) -- Ethernet II IF (into MAC)
);
end entity trashernet_eth;
architecture rtl of trashernet_eth is
signal tx_data_ack_ii : std_logic_vector(ethernet_ii_out'range);
signal tx_data_ack_i : std_logic;
begin
rx : block
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2;
signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high;
signal mac_destination_matches : std_logic;
signal rx_mac_header_rcv_delayed : std_logic;
begin
mac_destination_matches <= '1' when --
((mac_out.rx_header.mac_destination = (x"FF", x"FF", x"FF", x"FF", x"FF", x"FF")) or --
(mac_out.rx_header.mac_destination = config.mac_address)) else
'0';
mux : process(clk, rst) is
begin
if rst then
sel <= SEL_ETH_NONE;
rx_mac_header_rcv_delayed <= '0';
elsif rising_edge(clk) then
rx_mac_header_rcv_delayed <= mac_out.rx_mac_header_rcv;
if mac_out.rx_mac_header_rcv then
sel <= SEL_ETH_NONE; -- By default, let's assume it's not for us
if mac_destination_matches then
if (unsigned(std_logic_vector'(mac_out.rx_header.mac_ethertype(0) & mac_out.rx_header.mac_ethertype(1))) < 1500) then -- Ethernet 802.3 Frame
sel <= SEL_ETH_I;
else -- Ethernet II Frame
for i in ETHERNET_II_PROTOCOLS'range loop
if (mac_out.rx_header.mac_ethertype = ETHERNET_II_PROTOCOLS(i).ethertype) then
sel <= i;
end if;
end loop;
end if;
end if;
end if;
end if;
end process mux;
-- Shared fields in the Ethernet II application interface
shared_fields : for i in ethernet_ii_out'range generate
ethernet_ii_out(i).rx_mac_address <= mac_out.rx_header.mac_source;
ethernet_ii_out(i).rx_data <= mac_out.rx_mac_data;
ethernet_ii_out(i).rx_data_valid <= mac_out.rx_mac_valid when sel = i else '0';
ethernet_ii_out(i).rx_crc_error <= mac_out.rx_mac_crc_error when sel = i else '0';
ethernet_ii_out(i).rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = i else '0';
ethernet_ii_out(i).rx_header_rcv <= rx_mac_header_rcv_delayed when sel = i else '0';
end generate shared_fields;
-- Shared fields in the Ethernet application interface
ethernet_i_out.rx_data <= mac_out.rx_mac_data;
ethernet_i_out.rx_data_valid <= mac_out.rx_mac_valid when sel = SEL_ETH_I;
ethernet_i_out.rx_crc_error <= mac_out.rx_mac_crc_error when sel = SEL_ETH_I else '0';
ethernet_i_out.rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = SEL_ETH_I else '0';
ethernet_i_out.rx_header_rcv <= rx_mac_header_rcv_delayed when sel = SEL_ETH_I else '0';
ethernet_i_out.rx_mac_address <= mac_out.rx_header.mac_source;
ethernet_i_out.rx_length <= unsigned(mac_out.rx_header.mac_ethertype(0)) & unsigned(mac_out.rx_header.mac_ethertype(1));
-- TX fields (need to be driven here to avoid multiple drivers (blocks drive ALL signals of a record))
tx_fields : for i in ethernet_ii_out'range generate
ethernet_ii_out(i).tx_data_ack <= tx_data_ack_ii(i);
end generate;
ethernet_i_out.tx_data_ack <= tx_data_ack_i;
end block rx;
tx : block
type state_t is (IDLE, TXD);
signal state : state_t;
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
signal sel : integer range SEL_ETH_I to ethernet_ii_in'high;
begin
arb : process(clk, rst) is
begin
if rst then
elsif rising_edge(clk) then
case state is
when IDLE =>
if mac_out.tx_active then
if (ethernet_i_in.tx_en) then -- ETH I has priority
sel <= SEL_ETH_I;
state <= TXD;
else
for i in ethernet_ii_in'range loop
if ethernet_ii_in(i).tx_en then
sel <= i;
state <= TXD;
exit; -- Prioritize according to vector
end if;
end loop;
end if;
end if;
when TXD =>
state <= IDLE when (mac_in.tx_mac_data_en or mac_out.tx_active);
end case;
end if;
end process arb;
mux : process(all) is
begin
if (sel = SEL_ETH_I) then
mac_in.tx_mac_data_en <= ethernet_i_in.tx_en;
mac_in.tx_mac_data <= ethernet_i_in.tx_data;
tx_data_ack_i <= mac_out.tx_mac_data_ack;
mac_in.tx_header.mac_destination <= ethernet_i_in.tx_mac_address;
mac_in.tx_header.mac_ethertype <= byte(ethernet_i_in.tx_length(15 downto 8)) & byte(ethernet_i_in.tx_length(7 downto 0));
else
mac_in.tx_mac_data_en <= ethernet_ii_in(sel).tx_en;
mac_in.tx_mac_data <= ethernet_ii_in(sel).tx_data;
txack : for i in ethernet_ii_out'range loop
tx_data_ack_ii(i) <= mac_out.tx_mac_data_ack when sel = i else '0';
end loop txack;
mac_in.tx_header.mac_destination <= ethernet_ii_in(sel).tx_mac_address;
mac_in.tx_header.mac_ethertype <= ETHERNET_II_PROTOCOLS(sel).ethertype;
end if;
mac_in.tx_header.mac_source <= config.mac_address;
end process mux;
end block tx;
end architecture rtl;

View File

@ -10,6 +10,7 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package trashernet_pkg is
-- General types
@ -29,6 +30,11 @@ package trashernet_pkg is
-- IP specific types
subtype ip_addr_t is byte_vector(0 to 3);
-- Configuration interface
type configuration_t is record
mac_address : mac_addr_t; -- MAC address of this node
end record configuration_t;
-- PHY interface
type phy_in_t is record
tx_data : byte; -- TX Data
@ -65,6 +71,51 @@ package trashernet_pkg is
rx_mac_crc_error : std_logic; -- End of packet, CRC invalid
end record mac_out_t;
-- MAC ETH interface
type ethernet_ii_protocol_t is record
ethertype : ethertype_t;
end record;
type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t;
constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- NULL range
constant ETHERNET_II_PROTOCOL_IP : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06"));
type ethernet_i_out_t is record
rx_mac_address : mac_addr_t; -- Source MAC address
rx_crc_ok : std_logic; -- End of packet, CRC OK
rx_crc_error : std_logic; -- End of packet, CRC invalid
rx_data : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe
rx_length : unsigned(15 downto 0); -- RX payload length in bytes
rx_header_rcv : std_logic; -- Start of reception, `rx_length` is valid
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
end record;
type ethernet_i_in_t is record
tx_mac_address : mac_addr_t; -- Destination MAC address
tx_data : byte; -- TX data
tx_en : std_logic; -- Start and continue transmitting
tx_length : unsigned(15 downto 0); -- TX payload length in bytes
end record;
constant ETHERNET_I_IN_UNUSED : ethernet_i_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", tx_length => x"0000", others => '0'); -- TODO
type ethernet_ii_out_t is record
rx_mac_address : mac_addr_t; -- Source MAC address
rx_data : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe
rx_crc_ok : std_logic; -- End of packet, CRC OK
rx_crc_error : std_logic; -- End of packet, CRC invalid
rx_header_rcv : std_logic; -- Start of reception
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
end record;
type ethernet_ii_in_t is record
tx_mac_address : mac_addr_t; -- Destination MAC address
tx_data : byte; -- TX data
tx_en : std_logic; -- Start and continue transmitting
end record;
type ethernet_ii_out_vector is array (natural range <>) of ethernet_ii_out_t;
type ethernet_ii_in_vector is array (natural range <>) of ethernet_ii_in_t;
constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0');
end package trashernet_pkg;
package body trashernet_pkg is