Compare commits

...

59 Commits

Author SHA1 Message Date
R. Timothy Edwards 43e4cf9b03 Expanded the use of the "tagged" operator to allow the use of
"tagged <text>" without any additional arguments, to indicate
that the operator should look for text that overlaps the
current layer.  That allows handling the case were a text tag
is sitting over space inside a cell, which otherwise would not
be able to be searched.  Also corrected a likely issue in which
point labels could not be used as tags because they have no
area to search under.
2026-06-04 21:20:03 -04:00
R. Timothy Edwards 7a0e2aa2b9 Additional corrections fix the problem of devices being removed from
the output if they reference a killed node.  This will result in an
invalid netlist, but at least now all devices are represented in the
output, and a warning is issued about the error.  There are still
multiple problems that need to be tracked down and fixed.  Most
problems are stemming from the unusual "Short" device handling.
2026-06-04 17:45:03 -04:00
R. Timothy Edwards a157ec9aa8 Another overhaul of the R-C extraction code. This fixes a known
long-standing problem, in which the resistance extraction attempts
to determine the device type of each device.  Which is stupid,
because the original extraction process has already done this, and
the information is readily available in the .ext file.  This update
allows "extresist" to get all the device information from the .ext
file, so there is no guesswork and no extra time spent trying.
Unfortunately, it does not solve the problem I expected it to solve,
which is the disappearance of _dss and moscap devices from gf180mcu
netlists.  More work to do. . .
2026-06-04 12:50:13 -04:00
R. Timothy Edwards 1a1cee058e Fixed an issue with the connectivity stack which does not change
"lasttop" after a new heap has been allocated and "top" is reset.
This is a minor issue, as even random data at the top of the
newly allocated heap is vanishingly unlikely to make a match,
but valgrind caught it as a use of unallocated memory, and it
should be fixed.
2026-06-03 11:34:41 -04:00
Torleif Skår 799fd5d8f2 fix(ext2spice): use ';' instead of '$' for NGSpice end-of-line comments
'$' is only used by NGSpice, for broader compatibility use ';'.
2026-06-03 11:27:36 -04:00
R. Timothy Edwards 21b8579734 Found the error with rg_maxres, which is that it was being used in
one place as ohms and another as milliohms.  Changed it to be
milliohms always, except when written as output to the lumped
resistance file or as diagnostic output.  The result makes more
sense, but now has the issue that the maximum resistance is always
an over-estimate and may need to account for resistances in
parallel.
2026-06-01 17:33:45 -04:00
R. Timothy Edwards 03610f6d40 Additional tweaks to the handling of the three new options for
determining what does and doesn't get into the R-C extraction
output.  Added option "debug" and moved all excess diagnostic
information to be gated by this flag.  Corrected an issue in
the standard extraction in which the substrate is always output
with zero lumped resistance, preventing it from being checked
by "extresist" for inclusion or exclusion in the .res.ext output.
Reinstated the computation of minimum resistance transistor but
excluded other devices.  Added more sane handling of exceptional
conditions like loops.  The result is a more consistent check of
nets in the design.  However, I still need to recheck the
"rg_maxres" calculation because it appears to be too low.
2026-06-01 17:21:02 -04:00
R. Timothy Edwards c22031724a Some further refinements to the last commit, and other additions:
(1) An incorrect use of ExtResetTiles() was found in "extresist"
    which impacts performance, especially for small nets.
(2) Corrected units for resistance tolerance in extresist, and
    handled output printing in fs when delay values get below ps
    size.
(3) Added command option "extract do unique notopports" to be the
    extraction option equivalent of the standalone command
    "extract unique notopports".
(4) Changed the "extresist" default for "mindelay" to 1ps from 0,
    in response to the observation that lumped resistance from
    "extract" can be an extreme overestimate, and the extracted
    time delay from "extesist" calculations should be used as a
    better determination of whether a net should be output as a
    resistor network or not.
(5) Added documentation for both "extract do unique notopports"
    and the change to the "extresist" default values.
2026-05-29 17:16:37 -04:00
R. Timothy Edwards 349ffd091f Corrected an error when merging nodes during "ext2spice". The code
was checking for device terminals that were pointing to the
deallocated node and updating them.  However, it failed to check
for whether or not the substrate terminal was pointing to the
deallocated node, potentially causing a crash condition.  That has
now been fixed.
2026-05-29 14:06:50 -04:00
R. Timothy Edwards 4393d9310a More work on the "extresist" code, this time to revise the way
that "extresist" selects or rejects networks for resistance
extraction.  The "tolerance" value, which nobody (including
myself) seems to understand, is now deprecated, replaced by
a handful of more meaningful options:  "threshold" to set a
lumped resistance estimate threshold to determined whether or
not to extract a network;  "minres" to specify when to prune
small resistors from the resulting tree network, and "mindelay"
to gate the selection and output on the estimated signal
propagation delay.  With these settings, I am much better able
to control the size and complexity of the resulting output.
2026-05-29 11:47:01 -04:00
R. Timothy Edwards 1bcad6a25c Significant overhaul of the extresist code. The purpose of this
overhaul was to introduce the concept of a "connection point"
between two cells to the ".ext" file format so that it becomes
possible to do proper hierarchical R-C extraction.  Previously
this information was not kept in the .ext file, making it
impossible for "extresist" to know what sub-net of a net connects
up or down the hierarchy.  However, additional changes were made
to the extresist code (in the resis/ directory) to add the
handling of connections up and down the hierarchy, and to make
the code a lot clearer and fix ambiguous variable and subroutine
names.
2026-05-28 13:12:13 -04:00
R. Timothy Edwards d9e6c78adb Corrected an issue that was caused by early work on extresist:
The original version of extresist only worked with 4-terminal FET
devices, and handling of devices with fewer terminals was ignored,
and never properly dealt with.  This commit fixes the issues with
devices such as diodes that have fewer terminals.
2026-05-27 10:52:44 -04:00
R. Timothy Edwards d8580be739 Corrected a problem caused by fixing the "select visible" command
with respect to visible/invisible labels and cells.  The change
inadvertently made the "select area" command option stop selecting
labels on the same type as the layers being selected.  This has
been fixed.
2026-05-26 17:01:45 -04:00
R. Timothy Edwards 1a16502a69 Fixed a potential string overrun crash condition when doing
"getnode", as there is no limit on the length of a hierarchical
node name, and no check on the string length when copying back into
a fixed-length character array.
2026-05-25 14:39:12 -04:00
R. Timothy Edwards 5ecf10c022 Updated the version to go along with the merge of pull request #521
from Sylvain Munaut, which fixes an issue with the "property" command
when using the recently-added plane properties.
2026-05-25 12:07:33 -04:00
Sylvain Munaut e366cf6a4c commands: Fix parsing of plane properties in case of single string
The previous code would iterate over `proplen` chunks of strings
but would not initialize `proplen` for plane properties.

Technically for plane properties you don't need to "pre-count"
the string chunks but given how the code is currently written
it's easier to do the counting in all cases.

Also makes sure proprec is init to NULL so that if argument is
empty, property is set to NULL and not random value on the stack.

Fixes #520

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
2026-05-25 12:49:52 +02:00
R. Timothy Edwards 7ecebb5dd6 Corrected a stupid omission to set the plane hint during a search
in "extresist", which when missing causes the plane to be searched
repeatedly from the same point instead of from the last place searched.
Can knock down the "extresist" time by up to 50%.
2026-05-24 22:01:29 -04:00
R. Timothy Edwards 099d513011 Corrected the "Pick" button action in the "Library Manager" window;
this had two issues:  (1) "getcell" was not being called with
"child 0 0", leading to child cells that are not placed where
expected in relation to a grid snap setting;  and (2) "cursor"
was assuming internal units and needed that to be explicitly
stated using "cursor internal".
2026-05-24 16:23:18 -04:00
R. Timothy Edwards e45399d347 Yet another optimization on "extresist", to remove the calls to
DBPaint() and DBErase(), which are meant as high-level calls from
the "paint" and "erase" commands and are not appropriate for low-
level tasks like erasing all contacts out of a database.  This
cut maybe 40 to 50% time off of the resistance extraction.
2026-05-22 12:02:03 -04:00
R. Timothy Edwards 7a3717b02a Another performance optimization to "extresist", this one to
avoid re-parsing a linked list from the current breakpoint
to the end to find all entries pointing to the same node.
Instead, the change to be made is saved in a hash table and
applied at the following link, so only one pass through the
linked list is required.
2026-05-21 19:55:50 -04:00
R. Timothy Edwards a062fdcfe0 Continued optimizing poorly written routines in "extresist".
Changed the "ResDissolveContacts()" routine to avoid running both
redundant and useless code.  Changed the breakpoint sorting
routine from a bubble sort to a merge sort for linked lists
longer than 16, as merge sort is more efficient for long lists.
2026-05-20 19:02:22 -04:00
Ahmed Nematallah 29447a35cd Delete duplicate magicps.pro file
This file exists in the magic directory, and is the one the build system uses, this file is a duplicate that has one different line "/ms { rectfill } bind def", and is not used anywhere

Keeping two duplicate files causes issues like them being out of sync, therefore it would be great if it was deleted
2026-05-19 12:22:37 -04:00
Ahmed Nematallah 1dcd09e20c Update the postscript file to be able to support much more stipples
The postscript template had a hardcoded stipple length of 29, any more stipples would cause problems with a postscript parser. This was particularly a problem since the default mos.24bit.dstyle has 36 stipples already.

It also used a base-30 encoding.

I have updated the template to fix both issues, it now uses base-10 encoding, and can support up to 255 stipples (which is the maximum length allowed by the current approach of using fonts)

I was also wondering if it made more sense to encode this file directly into the C program as a literal, as half of it already is in the C program. (some "begin"s have an "end" in strings directly embedded in magic, so this file basically serves as a large string that has to be loaded each time from disk, while other parts of the string are stored normally in memory)
2026-05-19 12:22:37 -04:00
R. Timothy Edwards 9626b5e8c9 Changed two sprintf() to snprint() on recommendation from an AI. 2026-05-19 12:21:54 -04:00
R. Timothy Edwards 256955f48e Created alternative algorithms for routines ResParallelCheck() and
ResTriangleCheck().  By using hash tables, I reduced
ResParallelCheck() from O(N^2) to O(N), and I reduced
ResTriangleCheck() from O(N^3) to O(N^2);  however, it is a bit
better than that because previously there was an N^2 loop over
items in the same list, so if one list happened to be very long,
then N^2 would be huge; whereas now the N^2 is the length of two
lists multiplied together, where both lists would have to be very
long to have the same performance impact.  It appears that long
resistor lists are pathological and rare.  For an example
pathological case, the extresist runtime has been reduced from
hours to seconds.
2026-05-15 21:38:32 -04:00
R. Timothy Edwards b983e33be7 Corrected an error with the recently-added "exception" DRC rule.
The logical expression for determining an excepted area was wrong
for one direction, resulting in false positive DRC violations.
2026-05-15 11:04:23 -04:00
R. Timothy Edwards db224105a7 Found another couple of places where the environment variable
HOME was checked but no check was made on whether or not the
result was NULL.  This resolves issue #490 in the github issue
tracker.

Also:  Modified the way that "select visible" works to check if
labels and/or cells are rendered visible in the window.  If not,
then they are not selected.  Previously, cell instances and
labels would be selected whether or not they were visible.
This resolves issue #503 in the github issue tracker.
2026-05-13 09:46:21 -04:00
R. Timothy Edwards e5a6cf0df9 Handled the case in which environment variable HOME is not set.
See github issue tracker issue #490.  There were two cases:  When
running without the Tk console, a NULL value was being passed to a
string comparison.  When running with the Tk console, two variables
were not initialized.  Both prolems have been fixed.
2026-05-13 08:51:16 -04:00
R. Timothy Edwards 22e182f908 One additional modification to check for a space in the option
with no following text.
2026-05-13 08:38:11 -04:00
R. Timothy Edwards 0013dda92d Response to issue #513 in the github issue tracker: The command-
line parser does not recognize arguments that are both in the
same word and also space separated;  this is easy to do from, e.g.,
a python interpreter by setting an option as a single string as
in "-d null";  it is harder to do from the command line, but can
be done with quotes, as in 'magic "-d null"'.

The utils/args.c routine parses this with ArgStr(), which recognizes
two cases, one in which the option is split across two arguments,
and one in which the option is one word without space separation.

I modified ArgStr() to accept a third syntax in which the option
is in a single argument but is also space-separated.  This simply
detects a space in the third character position and moves forward
to the next non-space character and returns that position.
2026-05-13 08:30:32 -04:00
R. Timothy Edwards 85561d0503 Corrected an error with the new "tagged" operator when used in
"cifinput";  instead of cifSrTiles(), which ignores the "area"
argument and searches over the entire plane, it should have been
using cifSrTiles2(), which translates the area to CIF coordinates
and searches only over that area.  Otherwise, everything on the
same layer in a cell gets tagged, rather than just connected
material.
2026-05-12 12:06:16 -04:00
R. Timothy Edwards eb83075e16 Modified EXT_DOALL to include EXT_DOUNIQUE. Prior to a recent
change, unique node names could only be enforced by running the
command "extract unique", and there was no way to ensure that
the user would run the command as part of the extraction
recipe.  Now that it has been changed to an extraction option
instead of a separate command, and does not permanently alter
labels in the layout like the standalone command version did,
it is possible to include the setting in "extract do all",
making it run by default, and requiring the user to manually
turn it off for certain use cases (which are relatively rare).
2026-05-12 10:36:36 -04:00
R. Timothy Edwards 8fea20425e Merged PR #512 from Intubun, reworking the wasm workflow. Updated
the version to force a test of the workflow.
2026-05-12 08:57:42 -04:00
Intubun ad14c26597 ci(wasm): also publish on tag push 2026-05-12 11:19:11 +02:00
Intubun 19a2d5c57c ci(wasm): auto-publish @<owner>/magic-vlsi-wasm to GitHub Packages when VERSION changes 2026-05-12 11:13:25 +02:00
R. Timothy Edwards fb01f77755 Updated the version to go along with the merge of pull request
the repo parsing on opencircuitdesign.com, since yesterday I found
out that the tags were not updating, caused by a documentation
change in a copy of the repository that created a merge conflict
when seen by the script.  Will check tomorrow that everything ran
smoothly overnight.
2026-05-11 14:23:04 -04:00
Enno Schnackenberg 9d74f4f9a7 update main.yml 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 9af6b329de update main.yml 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 6d3494a8fe Update extract.tcl 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 068b93608a Update extract.tcl 2026-05-11 14:20:47 -04:00
Enno Schnackenberg e62626fc06 Update extract.tcl 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 145fba9ac0 Update extract.tcl 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 19113bec89 Update extract.tcl 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 480c40f516 Update extract.tcl 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 1fffc2b8b4 Update main-wasm.yml 2026-05-11 14:20:47 -04:00
Enno Schnackenberg 392d9c4408 Rework WASM CI structure 2026-05-11 14:20:47 -04:00
Enno Schnackenberg c8b50a702d Change npm examples to scmos examples 2026-05-11 14:20:47 -04:00
Intubun 354416f717 ci: add ARM WASM build job with Emscripten diagnostics to CI-aarch64 2026-05-11 14:20:47 -04:00
Intubun f802d729af Annotate ClientData callbacks: /*ARGSUSED*/ + /* UNUSED */ markers
All functions that received a ClientData (or FindRegion *) parameter in
commit fc21472e solely to satisfy WASM call_indirect signature matching
are now annotated so static analysers and casual readers can see the
intent has been verified:

  /*ARGSUSED*/          before each such function definition
  /* UNUSED */          on each unused parameter in the definition
  /* UNUSED */          on matching forward declarations

Affected: calmaWriteInitFunc, cifWriteInitFunc, cmdWindSet,
dbStampFunc, dbwElementAlways1, dbwfbWindFunc, DBWHLRedrawWind,
spcnodeHierVisit, extSDTileFunc, extTransPerimFunc,
extAnnularTileFunc, extResistorTileFunc, extDefInitFunc,
extTimesInitFunc.
2026-05-11 14:20:47 -04:00
Intubun 1d402d2c5f npm: stamp version as base-YYYYMMDD.hash on every CI build
Mirrors the AppImage versioning form (8.3.637~20260414~d157eea) as
closely as semver allows: 8.3.637-20260414.d157eea.

- package.json: placeholder 0.0.0-dev; CI always overwrites before pack
- npm-publish.yml: version step now runs unconditionally (not just on
  tags), reading VERSION + git date + git hash — dry-run artifacts are
  stamped too, making them unambiguously traceable to their source commit
2026-05-11 14:20:47 -04:00
Intubun 2d6a3a8ed6 CI: publish to GitHub Packages, emsdk latest by default, add ARM WASM job
- npm-publish.yml: target GitHub Packages instead of npm registry; no
  NPM_TOKEN needed, uses GITHUB_TOKEN
- Both workflows: emsdk defaults to 'latest' on every automated run so
  CI tracks emsdk HEAD and catches breakage early; version is overridable
  via workflow_dispatch input
- npm-publish.yml: add parallel ARM (ubuntu-24.04-arm) WASM build job
  with Emscripten diagnostics step
