Compare commits

...

13 Commits

Author SHA1 Message Date
9258edd0d9 eth+ipv4prot: Fix protocol arbitration
Fixes #13.
2025-09-02 12:38:46 +02:00
f62a464e7e ipv4: Implement gateway handling
Closes #17.
2025-09-02 12:33:23 +02:00
9f0f8c2775 cocotb: Add configuration 2025-09-02 12:00:26 +02:00
6dc42267fa cocotb: Add script to configure virtual network devices for cocotb test bench 2025-09-02 12:00:07 +02:00
1ed42918fc Add Python requirements.txt 2025-09-02 09:45:53 +02:00
f67e8d8b37 Reformat code using latest version of Sigasi 2025-09-02 09:45:30 +02:00
154604aacd udp: Add UDP decoder 2025-09-02 09:35:14 +02:00
02a00e875d ipv4: Implement tx_ok_stb 2025-09-02 09:31:38 +02:00
7379df7b74 phy: Improve clock accuracy at 0.5 dividers at the cost of some jitter 2024-07-15 13:34:54 +02:00
21a0d0e69a phy_cdc: Allow unlimited clock ratios
Actually, assuming that each clock domain is fast enough for 10 MBit/s,
there is no problem running crazy ratios.
2024-07-06 18:05:00 +02:00
943febcb99 phy_cdc: Fix synchronization behavior
For low ratios of the two clock domains, the slow clock domain would
receive incorrect data as the fast (PHY) clock domain's data output
would have already updated with the first bit of the next byte by
the time the slow (system) clock domain read it.

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

Additionally, `phy_out.rx_data` and `phy_out.rx_data_valid` are no
longer registered in the destination (system) clock domain as this
now introduces unnecessary latency. Also, this was broken the way
it was anyway as `rx_active` and `rx_error` would've also had to be
registered to keep all timings in sync.
2024-07-06 17:58:11 +02:00
da7e329939 des: Fix compatibility with Yosys 2024-06-22 11:19:02 +02:00
4bce111d69 doc: Add synthesized size for PHY-only design 2024-02-04 09:34:36 +01:00
29 changed files with 496 additions and 249 deletions

View File

@ -1,35 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?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"> <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="bench" Library="bench"/>
<Mappings Location="cocotb" Library="cocotb"/>
<Mappings Location="design" Library="design"/> <Mappings Location="design" Library="design"/>
<Mappings Location="Common Libraries/IEEE" Library="ieee"/> <Mappings Location="Common Libraries/IEEE" Library="ieee"/>
<Mappings Location="Common Libraries/IEEE Synopsys" 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" 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="Common Libraries/STD" Library="std"/>
<Mappings Location="trashernet" Library="trashernet"/> <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> </com.sigasi.hdt.shared.librarymapping.model:LibraryMappings>

View File

@ -5,16 +5,6 @@
<projects> <projects>
</projects> </projects>
<buildSpec> <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> <buildCommand>
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name> <name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
<arguments> <arguments>
@ -22,9 +12,9 @@
</buildCommand> </buildCommand>
</buildSpec> </buildSpec>
<natures> <natures>
<nature>com.sigasi.hdt.verilog.ui.verilogNature</nature>
<nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature> <nature>com.sigasi.hdt.vhdl.ui.vhdlNature</nature>
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature> <nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
<nature>com.sigasi.hdt.toolchains.vunit.nature</nature>
</natures> </natures>
<linkedResources> <linkedResources>
<link> <link>
@ -32,11 +22,6 @@
<type>2</type> <type>2</type>
<locationURI>virtual:/virtual</locationURI> <locationURI>virtual:/virtual</locationURI>
</link> </link>
<link>
<name>Common Libraries/DRAG_REUSABLE_LIBRARIES_HERE.txt</name>
<type>1</type>
<locationURI>sigasiresource:/vhdl/readme.txt</locationURI>
</link>
<link> <link>
<name>Common Libraries/IEEE</name> <name>Common Libraries/IEEE</name>
<type>2</type> <type>2</type>
@ -52,15 +37,5 @@
<type>2</type> <type>2</type>
<locationURI>sigasiresource:/vhdl/2008/STD</locationURI> <locationURI>sigasiresource:/vhdl/2008/STD</locationURI>
</link> </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> </linkedResources>
</projectDescription> </projectDescription>

View File

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

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
isEnabled=true

View File

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

View File

@ -0,0 +1,2 @@
eclipse.preferences.version=1
isEnabled=true

View File

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

View File

@ -28,6 +28,14 @@ Notes:
* The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice! * The RX pull up/downs are probably not the best idea, and fixable in a better way. But for now, this shall work -- and suffice!
* On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D * On the TX side, I'm grossly overloading the drivers. A line driver would probably be a good idea. Or you can just parallel multiple outputs ;D
## Stats
Sythesized for a Lattice LFXP2-5E FPGA using LSE / Diamond v3.12:
| Configuration | Slices | LUT4 | Registers |
| ---------------- | ------ | ---- | --------- |
| `trashernet_phy` | 115 | 188 | 91 |
## License ## 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.

View File

@ -19,12 +19,12 @@ end entity bench_crc;
architecture bench of bench_crc is architecture bench of bench_crc is
constant POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7"; constant POLYNOMIAL : std_logic_vector(31 downto 0) := x"04C11DB7";
signal clk : std_logic; signal clk : std_logic;
signal rst : std_logic; signal rst : std_logic;
signal data : std_logic_vector(7 downto 0); signal data : std_logic_vector(7 downto 0);
signal data_valid : std_logic; signal data_valid : std_logic;
signal crc_clear : std_logic; signal crc_clear : std_logic;
signal crc_out : std_logic_vector(POLYNOMIAL'range); signal crc_out : std_logic_vector(POLYNOMIAL'range);
begin begin
crc_inst : entity trashernet.crc crc_inst : entity trashernet.crc
generic map( generic map(

View File

@ -30,7 +30,7 @@ package body bench_pkg is
wait for 50 ns; wait for 50 ns;
end loop; end loop;
end loop; end loop;
wait for 16 us; -- IPG wait for 16 us; -- IPG
report "Frame TX complete"; report "Frame TX complete";
end procedure send_data; end procedure send_data;
end package body bench_pkg; end package body bench_pkg;

View File

@ -22,15 +22,14 @@ entity bench_trashernet_phy is
end entity bench_trashernet_phy; end entity bench_trashernet_phy;
architecture bench of bench_trashernet_phy is architecture bench of bench_trashernet_phy is
signal clk : std_logic; signal clk : std_logic;
signal phy_clk : std_logic; signal phy_clk : std_logic;
signal rst : std_logic; signal rst : std_logic;
signal phy_out : phy_out_t; signal phy_out : phy_out_t;
signal phy_in : phy_in_t; signal phy_in : phy_in_t;
signal rx_p : std_logic; signal rx_p : std_logic;
signal tx_p : std_logic; signal tx_p : std_logic;
signal tx_n : std_logic; signal tx_n : std_logic;
begin begin
trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc trashernet_phy_cdc_inst : entity trashernet.trashernet_phy_cdc
@ -39,14 +38,14 @@ begin
F_CLK_PHY => 100000000 F_CLK_PHY => 100000000
) )
port map( port map(
clk => clk, clk => clk,
phy_clk => phy_clk, phy_clk => phy_clk,
rst => rst, rst => rst,
phy_out => phy_out, phy_out => phy_out,
phy_in => phy_in, phy_in => phy_in,
rx_p => rx_p, rx_p => rx_p,
tx_p => tx_p, tx_p => tx_p,
tx_n => tx_n tx_n => tx_n
); );
clock_driver : process clock_driver : process

