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"?> <?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"> <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="bench" Library="bench"/>
<Mappings Location="cocotb" Library="cocotb"/>
<Mappings Location="design" Library="design"/> <Mappings Location="design" Library="design"/>
<Mappings Location="Common Libraries/IEEE" Library="ieee"/> <Mappings Location="Common Libraries/IEEE" Library="ieee"/>
<Mappings Location="Common Libraries/IEEE Synopsys" 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" 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="Common Libraries/STD" Library="std"/>
<Mappings Location="trashernet" Library="trashernet"/> <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> </com.sigasi.hdt.shared.librarymapping.model:LibraryMappings>

View File

@ -5,6 +5,16 @@
<projects> <projects>
</projects> </projects>
<buildSpec> <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> <buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name> <name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments> <arguments>
@ -12,9 +22,9 @@
</buildCommand> </buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>com.sigasi.hdt.verilog.ui.verilogNature</nature>
<nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature> <nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature> <nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
<nature>com.sigasi.hdt.toolchains.vunit.nature</nature>
</natures> </natures>
<linkedResources> <linkedResources>
<link> <link>
@ -22,6 +32,11 @@
<type>2</type> <type>2</type>
<locationURI>virtual:/virtual</locationURI> <locationURI>virtual:/virtual</locationURI>
</link> </link>
<link>
<name>Common Libraries/DRAG_REUSABLE_LIBRARIES_HERE.txt</name>
<type>1</type>
<locationURI>sigasiresource:/vhdl/readme.txt</locationURI>
</link>
<link> <link>
<name>Common Libraries/IEEE</name> <name>Common Libraries/IEEE</name>
<type>2</type> <type>2</type>
@ -37,5 +52,15 @@
<type>2</type> <type>2</type>
<locationURI>sigasiresource:/vhdl/2008/STD</locationURI> <locationURI>sigasiresource:/vhdl/2008/STD</locationURI>
</link> </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> </linkedResources>
</projectDescription> </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 eclipse.preferences.version=1
encoding//Common\ Libraries/IEEE=UTF-8 encoding//Common\ Libraries/IEEE=utf-8
encoding//Common\ Libraries/IEEE\ Synopsys=UTF-8 encoding//Common\ Libraries/IEEE\ Synopsys=utf-8
encoding//Common\ Libraries/STD=UTF-8 encoding//Common\ Libraries/STD=utf-8
encoding//Common\ Libraries=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: 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 2, Data link: `trashernet_mac`, `trashernet_eth`, `trashernet_arp`
* Layer 3, Network: `trashernet_ipv4`, `trashernet_ipv4prot`, `trashernet_icmp` * 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: 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. **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 ## Hardware
![Circuit Diagram](doc/circuit.svg) ![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! * 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 * 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 ## License
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.

View File

@ -19,12 +19,12 @@ end entity bench_crc;
architecture bench of bench_crc is architecture bench of bench_crc is
constant POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7"; constant POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7";
signal clk : std_logic; signal clk : std_logic;
signal rst : std_logic; signal rst : std_logic;
signal data : std_logic_vector(7 downto 0); signal data : std_logic_vector(7 downto 0);
signal data_valid : std_logic; signal data_valid : std_logic;
signal crc_clear : std_logic; signal crc_clear : std_logic;
signal crc_out : std_logic_vector(POLYNOMIAL'range); signal crc_out : std_logic_vector(POLYNOMIAL'range);
begin begin
crc_inst : entity trashernet.crc crc_inst : entity trashernet.crc
generic map( generic map(

View File

@ -30,7 +30,7 @@ package body bench_pkg is
wait for 50 ns; wait for 50 ns;
end loop; end loop;
end loop; end loop;
wait for 16 us; -- IPG wait for 16 us; -- IPG
report "Frame TX complete"; report "Frame TX complete";
end procedure send_data; end procedure send_data;
end package body bench_pkg; end package body bench_pkg;

View File

@ -22,14 +22,15 @@ entity bench_trashernet_phy is
end entity bench_trashernet_phy; end entity bench_trashernet_phy;
architecture bench of bench_trashernet_phy is architecture bench of bench_trashernet_phy is
signal clk : std_logic; signal clk : std_logic;
signal phy_clk : std_logic; signal phy_clk : std_logic;
signal rst : std_logic; signal rst : std_logic;
signal phy_out : phy_out_t; signal phy_out : phy_out_t;
signal phy_in : phy_in_t; signal phy_in : phy_in_t;
signal rx_p : std_logic; signal rx_p : std_logic;
signal tx_p : std_logic; signal tx_p : std_logic;
signal tx_n : std_logic; signal tx_n : std_logic;
begin begin
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
@ -38,14 +39,14 @@ begin
F_CLK_PHY => 100000000 F_CLK_PHY => 100000000
) )
port map( port map(
clk => clk, clk => clk,
phy_clk => phy_clk, phy_clk => phy_clk,
rst => rst, rst => rst,
phy_out => phy_out, phy_out => phy_out,
phy_in => phy_in, phy_in => phy_in,
rx_p => rx_p, rx_p => rx_p,
tx_p => tx_p, tx_p => tx_p,
tx_n => tx_n tx_n => tx_n
); );
clock_driver : process clock_driver : process

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

@ -29,8 +29,8 @@ end entity ledcon;
architecture RTL of ledcon is architecture RTL of ledcon is
constant MS_CNT_MAX : integer := integer(round(real(F_CLK) * (1.0 * 10.0 ** (-3)))); constant MS_CNT_MAX : integer := integer(round(real(F_CLK) * (1.0 * 10.0 ** (-3))));
signal ms_cnt : integer range 0 to MS_CNT_MAX; signal ms_cnt : integer range 0 to MS_CNT_MAX;
signal ms_tick : std_logic; signal ms_tick : std_logic;
begin begin
mstimer : process(clk, rst) is mstimer : process(clk, rst) is
begin begin
@ -50,7 +50,7 @@ begin
ledblock : block ledblock : block
constant LED_CNT_MAX : integer := 50; -- on time, in ms constant LED_CNT_MAX : integer := 50; -- on time, in ms
constant LED_CNT_MIN : integer := -50; -- off time, in ms constant LED_CNT_MIN : integer := -50; -- off time, in ms
signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX; signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX;
begin begin
ledgen : if BLINK(i) generate ledgen : if BLINK(i) generate
ledproc : process(clk, rst) is ledproc : process(clk, rst) is

View File

@ -57,13 +57,13 @@ architecture mac of top_hwitl is
signal mac_out : mac_out_t; signal mac_out : mac_out_t;
signal mac_in : mac_in_t; signal mac_in : mac_in_t;
type status_t is (IDLE, TX); type status_t is (IDLE, TX);
signal state : status_t; signal state : status_t;
constant BYTE_CNT_MAX : integer := 100; constant BYTE_CNT_MAX : integer := 100;
signal byte_cnt : integer range 0 to BYTE_CNT_MAX; signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1; constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
signal tmo : integer range 0 to TMO_MAX; signal tmo : integer range 0 to TMO_MAX;
begin begin
trashernet_mac_inst : entity trashernet.trashernet_mac trashernet_mac_inst : entity trashernet.trashernet_mac
port map( port map(
@ -206,10 +206,10 @@ architecture eth of top_hwitl is
PROT_ARP => ETHERNET_II_PROTOCOL_ARP, PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4 PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
); );
signal ethernet_i_out : ethernet_i_out_t; signal ethernet_i_out : ethernet_i_out_t;
signal ethernet_i_in : ethernet_i_in_t; signal ethernet_i_in : ethernet_i_in_t;
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); 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 ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
signal arp_out : arp_out_t; signal arp_out : arp_out_t;
signal arp_in : arp_in_t; signal arp_in : arp_in_t;
@ -217,45 +217,15 @@ architecture eth of top_hwitl is
signal ipv4_out : ipv4_out_t; signal ipv4_out : ipv4_out_t;
signal ipv4_in : ipv4_in_t; signal ipv4_in : ipv4_in_t;
constant IPROT_ICMP : integer := 0; constant IPROT_ICMP : integer := 0;
constant IPROT_UDP : integer := 1; constant IPROT_UDP : integer := 1;
constant IPV4_PROTOCOLS : ipv4_protocol_vector := ( constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
IPROT_ICMP => IPV4_PROTOCOL_ICMP, IPROT_ICMP => IPV4_PROTOCOL_ICMP,
IPROT_UDP => IPV4_PROTOCOL_UDP IPROT_UDP => IPV4_PROTOCOL_UDP
); );
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
signal ipv4_protocol_in : ipv4_protocol_in_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 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 trashernet_icmp_inst : entity trashernet.trashernet_icmp
port map( port map(
clk => clk, clk => clk,
@ -377,7 +347,7 @@ begin
elsif rising_edge(clk) then elsif rising_edge(clk) then
if ethernet_i_out.rx_header_rcv 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; end if;
if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then
report "RX ARP"; report "RX ARP";
@ -390,7 +360,7 @@ begin
report "RX IP error"; report "RX IP error";
end if; end if;
if ipv4_out.rx_header_rcv then 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 if; end if;
end process receiver; end process receiver;
@ -405,23 +375,20 @@ begin
end process udp; end process udp;
tx_udp_p : block tx_udp_p : block
type state_t is (IDLE, TX, DONE); type state_t is (IDLE, TX, DONE);
signal state : state_t; signal state : state_t;
constant PACKET : byte_vector := ( 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 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 sr : byte_vector(PACKET'range);
signal cnt : integer range 0 to sr'length - 1; signal cnt : integer range 0 to sr'length - 1;
begin 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 tx_udp : process(clk, rst) is
begin begin
if rst then if rst then
@ -434,12 +401,11 @@ begin
state <= TX; state <= TX;
sr <= PACKET; sr <= PACKET;
cnt <= sr'length - 1; cnt <= sr'length - 1;
report "UDP(0): Start TX"; report "UDP: Start TX";
end if; end if;
when TX => when TX =>
if udpprot_tx_out(0).tx_data_ack then if ipv4_protocol_out(IPROT_UDP).tx_data_ack then
report "UDP(0): byte ack";
sr <= sr(sr'low + 1 to sr'high) & x"00"; sr <= sr(sr'low + 1 to sr'high) & x"00";
if cnt = 0 then if cnt = 0 then
state <= DONE; state <= DONE;
@ -447,12 +413,12 @@ begin
cnt <= cnt - 1; cnt <= cnt - 1;
end if; end if;
end if; end if;
if udpprot_tx_out(0).tx_err_stb then if ipv4_protocol_out(IPROT_UDP).tx_err_stb then
report "UDP(0): TX ERROR"; report "UDP: TX ERROR";
state <= DONE; state <= DONE;
end if; end if;
if udpprot_tx_out(0).tx_ok_stb then if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then
report "UDP(0): TX OK stb in TX state -- that shouldn't happen, right?"; report "UDP: TX OK stb in TX state -- that shouldn't happen, right?";
end if; end if;
when DONE => when DONE =>
@ -461,29 +427,12 @@ begin
end case; end case;
end if; end if;
end process tx_udp; 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; 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 icmp : process(clk) is
begin begin
if rising_edge(clk) then if rising_edge(clk) then

View File

@ -68,16 +68,16 @@ architecture rtl of top_mac_test is
signal mac_out : mac_out_t; signal mac_out : mac_out_t;
signal mac_in : mac_in_t; signal mac_in : mac_in_t;
type status_t is (IDLE, TX); type status_t is (IDLE, TX);
signal state : status_t; signal state : status_t;
constant BYTE_CNT_MAX : integer := 100; constant BYTE_CNT_MAX : integer := 100;
signal byte_cnt : integer range 0 to BYTE_CNT_MAX; signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
signal button_n_sync : std_logic_vector(button_n'range); signal button_n_sync : std_logic_vector(button_n'range);
signal button : 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; constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
signal tmo : integer range 0 to TMO_MAX; signal tmo : integer range 0 to TMO_MAX;
begin begin
trashernet_mac_inst : entity trashernet.trashernet_mac trashernet_mac_inst : entity trashernet.trashernet_mac
port map( port map(
@ -190,7 +190,7 @@ begin
if mac_out.tx_mac_data_ack then if mac_out.tx_mac_data_ack then
if byte_cnt = 1 then if byte_cnt = 1 then
mac_in.tx_mac_data_en <= '0'; mac_in.tx_mac_data_en <= '0';
state <= IDLE; state <= IDLE;
else else
byte_cnt <= byte_cnt - 1; byte_cnt <= byte_cnt - 1;
end if; end if;

View File

@ -79,10 +79,10 @@ architecture rtl of top_pingable is
PROT_ARP => ETHERNET_II_PROTOCOL_ARP, PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4 PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
); );
signal ethernet_i_out : ethernet_i_out_t; signal ethernet_i_out : ethernet_i_out_t;
signal ethernet_i_in : ethernet_i_in_t; signal ethernet_i_in : ethernet_i_in_t;
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); 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 ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
signal arp_out : arp_out_t; signal arp_out : arp_out_t;
signal arp_in : arp_in_t; signal arp_in : arp_in_t;
@ -90,12 +90,12 @@ architecture rtl of top_pingable is
signal ipv4_out : ipv4_out_t; signal ipv4_out : ipv4_out_t;
signal ipv4_in : ipv4_in_t; signal ipv4_in : ipv4_in_t;
constant IPROT_ICMP : integer := 0; constant IPROT_ICMP : integer := 0;
constant IPV4_PROTOCOLS : ipv4_protocol_vector := ( constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
IPROT_ICMP => IPV4_PROTOCOL_ICMP IPROT_ICMP => IPV4_PROTOCOL_ICMP
); );
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
signal button_n_sync : std_logic_vector(button_n'range); signal button_n_sync : std_logic_vector(button_n'range);
signal button : std_logic_vector(button_n'range); signal button : std_logic_vector(button_n'range);

View File

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

View File

@ -34,11 +34,11 @@ entity fifo is
end entity fifo; end entity fifo;
architecture rtl of fifo is architecture rtl of fifo is
type memory_t is array (natural range <>) of std_logic_vector(data_in'range); type memory_t is array (natural range <>) of std_logic_vector(data_in'range);
signal memory : memory_t(0 to DEPTH - 1); signal memory : memory_t(0 to DEPTH - 1);
subtype memory_pointer_t is integer range 0 to DEPTH - 1; subtype memory_pointer_t is integer range 0 to DEPTH - 1;
subtype usage_counter_t is integer range 0 to DEPTH; subtype usage_counter_t is integer range 0 to DEPTH;
signal read_pointer : memory_pointer_t; signal read_pointer : memory_pointer_t;
signal write_pointer : memory_pointer_t; signal write_pointer : memory_pointer_t;

View File

@ -55,25 +55,26 @@ 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_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_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_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_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_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 signal arp_rx_spa : ip_addr_t; -- The IP address of the reply sender, valid with arp_rx_reply
begin begin
resolver : block resolver : block
type resolver_state_t is (IDLE, QUERY_MAC); type resolver_state_t is (IDLE, QUERY_MAC);
signal state : resolver_state_t; signal state : resolver_state_t;
signal query_mac_found : std_logic; signal query_mac_found : std_logic;
signal query_timeout : std_logic := '0'; -- TODO: Implement signal query_timeout : std_logic := '0'; -- TODO: Implement
signal replied_ip : ip_addr_t; signal replied_ip : ip_addr_t;
signal replied_mac : mac_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 arp_resolver_main : process(rst, clk) is
begin begin
if rst then if rst then
@ -96,7 +97,7 @@ begin
case state is case state is
when IDLE => when IDLE =>
if arp_in.arp_query_stb then if arp_query_stb then
if query_mac_found then if query_mac_found then
arp_out.arp_ok_stb <= '1'; arp_out.arp_ok_stb <= '1';
else else
@ -118,9 +119,21 @@ begin
end if; end if;
end process arp_resolver_main; end process arp_resolver_main;
query_mac_found <= '1' when (arp_tx_request_tpa = replied_ip) else '0'; 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_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 timeout_timer_inst : entity work.timer
generic map( generic map(
@ -146,19 +159,19 @@ begin
signal sr : byte_vector(0 to BYTECOUNT_ADDRESSES - 1); signal sr : byte_vector(0 to BYTECOUNT_ADDRESSES - 1);
constant SR_HEAD_OFFSET : integer := BYTECOUNT_ADDRESSES - BYTECOUNT_HEAD; constant SR_HEAD_OFFSET : integer := BYTECOUNT_ADDRESSES - BYTECOUNT_HEAD;
alias sr_head_htype is sr(SR_HEAD_OFFSET + 0 to SR_HEAD_OFFSET + 1); alias sr_head_htype is sr(SR_HEAD_OFFSET + 0 to SR_HEAD_OFFSET + 1);
alias sr_head_ptype is sr(SR_HEAD_OFFSET + 2 to SR_HEAD_OFFSET + 3); alias sr_head_ptype is sr(SR_HEAD_OFFSET + 2 to SR_HEAD_OFFSET + 3);
alias sr_head_hlen is sr(SR_HEAD_OFFSET + 4); alias sr_head_hlen is sr(SR_HEAD_OFFSET + 4);
alias sr_head_plen is sr(SR_HEAD_OFFSET + 5); alias sr_head_plen is sr(SR_HEAD_OFFSET + 5);
alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7); alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7);
constant SR_ADDRESSES_OFFSET : integer := -8; constant SR_ADDRESSES_OFFSET : integer := -8;
alias sr_addresses_sha is sr(8 + SR_ADDRESSES_OFFSET to 13 + SR_ADDRESSES_OFFSET); alias sr_addresses_sha is sr(8 + SR_ADDRESSES_OFFSET to 13 + SR_ADDRESSES_OFFSET);
alias sr_addresses_spa is sr(14 + SR_ADDRESSES_OFFSET to 17 + SR_ADDRESSES_OFFSET); alias sr_addresses_spa is sr(14 + SR_ADDRESSES_OFFSET to 17 + SR_ADDRESSES_OFFSET);
alias sr_addresses_tha is sr(18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET); -- @suppress "Unused declaration": We don't need to check this as this is already done by the MAC alias sr_addresses_tha is sr(18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET); -- @suppress "Unused declaration": We don't need to check this as this is already done by the MAC
alias sr_addresses_tpa is sr(24 + SR_ADDRESSES_OFFSET to 27 + SR_ADDRESSES_OFFSET); alias sr_addresses_tpa is sr(24 + SR_ADDRESSES_OFFSET to 27 + SR_ADDRESSES_OFFSET);
type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE); type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE);
signal state : state_t; signal state : state_t;
signal bytecount : integer range 0 to BYTECOUNT_ADDRESSES; signal bytecount : integer range 0 to BYTECOUNT_ADDRESSES;
signal shifted : std_logic; signal shifted : std_logic;
@ -240,7 +253,7 @@ begin
state <= HEAD; state <= HEAD;
end if; end if;
when IGNORE => -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err) when IGNORE => -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err)
null; null;
end case; end case;
end if; end if;
@ -253,14 +266,14 @@ begin
end block rx; end block rx;
tx : block tx : block
signal sr : byte_vector(0 to 27); signal sr : byte_vector(0 to 27);
signal arp_reply_preload : byte_vector(sr'range); signal arp_reply_preload : byte_vector(sr'range);
signal arp_request_preload : byte_vector(sr'range); signal arp_request_preload : byte_vector(sr'range);
constant BYTECOUNT_MAX : integer := sr'length + 1; constant BYTECOUNT_MAX : integer := sr'length + 1;
signal bytecount : integer range 0 to BYTECOUNT_MAX; signal bytecount : integer range 0 to BYTECOUNT_MAX;
type state_t is (IDLE, TRANSMIT); type state_t is (IDLE, TRANSMIT);
signal state : state_t; signal state : state_t;
begin begin
arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header

View File

@ -46,11 +46,11 @@ architecture rtl of trashernet_eth is
signal tx_data_ack_i : std_logic; signal tx_data_ack_i : std_logic;
begin begin
rx : block rx : block
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1; constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2; constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2;
signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high; signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high;
signal mac_destination_matches : std_logic; signal mac_destination_matches : std_logic;
signal rx_mac_header_rcv_delayed : std_logic; signal rx_mac_header_rcv_delayed : std_logic;
begin begin
mac_destination_matches <= '1' when -- mac_destination_matches <= '1' when --
@ -87,22 +87,22 @@ begin
-- Shared fields in the Ethernet II application interface -- Shared fields in the Ethernet II application interface
shared_fields : for i in ethernet_ii_out'range generate 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 <= 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_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_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_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'; ethernet_ii_out(i).rx_header_rcv <= rx_mac_header_rcv_delayed when sel = i else '0';
end generate shared_fields; end generate shared_fields;
-- Shared fields in the Ethernet application interface -- Shared fields in the Ethernet application interface
ethernet_i_out.rx_data <= mac_out.rx_mac_data; 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_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_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_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_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_mac_address <= mac_out.rx_header.mac_source;
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_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 (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 tx_fields : for i in ethernet_ii_out'range generate
@ -112,10 +112,10 @@ begin
end block rx; end block rx;
tx : block tx : block
type state_t is (IDLE, TXD); type state_t is (IDLE, TXD);
signal state : state_t; signal state : state_t;
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1; constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
signal sel : integer range SEL_ETH_I to ethernet_ii_in'high; signal sel : integer range SEL_ETH_I to ethernet_ii_in'high;
begin begin
arb : process(clk, rst) is arb : process(clk, rst) is
begin begin
@ -141,10 +141,7 @@ begin
end if; end if;
when TXD => when TXD =>
if not mac_in.tx_mac_data_en then state <= IDLE when (not mac_in.tx_mac_data_en or not mac_out.tx_active);
state <= IDLE;
sel <= SEL_ETH_I; -- To avoid arbitration errors, always select the highest priority one by default
end if;
end case; end case;
end if; end if;
end process arb; end process arb;

View File

@ -56,13 +56,13 @@ begin
); );
rx : block rx : block
signal sr : byte_vector(0 to 3); signal sr : byte_vector(0 to 3);
alias sr_type is sr(0); alias sr_type is sr(0);
alias sr_code is sr(1); alias sr_code is sr(1);
alias sr_checksum is sr(2 to 3); alias sr_checksum is sr(2 to 3);
type state_t is (HEADER, DATA, IGNORE); type state_t is (HEADER, DATA, IGNORE);
signal state : state_t; signal state : state_t;
signal byte_count : integer range 0 to sr'length; signal byte_count : integer range 0 to sr'length;
@ -117,8 +117,8 @@ begin
signal checksum : unsigned(16 downto 0); signal checksum : unsigned(16 downto 0);
signal checksum_ones : unsigned(15 downto 0); signal checksum_ones : unsigned(15 downto 0);
type state_t is (IDLE, HEADER, PAYLOAD); type state_t is (IDLE, HEADER, PAYLOAD);
signal state : state_t; signal state : state_t;
signal sr : byte_vector(0 to 3); signal sr : byte_vector(0 to 3);
signal byte_count : integer range 0 to sr'subtype'high; signal byte_count : integer range 0 to sr'subtype'high;
@ -140,8 +140,8 @@ begin
case state is case state is
when IDLE => when IDLE =>
if tx_response then 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_ip_address <= ipv4_protocol_out.rx_ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯
ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_header.length; ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_length;
sr <= byte_vector'( sr <= byte_vector'(
x"00", x"00",
x"00", x"00",

View File

@ -44,28 +44,27 @@ begin
constant BYTECOUNT_HEADER : integer := 20; -- Mandatory header fields constant BYTECOUNT_HEADER : integer := 20; -- Mandatory header fields
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
alias sr_version is sr(0)(7 downto 4); alias sr_version is sr(0)(7 downto 4);
alias sr_ihl is sr(0)(3 downto 0); alias sr_ihl is sr(0)(3 downto 0);
alias sr_dscp is sr(1)(7 downto 2); alias sr_dscp is sr(1)(7 downto 2);
alias sr_ecn is sr(1)(1 downto 0); alias sr_ecn is sr(1)(1 downto 0);
alias sr_total_length is sr(2 to 3); alias sr_total_length is sr(2 to 3);
alias sr_identification is sr(4 to 5); alias sr_identification is sr(4 to 5);
alias sr_flags is sr(6)(7 downto 5); alias sr_flags is sr(6)(7 downto 5);
signal sr_fragment_offset : std_logic_vector(12 downto 0); signal sr_fragment_offset : std_logic_vector(12 downto 0);
alias sr_ttl is sr(8); alias sr_ttl is sr(8);
alias sr_protocol is sr(9); alias sr_protocol is sr(9);
alias sr_header_cks is sr(10 to 11); alias sr_header_cks is sr(10 to 11);
alias sr_source_ip is sr(12 to 15); alias sr_source_ip is sr(12 to 15);
alias sr_destination_ip is sr(16 to 19); alias sr_destination_ip is sr(16 to 19);
signal header_ok : std_logic; signal header_ok : std_logic;
signal selected : std_logic;
signal bytecount : integer range 0 to 65535; signal bytecount : integer range 0 to 65535;
signal block_done : std_logic; signal block_done : std_logic;
type state_t is (HEADER, OPT, PAYLOAD, DONE); type state_t is (HEADER, OPT, PAYLOAD, DONE);
signal state : state_t; signal state : state_t;
begin begin
sr_fragment_offset <= sr(6)(4 downto 0) & sr(7); sr_fragment_offset <= sr(6)(4 downto 0) & sr(7);
@ -78,18 +77,24 @@ begin
rx_fsm : process(clk, rst) is rx_fsm : process(clk, rst) is
begin begin
if rst then if rst then
state <= DONE; state <= HEADER;
ipv4_out.rx_ok_stb <= '0'; ipv4_out.rx_ok_stb <= '0';
ipv4_out.rx_error_stb <= '0'; ipv4_out.rx_error_stb <= '0';
ipv4_out.rx_header_rcv <= '0'; ipv4_out.rx_header_rcv <= '0';
bytecount <= BYTECOUNT_HEADER; bytecount <= BYTECOUNT_HEADER;
selected <= '0';
elsif rising_edge(clk) then elsif rising_edge(clk) then
ipv4_out.rx_ok_stb <= '0'; ipv4_out.rx_ok_stb <= '0';
ipv4_out.rx_error_stb <= '0'; ipv4_out.rx_error_stb <= '0';
ipv4_out.rx_header_rcv <= '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 (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then
if state = HEADER then if state = HEADER then
sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data; 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 bytecount <= to_integer(unsigned(sr_ihl) - 5) * 4; -- five 32-bit words is the header itself
if header_ok then if header_ok then
state <= OPT; state <= OPT;
selected <= '1';
ipv4_out.rx_header_rcv <= '1'; ipv4_out.rx_header_rcv <= '1';
else
state <= DONE;
end if; end if;
end if; end if;
@ -124,30 +126,20 @@ begin
when DONE => -- @suppress "Dead state 'DONE'": Outgoing state transition is outside of case statement 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 null; -- We just wait here until the MAC gives us a CRC OK/error
end case; 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 if;
end process rx_fsm; end process rx_fsm;
ipv4_out.rx_data <= ethernet_ii_out.rx_data; 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_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_ip_address <= sr_source_ip;
ipv4_out.rx_header.ip_address <= sr_source_ip; ipv4_out.rx_protocol <= sr_protocol;
ipv4_out.rx_header.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;
ipv4_out.rx_header.length <= unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3))) - to_integer(unsigned(sr_ihl)) * 4;
end block rx; end block rx;
tx : block tx : block
type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD); type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD);
signal state : state_t; signal state : state_t;
signal full_length : ipv4_length; signal full_length : ipv4_length;
@ -182,13 +174,11 @@ begin
ipv4_out.tx_data_ack <= '0'; ipv4_out.tx_data_ack <= '0';
arp_in.arp_query_stb <= '0'; arp_in.arp_query_stb <= '0';
ipv4_out.tx_err_stb <= '0'; ipv4_out.tx_err_stb <= '0';
ipv4_out.tx_ok_stb <= '0';
elsif rising_edge(clk) then elsif rising_edge(clk) then
arp_in.arp_query_stb <= '0'; arp_in.arp_query_stb <= '0';
ipv4_out.tx_data_ack <= '0'; ipv4_out.tx_data_ack <= '0';
ipv4_out.tx_err_stb <= '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 (ethernet_ii_out.tx_data_ack = '1') or (state = CALC_CKS) then
if byte_cnt > 0 then if byte_cnt > 0 then
@ -202,12 +192,6 @@ begin
if ipv4_in.tx_en then if ipv4_in.tx_en then
arp_in.arp_query_stb <= '1'; arp_in.arp_query_stb <= '1';
state <= ARP; 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; end if;
when ARP => when ARP =>
@ -248,8 +232,7 @@ begin
sr(sr'low) <= ipv4_in.tx_data; sr(sr'low) <= ipv4_in.tx_data;
ipv4_out.tx_data_ack <= '1'; ipv4_out.tx_data_ack <= '1';
if not ipv4_in.tx_en then if not ipv4_in.tx_en then
state <= IDLE; state <= IDLE;
ipv4_out.tx_ok_stb <= '1';
end if; end if;
end if; end if;
end case; end case;
@ -258,6 +241,9 @@ begin
ethernet_ii_in.tx_data <= sr(sr'low); ethernet_ii_in.tx_data <= sr(sr'low);
ethernet_ii_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0'; 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 block tx;
end architecture rtl; end architecture rtl;