- main.yml, npm-publish.yml: upgrade runners from ubuntu-22.04 to
  ubuntu-latest
2026-05-11 14:20:47 -04:00
Intubun 8560027569 Add npm package and CI workflows
The user-facing layer of the WASM port: a publishable npm package
plus the GitHub Actions that build and ship it.

* npm/package.json — publishes as `magic-vlsi-wasm`, ESM-only, HPND
  licensed, version tracks Magic's own VERSION file (8.3.637).
  Whitelists the published files and exposes index.js + index.d.ts.

* npm/index.js, npm/index.d.ts — thin JS/TS wrapper around the four
  WASM exports. createMagic(opts) returns { init, runCommand,
  sourceFile, update, FS } so consumers can write into the
  Emscripten virtual filesystem and dispatch Magic commands from
  Node.js, browsers or Web Workers.

* npm/build.sh — end-to-end build: locates emsdk (via PATH or
  EMSDK_DIR), runs distclean+configure+make in the right order
  (techs before mains so embed-files are present), copies
  magic.js / magic.wasm into npm/. Optional --release, --test,
  --pack flags. Preserves configure's exec bits across invocations.

* npm/pack.sh — produces a reproducible npm tarball by touching
  every file to the build time and exporting SOURCE_DATE_EPOCH so
  pacote does not rewrite mtimes to its 1985 fallback.

* npm/examples/ — runnable smoke tests for the four common
  workflows (extract, gds, drc, cif), driven by examples/all.js.
  Each example is self-contained and uses the bundled siliwiz
  technology. helpers.js encapsulates the boilerplate.

* npm/LICENSE, npm/README.md — license text and consumer-facing
  docs (install, quick-start, API, examples, build-from-source,
  license, third-party content notice).

* .github/workflows/main.yml — adds a `simple_build_wasm` job that
  installs a pinned emsdk (3.1.56), builds the WASM module, runs
  the example test suite and uploads the npm tarball as an
  artifact. Pinned for reproducibility against the post-build.sh
  patches; switchable to "latest" by commenting two lines.

* .github/workflows/main-aarch64.yml — drops the now-redundant
  WASM ARM job. WASM is architecture-independent.

* .github/workflows/npm-publish.yml — new workflow. Publishes to
  npm on `v*` tag pushes (manual `workflow_dispatch` supported as
  a dry-run). Uses the same pinned emsdk and pack.sh.

Also sets FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 in both workflows to
silence the Node.js 20 deprecation warnings until
actions/upload-artifact@v6 ships a Node-24 release.
2026-05-11 14:20:47 -04:00
Intubun 02087ea3f8 cmwind: ARGSUSED/UNUSED annotation on CMWreposition (newScreenArea unused) 2026-05-11 14:20:47 -04:00
Intubun 8e8fada32f Add WASM entry point and Emscripten build wiring
The pieces that make Magic actually buildable as a WASM library.

* magic/magicWasm.c — new headless entry point exporting four
  functions used by the JS wrapper:
    - magic_wasm_init()           idempotent initialisation
    - magic_wasm_run_command(s)   dispatch one Magic command
    - magic_wasm_source_file(p)   execute a script from the VFS
    - magic_wasm_update()         drive a display-update cycle
  Sets CAD_ROOT=/ if unset, so embedded technology files under
  /magic/sys/ resolve correctly. Centers the command point inside
  GrScreenRect so commands route to the layout window client
  rather than the border/window-management client.

* utils/main.c, utils/main.h — split magicMain() into magicMainInit()
  + the dispatch loop. magicMainInit is idempotent (a static flag
  guards against re-initialisation) so JS callers can call any of
  the four wasm entry points first without sequencing.

* magic/Makefile — adds the WASM link target, gated by MAKE_WASM=1
  set from toolchains/emscripten/defs.mak. Conditionally compiles
  magicWasm.c into the main binary, links to magic.js and runs
  post-build.sh on the result.

* toolchains/emscripten/defs.mak — Emscripten linker flags (WASM=1,
  MODULARIZE, EXPORT_ES6, ALLOW_MEMORY_GROWTH, INITIAL_MEMORY=32M,
  STACK_SIZE=5M), the four EXPORTED_FUNCTIONS, and the embed-file
  bindings for the technology files under /magic/sys/.

* toolchains/emscripten/post-build.sh — patches Emscripten's ESM
  output so it works in pure Node.js ESM: aliases require()
  through createRequire, injects __filename / __dirname shims,
  and resyncs the ___emscripten_embedded_file_data constant from
  the wasm global section if Emscripten emitted a stale value.
  Idempotent and pinned to emsdk 3.1.56 (see WARNING in the
  header).

* toolchains/emscripten/README.md — full build documentation:
  quick-start via npm/build.sh, manual build, list of embedded
  files, exported C API, JavaScript usage example, and notes on
  CAD_ROOT, DISPLAY_SUSPEND, and the signal-API stubs.

* .gitignore — adds the WASM artefacts (magic.js, magic.wasm,
  magic.symbols), tightens the editor/OS cruft list, and keeps
  toolchains/emscripten/defs.mak tracked despite the `defs.mak`
  ignore rule.
2026-05-11 14:20:47 -04:00
Intubun f4c22438c6 Wire up headless / WASM mode at module boundaries
Glue between the null display driver and the rest of Magic so that
running with -d null does not require any process-level resources
(signals, timers, stdin, an X display, or a Tcl interpreter).

* utils/signals.c — gate setitimer, fcntl-based file watches, kill
  and the legacy sigsetmask/sigaction setup behind #ifdef
  __EMSCRIPTEN__. Every signals path becomes a no-op in WASM.
  Also fixes DBWriteBackup() being called with one argument when
  its real prototype takes three.

* windows/windDisp.c — WindUpdate() returns immediately when
  GrDisplayStatus == DISPLAY_SUSPEND. This is the runtime
  counterpart to the null driver's DISPLAY_SUSPEND state.

* extflat/EFargs.c — EFArgs() with a missing input name no longer
  jumps to "usage:" in headless WASM (which would call MainExit and
  kill the process); it sets *err_result and returns NULL so the
  caller can decide what to do. Native MAGIC_WRAPPER and native
  non-MAGIC_WRAPPER builds keep their original behavior.