View File

@ -0,0 +1,7 @@
configuration cfg_cocotb_top_hwitl_mac of cocotb_top_hwitl is
for bench
for top_mac_test_inst : top_hwitl
use entity design.top_hwitl(mac);
end for;
end for;
end configuration cfg_cocotb_top_hwitl_mac;

8
cocotb/configure_network.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
# Set up virtual device using
sudo ip link add dev veth1 type veth peer name veth2
sudo ip link set up veth1
sudo ip link set up veth2
sudo ip addr add 192.168.2.1/24 dev veth1
sudo ip link set promisc on dev veth1

View File

@ -29,8 +29,8 @@ end entity ledcon;
architecture RTL of ledcon is architecture RTL of ledcon is
constant MS_CNT_MAX : integer := integer(round(real(F_CLK) * (1.0 * 10.0 ** (-3)))); constant MS_CNT_MAX : integer := integer(round(real(F_CLK) * (1.0 * 10.0 ** (-3))));
signal ms_cnt : integer range 0 to MS_CNT_MAX; signal ms_cnt : integer range 0 to MS_CNT_MAX;
signal ms_tick : std_logic; signal ms_tick : std_logic;
begin begin
mstimer : process(clk, rst) is mstimer : process(clk, rst) is
begin begin
@ -50,7 +50,7 @@ begin
ledblock : block ledblock : block
constant LED_CNT_MAX : integer := 50; -- on time, in ms constant LED_CNT_MAX : integer := 50; -- on time, in ms
constant LED_CNT_MIN : integer := -50; -- off time, in ms constant LED_CNT_MIN : integer := -50; -- off time, in ms
signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX; signal led_cnt : integer range LED_CNT_MIN to LED_CNT_MAX;
begin begin
ledgen : if BLINK(i) generate ledgen : if BLINK(i) generate
ledproc : process(clk, rst) is ledproc : process(clk, rst) is

View File

