Advanced VLSI Verification Interview Handbook
Advanced VLSI Verification Interview Handbook
VERIFICATION
INTERVIEW HANDBOOK
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;
function new();
cg = new();
endfunction
endclass
initial begin
factory.set_type_override_by_type(base_txn::get_ty
pe(), derived_txn::get_type());
end
// 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")
task body();
s1 = seq1::type_id::create("s1");
s2 = seq2::type_id::create("s2");
fork
s1.start(seqr1);
s2.start(seqr2);
join
endtask
endclass
function T read();
return mem.pop_front();
endfunction
endclass
USAGE:
Answer:
class packet;
rand int size;
rand int gap;
constraint c_size {
size inside {[64:1500]};
}
constraint c_gap {
gap >= 96;
}
constraint c_burst {
burst_count inside {[3:5]};
}
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
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
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
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
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
}
module tb;
initial begin
dependency_example ex = new();
repeat (5) begin
assert(ex.randomize());
ex.display();
end
end
endmodule
Asnwer:
// SystemVerilog Code:
// 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
Answer:
class axi_lite_monitor extends uvm_component;
uvm_analysis_port #(axi_txn) ap;
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
module tb;
initial begin
cov c = new();
for (int i = 10; i <= 50; i += 5)
c.sample(i);
end
endmodule
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
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
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
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
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[$];
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
Answer:
class Packet;
virtual function void send();
$display("Sending base 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
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]};});
Answer:
class AssocArrayRand;
rand int aa[string];
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
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
Answer:
class Factory;
typedef string TypeName;
typedef PacketCtor* Creator;
module tb;
initial begin
Factory::register("TCP", TCP_ctor);
Factory::register("UDP", UDP_ctor);
Answer:
module tb;
mailbox high = new();
mailbox mid = new();
mailbox low = new();
Answer;
class Element;
rand int val;
constraint c_valid {
val >= 0;
}
endclass
class Container;
rand Element elems[];
int 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
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
module tb;
initial begin
Sub s = new();
assert(s.randomize());
$display("ID: %0d, Level: %0d", s.j.id, s.j.level);
end
endmodule
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
+91- 9182280927