-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathasync_fifo.v
588 lines (492 loc) · 19.6 KB
/
async_fifo.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
`timescale 1ns / 1ps
/*
*
* An asynchronous FIFO that allows the buffering of data between
* two clock domains. The empty and full status flags are asserted
* immediately, but are removed pessimistically (i.e. 2 clock cycles late)
* due to the latency of using double FFs to synchronize the write
* and read pointers between the clock domains.
*
* Formally verified.
*
* NOTE:
* - AW must be greater than 3
* - Almost empty flag is asserted when FIFO level is within 4 from being empty
* - Almost full flag is asserted when FIFO level is within 4 from being full
*
*/
`default_nettype none
module async_fifo
#( parameter DW = 4,
parameter AW = 4 )
( input wire w_clk,
input wire w_rstn,
input wire w_en,
input wire [DW-1:0] i_dat,
output reg w_almost_full,
output reg w_full,
input wire r_clk,
input wire r_rstn,
input wire r_en,
output wire [DW-1:0] o_dat,
output reg r_almost_empty,
output reg r_empty
);
// Almost full parameter, 4 less than full
localparam AF = (1 << AW) - 4;
// Write domain logic
wire [AW - 1 : 0] waddr; // write address in memory
wire [AW : 0] wgraynext;
wire [AW : 0] wbinnext;
wire wfull_val;
wire walmost_full_val;
reg [AW: 0] wbin; // write pointer in binary to address memory
reg [AW: 0] wptr; // write pointer in gray for (almost) empty/(almost) full flags
reg [AW: 0] wq1_rptr;
reg [AW: 0] wq2_rptr;
initial { wq2_rptr, wq1_rptr } = 0;
initial { wbin, wptr } = 0;
initial w_full = 0;
// Read domain logic
wire [AW - 1 :0] raddr; // read address in memory
wire [AW :0] rgraynext;
wire [AW :0] rbinnext;
wire rempty_val;
wire ralmost_empty_val;
reg [AW :0] rbin; // read pointer in binary to address memory
reg [AW :0] rptr; // read pointer in gray for (almost) empty/(almost) full flags
reg [AW :0] rq1_wptr;
reg [AW :0] rq2_wptr;
initial { rq2_wptr, rq1_wptr } = 0;
initial { rbin, rptr } = 0;
initial r_empty = 1'b1;
// FIFO Memory Style: Synchronous Dual-Port RAM
reg [DW-1:0] mem [0:((1<<AW)-1)];
assign o_dat = mem[raddr];
always @(posedge w_clk)
if((w_en) && (!w_full))
mem[waddr] <= i_dat;
/** =======================================================
Read-Domain Logic
======================================================= **/
// Double FF to synchronize and recude metastability of
// write pointer in gray to read clock domain
always @(posedge r_clk or negedge r_rstn)
begin
if(!r_rstn) {rq2_wptr, rq1_wptr} <= 0;
else {rq2_wptr, rq1_wptr} <= {rq1_wptr, wptr};
end
// Register next binary and gray read pointers
always @(posedge r_clk or negedge r_rstn)
begin
if(!r_rstn) {rbin, rptr} <= 0;
else {rbin, rptr} <= {rbinnext, rgraynext};
end
// Read address and binary/gray read pointer logic
assign raddr = rbin[AW-1:0];
assign rbinnext = rbin + { {(AW){1'b0}}, (r_en && (!r_empty)) };
assign rgraynext = (rbinnext >> 1) ^ rbinnext;
// Logic for FIFO is empty
always @(posedge r_clk or negedge r_rstn)
begin
if(!r_rstn) r_empty <= 1'b1;
else r_empty <= rempty_val;
end
assign rempty_val = (rq2_wptr == rgraynext);
// Logic for FIFO is almost empty in 3 steps.
wire [AW :0] rq2_wptr_bin;
wire [AW :0] rbin_wbin_diff;
// 1st: Convert write pointer in gray code to binary
assign rq2_wptr_bin[AW] = rq2_wptr[AW];
generate
genvar k;
for(k = AW - 1; k >= 0; k = k - 1)
begin
assign rq2_wptr_bin[k] = rq2_wptr_bin[k+1] ^ rq2_wptr[k];
end
endgenerate
// 2nd: Find difference between binary read and write pointers.
/* Check whether the difference is within the threshold.
- There are two cases since a FIFO is a circular queue.
Case 1: read pointer > write pointer.
Logic for difference: synchronized wptr_bin - rptr_bin + POINTER DEPTH
Case 2: read pointer <= write pointer
Logic for difference: synchronized wptr_bin - rptr_bin
*/
assign rbin_wbin_diff = (rbinnext > rq2_wptr_bin) ? (rq2_wptr_bin - rbinnext
+ (1 << (AW+1)))
: (rq2_wptr_bin - rbinnext);
assign ralmost_empty_val = (rbin_wbin_diff <= 4);
always @(posedge r_clk or negedge r_rstn)
begin
if(!r_rstn) r_almost_empty <= 1'b1;
else r_almost_empty <= ralmost_empty_val;
end
/** =======================================================
Write-Domain Logic
======================================================= **/
// Double FF to synchronize read pointer to write clock domain
always @(posedge w_clk or negedge w_rstn)
begin
if(!w_rstn) {wq2_rptr, wq1_rptr} <= 0;
else {wq2_rptr, wq1_rptr} <= {wq1_rptr, rptr};
end
// Write Binary and Gray pointer
always @(posedge w_clk or negedge w_rstn)
begin
if(!w_rstn) {wbin, wptr} <= 0;
else {wbin, wptr} <= {wbinnext, wgraynext};
end
// Use binary to address FIFO memory. Use Gray for synchronizing and logic for almost empty/empty flags
assign waddr = wbin[AW-1:0];
assign wbinnext = wbin + { {(AW){1'b0}}, (w_en && (!w_full)) };
assign wgraynext = (wbinnext >> 1) ^ wbinnext;
// Logic for FIFO is full
always @(posedge w_clk or negedge w_rstn)
begin
if(!w_rstn) w_full <= 1'b0;
else w_full <= wfull_val;
end
assign wfull_val = (wgraynext == {~wq2_rptr[AW:AW-1],
wq2_rptr[AW-2:0]});
// Logic for FIFO is almost full (similar to logic for when FIFO is empty)
wire [AW :0] wq2_rptr_bin;
wire [AW :0] wbin_rbin_diff;
assign wq2_rptr_bin[AW] = wq2_rptr[AW];
generate
genvar j;
for(j = AW - 1; j >= 0; j = j - 1)
begin
assign wq2_rptr_bin[j] = wq2_rptr_bin[j+1] ^ wq2_rptr[j];
end
endgenerate
assign wbin_rbin_diff = (wbinnext > wq2_rptr_bin) ? (wbinnext - wq2_rptr_bin)
: (wbinnext - wq2_rptr_bin
+ (1 << (AW+1)));
assign walmost_full_val = (wbin_rbin_diff >= AF);
always @(posedge w_clk or negedge w_rstn)
begin
if(!w_rstn) w_almost_full <= 1'b0;
else w_almost_full <= walmost_full_val;
end
`ifdef FORMAL
// Setting up f_past_valid registers for three clock domains: read, write, global sim
reg f_past_valid_rd;
reg f_past_valid_wr;
reg f_past_valid_gbl;
initial
begin
f_past_valid_rd = 0;
f_past_valid_wr = 0;
f_past_valid_gbl = 0;
end
always @($global_clock)
f_past_valid_gbl <= 1'b1;
always @(posedge w_clk)
f_past_valid_wr <= 1'b1;
always @(posedge r_clk)
f_past_valid_rd <= 1'b1;
always @(*)
if(!f_past_valid_gbl)
assert((!f_past_valid_wr) && (!f_past_valid_rd));
`ifdef AFIFO
// Setup the two read and write clocks
localparam F_CLKBITS = 5;
wire [F_CLKBITS-1:0] f_wclk_step;
wire [F_CLKBITS-1:0] f_rclk_step;
assign f_wclk_step = $anyconst;
assign f_rclk_step = $anyconst;
always @(*)
begin
assume(f_wclk_step != 0);
assume(f_rclk_step != 0);
end
reg [F_CLKBITS-1:0] f_wclk_count;
reg [F_CLKBITS-1:0] f_rclk_count;
always @($global_clock)
begin
f_wclk_count <= f_wclk_count + f_wclk_step;
f_rclk_count <= f_rclk_count + f_rclk_step;
end
always @(*)
begin
assume(w_clk == (f_wclk_count[F_CLKBITS-1]));
assume(r_clk == (f_rclk_count[F_CLKBITS-1]));
end
`endif
// Assume reset input wires are asynch asserted, but only synch de-asserted
//initial assume (w_rstn == 1'b0);
//initial assume (r_rstn == 1'b0);
initial assume(w_rstn == r_rstn);
always @($global_clock)
assume($fell(w_rstn) == $fell(r_rstn));
always @($global_clock)
if(!$rose(w_clk))
assume(!$rose(w_rstn));
always @($global_clock)
if(!$rose(r_clk))
assume(!$rose(r_rstn));
always @($global_clock)
if(!w_rstn)
assert(rbin == 0);
// Assume that input wires can only change on clock edges
// Assert that the full/empty flags are stable,
always @($global_clock)
if(f_past_valid_gbl)
begin
if(!$rose(w_clk))
begin
assume($stable(w_en));
assume($stable(i_dat));
assert($stable(w_full) || (!w_rstn));
end
if(!$rose(r_clk))
begin
assume($stable(r_en));
assert((r_empty) || ($stable(o_dat)));
assert((!r_rstn) || ($stable(r_empty)));
end
end
// Assert cross clock values are in a known configuration
always @($global_clock)
if((!f_past_valid_wr) || (!w_rstn)) // Either at reset or at initial start of formal
begin
assume(w_en == 0);
assert(wptr == 0);
assert(wbin == 0);
assert(!w_full);
assert(wq1_rptr == 0);
assert(wq2_rptr == 0);
assert(rq1_wptr == 0);
assert(rq2_wptr == 0);
assert(rbin == 0);
assert(r_empty);
end
always @($global_clock)
if((!f_past_valid_rd) || (!r_rstn))
begin
assume(r_en == 0);
assert(rptr == 0);
assert(rbin == 0);
assert(wq1_rptr == 0);
assert(wq2_rptr == 0);
assert(rq1_wptr == 0);
assert(rq2_wptr == 0);
end
// Calculate the fill level of FIFO (needs to be between 0 and 2^(AW))
wire [AW:0] f_fill;
assign f_fill = (wbin - rbin);
initial assert(f_fill == 0);
always @($global_clock)
assert(f_fill <= { 1'b1, {(AW){1'b0}} });
// Assert that if FIFO is full, then w_full should be 1
always @($global_clock)
if(f_fill == { 1'b1, {(AW){1'b0}}})
assert(w_full);
// Assert that logic will detect if FIFO is about to be full
always @($global_clock)
if(f_fill == { 1'b0, {(AW){1'b1}}})
assert((wfull_val)||(!w_en)||(w_full));
// Assert that if FIFO is empty, then r_empty should be 1
always @($global_clock)
if(f_fill == 0)
assert(r_empty);
// Assert that logic will detect if FIFO is about to be empty
always @($global_clock)
if(f_fill == 1)
assert((rempty_val)||(!r_en)||(r_empty));
// Assert that the gray coded pointers are copies of the binary addresses
always @(*)
begin
assert(wptr == ((wbin >> 1) ^ wbin));
end
always @(*)
begin
assert(rptr == ((rbin >> 1) ^ rbin));
end
// Assert that the FIFO is full based off the read and write gray pointers are equal
always @(*)
assert( (rptr == { ~wptr[AW:AW-1], wptr[AW-2:0] })
== (f_fill == { 1'b1, {(AW){1'b0}} }));
// Assert that the pointers should only equal when FIFO is empty
always @(*)
assert((rptr == wptr) == (f_fill == 0));
// Examine fill/empty flags from read or write clock POV
reg [AW:0] f_w2r_rbin;
reg [AW:0] f_w1r_rbin;
reg [AW:0] f_r2w_wbin;
reg [AW:0] f_r1w_wbin;
wire [AW:0] f_w2r_fill;
wire [AW:0] f_r2w_fill;
/** Note: since formal has no metastability, no need to pass
* gray pointers but can use binary values across clock domains.
**/
initial { f_w2r_rbin, f_w1r_rbin } = 0;
initial { f_r2w_wbin, f_r1w_wbin } = 0;
always @(posedge w_clk or negedge w_rstn)
if(!w_rstn)
{ f_w2r_rbin, f_w1r_rbin } <= 0;
else
{ f_w2r_rbin, f_w1r_rbin } <= { f_w1r_rbin, rbin };
always @(posedge r_clk or negedge r_rstn)
if(!r_rstn)
{ f_r2w_wbin, f_r1w_wbin } <= 0;
else
{ f_r2w_wbin, f_r1w_wbin } <= { f_r1w_wbin, wbin };
// Calculate fill level from perspective of each clock domain
// 1st: Assert each converted binary pointer is equal to its gray counterpart
always @(*)
assert(rq1_wptr == ((f_r1w_wbin >> 1)^f_r1w_wbin));
always @(*)
assert(rq2_wptr == ((f_r2w_wbin >> 1)^f_r2w_wbin));
always @(*)
assert(wq1_rptr == ((f_w1r_rbin >> 1)^f_w1r_rbin));
always @(*)
assert(wq2_rptr == ((f_w2r_rbin >> 1)^f_w2r_rbin));
assign f_w2r_fill = wbin - f_w2r_rbin;
assign f_r2w_fill = f_r2w_wbin - rbin;
// 2nd: Assert that fill is always less than or equal to full
always @(*)
assert(f_w2r_fill <= { 1'b1, {(AW){1'b0} }});
always @(*)
assert(f_r2w_fill <= { 1'b1, {(AW){1'b0} }});
// From write clock POV, FIFO is full if gray pointers are =,
// except for the MSB. Assert this property of the FIFO.
always @(*)
if(wptr == { ~wq2_rptr[AW:AW-1], wq2_rptr[AW-2:0] })
assert(w_full);
// For the read clock POV
always @(*)
if(rptr == rq2_wptr)
assert(r_empty);
// Assert that at most one bit of gray coded pointer values
// will ever change when crossing clock domains
genvar i;
generate
for(i = 0; i <= AW; i=i+1)
begin: CHECK_ONEHOT_WGRAY
always @(*)
assert((wptr[i] == wgraynext[i])
||(wptr ^ wgraynext ^ (1<<i) == 0));
always @(*)
assert((rq2_wptr[i] == rq1_wptr[i])
||(rq2_wptr ^ rq1_wptr ^ (1<<i) == 0));
end
endgenerate
genvar k;
generate
for(k = 0; k <= AW; k=k+1)
begin: CHECK_ONEHOT_RGRAY
always @(*)
assert((rptr[k] == rgraynext[k])
|| (rptr ^ rgraynext ^ (1<<k) == 0));
always @(*)
assert((wq2_rptr[k] == wq1_rptr[k])
||(wq2_rptr ^ wq1_rptr ^ (1<<k) == 0));
end
endgenerate
/** FIFO contract: If two subsequent values are written, then
those values must be read out later in the
same order.
**/
`ifdef AFIFO
(* anyconst *) wire [AW:0] f_const_addr;
wire [AW:0] f_const_next_addr;
assign f_const_next_addr = f_const_addr + 1'b1;
(* anyconst *) reg [DW-1:0] f_const_first_data;
(* anyconst *) reg [DW-1:0] f_const_next_data;
reg f_addr_valid;
reg f_addr_next_valid;
always @(*)
begin
f_addr_valid = 1'b0;
if((wbin > rbin) && (wbin > f_const_addr) && (rbin <= f_const_addr))
// rbin <= addr < wbin
f_addr_valid = 1'b1;
else if((wbin < rbin) && (wbin > f_const_addr))
// addr < wbin < rbin
f_addr_valid = 1'b1;
else if((wbin < rbin) && (rbin <= f_const_addr))
// wbin < rbin < addr
f_addr_valid = 1'b1;
end
always @(*)
begin
f_addr_next_valid = 1'b0;
if((wbin > rbin)&&(wbin > f_const_next_addr)&&(rbin <= f_const_next_addr))
// rbin <= addr < wbin
f_addr_next_valid = 1'b1;
else if ((wbin < rbin)&&(f_const_next_addr < wbin))
// addr < wbin < rbin
f_addr_next_valid = 1'b1;
else if ((wbin < rbin)&&(rbin <= f_const_next_addr))
// wbin < rbin < addr
f_addr_next_valid = 1'b1;
end
reg f_first_in_fifo;
reg f_second_in_fifo;
reg f_both_in_fifo;
always @(*)
f_first_in_fifo = (f_addr_valid)
&& (mem[f_const_addr[AW-1:0]] == f_const_first_data);
always @(*)
f_second_in_fifo = (f_addr_next_valid)
&& (mem[f_const_next_addr[AW-1:0]]==f_const_next_data);
always @(*)
f_both_in_fifo = (f_first_in_fifo) && (f_second_in_fifo);
reg f_wait_for_first_read;
reg f_read_first;
reg f_read_second;
reg f_wait_for_second_read;
always @(*)
f_wait_for_first_read = (f_both_in_fifo)
&& ((!r_en)||(f_const_addr != rbin)||(r_empty));
always @(*)
f_read_first = (r_en) && (o_dat == f_const_first_data)&&(!r_empty)
&& (rbin == f_const_addr)&&(f_both_in_fifo);
always @(*)
f_wait_for_second_read = (f_second_in_fifo)
&& ((!r_en) || (r_empty))
&& (f_const_next_addr == rbin);
always @(*)
f_read_second = (r_en) && (o_dat == f_const_next_data) && (!r_empty)
&& (rbin == f_const_next_addr)
&& (f_second_in_fifo);
always @($global_clock)
if((f_past_valid_gbl) && (w_rstn))
begin
if((!$past(f_read_first))&&($past(f_both_in_fifo)))
assert((f_wait_for_first_read) || (($rose(r_clk))&&(f_read_first)));
if($past(f_read_first))
assert( ((!$rose(r_clk))&&(f_read_first))
|| ($rose(r_clk)&&((f_read_second)
|| (f_wait_for_second_read))));
if($past(f_wait_for_second_read))
assert((f_wait_for_second_read)
|| (($rose(r_clk)) && (f_read_second)));
end
`endif
always @(posedge w_clk)
cover(w_rstn);
always @(posedge r_clk)
cover(r_rstn);
always @($global_clock)
if(f_past_valid_gbl)
cover((r_empty)&&(!$past(r_empty)));
always @(*)
if(f_past_valid_gbl)
cover(w_full);
always @(posedge w_clk)
if(f_past_valid_wr)
cover($past(w_full)&&($past(w_en))&&(w_full));
always @(posedge w_clk)
if (f_past_valid_wr)
cover($past(w_full)&&(!w_full));
always @(posedge w_clk)
cover((w_full)&&(w_en));
always @(posedge w_clk)
cover(w_en);
always @(posedge r_clk)
cover((r_empty) && (r_en));
`endif
endmodule