@ -57,13 +57,13 @@ architecture mac of top_hwitl is
signal mac_out : mac_out_t; signal mac_out : mac_out_t;
signal mac_in : mac_in_t; signal mac_in : mac_in_t;
type status_t is (IDLE, TX); type status_t is (IDLE, TX);
signal state : status_t; signal state : status_t;
constant BYTE_CNT_MAX : integer := 100; constant BYTE_CNT_MAX : integer := 100;
signal byte_cnt : integer range 0 to BYTE_CNT_MAX; signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1; constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
signal tmo : integer range 0 to TMO_MAX; signal tmo : integer range 0 to TMO_MAX;
begin begin
trashernet_mac_inst : entity trashernet.trashernet_mac trashernet_mac_inst : entity trashernet.trashernet_mac
port map( port map(
@ -206,10 +206,10 @@ architecture eth of top_hwitl is
PROT_ARP => ETHERNET_II_PROTOCOL_ARP, PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4 PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
); );
signal ethernet_i_out : ethernet_i_out_t; signal ethernet_i_out : ethernet_i_out_t;
signal ethernet_i_in : ethernet_i_in_t; signal ethernet_i_in : ethernet_i_in_t;
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range); signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
signal arp_out : arp_out_t; signal arp_out : arp_out_t;
signal arp_in : arp_in_t; signal arp_in : arp_in_t;
@ -217,15 +217,28 @@ architecture eth of top_hwitl is
signal ipv4_out : ipv4_out_t; signal ipv4_out : ipv4_out_t;
signal ipv4_in : ipv4_in_t; signal ipv4_in : ipv4_in_t;
constant IPROT_ICMP : integer := 0; constant IPROT_ICMP : integer := 0;
constant IPROT_UDP : integer := 1; constant IPROT_UDP : integer := 1;
constant IPV4_PROTOCOLS : ipv4_protocol_vector := ( constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
IPROT_ICMP => IPV4_PROTOCOL_ICMP, IPROT_ICMP => IPV4_PROTOCOL_ICMP,
IPROT_UDP => IPV4_PROTOCOL_UDP IPROT_UDP => IPV4_PROTOCOL_UDP
); );
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
signal udp_out : udp_out_t;
signal udp_in : udp_in_t;
begin begin
trashernet_udp_inst : entity trashernet.trashernet_udp
port map(
clk => clk,
rst => rst,
ipv4_protocol_in => ipv4_protocol_in(IPROT_UDP),
ipv4_protocol_out => ipv4_protocol_out(IPROT_UDP),
udp_out => udp_out,
udp_in => udp_in
);
trashernet_icmp_inst : entity trashernet.trashernet_icmp trashernet_icmp_inst : entity trashernet.trashernet_icmp
port map( port map(
clk => clk, clk => clk,
@ -375,20 +388,23 @@ begin
end process udp; end process udp;
tx_udp_p : block tx_udp_p : block
type state_t is (IDLE, TX, DONE); type state_t is (IDLE, TX, DONE);
signal state : state_t; signal state : state_t;
constant PACKET : byte_vector := ( constant PACKET : byte_vector := (
x"10", x"00", -- Source port
x"04", x"00", -- Destination port
x"00", x"15", -- Length
x"00", x"00", -- Checksum (not used)
x"48", x"65", x"6C", x"6C", x"6F", x"20", x"57", x"6F", x"72", x"6C", x"64", x"21", x"0a" -- Payload x"48", x"65", x"6C", x"6C", x"6F", x"20", x"57", x"6F", x"72", x"6C", x"64", x"21", x"0a" -- Payload
); );
signal sr : byte_vector(PACKET'range); signal sr : byte_vector(PACKET'range);
signal cnt : integer range 0 to sr'length - 1; signal cnt : integer range 0 to sr'length - 1;
begin begin
udp_in.tx_en <= '1' when state = TX else '0';
udp_in.tx_ip_address <= (x"C0", x"A8", x"02", x"01");
udp_in.tx_source_port <= x"ABCD";
udp_in.tx_destination_port <= x"00FF";
udp_in.tx_length <= to_unsigned(sr'length, 16);
udp_in.tx_data <= sr(0);
tx_udp : process(clk, rst) is tx_udp : process(clk, rst) is
begin begin
if rst then if rst then
@ -405,7 +421,8 @@ begin
end if; end if;
when TX => when TX =>
if ipv4_protocol_out(IPROT_UDP).tx_data_ack then if udp_out.tx_data_ack then
report "UDP: byte ack";
sr <= sr(sr'low + 1 to sr'high) & x"00"; sr <= sr(sr'low + 1 to sr'high) & x"00";
if cnt = 0 then if cnt = 0 then
state <= DONE; state <= DONE;
@ -413,11 +430,11 @@ begin
cnt <= cnt - 1; cnt <= cnt - 1;
end if; end if;
end if; end if;
if ipv4_protocol_out(IPROT_UDP).tx_err_stb then if udp_out.tx_err_stb then
report "UDP: TX ERROR"; report "UDP: TX ERROR";
state <= DONE; state <= DONE;
end if; end if;
if ipv4_protocol_out(IPROT_UDP).tx_ok_stb then if udp_out.tx_ok_stb then
report "UDP: TX OK stb in TX state -- that shouldn't happen, right?"; report "UDP: TX OK stb in TX state -- that shouldn't happen, right?";
end if; end if;
@ -427,12 +444,27 @@ begin
end case; end case;
end if; end if;
end process tx_udp; end process tx_udp;
ipv4_protocol_in(IPROT_UDP).tx_en <= '1' when state = TX else '0';
ipv4_protocol_in(IPROT_UDP).tx_ip_address <= (x"C0", x"A8", x"02", x"01");
ipv4_protocol_in(IPROT_UDP).tx_length <= to_unsigned(sr'length, 16);
ipv4_protocol_in(IPROT_UDP).tx_data <= sr(0);
end block tx_udp_p; end block tx_udp_p;
rx_udp_p : block
begin
rx_udp : process(clk, rst) is
begin
if rst then
elsif rising_edge(clk) then
if udp_out.rx_header_rcv then
report "UDP: RX on port " & --
to_string(to_integer(udp_out.rx_destination_port)) & " from port " & --
to_string(to_integer(udp_out.rx_source_port));
end if;
if udp_out.rx_data_valid then
report "UDP: RX: " & to_hstring(udp_out.rx_data);
end if;
end if;
end process rx_udp;
end block rx_udp_p;
icmp : process(clk) is icmp : process(clk) is
begin begin
if rising_edge(clk) then if rising_edge(clk) then

View File

@ -68,16 +68,16 @@ architecture rtl of top_mac_test is
signal mac_out : mac_out_t; signal mac_out : mac_out_t;
signal mac_in : mac_in_t; signal mac_in : mac_in_t;
type status_t is (IDLE, TX); type status_t is (IDLE, TX);
signal state : status_t; signal state : status_t;
constant BYTE_CNT_MAX : integer := 100; constant BYTE_CNT_MAX : integer := 100;
signal byte_cnt : integer range 0 to BYTE_CNT_MAX; signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
signal button_n_sync : std_logic_vector(button_n'range); signal button_n_sync : std_logic_vector(button_n'range);
signal button : 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; constant TMO_MAX : integer := integer(round((real(F_CLK) * 0.25))) - 1;
signal tmo : integer range 0 to TMO_MAX; signal tmo : integer range 0 to TMO_MAX;
begin begin
trashernet_mac_inst : entity trashernet.trashernet_mac trashernet_mac_inst : entity trashernet.trashernet_mac
port map( port map(
@ -190,7 +190,7 @@ begin
if mac_out.tx_mac_data_ack then if mac_out.tx_mac_data_ack then
if byte_cnt = 1 then if byte_cnt = 1 then
mac_in.tx_mac_data_en <= '0'; mac_in.tx_mac_data_en <= '0';
state <= IDLE; state <= IDLE;
else else
byte_cnt <= byte_cnt - 1; byte_cnt <= byte_cnt - 1;
end if; end if;

View File

@ -79,10 +79,10 @@ architecture rtl of top_pingable is
PROT_ARP => ETHERNET_II_PROTOCOL_ARP, PROT_ARP => ETHERNET_II_PROTOCOL_ARP,
PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4 PROT_IPV4 => ETHERNET_II_PROTOCOL_IPV4
); );
signal ethernet_i_out : ethernet_i_out_t; signal ethernet_i_out : ethernet_i_out_t;
signal ethernet_i_in : ethernet_i_in_t; signal ethernet_i_in : ethernet_i_in_t;
signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range); signal ethernet_ii_out : ethernet_ii_out_vector(ETHERNET_II_PROTOCOLS'range);
signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range); signal ethernet_ii_in : ethernet_ii_in_vector(ETHERNET_II_PROTOCOLS'range);
signal arp_out : arp_out_t; signal arp_out : arp_out_t;
signal arp_in : arp_in_t; signal arp_in : arp_in_t;
@ -90,12 +90,12 @@ architecture rtl of top_pingable is
signal ipv4_out : ipv4_out_t; signal ipv4_out : ipv4_out_t;
signal ipv4_in : ipv4_in_t; signal ipv4_in : ipv4_in_t;
constant IPROT_ICMP : integer := 0; constant IPROT_ICMP : integer := 0;
constant IPV4_PROTOCOLS : ipv4_protocol_vector := ( constant IPV4_PROTOCOLS : ipv4_protocol_vector := (
IPROT_ICMP => IPV4_PROTOCOL_ICMP IPROT_ICMP => IPV4_PROTOCOL_ICMP
); );
signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_out : ipv4_protocol_out_vector(IPV4_PROTOCOLS'range);
signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range); signal ipv4_protocol_in : ipv4_protocol_in_vector(IPV4_PROTOCOLS'range);
signal button_n_sync : std_logic_vector(button_n'range); signal button_n_sync : std_logic_vector(button_n'range);
signal button : std_logic_vector(button_n'range); signal button : std_logic_vector(button_n'range);

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
vunit_hdl
cocotb

View File

@ -34,11 +34,11 @@ entity fifo is
end entity fifo; end entity fifo;
architecture rtl of fifo is architecture rtl of fifo is
type memory_t is array (natural range <>) of std_logic_vector(data_in'range); type memory_t is array (natural range <>) of std_logic_vector(data_in'range);
signal memory : memory_t(0 to DEPTH - 1); signal memory : memory_t(0 to DEPTH - 1);
subtype memory_pointer_t is integer range 0 to DEPTH - 1; subtype memory_pointer_t is integer range 0 to DEPTH - 1;
subtype usage_counter_t is integer range 0 to DEPTH; subtype usage_counter_t is integer range 0 to DEPTH;
signal read_pointer : memory_pointer_t; signal read_pointer : memory_pointer_t;
signal write_pointer : memory_pointer_t; signal write_pointer : memory_pointer_t;

View File

@ -64,8 +64,8 @@ architecture rtl of trashernet_arp is
begin begin
resolver : block resolver : block
type resolver_state_t is (IDLE, QUERY_MAC); type resolver_state_t is (IDLE, QUERY_MAC);
signal state : resolver_state_t; signal state : resolver_state_t;
signal query_mac_found : std_logic; signal query_mac_found : std_logic;
signal query_timeout : std_logic := '0'; -- TODO: Implement signal query_timeout : std_logic := '0'; -- TODO: Implement
@ -146,19 +146,19 @@ begin
signal sr : byte_vector(0 to BYTECOUNT_ADDRESSES - 1); signal sr : byte_vector(0 to BYTECOUNT_ADDRESSES - 1);
constant SR_HEAD_OFFSET : integer := BYTECOUNT_ADDRESSES - BYTECOUNT_HEAD; constant SR_HEAD_OFFSET : integer := BYTECOUNT_ADDRESSES - BYTECOUNT_HEAD;
alias sr_head_htype is sr(SR_HEAD_OFFSET + 0 to SR_HEAD_OFFSET + 1); alias sr_head_htype is sr(SR_HEAD_OFFSET + 0 to SR_HEAD_OFFSET + 1);
alias sr_head_ptype is sr(SR_HEAD_OFFSET + 2 to SR_HEAD_OFFSET + 3); alias sr_head_ptype is sr(SR_HEAD_OFFSET + 2 to SR_HEAD_OFFSET + 3);
alias sr_head_hlen is sr(SR_HEAD_OFFSET + 4); alias sr_head_hlen is sr(SR_HEAD_OFFSET + 4);
alias sr_head_plen is sr(SR_HEAD_OFFSET + 5); alias sr_head_plen is sr(SR_HEAD_OFFSET + 5);
alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7); alias sr_head_oper is sr(SR_HEAD_OFFSET + 6 to SR_HEAD_OFFSET + 7);
constant SR_ADDRESSES_OFFSET : integer := -8; constant SR_ADDRESSES_OFFSET : integer := -8;
alias sr_addresses_sha is sr(8 + SR_ADDRESSES_OFFSET to 13 + SR_ADDRESSES_OFFSET); alias sr_addresses_sha is sr(8 + SR_ADDRESSES_OFFSET to 13 + SR_ADDRESSES_OFFSET);
alias sr_addresses_spa is sr(14 + SR_ADDRESSES_OFFSET to 17 + SR_ADDRESSES_OFFSET); alias sr_addresses_spa is sr(14 + SR_ADDRESSES_OFFSET to 17 + SR_ADDRESSES_OFFSET);
alias sr_addresses_tha is sr(18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET); -- @suppress "Unused declaration": We don't need to check this as this is already done by the MAC alias sr_addresses_tha is sr(18 + SR_ADDRESSES_OFFSET to 23 + SR_ADDRESSES_OFFSET); -- @suppress "Unused declaration": We don't need to check this as this is already done by the MAC
alias sr_addresses_tpa is sr(24 + SR_ADDRESSES_OFFSET to 27 + SR_ADDRESSES_OFFSET); alias sr_addresses_tpa is sr(24 + SR_ADDRESSES_OFFSET to 27 + SR_ADDRESSES_OFFSET);
type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE); type state_t is (HEAD, ADDRESSES, WAITCRC, SENDREPLY, IGNORE);
signal state : state_t; signal state : state_t;
signal bytecount : integer range 0 to BYTECOUNT_ADDRESSES; signal bytecount : integer range 0 to BYTECOUNT_ADDRESSES;
signal shifted : std_logic; signal shifted : std_logic;
@ -240,7 +240,7 @@ begin
state <= HEAD; state <= HEAD;
end if; end if;
when IGNORE => -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err) when IGNORE => -- @suppress "Dead state 'IGNORE'": Outgoing transition provided outside of case statement (on RX ok/err)
null; null;
end case; end case;
end if; end if;
@ -253,14 +253,14 @@ begin
end block rx; end block rx;
tx : block tx : block
signal sr : byte_vector(0 to 27); signal sr : byte_vector(0 to 27);
signal arp_reply_preload : byte_vector(sr'range); signal arp_reply_preload : byte_vector(sr'range);
signal arp_request_preload : byte_vector(sr'range); signal arp_request_preload : byte_vector(sr'range);
constant BYTECOUNT_MAX : integer := sr'length + 1; constant BYTECOUNT_MAX : integer := sr'length + 1;
signal bytecount : integer range 0 to BYTECOUNT_MAX; signal bytecount : integer range 0 to BYTECOUNT_MAX;
type state_t is (IDLE, TRANSMIT); type state_t is (IDLE, TRANSMIT);
signal state : state_t; signal state : state_t;
begin begin
arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header arp_reply_preload <= HTYPE & PTYPE & HLEN & PLEN & OPER_REPLY & -- Header

View File

@ -46,11 +46,11 @@ architecture rtl of trashernet_eth is
signal tx_data_ack_i : std_logic; signal tx_data_ack_i : std_logic;
begin begin
rx : block rx : block
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1; constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2; constant SEL_ETH_NONE : integer := ethernet_ii_out'low - 2;
signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high; signal sel : integer range SEL_ETH_NONE to ethernet_ii_out'high;
signal mac_destination_matches : std_logic; signal mac_destination_matches : std_logic;
signal rx_mac_header_rcv_delayed : std_logic; signal rx_mac_header_rcv_delayed : std_logic;
begin begin
mac_destination_matches <= '1' when -- mac_destination_matches <= '1' when --
@ -112,10 +112,10 @@ begin
end block rx; end block rx;
tx : block tx : block
type state_t is (IDLE, TXD); type state_t is (IDLE, TXD);
signal state : state_t; signal state : state_t;
constant SEL_ETH_I : integer := ethernet_ii_out'low - 1; constant SEL_ETH_I : integer := ethernet_ii_out'low - 1;
signal sel : integer range SEL_ETH_I to ethernet_ii_in'high; signal sel : integer range SEL_ETH_I to ethernet_ii_in'high;
begin begin
arb : process(clk, rst) is arb : process(clk, rst) is
begin begin
@ -141,7 +141,10 @@ begin
end if; end if;
when TXD => when TXD =>
state <= IDLE when (not mac_in.tx_mac_data_en or not mac_out.tx_active); if (not mac_in.tx_mac_data_en or not mac_out.tx_active) then
state <= IDLE;
sel <= SEL_ETH_I; -- To avoid arbitration errors, always select the highest priority one by default
end if;
end case; end case;
end if; end if;
end process arb; end process arb;

View File

@ -56,13 +56,13 @@ begin
); );
rx : block rx : block
signal sr : byte_vector(0 to 3); signal sr : byte_vector(0 to 3);
alias sr_type is sr(0); alias sr_type is sr(0);
alias sr_code is sr(1); alias sr_code is sr(1);
alias sr_checksum is sr(2 to 3); alias sr_checksum is sr(2 to 3);
type state_t is (HEADER, DATA, IGNORE); type state_t is (HEADER, DATA, IGNORE);
signal state : state_t; signal state : state_t;
signal byte_count : integer range 0 to sr'length; signal byte_count : integer range 0 to sr'length;
@ -117,8 +117,8 @@ begin
signal checksum : unsigned(16 downto 0); signal checksum : unsigned(16 downto 0);
signal checksum_ones : unsigned(15 downto 0); signal checksum_ones : unsigned(15 downto 0);
type state_t is (IDLE, HEADER, PAYLOAD); type state_t is (IDLE, HEADER, PAYLOAD);
signal state : state_t; signal state : state_t;
signal sr : byte_vector(0 to 3); signal sr : byte_vector(0 to 3);
signal byte_count : integer range 0 to sr'subtype'high; signal byte_count : integer range 0 to sr'subtype'high;

