Skip to content

Fk sc/uhdi pool#2

Open
fkhaidari wants to merge 3 commits into
fk-sc/debug-infofrom
fk-sc/uhdi-pool
Open

Fk sc/uhdi pool#2
fkhaidari wants to merge 3 commits into
fk-sc/debug-infofrom
fk-sc/uhdi-pool

Conversation

@fkhaidari
Copy link
Copy Markdown
Owner

No description provided.

@fkhaidari fkhaidari force-pushed the fk-sc/debug-info branch from d8eff39 to 75a087e Compare May 6, 2026 15:18
@fkhaidari fkhaidari force-pushed the fk-sc/uhdi-pool branch 2 times, most recently from aa1a00b to 24868be Compare May 6, 2026 16:53
fkhaidari added 3 commits May 6, 2026 19:58
Introduces a pool-based UHDI debug-info pipeline on top of the dbg
dialect.

Dialect additions:
- dbg.expression: named compound expression for source-level debug,
  used to capture compound when-guards as an op tree referenced by
  name from the statement tree.
- dbg.rootblock / dbg.subblock / dbg.connect_stmt / dbg.decl_stmt:
  region-bearing statement-tree captured before ExpandWhens so the
  source-level when/connect structure survives FIRRTL rewrites.
  StringAttr-based refs (varRef / valueRef / guardRef) decouple from
  the FIRRTL ops being rewritten.
- debug::verifyUhdiStatementRefs: optional lint helper that diagnoses
  unresolved statement-tree refs.

Passes:
- firrtl-uhdi-init (FModuleOp): stamps `uhdi.stable_id` on every
  dbg.* op so emit-time IDs are deterministic across runs.
- firrtl-uhdi-capture-when (FModuleOp): records when/connect tree
  into a dbg.rootblock before ExpandWhens. Materialises a
  dbg.expression at module body for compound when-guards. Synthesises
  per-port-per-field dbg.variable wrappers for firrtl.mem so leaf
  refs in the statement tree have matching dbg.variable ops.
- hw-uhdi-verilog-snapshot (HWModuleOp): after PrettifyVerilog,
  attaches `uhdi.repr_entry` to each stamped dbg.* op recording the
  Verilog-side signal name.

Emitter:
- circt-translate --emit-uhdi (and firtool --emit-uhdi
  --uhdi-output-file=<path>) writes a single pool-based UHDI JSON
  document with representations / types / expressions / variables /
  scopes pools.

Pipeline wiring (firtool):
- Conditional on shouldEnableDebugInfo(): UhdiInit + UhdiCaptureWhen
  before ExpandWhens; second UhdiInit after Inliner to stamp
  inlined dbg.scope ops; UhdiVerilogSnapshot before EmitUHDI.

Inliner support for the new ops:
- ModuleInliner allows debug::RootBlockOp / debug::SubBlockOp through
  inliningWalk so captured statement trees survive inlining.

Tests:
- test/Dialect/FIRRTL/uhdi-{init,capture-when}.mlir
- test/Dialect/HW/uhdi-verilog-snapshot.mlir
- test/Target/DebugInfo/emit-uhdi.mlir
- test/Analysis/verify-uhdi-refs.mlir

Signed-off-by: fkhaidari <khaidari.fg@gmail.com>
…cts + <const> placeholder

emitConnectStmt now does two things hgdb-firrtl already synthesises
on its side, so the .uhdi document carries the same breakpoint-
condition information our downstream converters need.

  * regreset target: a connect to a RegResetOp value (e.g. `r := r+1`
    inside a `when (en)` where `r := RegInit(0.U)`) gets `!<reset>`
    prepended to its enableRef.  Reset has implicit priority over
    user `when`s -- without this, our .uhdi flagged condition `en`
    while hgdb-firrtl's reference reported `!reset && en`.

  * src.empty() no longer skips the connect.  Constant-driven
    (`connect r, UInt<8>(0)`) and temp-wire-driven
    (`connect r, tail(...)`) connects had no source-level dbg.variable
    for `src`; the old code dropped the whole statement, leaving a
    gap in the body's assignment table.  We now synthesise the
    `<const>` placeholder so each connect still appears.

Verified against bench/Counter on the uhdi/bench harness: the
hgdb_firrtl pipeline's bp condition `!reset && en` now matches the
native reference verbatim (was: `en`).
Bundle-field operands of a `dbg.struct` post-LowerTypes were emitting
empty sigNames, so tywaves rendered every bundle field as 0 even when
the underlying flat signal carried real data.

Two issues:

1. LowerIntrinsics wraps each circt_debug_subfield ref in a
   `dbg.subfield` op (typeName/params/enumDef metadata). After
   LowerTypes those wrappers stay in the IR, so the SubFieldOp result
   (type !dbg.subfield) reaches `sv::resolveVerilogName`, which doesn't
   know about it and returns null.

2. LowerTypes flattens a bundle port into individual ports plus
   per-field alias wires; PrettifyVerilogNames renames the wire
   identifier (e.g. `io_a` -> `io_a_0`) to dodge the port-name
   collision. `sv::resolveVerilogName` returns the renamed wire, which
   isn't the canonical signal a VCD-bound consumer expects (and
   verilator's --trace may omit the alias wire entirely).

Add `unwrapDbgSubField` to see through (1) and `resolvePortAliasName`
to recognise the alias-wire pattern (both pre- and post-ExportVerilog
shapes) for (2). Wire them through TypePool::internValueType /
internStruct, ExpressionPool::operandFor, emitVariable's leafName,
indexStructFields, and the dbg.expression collector.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant