This is the first part of our Verilog interview series covering fundamental RTL design concepts. These questions target entry-level and mid-level design roles where fluency in basic Verilog constructs is essential.
wire and reg in Verilog?A wire is used to model combinational logic and represents a physical connection between elements. It must be driven by continuous assignment (assign) or by being the output of a module instance. A reg, despite its name, does not necessarily map to a hardware register. It is a variable that stores a value and is updated inside always or initial blocks. In Verilog, a reg can represent either sequential or combinational logic depending on how it is assigned. In SystemVerilog, logic replaces both wire and reg in most cases.
=) vs non-blocking (<=) assignments.Blocking assignments (=) execute sequentially in the order they appear within a begin-end block. Each assignment blocks the next until it completes, making them suitable for combinational logic. Non-blocking assignments (<=) evaluate the right-hand side immediately but defer the left-hand side update until the end of the time step. They are used for sequential logic (flip-flops) because they allow multiple assignments to happen concurrently, modeling real hardware behavior. Mixing them incorrectly is a common source of simulation-synthesis mismatches.
always @(*) and always @(a or b)?always @(*) is an implicit sensitivity list that automatically includes all signals read inside the block. It is the modern, preferred style for combinational logic because it avoids simulation mismatches when the block's inputs change. always @(a or b) is an explicit sensitivity list that only triggers when a or b change. If the block also reads signals not listed (e.g., c), the simulation will not re-evaluate when c changes, causing a mismatch with synthesized hardware (which is purely combinational). Always use @(*) for combinational logic.
&, |) and logical (&&, ||) operators?Bitwise operators (&, |, ^, ~) operate on every bit of the operands independently and return a vector result. For example, 4'b1010 & 4'b1100 yields 4'b1000. Logical operators (&&, ||, !) treat operands as boolean values (zero = false, non-zero = true) and return a single-bit result (0 or 1). For example, 4'b1010 && 4'b0000 yields 1'b0 because the second operand is zero.
parameter and localparam differ?Both parameter and localparam define constants in a module. A parameter can be overridden during module instantiation using #(.PARAM_NAME(value)) or with defparam, making it useful for parameterizable designs. A localparam cannot be overridden from outside the module; it is strictly internal to the module. Use localparam for constants that should not be modified by the instantiator, such as state encoding or derived values.
assign vs an always block for combinational logic?Use assign (continuous assignment) for simple combinational logic where the output is a direct function of inputs, such as assign sum = a ^ b;. Use an always @(*) block for more complex combinational logic that requires if-else or case statements, such as a multiplexer or priority encoder. Both synthesize to the same hardware, but assign is more concise for simple expressions while always provides greater control flow for complex logic.
Continuous assignment uses the assign keyword to drive a wire (or tri) with a combinational expression. It is called "continuous" because the right-hand side is continuously re-evaluated whenever any input changes. The left-hand side must be a net type (wire, tri, etc.) and cannot be a reg. Continuous assignments are the primary way to model combinational logic outside of procedural blocks and are synthesizable.
A sensitivity list specifies the signals that cause an always block to re-execute. In always @(posedge clk), the block triggers only on the positive edge of clk. For combinational logic, always @(*) or always_comb (SystemVerilog) infers the list automatically. An incomplete sensitivity list can lead to a simulation-synthesis mismatch: the simulation may hold stale values while the synthesized hardware always recomputes.
$display and $monitor.$display prints a formatted message once when the statement is executed, similar to printf in C. It is commonly used for debugging at specific simulation points. $monitor automatically prints a message whenever any variable in its argument list changes value. It is typically placed once in an initial block and continuously watches for changes throughout the simulation. $monitor is useful for tracing signal activity without adding explicit print statements.
Module instantiation creates an instance of a lower-level module inside a higher-level module. Ports can be connected by order (positional) or by name. Named connections are preferred for readability and maintainability.
module adder(input [3:0] a, b, output [4:0] sum);
assign sum = a + b;
endmodule
module top;
wire [3:0] x, y;
wire [4:0] z;
adder u_adder (.a(x), .b(y), .sum(z));
endmodule
Mastering these foundational Verilog concepts is critical for any RTL design interview. Practice coding these patterns until they become second nature. Proceed to Part 2 for FSM and race condition questions.