Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutRoadmapCareersInternshipsFAQContactPrivacyTerms
Skip to lesson

Course contents

  • Logic Synthesis Glossary
  • Logic Synthesis Cheatsheet
  • Synthesis Foundations14
    • Synthesis Flow Overview
    • Synthesizable RTL and Inference
      • Combinational Blocks Infer Logic
      • Priority Chains versus Parallel Selects
      • Latch Inference from Incomplete Assignment
      • Flops, Tri-States and Unrolled Loops
      • What Is Not Synthesizable
    • Reading Netlists and Reports
  • Logic Optimisation and Mapping20
  • Constraints and Timing15
  • Sequential, FSM and Memory19
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

←Synthesizable RTL and Inference
  1. Topics
  2. Logic Synthesis
  3. Synthesis Foundations
  4. Synthesizable RTL and Inference
  5. Combinational Blocks Infer Logic
On this page

    Part 1 of 9, lesson 6 of 14Part 1 of 9/Synthesis Foundations/Lesson 6 of 14Intermediate

    Combinational Blocks Infer Logic

    Each combinational operator infers its word cell, and techmap expands them to bit cells.

    Every combinational operator infers one word cell, and techmap expands each into bit cells. Without that reflex, a bit-level report reads as a verdict on the function instead of a count of the operators written.

    After this lesson you can: name the word cell each operator infers, read the bit-level count after techmap, and predict the latch a dropped default adds.

    The question

    Every combinational operator has a cell waiting for it. Addition becomes an adder, comparison becomes a comparator, a shift becomes a shifter, a ternary becomes a multiplexer, and a case becomes a parallel mux with decoders. Elaboration works one operator at a time and never looks at the neighbours, so the word-level netlist reads like the RTL with one cell per operation. A later pass, techmap, expands each word cell into single-bit generic cells, which is the level the mapper consumes; area and delay estimates begin only after mapping.

    The design below exercises five operators over 4-bit buses in a single module: + on two 4-bit buses, == on the same pair, a variable << shift, a ?: select, and a four-way case with a default. One script runs proc and opt_clean, records the word cells, then runs techmap and records the bit cells. Predict first, then read the recorded lists against your guess.

    Predict first Five operators, one module. Which word cell does each infer, and what survives techmap?

    Addition records 1 $add cell, comparison records 3 $eq cells with 2 of them decoding the case, the shift records 1 $shl cell, the ternary records 1 $mux cell, and the case records 1 $pmux cell. After techmap the netlist holds 118 single-bit generic cells and no word cells at all.

    One file, five operators

    The script reads the module on its own, selects it as top, and runs proc, which turns processes into word cells, plus opt_clean, which drops unused wires without merging operators. It records stat and the JSON netlist, then asserts the five expected families and the absence of latches: the case has a default, so every path assigns the output. Then techmap runs, the bit-level stat and netlist are recorded, and one PASS line covers both levels, so a passing run certifies the word reading and the bit reading together.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    read_verilog <<EOV
    module operators (
      input wire [3:0] a, b,
      input wire [2:0] sh,
      input wire sel,
      input wire [1:0] op,
      output wire [4:0] sum,
      output wire eq,
      output wire [3:0] shl,
      output wire [3:0] m,
      output wire [3:0] c
    );
      assign sum = a + b;
      assign eq = (a == b);
      assign shl = a << sh;
      assign m = sel ? a : b;
      always @* begin
        case (op)
          2'd0: c = a;
          2'd1: c = b;
          2'd2: c = a ^ b;
          default: c = 4'd0;
        endcase
      end
    endmodule
    EOV
    hierarchy -check -top operators
    proc
    opt_clean
    tee -o stat_word.txt stat
    write_json net_word.json
    select -assert-none t:$dlatch
    select -assert-count 1 t:$add
    select -assert-count 1 t:$shl
    select -assert-count 1 t:$mux
    select -assert-count 1 t:$pmux
    select -assert-min 1 t:$eq
    techmap
    tee -o stat_bits.txt stat
    write_json net_bits.json
    select -assert-min 1 t:$_AND_
    select -assert-min 1 t:$_MUX_
    log PASS: operators infer word cells and then bit cells

    What the run recorded

    The table below reads the word-level netlist cell by cell; every entry is a recorded count, not a guess from the RTL. Notice the comparator row: only one $eq compares the two buses, and the other two decode the four-valued case selector beside the $pmux. Elaboration does not share decoders between unrelated operators; each operation brings its own.

    RTL operator

    Recorded word cells

    Reading

    a + b

    1

    One adder on the 4-bit pair.

    a == b

    1 of 3 $eq

    One comparator; the rest decode the case.

    a << sh

    1

    One variable shifter.

    sel ? a : b

    1

    One 4-bit multiplexer.

    four-way case

    1 $pmux

    One parallel mux plus its $eq decoders.

    The full word list is 1 $add, 3 $eq, 1 $logic_not, 1 $pmux, 1 $shl, 1 $mux, 1 $xor: the table rows plus 1 $xor cell for the exclusive-or branch and 1 $logic_not cell finishing the case decode. Nothing here is shared, folded, or simplified; that happens later, in optimization passes that have the whole netlist in view.

    The operators module after proc and opt_clean: 1 $add, 3 $eq, 1 $logic_not, 1 $pmux, 1 $shl, 1 $mux, 1 $xor, with no latch and no bit cells yet.

    From words to bits

    Techmap replaces every word cell with single-bit generic cells, and the recorded bit list is 28 $_XOR_, 27 $_OR_, 29 $_AND_, 9 $_NOT_, 25 $_MUX_: 118 cells in total. The adder dominates the count because addition carries across all five sum bits, while the selects become trees of bit muxes. This is the level the mapper works at, so when a later lesson quotes gates or area, this expansion is what those numbers count.

    Break it: drop the default

    Delete the default line from the case twin and elaborate it with proc alone. The word netlist keeps its select core and gains 1 $dlatch cell holding the old byte whenever the selector parks on the uncovered value. The script below records both halves of that contrast under one PASS line: the full twin asserts no latch with select logic present, and the default-free twin elaborated with proc asserts the latch.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    read_verilog <<EOV
    module case_full (
      input wire [1:0] sel,
      input wire [7:0] a,
      input wire [7:0] b,
      input wire [7:0] c,
      input wire [7:0] d,
      output reg [7:0] y
    );
      always @* begin
        case (sel)
          2'd0: y = a;
          2'd1: y = b;
          2'd2: y = c;
          default: y = d;
        endcase
      end
    endmodule
    EOV
    hierarchy -check -top case_full
    proc
    tee -o stat_full.txt stat
    write_json net_full.json
    select -assert-none t:$dlatch
    select -assert-min 1 t:$pmux t:$mux %u
    design -reset
    read_verilog <<EOV
    module case_nodflt (
      input wire [1:0] sel,
      input wire [7:0] a,
      input wire [7:0] b,
      input wire [7:0] c,
      input wire [7:0] d,
      output reg [7:0] y
    );
      always @* begin
        case (sel)
          2'd0: y = a;
          2'd1: y = b;
          2'd2: y = c;
        endcase
      end
    endmodule
    EOV
    hierarchy -check -top case_nodflt
    proc
    tee -o stat_nodflt.txt stat
    write_json net_nodflt.json
    select -assert-count 1 t:$dlatch
    log PASS: full case infers select logic with no latch while the missing branch infers one latch

    Variant

    Recorded $dlatch cells

    Verdict

    Full case with default

    0

    Combinational: every path assigns y.

    Case with no default

    1

    Latch: selector value 3 must hold y.

    That is the whole latch lesson in two rows; the latch lesson later in this topic proves the fixes three ways and explains why an always_comb block refuses the second row outright. Read on there for the missing-else shape and the SystemVerilog check.

    Common mistakes

    Reading the bit count back onto the RTL. Techmap expands each word cell independently, so the bit total measures the operators you wrote, not the function you meant. A second classic is the half-added default: a default that assigns only some of several outputs leaves the other outputs latched, so check every output on every path.

    What does each combinational operator become, and what changes at techmap?

    Weak answer: operators become gates, and techmap makes them smaller.

    Strong answer: each operator infers its own word cell, recorded as 1 $add, 3 $eq, 1 $logic_not, 1 $pmux, 1 $shl, 1 $mux, 1 $xor, and techmap expands them into 118 two-input bit cells with no sharing between unrelated operators. Dropping the case default adds 1 $dlatch beside the select logic instead of simplifying it.

    Follow-up: the bit count looks too big for the function. Answer: read the word netlist first and find the operator you did not mean, because techmap only expands what elaboration inferred.

    Check yourself The module computes a + b and a == b. Which word cells does the recorded run show?

    One $add for the sum and one $eq for the comparison; the remaining recorded $eq cells decode the case selector, so each operation brings its own cells.

    Check yourself The case loses its default. What changes in the recorded netlist?

    One $dlatch appears beside the unchanged select core: the recorded default-free netlist shows 1 latch because the missing path must hold the old value.

    • Each operator infers its own word cell: + gives $add, == gives $eq, ?: gives $mux, case gives $pmux.

    • Techmap expands word cells into single-bit generic cells with no sharing between operators.

    • A case that leaves a selector value uncovered adds one $dlatch beside the select logic.

    Rule of thumb: one operator, one word cell; every selector value needs an assignment.

    How this was checked

    Every number and picture on this page comes from Yosys 0.70+post runs recorded by the course checker: the operators module runs proc and opt_clean for the word netlist plus write_json, then techmap for the bit netlist, with the expected families asserted and exactly one PASS line. A second unit contrasts the full case against its default-free twin, asserting no latch on one side and one $dlatch on the other.

    • Interview: run a mock interview on this topic

    Related topics

    • Priority Chains versus Parallel Selects

    • Reading Netlists and Reports

    • Muxes, Decoders and Encoders

    Previous

    8 / 139

    Next: Priority Chains versus Parallel Selects

    Up next

    Up nextPriority Chains versus Parallel Selects

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Latch Inference from Incomplete Assignment
    • Flops, Tri-States and Unrolled Loops
    • What Is Not Synthesizable