2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#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 $";
|
|
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResReadSim.c -- Routines to parse .sim files
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "utils/geofast.h"
|
|
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "database/database.h"
|
|
|
|
|
#include "utils/malloc.h"
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
#include "extract/extract.h"
|
|
|
|
|
#include "extract/extractInt.h"
|
2021-04-21 02:45:49 +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"
|
2021-04-21 02:45:49 +02:00
|
|
|
#include "resis/resis.h"
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/* constants defining where various fields can be found in .sim files. */
|
2019-10-17 22:21:56 +02:00
|
|
|
#define RDEV_LENGTH 4
|
|
|
|
|
#define RDEV_WIDTH 5
|
|
|
|
|
#define RDEV_DEVX 6
|
|
|
|
|
#define RDEV_DEVY 7
|
|
|
|
|
#define RDEV_ATTR 8
|
|
|
|
|
#define RDEV_NUM_ATTR 3
|
2017-04-25 14:41:48 +02:00
|
|
|
#define RESNODENAME 1
|
|
|
|
|
#define NODERESISTANCE 2
|
|
|
|
|
#define COUPLETERMINAL1 1
|
|
|
|
|
#define COUPLETERMINAL2 2
|
|
|
|
|
#define COUPLEVALUE 3
|
|
|
|
|
#define REALNAME 1
|
|
|
|
|
#define ALIASNAME 2
|
2021-04-21 19:03:26 +02:00
|
|
|
#define NODES_NODENAME 0
|
|
|
|
|
#define NODES_NODEX 1
|
|
|
|
|
#define NODES_NODEY 2
|
|
|
|
|
#define NODES_NODETYPE 3
|
2017-04-25 14:41:48 +02:00
|
|
|
#define NODE_BBOX_LL_X 5
|
|
|
|
|
#define NODE_BBOX_LL_Y 6
|
|
|
|
|
#define NODE_BBOX_UR_X 7
|
|
|
|
|
#define NODE_BBOX_UR_Y 8
|
|
|
|
|
#define NODELAMBDA 2
|
|
|
|
|
#define NODEUNITS 1
|
|
|
|
|
#define ATTRIBUTENODENAME 1
|
|
|
|
|
#define ATTRIBUTEVALUE 2
|
|
|
|
|
|
|
|
|
|
#define RES_EXT_ATTR 0
|
|
|
|
|
#define RES_EXT_ATTR_NAME 1
|
|
|
|
|
#define RES_EXT_ATTR_X 2
|
|
|
|
|
#define RES_EXT_ATTR_Y 3
|
|
|
|
|
#define RES_EXT_ATTR_TILE 6
|
|
|
|
|
#define RES_EXT_ATTR_TEXT 7
|
|
|
|
|
|
|
|
|
|
|
2023-03-25 21:11:46 +01:00
|
|
|
#define MAXTOKEN 1024
|
2017-04-25 14:41:48 +02:00
|
|
|
#define MAXLINE 40
|
|
|
|
|
#define MAXDIGIT 20
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ResSimNode *ResInitializeNode();
|
|
|
|
|
|
|
|
|
|
ResSimNode *ResOriginalNodes; /*Linked List of Nodes */
|
2019-10-28 18:10:16 +01:00
|
|
|
static float resscale=1.0; /* Scale factor */
|
2019-10-17 22:21:56 +02:00
|
|
|
char RDEV_NOATTR[1]={'0'};
|
2017-04-25 14:41:48 +02:00
|
|
|
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;\
|
2021-05-26 04:41:52 +02:00
|
|
|
(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;\
|
2017-04-25 14:41:48 +02:00
|
|
|
(n)->rn_status = TRUE;\
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Forward declarations */
|
|
|
|
|
|
|
|
|
|
extern void ResSimProcessDrivePoints();
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResReadSim--
|
|
|
|
|
*
|
|
|
|
|
* Results: returns 0 if sim file is correct, 1 if not.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:Reads in SimTable and makes a hash table of nodes.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2021-04-21 02:45:49 +02:00
|
|
|
ResReadSim(simfile, fetproc, capproc, resproc, attrproc, mergeproc, subproc)
|
2021-05-26 04:41:52 +02:00
|
|
|
char *simfile;
|
|
|
|
|
int (*fetproc)(), (*capproc)(), (*resproc)();
|
|
|
|
|
int (*attrproc)(), (*mergeproc)(), (*subproc)();
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[MAXLINE][MAXTOKEN];
|
|
|
|
|
int result, fettype, extfile;
|
|
|
|
|
FILE *fp, *fopen();
|
|
|
|
|
|
|
|
|
|
fp = PaOpen(simfile, "r", ".sim", ".", (char *)NULL, (char **)NULL);
|
|
|
|
|
if (fp == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Cannot open file %s%s\n", simfile, ".sim");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
extfile = 0;
|
|
|
|
|
|
|
|
|
|
/* Read in file */
|
|
|
|
|
while (gettokens(line, fp) != 0)
|
|
|
|
|
{
|
|
|
|
|
fettype = MINFINITY;
|
|
|
|
|
switch(line[0][0])
|
|
|
|
|
{
|
|
|
|
|
case '|':
|
|
|
|
|
if (strcmp(line[NODEUNITS],"units:") == 0)
|
|
|
|
|
{
|
|
|
|
|
resscale = (float)atof(line[NODELAMBDA]);
|
|
|
|
|
if (resscale == 0.0) resscale = 1.0;
|
|
|
|
|
}
|
|
|
|
|
result=0;
|
|
|
|
|
break;
|
|
|
|
|
case 'e':
|
|
|
|
|
fettype = DBTechNameType("efet");
|
|
|
|
|
break;
|
|
|
|
|
case 'd':
|
|
|
|
|
fettype = DBTechNameType("dfet");
|
|
|
|
|
break;
|
|
|
|
|
case 'n':
|
|
|
|
|
fettype = DBTechNameType("nfet");
|
|
|
|
|
break;
|
|
|
|
|
case 'p':
|
|
|
|
|
fettype = DBTechNameType("pfet");
|
|
|
|
|
break;
|
|
|
|
|
case 'b':
|
|
|
|
|
fettype = DBTechNameType("bnpn");
|
|
|
|
|
break;
|
|
|
|
|
case 'C':
|
|
|
|
|
if (capproc) result = (*capproc)(line);
|
|
|
|
|
break;
|
|
|
|
|
case 'R':
|
|
|
|
|
if (resproc) result = (*resproc)(line);
|
|
|
|
|
break;
|
|
|
|
|
case '=':
|
2022-05-02 18:27:39 +02:00
|
|
|
/* Do not merge nodes, as this interferes with */
|
|
|
|
|
/* extresist's primary function. */
|
|
|
|
|
/* if (mergeproc) result = (*mergeproc)(line); */
|
2021-05-26 04:41:52 +02:00
|
|
|
break;
|
|
|
|
|
case 'A':
|
|
|
|
|
if (attrproc)
|
2024-10-04 18:04:43 +02:00
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
result = (*attrproc)(line[ATTRIBUTENODENAME],
|
|
|
|
|
line[ATTRIBUTEVALUE], simfile, &extfile);
|
2024-10-04 18:04:43 +02:00
|
|
|
}
|
|
|
|
|
break;
|
2021-05-26 04:41:52 +02:00
|
|
|
case 'x':
|
|
|
|
|
fettype = DBNumTypes;
|
|
|
|
|
break;
|
|
|
|
|
case 'D':
|
|
|
|
|
case 'c':
|
|
|
|
|
case 'r':
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2025-02-23 12:41:11 +01:00
|
|
|
/* 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'
|
|
|
|
|
*/
|
2021-05-26 04:41:52 +02:00
|
|
|
result = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-15 16:00:02 +02:00
|
|
|
if ((fettype != MINFINITY) && (fettype < 0))
|
2021-05-26 04:41:52 +02:00
|
|
|
{
|
2025-10-15 16:00:02 +02:00
|
|
|
TxError("Error in Reading device line of sim file: ");
|
|
|
|
|
TxError("Ambiguous or unknown device.\n");
|
2021-05-26 04:41:52 +02:00
|
|
|
result = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (fettype == DBNumTypes)
|
|
|
|
|
{
|
|
|
|
|
result = (*subproc)(line);
|
|
|
|
|
}
|
|
|
|
|
else if (fettype != MINFINITY)
|
|
|
|
|
{
|
|
|
|
|
float sheetr;
|
|
|
|
|
ExtDevice *devptr;
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
HashEntry *he;
|
2021-05-26 04:41:52 +02:00
|
|
|
|
|
|
|
|
devptr = ExtCurStyle->exts_device[fettype];
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
he = HashLookOnly(&devptr->exts_deviceResist, "linear");
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
sheetr = (ResValue)(spointertype)HashGetValue(he);
|
|
|
|
|
else
|
|
|
|
|
sheetr = (ResValue)0.0;
|
2021-06-07 03:44:52 +02:00
|
|
|
result = (*fetproc)(line, sheetr, devptr);
|
2021-05-26 04:41:52 +02:00
|
|
|
}
|
|
|
|
|
if (result != 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Error in sim file %s\n", line[0]);
|
2025-02-13 09:22:28 +01:00
|
|
|
fclose(fp);
|
2021-05-26 04:41:52 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fclose(fp);
|
|
|
|
|
return(result);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResReadNode-- Reads in a node file, puts location of nodes into node
|
|
|
|
|
* structures.
|
|
|
|
|
*
|
|
|
|
|
* Results: returns 0 if nodes file is correct, 1 if not.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:see above
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResReadNode(nodefile)
|
2021-05-26 04:41:52 +02:00
|
|
|
char *nodefile;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
char line[MAXLINE][MAXTOKEN];
|
|
|
|
|
FILE *fp, *fopen();
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
char *cp;
|
2019-10-28 18:10:16 +01:00
|
|
|
float lambda;
|
|
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
fp = PaOpen(nodefile, "r", ".nodes", ".", (char *)NULL, (char **)NULL);
|
2017-04-25 14:41:48 +02:00
|
|
|
if (fp == NULL)
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
TxError("Cannot open file %s%s\n", nodefile, ".nodes");
|
|
|
|
|
return 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
while (gettokens(line,fp) != 0)
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
entry = HashFind(&ResNodeTable, line[NODES_NODENAME]);
|
2017-04-25 14:41:48 +02:00
|
|
|
node = ResInitializeNode(entry);
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2021-04-21 19:03:26 +02:00
|
|
|
node->location.p_x = atoi(line[NODES_NODEX]);
|
|
|
|
|
node->location.p_y = atoi(line[NODES_NODEY]);
|
2020-05-23 23:13:14 +02:00
|
|
|
#ifdef ARIEL
|
2021-04-21 19:03:26 +02:00
|
|
|
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]);
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif
|
2024-10-04 18:21:15 +02:00
|
|
|
if ((cp = strchr(line[NODES_NODETYPE], ';'))) *cp = '\0';
|
2021-04-21 19:03:26 +02:00
|
|
|
node->type = DBTechNameType(line[NODES_NODETYPE]);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
if (node->type == -1)
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
TxError("Bad tile type name in %s.nodes file for node %s\n",
|
|
|
|
|
nodefile, node->name);
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("Did you use the newest version of ext2sim?\n");
|
2021-05-26 04:41:52 +02:00
|
|
|
fclose(fp);
|
|
|
|
|
return 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2021-05-26 04:41:52 +02:00
|
|
|
fclose(fp);
|
|
|
|
|
return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* getline-- Gets a line from the current input file and breaks it into
|
2020-05-23 23:13:14 +02:00
|
|
|
* tokens.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results:returns the number of tokens in the current line
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: loads up its input line with the tokens.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-26 04:41:52 +02:00
|
|
|
gettokens(line, fp)
|
|
|
|
|
char line[][MAXTOKEN];
|
|
|
|
|
FILE *fp;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
int i = 0, j = 0;
|
|
|
|
|
int c;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2021-04-21 02:45:49 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResSimSubckt-- Processes a subcircuit line from a sim 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
|
|
|
|
|
* when using extresist.
|
|
|
|
|
*
|
|
|
|
|
* Results: returns 0 if line was added correctly.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: Allocates devices and adds nodes to the node hash table.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResSimSubckt(line)
|
|
|
|
|
char line[][MAXTOKEN];
|
|
|
|
|
{
|
|
|
|
|
RDev *device;
|
|
|
|
|
int rvalue, i, j, k;
|
|
|
|
|
static int nowarning = TRUE;
|
|
|
|
|
float lambda;
|
|
|
|
|
TileType ttype = TT_SPACE;
|
|
|
|
|
char *lptr = NULL, *wptr = NULL;
|
2021-05-22 04:41:51 +02:00
|
|
|
ExtDevice *devptr;
|
2021-04-21 02:45:49 +02:00
|
|
|
|
|
|
|
|
device = (RDev *) mallocMagic((unsigned) (sizeof(RDev)));
|
|
|
|
|
|
|
|
|
|
device->status = FALSE;
|
|
|
|
|
device->nextDev = ResRDevList;
|
|
|
|
|
|
|
|
|
|
lambda = (float)ExtCurStyle->exts_unitsPerLambda / resscale;
|
|
|
|
|
device->location.p_x = 0;
|
|
|
|
|
device->location.p_y = 0;
|
|
|
|
|
|
|
|
|
|
device->rs_gattr=RDEV_NOATTR;
|
|
|
|
|
device->rs_sattr=RDEV_NOATTR;
|
|
|
|
|
device->rs_dattr=RDEV_NOATTR;
|
|
|
|
|
|
|
|
|
|
ResRDevList = device;
|
|
|
|
|
device->layout = NULL;
|
2021-05-26 04:41:52 +02:00
|
|
|
device->source = device->drain = device->gate = device->subs = NULL;
|
2021-04-21 02:45:49 +02:00
|
|
|
|
|
|
|
|
/* The last argument is the name of the device */
|
|
|
|
|
for (i = 1; line[i][0] != '\0'; i++);
|
|
|
|
|
i--;
|
|
|
|
|
|
2021-06-07 03:44:52 +02:00
|
|
|
/* To do: Replace this search with a pre-prepared hash */
|
|
|
|
|
/* table to key off of the device name. */
|
2021-04-21 02:45:49 +02:00
|
|
|
for (j = 0; j < EFDevNumTypes; j++)
|
|
|
|
|
if (!strcmp(EFDevTypes[j], line[i]))
|
|
|
|
|
break;
|
|
|
|
|
|
2023-02-16 17:59:13 +01:00
|
|
|
/* Read attributes, especially to pick up values for L, W, X, and Y;
|
|
|
|
|
* and source and drain area and perimeter, that are critical for use
|
|
|
|
|
* by extresist.
|
2021-04-21 02:45:49 +02:00
|
|
|
*/
|
|
|
|
|
for (k = 1; line[k][0] != '\0'; k++)
|
|
|
|
|
{
|
|
|
|
|
char *eqptr;
|
|
|
|
|
eqptr = strchr(line[k], '=');
|
|
|
|
|
if (eqptr != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (k < i) i = k;
|
|
|
|
|
eqptr++;
|
|
|
|
|
switch (line[k][0]) {
|
|
|
|
|
case 'l':
|
|
|
|
|
lptr = eqptr;
|
|
|
|
|
break;
|
|
|
|
|
case 'w':
|
|
|
|
|
wptr = eqptr;
|
|
|
|
|
break;
|
|
|
|
|
case 'x':
|
|
|
|
|
device->location.p_x = (int)((float)atof(eqptr) / lambda);
|
|
|
|
|
break;
|
|
|
|
|
case 'y':
|
|
|
|
|
device->location.p_y = (int)((float)atof(eqptr) / lambda);
|
|
|
|
|
break;
|
2023-02-16 17:59:13 +01:00
|
|
|
case 's':
|
|
|
|
|
device->rs_sattr = StrDup((char **)NULL, eqptr);
|
|
|
|
|
break;
|
|
|
|
|
case 'd':
|
|
|
|
|
device->rs_dattr = StrDup((char **)NULL, eqptr);
|
|
|
|
|
break;
|
2021-04-21 02:45:49 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-07 03:44:52 +02:00
|
|
|
if (j == EFDevNumTypes)
|
2021-04-21 02:45:49 +02:00
|
|
|
{
|
2021-06-07 03:44:52 +02:00
|
|
|
TxError("Failure to find device type %s\n", line[i]);
|
|
|
|
|
return 1;
|
2021-04-21 02:45:49 +02:00
|
|
|
}
|
2021-06-07 03:44:52 +02:00
|
|
|
ttype = extGetDevType(EFDevTypes[j]);
|
2025-02-13 09:22:28 +01:00
|
|
|
ASSERT(ttype >= 0, "ttype<0");
|
2021-04-21 02:45:49 +02:00
|
|
|
|
2021-06-07 03:44:52 +02:00
|
|
|
/* Find the device record that corresponds to the device name */
|
|
|
|
|
for (devptr = ExtCurStyle->exts_device[ttype]; devptr; devptr = devptr->exts_next)
|
|
|
|
|
if (!strcmp(devptr->exts_deviceName, EFDevTypes[j]))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
device->rs_devptr = devptr;
|
2021-04-21 02:45:49 +02:00
|
|
|
device->rs_ttype = ttype;
|
|
|
|
|
|
|
|
|
|
if (lptr != NULL && wptr != NULL)
|
|
|
|
|
{
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
HashEntry *he;
|
2021-04-21 02:45:49 +02:00
|
|
|
float rpersquare;
|
|
|
|
|
|
A number of changes:
1) Corrected spurious error messages about cells already existing
in GDS when using "flatten" or "flatglob".
2) Fixed handling of resistance as a subcircuit parameter
3) Added area and perimeter resistance for a device; this is done
through the "devresist" statement in the tech file, which is an
extension of the original "fetresist" statement. Where "fetresist"
only supported type "linear", "devresist" supports types "area"
and "perimeter".
4) Support for CDL syntax, including generating subcircuit-like
parameters for components starting with SPICE-standard prefixes
like M, R, C, etc., adding "/" between pins and subcircuit name,
and saving the file as ".cdl" instead of ".spice".
5) Estimated L and W for devices whose geometry is complex and do not
reduce to a simple rectangle. L and W are estimated as the square
root of the area.
6) Changed the method of extracting L and W for diodes to use the same
method as capacitors. Note that diodes are not usually specified
by L and W, but if they are, this will produce the right result.
7) Corrected the reported filename and line number when printing error
messages related to errors inside a technology file, when the
technology file uses "include" to combine multiple files.
2025-10-01 21:17:49 +02:00
|
|
|
he = HashLookOnly(&devptr->exts_deviceResist, "linear");
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
rpersquare = (ResValue)(spointertype)HashGetValue(he);
|
|
|
|
|
else
|
|
|
|
|
rpersquare = (ResValue)0.0;
|
2021-05-21 22:33:20 +02:00
|
|
|
/* Subcircuit types may not have a length or width value, in which */
|
|
|
|
|
/* case it is zero. Don't induce a divide-by-zero error. */
|
|
|
|
|
if (MagAtof(wptr) == 0)
|
|
|
|
|
device->resistance = 0;
|
|
|
|
|
else
|
|
|
|
|
device->resistance = MagAtof(lptr) * rpersquare/MagAtof(wptr);
|
2021-04-21 02:45:49 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
device->resistance = 0;
|
|
|
|
|
|
|
|
|
|
rvalue = 0;
|
|
|
|
|
for (k = 1; k < i; k++)
|
|
|
|
|
{
|
|
|
|
|
if (k > SUBS)
|
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rvalue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
2019-10-17 22:21:56 +02:00
|
|
|
* ResSimDevice-- Processes a device line from a sim file.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results: returns 0 if line was added correctly.
|
|
|
|
|
*
|
2019-10-17 22:21:56 +02:00
|
|
|
* Side Effects: Allocates devices and adds nodes to the node hash table.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2021-06-07 03:44:52 +02:00
|
|
|
ResSimDevice(line, rpersquare, devptr)
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[][MAXTOKEN];
|
|
|
|
|
float rpersquare;
|
2021-06-07 03:44:52 +02:00
|
|
|
ExtDevice *devptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
RDev *device;
|
|
|
|
|
int rvalue, i, j, k;
|
|
|
|
|
char *newattr, tmpattr[MAXTOKEN];
|
|
|
|
|
static int nowarning = TRUE;
|
|
|
|
|
float lambda;
|
2023-02-13 19:41:58 +01:00
|
|
|
ExtDevice *devtest;
|
2021-05-26 04:41:52 +02:00
|
|
|
|
|
|
|
|
if ((line[RDEV_WIDTH][0] == '\0') || (line[RDEV_LENGTH][0] == '\0'))
|
|
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("error in input file:\n");
|
2021-05-26 04:41:52 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
device = (RDev *)mallocMagic((unsigned)(sizeof(RDev)));
|
|
|
|
|
if (nowarning && rpersquare == 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: FET resistance not included or "
|
|
|
|
|
"set to zero in technology file-\n");
|
|
|
|
|
TxError("All driven nodes will be extracted\n");
|
|
|
|
|
nowarning = FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (MagAtof(line[RDEV_WIDTH]) != 0)
|
|
|
|
|
device->resistance = MagAtof(line[RDEV_LENGTH]) * rpersquare /
|
|
|
|
|
MagAtof(line[RDEV_WIDTH]);
|
|
|
|
|
else
|
|
|
|
|
device->resistance = 0;
|
|
|
|
|
|
|
|
|
|
device->status = FALSE;
|
|
|
|
|
device->nextDev = ResRDevList;
|
|
|
|
|
|
2023-02-13 19:41:58 +01:00
|
|
|
/* Check that devptr matches the device name and number of terminals */
|
|
|
|
|
/* Note that this routine is only called for the original "fet" */
|
|
|
|
|
/* types with fixed names, so the names must match and there must */
|
|
|
|
|
/* always be three terminals (two source/drain terminals). */
|
|
|
|
|
|
|
|
|
|
if (devptr->exts_deviceSDCount != 2)
|
|
|
|
|
for (devtest = devptr->exts_next; devtest; devtest = devtest->exts_next)
|
|
|
|
|
if (devtest->exts_deviceSDCount == 2)
|
|
|
|
|
{
|
|
|
|
|
devptr = devtest;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
lambda = (float)ExtCurStyle->exts_unitsPerLambda / resscale;
|
|
|
|
|
device->location.p_x = (int)((float)atof(line[RDEV_DEVX]) / lambda);
|
|
|
|
|
device->location.p_y = (int)((float)atof(line[RDEV_DEVY]) / lambda);
|
|
|
|
|
|
|
|
|
|
device->rs_gattr=RDEV_NOATTR;
|
|
|
|
|
device->rs_sattr=RDEV_NOATTR;
|
|
|
|
|
device->rs_dattr=RDEV_NOATTR;
|
2021-06-07 03:44:52 +02:00
|
|
|
device->rs_devptr = devptr;
|
2021-05-26 04:41:52 +02:00
|
|
|
|
|
|
|
|
device->gate = device->source = device->drain = device->subs = NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-09-08 22:41:36 +02:00
|
|
|
device->rs_ttype = extGetDevType(devptr->exts_deviceName);
|
|
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
/* sim attributes look like g=a1,a2 */
|
|
|
|
|
/* ext attributes are "a1","a2" */
|
2023-02-16 17:59:13 +01:00
|
|
|
/* Do conversion from one to the other here */
|
|
|
|
|
/* NOTE: As of version 8.3.366, .ext attributes will end in two */
|
|
|
|
|
/* integer values, not quoted, for device area and perimeter. Do */
|
|
|
|
|
/* not quote them. */
|
2021-05-26 04:41:52 +02:00
|
|
|
|
|
|
|
|
for (i = RDEV_ATTR; i < RDEV_ATTR + RDEV_NUM_ATTR; i++)
|
|
|
|
|
{
|
2023-02-16 17:59:13 +01:00
|
|
|
char *cptr, *sptr;
|
|
|
|
|
int d1, d2;
|
|
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
if (line[i][0] == '\0') break;
|
2023-02-16 17:59:13 +01:00
|
|
|
|
|
|
|
|
sptr = &line[i][2]; /* Start after "s=" or "d=" */
|
|
|
|
|
tmpattr[0] = '\0';
|
|
|
|
|
while ((cptr = strchr(sptr, ',')) != NULL)
|
2021-05-26 04:41:52 +02:00
|
|
|
{
|
2023-02-16 17:59:13 +01:00
|
|
|
if (sscanf(sptr, "%d,%d", &d1, &d2) == 2)
|
2021-05-26 04:41:52 +02:00
|
|
|
{
|
2023-02-16 17:59:13 +01:00
|
|
|
strcat(tmpattr, sptr);
|
|
|
|
|
sptr = NULL;
|
|
|
|
|
break;
|
2021-05-26 04:41:52 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2023-02-16 17:59:13 +01:00
|
|
|
*cptr = '\0';
|
|
|
|
|
strcat(tmpattr, "\"");
|
|
|
|
|
strcat(tmpattr, sptr);
|
|
|
|
|
strcat(tmpattr, "\",");
|
|
|
|
|
sptr = cptr + 1;
|
|
|
|
|
*cptr = ',';
|
2021-05-26 04:41:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
2023-02-16 17:59:13 +01:00
|
|
|
if (sptr && (strlen(sptr) != 0))
|
|
|
|
|
{
|
|
|
|
|
strcat(tmpattr, "\"");
|
|
|
|
|
strcat(tmpattr, sptr);
|
|
|
|
|
strcat(tmpattr, "\"");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newattr = (char *)mallocMagic(strlen(tmpattr) + 1);
|
|
|
|
|
strcpy(newattr, tmpattr);
|
2021-05-26 04:41:52 +02:00
|
|
|
switch (line[i][0])
|
|
|
|
|
{
|
|
|
|
|
case 'g':
|
|
|
|
|
device->rs_gattr = newattr;
|
|
|
|
|
break;
|
|
|
|
|
case 's':
|
|
|
|
|
device->rs_sattr = newattr;
|
|
|
|
|
break;
|
|
|
|
|
case 'd':
|
|
|
|
|
device->rs_dattr = newattr;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
TxError("Bad fet attribute\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ResRDevList = device;
|
|
|
|
|
device->layout = NULL;
|
|
|
|
|
rvalue = ResSimNewNode(line[GATE], GATE, device) +
|
|
|
|
|
ResSimNewNode(line[SOURCE], SOURCE, device) +
|
|
|
|
|
ResSimNewNode(line[DRAIN], DRAIN, device);
|
|
|
|
|
|
|
|
|
|
return rvalue;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
2021-05-26 04:41:52 +02:00
|
|
|
* ResSimNewNode-- Adds a new node to the Node Hash Table.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results: returns zero if node is added correctly, one otherwise.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: Allocates a new ResSimNode
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2021-04-21 02:45:49 +02:00
|
|
|
ResSimNewNode(line, type, device)
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[];
|
|
|
|
|
int type;
|
|
|
|
|
RDev *device;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
devPtr *tptr;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
if (line[0] == '\0')
|
|
|
|
|
{
|
|
|
|
|
TxError("Missing device connection\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
entry = HashFind(&ResNodeTable, line);
|
|
|
|
|
node = ResInitializeNode(entry);
|
|
|
|
|
tptr = (devPtr *)mallocMagic((unsigned)(sizeof(devPtr)));
|
|
|
|
|
tptr->thisDev = device;
|
|
|
|
|
tptr->nextDev = node->firstDev;
|
|
|
|
|
node->firstDev = tptr;
|
|
|
|
|
tptr->terminal = type;
|
|
|
|
|
switch(type)
|
|
|
|
|
{
|
|
|
|
|
case GATE:
|
|
|
|
|
device->gate = node;
|
|
|
|
|
break;
|
|
|
|
|
case SOURCE:
|
|
|
|
|
device->source = node;
|
|
|
|
|
break;
|
|
|
|
|
case DRAIN:
|
|
|
|
|
device->drain = node;
|
|
|
|
|
break;
|
|
|
|
|
case SUBS:
|
|
|
|
|
device->subs = node;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
TxError("Bad Terminal Specifier\n");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResSimCapacitor-- Adds the capacitance from a C line to the appropriate
|
2020-05-23 23:13:14 +02:00
|
|
|
* node. Coupling capacitors are added twice, moving the capacitance
|
2017-04-25 14:41:48 +02:00
|
|
|
* to the substrate.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Always return 0
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: modifies capacitance field of ResSimNode.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResSimCapacitor(line)
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[][MAXTOKEN];
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
HashEntry *entry1, *entry2;
|
|
|
|
|
ResSimNode *node1, *node2;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
if (line[COUPLETERMINAL1][0] == 0 || line[COUPLETERMINAL2][0] == 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Bad Capacitor\n");
|
|
|
|
|
return(1);
|
|
|
|
|
}
|
|
|
|
|
entry1 = HashFind(&ResNodeTable, line[COUPLETERMINAL1]);
|
|
|
|
|
node1 = ResInitializeNode(entry1);
|
|
|
|
|
if (ResOptionsFlags & ResOpt_Signal)
|
|
|
|
|
{
|
|
|
|
|
node1->capacitance += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
if (strcmp(line[COUPLETERMINAL2], "GND") == 0 ||
|
|
|
|
|
strcmp(line[COUPLETERMINAL2], "Vdd") == 0)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
entry2 = HashFind(&ResNodeTable, line[COUPLETERMINAL2]);
|
|
|
|
|
node2 = ResInitializeNode(entry2);
|
|
|
|
|
node2->capacitance += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(line[COUPLETERMINAL2], "GND") == 0)
|
|
|
|
|
{
|
|
|
|
|
node1->capacitance += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(line[COUPLETERMINAL2], "Vdd") == 0)
|
|
|
|
|
{
|
|
|
|
|
node1->cap_vdd += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
entry2 = HashFind(&ResNodeTable, line[COUPLETERMINAL2]);
|
|
|
|
|
node2 = ResInitializeNode(entry2);
|
|
|
|
|
if (strcmp(line[COUPLETERMINAL1], "GND") == 0)
|
|
|
|
|
{
|
|
|
|
|
node2->capacitance += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(line[COUPLETERMINAL1], "Vdd") == 0)
|
|
|
|
|
{
|
|
|
|
|
node2->cap_vdd += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
node1->cap_couple += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
node2->cap_couple += MagAtof(line[COUPLEVALUE]);
|
|
|
|
|
return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResSimResistor-- Adds the capacitance from a R line to the appropriate
|
2020-05-23 23:13:14 +02:00
|
|
|
* node.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Results
|
|
|
|
|
* Return 0 to keep search going, 1 to abort
|
|
|
|
|
*
|
|
|
|
|
* Side Effects: modifies resistance field of ResSimNode
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResSimResistor(line)
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[][MAXTOKEN];
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
|
|
|
|
|
if (line[RESNODENAME][0] == 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Bad Resistor\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
entry = HashFind(&ResNodeTable, line[RESNODENAME]);
|
|
|
|
|
node = ResInitializeNode(entry);
|
|
|
|
|
if (node->resistance != 0)
|
|
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
TxError("Duplicate Resistance Entries\n");
|
2021-05-26 04:41:52 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
node->resistance = MagAtof(line[NODERESISTANCE]);
|
|
|
|
|
return(0);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* ResSimAttribute--checks to see if a node attribute is a resistance
|
2017-04-25 14:41:48 +02:00
|
|
|
* 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
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2021-05-26 04:41:52 +02:00
|
|
|
ResSimAttribute(aname, avalue, rootname, readextfile)
|
|
|
|
|
char *aname, *avalue, *rootname;
|
|
|
|
|
int *readextfile;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
char digit[MAXDIGIT];
|
|
|
|
|
int i;
|
|
|
|
|
static int notwarned=TRUE;
|
|
|
|
|
|
|
|
|
|
if (aname[0] == 0)
|
|
|
|
|
{
|
|
|
|
|
TxError("Bad Resistor\n");
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
entry = HashFind(&ResNodeTable, aname);
|
|
|
|
|
node = ResInitializeNode(entry);
|
|
|
|
|
if (strncmp(avalue, "res:skip", 8) == 0)
|
|
|
|
|
{
|
|
|
|
|
if (node->status & FORCE)
|
|
|
|
|
{
|
|
|
|
|
TxError("Warning: Node %s is both forced and skipped\n", aname);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
node->status |= SKIP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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++)
|
|
|
|
|
{
|
|
|
|
|
digit[i++] = *avalue;
|
|
|
|
|
}
|
|
|
|
|
digit[i++] = '\0';
|
|
|
|
|
node->minsizeres = MagAtof(digit);
|
|
|
|
|
}
|
|
|
|
|
else if (strncmp(avalue, "res:drive", 9) == 0 &&
|
2017-04-25 14:41:48 +02:00
|
|
|
(ResOptionsFlags & ResOpt_Signal))
|
2021-05-26 04:41:52 +02:00
|
|
|
{
|
|
|
|
|
if (*readextfile == 0)
|
|
|
|
|
{
|
|
|
|
|
ResSimProcessDrivePoints(rootname);
|
|
|
|
|
*readextfile = 1;
|
|
|
|
|
}
|
|
|
|
|
/* is the attribute in root.ext? */
|
|
|
|
|
if (node->drivepoint.p_x != INFINITY)
|
|
|
|
|
{
|
|
|
|
|
node->status |= DRIVELOC;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (notwarned)
|
|
|
|
|
TxError("Drivepoint for %s not defined in %s.ext; is it "
|
|
|
|
|
"defined in a child cell?\n", node->name, rootname);
|
|
|
|
|
notwarned = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
#ifdef ARIEL
|
2021-05-26 04:41:52 +02:00
|
|
|
else if (strncmp(avalue, "res:fix", 7) == 0 &&
|
2017-04-25 14:41:48 +02:00
|
|
|
(ResOptionsFlags & ResOpt_Power))
|
2021-05-26 04:41:52 +02:00
|
|
|
{
|
|
|
|
|
if (*readextfile == 0)
|
|
|
|
|
{
|
|
|
|
|
ResSimProcessFixPoints(rootname);
|
|
|
|
|
*readextfile = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif
|
2024-10-04 18:21:15 +02:00
|
|
|
if ((avalue = strchr(avalue, ',')))
|
2021-05-26 04:41:52 +02:00
|
|
|
{
|
|
|
|
|
ResSimAttribute(aname, avalue + 1, rootname, readextfile);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResSimProcessDrivePoints -- if the sim 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
|
2020-05-23 23:13:14 +02:00
|
|
|
* the first res:drive is encountered. res:drive labels only work if
|
2017-04-25 14:41:48 +02:00
|
|
|
* they are in the root cell.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ResSimProcessDrivePoints(filename)
|
2021-05-26 04:41:52 +02:00
|
|
|
char *filename;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[MAXLINE][MAXTOKEN];
|
|
|
|
|
FILE *fp;
|
|
|
|
|
HashEntry *entry;
|
|
|
|
|
ResSimNode *node;
|
|
|
|
|
|
2023-08-07 21:37:04 +02:00
|
|
|
fp = PaOpen(filename, "r", ".ext", (ExtLocalPath == NULL) ? "." : ExtLocalPath,
|
|
|
|
|
(char *)NULL, (char **)NULL);
|
2021-05-26 04:41:52 +02:00
|
|
|
if (fp == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Cannot open file %s%s\n", filename, ".ext");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
while (gettokens(line,fp) != 0)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp(line[RES_EXT_ATTR], "attr", 4) != 0 ||
|
|
|
|
|
strncmp(line[RES_EXT_ATTR_TEXT], "\"res:drive\"", 11) != 0)
|
|
|
|
|
continue;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
entry = HashFind(&ResNodeTable, line[RES_EXT_ATTR_NAME]);
|
|
|
|
|
node = ResInitializeNode(entry);
|
|
|
|
|
node->drivepoint.p_x = atoi(line[RES_EXT_ATTR_X]);
|
|
|
|
|
node->drivepoint.p_y = atoi(line[RES_EXT_ATTR_Y]);
|
|
|
|
|
node->rs_ttype = DBTechNoisyNameType(line[RES_EXT_ATTR_TILE]);
|
|
|
|
|
}
|
2025-02-13 09:22:28 +01:00
|
|
|
fclose(fp);
|
2021-05-26 04:41:52 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* ResSimProcessFixPoints -- if the sim file contains a "res:fix:name" label
|
|
|
|
|
* and we are checking for power supply noise, then we have to
|
2017-04-25 14:41:48 +02:00
|
|
|
* parse the .ext file looking for the fix label locations. This
|
|
|
|
|
* is only done after the first res:fix label is encountered.
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side Effects:
|
|
|
|
|
* For each new name, allocate memory
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ResSimProcessFixPoints(filename)
|
2021-05-26 04:41:52 +02:00
|
|
|
char *filename;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[MAXLINE][MAXTOKEN], *label, *c;
|
|
|
|
|
FILE *fp;
|
|
|
|
|
ResFixPoint *thisfix;
|
|
|
|
|
|
2023-08-07 21:37:04 +02:00
|
|
|
fp = PaOpen(filename, "r", ".ext", (ExtLocalPath == NULL) ? "." : ExtLocalPath,
|
|
|
|
|
(char *)NULL, (char **)NULL);
|
2021-05-26 04:41:52 +02:00
|
|
|
if (fp == NULL)
|
|
|
|
|
{
|
|
|
|
|
TxError("Cannot open file %s%s\n", filename, ".ext");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
while (gettokens(line, fp) != 0)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp(line[RES_EXT_ATTR], "attr", 4) != 0 ||
|
|
|
|
|
strncmp(line[RES_EXT_ATTR_TEXT], "\"res:fix", 8) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
label = line[RES_EXT_ATTR_TEXT];
|
|
|
|
|
label += 8;
|
|
|
|
|
if (*label == ':') label++;
|
|
|
|
|
if ((c=strrchr(label, '"')) != NULL) *c = '\0';
|
|
|
|
|
else if (*label != '\0')
|
|
|
|
|
{
|
|
|
|
|
TxError("Bad res:fix attribute label %s\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
line[RES_EXT_ATTR_TEXT]);
|
2021-05-26 04:41:52 +02:00
|
|
|
*label ='\0';
|
|
|
|
|
}
|
|
|
|
|
thisfix = (ResFixPoint *)mallocMagic((unsigned)(sizeof(ResFixPoint)
|
|
|
|
|
+ strlen(label)));
|
|
|
|
|
thisfix->fp_next = ResFixList;
|
|
|
|
|
ResFixList = thisfix;
|
|
|
|
|
thisfix->fp_loc.p_x = atoi(line[RES_EXT_ATTR_X]);
|
|
|
|
|
thisfix->fp_loc.p_y = atoi(line[RES_EXT_ATTR_Y]);
|
|
|
|
|
thisfix->fp_ttype = DBTechNoisyNameType(line[RES_EXT_ATTR_TILE]);
|
|
|
|
|
thisfix->fp_tile = NULL;
|
|
|
|
|
strcpy(thisfix->fp_name, label);
|
|
|
|
|
}
|
2025-02-13 09:22:28 +01:00
|
|
|
fclose(fp);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2021-05-26 04:41:52 +02:00
|
|
|
/*
|
2017-04-25 14:41:48 +02:00
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* ResSimMerge-- Processes = line in sim file
|
|
|
|
|
*
|
|
|
|
|
* Results: Success/Failure
|
|
|
|
|
*
|
|
|
|
|
* 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.
|
|
|
|
|
*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ResSimMerge(line)
|
2021-05-26 04:41:52 +02:00
|
|
|
char line[][MAXTOKEN];
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
ResSimNode *node;
|
|
|
|
|
devPtr *ptr;
|
|
|
|
|
|
|
|
|
|
if ((line[ALIASNAME][0] == '\0') || (line[REALNAME][0] == '\0'))
|
|
|
|
|
{
|
|
|
|
|
TxError("Bad node alias line\n");
|
|
|
|
|
return(1);
|
|
|
|
|
}
|
2021-09-14 19:42:27 +02:00
|
|
|
node = ResInitializeNode(HashFind(&ResNodeTable, line[ALIASNAME]));
|
2021-05-26 04:41:52 +02:00
|
|
|
node->status |= FORWARD;
|
2021-09-14 19:42:27 +02:00
|
|
|
node->forward = ResInitializeNode(HashFind(&ResNodeTable, line[REALNAME]));
|
2021-05-26 04:41:52 +02:00
|
|
|
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;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* 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 *
|
|
|
|
|
ResInitializeNode(entry)
|
2021-05-26 04:41:52 +02:00
|
|
|
HashEntry *entry;
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-05-26 04:41:52 +02:00
|
|
|
ResSimNode *node;
|
|
|
|
|
|
|
|
|
|
if ((node = (ResSimNode *) HashGetValue(entry)) == NULL)
|
|
|
|
|
{
|
|
|
|
|
node = (ResSimNode *)mallocMagic((unsigned)(sizeof(ResSimNode)));
|
|
|
|
|
HashSetValue(entry, (char *) node);
|
|
|
|
|
node->nextnode = ResOriginalNodes;
|
|
|
|
|
ResOriginalNodes = node;
|
|
|
|
|
node->status = FALSE;
|
|
|
|
|
node->forward = (ResSimNode *) NULL;
|
|
|
|
|
node->capacitance = 0;
|
|
|
|
|
node->cap_vdd = 0;
|
|
|
|
|
node->cap_couple = 0;
|
|
|
|
|
node->resistance = 0;
|
|
|
|
|
node->type = 0;
|
|
|
|
|
node->firstDev = NULL;
|
|
|
|
|
node->name = entry->h_key.h_name;
|
|
|
|
|
node->oldname = NULL;
|
|
|
|
|
node->drivepoint.p_x = INFINITY;
|
|
|
|
|
node->drivepoint.p_y = INFINITY;
|
|
|
|
|
node->location.p_x = INFINITY;
|
|
|
|
|
node->location.p_y = INFINITY;
|
|
|
|
|
node->rs_sublist[0] = NULL;
|
|
|
|
|
node->rs_sublist[1] = NULL;
|
|
|
|
|
}
|
|
|
|
|
while (node->status & FORWARD)
|
|
|
|
|
{
|
|
|
|
|
node = node->forward;
|
|
|
|
|
}
|
|
|
|
|
return node;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|