100% found this document useful (1 vote)
309 views14 pages

Normal Adder UVM Verification Full Document

This document describes the process of verifying a 1-bit adder design using UVM (Universal Verification Methodology). It defines the SystemVerilog interface, DUT module, transaction class, sequence, sequencer, driver, monitor, and testbench components required for UVM verification. The monitor samples inputs and outputs of the DUT and sends transactions to the analysis port. The driver drives input transactions to the DUT from the sequencer. The testbench brings together the DUT, interfaces, and UVM components.

Uploaded by

anjimedidi2610
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
100% found this document useful (1 vote)
309 views14 pages

Normal Adder UVM Verification Full Document

This document describes the process of verifying a 1-bit adder design using UVM (Universal Verification Methodology). It defines the SystemVerilog interface, DUT module, transaction class, sequence, sequencer, driver, monitor, and testbench components required for UVM verification. The monitor samples inputs and outputs of the DUT and sends transactions to the analysis port. The driver drives input transactions to the DUT from the sequencer. The testbench brings together the DUT, interfaces, and UVM components.

Uploaded by

anjimedidi2610
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/ 14

Adder

Verification
using
Normal Adder UVM verification
Let’s take adder of the following specification:

it is a one-bit adder, one-bit result, one-bit carry

So, system Verilog interface port definition will look like,


// Define an interface named dut_if1
interface dut_if1;

// Input signals
logic a_in;
logic b_in;
logic c_in;

// Output signals
logic sum_out;
logic carry_out;

// Control signals
logic clock;
logic reset;

endinterface // End of interface dut_if1

So, let’s go through the definition of each component

So, let's design DUT first


// Define a module named normal_adder with an interface dut_if1 as an input
argument
module normal_adder (dut_if1 dut_if);

// Include UVM macros for future use


`include "uvm_macros.svh"

// Combinational logic for the normal adder


always_comb begin
// If reset signal is asserted
if (dut_if.reset) begin
// Reset the sum and carry_out signals to 0
dut_if.sum_out = 0;
dut_if.carry_out = 0;
end
// If not in reset state
else begin
// Calculate sum using XOR operation
dut_if.sum_out = (dut_if.a_in ^ dut_if.b_in ^ dut_if.c_in);

// Calculate carry_out using bitwise operations


dut_if.carry_out = ((dut_if.a_in & dut_if.b_in) | (dut_if.b_in &
dut_if.c_in) | (dut_if.c_in & dut_if.a_in));
end

// Display the values for debugging or monitoring purposes


$display("The value of a_in=%0d, b_in=%0d, c_in=%0d, Sum=%0d, Carry=%0d",
dut_if.a_in, dut_if.b_in, dut_if.c_in, dut_if.sum_out,
dut_if.carry_out);
end

endmodule // End of module normal_adder

So, let’s Define basic transaction:

So in the adder, there could be any random bit in input operand, hence input a_in, b_in,c_inare
randomized

So the file will look like below

// Define a UVM sequence item class named normal_adder_txn


class normal_adder_txn extends uvm_sequence_item;

// Register the class with UVM factory


`uvm_object_utils(normal_adder_txn)

// Constructor for the class


function new(string name = "normal_adder_txn");
// Call the base class constructor
super.new(name);
endfunction
// Random variables for the transaction
rand bit a_in;
rand bit b_in;
rand bit c_in;
bit sum_out;
bit carry_out;

// Macros for additional UVM features (commented out, but you can enable if
needed)
/* `uvm_object_utils_begin(normal_adder_txn)
`uvm_field_int(a_in, UVM_ALL_ON)
`uvm_field_int(b_in, UVM_ALL_ON)
`uvm_field_int(c_in, UVM_ALL_ON)
`uvm_field_int(sum_out, UVM_ALL_ON)
`uvm_field_int(carry_out, UVM_ALL_ON)
`uvm_object_utils_end */
endclass // End of class normal_adder_txn

Now let’s define the sequence of adder

So in current scenario let the loop of randomization run for 8 times to get all inputcombination ,
hence it will look like below
// Define a UVM sequence class named normal_add_sequence
class normal_add_sequence extends uvm_sequence#(normal_adder_txn);

