trashernet-soc/fpga/hdl/generics/fifo_block.vhd

126 lines
4.1 KiB
VHDL

-- -------------------------------------------------------------------------- --
-- TRASHERNET SoC - A Trashy Ethernet SoC for FPGAs --
-- -------------------------------------------------------------------------- --
-- TODO
-- -------------------------------------------------------------------------- --
-- Author : Markus Koch <markus@notsyncing.net>
-- Contributors : None
-- License : Mozilla Public License (MPL) Version 2
-- -------------------------------------------------------------------------- --
-- Tech hint: Width of FIFO will be width of `data_in` + 1.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity fifo_block is
generic(
SIZE : integer := 15 -- Entries in the FIFO (of size data), SIZE=(n^2)-1 recommended
);
port(
-- generic ports
clk : in std_logic; -- System clock
rst_a : in std_logic; -- Asynchronous reset
clr : in std_logic; -- Remove all entries
-- FIFO in
data_in : in std_logic_vector; -- Data input into FIFO
write : in std_logic; -- Data is latched when `data_in_valid and not full`
commit : in std_logic; -- Finalize block
abort : in std_logic; -- Abort current block
full : out std_logic; -- FIFO is full
-- FIFO out
data_out : out std_logic_vector; -- Data output from FIFO
data_first : out std_logic; -- Currently visible word is first in packet
empty : out std_logic; -- FIFO is empty. '0' means "data is available".
read : in std_logic; -- Acknowledge that `data_out` was read. The word is removed and next one shifted from FIFO when `read and not empty`.
-- FIFO meta
usage : out integer -- Usage counter
);
end fifo_block;
architecture rtl of fifo_block is
constant MEMORY_WIDTH : integer := data_in'length + 1;
subtype pointer_t is integer range 0 to SIZE - 1;
type memory_t is array (pointer_t) of std_logic_vector(MEMORY_WIDTH - 1 downto 0);
signal memory : memory_t;
signal read_pointer : pointer_t;
signal write_pointer : pointer_t;
signal read_pointer_last : pointer_t;
signal write_pointer_committed : pointer_t;
signal current_is_first : std_logic;
begin
fifo : process(rst_a, clk) is
variable succesful_read : std_logic;
variable succesful_write : std_logic;
variable write_pointer_next : pointer_t;
variable read_pointer_next : pointer_t;
begin
if rst_a then
read_pointer <= 0;
write_pointer <= 0;
read_pointer_last <= SIZE - 1;
write_pointer_committed <= 0;
current_is_first <= '1';
elsif rising_edge(clk) then
succesful_read := read and not empty;
succesful_write := write and (not full or succesful_read);
write_pointer_next := write_pointer;
read_pointer_next := read_pointer;
if succesful_read then
read_pointer_next := (read_pointer_next + 1) mod SIZE;
read_pointer_last <= read_pointer;
end if;
if succesful_write then
memory(write_pointer) <= current_is_first & data_in;
write_pointer_next := (write_pointer_next + 1) mod SIZE;
current_is_first <= '0';
end if;
if succesful_write xor succesful_read then
if succesful_write then
usage <= usage + 1;
else
usage <= usage - 1;
end if;
end if;
if commit then
write_pointer_committed <= write_pointer;
current_is_first <= '1';
elsif abort then
write_pointer_next := write_pointer_committed;
current_is_first <= '1';
end if;
read_pointer <= read_pointer_next;
write_pointer <= write_pointer_next;
if clr then
read_pointer <= 0;
write_pointer <= 0;
write_pointer_committed <= 0;
current_is_first <= '1';
usage <= 0;
end if;
end if;
end process fifo;
full <= '1' when read_pointer_last = write_pointer else '0';
empty <= '1' when (read_pointer = write_pointer_committed) else '0';
data_out <= memory(read_pointer)(data_out'range);
data_first <= memory(read_pointer)(MEMORY_WIDTH - 1);
end rtl;