View File

@ -44,27 +44,27 @@ begin
constant BYTECOUNT_HEADER : integer := 20; -- Mandatory header fields constant BYTECOUNT_HEADER : integer := 20; -- Mandatory header fields
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
alias sr_version is sr(0)(7 downto 4); alias sr_version is sr(0)(7 downto 4);
alias sr_ihl is sr(0)(3 downto 0); alias sr_ihl is sr(0)(3 downto 0);
alias sr_dscp is sr(1)(7 downto 2); alias sr_dscp is sr(1)(7 downto 2);
alias sr_ecn is sr(1)(1 downto 0); alias sr_ecn is sr(1)(1 downto 0);
alias sr_total_length is sr(2 to 3); alias sr_total_length is sr(2 to 3);
alias sr_identification is sr(4 to 5); alias sr_identification is sr(4 to 5);
alias sr_flags is sr(6)(7 downto 5); alias sr_flags is sr(6)(7 downto 5);
signal sr_fragment_offset : std_logic_vector(12 downto 0); signal sr_fragment_offset : std_logic_vector(12 downto 0);
alias sr_ttl is sr(8); alias sr_ttl is sr(8);
alias sr_protocol is sr(9); alias sr_protocol is sr(9);
alias sr_header_cks is sr(10 to 11); alias sr_header_cks is sr(10 to 11);
alias sr_source_ip is sr(12 to 15); alias sr_source_ip is sr(12 to 15);
alias sr_destination_ip is sr(16 to 19); alias sr_destination_ip is sr(16 to 19);
signal header_ok : std_logic; signal header_ok : std_logic;
signal bytecount : integer range 0 to 65535; signal bytecount : integer range 0 to 65535;
signal block_done : std_logic; signal block_done : std_logic;
type state_t is (HEADER, OPT, PAYLOAD, DONE); type state_t is (HEADER, OPT, PAYLOAD, DONE);
signal state : state_t; signal state : state_t;
begin begin
sr_fragment_offset <= sr(6)(4 downto 0) & sr(7); sr_fragment_offset <= sr(6)(4 downto 0) & sr(7);
@ -138,8 +138,8 @@ begin
end block rx; end block rx;
tx : block tx : block
type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD); type state_t is (IDLE, ARP, CALC_CKS, HEADER, PAYLOAD);
signal state : state_t; signal state : state_t;
signal full_length : ipv4_length; signal full_length : ipv4_length;
@ -174,11 +174,13 @@ begin
ipv4_out.tx_data_ack <= '0'; ipv4_out.tx_data_ack <= '0';
arp_in.arp_query_stb <= '0'; arp_in.arp_query_stb <= '0';
ipv4_out.tx_err_stb <= '0'; ipv4_out.tx_err_stb <= '0';
ipv4_out.tx_ok_stb <= '0';
elsif rising_edge(clk) then elsif rising_edge(clk) then
arp_in.arp_query_stb <= '0'; arp_in.arp_query_stb <= '0';
ipv4_out.tx_data_ack <= '0'; ipv4_out.tx_data_ack <= '0';
ipv4_out.tx_err_stb <= '0'; ipv4_out.tx_err_stb <= '0';
ipv4_out.tx_ok_stb <= '0';
if (ethernet_ii_out.tx_data_ack = '1') or (state = CALC_CKS) then if (ethernet_ii_out.tx_data_ack = '1') or (state = CALC_CKS) then
if byte_cnt > 0 then if byte_cnt > 0 then
@ -192,6 +194,12 @@ begin
if ipv4_in.tx_en then if ipv4_in.tx_en then
arp_in.arp_query_stb <= '1'; arp_in.arp_query_stb <= '1';
state <= ARP; state <= ARP;
if or((to_std_logic_vector(ipv4_config.gateway) xor to_std_logic_vector(arp_in.arp_ip)) and to_std_logic_vector(ipv4_config.subnet_mask)) then
arp_in.arp_ip <= ipv4_config.gateway;
else
arp_in.arp_ip <= ipv4_in.tx_ip_address;
end if;
end if; end if;
when ARP => when ARP =>
@ -232,7 +240,8 @@ begin
sr(sr'low) <= ipv4_in.tx_data; sr(sr'low) <= ipv4_in.tx_data;
ipv4_out.tx_data_ack <= '1'; ipv4_out.tx_data_ack <= '1';
if not ipv4_in.tx_en then if not ipv4_in.tx_en then
state <= IDLE; state <= IDLE;
ipv4_out.tx_ok_stb <= '1';
end if; end if;
end if; end if;
end case; end case;
@ -241,9 +250,6 @@ begin
ethernet_ii_in.tx_data <= sr(sr'low); ethernet_ii_in.tx_data <= sr(sr'low);
ethernet_ii_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0'; ethernet_ii_in.tx_en <= '1' when (state = HEADER) or (state = PAYLOAD) else '0';
arp_in.arp_ip <= ipv4_in.tx_ip_address;
end block tx; end block tx;
end architecture rtl; end architecture rtl;