View File

@ -57,7 +57,7 @@ begin
if ipv4_out.rx_header_rcv then if ipv4_out.rx_header_rcv then
rx_sel <= SEL_PROTOCOL_NONE; rx_sel <= SEL_PROTOCOL_NONE;
for i in IPV4_PROTOCOLS'range loop 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; rx_sel <= i;
end if; end if;
end loop; end loop;
@ -67,7 +67,8 @@ begin
mux : for i in ipv4_protocol_out'range generate mux : for i in ipv4_protocol_out'range generate
ipv4_protocol_out(i).rx_data <= ipv4_out.rx_data; 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_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_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'; ipv4_protocol_out(i).rx_ok_stb <= ipv4_out.rx_ok_stb when rx_sel = i else '0';
@ -82,8 +83,8 @@ begin
end block rx; end block rx;
tx : block tx : block
type state_t is (IDLE, TXD, WAITDONE); type state_t is (IDLE, TXD);
signal state : state_t; signal state : state_t;
begin begin
arb : process(clk, rst) is arb : process(clk, rst) is
@ -104,13 +105,7 @@ begin
end loop; end loop;
when TXD => when TXD =>
state <= WAITDONE when (not ipv4_in.tx_en); state <= IDLE 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;
end case; end case;
end if; end if;
end process arb; end process arb;

