90+ free Verilog questions on data types, widths and signedness, blocking and non-blocking assignments, the event queue, case statements, generate, FSMs, memories, synthesis and testbenches. Includes 18 worked answers with code, 8 predict-the-output puzzles, one simulation time step traced region by region and a Verilog literal decoder.
580 questions with interview-ready answers and tips, 20 coding exercises with verified solutions, 25 predict-the-output puzzles and 30 debugging scenarios.
Verilog questions check whether you can see the hardware behind the code. They open most RTL design, verification and FPGA interviews, and they decide many fresher interviews on their own, because a short snippet quickly shows whether a candidate understands widths, assignments and timing or has only memorised syntax.
Strong answers say what the code becomes in hardware, what width it's evaluated at and when the simulator updates it. Start with the rules sheet and the decoder, skim the 50 questions by topic, then try each worked answer and puzzle before you open it. The code examples page has complete modules you can simulate.
Nets and variables, the four logic values, literals, widths, signedness and operators. Expect "what width is this?" and "what does this print?"
A flop with reset, a counter, an FSM, a small memory or a pulse generator, written on a whiteboard or shared editor, with no latches and the right assignment types.
The event queue, races, X-propagation, simulation–synthesis mismatches and gate-level surprises: "it passed RTL simulation, so why doesn't it work?"
Flip-flop, latch, mux or adder. Naming the hardware first shows you write RTL as a circuit description, not as a program.
Before giving a value, say how many bits the expression is evaluated at. Most trick questions are width questions in disguise.
<= in clocked blocks, = in combinational blocks, and each variable assigned in only one block.
Default-assign outputs in combinational blocks, reset every register that needs a known value, and give every case a default.
When an answer depends on ordering, walk through Active, NBA and Monitor. It turns guesswork into a short, confident explanation.
Delays, initial blocks, sensitivity lists and $display behave differently, or vanish, once the code becomes gates.
IEEE 1364-2005 Verilog. Most also hold in SystemVerilog, which adds types such as logic and always_ff on top.
Type a literal the way you'd write it in code. See its width, signedness, bits and value, including padding, truncation and x/z handling.
"Where would you use this?" and "what does it synthesise to?" come up for every one of these. Getting the update timing right answers half the follow-ups.
| CONSTRUCT | WHERE IT BELONGS | WHEN IT UPDATES | WHAT SYNTHESIS BUILDS | CLASSIC MISTAKE |
|---|---|---|---|---|
assign | Outside procedural blocks, driving a net | Whenever any right-hand operand changes | Combinational logic | Using it to drive a variable declared reg |
= in always @* | Combinational always blocks | Immediately, in the Active region | Combinational logic | Leaving a path unassigned, which builds a latch |
<= in always @(posedge clk) | Clocked always blocks | Right side read now, update applied in the NBA region | Flip-flops | Expecting the new value on the next line |
initial | Testbenches, simulation models | Once, at time 0 | Ignored by ASIC synthesis; FPGA tools may use it for power-up values | Using it instead of a reset |
function | Inside expressions | In zero time, returns one value | Combinational logic, inlined | Adding delays or event controls, which are illegal |
task | Called as a statement | Can consume time with # or @ | Inlined logic only if it has no timing | Expecting a task that waits for a clock to synthesise |
A sample of what Verilog interviewers ask most. The 580-question ebook goes much deeper on each topic and answers every question.
The first five minutes of a Verilog round.
wand or wor instead of wire?integer and reg [31:0] differ in simulation, and in what synthesis builds?output reg also be driven by a continuous assignment inside the module?Where most silent bugs come from.
$signed(a) > b still compare unsigned values?!, ~ and ~| applied to a 4-bit vector??: return a mix of known and unknown bits?a is 3 bits and b is 5 bits. What are the widths of {a, b} and of a + b, and why do they differ?Building hierarchy that others can reuse.
defparam discouraged, and what replaced it?`default_nettype none catch at compile time?DEPTH?Where hardware intent meets simulator behaviour.
always @* block calls a function that reads a module-level signal not passed as an argument. Why might the block miss changes to it?<= in clocked blocks even when = appears to work?always @(posedge clk or rst) fail to model an asynchronous reset properly?initial block appear in RTL that targets an FPGA, but not in ASIC RTL?rst moves from inside an if in a clocked block into its sensitivity list?How the simulator orders events inside one time step.
$display placed after a non-blocking assignment print the old value?$monitor print at most once per time step even if a signal toggles twice in it?`timescale 1ns/1ps mean, and what breaks if files use different time scales?Constructs that repeat or select hardware.
casez, what is the difference between an item written 4'b1??? and one written 4'b1xxx?generate if rather than an ordinary if inside an always block?Reusable code and the simulator's toolbox.
# delays or wait for a clock edge?automatic?$random % 10 not enough?$readmemh expect in its file, and what is left in memory locations the file doesn't cover?The blocks you'll be asked to write on the spot.
What survives the trip from RTL to gates.
initial block, a #5 delay and a $display when RTL goes through ASIC synthesis?assign gclk = clk & en; a bad way to gate a clock?Proving the RTL works, and finding out why it doesn't.
$finish, and when $stop?580 questions in 27 chapters, all answered: 20 coding exercises with verified solutions, 25 output puzzles, 30 debugging scenarios, plus GLS and SDF, lint and a 40-question rapid-fire round.
Write your answer or say it aloud first, then open it. Each answer shows what a strong reply covers, what the interviewer is listening for, and the slip that costs candidates most.
a → b → c → a on every clock. Write it, then explain what breaks with =.Foundationalalways @(posedge clk) begin b <= a; c <= b; a <= c; end // a=1, b=2, c=3 -> after one edge: a=3, b=1, c=2
Each non-blocking assignment reads its right-hand side when the statement runs, but the update is held back until every block woken by the edge has finished reading. All three registers therefore see the old values, exactly like three flip-flops sampling on the same edge.
With blocking assignments, b = a changes b at once, then c = b copies the new b, and a = c copies that too: all three end up holding the old a. Synthesis builds what the blocking code describes, which is not a rotation. So the rule is non-blocking in clocked blocks, blocking in combinational blocks, and never both on one variable.
X is a simulation idea. In silicon every node settles at 0 or 1; X says "we don't know which". An X that survives reset is therefore a design question, not a cosmetic one: the hardware will pick some value, and the design must not depend on which.
sum = a + b; keeps the carry, but sum = {a + b}; loses it. Why?FoundationalMost expressions are context-determined: the simulator finds the widest operand, including the left-hand side of the assignment, extends every operand to that width and then computes. With a 9-bit sum, a + b is computed at 9 bits and the carry survives.
reg [7:0] a = 8'd200, b = 8'd100;
reg [8:0] sum;
sum = a + b; // 9-bit context: 300
sum = {a + b}; // braces are self-determined: 8 bits, 300 mod 256 = 44A concatenation is self-determined: its operands keep their own width whatever the context. Other self-determined places are comparison operands, reduction operators, shift amounts and arguments to system tasks. Those are where carries quietly disappear.
if. What does RTL simulation do, and why can it mislead you?Foundationalreg en; // never assigned: en = X always @* begin if (en) y = a; // condition is X: treated as false else y = b; // RTL sim reports y = b end
An if treats an X or Z condition as false and takes the else branch. Simulation therefore shows a clean, believable value, while real hardware could select either input. This is called X-optimism: the X disappears instead of spreading, so the missing initialisation is never noticed.
Checks help: en === 1'bx in a testbench assertion compares X literally, and the conditional operator en ? a : b is more pessimistic, merging the two inputs bit by bit. The real fix is to give en a defined value, usually through reset.
casex decoder hides an X on its selector. How does that happen, and what would you write instead?Foundationalcasex treats X and Z bits as don't-care on both sides of the comparison. If the selector itself is X, for example because a register was never reset, the first item matches and simulation carries on with a believable result.
| X bits | Z or ? bits | |
|---|---|---|
case | Must match exactly | Must match exactly |
casez | Must match exactly | Don't care |
casex | Don't care | Don't care |
Write the wildcards with ? in a casez instead. The decoding is the same, but an X on the selector no longer matches a wildcard item, so it falls through to the default, where you can drive X or flag an error and actually see the problem.
parameter, localparam and `define fits each?Foundationalfifo #(.DEPTH(64)) u_fifo (...);localparam AW = $clog2(DEPTH);`ifdef.The trade-off is scope. Parameters and localparams belong to one instance and appear in the hierarchy; macros are global, and a later file can redefine one, so compile order can change the result.
module reg32_be (
input wire clk, rst_n,
input wire [3:0] be, // one enable per byte
input wire [31:0] wdata,
output reg [31:0] q
);
integer i;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) q <= 32'd0;
else
for (i = 0; i < 4; i = i + 1)
if (be[i]) q[8*i +: 8] <= wdata[8*i +: 8];
end
endmoduleThe loop unrolls into four independent byte registers, each with its own enable. The indexed part-select q[8*i +: 8] picks byte i with a variable start and a constant width. Bytes whose enable is low simply hold, which is what a flop with an enable does.
localparam IDLE = 2'd0, BUSY = 2'd1, DONE = 2'd2;
reg [1:0] state, nxt;
reg [1:0] cnt;
always @(posedge clk or negedge rst_n) // state register
if (!rst_n) state <= IDLE;
else state <= nxt;
always @* begin // next-state logic
nxt = state; // default: stay
case (state)
IDLE: if (start) nxt = BUSY;
BUSY: if (cnt == 2'd2) nxt = DONE;
DONE: nxt = IDLE;
default: nxt = IDLE; // recover from 2'd3
endcase
end
always @(posedge clk or negedge rst_n) // cycle counter
if (!rst_n) cnt <= 2'd0;
else if (state == BUSY) cnt <= cnt + 2'd1;
else cnt <= 2'd0;
assign busy = (state == BUSY);
assign done = (state == DONE);The default assignment nxt = state; at the top of the combinational block means no path leaves nxt unassigned, so there's no latch. The default item sends the unused code back to IDLE. Outputs decoded from the state alone make this a Moore machine with glitch-free, registered-state outputs.
Describe a RAM the way the block RAM works: synchronous write, and a registered read address or registered read data.
module spram #(parameter AW = 10, DW = 16) (
input wire clk, we,
input wire [AW-1:0] addr,
input wire [DW-1:0] din,
output reg [DW-1:0] dout
);
reg [DW-1:0] mem [0:(1<<AW)-1];
always @(posedge clk) begin
if (we) mem[addr] <= din;
dout <= mem[addr]; // registered read: maps to BRAM
end
endmoduleThings that push synthesis back to flip-flops or LUT RAM: an asynchronous read (assign dout = mem[addr];), a reset on the memory array, more read or write ports than the primitive has, or reading every location at once in a loop. Check the synthesis report for the inferred RAM type; don't assume.
// Latch: y is not assigned when sel == 2'b11
always @* begin
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
endcase
end
// Fix 1: default assignment first, then override
always @* begin
y = 1'b0;
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
endcase
end
// Fix 2: cover every value
default: y = 1'b0; // added as the last case itemWhen sel is 2'b11, no branch assigns y, so it has to keep its old value, and only a latch can do that. A default assignment at the top of the block covers every path for every output at once, which is why many teams make it a rule. Confirm with the lint and synthesis logs, which both list inferred latches.
module delay_line #(parameter W = 8, N = 4) (
input wire clk,
input wire [W-1:0] din,
output wire [W-1:0] dout
);
wire [W-1:0] stage [0:N];
assign stage[0] = din;
genvar k;
generate
for (k = 0; k < N; k = k + 1) begin : g_stage
reg [W-1:0] r;
always @(posedge clk) r <= stage[k];
assign stage[k+1] = r;
end
endgenerate
assign dout = stage[N];
endmoduleThe array has N + 1 entries so that each stage has an input and an output. Each loop iteration declares its own register inside the named block, giving stable names such as g_stage[2].r for waveforms and timing constraints. In an FPGA, a delay line with no reset like this one can map into shift-register LUTs instead of separate flops.
module stretch #(parameter N = 4) (
input wire clk, rst_n, pulse,
output wire out
);
localparam CW = $clog2(N + 1);
reg [CW-1:0] cnt;
always @(posedge clk or negedge rst_n)
if (!rst_n) cnt <= {CW{1'b0}};
else if (pulse) cnt <= N; // (re)start on every pulse
else if (cnt != 0) cnt <= cnt - 1'b1;
assign out = (cnt != 0);
endmoduleThe counter is sized with $clog2(N + 1) so it can hold N itself. A new pulse during the stretch restarts the count, so pulses close together merge into one longer output; say that out loud, because interviewers often ask what should happen in that case. The output is decoded from a register, so it's glitch-free.
Verilog creates an implicit 1-bit wire for any undeclared identifier used in a port connection or on the left of a continuous assignment. The misspelt name became a new, undriven net, so the instance sees Z (or X after logic), and the real signal is left unconnected. If the real signal was a bus, the implicit net is only one bit wide, which adds width-mismatch warnings that are easy to miss.
`default_nettype none at the top of every RTL file, so any undeclared name is a compile error. Restore `default_nettype wire at the end if third-party code relies on the default.The sum of four 8-bit values needs 10 bits (up to 1,020). If the sum is held in an 8-bit variable, or computed in a self-determined context, the top bits are lost before the divide.
reg [7:0] s0, s1, s2, s3, avg; reg [9:0] sum; always @(posedge clk) begin sum <= s0 + s1 + s2 + s3; // 10-bit context: no overflow avg <= sum[9:2]; // divide by 4 = drop two bits end
Work out the worst-case width first: adding N values of W bits needs W + ceil(log2 N) bits. Then check every intermediate variable and every concatenation along the path, since one narrow temporary is enough to break it.
reg [4:0] i; // 5 bits: holds 0 to 31
always @* begin
ones = 0;
for (i = 0; i < 32; i = i + 1) // i never reaches 32
ones = ones + data[i];
endA 5-bit index wraps from 31 back to 0, so i < 32 is always true and the loop never exits. Synthesis unrolls the loop using the constant bounds, so it may produce correct hardware, which makes the bug look like a simulator problem.
Use an integer index (32-bit signed), or make the index one bit wider than the highest value it must compare against.
while (count >= 0) and never stops. What's wrong?Scenario / debugcount is declared reg [7:0], which is unsigned. An unsigned value is always greater than or equal to zero, so the condition is always true. When the counter decrements past 0 it wraps to 255 and keeps going.
while (count != 0), or check before decrementing.signed or use integer, and make sure every operand in the comparison is signed, since one unsigned operand makes the whole comparison unsigned.Lint tools flag this as a "comparison is always true" warning, which is a good reason to read lint output carefully.
initial count = 0;. What will go wrong later?Scenario / debugASIC synthesis ignores initial blocks, so the real flops have no reset value. RTL simulation starts the counter at 0 and hides that. Two problems follow:
Fix it by adding a real reset to the register (or proving the value doesn't matter and documenting it). Many teams also run RTL simulation with random initial values, or with initialisation turned off, to expose missing resets early. FPGA flows are different: there, an initial value can set the bitstream's power-up state, but it still doesn't act as a reset you can reapply.
c, but its sensitivity list still reads @(a or b). RTL and gate-level results now disagree. Why?Scenario / debugalways @(a or b) // c is missing y = (a & b) | c;
In RTL simulation the block only wakes up when a or b changes. A change on c alone leaves y stale until the next change on a or b, so the waveform looks like a register. Synthesis ignores sensitivity lists and builds plain combinational logic, so the netlist responds to c immediately. The two no longer match.
always @*, which builds the list from everything the block reads.Write down what you expect before opening each one, then check it in a simulator. Each snippet sits inside a module with the declarations shown.
Output: b ee e data[15 -: 4] = data[15:12] = 4'hb data[4 +: 8] = data[11:4] = 8'hee data[11:8] = 4'he
Note: -: counts down from the start bit and +: counts up. The width must be a constant, but the start can be a variable, which is why indexed part-selects are used for sliding windows.
Output: 3 -3 3 real -> integer rounds to nearest, ties away from zero 2.5 -> 3, -2.5 -> -3 7 / 2 is integer division: truncates to 3
Note: assigning a real to an integer rounds; dividing two integers truncates. Mixing the two in one calculation is a common source of off-by-one results.
Output: 3 6 repeat (n) evaluates n once, when the loop starts. Changing n inside the loop doesn't change the count.
Note: a while loop would re-test the condition each time; repeat doesn't.
sel is never assigned. What is y?reg sel;
reg [3:0] y;
initial begin
#1 y = sel ? 4'b1100 : 4'b1010;
$display("%b", y);
endShow answer ↓Output: 1xx0 With an X select, ?: compares both options bit by bit: 1100 1010 ---- 1xx0 equal bits kept, different bits become x
Note: an if with an X condition would have picked the else branch and printed 1010. The conditional operator is more pessimistic, and closer to what the hardware might do.
Output: ax aX Lowercase x: every bit of that hex digit is x. Uppercase X: only some bits of the digit are x.
Note: the same rule applies to z and Z. When a hex dump shows a capital X, switch to %b to see which bits are unknown.
Output: 2 3 posedge on a vector looks only at the LSB: xxxx -> 0001 : LSB x -> 1 posedge (x->1 counts) 0001 -> 0010 : LSB 1 -> 0 no posedge 0010 -> 0011 : LSB 0 -> 1 posedge @(a) fires on any change: 3 times
Note: transitions from x or z to 1, and from 0 to x or z, count as a posedge. Edge events on multi-bit signals are almost always a bug; write posedge a[0] if that's what you mean.
Output: wait done at 10 @ done at 20 wait (flag): flag is already 1 at time 10, so it continues at once. @(flag): waits for the next change, which is 1 -> 0 at time 20.
Note: wait is level-sensitive and @ is edge-sensitive. Using @ on a condition that may already be true is a classic testbench hang.
Output (order may vary): top.u1 63 top.u2 15 localparam MAX is recomputed from each instance's W.
Note: the order of the two lines isn't defined, since the initial blocks run in an unspecified order. Trying m #(.MAX(7)) is a compile error: a localparam can't be overridden.
Race, delta-cycle and "why did it print the old value?" questions all come back to this picture. Trace one clock edge through it, and the rules about = and <= stop being rules to memorise.
always @(posedge clk) q1 <= d; always @(posedge clk) q2 <= q1; always @* y = q1 ^ q2;
d and the old q1. Which block runs first isn't defined, and it doesn't matter, because neither changes anything yet.q1 ← d and q2 ← old q1 wait in the NBA queue. Any $display in these blocks prints the old values.#0 would run here. Clean RTL has none.q1 and q2 change at once, just like two flops sharing a clock.always @*, so the simulator returns to Active and recomputes y, still at the same simulation time.$strobe and $monitor print the settled values, and simulation time moves forward.Every clocked block reads its inputs in the Active region before any register changes in the NBA region. So the result doesn't depend on which block the simulator happens to run first, which is the property real flops sharing a clock have.
= instead?If the q1 block runs first, q1 updates immediately and the q2 block copies the new value, so the two-stage pipeline collapses into one. If the q2 block runs first, it works. The order is up to the simulator, so this is a genuine race, and two tools can disagree.
$display and $strobe print in this step?$display runs in the Active region with the statement that calls it, so after a <= it shows the old value. $strobe waits until the end of the time step and shows the settled value. $monitor also prints at the end, and only if a watched value changed.
For RTL, yes: Active, Inactive and NBA work the same way. SystemVerilog adds regions around them, such as Preponed, where assertions sample values, and Observed and Reactive, used by assertions and testbench programs. The SystemVerilog questions page and the Verilog ebook both go further into scheduling and races.
Written tests and screening calls love these. Answer each in a few seconds, then tap to check.
data[15 -: 4] select?Bits 15 down to 12.
An implicit 1-bit wire, unless `default_nettype none is set.
localparam.
repeat (n) read n?Once, when the loop starts.
!4'b0100?1'b0: any nonzero value is true.
~4'b0100?4'b1011.
1'b1 & 1'bz?x.
1'b0 & 1'bx?0: a 0 forces AND low.
1'b1 | 1'bx?1: a 1 forces OR high.
4'b1010 & 4'b0110?4'b0010.
$random return?A signed 32-bit integer.
%m.
X.
a == b?1 bit.
`include do?Pastes the named file's text in place before compiling.
127.
4'b10x1 mean?A 4-bit value whose bit 1 is unknown.
Round to nearest, ties away from zero.
wait (cond) level- or edge-sensitive?Level-sensitive.
`timescale affect synthesis?No. It only sets simulation time units and precision.
It's legal, but the result depends on statement order and often doesn't match the flops you meant. Use non-blocking for every clocked assignment.
An if without else or a case without default in always @* infers a latch. Assign a default to every output at the top of the block.
Silent truncation, lost carries and zero-extension cause most "works for small numbers" bugs. Say the width before you say the value.
One unsigned operand makes the whole expression unsigned, and part-selects are always unsigned. Check every operand, not just the declaration.
# delays, initial blocks used as reset, and === all simulate fine and then vanish or break in synthesis.
An if on an X condition quietly takes the else branch, and casex treats X as a wildcard. Find the source of every X rather than coding around it.
Yes. SystemVerilog is a superset of Verilog, so design interviews still start with Verilog rules: assignments, widths, the event queue, latches and synthesis. Verification interviews then add classes, constraints and assertions on top, which the SystemVerilog questions cover.
For most semiconductor design and verification roles in India and the US, Verilog and SystemVerilog are far more common. VHDL still appears in some FPGA, defence and European companies, so check the job description.
Data types and the four logic values, widths and signedness, blocking versus non-blocking, always @* and latches, case statements, a flip-flop with reset, a counter, an FSM and a basic testbench. Then the event queue and simulation–synthesis differences.
Usually one or two small blocks: a counter, an edge detector, an FSM, a FIFO or a clock divider. Interviewers watch for clean resets, correct assignment types, no latches and sensible widths more than for clever tricks.
Any one you can run every day. Icarus Verilog and Verilator are free, EDA Playground runs in a browser, and the free editions of FPGA vendor simulators work well. What matters is typing the code and checking your prediction against the output.
The Verilog round tests the language and how the simulator runs it. The RTL design round uses the language to build blocks such as FIFOs, arbiters and pipelines, and asks about timing, area and clock domain crossing.
Book a 1:1 mock interview with a mentor working in this exact domain.
Book a mock interview