[go: up one dir, main page]

0% found this document useful (0 votes)
37 views29 pages

EE5530 Lecture16 Complete SV TestBench

This document provides information about writing a SystemVerilog testbench for verifying a memory model design. It includes: 1) An overview of the verification process and developing a verification plan for the memory design. 2) Details of the memory design under test (DUT) including its operations, features and a module implementation. 3) Components of the testbench including the transaction class, generator class, driver class, interface and developing tasks for reset, driving transactions.
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)
37 views29 pages

EE5530 Lecture16 Complete SV TestBench

This document provides information about writing a SystemVerilog testbench for verifying a memory model design. It includes: 1) An overview of the verification process and developing a verification plan for the memory design. 2) Details of the memory design under test (DUT) including its operations, features and a module implementation. 3) Components of the testbench including the transaction class, generator class, driver class, interface and developing tasks for reset, driving transactions.
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/ 29

Lecture 16

Complete SV TestBench
Memory Read

1. Difference between inheritance and composition?

2. What are virtual interfaces?

3. What are abstract classes?

4. What is pure virtual method?

5. What is super keyword?

6. Explain access control in systemverilog classes?


SystemVerilog TestBench Example — Memory

Courtesy: www.verificationguide.com
Verification Process

The steps involved in the verification process are,

 Creation of Verification plan


 Testbench Architecture
 Writing TestBench

Before writing/creating the verification plan, need to know about design, so will go through
the design specification.

* In this example Design/DUT is Memory Model.


I/O Specification
Design Specification

Operations:

Write Operation:

address, wr_en, and wdata should be driven at the same clock cycle.

Read Operation:

address and rd_en should be driven on the same clock cycle, Design will respond with the data in the
next clock cycle.

Design Features,

The Memory model is capable of storing 8bits of data per address location
Reset values of each address memory location is ‘hFF
Memory DUT
module memory
#(
parameter ADDR_WIDTH = 2, //Memory
parameter DATA_WIDTH = 8 reg [DATA_WIDTH-1:0] mem [2**ADDR_WIDTH];
)
( //Reset
input clk, always @(posedge reset)
input reset, for(int i=0;i<2**ADDR_WIDTH;i++) mem[i]=8'hFF;

//control signals // Write data to Memory


input [ADDR_WIDTH-1:0] addr, always @(posedge clk)
input wr_en, if (wr_en) mem[addr] <= wdata;
input rd_en,
// Read data from memory
//data signals always @(posedge clk)
input [DATA_WIDTH-1:0] wdata, if (rd_en) rdata <= mem[addr];
output reg [DATA_WIDTH-1:0] rdata
); endmodule
Verification Plan
1. Write and Read to a particular memory location:

Perform write to any memory location, read from the same memory location, read data should be the same
as written data

2. Write and Read to all memory locations:

Perform write and read to all the memory locations (as the address is 2bit width the possible address are
2‘b00, 2’b01, 2’b10, and 2’b11)

3. Default memory value check:

Check default memory values. (before writing any locations, do read operation we should get default values
as ‘hFF)

4. Reset in Middle of Write/Read Operation:

Assert reset in between write/read operation and check for default values. (after writing to few locations
assert the reset and perform read operation, we should get default memory location value ‘hFF)
TestBench Hierarchy
TestBench Architecture
TestBench Without Monitor, Agent, and Scoreboard
Transaction Class
Fields required to generate the stimulus are declared in the transaction class. Transaction class can
also be used as a placeholder for the activity monitored by the monitor on DUT signals. So, the first
step is to declare the Fields‘ in the transaction class

class transaction;

//declaring the transaction items


rand bit [1:0] addr;
rand bit wr_en;
rand bit rd_en;
rand bit [7:0] wdata;
bit [7:0] rdata;
bit [1:0] cnt;

//constaint, to generate any one among write and read


constraint wr_rd_c { wr_en != rd_en; };

endclass
Generator Class
Generator class is responsible for,

 Generating the stimulus by randomizing the transaction class


 Sending the randomized class to driver

class generator;

//declaring transaction class


transaction trans;

//main task, generates(create and randomizes) the packets and puts into mailbox
task main();
trans = new();
if( !trans.randomize() ) $fatal("Gen:: trans randomization failed");
gen2driv.put(trans);
endtask

