Combinational Logic in Verilog
Combinational logic has no memory. Its outputs depend only on current inputs. In an FPGA, combinational logic usually maps to LUTs and routing.
Learning Objectives
By the end of this lesson, you should be able to:
- describe combinational Verilog as hardware, not software flow;
- write continuous assignments;
- use
always @*safely; - avoid accidental latch inference;
- connect truth tables to HDL expressions and LUTs.
Continuous Logic
module majority3 (
input wire a,
input wire b,
input wire c,
output wire y
);
assign y = (a & b) | (a & c) | (b & c);
endmodule
This describes a combinational function. The synthesized result is logic, not a function call running on a processor.
Multiplexers
A multiplexer is one of the most common combinational structures.
assign y = sel ? b : a;
For a wider selector:
always @* begin
y = 1'b0;
case (sel)
2'b00: y = d0;
2'b01: y = d1;
2'b10: y = d2;
2'b11: y = d3;
endcase
end
The default assignment is deliberate. It gives y a value even before the case choices are considered.
Combinational Blocks
Use combinational blocks when logic is easier to read procedurally.
always @* begin
y = 1'b0;
if (enable) begin
y = a ^ b;
end
end
SystemVerilog users can write always_comb, which gives stronger tool checking:
always_comb begin
y = 1'b0;
if (enable)
y = a ^ b;
end
Accidental Latches
If a combinational output is not assigned in some branch, the tool may infer a latch to remember the old value.
always @* begin
if (enable)
y = a;
// y is missing when enable is 0
end
Unless you intentionally need a latch, this is a bug. In synchronous FPGA design, latches are rarely what you want.
Worked Example: 2-to-4 Decoder
module decoder2to4 (
input wire [1:0] sel,
output reg [3:0] y
);
always @* begin
y = 4'b0000;
case (sel)
2'b00: y = 4'b0001;
2'b01: y = 4'b0010;
2'b10: y = 4'b0100;
2'b11: y = 4'b1000;
endcase
end
endmodule
Every possible select value has a defined output.
Exercise
Write a 4-to-1 mux in Verilog:
- inputs:
d0,d1,d2,d3; - select:
sel[1:0]; - output:
y.
Implement it twice:
- with a ternary/conditional expression;
- with
always @*andcase.
Then write a truth table and confirm every select value has exactly one source.
Common Mistakes
- Missing default assignments in combinational blocks.
- Using blocking/nonblocking assignments randomly.
- Assuming HDL branch order implies time delay.
- Forgetting that all right-hand-side inputs create hardware dependencies.
- Writing a
casestatement without covering all choices.
Summary
Combinational FPGA logic maps naturally to LUTs. Use continuous assignments for simple expressions and combinational always @* / always_comb blocks for clearer decision logic. Always assign outputs on every path unless a latch is intentional.
Next: Exercise: Build Combinational Logic and a Testbench.
Further Reading
- Verilator warnings guide
- Yosys Verilog frontend documentation
- Vendor HDL coding guidelines