Memory UVM testbench

What is memory 

Memory is electronic component which can store information. it stores at certain address while reading from memory it retrieve the data from certain address from memory

Block Diagram of Memory

DUT

module memory
#( parameter ADDR_WIDTH = 32,
parameter DATA_WIDTH = 64 ) (
input clk,
input reset,

//control signals
input [ADDR_WIDTH-1:0] addr,
input wr_en,
input rd_en,

//data signals
input [DATA_WIDTH-1:0] wdata,
output [DATA_WIDTH-1:0] rdata
);

reg [DATA_WIDTH-1:0] rdata;

//Memory
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH];

//Reset
always @(posedge reset)
for(int i=0;i<2**ADDR_WIDTH;i++) mem[i]=8’hFF;

// Write data to Memory
always @(posedge clk)
if (wr_en) mem[addr] <= wdata;

// Read data from memory
always @(posedge clk)
if (rd_en) rdata <= mem[addr];

endmodule

//interface

interface mem_if(input logic clk,reset);

//—————————————
//declaring the signals
//—————————————
logic [31:0] addr;
logic wr_en;
logic rd_en;
logic [63:0] wdata;
logic [63:0] rdata;

//—————————————
//driver clocking block
//—————————————
clocking driver_cb @(posedge clk);
default input #2 output #2;
output addr;
output wr_en;
output rd_en;
output wdata;
input rdata;
endclocking

//—————————————
//monitor clocking block
//—————————————
clocking monitor_cb @(posedge clk);
default input #1 output #1;
input addr;
input wr_en;
input rd_en;
input wdata;
input rdata;
endclocking

//—————————————
//driver modport
//—————————————
modport DRIVER (clocking driver_cb,input clk,reset);

//—————————————
//monitor modport
//—————————————
modport MONITOR (clocking monitor_cb,input clk,reset);

endinterface

//Sequence item

class mem_seq_item extends uvm_sequence_item;
`uvm_object_utils(mem_seq_item)
//—————————————
//define random data
//—————————————
rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;

//—————————————
//Constructor
//—————————————
function new(string name = “mem_seq_item”);
super.new(name);
endfunction

//—————————————
//constraint
//—————————————
constraint wr_rd_c { wr_en != rd_en; };

endclass

//Sequences

class mem_sequence extends uvm_sequence#(mem_seq_item);

`uvm_object_utils(mem_sequence)

//—————————————
//Constructor
//—————————————
function new(string name = “mem_sequence”);
super.new(name);
endfunction

`uvm_declare_p_sequencer(mem_sequencer)

virtual task body();
repeat(5) begin
req = mem_seq_item::type_id::create(“req”);
req = mem_seq_item::type_id::create(“req”);
start_item(req);
assert(req.randomize());
finish_item(req);
end
endtask
endclass

//Sequencer

class mem_sequencer extends uvm_sequencer#(mem_seq_item);

`uvm_component_utils(mem_sequencer)

//—————————————
//constructor
//—————————————
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction

endclass

//driver

class mem_driver extends uvm_driver #(mem_seq_item);

//—————————————
// Virtual Interface
//—————————————
virtual mem_if vif;
`uvm_component_utils(mem_driver)

//—————————————
// Constructor
//—————————————
function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

//—————————————
// build phase
//—————————————
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, “”, “vif”, vif))
`uvm_fatal(“NO_VIF”,{“virtual interface must be set for: “,get_full_name(),”.vif”});
endfunction: build_phase

//—————————————
// run phase
//—————————————
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
drive();
seq_item_port.item_done();
end
endtask : run_phase

virtual task drive();
vif.DRIVER.driver_cb.wr_en <= 0;
vif.DRIVER.driver_cb.rd_en <= 0;
@(posedge vif.DRIVER.clk);

vif.DRIVER.driver_cb.addr <= req.addr;

if(req.wr_en) begin // write operation
vif.DRIVER.driver_cb.wr_en <= req.wr_en;
vif.DRIVER.driver_cb.wdata <= req.wdata;
@(posedge vif.DRIVER.clk);
end
else if(req.rd_en) begin //read operation
vif.DRIVER.driver_cb.rd_en <= req.rd_en;
@(posedge vif.DRIVER.clk);
vif.DRIVER.driver_cb.rd_en <= 0;
@(posedge vif.DRIVER.clk);
req.rdata = vif.DRIVER.driver_cb.rdata;
end

endtask : drive
endclass : mem_driver

//monitor

class mem_monitor extends uvm_monitor;

virtual mem_if vif;

uvm_analysis_port #(mem_seq_item) item_collected_port;

mem_seq_item trans_collected;

`uvm_component_utils(mem_monitor)

function new (string name, uvm_component parent);
super.new(name, parent);
trans_collected = new();
item_collected_port = new(“item_collected_port”, this);
endfunction : new

function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual mem_if)::get(this, “”, “vif”, vif))
`uvm_fatal(“NOVIF”,{“virtual interface must be set for: “,get_full_name(),”.vif”});
endfunction: build_phase

virtual task run_phase(uvm_phase phase);
forever begin
@(posedge vif.MONITOR.clk);
wait(vif.monitor_cb.wr_en || vif.monitor_cb.rd_en);
trans_collected.addr = vif.monitor_cb.addr;
if(vif.monitor_cb.wr_en) begin
trans_collected.wr_en = vif.monitor_cb.wr_en;
trans_collected.wdata = vif.monitor_cb.wdata;
trans_collected.rd_en = 0;
@(posedge vif.MONITOR.clk);
end
if(vif.monitor_cb.rd_en) begin
trans_collected.rd_en = vif.monitor_cb.rd_en;
trans_collected.wr_en = 0;
@(posedge vif.MONITOR.clk);
@(posedge vif.MONITOR.clk);
trans_collected.rdata = vif.monitor_cb.rdata;
end
item_collected_port.write(trans_collected);
end
endtask : run_phase

