Synchronous FIFO Design and Verification with SystemVerilog Testbench.
This project focuses on the design and verification of a Synchronous FIFO module in Verilog, ensuring synchronized data transfer and accurate FIFO behaviour. The system uses SystemVerilog for testbench development and covers various aspects of the design, including write and read operations, pointer management, and a scoreboard for verification.
A synchronous FIFO is a digital circuit that stores and retrieves data in a first-in, first-out manner, where data is written and read based on a common clock signal, ensuring synchronized data transfers between the input and output.
It uses pointers and counters to manage data flow and maintain proper synchronization between the read and write operations.
Synchronous FIFO: Data is transferred in sync with a clock signal, ensuring coordinated read and write operations.
Asynchronous FIFO: Data is transfer not in sync with a clock, potentially leading to timing issues and requiring additional handshaking signals.
Synchronous FIFO DUT using Verilog:
module fifo (
input reset, clk, rd, wr,
input [7:0] din,
output full, empty,
output reg [7:0] dout,
output reg [3:0] fifo_cnt
);
reg [7:0] fifo_ram [0:7];
reg [2:0] wr_ptr, rd_ptr;
Defination of “empty” and “full”
assign empty = (fifo_cnt == 0);
assign full = (fifo_cnt == 8);
Write and Read Block
// Write
always @(posedge clk) begin
if (wr && !full) begin
fifo_ram[wr_ptr] <= din;
wr_ptr <= wr_ptr + 1;
fifo_cnt <= fifo_cnt + 1;
end
end
// Read
always @(posedge clk) begin
if (rd && !empty) begin
dout <= fifo_ram[rd_ptr];
rd_ptr <= rd_ptr + 1;
fifo_cnt <= fifo_cnt — 1;
end
end
Pointer Block
always @(posedge clk or posedge reset) begin
if (reset) begin
wr_ptr <= 0;
rd_ptr <= 0;
end
else begin
if (wr && !full || wr && rd) wr_ptr <= wr_ptr + 1;
if (rd && !empty || rd && wr) rd_ptr <= rd_ptr + 1;
end
end
FIFO Counter
always @(posedge clk or posedge reset) begin
if (reset)
fifo_cnt <= 0;
else
case ({rd, wr})
2'b01: fifo_cnt <= (fifo_cnt == 8) ? 8 : fifo_cnt + 1;
2'b10: fifo_cnt <= (fifo_cnt == 0) ? 0 : fifo_cnt — 1;
endcase
end
endmodule
Synchronous FIFO testbench using System Verilog:
class transaction;
rand bit [7:0] din;
rand bit [7:0] addr;
bit wr;
bit [7:0] dout;
endclass
class generator;
mailbox mbx;
transaction t;
event done;
integer i;
function new(mailbox mbx);
this.mbx = mbx;
endfunction
task run();
t = new();
for (i = 0; i < 100; i++) begin
t.randomize();
mbx.put(t);
$display(“[GEN] : Data sent to driver”);
@(done);
end
endtask
endclass
interface counter_intf();
logic clk, rst, wr;
logic [7:0] din, addr;
logic [7:0] dout;
endinterface
class driver;
mailbox mbx;
event done;
transaction t;
virtual counter_intf vif;
function new(mailbox mbx);
this.mbx = mbx;
endfunction
task run();
t = new();
forever begin
mbx.get(t);
vif.din = t.din;
vif.addr = t.addr;
$display(“[DRV] : Interface Trigger”);
->done;
@(posedge vif.clk);
end
endtask
endclass
Get Mohan Sardar’s stories in your inbox
Join Medium for free to get updates from this writer.
class monitor;
mailbox mbx;
virtual counter_intf vif;
transaction t;
function new(mailbox mbx);
this.mbx = mbx;
endfunction
task run();
t = new();
forever begin
t.din = vif.din;
t.addr = vif.addr;
t.dout = vif.dout;
t.wr = vif.wr;
mbx.put(t);
$display(“[MON] : Data sent to Scoreboard”);
@(posedge vif.clk);
end
endtask
endclass
class scoreboard;
mailbox mbx;
transaction tarr[256];
transaction t;
function new(mailbox mbx);
this.mbx = mbx;
endfunction
task run();
forever begin
mbx.get(t);
if (t.wr == 1'b1) begin
if (tarr[t.addr] == null) begin
tarr[t.addr] = new();
tarr[t.addr] = t;
$display(“[SCO] : Data stored”);
end
end else begin
if (tarr[t.addr] == null) begin
if (t.dout == 0)
$display(“[SCO] : Data read Test Passed”);
else
$display(“[SCO] : Data read Test Failed”);
end else begin
if (t.dout == tarr[t.addr].din)
$display(“[SCO] : Data read Test Passed”);
else
$display(“[SCO] : Data read Test Failed”);
end
end
end
endtask
endclass
class environment;
generator gen;
driver drv;
monitor mon;
scoreboard sco;
virtual counter_intf vif;
mailbox gdmbx;
mailbox msmbx;
event gddone;
function new(mailbox gdmbx, mailbox msmbx);
this.gdmbx = gdmbx;
this.msmbx = msmbx;
gen = new(gdmbx);
drv = new(gdmbx);
mon = new(msmbx);
sco = new(msmbx);
endfunction
task run();
gen.done = gddone;
drv.done = gddone;
drv.vif = vif;
mon.vif = vif;
fork
gen.run();
drv.run();
mon.run();
sco.run();
join_any
endtask
endclass
module tb;
environment env;
counter_intf vif();
mailbox gdmbx, msmbx;
fifo dut (vif.clk, vif.rst, vif.wr, vif.din, vif.addr, vif.dout);
always #5 vif.clk = ~vif.clk;
initial begin
vif.clk = 0;
vif.rst = 1;
vif.wr = 1;
#50;
vif.wr = 1;
vif.rst = 0;
#300;
vif.wr = 0;
#200;
vif.rst = 0;
#50;
end
initial begin
gdmbx = new();
msmbx = new();
env = new(gdmbx, msmbx);
env.vif = vif;
env.run();
#600;
$finish;
end
initial begin
$dumpvars;
$dumpfile(“dump.vcd”);
end
endmodule
— — — — — — — — — — — — — — — — — — — — —
Output:
Asynchronous FIFO:
To convert the provided synchronous FIFO code into an asynchronous FIFO, we need to eliminate the clock dependency in the read and write operations and use asynchronous resets. Here’s a modified version of the synchronous FIFO code to make it asynchronous:
module async_fifo(
input clk, // Clock signal
input rst, // Asynchronous reset
input rd, // Read request
input wr, // Write request
input [7:0] din, // Data input
output empty, // Empty flag
output full, // Full flag
output reg [7:0] dout, // Data output
output reg [3:0] fifo_cnt // FIFO count
);
=====================================
reg [7:0] fifo_ram [0:7];
reg [2:0] rd_ptr, wr_ptr;
=====================================
// Empty and full conditions
assign empty = (fifo_cnt == 0);
assign full = (fifo_cnt == 8);
=====================================
Read and write logic without clock dependencies
always @(posedge clk or posedge rst) begin
if (rst) begin
// Asynchronous reset
rd_ptr <= 0;
wr_ptr <= 0;
fifo_cnt <= 0;
endelse begin
Read operation
if (rd && !empty) begin
dout <= fifo_ram[rd_ptr];
rd_ptr <= (rd_ptr == 7) ? 0 : rd_ptr + 1;
fifo_cnt <= fifo_cnt — 1;
endWrite operation
if (wr && !full) begin
fifo_ram[wr_ptr] <= din;
wr_ptr <= (wr_ptr == 7) ? 0 : wr_ptr + 1;
fifo_cnt <= fifo_cnt + 1;
end
end
end
endmodule





