Compare commits

..

64 Commits

Author SHA1 Message Date
7379df7b74 phy: Improve clock accuracy at 0.5 dividers at the cost of some jitter 2024-07-15 13:34:54 +02:00
21a0d0e69a phy_cdc: Allow unlimited clock ratios
Actually, assuming that each clock domain is fast enough for 10 MBit/s,
there is no problem running crazy ratios.
2024-07-06 18:05:00 +02:00
943febcb99 phy_cdc: Fix synchronization behavior
For low ratios of the two clock domains, the slow clock domain would
receive incorrect data as the fast (PHY) clock domain's data output
would have already updated with the first bit of the next byte by
the time the slow (system) clock domain read it.

This fix registers the data in the source clock domain as opposed
to the destination clock domain. This is allowed because the valid
strobe is synchronized in a way to guarantee that data has been
valid for a while when it becomes asserted.

Additionally, `phy_out.rx_data` and `phy_out.rx_data_valid` are no
longer registered in the destination (system) clock domain as this
now introduces unnecessary latency. Also, this was broken the way
it was anyway as `rx_active` and `rx_error` would've also had to be
registered to keep all timings in sync.
2024-07-06 17:58:11 +02:00
da7e329939 des: Fix compatibility with Yosys 2024-06-22 11:19:02 +02:00
4bce111d69 doc: Add synthesized size for PHY-only design 2024-02-04 09:34:36 +01:00
2190f146a0 doc: Add IPv4 modules to doc 2022-11-01 17:41:39 +01:00
91cdb13bb3 demo: Add ICMP ping reply demo design 2022-11-01 17:39:39 +01:00
0db98483dc ipv4: Change syntax to make Synplify happy 2022-11-01 17:10:33 +01:00
6de8d24537 icmp: Implement ICMP echo replies 2022-11-01 16:23:27 +01:00
87bc9ce3d7 ipv4: Add rx_length field 2022-11-01 16:22:59 +01:00
fdcb1cb719 ipv4: Implement IPv4 TX 2022-10-31 23:46:46 +01:00
a0c9df28f1 ipv4: Remove obsolete signals 2022-10-31 23:45:05 +01:00
0d7981f801 ipv4: Default sr to all zeroes to squelch warnings 2022-10-31 17:02:37 +01:00
172a11070b ipv4prot: Implement IPv4 protocol muxing 2022-10-31 16:57:54 +01:00
9d9b3b1cce eth: Fix incorrect TX exit condition 2022-10-31 16:56:17 +01:00
e421ba0dff eth: Fix missing reset for tx sel 2022-10-31 16:55:13 +01:00
277d01737d ipv4: Implement RX 2022-10-30 22:32:47 +01:00
a19de6d0c8 gen: Add generic timer module 2022-10-30 22:31:10 +01:00
1c08d48861 doc: Add ARP layer to README 2022-10-30 17:38:29 +01:00
ccb7d6c0da Fix linter warnings 2022-10-30 17:29:14 +01:00
8d9ca9e784 arp: Rename timeout_tick to systick 2022-10-30 17:27:43 +01:00
3240f363ba arp: Reformat code 2022-10-29 19:19:16 +02:00
a5d5ca280d arp: Add timeout 2022-10-29 19:18:51 +02:00
651b6bb11e bench: Test ARP requests in HWITL 2022-10-28 18:55:04 +02:00
4804674d8e arp: Implement ARP requests
This is still missing timeout in case of no reply.
This needs to be added!
2022-10-28 18:52:17 +02:00
1a41120afa bench: Clarify HWITL interface creation for local testing 2022-10-27 17:02:53 +02:00
5f7704cf63 pkg: Implement to_string functions for MAC and IP addresses 2022-05-15 14:51:52 +02:00
5f389b0c7b arp: Implement ARP replies 2022-05-15 14:51:49 +02:00
7bb82a9acf doc: Add trashernet_eth to documentation 2022-05-15 14:40:45 +02:00
3c1c14f953 bench: hw_itl: Add debug prints for RX/TX 2022-05-15 14:37:21 +02:00
50b65bccec eth: tx: Remove accidental latch 2022-05-15 14:30:45 +02:00
461c7ebce1 eth: tx: Fix polarity of tx_active checks 2022-05-15 14:27:26 +02:00
eab8783c6b bench: Disable wave logging by default 2022-05-13 21:03:12 +02:00
8f4e996d57 bench: Clean up HWITL bench 2022-05-13 20:42:18 +02:00
e420b08eb2 bench: Add preliminary HWITL test for eth core 2022-05-13 20:39:05 +02:00
7aa1b56bf9 eth: Add Ethernet type MUX 2022-05-13 20:37:28 +02:00
8f3420bd54 pkg: Add configuration_t to trashernet 2022-05-13 20:36:52 +02:00
4b4e5fdbfa bench: hw_itl: Calculate and append FCS for incoming frames 2022-05-13 18:20:13 +02:00
0e80900426 bench: hw_itl: Add comments 2022-05-13 18:19:35 +02:00
fd721ae24f bench: Move HWITL tests to own top level / test bench file 2022-04-30 12:53:35 +02:00
82d4e6808b bench: hwitl: Fix design to HW comms
Forgot to remove the sync header.
2022-04-30 12:48:39 +02:00
1c281bc0f7 bench: Add hardware-in-the-loop test 2022-04-29 20:53:20 +02:00
c4f4cfe68e bench: Allow for different bench top levels in Makefile 2022-04-29 20:52:31 +02:00
f224aee487 doc: Add licensing info about the components of the logo 2022-04-24 12:26:19 +02:00
f45ded2ca9 bench: Add cocotb-based test bench for MAC test 2022-04-18 17:06:53 +02:00
7d86f6a262 env: Add Sigasi project files 2022-04-18 10:17:42 +02:00
313711dbc0 doc: Add KiCad schematic 2022-04-18 10:04:24 +02:00
0f7437e901 demo: Fix missing record in mac demo
Will eventually become part of trashernet, I guess. But for now,
let's add it to the top level.
2022-04-18 09:44:27 +02:00
5c27b685dc demo: Move top level to library work
With Diamond, the design top level *must* be in the "work" library,
otherwise your project will be haunted with weird compile errors
that sometimes simply cause the build to fail with an arbitrary
error message, but can sometimes even lead to a straight-up broken
bitstream.