* dbwind/DBWcommands.c — registers exttosim / ext2sim / exttospice /
  ext2spice in non-MAGIC_WRAPPER builds. Without this, WASM users
  could not invoke these commands at all (they were previously
  inside an #ifdef MAGIC_WRAPPER block). The C implementations
  (CmdExtToSim / CmdExtToSpice) are linked unconditionally outside
  modular builds.

* textio/txCommands.c, textio/textio.h — TxDispatchString(), a new
  library-style command entry point that parses a single string,
  dispatches it through WindSendCommand and returns a status code.
  This is what magic_wasm_run_command() calls from JavaScript.
2026-05-11 14:20:47 -04:00
Intubun fdfb54badb graphics/grNull.c: typed no-op stubs for WASM call_indirect
Magic's graphics layer routes every drawing primitive through
function pointers (GrXxxPtr / grXxxPtr) that are bound to a driver
at startup. The original null driver assigned a single 0-arg
nullDoNothing() to every pointer, which works in native builds
because of K&R loose prototype rules but fails in WASM where
call_indirect requires an exact type match between caller and
callee.

This commit:
* Adds typed no-op stubs nullDoNothingI/II/IIII/IIIIIII for
  void-returning callbacks of various arities.
* Adds nullReturnFalseI/II/III for bool-returning callbacks and
  nullReturnZeroI for int-returning callbacks.
* Casts each pointer assignment in nullSetDisplay() to the K&R
  pointer type the public header still uses, while the underlying
  function carries the correct WASM signature.
* Fills in window-management and backing-store pointers that the
  original null driver left at NULL — many of these are called
  unconditionally by WindUpdate paths, and need at least a no-op
  to avoid traps.
* Guards the stdin watch in nullSetDisplay() with #ifndef
  __EMSCRIPTEN__: WASM has no real stdin file descriptor and
  TxAdd1InputDevice() / SigWatchFile() are POSIX-specific.

Native builds are unaffected: the K&R-loose prototype machinery
still accepts the previous and the new code identically.
2026-05-11 14:20:47 -04:00
Intubun 6fc7b30386 Save and restore GrDisplayStatus across long operations
CIFGenSubcells() and extSubtree() set GrDisplayStatus =
DISPLAY_IN_PROGRESS while they run a 5-second progress timer, then
unconditionally restore DISPLAY_IDLE on exit. In native builds the
initial state is always IDLE, so this is harmless. In WASM/headless
builds the null display driver sets DISPLAY_SUSPEND at startup, and
forcing IDLE at the end of these long operations destroys the
SUSPEND guard that protects WindUpdate() from running display
callbacks against a non-existent screen.

Save the previous status before overwriting and restore it on exit.
This is also reentrant-safe: nested DISPLAY_IN_PROGRESS scopes (e.g.
extract followed by gds write) now keep the outer state intact.

* cif/CIFhier.c — CIFGenSubcells
* extract/ExtSubtree.c — extSubtree
2026-05-11 14:20:47 -04:00
Intubun 476bb5474c Add ClientData parameter to indirect-call callbacks for WASM
WASM call_indirect enforces an exact type match between the caller
and the callee. Many Magic callbacks had K&R-style () forward
declarations and a single-argument definition, but were passed to
iterators that always push a trailing ClientData argument. Native
builds tolerated the mismatch via loose prototypes; WASM traps with
"indirect call signature mismatch".

Added the missing ClientData (or, where the concrete type is known,
FindRegion *) parameter to:

* calma/CalmaRead.c, calma/CalmaWrite.c, calma/CalmaWriteZ.c —
  calmaWriteInitFunc
* cif/CIFwrite.c — cifWriteInitFunc
* commands/CmdSubrs.c — cmdWindSet
* database/DBtimestmp.c — dbStampFunc
* dbwind/DBWelement.c — dbwElementAlways1
* dbwind/DBWfdback.c — dbwfbWindFunc
* dbwind/DBWhlights.c — DBWHLRedrawWind
* ext2spice/ext2hier.c — spcnodeHierVisit
* extract/ExtBasic.c — extSDTileFunc, extTransPerimFunc,
  extAnnularTileFunc, extResistorTileFunc
* extract/ExtMain.c — extDefInitFunc
* extract/ExtTimes.c — extTimesInitFunc

Also adjusted commands/CmdE.c and commands/CmdTZ.c: SelectExpand was
being called with four arguments (the legacy surroundFlag), but its
real signature has been three arguments for years (the surround mode
is encoded in the expandType bit). The fourth argument was redundant
(DB_EXPAND_SURROUND in arg 2 is the source of truth) and rejected by
WASM. Native behavior is unchanged.

The added parameters are unused in the function bodies; they exist
only to satisfy the indirect-call signature.
2026-05-11 14:20:47 -04:00
Intubun 537d370536 Fix five latent bugs surfaced while porting to WASM
These bugs all exist in stock Magic but were tolerated by the K&R-loose
native build. The strict WASM call_indirect type checks turned them up.

* cif/CIFhier.c: ASSERT in cifFlatMaskHints accessed
  oldproprec->prop_value.prop_type, but prop_type is a top-level member
  of PropertyRecord. Changed to oldproprec->prop_type.

* extflat/EFargs.c: efLoadSearchPath was assigning a pointer to a
  string literal ("." in RO data), which callers later try to free or
  StrDup. Replaced with StrDup(path, ".") so the pointer always lives
  on the heap.

* router/rtrVia.c: rtrFollowName called RtrMilestonePrint("#"), but
  the function takes no arguments.

* sim/SimSelect.c: SimAddLabels called DBWLabelChanged with five
  arguments, but its real signature is (CellDef *, Label *, int).
  Replaced with the equivalent DBWAreaChanged call.

* windows/windView.c: extern declaration of DBMovePoint had return
  type void, but the function actually returns bool.
2026-05-11 14:20:47 -04:00
108 changed files with 5307 additions and 1582 deletions

View File

@ -1,14 +1,12 @@
# This is a basic workflow to help you get started with Actions
# CI for native ARM64 Linux build.
name: CI-aarch64
# Controls when the workflow will run
on:
push:
pull_request:
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
simple_build_linux_arm:
runs-on: ubuntu-24.04-arm
@ -23,39 +21,3 @@ jobs:
./configure
make database/database.h
make -j$(nproc)
simple_build_wasm_arm:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v4
- name: Get Dependencies
run: |
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
- name: Emscripten Diagnostic
run: |
source ./emsdk/emsdk_env.sh
echo "===== gcc -dM -E - ====="
echo | gcc -dM -E - | sort
echo "===== g++ -dM -E - ====="
echo | g++ -dM -E - | sort
echo "===== emcc -dM -E - ====="
echo | emcc -dM -E - | sort
echo "===== em++ -dM -E - ====="
echo | em++ -dM -E - | sort
- name: Build
run: |
source ./emsdk/emsdk_env.sh
# The --without and --disable in these build options is due to no WASM library being available for that feature
CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1 -g" emconfigure ./configure --without-cairo --without-opengl --without-x --without-tk --without-tcl --disable-readline --disable-compression --target=asmjs-unknown-emscripten
echo "===== defs.mak ====="
cat defs.mak
echo "===== defs.mak ====="
emmake make
- name: archive wasm bundle
uses: actions/upload-artifact@v4
with:
name: magic-wasm-bundle-arm
path: |
${{ github.workspace }}/magic/magic.wasm

160
.github/workflows/main-wasm.yml vendored Normal file
View File

@ -0,0 +1,160 @@
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.
#
# WASM is architecture-independent — built once on x86-64, usable everywhere.
on:
push:
pull_request:
workflow_dispatch:
inputs:
emsdk_version:
description: 'emsdk version to build with (default: latest; pin a version number to bisect)'
type: string
default: 'latest'
dry_run:
description: 'Dry run: pack only, do not publish even on tag pushes'
type: boolean
default: true
# actions/upload-artifact@v5 still runs on Node.js 20. Force Node 24 to
# silence the deprecation warning until upload-artifact ships a Node-24
# release. Drop this once upgraded.
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
packages: write
jobs:
build-wasm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@v5
with:
node-version: '22'
registry-url: 'https://npm.pkg.github.com'
- name: Install emsdk
env:
# Defaults to latest so CI tracks emsdk HEAD and catches breakage early.
# Override via workflow_dispatch to pin a specific version when needed
# (e.g. to bisect a regression or verify a post-build.sh patch still applies).
EMSDK_VERSION: ${{ github.event.inputs.emsdk_version || 'latest' }}
run: |
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install "$EMSDK_VERSION"
./emsdk activate "$EMSDK_VERSION"
# Dump native + emscripten preprocessor defines. Useful for diagnosing
# WASM-build differences after an emsdk bump.
- name: Emscripten Diagnostic
run: |
source ./emsdk/emsdk_env.sh
echo "===== gcc -dM -E - ====="; echo | gcc -dM -E - | sort
echo "===== g++ -dM -E - ====="; echo | g++ -dM -E - | sort
echo "===== emcc -dM -E - ====="; echo | emcc -dM -E - | sort
echo "===== em++ -dM -E - ====="; echo | em++ -dM -E - | sort
- name: Build WASM
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
- name: Copy WASM artifacts into npm/
run: |
cp magic/magic.js npm/
cp magic/magic.wasm npm/
- name: Run example tests
run: cd npm && npm run test
# Dump generated text outputs (.ext, .spice, .cif, …) into the CI log
# so a regression in extraction / netlisting / cifoutput is visible
# without having to download artifacts. The .gds output is binary —
# skip it and just record its size.
- 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
done
- name: Set package version and scope
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
- name: Pack
run: ./npm/pack.sh
- name: Upload tarball as artifact
uses: actions/upload-artifact@v5
with:
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'
run: cd npm && npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,60 +1,21 @@
# This is a basic workflow to help you get started with Actions
name: CI
# Controls when the workflow will run
on:
push:
pull_request:
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
simple_build_linux:
runs-on: ubuntu-22.04
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Get Dependencies
run: |
sudo apt-get update
sudo apt-get install -y tcl-dev tk-dev libcairo-dev
- name: Build
run: |
./configure
make database/database.h
make -j$(nproc)
simple_build_wasm:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Get Dependencies
run: |
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
- name: Emscripten Diagnostic
run: |
source ./emsdk/emsdk_env.sh
echo "===== gcc -dM -E - ====="
echo | gcc -dM -E - | sort
echo "===== g++ -dM -E - ====="
echo | g++ -dM -E - | sort
echo "===== emcc -dM -E - ====="
echo | emcc -dM -E - | sort
echo "===== em++ -dM -E - ====="
echo | em++ -dM -E - | sort
- name: Build
run: |
source ./emsdk/emsdk_env.sh
# The --without and --disable in these build options is due to no WASM library being available for that feature
CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1 -g" emconfigure ./configure --without-cairo --without-opengl --without-x --without-tk --without-tcl --disable-readline --disable-compression --target=asmjs-unknown-emscripten
echo "===== defs.mak ====="
cat defs.mak
echo "===== defs.mak ====="
emmake make
- name: archive wasm bundle
uses: actions/upload-artifact@v4
with:
name: magic-wasm-bundle
path: |
${{ github.workspace }}/magic/magic.wasm

48
.gitignore vendored
View File

@ -1,19 +1,33 @@
# Autoconf / configure outputs
defs.mak
*/Depend
!toolchains/emscripten/defs.mak
config.cache
config.log
scripts/config.log
scripts/config.status
scripts/defs.mak
.*.swp
*.o
*.so
*~
scmos/cif_template/objs/*
database/database.h
install.log
magic/proto.magicrc
make.log
reconfigure.sh
# Compiled objects / libraries
*.o
*.a
*.so
*/Depend
database/database.h
# Editor / OS cruft
.*.swp
.*.swo
*~
.DS_Store
.vscode/
.idea/
# Magic runtime-generated files
magic/proto.magicrc
scmos/cif_template/objs/*
scmos/gdsquery.tech
scmos/minimum.tech
scmos/scmos-sub.tech
@ -21,14 +35,28 @@ scmos/scmos-tm.tech
scmos/scmos.tech
scmos/scmosWR.tech
scmos/nmos.tech
# Native build artifacts
magic/magic
magic/tclmagic.dylib
tcltk/magic.sh
tcltk/magic.tcl
tcltk/magicdnull
tcltk/magicexec
tcltk/ext2spice.sh
tcltk/ext2sim.sh
magic/tclmagic.dylib
tcltk/magicdnull.dSYM/
tcltk/magicexec.dSYM/
reconfigure.sh
pfx/
# WASM build artifacts
magic/magic.js
magic/magic.js.symbols
magic/magic.symbols
magic/magic.wasm
net2ir/net2ir
net2ir/net2ir.js
net2ir/net2ir.wasm
# Generated test output
npm/examples/output/

View File

@ -1 +1 @@
8.3.640
8.3.657

View File

@ -112,7 +112,7 @@ bool CalmaUnique = FALSE; /* If TRUE, then if a cell exists in
extern bool CalmaDoLibrary; /* Also used by GDS write */
extern void calmaUnexpected(int wanted, int got);
extern int calmaWriteInitFunc(CellDef *def);
extern int calmaWriteInitFunc(CellDef *def, ClientData cdata); /* UNUSED */
/*
* Scaling.

View File

@ -96,7 +96,7 @@ typedef struct {
} calmaOutputStruct;
/* Forward declarations */
extern int calmaWriteInitFunc(CellDef *def);
extern int calmaWriteInitFunc(CellDef *def, ClientData cdata); /* UNUSED */
extern int calmaWritePaintFunc(Tile *tile, TileType dinfo, calmaOutputStruct *cos);
extern int calmaMergePaintFunc(Tile *tile, TileType dinfo, calmaOutputStruct *cos);
extern int calmaWriteUseFunc(CellUse *use, FILE *f);
@ -822,9 +822,11 @@ done:
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
calmaWriteInitFunc(
CellDef *def)
CellDef *def,
ClientData cdata) /* UNUSED */
{
def->cd_client = (ClientData) 0;
return (0);

View File

@ -96,7 +96,7 @@ extern int calmaPaintLayerNumber;
extern int calmaPaintLayerType;
/* External functions from CalmaWrite.c */
extern int calmaWriteInitFunc(CellDef *def);
extern int calmaWriteInitFunc(CellDef *def, ClientData cdata); /* UNUSED */
/* Structure used by calmaWritePaintFuncZ() and others */

View File

@ -4117,7 +4117,7 @@ cifSrTiles(
* one or more times for the planes being used in processing
* where the CIF search should be conducted over "area" scaled
* to CIF units, rather than the entire plane. Currently used
* only for operator CIFOP_INTERACT.
* only for operators CIFOP_INTERACT and CIFOP_TAGGED.
*
* Results:
* Returns the value returned by the function.
@ -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))
cifSrTiles(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:

View File

@ -284,7 +284,7 @@ cifFlatMaskHints(
oldproprec = (PropertyRecord *)DBPropGet(mhd->mh_def, name, &propfound);
if (propfound)
{
ASSERT(oldproprec->prop_value.prop_type == PROPERTY_TYPE_PLANE,
ASSERT(oldproprec->prop_type == PROPERTY_TYPE_PLANE,
"cifFlatMaskHints");
plane = oldproprec->prop_value.prop_plane;
}
@ -735,8 +735,10 @@ CIFGenSubcells(
/* This routine can take a long time, so use the display
* timer to force a 5-second progress check (like is done
* with extract)
* with extract). Save and restore GrDisplayStatus so that
* a headless (DISPLAY_SUSPEND) build isn't left in DISPLAY_IDLE.
*/
unsigned char savedDisplayStatus = GrDisplayStatus;
GrDisplayStatus = DISPLAY_IN_PROGRESS;
SigSetTimer(5); /* Print at 5-second intervals */
cuts = 0;
@ -861,7 +863,7 @@ CIFGenSubcells(
CIFHierTileOps += CIFTileOps - oldTileOps;
GrDisplayStatus = DISPLAY_IDLE;
GrDisplayStatus = savedDisplayStatus;
SigRemoveTimer();
UndoEnable();

View File

@ -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;
}

View File

@ -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:

View File

@ -44,7 +44,7 @@ static const char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magi
#include "textio/textio.h"
/* Forward declarations */
extern int cifWriteInitFunc(CellDef *def);
extern int cifWriteInitFunc(CellDef *def, ClientData cdata); /* UNUSED */
extern int cifWriteMarkFunc(CellUse *use);
extern int cifWritePaintFunc(Tile *tile, TileType dinfo, FILE *f);
extern int cifWriteLabelFunc(Tile *tile, TileType dinfo, FILE *f);
@ -204,9 +204,11 @@ CIFWrite(
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
cifWriteInitFunc(
CellDef *def)
CellDef *def,
ClientData cdata) /* UNUSED */
{
def->cd_client = (ClientData) 0;
return (0);

View File

@ -217,10 +217,11 @@ CMWdelete(
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
void
CMWreposition(
MagWindow *window,
Rect *newScreenArea,
Rect *newScreenArea, /* UNUSED */
bool final)
{
if (final)

View File

@ -905,7 +905,7 @@ CmdExpand(
case EXPAND_SELECTION:
SelectExpand(windowMask,
(doToggle) ? DB_EXPAND_TOGGLE : DB_EXPAND,
(Rect *)NULL, FALSE);
(Rect *)NULL);
break;
case EXPAND_OVERLAP:
if (doToggle)
@ -913,18 +913,18 @@ CmdExpand(
DBExpandAll(rootBoxUse, &rootRect, windowMask,
DB_EXPAND_TOGGLE | DB_EXPAND_OVERLAP,
cmdExpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_EXPAND_TOGGLE | DB_EXPAND_OVERLAP,
&rootRect, FALSE);
SelectExpand(windowMask,
DB_EXPAND_TOGGLE | DB_EXPAND_OVERLAP,
&rootRect);
}
else
{
DBExpandAll(rootBoxUse, &rootRect, windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
cmdExpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
&rootRect, FALSE);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
&rootRect);
}
break;
case EXPAND_SURROUND:
@ -933,18 +933,18 @@ CmdExpand(
DBExpandAll(rootBoxUse, &rootRect, windowMask,
DB_EXPAND_TOGGLE | DB_EXPAND_SURROUND,
cmdExpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_EXPAND_TOGGLE | DB_EXPAND_SURROUND,
&rootRect, TRUE);
SelectExpand(windowMask,
DB_EXPAND_TOGGLE | DB_EXPAND_SURROUND,
&rootRect);
}
else
{
DBExpandAll(rootBoxUse, &rootRect, windowMask,
DB_EXPAND | DB_EXPAND_SURROUND,
cmdExpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_SURROUND,
&rootRect, TRUE);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_SURROUND,
&rootRect);
}
break;
case EXPAND_ALL:
@ -953,18 +953,18 @@ CmdExpand(
DBExpandAll(rootBoxUse, &TiPlaneRect, windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
cmdExpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
(Rect *)NULL, FALSE);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
(Rect *)NULL);
}
else
{
DBExpandAll(rootBoxUse, &TiPlaneRect, windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
cmdExpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
(Rect *)NULL, FALSE);
SelectExpand(windowMask,
DB_EXPAND | DB_EXPAND_OVERLAP,
(Rect *)NULL);
}
break;
}
@ -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

View File

@ -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];

View File

@ -635,10 +635,21 @@ cmdSelectArea(
int i;
for (i = 0; i < DBNumUserLayers; i++)
{
if((TTMaskHasType(&mask, i)) && !(TTMaskHasType(&crec->dbw_visibleLayers, i)))
if ((TTMaskHasType(&mask, i)) &&
!(TTMaskHasType(&crec->dbw_visibleLayers, i)))
TTMaskClearType(&mask, i);
}
/* Remove L_CELL and L_LABEL if crec->dbw_flags indicates that
* they are not visible in the layout window.
*/
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);
}
@ -1027,7 +1038,7 @@ CmdSelect(
/*--------------------------------------------------------------------
* Select everything under the box, perhaps looking only at
* particular layers, but only if its visible.
* particular layers, but only if it's visible.
*--------------------------------------------------------------------
*/

View File

@ -1019,7 +1019,7 @@ CmdSetWindCaption(
* edit cell was selected.
*/
{
int cmdWindSet(MagWindow *window);
int cmdWindSet(MagWindow *window, ClientData clientData); /* UNUSED */
newEditDef = (newEditUse) ? newEditUse->cu_def : NULL;
newRootDef = rootDef;
@ -1053,9 +1053,11 @@ CmdSetWindCaption(
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
cmdWindSet(
MagWindow *window)
MagWindow *window,
ClientData clientData) /* UNUSED */
{
char caption[200];
CellDef *wDef;

View File

@ -777,7 +777,7 @@ CmdUnexpand(
switch (option)
{
case UNEXPAND_SELECTION:
SelectExpand(windowMask, DB_UNEXPAND, (Rect *)NULL, FALSE);
SelectExpand(windowMask, DB_UNEXPAND, (Rect *)NULL);
break;
case UNEXPAND_OVERLAP:
DBExpandAll(((CellUse *)w->w_surfaceID), &rootRect, windowMask,
@ -785,7 +785,7 @@ CmdUnexpand(
cmdUnexpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_UNEXPAND | DB_EXPAND_OVERLAP,
&rootRect, FALSE);
&rootRect);
break;
case UNEXPAND_SURROUND:
DBExpandAll(((CellUse *)w->w_surfaceID), &rootRect, windowMask,
@ -793,7 +793,7 @@ CmdUnexpand(
cmdUnexpandFunc, (ClientData)(pointertype)windowMask);
SelectExpand(windowMask,
DB_UNEXPAND | DB_EXPAND_SURROUND,
&rootRect, TRUE);
&rootRect);
break;
case UNEXPAND_ALL:
DBExpandAll(((CellUse *)w->w_surfaceID), &TiPlaneRect, windowMask,

View File

@ -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;

View File

@ -2016,8 +2016,8 @@ badTransform:
if ((cellDef->cd_file != NULL) && (cellDef->cd_file[0] == '/'))
{
char *homedir = getenv("HOME");
if (strncmp(cellDef->cd_file, homedir, strlen(homedir)) ||
*(cellDef->cd_file + strlen(homedir)) != '/')
if (homedir && (strncmp(cellDef->cd_file, homedir, strlen(homedir)) ||
*(cellDef->cd_file + strlen(homedir)) != '/'))
{
char *homeroot = strrchr(homedir, '/');
int rootlen = (int)(homeroot - homedir) + 1;
@ -2060,9 +2060,10 @@ badTransform:
if (*pathptr == '~')
{
char *homedir = getenv("HOME");
if (!strncmp(subCellDef->cd_file, homedir, strlen(homedir))
&& (!strcmp(subCellDef->cd_file + strlen(homedir),
pathptr + 1)))
if (homedir && (!strncmp(subCellDef->cd_file, homedir,
strlen(homedir)) &&
(!strcmp(subCellDef->cd_file + strlen(homedir),
pathptr + 1))))
pathOK = TRUE;
}
else if (!strcmp(cwddir, pathptr)) pathOK = TRUE;
@ -2254,9 +2255,9 @@ badTransform:
/* Check if the path is the same as the current directory */
char *homedir = getenv("HOME");
if (!strncmp(cwddir, homedir, strlen(homedir))
if (homedir && (!strncmp(cwddir, homedir, strlen(homedir))
&& (!strcmp(cwddir + strlen(homedir),
pathptr + 1)))
pathptr + 1))))
pathOK = TRUE;
}
else if (!strcmp(cwddir, pathptr)) pathOK = TRUE;

View File

@ -238,9 +238,11 @@ DBUpdateStamps(def)
}
}
/*ARGSUSED*/
int
dbStampFunc(cellDef)
dbStampFunc(cellDef, cdata)
CellDef *cellDef;
ClientData cdata; /* UNUSED */
{
CellUse *cu;
CellDef *cd;

View File

@ -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 */

View File

@ -149,6 +149,9 @@ extern void CmdAutoExtToSpice();
#else
extern void CmdExtToSpice();
#endif
#else /* !MAGIC_WRAPPER */
extern void CmdExtToSim();
extern void CmdExtToSpice();
#endif
/*
@ -568,8 +571,26 @@ DBWInitCommands()
"ext2spice [args] convert extracted file(s) to a SPICE format file;"
" type\n\t\t\t\"ext2spice help\" for information on options",
CmdExtToSpice, FALSE);
#endif /* EXT2SPICE_AUTO */
#endif /* MAGIC_WRAPPER */
#endif /* EXT2SPICE_AUTO */
#else /* !MAGIC_WRAPPER */
/* In non-Tcl builds (e.g. WASM), register the C implementations directly */
WindAddCommand(DBWclientID,
"exttosim [args] convert extracted file(s) to a sim format file;"
" type\n\t\t\t\"exttosim help\" for information on options",
CmdExtToSim, FALSE);
WindAddCommand(DBWclientID,
"ext2sim [args] convert extracted file(s) to a sim format file;"
" type\n\t\t\t\"ext2sim help\" for information on options",
CmdExtToSim, FALSE);
WindAddCommand(DBWclientID,
"exttospice [args] convert extracted file(s) to a SPICE format file;"
" type\n\t\t\t\"exttospice help\" for information on options",
CmdExtToSpice, FALSE);
WindAddCommand(DBWclientID,
"ext2spice [args] convert extracted file(s) to a SPICE format file;"
" type\n\t\t\t\"ext2spice help\" for information on options",
CmdExtToSpice, FALSE);
#endif /* MAGIC_WRAPPER */
#ifdef USE_READLINE

View File

@ -867,8 +867,11 @@ dbwelemGetTransform(use, transform, cdarg)
return 1;
}
/*ARGSUSED*/
int
dbwElementAlways1()
dbwElementAlways1(w, clientData)
MagWindow *w; /* UNUSED */
ClientData clientData; /* UNUSED */
{
return 1;
}

View File

@ -531,8 +531,11 @@ dbwfbGetTransform(use, transform, cdarg)
* cell.
*/
/*ARGSUSED*/
int
dbwfbWindFunc()
dbwfbWindFunc(w, clientData)
MagWindow *w; /* UNUSED */
ClientData clientData; /* UNUSED */
{
return 1;
}

View File

@ -357,9 +357,11 @@ DBWHLRedrawPrepWindow(MagWindow *window, Rect *area)
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
DBWHLRedrawWind(window)
DBWHLRedrawWind(window, clientData)
MagWindow *window; /* Window in which to redraw highlights. */
ClientData clientData; /* UNUSED */
{
int i;
DBWclientRec *crec;

View File

@ -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

View File

@ -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>

View File

@ -830,7 +830,7 @@ drcTile (tile, dinfo, arg)
* an exception area. Exception rules are ignored if
* the edge is outside an exception area.
*/
if (!isinside && (!(cptr->drcc_exception & DRC_EXCEPTION_MASK) == 0))
if (!isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) == 0))
continue;
if (isinside && ((cptr->drcc_exception & DRC_EXCEPTION_MASK) != 0))
continue;

View File

@ -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;
@ -1624,12 +1624,14 @@ spcsubHierVisit(
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
spcnodeHierVisit(
HierContext *hc,
EFNode *node,
int res,
double cap)
double cap,
ClientData cdata) /* UNUSED */
{
HierName *hierName;
bool isConnected = FALSE;
@ -1656,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;
@ -1666,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)
{

View File

@ -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)
{

View File

@ -307,6 +307,12 @@ EFArgs(argc, argv, err_result, argsProc, cdata)
if (inname == NULL)
#ifdef MAGIC_WRAPPER
return NULL;
#elif defined(EMSCRIPTEN)
{
/* Headless WASM: signal error via err_result, no goto usage path */
if (err_result != NULL) *err_result = TRUE;
return NULL;
}
#else
goto usage;
#endif
@ -367,7 +373,7 @@ efLoadSearchPath(path)
PaVisitFiles(DOT_MAGIC_PATH, ".magicrc", pv);
PaVisitFree(pv);
if (*path == NULL)
*path = ".";
StrDup(path, ".");
}
int

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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();

View File

@ -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,28 @@ 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 "resis" 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++;
if (argstart + 4 >= argc)
efReadError("Bad terminal description for Short device\n");
else
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 +362,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 +396,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 +472,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],

View File

@ -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},

View File

@ -147,13 +147,13 @@ NodeRegion *temp_subsnode = NULL; /* Last subsnode found */
/* Forward declarations */
void extOutputNodes();
int extTransTileFunc();
int extTransPerimFunc();
int extTransPerimFunc(Boundary *, ClientData); /* UNUSED */
int extTransFindSubs();
int extTransFindId();
void extTermAPFunc();
int extAnnularTileFunc();
int extResistorTileFunc();
int extAnnularTileFunc(Tile *, TileType, int, FindRegion *); /* UNUSED */
int extResistorTileFunc(Tile *, TileType, int, FindRegion *); /* UNUSED */
int extSpecialPerimFunc();
void extFindDuplicateLabels();
@ -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
{
@ -1110,7 +1120,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 +1165,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);
}
/*
@ -2185,11 +2198,13 @@ extDevFindParamMatch(devptr, length, width)
*
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
extSDTileFunc(tile, dinfo, pNum)
extSDTileFunc(tile, dinfo, pNum, arg)
Tile *tile;
TileType dinfo; /* (unused) */
TileType dinfo; /* UNUSED */
int pNum;
FindRegion *arg; /* UNUSED */
{
LinkedTile *newdevtile;
@ -2650,30 +2665,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 ");
@ -3947,9 +3938,11 @@ extTermAPFunc(tile, dinfo, eapd)
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
extTransPerimFunc(bp)
extTransPerimFunc(bp, cdata)
Boundary *bp;
ClientData cdata; /* UNUSED */
{
TileType tinside, toutside, dinfo;
Tile *tile;
@ -4225,11 +4218,13 @@ extTransPerimFunc(bp)
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
extAnnularTileFunc(tile, dinfo, pNum)
extAnnularTileFunc(tile, dinfo, pNum, arg)
Tile *tile;
TileType dinfo;
int pNum;
FindRegion *arg; /* UNUSED */
{
TileTypeBitMask mask;
TileType loctype;
@ -4275,11 +4270,13 @@ extAnnularTileFunc(tile, dinfo, pNum)
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
extResistorTileFunc(tile, dinfo, pNum)
extResistorTileFunc(tile, dinfo, pNum, arg)
Tile *tile;
TileType dinfo;
int pNum;
FindRegion *arg; /* UNUSED */
{
TileTypeBitMask mask;
TileType loctype;

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -85,7 +85,7 @@ typedef struct _linkedDef {
Stack *extDefStack;
/* Forward declarations */
int extDefInitFunc();
int extDefInitFunc(CellDef *, ClientData); /* UNUSED */
void extDefPush();
void extDefIncremental();
void extParents();
@ -395,9 +395,11 @@ ExtAll(rootUse)
* cell defs, in preparation for extracting a subtree
* rooted at a particular def.
*/
/*ARGSUSED*/
int
extDefInitFunc(def)
extDefInitFunc(def, cdata)
CellDef *def;
ClientData cdata; /* UNUSED */
{
def->cd_client = (ClientData) 0;
return (0);
@ -471,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);

View File

@ -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 */
@ -174,8 +176,10 @@ extSubtree(parentUse, reg, f)
int cuts, totcuts;
float pdone, plast;
SearchContext scx;
int savedDisplayStatus;
/* Use the display timer to force a 5-second progress check */
savedDisplayStatus = GrDisplayStatus;
GrDisplayStatus = DISPLAY_IN_PROGRESS;
SigSetTimer(5); /* Print at 5-second intervals */
@ -348,7 +352,7 @@ done:
/* Output connections and node adjustments */
extOutputConns(&ha.ha_connHash, f);
HashKill(&ha.ha_connHash);
GrDisplayStatus = DISPLAY_IDLE;
GrDisplayStatus = savedDisplayStatus;
SigRemoveTimer();
/* Clear the CU_SUB_EXTRACTED flag from all children instances */

View File

@ -288,9 +288,11 @@ ExtTimes(rootUse, f)
* ----------------------------------------------------------------------------
*/
/*ARGSUSED*/
int
extTimesInitFunc(use)
extTimesInitFunc(use, cdata)
CellUse *use;
ClientData cdata; /* UNUSED */
{
CellDef *def = use->cu_def;
struct cellStats *cs;

View File

@ -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,

View File

@ -71,11 +71,12 @@ extern const char * const extDevTable[];
#define EXT_DORESISTANCE 0x008 /* Extract lumped resistance */
#define EXT_DOLENGTH 0x010 /* Extract pathlengths */
#define EXT_DOFRINGEHALO 0x020 /* Distributed fringe capacitance */
#define EXT_DOALL 0x03f /* ALL OF THE ABOVE */
#define EXT_DOUNIQUE 0x100 /* Force unique nodes during extraction */
#define EXT_DOALL 0x13f /* ALL OF THE ABOVE */
#define EXT_DOLABELCHECK 0x040 /* Check for connections by label */
#define EXT_DOALIASES 0x080 /* Output all node aliases */
#define EXT_DOUNIQUE 0x100 /* Force unique nodes during extraction */
#define EXT_DOEXTRESIST 0x200 /* Do full R-C extraction */
#define EXT_DOUNIQNOTOPPORTS 0x400 /* Ignore top cell ports w/EXT_DOUNIQUE */
extern int ExtOptions; /* Bitmask of above */
extern 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();

View File

@ -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

View File

@ -61,6 +61,74 @@ nullDoNothing()
{
}
/*
* Typed no-op stubs for WASM call_indirect type compatibility.
* WASM enforces exact type signatures at indirect call sites; assigning
* a 0-arg nullDoNothing to a pointer called with arguments causes a
* "null function or function signature mismatch" trap. These stubs
* have the correct arity so the WASM type check passes.
*/
/* 1-argument stub (int or pointer) */
static void
nullDoNothingI(int a)
{
(void) a;
}
/* 2-argument stub */
static void
nullDoNothingII(int a, int b)
{
(void) a; (void) b;
}
/* 4-argument stub */
static void
nullDoNothingIIII(int a, int b, int c, int d)
{
(void) a; (void) b; (void) c; (void) d;
}
/* 7-argument stub (for grFontTextPtr) */
static void
nullDoNothingIIIIIII(int a, int b, int c, int d, int e, int f, int g)
{
(void) a; (void) b; (void) c; (void) d; (void) e; (void) f; (void) g;
}
/* bool-returning stubs — return FALSE so callers treat backing store / window
* creation as unavailable, which is correct for the headless null driver. */
static bool
nullReturnFalseI(int a)
{
(void) a;
return FALSE;
}
static bool
nullReturnFalseII(int a, int b)
{
(void) a; (void) b;
return FALSE;
}
/* 3-argument bool-returning stub (for grDrawGridPtr) */
static bool
nullReturnFalseIII(int a, int b, int c)
{
(void) a; (void) b; (void) c;
return FALSE;
}
/* 1-argument int-returning stub (for GrWindowIdPtr) */
static int
nullReturnZeroI(int a)
{
(void) a;
return 0;
}
/*
*---------------------------------------------------------
*
@ -246,8 +314,10 @@ nullSetDisplay(dispType, outFileName, mouseFileName)
{
TxPrintf("Using NULL graphics device.\n");
#ifndef __EMSCRIPTEN__
TxAdd1InputDevice(fileno(stdin), nullStdin, (ClientData) NULL);
if (TxStdinIsatty) SigWatchFile(fileno(stdin), "stdin");
#endif
/* Set up the procedure values in the indirection table. */
@ -259,25 +329,49 @@ nullSetDisplay(dispType, outFileName, mouseFileName)
GrEnableTabletPtr = nullDoNothing;
GrDisableTabletPtr = nullDoNothing;
GrSetCursorPtr = nullDoNothing;
GrSetCursorPtr = (void (*)()) nullDoNothingI;
GrTextSizePtr = NullTextSize;
GrDrawGlyphPtr = nullDoNothing;
GrDrawGlyphPtr = (void (*)()) nullDoNothingII;
GrBitBltPtr = NullBitBlt;
GrReadPixelPtr = nullReturnZero;
GrFlushPtr = nullDoNothing;
/* Window management — null driver has no real windows; return FALSE so
* callers know the operation wasn't performed. */
GrCreateWindowPtr = NULL; /* headless: skip OS window creation, WindCreate stays OK */
GrDeleteWindowPtr = (void (*)()) nullDoNothingI;
GrConfigureWindowPtr = (void (*)()) nullDoNothingI;
GrOverWindowPtr = (void (*)()) nullDoNothingI;
GrUnderWindowPtr = (void (*)()) nullDoNothingI;
GrDamagedPtr = nullDoNothing;
GrUpdateIconPtr = (void (*)()) nullDoNothingII;
GrEventPendingPtr = (bool (*)()) nullReturnFalse;
GrWindowIdPtr = (int (*)()) nullReturnZeroI;
GrWindowNamePtr = NULL; /* protected by callers */
GrGetCursorPosPtr = (bool (*)()) nullReturnFalseII;
GrGetCursorRootPosPtr = (bool (*)()) nullReturnFalseII;
/* Backing store — not available in headless mode */
GrGetBackingStorePtr = (bool (*)()) nullReturnFalseII;
GrScrollBackingStorePtr = (bool (*)()) nullReturnFalseII;
GrPutBackingStorePtr = (void (*)()) nullDoNothingII;
GrFreeBackingStorePtr = (void (*)()) nullDoNothingI;
GrCreateBackingStorePtr = (void (*)()) nullDoNothingI;
/* local indirections */
grSetSPatternPtr = nullDoNothing;
grPutTextPtr = nullDoNothing;
grFontTextPtr = nullDoNothing;
grDefineCursorPtr = nullDoNothing;
grDrawGridPtr = nullReturnFalse;
grDrawLinePtr = nullDoNothing;
grSetWMandCPtr = nullDoNothing;
grFillRectPtr = nullDoNothing;
grSetStipplePtr = nullDoNothing;
grSetLineStylePtr = nullDoNothing;
grSetCharSizePtr = nullDoNothing;
grSetSPatternPtr = (void (*)()) nullDoNothingII;
grPutTextPtr = (void (*)()) nullDoNothingIIII;
grFontTextPtr = (void (*)()) nullDoNothingIIIIIII;
grDefineCursorPtr = (void (*)()) nullDoNothingI;
grFreeCursorPtr = (void (*)()) nullDoNothingI;
grDrawGridPtr = (bool (*)()) nullReturnFalseIII;
grDrawLinePtr = (void (*)()) nullDoNothingIIII;
grSetWMandCPtr = (void (*)()) nullDoNothingII;
grFillRectPtr = (void (*)()) nullDoNothingI;
grFillPolygonPtr = (void (*)()) nullDoNothingII;
grSetStipplePtr = (void (*)()) nullDoNothingI;
grSetLineStylePtr = (void (*)()) nullDoNothingI;
grSetCharSizePtr = (void (*)()) nullDoNothingI;
GrScreenRect.r_xtop = 511;
GrScreenRect.r_ytop = 483;

View File

@ -4,10 +4,14 @@
MODULE = magic
MAGICDIR = ..
SRCS = magicTop.c
include ${MAGICDIR}/defs.mak
SRCS = magicTop.c
ifeq (${MAKE_WASM},1)
SRCS += magicWasm.c
endif
EXTRA_LIBS = ${MAGICDIR}/bplane/libbplane.o \
${MAGICDIR}/cmwind/libcmwind.o \
${MAGICDIR}/commands/libcommands.o \
@ -37,7 +41,18 @@ LIBS += ${GR_LIBS} ${READLINE_LIBS} -lm ${LD_EXTRA_LIBS} \
${OA_LIBS} ${ZLIB_FLAG} ${TOP_EXTRA_LIBS}
CLEANS += tclmagic${SHDLIB_EXT} libtclmagic${SHDLIB_EXT}.a proto.magicrc
ifeq (${MAKE_WASM},1)
magic: magic.js
magic.js: lib${MODULE}.o ${EXTRA_LIBS}
@echo --- building main magic WASM
${RM} magic.js magic.wasm
${CC} ${CFLAGS} ${CPPFLAGS} ${DFLAGS} lib${MODULE}.o ${EXTRA_LIBS} -o magic.js ${LIBS}
endif
main: magic proto.magicrc
ifeq (${MAKE_WASM},1)
@bash ${MAGICDIR}/toolchains/emscripten/post-build.sh magic.js magic.wasm
endif
tcl-main: tclmagic${SHDLIB_EXT} proto.magicrc

122
magic/magicWasm.c Normal file
View File

@ -0,0 +1,122 @@
/*
* magicWasm.c --
*
* Headless Emscripten entry points for running Magic without a
* terminal-driven event loop.
*/
#include <stdio.h>
#include <stdlib.h>
#include "utils/main.h"
#include "utils/magic.h"
#include "utils/paths.h"
#include "textio/textio.h"
#include "textio/txcommands.h"
#include "utils/utils.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#ifdef __EMSCRIPTEN__
#include <emscripten/emscripten.h>
#else
#define EMSCRIPTEN_KEEPALIVE
#endif
static int
magicWasmEnsureCadRoot(void)
{
if (getenv("CAD_ROOT") == NULL)
{
if (setenv("CAD_ROOT", "/", 0) != 0)
{
TxError("Failed to set CAD_ROOT for the WASM runtime.\n");
return -1;
}
}
return 0;
}
EMSCRIPTEN_KEEPALIVE int
magic_wasm_init(void)
{
static char *argv[] = {
"magic",
"-d",
"null",
"-T",
"minimum",
NULL
};
if (magicWasmEnsureCadRoot() != 0)
return -1;
return magicMainInit(5, argv);
}
EMSCRIPTEN_KEEPALIVE int
magic_wasm_run_command(const char *command)
{
int status;
status = magic_wasm_init();
if (status != 0)
return status;
if ((command == NULL) || (*command == '\0'))
return 0;
/* Set the current point to the center of the screen so that
* WindSendCommand routes the command to the layout window client
* (not the window-management border client which handles point 0,0).
*/
TxSetPoint(GrScreenRect.r_xtop / 2, GrScreenRect.r_ytop / 2,
WIND_UNKNOWN_WINDOW);
return TxDispatchString(command, FALSE);
}
EMSCRIPTEN_KEEPALIVE int
magic_wasm_source_file(const char *path)
{
FILE *f;
int status;
status = magic_wasm_init();
if (status != 0)
return status;
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);
return -1;
}
/* Set the current point to the centre of the screen so that
* WindSendCommand routes all commands from the file to the layout
* window client, just as magic_wasm_run_command does for single
* commands. Without this, commands arrive with point (0,0) and
* end up in the border/windClient context where most commands are
* unknown.
*/
TxSetPoint(GrScreenRect.r_xtop / 2, GrScreenRect.r_ytop / 2,
WIND_UNKNOWN_WINDOW);
TxDispatch(f);
fclose(f);
return 0;
}
EMSCRIPTEN_KEEPALIVE void
magic_wasm_update(void)
{
if (magic_wasm_init() == 0)
WindUpdate();
}

View File

@ -26,17 +26,19 @@ StipplePattern begin
/FontMatrix [1 0 0 1 0 0] def
/FontBBox [0 0 1 1] def
/Encoding 256 array def
/PattName (P0) def
/tmpStr 1 string def
/PattName (P000) def
/tmpStr 3 string def
/NoPatt {<00>} def
0 1 255 { Encoding exch /NoPatt put } for
/BuildChar {
1 0 0 0 1 1 setcachedevice exch begin Encoding exch get load
64 64 true [64 0 0 64 0 0] 5 -1 roll imagemask end } def
end
/dp { StipplePattern begin dup 30 tmpStr cvrs PattName exch 1 exch
putinterval PattName cvn dup Encoding exch 4 -1 roll exch put exch
store end } def
/dp { StipplePattern begin
dup 10 tmpStr cvrs /num exch def
PattName 1 num putinterval
PattName 0 num length 1 add getinterval cvn
dup Encoding exch 4 -1 roll exch put exch store end } def
/sf { findfont exch scalefont setfont } bind def
/sp { patterns setfont 2 setlinewidth } def
/lb { gsave translate 0 0 moveto /just exch def gsave dup true charpath
@ -68,4 +70,3 @@ end
x y moveto w y lineto w h lineto x h lineto closepath clip bx } def
/tb {1 sub 3 1 roll gsave newpath moveto {lineto} repeat closepath clip pathbbox
/h exch def /w exch def /y exch def /x exch def bx } def

6
npm/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
magic.js
magic.wasm
*.tgz
node_modules/
package-lock.json
examples/output/

10
npm/LICENSE Normal file
View File

@ -0,0 +1,10 @@
Copyright (C) 1985, 1990 Regents of the University of California.
Permission to use, copy, modify, and distribute this
software and its documentation for any purpose and without
fee is hereby granted, provided that the above copyright
notice appear in all copies. The University of California
makes no representations about the suitability of this
software for any purpose. It is provided "as is" without
express or implied warranty. Export of this software outside
of the United States of America may require an export license.

153
npm/README.md Normal file
View File

@ -0,0 +1,153 @@
# magic-vlsi-wasm
[Magic VLSI](http://opencircuitdesign.com/magic/) layout tool, compiled to
WebAssembly as a headless library. Runs in Node.js, browsers, and Web Workers
— no X11, no Tk, no native dependencies.
Use it to programmatically read and write `.mag`, `.gds`, `.cif`, `.ext`, and
SPICE netlists; run DRC; extract parasitics — anywhere JavaScript runs.
## Install
```bash
npm install magic-vlsi-wasm
```
Requires Node.js 18 or newer.
## Quick start
```js
import createMagic from 'magic-vlsi-wasm';
const { runCommand, FS } = await createMagic();
// Drop a layout into Magic's virtual filesystem
FS.mkdirTree('/work');
FS.writeFile('/work/inv.mag', layoutBytes);
// Run Magic commands — scmos is built into the WASM binary, no tech file needed
runCommand('tech load scmos');
runCommand('load /work/inv');
runCommand('gds write /work/inv');
// Read the result back out
const gdsBytes = FS.readFile('/work/inv.gds');
```
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
the VFS at `/magic/sys/current/<name>.tech` before calling `tech load <name>`.
## API
```ts
createMagic(options?): Promise<MagicInstance>
```
`options` is forwarded to the underlying Emscripten module. Useful keys:
| Key | Default | Purpose |
|--------------|------------------|---------|
| `wasmBinary` | fetched lazily | Pre-fetched ArrayBuffer of `magic.wasm` (skips a network round-trip in browsers) |
| `print` | `console.log` | Callback for each stdout line |
| `printErr` | `console.error` | Callback for each stderr line |
The returned `MagicInstance` exposes:
| Method | Description |
|---------------------------|-------------|
| `runCommand(cmd: string)` | Dispatch a single Magic command. Returns 0 on success. |
| `sourceFile(path: string)` | Execute a script from the virtual filesystem. Returns 0 on success, -1 if the file could not be opened. |
| `init()` | Force initialization. Idempotent — `runCommand` and `sourceFile` call it for you. |
| `update()` | Drive a display-update cycle. No-op in this headless build. |
| `FS` | Emscripten virtual filesystem. See the [Emscripten docs](https://emscripten.org/docs/api_reference/Filesystem-API.html). |
Full TypeScript types ship in [`index.d.ts`](index.d.ts).
### Low-level access
`createMagic()` is a thin convenience wrapper over the underlying Emscripten
module. If you need direct access — for example to call `cwrap` yourself or
to drive `magic_wasm_init` manually — import the module factory directly:
```js
import createMagicModule from 'magic-vlsi-wasm/magic.js';
const module = await createMagicModule({ wasmBinary, print, printErr });
module._magic_wasm_init();
const run = module.cwrap('magic_wasm_run_command', 'number', ['string']);
run('tech load scmos');
```
The bundled examples use this lower-level path together with a small helper
class ([`examples/helpers.js`](examples/helpers.js)) that adds a
`runScript(text)` convenience method — it splits a multi-line Tcl block,
strips comments, and dispatches each line via `runCommand`. Useful when you
have a script as a string rather than as a file in the VFS.
## Examples
The package ships runnable examples for the most common workflows. After
installing, run one directly:
```bash
node node_modules/magic-vlsi-wasm/examples/extract.js
node node_modules/magic-vlsi-wasm/examples/gds.js
node node_modules/magic-vlsi-wasm/examples/drc.js
node node_modules/magic-vlsi-wasm/examples/cif.js
```
Or, when developing inside this repo:
```bash
npm test # full suite (extract, gds, drc, cif)
npm run example # extract.js — RC extraction + SPICE netlist
npm run test:gds # GDS write only
npm run test:drc # DRC check only
npm run test:cif # CIF write only
```
Each example loads the bundled [`min.mag`](examples/min.mag) (a small NPN
transistor cell from Magic's own scmos test suite) under the built-in `scmos`
technology — no external tech file required. See [`examples/`](examples/) for
the source; [`example.js`](examples/example.js) is the simplest entry point
(GDS → CIF conversion in ~40 lines).
## Build from source
If you want to rebuild the WASM module yourself, see
[`toolchains/emscripten/README.md`](../toolchains/emscripten/README.md).
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
```
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`).
## 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.
## License
[HPND](LICENSE) — Copyright (C) 1985, 1990 Regents of the University of California.
### Bundled test layout
The example layout [`examples/min.mag`](examples/min.mag) is taken from
Magic's own scmos test suite ([`scmos/examples/bipolar/min.mag`](../scmos/examples/bipolar/min.mag))
and is included here as a runnable smoke test for the WASM build. The `scmos`
technology it targets is compiled into the WASM binary, so no external tech
file is shipped.

132
npm/build.sh Executable file
View File

@ -0,0 +1,132 @@
#!/usr/bin/env bash
# Build Magic WASM and copy artifacts into this npm/ directory.
#
# Usage:
# npm/build.sh [--release] [--test] [--pack]
#
# --release Omit debug symbols (-g).
# --test Run `npm run test` after copying artifacts.
# --pack Run `npm pack` after copying artifacts (and tests, if given).
#
# Requirements (must be on PATH or set via env vars before running):
# emcc / emmake / emconfigure — Emscripten compiler tools
# make, gcc — standard build tools
# 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).
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
OPT_RELEASE=0
OPT_TEST=0
OPT_PACK=0
for arg in "$@"; do
case "$arg" in
--release) OPT_RELEASE=1 ;;
--test) OPT_TEST=1 ;;
--pack) OPT_PACK=1 ;;
*) echo "Unknown option: $arg" >&2; exit 1 ;;
esac
done
# --- locate emscripten -------------------------------------------------------
if [ -n "${EMSDK_DIR:-}" ]; then
if [ ! -f "$EMSDK_DIR/emsdk_env.sh" ]; then
echo "Error: EMSDK_DIR is set to '$EMSDK_DIR' but emsdk_env.sh was not found there." >&2
exit 1
fi
# shellcheck source=/dev/null
source "$EMSDK_DIR/emsdk_env.sh"
else
if ! command -v emcc &>/dev/null; then
echo "Error: emcc not found on PATH and EMSDK_DIR is not set." >&2
echo " Either source emsdk_env.sh before running this script," >&2
echo " or set EMSDK_DIR to your emsdk checkout directory." >&2
exit 1
fi
fi
echo "Using emcc: $(command -v emcc)"
emcc --version | head -1
# --- portability helpers -----------------------------------------------------
# CPU count: Linux has nproc, macOS has sysctl, fall back to getconf.
ncpu() {
if command -v nproc &>/dev/null; then
nproc
elif [ "$(uname)" = "Darwin" ]; then
sysctl -n hw.ncpu
else
getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1
fi
}
# Portable in-place sed (BSD sed on macOS disagrees with GNU on -i).
# Uses redirect-back instead of mv so the file's mode bits are preserved
# (configure must stay executable across build.sh invocations).
sed_strip_cr() {
local file=$1 tmp
tmp=$(mktemp)
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"
else
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
cat toolchains/emscripten/defs.mak >> defs.mak
# --- build -------------------------------------------------------------------
emmake make depend
emmake make -j"$(ncpu)" modules libs
emmake make techs
emmake make mains
# --- copy artifacts ----------------------------------------------------------
cp magic/magic.js "$SCRIPT_DIR/"
cp magic/magic.wasm "$SCRIPT_DIR/"
echo "Copied magic.js and magic.wasm to npm/"
# --- optional test -----------------------------------------------------------
# Runs the same smoke test that CI runs (see .github/workflows/main.yml).
if [ $OPT_TEST -eq 1 ]; then
cd "$SCRIPT_DIR"
npm run test
fi
# --- optional pack -----------------------------------------------------------
if [ $OPT_PACK -eq 1 ]; then
"$SCRIPT_DIR/pack.sh"
echo "npm package tarball created in npm/"
fi

50
npm/examples/all.js Normal file
View File

@ -0,0 +1,50 @@
// all.js — Run all Magic WASM example tests and print a summary.
//
// Usage: node examples/all.js
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';
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}`);
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' : ''}` : '';
default: return '';
}
}
console.log('\nMagic WASM — test suite\n');
const suite = [
['extract', runExtract],
['gds', runGds],
['drc', runDrc],
['cif', runCif],
];
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);

