Hardware Engineering
Interview Coding Guide
A from-scratch reference for RTL Design & Verification interviews
SECTION 0
Do I Need to Write 100% Correct Code?
Short answer: No, but you need to be close. You are writing
"pseudocode on a whiteboard that happens to be Verilog." The
interviewer cares that you think like a hardware designer — not that
every semicolon is in place.
W H AT I N T E R V I E W E R S F O R G I V E
Minor syntax typos (missing semicolon, misspelled signal name),
forgetting `timescale , not remembering exact SystemVerilog syntax
if you explain the intent clearly.
W H AT I N T E R V I E W E R S D O N O T F O R G I V E
Using blocking = in sequential logic. Forgetting reset. Creating
unintentional latches (missing else / default ). Not understanding
that Verilog describes parallel hardware. Inability to explain what
physical hardware your code creates.
What Matters Most (Ranked)
1. Correct architecture / logic — did you design the right
hardware?
2. Correct use of = vs <= assignments
3. Correct sensitivity lists / always block structure
4. Correct port declarations and signal widths
5. Handling of edge cases (reset, overflow, simultaneous
read/write)
6. Exact syntax (semicolons, begin / end , etc.) — least important
SECTION 1
Verilog Fundamentals (From Zero)
1.1 — What Is Verilog?
Verilog is not a programming language. It is a Hardware Description
Language (HDL). Every line describes physical wires, gates, or flip-
flops.
THE #1 CONCEPT
So(ware (C / Python): instructions execute one aZer another.
Hardware (Verilog): everything runs at the same time. Every always
block, every assign — they all run simultaneously, like physical
circuits that are always "on."
1.2 — Module: The Basic Building Block
A module is like a chip with pins — inputs, outputs, and internal
logic.
module my_and_gate (
input wire a, // input pin
input wire b, // input pin
output wire y // output pin
);
assign y = a & b; // continuous assignment (combinational)
endmodule
1.3 — Data Types
THINK OF
TYPE MEANING
IT AS…
wire Physical connection, driven by assign or A piece
module output. Cannot store a value. of copper
reg Can hold a value. Used inside always Depends
blocks. Does not always mean a register! on
context
integer 32-bit signed. Used in testbenches / loops. Loop
Not for synthesis. variable
parameter A constant. Makes modules configurable. A #define
1.4 — Number Literals
Format: <bit_width>'<base><value>
LITERAL MEANING
4'b1010 4-bit binary: 1010
8'hFF 8-bit hex: 11111111
8'd255 8-bit decimal: 255
1'b1 Single bit: 1
x Unknown (uninitialized)
z High-impedance (disconnected)
1.5 — Operators
CATEGORY OPERATORS
Bitwise & | ^ ~ (AND, OR, XOR, NOT)
Logical && || ! (for if-conditions)
Relational == != < > <= >=
ShiZ << >> (logical), <<< >>> (arithmetic)
Concatenation {a, b} merges signals
Replication {4{1'b0}} = 4'b0000
Ternary sel ? a : b (like a 2:1 mux)
Reduction &a |a ^a (reduce vector to 1 bit)
I N T E R V I E W T I P — K N OW T H E D I F F E R E N C E
& (bitwise AND): 4'b1100 & 4'b1010 = 4'b1000
&& (logical AND): (4'b1100) && (4'b1010) = 1'b1 (both nonzero)
&a (reduction AND): &4'b1110 = 1'b0 (not all bits are 1)
1.6 — Assign vs Always
Continuous Assignment — for simple combinational logic. LeZ side
must be wire .
assign y = a & b;
Procedural Block — for complex logic. LeZ side must be reg .
// COMBINATIONAL — use blocking (=)
always @(*) begin
y = a & b;
end
// SEQUENTIAL (clocked) — use non-blocking (<=)
always @(posedge clk) begin
q <= d;
end
SECTION 2
Combinational Logic Patterns
2.1 — Multiplexer (MUX)
module mux4to1 (
input wire [1:0] sel,
input wire [7:0] in0, in1, in2, in3,
output reg [7:0] out
);
always @(*) begin
case (sel)
2'b00: out = in0;
2'b01: out = in1;
2'b10: out = in2;
2'b11: out = in3;
default: out = 8'b0; // ← ALWAYS include default!
endcase
end
endmodule
W H Y D E FAU LT M AT T E R S
Without default , if sel takes an unexpected value, the synthesizer
infers a latch. Unintentional latches are the #1 synthesis bug —
interviewers watch for this.
2.2 — Decoder (2-to-4)
module decoder2to4 (
input wire [1:0] in,
input wire en,
output reg [3:0] out
);
always @(*) begin
out = 4'b0000; // default: all low
if (en) begin
case (in)
2'b00: out = 4'b0001;
2'b01: out = 4'b0010;
2'b10: out = 4'b0100;
2'b11: out = 4'b1000;
endcase
end
end
endmodule
2.3 — Priority Encoder
module priority_enc (
input wire [3:0] req,
output reg [1:0] idx,
output reg valid
);
always @(*) begin
valid = 1'b1;
casez (req)
4'b1???: idx = 2'd3; // bit 3 highest priority
4'b01??: idx = 2'd2;
4'b001?: idx = 2'd1;
4'b0001: idx = 2'd0;
default: begin
idx = 2'd0;
valid = 1'b0;
end
endcase
end
endmodule
2.4 — Parameterized Adder
module adder #(parameter N = 8) (
input wire [N-1:0] a, b,
input wire cin,
output wire [N-1:0] sum,
output wire cout
);
assign {cout, sum} = a + b + cin;
endmodule
INTERVIEW NOTE
They may ask about carry-lookahead vs ripple-carry. Ripple-carry is
simple with O(N) delay. Carry-lookahead is faster with O(log N) delay
but uses more area.
SECTION 3
Sequential Logic Patterns
3.1 — D Flip-Flop
// Synchronous Reset
always @(posedge clk) begin
if (rst) q <= 1'b0;
else q <= d;
end
// Asynchronous Reset — rst goes in sensitivity list!
always @(posedge clk or posedge rst) begin
if (rst) q <= 1'b0;
else q <= d;
end
Q: "What's the difference between sync and async reset?"
Synchronous: resets only on clock edge — cleaner timing.
Asynchronous: resets immediately when rst goes high, regardless of
clock — needed for power-on reset. Notice rst is in the sensitivity list.
3.2 — Counter
module counter #(parameter N = 8) (
input wire clk, rst, en,
output reg [N-1:0] count
);
always @(posedge clk) begin
if (rst) count <= {N{1'b0}};
else if (en) count <= count + 1'b1;
end
endmodule
3.3 — Shift Register (Serial-In, Parallel-Out)
always @(posedge clk) begin
if (rst)
parallel_out <= {N{1'b0}};
else
parallel_out <= {parallel_out[N-2:0], serial_in};
// ↑ shift left by 1 ↑ new bit at LSB
end
3.4 — Clock Divider
module clk_div #(parameter N = 4) (
input wire clk, rst,
output reg clk_out
);
reg [$clog2(N)-1:0] count;
always @(posedge clk) begin
if (rst) begin
count <= 0;
clk_out <= 1'b0;
end else if (count == N/2 - 1) begin
count <= 0;
clk_out <= ~clk_out;
end else begin
count <= count + 1'b1;
end
end
endmodule
CLASSIC TRICK QUESTION
"Design a divide-by-3 clock with 50% duty cycle." This requires using
both posedge and negedge of the clock and OR-ing the outputs.
SECTION 4
The "Big 5" Classic Interview Problems
These 5 designs appear in over 80% of hardware interviews.
Memorize the patterns.
4.1 — Synchronous FIFO MOST ASKED
A FIFO is a queue for hardware. Data enters one side and exits the
other in order. Key concepts: memory array, write/read pointers,
full/empty flags, circular buffer.
module sync_fifo #(
parameter DATA_W = 8,
parameter DEPTH = 8,
parameter PTR_W = $clog2(DEPTH)
)(
input wire clk, rst,
// Write interface
input wire wr_en,
input wire [DATA_W-1:0] wr_data,
output wire full,
// Read interface
input wire rd_en,
output reg [DATA_W-1:0] rd_data,
output wire empty
);
reg [DATA_W-1:0] mem [0:DEPTH-1];
reg [PTR_W:0] wr_ptr, rd_ptr; // 1 extra bit!
// Full & Empty detection
assign full = (wr_ptr[PTR_W] != rd_ptr[PTR_W]) &&
(wr_ptr[PTR_W-1:0] == rd_ptr[PTR_W-1:0]);
assign empty = (wr_ptr == rd_ptr);
// Write logic
always @(posedge clk) begin
if (rst)
wr_ptr <= 0;
else if (wr_en && !full) begin
mem[wr_ptr[PTR_W-1:0]] <= wr_data;
wr_ptr <= wr_ptr + 1'b1;
end
end
// Read logic
always @(posedge clk) begin
if (rst) begin
rd_ptr <= 0;
rd_data <= 0;
end else if (rd_en && !empty) begin
rd_data <= mem[rd_ptr[PTR_W-1:0]];
rd_ptr <= rd_ptr + 1'b1;
end
end
endmodule
Q: "What happens if we read and write simultaneously when full?"
The write is blocked ( wr_en && !full fails). The read proceeds,
making space. Next cycle, the write can proceed. Some designs add a
special case for simultaneous read+write when full.
Q: "Why is the pointer one bit wider than needed?"
The extra MSB distinguishes full from empty. When pointers are equal →
empty. When they differ only in the MSB → full.
Q: "How many flip-flops does this use?"
DEPTH × DATA_W for memory + 2 × (PTR_W+1) for pointers. For
DEPTH=8, DATA_W=8: 64 + 8 = 72 flip-flops.
4.2 — Arbiter (Fixed Priority)
module fixed_arb_v2 (
input wire [3:0] req,
output reg [3:0] grant
);
always @(*) begin
casez (req)
4'b???1: grant = 4'b0001;
4'b??10: grant = 4'b0010;
4'b?100: grant = 4'b0100;
4'b1000: grant = 4'b1000;
default: grant = 4'b0000;
endcase
end
endmodule
4.3 — Register Swap
// WITH temp register (blocking assignments):
always @(posedge clk) begin
temp = b; // b's value captured immediately
b = a;
a = temp;
end
// WITHOUT temp register (non-blocking assignments):
always @(posedge clk) begin
a <= b; // both RHS evaluated FIRST
b <= a; // then all LHS updated at end of time step
end
W H Y T H I S WO R K S
Non-blocking <= captures all right-hand-side values at the beginning
of the time step, then updates all leZ-hand-sides at the end. So a gets
the old value of b , and b gets the old value of a . Simultaneously.
This is hardware — two wires crossing.
4.4 — Edge Detector
module edge_detect (
input wire clk, rst, sig,
output wire rise, fall
);
reg sig_d; // delayed version
always @(posedge clk) begin
if (rst) sig_d <= 1'b0;
else sig_d <= sig;
end
assign rise = sig & ~sig_d; // was 0, now 1
assign fall = ~sig & sig_d; // was 1, now 0
endmodule
SECTION 5
Blocking vs Non-Blocking
This is tested in every hardware interview. Get this wrong = instant
reject.
THE GOLDEN RULE — MEMORIZE THIS
Combinational always @(*) → use =
logic
Sequential logic always @(posedge → use <=
clk)
Continuous assign assign y = ... → always
=
Why It Matters
B LO C K I N G ( = ) I N C LO C K E D LO G I C — B R O K E N P I P E L I N E
always @(posedge clk) begin
b = a; // b gets a's value RIGHT NOW
c = b; // c gets b's NEW value = a's value
end
// Result: b = a, c = a → SINGLE flip-flop, pipeline broken
N O N - B LO C K I N G ( < = ) I N C LO C K E D LO G I C — C O R R E C T P I P E L I N E
always @(posedge clk) begin
b <= a; // schedule: b will get a's CURRENT value
c <= b; // schedule: c will get b's CURRENT (old) value
end
// Result: b = old_a, c = old_b → TWO flip-flops in series ✓
SECTION 6
Finite State Machines (FSMs)
Moore: output depends only on current state. Mealy: output depends
on current state and current inputs (reacts faster, but can create
combinational paths).
Recommended: 3-Always-Block Style
// Sequence Detector — detects "101" on input
module seq_detect (
input wire clk, rst, din,
output reg detected
);
localparam S_IDLE = 2'b00,
S_1 = 2'b01,
S_10 = 2'b10,
S_101 = 2'b11;
reg [1:0] state, next_state;
// Block 1: State register (sequential)
always @(posedge clk) begin
if (rst) state <= S_IDLE;
else state <= next_state;
end
// Block 2: Next-state logic (combinational)
always @(*) begin
next_state = state; // default: stay
case (state)
S_IDLE: next_state = din ? S_1 : S_IDLE;
S_1: next_state = din ? S_1 : S_10;
S_10: next_state = din ? S_101 : S_IDLE;
S_101: next_state = din ? S_1 : S_10;
default: next_state = S_IDLE;
endcase
end
// Block 3: Output logic (Moore style)
always @(*) begin
detected = (state == S_101);
end
endmodule
FSM INTERVIEW TIPS
Always draw the state diagram first. Always include a default case and
reset. Assign a default value to next_state at the top. Know one-hot
(more FFs, faster decode) vs binary encoding (fewer FFs, more logic).
SECTION 7
Clock Domain Crossing (CDC)
Metastability: A flip-flop captures a signal changing right at the
clock edge (violating setup/hold time). The output oscillates
unpredictably before settling.
Solution: Two-Flop Synchronizer (single-bit signals)
module synchronizer (
input wire clk_b, rst,
input wire async_in, // from clock domain A
output wire sync_out // synchronized in domain B
);
reg sync_ff1, sync_ff2;
always @(posedge clk_b or posedge rst) begin
if (rst) begin
sync_ff1 <= 1'b0;
sync_ff2 <= 1'b0;
end else begin
sync_ff1 <= async_in; // may be metastable
sync_ff2 <= sync_ff1; // resolves metastability
end
end
assign sync_out = sync_ff2;
endmodule
M U LT I - B I T S I G N A LS → A SY N C F I F O W I T H G R AY C O D E
Gray code ensures only one bit changes per increment, so even if the
synchronizer catches it mid-transition, the error is at most off-by-one
(safe for full/empty detection).
gray = binary ^ (binary >> 1)
SECTION 8
Timing Diagrams
Interviewers will ask you to draw timing diagrams alongside your
code.
Rules for Drawing
1. Draw the clock waveform first (square wave)
2. Flip-flop outputs change aBer the active clock edge (small
delay)
3. Combinational outputs change immediately when inputs
change
4. Mark setup time (data stable before clock edge)
5. Mark hold time (data stable aBer clock edge)
Key Timing Parameters
PARAMETER SYMBOL MEANING
Setup Time Tsu How long before the clock edge data must be
stable
Hold Time Th How long aZer the clock edge data must stay
stable
Clock-to-Q Tcq Delay from clock edge to output change
If setup/hold are violated → metastability → undefined output.
SECTION 9
Synthesis Awareness
"What hardware does your code create?" — You must be able to
answer this.
CODE CONSTRUCT → HARDWARE
assign y = a & b; AND gate
assign y = sel ? a : b; 2:1 MUX
always @(posedge clk) q <= d; D Flip-Flop
case statement MUX tree or decoder
if-else chain Priority MUX
for loop (unrolled) Replicated hardware
always @(*) without full coverage LATCH (unintentional!)
Latch Inference — The Trap
B A D — C R E AT E S A L AT C H
always @(*) begin
if (sel)
y = a;
// no else! y holds old value when sel=0 → latch
end
G O O D — N O L AT C H ( T WO A P P R OAC H E S )
// Approach 1: Full if-else
always @(*) begin
if (sel) y = a;
else y = b;
end
// Approach 2: Default assignment
always @(*) begin
y = 1'b0; // default value
if (sel) y = a;
end
SECTION 10
SystemVerilog & UVM
Key SystemVerilog Additions
FEATURE WHAT IT DOES
logic Replaces both wire and reg — less confusion
always_comb Replaces always @(*) — compiler checks it's
combinational
always_ff Replaces always @(posedge clk) — compiler
checks it's sequential
always_latch Explicitly declares latch intent
Interfaces Bundle signals for cleaner port connections
Classes OOP for testbenches
Assertions assert property (...) for formal/sim checks
Constraints rand variables with constraint blocks
UVM Architecture (High Level)
UVM is a framework for building reusable, layered testbenches. The
flow:
sequence → sequencer → driver → DUT → monitor → scoreboard
COMPONENT ROLE
uvm_driver Drives stimulus to the DUT
uvm_monitor Observes DUT inputs/outputs
uvm_scoreboard Checks correctness (expected vs actual)
uvm_sequence Generates a stream of transactions
uvm_agent Groups driver + monitor + sequencer
uvm_env Top-level container for agents
SECTION 11
Python Scripting (DV/CAD Roles)
Common task: "Parse this log file and extract error information."
import re
from collections import Counter
error_counts = Counter()
with open("[Link]", "r") as f:
for line in f:
match = [Link](r"ERROR:\s*\[(\w+)\]", line)
if match:
error_counts[[Link](1)] += 1
for err_type, count in error_counts.most_common():
print(f"{err_type}: {count}")
Bit manipulation in Python:
value = 0xDEADBEEF
# Extract bits [15:8]
field = (value >> 8) & 0xFF # → 0xBE
SECTION 12
C/C++ for Firmware & Embedded
#include <stdint.h>
// Set bit n
uint32_t set_bit(uint32_t reg, int n) {
return reg | (1U << n);
}
// Clear bit n
uint32_t clear_bit(uint32_t reg, int n) {
return reg & ~(1U << n);
}
// Toggle bit n
uint32_t toggle_bit(uint32_t reg, int n) {
return reg ^ (1U << n);
}
// Extract bit field [high:low]
uint32_t extract_field(uint32_t reg, int high, int low) {
uint32_t mask = ((1U << (high - low + 1)) - 1) << low;
return (reg & mask) >> low;
}
Q: "Why volatile ?"
Tells the compiler the value can change at any time (hardware can
update it), so don't optimize away reads/writes to this address.
Q: "Is x a power of 2?"
(x != 0) && ((x & (x - 1)) == 0) — Classic trick. x-1 flips all
bits below the single set bit.
SECTION 13
Real Interview Questions from Industry
Reported from Intel, AMD, NVIDIA, Qualcomm, Broadcom, Cadence,
Synopsys, Apple, Google, and Amazon (Annapurna Labs).
RTL Design Questions
1. Design a synchronous FIFO with depth 8 and data width 8.
Follow-up: What happens on simultaneous read+write when full?
2. Design a parameterized N-bit counter with enable and reset.
3. Implement a priority encoder for 8 inputs.
4. Design a round-robin arbiter for 4 requestors.
5. Implement a sequence detector FSM for "1011" (overlapping).
6. Swap two registers without a temp register. Explain why it
works.
7. Write a 4:1 mux using (a) case, (b) ternary, (c) if-else.
8. Design a divide-by-3 clock with 50% duty cycle.
9. Sync vs async reset — write Verilog for both.
10. Write code that infers a latch. Now fix it.
11. Given b <= a; c <= b; — draw the timing diagram.
12. Design an asynchronous FIFO. Explain Gray code pointers.
13. What is metastability? How do you solve it?
14. Implement a dual-port RAM.
15. Design a CDC circuit for a single-bit signal.
16. Write a Moore FSM with 4 states.
17. Mealy vs Moore — what's the difference?
18. What does casez do differently from case ?
19. How do you avoid latch inference?
20. Design a serial-to-parallel converter.
Verification Questions
21. Explain the UVM testbench architecture.
22. Task vs function in SystemVerilog?
23. Write a constrained random class for valid Ethernet frames.
24. Functional coverage vs code coverage?
25. Write an assertion: "req must be followed by ack within 5
cycles."
26. What is a virtual function? Why use it in UVM?
Digital Logic & Concept Questions
27. Setup time and hold time?
28. Critical path? How to fix timing violations?
29. Explain pipelining and its trade-offs.
30. Clock skew vs clock jitter?
31. How does an async FIFO handle CDC?
32. What is DFT? What is scan chain?
33. Power gating vs clock gating?
34. FPGA vs ASIC flows?
SECTION 14
Interview Format & Strategy
Typical Structure
ROUND FORMAT CONTENT
Phone 45–60 Resume + technical Q&A (concepts, maybe
Screen min simple coding)
On-site 1 45–60 RTL Coding — spec → module → edge cases →
min synthesis
On-site 2 45–60 Digital Logic / Architecture — timing
min diagrams, pipelines
On-site 3 45–60 Verification (DV) or Scripting, or system
min design
On-site 4 45–60 Behavioral — past projects, debugging,
min teamwork
How to Approach the Coding Question
1. Clarify the spec — ask questions! Data width? Depth? Sync or
async? Overflow behavior?
2. Draw the block diagram first — show you can visualize
hardware before writing code. This alone can save you if your
code has bugs.
3. Write the module header — get the interface (ports) right
before any logic.
4. Write the logic — start with reset, then normal operation,
always add default/else.
5. Trace edge cases — reset, boundary conditions (full/empty),
simultaneous operations.
6. Discuss synthesis — how many flip-flops? Critical path? Any
latches?
SECTION 15
Resources & Links
Online Resources
RESOURCE BEST FOR
HDLBits ([Link]) Interactive Verilog exercises — best
for practice
ChipVerify ([Link]) Free Verilog/SV tutorials &
interview questions
FPGA4Student ([Link]) Complete Verilog projects with
code
Nandland ([Link]) Beginner-friendly Verilog & FPGA
tutorials
ASIC World ([Link]) Comprehensive Verilog reference
GitHub: RTL interview problems with
pengwubj/hw_interview_questions solutions
Books
BOOK BEST FOR
"Digital Design and Computer Architecture" Best intro to digital
— Harris & Harris design + Verilog
"Verilog HDL" — Samir Palnitkar The classic Verilog
reference
"SystemVerilog for Verification" — Chris Essential for DV roles
Spear
"CMOS VLSI Design" — Weste & Harris Transistor-level &
synthesis
Practice Strategy
1. Do all exercises on HDLBits (free, interactive, auto-graded)
2. Code the Big 5 from memory until you can write each in 15
minutes
3. For each design, practice: block diagram → code → timing
diagram → "what hardware?"
4. Review 5 interview questions per day from ChipVerify
5. If targeting DV roles, also study UVM architecture
QUICK REFERENCE
Cheat Sheet
TOPIC RULE
Combinational always @(*) + =
Sequential always @(posedge clk) + <=
Avoid Latches Always assign default values or cover all cases
FIFO Extra MSB on pointers. Empty: wr==rd . Full: MSBs
Full/Empty differ, rest equal.
CDC (1-bit) Two-flop synchronizer
CDC (multi- Async FIFO with Gray code pointers
bit)
FSM Style 3-block: state reg, next-state logic, output logic
Power of 2? (x != 0) && ((x & (x-1)) == 0)
Gray Code gray = bin ^ (bin >> 1)
Good luck with your interviews.