Added more handling of serial/parallel device networks, including
making any subcircuit serializable by using the new command option "property (device) serial|parallel enable|disable". Note that as of this commit, serial device detection is enabled but serial networks are not collapsed for matching, which will tend to lead to property errors in serial devices until this code is added, which should be in a day or two.
This commit is contained in:
parent
577dfb5a28
commit
8d976e5686
|
|
@ -1797,36 +1797,53 @@ struct Node *CreateNodeList(char *name, short graph)
|
|||
/* creates two lists of the correct 'shape', then traverses nodes
|
||||
* in sequence to link up 'subelement' field of ElementList,
|
||||
* then 'node' field of NodeList structures.
|
||||
*
|
||||
* Return the number of devices combined by serial/parallel merging
|
||||
*/
|
||||
|
||||
void CreateLists(char *name, short graph)
|
||||
int CreateLists(char *name, short graph)
|
||||
{
|
||||
struct Element *ElementScan;
|
||||
struct ElementList *EListScan;
|
||||
struct NodeList *NListScan;
|
||||
struct objlist *ob;
|
||||
struct nlist *tp;
|
||||
int ppass, spass, pcnt, scnt, total;
|
||||
|
||||
/* get a pointer to the cell */
|
||||
tp = LookupCellFile(name, graph);
|
||||
if (tp == NULL) {
|
||||
Fprintf(stderr, "No cell '%s' found.\n", name);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Circuit1 == NULL) Circuit1 = tp;
|
||||
else if (Circuit2 == NULL) Circuit2 = tp;
|
||||
else {
|
||||
Fprintf(stderr, "Error: CreateLists() called more than twice without a reset.\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CombineParallel(name, graph);
|
||||
CombineSerial(name, graph);
|
||||
/* Parallel and serial combinations. Run until networks of */
|
||||
/* devices are resolved into a single device with the network */
|
||||
/* represented by a number of property records. */
|
||||
|
||||
total = 0;
|
||||
for (ppass = 0; ; ppass++) {
|
||||
pcnt = CombineParallel(name, graph);
|
||||
total += pcnt;
|
||||
if (ppass > 0 && pcnt == 0) break;
|
||||
for (spass = 0; ; spass++) {
|
||||
scnt = CombineSerial(name, graph);
|
||||
total += scnt;
|
||||
if (scnt == 0) break;
|
||||
}
|
||||
if (spass == 0) break;
|
||||
}
|
||||
|
||||
Elements = CreateElementList(name, graph);
|
||||
Nodes = CreateNodeList(name, graph);
|
||||
if (LookupElementList == NULL) return;
|
||||
if (LookupElementList == NULL) return total;
|
||||
|
||||
ElementScan = NULL;
|
||||
NListScan = NULL; /* just to stop the compiler from bitching */
|
||||
|
|
@ -1852,6 +1869,7 @@ void CreateLists(char *name, short graph)
|
|||
|
||||
FREE(LookupElementList);
|
||||
LookupElementList = NULL;
|
||||
return total;
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
@ -1921,11 +1939,14 @@ struct Node *CreateNodeList(char *name, short graph)
|
|||
return (head);
|
||||
}
|
||||
|
||||
void CreateLists(char *name, short graph)
|
||||
/* creates two lists of the correct 'shape', then traverses nodes
|
||||
in sequence to link up 'subelement' field of ElementList,
|
||||
then 'node' field of NodeList structures.
|
||||
*/
|
||||
* in sequence to link up 'subelement' field of ElementList,
|
||||
* then 'node' field of NodeList structures.
|
||||
*
|
||||
* Return the number of devices combined by serial/parallel merging
|
||||
*/
|
||||
|
||||
int CreateLists(char *name, short graph)
|
||||
{
|
||||
struct Element *E, *ElementScan;
|
||||
struct Node *N, *NodeScan;
|
||||
|
|
@ -1933,25 +1954,40 @@ void CreateLists(char *name, short graph)
|
|||
struct NodeList *NListScan;
|
||||
struct objlist *ob, *obscan;
|
||||
struct nlist *tp;
|
||||
int node;
|
||||
int node, ppass, spass, pcnt, scnt, total;
|
||||
|
||||
/* get a pointer to the cell */
|
||||
tp = LookupCellFile(name, graph);
|
||||
if (tp == NULL) {
|
||||
Fprintf(stderr, "No cell '%s' found.\n", name);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Circuit1 == NULL) Circuit1 = tp;
|
||||
else if (Circuit2 == NULL) Circuit2 = tp;
|
||||
else {
|
||||
Fprintf(stderr, "Error: CreateLists() called more than twice without a reset.\n");
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ConnectAllNodes(name, graph);
|
||||
CombineParallel(name, graph);
|
||||
CombineSerial(name, graph);
|
||||
|
||||
/* Parallel and serial combinations. Run until networks of */
|
||||
/* devices are resolved into a single device with the network */
|
||||
/* represented by a number of property records. */
|
||||
|
||||
total = 0;
|
||||
for (ppass = 0; ; ppass++) {
|
||||
pcnt = CombineParallel(name, graph);
|
||||
total += pcnt;
|
||||
if (ppass > 0 && pcnt == 0) break;
|
||||
for (spass = 0; ; spass++) {
|
||||
scnt = CombineSerial(name, graph);
|
||||
total += scnt;
|
||||
if (scnt == 0) break;
|
||||
}
|
||||
if (spass == 0) break;
|
||||
}
|
||||
|
||||
E = CreateElementList(name, graph);
|
||||
N = CreateNodeList(name, graph);
|
||||
|
|
@ -1990,6 +2026,7 @@ void CreateLists(char *name, short graph)
|
|||
}
|
||||
Elements = E;
|
||||
Nodes = N;
|
||||
return total;
|
||||
}
|
||||
|
||||
#endif /* LOOKUP_INITIALIZATION */
|
||||
|
|
@ -2672,8 +2709,8 @@ int FlattenUnmatched(struct nlist *tc, char *parent, int stoplevel, int loclevel
|
|||
if (loclevel == stoplevel && !(tc->flags & CELL_MATCHED)) {
|
||||
ClearDumpedList();
|
||||
if (Debug == TRUE) Fprintf(stdout, "Level %d ", loclevel);
|
||||
Fprintf(stdout, "Flattening unmatched subcell %s in circuit %s ",
|
||||
tc->name, parent);
|
||||
Fprintf(stdout, "Flattening unmatched subcell %s in circuit %s (%s)",
|
||||
tc->name, parent, tc->file);
|
||||
changed = flattenInstancesOf(parent, tc->file, tc->name);
|
||||
Fprintf(stdout, "(%d instance%s)\n", changed, ((changed == 1) ? "" : "s"));
|
||||
return 1;
|
||||
|
|
@ -2972,6 +3009,7 @@ void CreateTwoLists(char *name1, int file1, char *name2, int file2)
|
|||
struct Element *El1;
|
||||
struct Node *N1;
|
||||
struct nlist *tc1, *tc2, *tcf;
|
||||
int modified;
|
||||
|
||||
ResetState();
|
||||
|
||||
|
|
@ -3004,7 +3042,7 @@ void CreateTwoLists(char *name1, int file1, char *name2, int file2)
|
|||
}
|
||||
}
|
||||
|
||||
CreateLists(name1, file1);
|
||||
modified = CreateLists(name1, file1);
|
||||
if (Elements == NULL) {
|
||||
Printf("Circuit %s contains no devices.\n", name1);
|
||||
return;
|
||||
|
|
@ -3049,7 +3087,7 @@ void CreateTwoLists(char *name1, int file1, char *name2, int file2)
|
|||
/* N1 now points to last element of list */
|
||||
|
||||
|
||||
CreateLists(name2, file2);
|
||||
modified += CreateLists(name2, file2);
|
||||
if (Elements == NULL) {
|
||||
Printf("Circuit %s contains no devices.\n", name2);
|
||||
ResetState();
|
||||
|
|
@ -3062,6 +3100,17 @@ void CreateTwoLists(char *name1, int file1, char *name2, int file2)
|
|||
return;
|
||||
}
|
||||
|
||||
if (modified > 0) {
|
||||
Printf("Circuit was modified by parallel/serial device merging.\n");
|
||||
Printf("New circuit summary:\n\n");
|
||||
/* print preliminary statistics */
|
||||
Printf("Contents of circuit 1: ");
|
||||
DescribeInstance(name1, file1);
|
||||
Printf("Contents of circuit 2: ");
|
||||
DescribeInstance(name2, file2);
|
||||
Printf("\n");
|
||||
}
|
||||
|
||||
/* splice new lists into existing lists */
|
||||
El1->next = Elements;
|
||||
for (El1 = Elements; El1->next != NULL; El1 = El1->next) {
|
||||
|
|
|
|||
247
base/netgen.c
247
base/netgen.c
|
|
@ -2849,6 +2849,157 @@ void ConnectAllNodes(char *model, int file)
|
|||
if (ob->node == -1) ob->node = nodenum++;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Serial and Parallel combination: */
|
||||
/* All devices of the same type that exist in serial and parallel */
|
||||
/* combinations will be treated as a single device in a network. */
|
||||
/* Serial connections are only allowed for resistors and inductors. */
|
||||
/* Any device may be connected in parallel. For combinations of serial */
|
||||
/* and parallel, as in a resistor network, there is a set of rules: */
|
||||
/* */
|
||||
/* Running parallel and serial checks: */
|
||||
/* 1. Run parallel once. If a repeat run and no devices are merged, */
|
||||
/* then go to 4. */
|
||||
/* 2. Run serial until no devices are merged. */
|
||||
/* 3. If serial ran more than once, then go to 1. */
|
||||
/* 4. End merge */
|
||||
/* */
|
||||
/* Each merge procedure, when it finds two devices that can be merged, */
|
||||
/* removes the second device from the netlist and adds its properties */
|
||||
/* to the first device. If a serial merge, then the nodes are adjusted */
|
||||
/* appropriately. Where A is the property list of the first device and */
|
||||
/* B is the property list of the second device, the first and last */
|
||||
/* properties of A and the first property of B may require a marker to */
|
||||
/* indicate the topology of the network, as follows (in order): */
|
||||
/* */
|
||||
/* For a parallel merge: */
|
||||
/* 1) If A has serial components then tag first property of A with */
|
||||
/* "open" and tag first property of B with "close". */
|
||||
/* 2) If B has serial components then tag first property of B with */
|
||||
/* "open". */
|
||||
/* */
|
||||
/* For a serial merge: */
|
||||
/* 1) If A has unbalanced "opens", then add "close" to first */
|
||||
/* property of B to balance the "opens". */
|
||||
/* 2) Always tag B with "serial". */
|
||||
/* */
|
||||
/* Tags are indicated by a property named "_tag" which has a string */
|
||||
/* value of ordered characters, "S" for serial, "O" for open, and "C" */
|
||||
/* for close. A device with only one property record has no "_tag" */
|
||||
/* record. A device which is in parallel with the device(s) in front */
|
||||
/* of it is implicitly parallel by not having an "S" tag, and may not */
|
||||
/* have a tag at all. */
|
||||
/* */
|
||||
/* The property check routine is responsible for comparing device */
|
||||
/* serial/parallel networks against each other. Otherwise, each */
|
||||
/* serial/parallel network is considered topologically as a single */
|
||||
/* device, and any differences in the serial/parallel networks between */
|
||||
/* two circuits being matched will be treated as a property error. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/* add_prop_tag --- add the tag character tagc to the property list of */
|
||||
/* obr. obr points to the first property record. */
|
||||
|
||||
int add_prop_tag(struct objlist *obr, char tagc)
|
||||
{
|
||||
struct objlist *nob;
|
||||
int i, k, l;
|
||||
struct valuelist *kv, *kv2;
|
||||
int hastag;
|
||||
char *tmpstr;
|
||||
|
||||
hastag = FALSE;
|
||||
for (nob = obr; nob->next && nob->next->type == PROPERTY; nob = nob->next) {
|
||||
for (i = 0; ; i++) {
|
||||
kv = &(nob->instance.props[i]);
|
||||
if (kv->type == PROP_ENDLIST) break;
|
||||
if (kv->type == PROP_STRING) {
|
||||
if (!strcmp(kv->key, "_tag")) {
|
||||
hastag = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hastag) {
|
||||
if (nob == obr) {
|
||||
// If _tag was first in the list, then just prepend tagc to the tag value
|
||||
tmpstr = kv->value.string;
|
||||
kv->value.string = (char *)MALLOC(strlen(tmpstr) + 2);
|
||||
sprintf(kv->value.string, "%c%s", tagc, tmpstr);
|
||||
FREE(tmpstr);
|
||||
}
|
||||
else {
|
||||
// Add a _tag key to the first property list and set value to tagc
|
||||
|
||||
kv = &(obr->instance.props[i]);
|
||||
k = 0;
|
||||
for (k = 0; ; k++) {
|
||||
kv = &(obr->instance.props[k]);
|
||||
if (kv->type == PROP_ENDLIST)
|
||||
break;
|
||||
}
|
||||
kv2 = (struct valuelist *)MALLOC((k + 2) * sizeof(struct valuelist));
|
||||
kv2->key = strsave("_tag");
|
||||
kv2->type = PROP_STRING;
|
||||
/* Value is set to tagc */
|
||||
kv2->value.string = (char *)MALLOC(2);
|
||||
sprintf(kv2->value.string, "%c", tagc);
|
||||
for (l = 0; l <= k; l++)
|
||||
kv2[l + 1] = obr->instance.props[l];
|
||||
FREE(obr->instance.props);
|
||||
obr->instance.props = kv2;
|
||||
}
|
||||
}
|
||||
return hastag;
|
||||
}
|
||||
|
||||
/* add_balancing_close --- find the number of unbalanced 'open' */
|
||||
/* records in ob1's property list, and prepend the correct number of */
|
||||
/* 'C' closures to the property list of ob2. */
|
||||
|
||||
void add_balancing_close(struct objlist *ob1, struct objlist *ob2)
|
||||
{
|
||||
struct objlist *nob;
|
||||
int i, k, l;
|
||||
struct valuelist *kv, *kv2;
|
||||
int opentags;
|
||||
char *tmpstr, *tag;
|
||||
|
||||
/* Find the first property record in ob1. */
|
||||
for (nob = ob1->next; nob && nob->type != FIRSTPIN; nob = nob->next)
|
||||
if (nob->type == PROPERTY)
|
||||
break;
|
||||
if (nob->type != PROPERTY) return; // shouldn't happen
|
||||
|
||||
opentags = 0;
|
||||
for (; nob->next && nob->next->type == PROPERTY; nob = nob->next) {
|
||||
for (i = 0; ; i++) {
|
||||
kv = &(nob->instance.props[i]);
|
||||
if (kv->type == PROP_ENDLIST) break;
|
||||
if (kv->type == PROP_STRING) {
|
||||
if (!strcmp(kv->key, "_tag")) {
|
||||
for (tag == kv->value.string; *tag != '\0'; tag++) {
|
||||
if (*tag == 'O') opentags++;
|
||||
else if (*tag == 'C') opentags--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opentags == 0) return;
|
||||
|
||||
/* Find the first property record in ob2. */
|
||||
for (nob = ob2->next; nob && nob->type != FIRSTPIN; nob = nob->next)
|
||||
if (nob->type == PROPERTY)
|
||||
break;
|
||||
if (nob->type != PROPERTY) return; // shouldn't happen
|
||||
|
||||
// This is slow but it's the easiest way to do it
|
||||
while (opentags-- > 0) add_prop_tag(nob, 'C');
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
/* Find all devices that are of the same class and check for parallel */
|
||||
/* combinations, and combine them where found, adjusting property "M" */
|
||||
|
|
@ -2865,9 +3016,11 @@ void ConnectAllNodes(char *model, int file)
|
|||
/* */
|
||||
/* If the device has permutable pins, then duplicate hashes are made */
|
||||
/* for each permutation. */
|
||||
/* */
|
||||
/* Return the number of devices merged. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void CombineParallel(char *model, int file)
|
||||
int CombineParallel(char *model, int file)
|
||||
{
|
||||
struct nlist *tp, *tsub;
|
||||
struct objlist *ob, *ob2, *nextob;
|
||||
|
|
@ -2875,13 +3028,13 @@ void CombineParallel(char *model, int file)
|
|||
struct hashdict devdict;
|
||||
struct Permutation *perm;
|
||||
size_t pcnt;
|
||||
int dcnt = 0;
|
||||
int i, dcnt = 0, hastag;
|
||||
char *pstr, *p2str, *pptr;
|
||||
struct valuelist *kv;
|
||||
|
||||
if ((tp = LookupCellFile(model, file)) == NULL) {
|
||||
Printf("Cell: %s does not exist.\n", model);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
|
||||
InitializeHashTable(&devdict, OBJHASHSIZE);
|
||||
|
|
@ -2890,12 +3043,20 @@ void CombineParallel(char *model, int file)
|
|||
for (ob = tp->cell; ob; ) {
|
||||
if (ob->type == FIRSTPIN) {
|
||||
|
||||
/* Watch for devices prohibited from parallel combination. */
|
||||
/* All devices allow parallel combination by default. */
|
||||
|
||||
tsub = LookupCellFile(ob->model.class, file);
|
||||
if ((tsub != NULL) && (tsub->flags & COMB_NO_PARALLEL)) {
|
||||
ob = ob->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ------------------------------------*/
|
||||
/* Generate hash key from pins */
|
||||
/* Handle pin permuations */
|
||||
/* ------------------------------------*/
|
||||
|
||||
tsub = LookupCellFile(ob->model.class, file);
|
||||
if ((tsub != NULL) && (tsub->permutes != NULL))
|
||||
perm = tsub->permutes;
|
||||
else
|
||||
|
|
@ -2971,7 +3132,7 @@ void CombineParallel(char *model, int file)
|
|||
lob = tlob;
|
||||
}
|
||||
else {
|
||||
/* Remove parallel device "ob" and append properties of */
|
||||
/* Find parallel device "ob" and append properties of */
|
||||
/* "sob" to it. If "ob" does not have properties, then */
|
||||
/* create a property record and set property "M" to 2. */
|
||||
|
||||
|
|
@ -3020,6 +3181,15 @@ void CombineParallel(char *model, int file)
|
|||
pob->next = obr;
|
||||
}
|
||||
lob->next = nextob;
|
||||
|
||||
// Serial/Parallel logic:
|
||||
|
||||
// If obr has _tag in properties, then add an "open" tag at obr
|
||||
add_prop_tag(obr, 'O');
|
||||
|
||||
// if ob2 has _tag in properties then add an "open" tag to ob2
|
||||
// and a "close" tag to obr
|
||||
if (add_prop_tag(ob2, 'O')) add_prop_tag(obr, 'C');
|
||||
}
|
||||
FREE((char *)pstr);
|
||||
}
|
||||
|
|
@ -3031,6 +3201,7 @@ void CombineParallel(char *model, int file)
|
|||
if (dcnt > 0) {
|
||||
Fprintf(stdout, "Class %s: Merged %d devices.\n", model, dcnt);
|
||||
}
|
||||
return dcnt;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
|
@ -3041,22 +3212,21 @@ void CombineParallel(char *model, int file)
|
|||
/* */
|
||||
/* This routine depends on CombineParallel() being run first so that no */
|
||||
/* parallel devices are reported as serial. */
|
||||
/* */
|
||||
/* Return the number of devices merged. */
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
void CombineSerial(char *model, int file)
|
||||
int CombineSerial(char *model, int file)
|
||||
{
|
||||
struct nlist *tp, *tp2;
|
||||
struct objlist ***instlist;
|
||||
struct objlist *ob, *ob2, *obs, *obp, *obn;
|
||||
int i, j;
|
||||
int i, j, scnt = 0;
|
||||
struct valuelist *kv;
|
||||
|
||||
// Work in progress. . .
|
||||
return;
|
||||
|
||||
if ((tp = LookupCellFile(model, file)) == NULL) {
|
||||
Printf("Cell: %s does not exist.\n", model);
|
||||
return;
|
||||
return -1;
|
||||
}
|
||||
instlist = (struct objlist ***)CALLOC((tp->nodename_cache_maxnodenum + 1),
|
||||
sizeof(struct objlist **));
|
||||
|
|
@ -3070,13 +3240,20 @@ void CombineSerial(char *model, int file)
|
|||
instlist[ob->node] = (struct objlist **)CALLOC(2,
|
||||
sizeof(struct objlist *));
|
||||
|
||||
/* For now, simple rule: Device must be a resistor */
|
||||
/* Device must be marked as able to be combined in serial. */
|
||||
/* Note that devices with more than two pins are expected */
|
||||
/* to serial connect along the first two pins, and the */
|
||||
/* remaining pins must all connect to the same nodes. By */
|
||||
/* default, CLASS_RES, CLASS_RES3, and CLASS_INDUCTOR are */
|
||||
/* all allowed to combine in serial. All other devices */
|
||||
/* must have serial combination explicitly enabled. */
|
||||
|
||||
tp2 = LookupCellFile(ob->model.class, file);
|
||||
if (tp2->class != CLASS_RES)
|
||||
/* invalidate node */
|
||||
instlist[ob->node][0] = NULL;
|
||||
else
|
||||
if (tp2->flags & COMB_SERIAL)
|
||||
instlist[ob->node][0] = obp;
|
||||
else
|
||||
/* invalidate node */
|
||||
instlist[ob->node][0] = NULL;
|
||||
}
|
||||
else if (instlist[ob->node][0] == NULL) {
|
||||
/* Node is not valid for serial connection */
|
||||
|
|
@ -3097,9 +3274,10 @@ void CombineSerial(char *model, int file)
|
|||
Fprintf(stdout, "Found serial instances %s and %s\n",
|
||||
instlist[i][0]->instance.name,
|
||||
instlist[i][1]->instance.name);
|
||||
scnt++;
|
||||
|
||||
/* To maintain knowledge of the topology, each device gets */
|
||||
/* a parameter 'S', string value set to "<node1>/<node2>". */
|
||||
/* a parameter '_tag', string value set to "S". */
|
||||
|
||||
for (j = 0; j <= 1; j++) {
|
||||
for (obp = instlist[i][j]; ; obp = obp->next) {
|
||||
|
|
@ -3120,13 +3298,11 @@ void CombineSerial(char *model, int file)
|
|||
}
|
||||
kv2 = (struct valuelist *)MALLOC((k + 2) *
|
||||
sizeof(struct valuelist));
|
||||
kv2->key = strsave("S");
|
||||
kv2->key = strsave("_tag");
|
||||
kv2->type = PROP_STRING;
|
||||
/* Value is set to "<node1>/<node2>" */
|
||||
kv2->value.string = (char *)MALLOC(strlen(nodename1) +
|
||||
strlen(nodename2) + 2);
|
||||
sprintf(kv2->value.string, "%s/%s", nodename1, nodename2);
|
||||
|
||||
/* Value is set to "S" */
|
||||
kv2->value.string = (char *)MALLOC(2);
|
||||
sprintf(kv2->value.string, "S");
|
||||
for (l = 0; l <= k; l++)
|
||||
kv2[l + 1] = obp->instance.props[l];
|
||||
FREE(obp->instance.props);
|
||||
|
|
@ -3145,14 +3321,13 @@ void CombineSerial(char *model, int file)
|
|||
strsave(obp->model.class);
|
||||
nob->instance.props = NewPropValue(2);
|
||||
|
||||
/* Create property record for property "S" */
|
||||
/* Create property record for property "_tag" */
|
||||
kv = &(nob->instance.props[0]);
|
||||
kv->key = strsave("S");
|
||||
kv->key = strsave("_tag");
|
||||
kv->type = PROP_STRING;
|
||||
/* Value is set to "<node1>/<node2>" */
|
||||
kv->value.string = (char *)MALLOC(strlen(nodename1) +
|
||||
strlen(nodename2) + 2);
|
||||
sprintf(kv->value.string, "%s/%s", nodename1, nodename2);
|
||||
/* Value is set to "S" */
|
||||
kv->value.string = (char *)MALLOC(2);
|
||||
sprintf(kv->value.string, "S");
|
||||
|
||||
/* End of property list */
|
||||
kv = &(nob->instance.props[1]);
|
||||
|
|
@ -3189,6 +3364,10 @@ void CombineSerial(char *model, int file)
|
|||
ob2->next = obs->next;
|
||||
if (obs->next) obs->next = NULL; // Terminate 2nd instance record
|
||||
|
||||
/* If 1st device has unbalanced 'open' records, then add 'close' */
|
||||
/* records to the 2nd device to balance. */
|
||||
add_balancing_close(instlist[i][0], instlist[i][1]);
|
||||
|
||||
/* Move property record(s) of the 2nd device to the first */
|
||||
for (obs = instlist[i][1]; obs && obs->type != PROPERTY; obs = obs->next);
|
||||
while (obs && (obs->type == PROPERTY)) {
|
||||
|
|
@ -3201,10 +3380,19 @@ void CombineSerial(char *model, int file)
|
|||
/* If 2nd device appears anywhere else in the serial device */
|
||||
/* list, replace it with the 1st device. */
|
||||
for (j = i + 1; j <= tp->nodename_cache_maxnodenum; j++) {
|
||||
if (instlist[j] == NULL) continue;
|
||||
|
||||
if (instlist[j][0] == instlist[i][1])
|
||||
instlist[j][0] = instlist[i][0];
|
||||
if (instlist[j][1] == instlist[i][1])
|
||||
instlist[j][1] = instlist[i][0];
|
||||
|
||||
/* If instlist[j]'s two entries point to the same device */
|
||||
/* then invalidate it. */
|
||||
if (instlist[j][0] == instlist[j][1]) {
|
||||
FREE(instlist[j]);
|
||||
instlist[j] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free 2nd device's object */
|
||||
|
|
@ -3228,6 +3416,7 @@ void CombineSerial(char *model, int file)
|
|||
}
|
||||
}
|
||||
FREE(instlist);
|
||||
return scnt;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
|
|
|||
|
|
@ -100,9 +100,9 @@ extern char *Res(char *fname, char *inststr, char *, char *);
|
|||
extern char *XLine(char *fname, char *inststr, char *, char *, char *, char *);
|
||||
extern char *Inductor(char *fname, char *inststr, char *, char *);
|
||||
|
||||
extern int StringIsValue(char *);
|
||||
extern int StringIsValue(char *);
|
||||
extern char *ConvertParam(char *);
|
||||
extern int ConvertStringToFloat(char *, double *);
|
||||
extern int ConvertStringToFloat(char *, double *);
|
||||
extern char *ScaleStringFloatValue(char *, double);
|
||||
extern void join(char *node1, char *node2);
|
||||
extern void Connect(char *tplt1, char *tplt2);
|
||||
|
|
@ -116,8 +116,8 @@ extern void FlattenCurrent();
|
|||
extern void ConvertGlobals(char *name, int fnum);
|
||||
extern int CleanupPins(char *name, int fnum);
|
||||
extern void ConnectAllNodes(char *model, int fnum);
|
||||
extern void CombineParallel(char *model, int fnum);
|
||||
extern void CombineSerial(char *model, int fnum);
|
||||
extern int CombineParallel(char *model, int fnum);
|
||||
extern int CombineSerial(char *model, int fnum);
|
||||
extern int NoDisconnectedNodes;
|
||||
extern int PropertyKeyMatch(char *, char *);
|
||||
extern int PropertyValueMatch(char *, char *);
|
||||
|
|
|
|||
|
|
@ -213,10 +213,10 @@ struct nlist {
|
|||
#define CELL_PROPSMATCHED 0x10 /* properties matched to matching cell */
|
||||
#define CELL_DUPLICATE 0x20 /* cell has a duplicate */
|
||||
|
||||
/* Flags for combination allowances */
|
||||
/* Flags for combination allowances and prohibitions */
|
||||
|
||||
#define COMB_SERIAL 0x20
|
||||
#define COMB_PARALLEL 0x40
|
||||
#define COMB_SERIAL 0x40
|
||||
#define COMB_NO_PARALLEL 0x80
|
||||
|
||||
extern struct nlist *CurrentCell;
|
||||
extern struct objlist *CurrentTail;
|
||||
|
|
|
|||
|
|
@ -2022,10 +2022,6 @@ _netcmp_compare(ClientData clientData,
|
|||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
// WIP!
|
||||
// CleanupPins(name1, fnum1); // Remove unconnected pins
|
||||
// CleanupPins(name2, fnum2); // Remove unconnected pins
|
||||
|
||||
UniquePins(name1, fnum1); // Check for and remove duplicate pins
|
||||
UniquePins(name2, fnum2); // Check for and remove duplicate pins
|
||||
|
||||
|
|
@ -2039,10 +2035,6 @@ _netcmp_compare(ClientData clientData,
|
|||
// but define global nodes that are brought out as ports by
|
||||
// ConvertGlobals().
|
||||
|
||||
// WIP!
|
||||
// CleanupPins(name1, fnum1);
|
||||
// CleanupPins(name2, fnum2);
|
||||
|
||||
CreateTwoLists(name1, fnum1, name2, fnum2);
|
||||
while (PrematchLists(name1, fnum1, name2, fnum2) > 0) {
|
||||
Fprintf(stdout, "Making another compare attempt.\n");
|
||||
|
|
@ -2973,6 +2965,13 @@ _netcmp_equate(ClientData clientData,
|
|||
/* merge --- set property merge behavior */
|
||||
/* or */
|
||||
/* netgen::property default */
|
||||
/* or */
|
||||
/* netgen::property <device>|<model> <option> */
|
||||
/* yes|no */
|
||||
/* Where <option> is one of: */
|
||||
/* serial --- allow/prohibit serial combination */
|
||||
/* parallel --- allow/prohibit parallel combination */
|
||||
/* */
|
||||
/* Formerly: (none) */
|
||||
/* Results: */
|
||||
/* Side Effects: */
|
||||
|
|
@ -2990,10 +2989,12 @@ _netcmp_property(ClientData clientData,
|
|||
int ival, argstart;
|
||||
|
||||
char *options[] = {
|
||||
"add", "create", "remove", "delete", "tolerance", "merge", NULL
|
||||
"add", "create", "remove", "delete", "tolerance", "merge", "serial",
|
||||
"parallel", NULL
|
||||
};
|
||||
enum OptionIdx {
|
||||
ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX
|
||||
ADD_IDX, CREATE_IDX, REMOVE_IDX, DELETE_IDX, TOLERANCE_IDX, MERGE_IDX,
|
||||
SERIAL_IDX, PARALLEL_IDX
|
||||
};
|
||||
int result, index, idx2;
|
||||
|
||||
|
|
@ -3010,6 +3011,9 @@ _netcmp_property(ClientData clientData,
|
|||
enum MergeOptionIdx {
|
||||
NONE_IDX, ADD_ONLY_IDX, ADD_CRIT_IDX, PAR_ONLY_IDX, PAR_CRIT_IDX
|
||||
};
|
||||
char *yesno[] = {
|
||||
"on", "yes", "true", "enable", "allow", "off", "no", "false", "disable", "prohibit", NULL
|
||||
};
|
||||
|
||||
if (objc < 2) {
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "valid_cellname ?option?");
|
||||
|
|
@ -3025,8 +3029,8 @@ _netcmp_property(ClientData clientData,
|
|||
/* compare source/drain area and perimeter. */
|
||||
|
||||
tp = FirstCell();
|
||||
while (tp != NULL) {
|
||||
switch (tp->class) {
|
||||
while (tp != NULL) {
|
||||
switch (tp->class) {
|
||||
case CLASS_NMOS: case CLASS_PMOS: case CLASS_FET3:
|
||||
case CLASS_NMOS4: case CLASS_PMOS4: case CLASS_FET4:
|
||||
case CLASS_FET:
|
||||
|
|
@ -3038,6 +3042,7 @@ _netcmp_property(ClientData clientData,
|
|||
break;
|
||||
case CLASS_RES: case CLASS_RES3:
|
||||
PropertyMerge(tp->name, tp->file, "l", MERGE_PAR_CRIT);
|
||||
tp->flags |= COMB_SERIAL;
|
||||
break;
|
||||
case CLASS_CAP: case CLASS_ECAP: case CLASS_CAP3:
|
||||
// NOTE: No attempt to combine area, width, or length;
|
||||
|
|
@ -3046,6 +3051,7 @@ _netcmp_property(ClientData clientData,
|
|||
break;
|
||||
case CLASS_INDUCTOR:
|
||||
PropertyMerge(tp->name, tp->file, "value", MERGE_PAR_CRIT);
|
||||
tp->flags |= COMB_SERIAL;
|
||||
break;
|
||||
}
|
||||
tp = NextCell();
|
||||
|
|
@ -3101,6 +3107,45 @@ _netcmp_property(ClientData clientData,
|
|||
argstart = 3;
|
||||
|
||||
switch (index) {
|
||||
case SERIAL_IDX:
|
||||
case PARALLEL_IDX:
|
||||
if (objc == 3) {
|
||||
if (index == SERIAL_IDX) {
|
||||
tobj1 = Tcl_NewBooleanObj((tp->flags & COMB_SERIAL) ? 1 : 0);
|
||||
Tcl_SetObjResult(interp, tobj1);
|
||||
return TCL_OK;
|
||||
}
|
||||
else {
|
||||
tobj1 = Tcl_NewBooleanObj((tp->flags & COMB_NO_PARALLEL) ? 0 : 1);
|
||||
Tcl_SetObjResult(interp, tobj1);
|
||||
return TCL_OK;
|
||||
}
|
||||
}
|
||||
else if (objc == 4) {
|
||||
if (Tcl_GetIndexFromObj(interp, objv[3],
|
||||
(CONST84 char **)yesno,
|
||||
"combine", 0, &idx2) != TCL_OK) {
|
||||
Tcl_WrongNumArgs(interp, 3, objv, "enable|disable");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if (idx2 <= 4) { /* true, enable, etc. */
|
||||
if (index == SERIAL_IDX)
|
||||
tp->flags |= COMB_SERIAL;
|
||||
else
|
||||
tp->flags &= ~COMB_NO_PARALLEL;
|
||||
}
|
||||
else { /* false, disable, etc. */
|
||||
if (index == SERIAL_IDX)
|
||||
tp->flags &= ~COMB_SERIAL;
|
||||
else
|
||||
tp->flags |= COMB_NO_PARALLEL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Tcl_WrongNumArgs(interp, 2, objv, "serial|parallel enable|disable");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
case ADD_IDX:
|
||||
case CREATE_IDX:
|
||||
|
|
|
|||
Loading…
Reference in New Issue