Skip to content

bug: Synthesis LEC fails with unproven $equiv cells on equivalent netlists (v0.1.0-alpha.12) #273

Description

@Emin017

Self checks

  • I have searched existing issues, including closed issues.
  • I have read the documentation and development guide.
  • This is a bug report, not a question, feature request, or usage discussion.
  • I will include logs, command output, or reproducer files where they help diagnose the issue.
  • I have removed secrets, private paths, tokens, and other sensitive information from the report.

ECC version

v0.1.0-alpha.12 (ecc --version reports ecc 0.1.0a12)

Affected area

Flow/runtime, EDA integration

How did you get ECC?

Release PyInstaller bundle

Where are you running ECC?

Native Linux

Environment

Any Linux x86_64 (reproduced with the bundled Yosys 0.62)

Toolchain, PDK, and dependency context

PDK: any (reproduced with icsprout55)
ECC-Tools: bundled
Yosys/OSS CAD Suite: bundled Yosys 0.62
Project/workspace: pm32 example (rtl/pm32.v + rtl/spm.v), default rtl2gds flow

Steps to reproduce

  1. Install ECC v0.1.0-alpha.12 from the release PyInstaller bundle.
  2. Run the default flow on a design with internal sequential state, e.g. the pm32 example:
    ecc run --project pm32 --workspace default
  3. Synthesis succeeds, then the lec (yosys_lec) step fails.

Expected behavior

The synthesis LEC step proves the pre-mapping golden netlist equivalent to the mapped gate netlist and the flow continues to floorplan.

Actual behavior

lec (yosys_lec) reports every primary output as unproven and the flow stops:

✗ lec (yosys_lec) incomplete 0:00:02
error: lec_yosys_lec/log/lec.log
  Unproven $equiv ...: \p_31_gold \p_31_gate
  ...
  Found a total of 65 unproven $equiv cells.

Logs or command output

24. Executing EQUIV_SIMPLE pass.
Found 65 unproven $equiv cells (65 groups) in equiv:
  Trying to prove $equiv for \p_63: failed.
  ...
25. Executing EQUIV_INDUCT pass.
Found a total of 65 unproven $equiv cells.

Root cause (confirmed)

Not a real functional mismatch — a proof-methodology bug in the bundled run_lec.tcl.

normalize_design runs opt_clean -purge, which strips internal public wire names. equiv_make's find_same_wires then only matches top-level ports (65 $equiv cells), leaving equiv_induct without internal cut-points, so no output can be proven. Both netlists have 171 name-matching flip-flops; bounded model checking with real $assert cells finds no reachable 2-valued divergence (the only "counterexample" traces are X-initialization artifacts with clk tied to 0).

This is fixed on the release branch by commit a6217b9a ("fix(lec): keep public wire names for equiv_make cut-points") and will ship in the next release. With the fix, the same design reports 202 are proven and 0 are unproven. Equivalence successfully proven!

Workaround for v0.1.0-alpha.12 (patch script)

Save the script below as patch-ecc-alpha12-lec.sh, chmod +x it, and run it against your ECC onedir installation (the directory containing the ecc executable), or with ecc on PATH:

./patch-ecc-alpha12-lec.sh /path/to/ecc        # explicit bundle directory
./patch-ecc-alpha12-lec.sh                     # resolve ecc from PATH (symlinks followed)

The script verifies ecc --version reports exactly ecc 0.1.0a12, sanity-checks the run_lec.tcl layout, rewrites only the normalize-side opt_clean -purge (the equiv-level one is intentionally kept), keeps a .bak backup, and is idempotent. It refuses any other version or an unexpected file layout. Afterwards re-run your flow (ecc run --project <project> --workspace <id>); the LEC step regenerates its script from the patched copy.

patch-ecc-alpha12-lec.sh (click to expand)
#!/usr/bin/env bash
# patch-ecc-alpha12-lec.sh
#
# Apply the synthesis-LEC fix (commit a6217b9a) to an ECC 0.1.0-alpha.12
# PyInstaller onedir installation.
#
# Background: normalize_design in run_lec.tcl ran `opt_clean -purge`, which
# strips internal public wire names. equiv_make then only matched top-level
# ports, leaving induction without internal cut-points, so every output
# stayed unproven and the LEC step failed. The fix drops `-purge` on the
# normalize-side opt_clean (the equiv-level one is intentionally kept).
#
# Usage:
#   ./patch-ecc-alpha12-lec.sh [ECC_APP_DIR]
#
# ECC_APP_DIR is the onedir bundle directory (the one containing the `ecc`
# executable). If omitted, the script resolves `ecc` from PATH.

set -euo pipefail

EXPECTED_VERSION="0.1.0a12"
TARGET_REL="_internal/chipcompiler/tools/yosys_lec/scripts/run_lec.tcl"
SPLITNETS_LINE='^    yosys splitnets -ports -format _$'
PURGE_LINE='^    yosys opt_clean -purge$'
EQUIV_ANCHOR='^    yosys hierarchy -top equiv$'

log() { printf '%s\n' "$*"; }
fail() { printf 'error: %s\n' "$*" >&2; exit 1; }