// Register the class with UVM factory


`uvm_object_utils(normal_add_sequence)

// Constructor for the class


function new(string name = "normal_add_sequence");
// Call the base class constructor
super.new(name);
endfunction

// Virtual task for defining the sequence body


virtual task body();
// Declare an instance of the normal_adder_txn transaction
normal_adder_txn add_txn1;

// Repeat the following block 8 times


repeat (8) begin
// Create a new instance of the normal_adder_txn transaction
add_txn1 = normal_adder_txn::type_id::create("add_txn1");

// Start the transaction item


start_item(add_txn1);

// Randomize the transaction item


assert(add_txn1.randomize());
// Finish the transaction item
finish_item(add_txn1);
end
endtask
endclass // End of class normal_add_sequence
Now Let’s Define Sequencer

// Include the sequence file named "normal_adder_seq.sv"


`include "normal_adder_seq.sv"

// Define a UVM sequencer class named normal_add_sequencer


class normal_add_sequencer extends uvm_sequencer#(normal_adder_txn);

// Register the class with UVM factory


`uvm_component_utils(normal_add_sequencer)

// Constructor for the class


function new(string name = "normal_add_sequencer", uvm_component parent);
// Call the base class constructor
super.new(name, parent);
endfunction: new

endclass: normal_add_sequencer

Now Let's Define the top of Test Bench

// Include the UVM macros file


`include "uvm_macros.svh"

// Include the test file named "normal_add_test.sv"


`include "normal_add_test.sv"

// Define the testbench module


module testbench;

// Import the UVM package


import uvm_pkg::*;

// Instantiate the interface dut_if1


dut_if1 intf1();

// Instantiate the DUT (Design Under Test) normal_adder, connecting it to


the interface
normal_adder dut_here(.dut_if(intf1));

// UVM Configuration: Set the interface instance as the virtual interface


for the DUT
initial begin
uvm_config_db#(virtual dut_if1)::set(null, "*", "dut_vif", intf1);
run_test("normal_add_test");
end

// Initialize clock and reset signals


initial begin
intf1.clock = 1;
intf1.reset = 1;
end
// Toggle clock signal every 5 time units
initial begin
forever #5 intf1.clock = ~intf1.clock;
end

// Dump VCD file for waveform analysis


initial begin
$dumpfile("dump.vcd");
$dumpvars(0, normal_adder);
end

endmodule : testbench

Now lets Define Driver


// Define a UVM driver class named normal_add_driver
class normal_add_driver extends uvm_driver#(normal_adder_txn);

// Register the class with UVM factory


`uvm_component_utils(normal_add_driver)

// Declare a virtual interface instance for communication with the DUT


virtual dut_if1 dut_vif;

// Constructor for the class


function new(string name = "normal_add_driver", uvm_component parent);
// Call the base class constructor
super.new(name, parent);
endfunction

// Build phase: Set up the virtual interface connection


function void build_phase(uvm_phase phase);
// Call the base class build_phase
super.build_phase(phase);

// Attempt to get the virtual interface from the UVM configuration


