Verilog coding is a core skill for freshers applying for RTL design and digital design roles. Technical interviews often require candidates to write a small hardware module, explain its behavior, identify edge cases, or modify the code when the interviewer introduces a new requirement.
Knowing Verilog syntax alone is not enough. You must understand whether a design is combinational or sequential, how reset and enable signals behave, how registers update at clock edges, and whether your code describes the intended hardware.
This guide covers practical Verilog coding problems every RTL fresher should practice, with code examples, explanations, and common mistakes. Start by solving each problem independently, then compare your approach with the examples.
1. Write a 2:1 Multiplexer
Problem: Design a 2:1 multiplexer with inputs a, b, and select signal sel. When sel is zero, the output should be a; otherwise, it should be b.
module mux2to1 (
input wire a,
input wire b,
input wire sel,
output wire y
);
assign y = sel ? b : a;
endmodule
Explanation: This is combinational logic. The output depends on the current input values, not on a clock edge. The conditional operator selects one of the two inputs.
Interview follow-up: How would you implement a 4:1 multiplexer? You could use nested conditional operators, a case statement inside a combinational process, or multiple 2:1 multiplexers.
Common mistake: Introducing a clocked process for a circuit that does not require storage.
2. Design a D Flip-Flop With Synchronous Reset
Problem: Create a D flip-flop that captures d on the rising edge of clk. If reset is high at that edge, the output must become zero.
module dff_sync_reset (
input wire clk,
input wire reset,
input wire d,
output reg q
);
always @(posedge clk) begin
if (reset)
q <= 1'b0;
else
q <= d;
end
endmodule
Explanation: The output changes only on the rising clock edge. Reset is synchronous because it is checked inside the clocked process and does not independently trigger an update.
Non-blocking assignment (<=) is the standard choice for modeling sequential register updates.
Interview follow-up: How would you implement an asynchronous reset? You would normally include the reset edge in the sensitivity list, such as negedge reset_n for an active-low reset, and define the reset branch accordingly.
Common mistake: Confusing synchronous and asynchronous reset behavior.
3. Implement an Up Counter With Enable
Problem: Design an 8-bit counter that increments on each rising clock edge when enable is high and resets to zero when synchronous reset is asserted.
module up_counter (
input wire clk,
input wire reset,
input wire enable,
output reg [7:0] count
);
always @(posedge clk) begin
if (reset)
count <= 8'd0;
else if (enable)
count <= count + 1'b1;
end
endmodule
Explanation: Reset has priority over enable. When both are low, no assignment occurs to count, so the register retains its previous value.
Because the counter is eight bits wide, it wraps from 255 to 0 under ordinary unsigned arithmetic.
Interview follow-up: How would you create a counter that stops at 99? Add a terminal-count condition so the counter holds its value when it reaches the required limit. Be clear about whether the design should wrap, saturate, or restart at zero.
Common mistake: Ignoring overflow behavior or failing to clarify the required counting range.
4. Write a Shift Register
Problem: Create a four-bit serial-in, serial-out shift register. On every rising edge, shift the stored bits toward the most significant bit and insert serial_in into bit zero.
module shift_register (
input wire clk,
input wire reset,
input wire serial_in,
output reg [3:0] q
);
always @(posedge clk) begin
if (reset)
q <= 4'b0000;
else
q <= {q[2:0], serial_in};
end
endmodule
Explanation: The concatenation {q[2:0], serial_in} constructs the next four-bit value. The previous bits move one position toward the most significant bit, and the new serial bit enters at the least significant position.
Practical example: Shift registers are useful in serial data handling and in circuits that need to move data through a sequence of register stages.
Interview follow-up: Add an enable input so shifting occurs only when enabled. The register should hold its value when enable is low.
Common mistake: Reversing the shift direction or inserting the serial bit into the wrong position.
5. Design a Mod-10 Counter
Problem: Design a counter that cycles through values 0 to 9 and then returns to zero.
module mod10_counter (
input wire clk,
input wire reset,
output reg [3:0] count
);
always @(posedge clk) begin
if (reset)
count <= 4'd0;
else if (count == 4'd9)
count <= 4'd0;
else
count <= count + 1'b1;
end
endmodule
Explanation: Four bits are sufficient to represent values 0 through 9. The terminal-count condition ensures that the counter returns to zero after reaching nine rather than continuing to 15.
Interview follow-up: What happens if the counter starts in an illegal state such as 12? In this implementation, it increments normally until it wraps around. If recovery from illegal states is required, add an explicit recovery condition based on the specification.
Common mistake: Using an incorrect terminal count or choosing a width without considering the required range.
6. Implement a Combinational ALU
Problem: Design a simple 4-bit arithmetic logic unit that supports addition, subtraction, AND, and OR operations.
module simple_alu (
input wire [3:0] a,
input wire [3:0] b,
input wire [1:0] op,
output reg [3:0] y
);
always @(*) begin
case (op)
2'b00: y = a + b;
2'b01: y = a - b;
2'b10: y = a & b;
2'b11: y = a | b;
default: y = 4'b0000;
endcase
end
endmodule
Explanation: The case statement selects the operation according to op. Since all possible two-bit values are covered, every valid operation has a defined assignment. The default assignment provides an explicit fallback.
The output is four bits wide, so arithmetic results are truncated to four bits. If a carry-out is required, the design needs a wider intermediate result or a separate carry output.
Interview follow-up: Add XOR, comparison, or a zero flag. Explain how you would handle signed versus unsigned arithmetic.
Common mistake: Forgetting that output width affects the result of arithmetic operations.
7. Design an FSM for a Simple Sequence Detector
Problem: Detect the serial bit pattern 101 in an incoming data stream. For this example, use a Moore-style FSM with overlapping sequence detection.
A practical solution requires defining states that represent the amount of the pattern matched so far. The following example uses four states: no match, matched 1, matched 10, and matched 101.
module sequence_detector (
input wire clk,
input wire reset,
input wire din,
output reg detected
);
localparam IDLE = 2'b00,
S1 = 2'b01,
S10 = 2'b10,
S101 = 2'b11;
reg [1:0] state, next_state;
always @(posedge clk) begin
if (reset)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
next_state = IDLE;
detected = 1'b0;
case (state)
IDLE: begin
if (din)
next_state = S1;
else
next_state = IDLE;
end
S1: begin
if (din)
next_state = S1;
else
next_state = S10;
end
S10: begin
if (din)
next_state = S101;
else
next_state = IDLE;
end
S101: begin
detected = 1'b1;
if (din)
next_state = S1;
else
next_state = S10;
end
default: next_state = IDLE;
endcase
end
endmodule
Explanation: The state represents the matched prefix of the sequence. After detecting 101, the machine retains a valid suffix for the next input, allowing overlap.
Interview follow-up: Explain how you would modify the machine to detect a different pattern or implement a Mealy version. Be prepared to trace the state transitions for an input stream such as 10101.
Common mistake: Writing the state transitions without checking overlapping patterns or failing to assign defaults in combinational next-state logic.
8. Find and Fix an Unintended Latch
Problem: An interviewer provides the following code and asks what is wrong.
always @(*) begin
if (enable)
y = a;
end
What is the issue? When enable is low, y is not assigned. It must retain its previous value, which implies storage and can infer a latch.
If combinational behavior is intended, provide a default assignment:
always @(*) begin
y = 1'b0;
if (enable)
y = a;
end
This version assigns y in every execution of the process. The correct default value depends on the required functionality.
Interview follow-up: How can you identify unintended latch inference? Review RTL and lint warnings, inspect synthesis reports, and check that every output of a combinational process receives an assignment on every possible path.
Common mistake: Assuming that always @(*) automatically makes every design combinational. The assignments inside the process determine whether storage is required.
9. Write a Parameterized Counter
Problem: Create a reusable counter whose width can be changed through a parameter.
module parameter_counter #(
parameter WIDTH = 8
) (
input wire clk,
input wire reset,
input wire enable,
output reg [WIDTH-1:0] count
);
always @(posedge clk) begin
if (reset)
count <= {WIDTH{1'b0}};
else if (enable)
count <= count + 1'b1;
end
endmodule
Explanation: The WIDTH parameter controls the number of counter bits. The replication expression {WIDTH{1'b0}} creates a zero value with the configured width.
For an unsigned counter of width (N), the representable range is (0) through (2^N-1), and normal incrementing wraps around after the maximum value.
Interview follow-up: How would you support a programmable terminal count? Explain how you would handle parameter values and prevent invalid configurations.
Common mistake: Hard-coding the width in part of the logic, which makes a supposedly reusable module harder to maintain.
10. How Should You Test Your Verilog Solutions?
Writing code is only part of an RTL coding problem. You should also explain how you would verify the implementation.
For each design, consider:
- Normal behavior: Does it work for typical input sequences?
- Reset behavior: Does it reset correctly at the specified time?
- Boundary conditions: What happens at zero, maximum count, or a terminal state?
- Control priority: What happens if reset and enable are asserted together?
- Combinational completeness: Are all outputs assigned on every path?
- Width and signedness: Could an operation overflow or truncate unexpectedly?
For example, a counter testbench should check reset, incrementing, holding when disabled, and wrap-around. An FSM testbench should check expected state transitions and detection behavior. A combinational ALU should be tested for every operation code and representative input values.
Use a simulator to validate your assumptions. Passing a few test cases does not prove correctness, but systematic tests make functional mistakes easier to find.
For additional practice, read VLSIGuru’s Verilog Coding Interview Questions in VLSI and InSkill’s Top SystemVerilog Coding Questions for VLSI Interviews.
Conclusion
Verilog coding interviews assess whether you can translate a specification into correct RTL and explain the hardware behavior clearly. Freshers should practise multiplexers, flip-flops, counters, shift registers, ALUs, FSMs, parameterized designs, and common coding-error scenarios.
For every problem, clarify reset behavior, input and output requirements, edge cases, and whether the design is combinational or sequential before writing code. Then explain the solution, identify its limitations, and describe how you would test it.
Consistent practice with simulation and debugging will help you develop the skills expected in entry-level RTL design interviews.