redpitaya-puzzlefw/fpga/rtl/simple_fifo.vhd

209 lines
6.0 KiB
VHDL
Raw Normal View History

2024-08-09 20:16:53 +02:00
--
-- Simple FIFO with single clock.
--
-- Joris van Rantwijk 2024
--
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library xpm;
use xpm.vcomponents.all;
use work.puzzlefw_pkg.all;
entity simple_fifo is
generic (
-- Word width.
data_width: integer range 1 to 1024;
-- Size of FIFO as 2-log of the number of words.
fifo_depth_bits: integer range 2 to 16
);
port (
-- Main clock, active on rising edge.
clk: in std_logic;
-- Reset, active high, synchronous to main clock.
-- After reset, the FIFO is empty.
reset: in std_logic;
-- Data input stream.
in_valid: in std_logic;
in_ready: out std_logic;
in_data: in std_logic_vector(data_width - 1 downto 0);
-- Data output stream.
out_valid: out std_logic;
out_ready: in std_logic;
out_data: out std_logic_vector(data_width - 1 downto 0);
-- Number of words currently in the FIFO.
-- This excludes the word currently expressed on "out_ready" when "out_valid" is high.
queue_length: out unsigned(fifo_depth_bits - 1 downto 0)
);
end entity;
architecture arch of simple_fifo is
type regs_type is record
in_ready: std_logic;
out_valid: std_logic;
nonempty: std_logic;
waddr: unsigned(fifo_depth_bits - 1 downto 0);
raddr: unsigned(fifo_depth_bits - 1 downto 0);
qlen: unsigned(fifo_depth_bits - 1 downto 0);
end record;
constant regs_init: regs_type := (
in_ready => '0',
out_valid => '0',
nonempty => '0',
waddr => (others => '0'),
raddr => (others => '0'),
qlen => (others => '0')
);
signal r: regs_type := regs_init;
signal rnext: regs_type;
signal s_ram_wen: std_logic;
signal s_ram_ren: std_logic;
begin
--
-- Xilinx Simple Dual Port RAM.
--
ram_inst: xpm_memory_sdpram
generic map (
ADDR_WIDTH_A => fifo_depth_bits,
ADDR_WIDTH_B => fifo_depth_bits,
AUTO_SLEEP_TIME => 0,
BYTE_WRITE_WIDTH_A => data_width,
CASCADE_HEIGHT => 0,
CLOCKING_MODE => "common_clock",
ECC_MODE => "no_ecc",
MEMORY_INIT_FILE => "none",
MEMORY_INIT_PARAM => "0",
MEMORY_OPTIMIZATION => "true",
MEMORY_PRIMITIVE => "block",
MEMORY_SIZE => data_width * 2**fifo_depth_bits,
MESSAGE_CONTROL => 0,
READ_DATA_WIDTH_B => data_width,
READ_LATENCY_B => 1,
READ_RESET_VALUE_B => "0",
RST_MODE_A => "SYNC",
RST_MODE_B => "SYNC",
SIM_ASSERT_CHK => 0,
USE_EMBEDDED_CONSTRAINT => 0,
USE_MEM_INIT => 0,
WAKEUP_TIME => "disable_sleep",
WRITE_DATA_WIDTH_A => data_width,
WRITE_MODE_B => "no_change" )
port map (
addra => std_logic_vector(r.waddr),
addrb => std_logic_vector(r.raddr),
clka => clk,
clkb => clk,
dbiterrb => open,
dina => in_data,
doutb => out_data,
ena => s_ram_wen,
enb => s_ram_ren,
injectdbiterra => '0',
injectsbiterra => '0',
regceb => '1',
rstb => reset,
sbiterrb => open,
sleep => '0',
wea => "1" );
--
-- Drive output ports.
--
in_ready <= r.in_ready;
out_valid <= r.out_valid;
queue_length <= r.qlen;
--
-- Drive control signals to RAM.
--
s_ram_wen <= in_valid and r.in_ready;
s_ram_ren <= r.nonempty and (out_ready or (not r.out_valid));
--
-- Combinatorial process.
--
process (all) is
variable v: regs_type;
begin
-- Load current register values.
v := r;
-- Update write pointer on write.
if (in_valid = '1') and (r.in_ready = '1') then
v.waddr := r.waddr + 1;
end if;
-- Update input ready status.
-- Note this uses the NEW value of the write pointer.
if v.waddr + 1 = r.raddr then
v.in_ready := '0';
else
v.in_ready := '1';
end if;
-- On read from RAM, update read pointer.
if (r.nonempty = '1') and (out_ready = '1' or r.out_valid = '0') then
v.raddr := r.raddr + 1;
end if;
-- Determine whether output word is valid in the next cycle.
if r.nonempty = '1' then
-- Keep output word or load a new word during this cycle.
v.out_valid := '1';
elsif out_ready = '1' then
-- Consume output word without refreshing it.
v.out_valid := '0';
end if;
-- Determine whether RAM will contain data on the next cycle.
-- Note this uses the NEW values of the read and write pointers.
if v.waddr = v.raddr then
v.nonempty := '0';
else
v.nonempty := '1';
end if;
-- Calculate queue length.
-- Note this uses the NEW values of "waddr" and "raddr".
v.qlen := v.waddr - v.raddr;
-- Synchronous reset.
if reset = '1' then
v := regs_init;
end if;
-- Drive new register values to synchronous process.
rnext <= v;
end process;
--
-- Synchronous process.
--
process (clk) is
begin
if rising_edge(clk) then
r <= rnext;
end if;
end process;
end architecture;