Attention

This documentation is a work in progress. Expect to see errors and unfinished things.

shortfifo Source File

  1// Short (2-32 long) FIFO meant to be efficiently implemented with
  2// Xilinx SRL16E or similar
  3// Except for the unified clock and the count output port,
  4// this is pin-compatible with ordinary fifo.v
  5// max. elements: 2**aw
  6
  7`timescale 1ns / 1ns
  8
  9module shortfifo #(
 10     parameter dw=8,
 11     parameter aw=3
 12) (
 13     // require single clock domain
 14     input clk,
 15     // input port
 16     input [dw-1:0] din,
 17     input we,
 18     // output port
 19     output [dw-1:0] dout,
 20     input re,
 21     // status
 22     output full,
 23     output empty,
 24     output last,
 25     output [aw:0] count  // -1: empty, 0: single element, 2**aw - 1: full
 26);
 27localparam len = 1 << aw;
 28
 29// Need 1 extra bit to distinguish between full and empty
 30reg [aw:0] raddr = ~0;
 31
 32wire re_ = re && !empty;
 33wire we_ = we && (!full || re);
 34
 35genvar ix;
 36generate for (ix=0; ix<dw; ix=ix+1) begin: bit_slice
 37     abstract_dsr #(.aw(aw)) srl(.clk(clk), .ce(we_), .addr(raddr[aw-1:0]),
 38             .din(din[ix]), .dout(dout[ix]) );
 39end endgenerate
 40
 41always @(posedge clk) raddr <= raddr + we_ - re_;
 42assign full = raddr == (len - 1);
 43assign empty = &raddr;
 44assign last = ~(|raddr);
 45assign count = raddr;
 46
 47// ---------------------------
 48
 49`ifdef FORMAL
 50// Formal properties based on:
 51// https://zipcpu.com/tutorial/lsn-10-fifo.pdf
 52
 53reg f_past_valid = 0;
 54
 55// 2 magic addresses we will follow up
 56(* anyconst *) reg [aw:0] f_first_addr;
 57wire [aw:0] f_second_addr = f_first_addr + 1;
 58
 59// with 2 corresponding magic values
 60(* anyconst *) reg [dw - 1: 0] f_first_data, f_second_data;
 61
 62// circular addr. ptrs. are used to track the queue position of the 2 values
 63reg [1:0] f_state = 0;
 64reg [aw:0] f_wr_addr = 0;
 65reg [aw:0] f_r_addr = 0;
 66
 67// How many elements are in the FIFO right now
 68wire [aw:0] f_fill = f_wr_addr - f_r_addr;
 69
 70// If these are < f_fill, the addresses are valid (= standing in queue)
 71wire f_first_valid = (f_first_addr - f_r_addr) < f_fill;
 72wire f_second_valid = (f_second_addr - f_r_addr) < f_fill;
 73
 74always @(posedge clk) begin
 75     f_past_valid <= 1;
 76
 77     if (we_)
 78             f_wr_addr <= f_wr_addr + 1;
 79
 80     if (re_)
 81             f_r_addr <= f_r_addr + 1;
 82
 83     // FIFO sequence
 84     // Follow up the FIFO state transitions as 2 values enter and leave it
 85     // Need to do it for 2 values to verify the correct order
 86     case (f_state)
 87             0: begin  // IDLE state
 88                     // write first magic value into FIFO
 89                     if (we_ && (f_wr_addr == f_first_addr) && (din == f_first_data))
 90                             f_state <= 1;
 91             end
 92
 93             1: begin  // 1 value is in
 94                     if (re_ && (f_r_addr == f_first_addr))
 95                             f_state <= 0;  // first value was read prematurely, restart
 96                     else if(we_)
 97                             if (din == f_second_data)
 98                                     f_state <= 2;  // write second value
 99                             else
100                                     f_state <= 0;  // wrote wrong second value, restart
101
102                     // f_first_addr must be in the valid range of the FIFO
103                     assert(f_first_valid);
104                     assert(f_wr_addr == f_second_addr);
105             end
106
107             2: begin  // 2 values are in, read out first value
108                     if (re_ && (f_r_addr == f_first_addr))
109                             f_state <= 3;
110                     else
111                             f_state <= 0;
112
113                     assert(f_first_valid);
114                     assert(f_second_valid);
115
116                     // TODO how to force data into memory when running the proof?
117                     // sby just assumes a wrong memory initial value and fails on dout
118                     if (f_r_addr == f_first_addr)
119                             assert(dout == f_first_data);
120             end
121
122             3: begin  // read out second value
123                     f_state <= 0;
124
125                     assert(f_second_valid);
126                     assert(dout == f_second_data);
127             end
128     endcase
129
130     // Cannot go from full to empty and vice versa
131     if (f_past_valid && $past(full))
132             assert(!empty);
133     if (f_past_valid && $past(empty))
134             assert(!full);
135
136     // Make sure the right things happen when read and write at the same time
137     if (f_past_valid && $past(we) && $past(re) && $past(f_fill > 0))
138             assert($stable(f_fill));
139
140     // Show 2 values entering and exiting the FIFO:
141     // cover(f_past_valid && $past(f_state) == 3 && f_state == 0);
142end
143
144// Fill up the FIFO and empty it again
145reg f_was_full = 0;
146reg f_both = 0; // show what happens when both are high
147always @(posedge clk) begin
148     // assume(din == $past(din) + 8'h1);
149     if (full)
150             f_was_full <= 1;
151     if (we && re && !empty)
152             f_both <= 1;
153     cover(f_was_full && empty && f_both);
154end
155
156always @(*) begin
157     assert(f_fill >= 0);
158     assert(raddr == f_fill - (aw'd1));
159
160     assert(empty == (f_fill == 0));
161     assert(last == (f_fill == 1));
162     assert(full == (f_fill == len));
163
164     // Can't have more items than the memory holds
165     assert(f_fill <= len);
166
167     // Can't be full and empty at the same time
168     assert(!(full && empty));
169end
170`endif
171endmodule
172
173// should infer as a single SRL16E, SRL32E, ...
174// See "Dynamic Shift Registers Verilog Coding Example" in UG687
175/* verilator lint_save */
176/* verilator lint_off DECLFILENAME */
177module abstract_dsr #(
178     parameter aw=4
179) (
180     input clk,
181     input ce,
182     input din,
183     input [aw-1:0] addr,
184     output dout
185);
186localparam len = 1 << aw;
187reg [len-1:0] sr=0;
188always @(posedge clk) if (ce) sr <= {sr[len-2:0],din};
189assign dout = sr[addr];
190endmodule
191/* verilator lint_restore */