30
npm/examples/cif.js Normal file
View File

@ -0,0 +1,30 @@
// cif.js — Export layout to CIF (Caltech Intermediate Form).
//
// Usage: node examples/cif.js [magFile [tech [outputDir]]]
import { createMagic, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.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', 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 => { console.error(e.message ?? e); process.exit(1); });
console.log(`\ncif: ${outPath} (${bytes} bytes)`);
console.log('Done.');
}

4
npm/examples/cif.tcl Normal file
View File

@ -0,0 +1,4 @@
# CIF export
tech load __TECH__
load /work/__CELL__
cif write /work/__CELL__

31
npm/examples/drc.js Normal file
View File

@ -0,0 +1,31 @@
// drc.js — Run design rule checking and report violations.
//
// Usage: node examples/drc.js [magFile [tech]]
import { createMagic, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG } from './helpers.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]', msg); },
onPrintErr: msg => { output.push(msg); console.error('[magic]', msg); },
});
const { FS } = magic;
const { tech: techName, cell } = loadCell(FS, tech, magFile);
magic.runScript(loadScript('drc.tcl', techName, cell));
// Magic prints "Total DRC errors found: N" at the end of drc listall.
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 => { console.error(e.message ?? e); process.exit(1); });
console.log(`\nDRC violations: ${violations ?? '(count not found in output)'}`);
console.log('Done.');
}

