Integration

VCS, Questa, Xcelium: Coverage Database Export Differences That Matter

Hiroshi Watanabe · 7 min read
Abstract connector diagram representing simulator compatibility

UCDB (Unified Coverage Database) exists to give the EDA ecosystem a common coverage data format. In practice, every major simulator implements UCDB with enough vendor-specific extensions and edge-case interpretations that parsing one simulator's output in another's tooling regularly produces corrupted or incomplete coverage views. When we built Photoniq's ingestion layer, we had to reverse-engineer those differences from actual exported files. This post documents the ones that caused us real problems.

A quick scope note: we're comparing VCS (Synopsys), Questa (Mentor/Siemens), and Xcelium (Cadence) specifically in their UCDB coverage export behavior. We're not evaluating simulation speed, language support, or any other dimension. And we're writing from the perspective of a tool that ingests coverage data — not as users running simulations.

The UCDB Standard and Its Gaps

The UCDB specification defines a hierarchical database schema with scopes, covergroups, coverpoints, cross-coverage matrices, and various count structures. It's comprehensive for the cases it covers explicitly. The problems emerge in:

  • How simulators handle bins defined with wildcard expressions when bin counts don't align
  • Cross-coverage bin naming when one dimension uses ignore_bins
  • FSM state encoding — whether extracted FSM states map to one coverpoint or are inlined differently
  • Hierarchical path encoding for module instances inside generate blocks
  • Toggle coverage resolution for multi-driver nets

None of these are bugs in the simulators. They're underspecified areas where vendors made different defensible choices. The problem is that if you're merging UCDB files from a mixed-simulator environment — which is more common than you'd expect at teams using IP from multiple sources — the merged view can be meaningless.

VCS: Coverage Export Specifics

VCS exports UCDB via urg (Unified Report Generator) or directly from simulation with -cm_dir flags. The exported UCDB is generally well-formed and has the most consistent bin naming of the three simulators in our experience.

The area that trips up external parsers most often is VCS's handling of arrayed module instances. When you have a generate-for loop creating N instances of a module, VCS encodes the instance scope with a numeric suffix that includes the loop variable: top.dut.blk[3].fsm. This is consistent and parseable, but the bracket notation conflicts with several coverage report merging tools that use brackets as their own scope separator syntax. Our UCDB parser had to explicitly detect and escape this pattern on ingestion.

VCS also encodes per-test coverage metadata (which test seed produced which hits) more verbosely than Questa or Xcelium. For large regression runs with thousands of seeds, VCS UCDB files are substantially larger. This is useful data — it's how you identify which seeds actually contributed coverage — but it makes ingestion of multi-seed merged databases expensive at scale. We load this data lazily rather than parsing it all at ingestion time.

Questa: FSM Extraction and Cross-Coverage Naming

Questa's FSM extraction is genuinely better than VCS in one specific way: it identifies FSM state encodings from RTL analysis and maps them to symbolic state names rather than numeric bin indices. So instead of bins labeled state[3] and state[7], Questa exports bins labeled IDLE and TX_ACTIVE. This is much more useful when you're trying to understand which FSM states are uncovered.

The complication: symbolic state name extraction only fires when Questa's FSM detection algorithm identifies the state register. It doesn't fire for all FSMs — notably, it tends to miss FSMs implemented with one-hot encoding and some parameterized state machines. When detection fails, Questa falls back to numeric encoding, but the fallback encoding is in a different bin namespace from the detected ones. If you merge a Questa run where FSM detection fired with one where it didn't, the FSM coverage bins look like completely different coverpoints. This tripped us up badly during early corpus building.

Questa's cross-coverage bin naming convention for ignored-bins edges is also non-standard. When a cross coverpoint has an ignore_bins clause on one dimension, Questa generates a bin entry in the UCDB with a vendor-specific marker that is not part of the UCDB specification. Parsing tools that don't know to filter this marker will count those entries as uncovered bins when they should be excluded from coverage calculation entirely.

Xcelium: Scope Path Encoding and Merge Behavior

Xcelium's scope path encoding uses dot-separated hierarchical identifiers that closely match the SystemVerilog scope resolution operator, which makes it the easiest to correlate back to RTL source line numbers. For our coverage-to-RTL mapping feature — where we show which source lines correspond to which coverage bins — Xcelium-generated UCDB files required the least normalization.

The area that causes problems is Xcelium's coverage database merge behavior. Xcelium's native merge tool (imc -merge) applies a deduplication pass that collapses bin entries with identical hit counts across seeds. This is a lossy operation from a test attribution standpoint — after merge, you can't determine which seed hit which bin. For most coverage closure workflows this doesn't matter, but for Photoniq's test-attribution analysis (which seeds contributed what), it means we need users to provide per-seed databases rather than pre-merged ones when they want the attribution layer.

Xcelium also has the most aggressive pruning of zero-count bins from the exported UCDB. VCS and Questa retain zero-count bins explicitly in the export; Xcelium prunes them by default unless you pass -keepall. Zero-count bins are actually the interesting ones for coverage gap analysis — they're the open bins you want to close. Parsing an Xcelium export without knowing about this pruning behavior leads to inflated coverage numbers, because the unpruned bins aren't present to be counted as uncovered. Our documentation now calls this out explicitly in the Xcelium integration guide.

The Mixed-Simulator Environment Problem

IP reuse is common enough that teams frequently run blocks through different simulators — a cryptographic IP block validated with Questa's formal extensions, the interconnect fabric tested with VCS due to license availability, and the accelerator datapath on Xcelium. All three feed into a coverage sign-off target that someone needs to merge and analyze.

We're not saying you should standardize on a single simulator to avoid this problem — that's often impractical given IP vendor requirements and license costs. But the normalization layer matters. Before any coverage analysis, Photoniq runs what we call a schema normalization pass that:

  • Resolves FSM bin naming to numeric indices across all simulators (symbolic names are preserved as metadata but not used for merge identity)
  • Reconstructs zero-count bins from RTL analysis when Xcelium pruning is detected
  • Normalizes scope path separators to a canonical form
  • Strips Questa's non-standard ignore_bins markers before bin counting

This normalization adds about 8 seconds to ingestion for a 200MB merged UCDB. It's not optional for mixed-simulator inputs.

What This Means in Practice

If you're running a homogeneous simulator environment — all VCS, all Questa, or all Xcelium — many of these issues won't surface. The merge tools from each vendor are well-calibrated to their own output format. The divergences show up when you cross simulator boundaries, use coverage data in external tools (including ours), or when you upgrade simulator versions, which sometimes changes FSM detection behavior or bin naming schemes.

The practical recommendation is straightforward: when integrating any external coverage analysis tool, run a validation pass against a design you already know the expected coverage numbers for, using your specific simulator and version. Don't assume the tool handles your simulator's UCDB output correctly until you've verified it against known-good ground truth.

For Photoniq specifically, we maintain per-simulator test suites using reference RTL designs with known coverage distributions. When a simulator releases a new major version, we run those suites before qualifying the new version for production ingestion. It's not a glamorous part of building a coverage tool, but it's the work that prevents a user from filing a support ticket saying "your tool says I'm at 74% coverage but my simulator reports 81%."