Compare commits
48 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
a38fa29dac | |
|
|
295ec7e492 | |
|
|
f650248764 | |
|
|
8466279b6c | |
|
|
7296aca107 | |
|
|
50320a055a | |
|
|
d37793e7d0 | |
|
|
4669fa9a9f | |
|
|
4fe8f12595 | |
|
|
bc340fba16 | |
|
|
c4923d44a2 | |
|
|
1ca85fa372 | |
|
|
6efb44db0a | |
|
|
db36e896e6 | |
|
|
ed576b23c8 | |
|
|
7c71d0f3e5 | |
|
|
5801acce62 | |
|
|
4dd0cfd472 | |
|
|
d873cd7f2b | |
|
|
2f2faf68eb | |
|
|
ab2515fd8f | |
|
|
e45db485d8 | |
|
|
8d61bae1f1 | |
|
|
3934b77f64 | |
|
|
788c73b867 | |
|
|
83569da8b3 | |
|
|
65f034777e | |
|
|
3667b348e8 | |
|
|
e2673e4e36 | |
|
|
14afb4bd52 | |
|
|
1db2567841 | |
|
|
0efed5813e | |
|
|
43e4cf9b03 | |
|
|
7a0e2aa2b9 | |
|
|
a157ec9aa8 | |
|
|
1a1cee058e | |
|
|
799fd5d8f2 | |
|
|
21b8579734 | |
|
|
03610f6d40 | |
|
|
c22031724a | |
|
|
349ffd091f | |
|
|
4393d9310a | |
|
|
1bcad6a25c | |
|
|
d9e6c78adb | |
|
|
d8580be739 | |
|
|
1a16502a69 | |
|
|
5ecf10c022 | |
|
|
e366cf6a4c |
|
|
@ -1,13 +1,17 @@
|
|||
name: CI-wasm
|
||||
|
||||
# Builds the Magic WebAssembly target on every push and pull request.
|
||||
# When the VERSION file changes on the default branch, the package is
|
||||
# additionally published to GitHub Packages (npm.pkg.github.com) as
|
||||
# @<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.
|
||||
# Builds the Magic WebAssembly target (both the non-TCL and TCL variants)
|
||||
# on every push and pull request as a CI check. **Publishing** only happens
|
||||
# when a release tag of the form v<x.y.z>... is pushed — that gate is the
|
||||
# manual release trigger:
|
||||
#
|
||||
# WASM is architecture-independent — built once on x86-64, usable everywhere.
|
||||
# # bump magic/VERSION, commit, push to default branch
|
||||
# git tag v8.3.638
|
||||
# git push origin v8.3.638
|
||||
#
|
||||
# The tag name (minus the leading "v") provides the base; the workflow appends
|
||||
# the commit date and short SHA: v8.3.799 → 8.3.799020261231+git01234cde.
|
||||
# Forks publish under their own namespace via the @<owner>/ scope.
|
||||
|
||||
on:
|
||||
push:
|
||||
|
|
@ -18,6 +22,14 @@ on:
|
|||
description: 'emsdk version to build with (default: latest; pin a version number to bisect)'
|
||||
type: string
|
||||
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:
|
||||
description: 'Dry run: pack only, do not publish even on tag pushes'
|
||||
type: boolean
|
||||
|
|
@ -70,32 +82,65 @@ jobs:
|
|||
echo "===== emcc -dM -E - ====="; echo | emcc -dM -E - | sort
|
||||
echo "===== em++ -dM -E - ====="; echo | em++ -dM -E - | sort
|
||||
|
||||
- name: Build WASM
|
||||
# Determine which TCL ref to build against.
|
||||
# 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: |
|
||||
source ./emsdk/emsdk_env.sh
|
||||
# --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
|
||||
bash npm/build.sh --variant=both
|
||||
|
||||
- name: Copy WASM artifacts into npm/
|
||||
run: |
|
||||
cp magic/magic.js npm/
|
||||
cp magic/magic.wasm npm/
|
||||
- name: Run example tests (non-TCL variant)
|
||||
run: cd npm && npm test
|
||||
|
||||
- name: Run example tests
|
||||
run: cd npm && npm run test
|
||||
- name: Run full test suite (TCL variant)
|
||||
run: cd npm && npm run test:tcl
|
||||
|
||||
# Dump generated text outputs (.ext, .spice, .cif, …) into the CI log
|
||||
# so a regression in extraction / netlisting / cifoutput is visible
|
||||
|
|
@ -104,29 +149,61 @@ jobs:
|
|||
- name: Display example outputs
|
||||
run: |
|
||||
shopt -s nullglob
|
||||
for f in npm/examples/output/*; do
|
||||
name=$(basename "$f")
|
||||
case "$f" in
|
||||
*.gds) echo "===== $name (binary, $(wc -c < "$f") bytes — skipped) =====" ;;
|
||||
*) echo "===== $name ====="; cat "$f" ;;
|
||||
esac
|
||||
for dir in npm/examples/output npm/examples/output-tcl; do
|
||||
[ -d "$dir" ] || continue
|
||||
echo "======== $dir ========"
|
||||
for f in "$dir"/*; do
|
||||
name=$(basename "$f")
|
||||
case "$f" in
|
||||
*.gds) echo "===== $name (binary, $(wc -c < "$f") bytes — skipped) =====" ;;
|
||||
*) echo "===== $name ====="; cat "$f" ;;
|
||||
esac
|
||||
done
|
||||
done
|
||||
|
||||
- name: Set package version and scope
|
||||
# The release gate. We publish a new npm version only when a tag of the
|
||||
# shape v<x.y.z>... is pushed.
|
||||
#
|
||||
# Version scheme (per dmiles' recommendation):
|
||||
# {MAJOR}.{MINOR}.{PATCH}0{YYYYMMDD}+git{SHORT_SHA}
|
||||
# e.g. v8.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
|
||||
env:
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
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
|
||||
# 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"
|
||||
cd npm
|
||||
npm pkg set name="$SCOPED_NAME"
|
||||
npm pkg set publishConfig.registry="https://npm.pkg.github.com"
|
||||
npm version "$VERSION" --no-git-tag-version
|
||||
npm version "$VERSION" --no-git-tag-version --allow-same-version
|
||||
|
||||
- name: Pack
|
||||
run: ./npm/pack.sh
|
||||
|
|
@ -137,24 +214,42 @@ jobs:
|
|||
name: magic-vlsi-wasm-npm
|
||||
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
|
||||
if: steps.version_changed.outputs.changed == 'true' && github.event.inputs.dry_run != 'true'
|
||||
if: steps.release.outputs.publish == 'true' && github.event.inputs.dry_run != 'true'
|
||||
run: cd npm && npm publish
|
||||
env:
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ magic/magic.js
|
|||
magic/magic.js.symbols
|
||||
magic/magic.symbols
|
||||
magic/magic.wasm
|
||||
build-tcl-wasm/
|
||||
net2ir/net2ir
|
||||
net2ir/net2ir.js
|
||||
net2ir/net2ir.wasm
|
||||
|
|
|
|||
|
|
@ -537,7 +537,6 @@ calmaElementPath(void)
|
|||
int layer, dt, width, pathtype, ciftype, savescale;
|
||||
int xmin, ymin, xmax, ymax, temp;
|
||||
CIFPath *pathheadp, *pathp, *previousp;
|
||||
Rect segment;
|
||||
Plane *plane;
|
||||
int first,last;
|
||||
CellUse *use;
|
||||
|
|
|
|||
65
cif/CIFgen.c
65
cif/CIFgen.c
|
|
@ -4171,12 +4171,21 @@ cifSrTiles2(
|
|||
}
|
||||
|
||||
cifScale = 1;
|
||||
for (t = 0; t < TT_MAXTYPES; t++, temps++)
|
||||
if (TTMaskHasType(&cifOp->co_cifMask, t))
|
||||
if (DBSrPaintArea((Tile *)NULL, *temps, area,
|
||||
if (TTMaskIsZero(&cifOp->co_cifMask) && TTMaskIsZero(&cifOp->co_paintMask))
|
||||
{
|
||||
/* Current CIF plane is in *temps */
|
||||
if (DBSrPaintArea((Tile *)NULL, *temps, area,
|
||||
&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;
|
||||
}
|
||||
|
||||
|
|
@ -5454,7 +5463,6 @@ CIFGenLayer(
|
|||
|
||||
if (bloats->bl_plane < 0) /* Bloat types are CIF types */
|
||||
{
|
||||
bls.temps = temps;
|
||||
for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++)
|
||||
if (bloats->bl_distance[ttype] > 0)
|
||||
(void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect,
|
||||
|
|
@ -5660,6 +5668,20 @@ CIFGenLayer(
|
|||
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 */
|
||||
op->co_client = (ClientData)bloats;
|
||||
|
|
@ -5680,15 +5702,33 @@ CIFGenLayer(
|
|||
}
|
||||
|
||||
for (label = cellDef->cd_labels; label; label = label->lab_next)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Reset marked tiles */
|
||||
|
||||
if (bloats->bl_plane < 0) /* Bloat types are CIF types */
|
||||
{
|
||||
bls.temps = temps;
|
||||
for (ttype = 0; ttype < TT_MAXTYPES; ttype++, bls.temps++)
|
||||
if (bloats->bl_distance[ttype] > 0)
|
||||
(void) DBSrPaintArea((Tile *)NULL, *bls.temps, &TiPlaneRect,
|
||||
|
|
@ -5706,6 +5746,15 @@ CIFGenLayer(
|
|||
/* Replace the client data */
|
||||
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;
|
||||
|
||||
case CIFOP_BOUNDARY:
|
||||
|
|
|
|||
|
|
@ -462,9 +462,22 @@ CIFPaintWirePath(
|
|||
/* Wire reverses direction. Break wire here, */
|
||||
/* draw, and start new polygon. */
|
||||
|
||||
TxError("Warning: direction reversal in path at (%d, %d).\n",
|
||||
pathp->cifp_x, pathp->cifp_y);
|
||||
/* Check first if last point and current point */
|
||||
/* are the same, in which case a different */
|
||||
/* 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;
|
||||
if (endcap)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1032,9 +1032,10 @@ CIFReadTechLine(
|
|||
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
|
||||
break;
|
||||
case CIFOP_TAGGED:
|
||||
if (argc != 3) goto wrongNumArgs;
|
||||
if ((argc != 2) && (argc != 3)) goto wrongNumArgs;
|
||||
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
|
||||
CIFParseReadLayers(argv[2], &newOp->co_cifMask, TRUE);
|
||||
if (argc == 3)
|
||||
CIFParseReadLayers(argv[2], &newOp->co_cifMask, TRUE);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1360,10 +1360,11 @@ bloatCheck:
|
|||
|
||||
case CIFOP_NET:
|
||||
case CIFOP_TAGGED:
|
||||
if (argc != 3) goto wrongNumArgs;
|
||||
if ((argc != 2) && (argc != 3)) goto wrongNumArgs;
|
||||
newOp->co_client = (ClientData)StrDup((char **)NULL, argv[1]);
|
||||
cifParseLayers(argv[2], CIFCurStyle, &newOp->co_paintMask,
|
||||
&newOp->co_cifMask, FALSE);
|
||||
if (argc == 3)
|
||||
cifParseLayers(argv[2], CIFCurStyle, &newOp->co_paintMask,
|
||||
&newOp->co_cifMask, FALSE);
|
||||
break;
|
||||
|
||||
case CIFOP_MASKHINTS:
|
||||
|
|
|
|||
|
|
@ -1096,7 +1096,7 @@ CmdExtract(
|
|||
"lumped estimate lumped resistance",
|
||||
"labelcheck check for connections through sticky labels",
|
||||
"aliases output all net name aliases",
|
||||
"unique ensure unique node names during extraction",
|
||||
"unique [notopports] ensure unique node names during extraction",
|
||||
"resistance extract resistance (same as \"do extresist\")",
|
||||
NULL
|
||||
};
|
||||
|
|
@ -1403,6 +1403,7 @@ CmdExtract(
|
|||
TxPrintf("%s label check\n", OPTSET(EXT_DOLABELCHECK));
|
||||
TxPrintf("%s aliases\n", OPTSET(EXT_DOALIASES));
|
||||
TxPrintf("%s unique\n", OPTSET(EXT_DOUNIQUE));
|
||||
TxPrintf("%s unique notopports\n", OPTSET(EXT_DOUNIQNOTOPPORTS));
|
||||
TxPrintf("%s resistance (extresist)\n", OPTSET(EXT_DOEXTRESIST));
|
||||
return;
|
||||
#undef OPTSET
|
||||
|
|
@ -1433,9 +1434,19 @@ CmdExtract(
|
|||
case DORESISTANCE: option = EXT_DORESISTANCE; break;
|
||||
case DOLABELCHECK: option = EXT_DOLABELCHECK; break;
|
||||
case DOALIASES: option = EXT_DOALIASES; break;
|
||||
case DOUNIQUE: option = EXT_DOUNIQUE; break;
|
||||
case DOEXTRESIST:
|
||||
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:
|
||||
/* "extract do local" and "extract no local" are kept for
|
||||
* backwards compatibility, but now effectively implement
|
||||
|
|
|
|||
|
|
@ -2324,7 +2324,7 @@ CmdDoProperty(
|
|||
TxCommand *cmd,
|
||||
int argstart)
|
||||
{
|
||||
PropertyRecord *proprec;
|
||||
PropertyRecord *proprec = NULL;
|
||||
char *value;
|
||||
bool propfound, dolist;
|
||||
int proptype, proplen, propvalue, i;
|
||||
|
|
@ -2662,31 +2662,31 @@ CmdDoProperty(
|
|||
* the valid number of arguments, then again to parse the
|
||||
* values, once the property record has been allocated
|
||||
*/
|
||||
if (proptype == PROPERTY_TYPE_PLANE)
|
||||
value = cmd->tx_argv[argstart + 1];
|
||||
for (proplen = 0; *value != '\0'; )
|
||||
{
|
||||
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord));
|
||||
plane = DBNewPlane((ClientData)TT_SPACE);
|
||||
proprec->prop_value.prop_plane = plane;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = cmd->tx_argv[argstart + 1];
|
||||
for (proplen = 0; *value != '\0'; )
|
||||
if (isspace(*value) && (*value != '\0')) value++;
|
||||
if (!isspace(*value))
|
||||
{
|
||||
if (isspace(*value) && (*value != '\0')) value++;
|
||||
if (!isspace(*value))
|
||||
{
|
||||
proplen++;
|
||||
while (!isspace(*value) && (*value != '\0')) value++;
|
||||
}
|
||||
proplen++;
|
||||
while (!isspace(*value) && (*value != '\0')) value++;
|
||||
}
|
||||
if (proplen > 0)
|
||||
}
|
||||
if (proplen > 0)
|
||||
{
|
||||
if (proptype == PROPERTY_TYPE_PLANE)
|
||||
{
|
||||
proprec = (PropertyRecord *)mallocMagic(sizeof(PropertyRecord));
|
||||
plane = DBNewPlane((ClientData)TT_SPACE);
|
||||
proprec->prop_value.prop_plane = plane;
|
||||
} else {
|
||||
proprec = (PropertyRecord *)mallocMagic(
|
||||
sizeof(PropertyRecord) +
|
||||
(proplen - 2) * sizeof(int));
|
||||
}
|
||||
proprec->prop_type = proptype;
|
||||
proprec->prop_len = proplen;
|
||||
}
|
||||
proprec->prop_type = proptype;
|
||||
proprec->prop_len = proplen;
|
||||
|
||||
/* Second pass */
|
||||
value = cmd->tx_argv[argstart + 1];
|
||||
|
|
|
|||
|
|
@ -647,6 +647,9 @@ cmdSelectArea(
|
|||
if (!(crec->dbw_flags & DBW_SEELABELS)) TTMaskClearType(&mask, L_LABEL);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,10 +53,10 @@ typedef struct dbcellboundstruct
|
|||
*/
|
||||
|
||||
int
|
||||
DBBoundCellPlane(def, extended, rect)
|
||||
CellDef *def;
|
||||
Rect *extended;
|
||||
Rect *rect;
|
||||
DBBoundCellPlane(
|
||||
CellDef *def,
|
||||
Rect *extended,
|
||||
Rect *rect)
|
||||
{
|
||||
TreeFilter filter;
|
||||
DBCellBoundStruct cbs;
|
||||
|
|
@ -79,9 +79,9 @@ DBBoundCellPlane(def, extended, rect)
|
|||
}
|
||||
|
||||
int
|
||||
dbCellBoundFunc(use, fp)
|
||||
CellUse *use;
|
||||
TreeFilter *fp;
|
||||
dbCellBoundFunc(
|
||||
CellUse *use,
|
||||
TreeFilter *fp)
|
||||
{
|
||||
DBCellBoundStruct *cbs;
|
||||
|
||||
|
|
@ -124,9 +124,9 @@ dbCellBoundFunc(use, fp)
|
|||
*/
|
||||
|
||||
bool
|
||||
DBBoundPlane(plane, rect)
|
||||
Plane *plane;
|
||||
Rect *rect;
|
||||
DBBoundPlane(
|
||||
Plane *plane,
|
||||
Rect *rect)
|
||||
{
|
||||
Tile *left, *right, *top, *bottom, *tp;
|
||||
|
||||
|
|
@ -205,9 +205,9 @@ DBBoundPlane(plane, rect)
|
|||
*/
|
||||
|
||||
bool
|
||||
DBBoundPlaneVert(plane, rect)
|
||||
Plane *plane;
|
||||
Rect *rect;
|
||||
DBBoundPlaneVert(
|
||||
Plane *plane,
|
||||
Rect *rect)
|
||||
{
|
||||
Tile *left, *right, *top, *bottom, *tp;
|
||||
|
||||
|
|
|
|||
|
|
@ -1037,7 +1037,13 @@ dbcConnectFunc(tile, dinfo, cx)
|
|||
if (++csa2->csa2_top == CSA2_LIST_SIZE)
|
||||
{
|
||||
/* Reached list size limit---need to push the list and */
|
||||
/* start a new one. */
|
||||
/* start a new one. NOTE: Setting lasttop to -1 means */
|
||||
/* 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;
|
||||
|
||||
|
|
@ -1045,6 +1051,7 @@ dbcConnectFunc(tile, dinfo, cx)
|
|||
StackPush((ClientData)csa2->csa2_list, csa2->csa2_stack);
|
||||
csa2->csa2_list = newlist;
|
||||
csa2->csa2_top = 0;
|
||||
csa2->csa2_lasttop = -1;
|
||||
}
|
||||
|
||||
csa2->csa2_list[csa2->csa2_top].area = newarea;
|
||||
|
|
|
|||
|
|
@ -574,7 +574,8 @@ DBReOrientLabel(cellDef, area, newPos)
|
|||
* dbGetLabelArea ---
|
||||
*
|
||||
* Callback function used by DBAdjustLabels. Find all material under a label
|
||||
* that is *not* the label type, and return the
|
||||
* that is *not* the label type, and return the label area adjusted to leave
|
||||
* out that amount.
|
||||
*
|
||||
* 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.
|
||||
|
|
@ -604,6 +605,26 @@ dbGetLabelArea(tile, dinfo, area)
|
|||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
|
|
@ -661,28 +682,37 @@ DBAdjustLabels(def, area)
|
|||
|
||||
TTMaskSetOnlyType(&lmask, lab->lab_type);
|
||||
/* To do: Add compatible types (contact, residue) */
|
||||
TTMaskCom(&lmask);
|
||||
|
||||
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 there is no material left inside the label area, then
|
||||
* the label gets reassigned to space.
|
||||
*/
|
||||
if (DBSrPaintArea((Tile *) NULL, def->cd_planes[DBPlane(lab->lab_type)],
|
||||
&lab->lab_rect, &lmask, dbLabelNotEmpty, (ClientData)NULL) == 1)
|
||||
{
|
||||
if ((DBVerbose >= DB_VERBOSE_ALL) && ((def->cd_flags & CDINTERNAL) == 0))
|
||||
{
|
||||
TxPrintf("Adjusting size of label \"%s\" in cell %s.\n",
|
||||
lab->lab_text, def->cd_name);
|
||||
}
|
||||
TTMaskCom(&lmask);
|
||||
|
||||
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;
|
||||
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) &&
|
||||
((def->cd_flags & CDINTERNAL) == 0))
|
||||
{
|
||||
TxPrintf("Adjusting size of label \"%s\" in cell %s.\n",
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -578,6 +578,20 @@ typedef struct extRectList
|
|||
struct extRectList *r_next;
|
||||
} 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 contexts are used in hierarchical searches */
|
||||
|
|
|
|||
|
|
@ -102,16 +102,17 @@ Circuit netlist extractor
|
|||
but will usually just slow down processing by commands
|
||||
like "ext2spice" that use the .ext file contents, so it
|
||||
is disabled by default.
|
||||
<DT> <B>unique</B>
|
||||
<DT> <B>unique</B> [<B>notopports</B>]
|
||||
<DD> (Added in magic version 8.3.594) This setting replaces
|
||||
the use of the command option "extract unique". Instead
|
||||
of changing labels in the design, unique labels are
|
||||
generated for the duration of the extraction, and then
|
||||
reverted back to the original text. The "extract unique"
|
||||
command option is maintained for backwards compatibility.
|
||||
Note the difference: "extract unique" is a command that
|
||||
runs immediately, and cannot be undone;
|
||||
"extract do unique" is an option setting for "extract".
|
||||
the use of the command option "extract unique" (and
|
||||
"extract unique notopports"). Instead of changing labels
|
||||
in the design, unique labels are generated for the duration
|
||||
of the extraction, and then reverted back to the original
|
||||
text. The "extract unique" command option is maintained
|
||||
for backwards compatibility. Note the difference:
|
||||
"extract unique" is a command that runs immediately, and
|
||||
cannot be undone; "extract do unique" is an option setting
|
||||
for "extract".
|
||||
<DT> <B>resistance</B>
|
||||
<DD> (Added in magic version 8.3.597) This setting replaces
|
||||
the use of the standalone command "extresist". The
|
||||
|
|
|
|||
|
|
@ -30,11 +30,30 @@ information.
|
|||
<BLOCKQUOTE>
|
||||
where <I>option</I> may be one of the following:
|
||||
<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>
|
||||
<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>]
|
||||
<DD> Turn on/off simplification of resistor nets.
|
||||
<DT> <B>extout</B> [<B>on</B>|<B>off</B>]
|
||||
|
|
@ -42,7 +61,9 @@ information.
|
|||
<DT> <B>lumped</B> [<B>on</B>|<B>off</B>]
|
||||
<DD> Turn on/off writing of updated lumped resistances.
|
||||
<DT> <B>silent</B> [<B>on</B>|<B>off</B>]
|
||||
<DD> Turn off/on printing of net statistics.
|
||||
<DD> Turn off/on printing of nets being processed.
|
||||
<DT> <B>debug</B> [<B>on</B>|<B>off</B>]
|
||||
<DD> Turn off/on additional diagnostic information.
|
||||
<DT> <B>skip</B> <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>]
|
||||
|
|
@ -69,6 +90,8 @@ information.
|
|||
<TT>.fh</TT> file. If <I>freq</I> is specified, the file will
|
||||
be customized for <B>fasthenry</B> analysis at the indicated
|
||||
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>
|
||||
<DD> Print help information
|
||||
</DL>
|
||||
|
|
@ -79,9 +102,9 @@ information.
|
|||
<BLOCKQUOTE>
|
||||
The normal flow through layout extraction into a simulation file
|
||||
treats routes as nonphysical entities, that is, with infinitesimal
|
||||
impedence through the wires. Extraction for digital simulation
|
||||
using <B>irsim</B> generates "lumped resistances", a single
|
||||
resistance per network node that, along with the node capacitance
|
||||
impedence through the wires. Standard extraction generates "lumped
|
||||
resistance" values, a single resistance per network node that, along
|
||||
with the node capacitance
|
||||
to substrate, provides an <I>RC</I> time constant to approximately
|
||||
model the delay from point to point in the network node. The
|
||||
lumped resistance model is inappropriate for analog (i.e., SPICE)
|
||||
|
|
@ -96,10 +119,9 @@ information.
|
|||
|
||||
Using <B>extresist</B> as a standalone command is a multi-step
|
||||
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
|
||||
<B>extresist</B> command may be run. The output is
|
||||
a file <TT>.res.ext</TT> for each cell in the hierarchy.
|
||||
the initial netlist. After a <TT>.ext</TT> file has been generated,
|
||||
the <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>
|
||||
will generate the final, detailed simulation file. <P>
|
||||
|
||||
|
|
@ -110,7 +132,42 @@ information.
|
|||
magic version 8.3.597 an option <B>extract do resistance</B>
|
||||
that runs the resistance extraction in sequence with the regular
|
||||
extraction, producing both the <TT>.ext</TT> and <TT>.res.ext</TT>
|
||||
files.
|
||||
files. When <B>extract do resistance</B> is used, there is no need
|
||||
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
|
||||
<B>magic</B> Tutorial number 8.
|
||||
|
|
@ -134,6 +191,6 @@ information.
|
|||
<TD> <A HREF=commands.html>Return to command index</A>
|
||||
</TR>
|
||||
</TABLE>
|
||||
<P><I>Last updated:</I> October 4, 2021 at 3:32pm <P>
|
||||
<P><I>Last updated:</I> May 29, 2026 at 11:33am <P>
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
|
|
|||
|
|
@ -757,7 +757,7 @@ spcdevHierVisit(
|
|||
case DEV_FET:
|
||||
if (source == drain)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
||||
fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n");
|
||||
}
|
||||
break;
|
||||
|
|
@ -765,7 +765,7 @@ spcdevHierVisit(
|
|||
default:
|
||||
if (gate == source)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
||||
fprintf(esSpiceF, "** SHORTED DEVICE\n");
|
||||
}
|
||||
break;
|
||||
|
|
@ -1658,7 +1658,7 @@ spcnodeHierVisit(
|
|||
static char ntmp[MAX_STR_SIZE];
|
||||
|
||||
EFHNSprintf(ntmp, hierName);
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " ; ");
|
||||
fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
|
||||
}
|
||||
cap = cap / 1000;
|
||||
|
|
@ -1668,14 +1668,14 @@ spcnodeHierVisit(
|
|||
esSIvalue(esSpiceF, 1.0E-15 * cap);
|
||||
if (!isConnected)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " $");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " ;");
|
||||
fprintf(esSpiceF, " **FLOATING");
|
||||
}
|
||||
fprintf(esSpiceF, "\n");
|
||||
}
|
||||
if (node->efnode_attrs && !esNoAttrs)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " ; ");
|
||||
fprintf(esSpiceF, "**nodeattr %s :",nsn );
|
||||
for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2817,7 +2817,7 @@ spcdevVisit(
|
|||
case DEV_FET:
|
||||
if (source == drain)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
||||
fprintf(esSpiceF, "** SOURCE/DRAIN TIED\n");
|
||||
}
|
||||
break;
|
||||
|
|
@ -2825,7 +2825,7 @@ spcdevVisit(
|
|||
default:
|
||||
if (gate == source)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
||||
fprintf(esSpiceF, "** SHORTED DEVICE\n");
|
||||
}
|
||||
break;
|
||||
|
|
@ -4111,7 +4111,7 @@ spcnodeVisit(
|
|||
static char ntmp[MAX_STR_SIZE];
|
||||
|
||||
EFHNSprintf(ntmp, hierName);
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "$ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, "; ");
|
||||
fprintf(esSpiceF, "** %s == %s\n", ntmp, nsn);
|
||||
}
|
||||
cap = cap / 1000;
|
||||
|
|
@ -4121,14 +4121,14 @@ spcnodeVisit(
|
|||
esSIvalue(esSpiceF, 1.0E-15 * cap);
|
||||
if (!isConnected)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " $");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " ;");
|
||||
fprintf(esSpiceF, " **FLOATING");
|
||||
}
|
||||
fprintf(esSpiceF, "\n");
|
||||
}
|
||||
if (node->efnode_attrs && !esNoAttrs)
|
||||
{
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " $ ");
|
||||
if (esFormat == NGSPICE) fprintf(esSpiceF, " ; ");
|
||||
fprintf(esSpiceF, "**nodeattr %s :",nsn );
|
||||
for (fmt = " %s", ap = node->efnode_attrs; ap; ap = ap->efa_next)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -706,6 +706,11 @@ efBuildEquiv(def, nodeName1, nodeName2, resist, isspice)
|
|||
dev->dev_terms[n].dterm_node =
|
||||
(nn1->efnn_node == NULL) ?
|
||||
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 */
|
||||
|
|
@ -1618,12 +1623,128 @@ 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 --
|
||||
*
|
||||
* 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
|
||||
* 'nodeName2'.
|
||||
*
|
||||
|
|
@ -1638,7 +1759,7 @@ efConnectionFreeLinkedList(Connection *conn)
|
|||
*/
|
||||
|
||||
void
|
||||
efBuildConnect(def, nodeName1, nodeName2, deltaC, av, ac)
|
||||
efBuildMerge(def, nodeName1, nodeName2, deltaC, av, ac)
|
||||
Def *def; /* Def to which this connection is to be added */
|
||||
char *nodeName1; /* Name of first node in connection */
|
||||
char *nodeName2; /* Name of other node in connection */
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ EFDone(func)
|
|||
efConnectionFreeLinkedList(def->def_conns);
|
||||
efConnectionFreeLinkedList(def->def_caps);
|
||||
efConnectionFreeLinkedList(def->def_resistors);
|
||||
efConnPointFreeLinkedList(def->def_connpts);
|
||||
|
||||
free_magic1_t mm1 = freeMagic1_init();
|
||||
for (kill = def->def_kills; kill; kill = kill->kill_next)
|
||||
|
|
@ -248,6 +249,7 @@ efDefNew(name)
|
|||
newdef->def_conns = (Connection *) NULL;
|
||||
newdef->def_caps = (Connection *) NULL;
|
||||
newdef->def_resistors = (Connection *) NULL;
|
||||
newdef->def_connpts = (ConnectionPoint *) NULL;
|
||||
newdef->def_kills = (Kill *) NULL;
|
||||
|
||||
/* Initialize circular list of nodes */
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#include "utils/utils.h"
|
||||
#include "extflat/extflat.h"
|
||||
#include "extflat/EFint.h"
|
||||
#include "textio/textio.h"
|
||||
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
|
|
@ -491,7 +492,13 @@ efHierVisitDevs(hc, ca)
|
|||
{
|
||||
dev = (Dev *)HashGetValue(he);
|
||||
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 */
|
||||
if ((*ca_hiervisitdevs_proc)(hc, dev, scale, ca->ca_cdata)) /* @invoke cb_extflat_hiervisitdevs_t */
|
||||
|
|
|
|||
|
|
@ -145,6 +145,8 @@ typedef struct conn
|
|||
#define conn_res conn_value.conn_val_res
|
||||
#define conn_cap conn_value.conn_val_cap
|
||||
|
||||
typedef struct connpoint ConnectionPoint;
|
||||
|
||||
/* -------------------------- Defs and uses --------------------------- */
|
||||
|
||||
/* A Def exists for each .ext file */
|
||||
|
|
@ -162,6 +164,7 @@ typedef struct def
|
|||
/* The following are all NULL-terminated lists */
|
||||
|
||||
Connection *def_conns; /* Hierarchical connections/adjustments */
|
||||
ConnectionPoint *def_connpts; /* Position of hierarchical connections */
|
||||
Connection *def_caps; /* Two-terminal capacitors */
|
||||
Connection *def_resistors; /* Two-terminal resistors */
|
||||
Kill *def_kills; /* Used to modify hierarchical structure
|
||||
|
|
@ -206,6 +209,17 @@ typedef struct use
|
|||
|
||||
#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 */
|
||||
|
|
@ -307,7 +321,9 @@ extern void CapHashSetValue();
|
|||
extern DevParam *efGetDeviceParams();
|
||||
extern void efBuildNode();
|
||||
extern void efConnectionFreeLinkedList(Connection *conn);
|
||||
extern void efConnPointFreeLinkedList(ConnectionPoint *conn);
|
||||
extern void efBuildConnect();
|
||||
extern void efBuildMerge();
|
||||
extern void efBuildResistor();
|
||||
extern void efBuildCap();
|
||||
extern HierContext *EFFlatBuildOneLevel();
|
||||
|
|
|
|||
|
|
@ -275,15 +275,11 @@ readfile:
|
|||
efBuildCap(def, argv[1], argv[2], (double) cap);
|
||||
break;
|
||||
|
||||
/* subcap node capacitance */
|
||||
case SUBCAP:
|
||||
cap = cscale*atoCap(argv[2]);
|
||||
efAdjustSubCap(def, argv[1], cap);
|
||||
break;
|
||||
|
||||
/* equiv node1 node2 */
|
||||
case EQUIV:
|
||||
efBuildEquiv(def, argv[1], argv[2], resist, isspice);
|
||||
/* connect useid llx lly urx ury type "node" ... */
|
||||
case CONNECT:
|
||||
efBuildConnect(def, atoi(argv[1]), atoi(argv[2]),
|
||||
atoi(argv[3]), atoi(argv[4]), argv[5],
|
||||
argv[6], argv[7]);
|
||||
break;
|
||||
|
||||
/* replaces "fet" (below) */
|
||||
|
|
@ -337,6 +333,41 @@ readfile:
|
|||
r.r_xtop = (int)(0.5 + (float)atoi(argv[5]) * 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)
|
||||
{
|
||||
efReadError("Incomplete terminal description for device\n");
|
||||
|
|
@ -344,6 +375,11 @@ readfile:
|
|||
}
|
||||
break;
|
||||
|
||||
/* equiv node1 node2 */
|
||||
case EQUIV:
|
||||
efBuildEquiv(def, argv[1], argv[2], resist, isspice);
|
||||
break;
|
||||
|
||||
/* for backwards compatibility */
|
||||
/* fet type xlo ylo xhi yhi area perim substrate GATE T1 T2 ... */
|
||||
case FET:
|
||||
|
|
@ -373,7 +409,7 @@ readfile:
|
|||
*/
|
||||
|
||||
cap = (argc > 3) ? atoCap(argv[3]) * cscale : 0;
|
||||
efBuildConnect(def, argv[1], argv[2], (double)cap, &argv[4], argc - 4);
|
||||
efBuildMerge(def, argv[1], argv[2], (double)cap, &argv[4], argc - 4);
|
||||
break;
|
||||
|
||||
/* node name R C x y layer a1 p1 a2 p2 ... [ attrs ] */
|
||||
|
|
@ -449,6 +485,12 @@ resistChanged:
|
|||
}
|
||||
break;
|
||||
|
||||
/* subcap node capacitance */
|
||||
case SUBCAP:
|
||||
cap = cscale*atoCap(argv[2]);
|
||||
efAdjustSubCap(def, argv[1], cap);
|
||||
break;
|
||||
|
||||
/* use def use-id T0 .. T5 */
|
||||
case USE:
|
||||
efBuildUse(def, argv[1], argv[2],
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@
|
|||
*/
|
||||
typedef enum
|
||||
{
|
||||
ABSTRACT, ADJUST, ATTR, CAP, DEVICE, DIST, EQUIV, FET, KILLNODE, MERGE,
|
||||
NODE, PARAMETERS, PORT, PRIMITIVE, RESISTOR, RESISTCLASS, RNODE, SCALE,
|
||||
SUBCAP, SUBSTRATE, TECH, TIMESTAMP, USE, VERSION, EXT_STYLE
|
||||
ABSTRACT, ADJUST, ATTR, CAP, CONNECT, DEVICE, DIST, EQUIV, FET, KILLNODE,
|
||||
MERGE, NODE, PARAMETERS, PORT, PRIMITIVE, RESISTOR, RESISTCLASS, RNODE,
|
||||
SCALE, SUBCAP, SUBSTRATE, TECH, TIMESTAMP, USE, VERSION, EXT_STYLE
|
||||
} Key;
|
||||
|
||||
static const struct
|
||||
|
|
@ -42,6 +42,7 @@ keyTable[] =
|
|||
{"adjust", ADJUST, 4},
|
||||
{"attr", ATTR, 8},
|
||||
{"cap", CAP, 4},
|
||||
{"connect", CONNECT, 7},
|
||||
{"device", DEVICE, 11}, /* effectively replaces "fet" */
|
||||
{"distance", DIST, 4},
|
||||
{"equiv", EQUIV, 3},
|
||||
|
|
|
|||
|
|
@ -629,6 +629,8 @@ extSetResist(reg)
|
|||
|
||||
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_perim = perim = extResistPerim[n];
|
||||
if (area > 0 && perim > 0)
|
||||
|
|
@ -639,8 +641,15 @@ extSetResist(reg)
|
|||
if (v < 0) s = 0; else s = sqrt(v);
|
||||
|
||||
fperim = (float) perim;
|
||||
reg->nreg_resist += (fperim + s) / (fperim - s)
|
||||
* ExtCurStyle->exts_resistByResistClass[n];
|
||||
resnew = (fperim + s) / (fperim - s) *
|
||||
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 */
|
||||
|
|
@ -722,7 +731,8 @@ extOutputNodes(nodeList, outFile)
|
|||
/* Check if this node is the substrate */
|
||||
if (reg == glob_subsnode)
|
||||
{
|
||||
fprintf(outFile, "substrate \"%s\" 0 0", text);
|
||||
intR = (reg->nreg_resist + rround) / ExtCurStyle->exts_resistScale;
|
||||
fprintf(outFile, "substrate \"%s\" %d 0", text, intR);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -811,8 +821,14 @@ extOutputNodes(nodeList, outFile)
|
|||
* For a net to be shorted to itself is not an error.
|
||||
* NOTE: Potentially the unique name could be removed
|
||||
* 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) &&
|
||||
(!(ExtOptions & EXT_DORESISTANCE)) &&
|
||||
(ll->ll_attr == LL_PORTATTR) &&
|
||||
(strcmp(ll->ll_label->lab_text, portname)))
|
||||
{
|
||||
|
|
@ -1110,7 +1126,7 @@ ExtSortTerminals(tran, ll)
|
|||
do
|
||||
{
|
||||
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]);
|
||||
p2 = &(tran->tr_termpos[nsd+1]);
|
||||
|
|
@ -1155,14 +1171,17 @@ ExtSortTerminals(tran, ll)
|
|||
* but S,D attributes are not that common so it should not matter
|
||||
* that much -- Stefanos 5/96 */
|
||||
|
||||
for ( lp = ll ; lp ; lp = lp->ll_next )
|
||||
if ( lp->ll_attr == nsd ) lp->ll_attr = LL_SORTATTR ;
|
||||
else if ( lp->ll_attr == nsd+1 ) lp->ll_attr = nsd ;
|
||||
for ( lp = ll ; lp ; lp = lp->ll_next )
|
||||
if ( lp->ll_attr == LL_SORTATTR ) lp->ll_attr = nsd+1;
|
||||
for (lp = ll; lp; lp = lp->ll_next)
|
||||
if (lp->ll_attr == nsd)
|
||||
lp->ll_attr = LL_SORTATTR;
|
||||
else if (lp->ll_attr == nsd + 1)
|
||||
lp->ll_attr = nsd;
|
||||
for (lp = ll; lp; lp = lp->ll_next)
|
||||
if (lp->ll_attr == LL_SORTATTR)
|
||||
lp->ll_attr = nsd + 1;
|
||||
}
|
||||
}
|
||||
while( changed );
|
||||
while (changed);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2652,30 +2671,6 @@ extOutputDevices(def, transList, outFile)
|
|||
if (!strcmp(devptr->exts_deviceName, "Ignore"))
|
||||
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 */
|
||||
if (devptr->exts_deviceClass != DEV_FET)
|
||||
fprintf(outFile, "device ");
|
||||
|
|
@ -5135,7 +5130,7 @@ extFindNodes(def, clipArea, subonly)
|
|||
|
||||
pNum = ExtCurStyle->exts_globSubstratePlane;
|
||||
/* Does the type set of this plane intersect the substrate types? */
|
||||
if (TTMaskIntersect(&DBPlaneTypes[pNum], &subsTypesNonSpace))
|
||||
if ((pNum != -1) && TTMaskIntersect(&DBPlaneTypes[pNum], &subsTypesNonSpace))
|
||||
{
|
||||
arg.fra_pNum = pNum;
|
||||
DBSrPaintClient((Tile *) NULL, def->cd_planes[pNum],
|
||||
|
|
|
|||
|
|
@ -76,18 +76,19 @@ void extHeader();
|
|||
*/
|
||||
|
||||
Plane *
|
||||
ExtCell(def, outName, doLength)
|
||||
ExtCell(def, outName, isTop)
|
||||
CellDef *def; /* Cell being extracted */
|
||||
char *outName; /* Name of output file; if NULL, derive from def name */
|
||||
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.
|
||||
*/
|
||||
bool isTop; /* If TRUE, cell is the top level cell */
|
||||
{
|
||||
char *filename;
|
||||
FILE *f = NULL;
|
||||
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,
|
||||
* then just prepare the substrate plane and return it to the caller.
|
||||
|
|
@ -111,7 +112,7 @@ ExtCell(def, outName, doLength)
|
|||
}
|
||||
|
||||
extNumErrors = extNumWarnings = 0;
|
||||
savePlane = extCellFile(def, f, doLength);
|
||||
savePlane = extCellFile(def, f, isTop);
|
||||
if (f != NULL) fclose(f);
|
||||
|
||||
if (extNumErrors > 0 || extNumWarnings > 0)
|
||||
|
|
@ -471,13 +472,10 @@ ExtRevertSubstrate(def, savePlane)
|
|||
*/
|
||||
|
||||
Plane *
|
||||
extCellFile(def, f, doLength)
|
||||
extCellFile(def, f, isTop)
|
||||
CellDef *def; /* Def to be extracted */
|
||||
FILE *f; /* Output to this file */
|
||||
bool doLength; /* TRUE if we should extract driver-receiver path
|
||||
* length information for this cell (see ExtCell
|
||||
* for more details).
|
||||
*/
|
||||
bool isTop; /* TRUE if the cell is the top level cell */
|
||||
{
|
||||
NodeRegion *reg;
|
||||
Plane *saveSub;
|
||||
|
|
@ -488,8 +486,19 @@ extCellFile(def, f, doLength)
|
|||
/* If "extract do unique" was specified, then make labels in the
|
||||
* cell unique.
|
||||
*/
|
||||
|
||||
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 */
|
||||
if (ExtOptions & EXT_DOEXTRESIST)
|
||||
|
|
@ -519,7 +528,7 @@ extCellFile(def, f, doLength)
|
|||
ExtResetTiles(def, CLIENTDEFAULT);
|
||||
|
||||
/* Final pass: extract length information if desired */
|
||||
if (!SigInterruptPending && doLength && (ExtOptions & EXT_DOLENGTH))
|
||||
if (!SigInterruptPending && isTop && (ExtOptions & EXT_DOLENGTH))
|
||||
extLength(extParentUse, f);
|
||||
|
||||
UndoEnable();
|
||||
|
|
|
|||
|
|
@ -281,6 +281,13 @@ extHierSubstrate(ha, use, x, y)
|
|||
nn->nn_next = node2->node_names->nn_next;
|
||||
node2->node_names->nn_next = node1->node_names;
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
|
@ -294,6 +301,13 @@ extHierSubstrate(ha, use, x, y)
|
|||
nn->nn_next = node1->node_names;
|
||||
node1->node_names = node2->node_names;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -499,6 +513,13 @@ extHierConnectFunc1(oneTile, dinfo, ha)
|
|||
nn->nn_next = node2->node_names->nn_next;
|
||||
node2->node_names->nn_next = node1->node_names;
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
|
@ -514,6 +535,13 @@ extHierConnectFunc1(oneTile, dinfo, ha)
|
|||
nn->nn_next = node1->node_names;
|
||||
node1->node_names = node2->node_names;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -523,6 +551,48 @@ 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 --
|
||||
*
|
||||
* Called once for each tile 'cum' in extHierCumFlat->et_use->cu_def
|
||||
|
|
@ -538,6 +608,8 @@ extHierConnectFunc1(oneTile, dinfo, ha)
|
|||
* if the types of ha->hierOneTile and 'cum' connect.
|
||||
* Otherwise, if the tiles actually overlap (as opposed
|
||||
* to merely abut), mark it with feedback as an error.
|
||||
*
|
||||
*------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
|
|
@ -608,6 +680,53 @@ extHierConnectFunc2(cum, dinfo, ha)
|
|||
|
||||
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)
|
||||
{
|
||||
/*
|
||||
|
|
@ -622,6 +741,12 @@ extHierConnectFunc2(cum, dinfo, ha)
|
|||
node2->node_names->nn_next = node1->node_names;
|
||||
node2->node_len += node1->node_len;
|
||||
freeMagic((char *) node1);
|
||||
|
||||
if (ExtOptions & EXT_DOEXTRESIST)
|
||||
{
|
||||
newport->r_next = node2->node_ports;
|
||||
node2->node_ports = newport;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -637,6 +762,12 @@ extHierConnectFunc2(cum, dinfo, ha)
|
|||
node1->node_names = node2->node_names;
|
||||
node1->node_len += node2->node_len;
|
||||
freeMagic((char *) node2);
|
||||
|
||||
if (ExtOptions & EXT_DOEXTRESIST)
|
||||
{
|
||||
newport->r_next = node1->node_ports;
|
||||
node1->node_ports = newport;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -733,6 +864,13 @@ extHierConnectFunc3(cum, dinfo, ha)
|
|||
nn->nn_next = node2->node_names->nn_next;
|
||||
node2->node_names->nn_next = node1->node_names;
|
||||
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);
|
||||
}
|
||||
else
|
||||
|
|
@ -748,6 +886,13 @@ extHierConnectFunc3(cum, dinfo, ha)
|
|||
nn->nn_next = node1->node_names;
|
||||
node1->node_names = node2->node_names;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -916,6 +1061,7 @@ extOutputConns(table, outf)
|
|||
NodeName *nfirst;
|
||||
HashSearch hs;
|
||||
HashEntry *he;
|
||||
ExtConnList *nport, *npnext;
|
||||
|
||||
HashStartSearch(&hs);
|
||||
while ((he = HashNext(table, &hs)))
|
||||
|
|
@ -948,7 +1094,6 @@ extOutputConns(table, outf)
|
|||
node->node_pa[n].pa_area,
|
||||
node->node_pa[n].pa_perim);
|
||||
fprintf(outf, "\n");
|
||||
|
||||
nn->nn_node = (Node *) NULL; /* Processed */
|
||||
|
||||
/* Subsequent merges */
|
||||
|
|
@ -960,6 +1105,23 @@ extOutputConns(table, outf)
|
|||
}
|
||||
}
|
||||
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 *) nfirst);
|
||||
|
|
@ -1005,6 +1167,7 @@ extHierNewNode(he)
|
|||
node->node_names = nn;
|
||||
node->node_cap = (CapValue) 0;
|
||||
node->node_len = 1;
|
||||
node->node_ports = (ExtConnList *)NULL;
|
||||
for (n = 0; n < nclasses; n++)
|
||||
node->node_pa[n].pa_perim = node->node_pa[n].pa_area = 0;
|
||||
HashSetValue(he, (char *) nn);
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ extInterSubtreeElement(use, trans, x, y, r)
|
|||
int
|
||||
extInterSubtreeTile(tile, dinfo, cxp)
|
||||
Tile *tile;
|
||||
TileType dinfo;
|
||||
TileType dinfo; /* (unused) */
|
||||
TreeContext *cxp;
|
||||
{
|
||||
SearchContext newscx;
|
||||
|
|
@ -343,8 +343,9 @@ extInterOverlapSubtree(scx)
|
|||
*/
|
||||
|
||||
int
|
||||
extInterOverlapTile(tile, cxp)
|
||||
extInterOverlapTile(tile, dinfo, cxp)
|
||||
Tile *tile;
|
||||
TileType dinfo; /* (unused) */
|
||||
TreeContext *cxp;
|
||||
{
|
||||
SearchContext *scx = cxp->tc_scx;
|
||||
|
|
|
|||
|
|
@ -473,6 +473,19 @@ ExtUnique(rootUse, option)
|
|||
/* Fix up bounding boxes if they've changed */
|
||||
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 */
|
||||
(void) DBCellSrDefs(0, extDefInitFunc, (ClientData) 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -54,12 +54,14 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
/* C99 compat */
|
||||
#include "drc/drc.h"
|
||||
|
||||
#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.
|
||||
* 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
|
||||
int ExtInterBloat = 10;
|
||||
#endif /* exactinteractions */
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
* label. This way, the unique label form can be used by the
|
||||
* extraction code but labels (and port indexes) can be reverted
|
||||
* 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:
|
||||
* Returns the number of warnings generated.
|
||||
|
|
@ -226,8 +228,9 @@ extMakeUnique(def, ll, lreg, lregList, labelHash, option)
|
|||
text = ll->ll_label->lab_text;
|
||||
if (option == EXT_UNIQ_ALL || option == EXT_UNIQ_TEMP)
|
||||
goto makeUnique;
|
||||
else if ((option == EXT_UNIQ_NOPORTS || option == EXT_UNIQ_NOTOPPORTS)
|
||||
&& !(ll->ll_label->lab_flags & PORT_DIR_MASK))
|
||||
else if ((option == EXT_UNIQ_NOPORTS || option == EXT_UNIQ_NOTOPPORTS ||
|
||||
option == EXT_UNIQ_TEMP_NOPORTS) &&
|
||||
!(ll->ll_label->lab_flags & PORT_DIR_MASK))
|
||||
goto makeUnique;
|
||||
|
||||
cpend = strchr(text, '\0');
|
||||
|
|
@ -326,7 +329,8 @@ makeUnique:
|
|||
saveLab = *lab;
|
||||
|
||||
/* Flag this label as having been modified */
|
||||
if (option == EXT_UNIQ_TEMP) flags |= LABEL_UNIQUE;
|
||||
if ((option == EXT_UNIQ_TEMP) || (option == EXT_UNIQ_TEMP_NOPORTS))
|
||||
flags |= LABEL_UNIQUE;
|
||||
|
||||
DBRemoveLabel(def, lab);
|
||||
DBPutFontLabel(def, &saveLab.lab_rect,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ extern const char * const extDevTable[];
|
|||
#define EXT_DOLABELCHECK 0x040 /* Check for connections by label */
|
||||
#define EXT_DOALIASES 0x080 /* Output all node aliases */
|
||||
#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 char *ExtLocalPath; /* If non-NULL, location to write .ext files */
|
||||
|
|
@ -86,6 +87,7 @@ extern char *ExtLocalPath; /* If non-NULL, location to write .ext files */
|
|||
#define EXT_UNIQ_NOPORTS 2
|
||||
#define EXT_UNIQ_NOTOPPORTS 3
|
||||
#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 void ExtTechInit();
|
||||
|
|
|
|||
|
|
@ -981,6 +981,7 @@ typedef struct node
|
|||
* in the list is the "official" node name.
|
||||
*/
|
||||
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 */
|
||||
PerimArea node_pa[1]; /* Dummy; each node actually has
|
||||
* ExtCurStyle->exts_numResistClasses
|
||||
|
|
|
|||
|
|
@ -42,12 +42,29 @@ LIBS += ${GR_LIBS} ${READLINE_LIBS} -lm ${LD_EXTRA_LIBS} \
|
|||
CLEANS += tclmagic${SHDLIB_EXT} libtclmagic${SHDLIB_EXT}.a proto.magicrc
|
||||
|
||||
ifeq (${MAKE_WASM},1)
|
||||
magic: magic.js
|
||||
# magicWasm.c bootstraps the embedded Tcl interp by calling Tcl_CreateInterp /
|
||||
# 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}
|
||||
@echo --- building main 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}
|
||||
endif
|
||||
endif
|
||||
|
||||
main: magic proto.magicrc
|
||||
ifeq (${MAKE_WASM},1)
|
||||
|
|
@ -93,3 +110,14 @@ $(DESTDIR)${INSTALL_SYSDIR}/magicps.pro: magicps.pro
|
|||
${CP} magicps.pro $(DESTDIR)${INSTALL_SYSDIR}/magicps.pro
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -59,8 +59,14 @@ main(int argc, char *argv[])
|
|||
* here, nor its format. It is updated by the Makefile in this directory.
|
||||
*
|
||||
* 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 *MagicRevision = MAGIC_REVISION;
|
||||
char *MagicCompileTime = MAGIC_BUILDDATE;
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef MAGIC_WRAPPER
|
||||
#include "tcltk/tclmagic.h"
|
||||
#endif
|
||||
|
||||
#include "utils/main.h"
|
||||
#include "utils/magic.h"
|
||||
#include "utils/paths.h"
|
||||
|
|
@ -38,6 +42,16 @@ magicWasmEnsureCadRoot(void)
|
|||
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
|
||||
magic_wasm_init(void)
|
||||
{
|
||||
|
|
@ -53,7 +67,52 @@ magic_wasm_init(void)
|
|||
if (magicWasmEnsureCadRoot() != 0)
|
||||
return -1;
|
||||
|
||||
return magicMainInit(5, argv);
|
||||
#ifdef MAGIC_WRAPPER
|
||||
/* 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
|
||||
|
|
@ -75,13 +134,20 @@ magic_wasm_run_command(const char *command)
|
|||
TxSetPoint(GrScreenRect.r_xtop / 2, GrScreenRect.r_ytop / 2,
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE int
|
||||
magic_wasm_source_file(const char *path)
|
||||
{
|
||||
FILE *f;
|
||||
int status;
|
||||
|
||||
status = magic_wasm_init();
|
||||
|
|
@ -91,27 +157,33 @@ magic_wasm_source_file(const char *path)
|
|||
if ((path == NULL) || (*path == '\0'))
|
||||
return -1;
|
||||
|
||||
f = PaOpen((char *)path, "r", (char *)NULL, ".", (char *)NULL,
|
||||
(char **)NULL);
|
||||
if (f == NULL)
|
||||
{
|
||||
TxError("Unable to open command file \"%s\".\n", path);
|
||||
#ifdef MAGIC_WRAPPER
|
||||
/* In wrapper mode the file contains Tcl; evaluate it through the
|
||||
* Tcl interpreter so that magic:: commands are dispatched via
|
||||
* _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,
|
||||
(char **)NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
#endif
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE void
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
# 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.wasm
|
||||
|
||||
*.tgz
|
||||
node_modules/
|
||||
package-lock.json
|
||||
|
|
|
|||
|
|
@ -7,16 +7,43 @@ 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
|
||||
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
|
||||
|
||||
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
|
||||
npm install magic-vlsi-wasm
|
||||
npm install @rtimothyedwards/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.
|
||||
|
||||
## Quick start
|
||||
|
||||
### Default variant (no Tcl)
|
||||
|
||||
```js
|
||||
import createMagic from 'magic-vlsi-wasm';
|
||||
|
||||
|
|
@ -35,6 +62,23 @@ runCommand('gds write /work/inv');
|
|||
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 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
|
||||
|
|
@ -122,21 +166,59 @@ If you want to rebuild the WASM module yourself, see
|
|||
The short version:
|
||||
|
||||
```bash
|
||||
bash npm/build.sh # debug build, copies magic.js + magic.wasm into npm/
|
||||
bash npm/build.sh --release # optimized
|
||||
bash npm/build.sh --test # build + run tests
|
||||
bash npm/build.sh --pack # build + produce magic-vlsi-wasm-<version>.tgz
|
||||
bash npm/build.sh # both variants, debug build
|
||||
bash npm/build.sh --variant=notcl # default variant only (faster)
|
||||
bash npm/build.sh --variant=tcl # TCL variant only
|
||||
bash npm/build.sh --release # optimized (-O2, no debug symbols)
|
||||
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)
|
||||
checkout (Magic pins emsdk `3.1.56` — see the comment in `npm/build.sh`).
|
||||
on your PATH. If you pass `EMSDK_DIR=/path/to/emsdk`, `build.sh` sources
|
||||
`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
|
||||
|
||||
- Headless only. There is no display driver, so commands that draw to a
|
||||
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
|
||||
per worker.
|
||||
|
||||
|
|
|
|||
173
npm/build.sh
173
npm/build.sh
|
|
@ -2,9 +2,13 @@
|
|||
# Build Magic WASM and copy artifacts into this npm/ directory.
|
||||
#
|
||||
# Usage:
|
||||
# npm/build.sh [--release] [--test] [--pack]
|
||||
# npm/build.sh [--variant=<tcl|notcl|both>] [--release] [--test] [--pack]
|
||||
#
|
||||
# --release Omit debug symbols (-g).
|
||||
# --variant=tcl Build only the TCL-embedded variant → npm/tcl/
|
||||
# --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.
|
||||
# --pack Run `npm pack` after copying artifacts (and tests, if given).
|
||||
#
|
||||
|
|
@ -14,23 +18,36 @@
|
|||
# node, npm — only required for --test / --pack
|
||||
#
|
||||
# Environment:
|
||||
# EMSDK_DIR Path to an activated emsdk checkout.
|
||||
# If set, emsdk_env.sh is sourced from there.
|
||||
# If unset, emcc must already be on PATH (e.g. sourced externally).
|
||||
# EMSDK_DIR Path to an activated emsdk checkout.
|
||||
# If set, emsdk_env.sh is sourced from there.
|
||||
# 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
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
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_TEST=0
|
||||
OPT_PACK=0
|
||||
OPT_VARIANT=both
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--release) OPT_RELEASE=1 ;;
|
||||
--test) OPT_TEST=1 ;;
|
||||
--pack) OPT_PACK=1 ;;
|
||||
--release) OPT_RELEASE=1 ;;
|
||||
--test) OPT_TEST=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 ;;
|
||||
esac
|
||||
done
|
||||
|
|
@ -76,53 +93,119 @@ sed_strip_cr() {
|
|||
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
|
||||
EXTRA_CFLAGS=" -O2"
|
||||
EXTRA_CFLAGS="-O2"
|
||||
else
|
||||
EXTRA_CFLAGS=" -g"
|
||||
EXTRA_CFLAGS="-g"
|
||||
fi
|
||||
|
||||
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
|
||||
# --- TCL fork: clone and prebuild (TCL variant only) ------------------------
|
||||
# Uses TCL_REPO_URL and TCL_REF from the environment (both have defaults).
|
||||
# The source is cloned into build-tcl-wasm/tcl on the first run and checked
|
||||
# out at the requested ref. Because this clone is private to the build dir,
|
||||
# we manage its HEAD freely — no user-supplied tree is ever mutated.
|
||||
#
|
||||
# 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}"
|
||||
|
||||
cat toolchains/emscripten/defs.mak >> defs.mak
|
||||
if [ ! -d "$TCL_SRC_DIR/.git" ]; then
|
||||
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
|
||||
|
||||
# --- build -------------------------------------------------------------------
|
||||
emmake make depend
|
||||
emmake make -j"$(ncpu)" modules libs
|
||||
emmake make techs
|
||||
emmake make mains
|
||||
( cd "$TCL_SRC_DIR"
|
||||
current_sha=$(git rev-parse HEAD 2>/dev/null || echo "")
|
||||
if [ "$current_sha" != "$TCL_REF" ]; then
|
||||
git fetch --quiet origin
|
||||
git checkout --quiet --detach "$TCL_REF"
|
||||
fi
|
||||
echo "Using TCL at $(git rev-parse HEAD) ($TCL_REPO_URL)"
|
||||
)
|
||||
|
||||
# --- copy artifacts ----------------------------------------------------------
|
||||
cp magic/magic.js "$SCRIPT_DIR/"
|
||||
cp magic/magic.wasm "$SCRIPT_DIR/"
|
||||
echo "Copied magic.js and magic.wasm to npm/"
|
||||
# Build TCL for WASM if it hasn't been built yet. The presence of
|
||||
# tclConfig.sh in the install prefix is the canonical "TCL is built" marker.
|
||||
if [ ! -f "$TCL_WASM_PREFIX/lib/tclConfig.sh" ]; then
|
||||
echo "=== building TCL for WASM into $TCL_BUILD_DIR (one-time) ==="
|
||||
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 -----------------------------------------------------------
|
||||
# Runs the same smoke test that CI runs (see .github/workflows/main.yml).
|
||||
# Runs the same smoke test that CI runs (see .github/workflows/main-wasm.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
|
||||
cd "$SCRIPT_DIR"
|
||||
npm run test
|
||||
( cd "$SCRIPT_DIR" && npm run test && npm run test:tcl )
|
||||
fi
|
||||
|
||||
# --- optional pack -----------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
// 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);
|
||||
|
|
@ -5,6 +5,7 @@ import { run as runExtract } from './extract.js';
|
|||
import { run as runGds } from './gds.js';
|
||||
import { run as runDrc } from './drc.js';
|
||||
import { run as runCif } from './cif.js';
|
||||
import { reportError } from './helpers.js';
|
||||
|
||||
const PAD = 9;
|
||||
|
||||
|
|
@ -16,6 +17,9 @@ async function test(name, fn) {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.');
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
magic::tech load __TECH__
|
||||
magic::load /work/__CELL__
|
||||
magic::cif write /work/__CELL__
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// Usage: node examples/cif.js [magFile [tech [outputDir]]]
|
||||
import { createMagic, vfsRead, loadCell, loadScript,
|
||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers.js';
|
||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
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)) {
|
||||
const { outPath, bytes } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
|
||||
const { outPath, bytes } = await run().catch(e => { reportError(e); process.exit(1); });
|
||||
console.log(`\ncif: ${outPath} (${bytes} bytes)`);
|
||||
console.log('Done.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.');
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
magic::tech load __TECH__
|
||||
magic::load /work/__CELL__
|
||||
magic::drc catchup
|
||||
magic::drc count total
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// Usage: node examples/drc.js [magFile [tech]]
|
||||
import { createMagic, loadCell, loadScript,
|
||||
DEFAULT_TECH, DEFAULT_MAG } from './helpers.js';
|
||||
DEFAULT_TECH, DEFAULT_MAG, reportError } from './helpers.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
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)) {
|
||||
const { violations } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
|
||||
const { violations } = await run().catch(e => { reportError(e); process.exit(1); });
|
||||
console.log(`\nDRC violations: ${violations ?? '(count not found in output)'}`);
|
||||
console.log('Done.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.');
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
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__
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// Usage: node examples/extract.js [magFile [tech [outputDir]]]
|
||||
import { createMagic, vfsWrite, vfsRead, loadCell, loadScript,
|
||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers.js';
|
||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
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)) {
|
||||
const { ext, spice } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
|
||||
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.');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
// 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.');
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
magic::tech load __TECH__
|
||||
magic::load /work/__CELL__
|
||||
magic::gds write /work/__CELL__
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
//
|
||||
// Usage: node examples/gds.js [magFile [tech [outputDir]]]
|
||||
import { createMagic, vfsRead, loadCell, loadScript,
|
||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.js';
|
||||
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT, reportError } from './helpers.js';
|
||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
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)) {
|
||||
const { outPath, bytes } = await run().catch(e => { console.error(e.message ?? e); process.exit(1); });
|
||||
const { outPath, bytes } = await run().catch(e => { reportError(e); process.exit(1); });
|
||||
console.log(`\ngds: ${outPath} (${bytes} bytes)`);
|
||||
console.log('Done.');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,91 @@
|
|||
// 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));
|
||||
}
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
// 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 { 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, '../magic.wasm'));
|
||||
export const wasmBinary = readFileSync(resolve(EXAMPLES_DIR, '../notcl/magic.wasm'));
|
||||
export const DEFAULT_TECH = 'scmos';
|
||||
export const DEFAULT_MAG = resolve(EXAMPLES_DIR, 'min.mag');
|
||||
export const DEFAULT_OUT = resolve(EXAMPLES_DIR, 'output');
|
||||
|
|
@ -35,11 +38,28 @@ export class MagicWasm {
|
|||
runScript(text) {
|
||||
for (const line of text.split('\n')) {
|
||||
const l = line.trim();
|
||||
if (l && !l.startsWith('#')) this._run(l);
|
||||
if (l && !l.startsWith('#')) {
|
||||
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().
|
||||
// onPrint / onPrintErr default to console.log/error with a [magic] prefix.
|
||||
// All output lines are also collected into the returned `lines` array.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.');
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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');
|
||||
20
npm/index.js
20
npm/index.js
|
|
@ -1,14 +1,24 @@
|
|||
import MagicModuleFactory from './magic.js';
|
||||
// Default entry point: the non-TCL build.
|
||||
//
|
||||
// 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 = {}) {
|
||||
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, []);
|
||||
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 };
|
||||
return { init, runCommand, sourceFile, update, FS: module.FS, variant: 'notcl' };
|
||||
}
|
||||
|
||||
export { createMagic };
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
// 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';
|
||||
|
|
@ -1,34 +1,38 @@
|
|||
{
|
||||
"name": "magic-vlsi-wasm",
|
||||
"version": "0.0.0-dev",
|
||||
"description": "Magic VLSI Layout Tool — headless WebAssembly build",
|
||||
"description": "Magic VLSI Layout Tool — headless WebAssembly build (TCL + non-TCL variants)",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.js",
|
||||
"types": "./index.d.ts"
|
||||
}
|
||||
".": { "import": "./index.js", "types": "./index.d.ts" },
|
||||
"./tcl": { "import": "./tcl.js", "types": "./index.d.ts" },
|
||||
"./notcl": { "import": "./notcl.js", "types": "./index.d.ts" }
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts",
|
||||
"magic.js",
|
||||
"magic.wasm",
|
||||
"tcl.js",
|
||||
"notcl.js",
|
||||
"tcl/magic.js",
|
||||
"tcl/magic.wasm",
|
||||
"notcl/magic.js",
|
||||
"notcl/magic.wasm",
|
||||
"examples/",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"example": "node examples/extract.js",
|
||||
"test": "node examples/all.js",
|
||||
"test:gds": "node examples/gds.js",
|
||||
"test:drc": "node examples/drc.js",
|
||||
"test:cif": "node examples/cif.js"
|
||||
"example": "node examples/extract.js",
|
||||
"test": "node examples/all.js",
|
||||
"test:tcl": "node examples/all-tcl.js",
|
||||
"test:gds": "node examples/gds.js",
|
||||
"test:drc": "node examples/drc.js",
|
||||
"test:cif": "node examples/cif.js"
|
||||
},
|
||||
|
||||
"keywords": ["magic", "vlsi", "eda", "wasm", "webassembly", "layout"],
|
||||
"keywords": ["magic", "vlsi", "eda", "wasm", "webassembly", "layout", "tcl"],
|
||||
"license": "HPND",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
// 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;
|
||||
300
resis/ResBasic.c
300
resis/ResBasic.c
|
|
@ -27,64 +27,24 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
/*
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* resNodeIsPort --
|
||||
*
|
||||
* If the given position is inside any port declared on the tile,
|
||||
* change the node name to the port name. Remove the port
|
||||
* declaration if it was used.
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
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 --
|
||||
* resMakePortBreakpoints --
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* Adds breakpoints where ports (drivers, sinks, or labels) have been
|
||||
* defined as connected to the tile.
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
resAllPortNodes(tile, list)
|
||||
resMakePortBreakpoints(tile, list)
|
||||
Tile *tile;
|
||||
resNode **list;
|
||||
{
|
||||
|
|
@ -92,6 +52,7 @@ resAllPortNodes(tile, list)
|
|||
resNode *resptr;
|
||||
resPort *pl;
|
||||
resInfo *info = (resInfo *)TiGetClientPTR(tile);
|
||||
ResConnect *connect;
|
||||
|
||||
free_magic1_t mm1 = freeMagic1_init();
|
||||
for (pl = info->portList; pl; pl = pl->rp_nextPort)
|
||||
|
|
@ -103,8 +64,13 @@ resAllPortNodes(tile, list)
|
|||
resptr->rn_status = TRUE;
|
||||
resptr->rn_noderes = 0;
|
||||
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);
|
||||
NEWBREAK(resptr, tile, x, y, NULL);
|
||||
ResNewBreak(resptr, tile, x, y, NULL);
|
||||
freeMagic1(&mm1, pl);
|
||||
}
|
||||
freeMagic1_end(&mm1);
|
||||
|
|
@ -113,89 +79,44 @@ resAllPortNodes(tile, list)
|
|||
/*
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* ResMultiPlaneFunc---
|
||||
* ResStartTile --
|
||||
*
|
||||
* If device is found overlapping one of its source/drain types, then
|
||||
* generate a new device at the center of the tile and add to ResNodeQueue.
|
||||
* For the tile at the starting point of the net, create an initial
|
||||
* resNode entry.
|
||||
*
|
||||
* Results:
|
||||
* Always 0 to keep the search going.
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side Effects:
|
||||
* creates a node.
|
||||
*
|
||||
* Side effects:
|
||||
* Adds to ResNodeQueue
|
||||
*
|
||||
*--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResMultiPlaneFunc(tile, dinfo, tpptr)
|
||||
Tile *tile;
|
||||
TileType dinfo; /* Not used, but needs to be handled */
|
||||
Tile **tpptr;
|
||||
void
|
||||
ResStartTile(tile, x, y)
|
||||
Tile *tile;
|
||||
int x, y;
|
||||
|
||||
{
|
||||
Tile *tp = *tpptr;
|
||||
int xj, yj;
|
||||
resNode *resptr;
|
||||
|
||||
/* 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;
|
||||
ResNewSDDevice(tp, tile, xj, yj, OTHERPLANE, &ResNodeQueue);
|
||||
|
||||
return 0;
|
||||
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
|
||||
InitializeResNode(resptr, x, y, RES_NODE_ORIGIN);
|
||||
resptr->rn_status = TRUE;
|
||||
resptr->rn_noderes = 0;
|
||||
ResAddToQueue(resptr, &ResNodeQueue);
|
||||
ResNewBreak(resptr, tile, x, y, NULL);
|
||||
if (resCurrentNode == NULL) resCurrentNode = resptr;
|
||||
}
|
||||
|
||||
/*
|
||||
*--------------------------------------------------------------------------
|
||||
*
|
||||
* ResSubstrateFunc---
|
||||
* ResEachTile --
|
||||
*
|
||||
* 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/
|
||||
* For each tile, make a list of all possible current sources/
|
||||
* sinks including contacts, devices, and junctions. Once this
|
||||
* list is made, calculate the resistor network for the tile.
|
||||
*
|
||||
|
|
@ -214,10 +135,9 @@ ResSubstrateFunc(tile, dinfo, tpptr)
|
|||
#define IGNORE_BOTTOM 8
|
||||
|
||||
bool
|
||||
ResEachTile(tile, startpoint)
|
||||
Tile *tile;
|
||||
Point *startpoint;
|
||||
|
||||
ResEachTile(tile, devNodeTable)
|
||||
Tile *tile; /* Tile being processed */
|
||||
HashTable *devNodeTable; /* Table of tiles connected to devices */
|
||||
{
|
||||
Tile *tp;
|
||||
resNode *resptr;
|
||||
|
|
@ -226,14 +146,13 @@ ResEachTile(tile, startpoint)
|
|||
int xj, yj, i;
|
||||
bool merged;
|
||||
tElement *tcell;
|
||||
resInfo *tstructs= (resInfo *)TiGetClientPTR(tile);
|
||||
resInfo *tstructs = (resInfo *)TiGetClientPTR(tile);
|
||||
ExtDevice *devptr;
|
||||
int sides;
|
||||
HashEntry *he;
|
||||
|
||||
ResTileCount++;
|
||||
|
||||
/* Process startpoint, if any. */
|
||||
|
||||
/* Simplification: Split tiles handle either the non-space side,
|
||||
* or if neither side is space, then handle the left side.
|
||||
*/
|
||||
|
|
@ -258,21 +177,7 @@ ResEachTile(tile, startpoint)
|
|||
t1 = TiGetTypeExact(tile);
|
||||
}
|
||||
|
||||
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)
|
||||
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t1))
|
||||
{
|
||||
/*
|
||||
* The device is put in the center of the tile. This is fine
|
||||
|
|
@ -295,9 +200,7 @@ ResEachTile(tile, startpoint)
|
|||
InitializeResNode(resptr, x, y, RES_NODE_JUNCTION);
|
||||
resptr->rn_te = tcell;
|
||||
ResAddToQueue(resptr, &ResNodeQueue);
|
||||
resNodeIsPort(resptr, x, y, tile);
|
||||
|
||||
NEWBREAK(resptr, tile, resptr->rn_loc.p_x,
|
||||
ResNewBreak(resptr, tile, resptr->rn_loc.p_x,
|
||||
resptr->rn_loc.p_y, NULL);
|
||||
}
|
||||
}
|
||||
|
|
@ -326,7 +229,7 @@ ResEachTile(tile, startpoint)
|
|||
|
||||
/* 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);
|
||||
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2))
|
||||
|
|
@ -341,14 +244,14 @@ ResEachTile(tile, startpoint)
|
|||
/* found device */
|
||||
xj = LEFT(tile);
|
||||
yj = (TOP(tp) + BOTTOM(tp)) >> 1;
|
||||
ResNewSDDevice(tile, tp, xj, yj, RIGHTEDGE, &ResNodeQueue);
|
||||
ResNewTermDevice(tile, tp, i, xj, yj, RIGHTEDGE, &ResNodeQueue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < devptr->exts_deviceSDCount) break;
|
||||
}
|
||||
}
|
||||
if TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2)
|
||||
if (TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2))
|
||||
{
|
||||
/* tile is junction */
|
||||
xj = LEFT(tile);
|
||||
|
|
@ -359,7 +262,7 @@ ResEachTile(tile, startpoint)
|
|||
|
||||
/* 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);
|
||||
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2))
|
||||
|
|
@ -374,18 +277,18 @@ ResEachTile(tile, startpoint)
|
|||
/* found device */
|
||||
xj = RIGHT(tile);
|
||||
yj = (TOP(tp) + BOTTOM(tp)) >> 1;
|
||||
ResNewSDDevice(tile, tp, xj, yj, LEFTEDGE, &ResNodeQueue);
|
||||
ResNewTermDevice(tile, tp, i, xj, yj, LEFTEDGE, &ResNodeQueue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < devptr->exts_deviceSDCount) break;
|
||||
}
|
||||
}
|
||||
if TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2)
|
||||
if (TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2))
|
||||
{
|
||||
/* tile is junction */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -407,25 +310,25 @@ ResEachTile(tile, startpoint)
|
|||
/* found device */
|
||||
yj = TOP(tile);
|
||||
xj = (LEFT(tp) + RIGHT(tp)) >> 1;
|
||||
ResNewSDDevice(tile, tp, xj, yj, BOTTOMEDGE, &ResNodeQueue);
|
||||
ResNewTermDevice(tile, tp, i, xj, yj, BOTTOMEDGE, &ResNodeQueue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < devptr->exts_deviceSDCount) break;
|
||||
}
|
||||
}
|
||||
if TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2)
|
||||
if (TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1], t2))
|
||||
{
|
||||
/* tile is junction */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2))
|
||||
|
|
@ -440,86 +343,59 @@ ResEachTile(tile, startpoint)
|
|||
/* found device */
|
||||
yj = BOTTOM(tile);
|
||||
xj = (LEFT(tp) + RIGHT(tp)) >> 1;
|
||||
ResNewSDDevice(tile, tp, xj, yj, TOPEDGE, &ResNodeQueue);
|
||||
ResNewTermDevice(tile, tp, i, xj, yj, TOPEDGE, &ResNodeQueue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < devptr->exts_deviceSDCount) break;
|
||||
}
|
||||
}
|
||||
if TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2)
|
||||
if (TTMaskHasType(&(ExtCurStyle->exts_nodeConn[t1]), t2))
|
||||
{
|
||||
/* tile is junction */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for source/drain on other planes (e.g., capacitors, bipolars, ...) */
|
||||
/* Check for terminals 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) */
|
||||
|
||||
if (TTMaskHasType(&ResSDTypesBitMask, t1))
|
||||
he = HashLookOnly(devNodeTable, (char *)tile);
|
||||
if (he != NULL)
|
||||
{
|
||||
Rect r;
|
||||
int pNum;
|
||||
TileTypeBitMask devMask;
|
||||
resDevTerm *resdevRec, *resdevList;
|
||||
Tile *devtile;
|
||||
|
||||
TiToRect(tile, &r);
|
||||
|
||||
for (pNum = 0; pNum < DBNumPlanes; pNum++)
|
||||
resdevList = (resDevTerm *)HashGetValue(he);
|
||||
while (resdevList != NULL)
|
||||
{
|
||||
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)
|
||||
for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
|
||||
if (TTMaskHasType(&devptr->exts_deviceSDTypes[i], t1))
|
||||
TTMaskSetType(&devMask, t2);
|
||||
|
||||
DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pNum],
|
||||
&r, &devMask, ResMultiPlaneFunc, (ClientData)&tile);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
resdevRec = resdevList;
|
||||
/* Set the position from the device, not the current tile */
|
||||
devtile = resdevRec->rdt_tile;
|
||||
xj = (RIGHT(devtile) + LEFT(devtile)) / 2;
|
||||
yj = (TOP(devtile) + BOTTOM(devtile)) / 2;
|
||||
if (resdevRec->rdt_term == -1) /* Substrate */
|
||||
{
|
||||
ResNewSubDevice(tile, resdevRec->rdt_tile, xj, yj,
|
||||
OTHERPLANE, &ResNodeQueue);
|
||||
}
|
||||
else /* Terminal */
|
||||
{
|
||||
ResNewTermDevice(tile, resdevRec->rdt_tile, resdevRec->rdt_term,
|
||||
xj, yj, OTHERPLANE, &ResNodeQueue);
|
||||
}
|
||||
resdevList = resdevList->rdt_next;
|
||||
freeMagic(resdevRec);
|
||||
}
|
||||
HashSetValue(he, (char *)NULL); /* Done with hash record */
|
||||
}
|
||||
|
||||
tstructs->ri_status |= RES_TILE_DONE;
|
||||
|
||||
resAllPortNodes(tile, &ResNodeQueue);
|
||||
resMakePortBreakpoints(tile, &ResNodeQueue);
|
||||
|
||||
merged = ResCalcTileResistance(tile, tstructs, &ResNodeQueue,
|
||||
&ResNodeList);
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ ResSanityChecks(nodename, resistorList, nodeList, devlist)
|
|||
{
|
||||
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;
|
||||
if (node->rn_why & RES_NODE_ORIGIN)
|
||||
|
|
@ -133,7 +133,7 @@ ResSanityChecks(nodename, resistorList, nodeList, devlist)
|
|||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -128,11 +128,11 @@ ResPrintDeviceList(fp, list)
|
|||
{
|
||||
if (list->rd_terminals[i] == NULL) continue;
|
||||
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_y);
|
||||
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_y);
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ enumerate:
|
|||
}
|
||||
else
|
||||
{
|
||||
resTopTile=BL(resTopTile);
|
||||
resTopTile = BL(resTopTile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,7 +205,7 @@ ResCheckConcavity(bot, top, tt)
|
|||
ylen = ypos - resWalkdown(bot, tt, xpos, ypos, NULL);
|
||||
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)
|
||||
Tile *tile;
|
||||
TileType tt;
|
||||
int xpos,ypos;
|
||||
int xpos, ypos;
|
||||
Tile * (*func)();
|
||||
|
||||
{
|
||||
|
|
@ -443,7 +443,7 @@ ResSplitX(tile, x)
|
|||
Tile *tp = TiSplitX(tile, x);
|
||||
Tile *tp2;
|
||||
|
||||
TiSetBody(tp,tt);
|
||||
TiSetBody(tp, tt);
|
||||
/* check to see if we can combine with the tiles above or below us */
|
||||
tp2 = RT(tile);
|
||||
if (TiGetType(tp2) == tt && LEFT(tp2) == LEFT(tile) && RIGHT(tp2) == RIGHT(tile))
|
||||
|
|
|
|||
104
resis/ResJunct.c
104
resis/ResJunct.c
|
|
@ -29,26 +29,33 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResNewSDDevice -- called when a device is reached via a piece of
|
||||
* diffusion. (Devices reached via poly, i.e.
|
||||
* gates, are handled by ResEachTile.)
|
||||
* ResNewTermDevice --
|
||||
*
|
||||
* Results:none
|
||||
* Called when a device is reached via a type in the device's
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* Results:
|
||||
* None
|
||||
*
|
||||
* 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
|
||||
ResNewSDDevice(tile, tp, xj, yj, direction, PendingList)
|
||||
ResNewTermDevice(tile, tp, n, xj, yj, direction, PendingList)
|
||||
Tile *tile, *tp;
|
||||
int xj, yj, direction;
|
||||
int n; /* Terminal index */
|
||||
int xj, yj; /* Location of connection */
|
||||
int direction; /* Direction of current */
|
||||
resNode **PendingList;
|
||||
{
|
||||
resNode *resptr;
|
||||
resNode *resptr = NULL;
|
||||
resDevice *resDev;
|
||||
tElement *tcell;
|
||||
int newnode;
|
||||
|
|
@ -64,32 +71,40 @@ ResNewSDDevice(tile, tp, xj, yj, direction, PendingList)
|
|||
|
||||
ri = (resInfo *) TiGetClientPTR(tp);
|
||||
resDev = ri->deviceList;
|
||||
if ((ri->sourceEdge & direction) != 0)
|
||||
if (resDev == NULL) return; /* Shouldn't happen? */
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if (resDev->rd_fet_source == (resNode *) NULL)
|
||||
{
|
||||
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
|
||||
newnode = TRUE;
|
||||
resDev->rd_fet_source = resptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
resptr = resDev->rd_fet_source;
|
||||
}
|
||||
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
|
||||
newnode = TRUE;
|
||||
resDev->rd_terminals[2 + n] = resptr;
|
||||
}
|
||||
else
|
||||
else if (((ri->sourceEdge & direction) != 0) || (resDev->rd_nterms < 4))
|
||||
{
|
||||
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;
|
||||
}
|
||||
resptr = resDev->rd_terminals[2 + n];
|
||||
}
|
||||
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)
|
||||
{
|
||||
tcell = (tElement *) mallocMagic((unsigned)(sizeof(tElement)));
|
||||
|
|
@ -99,7 +114,8 @@ ResNewSDDevice(tile, tp, xj, yj, direction, PendingList)
|
|||
resptr->rn_te = tcell;
|
||||
ResAddToQueue(resptr, PendingList);
|
||||
}
|
||||
NEWBREAK(resptr, tile, xj, yj, NULL);
|
||||
if (resptr != NULL)
|
||||
ResNewBreak(resptr, tile, xj, yj, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -131,20 +147,16 @@ ResNewSubDevice(tile, tp, xj, yj, direction, PendingList)
|
|||
ri = (resInfo *) TiGetClientPTR(tp);
|
||||
resDev = ri->deviceList;
|
||||
|
||||
/* 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 == NULL) return; /* Should not happen? */
|
||||
|
||||
if (resDev->rd_fet_subs == (resNode *) NULL)
|
||||
if (resDev->rd_fet_subs == (resNode *)NULL)
|
||||
{
|
||||
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
|
||||
newnode = TRUE;
|
||||
resDev->rd_fet_subs = resptr;
|
||||
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
|
||||
newnode = TRUE;
|
||||
resDev->rd_fet_subs = resptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
resptr = resDev->rd_fet_subs;
|
||||
}
|
||||
resptr = resDev->rd_fet_subs;
|
||||
|
||||
if (newnode)
|
||||
{
|
||||
|
|
@ -155,7 +167,7 @@ ResNewSubDevice(tile, tp, xj, yj, direction, PendingList)
|
|||
resptr->rn_te = tcell;
|
||||
ResAddToQueue(resptr, PendingList);
|
||||
}
|
||||
NEWBREAK(resptr, tile, xj, yj, NULL);
|
||||
ResNewBreak(resptr, tile, xj, yj, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -213,10 +225,10 @@ ResProcessJunction(tile, tp, xj, yj, NodeList)
|
|||
junction->rj_nextjunction[1] = ri2->junctionList;
|
||||
ri2->junctionList = junction;
|
||||
|
||||
NEWBREAK(junction->rj_jnode,tile, junction->rj_loc.p_x,
|
||||
ResNewBreak(junction->rj_jnode, tile, junction->rj_loc.p_x,
|
||||
junction->rj_loc.p_y, NULL);
|
||||
|
||||
NEWBREAK(junction->rj_jnode,tp, junction->rj_loc.p_x,
|
||||
ResNewBreak(junction->rj_jnode, tp, junction->rj_loc.p_x,
|
||||
junction->rj_loc.p_y, NULL);
|
||||
|
||||
}
|
||||
|
|
|
|||
452
resis/ResMain.c
452
resis/ResMain.c
|
|
@ -40,8 +40,7 @@ resNode *resCurrentNode;
|
|||
int ResTileCount = 0; /* Number of tiles rn_status */
|
||||
extern ExtRegion *ResFirst();
|
||||
extern Tile *FindStartTile();
|
||||
extern int ResEachTile();
|
||||
TileTypeBitMask ResSDTypesBitMask;
|
||||
TileTypeBitMask ResTermTypesBitMask;
|
||||
TileTypeBitMask ResSubTypesBitMask;
|
||||
|
||||
extern HashTable ResNodeTable;
|
||||
|
|
@ -100,7 +99,7 @@ ResInitializeConn()
|
|||
*
|
||||
* 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
|
||||
* named "__RESIS__".
|
||||
*
|
||||
|
|
@ -161,7 +160,7 @@ ResDissolveContacts(contacts)
|
|||
|
||||
#ifdef PARANOID
|
||||
if (conttype == TT_SPACE)
|
||||
TxError("Error in Contact Dissolving for %s \n",ResCurrentNode);
|
||||
TxError("Error in Contact Dissolving for %s \n", ResCurrentNode);
|
||||
#endif
|
||||
|
||||
/* Fill in details of the residue types for each contact type.
|
||||
|
|
@ -196,22 +195,38 @@ ResDissolveContacts(contacts)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------------
|
||||
*
|
||||
* ResMakePortBreakpoints --
|
||||
* ResMakeDriverSinkPorts --
|
||||
*
|
||||
* Search for nodes which are ports, and force them to be breakpoints
|
||||
* in the "resInfo" field of their respective tiles in ResUse. This
|
||||
* ensures that connected nodes that stretch between two ports will
|
||||
* not be assumed to be "hanging" nodes.
|
||||
* Search through the list of node drivers and sinks (connections up
|
||||
* and down in the hierarchy), and make sure this information is
|
||||
* copied to the resInfo record of the tile(s) found at the connection.
|
||||
*
|
||||
* Do the same thing for labels.
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* Adds information to the resInfo clientData of tiles in def.
|
||||
*
|
||||
*----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
ResMakePortBreakpoints(def)
|
||||
ResMakeDriverSinkPorts(def)
|
||||
CellDef *def;
|
||||
{
|
||||
Plane *plane;
|
||||
|
|
@ -220,21 +235,66 @@ ResMakePortBreakpoints(def)
|
|||
HashSearch hs;
|
||||
HashEntry *entry;
|
||||
ResExtNode *node;
|
||||
int ResAddBreakpointFunc(); /* Forward Declaration */
|
||||
ResConnect *rdriver, *rsink;
|
||||
DriverSinkData dsd;
|
||||
int ResAddPortFunc(); /* Forward Declaration */
|
||||
|
||||
HashStartSearch(&hs);
|
||||
while((entry = HashNext(&ResNodeTable, &hs)) != NULL)
|
||||
{
|
||||
node = (ResExtNode *)HashGetValue(entry);
|
||||
if (node->status & PORTNODE)
|
||||
|
||||
for (rdriver = node->drivepoints; rdriver; rdriver = rdriver->rc_next)
|
||||
{
|
||||
if (node->rs_ttype <= 0)
|
||||
if (rdriver->rc_type <= 0)
|
||||
{
|
||||
TxError("Warning: Label \"%s\" is unconnected.\n", node->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
rect = &(node->rs_bbox);
|
||||
rect = &(rdriver->rc_rect);
|
||||
|
||||
/* 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 */
|
||||
if (rect->r_xbot == rect->r_xtop)
|
||||
|
|
@ -254,11 +314,11 @@ ResMakePortBreakpoints(def)
|
|||
/* each residue plane to see which side of the contact is */
|
||||
/* the internal connection in the def. . . */
|
||||
|
||||
if (DBIsContact(node->rs_ttype))
|
||||
if (DBIsContact(rsink->rc_type))
|
||||
{
|
||||
TileType type;
|
||||
|
||||
DBFullResidueMask(node->rs_ttype, &mask);
|
||||
DBFullResidueMask(rsink->rc_type, &mask);
|
||||
for (type = DBNumUserLayers - 1; type >= TT_TECHDEPBASE; type--)
|
||||
if (TTMaskHasType(&mask, type))
|
||||
{
|
||||
|
|
@ -268,40 +328,122 @@ ResMakePortBreakpoints(def)
|
|||
}
|
||||
else
|
||||
{
|
||||
TTMaskSetOnlyType(&mask, node->rs_ttype);
|
||||
plane = def->cd_planes[DBPlane(node->rs_ttype)];
|
||||
TTMaskSetOnlyType(&mask, rsink->rc_type);
|
||||
plane = def->cd_planes[DBPlane(rsink->rc_type)];
|
||||
}
|
||||
|
||||
dsd.dsd_connect = rsink;
|
||||
dsd.dsd_node = node;
|
||||
(void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask,
|
||||
ResAddBreakpointFunc, (ClientData)node);
|
||||
ResAddPortFunc, (ClientData)&dsd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------------
|
||||
*----------------------------------------------------------------------------
|
||||
*
|
||||
* ResMakeLabelBreakpoints --
|
||||
* ResAddPortFunc --
|
||||
*
|
||||
* Search for labels that are part of a node, and force them to be
|
||||
* breakpoints in the "resInfo" field of their respective tiles in
|
||||
* ResUse. This ensures (among other things) that pins of a top level
|
||||
* cell will be retained and become the endpoint of a net.
|
||||
* 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 --
|
||||
*
|
||||
* Search for labels that are part of a node, and add them to the
|
||||
* portList linked list in the "resInfo" field of their respective tiles
|
||||
* in ResUse. This ensures (among other things) that pins of a top level
|
||||
* 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
|
||||
ResMakeLabelBreakpoints(def, resisdata)
|
||||
ResMakeLabelPorts(def, resisdata)
|
||||
CellDef *def;
|
||||
ResisData *resisdata;
|
||||
{
|
||||
Plane *plane;
|
||||
Rect *rect;
|
||||
TileTypeBitMask mask;
|
||||
HashEntry *entry;
|
||||
ResExtNode *node;
|
||||
ResConnect *rdriver, *newsink;
|
||||
Label *slab;
|
||||
int ResAddBreakpointFunc(); /* Forward Declaration */
|
||||
ResLabelData rld;
|
||||
int ResAddLabelFunc(); /* Forward Declaration */
|
||||
|
||||
for (slab = def->cd_labels; slab != NULL; slab = slab->lab_next)
|
||||
{
|
||||
|
|
@ -312,20 +454,26 @@ ResMakeLabelBreakpoints(def, resisdata)
|
|||
entry = HashFind(&ResNodeTable, slab->lab_text);
|
||||
node = ResExtInitNode(entry);
|
||||
|
||||
/* If the drivepoint position changes and the drivepoint is */
|
||||
/* in the "resisdata" record, then make sure the tile type */
|
||||
/* in "resisdata" gets changed to match. */
|
||||
/* If there is an existing drivepoint at this location, */
|
||||
/* then ignore it. */
|
||||
|
||||
if (resisdata->rg_devloc == &node->drivepoint)
|
||||
for (rdriver = node->drivepoints; rdriver; rdriver = rdriver->rc_next)
|
||||
{
|
||||
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;
|
||||
|
||||
node->drivepoint = slab->lab_rect.r_ll;
|
||||
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);
|
||||
newsink->rc_rect = slab->lab_rect;
|
||||
newsink->rc_type = slab->lab_type;
|
||||
|
||||
/* If label is on a contact, the contact has been dissolved. */
|
||||
/* Assume that the uppermost residue is the port. This may */
|
||||
|
|
@ -351,29 +499,44 @@ ResMakeLabelBreakpoints(def, resisdata)
|
|||
plane = def->cd_planes[DBPlane(slab->lab_type)];
|
||||
}
|
||||
|
||||
(void) DBSrPaintArea((Tile *) NULL, plane, rect, &mask,
|
||||
ResAddBreakpointFunc, (ClientData)node);
|
||||
|
||||
rld.rld_node = node;
|
||||
rld.rld_label = slab;
|
||||
rld.rld_connect = newsink;
|
||||
(void) DBSrPaintArea((Tile *) NULL, plane, &newsink->rc_rect, &mask,
|
||||
ResAddLabelFunc, (ClientData)&rld);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*----------------------------------------------------------------------------
|
||||
*
|
||||
* ResAddBreakpointFunc --
|
||||
* ResAddLabelFunc --
|
||||
*
|
||||
* Add a breakpoint to the "resInfo" structure of the tile
|
||||
* 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
|
||||
ResAddBreakpointFunc(tile, dinfo, node)
|
||||
Tile *tile;
|
||||
TileType dinfo; /* (unused) */
|
||||
ResExtNode *node;
|
||||
ResAddLabelFunc(tile, dinfo, rld)
|
||||
Tile *tile;
|
||||
TileType dinfo; /* (unused) */
|
||||
ResLabelData *rld; /* Label and node data */
|
||||
{
|
||||
resInfo *info;
|
||||
resPort *rp;
|
||||
resInfo *pX;
|
||||
Rect rect;
|
||||
Label *label;
|
||||
ResExtNode *node;
|
||||
ResConnect *connect;
|
||||
|
||||
if (TiGetClient(tile) == CLIENTDEFAULT)
|
||||
return 0;
|
||||
|
|
@ -386,12 +549,25 @@ ResAddBreakpointFunc(tile, dinfo, node)
|
|||
if (dinfo & TT_SIDE)
|
||||
return 0;
|
||||
|
||||
NEWPORT(node, tile);
|
||||
node = rld->rld_node;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*---------------------------------------------------------------------------
|
||||
*
|
||||
|
|
@ -521,10 +697,10 @@ ResFindNewContactTiles(contacts)
|
|||
*/
|
||||
|
||||
int
|
||||
ResProcessTiles(resisdata, origin)
|
||||
Point *origin;
|
||||
ResProcessTiles(resisdata, origin, devNodeTable)
|
||||
ResisData *resisdata;
|
||||
|
||||
Point *origin;
|
||||
HashTable *devNodeTable;
|
||||
{
|
||||
Tile *startTile;
|
||||
int tilenum, merged;
|
||||
|
|
@ -541,7 +717,8 @@ ResProcessTiles(resisdata, origin)
|
|||
if (startTile == NULL)
|
||||
return 1;
|
||||
resCurrentNode = NULL;
|
||||
(void) ResEachTile(startTile, origin);
|
||||
ResStartTile(startTile, origin->p_x, origin->p_y);
|
||||
(void) ResEachTile(startTile, devNodeTable);
|
||||
}
|
||||
#ifdef PARANOID
|
||||
else
|
||||
|
|
@ -578,7 +755,7 @@ ResProcessTiles(resisdata, origin)
|
|||
if ((ri->ri_status & RES_TILE_DONE) == 0)
|
||||
{
|
||||
resCurrentNode = resptr2;
|
||||
merged |= ResEachTile(tile, (Point *)NULL);
|
||||
merged |= ResEachTile(tile, devNodeTable);
|
||||
}
|
||||
}
|
||||
rj->rj_status = TRUE;
|
||||
|
|
@ -604,7 +781,7 @@ ResProcessTiles(resisdata, origin)
|
|||
if (cp->cp_cnode[tilenum] == resptr2)
|
||||
{
|
||||
resCurrentNode = resptr2;
|
||||
merged |= ResEachTile(tile, (Point *)NULL);
|
||||
merged |= ResEachTile(tile, devNodeTable);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -687,7 +864,7 @@ ResCalcPerimOverlap(tile, dev)
|
|||
}
|
||||
|
||||
/* 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))
|
||||
overlap += MIN(TOP(tile), TOP(tp)) - MAX(BOTTOM(tile), BOTTOM(tp));
|
||||
|
|
@ -701,7 +878,7 @@ ResCalcPerimOverlap(tile, dev)
|
|||
}
|
||||
|
||||
/* 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))
|
||||
overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp));
|
||||
|
|
@ -760,6 +937,7 @@ resMakeDevFunc(tile, dinfo, cx)
|
|||
{
|
||||
if (DBPlane(ttype) != DBPlane(thisDev->type))
|
||||
return 0; /* Completely different device? */
|
||||
|
||||
thisDev->type = ttype;
|
||||
}
|
||||
|
||||
|
|
@ -1032,6 +1210,10 @@ ResExtractNet(node, resisdata, cellname)
|
|||
int pNum;
|
||||
int resMakeDevFunc();
|
||||
int resExpandDevFunc();
|
||||
int result;
|
||||
HashTable DevNodeTable;
|
||||
HashSearch hs;
|
||||
HashEntry *he;
|
||||
|
||||
/* Make sure all global network variables are reset */
|
||||
|
||||
|
|
@ -1086,15 +1268,22 @@ ResExtractNet(node, resisdata, cellname)
|
|||
|
||||
/* Copy Paint */
|
||||
|
||||
/* If the node location is INFINITY, then use the rs_bbox */
|
||||
/* If the node location is INFINITY, then use the first drivepoint */
|
||||
|
||||
if ((node->location.p_x == INFINITY) || (node->location.p_y == INFINITY))
|
||||
{
|
||||
scx.scx_area.r_ll.p_x = node->rs_bbox.r_xbot;
|
||||
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_ur.p_y = node->rs_bbox.r_ytop;
|
||||
startpoint = node->drivepoint;
|
||||
ResConnect *rdriver = node->drivepoints;
|
||||
if (rdriver)
|
||||
{
|
||||
scx.scx_area.r_ll.p_x = rdriver->rc_rect.r_xbot - 2;
|
||||
scx.scx_area.r_ll.p_y = rdriver->rc_rect.r_ybot - 2;
|
||||
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
|
||||
{
|
||||
|
|
@ -1117,15 +1306,41 @@ ResExtractNet(node, resisdata, cellname)
|
|||
DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect,
|
||||
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]);
|
||||
|
||||
TTMaskZero(&ResSDTypesBitMask);
|
||||
DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect,
|
||||
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);
|
||||
|
||||
/* Add devices to ResUse from list in node */
|
||||
DevTiles = NULL;
|
||||
for (tptr = node->firstDev; tptr; tptr = tptr->nextDev)
|
||||
for (tptr = node->devices; tptr; tptr = tptr->nextDev)
|
||||
{
|
||||
int result;
|
||||
int i;
|
||||
ExtDevice *devptr;
|
||||
|
||||
|
|
@ -1148,6 +1363,27 @@ ResExtractNet(node, resisdata, cellname)
|
|||
freeMagic(thisDev);
|
||||
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;
|
||||
DevTiles = thisDev;
|
||||
|
||||
|
|
@ -1155,13 +1391,13 @@ ResExtractNet(node, resisdata, cellname)
|
|||
TTMaskSetOnlyType(&tMask, thisDev->type);
|
||||
DBTreeSrTiles(&scx, &tMask, 0, resExpandDevFunc, (ClientData)thisDev);
|
||||
|
||||
/* If the device has source/drain types in a different plane than */
|
||||
/* the device identifier type, then add the source/drain types to */
|
||||
/* the mask ResSDTypesBitMask. */
|
||||
/* If the device has terminal types in a different plane than */
|
||||
/* the device identifier type, then add the terminal types to */
|
||||
/* the mask ResTermTypesBitMask. */
|
||||
|
||||
devptr = tptr->thisDev->rs_devptr;
|
||||
for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
|
||||
TTMaskSetMask(&ResSDTypesBitMask, &devptr->exts_deviceSDTypes[i]);
|
||||
TTMaskSetMask(&ResTermTypesBitMask, &devptr->exts_deviceSDTypes[i]);
|
||||
|
||||
/* Add the substrate types to the mask ResSubTypesBitMask */
|
||||
TTMaskSetMask(&ResSubTypesBitMask, &devptr->exts_deviceSubstrateTypes);
|
||||
|
|
@ -1171,8 +1407,6 @@ ResExtractNet(node, resisdata, cellname)
|
|||
}
|
||||
DBReComputeBbox(ResUse->cu_def);
|
||||
|
||||
ExtResetTiles(scx.scx_use->cu_def, CLIENTDEFAULT);
|
||||
|
||||
/* To avoid issues with overlapping stacked contact types and */
|
||||
/* double-counting contacts on multiple planes, erase the top */
|
||||
/* contact layers of all contacts. ExtFindRegions() will still */
|
||||
|
|
@ -1208,35 +1442,73 @@ ResExtractNet(node, resisdata, cellname)
|
|||
|
||||
ResDissolveContacts(ResContactList);
|
||||
|
||||
/* Add "resInfo" fields to tiles */
|
||||
/* Fracture the plane to change maximum horizontal stripes to a
|
||||
* format better suited to tracking the path of current through
|
||||
* the wiring.
|
||||
*/
|
||||
|
||||
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
||||
{
|
||||
Plane *plane = ResUse->cu_def->cd_planes[pNum];
|
||||
Rect *rect = &ResUse->cu_def->cd_bbox;
|
||||
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
|
||||
* are made into the cell from ports. Otherwise, determine points
|
||||
* of entry by looking at how all parent cells connect to this
|
||||
* cell.
|
||||
*/
|
||||
|
||||
ResMakePortBreakpoints(ResUse->cu_def);
|
||||
ResMakeLabelBreakpoints(ResUse->cu_def, resisdata);
|
||||
ResMakeDriverSinkPorts(ResUse->cu_def);
|
||||
ResMakeLabelPorts(ResUse->cu_def, resisdata);
|
||||
|
||||
/* Finish preprocessing. */
|
||||
|
||||
ResFindNewContactTiles(ResContactList);
|
||||
ResPreProcessDevices(DevTiles, ResDevList, ResUse->cu_def);
|
||||
|
||||
HashInit(&DevNodeTable, HT_DEFAULTSIZE, HT_WORDKEYS);
|
||||
ResPreProcessDevices(DevTiles, ResDevList, ResUse->cu_def, &DevNodeTable);
|
||||
|
||||
/* do extraction */
|
||||
if (ResProcessTiles(resisdata, &startpoint) != 0) return TRUE;
|
||||
result = ResProcessTiles(resisdata, &startpoint, &DevNodeTable);
|
||||
|
||||
/* Free remaining table entries (if any) */
|
||||
HashStartSearch(&hs);
|
||||
while ((he = HashNext(&DevNodeTable, &hs)) != NULL)
|
||||
{
|
||||
resDevTerm *resdevList, *resdevNext;
|
||||
|
||||
resdevList = (resDevTerm *)HashGetValue(he);
|
||||
while (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);
|
||||
|
||||
resdevNext = resdevList->rdt_next;
|
||||
freeMagic((char *)resdevList);
|
||||
resdevList = resdevNext;
|
||||
}
|
||||
}
|
||||
HashKill(&DevNodeTable);
|
||||
|
||||
if (result != 0) return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
|
@ -1428,13 +1700,13 @@ FindStartTile(resisdata, SourcePoint)
|
|||
|
||||
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)
|
||||
return(tp);
|
||||
}
|
||||
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)
|
||||
return(tp);
|
||||
}
|
||||
|
|
@ -1491,7 +1763,7 @@ FindStartTile(resisdata, SourcePoint)
|
|||
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
||||
{
|
||||
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;
|
||||
return(tp);
|
||||
}
|
||||
|
|
@ -1534,7 +1806,7 @@ FindStartTile(resisdata, SourcePoint)
|
|||
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
||||
{
|
||||
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;
|
||||
return(tp);
|
||||
}
|
||||
|
|
@ -1590,7 +1862,7 @@ FindStartTile(resisdata, SourcePoint)
|
|||
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
||||
{
|
||||
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;
|
||||
while (!StackEmpty(devStack))
|
||||
{
|
||||
|
|
@ -1658,7 +1930,7 @@ FindStartTile(resisdata, SourcePoint)
|
|||
TTMaskHasType(&(devptr->exts_deviceSDTypes[i]), t2))
|
||||
{
|
||||
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;
|
||||
while (!StackEmpty(devStack))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,10 +76,9 @@ ResCalcTileResistance(tile, info, pendingList, doneList)
|
|||
if (x < MinX) MinX = x;
|
||||
if (y > MaxY) MaxY = y;
|
||||
if (y < MinY) MinY = y;
|
||||
|
||||
if (p1->br_this->rn_why == RES_NODE_DEVICE)
|
||||
{
|
||||
device = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, produce resistors for partition. Keep track of */
|
||||
|
|
@ -173,7 +172,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
|
|||
* a nested loop for each entry.
|
||||
*/
|
||||
if (count >= 16)
|
||||
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
|
||||
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_WORDKEYS);
|
||||
|
||||
/*
|
||||
* Eliminate breakpoints with the same X coordinate and merge
|
||||
|
|
@ -187,6 +186,9 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
|
|||
p2->br_this->rn_float.rn_area += height * (p2->br_loc.p_x - LEFT(tile));
|
||||
while (p2->br_next != NULL)
|
||||
{
|
||||
p1 = p2;
|
||||
p2 = p2->br_next;
|
||||
|
||||
/* Has the node been recorded as needing to be replaced? */
|
||||
if (count >= 16)
|
||||
{
|
||||
|
|
@ -197,8 +199,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
|
|||
p2->br_this = (resNode *)HashGetValue(he);
|
||||
}
|
||||
}
|
||||
p1 = p2;
|
||||
p2 = p2->br_next;
|
||||
|
||||
if (p2->br_loc.p_x == p1->br_loc.p_x)
|
||||
{
|
||||
if (p2->br_this == p1->br_this)
|
||||
|
|
@ -248,7 +249,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
|
|||
}
|
||||
else
|
||||
{
|
||||
p3 = p2->br_next;
|
||||
p3 = p2->br_next;
|
||||
while (p3 != NULL)
|
||||
{
|
||||
if (p3->br_this == currNode)
|
||||
|
|
@ -373,7 +374,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
|
|||
* a nested loop for each entry.
|
||||
*/
|
||||
if (count >= 16)
|
||||
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
|
||||
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_WORDKEYS);
|
||||
|
||||
/* Simplified split tile handling */
|
||||
if (IsSplit(tile))
|
||||
|
|
@ -397,6 +398,9 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
|
|||
p2->br_this->rn_float.rn_area += width * (p2->br_loc.p_y - BOTTOM(tile));
|
||||
while (p2->br_next != NULL)
|
||||
{
|
||||
p1 = p2;
|
||||
p2 = p2->br_next;
|
||||
|
||||
/* Has the node been recorded as needing to be replaced? */
|
||||
if (count >= 16)
|
||||
{
|
||||
|
|
@ -407,38 +411,37 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
|
|||
p2->br_this = (resNode *)HashGetValue(he);
|
||||
}
|
||||
}
|
||||
p1 = p2;
|
||||
p2 = p2->br_next;
|
||||
|
||||
if (p1->br_loc.p_y == p2->br_loc.p_y)
|
||||
{
|
||||
if (p2->br_this == p1->br_this)
|
||||
{
|
||||
currNode = NULL;
|
||||
p1->br_next = p2->br_next;
|
||||
freeMagic((char *)p2);
|
||||
p2 = p1;
|
||||
currNode = NULL;
|
||||
p1->br_next = p2->br_next;
|
||||
freeMagic((char *)p2);
|
||||
p2 = p1;
|
||||
}
|
||||
else if (p2->br_this == resCurrentNode)
|
||||
{
|
||||
currNode = p1->br_this;
|
||||
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
|
||||
freeMagic((char *)p1);
|
||||
merged = TRUE;
|
||||
currNode = p1->br_this;
|
||||
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
|
||||
freeMagic((char *)p1);
|
||||
merged = TRUE;
|
||||
}
|
||||
else if (p1->br_this == resCurrentNode)
|
||||
{
|
||||
currNode = p2->br_this;
|
||||
p1->br_next = p2->br_next;
|
||||
ResMergeNodes(p1->br_this, p2->br_this, pendingList, doneList);
|
||||
merged = TRUE;
|
||||
freeMagic((char *)p2);
|
||||
p2 = p1;
|
||||
currNode = p2->br_this;
|
||||
p1->br_next = p2->br_next;
|
||||
ResMergeNodes(p1->br_this, p2->br_this, pendingList, doneList);
|
||||
merged = TRUE;
|
||||
freeMagic((char *)p2);
|
||||
p2 = p1;
|
||||
}
|
||||
else
|
||||
{
|
||||
currNode = p1->br_this;
|
||||
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
|
||||
freeMagic((char *)p1);
|
||||
currNode = p1->br_this;
|
||||
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
|
||||
freeMagic((char *)p1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -559,7 +562,7 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
|
|||
* breakpoint, then return.
|
||||
*/
|
||||
|
||||
if (info->breakList->br_next == NULL)
|
||||
if (info->breakList->br_next == NULL)
|
||||
{
|
||||
freeMagic((char *)info->breakList);
|
||||
info->breakList = NULL;
|
||||
|
|
@ -688,7 +691,7 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
|
|||
}
|
||||
else
|
||||
{
|
||||
deltay=0;
|
||||
deltay = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -702,15 +705,15 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
|
|||
{
|
||||
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)
|
||||
{
|
||||
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
|
||||
{
|
||||
deltay=0;
|
||||
deltay = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -888,7 +891,7 @@ ResDoContacts(contact, nodes, resList)
|
|||
Tile *tile = contact->cp_tile[tilenum];
|
||||
|
||||
contact->cp_cnode[tilenum] = resptr;
|
||||
NEWBREAK(resptr, tile, contact->cp_center.p_x,
|
||||
ResNewBreak(resptr, tile, contact->cp_center.p_x,
|
||||
contact->cp_center.p_y, &contact->cp_rect);
|
||||
}
|
||||
}
|
||||
|
|
@ -937,7 +940,7 @@ ResDoContacts(contact, nodes, resList)
|
|||
ccell->ce_thisc = contact;
|
||||
|
||||
contact->cp_cnode[tilenum] = resptr;
|
||||
NEWBREAK(resptr, tile, contact->cp_center.p_x,
|
||||
ResNewBreak(resptr, tile, contact->cp_center.p_x,
|
||||
contact->cp_center.p_y, &contact->cp_rect);
|
||||
|
||||
/* Add resistors here */
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ ResFixParallel(elimResis, newResis)
|
|||
* ResSeriesCheck -- for nodes with no devices, sees if a series
|
||||
* 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.
|
||||
*
|
||||
|
|
@ -431,7 +431,7 @@ ResParallelCheck(resptr)
|
|||
HashEntry *he;
|
||||
|
||||
/* Hash the connections */
|
||||
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
|
||||
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_WORDKEYS);
|
||||
|
||||
for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl)
|
||||
{
|
||||
|
|
@ -549,7 +549,7 @@ ResTriangleCheck(resptr)
|
|||
HashSearch hs;
|
||||
|
||||
/* Hash the neighboring connections */
|
||||
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
|
||||
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_WORDKEYS);
|
||||
|
||||
for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl)
|
||||
{
|
||||
|
|
@ -839,9 +839,9 @@ ResTriangleCheck(resptr)
|
|||
*
|
||||
* 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
|
||||
* then eliminated.
|
||||
*
|
||||
|
|
@ -995,6 +995,9 @@ ResMergeNodes(node1, node2, pendingList, doneList)
|
|||
node2->rn_client = (ClientData)NULL;
|
||||
}
|
||||
|
||||
/* Don't merge away the ResNodeAtOrigin node */
|
||||
if (ResNodeAtOrigin == node2) ResNodeAtOrigin = node1;
|
||||
|
||||
node2->rn_re = (resElement *)CLIENTDEFAULT;
|
||||
node2->rn_ce = (cElement *)CLIENTDEFAULT;
|
||||
node2->rn_je = (jElement *)CLIENTDEFAULT;
|
||||
|
|
@ -1010,7 +1013,7 @@ ResMergeNodes(node1, node2, pendingList, doneList)
|
|||
* ResDeleteResPointer-- Deletes the pointer from a node to a resistor.
|
||||
* Used when a resistor is deleted.
|
||||
*
|
||||
* Results:none
|
||||
* Results: none
|
||||
*
|
||||
* Side Effects: Modifies a node's resistor list.
|
||||
*
|
||||
|
|
@ -1018,7 +1021,7 @@ ResMergeNodes(node1, node2, pendingList, doneList)
|
|||
*/
|
||||
|
||||
void
|
||||
ResDeleteResPointer(node,resistor)
|
||||
ResDeleteResPointer(node, resistor)
|
||||
resNode *node;
|
||||
resResistor *resistor;
|
||||
|
||||
|
|
@ -1059,7 +1062,7 @@ ResDeleteResPointer(node,resistor)
|
|||
*
|
||||
* ResEliminateResistor--
|
||||
*
|
||||
* Results:none
|
||||
* Results: none
|
||||
*
|
||||
* Side Effects: Deletes a resistor. Does not delete pointers from nodes to
|
||||
* resistor.
|
||||
|
|
@ -1097,8 +1100,7 @@ ResEliminateResistor(resistor, homelist)
|
|||
* they are no longer needed. If the 'info' option is used,
|
||||
* the node is eradicated.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
* Results: none.
|
||||
*
|
||||
* Side Effects: frees memory
|
||||
*
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ ResPrintExtRes(outextfile, resistors, nodename)
|
|||
char *nodename;
|
||||
|
||||
{
|
||||
int nodenum=0;
|
||||
int nodenum = 0;
|
||||
char newname[MAXNAME];
|
||||
HashEntry *entry;
|
||||
ResExtNode *node;
|
||||
|
|
@ -261,7 +261,7 @@ ResPrintExtNode(outextfile, nodelist, node)
|
|||
{
|
||||
if (snode->rn_name == NULL)
|
||||
{
|
||||
(void)sprintf(tmpname,"%s",nodename);
|
||||
(void)sprintf(tmpname, "%s", nodename);
|
||||
|
||||
cp = tmpname + strlen(tmpname) - 1;
|
||||
if (*cp == '!' || *cp == '#') *cp = '\0';
|
||||
|
|
@ -342,13 +342,13 @@ ResPrintStats(resisdata, name)
|
|||
nodes = 0;
|
||||
resistors = 0;
|
||||
totalnets++;
|
||||
for (node = ResNodeList; node != NULL; node=node->rn_more)
|
||||
for (node = ResNodeList; node != NULL; node = node->rn_more)
|
||||
|
||||
{
|
||||
nodes++;
|
||||
totalnodes++;
|
||||
}
|
||||
for (res = ResResList; res != NULL; res=res->rr_nextResistor)
|
||||
for (res = ResResList; res != NULL; res = res->rr_nextResistor)
|
||||
{
|
||||
resistors++;
|
||||
totalresistors++;
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#define DEV_PARAM_START 7
|
||||
|
||||
#define NODES_NODENAME 1
|
||||
#define NODES_NODERES 2
|
||||
#define NODES_NODEX 4
|
||||
#define NODES_NODEY 5
|
||||
#define NODES_NODETYPE 6
|
||||
|
|
@ -82,6 +83,24 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#define PORT_URY 6
|
||||
#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
|
||||
|
||||
ResExtNode *ResOriginalNodes; /*Linked List of Nodes */
|
||||
|
|
@ -91,11 +110,18 @@ ResFixPoint *ResFixList;
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResReadExt--
|
||||
* ResReadExt --
|
||||
*
|
||||
* Results: returns 0 if ext file is correct, 1 if not.
|
||||
* Read a .ext file for resistance extraction. Extresist does not use
|
||||
* 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.
|
||||
*
|
||||
* Side Effects:Reads in ExtTable and makes a hash table of nodes.
|
||||
* Results: Returns 0 if ext file is correct, 1 if not.
|
||||
*
|
||||
* Side Effects: Creates lists of nodes and devices for extresist.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
|
@ -107,8 +133,11 @@ ResReadExt(CellDef *def)
|
|||
int result, locresult;
|
||||
int argc, n, size = 0;
|
||||
FILE *fp;
|
||||
CellDef *dbdef;
|
||||
CellDef *dbdef, *parent;
|
||||
CellUse *use;
|
||||
ResExtNode *curnode;
|
||||
HashTable parentHash;
|
||||
HashEntry *he;
|
||||
|
||||
/* Search for the .ext file in the same way that efReadDef() does. */
|
||||
|
||||
|
|
@ -142,7 +171,10 @@ ResReadExt(CellDef *def)
|
|||
}
|
||||
|
||||
/* We don't care about most tokens, only DEVICE, NODE, PORT,
|
||||
* and SUBSTRATE; and MERGE is used to locate drive points.
|
||||
* and SUBSTRATE; and CONNECT is used to locate sink 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)
|
||||
{
|
||||
|
|
@ -161,17 +193,16 @@ ResReadExt(CellDef *def)
|
|||
case FET:
|
||||
locresult = ResReadFET(argc, argv);
|
||||
break;
|
||||
case MERGE:
|
||||
/* To be completed */
|
||||
/* ResReadDrivePoint(argc, argv); */
|
||||
case CONNECT:
|
||||
locresult = ResReadConnectPoint(def, argc, argv);
|
||||
break;
|
||||
case PORT:
|
||||
locresult = ResReadPort(argc, argv);
|
||||
break;
|
||||
case NODE:
|
||||
case SUBSTRATE:
|
||||
curnode = ResReadNode(argc, argv);
|
||||
break;
|
||||
case PORT:
|
||||
locresult = ResReadPort(argc, argv);
|
||||
break;
|
||||
case ATTR:
|
||||
locresult = ResReadAttribute(curnode, argc, argv);
|
||||
break;
|
||||
|
|
@ -184,9 +215,293 @@ ResReadExt(CellDef *def)
|
|||
if (locresult == 1) result = 1;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
|
|
@ -207,20 +522,18 @@ ResReadNode(int argc, char *argv[])
|
|||
{
|
||||
HashEntry *entry;
|
||||
ResExtNode *node;
|
||||
int noderesist;
|
||||
|
||||
entry = HashFind(&ResNodeTable, argv[NODES_NODENAME]);
|
||||
node = ResExtInitNode(entry);
|
||||
|
||||
node->location.p_x = atoi(argv[NODES_NODEX]);
|
||||
node->location.p_y = atoi(argv[NODES_NODEY]);
|
||||
|
||||
/* If this node was previously read as a port, then don't change the
|
||||
* node type, which is tracking the type at the drivepoint.
|
||||
*/
|
||||
if (!(node->status & PORTNODE))
|
||||
{
|
||||
node->type = DBTechNameType(argv[NODES_NODETYPE]);
|
||||
}
|
||||
node->type = DBTechNameType(argv[NODES_NODETYPE]);
|
||||
noderesist = atoi(argv[NODES_NODERES]);
|
||||
if (noderesist < 0) noderesist = INFINITY;
|
||||
/* Make sure node resistance is in units of milliohms */
|
||||
node->resistance = (float)noderesist * (float)ExtCurStyle->exts_resistScale;
|
||||
|
||||
if (node->type == -1)
|
||||
{
|
||||
|
|
@ -230,6 +543,85 @@ ResReadNode(int argc, char *argv[])
|
|||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
|
|
@ -240,6 +632,13 @@ ResReadNode(int argc, char *argv[])
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
|
|
@ -249,22 +648,33 @@ ResReadPort(int argc,
|
|||
{
|
||||
HashEntry *entry;
|
||||
ResExtNode *node;
|
||||
ResConnect *newdriver;
|
||||
|
||||
entry = HashFind(&ResNodeTable, argv[PORT_NAME]);
|
||||
node = ResExtInitNode(entry);
|
||||
|
||||
node->drivepoint.p_x = atoi(argv[PORT_LLX]);
|
||||
node->drivepoint.p_y = atoi(argv[PORT_LLY]);
|
||||
node->status |= FORCE;
|
||||
/* To do: Check for multiple ports on a net; each port needs its
|
||||
* own drivepoint.
|
||||
*/
|
||||
node->status |= DRIVELOC | PORTNODE;
|
||||
node->rs_bbox.r_ll = node->drivepoint;
|
||||
node->rs_bbox.r_ur.p_x = atoi(argv[PORT_URX]);
|
||||
node->rs_bbox.r_ur.p_y = atoi(argv[PORT_URY]);
|
||||
node->rs_ttype = DBTechNoisyNameType(argv[PORT_TYPE]);
|
||||
node->type = node->rs_ttype;
|
||||
/* Generate new drivepoint entry */
|
||||
|
||||
newdriver = (ResConnect *)mallocMagic(sizeof(ResConnect));
|
||||
|
||||
newdriver->rc_rect.r_xbot = atoi(argv[PORT_LLX]);
|
||||
newdriver->rc_rect.r_ybot = atoi(argv[PORT_LLY]);
|
||||
newdriver->rc_rect.r_xtop = atoi(argv[PORT_URX]);
|
||||
newdriver->rc_rect.r_ytop = atoi(argv[PORT_URY]);
|
||||
newdriver->rc_type = DBTechNoisyNameType(argv[PORT_TYPE]);
|
||||
newdriver->rc_node = (resNode *)NULL;
|
||||
|
||||
newdriver->rc_next = node->drivepoints;
|
||||
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)
|
||||
{
|
||||
|
|
@ -273,6 +683,7 @@ ResReadPort(int argc,
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
|
|
@ -287,7 +698,7 @@ ResReadPort(int argc,
|
|||
* None.
|
||||
*
|
||||
* Side effects:
|
||||
* Allocates memory for a devPtr, adds to the node's firstDev linked
|
||||
* Allocates memory for a devPtr, adds to the node's "devices" linked
|
||||
* list.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
|
|
@ -302,8 +713,8 @@ ResNodeAddDevice(ResExtNode *node,
|
|||
|
||||
tptr = (devPtr *)mallocMagic((unsigned)(sizeof(devPtr)));
|
||||
tptr->thisDev = device;
|
||||
tptr->nextDev = node->firstDev;
|
||||
node->firstDev = tptr;
|
||||
tptr->nextDev = node->devices;
|
||||
node->devices = tptr;
|
||||
tptr->terminal = termtype;
|
||||
}
|
||||
|
||||
|
|
@ -326,13 +737,11 @@ ResReadDevice(int argc,
|
|||
char *argv[])
|
||||
{
|
||||
RDev *device;
|
||||
int rvalue, i, j, k;
|
||||
int rvalue, i, j, k, w, l;
|
||||
ExtDevice *devptr;
|
||||
TileType ttype;
|
||||
HashEntry *entry;
|
||||
ResExtNode *node;
|
||||
ResValue rpersquare;
|
||||
float wval;
|
||||
|
||||
device = (RDev *)mallocMagic((unsigned)(sizeof(RDev)));
|
||||
|
||||
|
|
@ -361,34 +770,20 @@ ResReadDevice(int argc,
|
|||
device->drain = (ResExtNode *)NULL;
|
||||
device->subs = (ResExtNode *)NULL;
|
||||
|
||||
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;
|
||||
/* Find the end of parameter arguments */
|
||||
for (i = DEV_Y; i < argc; i++)
|
||||
{
|
||||
char *eptr;
|
||||
if ((eptr = strchr(argv[i], '=')) != NULL)
|
||||
{
|
||||
if (*argv[i] == 'w')
|
||||
sscanf(eptr + 1, "%f", &wval);
|
||||
}
|
||||
else if (!StrIsInt(argv[i]))
|
||||
break;
|
||||
if ((eptr = strchr(argv[i], '=')) == NULL)
|
||||
if (!StrIsInt(argv[i]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == argc)
|
||||
{
|
||||
TxError("Bad device %s: Too few arguments in .ext file\n",
|
||||
argv[DEV_NAME]);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
device->resistance = wval * rpersquare; /* Channel resistance */
|
||||
|
||||
/* Find and record the device terminal nodes */
|
||||
/* Note that this only records up to two terminals matching FET
|
||||
|
|
@ -406,6 +801,8 @@ ResReadDevice(int argc,
|
|||
entry = HashFind(&ResNodeTable, argv[i]);
|
||||
device->gate = (ResExtNode *)HashGetValue(entry);
|
||||
device->rs_gattr = StrDup((char **)NULL, argv[i + 2]);
|
||||
l = atoi(argv[i + 1]);
|
||||
w = 0;
|
||||
ResNodeAddDevice(device->gate, device, GATE);
|
||||
i += 3;
|
||||
|
||||
|
|
@ -414,6 +811,7 @@ ResReadDevice(int argc,
|
|||
entry = HashFind(&ResNodeTable, argv[i]);
|
||||
device->source = (ResExtNode *)HashGetValue(entry);
|
||||
device->rs_sattr = StrDup((char **)NULL, argv[i + 2]);
|
||||
w = atoi(argv[i + 1]);
|
||||
ResNodeAddDevice(device->source, device, SOURCE);
|
||||
i += 3;
|
||||
}
|
||||
|
|
@ -423,6 +821,7 @@ ResReadDevice(int argc,
|
|||
entry = HashFind(&ResNodeTable, argv[i]);
|
||||
device->drain = (ResExtNode *)HashGetValue(entry);
|
||||
device->rs_dattr = StrDup((char **)NULL, argv[i + 2]);
|
||||
w = MAX(w, atoi(argv[i + 1]));
|
||||
ResNodeAddDevice(device->drain, device, DRAIN);
|
||||
i += 3;
|
||||
}
|
||||
|
|
@ -433,6 +832,7 @@ ResReadDevice(int argc,
|
|||
}
|
||||
|
||||
device->rs_ttype = extGetDevType(devptr->exts_deviceName);
|
||||
device->rs_wl = (l == 0) ? 0.0 : (float)w / (float)l;
|
||||
|
||||
ResRDevList = device;
|
||||
device->layout = NULL;
|
||||
|
|
@ -456,13 +856,11 @@ ResReadFET(int argc,
|
|||
char *argv[])
|
||||
{
|
||||
RDev *device;
|
||||
int rvalue, i, j, k;
|
||||
int rvalue, i, j, k, w, l;
|
||||
ExtDevice *devptr;
|
||||
TileType ttype;
|
||||
HashEntry *entry;
|
||||
ResExtNode *node;
|
||||
ResValue rpersquare;
|
||||
float area, perim, wval, lval;
|
||||
|
||||
device = (RDev *)mallocMagic((unsigned)(sizeof(RDev)));
|
||||
|
||||
|
|
@ -487,20 +885,6 @@ ResReadFET(int argc,
|
|||
device->rs_dattr = RDEV_NOATTR;
|
||||
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 */
|
||||
|
||||
entry = HashFind(&ResNodeTable, argv[FET_GATE]);
|
||||
|
|
@ -522,6 +906,11 @@ ResReadFET(int argc,
|
|||
device->rs_sattr = StrDup((char **)NULL, argv[FET_SOURCE_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;
|
||||
device->layout = NULL;
|
||||
return 0;
|
||||
|
|
@ -628,10 +1017,22 @@ ResReadAttribute(ResExtNode *node,
|
|||
else if (strncmp(avalue, "res:drive", 9) == 0 &&
|
||||
(ResOptionsFlags & ResOpt_Signal))
|
||||
{
|
||||
node->drivepoint.p_x = atoi(argv[RES_EXT_ATTR_X]);
|
||||
node->drivepoint.p_y = atoi(argv[RES_EXT_ATTR_Y]);
|
||||
node->rs_ttype = DBTechNoisyNameType(argv[RES_EXT_ATTR_TYPE]);
|
||||
ResConnect *newdriver;
|
||||
|
||||
/* Generate new drivepoint entry */
|
||||
|
||||
newdriver = (ResConnect *)mallocMagic(sizeof(ResConnect));
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -668,11 +1069,11 @@ ResExtInitNode(entry)
|
|||
node->cap_couple = 0;
|
||||
node->resistance = 0;
|
||||
node->type = 0;
|
||||
node->firstDev = NULL;
|
||||
node->devices = NULL;
|
||||
node->name = entry->h_key.h_name;
|
||||
node->oldname = NULL;
|
||||
node->drivepoint.p_x = INFINITY;
|
||||
node->drivepoint.p_y = INFINITY;
|
||||
node->drivepoints = NULL;
|
||||
node->sinkpoints = NULL;
|
||||
node->location.p_x = INFINITY;
|
||||
node->location.p_y = INFINITY;
|
||||
}
|
||||
|
|
|
|||
811
resis/ResRex.c
811
resis/ResRex.c
File diff suppressed because it is too large
Load Diff
|
|
@ -178,7 +178,7 @@ ResSimplifyNet(nodelist, biglist, reslist, tolerance)
|
|||
/* other recievers at far end? If so, reschedule other node;
|
||||
* deadlock will be settled from that node.
|
||||
*/
|
||||
if ((MarkedReceivers+UnMarkedReceivers+NumberOfDrivers == 2) ||
|
||||
if ((MarkedReceivers + UnMarkedReceivers + NumberOfDrivers == 2) ||
|
||||
(UnMarkedReceivers == 0 && MarkedReceivers > 1 &&
|
||||
resistor2 == resistor1 && PendingReceivers == 0))
|
||||
{
|
||||
|
|
@ -229,7 +229,7 @@ ResSimplifyNet(nodelist, biglist, reslist, tolerance)
|
|||
* Two resistors in series? Combine them and move devices to
|
||||
* appropriate end.
|
||||
*/
|
||||
else if (numdrive+numreceive == 2 && (resistor1->rr_value < tolerance &&
|
||||
else if (numdrive + numreceive == 2 && (resistor1->rr_value < tolerance &&
|
||||
resistor2->rr_value < tolerance))
|
||||
{
|
||||
if ((resistor1->rr_status & RES_MARKED) == 0 &&
|
||||
|
|
@ -362,7 +362,7 @@ ResSimplifyNet(nodelist, biglist, reslist, tolerance)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResMoveDevices-- move devices from one node1 to node2
|
||||
* ResMoveDevices-- move devices from one node (node1) to anther (node2)
|
||||
*
|
||||
* Results: none
|
||||
*
|
||||
|
|
@ -387,11 +387,11 @@ ResMoveDevices(node1, node2)
|
|||
devptr = devptr->te_nextt;
|
||||
if (device->rd_fet_gate == node1)
|
||||
device->rd_fet_gate = node2;
|
||||
else if ((device->rd_nterms >= 4) && (device->rd_fet_subs == node1))
|
||||
else if (device->rd_fet_subs == node1)
|
||||
device->rd_fet_subs = node2;
|
||||
else if (device->rd_fet_source == node1)
|
||||
else if ((device->rd_nterms > 2) && (device->rd_fet_source == node1))
|
||||
device->rd_fet_source = node2;
|
||||
else if (device->rd_fet_drain == node1)
|
||||
else if ((device->rd_nterms > 3) && (device->rd_fet_drain == node1))
|
||||
device->rd_fet_drain = node2;
|
||||
else
|
||||
TxError("Missing Device connection in squish routines"
|
||||
|
|
@ -707,7 +707,7 @@ ResDistributeCapacitance(nodelist, totalcap)
|
|||
|
||||
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)
|
||||
TxError("Nonnull resistor area\n");
|
||||
|
||||
|
|
@ -829,11 +829,11 @@ ResCalculateTDi(node, resistor, resistorvalue)
|
|||
|
||||
ASSERT(rcd != NULL, "ResCalculateTdi");
|
||||
if (resistor == NULL)
|
||||
rcd->rc_Tdi = rcd->rc_Cdownstream*(float)resistorvalue;
|
||||
rcd->rc_Tdi = rcd->rc_Cdownstream * (float)resistorvalue;
|
||||
else
|
||||
{
|
||||
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 +
|
||||
rcd2->rc_Tdi;
|
||||
}
|
||||
|
|
@ -920,25 +920,20 @@ ResPruneTree(node, minTdi, nodelist1, nodelist2, resistorlist)
|
|||
*/
|
||||
|
||||
int
|
||||
ResDoSimplify(tolerance,resisdata)
|
||||
float tolerance;
|
||||
ResDoSimplify(resisdata)
|
||||
ResisData *resisdata;
|
||||
|
||||
{
|
||||
resNode *node, *slownode;
|
||||
float bigres = 0;
|
||||
float millitolerance;
|
||||
float bigres = 0.0;
|
||||
float totalcap;
|
||||
float rctol;
|
||||
resResistor *res;
|
||||
|
||||
rctol = resisdata->tdiTolerance;
|
||||
ResSetPathRes(resisdata);
|
||||
|
||||
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;
|
||||
|
||||
#ifdef PARANOID
|
||||
|
|
@ -952,8 +947,8 @@ ResDoSimplify(tolerance,resisdata)
|
|||
|
||||
(void) ResDistributeCapacitance(ResNodeList, resisdata->rg_nodecap);
|
||||
|
||||
if (((tolerance > bigres) || ((ResOptionsFlags & ResOpt_Simplify) == 0)) &&
|
||||
((ResOptionsFlags & ResOpt_DoLumpFile) == 0))
|
||||
if (((ResOptionsFlags & ResOpt_Simplify) == 0) &&
|
||||
((ResOptionsFlags & ResOpt_DoLumpFile) == 0))
|
||||
return 0;
|
||||
|
||||
res = ResResList;
|
||||
|
|
@ -963,16 +958,6 @@ ResDoSimplify(tolerance,resisdata)
|
|||
|
||||
res = res->rr_nextResistor;
|
||||
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)
|
||||
|
|
@ -980,7 +965,7 @@ ResDoSimplify(tolerance,resisdata)
|
|||
TxError("Error: Network simplification: Failed to to get origin node.\n");
|
||||
resisdata->rg_Tdi = 0;
|
||||
}
|
||||
else if (ResOptionsFlags & ResOpt_Tdi)
|
||||
else if (resisdata->mindelay > 0)
|
||||
{
|
||||
if ((resisdata->rg_nodecap != -1) &&
|
||||
(totalcap = ResCalculateChildCapacitance(ResNodeAtOrigin)) != -1)
|
||||
|
|
@ -988,8 +973,7 @@ ResDoSimplify(tolerance,resisdata)
|
|||
RCDelayStuff *rc = (RCDelayStuff *) ResNodeList->rn_client;
|
||||
|
||||
resisdata->rg_nodecap = totalcap;
|
||||
ResCalculateTDi(ResNodeAtOrigin, (resResistor *)NULL,
|
||||
resisdata->rg_bigdevres);
|
||||
ResCalculateTDi(ResNodeAtOrigin, (resResistor *)NULL, 0);
|
||||
if (rc != (RCDelayStuff *)NULL)
|
||||
resisdata->rg_Tdi = rc->rc_Tdi;
|
||||
else
|
||||
|
|
@ -1013,20 +997,10 @@ ResDoSimplify(tolerance,resisdata)
|
|||
else
|
||||
resisdata->rg_Tdi = 0;
|
||||
|
||||
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.
|
||||
*/
|
||||
/* Simplify network */
|
||||
|
||||
if (ResOptionsFlags & ResOpt_Simplify)
|
||||
{
|
||||
millitolerance = tolerance * MILLIOHMSPEROHM;
|
||||
|
||||
/*
|
||||
* Start simplification at driver (R=0). Remove it from the done list
|
||||
* and add it to the pending list. Call ResSimplifyNet as long as
|
||||
|
|
@ -1044,16 +1018,18 @@ ResDoSimplify(tolerance,resisdata)
|
|||
/* if Tdi is enabled, prune all branches whose end nodes */
|
||||
/* have time constants less than the tolerance. */
|
||||
|
||||
if ((ResOptionsFlags & ResOpt_Tdi) &&
|
||||
resisdata->rg_Tdi != -1 &&
|
||||
rctol != 0)
|
||||
{
|
||||
ResPruneTree(ResNodeAtOrigin, (rctol + 1) *
|
||||
resisdata->rg_bigdevres * resisdata->rg_nodecap / rctol,
|
||||
if ((resisdata->rg_Tdi != -1) && (resisdata->mindelay > 0))
|
||||
ResPruneTree(ResNodeAtOrigin, resisdata->mindelay,
|
||||
&ResNodeList, &ResNodeQueue, &ResResList);
|
||||
}
|
||||
|
||||
ResNodeAtOrigin->rn_status &= ~RES_MARKED;
|
||||
if (ResNodeAtOrigin->rn_less == NULL)
|
||||
if (ResNodeAtOrigin->rn_less == CLIENTDEFAULT)
|
||||
{
|
||||
TxError("ResSimplify: Bad resptr at node %s origin.\n",
|
||||
ResNodeAtOrigin->rn_name);
|
||||
return 0;
|
||||
}
|
||||
else if (ResNodeAtOrigin->rn_less == NULL)
|
||||
ResNodeList = ResNodeAtOrigin->rn_more;
|
||||
else
|
||||
ResNodeAtOrigin->rn_less->rn_more = ResNodeAtOrigin->rn_more;
|
||||
|
|
@ -1065,13 +1041,14 @@ ResDoSimplify(tolerance,resisdata)
|
|||
ResNodeAtOrigin->rn_less = NULL;
|
||||
ResNodeQueue = ResNodeAtOrigin;
|
||||
while (ResNodeQueue != NULL)
|
||||
ResSimplifyNet(&ResNodeQueue, &ResNodeList, &ResResList, millitolerance);
|
||||
ResSimplifyNet(&ResNodeQueue, &ResNodeList, &ResResList,
|
||||
resisdata->minres);
|
||||
|
||||
/*
|
||||
* Call ResScrunchNet to eliminate any remaining under tolerance
|
||||
* Call ResScrunchNet to eliminate any remaining under-tolerance
|
||||
* resistors.
|
||||
*/
|
||||
ResScrunchNet(&ResResList, &ResNodeQueue, &ResNodeList, millitolerance);
|
||||
ResScrunchNet(&ResResList, &ResNodeQueue, &ResNodeList, resisdata->minres);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
1101
resis/ResUtils.c
1101
resis/ResUtils.c
File diff suppressed because it is too large
Load Diff
|
|
@ -160,7 +160,7 @@ resCurrentPrintFunc(node, resistor, filename)
|
|||
void
|
||||
ResDeviceCounts()
|
||||
{
|
||||
int i,j,k;
|
||||
int i, j, k;
|
||||
resNode *n;
|
||||
resDevice *t;
|
||||
resResistor *r;
|
||||
|
|
|
|||
160
resis/resis.h
160
resis/resis.h
|
|
@ -51,16 +51,35 @@ typedef struct resistor
|
|||
#define rr_connection1 rr_node[0]
|
||||
#define rr_connection2 rr_node[1]
|
||||
|
||||
/* Definitions for old FET-style MOSFET devices */
|
||||
/* The resDevTerm structure holds information needed to quickly check */
|
||||
/* 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_SOURCE 1
|
||||
#define RT_DRAIN 2
|
||||
#define RT_SUBS 3
|
||||
#define RT_SUBS 1
|
||||
#define RT_SOURCE 2
|
||||
#define RT_DRAIN 3
|
||||
|
||||
#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_drain rd_terminals[RT_DRAIN]
|
||||
#define rd_fet_subs rd_terminals[RT_SUBS]
|
||||
|
||||
typedef struct device
|
||||
{
|
||||
|
|
@ -79,6 +98,19 @@ typedef struct device
|
|||
Tile *rd_tile; /* pointer to a tile in device */
|
||||
} 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.
|
||||
*/
|
||||
|
|
@ -100,9 +132,10 @@ typedef struct junction
|
|||
typedef struct resport
|
||||
{
|
||||
struct resport *rp_nextPort;
|
||||
Rect rp_bbox;
|
||||
Point rp_loc;
|
||||
char *rp_nodename;
|
||||
ResConnect *rp_connect;
|
||||
Rect rp_bbox;
|
||||
Point rp_loc;
|
||||
} resPort;
|
||||
|
||||
/*
|
||||
|
|
@ -252,9 +285,10 @@ typedef struct resoptions
|
|||
{
|
||||
/* Global options for extresist */
|
||||
|
||||
float tdiTolerance;
|
||||
float frequency;
|
||||
float rthresh;
|
||||
float minres; /* Minimum network resistance to output */
|
||||
float mindelay; /* Minimum network delay to output */
|
||||
float rthresh; /* Minimum individual resistance */
|
||||
float frequency; /* For FastHenry geometry extraction */
|
||||
struct saveList *savePlanes;
|
||||
CellDef *mainDef;
|
||||
|
||||
|
|
@ -266,9 +300,8 @@ typedef struct resoptions
|
|||
|
||||
TileType rg_ttype;
|
||||
float rg_maxres;
|
||||
float rg_nodecap;
|
||||
float rg_Tdi;
|
||||
int rg_bigdevres;
|
||||
float rg_nodecap; /* Node capacitance */
|
||||
float rg_Tdi; /* Node delay */
|
||||
int rg_tilecount;
|
||||
int rg_status;
|
||||
Point *rg_devloc;
|
||||
|
|
@ -289,21 +322,19 @@ typedef struct rcdelaystuff
|
|||
typedef struct rdev
|
||||
{
|
||||
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 */
|
||||
/* corresponds to RDev */
|
||||
int status;
|
||||
struct resextnode *gate; /* Terminals of transistor. */
|
||||
struct resextnode *source;
|
||||
struct resextnode *drain;
|
||||
struct resextnode *subs; /* Used with subcircuit type only */
|
||||
Point location; /* Location of lower left point of */
|
||||
/* device. */
|
||||
float resistance; /* "Resistance" of device. */
|
||||
TileType rs_ttype; /* tile type for device */
|
||||
ExtDevice *rs_devptr; /* device extraction record */
|
||||
char *rs_gattr; /* Gate attributes, if any */
|
||||
struct resextnode *subs; /* Used with subcircuit type only */
|
||||
Point location; /* Location of lower left point */
|
||||
/* of the device. */
|
||||
TileType rs_ttype; /* tile type for device */
|
||||
float rs_wl; /* device W/L, if relevant */
|
||||
ExtDevice *rs_devptr; /* device extraction record */
|
||||
char *rs_gattr; /* Gate attributes, if any */
|
||||
char *rs_sattr;
|
||||
char *rs_dattr;
|
||||
} RDev;
|
||||
|
|
@ -320,17 +351,13 @@ typedef struct resextnode
|
|||
float cap_couple; /* Coupling capacitance */
|
||||
float resistance; /* Lumped resistance */
|
||||
float minsizeres; /* Minimum size resistor allowed */
|
||||
Point drivepoint; /* optional, user specified drive */
|
||||
/* point for network. */
|
||||
TileType rs_ttype; /* Tiletype of drivepoint */
|
||||
ResConnect *drivepoints; /* Upward connections */
|
||||
ResConnect *sinkpoints; /* Downward connections */
|
||||
Point location; /* Location of bottom of leftmost */
|
||||
/* tile in the lowest numbered */
|
||||
/* 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 */
|
||||
struct devptr *firstDev; /* Linked list of devices */
|
||||
struct devptr *devices; /* Linked list of devices */
|
||||
/* connected to node. */
|
||||
char *name; /* Pointer to name of node stored */
|
||||
/* in hash table. */
|
||||
|
|
@ -443,6 +470,7 @@ typedef struct capval
|
|||
/* Capacitance table constants */
|
||||
|
||||
#define OHMSTOMILLIOHMS 1000
|
||||
#define MILLIOHMSTOOHMS 0.001
|
||||
|
||||
#define UNTOUCHED 0
|
||||
#define SERIES 1
|
||||
|
|
@ -477,12 +505,12 @@ typedef struct capval
|
|||
#define ResOpt_DoExtFile 0x0004
|
||||
#define ResOpt_DoLumpFile 0x0008
|
||||
#define ResOpt_RunSilent 0x0010
|
||||
#define ResOpt_Stats 0x0020
|
||||
#define ResOpt_Tdi 0x0040
|
||||
#define ResOpt_Debug 0x0020
|
||||
#define ResOpt_Stats 0x0040
|
||||
#define ResOpt_Signal 0x0080
|
||||
#define ResOpt_Geometry 0x0100
|
||||
#define ResOpt_FastHenry 0x0200
|
||||
#define ResOpt_Blackbox 0x0300
|
||||
#define ResOpt_Blackbox 0x0400
|
||||
#define ResOpt_DoSubstrate 0x0800
|
||||
#define ResOpt_Box 0x1000
|
||||
|
||||
|
|
@ -510,7 +538,7 @@ extern ResFixPoint *ResFixList;
|
|||
extern int ResTileCount;
|
||||
extern ResExtNode **ResNodeArray;
|
||||
extern CellDef *mainDef;
|
||||
extern TileTypeBitMask ResSDTypesBitMask;
|
||||
extern TileTypeBitMask ResTermTypesBitMask;
|
||||
extern TileTypeBitMask ResSubTypesBitMask;
|
||||
extern HashTable ResDevTable;
|
||||
extern TileTypeBitMask ResNoMergeMask[NT];
|
||||
|
|
@ -525,12 +553,14 @@ extern int ResReadResistor();
|
|||
extern int ResReadAttribute();
|
||||
extern int ResReadMerge();
|
||||
extern int ResReadSubckt();
|
||||
extern int ResReadParentExt();
|
||||
|
||||
extern int ResProcessNode();
|
||||
extern int ResExtCombineParallel();
|
||||
extern int dbSrConnectStartFunc();
|
||||
extern int ResEach();
|
||||
extern int ResAddPlumbing();
|
||||
extern void ResAddDevPlumbing();
|
||||
extern int ResRemovePlumbing();
|
||||
extern float ResCalculateChildCapacitance();
|
||||
extern ResDevTile *DBTreeCopyConnectDCS();
|
||||
|
|
@ -545,12 +575,16 @@ extern void ResFixDevName();
|
|||
extern void ResWriteLumpFile();
|
||||
extern int ResSortBreaks();
|
||||
extern Plane *extResPrepSubstrate();
|
||||
extern bool ResEachTile();
|
||||
extern void ResStartTile();
|
||||
|
||||
|
||||
/* C99 compat */
|
||||
|
||||
extern void ExtResisForDef(CellDef *celldef, ResisData *resisdata);
|
||||
extern char *ResExtGetAttribute(char *sptr);
|
||||
extern int ResReadFET(int argc, char *argv[]);
|
||||
extern int ResReadConnectPoint(CellDef *def, int argc, char *argv[]);
|
||||
extern int ResReadPort(int argc, char *argv[]);
|
||||
extern char *ResExtGetAttribute(char *sptr);
|
||||
|
||||
|
|
@ -567,9 +601,10 @@ extern void ResEliminateResistor();
|
|||
extern bool ResExtractNet();
|
||||
extern int ResFracture();
|
||||
extern void ResMergeNodes();
|
||||
extern void ResNewSDDevice();
|
||||
extern void ResNewTermDevice();
|
||||
extern void ResNewSubDevice();
|
||||
extern void ResPreProcessDevices();
|
||||
extern void ResFreeDevTiles();
|
||||
extern void ResPrintDeviceList();
|
||||
extern void ResPrintExtDev();
|
||||
extern void ResPrintReference();
|
||||
|
|
@ -596,59 +631,8 @@ extern int resWalkleft();
|
|||
extern int resWalkright();
|
||||
extern int resWalkup();
|
||||
|
||||
/* Macros */
|
||||
|
||||
#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; \
|
||||
}
|
||||
extern void InitializeResNode();
|
||||
extern void ResInfoInit();
|
||||
extern void ResNewBreak();
|
||||
|
||||
#endif /* _MAGIC__RESIS__RESIS_H */
|
||||
|
|
|
|||
|
|
@ -6276,6 +6276,7 @@ magic_with_tk_libraries=""
|
|||
|
||||
usingOGL=1
|
||||
usingTcl=1
|
||||
usingTk=1
|
||||
usingOA=0
|
||||
usingCairo=1
|
||||
usingPython3=1
|
||||
|
|
@ -7254,10 +7255,12 @@ if test "${with_tcl+set}" = set; then :
|
|||
magic_with_tcl=$withval
|
||||
if test "$withval" = "no" -o "$withval" = "NO"; then
|
||||
usingTcl=
|
||||
usingTk=
|
||||
elif test $usingScheme ; then
|
||||
echo Attempt to enable both Tcl and Scheme interpreters.
|
||||
echo Disabling Tcl, and using Scheme instead.
|
||||
usingTcl=
|
||||
usingTk=
|
||||
fi
|
||||
|
||||
fi
|
||||
|
|
@ -7268,6 +7271,9 @@ fi
|
|||
# Check whether --with-tk was given.
|
||||
if test "${with_tk+set}" = set; then :
|
||||
withval=$with_tk; magic_with_tk=$withval
|
||||
if test "$withval" = "no" -o "$withval" = "NO"; then
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
|
|
@ -7397,7 +7403,7 @@ fi
|
|||
# Find the Tk build configuration file "tkConfig.sh"
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
if test $usingTcl ; then
|
||||
if test $usingTk ; then
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tkConfig.sh" >&5
|
||||
$as_echo_n "checking for tkConfig.sh... " >&6; }
|
||||
|
|
@ -7477,8 +7483,8 @@ $as_echo "${tk_config_sh}" >&6; }
|
|||
|
||||
if test "x$tk_config_sh" = "x" ; then
|
||||
echo "can't find Tk configuration script \"tkConfig.sh\""
|
||||
echo "Reverting to non-Tcl compilation"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compilation"
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -7488,7 +7494,9 @@ fi
|
|||
|
||||
if test $usingTcl ; then
|
||||
. $tcl_config_sh
|
||||
. $tk_config_sh
|
||||
if test $usingTk ; then
|
||||
. $tk_config_sh
|
||||
fi
|
||||
|
||||
# Should probably trust the config file contents, but this configure
|
||||
# file checks the Tcl and Tk include and lib directories. Since
|
||||
|
|
@ -7504,21 +7512,24 @@ if test $usingTcl ; then
|
|||
|
||||
tmpstr=${TCL_LIB_SPEC#*-L}
|
||||
TCL_LIB_DIR=${tmpstr% -l*}
|
||||
tmpstr=${TK_LIB_SPEC#*-L}
|
||||
TK_LIB_DIR=${tmpstr% -l*}
|
||||
TCL_INC_DIR=${TCL_INCLUDE_SPEC#*-I}
|
||||
TK_INC_DIR=${TK_INCLUDE_SPEC#*-I}
|
||||
if test $usingTk ; then
|
||||
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
|
||||
:
|
||||
elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then
|
||||
:
|
||||
elif test "$TCL_VERSION" = "$TK_VERSION" ; then
|
||||
:
|
||||
else
|
||||
echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
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" = "$TK_VERSION" ; then
|
||||
:
|
||||
else
|
||||
echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -7551,14 +7562,14 @@ if test $usingTcl ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
if test $usingTcl ; then
|
||||
if test $usingTk ; then
|
||||
if test "x${magic_with_tk_includes}" != "x" ; then
|
||||
if test -r "${magic_with_tk_includes}/tk.h" ; then
|
||||
TK_INC_DIR=${magic_with_tk_includes}
|
||||
else
|
||||
echo "Can't find tk.h in \"${magic_with_tk_includes}\""
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
else
|
||||
for dir in \
|
||||
|
|
@ -7575,8 +7586,8 @@ if test $usingTcl ; then
|
|||
done
|
||||
if test "x${TK_INC_DIR}" = "x" ; then
|
||||
echo "Can't find tk.h header file"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
@ -7599,14 +7610,14 @@ if test $usingTcl ; then
|
|||
if test "x${TCL_LIB_SPEC}" = "x" ; then
|
||||
TCL_LIB_SPEC="-l${TCL_LIB_NAME}"
|
||||
fi
|
||||
if test "x${TK_LIB_SPEC}" = "x" ; then
|
||||
if test $usingTk -a "x${TK_LIB_SPEC}" = "x" ; then
|
||||
TK_LIB_SPEC="-l${TK_LIB_NAME}"
|
||||
fi
|
||||
|
||||
# Find the version of "wish" that corresponds to TCL_EXEC_PREFIX
|
||||
# We really ought to run "ldd" to confirm that the linked libraries match.
|
||||
|
||||
if test "x${magic_with_wish_binary}" = "x" ; then
|
||||
if test $usingTk -a "x${magic_with_wish_binary}" = "x" ; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for wish executable" >&5
|
||||
$as_echo_n "checking for wish executable... " >&6; }
|
||||
for dir in \
|
||||
|
|
@ -7638,7 +7649,7 @@ $as_echo "no" >&6; }
|
|||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${WISH_EXE}" >&5
|
||||
$as_echo "${WISH_EXE}" >&6; }
|
||||
fi
|
||||
else
|
||||
elif test $usingTk ; then
|
||||
WISH_EXE=${magic_with_wish_binary}
|
||||
fi
|
||||
|
||||
|
|
@ -7731,7 +7742,7 @@ $as_echo "${TCLSH_EXE}" >&6; }
|
|||
fi
|
||||
fi
|
||||
|
||||
if test $usingTcl ; then
|
||||
if test $usingTk ; then
|
||||
if test "x${magic_with_tk_libraries}" != "x" ; then
|
||||
for libname in \
|
||||
"${magic_with_tk_libraries}/${TCL_LIB_FILE}" \
|
||||
|
|
@ -7746,8 +7757,8 @@ if test $usingTcl ; then
|
|||
done
|
||||
if test "x${TK_LIB_DIR}" = "x" ; then
|
||||
echo "Can't find tk library in \"${magic_with_tk_libraries}\""
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
else
|
||||
for libname in \
|
||||
|
|
@ -7762,8 +7773,8 @@ if test $usingTcl ; then
|
|||
done
|
||||
if test "x${TK_LIB_DIR}" = "x" ; then
|
||||
echo "Can't find tk library"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
@ -8638,7 +8649,12 @@ if test $usingTcl ; then
|
|||
|
||||
extra_libs="$extra_libs \${MAGICDIR}/tcltk/libtcltk.o"
|
||||
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
|
||||
if test $usingTk ; then
|
||||
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
|
||||
modules="$modules lisp"
|
||||
unused="$unused tcltk"
|
||||
|
|
@ -8681,7 +8697,9 @@ if test $usingTcl ; then
|
|||
gr_libs="$gr_libs -lX11"
|
||||
fi
|
||||
fi
|
||||
gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
|
||||
if test $usingTk ; then
|
||||
gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
|
||||
fi
|
||||
else
|
||||
if test $usingX11 ; then
|
||||
gr_dflags="$gr_dflags -DX11 -DXLIB"
|
||||
|
|
@ -8791,23 +8809,25 @@ if test $usingTcl ; then
|
|||
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# Tk libraries and header files
|
||||
# Tk libraries and header files (skipped under --without-tk)
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
if test "${TK_INC_DIR}" != "/usr/include" ; then
|
||||
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
|
||||
fi
|
||||
if test "${TK_LIB_DIR}" = "/usr/lib" -o \
|
||||
"${TK_LIB_DIR}" = "/usr/lib64" ; then
|
||||
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_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}"
|
||||
if test $usingTk ; then
|
||||
if test "${TK_INC_DIR}" != "/usr/include" ; then
|
||||
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
|
||||
fi
|
||||
if test "${TK_LIB_DIR}" = "/usr/lib" -o \
|
||||
"${TK_LIB_DIR}" = "/usr/lib64" ; then
|
||||
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_LIB_SPEC}"
|
||||
LIB_SPECS="${LIB_SPECS} ${TK_STUB_LIB_SPEC}"
|
||||
else
|
||||
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
|
||||
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
|
||||
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -344,6 +344,7 @@ dnl disabled with --with-opengl=no
|
|||
|
||||
usingOGL=1
|
||||
usingTcl=1
|
||||
usingTk=1
|
||||
usingOA=0
|
||||
usingCairo=1
|
||||
usingPython3=1
|
||||
|
|
@ -442,10 +443,12 @@ AC_ARG_WITH(tcl,
|
|||
magic_with_tcl=$withval
|
||||
if test "$withval" = "no" -o "$withval" = "NO"; then
|
||||
usingTcl=
|
||||
usingTk=
|
||||
elif test $usingScheme ; then
|
||||
echo Attempt to enable both Tcl and Scheme interpreters.
|
||||
echo Disabling Tcl, and using Scheme instead.
|
||||
usingTcl=
|
||||
usingTk=
|
||||
fi
|
||||
], )
|
||||
|
||||
|
|
@ -455,10 +458,17 @@ dnl and don't set the usingTcl variable.
|
|||
dnl
|
||||
dnl This has been broken up into a number of sections, each of which
|
||||
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 ----------------------------------------------------------------
|
||||
|
||||
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],
|
||||
magic_with_tcl_includes=$withval)
|
||||
AC_ARG_WITH(tkincls, [ --with-tkincls=DIR Find tk.h in DIR],
|
||||
|
|
@ -564,7 +574,7 @@ fi
|
|||
# Find the Tk build configuration file "tkConfig.sh"
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
if test $usingTcl ; then
|
||||
if test $usingTk ; then
|
||||
|
||||
AC_MSG_CHECKING([for tkConfig.sh])
|
||||
tk_config_sh=""
|
||||
|
|
@ -642,8 +652,8 @@ if test $usingTcl ; then
|
|||
|
||||
if test "x$tk_config_sh" = "x" ; then
|
||||
echo "can't find Tk configuration script \"tkConfig.sh\""
|
||||
echo "Reverting to non-Tcl compilation"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compilation"
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -653,7 +663,9 @@ fi
|
|||
|
||||
if test $usingTcl ; then
|
||||
. $tcl_config_sh
|
||||
. $tk_config_sh
|
||||
if test $usingTk ; then
|
||||
. $tk_config_sh
|
||||
fi
|
||||
|
||||
# Should probably trust the config file contents, but this configure
|
||||
# file checks the Tcl and Tk include and lib directories. Since
|
||||
|
|
@ -669,21 +681,24 @@ if test $usingTcl ; then
|
|||
|
||||
tmpstr=${TCL_LIB_SPEC#*-L}
|
||||
TCL_LIB_DIR=${tmpstr% -l*}
|
||||
tmpstr=${TK_LIB_SPEC#*-L}
|
||||
TK_LIB_DIR=${tmpstr% -l*}
|
||||
TCL_INC_DIR=${TCL_INCLUDE_SPEC#*-I}
|
||||
TK_INC_DIR=${TK_INCLUDE_SPEC#*-I}
|
||||
if test $usingTk ; then
|
||||
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
|
||||
:
|
||||
elif test "$TCL_VERSION" = "7.5" -a "$TK_VERSION" = "4.1" ; then
|
||||
:
|
||||
elif test "$TCL_VERSION" = "$TK_VERSION" ; then
|
||||
:
|
||||
else
|
||||
echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
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" = "$TK_VERSION" ; then
|
||||
:
|
||||
else
|
||||
echo "Mismatched Tcl/Tk versions ($TCL_VERSION != $TK_VERSION)"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
@ -716,14 +731,14 @@ if test $usingTcl ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
if test $usingTcl ; then
|
||||
if test $usingTk ; then
|
||||
if test "x${magic_with_tk_includes}" != "x" ; then
|
||||
if test -r "${magic_with_tk_includes}/tk.h" ; then
|
||||
TK_INC_DIR=${magic_with_tk_includes}
|
||||
else
|
||||
echo "Can't find tk.h in \"${magic_with_tk_includes}\""
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
else
|
||||
for dir in \
|
||||
|
|
@ -740,8 +755,8 @@ if test $usingTcl ; then
|
|||
done
|
||||
if test "x${TK_INC_DIR}" = "x" ; then
|
||||
echo "Can't find tk.h header file"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
@ -764,14 +779,14 @@ if test $usingTcl ; then
|
|||
if test "x${TCL_LIB_SPEC}" = "x" ; then
|
||||
TCL_LIB_SPEC="-l${TCL_LIB_NAME}"
|
||||
fi
|
||||
if test "x${TK_LIB_SPEC}" = "x" ; then
|
||||
if test $usingTk -a "x${TK_LIB_SPEC}" = "x" ; then
|
||||
TK_LIB_SPEC="-l${TK_LIB_NAME}"
|
||||
fi
|
||||
|
||||
# Find the version of "wish" that corresponds to TCL_EXEC_PREFIX
|
||||
# We really ought to run "ldd" to confirm that the linked libraries match.
|
||||
|
||||
if test "x${magic_with_wish_binary}" = "x" ; then
|
||||
if test $usingTk -a "x${magic_with_wish_binary}" = "x" ; then
|
||||
AC_MSG_CHECKING([for wish executable])
|
||||
for dir in \
|
||||
${TK_EXEC_PREFIX}/bin \
|
||||
|
|
@ -800,7 +815,7 @@ if test $usingTcl ; then
|
|||
else
|
||||
AC_MSG_RESULT([${WISH_EXE}])
|
||||
fi
|
||||
else
|
||||
elif test $usingTk ; then
|
||||
WISH_EXE=${magic_with_wish_binary}
|
||||
fi
|
||||
|
||||
|
|
@ -890,7 +905,7 @@ if test $usingTcl ; then
|
|||
fi
|
||||
fi
|
||||
|
||||
if test $usingTcl ; then
|
||||
if test $usingTk ; then
|
||||
if test "x${magic_with_tk_libraries}" != "x" ; then
|
||||
for libname in \
|
||||
"${magic_with_tk_libraries}/${TCL_LIB_FILE}" \
|
||||
|
|
@ -905,8 +920,8 @@ if test $usingTcl ; then
|
|||
done
|
||||
if test "x${TK_LIB_DIR}" = "x" ; then
|
||||
echo "Can't find tk library in \"${magic_with_tk_libraries}\""
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
else
|
||||
for libname in \
|
||||
|
|
@ -921,8 +936,8 @@ if test $usingTcl ; then
|
|||
done
|
||||
if test "x${TK_LIB_DIR}" = "x" ; then
|
||||
echo "Can't find tk library"
|
||||
echo "Reverting to non-Tcl compile"
|
||||
usingTcl=
|
||||
echo "Reverting to non-Tk compile"
|
||||
usingTk=
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
@ -1356,7 +1371,12 @@ if test $usingTcl ; then
|
|||
AC_DEFINE(MAGIC_WRAPPER)
|
||||
extra_libs="$extra_libs \${MAGICDIR}/tcltk/libtcltk.o"
|
||||
extra_defs="$extra_defs -DTCL_DIR=\\\"\${TCLDIR}\\\""
|
||||
stub_defs="$stub_defs -DUSE_TCL_STUBS -DUSE_TK_STUBS"
|
||||
if test $usingTk ; then
|
||||
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
|
||||
modules="$modules lisp"
|
||||
unused="$unused tcltk"
|
||||
|
|
@ -1401,7 +1421,9 @@ if test $usingTcl ; then
|
|||
gr_libs="$gr_libs -lX11"
|
||||
fi
|
||||
fi
|
||||
gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
|
||||
if test $usingTk ; then
|
||||
gr_srcs="$gr_srcs \${TKCOMMON_SRCS}"
|
||||
fi
|
||||
else
|
||||
if test $usingX11 ; then
|
||||
gr_dflags="$gr_dflags -DX11 -DXLIB"
|
||||
|
|
@ -1506,23 +1528,25 @@ if test $usingTcl ; then
|
|||
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# Tk libraries and header files
|
||||
# Tk libraries and header files (skipped under --without-tk)
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
if test "${TK_INC_DIR}" != "/usr/include" ; then
|
||||
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
|
||||
fi
|
||||
if test "${TK_LIB_DIR}" = "/usr/lib" -o \
|
||||
"${TK_LIB_DIR}" = "/usr/lib64" ; then
|
||||
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_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}"
|
||||
if test $usingTk ; then
|
||||
if test "${TK_INC_DIR}" != "/usr/include" ; then
|
||||
INC_SPECS="${INC_SPECS} -I${TK_INC_DIR}"
|
||||
fi
|
||||
if test "${TK_LIB_DIR}" = "/usr/lib" -o \
|
||||
"${TK_LIB_DIR}" = "/usr/lib64" ; then
|
||||
LIB_SPECS_NOSTUB="${LIB_SPECS_NOSTUB} ${TK_LIB_SPEC}"
|
||||
LIB_SPECS="${LIB_SPECS} ${TK_STUB_LIB_SPEC}"
|
||||
else
|
||||
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
|
||||
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
|
||||
loader_run_path="${TK_LIB_DIR}:${loader_run_path}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -470,6 +470,9 @@ SelectArea(scx, types, xMask, globmatch)
|
|||
|
||||
if (TTMaskHasType(types, L_LABEL))
|
||||
{
|
||||
TTMaskClearType(types, L_LABEL);
|
||||
if (TTMaskIsZero(types)) types = &DBAllButSpaceAndDRCBits;
|
||||
|
||||
if (globmatch != NULL)
|
||||
DBCellCopyGlobLabels(scx, types, xMask, SelectUse, &labelArea,
|
||||
globmatch);
|
||||
|
|
@ -834,6 +837,8 @@ chunkdone:
|
|||
if (DBIsContact(type))
|
||||
TTMaskSetOnlyType(&typeMask, type);
|
||||
|
||||
/* Allow labels to be selected as part of the chunk */
|
||||
TTMaskSetType(&typeMask, L_LABEL);
|
||||
SelectArea(&newscx, &typeMask, xMask, NULL);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ SimConnectFunc(
|
|||
TileType loctype, ctype;
|
||||
TileType newdinfo = 0;
|
||||
int i, pNum;
|
||||
static char nodeName[256];
|
||||
static char nodeName[MAXPATHNAME];
|
||||
CellDef *def;
|
||||
TerminalPath *tpath = cx->tc_filter->tf_tpath;
|
||||
|
||||
|
|
@ -133,7 +133,8 @@ SimConnectFunc(
|
|||
char c = *n;
|
||||
|
||||
SigDisableInterrupts();
|
||||
strcpy(nodeName, SimGetNodeName(cx->tc_scx, tile, dinfo, tpath->tp_first));
|
||||
strncpy(nodeName, SimGetNodeName(cx->tc_scx, tile, dinfo, tpath->tp_first),
|
||||
MAXPATHNAME);
|
||||
SigEnableInterrupts();
|
||||
|
||||
*n = c;
|
||||
|
|
|
|||
|
|
@ -41,6 +41,16 @@ BIN_FILES = \
|
|||
|
||||
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}
|
||||
${RM} $(DESTDIR)${INSTALL_TCLDIR}/magicexec
|
||||
${CP} magicexec $(DESTDIR)${INSTALL_TCLDIR}/magicexec
|
||||
|
|
|
|||
|
|
@ -178,8 +178,10 @@ TagCallback(interp, tkpath, argc, argv)
|
|||
windCheckOnlyWindow(&w, DBWclientID);
|
||||
if (w != NULL && !(w->w_flags & WIND_OFFSCREEN))
|
||||
{
|
||||
#ifndef MAGIC_NO_TK
|
||||
Tk_Window tkwind = (Tk_Window) w->w_grdata;
|
||||
if (tkwind != NULL) tkpath = Tk_PathName(tkwind);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (tkpath == NULL)
|
||||
|
|
@ -654,6 +656,37 @@ process_rlimit_startup_check(void)
|
|||
#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 */
|
||||
/*------------------------------------------------------*/
|
||||
|
|
@ -742,8 +775,10 @@ _magic_initialize(ClientData clientData,
|
|||
/* (See graphics/grTkCommon.c) */
|
||||
/* (Unless "-dnull" option has been given) */
|
||||
|
||||
#ifndef MAGIC_NO_TK
|
||||
if (strcmp(MainDisplayType, "NULL"))
|
||||
RegisterTkCommands(interp);
|
||||
#endif
|
||||
|
||||
/* Set up the console so that its menu option File->Exit */
|
||||
/* calls magic's exit routine first. This should not be */
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@
|
|||
#ifdef MAGIC_WRAPPER
|
||||
|
||||
#include <tcl.h>
|
||||
#ifndef MAGIC_NO_TK
|
||||
#include <tk.h>
|
||||
#endif
|
||||
|
||||
/* Externally-defined global variables */
|
||||
|
||||
|
|
@ -30,5 +32,7 @@ extern void MakeWindowCommand();
|
|||
|
||||
extern const char *Tclmagic_InitStubsVersion;
|
||||
|
||||
extern void TclmagicRegisterCommands(Tcl_Interp *interp);
|
||||
|
||||
#endif /* MAGIC_WRAPPER */
|
||||
#endif /* _MAGIC__TCLTK__TCLMAGIC_H */
|
||||
|
|
|
|||
|
|
@ -481,6 +481,28 @@ proc magic::techmanager {{option "update"}} {
|
|||
"magic::tech load $j ; \
|
||||
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]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
#!/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/^/ /'
|
||||
|
|
@ -24,11 +24,12 @@ TOP_EXTRA_LIBS += \
|
|||
-sWASM=1 \
|
||||
-sMODULARIZE=1 \
|
||||
-sEXPORT_ES6=1 \
|
||||
-sUSE_ZLIB=1 \
|
||||
-sEXPORTED_FUNCTIONS=_magic_wasm_init,_magic_wasm_run_command,_magic_wasm_source_file,_magic_wasm_update \
|
||||
-sEXPORTED_RUNTIME_METHODS=cwrap,ccall,FS,setValue,getValue \
|
||||
-sALLOW_MEMORY_GROWTH=1 \
|
||||
-sINITIAL_MEMORY=33554432 \
|
||||
-sSTACK_SIZE=5242880 \
|
||||
-sINITIAL_MEMORY=67108864 \
|
||||
-Wl,-z,stack-size=10485760 \
|
||||
-sASSERTIONS=1 \
|
||||
-sENVIRONMENT=node,web,worker \
|
||||
-sFORCE_FILESYSTEM=1 \
|
||||
|
|
|
|||
|
|
@ -563,6 +563,7 @@ mainInitAfterArgs()
|
|||
SectionID sec_cifinput, sec_cifoutput;
|
||||
SectionID sec_drc, sec_extract, sec_wiring, sec_router;
|
||||
SectionID sec_plow, sec_plot, sec_mzrouter;
|
||||
char *syspath;
|
||||
|
||||
DBTypeInit();
|
||||
MacroInit();
|
||||
|
|
@ -577,9 +578,8 @@ mainInitAfterArgs()
|
|||
#endif
|
||||
|
||||
/*
|
||||
* Setup path names for system directory searches
|
||||
* Set up path names for system directory searches
|
||||
*/
|
||||
|
||||
StrDup(&SysLibPath, MAGIC_SYS_PATH);
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in New Issue