5
npm/examples/drc.tcl Normal file
View File

@ -0,0 +1,5 @@
# DRC check runs design rule checking and reports violation count.
tech load __TECH__
load /work/__CELL__
drc catchup
drc count total

40
npm/examples/example.js Normal file
View File

@ -0,0 +1,40 @@
// example.js — GDS → CIF format conversion using the Magic WASM module.
//
// Usage:
// node examples/example.js [input.gds] [output.cif]
//
// Defaults:
// input: design.gds (in current working directory)
// output: design.cif (in current working directory)
import { readFileSync, writeFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, resolve, basename } from 'node:path';
import createMagicModule from '../magic.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
const wasmBinary = readFileSync(resolve(__dirname, '../magic.wasm'));
const inputGds = process.argv[2] ?? 'design.gds';
const outputCif = process.argv[3] ?? inputGds.replace(/\.gds$/i, '.cif');
const cellName = basename(inputGds, '.gds');
const module = await createMagicModule({
wasmBinary,
print: msg => console.log('[magic]', msg),
printErr: msg => console.error('[magic]', msg),
});
module.FS.mkdirTree('/work');
module.FS.writeFile(`/work/${cellName}.gds`, readFileSync(inputGds));
module._magic_wasm_init();
module.cwrap('magic_wasm_run_command', 'number', ['string'])(`gds read /work/${cellName}`);
module.cwrap('magic_wasm_run_command', 'number', ['string'])(`load ${cellName}`);
module.cwrap('magic_wasm_run_command', 'number', ['string'])(`cif write /work/${cellName}`);
const cifBytes = module.FS.readFile(`/work/${cellName}.cif`);
writeFileSync(outputCif, cifBytes);
console.log(`Converted ${inputGds}${outputCif}`);

41
npm/examples/extract.js Normal file
View File

@ -0,0 +1,41 @@
// extract.js — RC extraction example (extract → extresist → ext2spice).
//
// Usage: node examples/extract.js [magFile [tech [outputDir]]]
import { createMagic, vfsWrite, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.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', 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 => { console.error(e.message ?? e); process.exit(1); });
console.log(`\next: ${ext}`);
if (spice) console.log(`spice: ${spice}`);
console.log('Done.');
}

36
npm/examples/extract.tcl Normal file
View File

@ -0,0 +1,36 @@
# extract.tcl
#
# Complete Magic extraction workflow:
# 1. Load technology
# 2. Load layout
# 3. Extract parasitic capacitances (extract all __CELL__.ext)
# 4. Extract parasitic resistances (extresist all __CELL__.res.ext)
# 5. Write SPICE netlist (ext2spice __CELL__.spice)
#
# __TECH__ and __CELL__ are substituted by extract.js before execution.
tech load __TECH__
load /work/__CELL__
# Write all intermediate files to /work/
extract path /work
# Enable resistance extraction
extract do resistance
# Generate __CELL__.ext
extract all
# extresist requires a valid selection/box
select top cell
# Generate __CELL__.res.ext
extresist all
# SPICE generation settings
ext2spice format ngspice
ext2spice extresist on
ext2spice cthresh 0
# Generate __CELL__.spice
ext2spice /work/__CELL__

30
npm/examples/gds.js Normal file
View File

@ -0,0 +1,30 @@
// gds.js — Export layout to GDS II stream format.
//
// Usage: node examples/gds.js [magFile [tech [outputDir]]]
import { createMagic, vfsRead, loadCell, loadScript,
DEFAULT_TECH, DEFAULT_MAG, DEFAULT_OUT } from './helpers.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', 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 => { console.error(e.message ?? e); process.exit(1); });
console.log(`\ngds: ${outPath} (${bytes} bytes)`);
console.log('Done.');
}

4
npm/examples/gds.tcl Normal file
View File

@ -0,0 +1,4 @@
# GDS export
tech load __TECH__
load /work/__CELL__
gds write /work/__CELL__

72
npm/examples/helpers.js Normal file
View File

@ -0,0 +1,72 @@
// Shared utilities for Magic WASM examples.
import createMagicModule from '../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 DEFAULT_TECH = 'scmos';
export const DEFAULT_MAG = resolve(EXAMPLES_DIR, 'min.mag');
export const DEFAULT_OUT = resolve(EXAMPLES_DIR, 'output');
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.FS = mod.FS;
}
init() {
const rc = this._init();
if (rc !== 0) throw new Error(`magic_wasm_init failed with code ${rc}`);
}
runScript(text) {
for (const line of text.split('\n')) {
const l = line.trim();
if (l && !l.startsWith('#')) this._run(l);
}
}
}
// 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.
export async function createMagic({ onPrint, onPrintErr } = {}) {
const lines = [];
const mod = await createMagicModule({
wasmBinary,
print: onPrint ?? (msg => { lines.push(msg); console.log('[magic]', msg); }),
printErr: onPrintErr ?? (msg => { lines.push(msg); console.error('[magic]', msg); }),
});
const magic = new MagicWasm(mod);
magic.init();
return { magic, lines };
}
// Loads a layout into the VFS. `tech` is the name of a technology that is
// either built into the WASM binary (e.g. 'scmos', 'minimum', 'nmos') or has
// already been written to /magic/sys/current/<tech>.tech by the caller.
export function loadCell(FS, tech, magFile) {
const cell = basename(magFile, '.mag');
vfsWrite(FS, `/work/${cell}.mag`, magFile);
return { tech, cell };
}
// Reads a TCL script from EXAMPLES_DIR, substitutes __TECH__ and __CELL__.
export function loadScript(name, tech, cell) {
return readFileSync(resolve(EXAMPLES_DIR, name), 'utf8')
.replaceAll('__TECH__', tech)
.replaceAll('__CELL__', cell);
}

33
npm/examples/min.mag Normal file
View File

@ -0,0 +1,33 @@
magic
tech scmos
timestamp 783737348
<< nwell >>
rect 5 3 46 27
<< metal1 >>
rect 9 0 13 13
rect 22 0 26 13
rect 33 0 37 13
<< collector >>
rect 8 17 14 18
rect 8 13 9 17
rect 13 13 14 17
rect 8 12 14 13
<< pbase >>
rect 18 17 40 21
rect 18 13 22 17
rect 26 13 33 17
rect 37 13 40 17
rect 18 9 40 13
<< collectorcontact >>
rect 9 13 13 17
<< emittercontact >>
rect 22 13 26 17
<< pbasecontact >>
rect 33 13 37 17
<< labels >>
rlabel space 0 17 0 17 3 without
rlabel space 0 13 0 13 3 guardring
rlabel metal1 35 1 35 1 5 base
rlabel metal1 24 1 24 1 5 emitter
rlabel metal1 11 1 11 1 5 collector
<< end >>

57
npm/index.d.ts vendored Normal file
View File

@ -0,0 +1,57 @@
export interface MagicInstance {
/**
* Initialize Magic (idempotent safe to call multiple times).
* Returns 0 on success, non-zero on failure.
*/
init(): number;
/**
* Dispatch a single Magic command string.
* Calls init() automatically on the first invocation.
* Returns 0 on success, non-zero on failure.
*/
runCommand(command: string): number;
/**
* Read and execute a command file at the given virtual filesystem path.
* Calls init() automatically on the first invocation.
* Returns 0 on success, -1 if the file could not be opened.
*/
sourceFile(path: string): number;
/**
* Drive a display-update cycle.
* No-op in headless builds the null display driver suspends all redraws.
*/
update(): void;
/**
* Emscripten virtual filesystem.
* Use FS.writeFile / FS.readFile to pass layout files in and out of Magic.
* See https://emscripten.org/docs/api_reference/Filesystem-API.html
*/
FS: any;
}
/**
* Create a Magic WASM instance.
*
* @param options Emscripten module options. Useful keys:
* - `wasmBinary` Pre-fetched ArrayBuffer of magic.wasm (avoids a second fetch)
* - `print` Callback for stdout lines (default: console.log)
* - `printErr` Callback for stderr lines (default: console.error)
*
* @example
* ```js
* import createMagic from 'magic-vlsi-wasm';
*
* const { runCommand, FS } = await createMagic();
* FS.writeFile('/work/inv.mag', layoutBytes);
* runCommand('tech load sky130A');
* runCommand('load /work/inv');
* runCommand('gds write /work/inv');
* const gds = FS.readFile('/work/inv.gds');
* ```
*/
export default function createMagic(options?: Record<string, unknown>): Promise<MagicInstance>;
export { createMagic };

