Compare commits
2 Commits
master
...
wip/cococi
Author | SHA1 | Date | |
---|---|---|---|
9f2f3223e5 | |||
64f6197dbb |
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType">
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_BUILDER_ENABLED" value="false"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_DISABLED_BUILDER" value="com.sigasi.hdt.toolchains.vunit.builder"/>
|
||||
<mapAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS"/>
|
||||
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
|
||||
</launchConfiguration>
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,12 +1 @@
|
||||
# VUnit
|
||||
vunit_out
|
||||
|
||||
# Cocotb
|
||||
__pycache__
|
||||
*.o
|
||||
cocotb/results.xml
|
||||
cocotb/sim_build
|
||||
cocotb/wave.ghw
|
||||
|
||||
cocotb/cocotb_top_mac_test
|
||||
cocotb/cocotb_top_hwitl
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "cocotb/cocotb_helpers"]
|
||||
path = cocotb/cocotb_helpers
|
||||
url = https://git.notsyncing.net/fpga/cocotb_helpers.git
|
@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<com.sigasi.hdt.shared.librarymapping.model:LibraryMappings xmlns:com.sigasi.hdt.shared.librarymapping.model="com.sigasi.hdt.vhdl.scoping.librarymapping" Version="2">
|
||||
<Mappings Location="bench" Library="bench"/>
|
||||
<Mappings Location="cocotb" Library="cocotb"/>
|
||||
<Mappings Location="design" Library="design"/>
|
||||
<Mappings Location="Common Libraries/IEEE" Library="ieee"/>
|
||||
<Mappings Location="Common Libraries/IEEE Synopsys" Library="ieee"/>
|
||||
<Mappings Location="" Library="not mapped"/>
|
||||
<Mappings Location="Common Libraries" Library="not mapped"/>
|
||||
<Mappings Location="Common Libraries/vunit" Library="not mapped"/>
|
||||
<Mappings Location="cocotb/__pycache__" Library="not mapped"/>
|
||||
<Mappings Location="cocotb/sim_build" Library="not mapped"/>
|
||||
<Mappings Location="Common Libraries/STD" Library="std"/>
|
||||
<Mappings Location="trashernet" Library="trashernet"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IP_complete_nomac.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4_RX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/IPv4_TX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_Complete_nomac.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_RX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/UDP_TX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_REQ.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_RX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_STORE_br.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_SYNC.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_TX.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arp_types.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/arpv2.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/axi.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/ipv4_types.vhd" Library="udpip"/>
|
||||
<Mappings Location="udp_ip_stack/rtl/vhdl/tx_arbitrator.vhd" Library="udpip"/>
|
||||
<Mappings Location="Common Libraries/vunit/vunit/vhdl" Library="vunit_lib"/>
|
||||
<Mappings Location="Common Libraries/src" Library="xp2"/>
|
||||
</com.sigasi.hdt.shared.librarymapping.model:LibraryMappings>
|
66
.project
66
.project
@ -1,66 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>trashernet</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<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>
|
||||
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature>
|
||||
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
|
||||
<nature>com.sigasi.hdt.toolchains.vunit.nature</nature>
|
||||
</natures>
|
||||
<linkedResources>
|
||||
<link>
|
||||
<name>Common Libraries</name>
|
||||
<type>2</type>
|
||||
<locationURI>virtual:/virtual</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/DRAG_REUSABLE_LIBRARIES_HERE.txt</name>
|
||||
<type>1</type>
|
||||
<locationURI>sigasiresource:/vhdl/readme.txt</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/IEEE</name>
|
||||
<type>2</type>
|
||||
<locationURI>sigasiresource:/vhdl/2008/IEEE</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/IEEE Synopsys</name>
|
||||
<type>2</type>
|
||||
<locationURI>sigasiresource:/vhdl/2008/IEEE%20Synopsys</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/STD</name>
|
||||
<type>2</type>
|
||||
<locationURI>sigasiresource:/vhdl/2008/STD</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/src</name>
|
||||
<type>2</type>
|
||||
<location>/usr/local/diamond/3.12/cae_library/simulation/vhdl/xp2/src</location>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/vunit</name>
|
||||
<type>2</type>
|
||||
<locationURI>PARENT-1-PROJECT_LOC/vunit</locationURI>
|
||||
</link>
|
||||
</linkedResources>
|
||||
</projectDescription>
|
@ -1,2 +0,0 @@
|
||||
VUnitScriptLocation=vunit/run.py
|
||||
eclipse.preferences.version=1
|
@ -1 +0,0 @@
|
||||
<project>=2008
|
@ -1,5 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//Common\ Libraries/IEEE=utf-8
|
||||
encoding//Common\ Libraries/IEEE\ Synopsys=utf-8
|
||||
encoding//Common\ Libraries/STD=utf-8
|
||||
encoding/Common\ Libraries=utf-8
|
20
README.MD
20
README.MD
@ -5,8 +5,7 @@
|
||||
Trashernet is a very trashy Ethernet stack for FPGAs written in VHDL aiming to cover all OSI layers:
|
||||
|
||||
* Layer 1, Physical: `trashernet_phy`
|
||||
* Layer 2, Data link: `trashernet_mac`, `trashernet_eth`, `trashernet_arp`
|
||||
* Layer 3, Network: `trashernet_ipv4`, `trashernet_ipv4prot`, `trashernet_icmp`
|
||||
* Layer 2, Data link: `trashernet_mac`
|
||||
|
||||
When writing it, the following were the main design philosophies:
|
||||
|
||||
@ -28,23 +27,6 @@ Notes:
|
||||
* The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice!
|
||||
* On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D
|
||||
|
||||
## Stats
|
||||
|
||||
Sythesized for a Lattice LFXP2-5E FPGA using LSE / Diamond v3.12:
|
||||
|
||||
| Configuration | Slices | LUT4 | Registers |
|
||||
| ---------------- | ------ | ---- | --------- |
|
||||
| `trashernet_phy` | 115 | 188 | 91 |
|
||||
|
||||
## License
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
|
||||
The Trashernet logo uses graphics from [svgrepo.com](https://svgrepo.com), which are released under the Creative Commons CC0 license, granting the right to freely adapt and use them in any context. Thank you for making these available:
|
||||
|
||||
* Trash can: https://www.svgrepo.com/svg/66604/trash
|
||||
* Ethernet jack: https://www.svgrepo.com/svg/292518/ethernet
|
||||
|
||||
The font used in the logo, "White Rabbit" by Matthew Welch, is also released on a free-to-use basis. Thank you!
|
||||
|
||||
* White Rabbit: https://www.fontsquirrel.com/fonts/white-rabbit
|
||||
|
@ -18,10 +18,10 @@ use work.bench_pkg.all;
|
||||
library trashernet;
|
||||
use trashernet.trashernet_pkg.all;
|
||||
|
||||
entity bench_trashernet_phy is
|
||||
end entity bench_trashernet_phy;
|
||||
entity cocotb_trashernet_phy is
|
||||
end entity cocotb_trashernet_phy;
|
||||
|
||||
architecture bench of bench_trashernet_phy is
|
||||
architecture bench of cocotb_trashernet_phy is
|
||||
signal clk : std_logic;
|
||||
signal phy_clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
|
@ -1,42 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- pll0.vhd : Simulates the PLL component present in some demo designs
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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;
|
||||
|
||||
entity pll0 is
|
||||
port(
|
||||
CLK : in std_logic;
|
||||
CLKOP : out std_logic;
|
||||
LOCK : out std_logic
|
||||
);
|
||||
end entity pll0;
|
||||
|
||||
architecture bench of pll0 is
|
||||
begin
|
||||
clock_driver : process
|
||||
constant period : time := 7.1429 ns; -- 140 MHz configured clock
|
||||
begin
|
||||
CLKOP <= '0';
|
||||
LOCK <= '0';
|
||||
|
||||
wait until CLK'event;
|
||||
wait for 12 ns;
|
||||
LOCK <= '1';
|
||||
|
||||
loop
|
||||
CLKOP <= '0';
|
||||
wait for period / 2;
|
||||
CLKOP <= '1';
|
||||
wait for period / 2;
|
||||
end loop;
|
||||
end process clock_driver;
|
||||
end architecture bench;
|
4
cocotb/.gitignore
vendored
Normal file
4
cocotb/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.pyc
|
||||
*.o
|
||||
results.xml
|
||||
*.cf
|
@ -1,19 +1,14 @@
|
||||
# defaults
|
||||
|
||||
SIM ?= ghdl
|
||||
TOPLEVEL_LANG ?= vhdl
|
||||
BASE = $(PWD)/..
|
||||
BENCHTOP ?= cocotb_top_mac_test
|
||||
|
||||
COMPILE_ARGS=--std=08
|
||||
# SIM_ARGS ?= --wave=wave.ghw
|
||||
|
||||
VHDL_SOURCES_trashernet += $(BASE)/trashernet/*.vhd
|
||||
VHDL_SOURCES_design += $(BASE)/design/*.vhd
|
||||
VHDL_SOURCES_design += $(BASE)/bench/pll0.vhd
|
||||
VHDL_SOURCES += $(BASE)/cocotb/*.vhd
|
||||
|
||||
TOPLEVEL ?= $(BENCHTOP)
|
||||
MODULE ?= $(BENCHTOP)
|
||||
TOPLEVEL = cocotb_trashernet_phy
|
||||
MODULE = cocotb_trashernet_phy
|
||||
|
||||
include $(shell cocotb-config --makefiles)/Makefile.sim
|
||||
|
@ -1 +0,0 @@
|
||||
Subproject commit e2c72ef61d7742fbcb7e048bc928446ed4b56a82
|
@ -1,90 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- cocotb_top_hwitl.vhd : Test bench for cocotb HW-in-the-loop tests
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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;
|
||||
|
||||
library design;
|
||||
use design.all;
|
||||
|
||||
entity cocotb_top_hwitl is
|
||||
end entity cocotb_top_hwitl;
|
||||
|
||||
architecture bench of cocotb_top_hwitl is
|
||||
-- DUT signals
|
||||
signal clk : std_logic;
|
||||
signal rst_n : std_logic;
|
||||
signal rx_p : std_logic;
|
||||
signal tx_p : std_logic;
|
||||
signal tx_n : std_logic;
|
||||
signal led_n : std_logic_vector(7 downto 0);
|
||||
signal button_n : std_logic_vector(3 downto 0);
|
||||
signal debug_data : std_logic_vector(7 downto 0);
|
||||
|
||||
-- Generic test bench
|
||||
signal bench_ready : std_logic := '0';
|
||||
begin
|
||||
top_mac_test_inst : entity top_hwitl(eth) -- TODO: I haven't managed to get configurations working with cocotb yet, so changing this line changes the test case :(
|
||||
port map(
|
||||
clk => clk,
|
||||
rst_n => rst_n,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n,
|
||||
led_n => led_n,
|
||||
button_n => button_n,
|
||||
debug_data => debug_data
|
||||
);
|
||||
|
||||
clock_driver : process
|
||||
constant period : time := 20 ns;
|
||||
begin
|
||||
clk <= '0';
|
||||
wait for period / 2;
|
||||
clk <= '1';
|
||||
wait for period / 2;
|
||||
end process clock_driver;
|
||||
|
||||
rstsim : process is
|
||||
begin
|
||||
rst_n <= '0';
|
||||
wait for 400 ns;
|
||||
rst_n <= '1';
|
||||
wait for 100 ns;
|
||||
wait until rising_edge(clk);
|
||||
bench_ready <= '1';
|
||||
wait;
|
||||
end process rstsim;
|
||||
|
||||
cocovc_eth_inst : entity work.cocovc_eth
|
||||
port map(
|
||||
rx_p => tx_p,
|
||||
rx_n => tx_n,
|
||||
tx_p => rx_p,
|
||||
tx_n => open
|
||||
);
|
||||
|
||||
test_seq : process is
|
||||
begin
|
||||
wait until bench_ready = '1';
|
||||
|
||||
button_n <= (others => '1');
|
||||
wait for 1 us;
|
||||
|
||||
wait until rising_edge(clk);
|
||||
button_n(0) <= '0';
|
||||
wait until rising_edge(clk);
|
||||
button_n(0) <= '1';
|
||||
|
||||
wait;
|
||||
end process test_seq;
|
||||
|
||||
end architecture bench;
|
@ -1,48 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import cocotb
|
||||
from cocotb.triggers import *
|
||||
from cocotb.result import *
|
||||
|
||||
from cocotb.queue import Queue
|
||||
|
||||
from cocotb_helpers import buffers
|
||||
|
||||
ETH_HEAD = b'\x55\x55\x55\x55\xD5'
|
||||
|
||||
async def timeout(dut):
|
||||
await Timer(1, units="ms")
|
||||
assert False, "Timeout"
|
||||
|
||||
async def rxprinter(dut, rx):
|
||||
while True:
|
||||
frame = await rx.queue.get()
|
||||
dut._log.info("RX Frame: " + str(frame))
|
||||
|
||||
@cocotb.test()
|
||||
async def my_test(dut):
|
||||
"""TBD description."""
|
||||
|
||||
# Start verification components
|
||||
eth_tx = buffers.tx_buffer(dut, "cocovc_eth_inst.cocotb_tx_")
|
||||
await eth_tx.start()
|
||||
eth_rx = buffers.rx_buffer(dut, "cocovc_eth_inst.cocotb_rx_")
|
||||
await eth_rx.start()
|
||||
|
||||
# Start local monitors
|
||||
await cocotb.start(rxprinter(dut, eth_rx))
|
||||
|
||||
# Start timeout
|
||||
await cocotb.start(timeout(dut))
|
||||
|
||||
# Wait for VHDL part to be ready
|
||||
await Edge(dut.bench_ready)
|
||||
|
||||
# Start test procedure
|
||||
await Timer(400, units="us") # The device will send a frame at startup
|
||||
|
||||
dut._log.info("Send first frame"); # This will trigger a response frame
|
||||
await eth_tx.send(ETH_HEAD + b'\x00\xff\xff\x11"3\x00\xff\xff\xaa\xbb\xcc\x00\n\n\t\x08\x07\x06\x05\x04\x03\x02\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00!\x10\x90\xe0');
|
||||
dut._log.info("Send second frame");
|
||||
await eth_tx.send(ETH_HEAD + b'World');
|
||||
await Timer(400, units="us") # Wait for response
|
@ -1,77 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- cocotb_top_mac_test.vhd : Test bench for the MAC test demo.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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;
|
||||
|
||||
library design;
|
||||
use design.all;
|
||||
|
||||
entity cocotb_top_mac_test is
|
||||
end entity cocotb_top_mac_test;
|
||||
|
||||
architecture bench of cocotb_top_mac_test is
|
||||
-- DUT signals
|
||||
signal clk : std_logic;
|
||||
signal rst_n : std_logic;
|
||||
signal rx_p : std_logic;
|
||||
signal tx_p : std_logic;
|
||||
signal tx_n : std_logic;
|
||||
signal led_n : std_logic_vector(7 downto 0);
|
||||
signal button_n : std_logic_vector(3 downto 0);
|
||||
signal debug_data : std_logic_vector(7 downto 0);
|
||||
|
||||
-- Generic test bench
|
||||
signal bench_ready : std_logic := '0';
|
||||
begin
|
||||
top_mac_test_inst : entity design.top_mac_test
|
||||
port map(
|
||||
clk => clk,
|
||||
rst_n => rst_n,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n,
|
||||
led_n => led_n,
|
||||
button_n => button_n,
|
||||
debug_data => debug_data
|
||||
);
|
||||
|
||||
clock_driver : process
|
||||
constant period : time := 20 ns;
|
||||
begin
|
||||
clk <= '0';
|
||||
wait for period / 2;
|
||||
clk <= '1';
|
||||
wait for period / 2;
|
||||
end process clock_driver;
|
||||
|
||||
rstsim : process is
|
||||
begin
|
||||
rst_n <= '0';
|
||||
wait for 400 ns;
|
||||
rst_n <= '1';
|
||||
wait for 100 ns;
|
||||
wait until rising_edge(clk);
|
||||
bench_ready <= '1';
|
||||
wait;
|
||||
end process rstsim;
|
||||
|
||||
cocovc_eth_inst : entity work.cocovc_eth
|
||||
port map(
|
||||
rx_p => tx_p,
|
||||
rx_n => tx_n,
|
||||
tx_p => rx_p,
|
||||
tx_n => open
|
||||
);
|
||||
|
||||
button_n <= (others => '1');
|
||||
|
||||
end architecture bench;
|
90
cocotb/cocotb_trashernet_phy.py
Normal file
90
cocotb/cocotb_trashernet_phy.py
Normal file
@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import cocotb
|
||||
from cocotb.triggers import *
|
||||
from cocotb.result import *
|
||||
|
||||
from cocotb.queue import Queue
|
||||
|
||||
# DUT interface (internal)
|
||||
tb_rx_queue = Queue()
|
||||
tb_rx_queue_done_ev = Event()
|
||||
|
||||
async def dut_receiver(dut):
|
||||
scoreboard = tb_rx_queue
|
||||
while True:
|
||||
await Edge(dut.coco_dut_rxs)
|
||||
byte = dut.coco_dut_rxd.value
|
||||
dut._log.info("[DT:RX <-] Byte %02X", byte)
|
||||
assert not scoreboard.empty(), "Data was provided on the RX interface even though none was expected"
|
||||
if not scoreboard.empty():
|
||||
sb_byte = await scoreboard.get()
|
||||
assert byte == sb_byte, f"Received {byte} instead of {sb_byte}"
|
||||
|
||||
async def dut_rxa(dut):
|
||||
scoreboard = tb_rx_queue
|
||||
await Edge(dut.coco_dut_rxa) # TODO: Workaround for some stupid bug somewhere
|
||||
while True:
|
||||
await Edge(dut.coco_dut_rxa)
|
||||
if dut.coco_dut_rxa.value:
|
||||
dut._log.info("[DT:RX <-] Start of frame")
|
||||
else:
|
||||
dut._log.info("[DT:RX <-] End of frame")
|
||||
if scoreboard.empty():
|
||||
tb_rx_queue_done_ev.set()
|
||||
|
||||
# TB / ETH interface (external)
|
||||
tb_tx_queue = Queue()
|
||||
|
||||
async def tb_transmitter(dut):
|
||||
queue = tb_tx_queue
|
||||
while True:
|
||||
byte = await queue.get()
|
||||
if byte == -1:
|
||||
dut._log.info("[TB:TX ->] Wait IPG")
|
||||
await Timer(16, units="us") # IPG
|
||||
else: # TX byte
|
||||
dut._log.info("[TB:TX ->] Byte %02X", byte)
|
||||
dut.coco_tb_txd.value = byte
|
||||
dut.coco_tb_txs.value = not dut.coco_tb_txs.value
|
||||
await Edge(dut.coco_tb_txa);
|
||||
|
||||
async def tb_send_packet(dut, data):
|
||||
queue = tb_tx_queue
|
||||
scoreboard = tb_rx_queue
|
||||
|
||||
queue.put_nowait(0x55)
|
||||
queue.put_nowait(0x55)
|
||||
queue.put_nowait(0x55)
|
||||
queue.put_nowait(0xD5)
|
||||
|
||||
for byte in data:
|
||||
scoreboard.put_nowait(byte)
|
||||
queue.put_nowait(byte)
|
||||
|
||||
queue.put_nowait(-1)
|
||||
|
||||
async def timeout(dut):
|
||||
await Timer(50, units="us")
|
||||
assert False, "Timeout"
|
||||
|
||||
@cocotb.test()
|
||||
async def simple_rx(dut):
|
||||
"""Receive a packet using trashernet."""
|
||||
|
||||
await cocotb.start(timeout(dut))
|
||||
|
||||
await cocotb.start(dut_receiver(dut))
|
||||
await cocotb.start(dut_rxa(dut))
|
||||
await cocotb.start(tb_transmitter(dut))
|
||||
|
||||
dut.rst.value = 1
|
||||
await Timer(20, units="ns")
|
||||
dut.rst.value = 0
|
||||
await Timer(20, units="ns")
|
||||
|
||||
await tb_send_packet(dut, b'\x02\x03\x05\x07')
|
||||
|
||||
await cocotb.start(timeout(dut))
|
||||
|
||||
await tb_rx_queue_done_ev.wait()
|
104
cocotb/cocotb_trashernet_phy.vhd
Normal file
104
cocotb/cocotb_trashernet_phy.vhd
Normal file
@ -0,0 +1,104 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- bench_trashernet_phy.vhd : Cocotb test bench for the 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;
|
||||
|
||||
library trashernet;
|
||||
use trashernet.trashernet_pkg.all;
|
||||
|
||||
entity cocotb_trashernet_phy is
|
||||
end entity cocotb_trashernet_phy;
|
||||
|
||||
architecture bench of cocotb_trashernet_phy is
|
||||
signal clk : std_logic;
|
||||
signal phy_clk : std_logic;
|
||||
signal rst : std_logic;
|
||||
signal phy_out : phy_out_t;
|
||||
signal phy_in : phy_in_t;
|
||||
signal rx_p : std_logic;
|
||||
signal tx_p : std_logic;
|
||||
signal tx_n : std_logic;
|
||||
|
||||
-- Cocotb interface
|
||||
signal coco_dut_rxd : std_logic_vector(7 downto 0); -- RX data
|
||||
signal coco_dut_rxs : std_logic := '0'; -- RX toggle
|
||||
signal coco_dut_rxa : std_logic; -- RX active
|
||||
|
||||
signal coco_tb_txd : std_logic_vector(7 downto 0); -- TX data
|
||||
signal coco_tb_txs : std_logic := '0'; -- TX toggle
|
||||
signal coco_tb_txa : std_logic := '0'; -- TX done toggle
|
||||
|
||||
begin
|
||||
-- Instantiate design
|
||||
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
|
||||
generic map(
|
||||
F_CLK => 49000000,
|
||||
F_CLK_PHY => 100000000
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
phy_clk => phy_clk,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
-- Create infrastructure
|
||||
clock_driver : process
|
||||
constant period : time := 20 ns;
|
||||
begin
|
||||
clk <= '0';
|
||||
wait for period / 2;
|
||||
clk <= '1';
|
||||
wait for period / 2;
|
||||
end process clock_driver;
|
||||
|
||||
phy_clock_driver : process
|
||||
constant period : time := 10 ns;
|
||||
begin
|
||||
phy_clk <= '0';
|
||||
wait for period / 2;
|
||||
phy_clk <= '1';
|
||||
wait for period / 2;
|
||||
end process phy_clock_driver;
|
||||
|
||||
-- Cocotb adapter (ETH)
|
||||
test : process is
|
||||
begin
|
||||
rx_p <= '0';
|
||||
|
||||
loop
|
||||
wait on coco_tb_txs;
|
||||
|
||||
for j in coco_tb_txd'low to coco_tb_txd'high loop
|
||||
rx_p <= not coco_tb_txd(j);
|
||||
wait for 50 ns;
|
||||
rx_p <= coco_tb_txd(j);
|
||||
wait for 50 ns;
|
||||
end loop;
|
||||
coco_tb_txa <= not coco_tb_txa;
|
||||
end loop;
|
||||
end process test;
|
||||
|
||||
receiver : process is
|
||||
begin
|
||||
wait until rising_edge(clk);
|
||||
if phy_out.rx_data_valid then
|
||||
coco_dut_rxd <= phy_out.rx_data;
|
||||
coco_dut_rxs <= not coco_dut_rxs;
|
||||
end if;
|
||||
end process receiver;
|
||||
coco_dut_rxa <= phy_out.rx_active;
|
||||
end architecture bench;
|
@ -1,123 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- cocovc_eth.vhd : Cocotb-based verification component for 10M Ethernet.
|
||||
-- Note: This is cheapskated to work with Trashernet. It may not work with
|
||||
-- every 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;
|
||||
|
||||
entity cocovc_eth is
|
||||
port(
|
||||
rx_p : in std_logic;
|
||||
rx_n : in std_logic;
|
||||
tx_p : out std_logic;
|
||||
tx_n : out std_logic
|
||||
);
|
||||
end entity cocovc_eth;
|
||||
|
||||
architecture bench of cocovc_eth is
|
||||
-- Ethernet TX (TB/Python to DUT/VHDL)
|
||||
signal cocotb_tx_d : std_logic_vector(7 downto 0) := x"00"; -- Data to be transferred to VHDL
|
||||
signal cocotb_tx_dv : std_logic := '0'; -- '1' when `d` holds valid data that is to be processed by VHDL
|
||||
signal cocotb_tx_da : std_logic := '0'; -- Toggles when VHDL has latched `d`, Python must advance data or deassert `dv`
|
||||
|
||||
-- Ethernet RX (DUT/VHDL to TB/Python)
|
||||
signal cocotb_rx_d : std_logic_vector(7 downto 0); -- Data to be transferred to Python
|
||||
signal cocotb_rx_dt : std_logic := '0'; -- Toggles to announce new data (Python has to latch immediately)
|
||||
signal cocotb_rx_a : std_logic := '0'; -- Block active indicator (rising on block start, falling on block end)
|
||||
begin
|
||||
-- Ethernet TX
|
||||
eth_tx_proc : process is
|
||||
variable d : std_logic_vector(cocotb_rx_d'range);
|
||||
begin
|
||||
tx_p <= '0';
|
||||
wait until cocotb_tx_dv = '1';
|
||||
|
||||
loop
|
||||
exit when not cocotb_tx_dv;
|
||||
|
||||
d := cocotb_tx_d;
|
||||
cocotb_tx_da <= not cocotb_tx_da;
|
||||
|
||||
for j in d'low to d'high loop
|
||||
tx_p <= not d(j);
|
||||
wait for 50 ns;
|
||||
tx_p <= d(j);
|
||||
wait for 50 ns;
|
||||
end loop;
|
||||
end loop;
|
||||
|
||||
wait for 16 us; -- Wait IPG
|
||||
cocotb_tx_da <= not cocotb_tx_da; -- Signal that end-of-block was latched
|
||||
|
||||
end process eth_tx_proc;
|
||||
tx_n <= not tx_p;
|
||||
|
||||
-- Ethernet RX
|
||||
eth_rx_proc : process is
|
||||
constant SYM_TIME : time := 50 ns;
|
||||
variable d : std_logic_vector(cocotb_rx_d'range) := x"00";
|
||||
variable cnt : integer range d'low to d'high;
|
||||
variable bit : std_logic;
|
||||
variable ignore : boolean;
|
||||
variable first : boolean; -- Just a cheap workaround because the weather is too nice to implement it properly this afternoon
|
||||
procedure latch(value : std_logic) is
|
||||
begin
|
||||
d(cnt) := value;
|
||||
if (cnt = 7) then
|
||||
cocotb_rx_d <= d;
|
||||
cocotb_rx_dt <= not cocotb_rx_dt;
|
||||
cnt := 0;
|
||||
else
|
||||
cnt := cnt + 1;
|
||||
end if;
|
||||
end procedure latch;
|
||||
|
||||
begin
|
||||
cocotb_rx_a <= '0';
|
||||
|
||||
loop
|
||||
wait on rx_p;
|
||||
exit when (rx_p'last_value = '0' or rx_p'last_value = '1');
|
||||
end loop;
|
||||
|
||||
cocotb_rx_a <= '1';
|
||||
wait for 0 ns;
|
||||
|
||||
cnt := 0;
|
||||
bit := '0';
|
||||
ignore := false;
|
||||
first := true;
|
||||
loop
|
||||
wait on rx_p for SYM_TIME * 1.5;
|
||||
exit when rx_p'last_event > 2.5 * SYM_TIME;
|
||||
|
||||
if rx_p'event then -- short
|
||||
if not ignore then
|
||||
latch(bit);
|
||||
end if;
|
||||
ignore := not ignore;
|
||||
else -- long
|
||||
if (first) then
|
||||
bit := '1';
|
||||
cnt := 1;
|
||||
d(0) := '1';
|
||||
end if;
|
||||
bit := not bit;
|
||||
latch(bit);
|
||||
ignore := true;
|
||||
end if;
|
||||
|
||||
first := false;
|
||||
end loop;
|
||||
end process eth_rx_proc;
|
||||
|
||||
end architecture bench;
|
@ -1,94 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import cocotb
|
||||
from cocotb.triggers import *
|
||||
from cocotb.result import *
|
||||
from cocotb.queue import Queue
|
||||
from cocotb_helpers import buffers
|
||||
|
||||
import sys
|
||||
import threading
|
||||
import socket
|
||||
import time
|
||||
import os
|
||||
import asyncio
|
||||
import fcntl
|
||||
import zlib
|
||||
|
||||
'''
|
||||
# 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
|
||||
'''
|
||||
|
||||
class MacDevReceiver():
|
||||
def __init__(self, dut, eth_tx, eth_rx, dev):
|
||||
self.dut = dut
|
||||
self.eth_tx = eth_tx
|
||||
self.eth_rx = eth_rx
|
||||
self.dev = dev
|
||||
self.mac_rx_ev = Event()
|
||||
|
||||
ETH_P_ALL=3
|
||||
self.macdev=socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))
|
||||
self.macdev.bind((dev, 0))
|
||||
fcntl.fcntl(self.macdev, fcntl.F_SETFL, os.O_NONBLOCK) # Not the best way to poll, but I couldn't get asyncio to play nicely with threading...
|
||||
|
||||
def eth_fcs(self, data):
|
||||
crc = zlib.crc32(data) & 0xFFFF_FFFF
|
||||
return crc.to_bytes(4, byteorder='little')
|
||||
|
||||
async def main(self):
|
||||
ETH_HEAD = b'\x55\x55\x55\x55\xD5'
|
||||
while True:
|
||||
try:
|
||||
r = self.macdev.recv(2000)
|
||||
if len(r) < 60:
|
||||
r += b'\x00' * (60 - len(r))
|
||||
r += self.eth_fcs(r)
|
||||
self.dut._log.debug("DUT RX: " + str(r))
|
||||
await self.eth_tx.send(ETH_HEAD + r);
|
||||
except:
|
||||
await Timer(1, "us")
|
||||
pass
|
||||
async def main_rx(self):
|
||||
while True:
|
||||
frame = await self.eth_rx.queue.get()
|
||||
try:
|
||||
while (frame[0] != 0xD5):
|
||||
frame = frame[1:]
|
||||
frame = frame[1:]
|
||||
except:
|
||||
self.dut._log.debug("NLP / Invalid frame")
|
||||
continue
|
||||
self.dut._log.debug("DUT TX: " + str(frame))
|
||||
self.macdev.send(frame)
|
||||
|
||||
async def start(self):
|
||||
await cocotb.start(self.main())
|
||||
await cocotb.start(self.main_rx())
|
||||
|
||||
@cocotb.test()
|
||||
async def hwitl(dut):
|
||||
"""Real-Ethernet-hardware in the loop test"""
|
||||
|
||||
# Start verification components
|
||||
eth_tx = buffers.tx_buffer(dut, "cocovc_eth_inst.cocotb_tx_")
|
||||
await eth_tx.start()
|
||||
eth_rx = buffers.rx_buffer(dut, "cocovc_eth_inst.cocotb_rx_")
|
||||
await eth_rx.start()
|
||||
|
||||
# Start local monitors
|
||||
macdev_receiver = MacDevReceiver(dut, eth_tx, eth_rx, "veth2")
|
||||
|
||||
# Wait for VHDL part to be ready
|
||||
await Edge(dut.bench_ready)
|
||||
|
||||
print("beep")
|
||||
await macdev_receiver.start()
|
||||
|
||||
print("Press Ctrl+C to stop the test.")
|
||||
await Timer(100, "sec")
|
@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo Hardware in the loop test
|
||||
|
||||
make TOPLEVEL=cocotb_top_hwitl MODULE=hw_itl
|
@ -1,68 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<BaliProject version="3.2" title="pingable" device="LFXP2-5E-6TN144C" default_implementation="impl1">
|
||||
<Options/>
|
||||
<Implementation title="impl1" dir="impl1" description="impl1" synthesis="lse" default_strategy="Strategy1">
|
||||
<Options def_top="trashernet.fifo" lib="work" top="top_pingable"/>
|
||||
<Source name="../ip/pll0.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options/>
|
||||
</Source>
|
||||
<Source name="../../../design/ledcon.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/cdc_strobe.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/crc.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/synchronizer.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_eth.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_mac.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_phy.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_phy_cdc.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_pkg.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_arp.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../design/top_hwitl.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/timer.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options/>
|
||||
</Source>
|
||||
<Source name="../../../design/top_pingable.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options top_module="top_pingable"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/fifo.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_icmp.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_ipv4.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="../../../trashernet/trashernet_ipv4prot.vhd" type="VHDL" type_short="VHDL">
|
||||
<Options lib="trashernet"/>
|
||||
</Source>
|
||||
<Source name="impl1/impl1.xcf" type="Programming Project File" type_short="Programming">
|
||||
<Options/>
|
||||
</Source>
|
||||
<Source name="pingable.lpf" type="Logic Preference" type_short="LPF">
|
||||
<Options/>
|
||||
</Source>
|
||||
</Implementation>
|
||||
<Strategy name="Strategy1" file="pingable1.sty"/>
|
||||
</BaliProject>
|
@ -1,47 +0,0 @@
|
||||
BLOCK RESETPATHS ;
|
||||
BLOCK ASYNCPATHS ;
|
||||
LOCATE COMP "clk" SITE "21" ;
|
||||
LOCATE COMP "rst_n" SITE "19" ;
|
||||
LOCATE COMP "rx_p" SITE "94" ;
|
||||
LOCATE COMP "led_n[0]" SITE "37" ;
|
||||
LOCATE COMP "led_n[1]" SITE "38" ;
|
||||
LOCATE COMP "led_n[2]" SITE "39" ;
|
||||
LOCATE COMP "led_n[3]" SITE "40" ;
|
||||
LOCATE COMP "led_n[4]" SITE "43" ;
|
||||
LOCATE COMP "led_n[5]" SITE "44" ;
|
||||
LOCATE COMP "led_n[6]" SITE "45" ;
|
||||
LOCATE COMP "led_n[7]" SITE "46" ;
|
||||
IOBUF PORT "rx_p" IO_TYPE=LVPECL33 ;
|
||||
IOBUF PORT "clk" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "rst_n" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[0]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[1]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[2]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[3]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[4]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[5]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[6]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "debug_data[7]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[0]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[1]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[2]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[3]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[4]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[5]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[6]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "led_n[7]" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "tx_n" IO_TYPE=LVCMOS33 ;
|
||||
IOBUF PORT "tx_p" IO_TYPE=LVCMOS33 ;
|
||||
LOCATE COMP "tx_n" SITE "114" ;
|
||||
LOCATE COMP "tx_p" SITE "116" ;
|
||||
LOCATE COMP "button_n[0]" SITE "54" ;
|
||||
LOCATE COMP "debug_data[0]" SITE "73" ;
|
||||
LOCATE COMP "debug_data[1]" SITE "74" ;
|
||||
LOCATE COMP "debug_data[2]" SITE "77" ;
|
||||
LOCATE COMP "debug_data[3]" SITE "87" ;
|
||||
LOCATE COMP "debug_data[4]" SITE "88" ;
|
||||
LOCATE COMP "debug_data[5]" SITE "89" ;
|
||||
LOCATE COMP "debug_data[6]" SITE "90" ;
|
||||
LOCATE COMP "debug_data[7]" SITE "91" ;
|
||||
BLOCK PATH FROM CLKNET "clk_c" TO CLKNET "clk_phy" ;
|
||||
BLOCK PATH FROM CLKNET "clk_phy" TO CLKNET "clk_c" ;
|
@ -1,206 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE strategy>
|
||||
<Strategy version="1.0" predefined="0" description="" label="Strategy1">
|
||||
<Property name="PROP_BD_CmdLineArgs" value="" time="0"/>
|
||||
<Property name="PROP_BD_EdfHardtimer" value="Enable" time="0"/>
|
||||
<Property name="PROP_BD_EdfInBusNameConv" value="None" time="0"/>
|
||||
<Property name="PROP_BD_EdfInLibPath" value="" time="0"/>
|
||||
<Property name="PROP_BD_EdfInRemLoc" value="Off" time="0"/>
|
||||
<Property name="PROP_BD_EdfMemPath" value="" time="0"/>
|
||||
<Property name="PROP_BD_ParSearchPath" value="" time="0"/>
|
||||
<Property name="PROP_BIT_AddressBitGen" value="Increment" time="0"/>
|
||||
<Property name="PROP_BIT_AllowReadBitGen" value="Disable" time="0"/>
|
||||
<Property name="PROP_BIT_ByteWideBitMirror" value="Disable" time="0"/>
|
||||
<Property name="PROP_BIT_CapReadBitGen" value="Disable" time="0"/>
|
||||
<Property name="PROP_BIT_ConModBitGen" value="Disable" time="0"/>
|
||||
<Property name="PROP_BIT_CreateBitFile" value="True" time="0"/>
|
||||
<Property name="PROP_BIT_DisRAMResBitGen" value="True" time="0"/>
|
||||
<Property name="PROP_BIT_DisableUESBitgen" value="False" time="0"/>
|
||||
<Property name="PROP_BIT_DonePinBitGen" value="Pullup" time="0"/>
|
||||
<Property name="PROP_BIT_DoneSigBitGen" value="4" time="0"/>
|
||||
<Property name="PROP_BIT_EnIOBitGen" value="TriStateDuringReConfig" time="0"/>
|
||||
<Property name="PROP_BIT_EnIntOscBitGen" value="Disable" time="0"/>
|
||||
<Property name="PROP_BIT_ExtClockBitGen" value="False" time="0"/>
|
||||
<Property name="PROP_BIT_GSREnableBitGen" value="True" time="0"/>
|
||||
<Property name="PROP_BIT_GSRRelOnBitGen" value="DoneIn" time="0"/>
|
||||
<Property name="PROP_BIT_GranTimBitGen" value="0" time="0"/>
|
||||
<Property name="PROP_BIT_IOTriRelBitGen" value="Cycle 2" time="0"/>
|
||||
<Property name="PROP_BIT_JTAGEnableBitGen" value="False" time="0"/>
|
||||
<Property name="PROP_BIT_LenBitsBitGen" value="24" time="0"/>
|
||||
<Property name="PROP_BIT_MIFFileBitGen" value="" time="0"/>
|
||||
<Property name="PROP_BIT_NoHeader" value="False" time="0"/>
|
||||
<Property name="PROP_BIT_OutFormatBitGen" value="Bit File (Binary)" time="0"/>
|
||||
<Property name="PROP_BIT_OutFormatBitGen_REF" value="Bit File (Binary)" time="0"/>
|
||||
<Property name="PROP_BIT_OutFormatPromGen" value="Intel Hex 32-bit" time="0"/>
|
||||
<Property name="PROP_BIT_ParityCheckBitGen" value="True" time="0"/>
|
||||
<Property name="PROP_BIT_ReadBackBitGen" value="Flash" time="0"/>
|
||||
<Property name="PROP_BIT_ReadCaptureBitGen" value="Disable" time="0"/>
|
||||
<Property name="PROP_BIT_RemZeroFramesBitGen" value="False" time="0"/>
|
||||
<Property name="PROP_BIT_RunDRCBitGen" value="True" time="0"/>
|
||||
<Property name="PROP_BIT_SearchPthBitGen" value="" time="0"/>
|
||||
<Property name="PROP_BIT_StartUpClkBitGen" value="Cclk" time="0"/>
|
||||
<Property name="PROP_BIT_SynchIOBitGen" value="True" time="0"/>
|
||||
<Property name="PROP_BIT_SysClockConBitGen" value="Reset" time="0"/>
|
||||
<Property name="PROP_BIT_SysConBitGen" value="Reset" time="0"/>
|
||||
<Property name="PROP_BIT_UFMOrder" value="Normal" time="0"/>
|
||||
<Property name="PROP_BIT_WaitStTimBitGen" value="5" time="0"/>
|
||||
<Property name="PROP_IOTIMING_AllSpeed" value="False" time="0"/>
|
||||
<Property name="PROP_LST_AllowDUPMod" value="False" time="0"/>
|
||||
<Property name="PROP_LST_CarryChain" value="True" time="0"/>
|
||||
<Property name="PROP_LST_CarryChainLength" value="0" time="0"/>
|
||||
<Property name="PROP_LST_CmdLineArgs" value="" time="0"/>
|
||||
<Property name="PROP_LST_DSPStyle" value="DSP" time="0"/>
|
||||
<Property name="PROP_LST_DSPUtil" value="100" time="0"/>
|
||||
<Property name="PROP_LST_DecodeUnreachableStates" value="False" time="0"/>
|
||||
<Property name="PROP_LST_DisableDistRam" value="False" time="0"/>
|
||||
<Property name="PROP_LST_EBRUtil" value="100" time="0"/>
|
||||
<Property name="PROP_LST_EdfFrequency" value="200" time="0"/>
|
||||
<Property name="PROP_LST_EdfHardtimer" value="Enable" time="0"/>
|
||||
<Property name="PROP_LST_EdfInLibPath" value="" time="0"/>
|
||||
<Property name="PROP_LST_EdfInRemLoc" value="Off" time="0"/>
|
||||
<Property name="PROP_LST_EdfMemPath" value="" time="0"/>
|
||||
<Property name="PROP_LST_FIXGATEDCLKS" value="True" time="0"/>
|
||||
<Property name="PROP_LST_FSMEncodeStyle" value="Auto" time="0"/>
|
||||
<Property name="PROP_LST_ForceGSRInfer" value="No" time="0"/>
|
||||
<Property name="PROP_LST_IOInsertion" value="True" time="0"/>
|
||||
<Property name="PROP_LST_InterFileDump" value="False" time="0"/>
|
||||
<Property name="PROP_LST_LoopLimit" value="1950" time="0"/>
|
||||
<Property name="PROP_LST_MaxFanout" value="1000" time="0"/>
|
||||
<Property name="PROP_LST_MuxStyle" value="Auto" time="0"/>
|
||||
<Property name="PROP_LST_NumCriticalPaths" value="" time="0"/>
|
||||
<Property name="PROP_LST_OptimizeGoal" value="Timing" time="0"/>
|
||||
<Property name="PROP_LST_PropagatConst" value="True" time="0"/>
|
||||
<Property name="PROP_LST_RAMStyle" value="Auto" time="0"/>
|
||||
<Property name="PROP_LST_ROMStyle" value="Auto" time="0"/>
|
||||
<Property name="PROP_LST_RemoveDupRegs" value="True" time="0"/>
|
||||
<Property name="PROP_LST_ResolvedMixedDrivers" value="False" time="0"/>
|
||||
<Property name="PROP_LST_ResourceShare" value="True" time="0"/>
|
||||
<Property name="PROP_LST_UseIOReg" value="Auto" time="0"/>
|
||||
<Property name="PROP_LST_UseLPF" value="True" time="0"/>
|
||||
<Property name="PROP_LST_VHDL2008" value="True" time="0"/>
|
||||
<Property name="PROP_MAPSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/>
|
||||
<Property name="PROP_MAPSTA_AutoTiming" value="True" time="0"/>
|
||||
<Property name="PROP_MAPSTA_CheckUnconstrainedConns" value="False" time="0"/>
|
||||
<Property name="PROP_MAPSTA_CheckUnconstrainedPaths" value="False" time="0"/>
|
||||
<Property name="PROP_MAPSTA_FullName" value="False" time="0"/>
|
||||
<Property name="PROP_MAPSTA_NumUnconstrainedPaths" value="0" time="0"/>
|
||||
<Property name="PROP_MAPSTA_ReportStyle" value="Verbose Timing Report" time="0"/>
|
||||
<Property name="PROP_MAPSTA_RouteEstAlogtithm" value="0" time="0"/>
|
||||
<Property name="PROP_MAPSTA_RptAsynTimLoop" value="False" time="0"/>
|
||||
<Property name="PROP_MAPSTA_WordCasePaths" value="1" time="0"/>
|
||||
<Property name="PROP_MAP_IgnorePreErr" value="True" time="0"/>
|
||||
<Property name="PROP_MAP_MAPIORegister" value="Auto" time="0"/>
|
||||
<Property name="PROP_MAP_MAPInferGSR" value="True" time="0"/>
|
||||
<Property name="PROP_MAP_MapModArgs" value="" time="0"/>
|
||||
<Property name="PROP_MAP_OvermapDevice" value="False" time="0"/>
|
||||
<Property name="PROP_MAP_PackLogMapDes" value="" time="0"/>
|
||||
<Property name="PROP_MAP_RegRetiming" value="False" time="0"/>
|
||||
<Property name="PROP_MAP_SigCrossRef" value="False" time="0"/>
|
||||
<Property name="PROP_MAP_SymCrossRef" value="False" time="0"/>
|
||||
<Property name="PROP_MAP_TimingDriven" value="False" time="0"/>
|
||||
<Property name="PROP_MAP_TimingDrivenNodeRep" value="False" time="0"/>
|
||||
<Property name="PROP_MAP_TimingDrivenPack" value="False" time="0"/>
|
||||
<Property name="PROP_PARSTA_AnalysisOption" value="Standard Setup and Hold Analysis" time="0"/>
|
||||
<Property name="PROP_PARSTA_AutoTiming" value="True" time="0"/>
|
||||
<Property name="PROP_PARSTA_CheckUnconstrainedConns" value="False" time="0"/>
|
||||
<Property name="PROP_PARSTA_CheckUnconstrainedPaths" value="False" time="0"/>
|
||||
<Property name="PROP_PARSTA_FullName" value="False" time="0"/>
|
||||
<Property name="PROP_PARSTA_NumUnconstrainedPaths" value="0" time="0"/>
|
||||
<Property name="PROP_PARSTA_ReportStyle" value="Verbose Timing Report" time="0"/>
|
||||
<Property name="PROP_PARSTA_RptAsynTimLoop" value="False" time="0"/>
|
||||
<Property name="PROP_PARSTA_SpeedForHoldAnalysis" value="m" time="0"/>
|
||||
<Property name="PROP_PARSTA_SpeedForSetupAnalysis" value="default" time="0"/>
|
||||
<Property name="PROP_PARSTA_WordCasePaths" value="10" time="0"/>
|
||||
<Property name="PROP_PAR_CrDlyStFileParDes" value="False" time="0"/>
|
||||
<Property name="PROP_PAR_DisableTDParDes" value="False" time="0"/>
|
||||
<Property name="PROP_PAR_EffortParDes" value="5" time="0"/>
|
||||
<Property name="PROP_PAR_MultiSeedSortMode" value="Worst Slack" time="0"/>
|
||||
<Property name="PROP_PAR_NewRouteParDes" value="NBR" time="0"/>
|
||||
<Property name="PROP_PAR_PARClockSkew" value="Off" time="0"/>
|
||||
<Property name="PROP_PAR_PARModArgs" value="" time="0"/>
|
||||
<Property name="PROP_PAR_ParMultiNodeList" value="" time="0"/>
|
||||
<Property name="PROP_PAR_ParRunPlaceOnly" value="False" time="0"/>
|
||||
<Property name="PROP_PAR_PlcIterParDes" value="1" time="0"/>
|
||||
<Property name="PROP_PAR_PlcStCostTblParDes" value="1" time="0"/>
|
||||
<Property name="PROP_PAR_PrefErrorOut" value="True" time="0"/>
|
||||
<Property name="PROP_PAR_RemoveDir" value="True" time="0"/>
|
||||
<Property name="PROP_PAR_RouteDlyRedParDes" value="0" time="0"/>
|
||||
<Property name="PROP_PAR_RoutePassParDes" value="6" time="0"/>
|
||||
<Property name="PROP_PAR_RouteResOptParDes" value="0" time="0"/>
|
||||
<Property name="PROP_PAR_RoutingCDP" value="Auto" time="0"/>
|
||||
<Property name="PROP_PAR_RoutingCDR" value="1" time="0"/>
|
||||
<Property name="PROP_PAR_RunParWithTrce" value="False" time="0"/>
|
||||
<Property name="PROP_PAR_RunTimeReduction" value="True" time="0"/>
|
||||
<Property name="PROP_PAR_SaveBestRsltParDes" value="1" time="0"/>
|
||||
<Property name="PROP_PAR_StopZero" value="False" time="0"/>
|
||||
<Property name="PROP_PAR_parHold" value="On" time="0"/>
|
||||
<Property name="PROP_PAR_parPathBased" value="Off" time="0"/>
|
||||
<Property name="PROP_PRE_CmdLineArgs" value="" time="0"/>
|
||||
<Property name="PROP_PRE_EdfArrayBoundsCase" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfAutoResOfRam" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfClockDomainCross" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfDSPAcrossHie" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfFullCase" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfIgnoreRamRWCol" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfMissConstraint" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfNetFanout" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_EdfParaCase" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfReencodeFSM" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_EdfResSharing" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_EdfTimingViolation" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_EdfUseSafeFSM" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_EdfVlog2001" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_VSynComArea" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_VSynCritcal" value="3" time="0"/>
|
||||
<Property name="PROP_PRE_VSynFSM" value="Auto" time="0"/>
|
||||
<Property name="PROP_PRE_VSynFreq" value="200" time="0"/>
|
||||
<Property name="PROP_PRE_VSynGSR" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_VSynGatedClk" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_VSynIOPad" value="False" time="0"/>
|
||||
<Property name="PROP_PRE_VSynOutNetForm" value="None" time="0"/>
|
||||
<Property name="PROP_PRE_VSynOutPref" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_VSynRepClkFreq" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_VSynRetime" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_VSynTimSum" value="10" time="0"/>
|
||||
<Property name="PROP_PRE_VSynTransform" value="True" time="0"/>
|
||||
<Property name="PROP_PRE_VSyninpd" value="0" time="0"/>
|
||||
<Property name="PROP_PRE_VSynoutd" value="0" time="0"/>
|
||||
<Property name="PROP_SYN_ClockConversion" value="True" time="0"/>
|
||||
<Property name="PROP_SYN_CmdLineArgs" value="" time="0"/>
|
||||
<Property name="PROP_SYN_DisableRegisterRep" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_EdfAllowDUPMod" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_EdfArea" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_EdfArrangeVHDLFiles" value="True" time="0"/>
|
||||
<Property name="PROP_SYN_EdfDefEnumEncode" value="Default" time="0"/>
|
||||
<Property name="PROP_SYN_EdfFanout" value="1000" time="0"/>
|
||||
<Property name="PROP_SYN_EdfFrequency" value="200" time="0"/>
|
||||
<Property name="PROP_SYN_EdfGSR" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_EdfInsertIO" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_EdfNumCritPath" value="" time="0"/>
|
||||
<Property name="PROP_SYN_EdfNumStartEnd" value="" time="0"/>
|
||||
<Property name="PROP_SYN_EdfOutNetForm" value="None" time="0"/>
|
||||
<Property name="PROP_SYN_EdfPushTirstates" value="True" time="0"/>
|
||||
<Property name="PROP_SYN_EdfResSharing" value="True" time="0"/>
|
||||
<Property name="PROP_SYN_EdfRunRetiming" value="Pipelining Only" time="0"/>
|
||||
<Property name="PROP_SYN_EdfSymFSM" value="True" time="0"/>
|
||||
<Property name="PROP_SYN_EdfUnconsClk" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_EdfVerilogInput" value="Verilog 2001" time="0"/>
|
||||
<Property name="PROP_SYN_ExportSetting" value="No" time="0"/>
|
||||
<Property name="PROP_SYN_LibPath" value="" time="0"/>
|
||||
<Property name="PROP_SYN_ResolvedMixedDrivers" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_UpdateCompilePtTimData" value="False" time="0"/>
|
||||
<Property name="PROP_SYN_UseLPF" value="True" time="0"/>
|
||||
<Property name="PROP_SYN_VHDL2008" value="True" time="0"/>
|
||||
<Property name="PROP_THERMAL_DefaultFreq" value="0" time="0"/>
|
||||
<Property name="PROP_TIM_MaxDelSimDes" value="" time="0"/>
|
||||
<Property name="PROP_TIM_MinSpeedGrade" value="False" time="0"/>
|
||||
<Property name="PROP_TIM_ModPreSimDes" value="" time="0"/>
|
||||
<Property name="PROP_TIM_NegStupHldTim" value="True" time="0"/>
|
||||
<Property name="PROP_TIM_TimSimGenPUR" value="True" time="0"/>
|
||||
<Property name="PROP_TIM_TimSimGenX" value="False" time="0"/>
|
||||
<Property name="PROP_TIM_TimSimHierSep" value="" time="0"/>
|
||||
<Property name="PROP_TIM_TransportModeOfPathDelay" value="False" time="0"/>
|
||||
<Property name="PROP_TIM_TrgtSpeedGrade" value="" time="0"/>
|
||||
<Property name="PROP_TIM_WriteVerboseNetlist" value="False" time="0"/>
|
||||
<Property name="PROP_TMCHK_EnableCheck" value="True" time="0"/>
|
||||
</Strategy>
|
@ -1,445 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- top_hwitl.vhd: Top level design for hardware-in-the-loop tests
|
||||
--
|
||||
-- Target: Simulation
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- Author : Markus Koch <markus@notsyncing.net>
|
||||
-- Contributors : None
|
||||
-- License : Mozilla Public License (MPL) Version 2
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
use ieee.math_real.all;
|
||||
|
||||
library trashernet;
|
||||
use trashernet.trashernet_pkg.all;
|
||||
|
||||
entity top_hwitl is
|
||||
port(
|
||||
clk : in std_logic;
|
||||
rst_n : in std_logic;
|
||||
rx_p : in std_logic;
|
||||
tx_p : out std_logic;
|
||||
tx_n : out std_logic;
|
||||
led_n : out std_logic_vector(7 downto 0); -- @suppress: Used in different architectures
|
||||
button_n : in std_logic_vector(3 downto 0); -- @suppress: Used in different architectures
|
||||
debug_data : out std_logic_vector(7 downto 0) -- @suppress: Used in different architectures
|
||||
);
|
||||
end entity top_hwitl;
|
||||
|
||||
architecture mac of top_hwitl is
|
||||
component pll0
|
||||
port(
|
||||
CLK : in std_logic;
|
||||
CLKOP : out std_logic;
|
||||
LOCK : out std_logic
|
||||
);
|
||||
end component pll0;
|
||||
|
||||
constant F_CLK : integer := 50000000;
|
||||
constant F_CLK_PHY : integer := 140000000;
|
||||
|
||||
constant ETH_CONFIG : configuration_t := (
|
||||
mac_address => (x"00", x"ff", x"ff", x"11", x"22", x"33")
|
||||
);
|
||||
|
||||
signal rst : std_logic;
|
||||
signal clk_phy : std_logic;
|
||||
|
||||
signal phy_out : phy_out_t;
|
||||
signal phy_in : phy_in_t;
|
||||
|
||||
signal mac_out : mac_out_t;
|
||||
signal mac_in : mac_in_t;
|
||||
|
||||
type status_t is (IDLE, TX);
|
||||
signal state : status_t;
|
||||
constant BYTE_CNT_MAX : integer := 100;
|
||||
signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
|
||||
|
||||
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
|
||||
signal tmo : integer range 0 to TMO_MAX;
|
||||
begin
|
||||
trashernet_mac_inst : entity trashernet.trashernet_mac
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
mac_out => mac_out,
|
||||
mac_in => mac_in
|
||||
);
|
||||
|
||||
pll0_inst : pll0
|
||||
port map(
|
||||
CLK => clk,
|
||||
CLKOP => clk_phy,
|
||||
LOCK => open
|
||||
);
|
||||
|
||||
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
|
||||
generic map(
|
||||
F_CLK => F_CLK,
|
||||
F_CLK_PHY => F_CLK_PHY
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
phy_clk => clk_phy,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
reset_sync : process(clk, rst_n) is
|
||||
begin
|
||||
if (rst_n = '0') then
|
||||
rst <= '1';
|
||||
elsif (rising_edge(clk)) then
|
||||
rst <= '0';
|
||||
end if;
|
||||
end process reset_sync;
|
||||
|
||||
receiver : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= IDLE;
|
||||
mac_in.tx_mac_data_en <= '0';
|
||||
tmo <= 0;
|
||||
elsif rising_edge(clk) then
|
||||
if (tmo /= 0) then
|
||||
tmo <= tmo - 1;
|
||||
end if;
|
||||
case state is
|
||||
when IDLE =>
|
||||
if mac_out.rx_mac_crc_ok then
|
||||
if (mac_out.rx_header.mac_destination = ETH_CONFIG.mac_address) then -- Note: We won't respond to broadcasts!
|
||||
state <= TX;
|
||||
byte_cnt <= BYTE_CNT_MAX;
|
||||
mac_in.tx_header.mac_destination <= mac_out.rx_header.mac_source;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
if tmo = 0 then
|
||||
state <= TX;
|
||||
byte_cnt <= BYTE_CNT_MAX;
|
||||
mac_in.tx_header.mac_destination <= (others => x"FF");
|
||||
end if;
|
||||
when TX =>
|
||||
tmo <= TMO_MAX;
|
||||
mac_in.tx_header.mac_ethertype <= (x"00", std_logic_vector(to_unsigned(BYTE_CNT_MAX, 8)));
|
||||
mac_in.tx_mac_data_en <= '1';
|
||||
mac_in.tx_mac_data <= std_logic_vector(to_unsigned(byte_cnt, 8));
|
||||
if mac_out.tx_mac_data_ack then
|
||||
if byte_cnt = 1 then
|
||||
mac_in.tx_mac_data_en <= '0';
|
||||
state <= IDLE;
|
||||
else
|
||||
byte_cnt <= byte_cnt - 1;
|
||||
end if;
|
||||
end if;
|
||||
end case;
|
||||
|
||||
end if;
|
||||
end process receiver;
|
||||
|
||||
mac_in.tx_header.mac_source <= ETH_CONFIG.mac_address;
|
||||
|
||||
debug_data(0) <= tx_p;
|
||||
debug_data(1) <= tx_n;
|
||||
|
||||
end architecture mac;
|
||||
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- top_hwitl (eth)
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
use ieee.math_real.all;
|
||||
|
||||
library trashernet;
|
||||
use trashernet.trashernet_pkg.all;
|
||||
|
||||
architecture eth of top_hwitl is
|
||||
component pll0
|
||||
port(
|
||||
CLK : in std_logic;
|
||||
CLKOP : out std_logic;
|
||||
LOCK : out std_logic
|
||||
);
|
||||
end component pll0;
|
||||
|
||||
constant F_CLK : integer := 50000000;
|
||||
constant F_CLK_PHY : integer := 140000000;
|
||||
|
||||
constant ETH_CONFIG : configuration_t := (
|
||||
mac_address => (x"00", x"FF", x"FF", x"11", x"22", x"33")
|
||||
);
|
||||
constant IP_CONFIG : ipv4_configuration_t := (
|
||||
ip_address => (x"C0", x"A8", x"02", x"02"),
|
||||
subnet_mask => (x"FF", x"FF", x"FF", x"00"),
|
||||
gateway => (x"C0", x"A8", x"02", x"01")
|
||||
);
|
||||
|
||||
signal rst : std_logic;
|
||||
signal clk_phy : std_logic;
|
||||
|
||||
signal phy_out : phy_out_t;
|
||||
signal phy_in : phy_in_t;
|
||||
|
||||
signal mac_out : mac_out_t;
|
||||
signal mac_in : mac_in_t;
|
||||
|
||||
constant PROT_ARP : integer := 0;
|
||||
constant PROT_IPV4 : integer := 1;
|
||||
constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := (
|
||||
PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
|
||||
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
|
||||
);
|
||||
signal ethernet_i_out : ethernet_i_out_t;
|
||||
signal ethernet_i_in : ethernet_i_in_t;
|
||||
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
|
||||
signal arp_out : arp_out_t;
|
||||
signal arp_in : arp_in_t;
|
||||
|
||||
signal ipv4_out : ipv4_out_t;
|
||||
signal ipv4_in : ipv4_in_t;
|
||||
|
||||
constant IPROT_ICMP : integer := 0;
|
||||
constant IPROT_UDP : integer := 1;
|
||||
constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
|
||||
IPROT_ICMP => IPV4_PROTOCOL_ICMP,
|
||||
IPROT_UDP => IPV4_PROTOCOL_UDP
|
||||
);
|
||||
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
|
||||
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
|
||||
begin
|
||||
trashernet_icmp_inst : entity trashernet.trashernet_icmp
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
ipv4_protocol_out => ipv4_protocol_out(IPROT_ICMP),
|
||||
ipv4_protocol_in => ipv4_protocol_in(IPROT_ICMP)
|
||||
);
|
||||
|
||||
trashernet_ipv4prot_inst : entity trashernet.trashernet_ipv4prot
|
||||
generic map(
|
||||
IPV4_PROTOCOLS => IPV4_PROTOCOLS
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
ipv4_out => ipv4_out,
|
||||
ipv4_in => ipv4_in,
|
||||
ipv4_protocol_out => ipv4_protocol_out,
|
||||
ipv4_protocol_in => ipv4_protocol_in
|
||||
);
|
||||
|
||||
trashernet_ipv4_inst : entity trashernet.trashernet_ipv4
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
ipv4_config => IP_CONFIG,
|
||||
arp_out => arp_out,
|
||||
arp_in => arp_in,
|
||||
ethernet_ii_out => ethernet_ii_out(PROT_IPV4),
|
||||
ethernet_ii_in => ethernet_ii_in(PROT_IPV4),
|
||||
ipv4_out => ipv4_out,
|
||||
ipv4_in => ipv4_in
|
||||
);
|
||||
|
||||
trashernet_arp_inst : entity trashernet.trashernet_arp
|
||||
generic map(
|
||||
SYSTICK_FREQ => F_CLK
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
systick => '1',
|
||||
mac_config => ETH_CONFIG,
|
||||
ip_config => IP_CONFIG,
|
||||
arp_out => arp_out,
|
||||
arp_in => arp_in,
|
||||
ethernet_ii_out => ethernet_ii_out(PROT_ARP),
|
||||
ethernet_ii_in => ethernet_ii_in(PROT_ARP)
|
||||
);
|
||||
|
||||
ethernet_i_in <= ethernet_i_in_t'(
|
||||
tx_mac_address => (others => (others => '-')),
|
||||
tx_data => (others => '-'),
|
||||
tx_en => '0',
|
||||
tx_length => (others => '-')
|
||||
);
|
||||
|
||||
trashernet_eth_inst : entity trashernet.trashernet_eth
|
||||
generic map(
|
||||
ETHERNET_II_PROTOCOLS => ETHERNET_II_PROTOCOLS
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
mac_out => mac_out,
|
||||
mac_in => mac_in,
|
||||
config => ETH_CONFIG,
|
||||
ethernet_i_out => ethernet_i_out,
|
||||
ethernet_i_in => ethernet_i_in,
|
||||
ethernet_ii_out => ethernet_ii_out,
|
||||
ethernet_ii_in => ethernet_ii_in
|
||||
);
|
||||
|
||||
trashernet_mac_inst : entity trashernet.trashernet_mac
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
mac_out => mac_out,
|
||||
mac_in => mac_in
|
||||
);
|
||||
|
||||
pll0_inst : pll0
|
||||
port map(
|
||||
CLK => clk,
|
||||
CLKOP => clk_phy,
|
||||
LOCK => open
|
||||
);
|
||||
|
||||
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
|
||||
generic map(
|
||||
F_CLK => F_CLK,
|
||||
F_CLK_PHY => F_CLK_PHY
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
phy_clk => clk_phy,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
reset_sync : process(clk, rst_n) is
|
||||
begin
|
||||
if (rst_n = '0') then
|
||||
rst <= '1';
|
||||
elsif (rising_edge(clk)) then
|
||||
rst <= '0';
|
||||
end if;
|
||||
end process reset_sync;
|
||||
|
||||
receiver : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
if ethernet_i_out.rx_header_rcv then
|
||||
report "RX ETH-I: " & to_string(to_integer(ethernet_i_out.rx_length));
|
||||
end if;
|
||||
if (ethernet_ii_out(PROT_ARP).rx_header_rcv) then
|
||||
report "RX ARP";
|
||||
end if;
|
||||
|
||||
if ipv4_out.rx_ok_stb then
|
||||
report "RX IP OK";
|
||||
end if;
|
||||
if ipv4_out.rx_error_stb then
|
||||
report "RX IP error";
|
||||
end if;
|
||||
if ipv4_out.rx_header_rcv then
|
||||
report "RX IP start. PROT=" & to_hstring(ipv4_out.rx_protocol) & ", FROM=" & ip_to_string(ipv4_out.rx_ip_address);
|
||||
end if;
|
||||
end if;
|
||||
end process receiver;
|
||||
|
||||
udp : process(clk) is
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if ipv4_protocol_out(IPROT_UDP).rx_header_rcv then
|
||||
report "Receive UDP";
|
||||
end if;
|
||||
end if;
|
||||
end process udp;
|
||||
|
||||
tx_udp_p : block
|
||||
type state_t is (IDLE, TX, DONE);
|
||||
signal state : state_t;
|
||||
|
||||
constant PACKET : byte_vector := (
|
||||
x"10", x"00", -- Source port
|
||||
x"04", x"00", -- Destination port
|
||||
x"00", x"15", -- Length
|
||||
x"00", x"00", -- Checksum (not used)
|
||||
x"48", x"65", x"6C", x"6C", x"6F", x"20", x"57", x"6F", x"72", x"6C", x"64", x"21", x"0a" -- Payload
|
||||
);
|
||||
signal sr : byte_vector(PACKET'range);
|
||||
signal cnt : integer range 0 to sr'length - 1;
|
||||
|
||||
begin
|
||||
tx_udp : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= IDLE;
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
case state is
|
||||
when IDLE =>
|
||||
if button_n(0) = '0' then
|
||||
state <= TX;
|
||||
sr <= PACKET;
|
||||
cnt <= sr'length - 1;
|
||||
report "UDP: Start TX";
|
||||
end if;
|
||||
|
||||
when TX =>
|
||||
if ipv4_protocol_out(IPROT_UDP).tx_data_ack then
|
||||
sr <= sr(sr'low + 1 to sr'high) & x"00";
|
||||
if cnt = 0 then
|
||||
state <= DONE;
|
||||
else
|
||||
cnt <= cnt - 1;
|
||||
end if;
|
||||
end if;
|
||||
if ipv4_protocol_out(IPROT_UDP).tx_err_stb then
|
||||
report "UDP: TX ERROR";
|
||||
state <= DONE;
|
||||
end if;
|
||||
if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then
|
||||
report "UDP: TX OK stb in TX state -- that shouldn't happen, right?";
|
||||
end if;
|
||||
|
||||
when DONE =>
|
||||
null;
|
||||
|
||||
end case;
|
||||
end if;
|
||||
end process tx_udp;
|
||||
ipv4_protocol_in(IPROT_UDP).tx_en <= '1' when state = TX else '0';
|
||||
ipv4_protocol_in(IPROT_UDP).tx_ip_address <= (x"C0", x"A8", x"02", x"01");
|
||||
ipv4_protocol_in(IPROT_UDP).tx_length <= to_unsigned(sr'length, 16);
|
||||
ipv4_protocol_in(IPROT_UDP).tx_data <= sr(0);
|
||||
end block tx_udp_p;
|
||||
|
||||
icmp : process(clk) is
|
||||
begin
|
||||
if rising_edge(clk) then
|
||||
if ipv4_protocol_out(IPROT_ICMP).rx_header_rcv then
|
||||
report "Receive ICMP";
|
||||
end if;
|
||||
end if;
|
||||
end process icmp;
|
||||
|
||||
end architecture eth;
|
@ -1,248 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- top_mac_test.vhd: Test design to verify the functionality of the MAC core
|
||||
-- Sends a broadcast with 100 decrementing numbers every 250 ms or when a
|
||||
-- button is pushed. It will also directly reply with that content to any
|
||||
-- packet addressed to us.
|
||||
--
|
||||
-- Target: Lattice Brevia 2 (LFXP2-5E)
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- Author : Markus Koch <markus@notsyncing.net>
|
||||
-- Contributors : None
|
||||
-- License : Mozilla Public License (MPL) Version 2
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
use ieee.math_real.all;
|
||||
|
||||
library trashernet;
|
||||
use trashernet.trashernet_pkg.all;
|
||||
|
||||
entity top_pingable is
|
||||
port(
|
||||
clk : in std_logic;
|
||||
rst_n : in std_logic;
|
||||
rx_p : in std_logic;
|
||||
tx_p : out std_logic;
|
||||
tx_n : out std_logic;
|
||||
led_n : out std_logic_vector(7 downto 0);
|
||||
button_n : in std_logic_vector(3 downto 0);
|
||||
debug_data : out std_logic_vector(7 downto 0)
|
||||
);
|
||||
end entity top_pingable;
|
||||
|
||||
architecture rtl of top_pingable is
|
||||
component pll0
|
||||
port(
|
||||
CLK : in std_logic;
|
||||
CLKOP : out std_logic;
|
||||
LOCK : out std_logic
|
||||
);
|
||||
end component pll0;
|
||||
|
||||
constant F_CLK : integer := 50000000;
|
||||
constant F_CLK_PHY : integer := 140000000;
|
||||
|
||||
constant LED_BLINK : boolean_vector(led_n'range) := (
|
||||
6 downto 2 => true,
|
||||
others => false
|
||||
);
|
||||
|
||||
constant ETH_CONFIG : configuration_t := (
|
||||
mac_address => (x"00", x"FF", x"FF", x"11", x"22", x"44")
|
||||
);
|
||||
constant IP_CONFIG : ipv4_configuration_t := (
|
||||
ip_address => (x"C0", x"A8", x"02", x"02"),
|
||||
subnet_mask => (x"FF", x"FF", x"FF", x"00"),
|
||||
gateway => (x"C0", x"A8", x"02", x"01")
|
||||
);
|
||||
|
||||
signal rst : std_logic;
|
||||
signal clk_phy : std_logic;
|
||||
signal phy_pll_lock : std_logic;
|
||||
|
||||
signal led_sig : std_logic_vector(led_n'range);
|
||||
|
||||
signal phy_out : phy_out_t;
|
||||
signal phy_in : phy_in_t;
|
||||
|
||||
signal mac_out : mac_out_t;
|
||||
signal mac_in : mac_in_t;
|
||||
|
||||
constant PROT_ARP : integer := 0;
|
||||
constant PROT_IPV4 : integer := 1;
|
||||
constant ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := (
|
||||
PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
|
||||
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
|
||||
);
|
||||
signal ethernet_i_out : ethernet_i_out_t;
|
||||
signal ethernet_i_in : ethernet_i_in_t;
|
||||
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
|
||||
|
||||
signal arp_out : arp_out_t;
|
||||
signal arp_in : arp_in_t;
|
||||
|
||||
signal ipv4_out : ipv4_out_t;
|
||||
signal ipv4_in : ipv4_in_t;
|
||||
|
||||
constant IPROT_ICMP : integer := 0;
|
||||
constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
|
||||
IPROT_ICMP => IPV4_PROTOCOL_ICMP
|
||||
);
|
||||
signal ipv4_protocol_out : ipv4_protocol_out_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 : std_logic_vector(button_n'range);
|
||||
begin
|
||||
trashernet_icmp_inst : entity trashernet.trashernet_icmp
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
ipv4_protocol_out => ipv4_protocol_out(IPROT_ICMP),
|
||||
ipv4_protocol_in => ipv4_protocol_in(IPROT_ICMP)
|
||||
);
|
||||
|
||||
trashernet_ipv4prot_inst : entity trashernet.trashernet_ipv4prot
|
||||
generic map(
|
||||
IPV4_PROTOCOLS => IPV4_PROTOCOLS
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
ipv4_out => ipv4_out,
|
||||
ipv4_in => ipv4_in,
|
||||
ipv4_protocol_out => ipv4_protocol_out,
|
||||
ipv4_protocol_in => ipv4_protocol_in
|
||||
);
|
||||
|
||||
trashernet_ipv4_inst : entity trashernet.trashernet_ipv4
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
ipv4_config => IP_CONFIG,
|
||||
arp_out => arp_out,
|
||||
arp_in => arp_in,
|
||||
ethernet_ii_out => ethernet_ii_out(PROT_IPV4),
|
||||
ethernet_ii_in => ethernet_ii_in(PROT_IPV4),
|
||||
ipv4_out => ipv4_out,
|
||||
ipv4_in => ipv4_in
|
||||
);
|
||||
|
||||
trashernet_arp_inst : entity trashernet.trashernet_arp
|
||||
generic map(
|
||||
SYSTICK_FREQ => F_CLK
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
systick => '1',
|
||||
mac_config => ETH_CONFIG,
|
||||
ip_config => IP_CONFIG,
|
||||
arp_out => arp_out,
|
||||
arp_in => arp_in,
|
||||
ethernet_ii_out => ethernet_ii_out(PROT_ARP),
|
||||
ethernet_ii_in => ethernet_ii_in(PROT_ARP)
|
||||
);
|
||||
|
||||
ethernet_i_in <= ETHERNET_I_IN_UNUSED;
|
||||
|
||||
trashernet_eth_inst : entity trashernet.trashernet_eth
|
||||
generic map(
|
||||
ETHERNET_II_PROTOCOLS => ETHERNET_II_PROTOCOLS
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
mac_out => mac_out,
|
||||
mac_in => mac_in,
|
||||
config => ETH_CONFIG,
|
||||
ethernet_i_out => ethernet_i_out,
|
||||
ethernet_i_in => ethernet_i_in,
|
||||
ethernet_ii_out => ethernet_ii_out,
|
||||
ethernet_ii_in => ethernet_ii_in
|
||||
);
|
||||
|
||||
trashernet_mac_inst : entity trashernet.trashernet_mac
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
mac_out => mac_out,
|
||||
mac_in => mac_in
|
||||
);
|
||||
|
||||
pll0_inst : pll0
|
||||
port map(
|
||||
CLK => clk,
|
||||
CLKOP => clk_phy,
|
||||
LOCK => phy_pll_lock
|
||||
);
|
||||
|
||||
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
|
||||
generic map(
|
||||
F_CLK => F_CLK,
|
||||
F_CLK_PHY => F_CLK_PHY
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
phy_clk => clk_phy,
|
||||
rst => rst,
|
||||
phy_out => phy_out,
|
||||
phy_in => phy_in,
|
||||
rx_p => rx_p,
|
||||
tx_p => tx_p,
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
reset_sync : process(clk, rst_n) is
|
||||
begin
|
||||
if (rst_n = '0') then
|
||||
rst <= '1';
|
||||
elsif (rising_edge(clk)) then
|
||||
rst <= '0';
|
||||
end if;
|
||||
end process reset_sync;
|
||||
|
||||
ledcon_inst : entity work.ledcon
|
||||
generic map(
|
||||
F_CLK => F_CLK,
|
||||
BLINK => LED_BLINK
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
sig => led_sig,
|
||||
led_n => led_n
|
||||
);
|
||||
|
||||
led_sig <= (
|
||||
0 => phy_pll_lock,
|
||||
1 => phy_out.carrier_detect,
|
||||
2 => phy_out.rx_active,
|
||||
3 => phy_out.rx_error,
|
||||
4 => mac_out.rx_mac_crc_ok,
|
||||
5 => mac_out.rx_mac_crc_error,
|
||||
6 => mac_out.tx_active,
|
||||
7 => '0'
|
||||
);
|
||||
|
||||
synchronizer_inst : entity trashernet.synchronizer
|
||||
generic map(
|
||||
SIZE => 2
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
data_in => button_n(0),
|
||||
data_out => button_n_sync(0)
|
||||
);
|
||||
|
||||
button <= not button_n_sync;
|
||||
end architecture rtl;
|
File diff suppressed because it is too large
Load Diff
9
micobu.sh
Executable file
9
micobu.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd cocotb
|
||||
make
|
||||
RET=$?
|
||||
|
||||
mv ./* -t $MCB_ARTEFACTS/
|
||||
|
||||
exit $RET
|
@ -1,98 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- fifo.vhd : Basic single-clock FIFO
|
||||
-- Implements a basic single-clock FIFO
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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;
|
||||
|
||||
entity fifo is
|
||||
generic(
|
||||
DEPTH : natural -- Number of elements the FIFO can hold
|
||||
);
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- FIFO
|
||||
clear : in std_logic; -- Synchronous reset (clear FIFO)
|
||||
data_in : in std_logic_vector; -- Data into the FIFO (automatically constrains width)
|
||||
push : in std_logic; -- Push `data_in` into the FIFO
|
||||
full : out std_logic; -- No further elements can be pushed into the FIFO
|
||||
data_out : out std_logic_vector; -- Data out of the FIFO
|
||||
pop : in std_logic; -- Get an element from the FIFO
|
||||
empty : out std_logic -- FIFO is empty
|
||||
);
|
||||
end entity fifo;
|
||||
|
||||
architecture rtl of fifo is
|
||||
type memory_t is array (natural range <>) of std_logic_vector(data_in'range);
|
||||
signal memory : memory_t(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;
|
||||
|
||||
signal read_pointer : memory_pointer_t;
|
||||
signal write_pointer : memory_pointer_t;
|
||||
signal usage_counter : usage_counter_t;
|
||||
begin
|
||||
fifo_proc : process(clk, rst) is
|
||||
procedure increment_pointer(signal pointer : inout memory_pointer_t) is
|
||||
begin
|
||||
if pointer = pointer'subtype'high then
|
||||
pointer <= 0;
|
||||
else
|
||||
pointer <= pointer + 1;
|
||||
end if;
|
||||
end procedure increment_pointer;
|
||||
|
||||
variable pushed : boolean;
|
||||
variable popped : boolean;
|
||||
begin
|
||||
if rst then
|
||||
read_pointer <= 0;
|
||||
write_pointer <= 0;
|
||||
usage_counter <= 0;
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
pushed := false;
|
||||
popped := false;
|
||||
|
||||
if push and not full then
|
||||
memory(write_pointer) <= data_in;
|
||||
increment_pointer(write_pointer);
|
||||
usage_counter <= usage_counter + 1;
|
||||
pushed := true;
|
||||
end if;
|
||||
|
||||
if pop and not empty then
|
||||
increment_pointer(read_pointer);
|
||||
usage_counter <= usage_counter - 1;
|
||||
popped := true;
|
||||
end if;
|
||||
|
||||
if pushed and popped then
|
||||
usage_counter <= usage_counter;
|
||||
end if;
|
||||
|
||||
if clear then
|
||||
read_pointer <= 0;
|
||||
write_pointer <= 0;
|
||||
usage_counter <= 0;
|
||||
end if;
|
||||
end if;
|
||||
end process fifo_proc;
|
||||
|
||||
data_out <= memory(read_pointer);
|
||||
empty <= '1' when usage_counter = 0 else '0';
|
||||
full <= '1' when usage_counter = DEPTH else '0';
|
||||
|
||||
end architecture rtl;
|
@ -1,64 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- timer.vhd : Simple Timer
|
||||
-- Implements a very simple integer timer
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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;
|
||||
|
||||
entity timer is
|
||||
generic(
|
||||
F_TICK : integer; -- Frequency of `tick` (or `clk` if tick is tied to '1')
|
||||
DURATION : time; -- Duration of the timer
|
||||
AUTOSTART : boolean := false -- Automatically start the timer after reset
|
||||
);
|
||||
port(
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
tick : in std_logic; -- Timer enable
|
||||
start : in std_logic; -- (Re-)start timer
|
||||
|
||||
expired : out std_logic; -- Timer is expired
|
||||
expired_stb : out std_logic -- Strobe when timer reaches zero
|
||||
);
|
||||
end entity timer;
|
||||
|
||||
architecture rtl of timer is
|
||||
constant PERIOD : time := 1 sec / real(F_TICK);
|
||||
constant TICKS : integer := integer(DURATION / PERIOD);
|
||||
|
||||
signal counter : integer range 0 to TICKS;
|
||||
begin
|
||||
timer_p : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
counter <= TICKS when AUTOSTART else 0;
|
||||
expired_stb <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
expired_stb <= '0';
|
||||
if tick then
|
||||
if counter = 1 then
|
||||
expired_stb <= '1';
|
||||
end if;
|
||||
if counter /= 0 then
|
||||
counter <= counter - 1;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
if start then
|
||||
counter <= TICKS;
|
||||
end if;
|
||||
end if;
|
||||
end process timer_p;
|
||||
|
||||
expired <= not start when counter = 0 else '0';
|
||||
end architecture rtl;
|
@ -1,317 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_arp.vhd : Address Resolution Protocol
|
||||
-- Implements simple single-entry cache ARP.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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_arp is
|
||||
generic(
|
||||
SYSTICK_FREQ : integer
|
||||
);
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
systick : in std_logic; -- Global time reference for slow events (here: timeout)
|
||||
|
||||
-- Configuration
|
||||
mac_config : in configuration_t; -- Trashernet MAC configuration
|
||||
ip_config : in ipv4_configuration_t; -- Trashernet IP configuration
|
||||
|
||||
-- ARP application interface
|
||||
arp_out : out arp_out_t; -- ARP IF (out from ARP)
|
||||
arp_in : in arp_in_t; -- ARP IF (into ARP)
|
||||
|
||||
-- Ethernet II application interface
|
||||
ethernet_ii_out : in ethernet_ii_out_t; -- Ethernet II IF (out from MAC)
|
||||
ethernet_ii_in : out ethernet_ii_in_t -- Ethernet II IF (into MAC)
|
||||
);
|
||||
end entity trashernet_arp;
|
||||
|
||||
architecture rtl of trashernet_arp is
|
||||
constant HTYPE : byte_vector := (x"00", x"01");
|
||||
constant PTYPE : byte_vector := (x"08", x"00");
|
||||
constant HLEN : byte := x"06";
|
||||
constant PLEN : byte := x"04";
|
||||
constant OPER_REQUEST : byte_vector := (x"00", x"01");
|
||||
constant OPER_REPLY : byte_vector := (x"00", x"02");
|
||||
|
||||
constant ARP_TIMEOUT : time := 1 sec;
|
||||
|
||||
signal arp_tx_reply_rq : std_logic; -- Request to transmit an ARP reply
|
||||
signal arp_tx_reply_tha : mac_addr_t; -- Target MAC to use in reply (SHA in received packet)
|
||||
signal arp_tx_reply_tpa : ip_addr_t; -- Target IP to use in reply (SPA in received packet)
|
||||
signal arp_tx_reply_ack : std_logic; -- ARP reply has been latched and is being sent
|
||||
|
||||
signal arp_tx_request_rq : std_logic; -- Request to transmit an ARP request
|
||||
signal arp_tx_request_tpa : ip_addr_t; -- IP address that we want to know the MAC address of
|
||||
signal arp_tx_request_ack : std_logic; -- ARP request has been latched and is being sent
|
||||
|
||||
signal arp_rx_reply_stb : std_logic; -- An ARP reply was received (strobe)
|
||||
signal arp_rx_sha : mac_addr_t; -- The MAC address of the reply sender, valid with arp_rx_reply
|
||||
signal arp_rx_spa : ip_addr_t; -- The IP address of the reply sender, valid with arp_rx_reply
|
||||
|
||||
begin
|
||||
resolver : block
|
||||
type resolver_state_t is (IDLE, QUERY_MAC);
|
||||
signal state : resolver_state_t;
|
||||
|
||||
signal query_mac_found : std_logic;
|
||||
signal query_timeout : std_logic := '0'; -- TODO: Implement
|
||||
|
||||
signal replied_ip : ip_addr_t;
|
||||
signal replied_mac : mac_addr_t;
|
||||
begin
|
||||
|
||||
arp_resolver_main : process(rst, clk) is
|
||||
begin
|
||||
if rst then
|
||||
state <= IDLE;
|
||||
replied_ip <= (others => x"00"); -- 0.0.0.0
|
||||
arp_out.arp_ok_stb <= '0';
|
||||
arp_out.arp_fail_stb <= '0';
|
||||
arp_tx_request_rq <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
arp_out.arp_ok_stb <= '0';
|
||||
arp_out.arp_fail_stb <= '0';
|
||||
|
||||
if arp_rx_reply_stb then
|
||||
if arp_tx_request_tpa = arp_rx_spa then -- If this reply is actually for our request
|
||||
replied_mac <= arp_rx_sha;
|
||||
replied_ip <= arp_rx_spa;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
case state is
|
||||
when IDLE =>
|
||||
if arp_in.arp_query_stb then
|
||||
if query_mac_found then
|
||||
arp_out.arp_ok_stb <= '1';
|
||||
else
|
||||
arp_tx_request_rq <= '1';
|
||||
state <= QUERY_MAC;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when QUERY_MAC =>
|
||||
if arp_tx_request_ack then
|
||||
arp_tx_request_rq <= '0';
|
||||
end if;
|
||||
if query_mac_found or query_timeout then
|
||||
arp_out.arp_ok_stb <= query_mac_found;
|
||||
arp_out.arp_fail_stb <= not query_mac_found;
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process arp_resolver_main;
|
||||
query_mac_found <= '1' when (arp_tx_request_tpa = replied_ip) else '0';
|
||||
|
||||
arp_out.arp_mac <= replied_mac;
|
||||
arp_tx_request_tpa <= arp_in.arp_ip;
|
||||
|
||||
timeout_timer_inst : entity work.timer
|
||||
generic map(
|
||||
F_TICK => SYSTICK_FREQ,
|
||||
DURATION => ARP_TIMEOUT,
|
||||
AUTOSTART => false
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
tick => systick,
|
||||
start => arp_tx_request_rq,
|
||||
expired => query_timeout,
|
||||
expired_stb => open
|
||||
);
|
||||
|
||||
end block resolver;
|
||||
|
||||
rx : block
|
||||
constant BYTECOUNT_HEAD : integer := 8; -- HTYPE -> OPER
|
||||
constant BYTECOUNT_ADDRESSES : integer := 20; -- SHA -> TPA
|
||||
|
||||
signal sr : byte_vector(0 to BYTECOUNT_ADDRESSES - 1);
|
||||
|
||||
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_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_plen is sr(SR_HEAD_OFFSET + 5);
|
||||
alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7);
|
||||
|
||||
constant SR_ADDRESSES_OFFSET : integer := -8;
|
||||
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_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);
|
||||
|
||||
type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE);
|
||||
signal state : state_t;
|
||||
signal bytecount : integer range 0 to BYTECOUNT_ADDRESSES;
|
||||
signal shifted : std_logic;
|
||||
|
||||
signal block_done : std_logic;
|
||||
|
||||
signal header_ok : std_logic;
|
||||
signal we_are_asked : std_logic;
|
||||
|
||||
signal is_request : std_logic;
|
||||
begin
|
||||
|
||||
block_done <= '1' when shifted = '1' and bytecount = 0 else '0';
|
||||
|
||||
header_ok <= '1' when --
|
||||
sr_head_htype = HTYPE and --
|
||||
sr_head_ptype = PTYPE and --
|
||||
sr_head_hlen = HLEN and sr_head_plen = PLEN --
|
||||
else
|
||||
'0';
|
||||
|
||||
we_are_asked <= '1' when --
|
||||
sr_addresses_tpa = ip_config.ip_address else
|
||||
'0';
|
||||
|
||||
rx_fsm : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= HEAD;
|
||||
shifted <= '0';
|
||||
bytecount <= BYTECOUNT_HEAD;
|
||||
arp_rx_reply_stb <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
arp_rx_reply_stb <= '0';
|
||||
|
||||
if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then
|
||||
state <= HEAD; -- Safe return from any state, may be overridden below
|
||||
bytecount <= BYTECOUNT_HEAD;
|
||||
end if;
|
||||
shifted <= ethernet_ii_out.rx_data_valid;
|
||||
if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then
|
||||
sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data;
|
||||
bytecount <= bytecount - 1;
|
||||
end if;
|
||||
case state is
|
||||
when HEAD =>
|
||||
if block_done then
|
||||
if header_ok then
|
||||
bytecount <= BYTECOUNT_ADDRESSES;
|
||||
state <= ADDRESSES;
|
||||
is_request <= '1' when (sr_head_oper = OPER_REQUEST) else '0';
|
||||
else
|
||||
state <= IGNORE;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when ADDRESSES =>
|
||||
if block_done then
|
||||
if (we_are_asked) then
|
||||
state <= WAITCRC;
|
||||
else
|
||||
state <= IGNORE;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when WAITCRC =>
|
||||
if ethernet_ii_out.rx_crc_ok then
|
||||
if is_request then
|
||||
state <= SENDREPLY;
|
||||
else
|
||||
arp_rx_reply_stb <= '1';
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when SENDREPLY =>
|
||||
if arp_tx_reply_ack then
|
||||
bytecount <= BYTECOUNT_HEAD;
|
||||
state <= HEAD;
|
||||
end if;
|
||||
|
||||
when IGNORE => -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err)
|
||||
null;
|
||||
end case;
|
||||
end if;
|
||||
end process rx_fsm;
|
||||
arp_tx_reply_rq <= '1' when state = SENDREPLY else '0';
|
||||
arp_tx_reply_tha <= sr_addresses_sha;
|
||||
arp_tx_reply_tpa <= sr_addresses_spa;
|
||||
arp_rx_sha <= sr_addresses_sha;
|
||||
arp_rx_spa <= sr_addresses_spa;
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
signal sr : byte_vector(0 to 27);
|
||||
signal arp_reply_preload : byte_vector(sr'range);
|
||||
signal arp_request_preload : byte_vector(sr'range);
|
||||
constant BYTECOUNT_MAX : integer := sr'length + 1;
|
||||
signal bytecount : integer range 0 to BYTECOUNT_MAX;
|
||||
|
||||
type state_t is (IDLE, TRANSMIT);
|
||||
signal state : state_t;
|
||||
begin
|
||||
|
||||
arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header
|
||||
mac_config.mac_address & ip_config.ip_address & -- Sender
|
||||
arp_tx_reply_tha & arp_tx_reply_tpa; -- Target
|
||||
|
||||
arp_request_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REQUEST & -- Header
|
||||
mac_config.mac_address & ip_config.ip_address & -- Sender
|
||||
mac_addr_t'(x"00", x"00", x"00", x"00", x"00", x"00") & arp_tx_request_tpa; -- Target
|
||||
|
||||
tx_fsm : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
arp_tx_reply_ack <= '0';
|
||||
arp_tx_request_ack <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
arp_tx_reply_ack <= '0';
|
||||
arp_tx_request_ack <= '0';
|
||||
|
||||
if (ethernet_ii_out.tx_data_ack = '1') and (bytecount > 0) then
|
||||
sr <= sr(sr'low + 1 to sr'high) & x"00";
|
||||
bytecount <= bytecount - 1;
|
||||
end if;
|
||||
|
||||
case state is
|
||||
when IDLE =>
|
||||
if arp_tx_reply_rq then
|
||||
state <= TRANSMIT;
|
||||
arp_tx_reply_ack <= '1';
|
||||
sr <= arp_reply_preload;
|
||||
ethernet_ii_in.tx_mac_address <= arp_tx_reply_tha;
|
||||
elsif arp_tx_request_rq then
|
||||
state <= TRANSMIT;
|
||||
arp_tx_request_ack <= '1';
|
||||
sr <= arp_request_preload;
|
||||
ethernet_ii_in.tx_mac_address <= MAC_ADDR_BROADCAST;
|
||||
end if;
|
||||
|
||||
bytecount <= BYTECOUNT_MAX;
|
||||
|
||||
when TRANSMIT =>
|
||||
if bytecount = 0 then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process tx_fsm;
|
||||
|
||||
ethernet_ii_in.tx_en <= '1' when state = TRANSMIT else '0';
|
||||
ethernet_ii_in.tx_data <= sr(0);
|
||||
|
||||
end block tx;
|
||||
end architecture rtl;
|
@ -1,176 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_eth.vhd : Ethernet OSI Layer 2, Data Link, ETH I/II Ethertype
|
||||
-- Implements arbitration of different ethertypes.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- Author : Markus Koch <markus@notsyncing.net>
|
||||
-- Contributors : None
|
||||
-- License : Mozilla Public License (MPL) Version 2
|
||||
-- -------------------------------------------------------------------------- --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
use work.trashernet_pkg.all;
|
||||
|
||||
entity trashernet_eth is
|
||||
generic(
|
||||
ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ETHERNET_II_PROTOCOLS_NONE
|
||||
);
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- MAC application interface
|
||||
mac_out : in mac_out_t; -- MAC application IF (out of MAC)
|
||||
mac_in : out mac_in_t; -- MAC application IF (into MAC)
|
||||
|
||||
-- Configuration
|
||||
config : in configuration_t; -- Global Trashernet configuration
|
||||
|
||||
-- Ethernet 802.3 application interface
|
||||
ethernet_i_out : out ethernet_i_out_t; -- Ethernet 802.3 IF (out from MAC)
|
||||
ethernet_i_in : in ethernet_i_in_t := ETHERNET_I_IN_UNUSED; -- Ethernet 802.3 IF (out from MAC)
|
||||
|
||||
-- Ethernet II application interface
|
||||
ethernet_ii_out : out ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); -- Ethernet II IF (out from MAC)
|
||||
ethernet_ii_in : in ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range) := (others => ETHERNET_II_IN_UNUSED) -- Ethernet II IF (into MAC)
|
||||
);
|
||||
end entity trashernet_eth;
|
||||
|
||||
architecture rtl of trashernet_eth is
|
||||
signal tx_data_ack_ii : std_logic_vector(ethernet_ii_out'range);
|
||||
signal tx_data_ack_i : std_logic;
|
||||
begin
|
||||
rx : block
|
||||
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
|
||||
constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2;
|
||||
signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high;
|
||||
signal mac_destination_matches : std_logic;
|
||||
signal rx_mac_header_rcv_delayed : std_logic;
|
||||
|
||||
begin
|
||||
mac_destination_matches <= '1' when --
|
||||
((mac_out.rx_header.mac_destination = MAC_ADDR_BROADCAST) or --
|
||||
(mac_out.rx_header.mac_destination = config.mac_address)) else
|
||||
'0';
|
||||
|
||||
mux : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
sel <= SEL_ETH_NONE;
|
||||
rx_mac_header_rcv_delayed <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
rx_mac_header_rcv_delayed <= mac_out.rx_mac_header_rcv;
|
||||
|
||||
if mac_out.rx_mac_header_rcv then
|
||||
sel <= SEL_ETH_NONE; -- By default, let's assume it's not for us
|
||||
if mac_destination_matches then
|
||||
if (unsigned(std_logic_vector'(mac_out.rx_header.mac_ethertype(0) & mac_out.rx_header.mac_ethertype(1))) < 1500) then -- Ethernet 802.3 Frame
|
||||
sel <= SEL_ETH_I;
|
||||
|
||||
else -- Ethernet II Frame
|
||||
for i in ETHERNET_II_PROTOCOLS'range loop
|
||||
if (mac_out.rx_header.mac_ethertype = ETHERNET_II_PROTOCOLS(i).ethertype) then
|
||||
sel <= i;
|
||||
end if;
|
||||
end loop;
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
end process mux;
|
||||
|
||||
-- Shared fields in the Ethernet II application interface
|
||||
shared_fields : for i in ethernet_ii_out'range generate
|
||||
ethernet_ii_out(i).rx_mac_address <= mac_out.rx_header.mac_source;
|
||||
ethernet_ii_out(i).rx_data <= mac_out.rx_mac_data;
|
||||
ethernet_ii_out(i).rx_data_valid <= mac_out.rx_mac_valid when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_crc_error <= mac_out.rx_mac_crc_error when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = i else '0';
|
||||
ethernet_ii_out(i).rx_header_rcv <= rx_mac_header_rcv_delayed when sel = i else '0';
|
||||
end generate shared_fields;
|
||||
|
||||
-- Shared fields in the Ethernet application interface
|
||||
ethernet_i_out.rx_data <= mac_out.rx_mac_data;
|
||||
ethernet_i_out.rx_data_valid <= mac_out.rx_mac_valid when sel = SEL_ETH_I;
|
||||
ethernet_i_out.rx_crc_error <= mac_out.rx_mac_crc_error when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_crc_ok <= mac_out.rx_mac_crc_ok when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_header_rcv <= rx_mac_header_rcv_delayed when sel = SEL_ETH_I else '0';
|
||||
ethernet_i_out.rx_mac_address <= mac_out.rx_header.mac_source;
|
||||
ethernet_i_out.rx_length <= unsigned(mac_out.rx_header.mac_ethertype(0)) & unsigned(mac_out.rx_header.mac_ethertype(1));
|
||||
|
||||
-- TX fields (need to be driven here to avoid multiple drivers (blocks drive ALL signals of a record))
|
||||
tx_fields : for i in ethernet_ii_out'range generate
|
||||
ethernet_ii_out(i).tx_data_ack <= tx_data_ack_ii(i);
|
||||
end generate;
|
||||
ethernet_i_out.tx_data_ack <= tx_data_ack_i;
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type state_t is (IDLE, TXD);
|
||||
signal state : state_t;
|
||||
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
|
||||
signal sel : integer range SEL_ETH_I to ethernet_ii_in'high;
|
||||
begin
|
||||
arb : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
sel <= SEL_ETH_I;
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
case state is
|
||||
when IDLE =>
|
||||
if not mac_out.tx_active then
|
||||
if (ethernet_i_in.tx_en) then -- ETH I has priority
|
||||
sel <= SEL_ETH_I;
|
||||
state <= TXD;
|
||||
else
|
||||
for i in ethernet_ii_in'range loop
|
||||
if ethernet_ii_in(i).tx_en then
|
||||
sel <= i;
|
||||
state <= TXD;
|
||||
exit; -- Prioritize according to vector
|
||||
end if;
|
||||
end loop;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when TXD =>
|
||||
state <= IDLE when (not mac_in.tx_mac_data_en or not mac_out.tx_active);
|
||||
end case;
|
||||
end if;
|
||||
end process arb;
|
||||
|
||||
mux : process(all) is
|
||||
begin
|
||||
-- Defaults to avoid latch
|
||||
tx_data_ack_i <= '0';
|
||||
tx_data_ack_ii <= (others => '0');
|
||||
|
||||
-- Actual MUX
|
||||
if (sel = SEL_ETH_I) then
|
||||
mac_in.tx_mac_data_en <= ethernet_i_in.tx_en;
|
||||
mac_in.tx_mac_data <= ethernet_i_in.tx_data;
|
||||
tx_data_ack_i <= mac_out.tx_mac_data_ack;
|
||||
mac_in.tx_header.mac_destination <= ethernet_i_in.tx_mac_address;
|
||||
mac_in.tx_header.mac_ethertype <= byte(ethernet_i_in.tx_length(15 downto 8)) & byte(ethernet_i_in.tx_length(7 downto 0));
|
||||
else
|
||||
mac_in.tx_mac_data_en <= ethernet_ii_in(sel).tx_en;
|
||||
mac_in.tx_mac_data <= ethernet_ii_in(sel).tx_data;
|
||||
txack : for i in ethernet_ii_out'range loop
|
||||
tx_data_ack_ii(i) <= mac_out.tx_mac_data_ack when sel = i else '0';
|
||||
end loop txack;
|
||||
|
||||
mac_in.tx_header.mac_destination <= ethernet_ii_in(sel).tx_mac_address;
|
||||
mac_in.tx_header.mac_ethertype <= ETHERNET_II_PROTOCOLS(sel).ethertype;
|
||||
end if;
|
||||
|
||||
mac_in.tx_header.mac_source <= config.mac_address;
|
||||
end process mux;
|
||||
end block tx;
|
||||
end architecture rtl;
|
@ -1,172 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_icmp.vhd : Ethernet OSI Layer 3, Network (ICMP)
|
||||
-- Implements ICMP echo replies
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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_icmp is
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- IPv4 application interface
|
||||
ipv4_protocol_out : in ipv4_protocol_out_t; -- IPv4 Protocol IF (out from IP Mux)
|
||||
ipv4_protocol_in : out ipv4_protocol_in_t -- IPv4 Protocol IF (into IP Mux)
|
||||
);
|
||||
end entity trashernet_icmp;
|
||||
|
||||
architecture rtl of trashernet_icmp is
|
||||
signal fifo_clear : std_logic;
|
||||
signal fifo_data_in : std_logic_vector(byte'range);
|
||||
signal fifo_push : std_logic;
|
||||
signal fifo_full : std_logic; -- TODO: FIFO full error handling
|
||||
signal fifo_data_out : std_logic_vector(fifo_data_in'range);
|
||||
signal fifo_pop : std_logic;
|
||||
signal fifo_empty : std_logic;
|
||||
|
||||
signal tx_response : std_logic;
|
||||
signal rx_checksum : std_logic_vector(15 downto 0);
|
||||
begin
|
||||
fifo_inst : entity work.fifo
|
||||
generic map(
|
||||
DEPTH => 64
|
||||
)
|
||||
port map(
|
||||
clk => clk,
|
||||
rst => rst,
|
||||
clear => fifo_clear,
|
||||
data_in => fifo_data_in,
|
||||
push => fifo_push,
|
||||
full => fifo_full,
|
||||
data_out => fifo_data_out,
|
||||
pop => fifo_pop,
|
||||
empty => fifo_empty
|
||||
);
|
||||
|
||||
rx : block
|
||||
signal sr : byte_vector(0 to 3);
|
||||
alias sr_type is sr(0);
|
||||
alias sr_code is sr(1);
|
||||
alias sr_checksum is sr(2 to 3);
|
||||
|
||||
type state_t is (HEADER, DATA, IGNORE);
|
||||
signal state : state_t;
|
||||
|
||||
signal byte_count : integer range 0 to sr'length;
|
||||
|
||||
begin
|
||||
rx_fsm : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= HEADER;
|
||||
byte_count <= byte_count'subtype'high;
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
|
||||
if (ipv4_protocol_out.rx_data_valid = '1') and (byte_count /= 0) then
|
||||
byte_count <= byte_count - 1;
|
||||
if state = HEADER then
|
||||
sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
if ipv4_protocol_out.rx_header_rcv then
|
||||
byte_count <= byte_count'subtype'high;
|
||||
state <= HEADER;
|
||||
end if;
|
||||
|
||||
case state is
|
||||
when HEADER =>
|
||||
if byte_count = 0 then
|
||||
if sr_type = x"08" then
|
||||
state <= DATA;
|
||||
else
|
||||
state <= IGNORE;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when DATA =>
|
||||
null; -- We just wait here and collect data
|
||||
|
||||
when IGNORE => -- Just wait until it's over
|
||||
null;
|
||||
end case;
|
||||
end if;
|
||||
end process rx_fsm;
|
||||
fifo_clear <= ipv4_protocol_out.rx_header_rcv; -- TODO: This will break things when we get another Ping too soon
|
||||
fifo_push <= ipv4_protocol_out.rx_data_valid when state = DATA else '0';
|
||||
fifo_data_in <= ipv4_protocol_out.rx_data;
|
||||
|
||||
tx_response <= ipv4_protocol_out.rx_ok_stb when state = DATA else '0';
|
||||
rx_checksum <= sr_checksum(2) & sr_checksum(3);
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
signal checksum : unsigned(16 downto 0);
|
||||
signal checksum_ones : unsigned(15 downto 0);
|
||||
|
||||
type state_t is (IDLE, HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
|
||||
signal sr : byte_vector(0 to 3);
|
||||
signal byte_count : integer range 0 to sr'subtype'high;
|
||||
begin
|
||||
|
||||
checksum <= ('0' & not (unsigned(rx_checksum))) - unsigned'('0' & x"0800"); -- The only change is Type 8 changes to Type 0
|
||||
checksum_ones <= not (checksum(15 downto 0) - checksum(16));
|
||||
|
||||
tx_fsm : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
if (ipv4_protocol_out.tx_data_ack = '1') and (byte_count /= 0) then
|
||||
sr <= sr(sr'low + 1 to sr'high) & x"00";
|
||||
byte_count <= byte_count - 1;
|
||||
end if;
|
||||
|
||||
case state is
|
||||
when IDLE =>
|
||||
if tx_response then
|
||||
ipv4_protocol_in.tx_ip_address <= ipv4_protocol_out.rx_ip_address; -- TODO: This is technically too late, but ¯\_(ツ)_/¯
|
||||
ipv4_protocol_in.tx_length <= ipv4_protocol_out.rx_length;
|
||||
sr <= byte_vector'(
|
||||
x"00",
|
||||
x"00",
|
||||
std_logic_vector(checksum_ones(15 downto 8)),
|
||||
std_logic_vector(checksum_ones(7 downto 0))
|
||||
);
|
||||
byte_count <= sr'subtype'high;
|
||||
state <= HEADER;
|
||||
end if;
|
||||
|
||||
when HEADER =>
|
||||
if (byte_count = 0) and (ipv4_protocol_out.tx_data_ack = '1') then
|
||||
state <= PAYLOAD;
|
||||
end if;
|
||||
|
||||
when PAYLOAD =>
|
||||
if fifo_empty then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process tx_fsm;
|
||||
|
||||
fifo_pop <= ipv4_protocol_out.tx_data_ack when state = PAYLOAD else '0';
|
||||
ipv4_protocol_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0';
|
||||
ipv4_protocol_in.tx_data <= sr(sr'low) when state = HEADER else fifo_data_out;
|
||||
end block tx;
|
||||
end architecture rtl;
|
@ -1,249 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_ipv4.vhd : Ethernet OSI Layer 3, Network (IPv4)
|
||||
-- Implements packet handling and IP-Layer en-/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_ipv4 is
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- Configuration
|
||||
ipv4_config : in ipv4_configuration_t; -- Trashernet IP configuration
|
||||
|
||||
-- ARP application interface
|
||||
arp_out : in arp_out_t; -- ARP IF (out from ARP)
|
||||
arp_in : out arp_in_t; -- ARP IF (into ARP)
|
||||
|
||||
-- Ethernet II application interface
|
||||
ethernet_ii_out : in ethernet_ii_out_t; -- Ethernet II IF (out from MAC)
|
||||
ethernet_ii_in : out ethernet_ii_in_t; -- Ethernet II IF (into MAC)
|
||||
|
||||
-- IPv4 application interface
|
||||
ipv4_out : out ipv4_out_t; -- IPv4 IF (out from IP)
|
||||
ipv4_in : in ipv4_in_t -- IPv4 IF (into IP)
|
||||
);
|
||||
end entity trashernet_ipv4;
|
||||
|
||||
architecture rtl of trashernet_ipv4 is
|
||||
begin
|
||||
rx : block
|
||||
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
|
||||
alias sr_version is sr(0)(7 downto 4);
|
||||
alias sr_ihl is sr(0)(3 downto 0);
|
||||
alias sr_dscp is sr(1)(7 downto 2);
|
||||
alias sr_ecn is sr(1)(1 downto 0);
|
||||
alias sr_total_length is sr(2 to 3);
|
||||
alias sr_identification is sr(4 to 5);
|
||||
alias sr_flags is sr(6)(7 downto 5);
|
||||
signal sr_fragment_offset : std_logic_vector(12 downto 0);
|
||||
alias sr_ttl is sr(8);
|
||||
alias sr_protocol is sr(9);
|
||||
alias sr_header_cks is sr(10 to 11);
|
||||
alias sr_source_ip is sr(12 to 15);
|
||||
alias sr_destination_ip is sr(16 to 19);
|
||||
|
||||
signal header_ok : std_logic;
|
||||
|
||||
signal bytecount : integer range 0 to 65535;
|
||||
signal block_done : std_logic;
|
||||
|
||||
type state_t is (HEADER, OPT, PAYLOAD, DONE);
|
||||
signal state : state_t;
|
||||
begin
|
||||
sr_fragment_offset <= sr(6)(4 downto 0) & sr(7);
|
||||
|
||||
block_done <= '1' when bytecount = 0 else '0';
|
||||
header_ok <= '1' when --
|
||||
(std_logic_vector'(sr_flags(7) & sr_flags(5)) = "00") and -- Reserved = 0, MF unset
|
||||
(to_integer(unsigned(sr_fragment_offset)) = 0) and -- No fragment offset -> not last fragment
|
||||
(sr_destination_ip = ipv4_config.ip_address) else -- It's addressed to us. TODO: We do not support broadcasts
|
||||
'0';
|
||||
rx_fsm : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= HEADER;
|
||||
ipv4_out.rx_ok_stb <= '0';
|
||||
ipv4_out.rx_error_stb <= '0';
|
||||
ipv4_out.rx_header_rcv <= '0';
|
||||
bytecount <= BYTECOUNT_HEADER;
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
ipv4_out.rx_ok_stb <= '0';
|
||||
ipv4_out.rx_error_stb <= '0';
|
||||
ipv4_out.rx_header_rcv <= '0';
|
||||
|
||||
if (ethernet_ii_out.rx_crc_ok or ethernet_ii_out.rx_crc_error) then
|
||||
state <= HEADER;
|
||||
bytecount <= BYTECOUNT_HEADER;
|
||||
|
||||
ipv4_out.rx_error_stb <= ethernet_ii_out.rx_crc_error or to_std_logic(state = PAYLOAD);
|
||||
ipv4_out.rx_ok_stb <= ethernet_ii_out.rx_crc_ok and to_std_logic(state = DONE);
|
||||
end if;
|
||||
if (ethernet_ii_out.rx_data_valid = '1') and (bytecount > 0) then
|
||||
if state = HEADER then
|
||||
sr <= sr(sr'low + 1 to sr'high) & ethernet_ii_out.rx_data;
|
||||
end if;
|
||||
bytecount <= bytecount - 1;
|
||||
end if;
|
||||
|
||||
case state is
|
||||
when HEADER =>
|
||||
if block_done then
|
||||
bytecount <= to_integer(unsigned(sr_ihl) - 5) * 4; -- five 32-bit words is the header itself
|
||||
if header_ok then
|
||||
state <= OPT;
|
||||
ipv4_out.rx_header_rcv <= '1';
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when OPT =>
|
||||
if block_done then
|
||||
bytecount <= to_integer(unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3)))) - to_integer(unsigned(sr_ihl)) * 4;
|
||||
state <= PAYLOAD;
|
||||
end if;
|
||||
|
||||
when PAYLOAD =>
|
||||
if block_done then
|
||||
state <= DONE;
|
||||
end if;
|
||||
|
||||
when DONE => -- @suppress "Dead state 'DONE'": Outgoing state transition is outside of case statement
|
||||
null; -- We just wait here until the MAC gives us a CRC OK/error
|
||||
end case;
|
||||
end if;
|
||||
end process rx_fsm;
|
||||
|
||||
ipv4_out.rx_data <= ethernet_ii_out.rx_data;
|
||||
ipv4_out.rx_data_valid <= ethernet_ii_out.rx_data_valid when state = PAYLOAD else '0';
|
||||
ipv4_out.rx_ip_address <= sr_source_ip;
|
||||
ipv4_out.rx_protocol <= sr_protocol;
|
||||
ipv4_out.rx_length <= unsigned(std_logic_vector'(sr_total_length(2) & sr_total_length(3))) - to_integer(unsigned(sr_ihl)) * 4;
|
||||
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD);
|
||||
signal state : state_t;
|
||||
|
||||
signal full_length : ipv4_length;
|
||||
|
||||
signal sr : byte_vector(0 to 19);
|
||||
signal byte_cnt : integer range 0 to sr'length - 1;
|
||||
signal alt_byte : std_logic;
|
||||
signal checksum : unsigned(20 downto 0); -- 20 Header fields -> 19 chances for carry -> 5 additional bits
|
||||
signal checksum_ones : unsigned(15 downto 0);
|
||||
|
||||
constant TTL : byte := x"40";
|
||||
impure function get_header(checksum : std_logic_vector(15 downto 0)) return byte_vector is
|
||||
variable ret : byte_vector(sr'range);
|
||||
begin
|
||||
ret := --
|
||||
byte_vector'(
|
||||
x"45", x"00", byte(full_length(15 downto 8)), byte(full_length(7 downto 0)), -- Ver/IHL, DSCP/ECN, Len
|
||||
x"00", x"00", x"00", x"00" -- Identification, Flags/FragOffset
|
||||
) & TTL & ipv4_in.tx_protocol & checksum(15 downto 8) & checksum(7 downto 0) & -- TTL, Protocol, Header Checksum
|
||||
ipv4_config.ip_address & -- Source IP
|
||||
ipv4_in.tx_ip_address -- Destination IP
|
||||
;
|
||||
return ret;
|
||||
end function get_header;
|
||||
begin
|
||||
full_length <= sr'length + ipv4_in.tx_length;
|
||||
checksum_ones <= checksum(15 downto 0) + checksum(checksum'high downto 16);
|
||||
|
||||
tx_fsm : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= IDLE;
|
||||
ipv4_out.tx_data_ack <= '0';
|
||||
arp_in.arp_query_stb <= '0';
|
||||
ipv4_out.tx_err_stb <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
arp_in.arp_query_stb <= '0';
|
||||
ipv4_out.tx_data_ack <= '0';
|
||||
ipv4_out.tx_err_stb <= '0';
|
||||
|
||||
if (ethernet_ii_out.tx_data_ack = '1') or (state = CALC_CKS) 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 ipv4_in.tx_en then
|
||||
arp_in.arp_query_stb <= '1';
|
||||
state <= ARP;
|
||||
end if;
|
||||
|
||||
when ARP =>
|
||||
if arp_out.arp_ok_stb then
|
||||
ethernet_ii_in.tx_mac_address <= arp_out.arp_mac;
|
||||
|
||||
byte_cnt <= sr'length - 1;
|
||||
sr <= get_header(x"0000");
|
||||
alt_byte <= '1';
|
||||
checksum <= (others => '0');
|
||||
state <= CALC_CKS;
|
||||
end if;
|
||||
if arp_out.arp_fail_stb then
|
||||
ipv4_out.tx_err_stb <= '1';
|
||||
-- Then we lock up in this state until tx_en is disabled
|
||||
end if;
|
||||
|
||||
when CALC_CKS =>
|
||||
alt_byte <= not alt_byte;
|
||||
|
||||
if alt_byte then
|
||||
checksum <= checksum + unsigned(std_logic_vector'(sr(sr'low) & sr(sr'low + 1)));
|
||||
else
|
||||
if byte_cnt = 0 then
|
||||
state <= HEADER;
|
||||
byte_cnt <= sr'length - 1;
|
||||
sr <= get_header(not std_logic_vector(checksum_ones));
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when HEADER =>
|
||||
if byte_cnt = 0 then
|
||||
state <= PAYLOAD;
|
||||
end if;
|
||||
|
||||
when PAYLOAD =>
|
||||
if ethernet_ii_out.tx_data_ack then
|
||||
sr(sr'low) <= ipv4_in.tx_data;
|
||||
ipv4_out.tx_data_ack <= '1';
|
||||
if not ipv4_in.tx_en then
|
||||
state <= IDLE;
|
||||
end if;
|
||||
end if;
|
||||
end case;
|
||||
end if;
|
||||
end process tx_fsm;
|
||||
|
||||
ethernet_ii_in.tx_data <= sr(sr'low);
|
||||
ethernet_ii_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0';
|
||||
|
||||
arp_in.arp_ip <= ipv4_in.tx_ip_address;
|
||||
|
||||
end block tx;
|
||||
|
||||
end architecture rtl;
|
@ -1,122 +0,0 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_eth.vhd : Ethernet OSI Layer 3, Network, Protocol Muxing
|
||||
-- Implements arbitration of different IP protocols.
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- 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_ipv4prot is
|
||||
generic(
|
||||
IPV4_PROTOCOLS : ipv4_protocol_vector
|
||||
);
|
||||
port(
|
||||
-- Global
|
||||
clk : in std_logic; -- Global clock
|
||||
rst : in std_logic; -- Asynchronous reset
|
||||
|
||||
-- IPv4 application interface
|
||||
ipv4_out : in ipv4_out_t; -- IPv4 IF (out from IP)
|
||||
ipv4_in : out ipv4_in_t; -- IPv4 IF (into IP)
|
||||
|
||||
-- IPv4 protocol interface
|
||||
ipv4_protocol_out : out ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); -- IPv4 Protocol IF (out from IP Mux)
|
||||
ipv4_protocol_in : in ipv4_protocol_in_vector(IPV4_PROTOCOLS'range) -- IPv4 Protocol IF (into IP Mux)
|
||||
);
|
||||
end entity trashernet_ipv4prot;
|
||||
|
||||
architecture rtl of trashernet_ipv4prot is
|
||||
constant SEL_PROTOCOL_NONE : integer := ipv4_protocol_out'low - 1;
|
||||
|
||||
signal rx_sel : integer range SEL_PROTOCOL_NONE to ipv4_protocol_out'high;
|
||||
signal tx_sel : integer range ipv4_protocol_in'low to ipv4_protocol_in'high;
|
||||
|
||||
begin
|
||||
rx : block
|
||||
signal rx_header_rcv_delayed : std_logic;
|
||||
|
||||
begin
|
||||
arb : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
rx_sel <= SEL_PROTOCOL_NONE;
|
||||
rx_header_rcv_delayed <= '0';
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
rx_header_rcv_delayed <= ipv4_out.rx_header_rcv;
|
||||
|
||||
if ipv4_out.rx_header_rcv then
|
||||
rx_sel <= SEL_PROTOCOL_NONE;
|
||||
for i in IPV4_PROTOCOLS'range loop
|
||||
if (ipv4_out.rx_protocol = IPV4_PROTOCOLS(i)) then
|
||||
rx_sel <= i;
|
||||
end if;
|
||||
end loop;
|
||||
end if;
|
||||
end if;
|
||||
end process arb;
|
||||
|
||||
mux : for i in ipv4_protocol_out'range generate
|
||||
ipv4_protocol_out(i).rx_data <= ipv4_out.rx_data;
|
||||
ipv4_protocol_out(i).rx_ip_address <= ipv4_out.rx_ip_address;
|
||||
ipv4_protocol_out(i).rx_length <= ipv4_out.rx_length;
|
||||
ipv4_protocol_out(i).rx_data_valid <= ipv4_out.rx_data_valid when rx_sel = i else '0';
|
||||
ipv4_protocol_out(i).rx_error_stb <= ipv4_out.rx_error_stb when rx_sel = i else '0';
|
||||
ipv4_protocol_out(i).rx_ok_stb <= ipv4_out.rx_ok_stb when rx_sel = i else '0';
|
||||
ipv4_protocol_out(i).rx_header_rcv <= rx_header_rcv_delayed when rx_sel = i else '0';
|
||||
end generate mux;
|
||||
|
||||
tx_mux : for i in ipv4_protocol_out'range generate
|
||||
ipv4_protocol_out(i).tx_data_ack <= ipv4_out.tx_data_ack when tx_sel = i else '0';
|
||||
ipv4_protocol_out(i).tx_err_stb <= ipv4_out.tx_err_stb when tx_sel = i else '0';
|
||||
ipv4_protocol_out(i).tx_ok_stb <= ipv4_out.tx_ok_stb when tx_sel = i else '0';
|
||||
end generate;
|
||||
end block rx;
|
||||
|
||||
tx : block
|
||||
type state_t is (IDLE, TXD);
|
||||
signal state : state_t;
|
||||
|
||||
begin
|
||||
arb : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
state <= IDLE;
|
||||
tx_sel <= ipv4_protocol_in'left;
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
case state is
|
||||
when IDLE =>
|
||||
for i in ipv4_protocol_in'range loop
|
||||
if ipv4_protocol_in(i).tx_en then
|
||||
tx_sel <= i;
|
||||
state <= TXD;
|
||||
exit; -- Prioritize according to vector
|
||||
end if;
|
||||
end loop;
|
||||
|
||||
when TXD =>
|
||||
state <= IDLE when (not ipv4_in.tx_en);
|
||||
end case;
|
||||
end if;
|
||||
end process arb;
|
||||
|
||||
demux : block
|
||||
begin
|
||||
ipv4_in.tx_data <= ipv4_protocol_in(tx_sel).tx_data;
|
||||
ipv4_in.tx_en <= ipv4_protocol_in(tx_sel).tx_en;
|
||||
ipv4_in.tx_ip_address <= ipv4_protocol_in(tx_sel).tx_ip_address;
|
||||
ipv4_in.tx_length <= ipv4_protocol_in(tx_sel).tx_length;
|
||||
ipv4_in.tx_protocol <= IPV4_PROTOCOLS(tx_sel);
|
||||
end block demux;
|
||||
end block tx;
|
||||
end architecture rtl;
|
@ -102,7 +102,7 @@ begin
|
||||
byte_count <= byte_count + 1;
|
||||
end if;
|
||||
end if;
|
||||
when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error)
|
||||
when PAYLOAD =>
|
||||
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
|
||||
|
@ -253,7 +253,6 @@ begin
|
||||
begin
|
||||
if rst then
|
||||
phy_out.rx_data_valid <= '0';
|
||||
phy_out.rx_data <= (others => '0'); -- Needed for yosys to compile
|
||||
|
||||
elsif rising_edge(clk) then
|
||||
phy_out.rx_data_valid <= '0';
|
||||
@ -297,10 +296,7 @@ begin
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
transmitter : block
|
||||
constant TX_STB_CNT_IDEAL : real := real(F_CLK) / real((F_ETH * 2));
|
||||
constant TX_STB_SKIP_ERROR : real := abs (round(TX_STB_CNT_IDEAL) - TX_STB_CNT_IDEAL);
|
||||
constant TX_STB_CNT_MAX : integer := integer(round(TX_STB_CNT_IDEAL + 0.25)) - 1; -- Round up starting for error > 0.25
|
||||
constant TX_STB_SKIP_SECOND : boolean := TX_STB_SKIP_ERROR >= 0.25; -- Skip one clock cycle every other symbol to hit clock rate in between when the divider is close to x.5
|
||||
constant TX_STB_CNT_MAX : integer := integer(round(real(F_CLK) / real((F_ETH * 2)))) - 1;
|
||||
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
|
||||
|
||||
type tx_state_t is (IDLE, NLP, TX, IPG);
|
||||
@ -371,9 +367,6 @@ begin
|
||||
|
||||
if tx_stb_cnt = 0 then
|
||||
tx_stb_cnt <= TX_STB_CNT_MAX;
|
||||
if TX_STB_SKIP_SECOND and bit_stage = '1' then
|
||||
tx_stb_cnt <= TX_STB_CNT_MAX - 1;
|
||||
end if;
|
||||
else
|
||||
tx_stb_cnt <= tx_stb_cnt - 1;
|
||||
end if;
|
||||
|
@ -44,14 +44,12 @@ architecture rtl of trashernet_phy_cdc is
|
||||
signal phy_phy_in : phy_in_t;
|
||||
|
||||
-- Helper signals
|
||||
signal rx_data_i : byte;
|
||||
signal rx_data_valid_i : std_logic;
|
||||
|
||||
begin
|
||||
-- -------------------------------------------------------------------------
|
||||
-- Drives: PHY clock domain
|
||||
-- -------------------------------------------------------------------------
|
||||
|
||||
-- Reset synchronizer for PHY
|
||||
rstsync : process(phy_clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
@ -61,7 +59,6 @@ begin
|
||||
end if;
|
||||
end process rstsync;
|
||||
|
||||
-- Operate Trashernet in PHY clock domain
|
||||
trashernet_phy_inst : entity work.trashernet_phy
|
||||
generic map(
|
||||
F_CLK => F_CLK_PHY
|
||||
@ -76,22 +73,6 @@ begin
|
||||
tx_n => tx_n
|
||||
);
|
||||
|
||||
-- Latch data in PHY clock domain when valid is strobed
|
||||
-- If the other clock domain is slower than the time it takes for the strobe to synchronize,
|
||||
-- `phy_phy_out.rx_data` will already have shifted in the next bit and no longer be valid.
|
||||
-- Therefore, we need to latch it here.
|
||||
rxdff : process(phy_clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
rx_data_i <= (others => '0');
|
||||
|
||||
elsif rising_edge(phy_clk) then
|
||||
if phy_phy_out.rx_data_valid then
|
||||
rx_data_i <= phy_phy_out.rx_data;
|
||||
end if;
|
||||
end if;
|
||||
end process rxdff;
|
||||
|
||||
synchronizer_txen_inst : entity work.synchronizer
|
||||
generic map(
|
||||
SIZE => 5
|
||||
@ -115,10 +96,18 @@ begin
|
||||
a_in => phy_phy_out.rx_data_valid,
|
||||
b_clk => clk,
|
||||
b_rst => rst,
|
||||
b_out => phy_out.rx_data_valid
|
||||
b_out => rx_data_valid_i
|
||||
);
|
||||
|
||||
phy_out.rx_data <= rx_data_i; -- No need to synchronize in new clock domain as latched data has been stable for a while thanks to the delay in the _valid synchronizer
|
||||
rxdvff : process(clk, rst) is
|
||||
begin
|
||||
if rst then
|
||||
phy_out.rx_data_valid <= '0';
|
||||
elsif rising_edge(clk) then
|
||||
phy_out.rx_data_valid <= rx_data_valid_i;
|
||||
phy_out.rx_data <= phy_phy_out.rx_data; -- Data should be stable after the time it takes the _valid signal to go through the synchronizer
|
||||
end if;
|
||||
end process rxdvff;
|
||||
|
||||
cdc_strobe_rxer_inst : entity work.cdc_strobe
|
||||
port map(
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
package trashernet_pkg is
|
||||
-- General types
|
||||
@ -19,8 +18,6 @@ package trashernet_pkg is
|
||||
|
||||
-- MAC specific types
|
||||
subtype mac_addr_t is byte_vector(0 to 5);
|
||||
constant MAC_ADDR_BROADCAST : mac_addr_t := (others => x"FF");
|
||||
function mac_to_string(constant mac_address : in mac_addr_t) return string;
|
||||
subtype ethertype_t is byte_vector(0 to 1);
|
||||
|
||||
type mac_header_fields is record
|
||||
@ -31,17 +28,6 @@ package trashernet_pkg is
|
||||
|
||||
-- IP specific types
|
||||
subtype ip_addr_t is byte_vector(0 to 3);
|
||||
function ip_to_string(constant ip_address : in ip_addr_t) return string;
|
||||
|
||||
-- Configuration interface
|
||||
type configuration_t is record
|
||||
mac_address : mac_addr_t; -- MAC address of this node
|
||||
end record configuration_t;
|
||||
type ipv4_configuration_t is record
|
||||
ip_address : ip_addr_t; -- IP address of this node
|
||||
gateway : ip_addr_t; -- Peer to use when target is outside of subnet
|
||||
subnet_mask : ip_addr_t; -- Subnet mask of this node
|
||||
end record ipv4_configuration_t;
|
||||
|
||||
-- PHY interface
|
||||
type phy_in_t is record
|
||||
@ -79,149 +65,8 @@ package trashernet_pkg is
|
||||
rx_mac_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
end record mac_out_t;
|
||||
|
||||
-- MAC ETH interface
|
||||
type ethernet_ii_protocol_t is record
|
||||
ethertype : ethertype_t;
|
||||
end record;
|
||||
type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t;
|
||||
constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- @suppress "Null range": We want a NULL vector here to remove the logic
|
||||
constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
|
||||
constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06"));
|
||||
|
||||
type ethernet_i_out_t is record
|
||||
rx_mac_address : mac_addr_t; -- Source MAC address
|
||||
rx_crc_ok : std_logic; -- End of packet, CRC OK
|
||||
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_length : unsigned(15 downto 0); -- RX payload length in bytes
|
||||
rx_header_rcv : std_logic; -- Start of reception, `rx_length` is valid
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
end record;
|
||||
type ethernet_i_in_t is record
|
||||
tx_mac_address : mac_addr_t; -- Destination MAC address
|
||||
tx_data : byte; -- TX data
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
tx_length : unsigned(15 downto 0); -- TX payload length in bytes
|
||||
end record;
|
||||
constant ETHERNET_I_IN_UNUSED : ethernet_i_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", tx_length => x"0000", others => '0'); -- TODO
|
||||
|
||||
type ethernet_ii_out_t is record
|
||||
rx_mac_address : mac_addr_t; -- Source MAC address
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_crc_ok : std_logic; -- End of packet, CRC OK
|
||||
rx_crc_error : std_logic; -- End of packet, CRC invalid
|
||||
rx_header_rcv : std_logic; -- Start of reception
|
||||
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
|
||||
end record;
|
||||
type ethernet_ii_in_t is record
|
||||
tx_mac_address : mac_addr_t; -- Destination MAC address
|
||||
tx_data : byte; -- TX data
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
end record;
|
||||
type ethernet_ii_out_vector is array (natural range <>) of ethernet_ii_out_t;
|
||||
type ethernet_ii_in_vector is array (natural range <>) of ethernet_ii_in_t;
|
||||
constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0');
|
||||
|
||||
-- ARP interface
|
||||
type arp_out_t is record
|
||||
arp_mac : mac_addr_t; -- Resulting MAC address for query
|
||||
arp_ok_stb : std_logic; -- ARP request successful, `arp_mac` valid
|
||||
arp_fail_stb : std_logic; -- ARP request failed, `arp_mac` invalid
|
||||
end record arp_out_t;
|
||||
type arp_in_t is record
|
||||
arp_ip : ip_addr_t; -- IP address to query
|
||||
arp_query_stb : std_logic; -- Request MAC for IP
|
||||
end record arp_in_t;
|
||||
|
||||
-- IPv4 interface
|
||||
subtype ipv4_protocol is byte;
|
||||
type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol;
|
||||
constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01";
|
||||
constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06";
|
||||
constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11";
|
||||
subtype ipv4_length is unsigned(15 downto 0);
|
||||
|
||||
type ipv4_out_t is record
|
||||
rx_ip_address : ip_addr_t; -- Source IP address
|
||||
rx_protocol : ipv4_protocol; -- Transport Protocol
|
||||
rx_length : ipv4_length; -- Telegram length (excluding header)
|
||||
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
rx_header_rcv : std_logic; -- Start of reception (`rx_ip_address` and `rx_protocol` are valid)
|
||||
|
||||
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
|
||||
tx_ok_stb : std_logic; -- Transmission successful
|
||||
tx_err_stb : std_logic; -- Transmission failed
|
||||
end record ipv4_out_t;
|
||||
type ipv4_in_t is record
|
||||
tx_ip_address : ip_addr_t; -- Destination IP address
|
||||
tx_protocol : ipv4_protocol; -- Transport Protocol
|
||||
tx_length : ipv4_length; -- Length of payload
|
||||
tx_data : byte; -- TX data
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
end record ipv4_in_t;
|
||||
|
||||
type ipv4_protocol_out_t is record
|
||||
rx_ip_address : ip_addr_t; -- Source IP address
|
||||
rx_length : ipv4_length; -- Telegram length (excluding header)
|
||||
|
||||
rx_data : byte; -- RX data
|
||||
rx_data_valid : std_logic; -- RX data valid strobe
|
||||
rx_ok_stb : std_logic; -- End of packet, checksum OK
|
||||
rx_error_stb : std_logic; -- End of packet, checksum invalid
|
||||
rx_header_rcv : std_logic; -- Start of reception
|
||||
|
||||
tx_data_ack : std_logic; -- Give next data byte of disable `tx_en`
|
||||
tx_ok_stb : std_logic; -- Transmission successful
|
||||
tx_err_stb : std_logic; -- Transmission failed
|
||||
end record ipv4_protocol_out_t;
|
||||
type ipv4_protocol_in_t is record
|
||||
tx_ip_address : ip_addr_t; -- Destination IP address
|
||||
tx_length : ipv4_length; -- Length of payload
|
||||
|
||||
tx_data : byte; -- TX data
|
||||
tx_en : std_logic; -- Start and continue transmitting
|
||||
end record ipv4_protocol_in_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;
|
||||
|
||||
-- General helper functions
|
||||
function to_std_logic(constant bool : boolean) return std_logic;
|
||||
end package trashernet_pkg;
|
||||
|
||||
package body trashernet_pkg is
|
||||
function mac_to_string(constant mac_address : in mac_addr_t) return string is
|
||||
begin
|
||||
return --
|
||||
to_hstring(mac_address(0)) & ":" & --
|
||||
to_hstring(mac_address(1)) & ":" & --
|
||||
to_hstring(mac_address(2)) & ":" & --
|
||||
to_hstring(mac_address(3)) & ":" & --
|
||||
to_hstring(mac_address(4)) & ":" & --
|
||||
to_hstring(mac_address(5)) --
|
||||
;
|
||||
end function mac_to_string;
|
||||
|
||||
function ip_to_string(constant ip_address : in ip_addr_t) return string is
|
||||
begin
|
||||
return --
|
||||
to_string(to_integer(unsigned(ip_address(0)))) & "." & --
|
||||
to_string(to_integer(unsigned(ip_address(1)))) & "." & --
|
||||
to_string(to_integer(unsigned(ip_address(2)))) & "." & --
|
||||
to_string(to_integer(unsigned(ip_address(3)))) --
|
||||
;
|
||||
end function ip_to_string;
|
||||
|
||||
function to_std_logic(constant bool : boolean) return std_logic is
|
||||
variable ret : std_logic;
|
||||
begin
|
||||
ret := '1' when bool else '0';
|
||||
return ret;
|
||||
end function to_std_logic;
|
||||
end package body trashernet_pkg;
|
||||
|
Loading…
Reference in New Issue
Block a user