endclass : mem_monitor

//Agent

class mem_agent extends uvm_agent;

mem_driver driver;
mem_sequencer sequencer;
mem_monitor monitor;

`uvm_component_utils(mem_agent)

function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);
super.build_phase(phase);

monitor = mem_monitor::type_id::create(“monitor”, this);

if(get_is_active() == UVM_ACTIVE) begin
driver = mem_driver::type_id::create(“driver”, this);
sequencer = mem_sequencer::type_id::create(“sequencer”, this);
end
endfunction : build_phase

function void connect_phase(uvm_phase phase);
if(get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction : connect_phase

endclass : mem_agent

//environment

class mem_model_env extends uvm_env;

mem_agent mem_agnt;
mem_scoreboard mem_scb;

`uvm_component_utils(mem_model_env)

function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);
super.build_phase(phase);

mem_agnt = mem_agent::type_id::create(“mem_agnt”, this);
mem_scb = mem_scoreboard::type_id::create(“mem_scb”, this);
endfunction : build_phase

function void connect_phase(uvm_phase phase);
mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);
endfunction : connect_phase

endclass : mem_model_env

//checker

class mem_scoreboard extends uvm_scoreboard;

mem_seq_item pkt_qu[$];

bit [63:0] sc_mem [4];

uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export;
`uvm_component_utils(mem_scoreboard)

function new (string name, uvm_component parent);
super.new(name, parent);
endfunction : new

function void build_phase(uvm_phase phase);
super.build_phase(phase);
item_collected_export = new(“item_collected_export”, this);
foreach(sc_mem[i]) sc_mem[i] = 8’hFF;
endfunction: build_phase

virtual function void write(mem_seq_item pkt);
//pkt.print();
pkt_qu.push_back(pkt);
endfunction : write

virtual task run_phase(uvm_phase phase);
mem_seq_item mem_pkt;

forever begin
wait(pkt_qu.size() > 0);
mem_pkt = pkt_qu.pop_front();

if(mem_pkt.wr_en) begin
sc_mem[mem_pkt.addr] = mem_pkt.wdata;
`uvm_info(get_type_name(),$sformatf(“—— :: WRITE DATA :: ——“),UVM_LOW)
`uvm_info(get_type_name(),$sformatf(“Addr: %0h”,mem_pkt.addr),UVM_LOW)
`uvm_info(get_type_name(),$sformatf(“Data: %0h”,mem_pkt.wdata),UVM_LOW)
`uvm_info(get_type_name(),”————————————“,UVM_LOW)
end
else if(mem_pkt.rd_en) begin
if(sc_mem[mem_pkt.addr] == mem_pkt.rdata) begin
`uvm_info(get_type_name(),$sformatf(“—— :: READ DATA Match :: ——“),UVM_LOW)
`uvm_info(get_type_name(),$sformatf(“Addr: %0h”,mem_pkt.addr),UVM_LOW)
`uvm_info(get_type_name(),$sformatf(“Expected Data: %0h Actual Data: %0h”,sc_mem[mem_pkt.addr],mem_pkt.rdata),UVM_LOW)
`uvm_info(get_type_name(),”————————————“,UVM_LOW)
end
else begin
`uvm_error(get_type_name(),”—— :: READ DATA MisMatch :: ——“)
`uvm_info(get_type_name(),$sformatf(“Addr: %0h”,mem_pkt.addr),UVM_LOW)
`uvm_info(get_type_name(),$sformatf(“Expected Data: %0h Actual Data: %0h”,sc_mem[mem_pkt.addr],mem_pkt.rdata),UVM_LOW)
`uvm_info(get_type_name(),”————————————“,UVM_LOW)
end
end
end
endtask : run_phase
endclass : mem_scoreboard

//uvm top

module tbench_top;

//—————————————
//clock and reset signal declaration
//—————————————
bit clk;
bit reset;

//—————————————
//clock generation
//—————————————
always #5 clk = ~clk;

//—————————————
//reset Generation
//—————————————
initial begin
reset = 1;
#5 reset =0;
end

mem_if intf(clk,reset);

memory DUT (
.clk(intf.clk),
.reset(intf.reset),
.addr(intf.addr),
.wr_en(intf.wr_en),
.rd_en(intf.rd_en),
.wdata(intf.wdata),
.rdata(intf.rdata)
);

initial begin
uvm_config_db#(virtual mem_if)::set(uvm_root::get(),”*”,”vif”,intf);
//enable wave dump
$dumpfile(“dump.vcd”);
$dumpvars;
end

//—————————————
//calling test
//—————————————
initial begin
run_test(“mem_wr_rd_test”);
end

endmodule

// UVM test

class mem_wr_rd_test extends uvm_test;

`uvm_component_utils(mem_wr_rd_test)

mem_sequence seq;
mem_model_env env;

function new(string name = “mem_wr_rd_test”,uvm_component parent);
super.new(name,parent);
endfunction : new

virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);

// Create the sequence
seq = mem_sequence ::type_id::create(“seq”);
env = mem_model_env::type_id::create(“env”, this);

endfunction : build_phase

//—————————————
// run_phase
//—————————————
task run_phase(uvm_phase phase);

phase.raise_objection(this);
seq.start(env.mem_agnt.sequencer);
phase.drop_objection(this);

//set a drain-time for the environment if desired
phase.phase_done.set_drain_time(this, 50);
endtask : run_phase

endclass : mem_wr_rd_test