15
npm/index.js Normal file
View File

@ -0,0 +1,15 @@
import MagicModuleFactory from './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 };
}
export { createMagic };
export default createMagic;

23
npm/pack.sh Executable file
View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Pack the magic-vlsi-wasm npm tarball with reproducible-but-current timestamps.
#
# npm/pacote normalizes file mtimes for reproducibility and, when no
# SOURCE_DATE_EPOCH is set, falls back to 1985-10-26. That makes published
# tarballs carry 1985 dates, which confuses users and tooling.
#
# This script:
# 1. touches every file in npm/ so mtimes reflect the build time
# 2. sets SOURCE_DATE_EPOCH to `now` so pacote's normalization uses it
# 3. runs `npm pack`, producing magic-vlsi-wasm-<version>.tgz in npm/
#
# Used by:
# - npm/build.sh --pack (local build)
# - .github/workflows/main-wasm.yml (CI build + tag-triggered publish)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
find . -exec touch {} +
SOURCE_DATE_EPOCH=$(date +%s) npm pack

36
npm/package.json Normal file
View File

@ -0,0 +1,36 @@
{
"name": "magic-vlsi-wasm",
"version": "0.0.0-dev",
"description": "Magic VLSI Layout Tool — headless WebAssembly build",
"type": "module",
"main": "index.js",
"types": "index.d.ts",
"exports": {
".": {
"import": "./index.js",
"types": "./index.d.ts"
}
},
"files": [
"index.js",
"index.d.ts",
"magic.js",
"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"
},
"keywords": ["magic", "vlsi", "eda", "wasm", "webassembly", "layout"],
"license": "HPND",
"engines": {
"node": ">=18"
}
}

View File

@ -1218,7 +1218,7 @@ PlotVersatec(scx, layers, xMask, user_scale)
/* Compute the name of the file to use for output, and open it. */
sprintf(fileName, "%s/magicPlotXXXXXX", PlotTempDirectory);
snprintf(fileName, 200, "%s/magicPlotXXXXXX", PlotTempDirectory);
result = mkstemp(fileName);
if (result == -1)
{
@ -1474,7 +1474,7 @@ PlotVersatec(scx, layers, xMask, user_scale)
TxPrintf("\n");
fclose(file);
sprintf(command, PlotVersCommand, PlotVersPrinter, fileName);
snprintf(command, 300, PlotVersCommand, PlotVersPrinter, fileName);
if (system(command) != 0)
{
TxError("Couldn't execute spooler command to print \"%s\"\n",

View File

@ -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,13 +64,30 @@ 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);
}
/*
* Structure used by ResEachTile for the callback to ResMultiPlaneFunc()
* to pass a pointer to the tile being processed, and the terminal being
* searched.
*/
typedef struct tile_and_term
{
Tile *tat_tile;
int tat_term;
} TileAndTerm;
/*
*--------------------------------------------------------------------------
*
@ -128,12 +106,13 @@ resAllPortNodes(tile, list)
*/
int
ResMultiPlaneFunc(tile, dinfo, tpptr)
ResMultiPlaneFunc(tile, dinfo, tat)
Tile *tile;
TileType dinfo; /* Not used, but needs to be handled */
Tile **tpptr;
TileAndTerm *tat;
{
Tile *tp = *tpptr;
Tile *tp = tat->tat_tile;
int term = tat->tat_term;
int xj, yj;
/* Simplified split tile handling---Ignore the right side of
@ -146,7 +125,7 @@ ResMultiPlaneFunc(tile, dinfo, tpptr)
xj = (LEFT(tile) + RIGHT(tile)) / 2;
yj = (TOP(tile) + BOTTOM(tile)) / 2;
ResNewSDDevice(tp, tile, xj, yj, OTHERPLANE, &ResNodeQueue);
ResNewTermDevice(tp, tile, term, xj, yj, OTHERPLANE, &ResNodeQueue);
return 0;
}
@ -195,7 +174,44 @@ ResSubstrateFunc(tile, dinfo, tpptr)
/*
*--------------------------------------------------------------------------
*
* ResEachTile--for each tile, make a list of all possible current sources/
* ResStartTile --
*
* For the tile at the starting point of the net, create an initial
* resNode entry.
*
* Results:
* None.
*
* Side Effects:
* creates a node.
*
*
*--------------------------------------------------------------------------
*/
void
ResStartTile(tile, x, y)
Tile *tile;
int x, y;
{
resNode *resptr;
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;
}
/*
*--------------------------------------------------------------------------
*
* ResEachTile --
*
* 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 +230,8 @@ ResSubstrateFunc(tile, dinfo, tpptr)
#define IGNORE_BOTTOM 8
bool
ResEachTile(tile, startpoint)
ResEachTile(tile)
Tile *tile;
Point *startpoint;
{
Tile *tp;
resNode *resptr;
@ -226,14 +240,12 @@ 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;
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 +270,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 +293,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 +322,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 +337,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 +355,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 +370,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 +403,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,29 +436,30 @@ 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, ...) */
if (TTMaskHasType(&ResSDTypesBitMask, t1))
if (TTMaskHasType(&ResTermTypesBitMask, t1))
{
Rect r;
int pNum;
TileTypeBitMask devMask;
TileAndTerm tat;
TiToRect(tile, &r);
@ -470,20 +467,29 @@ ResEachTile(tile, startpoint)
{
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;
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);
{
for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
{
/* Check if any type in the terminal type list exists on this
* plane before calling the search function.
*/
TileTypeBitMask termtypes;
DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pNum],
&r, &devMask, ResMultiPlaneFunc, (ClientData)&tile);
TTMaskAndMask3(&termtypes, &devptr->exts_deviceSDTypes[i],
&DBPlaneTypes[pNum]);
if (TTMaskIsZero(&termtypes))
{
tat.tat_tile = tile;
tat.tat_term = i;
DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pNum],
&r, &devptr->exts_deviceSDTypes[i],
ResMultiPlaneFunc, (ClientData)&tat);
}
}
}
}
}
@ -519,7 +525,7 @@ ResEachTile(tile, startpoint)
tstructs->ri_status |= RES_TILE_DONE;
resAllPortNodes(tile, &ResNodeQueue);
resMakePortBreakpoints(tile, &ResNodeQueue);
merged = ResCalcTileResistance(tile, tstructs, &ResNodeQueue,
&ResNodeList);

View File

@ -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)
{

View File

@ -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);

View File

@ -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))

View File

@ -29,26 +29,31 @@ 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, xj, yj, direction;
resNode **PendingList;
{
resNode *resptr;
resNode *resptr = NULL;
resDevice *resDev;
tElement *tcell;
int newnode;
@ -64,7 +69,9 @@ 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? */
if ((((ri->sourceEdge & direction) != 0) && (resDev->rd_nterms == 4))
|| (resDev->rd_nterms > 2))
{
if (resDev->rd_fet_source == (resNode *) NULL)
{
@ -73,11 +80,9 @@ ResNewSDDevice(tile, tp, xj, yj, direction, PendingList)
resDev->rd_fet_source = resptr;
}
else
{
resptr = resDev->rd_fet_source;
}
}
else
else if (resDev->rd_nterms > 3)
{
if (resDev->rd_fet_drain == (resNode *) NULL)
{
@ -86,9 +91,7 @@ ResNewSDDevice(tile, tp, xj, yj, direction, PendingList)
resDev->rd_fet_drain = resptr;
}
else
{
resptr = resDev->rd_fet_drain;
}
}
if (newnode)
{
@ -99,7 +102,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 +135,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 +155,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 +213,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);
}

View File