Real bad stuff, so let's move the stuff from our "design" library
to "work". That fixes it.
2022-02-12 13:19:14 +01:00
6beb9c8346 demo: Remove wait state
Not needed since a9039a175c.
2022-01-30 19:40:55 +01:00
5b54a596c1 doc: Add circuit diagram 2022-01-30 19:29:09 +01:00
61812c133f phy: Improve header synchronization 2022-01-30 19:26:43 +01:00
075f935e12 phy: Fix NLPs
We need a different NLP timeout for RX than TX.
Also use a prescaler to improve timing.
2022-01-29 20:15:02 +01:00
a9039a175c phy+mac: Improve interface
Instead of having the user wait until the en-low was latched by
the core, let's just latch it internally.
2022-01-23 16:52:05 +01:00
4a39c3d638 phy: Notify that the end-of-packet has been accepted 2022-01-23 16:31:20 +01:00
d1150fb7db demo: Add MAC demo design 2022-01-23 16:29:31 +01:00
d1e0143c9d phy: Fix TX manchester encoding
Ethernet uses "inverted" manchester as per IEEE 802.3.
2022-01-23 16:02:31 +01:00
1bcddf4273 phy: Improve NLP timeout accuracy 2022-01-23 16:01:33 +01:00
afb545cd02 Clean up code formatting 2022-01-22 20:50:18 +01:00
4e39e3b371 phy: Do not report tx_active when providing NLPs 2022-01-22 20:48:49 +01:00
a2edc540b2 phy: Fix NLP timebase error 2022-01-22 20:48:23 +01:00
6f8bb7ba54 phy: Fix rounding error for NLP timeout 2022-01-22 20:47:47 +01:00
76eaff20fd mac+phy: Initialize all required variables 2022-01-22 20:47:09 +01:00
37714d5c2d bench: Use VUnit
Well, not really. This just makes it compile and work with VUNIT,
but it's not a real VUnit test bench. Will do that at some other
point.
2022-01-16 17:46:01 +01:00
46 changed files with 8939 additions and 45 deletions

View File

@ -0,0 +1,7 @@
<?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>

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
# 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 Normal file
View File

@ -0,0 +1,3 @@
[submodule "cocotb/cocotb_helpers"]
path = cocotb/cocotb_helpers
url = https://git.notsyncing.net/fpga/cocotb_helpers.git

35
.library_mapping.xml Normal file
View File

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

66
.project Normal file
View File

