APB VERIFICATION USING UVM
agent.sv
`ifndef APB_AGENT_SV
`define APB_AGENT_SV
class apb_agent extends uvm_agent;
`uvm_component_utils(apb_agent)
// Components inside the agent
apb_sequencer sequencer;
apb_driver driver;
apb_monitor monitor;
// Is the agent active or passive?
uvm_active_passive_enum is_active;
// Constructor
function new(string name = "apb_agent", uvm_component parent);
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// Get agent activity from config (default to ACTIVE)
if (!uvm_config_db#(uvm_active_passive_enum)::get(this, "", "is_active", is_active))
is_active = UVM_ACTIVE;
monitor = apb_monitor::type_id::create("monitor", this);
if (is_active == UVM_ACTIVE) begin
sequencer = apb_sequencer::type_id::create("sequencer", this);
driver = apb_driver::type_id::create("driver", this);
end
endfunction
// Connect phase: hook up sequencer and driver
function void connect_phase(uvm_phase phase);
if (is_active == UVM_ACTIVE) begin
driver.seq_item_port.connect(sequencer.seq_item_export);
end
endfunction
endclass
`endif // APB_AGENT_SV
assertions.sv
module check (
input logic clk,
input logic reset,
input logic pwrite,
input logic [4:0] paddr,
input logic [31:0] pwdata,
input logic [31:0] prdata,
input logic pready,
input logic pslverr
);
property p1;
@(posedge clk) disable iff (reset)
!$isunknown(prdata);
endproperty
P1: assert property(p1) else $error("P1 FAILED: prdata is unknown");
property p2;
@(posedge clk) disable iff (reset)
pwrite |-> !$isunknown(pwdata);
endproperty
P2: assert property(p2) else $error("P2 FAILED: prdata unknown during read");
property p3;
@(posedge clk) disable iff (reset)
(pwrite == 1) |-> !$isunknown(pwdata);
endproperty
P3: assert property(p3) else $error("P3 FAILED: pwdata unknown during write");
endmodule
coverage.sv
//coverage class
class coverage extends uvm_component;
`uvm_component_utils(coverage)
apb_seq_item tx2;
uvm_analysis_imp#(apb_seq_item,coverage)cov_export;
covergroup cg;
pwdata:coverpoint tx2.pwdata{
bins low = {[0:80]};
bins middle = {[80:160]};
bins high = {[160:255]};
}
paddr:coverpoint tx2.paddr {
bins range = {[0:31]};
}
pwrite:coverpoint tx2.pwrite;
endgroup
function new(string name="coverage",uvm_component parent);
super.new(name,parent);
cov_export = new("cov_export",this);
cg = new();
endfunction
function void write(apb_seq_item t1);
tx2 = t1;
cg.sample();
endfunction
function void report_phase(uvm_phase phase);
`uvm_info(get_type_name(), $sformatf("Coverage = %0.2f%%", cg.get_coverage()),
UVM_LOW)
endfunction
endclass
driver.sv
`ifndef APB_DRIVER_SV
`define APB_DRIVER_SV
class apb_driver extends uvm_driver #(apb_seq_item);
`uvm_component_utils(apb_driver)
virtual apb_if vif; // Virtual interface handle
apb_seq_item tx; // Transaction object
// Constructor
function new(string name = "apb_driver", uvm_component parent);
super.new(name, parent);
endfunction
// Build phase: get the virtual interface
function void build_phase(uvm_phase phase);
super.build_phase(phase);
tx = apb_seq_item::type_id::create("tx");
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif)) begin
`uvm_fatal("DRV", "Virtual interface not set")
end
endfunction
// Main driver run phase
task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(tx); // Get transaction from sequencer
`uvm_info("DRV", $sformatf("Driving TX: write=%0b, addr=0x%0h, data=0x%0h",
tx.pwrite, tx.paddr, tx.pwdata), UVM_MEDIUM)
// Setup phase
@(posedge vif.pclk);
vif.psel <= 1;
vif.pwrite <= tx.pwrite;
vif.paddr <= tx.paddr;
vif.pwdata <= tx.pwdata;
vif.penable <= 0;
// Access phase
@(posedge vif.pclk);
vif.penable <= 1;
// Wait for pready
wait (vif.pready == 1);
// Capture read data if it's a read
if (!tx.pwrite) begin
tx.prdata = vif.prdata;
end
tx.pready = vif.pready;
tx.pslverr = vif.pslverr;
// Clear signals
@(posedge vif.pclk);
vif.psel <= 0;
vif.penable <= 0;
vif.pwrite <= 0;
seq_item_port.item_done(); // Complete the transaction
end
endtask
endclass
`endif // APB_DRIVER_SV
dut.sv
module apb_dut (
input logic pclk,
input logic presetn,
input logic psel,
input logic penable,
input logic pwrite,
input logic [4:0] paddr,
input logic [31:0] pwdata,
output logic [31:0] prdata,
output logic pready,
output logic pslverr
);
// Internal memory: 32 locations of 32-bit width
logic [31:0] mem [31:0];
// Default values
assign pready = 1'b1;
assign pslverr = 1'b0;
always_ff @(posedge pclk or negedge presetn) begin
if (!presetn) begin
prdata <= 0;
end
else if (psel && penable) begin
if (pwrite) begin
mem[paddr] <= pwdata;
end else begin
prdata <= mem[paddr];
end
end
end
endmodule
env.sv
`ifndef APB_ENV_SV
`define APB_ENV_SV
class apb_env extends uvm_env;
`uvm_component_utils(apb_env)
// Components in the environment
apb_agent agent;
apb_scoreboard scoreboard;
coverage c1;
// Constructor
function new(string name = "apb_env", uvm_component parent);
super.new(name, parent);
endfunction
// Build phase: create agent and scoreboard
function void build_phase(uvm_phase phase);
super.build_phase(phase);
agent = apb_agent::type_id::create("agent", this);
scoreboard = apb_scoreboard::type_id::create("scoreboard", this);
c1 = coverage::type_id::create("c1",this);
// Set agent as active (driver+sequencer will be created)
uvm_config_db#(uvm_active_passive_enum)::set(this, "agent", "is_active", UVM_ACTIVE);
endfunction
// Connect monitor to scoreboard
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
agent.monitor.ap.connect(scoreboard.item_collected_export);
agent.monitor.ap.connect(c1.cov_export);
endfunction
endclass
`endif // APB_ENV_SV
interface.sv
`ifndef APB_IF_SV
`define APB_IF_SV
interface apb_if(
input logic pclk,
input logic presetn
);
// APB Protocol Signals
logic psel;
logic penable;
logic pwrite;
logic [4:0] paddr;
logic [31:0] pwdata;
logic [31:0] prdata;
logic pready;
logic pslverr;
// Clocking block for driver (active side)
clocking drv_cb @(posedge pclk);
output psel, penable, pwrite, paddr, pwdata;
input prdata, pready, pslverr;
endclocking
// Clocking block for monitor (passive side)
clocking mon_cb @(posedge pclk);
input psel, penable, pwrite, paddr, pwdata, prdata, pready, pslverr;
endclocking
// Modport declarations
modport DRIVER (clocking drv_cb, input pclk, input presetn);
modport MONITOR (clocking mon_cb, input pclk, input presetn);
endinterface
`endif // APB_IF_SV
monitor.sv
`ifndef APB_MONITOR_SV
`define APB_MONITOR_SV
class apb_monitor extends uvm_monitor;
`uvm_component_utils(apb_monitor)
uvm_analysis_port #(apb_seq_item) ap;
virtual apb_if vif;
function new(string name = "apb_monitor", uvm_component parent);
super.new(name, parent);
ap = new("ap", this);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
`uvm_fatal("MON", "Virtual interface not found")
endfunction
task run_phase(uvm_phase phase);
forever begin
@(posedge vif.pclk);
if (vif.psel && vif.penable && vif.pready) begin
apb_seq_item tx = apb_seq_item::type_id::create("tx");
tx.paddr = vif.paddr;
tx.pwrite = vif.pwrite;
if (vif.pwrite) begin
tx.pwdata = vif.pwdata;
`uvm_info("MONW", $sformatf("WRITE addr=0x%0h, pwdata=0x%0h", tx.paddr,
tx.pwdata), UVM_MEDIUM)
ap.write(tx);
end
else begin
// Delay to next clock cycle for valid prdata
@(posedge vif.pclk);
tx.prdata = vif.prdata;
`uvm_info("MONR", $sformatf("READ addr=0x%0h, prdata=0x%0h", tx.paddr,
tx.prdata), UVM_MEDIUM)
ap.write(tx);
end
end
end
endtask
endclass
`endif // APB_MONITOR_SV
scoreboard.sv
`ifndef APB_SCOREBOARD_SV
`define APB_SCOREBOARD_SV
class apb_scoreboard extends uvm_scoreboard;
`uvm_component_utils(apb_scoreboard)
// Analysis export or port (receives data from monitor)
uvm_analysis_imp #(apb_seq_item, apb_scoreboard) item_collected_export;
// Local memory model
bit [31:0] mem [31:0]; // 32 locations, 32-bit wide
// Constructor
function new(string name = "apb_scoreboard", uvm_component parent);
super.new(name, parent);
item_collected_export = new("item_collected_export", this);
endfunction
// Write function is automatically called when monitor sends transaction
function void write(apb_seq_item tx);
if (tx.pwrite) begin
// Write to reference memory
mem[tx.paddr] = tx.pwdata;
`uvm_info("SCOREBOARD", $sformatf("WRITE: addr=0x%0h data=0x%0h",
tx.paddr, tx.pwdata), UVM_LOW)
end else begin
// Read from reference memory and compare
bit [31:0] expected = mem[tx.paddr];
if (tx.prdata !== expected) begin
`uvm_error("SCOREBOARD", $sformatf("READ MISMATCH: addr=0x%0h
Expected=0x%0h Got=0x%0h",
tx.paddr, expected, tx.prdata))
end else begin
`uvm_info("SCOREBOARD", $sformatf("READ OK: addr=0x%0h data=0x%0h",
tx.paddr, tx.prdata), UVM_LOW)
end
end
endfunction
endclass
`endif // APB_SCOREBOARD_SV
seq.sv
`ifndef APB_SEQUENCE_SV
`define APB_SEQUENCE_SV
class apb_sequence extends uvm_sequence #(apb_seq_item);
`uvm_object_utils(apb_sequence)
function new(string name = "apb_sequence");
super.new(name);
endfunction
virtual task body();
apb_seq_item tx;
bit [4:0] addr_arr[5]; // Array to store written addresses
// ----------------------------
// WRITE Phase (5 transactions)
// ----------------------------
for (int i = 0; i < 5; i++) begin
tx = apb_seq_item::type_id::create("tx");
start_item(tx);
assert(tx.randomize() with { pwrite == 1; });
finish_item(tx);
addr_arr[i] = tx.paddr; // store address for later read
`uvm_info("SEQ", $sformatf("WRITE TX[%0d]: addr=0x%0h data=0x%0h", i, tx.paddr,
tx.pwdata), UVM_MEDIUM)
end
// ----------------------------
// READ Phase (reuse same addresses)
// ----------------------------
for (int i = 0; i < 5; i++) begin
tx = apb_seq_item::type_id::create("tx");
start_item(tx);
assert(tx.randomize() with { pwrite == 0; paddr == addr_arr[i]; });
finish_item(tx);
`uvm_info("SEQ", $sformatf("READ TX[%0d]: addr=0x%0h", i, tx.paddr), UVM_MEDIUM)
end
endtask
endclass
`endif // APB_SEQUENCE_SV
seqr.sv
`ifndef APB_SEQUENCER_SV
`define APB_SEQUENCER_SV
class apb_sequencer extends uvm_sequencer #(apb_seq_item);
// Register this sequencer with the factory
`uvm_component_utils(apb_sequencer)
// Constructor
function new(string name = "apb_sequencer", uvm_component parent);
super.new(name, parent);
endfunction
endclass
`endif // APB_SEQUENCER_SV
seq_item.sv
`ifndef APB_SEQ_ITEM_SV
`define APB_SEQ_ITEM_SV
class apb_seq_item extends uvm_sequence_item;
// UVM macro for object creation and type identification
`uvm_object_utils(apb_seq_item)
// APB fields
rand bit pwrite; // Write = 1, Read = 0
rand bit [4:0] paddr; // 5-bit address
rand bit [31:0] pwdata; // Data to be written
bit [31:0] prdata; // Data read from DUT
bit pready; // Ready signal
bit pslverr; // Slave error
// Constraint to keep address within 0 to 31
constraint addr_c { paddr inside {[0:31]}; }
constraint wdats_c {pwdata inside {[0:255]};}
// Constructor
function new(string name = "apb_seq_item");
super.new(name);
endfunction
// Print function for debugging
function void do_print (uvm_printer printer);
super.do_print(printer);
printer.print_field("pwrite", pwrite, 1);
printer.print_field("paddr", paddr, 5);
printer.print_field("pwdata", pwdata, 32);
printer.print_field("prdata", prdata, 32);
printer.print_field("pready", pready, 1);
printer.print_field("pslverr", pslverr, 1);
endfunction
endclass
`endif // APB_SEQ_ITEM_SV
test.sv
`ifndef APB_TEST_SV
`define APB_TEST_SV
class apb_test extends uvm_test;
`uvm_component_utils(apb_test)
// Environment handle
apb_env env;
// Constructor
function new(string name = "apb_test", uvm_component parent);
super.new(name, parent);
endfunction
// Build phase: create environment
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env = apb_env::type_id::create("env", this);
endfunction
// Run phase: start the sequence
task run_phase(uvm_phase phase);
apb_sequence seq;
phase.raise_objection(this); // Start of test
// Create and start sequence
seq = apb_sequence::type_id::create("seq");
seq.start(env.agent.sequencer); // Start on the agent's sequencer
#20;
phase.drop_objection(this); // End of test
endtask
endclass
`endif // APB_TEST_SV
top.sv
`include "uvm_macros.svh"
import uvm_pkg::*;
// Include all design and TB files
`include "interface.sv"
`include "seq_item.sv"
`include "seq.sv"
`include "seqr.sv"
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
`include "agent.sv"
`include "coverage.sv"
`include "env.sv"
`include "test.sv"
`include "dut.sv"
`include "assertions.sv"
module tb_top;
// Clock and Reset
logic pclk;
logic presetn;
// Generate clock (10ns period)
initial pclk = 0;
always #5 pclk = ~pclk;
// Reset logic
initial begin
presetn = 0;
#20;
presetn = 1;
end
// Instantiate APB interface
apb_if apb_if_inst(pclk, presetn);
// DUT instantiation and connection
apb_dut dut (
.pclk (pclk),
.presetn (presetn),
.psel (apb_if_inst.psel),
.penable (apb_if_inst.penable),
.pwrite (apb_if_inst.pwrite),
.paddr (apb_if_inst.paddr),
.pwdata (apb_if_inst.pwdata),
.prdata (apb_if_inst.prdata),
.pready (apb_if_inst.pready),
.pslverr (apb_if_inst.pslverr)
);
bind apb_dut check A1
(.clk(apb_if_inst.pclk),.reset(apb_if_inst.presetn),.pwdata(apb_if_inst.pwdata),.paddr(apb_if_
inst.paddr),.pwrite(apb_if_inst.pwrite),.pslverr(apb_if_inst.pslverr),.pready(apb_if_inst.prea
dy),.prdata(apb_if_inst.prdata));
// Connect interface to UVM via config DB
initial begin
uvm_config_db#(virtual apb_if)::set(null, "*", "vif", apb_if_inst);
run_test("apb_test");
end
endmodule
Waveform: