Trying to verify Verilog/VHDL designs with formal methods and tools
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
9.3 KiB

  1. library ieee;
  2. use ieee.std_logic_1164.all;
  3. use ieee.numeric_std.all;
  4. entity fifo is
  5. generic (
  6. Formal : boolean := true;
  7. Depth : positive := 16;
  8. Width : positive := 16
  9. );
  10. port (
  11. Reset_n_i : in std_logic;
  12. Clk_i : in std_logic;
  13. -- write
  14. Wen_i : in std_logic;
  15. Din_i : in std_logic_vector(Width-1 downto 0);
  16. Full_o : out std_logic;
  17. Werror_o : out std_logic;
  18. -- read
  19. Ren_i : in std_logic;
  20. Dout_o : out std_logic_vector(Width-1 downto 0);
  21. Empty_o : out std_logic;
  22. Rerror_o : out std_logic
  23. );
  24. end entity fifo;
  25. architecture rtl of fifo is
  26. subtype t_fifo_pnt is natural range 0 to Depth-1;
  27. signal s_write_pnt : t_fifo_pnt;
  28. signal s_read_pnt : t_fifo_pnt;
  29. type t_fifo_mem is array (t_fifo_pnt'low to t_fifo_pnt'high) of std_logic_vector(Din_i'range);
  30. signal s_fifo_mem : t_fifo_mem;
  31. signal s_almost_full : boolean;
  32. signal s_almost_empty : boolean;
  33. function incr_pnt (data : t_fifo_pnt) return t_fifo_pnt is
  34. begin
  35. if (data = t_fifo_mem'high) then
  36. return 0;
  37. end if;
  38. return data + 1;
  39. end function incr_pnt;
  40. begin
  41. s_almost_full <= (s_write_pnt = s_read_pnt - 1) or
  42. (s_write_pnt = t_fifo_mem'high and s_read_pnt = t_fifo_mem'low);
  43. s_almost_empty <= (s_read_pnt = s_write_pnt - 1) or
  44. (s_read_pnt = t_fifo_mem'high and s_write_pnt = t_fifo_mem'low);
  45. WriteP : process (Reset_n_i, Clk_i) is
  46. begin
  47. if (Reset_n_i = '0') then
  48. s_write_pnt <= 0;
  49. Werror_o <= '0';
  50. elsif (rising_edge(Clk_i)) then
  51. Werror_o <= Wen_i and Full_o;
  52. if (Wen_i = '1' and Full_o = '0') then
  53. s_fifo_mem(s_write_pnt) <= Din_i;
  54. s_write_pnt <= incr_pnt(s_write_pnt);
  55. end if;
  56. end if;
  57. end process WriteP;
  58. ReadP : process (Reset_n_i, Clk_i) is
  59. begin
  60. if (Reset_n_i = '0') then
  61. s_read_pnt <= 0;
  62. Rerror_o <= '0';
  63. elsif (rising_edge(Clk_i)) then
  64. Rerror_o <= Ren_i and Empty_o;
  65. if (Ren_i = '1' and Empty_o = '0') then
  66. Dout_o <= s_fifo_mem(s_read_pnt);
  67. s_read_pnt <= incr_pnt(s_read_pnt);
  68. end if;
  69. end if;
  70. end process ReadP;
  71. FlagsP : process (Reset_n_i, Clk_i) is
  72. begin
  73. if (Reset_n_i = '0') then
  74. Full_o <= '0';
  75. Empty_o <= '1';
  76. elsif (rising_edge(Clk_i)) then
  77. if (Wen_i = '1') then
  78. if (Ren_i = '0' and s_almost_full) then
  79. Full_o <= '1';
  80. end if;
  81. Empty_o <= '0';
  82. end if;
  83. if (Ren_i = '1') then
  84. if (Wen_i = '0' and s_almost_empty) then
  85. Empty_o <= '1';
  86. end if;
  87. Full_o <= '0';
  88. end if;
  89. end if;
  90. end process FlagsP;
  91. FormalG : if Formal generate
  92. attribute anyconst : boolean;
  93. signal s_data : std_logic_vector(Width-1 downto 0);
  94. attribute anyconst of s_data : signal is true;
  95. signal s_cnt : natural range 0 to Depth;
  96. begin
  97. default clock is rising_edge(Clk_i);
  98. -- Initial reset
  99. RESTRICT_RESET : restrict
  100. {not Reset_n_i[*3]; Reset_n_i[+]}[*1];
  101. -- Inputs are low during reset for simplicity
  102. ASSUME_INPUTS_DURING_RESET : assume always
  103. not Reset_n_i ->
  104. not Wen_i and not Ren_i;
  105. -- Asynchronous (unclocked) Reset asserts
  106. FULL_RESET : process (all) is
  107. begin
  108. if (not Reset_n_i) then
  109. RESET_FULL : assert not Full_o;
  110. RESET_EMPTY : assert Empty_o;
  111. RESET_WERROR : assert not Werror_o;
  112. RESET_RERROR : assert not Rerror_o;
  113. RESET_WRITE_PNT : assert s_write_pnt = 0;
  114. RESET_READ_PNT : assert s_read_pnt = 0;
  115. end if;
  116. end process;
  117. -- No write pointer change when writing into full fifo
  118. WRITE_PNT_STABLE_WHEN_FULL : assert always
  119. Wen_i and Full_o ->
  120. next stable(s_write_pnt);
  121. -- No read pointer change when reading from empty fifo
  122. READ_PNT_STABLE_WHEN_EMPTY : assert always
  123. Ren_i and Empty_o ->
  124. next stable(s_read_pnt);
  125. -- Full when write and no read and write pointer ran up to read pointer
  126. FULL : assert always
  127. Wen_i and not Ren_i and
  128. (s_write_pnt = s_read_pnt - 1 or s_write_pnt = t_fifo_pnt'high and s_read_pnt = t_fifo_pnt'low) ->
  129. next Full_o;
  130. -- Not full when read
  131. NOT_FULL : assert always
  132. Ren_i ->
  133. next not Full_o;
  134. -- Empty when read and no write and read pointer ran up to write pointer
  135. EMPTY : assert always
  136. not Wen_i and Ren_i and
  137. (s_read_pnt = s_write_pnt - 1 or s_read_pnt = t_fifo_pnt'high and s_write_pnt = t_fifo_pnt'low) ->
  138. next Empty_o;
  139. -- Not empty when write
  140. NOT_EMPTY : assert always
  141. Wen_i ->
  142. next not Empty_o;
  143. -- Write error when writing into full fifo
  144. WERROR : assert always
  145. Wen_i and Full_o ->
  146. next Werror_o;
  147. -- No write error when fifo not full
  148. NO_WERROR : assert always
  149. not Full_o ->
  150. next not Werror_o;
  151. -- Read error when reading from empty fifo
  152. RERROR : assert always
  153. Ren_i and Empty_o ->
  154. next Rerror_o;
  155. -- No read error when fifo not empty
  156. NO_RERROR : assert always
  157. not Empty_o ->
  158. next not Rerror_o;
  159. -- Write pointer increment when writing into not full fifo
  160. -- and write pointer isn't at end value
  161. WRITE_PNT_INCR : assert always
  162. Wen_i and not Full_o and s_write_pnt /= t_fifo_pnt'high ->
  163. next s_write_pnt = prev(s_write_pnt) + 1;
  164. -- Write pointer wraparound when writing into not full fifo
  165. -- and write pointer is at end value
  166. WRITE_PNT_WRAP : assert always
  167. Wen_i and not Full_o and s_write_pnt = t_fifo_pnt'high ->
  168. next s_write_pnt = 0;
  169. -- Read pointer increment when reading from not empty fifo
  170. -- and read pointer isn't at end value
  171. READ_PNT_INCR : assert always
  172. Ren_i and not Empty_o and s_read_pnt /= t_fifo_pnt'high ->
  173. next s_read_pnt = prev(s_read_pnt) + 1;
  174. -- Read pointer wraparound when reading from not empty fifo
  175. -- and read pointer is at end value
  176. READ_PNT_WRAP : assert always
  177. Ren_i and not Empty_o and s_read_pnt = t_fifo_pnt'high ->
  178. next s_read_pnt = 0;
  179. -- Correct input data stored after valid write access
  180. DIN_VALID : assert always
  181. Wen_i and not Full_o ->
  182. next s_fifo_mem(s_write_pnt - 1) = prev(Din_i);
  183. -- Correct output data after valid read access
  184. DOUT_VALID : assert always
  185. Ren_i and not Empty_o ->
  186. next Dout_o = s_fifo_mem(s_read_pnt - 1);
  187. -- Fillstate calculation
  188. process (Clk_i) is
  189. begin
  190. if Reset_n_i = '0' then
  191. s_cnt <= 0;
  192. elsif rising_edge(Clk_i) then
  193. if Wen_i = '1' and Full_o = '0' and (Ren_i = '0' or Empty_o = '1') then
  194. s_cnt <= s_cnt + 1;
  195. elsif Ren_i = '1' and Empty_o = '0' and (Wen_i = '0' or Full_o = '1') then
  196. s_cnt <= s_cnt - 1;
  197. end if;
  198. end if;
  199. end process;
  200. -- Data flow checks
  201. -- GHDL only allows numerals in repetition operators
  202. -- so we have to use separate checks for each fill state
  203. DATA_FLOW_0 : assert always
  204. {{s_cnt = 0 and Wen_i = '1' and Din_i = s_data} ; {Ren_i[->1]}}
  205. |=> {Dout_o = s_data};
  206. DATA_FLOW_1 : assert always
  207. {{s_cnt = 1 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->2]}}
  208. |=> {Dout_o = s_data};
  209. DATA_FLOW_2 : assert always
  210. {{s_cnt = 2 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->3]}}
  211. |=> {Dout_o = s_data};
  212. DATA_FLOW_3 : assert always
  213. {{s_cnt = 3 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->4]}}
  214. |=> {Dout_o = s_data};
  215. DATA_FLOW_4 : assert always
  216. {{s_cnt = 4 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->5]}}
  217. |=> {Dout_o = s_data};
  218. DATA_FLOW_5 : assert always
  219. {{s_cnt = 5 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->6]}}
  220. |=> {Dout_o = s_data};
  221. DATA_FLOW_6 : assert always
  222. {{s_cnt = 6 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->7]}}
  223. |=> {Dout_o = s_data};
  224. DATA_FLOW_7 : assert always
  225. {{s_cnt = 7 and Wen_i = '1' and Din_i = s_data} : {Ren_i[->8]}}
  226. |=> {Dout_o = s_data};
  227. -- An alternative for GHDL: Replacing the [->] goto operator
  228. -- by counting Ren_i starting when s_data is written into fifo.
  229. -- The properties have to be slightly modified only.
  230. -- Sadly proving these needs much more (engine dependend) time than
  231. -- the separate checks above. I assume the counters are the reason.
  232. gen_data_checks : for i in 0 to Depth-1 generate
  233. begin
  234. GEN : if i = 0 generate
  235. DATA_FLOW_GEN : assert always
  236. {{s_cnt = i and Wen_i = '1' and Din_i = s_data} ; {not Ren_i[*]; Ren_i}}
  237. |=> {Dout_o = s_data};
  238. else generate
  239. signal s_ren_cnt : integer range -1 to i+1 := -1;
  240. begin
  241. process (Reset_n_i, Clk_i) is
  242. begin
  243. if not Reset_n_i then
  244. s_ren_cnt <= -1;
  245. elsif (rising_edge(Clk_i)) then
  246. if s_cnt = i and Wen_i = '1' and Din_i = s_data then
  247. s_ren_cnt <= 1 when Ren_i else 0;
  248. else
  249. s_ren_cnt <= s_ren_cnt + 1 when Ren_i = '1' and s_ren_cnt >= 0;
  250. end if;
  251. end if;
  252. end process;
  253. DATA_FLOW_GEN : assert always
  254. {{s_cnt = i and Wen_i = '1' and Din_i = s_data} : {Ren_i[*]; s_ren_cnt = i+1}}
  255. |-> {Dout_o = s_data};
  256. end generate GEN;
  257. end generate gen_data_checks;
  258. end generate FormalG;
  259. end architecture rtl;