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 */