446 lines
12 KiB
VHDL
446 lines
12 KiB
VHDL
-- -------------------------------------------------------------------------- --
|
|
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
|
-- -------------------------------------------------------------------------- --
|
|
-- top_hwitl.vhd: Top level design for hardware-in-the-loop tests
|
|
--
|
|
-- Target: Simulation
|
|
-- -------------------------------------------------------------------------- --
|
|
-- 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 ieee.math_real.all;
|
|
|
|
library trashernet;
|
|
use trashernet.trashernet_pkg.all;
|
|
|
|
entity top_hwitl is
|
|
port(
|
|
clk : in std_logic;
|
|
rst_n : in std_logic;
|
|
rx_p : in std_logic;
|
|
tx_p : out std_logic;
|
|
tx_n : out std_logic;
|
|
led_n : out std_logic_vector(7 downto 0); -- @suppress: Used in different architectures
|
|
button_n : in std_logic_vector(3 downto 0); -- @suppress: Used in different architectures
|
|
debug_data : out std_logic_vector(7 downto 0) -- @suppress: Used in different architectures
|
|
);
|
|
end entity top_hwitl;
|
|
|
|
architecture mac 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_out : phy_out_t;
|
|
signal phy_in : phy_in_t;
|
|
|
|
signal mac_out : mac_out_t;
|
|
signal mac_in : mac_in_t;
|
|
|
|
type status_t is (IDLE, TX);
|
|
signal state : status_t;
|
|
constant BYTE_CNT_MAX : integer := 100;
|
|
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
|
|
|
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
|
|
signal tmo : integer range 0 to TMO_MAX;
|
|
begin
|
|
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 => open
|
|
);
|
|
|
|
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
|
|
state <= IDLE;
|
|
mac_in.tx_mac_data_en <= '0';
|
|
tmo <= 0;
|
|
elsif rising_edge(clk) then
|
|
if (tmo /= 0) then
|
|
tmo <= tmo - 1;
|
|
end if;
|
|
case state is
|
|
when IDLE =>
|
|
if mac_out.rx_mac_crc_ok then
|
|
if (mac_out.rx_header.mac_destination = ETH_CONFIG.mac_address) then -- Note: We won't respond to broadcasts!
|
|
state <= TX;
|
|
byte_cnt <= BYTE_CNT_MAX;
|
|
mac_in.tx_header.mac_destination <= mac_out.rx_header.mac_source;
|
|
end if;
|
|
end if;
|
|
|
|
if tmo = 0 then
|
|
state <= TX;
|
|
byte_cnt <= BYTE_CNT_MAX;
|
|
mac_in.tx_header.mac_destination <= (others => x"FF");
|
|
end if;
|
|
when TX =>
|
|
tmo <= TMO_MAX;
|
|
mac_in.tx_header.mac_ethertype <= (x"00", std_logic_vector(to_unsigned(BYTE_CNT_MAX, 8)));
|
|
mac_in.tx_mac_data_en <= '1';
|
|
mac_in.tx_mac_data <= std_logic_vector(to_unsigned(byte_cnt, 8));
|
|
if mac_out.tx_mac_data_ack then
|
|
if byte_cnt = 1 then
|
|
mac_in.tx_mac_data_en <= '0';
|
|
state <= IDLE;
|
|
else
|
|
byte_cnt <= byte_cnt - 1;
|
|
end if;
|
|
end if;
|
|
end case;
|
|
|
|
end if;
|
|
end process receiver;
|
|
|
|
mac_in.tx_header.mac_source <= ETH_CONFIG.mac_address;
|
|
|
|
debug_data(0) <= tx_p;
|
|
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")
|
|
);
|
|
constant IP_CONFIG : ipv4_configuration_t := (
|
|
ip_address => (x"C0", x"A8", x"02", x"02"),
|
|
subnet_mask => (x"FF", x"FF", x"FF", x"00"),
|
|
gateway => (x"C0", x"A8", x"02", x"01")
|
|
);
|
|
|
|
signal rst : std_logic;
|
|
signal clk_phy : 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_IPV4 : integer := 1;
|
|
constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := (
|
|
PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
|
|
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
|
|
);
|
|
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);
|
|
|
|
signal arp_out : arp_out_t;
|
|
signal arp_in : arp_in_t;
|
|
|
|
signal ipv4_out : ipv4_out_t;
|
|
signal ipv4_in : ipv4_in_t;
|
|
|
|
constant IPROT_ICMP : integer := 0;
|
|
constant IPROT_UDP : integer := 1;
|
|
constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
|
|
IPROT_ICMP => IPV4_PROTOCOL_ICMP,
|
|
IPROT_UDP => IPV4_PROTOCOL_UDP
|
|
);
|
|
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
|
|
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
|
|
begin
|
|
trashernet_icmp_inst : entity trashernet.trashernet_icmp
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
ipv4_protocol_out => ipv4_protocol_out(IPROT_ICMP),
|
|
ipv4_protocol_in => ipv4_protocol_in(IPROT_ICMP)
|
|
);
|
|
|
|
trashernet_ipv4prot_inst : entity trashernet.trashernet_ipv4prot
|
|
generic map(
|
|
IPV4_PROTOCOLS => IPV4_PROTOCOLS
|
|
)
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
ipv4_out => ipv4_out,
|
|
ipv4_in => ipv4_in,
|
|
ipv4_protocol_out => ipv4_protocol_out,
|
|
ipv4_protocol_in => ipv4_protocol_in
|
|
);
|
|
|
|
trashernet_ipv4_inst : entity trashernet.trashernet_ipv4
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
ipv4_config => IP_CONFIG,
|
|
arp_out => arp_out,
|
|
arp_in => arp_in,
|
|
ethernet_ii_out => ethernet_ii_out(PROT_IPV4),
|
|
ethernet_ii_in => ethernet_ii_in(PROT_IPV4),
|
|
ipv4_out => ipv4_out,
|
|
ipv4_in => ipv4_in
|
|
);
|
|
|
|
trashernet_arp_inst : entity trashernet.trashernet_arp
|
|
generic map(
|
|
SYSTICK_FREQ => F_CLK
|
|
)
|
|
port map(
|
|
clk => clk,
|
|
rst => rst,
|
|
systick => '1',
|
|
mac_config => ETH_CONFIG,
|
|
ip_config => IP_CONFIG,
|
|
arp_out => arp_out,
|
|
arp_in => arp_in,
|
|
ethernet_ii_out => ethernet_ii_out(PROT_ARP),
|
|
ethernet_ii_in => ethernet_ii_in(PROT_ARP)
|
|
);
|
|
|
|
ethernet_i_in <= ethernet_i_in_t'(
|
|
tx_mac_address => (others => (others => '-')),
|
|
tx_data => (others => '-'),
|
|
tx_en => '0',
|
|
tx_length => (others => '-')
|
|
);
|
|
|
|
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 => open
|
|
);
|
|
|
|
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 ipv4_out.rx_ok_stb then
|
|
report "RX IP OK";
|
|
end if;
|
|
if ipv4_out.rx_error_stb then
|
|
report "RX IP error";
|
|
end if;
|
|
if ipv4_out.rx_header_rcv then
|
|
report "RX IP start. PROT=" & to_hstring(ipv4_out.rx_protocol) & ", FROM=" & ip_to_string(ipv4_out.rx_ip_address);
|
|
end if;
|
|
end if;
|
|
end process receiver;
|
|
|
|
udp : process(clk) is
|
|
begin
|
|
if rising_edge(clk) then
|
|
if ipv4_protocol_out(IPROT_UDP).rx_header_rcv then
|
|
report "Receive UDP";
|
|
end if;
|
|
end if;
|
|
end process udp;
|
|
|
|
tx_udp_p : block
|
|
type state_t is (IDLE, TX, DONE);
|
|
signal state : state_t;
|
|
|
|
constant PACKET : byte_vector := (
|
|
x"10", x"00", -- Source port
|
|
x"04", x"00", -- Destination port
|
|
x"00", x"15", -- Length
|
|
x"00", x"00", -- Checksum (not used)
|
|
x"48", x"65", x"6C", x"6C", x"6F", x"20", x"57", x"6F", x"72", x"6C", x"64", x"21", x"0a" -- Payload
|
|
);
|
|
signal sr : byte_vector(PACKET'range);
|
|
signal cnt : integer range 0 to sr'length - 1;
|
|
|
|
begin
|
|
tx_udp : process(clk, rst) is
|
|
begin
|
|
if rst then
|
|
state <= IDLE;
|
|
|
|
elsif rising_edge(clk) then
|
|
case state is
|
|
when IDLE =>
|
|
if button_n(0) = '0' then
|
|
state <= TX;
|
|
sr <= PACKET;
|
|
cnt <= sr'length - 1;
|
|
report "UDP: Start TX";
|
|
end if;
|
|
|
|
when TX =>
|
|
if ipv4_protocol_out(IPROT_UDP).tx_data_ack then
|
|
sr <= sr(sr'low + 1 to sr'high) & x"00";
|
|
if cnt = 0 then
|
|
state <= DONE;
|
|
else
|
|
cnt <= cnt - 1;
|
|
end if;
|
|
end if;
|
|
if ipv4_protocol_out(IPROT_UDP).tx_err_stb then
|
|
report "UDP: TX ERROR";
|
|
state <= DONE;
|
|
end if;
|
|
if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then
|
|
report "UDP: TX OK stb in TX state -- that shouldn't happen, right?";
|
|
end if;
|
|
|
|
when DONE =>
|
|
null;
|
|
|
|
end case;
|
|
end if;
|
|
end process tx_udp;
|
|
ipv4_protocol_in(IPROT_UDP).tx_en <= '1' when state = TX else '0';
|
|
ipv4_protocol_in(IPROT_UDP).tx_ip_address <= (x"C0", x"A8", x"02", x"01");
|
|
ipv4_protocol_in(IPROT_UDP).tx_length <= to_unsigned(sr'length, 16);
|
|
ipv4_protocol_in(IPROT_UDP).tx_data <= sr(0);
|
|
end block tx_udp_p;
|
|
|
|
icmp : process(clk) is
|
|
begin
|
|
if rising_edge(clk) then
|
|
if ipv4_protocol_out(IPROT_ICMP).rx_header_rcv then
|
|
report "Receive ICMP";
|
|
end if;
|
|
end if;
|
|
end process icmp;
|
|
|
|
end architecture eth;
|