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
  • Constraints and Timing15
  • Sequential, FSM and Memory19
    • Flops, Resets and Enables
      • D Flop Inference
      • Enables: Mux or Clock Enable
      • Synchronous and Asynchronous Reset
      • Reset-less Flops and Initial Values
      • Mapping Flops to Library Cells
    • FSM Synthesis
    • Memory and Macro Inference
    • Retiming and Pipelining
  • Datapath Synthesis14
  • Area, Power and Test14
  • Verification, ECO and Handoff15
  • Flows and Debugging15
  • Interview and Judgment11
Loading lesson

←Flops, Resets and Enables
  1. Topics
  2. Logic Synthesis
  3. Sequential, FSM and Memory
  4. Flops, Resets and Enables
  5. D Flop Inference
On this page

    Part 4 of 9, lesson 1 of 19Part 4 of 9/Sequential, FSM and Memory/Lesson 1 of 19Intermediate

    D Flop Inference

    How a plain clocked block becomes one $dff cell per vector, then one DFF per bit.

    A plain clocked block is the smallest sequential statement, and its cost scales one flop per bit. Seeing that scaling recorded settles what storage costs before resets and enables complicate it.

    After this lesson you can: infer $dff from a plain block, predict mapped area from width, and explain why a lone blocking assignment matches while a chained one collapses.

    The question

    A plain clocked block is the smallest sequential statement: on every rising edge, copy the input to the output. The question is what the tool builds from it, how that structure grows with width, and which spellings change the answer. This lesson infers a 12-bit plain register, maps it and a 24-bit twin to the teaching library, and breaks a chained blocking assignment. Every count and area below is read back from the recorded files the scripts saved.

    The plain D flop idiom

    A block clocked on one edge with no enable and no reset infers D flops: one bit of storage per bit of the register, updating on every edge. An if without an else still infers a flop here, because holding the old value is what a flop does. Combinational blocks with a skipped branch infer a latch instead; a clocked block never does, which is the first thing the assertions below pin down.

    What Yosys builds

    The first script elaborates a 12-bit plain register and pins its family: exactly one $dff cell, with no enabled, sync-reset, or async-reset variant beside it. Pre-mapping vectors stay together, so the 12 bits share one $dff cell carrying a width of 12. The stat report and the JSON netlist are saved so the table and the figure can read them back.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    read_verilog <<EOV
    module plain12(input clk, input [11:0] d, output reg [11:0] q);
    always @(posedge clk) q <= d;
    endmodule
    EOV
    hierarchy -check -top plain12
    proc
    opt
    select -assert-count 1 t:$dff
    select -assert-none t:$dffe
    select -assert-none t:$adff
    select -assert-none t:$sdff
    tee -o stat_plain12_generic.txt stat
    write_json net_plain12_generic.json
    log PASS: 12-bit plain register infers one $dff cell
    Predict first The 12-bit register infers one generic cell, and mapping turns each bit into its own library flop. What will the two mapped chip areas be, and how will the 24-bit twin compare?

    One flop per bit at 8 units each: the 12-bit register reports chip area 96, and the 24-bit twin reports 192, exactly double. Width scales the area linearly because every extra bit is one more identical DFF.

    Register

    Generic cells (recorded)

    Reading

    12-bit plain

    1 $dff

    One $dff vector cell pre-mapping; the bits split later.

    Width and chip area

    Generic cells have no area, so the price question needs mapping. The second script maps the 12-bit register and a 24-bit twin through synth, dfflegalize, dfflibmap against course.lib, then abc with -constr typical.constr and a loose -D 1000 target plus stat against course.lib. Each run asserts its DFF count: 12 DFFs for the narrow register and 24 DFFs for the wide one. With nothing but flops and no combinational logic, ABC has nothing to map and says so in its report; the price comes from the stat chip total, which includes the sequential cells. That split is worth remembering: the ABC summary line covers only mapped logic, so for any register the stat total is the number to quote.

    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
    read_verilog <<EOV
    module plain12(input clk, input [11:0] d, output reg [11:0] q);
    always @(posedge clk) q <= d;
    endmodule
    EOV
    read_liberty -lib course.lib
    hierarchy -check -top plain12
    synth -top plain12 -flatten
    dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
    dfflibmap -liberty course.lib
    tee -o abc_plain12.txt abc -liberty course.lib -constr typical.constr -D 1000
    tee -o stat_plain12.txt stat -liberty course.lib
    write_json net_plain12.json
    select -assert-count 12 t:DFF
    select -assert-none t:DFFR
    design -reset
    read_verilog <<EOV
    module wide24(input clk, input [23:0] d, output reg [23:0] q);
    always @(posedge clk) q <= d;
    endmodule
    EOV
    read_liberty -lib course.lib
    hierarchy -check -top wide24
    synth -top wide24 -flatten
    dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
    dfflibmap -liberty course.lib
    tee -o abc_wide24.txt abc -liberty course.lib -constr typical.constr -D 1000
    tee -o stat_wide24.txt stat -liberty course.lib
    write_json net_wide24.json
    select -assert-count 24 t:DFF
    log PASS: plain registers map to one DFF per bit at 12 and 24 bits

    Register

    Mapped library cells (recorded)

    Chip area (stat -liberty)

    12-bit plain

    12 DFF

    96

    24-bit plain

    24 DFF

    192

    Read the chip-area column as the per-bit price of storage in this library: 96 for 12 bits and 192 for 24 bits, which is 8 units per bit in both rows. Doubling the width doubles the area because every extra bit is one more identical DFF; there is no sharing, no control overhead, and no surprise in a plain register.

    Recorded total chip area from the stat reports for the 12-bit register and its 24-bit twin. The second bar is exactly twice the first.
    The recorded mapped 12-bit register: 12 DFF cells, one per bit, with no glue logic.

    Break it: chain two loads with blocking assignments

    The third script respells the same 12-bit register with a blocking assignment, q = d, and asserts the identical family: one $dff cell and no latch. The recorded netlist reads 1 $dff, the same single cell as the nonblocking form. For one assignment the synthesis claim stops there: the tool sees one edge-triggered copy either way. Chaining is where the spellings diverge, and the fourth script records it: begin a = d; b = a; end in one clocked block builds two $dff cells at proc, then opt merges them into 1 $dff, because both sample d on the same edge and no shift stage survives. The testbench below pins the behaviour: beside a nonblocking shift register fed the same data, the blocking chain's second output is already correct after one edge while the true shift needs two.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    read_verilog <<EOV
    module plain12b(input clk, input [11:0] d, output reg [11:0] q);
    always @(posedge clk) q = d;
    endmodule
    EOV
    hierarchy -check -top plain12b
    proc
    opt
    select -assert-count 1 t:$dff
    select -assert-none t:$dlatch
    tee -o stat_plain12b_generic.txt stat
    write_json net_plain12b_generic.json
    log PASS: blocking assignment in a clocked block infers the same $dff
    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    read_verilog <<EOV
    module chain1b(input clk, input d, output reg a, output reg b);
    always @(posedge clk) begin a = d; b = a; end
    endmodule
    EOV
    hierarchy -check -top chain1b
    proc
    select -assert-count 2 t:$dff
    select -clear
    opt
    select -assert-count 1 t:$dff
    tee -o stat_chain1b_generic.txt stat
    write_json net_chain1b_generic.json
    log PASS: chained blocking assignments collapse to one flop
    design.sv
    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
    // engine: icarus
    module chain1b_tb(input logic clk, input logic d, output logic a, output logic b);
      always @(posedge clk) begin a = d; b = a; end
    endmodule
    module chain1n_tb(input logic clk, input logic d, output logic a, output logic b);
      always @(posedge clk) begin a <= d; b <= a; end
    endmodule
    module tb_chain;
      logic clk = 0, d = 0, ab, bb, an, bn;
      chain1b_tb blk(.clk(clk), .d(d), .a(ab), .b(bb));
      chain1n_tb nblk(.clk(clk), .d(d), .a(an), .b(bn));
      always #5 clk = ~clk;
      int errors = 0;
      initial begin
        d = 0;
        @(posedge clk); #1;
        d = 1;
        @(posedge clk); #1;
        if (bb !== 1'b1) begin $display("FAIL: blocking chain did not collapse"); errors++; end
        if (bn !== 1'b0) begin $display("FAIL: reference shift misbehaving"); errors++; end
        @(posedge clk); #1;
        d = 0;
        @(posedge clk); #1;
        if (bn !== 1'b1) begin $display("FAIL: nonblocking chain did not shift"); errors++; end
        if (bb !== 1'b0) begin $display("FAIL: blocking chain held stale data"); errors++; end
        if (errors == 0) $display("PASS: chained blocking collapses the shift stage");
        $finish;
      end
    endmodule

    Common mistakes

    Reading the flop count off the generic netlist as the library cost. One $dff cell is one vector before mapping and 12 library flops after; only the mapped histogram tells you what the chip pays.

    Pricing a register from the ABC summary line. With flop-only logic ABC maps nothing and its line carries no flop area, so a register that costs 96 units in stat can show an empty ABC report. Quote the stat chip total whenever the design carries flops.

    Likely probes: what $dff means, and how the count changes through the flow.

    Weak answer: the 12-bit register infers 12 flop cells and costs whatever ABC prints.

    Strong answer: it infers one $dff vector cell, recorded as 1 $dff, which maps to 12 DFFs at chip area 96; the 24-bit twin doubles both to 24 cells and 192. ABC prints no area here because there is no combinational logic to map.

    Follow-up: the mapped count is 11 DFFs instead of 12. Answer: one bit was optimized away, usually a constant input or an undriven bit; check the loader before blaming the mapper.

    Check yourself The script asserts one $dff for a 12-bit register. Why one cell and not twelve?

    Pre-mapping vectors stay together in one cell with a width parameter. The twelve library flops appear only after mapping, as the recorded DFF counts confirm.

    Check yourself The 24-bit register reports double the 12-bit area. What does that doubling prove, and what would break it?

    It proves one flop per bit with no sharing or control overhead at this width. A bit tied to a constant, an undriven bit, or an enable folded in would break the exact doubling, and the mapped histogram would show it.

    • One $dff vector cell pre-mapping becomes one DFF per bit after mapping.

    • Plain-register area scales linearly: 8 library units per bit here.

    • A lone blocking assignment matches its nonblocking twin; a chained one collapses the shift.

    Rule of thumb: count vectors before mapping and library flops after, and never mix the two.

    How this was checked

    Every number and picture on this page comes from four Yosys 0.70 runs recorded by the course checker (scripts/tools/run-rtl-testbenches.mjs with COURSE=synth): a 12-bit plain register through proc and opt with family assertions, the 12-bit and 24-bit twins through synth, dfflegalize, dfflibmap -liberty course.lib, abc -liberty course.lib -constr typical.constr -D 1000 and stat -liberty course.lib, a single-assignment blocking respelling, and a chained-blocking collapse that builds two $dff at proc and merges them at opt. An Icarus sim proves the collapse against a nonblocking shift. The library is fictional, so absolute areas are teaching units; the linear scaling is the point.

    • Interview: run a mock interview on this topic

    Related topics

    • Enables: Mux or Clock Enable

    • Foundations: Flops, Tri-States and Unrolled Loops

    • RTL: Flip-Flops and Registers

    Previous

    52 / 139

    Next: Enables: Mux or Clock Enable

    Up next

    Up nextEnables: Mux or Clock Enable

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Synchronous and Asynchronous Reset
    • Reset-less Flops and Initial Values
    • Mapping Flops to Library Cells