endclass
Generator Class
class generator;

//declaring transaction class task main();


rand transaction trans;
repeat(repeat_count) begin
//declaring mailbox trans = new();
mailbox gen2driv; if( !trans.randomize() )
$fatal("Gen:: trans randomization failed");
//Specify number of items to generate gen2driv.put(trans);
int repeat_count; end
-> ended;
//event endtask
event ended; endclass

//constructor
function new(mailbox gen2driv,event ended);
//getting the mailbox handle from env
this.gen2driv = gen2driv;
this.ended = ended;
endfunction
Interface
Will group the signals, specifies the direction (Modport)

Synchronize the signals(Clocking Block).

//driver clocking block //monitor clocking block

clocking driver_cb @(posedge clk); clocking monitor_cb @(posedge clk);


default input #1 output #1; default input #1 output #1;
output addr; input addr;
output wr_en; input wr_en;
output rd_en; input rd_en;
output wdata; input wdata;
input rdata; input rdata;

endclocking endclocking
Complete Interface

interface mem_intf(input logic clk,reset);


//monitor clocking block
//declaring the signals clocking monitor_cb @(posedge clk);
logic [1:0] addr; default input #1 output #1;
logic wr_en; input addr;
logic rd_en; input wr_en;
logic [7:0] wdata; input rd_en;
logic [7:0] rdata; input wdata;
input rdata;
//driver clocking block endclocking
clocking driver_cb @(posedge clk);
default input #1 output #1; //driver modport
output addr; modport DRIVER (clocking driver_cb,input clk,reset);
output wr_en;
output rd_en; //monitor modport
output wdata; modport MONITOR (clocking monitor_cb,input clk,reset);
input rdata;
endclocking endinterface
Driver Class
Generator class is responsible for,

 Receiving the stimulus generated from the generator


 Driving to DUT by assigning transaction class values to interface signals.

//creating virtual interface handle


virtual mem_intf mem_vif;

//creating mailbox handle


mailbox gen2driv;

//constructor
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif;
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction
Reset Task
For simplicity, define is used to access interface signals.

