SystemVerilog Testbench
SystemVerilog Testbench
Look for
coding tips!
Checks Testbench
Creates correctness
stimulus Verification
Environment Identifies
Executes Test transactions
transactions
Transactor Scoreboard Checker Observes
Supplies data data
to the DUT from DUT
Driver Assertions Monitor
DUT
Testbench Design
Start with a fully randomizable testbench
Run many randomized simulation runs
Analyze cumulative coverage and coverage holes
Then with minimal code changes:
Add constrained stimulus to fill coverage holes
Finally:
Make few directed tests to hit the remaining holes
Coverage-Driven Productivity
With
Methodology gain
VIP
Goal Directed
% Coverage
Methodology
Self-checking
random environment
development time
Time
Run:
simv +user_tb_runtime_options
-l logfile Create log file
-gui Run GUI
-ucli Run with new command line debugger
-i cmd.key Execute UCLI commands
Source
code
tracing
Active
threads
Local
variables
Examples
$VCS_HOME/doc/examples
Email Support:
[email protected]
SystemVerilog LRM
www.Accellera.org or www.eda.org/sv
reset
request[1:0]
grant[1:0]
clock
arb.sv
clock
mem cpu
top
module mem ( module cpu (
input bit req, input bit clk,
bit clk, bit gnt,
bit start, bit rdy,
wire [1:0] mode, inout wire [7:0] data,
wire [7:0] addr, output bit req,
inout wire [7:0] data, bit start,
output bit gnt, wire [1:0] mode,
bit rdy); wire [7:0] addr);
… …
module top;
logic req, gnt, start, rdy;
bit clk;
always #10 clk = !clk;
logic [1:0] mode;
logic [7:0] addr;
wire [7:0] data;
mem m1(req, clk, start, mode, addr, data, gnt, rdy);
cpu c1(clk, gnt, rdy, data, req, start, mode, addr);
endmodule
SystemVerilog Testbench with VCS 05/07/2007
Testbench Environment -- Interfaces 22
Named Bundle of Signals
The RTL code is connected with bundled signals
simple_bus
clk
mem cpu
top
clock
data
clock
Design
Testbench
Sample inputs Drive outputs
before clock at clock
SystemVerilog Testbench with VCS 05/07/2007
Testbench Environment – Top Block 33
Step 3
Create top module
Scoping Rules
SystemVerilog defines a global scope, $root,
outside any module or program
Define global items such as shared enums
Use parameters for global constants, not macros
`timescale 1ns/1ns
typedef enum {IDLE, RUN, WAIT} fsm_state_t; root.sv
parameter TIMEOUT = 1_000_000;
module state_machine(…);
fsm_state_t state, next_state; dut.sv
endmodule
program test;
fsm_state_t state; test.sv
initial #TIMEOUT $finish;
endprogram
SystemVerilog Testbench with VCS 05/07/2007
Testbench Environment -- Communication 35
DUT visibility
The program block can see all signals & routines in
the design
A module can not see anything in program block
Use absolute hierarchical path to access DUT
Start with $root, then top-level instance name, DUT, etc.
Use care when calling DUT routines from program
Good practice is to use a function to get info
Don’t try to trigger DUT code
SV accesses ports & XMR signals immediately
(asynchronously)
in case of error…
“test.sv", 7: top.t1.a1: started at 55ns failed at 55ns
Offending '(arbif.cb.grant == 1)‘
arbif.cb.request <= 1;
repeat (2) @arbif.cb;
a1: assert (arbif.cb.grant==1)
success++;
else
$error(“No grant received”); Custom
end message
Run with
debugger
In SystemVerilog, the old reg type has been extended so it can be driven by
single drivers (gates, modules, continuous assignments) like a wire. It has a
new name logic. It can not have multiple drivers – use a wire.
j = q.pop_back(); // {7,0,2,3,4,5} j = 6
q.push_back(8); // {7,0,2,3,4,5,8} Insert at back
j = q.pop_front(); // {0,2,3,4,5,8} j = 7
$display(q.size); // “6”
foreach (q[i]) $display(q[i]);
SystemVerilog Testbench with VCS 05/07/2007
Checking Results with Queues 47
Driver Monitor
DUT
SystemVerilog Testbench with VCS 05/07/2007
SV Language Basics 48
Array Methods
Search through arrays (fixed, dynamic, queue, assoc.)
Many more methods will be implemented, such as sort…
Returns a queue or scalar
a.sum of single bit values returns 0/1
Unless you compare to wider value: a.sum == 32’h3
Also available: product, and, or, xor
int q[$] = {1,3,5,7}, tq[$]; IEEE changed array
int d[] = {9,1,8,3,4}; const from {0,1} to ’{0,1}
int f[6] = {1,6,2,6,8,6}; (VCS issues Warning for
old usage)
$display(q.sum, q.product); // 16 105
LRM requires
tq = q.min(); // {1}
a queue
tq = q.max(); // {7}
tq = f.unique; // {1,6,2,8}
tq = d.find with (item > 3); // {9,8,4}
tq = d.find_index with (item > 3); // {0,2,4}
SystemVerilog Testbench with VCS 05/07/2007
SV Language Basics 50
string s = “SystemVerilog”;
$display(s.getc(0),, s.toupper());
s = {s, “3.1b”}; // string concat
s.putc(s.len()-1, “a”); // change b-> a
$display(s);
$display(s.substr(2, 5)); // 4 characters
a, b: input logic
u, v: output bit [15:0]
SystemVerilog Testbench with VCS 05/07/2007
Lab 1 54
Verify an arbiter
Objective
Verify the arbiter’s reset
Verify arbiter handles simple requests and grants
Verify proper handling of request sequences
Key Topics
Port list, clocking block, program block, assert, drive samples
and check responses.
Time Allotted
45 minutes
HDL OOP
Verilog SystemVerilog
Block definition module class
A complete Object
house An object is an instance of a class
Address of Handle
a house Type-safe pointer to an object – can not be corrupted
Light Properties
switches Variables contained in the instance of the class
Methods
Turn on/off
switches Tasks/functions (algorithms) that operate on the properties in
this instance of the class
SystemVerilog Testbench with VCS 05/07/2007
OOP Basics 61
Class Example Variables & methods are
class Transaction; public by default
// properties (variables)
logic [31:0] src, dst, data[1024], crc;
logic [7:0] kind;
// methods
function void display;
$display(“Tr: %h, %h”, src, dst);
endfunction
Drives
transactions Monitor
Driver
into the DUT
Puts data
DUT
into
transactions
SystemVerilog Testbench with VCS 05/07/2007
OOP Basics 65
Accessing Class Members
Reference properties by pre-pending the object handle
class Transaction;
bit [31:0] src, dst, data[1024];
bit [7:0] kind;
function void display;
$display(“Tr: %h, %h”, src, dst);
endfunction
endclass
Transaction tr;
initial begin
tr = new();
tr.src = 5;
tr.dst = 7;
tr.display();
end
initial initial
tr = new(); tr = new(5); // dst uses default
endprogram endprogram
SystemVerilog Testbench with VCS 05/07/2007
Static attribute 67
class Thing;
int data;
endclass
… t1
Thing t1, t2; // Two handles
initial begin
t1 = new(); // Allocate first thing data=1
t1.data = 1;
t2 = new(); // Allocate second
t2.data = 2; t2
t2 = t1; // Second Thing is lost
t2.data = 5; // Modifies first thing
$display(t1.data); // Displays “5”
end
class Thing;
int data;
endclass
… t1
Thing t1, t2; // Two handles
initial begin
t1 = new(); // Allocate first thing data=1
t1.data = 1;
t2 = new(); // Allocate second
t2.data = 2; t2
t2 = t1; // Second Thing is lost
t2.data = 5; // Modifies first thing
$display(t1.data); // Displays “5” data=2
end
class Thing;
int data;
endclass
… t1
Thing t1, t2; // Two handles
initial begin
t1 = new(); // Allocate first thing data=1
t1.data = 1;
t2 = new(); // Allocate second
t2.data = 2; t2
t2 = t1; // Second Thing is lost
t2.data = 5; // Modifies first thing
$display(t1.data); // Displays “5” data=2
end
class Thing;
int data;
endclass
… t1
Thing t1, t2; // Two handles
initial begin
t1 = new(); // Allocate first thing data=5
t1.data = 1;
t2 = new(); // Allocate second
t2.data = 2; t2
t2 = t1; // Second Thing is lost
t2.data = 5; // Modifies first thing
$display(t1.data); // Displays “5”
end
id=5 id=5
body stuff body stuff
t2 t2
id=5
body
SystemVerilog does not currently support deep
object copy – look for it in a future IEEE version
To do a deep copy of all objects, make a copy() method
for all objects nested inside the class.
SystemVerilog Testbench with VCS 05/07/2007
Inheritance 73
BadTr bt;
bt = new; BadTr =
bt.src = 42; Transaction + bad_crc
bt.bad_crc = 1;
Cell.display()
Print ATM cell data if I’m at ATM cell
Print Ethernet MCA data if I’m an Ethernet packet
Print Sonet frame data if I’m a Sonet frame
Print USB packet data if I’m a USB packet
class Transaction;
rand bit [31:0] src, dst, data[]; // Dynamic array
randc bit [2:0] kind; // Cycle through all kinds
constraint c_len
{ data.size inside {[1:1000]}; } // Limit array size
endclass
Transaction tr;
initial begin
tr = new();
assert(tr.randomize());
send(tr);
end
endprogram
class C;
rand bit [5:0] a[];
Array size constraint cc {
a.size inside {[1:5]}; // Output
array[0] > 0; a[0] = 1;
Single element a[1] = 2;
foreach (a[i])
if (i > 0) a[2] = 33;
Multiple elements
a[i] > a[i-1]); a[3] = 39;
} a[4] = 40;
function void pre_randomize;
a.delete; // Needed in 2005.06
endfunction
endclass
class Nesting;
Make instances rand rand SubClass data;
endclass
Or they won’t be randomized
Don’t call randomize() in new() constructor
Test may want to change constraints first
Use rand_mode to make a variable random / non-random
env.first.rand_mode(0);
Just replace result of randomization for a directed test
SystemVerilog Testbench with VCS 05/07/2007
Randomization 95
More Tricks and Techniques
Need to modify a constraint in a test?
Use a variable in the constraint
rand int size;
int max_size = 100;
constraint c {
size inside {[1:max_size]}; }
Port0 port0
Data Self-
port1 port1
Generation DUT Checking
--- ---
Create stream of
port7 port7 Check received
transactions
transactions
Functional
Coverage
join join_any
join_none
// Timeout example
fork : check_block
wait (arbif.cb.grant == 1); // May never complete
#1000 $display(“@%0d: Error, grant never received”, $time);
join_any
disable check_block;
Features
FIFO with no size limit
get/put are atomic operations, no possible race conditions
Can suspend a process
Default mailbox has no data type
Queuing
of multiple threads is fair
mailbox mbx; // Declare a mailbox
mbx = new(); // allocate mailbox
mbx.put(p); // Put p object into mailbox
mbx.get(p); // p will get object removed from FIFO
success = mbx.try_get(p); // Non-blocking version
mbx.peek(p); // Look but don’t remove, can block
success = mbx.try_peek(p); // Non-blocking version
count = mbx.num(); // Number of elements in mailbox
SystemVerilog Testbench with VCS 05/07/2007
Communication Between Threads 104
Mailbox Example
program mailbox_example(…);
mailbox mbx = new();
Generator g = new(); Get data from mailbox
Driver d = new();
initial begin class Driver;
fork Allocate mailbox Transaction t;
g.main();
d.main(); task main;
join repeat (10) begin
end mbx.get(t);
endprogram @(posedge busif.cb.ack);
busif.cb.addr <= t.addr;
class Generator; busif.cb.kind <= t.kind;
Transaction t; …
task main; end
repeat (10) begin endtask
t = new();
assert(t.randomize());
mbx.put(t);
end
endtask
endclass
Put data into mailbox
SystemVerilog Testbench with VCS 05/07/2007
Communication Between Threads 105
Semaphore
Features
Variable number of keys can be put and removed
Controlled access to a shared object, such as sharing a bus from models
Thinkof two people wanting to drive the same car – the key is a
semaphore
Be careful – you can put back more keys than you took out!
Syntax
semaphore sem;
sem = new(optional_initial_keycount = 0);
sem.get(optional_num_keys = 1);
sem.put(optional_num_keys = 1);
Semaphore Example
program automatic test;
semaphore sem;
initial begin Allocate a semaphore, 1 key available
sem = new(1);
…
fork
task sequencer();
sequencer();
repeat($random()%10) @bus.cb;
sequencer();
sendTrans();
join
endtask
end
…
task sendTrans();
sem.get(1);
@bus.cb;
bus.cb.addr <= t.addr;
Wait for bus to be available bus.cb.kind <= t.kind;
bus.cb.data <= t.data;
When done, replace key sem.put(1);
endtask
endprogram
SystemVerilog Testbench with VCS 05/07/2007
Communication Between Threads 107
Events
Synchronize concurrent threads
Features
Synchronize parallel threads
Sync blocks process execution until event is triggered
Events connect triggers and syncs
Can be passed into tasks
event ev; // Declare event
-> ev; // Trigger an event
@ev; // Block process, wait for future event
wait (ev.triggered); // Block process, wait for event,
// including this timeslot
// Reduces race conditions
driver = new(ev); // Pass event into task
Objective
Write a structured testbench to learn more about classes,
randomization, threads and mailboxes
Verify that a basic transaction can be created, randomized, and
sent through the design
Time Allotted
1 hour
apb_trans
apb_gen
apb_mbox
apb_master DUT
enable_1
soc_1
data_1[7:0]
RX_1
4x4
enable_3
ATM
soc_3
data_3[7:0]
RX_3 Switch
Interface
module top; instances
Interface logic clk = 0;
name
Rx Rx0(clk), Rx1(clk), Rx2(clk), Rx3(clk);
Tx Tx0(clk), Tx1(clk), Tx2(clk), Tx3(clk);
task sendCell();
Rx.Rcb.soc <= 0; // Drive signal w/clocking block
...
endtask
endclass
Time Allotted
1 hour
mon2scb
apb_trans scoreboard
apb_gen mas2scb
apb_monitor
apb_mbox
apb_master DUT
$set_coverage_db_name ( name );
Sets the filename of the coverage database into which
coverage information is saved at the end of a simulation run.
$load_coverage_db ( name );
Load from the given filename the cumulative coverage
information for all coverage group types.
constraint addr_c {
Constraint addr inside {[0:8’hBF]};
Block }
constarint data_c {
data.size <= 1024;
}
Procedural
Code task display();
$display(“ %s, %h”, pkt.trans_type.name(), a
endtask
Coverage
covergroup pktCov;
Group coverpoint addr,data,trans_type;
endgroup
endclass
SystemVerilog Testbench with VCS 05/07/2007
Coverage Driven Verification 137
Directed
Add Testcase Functional
constraints Coverage
class packet;
rand bit [31:0]addr;
rand bit [7:0] data[];
rand transtype_t trans_type;
rand bit[7:0] pktSize;
constraint packetSize_c {
pktSize inside {[0:1024]};
data.size() == pktSize;
}
endclass
SystemVerilog Testbench with VCS 05/07/2007
Constrain the Randomness (Design Spec) 140
Design Spec:
• The valid address range is between 0 and 8’hBF.
• Address 8’hBB is not writable
Implementation: See below.
class packet;
rand bit [31:0]addr;
rand bit [7:0] data[];
... Implication
rand transtype_t trans_type; Constraints
constraint addr_c {
(trans_type == WRITE) -> addr != 8'hBB;
addr inside {[0:8’hBF]};
}
endclass
Test Plan:
Test device with WRITE type , data size 20 bytes at address
8’h55.
Implementation:
Override constraint custom_c. Same testbench as before.
constraint packet::custom_c {
pktSize == 20;
trans_type == WRITE;
addr == 8’h55;
}
constraint packet::custom_c {
addr dist {[8’h80:8’hBF] := 90, [0:8’h7F] := 10};
trans_type == WRITE;
}
program automatic test;
packet pkt;
initial begin
pkt = new();
repeat(50) if (pkt.randomize())
transmit(pkt);
end
endprogram
SystemVerilog Testbench with VCS 05/07/2007
Derive Coverage Model From Test Plan 144
Test Plan:
• Define 3 address regions: 0:0x3F, 0x40:0x7F and 0x80:0xBF
• Test with packets of all types = 2 bins
• Test with packets targeted to all address regions = 3 bins
• Test with all permutations of all packets types to all address
regions = 6 bins
• Implementation:
• Do not write directed test for each test item
• Instead map each test item into an executable coverage point
• Run random suite of tests and collect functional coverage.
• Analyze coverage results and to improve functional coverage
either run more random tests or for hard-to-reach coverage
points create a more constrained test (Directed random).
covergroup pktCov;
coverpoint trans_type; // Creates 2 bins
coverpoint addr { // Item 2: Creates 3 bins
bins low_range = {[0:0x3F]};
bins mid_range = {[0x40:0x7F]};
bins hi_range = {[0x80:0xBF]};
}
cross trans_type, dst; // Item 5: Creates 6 bins
endgroup
Time Allotted
1 hour
Test Tests
Scenario Generators
Functional Coverage
Functional Transactor Self Check Checker
DUT
Signal
class Transaction;
Data modeled using classes rand enum {READ, WRITE}
kind;
Packets rand bit [ 7:0] sel;
Frames rand bit [31:0] addr;
rand bit [31:0] data;
Cells endclass
Instructions
Pixels
Samples
Flows through the verification environment
100,000+ instances created and freed during a
simulation
100+ in existence at any given time
Duplicated only when necessary
Tests
Created Accumulated Freed
here here here
Generators
Duplicated
here
Created
Freed DUT here
here
sw_driver sw_driver
mbx.put(tr);
apb.write(...);
Mailbox
apb_master
task write(...); mbx.get(tr);
apb_master
Procedural Interface
interface object
mii_phy
phy.get(tr);
Test
eth_gen mac_layer
gmii_phy
mac.get(tr); phy.get(tr);
mac.put(tr); phy.put(tr);
xgmii_phy
phy.get(tr);
class apb_xactor;
function new(mailbox mgen, mdrv); generator
(xactor)
task main();
forever begin
mgen.get(t); transactor
// process transaction t (xactor)
mdrv.put(t);
end
endclass
driver DUT
(xactor)
Randomizable objects
Data streams
Configuration
Error injection
Election
Atomic generators atomic generator
blueprint
Modifying constraints Copy
Run-time generator control
Inserting directed stimulus
scenario generator
Scenario generators scenario
Atomic scenarios
Grammar-based scenarios
Defining new scenarios
Modifying scenario distributions
SystemVerilog Testbench with VCS 05/07/2007
Randomizable Aspects 162
Disturbance Injection
class cfg;
... ahb_mstr
endclass
Downloaded
into DUT via...
Entirely DUT-specific
Can only detect errors
Detected errors identified in planning stage
Each error detection mechanism part of coverage model
Ensure that opportunity to check error existed
No error was actually found
Where do I go next?
Complete the labs / and QuickStart
($VCS_HOME/doc/examples)
Read the Verification Methodology Manual
Take the VMM-Basic and Advanced classes
THANK YOU!!!
SystemVerilog Testbench with VCS 05/07/2007
Separation of testbench and RTL in SystemVerilog verification enhances reusability, allowing the same testbench to be used across multiple instances, projects, and systems, minimizing test-specific code . This separation supports coverage-driven verification by maximizing design quality through systematic detection of coverage holes and enabling the incorporation of directed and constrained stimuli without extensive code changes . Additionally, the separation facilitates the application of object-oriented programming (OOP) principles, such as encapsulation and inheritance, which organize the testbench into robust, self-contained, and reusable blocks . These OOP features enable debugging and maintenance of small sections of the code at a time, thus improving verification efficiency .
Testbench reusability in SystemVerilog is achieved through modularity, parameterization, and the use of virtual interfaces and OOP principles such as classes and inheritance. These enable components to be easily adapted for different tests or projects without rewriting significant amounts of code, which saves time and effort. Reusability is important as it enhances productivity, consistency, and reduces verification time by allowing testbenches to be shared across various designs or reused in future projects, ensuring verification resources are optimally utilized .
Object-oriented programming (OOP) enhances SystemVerilog testbench functionality by providing modularity, reusability, and scalability through classes and inheritance. OOP enables the encapsulation of related data and functions into classes, reducing code duplication and promoting reuse across different testbenches. Inheritance allows for base classes to define common behaviors which can be extended or overridden by derived classes, facilitating greater flexibility and adaptability in developing verification components .
Handle assignment in SystemVerilog affects only the reference to an object, not the object itself. When a handle is assigned to another, both refer to the same object in memory, which can lead to changes being reflected across variables that share the same handle. This is significant in memory and data structure interactions as it avoids unnecessary data duplication but requires careful management to prevent unintended side effects from shared reference mutations. SystemVerilog does not natively support deep copying, often necessitating custom copy functions for complex data structures .
Virtual interfaces in SystemVerilog enhance testbench connectivity and flexibility by allowing a generic connection to multiple different physical interfaces, thereby increasing reusability and abstraction. They group signals by function, which can be passed into testbench routines, allowing the separation of testbench structure from physical signal names . This aids in creating modular, reusable verification components by breaking the dependence on specific interface names or connectivity found in physical interfaces . Additionally, virtual interfaces can be utilized in various procedural methods and classes to operate across multiple physical interfaces using a uniform handle, significantly simplifying testbench design and reducing the amount of test-specific code . This approach in SystemVerilog enables a more flexible testbench architecture capable of handling different test scenarios and configurations with minimal modification .
SystemVerilog employs methodologies such as assertions and scoreboards to achieve self-checking in testbenches. Assertions automatically verify design properties and behavior, detecting mismatches during runtime. Scoreboards are used to compare the expected and actual outcomes, ensuring data integrity and consistency. These methodologies benefit verification by providing an automated way to detect errors, reducing the need for manual result checking, enhancing the reliability of testing, and accelerating the verification process by allowing continuous checking during simulation .
Functional coverage in SystemVerilog testbenches is used to measure how much of the design's functionality has been exercised during verification. It is defined based on the design's specification and tracks the occurrence of different scenarios and conditions during simulation runs. Functional coverage is important because it provides a quantitative measure of testing completeness, identifies untested parts of the design, and guides the creation of additional tests to fulfill coverage goals, thereby improving the overall quality and reliability of the verification effort .
A coverage-driven verification (CDV) methodology provides a systematic approach to measure the effectiveness and completeness of verification efforts in SystemVerilog environments. This methodology ensures that all functional aspects of the design are considered and tested, which helps in identifying coverage gaps or 'holes'. CDV helps optimize test benches by focusing effort on untested scenarios and ensures comprehensive testing, ultimately leading to higher verification quality .
Constrained random testing in SystemVerilog helps to automatically generate varied input stimuli within user-defined constraints ensuring that the design under test is exercised thoroughly. This contributes to design verification by enabling comprehensive testing through diverse scenarios, which increases the probability of finding deep, complex bugs that might not be detected with traditional hand-written test cases .
Assertions in SystemVerilog serve a critical role in automatically checking and ensuring the design's behavior conforms to its specification. They provide immediate feedback during simulation concerning whether the requirements are met, helping detect incorrect behaviors at run-time. Assertions contribute to design correctness by enabling early detection of design errors, simplifying debugging, and ensuring comprehensive testing across diverse scenarios by checking properties dynamically during verification .