0% found this document useful (0 votes)
8 views

13-VerificationExample

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

13-VerificationExample

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

Virtual Interface

and
Verification Example

*Taken from verificationguide.com


Virtual Interfaces
A virtual interface is a pointer to an actual interface in SystemVerilog. It is most
often used in classes to provide a connection point to allow classes to access the
signals in the interface through the virtual interface pointer.

Why do we need Virtual interfaces ?


• Physical interface in not supported in Object Oriented Programming (OOP)
Fundamentals. So, this virtual interface concept came into the picture to use
signals of interface.

• An interface can't be instantiated inside a class or program block, we need a


virtual interface to point the physical interface. So, the virtual interface is a
pointer to the actual interface and using virtual interface, a class can point to
different physical interfaces, dynamically (at run time).

• With virtual interface, we can change references to physical interface


dynamically. Without virtual interfaces, all the connectivity is determined
during compilation time, and therefore can't be randomized or reconfigured.

12/25/2024 Verification with System Verilog 2


Virtual Interface
A virtual interface is a variable that represents an interface instance.

Syntax:
virtual interface_name instance_name;

example:
virtual mem_intf intf;

• Virtual interface must be initialized before using it. i.e, Virtual interface must be
connected/pointed to the actual interface.
• Accessing the uninitialized virtual interface results in a run-time fatal error.
• Virtual interfaces can be declared as class properties, which can be initialized
using a procedure or by an argument to new().
• Virtual interface variables can be passed as arguments to the tasks, functions, or
methods.
• All the interface variables/Methods can be accessed via virtual interface handle.
i.e virtual_interface.variable
12/25/2024 Verification with System Verilog 3
Verification flow examples
(without the use of modport, clocking blocks and monitor blocks)
Verification of an adder unit

Valid signal indicates the valid values on a and b.


On valid signal, adder will add the a and b, drives the result in next clock on c.

12/25/2024 Verification with System Verilog 5


Hierarchy for verification

Generator generates random signals (called transactions) and puts in mail box.
Driver takes from mail box and sends them to DUT through interface.
(interface needs to be defined before calling the driver).
(mailbox connects generator and driver and is defined in environment class)

12/25/2024 Verification with System Verilog 6


Design Steps for Adder verification

Step 1: Design the module that needs to be verified


module adder(
input clk ,
input reset,
input [3:0] a ,
input [3:0] b ,
input valid,
output [6:0] c );

reg [6:0] tmp_c;

always @(posedge reset or posedge clk)


begin
if(reset)
tmp_c <= 0;
else
if (valid)
tmp_c <= a + b;
else tmp_c <= temp_c;
end

assign c = tmp_c;
endmodule
Step 2: Define Transaction class
• Transaction.sv
– Fields required to generate the stimulus are declared in the transaction class.
– Transaction class can also be used as placeholder for the activity monitored by
monitor on DUT signals.
– So, first step is to declare the 'Fields‘ (inputs, outputs, signals) in the transaction class.
– To generate the random stimulus, declare the fields as 'rand'.
– Add display() method to display Transaction properties.
class transaction;
//declaring the transaction items
rand bit [3:0] a;
rand bit [3:0] b;
bit [6:0] c; //c is output. Hence not defined as random

function void display(string name); % to display if it is a transaction by generator or driver


$display("-------------------------");
$display("- %s ",name);
$display("-------------------------");
$display("- a = %0d, b = %0d",a,b);
$display("- c = %0d",c);
$display("-------------------------");
endfunction
endclass
Step 3: Define Generator Class
• Generator.sv
– Generating the stimulus by randomizing the transaction class
– Sending the randomized class to driver

• Adding Mailbox and event, (mailbox will be in environment)


Mailbox is used to send the randomized transaction to Driver.
Event to indicate end of packet generation.
This involves,
• Declaring the Mailbox and Event;
• Getting the Mailbox handle from environment class ( because, same mailbox will be shared across generator and driver).

• Adding variable to control the number of packets to be created,


Generator Class
class generator;

//declaring transaction class


transaction trans;

//To specify number of packets to generate


int packet_count;

//mailbox, to generate and send the packet to driver (this mailbox has to be passed from environment)
mailbox gen2driv;

//event, to dicate the end of transaction generation


event PacketGenerationEnded;

//constructor
//Getting the mailbox handle from env. The environment class has the mailbox since the transaction packet is
// to be shared between the generator and driver

function new(mailbox gen2driv);


this.gen2driv = gen2driv;
endfunction
Generator Class (continued)

// main task : To generate(create and randomize) required number of transaction packets and
// put into mailbox

task main(); // We need to use ‘task’ and not ‘function’ since we are dealing with time/ events

