0% found this document useful (0 votes)
132 views16 pages

APB Protocol Verification Using UVM

The document outlines the implementation of an APB verification environment using UVM, detailing various components such as agents, drivers, monitors, and scoreboards. It includes code snippets for each component, demonstrating their roles in transaction handling, assertion checking, and coverage analysis. The design emphasizes a structured approach to verify APB protocol interactions within a testbench framework.

Uploaded by

cplprethi123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
132 views16 pages

APB Protocol Verification Using UVM

The document outlines the implementation of an APB verification environment using UVM, detailing various components such as agents, drivers, monitors, and scoreboards. It includes code snippets for each component, demonstrating their roles in transaction handling, assertion checking, and coverage analysis. The design emphasizes a structured approach to verify APB protocol interactions within a testbench framework.

Uploaded by

cplprethi123
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

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:

You might also like