View File

@ -41,10 +41,10 @@ begin
constant HEAD_LENGTH : integer := mac_out.rx_header.mac_destination'length + mac_out.rx_header.mac_source'length + mac_out.rx_header.mac_ethertype'length; constant HEAD_LENGTH : integer := mac_out.rx_header.mac_destination'length + mac_out.rx_header.mac_source'length + mac_out.rx_header.mac_ethertype'length;
constant CRC_LENGTH : integer := 4; constant CRC_LENGTH : integer := 4;
signal state : state_t; signal state : state_t;
signal sr_head : byte_vector(0 to HEAD_LENGTH - 1); signal sr_head : byte_vector(0 to HEAD_LENGTH - 1);
constant BYTE_COUNT_MAX : integer := maximum(HEAD_LENGTH, CRC_LENGTH) - 1; constant BYTE_COUNT_MAX : integer := maximum(HEAD_LENGTH, CRC_LENGTH) - 1;
signal byte_count : integer range 0 to BYTE_COUNT_MAX; signal byte_count : integer range 0 to BYTE_COUNT_MAX;
signal sr_payload : byte_vector(0 to 4); signal sr_payload : byte_vector(0 to 4);
@ -102,7 +102,7 @@ begin
byte_count <= byte_count + 1; byte_count <= byte_count + 1;
end if; end if;
end if; end if;
when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error) when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error)
if phy_out.rx_data_valid then if phy_out.rx_data_valid then
sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data; sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data;
if byte_count = CRC_LENGTH then if byte_count = CRC_LENGTH then
@ -136,14 +136,14 @@ begin
end block rx; end block rx;
tx : block tx : block
type tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC); type tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC);
signal tx_state : tx_state_t; signal tx_state : tx_state_t;
signal sr : byte_vector(0 to mac_in.tx_header.mac_destination'length + mac_in.tx_header.mac_source'length + mac_in.tx_header.mac_ethertype'length - 1); signal sr : byte_vector(0 to mac_in.tx_header.mac_destination'length + mac_in.tx_header.mac_source'length + mac_in.tx_header.mac_ethertype'length - 1);
constant SR_CNT_MAX : integer := sr'high; constant SR_CNT_MAX : integer := sr'high;
signal sr_cnt : integer range 0 to SR_CNT_MAX; signal sr_cnt : integer range 0 to SR_CNT_MAX;
constant BYTE_CNT_MAX : integer := ETH_FRAME_MIN_LENGTH - 4; -- Minus four because the CRC is *after* the PAD constant BYTE_CNT_MAX : integer := ETH_FRAME_MIN_LENGTH - 4; -- Minus four because the CRC is *after* the PAD
signal byte_cnt : integer range 0 to BYTE_CNT_MAX; signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
signal crc : std_logic_vector(ETH_POLYNOMIAL'range); signal crc : std_logic_vector(ETH_POLYNOMIAL'range);
signal crc_clear : std_logic; signal crc_clear : std_logic;