View File

@ -83,8 +83,8 @@ begin
end block rx; end block rx;
tx : block tx : block
type state_t is (IDLE, TXD); type state_t is (IDLE, TXD);
signal state : state_t; signal state : state_t;
begin begin
arb : process(clk, rst) is arb : process(clk, rst) is
@ -105,7 +105,10 @@ begin
end loop; end loop;
when TXD => when TXD =>
state <= IDLE when (not ipv4_in.tx_en); if not ipv4_in.tx_en then
state <= IDLE;
tx_sel <= ipv4_protocol_in'left; -- To avoid arbitration errors, always select the highest priority one by default
end if;
end case; end case;
end if; end if;
end process arb; end process arb;

View File

@ -41,10 +41,10 @@ begin
constant HEAD_LENGTH : integer := mac_out.rx_header.mac_destination'length + mac_out.rx_header.mac_source'length + mac_out.rx_header.mac_ethertype'length; constant HEAD_LENGTH : integer := mac_out.rx_header.mac_destination'length + mac_out.rx_header.mac_source'length + mac_out.rx_header.mac_ethertype'length;
constant CRC_LENGTH : integer := 4; constant CRC_LENGTH : integer := 4;
signal state : state_t; signal state : state_t;
signal sr_head : byte_vector(0 to HEAD_LENGTH - 1); signal sr_head : byte_vector(0 to HEAD_LENGTH - 1);
constant BYTE_COUNT_MAX : integer := maximum(HEAD_LENGTH, CRC_LENGTH) - 1; constant BYTE_COUNT_MAX : integer := maximum(HEAD_LENGTH, CRC_LENGTH) - 1;
signal byte_count : integer range 0 to BYTE_COUNT_MAX; signal byte_count : integer range 0 to BYTE_COUNT_MAX;
signal sr_payload : byte_vector(0 to 4); signal sr_payload : byte_vector(0 to 4);
@ -102,7 +102,7 @@ begin
byte_count <= byte_count + 1; byte_count <= byte_count + 1;
end if; end if;
end if; end if;
when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error) when PAYLOAD => -- @suppress "Dead state 'PAYLOAD'": Outgoing transition provided outside of case statement (RX disabled or error)
if phy_out.rx_data_valid then if phy_out.rx_data_valid then
sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data; sr_payload <= sr_payload(sr_payload'low + 1 to sr_payload'high) & phy_out.rx_data;
if byte_count = CRC_LENGTH then if byte_count = CRC_LENGTH then
@ -136,14 +136,14 @@ begin
end block rx; end block rx;
tx : block tx : block
type tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC); type tx_state_t is (IDLE, HEADER, DATA, PAD, TXCRC);
signal tx_state : tx_state_t; signal tx_state : tx_state_t;
signal sr : byte_vector(0 to mac_in.tx_header.mac_destination'length + mac_in.tx_header.mac_source'length + mac_in.tx_header.mac_ethertype'length - 1); signal sr : byte_vector(0 to mac_in.tx_header.mac_destination'length + mac_in.tx_header.mac_source'length + mac_in.tx_header.mac_ethertype'length - 1);
constant SR_CNT_MAX : integer := sr'high; constant SR_CNT_MAX : integer := sr'high;
signal sr_cnt : integer range 0 to SR_CNT_MAX; signal sr_cnt : integer range 0 to SR_CNT_MAX;
constant BYTE_CNT_MAX : integer := ETH_FRAME_MIN_LENGTH - 4; -- Minus four because the CRC is *after* the PAD constant BYTE_CNT_MAX : integer := ETH_FRAME_MIN_LENGTH - 4; -- Minus four because the CRC is *after* the PAD
signal byte_cnt : integer range 0 to BYTE_CNT_MAX; signal byte_cnt : integer range 0 to BYTE_CNT_MAX;
signal crc : std_logic_vector(ETH_POLYNOMIAL'range); signal crc : std_logic_vector(ETH_POLYNOMIAL'range);
signal crc_clear : std_logic; signal crc_clear : std_logic;

View File

