Skip to content

[ImportVerilog] Support sequential user-defined primitives (UDPs) in circt::ImportVerilog (edge-sensitive and level-sensitive). - #11289

Open
qiaozhang-pr wants to merge 1 commit into
llvm:mainfrom
qiaozhang-pr:piper_export_cl_995960821
Open

qiaozhang-pr wants to merge 1 commit into
llvm:mainfrom
qiaozhang-pr:piper_export_cl_995960821

Conversation

@qiaozhang-pr

@qiaozhang-pr qiaozhang-pr commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

This change includes the following:

  • Support sequential user-defined primitives (UDPs) in circt::ImportVerilog for both edge-sensitive (flip-flops, clock enables, dual-edge) and level-sensitive (asynchronous reset/preset, latches) models.
  • In classifySequentialTableRows, filter non-functional entries (4-state simulation artifacts, explicit '-' holds, and idempotent state == output holds) and enforce 5 structural hardware requirements on surviving transitions:
    1. Well-formed edge transitions:
      a. At most one edge transition per table row.
      b. Supported transition specifiers: each edge transition must specify a recognized 2-state edge token.
    2. Single edge-sensitive port: all active edge transitions must share the same edge-sensitive clock port, diagnosing unsupported multi-clock UDPs.
    3. No mixed sensitivity on edge-sensitive port: in edge-triggered models, active level-sensitive rows must specify '?' on the edge-sensitive clock pin, disallowing unsupported mixed latch/flip-flop models.
    4. Pure asynchronous control: asynchronous clear/preset rows must not depend on internal state q, disallowing feedback loops in asynchronous control.
    5. Separation of asynchronous control and synchronous data:
      a. Asynchronous control pins cannot also act as varying synchronous data inputs (only dedicated clear/preset lines are supported).
      b. Synchronous transitions cannot be conditioned on active asynchronous clear/preset levels.
  • Correctly classify clock enables: ports that contain wildcards / don't-cares ('?') in any synchronous row are not misclassified as common clock enables (syncEn), but are instead preserved in the next-state logic (computeSyncNextState).
  • In buildAsyncConditions, synthesize composite boolean conditions for clear and preset (clearCond, presetCond). Evaluate wildcards symmetrically across clear and preset rows to determine relative dominance (presetDominates).
  • For clocked sequential primitives with dual clear and preset, lower clear and preset as direct asynchronous reset on the register via asyncCond = clearCond | presetCond, asyncVal = presetCond. This allows downstream synthesis and Deseq to infer a single seq.firreg ... reset async (clear | preset), preset, ensuring the register itself is asynchronously updated even when clocks are gated. For single asynchronous reset or preset, emit true asynchronous reset with constant value.
  • For dual-edge triggered primitives (BothEdges), emit two independent single-edge procedures (q_pos on posedge and q_neg on negedge) driving separate state registers combined by a clock multiplexer (q = (clk & q_pos) | (~clk & q_neg)). This avoids multiple processes driving the same signal in LLHD, enabling downstream Deseq to extract two standard single-edge seq.firreg ops and allowing CIRCT clock inference and downstream simulation tools to execute dual-edge UDPs natively.
  • In emitSequentialLevelSensitiveProcedure, emit moore.procedure always_latch for purely level-sensitive sequential UDPs (latches).
  • Unify instantiation of combinational and sequential UDPs via moore.instance.
  • Diagnostics:
    • Add diagnostic error tests in errors.sv covering multi-clock UDPs (udp_multi_clock), steady-clock mixed latch/flip-flop transitions (udp_mixed_latch), and state-dependent asynchronous control (udp_async_state_dep).
  • Tests:
    • In udp.sv: Test posedge, negedge, clock-enable, benign hold rows, dual-edge flip-flops, dual-edge muxes, asymmetric condition UDPs (udp_dff_asym_cond), async reset (udp_dff_async_rst), async set/reset (udp_dff_async_sr), and generic active-low clear/preset (udp_dff_async_clear_preset).
    • In primitives.sv: Expand sequential primitive suite with udp_seq_dff, udp_seq_dff_neg, udp_seq_dff_en, udp_seq_dff_hold, udp_seq_dual_edge_dff, udp_seq_dual_edge_mux, udp_seq_dff_rst, udp_seq_dff_sr, asymmetric condition UDPs (udp_seq_asym_cond), and generic udp_seq_dff_clear_preset along with instantiation test module udp_seq_prim.

