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.
129 lines
4.3 KiB
VHDL
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;
|