809 lines
19 KiB
Verilog
809 lines
19 KiB
Verilog
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Filename: afifo.v
|
|
// {{{
|
|
// Project: 10Gb Ethernet switch
|
|
//
|
|
// Purpose: A basic asynchronous FIFO.
|
|
//
|
|
// Creator: Dan Gisselquist, Ph.D.
|
|
// Gisselquist Technology, LLC
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// }}}
|
|
// Copyright (C) 2023, Gisselquist Technology, LLC
|
|
// {{{
|
|
// This file is part of the ETH10G project.
|
|
//
|
|
// The ETH10G project contains free software and gateware, licensed under the
|
|
// Apache License, Version 2.0 (the "License"). You may not use this project,
|
|
// or this file, except in compliance with the License. You may obtain a copy
|
|
// of the License at
|
|
// }}}
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
// {{{
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
`default_nettype none
|
|
// }}}
|
|
module afifo #(
|
|
// {{{
|
|
// LGFIFO is the log based-two of the number of entries
|
|
// in the FIFO, log_2(fifo size)
|
|
parameter LGFIFO = 3,
|
|
//
|
|
// WIDTH is the number of data bits in each entry
|
|
parameter WIDTH = 16,
|
|
//
|
|
// NFF is the number of flip flops used to cross clock domains.
|
|
// 2 is a minimum. Some applications appreciate the better
|
|
parameter NFF = 2,
|
|
//
|
|
// This core can either write on the positive edge of the clock
|
|
// or the negative edge. Set WRITE_ON_POSEDGE (the default)
|
|
// to write on the positive edge of the clock.
|
|
parameter [0:0] WRITE_ON_POSEDGE = 1'b1,
|
|
//
|
|
// Many logic elements can read from memory asynchronously.
|
|
// This burdens any following logic. By setting
|
|
// OPT_REGISTER_READS, we force all reads to be synchronous and
|
|
// not burdened by any logic. You can spare a clock of latency
|
|
// by clearing this register.
|
|
parameter [0:0] OPT_REGISTER_READS = 1'b1
|
|
`ifdef FORMAL
|
|
// F_OPT_DATA_STB
|
|
// {{{
|
|
// In the formal proof, F_OPT_DATA_STB includes a series of
|
|
// assumptions associated with a data strobe I/O pin--things
|
|
// like a discontinuous clock--just to make sure the core still
|
|
// works in those circumstances
|
|
, parameter [0:0] F_OPT_DATA_STB = 1'b1
|
|
// }}}
|
|
`endif
|
|
// }}}
|
|
) (
|
|
// {{{
|
|
//
|
|
// The (incoming) write data interface
|
|
input wire i_wclk,
|
|
// Verilator lint_off SYNCASYNCNET
|
|
input wire i_wr_reset_n,
|
|
// Verilator lint_on SYNCASYNCNET
|
|
input wire i_wr,
|
|
input wire [WIDTH-1:0] i_wr_data,
|
|
output reg o_wr_full,
|
|
//
|
|
// The (incoming) write data interface
|
|
input wire i_rclk,
|
|
// Verilator lint_off SYNCASYNCNET
|
|
input wire i_rd_reset_n,
|
|
// Verilator lint_on SYNCASYNCNET
|
|
input wire i_rd,
|
|
output reg [WIDTH-1:0] o_rd_data,
|
|
output reg o_rd_empty
|
|
`ifdef FORMAL
|
|
, output reg [LGFIFO:0] f_fill
|
|
`endif
|
|
// }}}
|
|
);
|
|
|
|
// Register/net declarations
|
|
// {{{
|
|
// MSB = most significant bit of the FIFO address vector. It's
|
|
// just short-hand for LGFIFO, and won't work any other way.
|
|
localparam MSB = LGFIFO;
|
|
//
|
|
reg [WIDTH-1:0] mem [(1<<LGFIFO)-1:0];
|
|
reg [LGFIFO:0] rd_addr, wr_addr,
|
|
rd_wgray, wr_rgray;
|
|
wire [LGFIFO:0] next_rd_addr, next_wr_addr;
|
|
reg [LGFIFO:0] rgray, wgray;
|
|
(* ASYNC_REG = "TRUE" *) reg [(LGFIFO+1)*(NFF-1)-1:0]
|
|
rgray_cross, wgray_cross;
|
|
wire wclk;
|
|
reg [WIDTH-1:0] lcl_rd_data;
|
|
reg lcl_read, lcl_rd_empty;
|
|
// }}}
|
|
|
|
// wclk - Write clock generation
|
|
// {{{
|
|
generate if (WRITE_ON_POSEDGE)
|
|
begin : GEN_POSEDGE_WRITES
|
|
|
|
assign wclk = i_wclk;
|
|
|
|
end else begin : GEN_NEGEDGE_WRITES
|
|
|
|
assign wclk = !i_wclk;
|
|
|
|
end endgenerate
|
|
// }}}
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Write to and read from memory
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
// wr_addr, wgray
|
|
// {{{
|
|
assign next_wr_addr = wr_addr + 1;
|
|
always @(posedge wclk or negedge i_wr_reset_n)
|
|
if (!i_wr_reset_n)
|
|
begin
|
|
wr_addr <= 0;
|
|
wgray <= 0;
|
|
end else if (i_wr && !o_wr_full)
|
|
begin
|
|
wr_addr <= next_wr_addr;
|
|
wgray <= next_wr_addr ^ (next_wr_addr >> 1);
|
|
end
|
|
// }}}
|
|
|
|
// Write to memory
|
|
// {{{
|
|
always @(posedge wclk)
|
|
if (i_wr && !o_wr_full)
|
|
mem[wr_addr[LGFIFO-1:0]] <= i_wr_data;
|
|
// }}}
|
|
|
|
// rd_addr, rgray
|
|
// {{{
|
|
assign next_rd_addr = rd_addr + 1;
|
|
always @(posedge i_rclk or negedge i_rd_reset_n)
|
|
if (!i_rd_reset_n)
|
|
begin
|
|
rd_addr <= 0;
|
|
rgray <= 0;
|
|
end else if (lcl_read && !lcl_rd_empty)
|
|
begin
|
|
rd_addr <= next_rd_addr;
|
|
rgray <= next_rd_addr ^ (next_rd_addr >> 1);
|
|
end
|
|
// }}}
|
|
|
|
// Read from memory
|
|
// {{{
|
|
always @(*)
|
|
lcl_rd_data = mem[rd_addr[LGFIFO-1:0]];
|
|
// }}}
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cross clock domains
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
// read pointer -> wr_rgray
|
|
// {{{
|
|
always @(posedge wclk or negedge i_wr_reset_n)
|
|
if (!i_wr_reset_n)
|
|
{ wr_rgray, rgray_cross } <= 0;
|
|
else
|
|
{ wr_rgray, rgray_cross } <= { rgray_cross, rgray };
|
|
// }}}
|
|
|
|
// write pointer -> rd_wgray
|
|
// {{{
|
|
always @(posedge i_rclk or negedge i_rd_reset_n)
|
|
if (!i_rd_reset_n)
|
|
{ rd_wgray, wgray_cross } <= 0;
|
|
else
|
|
{ rd_wgray, wgray_cross } <= { wgray_cross, wgray };
|
|
// }}}
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Flag generation
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
always @(*)
|
|
o_wr_full = (wr_rgray == { ~wgray[MSB:MSB-1], wgray[MSB-2:0] });
|
|
|
|
always @(*)
|
|
lcl_rd_empty = (rd_wgray == rgray);
|
|
|
|
// o_rd_empty, o_rd_data
|
|
// {{{
|
|
generate if (OPT_REGISTER_READS)
|
|
begin : GEN_REGISTERED_READ
|
|
// {{{
|
|
always @(*)
|
|
lcl_read = (o_rd_empty || i_rd);
|
|
|
|
always @(posedge i_rclk or negedge i_rd_reset_n)
|
|
if (!i_rd_reset_n)
|
|
o_rd_empty <= 1'b1;
|
|
else if (lcl_read)
|
|
o_rd_empty <= lcl_rd_empty;
|
|
|
|
always @(posedge i_rclk)
|
|
if (lcl_read)
|
|
o_rd_data <= lcl_rd_data;
|
|
// }}}
|
|
end else begin : GEN_COMBINATORIAL_FLAGS
|
|
// {{{
|
|
always @(*)
|
|
lcl_read = i_rd;
|
|
|
|
always @(*)
|
|
o_rd_empty = lcl_rd_empty;
|
|
|
|
always @(*)
|
|
o_rd_data = lcl_rd_data;
|
|
// }}}
|
|
end endgenerate
|
|
// }}}
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Formal properties
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
`ifdef FORMAL
|
|
// Start out with some register/net/macro declarations, f_past_valid,etc
|
|
// {{{
|
|
`ifdef AFIFO
|
|
`define ASSERT assert
|
|
`define ASSUME assume
|
|
`else
|
|
`define ASSERT assert
|
|
`define ASSUME assert
|
|
`endif
|
|
|
|
(* gclk *) reg gbl_clk;
|
|
reg f_past_valid_gbl, f_past_valid_rd,
|
|
f_rd_in_reset, f_wr_in_reset;
|
|
reg [WIDTH-1:0] past_rd_data, past_wr_data;
|
|
reg past_wr_reset_n, past_rd_reset_n,
|
|
past_rd_empty, past_wclk, past_rclk, past_rd;
|
|
reg [(LGFIFO+1)*(NFF-1)-1:0] f_wcross, f_rcross;
|
|
reg [LGFIFO:0] f_rd_waddr, f_wr_raddr;
|
|
reg [LGFIFO:0] f_rdcross_fill [NFF-1:0];
|
|
reg [LGFIFO:0] f_wrcross_fill [NFF-1:0];
|
|
|
|
|
|
initial f_past_valid_gbl = 1'b0;
|
|
always @(posedge gbl_clk)
|
|
f_past_valid_gbl <= 1'b1;
|
|
|
|
initial f_past_valid_rd = 1'b0;
|
|
always @(posedge i_rclk)
|
|
f_past_valid_rd <= 1'b1;
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Reset checks
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
initial f_wr_in_reset = 1'b1;
|
|
always @(posedge wclk or negedge i_wr_reset_n)
|
|
if (!i_wr_reset_n)
|
|
f_wr_in_reset <= 1'b1;
|
|
else
|
|
f_wr_in_reset <= 1'b0;
|
|
|
|
initial f_rd_in_reset = 1'b1;
|
|
always @(posedge i_rclk or negedge i_rd_reset_n)
|
|
if (!i_rd_reset_n)
|
|
f_rd_in_reset <= 1'b1;
|
|
else
|
|
f_rd_in_reset <= 1'b0;
|
|
|
|
//
|
|
// Resets are ...
|
|
// 1. Asserted always initially, and ...
|
|
always @(*)
|
|
if (!f_past_valid_gbl)
|
|
begin
|
|
`ASSUME(!i_wr_reset_n);
|
|
`ASSUME(!i_rd_reset_n);
|
|
end
|
|
|
|
// 2. They only ever become active together
|
|
always @(*)
|
|
if (past_wr_reset_n && !i_wr_reset_n)
|
|
`ASSUME(!i_rd_reset_n);
|
|
|
|
always @(*)
|
|
if (past_rd_reset_n && !i_rd_reset_n)
|
|
`ASSUME(!i_wr_reset_n);
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Synchronous signal assumptions
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
always @(posedge gbl_clk)
|
|
begin
|
|
past_wr_reset_n <= i_wr_reset_n;
|
|
past_rd_reset_n <= i_rd_reset_n;
|
|
|
|
past_wclk <= wclk;
|
|
past_rclk <= i_rclk;
|
|
|
|
past_rd <= i_rd;
|
|
past_rd_data <= lcl_rd_data;
|
|
past_wr_data <= i_wr_data;
|
|
|
|
past_rd_empty<= lcl_rd_empty;
|
|
end
|
|
|
|
//
|
|
// Read side may be assumed to be synchronous
|
|
always @(*)
|
|
if (f_past_valid_gbl && i_rd_reset_n && (past_rclk || !i_rclk))
|
|
// i.e. if (!$rose(i_rclk))
|
|
`ASSUME(i_rd == past_rd);
|
|
|
|
always @(*)
|
|
if (f_past_valid_rd && !f_rd_in_reset && !lcl_rd_empty
|
|
&&(past_rclk || !i_rclk))
|
|
begin
|
|
`ASSERT(lcl_rd_data == past_rd_data);
|
|
`ASSERT(lcl_rd_empty == past_rd_empty);
|
|
end
|
|
|
|
|
|
generate if (F_OPT_DATA_STB)
|
|
begin
|
|
|
|
always @(posedge gbl_clk)
|
|
`ASSUME(!o_wr_full);
|
|
|
|
always @(posedge gbl_clk)
|
|
if (!i_wr_reset_n)
|
|
`ASSUME(!i_wclk);
|
|
|
|
always @(posedge gbl_clk)
|
|
`ASSUME(i_wr == i_wr_reset_n);
|
|
|
|
always @(posedge gbl_clk)
|
|
if ($changed(i_wr_reset_n))
|
|
`ASSUME($stable(wclk));
|
|
|
|
end endgenerate
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Fill checks
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
always @(*)
|
|
f_fill = wr_addr - rd_addr;
|
|
|
|
always @(*)
|
|
if (!f_wr_in_reset)
|
|
`ASSERT(f_fill <= { 1'b1, {(MSB){1'b0}} });
|
|
|
|
always @(*)
|
|
if (wr_addr == rd_addr)
|
|
`ASSERT(lcl_rd_empty);
|
|
|
|
always @(*)
|
|
if ((!f_wr_in_reset && !f_rd_in_reset)
|
|
&& wr_addr == { ~rd_addr[MSB], rd_addr[MSB-1:0] })
|
|
`ASSERT(o_wr_full);
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Induction checks
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
// f_wr_in_reset -- write logic is in its reset state
|
|
// {{{
|
|
always @(*)
|
|
if (f_wr_in_reset)
|
|
begin
|
|
`ASSERT(wr_addr == 0);
|
|
`ASSERT(wgray_cross == 0);
|
|
|
|
`ASSERT(rd_addr == 0);
|
|
`ASSERT(rgray_cross == 0);
|
|
`ASSERT(rd_wgray == 0);
|
|
|
|
`ASSERT(lcl_rd_empty);
|
|
`ASSERT(!o_wr_full);
|
|
end
|
|
// }}}
|
|
|
|
// f_rd_in_reset -- read logic is in its reset state
|
|
// {{{
|
|
always @(*)
|
|
if (f_rd_in_reset)
|
|
begin
|
|
`ASSERT(rd_addr == 0);
|
|
`ASSERT(rgray_cross == 0);
|
|
`ASSERT(rd_wgray == 0);
|
|
|
|
`ASSERT(lcl_rd_empty);
|
|
end
|
|
// }}}
|
|
|
|
// f_wr_raddr -- a read address to match the gray values
|
|
// {{{
|
|
always @(posedge wclk or negedge i_wr_reset_n)
|
|
if (!i_wr_reset_n)
|
|
{ f_wr_raddr, f_rcross } <= 0;
|
|
else
|
|
{ f_wr_raddr, f_rcross } <= { f_rcross, rd_addr };
|
|
// }}}
|
|
|
|
// f_rd_waddr -- a write address to match the gray values
|
|
// {{{
|
|
always @(posedge i_rclk or negedge i_rd_reset_n)
|
|
if (!i_rd_reset_n)
|
|
{ f_rd_waddr, f_wcross } <= 0;
|
|
else
|
|
{ f_rd_waddr, f_wcross } <= { f_wcross, wr_addr };
|
|
// }}}
|
|
|
|
integer k;
|
|
|
|
// wgray check
|
|
// {{{
|
|
always @(*)
|
|
`ASSERT((wr_addr ^ (wr_addr >> 1)) == wgray);
|
|
// }}}
|
|
|
|
// wgray_cross check
|
|
// {{{
|
|
always @(*)
|
|
for(k=0; k<NFF-1; k=k+1)
|
|
`ASSERT((f_wcross[k*(LGFIFO+1) +: LGFIFO+1]
|
|
^ (f_wcross[k*(LGFIFO+1)+: LGFIFO+1]>>1))
|
|
== wgray_cross[k*(LGFIFO+1) +: LGFIFO+1]);
|
|
// }}}
|
|
|
|
// rgray check
|
|
// {{{
|
|
always @(*)
|
|
`ASSERT((rd_addr ^ (rd_addr >> 1)) == rgray);
|
|
// }}}
|
|
|
|
// rgray_cross check
|
|
// {{{
|
|
always @(*)
|
|
for(k=0; k<NFF-1; k=k+1)
|
|
`ASSERT((f_rcross[k*(LGFIFO+1) +: LGFIFO+1]
|
|
^ (f_rcross[k*(LGFIFO+1) +: LGFIFO+1]>>1))
|
|
== rgray_cross[k*(LGFIFO+1) +: LGFIFO+1]);
|
|
// }}}
|
|
|
|
// wr_rgray
|
|
// {{{
|
|
always @(*)
|
|
`ASSERT((f_wr_raddr ^ (f_wr_raddr >> 1)) == wr_rgray);
|
|
// }}}
|
|
|
|
// rd_wgray
|
|
// {{{
|
|
always @(*)
|
|
`ASSERT((f_rd_waddr ^ (f_rd_waddr >> 1)) == rd_wgray);
|
|
// }}}
|
|
|
|
// f_rdcross_fill
|
|
// {{{
|
|
always @(*)
|
|
for(k=0; k<NFF-1; k=k+1)
|
|
f_rdcross_fill[k] = f_wcross[k*(LGFIFO+1) +: LGFIFO+1]
|
|
- rd_addr;
|
|
|
|
always @(*)
|
|
f_rdcross_fill[NFF-1] = f_rd_waddr - rd_addr;
|
|
|
|
always @(*)
|
|
for(k=0; k<NFF; k=k+1)
|
|
`ASSERT(f_rdcross_fill[k] <= { 1'b1, {(LGFIFO){1'b0}} });
|
|
|
|
always @(*)
|
|
for(k=1; k<NFF; k=k+1)
|
|
`ASSERT(f_rdcross_fill[k] <= f_rdcross_fill[k-1]);
|
|
always @(*)
|
|
`ASSERT(f_rdcross_fill[0] <= f_fill);
|
|
// }}}
|
|
|
|
// f_wrcross_fill
|
|
// {{{
|
|
always @(*)
|
|
for(k=0; k<NFF-1; k=k+1)
|
|
f_wrcross_fill[k] = wr_addr -
|
|
f_rcross[k*(LGFIFO+1) +: LGFIFO+1];
|
|
|
|
always @(*)
|
|
f_wrcross_fill[NFF-1] = wr_addr - f_wr_raddr;
|
|
|
|
always @(*)
|
|
for(k=0; k<NFF; k=k+1)
|
|
`ASSERT(f_wrcross_fill[k] <= { 1'b1, {(LGFIFO){1'b0}} });
|
|
|
|
always @(*)
|
|
for(k=1; k<NFF; k=k+1)
|
|
`ASSERT(f_wrcross_fill[k] >= f_wrcross_fill[k-1]);
|
|
always @(*)
|
|
`ASSERT(f_wrcross_fill[0] >= f_fill);
|
|
// }}}
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Clock generation
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
// Here's the challenge: if we use $past with any of our clocks, such
|
|
// as to determine stability or any such, the proof takes forever, and
|
|
// we need to guarantee a minimum number of transitions within the
|
|
// depth of the proof. If, on the other hand, we build our own $past
|
|
// primitives--we can then finish much faster and be successful on
|
|
// any depth of proof.
|
|
|
|
// pre_xclk is what the clock will become on the next global clock edge.
|
|
// By using it here, we can check things @(*) instead of
|
|
// @(posedge gbl_clk). Further, we can check $rose(pre_xclk) (or $fell)
|
|
// and essentially check things @(*) but while using @(global_clk).
|
|
// In other words, we can transition on @(posedge gbl_clk), but stay
|
|
// in sync with the data--rather than being behind by a clock.
|
|
// now_xclk is what the clock is currently.
|
|
//
|
|
(* anyseq *) reg pre_wclk, pre_rclk;
|
|
reg now_wclk, now_rclk;
|
|
always @(posedge gbl_clk)
|
|
begin
|
|
now_wclk <= pre_wclk;
|
|
now_rclk <= pre_rclk;
|
|
end
|
|
|
|
always @(*)
|
|
begin
|
|
assume(i_wclk == now_wclk);
|
|
assume(i_rclk == now_rclk);
|
|
end
|
|
|
|
always @(posedge gbl_clk)
|
|
assume(i_rclk == $past(pre_rclk));
|
|
|
|
// Assume both clocks start idle
|
|
// {{{
|
|
always @(*)
|
|
if (!f_past_valid_gbl)
|
|
begin
|
|
assume(!pre_wclk && !wclk);
|
|
assume(!pre_rclk && !i_rclk);
|
|
end
|
|
// }}}
|
|
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Formal contract check --- the twin write test
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
// Tracking register declarations
|
|
// {{{
|
|
reg [WIDTH-1:0] f_first, f_next;
|
|
(* anyconst *) reg [LGFIFO:0] f_addr;
|
|
reg [LGFIFO:0] f_next_addr;
|
|
reg f_first_in_fifo, f_next_in_fifo;
|
|
reg [LGFIFO:0] f_to_first, f_to_next;
|
|
reg [1:0] f_state;
|
|
|
|
always @(*)
|
|
f_next_addr = f_addr + 1;
|
|
// }}}
|
|
|
|
// distance_to*, *_in_fifo
|
|
// {{{
|
|
always @(*)
|
|
begin
|
|
f_to_first = f_addr - rd_addr;
|
|
|
|
f_first_in_fifo = 1'b1;
|
|
if ((f_to_first >= f_fill)||(f_fill == 0))
|
|
f_first_in_fifo = 1'b0;
|
|
|
|
if (mem[f_addr] != f_first)
|
|
f_first_in_fifo = 1'b0;
|
|
|
|
//
|
|
// Check the second item
|
|
//
|
|
|
|
f_to_next = f_next_addr - rd_addr;
|
|
f_next_in_fifo = 1'b1;
|
|
if ((f_to_next >= f_fill)||(f_fill == 0))
|
|
f_next_in_fifo = 1'b0;
|
|
|
|
if (mem[f_next_addr] != f_next)
|
|
f_next_in_fifo = 1'b0;
|
|
end
|
|
// }}}
|
|
|
|
// f_state -- generate our state variable
|
|
// {{{
|
|
initial f_state = 0;
|
|
always @(posedge gbl_clk)
|
|
if (!i_wr_reset_n)
|
|
f_state <= 0;
|
|
else case(f_state)
|
|
2'b00: if (($rose(pre_wclk))&& i_wr && !o_wr_full &&(wr_addr == f_addr))
|
|
begin
|
|
f_state <= 2'b01;
|
|
f_first <= i_wr_data;
|
|
end
|
|
2'b01: if ($rose(pre_rclk)&& lcl_read && rd_addr == f_addr)
|
|
f_state <= 2'b00;
|
|
else if ($rose(pre_wclk) && i_wr && !o_wr_full )
|
|
begin
|
|
f_state <= 2'b10;
|
|
f_next <= i_wr_data;
|
|
end
|
|
2'b10: if ($rose(pre_rclk) && lcl_read && !lcl_rd_empty && rd_addr == f_addr)
|
|
f_state <= 2'b11;
|
|
2'b11: if ($rose(pre_rclk) && lcl_read && !lcl_rd_empty && rd_addr == f_next_addr)
|
|
f_state <= 2'b00;
|
|
endcase
|
|
// }}}
|
|
|
|
// f_state invariants
|
|
// {{{
|
|
always @(*)
|
|
if (i_wr_reset_n) case(f_state)
|
|
2'b00: begin end
|
|
2'b01: begin
|
|
`ASSERT(f_first_in_fifo);
|
|
`ASSERT(wr_addr == f_next_addr);
|
|
`ASSERT(f_fill >= 1);
|
|
end
|
|
2'b10: begin
|
|
`ASSERT(f_first_in_fifo);
|
|
`ASSERT(f_next_in_fifo);
|
|
if (!lcl_rd_empty && (rd_addr == f_addr))
|
|
`ASSERT(lcl_rd_data == f_first);
|
|
`ASSERT(f_fill >= 2);
|
|
end
|
|
2'b11: begin
|
|
`ASSERT(rd_addr == f_next_addr);
|
|
`ASSERT(f_next_in_fifo);
|
|
`ASSERT(f_fill >= 1);
|
|
if (!lcl_rd_empty)
|
|
`ASSERT(lcl_rd_data == f_next);
|
|
end
|
|
endcase
|
|
// }}}
|
|
|
|
generate if (OPT_REGISTER_READS)
|
|
begin
|
|
reg past_o_rd_empty;
|
|
|
|
always @(posedge gbl_clk)
|
|
past_o_rd_empty <= o_rd_empty;
|
|
|
|
always @(posedge gbl_clk)
|
|
if (f_past_valid_gbl && i_rd_reset_n)
|
|
begin
|
|
if ($past(!o_rd_empty && !i_rd && i_rd_reset_n))
|
|
`ASSERT($stable(o_rd_data));
|
|
end
|
|
|
|
always @(posedge gbl_clk)
|
|
if (!f_rd_in_reset && i_rd_reset_n && i_rclk && !past_rclk)
|
|
begin
|
|
if (past_o_rd_empty)
|
|
`ASSERT(o_rd_data == past_rd_data);
|
|
if (past_rd)
|
|
`ASSERT(o_rd_data == past_rd_data);
|
|
end
|
|
|
|
end endgenerate
|
|
// }}}
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Cover checks
|
|
// {{{
|
|
////////////////////////////////////////////////////////////////////////
|
|
//
|
|
//
|
|
|
|
// Prove that we can read and write the FIFO
|
|
// {{{
|
|
always @(*)
|
|
if (i_wr_reset_n && i_rd_reset_n)
|
|
begin
|
|
cover(o_rd_empty);
|
|
cover(!o_rd_empty);
|
|
cover(f_state == 2'b01);
|
|
cover(f_state == 2'b10);
|
|
cover(f_state == 2'b11);
|
|
cover(&f_fill[MSB-1:0]);
|
|
|
|
cover(i_rd);
|
|
cover(i_rd && !o_rd_empty);
|
|
end
|
|
// }}}
|
|
|
|
`ifdef AFIFO
|
|
generate if (!F_OPT_DATA_STB)
|
|
begin : COVER_FULL
|
|
// {{{
|
|
reg cvr_full;
|
|
|
|
initial cvr_full = 1'b0;
|
|
always @(posedge gbl_clk)
|
|
if (!i_wr_reset_n)
|
|
cvr_full <= 1'b0;
|
|
else if (o_wr_full)
|
|
cvr_full <= 1'b1;
|
|
|
|
|
|
always @(*)
|
|
if (f_past_valid_gbl && i_wr_reset_n)
|
|
begin
|
|
cover(o_wr_full);
|
|
cover(o_rd_empty && cvr_full);
|
|
cover(o_rd_empty && f_fill == 0 && cvr_full);
|
|
end
|
|
// }}}
|
|
end else begin : COVER_NEARLY_FULL
|
|
// {{{
|
|
reg cvr_nearly_full;
|
|
|
|
initial cvr_nearly_full = 1'b0;
|
|
always @(posedge gbl_clk)
|
|
if (!i_wr_reset_n)
|
|
cvr_nearly_full <= 1'b0;
|
|
else if (f_fill == { 1'b0, {(LGFIFO){1'b1} }})
|
|
cvr_nearly_full <= 1'b1;
|
|
|
|
|
|
always @(*)
|
|
if (f_past_valid_gbl && i_wr_reset_n)
|
|
begin
|
|
cover(f_fill == { 1'b0, {(LGFIFO){1'b1} }});
|
|
cover(cvr_nearly_full && i_wr_reset_n);
|
|
cover(o_rd_empty && cvr_nearly_full);
|
|
cover(o_rd_empty && f_fill == 0 && cvr_nearly_full);
|
|
end
|
|
// }}}
|
|
end endgenerate
|
|
`endif
|
|
// }}}
|
|
`endif
|
|
// }}}
|
|
endmodule
|