repeat(packet_count)
begin
trans = new();
if( !trans.randomize() )
% ‘randomize’ will generate random values for all ‘rand’ properties a and return a ‘1’ or will fail and return a ‘0’
% randomize function may not work in modelsim, hence randomize numbers may be manually usuing rand function.
$fatal("Gen:: trans randomization failed"); // randomize and check if succeeded
else
begin
trans.display("[ Generator ]");
gen2driv.put(trans); // put in mailbox for driver to see
end
end

-> packetGenerationEnded; //triggering indicates the end of generation


endtask

endclass
Step 4: Define Interface
• interface.sv

# simple interface without modport and clocking


# you may modify including modport

interface intf(input logic clk,reset);

//declaring the signals


logic valid;
logic [3:0] a; logic [3:0] b;
logic [6:0] c;

Endinterface
Step 4: Define Driver class
• driver.sv
(Receive the stimulus generated from generator and drive to DUT by assigning
transaction class values to interface signals)

– Declare interface and mailbox. ( can u guess what type of interface ? Why?)
– Get the interface and mailbox handle from environment through constructor.
– Add reset task, which initializes the Interface signals to default values.
– Add drive task to drive the transaction packet to interface signal.
– Add local variable to track the number of packets driven, and increment the
variable in drive task.
(This will be useful to end the test-case/Simulation. i.e compare the generated
packet’s and driven packet’s data. If both are same then end the simulation)
Driver class
class driver
//variable used to count the number of transactions
int no_transactions;

// define a handle for transaction. since we will be reading a transaction from the mailbox
transaction trans;

//creating virtual interface handle


virtual intf vif;

//creating mailbox handle


mailbox gen2driv;

//constructor
// Obtain mailbox and virtual interface handles from environment

function new(virtual intf vif, mailbox gen2driv);


this.vif = vif;
this.gen2driv = gen2driv;
endfunction
Driver class (continued)

//Reset task, Reset the Interface signals to default/initial values

task reset; // We need to use ‘task’ and not ‘function’ since we are dealing with time/ events

wait(vif.reset); // Wait till we get reset from the interface

$display("[ DRIVER ] ----- Reset Started -----");


vif.a <= 0;
vif.b <= 0;
vif.valid <= 0;

wait(!vif.reset); // Wait till reset is still enabled. Wait for it to be disabled

$display("[ DRIVER ] ----- Reset Ended -----");

endtask
Driver class (continued)
//Main task : drives the transaction items to interface signals
task main;
forever begin

gen2driv.get(trans); // get the transaction from mailbox that was filled by generator
@(posedge vif.clk); // at rising edge read the value of a, b from generator and give to interface
vif.valid <= 1;
vif.a <= trans.a;
vif.b <= trans.b;

@(posedge vif.clk); // at next rising edge read the value of c from interface and write to transaction
vif.valid <= 0;
trans.c <= vif.c;

@(posedge vif.clk); // at next rising edge, display that a transaction has happened and increment count
trans.display("[ Driver ]");
no_transactions++;

end
endtask

endclass
Step 5: Define Environment class
• environment.sv
Environment is a container class that contains Mailbox, Generator and Driver.
Creates the mailbox, generator and driver, shares the mailbox handle across the
Generator and Driver.

– Create mailbox, generator and driver and pass interface through new() constructor.
– Generator and Driver activity can be divided and controlled in three methods.

• pre_test() - Method to call Initialization. i.e, reset method


• test() - Method to call Stimulus Generation and Stimulus Driving.
• post_test() - Method to wait the completion of generation and driving.
– Run task to call these three methods
Environment Class
`include "transaction.sv"
`include "generator.sv"
`include "driver.sv“

class environment;

//generator and driver instance


generator gen;
driver driv;
//mailbox handle
mailbox gen2driv;
//virtual interface
virtual intf vif;

//constructor
// Interface needs to be brought in to environment from ‘top -> test ’
function new(virtual intf vif);
this.vif = vif;

//creating the mailbox (Same handle will be shared across generator and driver)
gen2driv = new();

//creating generator and driver


gen = new(gen2driv);
driv = new(vif, gen2driv);

endfunction //end of constructor


Environment Class (continued)
// Generator and driver activity
task pre_test();
driv.reset();
endtask

task test();
fork
gen.main();
driv.main();
join_any // as soon as generate is finished, go to post_test and wait for packet Gen ending
endtask

task post_test();
wait(gen. PacketGenerationEnded.triggered);
wait(gen.repeat_count == driv.no_transactions);
endtask

//run all tasks


task run;
pre_test();
test();
post_test();
$finish;
endtask
endclass
Step 6: Test module

• Test code is written with the program block.

Test is responsible for


– Creating the environment
– Configuring the testbench i.e, setting the type and
number of transactions to be generated.
– Initiating the stimulus driving.
Test ‘Program’
randomTest.sv

`include "environment.sv“

program test(intf intf);

//declaring environment instance


environment env;

initial
// Interface needs to be brought in to environment from ‘top -> test ’
begin
//creating environment
env = new(intf);