@ -44,7 +44,7 @@ begin
common : block common : block
constant TICK_MS_CNT_MAX : integer := (F_CLK / 1000) - 1; constant TICK_MS_CNT_MAX : integer := (F_CLK / 1000) - 1;
signal tick_ms_count : integer range 0 to TICK_MS_CNT_MAX; signal tick_ms_count : integer range 0 to TICK_MS_CNT_MAX;
begin begin
mstick : process(clk, rst) is mstick : process(clk, rst) is
begin begin
@ -80,7 +80,7 @@ begin
-- NLP supervision -- NLP supervision
constant NLP_TIMEOUT_CNT_MAX : integer := (16 + 8) - 1; -- Every 16 ms (timebase 1 ms) constant NLP_TIMEOUT_CNT_MAX : integer := (16 + 8) - 1; -- Every 16 ms (timebase 1 ms)
signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX; signal nlp_timeout_cnt : integer range 0 to NLP_TIMEOUT_CNT_MAX;
begin begin
-- Synchronize RX input -- Synchronize RX input
synchronizer_rxp_inst : entity work.synchronizer synchronizer_rxp_inst : entity work.synchronizer
@ -111,26 +111,26 @@ begin
demanchestizer : block demanchestizer : block
-- Transition detector -- Transition detector
constant BIT_LENGTH_LONG : integer := F_CLK / F_ETH - 1; constant BIT_LENGTH_LONG : integer := F_CLK / F_ETH - 1;
constant BIT_LENGTH_SHORT : integer := F_CLK / (F_ETH * 2) - 1; constant BIT_LENGTH_SHORT : integer := F_CLK / (F_ETH * 2) - 1;
constant BIT_LENGTH_TOLERANCE : integer := (F_CLK / F_ETH) / 5; constant BIT_LENGTH_TOLERANCE : integer := (F_CLK / F_ETH) / 5;
constant BIT_LENGTH_TIMEOUT : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1; constant BIT_LENGTH_TIMEOUT : integer := BIT_LENGTH_LONG + BIT_LENGTH_TOLERANCE + 1;
signal sample_count : integer range 0 to BIT_LENGTH_TIMEOUT; signal sample_count : integer range 0 to BIT_LENGTH_TIMEOUT;
type transition_duration_t is (SHORT, LONG); type transition_duration_t is (SHORT, LONG);
signal transition_duration : transition_duration_t; signal transition_duration : transition_duration_t;
signal transition_stb : std_logic; signal transition_stb : std_logic;
signal transition_activity : std_logic; signal transition_activity : std_logic;
-- Transition analysis -- Transition analysis
signal last_transition : transition_duration_t; signal last_transition : transition_duration_t;
type bit_ev_t is (NONE, TOGGLE, KEEP, ERROR); type bit_ev_t is (NONE, TOGGLE, KEEP, ERROR);
signal bit_ev : bit_ev_t; signal bit_ev : bit_ev_t;
-- Bit recovery -- Bit recovery
type demanchestization_state_t is (SYNC, DATA, ERROR); type demanchestization_state_t is (SYNC, DATA, ERROR);
signal demanchestization_state : demanchestization_state_t; signal demanchestization_state : demanchestization_state_t;
constant DEMANCHESTIZATION_MIN_SYNC_CNT_MAX : integer := (4 * 8) - 1; -- 4 good sync bytes constant DEMANCHESTIZATION_MIN_SYNC_CNT_MAX : integer := (4 * 8) - 1; -- 4 good sync bytes
signal demanchestization_min_sync_cnt : integer range 0 to DEMANCHESTIZATION_MIN_SYNC_CNT_MAX; signal demanchestization_min_sync_cnt : integer range 0 to DEMANCHESTIZATION_MIN_SYNC_CNT_MAX;
begin begin
-- Detects spacing of transitions -- Detects spacing of transitions
transition_detector : process(clk, rst) is transition_detector : process(clk, rst) is
@ -253,6 +253,7 @@ begin
begin begin
if rst then if rst then
phy_out.rx_data_valid <= '0'; phy_out.rx_data_valid <= '0';
phy_out.rx_data <= (others => '0'); -- Needed for yosys to compile
elsif rising_edge(clk) then elsif rising_edge(clk) then
phy_out.rx_data_valid <= '0'; phy_out.rx_data_valid <= '0';
@ -296,22 +297,25 @@ begin
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
transmitter : block transmitter : block
constant TX_STB_CNT_MAX : integer := integer(round(real(F_CLK) / real((F_ETH * 2)))) - 1; constant TX_STB_CNT_IDEAL : real := real(F_CLK) / real((F_ETH * 2));
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX; constant TX_STB_SKIP_ERROR : real := abs (round(TX_STB_CNT_IDEAL) - TX_STB_CNT_IDEAL);
constant TX_STB_CNT_MAX : integer := integer(round(TX_STB_CNT_IDEAL + 0.25)) - 1; -- Round up starting for error > 0.25
constant TX_STB_SKIP_SECOND : boolean := TX_STB_SKIP_ERROR >= 0.25; -- Skip one clock cycle every other symbol to hit clock rate in between when the divider is close to x.5
signal tx_stb_cnt : integer range 0 to TX_STB_CNT_MAX;
type tx_state_t is (IDLE, NLP, TX, IPG); type tx_state_t is (IDLE, NLP, TX, IPG);
signal tx_state : tx_state_t; signal tx_state : tx_state_t;
signal sr : std_logic_vector(phy_in.tx_data'range); signal sr : std_logic_vector(phy_in.tx_data'range);
signal bit_stage : std_logic; signal bit_stage : std_logic;
constant BIT_CNT_MAX_IPG : integer := 96; constant BIT_CNT_MAX_IPG : integer := 96;
constant BIT_CNT_MAX_DATA : integer := sr'length - 1; constant BIT_CNT_MAX_DATA : integer := sr'length - 1;
signal bit_cnt : integer range 0 to maximum(BIT_CNT_MAX_IPG, BIT_CNT_MAX_DATA); signal bit_cnt : integer range 0 to maximum(BIT_CNT_MAX_IPG, BIT_CNT_MAX_DATA);
constant NLP_CNT_MAX : integer := 15; -- specced 16 ms, but there's margin, so let's choose 15 to save a bit (timebase 1 ms) constant NLP_CNT_MAX : integer := 15; -- specced 16 ms, but there's margin, so let's choose 15 to save a bit (timebase 1 ms)
signal nlp_cnt : integer range 0 to NLP_CNT_MAX; signal nlp_cnt : integer range 0 to NLP_CNT_MAX;
type tx_mode_t is (OFF, NLP, ACTIVE); type tx_mode_t is (OFF, NLP, ACTIVE);
signal tx_mode : tx_mode_t; signal tx_mode : tx_mode_t;
begin begin
tx_main : process(clk, rst) is tx_main : process(clk, rst) is
procedure transmit_byte is procedure transmit_byte is
@ -367,6 +371,9 @@ begin
if tx_stb_cnt = 0 then if tx_stb_cnt = 0 then
tx_stb_cnt <= TX_STB_CNT_MAX; tx_stb_cnt <= TX_STB_CNT_MAX;
if TX_STB_SKIP_SECOND and bit_stage = '1' then
tx_stb_cnt <= TX_STB_CNT_MAX - 1;
end if;
else else
tx_stb_cnt <= tx_stb_cnt - 1; tx_stb_cnt <= tx_stb_cnt - 1;
end if; end if;

View File

