Memory Verification Using UVM
Memory Verification Using UVM
Verification
using
Normal Read and Write to Memory Verification UVM Testbench
What is memory?
Memory is an electronic component that can store information. it stores at a certain address
while reading from memory it retrieves the data from a certain memory.
DUT
module memory #(
parameter ADDR_WIDTH = 32,
parameter DATA_WIDTH = 64
) (
input clk, // Clock signal
input reset, // Reset signal
// Control signals
input [ADDR_WIDTH-1:0] addr, // Memory address
input wr_en, // Write enable signal
input rd_en, // Read enable signal
// Data signals
input [DATA_WIDTH-1:0] wdata, // Data to be written
output reg [DATA_WIDTH-1:0] rdata // Data read from memory
);
// Memory array
reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH];
// Read data from memory on positive clock edge if read enable is asserted
always @(posedge clk)
if (rd_en)
rdata <= mem[addr];
endmodule
INTERFACE
// ------------------------
// Driver clocking block
// ------------------------
clocking driver_cb @(posedge clk);
default input #2 output #2; // Set timing delays for inputs and outputs
output addr; // Output address
output wr_en; // Output write enable signal
output rd_en; // Output read enable signal
output wdata; // Output data to be written
input rdata; // Input data read from memory
endclocking
// ------------------------
// Monitor clocking block
// ------------------------
clocking monitor_cb @(posedge clk);
default input #1 output #1; // Set timing delays for inputs and outputs
input addr; // Input address
input wr_en; // Input write enable signal
input rd_en; // Input read enable signal
input wdata; // Input data to be written
input rdata; // Input data read from memory
endclocking
// ------------------------
// Driver modport
// ------------------------
modport DRIVER (clocking driver_cb, input clk, reset);
// ------------------------
// Monitor modport
// ------------------------
modport MONITOR (clocking monitor_cb, input clk, reset);
endinterface
SEQUENCE ITEM
// ------------------------
// Define random data
// ------------------------
rand bit [1:0] addr; // Random 2-bit address
rand bit wr_en; // Random write enable signal
rand bit rd_en; // Random read enable signal
rand bit [7:0] wdata; // Random 8-bit data to be written
bit [7:0] rdata; // 8-bit data read from memory
// ------------------------
// Constructor
// ------------------------
function new(string name = "mem_seq_item");
super.new(name); // Call base class constructor
endfunction
// ------------------------
// Constraint
// ------------------------
constraint wr_rd_c { wr_en != rd_en; }; // Ensure write and read signals
are different
endclass
SEQUENCE
// ------------------------
// Constructor
// ------------------------
function new(string name = "mem_sequence");
super.new(name); // Call base class constructor
endfunction
// ------------------------
// Virtual task body
// ------------------------
virtual task body();
mem_seq_item req; // Declare a memory sequence item
SEQUENCER
// ------------------------
// Constructor
// ------------------------
function new(string name, uvm_component parent);
super.new(name, parent); // Call base class constructor with name and
parent component
endfunction
endclass
DRIVER
// ------------------------
// Constructor
// ------------------------
function new (string name, uvm_component parent);
super.new(name, parent); // Call base class constructor
endfunction : new
// ------------------------
// Build phase
// ------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase); // Call base class build 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); // Get the next sequence item
drive(); // Drive the sequence item
seq_item_port.item_done(); // Notify that the item has been processed
end
endtask : run_phase
// ------------------------
// Drive task
// ------------------------
virtual task drive();
vif.DRIVER.driver_cb.wr_en <= 0; // Initialize write enable to 0
vif.DRIVER.driver_cb.rd_en <= 0; // Initialize read enable to 0
MONITOR
class mem_monitor extends uvm_monitor;
virtual mem_if vif; // Virtual interface for communication with the DUT
uvm_analysis_port #(mem_seq_item) item_collected_port; // Analysis port for
collected items
mem_seq_item trans_collected; // Memory transaction item for collecting
data
// ------------------------
// Constructor
// ------------------------
function new(string name, uvm_component parent);
super.new(name, parent); // Call base class constructor
trans_collected = new(); // Initialize the collected transaction item
item_collected_port = new("item_collected_port", this); // Initialize the
analysis port
endfunction : new
// ------------------------
// Build phase
// ------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase); // Call base class build 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
// ------------------------
// Run phase
// ------------------------
virtual task run_phase(uvm_phase phase);
forever begin
@(posedge vif.MONITOR.clk); // Wait for the next clock edge
wait(vif.monitor_cb.wr_en || vif.monitor_cb.rd_en); // Wait until
either write or read operation
if (vif.monitor_cb.wr_en) begin
trans_collected.wr_en = vif.monitor_cb.wr_en; // Collect write enable
information
trans_collected.wdata = vif.monitor_cb.wdata; // Collect write data
information
trans_collected.rd_en = 0; // Reset read enable
@(posedge vif.MONITOR.clk); // Wait for the next clock edge
end
if (vif.monitor_cb.rd_en) begin
trans_collected.rd_en = vif.monitor_cb.rd_en; // Collect read enable
information
trans_collected.wr_en = 0; // Reset write enable
@(posedge vif.MONITOR.clk); // Wait for the next clock edge
@(posedge vif.MONITOR.clk); // Wait for an additional clock edge
(double edge for read)
trans_collected.rdata = vif.monitor_cb.rdata; // Collect read data
information
end
AGENT
// ------------------------
// Constructor
// ------------------------
function new(string name, uvm_component parent);
super.new(name, parent); // Call base class constructor
endfunction : new
// ------------------------
// Build phase
// ------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase); // Call base class build phase
monitor = mem_monitor::type_id::create("monitor", this); // Create the
monitor component
// ------------------------
// Connect phase
// ------------------------
function void connect_phase(uvm_phase phase);
if (get_is_active() == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export); // Connect
driver's seq_item_port to sequencer's seq_item_export
end
endfunction : connect_phase
endclass : mem_agent
ENVIRONMENT
class mem_model_env extends uvm_env;
mem_agent mem_agnt; // Memory agent component
mem_scoreboard mem_scb; // Memory scoreboard component
// ------------------------
// Constructor
// ------------------------
function new(string name, uvm_component parent);
super.new(name, parent); // Call base class constructor
endfunction : new
// ------------------------
// Build phase
// ------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase); // Call base class build phase
mem_agnt = mem_agent::type_id::create("mem_agnt", this); // Create the
memory agent component
mem_scb = mem_scoreboard::type_id::create("mem_scb", this); // Create the
memory scoreboard component
endfunction : build_phase
// ------------------------
// Connect phase
// ------------------------
function void connect_phase(uvm_phase phase);
mem_agnt.monitor.item_collected_port.connect(mem_scb.item_collected_export);
// Connect the analysis port of the monitor in mem_agnt to the
item_collected_export of mem_scb
endfunction : connect_phase
endclass : mem_model_env
SCOREBOARD
class mem_scoreboard extends uvm_scoreboard;
mem_seq_item pkt_qu[$]; // Queue to store received memory sequence items
bit [63:0] sc_mem [4]; // Memory array for expected data
uvm_analysis_imp#(mem_seq_item, mem_scoreboard) item_collected_export; //
Analysis port for collected items
// ------------------------
// Constructor
// ------------------------
function new(string name, uvm_component parent);
super.new(name, parent); // Call base class constructor
endfunction : new
// ------------------------
// Build phase
// ------------------------
function void build_phase(uvm_phase phase);
super.build_phase(phase); // Call base class build phase
item_collected_export = new("item_collected_export", this); // Initialize
the analysis port
foreach (sc_mem[i]) sc_mem[i] = 8'hFF; // Initialize memory array with
default values
endfunction : build_phase
// ------------------------
// Write virtual function
// ------------------------
virtual function void write(mem_seq_item pkt);
pkt_qu.push_back(pkt); // Enqueue the received memory sequence item
endfunction : write
// ------------------------
// Run phase task
// ------------------------
virtual task run_phase(uvm_phase phase);
mem_seq_item mem_pkt;
forever begin
wait(pkt_qu.size() > 0); // Wait until there are items in the queue
mem_pkt = pkt_qu.pop_front(); // Dequeue the front item from the queue
if (mem_pkt.wr_en) begin
sc_mem[mem_pkt.addr] = mem_pkt.wdata; // Update expected data in the
memory array
UVM TOP
module tbench_top;
// ------------------------
// Clock and Reset Signal Declaration
// ------------------------
bit clk;
bit reset;
// ------------------------
// Clock Generation
// ------------------------
always #5 clk = ~clk; // Generate a clock signal with a period of 10 time
units
// ------------------------
// Reset Generation
// ------------------------
initial begin
reset = 1; // Assert reset
#5 reset = 0; // Deassert reset after 5 time units
end
// ------------------------
// Interface and DUT instantiation
// ------------------------
mem_if intf(clk, reset); // Instantiate the virtual interface
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)
); // Instantiate the memory DUT
// ------------------------
// Initial Block
// ------------------------
initial begin
uvm_config_db#(virtual mem_if)::set(uvm_root::get(), "*", "vif", intf);
// Set the virtual interface in the UVM configuration database
// Enable waveform dumping
$dumpfile("dump.vcd");
$dumpvars;
end
// ------------------------
// Test Execution
// ------------------------
initial begin
run_test("mem_wr_rd_test"); // Start the UVM test
end
endmodule
UVM TEST
class mem_wr_rd_test extends uvm_test;
`uvm_component_utils(mem_wr_rd_test) // Macro for automatic UVM component
registration
// ------------------------
// Constructor
// ------------------------
function new(string name = "mem_wr_rd_test", uvm_component parent);
super.new(name, parent); // Call base class constructor
endfunction : new
// ------------------------
// Build phase
// ------------------------
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase); // Call base class build phase
// ------------------------
// Run phase task
// ------------------------
task run_phase(uvm_phase phase);
phase.raise_objection(this); // Notify the phase that the test is active