View File

@ -44,7 +44,7 @@ begin
common : block common : block
constant TICK_MS_CNT_MAX : integer := (F_CLK / 1000) - 1; constant TICK_MS_CNT_MAX : integer := (F_CLK / 1000) - 1;
signal tick_ms_count : integer range 0 to TICK_MS_CNT_MAX; signal tick_ms_count : integer range 0 to TICK_MS_CNT_MAX;
begin begin
mstick : process(clk, rst) is mstick : process(clk, rst) is
begin begin
@ -80,7 +80,7 @@ begin
-- NLP supervision -- NLP supervision
constant NLP_TIMEOUT_CNT_MAX : integer := (16 + 8) - 1; -- Every 16 ms (timebase 1 ms) constant NLP_TIMEOUT_CNT_MAX : integer := (16 + 8) - 1; -- Every 16 ms (timebase 1 ms)
signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX; signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX;
begin begin
-- Synchronize RX input -- Synchronize RX input
synchronizer_rxp_inst : entity work.synchronizer synchronizer_rxp_inst : entity work.synchronizer
@ -111,26 +111,26 @@ begin
demanchestizer : block demanchestizer : block
-- Transition detector -- Transition detector
constant BIT_LENGTH_LONG : integer := F_CLK / F_ETH - 1; constant BIT_LENGTH_LONG : integer := F_CLK / F_ETH - 1;
constant BIT_LENGTH_SHORT : integer := F_CLK / (F_ETH * 2) - 1; constant BIT_LENGTH_SHORT : integer := F_CLK / (F_ETH * 2) - 1;
constant BIT_LENGTH_TOLERANCE : integer := (F_CLK / F_ETH) / 5; constant BIT_LENGTH_TOLERANCE : integer := (F_CLK / F_ETH) / 5;
constant BIT_LENGTH_TIMEOUT : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1; constant BIT_LENGTH_TIMEOUT : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1;
signal sample_count : integer range 0 to BIT_LENGTH_TIMEOUT; signal sample_count : integer range 0 to BIT_LENGTH_TIMEOUT;
type transition_duration_t is (SHORT, LONG); type transition_duration_t is (SHORT, LONG);
signal transition_duration : transition_duration_t; signal transition_duration : transition_duration_t;
signal transition_stb : std_logic; signal transition_stb : std_logic;
signal transition_activity : std_logic; signal transition_activity : std_logic;
-- Transition analysis -- Transition analysis
signal last_transition : transition_duration_t; signal last_transition : transition_duration_t;
type bit_ev_t is (NONE, TOGGLE, KEEP, ERROR); type bit_ev_t is (NONE, TOGGLE, KEEP, ERROR);
signal bit_ev : bit_ev_t; signal bit_ev : bit_ev_t;
-- Bit recovery -- Bit recovery
type demanchestization_state_t is (SYNC, DATA, ERROR); type demanchestization_state_t is (SYNC, DATA, ERROR);
signal demanchestization_state : demanchestization_state_t; signal demanchestization_state : demanchestization_state_t;
constant DEMANCHESTIZATION_MIN_SYNC_CNT_MAX : integer := (4 * 8) - 1; -- 4 good sync bytes constant DEMANCHESTIZATION_MIN_SYNC_CNT_MAX : integer := (4 * 8) - 1; -- 4 good sync bytes
signal demanchestization_min_sync_cnt : integer range 0 to DEMANCHESTIZATION_MIN_SYNC_CNT_MAX; signal demanchestization_min_sync_cnt : integer range 0 to DEMANCHESTIZATION_MIN_SYNC_CNT_MAX;
begin begin
-- Detects spacing of transitions -- Detects spacing of transitions
transition_detector : process(clk, rst) is transition_detector : process(clk, rst) is
@ -253,7 +253,6 @@ begin
begin begin
if rst then if rst then
phy_out.rx_data_valid <= '0'; phy_out.rx_data_valid <= '0';
phy_out.rx_data <= (others => '0'); -- Needed for yosys to compile
elsif rising_edge(clk) then elsif rising_edge(clk) then
phy_out.rx_data_valid <= '0'; phy_out.rx_data_valid <= '0';
@ -297,25 +296,22 @@ begin
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
transmitter : block transmitter : block
constant TX_STB_CNT_IDEAL : real := real(F_CLK) / real((F_ETH * 2)); constant TX_STB_CNT_MAX : integer := integer(round(real(F_CLK) / real((F_ETH * 2)))) - 1;
constant TX_STB_SKIP_ERROR : real := abs (round(TX_STB_CNT_IDEAL) - TX_STB_CNT_IDEAL); signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
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
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
type tx_state_t is (IDLE, NLP, TX, IPG); type tx_state_t is (IDLE, NLP, TX, IPG);
signal tx_state : tx_state_t; signal tx_state : tx_state_t;
signal sr : std_logic_vector(phy_in.tx_data'range); signal sr : std_logic_vector(phy_in.tx_data'range);
signal bit_stage : std_logic; signal bit_stage : std_logic;
constant BIT_CNT_MAX_IPG : integer := 96; constant BIT_CNT_MAX_IPG : integer := 96;
constant BIT_CNT_MAX_DATA : integer := sr'length - 1; constant BIT_CNT_MAX_DATA : integer := sr'length - 1;
signal bit_cnt : integer range 0 to maximum(BIT_CNT_MAX_IPG, BIT_CNT_MAX_DATA); signal bit_cnt : integer range 0 to maximum(BIT_CNT_MAX_IPG, BIT_CNT_MAX_DATA);
constant NLP_CNT_MAX : integer := 15; -- specced 16 ms, but there's margin, so let's choose 15 to save a bit (timebase 1 ms) constant NLP_CNT_MAX : integer := 15; -- specced 16 ms, but there's margin, so let's choose 15 to save a bit (timebase 1 ms)
signal nlp_cnt : integer range 0 to NLP_CNT_MAX; signal nlp_cnt : integer range 0 to NLP_CNT_MAX;
type tx_mode_t is (OFF, NLP, ACTIVE); type tx_mode_t is (OFF, NLP, ACTIVE);
signal tx_mode : tx_mode_t; signal tx_mode : tx_mode_t;
begin begin
tx_main : process(clk, rst) is tx_main : process(clk, rst) is
procedure transmit_byte is procedure transmit_byte is
@ -371,9 +367,6 @@ begin
if tx_stb_cnt = 0 then if tx_stb_cnt = 0 then
tx_stb_cnt <= TX_STB_CNT_MAX; 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 else
tx_stb_cnt <= tx_stb_cnt - 1; tx_stb_cnt <= tx_stb_cnt - 1;
end if; end if;

