Combinational logic has no memory. Its output is a pure function of its inputs, right now, and whenever any input changes, the output must be recomputed. In SystemVerilog you express this with an always block that runs whenever its inputs change. The defining property to hold in your head: a combinational block must react to every input, every time, with no exceptions and nothing remembered from before.
Older style code listed, by hand, the signals a block should react to, its sensitivity list. Hand written lists invite a quiet disaster: forget one signal, and the block simply does not react when that signal changes. The output holds its old value instead of recomputing, and holding an old value is memory. Your supposedly combinational logic has just grown state you never asked for.
SystemVerilog provides a construct that removes the hand written list entirely: always_comb. It computes the sensitivity automatically from the signals the block actually reads, so nothing can be forgotten. It also declares your intent to the tools: this block is meant to be purely combinational, and tools are entitled to complain if the code inside does not live up to that promise. In this primer, combinational logic is always written with always_comb, and hand written sensitivity lists are treated as the legacy hazard they are.
Here is the trap this page exists to teach. Even with automatic sensitivity, there is a second way to smuggle memory into combinational logic: fail to assign the output on some path through the block. If an if has no else, or a case does not cover every possibility, then on the uncovered path the output is never assigned, so it must hold its previous value. Holding a previous value requires storage, and the synthesis tool will oblige by inferring a latch, a level sensitive storage element you never intended, with timing and testability problems attached.
The rule that prevents it is simple and absolute: every output must be assigned on every path. Cover every branch, give every if its else or a default assignment up front, and the latch has nowhere to hide. The engine on this page will catch you if you leave a path open, exactly the way a real lint tool does on the job. This is the same discipline as always holding a value across simulated time, seen back on Page 03: an unassigned output is not nothing, it is a stale value quietly held in place.
Complete the combinational block below so the output is assigned on every path. The block selects a result based on a two bit selector: selector zero gives the first input, selector one gives the second, selector two gives the third, and any other value must give zero. Write it with always_comb and make sure no path leaves the output unassigned. Run and submit. Leave a path open, and the engine will flag the latch it infers.
Weekly intelligence on VLSI, AI for EDA, and chip careers. 20,000+ engineers already inside.