`define DRIV_IF mem_vif.DRIVER.driver_cb

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


task reset;
wait(mem_vif.reset);
$display("--------- [DRIVER] Reset Started ---------");
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;
`DRIV_IF.addr <= 0;
`DRIV_IF.wdata <= 0;
wait(!mem_vif.reset);
$display("--------- [DRIVER] Reset Ended ---------");
endtask
Drive Task
//drive the transaction items to interface signals
task drive;
forever begin
transaction trans;
`DRIV_IF.wr_en <= 0;
`DRIV_IF.rd_en <= 0;

gen2driv.get(trans);
$display("--------- [DRIVER-TRANSFER: %0d] ---------",no_transactions);

@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.addr <= trans.addr;

if(trans.wr_en) begin
`DRIV_IF.wr_en <= trans.wr_en;
`DRIV_IF.wdata <= trans.wdata;
$display("\tADDR = %0h \tWDATA = %0h",trans.addr,trans.wdata);
@(posedge mem_vif.DRIVER.clk);
end
Drive Task (contd..)
if(trans.rd_en) begin
`DRIV_IF.rd_en <= trans.rd_en;
@(posedge mem_vif.DRIVER.clk);
`DRIV_IF.rd_en <= 0;
@(posedge mem_vif.DRIVER.clk);
trans.rdata = `DRIV_IF.rdata;
$display("\tADDR = %0h \tRDATA = %0h",trans.addr,`DRIV_IF.rdata);
end
$display("-----------------------------------------");
no_transactions++;
end
endtask

//used to count the number of transactions


int no_transactions;
Adding a local variable to track the
number of packets driven, and increment
//drive the transaction items to interface signals
the variable in drive task (This will be
task drive;
useful to end the test-case/Simulation. i.e
------
compare the generated pkt’s and driven
------
pkt’s, if both are same then end the
no_transactions++;
simulation)
endtask
Complete Driver Code

class driver;

//used to count the number of transactions task reset;


int no_transactions; ---
---
//creating virtual interface handle endtask
virtual mem_intf mem_vif;
task drive;
//creating mailbox handle
------
mailbox gen2driv;
------
no_transactions++;
//constructor
endtask
function new(virtual mem_intf mem_vif,mailbox gen2driv);
//getting the interface
this.mem_vif = mem_vif; endclass
//getting the mailbox handle from environment
this.gen2driv = gen2driv;
endfunction
TestBench Without Monitor, Agent, and Scoreboard
Environment
Environment class is responsible for,

 Container class contains Mailbox, Generator and Driver.


 Creates the mailbox, generator and driver shares the mailbox handle across the Generator and Driver.

//generator and driver instance


generator gen;
driver driv;

//mailbox handle's
mailbox gen2driv;

//event for synchronization between generator and test


event gen_ended;

//virtual interface
virtual mem_intf mem_vif;
Environment Constructor
Create Mailbox, Generator, Driver and pass the interface handle through the new() method.

//constructor
function new(virtual mem_intf mem_vif);
//get the interface from test
this.mem_vif = mem_vif;

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

//creating generator and driver


gen = new(gen2driv,gen_ended);
driv = new(mem_vif,gen2driv);
endfunction
Tasks in the Environment
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.method.
Add a run task to call the above methods,call $finish after post_test() to end the simulation.

task post_test();
task pre_test(); wait(gen_ended.triggered);
driv.reset(); wait(gen.repeat_count == driv.no_transactions);
endtask endtask

task test();
fork task run;
gen.main(); pre_test();
driv.main(); test();
join_any post_test();
endtask $finish;
endtask
`include "transaction.sv"
Complete Environment Code
`include "generator.sv"
`include "driver.sv"
class environment; //creating the mailbox
//(Same handle will be shared
//generator and driver instance //across generator and driver)
generator gen; gen2driv = new();
driver driv; task post_test();
//creating generator and driver wait(gen_ended.triggered);
//mailbox handle's gen = new(gen2driv,gen_ended); wait(gen.repeat_count ==
mailbox gen2driv; driv = new(mem_vif,gen2driv); driv.no_transactions);
endfunction endtask
//event for synchronization
//between generator and test task pre_test(); //run task
event gen_ended; driv.reset(); task run;
endtask pre_test();
//virtual interface test();
virtual mem_intf mem_vif; task test(); post_test();
fork $finish;
//constructor gen.main(); endtask
function new(virtual mem_intf mem_vif); driv.main();
//get the interface from test join_any endclass
this.mem_vif = mem_vif; endtask
Test
Test is written with program construct and 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..

`include "environment.sv"
program test(mem_intf intf);

//declaring environment instance


environment env;

initial begin
//creating environment
env = new(intf);

//setting the repeat count of generator as 10, means to generate 10 packets


env.gen.repeat_count = 10;

//calling run of env, it interns calls generator and driver main tasks.
env.run();
end
endprogram
`include "interface.sv"
TestBench Top
`include "random_test.sv"

module tbench_top; //Testcase instance, interface handle is passed to test


test t1(intf);
//clock and reset signal declaration
bit clk; //DUT instance
bit reset; memory DUT (
.clk(intf.clk),
//clock generation .reset(intf.reset),
always #5 clk = ~clk; .addr(intf.addr),
.wr_en(intf.wr_en),
//reset Generation .rd_en(intf.rd_en),
initial begin .wdata(intf.wdata),
reset = 1; .rdata(intf.rdata)
#5 reset =0; );
end
//enabling the wave dump
//creatinng instance of interface, inorder to connect DUT initial begin
and testcase $dumpfile("dump.vcd"); $dumpvars;
mem_intf intf(clk,reset); end
endmodule
`include "environment.sv"
program test(mem_intf intf); //declaring environment instance
environment env;
class my_trans extends transaction; my_trans my_tr;

bit [1:0] count; initial begin


//creating environment
function void pre_randomize(); env = new(intf);
wr_en.rand_mode(0);
rd_en.rand_mode(0); my_tr = new();
addr.rand_mode(0);
//setting the repeat count of generator as 4, means to generate 4 packets
if(cnt %2 == 0) begin env.gen.repeat_count = 10;
wr_en = 1;
rd_en = 0; env.gen.trans = my_tr;
addr = count;
end //calling run of env, it interns calls generator and driver main tasks.
else begin env.run();
wr_en = 0; end
rd_en = 1; endprogram
addr = count;
count++;
end
cnt++;
endfunction

endclass

You might also like