2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#ifndef lint
|
|
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResRex.c,v 1.3 2010/03/08 13:33:33 tim Exp $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <float.h>
|
|
|
|
|
|
|
|
|
|
#include "tcltk/tclmagic.h"
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "utils/geofast.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "utils/undo.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "extract/extract.h"
|
|
|
|
|
#include "extract/extractInt.h"
|
2021-05-21 22:33:20 +02:00
|
|
|
#include "extflat/extflat.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "windows/windows.h"
|
|
|
|
|
#include "dbwind/dbwind.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "utils/tech.h"
|
|
|
|
|
#include "textio/txcommands.h"
|
|
|
|
|
#include "resis/resis.h"
|
|
|
|
|
#ifdef LAPLACE
|
|
|
|
|
#include "laplace.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define INITFLATSIZE 1024
|
|
|
|
|
#define MAXNAME 1000
|
|
|
|
|
|
2019-12-08 23:37:48 +01:00
|
|
|
/* Time constants are produced by multiplying attofarads by milliohms, */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* giving zeptoseconds (yes, really. Look it up). This constant */
|
2021-04-21 19:03:26 +02:00
|
|
|
/* converts zeptoseconds to picoseconds. */
|
2019-12-08 23:37:48 +01:00
|
|
|
|
2021-04-21 19:03:26 +02:00
|
|
|
#define Z_TO_P 1e9
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* ResSimNode is a node read in from a sim file */
|
|
|
|
|
|
|
|
|
|
HashTable ResNodeTable; /* Hash table of sim file nodes */
|
2019-10-17 22:21:56 +02:00
|
|
|
RDev *ResRDevList; /* Linked list of Sim devices */
|
2017-04-25 14:41:48 +02:00
|
|
|
ResGlobalParams gparams; /* Junk passed between */
|
|
|
|
|
/* ResCheckSimNodes and */
|
|
|
|
|
/* ResExtractNet. */
|
|
|
|
|
extern ResSimNode *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;
|
|
|
|
|
FILE *ResLumpFile;
|
|
|
|
|
FILE *ResFHFile;
|
|
|
|
|
|
|
|
|
|
int ResPortIndex; /* Port ordering to backannotate into magic */
|
|
|
|
|
|
|
|
|
|
/* external declarations */
|
|
|
|
|
extern ResSimNode *ResInitializeNode();
|
|
|
|
|
extern CellUse *CmdGetSelectedCell();
|
|
|
|
|
|
|
|
|
|
/* Structure stores information required to be sent to ExtResisForDef() */
|
|
|
|
|
typedef struct
|
|
|
|
|
{
|
|
|
|
|
float tolerance;
|
|
|
|
|
float tdiTolerance;
|
|
|
|
|
float frequency;
|
|
|
|
|
CellDef *mainDef;
|
|
|
|
|
} ResisData;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ExtResisForDef --
|
|
|
|
|
*
|
|
|
|
|
* Do resistance network extraction for the indicated CellDef.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExtResisForDef(celldef, resisdata)
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
ResisData *resisdata;
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
RDev *oldRDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *entry;
|
2019-10-17 22:21:56 +02:00
|
|
|
devPtr *tptr,*oldtptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
ResSimNode *node;
|
2021-05-21 22:33:20 +02:00
|
|
|
int result, idx;
|
|
|
|
|
char *devname;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2019-10-17 22:21:56 +02:00
|
|
|
ResRDevList = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
ResOriginalNodes = NULL;
|
|
|
|
|
|
2021-05-21 22:33:20 +02:00
|
|
|
/* Get device information from the current extraction style */
|
|
|
|
|
idx = 0;
|
|
|
|
|
while (ExtGetDevInfo(idx++, &devname, NULL, NULL, NULL, NULL, NULL))
|
|
|
|
|
{
|
|
|
|
|
if (idx == MAXDEVTYPES)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: Ran out of space for device types!\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
efBuildAddStr(EFDevTypes, &EFDevNumTypes, MAXDEVTYPES, devname);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
HashInit(&ResNodeTable, INITFLATSIZE, HT_STRINGKEYS);
|
|
|
|
|
/* read in .sim file */
|
2018-01-02 15:37:44 +01:00
|
|
|
result = (ResReadSim(celldef->cd_name,
|
2021-04-21 02:45:49 +02:00
|
|
|
ResSimDevice, ResSimCapacitor, ResSimResistor,
|
|
|
|
|
ResSimAttribute, ResSimMerge, ResSimSubckt) == 0);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-05-21 22:33:20 +02:00
|
|
|
/* Clean up the EFDevTypes table */
|
|
|
|
|
for (idx = 0; idx < EFDevNumTypes; idx++) freeMagic(EFDevTypes[idx]);
|
|
|
|
|
EFDevNumTypes = 0;
|
|
|
|
|
|
2018-01-02 15:37:44 +01:00
|
|
|
if (result)
|
2017-04-25 14:41:48 +02:00
|
|
|
/* read in .nodes file */
|
2018-01-02 15:37:44 +01:00
|
|
|
result = (ResReadNode(celldef->cd_name) == 0);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2018-01-02 15:37:44 +01:00
|
|
|
if (result)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2018-01-02 15:37:44 +01:00
|
|
|
/* Check for subcircuit ports */
|
|
|
|
|
if (ResOptionsFlags & ResOpt_Blackbox)
|
|
|
|
|
ResCheckBlackbox(celldef);
|
|
|
|
|
else
|
|
|
|
|
ResCheckPorts(celldef);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Extract networks for nets that require it. */
|
2020-05-23 23:13:14 +02:00
|
|
|
if (!(ResOptionsFlags & ResOpt_FastHenry) ||
|
2017-04-25 14:41:48 +02:00
|
|
|
DBIsSubcircuit(celldef))
|
|
|
|
|
ResCheckSimNodes(celldef, resisdata);
|
|
|
|
|
|
|
|
|
|
if (ResOptionsFlags & ResOpt_Stat)
|
|
|
|
|
ResPrintStats((ResGlobalParams *)NULL,"");
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-02 15:37:44 +01:00
|
|
|
/* Clean up */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
HashStartSearch(&hs);
|
|
|
|
|
while((entry = HashNext(&ResNodeTable,&hs)) != NULL)
|
|
|
|
|
{
|
|
|
|
|
node=(ResSimNode *) HashGetValue(entry);
|
2019-10-17 22:21:56 +02:00
|
|
|
tptr = node->firstDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (node == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error: NULL Hash entry!\n");
|
|
|
|
|
TxFlushErr();
|
|
|
|
|
}
|
|
|
|
|
while (tptr != NULL)
|
|
|
|
|
{
|
|
|
|
|
oldtptr = tptr;
|
2019-10-17 22:21:56 +02:00
|
|
|
tptr = tptr->nextDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
freeMagic((char *)oldtptr);
|
|
|
|
|
}
|
|
|
|
|
freeMagic((char *) node);
|
|
|
|
|
}
|
|
|
|
|
HashKill(&ResNodeTable);
|
2019-10-17 22:21:56 +02:00
|
|
|
while (ResRDevList != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
oldRDev = ResRDevList;
|
|
|
|
|
ResRDevList = ResRDevList->nextDev;
|
|
|
|
|
if (oldRDev->layout != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
freeMagic((char *)oldRDev->layout);
|
|
|
|
|
oldRDev->layout = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
freeMagic((char *)oldRDev);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* CmdExtResis-- reads in sim file and layout, and produces patches to the
|
|
|
|
|
* .ext files and .sim files that include resistors.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: Produces .res.sim file and .res.ext file for all nets that
|
|
|
|
|
* require resistors.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
CmdExtResis(win, cmd)
|
|
|
|
|
MagWindow *win;
|
|
|
|
|
TxCommand *cmd;
|
|
|
|
|
{
|
|
|
|
|
int i, j, k, option, value, saveFlags;
|
|
|
|
|
static int init=1;
|
|
|
|
|
static float tolerance, tdiTolerance, fhFrequency;
|
|
|
|
|
CellDef *mainDef;
|
|
|
|
|
CellUse *selectedUse;
|
|
|
|
|
ResisData resisdata;
|
|
|
|
|
char *endptr; /* for use with strtod() */
|
|
|
|
|
|
|
|
|
|
extern int resSubcircuitFunc(); /* Forward declaration */
|
|
|
|
|
|
|
|
|
|
static char *onOff[] =
|
|
|
|
|
{
|
|
|
|
|
"off",
|
|
|
|
|
"on",
|
|
|
|
|
NULL
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
static char *cmdExtresisCmd[] =
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
"tolerance [value] set ratio between resistor and device tol.",
|
2017-04-25 14:41:48 +02:00
|
|
|
"all extract all the nets",
|
|
|
|
|
"simplify [on/off] turn on/off simplification of resistor nets",
|
|
|
|
|
"extout [on/off] turn on/off writing of .res.ext file",
|
|
|
|
|
"lumped [on/off] turn on/off writing of updated lumped resistances",
|
|
|
|
|
"silent [on/off] turn on/off printing of net statistics",
|
|
|
|
|
"skip mask don't extract these types",
|
|
|
|
|
"box type extract the signal under the box on layer type",
|
|
|
|
|
"cell cellname extract the network for the cell named cellname",
|
|
|
|
|
"blackbox [on/off] treat subcircuits with ports as black boxes",
|
|
|
|
|
"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
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
RES_BAD=-2, RES_AMBIG, RES_TOL,
|
|
|
|
|
RES_ALL, RES_SIMP, RES_EXTOUT, RES_LUMPED,
|
|
|
|
|
RES_SILENT, RES_SKIP, RES_BOX, RES_CELL, RES_BLACKBOX,
|
|
|
|
|
RES_FASTHENRY, RES_GEOMETRY, RES_HELP,
|
|
|
|
|
#ifdef LAPLACE
|
|
|
|
|
RES_LAPLACE,
|
|
|
|
|
#endif
|
|
|
|
|
RES_RUN
|
|
|
|
|
} ResOptions;
|
|
|
|
|
|
|
|
|
|
if (init)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < NT; i++)
|
|
|
|
|
{
|
|
|
|
|
TTMaskZero(&(ResCopyMask[i]));
|
|
|
|
|
TTMaskSetMask(&ResCopyMask[i], &DBConnectTbl[i]);
|
|
|
|
|
}
|
|
|
|
|
tolerance = 1;
|
|
|
|
|
tdiTolerance = 1;
|
|
|
|
|
fhFrequency = 10e6; /* 10 MHz default */
|
|
|
|
|
init = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
option = (cmd->tx_argc > 1) ? Lookup(cmd->tx_argv[1], cmdExtresisCmd)
|
|
|
|
|
: RES_RUN;
|
|
|
|
|
|
|
|
|
|
switch (option)
|
|
|
|
|
{
|
|
|
|
|
case RES_SIMP:
|
|
|
|
|
case RES_EXTOUT:
|
|
|
|
|
case RES_LUMPED:
|
|
|
|
|
case RES_SILENT:
|
|
|
|
|
case RES_BLACKBOX:
|
|
|
|
|
if (cmd->tx_argc > 2)
|
|
|
|
|
{
|
|
|
|
|
value = Lookup(cmd->tx_argv[2], onOff);
|
|
|
|
|
if (value < 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Value must be either \"on\" or \"off\".\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (option)
|
|
|
|
|
{
|
|
|
|
|
case RES_TOL:
|
|
|
|
|
ResOptionsFlags |= ResOpt_ExplicitRtol;
|
2020-05-23 23:13:14 +02:00
|
|
|
if (cmd->tx_argc > 2)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
tolerance = MagAtof(cmd->tx_argv[2]);
|
|
|
|
|
if (tolerance <= 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Usage: %s tolerance [value]\n", cmd->tx_argv[0]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
tdiTolerance = tolerance;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
|
|
|
Tcl_SetObjResult(magicinterp, Tcl_NewDoubleObj((double)tdiTolerance));
|
|
|
|
|
#else
|
|
|
|
|
TxPrintf("Tolerance ratio is %g.\n", tdiTolerance);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case RES_ALL:
|
|
|
|
|
ResOptionsFlags |= ResOpt_ExtractAll;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RES_GEOMETRY:
|
|
|
|
|
saveFlags = ResOptionsFlags;
|
|
|
|
|
ResOptionsFlags |= ResOpt_Geometry | ResOpt_ExtractAll;
|
|
|
|
|
ResOptionsFlags &= ~(ResOpt_DoExtFile | ResOpt_DoLumpFile
|
|
|
|
|
| ResOpt_Simplify | ResOpt_Tdi);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RES_FASTHENRY:
|
|
|
|
|
if (cmd->tx_argc == 3)
|
|
|
|
|
{
|
|
|
|
|
double tmpf = strtod(cmd->tx_argv[2], &endptr);
|
|
|
|
|
if (endptr == cmd->tx_argv[2])
|
|
|
|
|
{
|
|
|
|
|
TxError("Cannot parse frequency value. Assuming default\n");
|
|
|
|
|
TxError("Frequency = %2.1f Hz\n", fhFrequency);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
fhFrequency = (float)tmpf;
|
|
|
|
|
}
|
|
|
|
|
saveFlags = ResOptionsFlags;
|
|
|
|
|
ResOptionsFlags |= ResOpt_FastHenry | ResOpt_ExtractAll;
|
|
|
|
|
ResOptionsFlags &= ~(ResOpt_DoExtFile | ResOpt_DoLumpFile
|
|
|
|
|
| ResOpt_Simplify | ResOpt_Tdi);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RES_BLACKBOX:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
value = (ResOptionsFlags & ResOpt_Blackbox) ?
|
|
|
|
|
TRUE : FALSE;
|
|
|
|
|
TxPrintf("%s\n", onOff[value]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = Lookup(cmd->tx_argv[2], onOff);
|
|
|
|
|
|
|
|
|
|
if (value)
|
2020-05-23 23:13:14 +02:00
|
|
|
ResOptionsFlags |= ResOpt_Blackbox;
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
ResOptionsFlags &= ~ResOpt_Blackbox;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case RES_SIMP:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
value = (ResOptionsFlags & (ResOpt_Simplify | ResOpt_Tdi)) ?
|
|
|
|
|
TRUE : FALSE;
|
|
|
|
|
TxPrintf("%s\n", onOff[value]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = Lookup(cmd->tx_argv[2], onOff);
|
|
|
|
|
|
|
|
|
|
if (value)
|
2020-05-23 23:13:14 +02:00
|
|
|
ResOptionsFlags |= ResOpt_Simplify | ResOpt_Tdi;
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
ResOptionsFlags &= ~(ResOpt_Simplify | ResOpt_Tdi);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case RES_EXTOUT:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
value = (ResOptionsFlags & ResOpt_DoExtFile) ?
|
|
|
|
|
TRUE : FALSE;
|
|
|
|
|
TxPrintf("%s\n", onOff[value]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = Lookup(cmd->tx_argv[2], onOff);
|
|
|
|
|
if (value)
|
|
|
|
|
ResOptionsFlags |= ResOpt_DoExtFile;
|
|
|
|
|
else
|
|
|
|
|
ResOptionsFlags &= ~ResOpt_DoExtFile;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case RES_LUMPED:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
value = (ResOptionsFlags & ResOpt_DoLumpFile) ?
|
|
|
|
|
TRUE : FALSE;
|
|
|
|
|
TxPrintf("%s\n", onOff[value]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = Lookup(cmd->tx_argv[2], onOff);
|
|
|
|
|
if (value)
|
|
|
|
|
ResOptionsFlags |= ResOpt_DoLumpFile;
|
|
|
|
|
else
|
|
|
|
|
ResOptionsFlags &= ~ResOpt_DoLumpFile;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
case RES_SILENT:
|
|
|
|
|
if (cmd->tx_argc == 2)
|
|
|
|
|
{
|
|
|
|
|
value = (ResOptionsFlags & ResOpt_RunSilent) ?
|
|
|
|
|
TRUE : FALSE;
|
|
|
|
|
TxPrintf("%s\n", onOff[value]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
value = Lookup(cmd->tx_argv[2], onOff);
|
|
|
|
|
if (value)
|
|
|
|
|
ResOptionsFlags |= ResOpt_RunSilent;
|
|
|
|
|
else
|
|
|
|
|
ResOptionsFlags &= ~ResOpt_RunSilent;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case RES_SKIP:
|
2020-05-23 23:13:14 +02:00
|
|
|
if (cmd->tx_argc > 2)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
j = DBTechNoisyNameType(cmd->tx_argv[2]);
|
|
|
|
|
if (j >= 0)
|
|
|
|
|
for (k = TT_TECHDEPBASE; k < TT_MAXTYPES; k++)
|
|
|
|
|
TTMaskClearType(&ResCopyMask[k], j);
|
|
|
|
|
TTMaskZero(&(ResCopyMask[j]));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i != NT; i++)
|
|
|
|
|
{
|
|
|
|
|
TTMaskZero(&(ResCopyMask[i]));
|
|
|
|
|
TTMaskSetMask(&ResCopyMask[i], &DBConnectTbl[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case RES_HELP:
|
|
|
|
|
for (i = 0; cmdExtresisCmd[i] != NULL; i++)
|
|
|
|
|
TxPrintf("%s\n", cmdExtresisCmd[i]);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case RES_BOX:
|
|
|
|
|
{
|
|
|
|
|
TileType tt;
|
|
|
|
|
CellDef *def;
|
|
|
|
|
Rect rect;
|
|
|
|
|
int oldoptions;
|
|
|
|
|
ResFixPoint fp;
|
|
|
|
|
|
|
|
|
|
if (ToolGetBoxWindow((Rect *) NULL, (int *) NULL) == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Sorry, the box must appear in one of the windows.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cmd->tx_argc != 3) return;
|
|
|
|
|
tt = DBTechNoisyNameType(cmd->tx_argv[2]);
|
|
|
|
|
if (tt <= 0 || ToolGetBox(&def, &rect)== FALSE) return;
|
2019-10-17 22:21:56 +02:00
|
|
|
gparams.rg_devloc = &rect.r_ll;
|
2017-04-25 14:41:48 +02:00
|
|
|
gparams.rg_ttype = tt;
|
|
|
|
|
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
|
|
|
|
|
fp.fp_ttype = tt;
|
|
|
|
|
fp.fp_loc = rect.r_ll;
|
|
|
|
|
fp.fp_next = NULL;
|
|
|
|
|
if (ResExtractNet(&fp, &gparams, NULL) != 0) return;
|
|
|
|
|
ResPrintResistorList(stdout,ResResList);
|
2019-10-17 22:21:56 +02:00
|
|
|
ResPrintDeviceList(stdout,ResRDevList);
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef LAPLACE
|
|
|
|
|
if (ResOptionsFlags & ResOpt_DoLaplace)
|
|
|
|
|
{
|
|
|
|
|
TxPrintf("Laplace solved: %d matched %d\n",
|
|
|
|
|
LaplaceMissCount,LaplaceMatchCount);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
ResOptionsFlags = oldoptions;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
case RES_CELL:
|
|
|
|
|
selectedUse = CmdGetSelectedCell((Transform *) NULL);
|
|
|
|
|
if (selectedUse == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("No cell selected\n");
|
2020-05-23 23:13:14 +02:00
|
|
|
return;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
mainDef = selectedUse->cu_def;
|
|
|
|
|
ResOptionsFlags &= ~ResOpt_ExtractAll;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
return;
|
|
|
|
|
case RES_BAD:
|
|
|
|
|
TxPrintf("Unknown option: %s\n",cmd->tx_argv[1]);
|
|
|
|
|
TxFlushOut();
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef LAPLACE
|
|
|
|
|
LaplaceMatchCount = 0;
|
|
|
|
|
LaplaceMissCount = 0;
|
|
|
|
|
#endif
|
|
|
|
|
/* turn off undo stuff */
|
|
|
|
|
UndoDisable();
|
|
|
|
|
|
|
|
|
|
if (!ToolGetBox(&mainDef,(Rect *) NULL))
|
|
|
|
|
{
|
|
|
|
|
TxError("Couldn't find def corresponding to box\n");
|
|
|
|
|
if ((option == RES_FASTHENRY) || (option == RES_GEOMETRY))
|
|
|
|
|
ResOptionsFlags = saveFlags;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ResOptionsFlags |= ResOpt_Signal;
|
|
|
|
|
#ifdef ARIEL
|
|
|
|
|
ResOptionsFlags &= ~ResOpt_Power;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
resisdata.tolerance = tolerance;
|
|
|
|
|
resisdata.tdiTolerance = tdiTolerance;
|
|
|
|
|
resisdata.frequency = fhFrequency;
|
|
|
|
|
resisdata.mainDef = mainDef;
|
|
|
|
|
|
|
|
|
|
/* Do subcircuits (if any) first */
|
|
|
|
|
if (!(ResOptionsFlags & ResOpt_Blackbox))
|
|
|
|
|
(void) DBCellSrDefs(0, resSubcircuitFunc, (ClientData) &resisdata);
|
|
|
|
|
|
|
|
|
|
ExtResisForDef(mainDef, &resisdata);
|
|
|
|
|
|
|
|
|
|
/* 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. */
|
|
|
|
|
|
|
|
|
|
if ((option == RES_FASTHENRY) || (option == RES_GEOMETRY))
|
|
|
|
|
ResOptionsFlags = saveFlags;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* resSubcircuitFunc --
|
|
|
|
|
* For each encountered cell, call the resistance extractor.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Always return 0 to keep search alive.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* Does resistance extraction for an entire cell.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
resSubcircuitFunc(cellDef, rdata)
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
ResisData *rdata;
|
|
|
|
|
{
|
2018-01-02 16:03:16 +01:00
|
|
|
if ((cellDef->cd_flags & CDINTERNAL) == CDINTERNAL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (cellDef != rdata->mainDef)
|
|
|
|
|
if (DBIsSubcircuit(cellDef))
|
|
|
|
|
ExtResisForDef(cellDef, rdata);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* Callback routine for ResCheckBlackBox. For each label found in a
|
|
|
|
|
* subcell, transform the label position back to the top level and
|
|
|
|
|
* add to the list of nodes for extresis.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
resPortFunc(scx, lab, tpath, result)
|
|
|
|
|
SearchContext *scx;
|
|
|
|
|
Label *lab;
|
|
|
|
|
TerminalPath *tpath;
|
|
|
|
|
int *result;
|
|
|
|
|
{
|
|
|
|
|
Rect r;
|
|
|
|
|
int pclass, puse;
|
|
|
|
|
Point portloc;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
|
|
|
|
|
GeoTransRect(&scx->scx_trans, &lab->lab_rect, &r);
|
|
|
|
|
|
|
|
|
|
// To be expanded. Currently this handles digital signal inputs
|
|
|
|
|
// and outputs, for standard cells.
|
|
|
|
|
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK) {
|
|
|
|
|
pclass = lab->lab_flags & PORT_CLASS_MASK;
|
|
|
|
|
puse = lab->lab_flags & PORT_USE_MASK;
|
|
|
|
|
|
|
|
|
|
// Ad hoc rule: If port use is not declared, but port
|
|
|
|
|
// direction is either INPUT or OUTPUT, then use SIGNAL is implied.
|
|
|
|
|
|
|
|
|
|
if ((puse == 0) && ((pclass == PORT_CLASS_INPUT)
|
|
|
|
|
|| (pclass == PORT_CLASS_OUTPUT)))
|
|
|
|
|
puse = PORT_USE_SIGNAL;
|
|
|
|
|
|
|
|
|
|
if (puse == PORT_USE_SIGNAL || puse == PORT_USE_CLOCK) {
|
|
|
|
|
|
|
|
|
|
if (lab->lab_flags & (PORT_DIR_NORTH | PORT_DIR_SOUTH))
|
|
|
|
|
portloc.p_x = (r.r_xbot + r.r_xtop) >> 1;
|
|
|
|
|
else if (lab->lab_flags & (PORT_DIR_EAST | PORT_DIR_WEST))
|
|
|
|
|
portloc.p_y = (r.r_ybot + r.r_ytop) >> 1;
|
|
|
|
|
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_NORTH)
|
|
|
|
|
portloc.p_y = r.r_ytop;
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_SOUTH)
|
|
|
|
|
portloc.p_y = r.r_ybot;
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_EAST)
|
|
|
|
|
portloc.p_x = r.r_xtop;
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_WEST)
|
|
|
|
|
portloc.p_x = r.r_xbot;
|
|
|
|
|
|
|
|
|
|
if ((pclass == PORT_CLASS_INPUT) || (pclass == PORT_CLASS_OUTPUT)) {
|
|
|
|
|
int len;
|
|
|
|
|
char *nodename;
|
|
|
|
|
|
|
|
|
|
// Port name is the instance name / pin name
|
|
|
|
|
// To do: Make use of tpath
|
|
|
|
|
len = strlen(scx->scx_use->cu_id) + strlen(lab->lab_text) + 2;
|
|
|
|
|
nodename = (char *) mallocMagic((unsigned) len);
|
|
|
|
|
sprintf(nodename, "%s/%s", scx->scx_use->cu_id, lab->lab_text);
|
|
|
|
|
|
|
|
|
|
entry = HashFind(&ResNodeTable, nodename);
|
|
|
|
|
node = ResInitializeNode(entry);
|
|
|
|
|
|
|
|
|
|
/* Digital outputs are drivers */
|
|
|
|
|
if (pclass == PORT_CLASS_OUTPUT) node->status |= FORCE;
|
|
|
|
|
|
|
|
|
|
node->drivepoint = portloc;
|
|
|
|
|
node->status |= DRIVELOC | PORTNODE;
|
|
|
|
|
node->rs_bbox = r;
|
|
|
|
|
node->location = portloc;
|
|
|
|
|
node->rs_ttype = lab->lab_type;
|
|
|
|
|
node->type = lab->lab_type;
|
|
|
|
|
|
|
|
|
|
*result = 0;
|
|
|
|
|
freeMagic(nodename);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0; /* Keep the search going */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResCheckBlackbox--
|
|
|
|
|
*
|
|
|
|
|
* For standard cell parasitic extraction, search all children
|
|
|
|
|
* of cellDef for ports, and add each port to the list of nodes
|
|
|
|
|
* for extresist to process. If the port use is "ground" or
|
|
|
|
|
* "power", then don't process the node. If the port class is
|
|
|
|
|
* "output", then make this node a (forced) driver.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Results: 0 if one or more nodes was created, 1 otherwise
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: Adds driving nodes to the extresis network database.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResCheckBlackbox(cellDef)
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
{
|
|
|
|
|
int result = 1;
|
|
|
|
|
SearchContext scx;
|
|
|
|
|
CellUse dummy;
|
|
|
|
|
|
|
|
|
|
dummy.cu_expandMask = 0;
|
|
|
|
|
dummy.cu_transform = GeoIdentityTransform;
|
|
|
|
|
dummy.cu_def = cellDef;
|
|
|
|
|
dummy.cu_id = NULL;
|
|
|
|
|
|
|
|
|
|
scx.scx_area = cellDef->cd_bbox;
|
|
|
|
|
scx.scx_trans = GeoIdentityTransform;
|
|
|
|
|
scx.scx_use = (CellUse *)&dummy;
|
|
|
|
|
|
|
|
|
|
/* Do a search on all children */
|
|
|
|
|
|
|
|
|
|
DBTreeSrLabels(&scx, &DBAllButSpaceAndDRCBits, 0, NULL,
|
|
|
|
|
TF_LABEL_ATTACH, resPortFunc, (ClientData)&result);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResCheckPorts--
|
|
|
|
|
*
|
|
|
|
|
* Subcircuit boundaries mark an area which is to be checked
|
|
|
|
|
* explicitly for geometry information. Because there may be
|
2019-10-17 22:21:56 +02:00
|
|
|
* no devices in the subcircuit cell, we must find the ports
|
2017-04-25 14:41:48 +02:00
|
|
|
* into the subcircuit and declare them to be "driving" nodes so
|
|
|
|
|
* the extresis algorithm will treat them as being part of valid
|
|
|
|
|
* networks.
|
2020-05-23 23:13:14 +02:00
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Results: 0 if one or more nodes was created, 1 otherwise
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: Adds driving nodes to the extresis network database.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResCheckPorts(cellDef)
|
|
|
|
|
CellDef *cellDef;
|
|
|
|
|
{
|
|
|
|
|
Point portloc;
|
|
|
|
|
Label *lab;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
int result = 1;
|
|
|
|
|
|
|
|
|
|
for (lab = cellDef->cd_labels; lab; lab = lab->lab_next)
|
|
|
|
|
{
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
{
|
|
|
|
|
/* Get drivepoint from the port connection direction(s) */
|
|
|
|
|
/* NOTE: This is not rigorous! */
|
|
|
|
|
|
|
|
|
|
if (lab->lab_flags & (PORT_DIR_NORTH | PORT_DIR_SOUTH))
|
|
|
|
|
portloc.p_x = (lab->lab_rect.r_xbot + lab->lab_rect.r_xtop) >> 1;
|
|
|
|
|
else if (lab->lab_flags & (PORT_DIR_EAST | PORT_DIR_WEST))
|
|
|
|
|
portloc.p_y = (lab->lab_rect.r_ybot + lab->lab_rect.r_ytop) >> 1;
|
|
|
|
|
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_NORTH)
|
|
|
|
|
portloc.p_y = lab->lab_rect.r_ytop;
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_SOUTH)
|
|
|
|
|
portloc.p_y = lab->lab_rect.r_ybot;
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_EAST)
|
|
|
|
|
portloc.p_x = lab->lab_rect.r_xtop;
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_WEST)
|
|
|
|
|
portloc.p_x = lab->lab_rect.r_xbot;
|
|
|
|
|
|
|
|
|
|
entry = HashFind(&ResNodeTable, lab->lab_text);
|
|
|
|
|
result = 0;
|
|
|
|
|
if ((node = (ResSimNode *) HashGetValue(entry)) != NULL)
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
TxPrintf("Port: name = %s exists, forcing drivepoint\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
lab->lab_text);
|
2021-04-21 19:03:26 +02:00
|
|
|
TxPrintf("Location is (%d, %d); drivepoint (%d, %d)\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
node->location.p_x, node->location.p_y,
|
|
|
|
|
portloc.p_x, portloc.p_y);
|
2021-04-21 19:03:26 +02:00
|
|
|
TxFlush();
|
2017-04-25 14:41:48 +02:00
|
|
|
node->drivepoint = portloc;
|
|
|
|
|
node->status |= FORCE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* This is a port, but it's merged with another node. */
|
|
|
|
|
/* We have to make sure it's listed as a separate node */
|
|
|
|
|
/* and a drivepoint. */
|
|
|
|
|
|
|
|
|
|
node = ResInitializeNode(entry);
|
2021-04-21 19:03:26 +02:00
|
|
|
TxPrintf("Port: name = %s is new node 0x%x\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
lab->lab_text, node);
|
2021-04-21 19:03:26 +02:00
|
|
|
TxPrintf("Location is (%d, %d); drivepoint (%d, %d)\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
portloc.p_x, portloc.p_y,
|
|
|
|
|
portloc.p_x, portloc.p_y);
|
|
|
|
|
node->location = portloc;
|
|
|
|
|
node->drivepoint = node->location;
|
|
|
|
|
}
|
|
|
|
|
node->status |= DRIVELOC | PORTNODE;
|
|
|
|
|
node->rs_bbox = lab->lab_rect;
|
|
|
|
|
node->rs_ttype = lab->lab_type;
|
|
|
|
|
node->type = lab->lab_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResCheckSimNodes-- check to see if lumped resistance is greater than the
|
2020-05-23 23:13:14 +02:00
|
|
|
* device resistance; if it is, Extract the net
|
2017-04-25 14:41:48 +02:00
|
|
|
* resistance. If the maximum point to point resistance
|
2020-05-23 23:13:14 +02:00
|
|
|
* in the extracted net is still creater than the
|
2017-04-25 14:41:48 +02:00
|
|
|
* tolerance, then output the extracted net.
|
|
|
|
|
*
|
|
|
|
|
* Results: none
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: Writes networks to .res.ext and .res.sim files.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ResCheckSimNodes(celldef, resisdata)
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
ResisData *resisdata;
|
|
|
|
|
{
|
|
|
|
|
ResSimNode *node;
|
2019-10-17 22:21:56 +02:00
|
|
|
devPtr *ptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
float ftolerance, rctolerance, minRes, cumRes;
|
|
|
|
|
int failed1=0;
|
|
|
|
|
int failed3=0;
|
|
|
|
|
int total =0;
|
|
|
|
|
char *outfile = celldef->cd_name;
|
|
|
|
|
float tol = resisdata->tolerance;
|
|
|
|
|
float rctol = resisdata->tdiTolerance;
|
|
|
|
|
int nidx = 1, eidx = 1; /* node & segment counters for geom. */
|
|
|
|
|
|
|
|
|
|
if (ResOptionsFlags & ResOpt_DoExtFile)
|
|
|
|
|
{
|
|
|
|
|
ResExtFile = PaOpen(outfile,"w",".res.ext",".",(char *) NULL, (char **) NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResExtFile = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (ResOptionsFlags & ResOpt_DoLumpFile)
|
|
|
|
|
{
|
|
|
|
|
ResLumpFile = PaOpen(outfile,"w",".res.lump",".",(char *) NULL, (char **) NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResLumpFile = NULL;
|
|
|
|
|
}
|
|
|
|
|
if (ResOptionsFlags & ResOpt_FastHenry)
|
|
|
|
|
{
|
|
|
|
|
char *geofilename;
|
|
|
|
|
ResFHFile = PaOpen(outfile,"w",".fh",".",(char *) NULL, &geofilename);
|
|
|
|
|
TxPrintf("Writing FastHenry-format geometry file \"%s\"\n", geofilename);
|
|
|
|
|
ResPortIndex = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResFHFile = NULL;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (ResExtFile == NULL && (ResOptionsFlags & ResOpt_DoExtFile)
|
|
|
|
|
|| (ResOptionsFlags & ResOpt_DoLumpFile) && ResLumpFile == NULL
|
|
|
|
|
|| (ResOptionsFlags & ResOpt_FastHenry) && ResFHFile == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Couldn't open output file\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-08 23:37:48 +01:00
|
|
|
/*
|
|
|
|
|
* Write a scale line at the top of the .res.ext file, as the
|
|
|
|
|
* scale may be different from the original .ext file.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (ResExtFile != NULL)
|
|
|
|
|
{
|
|
|
|
|
fprintf(ResExtFile, "scale %d %d %g\n",
|
|
|
|
|
ExtCurStyle->exts_resistScale,
|
|
|
|
|
ExtCurStyle->exts_capScale,
|
|
|
|
|
ExtCurStyle->exts_unitsPerLambda);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* Write reference plane (substrate) definition and end statement
|
2020-05-23 23:13:14 +02:00
|
|
|
* to the FastHenry geometry file.
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
if (ResOptionsFlags & ResOpt_FastHenry)
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
ResPrintReference(ResFHFile, ResRDevList, celldef);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (node = ResOriginalNodes; node != NULL; node=node->nextnode)
|
|
|
|
|
{
|
|
|
|
|
ResCurrentNode = node->name;
|
|
|
|
|
if (!(ResOptionsFlags & ResOpt_FastHenry))
|
|
|
|
|
{
|
|
|
|
|
/* Hack!! Don't extract Vdd or GND lines */
|
|
|
|
|
|
|
|
|
|
char *last4, *last3;
|
|
|
|
|
last4 = node->name+strlen(node->name)-4;
|
|
|
|
|
last3 = node->name+strlen(node->name)-3;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if ((strncmp(last4,"Vdd!",4) == 0 ||
|
2017-04-25 14:41:48 +02:00
|
|
|
strncmp(last4,"VDD!",4) == 0 ||
|
|
|
|
|
strncmp(last4,"vdd!",4) == 0 ||
|
|
|
|
|
strncmp(last4,"Gnd!",4) == 0 ||
|
|
|
|
|
strncmp(last4,"gnd!",4) == 0 ||
|
|
|
|
|
strncmp(last4,"GND!",4) == 0 ||
|
2020-05-23 23:13:14 +02:00
|
|
|
strncmp(last3,"Vdd",3) == 0 ||
|
2017-04-25 14:41:48 +02:00
|
|
|
strncmp(last3,"VDD",3) == 0 ||
|
|
|
|
|
strncmp(last3,"vdd",3) == 0 ||
|
|
|
|
|
strncmp(last3,"Gnd",3) == 0 ||
|
|
|
|
|
strncmp(last3,"gnd",3) == 0 ||
|
|
|
|
|
strncmp(last3,"GND",3) == 0) &&
|
|
|
|
|
(node->status & FORCE) != FORCE) continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Has this node been merged away or is it marked as skipped? */
|
|
|
|
|
/* If so, skip it */
|
|
|
|
|
if ((node->status & (FORWARD | REDUNDANT)) ||
|
2020-05-23 23:13:14 +02:00
|
|
|
((node->status & SKIP) &&
|
2017-04-25 14:41:48 +02:00
|
|
|
(ResOptionsFlags & ResOpt_ExtractAll) == 0))
|
|
|
|
|
continue;
|
|
|
|
|
total++;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-10-17 22:21:56 +02:00
|
|
|
ResSortByGate(&node->firstDev);
|
|
|
|
|
/* Find largest SD device connected to node. */
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
minRes = FLT_MAX;
|
2019-10-17 22:21:56 +02:00
|
|
|
gparams.rg_devloc = (Point *) NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
gparams.rg_status = FALSE;
|
|
|
|
|
gparams.rg_nodecap = node->capacitance;
|
|
|
|
|
|
|
|
|
|
/* the following is only used if there is a drivepoint */
|
|
|
|
|
/* to identify which tile the drivepoint is on. */
|
|
|
|
|
gparams.rg_ttype = node->rs_ttype;
|
|
|
|
|
|
2019-10-17 22:21:56 +02:00
|
|
|
for (ptr = node->firstDev; ptr != NULL; ptr=ptr->nextDev)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
RDev *t1;
|
|
|
|
|
RDev *t2;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (ptr->terminal == GATE)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
/* get cumulative resistance of all devices */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* with same connections. */
|
2019-10-17 22:21:56 +02:00
|
|
|
cumRes = ptr->thisDev->resistance;
|
|
|
|
|
t1 = ptr->thisDev;
|
|
|
|
|
for (; ptr->nextDev != NULL; ptr = ptr->nextDev)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
t1 = ptr->thisDev;
|
|
|
|
|
t2 = ptr->nextDev->thisDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (t1->gate != t2->gate) break;
|
|
|
|
|
if ((t1->source != t2->source ||
|
|
|
|
|
t1->drain != t2->drain) &&
|
|
|
|
|
(t1->source != t2->drain ||
|
|
|
|
|
t1->drain != t2->source)) break;
|
|
|
|
|
|
|
|
|
|
/* do parallel combination */
|
|
|
|
|
if (cumRes != 0.0 && t2->resistance != 0.0)
|
|
|
|
|
{
|
|
|
|
|
cumRes = (cumRes * t2->resistance) /
|
|
|
|
|
(cumRes + t2->resistance);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
cumRes = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (minRes > cumRes)
|
|
|
|
|
{
|
|
|
|
|
minRes = cumRes;
|
2019-10-17 22:21:56 +02:00
|
|
|
gparams.rg_devloc = &t1->location;
|
2017-04-25 14:41:48 +02:00
|
|
|
gparams.rg_ttype = t1->rs_ttype;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* special handling for FORCE and DRIVELOC labels: */
|
|
|
|
|
/* set minRes = node->minsizeres if it exists, 0 otherwise */
|
|
|
|
|
|
|
|
|
|
if (node->status & (FORCE|DRIVELOC))
|
|
|
|
|
{
|
|
|
|
|
if (node->status & MINSIZE)
|
|
|
|
|
{
|
|
|
|
|
minRes = node->minsizeres;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
minRes = 0;
|
|
|
|
|
}
|
|
|
|
|
if (node->status & DRIVELOC)
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
gparams.rg_devloc = &node->drivepoint;
|
2017-04-25 14:41:48 +02:00
|
|
|
gparams.rg_status |= DRIVEONLY;
|
|
|
|
|
}
|
2019-10-15 15:51:25 +02:00
|
|
|
if (node->status & PORTNODE)
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
/* The node is a port, not a device, so make */
|
2019-10-15 15:51:25 +02:00
|
|
|
/* sure rg_ttype is set accordingly. */
|
|
|
|
|
gparams.rg_ttype = node->rs_ttype;
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
if (gparams.rg_devloc == NULL && node->status & FORCE)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
TxError("Node %s has force label but no drive point or "
|
2019-10-17 22:21:56 +02:00
|
|
|
"driving device\n",node->name);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
if (minRes == FLT_MAX || gparams.rg_devloc == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
gparams.rg_bigdevres = (int)minRes*OHMSTOMILLIOHMS;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (rctol == 0.0 || tol == 0.0)
|
|
|
|
|
{
|
|
|
|
|
ftolerance = 0.0;
|
|
|
|
|
rctolerance = 0.0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ftolerance = minRes/tol;
|
|
|
|
|
rctolerance = minRes/rctol;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
/*
|
|
|
|
|
* Is the device resistance greater than the lumped node
|
2017-04-25 14:41:48 +02:00
|
|
|
* resistance? If so, extract net.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (node->resistance > ftolerance || node->status & FORCE ||
|
|
|
|
|
(ResOpt_ExtractAll & ResOptionsFlags))
|
|
|
|
|
{
|
|
|
|
|
ResFixPoint fp;
|
|
|
|
|
|
|
|
|
|
failed1++;
|
|
|
|
|
fp.fp_loc = node->location;
|
|
|
|
|
fp.fp_ttype = node->type;
|
|
|
|
|
fp.fp_next = NULL;
|
|
|
|
|
if (ResExtractNet(&fp, &gparams, outfile) != 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error in extracting node %s\n",node->name);
|
|
|
|
|
// break; // Don't stop for one error. . .
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResDoSimplify(ftolerance,rctol,&gparams);
|
|
|
|
|
if (ResOptionsFlags & ResOpt_DoLumpFile)
|
|
|
|
|
{
|
|
|
|
|
ResWriteLumpFile(node);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
if (gparams.rg_maxres >= ftolerance ||
|
|
|
|
|
gparams.rg_maxres >= rctolerance ||
|
2017-04-25 14:41:48 +02:00
|
|
|
(ResOptionsFlags & ResOpt_ExtractAll))
|
|
|
|
|
{
|
|
|
|
|
resNodeNum = 0;
|
|
|
|
|
failed3 += ResWriteExtFile(celldef, node, tol, rctol,
|
|
|
|
|
&nidx, &eidx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#ifdef PARANOID
|
2019-10-17 22:21:56 +02:00
|
|
|
ResSanityChecks(node->name,ResResList,ResNodeList,ResDevList);
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif
|
|
|
|
|
ResCleanUpEverything();
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
|
|
|
|
/*
|
2019-10-17 22:21:56 +02:00
|
|
|
* Print out all device which have had at least one terminal changed
|
2017-04-25 14:41:48 +02:00
|
|
|
* by resistance extraction.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (ResOptionsFlags & ResOpt_DoExtFile)
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
ResPrintExtDev(ResExtFile,ResRDevList);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
2020-05-23 23:13:14 +02:00
|
|
|
* Write end statement to the FastHenry geometry file.
|
2017-04-25 14:41:48 +02:00
|
|
|
* (Frequency range should be user-specified. . .)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (ResOptionsFlags & ResOpt_FastHenry)
|
|
|
|
|
{
|
|
|
|
|
Label *lab;
|
|
|
|
|
|
|
|
|
|
fprintf(ResFHFile, "\n.freq fmin=%2.1g fmax=%2.1g\n",
|
|
|
|
|
resisdata->frequency, resisdata->frequency);
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
/* Write (in comment lines) the order in which arguments are written */
|
|
|
|
|
/* to a SPICE subcircuit call when Magic runs ext2spice (exttospice). */
|
|
|
|
|
/* At present, it is the responsibility of the program that generates */
|
|
|
|
|
/* SPICE from FastHenry output to use this information to appropriately */
|
|
|
|
|
/* order the arguments in the ".subckt" definition. */
|
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
fprintf(ResFHFile, "\n* Order of arguments to SPICE subcircuit call:\n");
|
|
|
|
|
for (lab = celldef->cd_labels; lab != NULL; lab = lab->lab_next)
|
|
|
|
|
if (lab->lab_flags & PORT_DIR_MASK)
|
|
|
|
|
fprintf(ResFHFile, "* %d %s\n", lab->lab_flags & PORT_NUM_MASK,
|
|
|
|
|
lab->lab_text);
|
|
|
|
|
|
|
|
|
|
fprintf(ResFHFile, "\n.end\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output statistics about extraction */
|
|
|
|
|
|
|
|
|
|
if (total)
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
TxPrintf("Total Nets: %d\nNets extracted: "
|
2017-04-25 14:41:48 +02:00
|
|
|
"%d (%f)\nNets output: %d (%f)\n", total, failed1,
|
|
|
|
|
(float)failed1 / (float)total, failed3,
|
|
|
|
|
(float)failed3 / (float)total);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
TxPrintf("Total Nodes: %d\n",total);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* close output files */
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
if (ResExtFile != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
(void) fclose(ResExtFile);
|
2021-04-21 19:03:26 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (ResLumpFile != NULL)
|
|
|
|
|
(void) fclose(ResLumpFile);
|
2021-04-21 19:03:26 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (ResFHFile != NULL)
|
|
|
|
|
(void) fclose(ResFHFile);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
2021-04-21 19:03:26 +02:00
|
|
|
* ResFixUpConnections-- Changes the connection to a terminal of the sim
|
2019-10-17 22:21:56 +02:00
|
|
|
* device. The new name is formed by appending .t# to the old name.
|
2017-04-25 14:41:48 +02:00
|
|
|
* The new name is added to the hash table of node names.
|
|
|
|
|
*
|
|
|
|
|
* Results:none
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: Allocates new ResSimNodes. Modifies the terminal connections
|
2019-10-17 22:21:56 +02:00
|
|
|
* of sim Devices.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2019-10-17 22:21:56 +02:00
|
|
|
ResFixUpConnections(simDev, layoutDev, simNode, nodename)
|
|
|
|
|
RDev *simDev;
|
|
|
|
|
resDevice *layoutDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
ResSimNode *simNode;
|
|
|
|
|
char *nodename;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
static char newname[MAXNAME], oldnodename[MAXNAME];
|
|
|
|
|
int notdecremented;
|
|
|
|
|
resNode *gate, *source, *drain;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* If we aren't doing output (i.e. this is just a statistical run) */
|
|
|
|
|
/* don't patch up networks. This cuts down on memory use. */
|
|
|
|
|
|
|
|
|
|
if ((ResOptionsFlags & (ResOpt_DoRsmFile | ResOpt_DoExtFile)) == 0)
|
|
|
|
|
return;
|
2021-04-22 03:04:17 +02:00
|
|
|
|
2019-10-17 22:21:56 +02:00
|
|
|
if (simDev->layout == NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
layoutDev->rd_status |= RES_DEV_SAVE;
|
|
|
|
|
simDev->layout = layoutDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
simDev->status |= TRUE;
|
2021-04-22 03:04:17 +02:00
|
|
|
if (strcmp(nodename, oldnodename) != 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-22 03:04:17 +02:00
|
|
|
strcpy(oldnodename, nodename);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-04-21 19:03:26 +02:00
|
|
|
sprintf(newname, "%s%s%d", nodename, ".t", resNodeNum++);
|
2017-04-25 14:41:48 +02:00
|
|
|
notdecremented = TRUE;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-10-17 22:21:56 +02:00
|
|
|
if (simDev->gate == simNode)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
if ((gate=layoutDev->rd_fet_gate) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
/* Cosmetic addition: If the layout device already has a */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* name, the new one won't be used, so we decrement resNodeNum */
|
|
|
|
|
if (gate->rn_name != NULL)
|
|
|
|
|
{
|
|
|
|
|
resNodeNum--;
|
|
|
|
|
notdecremented = FALSE;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-04-21 19:03:26 +02:00
|
|
|
ResFixDevName(newname, GATE, simDev, gate);
|
2019-10-17 22:21:56 +02:00
|
|
|
gate->rn_name = simDev->gate->name;
|
2021-04-21 19:03:26 +02:00
|
|
|
sprintf(newname, "%s%s%d", nodename, ".t", resNodeNum++);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TxError("Missing gate connection\n");
|
|
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
if (simDev->source == simNode)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
if (simDev->drain == simNode)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-22 03:04:17 +02:00
|
|
|
if (((source = layoutDev->rd_fet_source) != NULL) &&
|
|
|
|
|
((drain = layoutDev->rd_fet_drain) != NULL))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (source->rn_name != NULL && notdecremented)
|
|
|
|
|
{
|
|
|
|
|
resNodeNum--;
|
|
|
|
|
notdecremented = FALSE;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2021-04-21 19:03:26 +02:00
|
|
|
ResFixDevName(newname, SOURCE, simDev, source);
|
2019-10-17 22:21:56 +02:00
|
|
|
source->rn_name = simDev->source->name;
|
2021-04-21 19:03:26 +02:00
|
|
|
(void)sprintf(newname, "%s%s%d", nodename, ".t", resNodeNum++);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (drain->rn_name != NULL) resNodeNum--;
|
2021-04-21 19:03:26 +02:00
|
|
|
ResFixDevName(newname, DRAIN, simDev, drain);
|
2019-10-17 22:21:56 +02:00
|
|
|
drain->rn_name = simDev->drain->name;
|
2017-04-25 14:41:48 +02:00
|
|
|
/* one to each */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TxError("Missing SD connection\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
if ((source = layoutDev->rd_fet_source) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
if ((drain = layoutDev->rd_fet_drain) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (source != drain)
|
|
|
|
|
{
|
|
|
|
|
if (drain->rn_why & RES_NODE_ORIGIN)
|
|
|
|
|
{
|
|
|
|
|
ResMergeNodes(drain, source, &ResNodeQueue,
|
|
|
|
|
&ResNodeList);
|
|
|
|
|
ResDoneWithNode(drain);
|
|
|
|
|
source = drain;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResMergeNodes(source, drain, &ResNodeQueue,
|
|
|
|
|
&ResNodeList);
|
|
|
|
|
ResDoneWithNode(source);
|
|
|
|
|
drain = source;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
layoutDev->rd_fet_drain = (resNode *)NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (source->rn_name != NULL) resNodeNum--;
|
2021-04-21 19:03:26 +02:00
|
|
|
ResFixDevName(newname, SOURCE, simDev, source);
|
2019-10-17 22:21:56 +02:00
|
|
|
source->rn_name = simDev->source->name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (source->rn_name != NULL && notdecremented)
|
|
|
|
|
{
|
|
|
|
|
resNodeNum--;
|
|
|
|
|
notdecremented = FALSE;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2021-04-21 19:03:26 +02:00
|
|
|
ResFixDevName(newname, SOURCE, simDev, source);
|
2019-10-17 22:21:56 +02:00
|
|
|
source->rn_name = simDev->source->name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TxError("missing SD connection\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
else if (simDev->drain == simNode)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
if ((source = layoutDev->rd_fet_source) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
if ((drain = layoutDev->rd_fet_drain) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (drain != source)
|
|
|
|
|
{
|
|
|
|
|
if (drain->rn_why & ORIGIN)
|
|
|
|
|
{
|
|
|
|
|
ResMergeNodes(drain, source, &ResNodeQueue,
|
|
|
|
|
&ResNodeList);
|
|
|
|
|
ResDoneWithNode(drain);
|
|
|
|
|
source = drain;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ResMergeNodes(source, drain, &ResNodeQueue,
|
|
|
|
|
&ResNodeList);
|
|
|
|
|
ResDoneWithNode(source);
|
|
|
|
|
drain = source;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
layoutDev->rd_fet_source = (resNode *) NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (drain->rn_name != NULL)
|
|
|
|
|
{
|
|
|
|
|
resNodeNum--;
|
|
|
|
|
notdecremented = FALSE;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
ResFixDevName(newname, DRAIN, simDev, drain);
|
|
|
|
|
drain->rn_name = simDev->drain->name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (source->rn_name != NULL && notdecremented)
|
|
|
|
|
{
|
|
|
|
|
resNodeNum--;
|
|
|
|
|
notdecremented = FALSE;
|
2020-05-23 23:13:14 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
ResFixDevName(newname,DRAIN,simDev,source);
|
|
|
|
|
source->rn_name = simDev->drain->name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
TxError("missing SD connection\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
resNodeNum--;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
2019-10-17 22:21:56 +02:00
|
|
|
* ResFixDevName-- Moves device connection to new node.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
2019-10-17 22:21:56 +02:00
|
|
|
* Side Effects: May create a new node. Creates a new device pointer.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2021-04-21 19:03:26 +02:00
|
|
|
ResFixDevName(line, type, device, layoutnode)
|
2017-04-25 14:41:48 +02:00
|
|
|
char line[];
|
|
|
|
|
int type;
|
2019-10-17 22:21:56 +02:00
|
|
|
RDev *device;
|
2017-04-25 14:41:48 +02:00
|
|
|
resNode *layoutnode;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
2019-10-17 22:21:56 +02:00
|
|
|
devPtr *tptr;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (layoutnode->rn_name != NULL)
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
entry = HashFind(&ResNodeTable, layoutnode->rn_name);
|
2017-04-25 14:41:48 +02:00
|
|
|
node = ResInitializeNode(entry);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
entry = HashFind(&ResNodeTable, line);
|
2017-04-25 14:41:48 +02:00
|
|
|
node = ResInitializeNode(entry);
|
|
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
tptr = (devPtr *) mallocMagic((unsigned) (sizeof(devPtr)));
|
|
|
|
|
tptr->thisDev = device;
|
|
|
|
|
tptr->nextDev = node->firstDev;
|
|
|
|
|
node->firstDev = tptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
tptr->terminal = type;
|
|
|
|
|
switch(type)
|
|
|
|
|
{
|
|
|
|
|
case GATE:
|
2019-10-17 22:21:56 +02:00
|
|
|
node->oldname = device->gate->name;
|
|
|
|
|
device->gate = node;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case SOURCE:
|
2019-10-17 22:21:56 +02:00
|
|
|
node->oldname = device->source->name;
|
|
|
|
|
device->source = node;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DRAIN:
|
2019-10-17 22:21:56 +02:00
|
|
|
node->oldname = device->drain->name;
|
|
|
|
|
device->drain = node;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
TxError("Bad Terminal Specifier\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
2021-04-21 19:03:26 +02:00
|
|
|
* ResSortByGate -- sorts device pointers whose terminal field is either
|
2017-04-25 14:41:48 +02:00
|
|
|
* drain or source by gate node number, then by drain (source) number.
|
2020-05-23 23:13:14 +02:00
|
|
|
* This places devices with identical connections next to one
|
2017-04-25 14:41:48 +02:00
|
|
|
* another.
|
|
|
|
|
*
|
|
|
|
|
* Results: none
|
|
|
|
|
*
|
2019-10-17 22:21:56 +02:00
|
|
|
* Side Effects: modifies order of devices
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2019-10-17 22:21:56 +02:00
|
|
|
ResSortByGate(DevpointerList)
|
|
|
|
|
devPtr **DevpointerList;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
int changed=TRUE;
|
|
|
|
|
int localchange=TRUE;
|
2019-10-17 22:21:56 +02:00
|
|
|
devPtr *working, *last=NULL, *current, *gatelist=NULL;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-10-17 22:21:56 +02:00
|
|
|
working = *DevpointerList;
|
2017-04-25 14:41:48 +02:00
|
|
|
while (working != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (working->terminal == GATE)
|
|
|
|
|
{
|
|
|
|
|
current = working;
|
2019-10-17 22:21:56 +02:00
|
|
|
working = working->nextDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (last == NULL)
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
*DevpointerList = working;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
last->nextDev = working;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
current->nextDev = gatelist;
|
2017-04-25 14:41:48 +02:00
|
|
|
gatelist = current;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
last = working;
|
2019-10-17 22:21:56 +02:00
|
|
|
working = working->nextDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (changed == TRUE)
|
|
|
|
|
{
|
|
|
|
|
changed = localchange = FALSE;
|
2019-10-17 22:21:56 +02:00
|
|
|
working = *DevpointerList;
|
2017-04-25 14:41:48 +02:00
|
|
|
last = NULL;
|
2019-10-17 22:21:56 +02:00
|
|
|
while (working != NULL && (current = working->nextDev) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
RDev *w = working->thisDev;
|
|
|
|
|
RDev *c = current->thisDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (w->gate > c->gate)
|
|
|
|
|
{
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
localchange = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else if (w->gate == c->gate &&
|
2020-05-23 23:13:14 +02:00
|
|
|
(working->terminal == SOURCE &&
|
|
|
|
|
current->terminal == SOURCE &&
|
2017-04-25 14:41:48 +02:00
|
|
|
w->drain > c->drain ||
|
2020-05-23 23:13:14 +02:00
|
|
|
working->terminal == SOURCE &&
|
|
|
|
|
current->terminal == DRAIN &&
|
2017-04-25 14:41:48 +02:00
|
|
|
w->drain > c->source ||
|
2020-05-23 23:13:14 +02:00
|
|
|
working->terminal == DRAIN &&
|
|
|
|
|
current->terminal == SOURCE &&
|
2017-04-25 14:41:48 +02:00
|
|
|
w->source > c->drain ||
|
2020-05-23 23:13:14 +02:00
|
|
|
working->terminal == DRAIN &&
|
|
|
|
|
current->terminal == DRAIN &&
|
2017-04-25 14:41:48 +02:00
|
|
|
w->source > c->source))
|
|
|
|
|
{
|
|
|
|
|
changed = TRUE;
|
|
|
|
|
localchange = TRUE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
last = working;
|
2019-10-17 22:21:56 +02:00
|
|
|
working = working->nextDev;
|
2017-04-25 14:41:48 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (localchange)
|
|
|
|
|
{
|
|
|
|
|
localchange = FALSE;
|
|
|
|
|
if (last == NULL)
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
*DevpointerList = current;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
last->nextDev = current;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
working->nextDev = current->nextDev;
|
|
|
|
|
current->nextDev = working;
|
2017-04-25 14:41:48 +02:00
|
|
|
last = current;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (working == NULL)
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
*DevpointerList = gatelist;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
if (working->nextDev != NULL)
|
|
|
|
|
TxError("Bad Device pointer in sort\n");
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
2019-10-17 22:21:56 +02:00
|
|
|
working->nextDev = gatelist;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResWriteLumpFile
|
|
|
|
|
*
|
|
|
|
|
* Results: none
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ResWriteLumpFile(node)
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
{
|
|
|
|
|
int lumpedres;
|
|
|
|
|
|
|
|
|
|
if (ResOptionsFlags & ResOpt_Tdi)
|
|
|
|
|
{
|
|
|
|
|
if (gparams.rg_nodecap != 0)
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
lumpedres = (int)((gparams.rg_Tdi / gparams.rg_nodecap
|
|
|
|
|
- (float)(gparams.rg_bigdevres)) / OHMSTOMILLIOHMS);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
lumpedres = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
lumpedres = gparams.rg_maxres;
|
|
|
|
|
}
|
|
|
|
|
fprintf(ResLumpFile,"R %s %d\n", node->name, lumpedres);
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResAlignNodes --
|
|
|
|
|
* Attempt to put nodes onto a Manhattan grid.
|
|
|
|
|
* At the same time, assign height values to nodes and thickness
|
|
|
|
|
* values to resistors.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ResAlignNodes(nodelist, reslist)
|
|
|
|
|
resNode *nodelist;
|
|
|
|
|
resResistor *reslist;
|
|
|
|
|
{
|
|
|
|
|
resResistor *resistor;
|
|
|
|
|
resNode *node1;
|
|
|
|
|
short i;
|
|
|
|
|
|
|
|
|
|
for (resistor = reslist; resistor->rr_nextResistor != NULL;
|
|
|
|
|
resistor = resistor->rr_nextResistor)
|
|
|
|
|
{
|
|
|
|
|
/* Don't try to align nodes which came from split */
|
|
|
|
|
/* tiles; we assume that the geometry there is */
|
|
|
|
|
/* supposed to be non-Manhattan. */
|
|
|
|
|
|
|
|
|
|
if (resistor->rr_status & RES_DIAGONAL) continue;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 2; i++)
|
|
|
|
|
{
|
|
|
|
|
node1 = resistor->rr_node[i];
|
|
|
|
|
if (resistor->rr_status & RES_EW)
|
|
|
|
|
{
|
|
|
|
|
if (node1->rn_loc.p_y != resistor->rr_cl)
|
|
|
|
|
{
|
|
|
|
|
if (node1->rn_status & RES_NODE_YADJ)
|
|
|
|
|
TxError("Warning: contention over node Y position\n");
|
|
|
|
|
node1->rn_loc.p_y = resistor->rr_cl;
|
|
|
|
|
node1->rn_status |= RES_NODE_YADJ;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (resistor->rr_status & RES_NS)
|
|
|
|
|
{
|
|
|
|
|
if (node1->rn_loc.p_x != resistor->rr_cl)
|
|
|
|
|
{
|
|
|
|
|
if (node1->rn_status & RES_NODE_XADJ)
|
|
|
|
|
TxError("Warning: contention over node X position\n");
|
|
|
|
|
node1->rn_loc.p_x = resistor->rr_cl;
|
|
|
|
|
node1->rn_status |= RES_NODE_XADJ;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResWriteExtFile --
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* 1 if output was generated
|
|
|
|
|
* 0 if no output was generated
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResWriteExtFile(celldef, node, tol, rctol, nidx, eidx)
|
|
|
|
|
CellDef *celldef;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
float tol, rctol;
|
|
|
|
|
int *nidx, *eidx;
|
|
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
float RCdev;
|
2017-04-25 14:41:48 +02:00
|
|
|
char *cp, newname[MAXNAME];
|
2019-10-17 22:21:56 +02:00
|
|
|
devPtr *ptr;
|
|
|
|
|
resDevice *layoutDev, *ResGetDevice();
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-10-17 22:21:56 +02:00
|
|
|
RCdev = gparams.rg_bigdevres * gparams.rg_nodecap;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-04-21 19:03:26 +02:00
|
|
|
if (tol == 0.0 || (node->status & FORCE) ||
|
|
|
|
|
(ResOptionsFlags & ResOpt_ExtractAll) ||
|
|
|
|
|
(ResOptionsFlags & ResOpt_Simplify) == 0 ||
|
|
|
|
|
(rctol + 1) * RCdev < rctol * gparams.rg_Tdi)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
ASSERT(gparams.rg_Tdi != -1, "ResWriteExtFile");
|
|
|
|
|
(void)sprintf(newname,"%s", node->name);
|
|
|
|
|
cp = newname + strlen(newname)-1;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (*cp == '!' || *cp == '#') *cp = '\0';
|
2021-04-21 19:03:26 +02:00
|
|
|
if ((rctol + 1) * RCdev < rctol * gparams.rg_Tdi ||
|
2017-04-25 14:41:48 +02:00
|
|
|
(ResOptionsFlags & ResOpt_Tdi) == 0)
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
if ((ResOptionsFlags & (ResOpt_RunSilent | ResOpt_Tdi)) == ResOpt_Tdi)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
TxPrintf("Adding %s; Tnew = %.2fns, Told = %.2fns\n",
|
|
|
|
|
node->name, gparams.rg_Tdi / Z_TO_P, RCdev / Z_TO_P);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-10-17 22:21:56 +02:00
|
|
|
for (ptr = node->firstDev; ptr != NULL; ptr=ptr->nextDev)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2019-10-17 22:21:56 +02:00
|
|
|
if (layoutDev = ResGetDevice(&ptr->thisDev->location))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
ResFixUpConnections(ptr->thisDev, layoutDev, node, newname);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (ResOptionsFlags & ResOpt_DoExtFile)
|
|
|
|
|
{
|
2021-04-21 19:03:26 +02:00
|
|
|
ResPrintExtNode(ResExtFile, ResNodeList, node->name);
|
|
|
|
|
ResPrintExtRes(ResExtFile, ResResList, newname);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
if (ResOptionsFlags & ResOpt_FastHenry)
|
|
|
|
|
{
|
|
|
|
|
if (ResResList)
|
|
|
|
|
ResAlignNodes(ResNodeList, ResResList);
|
|
|
|
|
ResPrintFHNodes(ResFHFile, ResNodeList, node->name, nidx, celldef);
|
|
|
|
|
ResPrintFHRects(ResFHFile, ResResList, newname, eidx);
|
|
|
|
|
}
|
|
|
|
|
if (ResOptionsFlags & ResOpt_Geometry)
|
|
|
|
|
{
|
|
|
|
|
if (ResResList)
|
|
|
|
|
ResAlignNodes(ResNodeList, ResResList);
|
|
|
|
|
if (ResCreateCenterlines(ResResList, nidx, celldef) < 0)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else return 0;
|
|
|
|
|
}
|
|
|
|
|
|