Compare commits

..

No commits in common. "master" and "8.3.646" have entirely different histories.

92 changed files with 1711 additions and 4810 deletions

View File

@ -1,17 +1,13 @@
name: CI-wasm name: CI-wasm
# Builds the Magic WebAssembly target (both the non-TCL and TCL variants) # Builds the Magic WebAssembly target on every push and pull request.
# on every push and pull request as a CI check. **Publishing** only happens # When the VERSION file changes on the default branch, the package is
# when a release tag of the form <x.y.z>... (optionally v-prefixed) is # additionally published to GitHub Packages (npm.pkg.github.com) as
# pushed — the same tag that triggers the AppImage release workflows: # @<owner>/magic-vlsi-wasm — no manual tag or token required.
# Tim Edwards updates VERSION to trigger a new release; the scope resolves
# automatically to the repo owner, so forks publish under their own namespace.
# #
# # bump magic/VERSION, commit, push to default branch # WASM is architecture-independent — built once on x86-64, usable everywhere.
# git tag 8.3.638
# git push origin 8.3.638
#
# The tag name (minus any leading "v") provides the base; the workflow appends
# the commit date and short SHA: 8.3.799 → 8.3.799020261231+git01234cde.
# Forks publish under their own namespace via the @<owner>/ scope.
on: on:
push: push:
@ -22,14 +18,6 @@ on:
description: 'emsdk version to build with (default: latest; pin a version number to bisect)' description: 'emsdk version to build with (default: latest; pin a version number to bisect)'
type: string type: string
default: 'latest' default: 'latest'
tcl_ref:
description: 'TCL ref to build against (default: auto-resolve latest stable tag). Use a tag like core-9-0-3, a branch, or a commit SHA to bisect a regression.'
type: string
default: ''
tcl_repo_url:
description: 'TCL repository URL (default: https://github.com/tcltk/tcl.git)'
type: string
default: ''
dry_run: dry_run:
description: 'Dry run: pack only, do not publish even on tag pushes' description: 'Dry run: pack only, do not publish even on tag pushes'
type: boolean type: boolean
@ -82,65 +70,32 @@ jobs:
echo "===== emcc -dM -E - ====="; echo | emcc -dM -E - | sort echo "===== emcc -dM -E - ====="; echo | emcc -dM -E - | sort
echo "===== em++ -dM -E - ====="; echo | em++ -dM -E - | sort echo "===== em++ -dM -E - ====="; echo | em++ -dM -E - | sort
# Determine which TCL ref to build against. - name: Build WASM
# Priority: workflow_dispatch input > auto-resolved latest stable tag.
# TCL stable releases follow the core-<major>-<even_minor>-<patch>
# naming convention; core-9-0-x is the current stable series.
# Falls back to main only if no release tags are found at all.
- name: Resolve TCL ref
id: resolve-tcl
env:
TCL_REPO_URL: ${{ github.event.inputs.tcl_repo_url || 'https://github.com/tcltk/tcl.git' }}
TCL_REF_INPUT: ${{ github.event.inputs.tcl_ref || '' }}
run: |
if [ -n "$TCL_REF_INPUT" ]; then
TCL_REF="$TCL_REF_INPUT"
echo "Using workflow_dispatch TCL_REF: $TCL_REF"
else
TCL_REF=$(git ls-remote --tags --sort=-version:refname "$TCL_REPO_URL" \
'refs/tags/core-9-0-*' \
| grep -v '\^{}' \
| head -1 \
| awk '{print $2}' \
| sed 's|refs/tags/||')
if [ -z "$TCL_REF" ]; then
TCL_REF=main
echo "Warning: no stable core-9-0-x tag found, falling back to main"
else
echo "Auto-resolved latest stable TCL tag: $TCL_REF"
fi
fi
echo "tcl_ref=$TCL_REF" >> "$GITHUB_OUTPUT"
echo "tcl_repo_url=$TCL_REPO_URL" >> "$GITHUB_OUTPUT"
# Clone tcltk/tcl into a sibling directory at the resolved ref.
# Done as an explicit step so the exact commit is visible in the job
# log. The source tree is read-only — the WASM build runs inside magic.
- name: Clone tcltk/tcl
env:
TCL_REPO_URL: ${{ steps.resolve-tcl.outputs.tcl_repo_url }}
TCL_REF: ${{ steps.resolve-tcl.outputs.tcl_ref }}
run: |
# autocrlf=false: ubuntu-latest is already LF, but make it explicit.
git -c core.autocrlf=false clone "$TCL_REPO_URL" ../tcl
cd ../tcl
git checkout --detach "$TCL_REF"
echo "=== TCL commit ==="
git log -n1 --format="commit %H%nauthor %an <%ae>%ndate %ci%nref %D%n%n %s"
- name: Build WASM — both variants (tcl + notcl)
env:
TCL_REF: ${{ steps.resolve-tcl.outputs.tcl_ref }}
TCL_REPO_URL: ${{ steps.resolve-tcl.outputs.tcl_repo_url }}
run: | run: |
source ./emsdk/emsdk_env.sh source ./emsdk/emsdk_env.sh
bash npm/build.sh --variant=both # --without/--disable flags: no WASM library available for these features
CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1" emconfigure ./configure \
--without-cairo --without-opengl --without-x --without-tk --without-tcl \
--disable-readline --disable-compression \
--host=asmjs-unknown-emscripten \
--target=asmjs-unknown-emscripten
# Append WASM linker flags and activate the WASM link target
cat toolchains/emscripten/defs.mak >> defs.mak
# Echo the merged defs.mak so CI logs show the exact build config
echo "===== defs.mak ====="; cat defs.mak; echo "===== defs.mak ====="
# Build in order: techs must exist before mains (--embed-file embeds them)
emmake make depend
emmake make -j$(nproc) modules libs
emmake make techs
emmake make mains
- name: Run example tests (non-TCL variant) - name: Copy WASM artifacts into npm/
run: cd npm && npm test run: |
cp magic/magic.js npm/
cp magic/magic.wasm npm/
- name: Run full test suite (TCL variant) - name: Run example tests
run: cd npm && npm run test:tcl run: cd npm && npm run test
# Dump generated text outputs (.ext, .spice, .cif, …) into the CI log # Dump generated text outputs (.ext, .spice, .cif, …) into the CI log
# so a regression in extraction / netlisting / cifoutput is visible # so a regression in extraction / netlisting / cifoutput is visible
@ -149,62 +104,29 @@ jobs:
- name: Display example outputs - name: Display example outputs
run: | run: |
shopt -s nullglob shopt -s nullglob
for dir in npm/examples/output npm/examples/output-tcl; do for f in npm/examples/output/*; do
[ -d "$dir" ] || continue name=$(basename "$f")
echo "======== $dir ========" case "$f" in
for f in "$dir"/*; do *.gds) echo "===== $name (binary, $(wc -c < "$f") bytes — skipped) =====" ;;
name=$(basename "$f") *) echo "===== $name ====="; cat "$f" ;;
case "$f" in esac
*.gds) echo "===== $name (binary, $(wc -c < "$f") bytes — skipped) =====" ;;
*) echo "===== $name ====="; cat "$f" ;;
esac
done
done done
# The release gate. We publish a new npm version only when a tag of the
# shape <x.y.z>... (optionally v-prefixed) is pushed, matching the
# AppImage release workflows so one tag releases everything.
#
# Version scheme (per dmiles' recommendation):
# {MAJOR}.{MINOR}.{PATCH}0{YYYYMMDD}+git{SHORT_SHA}
# e.g. 8.3.799 pushed on 2026-12-31 → 8.3.799020261231+git01234cde
#
# The leading zero between PATCH and date keeps the number readable and
# ensures correct numeric ordering: 799020261231 < 800020261231.
# Build metadata (+git...) is ignored by npm for comparison but retained
# for traceability. Security patches for the 799 series can be inserted
# as later dates (799020270101, 799020270201, …) and are matched by the
# range <=8.3.799900000000 or <8.3.8000000000000.
- name: Determine release version (tag-driven only)
id: release
run: |
date=$(git show -s --format=%cs | tr -d '-')
hash=$(git show -s --format=%h)
if [ "${{ github.event_name }}" = "push" ] && \
echo "${{ github.ref }}" | grep -Eq '^refs/tags/v?[0-9]+\.[0-9]+\.[0-9]+'; then
tag="${GITHUB_REF#refs/tags/}"
base="${tag#v}"
echo "publish=true" >> "$GITHUB_OUTPUT"
echo "version=${base}0${date}+git${hash}" >> "$GITHUB_OUTPUT"
echo "Tag release: $tag → npm version ${base}0${date}+git${hash}"
else
base=$(cat VERSION)
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "version=${base}0${date}+git${hash}" >> "$GITHUB_OUTPUT"
echo "Non-tag build: will not publish."
fi
- name: Set package version and scope - name: Set package version and scope
env:
VERSION: ${{ steps.release.outputs.version }}
run: | run: |
base=$(cat VERSION) # e.g. 8.3.637
date=$(git show -s --format=%cs | tr -d '-') # e.g. 20260414
hash=$(git show -s --format=%h) # e.g. d157eea
VERSION="${base}-${date}.${hash}"
# Scope the package to the repo owner so it lands in the right # Scope the package to the repo owner so it lands in the right
# GitHub Packages namespace regardless of who hosts the repo. # GitHub Packages namespace regardless of who hosts the repo.
# e.g. @rtimothyedwards/magic-vlsi-wasm on Tim's repo,
# @intubun/magic-vlsi-wasm on a fork.
SCOPED_NAME="@${{ github.repository_owner }}/magic-vlsi-wasm" SCOPED_NAME="@${{ github.repository_owner }}/magic-vlsi-wasm"
cd npm cd npm
npm pkg set name="$SCOPED_NAME" npm pkg set name="$SCOPED_NAME"
npm pkg set publishConfig.registry="https://npm.pkg.github.com" npm pkg set publishConfig.registry="https://npm.pkg.github.com"
npm version "$VERSION" --no-git-tag-version --allow-same-version npm version "$VERSION" --no-git-tag-version
- name: Pack - name: Pack
run: ./npm/pack.sh run: ./npm/pack.sh
@ -215,42 +137,24 @@ jobs:
name: magic-vlsi-wasm-npm name: magic-vlsi-wasm-npm
path: npm/*.tgz path: npm/*.tgz
- name: Check if VERSION changed
id: version_changed
if: github.event_name == 'push'
run: |
if echo "${{ github.ref }}" | grep -q '^refs/tags/'; then
echo "changed=true" >> $GITHUB_OUTPUT
elif [ "${{ github.ref }}" = "refs/heads/${{ github.event.repository.default_branch }}" ]; then
if git diff --name-only HEAD~1 HEAD 2>/dev/null | grep -q '^VERSION$'; then
echo "changed=true" >> $GITHUB_OUTPUT
else
echo "changed=false" >> $GITHUB_OUTPUT
fi
else
echo "changed=false" >> $GITHUB_OUTPUT
fi
- name: Publish to GitHub Packages - name: Publish to GitHub Packages
if: steps.release.outputs.publish == 'true' && github.event.inputs.dry_run != 'true' if: steps.version_changed.outputs.changed == 'true' && github.event.inputs.dry_run != 'true'
run: cd npm && npm publish run: cd npm && npm publish
env: env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Write a Markdown summary visible next to the artifacts on the Actions
# page. Captures the exact versions used so a future regression can be
# bisected without scrolling through raw logs.
- name: Build summary
if: always()
env:
TCL_REF: ${{ steps.resolve-tcl.outputs.tcl_ref }}
TCL_REPO_URL: ${{ steps.resolve-tcl.outputs.tcl_repo_url }}
run: |
source ./emsdk/emsdk_env.sh 2>/dev/null || true
EMCC_VER=$(emcc --version 2>/dev/null | head -1 || echo "unavailable")
GCC_VER=$(gcc --version 2>/dev/null | head -1 || echo "unavailable")
NODE_VER=$(node --version 2>/dev/null || echo "unavailable")
MAGIC_VER=$(cat VERSION 2>/dev/null || echo "unavailable")
if [ -d ../tcl/.git ]; then
TCL_SHA=$(cd ../tcl && git rev-parse HEAD)
TCL_DATE=$(cd ../tcl && git log -1 --format="%ci")
TCL_SUBJECT=$(cd ../tcl && git log -1 --format="%s")
else
TCL_SHA="(not cloned)"; TCL_DATE=""; TCL_SUBJECT=""
fi
printf '## Build info\n\n' >> "$GITHUB_STEP_SUMMARY"
printf '| Component | Details |\n' >> "$GITHUB_STEP_SUMMARY"
printf '|-----------|----------|\n' >> "$GITHUB_STEP_SUMMARY"
printf '| Magic | `%s` |\n' "$MAGIC_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| Emscripten | %s |\n' "$EMCC_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| GCC | %s |\n' "$GCC_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| Node.js | %s |\n' "$NODE_VER" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL repo | %s |\n' "$TCL_REPO_URL" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL ref | `%s` |\n' "$TCL_REF" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL commit | `%s` |\n' "$TCL_SHA" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL date | %s |\n' "$TCL_DATE" >> "$GITHUB_STEP_SUMMARY"
printf '| TCL subject | %s |\n' "$TCL_SUBJECT" >> "$GITHUB_STEP_SUMMARY"

1
.gitignore vendored
View File

@ -54,7 +54,6 @@ magic/magic.js
magic/magic.js.symbols magic/magic.js.symbols
magic/magic.symbols magic/magic.symbols
magic/magic.wasm magic/magic.wasm
build-tcl-wasm/
net2ir/net2ir net2ir/net2ir
net2ir/net2ir.js net2ir/net2ir.js
net2ir/net2ir.wasm net2ir/net2ir.wasm

View File

@ -1 +1 @@
8.3.664 8.3.646

View File

@ -537,6 +537,7 @@ calmaElementPath(void)
int layer, dt, width, pathtype, ciftype, savescale; int layer, dt, width, pathtype, ciftype, savescale;
int xmin, ymin, xmax, ymax, temp; int xmin, ymin, xmax, ymax, temp;
CIFPath *pathheadp, *pathp, *previousp; CIFPath *pathheadp, *pathp, *previousp;
Rect segment;
Plane *plane; Plane *plane;
int first,last; int first,last;
CellUse *use; CellUse *use;

View File

@ -4171,21 +4171,12 @@ cifSrTiles2(
} }
cifScale = 1; cifScale = 1;
if (TTMaskIsZero(&cifOp->co_cifMask) && TTMaskIsZero(&cifOp->co_paintMask)) for (t = 0; t < TT_MAXTYPES; t++, temps++)
{ if (TTMaskHasType(&cifOp->co_cifMask, t))
/* Current CIF plane is in *temps */ if (DBSrPaintArea((Tile *)NULL, *temps, area,
if (DBSrPaintArea((Tile *)NULL, *temps, area,
&CIFSolidBits, func, (ClientData)cdArg)) &CIFSolidBits, func, (ClientData)cdArg))
return 1; return 1;
}
else
{
for (t = 0; t < TT_MAXTYPES; t++, temps++)
if (TTMaskHasType(&cifOp->co_cifMask, t))
if (DBSrPaintArea((Tile *)NULL, *temps, area,
&CIFSolidBits, func, (ClientData)cdArg))
return 1;
}
return 0; return 0;
} }
@ -5463,6 +5454,7 @@ CIFGenLayer(
if (bloats->bl_plane < 0) /* Bloat types are CIF types */ if (bloats->bl_plane < 0) /* Bloat types are CIF types */
{ {
bls.temps = temps;
for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++) for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++)
if (bloats->bl_distance[ttype] > 0) if (bloats->bl_distance[ttype] > 0)
(void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect, (void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect,
@ -5668,20 +5660,6 @@ CIFGenLayer(
bloats->bl_distance[ttype] = 0; bloats->bl_distance[ttype] = 0;
} }
} }
else
{
/* Operate on the existing plane. */
bloats->bl_distance[0] = 1;
for (ttype = 1; ttype < TT_MAXTYPES; ttype++)
bloats->bl_distance[ttype] = 0;
bloats->bl_plane = -1;
bls.temps = &curPlane;
DBClearPaintPlane(nextPlane);
cifPlane = nextPlane;
}
/* Replace the client data with the bloat record */ /* Replace the client data with the bloat record */
op->co_client = (ClientData)bloats; op->co_client = (ClientData)bloats;
@ -5702,33 +5680,15 @@ CIFGenLayer(
} }
for (label = cellDef->cd_labels; label; label = label->lab_next) for (label = cellDef->cd_labels; label; label = label->lab_next)
{
if (!strcmp(label->lab_text, text)) if (!strcmp(label->lab_text, text))
{ cifSrTiles2(op, &label->lab_rect, cellDef, temps,
Rect labr = label->lab_rect;
/* Since cifSrTiles2() searches over an area, the
* area must not be degenerate.
*/
if (labr.r_xbot == labr.r_xtop)
{
labr.r_xbot--;
labr.r_xtop++;
}
if (labr.r_ybot == labr.r_ytop)
{
labr.r_ybot--;
labr.r_ytop++;
}
cifSrTiles2(op, &labr, cellDef, bls.temps,
cifBloatAllFunc, (ClientData)&bls); cifBloatAllFunc, (ClientData)&bls);
}
}
/* Reset marked tiles */ /* Reset marked tiles */
if (bloats->bl_plane < 0) /* Bloat types are CIF types */ if (bloats->bl_plane < 0) /* Bloat types are CIF types */
{ {
bls.temps = temps;
for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++) for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++)
if (bloats->bl_distance[ttype] > 0) if (bloats->bl_distance[ttype] > 0)
(void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect, (void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect,
@ -5746,15 +5706,6 @@ CIFGenLayer(
/* Replace the client data */ /* Replace the client data */
op->co_client = (ClientData)text; op->co_client = (ClientData)text;
/* If operating on the current plane, swap the current
* and next planes.
*/
if (TTMaskIsZero(&op->co_cifMask) && TTMaskIsZero(&op->co_paintMask))
{
temp = curPlane;
curPlane = nextPlane;
nextPlane = temp;
}
break; break;
case CIFOP_BOUNDARY: case CIFOP_BOUNDARY:

View File

@ -462,22 +462,9 @@ CIFPaintWirePath(
/* Wire reverses direction. Break wire here, */ /* Wire reverses direction. Break wire here, */
/* draw, and start new polygon. */ /* draw, and start new polygon. */
/* Check first if last point and current point */ TxError("Warning: direction reversal in path at (%d, %d).\n",
/* are the same, in which case a different */ pathp->cifp_x, pathp->cifp_y);
/* message should be issued (and possibly */
/* should be handled differently?) */
if (previousp && previousp->cifp_x == pathp->cifp_x
&& previousp->cifp_y == pathp->cifp_y)
{
TxError("Warning: duplicate point in path at (%d, %d).\n",
pathp->cifp_x / 2, pathp->cifp_y / 2);
}
else
{
TxError("Warning: direction reversal in path at (%d, %d).\n",
pathp->cifp_x / 2, pathp->cifp_y / 2);
}
phi = theta; phi = theta;
if (endcap) if (endcap)
{ {

View File

@ -1032,10 +1032,9 @@ CIFReadTechLine(
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]); newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
break; break;
case CIFOP_TAGGED: case CIFOP_TAGGED:
if ((argc != 2) && (argc != 3)) goto wrongNumArgs; if (argc != 3) goto wrongNumArgs;
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]); newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
if (argc == 3) CIFParseReadLayers(argv[2], &newOp->co_cifMask, TRUE);
CIFParseReadLayers(argv[2], &newOp->co_cifMask, TRUE);
break; break;
} }

View File

@ -1360,11 +1360,10 @@ bloatCheck:
case CIFOP_NET: case CIFOP_NET:
case CIFOP_TAGGED: case CIFOP_TAGGED:
if ((argc != 2) && (argc != 3)) goto wrongNumArgs; if (argc != 3) goto wrongNumArgs;
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]); newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
if (argc == 3) cifParseLayers(argv[2], CIFCurStyle, &newOp->co_paintMask,
cifParseLayers(argv[2], CIFCurStyle, &newOp->co_paintMask, &newOp->co_cifMask, FALSE);
&newOp->co_cifMask, FALSE);
break; break;
case CIFOP_MASKHINTS: case CIFOP_MASKHINTS:

View File

@ -1096,7 +1096,7 @@ CmdExtract(
"lumped estimate lumped resistance", "lumped estimate lumped resistance",
"labelcheck check for connections through sticky labels", "labelcheck check for connections through sticky labels",
"aliases output all net name aliases", "aliases output all net name aliases",
"unique [notopports] ensure unique node names during extraction", "unique ensure unique node names during extraction",
"resistance extract resistance (same as \"do extresist\")", "resistance extract resistance (same as \"do extresist\")",
NULL NULL
}; };
@ -1403,7 +1403,6 @@ CmdExtract(
TxPrintf("%s label check\n", OPTSET(EXT_DOLABELCHECK)); TxPrintf("%s label check\n", OPTSET(EXT_DOLABELCHECK));
TxPrintf("%s aliases\n", OPTSET(EXT_DOALIASES)); TxPrintf("%s aliases\n", OPTSET(EXT_DOALIASES));
TxPrintf("%s unique\n", OPTSET(EXT_DOUNIQUE)); TxPrintf("%s unique\n", OPTSET(EXT_DOUNIQUE));
TxPrintf("%s unique notopports\n", OPTSET(EXT_DOUNIQNOTOPPORTS));
TxPrintf("%s resistance (extresist)\n", OPTSET(EXT_DOEXTRESIST)); TxPrintf("%s resistance (extresist)\n", OPTSET(EXT_DOEXTRESIST));
return; return;
#undef OPTSET #undef OPTSET
@ -1434,19 +1433,9 @@ CmdExtract(
case DORESISTANCE: option = EXT_DORESISTANCE; break; case DORESISTANCE: option = EXT_DORESISTANCE; break;
case DOLABELCHECK: option = EXT_DOLABELCHECK; break; case DOLABELCHECK: option = EXT_DOLABELCHECK; break;
case DOALIASES: option = EXT_DOALIASES; break; case DOALIASES: option = EXT_DOALIASES; break;
case DOUNIQUE: option = EXT_DOUNIQUE; break;
case DOEXTRESIST: case DOEXTRESIST:
case DOEXTRESIST2: option = EXT_DOEXTRESIST; break; case DOEXTRESIST2: option = EXT_DOEXTRESIST; break;
case DOUNIQUE:
if (argc == 4)
{
if (!strncmp(argv[3], "notop", 5))
option = EXT_DOUNIQNOTOPPORTS | EXT_DOUNIQUE;
else
TxError("Usage: extract do unique [notopports]\n");
}
else
option = EXT_DOUNIQUE;
break;
case DOLOCAL: case DOLOCAL:
/* "extract do local" and "extract no local" are kept for /* "extract do local" and "extract no local" are kept for
* backwards compatibility, but now effectively implement * backwards compatibility, but now effectively implement

View File

@ -2324,7 +2324,7 @@ CmdDoProperty(
TxCommand *cmd, TxCommand *cmd,
int argstart) int argstart)
{ {
PropertyRecord *proprec = NULL; PropertyRecord *proprec;
char *value; char *value;
bool propfound, dolist; bool propfound, dolist;
int proptype, proplen, propvalue, i; int proptype, proplen, propvalue, i;
@ -2662,31 +2662,31 @@ CmdDoProperty(
* the valid number of arguments, then again to parse the * the valid number of arguments, then again to parse the
* values, once the property record has been allocated * values, once the property record has been allocated
*/ */
value = cmd->tx_argv[argstart + 1]; if (proptype == PROPERTY_TYPE_PLANE)
for (proplen = 0; *value != '\0'; )
{ {
if (isspace(*value) && (*value != '\0')) value++; proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord));
if (!isspace(*value)) plane = DBNewPlane((ClientData)TT_SPACE);
{ proprec->prop_value.prop_plane = plane;
proplen++;
while (!isspace(*value) && (*value != '\0')) value++;
}
} }
if (proplen > 0) else
{ {
if (proptype == PROPERTY_TYPE_PLANE) value = cmd->tx_argv[argstart + 1];
for (proplen = 0; *value != '\0'; )
{ {
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord)); if (isspace(*value) && (*value != '\0')) value++;
plane = DBNewPlane((ClientData)TT_SPACE); if (!isspace(*value))
proprec->prop_value.prop_plane = plane; {
} else { proplen++;
while (!isspace(*value) && (*value != '\0')) value++;
}
}
if (proplen > 0)
proprec = (PropertyRecord *)mallocMagic( proprec = (PropertyRecord *)mallocMagic(
sizeof(PropertyRecord) + sizeof(PropertyRecord) +
(proplen - 2) * sizeof(int)); (proplen - 2) * sizeof(int));
}
proprec->prop_type = proptype;
proprec->prop_len = proplen;
} }
proprec->prop_type = proptype;
proprec->prop_len = proplen;
/* Second pass */ /* Second pass */
value = cmd->tx_argv[argstart + 1]; value = cmd->tx_argv[argstart + 1];

View File

@ -647,9 +647,6 @@ cmdSelectArea(
if (!(crec->dbw_flags & DBW_SEELABELS)) TTMaskClearType(&mask, L_LABEL); if (!(crec->dbw_flags & DBW_SEELABELS)) TTMaskClearType(&mask, L_LABEL);
if (!(crec->dbw_flags & DBW_SEECELLS)) TTMaskClearType(&mask, L_CELL); if (!(crec->dbw_flags & DBW_SEECELLS)) TTMaskClearType(&mask, L_CELL);
} }
else if (option == SEL_AREA)
TTMaskSetType(&mask, L_LABEL);
SelectArea(&scx, &mask, crec->dbw_bitmask, globmatch); SelectArea(&scx, &mask, crec->dbw_bitmask, globmatch);
} }

View File

@ -53,10 +53,10 @@ typedef struct dbcellboundstruct
*/ */
int int
DBBoundCellPlane( DBBoundCellPlane(def, extended, rect)
CellDef *def, CellDef *def;
Rect *extended, Rect *extended;
Rect *rect) Rect *rect;
{ {
TreeFilter filter; TreeFilter filter;
DBCellBoundStruct cbs; DBCellBoundStruct cbs;
@ -79,9 +79,9 @@ DBBoundCellPlane(
} }
int int
dbCellBoundFunc( dbCellBoundFunc(use, fp)
CellUse *use, CellUse *use;
TreeFilter *fp) TreeFilter *fp;
{ {
DBCellBoundStruct *cbs; DBCellBoundStruct *cbs;
@ -124,9 +124,9 @@ dbCellBoundFunc(
*/ */
bool bool
DBBoundPlane( DBBoundPlane(plane, rect)
Plane *plane, Plane *plane;
Rect *rect) Rect *rect;
{ {
Tile *left, *right, *top, *bottom, *tp; Tile *left, *right, *top, *bottom, *tp;
@ -205,9 +205,9 @@ DBBoundPlane(
*/ */
bool bool
DBBoundPlaneVert( DBBoundPlaneVert(plane, rect)
Plane *plane, Plane *plane;
Rect *rect) Rect *rect;
{ {
Tile *left, *right, *top, *bottom, *tp; Tile *left, *right, *top, *bottom, *tp;

View File

@ -1037,13 +1037,7 @@ dbcConnectFunc(tile, dinfo, cx)
if (++csa2->csa2_top == CSA2_LIST_SIZE) if (++csa2->csa2_top == CSA2_LIST_SIZE)
{ {
/* Reached list size limit---need to push the list and */ /* Reached list size limit---need to push the list and */
/* start a new one. NOTE: Setting lasttop to -1 means */ /* start a new one. */
/* that some entries may be duplicated between the */
/* stacks, which is a small inefficiency. In theory, */
/* lasttop could be left as is, then if lasttop > top */
/* when searching the last 5 entries, pop the stack, do */
/* the search, and then push the stack again. But it's */
/* a lot easier just to be slightly inefficient. */
conSrArea *newlist; conSrArea *newlist;
@ -1051,7 +1045,6 @@ dbcConnectFunc(tile, dinfo, cx)
StackPush((ClientData)csa2->csa2_list, csa2->csa2_stack); StackPush((ClientData)csa2->csa2_list, csa2->csa2_stack);
csa2->csa2_list = newlist; csa2->csa2_list = newlist;
csa2->csa2_top = 0; csa2->csa2_top = 0;
csa2->csa2_lasttop = -1;
} }
csa2->csa2_list[csa2->csa2_top].area = newarea; csa2->csa2_list[csa2->csa2_top].area = newarea;

View File

@ -574,8 +574,7 @@ DBReOrientLabel(cellDef, area, newPos)
* dbGetLabelArea --- * dbGetLabelArea ---
* *
* Callback function used by DBAdjustLabels. Find all material under a label * Callback function used by DBAdjustLabels. Find all material under a label
* that is *not* the label type, and return the label area adjusted to leave * that is *not* the label type, and return the
* out that amount.
* *
* Note: This clips in a regular order, and does not consider what is the * Note: This clips in a regular order, and does not consider what is the
* largest rectangular area outside the area that has been clipped out. * largest rectangular area outside the area that has been clipped out.
@ -605,26 +604,6 @@ dbGetLabelArea(tile, dinfo, area)
return 0; return 0;
} }
/*
* ----------------------------------------------------------------------------
*
* dbLabelNotEmpty ---
*
* Callback function used by DBAdjustLabels. Finds any material under a
* label that is the label type, and returns 1 to stop the search.
*
* ----------------------------------------------------------------------------
*/
int
dbLabelNotEmpty(tile, dinfo, clientData)
Tile *tile; /* Tile found. */
TileType dinfo; /* Split tile information (unused) */
ClientData clientData; /* (unused) */
{
return 1;
}
/* /*
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
* *
@ -682,37 +661,28 @@ DBAdjustLabels(def, area)
TTMaskSetOnlyType(&lmask, lab->lab_type); TTMaskSetOnlyType(&lmask, lab->lab_type);
/* To do: Add compatible types (contact, residue) */ /* To do: Add compatible types (contact, residue) */
TTMaskCom(&lmask);
/* If there is no material left inside the label area, then r = lab->lab_rect;
* the label gets reassigned to space. DBSrPaintArea((Tile *) NULL, def->cd_planes[DBPlane(lab->lab_type)],
*/ &lab->lab_rect, &lmask, dbGetLabelArea, (ClientData) &r);
if (DBSrPaintArea((Tile *) NULL, def->cd_planes[DBPlane(lab->lab_type)],
&lab->lab_rect, &lmask, dbLabelNotEmpty, (ClientData)NULL) == 1) if (!GEO_RECTNULL(&r))
{ {
TTMaskCom(&lmask); if ((DBVerbose >= DB_VERBOSE_ALL) && ((def->cd_flags & CDINTERNAL) == 0))
r = lab->lab_rect;
DBSrPaintArea((Tile *) NULL, def->cd_planes[DBPlane(lab->lab_type)],
&lab->lab_rect, &lmask, dbGetLabelArea, (ClientData) &r);
if (!GEO_RECTNULL(&r))
{ {
if ((DBVerbose >= DB_VERBOSE_ALL) && TxPrintf("Adjusting size of label \"%s\" in cell %s.\n",
((def->cd_flags & CDINTERNAL) == 0))
{
TxPrintf("Adjusting size of label \"%s\" in cell %s.\n",
lab->lab_text, def->cd_name); lab->lab_text, def->cd_name);
}
DBUndoEraseLabel(def, lab);
DBWLabelChanged(def, lab, DBW_ALLWINDOWS);
lab->lab_rect = r;
DBFontLabelSetBBox(lab);
DBUndoPutLabel(def, lab);
DBWLabelChanged(def, lab, DBW_ALLWINDOWS);
modified = TRUE;
adjusted = TRUE;
} }
DBUndoEraseLabel(def, lab);
DBWLabelChanged(def, lab, DBW_ALLWINDOWS);
lab->lab_rect = r;
DBFontLabelSetBBox(lab);
DBUndoPutLabel(def, lab);
DBWLabelChanged(def, lab, DBW_ALLWINDOWS);
modified = TRUE;
adjusted = TRUE;
} }
} }

View File

@ -578,20 +578,6 @@ typedef struct extRectList
struct extRectList *r_next; struct extRectList *r_next;
} ExtRectList; } ExtRectList;
/* Structure similar to the above, but adding a pointer to a cell use ID
* and a client data record which can be used to hold a region pointer.
*/
typedef struct extConnList
{
char *r_useid; /* Cell Use being connected to */
TileType r_type; /* Connecting tile type in the parent */
Rect r_r; /* Area of connection */
ClientData r_upnode; /* Parent node making the connection */
ClientData r_downnode; /* Child node making the connection */
struct extConnList *r_next; /* Next item in the linked list */
} ExtConnList;
/* -------------------- Search context information -------------------- */ /* -------------------- Search context information -------------------- */
/* Search contexts are used in hierarchical searches */ /* Search contexts are used in hierarchical searches */

View File

@ -102,17 +102,16 @@ Circuit netlist extractor
but will usually just slow down processing by commands but will usually just slow down processing by commands
like "ext2spice" that use the .ext file contents, so it like "ext2spice" that use the .ext file contents, so it
is disabled by default. is disabled by default.
<DT> <B>unique</B> [<B>notopports</B>] <DT> <B>unique</B>
<DD> (Added in magic version 8.3.594) This setting replaces <DD> (Added in magic version 8.3.594) This setting replaces
the use of the command option "extract unique" (and the use of the command option "extract unique". Instead
"extract unique notopports"). Instead of changing labels of changing labels in the design, unique labels are
in the design, unique labels are generated for the duration generated for the duration of the extraction, and then
of the extraction, and then reverted back to the original reverted back to the original text. The "extract unique"
text. The "extract unique" command option is maintained command option is maintained for backwards compatibility.
for backwards compatibility. Note the difference: Note the difference: "extract unique" is a command that
"extract unique" is a command that runs immediately, and runs immediately, and cannot be undone;
cannot be undone; "extract do unique" is an option setting "extract do unique" is an option setting for "extract".
for "extract".
<DT> <B>resistance</B> <DT> <B>resistance</B>
<DD> (Added in magic version 8.3.597) This setting replaces <DD> (Added in magic version 8.3.597) This setting replaces
the use of the standalone command "extresist". The the use of the standalone command "extresist". The

View File

@ -30,30 +30,11 @@ information.
<BLOCKQUOTE> <BLOCKQUOTE>
where <I>option</I> may be one of the following: where <I>option</I> may be one of the following:
<DL> <DL>
<DT> <B>tolerance</B> <I>value</I>
<DD> Set the ratio between resistor and transistor tolerance
for determining when to insert resistance into a network route.
<DT> <B>all</B> <DT> <B>all</B>
<DD> Extract all the nets. <DD> Extract all the nets.
<DT> <B>threshold</B> [<I>value</I>]
<DD> With no value given, returns the current lumped resistance
threshold used to determine if a network will or will not be
analyzed for resistance extraction, in milliohms. The default
<B>threshold</B> value is 10000 milliohms (10 ohms). If
<I>value</I> is given, then set the lumped resistance threshold
to <I>value</I> milliohms.
<DT> <B>mindelay</B> [<I>value</I>]
<DD> With no value given, returns the current delay time threshold
used to determine if a network will or will not be analyzed for
resistance extraction, in picoseconds. The default
<B>mindelay</B> value is 1ps. If <I>value</I> is given,
then set the delay threshold to <I>value</I> picoseconds.
<DT> <B>minres</B> [<I>value</I>]
<DD> With no value given, returns the current absolute resistance
threshold used to prune small resistances from a network when
simplifying, in milliohms. The default <B>minres</B> value
is 1000 milliohms (1 ohm). If <I>value</I> is given, then set
the absolute resistance threshold to <I>value</I> milliohms.
Note that resistances smaller than <I>value</I> may still
appear in the output netlist if the algorithm is unable to
simplify the network around the resistor.
<DT> <B>simplify</B> [<B>on</B>|<B>off</B>] <DT> <B>simplify</B> [<B>on</B>|<B>off</B>]
<DD> Turn on/off simplification of resistor nets. <DD> Turn on/off simplification of resistor nets.
<DT> <B>extout</B> [<B>on</B>|<B>off</B>] <DT> <B>extout</B> [<B>on</B>|<B>off</B>]
@ -61,9 +42,7 @@ information.
<DT> <B>lumped</B> [<B>on</B>|<B>off</B>] <DT> <B>lumped</B> [<B>on</B>|<B>off</B>]
<DD> Turn on/off writing of updated lumped resistances. <DD> Turn on/off writing of updated lumped resistances.
<DT> <B>silent</B> [<B>on</B>|<B>off</B>] <DT> <B>silent</B> [<B>on</B>|<B>off</B>]
<DD> Turn off/on printing of nets being processed. <DD> Turn off/on printing of net statistics.
<DT> <B>debug</B> [<B>on</B>|<B>off</B>]
<DD> Turn off/on additional diagnostic information.
<DT> <B>skip</B> <I>mask</I> <DT> <B>skip</B> <I>mask</I>
<DD> Don't extract types indicated in the comma-separated list <I>mask</I> <DD> Don't extract types indicated in the comma-separated list <I>mask</I>
<DT> <B>ignore</B> [<I>netname</I>|<B>none</B>] <DT> <B>ignore</B> [<I>netname</I>|<B>none</B>]
@ -90,8 +69,6 @@ information.
<TT>.fh</TT> file. If <I>freq</I> is specified, the file will <TT>.fh</TT> file. If <I>freq</I> is specified, the file will
be customized for <B>fasthenry</B> analysis at the indicated be customized for <B>fasthenry</B> analysis at the indicated
frequency (in Hz). frequency (in Hz).
<DT> <B>tolerance</B> <I>value</I>
<DD> <I>Deprecated!</I> This option is no longer used by extresist.
<DT> <B>help</B> <DT> <B>help</B>
<DD> Print help information <DD> Print help information
</DL> </DL>
@ -102,9 +79,9 @@ information.
<BLOCKQUOTE> <BLOCKQUOTE>
The normal flow through layout extraction into a simulation file The normal flow through layout extraction into a simulation file
treats routes as nonphysical entities, that is, with infinitesimal treats routes as nonphysical entities, that is, with infinitesimal
impedence through the wires. Standard extraction generates "lumped impedence through the wires. Extraction for digital simulation
resistance" values, a single resistance per network node that, along using <B>irsim</B> generates "lumped resistances", a single
with the node capacitance resistance per network node that, along with the node capacitance
to substrate, provides an <I>RC</I> time constant to approximately to substrate, provides an <I>RC</I> time constant to approximately
model the delay from point to point in the network node. The model the delay from point to point in the network node. The
lumped resistance model is inappropriate for analog (i.e., SPICE) lumped resistance model is inappropriate for analog (i.e., SPICE)
@ -119,9 +96,10 @@ information.
Using <B>extresist</B> as a standalone command is a multi-step Using <B>extresist</B> as a standalone command is a multi-step
process. It is first necessary to run <B>extract</B> to get process. It is first necessary to run <B>extract</B> to get
the initial netlist. After a <TT>.ext</TT> file has been generated, the initial netlist.
the <B>extresist</B> command may be run. The output is a file After a <TT>.ext</TT> file has been generated, the
<TT>.res.ext</TT> for each cell in the hierarchy. <B>extresist</B> command may be run. The output is
a file <TT>.res.ext</TT> for each cell in the hierarchy.
Finally, with the option <B>extresist on</B> set, <B>ext2spice</B> Finally, with the option <B>extresist on</B> set, <B>ext2spice</B>
will generate the final, detailed simulation file. <P> will generate the final, detailed simulation file. <P>
@ -132,42 +110,7 @@ information.
magic version 8.3.597 an option <B>extract do resistance</B> magic version 8.3.597 an option <B>extract do resistance</B>
that runs the resistance extraction in sequence with the regular that runs the resistance extraction in sequence with the regular
extraction, producing both the <TT>.ext</TT> and <TT>.res.ext</TT> extraction, producing both the <TT>.ext</TT> and <TT>.res.ext</TT>
files. When <B>extract do resistance</B> is used, there is no need files.
to run <B>extresist</B> as a separate command. However,
<B>extresist</B> may be run prior to <B>extract</B> to set the
options that affect resistance network extraction, such as
<B>extresist threshold</B>, <B>extresist minres</B>, etc. <P>
As of magic version 8.3.653, the <B>extresist tolerance</B> option
is deprecated and has no effect on network resistance extraction.
Instead, extraction is controlled by three main options: <P>
<B>extresist threshold</B> (value in milliohms) sets a cutoff for
considering a network for detailed resistance extraction based on
the lumped resistance estimate (see above). For point-to-point
wires, the lumped resistance is approximately equal to the actual
wire resistance. For branching networks, it will generally be an
over-estimate. The default <B>threshold</B> value is set to
10 ohms. <P>
<B>extresist minres</B> (value in milliohms) sets a cutoff for
individual resistors in the detailed resistor network. Resistors
below this threshold will get pruned out of the network if the
simplification algorithm is able to remove them. The default
<B>minres</B> value is set to 1 ohm. <P>
<B>extresist mindelay</B> (value in picoseconds) sets a cutoff for
considering a network for detailed resistance extraction based on
the end-to-end delay calculated from the lumped resistance
estimate. Once the network has been extracted, the <B>mindelay</B>
value is again evaluated against a revised calculation of the
delay to determine if the extracted detailed network should be
output. The default <B>mindelay<B> value is set to 0, indicating
that only the lumped resistance threshold should be used for
determining when to extract a detailed resistance network.
<B>mindelay</B> may be used in place of <B>threshold</B>, or both
may be used together, in which case a network is only extracted
if both <B>threshold</B> and <B>mindelay</B> are exceeded. <P>
More details on using <B>extresist</B> can be found in More details on using <B>extresist</B> can be found in
<B>magic</B> Tutorial number 8. <B>magic</B> Tutorial number 8.
@ -191,6 +134,6 @@ information.
<TD> <A HREF=commands.html>Return to command index</A> <TD> <A HREF=commands.html>Return to command index</A>
</TR> </TR>
</TABLE> </TABLE>
<P><I>Last updated:</I> May 29, 2026 at 11:33am <P> <P><I>Last updated:</I> October 4, 2021 at 3:32pm <P>
</BODY> </BODY>
</HTML> </HTML>

View File

@ -757,7 +757,7 @@ spcdevHierVisit(
case DEV_FET: case DEV_FET:
if (source == drain) if (source == drain)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, "; "); if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n"); fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n");
} }
break; break;
@ -765,7 +765,7 @@ spcdevHierVisit(
default: default:
if (gate == source) if (gate == source)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, "; "); if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
fprintf(esSpiceF, "** SHORTED DEVICE\n"); fprintf(esSpiceF, "** SHORTED DEVICE\n");
} }
break; break;
@ -1658,7 +1658,7 @@ spcnodeHierVisit(
static char ntmp[MAX_STR_SIZE]; static char ntmp[MAX_STR_SIZE];
EFHNSprintf(ntmp, hierName); EFHNSprintf(ntmp, hierName);
if (esFormat == NGSPICE) fprintf(esSpiceF, " ; "); if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn); fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
} }
cap = cap / 1000; cap = cap / 1000;
@ -1668,14 +1668,14 @@ spcnodeHierVisit(
esSIvalue(esSpiceF, 1.0E-15 * cap); esSIvalue(esSpiceF, 1.0E-15 * cap);
if (!isConnected) if (!isConnected)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, " ;"); if (esFormat == NGSPICE) fprintf(esSpiceF, " $");
fprintf(esSpiceF, " **FLOATING"); fprintf(esSpiceF, " **FLOATING");
} }
fprintf(esSpiceF, "\n"); fprintf(esSpiceF, "\n");
} }
if (node->efnode_attrs && !esNoAttrs) if (node->efnode_attrs && !esNoAttrs)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, " ; "); if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
fprintf(esSpiceF, "**nodeattr %s :",nsn ); fprintf(esSpiceF, "**nodeattr %s :",nsn );
for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next) for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
{ {

View File

@ -2817,7 +2817,7 @@ spcdevVisit(
case DEV_FET: case DEV_FET:
if (source == drain) if (source == drain)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, "; "); if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n"); fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n");
} }
break; break;
@ -2825,7 +2825,7 @@ spcdevVisit(
default: default:
if (gate == source) if (gate == source)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, "; "); if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
fprintf(esSpiceF, "** SHORTED DEVICE\n"); fprintf(esSpiceF, "** SHORTED DEVICE\n");
} }
break; break;
@ -4111,7 +4111,7 @@ spcnodeVisit(
static char ntmp[MAX_STR_SIZE]; static char ntmp[MAX_STR_SIZE];
EFHNSprintf(ntmp, hierName); EFHNSprintf(ntmp, hierName);
if (esFormat == NGSPICE) fprintf(esSpiceF, "; "); if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn); fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
} }
cap = cap / 1000; cap = cap / 1000;
@ -4121,14 +4121,14 @@ spcnodeVisit(
esSIvalue(esSpiceF, 1.0E-15 * cap); esSIvalue(esSpiceF, 1.0E-15 * cap);
if (!isConnected) if (!isConnected)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, " ;"); if (esFormat == NGSPICE) fprintf(esSpiceF, " $");
fprintf(esSpiceF, " **FLOATING"); fprintf(esSpiceF, " **FLOATING");
} }
fprintf(esSpiceF, "\n"); fprintf(esSpiceF, "\n");
} }
if (node->efnode_attrs && !esNoAttrs) if (node->efnode_attrs && !esNoAttrs)
{ {
if (esFormat == NGSPICE) fprintf(esSpiceF, " ; "); if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
fprintf(esSpiceF, "**nodeattr %s :",nsn ); fprintf(esSpiceF, "**nodeattr %s :",nsn );
for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next) for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
{ {

View File

@ -706,11 +706,6 @@ efBuildEquiv(def, nodeName1, nodeName2, resist, isspice)
dev->dev_terms[n].dterm_node = dev->dev_terms[n].dterm_node =
(nn1->efnn_node == NULL) ? (nn1->efnn_node == NULL) ?
nn2->efnn_node : nn1->efnn_node; nn2->efnn_node : nn1->efnn_node;
/* Also check the substrate terminal */
if (dev->dev_subsnode == lostnode)
dev->dev_subsnode = (nn1->efnn_node == NULL) ?
nn2->efnn_node : nn1->efnn_node;
} }
/* If a node has been merged away, make sure that its name */ /* If a node has been merged away, make sure that its name */
@ -1623,128 +1618,12 @@ efConnectionFreeLinkedList(Connection *conn)
} }
} }
/*
* ----------------------------------------------------------------------------
*
* efConnPointFreeLinkedList --
*
* Release memory for linked-list of ConnectionPoint* based on internal
* list at ConnectionPoint->conn_next. 'connpt' argument must be non-NULL.
*
* Results:
* Deallocates linked-list of ConnectionPoint* starting at 'connpt'
*
* Side effects:
* Deallocates one or more connection point record(s).
*
* ----------------------------------------------------------------------------
*/
void
efConnPointFreeLinkedList(ConnectionPoint *connpt)
{
while (connpt)
{
ConnectionPoint *next = connpt->conn_next;
if (connpt->conn_name != NULL)
freeMagic(connpt->conn_name);
freeMagic(connpt);
connpt = next;
}
}
/* /*
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
* *
* efBuildConnect -- * efBuildConnect --
* *
* Process a "connect" line from a .ext file. * Process a "connect" line from a .ext file.
* Creates a record of the area and type of the connection. Since the
* extraction at the point of finding connections no longer knows what
* net in the celldef (if any) is part of the connection, only the
* location and type is preserved, and the cell being connected has to
* be recovered by a search on the celldef's layout. These records are
* used only by "extresist".
*
* Results:
* None.
*
* Side effects:
* Allocates a new connection port record for extresist, and prepends
* it to the list for def.
*
* ----------------------------------------------------------------------------
*/
void
efBuildConnect(def, llx, lly, urx, ury, layerName, upnodeName, downnodeName)
Def *def;
int llx, lly, urx, ury;
char *layerName;
char *upnodeName;
char *downnodeName;
{
int tnew;
ConnectionPoint *connpt;
HashEntry *he;
Use *subuse;
char *hierptr, *qptr, *useid;
/* Can't do anything without a node name to connect to */
if (!strcmp(downnodeName, "\"None\"")) return;
/* "downnodeName" should be hierarchical; stop at the first hierarchical
* divider and use the prefix to find the use being connected to.
* NOTE: This will require dealing with connections that are more than
* one hierarchical level deep (to be done).
*/
useid = downnodeName;
if (*useid == '"') useid++;
hierptr = strchr(useid, '/');
if (hierptr != NULL) *hierptr = '\0';
qptr = strrchr(useid, '"');
if (qptr != NULL) *qptr = '\0';
if (layerName)
tnew = efBuildAddStr(EFLayerNames, &EFLayerNumNames, MAXTYPES, layerName);
else
tnew = 0;
connpt = (ConnectionPoint *)mallocMagic(sizeof(ConnectionPoint));
he = HashFind(&def->def_uses, useid);
subuse = (Use *)HashGetValue(he);
connpt->conn_use = subuse;
connpt->conn_r.r_xbot = llx;
connpt->conn_r.r_ybot = lly;
connpt->conn_r.r_xtop = urx;
connpt->conn_r.r_ytop = ury;
connpt->conn_type = tnew;
if (!strcmp(upnodeName, "\"None\""))
connpt->conn_name = (char *)NULL;
else
{
if (*upnodeName == '"') upnodeName++;
connpt->conn_name = StrDup((char **)NULL, upnodeName);
if ((qptr = strrchr(connpt->conn_name, '"')) != NULL) *qptr = '\0';
}
/* To do: Add "downnodeName" to the ConnectionPoint structure. This
* may not be necessary, as it is only being used by "extresist" which
* is not using the extflat parser.
*/
connpt->conn_next = def->def_connpts;
def->def_connpts = connpt;
}
/*
* ----------------------------------------------------------------------------
*
* efBuildMerge --
*
* Process a "merge" line from a .ext file.
* Creates a connection record for the names 'nodeName1' and * Creates a connection record for the names 'nodeName1' and
* 'nodeName2'. * 'nodeName2'.
* *
@ -1759,7 +1638,7 @@ efBuildConnect(def, llx, lly, urx, ury, layerName, upnodeName, downnodeName)
*/ */
void void
efBuildMerge(def, nodeName1, nodeName2, deltaC, av, ac) efBuildConnect(def, nodeName1, nodeName2, deltaC, av, ac)
Def *def; /* Def to which this connection is to be added */ Def *def; /* Def to which this connection is to be added */
char *nodeName1; /* Name of first node in connection */ char *nodeName1; /* Name of first node in connection */
char *nodeName2; /* Name of other node in connection */ char *nodeName2; /* Name of other node in connection */

View File

@ -122,7 +122,6 @@ EFDone(func)
efConnectionFreeLinkedList(def->def_conns); efConnectionFreeLinkedList(def->def_conns);
efConnectionFreeLinkedList(def->def_caps); efConnectionFreeLinkedList(def->def_caps);
efConnectionFreeLinkedList(def->def_resistors); efConnectionFreeLinkedList(def->def_resistors);
efConnPointFreeLinkedList(def->def_connpts);
free_magic1_t mm1 = freeMagic1_init(); free_magic1_t mm1 = freeMagic1_init();
for (kill = def->def_kills; kill; kill = kill->kill_next) for (kill = def->def_kills; kill; kill = kill->kill_next)
@ -249,7 +248,6 @@ efDefNew(name)
newdef->def_conns = (Connection *) NULL; newdef->def_conns = (Connection *) NULL;
newdef->def_caps = (Connection *) NULL; newdef->def_caps = (Connection *) NULL;
newdef->def_resistors = (Connection *) NULL; newdef->def_resistors = (Connection *) NULL;
newdef->def_connpts = (ConnectionPoint *) NULL;
newdef->def_kills = (Kill *) NULL; newdef->def_kills = (Kill *) NULL;
/* Initialize circular list of nodes */ /* Initialize circular list of nodes */

View File

@ -32,7 +32,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#include "utils/utils.h" #include "utils/utils.h"
#include "extflat/extflat.h" #include "extflat/extflat.h"
#include "extflat/EFint.h" #include "extflat/EFint.h"
#include "textio/textio.h"
/* /*
* ---------------------------------------------------------------------------- * ----------------------------------------------------------------------------
@ -492,13 +491,7 @@ efHierVisitDevs(hc, ca)
{ {
dev = (Dev *)HashGetValue(he); dev = (Dev *)HashGetValue(he);
if (efHierDevKilled(hc, dev, hc->hc_hierName)) if (efHierDevKilled(hc, dev, hc->hc_hierName))
{ continue;
TxError("Error: Device at (%d %d) is connected to one or more"
" eliminated nodes.\n",
dev->dev_rect.r_xbot, dev->dev_rect.r_ybot);
/* Output the device anyway, but something needs fixing */
// continue;
}
const cb_extflat_hiervisitdevs_t ca_hiervisitdevs_proc = (cb_extflat_hiervisitdevs_t) ca->ca_proc; /* FIXME temporary */ const cb_extflat_hiervisitdevs_t ca_hiervisitdevs_proc = (cb_extflat_hiervisitdevs_t) ca->ca_proc; /* FIXME temporary */
if ((*ca_hiervisitdevs_proc)(hc, dev, scale, ca->ca_cdata)) /* @invoke cb_extflat_hiervisitdevs_t */ if ((*ca_hiervisitdevs_proc)(hc, dev, scale, ca->ca_cdata)) /* @invoke cb_extflat_hiervisitdevs_t */

View File

@ -145,8 +145,6 @@ typedef struct conn
#define conn_res conn_value.conn_val_res #define conn_res conn_value.conn_val_res
#define conn_cap conn_value.conn_val_cap #define conn_cap conn_value.conn_val_cap
typedef struct connpoint ConnectionPoint;
/* -------------------------- Defs and uses --------------------------- */ /* -------------------------- Defs and uses --------------------------- */
/* A Def exists for each .ext file */ /* A Def exists for each .ext file */
@ -164,7 +162,6 @@ typedef struct def
/* The following are all NULL-terminated lists */ /* The following are all NULL-terminated lists */
Connection *def_conns; /* Hierarchical connections/adjustments */ Connection *def_conns; /* Hierarchical connections/adjustments */
ConnectionPoint *def_connpts; /* Position of hierarchical connections */
Connection *def_caps; /* Two-terminal capacitors */ Connection *def_caps; /* Two-terminal capacitors */
Connection *def_resistors; /* Two-terminal resistors */ Connection *def_resistors; /* Two-terminal resistors */
Kill *def_kills; /* Used to modify hierarchical structure Kill *def_kills; /* Used to modify hierarchical structure
@ -209,17 +206,6 @@ typedef struct use
#define IsArray(u) ((u)->use_xlo!=(u)->use_xhi || (u)->use_ylo!=(u)->use_yhi) #define IsArray(u) ((u)->use_xlo!=(u)->use_xhi || (u)->use_ylo!=(u)->use_yhi)
/* Connection point structure (used by "extresist") */
typedef struct connpoint
{
Use *conn_use; /* Use being connected to */
Rect conn_r; /* Area, edge, or point of connection */
int conn_type; /* A tile type at the connection */
char *conn_name; /* Top level name of node, or NULL */
struct connpoint *conn_next; /* Next connection point in list */
} ConnectionPoint;
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/* Structure passed down during hierarchical searching */ /* Structure passed down during hierarchical searching */
@ -321,9 +307,7 @@ extern void CapHashSetValue();
extern DevParam *efGetDeviceParams(); extern DevParam *efGetDeviceParams();
extern void efBuildNode(); extern void efBuildNode();
extern void efConnectionFreeLinkedList(Connection *conn); extern void efConnectionFreeLinkedList(Connection *conn);
extern void efConnPointFreeLinkedList(ConnectionPoint *conn);
extern void efBuildConnect(); extern void efBuildConnect();
extern void efBuildMerge();
extern void efBuildResistor(); extern void efBuildResistor();
extern void efBuildCap(); extern void efBuildCap();
extern HierContext *EFFlatBuildOneLevel(); extern HierContext *EFFlatBuildOneLevel();

View File

@ -275,11 +275,15 @@ readfile:
efBuildCap(def, argv[1], argv[2], (double) cap); efBuildCap(def, argv[1], argv[2], (double) cap);
break; break;
/* connect useid llx lly urx ury type "node" ... */ /* subcap node capacitance */
case CONNECT: case SUBCAP:
efBuildConnect(def, atoi(argv[1]), atoi(argv[2]), cap = cscale*atoCap(argv[2]);
atoi(argv[3]), atoi(argv[4]), argv[5], efAdjustSubCap(def, argv[1], cap);
argv[6], argv[7]); break;
/* equiv node1 node2 */
case EQUIV:
efBuildEquiv(def, argv[1], argv[2], resist, isspice);
break; break;
/* replaces "fet" (below) */ /* replaces "fet" (below) */
@ -333,41 +337,6 @@ readfile:
r.r_xtop = (int)(0.5 + (float)atoi(argv[5]) * locScale); r.r_xtop = (int)(0.5 + (float)atoi(argv[5]) * locScale);
r.r_ytop = (int)(0.5 + (float)atoi(argv[6]) * locScale); r.r_ytop = (int)(0.5 + (float)atoi(argv[6]) * locScale);
if (!strcmp(argv[2], "Short"))
{
/* Device name "Short" is a reserved name indicating
* that the device does not get output but acts as a
* short between the first two terminals. Consequently,
* it acts like an "equiv" statement. However, unlike
* regular "equiv" statements, it should always merge
* the nodes, so do not pass "resist" to efBuildEquiv().
*/
int argstart = 7;
/* "Short" devices should not have parameters, but just in
* case, skip over any that are found.
*/
while (strchr(argv[argstart], '=') != NULL) argstart++;
/* Tricky---Since "Short" devices are treated like "equiv"
* statements, then when doing full R-C extraction, it's
* important *not* to merge the nodes when reading the
* .ext file, but only when reading the .res.ext file.
* Otherwise the wrong nodes may get merged. "resist" is
* TRUE when "ext2spice extresist on" is selected, and
* DoResist is set to FALSE when the .res.ext file is
* opened for reading.
*/
if (argstart + 4 >= argc)
efReadError("Bad terminal description for Short device\n");
else if ((!resist) || (resist && (!(DoResist))))
{
efBuildEquiv(def, argv[argstart + 1], argv[argstart + 4],
FALSE, isspice);
}
break;
}
if (efBuildDevice(def, (char)n, argv[2], &r, argc - 7, &argv[7]) != 0) if (efBuildDevice(def, (char)n, argv[2], &r, argc - 7, &argv[7]) != 0)
{ {
efReadError("Incomplete terminal description for device\n"); efReadError("Incomplete terminal description for device\n");
@ -375,11 +344,6 @@ readfile:
} }
break; break;
/* equiv node1 node2 */
case EQUIV:
efBuildEquiv(def, argv[1], argv[2], resist, isspice);
break;
/* for backwards compatibility */ /* for backwards compatibility */
/* fet type xlo ylo xhi yhi area perim substrate GATE T1 T2 ... */ /* fet type xlo ylo xhi yhi area perim substrate GATE T1 T2 ... */
case FET: case FET:
@ -409,7 +373,7 @@ readfile:
*/ */
cap = (argc > 3) ? atoCap(argv[3]) * cscale : 0; cap = (argc > 3) ? atoCap(argv[3]) * cscale : 0;
efBuildMerge(def, argv[1], argv[2], (double)cap, &argv[4], argc - 4); efBuildConnect(def, argv[1], argv[2], (double)cap, &argv[4], argc - 4);
break; break;
/* node name R C x y layer a1 p1 a2 p2 ... [ attrs ] */ /* node name R C x y layer a1 p1 a2 p2 ... [ attrs ] */
@ -485,12 +449,6 @@ resistChanged:
} }
break; break;
/* subcap node capacitance */
case SUBCAP:
cap = cscale*atoCap(argv[2]);
efAdjustSubCap(def, argv[1], cap);
break;
/* use def use-id T0 .. T5 */ /* use def use-id T0 .. T5 */
case USE: case USE:
efBuildUse(def, argv[1], argv[2], efBuildUse(def, argv[1], argv[2],

View File

@ -25,9 +25,9 @@
*/ */
typedef enum typedef enum
{ {
ABSTRACT, ADJUST, ATTR, CAP, CONNECT, DEVICE, DIST, EQUIV, FET, KILLNODE, ABSTRACT, ADJUST, ATTR, CAP, DEVICE, DIST, EQUIV, FET, KILLNODE, MERGE,
MERGE, NODE, PARAMETERS, PORT, PRIMITIVE, RESISTOR, RESISTCLASS, RNODE, NODE, PARAMETERS, PORT, PRIMITIVE, RESISTOR, RESISTCLASS, RNODE, SCALE,
SCALE, SUBCAP, SUBSTRATE, TECH, TIMESTAMP, USE, VERSION, EXT_STYLE SUBCAP, SUBSTRATE, TECH, TIMESTAMP, USE, VERSION, EXT_STYLE
} Key; } Key;
static const struct static const struct
@ -42,7 +42,6 @@ keyTable[] =
{"adjust", ADJUST, 4}, {"adjust", ADJUST, 4},
{"attr", ATTR, 8}, {"attr", ATTR, 8},
{"cap", CAP, 4}, {"cap", CAP, 4},
{"connect", CONNECT, 7},
{"device", DEVICE, 11}, /* effectively replaces "fet" */ {"device", DEVICE, 11}, /* effectively replaces "fet" */
{"distance", DIST, 4}, {"distance", DIST, 4},
{"equiv", EQUIV, 3}, {"equiv", EQUIV, 3},

View File

@ -629,8 +629,6 @@ extSetResist(reg)
for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++) for (n = 0; n < ExtCurStyle->exts_numResistClasses; n++)
{ {
ResValue resnew, restot;
reg->nreg_pa[n].pa_area = area = extResistArea[n]; reg->nreg_pa[n].pa_area = area = extResistArea[n];
reg->nreg_pa[n].pa_perim = perim = extResistPerim[n]; reg->nreg_pa[n].pa_perim = perim = extResistPerim[n];
if (area > 0 && perim > 0) if (area > 0 && perim > 0)
@ -641,15 +639,8 @@ extSetResist(reg)
if (v < 0) s = 0; else s = sqrt(v); if (v < 0) s = 0; else s = sqrt(v);
fperim = (float) perim; fperim = (float) perim;
resnew = (fperim + s) / (fperim - s) * reg->nreg_resist += (fperim + s) / (fperim - s)
ExtCurStyle->exts_resistByResistClass[n]; * ExtCurStyle->exts_resistByResistClass[n];
restot = reg->nreg_resist + resnew;
/* Check for integer overflow. There is no point in trying
* to accommodate huge resistance values for an estimate.
* The value just saturates at the maximum integer value.
*/
if (restot > 0) reg->nreg_resist = restot;
} }
/* Reset for the next pass */ /* Reset for the next pass */
@ -731,8 +722,7 @@ extOutputNodes(nodeList, outFile)
/* Check if this node is the substrate */ /* Check if this node is the substrate */
if (reg == glob_subsnode) if (reg == glob_subsnode)
{ {
intR = (reg->nreg_resist + rround) / ExtCurStyle->exts_resistScale; fprintf(outFile, "substrate \"%s\" 0 0", text);
fprintf(outFile, "substrate \"%s\" %d 0", text, intR);
} }
else else
{ {
@ -821,14 +811,8 @@ extOutputNodes(nodeList, outFile)
* For a net to be shorted to itself is not an error. * For a net to be shorted to itself is not an error.
* NOTE: Potentially the unique name could be removed * NOTE: Potentially the unique name could be removed
* here and save ext2spice the trouble. * here and save ext2spice the trouble.
*
* Also: If "extresist" is being run in the same
* pass, then don't print an error, because
* electrical shorts are meaningless in an R-C
* extraction.
*/ */
if ((portname != NULL) && if ((portname != NULL) &&
(!(ExtOptions & EXT_DORESISTANCE)) &&
(ll->ll_attr == LL_PORTATTR) && (ll->ll_attr == LL_PORTATTR) &&
(strcmp(ll->ll_label->lab_text, portname))) (strcmp(ll->ll_label->lab_text, portname)))
{ {
@ -1126,7 +1110,7 @@ ExtSortTerminals(tran, ll)
do do
{ {
changed = 0; changed = 0;
for (nsd = 0; nsd < tran->tr_nterm-1; nsd++) for( nsd = 0; nsd < tran->tr_nterm-1; nsd++ )
{ {
p1 = &(tran->tr_termpos[nsd]); p1 = &(tran->tr_termpos[nsd]);
p2 = &(tran->tr_termpos[nsd+1]); p2 = &(tran->tr_termpos[nsd+1]);
@ -1171,17 +1155,14 @@ ExtSortTerminals(tran, ll)
* but S,D attributes are not that common so it should not matter * but S,D attributes are not that common so it should not matter
* that much -- Stefanos 5/96 */ * that much -- Stefanos 5/96 */
for (lp = ll; lp; lp = lp->ll_next) for ( lp = ll ; lp ; lp = lp->ll_next )
if (lp->ll_attr == nsd) if ( lp->ll_attr == nsd ) lp->ll_attr = LL_SORTATTR ;
lp->ll_attr = LL_SORTATTR; else if ( lp->ll_attr == nsd+1 ) lp->ll_attr = nsd ;
else if (lp->ll_attr == nsd + 1) for ( lp = ll ; lp ; lp = lp->ll_next )
lp->ll_attr = nsd; if ( lp->ll_attr == LL_SORTATTR ) lp->ll_attr = nsd+1;
for (lp = ll; lp; lp = lp->ll_next)
if (lp->ll_attr == LL_SORTATTR)
lp->ll_attr = nsd + 1;
} }
} }
while (changed); while( changed );
} }
/* /*
@ -2671,6 +2652,30 @@ extOutputDevices(def, transList, outFile)
if (!strcmp(devptr->exts_deviceName, "Ignore")) if (!strcmp(devptr->exts_deviceName, "Ignore"))
continue; continue;
/* Model type "Short" in the techfile indicates a device */
/* to short across the first two nodes (the gate and the */
/* source). This solves the specific issue of a transistor */
/* extended drain where the drain is a resistor but the */
/* resistor is part of the model and should not be output. */
if (!strcmp(devptr->exts_deviceName, "Short"))
{
fprintf(outFile, "equiv ");
/* To do: Use parameters to specify which terminals */
/* are shorted. */
/* gate */
node = (NodeRegion *)ExtGetRegion(reg->treg_tile, reg->treg_dinfo);
fprintf(outFile, "\"%s\" ", extNodeName((LabRegion *)node));
/* First non-gate terminal */
node = (NodeRegion *)extTransRec.tr_termnode[0];
fprintf(outFile, "\"%s\"\n", extNodeName((LabRegion *)node));
continue;
}
/* Original-style FET record backward compatibility */ /* Original-style FET record backward compatibility */
if (devptr->exts_deviceClass != DEV_FET) if (devptr->exts_deviceClass != DEV_FET)
fprintf(outFile, "device "); fprintf(outFile, "device ");
@ -5130,7 +5135,7 @@ extFindNodes(def, clipArea, subonly)
pNum = ExtCurStyle->exts_globSubstratePlane; pNum = ExtCurStyle->exts_globSubstratePlane;
/* Does the type set of this plane intersect the substrate types? */ /* Does the type set of this plane intersect the substrate types? */
if ((pNum != -1) && TTMaskIntersect(&DBPlaneTypes[pNum], &subsTypesNonSpace)) if (TTMaskIntersect(&DBPlaneTypes[pNum], &subsTypesNonSpace))
{ {
arg.fra_pNum = pNum; arg.fra_pNum = pNum;
DBSrPaintClient((Tile *) NULL, def->cd_planes[pNum], DBSrPaintClient((Tile *) NULL, def->cd_planes[pNum],

View File

@ -76,19 +76,18 @@ void extHeader();
*/ */
Plane * Plane *
ExtCell(def, outName, isTop) ExtCell(def, outName, doLength)
CellDef *def; /* Cell being extracted */ CellDef *def; /* Cell being extracted */
char *outName; /* Name of output file; if NULL, derive from def name */ char *outName; /* Name of output file; if NULL, derive from def name */
bool isTop; /* If TRUE, cell is the top level cell */ bool doLength; /* If TRUE, extract pathlengths from drivers to
* receivers (the names are stored in ExtLength.c).
* Should only be TRUE for the root cell in a
* hierarchy.
*/
{ {
char *filename; char *filename;
FILE *f = NULL; FILE *f = NULL;
Plane *savePlane; Plane *savePlane;
bool noextract;
/* If marked abstract, then don't extract the cell */
DBPropGet(def, "noextract", &noextract);
if (noextract) return extPrepSubstrate(def);
/* Incremental extraction: If the cell is marked for no extraction, /* Incremental extraction: If the cell is marked for no extraction,
* then just prepare the substrate plane and return it to the caller. * then just prepare the substrate plane and return it to the caller.
@ -112,7 +111,7 @@ ExtCell(def, outName, isTop)
} }
extNumErrors = extNumWarnings = 0; extNumErrors = extNumWarnings = 0;
savePlane = extCellFile(def, f, isTop); savePlane = extCellFile(def, f, doLength);
if (f != NULL) fclose(f); if (f != NULL) fclose(f);
if (extNumErrors > 0 || extNumWarnings > 0) if (extNumErrors > 0 || extNumWarnings > 0)
@ -472,10 +471,13 @@ ExtRevertSubstrate(def, savePlane)
*/ */
Plane * Plane *
extCellFile(def, f, isTop) extCellFile(def, f, doLength)
CellDef *def; /* Def to be extracted */ CellDef *def; /* Def to be extracted */
FILE *f; /* Output to this file */ FILE *f; /* Output to this file */
bool isTop; /* TRUE if the cell is the top level cell */ bool doLength; /* TRUE if we should extract driver-receiver path
* length information for this cell (see ExtCell
* for more details).
*/
{ {
NodeRegion *reg; NodeRegion *reg;
Plane *saveSub; Plane *saveSub;
@ -486,19 +488,8 @@ extCellFile(def, f, isTop)
/* If "extract do unique" was specified, then make labels in the /* If "extract do unique" was specified, then make labels in the
* cell unique. * cell unique.
*/ */
if (ExtOptions & EXT_DOUNIQUE) if (ExtOptions & EXT_DOUNIQUE)
{ extUniqueCell(def, EXT_UNIQ_TEMP);
if (ExtOptions & EXT_DOUNIQNOTOPPORTS)
{
if (isTop)
extUniqueCell(def, EXT_UNIQ_TEMP_NOPORTS);
else
extUniqueCell(def, EXT_UNIQ_TEMP);
}
else
extUniqueCell(def, EXT_UNIQ_TEMP);
}
/* Prep any isolated substrate areas */ /* Prep any isolated substrate areas */
if (ExtOptions & EXT_DOEXTRESIST) if (ExtOptions & EXT_DOEXTRESIST)
@ -528,7 +519,7 @@ extCellFile(def, f, isTop)
ExtResetTiles(def, CLIENTDEFAULT); ExtResetTiles(def, CLIENTDEFAULT);
/* Final pass: extract length information if desired */ /* Final pass: extract length information if desired */
if (!SigInterruptPending && isTop && (ExtOptions & EXT_DOLENGTH)) if (!SigInterruptPending && doLength && (ExtOptions & EXT_DOLENGTH))
extLength(extParentUse, f); extLength(extParentUse, f);
UndoEnable(); UndoEnable();

View File

@ -113,91 +113,23 @@ bool extTestNMInteract(Tile *t1, TileType di1, Tile *t2, TileType di2)
return DBTestNMInteract(&rect1, tt1, t2, di2, FALSE); return DBTestNMInteract(&rect1, tt1, t2, di2, FALSE);
} }
/*
*----------------------------------------------------------------------
* extHierSubInteractFunc --
*
* Simple callback function for extHierSubShielfFunc() that halts
* the search if any type connecting to substrate is found in
* the area.
*
* Results: 1 to stop the search.
*
* Side effects: None.
*
*----------------------------------------------------------------------
*/
int
extHierSubInteractFunc(tile, dinfo, clientdata)
Tile *tile; /* unused */
TileType dinfo; /* unused */
ClientData clientdata; /* unused */
{
return 1;
}
/* /*
*---------------------------------------------------------------------- *----------------------------------------------------------------------
* extHierSubShieldFunc -- * extHierSubShieldFunc --
* *
* Callback function for extHierSubstrate() that halts the search * Simple callback function for extHierSubstrate() that halts the
* if any substrate shield type is found in the search area. To * search if any substrate shield type is found in the search area
* avoid flagging substrate shields that overlap the search area
* but do not interact with the cell, check the area of the
* substrate shield for device types that connect to substrate
* (ExtCurStyle->exts_subsDevTypes). If something is found, then
* return 1 immediately to stop the search.
*
* Results: 1 if an interacting substrate shield is found, 0 otherwise.
*
* Side effects: None.
* *
*---------------------------------------------------------------------- *----------------------------------------------------------------------
*/ */
int int
extHierSubShieldFunc(tile, dinfo, use) extHierSubShieldFunc(tile, dinfo, clientdata)
Tile *tile; Tile *tile; /* (unused) */
TileType dinfo; TileType dinfo; /* (unused) */
CellUse *use; ClientData clientdata; /* (unused) */
{ {
Rect r, rsub; return 1;
int pNum;
TileType ttype;
CellDef *subdef;
Transform t;
if (IsSplit(tile))
{
ttype = TiGetLeftType(tile);
if (!TTMaskHasType(&ExtCurStyle->exts_globSubstrateShieldTypes, ttype))
ttype = TiGetRightType(tile);
}
else
ttype = TiGetTypeExact(tile);
TiToRect(tile, &r);
/* Convert area of tile to the coordinates of the cell "subdef", which is
* a child of the cell containing "tile".
*/
subdef = use->cu_def;
GeoInvertTrans(&use->cu_transform, &t);
GeoTransRect(&t, &r, &rsub);
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
if (TTMaskIntersect(&DBPlaneTypes[pNum], &ExtCurStyle->exts_subsDevTypes))
{
if (DBSrPaintNMArea((Tile *)NULL,
subdef->cd_planes[pNum], dinfo, &rsub,
&ExtCurStyle->exts_subsDevTypes,
extHierSubInteractFunc, (ClientData)NULL) == 1)
return 1;
}
}
return 0;
} }
/* /*
@ -297,7 +229,7 @@ extHierSubstrate(ha, use, x, y)
if (DBSrPaintArea((Tile *) NULL, if (DBSrPaintArea((Tile *) NULL,
def->cd_planes[pNum], &subArea, def->cd_planes[pNum], &subArea,
&ExtCurStyle->exts_globSubstrateShieldTypes, &ExtCurStyle->exts_globSubstrateShieldTypes,
extHierSubShieldFunc, PTR2CD(use)) != 0) extHierSubShieldFunc, (ClientData)NULL) != 0)
{ {
freeMagic(nodeList); freeMagic(nodeList);
ExtResetTiles(use->cu_def, CLIENTDEFAULT); ExtResetTiles(use->cu_def, CLIENTDEFAULT);
@ -349,13 +281,6 @@ extHierSubstrate(ha, use, x, y)
nn->nn_next = node2->node_names->nn_next; nn->nn_next = node2->node_names->nn_next;
node2->node_names->nn_next = node1->node_names; node2->node_names->nn_next = node1->node_names;
node2->node_len += node1->node_len; node2->node_len += node1->node_len;
if (node2->node_ports)
{
ExtConnList *nport;
for (nport = node2->node_ports; nport && nport->r_next;
nport = nport->r_next);
if (nport) nport->r_next = node1->node_ports;
}
freeMagic((char *)node1); freeMagic((char *)node1);
} }
else else
@ -369,13 +294,6 @@ extHierSubstrate(ha, use, x, y)
nn->nn_next = node1->node_names; nn->nn_next = node1->node_names;
node1->node_names = node2->node_names; node1->node_names = node2->node_names;
node1->node_len += node2->node_len; node1->node_len += node2->node_len;
if (node1->node_ports)
{
ExtConnList *nport;
for (nport = node1->node_ports; nport && nport->r_next;
nport = nport->r_next);
if (nport) nport->r_next = node2->node_ports;
}
freeMagic((char *)node2); freeMagic((char *)node2);
} }
} }
@ -581,13 +499,6 @@ extHierConnectFunc1(oneTile, dinfo, ha)
nn->nn_next = node2->node_names->nn_next; nn->nn_next = node2->node_names->nn_next;
node2->node_names->nn_next = node1->node_names; node2->node_names->nn_next = node1->node_names;
node2->node_len += node1->node_len; node2->node_len += node1->node_len;
if (node2->node_ports)
{
ExtConnList *nport;
for (nport = node2->node_ports; nport && nport->r_next;
nport = nport->r_next);
if (nport) nport->r_next = node1->node_ports;
}
freeMagic((char *) node1); freeMagic((char *) node1);
} }
else else
@ -603,13 +514,6 @@ extHierConnectFunc1(oneTile, dinfo, ha)
nn->nn_next = node1->node_names; nn->nn_next = node1->node_names;
node1->node_names = node2->node_names; node1->node_names = node2->node_names;
node1->node_len += node2->node_len; node1->node_len += node2->node_len;
if (node1->node_ports)
{
ExtConnList *nport;
for (nport = node1->node_ports; nport && nport->r_next;
nport = nport->r_next);
if (nport) nport->r_next = node2->node_ports;
}
freeMagic((char *) node2); freeMagic((char *) node2);
} }
} }
@ -619,48 +523,6 @@ extHierConnectFunc1(oneTile, dinfo, ha)
} }
/* /*
*------------------------------------------------------------------------
*
* extHierFindTopNode --
*
* Simple callback function used in extHierConnectFunc2() to retrieve
* the node name of a node in the CellDef being extracted at a specific
* point. If there is no node at that point (indicating that there is
* paint in a subcell at that location but no paint in the top level
* cell) then return NULL.
*
* Returns:
* 1 if a node is found, otherwise 0 to keep the search going.
*
* Side effects:
* A pointer to the node is returned in the clientData field.
*
*------------------------------------------------------------------------
*/
int
extHierFindTopNode(Tile *tile,
TileType dinfo,
ExtRegion **nreg)
{
ExtRegion *tireg;
tireg = (ExtRegion *)ExtGetRegion(tile, dinfo);
if ((ClientData)tireg == CLIENTDEFAULT)
{
*nreg = (ExtRegion *)0;
return 0;
}
else
{
*nreg = tireg;
return 1;
}
}
/*
*------------------------------------------------------------------------
*
* extHierConnectFunc2 -- * extHierConnectFunc2 --
* *
* Called once for each tile 'cum' in extHierCumFlat->et_use->cu_def * Called once for each tile 'cum' in extHierCumFlat->et_use->cu_def
@ -676,8 +538,6 @@ extHierFindTopNode(Tile *tile,
* if the types of ha->hierOneTile and 'cum' connect. * if the types of ha->hierOneTile and 'cum' connect.
* Otherwise, if the tiles actually overlap (as opposed * Otherwise, if the tiles actually overlap (as opposed
* to merely abut), mark it with feedback as an error. * to merely abut), mark it with feedback as an error.
*
*------------------------------------------------------------------------
*/ */
int int
@ -748,53 +608,6 @@ extHierConnectFunc2(cum, dinfo, ha)
if (node1 != node2) if (node1 != node2)
{ {
ExtConnList *newport;
int pNum;
if (ExtOptions & EXT_DOEXTRESIST)
{
NodeRegion *topnode = NULL;
/* Record the area of connection for both nodes in their
* respective coordinate systems, and the name of the
* cell use to which the connection is made.
*/
newport = (ExtConnList *)mallocMagic(sizeof(ExtConnList));
newport->r_r = r;
newport->r_type = ttype;
newport->r_useid = ha->ha_subUse->cu_id;
/* Find a node at the given location in et_lookNames (the
* original CellDef being extracted). If there is no node
* in the def itself then the entry is NULL.
*/
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
if (TTMaskHasType(&DBPlaneTypes[pNum], ttype))
{
/* Make sure that the rect is not zero area */
if (r.r_xtop == r.r_xbot)
{
r.r_xtop++;
r.r_xbot--;
}
if (r.r_ytop == r.r_ybot)
{
r.r_ytop++;
r.r_ybot--;
}
DBSrPaintArea((Tile *)NULL,
ha->ha_cumFlat.et_lookNames->cd_planes[pNum],
&r, &DBConnectTbl[ttype], extHierFindTopNode,
(ClientData)PTR2CD(&topnode));
newport->r_upnode = PTR2CD(topnode);
newport->r_downnode = PTR2CD(node2->node_names);
}
}
}
if (node1->node_len < node2->node_len) if (node1->node_len < node2->node_len)
{ {
/* /*
@ -809,12 +622,6 @@ extHierConnectFunc2(cum, dinfo, ha)
node2->node_names->nn_next = node1->node_names; node2->node_names->nn_next = node1->node_names;
node2->node_len += node1->node_len; node2->node_len += node1->node_len;
freeMagic((char *) node1); freeMagic((char *) node1);
if (ExtOptions & EXT_DOEXTRESIST)
{
newport->r_next = node2->node_ports;
node2->node_ports = newport;
}
} }
else else
{ {
@ -830,12 +637,6 @@ extHierConnectFunc2(cum, dinfo, ha)
node1->node_names = node2->node_names; node1->node_names = node2->node_names;
node1->node_len += node2->node_len; node1->node_len += node2->node_len;
freeMagic((char *) node2); freeMagic((char *) node2);
if (ExtOptions & EXT_DOEXTRESIST)
{
newport->r_next = node1->node_ports;
node1->node_ports = newport;
}
} }
} }
} }
@ -932,13 +733,6 @@ extHierConnectFunc3(cum, dinfo, ha)
nn->nn_next = node2->node_names->nn_next; nn->nn_next = node2->node_names->nn_next;
node2->node_names->nn_next = node1->node_names; node2->node_names->nn_next = node1->node_names;
node2->node_len += node1->node_len; node2->node_len += node1->node_len;
if (node2->node_ports)
{
ExtConnList *nport;
for (nport = node2->node_ports; nport && nport->r_next;
nport = nport->r_next);
if (nport) nport->r_next = node1->node_ports;
}
freeMagic((char *) node1); freeMagic((char *) node1);
} }
else else
@ -954,13 +748,6 @@ extHierConnectFunc3(cum, dinfo, ha)
nn->nn_next = node1->node_names; nn->nn_next = node1->node_names;
node1->node_names = node2->node_names; node1->node_names = node2->node_names;
node1->node_len += node2->node_len; node1->node_len += node2->node_len;
if (node1->node_ports)
{
ExtConnList *nport;
for (nport = node1->node_ports; nport && nport->r_next;
nport = nport->r_next);
if (nport) nport->r_next = node2->node_ports;
}
freeMagic((char *) node2); freeMagic((char *) node2);
} }
} }
@ -1129,7 +916,6 @@ extOutputConns(table, outf)
NodeName *nfirst; NodeName *nfirst;
HashSearch hs; HashSearch hs;
HashEntry *he; HashEntry *he;
ExtConnList *nport, *npnext;
HashStartSearch(&hs); HashStartSearch(&hs);
while ((he = HashNext(table, &hs))) while ((he = HashNext(table, &hs)))
@ -1162,6 +948,7 @@ extOutputConns(table, outf)
node->node_pa[n].pa_area, node->node_pa[n].pa_area,
node->node_pa[n].pa_perim); node->node_pa[n].pa_perim);
fprintf(outf, "\n"); fprintf(outf, "\n");
nn->nn_node = (Node *) NULL; /* Processed */ nn->nn_node = (Node *) NULL; /* Processed */
/* Subsequent merges */ /* Subsequent merges */
@ -1173,23 +960,6 @@ extOutputConns(table, outf)
} }
} }
nn->nn_node = (Node *) NULL; nn->nn_node = (Node *) NULL;
for (nport = node->node_ports; nport;)
{
LabRegion *lreg = (LabRegion *)CD2PTR(nport->r_upnode);
NodeName *nn2 = (NodeName *)CD2PTR(nport->r_downnode);
npnext = nport->r_next;
/* Output port positions */
fprintf(outf, "connect %d %d %d %d %s \"%s\" \"%s\"\n",
nport->r_r.r_xbot, nport->r_r.r_ybot,
nport->r_r.r_xtop, nport->r_r.r_ytop,
DBTypeShortName(nport->r_type),
(lreg == (LabRegion *)NULL) ? "None" :
extNodeName(lreg),
(nn2) ? nn2->nn_name : "None");
freeMagic((char *)nport);
nport = npnext;
}
freeMagic((char *) node); freeMagic((char *) node);
} }
freeMagic((char *) nfirst); freeMagic((char *) nfirst);
@ -1235,7 +1005,6 @@ extHierNewNode(he)
node->node_names = nn; node->node_names = nn;
node->node_cap = (CapValue) 0; node->node_cap = (CapValue) 0;
node->node_len = 1; node->node_len = 1;
node->node_ports = (ExtConnList *)NULL;
for (n = 0; n < nclasses; n++) for (n = 0; n < nclasses; n++)
node->node_pa[n].pa_perim = node->node_pa[n].pa_area = 0; node->node_pa[n].pa_perim = node->node_pa[n].pa_area = 0;
HashSetValue(he, (char *) nn); HashSetValue(he, (char *) nn);

View File

@ -272,7 +272,7 @@ extInterSubtreeElement(use, trans, x, y, r)
int int
extInterSubtreeTile(tile, dinfo, cxp) extInterSubtreeTile(tile, dinfo, cxp)
Tile *tile; Tile *tile;
TileType dinfo; /* (unused) */ TileType dinfo;
TreeContext *cxp; TreeContext *cxp;
{ {
SearchContext newscx; SearchContext newscx;
@ -343,9 +343,8 @@ extInterOverlapSubtree(scx)
*/ */
int int
extInterOverlapTile(tile, dinfo, cxp) extInterOverlapTile(tile, cxp)
Tile *tile; Tile *tile;
TileType dinfo; /* (unused) */
TreeContext *cxp; TreeContext *cxp;
{ {
SearchContext *scx = cxp->tc_scx; SearchContext *scx = cxp->tc_scx;

View File

@ -473,19 +473,6 @@ ExtUnique(rootUse, option)
/* Fix up bounding boxes if they've changed */ /* Fix up bounding boxes if they've changed */
DBFixMismatch(); DBFixMismatch();
/* Because the "extract unique" does the same thing as "extract do unique"
* but the options may be different, disable "extract do unique" when
* "extract unique" is run, on the assumption that no user would
* intentionally use both methods. If "do unique" was set and got
* disabled, then flag a warning.
*/
if (ExtOptions & EXT_DOUNIQUE)
{
ExtOptions &= ~EXT_DOUNIQUE;
TxPrintf("Warning: Extract option \"do unique\" disabled because "
"\"extract unique\" was run.\n");
}
/* Mark all defs as being unvisited */ /* Mark all defs as being unvisited */
(void) DBCellSrDefs(0, extDefInitFunc, (ClientData) 0); (void) DBCellSrDefs(0, extDefInitFunc, (ClientData) 0);

View File

@ -54,14 +54,12 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
/* C99 compat */ /* C99 compat */
#include "drc/drc.h" #include "drc/drc.h"
/*
* If "exactinteractions" is defined, use an experimental algorithm
* for finding exact interaction areas. Currently it doesn't work
* too well, so it is disabled.
*/
/* #define exactinteractions */
#ifdef exactinteractions #ifdef exactinteractions
/*
* If "exactinteractions" is defined, we use an experimental algorithm
* for finding exact interaction areas. Currently it doesn't work too
* well, so we leave it turned off.
*/
int ExtInterBloat = 10; int ExtInterBloat = 10;
#endif /* exactinteractions */ #endif /* exactinteractions */

View File

@ -836,7 +836,6 @@ extTechStyleInit(style)
style->exts_sidePlanes = style->exts_overlapPlanes = 0; style->exts_sidePlanes = style->exts_overlapPlanes = 0;
TTMaskZero(&style->exts_deviceMask); TTMaskZero(&style->exts_deviceMask);
TTMaskZero(&style->exts_subsDevTypes);
style->exts_activeTypes = DBAllButSpaceAndDRCBits; style->exts_activeTypes = DBAllButSpaceAndDRCBits;
for (r = 0; r < NP; r++) for (r = 0; r < NP; r++)
@ -2356,9 +2355,6 @@ ExtTechLine(sectionName, argc, argv)
} }
TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1); TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1);
if (!TTMaskIsZero(&subsTypes))
TTMaskSetMask(&ExtCurStyle->exts_subsDevTypes, &types1);
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
if (TTMaskHasType(&types1, t)) if (TTMaskHasType(&types1, t))
{ {
@ -2384,7 +2380,6 @@ ExtTechLine(sectionName, argc, argv)
devptr->exts_next = ExtCurStyle->exts_device[t]; devptr->exts_next = ExtCurStyle->exts_device[t];
ExtCurStyle->exts_device[t] = devptr; ExtCurStyle->exts_device[t] = devptr;
#ifdef ARIEL #ifdef ARIEL
{ {
int z; int z;
@ -2872,8 +2867,6 @@ ExtTechLine(sectionName, argc, argv)
} }
TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1); TTMaskSetMask(&ExtCurStyle->exts_deviceMask, &types1);
if (!TTMaskIsZero(&subsTypes))
TTMaskSetMask(&ExtCurStyle->exts_subsDevTypes, &types1);
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++) for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
{ {

View File

@ -66,8 +66,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
* label. This way, the unique label form can be used by the * label. This way, the unique label form can be used by the
* extraction code but labels (and port indexes) can be reverted * extraction code but labels (and port indexes) can be reverted
* afterward, and no permanent change is made to the circuit. * afterward, and no permanent change is made to the circuit.
* Option EXT_UNIQ_TEMP_NOPORTS is a combination of EXT_UNIQ_TEMP and
* EXT_UNIQ_NOPORTS.
* *
* Results: * Results:
* Returns the number of warnings generated. * Returns the number of warnings generated.
@ -228,9 +226,8 @@ extMakeUnique(def, ll, lreg, lregList, labelHash, option)
text = ll->ll_label->lab_text; text = ll->ll_label->lab_text;
if (option == EXT_UNIQ_ALL || option == EXT_UNIQ_TEMP) if (option == EXT_UNIQ_ALL || option == EXT_UNIQ_TEMP)
goto makeUnique; goto makeUnique;
else if ((option == EXT_UNIQ_NOPORTS || option == EXT_UNIQ_NOTOPPORTS || else if ((option == EXT_UNIQ_NOPORTS || option == EXT_UNIQ_NOTOPPORTS)
option == EXT_UNIQ_TEMP_NOPORTS) && && !(ll->ll_label->lab_flags & PORT_DIR_MASK))
!(ll->ll_label->lab_flags & PORT_DIR_MASK))
goto makeUnique; goto makeUnique;
cpend = strchr(text, '\0'); cpend = strchr(text, '\0');
@ -329,8 +326,7 @@ makeUnique:
saveLab = *lab; saveLab = *lab;
/* Flag this label as having been modified */ /* Flag this label as having been modified */
if ((option == EXT_UNIQ_TEMP) || (option == EXT_UNIQ_TEMP_NOPORTS)) if (option == EXT_UNIQ_TEMP) flags |= LABEL_UNIQUE;
flags |= LABEL_UNIQUE;
DBRemoveLabel(def, lab); DBRemoveLabel(def, lab);
DBPutFontLabel(def, &saveLab.lab_rect, DBPutFontLabel(def, &saveLab.lab_rect,

View File

@ -76,7 +76,6 @@ extern const char * const extDevTable[];
#define EXT_DOLABELCHECK 0x040 /* Check for connections by label */ #define EXT_DOLABELCHECK 0x040 /* Check for connections by label */
#define EXT_DOALIASES 0x080 /* Output all node aliases */ #define EXT_DOALIASES 0x080 /* Output all node aliases */
#define EXT_DOEXTRESIST 0x200 /* Do full R-C extraction */ #define EXT_DOEXTRESIST 0x200 /* Do full R-C extraction */
#define EXT_DOUNIQNOTOPPORTS 0x400 /* Ignore top cell ports w/EXT_DOUNIQUE */
extern int ExtOptions; /* Bitmask of above */ extern int ExtOptions; /* Bitmask of above */
extern char *ExtLocalPath; /* If non-NULL, location to write .ext files */ extern char *ExtLocalPath; /* If non-NULL, location to write .ext files */
@ -87,7 +86,6 @@ extern char *ExtLocalPath; /* If non-NULL, location to write .ext files */
#define EXT_UNIQ_NOPORTS 2 #define EXT_UNIQ_NOPORTS 2
#define EXT_UNIQ_NOTOPPORTS 3 #define EXT_UNIQ_NOTOPPORTS 3
#define EXT_UNIQ_TEMP 4 /* Used only with "EXT_DOUNIQUE" */ #define EXT_UNIQ_TEMP 4 /* Used only with "EXT_DOUNIQUE" */
#define EXT_UNIQ_TEMP_NOPORTS 5 /* Used only with "EXT_DOUNIQUE" */
extern bool ExtTechLine(); extern bool ExtTechLine();
extern void ExtTechInit(); extern void ExtTechInit();

View File

@ -626,13 +626,6 @@ typedef struct extstyle
*/ */
TileTypeBitMask exts_deviceConn[NT]; TileTypeBitMask exts_deviceConn[NT];
/*
* List of device types which connect to a substrate type. This
* distinguishes between devices that make a connection to substrate
* (e.g., FETs) and those that don't (e.g., MiM caps, metal resistors).
*/
TileTypeBitMask exts_subsDevTypes;
/* /*
* Set of types to be considered for extraction. Types not in * Set of types to be considered for extraction. Types not in
* this list cannot be nodes (e.g., implant layers) * this list cannot be nodes (e.g., implant layers)
@ -988,7 +981,6 @@ typedef struct node
* in the list is the "official" node name. * in the list is the "official" node name.
*/ */
int node_len; /* Number of entries in node_names */ int node_len; /* Number of entries in node_names */
ExtConnList *node_ports; /* List of areas that connect to other cells */
CapValue node_cap; /* Capacitance to substrate */ CapValue node_cap; /* Capacitance to substrate */
PerimArea node_pa[1]; /* Dummy; each node actually has PerimArea node_pa[1]; /* Dummy; each node actually has
* ExtCurStyle->exts_numResistClasses * ExtCurStyle->exts_numResistClasses

View File

@ -42,29 +42,12 @@ LIBS += ${GR_LIBS} ${READLINE_LIBS} -lm ${LD_EXTRA_LIBS} \
CLEANS += tclmagic${SHDLIB_EXT} libtclmagic${SHDLIB_EXT}.a proto.magicrc CLEANS += tclmagic${SHDLIB_EXT} libtclmagic${SHDLIB_EXT}.a proto.magicrc
ifeq (${MAKE_WASM},1) ifeq (${MAKE_WASM},1)
# magicWasm.c bootstraps the embedded Tcl interp by calling Tcl_CreateInterp / magic: magic.js
# Tcl_Init before tclStubsPtr is initialised. With -DUSE_TCL_STUBS those calls
# expand to (*tclStubsPtr->...)() and dereference a NULL stubs pointer; so
# this one file must be compiled with DFLAGS_NOSTUB (= DFLAGS without
# -DUSE_TCL_STUBS). All other files keep using stubs.
magicWasm.o: magicWasm.c
@echo --- compiling magic/magicWasm.o '(no Tcl stubs)'
${RM} magicWasm.o
${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS_NOSTUB} -c magicWasm.c
magic.js: lib${MODULE}.o ${EXTRA_LIBS} magic.js: lib${MODULE}.o ${EXTRA_LIBS}
@echo --- building main magic WASM @echo --- building main magic WASM
${RM} magic.js magic.wasm ${RM} magic.js magic.wasm
ifneq (${TCL_LIB_DIR},)
# TCL variant: pull in the main TCL archive (LIB_SPECS_NOSTUB → -ltcl9.x) and
# the stub-bootstrap archive (-ltclstub). Both are required: magic's objects
# use USE_TCL_STUBS macros (resolved by tclStubsPtr from libtclstub.a), and
# tclStubsPtr itself must point into the real TCL implementation (libtcl9.x.a).
${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} lib${MODULE}.o ${EXTRA_LIBS} -o magic.js ${LIBS} ${LIB_SPECS_NOSTUB} -L${TCL_LIB_DIR} -ltclstub
else
${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} lib${MODULE}.o ${EXTRA_LIBS} -o magic.js ${LIBS} ${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} lib${MODULE}.o ${EXTRA_LIBS} -o magic.js ${LIBS}
endif endif
endif
main: magic proto.magicrc main: magic proto.magicrc
ifeq (${MAKE_WASM},1) ifeq (${MAKE_WASM},1)
@ -110,14 +93,3 @@ $(DESTDIR)${INSTALL_SYSDIR}/magicps.pro: magicps.pro
${CP} magicps.pro $(DESTDIR)${INSTALL_SYSDIR}/magicps.pro ${CP} magicps.pro $(DESTDIR)${INSTALL_SYSDIR}/magicps.pro
include ${MAGICDIR}/rules.mak include ${MAGICDIR}/rules.mak
ifeq (${MAKE_WASM},1)
# rules.mak defines `${MODULE}` (= `magic`) with a recipe that links a native
# executable without the TCL libraries. For the WASM build the real artifact
# is `magic.js` (+ `magic.wasm`), so override the target to be a phony alias
# that just rebuilds magic.js. Must come after the include so make uses this
# recipe instead of rules.mak's.
.PHONY: magic
magic: magic.js
@:
endif

View File

@ -59,14 +59,8 @@ main(int argc, char *argv[])
* here, nor its format. It is updated by the Makefile in this directory. * here, nor its format. It is updated by the Makefile in this directory.
* *
* The version string originates at the top of scripts/config. * The version string originates at the top of scripts/config.
*
* Under MAGIC_WRAPPER (Tcl-embedded builds), tclmagic.c owns these globals;
* defining them here as well would produce duplicate-symbol errors when both
* objects end up in the same binary (as in the WASM build).
*/ */
#ifndef MAGIC_WRAPPER
char *MagicVersion = MAGIC_VERSION; char *MagicVersion = MAGIC_VERSION;
char *MagicRevision = MAGIC_REVISION; char *MagicRevision = MAGIC_REVISION;
char *MagicCompileTime = MAGIC_BUILDDATE; char *MagicCompileTime = MAGIC_BUILDDATE;
#endif

View File

@ -8,10 +8,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef MAGIC_WRAPPER
#include "tcltk/tclmagic.h"
#endif
#include "utils/main.h" #include "utils/main.h"
#include "utils/magic.h" #include "utils/magic.h"
#include "utils/paths.h" #include "utils/paths.h"
@ -42,16 +38,6 @@ magicWasmEnsureCadRoot(void)
return 0; return 0;
} }
#ifdef MAGIC_WRAPPER
/* Forward decl — Tclmagic_Init bootstraps the Tcl interpreter (registers
* the magic::initialize command and calls Tcl_InitStubs(), which sets
* tclStubsPtr). Without this, any Tcl_X macro dereferences a NULL stubs
* pointer at runtime (crashes the wasm). The actual magic:: commands
* (magic::load, magic::gds, etc.) are registered separately by
* TclmagicRegisterCommands() after magicMainInit() populates the clients. */
extern int Tclmagic_Init(Tcl_Interp *interp);
#endif
EMSCRIPTEN_KEEPALIVE int EMSCRIPTEN_KEEPALIVE int
magic_wasm_init(void) magic_wasm_init(void)
{ {
@ -67,52 +53,7 @@ magic_wasm_init(void)
if (magicWasmEnsureCadRoot() != 0) if (magicWasmEnsureCadRoot() != 0)
return -1; return -1;
#ifdef MAGIC_WRAPPER return magicMainInit(5, argv);
/* In wrapper mode, magic's code (and our PaExpand path expansion) reaches
* for `magicinterp` to resolve $env vars via Tcl_GetVar. In the normal
* Linux flow Tclmagic_Init() is called by tclsh after dlopen(); here we
* embed the interp directly, so we have to bootstrap it before
* magicMainInit() runs anything that might touch Tcl.
*
* Note: we deliberately avoid TxError here in MAGIC_WRAPPER mode
* TxError flushes via Tcl_EvalEx through tclStubsPtr, which only becomes
* non-NULL after Tclmagic_Init -> Tcl_InitStubs. So early errors go
* straight to stderr. */
if (magicinterp == NULL)
{
Tcl_Interp *interp = Tcl_CreateInterp();
if (interp == NULL)
{
fprintf(stderr, "magic_wasm_init: Tcl_CreateInterp returned NULL\n");
return -1;
}
/* Tcl_Init loads /init.tcl from the Tcl library directory; in our
* embedded VFS that script isn't shipped, so failure here is expected
* and non-fatal the interpreter itself is still usable for embedded
* evaluation, which is all we need. */
(void)Tcl_Init(interp);
consoleinterp = interp;
if (Tclmagic_Init(interp) != TCL_OK)
{
fprintf(stderr, "magic_wasm_init: Tclmagic_Init failed: %s\n",
Tcl_GetStringResult(interp));
return -1;
}
}
#endif
{
static int commandsRegistered = FALSE;
int rc = magicMainInit(5, argv);
#ifdef MAGIC_WRAPPER
if (rc == 0 && !commandsRegistered)
{
TclmagicRegisterCommands(magicinterp);
commandsRegistered = TRUE;
}
#endif
return rc;
}
} }
EMSCRIPTEN_KEEPALIVE int EMSCRIPTEN_KEEPALIVE int
@ -134,20 +75,13 @@ magic_wasm_run_command(const char *command)
TxSetPoint(GrScreenRect.r_xtop / 2, GrScreenRect.r_ytop / 2, TxSetPoint(GrScreenRect.r_xtop / 2, GrScreenRect.r_ytop / 2,
WIND_UNKNOWN_WINDOW); WIND_UNKNOWN_WINDOW);
#ifdef MAGIC_WRAPPER
/* In wrapper mode the command is Tcl. Evaluate it via the magic interp;
* the magic backend is reachable through ::magic:: ensemble commands. */
if (magicinterp == NULL)
return -1;
return Tcl_EvalEx(magicinterp, command, -1, 0);
#else
return TxDispatchString(command, FALSE); return TxDispatchString(command, FALSE);
#endif
} }
EMSCRIPTEN_KEEPALIVE int EMSCRIPTEN_KEEPALIVE int
magic_wasm_source_file(const char *path) magic_wasm_source_file(const char *path)
{ {
FILE *f;
int status; int status;
status = magic_wasm_init(); status = magic_wasm_init();
@ -157,33 +91,27 @@ magic_wasm_source_file(const char *path)
if ((path == NULL) || (*path == '\0')) if ((path == NULL) || (*path == '\0'))
return -1; return -1;
#ifdef MAGIC_WRAPPER f = PaOpen((char *)path, "r", (char *)NULL, ".", (char *)NULL,
/* In wrapper mode the file contains Tcl; evaluate it through the (char **)NULL);
* Tcl interpreter so that magic:: commands are dispatched via if (f == NULL)
* _tcl_dispatch just like magic_wasm_run_command does for strings. */
if (magicinterp == NULL)
return -1;
return Tcl_EvalFile(magicinterp, path);
#else
{ {
FILE *f = PaOpen((char *)path, "r", (char *)NULL, ".", (char *)NULL, TxError("Unable to open command file \"%s\".\n", path);
(char **)NULL); return -1;
if (f == NULL)
{
TxError("Unable to open command file \"%s\".\n", path);
return -1;
}
/* Set the current point to the center of the screen so that
* WindSendCommand routes all commands from the file to the layout
* window client. Without this, commands arrive with point (0,0)
* and end up in the border/windClient context where most are unknown. */
TxSetPoint(GrScreenRect.r_xtop / 2, GrScreenRect.r_ytop / 2,
WIND_UNKNOWN_WINDOW);
TxDispatch(f);
fclose(f);
return 0;
} }
#endif
/* Set the current point to the centre of the screen so that
* WindSendCommand routes all commands from the file to the layout
* window client, just as magic_wasm_run_command does for single
* commands. Without this, commands arrive with point (0,0) and
* end up in the border/windClient context where most commands are
* unknown.
*/
TxSetPoint(GrScreenRect.r_xtop / 2, GrScreenRect.r_ytop / 2,
WIND_UNKNOWN_WINDOW);
TxDispatch(f);
fclose(f);
return 0;
} }
EMSCRIPTEN_KEEPALIVE void EMSCRIPTEN_KEEPALIVE void

8
npm/.gitignore vendored
View File

@ -1,13 +1,5 @@
# Variant build outputs (regenerable via npm/build.sh).
tcl/magic.js
tcl/magic.wasm
notcl/magic.js
notcl/magic.wasm
# Pre-restructure artifacts (just in case anyone still has them locally).
magic.js magic.js
magic.wasm magic.wasm
*.tgz *.tgz
node_modules/ node_modules/
package-lock.json package-lock.json

View File

@ -7,43 +7,16 @@ WebAssembly as a headless library. Runs in Node.js, browsers, and Web Workers
Use it to programmatically read and write `.mag`, `.gds`, `.cif`, `.ext`, and Use it to programmatically read and write `.mag`, `.gds`, `.cif`, `.ext`, and
SPICE netlists; run DRC; extract parasitics — anywhere JavaScript runs. SPICE netlists; run DRC; extract parasitics — anywhere JavaScript runs.
The package ships two variants:
| Variant | Entry point | Description |
|---------|-------------|-------------|
| **notcl** (default) | `magic-vlsi-wasm` | Standalone — no Tcl interpreter. Commands are plain Magic command strings. |
| **tcl** | `magic-vlsi-wasm/tcl` | Embeds a full Tcl 9 interpreter. Commands are evaluated as Tcl; Magic commands are available as the `::magic::` ensemble. |
## Install ## Install
The package is published to GitHub Packages. Add the following to your
project's `.npmrc` so npm knows where to find it:
```
@rtimothyedwards:registry=https://npm.pkg.github.com
```
Then install:
```bash ```bash
npm install @rtimothyedwards/magic-vlsi-wasm npm install magic-vlsi-wasm
```
If the package is private or you hit a 401, authenticate with a GitHub
[personal access token](https://github.com/settings/tokens) that has the
`read:packages` scope:
```
//npm.pkg.github.com/:_authToken=YOUR_TOKEN
@rtimothyedwards:registry=https://npm.pkg.github.com
``` ```
Requires Node.js 18 or newer. Requires Node.js 18 or newer.
## Quick start ## Quick start
### Default variant (no Tcl)
```js ```js
import createMagic from 'magic-vlsi-wasm'; import createMagic from 'magic-vlsi-wasm';
@ -62,23 +35,6 @@ runCommand('gds write /work/inv');
const gdsBytes = FS.readFile('/work/inv.gds'); const gdsBytes = FS.readFile('/work/inv.gds');
``` ```
### TCL variant
```js
import createMagic from 'magic-vlsi-wasm/tcl';
const { runCommand, FS } = await createMagic();
// Pure Tcl works directly
runCommand('set x 42');
runCommand('puts $tcl_version');
// Magic commands are available as the ::magic:: ensemble
runCommand('magic::tech load scmos');
runCommand('magic::load /work/inv');
runCommand('magic::gds write /work/inv');
```
The `scmos` technology family (`scmos`, `minimum`, `nmos`, ...) is embedded in The `scmos` technology family (`scmos`, `minimum`, `nmos`, ...) is embedded in
the WASM binary and available out of the box — those names work without the WASM binary and available out of the box — those names work without
writing any tech file. To use a custom technology, write its `.tech` file into writing any tech file. To use a custom technology, write its `.tech` file into
@ -166,59 +122,21 @@ If you want to rebuild the WASM module yourself, see
The short version: The short version:
```bash ```bash
bash npm/build.sh # both variants, debug build bash npm/build.sh # debug build, copies magic.js + magic.wasm into npm/
bash npm/build.sh --variant=notcl # default variant only (faster) bash npm/build.sh --release # optimized
bash npm/build.sh --variant=tcl # TCL variant only bash npm/build.sh --test # build + run tests
bash npm/build.sh --release # optimized (-O2, no debug symbols) bash npm/build.sh --pack # build + produce magic-vlsi-wasm-<version>.tgz
bash npm/build.sh --test # build + run tests
bash npm/build.sh --pack # build + produce magic-vlsi-wasm-<version>.tgz
``` ```
You will need an activated [emsdk](https://emscripten.org/docs/getting_started/downloads.html) You will need an activated [emsdk](https://emscripten.org/docs/getting_started/downloads.html)
on your PATH. If you pass `EMSDK_DIR=/path/to/emsdk`, `build.sh` sources checkout (Magic pins emsdk `3.1.56` — see the comment in `npm/build.sh`).
`emsdk_env.sh` for you.
### TCL variant: cloning the TCL source tree
The TCL variant links against a static WASM build of
[tcltk/tcl](https://github.com/tcltk/tcl). `build.sh` clones the source tree
automatically into `build-tcl-wasm/tcl` on the first run and builds it
out-of-source in the same directory — everything stays under `build-tcl-wasm/`
(which is gitignored), so nothing outside it is touched. Subsequent runs reuse
the existing clone and build.
```bash
# Override the TCL version or source repository
TCL_REF=core-9-0-3 bash npm/build.sh --variant=tcl
TCL_REPO_URL=https://github.com/tcltk/tcl.git bash npm/build.sh --variant=tcl
```
CI always resolves the latest stable `core-9-0-x` tag automatically. To build
against a specific version, set `TCL_REF` in the environment.
## Versioning
Published versions follow the scheme `{MAJOR}.{MINOR}.{PATCH}0{YYYYMMDD}+git{SHA}`,
for example `8.3.799020261231+git01234cde`.
The date is embedded directly into the patch number (separated by a leading
zero for readability). This means:
- Versions are orderable numerically — a later build date is always a higher
version number within the same patch series.
- Security or bugfix releases for `8.3.799` can be inserted as later dates
(`8.3.799020270101`, `8.3.799020270201`, …) without bumping the patch number.
- Users who want to lock to the `8.3.799` series and receive only those patches
can use the range `<=8.3.799900000000` or `<8.3.8000000000000`.
- `~8.3.799` matches all `8.3.*` versions (broader than the 799 series alone).
The `+git…` suffix is build metadata — it is ignored by npm for version
comparison and range matching. It exists purely for traceability.
## Limitations ## Limitations
- Headless only. There is no display driver, so commands that draw to a - Headless only. There is no display driver, so commands that draw to a
window (`view`, `findbox`, interactive macros) are no-ops. window (`view`, `findbox`, interactive macros) are no-ops.
- WASM memory starts at 32 MB and grows as needed. Very large GDS imports
may need `INITIAL_MEMORY` bumped (rebuild required).
- Single-threaded. WASM modules are not thread-safe — create one instance - Single-threaded. WASM modules are not thread-safe — create one instance
per worker. per worker.

View File

@ -2,13 +2,9 @@
# Build Magic WASM and copy artifacts into this npm/ directory. # Build Magic WASM and copy artifacts into this npm/ directory.
# #
# Usage: # Usage:
# npm/build.sh [--variant=<tcl|notcl|both>] [--release] [--test] [--pack] # npm/build.sh [--release] [--test] [--pack]
# #
# --variant=tcl Build only the TCL-embedded variant → npm/tcl/ # --release Omit debug symbols (-g).
# --variant=notcl Build only the plain (no Tcl/Tk) variant → npm/notcl/
# --variant=both Build both (default)
#
# --release Omit debug symbols (-g) and build with -O2.
# --test Run `npm run test` after copying artifacts. # --test Run `npm run test` after copying artifacts.
# --pack Run `npm pack` after copying artifacts (and tests, if given). # --pack Run `npm pack` after copying artifacts (and tests, if given).
# #
@ -18,36 +14,23 @@
# node, npm — only required for --test / --pack # node, npm — only required for --test / --pack
# #
# Environment: # Environment:
# EMSDK_DIR Path to an activated emsdk checkout. # EMSDK_DIR Path to an activated emsdk checkout.
# If set, emsdk_env.sh is sourced from there. # If set, emsdk_env.sh is sourced from there.
# If unset, emcc must already be on PATH (e.g. sourced externally). # If unset, emcc must already be on PATH (e.g. sourced externally).
# TCL_REF git ref (tag/branch/SHA) of tcltk/tcl to build for the TCL
# variant. Default: main. (CI pins the latest stable tag.)
# TCL_REPO_URL git URL to clone tcltk/tcl from. Default: the upstream repo.
set -euo pipefail set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")" REPO_ROOT="$(dirname "$SCRIPT_DIR")"
# The TCL variant builds a static WASM Tcl from a pristine clone of tcltk/tcl.
# Both the clone (tcl/) and the out-of-source build artifacts live entirely
# under build-tcl-wasm/ (gitignored), so nothing outside it is ever touched.
TCL_BUILD_DIR="${TCL_BUILD_DIR:-$REPO_ROOT/build-tcl-wasm}"
TCL_SRC_DIR="$TCL_BUILD_DIR/tcl"
TCL_WASM_PREFIX="$TCL_BUILD_DIR/install"
OPT_RELEASE=0 OPT_RELEASE=0
OPT_TEST=0 OPT_TEST=0
OPT_PACK=0 OPT_PACK=0
OPT_VARIANT=both
for arg in "$@"; do for arg in "$@"; do
case "$arg" in case "$arg" in
--release) OPT_RELEASE=1 ;; --release) OPT_RELEASE=1 ;;
--test) OPT_TEST=1 ;; --test) OPT_TEST=1 ;;
--pack) OPT_PACK=1 ;; --pack) OPT_PACK=1 ;;
--variant=tcl) OPT_VARIANT=tcl ;;
--variant=notcl) OPT_VARIANT=notcl ;;
--variant=both) OPT_VARIANT=both ;;
*) echo "Unknown option: $arg" >&2; exit 1 ;; *) echo "Unknown option: $arg" >&2; exit 1 ;;
esac esac
done done
@ -93,119 +76,53 @@ sed_strip_cr() {
sed 's/\r//' "$file" > "$tmp" && cat "$tmp" > "$file" && rm "$tmp" sed 's/\r//' "$file" > "$tmp" && cat "$tmp" > "$file" && rm "$tmp"
} }
# --- clean -------------------------------------------------------------------
cd "$REPO_ROOT"
# Only distclean if there's something to clean. A stale `|| true` here would
# hide real failures (e.g. broken toolchain) on a fresh checkout.
if [ -f defs.mak ]; then
emmake make distclean || true
fi
rm -f defs.mak database/database.h
# --- configure ---------------------------------------------------------------
# Strip Windows CRLF line endings (no-op on Linux-native files).
sed_strip_cr configure
find scripts/ -type f -print0 | while IFS= read -r -d '' f; do sed_strip_cr "$f"; done
if [ $OPT_RELEASE -eq 1 ]; then if [ $OPT_RELEASE -eq 1 ]; then
EXTRA_CFLAGS="-O2" EXTRA_CFLAGS=" -O2"
else else
EXTRA_CFLAGS="-g" EXTRA_CFLAGS=" -g"
fi fi
# --- TCL fork: clone and prebuild (TCL variant only) ------------------------ CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1${EXTRA_CFLAGS}" \
# Uses TCL_REPO_URL and TCL_REF from the environment (both have defaults). emconfigure ./configure \
# The source is cloned into build-tcl-wasm/tcl on the first run and checked --without-cairo --without-opengl --without-x --without-tk --without-tcl \
# out at the requested ref. Because this clone is private to the build dir, --disable-readline --disable-compression \
# we manage its HEAD freely — no user-supplied tree is ever mutated. --host=asmjs-unknown-emscripten \
# --target=asmjs-unknown-emscripten
# The actual WASM build runs out-of-source in $TCL_BUILD_DIR, driven by
# toolchains/emscripten/build-tcl-wasm.sh.
ensure_tcl_built() {
: "${TCL_REPO_URL:=https://github.com/tcltk/tcl.git}"
: "${TCL_REF:=main}"
if [ ! -d "$TCL_SRC_DIR/.git" ]; then cat toolchains/emscripten/defs.mak >> defs.mak
echo "=== cloning $TCL_REPO_URL into $TCL_SRC_DIR ==="
mkdir -p "$TCL_BUILD_DIR"
git -c core.autocrlf=false clone "$TCL_REPO_URL" "$TCL_SRC_DIR"
fi
( cd "$TCL_SRC_DIR" # --- build -------------------------------------------------------------------
current_sha=$(git rev-parse HEAD 2>/dev/null || echo "") emmake make depend
if [ "$current_sha" != "$TCL_REF" ]; then emmake make -j"$(ncpu)" modules libs
git fetch --quiet origin emmake make techs
git checkout --quiet --detach "$TCL_REF" emmake make mains
fi
echo "Using TCL at $(git rev-parse HEAD) ($TCL_REPO_URL)"
)
# Build TCL for WASM if it hasn't been built yet. The presence of # --- copy artifacts ----------------------------------------------------------
# tclConfig.sh in the install prefix is the canonical "TCL is built" marker. cp magic/magic.js "$SCRIPT_DIR/"
if [ ! -f "$TCL_WASM_PREFIX/lib/tclConfig.sh" ]; then cp magic/magic.wasm "$SCRIPT_DIR/"
echo "=== building TCL for WASM into $TCL_BUILD_DIR (one-time) ===" echo "Copied magic.js and magic.wasm to npm/"
bash "$REPO_ROOT/toolchains/emscripten/build-tcl-wasm.sh" \
--src="$TCL_SRC_DIR" --out="$TCL_BUILD_DIR"
fi
}
# --- build a single variant --------------------------------------------------
# Each variant gets a fresh configure run because the two configurations
# select different code paths (MAGIC_WRAPPER on/off, MAGIC_NO_TK, link flags),
# so the object cache from one variant is not compatible with the other.
build_variant() {
local variant=$1
local out_dir="$SCRIPT_DIR/$variant"
echo
echo "==============================================================="
echo "=== building variant: $variant"
echo "==============================================================="
cd "$REPO_ROOT"
# Full clean — distclean removes the generated defs.mak and module objects.
if [ -f defs.mak ]; then
emmake make distclean || true
fi
rm -f defs.mak database/database.h
# Strip Windows CRLF line endings (no-op on Linux-native files).
sed_strip_cr configure
find scripts/ -type f -print0 | while IFS= read -r -d '' f; do sed_strip_cr "$f"; done
if [ "$variant" = "tcl" ]; then
ensure_tcl_built
CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1 ${EXTRA_CFLAGS}" \
emconfigure ./configure \
--without-cairo --without-opengl --without-x --without-tk \
--with-tcl="$TCL_WASM_PREFIX/lib" \
--with-tclincls="$TCL_WASM_PREFIX/include" \
--with-tcllibs="$TCL_WASM_PREFIX/lib" \
--disable-readline --disable-compression \
--host=asmjs-unknown-emscripten \
--target=asmjs-unknown-emscripten
else
CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1 ${EXTRA_CFLAGS}" \
emconfigure ./configure \
--without-cairo --without-opengl --without-x \
--without-tk --without-tcl \
--disable-readline --disable-compression \
--host=asmjs-unknown-emscripten \
--target=asmjs-unknown-emscripten
fi
cat toolchains/emscripten/defs.mak >> defs.mak
emmake make depend
emmake make -j"$(ncpu)" modules libs
emmake make techs
emmake make mains
mkdir -p "$out_dir"
cp magic/magic.js "$out_dir/"
cp magic/magic.wasm "$out_dir/"
echo "Copied magic.js + magic.wasm into npm/$variant/"
}
case "$OPT_VARIANT" in
tcl|notcl) build_variant "$OPT_VARIANT" ;;
both) build_variant notcl
build_variant tcl ;;
esac
# --- optional test ----------------------------------------------------------- # --- optional test -----------------------------------------------------------
# Runs the same smoke test that CI runs (see .github/workflows/main-wasm.yml). # Runs the same smoke test that CI runs (see .github/workflows/main.yml).
# Run in a subshell so the cd does not leak into the --pack step below, which
# relies on the script's working directory being unchanged.
if [ $OPT_TEST -eq 1 ]; then if [ $OPT_TEST -eq 1 ]; then
( cd "$SCRIPT_DIR" && npm run test && npm run test:tcl ) cd "$SCRIPT_DIR"
npm run test
fi fi
# --- optional pack ----------------------------------------------------------- # --- optional pack -----------------------------------------------------------

View File

@ -1,60 +0,0 @@
// all-tcl.js — Run the full test suite against the TCL variant.
//
// Covers all non-TCL tests (extract, gds, drc, cif) run through the
// Tcl interpreter, plus a PCell generation test that is TCL-only.
//
// Usage: node examples/all-tcl.js
import { run as runExtract } from './extract-tcl.js';
import { run as runGds } from './gds-tcl.js';
import { run as runDrc } from './drc-tcl.js';
import { run as runCif } from './cif-tcl.js';
import { run as runPcell } from './pcell.js';
import { reportError } from './helpers-tcl.js';
const PAD = 9;
async function test(name, fn) {
process.stdout.write(` ${name.padEnd(PAD)} `);
try {
const result = await fn();
console.log(`PASS ${formatResult(name, result)}`);
return true;
} catch (e) {
console.log(`FAIL ${e.message ?? e}`);
// Full stack to stderr so CI shows the wasm-function offsets, while the
// one-line summary above stays readable. Other tests still run.
reportError(e);
return false;
}
}
function formatResult(name, r) {
if (!r) return '';
switch (name) {
case 'extract': return [r.ext, r.spice].filter(Boolean).map(p => p.split(/[\\/]/).pop()).join(', ');
case 'gds': return `${r.outPath.split(/[\\/]/).pop()} (${r.bytes} B)`;
case 'cif': return `${r.outPath.split(/[\\/]/).pop()} (${r.bytes} B)`;
case 'drc': return r.violations != null ? `${r.violations} violation${r.violations !== 1 ? 's' : ''}` : '';
case 'pcell': return Object.entries(r).map(([n, { bytes }]) => `${n}.gds (${bytes} B)`).join(', ');
default: return '';
}
}
console.log('\nMagic WASM TCL variant — test suite\n');
const suite = [
['extract', runExtract],
['gds', runGds],
['drc', runDrc],
['cif', runCif],
['pcell', runPcell],
];
const passed = [];
for (const [name, fn] of suite) {
passed.push(await test(name, fn));
}
const ok = passed.filter(Boolean).length;
console.log(`\n${ok}/${suite.length} passed`);
process.exit(ok === suite.length ? 0 : 1);

View File

@ -5,7 +5,6 @@ import { run as runExtract } from './extract.js';
import { run as runGds } from './gds.js'; import { run as runGds } from './gds.js';
import { run as runDrc } from './drc.js'; import { run as runDrc } from './drc.js';
import { run as runCif } from './cif.js'; import { run as runCif } from './cif.js';
import { reportError } from './helpers.js';
const PAD = 9; const PAD = 9;
@ -17,9 +16,6 @@ async function test(name, fn) {
return true; return true;
} catch (e) { } catch (e) {
console.log(`FAIL ${e.message ?? e}`); console.log(`FAIL ${e.message ?? e}`);
// Full stack to stderr so CI shows the wasm-function offsets, while the
// one-line summary above stays readable. Other tests still run.
reportError(e);
return false; return false;
} }
} }

View File

@ -1,28 +0,0 @@
// cif-tcl.js — CIF export via the TCL variant.
import { createMagic, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers-tcl.js';
import { writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
const { magic } = await createMagic();
const { FS } = magic;
const { tech: techName, cell } = loadCell(FS, tech, magFile);
magic.runScript(loadScript('cif-tcl.tcl', techName, cell));
mkdirSync(outputDir, { recursive: true });
const data = vfsRead(FS, `/work/${cell}.cif`);
if (!data) throw new Error(`CIF export failed: /work/${cell}.cif not created`);
const outPath = resolve(outputDir, `${cell}.cif`);
writeFileSync(outPath, data);
return { outPath, bytes: data.length };
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { outPath, bytes } = await run().catch(e => { reportError(e); process.exit(1); });
console.log(`\ncif: ${outPath} (${bytes} bytes)`);
console.log('Done.');
}

View File

@ -1,3 +0,0 @@
magic::tech load __TECH__
magic::load /work/__CELL__
magic::cif write /work/__CELL__

View File

@ -2,7 +2,7 @@
// //
// Usage: node examples/cif.js [magFile [tech [outputDir]]] // Usage: node examples/cif.js [magFile [tech [outputDir]]]
import { createMagic, vfsRead, loadCell, loadScript, import { createMagic, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers.js'; DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
import { writeFileSync, mkdirSync } from 'node:fs'; import { writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path'; import { resolve } from 'node:path';
@ -24,7 +24,7 @@ export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDi
} }
if (process.argv[1] === fileURLToPath(import.meta.url)) { if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { outPath, bytes } = await run().catch(e => { reportError(e); process.exit(1); }); const { outPath, bytes } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
console.log(`\ncif: ${outPath} (${bytes} bytes)`); console.log(`\ncif: ${outPath} (${bytes} bytes)`);
console.log('Done.'); console.log('Done.');
} }

View File

@ -1,28 +0,0 @@
// drc-tcl.js — DRC check via the TCL variant.
import { createMagic, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, reportError } from './helpers-tcl.js';
import { fileURLToPath } from 'node:url';
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH } = {}) {
const output = [];
const { magic } = await createMagic({
onPrint: msg => { output.push(msg); console.log('[magic-tcl]', msg); },
onPrintErr: msg => { output.push(msg); console.error('[magic-tcl]', msg); },
});
const { FS } = magic;
const { tech: techName, cell } = loadCell(FS, tech, magFile);
magic.runScript(loadScript('drc-tcl.tcl', techName, cell));
const summary = output.find(l => /Total DRC errors/i.test(l));
const match = summary?.match(/(\d+)/);
const violations = match ? parseInt(match[1], 10) : null;
return { violations, output };
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { violations } = await run().catch(e => { reportError(e); process.exit(1); });
console.log(`\nDRC violations: ${violations ?? '(count not found in output)'}`);
console.log('Done.');
}

View File

@ -1,4 +0,0 @@
magic::tech load __TECH__
magic::load /work/__CELL__
magic::drc catchup
magic::drc count total

View File

@ -2,7 +2,7 @@
// //
// Usage: node examples/drc.js [magFile [tech]] // Usage: node examples/drc.js [magFile [tech]]
import { createMagic, loadCell, loadScript, import { createMagic, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, reportError } from './helpers.js'; DEFAULT_TECH, DEFAULT_MAG } from './helpers.js';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH } = {}) { export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH } = {}) {
@ -25,7 +25,7 @@ export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH } = {}) {
} }
if (process.argv[1] === fileURLToPath(import.meta.url)) { if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { violations } = await run().catch(e => { reportError(e); process.exit(1); }); const { violations } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
console.log(`\nDRC violations: ${violations ?? '(count not found in output)'}`); console.log(`\nDRC violations: ${violations ?? '(count not found in output)'}`);
console.log('Done.'); console.log('Done.');
} }

View File

@ -1,39 +0,0 @@
// extract-tcl.js — RC extraction via the TCL variant.
import { createMagic, vfsWrite, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers-tcl.js';
import { writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
const { magic } = await createMagic();
const { FS } = magic;
const { tech: techName, cell } = loadCell(FS, tech, magFile);
magic.runScript(loadScript('extract-tcl.tcl', techName, cell));
mkdirSync(outputDir, { recursive: true });
const extData = vfsRead(FS, `/work/${cell}.ext`);
if (!extData) throw new Error(`Extraction failed: /work/${cell}.ext not created`);
writeFileSync(resolve(outputDir, `${cell}.ext`), extData);
const resExtData = vfsRead(FS, `/work/${cell}.res.ext`);
if (resExtData) writeFileSync(resolve(outputDir, `${cell}.res.ext`), resExtData);
const spiceData = vfsRead(FS, `/work/${cell}.spice`) ?? vfsRead(FS, `/work/${cell}.spc`);
const spiceExt = vfsRead(FS, `/work/${cell}.spice`) ? 'spice' : 'spc';
if (spiceData) writeFileSync(resolve(outputDir, `${cell}.${spiceExt}`), spiceData);
return {
ext: resolve(outputDir, `${cell}.ext`),
spice: spiceData ? resolve(outputDir, `${cell}.${spiceExt}`) : null,
};
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { ext, spice } = await run().catch(e => { reportError(e); process.exit(1); });
console.log(`\next: ${ext}`);
if (spice) console.log(`spice: ${spice}`);
console.log('Done.');
}

View File

@ -1,11 +0,0 @@
magic::tech load __TECH__
magic::load /work/__CELL__
magic::extract path /work
magic::extract do resistance
magic::extract all
magic::select top cell
magic::extresist all
magic::ext2spice format ngspice
magic::ext2spice extresist on
magic::ext2spice cthresh 0
magic::ext2spice /work/__CELL__

View File

@ -2,7 +2,7 @@
// //
// Usage: node examples/extract.js [magFile [tech [outputDir]]] // Usage: node examples/extract.js [magFile [tech [outputDir]]]
import { createMagic, vfsWrite, vfsRead, loadCell, loadScript, import { createMagic, vfsWrite, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers.js'; DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
import { writeFileSync, mkdirSync } from 'node:fs'; import { writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path'; import { resolve } from 'node:path';
@ -34,7 +34,7 @@ export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDi
} }
if (process.argv[1] === fileURLToPath(import.meta.url)) { if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { ext, spice } = await run().catch(e => { reportError(e); process.exit(1); }); const { ext, spice } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
console.log(`\next: ${ext}`); console.log(`\next: ${ext}`);
if (spice) console.log(`spice: ${spice}`); if (spice) console.log(`spice: ${spice}`);
console.log('Done.'); console.log('Done.');

View File

@ -1,28 +0,0 @@
// gds-tcl.js — GDS export via the TCL variant.
import { createMagic, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers-tcl.js';
import { writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
const { magic } = await createMagic();
const { FS } = magic;
const { tech: techName, cell } = loadCell(FS, tech, magFile);
magic.runScript(loadScript('gds-tcl.tcl', techName, cell));
mkdirSync(outputDir, { recursive: true });
const data = vfsRead(FS, `/work/${cell}.gds`);
if (!data) throw new Error(`GDS export failed: /work/${cell}.gds not created`);
const outPath = resolve(outputDir, `${cell}.gds`);
writeFileSync(outPath, data);
return { outPath, bytes: data.length };
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { outPath, bytes } = await run().catch(e => { reportError(e); process.exit(1); });
console.log(`\ngds: ${outPath} (${bytes} bytes)`);
console.log('Done.');
}

View File

@ -1,3 +0,0 @@
magic::tech load __TECH__
magic::load /work/__CELL__
magic::gds write /work/__CELL__

View File

@ -2,7 +2,7 @@
// //
// Usage: node examples/gds.js [magFile [tech [outputDir]]] // Usage: node examples/gds.js [magFile [tech [outputDir]]]
import { createMagic, vfsRead, loadCell, loadScript, import { createMagic, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers.js'; DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
import { writeFileSync, mkdirSync } from 'node:fs'; import { writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path'; import { resolve } from 'node:path';
@ -24,7 +24,7 @@ export async function run({ magFile = DEFAULT_MAG, tech = DEFAULT_TECH, outputDi
} }
if (process.argv[1] === fileURLToPath(import.meta.url)) { if (process.argv[1] === fileURLToPath(import.meta.url)) {
const { outPath, bytes } = await run().catch(e => { reportError(e); process.exit(1); }); const { outPath, bytes } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
console.log(`\ngds: ${outPath} (${bytes} bytes)`); console.log(`\ngds: ${outPath} (${bytes} bytes)`);
console.log('Done.'); console.log('Done.');
} }

View File

@ -1,91 +0,0 @@
// Shared utilities for Magic WASM TCL-variant examples.
//
// Loads the TCL-enabled WASM variant. Scripts use magic:: prefixed commands;
// runScript() evaluates them through the Tcl interpreter via Tcl_EvalFile.
import createMagicModule from '../tcl/magic.js';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, resolve, basename } from 'node:path';
export const EXAMPLES_DIR = dirname(fileURLToPath(import.meta.url));
export const wasmBinary = readFileSync(resolve(EXAMPLES_DIR, '../tcl/magic.wasm'));
export const DEFAULT_TECH = 'scmos';
export const DEFAULT_MAG = resolve(EXAMPLES_DIR, 'min.mag');
export const DEFAULT_OUT = resolve(EXAMPLES_DIR, 'output-tcl');
export { basename };
export function vfsWrite(FS, vfsPath, source) {
const dir = vfsPath.substring(0, vfsPath.lastIndexOf('/'));
if (dir) FS.mkdirTree(dir);
FS.writeFile(vfsPath, typeof source === 'string' ? readFileSync(source) : source);
}
export function vfsRead(FS, vfsPath) {
try { return Buffer.from(FS.readFile(vfsPath)); } catch { return null; }
}
export class MagicWasm {
constructor(mod) {
this._init = mod.cwrap('magic_wasm_init', 'number', []);
this._run = mod.cwrap('magic_wasm_run_command', 'number', ['string']);
this._sourceFile = mod.cwrap('magic_wasm_source_file', 'number', ['string']);
this.FS = mod.FS;
}
init() {
const rc = this._init();
if (rc !== 0) throw new Error(`magic_wasm_init failed with code ${rc}`);
}
// Evaluate a Tcl script (with magic:: prefixed commands) through the
// Tcl interpreter. Writes the script to VFS then calls magic_wasm_source_file,
// which in TCL mode uses Tcl_EvalFile.
runScript(text) {
const path = '/tmp/_magic_script.tcl';
this.FS.mkdirTree('/tmp');
this.FS.writeFile(path, text);
try {
this._sourceFile(path);
} catch (e) {
// A WASM trap during Tcl evaluation otherwise gives no hint of the
// source; flag it before the error propagates.
console.error('[magic-tcl] script evaluation failed');
throw e;
}
}
// Evaluate a Tcl expression directly (needed for proc definitions in PCell).
runTcl(text) {
this._run(text);
}
}
export async function createMagic({ onPrint, onPrintErr } = {}) {
const lines = [];
const mod = await createMagicModule({
wasmBinary,
print: onPrint ?? (msg => { lines.push(msg); console.log('[magic-tcl]', msg); }),
printErr: onPrintErr ?? (msg => { lines.push(msg); console.error('[magic-tcl]', msg); }),
});
const magic = new MagicWasm(mod);
magic.init();
return { magic, lines };
}
export function loadCell(FS, tech, magFile) {
const cell = basename(magFile, '.mag');
vfsWrite(FS, `/work/${cell}.mag`, magFile);
return { tech, cell };
}
export function loadScript(name, tech, cell) {
return readFileSync(resolve(EXAMPLES_DIR, name), 'utf8')
.replaceAll('__TECH__', tech)
.replaceAll('__CELL__', cell);
}
// Print a failure with enough detail to be actionable in CI. WASM aborts
// surface as a RuntimeError whose .message is terse while .stack carries the
// wasm-function offsets that emsymbolizer maps back to C source — so always
// prefer the stack.
export function reportError(e) {
console.error(e?.stack ?? String(e?.message ?? e));
}

View File

@ -1,14 +1,11 @@
// Shared utilities for Magic WASM examples. // Shared utilities for Magic WASM examples.
// import createMagicModule from '../magic.js';
// These examples drive the non-TCL variant (legacy magic command parser).
// For Tcl-eval semantics, see examples/smoke-tcl.mjs.
import createMagicModule from '../notcl/magic.js';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url';
import { dirname, resolve, basename } from 'node:path'; import { dirname, resolve, basename } from 'node:path';
export const EXAMPLES_DIR = dirname(fileURLToPath(import.meta.url)); export const EXAMPLES_DIR = dirname(fileURLToPath(import.meta.url));
export const wasmBinary = readFileSync(resolve(EXAMPLES_DIR, '../notcl/magic.wasm')); export const wasmBinary = readFileSync(resolve(EXAMPLES_DIR, '../magic.wasm'));
export const DEFAULT_TECH = 'scmos'; export const DEFAULT_TECH = 'scmos';
export const DEFAULT_MAG = resolve(EXAMPLES_DIR, 'min.mag'); export const DEFAULT_MAG = resolve(EXAMPLES_DIR, 'min.mag');
export const DEFAULT_OUT = resolve(EXAMPLES_DIR, 'output'); export const DEFAULT_OUT = resolve(EXAMPLES_DIR, 'output');
@ -38,28 +35,11 @@ export class MagicWasm {
runScript(text) { runScript(text) {
for (const line of text.split('\n')) { for (const line of text.split('\n')) {
const l = line.trim(); const l = line.trim();
if (l && !l.startsWith('#')) { if (l && !l.startsWith('#')) this._run(l);
try {
this._run(l);
} catch (e) {
// Name the command that aborted before the error propagates — a
// WASM trap otherwise gives no hint which step failed.
console.error(`[magic] command failed: ${l}`);
throw e;
}
}
} }
} }
} }
// Print a failure with enough detail to be actionable in CI. WASM aborts
// surface as a RuntimeError whose .message is terse ("memory access out of
// bounds") while .stack carries the wasm-function offsets that emsymbolizer
// maps back to C source — so always prefer the stack.
export function reportError(e) {
console.error(e?.stack ?? String(e?.message ?? e));
}
// Creates a fresh Magic WASM instance and calls init(). // Creates a fresh Magic WASM instance and calls init().
// onPrint / onPrintErr default to console.log/error with a [magic] prefix. // onPrint / onPrintErr default to console.log/error with a [magic] prefix.
// All output lines are also collected into the returned `lines` array. // All output lines are also collected into the returned `lines` array.

View File

@ -1,39 +0,0 @@
// pcell.js — PCell generation test (TCL variant only).
//
// Defines a Tcl proc as a PCell, instantiates it with two different sizes,
// and verifies that both GDS outputs are non-empty.
//
// Usage: node examples/pcell.js
import { createMagic, vfsRead, loadScript, DEFAULT_TECH, DEFAULT_OUT, reportError } from './helpers-tcl.js';
import { writeFileSync, mkdirSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { resolve } from 'node:path';
export async function run({ tech = DEFAULT_TECH, outputDir = DEFAULT_OUT } = {}) {
const { magic } = await createMagic();
const { FS } = magic;
FS.mkdirTree('/work');
magic.runTcl(loadScript('pcell.tcl', tech, ''));
mkdirSync(outputDir, { recursive: true });
const cells = ['pcell_4x8', 'pcell_8x4'];
const results = {};
for (const name of cells) {
const data = vfsRead(FS, `/work/${name}.gds`);
if (!data || data.length === 0)
throw new Error(`PCell GDS output missing or empty: /work/${name}.gds`);
const outPath = resolve(outputDir, `${name}.gds`);
writeFileSync(outPath, data);
results[name] = { outPath, bytes: data.length };
}
return results;
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const results = await run().catch(e => { reportError(e); process.exit(1); });
for (const [name, { outPath, bytes }] of Object.entries(results))
console.log(` ${name}.gds: ${outPath} (${bytes} bytes)`);
console.log('Done.');
}

View File

@ -1,12 +0,0 @@
magic::tech load __TECH__
proc make_rect {name width height} {
magic::load $name
magic::box 0 0 $width $height
magic::paint m1
magic::save /work/$name
magic::gds write /work/$name
}
make_rect pcell_4x8 4 8
make_rect pcell_8x4 8 4

View File

@ -1,39 +0,0 @@
// Smoke-test that confirms the TCL interpreter is live inside magic.wasm.
//
// In wrapper mode magic_wasm_run_command routes its argument to
// Tcl_EvalEx(magicinterp, ...). So:
// - pure Tcl (`set x 42; puts ...`) should work
// - magic commands are available as ::magic:: ensemble commands too
//
// Run: node npm/examples/smoke-tcl.mjs
// Pull in the TCL-enabled variant explicitly via the /tcl subpath export.
import createMagic from '../tcl.js';
const m = await createMagic();
const status = m.init();
if (status !== 0) {
console.error(`magic_wasm_init failed: ${status}`);
process.exit(1);
}
console.log('magic_wasm_init: OK');
function runTcl(label, command) {
const rc = m.runCommand(command);
console.log(`[rc=${rc}] ${label}: ${command}`);
return rc;
}
// 1. Pure Tcl arithmetic — proves the TCL interp is parsing.
runTcl('tcl-set', 'set tcl_smoke_x 42');
runTcl('tcl-expr', 'set tcl_smoke_y [expr {$tcl_smoke_x * 2}]');
// 2. Tcl introspection — magic should publish a Tclmagic package.
runTcl('tcl-info', 'puts "tcl_version=$tcl_version patchlevel=$tcl_patchLevel"');
runTcl('tcl-pkgs', 'puts "packages=[package names]"');
// 3. A real magic command via the wrapper.
runTcl('magic-help', 'magic::help');
console.log('done');

View File

@ -1,24 +1,14 @@
// Default entry point: the non-TCL build. import MagicModuleFactory from './magic.js';
//
// This preserves the original API and behavior of magic-vlsi-wasm. Magic
// commands ("tech load sky130A", "load /work/inv", …) are dispatched through
// magic's legacy parser; no Tcl interpreter is involved.
//
// For the TCL-enabled build (commands are evaluated by Tcl_EvalEx, exposing
// the full Tcl 9 runtime alongside the ::magic:: command ensemble), import
// from "magic-vlsi-wasm/tcl" instead.
import MagicModuleFactory from './notcl/magic.js';
async function createMagic(options = {}) { async function createMagic(options = {}) {
const module = await MagicModuleFactory(options); const module = await MagicModuleFactory(options);
const init = module.cwrap('magic_wasm_init', 'number', []); const init = module.cwrap('magic_wasm_init', 'number', []);
const runCommand = module.cwrap('magic_wasm_run_command', 'number', ['string']); const runCommand = module.cwrap('magic_wasm_run_command', 'number', ['string']);
const sourceFile = module.cwrap('magic_wasm_source_file', 'number', ['string']); const sourceFile = module.cwrap('magic_wasm_source_file', 'number', ['string']);
const update = module.cwrap('magic_wasm_update', null, []); const update = module.cwrap('magic_wasm_update', null, []);
return { init, runCommand, sourceFile, update, FS: module.FS, variant: 'notcl' }; return { init, runCommand, sourceFile, update, FS: module.FS };
} }
export { createMagic }; export { createMagic };

View File

@ -1,6 +0,0 @@
// Explicit non-TCL entry point: import from "magic-vlsi-wasm/notcl".
// Identical to the default `magic-vlsi-wasm` import — exists so callers can
// be explicit about which variant they want.
export { createMagic } from './index.js';
export { default } from './index.js';

View File

@ -1,38 +1,34 @@
{ {
"name": "magic-vlsi-wasm", "name": "magic-vlsi-wasm",
"version": "0.0.0-dev", "version": "0.0.0-dev",
"description": "Magic VLSI Layout Tool — headless WebAssembly build (TCL + non-TCL variants)", "description": "Magic VLSI Layout Tool — headless WebAssembly build",
"type": "module", "type": "module",
"main": "index.js", "main": "index.js",
"types": "index.d.ts", "types": "index.d.ts",
"exports": { "exports": {
".": { "import": "./index.js", "types": "./index.d.ts" }, ".": {
"./tcl": { "import": "./tcl.js", "types": "./index.d.ts" }, "import": "./index.js",
"./notcl": { "import": "./notcl.js", "types": "./index.d.ts" } "types": "./index.d.ts"
}
}, },
"files": [ "files": [
"index.js", "index.js",
"index.d.ts", "index.d.ts",
"tcl.js", "magic.js",
"notcl.js", "magic.wasm",
"tcl/magic.js",
"tcl/magic.wasm",
"notcl/magic.js",
"notcl/magic.wasm",
"examples/", "examples/",
"LICENSE", "LICENSE",
"README.md" "README.md"
], ],
"scripts": { "scripts": {
"example": "node examples/extract.js", "example": "node examples/extract.js",
"test": "node examples/all.js", "test": "node examples/all.js",
"test:tcl": "node examples/all-tcl.js", "test:gds": "node examples/gds.js",
"test:gds": "node examples/gds.js", "test:drc": "node examples/drc.js",
"test:drc": "node examples/drc.js", "test:cif": "node examples/cif.js"
"test:cif": "node examples/cif.js"
}, },
"keywords": ["magic", "vlsi", "eda", "wasm", "webassembly", "layout", "tcl"], "keywords": ["magic", "vlsi", "eda", "wasm", "webassembly", "layout"],
"license": "HPND", "license": "HPND",
"engines": { "engines": {
"node": ">=18" "node": ">=18"

View File

@ -1,33 +0,0 @@
// TCL-enabled entry point: import from "magic-vlsi-wasm/tcl".
//
// In this variant magic.wasm embeds a full Tcl 9 interpreter (from
// tcltk/tcl, pinned via magic/npm/tcl.ref) and `runCommand(str)` calls
// Tcl_EvalEx(magicinterp, str, ...). Pure Tcl works:
//
// await magic.runCommand('set x 42');
// await magic.runCommand('puts $tcl_version');
//
// Magic commands are exposed as the ::magic:: ensemble:
//
// await magic.runCommand('magic::tech load sky130A');
// await magic.runCommand('magic::load /work/inv');
//
// (Bare command names like "tech load …" are not imported into the global
// namespace by this build — invoke them with the ::magic:: prefix, or set
// up `namespace import ::magic::*` yourself after init().)
import MagicModuleFactory from './tcl/magic.js';
async function createMagic(options = {}) {
const module = await MagicModuleFactory(options);
const init = module.cwrap('magic_wasm_init', 'number', []);
const runCommand = module.cwrap('magic_wasm_run_command', 'number', ['string']);
const sourceFile = module.cwrap('magic_wasm_source_file', 'number', ['string']);
const update = module.cwrap('magic_wasm_update', null, []);
return { init, runCommand, sourceFile, update, FS: module.FS, variant: 'tcl' };
}
export { createMagic };
export default createMagic;

View File

@ -27,24 +27,64 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
/* /*
*-------------------------------------------------------------------------- *--------------------------------------------------------------------------
* *
* resMakePortBreakpoints -- * resNodeIsPort --
* *
* Generate new nodes and breakpoints for every unused port declared * If the given position is inside any port declared on the tile,
* on a tile. However, if "startpoint" is inside the port position, * change the node name to the port name. Remove the port
* then it has already been processed, so ignore it. * declaration if it was used.
*
* Results:
* None.
*
* Side effects:
* Adds breakpoints where ports (drivers, sinks, or labels) have been
* defined as connected to the tile.
* *
*-------------------------------------------------------------------------- *--------------------------------------------------------------------------
*/ */
void void
resMakePortBreakpoints(tile, list) resNodeIsPort(node, x, y, tile)
resNode *node;
int x;
int y;
Tile *tile;
{
Rect *rect;
Point p;
resPort *pl, *lp;
resInfo *info = (resInfo *)TiGetClientPTR(tile);
p.p_x = x;
p.p_y = y;
for (pl = info->portList; pl; pl = pl->rp_nextPort)
{
rect = &(pl->rp_bbox);
if (GEO_ENCLOSE(&p, rect))
{
node->rn_name = pl->rp_nodename;
if (info->portList == pl)
info->portList = pl->rp_nextPort;
else
{
for (lp = info->portList; lp && (lp->rp_nextPort != pl);
lp = lp->rp_nextPort);
lp->rp_nextPort = pl->rp_nextPort;
}
freeMagic(pl);
break;
}
}
}
/*
*--------------------------------------------------------------------------
*
* resAllPortNodes --
*
* Generate new nodes and breakpoints for every unused port declared
* on a tile. However, if "startpoint" is inside the port position,
* then it has already been processed, so ignore it.
*
*--------------------------------------------------------------------------
*/
void
resAllPortNodes(tile, list)
Tile *tile; Tile *tile;
resNode **list; resNode **list;
{ {
@ -52,7 +92,6 @@ resMakePortBreakpoints(tile, list)
resNode *resptr; resNode *resptr;
resPort *pl; resPort *pl;
resInfo *info = (resInfo *)TiGetClientPTR(tile); resInfo *info = (resInfo *)TiGetClientPTR(tile);
ResConnect *connect;
free_magic1_t mm1 = freeMagic1_init(); free_magic1_t mm1 = freeMagic1_init();
for (pl = info->portList; pl; pl = pl->rp_nextPort) for (pl = info->portList; pl; pl = pl->rp_nextPort)
@ -64,13 +103,8 @@ resMakePortBreakpoints(tile, list)
resptr->rn_status = TRUE; resptr->rn_status = TRUE;
resptr->rn_noderes = 0; resptr->rn_noderes = 0;
resptr->rn_name = pl->rp_nodename; resptr->rn_name = pl->rp_nodename;
/* Link back to the resnode from the ResConnect record */
connect = pl->rp_connect;
connect->rc_node = resptr;
ResAddToQueue(resptr, list); ResAddToQueue(resptr, list);
ResNewBreak(resptr, tile, x, y, NULL); NEWBREAK(resptr, tile, x, y, NULL);
freeMagic1(&mm1, pl); freeMagic1(&mm1, pl);
} }
freeMagic1_end(&mm1); freeMagic1_end(&mm1);
@ -79,44 +113,89 @@ resMakePortBreakpoints(tile, list)
/* /*
*-------------------------------------------------------------------------- *--------------------------------------------------------------------------
* *
* ResStartTile -- * ResMultiPlaneFunc---
* *
* For the tile at the starting point of the net, create an initial * If device is found overlapping one of its source/drain types, then
* resNode entry. * generate a new device at the center of the tile and add to ResNodeQueue.
* *
* Results: * Results:
* None. * Always 0 to keep the search going.
*
* Side Effects:
* creates a node.
* *
* Side effects:
* Adds to ResNodeQueue
* *
*-------------------------------------------------------------------------- *--------------------------------------------------------------------------
*/ */
void int
ResStartTile(tile, x, y) ResMultiPlaneFunc(tile, dinfo, tpptr)
Tile *tile; Tile *tile;
int x, y; TileType dinfo; /* Not used, but needs to be handled */
Tile **tpptr;
{ {
resNode *resptr; Tile *tp = *tpptr;
int xj, yj;
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode))); /* Simplified split tile handling---Ignore the right side of
InitializeResNode(resptr, x, y, RES_NODE_ORIGIN); * tiles that have non-space types on both sides.
resptr->rn_status = TRUE; */
resptr->rn_noderes = 0; if (IsSplit(tile))
ResAddToQueue(resptr, &ResNodeQueue); if (TiGetLeftType(tile) != TT_SPACE && TiGetRightType(tile) != TT_SPACE)
ResNewBreak(resptr, tile, x, y, NULL); if (dinfo & TT_SIDE)
if (resCurrentNode == NULL) resCurrentNode = resptr; return 0;
xj = (LEFT(tile) + RIGHT(tile)) / 2;
yj = (TOP(tile) + BOTTOM(tile)) / 2;
ResNewSDDevice(tp, tile, xj, yj, OTHERPLANE, &ResNodeQueue);
return 0;
} }
/* /*
*-------------------------------------------------------------------------- *--------------------------------------------------------------------------
* *
* ResEachTile -- * ResSubstrateFunc---
* *
* For each tile, make a list of all possible current sources/ * If device is found overlapping its substrate type, then generate a new
* device at the center of the tile and add to ResNodeQueue.
*
* Results:
* Always 0 to keep the search going.
*
* Side effects:
* Adds to ResNodeQueue
*
*--------------------------------------------------------------------------
*/
int
ResSubstrateFunc(tile, dinfo, tpptr)
Tile *tile;
TileType dinfo;
Tile **tpptr;
{
Tile *tp = *tpptr;
int xj, yj;
/* Simplified split tile handling---Ignore the right side of
* tiles that have non-space types on both sides.
*/
if (IsSplit(tile))
if (TiGetLeftType(tile) != TT_SPACE && TiGetRightType(tile) != TT_SPACE)
if (dinfo & TT_SIDE)
return 0;
xj = (LEFT(tile) + RIGHT(tile)) / 2;
yj = (TOP(tile) + BOTTOM(tile)) / 2;
ResNewSubDevice(tp, tile, xj, yj, OTHERPLANE, &ResNodeQueue);
return 0;
}
/*
*--------------------------------------------------------------------------
*
* ResEachTile--for each tile, make a list of all possible current sources/
* sinks including contacts, devices, and junctions. Once this * sinks including contacts, devices, and junctions. Once this
* list is made, calculate the resistor network for the tile. * list is made, calculate the resistor network for the tile.
* *
@ -135,9 +214,10 @@ ResStartTile(tile, x, y)
#define IGNORE_BOTTOM 8 #define IGNORE_BOTTOM 8
bool bool
ResEachTile(tile, devNodeTable) ResEachTile(tile, startpoint)
Tile *tile; /* Tile being processed */ Tile *tile;
HashTable *devNodeTable; /* Table of tiles connected to devices */ Point *startpoint;
{ {
Tile *tp; Tile *tp;
resNode *resptr; resNode *resptr;
@ -146,13 +226,14 @@ ResEachTile(tile, devNodeTable)
int xj, yj, i; int xj, yj, i;
bool merged; bool merged;
tElement *tcell; tElement *tcell;
resInfo *tstructs = (resInfo *)TiGetClientPTR(tile); resInfo *tstructs= (resInfo *)TiGetClientPTR(tile);
ExtDevice *devptr; ExtDevice *devptr;
int sides; int sides;
HashEntry *he;
ResTileCount++; ResTileCount++;
/* Process startpoint, if any. */
/* Simplification: Split tiles handle either the non-space side, /* Simplification: Split tiles handle either the non-space side,
* or if neither side is space, then handle the left side. * or if neither side is space, then handle the left side.
*/ */
@ -177,7 +258,21 @@ ResEachTile(tile, devNodeTable)
t1 = TiGetTypeExact(tile); t1 = TiGetTypeExact(tile);
} }
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t1)) if (startpoint != (Point *) NULL)
{
int x = startpoint->p_x;
int y = startpoint->p_y;
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
InitializeResNode(resptr, x, y, RES_NODE_ORIGIN);
resptr->rn_status = TRUE;
resptr->rn_noderes = 0;
ResAddToQueue(resptr, &ResNodeQueue);
NEWBREAK(resptr, tile, x, y, NULL);
if (resCurrentNode == NULL) resCurrentNode = resptr;
resNodeIsPort(resptr, x, y, tile);
}
if TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t1)
{ {
/* /*
* The device is put in the center of the tile. This is fine * The device is put in the center of the tile. This is fine
@ -200,7 +295,9 @@ ResEachTile(tile, devNodeTable)
InitializeResNode(resptr, x, y, RES_NODE_JUNCTION); InitializeResNode(resptr, x, y, RES_NODE_JUNCTION);
resptr->rn_te = tcell; resptr->rn_te = tcell;
ResAddToQueue(resptr, &ResNodeQueue); ResAddToQueue(resptr, &ResNodeQueue);
ResNewBreak(resptr, tile, resptr->rn_loc.p_x, resNodeIsPort(resptr, x, y, tile);
NEWBREAK(resptr, tile, resptr->rn_loc.p_x,
resptr->rn_loc.p_y, NULL); resptr->rn_loc.p_y, NULL);
} }
} }
@ -229,7 +326,7 @@ ResEachTile(tile, devNodeTable)
/* left */ /* left */
if (!(sides & IGNORE_LEFT)) if (!(sides & IGNORE_LEFT))
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp=RT(tp))
{ {
t2 = TiGetRightType(tp); t2 = TiGetRightType(tp);
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2)) if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2))
@ -244,14 +341,14 @@ ResEachTile(tile, devNodeTable)
/* found device */ /* found device */
xj = LEFT(tile); xj = LEFT(tile);
yj = (TOP(tp) + BOTTOM(tp)) >> 1; yj = (TOP(tp) + BOTTOM(tp)) >> 1;
ResNewTermDevice(tile, tp, i, xj, yj, RIGHTEDGE, &ResNodeQueue); ResNewSDDevice(tile, tp, xj, yj, RIGHTEDGE, &ResNodeQueue);
break; break;
} }
} }
if (i < devptr->exts_deviceSDCount) break; if (i < devptr->exts_deviceSDCount) break;
} }
} }
if (TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2)) if TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2)
{ {
/* tile is junction */ /* tile is junction */
xj = LEFT(tile); xj = LEFT(tile);
@ -262,7 +359,7 @@ ResEachTile(tile, devNodeTable)
/* right */ /* right */
if (!(sides & IGNORE_RIGHT)) if (!(sides & IGNORE_RIGHT))
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp)) for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp=LB(tp))
{ {
t2 = TiGetLeftType(tp); t2 = TiGetLeftType(tp);
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2)) if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2))
@ -277,18 +374,18 @@ ResEachTile(tile, devNodeTable)
/* found device */ /* found device */
xj = RIGHT(tile); xj = RIGHT(tile);
yj = (TOP(tp) + BOTTOM(tp)) >> 1; yj = (TOP(tp) + BOTTOM(tp)) >> 1;
ResNewTermDevice(tile, tp, i, xj, yj, LEFTEDGE, &ResNodeQueue); ResNewSDDevice(tile, tp, xj, yj, LEFTEDGE, &ResNodeQueue);
break; break;
} }
} }
if (i < devptr->exts_deviceSDCount) break; if (i < devptr->exts_deviceSDCount) break;
} }
} }
if (TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2)) if TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2)
{ {
/* tile is junction */ /* tile is junction */
xj = RIGHT(tile); xj = RIGHT(tile);
yj = (MAX(BOTTOM(tile), BOTTOM(tp)) + MIN(TOP(tile), TOP(tp))) >> 1; yj = (MAX(BOTTOM(tile),BOTTOM(tp)) + MIN(TOP(tile), TOP(tp))) >> 1;
(void)ResProcessJunction(tile, tp, xj, yj, &ResNodeQueue); (void)ResProcessJunction(tile, tp, xj, yj, &ResNodeQueue);
} }
} }
@ -310,25 +407,25 @@ ResEachTile(tile, devNodeTable)
/* found device */ /* found device */
yj = TOP(tile); yj = TOP(tile);
xj = (LEFT(tp) + RIGHT(tp)) >> 1; xj = (LEFT(tp) + RIGHT(tp)) >> 1;
ResNewTermDevice(tile, tp, i, xj, yj, BOTTOMEDGE, &ResNodeQueue); ResNewSDDevice(tile, tp, xj, yj, BOTTOMEDGE, &ResNodeQueue);
break; break;
} }
} }
if (i < devptr->exts_deviceSDCount) break; if (i < devptr->exts_deviceSDCount) break;
} }
} }
if (TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2)) if TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2)
{ {
/* tile is junction */ /* tile is junction */
yj = TOP(tile); yj = TOP(tile);
xj = (MAX(LEFT(tile), LEFT(tp)) + MIN(RIGHT(tile), RIGHT(tp))) >> 1; xj = (MAX(LEFT(tile),LEFT(tp)) + MIN(RIGHT(tile),RIGHT(tp))) >> 1;
ResProcessJunction(tile, tp, xj, yj, &ResNodeQueue); ResProcessJunction(tile, tp, xj, yj, &ResNodeQueue);
} }
} }
/* bottom */ /* bottom */
if (!(sides & IGNORE_BOTTOM)) if (!(sides & IGNORE_BOTTOM))
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp=TR(tp))
{ {
t2 = TiGetTopType(tp); t2 = TiGetTopType(tp);
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2)) if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2))
@ -343,59 +440,86 @@ ResEachTile(tile, devNodeTable)
/* found device */ /* found device */
yj = BOTTOM(tile); yj = BOTTOM(tile);
xj = (LEFT(tp) + RIGHT(tp)) >> 1; xj = (LEFT(tp) + RIGHT(tp)) >> 1;
ResNewTermDevice(tile, tp, i, xj, yj, TOPEDGE, &ResNodeQueue); ResNewSDDevice(tile, tp, xj, yj, TOPEDGE, &ResNodeQueue);
break; break;
} }
} }
if (i < devptr->exts_deviceSDCount) break; if (i < devptr->exts_deviceSDCount) break;
} }
} }
if (TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2)) if TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2)
{ {
/* tile is junction */ /* tile is junction */
yj = BOTTOM(tile); yj = BOTTOM(tile);
xj = (MAX(LEFT(tile), LEFT(tp)) + MIN(RIGHT(tile), RIGHT(tp))) >> 1; xj = (MAX(LEFT(tile),LEFT(tp)) + MIN(RIGHT(tile),RIGHT(tp))) >> 1;
ResProcessJunction(tile, tp, xj, yj, &ResNodeQueue); ResProcessJunction(tile, tp, xj, yj, &ResNodeQueue);
} }
} }
/* Check for terminals on other planes (e.g., capacitors, bipolars, ...) */ /* Check for source/drain on other planes (e.g., capacitors, bipolars, ...) */
/* Note: Need to tag these tiles per device to avoid checking through */
/* the device list for each tile. (To be done) */
he = HashLookOnly(devNodeTable, (char *)tile); if (TTMaskHasType(&ResSDTypesBitMask, t1))
if (he != NULL)
{ {
resDevTerm *resdevRec, *resdevList; Rect r;
Tile *devtile; int pNum;
TileTypeBitMask devMask;
resdevList = (resDevTerm *)HashGetValue(he); TiToRect(tile, &r);
while (resdevList != NULL)
for (pNum = 0; pNum < DBNumPlanes; pNum++)
{ {
resdevRec = resdevList; if (DBTypeOnPlane(t1, pNum)) continue;
/* Set the position from the device, not the current tile */
devtile = resdevRec->rdt_tile; /* NOTE: This is ridiculously inefficient and should be done
xj = (RIGHT(devtile) + LEFT(devtile)) / 2; * in a different way.
yj = (TOP(devtile) + BOTTOM(devtile)) / 2; */
if (resdevRec->rdt_term == -1) /* Substrate */
{ TTMaskZero(&devMask);
ResNewSubDevice(tile, resdevRec->rdt_tile, xj, yj, for (t2 = TT_TECHDEPBASE; t2 < DBNumUserLayers; t2++)
OTHERPLANE, &ResNodeQueue); for (devptr = ExtCurStyle->exts_device[t2]; devptr;
} devptr = devptr->exts_next)
else /* Terminal */ for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
{ if (TTMaskHasType(&devptr->exts_deviceSDTypes[i], t1))
ResNewTermDevice(tile, resdevRec->rdt_tile, resdevRec->rdt_term, TTMaskSetType(&devMask, t2);
xj, yj, OTHERPLANE, &ResNodeQueue);
} DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pNum],
resdevList = resdevList->rdt_next; &r, &devMask, ResMultiPlaneFunc, (ClientData)&tile);
freeMagic(resdevRec); }
}
/* Check for substrate under device */
if (TTMaskHasType(&ResSubTypesBitMask, t1))
{
Rect r;
int pNum;
TileTypeBitMask devMask;
TiToRect(tile, &r);
for (pNum = 0; pNum < DBNumPlanes; pNum++)
{
if (DBTypeOnPlane(t1, pNum)) continue;
/* NOTE: This is ridiculously inefficient and should be done
* in a different way.
*/
TTMaskZero(&devMask);
for (t2 = TT_TECHDEPBASE; t2 < DBNumUserLayers; t2++)
for (devptr = ExtCurStyle->exts_device[t2]; devptr;
devptr = devptr->exts_next)
if (TTMaskHasType(&devptr->exts_deviceSubstrateTypes, t1))
TTMaskSetType(&devMask, t2);
DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pNum],
&r, &devMask, ResSubstrateFunc, (ClientData)&tile);
} }
HashSetValue(he, (char *)NULL); /* Done with hash record */
} }
tstructs->ri_status |= RES_TILE_DONE; tstructs->ri_status |= RES_TILE_DONE;
resMakePortBreakpoints(tile, &ResNodeQueue); resAllPortNodes(tile, &ResNodeQueue);
merged = ResCalcTileResistance(tile, tstructs, &ResNodeQueue, merged = ResCalcTileResistance(tile, tstructs, &ResNodeQueue,
&ResNodeList); &ResNodeList);

View File

@ -61,7 +61,7 @@ ResSanityChecks(nodename, resistorList, nodeList, devlist)
{ {
resSanityStack = StackNew(64); resSanityStack = StackNew(64);
} }
for (node = nodeList; node != NULL; node = node->rn_more) for (node = nodeList; node != NULL; node=node->rn_more)
{ {
node->rn_status &= ~RES_REACHED_NODE; node->rn_status &= ~RES_REACHED_NODE;
if (node->rn_why & RES_NODE_ORIGIN) if (node->rn_why & RES_NODE_ORIGIN)
@ -133,7 +133,7 @@ ResSanityChecks(nodename, resistorList, nodeList, devlist)
} }
} }
foundorigin = 0; foundorigin = 0;
for (node = nodeList; node != NULL; node = node->rn_more) for (node = nodeList; node != NULL; node=node->rn_more)
{ {
if ((node->rn_status & RES_REACHED_NODE) == 0) if ((node->rn_status & RES_REACHED_NODE) == 0)
{ {

View File

@ -128,11 +128,11 @@ ResPrintDeviceList(fp, list)
{ {
if (list->rd_terminals[i] == NULL) continue; if (list->rd_terminals[i] == NULL) continue;
if (fp == stdout) if (fp == stdout)
TxPrintf("%c (%d,%d) ", termtype[i], TxPrintf("%c (%d,%d) ",termtype[i],
list->rd_terminals[i]->rn_loc.p_x, list->rd_terminals[i]->rn_loc.p_x,
list->rd_terminals[i]->rn_loc.p_y); list->rd_terminals[i]->rn_loc.p_y);
else else
fprintf(fp, "%c (%d,%d) ", termtype[i], fprintf(fp, "%c (%d,%d) ",termtype[i],
list->rd_terminals[i]->rn_loc.p_x, list->rd_terminals[i]->rn_loc.p_x,
list->rd_terminals[i]->rn_loc.p_y); list->rd_terminals[i]->rn_loc.p_y);

View File

@ -106,7 +106,7 @@ enumerate:
} }
else else
{ {
resTopTile = BL(resTopTile); resTopTile=BL(resTopTile);
} }
} }
} }
@ -205,7 +205,7 @@ ResCheckConcavity(bot, top, tt)
ylen = ypos - resWalkdown(bot, tt, xpos, ypos, NULL); ylen = ypos - resWalkdown(bot, tt, xpos, ypos, NULL);
if (xlen > ylen) if (xlen > ylen)
{ {
(void) resWalkdown(bot, tt, xpos, ypos, ResSplitX); (void) resWalkdown(bot,tt,xpos,ypos,ResSplitX);
} }
} }
} }
@ -274,7 +274,7 @@ int
resWalkup(tile, tt, xpos, ypos, func) resWalkup(tile, tt, xpos, ypos, func)
Tile *tile; Tile *tile;
TileType tt; TileType tt;
int xpos, ypos; int xpos,ypos;
Tile * (*func)(); Tile * (*func)();
{ {
@ -443,7 +443,7 @@ ResSplitX(tile, x)
Tile *tp = TiSplitX(tile, x); Tile *tp = TiSplitX(tile, x);
Tile *tp2; Tile *tp2;
TiSetBody(tp, tt); TiSetBody(tp,tt);
/* check to see if we can combine with the tiles above or below us */ /* check to see if we can combine with the tiles above or below us */
tp2 = RT(tile); tp2 = RT(tile);
if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tile) && RIGHT(tp2) == RIGHT(tile)) if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tile) && RIGHT(tp2) == RIGHT(tile))

View File

@ -29,33 +29,26 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
/* /*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
* *
* ResNewTermDevice -- * ResNewSDDevice -- called when a device is reached via a piece of
* diffusion. (Devices reached via poly, i.e.
* gates, are handled by ResEachTile.)
* *
* Called when a device is reached via a type in the device's * Results:none
* terminal type list (e.g., diffusion, for MOSFETs). Note that
* devices reached by the device type (e.g., poly, for MOSFETs)
* are handled by ResEachTile.
* *
* Results: * Side Effects: determines to which terminal (source or drain) node
* None * is connected. Makes new node if node hasn't already been created .
* * Allocates breakpoint in current tile for device.
* Side Effects:
* Determines to which terminal (source or drain) node is connected.
* Makes new node if node hasn't already been created. Allocates
* breakpoint in current tile for device.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
void void
ResNewTermDevice(tile, tp, n, xj, yj, direction, PendingList) ResNewSDDevice(tile, tp, xj, yj, direction, PendingList)
Tile *tile, *tp; Tile *tile, *tp;
int n; /* Terminal index */ int xj, yj, direction;
int xj, yj; /* Location of connection */
int direction; /* Direction of current */
resNode **PendingList; resNode **PendingList;
{ {
resNode *resptr = NULL; resNode *resptr;
resDevice *resDev; resDevice *resDev;
tElement *tcell; tElement *tcell;
int newnode; int newnode;
@ -71,40 +64,32 @@ ResNewTermDevice(tile, tp, n, xj, yj, direction, PendingList)
ri = (resInfo *) TiGetClientPTR(tp); ri = (resInfo *) TiGetClientPTR(tp);
resDev = ri->deviceList; resDev = ri->deviceList;
if (resDev == NULL) return; /* Shouldn't happen? */ if ((ri->sourceEdge & direction) != 0)
/* Set the terminal indicated (source or drain). If the terminal
* indicated is already set, then create a new breakpoint on the
* terminal. However, to handle cases where a device may have
* source and drain tied to the same net, if there is an existing
* entry and the edge direction is opposite of "direction", then
* create a new terminal on the other side (i.e., permute source
* and drain).
*/
if (resDev->rd_terminals[2 + n] == (resNode *)NULL)
{ {
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode))); if (resDev->rd_fet_source == (resNode *) NULL)
newnode = TRUE; {
resDev->rd_terminals[2 + n] = resptr; resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
newnode = TRUE;
resDev->rd_fet_source = resptr;
}
else
{
resptr = resDev->rd_fet_source;
}
} }
else if (((ri->sourceEdge & direction) != 0) || (resDev->rd_nterms < 4)) else
{ {
resptr = resDev->rd_terminals[2 + n]; if (resDev->rd_fet_drain == (resNode *) NULL)
{
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
newnode = TRUE;
resDev->rd_fet_drain = resptr;
}
else
{
resptr = resDev->rd_fet_drain;
}
} }
else if ((n == 0) && (resDev->rd_terminals[3] == (resNode *)NULL))
{
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
newnode = TRUE;
resDev->rd_terminals[3] = resptr;
}
else if ((n == 1) && (resDev->rd_terminals[2] == (resNode *)NULL))
{
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
newnode = TRUE;
resDev->rd_terminals[2] = resptr;
}
if (newnode) if (newnode)
{ {
tcell = (tElement *) mallocMagic((unsigned)(sizeof(tElement))); tcell = (tElement *) mallocMagic((unsigned)(sizeof(tElement)));
@ -114,8 +99,7 @@ ResNewTermDevice(tile, tp, n, xj, yj, direction, PendingList)
resptr->rn_te = tcell; resptr->rn_te = tcell;
ResAddToQueue(resptr, PendingList); ResAddToQueue(resptr, PendingList);
} }
if (resptr != NULL) NEWBREAK(resptr, tile, xj, yj, NULL);
ResNewBreak(resptr, tile, xj, yj, NULL);
} }
/* /*
@ -147,16 +131,20 @@ ResNewSubDevice(tile, tp, xj, yj, direction, PendingList)
ri = (resInfo *) TiGetClientPTR(tp); ri = (resInfo *) TiGetClientPTR(tp);
resDev = ri->deviceList; resDev = ri->deviceList;
if (resDev == NULL) return; /* Should not happen? */ /* Arrived at a device that has a terminal connected to substrate */
/* that is not a FET bulk terminal (e.g., varactor, diode). */
if (resDev->rd_nterms < 4) return;
if (resDev->rd_fet_subs == (resNode *)NULL) if (resDev->rd_fet_subs == (resNode *) NULL)
{ {
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode))); resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
newnode = TRUE; newnode = TRUE;
resDev->rd_fet_subs = resptr; resDev->rd_fet_subs = resptr;
} }
else else
resptr = resDev->rd_fet_subs; {
resptr = resDev->rd_fet_subs;
}
if (newnode) if (newnode)
{ {
@ -167,7 +155,7 @@ ResNewSubDevice(tile, tp, xj, yj, direction, PendingList)
resptr->rn_te = tcell; resptr->rn_te = tcell;
ResAddToQueue(resptr, PendingList); ResAddToQueue(resptr, PendingList);
} }
ResNewBreak(resptr, tile, xj, yj, NULL); NEWBREAK(resptr, tile, xj, yj, NULL);
} }
/* /*
@ -225,10 +213,10 @@ ResProcessJunction(tile, tp, xj, yj, NodeList)
junction->rj_nextjunction[1] = ri2->junctionList; junction->rj_nextjunction[1] = ri2->junctionList;
ri2->junctionList = junction; ri2->junctionList = junction;
ResNewBreak(junction->rj_jnode, tile, junction->rj_loc.p_x, NEWBREAK(junction->rj_jnode,tile, junction->rj_loc.p_x,
junction->rj_loc.p_y, NULL); junction->rj_loc.p_y, NULL);
ResNewBreak(junction->rj_jnode, tp, junction->rj_loc.p_x, NEWBREAK(junction->rj_jnode,tp, junction->rj_loc.p_x,
junction->rj_loc.p_y, NULL); junction->rj_loc.p_y, NULL);
} }

View File

@ -40,7 +40,8 @@ resNode *resCurrentNode;
int ResTileCount = 0; /* Number of tiles rn_status */ int ResTileCount = 0; /* Number of tiles rn_status */
extern ExtRegion *ResFirst(); extern ExtRegion *ResFirst();
extern Tile *FindStartTile(); extern Tile *FindStartTile();
TileTypeBitMask ResTermTypesBitMask; extern int ResEachTile();
TileTypeBitMask ResSDTypesBitMask;
TileTypeBitMask ResSubTypesBitMask; TileTypeBitMask ResSubTypesBitMask;
extern HashTable ResNodeTable; extern HashTable ResNodeTable;
@ -99,7 +100,7 @@ ResInitializeConn()
* *
* ResGetReCell -- * ResGetReCell --
* *
* This procedure makes sure that ResUse, ResDef * This procedure makes sure that ResUse,ResDef
* have been properly initialized to refer to a cell definition * have been properly initialized to refer to a cell definition
* named "__RESIS__". * named "__RESIS__".
* *
@ -147,86 +148,54 @@ void
ResDissolveContacts(contacts) ResDissolveContacts(contacts)
ResContactPoint *contacts; ResContactPoint *contacts;
{ {
TileType t, conttype; TileType t, oldtype, lasttype = TT_SPACE;
Tile *tp; Tile *tp;
TileType residue[NP][NT]; TileTypeBitMask residues;
int pNum;
bzero((char *)residue, NP * NT * sizeof(TileType));
for (; contacts != (ResContactPoint *)NULL; contacts = contacts->cp_nextcontact) for (; contacts != (ResContactPoint *)NULL; contacts = contacts->cp_nextcontact)
{ {
conttype = contacts->cp_type; oldtype = contacts->cp_type;
#ifdef PARANOID #ifdef PARANOID
if (conttype == TT_SPACE) if (oldtype == TT_SPACE)
TxError("Error in Contact Dissolving for %s \n", ResCurrentNode); TxError("Error in Contact Dissolving for %s \n",ResCurrentNode);
#endif #endif
if (oldtype != lasttype)
/* Fill in details of the residue types for each contact type.
* This is done only once per contact type. This could be refined
* further by temporarily changing the paint table directly or
* creating a separate paint table which erases contact cuts and
* replaces them with the residues.
*/
if (residue[DBPlane(conttype)][conttype] == TT_SPACE)
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
residue[pNum][conttype] = DBPlaneToResidue(conttype, pNum);
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{ {
if (DBTypeOnPlane(conttype, pNum)) lasttype = oldtype;
{ DBFullResidueMask(oldtype, &residues);
DBPaintPlane(ResUse->cu_def->cd_planes[pNum], &(contacts->cp_rect),
DBStdEraseTbl(conttype, pNum), (PaintUndoInfo *)NULL);
DBPaintPlane(ResUse->cu_def->cd_planes[pNum], &(contacts->cp_rect),
DBStdPaintTbl(residue[pNum][conttype], pNum),
(PaintUndoInfo *)NULL);
}
} }
DBErase(ResUse->cu_def, &(contacts->cp_rect), oldtype);
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
if (TTMaskHasType(&residues, t))
DBPaint(ResUse->cu_def, &(contacts->cp_rect), t);
#ifdef PARANOID #ifdef PARANOID
tp = PlaneGetHint(ResDef->cd_planes[DBPlane(conttype)]); tp = PlaneGetHint(ResDef->cd_planes[DBPlane(contacts->cp_type)]);
GOTOPOINT(tp, &(contacts->cp_rect.r_ll)); GOTOPOINT(tp, &(contacts->cp_rect.r_ll));
if (TiGetTypeExact(tp) == conttype) if (TiGetTypeExact(tp) == contacts->cp_type)
TxError("Error in Contact Preprocess Routines\n"); TxError("Error in Contact Preprocess Routines\n");
#endif #endif
} }
} }
/* Structure used by ResMakeDriverSinkPorts() to pass information to
* ResAddPortFunc(). Contains a reference to a node, so that the
* link between the tile and the node can be maintained, and the
* driver or sink, which has the information about the position and
* tile type of the connection.
*/
typedef struct driversinkdata {
ResExtNode *dsd_node;
ResConnect *dsd_connect;
} DriverSinkData;
/* /*
*--------------------------------------------------------------------------- *---------------------------------------------------------------------------
* *
* ResMakeDriverSinkPorts -- * ResMakePortBreakpoints --
* *
* Search through the list of node drivers and sinks (connections up * Search for nodes which are ports, and force them to be breakpoints
* and down in the hierarchy), and make sure this information is * in the "resInfo" field of their respective tiles in ResUse. This
* copied to the resInfo record of the tile(s) found at the connection. * ensures that connected nodes that stretch between two ports will
* not be assumed to be "hanging" nodes.
* *
* Results: * Do the same thing for labels.
* None.
*
* Side effects:
* Adds information to the resInfo clientData of tiles in def.
* *
*---------------------------------------------------------------------------- *----------------------------------------------------------------------------
*/ */
void void
ResMakeDriverSinkPorts(def) ResMakePortBreakpoints(def)
CellDef *def; CellDef *def;
{ {
Plane *plane; Plane *plane;
@ -235,66 +204,21 @@ ResMakeDriverSinkPorts(def)
HashSearch hs; HashSearch hs;
HashEntry *entry; HashEntry *entry;
ResExtNode *node; ResExtNode *node;
ResConnect *rdriver, *rsink; int ResAddBreakpointFunc(); /* Forward Declaration */
DriverSinkData dsd;
int ResAddPortFunc(); /* Forward Declaration */
HashStartSearch(&hs); HashStartSearch(&hs);
while((entry = HashNext(&ResNodeTable, &hs)) != NULL) while((entry = HashNext(&ResNodeTable, &hs)) != NULL)
{ {
node = (ResExtNode *)HashGetValue(entry); node = (ResExtNode *)HashGetValue(entry);
if (node->status & PORTNODE)
for (rdriver = node->drivepoints; rdriver; rdriver = rdriver->rc_next)
{ {
if (rdriver->rc_type <= 0) if (node->rs_ttype <= 0)
{ {
TxError("Warning: Label \"%s\" is unconnected.\n", node->name); TxError("Warning: Label \"%s\" is unconnected.\n", node->name);
continue; continue;
} }
rect = &(rdriver->rc_rect); rect = &(node->rs_bbox);
/* If label is on a contact, the contact has been dissolved. */
/* Assume that the uppermost residue is the port. This may */
/* not necessarily be the case. Could do a boundary scan on */
/* each residue plane to see which side of the contact is */
/* the internal connection in the def. . . */
if (DBIsContact(rdriver->rc_type))
{
TileType type;
DBFullResidueMask(rdriver->rc_type, &mask);
for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--)
if (TTMaskHasType(&mask, type))
{
plane = def->cd_planes[DBPlane(type)];
break;
}
}
else
{
TTMaskSetOnlyType(&mask, rdriver->rc_type);
plane = def->cd_planes[DBPlane(rdriver->rc_type)];
}
dsd.dsd_connect = rdriver;
dsd.dsd_node = node;
(void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask,
ResAddPortFunc, (ClientData)&dsd);
}
/* Process sink points in the same way */
for (rsink = node->sinkpoints; rsink; rsink = rsink->rc_next)
{
if (rsink->rc_type <= 0)
{
TxError("Warning: Label \"%s\" is unconnected.\n", node->name);
continue;
}
rect = &(rsink->rc_rect);
/* Beware of zero-area ports */ /* Beware of zero-area ports */
if (rect->r_xbot == rect->r_xtop) if (rect->r_xbot == rect->r_xtop)
@ -314,11 +238,11 @@ ResMakeDriverSinkPorts(def)
/* each residue plane to see which side of the contact is */ /* each residue plane to see which side of the contact is */
/* the internal connection in the def. . . */ /* the internal connection in the def. . . */
if (DBIsContact(rsink->rc_type)) if (DBIsContact(node->rs_ttype))
{ {
TileType type; TileType type;
DBFullResidueMask(rsink->rc_type, &mask); DBFullResidueMask(node->rs_ttype, &mask);
for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--) for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--)
if (TTMaskHasType(&mask, type)) if (TTMaskHasType(&mask, type))
{ {
@ -328,122 +252,40 @@ ResMakeDriverSinkPorts(def)
} }
else else
{ {
TTMaskSetOnlyType(&mask, rsink->rc_type); TTMaskSetOnlyType(&mask, node->rs_ttype);
plane = def->cd_planes[DBPlane(rsink->rc_type)]; plane = def->cd_planes[DBPlane(node->rs_ttype)];
} }
dsd.dsd_connect = rsink;
dsd.dsd_node = node;
(void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask, (void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask,
ResAddPortFunc, (ClientData)&dsd); ResAddBreakpointFunc, (ClientData)node);
} }
} }
} }
/*
*----------------------------------------------------------------------------
*
* ResAddPortFunc --
*
* Add a portList entry to the "resInfo" structure of the tile. The
* portList entry keeps a record of the area of overlap or abutment
* of the port, as well as a pointer to the resNode.
*
* Results:
* Always returns 0;
*
* Side effects:
* Adds information to a tile's "resInfo" clientData.
*
*----------------------------------------------------------------------------
*/
int
ResAddPortFunc(tile, dinfo, dsd)
Tile *tile;
TileType dinfo; /* (unused) */
DriverSinkData *dsd; /* Data for driver or sink */
{
resPort *rp;
resInfo *pX;
Rect rect;
ResConnect *connect;
ResExtNode *node;
if (TiGetClient(tile) == CLIENTDEFAULT)
return 0;
/* To simplify processing, if a split tile does not have TT_SPACE
* on either side, then only the left side is processed.
*/
if (IsSplit(tile))
if (TiGetLeftType(tile) != TT_SPACE && TiGetRightType(tile) != TT_SPACE)
if (dinfo & TT_SIDE)
return 0;
node = dsd->dsd_node;
connect = dsd->dsd_connect;
TiToRect(tile, &rect);
pX = (resInfo *)TiGetClient(tile);
rp = (resPort *) mallocMagic((unsigned)(sizeof(resPort)));
rp->rp_nextPort = pX->portList;
rp->rp_bbox = connect->rc_rect;
rp->rp_loc = connect->rc_rect.r_ll;
rp->rp_connect = connect;
rp->rp_nodename = node->name;
pX->portList = rp;
return 0;
}
/* Structure used by ResMakeLabelPorts() to pass information to
* ResAddPortFunc(). Contains a reference to a node, so that the
* link between the tile and the node can be maintained, and the
* label, which has the information about the position and tile
* type of the label.
*/
typedef struct reslabeldata {
ResExtNode *rld_node;
Label *rld_label;
ResConnect *rld_connect;
} ResLabelData;
/* /*
*--------------------------------------------------------------------------- *---------------------------------------------------------------------------
* *
* ResMakeLabelPorts -- * ResMakeLabelBreakpoints --
* *
* Search for labels that are part of a node, and add them to the * Search for labels that are part of a node, and force them to be
* portList linked list in the "resInfo" field of their respective tiles * breakpoints in the "resInfo" field of their respective tiles in
* in ResUse. This ensures (among other things) that pins of a top level * ResUse. This ensures (among other things) that pins of a top level
* cell will be retained and become the endpoint of a net. * cell will be retained and become the endpoint of a net.
* *
* Results:
* None.
*
* Side effects:
* Adds information to the resInfo clientData of tiles in def.
*
*---------------------------------------------------------------------------- *----------------------------------------------------------------------------
*/ */
void void
ResMakeLabelPorts(def, resisdata) ResMakeLabelBreakpoints(def, resisdata)
CellDef *def; CellDef *def;
ResisData *resisdata; ResisData *resisdata;
{ {
Plane *plane; Plane *plane;
Rect *rect;
TileTypeBitMask mask; TileTypeBitMask mask;
HashEntry *entry; HashEntry *entry;
ResExtNode *node; ResExtNode *node;
ResConnect *rdriver, *newsink;
Label *slab; Label *slab;
ResLabelData rld; int ResAddBreakpointFunc(); /* Forward Declaration */
int ResAddLabelFunc(); /* Forward Declaration */
for (slab = def->cd_labels; slab != NULL; slab = slab->lab_next) for (slab = def->cd_labels; slab != NULL; slab = slab->lab_next)
{ {
@ -454,26 +296,20 @@ ResMakeLabelPorts(def, resisdata)
entry = HashFind(&ResNodeTable, slab->lab_text); entry = HashFind(&ResNodeTable, slab->lab_text);
node = ResExtInitNode(entry); node = ResExtInitNode(entry);
/* If there is an existing drivepoint at this location, */ /* If the drivepoint position changes and the drivepoint is */
/* then ignore it. */ /* in the "resisdata" record, then make sure the tile type */
/* in "resisdata" gets changed to match. */
for (rdriver = node->drivepoints; rdriver; rdriver = rdriver->rc_next) if (resisdata->rg_devloc == &node->drivepoint)
{
if (GEO_TOUCH(&slab->lab_rect, &rdriver->rc_rect))
break;
}
if (rdriver != NULL) break;
/* Add a new sinkpoint to the node where the label is */
newsink = (ResConnect *)mallocMagic(sizeof(ResConnect));
newsink->rc_next = node->sinkpoints;
node->sinkpoints = newsink;
if (GEO_ENCLOSE(resisdata->rg_devloc, &slab->lab_rect))
resisdata->rg_ttype = slab->lab_type; resisdata->rg_ttype = slab->lab_type;
newsink->rc_rect = slab->lab_rect; node->drivepoint = slab->lab_rect.r_ll;
newsink->rc_type = slab->lab_type; node->rs_bbox = slab->lab_rect;
node->location = slab->lab_rect.r_ll;
node->rs_ttype = slab->lab_type;
node->type = slab->lab_type;
rect = &(node->rs_bbox);
/* If label is on a contact, the contact has been dissolved. */ /* If label is on a contact, the contact has been dissolved. */
/* Assume that the uppermost residue is the port. This may */ /* Assume that the uppermost residue is the port. This may */
@ -499,44 +335,29 @@ ResMakeLabelPorts(def, resisdata)
plane = def->cd_planes[DBPlane(slab->lab_type)]; plane = def->cd_planes[DBPlane(slab->lab_type)];
} }
rld.rld_node = node; (void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask,
rld.rld_label = slab; ResAddBreakpointFunc, (ClientData)node);
rld.rld_connect = newsink;
(void) DBSrPaintArea((Tile *) NULL, plane, &newsink->rc_rect, &mask,
ResAddLabelFunc, (ClientData)&rld);
} }
} }
/* /*
*---------------------------------------------------------------------------- *----------------------------------------------------------------------------
* *
* ResAddLabelFunc -- * ResAddBreakpointFunc --
* *
* Add a portList entry to the "resInfo" structure of the tile. The * Add a breakpoint to the "resInfo" structure of the tile
* portList entry keeps a record of the area of overlap or abutment
* of the port, as well as a pointer to the resNode.
*
* Results:
* Always returns 0;
*
* Side effects:
* Adds information to a tile's "resInfo" clientData.
* *
*---------------------------------------------------------------------------- *----------------------------------------------------------------------------
*/ */
int int
ResAddLabelFunc(tile, dinfo, rld) ResAddBreakpointFunc(tile, dinfo, node)
Tile *tile; Tile *tile;
TileType dinfo; /* (unused) */ TileType dinfo; /* (unused) */
ResLabelData *rld; /* Label and node data */ ResExtNode *node;
{ {
resPort *rp; resInfo *info;
resInfo *pX;
Rect rect;
Label *label;
ResExtNode *node;
ResConnect *connect;
if (TiGetClient(tile) == CLIENTDEFAULT) if (TiGetClient(tile) == CLIENTDEFAULT)
return 0; return 0;
@ -549,25 +370,12 @@ ResAddLabelFunc(tile, dinfo, rld)
if (dinfo & TT_SIDE) if (dinfo & TT_SIDE)
return 0; return 0;
node = rld->rld_node; NEWPORT(node, tile);
label = rld->rld_label;
connect = rld->rld_connect;
TiToRect(tile, &rect);
pX = (resInfo *)TiGetClient(tile);
rp = (resPort *) mallocMagic((unsigned)(sizeof(resPort)));
rp->rp_nextPort = pX->portList;
rp->rp_bbox = label->lab_rect;
rp->rp_loc = label->lab_rect.r_ll;
rp->rp_connect = connect;
rp->rp_nodename = node->name;
pX->portList = rp;
return 0; return 0;
} }
/* /*
*--------------------------------------------------------------------------- *---------------------------------------------------------------------------
* *
@ -625,7 +433,6 @@ ResFindNewContactTiles(contacts)
tile = PlaneGetHint(ResDef->cd_planes[pNum]); tile = PlaneGetHint(ResDef->cd_planes[pNum]);
GOTOPOINT(tile, &(contacts->cp_center)); GOTOPOINT(tile, &(contacts->cp_center));
PlaneSetHint(ResDef->cd_planes[pNum], tile);
#ifdef PARANOID #ifdef PARANOID
if (tile == (Tile *) NULL) if (tile == (Tile *) NULL)
{ {
@ -697,10 +504,10 @@ ResFindNewContactTiles(contacts)
*/ */
int int
ResProcessTiles(resisdata, origin, devNodeTable) ResProcessTiles(resisdata, origin)
ResisData *resisdata;
Point *origin; Point *origin;
HashTable *devNodeTable; ResisData *resisdata;
{ {
Tile *startTile; Tile *startTile;
int tilenum, merged; int tilenum, merged;
@ -717,8 +524,7 @@ ResProcessTiles(resisdata, origin, devNodeTable)
if (startTile == NULL) if (startTile == NULL)
return 1; return 1;
resCurrentNode = NULL; resCurrentNode = NULL;
ResStartTile(startTile, origin->p_x, origin->p_y); (void) ResEachTile(startTile, origin);
(void) ResEachTile(startTile, devNodeTable);
} }
#ifdef PARANOID #ifdef PARANOID
else else
@ -755,7 +561,7 @@ ResProcessTiles(resisdata, origin, devNodeTable)
if ((ri->ri_status & RES_TILE_DONE) == 0) if ((ri->ri_status & RES_TILE_DONE) == 0)
{ {
resCurrentNode = resptr2; resCurrentNode = resptr2;
merged |= ResEachTile(tile, devNodeTable); merged |= ResEachTile(tile, (Point *)NULL);
} }
} }
rj->rj_status = TRUE; rj->rj_status = TRUE;
@ -781,7 +587,7 @@ ResProcessTiles(resisdata, origin, devNodeTable)
if (cp->cp_cnode[tilenum] == resptr2) if (cp->cp_cnode[tilenum] == resptr2)
{ {
resCurrentNode = resptr2; resCurrentNode = resptr2;
merged |= ResEachTile(tile, devNodeTable); merged |= ResEachTile(tile, (Point *)NULL);
} }
else else
{ {
@ -864,7 +670,7 @@ ResCalcPerimOverlap(tile, dev)
} }
/* right */ /* right */
for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp = LB(tp)) for (tp = TR(tile); TOP(tp) > BOTTOM(tile); tp=LB(tp))
{ {
if TTMaskHasType(omask, TiGetLeftType(tp)) if TTMaskHasType(omask, TiGetLeftType(tp))
overlap += MIN(TOP(tile), TOP(tp)) - MAX(BOTTOM(tile), BOTTOM(tp)); overlap += MIN(TOP(tile), TOP(tp)) - MAX(BOTTOM(tile), BOTTOM(tp));
@ -878,7 +684,7 @@ ResCalcPerimOverlap(tile, dev)
} }
/* bottom */ /* bottom */
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp=TR(tp))
{ {
if TTMaskHasType(omask, TiGetTopType(tp)) if TTMaskHasType(omask, TiGetTopType(tp))
overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp)); overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp));
@ -937,7 +743,6 @@ resMakeDevFunc(tile, dinfo, cx)
{ {
if (DBPlane(ttype) != DBPlane(thisDev->type)) if (DBPlane(ttype) != DBPlane(thisDev->type))
return 0; /* Completely different device? */ return 0; /* Completely different device? */
thisDev->type = ttype; thisDev->type = ttype;
} }
@ -1210,10 +1015,6 @@ ResExtractNet(node, resisdata, cellname)
int pNum; int pNum;
int resMakeDevFunc(); int resMakeDevFunc();
int resExpandDevFunc(); int resExpandDevFunc();
int result;
HashTable DevNodeTable;
HashSearch hs;
HashEntry *he;
/* Make sure all global network variables are reset */ /* Make sure all global network variables are reset */
@ -1268,22 +1069,15 @@ ResExtractNet(node, resisdata, cellname)
/* Copy Paint */ /* Copy Paint */
/* If the node location is INFINITY, then use the first drivepoint */ /* If the node location is INFINITY, then use the rs_bbox */
if ((node->location.p_x == INFINITY) || (node->location.p_y == INFINITY)) if ((node->location.p_x == INFINITY) || (node->location.p_y == INFINITY))
{ {
ResConnect *rdriver = node->drivepoints; scx.scx_area.r_ll.p_x = node->rs_bbox.r_xbot;
if (rdriver) scx.scx_area.r_ll.p_y = node->rs_bbox.r_ybot;
{ scx.scx_area.r_ur.p_x = node->rs_bbox.r_xtop;
scx.scx_area.r_ll.p_x = rdriver->rc_rect.r_xbot - 2; scx.scx_area.r_ur.p_y = node->rs_bbox.r_ytop;
scx.scx_area.r_ll.p_y = rdriver->rc_rect.r_ybot - 2; startpoint = node->drivepoint;
scx.scx_area.r_ur.p_x = rdriver->rc_rect.r_xtop + 2;
scx.scx_area.r_ur.p_y = rdriver->rc_rect.r_ytop + 2;
startpoint.p_x = (rdriver->rc_rect.r_xtop + rdriver->rc_rect.r_xbot) / 2;
startpoint.p_y = (rdriver->rc_rect.r_ytop + rdriver->rc_rect.r_ybot) / 2;
}
else
TxError("Internal error: Node location is set to infinity.\n");
} }
else else
{ {
@ -1306,41 +1100,15 @@ ResExtractNet(node, resisdata, cellname)
DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect, DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect,
SEL_DO_LABELS, ResUse); SEL_DO_LABELS, ResUse);
} }
else if (node->drivepoints)
{
/* Use the first valid drivepoint */
ResConnect *drivepoint = node->drivepoints;
while (drivepoint && (drivepoint->rc_type == TT_SPACE))
drivepoint = drivepoint->rc_next;
if (drivepoint)
{
TTMaskZero(&FirstTileMask);
TTMaskSetMask(&FirstTileMask, &DBConnectTbl[drivepoint->rc_type]);
DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect, TTMaskZero(&ResSDTypesBitMask);
SEL_DO_LABELS, ResUse);
}
else
{
TxError("Node %s: Did not find the net layout at any drivepoint.\n",
node->name);
return TRUE;
}
}
else
{
TxError("Node %s: Did not find the net layout at node location (%d %d).\n",
node->name, node->location.p_x, node->location.p_y);
return TRUE;
}
TTMaskZero(&ResTermTypesBitMask);
TTMaskZero(&ResSubTypesBitMask); TTMaskZero(&ResSubTypesBitMask);
/* Add devices to ResUse from list in node */ /* Add devices to ResUse from list in node */
DevTiles = NULL; DevTiles = NULL;
for (tptr = node->devices; tptr; tptr = tptr->nextDev) for (tptr = node->firstDev; tptr; tptr = tptr->nextDev)
{ {
int result;
int i; int i;
ExtDevice *devptr; ExtDevice *devptr;
@ -1363,27 +1131,6 @@ ResExtractNet(node, resisdata, cellname)
freeMagic(thisDev); freeMagic(thisDev);
continue; continue;
} }
else if (thisDev->type != tptr->thisDev->rs_ttype)
{
/* The type changed. Note that when reading the .ext file, only
* the device name is given. If the device name maps to multiple
* entries, then it may point to the wrong type. Regardless of
* the reason, rewrite the tptr->thisDev and local thisDev records
* to match the actual device at the location.
*/
tptr->thisDev->rs_ttype = thisDev->type;
for (devptr = ExtCurStyle->exts_device[thisDev->type]; devptr;
devptr = devptr->exts_next)
{
if (!strcmp(devptr->exts_deviceName,
tptr->thisDev->rs_devptr->exts_deviceName))
{
tptr->thisDev->rs_devptr = devptr;
thisDev->devptr = devptr;
break;
}
}
}
thisDev->nextDev = DevTiles; thisDev->nextDev = DevTiles;
DevTiles = thisDev; DevTiles = thisDev;
@ -1391,13 +1138,13 @@ ResExtractNet(node, resisdata, cellname)
TTMaskSetOnlyType(&tMask, thisDev->type); TTMaskSetOnlyType(&tMask, thisDev->type);
DBTreeSrTiles(&scx, &tMask, 0, resExpandDevFunc, (ClientData)thisDev); DBTreeSrTiles(&scx, &tMask, 0, resExpandDevFunc, (ClientData)thisDev);
/* If the device has terminal types in a different plane than */ /* If the device has source/drain types in a different plane than */
/* the device identifier type, then add the terminal types to */ /* the device identifier type, then add the source/drain types to */
/* the mask ResTermTypesBitMask. */ /* the mask ResSDTypesBitMask. */
devptr = tptr->thisDev->rs_devptr; devptr = tptr->thisDev->rs_devptr;
for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++) for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
TTMaskSetMask(&ResTermTypesBitMask, &devptr->exts_deviceSDTypes[i]); TTMaskSetMask(&ResSDTypesBitMask, &devptr->exts_deviceSDTypes[i]);
/* Add the substrate types to the mask ResSubTypesBitMask */ /* Add the substrate types to the mask ResSubTypesBitMask */
TTMaskSetMask(&ResSubTypesBitMask, &devptr->exts_deviceSubstrateTypes); TTMaskSetMask(&ResSubTypesBitMask, &devptr->exts_deviceSubstrateTypes);
@ -1407,6 +1154,8 @@ ResExtractNet(node, resisdata, cellname)
} }
DBReComputeBbox(ResUse->cu_def); DBReComputeBbox(ResUse->cu_def);
ExtResetTiles(scx.scx_use->cu_def, CLIENTDEFAULT);
/* To avoid issues with overlapping stacked contact types and */ /* To avoid issues with overlapping stacked contact types and */
/* double-counting contacts on multiple planes, erase the top */ /* double-counting contacts on multiple planes, erase the top */
/* contact layers of all contacts. ExtFindRegions() will still */ /* contact layers of all contacts. ExtFindRegions() will still */
@ -1442,76 +1191,35 @@ ResExtractNet(node, resisdata, cellname)
ResDissolveContacts(ResContactList); ResDissolveContacts(ResContactList);
/* Fracture the plane to change maximum horizontal stripes to a /* Add "resInfo" fields to tiles */
* format better suited to tracking the path of current through
* the wiring.
*/
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++) for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{ {
Plane *plane = ResUse->cu_def->cd_planes[pNum]; Plane *plane = ResUse->cu_def->cd_planes[pNum];
Rect *rect = &ResUse->cu_def->cd_bbox; Rect *rect = &ResUse->cu_def->cd_bbox;
ResFracture(plane, rect); ResFracture(plane, rect);
(void) DBSrPaintClient((Tile *) NULL, plane, rect,
&DBAllButSpaceAndDRCBits,
(ClientData) CLIENTDEFAULT, ResAddPlumbing,
(ClientData) &ResDevList);
} }
/* Add "resInfo" fields to device tiles. */
for (thisDev = DevTiles; thisDev; thisDev = thisDev->nextDev)
ResAddDevPlumbing(thisDev, &ResDevList);
/* Add "resInfo" fields to any untouched tiles. */
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
DBSrPaintClient((Tile *)NULL,
ResUse->cu_def->cd_planes[pNum],
&TiPlaneRect, &DBAllButSpaceAndDRCBits,
(ClientData)CLIENTDEFAULT, ResAddPlumbing,
(ClientData)NULL);
/* If this is a top-level cell, then determine where connections /* If this is a top-level cell, then determine where connections
* are made into the cell from ports. Otherwise, determine points * are made into the cell from ports. Otherwise, determine points
* of entry by looking at how all parent cells connect to this * of entry by looking at how all parent cells connect to this
* cell. * cell.
*/ */
ResMakeDriverSinkPorts(ResUse->cu_def); ResMakePortBreakpoints(ResUse->cu_def);
ResMakeLabelPorts(ResUse->cu_def, resisdata); ResMakeLabelBreakpoints(ResUse->cu_def, resisdata);
/* Finish preprocessing. */ /* Finish preprocessing. */
ResFindNewContactTiles(ResContactList); ResFindNewContactTiles(ResContactList);
ResPreProcessDevices(DevTiles, ResDevList, ResUse->cu_def);
HashInit(&DevNodeTable, HT_DEFAULTSIZE, HT_WORDKEYS);
ResPreProcessDevices(DevTiles, ResDevList, ResUse->cu_def, &DevNodeTable);
/* do extraction */ /* do extraction */
result = ResProcessTiles(resisdata, &startpoint, &DevNodeTable); if (ResProcessTiles(resisdata, &startpoint) != 0) return TRUE;
/* Free remaining table entries (if any) */
HashStartSearch(&hs);
while ((he = HashNext(&DevNodeTable, &hs)) != NULL)
{
resDevTerm *resdevList, *resdevNext;
resdevList = (resDevTerm *)HashGetValue(he);
if (resdevList)
{
/* Diagnostic */
Tile *tp;
tp = (Tile *)he->h_key.h_ptr;
TxError("Error: %s tile at (%d %d) not visited!\n",
(resdevList->rdt_term < 0) ? "Substrate" : "Terminal",
tp->ti_ll.p_x, tp->ti_ll.p_y);
while (resdevList)
{
resdevNext = resdevList->rdt_next;
freeMagic((char *)resdevList);
resdevList = resdevNext;
}
}
}
HashKill(&DevNodeTable);
if (result != 0) return TRUE;
return FALSE; return FALSE;
} }
@ -1688,7 +1396,6 @@ FindStartTile(resisdata, SourcePoint)
{ {
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]); tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
GOTOPOINT(tile, &workingPoint); GOTOPOINT(tile, &workingPoint);
PlaneSetHint(ResUse->cu_def->cd_planes[pnum], tile);
SourcePoint->p_x = workingPoint.p_x; SourcePoint->p_x = workingPoint.p_x;
SourcePoint->p_y = workingPoint.p_y; SourcePoint->p_y = workingPoint.p_y;
@ -1703,13 +1410,13 @@ FindStartTile(resisdata, SourcePoint)
if (workingPoint.p_x == LEFT(tile)) if (workingPoint.p_x == LEFT(tile))
{ {
for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp = RT(tp)) for (tp = BL(tile); BOTTOM(tp) < TOP(tile); tp=RT(tp))
if (TiGetRightType(tp) == resisdata->rg_ttype) if (TiGetRightType(tp) == resisdata->rg_ttype)
return(tp); return(tp);
} }
else if (workingPoint.p_y == BOTTOM(tile)) else if (workingPoint.p_y == BOTTOM(tile))
{ {
for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp = TR(tp)) for (tp = LB(tile); LEFT(tp) < RIGHT(tile); tp=TR(tp))
if (TiGetTopType(tp) == resisdata->rg_ttype) if (TiGetTopType(tp) == resisdata->rg_ttype)
return(tp); return(tp);
} }
@ -1722,7 +1429,6 @@ FindStartTile(resisdata, SourcePoint)
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]); tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
GOTOPOINT(tile, &workingPoint); GOTOPOINT(tile, &workingPoint);
PlaneSetHint(ResUse->cu_def->cd_planes[pnum], tile);
if (IsSplit(tile)) if (IsSplit(tile))
{ {
@ -1766,7 +1472,7 @@ FindStartTile(resisdata, SourcePoint)
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
{ {
SourcePoint->p_x = LEFT(tile); SourcePoint->p_x = LEFT(tile);
SourcePoint->p_y = (MIN(TOP(tile), TOP(tp)) + SourcePoint->p_y = (MIN(TOP(tile),TOP(tp)) +
MAX(BOTTOM(tile), BOTTOM(tp))) >> 1; MAX(BOTTOM(tile), BOTTOM(tp))) >> 1;
return(tp); return(tp);
} }
@ -1809,7 +1515,7 @@ FindStartTile(resisdata, SourcePoint)
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
{ {
SourcePoint->p_y = TOP(tile); SourcePoint->p_y = TOP(tile);
SourcePoint->p_x = (MIN(RIGHT(tile), RIGHT(tp)) + SourcePoint->p_x = (MIN(RIGHT(tile),RIGHT(tp)) +
MAX(LEFT(tile), LEFT(tp))) >> 1; MAX(LEFT(tile), LEFT(tp))) >> 1;
return(tp); return(tp);
} }
@ -1865,7 +1571,7 @@ FindStartTile(resisdata, SourcePoint)
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
{ {
SourcePoint->p_x = LEFT(tile); SourcePoint->p_x = LEFT(tile);
SourcePoint->p_y = (MIN(TOP(tile), TOP(tp)) + SourcePoint->p_y = (MIN(TOP(tile),TOP(tp)) +
MAX(BOTTOM(tile), BOTTOM(tp))) >> 1; MAX(BOTTOM(tile), BOTTOM(tp))) >> 1;
while (!StackEmpty(devStack)) while (!StackEmpty(devStack))
{ {
@ -1933,7 +1639,7 @@ FindStartTile(resisdata, SourcePoint)
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2)) TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
{ {
SourcePoint->p_y = TOP(tile); SourcePoint->p_y = TOP(tile);
SourcePoint->p_x = (MIN(RIGHT(tile), RIGHT(tp)) + SourcePoint->p_x = (MIN(RIGHT(tile),RIGHT(tp)) +
MAX(LEFT(tile), LEFT(tp))) >> 1; MAX(LEFT(tile), LEFT(tp))) >> 1;
while (!StackEmpty(devStack)) while (!StackEmpty(devStack))
{ {
@ -2074,7 +1780,6 @@ ResGetDevice(pt, type)
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]); tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
GOTOPOINT(tile, &workingPoint); GOTOPOINT(tile, &workingPoint);
PlaneSetHint(ResUse->cu_def->cd_planes[pnum], tile);
const ClientData ticlient = TiGetClient(tile); const ClientData ticlient = TiGetClient(tile);
if (IsSplit(tile)) if (IsSplit(tile))

View File

@ -76,9 +76,10 @@ ResCalcTileResistance(tile, info, pendingList, doneList)
if (x < MinX) MinX = x; if (x < MinX) MinX = x;
if (y > MaxY) MaxY = y; if (y > MaxY) MaxY = y;
if (y < MinY) MinY = y; if (y < MinY) MinY = y;
if (p1->br_this->rn_why == RES_NODE_DEVICE) if (p1->br_this->rn_why == RES_NODE_DEVICE)
{
device = TRUE; device = TRUE;
}
} }
/* Finally, produce resistors for partition. Keep track of */ /* Finally, produce resistors for partition. Keep track of */
@ -124,7 +125,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
resNode **pendingList, **doneList; resNode **pendingList, **doneList;
resResistor **resList; resResistor **resList;
{ {
int count, height; int height;
bool merged; bool merged;
TileType ttype; TileType ttype;
Breakpoint *p1, *p2, *p3; Breakpoint *p1, *p2, *p3;
@ -133,8 +134,6 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
resNode *currNode; resNode *currNode;
float rArea; float rArea;
resInfo *info = (resInfo *)TiGetClientPTR(tile); resInfo *info = (resInfo *)TiGetClientPTR(tile);
HashTable BreakTable;
HashEntry *he;
merged = FALSE; merged = FALSE;
height = TOP(tile) - BOTTOM(tile); height = TOP(tile) - BOTTOM(tile);
@ -164,22 +163,15 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
ttype = TiGetTypeExact(tile); ttype = TiGetTypeExact(tile);
/* Re-sort nodes left to right. */ /* Re-sort nodes left to right. */
count = ResSortBreaks(&info->breakList, TRUE);
/* For long lists (defined as >= 16 entries), make a hash table of ResSortBreaks(&info->breakList, TRUE);
* the node pointer conversions so that each node can be updated
* as we walk the list, instead of walking the rest of the list in
* a nested loop for each entry.
*/
if (count >= 16)
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_WORDKEYS);
/* /*
* Eliminate breakpoints with the same X coordinate and merge * Eliminate breakpoints with the same X coordinate and merge
* their nodes. * their nodes.
*/ */
p2 = info->breakList; p2= info->breakList;
/* Add extra left area to leftmost node */ /* Add extra left area to leftmost node */
@ -188,18 +180,6 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
{ {
p1 = p2; p1 = p2;
p2 = p2->br_next; p2 = p2->br_next;
/* Has the node been recorded as needing to be replaced? */
if (count >= 16)
{
while (TRUE)
{
he = HashLookOnly(&BreakTable, (char *)p2->br_this);
if (!he) break;
p2->br_this = (resNode *)HashGetValue(he);
}
}
if (p2->br_loc.p_x == p1->br_loc.p_x) if (p2->br_loc.p_x == p1->br_loc.p_x)
{ {
if (p2->br_this == p1->br_this) if (p2->br_this == p1->br_this)
@ -235,38 +215,25 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
/* /*
* Was the node used in another info or breakpoint? * Was the node used in another info or breakpoint?
* If so, replace the old node with the new one. * If so, replace the old node with the new one.
*
* Short lists: Walk the list to the end and change
* nodes on the fly.
* Long lists: Record the change to be made in the
* hash table so that it can be executed as each list
* entry is encountered.
*/ */
if (count >= 16)
p3 = p2->br_next;
while (p3 != NULL)
{ {
he = HashFind(&BreakTable, (char *)currNode); if (p3->br_this == currNode)
HashSetValue(he, (char *)p2->br_this); p3->br_this = p2->br_this;
p3 = p3->br_next;
} }
else }
{
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
p3 = p3->br_next; /*
} * If the X coordinates don't match, make a resistor between
} * the breakpoints.
} */
/* else
* If the X coordinates don't match, make a resistor between {
* the breakpoints.
*/
else
{
resistor = (resResistor *)mallocMagic((unsigned)sizeof(resResistor)); resistor = (resResistor *)mallocMagic((unsigned)sizeof(resResistor));
resistor->rr_nextResistor = (*resList); resistor->rr_nextResistor = (*resList);
resistor->rr_lastResistor = NULL; resistor->rr_lastResistor = NULL;
@ -309,8 +276,6 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
} }
} }
if (count >= 16) HashKill(&BreakTable);
p2->br_this->rn_float.rn_area += height * (RIGHT(tile) - p2->br_loc.p_x); p2->br_this->rn_float.rn_area += height * (RIGHT(tile) - p2->br_loc.p_x);
freeMagic((char *)p2); freeMagic((char *)p2);
info->breakList = NULL; info->breakList = NULL;
@ -336,7 +301,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
resNode **pendingList, **doneList; resNode **pendingList, **doneList;
resResistor **resList; resResistor **resList;
{ {
int count, width; int width;
bool merged; bool merged;
TileType ttype; TileType ttype;
Breakpoint *p1, *p2, *p3; Breakpoint *p1, *p2, *p3;
@ -345,8 +310,6 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
resNode *currNode; resNode *currNode;
float rArea; float rArea;
resInfo *info = (resInfo *)TiGetClientPTR(tile); resInfo *info = (resInfo *)TiGetClientPTR(tile);
HashTable BreakTable;
HashEntry *he;
merged = FALSE; merged = FALSE;
width = RIGHT(tile) - LEFT(tile); width = RIGHT(tile) - LEFT(tile);
@ -366,15 +329,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
} }
/* Re-sort nodes south to north. */ /* Re-sort nodes south to north. */
count = ResSortBreaks(&info->breakList, FALSE); ResSortBreaks(&info->breakList, FALSE);
/* For long lists (defined as >= 16 entries), make a hash table of
* the node pointer conversions so that each node can be updated
* as we walk the list, instead of walking the rest of the list in
* a nested loop for each entry.
*/
if (count >= 16)
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_WORDKEYS);
/* Simplified split tile handling */ /* Simplified split tile handling */
if (IsSplit(tile)) if (IsSplit(tile))
@ -400,75 +355,49 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
{ {
p1 = p2; p1 = p2;
p2 = p2->br_next; p2 = p2->br_next;
/* Has the node been recorded as needing to be replaced? */
if (count >= 16)
{
while (TRUE)
{
he = HashLookOnly(&BreakTable, (char *)p2->br_this);
if (!he) break;
p2->br_this = (resNode *)HashGetValue(he);
}
}
if (p1->br_loc.p_y == p2->br_loc.p_y) if (p1->br_loc.p_y == p2->br_loc.p_y)
{ {
if (p2->br_this == p1->br_this) if (p2->br_this == p1->br_this)
{ {
currNode = NULL; currNode = NULL;
p1->br_next = p2->br_next; p1->br_next = p2->br_next;
freeMagic((char *)p2); freeMagic((char *)p2);
p2 = p1; p2 = p1;
} }
else if (p2->br_this == resCurrentNode) else if (p2->br_this == resCurrentNode)
{ {
currNode = p1->br_this; currNode = p1->br_this;
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList); ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
freeMagic((char *)p1); freeMagic((char *)p1);
merged = TRUE; merged = TRUE;
} }
else if (p1->br_this == resCurrentNode) else if (p1->br_this == resCurrentNode)
{ {
currNode = p2->br_this; currNode = p2->br_this;
p1->br_next = p2->br_next; p1->br_next = p2->br_next;
ResMergeNodes(p1->br_this, p2->br_this, pendingList, doneList); ResMergeNodes(p1->br_this, p2->br_this, pendingList, doneList);
merged = TRUE; merged = TRUE;
freeMagic((char *)p2); freeMagic((char *)p2);
p2 = p1; p2 = p1;
} }
else else
{ {
currNode = p1->br_this; currNode = p1->br_this;
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList); ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
freeMagic((char *)p1); freeMagic((char *)p1);
} }
/* /*
* Was the node used in another info or breakpoint? * Was the node used in another info or breakpoint?
* If so, replace the old node with the new one. * If so, replace the old node with the new one.
*
* Short lists: Walk the list to the end and change
* nodes on the fly.
* Long lists: Record the change to be made in the
* hash table so that it can be executed as each list
* entry is encountered.
*/ */
if (count >= 16) p3 = p2->br_next;
while (p3 != NULL)
{ {
he = HashFind(&BreakTable, (char *)currNode); if (p3->br_this == currNode)
HashSetValue(he, (char *)p2->br_this); p3->br_this = p2->br_this;
}
else
{
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
p3 = p3->br_next; p3 = p3->br_next;
}
} }
} }
@ -562,7 +491,7 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
* breakpoint, then return. * breakpoint, then return.
*/ */
if (info->breakList->br_next == NULL) if (info->breakList->br_next == NULL)
{ {
freeMagic((char *)info->breakList); freeMagic((char *)info->breakList);
info->breakList = NULL; info->breakList = NULL;
@ -691,7 +620,7 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
} }
else else
{ {
deltay = 0; deltay=0;
} }
} }
else else
@ -705,15 +634,15 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
{ {
if (p2->br_crect->r_ll.p_y > p1->br_loc.p_y) if (p2->br_crect->r_ll.p_y > p1->br_loc.p_y)
{ {
deltay = MIN(deltay, p2->br_crect->r_ll.p_y - p1->br_loc.p_y); deltay = MIN(deltay,p2->br_crect->r_ll.p_y - p1->br_loc.p_y);
} }
else if (p2->br_crect->r_ur.p_y < p1->br_loc.p_y) else if (p2->br_crect->r_ur.p_y < p1->br_loc.p_y)
{ {
deltay = MIN(deltay, p1->br_loc.p_y - p2->br_crect->r_ur.p_y); deltay = MIN(deltay,p1->br_loc.p_y - p2->br_crect->r_ur.p_y);
} }
else else
{ {
deltay = 0; deltay=0;
} }
} }
else else
@ -891,7 +820,7 @@ ResDoContacts(contact, nodes, resList)
Tile *tile = contact->cp_tile[tilenum]; Tile *tile = contact->cp_tile[tilenum];
contact->cp_cnode[tilenum] = resptr; contact->cp_cnode[tilenum] = resptr;
ResNewBreak(resptr, tile, contact->cp_center.p_x, NEWBREAK(resptr, tile, contact->cp_center.p_x,
contact->cp_center.p_y, &contact->cp_rect); contact->cp_center.p_y, &contact->cp_rect);
} }
} }
@ -940,7 +869,7 @@ ResDoContacts(contact, nodes, resList)
ccell->ce_thisc = contact; ccell->ce_thisc = contact;
contact->cp_cnode[tilenum] = resptr; contact->cp_cnode[tilenum] = resptr;
ResNewBreak(resptr, tile, contact->cp_center.p_x, NEWBREAK(resptr, tile, contact->cp_center.p_x,
contact->cp_center.p_y, &contact->cp_rect); contact->cp_center.p_y, &contact->cp_rect);
/* Add resistors here */ /* Add resistors here */
@ -1164,14 +1093,12 @@ MergeSortBreaks(Breakpoint *list, int xsort)
* bottleneck. * bottleneck.
* *
* Results: * Results:
* Return the length of the list (maximum truncated at 16) so that * None
* the calling routine can determine if this is a long or a short
* linked list and treat it accordingly.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
int void
ResSortBreaks(masterlist, xsort) ResSortBreaks(masterlist, xsort)
Breakpoint **masterlist; Breakpoint **masterlist;
int xsort; int xsort;
@ -1186,7 +1113,7 @@ ResSortBreaks(masterlist, xsort)
if (count > 16) if (count > 16)
{ {
*masterlist = MergeSortBreaks(*masterlist, xsort); *masterlist = MergeSortBreaks(*masterlist, xsort);
return count; return;
} }
} }
@ -1227,6 +1154,5 @@ ResSortBreaks(masterlist, xsort)
} }
} }
} }
return count;
} }

View File

@ -210,7 +210,7 @@ ResFixParallel(elimResis, newResis)
* ResSeriesCheck -- for nodes with no devices, sees if a series * ResSeriesCheck -- for nodes with no devices, sees if a series
* or loop combination is possible. * or loop combination is possible.
* *
* Results: returns SINGLE, LOOP, or SERIES if succesful. * Results: returns SINGLE,LOOP,or SERIES if succesful.
* *
* Side Effects: may delete some nodes and resistors. * Side Effects: may delete some nodes and resistors.
* *
@ -431,7 +431,7 @@ ResParallelCheck(resptr)
HashEntry *he; HashEntry *he;
/* Hash the connections */ /* Hash the connections */
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_WORDKEYS); HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl) for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl)
{ {
@ -549,7 +549,7 @@ ResTriangleCheck(resptr)
HashSearch hs; HashSearch hs;
/* Hash the neighboring connections */ /* Hash the neighboring connections */
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_WORDKEYS); HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl) for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl)
{ {
@ -839,9 +839,9 @@ ResTriangleCheck(resptr)
* *
* ResMergeNodes-- * ResMergeNodes--
* *
* Results: none * results: none
* *
* Side Effects: appends all the cElement, jElement, tElement and * side effects: appends all the cElement, jElement, tElement and
* resElement structures from node 2 onto node 1. Node 2 is * resElement structures from node 2 onto node 1. Node 2 is
* then eliminated. * then eliminated.
* *
@ -995,9 +995,6 @@ ResMergeNodes(node1, node2, pendingList, doneList)
node2->rn_client = (ClientData)NULL; node2->rn_client = (ClientData)NULL;
} }
/* Don't merge away the ResNodeAtOrigin node */
if (ResNodeAtOrigin == node2) ResNodeAtOrigin = node1;
node2->rn_re = (resElement *)CLIENTDEFAULT; node2->rn_re = (resElement *)CLIENTDEFAULT;
node2->rn_ce = (cElement *)CLIENTDEFAULT; node2->rn_ce = (cElement *)CLIENTDEFAULT;
node2->rn_je = (jElement *)CLIENTDEFAULT; node2->rn_je = (jElement *)CLIENTDEFAULT;
@ -1013,7 +1010,7 @@ ResMergeNodes(node1, node2, pendingList, doneList)
* ResDeleteResPointer-- Deletes the pointer from a node to a resistor. * ResDeleteResPointer-- Deletes the pointer from a node to a resistor.
* Used when a resistor is deleted. * Used when a resistor is deleted.
* *
* Results: none * Results:none
* *
* Side Effects: Modifies a node's resistor list. * Side Effects: Modifies a node's resistor list.
* *
@ -1021,7 +1018,7 @@ ResMergeNodes(node1, node2, pendingList, doneList)
*/ */
void void
ResDeleteResPointer(node, resistor) ResDeleteResPointer(node,resistor)
resNode *node; resNode *node;
resResistor *resistor; resResistor *resistor;
@ -1062,7 +1059,7 @@ ResDeleteResPointer(node, resistor)
* *
* ResEliminateResistor-- * ResEliminateResistor--
* *
* Results: none * Results:none
* *
* Side Effects: Deletes a resistor. Does not delete pointers from nodes to * Side Effects: Deletes a resistor. Does not delete pointers from nodes to
* resistor. * resistor.
@ -1100,7 +1097,8 @@ ResEliminateResistor(resistor, homelist)
* they are no longer needed. If the 'info' option is used, * they are no longer needed. If the 'info' option is used,
* the node is eradicated. * the node is eradicated.
* *
* Results: none. * Results:
* None.
* *
* Side Effects: frees memory * Side Effects: frees memory
* *

View File

@ -52,7 +52,7 @@ ResPrintExtRes(outextfile, resistors, nodename)
char *nodename; char *nodename;
{ {
int nodenum = 0; int nodenum=0;
char newname[MAXNAME]; char newname[MAXNAME];
HashEntry *entry; HashEntry *entry;
ResExtNode *node; ResExtNode *node;
@ -261,7 +261,7 @@ ResPrintExtNode(outextfile, nodelist, node)
{ {
if (snode->rn_name == NULL) if (snode->rn_name == NULL)
{ {
(void)sprintf(tmpname, "%s", nodename); (void)sprintf(tmpname,"%s",nodename);
cp = tmpname + strlen(tmpname) - 1; cp = tmpname + strlen(tmpname) - 1;
if (*cp == '!' || *cp == '#') *cp = '\0'; if (*cp == '!' || *cp == '#') *cp = '\0';
@ -342,13 +342,13 @@ ResPrintStats(resisdata, name)
nodes = 0; nodes = 0;
resistors = 0; resistors = 0;
totalnets++; totalnets++;
for (node = ResNodeList; node != NULL; node = node->rn_more) for (node = ResNodeList; node != NULL; node=node->rn_more)
{ {
nodes++; nodes++;
totalnodes++; totalnodes++;
} }
for (res = ResResList; res != NULL; res = res->rr_nextResistor) for (res = ResResList; res != NULL; res=res->rr_nextResistor)
{ {
resistors++; resistors++;
totalresistors++; totalresistors++;

View File

@ -61,7 +61,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#define DEV_PARAM_START 7 #define DEV_PARAM_START 7
#define NODES_NODENAME 1 #define NODES_NODENAME 1
#define NODES_NODERES 2
#define NODES_NODEX 4 #define NODES_NODEX 4
#define NODES_NODEY 5 #define NODES_NODEY 5
#define NODES_NODETYPE 6 #define NODES_NODETYPE 6
@ -83,24 +82,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
#define PORT_URY 6 #define PORT_URY 6
#define PORT_TYPE 7 #define PORT_TYPE 7
#define USE_DEF_NAME 1
#define USE_ID_NAME 2
#define USE_TRANSFORM_A 3
#define USE_TRANSFORM_B 4
#define USE_TRANSFORM_C 5
#define USE_TRANSFORM_D 6
#define USE_TRANSFORM_E 7
#define USE_TRANSFORM_F 8
/* Note that "connect" lines may repeat these six entries up to argc */
#define CONNECT_LLX 1
#define CONNECT_LLY 2
#define CONNECT_URX 3
#define CONNECT_URY 4
#define CONNECT_TYPE 5
#define CONNECT_UP_NAME 6
#define CONNECT_DOWN_NAME 7
#define MAXDIGIT 20 #define MAXDIGIT 20
ResExtNode *ResOriginalNodes; /*Linked List of Nodes */ ResExtNode *ResOriginalNodes; /*Linked List of Nodes */
@ -110,18 +91,11 @@ ResFixPoint *ResFixList;
/* /*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
* *
* ResReadExt -- * ResReadExt--
* *
* Read a .ext file for resistance extraction. Extresist does not use * Results: returns 0 if ext file is correct, 1 if not.
* the .ext file reader in extflat/EFread.c because it takes only a
* small amount of information from the .ext file, mainly to keep a
* list of nets and net names, devices and their terminals and
* connections, and subcell connections. However, it does make use
* of the line parser and tokenizer in extflat.
* *
* Results: Returns 0 if ext file is correct, 1 if not. * Side Effects:Reads in ExtTable and makes a hash table of nodes.
*
* Side Effects: Creates lists of nodes and devices for extresist.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -133,11 +107,8 @@ ResReadExt(CellDef *def)
int result, locresult; int result, locresult;
int argc, n, size = 0; int argc, n, size = 0;
FILE *fp; FILE *fp;
CellDef *dbdef, *parent; CellDef *dbdef;
CellUse *use;
ResExtNode *curnode; ResExtNode *curnode;
HashTable parentHash;
HashEntry *he;
/* Search for the .ext file in the same way that efReadDef() does. */ /* Search for the .ext file in the same way that efReadDef() does. */
@ -171,10 +142,7 @@ ResReadExt(CellDef *def)
} }
/* We don't care about most tokens, only DEVICE, NODE, PORT, /* We don't care about most tokens, only DEVICE, NODE, PORT,
* and SUBSTRATE; and CONNECT is used to locate sink points. * and SUBSTRATE; and MERGE is used to locate drive points.
* Note that MERGE is not useful here, as it may implicitly
* merge nets in the cell, which is useful for netlisting but
* not for annotating the extraction file.
*/ */
switch (keyTable[n].k_key) switch (keyTable[n].k_key)
{ {
@ -193,16 +161,17 @@ ResReadExt(CellDef *def)
case FET: case FET:
locresult = ResReadFET(argc, argv); locresult = ResReadFET(argc, argv);
break; break;
case CONNECT: case MERGE:
locresult = ResReadConnectPoint(def, argc, argv); /* To be completed */
break; /* ResReadDrivePoint(argc, argv); */
case PORT:
locresult = ResReadPort(argc, argv);
break; break;
case NODE: case NODE:
case SUBSTRATE: case SUBSTRATE:
curnode = ResReadNode(argc, argv); curnode = ResReadNode(argc, argv);
break; break;
case PORT:
locresult = ResReadPort(argc, argv);
break;
case ATTR: case ATTR:
locresult = ResReadAttribute(curnode, argc, argv); locresult = ResReadAttribute(curnode, argc, argv);
break; break;
@ -215,293 +184,9 @@ ResReadExt(CellDef *def)
if (locresult == 1) result = 1; if (locresult == 1) result = 1;
} }
fclose(fp); fclose(fp);
/* Find all the parent CellDefs of "def" and read the .ext file of
* each one to find where connections are made to this cell from
* parent cells. Place drive points at each connection point.
*/
HashInit(&parentHash, 32, HT_STRINGKEYS);
for (use = def->cd_parents; use; use = use->cu_nextuse)
{
if ((parent = use->cu_parent) == NULL) continue;
if (parent->cd_flags & CDINTERNAL) continue;
he = HashFind(&parentHash, parent->cd_name);
if ((CellDef *)HashGetValue(he) == NULL)
{
/* Mark parent def as being visited */
HashSetValue(he, (char *)parent);
/* Read connection information from the parent's .ext file */
ResReadParentExt(parent, def);
}
}
HashKill(&parentHash);
return(result); return(result);
} }
/*
*-------------------------------------------------------------------------
*
* ResReadUse --
*
* Read a "use" statement from the .ext file of a parent CellDef of
* the current def being extracted. If the use is a use of the
* current def, then save the use name and its transform in the
* hash table so that later "connect" statements can be translated
* into the coordinate system of the current cell def.
*
* Results:
* 1 if something went wrong with the parsing, 0 otherwise.
*
* Side effects:
* May add to the hash table.
*
*-------------------------------------------------------------------------
*/
int
ResReadUse(CellDef *def,
int argc,
char *argv[],
HashTable *useHash)
{
char *defname, *useid;
Transform *tinv, t;
HashEntry *he;
defname = argv[USE_DEF_NAME];
if (strcmp(defname, def->cd_name)) return 0; /* Not my use */
useid = argv[USE_ID_NAME];
he = HashFind(useHash, useid);
t.t_a = atoi(argv[USE_TRANSFORM_A]);
t.t_b = atoi(argv[USE_TRANSFORM_B]);
t.t_c = atoi(argv[USE_TRANSFORM_C]);
t.t_d = atoi(argv[USE_TRANSFORM_D]);
t.t_e = atoi(argv[USE_TRANSFORM_E]);
t.t_f = atoi(argv[USE_TRANSFORM_F]);
tinv = (Transform *)mallocMagic(sizeof(Transform));
GeoInvertTrans(&t, tinv);
HashSetValue(he, (char *)tinv);
return 0;
}
/*
*-------------------------------------------------------------------------
*
* ResReadDrivePoint --
*
* Read a "connect" statement from the .ext file of a parent CellDef
* of the current def being extracted. If the connection is made to
* a use of the current def, then translate the area of the connection
* into the current def, and mark the connection as a drive point of
* def.
*
* Results:
* 1 if something went wrong with the parsing, 0 otherwise.
*
* Side effects:
* May add information to the node list of def.
*
*-------------------------------------------------------------------------
*/
int
ResReadDrivePoint(CellDef *def,
int argc,
char *argv[],
HashTable *useHash)
{
HashEntry *entry;
ResExtNode *node;
ResConnect *newdriver;
int pNum;
TileType ttype;
Transform *t;
Rect r;
char *hierptr, *useid, *qptr, *downname;
/* Only handle entries that are in the use ID hash table */
useid = argv[CONNECT_DOWN_NAME];
if (*useid == '"') useid++;
hierptr = strchr(useid, '/');
if (hierptr != NULL) *hierptr = '\0';
qptr = strrchr(useid, '"');
if (qptr != NULL) *qptr = '\0';
if (hierptr != NULL)
downname = hierptr + 1;
else
downname = useid; /* This is probably invalid */
entry = HashFind(useHash, useid);
if ((t = (Transform *)HashGetValue(entry)) == NULL) return 0;
/* Check for the given tile type */
ttype = DBTechNoisyNameType(argv[CONNECT_TYPE]);
if (ttype == -1)
{
TxError("Bad tile type name \"%s\" in .ext file for node %s\n",
argv[CONNECT_TYPE], argv[CONNECT_UP_NAME]);
return 1;
}
/* Look up the node name */
if (strcmp(downname, "None"))
{
entry = HashLookOnly(&ResNodeTable, downname);
if (entry != NULL)
node = (ResExtNode *)HashGetValue(entry);
else
{
TxError("Unknown node name \"%s\" in .ext file connect entry\n",
downname);
return 1;
}
/* Generate new drivepoint entry */
newdriver = (ResConnect *)mallocMagic(sizeof(ResConnect));
r.r_xbot = atoi(argv[CONNECT_LLX]);
r.r_ybot = atoi(argv[CONNECT_LLY]);
r.r_xtop = atoi(argv[CONNECT_URX]);
r.r_ytop = atoi(argv[CONNECT_URY]);
/* Translate the connection position from the parent to the
* current cell def.
*/
GeoTransRect(t, &r, &newdriver->rc_rect);
newdriver->rc_type = ttype;
newdriver->rc_node = (resNode *)NULL;
newdriver->rc_next = node->drivepoints;
node->drivepoints = newdriver;
node->status |= FORCE | DRIVELOC;
if (ResOptionsFlags & ResOpt_Debug)
{
/* Diagnostic */
TxPrintf("Added driver at %d %d %d %d\n",
newdriver->rc_rect.r_xbot, newdriver->rc_rect.r_ybot,
newdriver->rc_rect.r_xtop, newdriver->rc_rect.r_ytop);
}
}
return 0;
}
/*
*-------------------------------------------------------------------------
*
* ResReadParentExt --
*
* Read a .ext file for a parent cell of the cell being extracted.
* Each .ext file contains a list of connection points into its
* subcells. However, no .ext file has information about how a
* parent cell connects to it; the exact connection may depend on
* the layout, and may or may not coincide with marked ports.
* Except for the top level cell, for which only marked ports can
* be used to guess at intended points of connection, every subcell
* can query its parents to find exact points of connection.
*
* Results: Returns 0 if ext file is correct, 1 if not.
*
* Side Effects: Creates lists of connection points for extresist.
*
*-------------------------------------------------------------------------
*/
int
ResReadParentExt(CellDef *parent,
CellDef *def)
{
char *line = NULL, *argv[128];
int result, locresult;
int argc, n, size = 0;
FILE *fp;
CellDef *dbdef;
ResExtNode *curnode;
HashTable useHash;
HashEntry *he;
HashSearch hs;
/* Search for the .ext file in the same way that efReadDef() does. */
fp = ExtFileOpen(parent, (char *)NULL, "r", (char **)NULL);
if (fp == NULL)
{
TxError("Cannot open file %s%s\n", parent->cd_name, ".ext");
return 1;
}
HashInit(&useHash, 32, HT_STRINGKEYS);
/* Read in the file. Makes use of various functions
* from extflat, mostly in EFread.c.
*/
EFSaveLocs = FALSE;
efReadLineNum = 0;
result = 0;
while ((argc = efReadLine(&line, &size, fp, argv)) >= 0)
{
n = LookupStruct(argv[0], (const LookupTable *)keyTable, sizeof keyTable[0]);
if (n < 0)
{
efReadError("Unrecognized token \"%s\" (ignored)\n", argv[0]);
continue;
}
if (argc < keyTable[n].k_mintokens)
{
efReadError("Not enough tokens for %s line\n", argv[0]);
continue;
}
/* When reading a parent .ext file to find connections to
* the cell being extracted by "extresist", we only care
* about CONNECT lines, and USE lines so that we can
* translate the connection points into the current cell def.
*
* Note: This method depends on the .ext file format having
* all "use" lines before "connect" lines.
*/
switch (keyTable[n].k_key)
{
case USE:
locresult = ResReadUse(def, argc, argv, &useHash);
break;
case CONNECT:
locresult = ResReadDrivePoint(def, argc, argv, &useHash);
break;
default:
break;
}
if (locresult == 1) result = 1;
}
fclose(fp);
HashStartSearch(&hs);
while ((he = HashNext(&useHash, &hs)))
{
if (HashGetValue(he) != NULL)
{
freeMagic(HashGetValue(he)); /* Free the allocated tranform */
HashSetValue(he, (ClientData)NULL);
}
}
HashKill(&useHash);
return(result);
}
/* /*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
@ -522,18 +207,20 @@ ResReadNode(int argc, char *argv[])
{ {
HashEntry *entry; HashEntry *entry;
ResExtNode *node; ResExtNode *node;
int noderesist;
entry = HashFind(&ResNodeTable, argv[NODES_NODENAME]); entry = HashFind(&ResNodeTable, argv[NODES_NODENAME]);
node = ResExtInitNode(entry); node = ResExtInitNode(entry);
node->location.p_x = atoi(argv[NODES_NODEX]); node->location.p_x = atoi(argv[NODES_NODEX]);
node->location.p_y = atoi(argv[NODES_NODEY]); node->location.p_y = atoi(argv[NODES_NODEY]);
node->type = DBTechNameType(argv[NODES_NODETYPE]);
noderesist = atoi(argv[NODES_NODERES]); /* If this node was previously read as a port, then don't change the
if (noderesist < 0) noderesist = INFINITY; * node type, which is tracking the type at the drivepoint.
/* Make sure node resistance is in units of milliohms */ */
node->resistance = (float)noderesist * (float)ExtCurStyle->exts_resistScale; if (!(node->status & PORTNODE))
{
node->type = DBTechNameType(argv[NODES_NODETYPE]);
}
if (node->type == -1) if (node->type == -1)
{ {
@ -543,85 +230,6 @@ ResReadNode(int argc, char *argv[])
return node; return node;
} }
/*
*-------------------------------------------------------------------------
*
* ResReadConnectPoint-- Reads in a "connect" statement from the .ext file
* and sets node records accordingly to mark the node as a connection
* point. There is a use (instance) name associated with each connection,
* which is unused for finding connection points to subcells; we
* don't care what the subcell is, only that there is a connection at
* a point on a net in this cell that should be recorded and never
* optimized out.
*
* Results: 0 if successful and 1 otherwise.
*
* Side Effects: see above
*
*-------------------------------------------------------------------------
*/
int
ResReadConnectPoint(CellDef *def,
int argc,
char *argv[])
{
HashEntry *entry;
ResExtNode *node;
ResConnect *newsink;
int pNum;
TileType ttype;
/* Check for the given tile type */
ttype = DBTechNoisyNameType(argv[CONNECT_TYPE]);
if (ttype == -1)
{
TxError("Bad tile type name \"%s\" in .ext file for node %s\n",
argv[CONNECT_TYPE], argv[CONNECT_UP_NAME]);
return 1;
}
/* Look up the node name */
if (strcmp(argv[CONNECT_UP_NAME], "None"))
{
entry = HashLookOnly(&ResNodeTable, argv[CONNECT_UP_NAME]);
if (entry != NULL)
node = (ResExtNode *)HashGetValue(entry);
else
{
TxError("Unknown node name \"%s\" in .ext file connect entry\n",
argv[CONNECT_UP_NAME]);
return 1;
}
/* Generate new sinkpoint entry */
newsink = (ResConnect *)mallocMagic(sizeof(ResConnect));
newsink->rc_rect.r_xbot = atoi(argv[CONNECT_LLX]);
newsink->rc_rect.r_ybot = atoi(argv[CONNECT_LLY]);
newsink->rc_rect.r_xtop = atoi(argv[CONNECT_URX]);
newsink->rc_rect.r_ytop = atoi(argv[CONNECT_URY]);
newsink->rc_type = ttype;
newsink->rc_node = (resNode *)NULL;
newsink->rc_next = node->sinkpoints;
node->sinkpoints = newsink;
node->status |= FORCE | DRIVELOC;
if (ResOptionsFlags & ResOpt_Debug)
{
/* Diagnostic */
TxPrintf("Added sink at %d %d %d %d\n", newsink->rc_rect.r_xbot,
newsink->rc_rect.r_ybot, newsink->rc_rect.r_xtop,
newsink->rc_rect.r_ytop);
}
}
return 0;
}
/* /*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
* *
@ -632,13 +240,6 @@ ResReadConnectPoint(CellDef *def,
* *
* Side Effects: see above * Side Effects: see above
* *
* NOTE: The use of "port" to mark drive points is restricted to top
* level cells, because no other information is available about how the
* cell connects to a parent cell. For every cell other than the top
* level, the "connect" statements are used to find the actual locations
* where signals connect between cells through abutting or overlapping
* material.
*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
@ -648,33 +249,22 @@ ResReadPort(int argc,
{ {
HashEntry *entry; HashEntry *entry;
ResExtNode *node; ResExtNode *node;
ResConnect *newdriver;
entry = HashFind(&ResNodeTable, argv[PORT_NAME]); entry = HashFind(&ResNodeTable, argv[PORT_NAME]);
node = ResExtInitNode(entry); node = ResExtInitNode(entry);
/* Generate new drivepoint entry */ node->drivepoint.p_x = atoi(argv[PORT_LLX]);
node->drivepoint.p_y = atoi(argv[PORT_LLY]);
newdriver = (ResConnect *)mallocMagic(sizeof(ResConnect)); node->status |= FORCE;
/* To do: Check for multiple ports on a net; each port needs its
newdriver->rc_rect.r_xbot = atoi(argv[PORT_LLX]); * own drivepoint.
newdriver->rc_rect.r_ybot = atoi(argv[PORT_LLY]); */
newdriver->rc_rect.r_xtop = atoi(argv[PORT_URX]); node->status |= DRIVELOC | PORTNODE;
newdriver->rc_rect.r_ytop = atoi(argv[PORT_URY]); node->rs_bbox.r_ll = node->drivepoint;
newdriver->rc_type = DBTechNoisyNameType(argv[PORT_TYPE]); node->rs_bbox.r_ur.p_x = atoi(argv[PORT_URX]);
newdriver->rc_node = (resNode *)NULL; node->rs_bbox.r_ur.p_y = atoi(argv[PORT_URY]);
node->rs_ttype = DBTechNoisyNameType(argv[PORT_TYPE]);
newdriver->rc_next = node->drivepoints; node->type = node->rs_ttype;
node->drivepoints = newdriver;
node->status |= FORCE | DRIVELOC | PORTNODE;
if (ResOptionsFlags & ResOpt_Debug)
{
/* Diagnostic */
TxPrintf("Added port at %d %d %d %d\n",
newdriver->rc_rect.r_xbot, newdriver->rc_rect.r_ybot,
newdriver->rc_rect.r_xtop, newdriver->rc_rect.r_ytop);
}
if (node->type == -1) if (node->type == -1)
{ {
@ -683,7 +273,6 @@ ResReadPort(int argc,
} }
return 0; return 0;
} }
/* /*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
* *
@ -698,7 +287,7 @@ ResReadPort(int argc,
* None. * None.
* *
* Side effects: * Side effects:
* Allocates memory for a devPtr, adds to the node's "devices" linked * Allocates memory for a devPtr, adds to the node's firstDev linked
* list. * list.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
@ -713,8 +302,8 @@ ResNodeAddDevice(ResExtNode *node,
tptr = (devPtr *)mallocMagic((unsigned)(sizeof(devPtr))); tptr = (devPtr *)mallocMagic((unsigned)(sizeof(devPtr)));
tptr->thisDev = device; tptr->thisDev = device;
tptr->nextDev = node->devices; tptr->nextDev = node->firstDev;
node->devices = tptr; node->firstDev = tptr;
tptr->terminal = termtype; tptr->terminal = termtype;
} }
@ -737,11 +326,13 @@ ResReadDevice(int argc,
char *argv[]) char *argv[])
{ {
RDev *device; RDev *device;
int rvalue, i, j, k, w, l, n; int rvalue, i, j, k;
ExtDevice *devptr; ExtDevice *devptr;
TileType ttype; TileType ttype;
HashEntry *entry; HashEntry *entry;
ResExtNode *node; ResExtNode *node;
ResValue rpersquare;
float wval;
device = (RDev *)mallocMagic((unsigned)(sizeof(RDev))); device = (RDev *)mallocMagic((unsigned)(sizeof(RDev)));
@ -770,20 +361,34 @@ ResReadDevice(int argc,
device->drain = (ResExtNode *)NULL; device->drain = (ResExtNode *)NULL;
device->subs = (ResExtNode *)NULL; device->subs = (ResExtNode *)NULL;
/* Find the end of parameter arguments */ entry = HashLookOnly(&devptr->exts_deviceResist, "linear");
if (entry != NULL)
rpersquare = (ResValue)(spointertype)HashGetValue(entry);
else
rpersquare = (ResValue)10000.0; /* Default to a sane value */
/* For devices, the device width is in the parameter list */
wval = 0.0;
for (i = DEV_Y; i < argc; i++) for (i = DEV_Y; i < argc; i++)
{ {
char *eptr; char *eptr;
if ((eptr = strchr(argv[i], '=')) == NULL) if ((eptr = strchr(argv[i], '=')) != NULL)
if (!StrIsInt(argv[i])) {
break; if (*argv[i] == 'w')
sscanf(eptr + 1, "%f", &wval);
}
else if (!StrIsInt(argv[i]))
break;
} }
if (i == argc) if (i == argc)
{ {
TxError("Bad device %s: Too few arguments in .ext file\n", TxError("Bad device %s: Too few arguments in .ext file\n",
argv[DEV_NAME]); argv[DEV_NAME]);
return 1; return 1;
} }
else
device->resistance = wval * rpersquare; /* Channel resistance */
/* Find and record the device terminal nodes */ /* Find and record the device terminal nodes */
/* Note that this only records up to two terminals matching FET /* Note that this only records up to two terminals matching FET
@ -791,30 +396,16 @@ ResReadDevice(int argc,
* arbitrary number of terminals. * arbitrary number of terminals.
*/ */
/* See code in extflat/EFbuild.c: Devices do not necessarily if (strcmp(argv[i], "None"))
* declare a substrate terminal. This can be determined by
* noting that all terminals other than the substrate come in
* triplets of arguments, so if the remaining count of arguments
* is divisible by 3, then there is no substrate node, and if
* there is a remainder of 1, then there is. It would probably
* be simpler if all devices just put "None" in this position.
*/
n = argc - i;
if ((n % 3) == 1) /* Device has a substrate argument */
{ {
if (strcmp(argv[i], "None")) entry = HashFind(&ResNodeTable, argv[i]);
{ device->subs = (ResExtNode *)HashGetValue(entry);
entry = HashFind(&ResNodeTable, argv[i]); ResNodeAddDevice(device->subs, device, SUBS);
device->subs = (ResExtNode *)HashGetValue(entry);
ResNodeAddDevice(device->subs, device, SUBS);
}
i++;
} }
i++;
entry = HashFind(&ResNodeTable, argv[i]); entry = HashFind(&ResNodeTable, argv[i]);
device->gate = (ResExtNode *)HashGetValue(entry); device->gate = (ResExtNode *)HashGetValue(entry);
device->rs_gattr = StrDup((char **)NULL, argv[i + 2]); device->rs_gattr = StrDup((char **)NULL, argv[i + 2]);
l = atoi(argv[i + 1]);
w = 0;
ResNodeAddDevice(device->gate, device, GATE); ResNodeAddDevice(device->gate, device, GATE);
i += 3; i += 3;
@ -823,7 +414,6 @@ ResReadDevice(int argc,
entry = HashFind(&ResNodeTable, argv[i]); entry = HashFind(&ResNodeTable, argv[i]);
device->source = (ResExtNode *)HashGetValue(entry); device->source = (ResExtNode *)HashGetValue(entry);
device->rs_sattr = StrDup((char **)NULL, argv[i + 2]); device->rs_sattr = StrDup((char **)NULL, argv[i + 2]);
w = atoi(argv[i + 1]);
ResNodeAddDevice(device->source, device, SOURCE); ResNodeAddDevice(device->source, device, SOURCE);
i += 3; i += 3;
} }
@ -833,7 +423,6 @@ ResReadDevice(int argc,
entry = HashFind(&ResNodeTable, argv[i]); entry = HashFind(&ResNodeTable, argv[i]);
device->drain = (ResExtNode *)HashGetValue(entry); device->drain = (ResExtNode *)HashGetValue(entry);
device->rs_dattr = StrDup((char **)NULL, argv[i + 2]); device->rs_dattr = StrDup((char **)NULL, argv[i + 2]);
w = MAX(w, atoi(argv[i + 1]));
ResNodeAddDevice(device->drain, device, DRAIN); ResNodeAddDevice(device->drain, device, DRAIN);
i += 3; i += 3;
} }
@ -844,7 +433,6 @@ ResReadDevice(int argc,
} }
device->rs_ttype = extGetDevType(devptr->exts_deviceName); device->rs_ttype = extGetDevType(devptr->exts_deviceName);
device->rs_wl = (l == 0) ? 0.0 : (float)w / (float)l;
ResRDevList = device; ResRDevList = device;
device->layout = NULL; device->layout = NULL;
@ -868,11 +456,13 @@ ResReadFET(int argc,
char *argv[]) char *argv[])
{ {
RDev *device; RDev *device;
int rvalue, i, j, k, w, l; int rvalue, i, j, k;
ExtDevice *devptr; ExtDevice *devptr;
TileType ttype; TileType ttype;
HashEntry *entry; HashEntry *entry;
ResExtNode *node; ResExtNode *node;
ResValue rpersquare;
float area, perim, wval, lval;
device = (RDev *)mallocMagic((unsigned)(sizeof(RDev))); device = (RDev *)mallocMagic((unsigned)(sizeof(RDev)));
@ -897,6 +487,20 @@ ResReadFET(int argc,
device->rs_dattr = RDEV_NOATTR; device->rs_dattr = RDEV_NOATTR;
device->rs_devptr = devptr; device->rs_devptr = devptr;
entry = HashLookOnly(&devptr->exts_deviceResist, "linear");
if (entry != NULL)
rpersquare = (ResValue)(spointertype)HashGetValue(entry);
else
rpersquare = (ResValue)10000.0; /* Default to a sane value */
/* For old-style FETs, the width is determined from area and perimeter */
area = MagAtof(argv[FET_AREA]);
perim = MagAtof(argv[FET_PERIM]);
lval = 0.5 * (perim + sqrt(perim * perim - 4 * area));
wval = area / lval;
device->resistance = wval * rpersquare; /* Channel resistance */
/* Find and record the FET terminal nodes */ /* Find and record the FET terminal nodes */
entry = HashFind(&ResNodeTable, argv[FET_GATE]); entry = HashFind(&ResNodeTable, argv[FET_GATE]);
@ -918,11 +522,6 @@ ResReadFET(int argc,
device->rs_sattr = StrDup((char **)NULL, argv[FET_SOURCE_ATTR]); device->rs_sattr = StrDup((char **)NULL, argv[FET_SOURCE_ATTR]);
device->rs_dattr = StrDup((char **)NULL, argv[FET_DRAIN_ATTR]); device->rs_dattr = StrDup((char **)NULL, argv[FET_DRAIN_ATTR]);
l = atoi(argv[FET_GATE_ATTR - 1]);
w = atoi(argv[FET_SOURCE_ATTR - 1]);
w = MAX(w, atoi(argv[FET_DRAIN_ATTR - 1]));
device->rs_wl = (l == 0) ? 0.0 : (float)w / (float)l;
ResRDevList = device; ResRDevList = device;
device->layout = NULL; device->layout = NULL;
return 0; return 0;
@ -1029,22 +628,10 @@ ResReadAttribute(ResExtNode *node,
else if (strncmp(avalue, "res:drive", 9) == 0 && else if (strncmp(avalue, "res:drive", 9) == 0 &&
(ResOptionsFlags & ResOpt_Signal)) (ResOptionsFlags & ResOpt_Signal))
{ {
ResConnect *newdriver; node->drivepoint.p_x = atoi(argv[RES_EXT_ATTR_X]);
node->drivepoint.p_y = atoi(argv[RES_EXT_ATTR_Y]);
/* Generate new drivepoint entry */ node->rs_ttype = DBTechNoisyNameType(argv[RES_EXT_ATTR_TYPE]);
newdriver = (ResConnect *)mallocMagic(sizeof(ResConnect));
node->status |= DRIVELOC; node->status |= DRIVELOC;
newdriver->rc_rect.r_xbot = atoi(argv[RES_EXT_ATTR_X]);
newdriver->rc_rect.r_ybot = atoi(argv[RES_EXT_ATTR_Y]);
newdriver->rc_rect.r_xtop = atoi(argv[RES_EXT_ATTR_X]);
newdriver->rc_rect.r_ytop = atoi(argv[RES_EXT_ATTR_Y]);
newdriver->rc_type = DBTechNoisyNameType(argv[RES_EXT_ATTR_TYPE]);
newdriver->rc_node = (resNode *)NULL;
newdriver->rc_next = node->drivepoints;
node->drivepoints = newdriver;
} }
return 0; return 0;
} }
@ -1081,11 +668,11 @@ ResExtInitNode(entry)
node->cap_couple = 0; node->cap_couple = 0;
node->resistance = 0; node->resistance = 0;
node->type = 0; node->type = 0;
node->devices = NULL; node->firstDev = NULL;
node->name = entry->h_key.h_name; node->name = entry->h_key.h_name;
node->oldname = NULL; node->oldname = NULL;
node->drivepoints = NULL; node->drivepoint.p_x = INFINITY;
node->sinkpoints = NULL; node->drivepoint.p_y = INFINITY;
node->location.p_x = INFINITY; node->location.p_x = INFINITY;
node->location.p_y = INFINITY; node->location.p_y = INFINITY;
} }

File diff suppressed because it is too large Load Diff

View File

@ -178,7 +178,7 @@ ResSimplifyNet(nodelist, biglist, reslist, tolerance)
/* other recievers at far end? If so, reschedule other node; /* other recievers at far end? If so, reschedule other node;
* deadlock will be settled from that node. * deadlock will be settled from that node.
*/ */
if ((MarkedReceivers + UnMarkedReceivers + NumberOfDrivers == 2) || if ((MarkedReceivers+UnMarkedReceivers+NumberOfDrivers == 2) ||
(UnMarkedReceivers == 0 && MarkedReceivers > 1 && (UnMarkedReceivers == 0 && MarkedReceivers > 1 &&
resistor2 == resistor1 && PendingReceivers == 0)) resistor2 == resistor1 && PendingReceivers == 0))
{ {
@ -229,7 +229,7 @@ ResSimplifyNet(nodelist, biglist, reslist, tolerance)
* Two resistors in series? Combine them and move devices to * Two resistors in series? Combine them and move devices to
* appropriate end. * appropriate end.
*/ */
else if (numdrive + numreceive == 2 && (resistor1->rr_value < tolerance && else if (numdrive+numreceive == 2 && (resistor1->rr_value < tolerance &&
resistor2->rr_value < tolerance)) resistor2->rr_value < tolerance))
{ {
if ((resistor1->rr_status & RES_MARKED) == 0 && if ((resistor1->rr_status & RES_MARKED) == 0 &&
@ -362,7 +362,7 @@ ResSimplifyNet(nodelist, biglist, reslist, tolerance)
/* /*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
* *
* ResMoveDevices-- move devices from one node (node1) to anther (node2) * ResMoveDevices-- move devices from one node1 to node2
* *
* Results: none * Results: none
* *
@ -387,11 +387,11 @@ ResMoveDevices(node1, node2)
devptr = devptr->te_nextt; devptr = devptr->te_nextt;
if (device->rd_fet_gate == node1) if (device->rd_fet_gate == node1)
device->rd_fet_gate = node2; device->rd_fet_gate = node2;
else if (device->rd_fet_subs == node1) else if ((device->rd_nterms >= 4) && (device->rd_fet_subs == node1))
device->rd_fet_subs = node2; device->rd_fet_subs = node2;
else if ((device->rd_nterms > 2) && (device->rd_fet_source == node1)) else if (device->rd_fet_source == node1)
device->rd_fet_source = node2; device->rd_fet_source = node2;
else if ((device->rd_nterms > 3) && (device->rd_fet_drain == node1)) else if (device->rd_fet_drain == node1)
device->rd_fet_drain = node2; device->rd_fet_drain = node2;
else else
TxError("Missing Device connection in squish routines" TxError("Missing Device connection in squish routines"
@ -707,7 +707,7 @@ ResDistributeCapacitance(nodelist, totalcap)
for (workingNode = nodelist; workingNode != NULL; workingNode = workingNode->rn_more) for (workingNode = nodelist; workingNode != NULL; workingNode = workingNode->rn_more)
{ {
for (rptr = workingNode->rn_re; rptr != NULL; rptr = rptr->re_nextEl) for (rptr = workingNode->rn_re; rptr != NULL; rptr=rptr->re_nextEl)
if (rptr->re_thisEl->rr_float.rr_area != 0.0) if (rptr->re_thisEl->rr_float.rr_area != 0.0)
TxError("Nonnull resistor area\n"); TxError("Nonnull resistor area\n");
@ -829,11 +829,11 @@ ResCalculateTDi(node, resistor, resistorvalue)
ASSERT(rcd != NULL, "ResCalculateTdi"); ASSERT(rcd != NULL, "ResCalculateTdi");
if (resistor == NULL) if (resistor == NULL)
rcd->rc_Tdi = rcd->rc_Cdownstream * (float)resistorvalue; rcd->rc_Tdi = rcd->rc_Cdownstream*(float)resistorvalue;
else else
{ {
rcd2 = (RCDelayStuff *)resistor->rr_connection1->rn_client; rcd2 = (RCDelayStuff *)resistor->rr_connection1->rn_client;
ASSERT(rcd2 != NULL, "ResCalculateTdi"); ASSERT(rcd2 != NULL,"ResCalculateTdi");
rcd->rc_Tdi = rcd->rc_Cdownstream * (float)resistor->rr_value + rcd->rc_Tdi = rcd->rc_Cdownstream * (float)resistor->rr_value +
rcd2->rc_Tdi; rcd2->rc_Tdi;
} }
@ -920,20 +920,25 @@ ResPruneTree(node, minTdi, nodelist1, nodelist2, resistorlist)
*/ */
int int
ResDoSimplify(resisdata) ResDoSimplify(tolerance,resisdata)
float tolerance;
ResisData *resisdata; ResisData *resisdata;
{ {
resNode *node, *slownode; resNode *node, *slownode;
float bigres = 0.0; float bigres = 0;
float millitolerance;
float totalcap; float totalcap;
float rctol;
resResistor *res; resResistor *res;
rctol = resisdata->tdiTolerance;
ResSetPathRes(resisdata); ResSetPathRes(resisdata);
for (node = ResNodeList; node != NULL; node = node->rn_more) for (node = ResNodeList; node != NULL; node = node->rn_more)
bigres = MAX(bigres, node->rn_noderes); bigres = MAX(bigres, node->rn_noderes);
bigres /= OHMSTOMILLIOHMS; /* convert from milliohms to ohms */
resisdata->rg_maxres = bigres; resisdata->rg_maxres = bigres;
#ifdef PARANOID #ifdef PARANOID
@ -947,8 +952,8 @@ ResDoSimplify(resisdata)
(void) ResDistributeCapacitance(ResNodeList, resisdata->rg_nodecap); (void) ResDistributeCapacitance(ResNodeList, resisdata->rg_nodecap);
if (((ResOptionsFlags & ResOpt_Simplify) == 0) && if (((tolerance > bigres) || ((ResOptionsFlags & ResOpt_Simplify) == 0)) &&
((ResOptionsFlags & ResOpt_DoLumpFile) == 0)) ((ResOptionsFlags & ResOpt_DoLumpFile) == 0))
return 0; return 0;
res = ResResList; res = ResResList;
@ -958,6 +963,16 @@ ResDoSimplify(resisdata)
res = res->rr_nextResistor; res = res->rr_nextResistor;
oldres->rr_status &= ~RES_HEAP; oldres->rr_status &= ~RES_HEAP;
/*------ NOTE: resistors marked with RES_TDI_IGNORE are
* part of loops but should NOT be removed.
if (oldres->rr_status & RES_TDI_IGNORE)
{
ResDeleteResPointer(oldres->rr_node[0], oldres);
ResDeleteResPointer(oldres->rr_node[1], oldres);
ResEliminateResistor(oldres, &ResResList);
}
------*/
} }
if (ResNodeAtOrigin == NULL) if (ResNodeAtOrigin == NULL)
@ -965,7 +980,7 @@ ResDoSimplify(resisdata)
TxError("Error: Network simplification: Failed to to get origin node.\n"); TxError("Error: Network simplification: Failed to to get origin node.\n");
resisdata->rg_Tdi = 0; resisdata->rg_Tdi = 0;
} }
else if (resisdata->mindelay > 0) else if (ResOptionsFlags & ResOpt_Tdi)
{ {
if ((resisdata->rg_nodecap != -1) && if ((resisdata->rg_nodecap != -1) &&
(totalcap = ResCalculateChildCapacitance(ResNodeAtOrigin)) != -1) (totalcap = ResCalculateChildCapacitance(ResNodeAtOrigin)) != -1)
@ -973,7 +988,8 @@ ResDoSimplify(resisdata)
RCDelayStuff *rc = (RCDelayStuff *) ResNodeList->rn_client; RCDelayStuff *rc = (RCDelayStuff *) ResNodeList->rn_client;
resisdata->rg_nodecap = totalcap; resisdata->rg_nodecap = totalcap;
ResCalculateTDi(ResNodeAtOrigin, (resResistor *)NULL, 0); ResCalculateTDi(ResNodeAtOrigin, (resResistor *)NULL,
resisdata->rg_bigdevres);
if (rc != (RCDelayStuff *)NULL) if (rc != (RCDelayStuff *)NULL)
resisdata->rg_Tdi = rc->rc_Tdi; resisdata->rg_Tdi = rc->rc_Tdi;
else else
@ -997,10 +1013,20 @@ ResDoSimplify(resisdata)
else else
resisdata->rg_Tdi = 0; resisdata->rg_Tdi = 0;
/* Simplify network */ if ((rctol+1) * resisdata->rg_bigdevres * resisdata->rg_nodecap >
rctol * resisdata->rg_Tdi &&
(ResOptionsFlags & ResOpt_Tdi) &&
resisdata->rg_Tdi != -1)
return 0;
/* Simplify network; resistors are still in milliohms, so use
* millitolerance.
*/
if (ResOptionsFlags & ResOpt_Simplify) if (ResOptionsFlags & ResOpt_Simplify)
{ {
millitolerance = tolerance * MILLIOHMSPEROHM;
/* /*
* Start simplification at driver (R=0). Remove it from the done list * Start simplification at driver (R=0). Remove it from the done list
* and add it to the pending list. Call ResSimplifyNet as long as * and add it to the pending list. Call ResSimplifyNet as long as
@ -1018,18 +1044,16 @@ ResDoSimplify(resisdata)
/* if Tdi is enabled, prune all branches whose end nodes */ /* if Tdi is enabled, prune all branches whose end nodes */
/* have time constants less than the tolerance. */ /* have time constants less than the tolerance. */
if ((resisdata->rg_Tdi != -1) && (resisdata->mindelay > 0)) if ((ResOptionsFlags & ResOpt_Tdi) &&
ResPruneTree(ResNodeAtOrigin, resisdata->mindelay, resisdata->rg_Tdi != -1 &&
&ResNodeList, &ResNodeQueue, &ResResList); rctol != 0)
ResNodeAtOrigin->rn_status &= ~RES_MARKED;
if (ResNodeAtOrigin->rn_less == CLIENTDEFAULT)
{ {
TxError("ResSimplify: Bad resptr at node %s origin.\n", ResPruneTree(ResNodeAtOrigin, (rctol + 1) *
ResNodeAtOrigin->rn_name); resisdata->rg_bigdevres * resisdata->rg_nodecap / rctol,
return 0; &ResNodeList, &ResNodeQueue, &ResResList);
} }
else if (ResNodeAtOrigin->rn_less == NULL) ResNodeAtOrigin->rn_status &= ~RES_MARKED;
if (ResNodeAtOrigin->rn_less == NULL)
ResNodeList = ResNodeAtOrigin->rn_more; ResNodeList = ResNodeAtOrigin->rn_more;
else else
ResNodeAtOrigin->rn_less->rn_more = ResNodeAtOrigin->rn_more; ResNodeAtOrigin->rn_less->rn_more = ResNodeAtOrigin->rn_more;
@ -1041,14 +1065,13 @@ ResDoSimplify(resisdata)
ResNodeAtOrigin->rn_less = NULL; ResNodeAtOrigin->rn_less = NULL;
ResNodeQueue = ResNodeAtOrigin; ResNodeQueue = ResNodeAtOrigin;
while (ResNodeQueue != NULL) while (ResNodeQueue != NULL)
ResSimplifyNet(&ResNodeQueue, &ResNodeList, &ResResList, ResSimplifyNet(&ResNodeQueue, &ResNodeList, &ResResList, millitolerance);
resisdata->minres);
/* /*
* Call ResScrunchNet to eliminate any remaining under-tolerance * Call ResScrunchNet to eliminate any remaining under tolerance
* resistors. * resistors.
*/ */
ResScrunchNet(&ResResList, &ResNodeQueue, &ResNodeList, resisdata->minres); ResScrunchNet(&ResResList, &ResNodeQueue, &ResNodeList, millitolerance);
} }
} }
return 0; return 0;

File diff suppressed because it is too large Load Diff

View File

@ -160,7 +160,7 @@ resCurrentPrintFunc(node, resistor, filename)
void void
ResDeviceCounts() ResDeviceCounts()
{ {
int i, j, k; int i,j,k;
resNode *n; resNode *n;
resDevice *t; resDevice *t;
resResistor *r; resResistor *r;

View File

@ -51,35 +51,16 @@ typedef struct resistor
#define rr_connection1 rr_node[0] #define rr_connection1 rr_node[0]
#define rr_connection2 rr_node[1] #define rr_connection2 rr_node[1]
/* The resDevTerm structure holds information needed to quickly check */ /* Definitions for old FET-style MOSFET devices */
/* if a tile belongs to a terminal of a device in a plane other than */
/* the plane of the device. */
typedef struct resdevterm
{
struct resdevterm *rdt_next; /* next connected device in the list */
Tile *rdt_tile; /* the tile of the device */
int rdt_term; /* terminal number, or -1 for substrate */
} resDevTerm;
/* Definitions for old FET-style MOSFET devices. Actual devices may have
* any number of terminals. "GATE" is the identifying type; "SUBS" is
* the substrate/well connection (if it exists), and the other terminals
* make up the remaining entries. Memory will be allocated for the
* substrate whether or not one is defined for the device. If the device
* does not define a substrate connection, then this entry will remain
* NULL.
*/
#define RT_GATE 0 #define RT_GATE 0
#define RT_SUBS 1 #define RT_SOURCE 1
#define RT_SOURCE 2 #define RT_DRAIN 2
#define RT_DRAIN 3 #define RT_SUBS 3
#define rd_fet_gate rd_terminals[RT_GATE] #define rd_fet_gate rd_terminals[RT_GATE]
#define rd_fet_subs rd_terminals[RT_SUBS]
#define rd_fet_source rd_terminals[RT_SOURCE] #define rd_fet_source rd_terminals[RT_SOURCE]
#define rd_fet_drain rd_terminals[RT_DRAIN] #define rd_fet_drain rd_terminals[RT_DRAIN]
#define rd_fet_subs rd_terminals[RT_SUBS]
typedef struct device typedef struct device
{ {
@ -98,19 +79,6 @@ typedef struct device
Tile *rd_tile; /* pointer to a tile in device */ Tile *rd_tile; /* pointer to a tile in device */
} resDevice; } resDevice;
/*
* A resConnect maintains a location and tile type of a connections up
* (driver) or down (sink), a tile type that makes the connection, and
* a link to the resNode that must exist at the point of connection.
*/
typedef struct resconnect
{
TileType rc_type;
Rect rc_rect;
struct resnode *rc_node;
struct resconnect *rc_next;
} ResConnect;
/* /*
* A junction is formed when two tiles that connect are next to one another. * A junction is formed when two tiles that connect are next to one another.
*/ */
@ -132,10 +100,9 @@ typedef struct junction
typedef struct resport typedef struct resport
{ {
struct resport *rp_nextPort; struct resport *rp_nextPort;
Rect rp_bbox;
Point rp_loc;
char *rp_nodename; char *rp_nodename;
ResConnect *rp_connect;
Rect rp_bbox;
Point rp_loc;
} resPort; } resPort;
/* /*
@ -285,10 +252,9 @@ typedef struct resoptions
{ {
/* Global options for extresist */ /* Global options for extresist */
float minres; /* Minimum network resistance to output */ float tdiTolerance;
float mindelay; /* Minimum network delay to output */ float frequency;
float rthresh; /* Minimum individual resistance */ float rthresh;
float frequency; /* For FastHenry geometry extraction */
struct saveList *savePlanes; struct saveList *savePlanes;
CellDef *mainDef; CellDef *mainDef;
@ -300,8 +266,9 @@ typedef struct resoptions
TileType rg_ttype; TileType rg_ttype;
float rg_maxres; float rg_maxres;
float rg_nodecap; /* Node capacitance */ float rg_nodecap;
float rg_Tdi; /* Node delay */ float rg_Tdi;
int rg_bigdevres;
int rg_tilecount; int rg_tilecount;
int rg_status; int rg_status;
Point *rg_devloc; Point *rg_devloc;
@ -322,19 +289,21 @@ typedef struct rcdelaystuff
typedef struct rdev typedef struct rdev
{ {
struct rdev *nextDev; /* Next device in linked list */ struct rdev *nextDev; /* Next device in linked list */
struct rdev *realDev; /* Single Lumped Device for */
/* devices connected in parallel */
resDevice *layout; /* pointer to resDevice that */ resDevice *layout; /* pointer to resDevice that */
/* corresponds to RDev */ /* corresponds to RDev */
int status; int status;
struct resextnode *gate; /* Terminals of transistor. */ struct resextnode *gate; /* Terminals of transistor. */
struct resextnode *source; struct resextnode *source;
struct resextnode *drain; struct resextnode *drain;
struct resextnode *subs; /* Used with subcircuit type only */ struct resextnode *subs; /* Used with subcircuit type only */
Point location; /* Location of lower left point */ Point location; /* Location of lower left point of */
/* of the device. */ /* device. */
TileType rs_ttype; /* tile type for device */ float resistance; /* "Resistance" of device. */
float rs_wl; /* device W/L, if relevant */ TileType rs_ttype; /* tile type for device */
ExtDevice *rs_devptr; /* device extraction record */ ExtDevice *rs_devptr; /* device extraction record */
char *rs_gattr; /* Gate attributes, if any */ char *rs_gattr; /* Gate attributes, if any */
char *rs_sattr; char *rs_sattr;
char *rs_dattr; char *rs_dattr;
} RDev; } RDev;
@ -351,13 +320,17 @@ typedef struct resextnode
float cap_couple; /* Coupling capacitance */ float cap_couple; /* Coupling capacitance */
float resistance; /* Lumped resistance */ float resistance; /* Lumped resistance */
float minsizeres; /* Minimum size resistor allowed */ float minsizeres; /* Minimum size resistor allowed */
ResConnect *drivepoints; /* Upward connections */ Point drivepoint; /* optional, user specified drive */
ResConnect *sinkpoints; /* Downward connections */ /* point for network. */
TileType rs_ttype; /* Tiletype of drivepoint */
Point location; /* Location of bottom of leftmost */ Point location; /* Location of bottom of leftmost */
/* tile in the lowest numbered */ /* tile in the lowest numbered */
/* plane contained in the node. */ /* plane contained in the node. */
Rect rs_bbox; /* Location of bottom of leftmost */
/* tile in the lowest numbered */
/* plane contained in the node. */
TileType type; /* Tile type of tile at location */ TileType type; /* Tile type of tile at location */
struct devptr *devices; /* Linked list of devices */ struct devptr *firstDev; /* Linked list of devices */
/* connected to node. */ /* connected to node. */
char *name; /* Pointer to name of node stored */ char *name; /* Pointer to name of node stored */
/* in hash table. */ /* in hash table. */
@ -470,7 +443,6 @@ typedef struct capval
/* Capacitance table constants */ /* Capacitance table constants */
#define OHMSTOMILLIOHMS 1000 #define OHMSTOMILLIOHMS 1000
#define MILLIOHMSTOOHMS 0.001
#define UNTOUCHED 0 #define UNTOUCHED 0
#define SERIES 1 #define SERIES 1
@ -505,12 +477,12 @@ typedef struct capval
#define ResOpt_DoExtFile 0x0004 #define ResOpt_DoExtFile 0x0004
#define ResOpt_DoLumpFile 0x0008 #define ResOpt_DoLumpFile 0x0008
#define ResOpt_RunSilent 0x0010 #define ResOpt_RunSilent 0x0010
#define ResOpt_Debug 0x0020 #define ResOpt_Stats 0x0020
#define ResOpt_Stats 0x0040 #define ResOpt_Tdi 0x0040
#define ResOpt_Signal 0x0080 #define ResOpt_Signal 0x0080
#define ResOpt_Geometry 0x0100 #define ResOpt_Geometry 0x0100
#define ResOpt_FastHenry 0x0200 #define ResOpt_FastHenry 0x0200
#define ResOpt_Blackbox 0x0400 #define ResOpt_Blackbox 0x0300
#define ResOpt_DoSubstrate 0x0800 #define ResOpt_DoSubstrate 0x0800
#define ResOpt_Box 0x1000 #define ResOpt_Box 0x1000
@ -538,7 +510,7 @@ extern ResFixPoint *ResFixList;
extern int ResTileCount; extern int ResTileCount;
extern ResExtNode **ResNodeArray; extern ResExtNode **ResNodeArray;
extern CellDef *mainDef; extern CellDef *mainDef;
extern TileTypeBitMask ResTermTypesBitMask; extern TileTypeBitMask ResSDTypesBitMask;
extern TileTypeBitMask ResSubTypesBitMask; extern TileTypeBitMask ResSubTypesBitMask;
extern HashTable ResDevTable; extern HashTable ResDevTable;
extern TileTypeBitMask ResNoMergeMask[NT]; extern TileTypeBitMask ResNoMergeMask[NT];
@ -553,14 +525,12 @@ extern int ResReadResistor();
extern int ResReadAttribute(); extern int ResReadAttribute();
extern int ResReadMerge(); extern int ResReadMerge();
extern int ResReadSubckt(); extern int ResReadSubckt();
extern int ResReadParentExt();
extern int ResProcessNode(); extern int ResProcessNode();
extern int ResExtCombineParallel(); extern int ResExtCombineParallel();
extern int dbSrConnectStartFunc(); extern int dbSrConnectStartFunc();
extern int ResEach(); extern int ResEach();
extern int ResAddPlumbing(); extern int ResAddPlumbing();
extern void ResAddDevPlumbing();
extern int ResRemovePlumbing(); extern int ResRemovePlumbing();
extern float ResCalculateChildCapacitance(); extern float ResCalculateChildCapacitance();
extern ResDevTile *DBTreeCopyConnectDCS(); extern ResDevTile *DBTreeCopyConnectDCS();
@ -573,18 +543,14 @@ extern void ResCheckExtNodes();
extern void ResSortByGate(); extern void ResSortByGate();
extern void ResFixDevName(); extern void ResFixDevName();
extern void ResWriteLumpFile(); extern void ResWriteLumpFile();
extern int ResSortBreaks(); extern void ResSortBreaks();
extern Plane *extResPrepSubstrate(); extern Plane *extResPrepSubstrate();
extern bool ResEachTile();
extern void ResStartTile();
/* C99 compat */ /* C99 compat */
extern void ExtResisForDef(CellDef *celldef, ResisData *resisdata); extern void ExtResisForDef(CellDef *celldef, ResisData *resisdata);
extern char *ResExtGetAttribute(char *sptr); extern char *ResExtGetAttribute(char *sptr);
extern int ResReadFET(int argc, char *argv[]); extern int ResReadFET(int argc, char *argv[]);
extern int ResReadConnectPoint(CellDef *def, int argc, char *argv[]);
extern int ResReadPort(int argc, char *argv[]); extern int ResReadPort(int argc, char *argv[]);
extern char *ResExtGetAttribute(char *sptr); extern char *ResExtGetAttribute(char *sptr);
@ -601,10 +567,9 @@ extern void ResEliminateResistor();
extern bool ResExtractNet(); extern bool ResExtractNet();
extern int ResFracture(); extern int ResFracture();
extern void ResMergeNodes(); extern void ResMergeNodes();
extern void ResNewTermDevice(); extern void ResNewSDDevice();
extern void ResNewSubDevice(); extern void ResNewSubDevice();
extern void ResPreProcessDevices(); extern void ResPreProcessDevices();
extern void ResFreeDevTiles();
extern void ResPrintDeviceList(); extern void ResPrintDeviceList();
extern void ResPrintExtDev(); extern void ResPrintExtDev();
extern void ResPrintReference(); extern void ResPrintReference();
@ -631,8 +596,59 @@ extern int resWalkleft();
extern int resWalkright(); extern int resWalkright();
extern int resWalkup(); extern int resWalkup();
extern void InitializeResNode(); /* Macros */
extern void ResInfoInit();
extern void ResNewBreak(); #define InitializeResNode(node,x,y,why) \
{\
(node)->rn_te = NULL;\
(node)->rn_id=0;\
(node)->rn_float.rn_area = 0.0;\
(node)->rn_name = NULL;\
(node)->rn_client = (ClientData)NULL;\
(node)->rn_noderes = RES_INFINITY;\
(node)->rn_je = NULL;\
(node)->rn_status = FALSE;\
(node)->rn_loc.p_x = (x);\
(node)->rn_loc.p_y = (y);\
(node)->rn_why = (why);\
(node)->rn_ce = (cElement *) NULL;\
(node)->rn_re = (resElement *) NULL;\
}
#define ResInfoInit(Info) \
{ \
Info->contactList = (cElement *) NULL; \
Info->deviceList = (resDevice *) NULL; \
Info->junctionList = (ResJunction *) NULL; \
Info->breakList = (Breakpoint *) NULL; \
Info->portList = (resPort *) NULL; \
Info->ri_status = FALSE; \
Info->sourceEdge = 0 ; \
}
#define NEWBREAK(node,tile,px,py,crect)\
{\
Breakpoint *bp;\
resInfo *jX_ = (resInfo *)((tile)->ti_client); \
bp = (Breakpoint *) mallocMagic((unsigned)(sizeof(Breakpoint))); \
bp->br_next= jX_->breakList; \
bp->br_this = (node); \
bp->br_loc.p_x = px; \
bp->br_loc.p_y = py; \
bp->br_crect = (Rect *) (crect); \
jX_->breakList = bp; \
}
#define NEWPORT(node,tile)\
{\
resPort *rp;\
resInfo *pX_ = (resInfo *)((tile)->ti_client); \
rp = (resPort *) mallocMagic((unsigned)(sizeof(resPort))); \
rp->rp_nextPort = pX_->portList; \
rp->rp_bbox = node->rs_bbox; \
rp->rp_loc = node->drivepoint; \
rp->rp_nodename = node->name; \
pX_->portList = rp; \
}
#endif /* _MAGIC__RESIS__RESIS_H */ #endif /* _MAGIC__RESIS__RESIS_H */

112
scripts/configure vendored
View File

@ -6276,7 +6276,6 @@ magic_with_tk_libraries=""
usingOGL=1 usingOGL=1
usingTcl=1 usingTcl=1
usingTk=1
usingOA=0 usingOA=0
usingCairo=1 usingCairo=1
usingPython3=1 usingPython3=1
@ -7255,12 +7254,10 @@ if test "${with_tcl+set}" = set; then :
magic_with_tcl=$withval magic_with_tcl=$withval
if test "$withval" = "no" -o "$withval" = "NO"; then if test "$withval" = "no" -o "$withval" = "NO"; then
usingTcl= usingTcl=
usingTk=
elif test $usingScheme ; then elif test $usingScheme ; then
echo Attempt to enable both Tcl and Scheme interpreters. echo Attempt to enable both Tcl and Scheme interpreters.
echo Disabling Tcl, and using Scheme instead. echo Disabling Tcl, and using Scheme instead.
usingTcl= usingTcl=
usingTk=
fi fi
fi fi
@ -7271,9 +7268,6 @@ fi
# Check whether --with-tk was given. # Check whether --with-tk was given.
if test "${with_tk+set}" = set; then : if test "${with_tk+set}" = set; then :
withval=$with_tk; magic_with_tk=$withval withval=$with_tk; magic_with_tk=$withval
if test "$withval" = "no" -o "$withval" = "NO"; then
usingTk=
fi
fi fi
@ -7403,7 +7397,7 @@ fi
# Find the Tk build configuration file "tkConfig.sh" # Find the Tk build configuration file "tkConfig.sh"
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
if test $usingTk ; then if test $usingTcl ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tkConfig.sh" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for tkConfig.sh" >&5
$as_echo_n "checking for tkConfig.sh... " >&6; } $as_echo_n "checking for tkConfig.sh... " >&6; }
@ -7483,8 +7477,8 @@ $as_echo "${tk_config_sh}" >&6; }
if test "x$tk_config_sh" = "x" ; then if test "x$tk_config_sh" = "x" ; then
echo "can't find Tk configuration script \"tkConfig.sh\"" echo "can't find Tk configuration script \"tkConfig.sh\""
echo "Reverting to non-Tk compilation" echo "Reverting to non-Tcl compilation"
usingTk= usingTcl=
fi fi
fi fi
@ -7494,9 +7488,7 @@ fi
if test $usingTcl ; then if test $usingTcl ; then
. $tcl_config_sh . $tcl_config_sh
if test $usingTk ; then . $tk_config_sh
. $tk_config_sh
fi
# Should probably trust the config file contents, but this configure # Should probably trust the config file contents, but this configure
# file checks the Tcl and Tk include and lib directories. Since # file checks the Tcl and Tk include and lib directories. Since
@ -7512,24 +7504,21 @@ if test $usingTcl ; then
tmpstr=${TCL_LIB_SPEC#*-L} tmpstr=${TCL_LIB_SPEC#*-L}
TCL_LIB_DIR=${tmpstr% -l*} TCL_LIB_DIR=${tmpstr% -l*}
tmpstr=${TK_LIB_SPEC#*-L}
TK_LIB_DIR=${tmpstr% -l*}
TCL_INC_DIR=${TCL_INCLUDE_SPEC#*-I} TCL_INC_DIR=${TCL_INCLUDE_SPEC#*-I}
if test $usingTk ; then TK_INC_DIR=${TK_INCLUDE_SPEC#*-I}
tmpstr=${TK_LIB_SPEC#*-L}
TK_LIB_DIR=${tmpstr% -l*}
TK_INC_DIR=${TK_INCLUDE_SPEC#*-I}
if test "$TCL_VERSION" = "7.6" -a "$TK_VERSION" = "4.2" ; then if test "$TCL_VERSION" = "7.6" -a "$TK_VERSION" = "4.2" ; then
: :
elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then
: :
elif test "$TCL_VERSION" = "$TK_VERSION" ; then elif test "$TCL_VERSION" = "$TK_VERSION" ; then
: :
else else
echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)" echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
echo "Reverting to non-Tcl compile" echo "Reverting to non-Tcl compile"
usingTcl= usingTcl=
usingTk=
fi
fi fi
fi fi
@ -7562,14 +7551,14 @@ if test $usingTcl ; then
fi fi
fi fi
if test $usingTk ; then if test $usingTcl ; then
if test "x${magic_with_tk_includes}" != "x" ; then if test "x${magic_with_tk_includes}" != "x" ; then
if test -r "${magic_with_tk_includes}/tk.h" ; then if test -r "${magic_with_tk_includes}/tk.h" ; then
TK_INC_DIR=${magic_with_tk_includes} TK_INC_DIR=${magic_with_tk_includes}
else else
echo "Can't find tk.h in \"${magic_with_tk_includes}\"" echo "Can't find tk.h in \"${magic_with_tk_includes}\""
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
else else
for dir in \ for dir in \
@ -7586,8 +7575,8 @@ if test $usingTk ; then
done done
if test "x${TK_INC_DIR}" = "x" ; then if test "x${TK_INC_DIR}" = "x" ; then
echo "Can't find tk.h header file" echo "Can't find tk.h header file"
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
fi fi
fi fi
@ -7610,14 +7599,14 @@ if test $usingTcl ; then
if test "x${TCL_LIB_SPEC}" = "x" ; then if test "x${TCL_LIB_SPEC}" = "x" ; then
TCL_LIB_SPEC="-l${TCL_LIB_NAME}" TCL_LIB_SPEC="-l${TCL_LIB_NAME}"
fi fi
if test $usingTk -a "x${TK_LIB_SPEC}" = "x" ; then if test "x${TK_LIB_SPEC}" = "x" ; then
TK_LIB_SPEC="-l${TK_LIB_NAME}" TK_LIB_SPEC="-l${TK_LIB_NAME}"
fi fi
# Find the version of "wish" that corresponds to TCL_EXEC_PREFIX # Find the version of "wish" that corresponds to TCL_EXEC_PREFIX
# We really ought to run "ldd" to confirm that the linked libraries match. # We really ought to run "ldd" to confirm that the linked libraries match.
if test $usingTk -a "x${magic_with_wish_binary}" = "x" ; then if test "x${magic_with_wish_binary}" = "x" ; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wish executable" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for wish executable" >&5
$as_echo_n "checking for wish executable... " >&6; } $as_echo_n "checking for wish executable... " >&6; }
for dir in \ for dir in \
@ -7649,7 +7638,7 @@ $as_echo "no" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${WISH_EXE}" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${WISH_EXE}" >&5
$as_echo "${WISH_EXE}" >&6; } $as_echo "${WISH_EXE}" >&6; }
fi fi
elif test $usingTk ; then else
WISH_EXE=${magic_with_wish_binary} WISH_EXE=${magic_with_wish_binary}
fi fi
@ -7742,7 +7731,7 @@ $as_echo "${TCLSH_EXE}" >&6; }
fi fi
fi fi
if test $usingTk ; then if test $usingTcl ; then
if test "x${magic_with_tk_libraries}" != "x" ; then if test "x${magic_with_tk_libraries}" != "x" ; then
for libname in \ for libname in \
"${magic_with_tk_libraries}/${TCL_LIB_FILE}" \ "${magic_with_tk_libraries}/${TCL_LIB_FILE}" \
@ -7757,8 +7746,8 @@ if test $usingTk ; then
done done
if test "x${TK_LIB_DIR}" = "x" ; then if test "x${TK_LIB_DIR}" = "x" ; then
echo "Can't find tk library in \"${magic_with_tk_libraries}\"" echo "Can't find tk library in \"${magic_with_tk_libraries}\""
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
else else
for libname in \ for libname in \
@ -7773,8 +7762,8 @@ if test $usingTk ; then
done done
if test "x${TK_LIB_DIR}" = "x" ; then if test "x${TK_LIB_DIR}" = "x" ; then
echo "Can't find tk library" echo "Can't find tk library"
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
fi fi
fi fi
@ -8649,12 +8638,7 @@ if test $usingTcl ; then
extra_libs="$extra_libs \${MAGICDIR}/tcltk/libtcltk.o" extra_libs="$extra_libs \${MAGICDIR}/tcltk/libtcltk.o"
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\"" extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
if test $usingTk ; then stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
else
stub_defs="$stub_defs -DUSE_TCL_STUBS -DMAGIC_NO_TK"
extra_defs="$extra_defs -DMAGIC_NO_TK"
fi
elif test $usingScheme ; then elif test $usingScheme ; then
modules="$modules lisp" modules="$modules lisp"
unused="$unused tcltk" unused="$unused tcltk"
@ -8697,9 +8681,7 @@ if test $usingTcl ; then
gr_libs="$gr_libs -lX11" gr_libs="$gr_libs -lX11"
fi fi
fi fi
if test $usingTk ; then gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
fi
else else
if test $usingX11 ; then if test $usingX11 ; then
gr_dflags="$gr_dflags -DX11 -DXLIB" gr_dflags="$gr_dflags -DX11 -DXLIB"
@ -8809,25 +8791,23 @@ if test $usingTcl ; then
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# #
# Tk libraries and header files (skipped under --without-tk) # Tk libraries and header files
# #
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
if test $usingTk ; then if test "${TK_INC_DIR}" != "/usr/include" ; then
if test "${TK_INC_DIR}" != "/usr/include" ; then INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}" fi
fi if test "${TK_LIB_DIR}" = "/usr/lib" -o \
if test "${TK_LIB_DIR}" = "/usr/lib" -o \ "${TK_LIB_DIR}" = "/usr/lib64" ; then
"${TK_LIB_DIR}" = "/usr/lib64" ; then LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_LIB_SPEC}"
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_LIB_SPEC}" LIB_SPECS="${LIB_SPECS} ${TK_STUB_LIB_SPEC}"
LIB_SPECS="${LIB_SPECS} ${TK_STUB_LIB_SPEC}" else
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} -L${TK_LIB_DIR} ${TK_LIB_SPEC}"
LIB_SPECS="${LIB_SPECS} -L${TK_LIB_DIR} ${TK_STUB_LIB_SPEC}"
if test "x${loader_run_path}" = "x" ; then
loader_run_path="${TK_LIB_DIR}"
else else
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} -L${TK_LIB_DIR} ${TK_LIB_SPEC}" loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
LIB_SPECS="${LIB_SPECS} -L${TK_LIB_DIR} ${TK_STUB_LIB_SPEC}"
if test "x${loader_run_path}" = "x" ; then
loader_run_path="${TK_LIB_DIR}"
else
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
fi
fi fi
fi fi

View File

@ -344,7 +344,6 @@ dnl disabled with --with-opengl=no
usingOGL=1 usingOGL=1
usingTcl=1 usingTcl=1
usingTk=1
usingOA=0 usingOA=0
usingCairo=1 usingCairo=1
usingPython3=1 usingPython3=1
@ -443,12 +442,10 @@ AC_ARG_WITH(tcl,
magic_with_tcl=$withval magic_with_tcl=$withval
if test "$withval" = "no" -o "$withval" = "NO"; then if test "$withval" = "no" -o "$withval" = "NO"; then
usingTcl= usingTcl=
usingTk=
elif test $usingScheme ; then elif test $usingScheme ; then
echo Attempt to enable both Tcl and Scheme interpreters. echo Attempt to enable both Tcl and Scheme interpreters.
echo Disabling Tcl, and using Scheme instead. echo Disabling Tcl, and using Scheme instead.
usingTcl= usingTcl=
usingTk=
fi fi
], ) ], )
@ -458,17 +455,10 @@ dnl and don't set the usingTcl variable.
dnl dnl
dnl This has been broken up into a number of sections, each of which dnl This has been broken up into a number of sections, each of which
dnl depends independently on the setting of usingTcl. dnl depends independently on the setting of usingTcl.
dnl
dnl `usingTk` is independent of `usingTcl`: --without-tk enables
dnl Tcl-only embedding (used by the WASM build, which has no display
dnl server and cannot run Tk).
dnl ---------------------------------------------------------------- dnl ----------------------------------------------------------------
AC_ARG_WITH(tk, [ --with-tk=DIR Find tkConfig.sh in DIR], AC_ARG_WITH(tk, [ --with-tk=DIR Find tkConfig.sh in DIR],
magic_with_tk=$withval magic_with_tk=$withval)
if test "$withval" = "no" -o "$withval" = "NO"; then
usingTk=
fi)
AC_ARG_WITH(tclincls, [ --with-tclincls=DIR Find tcl.h in DIR], AC_ARG_WITH(tclincls, [ --with-tclincls=DIR Find tcl.h in DIR],
magic_with_tcl_includes=$withval) magic_with_tcl_includes=$withval)
AC_ARG_WITH(tkincls, [ --with-tkincls=DIR Find tk.h in DIR], AC_ARG_WITH(tkincls, [ --with-tkincls=DIR Find tk.h in DIR],
@ -574,7 +564,7 @@ fi
# Find the Tk build configuration file "tkConfig.sh" # Find the Tk build configuration file "tkConfig.sh"
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
if test $usingTk ; then if test $usingTcl ; then
AC_MSG_CHECKING([for tkConfig.sh]) AC_MSG_CHECKING([for tkConfig.sh])
tk_config_sh="" tk_config_sh=""
@ -652,8 +642,8 @@ if test $usingTk ; then
if test "x$tk_config_sh" = "x" ; then if test "x$tk_config_sh" = "x" ; then
echo "can't find Tk configuration script \"tkConfig.sh\"" echo "can't find Tk configuration script \"tkConfig.sh\""
echo "Reverting to non-Tk compilation" echo "Reverting to non-Tcl compilation"
usingTk= usingTcl=
fi fi
fi fi
@ -663,9 +653,7 @@ fi
if test $usingTcl ; then if test $usingTcl ; then
. $tcl_config_sh . $tcl_config_sh
if test $usingTk ; then . $tk_config_sh
. $tk_config_sh
fi
# Should probably trust the config file contents, but this configure # Should probably trust the config file contents, but this configure
# file checks the Tcl and Tk include and lib directories. Since # file checks the Tcl and Tk include and lib directories. Since
@ -681,24 +669,21 @@ if test $usingTcl ; then
tmpstr=${TCL_LIB_SPEC#*-L} tmpstr=${TCL_LIB_SPEC#*-L}
TCL_LIB_DIR=${tmpstr% -l*} TCL_LIB_DIR=${tmpstr% -l*}
tmpstr=${TK_LIB_SPEC#*-L}
TK_LIB_DIR=${tmpstr% -l*}
TCL_INC_DIR=${TCL_INCLUDE_SPEC#*-I} TCL_INC_DIR=${TCL_INCLUDE_SPEC#*-I}
if test $usingTk ; then TK_INC_DIR=${TK_INCLUDE_SPEC#*-I}
tmpstr=${TK_LIB_SPEC#*-L}
TK_LIB_DIR=${tmpstr% -l*}
TK_INC_DIR=${TK_INCLUDE_SPEC#*-I}
if test "$TCL_VERSION" = "7.6" -a "$TK_VERSION" = "4.2" ; then if test "$TCL_VERSION" = "7.6" -a "$TK_VERSION" = "4.2" ; then
: :
elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then
: :
elif test "$TCL_VERSION" = "$TK_VERSION" ; then elif test "$TCL_VERSION" = "$TK_VERSION" ; then
: :
else else
echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)" echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
echo "Reverting to non-Tcl compile" echo "Reverting to non-Tcl compile"
usingTcl= usingTcl=
usingTk=
fi
fi fi
fi fi
@ -731,14 +716,14 @@ if test $usingTcl ; then
fi fi
fi fi
if test $usingTk ; then if test $usingTcl ; then
if test "x${magic_with_tk_includes}" != "x" ; then if test "x${magic_with_tk_includes}" != "x" ; then
if test -r "${magic_with_tk_includes}/tk.h" ; then if test -r "${magic_with_tk_includes}/tk.h" ; then
TK_INC_DIR=${magic_with_tk_includes} TK_INC_DIR=${magic_with_tk_includes}
else else
echo "Can't find tk.h in \"${magic_with_tk_includes}\"" echo "Can't find tk.h in \"${magic_with_tk_includes}\""
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
else else
for dir in \ for dir in \
@ -755,8 +740,8 @@ if test $usingTk ; then
done done
if test "x${TK_INC_DIR}" = "x" ; then if test "x${TK_INC_DIR}" = "x" ; then
echo "Can't find tk.h header file" echo "Can't find tk.h header file"
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
fi fi
fi fi
@ -779,14 +764,14 @@ if test $usingTcl ; then
if test "x${TCL_LIB_SPEC}" = "x" ; then if test "x${TCL_LIB_SPEC}" = "x" ; then
TCL_LIB_SPEC="-l${TCL_LIB_NAME}" TCL_LIB_SPEC="-l${TCL_LIB_NAME}"
fi fi
if test $usingTk -a "x${TK_LIB_SPEC}" = "x" ; then if test "x${TK_LIB_SPEC}" = "x" ; then
TK_LIB_SPEC="-l${TK_LIB_NAME}" TK_LIB_SPEC="-l${TK_LIB_NAME}"
fi fi
# Find the version of "wish" that corresponds to TCL_EXEC_PREFIX # Find the version of "wish" that corresponds to TCL_EXEC_PREFIX
# We really ought to run "ldd" to confirm that the linked libraries match. # We really ought to run "ldd" to confirm that the linked libraries match.
if test $usingTk -a "x${magic_with_wish_binary}" = "x" ; then if test "x${magic_with_wish_binary}" = "x" ; then
AC_MSG_CHECKING([for wish executable]) AC_MSG_CHECKING([for wish executable])
for dir in \ for dir in \
${TK_EXEC_PREFIX}/bin \ ${TK_EXEC_PREFIX}/bin \
@ -815,7 +800,7 @@ if test $usingTcl ; then
else else
AC_MSG_RESULT([${WISH_EXE}]) AC_MSG_RESULT([${WISH_EXE}])
fi fi
elif test $usingTk ; then else
WISH_EXE=${magic_with_wish_binary} WISH_EXE=${magic_with_wish_binary}
fi fi
@ -905,7 +890,7 @@ if test $usingTcl ; then
fi fi
fi fi
if test $usingTk ; then if test $usingTcl ; then
if test "x${magic_with_tk_libraries}" != "x" ; then if test "x${magic_with_tk_libraries}" != "x" ; then
for libname in \ for libname in \
"${magic_with_tk_libraries}/${TCL_LIB_FILE}" \ "${magic_with_tk_libraries}/${TCL_LIB_FILE}" \
@ -920,8 +905,8 @@ if test $usingTk ; then
done done
if test "x${TK_LIB_DIR}" = "x" ; then if test "x${TK_LIB_DIR}" = "x" ; then
echo "Can't find tk library in \"${magic_with_tk_libraries}\"" echo "Can't find tk library in \"${magic_with_tk_libraries}\""
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
else else
for libname in \ for libname in \
@ -936,8 +921,8 @@ if test $usingTk ; then
done done
if test "x${TK_LIB_DIR}" = "x" ; then if test "x${TK_LIB_DIR}" = "x" ; then
echo "Can't find tk library" echo "Can't find tk library"
echo "Reverting to non-Tk compile" echo "Reverting to non-Tcl compile"
usingTk= usingTcl=
fi fi
fi fi
fi fi
@ -1371,12 +1356,7 @@ if test $usingTcl ; then
AC_DEFINE(MAGIC_WRAPPER) AC_DEFINE(MAGIC_WRAPPER)
extra_libs="$extra_libs \${MAGICDIR}/tcltk/libtcltk.o" extra_libs="$extra_libs \${MAGICDIR}/tcltk/libtcltk.o"
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\"" extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
if test $usingTk ; then stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
else
stub_defs="$stub_defs -DUSE_TCL_STUBS -DMAGIC_NO_TK"
extra_defs="$extra_defs -DMAGIC_NO_TK"
fi
elif test $usingScheme ; then elif test $usingScheme ; then
modules="$modules lisp" modules="$modules lisp"
unused="$unused tcltk" unused="$unused tcltk"
@ -1421,9 +1401,7 @@ if test $usingTcl ; then
gr_libs="$gr_libs -lX11" gr_libs="$gr_libs -lX11"
fi fi
fi fi
if test $usingTk ; then gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
fi
else else
if test $usingX11 ; then if test $usingX11 ; then
gr_dflags="$gr_dflags -DX11 -DXLIB" gr_dflags="$gr_dflags -DX11 -DXLIB"
@ -1528,25 +1506,23 @@ if test $usingTcl ; then
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
# #
# Tk libraries and header files (skipped under --without-tk) # Tk libraries and header files
# #
# ----------------------------------------------------------------------- # -----------------------------------------------------------------------
if test $usingTk ; then if test "${TK_INC_DIR}" != "/usr/include" ; then
if test "${TK_INC_DIR}" != "/usr/include" ; then INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}" fi
fi if test "${TK_LIB_DIR}" = "/usr/lib" -o \
if test "${TK_LIB_DIR}" = "/usr/lib" -o \ "${TK_LIB_DIR}" = "/usr/lib64" ; then
"${TK_LIB_DIR}" = "/usr/lib64" ; then LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_LIB_SPEC}"
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_LIB_SPEC}" LIB_SPECS="${LIB_SPECS} ${TK_STUB_LIB_SPEC}"
LIB_SPECS="${LIB_SPECS} ${TK_STUB_LIB_SPEC}" else
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} -L${TK_LIB_DIR} ${TK_LIB_SPEC}"
LIB_SPECS="${LIB_SPECS} -L${TK_LIB_DIR} ${TK_STUB_LIB_SPEC}"
if test "x${loader_run_path}" = "x" ; then
loader_run_path="${TK_LIB_DIR}"
else else
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} -L${TK_LIB_DIR} ${TK_LIB_SPEC}" loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
LIB_SPECS="${LIB_SPECS} -L${TK_LIB_DIR} ${TK_STUB_LIB_SPEC}"
if test "x${loader_run_path}" = "x" ; then
loader_run_path="${TK_LIB_DIR}"
else
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
fi
fi fi
fi fi

View File

@ -470,9 +470,6 @@ SelectArea(scx, types, xMask, globmatch)
if (TTMaskHasType(types, L_LABEL)) if (TTMaskHasType(types, L_LABEL))
{ {
TTMaskClearType(types, L_LABEL);
if (TTMaskIsZero(types)) types = &DBAllButSpaceAndDRCBits;
if (globmatch != NULL) if (globmatch != NULL)
DBCellCopyGlobLabels(scx, types, xMask, SelectUse, &labelArea, DBCellCopyGlobLabels(scx, types, xMask, SelectUse, &labelArea,
globmatch); globmatch);
@ -837,8 +834,6 @@ chunkdone:
if (DBIsContact(type)) if (DBIsContact(type))
TTMaskSetOnlyType(&typeMask, type); TTMaskSetOnlyType(&typeMask, type);
/* Allow labels to be selected as part of the chunk */
TTMaskSetType(&typeMask, L_LABEL);
SelectArea(&newscx, &typeMask, xMask, NULL); SelectArea(&newscx, &typeMask, xMask, NULL);
} }

View File

@ -93,7 +93,7 @@ SimConnectFunc(
TileType loctype, ctype; TileType loctype, ctype;
TileType newdinfo = 0; TileType newdinfo = 0;
int i, pNum; int i, pNum;
static char nodeName[MAXPATHNAME]; static char nodeName[256];
CellDef *def; CellDef *def;
TerminalPath *tpath = cx->tc_filter->tf_tpath; TerminalPath *tpath = cx->tc_filter->tf_tpath;
@ -133,8 +133,7 @@ SimConnectFunc(
char c = *n; char c = *n;
SigDisableInterrupts(); SigDisableInterrupts();
strncpy(nodeName, SimGetNodeName(cx->tc_scx, tile, dinfo, tpath->tp_first), strcpy(nodeName, SimGetNodeName(cx->tc_scx, tile, dinfo, tpath->tp_first));
MAXPATHNAME);
SigEnableInterrupts(); SigEnableInterrupts();
*n = c; *n = c;

View File

@ -41,16 +41,6 @@ BIN_FILES = \
tcl-main: magicexec magicdnull magic.tcl magic.sh ext2spice.sh ext2sim.sh tcl-main: magicexec magicdnull magic.tcl magic.sh ext2spice.sh ext2sim.sh
# In the WASM build the tcltk module ships only as libtcltk.o; there is no
# wish-like launcher binary, no magic.sh, etc. `make mains` iterates over
# PROGRAMS which now includes tcltk (because --with-tcl is on), so define a
# no-op `main` to keep that loop happy. The native build still uses tcl-main
# via `make all` → tcllibrary.
ifeq (${MAKE_WASM},1)
main:
@:
endif
install-tcl: magicexec magicdnull ${BIN_FILES} ${TCL_FILES} install-tcl: magicexec magicdnull ${BIN_FILES} ${TCL_FILES}
${RM} $(DESTDIR)${INSTALL_TCLDIR}/magicexec ${RM} $(DESTDIR)${INSTALL_TCLDIR}/magicexec
${CP} magicexec $(DESTDIR)${INSTALL_TCLDIR}/magicexec ${CP} magicexec $(DESTDIR)${INSTALL_TCLDIR}/magicexec

View File

@ -35,10 +35,10 @@ proc magic::libcallback {command} {
switch $command { switch $command {
load {$winname load $celldef} load {$winname load $celldef}
place {$winname getcell $celldef child 0 0} place {$winname getcell $celldef}
pick { pick {
magic::tool pick magic::tool pick
$winname getcell $celldef child 0 0 $winname getcell $celldef
magic::startselect $winname pick magic::startselect $winname pick
} }
} }

View File

@ -178,10 +178,8 @@ TagCallback(interp, tkpath, argc, argv)
windCheckOnlyWindow(&w, DBWclientID); windCheckOnlyWindow(&w, DBWclientID);
if (w != NULL && !(w->w_flags & WIND_OFFSCREEN)) if (w != NULL && !(w->w_flags & WIND_OFFSCREEN))
{ {
#ifndef MAGIC_NO_TK
Tk_Window tkwind = (Tk_Window) w->w_grdata; Tk_Window tkwind = (Tk_Window) w->w_grdata;
if (tkwind != NULL) tkpath = Tk_PathName(tkwind); if (tkwind != NULL) tkpath = Tk_PathName(tkwind);
#endif
} }
} }
if (tkpath == NULL) if (tkpath == NULL)
@ -656,37 +654,6 @@ process_rlimit_startup_check(void)
#endif /* HAVE_GETRLIMIT */ #endif /* HAVE_GETRLIMIT */
} }
/*--------------------------------------------------------------*/
/* Register magic:: commands with the Tcl interpreter. */
/* Called after Magic's C subsystems are fully */
/* initialized (i.e. after magicMainInit returns 0) */
/* so that WindNextClient / WindGetCommandTable return */
/* populated tables. */
/*--------------------------------------------------------------*/
void
TclmagicRegisterCommands(Tcl_Interp *interp)
{
WindClient client;
int n;
char keyword[100];
char *kwptr = keyword + 7;
const char * const *commandTable;
sprintf(keyword, "magic::");
client = (WindClient)NULL;
while ((client = WindNextClient(client)) != NULL)
{
commandTable = WindGetCommandTable(client);
for (n = 0; commandTable[n] != NULL; n++)
{
sscanf(commandTable[n], "%92s", kwptr);
Tcl_CreateCommand(interp, keyword, (Tcl_CmdProc *)_tcl_dispatch,
(ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
}
}
}
/*------------------------------------------------------*/ /*------------------------------------------------------*/
/* Main startup procedure */ /* Main startup procedure */
/*------------------------------------------------------*/ /*------------------------------------------------------*/
@ -775,10 +742,8 @@ _magic_initialize(ClientData clientData,
/* (See graphics/grTkCommon.c) */ /* (See graphics/grTkCommon.c) */
/* (Unless "-dnull" option has been given) */ /* (Unless "-dnull" option has been given) */
#ifndef MAGIC_NO_TK
if (strcmp(MainDisplayType, "NULL")) if (strcmp(MainDisplayType, "NULL"))
RegisterTkCommands(interp); RegisterTkCommands(interp);
#endif
/* Set up the console so that its menu option File->Exit */ /* Set up the console so that its menu option File->Exit */
/* calls magic's exit routine first. This should not be */ /* calls magic's exit routine first. This should not be */

View File

@ -12,9 +12,7 @@
#ifdef MAGIC_WRAPPER #ifdef MAGIC_WRAPPER
#include <tcl.h> #include <tcl.h>
#ifndef MAGIC_NO_TK
#include <tk.h> #include <tk.h>
#endif
/* Externally-defined global variables */ /* Externally-defined global variables */
@ -32,7 +30,5 @@ extern void MakeWindowCommand();
extern const char *Tclmagic_InitStubsVersion; extern const char *Tclmagic_InitStubsVersion;
extern void TclmagicRegisterCommands(Tcl_Interp *interp);
#endif /* MAGIC_WRAPPER */ #endif /* MAGIC_WRAPPER */
#endif /* _MAGIC__TCLTK__TCLMAGIC_H */ #endif /* _MAGIC__TCLTK__TCLMAGIC_H */

View File

@ -651,9 +651,9 @@ proc magic::startselect {window {option {}}} {
select nocycle select nocycle
} }
} }
set Opts(origin) [cursor internal] set Opts(origin) [cursor]
set Opts(motion) [bind ${window} <Motion>] set Opts(motion) [bind ${window} <Motion>]
bind ${window} <Motion> [subst {$Opts(motion); set p \[cursor internal\]; \ bind ${window} <Motion> [subst {$Opts(motion); set p \[cursor\]; \
set x \[expr {\[lindex \$p 0\] - [lindex $Opts(origin) 0]}\]i; \ set x \[expr {\[lindex \$p 0\] - [lindex $Opts(origin) 0]}\]i; \
set y \[expr {\[lindex \$p 1\] - [lindex $Opts(origin) 1]}\]i; \ set y \[expr {\[lindex \$p 1\] - [lindex $Opts(origin) 1]}\]i; \
*bypass select move \${x} \${y}}] *bypass select move \${x} \${y}}]

View File

@ -481,28 +481,6 @@ proc magic::techmanager {{option "update"}} {
"magic::tech load $j ; \ "magic::tech load $j ; \
magic::techmanager update" magic::techmanager update"
} }
# Open PDK support: If environment variable $PDK_ROOT exists
# then search in that directory using the standard open PDK
# path <tech_name>/libs.tech/magic/<tech_name>.tech. NOTE:
# This can be done by sourcing the .magicrc file instead of
# loading the .tech file, but the window menus will not be
# updated until the next window is opened.
set dirtop {}
if {[catch {set dirtop $PDK_ROOT}]} {
catch {set dirtop $::env(PDK_ROOT)}
}
if {$dirtop != {}} {
set tlist [glob -nocomplain ${dirtop}/*]
foreach i [join $tlist] {
set j [file tail ${i}]
set rcfile ${i}/libs.tech/magic/${j}.magicrc
.techmgr.title.tname.menu add command -label ${j} -command \
"source ${rcfile} ; \
magic::techmanager update"
}
}
} }
set techlambda [magic::tech lambda] set techlambda [magic::tech lambda]

View File

@ -1,152 +0,0 @@
#!/usr/bin/env bash
# Build tcltk/tcl as a static WASM library for linking into magic.wasm.
#
# This script does NOT modify the TCL source tree — the build is fully
# out-of-source. configure is invoked from the build directory inside magic,
# with the TCL source tree referenced through $0's path. All generated files
# (Makefile, objects, tclConfig.sh, libtcl9.x.a, ...) live under $OUT.
#
# Outputs (under $OUT):
# $OUT/Makefile, *.o, tclConfig.sh, libtcl9.x.a, libtclstub.a
# $OUT/install/include/{tcl.h, tclDecls.h, ...}
# $OUT/install/lib/{libtcl9.x.a, libtclstub.a, tclConfig.sh, tcl9.x/<scripts>}
#
# Usage:
# build-tcl-wasm.sh --src=<TCL source tree> [--out=<build dir>] [--clean]
#
# Requirements: an activated emsdk (emcc/emconfigure/emmake on PATH), a host
# gcc (used to build TCL's minizip helper, which runs natively), make, and a
# git checkout of tcltk/tcl pointed to by --src.
#
# Note on line endings: if the TCL source tree was cloned on Windows with
# git's core.autocrlf=true, unix/configure may have CRLF line endings and
# bash will reject it. Clone with `git -c core.autocrlf=false clone ...` to
# avoid this; magic/npm/build.sh does that automatically.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# magic/ root is two levels up from toolchains/emscripten/.
MAGIC_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
TCL_SRC=""
OUT="$MAGIC_ROOT/build-tcl-wasm"
OPT_CLEAN=0
for arg in "$@"; do
case "$arg" in
--src=*) TCL_SRC=${arg#--src=} ;;
--out=*) OUT=${arg#--out=} ;;
--clean) OPT_CLEAN=1 ;;
*) echo "Unknown option: $arg" >&2; exit 1 ;;
esac
done
if [ -z "$TCL_SRC" ]; then
echo "Error: --src=<tcl source tree> is required." >&2
exit 1
fi
if [ ! -f "$TCL_SRC/unix/configure" ]; then
echo "Error: $TCL_SRC/unix/configure not found — not a TCL source tree." >&2
exit 1
fi
# Detect CRLF in configure up-front so the failure mode is a useful error and
# not a cryptic `set: pipefail: invalid option name` from bash.
if head -1 "$TCL_SRC/unix/configure" | grep -q $'\r'; then
echo "Error: $TCL_SRC/unix/configure has CRLF line endings." >&2
echo " Reclone tcltk/tcl with: git -c core.autocrlf=false clone …" >&2
exit 1
fi
# Normalise to absolute paths so the generated tclConfig.sh has stable paths.
TCL_SRC=$(cd "$TCL_SRC" && pwd)
mkdir -p "$OUT"
OUT=$(cd "$OUT" && pwd)
if [ $OPT_CLEAN -eq 1 ]; then
rm -rf "$OUT"
mkdir -p "$OUT"
fi
command -v emcc >/dev/null 2>&1 || {
echo "Error: emcc not on PATH. Activate emsdk first." >&2
exit 1
}
echo "Using emcc: $(command -v emcc)"
emcc --version | head -1
ncpu() {
if command -v nproc >/dev/null 2>&1; then nproc
else echo 2
fi
}
cd "$OUT"
# --- configure --------------------------------------------------------------
# Standard autoconf out-of-source pattern: invoke configure from the build
# dir via its absolute path. configure uses dirname($0) as srcdir.
#
# -sUSE_ZLIB=1 makes emcc inject its zlib port so configure finds zlib.h /
# deflateSetHeader on the link line. Without it, TCL falls back to its
# compat/zlib copy whose include path isn't picked up by tclEvent.o.
#
# --disable-shared we statically link libtcl into magic.wasm.
# --disable-load no dynamic loading inside wasm.
# --enable-symbols=no release (-O2). Override CFLAGS to add -g for debug.
if [ ! -f Makefile ]; then
echo "=== emconfigure ==="
CFLAGS="-O2 -sUSE_ZLIB=1" \
emconfigure "$TCL_SRC/unix/configure" \
--disable-shared \
--disable-load \
--enable-symbols=no \
--host=wasm32-unknown-emscripten \
--prefix="$OUT/install"
fi
# emconfigure writes tclConfig.sh into the build dir. If it is missing, the
# $(. tclConfig.sh) assignments below silently produce empty strings (bash does
# not propagate errors from command-substitution assignments through set -e),
# so we catch the missing file here before the cryptic "no rule to make target
# ''" error from emmake.
if [ ! -f "$OUT/tclConfig.sh" ]; then
echo "Error: $OUT/tclConfig.sh not found — emconfigure may have failed." >&2
exit 1
fi
# --- build ------------------------------------------------------------------
# HOST_CC/AR/RANLIB must be native — TCL's Makefile builds a `minizip` tool
# that runs on the host to produce the embedded zipfs resource. Without this
# override emcc would build minizip itself as a wasm module, which crashes on
# small stacks. HOST_OBJEXT is kept distinct from OBJEXT so host and target
# objects don't collide.
#
# Archive names come from tclConfig.sh so we don't bake in a fixed version.
TCL_LIB_FILE=$(. "$OUT/tclConfig.sh" && echo "$TCL_LIB_FILE")
TCL_STUB_LIB_FILE=$(. "$OUT/tclConfig.sh" && echo "$TCL_STUB_LIB_FILE")
HOST_OBJEXT=hostobj
echo "=== build ($TCL_LIB_FILE, $TCL_STUB_LIB_FILE) ==="
emmake make -j"$(ncpu)" \
HOST_CC=gcc HOST_AR=ar HOST_RANLIB=ranlib HOST_EXEEXT= HOST_OBJEXT="$HOST_OBJEXT" \
"$TCL_LIB_FILE" "$TCL_STUB_LIB_FILE"
# --- install ----------------------------------------------------------------
# install-headers populates $OUT/install/include with tcl.h + friends.
# install-libraries copies the Tcl script library (init.tcl, encodings, ...)
# under $OUT/install/lib/tcl9.x. We skip install-binaries because it would
# build a tclsh executable that we don't need; we cp the static archives
# manually so magic's configure (which scans <prefix>/lib for tclConfig.sh
# and a libtcl*.a) finds everything in one place.
emmake make \
HOST_CC=gcc HOST_AR=ar HOST_RANLIB=ranlib HOST_EXEEXT= HOST_OBJEXT="$HOST_OBJEXT" \
install-headers install-libraries
mkdir -p "$OUT/install/lib"
cp -f "$TCL_LIB_FILE" "$TCL_STUB_LIB_FILE" tclConfig.sh "$OUT/install/lib/"
echo
echo "=== artifacts ==="
ls -la "$TCL_LIB_FILE" "$TCL_STUB_LIB_FILE" tclConfig.sh 2>&1 | sed 's/^/ /'
echo " install/lib:"
ls -la install/lib 2>&1 | sed 's/^/ /'

View File

@ -24,12 +24,11 @@ TOP_EXTRA_LIBS += \
-sWASM=1 \ -sWASM=1 \
-sMODULARIZE=1 \ -sMODULARIZE=1 \
-sEXPORT_ES6=1 \ -sEXPORT_ES6=1 \
-sUSE_ZLIB=1 \
-sEXPORTED_FUNCTIONS=_magic_wasm_init,_magic_wasm_run_command,_magic_wasm_source_file,_magic_wasm_update \ -sEXPORTED_FUNCTIONS=_magic_wasm_init,_magic_wasm_run_command,_magic_wasm_source_file,_magic_wasm_update \
-sEXPORTED_RUNTIME_METHODS=cwrap,ccall,FS,setValue,getValue \ -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,FS,setValue,getValue \
-sALLOW_MEMORY_GROWTH=1 \ -sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=67108864 \ -sINITIAL_MEMORY=33554432 \
-Wl,-z,stack-size=10485760 \ -sSTACK_SIZE=5242880 \
-sASSERTIONS=1 \ -sASSERTIONS=1 \
-sENVIRONMENT=node,web,worker \ -sENVIRONMENT=node,web,worker \
-sFORCE_FILESYSTEM=1 \ -sFORCE_FILESYSTEM=1 \

View File

@ -563,7 +563,6 @@ mainInitAfterArgs()
SectionID sec_cifinput, sec_cifoutput; SectionID sec_cifinput, sec_cifoutput;
SectionID sec_drc, sec_extract, sec_wiring, sec_router; SectionID sec_drc, sec_extract, sec_wiring, sec_router;
SectionID sec_plow, sec_plot, sec_mzrouter; SectionID sec_plow, sec_plot, sec_mzrouter;
char *syspath;
DBTypeInit(); DBTypeInit();
MacroInit(); MacroInit();
@ -578,8 +577,9 @@ mainInitAfterArgs()
#endif #endif
/* /*
* Set up path names for system directory searches * Setup path names for system directory searches
*/ */
StrDup(&SysLibPath, MAGIC_SYS_PATH); StrDup(&SysLibPath, MAGIC_SYS_PATH);
/* /*