FIFO
Design Code
module FIFO(input clk, rst, wr, rd,
input [7:0] din, output reg [7:0] dout,
output empty, full);
// Pointers for write and read operations
reg [3:0] wptr = 0, rptr = 0;
// Counter for tracking the number of elements in the FIFO
reg [4:0] cnt = 0;
// Memory array to store data
reg [7:0] mem [15:0];
always @(posedge clk)
begin
if (rst == 1'b1)
begin
// Reset the pointers and counter when the reset signal is asserted
wptr <= 0;
rptr <= 0;
cnt <= 0;
end
else if (wr && !full)
begin
// Write data to the FIFO if it's not full
mem[wptr] <= din;
wptr <= wptr + 1;
cnt <= cnt + 1;
end
else if (rd && !empty)
begin
// Read data from the FIFO if it's not empty
dout <= mem[rptr];
rptr <= rptr + 1;
cnt <= cnt - 1;
end
end
// Determine if the FIFO is empty or full
assign empty = (cnt == 0) ? 1'b1 : 1'b0;
assign full = (cnt == 16) ? 1'b1 : 1'b0;
endmodule
//////////////////////////////////////
// Define an interface for the FIFO
interface fifo_if;
logic clock, rd, wr; // Clock, read, and write signals
logic full, empty; // Flags indicating FIFO status
logic [7:0] data_in; // Data input
logic [7:0] data_out; // Data output
logic rst; // Reset signal
endinterface
TB code with comments
class transaction;
rand bit oper; // Randomized bit for operation control (1 or 0)
bit rd, wr; // Read and write control bits
bit [7:0] data_in; // 8-bit data input
bit full, empty; // Flags for full and empty status
bit [7:0] data_out; // 8-bit data output
constraint oper_ctrl {
oper dist {1 :/ 50 , 0 :/ 50}; // Constraint to randomize 'oper' with 50% probability of 1 and
50% probability of 0
endclass
///////////////////////////////////////////////////
class generator;
transaction tr; // Transaction object to generate and send
mailbox #(transaction) mbx; // Mailbox for communication
int count = 0; // Number of transactions to generate
int i = 0; // Iteration counter
event next; // Event to signal when to send the next transaction
event done; // Event to convey completion of requested number of transactions
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
tr = new();
endfunction;
task run();
repeat (count) begin
assert (tr.randomize) else $error("Randomization failed");
i++;
mbx.put(tr);
$display("[GEN] : Oper : %0d iteration : %0d", tr.oper, i);
@(next);
end -> done;
endtask
endclass
////////////////////////////////////////////
class driver;
virtual fifo_if fif; // Virtual interface to the FIFO
mailbox #(transaction) mbx; // Mailbox for communication
transaction datac; // Transaction object for communication
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction;
// Reset the DUT
task reset();
fif.rst <= 1'b1;
fif.rd <= 1'b0;
fif.wr <= 1'b0;
fif.data_in <= 0;
repeat (5) @(posedge fif.clock);
fif.rst <= 1'b0;
$display("[DRV] : DUT Reset Done");
$display("------------------------------------------");
endtask
// Write data to the FIFO
task write();
@(posedge fif.clock);
fif.rst <= 1'b0;
fif.rd <= 1'b0;
fif.wr <= 1'b1;
fif.data_in <= $urandom_range(1, 10);
@(posedge fif.clock);
fif.wr <= 1'b0;
$display("[DRV] : DATA WRITE data : %0d", fif.data_in);
@(posedge fif.clock);
endtask
// Read data from the FIFO
task read();
@(posedge fif.clock);
fif.rst <= 1'b0;
fif.rd <= 1'b1;
fif.wr <= 1'b0;
@(posedge fif.clock);
fif.rd <= 1'b0;
$display("[DRV] : DATA READ");
@(posedge fif.clock);
endtask
// Apply random stimulus to the DUT
task run();
forever begin
mbx.get(datac);
if (datac.oper == 1'b1)
write();
else
read();
end
endtask
endclass
///////////////////////////////////////////////////////
class monitor;
virtual fifo_if fif; // Virtual interface to the FIFO
mailbox #(transaction) mbx; // Mailbox for communication
transaction tr; // Transaction object for monitoring
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction;
task run();
tr = new();
forever begin
repeat (2) @(posedge fif.clock);
tr.wr = fif.wr;
tr.rd = fif.rd;
tr.data_in = fif.data_in;
tr.full = fif.full;
tr.empty = fif.empty;
@(posedge fif.clock);
tr.data_out = fif.data_out;
mbx.put(tr);
$display("[MON] : Wr:%0d rd:%0d din:%0d dout:%0d full:%0d empty:%0d", tr.wr, tr.rd,
tr.data_in, tr.data_out, tr.full, tr.empty);
end
endtask
endclass
/////////////////////////////////////////////////////
class scoreboard;
mailbox #(transaction) mbx; // Mailbox for communication
transaction tr; // Transaction object for monitoring
event next;
bit [7:0] din[$]; // Array to store written data
bit [7:0] temp; // Temporary data storage
int err = 0; // Error count
function new(mailbox #(transaction) mbx);
this.mbx = mbx;
endfunction;
task run();
forever begin
mbx.get(tr);
$display("[SCO] : Wr:%0d rd:%0d din:%0d dout:%0d full:%0d empty:%0d", tr.wr, tr.rd,
tr.data_in, tr.data_out, tr.full, tr.empty);
if (tr.wr == 1'b1) begin
if (tr.full == 1'b0) begin
din.push_front(tr.data_in);
$display("[SCO] : DATA STORED IN QUEUE :%0d", tr.data_in);
end
else begin
$display("[SCO] : FIFO is full");
end
$display("--------------------------------------");
end
if (tr.rd == 1'b1) begin
if (tr.empty == 1'b0) begin
temp = din.pop_back();
if (tr.data_out == temp)
$display("[SCO] : DATA MATCH");
else begin
$error("[SCO] : DATA MISMATCH");
err++;
end
end
else begin
$display("[SCO] : FIFO IS EMPTY");
end
$display("--------------------------------------");
end
-> next;
end
endtask
endclass
///////////////////////////////////////////////////////
class environment;
generator gen;
driver drv;
monitor mon;
scoreboard sco;
mailbox #(transaction) gdmbx; // Generator + Driver mailbox
mailbox #(transaction) msmbx; // Monitor + Scoreboard mailbox
event nextgs;
virtual fifo_if fif;
function new(virtual fifo_if fif);
gdmbx = new();
gen = new(gdmbx);
drv = new(gdmbx);
msmbx = new();
mon = new(msmbx);
sco = new(msmbx);
this.fif = fif;
drv.fif = this.fif;
mon.fif = this.fif;
gen.next = nextgs;
sco.next = nextgs;
endfunction
task pre_test();
drv.reset();
endtask
task test();
fork
gen.run();
drv.run();
mon.run();
sco.run();
join_any
endtask
task post_test();
wait(gen.done.triggered);
$display("---------------------------------------------");
$display("Error Count :%0d", sco.err);
$display("---------------------------------------------");
$finish();
endtask
task run();
pre_test();
test();
post_test();
endtask
endclass
///////////////////////////////////////////////////////
module tb;
fifo_if fif();
FIFO dut (fif.clock, fif.rst, fif.wr, fif.rd, fif.data_in, fif.data_out, fif.empty, fif.full);
initial begin
fif.clock <= 0;
end
always #10 fif.clock <= ~fif.clock;
environment env;
initial begin
env = new(fif);
env.gen.count = 10;
env.run();
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
endmodule
SPI PROTOCOL
Design Code
module spi_master(
input clk, newd,rst,
input [11:0] din,
output reg sclk,cs,mosi
);
typedef enum bit [1:0] {idle = 2'b00, enable = 2'b01, send = 2'b10, comp = 2'b11 } state_type;
state_type state = idle;
int countc = 0;
int count = 0;
/////////////////////////generation of sclk
always@(posedge clk)
begin
if(rst == 1'b1) begin
countc <= 0;
sclk <= 1'b0;
end
else begin
if(countc < 10 )
countc <= countc + 1;
else
begin
countc <= 0;
sclk <= ~sclk;
end
end
end
//////////////////state machine
reg [11:0] temp;
always@(posedge sclk)
begin
if(rst == 1'b1) begin
cs <= 1'b1;
mosi <= 1'b0;
end
else begin
case(state)
idle:
begin
if(newd == 1'b1) begin
state <= send;
temp <= din;
cs <= 1'b0;
end
else begin
state <= idle;
temp <= 8'h00;
end
end
send : begin
if(count <= 11) begin
mosi <= temp[count]; /////sending lsb first
count <= count + 1;
end
else
begin
count <= 0;
state <= idle;
cs <= 1'b1;
mosi <= 1'b0;
end
end
default : state <= idle;
endcase
end
end
endmodule
///////////////////////////
module spi_slave (
input sclk, cs, mosi,
output [11:0] dout,
output reg done
);
typedef enum bit {detect_start = 1'b0, read_data = 1'b1} state_type;
state_type state = detect_start;
reg [11:0] temp = 12'h000;
int count = 0;
always@(posedge sclk)
begin
case(state)
detect_start:
begin
done <= 1'b0;
if(cs == 1'b0)
state <= read_data;
else
state <= detect_start;
end
read_data : begin
if(count <= 11)
begin
count <= count + 1;
temp <= { mosi, temp[11:1]};
end
else
begin
count <= 0;
done <= 1'b1;
state <= detect_start;
end
end
endcase
end
assign dout = temp;
endmodule
///////////////////////////////
module top (
input clk, rst, newd,
input [11:0] din,
output [11:0] dout,
output done
);
wire sclk, cs, mosi;
spi_master m1 (clk, newd, rst, din, sclk, cs, mosi);
spi_slave s1 (sclk, cs, mosi, dout, done);
endmodule
Verilog TB
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
module tb( );
reg clk = 0, rst = 0, newd = 0;
reg [11:0] din = 0;
wire [11:0] dout;
wire done;
always #10 clk = ~clk;
top dut (clk, rst, newd, din, dout, done);
initial begin
rst = 1;
repeat(5) @(posedge clk);
rst = 0;
for(int i = 0; i < 10; i++)
begin
newd = 1;
din = $urandom;
@(posedge dut.s1.sclk);
newd = 0;
@(posedge done);
end
end
endmodule
Testbench Code
////////////////Transaction Class
class transaction;
bit newd; // Flag for new transaction
rand bit [11:0] din; // Random 12-bit data input
bit [11:0] dout; // 12-bit data output
function transaction copy();
copy = new(); // Create a copy of the transaction
copy.newd = this.newd; // Copy the newd flag
copy.din = this.din; // Copy the data input
copy.dout = this.dout; // Copy the data output
endfunction
endclass
////////////////Generator Class
class generator;
transaction tr; // Transaction object
mailbox #(transaction) mbx; // Mailbox for transactions
event done; // Done event
int count = 0; // Transaction count
event drvnext; // Event to synchronize with driver
event sconext; // Event to synchronize with scoreboard
function new(mailbox #(transaction) mbx);
this.mbx = mbx; // Initialize mailbox
tr = new(); // Create a new transaction
endfunction
task run();
repeat(count) begin
assert(tr.randomize) else $error("[GEN] :Randomization Failed");
mbx.put(tr.copy); // Put a copy of the transaction in the mailbox
$display("[GEN] : din : %0d", tr.din);
@(sconext); // Wait for the scoreboard synchronization event
end
-> done; // Signal when done
endtask
endclass
////////////////Driver Class
class driver;
virtual spi_if vif; // Virtual interface
transaction tr; // Transaction object
mailbox #(transaction) mbx; // Mailbox for transactions
mailbox #(bit [11:0]) mbxds; // Mailbox for data output to monitor
event drvnext; // Event to synchronize with generator
bit [11:0] din; // Data input
function new(mailbox #(bit [11:0]) mbxds, mailbox #(transaction) mbx);
this.mbx = mbx; // Initialize mailboxes
this.mbxds = mbxds;
endfunction
task reset();
vif.rst <= 1'b1; // Set reset signal
vif.newd <= 1'b0; // Clear new data flag
vif.din <= 1'b0; // Clear data input
repeat(10) @(posedge vif.clk);
vif.rst <= 1'b0; // Clear reset signal
repeat(5) @(posedge vif.clk);
$display("[DRV] : RESET DONE");
$display("-----------------------------------------");
endtask
task run();
forever begin
mbx.get(tr); // Get a transaction from the mailbox
vif.newd <= 1'b1; // Set new data flag
vif.din <= tr.din; // Set data input
mbxds.put(tr.din); // Put data in the mailbox for the monitor
@(posedge vif.sclk);
vif.newd <= 1'b0; // Clear new data flag
@(posedge vif.done);
$display("[DRV] : DATA SENT TO DAC : %0d",tr.din);
@(posedge vif.sclk);
end
endtask
endclass
////////////////Monitor Class
class monitor;
transaction tr; // Transaction object
mailbox #(bit [11:0]) mbx; // Mailbox for data output
virtual spi_if vif; // Virtual interface
function new(mailbox #(bit [11:0]) mbx);
this.mbx = mbx; // Initialize the mailbox
endfunction
task run();
tr = new(); // Create a new transaction
forever begin
@(posedge vif.sclk);
@(posedge vif.done);
tr.dout = vif.dout; // Record data output
@(posedge vif.sclk);
$display("[MON] : DATA SENT : %0d", tr.dout);
mbx.put(tr.dout); // Put data in the mailbox
end
endtask
endclass
////////////////Scoreboard Class
class scoreboard;
mailbox #(bit [11:0]) mbxds, mbxms; // Mailboxes for data from driver and monitor
bit [11:0] ds; // Data from driver
bit [11:0] ms; // Data from monitor
event sconext; // Event to synchronize with environment
function new(mailbox #(bit [11:0]) mbxds, mailbox #(bit [11:0]) mbxms);
this.mbxds = mbxds; // Initialize mailboxes
this.mbxms = mbxms;
endfunction
task run();
forever begin
mbxds.get(ds); // Get data from driver
mbxms.get(ms); // Get data from monitor
$display("[SCO] : DRV : %0d MON : %0d", ds, ms);
if(ds == ms)
$display("[SCO] : DATA MATCHED");
else
$display("[SCO] : DATA MISMATCHED");
$display("-----------------------------------------");
->sconext; // Synchronize with the environment
end
endtask
endclass
////////////////Environment Class
class environment;
generator gen; // Generator object
driver drv; // Driver object
monitor mon; // Monitor object
scoreboard sco; // Scoreboard object
event nextgd; // Event for generator to driver communication
event nextgs; // Event for generator to scoreboard communication
mailbox #(transaction) mbxgd; // Mailbox for generator to driver communication
mailbox #(bit [11:0]) mbxds; // Mailbox for driver to monitor communication
mailbox #(bit [11:0]) mbxms; // Mailbox for monitor to scoreboard communication
virtual spi_if vif; // Virtual interface
function new(virtual spi_if vif);
mbxgd = new(); // Initialize mailboxes
mbxms = new();
mbxds = new();
gen = new(mbxgd); // Initialize generator
drv = new(mbxds,mbxgd); // Initialize driver
mon = new(mbxms); // Initialize monitor
sco = new(mbxds, mbxms); // Initialize scoreboard
this.vif = vif;
drv.vif = this.vif;
mon.vif = this.vif;
gen.sconext = nextgs; // Set synchronization events
sco.sconext = nextgs;
gen.drvnext = nextgd;
drv.drvnext = nextgd;
endfunction
task pre_test();
drv.reset(); // Perform driver reset
endtask
task test();
fork
gen.run(); // Run generator
drv.run(); // Run driver
mon.run(); // Run monitor
sco.run(); // Run scoreboard
join_any
endtask
task post_test();
wait(gen.done.triggered); // Wait for generator to finish
$finish();
endtask
task run();
pre_test();
test();
post_test();
endtask
endclass
////////////////Testbench Top
module tb;
spi_if vif(); // Virtual interface instance
top dut(vif.clk,vif.rst,vif.newd,vif.din,vif.dout,vif.done);
initial begin
vif.clk <= 0;
end
always #10 vif.clk <= ~vif.clk;
environment env;
assign vif.sclk = dut.m1.sclk;
initial begin
env = new(vif);
env.gen.count = 4;
env.run();
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
endmodule