@ -44,14 +44,14 @@ architecture rtl of trashernet_phy_cdc is
signal phy_phy_in : phy_in_t; signal phy_phy_in : phy_in_t;
-- Helper signals -- Helper signals
signal rx_data_valid_i : std_logic; signal rx_data_i : byte;
begin begin
assert F_CLK_PHY > 2 * F_CLK report "F_CLK_PHY must be at least 2x F_CLK" severity failure;
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
-- Drives: PHY clock domain -- Drives: PHY clock domain
-- ------------------------------------------------------------------------- -- -------------------------------------------------------------------------
-- Reset synchronizer for PHY
rstsync : process(phy_clk, rst) is rstsync : process(phy_clk, rst) is
begin begin
if rst then if rst then
@ -61,6 +61,7 @@ begin
end if; end if;
end process rstsync; end process rstsync;
-- Operate Trashernet in PHY clock domain
trashernet_phy_inst : entity work.trashernet_phy trashernet_phy_inst : entity work.trashernet_phy
generic map( generic map(
F_CLK => F_CLK_PHY F_CLK => F_CLK_PHY
@ -75,6 +76,22 @@ begin
tx_n => tx_n tx_n => tx_n
); );
-- Latch data in PHY clock domain when valid is strobed
-- If the other clock domain is slower than the time it takes for the strobe to synchronize,
-- `phy_phy_out.rx_data` will already have shifted in the next bit and no longer be valid.
-- Therefore, we need to latch it here.
rxdff : process(phy_clk, rst) is
begin
if rst then
rx_data_i <= (others => '0');
elsif rising_edge(phy_clk) then
if phy_phy_out.rx_data_valid then
rx_data_i <= phy_phy_out.rx_data;
end if;
end if;
end process rxdff;
synchronizer_txen_inst : entity work.synchronizer synchronizer_txen_inst : entity work.synchronizer
generic map( generic map(
SIZE => 5 SIZE => 5
@ -98,18 +115,10 @@ begin
a_in => phy_phy_out.rx_data_valid, a_in => phy_phy_out.rx_data_valid,
b_clk => clk, b_clk => clk,
b_rst => rst, b_rst => rst,
b_out => rx_data_valid_i b_out => phy_out.rx_data_valid
); );
rxdvff : process(clk, rst) is phy_out.rx_data <= rx_data_i; -- No need to synchronize in new clock domain as latched data has been stable for a while thanks to the delay in the _valid synchronizer
begin
if rst then
phy_out.rx_data_valid <= '0';
elsif rising_edge(clk) then
phy_out.rx_data_valid <= rx_data_valid_i;
phy_out.rx_data <= phy_phy_out.rx_data; -- Data should be stable after the time it takes the _valid signal to go through the synchronizer
end if;
end process rxdvff;
cdc_strobe_rxer_inst : entity work.cdc_strobe cdc_strobe_rxer_inst : entity work.cdc_strobe
port map( port map(

View File

@ -14,14 +14,14 @@ use ieee.numeric_std.all;
package trashernet_pkg is package trashernet_pkg is
-- General types -- General types
subtype byte is std_logic_vector(7 downto 0); subtype byte is std_logic_vector(7 downto 0);
type byte_vector is array (natural range <>) of byte; type byte_vector is array (natural range <>) of byte;
-- MAC specific types -- MAC specific types
subtype mac_addr_t is byte_vector(0 to 5); subtype mac_addr_t is byte_vector(0 to 5);
constant MAC_ADDR_BROADCAST : mac_addr_t := (others => x"FF"); constant MAC_ADDR_BROADCAST : mac_addr_t := (others => x"FF");
function mac_to_string(constant mac_address : in mac_addr_t) return string; function mac_to_string(constant mac_address : in mac_addr_t) return string;
subtype ethertype_t is byte_vector(0 to 1); subtype ethertype_t is byte_vector(0 to 1);
type mac_header_fields is record type mac_header_fields is record
mac_destination : mac_addr_t; -- Destination MAC address mac_destination : mac_addr_t; -- Destination MAC address
@ -32,6 +32,7 @@ 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);
function ip_to_string(constant ip_address : in ip_addr_t) return string; function ip_to_string(constant ip_address : in ip_addr_t) return string;
function to_std_logic_vector(ip_address : ip_addr_t) return std_logic_vector;
-- Configuration interface -- Configuration interface
type configuration_t is record type configuration_t is record
@ -80,13 +81,13 @@ package trashernet_pkg is
end record mac_out_t; end record mac_out_t;
-- MAC ETH interface -- MAC ETH interface
type ethernet_ii_protocol_t is record type ethernet_ii_protocol_t is record
ethertype : ethertype_t; ethertype : ethertype_t;
end record; end record;
type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t; type ethernet_ii_protocol_vector is array (natural range <>) of ethernet_ii_protocol_t;
constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- @suppress "Null range": We want a NULL vector here to remove the logic constant ETHERNET_II_PROTOCOLS_NONE : ethernet_ii_protocol_vector(0 to -1) := (others => (ethertype => (x"00", x"00"))); -- @suppress "Null range": We want a NULL vector here to remove the logic
constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00")); constant ETHERNET_II_PROTOCOL_IPV4 : ethernet_ii_protocol_t := (ethertype => (x"08", x"00"));
constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06")); constant ETHERNET_II_PROTOCOL_ARP : ethernet_ii_protocol_t := (ethertype => (x"08", x"06"));
type ethernet_i_out_t is record type ethernet_i_out_t is record
rx_mac_address : mac_addr_t; -- Source MAC address rx_mac_address : mac_addr_t; -- Source MAC address
@ -121,9 +122,9 @@ package trashernet_pkg is
tx_data : byte; -- TX data tx_data : byte; -- TX data
tx_en : std_logic; -- Start and continue transmitting tx_en : std_logic; -- Start and continue transmitting
end record; end record;
type ethernet_ii_out_vector is array (natural range <>) of ethernet_ii_out_t; 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; 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'); constant ETHERNET_II_IN_UNUSED : ethernet_ii_in_t := (tx_mac_address => (others => x"00"), tx_data => x"00", others => '0');
-- ARP interface -- ARP interface
type arp_out_t is record type arp_out_t is record
@ -137,12 +138,12 @@ package trashernet_pkg is
end record arp_in_t; end record arp_in_t;
-- IPv4 interface -- IPv4 interface
subtype ipv4_protocol is byte; subtype ipv4_protocol is byte;
type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol; type ipv4_protocol_vector is array (natural range <>) of ipv4_protocol;
constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01"; constant IPV4_PROTOCOL_ICMP : ipv4_protocol := x"01";
constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06"; constant IPV4_PROTOCOL_TCP : ipv4_protocol := x"06";
constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11"; constant IPV4_PROTOCOL_UDP : ipv4_protocol := x"11";
subtype ipv4_length is unsigned(15 downto 0); subtype ipv4_length is unsigned(15 downto 0);
type ipv4_out_t is record type ipv4_out_t is record
rx_ip_address : ip_addr_t; -- Source IP address rx_ip_address : ip_addr_t; -- Source IP address
@ -189,7 +190,51 @@ package trashernet_pkg is
tx_en : std_logic; -- Start and continue transmitting tx_en : std_logic; -- Start and continue transmitting
end record ipv4_protocol_in_t; end record ipv4_protocol_in_t;
type ipv4_protocol_out_vector is array (natural range <>) of ipv4_protocol_out_t; type ipv4_protocol_out_vector is array (natural range <>) of ipv4_protocol_out_t;
type ipv4_protocol_in_vector is array (natural range <>) of ipv4_protocol_in_t; type ipv4_protocol_in_vector is array (natural range <>) of ipv4_protocol_in_t;
-- UDP interface
subtype udp_port_t is unsigned(15 downto 0);
subtype udp_length_t is unsigned(15 downto 0);
type udp_out_t is record
rx_data : byte; -- RX Data
rx_data_valid : std_logic; -- RX data valid strobe
rx_header_rcv : std_logic; -- Start of reception
rx_source_port : udp_port_t; -- UDP source port
rx_destination_port : udp_port_t; -- UDP destination port
rx_length : udp_length_t; -- UDP length
rx_ok_stb : std_logic; -- End of packet, checksum OK
rx_error_stb : std_logic; -- End of packet, checksum invalid
tx_data_ack : std_logic; -- Give next data byte or disable `tx_en`
tx_ok_stb : std_logic; -- Transmission successful
tx_err_stb : std_logic; -- Transmission failed
end record udp_out_t;
type udp_in_t is record
tx_ip_address : ip_addr_t; -- Destination IP address
tx_source_port : udp_port_t; -- UDP source port
tx_destination_port : udp_port_t; -- UDP destination port
tx_length : udp_length_t; -- UDP length
tx_en : std_logic; -- Start and continue transmitting
tx_data : byte;
end record udp_in_t;
type udpprot_rx_out_t is record
temp : std_logic;
end record udpprot_rx_out_t;
type udpprot_rx_in_t is record
temp : std_logic;
end record udpprot_rx_in_t;
type udpprot_tx_out_t is record
temp : std_logic;
end record udpprot_tx_out_t;
type udpprot_tx_in_t is record
temp : std_logic;
end record udpprot_tx_in_t;
subtype portnum is integer range 0 to 65535;
type udp_port_vector is array (natural range <>) of portnum;
-- General helper functions -- General helper functions
function to_std_logic(constant bool : boolean) return std_logic; function to_std_logic(constant bool : boolean) return std_logic;
@ -218,6 +263,11 @@ package body trashernet_pkg is
; ;
end function ip_to_string; end function ip_to_string;
function to_std_logic_vector(ip_address : ip_addr_t) return std_logic_vector is
begin
return ip_address(0) & ip_address(1) & ip_address(2) & ip_address(3);
end function to_std_logic_vector;
function to_std_logic(constant bool : boolean) return std_logic is function to_std_logic(constant bool : boolean) return std_logic is
variable ret : std_logic; variable ret : std_logic;
begin begin

View File

@ -0,0 +1,160 @@
-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- trashernet_ipv4.vhd : Ethernet OSI Layer 3, Network (IPv4)
-- Implements packet handling and IP-Layer en-/decoding.
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.trashernet_pkg.all;
entity trashernet_udp is
-- generic(
-- UDP_PORTS_RX : udp_port_vector; -- Ports to receive on
-- UDP_PORTS_TX : udp_port_vector -- Ports to transmit on
-- );
port(
-- Global
clk : in std_logic; -- Global clock
rst : in std_logic; -- Asynchronous reset
-- IPv4 application interface
ipv4_protocol_in : out ipv4_protocol_in_t; -- IPv4 IF (out from IP Protocol)
ipv4_protocol_out : in ipv4_protocol_out_t; -- IPv4 IF (into IP Protocol)
-- UDP application interface
udp_out : out udp_out_t;
udp_in : in udp_in_t
-- udpprot_rx_out : out udpprot_rx_out_t; -- UDP Application IF for receiving data (out from UDP)
-- udpprot_rx_in : in udpprot_rx_in_t; -- UDP Application IF for receiving data (into UDP)
-- udpprot_tx_out : out udpprot_tx_out_t; -- UDP Application IF for transmitting data (out from UDP)
-- udpprot_tx_in : in udpprot_tx_in_t -- UDP Application IF for transmitting data (in from UDP)
);
end entity trashernet_udp;
architecture rtl of trashernet_udp is
begin
receiver : block
constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
alias sr_source_port is sr(0 to 1);
alias sr_destination_port is sr(2 to 3);
alias sr_length is sr(4 to 5);
alias sr_source_checksum is sr(6 to 7);
signal bytecount : integer range 0 to BYTECOUNT_HEADER - 1;
type state_t is (HEADER, PAYLOAD);
signal state : state_t;
begin
rxp : process(rst, clk) is
begin
if rst then
state <= HEADER;
udp_out.rx_header_rcv <= '0';
udp_out.rx_error_stb <= '0';
udp_out.rx_ok_stb <= '0';
elsif rising_edge(clk) then
udp_out.rx_header_rcv <= '0';
udp_out.rx_error_stb <= ipv4_protocol_out.rx_error_stb;
udp_out.rx_ok_stb <= ipv4_protocol_out.rx_ok_stb;
case state is
when HEADER =>
if ipv4_protocol_out.rx_data_valid then
sr <= sr(sr'low + 1 to sr'high) & ipv4_protocol_out.rx_data;
if bytecount = BYTECOUNT_HEADER - 1 then
state <= PAYLOAD;
udp_out.rx_header_rcv <= '1';
else
bytecount <= bytecount + 1;
end if;
end if;
when PAYLOAD =>
udp_out.rx_data <= ipv4_protocol_out.rx_data;
udp_out.rx_data_valid <= ipv4_protocol_out.rx_data_valid;
end case;
if ipv4_protocol_out.rx_header_rcv or ipv4_protocol_out.rx_error_stb or ipv4_protocol_out.rx_ok_stb then
state <= HEADER;
bytecount <= 0;
end if;
end if;
end process rxp;
udp_out.rx_source_port <= unsigned(std_logic_vector'(sr_source_port(0) & sr_source_port(1)));
udp_out.rx_destination_port <= unsigned(std_logic_vector'(sr_destination_port(2) & sr_destination_port(3)));
udp_out.rx_length <= unsigned(std_logic_vector'(sr_length(4) & sr_length(5)));
end block receiver;
transmitter : block
constant BYTECOUNT_HEADER : integer := 8; -- Mandatory header fields
signal sr : byte_vector(0 to BYTECOUNT_HEADER - 1) := (others => x"00"); -- Init to zero to squelch metavalue warnings
signal byte_cnt : integer range 0 to sr'length - 1;
type state_t is (IDLE, HEADER, PAYLOAD);
signal state : state_t;
begin
txp : process(rst, clk) is
variable full_length : udp_length_t;
begin
if rst then
udp_out.tx_data_ack <= '0';
udp_out.tx_err_stb <= '0';
udp_out.tx_ok_stb <= '0';
elsif rising_edge(clk) then
udp_out.tx_data_ack <= '0';
udp_out.tx_err_stb <= ipv4_protocol_out.tx_err_stb;
udp_out.tx_ok_stb <= ipv4_protocol_out.tx_ok_stb;
if ipv4_protocol_out.tx_data_ack = '1' then
if byte_cnt > 0 then
byte_cnt <= byte_cnt - 1;
end if;
sr <= sr(sr'low + 1 to sr'high) & x"00";
end if;
case state is
when IDLE =>
if udp_in.tx_en then
full_length := udp_in.tx_length + BYTECOUNT_HEADER;
sr <= byte_vector'(byte(udp_in.tx_source_port(15 downto 8)), byte(udp_in.tx_source_port(7 downto 0)), --
byte(udp_in.tx_destination_port(15 downto 8)), byte(udp_in.tx_destination_port(7 downto 0)), --
byte(full_length(15 downto 8)), byte(full_length(7 downto 0)), --
x"00", x"00");
byte_cnt <= sr'length - 1;
ipv4_protocol_in.tx_length <= full_length;
ipv4_protocol_in.tx_ip_address <= udp_in.tx_ip_address;
state <= HEADER;
end if;
when HEADER =>
if byte_cnt = 0 then
state <= PAYLOAD;
end if;
when PAYLOAD =>
if ipv4_protocol_out.tx_data_ack then
sr(sr'low) <= udp_in.tx_data;
udp_out.tx_data_ack <= '1';
if not udp_in.tx_en then
state <= IDLE;
end if;
end if;
end case;
end if;
end process txp;
ipv4_protocol_in.tx_data <= sr(sr'low);
ipv4_protocol_in.tx_en <= '1' when (state /= IDLE) else '0';
end block transmitter;
end architecture rtl;