Compare commits

..

1 Commits

Author SHA1 Message Date
e441663586 arp: Implement gateway selection
Fixes #17.
2023-01-21 20:22:12 +01:00
32 changed files with 340 additions and 1319 deletions

View File

@ -1,10 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<com.sigasi.hdt.shared.librarymapping.model:LibraryMappings xmlns:com.sigasi.hdt.shared.librarymapping.model="com.sigasi.hdt.vhdl.scoping.librarymapping" Version="2">
<Mappings Location="bench" Library="bench"/>
<Mappings Location="cocotb" Library="cocotb"/>
<Mappings Location="design" Library="design"/>
<Mappings Location="Common Libraries/IEEE" Library="ieee"/>
<Mappings Location="Common Libraries/IEEE Synopsys" Library="ieee"/>
<Mappings Location="" Library="not mapped"/>
<Mappings Location="Common Libraries" Library="not mapped"/>
<Mappings Location="Common Libraries/vunit" Library="not mapped"/>
<Mappings Location="cocotb/__pycache__" Library="not mapped"/>
<Mappings Location="cocotb/sim_build" Library="not mapped"/>
<Mappings Location="Common Libraries/STD" Library="std"/>
<Mappings Location="trashernet" Library="trashernet"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/IP_complete_nomac.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4_RX.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4_TX.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_Complete_nomac.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_RX.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_TX.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arp.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_REQ.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_RX.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_STORE_br.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_SYNC.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_TX.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_types.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/arpv2.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/axi.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/ipv4_types.vhd" Library="udpip"/>
<Mappings Location="udp_ip_stack/rtl/vhdl/tx_arbitrator.vhd" Library="udpip"/>
<Mappings Location="Common Libraries/vunit/vunit/vhdl" Library="vunit_lib"/>
<Mappings Location="Common Libraries/src" Library="xp2"/>
</com.sigasi.hdt.shared.librarymapping.model:LibraryMappings>

View File

@ -5,6 +5,16 @@
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/com.sigasi.hdt.toolchains.vunit.builder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments>
@ -12,9 +22,9 @@
</buildCommand>
</buildSpec>
<natures>
<nature>com.sigasi.hdt.verilog.ui.verilogNature</nature>
<nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
<nature>com.sigasi.hdt.toolchains.vunit.nature</nature>
</natures>
<linkedResources>
<link>
@ -22,6 +32,11 @@
<type>2</type>
<locationURI>virtual:/virtual</locationURI>
</link>
<link>
<name>Common Libraries/DRAG_REUSABLE_LIBRARIES_HERE.txt</name>
<type>1</type>
<locationURI>sigasiresource:/vhdl/readme.txt</locationURI>
</link>
<link>
<name>Common Libraries/IEEE</name>
<type>2</type>
@ -37,5 +52,15 @@
<type>2</type>
<locationURI>sigasiresource:/vhdl/2008/STD</locationURI>
</link>
<link>
<name>Common Libraries/src</name>
<type>2</type>
<location>/usr/local/diamond/3.12/cae_library/simulation/vhdl/xp2/src</location>
</link>
<link>
<name>Common Libraries/vunit</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/vunit</locationURI>
</link>
</linkedResources>
</projectDescription>

View File

@ -0,0 +1,2 @@
VUnitScriptLocation=vunit/run.py
eclipse.preferences.version=1

View File

@ -1,2 +0,0 @@
eclipse.preferences.version=1
isEnabled=true

View File

@ -1 +0,0 @@
<project>=sv2012

View File

@ -1,2 +0,0 @@
eclipse.preferences.version=1
isEnabled=true

View File

@ -1,5 +1,5 @@
eclipse.preferences.version=1
encoding//Common\ Libraries/IEEE=UTF-8
encoding//Common\ Libraries/IEEE\ Synopsys=UTF-8
encoding//Common\ Libraries/STD=UTF-8
encoding//Common\ Libraries=UTF-8
encoding//Common\ Libraries/IEEE=utf-8
encoding//Common\ Libraries/IEEE\ Synopsys=utf-8
encoding//Common\ Libraries/STD=utf-8
encoding/Common\ Libraries=utf-8

View File

@ -4,10 +4,9 @@
Trashernet is a very trashy Ethernet stack for FPGAs written in VHDL aiming to cover all OSI layers:
* Layer 1, Physical: `trashernet_phy`, (`trashernet_rmii`)
* Layer 1, Physical: `trashernet_phy`
* Layer 2, Data link: `trashernet_mac`, `trashernet_eth`, `trashernet_arp`
* Layer 3, Network: `trashernet_ipv4`, `trashernet_ipv4prot`, `trashernet_icmp`
* Layer 4, Transport: `trashernet_udp`, `trashernet_udpprot`
When writing it, the following were the main design philosophies:
@ -17,8 +16,6 @@ When writing it, the following were the main design philosophies:
**IMPORTANT:** This code (and HW suggestions) violate the Ethernet standard in many ways. Do not use in proper (especially commercial) products.
Note: The `trashernet_rmii` component uses a standard RMII Ethernet PHY instead of the hardware suggestions below. If you are looking for the true Trashernet experience, choose the `trashernet_phy` instead.
## Hardware
![Circuit Diagram](doc/circuit.svg)
@ -31,14 +28,6 @@ Notes:
* The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice!
* On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D
## Stats
Sythesized for a Lattice LFXP2-5E FPGA using LSE / Diamond v3.12:
| Configuration | Slices | LUT4 | Registers |
| ---------------- | ------ | ---- | --------- |
| `trashernet_phy` | 115 | 188 | 91 |
## License
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.

View File

