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 */
|
2025-07-19 03:08:18 +02:00
|
|
|
EFNode *efBuildDevNode(Def *def, char *name, bool isSubsNode);
|
|
|
|
|
void efNodeAddName(EFNode *node, HashEntry *he, HierName *hn, bool isNew);
|
|
|
|
|
EFNode *efNodeMerge(EFNode **node1ptr, EFNode **node2ptr);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2025-07-19 03:08:18 +02:00
|
|
|
bool efConnBuildName(ConnName *cnp, char *name);
|
|
|
|
|
bool efConnInitSubs(Connection *conn, char *nodeName1, char *nodeName2);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
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
|
|
|
{
|
2025-12-24 22:00:08 +01:00
|
|
|
/* If one of the nodes has been generated from the
|
|
|
|
|
* other by "extract unique", then this is a case where
|
|
|
|
|
* the "extract unique" algorithm is blind to shorts
|
|
|
|
|
* through subcell hierarchy and has made a name unique
|
|
|
|
|
* unnecessarily. In that case, merge the node instead
|
|
|
|
|
* of generating a short.
|
|
|
|
|
*/
|
|
|
|
|
char *uniqstr1, *uniqstr2;
|
|
|
|
|
bool isuniq;
|
|
|
|
|
|
|
|
|
|
uniqstr1 = strstr(nodeName1, "_uq");
|
|
|
|
|
uniqstr2 = strstr(nodeName2, "_uq");
|
|
|
|
|
if (uniqstr1) *uniqstr1 = '\0';
|
|
|
|
|
if (uniqstr2) *uniqstr2 = '\0';
|
|
|
|
|
isuniq = !strcmp(nodeName1, nodeName2);
|
|
|
|
|
if (uniqstr1) *uniqstr1 = '_';
|
|
|
|
|
if (uniqstr2) *uniqstr2 = '_';
|
|
|
|
|
|
|
|
|
|
if (!isuniq)
|
2023-07-08 18:14:57 +02:00
|
|
|
{
|
2025-12-24 22:00:08 +01:00
|
|
|
if ((EFOutputFlags & EF_SHORT_MASK) != EF_SHORT_NONE)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int sdev;
|
|
|
|
|
char *argv[10], zeroarg[] = "0";
|
2023-07-08 18:14:57 +02:00
|
|
|
|
2025-12-24 22:00:08 +01:00
|
|
|
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);
|
2023-07-08 18:14:57 +02:00
|
|
|
else
|
2025-12-24 22:00:08 +01:00
|
|
|
/* Do not merge the nodes when folding in extresist parasitics */
|
|
|
|
|
return;
|
2023-07-08 18:14:57 +02:00
|
|
|
}
|
2025-12-24 22:00:08 +01:00
|
|
|
else if (resist)
|
2023-07-08 18:14:57 +02:00
|
|
|
/* 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);
|
2025-12-24 17:31:43 +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
|
2025-10-03 23:33:46 +02:00
|
|
|
efBuildDevice(
|
|
|
|
|
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 */
|
|
|
|
|
const Rect *r, /* Coordinates of 1x1 rectangle entirely inside device */
|
|
|
|
|
int argc, /* Size of argv */
|
|
|
|
|
char *argv[]) /* Tokens for the rest of the dev line.
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
* Starts after the four device coordinate arguments.
|
|
|
|
|
* The next arguments (0, 1, or 2) depend on the type of
|
|
|
|
|
* device, followed by optional parameters. 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];
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
int termstart;
|
2017-04-25 14:41:48 +02:00
|
|
|
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;
|
|
|
|
|
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
termstart = 1; /* Start of terminal list in argv[]; this is a default
|
|
|
|
|
* value if none of the cases below applies. The value
|
|
|
|
|
* of termstart does not initially account for parameter
|
|
|
|
|
* entries (<param>=<value>) but is adjusted as the
|
|
|
|
|
* parameters are parsed. The first terminal may be an
|
|
|
|
|
* (optional) substrate which is determined by whether
|
|
|
|
|
* the number of remaining arguments is divisible by 3
|
|
|
|
|
* or not.
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
switch (class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_FET:
|
|
|
|
|
case DEV_MOSFET:
|
|
|
|
|
case DEV_ASYMMETRIC:
|
|
|
|
|
case DEV_BJT:
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
/* Terminals start after L and W values, plus parameters */
|
|
|
|
|
termstart = 2;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEV_DIODE:
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
case DEV_PDIODE:
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
/* Terminals start immediately after parameters */
|
|
|
|
|
termstart = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
case DEV_CAPREV:
|
|
|
|
|
if (hasModel)
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
/* Terminals start after L and W values, plus parameters */
|
|
|
|
|
termstart = 2;
|
|
|
|
|
/* Otherwise, terminals start after device value, plus parameters */
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
case DEV_SUBCKT:
|
2025-08-07 17:54:49 +02:00
|
|
|
case DEV_VERILOGA:
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
case DEV_CSUBCKT:
|
2025-10-07 22:45:22 +02:00
|
|
|
case DEV_DSUBCKT:
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
/* Terminals start immediately after parameters */
|
|
|
|
|
termstart = 0;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
devp = efGetDeviceParams(type);
|
|
|
|
|
|
|
|
|
|
/* Parse initial arguments for parameters */
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
while ((pptr = strchr(argv[termstart], '=')) != NULL)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
2025-12-29 16:52:08 +01:00
|
|
|
/* If the parameter is in the parameter list "devp", then save
|
|
|
|
|
* the value as appropriate. If not, then the entire phrase
|
|
|
|
|
* will be output verbatim, so just save the whole string.
|
|
|
|
|
*
|
|
|
|
|
* The "parameters" line comes before any "device" line in the
|
|
|
|
|
* .ext file, so the "devp" list should be complete.
|
|
|
|
|
*/
|
2017-04-25 14:41:48 +02:00
|
|
|
|
|
|
|
|
*pptr = '\0';
|
|
|
|
|
for (sparm = devp; sparm; sparm = sparm->parm_next)
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if (!strncasecmp(sparm->parm_type, argv[termstart], 2))
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
|
|
|
|
*pptr = '=';
|
|
|
|
|
if (sparm == NULL)
|
|
|
|
|
{
|
2025-12-29 16:52:08 +01:00
|
|
|
/* Copy the whole string into dev_params */
|
2017-04-25 14:41:48 +02:00
|
|
|
/* (parm_type and parm_scale records are not used) */
|
|
|
|
|
newparm = (DevParam *)mallocMagic(sizeof(DevParam));
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
newparm->parm_name = StrDup((char **)NULL, argv[termstart]);
|
2017-04-25 14:41:48 +02:00
|
|
|
newparm->parm_next = devtmp.dev_params;
|
|
|
|
|
devtmp.dev_params = newparm;
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
termstart++;
|
2017-04-25 14:41:48 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pptr++;
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
switch(*argv[termstart])
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
case 'a':
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if ((pptr - argv[termstart]) == 2)
|
2025-12-29 16:52:08 +01:00
|
|
|
devtmp.dev_area = (int)(0.5 + (float)atoi(pptr)
|
|
|
|
|
* locScale * locScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
{
|
2025-12-29 16:52:08 +01:00
|
|
|
/* Check for a0, a1, a2, ... If a0, handle like "a".
|
|
|
|
|
* Otherwise, don't handle it here.
|
|
|
|
|
*/
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
pn = *(argv[termstart] + 1) - '0';
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
break;
|
2025-12-29 16:52:08 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'p':
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if ((pptr - argv[termstart]) == 2)
|
2025-12-29 16:52:08 +01:00
|
|
|
devtmp.dev_perim = (int)(0.5 + (float)atoi(pptr) * locScale);
|
2017-04-25 14:41:48 +02:00
|
|
|
else
|
|
|
|
|
{
|
2025-12-29 16:52:08 +01:00
|
|
|
/* Check for p0, p1, p2, ... If p0, handle like "p".
|
|
|
|
|
* Otherwise, don't handle it here.
|
|
|
|
|
*/
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
pn = *(argv[termstart] + 1) - '0';
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
break;
|
2025-12-29 16:52:08 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'l':
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if ((pptr - argv[termstart]) == 2)
|
2025-12-29 16:52:08 +01:00
|
|
|
devtmp.dev_length = (int)(0.5 + (float)atoi(pptr) * locScale);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Check for l0, l1, l2, ... If l0, handle like "l".
|
|
|
|
|
* Otherwise, save it verbatim like an unknown parameter,
|
|
|
|
|
* because its value will not be calculated from terminal
|
|
|
|
|
* values like "a1, a2, ..." or "p1, p2, ...".
|
|
|
|
|
*/
|
|
|
|
|
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
pn = *(argv[termstart] + 1) - '0';
|
2025-12-29 16:52:08 +01:00
|
|
|
if (pn == 0)
|
|
|
|
|
devtmp.dev_length = (int)(0.5 + (float)atoi(pptr) * locScale);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Copy the whole string into dev_params */
|
|
|
|
|
newparm = (DevParam *)mallocMagic(sizeof(DevParam));
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
newparm->parm_name = StrDup((char **)NULL, argv[termstart]);
|
2025-12-29 16:52:08 +01:00
|
|
|
newparm->parm_next = devtmp.dev_params;
|
|
|
|
|
devtmp.dev_params = newparm;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-25 14:41:48 +02:00
|
|
|
break;
|
2025-12-29 16:52:08 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
case 'w':
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if ((pptr - argv[termstart]) == 2)
|
|
|
|
|
devtmp.dev_width = (int)(0.5 + (float)atoi(pptr) * locScale);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Check for w0, w1, w2, ... If w0, handle like "w".
|
|
|
|
|
* Otherwise, save it verbatim like an unknown parameter,
|
|
|
|
|
* because its value will not be calculated from terminal
|
|
|
|
|
* values like "a1, a2, ..." or "p1, p2, ...".
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
pn = *(argv[termstart] + 1) - '0';
|
|
|
|
|
if (pn == 0)
|
|
|
|
|
devtmp.dev_width = (int)(0.5 + (float)atoi(pptr) * locScale);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Copy the whole string into dev_params */
|
|
|
|
|
newparm = (DevParam *)mallocMagic(sizeof(DevParam));
|
|
|
|
|
newparm->parm_name = StrDup((char **)NULL, argv[termstart]);
|
|
|
|
|
newparm->parm_next = devtmp.dev_params;
|
|
|
|
|
devtmp.dev_params = newparm;
|
|
|
|
|
}
|
|
|
|
|
}
|
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;
|
|
|
|
|
}
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
termstart++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check for optional substrate node */
|
|
|
|
|
switch (class)
|
|
|
|
|
{
|
|
|
|
|
case DEV_RES:
|
|
|
|
|
case DEV_CAP:
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
case DEV_BJT:
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEV_CAPREV:
|
|
|
|
|
case DEV_RSUBCKT:
|
2018-10-30 21:19:20 +01:00
|
|
|
case DEV_CSUBCKT:
|
2025-10-07 22:45:22 +02:00
|
|
|
case DEV_DSUBCKT:
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEV_MSUBCKT:
|
|
|
|
|
case DEV_SUBCKT:
|
2025-08-07 17:54:49 +02:00
|
|
|
case DEV_VERILOGA:
|
2017-04-25 14:41:48 +02:00
|
|
|
case DEV_DIODE:
|
|
|
|
|
case DEV_NDIODE:
|
|
|
|
|
case DEV_PDIODE:
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
n = argc - termstart;
|
2017-04-25 14:41:48 +02:00
|
|
|
if ((n % 3) == 1)
|
|
|
|
|
{
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if (strncmp(argv[termstart], "None", 4) != 0)
|
|
|
|
|
devtmp.dev_subsnode = efBuildDevNode(def, argv[termstart], TRUE);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
termstart++;
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
/* Between termstart and argc, we should only have terminal triples */
|
|
|
|
|
if (((argc - termstart) % 3) != 0)
|
2017-04-25 14:41:48 +02:00
|
|
|
return 1;
|
|
|
|
|
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
nterminals = (argc - termstart) / 3;
|
2017-04-25 14:41:48 +02:00
|
|
|
|
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 */
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if ((termstart == 3) && (strncmp(argv[2], "None", 4) != 0))
|
2017-04-25 14:41:48 +02:00
|
|
|
newdev->dev_subsnode = efBuildDevNode(def, argv[2], TRUE);
|
|
|
|
|
break;
|
|
|
|
|
case DEV_RES:
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if ((termstart == 3) && (strncmp(argv[2], "None", 4) != 0))
|
2017-04-25 14:41:48 +02:00
|
|
|
newdev->dev_subsnode = efBuildDevNode(def, argv[2], TRUE);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case DEV_CAP:
|
|
|
|
|
case DEV_CAPREV:
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
if ((termstart == 3) && (strncmp(argv[2], "None", 4) != 0))
|
2017-04-25 14:41:48 +02:00
|
|
|
newdev->dev_subsnode = efBuildDevNode(def, argv[2], TRUE);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define TERM_NAME 0
|
|
|
|
|
#define TERM_PERIM 1
|
|
|
|
|
#define TERM_ATTRS 2
|
|
|
|
|
|
Corrected an error in EFbuild.c in routine efBuildDevice(), which
had one error and one missing method. The error was an incorrect
argument count for regular (not subcircuit) FETs and BJTs. The
missing method was to handle parameters w0, w1, w2, etc., like
l0, l1, l2, . . . Those had been defined for output in ExtBasic.c
but were not being handled on input from the .ext file. This fix
corresponds more or less to PR #480 on github, although that PR
incorrectly addressed the argument numbering problem, so I have
redone the code changes by hand.
2026-01-09 17:51:11 +01:00
|
|
|
for (av = &argv[termstart], n = 0; n < nterminals; n++, av += 3)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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 *
|
2025-10-04 00:11:45 +02:00
|
|
|
efBuildDevNode(
|
|
|
|
|
Def *def,
|
|
|
|
|
char *name,
|
|
|
|
|
bool isSubsNode)
|
2017-04-25 14:41:48 +02:00
|
|
|
{
|
|
|
|
|
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);
|
2024-10-04 21:09:48 +02:00
|
|
|
ASSERT(nn, "nn");
|
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
|
|
|
|
2025-02-26 14:21:46 +01:00
|
|
|
/*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*
|
|
|
|
|
* efConnectionFreeLinkedList --
|
|
|
|
|
*
|
|
|
|
|
* Release memory for linked-list of Connection* based on internal list
|
|
|
|
|
* at Connection->conn_next. 'conn' argument must be non-NULL.
|
|
|
|
|
*
|
|
|
|
|
* Results:
|
|
|
|
|
* Deallocates linked-list of Connection* starting at 'conn'
|
|
|
|
|
*
|
|
|
|
|
* Side effects:
|
|
|
|
|
* Deallocates one or more connection record(s).
|
|
|
|
|
*
|
|
|
|
|
* ----------------------------------------------------------------------------
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
efConnectionFreeLinkedList(Connection *conn)
|
|
|
|
|
{
|
|
|
|
|
while (conn)
|
|
|
|
|
{
|
|
|
|
|
Connection *next = conn->conn_next;
|
|
|
|
|
efFreeConn(conn);
|
|
|
|
|
conn = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2024-11-02 16:00:05 +01:00
|
|
|
|
2017-04-25 14:41:48 +02:00
|
|
|
unsigned size = sizeof (Connection)
|
2019-10-18 20:12:52 +02:00
|
|
|
+ (efNumResistClasses - 1) * sizeof (EFPerimArea);
|
2017-04-25 14:41:48 +02:00
|
|
|
|
2025-12-24 22:00:08 +01:00
|
|
|
/* If one of the nodes has been generated from the
|
|
|
|
|
* other by "extract unique", then this is a case where
|
|
|
|
|
* the "extract unique" algorithm is blind to shorts
|
|
|
|
|
* through subcell hierarchy and has made a name unique
|
|
|
|
|
* unnecessarily. In that case, merge the node instead
|
|
|
|
|
* of generating a short.
|
|
|
|
|
*/
|
|
|
|
|
char *uniqstr1, *uniqstr2;
|
|
|
|
|
bool isuniq;
|
|
|
|
|
|
|
|
|
|
uniqstr1 = strstr(nodeName1, "_uq");
|
|
|
|
|
uniqstr2 = strstr(nodeName2, "_uq");
|
|
|
|
|
if (uniqstr1) *uniqstr1 = '\0';
|
|
|
|
|
if (uniqstr2) *uniqstr2 = '\0';
|
|
|
|
|
isuniq = !strcmp(nodeName1, nodeName2);
|
|
|
|
|
if (uniqstr1) *uniqstr1 = '_';
|
|
|
|
|
if (uniqstr2) *uniqstr2 = '_';
|
|
|
|
|
|
|
|
|
|
if (!isuniq && ((EFOutputFlags & EF_SHORT_MASK) != EF_SHORT_NONE))
|
2024-11-02 16:00:05 +01:00
|
|
|
{
|
|
|
|
|
/* Handle the case where two ports on different nets get merged.
|
|
|
|
|
* If "extract short resistor" or "extract short voltage" has
|
|
|
|
|
* been specified, then his is similar to parsing an "equiv" statement,
|
|
|
|
|
* resulting in a shorting resistor or voltage source being placed
|
|
|
|
|
* between the two node names, and abandoning the merge.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
HashEntry *he1, *he2;
|
|
|
|
|
|
|
|
|
|
he1 = HashLookOnly(&def->def_nodes, nodeName1);
|
|
|
|
|
he2 = HashLookOnly(&def->def_nodes, nodeName2);
|
|
|
|
|
|
|
|
|
|
if (he1 && he2)
|
|
|
|
|
{
|
|
|
|
|
EFNodeName *nn1, *nn2;
|
|
|
|
|
|
|
|
|
|
nn1 = (EFNodeName *) HashGetValue(he1);
|
|
|
|
|
nn2 = (EFNodeName *) HashGetValue(he2);
|
|
|
|
|
|
|
|
|
|
if (nn1 && nn2 && (nn1->efnn_port >= 0) && (nn2->efnn_port >= 0) &&
|
|
|
|
|
(nn1->efnn_port != nn2->efnn_port))
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
2025-12-24 22:00:08 +01:00
|
|
|
EFNodeName *nn, *nnlast;
|
2017-04-25 14:41:48 +02:00
|
|
|
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
|
|
|
|
2025-12-24 17:31:43 +01:00
|
|
|
/*
|
|
|
|
|
* Keep the node with the greater number of entries, and merge
|
|
|
|
|
* the node with fewer entries into it.
|
2025-12-24 22:00:08 +01:00
|
|
|
*/
|
2020-03-13 15:33:44 +01:00
|
|
|
|
2025-12-24 22:00:08 +01:00
|
|
|
if ((*node1ptr)->efnode_num >= (*node2ptr)->efnode_num)
|
2020-03-13 15:33:44 +01:00
|
|
|
{
|
|
|
|
|
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
|
|
|
{
|
2025-12-24 22:00:08 +01:00
|
|
|
bool topportk, topportr, bestname;
|
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 */
|
2025-12-24 22:00:08 +01:00
|
|
|
if ((!keeping->efnode_name) || (!topportk && topportr)
|
2020-03-13 15:33:44 +01:00
|
|
|
|| EFHNBest(removing->efnode_name->efnn_hier,
|
2025-12-24 22:00:08 +01:00
|
|
|
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
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
free_magic1_t mm1 = freeMagic1_init();
|
2017-04-25 14:41:48 +02:00
|
|
|
for (node = (EFNode *) head->efnode_next;
|
|
|
|
|
node != head;
|
|
|
|
|
node = (EFNode *) node->efnode_next)
|
|
|
|
|
{
|
2025-02-13 09:11:16 +01:00
|
|
|
{
|
|
|
|
|
free_magic1_t mm1_ = freeMagic1_init();
|
|
|
|
|
for (ap = node->efnode_attrs; ap; ap = ap->efa_next)
|
|
|
|
|
freeMagic1(&mm1_, (char *) ap);
|
|
|
|
|
freeMagic1_end(&mm1_);
|
|
|
|
|
}
|
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);
|
|
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
{
|
|
|
|
|
free_magic1_t mm1_ = freeMagic1_init();
|
|
|
|
|
for (lr = node->efnode_disjoint; lr; lr = lr->r_next)
|
|
|
|
|
freeMagic1(&mm1_, (char *)lr);
|
|
|
|
|
freeMagic1_end(&mm1_);
|
|
|
|
|
}
|
2022-11-20 04:02:44 +01:00
|
|
|
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1(&mm1, (char *) node);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
2025-02-13 09:11:16 +01:00
|
|
|
freeMagic1_end(&mm1);
|
2017-04-25 14:41:48 +02:00
|
|
|
}
|
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);
|
|
|
|
|
}
|