//setting the packet_count of generator to generate 10 packets


env.gen.packet_count = 10;

//calling ‘run’ of env, it inturn calls generator and driver main tasks.
env.run();
end
endprogram
Top Level Test bench
• TbenchTop.sv
• This is the top most file, which connects the
DUT and Testbench.
• ‘TestBenchTop’ consists of DUT, Test and
Interface instances.
• Interface connects the DUT and TestBench.
Top level test

`include "interface.sv"
`include "random_test.sv"

module tbench_top; //DUT instance, interface signals are connected


to the DUT ports
//clock and reset signal declaration adder DUT (
bit clk; .clk(i_intf.clk),
bit reset; .reset(i_intf.reset),
.a(i_intf.a),
//clock generation .b(i_intf.b),
always #5 clk = ~clk; .valid(i_intf.valid),
.c(i_intf.c)
//reset Generation );
initial begin
reset = 1; //enabling the wave dump
#5 reset =0; initial begin
end $dumpfile("dump.vcd"); $dumpvars;
end
endmodule
//creatinng instance of interface, inorder to connect
DUT and testcase
intf i_intf(clk,reset);

//Testcase instance, interface handle is passed to test as


an argument
test t1(i_intf);
Hierarchy for verification

Generator generates random signals (called transactions) and puts in mail box.
Driver takes from mail box and sends them to DUT through interface.
(interface needs to be defined before calling the driver).

12/25/2024 Verification with System Verilog 24


Adding monitor and scoreboard to the adder
testbench
Hierarchy for verification

Layered Testbench
without scoreboard
and monitor

Layered Testbench with


scoreboard and monitor

12/25/2024 Verification with System Verilog 26


Monitor Design
class monitor;

//creating virtual interface handle


Samples the interface signals and converts
virtual intf vif; the signal level activity to the transaction
//creating mailbox handle
level.
mailbox mon2scb; Send the sampled transaction to Scoreboard
//constructor
via Mailbox. Create new mailbox mon2scb
function new(virtual intf vif,mailbox mon2scb);
//getting the interface
this.vif = vif;
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

//Samples the interface signal and send the sample


packet to scoreboard
task main;
forever begin
transaction trans;
trans = new(); Note: we use the same ‘trans’ class.
@(posedge vif.clk);
wait(vif.valid);
We are not generating data by randomization. We
trans.a = vif.a; are reading values that are present in the interface
trans.b = vif.b; and assigning it to a, b and c of the transactor.
@(posedge vif.clk);
trans.c = vif.c;
@(posedge vif.clk); The advantage of using classes is that the signals
mon2scb.put(trans);
trans.display("[ Monitor ]"); need not be declared as input or output. We can use
end
endtask them anyway we want.
endclass
Scoreboard Design
class scoreboard;
Compares the actual result from the monitor
//creating mailbox handle
mailbox mon2scb; with the expected result.
//used to count the number of transactions
int no_transactions; Pick up from mailbox: mon2scb
//constructor
function new(mailbox mon2scb);
//getting the mailbox handles from environment
this.mon2scb = mon2scb;
endfunction

//Compares the Actual result with the expected result


task main;
transaction trans;
forever begin
mon2scb.get(trans);
if((trans.a+trans.b) == trans.c)
$display("Result is as Expected");
else
$error("Wrong Result.\n\tExpeced: %0d Actual: %0d",
(trans.a+trans.b),trans.c);

no_transactions++;
trans.display("[ Scoreboard ]");
end
endtask

endclass
`include "transaction.sv"
`include "generator.sv"
Environment Code
`include "driver.sv"
`include "monitor.sv"
`include "scoreboard.sv"
class environment;

//generator and driver instance


generator gen;
driver driv;
monitor mon;
scoreboard scb;

//mailbox handle's
mailbox gen2driv;
mailbox mon2scb; task test();
//constructor
fork
//virtual interface function new(virtual intf vif); gen.main();
//get the interface from test driv.main();
virtual intf vif;
this.vif = vif; mon.main();
scb.main();
//creating the mailbox (Same handle will be join_any
shared across generator and driver) endtask
gen2driv = new();
mon2scb = new(); task post_test();
wait(gen.ended.triggered);
wait(gen.repeat_count == driv.no_transactions); //Optional
//creating generator and driver
gen = new(gen2driv); wait(gen.repeat_count == scb.no_transactions);

driv = new(vif,gen2driv); endtask

mon = new(vif,mon2scb);
//run task
scb = new(mon2scb);
task run;
endfunction
pre_test();
test();
// post_test();
task pre_test(); $finish;
driv.reset(); endtask
endtask
endclass
Where to look for codes

DETAILED ANALYSIS and CODE available at

Verificationguide.com
testbench.in
Chipverify.com

12/25/2024 Verification with System Verilog 30


Thank you

You might also like