UVM_PROJECTS
APB_RAM Verification
Anoushka Tripathi 13/11/24 UVM_PROJECTS
BASICS OF MEMORY
2nd half of
APB Addr where we have to store data
Transaction
If PWRITE is high user wish to
write data in memory , if low user
wants to read the data from
memory
Writing data to memory It is use to select slave device,if
we are using one single slave
PREADY is one indicates device than it will enable one
completion of a operation, if not signle slave and start
than it means slave want to extend transaction, If PSEL is low than it
the operation, it is use to extend will be in idle state
the transfer by slave
Requester Completer
- Master block will
generate new
tx transaction.
Master Slave
- Slave will be predominantly idle until and unless it receives a valid transaction from master.It
will serve the transaction request of master.
Master initiates transaction
Slave process request
APB Slave A
psel0
psel1
APB Master APB Slave B
psel2
APB Slave C
Multiple slaves with a single master
For two or more slaves
For example 2 slaves , we will declare psel [1:0]
PSEL 1 PSEL 0
Slave 1 Slave
0 1
1 0
Address phase Data phase
Understanding STRB
It is use to indicate valid data in lane
PWRITE(32 bits)
PSTRB
UNDERSTANDING WRITE OPERATION:
Start
Setup Psel = 1
Access PENABLE =1
COMPLETE
Let us design our UVM Testbench
We will start with config class
➔ We start by declaring class(apb_config) which we build by extending uvm_object, here in
this class we will declare the type of agent
Register class to factory
Standard constructor of uvm object
Enum type variable, which is
holding type of agent, default
value is UVM_ACTIVE, because
we plan to add driver in our
verification env and variable
name is_active
Building the Transaction class:
Here we divide process
into 3 main operations
read write and reset, to
PWRITE will Generate random value
facilate this we have
depend on for operation mode
used enum
oper_mode so
no need of rand oper_mode : type of
here operationcv
Add field macros for all
data members of class
For enum type
Memory is capable of adding 32 elements
that is why used this constraint
Added purposefully to generate SLV error
Now we will develop sequences :
First sequence will be 15 random transaction within valid address range of memory.
We name our sequence write_data we create it by extending uvm_sequence
Write_data represents a valid transaction hence because of which first constraint should be
enable while 2nd should be disabled.
This will be This body task will be called when we want to generate sequence
used to
send 15
Creating a object
random
sequences
Send Enable first constraint, NEXT line is to disable the 2nd constraint
request to
sequencer
As soon as our request is granted we call randomize to generate random
data
tr.op -> will overwrite the random mode to write mode
finish_item → here we will send transaction to sequencer
Let us now see read transaction
READ AFTER WRITE
Here we perform a single write and immediately next transaction will be read.
Send first write transaction
WRITE ERROR
Address generator will be
greater than 31
Next we have writeb_readb which writes bulk of data in memory
RESET DUT
Creating Driver class
Data container We require a interface by which we will apply
stimulus which we receive from sequencer to DUT
In build phase we will create object of transaction
Transaction tr is a data container here so whatever data sequencer send us we will store it in
data container and utilizing the data we have in transaction we will be triggering input signals of
our DUT.
We will utilize uvm config db to get
access of interface.
Creating tasks
This task will be used at start of simulation to reset our
Apply reset to our system for 5 clock ticks
We wait for 1 clock tick
If operation is write, we aleady know to write data in memory at first clock tick we need
to make psel high at the same time pwrite=1 , in next clk tick make penable = 1 than we need to
wait for pready to high to complete our write operation.
Wait for 1 clk tick
Waiting for ready signal
Storing SLV error
Here reset is 1 because it is active low.
Pwrite should be 1 this indicates that we want to write data to memory.
Requesting data
Notify sequencer to send next item
UVM MONITOR
Data Analysis port with which we will sending data to scoreboard
container Virtual interface through which we capture response of DUT
where we
will update
the
response of
DUT
Let us work on main section of monitor
Here we do exactly opposite what we did in driver
In driver flow we wait for positive edge of clock, than we wait for ready to become high
In monitor we will add rea
dy delay
first than capture the response.
In main phase of monitor we first wait for the positive edge of clock, ready don’t play any role in
reset phase so we did not added that for reset section…
We wait for ready here
Scoreboard
Implementation port we named it as recv
Agent
To determine type of agent we have
Connection between driver
and sequencer will happen
in connect phase of agent
Variable which is storing Default value
of agent of an verification env, if it is
active than we add a constructor to
driver and seq
Connecting item port and export
Test
In build phase we add a
constructor to each of
sequence
ENV
Connect phase connects monitor
and scoreboard together
Setting interface accessible to all
componenets
TESTBENCH
`timescale 1ns / 1ps
/*
module tb();
reg presetn = 0;
reg pclk = 0;
reg psel = 0;
reg penable = 0 ;
reg pwrite = 0;
reg [31:0] paddr = 0, pwdata = 0;
wire [31:0] prdata;
wire pready, pslverr;
apb_ram dut (presetn, pclk, psel, penable, pwrite, paddr, pwdata, prdata, pready, pslverr);
always #10 pclk = ~pclk;
initial begin
presetn = 0;
repeat(5) @(posedge pclk);
presetn = 1;
psel = 1;
pwrite = 1;
paddr = 12;
pwdata = 35;
@(posedge pclk);
penable = 1;
@(posedge pready);
psel = 0;
penable = 0;
@(posedge pclk);
psel = 1;
pwrite = 1'b0;
paddr = 12;
pwdata = 35;
@(posedge pclk);
penable = 1'b1;
@(posedge pready);
psel = 0;
penable = 0;
@(posedge pclk);
psel = 1;
pwrite = 1;
paddr = 45;
pwdata = 35;
@(posedge pclk);
penable = 1;
@(posedge pready);
psel = 0;
penable = 0;
@(posedge pclk);
psel = 1;
pwrite = 0;
paddr = 45;
pwdata = 35;
@(posedge pclk);
penable = 1;
@(posedge pready);
@(posedge pclk);
$stop();
end
endmodule
*/
`include "uvm_macros.svh"
import uvm_pkg::*;
////////////////////////////////////////////////////////////////////////////////////
class abp_config extends uvm_object; /////configuration of env
`uvm_object_utils(abp_config)
function new(string name = "abp_config");
super.new(name);
endfunction
uvm_active_passive_enum is_active = UVM_ACTIVE;
endclass
///////////////////////////////////////////////////////
typedef enum bit [1:0] {readd = 0, writed = 1, rst = 2} oper_mode;
//////////////////////////////////////////////////////////////////////////////////
class transaction extends uvm_sequence_item;
rand oper_mode op;
rand logic PWRITE;
rand logic [31 : 0] PWDATA;
rand logic [31 : 0] PADDR;
// Output Signals of DUT for APB UART's transaction
logic PREADY;
logic PSLVERR;
logic [31: 0] PRDATA;
`uvm_object_utils_begin(transaction)
`uvm_field_int (PWRITE,UVM_ALL_ON)
`uvm_field_int (PWDATA,UVM_ALL_ON)
`uvm_field_int (PADDR,UVM_ALL_ON)
`uvm_field_int (PREADY,UVM_ALL_ON)
`uvm_field_int (PSLVERR,UVM_ALL_ON)
`uvm_field_int (PRDATA,UVM_ALL_ON)
`uvm_field_enum(oper_mode, op, UVM_DEFAULT)
`uvm_object_utils_end
constraint addr_c { PADDR <= 31; }
constraint addr_c_err { PADDR > 31; }
function new(string name = "transaction");
super.new(name);
endfunction
endclass : transaction
///////////////////////////////////////////////////////////////
/*
module tb;
transaction tr;
initial begin
tr = transaction::type_id::create("tr");
tr.randomize();
tr.print();
end
endmodule
*/
//////////////////////////////////////////////////////////////////
///////////////////write seq
class write_data extends uvm_sequence#(transaction);
`uvm_object_utils(write_data)
transaction tr;
function new(string name = "write_data");
super.new(name);
endfunction
virtual task body();
repeat(15)
begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(1);//enable
tr.addr_c_err.constraint_mode(0);//disable
start_item(tr);
assert(tr.randomize);
tr.op = writed;
finish_item(tr);
end
endtask
endclass
//////////////////////////////////////////////////////////
////////////////////////read seq
class read_data extends uvm_sequence#(transaction);
`uvm_object_utils(read_data)
transaction tr;
function new(string name = "read_data");
super.new(name);
endfunction
virtual task body();
repeat(15)
begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(1);
tr.addr_c_err.constraint_mode(0);//disable
start_item(tr);
assert(tr.randomize);
tr.op = readd;
finish_item(tr);
end
endtask
endclass
/////////////////////////////////////////////
class write_read extends uvm_sequence#(transaction); //////read after write
`uvm_object_utils(write_read)
transaction tr;
function new(string name = "write_read");
super.new(name);
endfunction
virtual task body();
repeat(15)
begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(1);
tr.addr_c_err.constraint_mode(0);
start_item(tr);
assert(tr.randomize);
tr.op = writed;
finish_item(tr);
start_item(tr);
assert(tr.randomize);
tr.op = readd;
finish_item(tr);
end
endtask
endclass
///////////////////////////////////////////////////////
///////////////write bulk read bulk
class writeb_readb extends uvm_sequence#(transaction);
`uvm_object_utils(writeb_readb)
transaction tr;
function new(string name = "writeb_readb");
super.new(name);
endfunction
virtual task body();
repeat(15) begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(1);
tr.addr_c_err.constraint_mode(0);
start_item(tr);
assert(tr.randomize);
tr.op = writed;
finish_item(tr);
end
repeat(15) begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(1);
tr.addr_c_err.constraint_mode(0);
start_item(tr);
assert(tr.randomize);
tr.op = readd;
finish_item(tr);
end
endtask
endclass
/////////////////////////////////////////////////////////////////
//////////////////////slv_error_write
class write_err extends uvm_sequence#(transaction);
`uvm_object_utils(write_err)
transaction tr;
function new(string name = "write_err");
super.new(name);
endfunction
virtual task body();
repeat(15)
begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(0);
tr.addr_c_err.constraint_mode(1);
start_item(tr);
assert(tr.randomize);
tr.op = writed;
finish_item(tr);
end
endtask
endclass
///////////////////////////////////////////////////////////////
/////////////////////////read err
class read_err extends uvm_sequence#(transaction);
`uvm_object_utils(read_err)
transaction tr;
function new(string name = "read_err");
super.new(name);
endfunction
virtual task body();
repeat(15)
begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(0);
tr.addr_c_err.constraint_mode(1);
start_item(tr);
assert(tr.randomize);
tr.op = readd;
finish_item(tr);
end
endtask
endclass
///////////////////////////////////////////////////////////////
class reset_dut extends uvm_sequence#(transaction);
`uvm_object_utils(reset_dut)
transaction tr;
function new(string name = "reset_dut");
super.new(name);
endfunction
virtual task body();
repeat(15)
begin
tr = transaction::type_id::create("tr");
tr.addr_c.constraint_mode(1);
tr.addr_c_err.constraint_mode(0);
start_item(tr);
assert(tr.randomize);
tr.op = rst;
finish_item(tr);
end
endtask
endclass
////////////////////////////////////////////////////////////
class driver extends uvm_driver #(transaction);
`uvm_component_utils(driver)
virtual apb_if vif;
transaction tr;
function new(input string path = "drv", uvm_component parent = null);
super.new(path,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
tr = transaction::type_id::create("tr");
if(!uvm_config_db#(virtual apb_if)::get(this,"","vif",vif))//uvm_test_top.env.agent.drv.aif
`uvm_error("drv","Unable to access Interface");
endfunction
task reset_dut();
repeat(5)
begin
vif.presetn <= 1'b0;
vif.paddr <= 'h0;
vif.pwdata <= 'h0;
vif.pwrite <= 'b0;
vif.psel <= 'b0;
vif.penable <= 'b0;
`uvm_info("DRV", "System Reset : Start of Simulation", UVM_MEDIUM);
@(posedge vif.pclk);
end
endtask
task drive();
reset_dut();
forever begin
seq_item_port.get_next_item(tr);
if(tr.op == rst)
begin
vif.presetn <= 1'b0;
vif.paddr <= 'h0;
vif.pwdata <= 'h0;
vif.pwrite <= 'b0;
vif.psel <= 'b0;
vif.penable <= 'b0;
@(posedge vif.pclk);
end
else if(tr.op == writed)
begin
vif.psel <= 1'b1;
vif.paddr <= tr.PADDR;
vif.pwdata <= tr.PWDATA;
vif.presetn <= 1'b1;
vif.pwrite <= 1'b1;
@(posedge vif.pclk);
vif.penable <= 1'b1;
`uvm_info("DRV", $sformatf("mode:%0s, addr:%0d, wdata:%0d, rdata:%0d,
slverr:%0d",tr.op.name(),tr.PADDR,tr.PWDATA,tr.PRDATA,tr.PSLVERR), UVM_NONE);
@(negedge vif.pready);
vif.penable <= 1'b0;
tr.PSLVERR = vif.pslverr;
end
else if(tr.op == readd)
begin
vif.psel <= 1'b1;
vif.paddr <= tr.PADDR;
vif.presetn <= 1'b1;
vif.pwrite <= 1'b0;
@(posedge vif.pclk);
vif.penable <= 1'b1;
`uvm_info("DRV", $sformatf("mode:%0s, addr:%0d, wdata:%0d, rdata:%0d,
slverr:%0d",tr.op.name(),tr.PADDR,tr.PWDATA,tr.PRDATA,tr.PSLVERR), UVM_NONE);
@(negedge vif.pready);
vif.penable <= 1'b0;
tr.PRDATA = vif.prdata;
tr.PSLVERR = vif.pslverr;
end
seq_item_port.item_done();
end
endtask
virtual task run_phase(uvm_phase phase);
drive();
endtask
endclass
//////////////////////////////////////////////////////////////////
class mon extends uvm_monitor;
`uvm_component_utils(mon)
uvm_analysis_port#(transaction) send;
transaction tr;
virtual apb_if vif;
function new(input string inst = "mon", uvm_component parent = null);
super.new(inst,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
tr = transaction::type_id::create("tr");
send = new("send", this);
if(!uvm_config_db#(virtual apb_if)::get(this,"","vif",vif))//uvm_test_top.env.agent.drv.aif
`uvm_error("MON","Unable to access Interface");
endfunction
virtual task run_phase(uvm_phase phase);
forever begin
@(posedge vif.pclk);
if(!vif.presetn)
begin
tr.op = rst;
`uvm_info("MON", "SYSTEM RESET DETECTED", UVM_NONE);
send.write(tr);
end
else if (vif.presetn && vif.pwrite)
begin
@(negedge vif.pready);
tr.op = writed;
tr.PWDATA = vif.pwdata;
tr.PADDR = vif.paddr;
tr.PSLVERR = vif.pslverr;
`uvm_info("MON", $sformatf("DATA WRITE addr:%0d data:%0d
slverr:%0d",tr.PADDR,tr.PWDATA,tr.PSLVERR), UVM_NONE);
send.write(tr);
end
else if (vif.presetn && !vif.pwrite)
begin
@(negedge vif.pready);
tr.op = readd;
tr.PADDR = vif.paddr;
tr.PRDATA = vif.prdata;
tr.PSLVERR = vif.pslverr;
`uvm_info("MON", $sformatf("DATA READ addr:%0d data:%0d slverr:%0d",tr.PADDR,
tr.PRDATA,tr.PSLVERR), UVM_NONE);
send.write(tr);
end
end
endtask
endclass
/////////////////////////////////////////////////////////////////////
class sco extends uvm_scoreboard;
`uvm_component_utils(sco)
uvm_analysis_imp#(transaction,sco) recv;
bit [31:0] arr[32] = '{default:0};
bit [31:0] addr = 0;
bit [31:0] data_rd = 0;
function new(input string inst = "sco", uvm_component parent = null);
super.new(inst,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
recv = new("recv", this);
endfunction
virtual function void write(transaction tr);
if(tr.op == rst)
begin
`uvm_info("SCO", "SYSTEM RESET DETECTED", UVM_NONE);
end
else if (tr.op == writed)
begin
if(tr.PSLVERR == 1'b1)
begin
`uvm_info("SCO", "SLV ERROR during WRITE OP", UVM_NONE);
end
else
begin
arr[tr.PADDR] = tr.PWDATA;
`uvm_info("SCO", $sformatf("DATA WRITE OP addr:%0d, wdata:%0d
arr_wr:%0d",tr.PADDR,tr.PWDATA, arr[tr.PADDR]), UVM_NONE);
end
end
else if (tr.op == readd)
begin
if(tr.PSLVERR == 1'b1)
begin
`uvm_info("SCO", "SLV ERROR during READ OP", UVM_NONE);
end
else
begin
data_rd = arr[tr.PADDR];
if (data_rd == tr.PRDATA)
`uvm_info("SCO", $sformatf("DATA MATCHED : addr:%0d,
rdata:%0d",tr.PADDR,tr.PRDATA), UVM_NONE)
else
`uvm_info("SCO",$sformatf("TEST FAILED : addr:%0d, rdata:%0d
data_rd_arr:%0d",tr.PADDR,tr.PRDATA,data_rd), UVM_NONE)
end
end
$display("----------------------------------------------------------------");
endfunction
endclass
/////////////////////////////////////////////////////////////////////
class agent extends uvm_agent;
`uvm_component_utils(agent)
abp_config cfg;
function new(input string inst = "agent", uvm_component parent = null);
super.new(inst,parent);
endfunction
driver d;
uvm_sequencer#(transaction) seqr;
mon m;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
cfg = abp_config::type_id::create("cfg");
m = mon::type_id::create("m",this);
if(cfg.is_active == UVM_ACTIVE)
begin
d = driver::type_id::create("d",this);
seqr = uvm_sequencer#(transaction)::type_id::create("seqr", this);
end
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(cfg.is_active == UVM_ACTIVE) begin
d.seq_item_port.connect(seqr.seq_item_export);
end
endfunction
endclass
//////////////////////////////////////////////////////////////////////////////////
class env extends uvm_env;
`uvm_component_utils(env)
function new(input string inst = "env", uvm_component c);
super.new(inst,c);
endfunction
agent a;
sco s;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
a = agent::type_id::create("a",this);
s = sco::type_id::create("s", this);
endfunction
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
a.m.send.connect(s.recv);
endfunction
endclass
//////////////////////////////////////////////////////////////////////////
class test extends uvm_test;
`uvm_component_utils(test)
function new(input string inst = "test", uvm_component c);
super.new(inst,c);
endfunction
env e;
write_read wrrd;
writeb_readb wrrdb;
write_data wdata;
read_data rdata;
write_err werr;
read_err rerr;
reset_dut rstdut;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
e = env::type_id::create("env",this);
wrrd = write_read::type_id::create("wrrd");
wdata = write_data::type_id::create("wdata");
rdata = read_data::type_id::create("rdata");
wrrdb = writeb_readb::type_id::create("wrrdb");
werr = write_err::type_id::create("werr");
rerr = read_err::type_id::create("rerr");
rstdut = reset_dut::type_id::create("rstdut");
endfunction
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this);
wrrdb.start(e.a.seqr);
#20;
phase.drop_objection(this);
endtask
endclass
//////////////////////////////////////////////////////////////////////
module tb;
apb_if vif();
apb_ram dut (.presetn(vif.presetn), .pclk(vif.pclk), .psel(vif.psel), .penable(vif.penable),
.pwrite(vif.pwrite), .paddr(vif.paddr), .pwdata(vif.pwdata), .prdata(vif.prdata),
.pready(vif.pready), .pslverr(vif.pslverr));
initial begin
vif.pclk <= 0;
end
always #10 vif.pclk <= ~vif.pclk;
initial begin
uvm_config_db#(virtual apb_if)::set(null, "*", "vif", vif);
run_test("test");
end
initial begin
$dumpfile("dump.vcd");
$dumpvars;
end
endmodule