resolve_exe() {
    # Follow symlinks so a ~/.local/bin/ecc shim still finds its _internal/.
    if readlink -f "$1" >/dev/null 2>&1; then
        readlink -f "$1"
    elif command -v realpath >/dev/null 2>&1; then
        realpath "$1"
    else
        fail "need readlink -f or realpath to resolve $1"
    fi
}

resolve_app_dir() {
    if [ $# -ge 1 ]; then
        local candidate="$1"
        if [ -d "$candidate" ]; then
            printf '%s\n' "$candidate"
            return
        fi
        if [ -f "$candidate" ] && [ -x "$candidate" ]; then
            dirname "$(resolve_exe "$candidate")"
            return
        fi
        fail "ECC_APP_DIR is neither a directory nor an executable: $candidate"
    fi
    local ecc_on_path
    ecc_on_path=$(command -v ecc 2>/dev/null) || fail "no argument given and 'ecc' not found on PATH"
    dirname "$(resolve_exe "$ecc_on_path")"
}

APP_DIR=$(resolve_app_dir "$@")
ECC_BIN="$APP_DIR/ecc"
[ -f "$ECC_BIN" ] && [ -x "$ECC_BIN" ] || fail "ecc executable not found at $ECC_BIN"

# 1. Version gate: only 0.1.0-alpha.12 (PEP 440: 0.1.0a12) is known affected.
VERSION_OUTPUT=$("$ECC_BIN" --version 2>/dev/null) || fail "failed to run '$ECC_BIN --version'"
if [ "$VERSION_OUTPUT" != "ecc $EXPECTED_VERSION" ]; then
    fail "this patch only applies to 'ecc $EXPECTED_VERSION', detected: '${VERSION_OUTPUT:-<empty>}'"
fi
log "detected $VERSION_OUTPUT at $APP_DIR"

TARGET="$APP_DIR/$TARGET_REL"
[ -f "$TARGET" ] || fail "LEC script not found: $TARGET (not an ECC onedir install?)"

# 2. Layout sanity: exactly one splitnets anchor and one or two purge sites.
ANCHOR_COUNT=$(grep -c "$SPLITNETS_LINE" "$TARGET" || true)
[ "$ANCHOR_COUNT" = "1" ] || fail "unexpected run_lec.tcl layout (expected 1 splitnets line, found $ANCHOR_COUNT); refusing to patch"
EQUIV_ANCHOR_COUNT=$(grep -c "$EQUIV_ANCHOR" "$TARGET" || true)
[ "$EQUIV_ANCHOR_COUNT" = "1" ] || fail "unexpected run_lec.tcl layout (expected 1 'hierarchy -top equiv' line, found $EQUIV_ANCHOR_COUNT); refusing to patch"
PURGE_COUNT=$(grep -c "$PURGE_LINE" "$TARGET" || true)
NORMALIZE_LINE=$(sed -n "/$SPLITNETS_LINE/{n;p;}" "$TARGET")
EQUIV_LINE=$(sed -n "/$EQUIV_ANCHOR/{n;p;}" "$TARGET")
[ "$EQUIV_LINE" = "    yosys opt_clean -purge" ] \
    || fail "unexpected run_lec.tcl layout (equiv-level line is '$EQUIV_LINE'); refusing to patch"

if [ "$NORMALIZE_LINE" = "    yosys opt_clean" ] && [ "$PURGE_COUNT" = "1" ]; then
    log "already patched: $TARGET"
    exit 0
fi
if [ "$NORMALIZE_LINE" != "    yosys opt_clean -purge" ] || [ "$PURGE_COUNT" != "2" ]; then
    fail "unexpected run_lec.tcl layout (normalize='$NORMALIZE_LINE', purge sites=$PURGE_COUNT); refusing to patch"
fi

[ -w "$TARGET" ] || fail "no write permission on $TARGET (re-run with sufficient privileges)"

# 3. Patch only the normalize-side occurrence (line after the unique splitnets anchor).
sed -i.bak "/$SPLITNETS_LINE/{n;s/$PURGE_LINE/    yosys opt_clean/;}" "$TARGET"

# 4. Verify: normalize patched, equiv-level -purge still present, backup kept.
[ "$(sed -n "/$SPLITNETS_LINE/{n;p;}" "$TARGET")" = "    yosys opt_clean" ] \
    || fail "patch verification failed (normalize opt_clean unchanged)"
[ "$(grep -c "$PURGE_LINE" "$TARGET" || true)" = "1" ] \
    || fail "patch verification failed (equiv-level opt_clean -purge missing)"
[ "$(sed -n "/$EQUIV_ANCHOR/{n;p;}" "$TARGET")" = "    yosys opt_clean -purge" ] \
    || fail "patch verification failed (equiv-level opt_clean -purge moved)"

log "patched: $TARGET"
log "backup:  $TARGET.bak"
log "next:    re-run your ECC workspace (e.g. ecc run --project <project>); the LEC step regenerates its script from the patched copy."

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdocumentationImprovements or additions to documentation

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions