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. Flops, Tri-States and Unrolled Loops
On this page

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

    Flops, Tri-States and Unrolled Loops

    Clocked blocks infer flops, tri-states belong at pins, and loops unroll.

    Resets are priced in silicon: an async reset buys a dedicated pin on every flop, while a sync reset pays in data-path gates. Quoting the ABC summary line as the register cost misprices both, because that line excludes the flops.

    After this lesson you can: tell $sdff from $adff in a proc report, read the real chip-area totals, and place tri-states and loops correctly.

    The question

    Both registers below store eight bits on every clock edge, and both pay for reset somewhere. A synchronous reset is data-path logic in front of a plain flop; an asynchronous reset is a dedicated pin on the flop cell. In this library the chip-area totals come out equal while the sync data-path delay runs roughly twice the async one; the reset cost moves between the flop cell and the glue logic. Resets are the main lesson; the pin tri-state and the fixed-bound loop get one short recorded run each.

    Predict first An 8-bit register with sync reset and the same register with async reset go through proc. Which generic cell does each infer, and where does the reset cost sit after mapping?

    The sync register records 1 $sdff cell with no $adff, and the async register records 1 $adff cell with no $sdff. After mapping both report chip area 88 and 88: sync pays in plain DFFs plus gating, async in reset-pin DFFRs plus polarity inverters.

    Clocked blocks infer flops, with the reset in the cell family

    A clocked always block infers one flip-flop per bit, and branches inside it become data or enable logic, never latches. The reset style decides the family: tested inside a posedge-clk block it is synchronous ($sdff); in the sensitivity list beside the clock it is asynchronous ($adff). One vector cell covers all eight bits here.

    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
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    read_verilog <<EOV
    module reg_sync (
      input wire clk,
      input wire rst,
      input wire [7:0] d,
      output reg [7:0] q
    );
      always @(posedge clk) begin
        if (rst)
          q <= 8'd0;
        else
          q <= d;
      end
    endmodule
    EOV
    hierarchy -check -top reg_sync
    proc
    write_json net_sync_preopt.json
    opt
    tee -o stat_sync_generic.txt stat
    write_json net_sync_generic.json
    select -assert-count 1 t:$sdff
    select -assert-none t:$adff
    select -assert-none t:$dlatch
    design -reset
    read_verilog <<EOV
    module reg_async (
      input wire clk,
      input wire arst,
      input wire [7:0] d,
      output reg [7:0] q
    );
      always @(posedge clk or posedge arst) begin
        if (arst)
          q <= 8'd0;
        else
          q <= d;
      end
    endmodule
    EOV
    hierarchy -check -top reg_async
    proc
    tee -o stat_async_generic.txt stat
    write_json net_async_generic.json
    select -assert-count 1 t:$adff
    select -assert-none t:$sdff
    select -assert-none t:$dlatch
    design -reset
    read_verilog <<EOV
    module reg_sens (
      input wire clk,
      input wire arst,
      input wire [7:0] d,
      output reg [7:0] q
    );
      always @(posedge clk) begin
        if (arst)
          q <= 8'd0;
        else
          q <= d;
      end
    endmodule
    EOV
    hierarchy -check -top reg_sens
    proc
    opt
    write_json net_syncsens.json
    select -assert-count 1 t:$sdff
    select -assert-none t:$adff
    select -assert-none t:$dlatch
    log PASS: sync reset infers one sdff while async reset infers one adff

    8-bit register

    $sdff cells (recorded)

    $adff cells (recorded)

    Reading

    Sync reset

    1

    0

    Reset is data-path logic in front of the flop.

    Async reset

    0

    1

    Reset is a dedicated pin on the flop.

    Reset tested, clock-only sensitivity

    1

    0

    Reads as sync: no reset edge, no $adff.

    Proc alone records 1 $dff, 1 $mux, and opt folds that mux into the flop to make 1 $sdff; the async form needs no opt because proc already detects the reset edge and emits $adff directly.

    The real mapped cost of the two resets

    Generic families have no area, so the price question needs mapping. The second script maps each register through synth, dfflegalize, dfflibmap, abc with a loose -D 1000 target, and stat against course.lib. The cost comes from the recorded abc summaries and stat histograms below.

    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
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    read_verilog <<EOV
    module reg_sync (
      input wire clk,
      input wire rst,
      input wire [7:0] d,
      output reg [7:0] q
    );
      always @(posedge clk) begin
        if (rst)
          q <= 8'd0;
        else
          q <= d;
      end
    endmodule
    EOV
    read_liberty -lib course.lib
    hierarchy -check -top reg_sync
    synth -top reg_sync -flatten
    dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
    dfflibmap -liberty course.lib
    tee -o abc_sync.txt abc -liberty course.lib -constr typical.constr -D 1000
    tee -o stat_sync_map.txt stat -liberty course.lib
    write_json net_sync_map.json
    select -assert-none t:$dlatch
    design -reset
    read_verilog <<EOV
    module reg_async (
      input wire clk,
      input wire arst,
      input wire [7:0] d,
      output reg [7:0] q
    );
      always @(posedge clk or posedge arst) begin
        if (arst)
          q <= 8'd0;
        else
          q <= d;
      end
    endmodule
    EOV
    read_liberty -lib course.lib
    hierarchy -check -top reg_async
    synth -top reg_async -flatten
    dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
    dfflibmap -liberty course.lib
    tee -o abc_async.txt abc -liberty course.lib -constr typical.constr -D 1000
    tee -o stat_async_map.txt stat -liberty course.lib
    write_json net_async_map.json
    select -assert-none t:$dlatch
    design -reset
    read_verilog <<EOV
    module reg_sens (
      input wire clk,
      input wire arst,
      input wire [7:0] d,
      output reg [7:0] q
    );
      always @(posedge clk) begin
        if (arst)
          q <= 8'd0;
        else
          q <= d;
      end
    endmodule
    EOV
    read_liberty -lib course.lib
    hierarchy -check -top reg_sens
    synth -top reg_sens -flatten
    dfflegalize -cell $_DFF_P_ 01 -cell $_DFF_PN0_ 01
    dfflibmap -liberty course.lib
    tee -o abc_sens.txt abc -liberty course.lib -constr typical.constr -D 1000
    tee -o stat_sens_map.txt stat -liberty course.lib
    select -assert-none t:$dlatch
    log PASS: both reset styles map with no latches and their costs are recorded

    8-bit register

    ABC logic area (glue only)

    Chip area (stat -liberty)

    Mapped delay (abc)

    Sync reset

    24

    88

    158.07 ps

    Async reset

    8

    88

    83.95 ps

    Clock-only sensitivity (recorded)

    24

    88

    158.07 ps

    Read the chip-area column as the corrected cost model: all three rows report the same total here, so neither reset costs more silicon. The delay column tells the rest: the sync forms pay roughly twice the data-path delay of the async form. ABC's area excludes the flops: quote chip area from stat -liberty whenever the design carries flops.

    Library cell

    Sync count

    Sync area

    Async count

    Async area

    DFF

    8

    64

    0

    0

    INV

    8

    8

    8

    8

    NOR2

    8

    16

    0

    0

    DFFR

    0

    0

    8

    80

    Recorded total chip area from the stat reports: the two bars match because the reset cost moved between the flop cell and the glue logic, as the histogram above shows.
    The async-reset register as mapped: 16 cells at chip area 88 (80 in DFFRs plus 8 in INVs), with the reset-pin flops highlighted.

    The sync form maps to 24 cells with the same total: plain DFFs plus data-path gating. The async INVs are polarity: the library clear is active-low while the RTL reset is active-high.

    Tri-states live at the boundary

    An output driven to high impedance infers a pin driver. The mini-run below elaborates the pin driver through proc. Keep tri-states at pins and the interior mux-only: two on-chip drivers fighting over one net is contention in physics. The Yosys tribuf pass turns $mux cells with 'z' inputs into tri-state buffers.

    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
    read_verilog <<EOV
    module tri_pin (
      input wire en,
      input wire [7:0] d,
      output wire [7:0] y
    );
      assign y = en ? d : 8'bz;
    endmodule
    EOV
    hierarchy -check -top tri_pin
    proc
    tee -o stat_tri.txt stat
    write_json net_tri.json
    select -assert-none t:$dlatch
    select -assert-none t:$dff
    select -assert-none t:$adff
    select -assert-none t:$sdff
    design -reset
    read_verilog <<EOV
    module bus_top (
      input wire a, b,
      output wire y
    );
      drv u1 (.a(a), .y(y));
      drv u2 (.a(b), .y(y));
    endmodule
    module drv (
      input wire a,
      output wire y
    );
      assign y = a;
    endmodule
    EOV
    hierarchy -check -top bus_top
    proc
    tee -o check_multi.txt check
    write_json net_fight.json
    log PASS: boundary tri-state infers combinational cells and the bus fight is flagged

    The recorded tri_pin netlist holds 1 $mux: the high-impedance choice stays a mux input tied to high impedance, with no flop and no latch. The second half drives one net from two instances and runs check, which answers with the recorded warning:

    Diagram
    Recorded check output for the two-driver net (Yosys check pass):
    9. Executing CHECK pass (checking for obvious problems).
    Checking module drv...
    Checking module bus_top...
    Warning: multiple conflicting drivers for bus_top.\y:
        port y[0] of cell u2 (drv)
        port y[0] of cell u1 (drv)

    Generic cell in tri_pin (recorded)

    Count

    $mux

    1

    Read the warning as the tool doing its job: synthesis does not silently keep both drivers. Simulate the overlap first and it reads as unknown. The fix is structural: give the net one driver, or move the sharing to pins.

    Loops unroll, one copy per iteration

    A for loop with elaboration-fixed bounds is not a sequencer; it is shorthand for writing the body out once per iteration. A bound the tool cannot fix at elaboration cannot unroll, and fails before any cell is inferred.

    yosys
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    read_verilog <<EOV
    module loop_mux8 (
      input wire sel,
      input wire [7:0] a,
      input wire [7:0] b,
      output reg [7:0] y
    );
      integer i;
      always @* begin
        for (i = 0; i < 8; i = i + 1) begin
          y[i] = sel ? b[i] : a[i];
        end
      end
    endmodule
    EOV
    hierarchy -check -top loop_mux8
    proc
    opt
    tee -o stat_loop.txt stat
    write_json net_loop.json
    select -assert-none t:$dlatch
    select -assert-none t:$dff
    log PASS: fixed-bound loop unrolls to combinational cells with no storage

    The recorded loop netlist contains 8 $mux: select logic with no counter, no state machine, and no storage. That is the whole economics of unrolling: the loop saves typing, and the hardware pays per bit selected.

    Break it: write the wrong sensitivity for your reset

    Take the async register and delete arst from its sensitivity list while keeping the if (arst) test inside: the sens row of the first table, not the async row, because without the reset edge the tool reads a clocked test and infers 1 $sdff. The sens variant is mapped and recorded too, at chip area 88 with delay 158.07 ps: the sync row. Every report hides the mistake except the generic cell family. The recorded bus fight above is the second break-it: rerun that half and read the check warning first.

    Common mistakes

    Pricing a register from the ABC summary line. That line covers only mapped logic and excludes the flops: read the $sdff/$adff rows first, then the stat -liberty chip totals. A clocked branch with no else infers a flop with an enable path, never a latch.

    Sync reset or async reset: which costs more, and where do tri-states and loops belong?

    Weak answer: sync and async reset cost about the same, tri-states go anywhere, and loops save hardware.

    Strong answer: neither costs more silicon here: both report chip area 88 and 88. Sync infers $sdff and pays in plain flops plus gating; async infers $adff and pays in reset-pin flops. The async discount has a price: deassertion must be synchronized and checked against recovery and removal. Tri-states stay at pins, and only elaboration-fixed loops unroll.

    Follow-up: the mapped sync register is bigger than expected on a new library. Answer: read the per-type histogram for feedback muxing the library did not absorb, and check whether a dedicated sync-reset flop exists there before rewriting the RTL.

    Check yourself A clocked if without an else skips the register update on one branch. Latch or flop?

    Flop with an enable path. Clocked blocks infer storage with enables, never latches: both recorded registers show their reset family cell and record 0 latches between them, as the scripts assert.

    Check yourself The two mapped registers report different ABC areas and delays. Which reset costs what here, and why?

    Neither costs more silicon: both report chip area 88 and 88. The sync form pays in data-path gating (158.07 ps against 83.95 ps), the async form in reset-pin flops; the ABC-only gap measures excluded flops.

    • Sync reset infers $sdff; async reset infers $adff; both map without latches.

    • Equal chip area here, roughly twice the data-path delay for sync; async deassertion needs a synchronizer plus recovery and removal checks.

    • Only loops with elaboration-fixed bounds unroll, one copy per iteration.

    Rule of thumb: price the reset you wrote, not the reset you meant.

    How this was checked

    Every number on this page comes from Yosys 0.70+post runs recorded by the course checker: 8-bit sync and async registers run proc (plus opt on the sync form) for the $sdff-against-$adff proof, plus a clock-only-sensitivity variant that reads as sync; then synth with dfflegalize, dfflibmap, abc and stat -liberty, including the sensitivity variant, whose mapped cost follows the sync row. Totals come from the stat -liberty chip area, which includes flops; delays from the abc summaries. The tri-state mini-run records proc plus a two-driver bus fight answered by the recorded check warning, and the loop mini-run records proc. Mapped costs are read from the recorded abc and stat reports.

    • Interview: run a mock interview on this topic

    Related topics

    • What Is Not Synthesizable

    • Flops Resets and Enables

    • Flip-Flops and Registers

    Previous

    11 / 139

    Next: What Is Not Synthesizable

    Up next

    Up nextWhat Is Not Synthesizable

    Sign in to save progress

    Practice this lessonPrevious lessonReport an error

    See also

    • Combinational Blocks Infer Logic
    • Priority Chains versus Parallel Selects