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