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;