Self checks
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
- Install ECC v0.1.0-alpha.12 from the release PyInstaller bundle.
- Run the default flow on a design with internal sequential state, e.g. the pm32 example:
ecc run --project pm32 --workspace default
- 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."
Self checks
ECC version
v0.1.0-alpha.12 (
ecc --versionreportsecc 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
Steps to reproduce
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:Logs or command output
Root cause (confirmed)
Not a real functional mismatch — a proof-methodology bug in the bundled
run_lec.tcl.normalize_designrunsopt_clean -purge, which strips internal public wire names.equiv_make'sfind_same_wiresthen only matches top-level ports (65$equivcells), leavingequiv_inductwithout internal cut-points, so no output can be proven. Both netlists have 171 name-matching flip-flops; bounded model checking with real$assertcells finds no reachable 2-valued divergence (the only "counterexample" traces are X-initialization artifacts withclktied 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 reports202 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 +xit, and run it against your ECC onedir installation (the directory containing theeccexecutable), or withecconPATH:The script verifies
ecc --versionreports exactlyecc 0.1.0a12, sanity-checks therun_lec.tcllayout, rewrites only the normalize-sideopt_clean -purge(the equiv-level one is intentionally kept), keeps a.bakbackup, 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)