FIFO (First-InFirst-Out) sind Pufferspeicher für den Übergang von schnellen Übertragungspfaden auf langsamere (z.B. USB ==> RS232). Hier werden Daten in einen Ringpuffer eingespeichert, und später mit einer niedrigeren Datenrate ausgelesen.
Alternativ können Daten von einem langsamen Medium zwischengespeichert, und dann in einem Paket auf einem schnelleren Datenpfad übertragen werden.
Ein FIFO wird demzufolge am einfachsten über einen 2^n langen Ringspeicher realisiert, der mit 2 Zählern der Breite n adressiert wird. Ist der Schreibzeiger gleich dem Lesezeiger, dann ist der FIFO leer. Erreicht der Schreibzeiger den Wert des Lesezeigers-1, dann ist der FIFO voll, der Schreibzeiger hat den Lesezeiger quasi "von hinten" eingeholt.
Konkret mit einem BRAM (BlockRAM) implementiert könnte das z.B. so aussehen:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity FIFO is
Generic (
Addrbreite : natural := 8; -- Speicherlänge = 2^Addrbreite
Wortbreite : natural := 8
);
Port ( Din : in STD_LOGIC_VECTOR (Wortbreite-1 downto 0);
Wr : in STD_LOGIC;
Dout : out STD_LOGIC_VECTOR (Wortbreite-1 downto 0);
Rd : in STD_LOGIC;
Empty : out STD_LOGIC;
Full : out STD_LOGIC;
CLK : in STD_LOGIC
);
end FIFO;
architecture Verhalten of FIFO is
signal wrcnt : unsigned (Addrbreite-1 downto 0) := (others => '0');
signal rdcnt : unsigned (Addrbreite-1 downto 0) := (others => '0');
type speicher is array(0 to (2**Addrbreite)-1) of unsigned(Wortbreite-1 downto 0);
signal memory : speicher;
signal full_loc : std_logic;
signal empty_loc : std_logic;
begin
process begin
wait until rising_edge(CLK);
if (Wr='1' and full_loc='0') then
memory(to_integer(wrcnt)) <= unsigned(Din);
wrcnt <= wrcnt+1;
end if;
if (Rd='1' and empty_loc='0') then
Dout <= std_logic_vector(memory(to_integer(rdcnt))); -- Adresse getaktet --> BRAM
rdcnt <= rdcnt+1;
end if;
end process;
full_loc <= '1' when rdcnt = wrcnt+1 else '0';
empty_loc <= '1' when rdcnt = wrcnt else '0';
Full <= full_loc;
Empty <= empty_loc;
end Verhalten;
Die Lese-Adresse ist getaktet, deshalb erhalten wir ein Block-RAM und damit auch einen Takt Latency beim Lesen.
Wer das Ganze lieber mit Integer als Adresszähler implementiert haben möchte, der kann mal auf http://www.mikrocontroller.net/topic/171035 nachsehen. Hier wird der Modulo-Operator zur korrekten Implementierung des Überlaufs bei der Simulation benötigt. Die Hardware ist in beiden Fällen (unsigned bzw. integer) exakt die selbe.
Mit einem Distributed RAM implementiert würde das fast gleich aussehen, nur das Registrieren der Adresse muss unterlassen werden:
:
if (Read='1') then
rdcnt <= rdcnt+1;
end if;
end process;
Dout <= std_logic_vector(memory(rdcnt)); -- Adresse asynchron
full_loc <= '1' when rdcnt = wrcnt+1 else '0';
: