Compare commits
10 Commits
6900a41528
...
a15fb1f389
Author | SHA1 | Date | |
---|---|---|---|
a15fb1f389 | |||
fd721ae24f | |||
82d4e6808b | |||
1c281bc0f7 | |||
c4f4cfe68e | |||
f224aee487 | |||
f45ded2ca9 | |||
7d86f6a262 | |||
313711dbc0 | |||
0f7437e901 |
@ -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
11
.gitignore
vendored
@ -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
3
.gitmodules
vendored
Normal 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
35
.library_mapping.xml
Normal 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
66
.project
Normal 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><project>/.externalToolBuilders/com.sigasi.hdt.toolchains.vunit.builder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature>
|
||||
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
|
||||
<nature>com.sigasi.hdt.toolchains.vunit.nature</nature>
|
||||
</natures>
|
||||
<linkedResources>
|
||||
<link>
|
||||
<name>Common Libraries</name>
|
||||
<type>2</type>
|
||||
<locationURI>virtual:/virtual</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/DRAG_REUSABLE_LIBRARIES_HERE.txt</name>
|
||||
<type>1</type>
|
||||
<locationURI>sigasiresource:/vhdl/readme.txt</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/IEEE</name>
|
||||
<type>2</type>
|
||||
<locationURI>sigasiresource:/vhdl/2008/IEEE</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/IEEE Synopsys</name>
|
||||
<type>2</type>
|
||||
<locationURI>sigasiresource:/vhdl/2008/IEEE%20Synopsys</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/STD</name>
|
||||
<type>2</type>
|
||||
<locationURI>sigasiresource:/vhdl/2008/STD</locationURI>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/src</name>
|
||||
<type>2</type>
|
||||
<location>/usr/local/diamond/3.12/cae_library/simulation/vhdl/xp2/src</location>
|
||||
</link>
|
||||
<link>
|
||||
<name>Common Libraries/vunit</name>
|
||||
<type>2</type>
|
||||
<locationURI>PARENT-1-PROJECT_LOC/vunit</locationURI>
|
||||
</link>
|
||||
</linkedResources>
|
||||
</projectDescription>
|
2
.settings/com.sigasi.hdt.toolchains.vunit.prefs
Normal file
2
.settings/com.sigasi.hdt.toolchains.vunit.prefs
Normal file
@ -0,0 +1,2 @@
|
||||
VUnitScriptLocation=vunit/run.py
|
||||
eclipse.preferences.version=1
|
1
.settings/com.sigasi.hdt.vhdl.version.prefs
Normal file
1
.settings/com.sigasi.hdt.vhdl.version.prefs
Normal file
@ -0,0 +1 @@
|
||||
<project>=2008
|
5
.settings/org.eclipse.core.resources.prefs
Normal file
5
.settings/org.eclipse.core.resources.prefs
Normal 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
|
@ -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
42
bench/pll0.vhd
Normal 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
19
cocotb/Makefile
Normal 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
1
cocotb/cocotb_helpers
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit e2c72ef61d7742fbcb7e048bc928446ed4b56a82
|
77
cocotb/cocotb_top_hwitl.vhd
Normal file
77
cocotb/cocotb_top_hwitl.vhd
Normal 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;
|
48
cocotb/cocotb_top_mac_test.py
Normal file
48
cocotb/cocotb_top_mac_test.py
Normal 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
|
77
cocotb/cocotb_top_mac_test.vhd
Normal file
77
cocotb/cocotb_top_mac_test.vhd
Normal 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
123
cocotb/cocovc_eth.vhd
Normal 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
79
cocotb/hw_itl.py
Normal 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
5
cocotb/hw_itl.sh
Executable 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
205
design/top_hwitl.vhd
Normal 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
2016
doc/hardware.kicad_sch
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,8 @@
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
|
||||
-- -------------------------------------------------------------------------- --
|
||||
-- trashernet_mac_top.vhd : Ethernet OSI Layer 2, Data Link, filtered
|
||||
-- Implements header creation, parsing, and protocol arbitration.
|
||||
-- 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
|
||||
@ -17,8 +17,6 @@ use work.trashernet_pkg.all;
|
||||
|
||||
entity trashernet_eth is
|
||||
generic(
|
||||
F_CLK : in integer; -- Clock frequency of design IF
|
||||
F_CLK_PHY : in integer; -- PHY clock (expected to be faster than F_CLK)
|
||||
ETHERNET_II_PROTOCOLS : ethernet_ii_protocol_vector := ETHERNET_II_PROTOCOLS_NONE
|
||||
);
|
||||
port(
|
||||
@ -46,7 +44,8 @@ end entity trashernet_eth;
|
||||
architecture rtl of trashernet_eth is
|
||||
begin
|
||||
rx : block
|
||||
signal sel : integer range ethernet_ii_out'low to ethernet_ii_out'high;
|
||||
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
|
||||
|
||||
@ -63,7 +62,7 @@ begin
|
||||
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
|
||||
null; -- TODO: Not yet implemented
|
||||
sel <= SEL_ETH_I;
|
||||
|
||||
else -- Ethernet II Frame
|
||||
for i in ETHERNET_II_PROTOCOLS'range loop
|
||||
@ -77,18 +76,26 @@ begin
|
||||
end if;
|
||||
end process mux;
|
||||
|
||||
-- Shared fields in the Ethernet application interface
|
||||
-- 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;
|
||||
signal sel : integer range ethernet_ii_in'low to ethernet_ii_in'high;
|
||||
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
|
||||
@ -98,6 +105,10 @@ begin
|
||||
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;
|
||||
@ -106,6 +117,7 @@ begin
|
||||
end if;
|
||||
end loop;
|
||||
end if;
|
||||
end if;
|
||||
|
||||
when TXD =>
|
||||
state <= IDLE when (mac_in.tx_mac_data_en or mac_out.tx_active);
|
||||
@ -113,15 +125,27 @@ begin
|
||||
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 generate
|
||||
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 generate txack;
|
||||
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;
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
use ieee.numeric_std.all;
|
||||
|
||||
package trashernet_pkg is
|
||||
-- General types
|
||||
@ -78,12 +79,19 @@ package trashernet_pkg is
|
||||
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
|
||||
tbd : std_logic; -- TODO
|
||||
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
|
||||
tbd : std_logic; -- TODO
|
||||
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 := (others => '0'); -- TODO
|
||||
constant ETHERNET_I_IN_UNUSED : ethernet_i_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", tx_length => x"0000", others => '0'); -- TODO
|
||||
|
||||
type ethernet_ii_out_t is record
|
||||
rx_mac_address : mac_addr_t; -- Source MAC address
|
||||
|
Loading…
Reference in New Issue
Block a user