0% found this document useful (0 votes)
5 views48 pages

Advanced VLSI Verification Interview Handbook

Uploaded by

amathya45
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)
5 views48 pages

Advanced VLSI Verification Interview Handbook

Uploaded by

amathya45
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/ 48

ADVANCED VLSI

VERIFICATION
INTERVIEW HANDBOOK

SPECIALLY DESIGNED FOR THOSE AIMING FOR


DESIGN VERIFICATION ROLE IN TOP COMPANIES

YOU NEED TO KNOW

Prasanthi Chanda
1. How does the SystemVerilog scheduler work?
Explain each region in detail.
Answer:
SystemVerilog uses a 4-phase event scheduler,
which consists of:
1. Active Region
Executes RTL code (always, initial, assign,
continuous assignments).
Executes blocking (=) and non-blocking (<=)
statements.
SystemVerilog interface connections are
resolved here.
2. Inactive Region
Used for delaying updates. If a signal changes
using <=, that change is scheduled here.
Mainly for consistent signal update ordering.
3. NBA (Non-blocking Assignment) Region
All <= updates occur here.
Ensures updates don’t affect the current
simulation time slot.
4. Observed Region (Postponed)
Assertions, monitors, and functional coverage
are evaluated here.
No changes should be made here—it's for
observing only.
2. How do you achieve cross coverage in
SystemVerilog?
Answer:
class transaction;
rand bit [1:0] opcode;
rand bit [1:0] status;

covergroup cg;
coverpoint opcode;
coverpoint status;

cross opcode, status;


endgroup

function new();
cg = new();
endfunction
endclass

The cross captures combinations of values.


It helps identify which value pairs have been
hit.
Can be enhanced using cross iff, bins, and
ignore_bins.
3. Explain the concept of factory override in UVM
with example.
Answer:
Factory override is used to replace a base class with
a derived class at runtime, without changing the
instantiation.

class base_txn extends uvm_sequence_item;


`uvm_object_utils(base_txn)
endclass

class derived_txn extends base_txn;


`uvm_object_utils(derived_txn)
endclass

initial begin

factory.set_type_override_by_type(base_txn::get_ty
pe(), derived_txn::get_type());
end

If any component uses


base_txn::type_id::create("txn"), it will create a
derived_txn object.
Helpful in testcase flexibility without modifying
code.
4. What is the purpose of uvm_config_db#(...)::set()
and get()? Explain with example.
Answer:
Used to pass configuration data between testbench
components using a central configuration
mechanism.

// Set in test
uvm_config_db#(int)::set(null, "env.agent",
"bus_width", 32);

// Get in agent
int bw;
if (!uvm_config_db#(int)::get(this, "", "bus_width",
bw))
`uvm_fatal("CFG", "Bus width not set")

set() → used by top-level components (like test)


get() → used by lower components (like agent,
driver)
This avoids tight coupling between testbench
components.
5. How is sequence layering implemented in
SystemVerilog/UVM?
Answer:
Sequence layering refers to starting one sequence
from another, often on different sequencers.

class top_seq extends uvm_sequence;


`uvm_object_utils(top_seq)
seq1 s1;
seq2 s2;

task body();
s1 = seq1::type_id::create("s1");
s2 = seq2::type_id::create("s2");

fork
s1.start(seqr1);
s2.start(seqr2);
join
endtask
endclass

Used in multi-protocol testbenches or layered


protocols (like TCP over IP).
Requires careful handling of arbitration and
virtual sequencers.
6. How to write parameterized classes in
SystemVerilog? Explain with an example.
Answer:

class fifo #(type T = int);


T mem[$];

function void write(T data);


mem.push_back(data);
endfunction

function T read();
return mem.pop_front();
endfunction
endclass

USAGE:

fifo#(bit [7:0]) byte_fifo = new();


