Slowly working through things needed for removing the ".sim"
file dependency of "extresist". Stopping and committing work in order to rebase from the master branch.
This commit is contained in:
parent
297a05c4ed
commit
76f97c90e5
68
README
68
README
|
|
@ -132,3 +132,71 @@ around for the duration of the extraction, and needs to have the
|
|||
extresist splits on hand for coupling capacitance calculations
|
||||
and all output. Maybe copy in a simplified form to yet another
|
||||
ClientData field on a nodeRegion.
|
||||
|
||||
So there's another way to go about it:
|
||||
(1) When running extract, create the device record for extresist
|
||||
after running transList = ExtFindRegions(). This list does
|
||||
not change between being created and the transistors being
|
||||
output.
|
||||
(2) Create the node record for extresist after running extFindNodes().
|
||||
(3) With this information, it's possible to run extresist on the
|
||||
node. Because nodes have all been marked with regions, use
|
||||
a ClientData record in NodeRegion to store the extresist working
|
||||
data. Clean up afterward; the NodeRegion records still remain
|
||||
in place on the tiles.
|
||||
|
||||
The simplest approach is just to write the .res.ext annotation file
|
||||
as before, although with the extresist code being run before the
|
||||
.ext file is written, it is possible to combine the two. But the
|
||||
use of .res.ext is a quicker intermediate step that allows the
|
||||
extresist code to be checked for hierarchical operation, which it
|
||||
will now be able to do relatively smoothly, as it is not necessary
|
||||
to create multiple .sim and .node files for each subcircuit.
|
||||
|
||||
Trickier: Replace NodeRegions with the subnet regions. One
|
||||
difficulty here is dealing with multiple subnets in a single tile.
|
||||
|
||||
Simplifying the extresist code by removing references to ARIEL
|
||||
and LAPLACE (which are missing code and so cannot be reinstated
|
||||
without a lot of guesswork), and removing some apparently nonfunctional
|
||||
code having to do with events.
|
||||
|
||||
To do after finding transistors: What's done in ResReadSim():
|
||||
Specifically, ResSimDevice() and ResSimSubckt().
|
||||
|
||||
ResReadNode() is the only thing needed to be done for nodes.
|
||||
It calls ResInitializeNode() and sets location and (tile) type.
|
||||
Everything else in the structure is TBD.
|
||||
|
||||
Another issue: The device type is determined by "extract" only
|
||||
during extOutputDevices. The device type in "devrec" is not
|
||||
computed until just before the device is output. But. . . is
|
||||
this really needed by extresist? It was being used by extresist
|
||||
because the device name is what's in the .sim file.
|
||||
|
||||
You know what probably works best?
|
||||
(1) When nodes are written to the .ext file, save the information
|
||||
for extresist.
|
||||
(2) When devices are written to the .ext file, save the information
|
||||
for extresist.
|
||||
(3) Run extresist at the end of the cell def's extraction, and write
|
||||
the .res.ext file as usual. (Later, maybe, get rid of the
|
||||
.res.ext file and just re-write the annotated .ext file.)
|
||||
(4) Create a new method for annotated coupling capacitances.
|
||||
Recompute the coupling and output adjustments (positive for
|
||||
the new node/terminal nodes, negative for the original node).
|
||||
Coupling will be recomputed only for nets processed by
|
||||
extresist. Node substrate capacitance changes will be
|
||||
recorded in .res.ext (but---I think substrate capacitance is
|
||||
now unused in the node record, because there is a substrate
|
||||
node and substrate capacitance is now treated like coupling
|
||||
to that node; isn't that right?) (and there will be multiple
|
||||
substrate nodes, as well.)
|
||||
|
||||
This can also be done in two steps:
|
||||
(1) Replace ResReadSim() with ResReadExt() and read back the .ext
|
||||
file immediately after writing it.
|
||||
(2) Remove the read-back and just generate the information on the
|
||||
fly.
|
||||
|
||||
This also has the advantage that it can be tested in parts.
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#include "commands/commands.h"
|
||||
#include "database/database.h"
|
||||
#include "extflat/extflat.h"
|
||||
#include "extflat/extparse.h"
|
||||
#include "extflat/EFint.h"
|
||||
#include "extract/extract.h"
|
||||
#include "extract/extractInt.h"
|
||||
|
|
@ -45,61 +46,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
/* C99 compat */
|
||||
#include "textio/textio.h"
|
||||
|
||||
#ifndef MAGIC_WRAPPER
|
||||
/* This must match the definition for extDevTable in extract/ExtBasic.c */
|
||||
const char * const extDevTable[] = {"fet", "mosfet", "asymmetric", "bjt", "devres",
|
||||
"devcap", "devcaprev", "vsource", "diode", "pdiode",
|
||||
"ndiode", "subckt", "rsubckt", "msubckt", "csubckt",
|
||||
"dsubckt", "veriloga", NULL};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following table describes the kinds of lines
|
||||
* that may be read in a .ext file.
|
||||
*/
|
||||
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
|
||||
} Key;
|
||||
|
||||
static const struct
|
||||
{
|
||||
const char *k_name; /* Name of first token on line */
|
||||
Key k_key; /* Internal name for token of this type */
|
||||
int k_mintokens; /* Min total # of tokens on line of this type */
|
||||
}
|
||||
keyTable[] =
|
||||
{
|
||||
{"abstract", ABSTRACT, 0}, /* defines a LEF-like view */
|
||||
{"adjust", ADJUST, 4},
|
||||
{"attr", ATTR, 8},
|
||||
{"cap", CAP, 4},
|
||||
{"device", DEVICE, 11}, /* effectively replaces "fet" */
|
||||
{"distance", DIST, 4},
|
||||
{"equiv", EQUIV, 3},
|
||||
{"fet", FET, 12}, /* for backwards compatibility */
|
||||
{"killnode", KILLNODE, 2},
|
||||
{"merge", MERGE, 3},
|
||||
{"node", NODE, 7},
|
||||
{"parameters", PARAMETERS, 3},
|
||||
{"port", PORT, 8},
|
||||
{"primitive", PRIMITIVE, 0}, /* defines a primitive device */
|
||||
{"resist", RESISTOR, 4},
|
||||
{"resistclasses", RESISTCLASS, 1},
|
||||
{"rnode", RNODE, 5},
|
||||
{"scale", SCALE, 4},
|
||||
{"subcap", SUBCAP, 3},
|
||||
{"substrate", SUBSTRATE, 3},
|
||||
{"tech", TECH, 2},
|
||||
{"timestamp", TIMESTAMP, 2},
|
||||
{"use", USE, 9},
|
||||
{"version", VERSION, 2},
|
||||
{"style", EXT_STYLE, 2},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* Data shared with EFerror.c */
|
||||
char *efReadFileName; /* Name of file currently being read */
|
||||
int efReadLineNum; /* Current line number in above file */
|
||||
|
|
@ -109,10 +55,6 @@ bool EFSaveLocs; /* If TRUE, save location of merged top-level nodes */
|
|||
/* Data local to this file */
|
||||
static bool efReadDef(Def *def, bool dosubckt, bool resist, bool noscale, bool toplevel, bool isspice);
|
||||
|
||||
/* atoCap - convert a string to a EFCapValue */
|
||||
#define atoCap(s) ((EFCapValue)atof(s))
|
||||
|
||||
|
||||
/*
|
||||
* ----------------------------------------------------------------------------
|
||||
*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* extparse.h
|
||||
*
|
||||
* Definitions for the .ext file parser. Relocated from EFread.c.
|
||||
*
|
||||
* *********************************************************************
|
||||
* * 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. *
|
||||
* *********************************************************************
|
||||
*/
|
||||
|
||||
/* These must be in the order of "known devices" in extract/extract.h. */
|
||||
/* This table is also used in extract/ExtBasic.c */
|
||||
|
||||
/* Note: "fet" refers to the original fet type; "mosfet" refers to the */
|
||||
/* new type. The main difference is that "fet" records area/perimeter */
|
||||
/* while "mosfet" records length/width. */
|
||||
|
||||
#ifndef MAGIC_WRAPPER
|
||||
const char * const extDevTable[] = {"fet", "mosfet", "asymmetric", "bjt", "devres",
|
||||
"devcap", "devcaprev", "vsource", "diode", "pdiode",
|
||||
"ndiode", "subckt", "rsubckt", "msubckt", "csubckt",
|
||||
"dsubckt", "veriloga", NULL};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following table describes the kinds of lines
|
||||
* that may be read in a .ext file.
|
||||
*/
|
||||
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
|
||||
} Key;
|
||||
|
||||
static const struct
|
||||
{
|
||||
const char *k_name; /* Name of first token on line */
|
||||
Key k_key; /* Internal name for token of this type */
|
||||
int k_mintokens; /* Min total # of tokens on line of this type */
|
||||
}
|
||||
keyTable[] =
|
||||
{
|
||||
{"abstract", ABSTRACT, 0}, /* defines a LEF-like view */
|
||||
{"adjust", ADJUST, 4},
|
||||
{"attr", ATTR, 8},
|
||||
{"cap", CAP, 4},
|
||||
{"device", DEVICE, 11}, /* effectively replaces "fet" */
|
||||
{"distance", DIST, 4},
|
||||
{"equiv", EQUIV, 3},
|
||||
{"fet", FET, 12}, /* for backwards compatibility */
|
||||
{"killnode", KILLNODE, 2},
|
||||
{"merge", MERGE, 3},
|
||||
{"node", NODE, 7},
|
||||
{"parameters", PARAMETERS, 3},
|
||||
{"port", PORT, 8},
|
||||
{"primitive", PRIMITIVE, 0}, /* defines a primitive device */
|
||||
{"resist", RESISTOR, 4},
|
||||
{"resistclasses", RESISTCLASS, 1},
|
||||
{"rnode", RNODE, 5},
|
||||
{"scale", SCALE, 4},
|
||||
{"subcap", SUBCAP, 3},
|
||||
{"substrate", SUBSTRATE, 3},
|
||||
{"tech", TECH, 2},
|
||||
{"timestamp", TIMESTAMP, 2},
|
||||
{"use", USE, 9},
|
||||
{"version", VERSION, 2},
|
||||
{"style", EXT_STYLE, 2},
|
||||
{0}
|
||||
};
|
||||
|
||||
/* atoCap - convert a string to a EFCapValue */
|
||||
#define atoCap(s) ((EFCapValue)atof(s))
|
||||
|
||||
extern int efReadLineNum; /* Current line number in the .ext file */
|
||||
|
||||
|
|
@ -37,6 +37,7 @@ static char sccsid[] = "@(#)ExtBasic.c 4.13 MAGIC (Berkeley) 12/5/85";
|
|||
#include "utils/malloc.h"
|
||||
#include "textio/textio.h"
|
||||
#include "debug/debug.h"
|
||||
#include "extflat/extparse.h"
|
||||
#include "extract/extract.h"
|
||||
#include "extract/extractInt.h"
|
||||
#include "utils/signals.h"
|
||||
|
|
@ -45,20 +46,7 @@ static char sccsid[] = "@(#)ExtBasic.c 4.13 MAGIC (Berkeley) 12/5/85";
|
|||
#include "utils/styles.h"
|
||||
#include "utils/stack.h"
|
||||
#include "utils/utils.h"
|
||||
|
||||
/* These must be in the order of "known devices" in extract.h. */
|
||||
|
||||
/* Note: "fet" refers to the original fet type; "mosfet" refers to the */
|
||||
/* new type. The main difference is that "fet" records area/perimeter */
|
||||
/* while "mosfet" records length/width. */
|
||||
/* Also: Note that this table is repeated in extflat/EFread.c when */
|
||||
/* ext2spice/ext2sim are compiled as separate programs (i.e., non-Tcl) */
|
||||
|
||||
#ifdef MAGIC_WRAPPER
|
||||
const char * const extDevTable[] = {"fet", "mosfet", "asymmetric", "bjt", "devres",
|
||||
"devcap", "devcaprev", "vsource", "diode", "pdiode", "ndiode",
|
||||
"subckt", "rsubckt", "msubckt", "csubckt", "dsubckt", "veriloga", NULL};
|
||||
#endif
|
||||
#include "resis/resis.h"
|
||||
|
||||
/* --------------------- Data local to this file ---------------------- */
|
||||
|
||||
|
|
@ -301,28 +289,6 @@ extBasic(def, outFile)
|
|||
if (!SigInterruptPending && (ExtDoWarn & EXTWARN_DUP) && !isabstract)
|
||||
extFindDuplicateLabels(def, nodeList);
|
||||
|
||||
/*
|
||||
* If full R-C extraction is requested, then run it now. This
|
||||
* code was previously run as the "extresist" command, but it
|
||||
* makes more sense to be integral to the extraction process.
|
||||
* It must be run prior to extFindCoupling() so that coupling
|
||||
* capacitances are correctly assigned to subnets.
|
||||
*/
|
||||
if (!SigInterruptPending && (ExtOptions & EXT_DOEXTRESIST))
|
||||
{
|
||||
ResSimNode *rsimnode;
|
||||
ResisData resisdata;
|
||||
int n_ext = 0, n_out = 0;
|
||||
|
||||
for (reg = nodeList; reg && !SigInterruptPending; reg = reg->nreg_next)
|
||||
{
|
||||
rsimnode = ResCreateNode();
|
||||
ResProcessNode(rsimnode, def, &resisdata, &n_ext, &n_out);
|
||||
|
||||
/* To be completed */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Build up table of coupling capacitances (overlap, sidewall).
|
||||
* This comes before extOutputNodes because we may have to adjust
|
||||
|
|
@ -580,6 +546,24 @@ extBasic(def, outFile)
|
|||
extOutputDevices(def, transList, outFile);
|
||||
}
|
||||
|
||||
/* Integrated extresist --- Run "extresist" on the cell def just
|
||||
* extracted and produce an annotation file "<file>.res.ext".
|
||||
*/
|
||||
|
||||
if (ExtOptions & EXT_DOEXTRESIST)
|
||||
{
|
||||
ResisData resisdata;
|
||||
|
||||
/* These need to be passed to extresist somehow. Most are unused. */
|
||||
resisdata.rthresh = 0;
|
||||
resisdata.tdiTolerance = 1;
|
||||
resisdata.frequency = 10e6;
|
||||
resisdata.mainDef = def;
|
||||
resisdata.savePlanes = (struct saveList *)NULL; /* unused */
|
||||
|
||||
ExtResisForDef(def, &resisdata);
|
||||
}
|
||||
|
||||
/* Clean up */
|
||||
if (coupleInitialized)
|
||||
extCapHashKill(&extCoupleHash);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
MODULE = resis
|
||||
MAGICDIR = ..
|
||||
SRCS = ResMain.c ResJunct.c ResMakeRes.c ResSimple.c ResPrint.c \
|
||||
ResReadSim.c ResRex.c ResBasic.c ResMerge.c ResChecks.c \
|
||||
ResReadExt.c ResRex.c ResBasic.c ResMerge.c ResChecks.c \
|
||||
ResFract.c ResUtils.c ResDebug.c
|
||||
|
||||
include ${MAGICDIR}/defs.mak
|
||||
|
|
|
|||
|
|
@ -112,7 +112,6 @@ ResSanityChecks(nodename, resistorList, nodeList, devlist)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (dev->rd_status & RES_DEV_PLUG) continue;
|
||||
reached = FALSE;
|
||||
for (i = 0; i != dev->rd_nterms; i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@ ResPrintDeviceList(fp, list)
|
|||
int i;
|
||||
for (; list != NULL; list = list->rd_nextDev)
|
||||
{
|
||||
if (list->rd_status & RES_DEV_PLUG) continue;
|
||||
if (fp == stdout)
|
||||
TxPrintf("t w %d l %d ", list->rd_width, list->rd_length);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ int ResTileCount = 0; /* Number of tiles rn_status */
|
|||
extern ExtRegion *ResFirst();
|
||||
extern Tile *FindStartTile();
|
||||
extern int ResEachTile();
|
||||
extern ResSimNode *ResInitializeNode();
|
||||
extern ResExtNode *ResInitializeNode();
|
||||
TileTypeBitMask ResSDTypesBitMask;
|
||||
TileTypeBitMask ResSubTypesBitMask;
|
||||
|
||||
|
|
@ -201,13 +201,13 @@ ResMakePortBreakpoints(def)
|
|||
TileTypeBitMask mask;
|
||||
HashSearch hs;
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
int ResAddBreakpointFunc(); /* Forward Declaration */
|
||||
|
||||
HashStartSearch(&hs);
|
||||
while((entry = HashNext(&ResNodeTable,&hs)) != NULL)
|
||||
{
|
||||
node = (ResSimNode *)HashGetValue(entry);
|
||||
node = (ResExtNode *)HashGetValue(entry);
|
||||
if (node->status & PORTNODE)
|
||||
{
|
||||
if (node->rs_ttype <= 0)
|
||||
|
|
@ -281,7 +281,7 @@ ResMakeLabelBreakpoints(def, goodies)
|
|||
Rect *rect;
|
||||
TileTypeBitMask mask;
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
Label *slab;
|
||||
int ResAddBreakpointFunc(); /* Forward Declaration */
|
||||
|
||||
|
|
@ -353,7 +353,7 @@ int
|
|||
ResAddBreakpointFunc(tile, dinfo, node)
|
||||
Tile *tile;
|
||||
TileType dinfo; /* (unused) */
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
{
|
||||
tileJunk *junk;
|
||||
|
||||
|
|
@ -512,51 +512,6 @@ ResProcessTiles(goodies, origin)
|
|||
resCurrentNode = NULL;
|
||||
(void) ResEachTile(startTile, origin);
|
||||
}
|
||||
#ifdef ARIEL
|
||||
else if (ResOptionsFlags & ResOpt_Power)
|
||||
{
|
||||
for (fix = ResFixList; fix != NULL; fix = fix->fp_next)
|
||||
{
|
||||
Tile *tile = fix->fp_tile;
|
||||
if (tile == NULL)
|
||||
{
|
||||
tile = PlaneGetHint(ResDef->cd_planes[DBPlane(fix->fp_ttype)]);
|
||||
GOTOPOINT(tile, &(fix->fp_loc));
|
||||
if (TiGetTypeExact(tile) != TT_SPACE)
|
||||
{
|
||||
fix->fp_tile = tile;
|
||||
}
|
||||
else
|
||||
{
|
||||
tile = NULL;
|
||||
}
|
||||
}
|
||||
if (tile != NULL)
|
||||
{
|
||||
int x = fix->fp_loc.p_x;
|
||||
int y = fix->fp_loc.p_y;
|
||||
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
|
||||
InitializeNode(resptr, x, y, RES_NODE_ORIGIN);
|
||||
resptr->rn_status = TRUE;
|
||||
resptr->rn_noderes = 0;
|
||||
ResAddToQueue(resptr, &ResNodeQueue);
|
||||
fix->fp_node = resptr;
|
||||
NEWBREAK(resptr, tile, x, y, NULL);
|
||||
}
|
||||
}
|
||||
for (fix = ResFixList; fix != NULL; fix = fix->fp_next)
|
||||
{
|
||||
Tile *tile = fix->fp_tile;
|
||||
|
||||
if (tile != NULL && (((tileJunk *)TiGetClientPTR(tile)->tj_status &
|
||||
RES_TILE_DONE) == 0)
|
||||
{
|
||||
resCurrentNode = fix->fp_node;
|
||||
(void) ResEachTile(startile, (Point *)NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#ifdef PARANOID
|
||||
else
|
||||
{
|
||||
|
|
@ -1036,7 +991,7 @@ ResShaveContacts(tile, dinfo, def)
|
|||
|
||||
bool
|
||||
ResExtractNet(node, goodies, cellname)
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
ResGlobalParams *goodies;
|
||||
char *cellname;
|
||||
{
|
||||
|
|
@ -1103,11 +1058,6 @@ ResExtractNet(node, goodies, cellname)
|
|||
|
||||
DBCellClearDef(ResUse->cu_def);
|
||||
|
||||
#ifdef ARIEL
|
||||
if ((ResOptionsFlags & ResOpt_Power) &&
|
||||
strcmp(node->name, goodies->rg_name) != 0) continue;
|
||||
#endif
|
||||
|
||||
/* Copy Paint */
|
||||
|
||||
scx.scx_area.r_ll.p_x = node->location.p_x - 2;
|
||||
|
|
@ -1239,30 +1189,6 @@ ResExtractNet(node, goodies, cellname)
|
|||
ResFindNewContactTiles(ResContactList);
|
||||
ResPreProcessDevices(DevTiles, ResDevList, ResUse->cu_def);
|
||||
|
||||
#ifdef LAPLACE
|
||||
if (ResOptionsFlags & ResOpt_DoLaplace)
|
||||
{
|
||||
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
|
||||
{
|
||||
Plane *plane = ResUse->cu_def->cd_planes[pNum];
|
||||
Rect *rect = &ResUse->cu_def->cd_bbox;
|
||||
Res1d(plane, rect);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef ARIEL
|
||||
if (ResOptionsFlags & ResOpt_Power)
|
||||
{
|
||||
for (fix = startlist; fix != NULL; fix = fix->fp_next)
|
||||
{
|
||||
fix->fp_tile = PlaneGetHint(ResUse->cu_def->cd_planes[DBPlane(fix->fp_ttype)]);
|
||||
GOTOPOINT(fix->fp_tile, &fix->fp_loc);
|
||||
if (TiGetTypeExact(fix->fp_tile) == TT_SPACE) fix->fp_tile = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* do extraction */
|
||||
if (ResProcessTiles(goodies, &startpoint) != 0) return TRUE;
|
||||
return FALSE;
|
||||
|
|
|
|||
|
|
@ -263,10 +263,6 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
|
|||
{
|
||||
resistor->rr_status = RES_EW;
|
||||
}
|
||||
#ifdef ARIEL
|
||||
resistor->rr_csArea = height *
|
||||
ExtCurStyle->exts_thick[resistor->rr_tt];
|
||||
#endif
|
||||
resistor->rr_value =
|
||||
(float)ExtCurStyle->exts_sheetResist[resistor->rr_tt]
|
||||
* (float)(p2->br_loc.p_x - p1->br_loc.p_x)
|
||||
|
|
@ -440,10 +436,6 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
|
|||
{
|
||||
resistor->rr_status = RES_NS;
|
||||
}
|
||||
#ifdef ARIEL
|
||||
resistor->rr_csArea = width
|
||||
* ExtCurStyle->exts_thick[resistor->rr_tt];
|
||||
#endif
|
||||
resistor->rr_value =
|
||||
(float)ExtCurStyle->exts_sheetResist[resistor->rr_tt]
|
||||
* (float)(p2->br_loc.p_y - p1->br_loc.p_y)
|
||||
|
|
@ -916,11 +908,6 @@ ResDoContacts(contact, nodes, resList)
|
|||
resistor->rr_value =
|
||||
(float)ExtCurStyle->exts_viaResist[contact->cp_type] /
|
||||
(float)(squaresx * squaresy);
|
||||
#ifdef ARIEL
|
||||
resistor->rr_csArea =
|
||||
(float)ExtCurStyle->exts_thick[contact->cp_type] /
|
||||
(float)(squaresx * squaresy);
|
||||
#endif
|
||||
resistor->rr_tt = contact->cp_type;
|
||||
resistor->rr_float.rr_area = 0;
|
||||
resistor->rr_status = 0;
|
||||
|
|
|
|||
|
|
@ -150,14 +150,6 @@ ResFixRes(resptr, resptr2, resptr3, elimResis, newResis)
|
|||
ASSERT(newResis->rr_value > 0, "series");
|
||||
newResis->rr_float.rr_area += elimResis->rr_float.rr_area;
|
||||
|
||||
#ifdef ARIEL
|
||||
if (elimResis->rr_csArea && elimResis->rr_csArea < newResis->rr_csArea
|
||||
|| newResis->rr_csArea == 0)
|
||||
{
|
||||
newResis->rr_csArea = elimResis->rr_csArea;
|
||||
newResis->rr_tt = elimResis->rr_tt;
|
||||
}
|
||||
#endif
|
||||
for (thisREl = resptr3->rn_re; (thisREl != NULL); thisREl = thisREl->re_nextEl)
|
||||
{
|
||||
if (thisREl->re_thisEl == elimResis)
|
||||
|
|
@ -206,9 +198,6 @@ ResFixParallel(elimResis, newResis)
|
|||
newResis->rr_value = 0;
|
||||
}
|
||||
newResis->rr_float.rr_area += elimResis->rr_float.rr_area;
|
||||
#ifdef ARIEL
|
||||
newResis->rr_csArea += elimResis->rr_csArea;
|
||||
#endif
|
||||
ResDeleteResPointer(elimResis->rr_connection1, elimResis);
|
||||
ResDeleteResPointer(elimResis->rr_connection2, elimResis);
|
||||
ResEliminateResistor(elimResis, &ResResList);
|
||||
|
|
@ -671,31 +660,16 @@ ResMergeNodes(node1, node2, pendingList, doneList)
|
|||
workingDev = node2->rn_te;
|
||||
while (workingDev != NULL)
|
||||
{
|
||||
if (workingDev->te_thist->rd_status & RES_DEV_PLUG)
|
||||
{
|
||||
ResPlug *plug = (ResPlug *) workingDev->te_thist;
|
||||
if (plug->rpl_node == node2)
|
||||
plug->rpl_node = node1;
|
||||
else
|
||||
{
|
||||
TxError("Bad plug node: is (%d %d), should be (%d %d)\n",
|
||||
plug->rpl_node->rn_loc.p_x, plug->rpl_node->rn_loc.p_y,
|
||||
node2->rn_loc.p_x, node2->rn_loc.p_y);
|
||||
plug->rpl_node = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int j;
|
||||
int j;
|
||||
|
||||
for (j = 0; j != workingDev->te_thist->rd_nterms; j++)
|
||||
if (workingDev->te_thist->rd_terminals[j] == node2)
|
||||
workingDev->te_thist->rd_terminals[j] = node1;
|
||||
}
|
||||
tDev = workingDev;
|
||||
workingDev = workingDev->te_nextt;
|
||||
tDev->te_nextt = node1->rn_te;
|
||||
node1->rn_te = tDev;
|
||||
for (j = 0; j != workingDev->te_thist->rd_nterms; j++)
|
||||
if (workingDev->te_thist->rd_terminals[j] == node2)
|
||||
workingDev->te_thist->rd_terminals[j] = node1;
|
||||
|
||||
tDev = workingDev;
|
||||
workingDev = workingDev->te_nextt;
|
||||
tDev->te_nextt = node1->rn_te;
|
||||
node1->rn_te = tDev;
|
||||
}
|
||||
|
||||
/* append junction lists */
|
||||
|
|
@ -749,11 +723,11 @@ ResMergeNodes(node1, node2, pendingList, doneList)
|
|||
else if ((node2->rn_name != NULL) && (node2->rn_name != node1->rn_name))
|
||||
{
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
|
||||
/* Check if node2 is a port */
|
||||
entry = HashFind(&ResNodeTable, node2->rn_name);
|
||||
node = (ResSimNode *)HashGetValue(entry);
|
||||
node = (ResExtNode *)HashGetValue(entry);
|
||||
if (node && (node->status & PORTNODE))
|
||||
node1->rn_name = node2->rn_name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#define MAXNAME 1000
|
||||
#define KV_TO_mV 1000000
|
||||
|
||||
extern ResSimNode *ResInitializeNode();
|
||||
extern ResExtNode *ResInitializeNode();
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -57,7 +57,7 @@ ResPrintExtRes(outextfile, resistors, nodename)
|
|||
int nodenum=0;
|
||||
char newname[MAXNAME];
|
||||
HashEntry *entry;
|
||||
ResSimNode *node, *ResInitializeNode();
|
||||
ResExtNode *node, *ResInitializeNode();
|
||||
|
||||
for (; resistors != NULL; resistors = resistors->rr_nextResistor)
|
||||
{
|
||||
|
|
@ -215,13 +215,13 @@ void
|
|||
ResPrintExtNode(outextfile, nodelist, node)
|
||||
FILE *outextfile;
|
||||
resNode *nodelist;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
{
|
||||
char *nodename = node->name;
|
||||
int nodenum = 0;
|
||||
char newname[MAXNAME+32], tmpname[MAXNAME], *cp;
|
||||
HashEntry *entry;
|
||||
ResSimNode *newnode, *ResInitializeNode();
|
||||
ResExtNode *newnode, *ResInitializeNode();
|
||||
bool DoKillNode = TRUE;
|
||||
bool NeedFix = FALSE;
|
||||
resNode *snode;
|
||||
|
|
@ -411,14 +411,14 @@ ResPrintFHNodes(fp, nodelist, nodename, nidx, celldef)
|
|||
else
|
||||
{
|
||||
HashEntry *entry;
|
||||
ResSimNode *simnode;
|
||||
ResExtNode *simnode;
|
||||
|
||||
/* If we process another sim file node while doing this */
|
||||
/* one, mark it as status "REDUNDANT" so we don't duplicate */
|
||||
/* the entry. */
|
||||
|
||||
entry = HashFind(&ResNodeTable, nodeptr->rn_name);
|
||||
simnode = (ResSimNode *)HashGetValue(entry);
|
||||
simnode = (ResExtNode *)HashGetValue(entry);
|
||||
if (simnode != NULL)
|
||||
simnode->status |= REDUNDANT;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
|
||||
#ifndef lint
|
||||
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResReadSim.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
||||
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResReadExt.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
||||
#endif /* not lint */
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResReadSim.c -- Routines to parse .sim files
|
||||
* ResReadExt.c -- Routines to parse .ext files for information needed
|
||||
* by extresist.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
|
@ -35,8 +36,9 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#include "textio/txcommands.h"
|
||||
#include "resis/resis.h"
|
||||
|
||||
/* constants defining where various fields can be found in .ext files. */
|
||||
/* (FIXME---Needs to be changed to positions in .ext files) */
|
||||
|
||||
/* constants defining where various fields can be found in .sim files. */
|
||||
#define RDEV_LENGTH 4
|
||||
#define RDEV_WIDTH 5
|
||||
#define RDEV_DEVX 6
|
||||
|
|
@ -70,163 +72,99 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#define RES_EXT_ATTR_TILE 6
|
||||
#define RES_EXT_ATTR_TEXT 7
|
||||
|
||||
|
||||
#define MAXTOKEN 1024
|
||||
#define MAXLINE 40
|
||||
#define MAXDIGIT 20
|
||||
|
||||
ResExtNode *ResInitializeNode();
|
||||
|
||||
ResSimNode *ResInitializeNode();
|
||||
|
||||
ResSimNode *ResOriginalNodes; /*Linked List of Nodes */
|
||||
static float resscale=1.0; /* Scale factor */
|
||||
char RDEV_NOATTR[1]={'0'};
|
||||
ResFixPoint *ResFixList;
|
||||
|
||||
#define nodeinit(n)\
|
||||
{\
|
||||
(n)->rn_more = ResNodeList;\
|
||||
(n)->rn_less = NULL;\
|
||||
if (ResNodeList)\
|
||||
ResNodeList->rn_less = n;\
|
||||
ResNodeList = n;\
|
||||
(n)->rn_te = NULL;\
|
||||
(n)->rn_re = NULL;\
|
||||
(n)->rn_je = NULL;\
|
||||
(n)->rn_ce = NULL;\
|
||||
(n)->rn_noderes = RES_INFINITY;\
|
||||
(n)->location.p_x = MINFINITY;\
|
||||
(n)->location.p_y = MINFINITY;\
|
||||
(n)->rn_why = 0;\
|
||||
(n)->rn_status = TRUE;\
|
||||
}
|
||||
|
||||
/* Forward declarations */
|
||||
|
||||
extern void ResSimProcessDrivePoints();
|
||||
ResExtNode *ResOriginalNodes; /*Linked List of Nodes */
|
||||
static float resscale = 1.0; /* Scale factor */
|
||||
char RDEV_NOATTR[1] = {'0'};
|
||||
ResFixPoint *ResFixList;
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResReadSim--
|
||||
* ResReadExt--
|
||||
*
|
||||
* Results: returns 0 if sim file is correct, 1 if not.
|
||||
* Results: returns 0 if ext file is correct, 1 if not.
|
||||
*
|
||||
* Side Effects:Reads in SimTable and makes a hash table of nodes.
|
||||
* Side Effects:Reads in ExtTable and makes a hash table of nodes.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResReadSim(simfile, fetproc, capproc, resproc, attrproc, mergeproc, subproc)
|
||||
char *simfile;
|
||||
int (*fetproc)(), (*capproc)(), (*resproc)();
|
||||
int (*attrproc)(), (*mergeproc)(), (*subproc)();
|
||||
|
||||
ResReadExt(char *extfile);
|
||||
{
|
||||
char line[MAXLINE][MAXTOKEN];
|
||||
int result, fettype, extfile;
|
||||
FILE *fp, *fopen();
|
||||
char *line = NULL, *argv[128];
|
||||
int result, fettype, readdrivepoints;
|
||||
int size = 0;
|
||||
FILE *fp;
|
||||
|
||||
fp = PaOpen(simfile, "r", ".sim", ".", (char *)NULL, (char **)NULL);
|
||||
fp = PaOpen(extfile, "r", ".ext", EFSearchPath, EFLibPath, (char **)NULL);
|
||||
if (fp == NULL)
|
||||
{
|
||||
TxError("Cannot open file %s%s\n", simfile, ".sim");
|
||||
TxError("Cannot open file %s%s\n", extfile, ".ext");
|
||||
return 1;
|
||||
}
|
||||
extfile = 0;
|
||||
readdrivepoints = FALSE;
|
||||
|
||||
/* Read in file */
|
||||
while (gettokens(line, fp) != 0)
|
||||
/* Read in the file. Makes use of various functions
|
||||
* from extflat, mostly in EFread.c.
|
||||
*/
|
||||
|
||||
EFSaveLocs = False;
|
||||
efReadLineNum = 0;
|
||||
|
||||
while ((argc = efReadLine(&line, &size, fp, argv)) >= 0)
|
||||
{
|
||||
fettype = MINFINITY;
|
||||
switch(line[0][0])
|
||||
n = LookupStruct(argv[0], (const LookupTable *)keyTable, sizeof keyTable[0]);
|
||||
if (n < 0)
|
||||
{
|
||||
case '|':
|
||||
if (strcmp(line[NODEUNITS],"units:") == 0)
|
||||
{
|
||||
resscale = (float)atof(line[NODELAMBDA]);
|
||||
if (resscale == 0.0) resscale = 1.0;
|
||||
}
|
||||
result=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;
|
||||
}
|
||||
|
||||
/* We don't care about most tokens, only DEVICE, NODE, PORT,
|
||||
* and SUBSTRATE; and MERGE is used to locate drive points.
|
||||
*/
|
||||
switch (keyTable[n].k_key)
|
||||
{
|
||||
case SCALE:
|
||||
resscale = (float)atof(argv[1]);
|
||||
if (resscale == 0.0) resscale = 1.0;
|
||||
break;
|
||||
case 'e':
|
||||
fettype = DBTechNameType("efet");
|
||||
break;
|
||||
case 'd':
|
||||
fettype = DBTechNameType("dfet");
|
||||
case DEVICE:
|
||||
ResReadSubckt(argc, argv);
|
||||
break;
|
||||
case 'n':
|
||||
fettype = DBTechNameType("nfet");
|
||||
case FET:
|
||||
ResReadDevice(argc, argv);
|
||||
break;
|
||||
case 'p':
|
||||
fettype = DBTechNameType("pfet");
|
||||
case MERGE:
|
||||
ResReadDrivePoint(argc, argv);
|
||||
break;
|
||||
case 'b':
|
||||
fettype = DBTechNameType("bnpn");
|
||||
case NODE:
|
||||
case SUBSTRATE:
|
||||
ResReadNode(argc, argv);
|
||||
break;
|
||||
case 'C':
|
||||
if (capproc) result = (*capproc)(line);
|
||||
case PORT:
|
||||
ResReadPort(argc, argv);
|
||||
break;
|
||||
case 'R':
|
||||
if (resproc) result = (*resproc)(line);
|
||||
break;
|
||||
case '=':
|
||||
/* Do not merge nodes, as this interferes with */
|
||||
/* extresist's primary function. */
|
||||
/* if (mergeproc) result = (*mergeproc)(line); */
|
||||
break;
|
||||
case 'A':
|
||||
if (attrproc)
|
||||
{
|
||||
result = (*attrproc)(line[ATTRIBUTENODENAME],
|
||||
line[ATTRIBUTEVALUE], simfile, &extfile);
|
||||
}
|
||||
break;
|
||||
case 'x':
|
||||
fettype = DBNumTypes;
|
||||
break;
|
||||
case 'D':
|
||||
case 'c':
|
||||
case 'r':
|
||||
case ATTR:
|
||||
result = ResExtAttribute(curnodename,
|
||||
argv[1], &readdrivepoints);
|
||||
break;
|
||||
default:
|
||||
/* we expect fclose(fp) and early return to occur below,
|
||||
* but code path might still modify result (maybe to zero)
|
||||
* allowing us to loop again, which will then access 'fp'
|
||||
*/
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
if ((fettype != MINFINITY) && (fettype < 0))
|
||||
{
|
||||
TxError("Error in Reading device line of sim file: ");
|
||||
TxError("Ambiguous or unknown device.\n");
|
||||
result = 1;
|
||||
}
|
||||
else if (fettype == DBNumTypes)
|
||||
{
|
||||
result = (*subproc)(line);
|
||||
}
|
||||
else if (fettype != MINFINITY)
|
||||
{
|
||||
float sheetr;
|
||||
ExtDevice *devptr;
|
||||
HashEntry *he;
|
||||
|
||||
devptr = ExtCurStyle->exts_device[fettype];
|
||||
he = HashLookOnly(&devptr->exts_deviceResist, "linear");
|
||||
if (he != NULL)
|
||||
sheetr = (ResValue)(spointertype)HashGetValue(he);
|
||||
else
|
||||
sheetr = (ResValue)0.0;
|
||||
result = (*fetproc)(line, sheetr, devptr);
|
||||
}
|
||||
if (result != 0)
|
||||
{
|
||||
TxError("Error in sim file %s\n", line[0]);
|
||||
fclose(fp);
|
||||
return 1;
|
||||
/* to do: Handle CAP, RESISTOR, maybe others? */
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
|
@ -240,112 +178,45 @@ ResReadSim(simfile, fetproc, capproc, resproc, attrproc, mergeproc, subproc)
|
|||
* ResReadNode-- Reads in a node file, puts location of nodes into node
|
||||
* structures.
|
||||
*
|
||||
* Results: returns 0 if nodes file is correct, 1 if not.
|
||||
* Results: 0 if the node was read correctly, 1 otherwise.
|
||||
*
|
||||
* Side Effects:see above
|
||||
* Side Effects: see above
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResReadNode(nodefile)
|
||||
char *nodefile;
|
||||
ResReadNode(int argc, char *argv[])
|
||||
{
|
||||
char line[MAXLINE][MAXTOKEN];
|
||||
FILE *fp, *fopen();
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
char *cp;
|
||||
float lambda;
|
||||
|
||||
fp = PaOpen(nodefile, "r", ".nodes", ".", (char *)NULL, (char **)NULL);
|
||||
if (fp == NULL)
|
||||
entry = HashFind(&ResNodeTable, line[NODES_NODENAME]);
|
||||
node = ResInitializeNode(entry);
|
||||
|
||||
node->location.p_x = atoi(line[NODES_NODEX]);
|
||||
node->location.p_y = atoi(line[NODES_NODEY]);
|
||||
if ((cp = strchr(line[NODES_NODETYPE], ';'))) *cp = '\0';
|
||||
node->type = DBTechNameType(line[NODES_NODETYPE]);
|
||||
|
||||
if (node->type == -1)
|
||||
{
|
||||
TxError("Cannot open file %s%s\n", nodefile, ".nodes");
|
||||
TxError("Bad tile type name in .ext file for node %s\n",
|
||||
node->name);
|
||||
return 1;
|
||||
}
|
||||
while (gettokens(line,fp) != 0)
|
||||
{
|
||||
entry = HashFind(&ResNodeTable, line[NODES_NODENAME]);
|
||||
node = ResInitializeNode(entry);
|
||||
|
||||
node->location.p_x = atoi(line[NODES_NODEX]);
|
||||
node->location.p_y = atoi(line[NODES_NODEY]);
|
||||
#ifdef ARIEL
|
||||
node->rs_bbox.r_xbot = atoi(line[NODE_BBOX_LL_X]);
|
||||
node->rs_bbox.r_ybot = atoi(line[NODE_BBOX_LL_Y]);
|
||||
node->rs_bbox.r_xtop = atoi(line[NODE_BBOX_UR_X]);
|
||||
node->rs_bbox.r_ytop = atoi(line[NODE_BBOX_UR_Y]);
|
||||
#endif
|
||||
if ((cp = strchr(line[NODES_NODETYPE], ';'))) *cp = '\0';
|
||||
node->type = DBTechNameType(line[NODES_NODETYPE]);
|
||||
|
||||
if (node->type == -1)
|
||||
{
|
||||
TxError("Bad tile type name in %s.nodes file for node %s\n",
|
||||
nodefile, node->name);
|
||||
TxError("Did you use the newest version of ext2sim?\n");
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* getline-- Gets a line from the current input file and breaks it into
|
||||
* tokens.
|
||||
*
|
||||
* Results:returns the number of tokens in the current line
|
||||
*
|
||||
* Side Effects: loads up its input line with the tokens.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
gettokens(line, fp)
|
||||
char line[][MAXTOKEN];
|
||||
FILE *fp;
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
int c;
|
||||
|
||||
while ((c = getc(fp)) != EOF && c != '\n')
|
||||
{
|
||||
switch(c)
|
||||
{
|
||||
case ' ':
|
||||
case ' ' :
|
||||
line[i++][j] = '\0';
|
||||
j = 0;
|
||||
break;
|
||||
default:
|
||||
line[i][j++] = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == '\n')
|
||||
{
|
||||
line[i++][j] = '\0';
|
||||
j = 0;
|
||||
}
|
||||
for (j = i; j < MAXLINE; j++)
|
||||
line[j][0] = '\0';
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimSubckt-- Processes a subcircuit line from a sim file.
|
||||
* ResReadSubckt-- Processes a subcircuit line from a ext file.
|
||||
* This uses the "user subcircuit" extension defined in
|
||||
* IRSIM, although it is mostly intended as a way to work
|
||||
* around the device type limitations of the .sim format
|
||||
* around the device type limitations of the .ext format
|
||||
* when using extresist.
|
||||
*
|
||||
* Results: returns 0 if line was added correctly.
|
||||
|
|
@ -356,7 +227,7 @@ gettokens(line, fp)
|
|||
*/
|
||||
|
||||
int
|
||||
ResSimSubckt(line)
|
||||
ResReadSubckt(line)
|
||||
char line[][MAXTOKEN];
|
||||
{
|
||||
RDev *device;
|
||||
|
|
@ -473,7 +344,7 @@ ResSimSubckt(line)
|
|||
TxError("Device %s has more than 4 ports (not handled).\n", line[i]);
|
||||
break; /* No method to handle more ports than this */
|
||||
}
|
||||
rvalue += ResSimNewNode(line[k], k, device);
|
||||
rvalue += ResExtNewNode(line[k], k, device);
|
||||
}
|
||||
|
||||
return rvalue;
|
||||
|
|
@ -482,7 +353,7 @@ ResSimSubckt(line)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimDevice-- Processes a device line from a sim file.
|
||||
* ResReadDevice-- Processes a device line from a ext file.
|
||||
*
|
||||
* Results: returns 0 if line was added correctly.
|
||||
*
|
||||
|
|
@ -492,7 +363,7 @@ ResSimSubckt(line)
|
|||
*/
|
||||
|
||||
int
|
||||
ResSimDevice(line, rpersquare, devptr)
|
||||
ResReadDevice(line, rpersquare, devptr)
|
||||
char line[][MAXTOKEN];
|
||||
float rpersquare;
|
||||
ExtDevice *devptr;
|
||||
|
|
@ -554,7 +425,7 @@ ResSimDevice(line, rpersquare, devptr)
|
|||
|
||||
device->rs_ttype = extGetDevType(devptr->exts_deviceName);
|
||||
|
||||
/* sim attributes look like g=a1,a2 */
|
||||
/* ext attributes look like g=a1,a2 */
|
||||
/* ext attributes are "a1","a2" */
|
||||
/* Do conversion from one to the other here */
|
||||
/* NOTE: As of version 8.3.366, .ext attributes will end in two */
|
||||
|
|
@ -615,9 +486,9 @@ ResSimDevice(line, rpersquare, devptr)
|
|||
}
|
||||
ResRDevList = device;
|
||||
device->layout = NULL;
|
||||
rvalue = ResSimNewNode(line[GATE], GATE, device) +
|
||||
ResSimNewNode(line[SOURCE], SOURCE, device) +
|
||||
ResSimNewNode(line[DRAIN], DRAIN, device);
|
||||
rvalue = ResExtNewNode(line[GATE], GATE, device) +
|
||||
ResExtNewNode(line[SOURCE], SOURCE, device) +
|
||||
ResExtNewNode(line[DRAIN], DRAIN, device);
|
||||
|
||||
return rvalue;
|
||||
}
|
||||
|
|
@ -625,24 +496,24 @@ ResSimDevice(line, rpersquare, devptr)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimNewNode-- Adds a new node to the Node Hash Table.
|
||||
* ResExtNewNode-- Adds a new node to the Node Hash Table.
|
||||
*
|
||||
* Results: returns zero if node is added correctly, one otherwise.
|
||||
*
|
||||
* Side Effects: Allocates a new ResSimNode
|
||||
* Side Effects: Allocates a new ResExtNode
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResSimNewNode(line, type, device)
|
||||
ResExtNewNode(line, type, device)
|
||||
char line[];
|
||||
int type;
|
||||
RDev *device;
|
||||
|
||||
{
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
devPtr *tptr;
|
||||
|
||||
if (line[0] == '\0')
|
||||
|
|
@ -681,25 +552,25 @@ ResSimNewNode(line, type, device)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimCapacitor-- Adds the capacitance from a C line to the appropriate
|
||||
* ResReadCapacitor-- Adds the capacitance from a C line to the appropriate
|
||||
* node. Coupling capacitors are added twice, moving the capacitance
|
||||
* to the substrate.
|
||||
*
|
||||
* Results:
|
||||
* Always return 0
|
||||
*
|
||||
* Side Effects: modifies capacitance field of ResSimNode.
|
||||
* Side Effects: modifies capacitance field of ResExtNode.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResSimCapacitor(line)
|
||||
ResReadCapacitor(line)
|
||||
char line[][MAXTOKEN];
|
||||
|
||||
{
|
||||
HashEntry *entry1, *entry2;
|
||||
ResSimNode *node1, *node2;
|
||||
ResExtNode *node1, *node2;
|
||||
|
||||
if (line[COUPLETERMINAL1][0] == 0 || line[COUPLETERMINAL2][0] == 0)
|
||||
{
|
||||
|
|
@ -752,23 +623,23 @@ ResSimCapacitor(line)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimResistor-- Adds the capacitance from a R line to the appropriate
|
||||
* ResReadResistor-- Adds the capacitance from a R line to the appropriate
|
||||
* node.
|
||||
*
|
||||
* Results
|
||||
* Return 0 to keep search going, 1 to abort
|
||||
*
|
||||
* Side Effects: modifies resistance field of ResSimNode
|
||||
* Side Effects: modifies resistance field of ResExtNode
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResSimResistor(line)
|
||||
ResReadResistor(line)
|
||||
char line[][MAXTOKEN];
|
||||
{
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
|
||||
if (line[RESNODENAME][0] == 0)
|
||||
{
|
||||
|
|
@ -789,29 +660,30 @@ ResSimResistor(line)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimAttribute--checks to see if a node attribute is a resistance
|
||||
* ResExtAttribute--checks to see if a node attribute is a resistance
|
||||
* attribute. If it is, add it to the correct node's status flag.
|
||||
* Only works with 5.0 1/line attributes
|
||||
*
|
||||
* Results:
|
||||
* Return 0 to keep search going, 1 to abort
|
||||
*
|
||||
* Side Effects: modifies resistance field of ResSimNode
|
||||
* Side Effects: modifies resistance field of ResExtNode
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResSimAttribute(aname, avalue, rootname, readextfile)
|
||||
char *aname, *avalue, *rootname;
|
||||
int *readextfile;
|
||||
ResReadAttribute(aname, avalue, readdrivepoints)
|
||||
char *aname;
|
||||
char *avalue;
|
||||
bool *readdrivepoints;
|
||||
|
||||
{
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
char digit[MAXDIGIT];
|
||||
int i;
|
||||
static int notwarned=TRUE;
|
||||
static int notwarned = TRUE;
|
||||
|
||||
if (aname[0] == 0)
|
||||
{
|
||||
|
|
@ -834,18 +706,14 @@ ResSimAttribute(aname, avalue, rootname, readextfile)
|
|||
else if (strncmp(avalue, "res:force", 9) == 0)
|
||||
{
|
||||
if (node->status & SKIP)
|
||||
{
|
||||
TxError("Warning: Node %s is both skipped and forced \n", aname);
|
||||
}
|
||||
else
|
||||
{
|
||||
node->status |= FORCE;
|
||||
}
|
||||
}
|
||||
else if (strncmp(avalue, "res:min=", 8) == 0)
|
||||
{
|
||||
node->status |= MINSIZE;
|
||||
for (i = 0, avalue += 8; *avalue != '\0' && *avalue != ','; avalue++)
|
||||
for (i = 0, avalue += 8; *avalue != '\0'; avalue++)
|
||||
{
|
||||
digit[i++] = *avalue;
|
||||
}
|
||||
|
|
@ -855,16 +723,14 @@ ResSimAttribute(aname, avalue, rootname, readextfile)
|
|||
else if (strncmp(avalue, "res:drive", 9) == 0 &&
|
||||
(ResOptionsFlags & ResOpt_Signal))
|
||||
{
|
||||
if (*readextfile == 0)
|
||||
if (*readdrivepoints == FALSE)
|
||||
{
|
||||
ResSimProcessDrivePoints(rootname);
|
||||
*readextfile = 1;
|
||||
ResExtProcessDrivePoints(rootname);
|
||||
*readddrivepoints = TRUE;
|
||||
}
|
||||
/* is the attribute in root.ext? */
|
||||
if (node->drivepoint.p_x != INFINITY)
|
||||
{
|
||||
node->status |= DRIVELOC;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (notwarned)
|
||||
|
|
@ -873,34 +739,22 @@ ResSimAttribute(aname, avalue, rootname, readextfile)
|
|||
notwarned = FALSE;
|
||||
}
|
||||
}
|
||||
#ifdef ARIEL
|
||||
else if (strncmp(avalue, "res:fix", 7) == 0 &&
|
||||
(ResOptionsFlags & ResOpt_Power))
|
||||
{
|
||||
if (*readextfile == 0)
|
||||
{
|
||||
ResSimProcessFixPoints(rootname);
|
||||
*readextfile = 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if ((avalue = strchr(avalue, ',')))
|
||||
{
|
||||
ResSimAttribute(aname, avalue + 1, rootname, readextfile);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimProcessDrivePoints -- if the sim file contains a res:drive attribute,
|
||||
* ResExtProcessDrivePoints -- if the ext file contains a res:drive attribute,
|
||||
* and we are doing a signal extraction,
|
||||
* we need to search through the .ext file looking for attr labels that
|
||||
* contain this text. For efficiency, the .ext file is only parsed when
|
||||
* the first res:drive is encountered. res:drive labels only work if
|
||||
* they are in the root cell.
|
||||
*
|
||||
* FIXME---The .ext file is being read and this routine should only
|
||||
* read in additional lines as necessary.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
|
|
@ -910,23 +764,14 @@ ResSimAttribute(aname, avalue, rootname, readextfile)
|
|||
*/
|
||||
|
||||
void
|
||||
ResSimProcessDrivePoints(filename)
|
||||
char *filename;
|
||||
|
||||
ResExtProcessDrivePoints(FILE *fp)
|
||||
{
|
||||
char line[MAXLINE][MAXTOKEN];
|
||||
FILE *fp;
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
|
||||
fp = PaOpen(filename, "r", ".ext", (ExtLocalPath == NULL) ? "." : ExtLocalPath,
|
||||
(char *)NULL, (char **)NULL);
|
||||
if (fp == NULL)
|
||||
{
|
||||
TxError("Cannot open file %s%s\n", filename, ".ext");
|
||||
return;
|
||||
}
|
||||
while (gettokens(line,fp) != 0)
|
||||
while (gettokens(line, fp) != 0)
|
||||
{
|
||||
if (strncmp(line[RES_EXT_ATTR], "attr", 4) != 0 ||
|
||||
strncmp(line[RES_EXT_ATTR_TEXT], "\"res:drive\"", 11) != 0)
|
||||
|
|
@ -944,11 +789,13 @@ ResSimProcessDrivePoints(filename)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimProcessFixPoints -- if the sim file contains a "res:fix:name" label
|
||||
* ResExtProcessFixPoints -- if the ext file contains a "res:fix:name" label
|
||||
* and we are checking for power supply noise, then we have to
|
||||
* parse the .ext file looking for the fix label locations. This
|
||||
* is only done after the first res:fix label is encountered.
|
||||
*
|
||||
* (FIXME---This is now all in the .ext file so the information does
|
||||
* not need to be read twice.)
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
|
|
@ -960,7 +807,7 @@ ResSimProcessDrivePoints(filename)
|
|||
*/
|
||||
|
||||
void
|
||||
ResSimProcessFixPoints(filename)
|
||||
ResExtProcessFixPoints(filename)
|
||||
char *filename;
|
||||
{
|
||||
char line[MAXLINE][MAXTOKEN], *label, *c;
|
||||
|
|
@ -1006,72 +853,31 @@ ResSimProcessFixPoints(filename)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResSimMerge-- Processes = line in sim file
|
||||
* ResInitializeNode --
|
||||
* Gets the node corresponding to a given hash table entry. If no
|
||||
* such node exists, one is created.
|
||||
*
|
||||
* Results: Success/Failure
|
||||
* Results: Returns ResExtNode corresponding to entry.
|
||||
*
|
||||
* Side Effects: The forward field of one node is set to point to the
|
||||
* other node. All of the junkt from the first node is moved to
|
||||
* the second node.
|
||||
* Side Effects: May allocate a new ResExtNode.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
int
|
||||
ResSimMerge(line)
|
||||
char line[][MAXTOKEN];
|
||||
|
||||
{
|
||||
ResSimNode *node;
|
||||
devPtr *ptr;
|
||||
|
||||
if ((line[ALIASNAME][0] == '\0') || (line[REALNAME][0] == '\0'))
|
||||
{
|
||||
TxError("Bad node alias line\n");
|
||||
return(1);
|
||||
}
|
||||
node = ResInitializeNode(HashFind(&ResNodeTable, line[ALIASNAME]));
|
||||
node->status |= FORWARD;
|
||||
node->forward = ResInitializeNode(HashFind(&ResNodeTable, line[REALNAME]));
|
||||
node->forward->resistance += node->resistance;
|
||||
node->forward->capacitance += node->capacitance;
|
||||
while (node->firstDev != NULL)
|
||||
{
|
||||
ptr = node->firstDev;
|
||||
node->firstDev = node->firstDev->nextDev;
|
||||
ptr->nextDev = node->forward->firstDev;
|
||||
node->forward->firstDev = ptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResInitializeNode-- Gets the node corresponding to a given hash table
|
||||
* entry. If no such node exists, one is created.
|
||||
*
|
||||
* Results:Returns ResSimNode corresponding to entry.
|
||||
*
|
||||
* Side Effects: May allocate a new ResSimNode.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
ResSimNode *
|
||||
ResExtNode *
|
||||
ResInitializeNode(entry)
|
||||
HashEntry *entry;
|
||||
{
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
|
||||
if ((node = (ResSimNode *) HashGetValue(entry)) == NULL)
|
||||
if ((node = (ResExtNode *) HashGetValue(entry)) == NULL)
|
||||
{
|
||||
node = (ResSimNode *)mallocMagic((unsigned)(sizeof(ResSimNode)));
|
||||
node = (ResExtNode *)mallocMagic((unsigned)(sizeof(ResExtNode)));
|
||||
HashSetValue(entry, (char *) node);
|
||||
node->nextnode = ResOriginalNodes;
|
||||
ResOriginalNodes = node;
|
||||
node->status = FALSE;
|
||||
node->forward = (ResSimNode *) NULL;
|
||||
node->forward = (ResExtNode *) NULL;
|
||||
node->capacitance = 0;
|
||||
node->cap_vdd = 0;
|
||||
node->cap_couple = 0;
|
||||
156
resis/ResRex.c
156
resis/ResRex.c
|
|
@ -31,9 +31,6 @@ static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/
|
|||
#include "utils/tech.h"
|
||||
#include "textio/txcommands.h"
|
||||
#include "resis/resis.h"
|
||||
#ifdef LAPLACE
|
||||
#include "laplace.h"
|
||||
#endif
|
||||
|
||||
#define INITFLATSIZE 1024
|
||||
#define MAXNAME 1000
|
||||
|
|
@ -56,22 +53,17 @@ HashTable ResIncludeTable; /* Hash table of nodes to include */
|
|||
|
||||
HashTable ResProcessedTable;
|
||||
|
||||
/* ResSimNode is a node read in from a sim file */
|
||||
/* ResExtNode is a node read in from a .ext file */
|
||||
|
||||
HashTable ResNodeTable; /* Hash table of sim file nodes */
|
||||
RDev *ResRDevList; /* Linked list of Sim devices */
|
||||
HashTable ResNodeTable; /* Hash table of ext file nodes */
|
||||
RDev *ResRDevList; /* Linked list of Ext devices */
|
||||
ResGlobalParams gparams; /* Junk passed between */
|
||||
/* ResCheckSimNodes and */
|
||||
/* ResCheckExtNodes and */
|
||||
/* ResExtractNet. */
|
||||
extern ResSimNode *ResOriginalNodes; /*Linked List of Nodes */
|
||||
extern ResExtNode *ResOriginalNodes; /*Linked List of Nodes */
|
||||
int resNodeNum;
|
||||
|
||||
#ifdef LAPLACE
|
||||
int ResOptionsFlags = ResOpt_Simplify | ResOpt_Tdi | ResOpt_DoExtFile
|
||||
| ResOpt_CacheLaplace;
|
||||
#else
|
||||
int ResOptionsFlags = ResOpt_Simplify | ResOpt_Tdi | ResOpt_DoExtFile;
|
||||
#endif
|
||||
char *ResCurrentNode;
|
||||
|
||||
FILE *ResExtFile;
|
||||
|
|
@ -81,29 +73,9 @@ FILE *ResFHFile;
|
|||
int ResPortIndex; /* Port ordering to backannotate into magic */
|
||||
|
||||
/* external declarations */
|
||||
extern ResSimNode *ResInitializeNode();
|
||||
extern ResExtNode *ResInitializeNode();
|
||||
extern CellUse *CmdGetSelectedCell();
|
||||
|
||||
/* Linked list structure to use to store the substrate plane from each */
|
||||
/* extracted CellDef so that they can be returned to the original after */
|
||||
/* extraction. */
|
||||
|
||||
struct saveList {
|
||||
Plane *sl_plane;
|
||||
CellDef *sl_def;
|
||||
struct saveList *sl_next;
|
||||
};
|
||||
|
||||
/* Structure stores information required to be sent to ExtResisForDef() */
|
||||
typedef struct
|
||||
{
|
||||
float tdiTolerance;
|
||||
float frequency;
|
||||
float rthresh;
|
||||
struct saveList *savePlanes;
|
||||
CellDef *mainDef;
|
||||
} ResisData;
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
|
|
@ -123,7 +95,7 @@ ExtResisForDef(celldef, resisdata)
|
|||
HashSearch hs;
|
||||
HashEntry *entry;
|
||||
devPtr *tptr, *oldtptr;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
int result, idx;
|
||||
char *devname;
|
||||
Plane *savePlane;
|
||||
|
|
@ -160,10 +132,8 @@ ExtResisForDef(celldef, resisdata)
|
|||
}
|
||||
|
||||
HashInit(&ResNodeTable, INITFLATSIZE, HT_STRINGKEYS);
|
||||
/* read in .sim file */
|
||||
result = (ResReadSim(celldef->cd_name,
|
||||
ResSimDevice, ResSimCapacitor, ResSimResistor,
|
||||
ResSimAttribute, ResSimMerge, ResSimSubckt) == 0);
|
||||
/* Read in the .ext file */
|
||||
result = (ResReadExt(celldef->cd_name) == 0);
|
||||
|
||||
/* Clean up the EFDevTypes table */
|
||||
for (idx = 0; idx < EFDevNumTypes; idx++) freeMagic(EFDevTypes[idx]);
|
||||
|
|
@ -184,7 +154,7 @@ ExtResisForDef(celldef, resisdata)
|
|||
/* Extract networks for nets that require it. */
|
||||
if (!(ResOptionsFlags & ResOpt_FastHenry) ||
|
||||
DBIsSubcircuit(celldef))
|
||||
ResCheckSimNodes(celldef, resisdata);
|
||||
ResCheckExtNodes(celldef, resisdata);
|
||||
|
||||
if (ResOptionsFlags & ResOpt_Stat)
|
||||
ResPrintStats((ResGlobalParams *)NULL, "");
|
||||
|
|
@ -195,7 +165,7 @@ ExtResisForDef(celldef, resisdata)
|
|||
HashStartSearch(&hs);
|
||||
while((entry = HashNext(&ResNodeTable, &hs)) != NULL)
|
||||
{
|
||||
node=(ResSimNode *) HashGetValue(entry);
|
||||
node=(ResExtNode *) HashGetValue(entry);
|
||||
tptr = node->firstDev;
|
||||
if (node == NULL)
|
||||
{
|
||||
|
|
@ -227,14 +197,13 @@ ExtResisForDef(celldef, resisdata)
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* CmdExtResis-- reads in sim file and layout, and produces patches to the
|
||||
* .ext files and .sim files that include resistors.
|
||||
* CmdExtResis-- reads in ext file and layout, and produces patches to the
|
||||
* .ext files that include resistors.
|
||||
*
|
||||
* Results:
|
||||
* None.
|
||||
*
|
||||
* Side Effects: Produces .res.sim file and .res.ext file for all nets that
|
||||
* require resistors.
|
||||
* Side Effects: Produces .res.ext file for all nets that require resistors.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
|
@ -281,9 +250,6 @@ CmdExtResis(win, cmd)
|
|||
"fasthenry [freq] extract subcircuit network geometry into .fh file",
|
||||
"geometry extract network centerline geometry (experimental)",
|
||||
"help print this message",
|
||||
#ifdef LAPLACE
|
||||
"laplace [on/off] solve Laplace's equation using FEM",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -293,9 +259,6 @@ typedef enum {
|
|||
RES_SIMP, RES_EXTOUT, RES_LUMPED, RES_SILENT,
|
||||
RES_SKIP, RES_IGNORE, RES_INCLUDE, RES_BOX, RES_CELL,
|
||||
RES_BLACKBOX, RES_FASTHENRY, RES_GEOMETRY, RES_HELP,
|
||||
#ifdef LAPLACE
|
||||
RES_LAPLACE,
|
||||
#endif
|
||||
RES_RUN
|
||||
} ResOptions;
|
||||
|
||||
|
|
@ -582,7 +545,7 @@ typedef enum {
|
|||
CellDef *def;
|
||||
Rect rect;
|
||||
int oldoptions;
|
||||
ResSimNode lnode;
|
||||
ResExtNode lnode;
|
||||
|
||||
if (ToolGetBoxWindow((Rect *) NULL, (int *) NULL) == NULL)
|
||||
{
|
||||
|
|
@ -598,25 +561,11 @@ typedef enum {
|
|||
gparams.rg_status = DRIVEONLY;
|
||||
oldoptions = ResOptionsFlags;
|
||||
ResOptionsFlags = ResOpt_DoSubstrate | ResOpt_Signal | ResOpt_Box;
|
||||
#ifdef LAPLACE
|
||||
ResOptionsFlags |= (oldoptions &
|
||||
(ResOpt_CacheLaplace | ResOpt_DoLaplace));
|
||||
LaplaceMatchCount = 0;
|
||||
LaplaceMissCount = 0;
|
||||
#endif
|
||||
lnode.location = rect.r_ll;
|
||||
lnode.type = tt;
|
||||
if (ResExtractNet(&lnode, &gparams, NULL) != 0) return;
|
||||
ResPrintResistorList(stdout, ResResList);
|
||||
ResPrintDeviceList(stdout, ResRDevList);
|
||||
#ifdef LAPLACE
|
||||
if (ResOptionsFlags & ResOpt_DoLaplace)
|
||||
{
|
||||
TxPrintf("Laplace solved: %d matched %d\n",
|
||||
LaplaceMissCount, LaplaceMatchCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
ResOptionsFlags = oldoptions;
|
||||
return;
|
||||
}
|
||||
|
|
@ -634,11 +583,6 @@ typedef enum {
|
|||
case RES_RUN:
|
||||
ResOptionsFlags &= ~ResOpt_ExtractAll;
|
||||
break;
|
||||
#ifdef LAPLACE
|
||||
case RES_LAPLACE:
|
||||
LaplaceParseString(cmd);
|
||||
return;
|
||||
#endif
|
||||
case RES_AMBIG:
|
||||
TxPrintf("Ambiguous option: %s\n", cmd->tx_argv[1]);
|
||||
TxFlushOut();
|
||||
|
|
@ -651,10 +595,6 @@ typedef enum {
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef LAPLACE
|
||||
LaplaceMatchCount = 0;
|
||||
LaplaceMissCount = 0;
|
||||
#endif
|
||||
/* turn off undo stuff */
|
||||
UndoDisable();
|
||||
|
||||
|
|
@ -666,9 +606,6 @@ typedef enum {
|
|||
return;
|
||||
}
|
||||
ResOptionsFlags |= ResOpt_Signal;
|
||||
#ifdef ARIEL
|
||||
ResOptionsFlags &= ~ResOpt_Power;
|
||||
#endif
|
||||
|
||||
resisdata.rthresh = rthresh;
|
||||
resisdata.tdiTolerance = tdiTolerance;
|
||||
|
|
@ -695,13 +632,6 @@ typedef enum {
|
|||
|
||||
/* turn back on undo stuff */
|
||||
UndoEnable();
|
||||
#ifdef LAPLACE
|
||||
if (ResOptionsFlags & ResOpt_DoLaplace)
|
||||
{
|
||||
TxPrintf("Laplace solved: %d matched %d\n",
|
||||
LaplaceMissCount, LaplaceMatchCount);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Revert to the original flags in the case of FastHenry or */
|
||||
/* geometry centerline extraction. */
|
||||
|
|
@ -765,7 +695,7 @@ resPortFunc(scx, lab, tpath, result)
|
|||
int pclass, puse;
|
||||
Point portloc;
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
|
||||
// Ignore the top level cell
|
||||
if (scx->scx_use->cu_id == NULL) return 0;
|
||||
|
|
@ -904,7 +834,7 @@ ResCheckPorts(cellDef)
|
|||
Point portloc;
|
||||
Label *lab;
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
int result = 1;
|
||||
|
||||
for (lab = cellDef->cd_labels; lab; lab = lab->lab_next)
|
||||
|
|
@ -930,7 +860,7 @@ ResCheckPorts(cellDef)
|
|||
|
||||
entry = HashFind(&ResNodeTable, lab->lab_text);
|
||||
result = 0;
|
||||
if ((node = (ResSimNode *) HashGetValue(entry)) != NULL)
|
||||
if ((node = (ResExtNode *) HashGetValue(entry)) != NULL)
|
||||
{
|
||||
TxPrintf("Port: name = %s exists, forcing drivepoint\n",
|
||||
lab->lab_text);
|
||||
|
|
@ -966,34 +896,6 @@ ResCheckPorts(cellDef)
|
|||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResCreateNode ---
|
||||
*
|
||||
* Create a node structure for extresist for the method where
|
||||
* ResProcessNode() is called from extBasic() as part of the
|
||||
* "extract" command.
|
||||
*
|
||||
* Results:
|
||||
* Returns the node structure to be passed to ResProcessNode().
|
||||
*
|
||||
* Side effects:
|
||||
* Memory is allocated for the node structure.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
ResSimNode *ResCreateNode()
|
||||
{
|
||||
ResSimNode *node;
|
||||
|
||||
/* To be completed */
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
|
|
@ -1013,7 +915,7 @@ ResSimNode *ResCreateNode()
|
|||
|
||||
int
|
||||
ResProcessNode(
|
||||
ResSimNode *node, /* Node record for network */
|
||||
ResExtNode *node, /* Node record for network */
|
||||
CellDef *celldef, /* Cell def being processed */
|
||||
ResisData *resisdata, /* Extraction parameters kept here */
|
||||
char *outfile, /* Name of output file */
|
||||
|
|
@ -1186,7 +1088,7 @@ ResProcessNode(
|
|||
/*
|
||||
*-------------------------------------------------------------------------
|
||||
*
|
||||
* ResCheckSimNodes-- check to see if lumped resistance is greater than the
|
||||
* ResCheckExtNodes-- check to see if lumped resistance is greater than the
|
||||
* device resistance; if it is, Extract the net
|
||||
* resistance. If the maximum point to point resistance
|
||||
* in the extracted net is still creater than the
|
||||
|
|
@ -1194,17 +1096,17 @@ ResProcessNode(
|
|||
*
|
||||
* Results: none
|
||||
*
|
||||
* Side Effects: Writes networks to .res.ext and .res.sim files.
|
||||
* Side Effects: Writes networks to .res.ext files.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
void
|
||||
ResCheckSimNodes(celldef, resisdata)
|
||||
ResCheckExtNodes(celldef, resisdata)
|
||||
CellDef *celldef;
|
||||
ResisData *resisdata;
|
||||
{
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
int numext = 0; /* Number of nets extracted */
|
||||
int numout = 0; /* Number of nets output */
|
||||
int total = 0; /* Total number of nets processed */
|
||||
|
|
@ -1339,8 +1241,8 @@ ResCheckSimNodes(celldef, resisdata)
|
|||
*
|
||||
* Results:none
|
||||
*
|
||||
* Side Effects: Allocates new ResSimNodes. Modifies the terminal connections
|
||||
* of sim Devices.
|
||||
* Side Effects: Allocates new ResExtNodes. Modifies the terminal connections
|
||||
* of ext Devices.
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
|
@ -1349,7 +1251,7 @@ void
|
|||
ResFixUpConnections(simDev, layoutDev, simNode, nodename)
|
||||
RDev *simDev;
|
||||
resDevice *layoutDev;
|
||||
ResSimNode *simNode;
|
||||
ResExtNode *simNode;
|
||||
char *nodename;
|
||||
|
||||
{
|
||||
|
|
@ -1601,7 +1503,7 @@ ResFixDevName(line, type, device, layoutnode)
|
|||
|
||||
{
|
||||
HashEntry *entry;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
devPtr *tptr;
|
||||
|
||||
if (layoutnode->rn_name != NULL)
|
||||
|
|
@ -1757,7 +1659,7 @@ ResSortByGate(DevpointerList)
|
|||
|
||||
void
|
||||
ResWriteLumpFile(node)
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
{
|
||||
int lumpedres;
|
||||
|
||||
|
|
@ -1852,7 +1754,7 @@ ResAlignNodes(nodelist, reslist)
|
|||
int
|
||||
ResWriteExtFile(celldef, node, rctol, nidx, eidx)
|
||||
CellDef *celldef;
|
||||
ResSimNode *node;
|
||||
ResExtNode *node;
|
||||
float rctol;
|
||||
int *nidx, *eidx;
|
||||
{
|
||||
|
|
|
|||
|
|
@ -383,27 +383,17 @@ ResMoveDevices(node1, node2)
|
|||
device = devptr->te_thist;
|
||||
oldptr = devptr;
|
||||
devptr = devptr->te_nextt;
|
||||
if (device->rd_status & RES_DEV_PLUG)
|
||||
{
|
||||
if (((ResPlug *)(device))->rpl_node == node1)
|
||||
((ResPlug *)(device))->rpl_node = node2;
|
||||
else
|
||||
TxError("Bad node connection in plug\n");
|
||||
}
|
||||
if (device->rd_fet_gate == node1)
|
||||
device->rd_fet_gate = node2;
|
||||
else if (device->rd_fet_subs == node1)
|
||||
device->rd_fet_subs = node2;
|
||||
else if (device->rd_fet_source == node1)
|
||||
device->rd_fet_source = node2;
|
||||
else if (device->rd_fet_drain == node1)
|
||||
device->rd_fet_drain = node2;
|
||||
else
|
||||
{
|
||||
if (device->rd_fet_gate == node1)
|
||||
device->rd_fet_gate = node2;
|
||||
else if (device->rd_fet_subs == node1)
|
||||
device->rd_fet_subs = node2;
|
||||
else if (device->rd_fet_source == node1)
|
||||
device->rd_fet_source = node2;
|
||||
else if (device->rd_fet_drain == node1)
|
||||
device->rd_fet_drain = node2;
|
||||
else
|
||||
TxError("Missing Device connection in squish routines"
|
||||
TxError("Missing Device connection in squish routines"
|
||||
" at %d, %d\n", node1->rn_loc.p_x, node1->rn_loc.p_y);
|
||||
}
|
||||
oldptr->te_nextt = node2->rn_te;
|
||||
node2->rn_te = oldptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -140,9 +140,8 @@ resCurrentPrintFunc(node, resistor, filename)
|
|||
for (workingDev = node->rn_te; workingDev != NULL;
|
||||
workingDev = workingDev->te_nextt)
|
||||
{
|
||||
if ((workingDev->te_thist->rd_status & RES_DEV_PLUG) ||
|
||||
workingDev->te_thist->rd_gate != node)
|
||||
i_sum += workingDev->te_thist->rd_i;
|
||||
if (workingDev->te_thist->rd_gate != node)
|
||||
i_sum += workingDev->te_thist->rd_i;
|
||||
}
|
||||
if (i_sum != 0.0)
|
||||
{
|
||||
|
|
|
|||
164
resis/resis.h
164
resis/resis.h
|
|
@ -57,10 +57,6 @@ typedef struct resistor
|
|||
int rr_cl; /* resistor centerline for geometry */
|
||||
int rr_width; /* resistor width for geometry */
|
||||
TileType rr_tt; /* type that composes this */
|
||||
/* resistor. */
|
||||
#ifdef ARIEL
|
||||
int rr_csArea; /* crosssectional area in lamba**2*/
|
||||
#endif
|
||||
} resResistor;
|
||||
|
||||
#define rr_connection1 rr_node[0]
|
||||
|
|
@ -92,10 +88,6 @@ typedef struct device
|
|||
int rd_devtype; /* tiletype of device. */
|
||||
Rect rd_inside; /* 1x1 rectangle inside device */
|
||||
Tile *rd_tile; /* pointer to a tile in device */
|
||||
#ifdef ARIEL
|
||||
float rd_i; /* Current injected from this device */
|
||||
/* in milliamps */
|
||||
#endif
|
||||
} resDevice;
|
||||
|
||||
/*
|
||||
|
|
@ -254,7 +246,7 @@ typedef struct resdevtile
|
|||
|
||||
/*
|
||||
Goodies contains random stuff passed between the node extractor
|
||||
and ResCheckSimNodes. The location of a start tile and the resistive
|
||||
and ResCheckExtNodes. The location of a start tile and the resistive
|
||||
tolerance are passed down, while the derived network is passed back.
|
||||
*/
|
||||
|
||||
|
|
@ -271,7 +263,28 @@ typedef struct goodstuff
|
|||
char *rg_name;
|
||||
} ResGlobalParams;
|
||||
|
||||
/* Used in RC delay calculations for Tdi filter */
|
||||
/* Linked list structure to use to store the substrate plane from each */
|
||||
/* extracted CellDef so that they can be returned to the original after */
|
||||
/* extraction. */
|
||||
|
||||
struct saveList {
|
||||
Plane *sl_plane;
|
||||
CellDef *sl_def;
|
||||
struct saveList *sl_next;
|
||||
};
|
||||
|
||||
/* Structure stores information required to be sent to ExtResisForDef() */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float tdiTolerance;
|
||||
float frequency;
|
||||
float rthresh;
|
||||
struct saveList *savePlanes;
|
||||
CellDef *mainDef;
|
||||
} ResisData;
|
||||
|
||||
/* Structure used in RC delay calculations for Tdi filter. */
|
||||
/* Attaches to rn_client field of resNode */
|
||||
|
||||
typedef struct rcdelaystuff
|
||||
|
|
@ -281,7 +294,7 @@ typedef struct rcdelaystuff
|
|||
} RCDelayStuff;
|
||||
|
||||
|
||||
/* ResSim.c type declarations */
|
||||
/* Type declarations */
|
||||
|
||||
typedef struct rdev
|
||||
{
|
||||
|
|
@ -291,10 +304,10 @@ typedef struct rdev
|
|||
resDevice *layout; /* pointer to resDevice that */
|
||||
/* corresponds to RDev */
|
||||
int status;
|
||||
struct ressimnode *gate; /* Terminals of transistor. */
|
||||
struct ressimnode *source;
|
||||
struct ressimnode *drain;
|
||||
struct ressimnode *subs; /* Used with subcircuit type only */
|
||||
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. */
|
||||
|
|
@ -305,12 +318,12 @@ typedef struct rdev
|
|||
char *rs_dattr;
|
||||
} RDev;
|
||||
|
||||
typedef struct ressimnode
|
||||
typedef struct resextnode
|
||||
{
|
||||
struct ressimnode *nextnode; /* next node in OriginalNodes */
|
||||
struct resextnode *nextnode; /* next node in OriginalNodes */
|
||||
/* linked list. */
|
||||
int status;
|
||||
struct ressimnode *forward; /* If node has been merged, this */
|
||||
struct resextnode *forward; /* If node has been merged, this */
|
||||
/* points to the merged node. */
|
||||
float capacitance; /* capacitance between node and */
|
||||
/* GND for power connections */
|
||||
|
|
@ -340,7 +353,7 @@ typedef struct ressimnode
|
|||
tElement *rs_sublist[2]; /* pointers to Gnd and Vdd sub */
|
||||
/* strate connections,
|
||||
if they exist */
|
||||
} ResSimNode;
|
||||
} ResExtNode;
|
||||
|
||||
#define RES_SUB_GND 0
|
||||
#define RES_SUB_VDD 1
|
||||
|
|
@ -355,40 +368,6 @@ typedef struct devptr
|
|||
/* is connected to node. */
|
||||
} devPtr;
|
||||
|
||||
/* ResTime.c type declarations */
|
||||
|
||||
typedef struct resevent /* Raw event list read in from rsim/tv */
|
||||
{
|
||||
int rv_node; /* node number */
|
||||
int rv_final; /* final value; (0,1, or X) */
|
||||
int rv_tmin; /* minimum event time in units of 100ps */
|
||||
int rv_tmax; /* maximum event time in units of 100ps */
|
||||
float rv_i; /* event current in milliamps */
|
||||
resDevice *rv_dev; /* device where charge drains */
|
||||
} ResEvent;
|
||||
|
||||
typedef struct reseventcell
|
||||
{
|
||||
ResEvent *rl_this;
|
||||
struct reseventcell *rl_next;
|
||||
} REcell;
|
||||
|
||||
typedef struct rescurrentevent /* processed event used to feed relaxer */
|
||||
{
|
||||
struct rescurrentevent *ri_next;
|
||||
float ri_i;
|
||||
resDevice *ri_dev;
|
||||
} ResCurrentEvent;
|
||||
|
||||
typedef struct restimebin /* Holds one timestep's worth of Events */
|
||||
{
|
||||
struct restimebin *rb_next;
|
||||
struct restimebin *rb_last;
|
||||
int rb_start;
|
||||
int rb_end;
|
||||
ResCurrentEvent *rb_first;
|
||||
} ResTimeBin;
|
||||
|
||||
typedef struct resfixpoint /* Keeps track of where voltage sources are */
|
||||
{
|
||||
struct resfixpoint *fp_next;
|
||||
|
|
@ -400,30 +379,6 @@ typedef struct resfixpoint /* Keeps track of where voltage sources are */
|
|||
char fp_name[1];
|
||||
} ResFixPoint;
|
||||
|
||||
typedef struct clump
|
||||
{
|
||||
unsigned rp_status;
|
||||
rElement *rp_grouplist;
|
||||
nElement *rp_nodelist;
|
||||
rElement *rp_downlist;
|
||||
rElement *rp_singlelist;
|
||||
} ResClump;
|
||||
|
||||
/* the first two fields of this plug must be the the same as for
|
||||
resDevice
|
||||
*/
|
||||
typedef struct plug
|
||||
{
|
||||
float rpl_i; /* current injected through
|
||||
this plug
|
||||
*/
|
||||
int rpl_status; /* status bits for this plug */
|
||||
struct plug *rpl_next; /* next plug in this bin */
|
||||
Point rpl_loc; /*location of plug */
|
||||
int rpl_type; /*type of plug */
|
||||
resNode *rpl_node; /* this point's node */
|
||||
} ResPlug;
|
||||
|
||||
typedef struct capval
|
||||
{
|
||||
float cap[1][2]; /* multipliers telling what portion of capacitance is
|
||||
|
|
@ -458,7 +413,6 @@ typedef struct capval
|
|||
|
||||
/* device flags */
|
||||
#define RES_DEV_SAVE 0x00000001
|
||||
#define RES_DEV_PLUG 0x00000002
|
||||
|
||||
/* flags for tiles */
|
||||
/* A tile which is part of a substrate region. */
|
||||
|
|
@ -473,11 +427,6 @@ typedef struct capval
|
|||
#define RES_TILE_MARK 0x10
|
||||
/*another temporary marking flag */
|
||||
#define RES_TILE_PUSHED 0x20
|
||||
/* indicates that tile has unidirectional current flow */
|
||||
#ifdef LAPLACE
|
||||
#define RES_TILE_1D 0x40
|
||||
#define RES_TILE_GDONE 0x80
|
||||
#endif
|
||||
/* tree walking flags */
|
||||
#define RES_LOOP_OK 1
|
||||
#define RES_NO_LOOP 1
|
||||
|
|
@ -486,7 +435,7 @@ typedef struct capval
|
|||
#define RES_NO_FLAGS 0
|
||||
|
||||
|
||||
/* ResSim Constants */
|
||||
/* Constants */
|
||||
#define FORWARD 0x0000010
|
||||
#define SKIP 0x0000020
|
||||
#define FORCE 0x0000040
|
||||
|
|
@ -561,16 +510,9 @@ typedef struct capval
|
|||
#define ResOpt_Blackbox 0x00010000
|
||||
#define ResOpt_Dump 0x00020000
|
||||
#define ResOpt_DoSubstrate 0x00040000
|
||||
#define ResOpt_GndPlugs 0x00200000
|
||||
#define ResOpt_VddPlugs 0x00400000
|
||||
#define ResOpt_CMOS 0x00800000
|
||||
#define ResOpt_Bipolar 0x01000000
|
||||
#define ResOpt_Box 0x02000000
|
||||
#ifdef LAPLACE
|
||||
#define ResOpt_DoLaplace 0x04000000
|
||||
#define ResOpt_CacheLaplace 0x08000000
|
||||
#define ResOpt_Checkpoint 0x80000000
|
||||
#endif
|
||||
|
||||
#define ResOpt_VDisplay 0x10000000
|
||||
#define ResOpt_IDisplay 0x20000000
|
||||
|
|
@ -579,18 +521,9 @@ typedef struct capval
|
|||
/* Assorted Variables */
|
||||
|
||||
extern RDev *ResRDevList;
|
||||
extern REcell *ResBigEventList;
|
||||
extern int ResOptionsFlags;
|
||||
extern char *ResCurrentNode;
|
||||
extern ResSimNode *ResOriginalNodes;
|
||||
#ifdef ARIEL
|
||||
extern int ResMinEventTime;
|
||||
extern int ResMaxEventTime;
|
||||
typedef float ResCapElement[2];
|
||||
extern ResCapElement *ResCapTableMax;
|
||||
extern ResCapElement *ResCapTableMin;
|
||||
extern HashTable ResPlugTable;
|
||||
#endif
|
||||
extern ResExtNode *ResOriginalNodes;
|
||||
|
||||
extern CellUse *ResUse;
|
||||
extern CellDef *ResDef;
|
||||
|
|
@ -604,10 +537,10 @@ extern resNode *ResNodeQueue;
|
|||
extern resNode *ResOriginNode;
|
||||
extern resNode *resCurrentNode;
|
||||
extern HashTable ResNodeTable;
|
||||
extern HashTable ResSimDevTable;
|
||||
extern HashTable ResExtDevTable;
|
||||
extern ResFixPoint *ResFixList;
|
||||
extern int ResTileCount;
|
||||
extern ResSimNode **ResNodeArray;
|
||||
extern ResExtNode **ResNodeArray;
|
||||
extern CellDef *mainDef;
|
||||
extern TileTypeBitMask ResSDTypesBitMask;
|
||||
extern TileTypeBitMask ResSubTypesBitMask;
|
||||
|
|
@ -616,24 +549,26 @@ extern TileTypeBitMask ResNoMergeMask[NT];
|
|||
extern ResGlobalParams gparams;
|
||||
extern int ResPortIndex;
|
||||
|
||||
extern int ResSimDevice();
|
||||
extern int ResSimCombineParallel();
|
||||
extern int ResSimCapacitor();
|
||||
extern int ResSimResistor();
|
||||
extern int ResSimAttribute();
|
||||
extern int ResSimMerge();
|
||||
extern int ResSimSubckt();
|
||||
/* Routines used by ResReadExt() */
|
||||
extern int ResReadDevice();
|
||||
extern int ResReadCapacitor();
|
||||
extern int ResReadResistor();
|
||||
extern int ResReadAttribute();
|
||||
extern int ResReadMerge();
|
||||
extern int ResReadSubckt();
|
||||
|
||||
extern int ResProcessNode();
|
||||
extern int ResExtCombineParallel();
|
||||
extern int dbSrConnectStartFunc();
|
||||
extern int ResEach(),ResAddPlumbing(),ResRemovePlumbing();
|
||||
extern float ResCalculateChildCapacitance();
|
||||
extern ResDevTile *DBTreeCopyConnectDCS();
|
||||
extern Tile *ResFindTile();
|
||||
extern resDevice *ResImageAddPlug();
|
||||
extern resDevice *ResGetDevice();
|
||||
extern tileJunk *resAddField();
|
||||
extern int ResCheckPorts();
|
||||
extern int ResCheckBlackbox();
|
||||
extern void ResCheckSimNodes();
|
||||
extern void ResCheckExtNodes();
|
||||
extern void ResSortByGate();
|
||||
extern void ResFixDevName();
|
||||
extern void ResWriteLumpFile();
|
||||
|
|
@ -663,9 +598,10 @@ extern void ResPrintResistorList();
|
|||
extern void ResPrintStats();
|
||||
extern void ResProcessJunction();
|
||||
extern int ResReadNode();
|
||||
extern int ResReadSim();
|
||||
extern int ResReadExt();
|
||||
extern void ResRemoveFromQueue();
|
||||
extern int ResSimNewNode();
|
||||
extern int ResExtNewNode();
|
||||
extern void ResExtProcessDrivePoints();
|
||||
extern int ResWriteExtFile();
|
||||
extern void ResPrintExtNode();
|
||||
extern void ResPrintExtRes();
|
||||
|
|
|
|||
Loading…
Reference in New Issue