trashernet/trashernet/trashernet_ipv4prot.vhd
Markus Koch 7a0da0f91d eth+ipv4prot: Fix protocol arbitration for real
The problem arises in the following scenario.

There are two sources: HIGH-prio and LOW-prio. Current selection
(from last transaction) is LOW-prio. This one completes.

Next, both request at the same time. Data and tx_en from LOW-prio is
latched -> first word is taken from there. Now, arbitration does its
job and switches to high prio. All acks and data go here.

Result: First byte is from LOW-prio source, rest is from HIGH-prio.

This commit fixes this behavior by resetting the selected channel to
the highest priority one, preventing the switch from pulling it away
from the selected one as the high priority is the one that will get
selected by the switching logic anyways.

Fixes #13.
2025-09-02 14:11:21 +02:00

129 lines
4.3 KiB
VHDL

-- -------------------------------------------------------------------------- --
-- TRASHERNET - A Trashy Ethernet Stack for FPGAs --
-- -------------------------------------------------------------------------- --
-- trashernet_eth.vhd : Ethernet OSI Layer 3, Network, Protocol Muxing
-- Implements arbitration of different IP protocols.
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.trashernet_pkg.all;
entity trashernet_ipv4prot is
generic(
IPV4_PROTOCOLS : ipv4_protocol_vector
);
port(
-- Global
clk : in std_logic; -- Global clock
rst : in std_logic; -- Asynchronous reset
-- IPv4 application interface
ipv4_out : in ipv4_out_t; -- IPv4 IF (out from IP)
ipv4_in : out ipv4_in_t; -- IPv4 IF (into IP)
-- IPv4 protocol interface
ipv4_protocol_out : out ipv4_protocol_out_vector(IPV4_PROTOCOLS'range); -- IPv4 Protocol IF (out from IP Mux)
ipv4_protocol_in : in ipv4_protocol_in_vector(IPV4_PROTOCOLS'range) -- IPv4 Protocol IF (into IP Mux)
);
end entity trashernet_ipv4prot;
architecture rtl of trashernet_ipv4prot is
constant SEL_PROTOCOL_NONE : integer := ipv4_protocol_out'low - 1;
signal rx_sel : integer range SEL_PROTOCOL_NONE to ipv4_protocol_out'high;
signal tx_sel : integer range ipv4_protocol_in'low to ipv4_protocol_in'high;
begin
rx : block
signal rx_header_rcv_delayed : std_logic;
begin
arb : process(clk, rst) is
begin
if rst then
rx_sel <= SEL_PROTOCOL_NONE;
rx_header_rcv_delayed <= '0';
elsif rising_edge(clk) then
rx_header_rcv_delayed <= ipv4_out.rx_header_rcv;
if ipv4_out.rx_header_rcv then
rx_sel <= SEL_PROTOCOL_NONE;
for i in IPV4_PROTOCOLS'range loop
if (ipv4_out.rx_protocol = IPV4_PROTOCOLS(i)) then
rx_sel <= i;
end if;
end loop;
end if;
end if;
end process arb;
mux : for i in ipv4_protocol_out'range generate
ipv4_protocol_out(i).rx_data <= ipv4_out.rx_data;
ipv4_protocol_out(i).rx_ip_address <= ipv4_out.rx_ip_address;
ipv4_protocol_out(i).rx_length <= ipv4_out.rx_length;
ipv4_protocol_out(i).rx_data_valid <= ipv4_out.rx_data_valid when rx_sel = i else '0';
ipv4_protocol_out(i).rx_error_stb <= ipv4_out.rx_error_stb when rx_sel = i else '0';
ipv4_protocol_out(i).rx_ok_stb <= ipv4_out.rx_ok_stb when rx_sel = i else '0';
ipv4_protocol_out(i).rx_header_rcv <= rx_header_rcv_delayed when rx_sel = i else '0';
end generate mux;
tx_mux : for i in ipv4_protocol_out'range generate
ipv4_protocol_out(i).tx_data_ack <= ipv4_out.tx_data_ack when tx_sel = i else '0';
ipv4_protocol_out(i).tx_err_stb <= ipv4_out.tx_err_stb when tx_sel = i else '0';
ipv4_protocol_out(i).tx_ok_stb <= ipv4_out.tx_ok_stb when tx_sel = i else '0';
end generate;
end block rx;
tx : block
type state_t is (IDLE, TXD, WAITDONE);
signal state : state_t;
begin
arb : process(clk, rst) is
begin
if rst then
state <= IDLE;
tx_sel <= ipv4_protocol_in'left;
elsif rising_edge(clk) then
case state is
when IDLE =>
for i in ipv4_protocol_in'range loop
if ipv4_protocol_in(i).tx_en then
tx_sel <= i;
state <= TXD;
exit; -- Prioritize according to vector
end if;
end loop;
when TXD =>
state <= WAITDONE when (not ipv4_in.tx_en);
when WAITDONE =>
if ipv4_out.tx_err_stb or ipv4_out.tx_ok_stb or ipv4_out.tx_data_ack 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 if;
end process arb;
demux : block
begin
ipv4_in.tx_data <= ipv4_protocol_in(tx_sel).tx_data;
ipv4_in.tx_en <= ipv4_protocol_in(tx_sel).tx_en;
ipv4_in.tx_ip_address <= ipv4_protocol_in(tx_sel).tx_ip_address;
ipv4_in.tx_length <= ipv4_protocol_in(tx_sel).tx_length;
ipv4_in.tx_protocol <= IPV4_PROTOCOLS(tx_sel);
end block demux;
end block tx;
end architecture rtl;