@ -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__".
*
@ -148,51 +147,86 @@ void
ResDissolveContacts(contacts)
ResContactPoint *contacts;
{
TileType t, oldtype;
TileType t, conttype;
Tile *tp;
TileTypeBitMask residues;
TileType residue[NP][NT];
int pNum;
bzero((char *)residue, NP * NT * sizeof(TileType));
for (; contacts != (ResContactPoint *)NULL; contacts = contacts->cp_nextcontact)
{
oldtype=contacts->cp_type;
conttype = contacts->cp_type;
#ifdef PARANOID
if (oldtype == TT_SPACE)
TxError("Error in Contact Dissolving for %s \n",ResCurrentNode);
if (conttype == TT_SPACE)
TxError("Error in Contact Dissolving for %s \n", ResCurrentNode);
#endif
DBFullResidueMask(oldtype, &residues);
DBErase(ResUse->cu_def, &(contacts->cp_rect), oldtype);
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
if (TTMaskHasType(&residues, t))
DBPaint(ResUse->cu_def, &(contacts->cp_rect), t);
/* Fill in details of the residue types for each contact type.
* This is done only once per contact type. This could be refined
* further by temporarily changing the paint table directly or
* creating a separate paint table which erases contact cuts and
* replaces them with the residues.
*/
tp = PlaneGetHint(ResDef->cd_planes[DBPlane(contacts->cp_type)]);
GOTOPOINT(tp, &(contacts->cp_rect.r_ll));
if (residue[DBPlane(conttype)][conttype] == TT_SPACE)
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
residue[pNum][conttype] = DBPlaneToResidue(conttype, pNum);
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
if (DBTypeOnPlane(conttype, pNum))
{
DBPaintPlane(ResUse->cu_def->cd_planes[pNum], &(contacts->cp_rect),
DBStdEraseTbl(conttype, pNum), (PaintUndoInfo *)NULL);
DBPaintPlane(ResUse->cu_def->cd_planes[pNum], &(contacts->cp_rect),
DBStdPaintTbl(residue[pNum][conttype], pNum),
(PaintUndoInfo *)NULL);
}
}
#ifdef PARANOID
if (TiGetTypeExact(tp) == contacts->cp_type)
tp = PlaneGetHint(ResDef->cd_planes[DBPlane(conttype)]);
GOTOPOINT(tp, &(contacts->cp_rect.r_ll));
if (TiGetTypeExact(tp) == conttype)
TxError("Error in Contact Preprocess Routines\n");
#endif
}
}
/* Structure used by ResMakeDriverSinkPorts() to pass information to
* ResAddPortFunc(). Contains a reference to a node, so that the
* link between the tile and the node can be maintained, and the
* driver or sink, which has the information about the position and
* tile type of the connection.
*/
typedef struct driversinkdata {
ResExtNode *dsd_node;
ResConnect *dsd_connect;
} DriverSinkData;
/*
*---------------------------------------------------------------------------
*
* 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;
@ -201,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)
@ -235,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))
{
@ -249,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)
{
@ -293,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 */
@ -332,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;
@ -367,24 +549,38 @@ 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;
}
/*
*---------------------------------------------------------------------------
*
* ResFindNewContactTiles --
*
* Dissolving contacts eliminated the tiles that contacts->nextcontact
* pointed to. This procedure finds the tile now under center and sets
* that tile's ti_client field to point to the contact. The old value
* of clientdata is set to nextTilecontact.
*
* Results: none
*
* Side Effects: dissolving contacts eliminated the tiles that
* contacts->nextcontact pointed to. This procedure finds the tile now under
* center and sets that tile's ti_client field to point to the contact. The
* old value of clientdata is set to nextTilecontact.
* Side Effects: modifies information in the contact records.
*
*----------------------------------------------------------------------------
*/
@ -396,29 +592,40 @@ ResFindNewContactTiles(contacts)
int pNum;
Tile *tile;
TileTypeBitMask mask;
TileType lastType = TT_SPACE;
for (; contacts != (ResContactPoint *) NULL; contacts = contacts->cp_nextcontact)
{
DBFullResidueMask(contacts->cp_type, &mask);
/* Watch for types that connect to the substrate plane or well; */
/* e.g., psubstratepdiff connects to nwell but not through a */
/* contact. */
if (ExtCurStyle->exts_globSubstratePlane != -1)
/* Avoid re-running the following code for the same contact type */
if (contacts->cp_type != lastType)
{
TileTypeBitMask cMask;
TTMaskAndMask3(&cMask, &DBConnectTbl[contacts->cp_type],
&DBPlaneTypes[ExtCurStyle->exts_globSubstratePlane]);
lastType = contacts->cp_type;
DBFullResidueMask(contacts->cp_type, &mask);
if (!TTMaskIsZero(&cMask))
TTMaskSetMask(&mask, &cMask);
/* Watch for types that connect to the substrate plane or well; */
/* e.g., psubstratepdiff connects to nwell but not through a */
/* contact. */
if (ExtCurStyle->exts_globSubstratePlane != -1)
{
TileTypeBitMask cMask;
TTMaskAndMask3(&cMask, &DBConnectTbl[contacts->cp_type],
&DBPlaneTypes[ExtCurStyle->exts_globSubstratePlane]);
if (!TTMaskIsZero(&cMask))
TTMaskSetMask(&mask, &cMask);
}
}
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
if (!DBTypeOnPlane(contacts->cp_type, pNum) &&
(pNum != ExtCurStyle->exts_globSubstratePlane))
continue;
tile = PlaneGetHint(ResDef->cd_planes[pNum]);
GOTOPOINT(tile, &(contacts->cp_center));
PlaneSetHint(ResDef->cd_planes[pNum], tile);
#ifdef PARANOID
if (tile == (Tile *) NULL)
{
@ -476,10 +683,11 @@ ResFindNewContactTiles(contacts)
/*
*--------------------------------------------------------------------------
*
* ResProcessTiles--Calls ResEachTile with processed tiles belonging to
* nodes in ResNodeQueue. When all the tiles corresponding
* to a node have been processed, the node is moved to
* ResNodeList.
* ResProcessTiles --
*
* Calls ResEachTile with processed tiles belonging to nodes in ResNodeQueue.
* When all the tiles corresponding to a node have been processed, the node
* is moved to ResNodeList.
*
* Results: Return 1 if any error occurred, 0 otherwise.
*
@ -490,8 +698,8 @@ ResFindNewContactTiles(contacts)
int
ResProcessTiles(resisdata, origin)
Point *origin;
ResisData *resisdata;
Point *origin;
{
Tile *startTile;
@ -509,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);
}
#ifdef PARANOID
else
@ -546,7 +755,7 @@ ResProcessTiles(resisdata, origin)
if ((ri->ri_status & RES_TILE_DONE) == 0)
{
resCurrentNode = resptr2;
merged |= ResEachTile(tile, (Point *)NULL);
merged |= ResEachTile(tile);
}
}
rj->rj_status = TRUE;
@ -572,7 +781,7 @@ ResProcessTiles(resisdata, origin)
if (cp->cp_cnode[tilenum] == resptr2)
{
resCurrentNode = resptr2;
merged |= ResEachTile(tile, (Point *)NULL);
merged |= ResEachTile(tile);
}
else
{
@ -655,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));
@ -669,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));
@ -1054,15 +1263,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
{
@ -1085,13 +1301,40 @@ 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;
@ -1123,13 +1366,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);
@ -1139,8 +1382,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 */
@ -1176,27 +1417,38 @@ 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. */
@ -1381,6 +1633,7 @@ FindStartTile(resisdata, SourcePoint)
{
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
GOTOPOINT(tile, &workingPoint);
PlaneSetHint(ResUse->cu_def->cd_planes[pnum], tile);
SourcePoint->p_x = workingPoint.p_x;
SourcePoint->p_y = workingPoint.p_y;
@ -1395,13 +1648,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);
}
@ -1414,6 +1667,7 @@ FindStartTile(resisdata, SourcePoint)
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
GOTOPOINT(tile, &workingPoint);
PlaneSetHint(ResUse->cu_def->cd_planes[pnum], tile);
if (IsSplit(tile))
{
@ -1457,7 +1711,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);
}
@ -1500,7 +1754,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);
}
@ -1556,7 +1810,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))
{
@ -1624,7 +1878,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))
{
@ -1765,6 +2019,7 @@ ResGetDevice(pt, type)
tile = PlaneGetHint(ResUse->cu_def->cd_planes[pnum]);
GOTOPOINT(tile, &workingPoint);
PlaneSetHint(ResUse->cu_def->cd_planes[pnum], tile);
const ClientData ticlient = TiGetClient(tile);
if (IsSplit(tile))

View File

@ -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 */
@ -125,7 +124,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
resNode **pendingList, **doneList;
resResistor **resList;
{
int height;
int count, height;
bool merged;
TileType ttype;
Breakpoint *p1, *p2, *p3;
@ -134,6 +133,8 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
resNode *currNode;
float rArea;
resInfo *info = (resInfo *)TiGetClientPTR(tile);
HashTable BreakTable;
HashEntry *he;
merged = FALSE;
height = TOP(tile) - BOTTOM(tile);
@ -163,21 +164,38 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
ttype = TiGetTypeExact(tile);
/* Re-sort nodes left to right. */
count = ResSortBreaks(&info->breakList, TRUE);
ResSortBreaks(&info->breakList, TRUE);
/* For long lists (defined as >= 16 entries), make a hash table of
* the node pointer conversions so that each node can be updated
* as we walk the list, instead of walking the rest of the list in
* a nested loop for each entry.
*/
if (count >= 16)
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
/*
* Eliminate breakpoints with the same X coordinate and merge
* their nodes.
*/
p2= info->breakList;
p2 = info->breakList;
/* Add extra left area to leftmost node */
p2->br_this->rn_float.rn_area += height * (p2->br_loc.p_x - LEFT(tile));
while (p2->br_next != NULL)
{
/* Has the node been recorded as needing to be replaced? */
if (count >= 16)
{
while (TRUE)
{
he = HashLookOnly(&BreakTable, (char *)p2->br_this);
if (!he) break;
p2->br_this = (resNode *)HashGetValue(he);
}
}
p1 = p2;
p2 = p2->br_next;
if (p2->br_loc.p_x == p1->br_loc.p_x)
@ -215,25 +233,38 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
/*
* Was the node used in another info or breakpoint?
* If so, replace the old node with the new one.
*
* Short lists: Walk the list to the end and change
* nodes on the fly.
* Long lists: Record the change to be made in the
* hash table so that it can be executed as each list
* entry is encountered.
*/
p3 = p2->br_next;
while (p3 != NULL)
if (count >= 16)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
p3 = p3->br_next;
he = HashFind(&BreakTable, (char *)currNode);
HashSetValue(he, (char *)p2->br_this);
}
}
else
{
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
/*
* If the X coordinates don't match, make a resistor between
* the breakpoints.
*/
p3 = p3->br_next;
}
}
}
else
{
/*
* If the X coordinates don't match, make a resistor between
* the breakpoints.
*/
else
{
resistor = (resResistor *)mallocMagic((unsigned)sizeof(resResistor));
resistor->rr_nextResistor = (*resList);
resistor->rr_lastResistor = NULL;
@ -276,6 +307,8 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
}
}
if (count >= 16) HashKill(&BreakTable);
p2->br_this->rn_float.rn_area += height * (RIGHT(tile) - p2->br_loc.p_x);
freeMagic((char *)p2);
info->breakList = NULL;
@ -301,7 +334,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
resNode **pendingList, **doneList;
resResistor **resList;
{
int width;
int count, width;
bool merged;
TileType ttype;
Breakpoint *p1, *p2, *p3;
@ -310,6 +343,8 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
resNode *currNode;
float rArea;
resInfo *info = (resInfo *)TiGetClientPTR(tile);
HashTable BreakTable;
HashEntry *he;
merged = FALSE;
width = RIGHT(tile) - LEFT(tile);
@ -329,7 +364,15 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
}
/* Re-sort nodes south to north. */
ResSortBreaks(&info->breakList, FALSE);
count = ResSortBreaks(&info->breakList, FALSE);
/* For long lists (defined as >= 16 entries), make a hash table of
* the node pointer conversions so that each node can be updated
* as we walk the list, instead of walking the rest of the list in
* a nested loop for each entry.
*/
if (count >= 16)
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
/* Simplified split tile handling */
if (IsSplit(tile))
@ -353,51 +396,75 @@ 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)
{
/* Has the node been recorded as needing to be replaced? */
if (count >= 16)
{
while (TRUE)
{
he = HashLookOnly(&BreakTable, (char *)p2->br_this);
if (!he) break;
p2->br_this = (resNode *)HashGetValue(he);
}
}
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);
}
/*
* Was the node used in another info or breakpoint?
* If so, replace the old node with the new one.
*
* Short lists: Walk the list to the end and change
* nodes on the fly.
* Long lists: Record the change to be made in the
* hash table so that it can be executed as each list
* entry is encountered.
*/
p3 = p2->br_next;
while (p3 != NULL)
if (count >= 16)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
he = HashFind(&BreakTable, (char *)currNode);
HashSetValue(he, (char *)p2->br_this);
}
else
{
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
p3 = p3->br_next;
p3 = p3->br_next;
}
}
}
@ -491,7 +558,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;
@ -525,7 +592,7 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
(devedge & TOPEDGE) == devedge ||
(devedge & BOTTOMEDGE) == devedge)
{
ResSortBreaks(&info->breakList,TRUE);
ResSortBreaks(&info->breakList, TRUE);
p2 = NULL;
for (p1 = info->breakList; p1 != NULL; p1 = p1->br_next)
{
@ -620,7 +687,7 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
}
else
{
deltay=0;
deltay = 0;
}
}
else
@ -634,15 +701,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
@ -820,7 +887,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);
}
}
@ -869,7 +936,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 */
@ -919,21 +986,207 @@ ResDoContacts(contact, nodes, resList)
/*
*-------------------------------------------------------------------------
*
* ResSortBreaks --
* BreakCompare --
*
* Helper routine for MergeSortBreaks() (below). Simple
* comparison of the breakpoint position. Comparison is
* done for the X position if "xsort" is TRUE, and the Y
* position if "xsort" is FALSE.
*
* Return value:
* Return -1 if the (x or y) position of a is less than the
* (x or y) position of b; return +1 if the position of a is
* greater than the position of b; and return 0 if they have
* equal positions.
*
* Side effect:
* None.
*
*-------------------------------------------------------------------------
*/
int
BreakCompare(
Breakpoint *a,
Breakpoint *b,
int xsort)
{
if (xsort == TRUE)
{
if (a->br_loc.p_x < b->br_loc.p_x) return -1;
if (a->br_loc.p_x > b->br_loc.p_x) return 1;
}
else
{
if (a->br_loc.p_y < b->br_loc.p_y) return -1;
if (a->br_loc.p_y > b->br_loc.p_y) return 1;
}
return 0;
}
/*
*-------------------------------------------------------------------------
*
* MergeSorted --
*
* Helper routine for MergeSortBreaks() (below). Merge sort
* merging routine.
*
*-------------------------------------------------------------------------
*/
Breakpoint *
MergeSorted(
Breakpoint *a,
Breakpoint *b,
int xsort)
{
Breakpoint head;
Breakpoint *tail = &head;
head.br_next = NULL;
while (a != NULL && b != NULL)
{
if (BreakCompare(a, b, xsort) <= 0)
{
tail->br_next = a;
a = a->br_next;
}
else
{
tail->br_next = b;
b = b->br_next;
}
tail = tail->br_next;
}
tail->br_next = (a != NULL) ? a : b;
return head.br_next;
}
/*
*-------------------------------------------------------------------------
*
* SplitList --
*
* Helper routine for MergeSortBreaks() (below). Merge sort
* splitting routine.
*
* Results:
* None
* None.
*
*-------------------------------------------------------------------------
*/
void
SplitList(
Breakpoint *source,
Breakpoint **front,
Breakpoint **back)
{
Breakpoint *slow;
Breakpoint *fast;
if (source == NULL || source->br_next == NULL)
{
*front = source;
*back = NULL;
return;
}
slow = source;
fast = source->br_next;
while (fast != NULL)
{
fast = fast->br_next;
if (fast != NULL)
{
slow = slow->br_next;
fast = fast->br_next;
}
}
*front = source;
*back = slow->br_next;
slow->br_next = NULL;
}
/*
*-------------------------------------------------------------------------
*
* MergeSortBreaks --
*
* See "ResSortBreaks" below. Alternative to bubble sort for long
* linked lists.
*
* Results:
* Pointer to a sorted breakpoint list.
*
* Side effects:
* The breakpoints are sorted.
*
*-------------------------------------------------------------------------
*/
Breakpoint *
MergeSortBreaks(Breakpoint *list, int xsort)
{
Breakpoint *a, *b;
if (list == NULL || list->br_next == NULL)
return list;
SplitList(list, &a, &b);
a = MergeSortBreaks(a, xsort);
b = MergeSortBreaks(b, xsort);
return MergeSorted(a, b, xsort);
}
/*
*-------------------------------------------------------------------------
*
* ResSortBreaks --
*
* Sort breakpoints, either in the X direction (if "xsort" is TRUE)
* or in the Y direction (if "xsort" is FALSE). For short lists
* (< 16 elements), a simple bubble sort is used. For larger lists,
* a merge sort is used. Most resistor networks are short, but
* power/ground networks can be huge and cause a performance
* bottleneck.
*
* Results:
* Return the length of the list (maximum truncated at 16) so that
* the calling routine can determine if this is a long or a short
* linked list and treat it accordingly.
*
*-------------------------------------------------------------------------
*/
int
ResSortBreaks(masterlist, xsort)
Breakpoint **masterlist;
int xsort;
{
Breakpoint *p1, *p2, *p3, *p4;
bool changed;
int count = 0;
for (p1 = *masterlist; p1; p1 = p1->br_next)
{
count++;
if (count > 16)
{
*masterlist = MergeSortBreaks(*masterlist, xsort);
return count;
}
}
/* Simple bubble sort */
changed = TRUE;
while (changed == TRUE)
@ -970,5 +1223,6 @@ ResSortBreaks(masterlist, xsort)
}
}
}
return count;
}

View File

@ -26,7 +26,6 @@ extern void ResEliminateResistor();
extern void ResCleanNode();
extern void ResFixBreakPoint();
/*
*-------------------------------------------------------------------------
*
@ -211,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.
*
@ -406,13 +405,70 @@ ResSeriesCheck(resptr)
int
ResParallelCheck(resptr)
resNode *resptr;
{
resResistor *r1,*r2;
resNode *resptr2,*resptr3;
resResistor *r1, *r2;
resNode *resptr2, *resptr3;
int status = UNTOUCHED;
resElement *rcell1, *rcell2;
int rcount = 0;
/* When the number of resistors gets to be large enough, it is more efficient to
* sort the resistor list and then do a single pass to see if any two consecutive
* items in the sorted list can be merged, than to do a double loop through the
* resistor list at ~O(N^2).
*/
for (rcell1 = resptr->rn_re; rcell1 != NULL; rcell1 = rcell1->re_nextEl)
{
rcount++;
if (rcount >= 10) break;
}
if (rcount >= 10)
{
HashTable NodeResTable;
HashEntry *he;
/* Hash the connections */
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl)
{
/* One connection is always resptr; find the other one */
resptr3 = rcell2->re_thisEl->rr_connection1;
if (resptr3 == resptr) resptr3 = rcell2->re_thisEl->rr_connection2;
he = HashFind(&NodeResTable, (char *)resptr3);
if ((rcell1 = (resElement*)HashGetValue(he)))
{
r1 = rcell1->re_thisEl;
r2 = rcell2->re_thisEl;
if (TTMaskHasType(ResNoMergeMask+r1->rr_tt, r2->rr_tt)) continue;
ResFixParallel(r1, r2);
status = PARALLEL;
resptr2 = NULL;
if (resptr3->rn_status & RES_TRUE)
{
resptr2 = resptr3;
resptr2->rn_status &= ~RES_TRUE;
}
ResDoneWithNode(resptr);
if (resptr2 != NULL) ResDoneWithNode(resptr2);
break;
}
else
HashSetValue(he, (char *)rcell2);
}
HashKill(&NodeResTable);
return status;
}
/* This does the same thing as above, but for a small number of resistors
* per node, it avoids the overhead of creating and destroying hash tables.
*/
for (rcell1 = resptr->rn_re; rcell1->re_nextEl != NULL;
rcell1 = rcell1->re_nextEl)
@ -447,6 +503,7 @@ ResParallelCheck(resptr)
}
if (status == PARALLEL) break;
}
return status;
}
@ -473,6 +530,179 @@ ResTriangleCheck(resptr)
float r1, r2, r3, denom;
resNode *n1, *n2, *n3;
resElement *rcell1, *rcell2, *rcell3, *element;
int rcount = 0;
/* When the size of the linked list of resistors is long, it is faster to
* hash the neighboring connections and then find the first entry in the
* neighbor's node list that is another neighbor.
*/
for (rcell1 = resptr->rn_re; rcell1 != NULL; rcell1 = rcell1->re_nextEl)
{
rcount++;
if (rcount >= 10) break;
}
if (rcount >= 10)
{
HashTable NodeResTable;
HashEntry *he, *he2;
HashSearch hs;
/* Hash the neighboring connections */
HashInit(&NodeResTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
for (rcell2 = resptr->rn_re; rcell2 != NULL; rcell2 = rcell2->re_nextEl)
{
rr2 = rcell2->re_thisEl;
/* One connection is always resptr; find the other one */
n2 = rr2->rr_connection1;
if (n2 == resptr) n2 = rr2->rr_connection2;
he = HashFind(&NodeResTable, (char *)n2);
if (!(rcell1 = (resElement *)HashGetValue(he)))
HashSetValue(he, (char *)rcell2);
}
HashStartSearch(&hs);
while ((he = HashNext(&NodeResTable, &hs)))
{
/* Get each node that neighbors resptr */
n1 = (resNode *)he->h_key.h_ptr;
rcell1 = (resElement *)HashGetValue(he);
rr1 = rcell1->re_thisEl;
/* Check the list of resistors of neighbor n1 for any resistor whose
* other end is also a neighbor of resptr.
*/
for (rcell3 = n1->rn_re; rcell3 != NULL; rcell3 = rcell3->re_nextEl)
{
rr3 = rcell3->re_thisEl;
/* Resistor can't be merged */
if (TTMaskHasType(ResNoMergeMask + rr1->rr_tt, rr3->rr_tt))
continue;
/* One connection is always n1; find the other one */
n2 = rr3->rr_connection1;
if (n2 == n1) n2 = rr3->rr_connection2;
he2 = HashLookOnly(&NodeResTable, (char *)n2);
if (he2)
{
/* Found a triangle */
rcell2 = (resElement *)HashGetValue(he2);
rr2 = rcell2->re_thisEl;
/* . . . But it can't be merged */
if (TTMaskHasType(ResNoMergeMask + rr1->rr_tt, rr2->rr_tt))
continue;
if (TTMaskHasType(ResNoMergeMask + rr2->rr_tt, rr3->rr_tt))
continue;
status = TRIANGLE;
if ((denom = rr1->rr_value + rr2->rr_value + rr3->rr_value) != 0.0)
{
denom = 1.0 /denom;
/* calculate new values for resistors */
r1 = (((float)rr1->rr_value) * ((float)rr2->rr_value)) * denom;
r2 = (((float)rr2->rr_value) * ((float)rr3->rr_value)) * denom;
r3 = (((float)rr1->rr_value) * ((float)rr3->rr_value)) * denom;
rr1->rr_value = r1 + 0.5;
rr2->rr_value = r2 + 0.5;
rr3->rr_value = r3 + 0.5;
ASSERT(rr1->rr_value >= 0, "Triangle");
ASSERT(rr2->rr_value >= 0, "Triangle");
ASSERT(rr3->rr_value >= 0, "Triangle");
}
else
{
rr1->rr_value = 0;
rr2->rr_value = 0;
rr3->rr_value = 0;
}
n3 = (resNode *)mallocMagic((unsigned)(sizeof(resNode)));
/* Where should the new node be put? It */
/* is arbitrarily assigned to the location */
/* occupied by the first node. */
InitializeResNode(n3, resptr->rn_loc.p_x, resptr->rn_loc.p_y, TRIANGLE);
n3->rn_status = RES_FINISHED | RES_TRUE | RES_MARKED;
n3->rn_less = NULL;
n3->rn_more = ResNodeList;
ResNodeList->rn_less = n3;
ResNodeList = n3;
if (resptr == rr1->rr_connection1)
{
ResDeleteResPointer(rr1->rr_connection2, rr1);
rr1->rr_connection2 = n3;
}
else
{
ResDeleteResPointer(rr1->rr_connection1, rr1);
rr1->rr_connection1 = n3;
}
if (n2 == rr2->rr_connection1)
{
ResDeleteResPointer(rr2->rr_connection2, rr2);
rr2->rr_connection2 = n3;
}
else
{
ResDeleteResPointer(rr2->rr_connection1, rr2);
rr2->rr_connection1 = n3;
}
if (n1 == rr3->rr_connection1)
{
ResDeleteResPointer(rr3->rr_connection2, rr3);
rr3->rr_connection2 = n3;
}
else
{
ResDeleteResPointer(rr3->rr_connection1, rr3);
rr3->rr_connection1 = n3;
}
element = (resElement *)mallocMagic((unsigned)(sizeof(resElement)));
element->re_nextEl = NULL;
element->re_thisEl = rr1;
n3->rn_re = element;
element = (resElement *)mallocMagic((unsigned)(sizeof(resElement)));
element->re_nextEl = n3->rn_re;
element->re_thisEl = rr2;
n3->rn_re = element;
element = (resElement *)mallocMagic((unsigned)(sizeof(resElement)));
element->re_nextEl = n3->rn_re;
element->re_thisEl = rr3;
n3->rn_re = element;
if ((n1->rn_status & RES_TRUE) == RES_TRUE)
n1->rn_status &= ~RES_TRUE;
else
n1 = NULL;
if ((n2->rn_status & RES_TRUE) == RES_TRUE)
n2->rn_status &= ~RES_TRUE;
else
n2 = NULL;
ResDoneWithNode(resptr);
if (n1 != NULL) ResDoneWithNode(n1);
if (n2 != NULL) ResDoneWithNode(n2);
break;
}
}
if (status == TRIANGLE) break;
}
HashKill(&NodeResTable);
return status;
}
/* This does the same thing as above, but for a small number of resistors
* per node, avoiding the overhead of creating and destroying hash tables.
*/
for (rcell1 = resptr->rn_re; rcell1->re_nextEl != NULL;
rcell1 = rcell1->re_nextEl)
@ -609,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.
*
@ -780,7 +1010,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.
*
@ -788,7 +1018,7 @@ ResMergeNodes(node1, node2, pendingList, doneList)
*/
void
ResDeleteResPointer(node,resistor)
ResDeleteResPointer(node, resistor)
resNode *node;
resResistor *resistor;
@ -829,7 +1059,7 @@ ResDeleteResPointer(node,resistor)
*
* ResEliminateResistor--
*
* Results:none
* Results: none
*
* Side Effects: Deletes a resistor. Does not delete pointers from nodes to
* resistor.
@ -867,8 +1097,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
*

View File

@ -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++;

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResSimple.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
#endif /* not lint */
#include <stdio.h>
#include <stdlib.h> /* for qsort() */
#include <string.h>
#include <ctype.h>
#include <math.h>
@ -177,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))
{
@ -228,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 &&
@ -361,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
*
@ -386,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"
@ -401,6 +402,29 @@ ResMoveDevices(node1, node2)
node1->rn_te = NULL;
}
/*
*-------------------------------------------------------------------------
*
* qrescompare ---
*
* Sort routine for qsort() to be used by ResScrunchNet(). Sorts in
* order of the resistor value, smallest to largest.
*-------------------------------------------------------------------------
*/
int
qrescompare(const void *one, const void *two)
{
int cval;
resResistor *r1 = *((resResistor **)one);
resResistor *r2 = *((resResistor **)two);
if (r1->rr_value < r2->rr_value) return -1;
else if (r1->rr_value == r2->rr_value) return 0;
else return 1;
}
/*
*-------------------------------------------------------------------------
*
@ -424,29 +448,82 @@ ResScrunchNet(reslist, pendingList, biglist, tolerance)
float tolerance;
{
resResistor *locallist = NULL, *current, *working;
resResistor *current, *working;
resNode *node1, *node2;
resElement *rcell1;
int c1, c2;
int c1, c2, count = 0;
/* Sort resistors by size */
current = *reslist;
while (current != NULL)
/* Method used to sort resistors by size depends on list length */
for (current = *reslist; current; current = current->rr_nextResistor)
{
working = current;
current = current->rr_nextResistor;
if (working == *reslist)
*reslist = current;
else
working->rr_lastResistor->rr_nextResistor = current;
count++;
if (count >= 10) break;
}
/* Sort resistors by size */
if (current != NULL)
current->rr_lastResistor = working->rr_lastResistor;
if (count >= 10)
{
int i;
resResistor **resSortList;
ResAddResistorToList(working, &locallist);
/* For long lists, sort using qsort() */
/* NOTE: It might be better to use the same merge sort used for
* MergeSortBreaks() in ResMakeRes.c, as it does not incur the
* overhead of allocating memory and populating the array.
*/
count = 0;
for (current = *reslist; current; current = current->rr_nextResistor)
count++;
resSortList = (resResistor **)mallocMagic(count * sizeof(resResistor *));
count = 0;
for (current = *reslist; current; current = current->rr_nextResistor)
{
resSortList[count] = current;
count++;
}
/* Sort the list */
qsort(resSortList, count, sizeof(resResistor *), qrescompare);
/* Regenerate links on sorted list */
for (i = 0; i < count; i++)
{
current = resSortList[i];
current->rr_nextResistor = (i == count - 1) ? NULL : resSortList[i + 1];
current->rr_lastResistor = (i == 0) ? NULL : resSortList[i - 1];
}
*reslist = resSortList[0];
freeMagic(resSortList);
}
else
{
/* Original method: Walk the linked list and re-sort by size. */
resResistor *locallist = NULL;
current = *reslist;
while (current != NULL)
{
working = current;
current = current->rr_nextResistor;
if (working == *reslist)
*reslist = current;
else
working->rr_lastResistor->rr_nextResistor = current;
if (current != NULL)
current->rr_lastResistor = working->rr_lastResistor;
ResAddResistorToList(working, &locallist);
}
*reslist = locallist;
}
*reslist = locallist;
while (*reslist != NULL && (*reslist)->rr_value < tolerance)
{
current = *reslist;
@ -501,8 +578,8 @@ ResScrunchNet(reslist, pendingList, biglist, tolerance)
}
}
/*
* If the current resistor isn't a deadend, add its value and
* area to that of the next smallest one. If it is a deadend,
* If the current resistor isn't a dead end, add its value and
* area to that of the next smallest one. If it is a dead end,
* simply add its area to its node.
*/
if (c1 != 0 && c2 != 0)
@ -567,7 +644,7 @@ ResAddResistorToList(resistor, locallist)
resResistor *resistor, **locallist;
{
resResistor *local,*last=NULL;
resResistor *local, *last = NULL;
for (local = *locallist; local != NULL; local = local->rr_nextResistor)
{
@ -630,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");
@ -752,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;
}
@ -843,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
@ -875,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;
@ -886,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)
@ -903,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)
@ -911,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
@ -936,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
@ -967,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;
@ -988,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;

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -51,16 +51,24 @@ typedef struct resistor
#define rr_connection1 rr_node[0]
#define rr_connection2 rr_node[1]
/* Definitions for old FET-style MOSFET devices */
/* 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 +87,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 +121,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 +274,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 +289,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 +311,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 +340,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 +459,7 @@ typedef struct capval
/* Capacitance table constants */
#define OHMSTOMILLIOHMS 1000
#define MILLIOHMSTOOHMS 0.001
#define UNTOUCHED 0
#define SERIES 1
@ -477,12 +494,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 +527,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 +542,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();
@ -543,14 +562,18 @@ extern void ResCheckExtNodes();
extern void ResSortByGate();
extern void ResFixDevName();
extern void ResWriteLumpFile();
extern void ResSortBreaks();
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,7 +590,7 @@ 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 ResPrintDeviceList();
@ -596,59 +619,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 */