Note: This PR is follow-up of #11238.

Assisted-by: Antigravity:GeminiFlash

@qiaozhang-pr

Copy link
Copy Markdown
Contributor Author

cc: @jpienaar @jmolloy PTAL, thanks!

@circt-bot

circt-bot Bot commented Oct 14, 2026

Copy link
Copy Markdown

Results of circt-tests run for bc98646 compared to results for 4f82d6f:

sv-tests

Changes in emitted diagnostics:

  • -15 total change
  • -23 error: unsupported construct: Primitive
  • +2 error: failed to legalize operation 'moore.fork'
  • +2 error: sequential UDP `latch` without clock edge is not supported
  • +1 error: 'moore.instance' op has 3 operands, but target module @mux has 4 inputs
  • +1 error: sequential UDP `edet` without clock edge is not supported
  • +1 error: sequential UDP `p` without clock edge is not supported
  • +1 error: unsupported module member: SpecifyBlock

@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch 2 times, most recently from c9107d3 to 589fe72 Compare October 14, 2026 20:59
@circt-bot

circt-bot Bot commented Oct 15, 2026

Copy link
Copy Markdown

Results of circt-tests run for 589fe72 compared to results for fec4477:

sv-tests

Changes in emitted diagnostics:

  • -5 total change
  • -10 error: sequential user-defined primitives are not supported yet
  • +2 error: sequential UDP `latch` without clock edge is not supported
  • +1 error: sequential UDP `edet` without clock edge is not supported
  • +1 error: sequential UDP `p` without clock edge is not supported
  • +1 error: unsupported module member: SpecifyBlock

@qiaozhang-pr

Copy link
Copy Markdown
Contributor Author

Results of circt-tests run for 589fe72 compared to results for fec4477:

sv-tests

Changes in emitted diagnostics:

  • -5 total change
  • -10 error: sequential user-defined primitives are not supported yet
  • +2 error: sequential UDP `latch` without clock edge is not supported

async-row support to come in follow-up change.

  • +1 error: sequential UDP `edet` without clock edge is not supported

Expected behavior: Not supporting not 2-state edge.

  • +1 error: sequential UDP `p` without clock edge is not supported

async-row support to come in follow-up change.

  • +1 error: unsupported module member: SpecifyBlock

Unrelated after UDP successful conversion.

@qiaozhang-pr

Copy link
Copy Markdown
Contributor Author

cc @fabianschuiki @TaoBi22

