Compare commits

...

10 Commits

Author SHA1 Message Date
Markus Koch a15fb1f389 wip: eth 2022-04-30 12:54:30 +02:00
Markus Koch fd721ae24f bench: Move HWITL tests to own top level / test bench file 2022-04-30 12:53:35 +02:00
Markus Koch 82d4e6808b bench: hwitl: Fix design to HW comms
Forgot to remove the sync header.
2022-04-30 12:48:39 +02:00
Markus Koch 1c281bc0f7 bench: Add hardware-in-the-loop test 2022-04-29 20:53:20 +02:00
Markus Koch c4f4cfe68e bench: Allow for different bench top levels in Makefile 2022-04-29 20:52:31 +02:00
Markus Koch f224aee487 doc: Add licensing info about the components of the logo 2022-04-24 12:26:19 +02:00
Markus Koch f45ded2ca9 bench: Add cocotb-based test bench for MAC test 2022-04-18 17:06:53 +02:00
Markus Koch 7d86f6a262 env: Add Sigasi project files 2022-04-18 10:17:42 +02:00
Markus Koch 313711dbc0 doc: Add KiCad schematic 2022-04-18 10:04:24 +02:00
Markus Koch 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
# 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
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;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package trashernet_pkg is
-- General types
@ -29,6 +30,11 @@ package trashernet_pkg is
-- IP specific types
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
type phy_in_t is record
tx_data : byte; -- TX Data
@ -65,6 +71,42 @@ 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"))); -- 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;
package body trashernet_pkg is