View File

@ -159,7 +159,7 @@ rtrFollowName(name, firstInNet, area)
{
if ( firstInNet )
{
RtrMilestonePrint("#");
RtrMilestonePrint();
(void) DBSrLabelLoc(EditCellUse, name, rtrFollowLocFunc, (ClientData) area);
}
return 0;

View File

@ -471,12 +471,11 @@ SelectArea(scx, types, xMask, globmatch)
if (TTMaskHasType(types, L_LABEL))
{
if (globmatch != NULL)
DBCellCopyGlobLabels(scx, &DBAllTypeBits, xMask, SelectUse, &labelArea,
DBCellCopyGlobLabels(scx, types, xMask, SelectUse, &labelArea,
globmatch);
else
DBCellCopyAllLabels(scx, &DBAllTypeBits, xMask, SelectUse, &labelArea);
DBCellCopyAllLabels(scx, types, xMask, SelectUse, &labelArea);
}
else (void) DBCellCopyAllLabels(scx, types, xMask, SelectUse, &labelArea);
/* Select cell uses. */
@ -835,6 +834,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);
}

View File

@ -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;

View File

@ -640,8 +640,7 @@ SimAddLabels(
pos = SimPutLabel(rootuse, &selectBox, GEO_CENTER,
current->tl_simLabel, TT_SPACE);
DBReComputeBbox(rootuse);
DBWLabelChanged(rootuse, current->tl_simLabel, &selectBox,
pos, DBW_ALLWINDOWS);
DBWAreaChanged(rootuse, &selectBox, DBW_ALLWINDOWS, &DBAllButSpaceBits);
}
}

View File

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

View File

@ -1,70 +0,0 @@
%%BeginProlog
%
% PostScript prolog for output from magic plot
% Version: 1.0
% written by Tim Edwards 4/05/00 JHU Applied Physics Laboratory
%
%%BeginResource: procset MAGICproc 1.0 1
% supporting definitions
/MAGICsave save def
/bop { 1 setlinecap 0 setlinejoin 6 setmiterlimit 0 setgray } def
/ninit { /nChars matrix currentmatrix dup 0 get 0 eq {1} {0}
ifelse get abs 72 8.5 mul mul 64 div ceiling cvi def } def
/minit { 1 1 dtransform abs dup 1 exch div /onePix exch def
dup /resY exch def 1 exch div /iresY exch def
abs dup /resX exch def 1 exch div /iresX exch def
/bX 64 iresX mul def /bY 64 iresY mul def
/pattFont StipplePattern definefont pop
/patterns /pattFont findfont [iresX 64 mul 0 0 iresY 64 mul 0 0] makefont def
/ca nChars 1 add string def
} def
/StipplePattern 45 dict def
StipplePattern begin
/FontType 3 def
/FontMatrix [1 0 0 1 0 0] def
/FontBBox [0 0 1 1] def
/Encoding 256 array def
/PattName (P0) def
/tmpStr 1 string def
/NoPatt {<00>} def
0 1 255 { Encoding exch /NoPatt put } for
/BuildChar {
1 0 0 0 1 1 setcachedevice exch begin Encoding exch get load
64 64 true [64 0 0 64 0 0] 5 -1 roll imagemask end } def
end
/dp { StipplePattern begin dup 30 tmpStr cvrs PattName exch 1 exch
putinterval PattName cvn dup Encoding exch 4 -1 roll exch put exch
store end } def
/sf { findfont exch scalefont setfont } bind def
/sp { patterns setfont 2 setlinewidth } def
/lb { gsave translate 0 0 moveto /just exch def gsave dup true charpath
flattenpath pathbbox grestore exch 4 -1 roll exch sub 3 1 roll sub
just 4 and 0 gt {just 8 and 0 eq {0.5 mul} if}{pop 0} ifelse exch
just 1 and 0 gt {just 2 and 0 eq {0.5 mul} if}{pop 0} ifelse exch
rmoveto show grestore } def
/sl { 0 1 nChars { exch dup 3 1 roll ca 3 1 roll put } for pop } def
/sc { setcmykcolor } bind def
/l1 { onePix setlinewidth } def
/l2 { onePix 2 mul setlinewidth } def
/l3 { onePix 3 mul setlinewidth } def
/ml { moveto lineto stroke } bind def
/vl { moveto 0 exch rlineto stroke } bind def
/hl { moveto 0 rlineto stroke } bind def
/mr { rectstroke } bind def
/mx { 4 copy rectstroke 4 -1 roll 4 -1 roll 4 copy moveto rlineto stroke
3 -1 roll dup neg 4 1 roll add moveto rlineto stroke } bind def
/pl { gsave translate /d exch def 0 d neg moveto 0 d lineto stroke
d neg 0 moveto d 0 lineto stroke grestore } bind def
/bx { x resX mul cvi 63 not and dup iresX mul exch
w resX mul sub abs 63 add cvi 64 idiv /w exch def
y resY mul cvi 63 not and dup iresY mul exch
h resY mul sub abs 63 add cvi 64 idiv /h exch def
/ch ca 0 w getinterval def
moveto h { ch gsave show grestore 0 bY rmoveto } repeat grestore } def
/fb {/h exch def /w exch def /y exch def /x exch def gsave newpath
x y moveto w y lineto w h lineto x h lineto closepath clip bx } def
/tb {1 sub 3 1 roll gsave newpath moveto {lineto} repeat closepath clip pathbbox
/h exch def /w exch def /y exch def /x exch def bx } def

View File

@ -249,6 +249,9 @@ proc ::tkcon::Init {} {
if {![info exists PRIV(histfile)]} {
set PRIV(histfile) [file join $env($envHome) $histfile]
}
} else {
set PRIV(rcfile) ""
set PRIV(histfile) ""
}
## Handle command line arguments before sourcing resource file to

View File

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

View File

@ -96,6 +96,7 @@ extern char TxInterruptChar; /* The current interrupt character */
/* command procedures */
extern void TxDispatch(FILE *f);
extern int TxDispatchString(const char *str, bool quiet);
/* C99 compat */
extern void TxMore(const char *mesg);

View File

@ -1751,6 +1751,47 @@ done:
DQFree(&inputCommands);
}
int
TxDispatchString(
const char *str,
bool quiet)
{
int result = 0;
DQueue inputCommands;
DQInit(&inputCommands, 4);
TxParseString_internal(str, &inputCommands, (TxInputEvent *) NULL);
while (!DQIsEmpty(&inputCommands))
{
TxCommand *cmd;
cmd = (TxCommand *) DQPopFront(&inputCommands);
if (txHaveCurrentPoint)
{
cmd->tx_p = txCurrentPoint;
cmd->tx_wid = txCurrentWindowID;
txHaveCurrentPoint = FALSE;
}
result = WindSendCommand((MagWindow *) NULL, cmd, quiet);
TxFreeCommand(cmd);
TxCommandNumber++;
if (result != 0)
break;
}
WindUpdate();
while (!DQIsEmpty(&inputCommands))
TxFreeCommand((TxCommand *) DQPopFront(&inputCommands));
DQFree(&inputCommands);
return result;
}
#endif /* !MAGIC_WRAPPER */
/*

View File

@ -0,0 +1,100 @@
# Magic VLSI — Headless WASM Build
This toolchain builds Magic as a headless WebAssembly module using Emscripten.
X11, Tk, OpenGL, and readline are all disabled. The resulting `magic.js` /
`magic.wasm` pair can be loaded in Node.js, a browser, or a Web Worker.
## Quick start (npm package)
The easiest way to build and use the WASM module is through the npm package:
```bash
# Build magic.js + magic.wasm and copy them into npm/
bash npm/build.sh
# Run the test suite (extract, GDS, DRC, CIF)
npm --prefix npm test
```
See [`npm/examples/`](../../npm/examples/) for usage examples.
## Manual build
Prerequisites: an activated [emsdk](https://emscripten.org/docs/getting_started/downloads.html)
checkout (`emcc`, `emar`, `emranlib` on `PATH`), plus standard `make` and `gcc`.
```bash
# 1. Configure for Emscripten
CFLAGS="--std=c17 -D_DEFAULT_SOURCE=1 -DEMSCRIPTEN=1 -g" \
emconfigure ./configure \
--without-cairo --without-opengl --without-x --without-tk --without-tcl \
--disable-readline --disable-compression \
--host=asmjs-unknown-emscripten \
--target=asmjs-unknown-emscripten
# 2. Append the Emscripten-specific make settings
cat toolchains/emscripten/defs.mak >> defs.mak
# 3. Build
emmake make depend
emmake make -j$(nproc) modules libs
emmake make techs
emmake make mains
```
The outputs are `magic/magic.js` and `magic/magic.wasm`.
## Embedded files
The following runtime files are baked directly into the WASM binary via
Emscripten's `--embed-file` mechanism and are available at startup without
any host filesystem access:
| Host path | VFS path |
|-----------|----------|
| `scmos/` | `/magic/sys/current/` |
| `windows/windows7.glyphs` | `/magic/sys/windows7.glyphs` |
| `windows/windows7.glyphs` | `/magic/sys/bw.glyphs` |
To embed a custom technology file, add an `--embed-file` entry to
`TOP_EXTRA_LIBS` in [`defs.mak`](defs.mak).
## Exported C API
The WASM module exports four functions:
| Function | Description |
|----------|-------------|
| `magic_wasm_init()` | Initialize Magic (idempotent — safe to call multiple times). Returns 0 on success. |
| `magic_wasm_run_command(const char *cmd)` | Dispatch one Magic command. Calls `magic_wasm_init()` automatically if needed. Returns 0 on success. |
| `magic_wasm_source_file(const char *path)` | Read and execute a command file from the virtual filesystem. |
| `magic_wasm_update()` | Drive a display-update cycle. No-op in headless builds (null display suspends all redraws). |
### JavaScript usage
```js
import createMagic from 'magic-vlsi-wasm';
const { runCommand, FS } = await createMagic();
// Write a layout file into the virtual filesystem
FS.writeFile('/work/inv.mag', layoutBytes);
// Run Magic commands
runCommand('tech load sky130A');
runCommand('load /work/inv');
runCommand('gds write /work/inv');
// Read the result back out
const gdsBytes = FS.readFile('/work/inv.gds');
```
## Notes
- `CAD_ROOT` is automatically set to `/` so that embedded system files are
resolved under `/magic/sys/`.
- The null display driver (`-d null`) sets `GrDisplayStatus = DISPLAY_SUSPEND`,
which causes `WindUpdate` to return immediately without invoking any display
callbacks. This is what makes the WASM build safe to run without a screen.
- All POSIX signal/timer APIs (`setitimer`, `SIGALRM`, `fcntl`) are compiled
out under `__EMSCRIPTEN__`; the display progress timer becomes a no-op.

Some files were not shown because too many files have changed in this diff Show More