2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* EFbuild.c -
|
|
|
|
|
*
|
|
|
|
|
* Procedures for building up the hierarchical representation
|
|
|
|
|
* of a circuit. These are all called from efReadDef() in EFread.c.
|
|
|
|
|
*
|
2020-05-23 23:13:14 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
|
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
|
|
|
* * software and its documentation for any purpose and without *
|
|
|
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
|
|
|
* * notice appear in all copies. The University of California *
|
|
|
|
|
* * makes no representations about the suitability of this *
|
|
|
|
|
* * software for any purpose. It is provided "as is" without *
|
|
|
|
|
* * express or implied warranty. Export of this software outside *
|
|
|
|
|
* * of the United States of America may require an export license. *
|
2017-04-25 14:41:48 +02:00
|
|
|
* *********************************************************************
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifndef lint
|
2020-05-25 21:46:59 +02:00
|
|
|
static char rcsid[] __attribute__ ((unused)) = "$Header$";
|
2017-04-25 14:41:48 +02:00
|
|
|
#endif /* not lint */
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h> /* for atof() */
|
|
|
|
|
#include <string.h>
|
2024-10-04 19:59:39 +02:00
|
|
|
#include <strings.h>
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
#include "utils/magic.h"
|
|
|
|
|
#include "utils/geometry.h"
|
|
|
|
|
#include "utils/hash.h"
|
|
|
|
|
#include "utils/utils.h"
|
|
|
|
|
#include "utils/malloc.h"
|
2021-05-22 04:41:51 +02:00
|
|
|
#include "tiles/tile.h"
|
|
|
|
|
#include "database/database.h" /* for TileType definition */
|
2017-04-25 14:41:48 +02:00
|
|
|
#include "extflat/extflat.h"
|
|
|
|
|
#include "extflat/EFint.h"
|
|
|
|
|
#include "extract/extract.h" /* for device class list */
|
2022-02-08 22:12:07 +01:00
|
|
|
#include "extract/extractInt.h" /* for extGetDevType() */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2022-10-10 11:50:15 +02:00
|
|
|
/* C99 compat */
|
|
|
|
|
#include "textio/textio.h"
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* To avoid allocating ridiculously large amounts of memory to hold
|
|
|
|
|
* transistor types and the names of node types, we maintain the following
|
|
|
|
|
* string tables. Each string (transistor type or Magic layername) appears
|
|
|
|
|
* exactly once in its respective table; each Dev structure's dev_type field
|
|
|
|
|
* is an index into EFDevTypes[], and each node layer name an index into
|
|
|
|
|
* EFLayerNames[].
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* The following are ridiculously high */
|
|
|
|
|
#define MAXTYPES 100
|
|
|
|
|
|
|
|
|
|
/* Table of transistor types */
|
2021-05-27 22:13:06 +02:00
|
|
|
char *EFDevTypes[TT_MAXTYPES];
|
2017-04-25 14:41:48 +02:00
|
|
|
int EFDevNumTypes;
|
|
|
|
|
|
|
|
|
|
/* Table of Magic layers */
|
|
|
|
|
char *EFLayerNames[MAXTYPES] = { "space" };
|
|
|
|
|
int EFLayerNumNames;
|
|
|
|
|
|
|
|
|
|
/* Forward declarations */
|
|
|
|
|
Connection *efAllocConn();
|
|
|
|
|
EFNode *efBuildDevNode();
|
|
|
|
|
void efNodeAddName();
|
2023-04-28 02:47:55 +02:00
|
|
|
EFNode *efNodeMerge();
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
bool efConnBuildName();
|
|
|
|
|
bool efConnInitSubs();
|
|
|
|
|
|
2019-12-08 23:37:48 +01:00
|
|
|
extern float locScale;
|
|
|
|
|
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildNode --
|
|
|
|
|
*
|
|
|
|
|
* Process a "node" line from a .ext file.
|
|
|
|
|
* Creates a new node with an initial name of 'nodeName'
|
|
|
|
|
* and capacitance to substrate 'nodeCap'. If there is
|
|
|
|
|
* already a node by the name of 'nodeName', adds 'nodeCap'
|
|
|
|
|
* to its existing capacitance.
|
|
|
|
|
*
|
|
|
|
|
* In addition, the arguments 'av' and 'ac' are an (argv, argc)
|
|
|
|
|
* vector of pairs of perimeters and areas for each of the
|
|
|
|
|
* resist classes; these are either stored in the newly created
|
|
|
|
|
* node, or added to the values already stored in an existing one.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
2022-11-20 04:02:44 +01:00
|
|
|
* Return a pointer to the new node.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Updates the HashTable and node list of 'def'.
|
|
|
|
|
*
|
|
|
|
|
* EFNode tables:
|
|
|
|
|
* Each hash table of nodes is organized in the following way.
|
|
|
|
|
* This organization is true both for the node tables for each
|
|
|
|
|
* Def, and for the global table of flattened nodes maintained
|
|
|
|
|
* in EFflatten.c (although the flattened nodes use the HierName
|
|
|
|
|
* struct for representing hierarchical names efficiently).
|
|
|
|
|
*
|
|
|
|
|
* Each HashEntry points to a EFNodeName struct. The EFNodeName
|
|
|
|
|
* is a link back to the hash key (a HierName), as well as
|
|
|
|
|
* a link to the actual EFNode for that name. The EFNode points
|
|
|
|
|
* to the first EFNodeName in the NULL-terminated list of all
|
|
|
|
|
* EFNodeNames pointing to that EFNode; the intent is that this
|
|
|
|
|
* first EFNodeName is the "official" or highest-precedence
|
|
|
|
|
* name for the node.
|
|
|
|
|
*
|
|
|
|
|
* The nodes themselves are linked into a circular, doubly
|
|
|
|
|
* linked list, for ease in merging two nodes into a single
|
|
|
|
|
* one as a result of a "connect" statement.
|
|
|
|
|
*
|
|
|
|
|
* HashEntries EFNodeNames EFNodes
|
|
|
|
|
*
|
|
|
|
|
* +---------------+
|
|
|
|
|
* | |
|
|
|
|
|
* V | to from
|
|
|
|
|
* +-------+ +-------+ | prev prev
|
|
|
|
|
* | | ----> | |-+ | ^ |
|
|
|
|
|
* +-------+ +-------+ | | | |
|
|
|
|
|
* | | | | V
|
|
|
|
|
* V | +---------------+
|
|
|
|
|
* +-------+ +-------+ +---> | |
|
|
|
|
|
* | | ----> | | ----> | |
|
|
|
|
|
* +-------+ +-------+ +---> | |
|
|
|
|
|
* | | +---------------+
|
|
|
|
|
* V | ^ |
|
|
|
|
|
* +-------+ +-------+ | | |
|
|
|
|
|
* | | ----> | |-+ | V
|
|
|
|
|
* +-------+ +-------+ from to
|
|
|
|
|
* | next next
|
|
|
|
|
* V
|
|
|
|
|
* NIL
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2022-11-20 04:02:44 +01:00
|
|
|
efBuildNode(def, isSubsnode, isDevSubsnode, isExtNode, nodeName, nodeCap,
|
|
|
|
|
x, y, layerName, av, ac)
|
2017-04-25 14:41:48 +02:00
|
|
|
Def *def; /* Def to which this connection is to be added */
|
2021-12-30 19:27:42 +01:00
|
|
|
bool isSubsnode; /* TRUE if the node is the global substrate */
|
|
|
|
|
bool isDevSubsnode; /* TRUE if the node is a device body connection */
|
2022-11-20 04:02:44 +01:00
|
|
|
bool isExtNode; /* TRUE if this was a "node" or "substrate" in .ext */
|
2017-04-25 14:41:48 +02:00
|
|
|
char *nodeName; /* One of the names for this node */
|
|
|
|
|
double nodeCap; /* Capacitance of this node to ground */
|
|
|
|
|
int x; int y; /* Location of a point inside this node */
|
|
|
|
|
char *layerName; /* Name of tile type */
|
|
|
|
|
char **av; /* Pairs of area, perimeter strings */
|
|
|
|
|
int ac; /* Number of strings in av */
|
|
|
|
|
{
|
|
|
|
|
EFNodeName *newname;
|
|
|
|
|
EFNode *newnode;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
unsigned size;
|
|
|
|
|
int n;
|
2022-11-20 04:02:44 +01:00
|
|
|
LinkedRect *lr;
|
|
|
|
|
Rect rnew;
|
|
|
|
|
int tnew = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
he = HashFind(&def->def_nodes, nodeName);
|
2023-11-24 22:51:26 +01:00
|
|
|
newname = (EFNodeName *)HashGetValue(he);
|
|
|
|
|
|
|
|
|
|
if (newname && (def->def_kills != NULL))
|
|
|
|
|
{
|
|
|
|
|
HashEntry *hek;
|
|
|
|
|
EFNodeName *nn, *knn, *nodeAlias, *lastAlias;
|
|
|
|
|
|
|
|
|
|
/* Watch for nodes that are aliases of the node that was most
|
|
|
|
|
* recently killed. This can occur in .res.ext files where an
|
|
|
|
|
* equivalent node name (alias) is used as one of the node
|
|
|
|
|
* points. It must be regenerated as its own node and removed
|
|
|
|
|
* from the name list of the killed node.
|
|
|
|
|
*/
|
|
|
|
|
hek = HashLookOnly(&def->def_nodes, EFHNToStr(def->def_kills->kill_name));
|
|
|
|
|
if (hek != NULL)
|
|
|
|
|
{
|
|
|
|
|
knn = (EFNodeName *) HashGetValue(hek);
|
|
|
|
|
if ((knn != NULL) && (knn->efnn_node == newname->efnn_node))
|
|
|
|
|
{
|
|
|
|
|
/* Remove alias from killed node's name list */
|
|
|
|
|
lastAlias = NULL;
|
|
|
|
|
for (nodeAlias = knn->efnn_node->efnode_name; nodeAlias != NULL;
|
|
|
|
|
nodeAlias = nodeAlias->efnn_next)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(EFHNToStr(nodeAlias->efnn_hier), nodeName))
|
|
|
|
|
{
|
|
|
|
|
if (lastAlias == NULL)
|
|
|
|
|
knn->efnn_node->efnode_name = nodeAlias->efnn_next;
|
|
|
|
|
else
|
|
|
|
|
lastAlias->efnn_next = nodeAlias->efnn_next;
|
|
|
|
|
EFHNFree(nodeAlias->efnn_hier, (HierName *)NULL, HN_ALLOC);
|
|
|
|
|
freeMagic(nodeAlias);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
lastAlias = nodeAlias;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Force name to be made into a new node */
|
|
|
|
|
newname = (EFNodeName *)NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (newname)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
if (efWarn)
|
|
|
|
|
efReadError("Warning: duplicate node name %s\n", nodeName);
|
|
|
|
|
|
2020-08-02 15:37:45 +02:00
|
|
|
/* newnode should exist if newname does. Having a NULL node */
|
|
|
|
|
/* may be caused by detached labels "connected" to space. */
|
|
|
|
|
|
|
|
|
|
if ((newnode = newname->efnn_node) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-08-02 15:37:45 +02:00
|
|
|
/* Just add to C, perim, area of existing node */
|
|
|
|
|
newnode->efnode_cap += (EFCapValue) nodeCap;
|
|
|
|
|
for (n = 0; n < efNumResistClasses && ac > 1; n++, ac -= 2)
|
|
|
|
|
{
|
|
|
|
|
newnode->efnode_pa[n].pa_area += atoi(*av++);
|
|
|
|
|
newnode->efnode_pa[n].pa_perim += atoi(*av++);
|
|
|
|
|
}
|
2020-10-15 04:53:03 +02:00
|
|
|
|
2021-12-30 19:27:42 +01:00
|
|
|
/* If this node is identified as a device substrate or */
|
|
|
|
|
/* the global substrate, ensure that the corresponding */
|
|
|
|
|
/* flag is set. */
|
|
|
|
|
|
|
|
|
|
if (isDevSubsnode == TRUE)
|
2020-10-15 04:53:03 +02:00
|
|
|
newnode->efnode_flags |= EF_SUBS_NODE;
|
2021-12-30 19:27:42 +01:00
|
|
|
|
|
|
|
|
if (isSubsnode == TRUE)
|
2023-11-15 02:10:57 +01:00
|
|
|
{
|
2021-12-30 19:27:42 +01:00
|
|
|
newnode->efnode_flags |= EF_GLOB_SUBS_NODE;
|
2023-11-15 02:10:57 +01:00
|
|
|
EFCompat = FALSE;
|
|
|
|
|
}
|
2022-11-19 04:28:20 +01:00
|
|
|
|
2022-11-20 04:02:44 +01:00
|
|
|
/* The node is a duplicate port name at a different location. */
|
|
|
|
|
/* If EFSaveLocs is TRUE, then save the layer and position in */
|
|
|
|
|
/* newnode's efnode_disjoint list. */
|
|
|
|
|
|
|
|
|
|
if ((EFSaveLocs == TRUE) && (isExtNode == TRUE))
|
2022-11-19 03:44:06 +01:00
|
|
|
{
|
2022-11-20 04:02:44 +01:00
|
|
|
rnew.r_xbot = (int)(0.5 + (float)x * locScale);
|
|
|
|
|
rnew.r_ybot = (int)(0.5 + (float)y * locScale);
|
|
|
|
|
rnew.r_xtop = rnew.r_xbot + 1;
|
|
|
|
|
rnew.r_ytop = rnew.r_ybot + 1;
|
|
|
|
|
|
|
|
|
|
if (layerName)
|
|
|
|
|
tnew = efBuildAddStr(EFLayerNames, &EFLayerNumNames,
|
|
|
|
|
MAXTYPES, layerName);
|
|
|
|
|
else
|
|
|
|
|
tnew = 0;
|
|
|
|
|
lr = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
|
|
|
|
|
lr->r_r = rnew;
|
|
|
|
|
lr->r_type = tnew;
|
|
|
|
|
lr->r_next = newnode->efnode_disjoint;
|
|
|
|
|
newnode->efnode_disjoint = lr;
|
2022-11-19 03:44:06 +01:00
|
|
|
}
|
2022-11-20 04:02:44 +01:00
|
|
|
return;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-02 15:37:45 +02:00
|
|
|
if (!newname)
|
|
|
|
|
{
|
|
|
|
|
/* Allocate a new node with 'nodeName' as its single name */
|
|
|
|
|
newname = (EFNodeName *) mallocMagic((unsigned)(sizeof (EFNodeName)));
|
2022-11-20 04:02:44 +01:00
|
|
|
newname->efnn_hier = EFStrToHN((HierName *) NULL, nodeName);
|
2020-08-02 15:37:45 +02:00
|
|
|
newname->efnn_port = -1; /* No port assignment */
|
2021-03-17 19:54:36 +01:00
|
|
|
newname->efnn_refc = 0; /* Only reference is self */
|
2020-08-02 15:37:45 +02:00
|
|
|
newname->efnn_next = NULL;
|
|
|
|
|
HashSetValue(he, (char *) newname);
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* New node itself */
|
2019-10-18 20:12:52 +02:00
|
|
|
size = sizeof (EFNode) + (efNumResistClasses - 1) * sizeof (EFPerimArea);
|
2017-04-25 14:41:48 +02:00
|
|
|
newnode = (EFNode *) mallocMagic((unsigned)(size));
|
|
|
|
|
newnode->efnode_cap = nodeCap;
|
2021-12-30 19:27:42 +01:00
|
|
|
newnode->efnode_flags = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
newnode->efnode_attrs = (EFAttr *) NULL;
|
2019-12-08 23:37:48 +01:00
|
|
|
newnode->efnode_loc.r_xbot = (int)(0.5 + (float)x * locScale);
|
|
|
|
|
newnode->efnode_loc.r_ybot = (int)(0.5 + (float)y * locScale);
|
|
|
|
|
newnode->efnode_loc.r_xtop = newnode->efnode_loc.r_xbot + 1;
|
|
|
|
|
newnode->efnode_loc.r_ytop = newnode->efnode_loc.r_ybot + 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
newnode->efnode_client = (ClientData) NULL;
|
2020-03-13 15:33:44 +01:00
|
|
|
newnode->efnode_num = 1;
|
2017-04-25 14:41:48 +02:00
|
|
|
if (layerName) newnode->efnode_type =
|
|
|
|
|
efBuildAddStr(EFLayerNames, &EFLayerNumNames, MAXTYPES, layerName);
|
|
|
|
|
else newnode->efnode_type = 0;
|
|
|
|
|
|
2021-12-30 19:27:42 +01:00
|
|
|
if (isSubsnode == TRUE) newnode->efnode_flags |= EF_GLOB_SUBS_NODE;
|
|
|
|
|
if (isDevSubsnode == TRUE) newnode->efnode_flags |= EF_SUBS_NODE;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
for (n = 0; n < efNumResistClasses && ac > 1; n++, ac -= 2)
|
|
|
|
|
{
|
|
|
|
|
newnode->efnode_pa[n].pa_area = atoi(*av++);
|
|
|
|
|
newnode->efnode_pa[n].pa_perim = atoi(*av++);
|
|
|
|
|
}
|
|
|
|
|
for ( ; n < efNumResistClasses; n++)
|
|
|
|
|
newnode->efnode_pa[n].pa_area = newnode->efnode_pa[n].pa_perim = 0;
|
|
|
|
|
|
|
|
|
|
/* Update back pointers */
|
|
|
|
|
newnode->efnode_name = newname;
|
|
|
|
|
newname->efnn_node = newnode;
|
|
|
|
|
|
|
|
|
|
/* Link the node into the list for this def */
|
|
|
|
|
newnode->efnode_next = def->def_firstn.efnode_next;
|
|
|
|
|
newnode->efnode_prev = (EFNodeHdr *) &def->def_firstn;
|
|
|
|
|
def->def_firstn.efnode_next->efnhdr_prev = (EFNodeHdr *) newnode;
|
|
|
|
|
def->def_firstn.efnode_next = (EFNodeHdr *) newnode;
|
|
|
|
|
|
|
|
|
|
/* If isSubsnode was TRUE, then turn off backwards compatibility mode */
|
|
|
|
|
if (isSubsnode == TRUE) EFCompat = FALSE;
|
2022-11-19 03:44:06 +01:00
|
|
|
|
2022-11-20 04:02:44 +01:00
|
|
|
/* Save location of top-level geometry if EFSaveLocs is TRUE */
|
|
|
|
|
if ((EFSaveLocs == TRUE) && (isExtNode == TRUE))
|
2022-11-19 03:44:06 +01:00
|
|
|
{
|
2022-11-20 04:02:44 +01:00
|
|
|
lr = (LinkedRect *)mallocMagic(sizeof(LinkedRect));
|
|
|
|
|
lr->r_r = newnode->efnode_loc;
|
|
|
|
|
lr->r_type = newnode->efnode_type;
|
|
|
|
|
lr->r_next = (LinkedRect *)NULL;
|
|
|
|
|
newnode->efnode_disjoint = lr;
|
2022-11-19 03:44:06 +01:00
|
|
|
}
|
2022-11-20 04:02:44 +01:00
|
|
|
else
|
|
|
|
|
newnode->efnode_disjoint = (LinkedRect *)NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Process a "subcap" line by adding the specified adjustment
|
|
|
|
|
* value to the indicated node's substrate capacitance.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efAdjustSubCap(def, nodeName, nodeCapAdjust)
|
|
|
|
|
Def *def; /* Def to which this connection is to be added */
|
|
|
|
|
char *nodeName; /* One of the names for this node */
|
|
|
|
|
double nodeCapAdjust; /* Substrate capacitance adjustment */
|
|
|
|
|
{
|
|
|
|
|
EFNodeName *nodename;
|
|
|
|
|
EFNode *node;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
2019-06-06 20:53:07 +02:00
|
|
|
he = HashLookOnly(&def->def_nodes, nodeName);
|
|
|
|
|
if (he && (nodename = (EFNodeName *) HashGetValue(he)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
node = nodename->efnn_node;
|
|
|
|
|
node->efnode_cap += (EFCapValue) nodeCapAdjust;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (efWarn)
|
|
|
|
|
efReadError("Error: subcap has unknown node %s\n", nodeName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildAttr --
|
|
|
|
|
*
|
|
|
|
|
* Prepend another node attribute to the list for node 'nodeName'.
|
|
|
|
|
* The attribute is located at the coordinates given by 'r' and
|
|
|
|
|
* is on the layer 'layerName'. The text of the attribute is 'text'.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildAttr(def, nodeName, r, layerName, text)
|
|
|
|
|
Def *def;
|
|
|
|
|
char *nodeName;
|
|
|
|
|
Rect *r;
|
|
|
|
|
char *layerName;
|
|
|
|
|
char *text;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
EFAttr *ap;
|
|
|
|
|
int size;
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&def->def_nodes, nodeName);
|
|
|
|
|
if (he == NULL || HashGetValue(he) == NULL)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Attribute for nonexistent node %s ignored\n", nodeName);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
|
|
|
|
|
size = ATTRSIZE(strlen(text));
|
|
|
|
|
ap = (EFAttr *) mallocMagic((unsigned)(size));
|
|
|
|
|
(void) strcpy(ap->efa_text, text);
|
|
|
|
|
ap->efa_type =
|
|
|
|
|
efBuildAddStr(EFLayerNames, &EFLayerNumNames, MAXTYPES, layerName);
|
|
|
|
|
ap->efa_loc = *r;
|
|
|
|
|
ap->efa_next = nn->efnn_node->efnode_attrs;
|
|
|
|
|
nn->efnn_node->efnode_attrs = ap;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildDist --
|
|
|
|
|
*
|
|
|
|
|
* Process a "dist" line from a .ext file.
|
|
|
|
|
* Both of the names driver and receiver are pathnames with slashes.
|
|
|
|
|
* Add a new Distance record to the hash table for Def, or update
|
|
|
|
|
* an existing Distance record.
|
|
|
|
|
*
|
|
|
|
|
* This strategy allows the .ext file to contain several distance
|
|
|
|
|
* lines for the same pair of points; we do the compression here
|
|
|
|
|
* rather than requiring it be done during extraction. It's necessary
|
|
|
|
|
* to do compression at some point before flattening; see the description
|
|
|
|
|
* in efFlatDists().
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildDist(def, driver, receiver, min, max)
|
|
|
|
|
Def *def; /* Def for which we're adding a new Distance */
|
|
|
|
|
char *driver; /* Source terminal */
|
|
|
|
|
char *receiver; /* Destination terminal */
|
|
|
|
|
int min, max; /* Minimum and maximum acyclic distance from source
|
|
|
|
|
* to destination.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Distance *dist, distKey;
|
|
|
|
|
HierName *hn1, *hn2;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
|
|
|
|
|
hn1 = EFStrToHN((HierName *) NULL, driver);
|
|
|
|
|
hn2 = EFStrToHN((HierName *) NULL, receiver);
|
|
|
|
|
distKey.dist_min = min;
|
|
|
|
|
distKey.dist_max = max;
|
|
|
|
|
if (EFHNBest(hn1, hn2))
|
|
|
|
|
{
|
|
|
|
|
distKey.dist_1 = hn1;
|
|
|
|
|
distKey.dist_2 = hn2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
distKey.dist_1 = hn2;
|
|
|
|
|
distKey.dist_2 = hn1;
|
|
|
|
|
}
|
|
|
|
|
#ifdef notdef
|
|
|
|
|
TxError("ADD %s ", EFHNToStr(distKey.dist_1));
|
|
|
|
|
TxError("%s ", EFHNToStr(distKey.dist_2));
|
|
|
|
|
TxError("%d %d\n", min, max);
|
|
|
|
|
#endif /* notdef */
|
|
|
|
|
|
|
|
|
|
he = HashFind(&def->def_dists, (char *) &distKey);
|
2024-10-04 18:20:25 +02:00
|
|
|
if ((dist = (Distance *) HashGetValue(he)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* There was already an entry in the table; update it
|
|
|
|
|
* to reflect new minimum and maximum distances. We
|
|
|
|
|
* can free the keys since they were already in the
|
|
|
|
|
* table.
|
|
|
|
|
*/
|
|
|
|
|
dist->dist_min = MIN(dist->dist_min, min);
|
|
|
|
|
dist->dist_max = MAX(dist->dist_max, max);
|
|
|
|
|
EFHNFree(hn1, (HierName *) NULL, HN_ALLOC);
|
|
|
|
|
EFHNFree(hn2, (HierName *) NULL, HN_ALLOC);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* When the key was installed in the hash table, it was
|
|
|
|
|
* a copy of the Distance 'distKey'. Leave this as the
|
|
|
|
|
* value of the HashEntry.
|
|
|
|
|
*/
|
|
|
|
|
HashSetValue(he, (ClientData) he->h_key.h_ptr);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildKill --
|
|
|
|
|
*
|
|
|
|
|
* Process a "killnode" line from a .ext file.
|
|
|
|
|
* Prepends a Kill to the list for def.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildKill(def, name)
|
|
|
|
|
Def *def; /* Def for which we're adding a new Kill */
|
|
|
|
|
char *name; /* Name of node to die */
|
|
|
|
|
{
|
|
|
|
|
Kill *kill;
|
|
|
|
|
|
|
|
|
|
kill = (Kill *) mallocMagic((unsigned)(sizeof (Kill)));
|
|
|
|
|
kill->kill_name = EFStrToHN((HierName *) NULL, name);
|
|
|
|
|
kill->kill_next = def->def_kills;
|
|
|
|
|
def->def_kills = kill;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildEquiv --
|
|
|
|
|
*
|
|
|
|
|
* Process an "equiv" line from a .ext file.
|
|
|
|
|
* One of the names 'nodeName1' or 'nodeName2' should be a name for
|
|
|
|
|
* an existing node in the def 'def'. We simply prepend this name to
|
|
|
|
|
* the list of names for that node.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2023-07-08 18:14:57 +02:00
|
|
|
efBuildEquiv(def, nodeName1, nodeName2, resist, isspice)
|
2017-04-25 14:41:48 +02:00
|
|
|
Def *def; /* Def for which we're adding a new node name */
|
|
|
|
|
char *nodeName1; /* One of node names to be made equivalent */
|
|
|
|
|
char *nodeName2; /* Other name to be made equivalent. One of nodeName1
|
|
|
|
|
* or nodeName2 must already be known.
|
|
|
|
|
*/
|
2022-05-02 18:27:39 +02:00
|
|
|
bool resist; /* True if "extresist on" option was selected */
|
2023-07-28 15:40:41 +02:00
|
|
|
bool isspice; /* Passed from EFReadFile(), is only TRUE when
|
|
|
|
|
* running ext2spice. Indicates that nodes are
|
|
|
|
|
* case-insensitive.
|
2023-07-08 18:14:57 +02:00
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
EFNodeName *nn1, *nn2;
|
|
|
|
|
HashEntry *he1, *he2;
|
|
|
|
|
|
|
|
|
|
/* Look up both names in the hash table for this def */
|
|
|
|
|
he1 = HashFind(&def->def_nodes, nodeName1);
|
|
|
|
|
he2 = HashFind(&def->def_nodes, nodeName2);
|
|
|
|
|
|
|
|
|
|
nn1 = (EFNodeName *) HashGetValue(he1);
|
|
|
|
|
nn2 = (EFNodeName *) HashGetValue(he2);
|
|
|
|
|
|
2021-03-18 16:37:44 +01:00
|
|
|
if (nn1 == nn2) return; /* These nodes already merged */
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (nn2 == (EFNodeName *) NULL)
|
|
|
|
|
{
|
2023-06-13 23:14:36 +02:00
|
|
|
bool isNew = TRUE;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Create nodeName1 if it doesn't exist */
|
|
|
|
|
if (nn1 == (EFNodeName *) NULL)
|
|
|
|
|
{
|
|
|
|
|
if (efWarn)
|
|
|
|
|
efReadError("Creating new node %s\n", nodeName1);
|
2022-11-20 04:02:44 +01:00
|
|
|
efBuildNode(def, FALSE, FALSE, FALSE,
|
2017-04-25 14:41:48 +02:00
|
|
|
nodeName1, (double)0, 0, 0,
|
|
|
|
|
(char *) NULL, (char **) NULL, 0);
|
|
|
|
|
nn1 = (EFNodeName *) HashGetValue(he1);
|
2023-06-13 23:14:36 +02:00
|
|
|
isNew = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make nodeName2 be another alias for node1 */
|
|
|
|
|
efNodeAddName(nn1->efnn_node, he2,
|
2023-06-13 23:14:36 +02:00
|
|
|
EFStrToHN((HierName *) NULL, nodeName2), isNew);
|
2017-04-25 14:41:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2020-05-24 03:33:42 +02:00
|
|
|
else if (nn2->efnn_node == (EFNode *)NULL)
|
|
|
|
|
return; /* Repeated "equiv" statement */
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2021-07-11 03:13:24 +02:00
|
|
|
/* If both names exist and are for different ports, then keep */
|
|
|
|
|
/* them separate and add a zero ohm resistor or a zero volt */
|
|
|
|
|
/* source between them, based on the method set by "ext2spice */
|
|
|
|
|
/* shorts". */
|
|
|
|
|
|
|
|
|
|
if (nn1 && nn2 && (nn1->efnn_port >= 0) && (nn2->efnn_port >= 0) &&
|
|
|
|
|
(nn1->efnn_port != nn2->efnn_port))
|
|
|
|
|
{
|
2023-07-08 18:47:09 +02:00
|
|
|
bool equalByCase = FALSE;
|
2023-07-08 18:14:57 +02:00
|
|
|
if (isspice)
|
2021-07-11 03:13:24 +02:00
|
|
|
{
|
2023-07-08 18:14:57 +02:00
|
|
|
/* If ports have the same name under the assumption of
|
|
|
|
|
* case-insensitivity, then just quietly merge them.
|
|
|
|
|
*/
|
|
|
|
|
if (!strcasecmp(nodeName1, nodeName2)) equalByCase = TRUE;
|
2021-07-11 03:13:24 +02:00
|
|
|
}
|
2023-07-08 18:14:57 +02:00
|
|
|
if (!equalByCase)
|
2021-09-13 16:36:01 +02:00
|
|
|
{
|
2023-07-08 18:14:57 +02:00
|
|
|
if ((EFOutputFlags & EF_SHORT_MASK) != EF_SHORT_NONE)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int sdev;
|
|
|
|
|
char *argv[10], zeroarg[] = "0";
|
|
|
|
|
|
|
|
|
|
if ((EFOutputFlags & EF_SHORT_MASK) == EF_SHORT_R)
|
|
|
|
|
sdev = DEV_RES;
|
|
|
|
|
else
|
|
|
|
|
sdev = DEV_VOLT;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 10; i++) argv[i] = zeroarg;
|
|
|
|
|
argv[0] = StrDup((char **)NULL, "0.0");
|
|
|
|
|
argv[1] = StrDup((char **)NULL, "dummy");
|
|
|
|
|
argv[4] = StrDup((char **)NULL, nodeName1);
|
|
|
|
|
argv[7] = StrDup((char **)NULL, nodeName2);
|
|
|
|
|
efBuildDevice(def, sdev, "None", &GeoNullRect, 10, argv);
|
|
|
|
|
freeMagic(argv[0]);
|
|
|
|
|
freeMagic(argv[1]);
|
|
|
|
|
freeMagic(argv[4]);
|
|
|
|
|
freeMagic(argv[7]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (!resist)
|
|
|
|
|
TxError("Warning: Ports \"%s\" and \"%s\" are electrically shorted.\n",
|
|
|
|
|
nodeName1, nodeName2);
|
|
|
|
|
else
|
|
|
|
|
/* Do not merge the nodes when folding in extresist parasitics */
|
|
|
|
|
return;
|
2021-09-13 16:36:01 +02:00
|
|
|
}
|
2021-07-11 03:13:24 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* If both names exist and are for different nodes, merge them */
|
|
|
|
|
if (nn1)
|
|
|
|
|
{
|
2023-04-28 02:47:55 +02:00
|
|
|
EFNode *lostnode;
|
|
|
|
|
|
2020-05-24 03:33:42 +02:00
|
|
|
if (nn1->efnn_node == (EFNode *)NULL)
|
|
|
|
|
return; /* Repeated "equiv" statement */
|
2017-04-25 14:41:48 +02:00
|
|
|
if (nn1->efnn_node != nn2->efnn_node)
|
|
|
|
|
{
|
2021-03-18 16:37:44 +01:00
|
|
|
struct efnode *node1 = nn1->efnn_node;
|
|
|
|
|
struct efnode *node2 = nn2->efnn_node;
|
|
|
|
|
HashSearch hs;
|
2023-04-28 02:47:55 +02:00
|
|
|
HashEntry *he;
|
2021-03-18 16:37:44 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (efWarn)
|
|
|
|
|
efReadError("Merged nodes %s and %s\n", nodeName1, nodeName2);
|
2023-04-28 02:47:55 +02:00
|
|
|
lostnode = efNodeMerge(&nn1->efnn_node, &nn2->efnn_node);
|
2020-03-20 19:50:56 +01:00
|
|
|
if (nn1->efnn_port > 0) nn2->efnn_port = nn1->efnn_port;
|
|
|
|
|
else if (nn2->efnn_port > 0) nn1->efnn_port = nn2->efnn_port;
|
2021-03-17 01:31:29 +01:00
|
|
|
|
2023-04-28 02:47:55 +02:00
|
|
|
/* Check if there are any device terminals pointing to the
|
|
|
|
|
* node that was just removed.
|
|
|
|
|
*/
|
|
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:20:25 +02:00
|
|
|
while ((he = HashNext(&def->def_devs, &hs)))
|
2023-04-28 02:47:55 +02:00
|
|
|
{
|
|
|
|
|
Dev *dev;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
dev = (Dev *)HashGetValue(he);
|
|
|
|
|
for (n = 0; n < dev->dev_nterm; n++)
|
|
|
|
|
if (dev->dev_terms[n].dterm_node == lostnode)
|
|
|
|
|
dev->dev_terms[n].dterm_node =
|
|
|
|
|
(nn1->efnn_node == NULL) ?
|
|
|
|
|
nn2->efnn_node : nn1->efnn_node;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-17 01:31:29 +01:00
|
|
|
/* If a node has been merged away, make sure that its name */
|
2021-03-18 16:37:44 +01:00
|
|
|
/* and all aliases point to the merged name's hash. */
|
2021-03-17 01:31:29 +01:00
|
|
|
|
|
|
|
|
if (nn1->efnn_node == NULL)
|
2021-03-17 19:54:36 +01:00
|
|
|
{
|
|
|
|
|
nn2->efnn_refc += nn1->efnn_refc + 1;
|
2021-03-18 16:37:44 +01:00
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:20:25 +02:00
|
|
|
while ((he1 = HashNext(&def->def_nodes, &hs)))
|
2021-03-18 16:37:44 +01:00
|
|
|
if ((EFNodeName *)HashGetValue(he1) == nn1)
|
|
|
|
|
HashSetValue(he1, (char *)nn2);
|
2021-03-17 19:54:36 +01:00
|
|
|
}
|
2021-03-17 01:31:29 +01:00
|
|
|
else if (nn2->efnn_node == NULL)
|
2021-03-17 19:54:36 +01:00
|
|
|
{
|
|
|
|
|
nn1->efnn_refc += nn2->efnn_refc + 1;
|
2021-03-18 16:37:44 +01:00
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:20:25 +02:00
|
|
|
while ((he2 = HashNext(&def->def_nodes, &hs)))
|
2021-03-18 16:37:44 +01:00
|
|
|
if ((EFNodeName *)HashGetValue(he2) == nn2)
|
|
|
|
|
HashSetValue(he2, (char *)nn1);
|
2021-03-17 19:54:36 +01:00
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make nodeName1 be another alias for node2 */
|
|
|
|
|
efNodeAddName(nn2->efnn_node, he1,
|
2023-06-13 23:14:36 +02:00
|
|
|
EFStrToHN((HierName *) NULL, nodeName1), FALSE);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
DevParam *
|
|
|
|
|
efGetDeviceParams(name)
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
DevParam *plist = NULL;
|
|
|
|
|
|
|
|
|
|
he = HashLookOnly(&efDevParamTable, (char *)name);
|
|
|
|
|
if (he != NULL)
|
|
|
|
|
plist = (DevParam *)HashGetValue(he);
|
|
|
|
|
return plist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildDeviceParams --
|
|
|
|
|
*
|
|
|
|
|
* Fill in a device parameter hash table entry from a "parameters" line in
|
|
|
|
|
* the .ext file.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildDeviceParams(name, argc, argv)
|
|
|
|
|
char *name;
|
|
|
|
|
int argc;
|
|
|
|
|
char *argv[];
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
DevParam *plist = NULL, *newparm;
|
|
|
|
|
char *pptr;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
he = HashFind(&efDevParamTable, name);
|
|
|
|
|
plist = (DevParam *)HashGetValue(he);
|
|
|
|
|
if (plist != NULL) return; /* Already got one! */
|
|
|
|
|
|
|
|
|
|
/* Parse arguments for each parameter */
|
|
|
|
|
for (n = 0; n < argc; n++)
|
|
|
|
|
{
|
2023-06-22 02:44:38 +02:00
|
|
|
char *mult, *offset;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
pptr = strchr(argv[n], '=');
|
|
|
|
|
if (pptr == NULL)
|
|
|
|
|
{
|
2020-05-23 23:13:14 +02:00
|
|
|
efReadError("Bad parameter assignment \"%s\" for device \"%s\"\n",
|
2017-04-25 14:41:48 +02:00
|
|
|
argv[n], name);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
newparm = (DevParam *)mallocMagic(sizeof(DevParam));
|
|
|
|
|
newparm->parm_type[0] = *argv[n];
|
|
|
|
|
if ((pptr - argv[n]) == 1)
|
|
|
|
|
newparm->parm_type[1] = '\0';
|
|
|
|
|
else
|
|
|
|
|
newparm->parm_type[1] = *(argv[n] + 1);
|
|
|
|
|
|
|
|
|
|
if ((mult = strchr(pptr + 1, '*')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*mult = '\0';
|
|
|
|
|
newparm->parm_scale = atof(mult + 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
2023-06-23 14:39:59 +02:00
|
|
|
{
|
2017-04-25 14:41:48 +02:00
|
|
|
newparm->parm_scale = 1.0;
|
|
|
|
|
|
2023-06-23 14:39:59 +02:00
|
|
|
/* NOTE: If extending feature to allow for both scale
|
|
|
|
|
* and offset, be sure to distinguish between +/- as an
|
|
|
|
|
* offset and +/- as a sign.
|
|
|
|
|
*/
|
|
|
|
|
if ((offset = strchr(pptr + 1, '+')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*offset = '\0';
|
2023-10-25 02:29:04 +02:00
|
|
|
newparm->parm_offset = atoi(offset + 1);
|
2023-06-23 14:39:59 +02:00
|
|
|
}
|
|
|
|
|
else if ((offset = strchr(pptr + 1, '-')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
*offset = '\0';
|
2023-10-25 02:29:04 +02:00
|
|
|
newparm->parm_offset = -atoi(offset + 1);
|
2023-06-23 14:39:59 +02:00
|
|
|
}
|
|
|
|
|
else
|
2023-10-25 02:29:04 +02:00
|
|
|
newparm->parm_offset = 0;
|
2023-06-22 02:44:38 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
// For parameters defined for cell defs, copy the whole
|
|
|
|
|
// expression verbatim into parm_name. parm_type is
|
|
|
|
|
// reassigned to be a numerical order.
|
|
|
|
|
|
|
|
|
|
if (name[0] == ':')
|
|
|
|
|
{
|
|
|
|
|
newparm->parm_name = StrDup((char **)NULL, argv[n]);
|
2019-10-02 01:32:52 +02:00
|
|
|
newparm->parm_type[0] = '0' + n / 10;
|
|
|
|
|
newparm->parm_type[1] = '0' + n % 10;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
newparm->parm_name = StrDup((char **)NULL, pptr + 1);
|
|
|
|
|
newparm->parm_next = plist;
|
|
|
|
|
plist = newparm;
|
|
|
|
|
}
|
|
|
|
|
HashSetValue(he, (char *)plist);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildDevice --
|
|
|
|
|
*
|
|
|
|
|
* Process a device line from a .ext file.
|
|
|
|
|
* The number of terminals in the dev is argc/3 (which must be integral).
|
|
|
|
|
* Each block of 3 strings in argv describes a single terminal; see the
|
|
|
|
|
* comments below for their interpretation.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns 0 on success, 1 on failure to parse any terminal's values
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Prepends this dev to the list for the def 'def'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
efBuildDevice(def, class, type, r, argc, argv)
|
|
|
|
|
Def *def; /* Def to which this connection is to be added */
|
|
|
|
|
char class; /* Class (dev, bjt, etc.) of this device */
|
|
|
|
|
char *type; /* Type (name) of this device */
|
|
|
|
|
Rect *r; /* Coordinates of 1x1 rectangle entirely inside device */
|
|
|
|
|
int argc; /* Size of argv */
|
|
|
|
|
char *argv[]; /* Tokens for the rest of the dev line.
|
2019-10-28 18:10:16 +01:00
|
|
|
* Starts with the last two position values, used to
|
|
|
|
|
* hash the device record. The next arguments depend
|
|
|
|
|
* on the type of device. The rest are taken in groups
|
|
|
|
|
* of 3, one for each terminal. Each group of 3 consists
|
|
|
|
|
* of the node name to which the terminal connects, the
|
|
|
|
|
* length of the terminal, and an attribute list (or the
|
|
|
|
|
* token 0).
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
int n, nterminals, pn;
|
2019-10-28 18:10:16 +01:00
|
|
|
HashEntry *he;
|
2017-04-25 14:41:48 +02:00
|
|
|
DevTerm *term;
|
|
|
|
|
Dev *newdev, devtmp;
|
|
|
|
|
DevParam *newparm, *devp, *sparm;
|
2021-05-22 04:41:51 +02:00
|
|
|
TileType ttype;
|
|
|
|
|
int dev_type;
|
2017-04-25 14:41:48 +02:00
|
|
|
char ptype, *pptr, **av;
|
2019-11-13 19:05:03 +01:00
|
|
|
char devhash[64];
|
2017-04-25 14:41:48 +02:00
|
|
|
int argstart = 1; /* start of terminal list in argv[] */
|
|
|
|
|
bool hasModel = strcmp(type, "None") ? TRUE : FALSE;
|
|
|
|
|
|
|
|
|
|
int area, perim; /* Total area, perimeter of primary type (i.e., channel) */
|
|
|
|
|
|
2019-10-28 18:10:16 +01:00
|
|
|
newdev = (Dev *)NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
devtmp.dev_subsnode = NULL;
|
|
|
|
|
devtmp.dev_cap = 0.0;
|
|
|
|
|
devtmp.dev_res = 0.0;
|
|
|
|
|
devtmp.dev_area = 0;
|
|
|
|
|
devtmp.dev_perim = 0;
|
|
|
|
|
devtmp.dev_length = 0;
|
|
|
|
|
devtmp.dev_width = 0;
|
|
|
|
|
devtmp.dev_params = NULL;
|
|
|
|
|
|
|
|
|
|
switch (class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_FET:
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
case DEV_BJT:
|
|
|
|
|
argstart = 3;
|
|
|
|
|
break;
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
argstart = 0;
|
|
|
|
|
break;
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
if (hasModel)
|
|
|
|
|
argstart = 2;
|
|
|
|
|
break;
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
argstart = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
devp = efGetDeviceParams(type);
|
|
|
|
|
|
|
|
|
|
/* Parse initial arguments for parameters */
|
|
|
|
|
while ((pptr = strchr(argv[argstart], '=')) != NULL)
|
|
|
|
|
{
|
|
|
|
|
// Check if this parameter is in the table.
|
|
|
|
|
// If so, handle appropriately. Otherwise, the
|
|
|
|
|
// parameter gets saved verbatim locally. The
|
|
|
|
|
// "parameters" line comes before any "device" line
|
|
|
|
|
// in the .ext file, so the table should be complete.
|
|
|
|
|
|
|
|
|
|
*pptr = '\0';
|
|
|
|
|
for (sparm = devp; sparm; sparm = sparm->parm_next)
|
|
|
|
|
if (!strcasecmp(sparm->parm_type, argv[argstart]))
|
|
|
|
|
break;
|
|
|
|
|
*pptr = '=';
|
|
|
|
|
if (sparm == NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Copy the parameter into dev_params */
|
|
|
|
|
/* (parm_type and parm_scale records are not used) */
|
|
|
|
|
newparm = (DevParam *)mallocMagic(sizeof(DevParam));
|
|
|
|
|
newparm->parm_name = StrDup((char **)NULL, argv[argstart]);
|
|
|
|
|
newparm->parm_next = devtmp.dev_params;
|
|
|
|
|
devtmp.dev_params = newparm;
|
|
|
|
|
argstart++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pptr++;
|
|
|
|
|
switch(*argv[argstart])
|
|
|
|
|
{
|
|
|
|
|
case 'a':
|
|
|
|
|
if ((pptr - argv[argstart]) == 2)
|
|
|
|
|
devtmp.dev_area = atoi(pptr);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pn = *(argv[argstart] + 1) - '0';
|
|
|
|
|
if (pn == 0)
|
2019-12-08 23:37:48 +01:00
|
|
|
devtmp.dev_area = (int)(0.5 + (float)atoi(pptr)
|
|
|
|
|
* locScale * locScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Otherwise, punt */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'p':
|
|
|
|
|
if ((pptr - argv[argstart]) == 2)
|
|
|
|
|
devtmp.dev_perim = atoi(pptr);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pn = *(argv[argstart] + 1) - '0';
|
|
|
|
|
if (pn == 0)
|
2019-12-08 23:37:48 +01:00
|
|
|
devtmp.dev_perim = (int)(0.5 + (float)atoi(pptr) * locScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Otherwise, use verbatim */
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'l':
|
2019-12-08 23:37:48 +01:00
|
|
|
devtmp.dev_length = (int)(0.5 + (float)atoi(pptr) * locScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case 'w':
|
2019-12-08 23:37:48 +01:00
|
|
|
devtmp.dev_width = (int)(0.5 + (float)atoi(pptr) * locScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case 'c':
|
|
|
|
|
devtmp.dev_cap = (float)atof(pptr);
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
devtmp.dev_res = (float)atof(pptr);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
argstart++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check for optional substrate node */
|
|
|
|
|
switch (class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
case DEV_CSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
case DEV_SUBCKT:
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
case DEV_PDIODE:
|
|
|
|
|
n = argc - argstart;
|
|
|
|
|
if ((n % 3) == 1)
|
|
|
|
|
{
|
|
|
|
|
if (strncmp(argv[argstart], "None", 4) != 0)
|
|
|
|
|
devtmp.dev_subsnode = efBuildDevNode(def, argv[argstart], TRUE);
|
|
|
|
|
|
|
|
|
|
argstart++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Between argstart and argc, we should only have terminal triples */
|
|
|
|
|
if (((argc - argstart) % 3) != 0)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
nterminals = (argc - argstart) / 3;
|
|
|
|
|
|
2021-05-27 22:13:06 +02:00
|
|
|
dev_type = efBuildAddStr(EFDevTypes, &EFDevNumTypes, TT_MAXTYPES, type);
|
2019-10-28 18:10:16 +01:00
|
|
|
|
2021-05-22 04:41:51 +02:00
|
|
|
/* Determine if this device has been seen before */
|
|
|
|
|
/* NOTE: This is done by tile type, not name, because the extresist
|
|
|
|
|
* device extraction is less sophisticated than the standard extraction
|
|
|
|
|
* and does not differentiate between different device names belonging
|
|
|
|
|
* to the same tile type. The extGetDevType() function is not efficient,
|
|
|
|
|
* and all of this needs to be done better.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
ttype = extGetDevType(type);
|
2021-07-11 03:13:24 +02:00
|
|
|
if (ttype < 0)
|
|
|
|
|
{
|
|
|
|
|
/* For zero-ohm resistors used to separate ports on the same */
|
|
|
|
|
/* net, generate a unique devhash. */
|
|
|
|
|
ttype = DBNumTypes;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
sprintf(devhash, "%dx%d_%d", r->r_xbot, r->r_ybot, ttype);
|
|
|
|
|
he = HashLookOnly(&def->def_devs, devhash);
|
|
|
|
|
if (he == NULL) break;
|
|
|
|
|
ttype++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-22 04:41:51 +02:00
|
|
|
sprintf(devhash, "%dx%d_%d", r->r_xbot, r->r_ybot, ttype);
|
2019-10-28 18:10:16 +01:00
|
|
|
he = HashFind(&def->def_devs, devhash);
|
|
|
|
|
newdev = (Dev *)HashGetValue(he);
|
2021-07-11 03:13:24 +02:00
|
|
|
|
2019-10-28 18:10:16 +01:00
|
|
|
if (newdev)
|
|
|
|
|
{
|
|
|
|
|
/* Duplicate device. Duplicates will only appear in res.ext files
|
|
|
|
|
* where a device has nodes changed. Merge all properties of the
|
|
|
|
|
* original device with nodes from the new device. Keep the
|
|
|
|
|
* original device and discard the new one.
|
|
|
|
|
*
|
|
|
|
|
* Check that the device is actually the same device type and number
|
|
|
|
|
* of terminals. If not, throw an error and abandon the new device.
|
2021-05-22 04:41:51 +02:00
|
|
|
*
|
|
|
|
|
* NOTE: Quick check is made on dev_type, but for the reason stated
|
|
|
|
|
* above for the calculation of ttype, only the tile types need to
|
|
|
|
|
* match, so make an additional (expensive) check on tile type.
|
2019-10-28 18:10:16 +01:00
|
|
|
*/
|
|
|
|
|
|
2021-05-22 04:41:51 +02:00
|
|
|
if ((newdev->dev_class != class) || ((newdev->dev_type != dev_type)
|
|
|
|
|
&& (ttype != extGetDevType(EFDevTypes[newdev->dev_type]))))
|
2019-10-28 18:10:16 +01:00
|
|
|
{
|
|
|
|
|
TxError("Device %s %s at (%d, %d) overlaps incompatible device %s %s!\n",
|
2024-10-04 18:15:21 +02:00
|
|
|
extDevTable[(unsigned char)class], type, r->r_xbot, r->r_ybot,
|
2019-10-28 18:10:16 +01:00
|
|
|
extDevTable[newdev->dev_class], EFDevTypes[newdev->dev_type]);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else if (newdev->dev_nterm != nterminals)
|
|
|
|
|
{
|
|
|
|
|
TxError("Device %s %s at (%d, %d) overlaps device with incompatible"
|
|
|
|
|
" number of terminals (%d vs. %d)!\n",
|
2024-10-04 18:15:21 +02:00
|
|
|
extDevTable[(unsigned char)class], type, r->r_xbot, r->r_ybot, nterminals,
|
2019-10-28 18:10:16 +01:00
|
|
|
newdev->dev_nterm);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
newdev = (Dev *) mallocMagic((unsigned) DevSize(nterminals));
|
|
|
|
|
|
|
|
|
|
/* Add this dev to the hash table for def */
|
|
|
|
|
HashSetValue(he, (ClientData)newdev);
|
|
|
|
|
|
|
|
|
|
newdev->dev_cap = devtmp.dev_cap;
|
|
|
|
|
newdev->dev_res = devtmp.dev_res;
|
|
|
|
|
newdev->dev_area = devtmp.dev_area;
|
|
|
|
|
newdev->dev_perim = devtmp.dev_perim;
|
|
|
|
|
newdev->dev_length = devtmp.dev_length;
|
|
|
|
|
newdev->dev_width = devtmp.dev_width;
|
|
|
|
|
newdev->dev_params = devtmp.dev_params;
|
|
|
|
|
|
|
|
|
|
newdev->dev_nterm = nterminals;
|
|
|
|
|
newdev->dev_rect = *r;
|
2021-05-22 04:41:51 +02:00
|
|
|
newdev->dev_type = dev_type;
|
2019-10-28 18:10:16 +01:00
|
|
|
newdev->dev_class = class;
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-10-28 18:10:16 +01:00
|
|
|
switch (class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_FET: /* old-style "fet" record */
|
|
|
|
|
newdev->dev_area = atoi(argv[0]);
|
|
|
|
|
newdev->dev_perim = atoi(argv[1]);
|
|
|
|
|
break;
|
|
|
|
|
case DEV_MOSFET: /* new-style "device mosfet" record */
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
case DEV_BJT:
|
|
|
|
|
newdev->dev_length = atoi(argv[0]);
|
|
|
|
|
newdev->dev_width = atoi(argv[1]);
|
|
|
|
|
break;
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
if (hasModel && StrIsInt(argv[0]) && StrIsInt(argv[1]))
|
|
|
|
|
{
|
|
|
|
|
newdev->dev_length = atoi(argv[0]);
|
|
|
|
|
newdev->dev_width = atoi(argv[1]);
|
|
|
|
|
}
|
|
|
|
|
else if (StrIsNumeric(argv[0]))
|
|
|
|
|
{
|
|
|
|
|
newdev->dev_res = (float)atof(argv[0]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (hasModel)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Error: expected L and W, got %s %s\n", argv[0],
|
|
|
|
|
argv[1]);
|
|
|
|
|
newdev->dev_length = 0;
|
|
|
|
|
newdev->dev_width = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
efReadError("Error: expected resistance value, got %s\n",
|
|
|
|
|
argv[0]);
|
|
|
|
|
newdev->dev_res = 0.0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
if (hasModel && StrIsInt(argv[0]) && StrIsInt(argv[1]))
|
|
|
|
|
{
|
|
|
|
|
newdev->dev_length = atoi(argv[0]);
|
|
|
|
|
newdev->dev_width = atoi(argv[1]);
|
|
|
|
|
}
|
|
|
|
|
else if (StrIsNumeric(argv[0]))
|
|
|
|
|
{
|
|
|
|
|
newdev->dev_cap = (float)atof(argv[0]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (hasModel)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Error: expected L and W, got %s %s\n", argv[0],
|
|
|
|
|
argv[1]);
|
|
|
|
|
newdev->dev_length = 0;
|
|
|
|
|
newdev->dev_width = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
efReadError("Error: expected capacitance value, got %s\n",
|
|
|
|
|
argv[0]);
|
|
|
|
|
newdev->dev_cap = 0.0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newdev->dev_subsnode = devtmp.dev_subsnode;
|
2017-04-25 14:41:48 +02:00
|
|
|
switch (class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_FET: /* old-style "fet" record */
|
|
|
|
|
newdev->dev_subsnode = efBuildDevNode(def, argv[2], TRUE);
|
|
|
|
|
break;
|
|
|
|
|
case DEV_MOSFET: /* new-style "device mosfet" record */
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
case DEV_BJT:
|
|
|
|
|
/* "None" in the place of the substrate name means substrate is ignored */
|
|
|
|
|
if ((argstart == 3) && (strncmp(argv[2], "None", 4) != 0))
|
|
|
|
|
newdev->dev_subsnode = efBuildDevNode(def, argv[2], TRUE);
|
|
|
|
|
break;
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
if ((argstart == 3) && (strncmp(argv[2], "None", 4) != 0))
|
|
|
|
|
newdev->dev_subsnode = efBuildDevNode(def, argv[2], TRUE);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
if ((argstart == 3) && (strncmp(argv[2], "None", 4) != 0))
|
|
|
|
|
newdev->dev_subsnode = efBuildDevNode(def, argv[2], TRUE);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define TERM_NAME 0
|
|
|
|
|
#define TERM_PERIM 1
|
|
|
|
|
#define TERM_ATTRS 2
|
|
|
|
|
|
|
|
|
|
for (av = &argv[argstart], n = 0; n < nterminals; n++, av += 3)
|
|
|
|
|
{
|
|
|
|
|
term = &newdev->dev_terms[n];
|
|
|
|
|
term->dterm_node = efBuildDevNode(def, av[TERM_NAME], FALSE);
|
|
|
|
|
term->dterm_length = atoi(av[TERM_PERIM]);
|
|
|
|
|
|
|
|
|
|
/* If the attr list is '0', this signifies no attributes */
|
|
|
|
|
if (av[TERM_ATTRS][0] == '0' && av[TERM_ATTRS][1] == '\0')
|
|
|
|
|
term->dterm_attrs = (char *) NULL;
|
|
|
|
|
else
|
|
|
|
|
term->dterm_attrs = StrDup((char **) NULL, av[TERM_ATTRS]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef TERM_NAME
|
|
|
|
|
#undef TERM_PERIM
|
|
|
|
|
#undef TERM_ATTRS
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildPortNode --
|
|
|
|
|
*
|
|
|
|
|
* Look for the node named 'name' in the local table for 'def', or in
|
|
|
|
|
* the global node name table. If it doesn't already exist, create a
|
|
|
|
|
* EFNode for it, and set capacitance and area/perimeter to zero.
|
|
|
|
|
* Set the efnode_flags value to EF_PORT, with the port number encoded
|
|
|
|
|
* in the efNodeName structure.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2018-10-29 22:29:15 +01:00
|
|
|
efBuildPortNode(def, name, idx, x, y, layername, toplevel)
|
2017-04-25 14:41:48 +02:00
|
|
|
Def *def; /* Def to which this connection is to be added */
|
|
|
|
|
char *name; /* One of the names for this node */
|
|
|
|
|
int idx; /* Port number (order) */
|
|
|
|
|
int x; int y; /* Location of a point inside this node */
|
|
|
|
|
char *layername; /* Name of tile type */
|
2018-10-29 22:29:15 +01:00
|
|
|
bool toplevel; /* 1 if the cell def is the top level cell */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
|
|
|
|
he = HashFind(&def->def_nodes, name);
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
if (nn == (EFNodeName *) NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Create node if it doesn't already exist */
|
2022-11-20 04:02:44 +01:00
|
|
|
efBuildNode(def, FALSE, FALSE, FALSE, name, (double)0, x, y,
|
2017-04-25 14:41:48 +02:00
|
|
|
layername, (char **) NULL, 0);
|
|
|
|
|
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
}
|
|
|
|
|
if (nn != (EFNodeName *) NULL)
|
|
|
|
|
{
|
|
|
|
|
nn->efnn_node->efnode_flags |= EF_PORT;
|
2020-05-23 23:13:14 +02:00
|
|
|
if (toplevel)
|
2018-10-29 22:29:15 +01:00
|
|
|
nn->efnn_node->efnode_flags |= EF_TOP_PORT;
|
2017-04-25 14:41:48 +02:00
|
|
|
nn->efnn_port = idx;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* EFGetPortMax --
|
|
|
|
|
*
|
|
|
|
|
* Find the highest port number in the cell def and return the value.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Value of highest port number in the cell def's node list
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
2022-01-12 21:30:07 +01:00
|
|
|
* None.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
2022-01-12 21:30:07 +01:00
|
|
|
EFGetPortMax(def)
|
2017-04-25 14:41:48 +02:00
|
|
|
Def *def;
|
|
|
|
|
{
|
|
|
|
|
EFNode *snode;
|
|
|
|
|
EFNodeName *nodeName;
|
|
|
|
|
int portmax, portorder;
|
|
|
|
|
|
|
|
|
|
portmax = -1;
|
|
|
|
|
|
|
|
|
|
for (snode = (EFNode *) def->def_firstn.efnode_next;
|
|
|
|
|
snode != &def->def_firstn;
|
|
|
|
|
snode = (EFNode *) snode->efnode_next)
|
|
|
|
|
{
|
2021-12-02 17:38:46 +01:00
|
|
|
if (snode->efnode_flags & EF_PORT)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
for (nodeName = snode->efnode_name; nodeName != NULL; nodeName =
|
|
|
|
|
nodeName->efnn_next)
|
|
|
|
|
{
|
|
|
|
|
portorder = nodeName->efnn_port;
|
|
|
|
|
if (portorder > portmax) portmax = portorder;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return portmax;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildDevNode --
|
|
|
|
|
*
|
|
|
|
|
* Look for the node named 'name' in the local table for 'def', or
|
|
|
|
|
* in the global node name table. If it doesn't already exist,
|
|
|
|
|
* create a EFNode for it. If 'isSubsNode' is TRUE, this is node
|
|
|
|
|
* is a substrate node and may not exist yet; otherwise, the node
|
|
|
|
|
* must already exist.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns a pointer to the EFNode for 'name'.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* May create a new node, as per above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
EFNode *
|
|
|
|
|
efBuildDevNode(def, name, isSubsNode)
|
|
|
|
|
Def *def;
|
|
|
|
|
char *name;
|
|
|
|
|
bool isSubsNode;
|
|
|
|
|
{
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
EFNodeName *nn;
|
2021-12-03 18:29:02 +01:00
|
|
|
bool isNewNode = FALSE;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
he = HashFind(&def->def_nodes, name);
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
|
|
|
|
if (nn == (EFNodeName *) NULL)
|
|
|
|
|
{
|
|
|
|
|
/* Create node if it doesn't already exist */
|
|
|
|
|
if (efWarn && !isSubsNode)
|
|
|
|
|
efReadError("Node %s doesn't exist so creating it\n", name);
|
2022-11-20 04:02:44 +01:00
|
|
|
efBuildNode(def, FALSE, isSubsNode, FALSE, name, (double)0, 0, 0,
|
2017-04-25 14:41:48 +02:00
|
|
|
(char *) NULL, (char **) NULL, 0);
|
|
|
|
|
|
|
|
|
|
nn = (EFNodeName *) HashGetValue(he);
|
2021-12-03 18:29:02 +01:00
|
|
|
isNewNode = TRUE;
|
|
|
|
|
}
|
2023-07-31 18:11:04 +02:00
|
|
|
if (isSubsNode || (nn->efnn_node->efnode_flags & EF_GLOB_SUBS_NODE))
|
2021-12-03 18:29:02 +01:00
|
|
|
{
|
|
|
|
|
if (!EFHNIsGlob(nn->efnn_hier))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2021-12-03 18:29:02 +01:00
|
|
|
/* This node is declared to be an implicit port */
|
|
|
|
|
nn->efnn_node->efnode_flags |= EF_SUBS_PORT;
|
2023-05-08 15:51:13 +02:00
|
|
|
if (isNewNode == TRUE)
|
|
|
|
|
nn->efnn_port = -1;
|
2021-12-03 18:29:02 +01:00
|
|
|
def->def_flags |= DEF_SUBSNODES;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2021-12-03 18:29:02 +01:00
|
|
|
nn->efnn_node->efnode_flags |= EF_SUBS_NODE;
|
|
|
|
|
if (isNewNode)
|
|
|
|
|
nn->efnn_node->efnode_flags |= EF_DEVTERM;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
return nn->efnn_node;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildAddStr --
|
|
|
|
|
*
|
|
|
|
|
* Return the index of 'str' in 'table'.
|
|
|
|
|
* Add the string 'str' to the table 'table' if it's not already there.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Increments *pMax if we add an entry to the table.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
efBuildAddStr(table, pMax, size, str)
|
|
|
|
|
char *table[]; /* Table to search */
|
|
|
|
|
int *pMax; /* Increment this if we add an entry */
|
|
|
|
|
int size; /* Maximum size of table */
|
|
|
|
|
char *str; /* String to add */
|
|
|
|
|
{
|
|
|
|
|
int n, max;
|
|
|
|
|
|
|
|
|
|
max = *pMax;
|
|
|
|
|
for (n = 0; n < max; n++)
|
|
|
|
|
if (strcmp(table[n], str) == 0)
|
|
|
|
|
return n;
|
|
|
|
|
|
|
|
|
|
if (max >= size)
|
|
|
|
|
{
|
|
|
|
|
printf("Too many entries in table (max is %d) to add %s\n", size, str);
|
|
|
|
|
printf("Recompile libextflat.a with a bigger table size\n");
|
|
|
|
|
exit (1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table[n++] = StrDup((char **) NULL, str);
|
|
|
|
|
*pMax = n;
|
|
|
|
|
|
|
|
|
|
return max;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildUse --
|
|
|
|
|
*
|
|
|
|
|
* Process a "use" line from a .ext file.
|
|
|
|
|
* Creates a new use by the name 'subUseId' of the def named 'subDefName'.
|
|
|
|
|
* If 'subDefName' doesn't exist, it is created, but left marked as
|
|
|
|
|
* unavailable so that readfile() will read it in after it is done
|
|
|
|
|
* with this file. If 'subUseId' ends in an array subscript, e.g,
|
|
|
|
|
* useid[xlo:xhi:xsep][ylo:yhi:ysep]
|
|
|
|
|
* its ArrayInfo is filled in from this information; otherwise, its
|
|
|
|
|
* ArrayInfo is marked as not being needed (xlo == xhi, ylo == yhi).
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildUse(def, subDefName, subUseId, ta, tb, tc, td, te, tf)
|
|
|
|
|
Def *def; /* Def to which this connection is to be added */
|
|
|
|
|
char *subDefName; /* Def of which this a use */
|
|
|
|
|
char *subUseId; /* Use identifier for the def 'subDefName' */
|
|
|
|
|
int ta, tb, tc,
|
|
|
|
|
td, te, tf; /* Elements of a transform from coordinates of
|
|
|
|
|
* subDefName up to def.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
Use *newuse;
|
|
|
|
|
Def *newdef;
|
|
|
|
|
char *cp;
|
2019-02-07 16:54:07 +01:00
|
|
|
HashEntry *he;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
newdef = efDefLook(subDefName);
|
|
|
|
|
if (newdef == NULL)
|
|
|
|
|
newdef = efDefNew(subDefName);
|
|
|
|
|
|
|
|
|
|
newuse = (Use *) mallocMagic((unsigned)(sizeof (Use)));
|
|
|
|
|
newuse->use_def = newdef;
|
|
|
|
|
newuse->use_trans.t_a = ta;
|
|
|
|
|
newuse->use_trans.t_b = tb;
|
|
|
|
|
newuse->use_trans.t_c = tc;
|
|
|
|
|
newuse->use_trans.t_d = td;
|
|
|
|
|
newuse->use_trans.t_e = te;
|
|
|
|
|
newuse->use_trans.t_f = tf;
|
|
|
|
|
|
|
|
|
|
/* Set the use identifier and array information */
|
|
|
|
|
if ((cp = strchr(subUseId, '[')) == NULL)
|
|
|
|
|
{
|
|
|
|
|
newuse->use_id = StrDup((char **) NULL, subUseId);
|
|
|
|
|
newuse->use_xlo = newuse->use_xhi = 0;
|
|
|
|
|
newuse->use_ylo = newuse->use_yhi = 0;
|
|
|
|
|
newuse->use_xsep = newuse->use_ysep = 0;
|
|
|
|
|
}
|
2019-02-07 16:54:07 +01:00
|
|
|
else
|
|
|
|
|
{
|
2019-07-25 16:20:24 +02:00
|
|
|
/* Note: Preserve any use of brackets as-is other than the */
|
|
|
|
|
/* standard magic array notation below. This allows, for */
|
|
|
|
|
/* example, verilog instance arrays read from DEF files to */
|
|
|
|
|
/* be passed through correctly. */
|
|
|
|
|
|
|
|
|
|
if ((sscanf(cp, "[%d:%d:%d][%d:%d:%d]",
|
2017-04-25 14:41:48 +02:00
|
|
|
&newuse->use_xlo, &newuse->use_xhi, &newuse->use_xsep,
|
2019-07-25 16:20:24 +02:00
|
|
|
&newuse->use_ylo, &newuse->use_yhi, &newuse->use_ysep)) == 6)
|
|
|
|
|
{
|
|
|
|
|
*cp = '\0';
|
2021-02-24 18:35:06 +01:00
|
|
|
newuse->use_id = StrDup((char **) NULL, subUseId);
|
2019-07-25 16:20:24 +02:00
|
|
|
*cp = '[';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2021-02-24 18:35:06 +01:00
|
|
|
newuse->use_id = StrDup((char **) NULL, subUseId);
|
2019-07-25 16:20:24 +02:00
|
|
|
newuse->use_xlo = newuse->use_xhi = 0;
|
|
|
|
|
newuse->use_ylo = newuse->use_yhi = 0;
|
|
|
|
|
newuse->use_xsep = newuse->use_ysep = 0;
|
|
|
|
|
}
|
2019-02-07 16:54:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
he = HashFind(&def->def_uses, newuse->use_id);
|
|
|
|
|
if (HashGetValue(he))
|
|
|
|
|
TxError("Warning: use %s appears more than once in def!\n", newuse->use_id);
|
|
|
|
|
HashSetValue(he, (ClientData)newuse);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildConnect --
|
|
|
|
|
*
|
|
|
|
|
* Process a "connect" line from a .ext file.
|
|
|
|
|
* Creates a connection record for the names 'nodeName1' and
|
|
|
|
|
* 'nodeName2'.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Allocates a new connection record, and prepends it to the
|
|
|
|
|
* list for def.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildConnect(def, nodeName1, nodeName2, deltaC, av, ac)
|
|
|
|
|
Def *def; /* Def to which this connection is to be added */
|
|
|
|
|
char *nodeName1; /* Name of first node in connection */
|
|
|
|
|
char *nodeName2; /* Name of other node in connection */
|
|
|
|
|
double deltaC; /* Adjustment in capacitance */
|
|
|
|
|
char **av; /* Strings for area, perimeter adjustment */
|
|
|
|
|
int ac; /* Number of strings in av */
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
Connection *conn;
|
|
|
|
|
unsigned size = sizeof (Connection)
|
2019-10-18 20:12:52 +02:00
|
|
|
+ (efNumResistClasses - 1) * sizeof (EFPerimArea);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
conn = (Connection *) mallocMagic((unsigned)(size));
|
|
|
|
|
|
2021-02-24 18:35:06 +01:00
|
|
|
if (efConnInitSubs(conn, nodeName1, nodeName2))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
conn->conn_cap = (EFCapValue) deltaC;
|
|
|
|
|
conn->conn_next = def->def_conns;
|
|
|
|
|
for (n = 0; n < efNumResistClasses && ac > 1; n++, ac -= 2)
|
|
|
|
|
{
|
2019-12-08 23:37:48 +01:00
|
|
|
conn->conn_pa[n].pa_area = (int)(0.5 + (float)atoi(*av++)
|
|
|
|
|
* locScale * locScale);
|
|
|
|
|
conn->conn_pa[n].pa_perim = (int)(0.5 + (float)atoi(*av++) * locScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
for ( ; n < efNumResistClasses; n++)
|
|
|
|
|
conn->conn_pa[n].pa_area = conn->conn_pa[n].pa_perim = 0;
|
|
|
|
|
def->def_conns = conn;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildResistor --
|
|
|
|
|
*
|
|
|
|
|
* Process a "resistor" line from a .ext file.
|
|
|
|
|
* Creates a resistor record for the names 'nodeName1' and
|
|
|
|
|
* 'nodeName2'. Both 'nodeName1' and 'nodeName2' must be non-NULL.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Allocates a new connection record, and prepends it to the
|
|
|
|
|
* def_resistors list for def.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildResistor(def, nodeName1, nodeName2, resistance)
|
|
|
|
|
Def *def; /* Def to which this connection is to be added */
|
|
|
|
|
char *nodeName1; /* Name of first node in resistor */
|
|
|
|
|
char *nodeName2; /* Name of second node in resistor */
|
2023-03-25 16:01:52 +01:00
|
|
|
float resistance; /* Resistor value */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
Connection *conn;
|
|
|
|
|
|
|
|
|
|
conn = (Connection *) mallocMagic((unsigned)(sizeof (Connection)));
|
|
|
|
|
if (efConnInitSubs(conn, nodeName1, nodeName2))
|
|
|
|
|
{
|
2023-03-25 16:01:52 +01:00
|
|
|
conn->conn_res = resistance;
|
2017-04-25 14:41:48 +02:00
|
|
|
conn->conn_next = def->def_resistors;
|
|
|
|
|
def->def_resistors = conn;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efBuildCap --
|
|
|
|
|
*
|
|
|
|
|
* Process a "cap" line from a .ext file.
|
|
|
|
|
* Creates a capacitor record for the names 'nodeName1' and
|
|
|
|
|
* 'nodeName2'. Both 'nodeName1' and 'nodeName2' must be non-NULL.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Allocates a new connection record, and prepends it to the
|
|
|
|
|
* def_caps list for def.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efBuildCap(def, nodeName1, nodeName2, cap)
|
|
|
|
|
Def *def; /* Def to which this connection is to be added */
|
|
|
|
|
char *nodeName1; /* Name of first node in capacitor */
|
|
|
|
|
char *nodeName2; /* Name of second node in capacitor */
|
|
|
|
|
double cap; /* Capacitor value */
|
|
|
|
|
{
|
|
|
|
|
Connection *conn;
|
|
|
|
|
|
|
|
|
|
conn = (Connection *) mallocMagic((unsigned)(sizeof (Connection)));
|
|
|
|
|
if (efConnInitSubs(conn, nodeName1, nodeName2))
|
|
|
|
|
{
|
|
|
|
|
conn->conn_cap = (EFCapValue) cap;
|
|
|
|
|
conn->conn_next = def->def_caps;
|
|
|
|
|
def->def_caps = conn;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efConnInitSubs --
|
|
|
|
|
*
|
|
|
|
|
* Fill in and check the subscript information for the newly allocated
|
|
|
|
|
* Connection 'conn'.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns TRUE if successful, FALSE on error.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Fills in the two ConnNames conn->conn_1 and conn->conn_2.
|
|
|
|
|
* Frees 'conn' in the event of an error.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
efConnInitSubs(conn, nodeName1, nodeName2)
|
|
|
|
|
Connection *conn;
|
|
|
|
|
char *nodeName1, *nodeName2;
|
|
|
|
|
{
|
|
|
|
|
ConnName *c1, *c2;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
c1 = &conn->conn_1;
|
|
|
|
|
c2 = &conn->conn_2;
|
|
|
|
|
if (!efConnBuildName(c1, nodeName1) || !efConnBuildName(c2, nodeName2))
|
|
|
|
|
goto bad;
|
|
|
|
|
|
|
|
|
|
if (c1->cn_nsubs != c2->cn_nsubs)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Number of subscripts doesn't match\n");
|
|
|
|
|
goto bad;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (n = 0; n < c1->cn_nsubs; n++)
|
|
|
|
|
{
|
|
|
|
|
if (c1->cn_subs[n].r_hi - c1->cn_subs[n].r_lo
|
|
|
|
|
!= c2->cn_subs[n].r_hi - c2->cn_subs[n].r_lo)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Subscript %d range mismatch\n", n);
|
|
|
|
|
goto bad;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
bad:
|
|
|
|
|
if (c1->cn_name) freeMagic((char *) c1->cn_name);
|
|
|
|
|
if (c2->cn_name) freeMagic((char *) c2->cn_name);
|
|
|
|
|
freeMagic((char *) conn);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efConnBuildName --
|
|
|
|
|
*
|
|
|
|
|
* Fill in the fields of 'cnp' from the string 'name'.
|
|
|
|
|
* If 'name' contains no trailing subscript ranges (which are
|
|
|
|
|
* of the form [lo1:hi1] or [lo1:hi1,lo2:hi2], or [lo1:hi1][lo2:hi2] for
|
|
|
|
|
* compatibility with older versions of Magic), we set cnp->cn_nsubs
|
|
|
|
|
* to zero and cnp->cn_name to a copy of 'name'. Otherwise, we decode
|
|
|
|
|
* the subscripts and fill in cnp->cn_subs and cnp->cn_nsubs appropriately.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Returns TRUE if successful, FALSE on error.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Fills in the fields of the ConnName 'cnp'.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
efConnBuildName(cnp, name)
|
|
|
|
|
ConnName *cnp;
|
|
|
|
|
char *name;
|
|
|
|
|
{
|
|
|
|
|
char *srcp, *dstp, *cp, *dp;
|
|
|
|
|
int nsubs;
|
|
|
|
|
Range *rp;
|
|
|
|
|
char newname[1024];
|
|
|
|
|
char c;
|
|
|
|
|
|
|
|
|
|
cnp->cn_nsubs = 0;
|
|
|
|
|
if (name == NULL)
|
|
|
|
|
{
|
|
|
|
|
cnp->cn_name = NULL;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cp = name;
|
|
|
|
|
/* Make sure it's an array subscript range before treating it specially */
|
|
|
|
|
again:
|
|
|
|
|
if ((cp = strchr(cp, '[')) == NULL)
|
|
|
|
|
{
|
|
|
|
|
cnp->cn_name = StrDup((char **) NULL, name);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
for (dp = cp + 1; *dp && *dp != ':'; dp++)
|
|
|
|
|
{
|
|
|
|
|
if (*dp == ']')
|
|
|
|
|
{
|
|
|
|
|
cp = dp+1;
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy the initial part of the name */
|
|
|
|
|
for (srcp = name, dstp = newname; srcp < cp; *dstp++ = *srcp++)
|
|
|
|
|
/* Nothing */;
|
|
|
|
|
|
|
|
|
|
/* Replace each subscript range with %d */
|
|
|
|
|
for (nsubs = 0, rp = cnp->cn_subs; (c = *cp) == '[' || c == ','; nsubs++)
|
|
|
|
|
{
|
|
|
|
|
if (nsubs >= MAXSUBS)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Too many array subscripts (maximum=2)\n");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (sscanf(++cp, "%d:%d", &rp[nsubs].r_lo, &rp[nsubs].r_hi) != 2)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Subscript syntax error\n");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
if (rp[nsubs].r_lo > rp[nsubs].r_hi)
|
|
|
|
|
{
|
|
|
|
|
efReadError("Backwards subscript range [%d:%d]\n",
|
|
|
|
|
rp[nsubs].r_lo, rp[nsubs].r_hi);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (*cp && *cp != ']' && *cp != ',')
|
|
|
|
|
cp++;
|
|
|
|
|
if (*cp == ']') cp++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generate format for sprintf */
|
|
|
|
|
*dstp++ = '[';
|
|
|
|
|
*dstp++ = '%';
|
|
|
|
|
*dstp++ = 'd';
|
|
|
|
|
if (nsubs == 2)
|
|
|
|
|
{
|
|
|
|
|
*dstp++ = ',';
|
|
|
|
|
*dstp++ = '%';
|
|
|
|
|
*dstp++ = 'd';
|
|
|
|
|
}
|
|
|
|
|
*dstp++ = ']';
|
|
|
|
|
|
|
|
|
|
/* Copy remainder of path */
|
2024-10-04 18:20:25 +02:00
|
|
|
while ((*dstp++ = *cp++))
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Nothing */;
|
|
|
|
|
|
|
|
|
|
cnp->cn_name = StrDup((char **) NULL, newname);
|
|
|
|
|
cnp->cn_nsubs = nsubs;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efNodeAddName --
|
|
|
|
|
*
|
|
|
|
|
* Add a name to the list for 'node'.
|
|
|
|
|
* We already have a HashEntry for the new name.
|
|
|
|
|
* The new name is added to the front of the list
|
|
|
|
|
* for 'node' only if it is higher in precedence
|
|
|
|
|
* than the name already at the front of the list.
|
|
|
|
|
* Sets the value of 'he' to be the newly allocated
|
|
|
|
|
* EFNodeName.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2023-06-13 23:14:36 +02:00
|
|
|
efNodeAddName(node, he, hn, isNew)
|
2017-04-25 14:41:48 +02:00
|
|
|
EFNode *node;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
HierName *hn;
|
2023-06-13 23:14:36 +02:00
|
|
|
bool isNew; // If TRUE, added name is never the preferred name.
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
EFNodeName *newnn;
|
|
|
|
|
EFNodeName *oldnn;
|
2018-10-29 22:29:15 +01:00
|
|
|
bool topport; // New node to add is a top-level port
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
newnn = (EFNodeName *) mallocMagic((unsigned)(sizeof (EFNodeName)));
|
|
|
|
|
newnn->efnn_node = node;
|
|
|
|
|
newnn->efnn_hier = hn;
|
|
|
|
|
newnn->efnn_port = -1;
|
2021-03-17 19:54:36 +01:00
|
|
|
newnn->efnn_refc = 0;
|
2023-06-13 23:14:36 +02:00
|
|
|
|
|
|
|
|
HashSetValue(he, (char *)newnn);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2018-10-29 22:29:15 +01:00
|
|
|
/* If the node is a port of the top level cell, denoted by flag */
|
|
|
|
|
/* EF_TOP_PORT, then the name given to the port always stays at the */
|
|
|
|
|
/* head of the list. */
|
|
|
|
|
|
|
|
|
|
topport = (node->efnode_flags & EF_TOP_PORT) ? TRUE : FALSE;
|
2018-09-27 14:13:31 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Link in the new name */
|
|
|
|
|
oldnn = node->efnode_name;
|
2023-06-13 23:14:36 +02:00
|
|
|
if ((oldnn == NULL) || (EFHNBest(newnn->efnn_hier, oldnn->efnn_hier)
|
|
|
|
|
&& !topport && !isNew))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/* New head of list */
|
|
|
|
|
newnn->efnn_next = oldnn;
|
|
|
|
|
node->efnode_name = newnn;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Link it in behind the head of the list */
|
|
|
|
|
newnn->efnn_next = oldnn->efnn_next;
|
|
|
|
|
oldnn->efnn_next = newnn;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efNodeMerge --
|
|
|
|
|
*
|
|
|
|
|
* Combine two nodes. The resistances and capacitances are summed.
|
|
|
|
|
* The attribute lists are appended. The location chosen is the
|
|
|
|
|
* lower-leftmost, with lowest being considered before leftmost.
|
|
|
|
|
* The canonical name of the new node is taken to be the highest
|
|
|
|
|
* precedence among the names for all nodes.
|
|
|
|
|
*
|
|
|
|
|
* One of the nodes will no longer be referenced, so we arbitrarily
|
|
|
|
|
* make this node2 and free its memory.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
2023-04-28 02:47:55 +02:00
|
|
|
* Return the pointer to the node that is removed and will be
|
|
|
|
|
* deallocated.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* See above.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
2023-04-28 02:47:55 +02:00
|
|
|
EFNode *
|
2020-03-13 15:33:44 +01:00
|
|
|
efNodeMerge(node1ptr, node2ptr)
|
|
|
|
|
EFNode **node1ptr, **node2ptr; /* Pointers to hierarchical nodes */
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
EFNodeName *nn, *nnlast;
|
|
|
|
|
EFAttr *ap;
|
|
|
|
|
int n;
|
2020-03-13 15:33:44 +01:00
|
|
|
EFNode *keeping, *removing;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/* Sanity check: ignore if same node */
|
2020-03-13 15:33:44 +01:00
|
|
|
if (*node1ptr == *node2ptr)
|
2023-05-18 15:25:37 +02:00
|
|
|
return NULL;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
/* Keep the node with the greater number of entries, and merge */
|
|
|
|
|
/* the node with fewer entries into it. */
|
|
|
|
|
|
|
|
|
|
if ((*node1ptr)->efnode_num >= (*node2ptr)->efnode_num)
|
|
|
|
|
{
|
|
|
|
|
keeping = *node1ptr;
|
|
|
|
|
removing = *node2ptr;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
keeping = *node2ptr;
|
|
|
|
|
removing = *node1ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
if (efWatchNodes)
|
|
|
|
|
{
|
2020-03-13 15:33:44 +01:00
|
|
|
if (HashLookOnly(&efWatchTable, (char *) keeping->efnode_name->efnn_hier)
|
|
|
|
|
|| (removing->efnode_name
|
2017-04-25 14:41:48 +02:00
|
|
|
&& HashLookOnly(&efWatchTable,
|
2020-03-13 15:33:44 +01:00
|
|
|
(char *) removing->efnode_name->efnn_hier)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
printf("\ncombine: %s\n",
|
2020-03-13 15:33:44 +01:00
|
|
|
EFHNToStr(keeping->efnode_name->efnn_hier));
|
2020-05-23 23:13:14 +02:00
|
|
|
printf(" with %s\n\n",
|
2020-03-13 15:33:44 +01:00
|
|
|
removing->efnode_name
|
|
|
|
|
? EFHNToStr(removing->efnode_name->efnn_hier)
|
2017-04-25 14:41:48 +02:00
|
|
|
: "(unnamed)");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Sum capacitances, perimeters, areas */
|
2020-03-13 15:33:44 +01:00
|
|
|
keeping->efnode_cap += removing->efnode_cap;
|
2017-04-25 14:41:48 +02:00
|
|
|
for (n = 0; n < efNumResistClasses; n++)
|
|
|
|
|
{
|
2020-03-13 15:33:44 +01:00
|
|
|
keeping->efnode_pa[n].pa_area += removing->efnode_pa[n].pa_area;
|
|
|
|
|
keeping->efnode_pa[n].pa_perim += removing->efnode_pa[n].pa_perim;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
/* Make all EFNodeNames point to "keeping" */
|
|
|
|
|
if (removing->efnode_name)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-03-13 15:33:44 +01:00
|
|
|
bool topportk, topportr;
|
2018-09-27 14:13:31 +02:00
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
for (nn = removing->efnode_name; nn; nn = nn->efnn_next)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
nnlast = nn;
|
2020-03-13 15:33:44 +01:00
|
|
|
nn->efnn_node = keeping;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
topportk = (keeping->efnode_flags & EF_TOP_PORT) ? TRUE : FALSE;
|
|
|
|
|
topportr = (removing->efnode_flags & EF_TOP_PORT) ? TRUE : FALSE;
|
2018-09-27 14:13:31 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Concatenate list of EFNodeNames, taking into account precedence */
|
2020-03-13 15:33:44 +01:00
|
|
|
if ((!keeping->efnode_name) || (!topportk && (topportr
|
|
|
|
|
|| EFHNBest(removing->efnode_name->efnn_hier,
|
|
|
|
|
keeping->efnode_name->efnn_hier))))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
/*
|
2020-03-13 15:33:44 +01:00
|
|
|
* New official name is that of "removing".
|
2017-04-25 14:41:48 +02:00
|
|
|
* The new list of names is:
|
2020-03-13 15:33:44 +01:00
|
|
|
* removing-names, keeping-names
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
2020-03-13 15:33:44 +01:00
|
|
|
nnlast->efnn_next = keeping->efnode_name;
|
|
|
|
|
keeping->efnode_name = removing->efnode_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
2020-03-13 15:33:44 +01:00
|
|
|
* Choose the new location only if "removing"'s location is a valid one,
|
|
|
|
|
* i.e, "removing" wasn't created before it was mentioned. This is mainly
|
2017-04-25 14:41:48 +02:00
|
|
|
* to deal with new fets, resistors, and capacitors created by resistance
|
|
|
|
|
* extraction, which appear with their full hierarchical names in the
|
|
|
|
|
* .ext file for the root cell.
|
|
|
|
|
*
|
|
|
|
|
* This code has been moved up from below so that the original
|
|
|
|
|
* location and type will be prefered when the original name
|
|
|
|
|
* is preferred.
|
|
|
|
|
*
|
|
|
|
|
* I am purposefully subverting the original specification that
|
|
|
|
|
* the node refer to the bottom corner of the network. Does
|
|
|
|
|
* this have any effect on exttosim or exttospice?
|
|
|
|
|
*
|
|
|
|
|
* Tim, 6/14/04
|
|
|
|
|
*/
|
2020-03-13 15:33:44 +01:00
|
|
|
if (removing->efnode_type > 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2020-03-13 15:33:44 +01:00
|
|
|
keeping->efnode_loc = removing->efnode_loc;
|
|
|
|
|
keeping->efnode_type = removing->efnode_type;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Keep old official name.
|
|
|
|
|
* The new list of names is:
|
2020-03-13 15:33:44 +01:00
|
|
|
* keeping-names[0], removing-names, keeping-names[1-]
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
2020-03-13 15:33:44 +01:00
|
|
|
nnlast->efnn_next = keeping->efnode_name->efnn_next;
|
|
|
|
|
keeping->efnode_name->efnn_next = removing->efnode_name;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
/* Merge list counts */
|
|
|
|
|
keeping->efnode_num += removing->efnode_num;
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/* Merge attribute lists */
|
2024-10-04 18:20:25 +02:00
|
|
|
if ((ap = removing->efnode_attrs))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
while (ap->efa_next)
|
|
|
|
|
ap = ap->efa_next;
|
2020-03-13 15:33:44 +01:00
|
|
|
ap->efa_next = keeping->efnode_attrs;
|
|
|
|
|
keeping->efnode_attrs = ap;
|
|
|
|
|
removing->efnode_attrs = (EFAttr *) NULL; /* Sanity */
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
/* Unlink "removing" from list for def */
|
|
|
|
|
removing->efnode_prev->efnhdr_next = removing->efnode_next;
|
|
|
|
|
removing->efnode_next->efnhdr_prev = removing->efnode_prev;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Only if both nodes were EF_DEVTERM do we keep EF_DEVTERM set
|
|
|
|
|
* in the resultant node.
|
|
|
|
|
*/
|
2020-03-13 15:33:44 +01:00
|
|
|
if ((removing->efnode_flags & EF_DEVTERM) == 0)
|
|
|
|
|
keeping->efnode_flags &= ~EF_DEVTERM;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
2020-03-13 15:33:44 +01:00
|
|
|
* If "removing" has the EF_PORT flag set, then copy the port
|
2017-04-25 14:41:48 +02:00
|
|
|
* record in the flags to node1.
|
|
|
|
|
*/
|
2020-03-13 15:33:44 +01:00
|
|
|
if (removing->efnode_flags & EF_PORT)
|
|
|
|
|
keeping->efnode_flags |= EF_PORT;
|
|
|
|
|
if (removing->efnode_flags & EF_TOP_PORT)
|
|
|
|
|
keeping->efnode_flags |= EF_TOP_PORT;
|
2021-10-06 02:34:22 +02:00
|
|
|
if (removing->efnode_flags & EF_SUBS_PORT)
|
|
|
|
|
keeping->efnode_flags |= EF_SUBS_PORT;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
/*
|
2020-03-13 15:33:44 +01:00
|
|
|
* If "removing" has the EF_SUBS_NODE flag set, then copy the port
|
|
|
|
|
* record in the flags to "keeping".
|
2017-04-25 14:41:48 +02:00
|
|
|
*/
|
2020-03-13 15:33:44 +01:00
|
|
|
if (removing->efnode_flags & EF_SUBS_NODE)
|
|
|
|
|
keeping->efnode_flags |= EF_SUBS_NODE;
|
|
|
|
|
|
2022-11-20 04:02:44 +01:00
|
|
|
/* If EFSaveLocs is set, then merge any disjoint segments from
|
|
|
|
|
* removing to keeping.
|
|
|
|
|
*/
|
|
|
|
|
if (EFSaveLocs == TRUE)
|
|
|
|
|
{
|
|
|
|
|
LinkedRect *lr;
|
|
|
|
|
|
|
|
|
|
if (keeping->efnode_disjoint == NULL)
|
|
|
|
|
keeping->efnode_disjoint = removing->efnode_disjoint;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for (lr = keeping->efnode_disjoint; lr->r_next; lr = lr->r_next);
|
|
|
|
|
lr->r_next = removing->efnode_disjoint;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 21:30:07 +01:00
|
|
|
removing->efnode_flags = 0;
|
|
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
/* Get rid of "removing" */
|
|
|
|
|
freeMagic((char *) removing);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2020-03-13 15:33:44 +01:00
|
|
|
/* Make sure that the active node is always node1 */
|
|
|
|
|
*node1ptr = keeping;
|
|
|
|
|
*node2ptr = (EFNode *)NULL; /* Sanity check */
|
2023-04-28 02:47:55 +02:00
|
|
|
|
|
|
|
|
return removing;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2019-02-07 16:54:07 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efFreeUseTable --
|
|
|
|
|
*
|
|
|
|
|
* Free the cell IDs allocated for each entry in the use hash table, and
|
|
|
|
|
* the memory allocated by the cell use, leaving the hash entry null.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efFreeUseTable(table)
|
|
|
|
|
HashTable *table;
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
Use *use;
|
|
|
|
|
HierName *hn;
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:20:25 +02:00
|
|
|
while ((he = HashNext(table, &hs)))
|
|
|
|
|
if ((use = (Use *) HashGetValue(he)))
|
2019-02-07 16:54:07 +01:00
|
|
|
{
|
|
|
|
|
if (use->use_id != NULL) freeMagic((char *)use->use_id);
|
|
|
|
|
freeMagic(use);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-28 18:10:16 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efFreeDevTable --
|
|
|
|
|
*
|
|
|
|
|
* Free the device records allocated for each entry in the device hash table,
|
|
|
|
|
* the memory allocated by the device, leaving the hash entry null.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efFreeDevTable(table)
|
|
|
|
|
HashTable *table;
|
|
|
|
|
{
|
|
|
|
|
Dev *dev;
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:20:25 +02:00
|
|
|
while ((he = HashNext(table, &hs)))
|
2019-10-28 18:10:16 +01:00
|
|
|
{
|
|
|
|
|
dev = (Dev *)HashGetValue(he);
|
|
|
|
|
for (n = 0; n < (int)dev->dev_nterm; n++)
|
|
|
|
|
if (dev->dev_terms[n].dterm_attrs)
|
|
|
|
|
freeMagic((char *) dev->dev_terms[n].dterm_attrs);
|
|
|
|
|
freeMagic((char *) dev);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-02-07 16:54:07 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efFreeNodeTable --
|
|
|
|
|
*
|
|
|
|
|
* Free the EFNodeNames (and the HierNames they point to) pointed to by
|
|
|
|
|
* the entries in the HashTable 'table'. Each EFNodeName is assumed to
|
|
|
|
|
* be pointed to by exactly one HashEntry, but each HierName can be
|
|
|
|
|
* pointed to by many entries (some of which may be in other HashTables).
|
|
|
|
|
* As a result, the HierNames aren't freed here; instead, an entry is
|
|
|
|
|
* added to efFreeHashTable for each HierName encountered. Everything
|
|
|
|
|
* is then freed at the end by EFDone().
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Frees memory.
|
|
|
|
|
* Adds an entry to hnTable for each HierName.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efFreeNodeTable(table)
|
|
|
|
|
HashTable *table;
|
|
|
|
|
{
|
|
|
|
|
HashSearch hs;
|
|
|
|
|
HashEntry *he;
|
|
|
|
|
HierName *hn;
|
|
|
|
|
EFNodeName *nn;
|
|
|
|
|
|
|
|
|
|
HashStartSearch(&hs);
|
2024-10-04 18:20:25 +02:00
|
|
|
while ((he = HashNext(table, &hs)))
|
|
|
|
|
if ((nn = (EFNodeName *) HashGetValue(he)))
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
for (hn = nn->efnn_hier; hn; hn = hn->hn_parent)
|
|
|
|
|
(void) HashFind(&efFreeHashTable, (char *) hn);
|
2021-03-17 19:54:36 +01:00
|
|
|
|
|
|
|
|
/* Node equivalences made by "equiv" statements are handled */
|
|
|
|
|
/* by reference count. Don't free the node structure until */
|
|
|
|
|
/* all references have been seen. */
|
|
|
|
|
|
|
|
|
|
if (nn->efnn_refc > 0)
|
|
|
|
|
nn->efnn_refc--;
|
|
|
|
|
else
|
|
|
|
|
freeMagic((char *) nn);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efFreeNodeList --
|
|
|
|
|
*
|
|
|
|
|
* Free the circular list of nodes of which 'head' is the head.
|
|
|
|
|
* Don't free 'head' itself, since it's statically allocated.
|
|
|
|
|
*
|
2021-12-14 00:05:53 +01:00
|
|
|
* If the client (e.g., ext2spice, ext2sim, ...) allocates memory for a
|
|
|
|
|
* node, then that client needs to provide a function "func" of the form:
|
|
|
|
|
*
|
|
|
|
|
* int func(client)
|
|
|
|
|
* ClientData client;
|
|
|
|
|
* {
|
|
|
|
|
* }
|
|
|
|
|
*
|
|
|
|
|
* The return value is unused but should be zero for consistency.
|
|
|
|
|
*
|
2017-04-25 14:41:48 +02:00
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
2021-12-14 00:05:53 +01:00
|
|
|
* Frees memory. Calls func() to free additional memory allocated
|
|
|
|
|
* in the node structure by a client.
|
2017-04-25 14:41:48 +02:00
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
2021-12-14 00:05:53 +01:00
|
|
|
efFreeNodeList(head, func)
|
2017-04-25 14:41:48 +02:00
|
|
|
EFNode *head;
|
2021-12-14 00:05:53 +01:00
|
|
|
int (*func)();
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
EFNode *node;
|
|
|
|
|
EFAttr *ap;
|
2022-11-20 04:02:44 +01:00
|
|
|
LinkedRect *lr;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
for (node = (EFNode *) head->efnode_next;
|
|
|
|
|
node != head;
|
|
|
|
|
node = (EFNode *) node->efnode_next)
|
|
|
|
|
{
|
|
|
|
|
for (ap = node->efnode_attrs; ap; ap = ap->efa_next)
|
|
|
|
|
freeMagic((char *) ap);
|
2021-12-14 00:05:53 +01:00
|
|
|
if (node->efnode_client != (ClientData)NULL)
|
|
|
|
|
{
|
|
|
|
|
if (func != NULL)
|
|
|
|
|
(*func)(node->efnode_client);
|
|
|
|
|
freeMagic((char *)node->efnode_client);
|
|
|
|
|
}
|
2022-11-20 04:02:44 +01:00
|
|
|
for (lr = node->efnode_disjoint; lr; lr = lr->r_next)
|
|
|
|
|
freeMagic((char *)lr);
|
|
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
freeMagic((char *) node);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-23 23:13:14 +02:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efFreeConn --
|
|
|
|
|
*
|
|
|
|
|
* Free the Connection *conn.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* None.
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Frees memory.
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efFreeConn(conn)
|
|
|
|
|
Connection *conn;
|
|
|
|
|
{
|
|
|
|
|
if (conn->conn_name1) freeMagic(conn->conn_name1);
|
|
|
|
|
if (conn->conn_name2) freeMagic(conn->conn_name2);
|
|
|
|
|
freeMagic((char *) conn);
|
|
|
|
|
}
|