mirror of
				https://github.com/YosysHQ/sby.git
				synced 2025-10-31 13:02:28 +00:00 
			
		
		
		
	w_underfill should provide identical results regardless of whether or not Verific is used. w_overfill doesn't have the extra check for prettiness without Verific because I'm too lazy to do it. Replaced $past function with past_nwen register to ensure correct operation. Expanded w_underfill under Verific to use a property block to more easily compare the two versions side by side. Changed Concurrent assertions section of doc to compare the two implementations of w_underfill. Should provide a better example for why using verific makes it easier.
		
			
				
	
	
		
			197 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			Systemverilog
		
	
	
	
	
	
			
		
		
	
	
			197 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			Systemverilog
		
	
	
	
	
	
| // address generator/counter
 | |
| module addr_gen 
 | |
| #(  parameter MAX_DATA=16
 | |
| ) ( input en, clk, rst,
 | |
|     output reg [3:0] addr
 | |
| );
 | |
|     initial addr <= 0;
 | |
| 
 | |
|     // async reset
 | |
|     // increment address when enabled
 | |
|     always @(posedge clk or posedge rst)
 | |
|         if (rst)
 | |
|             addr <= 0;
 | |
|         else if (en) begin
 | |
|             if (addr == MAX_DATA-1)
 | |
|                 addr <= 0;
 | |
|             else
 | |
|                 addr <= addr + 1;
 | |
|         end
 | |
| endmodule
 | |
| 
 | |
| // Define our top level fifo entity
 | |
| module fifo 
 | |
| #(  parameter MAX_DATA=16
 | |
| ) ( input wen, ren, clk, rst,
 | |
|     input [7:0] wdata,
 | |
|     output [7:0] rdata,
 | |
|     output [4:0] count,
 | |
|     output full, empty
 | |
| );
 | |
|     wire wskip, rskip;
 | |
|     reg [4:0] data_count;
 | |
| 
 | |
|     // fifo storage
 | |
|     // async read, sync write
 | |
|     wire [3:0] waddr, raddr;
 | |
|     reg [7:0] data [MAX_DATA-1:0];
 | |
|     always @(posedge clk)
 | |
|         if (wen) 
 | |
|             data[waddr] <= wdata;
 | |
|     assign rdata = data[raddr];
 | |
|     // end storage
 | |
| 
 | |
|     // addr_gen for both write and read addresses
 | |
|     addr_gen #(.MAX_DATA(MAX_DATA))
 | |
|     fifo_writer (
 | |
|         .en     (wen || wskip),
 | |
|         .clk    (clk  ),
 | |
|         .rst    (rst),
 | |
|         .addr   (waddr)
 | |
|     );
 | |
| 
 | |
|     addr_gen #(.MAX_DATA(MAX_DATA))
 | |
|     fifo_reader (
 | |
|         .en     (ren || rskip),
 | |
|         .clk    (clk  ),
 | |
|         .rst    (rst),
 | |
|         .addr   (raddr)
 | |
|     );
 | |
| 
 | |
|     // status signals
 | |
|     initial data_count <= 0;
 | |
| 
 | |
|     always @(posedge clk or posedge rst) begin
 | |
|         if (rst)
 | |
|             data_count <= 0;
 | |
|         else if (wen && !ren && data_count < MAX_DATA)
 | |
|             data_count <= data_count + 1;
 | |
|         else if (ren && !wen && data_count > 0)
 | |
|             data_count <= data_count - 1;
 | |
|     end
 | |
| 
 | |
|     assign full  = data_count == MAX_DATA;
 | |
|     assign empty = (data_count == 0) && ~rst;
 | |
|     assign count = data_count;
 | |
| 
 | |
|     // overflow protection
 | |
| `ifndef NO_FULL_SKIP
 | |
|     // write while full => overwrite oldest data, move read pointer
 | |
|     assign rskip = wen && !ren && data_count >= MAX_DATA;
 | |
|     // read while empty => read invalid data, keep write pointer in sync
 | |
|     assign wskip = ren && !wen && data_count == 0;
 | |
| `else
 | |
|     assign rskip = 0;
 | |
|     assign wskip = 0;
 | |
| `endif // NO_FULL_SKIP
 | |
| 
 | |
| `ifdef FORMAL
 | |
|     // observers
 | |
|     wire [4:0] addr_diff;
 | |
|     assign addr_diff = waddr >= raddr 
 | |
|                      ? waddr - raddr 
 | |
|                      : waddr + MAX_DATA - raddr;
 | |
| 
 | |
|     // tests
 | |
|     always @(posedge clk) begin
 | |
|         if (~rst) begin
 | |
|             // waddr and raddr can only be non zero if reset is low
 | |
|             w_nreset: cover (waddr || raddr);
 | |
