Every answer in this bank follows one formula: direct answer first, evidence second, the edge third, and the testbench payoff last. Each question below gives the short answer and names the lesson that teaches it.
Direct answer: one sentence that answers exactly what was asked.
Evidence: the smallest code that proves it.
The edge: the follow-up, raised before the interviewer does.
So what: where it bites in a real testbench.
logic is a 4-state variable type: one continuous driver, or any number of procedural writers, but not both kinds. wire is a net that resolves many drivers, for tristate buses. reg is the legacy name logic replaces; it never implied a register.
Taught in: Nets vs Variables.
Four-state belongs wherever a signal touches the DUT, because X is a debugging signal. Two-state is for testbench bookkeeping, but assigning X to it silently gives 0. Rule: 4-state at the DUT boundary, 2-state inside, with $isunknown guarding the crossing.
Taught in: 4-State vs 2-State Types.
Blocking (=) executes in statement order. Nonblocking (<=) samples its right-hand side now and assigns in the NBA region. Use nonblocking in clocked logic so every block reads pre-edge values and results are deterministic.
The classic whiteboard proof is the swap. The bench shows the nonblocking pair exchanging values while the blocking pair collapses to one value.
module tb_swap;
timeunit 1ns; timeprecision 1ps;
logic clk = 0;
always #5 clk = ~clk;
logic a_nb = 1'b0, b_nb = 1'b1, a_b = 1'b0, b_b = 1'b1;
int errors = 0;
always_ff @(posedge clk) begin a_nb <= b_nb; b_nb <= a_nb; end
always @(posedge clk) begin a_b = b_b; b_b = a_b; end
initial begin
@(posedge clk); #1;
if (a_nb !== 1'b1 || b_nb !== 1'b0) errors++; // clean swap
if (a_b !== 1'b1 || b_b !== 1'b1) errors++; // blocking kept itself
@(posedge clk); #1;
if (a_nb !== 1'b0 || b_nb !== 1'b1) errors++; // swapped back
if (errors == 0) $display("PASS: nonblocking swap exchanges values and blocking version collapses");
else $fatal(1, "FAIL: swap behavior wrong");
$finish;
end
endmoduleTaught in: Blocking vs Nonblocking Assignments, The Event Scheduler & Stratified Regions.
No. always_comb runs once at time zero, is sensitive to signals read inside called functions, and forbids other processes from writing its outputs. always @* waits for an input change and can miss function-internal reads. An inferred latch is a warning that lint may escalate.
Taught in: always_comb, always_ff, always_latch.
A function runs in zero time and returns a value. A task may consume time. Anything touching the clock is a task, pure computation is a function, and both should be automatic in testbenches.
Taught in: Tasks vs Functions.
Packed dimensions sit before the name and form one contiguous vector, so whole-vector operations work. Unpacked dimensions sit after the name and form separate elements, the natural model for memories.
Taught in: Packed vs Unpacked Arrays.
a == b?The first line updates a immediately, so the second line reads the new a instead of the old one. Both end up holding the old b.
always_comb stays correct because its sensitivity includes function-internal reads. always @* misses them and goes stale.
When it consumes time: driving pins across clock edges or waiting for a response. Computation with no time advance belongs in a function.