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
  • Logic Optimisation and Mapping20
    • Boolean Optimisation
      • Constant Propagation
      • Redundancy Removal
      • Factoring, Collapsing and Sharing
      • AIGs, Depth and Area
      • Don't-Cares
    • Technology Mapping
    • Cell Libraries and Liberty
    • Structuring and Buffering
  • 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

←Boolean Optimisation
  1. Topics
  2. Logic Synthesis
  3. Logic Optimisation and Mapping
  4. Boolean Optimisation
  5. Constant Propagation
On this page

    Part 2 of 9, lesson 1 of 20Part 2 of 9/Logic Optimisation and Mapping/Lesson 1 of 20Intermediate

    Constant Propagation

    How tied inputs fold through gates, with a worked gate-count reduction.

    A single strapped pin can delete a whole cone of logic, and a changed parameter silently unfolds it again. Without seeing the fold, area moves for no visible reason.

    After this lesson you can: predict what a tied input, a mask bit, or a constant flop does to its cone, and read the before and after reports.

    The question

    A tied input looks harmless: one pin strapped to 1, one to 0. But each constant turns its gate into a simpler function, and the simpler function feeds the next gate, which may fold in turn. One tie-off can therefore delete a whole cone of logic, and this lesson measures exactly that on a real run.

    Constants arrive from several places: pins strapped on the board, parameters fixed for a build, width masks that keep some bits and kill others, and flops loaded with a constant. The tool treats them all the same way, folding forward until no gate has a constant input left. The script flattens the hierarchy first because opt does not push constants through module ports on its own.

    One tied input, three gates gone

    The design below computes one bit as y = (a & one) | (b & zero) inside a submodule, with the tie-offs strapped at the top level, alongside a 4-bit bus masked by a constant vector. The constants cross the hierarchy as port values rather than literals, so elaboration keeps the gates and the before picture shows them. The flattened script saves the generic netlist, records a stat report, runs opt, and records the second report. Mapping the folded result with ABC gives the final cover. The two assertions pin the outcome: no generic AND, OR or NOT cell survives.

    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
    read_verilog <<EOV
    module constfold_core(input wire a, input wire b, input wire [3:0] d, input wire one, input wire zero, input wire [3:0] mask, output wire y, output wire [3:0] q);
    assign y = (a & one) | (b & zero);
    assign q = d & mask;
    endmodule
    module constfold(input wire a, input wire b, input wire [3:0] d, output wire y, output wire [3:0] q);
    constfold_core core (.a(a), .b(b), .d(d), .one(1'b1), .zero(1'b0), .mask(4'b1010), .y(y), .q(q));
    endmodule
    EOV
    hierarchy -check -top constfold
    proc
    flatten
    delete t:$scopeinfo
    design -save pre
    tee -o stat_before.txt stat
    write_json net_before.json
    design -load pre
    opt
    opt_clean
    check
    tee -o stat_after.txt stat
    write_json net_opt.json
    techmap
    tee -o abc_after.txt abc -liberty course.lib -constr typical.constr -D 1000
    tee -o stat_mapped.txt stat -liberty course.lib
    write_json net_after.json
    select -assert-none t:$and t:$or t:$not
    check
    log PASS: constants fold away leaving wires that map to no cells
    Predict first Before you read the table: the expression has two ANDs and one OR, and the mask has a fourth AND. How many generic gates survive opt, and what does ABC cover?

    None of them. The recorded netlist after optimisation holds no AND cells and no OR cells, down from 3 and 1, and ABC maps the remainder to no cells at all. Folding (a & one) to a and (b & zero) to 0 leaves y = a | 0, which is the wire a, and the mask resolves bit by bit in the same way.

    Stage

    Generic AND

    Generic OR

    Mapped cover

    Before opt (recorded netlist)

    3

    1

    not mapped yet

    After opt (recorded netlist)

    0 (everything folded)

    0 (everything folded)

    not mapped yet

    After ABC to course.lib

    0 (nothing left to map)

    0 (nothing left to map)

    no cells: y is the wire a

    Read the table as cause and effect. Before optimisation the recorded netlist holds 3 generic AND cells and 1 generic OR cell: two ANDs plus the OR from the single bit, and one wide AND from the mask. After optimisation no generic cells remain, each fold having replaced a gate with an equal function.

    Diagram
    recorded stat before opt
    5. Printing statistics.
    
    === constfold ===
    
            +----------Local Count, excluding submodules.
            | 
           17 wires
           35 wire bits
           13 public wires
           28 public wire bits
            5 ports
           11 port bits
            4 cells
            3   $and
            1   $or
    Diagram
    recorded stat after opt
    10. Printing statistics.
    
    === constfold ===
    
            +----------Local Count, excluding submodules.
            | 
           13 wires
           28 wire bits
           13 public wires
           28 public wire bits
            5 ports
           11 port bits
    Diagram
    hand count for the single bit, from the course.lib cell table (AND2 and OR2 cost 3 units each)
    before: 2 AND2 + 1 OR2 = 2 x 3 + 1 x 3 = 9 area units
    after: y is the wire a = 0 area units
    saving: 9 area units, the whole cone
    Before optimisation: 3 AND cells and 1 OR cell from the inputs on the left to outputs y and q on the right.
    After optimisation: no cells remain. Output y is the wire a, and each bit of q is a wire or a constant.

    Wider constants: the mask

    The second output applies the same idea four times at once: q = d & mask with the top level driving 4'b1010. Each bit folds independently against its own mask bit. Bit 3 and bit 1 meet a 1 and survive as wires, while bit 2 and bit 0 meet a 0 and become constant drivers. The wide AND in the before picture is the elaborated form of this expression, and it is gone in the after picture for exactly this reason.

    Masks like this appear wherever software writes a control register with reserved bits: a constant vector folds lane by lane, so a masked bus costs nothing after optimisation. Parameters behave the same way, which is why changing one re-opens the cone and moves the area.

    Constants through a flop

    The second script ties a constant to a register instead of a gate: a flop whose data input is always 0. There is no combinational fold to perform, so a different pass does the work. opt_dff deletes the flop cell itself once its data input is constant, provided the flop's initial or reset value is undefined or already equals that constant (a flop initialized to 1 with data tied to 0 is kept, since deleting it would change the reset behaviour), and opt_clean then removes the dangling wires.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    read_verilog <<EOV
    module flopconst(input wire clk, output wire q);
    reg qq;
    always @(posedge clk) qq <= 1'b0;
    assign q = qq;
    endmodule
    EOV
    hierarchy -check -top flopconst
    proc
    write_json net_flop_before.json
    tee -o log_dff.txt opt_dff
    opt_clean
    check
    write_json net_flop_after.json
    select -assert-none t:$dff
    log PASS: flop with constant input becomes a constant driver
    Diagram
    recorded opt_dff log: banner plus the action line
    5. Executing OPT_DFF pass (perform DFF optimizations).
    Setting constant 0-bit at position 0 on $procdff$2 ($dff) from module flopconst.

    The recorded netlists hold 1 flop before the pass and 0 after it: the register is gone and the output is a constant wire. Resets and enables fold the same way whenever they are stuck: a flop that can never change state is not storage, it is a tie-off with a clock pin, and the tool treats it as one.

    Break it: leave an input undriven

    Now change one thing about the setup: leave the second AND input floating. An undriven input is not a constant; it is a wire with no driver, so it carries no value at all: not 0, not 1, and not even an x the folder could resolve. The third script builds y = a & u with u undriven, runs the same opt, and asserts that the AND gate is still there.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    read_verilog <<EOV
    module floatin(input wire a, output wire y);
    wire u;
    assign y = a & u;
    endmodule
    EOV
    hierarchy -top floatin
    proc
    opt
    write_json net_float.json
    select -assert-count 1 t:$and
    log PASS: undriven input has no driver so its gate survives optimisation

    The recorded break-it netlist keeps 1 AND cell after optimisation: nothing folded, because opt folds only known constants and an undriven wire is not one. Contrast that with the main run, where b and zero folded on a known value. Giving the wire a value takes an explicit step such as opt -undriven or setundef, so drive every input explicitly and never depend on a default you have not read.

    Common mistakes

    Read the after picture bit by bit before quoting any saving: the 1-bits survive as wires, the 0-bits as constants, and neither shows up in the cell count.

    An undriven input is not a 0, and a parameter is not a constant forever. The break-it run keeps its gate on a wire with no driver, and a rebuilt parameter set unfolds cones the last run had deleted. Pin inputs, and recheck area after every parameter change.

    Likely probes: folding order and cone deletion.

    • Weak: constants simplify gates, so the design gets smaller.

    • Strong: each gate with a constant input becomes a simpler equal function, and the results feed forward, so one tie-off deletes the whole cone: every generic gate folds away, and ABC maps the remainder to no cells at all. A flop with constant data goes the same way through opt_dff when its init already matches, and an undriven input folds nothing because a wire with no driver carries no value to fold.

    • Follow-up: your area report grows after a parameter change with no RTL edit. Which report do you reread first, and what do you expect to find unfolded?

    Check yourself y = (a & one) | (b & zero). What does the tool keep, and what does the mapped report show?

    Just the wire a. The ANDs and the OR fold away, and the mapped report lists no cells at all.

    Check yourself The break-it run keeps its AND gate while the main run loses all of its gates. What explains the difference?

    Known against driverless. The main run folds on the known 0 and 1, while the break-it input has no driver and no value, so there is nothing to fold. An explicit opt -undriven or setundef step would give it one.

    • Each gate with a constant input becomes a simpler equal function, feeding forward until nothing constant-fed remains.

    • A flop with constant data and a matching (or undefined) init is a tie-off with a clock pin; opt_dff deletes it.

    • An undriven input has no driver and no value: opt leaves its gate alone unless opt -undriven or setundef gives it one.

    Rule of thumb: drive every input explicitly and recheck area after every parameter change.

    How this was checked

    Every number and picture on this page comes from three Yosys 0.69+post runs recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): a tied-input plus mask design through proc, opt and abc -liberty course.lib with stat and netlists before and after; a constant flop through opt_dff; an undriven-input design through opt. course.lib is fictional, so areas are teaching units.

    • Interview: run a mock interview on this topic

    Related topics

    • Next: Redundancy removal

    • Muxes, decoders and encoders

    Previous

    17 / 139

    Next: Redundancy Removal

    Up next

    Up nextRedundancy Removal

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Factoring, Collapsing and Sharing
    • AIGs, Depth and Area
    • Don't-Cares