@qiaozhang-pr qiaozhang-pr changed the title [Stacked on #11238] Support sequential user-defined primitives (UDPs) in circt::ImportVerilog (edge-sensitive/sync-row only). [ImportVerilog] Support sequential user-defined primitives (UDPs) in circt::ImportVerilog (edge-sensitive/sync-row only). Oct 19, 2026
@jmolloy
jmolloy requested review from TaoBi22 and fabianschuiki and removed request for TaoBi22 October 20, 2026 15:04
@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch from 589fe72 to f7e99fa Compare October 22, 2026 03:18
@qiaozhang-pr

Copy link
Copy Markdown
Contributor Author

Besides the comment, I also made 3 changes: 1. add skipUnsupportedRow back for minimal change, 2. added back some change accidentally reverted 3. re-ordered test cases in .sv for better readability and consistency (some check rules also updated).

@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch from f7e99fa to 632c3cc Compare October 22, 2026 03:23
@qiaozhang-pr
qiaozhang-pr requested a review from jpienaar October 22, 2026 03:29
@circt-bot

circt-bot Bot commented Oct 22, 2026

Copy link
Copy Markdown

Results of circt-tests run for 632c3cc compared to results for fec4477:

sv-tests

Changes in emitted diagnostics:

  • -3 total change
  • -10 error: sequential user-defined primitives are not supported yet
  • +2 error: sequential UDP `latch` without clock edge is not supported
  • +1 error: sequential UDP `edet` without clock edge is not supported
  • +1 error: sequential UDP `p` without clock edge is not supported
  • +1 error: unknown name `EXP`
  • +1 error: unsupported module member: SpecifyBlock
  • +1 error: unsupported system call `$countbits`

@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch 2 times, most recently from e6ab9a3 to 59d8d84 Compare October 30, 2026 03:06
@TaoBi22 TaoBi22 added the run-circt-tests Enables automatic circt-test result reporting on labelled PRs (WIP) label Oct 30, 2026
@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch from 59d8d84 to 0d5c5c9 Compare October 30, 2026 22:27
@qiaozhang-pr qiaozhang-pr changed the title [ImportVerilog] Support sequential user-defined primitives (UDPs) in circt::ImportVerilog (edge-sensitive/sync-row only). [ImportVerilog] Support sequential user-defined primitives (UDPs) in circt::ImportVerilog (edge-sensitive and level-sensitive). Oct 30, 2026
@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch 2 times, most recently from 37bd594 to 1531728 Compare October 30, 2026 23:01
@circt-bot

circt-bot Bot commented Oct 30, 2026

Copy link
Copy Markdown

Results of circt-tests run for 1531728 compared to results for 2d74cea:

sv-tests

Changes in emitted diagnostics:

  • -7 total change
  • -10 error: sequential user-defined primitives are not supported yet
  • +1 error: sequential UDP `edet` without clock edge is not supported
  • +1 error: sequential UDP `jkff` defines synchronous transitions conditioned on active asynchronous reset/set
  • +1 error: unsupported module member: SpecifyBlock

@qiaozhang-pr

Copy link
Copy Markdown
Contributor Author

@jpienaar @TaoBi22 @fabianschuiki Dear reviewers, would you have time for another review? Thanks in advance.

@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch from 1531728 to 3045efa Compare November 5, 2026 05:53
…circt::ImportVerilog (edge-sensitive and level-sensitive).

* Support sequential user-defined primitives (UDPs) in `circt::ImportVerilog` for both edge-sensitive (flip-flops, clock enables, dual-edge) and level-sensitive (asynchronous reset/preset, latches) models.
* In `classifySequentialTableRows`, filter non-functional entries (4-state simulation artifacts, explicit '-' holds, and idempotent state == output holds) and enforce 5 structural hardware requirements on surviving transitions:
  1. Well-formed edge transitions:
     a. At most one edge transition per table row.
     b. Supported transition specifiers: each edge transition must specify a recognized 2-state edge token.
  2. Single edge-sensitive port: all active edge transitions must share the same edge-sensitive clock port, diagnosing unsupported multi-clock UDPs.
  3. No mixed sensitivity on edge-sensitive port: in edge-triggered models, active level-sensitive rows must specify '?' on the edge-sensitive clock pin, disallowing unsupported mixed latch/flip-flop models.
  4. Pure asynchronous control: asynchronous clear/preset rows must not depend on internal state `q`, disallowing feedback loops in asynchronous control.
  5. Separation of asynchronous control and synchronous data:
     a. Asynchronous control pins cannot also act as varying synchronous data inputs (only dedicated clear/preset lines are supported).
     b. Synchronous transitions cannot be conditioned on active asynchronous clear/preset levels.
* Correctly classify clock enables: ports that contain wildcards / don't-cares ('?') in any synchronous row are not misclassified as common clock enables (`syncEn`), but are instead preserved in the next-state logic (`computeSyncNextState`).
* In `buildAsyncConditions`, synthesize composite boolean conditions for clear and preset (`clearCond`, `presetCond`). Evaluate wildcards symmetrically across clear and preset rows to determine relative dominance (`presetDominates`).
* For clocked sequential primitives with dual clear and preset, lower clear and preset as direct asynchronous reset on the register via `asyncCond = clearCond | presetCond`, `asyncVal = presetCond`. This allows downstream synthesis and Deseq to infer a single `seq.firreg ... reset async (clear | preset), preset`, ensuring the register itself is asynchronously updated even when clocks are gated. For single asynchronous reset or preset, emit true asynchronous reset with constant value.
* For dual-edge triggered primitives (`BothEdges`), emit two independent single-edge procedures (`q_pos` on posedge and `q_neg` on negedge) driving separate state registers combined by a clock multiplexer (`q = (clk & q_pos) | (~clk & q_neg)`). This avoids multiple processes driving the same signal in LLHD, enabling downstream Deseq to extract two standard single-edge `seq.firreg` ops and allowing CIRCT clock inference and downstream simulation tools to execute dual-edge UDPs natively.
* In `emitSequentialLevelSensitiveProcedure`, emit `moore.procedure always_latch` for purely level-sensitive sequential UDPs (latches).
* Unify instantiation of combinational and sequential UDPs via `moore.instance`.
* Diagnostics:
  * Add diagnostic error tests in `errors.sv` covering multi-clock UDPs (`udp_multi_clock`), steady-clock mixed latch/flip-flop transitions (`udp_mixed_latch`), and state-dependent asynchronous control (`udp_async_state_dep`).
* Tests:
  * In `udp.sv`: Test posedge, negedge, clock-enable, benign hold rows, dual-edge flip-flops, dual-edge muxes, asymmetric condition UDPs (`udp_dff_asym_cond`), async reset (`udp_dff_async_rst`), async set/reset (`udp_dff_async_sr`), and generic active-low clear/preset (`udp_dff_async_clear_preset`).
  * In `primitives.sv`: Expand sequential primitive suite with `udp_seq_dff`, `udp_seq_dff_neg`, `udp_seq_dff_en`, `udp_seq_dff_hold`, `udp_seq_dual_edge_dff`, `udp_seq_dual_edge_mux`, `udp_seq_dff_rst`, `udp_seq_dff_sr`, asymmetric condition UDPs (`udp_seq_asym_cond`), and generic `udp_seq_dff_clear_preset` along with instantiation test module `udp_seq_prim`.
@qiaozhang-pr
qiaozhang-pr force-pushed the piper_export_cl_995960821 branch from 3045efa to 6f95676 Compare November 5, 2026 05:57
@circt-bot

circt-bot Bot commented Nov 5, 2026

Copy link
Copy Markdown

Results of circt-tests run for 6f95676 compared to results for 2d74cea:

sv-tests

Changes in emitted diagnostics:

  • -3 total change
  • -10 error: sequential user-defined primitives are not supported yet
  • +3 error: unsupported module member: SpecifyBlock
  • +1 error: sequential UDP `edet` without clock edge is not supported
  • +1 error: sequential UDP `jkff` defines synchronous transitions conditioned on active asynchronous clear/preset
  • +1 error: unsupported format specifier `%u`
  • +1 error: unsupported format specifier `%z`

@TaoBi22

TaoBi22 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

@jpienaar @TaoBi22 @fabianschuiki Dear reviewers, would you have time for another review? Thanks in advance.

@qiaozhang-pr it looks like this PR is adding a few things at once now and the diff has become quite big to review 😅 do you think it would be possible to split this up any further? (or trim it down at all - I'm a little surprised we need so much churn for this coverage but I guess there are a lot of corner cases?)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-circt-tests Enables automatic circt-test result reporting on labelled PRs (WIP)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants