library ieee;
|
|
use ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
|
|
|
|
|
|
entity fifo is
|
|
generic (
|
|
Formal : boolean := true;
|
|
Depth : positive := 16;
|
|
Width : positive := 16
|
|
);
|
|
port (
|
|
Reset_n_i : in std_logic;
|
|
Clk_i : in std_logic;
|
|
-- write
|
|
Wen_i : in std_logic;
|
|
Din_i : in std_logic_vector(Width-1 downto 0);
|
|
Full_o : out std_logic;
|
|
Werror_o : out std_logic;
|
|
-- read
|
|
Ren_i : in std_logic;
|
|
Dout_o : out std_logic_vector(Width-1 downto 0);
|
|
Empty_o : out std_logic;
|
|
Rerror_o : out std_logic
|
|
);
|
|
end entity fifo;
|
|
|
|
|
|
architecture rtl of fifo is
|
|
|
|
|
|
subtype t_fifo_pnt is natural range 0 to Depth-1;
|
|
signal s_write_pnt : t_fifo_pnt;
|
|
signal s_read_pnt : t_fifo_pnt;
|
|
|
|
type t_fifo_mem is array (t_fifo_pnt'low to t_fifo_pnt'high) of std_logic_vector(Din_i'range);
|
|
signal s_fifo_mem : t_fifo_mem;
|
|
|
|
signal s_almost_full : boolean;
|
|
signal s_almost_empty : boolean;
|
|
|
|
function incr_pnt (data : t_fifo_pnt) return t_fifo_pnt is
|
|
begin
|
|
if (data = t_fifo_mem'high) then
|
|
return 0;
|
|
end if;
|
|
return data + 1;
|
|
end function incr_pnt;
|
|
|
|
|
|
begin
|
|
|
|
|
|
s_almost_full <= (s_write_pnt = s_read_pnt - 1) or
|
|
(s_write_pnt = t_fifo_mem'high and s_read_pnt = t_fifo_mem'low);
|
|
|
|
s_almost_empty <= (s_read_pnt = s_write_pnt - 1) or
|
|
(s_read_pnt = t_fifo_mem'high and s_write_pnt = t_fifo_mem'low);
|
|
|
|
WriteP : process (Reset_n_i, Clk_i) is
|
|
begin
|
|
if (Reset_n_i = '0') then
|
|
s_write_pnt <= 0;
|
|
Werror_o <= '0';
|
|
elsif (rising_edge(Clk_i)) then
|
|
Werror_o <= Wen_i and Full_o;
|
|
if (Wen_i = '1' and Full_o = '0') then
|
|
s_fifo_mem(s_write_pnt) <= Din_i;
|
|
s_write_pnt <= incr_pnt(s_write_pnt);
|
|
end if;
|
|
end if;
|
|
end process WriteP;
|
|
|
|
|
|
ReadP : process (Reset_n_i, Clk_i) is
|
|
begin
|
|
if (Reset_n_i = '0') then
|
|
s_read_pnt <= 0;
|
|
Rerror_o <= '0';
|
|
elsif (rising_edge(Clk_i)) then
|
|
Rerror_o <= Ren_i and Empty_o;
|
|
if (Ren_i = '1' and Empty_o = '0') then
|
|
Dout_o <= s_fifo_mem(s_read_pnt);
|
|
s_read_pnt <= incr_pnt(s_read_pnt);
|
|
end if;
|
|
end if;
|
|
end process ReadP;
|
|
|
|
|
|
FlagsP : process (Reset_n_i, Clk_i) is
|
|
begin
|
|
if (Reset_n_i = '0') then
|
|
Full_o <= '0';
|
|
Empty_o <= '1';
|
|
elsif (rising_edge(Clk_i)) then
|
|
if (Wen_i = '1') then
|
|
if (Ren_i = '0' and s_almost_full) then
|
|
Full_o <= '1';
|
|
end if;
|
|
Empty_o <= '0';
|
|
end if;
|
|
if (Ren_i = '1') then
|
|
if (Wen_i = '0' and s_almost_empty) then
|
|
Empty_o <= '1';
|
|
end if;
|
|
Full_o <= '0';
|
|
end if;
|
|
end if;
|
|
end process FlagsP;
|
|
|
|
|
|
FormalG : if Formal generate
|
|
|
|
attribute anyconst : boolean;
|
|
signal s_data : std_logic_vector(Width-1 downto 0);
|
|
attribute anyconst of s_data : signal is true;
|
|
|
|
signal s_cnt : natural range 0 to Depth;
|
|
|
|
begin
|
|
|
|
|
|
default clock is rising_edge(Clk_i);
|
|
|
|
|
|
-- Initial reset
|
|
RESTRICT_RESET : restrict
|
|
{not Reset_n_i[*3]; Reset_n_i[+]}[*1];
|
|
|
|
-- Inputs are low during reset for simplicity
|
|
ASSUME_INPUTS_DURING_RESET : assume always
|
|
not Reset_n_i ->
|
|
not Wen_i and not Ren_i;
|
|
|
|
|
|
-- Asynchronous (unclocked) Reset asserts
|
|
FULL_RESET : process (all) is
|
|
begin
|
|
if (not Reset_n_i) then
|
|
RESET_FULL : assert not Full_o;
|
|
RESET_EMPTY : assert Empty_o;
|
|
RESET_WERROR : assert not Werror_o;
|
|
RESET_RERROR : assert not Rerror_o;
|
|
RESET_WRITE_PNT : assert s_write_pnt = 0;
|
|
RESET_READ_PNT : assert s_read_pnt = 0;
|
|
end if;
|
|
end process;
|
|
|
|
|
|
-- No write pointer change when writing into full fifo
|
|
WRITE_PNT_STABLE_WHEN_FULL : assert always
|
|
Wen_i and Full_o ->
|
|
next stable(s_write_pnt);
|
|
|
|
-- No read pointer change when reading from empty fifo
|
|
READ_PNT_STABLE_WHEN_EMPTY : assert always
|
|
Ren_i and Empty_o ->
|
|
next stable(s_read_pnt);
|
|
|
|
-- Full when write and no read and write pointer ran up to read pointer
|
|
FULL : assert always
|
|
Wen_i and not Ren_i and
|
|
(s_write_pnt = s_read_pnt - 1 or s_write_pnt = t_fifo_pnt'high and s_read_pnt = t_fifo_pnt'low) ->
|
|
next Full_o;
|
|
|
|
-- Not full when read
|
|
NOT_FULL : assert always
|
|
Ren_i ->
|
|
next not Full_o;
|
|
|
|
-- Empty when read and no write and read pointer ran up to write pointer
|
|
EMPTY : assert always
|
|
not Wen_i and Ren_i and
|
|
(s_read_pnt = s_write_pnt - 1 or s_read_pnt = t_fifo_pnt'high and s_write_pnt = t_fifo_pnt'low) ->
|
|
next Empty_o;
|
|
|
|
-- Not empty when write
|
|
NOT_EMPTY : assert always
|
|
Wen_i ->
|
|
next not Empty_o;
|
|
|
|
-- Write error when writing into full fifo
|
|
WERROR : assert always
|
|
Wen_i and Full_o ->
|
|
next Werror_o;
|
|
|
|
-- No write error when fifo not full
|
|
NO_WERROR : assert always
|
|
not Full_o ->
|
|
next not Werror_o;
|
|
|
|
-- Read error when reading from empty fifo
|
|
RERROR : assert always
|
|
Ren_i and Empty_o ->
|
|
next Rerror_o;
|
|
|
|
-- No read error when fifo not empty
|
|
NO_RERROR : assert always
|
|
not Empty_o ->
|
|
next not Rerror_o;
|
|
|
|
-- Write pointer increment when writing into not full fifo
|
|
-- and write pointer isn't at end value
|
|
WRITE_PNT_INCR : assert always
|
|
Wen_i and not Full_o and s_write_pnt /= t_fifo_pnt'high ->
|
|
next s_write_pnt = prev(s_write_pnt) + 1;
|
|
|
|
-- Write pointer wraparound when writing into not full fifo
|
|
-- and write pointer is at end value
|
|
WRITE_PNT_WRAP : assert always
|
|
Wen_i and not Full_o and s_write_pnt = t_fifo_pnt'high ->
|
|
next s_write_pnt = 0;
|
|
|
|
-- Read pointer increment when reading from not empty fifo
|
|
-- and read pointer isn't at end value
|
|
READ_PNT_INCR : assert always
|
|
Ren_i and not Empty_o and s_read_pnt /= t_fifo_pnt'high ->
|
|
next s_read_pnt = prev(s_read_pnt) + 1;
|
|
|
|
-- Read pointer wraparound when reading from not empty fifo
|
|
-- and read pointer is at end value
|
|
READ_PNT_WRAP : assert always
|
|
Ren_i and not Empty_o and s_read_pnt = t_fifo_pnt'high ->
|
|
next s_read_pnt = 0;
|
|
|
|
-- Correct input data stored after valid write access
|
|
DIN_VALID : assert always
|
|
Wen_i and not Full_o ->
|
|
next s_fifo_mem(s_write_pnt - 1) = prev(Din_i);
|
|
|
|
-- Correct output data after valid read access
|
|
DOUT_VALID : assert always
|
|
Ren_i and not Empty_o ->
|
|
next Dout_o = s_fifo_mem(s_read_pnt - 1);
|
|
|
|
-- Fillstate calculation
|
|
process (Clk_i) is
|
|
begin
|
|
if Reset_n_i = '0' then
|
|
s_cnt <= 0;
|
|
elsif rising_edge(Clk_i) then
|
|
if Wen_i = '1' and Full_o = '0' and (Ren_i = '0' or Empty_o = '1') then
|
|
s_cnt <= s_cnt + 1;
|
|
elsif Ren_i = '1' and Empty_o = '0' and (Wen_i = '0' or Full_o = '1') then
|
|
s_cnt <= s_cnt - 1;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
-- Data flow checks
|
|
-- GHDL only allows numerals in repetition operators
|
|
-- so we have to use separate checks for each fill state
|
|
DATA_FLOW_0 : assert always
|
|
{{s_cnt = 0 and Wen_i = '1' and Din_i = s_data} ; {Ren_i[->1]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
DATA_FLOW_1 : assert always
|
|
{{s_cnt = 1 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->2]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
DATA_FLOW_2 : assert always
|
|
{{s_cnt = 2 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->3]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
DATA_FLOW_3 : assert always
|
|
{{s_cnt = 3 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->4]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
DATA_FLOW_4 : assert always
|
|
{{s_cnt = 4 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->5]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
DATA_FLOW_5 : assert always
|
|
{{s_cnt = 5 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->6]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
DATA_FLOW_6 : assert always
|
|
{{s_cnt = 6 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->7]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
DATA_FLOW_7 : assert always
|
|
{{s_cnt = 7 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->8]}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
|
|
-- An alternative for GHDL: Replacing the [->] goto operator
|
|
-- by counting Ren_i starting when s_data is written into fifo.
|
|
-- The properties have to be slightly modified only.
|
|
-- Sadly proving these needs much more (engine dependend) time than
|
|
-- the separate checks above. I assume the counters are the reason.
|
|
gen_data_checks : for i in 0 to Depth-1 generate
|
|
|
|
begin
|
|
|
|
GEN : if i = 0 generate
|
|
|
|
DATA_FLOW_GEN : assert always
|
|
{{s_cnt = i and Wen_i = '1' and Din_i = s_data} ; {not Ren_i[*]; Ren_i}}
|
|
|=> {Dout_o = s_data};
|
|
|
|
else generate
|
|
|
|
signal s_ren_cnt : integer range -1 to i+1 := -1;
|
|
|
|
begin
|
|
|
|
process (Reset_n_i, Clk_i) is
|
|
begin
|
|
if not Reset_n_i then
|
|
s_ren_cnt <= -1;
|
|
elsif (rising_edge(Clk_i)) then
|
|
if s_cnt = i and Wen_i = '1' and Din_i = s_data then
|
|
s_ren_cnt <= 1 when Ren_i else 0;
|
|
else
|
|
s_ren_cnt <= s_ren_cnt + 1 when Ren_i = '1' and s_ren_cnt >= 0;
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
DATA_FLOW_GEN : assert always
|
|
{{s_cnt = i and Wen_i = '1' and Din_i = s_data} : {Ren_i[*]; s_ren_cnt = i+1}}
|
|
|-> {Dout_o = s_data};
|
|
|
|
end generate GEN;
|
|
|
|
end generate gen_data_checks;
|
|
|
|
end generate FormalG;
|
|
|
|
|
|
end architecture rtl;
|