Combinational Logic
assign and always @(*) - output depends only on inputs
Combinational logic means the output depends only on the inputs right now. There is no memory. If you know the inputs, you know the output - like a calculator that shows the answer the instant you type the numbers.
Two ways to write it
Verilog gives you two styles for the same thing. Use whichever reads more clearly for the job.
Style 1 - assign (dataflow)
// 2-to-1 mux with a continuous assignment
module mux2 (
input wire a,
input wire b,
input wire sel,
output wire y
);
assign y = sel ? a : b;
endmoduleStyle 2 - always @(*) (behavioral)
When the logic has several cases, an always block reads better. For combinational logic, the sensitivity list must be @(*), which means "re-evaluate whenever any input changes."
module mux2 (
input wire a,
input wire b,
input wire sel,
output reg y // reg because it is assigned in always
);
always @(*) begin
if (sel)
y = a;
else
y = b;
end
endmoduleNotice y is declared output reg here, but output wire in the assign version. Rule of thumb again: assigned inside always -> reg. Assigned with assign -> wire. It is purely about syntax, not about whether real hardware registers appear.
The golden rules for combinational always blocks
- Use @(*) for the sensitivity list - never list signals by hand, you will forget one.
- Use blocking assignment (=) inside, not <=. (Lesson 8 explains why.)
- Assign the output in every possible path through the block. If you do not, Verilog builds an unwanted memory element called a latch.
This is the famous "inferred latch" bug. If your always @(*) sets y only in some branches and forgets others, the tool decides y must "remember" its old value, and silently adds a latch. The fix is simple: give every output a default value at the top of the block, before the if/case. The next lesson shows exactly how.