Compare commits

...

10 Commits

Author SHA1 Message Date
a15fb1f389 wip: eth 2022-04-30 12:54:30 +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
22 changed files with 3025 additions and 0 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>

11
.gitignore vendored
View File

@ -1 +1,12 @@
# VUnit
vunit_out 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

@ -30,3 +30,12 @@ Notes:
## License ## License
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
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

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,77 @@
-- -------------------------------------------------------------------------- --
-- 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 (mac) -- 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
);
button_n <= (others => '1');
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;

79
cocotb/hw_itl.py Normal file
View File

@ -0,0 +1,79 @@
#!/usr/bin/env python
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
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()
print("Setting IF to promisc mode...")
os.system("ip link set promisc on dev {}".format(dev))
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)
async def main(self):
ETH_HEAD = b'\x55\x55\x55\x55\xD5'
while True:
try:
r = self.macdev.recv(2000)
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.info("RX Frame: " + 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, "virt0")
# 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

205
design/top_hwitl.vhd Normal file
View File

@ -0,0 +1,205 @@
-- -------------------------------------------------------------------------- --
-- 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);
button_n : in std_logic_vector(3 downto 0);
debug_data : out std_logic_vector(7 downto 0)
);
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 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 mac;

2016
doc/hardware.kicad_sch Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,152 @@
-- -------------------------------------------------------------------------- --
-- 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
begin
rx : block
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
signal sel : integer range SEL_ETH_I to ethernet_ii_out'high;
signal mac_destination_matches : std_logic;
begin
mac_destination_matches <= '1' when --
((mac_out.rx_header.mac_destination = (x"FF", x"FF")) or --
(mac_out.rx_header.mac_destination = config.mac_address)) else
'0';
mux : process(clk, rst) is
begin
if rst then
elsif rising_edge(clk) then
if mac_out.rx_mac_header_rcv then
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;
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_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));
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
elsif rising_edge(clk) then
case state is
when IDLE =>
if 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 (mac_in.tx_mac_data_en or mac_out.tx_active);
end case;
end if;
end process arb;
mux : process(all) is
begin
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;
ethernet_i_out.tx_data_ack <= 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
ethernet_ii_out(i).tx_data_ack <= 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

@ -10,6 +10,7 @@
library ieee; library ieee;
use ieee.std_logic_1164.all; use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package trashernet_pkg is package trashernet_pkg is
-- General types -- General types
@ -29,6 +30,11 @@ package trashernet_pkg is
-- IP specific types -- IP specific types
subtype ip_addr_t is byte_vector(0 to 3); subtype ip_addr_t is byte_vector(0 to 3);
-- Configuration interface
type configuration_t is record
mac_address : mac_addr_t; -- MAC address of this node
end record configuration_t;
-- PHY interface -- PHY interface
type phy_in_t is record type phy_in_t is record
tx_data : byte; -- TX Data tx_data : byte; -- TX Data
@ -65,6 +71,42 @@ package trashernet_pkg is
rx_mac_crc_error : std_logic; -- End of packet, CRC invalid rx_mac_crc_error : std_logic; -- End of packet, CRC invalid
end record mac_out_t; end record mac_out_t;
-- 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"))); -- NULL range
type ethernet_i_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
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
rx_length : unsigned(15 downto 0); -- RX payload length in bytes
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
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');
end package trashernet_pkg; end package trashernet_pkg;
package body trashernet_pkg is package body trashernet_pkg is