├── README.md ├── _config.yml ├── sync_fifo.sv └── testbench.sv /README.md: -------------------------------------------------------------------------------- 1 | # Synchronous FIFO: Assertion based Verification 2 | 3 | FIFOs or any other memory element require more detailed verification effort before it can synthesized on hardware like FPGAs/ASIC. 4 | Here, I have presented many different assertions that can be utilized to verify a synchronous FIFO using SystemVerilog. 5 | 6 | I encourage you to go through them and then try to write your own assertions for your design specific FIFO. **The assertions described 7 | are not an exhastive list. I have left other check for you to add.** 8 | Remember, there are many ways of capturing an assertion, so don't use my constructs to be the only way of wirting assertions :-) 9 | 10 | 1) Asynchronous reset assertions 11 | 12 | ``` sv 13 | // Reset startup check // 14 | // need this at the very begining of the simulation // 15 | property async_rst_startup; 16 | @(posedge i_clk) !i_rst_n |-> ##1 (wr_ptr==0 && rd_ptr == 0 && o_empty); 17 | endproperty 18 | 19 | // rst check in general 20 | property async_rst_chk; 21 | @(negedge i_rst_n) 1'b1 |-> ##1 @(posedge i_clk) (wr_ptr==0 && rd_ptr == 0 && o_empty); 22 | endproperty 23 | ``` 24 | 25 | 2) Check data written at a location is the same data read when read_ptr reaches that location 26 | ``` sv 27 | sequence rd_detect(ptr); 28 | ##[0:$] (rd_en && !o_empty && (rd_ptr == ptr)); 29 | endsequence 30 | 31 | property data_wr_rd_chk(wrPtr); 32 | // local variable 33 | integer ptr, data; 34 | @(posedge i_clk) disable iff(!i_rst_n) 35 | (wr_en && !o_full, ptr = wrPtr, data = i_data, $display($time, " wr_ptr=%h, i_fifo=%h",wr_ptr, i_data)) 36 | |-> ##1 first_match(rd_detect(ptr), $display($time, " rd_ptr=%h, o_fifo=%h",rd_ptr, o_data)) ##0 o_data == data; 37 | endproperty 38 | ``` 39 | 3) Rule-1: Never write to FIFO if it's Full! 40 | ```sv 41 | property dont_write_if_full; 42 | // @(posedge i_clk) disable iff(!i_rst_n) o_full |-> ##1 $stable(wr_ptr); 43 | // alternative way of writing the same assertion 44 | @(posedge i_clk) disable iff(!i_rst_n) wr_en && o_full |-> ##1 wr_ptr == $past(wr_ptr); 45 | endproperty 46 | ``` 47 | 48 | 4) Rule-2: Never read from an Empty FIFO! 49 | ```sv 50 | property dont_read_if_empty; 51 | @(posedge i_clk) disable iff(!i_rst_n) rd_en && o_empty |-> ##1 $stable(rd_ptr); 52 | endproperty 53 | ``` 54 | 55 | 5) On successful write, write_ptr should only increment by 1 56 | ```sv 57 | property inc_wr_one; 58 | @(posedge i_clk) disable iff(!i_rst_n) wr_en && !o_full |-> ##1 (wr_ptr-1'b1 == $past(wr_ptr)); 59 | endproperty 60 | ``` 61 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /sync_fifo.sv: -------------------------------------------------------------------------------- 1 | module syncFIFO_v2 2 | #(parameter WIDTH = 4, 3 | parameter DEPTH_LEN = 4) // 2^4 depth 4 | ( 5 | i_clk, i_rst_n, i_data, wr_en, 6 | rd_en, o_data, o_full, o_empty 7 | ); 8 | 9 | input i_clk, i_rst_n; 10 | input [WIDTH-1:0] i_data; 11 | 12 | input wr_en, rd_en; 13 | 14 | output reg[WIDTH-1:0] o_data; 15 | 16 | reg [WIDTH-1:0] mem [(1< ##1 (wr_ptr==0 && rd_ptr == 0 && o_empty); 81 | endproperty 82 | assert property (async_rst_startup) 83 | else $display("rst assertion failed at strup", $time); 84 | 85 | // rst check in general 86 | property async_rst_chk; 87 | @(negedge i_rst_n) 1'b1 |-> ##1 @(posedge i_clk) (wr_ptr==0 && rd_ptr == 0 && o_empty); 88 | endproperty 89 | assert property (async_rst_chk) 90 | else $display("rst assertion failed: ", $time); 91 | // 1) check if data written to a location is the same data read when read ptr reaches the location 92 | // This is a really great way to test fifo and should be used for any memory based design 93 | // it's more involved to write such an assertion but great way to use systemverilog features 94 | 95 | sequence rd_detect(ptr); 96 | ##[0:$] (rd_en && !o_empty && (rd_ptr == ptr)); 97 | endsequence 98 | 99 | property data_wr_rd_chk(wrPtr); 100 | // local variable 101 | integer ptr, data; 102 | @(posedge i_clk) disable iff(!i_rst_n) 103 | (wr_en && !o_full, ptr = wrPtr, data = i_data, $display($time, " wr_ptr=%h, i_fifo=%h",wr_ptr, i_data)) 104 | |-> ##1 first_match(rd_detect(ptr), $display($time, " rd_ptr=%h, o_fifo=%h",rd_ptr, o_data)) ##0 o_data == data; 105 | endproperty 106 | 107 | assert property(data_wr_rd_chk(wr_ptr)) 108 | else $display("time data chk: ", $time); 109 | 110 | // 2) Don't write to fifo if full: one of the two golden rule in fifo design // 111 | 112 | property dont_write_if_full; 113 | // @(posedge i_clk) disable iff(!i_rst_n) o_full |-> ##1 $stable(wr_ptr); 114 | // alternative way of writing the same assertion 115 | @(posedge i_clk) disable iff(!i_rst_n) wr_en && o_full |-> ##1 wr_ptr == $past(wr_ptr); 116 | endproperty 117 | 118 | assert property (dont_write_if_full) 119 | else $display("failed at time p2: ", $time); 120 | 121 | // 3) don't read when empty: second golden rule 122 | property dont_read_if_empty; 123 | @(posedge i_clk) disable iff(!i_rst_n) rd_en && o_empty |-> ##1 $stable(rd_ptr); 124 | endproperty 125 | 126 | assert property (dont_read_if_empty) 127 | else $display("failed at time p3: ", $time); 128 | 129 | // 4) rd/wr ptr should onlu increment by 1 on rd/wr req 130 | property inc_wr_one; 131 | @(posedge i_clk) disable iff(!i_rst_n) wr_en && !o_full |-> ##1 (wr_ptr-1'b1 == $past(wr_ptr)); 132 | endproperty 133 | 134 | assert property (inc_wr_one) 135 | else $display("time p4: ", $time); 136 | 137 | // 5) rd/wr ptr should onlu increment by 1 on rd/wr req 138 | property inc_rd_ptr; 139 | @(posedge i_clk) disable iff(!i_rst_n) rd_en && !o_empty |-> ##1 (rd_ptr - 1'b1 == $past(rd_ptr)); 140 | endproperty 141 | 142 | assert property (inc_rd_ptr) 143 | else $display("time p5: ", $time); 144 | 145 | endmodule 146 | -------------------------------------------------------------------------------- /testbench.sv: -------------------------------------------------------------------------------- 1 | module test_v2(); 2 | 3 | parameter WIDTH = 32; 4 | parameter DEPTH_LEN = 4; 5 | 6 | reg i_clk, i_rst_n; 7 | reg [WIDTH-1:0] i_data; 8 | reg wr_en, rd_en; 9 | wire [WIDTH-1:0] o_data; 10 | wire o_full; 11 | wire o_empty; 12 | 13 | syncFIFO_v2 #( .WIDTH(4), .DEPTH_LEN(4)) DUT ( 14 | .i_clk(i_clk), .i_rst_n(i_rst_n), .i_data(i_data), .wr_en(wr_en), 15 | .rd_en(rd_en), .o_data(o_data), .o_full(o_full), .o_empty(o_empty) 16 | ); 17 | 18 | initial begin 19 | // below two lines are used to show waveform 20 | $dumpfile("dump.vcd"); 21 | $dumpvars; 22 | end 23 | 24 | initial begin 25 | i_clk = 1'b0; 26 | i_rst_n = 1'b0; 27 | wr_en = 1'b0; 28 | rd_en = 1'b0; 29 | i_data = 32'h00000000; 30 | #100 i_rst_n = 1'b1; 31 | 32 | write_task; 33 | #20; 34 | @(negedge i_clk); 35 | wr_en = 1'b0; 36 | read_task; 37 | #10; 38 | write_task; 39 | #100 $finish; 40 | end 41 | 42 | always #5 i_clk <= ~i_clk; 43 | 44 | task write_task; 45 | begin 46 | @(negedge i_clk); 47 | wr_en = 1'b1; 48 | repeat(10) 49 | begin 50 | @(negedge i_clk); 51 | i_data = $urandom($random)%16; 52 | @(posedge i_clk); 53 | // $display(" FIFO WRITE DATA"); 54 | // $display("in data: %d", i_data); 55 | end 56 | end 57 | endtask 58 | 59 | task read_task; 60 | begin 61 | @(negedge i_clk); 62 | rd_en = 1'b1; 63 | repeat(10) 64 | begin 65 | @(posedge i_clk); 66 | @(posedge i_clk); 67 | // $display(" FIFO READ DATA"); 68 | // $display("rd data: ", $time); 69 | // $display("out data: %d", o_data); 70 | end 71 | end 72 | endtask 73 | 74 | endmodule 75 | --------------------------------------------------------------------------------