database
if (!uvm_config_db#(virtual dut_if1)::get(this, "", "dut_vif", dut_vif))
`uvm_error("", "uvm_config_db::get failed")
endfunction : build_phase

// Run phase: Drive transactions to the DUT


task run_phase(uvm_phase phase);
// Declare a transaction item variable
normal_adder_txn add_txn;

// Assert and deassert reset signal


dut_vif.reset = 1;
#5 dut_vif.reset = 0;

// Forever loop to handle incoming sequence items


forever begin
// Get the next transaction item from the sequence
seq_item_port.get_next_item(add_txn);
// Drive input signals of the DUT with values from the transaction item
dut_vif.a_in = add_txn.a_in;
dut_vif.b_in = add_txn.b_in;
dut_vif.c_in = add_txn.c_in;

// Notify the sequencer that the item has been processed


seq_item_port.item_done();
end
endtask

endclass : normal_add_driver

Now lets Define Monitor

// Define a UVM monitor class named normal_add_monitor_before


class normal_add_monitor_before extends uvm_monitor;

// Register the class with UVM factory


`uvm_component_utils(normal_add_monitor_before)

// Declare an analysis port for sending transactions to the analysis


component
uvm_analysis_port#(normal_adder_txn) mon_a_port_before;

// Declare a transaction item variable


normal_adder_txn add_tx_mon;

// Declare a virtual interface instance for communication with the DUT


virtual dut_if1 dut_vif;

// Constructor for the class


function new(string name = "normal_add_monitor_before", uvm_component
parent);
// Call the base class constructor
super.new(name, parent);
endfunction : new

// Build phase: Set up the virtual interface connection and analysis port
function void build_phase(uvm_phase phase);
// Call the base class build_phase
super.build_phase(phase);

// Create an analysis port for sending transactions


mon_a_port_before = new("mon_a_port_before", this);

// Attempt to get the virtual interface from the UVM configuration


database
if (!uvm_config_db#(virtual dut_if1)::get(this, "", "dut_vif", dut_vif))
begin
`uvm_error("", "uvm_config_db::get failed")
end
endfunction : build_phase
// Run phase: Monitor signals and send transactions to the analysis
component
task run_phase(uvm_phase phase);
// Create an instance of the transaction item
add_tx_mon = normal_adder_txn::type_id::create("add_tx_mon", this);

// Forever loop to monitor signals and send transactions


forever begin
// Wait for a change in signals a_in, b_in, and c_in
@(dut_vif.a_in, dut_vif.b_in, dut_vif.c_in) begin
// Populate the transaction item with signal values
add_tx_mon.a_in = dut_vif.a_in;
add_tx_mon.b_in = dut_vif.b_in;
add_tx_mon.c_in = dut_vif.c_in;
add_tx_mon.sum_out = dut_vif.sum_out;
add_tx_mon.carry_out = dut_vif.carry_out;

// Send the transaction to the analysis component through the


analysis port
mon_a_port_before.write(add_tx_mon);
end
end
endtask : run_phase

endclass : normal_add_monitor_before

///////////////////////////////////////////////////////////////////////////
// This Monitor Calculates the reference result
class normal_add_ref_monitor_after extends uvm_monitor;

// Register the class with UVM factory


`uvm_component_utils(normal_add_ref_monitor_after)

// Declare a virtual interface instance for communication with the DUT


virtual dut_if1 dut_vif;

// Declare an analysis port for sending transactions to the analysis


component
uvm_analysis_port#(normal_adder_txn) ref_mon_a_port_after;

// Declare a transaction item variable


normal_adder_txn add_mon_ref_txn;

// Constructor for the class


function new(string name = "normal_add_ref_monitor_after", uvm_component
parent);
// Call the base class constructor
super.new(name, parent);
endfunction : new

// Build phase: Set up the virtual interface connection and analysis port
function void build_phase(uvm_phase phase);
// Call the base class build_phase
super.build_phase(phase);
// Attempt to get the virtual interface from the UVM configuration
database
if (!uvm_config_db#(virtual dut_if1)::get(this, "", "dut_vif", dut_vif))
begin
`uvm_error("", "uvm_config_db::get failed")
end

// Create an analysis port for sending transactions


ref_mon_a_port_after = new("ref_mon_a_port_after", this);
endfunction : build_phase

// Run phase: Monitor signals, calculate reference result, and send


transactions to the analysis component
task run_phase(uvm_phase phase);
// Create an instance of the transaction item
add_mon_ref_txn = normal_adder_txn::type_id::create("add_mon_ref_txn");

// Forever loop to monitor signals, calculate reference result, and send


transactions
forever begin
// Wait for a change in signals a_in, b_in, and c_in
@(dut_vif.a_in, dut_vif.b_in, dut_vif.c_in) begin
// Populate the transaction item with signal values
add_mon_ref_txn.a_in = dut_vif.a_in;
add_mon_ref_txn.b_in = dut_vif.b_in;
add_mon_ref_txn.c_in = dut_vif.c_in;

// Calculate reference result and update the transaction item


add_mon_ref_add_result();

// Send the transaction to the analysis component through the


analysis port
ref_mon_a_port_after.write(add_mon_ref_txn);
end
end
endtask : run_phase

// Virtual function to calculate the reference result


virtual function void add_mon_ref_add_result();
bit [1:0] sum_res;
// Perform binary addition on a, b, and c
sum_res = add_mon_ref_txn.a_in + add_mon_ref_txn.b_in +
add_mon_ref_txn.c_in;

// Update the transaction item with the calculated sum and carry_out
add_mon_ref_txn.sum_out = sum_res[0];
add_mon_ref_txn.carry_out = sum_res[1];
endfunction : add_mon_ref_add_result

endclass : normal_add_ref_monitor_after
Now let’s Define Agent
// Include the sequencer file named "normal_adder_seqr.sv"
`include "normal_adder_seqr.sv"

// Include the monitor file named "normal_add_monitor.sv"


`include "normal_add_monitor.sv"

// Include the driver file named "normal_adder_driver.sv"


`include "normal_adder_driver.sv"

// Include the sequence file named "normal_add_seqn.sv"


`include "normal_add_seqn.sv"

// Define a UVM agent class named normal_add_agent


class normal_add_agent extends uvm_agent;

// Register the class with UVM factory


`uvm_component_utils(normal_add_agent)

// Declare analysis ports for sending transactions to the analysis


components
uvm_analysis_port#(normal_adder_txn) agent_a_port_before;
uvm_analysis_port#(normal_adder_txn) ref_agent_a_port_after;

// Declare instances of sequencer, monitors, and driver


normal_add_sequencer add_seq;
normal_add_monitor_before add_mon_before;
normal_add_ref_monitor_after add_mon_ref_after;
normal_add_driver add_driv;

// Constructor for the class


function new(string name = "normal_add_agent", uvm_component parent);
// Call the base class constructor
super.new(name, parent);
endfunction : new

// Build phase: Create instances of sequencer, monitors, and driver


function void build_phase(uvm_phase phase);
// Call the base class build_phase
super.build_phase(phase);

// Create an instance of the sequencer


add_seq = normal_add_sequencer::type_id::create("add_seq", this);

// Create instances of monitors


add_mon_before =
normal_add_monitor_before::type_id::create("add_mon_before", this);
add_mon_ref_after =
normal_add_ref_monitor_after::type_id::create("add_mon_ref_after", this);

// Create an instance of the driver


add_driv = normal_add_driver::type_id::create("add_driv", this);

// Create analysis ports for sending transactions


agent_a_port_before = new("agent_a_port_before", this);
ref_agent_a_port_after = new("ref_agent_a_port_after", this);
endfunction : build_phase

// Connect phase: Connect components to each other


function void connect_phase(uvm_phase phase);
// Call the base class connect_phase
super.connect_phase(phase);

// Connect the driver to the sequencer


add_driv.seq_item_port.connect(add_seq.seq_item_export);

// Connect the monitor to the analysis port


add_mon_before.mon_a_port_before.connect(agent_a_port_before);

// Connect the reference monitor to the reference analysis port


add_mon_ref_after.ref_mon_a_port_after.connect(ref_agent_a_port_after);
endfunction : connect_phase

endclass : normal_add_agent

Now let's Define Environment


// Include the agent file named "normal_add_agent.sv"
`include "normal_add_agent.sv"

// Include the scoreboard file named "normal_adder_score.sv"


`include "normal_adder_score.sv"

// Define a UVM environment class named normal_add_env


class normal_add_env extends uvm_env;

// Register the class with UVM factory


`uvm_component_utils(normal_add_env)

// Declare instances of the agent and scoreboard


normal_add_agent agent;
normal_adder_score score_e;

// Constructor for the class


function new(string name = "normal_add_env", uvm_component parent);
// Call the base class constructor
super.new(name, parent);
endfunction : new

// Build phase: Create instances of the agent and scoreboard


function void build_phase(uvm_phase phase);
// Call the base class build_phase
super.build_phase(phase);

// Create an instance of the agent


agent = normal_add_agent::type_id::create("agent", this);

// Create an instance of the scoreboard


score_e = normal_adder_score::type_id::create("score_e", this);
endfunction : build_phase
// Connect phase: Connect components to each other
function void connect_phase(uvm_phase phase);
// Call the base class connect_phase
super.connect_phase(phase);

// Connect the agent's analysis ports to the scoreboard


agent.agent_a_port_before.connect(score_e.add_sc_before);
agent.ref_agent_a_port_after.connect(score_e.ref_add_sc_after);
endfunction : connect_phase

endclass : normal_add_env

Now let’s Define Scoreboard

// Declare analysis ports for the scoreboard


`uvm_analysis_imp_decl(_before)
`uvm_analysis_imp_decl(_after)

// Define a UVM scoreboard class named normal_adder_score


class normal_adder_score extends uvm_scoreboard;

// Register the class with UVM factory


`uvm_component_utils(normal_adder_score)

// Constructor for the class


function new(string name = "normal_adder_score", uvm_component parent);
// Call the base class constructor
super.new(name, parent);
endfunction

// Declare analysis exports for sending transactions to the scoreboard


uvm_analysis_export#(normal_adder_txn) add_sc_before;
uvm_analysis_export#(normal_adder_txn) ref_add_sc_after;

// Declare TLM fifos for storing transactions


uvm_tlm_analysis_fifo#(normal_adder_txn) add_f_sc;
uvm_tlm_analysis_fifo#(normal_adder_txn) ref_add_tlm_sc;

// Declare transaction variables for storing incoming transactions


normal_adder_txn add_f_before;
normal_adder_txn ref_add_f_after;

// Build phase: Create instances of transactions and analysis ports


function void build_phase(uvm_phase phase);
// Create instances of transactions
add_f_before = normal_adder_txn::type_id::create("add_f_before");
ref_add_f_after = normal_adder_txn::type_id::create("ref_add_f_after");

// Create analysis ports for sending transactions


add_sc_before = new("add_sc_before", this);
add_f_sc = new("add_f_sc", this);
ref_add_sc_after = new("ref_add_sc_after", this);
ref_add_tlm_sc = new("ref_add_tlm_sc", this);
endfunction
// Connect phase: Connect analysis exports to TLM fifos
function void connect_phase(uvm_phase phase);
add_sc_before.connect(add_f_sc.analysis_export);
ref_add_sc_after.connect(ref_add_tlm_sc.analysis_export);
endfunction : connect_phase

// Run phase: Compare received transactions and report Pass/Fail


task run_phase(uvm_phase phase);
forever begin
// Get transactions from TLM fifos
add_f_sc.get(add_f_before);
ref_add_tlm_sc.get(ref_add_f_after);

// Compare the sum_out values of the transactions


if (add_f_before.sum_out == ref_add_f_after.sum_out)
`uvm_info("compare", {"Test : Pass"}, UVM_LOW)
else
`uvm_info("compare", {"Test : Fail"}, UVM_LOW)
end
endtask

endclass : normal_adder_score

Now let’s Define Test Case

// Include the environment file named "normal_add_env.sv"


`include "normal_add_env.sv"

// Define a UVM test class named normal_add_test


class normal_add_test extends uvm_test;

// Register the class with UVM factory


`uvm_component_utils(normal_add_test)

// Declare instances of the environment and sequence


normal_add_env add_test_env;
normal_add_sequence seq_txn1;

// Constructor for the class


function new(string name = "normal_add_test", uvm_component parent);
// Call the base class constructor
super.new(name, parent);
endfunction

// Build phase: Create an instance of the environment


function void build_phase(uvm_phase phase);
// Call the base class build_phase
super.build_phase(phase);

// Create an instance of the environment


add_test_env = normal_add_env::type_id::create("add_test_env", this);
endfunction
// Run phase: Execute the test sequence
task run_phase(uvm_phase phase);
// Raise objection to keep the simulation running
phase.raise_objection(this);

// Create an instance of the sequence


seq_txn1 = normal_add_sequence::type_id::create("seq_txn1", this);

// Start the sequence on the sequencer within the agent


seq_txn1.start(add_test_env.agent.add_seq);

// Drop objection to allow the simulation to proceed


phase.drop_objection(this);

// Set a drain time to ensure that all activities complete before ending
the phase
phase.phase_done.set_drain_time(this, 50);
endtask

endclass

You might also like