View File

@ -44,14 +44,14 @@ architecture rtl of trashernet_phy_cdc is
signal phy_phy_in : phy_in_t; signal phy_phy_in : phy_in_t;
-- Helper signals -- Helper signals
signal rx_data_i : byte; signal rx_data_valid_i : std_logic;
begin 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 -- Drives: PHY clock domain
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
-- Reset synchronizer for PHY
rstsync : process(phy_clk, rst) is rstsync : process(phy_clk, rst) is
begin begin
if rst then if rst then
@ -61,7 +61,6 @@ begin
end if; end if;
end process rstsync; end process rstsync;
-- Operate Trashernet in PHY clock domain
trashernet_phy_inst : entity work.trashernet_phy trashernet_phy_inst : entity work.trashernet_phy
generic map( generic map(
F_CLK => F_CLK_PHY F_CLK => F_CLK_PHY
@ -76,22 +75,6 @@ begin
tx_n => tx_n 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 synchronizer_txen_inst : entity work.synchronizer
generic map( generic map(
SIZE => 5 SIZE => 5
@ -115,10 +98,18 @@ begin
a_in => phy_phy_out.rx_data_valid, a_in => phy_phy_out.rx_data_valid,
b_clk => clk, b_clk => clk,
b_rst => rst, 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 cdc_strobe_rxer_inst : entity work.cdc_strobe
port map( port map(

View File

@ -13,70 +13,27 @@ use ieee.std_logic_1164.all;
use ieee.numeric_std.all; use ieee.numeric_std.all;
package trashernet_pkg is package trashernet_pkg is
-- ----------------
-- Type definitions
-- ----------------
-- General types -- General types
subtype byte is std_logic_vector(7 downto 0); subtype byte is std_logic_vector(7 downto 0);
type byte_vector is array (natural range <>) of byte; type byte_vector is array (natural range <>) of byte;
-- MAC specific types -- MAC specific types
subtype mac_addr_t is byte_vector(0 to 5); subtype mac_addr_t is byte_vector(0 to 5);
constant MAC_ADDR_BROADCAST : mac_addr_t := (others => x"FF"); constant MAC_ADDR_BROADCAST : mac_addr_t := (others => x"FF");
function mac_to_string(constant mac_address : in mac_addr_t) return string; function mac_to_string(constant mac_address : in mac_addr_t) return string;
subtype ethertype_t is byte_vector(0 to 1); 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_destination : mac_addr_t; -- Destination MAC address
mac_source : mac_addr_t; -- Source MAC address mac_source : mac_addr_t; -- Source MAC address
mac_ethertype : ethertype_t; -- Ethertype or length mac_ethertype : ethertype_t; -- Ethertype or length
end record mac_header_t; end record mac_header_fields;
-- 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;
-- IP specific types -- IP specific types
subtype ip_addr_t is byte_vector(0 to 3); subtype ip_addr_t is byte_vector(0 to 3);
function ip_to_string(constant ip_address : in ip_addr_t) return string; 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; -- Configuration interface
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
type configuration_t is record type configuration_t is record
mac_address : mac_addr_t; -- MAC address of this node mac_address : mac_addr_t; -- MAC address of this node
end record configuration_t; end record configuration_t;
@ -86,19 +43,6 @@ package trashernet_pkg is
subnet_mask : ip_addr_t; -- Subnet mask of this node subnet_mask : ip_addr_t; -- Subnet mask of this node
end record ipv4_configuration_t; 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 -- PHY interface
type phy_in_t is record type phy_in_t is record
tx_data : byte; -- TX Data tx_data : byte; -- TX Data
@ -118,12 +62,12 @@ package trashernet_pkg is
-- MAC interface -- MAC interface
type mac_in_t is record 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 : byte; -- Payload
tx_mac_data_en : std_logic; -- Start (and keep) transmitting a frame tx_mac_data_en : std_logic; -- Start (and keep) transmitting a frame
end record mac_in_t; end record mac_in_t;
type mac_out_t is record 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_header_rcv : std_logic; -- `rx_mac` header have been received and are valid
rx_mac_data : byte; -- Ethernet data (after Ethertype) rx_mac_data : byte; -- Ethernet data (after Ethertype)
@ -135,18 +79,25 @@ package trashernet_pkg is
rx_mac_crc_error : std_logic; -- End of packet, CRC invalid rx_mac_crc_error : std_logic; -- End of packet, CRC invalid
end record mac_out_t; 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 type ethernet_i_out_t is record
rx_crc_ok : std_logic; -- End of packet, CRC OK rx_mac_address : mac_addr_t; -- Source MAC address
rx_crc_error : std_logic; -- End of packet, CRC invalid 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_data : byte; -- RX data
rx_header_rcv : std_logic; -- Start of reception, `header` is valid rx_data_valid : std_logic; -- RX data valid strobe
rx_length : unsigned(15 downto 0); -- RX payload length in bytes
rx_data : byte; -- RX data rx_header_rcv : std_logic; -- Start of reception, `rx_length` is valid
rx_data_valid : std_logic; -- RX data valid strobe tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
end record; end record;
type ethernet_i_in_t is record type ethernet_i_in_t is record
tx_mac_address : mac_addr_t; -- Destination MAC address tx_mac_address : mac_addr_t; -- Destination MAC address
@ -156,27 +107,23 @@ package trashernet_pkg is
end record; 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 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 type ethernet_ii_out_t is record
rx_header : ethernet_ii_header_t; rx_mac_address : mac_addr_t; -- Source MAC address
rx_header_rcv : std_logic; -- Start of reception 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_ok : std_logic; -- End of packet, CRC OK
rx_crc_error : std_logic; -- End of packet, CRC invalid rx_crc_error : std_logic; -- End of packet, CRC invalid
rx_header_rcv : std_logic; -- Start of reception
rx_data : byte; -- RX data tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
rx_data_valid : std_logic; -- RX data valid strobe
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
end record; end record;
type ethernet_ii_in_t is record type ethernet_ii_in_t is record
tx_mac_address : mac_addr_t; -- Destination MAC address tx_mac_address : mac_addr_t; -- Destination MAC address
tx_data : byte; -- TX data tx_data : byte; -- TX data
tx_en : std_logic; -- Start and continue transmitting tx_en : std_logic; -- Start and continue transmitting
end record; end record;
type ethernet_ii_out_vector is array (natural range <>) of ethernet_ii_out_t; 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; 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'); constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0');
-- ARP interface -- ARP interface
type arp_out_t is record type arp_out_t is record
@ -190,15 +137,23 @@ package trashernet_pkg is
end record arp_in_t; end record arp_in_t;
-- IPv4 interface -- IPv4 interface
type ipv4_out_t is record subtype ipv4_protocol is byte;
rx_header : ipv4_header_t; type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol;
rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid) 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 type ipv4_out_t is record
rx_error_stb : std_logic; -- End of packet, checksum invalid 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 : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe 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_data_ack : std_logic; -- Give next data byte of disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful tx_ok_stb : std_logic; -- Transmission successful
@ -213,14 +168,14 @@ package trashernet_pkg is
end record ipv4_in_t; end record ipv4_in_t;
type ipv4_protocol_out_t is record type ipv4_protocol_out_t is record
rx_header : ipv4_header_t; rx_ip_address : ip_addr_t; -- Source IP address
rx_header_rcv : std_logic; -- Start of reception rx_length : ipv4_length; -- Telegram length (excluding header)
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
rx_data : byte; -- RX data rx_data : byte; -- RX data
rx_data_valid : std_logic; -- RX data valid strobe 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_data_ack : std_logic; -- Give next data byte of disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful tx_ok_stb : std_logic; -- Transmission successful
@ -234,68 +189,9 @@ package trashernet_pkg is
tx_en : std_logic; -- Start and continue transmitting tx_en : std_logic; -- Start and continue transmitting
end record ipv4_protocol_in_t; end record ipv4_protocol_in_t;
type ipv4_protocol_out_vector is array (natural range <>) of ipv4_protocol_out_t; 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; 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 -- General helper functions
-- ------------------------
function to_std_logic(constant bool : boolean) return std_logic; function to_std_logic(constant bool : boolean) return std_logic;
end package trashernet_pkg; end package trashernet_pkg;
@ -322,11 +218,6 @@ package body trashernet_pkg is
; ;
end function ip_to_string; 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 function to_std_logic(constant bool : boolean) return std_logic is
variable ret : std_logic; variable ret : std_logic;
begin 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;