Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rtl] rework FIFO module (to allow inferring block RAM) #754

Merged
merged 3 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12

| Date | Version | Comment | Link |
|:----:|:-------:|:--------|:----:|
| 19.12.2023 | 1.9.2.6 | rework FIFO component fixing problems with inferring block RAM | [#754](https://github.com/stnolting/neorv32/pull/754) |
| 11.12.2023 | 1.9.2.5 | clean-up software framework | [#752](https://github.com/stnolting/neorv32/pull/752) |
| 09.12.2023 | 1.9.2.4 | minor rtl code cleanups | [#747](https://github.com/stnolting/neorv32/pull/747) |
| 09.12.2023 | 1.9.2.3 | refine behavior of CPU's sleep state & signal | [#746](https://github.com/stnolting/neorv32/pull/746) |
Expand Down
150 changes: 57 additions & 93 deletions rtl/core/neorv32_fifo.vhd
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-- #################################################################################################
-- # << NEORV32 - Generic Single-Clock FIFO >> #
-- # << NEORV32 - Generic Single-Clock FIFO Component >> #
-- # ********************************************************************************************* #
-- # BSD 3-Clause License #
-- # #
Expand Down Expand Up @@ -68,139 +68,103 @@ architecture neorv32_fifo_rtl of neorv32_fifo is
-- make sure FIFO depth is a power of two --
constant fifo_depth_c : natural := cond_sel_natural_f(is_power_of_two_f(FIFO_DEPTH), FIFO_DEPTH, 2**index_size_f(FIFO_DEPTH));

-- FIFO --
type fifo_data_t is array (0 to fifo_depth_c-1) of std_ulogic_vector(FIFO_WIDTH-1 downto 0);
type fifo_t is record
we : std_ulogic; -- write enable
re : std_ulogic; -- read enable
w_pnt : std_ulogic_vector(index_size_f(fifo_depth_c) downto 0); -- write pointer
r_pnt : std_ulogic_vector(index_size_f(fifo_depth_c) downto 0); -- read pointer
data : fifo_data_t; -- fifo memory
match : std_ulogic;
empty : std_ulogic;
full : std_ulogic;
half : std_ulogic;
free : std_ulogic;
avail : std_ulogic;
end record;
signal fifo : fifo_t;

-- misc --
signal level_diff : std_ulogic_vector(index_size_f(fifo_depth_c) downto 0);
-- FIFO storage --
type fifo_mem_t is array (0 to fifo_depth_c-1) of std_ulogic_vector(FIFO_WIDTH-1 downto 0);
signal fifo_mem : fifo_mem_t;

-- FIFO control --
signal we, re : std_ulogic; -- write-/read-enable
signal w_pnt, r_pnt : std_ulogic_vector(index_size_f(fifo_depth_c) downto 0); -- write/read pointer

-- status --
signal match, empty, full, half, free, avail : std_ulogic;

-- fill level --
signal diff : std_ulogic_vector(index_size_f(fifo_depth_c) downto 0);

begin

-- Access Control -------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
fifo.re <= re_i when (FIFO_SAFE = false) else (re_i and fifo.avail); -- SAFE = read only if data available
fifo.we <= we_i when (FIFO_SAFE = false) else (we_i and fifo.free); -- SAFE = write only if space left
re <= re_i when (FIFO_SAFE = false) else (re_i and avail); -- SAFE = read only if data available
we <= we_i when (FIFO_SAFE = false) else (we_i and free); -- SAFE = write only if free space left


-- FIFO Pointers --------------------------------------------------------------------------
-- Pointers -------------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
pointer_update: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
fifo.w_pnt <= (others => '0');
fifo.r_pnt <= (others => '0');
w_pnt <= (others => '0');
r_pnt <= (others => '0');
elsif rising_edge(clk_i) then
-- write port --
if (clear_i = '1') then
fifo.w_pnt <= (others => '0');
elsif (fifo.we = '1') then
fifo.w_pnt <= std_ulogic_vector(unsigned(fifo.w_pnt) + 1);
w_pnt <= (others => '0');
elsif (we = '1') then
w_pnt <= std_ulogic_vector(unsigned(w_pnt) + 1);
end if;
-- read port --
if (clear_i = '1') then
fifo.r_pnt <= (others => '0');
elsif (fifo.re = '1') then
fifo.r_pnt <= std_ulogic_vector(unsigned(fifo.r_pnt) + 1);
r_pnt <= (others => '0');
elsif (re = '1') then
r_pnt <= std_ulogic_vector(unsigned(r_pnt) + 1);
end if;
end if;
end process pointer_update;

check_large:
if (fifo_depth_c > 1) generate
fifo.match <= '1' when (fifo.r_pnt(fifo.r_pnt'left-1 downto 0) = fifo.w_pnt(fifo.w_pnt'left-1 downto 0)) else '0';
fifo.full <= '1' when (fifo.r_pnt(fifo.r_pnt'left) /= fifo.w_pnt(fifo.w_pnt'left)) and (fifo.match = '1') else '0';
fifo.empty <= '1' when (fifo.r_pnt(fifo.r_pnt'left) = fifo.w_pnt(fifo.w_pnt'left)) and (fifo.match = '1') else '0';
level_diff <= std_ulogic_vector(unsigned(fifo.w_pnt) - unsigned(fifo.r_pnt));
fifo.half <= level_diff(level_diff'left-1) or fifo.full;
match <= '1' when (r_pnt(r_pnt'left-1 downto 0) = w_pnt(w_pnt'left-1 downto 0)) else '0';
full <= '1' when (r_pnt(r_pnt'left) /= w_pnt(w_pnt'left)) and (match = '1') else '0';
empty <= '1' when (r_pnt(r_pnt'left) = w_pnt(w_pnt'left)) and (match = '1') else '0';
diff <= std_ulogic_vector(unsigned(w_pnt) - unsigned(r_pnt));
half <= diff(diff'left-1) or full;
end generate;

check_small:
if (fifo_depth_c = 1) generate
fifo.match <= '1' when (fifo.r_pnt(0) = fifo.w_pnt(0)) else '0';
fifo.full <= not fifo.match;
fifo.empty <= fifo.match;
fifo.half <= fifo.full;
match <= '1' when (r_pnt(0) = w_pnt(0)) else '0';
full <= not match;
empty <= match;
half <= full;
end generate;

fifo.free <= not fifo.full;
fifo.avail <= not fifo.empty;
free <= not full;
avail <= not empty;


-- FIFO Write -----------------------------------------------------------------------------
-- Write Data -----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
fifo_memory: -- real FIFO memory (several entries)
if (fifo_depth_c > 1) generate
sync_read: process(clk_i)
begin
if rising_edge(clk_i) then -- no reset to infer block RAM
if (fifo.we = '1') then
fifo.data(to_integer(unsigned(fifo.w_pnt(fifo.w_pnt'left-1 downto 0)))) <= wdata_i;
end if;
end if;
end process sync_read;
end generate;

fifo_buffer: -- simple register (single entry)
if (fifo_depth_c = 1) generate
sync_read: process(rstn_i, clk_i)
begin
if (rstn_i = '0') then
fifo.data(0) <= (others => '0');
elsif rising_edge(clk_i) then
if (fifo.we = '1') then
fifo.data(0) <= wdata_i;
end if;
fifo_write: process(clk_i)
begin
if rising_edge(clk_i) then -- no reset to infer block RAM
if (we = '1') then
fifo_mem(to_integer(unsigned(w_pnt(w_pnt'left-1 downto 0)))) <= wdata_i;
end if;
end process sync_read;
end generate;
end if;
end process fifo_write;


-- FIFO Read ------------------------------------------------------------------------------
-- Read Data ------------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
fifo_read_async: -- asynchronous read
if (FIFO_RSYNC = false) generate
async_read: process(fifo)
begin
if (fifo_depth_c = 1) then
rdata_o <= fifo.data(0);
else
rdata_o <= fifo.data(to_integer(unsigned(fifo.r_pnt(fifo.r_pnt'left-1 downto 0))));
end if;
end process async_read;

if not FIFO_RSYNC generate
rdata_o <= fifo_mem(to_integer(unsigned(r_pnt(r_pnt'left-1 downto 0))));
-- status --
free_o <= fifo.free;
avail_o <= fifo.avail;
half_o <= fifo.half;
free_o <= free;
avail_o <= avail;
half_o <= half;
end generate;

fifo_read_sync: -- synchronous read
if (FIFO_RSYNC = true) generate
async_read: process(clk_i)
if FIFO_RSYNC generate
sync_read: process(clk_i)
begin
if rising_edge(clk_i) then
if (fifo_depth_c = 1) then
rdata_o <= fifo.data(0);
else
rdata_o <= fifo.data(to_integer(unsigned(fifo.r_pnt(fifo.r_pnt'left-1 downto 0))));
end if;
rdata_o <= fifo_mem(to_integer(unsigned(r_pnt(r_pnt'left-1 downto 0))));
end if;
end process async_read;

end process sync_read;
-- status --
sync_status_flags: process(rstn_i, clk_i)
begin
Expand All @@ -209,9 +173,9 @@ begin
avail_o <= '0';
half_o <= '0';
elsif rising_edge(clk_i) then
free_o <= fifo.free;
avail_o <= fifo.avail;
half_o <= fifo.half;
free_o <= free;
avail_o <= avail;
half_o <= half;
end if;
end process sync_status_flags;
end generate;
Expand Down
2 changes: 1 addition & 1 deletion rtl/core/neorv32_package.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ package neorv32_package is

-- Architecture Constants -----------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090205"; -- hardware version
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01090206"; -- hardware version
constant archid_c : natural := 19; -- official RISC-V architecture ID
constant XLEN : natural := 32; -- native data path width

Expand Down