| 
 | |
|             // count never more than max
 | |
|             a_oflow:  assert (count <= MAX_DATA);
 | |
|             a_oflow2: assert (waddr < MAX_DATA);
 | |
| 
 | |
|             // count should be equal to the difference between writer and reader address
 | |
|             a_count_diff: assert (count == addr_diff 
 | |
|                                || count == MAX_DATA && addr_diff == 0);
 | |
| 
 | |
|             // count should only be able to increase or decrease by 1
 | |
|             a_counts: assert (count == 0
 | |
|                            || count == $past(count)
 | |
|                            || count == $past(count) + 1
 | |
|                            || count == $past(count) - 1);
 | |
| 
 | |
|             // read/write addresses can only increase (or stay the same)
 | |
|             a_raddr: assert (raddr == 0
 | |
|                           || raddr == $past(raddr)
 | |
|                           || raddr == $past(raddr + 1));
 | |
|             a_waddr: assert (waddr == 0
 | |
|                           || waddr == $past(waddr)
 | |
|                           || waddr == $past(waddr + 1));
 | |
| 
 | |
|             // full and empty work as expected
 | |
|             a_full:  assert (!full || count == MAX_DATA);
 | |
|             w_full:  cover  (wen && !ren && count == MAX_DATA-1);
 | |
|             a_empty: assert (!empty || count == 0);
 | |
|             w_empty: cover  (ren && !wen && count == 1);
 | |
| 
 | |
|             // reading/writing non zero values
 | |
|             w_nzero_write: cover (wen && wdata);
 | |
|             w_nzero_read:  cover (ren && rdata);
 | |
|         end else begin
 | |
|             // waddr and raddr are zero while reset is high
 | |
|             a_reset: assert (!waddr && !raddr);
 | |
|             w_reset: cover  (rst);
 | |
| 
 | |
|             // outputs are zero while reset is high
 | |
|             a_zero_out: assert (!empty && !full && !count);
 | |
|         end
 | |
|     end
 | |
| 
 | |
| `ifdef VERIFIC
 | |
|     // if we have verific we can also do the following additional tests
 | |
|     // read/write enables enable
 | |
|     ap_raddr2: assert property (@(posedge clk) disable iff (rst) ren |=> $changed(raddr));
 | |
|     ap_waddr2: assert property (@(posedge clk) disable iff (rst) wen |=> $changed(waddr));
 | |
| 
 | |
|     // read/write needs enable UNLESS full/empty
 | |
|     ap_raddr3: assert property (@(posedge clk) disable iff (rst) !ren && !full  |=> $stable(raddr));
 | |
|     ap_waddr3: assert property (@(posedge clk) disable iff (rst) !wen && !empty |=> $stable(waddr));
 | |
| 
 | |
|     // use block formatting for w_underfill so it's easier to describe in docs
 | |
|     // and is more readily comparable with the non SVA implementation
 | |
|     property write_skip;
 | |
|         @(posedge clk) disable iff (rst)
 | |
|         !wen |=> $changed(waddr);
 | |
|     endproperty
 | |
|     w_underfill: cover property (write_skip);
 | |
| 
 | |
|     // look for an overfill where the value in memory changes
 | |
|     // the change in data makes certain that the value is overriden
 | |
|     let d_change = (wdata != rdata);
 | |
|     property read_skip;
 | |
|         @(posedge clk) disable iff (rst) 
 | |
|         !ren && d_change |=> $changed(raddr);
 | |
|     endproperty
 | |
|     w_overfill:  cover property (read_skip);
 | |
| `else // !VERIFIC
 | |
|     // implementing w_underfill without properties
 | |
|     // can't use !$past(wen) since it will always trigger in the first cycle
 | |
|     reg past_nwen;
 | |
|     initial past_nwen <= 0;
 | |
|     always @(posedge clk) begin
 | |
|         if (rst) past_nwen <= 0;
 | |
|         if (!rst) begin
 | |
|             w_underfill: cover (past_nwen && $changed(waddr));
 | |
|             past_nwen <= !wen;
 | |
|         end
 | |
|     end
 | |
|     // end w_underfill
 | |
| 
 | |
|     // w_overfill does the same, but has been separated so that w_underfill
 | |
|     // can be included in the docs more cleanly
 | |
|     reg past_nren;
 | |
|     initial past_nren <= 0;
 | |
|     always @(posedge clk) begin
 | |
|         if (rst) past_nren <= 0;
 | |
|         if (!rst) begin
 | |
|             w_overfill: cover (past_nren && $changed(raddr));
 | |
|             past_nren <= !ren;
 | |
|         end
 | |
|     end
 | |
| `endif // VERIFIC
 | |
| 
 | |
| `endif // FORMAL
 | |
| 
 | |
| endmodule
 |