Compare commits
1 Commits
master
...
feature/tr
| Author | SHA1 | Date | |
|---|---|---|---|
| 206332f3d1 |
@ -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>
|
||||||
|
|||||||
27
.project
27
.project
@ -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><project>/.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>
|
||||||
|
|||||||
2
.settings/com.sigasi.hdt.toolchains.vunit.prefs
Normal file
2
.settings/com.sigasi.hdt.toolchains.vunit.prefs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
VUnitScriptLocation=vunit/run.py
|
||||||
|
eclipse.preferences.version=1
|
||||||
@ -1,2 +0,0 @@
|
|||||||
eclipse.preferences.version=1
|
|
||||||
isEnabled=true
|
|
||||||
@ -1 +0,0 @@
|
|||||||
<project>=sv2012
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
eclipse.preferences.version=1
|
|
||||||
isEnabled=true
|
|
||||||
@ -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
|
||||||
|
|||||||
@ -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_raw_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
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@ -31,6 +31,7 @@ architecture bench of bench_trashernet_phy is
|
|||||||
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
|
||||||
generic map(
|
generic map(
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
@ -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
|
|
||||||
|
|
||||||
@ -225,37 +225,7 @@ architecture eth of top_hwitl is
|
|||||||
);
|
);
|
||||||
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;
|
||||||
@ -409,19 +379,16 @@ begin
|
|||||||
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
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
vunit_hdl
|
|
||||||
cocotb
|
|
||||||
@ -87,7 +87,7 @@ 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';
|
||||||
@ -101,8 +101,8 @@ begin
|
|||||||
ethernet_i_out.rx_crc_error <= mac_out.rx_mac_crc_error when sel = SEL_ETH_I else '0';
|
ethernet_i_out.rx_crc_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
|
||||||
@ -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;
|
||||||
|
|||||||
@ -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",
|
||||||
|
|||||||
@ -59,7 +59,6 @@ begin
|
|||||||
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;
|
||||||
@ -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,24 +126,14 @@ 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;
|
||||||
|
|
||||||
@ -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 =>
|
||||||
@ -249,7 +233,6 @@ begin
|
|||||||
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;
|
||||||
|
|||||||
@ -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,7 +83,7 @@ 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
|
||||||
@ -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;
|
||||||
|
|||||||
@ -13,10 +13,6 @@ 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;
|
||||||
@ -27,56 +23,17 @@ package trashernet_pkg is
|
|||||||
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,17 +79,39 @@ 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
|
-- Raw MAC interface
|
||||||
|
type raw_mac_in_t is record
|
||||||
|
tx_mac_data : byte; -- Payload
|
||||||
|
tx_mac_data_en : std_logic; -- Start (and keep) transmitting a frame
|
||||||
|
end record raw_mac_in_t;
|
||||||
|
type raw_mac_out_t is record
|
||||||
|
rx_mac_data : byte; -- Ethernet data (after Ethertype)
|
||||||
|
rx_mac_valid : std_logic; -- `rx_mac` values (headers + data) are valid
|
||||||
|
tx_mac_data_ack : std_logic; -- The byte on `tx_mac_data` has been latched. Update to next word.
|
||||||
|
tx_active : std_logic; -- Transmission in progress
|
||||||
|
|
||||||
|
rx_mac_crc_ok : std_logic; -- End of packet, CRC OK (strobe independent from other rx_mac fields)
|
||||||
|
rx_mac_crc_error : std_logic; -- End of packet, CRC invalid
|
||||||
|
end record raw_mac_out_t;
|
||||||
|
|
||||||
|
-- MAC ETH interface
|
||||||
|
type ethernet_ii_protocol_t is record
|
||||||
|
ethertype : ethertype_t;
|
||||||
|
end record;
|
||||||
|
type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t;
|
||||||
|
constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- @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_mac_address : mac_addr_t; -- Source MAC address
|
||||||
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 : ethernet_i_header_t;
|
|
||||||
rx_header_rcv : std_logic; -- Start of reception, `header` is valid
|
|
||||||
|
|
||||||
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_length : unsigned(15 downto 0); -- RX payload length in bytes
|
||||||
|
rx_header_rcv : std_logic; -- Start of reception, `rx_length` is valid
|
||||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
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
|
||||||
@ -156,17 +122,13 @@ 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_crc_ok : std_logic; -- End of packet, CRC OK
|
|
||||||
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
|
||||||
|
|
||||||
rx_data : byte; -- RX data
|
rx_data : byte; -- RX data
|
||||||
rx_data_valid : std_logic; -- RX data valid strobe
|
rx_data_valid : std_logic; -- RX data valid strobe
|
||||||
|
rx_crc_ok : std_logic; -- End of packet, CRC OK
|
||||||
|
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
||||||
|
rx_header_rcv : std_logic; -- Start of reception
|
||||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
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
|
||||||
@ -190,15 +152,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 +183,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
|
||||||
@ -236,66 +206,7 @@ package trashernet_pkg is
|
|||||||
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 +233,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
|
||||||
|
|||||||
214
trashernet/trashernet_raw_mac.vhd
Normal file
214
trashernet/trashernet_raw_mac.vhd
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- trashernet_raw_mac.vhd : Ethernet OSI Layer 2, Data Link
|
||||||
|
-- Implements packet handling for sync header + CRC.
|
||||||
|
-- -------------------------------------------------------------------------- --
|
||||||
|
-- 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_raw_mac is
|
||||||
|
port(
|
||||||
|
-- Global
|
||||||
|
clk : in std_logic; -- Global clock
|
||||||
|
rst : in std_logic; -- Asynchronous reset
|
||||||
|
|
||||||
|
-- PHY signals
|
||||||
|
phy_out : in phy_out_t; -- PHY application IF (out of PHY)
|
||||||
|
phy_in : out phy_in_t; -- PHY application IF (into PHY)
|
||||||
|
|
||||||
|
-- MAC application IF
|
||||||
|
mac_out : out raw_mac_out_t; -- MAC application IF (out of MAC)
|
||||||
|
mac_in : in raw_mac_in_t -- MAC application IF (into MAC)
|
||||||
|
);
|
||||||
|
end entity trashernet_raw_mac;
|
||||||
|
|
||||||
|
architecture rtl of trashernet_raw_mac is
|
||||||
|
constant ETH_POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7";
|
||||||
|
constant ETH_FRAME_MIN_LENGTH : integer := 64;
|
||||||
|
|
||||||
|
begin
|
||||||
|
rx : block
|
||||||
|
constant CRC_LENGTH : integer := 4;
|
||||||
|
signal sr_head : byte_vector(0 to CRC_LENGTH - 1);
|
||||||
|
constant BYTE_COUNT_MAX : integer := CRC_LENGTH;
|
||||||
|
signal byte_count : integer range 0 to BYTE_COUNT_MAX;
|
||||||
|
|
||||||
|
signal sr_payload : byte_vector(0 to 4);
|
||||||
|
|
||||||
|
signal crc : std_logic_vector(ETH_POLYNOMIAL'range);
|
||||||
|
signal crc_ok : std_logic;
|
||||||
|
signal crc_clear : std_logic;
|
||||||
|
begin
|
||||||
|
crc_inst : entity work.crc
|
||||||
|
generic map(
|
||||||
|
POLYNOMIAL => ETH_POLYNOMIAL,
|
||||||
|
START => x"FFFFFFFF",
|
||||||
|
FINAL_XOR => x"FFFFFFFF",
|
||||||
|
REVERSE_OUT => true
|
||||||
|
)
|
||||||
|
port map(
|
||||||
|
clk => clk,
|
||||||
|
rst => rst,
|
||||||
|
data => phy_out.rx_data,
|
||||||
|
data_valid => phy_out.rx_data_valid,
|
||||||
|
crc_clear => crc_clear,
|
||||||
|
crc_out => crc
|
||||||
|
);
|
||||||
|
|
||||||
|
crc_ok <= '1' when crc = x"2144DF1C" else '0';
|
||||||
|
|
||||||
|
main : process(clk, rst) is
|
||||||
|
begin
|
||||||
|
if rst then
|
||||||
|
byte_count <= 0;
|
||||||
|
mac_out.rx_mac_valid <= '0';
|
||||||
|
mac_out.rx_mac_crc_error <= '0';
|
||||||
|
mac_out.rx_mac_crc_ok <= '0';
|
||||||
|
crc_clear <= '0';
|
||||||
|
|
||||||
|
elsif rising_edge(clk) then
|
||||||
|
mac_out.rx_mac_valid <= '0';
|
||||||
|
mac_out.rx_mac_crc_error <= '0';
|
||||||
|
mac_out.rx_mac_crc_ok <= '0';
|
||||||
|
crc_clear <= '0';
|
||||||
|
|
||||||
|
if phy_out.rx_data_valid then
|
||||||
|
sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data;
|
||||||
|
if byte_count = CRC_LENGTH then
|
||||||
|
mac_out.rx_mac_valid <= '1';
|
||||||
|
else
|
||||||
|
byte_count <= byte_count + 1;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
if not crc_clear and not phy_out.rx_active then
|
||||||
|
mac_out.rx_mac_crc_ok <= crc_ok;
|
||||||
|
mac_out.rx_mac_crc_error <= not crc_ok;
|
||||||
|
end if;
|
||||||
|
if phy_out.rx_error then
|
||||||
|
mac_out.rx_mac_crc_error <= '1';
|
||||||
|
end if;
|
||||||
|
|
||||||
|
if (not phy_out.rx_active) or phy_out.rx_error then
|
||||||
|
byte_count <= 0;
|
||||||
|
crc_clear <= '1';
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
end process main;
|
||||||
|
mac_out.rx_mac_data <= sr_payload(0);
|
||||||
|
|
||||||
|
end block rx;
|
||||||
|
|
||||||
|
tx : block
|
||||||
|
type tx_state_t is (IDLE, DATA, PAD, TXCRC);
|
||||||
|
signal tx_state : tx_state_t;
|
||||||
|
|
||||||
|
constant SYNC_HEADER : byte_vector(0 to 7) := byte_vector'(0 to 6 => x"55", 7 => x"D5");
|
||||||
|
signal sr : byte_vector(0 to SYNC_HEADER'length - 1);
|
||||||
|
constant SR_CNT_MAX : integer := sr'high;
|
||||||
|
signal sr_cnt : integer range 0 to SR_CNT_MAX;
|
||||||
|
constant BYTE_CNT_MAX : integer := ETH_FRAME_MIN_LENGTH - 4 + 1; -- Minus four because the CRC is *after* the PAD, plus one because the logic counts one of the header bytes
|
||||||
|
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||||
|
|
||||||
|
signal crc : std_logic_vector(ETH_POLYNOMIAL'range);
|
||||||
|
signal crc_clear : std_logic;
|
||||||
|
signal crc_valid : std_logic;
|
||||||
|
begin
|
||||||
|
crc_inst : entity work.crc
|
||||||
|
generic map(
|
||||||
|
POLYNOMIAL => ETH_POLYNOMIAL,
|
||||||
|
START => x"FFFFFFFF",
|
||||||
|
FINAL_XOR => x"FFFFFFFF",
|
||||||
|
REVERSE_OUT => true
|
||||||
|
)
|
||||||
|
port map(
|
||||||
|
clk => clk,
|
||||||
|
rst => rst,
|
||||||
|
data => sr(0),
|
||||||
|
data_valid => crc_valid,
|
||||||
|
crc_clear => crc_clear,
|
||||||
|
crc_out => crc
|
||||||
|
);
|
||||||
|
|
||||||
|
tx_main : process(clk, rst) is
|
||||||
|
variable more_data : std_logic;
|
||||||
|
begin
|
||||||
|
if rst then
|
||||||
|
mac_out.tx_mac_data_ack <= '0';
|
||||||
|
tx_state <= IDLE;
|
||||||
|
sr_cnt <= 0;
|
||||||
|
byte_cnt <= 0;
|
||||||
|
|
||||||
|
elsif rising_edge(clk) then
|
||||||
|
mac_out.tx_mac_data_ack <= '0';
|
||||||
|
crc_valid <= '0';
|
||||||
|
more_data := more_data and mac_in.tx_mac_data_en; -- Latch a short low-pulse on tx_en
|
||||||
|
|
||||||
|
if phy_out.tx_data_ack then
|
||||||
|
sr <= sr(sr'low + 1 to sr'high) & x"00";
|
||||||
|
if sr_cnt /= 0 then
|
||||||
|
sr_cnt <= sr_cnt - 1;
|
||||||
|
end if;
|
||||||
|
if byte_cnt /= 0 then
|
||||||
|
byte_cnt <= byte_cnt - 1;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
if (sr_cnt /= 0) then
|
||||||
|
byte_cnt <= BYTE_CNT_MAX;
|
||||||
|
end if;
|
||||||
|
case tx_state is
|
||||||
|
when IDLE => -- Idle + prepare sync header
|
||||||
|
if not phy_out.tx_active and mac_in.tx_mac_data_en then
|
||||||
|
more_data := '1';
|
||||||
|
sr(SYNC_HEADER'range) <= SYNC_HEADER;
|
||||||
|
sr_cnt <= 7;
|
||||||
|
tx_state <= DATA;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
when DATA => -- Wait for MAC header + transmit payload until tx_mac_data_en goes away
|
||||||
|
if (phy_out.tx_data_ack = '1') and (sr_cnt = 0) then -- MAC Header TX complete
|
||||||
|
if more_data then
|
||||||
|
sr(0) <= mac_in.tx_mac_data;
|
||||||
|
crc_valid <= '1';
|
||||||
|
mac_out.tx_mac_data_ack <= '1';
|
||||||
|
else
|
||||||
|
tx_state <= PAD;
|
||||||
|
if (byte_cnt /= 0) then -- TODO: just testing!
|
||||||
|
-- sr(0) will have been shifted to x"00"
|
||||||
|
crc_valid <= '1';
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
when PAD => -- Wait for PADs / last payload byte + prepare CRC
|
||||||
|
if phy_out.tx_data_ack then
|
||||||
|
crc_valid <= '1';
|
||||||
|
elsif (byte_cnt = 0) then
|
||||||
|
sr(0 to 3) <= (crc(7 downto 0), crc(15 downto 8), crc(23 downto 16), crc(31 downto 24));
|
||||||
|
sr_cnt <= 4 - 1;
|
||||||
|
tx_state <= TXCRC;
|
||||||
|
end if;
|
||||||
|
|
||||||
|
when TXCRC => -- Wait for CRC + jump back to idle
|
||||||
|
if (phy_out.tx_data_ack = '1') and (sr_cnt = 0) then -- CRC TX complete
|
||||||
|
tx_state <= IDLE;
|
||||||
|
end if;
|
||||||
|
end case;
|
||||||
|
end if;
|
||||||
|
end process tx_main;
|
||||||
|
phy_in.tx_data_en <= '1' when tx_state /= IDLE else '0';
|
||||||
|
crc_clear <= '1' when sr_cnt /= 0 else '0'; -- todo: this might be to early
|
||||||
|
phy_in.tx_data <= sr(sr'low);
|
||||||
|
|
||||||
|
mac_out.tx_active <= phy_out.tx_active;
|
||||||
|
end block tx;
|
||||||
|
|
||||||
|
end architecture rtl;
|
||||||
@ -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;
|
|
||||||
|
|
||||||
@ -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;
|
|
||||||
@ -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;
|
|
||||||
Loading…
x
Reference in New Issue
Block a user