Conversation
The elaborator lowers latches (e.g. a latch-based clock gate) to feedback muxes. syn cuts such a loop with a loop_breaker and optimizes it as combinational logic, which does not preserve the state the loop holds. For ibex's prim_clock_gating the enable dropped out entirely, leaving AND2(clk_i, clk) -> clk; CTS then recursed around that loop until the stack overflowed. synthesize now checks for combinational loops after the full bitblast and fails with SYN-0080, naming nets on each loop (SYN-0079). Loops are found as strongly connected components of a per-pin dependency graph: a liberty cell output depends only on the inputs in its function or three_state expression, or with a combinational or tristate timing arc to it. An output with neither, such as a RAM read port, breaks the loop. Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces a combinational loop detection mechanism (checkCombinationalLoops) to the synthesis flow, which identifies and rejects designs containing combinational loops (such as inferred latches) using Tarjan's strongly connected components algorithm. Feedback on the implementation suggests fixing the net name indexing for vectors declared in descending order to prevent incorrect names in warnings, and falling back to printing net IDs for unnamed nets to improve debuggability.
Unnamed nets on a reported loop print as %<id>, which dump_fanin_cone accepts, instead of being dropped; unnamed loop-breaker aliases are skipped. Vector bit names use the same from/to mapping as resolveNetRef. Signed-off-by: Matt Liberty <mliberty@precisioninno.com>
maliberty
marked this pull request as ready for review
October 3, 2026 02:18
Member
Author
|
@povik FYI |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The elaborator lowers latches (e.g. a latch-based clock gate) to feedback muxes.
syncuts such a loop with aloop_breakerand then optimizes it as combinational logic, which does not preserve the state the loop holds. Onnangate45/ibexthe enable ofprim_clock_gatingdropped out entirely, leavingAND2(clk_i, clk) -> clk. CTS'sLatencyBalancer::computeSinkArrivalRecurthen recursed around that loop until the stack overflowed (a segfault in4_1_cts).synthesizenow checks for combinational loops after the full bitblast and fails with SYN-0080, naming nets on each loop (SYN-0079), instead of silently producing a wrong netlist.function/three_stateexpression or with a combinational/tristate timing arc to it. An output with neither (such as a fakeram read port) breaks the loop, matching how Yosys treats such cells as opaque.comb_loopregression test covers a word-level false loop, a loop through a RAM macro, a multi-output cell with independent paths (all accepted), and a loop through one pin of a partially opaque cell and a latch clock gate (both rejected).Running the check across all ORFS designs: every design that uses
synupstream (SYNTH_USE_SYN = 1) still passes. Designs containing real latches now fail at synthesis rather than producing a broken netlist: ibex (all platforms), picorv32 ((* full_case *)is not honored by the elaborator), bp_quad and mempool_group. None of these usesynupstream.