@ -31,6 +31,7 @@ architecture bench of bench_trashernet_phy is
signal tx_p : std_logic;
signal tx_n : std_logic;
begin
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
generic map(

View File

@ -1,218 +0,0 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- bench_trashernet_mac.vhd : Stimulus-only test bench for the MAC+PHY parts
-- Tests TX path through the RX path. Not great, but whatever.
-- -------------------------------------------------------------------------- --
-- 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.bench_pkg.all;
library trashernet;
use trashernet.trashernet_pkg.all;
library vunit_lib;
context vunit_lib.vunit_context;
entity bench_trashernet_rmii is
generic(
runner_cfg : string
);
end entity bench_trashernet_rmii;
architecture bench of bench_trashernet_rmii is
signal clk : std_logic;
signal rst : std_logic;
signal phy_out : phy_out_t;
signal phy_in : phy_in_t;
signal rmii_ref_clk : std_logic;
signal rmii_crs_dv : std_logic;
signal rmii_rxd : std_logic_vector(1 downto 0);
signal rmii_tx_en : std_logic;
signal rmii_txd : std_logic_vector(1 downto 0);
signal rmii_tx_start : std_logic := '0';
signal trashernet_tx_start : std_logic := '0';
begin
trashernet_rmii_inst : entity trashernet.trashernet_rmii
generic map(
SYSCLK_IS_REFCLK => false
)
port map(
clk => clk,
rst => rst,
phy_out => phy_out,
phy_in => phy_in,
rmii_ref_clk => rmii_ref_clk,
rmii_crs_dv => rmii_crs_dv,
rmii_rxd => rmii_rxd,
rmii_tx_en => rmii_tx_en,
rmii_txd => rmii_txd
);
clockDriver : process
constant period : time := 10 ns;
begin
clk <= '0';
wait for period / 2;
clk <= '1';
wait for period / 2;
end process clockDriver;
test : process is
begin
rst <= '1';
test_runner_setup(runner, runner_cfg);
wait for 10 ns;
rst <= '0';
wait for 10 ns;
while test_suite loop
if run("rx") then
rmii_tx_start <= not rmii_tx_start;
wait until phy_out.rx_active = '1' for 1 ms;
assert phy_out.rx_active'event report "Timeout for reception to start";
wait until phy_out.rx_active = '0' for 1 ms;
assert phy_out.rx_active'event report "Timeout for reception to end";
wait for 1 us;
elsif run("tx") then
trashernet_tx_start <= not trashernet_tx_start;
wait until phy_out.tx_active = '1' for 1 ms;
assert phy_out.tx_active'event report "Timeout for transmission to start";
wait until phy_out.tx_active = '0' for 1 ms;
assert phy_out.tx_active'event report "Timeout for transmission to stop";
wait until phy_out.tx_active = '1' for 1 ms;
assert phy_out.tx_active'event report "Timeout for transmission to start";
wait until phy_out.tx_active = '0' for 1 ms;
assert phy_out.tx_active'event report "Timeout for transmission to stop";
end if;
end loop;
wait for 1 ns;
test_runner_cleanup(runner);
end process test;
phy_rx : process(clk) is
begin
if rising_edge(clk) then
if phy_out.rx_data_valid = '1' then
report "RX data: " & to_hstring(phy_out.rx_data);
end if;
end if;
end process phy_rx;
rmiirefclk : process
constant period : time := 20 ns;
begin
rmii_ref_clk <= '0';
wait for period / 2;
rmii_ref_clk <= '1';
wait for period / 2;
end process rmiirefclk;
rmii_tx_gen : process is
procedure send_frame(data : byte_vector) is
variable sr : byte;
begin
wait until rising_edge(rmii_ref_clk);
rmii_rxd <= "01";
wait for 2.5 ns;
rmii_crs_dv <= '1';
for i in 0 to 10 loop
wait until rmii_ref_clk;
end loop;
rmii_rxd <= "11";
wait until rmii_ref_clk;
for i in data'range loop
sr := data(i);
for j in 0 to 3 loop
rmii_rxd <= sr(1 downto 0);
wait until rising_edge(rmii_ref_clk);
sr := "XX" & sr(sr'high downto 2);
end loop;
end loop;
rmii_crs_dv <= '0';
wait for 1 us; -- IPG
end procedure send_frame;
begin
rmii_crs_dv <= '0';
rmii_rxd <= (others => '0');
wait on rmii_tx_start;
send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", 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", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea"));
wait;
end process rmii_tx_gen;
trashernet_tx_gen : process is
procedure send_frame(data : byte_vector) is
begin
for i in data'range loop
phy_in.tx_data_en <= '1';
phy_in.tx_data <= data(i);
wait until rising_edge(clk);
while not phy_out.tx_data_ack loop
wait until rising_edge(clk);
end loop;
end loop;
phy_in.tx_data_en <= '0';
wait until rising_edge(clk);
end procedure send_frame;
begin
phy_in.tx_data_en <= '0';
wait on trashernet_tx_start;
wait until rising_edge(clk);
send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", 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", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea"));
-- Currently, there's a bug in the CDC preventing single-cycle deassertions from deasserting the transmit enable
wait until rising_edge(clk);
wait until rising_edge(clk);
wait until rising_edge(clk);
send_frame(byte_vector'(x"c6", x"b8", x"c1", x"db", x"b1", x"1d", x"00", x"ff", x"ff", x"11", x"22", x"33", x"08", x"00", x"45", x"00", x"00", x"29", x"00", x"00", x"00", x"00", x"40", x"11", x"f5", x"70", x"c0", x"a8", x"02", x"02", x"c0", x"a8", x"02", x"01", x"ab", x"cd", x"00", x"ff", x"00", x"15", x"00", x"00", 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", x"00", x"00", x"00", x"00", x"00", x"64", x"90", x"a9", x"ea"));
end process trashernet_tx_gen;
rmii_receiver : process(rmii_ref_clk) is
variable sr : byte;
variable cnt : integer range 0 to 3;
variable active : boolean := false;
begin
if rising_edge(rmii_ref_clk) then
if rmii_tx_en then
if not active then
report "RMII RX start";
end if;
sr := rmii_txd & sr(sr'high downto 2);
if cnt = 3 then
cnt := 0;
report "RMII RX: " & to_hstring(sr);
else
cnt := cnt + 1;
end if;
active := true;
else
if active then
report "RMII RX stop";
end if;
cnt := 0;
active := false;
end if;
end if;
end process rmii_receiver;
end architecture bench;

View File

@ -1,7 +0,0 @@
configuration cfg_cocotb_top_hwitl_mac of cocotb_top_hwitl is
for bench
for top_mac_test_inst : top_hwitl
use entity design.top_hwitl(mac);
end for;
end for;
end configuration cfg_cocotb_top_hwitl_mac;

View File

@ -1,8 +0,0 @@
#!/bin/bash
# Set up virtual device using
sudo ip link add dev veth1 type veth peer name veth2
sudo ip link set up veth1
sudo ip link set up veth2
sudo ip addr add 192.168.2.1/24 dev veth1
sudo ip link set promisc on dev veth1

View File

@ -225,37 +225,7 @@ architecture eth of top_hwitl is
);
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
signal udp_out : udp_out_t;
signal udp_in : udp_in_t;
signal udpprot_rx_out : udpprot_rx_out_vector(0 to 0);
signal udpprot_rx_in : udpprot_rx_in_vector(0 to 0);
signal udpprot_tx_out : udpprot_tx_out_vector(0 to 0);
signal udpprot_tx_in : udpprot_tx_in_vector(0 to 0);
begin
trashernet_udpprot_inst : entity trashernet.trashernet_udpprot
port map(
clk => clk,
rst => rst,
udp_out => udp_out,
udp_in => udp_in,
udpprot_rx_out => udpprot_rx_out,
udpprot_rx_in => udpprot_rx_in,
udpprot_tx_out => udpprot_tx_out,
udpprot_tx_in => udpprot_tx_in
);
trashernet_udp_inst : entity trashernet.trashernet_udp
port map(
clk => clk,
rst => rst,
ipv4_protocol_in => ipv4_protocol_in(IPROT_UDP),
ipv4_protocol_out => ipv4_protocol_out(IPROT_UDP),
udp_out => udp_out,
udp_in => udp_in
);
trashernet_icmp_inst : entity trashernet.trashernet_icmp
port map(
clk => clk,
@ -377,7 +347,7 @@ begin
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_header.length));
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";
@ -390,7 +360,7 @@ begin
report "RX IP error";
end if;
if ipv4_out.rx_header_rcv then
report "RX IP start. PROT=" & to_hstring(ipv4_out.rx_header.protocol) & ", FROM=" & ip_to_string(ipv4_out.rx_header.ip_address);
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;
@ -409,19 +379,16 @@ begin
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
udpprot_tx_in(0).tx_ip_address <= (x"C0", x"A8", x"02", x"01");
udpprot_tx_in(0).tx_destination_port <= x"00FF";
udpprot_tx_in(0).tx_source_port <= x"ABCD";
udpprot_tx_in(0).tx_length <= to_unsigned(sr'length, 16);
udpprot_tx_in(0).tx_data <= sr(0);
udpprot_tx_in(0).tx_en <= '1' when state = TX else '0';
tx_udp : process(clk, rst) is
begin
if rst then
@ -434,12 +401,11 @@ begin
state <= TX;
sr <= PACKET;
cnt <= sr'length - 1;
report "UDP(0): Start TX";
report "UDP: Start TX";
end if;
when TX =>
if udpprot_tx_out(0).tx_data_ack then
report "UDP(0): byte ack";
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;
@ -447,12 +413,12 @@ begin
cnt <= cnt - 1;
end if;
end if;
if udpprot_tx_out(0).tx_err_stb then
report "UDP(0): TX ERROR";
if ipv4_protocol_out(IPROT_UDP).tx_err_stb then
report "UDP: TX ERROR";
state <= DONE;
end if;
if udpprot_tx_out(0).tx_ok_stb then
report "UDP(0): TX OK stb in TX state -- that shouldn't happen, right?";
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 =>
@ -461,29 +427,12 @@ begin
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;
udpprot_rx_in(0).port_bind <= x"0400";
rx_udp_p : block
begin
rx_udp : process(clk, rst) is
begin
if rst then
elsif rising_edge(clk) then
if udpprot_rx_out(0).rx_header_rcv then
report "UDP(0): RX on port " & --
to_string(to_integer(udpprot_rx_out(0).rx_header.destination_port)) & " from port " & --
to_string(to_integer(udpprot_rx_out(0).rx_header.source_port));
end if;
if udpprot_rx_out(0).rx_data_valid then
report "UDP(0): RX: " & to_hstring(udpprot_rx_out(0).rx_data);
end if;
end if;
end process rx_udp;
end block rx_udp_p;
icmp : process(clk) is
begin
if rising_edge(clk) then

View File

@ -1,2 +0,0 @@
vunit_hdl
cocotb

View File

@ -55,13 +55,12 @@ architecture rtl of trashernet_arp is
signal arp_tx_reply_ack : std_logic; -- ARP reply has been latched and is being sent
signal arp_tx_request_rq : std_logic; -- Request to transmit an ARP request
signal arp_tx_request_tpa : ip_addr_t; -- IP address that we want to know the MAC address of
signal arp_tx_request_tpa : ip_addr_t; -- IP address that we want to know the MAC address of, either real target or gateway
signal arp_tx_request_ack : std_logic; -- ARP request has been latched and is being sent
signal arp_rx_reply_stb : std_logic; -- An ARP reply was received (strobe)
signal arp_rx_sha : mac_addr_t; -- The MAC address of the reply sender, valid with arp_rx_reply
signal arp_rx_spa : ip_addr_t; -- The IP address of the reply sender, valid with arp_rx_reply
begin
resolver : block
type resolver_state_t is (IDLE, QUERY_MAC);
@ -72,8 +71,10 @@ begin
signal replied_ip : ip_addr_t;
signal replied_mac : mac_addr_t;
begin
signal arp_query_stb : std_logic; -- Pipelined version of arp_in.arp_query_stb
signal target_is_in_subnet : std_logic; -- Indicates whether the target IP is the subnet or whether to send this to the gateway
begin
arp_resolver_main : process(rst, clk) is
begin
if rst then
@ -96,7 +97,7 @@ begin
case state is
when IDLE =>
if arp_in.arp_query_stb then
if arp_query_stb then
if query_mac_found then
arp_out.arp_ok_stb <= '1';
else
@ -118,9 +119,21 @@ begin
end if;
end process arp_resolver_main;
query_mac_found <= '1' when (arp_tx_request_tpa = replied_ip) else '0';
target_is_in_subnet <= or((arp_in.arp_ip xor ip_config.gateway) and ip_config.subnet_mask);
arp_out.arp_mac <= replied_mac;
arp_tx_request_tpa <= arp_in.arp_ip;
-- Pipelines the start of query so that we get some additional time to evaluate the target+netmask.
pipeline : process (clk, rst) is
begin
if rst then
arp_tx_request_tpa <= (others => x"00");
arp_query_stb <= '0';
elsif rising_edge(clk) then
arp_tx_request_tpa <= arp_in.arp_ip when target_is_in_subnet else ip_config.gateway;
arp_query_stb <= arp_in.arp_query_stb;
end if;
end process pipeline;
timeout_timer_inst : entity work.timer
generic map(

View File

@ -87,7 +87,7 @@ begin
-- Shared fields in the Ethernet II application interface
shared_fields : for i in ethernet_ii_out'range generate
ethernet_ii_out(i).rx_header.mac_header <= mac_out.rx_header;
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';
@ -101,8 +101,8 @@ begin
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_header.mac_header <= mac_out.rx_header;
ethernet_i_out.rx_header.length <= unsigned(mac_out.rx_header.mac_ethertype(0)) & unsigned(mac_out.rx_header.mac_ethertype(1));
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
@ -141,10 +141,7 @@ begin
end if;
when TXD =>
if not mac_in.tx_mac_data_en then
state <= IDLE;
sel <= SEL_ETH_I; -- To avoid arbitration errors, always select the highest priority one by default
end if;
state <= IDLE when (not mac_in.tx_mac_data_en or not mac_out.tx_active);
end case;
end if;
end process arb;

View File

@ -140,8 +140,8 @@ begin
case state is
when IDLE =>
if tx_response then
ipv4_protocol_in.tx_ip_address <= ipv4_protocol_out.rx_header.ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯
ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_header.length;
ipv4_protocol_in.tx_ip_address <= ipv4_protocol_out.rx_ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯
ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_length;
sr <= byte_vector'(
x"00",
x"00",

View File

@ -59,7 +59,6 @@ begin
alias sr_destination_ip is sr(16 to 19);
signal header_ok : std_logic;
signal selected : std_logic;
signal bytecount : integer range 0 to 65535;
signal block_done : std_logic;
@ -78,18 +77,24 @@ begin
rx_fsm : process(clk, rst) is
begin
if rst then
state <= DONE;
state <= HEADER;
ipv4_out.rx_ok_stb <= '0';
ipv4_out.rx_error_stb <= '0';
ipv4_out.rx_header_rcv <= '0';
bytecount <= BYTECOUNT_HEADER;
selected <= '0';
elsif rising_edge(clk) then
ipv4_out.rx_ok_stb <= '0';
ipv4_out.rx_error_stb <= '0';
ipv4_out.rx_header_rcv <= '0';
if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then
state <= HEADER;
bytecount <= BYTECOUNT_HEADER;
ipv4_out.rx_error_stb <= ethernet_ii_out.rx_crc_error or to_std_logic(state = PAYLOAD);
ipv4_out.rx_ok_stb <= ethernet_ii_out.rx_crc_ok and to_std_logic(state = DONE);
end if;
if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then
if state = HEADER then
sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data;
@ -103,10 +108,7 @@ begin
bytecount <= to_integer(unsigned(sr_ihl) - 5) * 4; -- five 32-bit words is the header itself
if header_ok then
state <= OPT;
selected <= '1';
ipv4_out.rx_header_rcv <= '1';
else
state <= DONE;
end if;
end if;
@ -124,24 +126,14 @@ begin
when DONE => -- @suppress "Dead state 'DONE'": Outgoing state transition is outside of case statement
null; -- We just wait here until the MAC gives us a CRC OK/error
end case;
if selected and (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then
ipv4_out.rx_error_stb <= ethernet_ii_out.rx_crc_error or to_std_logic(state = PAYLOAD);
ipv4_out.rx_ok_stb <= ethernet_ii_out.rx_crc_ok and to_std_logic(state = DONE);
end if;
if (ethernet_ii_out.rx_header_rcv) then
state <= HEADER;
bytecount <= BYTECOUNT_HEADER;
selected <= '0';
end if;
end if;
end process rx_fsm;
ipv4_out.rx_data <= ethernet_ii_out.rx_data;
ipv4_out.rx_data_valid <= ethernet_ii_out.rx_data_valid when state = PAYLOAD else '0';
ipv4_out.rx_header.eth_header <= ethernet_ii_out.rx_header;
ipv4_out.rx_header.ip_address <= sr_source_ip;
ipv4_out.rx_header.protocol <= sr_protocol;
ipv4_out.rx_header.length <= unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3))) - to_integer(unsigned(sr_ihl)) * 4;
ipv4_out.rx_ip_address <= sr_source_ip;
ipv4_out.rx_protocol <= sr_protocol;
ipv4_out.rx_length <= unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3))) - to_integer(unsigned(sr_ihl)) * 4;
end block rx;
@ -182,13 +174,11 @@ begin
ipv4_out.tx_data_ack <= '0';
arp_in.arp_query_stb <= '0';
ipv4_out.tx_err_stb <= '0';
ipv4_out.tx_ok_stb <= '0';
elsif rising_edge(clk) then
arp_in.arp_query_stb <= '0';
ipv4_out.tx_data_ack <= '0';
ipv4_out.tx_err_stb <= '0';
ipv4_out.tx_ok_stb <= '0';
if (ethernet_ii_out.tx_data_ack = '1') or (state = CALC_CKS) then
if byte_cnt > 0 then
@ -202,12 +192,6 @@ begin
if ipv4_in.tx_en then
arp_in.arp_query_stb <= '1';
state <= ARP;
if or((to_std_logic_vector(ipv4_config.gateway) xor to_std_logic_vector(arp_in.arp_ip)) and to_std_logic_vector(ipv4_config.subnet_mask)) then
arp_in.arp_ip <= ipv4_config.gateway;
else
arp_in.arp_ip <= ipv4_in.tx_ip_address;
end if;
end if;
when ARP =>
@ -249,7 +233,6 @@ begin
ipv4_out.tx_data_ack <= '1';
if not ipv4_in.tx_en then
state <= IDLE;
ipv4_out.tx_ok_stb <= '1';
end if;
end if;
end case;
@ -258,6 +241,9 @@ begin
ethernet_ii_in.tx_data <= sr(sr'low);
ethernet_ii_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0';
arp_in.arp_ip <= ipv4_in.tx_ip_address;
end block tx;
end architecture rtl;

View File

@ -57,7 +57,7 @@ begin
if ipv4_out.rx_header_rcv then
rx_sel <= SEL_PROTOCOL_NONE;
for i in IPV4_PROTOCOLS'range loop
if (ipv4_out.rx_header.protocol = IPV4_PROTOCOLS(i)) then
if (ipv4_out.rx_protocol = IPV4_PROTOCOLS(i)) then
rx_sel <= i;
end if;
end loop;
@ -67,7 +67,8 @@ begin
mux : for i in ipv4_protocol_out'range generate
ipv4_protocol_out(i).rx_data <= ipv4_out.rx_data;
ipv4_protocol_out(i).rx_header <= ipv4_out.rx_header;
ipv4_protocol_out(i).rx_ip_address <= ipv4_out.rx_ip_address;
ipv4_protocol_out(i).rx_length <= ipv4_out.rx_length;
ipv4_protocol_out(i).rx_data_valid <= ipv4_out.rx_data_valid when rx_sel = i else '0';
ipv4_protocol_out(i).rx_error_stb <= ipv4_out.rx_error_stb when rx_sel = i else '0';
ipv4_protocol_out(i).rx_ok_stb <= ipv4_out.rx_ok_stb when rx_sel = i else '0';
@ -82,7 +83,7 @@ begin
end block rx;
tx : block
type state_t is (IDLE, TXD, WAITDONE);
type state_t is (IDLE, TXD);
signal state : state_t;
begin
@ -104,13 +105,7 @@ begin
end loop;
when TXD =>
state <= WAITDONE when (not ipv4_in.tx_en);
when WAITDONE =>
if ipv4_out.tx_err_stb or ipv4_out.tx_ok_stb or ipv4_out.tx_data_ack then
state <= IDLE;
tx_sel <= ipv4_protocol_in'left; -- To avoid arbitration errors, always select the highest priority one by default
end if;
state <= IDLE when (not ipv4_in.tx_en);
end case;
end if;
end process arb;

View File

@ -253,7 +253,6 @@ begin
begin
if rst then
phy_out.rx_data_valid <= '0';
phy_out.rx_data <= (others => '0'); -- Needed for yosys to compile
elsif rising_edge(clk) then
phy_out.rx_data_valid <= '0';
@ -297,10 +296,7 @@ begin
-- -------------------------------------------------------------------------
transmitter : block
constant TX_STB_CNT_IDEAL : real := real(F_CLK) / real((F_ETH * 2));
constant TX_STB_SKIP_ERROR : real := abs (round(TX_STB_CNT_IDEAL) - TX_STB_CNT_IDEAL);
constant TX_STB_CNT_MAX : integer := integer(round(TX_STB_CNT_IDEAL + 0.25)) - 1; -- Round up starting for error > 0.25
constant TX_STB_SKIP_SECOND : boolean := TX_STB_SKIP_ERROR >= 0.25; -- Skip one clock cycle every other symbol to hit clock rate in between when the divider is close to x.5
constant TX_STB_CNT_MAX : integer := integer(round(real(F_CLK) / real((F_ETH * 2)))) - 1;
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
type tx_state_t is (IDLE, NLP, TX, IPG);
@ -371,9 +367,6 @@ begin
if tx_stb_cnt = 0 then
tx_stb_cnt <= TX_STB_CNT_MAX;
if TX_STB_SKIP_SECOND and bit_stage = '1' then
tx_stb_cnt <= TX_STB_CNT_MAX - 1;
end if;
else
tx_stb_cnt <= tx_stb_cnt - 1;
end if;

View File

@ -44,14 +44,14 @@ architecture rtl of trashernet_phy_cdc is
signal phy_phy_in : phy_in_t;
-- Helper signals
signal rx_data_i : byte;
signal rx_data_valid_i : std_logic;
begin
assert F_CLK_PHY > 2 * F_CLK report "F_CLK_PHY must be at least 2x F_CLK" severity failure;
-- -------------------------------------------------------------------------
-- Drives: PHY clock domain
-- -------------------------------------------------------------------------
-- Reset synchronizer for PHY
rstsync : process(phy_clk, rst) is
begin
if rst then
@ -61,7 +61,6 @@ begin
end if;
end process rstsync;
-- Operate Trashernet in PHY clock domain
trashernet_phy_inst : entity work.trashernet_phy
generic map(
F_CLK => F_CLK_PHY
@ -76,22 +75,6 @@ begin
tx_n => tx_n
);
-- Latch data in PHY clock domain when valid is strobed
-- If the other clock domain is slower than the time it takes for the strobe to synchronize,
-- `phy_phy_out.rx_data` will already have shifted in the next bit and no longer be valid.
-- Therefore, we need to latch it here.
rxdff : process(phy_clk, rst) is
begin
if rst then
rx_data_i <= (others => '0');
elsif rising_edge(phy_clk) then
if phy_phy_out.rx_data_valid then
rx_data_i <= phy_phy_out.rx_data;
end if;
end if;
end process rxdff;
synchronizer_txen_inst : entity work.synchronizer
generic map(
SIZE => 5
@ -115,10 +98,18 @@ begin
a_in => phy_phy_out.rx_data_valid,
b_clk => clk,
b_rst => rst,
b_out => phy_out.rx_data_valid
b_out => rx_data_valid_i
);
phy_out.rx_data <= rx_data_i; -- No need to synchronize in new clock domain as latched data has been stable for a while thanks to the delay in the _valid synchronizer
rxdvff : process(clk, rst) is
begin
if rst then
phy_out.rx_data_valid <= '0';
elsif rising_edge(clk) then
phy_out.rx_data_valid <= rx_data_valid_i;
phy_out.rx_data <= phy_phy_out.rx_data; -- Data should be stable after the time it takes the _valid signal to go through the synchronizer
end if;
end process rxdvff;
cdc_strobe_rxer_inst : entity work.cdc_strobe
port map(

View File

@ -13,10 +13,6 @@ use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package trashernet_pkg is
-- ----------------
-- Type definitions
-- ----------------
-- General types
subtype byte is std_logic_vector(7 downto 0);
type byte_vector is array (natural range <>) of byte;
@ -27,56 +23,17 @@ package trashernet_pkg is
function mac_to_string(constant mac_address : in mac_addr_t) return string;
subtype ethertype_t is byte_vector(0 to 1);
type mac_header_t is record
type mac_header_fields is record
mac_destination : mac_addr_t; -- Destination MAC address
mac_source : mac_addr_t; -- Source MAC address
mac_ethertype : ethertype_t; -- Ethertype or length
end record mac_header_t;
-- Ethernet specific types
type ethernet_i_header_t is record
mac_header : mac_header_t; -- MAC layer header
length : unsigned(15 downto 0); -- RX payload length in bytes
end record;
type ethernet_ii_header_t is record
mac_header : mac_header_t; -- MAC layer header
end record;
end record mac_header_fields;
-- IP specific types
subtype ip_addr_t is byte_vector(0 to 3);
function ip_to_string(constant ip_address : in ip_addr_t) return string;
function to_std_logic_vector(ip_address : ip_addr_t) return std_logic_vector;
subtype ipv4_protocol is byte;
type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol;
constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01";
constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06";
constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11";
subtype ipv4_length is unsigned(15 downto 0);
type ipv4_header_t is record
ip_address : ip_addr_t; -- Source IP address
protocol : ipv4_protocol; -- Transport Protocol
length : ipv4_length; -- Telegram length (excluding header)
eth_header : ethernet_ii_header_t; -- Eth II header
end record ipv4_header_t;
-- UDP specific types
subtype udp_port_t is unsigned(15 downto 0);
subtype udp_length_t is unsigned(15 downto 0);
type udp_header_t is record
source_port : udp_port_t; -- UDP source port
destination_port : udp_port_t; -- UDP destination port
length : udp_length_t; -- UDP length
ipv4_header : ipv4_header_t; -- IPv4 header
end record;
-- ------------------------
-- Configuration interfaces
-- ------------------------
-- System configuration
-- Configuration interface
type configuration_t is record
mac_address : mac_addr_t; -- MAC address of this node
end record configuration_t;
@ -86,19 +43,6 @@ package trashernet_pkg is
subnet_mask : ip_addr_t; -- Subnet mask of this node
end record ipv4_configuration_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"))); -- @suppress "Null range": We want a NULL vector here to remove the logic
constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06"));
-- ----------------------
-- Application interfaces
-- ----------------------
-- PHY interface
type phy_in_t is record
tx_data : byte; -- TX Data
@ -118,12 +62,12 @@ package trashernet_pkg is
-- MAC interface
type mac_in_t is record
tx_header : mac_header_t; -- TX MAC Header Data
tx_header : mac_header_fields; -- TX MAC Header Data
tx_mac_data : byte; -- Payload
tx_mac_data_en : std_logic; -- Start (and keep) transmitting a frame
end record mac_in_t;
type mac_out_t is record
rx_header : mac_header_t; -- RX MAC Header Data
rx_header : mac_header_fields; -- RX MAC Header Data
rx_mac_header_rcv : std_logic; -- `rx_mac` header have been received and are valid
rx_mac_data : byte; -- Ethernet data (after Ethertype)
@ -135,17 +79,24 @@ package trashernet_pkg is
rx_mac_crc_error : std_logic; -- End of packet, CRC invalid
end record mac_out_t;
-- Ethernet I interface
-- 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"))); -- @suppress "Null range": We want a NULL vector here to remove the logic
constant ETHERNET_II_PROTOCOL_IPV4 : 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_header : ethernet_i_header_t;
rx_header_rcv : std_logic; -- Start of reception, `header` is valid
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
@ -156,17 +107,13 @@ package trashernet_pkg is
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
-- Ethernet II interface
type ethernet_ii_out_t is record
rx_header : ethernet_ii_header_t;
rx_header_rcv : std_logic; -- Start of reception
rx_crc_ok : std_logic; -- End of packet, CRC OK
rx_crc_error : std_logic; -- End of packet, CRC invalid
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
@ -190,15 +137,23 @@ package trashernet_pkg is
end record arp_in_t;
-- IPv4 interface
type ipv4_out_t is record
rx_header : ipv4_header_t;
rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid)
subtype ipv4_protocol is byte;
type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol;
constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01";
constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06";
constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11";
subtype ipv4_length is unsigned(15 downto 0);
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
type ipv4_out_t is record
rx_ip_address : ip_addr_t; -- Source IP address
rx_protocol : ipv4_protocol; -- Transport Protocol
rx_length : ipv4_length; -- Telegram length (excluding header)
rx_data : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid)
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful
@ -213,14 +168,14 @@ package trashernet_pkg is
end record ipv4_in_t;
type ipv4_protocol_out_t is record
rx_header : ipv4_header_t;
rx_header_rcv : std_logic; -- Start of reception
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
rx_ip_address : ip_addr_t; -- Source IP address
rx_length : ipv4_length; -- Telegram length (excluding header)
rx_data : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
rx_header_rcv : std_logic; -- Start of reception
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful
@ -236,66 +191,7 @@ package trashernet_pkg is
type ipv4_protocol_out_vector is array (natural range <>) of ipv4_protocol_out_t;
type ipv4_protocol_in_vector is array (natural range <>) of ipv4_protocol_in_t;
-- UDP interface
type udp_out_t is record
rx_data : byte; -- RX Data
rx_data_valid : std_logic; -- RX data valid strobe
rx_header : udp_header_t; -- UDP header
rx_header_rcv : std_logic; -- Start of reception, `rx_header` valid
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful
tx_err_stb : std_logic; -- Transmission failed
end record udp_out_t;
type udp_in_t is record
tx_ip_address : ip_addr_t; -- Destination IP address
tx_source_port : udp_port_t; -- UDP source port
tx_destination_port : udp_port_t; -- UDP destination port
tx_length : udp_length_t; -- UDP length
tx_en : std_logic; -- Start and continue transmitting
tx_data : byte;
end record udp_in_t;
type udpprot_rx_out_t is record
rx_data : byte; -- RX Data
rx_data_valid : std_logic; -- RX data valid strobe
rx_header : udp_header_t; -- UDP header
rx_header_rcv : std_logic; -- Start of reception, `rx_header` valid
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
end record udpprot_rx_out_t;
type udpprot_rx_in_t is record
port_bind : udp_port_t;
end record udpprot_rx_in_t;
type udpprot_tx_out_t is record
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful
tx_err_stb : std_logic; -- Transmission failed
end record udpprot_tx_out_t;
type udpprot_tx_in_t is record
tx_ip_address : ip_addr_t; -- Destination IP address
tx_source_port : udp_port_t; -- UDP source port
tx_destination_port : udp_port_t; -- UDP destination port
tx_length : udp_length_t; -- UDP length
tx_en : std_logic; -- Start and continue transmitting
tx_data : byte;
end record udpprot_tx_in_t;
type udpprot_tx_out_vector is array (natural range <>) of udpprot_tx_out_t;
type udpprot_tx_in_vector is array (natural range <>) of udpprot_tx_in_t;
type udpprot_rx_out_vector is array (natural range <>) of udpprot_rx_out_t;
type udpprot_rx_in_vector is array (natural range <>) of udpprot_rx_in_t;
-- ------------------------
-- General helper functions
-- ------------------------
function to_std_logic(constant bool : boolean) return std_logic;
end package trashernet_pkg;
@ -322,11 +218,6 @@ package body trashernet_pkg is
;
end function ip_to_string;
function to_std_logic_vector(ip_address : ip_addr_t) return std_logic_vector is
begin
return ip_address(0) & ip_address(1) & ip_address(2) & ip_address(3);
end function to_std_logic_vector;
function to_std_logic(constant bool : boolean) return std_logic is
variable ret : std_logic;
begin