@ -0,0 +1,66 @@
<?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>&lt;project&gt;/.externalToolBuilders/com.sigasi.hdt.toolchains.vunit.builder.launch</value>
</dictionary>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments>
</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>

View File

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

View File

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

View File

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

View File

@ -5,7 +5,8 @@
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`
* Layer 2, Data link: `trashernet_mac`, `trashernet_eth`, `trashernet_arp`
* Layer 3, Network: `trashernet_ipv4`, `trashernet_ipv4prot`, `trashernet_icmp`
When writing it, the following were the main design philosophies:
@ -17,11 +18,33 @@ When writing it, the following were the main design philosophies:
## Hardware
TBD. TL;DR:
![Circuit Diagram](doc/circuit.svg)
* RX: Use resistors behind Ethernet magnetics to reduce Ethernet RX voltage levels (0 +-2.5V) to LVPECL voltage levels (1.65V +-400mV). Or LVDS if your FPGA can't handle LVPECL.
* TX: Meh, a series current limiting resistor is good enough.
* RX: Use resistors behind Ethernet magnetics to reduce Ethernet RX voltage levels (0 +-2.5V) to LVPECL voltage levels (1.65V +-400mV). Or LVDS if your FPGA can't handle LVPECL. Also, pull up/down resistors help to avoid glitchy behavior with no cable connected or little data activity.
* TX: Meh, a series current limiting resistor is good enough. Drive two outputs differentially to increase the voltage swing. (Technically not true antivalent signals as there's also the "off" state with both disabled.)
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

View File

@ -18,7 +18,13 @@ use work.bench_pkg.all;
library trashernet;
use trashernet.trashernet_pkg.all;
library vunit_lib;
context vunit_lib.vunit_context;
entity bench_trashernet_mac is
generic(
runner_cfg : string
);
end entity bench_trashernet_mac;
architecture bench of bench_trashernet_mac is
@ -37,6 +43,8 @@ architecture bench of bench_trashernet_mac is
constant TEST_BENCH_LOOPBACK : boolean := true;
signal mac_tx_start : std_logic := '0';
begin
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
generic map(
@ -144,6 +152,8 @@ begin
mac_tx : process is
begin
wait on mac_tx_start;
mac_in.tx_mac_data <= x"11";
mac_in.tx_header.mac_destination <= (x"12", x"23", x"34", x"45", x"56", x"67");
mac_in.tx_header.mac_source <= (x"a2", x"a3", x"a4", x"a5", x"a6", x"a7");
@ -159,8 +169,27 @@ begin
wait until rising_edge(mac_out.tx_mac_data_ack);
wait until rising_edge(clk);
mac_in.tx_mac_data_en <= '0';
wait;
end process mac_tx;
test : process is
begin
wait for 1 ns;
test_runner_setup(runner, runner_cfg);
while test_suite loop
if run("mac_tx_rx_simple") then
mac_tx_start <= not mac_tx_start;
wait on mac_out.rx_mac_crc_ok, mac_out.rx_mac_crc_error for 1 ms;
assert mac_out.rx_mac_crc_ok = '1' report "Did not receive CRC OK" severity error;
end if;
end loop;
wait for 1 ns;
wait until rising_edge(clk);
test_runner_cleanup(runner);
end process test;
end architecture bench;

42
bench/pll0.vhd Normal file
View File

@ -0,0 +1,42 @@
-- -------------------------------------------------------------------------- --
-- 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;

19
cocotb/Makefile Normal file
View File

@ -0,0 +1,19 @@
# 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)
include $(shell cocotb-config --makefiles)/Makefile.sim

1
cocotb/cocotb_helpers Submodule

@ -0,0 +1 @@
Subproject commit e2c72ef61d7742fbcb7e048bc928446ed4b56a82

View File

@ -0,0 +1,90 @@
-- -------------------------------------------------------------------------- --
-- 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;

View File

@ -0,0 +1,48 @@
#!/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

View File

@ -0,0 +1,77 @@
-- -------------------------------------------------------------------------- --
-- 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;

123
cocotb/cocovc_eth.vhd Normal file
View File

@ -0,0 +1,123 @@
-- -------------------------------------------------------------------------- --
-- 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;

94
cocotb/hw_itl.py Normal file
View File

@ -0,0 +1,94 @@
#!/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")

5
cocotb/hw_itl.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
echo Hardware in the loop test
make TOPLEVEL=cocotb_top_hwitl MODULE=hw_itl

7
demo/lattice_brevia2/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
impl1/
*.ccl
.recovery
.run_manager.ini
.setting.ini
.spread_sheet.ini
.spreadsheet_view.ini

View File

@ -0,0 +1,112 @@
-- VHDL netlist generated by SCUBA Diamond (64-bit) 3.12.0.240.2
-- Module Version: 5.7
--/usr/local/diamond/3.12/ispfpga/bin/lin64/scuba -w -n pll0 -lang vhdl -synth lse -arch mg5a00 -type pll -fin 50 -phase_cntl STATIC -fclkop 140 -fclkop_tol 0.0 -fb_mode CLOCKTREE -noclkos -noclkok -norst -noclkok2
-- Sat Jan 22 20:39:11 2022
library IEEE;
use IEEE.std_logic_1164.all;
-- synopsys translate_off
library xp2;
use xp2.components.all;
-- synopsys translate_on
entity pll0 is
port(
CLK : in std_logic;
CLKOP : out std_logic;
LOCK : out std_logic);
end pll0;
architecture Structure of pll0 is
-- internal signal declarations
signal CLKOP_t : std_logic;
signal scuba_vlo : std_logic;
-- local component declarations
component VLO
port(Z : out std_logic);
end component;
component EPLLD1
-- synopsys translate_off
generic(CLKOK_BYPASS : in String; CLKOS_BYPASS : in String;
CLKOP_BYPASS : in String; DUTY : in Integer;
PHASEADJ : in String; PHASE_CNTL : in String;
CLKOK_DIV : in Integer; CLKFB_DIV : in Integer;
CLKOP_DIV : in Integer; CLKI_DIV : in Integer);
-- synopsys translate_on
port(CLKI : in std_logic; CLKFB : in std_logic; RST : in std_logic;
RSTK : in std_logic; DPAMODE : in std_logic; DRPAI3 : in std_logic;
DRPAI2 : in std_logic; DRPAI1 : in std_logic; DRPAI0 : in std_logic;
DFPAI3 : in std_logic; DFPAI2 : in std_logic; DFPAI1 : in std_logic;
DFPAI0 : in std_logic; PWD : in std_logic; CLKOP : out std_logic;
CLKOS : out std_logic; CLKOK : out std_logic; LOCK : out std_logic;
CLKINTFB : out std_logic);
end component;
attribute CLKOK_BYPASS : string;
attribute CLKOS_BYPASS : string;
attribute FREQUENCY_PIN_CLKOP : string;
attribute CLKOP_BYPASS : string;
attribute PHASE_CNTL : string;
attribute DUTY : string;
attribute PHASEADJ : string;
attribute FREQUENCY_PIN_CLKI : string;
attribute CLKOK_DIV : string;
attribute CLKOP_DIV : string;
attribute CLKFB_DIV : string;
attribute CLKI_DIV : string;
attribute FIN : string;
attribute CLKOK_BYPASS of PLLInst_0 : label is "DISABLED";
attribute CLKOS_BYPASS of PLLInst_0 : label is "DISABLED";
attribute FREQUENCY_PIN_CLKOP of PLLInst_0 : label is "140.000000";
attribute CLKOP_BYPASS of PLLInst_0 : label is "DISABLED";
attribute PHASE_CNTL of PLLInst_0 : label is "STATIC";
attribute DUTY of PLLInst_0 : label is "8";
attribute PHASEADJ of PLLInst_0 : label is "0.0";
attribute FREQUENCY_PIN_CLKI of PLLInst_0 : label is "50.000000";
attribute CLKOK_DIV of PLLInst_0 : label is "2";
attribute CLKOP_DIV of PLLInst_0 : label is "4";
attribute CLKFB_DIV of PLLInst_0 : label is "14";
attribute CLKI_DIV of PLLInst_0 : label is "5";
attribute FIN of PLLInst_0 : label is "50.000000";
attribute syn_keep : boolean;
attribute NGD_DRC_MASK : integer;
attribute NGD_DRC_MASK of Structure : architecture is 1;
begin
-- component instantiation statements
scuba_vlo_inst : VLO
port map(Z => scuba_vlo);
PLLInst_0 : EPLLD1
-- synopsys translate_off
generic map(CLKOK_BYPASS => "DISABLED", CLKOS_BYPASS => "DISABLED",
CLKOP_BYPASS => "DISABLED", PHASE_CNTL => "STATIC", DUTY => 8,
PHASEADJ => "0.0", CLKOK_DIV => 2, CLKOP_DIV => 4, CLKFB_DIV => 14,
CLKI_DIV => 5)
-- synopsys translate_on
port map(CLKI => CLK, CLKFB => CLKOP_t, RST => scuba_vlo,
RSTK => scuba_vlo, DPAMODE => scuba_vlo, DRPAI3 => scuba_vlo,
DRPAI2 => scuba_vlo, DRPAI1 => scuba_vlo, DRPAI0 => scuba_vlo,
DFPAI3 => scuba_vlo, DFPAI2 => scuba_vlo, DFPAI1 => scuba_vlo,
DFPAI0 => scuba_vlo, PWD => scuba_vlo, CLKOP => CLKOP_t,
CLKOS => open, CLKOK => open, LOCK => LOCK, CLKINTFB => open);
CLKOP <= CLKOP_t;
end Structure;
-- synopsys translate_off
library xp2;
configuration Structure_CON of pll0 is
for Structure
for all : VLO
use entity xp2.VLO(V);
end for;
for all : EPLLD1
use entity xp2.EPLLD1(V);
end for;
end for;
end Structure_CON;
-- synopsys translate_on

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<BaliProject version="3.2" title="mac_test" device="LFXP2-5E-6TN144C" default_implementation="impl1">
<Options/>
<Implementation title="impl1" dir="impl1" description="impl1" synthesis="lse" default_strategy="Strategy1">
<Options def_top="trashernet.trashernet_eth" lib="work" top="top_mac_test"/>
<Source name="../ip/pll0.vhd" type="VHDL" type_short="VHDL">
<Options/>
</Source>
<Source name="../../../design/top_mac_test.vhd" type="VHDL" type_short="VHDL">
<Options top_module="top_mac_test"/>
</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="impl1/impl1.xcf" type="Programming Project File" type_short="Programming">
<Options/>
</Source>
<Source name="mac_test.lpf" type="Logic Preference" type_short="LPF">
<Options/>
</Source>
</Implementation>
<Strategy name="Strategy1" file="mac_test1.sty"/>
</BaliProject>

View File

@ -0,0 +1,47 @@
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" ;

View File

@ -0,0 +1,206 @@
<?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>

View File

@ -0,0 +1,68 @@
<?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>

View File

@ -0,0 +1,47 @@
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" ;

View File

@ -0,0 +1,206 @@
<?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>

77
design/ledcon.vhd Normal file
View File

@ -0,0 +1,77 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- ledcon.vhd : Simple debug LED helper
-- To blink or not blink.
-- -------------------------------------------------------------------------- --
-- 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;
entity ledcon is
generic(
F_CLK : integer;
BLINK : boolean_vector
);
port(
clk : in std_logic;
rst : in std_logic;
sig : in std_logic_vector;
led_n : out std_logic_vector
);
end entity ledcon;
architecture RTL of ledcon is
constant MS_CNT_MAX : integer := integer(round(real(F_CLK) * (1.0 * 10.0 ** (-3))));
signal ms_cnt : integer range 0 to MS_CNT_MAX;
signal ms_tick : std_logic;
begin
mstimer : process(clk, rst) is
begin
if rst then
ms_cnt <= MS_CNT_MAX;
elsif rising_edge(clk) then
if (ms_cnt = 0) then
ms_cnt <= MS_CNT_MAX;
else
ms_cnt <= ms_cnt - 1;
end if;
end if;
end process mstimer;
ms_tick <= '1' when ms_cnt = 0 else '0';
ledcongen : for i in sig'low to sig'high generate
ledblock : block
constant LED_CNT_MAX : integer := 50; -- on time, in ms
constant LED_CNT_MIN : integer := -50; -- off time, in ms
signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX;
begin
ledgen : if BLINK(i) generate
ledproc : process(clk, rst) is
begin
if rst then
led_cnt <= LED_CNT_MIN;
elsif rising_edge(clk) then
if (led_cnt = LED_CNT_MIN) then
if sig(i) then
led_cnt <= LED_CNT_MAX;
end if;
elsif ms_tick then
led_cnt <= led_cnt - 1;
end if;
end if;
end process ledproc;
led_n(i) <= '0' when led_cnt > 0 else '1';
else generate
led_n(i) <= not sig(i);
end generate ledgen;
end block ledblock;
end generate ledcongen;
end architecture RTL;

445
design/top_hwitl.vhd Normal file
View File

@ -0,0 +1,445 @@
-- -------------------------------------------------------------------------- --
-- 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;

208
design/top_mac_test.vhd Normal file
View File

@ -0,0 +1,208 @@
-- -------------------------------------------------------------------------- --
-- 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_mac_test 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_mac_test;
architecture rtl of top_mac_test 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"33")
);
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;
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;
signal button_n_sync : std_logic_vector(button_n'range);
signal button : std_logic_vector(button_n'range);
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
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 => 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;
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) or (button(0) = '1') 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 rtl;

248
design/top_pingable.vhd Normal file
View File

@ -0,0 +1,248 @@
-- -------------------------------------------------------------------------- --
-- 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;

3039
doc/circuit.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 229 KiB

2016
doc/hardware.kicad_sch Normal file

File diff suppressed because it is too large Load Diff

16
run.py Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env python
from vunit import VUnit
import os
library_names = ["trashernet", "bench"]
libs = {}
vu = VUnit.from_argv()
for library_name in library_names:
libs[library_name] = vu.add_library(library_name)
libs[library_name].add_source_files(os.path.join(library_name, "*.vhd"))
vu.main()

View File

@ -54,8 +54,7 @@ begin
);
-- strobify
b_toggle_last <= '0' when b_rst
else b_toggle when rising_edge(b_clk)
;
b_toggle_last <= '0' when b_rst else
b_toggle when rising_edge(b_clk);
b_out <= (b_toggle ?/= b_toggle_last);
end architecture rtl;

98
trashernet/fifo.vhd Normal file
View File

@ -0,0 +1,98 @@
-- -------------------------------------------------------------------------- --
-- 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;

64
trashernet/timer.vhd Normal file
View File

@ -0,0 +1,64 @@
-- -------------------------------------------------------------------------- --
-- 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;

View File

@ -0,0 +1,317 @@
-- -------------------------------------------------------------------------- --
-- 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;

View File

@ -0,0 +1,176 @@
-- -------------------------------------------------------------------------- --
-- 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;

View File

@ -0,0 +1,172 @@
-- -------------------------------------------------------------------------- --
-- 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;

View File

@ -0,0 +1,249 @@
-- -------------------------------------------------------------------------- --
-- 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;

View File

@ -0,0 +1,122 @@
-- -------------------------------------------------------------------------- --
-- 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;

View File

@ -79,6 +79,7 @@ begin
mac_out.rx_mac_crc_ok <= '0';
mac_out.rx_mac_header_rcv <= '0';
crc_clear <= '0';
state <= HEAD;
elsif rising_edge(clk) then
mac_out.rx_mac_valid <= '0';
@ -101,7 +102,7 @@ begin
byte_count <= byte_count + 1;
end if;
end if;
when PAYLOAD =>
when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error)
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
@ -165,13 +166,18 @@ begin
);
tx_main : process(clk, rst) is
variable more_data : std_logic;
begin
if rst then
mac_out.tx_mac_data_ack <= '0';
tx_state <= IDLE;
sr_cnt <= 0;
byte_cnt <= 0;
elsif rising_edge(clk) then
mac_out.tx_mac_data_ack <= '0';
crc_valid <= '0';
more_data := more_data and mac_in.tx_mac_data_en; -- Latch a short low-pulse on tx_en
if phy_out.tx_data_ack then
sr <= sr(sr'low + 1 to sr'high) & x"00";
@ -186,6 +192,7 @@ begin
case tx_state is
when IDLE => -- Idle + prepare sync header
if not phy_out.tx_active and mac_in.tx_mac_data_en then
more_data := '1';
sr(0 to 7) <= byte_vector'(0 to 6 => x"55", 7 => x"D5");
sr_cnt <= 7;
tx_state <= HEADER;
@ -202,7 +209,7 @@ begin
when DATA => -- Wait for MAC header + transmit payload until tx_mac_data_en goes away
if (phy_out.tx_data_ack = '1') and (sr_cnt = 0) then -- MAC Header TX complete
if mac_in.tx_mac_data_en then
if more_data then
sr(0) <= mac_in.tx_mac_data;
mac_out.tx_mac_data_ack <= '1';
else

View File

@ -39,8 +39,31 @@ end entity trashernet_phy;
architecture rtl of trashernet_phy is
constant F_ETH : integer := 10000000;
signal tick_ms : std_logic; -- 1 ms tick (for NLP)
begin
common : block
constant TICK_MS_CNT_MAX : integer := (F_CLK / 1000) - 1;
signal tick_ms_count : integer range 0 to TICK_MS_CNT_MAX;
begin
mstick : process(clk, rst) is
begin
if rst then
tick_ms_count <= TICK_MS_CNT_MAX;
tick_ms <= '0';
elsif rising_edge(clk) then
tick_ms <= '0';
if tick_ms_count = 0 then
tick_ms <= '1';
tick_ms_count <= TICK_MS_CNT_MAX;
else
tick_ms_count <= tick_ms_count - 1;
end if;
end if;
end process mstick;
end block common;
receiver : block
-- Signal conditioning
signal rx : std_logic;
@ -56,7 +79,7 @@ begin
signal bit_cnt : integer range 0 to 7;
-- NLP supervision
constant NLP_TIMEOUT_CNT_MAX : integer := integer(round(real(F_CLK) * 16.0 * 10.0**(-3))); -- Every 16 ms
constant NLP_TIMEOUT_CNT_MAX : integer := (16 + 8) - 1; -- Every 16 ms (timebase 1 ms)
signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX;
begin
-- Synchronize RX input
@ -106,6 +129,8 @@ begin
-- Bit recovery
type demanchestization_state_t is (SYNC, DATA, ERROR);
signal demanchestization_state : demanchestization_state_t;
constant DEMANCHESTIZATION_MIN_SYNC_CNT_MAX : integer := (4 * 8) - 1; -- 4 good sync bytes
signal demanchestization_min_sync_cnt : integer range 0 to DEMANCHESTIZATION_MIN_SYNC_CNT_MAX;
begin
-- Detects spacing of transitions
transition_detector : process(clk, rst) is
@ -181,18 +206,27 @@ begin
bit_stb <= '0';
phy_out.rx_active <= '0';
phy_out.rx_error <= '0';
demanchestization_min_sync_cnt <= DEMANCHESTIZATION_MIN_SYNC_CNT_MAX;
elsif rising_edge(clk) then
bit_stb <= '0';
phy_out.rx_error <= '0';
if (bit_ev = TOGGLE) and (demanchestization_min_sync_cnt /= 0) then
demanchestization_min_sync_cnt <= demanchestization_min_sync_cnt - 1;
elsif (bit_ev = KEEP) or (bit_ev = ERROR) then
demanchestization_min_sync_cnt <= DEMANCHESTIZATION_MIN_SYNC_CNT_MAX;
end if;
if (bit_ev /= NONE) then
case demanchestization_state is
when SYNC =>
if (bit_ev = KEEP) then
if demanchestization_min_sync_cnt = 0 then
bit_value <= '1';
demanchestization_state <= DATA;
phy_out.rx_active <= '1';
end if;
end if;
when DATA => -- @suppress: Condition outside of case allows to exit this state
bit_value <= not bit_value when bit_ev = TOGGLE else bit_value;
bit_stb <= '1';
@ -202,7 +236,7 @@ begin
end if;
if (bit_ev = ERROR) then
phy_out.rx_error <= '1';
phy_out.rx_error <= '1' when (demanchestization_state = DATA) else '0';
demanchestization_state <= ERROR;
end if;
@ -219,6 +253,7 @@ 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';
@ -248,11 +283,13 @@ begin
if rx_edge then -- Technically, we should use only the rising edge here, but a project called `trashernet` probably won't mind ;)
nlp_timeout_cnt <= NLP_TIMEOUT_CNT_MAX;
else
if (nlp_timeout_cnt /= 0) then
if tick_ms then
if nlp_timeout_cnt /= 0 then
nlp_timeout_cnt <= nlp_timeout_cnt - 1;
end if;
end if;
end if;
end if;
end process nlp_timeout_p;
phy_out.carrier_detect <= '1' when nlp_timeout_cnt /= 0 else '0';
end block receiver;
@ -260,7 +297,10 @@ begin
-- -------------------------------------------------------------------------
transmitter : block
constant TX_STB_CNT_MAX : integer := F_CLK / (F_ETH * 2) - 1;
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
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
type tx_state_t is (IDLE, NLP, TX, IPG);
@ -268,10 +308,11 @@ begin
signal sr : std_logic_vector(phy_in.tx_data'range);
signal bit_stage : std_logic;
constant BIT_CNT_MAX_NLP : integer := 16000 / 100; -- 16 ms (timebase 100 ns)
constant BIT_CNT_MAX_IPG : integer := 96;
constant BIT_CNT_MAX_DATA : integer := sr'length - 1;
signal bit_cnt : integer range 0 to maximum(maximum(BIT_CNT_MAX_NLP, BIT_CNT_MAX_DATA), BIT_CNT_MAX_IPG);
signal bit_cnt : integer range 0 to maximum(BIT_CNT_MAX_IPG, BIT_CNT_MAX_DATA);
constant NLP_CNT_MAX : integer := 15; -- specced 16 ms, but there's margin, so let's choose 15 to save a bit (timebase 1 ms)
signal nlp_cnt : integer range 0 to NLP_CNT_MAX;
type tx_mode_t is (OFF, NLP, ACTIVE);
signal tx_mode : tx_mode_t;
@ -308,24 +349,31 @@ begin
tx_state <= IDLE;
tx_mode <= OFF;
bit_cnt <= BIT_CNT_MAX_NLP;
nlp_cnt <= NLP_CNT_MAX;
bit_stage <= '0';
end procedure go_idle;
variable tx_stb : std_logic; -- Strobe every 50 ns (20 MHz)
variable more_data : std_logic; -- tx_data_en low-latched
begin
if rst then
phy_out.tx_data_ack <= '0';
tx_stb_cnt <= TX_STB_CNT_MAX;
tx_mode <= OFF;
tx_state <= IDLE;
bit_cnt <= 0;
go_idle;
elsif rising_edge(clk) then
phy_out.tx_data_ack <= '0';
more_data := more_data and phy_in.tx_data_en; -- Latch a short low-pulse on tx_en
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;
@ -339,15 +387,20 @@ begin
bit_cnt <= bit_cnt - 1;
end if;
end if;
if tick_ms then
if (nlp_cnt /= 0) then
nlp_cnt <= nlp_cnt - 1;
end if;
end if;
case tx_state is
when IDLE =>
if phy_in.tx_data_en then -- New packet to TX
transmit_byte;
more_data := '1';
bit_stage <= '1';
tx_stb_cnt <= TX_STB_CNT_MAX; -- resync
elsif bit_cnt = 0 then -- NLP timeout
elsif nlp_cnt = 0 then -- NLP timeout
transmit_nlp;
--bit_cnt <= 1; Let's save some resources here...
@ -363,7 +416,7 @@ begin
if (bit_stage = '1') then
sr <= '0' & sr(sr'high downto sr'low + 1);
if bit_cnt = 0 then
if phy_in.tx_data_en then
if more_data then
transmit_byte;
else
transmit_ipg;
@ -378,7 +431,7 @@ begin
end case;
end if;
end process tx_main;
phy_out.tx_active <= '1' when tx_state /= IDLE else '0';
phy_out.tx_active <= '1' when (tx_state /= IDLE and tx_state /= NLP) else '0';
driver : process(clk, rst) is
begin
@ -394,8 +447,8 @@ begin
tx_p <= '1';
tx_n <= '0';
when ACTIVE =>
tx_p <= sr(sr'low) xor bit_stage;
tx_n <= not (sr(sr'low) xor bit_stage);
tx_p <= sr(sr'low) xnor bit_stage;
tx_n <= not (sr(sr'low) xnor bit_stage);
end case;
end if;
end process driver;

View File

@ -44,12 +44,14 @@ architecture rtl of trashernet_phy_cdc is
signal phy_phy_in : phy_in_t;
-- Helper signals
signal rx_data_valid_i : std_logic;
signal rx_data_i : byte;
begin
-- -------------------------------------------------------------------------
-- Drives: PHY clock domain
-- -------------------------------------------------------------------------
-- Reset synchronizer for PHY
rstsync : process(phy_clk, rst) is
begin
if rst then
@ -59,6 +61,7 @@ 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
@ -73,6 +76,22 @@ 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
@ -96,18 +115,10 @@ begin
a_in => phy_phy_out.rx_data_valid,
b_clk => clk,
b_rst => rst,
b_out => rx_data_valid_i
b_out => phy_out.rx_data_valid
);
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;
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
cdc_strobe_rxer_inst : entity work.cdc_strobe
port map(

View File

@ -10,6 +10,7 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package trashernet_pkg is
-- General types
@ -18,6 +19,8 @@ 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
@ -28,6 +31,17 @@ 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
@ -65,8 +79,149 @@ 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;