Sitemap
Mohan Sardar

Mohan Sardar

Automation and AI Innovation in Design Verification Engineer.

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.

4 min readOct 20, 2023

--

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:

Press enter or click to view image in full size

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;
end

else 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;
end

Write 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

--

--

Mohan Sardar
Mohan Sardar

Automation and AI Innovation in Design Verification Engineer.