View File

@ -1,304 +0,0 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- trashernet_rmii.vhd : Ethernet OSI Layer 1, Physical
-- Implements interface to an RMII PHY.
-- -------------------------------------------------------------------------- --
-- 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;
use work.trashernet_pkg.all;
entity trashernet_rmii is
generic(
SYSCLK_IS_REFCLK : boolean := false -- Do not generate synchronizers between the RMII and system clock domains
);
port(
-- Global
clk : in std_logic; -- Global clock (must not be slower than rmii_ref_clk)
rst : in std_logic; -- Asynchronous reset
-- PHY application interface
phy_out : out phy_out_t; -- PHY application IF (out)
phy_in : in phy_in_t; -- PHY application IF (in)
-- RMII physical signals
rmii_ref_clk : in std_logic; -- Synchronous clock reference for receive, transmit and control interface
rmii_crs_dv : in std_logic; -- Carrier Sense/Receive Data Valid
rmii_rxd : in std_logic_vector(1 downto 0); -- Receive Data
rmii_tx_en : out std_logic; -- Transmit Enable
rmii_txd : out std_logic_vector(1 downto 0) -- Transmit Data
);
end entity trashernet_rmii;
architecture rtl of trashernet_rmii is
signal rmii_rst : std_logic;
signal rmii_crs_dv_sync : std_logic;
begin
synchronizer_inst : entity work.synchronizer
generic map(
SIZE => 2
)
port map(
clk => clk,
rst => rst,
data_in => rmii_crs_dv,
data_out => rmii_crs_dv_sync
);
rmii_reset_gen : if SYSCLK_IS_REFCLK generate
rmii_rst <= rst;
else generate
rmii_reset_gen_p : process(rmii_ref_clk, rst) is
begin
if rst then
rmii_rst <= '1';
elsif rising_edge(rmii_ref_clk) then
rmii_rst <= '0';
end if;
end process rmii_reset_gen_p;
end generate rmii_reset_gen;
receive : block
type state_t is (IDLE, DATA);
signal state : state_t;
signal sr : byte;
signal dibit_cnt : integer range 0 to 3;
signal temp_rx_data : byte;
signal temp_rx_active : std_logic;
signal temp_rx_data_valid : std_logic;
begin
rxp : process(rmii_rst, rmii_ref_clk) is
begin
if rmii_rst then
state <= IDLE;
elsif rising_edge(rmii_ref_clk) then
-- Shift in data bytes
sr <= rmii_rxd & sr(sr'high downto 2);
if dibit_cnt = 3 then
dibit_cnt <= 0;
else
dibit_cnt <= dibit_cnt + 1;
end if;
-- Sync header
case state is
when IDLE =>
if sr = x"D5" then -- Sync header
state <= DATA;
dibit_cnt <= 1;
end if;
when DATA =>
null;
end case;
-- Stop receiving whenever CRSDV disappears
if rmii_crs_dv_sync = '0' then
state <= IDLE;
end if;
end if;
end process rxp;
temp_rx_data <= sr;
temp_rx_active <= '1' when (state = DATA) else '0'; -- TODO: We might want to delay this by one cycle
temp_rx_data_valid <= '1' when (state = DATA) and (dibit_cnt = 0) else '0';
phy_out.rx_error <= '0'; -- We don't implement RXER
phy_out.carrier_detect <= '1'; -- TODO: Not yet implemented.
cdc_or_register : if SYSCLK_IS_REFCLK generate
register_out : process(rmii_rst, rmii_ref_clk) is
begin
if rmii_rst then
phy_out.rx_data <= (others => '0');
phy_out.rx_active <= '0';
phy_out.rx_data_valid <= '0';
elsif rising_edge(rmii_ref_clk) then
phy_out.rx_data <= temp_rx_data;
phy_out.rx_active <= temp_rx_active;
phy_out.rx_data_valid <= temp_rx_data_valid;
end if;
end process register_out;
else generate
-- CDC
synchronizer_inst : entity work.synchronizer
generic map(
SIZE => 4
)
port map(
clk => clk,
rst => rst,
data_in => temp_rx_active,
data_out => phy_out.rx_active
);
cdc_strobe_inst : entity work.cdc_strobe
generic map(
SYNCHRONIZERS => 2
)
port map(
a_clk => rmii_ref_clk,
a_rst => rmii_rst,
a_in => temp_rx_data_valid,
b_clk => clk,
b_rst => rst,
b_out => phy_out.rx_data_valid
);
-- Not a synchronizer, just a byte latch in the rmii_ref_clk domain
rx_data_reg : process(rmii_ref_clk) is
begin
if rising_edge(rmii_ref_clk) then
if temp_rx_data_valid then
phy_out.rx_data <= temp_rx_data; -- TODO: We are assuming that the system clock domains runs at least x times faster than the PHY clock domain
end if;
end if;
end process rx_data_reg;
end generate cdc_or_register;
end block receive;
transmitter : block
constant SYNC_HEADER_SIZE_BYTES : natural := 8 - 1; -- Sync header 0x55 bytes ()
constant IPG_SIZE_BYTES : natural := 96 / 8;
signal temp_tx_data : byte;
signal temp_tx_data_en : std_logic;
signal temp_tx_data_ack : std_logic;
signal temp_tx_active : std_logic;
type state_t is (IDLE, DATA, IPG);
signal state : state_t;
signal sr : byte;
signal dibit_cnt : integer range 0 to 3;
signal byte_cnt : integer range 0 to maximum(SYNC_HEADER_SIZE_BYTES, IPG_SIZE_BYTES) - 1;
signal byte_done : std_logic;
signal block_done : std_logic;
begin
cdc_or_register : if SYSCLK_IS_REFCLK generate
temp_tx_data_en <= phy_in.tx_data_en;
temp_tx_data <= phy_in.tx_data;
-- -----------------------------------------------------------------
phy_out_reg : process(rst, clk) is
begin
if rst then
phy_out.tx_data_ack <= '0';
phy_out.tx_active <= '0';
elsif rising_edge(clk) then
phy_out.tx_data_ack <= temp_tx_data_ack;
phy_out.tx_active <= temp_tx_active;
end if;
end process phy_out_reg;
else generate
synchronizer_txdv_inst : entity work.synchronizer
generic map(
SIZE => 2
)
port map(
clk => rmii_ref_clk,
rst => rmii_rst,
data_in => phy_in.tx_data_en,
data_out => temp_tx_data_en
);
temp_tx_data <= phy_in.tx_data; -- TODO: Again, we are making assumptions about the clock relationship here
-- -----------------------------------------------------------------
cdc_strobe_inst : entity work.cdc_strobe
generic map(
SYNCHRONIZERS => 2
)
port map(
a_clk => rmii_ref_clk,
a_rst => rmii_rst,
a_in => temp_tx_data_ack,
b_clk => clk,
b_rst => rst,
b_out => phy_out.tx_data_ack
);
synchronizer_inst : entity work.synchronizer
generic map(
SIZE => 2
)
port map(
clk => clk,
rst => rst,
data_in => temp_tx_active,
data_out => phy_out.tx_active
);
end generate cdc_or_register;
txp : process(rmii_ref_clk, rmii_rst) is
begin
if rmii_rst then
state <= IDLE;
elsif rising_edge(rmii_ref_clk) then
sr <= "00" & sr(sr'high downto 2);
if dibit_cnt = 3 then
dibit_cnt <= 0;
else
dibit_cnt <= dibit_cnt + 1;
end if;
if byte_done then
if byte_cnt > 0 then
byte_cnt <= byte_cnt - 1;
end if;
end if;
case state is
when IDLE =>
sr <= temp_tx_data;
dibit_cnt <= 0;
if temp_tx_data_en then
state <= DATA;
end if;
when DATA =>
if byte_done then
if temp_tx_data_en then
sr <= temp_tx_data;
else
state <= IPG;
byte_cnt <= IPG_SIZE_BYTES - 1;
end if;
end if;
when IPG =>
if block_done then
state <= IDLE;
end if;
end case;
end if;
end process txp;
byte_done <= '1' when dibit_cnt = 3 else '0';
block_done <= '1' when (byte_cnt = 0) and (byte_done = '1') else '0';
temp_tx_data_ack <= '1' when (state = DATA) and (byte_done = '1') and (temp_tx_data_en = '1') else '0';
temp_tx_active <= '1' when state = DATA else '0';
rmii_txd <= sr(1 downto 0);
rmii_tx_en <= '1' when state = DATA else '0';
end block transmitter;
end architecture rtl;

View File

@ -1,153 +0,0 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- trashernet_udp.vhd : Ethernet OSI Layer 4, Transport (UDP)
-- Implements UDP frame encoding and decoding.
-- -------------------------------------------------------------------------- --
-- 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_udp is
port(
-- Global
clk : in std_logic; -- Global clock
rst : in std_logic; -- Asynchronous reset
-- IPv4 application interface
ipv4_protocol_in : out ipv4_protocol_in_t; -- IPv4 IF (out from IP Protocol)
ipv4_protocol_out : in ipv4_protocol_out_t; -- IPv4 IF (into IP Protocol)
-- UDP application interface
udp_out : out udp_out_t; -- UDP application IF (out from UDP)
udp_in : in udp_in_t -- UDP application IF (into UDP)
);
end entity trashernet_udp;
architecture rtl of trashernet_udp is
begin
receiver : block
constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
alias sr_source_port is sr(0 to 1);
alias sr_destination_port is sr(2 to 3);
alias sr_length is sr(4 to 5);
alias sr_source_checksum is sr(6 to 7);
signal bytecount : integer range 0 to BYTECOUNT_HEADER - 1;
type state_t is (HEADER, PAYLOAD);
signal state : state_t;
begin
rxp : process(rst, clk) is
begin
if rst then
state <= HEADER;
udp_out.rx_header_rcv <= '0';
udp_out.rx_error_stb <= '0';
udp_out.rx_ok_stb <= '0';
elsif rising_edge(clk) then
udp_out.rx_header_rcv <= '0';
udp_out.rx_error_stb <= ipv4_protocol_out.rx_error_stb;
udp_out.rx_ok_stb <= ipv4_protocol_out.rx_ok_stb;
case state is
when HEADER =>
if ipv4_protocol_out.rx_data_valid then
sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data;
if bytecount = BYTECOUNT_HEADER - 1 then
state <= PAYLOAD;
udp_out.rx_header_rcv <= '1';
else
bytecount <= bytecount + 1;
end if;
end if;
when PAYLOAD =>
udp_out.rx_data <= ipv4_protocol_out.rx_data;
udp_out.rx_data_valid <= ipv4_protocol_out.rx_data_valid;
end case;
if ipv4_protocol_out.rx_header_rcv or ipv4_protocol_out.rx_error_stb or ipv4_protocol_out.rx_ok_stb then
state <= HEADER;
bytecount <= 0;
end if;
end if;
end process rxp;
udp_out.rx_header.source_port <= unsigned(std_logic_vector'(sr_source_port(0) & sr_source_port(1)));
udp_out.rx_header.destination_port <= unsigned(std_logic_vector'(sr_destination_port(2) & sr_destination_port(3)));
udp_out.rx_header.length <= unsigned(std_logic_vector'(sr_length(4) & sr_length(5))) - BYTECOUNT_HEADER;
udp_out.rx_header.ipv4_header <= ipv4_protocol_out.rx_header;
end block receiver;
transmitter : block
constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
signal byte_cnt : integer range 0 to sr'length - 1;
type state_t is (IDLE, HEADER, PAYLOAD);
signal state : state_t;
begin
txp : process(rst, clk) is
variable full_length : udp_length_t;
begin
if rst then
udp_out.tx_data_ack <= '0';
udp_out.tx_err_stb <= '0';
udp_out.tx_ok_stb <= '0';
elsif rising_edge(clk) then
udp_out.tx_data_ack <= '0';
udp_out.tx_err_stb <= ipv4_protocol_out.tx_err_stb;
udp_out.tx_ok_stb <= ipv4_protocol_out.tx_ok_stb;
if ipv4_protocol_out.tx_data_ack = '1' then
if byte_cnt > 0 then
byte_cnt <= byte_cnt - 1;
end if;
sr <= sr(sr'low + 1 to sr'high) & x"00";
end if;
case state is
when IDLE =>
if udp_in.tx_en then
full_length := udp_in.tx_length + BYTECOUNT_HEADER;
sr <= byte_vector'(byte(udp_in.tx_source_port(15 downto 8)), byte(udp_in.tx_source_port(7 downto 0)), --
byte(udp_in.tx_destination_port(15 downto 8)), byte(udp_in.tx_destination_port(7 downto 0)), --
byte(full_length(15 downto 8)), byte(full_length(7 downto 0)), --
x"00", x"00");
byte_cnt <= sr'length - 1;
ipv4_protocol_in.tx_length <= full_length;
ipv4_protocol_in.tx_ip_address <= udp_in.tx_ip_address;
state <= HEADER;
end if;
when HEADER =>
if byte_cnt = 0 then
state <= PAYLOAD;
end if;
when PAYLOAD =>
if ipv4_protocol_out.tx_data_ack then
sr(sr'low) <= udp_in.tx_data;
udp_out.tx_data_ack <= '1';
if not udp_in.tx_en then
state <= IDLE;
end if;
end if;
end case;
end if;
end process txp;
ipv4_protocol_in.tx_data <= sr(sr'low);
ipv4_protocol_in.tx_en <= '1' when (state /= IDLE) else '0';
end block transmitter;
end architecture rtl;

View File

@ -1,139 +0,0 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- trashernet_udpprot.vhd : Ethernet OSI Layer 4, Transport (UDP)
-- Provides a convenient port-based muxed interface for UDP connections.
-- -------------------------------------------------------------------------- --
-- 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_udpprot is
generic(
ROUND_ROBIN : boolean := true -- Prioritize, but prevent one channel from hogging all bandwidth (may add one cycle of latency between frames)
);
port(
-- Global
clk : in std_logic; -- Global clock
rst : in std_logic; -- Asynchronous reset
-- UDP application interface
udp_out : in udp_out_t; -- UDP application IF (out from UDP)
udp_in : out udp_in_t; -- UDP application IF (into UDP)
-- UDP protocols interface
udpprot_rx_out : out udpprot_rx_out_vector; -- UDP Application IF for receiving data (out from UDP)
udpprot_rx_in : in udpprot_rx_in_vector; -- UDP Application IF for receiving data (into UDP)
udpprot_tx_out : out udpprot_tx_out_vector; -- UDP Application IF for transmitting data (out from UDP)
udpprot_tx_in : in udpprot_tx_in_vector -- UDP Application IF for transmitting data (in from UDP)
);
end entity trashernet_udpprot;
architecture rtl of trashernet_udpprot is
constant RX_SEL_PROTOCOL_NONE : integer := udpprot_rx_out'low - 1;
signal rx_sel : integer range RX_SEL_PROTOCOL_NONE to udpprot_rx_out'high;
signal tx_sel : integer range udpprot_tx_out'low to udpprot_tx_out'high;
begin
assert udpprot_tx_out'length = udpprot_tx_in'length report "UDP in and out ports must have the same length" severity FAILURE;
assert udpprot_rx_out'length = udpprot_rx_in'length report "UDP in and out ports must have the same length" severity FAILURE;
rx : block
signal rx_header_rcv_delayed : std_logic;
begin
arb : process(clk, rst) is
begin
if rst then
rx_sel <= RX_SEL_PROTOCOL_NONE;
rx_header_rcv_delayed <= '0';
elsif rising_edge(clk) then
rx_header_rcv_delayed <= udp_out.rx_header_rcv;
if udp_out.rx_header_rcv then
rx_sel <= RX_SEL_PROTOCOL_NONE;
for i in udpprot_rx_out'range loop
if (udp_out.rx_header.destination_port = udpprot_rx_in(i).port_bind) then
rx_sel <= i;
exit;
end if;
end loop;
end if;
end if;
end process arb;
mux : for i in udpprot_rx_out'range generate
udpprot_rx_out(i).rx_data <= udp_out.rx_data;
udpprot_rx_out(i).rx_header <= udp_out.rx_header;
udpprot_rx_out(i).rx_data_valid <= udp_out.rx_data_valid when rx_sel = i else '0';
udpprot_rx_out(i).rx_error_stb <= udp_out.rx_error_stb when rx_sel = i else '0';
udpprot_rx_out(i).rx_ok_stb <= udp_out.rx_ok_stb when rx_sel = i else '0';
udpprot_rx_out(i).rx_header_rcv <= rx_header_rcv_delayed when rx_sel = i else '0';
end generate mux;
tx_mux : for i in udpprot_tx_out'range generate
udpprot_tx_out(i).tx_data_ack <= udp_out.tx_data_ack when tx_sel = i else '0';
udpprot_tx_out(i).tx_err_stb <= udp_out.tx_err_stb when tx_sel = i else '0';
udpprot_tx_out(i).tx_ok_stb <= udp_out.tx_ok_stb when tx_sel = i else '0';
end generate;
end block rx;
tx : block
type state_t is (IDLE, TXD, WAITDONE);
signal state : state_t;
begin
arb : process(clk, rst) is
begin
if rst then
state <= IDLE;
tx_sel <= udpprot_tx_in'left;
elsif rising_edge(clk) then
case state is
when IDLE =>
tx_sel <= udpprot_tx_out'left;
for i in udpprot_tx_in'range loop
if (udpprot_tx_in(i).tx_en = '1') and (i >= tx_sel) then
tx_sel <= i;
state <= TXD;
exit; -- Prioritize according to vector
end if;
end loop;
when TXD =>
state <= WAITDONE when (not udp_in.tx_en);
when WAITDONE =>
if udp_out.tx_err_stb or udp_out.tx_ok_stb then
state <= IDLE;
if (not ROUND_ROBIN) or (tx_sel = udpprot_tx_out'high) then
tx_sel <= udpprot_tx_out'left;
else
tx_sel <= tx_sel + 1;
end if;
end if;
end case;
end if;
end process arb;
demux : block
begin
udp_in.tx_data <= udpprot_tx_in(tx_sel).tx_data;
udp_in.tx_en <= udpprot_tx_in(tx_sel).tx_en;
udp_in.tx_ip_address <= udpprot_tx_in(tx_sel).tx_ip_address;
udp_in.tx_length <= udpprot_tx_in(tx_sel).tx_length;
udp_in.tx_source_port <= udpprot_tx_in(tx_sel).tx_source_port;
udp_in.tx_destination_port <= udpprot_tx_in(tx_sel).tx_destination_port;
end block demux;
end block tx;
end architecture rtl;