Combinational logic derives outputs from current inputs with no stored state. This section covers timing, selection, arithmetic, comparison and shifting, shared buses, and latch inference. Each block must handle every input combination, settle within a clock period, and assign every output on every path.
Topic | Lessons | After it you can… | Study time |
|---|---|---|---|
3 | Estimate logic depth and find and shorten the critical path. | 1–2 h | |
4 | Write parameterized selection logic and priority encoders. | 2 h | |
4 | Build and reason about adders, subtractors, multipliers. | 3 h | |
3 | Compare signed and unsigned values; build a barrel shifter. | 1–2 h | |
3 | Use tri-state correctly and replace it with muxes on-chip. | 1–2 h | |
3 | Write latch-free logic and use intentional latches safely. | 1–2 h | |
5 | Design fixed-point formats, width growth, rounding, saturation and dividers. | 2–3 h |
Study time covers reading each lesson and running its examples. The section's hands-on project takes extra hours on top.
always_comb block must assign every output on every path?Because an unassigned path forces the hardware to remember the old value, and that memory is a latch. The Latch inference topic covers the causes, the fix and the intentional exceptions.
Use these four questions to decide whether you are ready to move on: explain, predict, implement and debug. If one is shaky, each answer names the lesson to revisit. New readers can skip this until they finish the section.
always_comb block with a missing else create a latch, and what are the two standard ways to prevent it?On the path where the output is not assigned, the language says it keeps its old value, and keeping a value needs storage, so synthesis builds a latch. Prevent it with a default assignment at the top of the block, or by assigning every output in every branch (including a default case item). Revisit Why accidental latches happen and The complete-assignment pattern.
a = 4'b1100, b = 4'b0101, and sum is 4 bits wide. What does sum = a + b give, and what happened to the carry?12 + 5 = 17, which needs 5 bits. A 4-bit sum keeps the low four bits, 4'b0001, and the carry is silently dropped. Make the result one bit wider, or capture the carry with {carry, sum} = a + b. Revisit Adder building blocks.
d with select sel. What must you decide for sel values of N or more?assign y = d[sel*W +: W]; picks word sel. When N is not a power of two, codes N and above select past the end of d: simulation returns X and synthesis may give anything, so define them, for example assign y = (sel < N) ? d[sel*W +: W] : '0;. Revisit A parameterized mux.
>>> on logic [7:0] x, but negative values come out with zeros shifted in. Why?>>> fills with the sign bit only when the operand is signed. logic [7:0] is unsigned, so >>> behaves like >>. Declare it logic signed [7:0] or shift $signed(x). Also remember that >>> rounds toward minus infinity, unlike /. Revisit Barrel shifter and rotate.
Authors compile complete examples with node scripts/tools/check-rtl-snippets.mjs rtl/combinational and run the self-checking benches with node scripts/tools/run-rtl-testbenches.mjs rtl/combinational. These are separate validation commands, not an every-build gate. Partial snippets are excluded. The last full run (2026-10-16, Icarus Verilog 13.0) passed every bench.
Where the input space is small (up to about 16 bits), the testbench is exhaustive: it compares against a correct independent reference for every input combination in the tested configuration, so the PASS line shows agreement over all enumerated inputs, not a sample. It does not establish timing, X/Z handling or untested parameter choices. Timing and its limits are covered in the overview that follows.
The latch examples are synthesized with Yosys to show the real tool response.