byte_fifo.write(8'hA5);

Parameterized classes make code type-safe and


reusable.
7. How would you implement scoreboarding in a
reusable and scalable way in UVM?
Answer:
1. Use parameterized scoreboard base class
2. Add write_expected() and write_actual()
methods
3. Use uvm_tlm_analysis_fifo or analysis_export

class scoreboard #(type T = transaction) extends


uvm_component;
uvm_analysis_imp#(T, scoreboard) actual_export;
queue expected_q[$];

function void write(T tr);


if (expected_q.size() == 0)
`uvm_error("SB", "Unexpected transaction
received")
else if (!compare(tr, expected_q.pop_front()))
`uvm_error("SB", "Mismatch")
endfunction

function void add_expected(T tr);


expected_q.push_back(tr);
endfunction
endclass
8. Design a random packet generator using
constraints that ensures:
Packet size is between 64 and 1500.
Inter-packet gap is always ≥ 96-bit times.
Burst of 3-5 packets can be generated with
gaps

Answer:
class packet;
rand int size;
rand int gap;

constraint c_size {
size inside {[64:1500]};
}

constraint c_gap {
gap >= 96;
}

function void display();


$display("Packet Size = %0d, Inter-Packet Gap =
%0d", size, gap);
endfunction
endclass
class packet_burst;
packet pkts[5];
rand int burst_count;

constraint c_burst {
burst_count inside {[3:5]};
}

function void generate();


foreach(pkts[i])
pkts[i] = new();

assert(this.randomize());
for (int i = 0; i < burst_count; i++) begin
assert(pkts[i].randomize());
pkts[i].display();
end
endfunction
endclass

module tb;
initial begin
packet_burst burst = new();
burst.generate();
end
endmodule
9. Override base sequence with a derived one using
string names in the UVM factory. Show
instantiation using create().

Answer:
class base_seq extends uvm_sequence;
`uvm_object_utils(base_seq)
virtual task body(); $display("Base sequence");
endtask
endclass

class derived_seq extends base_seq;


`uvm_object_utils(derived_seq)
virtual task body(); $display("Derived sequence");
endtask
endclass

module tb;
initial begin
uvm_factory factory = uvm_factory::get();
factory.set_type_override_by_name("base_seq",
"derived_seq");

base_seq s;
s = base_seq::type_id::create("s"); // Actually
creates derived_seq
s.start(null); // No sequencer
end
endmodule

Uses string-based override.


Very useful when class names are
passed/configured externally (e.g., YAML or
JSON test configs).

10. Write a class to generate a MAC address where:


First byte must be even (multicast bit = 0).
Last byte must not be zero.

Answer:
class mac_addr;
rand bit [47:0] mac;

constraint c_first_byte_even {
mac[47:40] % 2 == 0;
}

constraint c_last_byte_non_zero {
mac[7:0] != 8'b0;
}
function void display();
$display("MAC =
%02h:%02h:%02h:%02h:%02h:%02h",
mac[47:40], mac[39:32], mac[31:24],
mac[23:16], mac[15:8], mac[7:0]);
endfunction
endclass

module tb;
initial begin
mac_addr m = new();
repeat (5) begin
assert(m.randomize());
m.display();
end
end
endmodule

Shows bit slicing and constraints on parts of a


rand variable.
Useful in L2 protocol testing or Ethernet MAC
layer validation.
11. Implement a scoreboard that matches outputs
from two FIFOs, compares the result, and reports
mismatches.

Answer:
class transaction;
rand bit [7:0] data;
function bit compare(transaction t);
return (this.data == t.data);
endfunction
endclass

class scoreboard;
mailbox #(transaction) fifo1, fifo2;

function new();
fifo1 = new();
fifo2 = new();
endfunction

task run();
transaction t1, t2;
forever begin
fifo1.get(t1);
fifo2.get(t2);
if (!t1.compare(t2))
$display("Mismatch! FIFO1=%0h FIFO2=%0h",
t1.data, t2.data);
else
$display("Match: %0h", t1.data);
end
endtask
endclass

module tb;
scoreboard sb = new();

initial fork
sb.run();
begin
transaction t;
repeat (5) begin
t = new();
assert(t.randomize());
sb.fifo1.put(t);
sb.fifo2.put(t); // Can change one to make
mismatch
end
end
join
endmodule
12. Write a test where a virtual sequencer
coordinates two physical sequencers to drive
different sequences.

Answer:
class virtual_seqr extends uvm_sequencer;
`uvm_component_utils(virtual_seqr)
phy_seqr seqr1;
phy_seqr seqr2;
endclass

class coordinated_seq extends uvm_sequence;


`uvm_object_utils(coordinated_seq)
virtual task body();
simple_seq s1 = simple_seq::type_id::create("s1");
simple_seq s2 = simple_seq::type_id::create("s2");
s1.start(p_sequencer.seqr1);
s2.start(p_sequencer.seqr2);
endtask
endclass

Introduces the virtual sequencer pattern,


essential in UVM multi-agent testbenches.
Helps in layered and coordinated transactions.
13. Write a class that randomizes:
Two variables a and b, where:
a ∈ [0:10]
b ∈ [a+1:20]
Use solve ... before ... to ensure correct
constraint dependency order.

Answer:
class dependency_example;
rand int a, b;

constraint c_range {
a inside {[0:10]};
b inside {[a+1:20]};
solve a before b; // Ensure a is solved before b
}

function void display();


$display("a = %0d, b = %0d", a, b);
endfunction
endclass

module tb;
initial begin
dependency_example ex = new();
repeat (5) begin
assert(ex.randomize());
ex.display();
end
end
endmodule

This tests understanding of constraint


dependency ordering, which is critical in
avoiding constraint solver failures in complex
hierarchies.

14. Show how to call a SystemVerilog function


from C using DPI-C import/export, to simulate a
call-back scenario.

Asnwer:
// SystemVerilog Code:

export "DPI-C" function sv_callback;


function void sv_callback(int val);
$display("From SystemVerilog: Received from C =
%0d", val);
endfunction
import "DPI-C" function void call_sv_callback();
module tb;
initial begin
call_sv_callback();
end
endmodule

// C Code
#include <svdpi.h>
#include <stdio.h>
extern void sv_callback(int val);
void call_sv_callback() {
sv_callback(1234); // Call into SV from C

This demonstrates SystemVerilog callbacks from C,


useful in:
C-model simulation
Performance-critical IP verification
Co-simulation environments
15. Implement a simple AXI-Lite monitor which uses
both address and data channels and combines
them to reconstruct transactions.

Answer:
class axi_lite_monitor extends uvm_component;
uvm_analysis_port #(axi_txn) ap;

mailbox #(axi_addr_phase) addr_mbx;


mailbox #(axi_data_phase) data_mbx;

function new(string name, uvm_component


parent);
super.new(name, parent);
ap = new("ap", this);
addr_mbx = new(); data_mbx = new();
endfunction

task run_phase(uvm_phase phase);


axi_txn t;
axi_addr_phase addr;
axi_data_phase data;
forever begin
addr_mbx.get(addr);
data_mbx.get(data);
t = new();
t.addr = addr.addr;
t.data = data.data;
ap.write(t);
end
endtask
endclass

This tests deep understanding of multi-channel


protocols, and how to recombine transactions.
Required for building accurate monitors and
scoreboard models in UVM.

16. Create a functional coverage model with


dynamic bins based on user-defined range.

Answer:
class cov;
rand int low = 10;
rand int high = 50;
covergroup cg;
coverpoint var {
bins dynamic_bins[] = {[low:high]};
}
endgroup
int var;
function new();
cg = new();
endfunction

task sample(int v);


var = v;
cg.sample();
endtask
endclass

module tb;
initial begin
cov c = new();
for (int i = 10; i <= 50; i += 5)
c.sample(i);
end
endmodule

Advanced coverage model: bins created


dynamically from random variables.
Important in adaptive coverage strategies in
constraint-random testing.
17. Write a top virtual sequence that starts a TCP
sequence and waits for an IP sequence to finish.
Emulate inter-sequence synchronization.

Answer:
class top_seq extends uvm_sequence;
virtual_sequencer vseqr;

`uvm_object_utils(top_seq)
function new(string name = "top_seq");
super.new(name);
endfunction

task body();
ip_seq ip = ip_seq::type_id::create("ip");
tcp_seq tcp = tcp_seq::type_id::create("tcp");

fork
begin
`uvm_info("TOPSEQ", "Starting TCP",
UVM_MEDIUM)
tcp.start(vseqr.tcp_sequencer);
end
begin
`uvm_info("TOPSEQ", "Starting IP",
UVM_MEDIUM)
ip.start(vseqr.ip_sequencer);
end
join_any

wait (ip.done); // assume .done is triggered by


ip_seq
`uvm_info("TOPSEQ", "IP finished, continuing
TCP", UVM_LOW)
endtask
endclass

Tests advanced knowledge of parallel sequence


execution, inter-sequence synchronization, and
protocol layering.
Essential in SoC-level testbenches for multi-
layer packet processing (e.g., TCP/IP, USB
stacks).

18. Create a function that spawns a task with a


time limit (timeout). If the task doesn't finish in
time, terminate it.

Answer:
task automatic with_timeout(task automatic input
void_func(), time timeout);
process p;
p = process::create("task_proc", void_func);
p.resume();

fork
begin
#timeout;
if (!p.status() == process::FINISHED) begin
$display("Timeout! Killing task...");
p.kill();
end
end
join_none
wait fork;
endtask

task automatic some_long_task();


#100ns;
$display("Task completed");
endtask

module tb;
initial begin
with_timeout(some_long_task, 50ns); // Should
timeout and kill
end
endmodule
19. Implement a blocking thread-safe queue using
semaphores. Multiple producers/consumers should
safely enqueue and dequeue data.

Answer:
class thread_safe_queue #(type T=int);
semaphore sem;
T data_q[$];
function new();
sem = new(1); // Binary semaphore
endfunction
task enqueue(T val);
sem.get(); // Lock
data_q.push_back(val);
sem.put(); // Unlock
endtask

task dequeue(output T val);


sem.get();
if (data_q.size() == 0)
$fatal("Queue Empty!");
val = data_q.pop_front();
sem.put();
endtask
endclass
initial fork
q.enqueue(10);
q.enqueue(20);
begin
int d;
#5;
q.dequeue(d);
$display("Dequeued: %0d", d);
end
join
endmodule

Demonstrates explicit thread synchronization


using semaphore.
Useful in modeling mutex-style testbench
structures or simulated RTOS queues.

20. Write a scenario with fork-join_any, but stop all


parallel threads once a task completes, using
disable fork.

Answer:
module tb;
task task_a(); #50; $display("Task A"); endtask
task task_b(); #100; $display("Task B"); endtask
task task_c(); #150; $display("Task C"); endtask
initial begin
fork
begin task_a(); disable fork; end // completes
in 50ns
task_b(); // will be killed
task_c(); // will be killed
join_any
$display("Only Task A completed");
end
endmodule

Advanced fork-join control with disable fork to


kill sibling processes — heavily used in timeout,
early-exit test environments.

21. Create a simple Least Recently Used (LRU)


cache of fixed size using a queue of class handles.

Answer:
class CacheEntry;
int key, value;
function new(int k, int v);
key = k; value = v;
endfunction
endclass
class LRUCache;
int max_size;
CacheEntry q[$];

function new(int size); max_size = size;


endfunction

function void access(int k, int v);


foreach (q[i])
if (q[i].key == k) begin
q.delete(i); // Move to back
break;
end
q.push_back(new(k, v));
if (q.size() > max_size)
q.pop_front(); // Remove LRU
endfunction

function void display();


foreach (q[i])
$display("Key: %0d, Value: %0d", q[i].key,
q[i].value);
endfunction
endclass
module tb;
initial begin
LRUCache c = new(3);
c.access(1,10); c.access(2,20); c.access(3,30);
c.access(2,22); c.access(4,40); // Now 1 is evicted
c.display();
end
endmodule

Strong example of OOP + data structure logic


in SV.
Demonstrates ability to use queues, class
handles, and indexing together — often asked
to test algorithmic thinking in SV.

22. Write a SystemVerilog assertion to detect if a


signal sig goes high for less than 2 ns (i.e., a glitch).

Answer:
module tb;
logic clk, sig;
always #1 clk = ~clk;
initial begin
sig = 0;
#3 sig = 1;
#1 sig = 0; // 1ns pulse
#10 $finish;
end

// Assertion: detect pulse < 2ns


property glitch_check;
@(posedge clk)
$rose(sig) |=> ##[0:1] $fell(sig);
endproperty

assert property (glitch_check)


else $error("Glitch detected on sig (pulse <
2ns)");
endmodule

This tests temporal logic and cycle-accurate


pattern detection.
Real-world use: glitch detection, metastability
bugs, or protocol violations.
23. Demonstrate runtime polymorphism in
SystemVerilog where a parent handle dynamically
calls different overridden methods.

Answer:
class Packet;
virtual function void send();
$display("Sending base Packet");
endfunction
endclass

class TCPPacket extends Packet;


function void send();
$display("Sending TCP Packet");
endfunction
endclass

class UDPPacket extends Packet;


function void send();
$display("Sending UDP Packet");
endfunction
endclass

module tb;
Packet p;
initial begin
p = new TCPPacket();
p.send(); // Should call TCP

p = new UDPPacket();
p.send(); // Should call UDP
end
endmodule

Tests your understanding of polymorphism,


dynamic dispatch, and OOP structure in
testbenches.
Commonly used in UVM factories, sequence
selection, and layered verification.

24. Create a constrained random class where each


object has a parent reference and the child’s ID
must be greater than the parent’s.

Answer:
class Node;
rand int id;
Node parent;
constraint id_c {
if (parent != null)
id > parent.id;
}
function new(Node p = null);
parent = p;
endfunction
endclass

module tb;
initial begin
Node root = new();
assert(root.randomize() with {id inside {[1:10]};});

Node child = new(root);


assert(child.randomize());

$display("Parent ID: %0d, Child ID: %0d", root.id,


child.id);
end
endmodule

Demonstrates constraint inheritance across


object hierarchies and conditional constraints
using handles.
25. Randomize an int associative array with string
keys such that:
At least 3 keys
All values are even and unique

Answer:
class AssocArrayRand;
rand int aa[string];

constraint key_count { aa.num() >= 3; }


constraint value_even {
foreach (aa[k]) aa[k] % 2 == 0;
}
constraint unique_vals {
foreach (aa[k1]) foreach (aa[k2])
if (k1 != k2) aa[k1] != aa[k2];
}

function void display();


foreach (aa[k])
$display("Key = %s, Val = %0d", k, aa[k]);
endfunction
endclass

module tb;
initial begin
AssocArrayRand obj = new();
// Pre-fill keys
obj.aa["a"]; obj.aa["b"]; obj.aa["c"]; obj.aa["d"];
assert(obj.randomize());
obj.display();
end
endmodule

Rare use case of randomizing associative arrays,


showcasing deep understanding of constraint
modeling.

26. Write an SVA that checks if signal sig toggles


more than 2 times within any 5-clock-cycle window.

Answer:
property too_many_toggles;
int toggle_count = 0;
@(posedge clk)
(1, toggle_count = 0) ##[0:4]
(sig != $past(sig)) |=> (toggle_count += 1)
##0 (toggle_count > 2);
endproperty

assert property (too_many_toggles)


else $error("Signal toggled >2 times in 5 cycles");
27. Create your own mini factory class to register
and create types by name (like UVM factory but
custom).

Answer:
class Factory;
typedef string TypeName;
typedef PacketCtor* Creator;

static associative array [TypeName] of Creator


registry;

static function void register(TypeName tname,


Creator fn);
registry[tname] = fn;
endfunction

static function Packet create(TypeName tname);


if (!registry.exists(tname))
$fatal("Type %s not registered", tname);
return registry[tname]();
endfunction
endclass

function Packet TCP_ctor(); return new TCPPacket();


endfunction
function Packet UDP_ctor(); return new UDPPacket();
endfunction

module tb;
initial begin
Factory::register("TCP", TCP_ctor);
Factory::register("UDP", UDP_ctor);

Packet pkt1 = Factory::create("TCP");


pkt1.send(); // TCP Packet

Packet pkt2 = Factory::create("UDP");


pkt2.send(); // UDP Packet
end
endmodule

Shows how to replicate UVM-like factory


behavior from scratch using typedefs, function
pointers, and static arrays.
Highly useful to test abstraction and meta-
programming capabilities.
28. Simulate a 3-level priority arbiter using
mailboxes. The highest non-empty priority must
always be served first.

Answer:
module tb;
mailbox high = new();
mailbox mid = new();
mailbox low = new();

task automatic arbiter();


int data;
forever begin
if (high.num() > 0)
high.get(data);
else if (mid.num() > 0)
mid.get(data);
else if (low.num() > 0)
low.get(data);
else
#1; // Idle

$display("Time %0t: Served %0d", $time, data);


end
endtask
initial fork
arbiter();

begin // Inject jobs


#5 low.put(100);
#6 mid.put(200);
#7 high.put(300); // Served first!
#20 $finish;
end
join
endmodule

Prioritized arbitration is critical in DV for


scheduling transactions or controlling stimulus
flow.
Demonstrates mailbox-based scheduling, useful
for bus arbitration models or testbench
schedulers.
29. Create a class that holds a dynamic array of
class objects, where each inner object must satisfy
constraints based on its index.

Answer;
class Element;
rand int val;

constraint c_valid {
val >= 0;
}
endclass

class Container;
rand Element elems[];
int size;

function new(int sz); size = sz; endfunction

constraint c_len { elems.size() == size; }

constraint indexed {
foreach (elems[i]) elems[i].val == i * 10;
}
endclass
module tb;
initial begin
Container c = new(5);
assert(c.randomize());
foreach (c.elems[i])
$display("Elem[%0d] = %0d", i, c.elems[i].val);
end
endmodule

Advanced question about nested constraint


handling and array-of-objects.
Often used in packetized data modeling,
memory maps, or protocol sequence creation.

30. Demonstrate how to bind a virtual interface


from top module into a testbench class using a
config-like mechanism.

Answer:
interface bus_if(input bit clk);
logic req, gnt;
endinterface

class BusDriver;
virtual bus_if vif;
function new(virtual bus_if vif);
this.vif = vif;
endfunction

task drive();
@(posedge vif.clk);
vif.req <= 1;
@(posedge vif.clk);
vif.req <= 0;
endtask
endclass

module tb;
logic clk = 0;
always #5 clk = ~clk;

bus_if b(clk);
BusDriver drv;

initial begin
drv = new(b);
fork drv.drive(); join_none
#50 $finish;
end
endmodule
31. Extend a base class to include a typedef struct
that must be randomly assigned within range.

Answer:
typedef struct {
int id;
int level;
} Job;

class Base;
rand Job j;

constraint c_range {
j.id inside {[1:10]};
j.level inside {[0:3]};
}
endclass

class Sub extends Base;


constraint sub_c {
j.level != 2;
}
endclass

module tb;
initial begin
Sub s = new();
assert(s.randomize());
$display("ID: %0d, Level: %0d", s.j.id, s.j.level);
end
endmodule

Rare use of randomizable structs inside class


with inherited constraints.
Powerful for modeling packet headers, protocol
formats, registers, etc.

32. Create a CDC monitor that checks if a signal sig


sampled on clk2 had a clean transition from clk1.

Answer:
module cdc_monitor(input clk1, clk2, sig);
logic sync1, sync2;
always_ff @(posedge clk2) begin
sync1 <= sig;
sync2 <= sync1;
end
property no_glitch;
@(posedge clk2) disable iff (!reset)
$stable(sync2) || $changed(sync2);
endproperty
assert property(no_glitch)
else $error("CDC Glitch Detected at time %0t",
$time);

endmodule

This is gold-level verification logic, modeling


synchronizer checks using assertions.
Such questions evaluate ability to design
assertion-based CDC checks — very high signal
integrity expertise.
ADVANCED & TRICKY INTERVIEW QUESTIONS
1. Override a constraint from the base class in the
derived class such that the derived class object
follows different rules.
2. Design a sequence that triggers itself
recursively using mailbox + fork-join_none.
3. Randomize an associative array such that:
5 keys starting with 'pkt_0' to 'pkt_4'
All values are multiple of 3
No values repeat
4. Write an assertion that detects if data toggles
more than once in a single clock cycle.
5. Create a covergroup that checks all 1-hot
patterns of a 4-bit signal.
6. Create a FSM with 3 states, where each state
waits random cycles (1–5) before moving on.
7. Simulate weighted randomization (90% chance
of 1, 10% of 0) without using dist keyword.
8. Check if bus req stays high but grant is never
asserted within 5 cycles → raise error.
9. Replicate UVM-style factory without UVM.
Register class constructors and call them
dynamically.
Excellence in World class
VLSI Training & Placements

Do follow for updates & enquires

+91- 9182280927

You might also like