SystemVerilog & Verilog Tricky Interview Questions – Part 4

UVM & Advanced Verification – VLSI Company Focus

📌 Question 76: UVM Factory Override Scope

class base_driver extends uvm_driver#(base_transaction);
    `uvm_component_utils(base_driver)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
endclass

class ext_driver extends base_driver;
    `uvm_component_utils(ext_driver)
    function new(string name, uvm_component parent);
        super.new(name, parent);
    endfunction
endclass

class my_test extends uvm_test;
    `uvm_component_utils(my_test)
    
    function void build_phase(uvm_phase phase);
        // Type override
        base_driver::type_id::set_type_override(ext_driver::get_type());
        
        // Instance override
        set_inst_override_by_type("env.agent1.*", 
            base_driver::get_type(), ext_driver::get_type());
    endfunction
endclass

Question: Which agents will use ext_driver?

Answer:

  • All agents use ext_driver (type override is global)
  • Instance override has no effect (type override takes precedence)

Explanation:

  • Type override affects all instances globally
  • Applied first, affects entire hierarchy
  • Instance override only affects specific paths
  • Type override already changed the type, so instance override sees ext_driver
  • Order matters: Last override wins
  • Most specific (instance) should be done after type override
  • Common interview trap at Intel, NVIDIA

📌 Question 77: UVM Config DB Get Timing

class my_agent extends uvm_agent;
    `uvm_component_utils(my_agent)
    
    int num_drivers;
    
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        
        if (!uvm_config_db#(int)::get(this, "", "num_drivers", num_drivers)) begin
            `uvm_info("AGENT", "Using default num_drivers = 1", UVM_LOW)
            num_drivers = 1;
        end
        
        // Create drivers based on num_drivers
    endfunction
endclass

class my_test extends uvm_test;
    my_agent agent;
    
    function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        agent = my_agent::type_id::create("agent", this);
        
        // Set config AFTER creating agent - will it work?
        uvm_config_db#(int)::set(this, "agent", "num_drivers", 4);
    endfunction
endclass

Question: Will agent get num_drivers = 4?

Answer: NO! Agent gets default value 1

Explanation:

  • config_db::set must happen BEFORE component creation
  • create() calls build_phase immediately
  • Agent’s build_phase runs before the set
  • Correct order:
uvm_config_db#(int)::set(this, "agent", "num_drivers", 4);
agent = my_agent::type_id::create("agent", this);
  • Common mistake in Qualcomm, Broadcom interviews
  • Hierarchy matters: Set from parent before creating child

📌 Question 78: Sequence Item Race Condition

class my_sequence extends uvm_sequence#(my_transaction);
    `uvm_object_utils(my_sequence)
    
    task body();
        my_transaction req;
        
        req = my_transaction::type_id::create("req");
        start_item(req);
        assert(req.randomize() with {addr < 100;});
        finish_item(req);
        
        // Use req again - PROBLEM!
        start_item(req);
        assert(req.randomize() with {addr >= 100;});
        finish_item(req);
    endtask
endclass

Question: What’s wrong with reusing req?

Answer: Race condition – req is modified by driver!

Explanation:

  • After finish_item(), driver gets req handle
  • Driver may modify req (set status, response, etc.)
  • Reusing same handle creates race:
    • Sequence modifies req
    • Driver might still be processing previous req
  • Correct approach:
req = my_transaction::type_id::create("req");
start_item(req);
// ...
finish_item(req);

req = my_transaction::type_id::create("req");  // NEW object
start_item(req);
// ...
  • Asked at AMD, Marvell

📌 Question 79: Virtual Sequence Hierarchy

class base_vseq extends uvm_sequence#(uvm_sequence_item);
    `uvm_object_utils(base_vseq)
    
    my_sequencer seqr1, seqr2;
    
    task body();
        // How to get sequencer handles?
        // p_sequencer is uvm_sequencer#(uvm_sequence_item)!
    endtask
endclass

Question: How to access specific sequencers in virtual sequence?

Answer: Use config_DB or declare typed p_sequencer

Explanation:
Method 1: Config DB

task body();
    if (!uvm_config_db#(my_sequencer)::get(null, "", "seqr1", seqr1))
        `uvm_fatal("VSEQ", "Cannot get seqr1")
    // Now use seqr1
endtask

Method 2: Typed p_sequencer (Better)

class my_virtual_sequencer extends uvm_sequencer#(uvm_sequence_item);
    my_sequencer seqr1, seqr2;
    `uvm_component_utils(my_virtual_sequencer)
endclass

class base_vseq extends uvm_sequence#(uvm_sequence_item);
    `uvm_object_utils(base_vseq)
    `uvm_declare_p_sequencer(my_virtual_sequencer)
    
    task body();
        // Now p_sequencer is typed!
        my_seq seq = my_seq::type_id::create("seq");
        seq.start(p_sequencer.seqr1);
    endtask
endclass
  • Critical for Synopsys, Cadence interviews

📌 Question 80: TLM Analysis Port Fanout

class my_monitor extends uvm_monitor;
    uvm_analysis_port#(my_transaction) ap;
    
    task run_phase(uvm_phase phase);
        my_transaction tr;
        forever begin
            // Collect transaction
            tr = my_transaction::type_id::create("tr");
            collect_transaction(tr);
            ap.write(tr);  // Broadcast
        end
    endtask
endclass

class subscriber1 extends uvm_subscriber#(my_transaction);
    function void write(my_transaction t);
        t.data = 100;  // MODIFY transaction!
    endfunction
endclass

class subscriber2 extends uvm_subscriber#(my_transaction);
    function void write(my_transaction t);
        `uvm_info("SUB2", $sformatf("data = %0d", t.data), UVM_LOW)
    endfunction
endclass

Question: What does subscriber2 see?

Answer: data = 100 (modified value!)

Explanation:

  • Analysis port broadcasts SAME handle to all subscribers
  • All subscribers get reference to same object
  • subscriber1 modifies shared object
  • subscriber2 sees modified value
  • Calling order not guaranteed (could see 100 or original)
  • Solution: Clone transaction before writing
my_transaction tr_clone;
$cast(tr_clone, tr.clone());
ap.write(tr_clone);
  • Common bug in Apple, Google verification

📌 Question 81: Sequence Priority and Arbitration

class my_sequencer extends uvm_sequencer#(my_transaction);
    `uvm_component_utils(my_sequencer)
endclass

class my_test extends uvm_test;
    task run_phase(uvm_phase phase);
        phase.raise_objection(this);
        
        fork
            begin
                seq1.start(seqr, null, 100);  // Priority 100
            end
            begin
                seq2.start(seqr, null, 200);  // Priority 200
            end
            begin
                seq3.start(seqr, null, 100);  // Priority 100
            end
        join
        
        phase.drop_objection(this);
    endtask
endclass

Question: Which sequence gets serviced first?

Answer: seq2 (highest priority 200)

Explanation:

  • Sequencer arbitrates by priority
  • Higher number = higher priority
  • seq2 (200) > seq1,seq3 (100)
  • Among equal priorities (seq1, seq3): FIFO order
  • seq1 started first, so seq1 before seq3
  • Execution order: seq2 → seq1 → seq3
  • Default priority = 100
  • Asked at Texas Instruments, NXP

📌 Question 82: Phase Jump and Objections

class my_test extends uvm_test;
    task main_phase(uvm_phase phase);
        phase.raise_objection(this);
        #100;
        phase.drop_objection(this);
    endtask
    
    task shutdown_phase(uvm_phase phase);
        phase.raise_objection(this);
        #50;
        phase.drop_objection(this);
    endtask
endclass

class my_env extends uvm_env;
    task main_phase(uvm_phase phase);
        phase.raise_objection(this);
        #200;  // Takes longer than test!
        phase.drop_objection(this);
    endtask
endclass

Question: When does main_phase end?

Answer: After 200ns (waits for env to finish)

Explanation:

  • Phase ends when ALL objections dropped
  • test drops at 100ns
  • env still has objection until 200ns
  • Phase waits for env to finish
  • Hierarchical objection counting
  • All components must drop before phase advances
  • Common issue at Xilinx, Altera/Intel

📌 Question 83: Callback Execution Order

class driver_cb extends uvm_callback;
    virtual task pre_send(my_driver drv, my_transaction tr);
        `uvm_info("CB", "Callback executed", UVM_LOW)
    endtask
endclass

class my_driver extends uvm_driver#(my_transaction);
    `uvm_register_cb(my_driver, driver_cb)
    
    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            `uvm_do_callbacks(my_driver, driver_cb, pre_send(this, req))
            drive_transaction(req);
            seq_item_port.item_done();
        end
    endtask
endclass

// Add callbacks in test
driver_cb cb1 = new();
driver_cb cb2 = new();
uvm_callbacks#(my_driver, driver_cb)::add(drv, cb1);
uvm_callbacks#(my_driver, driver_cb)::add(drv, cb2);

Question: Which callback executes first?

Answer: cb1 executes first (FIFO order)

Explanation:

  • Callbacks execute in order added
  • cb1 added first → executes first
  • FIFO queue of callbacks
  • Can use add_by_name for different ordering
  • delete to remove callbacks
  • Asked at Arm, Samsung

📌 Question 84: Packed vs Unpacked Array Randomization

class my_transaction extends uvm_sequence_item;
    rand bit [7:0] packed_arr [4];    // Unpacked array of packed
    rand bit unpacked_arr [4][7:0];   // Unpacked only
    
    `uvm_object_utils_begin(my_transaction)
        `uvm_field_sarray_int(packed_arr, UVM_DEFAULT)
        // unpacked_arr - NO FIELD MACRO!
    `uvm_object_utils_end
endclass

Question: What gets randomized?

Answer: Only packed_arr gets randomized properly

Explanation:

  • rand keyword works on both
  • Field macros only support packed
  • packed_arr: Works for copy/compare/print/randomize
  • unpacked_arr: Randomizes but no copy/compare/print support
  • Limitation: Field macros don’t support 2D unpacked
  • Workaround: Custom do_copy, do_compare
function void do_copy(uvm_object rhs);
    my_transaction rhs_;
    $cast(rhs_, rhs);
    foreach(unpacked_arr[i,j])
        unpacked_arr[i][j] = rhs_.unpacked_arr[i][j];
endfunction
  • Important for Nvidia, Intel verification

📌 Question 85: UVM Resource DB vs Config DB

class my_test extends uvm_test;
    function void build_phase(uvm_phase phase);
        // Using config_db
        uvm_config_db#(int)::set(this, "*", "timeout", 1000);
        
        // Using resource_db
        uvm_resource_db#(int)::set("*", "timeout", 2000);
        
        // Which one wins?
    endfunction
endclass

class my_component extends uvm_component;
    int timeout;
    
    function void build_phase(uvm_phase phase);
        if (!uvm_config_db#(int)::get(this, "", "timeout", timeout))
            `uvm_error("COMP", "Cannot get timeout")
    endfunction
endclass

Question: What value does component get?

Answer: timeout = 1000 (config_db wins)

Explanation:

  • config_db has higher priority than resource_db
  • config_db::get searches:
    1. config_db first
    2. resource_db second (if not found)
  • Here config_db has entry → returns 1000
  • Best practice: Use config_db consistently
  • resource_db is lower level, config_db built on top
  • Mixing both can cause confusion
  • Asked at Synopsys, Mentor/Siemens

📌 Question 86: Sequence Lock/Unlock

class my_sequence extends uvm_sequence#(my_transaction);
    task body();
        lock(m_sequencer);
        
        repeat(5) begin
            `uvm_do(req)
        end
        
        unlock(m_sequencer);
    endtask
endclass

// Two instances run in parallel
fork
    seq1.start(seqr);
    seq2.start(seqr);
join

Question: How do sequences execute?

Answer: seq1 runs 5 transactions, THEN seq2 runs

Explanation:

  • lock() grants exclusive access to sequencer
  • seq1 locks sequencer
  • seq2 waits at lock() call
  • seq1 executes all 5 transactions
  • seq1 unlocks
  • seq2 gets lock and executes
  • Serializes access – useful for atomic operations
  • grab() is similar but with priority
  • Important for Qualcomm, Broadcom protocols

📌 Question 87: Field Macro Flags Impact

class my_transaction extends uvm_sequence_item;
    rand bit [7:0] addr;
    rand bit [31:0] data;
    bit [7:0] status;  // Not rand
    
    `uvm_object_utils_begin(my_transaction)
        `uvm_field_int(addr, UVM_DEFAULT)
        `uvm_field_int(data, UVM_DEFAULT | UVM_NOCOMPARE)
        `uvm_field_int(status, UVM_DEFAULT | UVM_NOPACK)
    `uvm_object_utils_end
endclass

my_transaction tr1, tr2;
tr1.addr = 10; tr1.data = 100; tr1.status = 5;
tr2.addr = 10; tr2.data = 200; tr2.status = 5;

if (tr1.compare(tr2))
    $display("Match");

Question: Do they compare as equal?

Answer: YES – Match!

Explanation:

  • UVM_NOCOMPARE excludes data from comparison
  • compare() checks: addr and status only
  • addr: 10 == 10 IGNORED (UVM_NOCOMPARE)
  • status: 5 == 5 âś“
  • Result: Match
  • Other flags:
    • UVM_NOPRINT – exclude from print
    • UVM_NOCOPY – exclude from copy
    • UVM_NOPACK – exclude from pack/unpack
  • Asked at Apple, Amazon

📌 Question 88: Virtual Interface Assignment Timing

interface my_if(input logic clk);
    logic [7:0] data;
    logic valid;
endinterface

module top;
    logic clk;
    my_if if1(clk), if2(clk);
    
    initial begin
        uvm_config_db#(virtual my_if)::set(null, "*", "vif", if1);
        run_test();
    end
endmodule

class my_driver extends uvm_driver#(my_transaction);
    virtual my_if vif;
    
    function void build_phase(uvm_phase phase);
        if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
            `uvm_fatal("DRV", "Cannot get vif")
    endfunction
    
    function void connect_phase(uvm_phase phase);
        // Can we reassign vif here?
        uvm_config_db#(virtual my_if)::get(this, "", "vif", vif);
    endfunction
endclass

Question: Can vif change between build and connect phase?

Answer: YES – if config_db updated in between!

Explanation:

  • Virtual interface is handle/reference
  • Can be reassigned anytime
  • If another component does:
function void build_phase(uvm_phase phase);
    uvm_config_db#(virtual my_if)::set(null, "*", "vif", if2);
endfunction
  • Later components see new value
  • Last set wins in config_db
  • Best practice: Set once in top, before run_test()
  • Common pitfall at Marvell, Microchip

📌 Question 89: Scoreboard Analysis Export Connection

class my_scoreboard extends uvm_scoreboard;
    uvm_analysis_export#(my_transaction) actual_export;
    uvm_analysis_export#(my_transaction) expect_export;
    
    `uvm_component_utils(my_scoreboard)
    
    function void build_phase(uvm_phase phase);
        actual_export = new("actual_export", this);
        expect_export = new("expect_export", this);
    endfunction
    
    // How to implement write()?
endclass

Question: How to implement write() for exports?

Answer: Use analysis imp or analysis fifo

Explanation:
Method 1: Use TLM FIFOs

uvm_tlm_analysis_fifo#(my_transaction) actual_fifo, expect_fifo;

function void build_phase(uvm_phase phase);
    actual_fifo = new("actual_fifo", this);
    expect_fifo = new("expect_fifo", this);
    actual_export = new("actual_export", this);
    expect_export = new("expect_export", this);
endfunction

function void connect_phase(uvm_phase phase);
    actual_export.connect(actual_fifo.analysis_export);
    expect_export.connect(expect_fifo.analysis_export);
endfunction

task run_phase(uvm_phase phase);
    forever begin
        actual_fifo.get(actual_tr);
        expect_fifo.get(expect_tr);
        compare(actual_tr, expect_tr);
    end
endtask

Method 2: Use imp with macros

`uvm_analysis_imp_decl(_actual)
`uvm_analysis_imp_decl(_expect)

uvm_analysis_imp_actual#(my_transaction, my_scoreboard) actual_imp;
uvm_analysis_imp_expect#(my_transaction, my_scoreboard) expect_imp;

function void write_actual(my_transaction t);
    // Handle actual transaction
endfunction

function void write_expect(my_transaction t);
    // Handle expected transaction
endfunction
  • Critical for verification engineer roles

📌 Question 90: Randomization with Inside Operator

class my_transaction extends uvm_sequence_item;
    rand bit [7:0] addr;
    rand bit [3:0] cmd;
    
    constraint c1 {
        addr inside {[0:10], [20:30], 100};
    }
    
    constraint c2 {
        !(cmd inside {4'h0, 4'hF});
    }
endclass

Question: What values can addr and cmd have?

Answer:

  • addr: 0-10, 20-30, or exactly 100
  • cmd: 1-14 (all except 0 and 15)

Explanation:

  • inside with ranges: [0:10] means 0,1,2…10
  • Multiple ranges: union of all ranges
  • Single value: exactly that value
  • !(inside): negation excludes those values
  • cmd can be: 1,2,3,4,5,6,7,8,9,10,11,12,13,14
  • More efficient than individual constraints
  • Common at Intel, AMD interviews

📌 Question 91: UVM Report Catcher

class my_catcher extends uvm_report_catcher;
    function action_e catch();
        if (get_severity() == UVM_ERROR && 
            get_id() == "TIMEOUT") begin
            set_severity(UVM_WARNING);
            return CAUGHT;
        end
        return THROW;
    endfunction
endclass

class my_test extends uvm_test;
    function void build_phase(uvm_phase phase);
        my_catcher catcher = new();
        uvm_report_cb::add(null, catcher);  // Global
    endfunction
    
    task run_phase(uvm_phase phase);
        `uvm_error("TIMEOUT", "Operation timed out")
        `uvm_error("CONFIG", "Configuration error")
    endtask
endclass

Question: What happens to the errors?

Answer:

  • TIMEOUT: Downgraded to WARNING
  • CONFIG: Remains ERROR

Explanation:

  • Report catcher intercepts messages
  • Can modify: severity, verbosity, action
  • TIMEOUT error caught → changed to WARNING → CAUGHT
  • CONFIG error → no match → THROW (passes through)
  • CAUGHT: Message handled, don’t propagate
  • THROW: Pass to next catcher or default handler
  • Useful for known issues or debug control
  • Asked at Synopsys, Cadence

📌 Question 92: Constraint Solver Iteration Order

class my_packet extends uvm_sequence_item;
    rand bit [7:0] len;
    rand bit [7:0] data[];
    rand bit [7:0] checksum;
    
    constraint c_size {
        len inside {[1:10]};
        data.size() == len;
    }
    
    constraint c_checksum {
        checksum == calculate_checksum(data);
    }
    
    function bit [7:0] calculate_checksum(bit [7:0] arr[]);
        // Sum all data bytes
    endfunction
endclass

Question: Will this constraint work?

Answer: NO – Solver cannot call function!

Explanation:

  • Constraints must be equations, not function calls
  • Solver can’t execute calculate_checksum() during randomization
  • Solution: Use post_randomize()
constraint c_size {
    len inside {[1:10]};
    data.size() == len;
}

function void post_randomize();
    checksum = calculate_checksum(data);
endfunction
  • Constraints: Mathematical relationships only
  • Functions: Procedural code, not constraint-solvable
  • Common mistake at Qualcomm, Broadcom

📌 Question 93: Sequence Response Handling

class my_driver extends uvm_driver#(my_transaction);
    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            drive_bus(req);
            
            // Create response
            rsp = my_transaction::type_id::create("rsp");
            rsp.set_id_info(req);  // Link to request
            rsp.status = get_bus_status();
            
            seq_item_port.item_done(rsp);
        end
    endtask
endclass

class my_sequence extends uvm_sequence#(my_transaction);
    task body();
        `uvm_do_with(req, {addr == 100;})
        
        // How to get response?
        get_response(rsp);
        `uvm_info("SEQ", $sformatf("Status = %0d", rsp.status), UVM_LOW)
    endtask
endclass

Question: Is get_response() needed?

Answer: YES – to receive driver response!

Explanation:

  • `uvm_do macro does NOT get response automatically
  • Must explicitly call get_response(rsp)
  • Blocking call – waits for driver to send response
  • Alternative: Use uvm_do_with_response (custom macro)
  • Response queue in sequence
  • Common bug: Forgetting get_response → hangs!
`uvm_do_with(req, {addr == 100;})
get_response(rsp);
  • Or use item_done() without response if not needed
  • Asked at Nvidia, ARM

📌 Question 94: Plusargs vs Config DB

module top;
    int timeout;
    
    initial begin
        // Method 1: Plusargs
        if ($value$plusargs("timeout=%0d", timeout))
            $display("Got timeout from plusargs: %0d", timeout);
        
        // Method 2: Config DB
        uvm_config_db#(int)::set(null, "*", "timeout", timeout);
        run_test();
    end
endmodule

class my_test extends uvm_test;
    int timeout = 1000;  // Default
    
    function void build_phase(uvm_phase phase);
        void'(uvm_config_db#(int)::get(this, "", "timeout", timeout));
        `uvm_info("TEST", $sformatf("Timeout = %0d", timeout), UVM_LOW)
    endfunction
endclass

Question: Run with +timeout=5000. What’s the result?

Answer: timeout = 5000

Explanation:

  • Plusargs: Command-line arguments (+name=value)
  • Parsed in initial block before UVM starts
  • Value stored in timeout variable
  • Config_db::set uses that value
  • Test gets 5000 from config_db
  • Flow: Plusargs → Variable → Config DB → Component
  • Best practice: Parse plusargs in top, push to config_db
  • Common pattern at all VLSI companies

📌 Question 95: Phase Domain Jumping

class my_test extends uvm_test;
    task main_phase(uvm_phase phase);
        phase.raise_objection(this);
        `uvm_info("TEST", "In main_phase", UVM_LOW)
        phase.drop_objection(this);
    endtask
    
    task shutdown_phase(uvm_phase phase);
        phase.raise_objection(this);
        `uvm_info("TEST", "In shutdown_phase", UVM_LOW)
        
        // Jump back to reset phase!
        phase.jump(uvm_reset_phase::get());
        
        phase.drop_objection(this);
    endtask
endclass

Question: What happens after jump?

Answer: Simulation goes back to reset_phase!

Explanation:

  • phase.jump() restarts from specified phase
  • All subsequent phases re-execute
  • Useful for restarting DUT mid-simulation
  • Dangerous: Can create infinite loops
  • Must have exit condition
static int jump_count = 0;
if (jump_count < 3) begin
    jump_count++;
    phase.jump(uvm_reset_phase::get());
end
  • Rarely used in practice
  • More common in emulation/acceleration environments
  • Asked at Cadence, Synopsys for advanced roles

📌 Question 96: Coverage Bin Overflow

bit [2:0] addr;

covergroup cg @(posedge clk);
    cp_addr: coverpoint addr {
        bins low = {[0:3]};
        bins high = {[4:7]};
    }
endcovergroup

cg cg_inst = new();

initial begin
    addr = 0; @(posedge clk);
    addr = 5; @(posedge clk);
    addr = 8; @(posedge clk);  // Out of range!
    addr = 2; @(posedge clk);
end

Question: What happens with addr=8?

Answer: Ignored – out of bit range!

Explanation:

  • addr is bit [2:0]: max value = 7
  • addr = 8 wraps to 0 (3-bit overflow)
  • Coverage sees: 0, 5, 0, 2
  • Both bins hit: low{0,2}, high{5}
  • No error for overflow in RTL
  • Coverage samples actual value (0, not 8)
  • Be careful with bit widths!
  • Common at Intel, TI interviews

📌 Question 97: Parameterized Transaction

class my_transaction#(int ADDR_WIDTH = 32, 
                       type DATA_TYPE = bit [31:0]) 
    extends uvm_sequence_item;
    
    rand bit [ADDR_WIDTH-1:0] addr;
    rand DATA_TYPE data;
    
    `uvm_object_param_utils(my_transaction#(ADDR_WIDTH, DATA_TYPE))
    
    function new(string name = "");
        super.new(name);
    endfunction
endclass

// Use different specializations
typedef my_transaction#(16, bit [15:0]) narrow_transaction;
typedef my_transaction#(64, bit [63:0]) wide_transaction;

narrow_transaction n_tr;
wide_transaction w_tr;

Question: Can we assign n_tr to w_tr?

Answer: NO – Different types!

Explanation:

  • Each specialization creates a new type
  • narrow_transaction ≠ wide_transaction
  • Different addr widths and data types
  • No implicit conversion between them
  • Factory sees them as completely different types
  • Type safety enforced by SystemVerilog
  • Must use correct type throughout hierarchy
w_tr = n_tr;  // ERROR: Type mismatch
  • Important for generic verification IPs
  • Asked at verification IP developer roles

📌 Question 98: Multiple TLM Get/Put Ports

class my_driver extends uvm_driver#(my_transaction);
    uvm_get_port#(my_transaction) input_port;
    uvm_put_port#(my_transaction) output_port;
    
    task run_phase(uvm_phase phase);
        my_transaction tr;
        forever begin
            input_port.get(tr);
            process_transaction(tr);
            output_port.put(tr);
        end
    endtask
endclass

class my_env extends uvm_env;
    my_driver drv;
    uvm_tlm_fifo#(my_transaction) input_fifo, output_fifo;
    
    function void connect_phase(uvm_phase phase);
        drv.input_port.connect(input_fifo.get_export);
        drv.output_port.connect(output_fifo.put_export);
    endfunction
endclass

Question: What if both FIFOs are full/empty?

Answer:

  • input_fifo empty: get() blocks
  • output_fifo full: put() blocks

Explanation:

  • Blocking TLM – get/put wait until ready
  • get() blocks until data available
  • put() blocks until space available
  • Can cause deadlock if not careful!
  • Solution: Use try_get/try_put or proper sizing
if (input_port.try_get(tr))
    process_transaction(tr);
  • Or size FIFOs appropriately
  • Common issue at Verification Service Companies

📌 Question 99: Register Model Integration

class my_reg extends uvm_reg;
    rand uvm_reg_field status;
    rand uvm_reg_field control;
    
    function void build();
        status = uvm_reg_field::type_id::create("status");
        status.configure(this, 8, 0, "RO", 0, 8'h00, 1, 1, 0);
        
        control = uvm_reg_field::type_id::create("control");
        control.configure(this, 8, 8, "RW", 0, 8'hFF, 1, 1, 1);
    endfunction
endclass

// In test
my_reg reg_inst;
uvm_status_e status;
bit [7:0] value;

reg_inst.control.write(status, 8'h55);
reg_inst.control.read(status, value);

Question: What is value after read?

Answer: value = 8’h55 (if no errors)

Explanation:

  • Register model abstracts register access
  • write() updates register and mirror value
  • read() reads from DUT (or mirror)
  • status field is RO (Read-Only) – can’t write
  • control field is RW – can read/write
  • Access modes:
    • RO: Read Only
    • WO: Write Only
    • RW: Read Write
    • RC: Read Clear
    • WC: Write Clear
    • W1C: Write 1 to Clear
  • Essential for Register Verification roles
  • Asked at ARM, Qualcomm

📌 Question 100: Heartbeat Mechanism

class my_env extends uvm_env;
    uvm_heartbeat hb;
    uvm_event hb_event;
    
    function void build_phase(uvm_phase phase);
        hb_event = new("hb_event");
        hb = new("hb", this, hb_event);
    endfunction
    
    task run_phase(uvm_phase phase);
        hb.set_mode(UVM_ALL_ACTIVE);
        hb.start(hb_event);
    endtask
endclass

class my_driver extends uvm_driver#(my_transaction);
    task run_phase(uvm_phase phase);
        forever begin
            seq_item_port.get_next_item(req);
            drive_transaction(req);
            // Trigger heartbeat every transaction
            // How to access hb_event?
            seq_item_port.item_done();
        end
    endtask
endclass

Question: How does driver access hb_event?

Answer: Must get from config_db or event pool!

Explanation:
Method 1: Config DB

class my_env extends uvm_env;
    function void build_phase(uvm_phase phase);
        hb_event = new("hb_event");
        uvm_config_db#(uvm_event)::set(this, "*", "hb_event", hb_event);
    endfunction
endclass

class my_driver extends uvm_driver#(my_transaction);
    uvm_event hb_event;
    
    function void build_phase(uvm_phase phase);
        if (!uvm_config_db#(uvm_event)::get(this, "", "hb_event", hb_event))
            `uvm_fatal("DRV", "Cannot get hb_event")
    endfunction
    
    task run_phase(uvm_phase phase);
        hb_event.trigger();
    endtask
endclass

Method 2: Event pool (Better)

uvm_event hb_event = uvm_event_pool::get_global("heartbeat");
hb_event.trigger();
  • Heartbeat: Watchdog for liveness detection
  • Ensures components are active
  • Asked at AMD, Marvell for complex testbenches

Summary Statistics – Part 4

Total Questions in Part 4: 25
Grand Total: 100 Questions! đźŽ‰

UVM & Advanced Topics Covered:

  • âś… UVM factory overrides (type vs instance)
  • âś… Config DB timing and hierarchy
  • âś… Sequence item handling and reuse
  • âś… Virtual sequences and typed p_sequencer
  • âś… TLM analysis ports and fanout
  • âś… Sequence arbitration and priority
  • âś… Phase objections and jumping
  • âś… Callbacks and execution order
  • âś… Field macros and flags
  • âś… Virtual interface timing
  • âś… Scoreboard implementation patterns
  • âś… Constraint limitations
  • âś… Response handling in sequences
  • âś… Plusargs integration
  • âś… Coverage edge cases
  • âś… Parameterized transactions
  • âś… TLM get/put blocking behavior
  • âś… Register models
  • âś… Heartbeat mechanisms
  • âś… Report catchers

🏢 VLSI Company Focus Areas

Intel / AMD / Nvidia (CPU/GPU Verification):

  • Factory overrides and polymorphism
  • Complex sequence hierarchies
  • Register models for control/status
  • Coverage-driven verification
  • Assertion-based verification

Qualcomm / Broadcom (Communication SoCs):

  • Protocol-specific sequences
  • Layered sequences and virtual sequences
  • TLM-based communication
  • Constrained-random verification
  • Power-aware verification

ARM / Apple (Processor IP):

  • Parameterized VIPs
  • Register abstraction layers
  • Functional coverage closure
  • Temporal assertions
  • Low-power verification

Synopsys / Cadence / Mentor (EDA Tools/VIP):

  • Generic, reusable components
  • UVM best practices
  • Factory patterns extensively
  • Configurability
  • Advanced UVM features

Verification Service Companies:

  • Clean coding practices
  • Well-structured environments
  • Proper phase usage
  • TLM connectivity
  • Comprehensive coverage

Master-Level UVM Takeaways

  1. Factory: Type overrides are global, instance are specific
  2. Config DB: Set before component creation
  3. Sequence items: Create new object for each transaction
  4. Virtual sequences: Use typed p_sequencer or config_db
  5. Analysis ports: Broadcast same handle – clone if needed
  6. Priorities: Higher number = higher priority (200 > 100)
  7. Objections: Hierarchical – all must drop
  8. Callbacks: FIFO order, can modify behavior
  9. Field macros: Control copy/compare/print/pack
  10. Resources: config_db has priority over resource_db

Interview Success Strategy

For UVM Questions:

  1. Understand phases – Build before connect, objection management
  2. Know factory – Type vs instance overrides
  3. Master config_db – Timing, hierarchy, wildcards
  4. TLM expertise – Ports, exports, imps, FIFOs
  5. Sequence control – Lock, grab, priority, responses
  6. Coverage goals – Functional coverage, assertions
  7. Register models – Abstraction, access policies
  8. Debug skills – Report catchers, verbosity, logging

Common Pitfalls to Avoid:

  • ❌ Setting config_db after component creation
  • ❌ Reusing sequence items without creating new
  • ❌ Forgetting get_response() for driver responses
  • ❌ Not checking randomize() return value
  • ❌ Mixing resource_db and config_db
  • ❌ Using procedural code in constraints
  • ❌ Forgetting to raise/drop objections
  • ❌ Not cloning transactions before analysis broadcast

Part 4 completes our comprehensive collection with 100 expert-level questions covering all aspects tested by top VLSI companies worldwide!

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top