1165 lines
32 KiB
C
1165 lines
32 KiB
C
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResSimplify -- contains routines used to simplify signal nets.
|
||
*
|
||
*
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
#ifndef lint
|
||
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/resis/ResSimple.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
|
||
#endif /* not lint */
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
#include <math.h>
|
||
|
||
#include "utils/magic.h"
|
||
#include "utils/geometry.h"
|
||
#include "utils/geofast.h"
|
||
#include "tiles/tile.h"
|
||
#include "utils/hash.h"
|
||
#include "utils/heap.h"
|
||
#include "database/database.h"
|
||
#include "utils/malloc.h"
|
||
#include "textio/textio.h"
|
||
#include "extract/extract.h"
|
||
#include "extract/extractInt.h"
|
||
#include "windows/windows.h"
|
||
#include "dbwind/dbwind.h"
|
||
#include "utils/stack.h"
|
||
#include "utils/tech.h"
|
||
#include "textio/txcommands.h"
|
||
#include "resis/resis.h"
|
||
|
||
#define MILLIOHMSPEROHM 1000
|
||
|
||
/* Forward declarations */
|
||
void ResSetPathRes();
|
||
void resPathNode();
|
||
void resPathRes();
|
||
Heap ResistorHeap;
|
||
int resRemoveLoops = FALSE;
|
||
|
||
/* Forward declarations */
|
||
|
||
extern void ResMoveDevices();
|
||
extern void ResAddResistorToList();
|
||
|
||
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResSimplifyNet- Reduces complete (?) net produced by ResProcessTiles into
|
||
* something a little less chaotic.
|
||
*
|
||
* Results: none
|
||
*
|
||
* Side Effects: Can eliminate nodes and resistors, and move devices from
|
||
* one node to another.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
ResSimplifyNet(nodelist,biglist,reslist,tolerance)
|
||
resNode **nodelist,**biglist;
|
||
resResistor **reslist;
|
||
float tolerance;
|
||
|
||
{
|
||
resElement *resisptr;
|
||
resNode *node,*otherNode,*node1,*node2;
|
||
resResistor *resistor1 = NULL,*resistor2 = NULL;
|
||
int numdrive = 0, numreceive = 0;
|
||
int MarkedReceivers,UnMarkedReceivers,NumberOfDrivers,PendingReceivers;
|
||
|
||
if (*nodelist == NULL) return;
|
||
node = *nodelist;
|
||
node->rn_status |= MARKED | FINISHED;
|
||
*nodelist = node->rn_more;
|
||
if (node->rn_more != NULL)
|
||
{
|
||
node->rn_more->rn_less = (resNode *) NULL;
|
||
}
|
||
node->rn_more = *biglist;
|
||
if (*biglist != (resNode *) NULL)
|
||
{
|
||
(*biglist)->rn_less = node;
|
||
}
|
||
*biglist = node;
|
||
|
||
/*
|
||
Walk though resistors. Mark uninitialized ones and assign them
|
||
A direction. Keep track of the number of resistors pointing in
|
||
each direction.
|
||
*/
|
||
for (resisptr = node->rn_re; resisptr != NULL; resisptr = resisptr->re_nextEl)
|
||
{
|
||
if (((resisptr->re_thisEl->rr_status & RES_MARKED) == RES_MARKED) &&
|
||
(resisptr->re_thisEl->rr_connection2 == node))
|
||
{
|
||
if (resistor1 == NULL)
|
||
{
|
||
resistor1 = resisptr->re_thisEl;
|
||
}
|
||
else
|
||
{
|
||
resistor2 = resisptr->re_thisEl;
|
||
}
|
||
numdrive++;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
Resistor direction is from node1 to node2. If the resistor
|
||
is not marked, mark it and make sure the direction is
|
||
set properly.
|
||
*/
|
||
|
||
if ((resisptr->re_thisEl->rr_status & RES_MARKED) != RES_MARKED)
|
||
{
|
||
if (resisptr->re_thisEl->rr_connection2 == node)
|
||
{
|
||
resisptr->re_thisEl->rr_connection2 = resisptr->re_thisEl->rr_connection1;
|
||
resisptr->re_thisEl->rr_connection1 = node;
|
||
}
|
||
resisptr->re_thisEl->rr_status |= RES_MARKED;
|
||
}
|
||
if (resistor1 == NULL)
|
||
{
|
||
resistor1 = resisptr->re_thisEl;
|
||
}
|
||
else
|
||
{
|
||
resistor2 = resisptr->re_thisEl;
|
||
}
|
||
numreceive++;
|
||
}
|
||
}
|
||
|
||
/*
|
||
Is the node reached by one resistor? If it is, check the resistor's
|
||
other end. Check the number of drivers at the other end. If it is
|
||
more than 1, delete the current resistor to break the deadlock.
|
||
*/
|
||
if (numreceive == 0 && numdrive == 1 && node->rn_why != RES_NODE_ORIGIN)
|
||
{
|
||
resistor1->rr_status |= RES_DEADEND;
|
||
if (resistor1->rr_value < tolerance)
|
||
{
|
||
otherNode = (resistor1->rr_connection1 == node) ? resistor1->rr_connection2 : resistor1->rr_connection1;
|
||
MarkedReceivers = 0;
|
||
UnMarkedReceivers = 0;
|
||
NumberOfDrivers = 0;
|
||
PendingReceivers = 0;
|
||
resistor2 = resistor1;
|
||
for (resisptr = otherNode->rn_re; resisptr != NULL; resisptr=resisptr->re_nextEl)
|
||
{
|
||
if (resisptr->re_thisEl->rr_connection1 == otherNode)
|
||
{
|
||
if ((resisptr->re_thisEl->rr_connection2->rn_status & MARKED) != MARKED)
|
||
{
|
||
PendingReceivers++;
|
||
}
|
||
if (resisptr->re_thisEl->rr_status & RES_DEADEND ||
|
||
resisptr->re_thisEl->rr_value > tolerance)
|
||
{
|
||
MarkedReceivers++;
|
||
resistor2 = (resisptr->re_thisEl->rr_value >= resistor2->rr_value) ? resisptr->re_thisEl : resistor2;
|
||
}
|
||
else
|
||
{
|
||
UnMarkedReceivers++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
NumberOfDrivers++;
|
||
}
|
||
}
|
||
/* other recievers at far end? If so, reschedule other node;
|
||
deadlock will be settled from that node.
|
||
*/
|
||
if ((MarkedReceivers+UnMarkedReceivers+NumberOfDrivers == 2) ||
|
||
(UnMarkedReceivers == 0 && MarkedReceivers > 1 && resistor2 == resistor1 && PendingReceivers == 0))
|
||
{
|
||
if (otherNode->rn_status & MARKED)
|
||
{
|
||
otherNode->rn_status &= ~MARKED;
|
||
ResRemoveFromQueue(otherNode,biglist);
|
||
otherNode->rn_less= NULL;
|
||
otherNode->rn_more = *nodelist;
|
||
if (*nodelist != NULL)
|
||
{
|
||
(*nodelist)->rn_less = otherNode;
|
||
}
|
||
*nodelist = otherNode;
|
||
}
|
||
return;
|
||
}
|
||
|
||
/*
|
||
Break loop here. More than one driver indicates a loop;
|
||
remove deadend, allowing drivers to be merged
|
||
*/
|
||
else if (UnMarkedReceivers == 0 && (MarkedReceivers == 1 && NumberOfDrivers > 1 || resistor2 != resistor1))
|
||
|
||
{
|
||
otherNode->rn_float.rn_area += resistor1->rr_float.rr_area;
|
||
otherNode->rn_status &= ~RES_DONE_ONCE;
|
||
ResDeleteResPointer(resistor1->rr_connection1,resistor1);
|
||
ResDeleteResPointer(resistor1->rr_connection2,resistor1);
|
||
ResEliminateResistor(resistor1,reslist);
|
||
ResMergeNodes(otherNode,node,nodelist,biglist);
|
||
if (otherNode->rn_status & MARKED)
|
||
{
|
||
otherNode->rn_status &= ~MARKED;
|
||
ResRemoveFromQueue(otherNode,biglist);
|
||
otherNode->rn_less= NULL;
|
||
otherNode->rn_more = *nodelist;
|
||
if (*nodelist != NULL)
|
||
{
|
||
(*nodelist)->rn_less = otherNode;
|
||
}
|
||
*nodelist = otherNode;
|
||
}
|
||
ResDoneWithNode(otherNode);
|
||
}
|
||
}
|
||
|
||
}
|
||
/*
|
||
Two resistors in series? Combine them and move devices to
|
||
appropriate end.
|
||
*/
|
||
else if (numdrive+numreceive == 2 &&
|
||
(resistor1->rr_value < tolerance && resistor2->rr_value < tolerance))
|
||
{
|
||
if ((resistor1->rr_status & RES_MARKED) == 0 && (resistor1->rr_connection2 == node))
|
||
{
|
||
resistor1->rr_connection2 = resistor1->rr_connection1;
|
||
resistor1->rr_connection1 = node;
|
||
}
|
||
resistor1->rr_status |= RES_MARKED;
|
||
if ((resistor2->rr_status & RES_MARKED) == 0 && (resistor2->rr_connection2 == node))
|
||
{
|
||
resistor2->rr_connection2 = resistor2->rr_connection1;
|
||
resistor2->rr_connection1 = node;
|
||
}
|
||
resistor2->rr_status |= RES_MARKED;
|
||
node1 = (resistor1->rr_connection1 == node) ? resistor1->rr_connection2 : resistor1->rr_connection1;
|
||
node2 = (resistor2->rr_connection1 == node) ? resistor2->rr_connection2 : resistor2->rr_connection1;
|
||
otherNode = (resistor1->rr_status & RES_DEADEND &&
|
||
resistor1->rr_value < tolerance / 2) ||
|
||
((resistor2->rr_status & RES_DEADEND) == 0 &&
|
||
resistor1->rr_value < resistor2->rr_value) ? node1 : node2;
|
||
|
||
/*
|
||
make one big resistor out of two little ones, eliminating
|
||
the current node. Devices connected to this node are
|
||
moved to either end depending on their resistance.
|
||
*/
|
||
ResMoveDevices(node,otherNode);
|
||
otherNode->rn_noderes = MIN(node->rn_noderes,otherNode->rn_noderes);
|
||
node2->rn_float.rn_area += resistor1->rr_value*node->rn_float.rn_area/(resistor1->rr_value+resistor2->rr_value);
|
||
node1->rn_float.rn_area += resistor2->rr_value*node->rn_float.rn_area/(resistor1->rr_value+resistor2->rr_value);
|
||
resistor1->rr_value += resistor2->rr_value;
|
||
resistor1->rr_float.rr_area +=resistor2->rr_float.rr_area;
|
||
if (resistor1 == *reslist)
|
||
{
|
||
*reslist = resistor1->rr_nextResistor;
|
||
}
|
||
else
|
||
{
|
||
resistor1->rr_lastResistor->rr_nextResistor = resistor1->rr_nextResistor;
|
||
}
|
||
if (resistor1->rr_nextResistor != NULL)
|
||
{
|
||
resistor1->rr_nextResistor->rr_lastResistor = resistor1->rr_lastResistor;
|
||
}
|
||
ResAddResistorToList(resistor1,reslist);
|
||
ResDeleteResPointer(node,resistor1);
|
||
ResDeleteResPointer(node,resistor2);
|
||
ResDeleteResPointer(node2,resistor2);
|
||
if (resistor1->rr_connection1 == node)
|
||
{
|
||
resistor1->rr_connection1 = node2;
|
||
}
|
||
else
|
||
{
|
||
resistor1->rr_connection2 = node2;
|
||
}
|
||
resisptr = (resElement *) mallocMagic((unsigned)(sizeof(resElement)));
|
||
resisptr->re_thisEl = resistor1;
|
||
resisptr->re_nextEl = node2->rn_re;
|
||
node2->rn_re = resisptr;
|
||
ResEliminateResistor(resistor2,reslist);
|
||
otherNode->rn_status |= (node->rn_status & RN_MAXTDI);
|
||
ResCleanNode(node,TRUE,biglist,nodelist);
|
||
node1->rn_status &= ~RES_DONE_ONCE;
|
||
if (node1->rn_status & MARKED)
|
||
{
|
||
node1->rn_status &= ~MARKED;
|
||
ResRemoveFromQueue(node1,biglist);
|
||
node1->rn_less= NULL;
|
||
node1->rn_more = *nodelist;
|
||
if (*nodelist != NULL)
|
||
{
|
||
(*nodelist)->rn_less = node1;
|
||
}
|
||
*nodelist = node1;
|
||
}
|
||
node2->rn_status &= ~RES_DONE_ONCE;
|
||
if (node2->rn_status & MARKED)
|
||
{
|
||
node2->rn_status &= ~MARKED;
|
||
ResRemoveFromQueue(node2,biglist);
|
||
node2->rn_less= NULL;
|
||
node2->rn_more = *nodelist;
|
||
if (*nodelist != NULL)
|
||
{
|
||
(*nodelist)->rn_less = node2;
|
||
}
|
||
*nodelist = node2;
|
||
}
|
||
ResDoneWithNode(node1);
|
||
}
|
||
|
||
/*
|
||
Last resort- keep propagating down the tree. To avoid looping,
|
||
mark each node when it is reached. Don't reschedule node if
|
||
none of the connections to it have changed since it was marked
|
||
*/
|
||
else if (numreceive > 0 && (node->rn_status & RES_DONE_ONCE) == 0)
|
||
{
|
||
node->rn_status |= RES_DONE_ONCE;
|
||
for (resisptr = node->rn_re; resisptr != NULL; resisptr = resisptr->re_nextEl)
|
||
{
|
||
if (resisptr->re_thisEl->rr_connection1 == node)
|
||
{
|
||
/*
|
||
elements with a resistance greater than the
|
||
tolerance should only be propagated past once-
|
||
loops may occur otherwise.
|
||
*/
|
||
if (resisptr->re_thisEl->rr_status & RES_DONE_ONCE)
|
||
{
|
||
continue;
|
||
}
|
||
if (resisptr->re_thisEl->rr_connection2->rn_status & MARKED)
|
||
{
|
||
/*
|
||
mark big resistors so we only process them
|
||
once.
|
||
*/
|
||
if (resisptr->re_thisEl->rr_value > tolerance)
|
||
{
|
||
resisptr->re_thisEl->rr_status |= RES_DONE_ONCE;
|
||
}
|
||
resisptr->re_thisEl->rr_connection2->rn_status &= ~MARKED;
|
||
ResRemoveFromQueue(resisptr->re_thisEl->rr_connection2,biglist);
|
||
resisptr->re_thisEl->rr_connection2->rn_less= NULL;
|
||
resisptr->re_thisEl->rr_connection2->rn_more = *nodelist;
|
||
if (*nodelist != NULL)
|
||
{
|
||
(*nodelist)->rn_less = resisptr->re_thisEl->rr_connection2;
|
||
}
|
||
*nodelist = resisptr->re_thisEl->rr_connection2;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResMoveDevices-- move devices from one node1 to node2
|
||
*
|
||
* Results: none
|
||
*
|
||
* Side Effects: Changes device connections and node tElements.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
ResMoveDevices(node1,node2)
|
||
resNode *node1,*node2;
|
||
|
||
{
|
||
tElement *devptr,*oldptr;
|
||
resDevice *device;
|
||
|
||
devptr = node1->rn_te;
|
||
while (devptr != NULL)
|
||
{
|
||
device = devptr->te_thist;
|
||
oldptr = devptr;
|
||
devptr = devptr->te_nextt;
|
||
if (device->rd_status & RES_DEV_PLUG)
|
||
{
|
||
if (((ResPlug *)(device))->rpl_node == node1)
|
||
{
|
||
((ResPlug *)(device))->rpl_node = node2;
|
||
}
|
||
else
|
||
{
|
||
TxError("Bad node connection in plug\n");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (device->rd_fet_gate == node1)
|
||
{
|
||
device->rd_fet_gate = node2;
|
||
}
|
||
else if (device->rd_fet_source == node1)
|
||
{
|
||
device->rd_fet_source = node2;
|
||
}
|
||
else if (device->rd_fet_drain == node1)
|
||
{
|
||
device->rd_fet_drain = node2;
|
||
}
|
||
else
|
||
{
|
||
TxError("Missing Device connection in squish routines at %d, %d\n",node1->rn_loc.p_x,node1->rn_loc.p_y);
|
||
}
|
||
}
|
||
oldptr->te_nextt = node2->rn_te;
|
||
node2->rn_te = oldptr;
|
||
}
|
||
node1->rn_te = NULL;
|
||
}
|
||
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResScrunchNet-- Last ditch net simplification. Used to break deadlocks
|
||
* in ResSimplifyNet. Resistors are sorted by value. The smallest
|
||
* resistor is combined with its smallest neighbor, and ResSimplifyNet
|
||
* is called. This continues until the smallest resistor is greater
|
||
* than the tolerance.
|
||
*
|
||
* Results:none
|
||
*
|
||
* Side Effects: Nodes and resistors are eliminated.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
ResScrunchNet(reslist,pendingList,biglist,tolerance)
|
||
resResistor **reslist;
|
||
resNode **pendingList,**biglist;
|
||
float tolerance;
|
||
|
||
{
|
||
resResistor *locallist=NULL,*current,*working;
|
||
resNode *node1,*node2;
|
||
resElement *rcell1;
|
||
int c1,c2;
|
||
|
||
/* sort resistors by size */
|
||
current = *reslist;
|
||
while (current != NULL)
|
||
{
|
||
working = current;
|
||
current = current->rr_nextResistor;
|
||
if (working == *reslist)
|
||
{
|
||
*reslist = current;
|
||
}
|
||
else
|
||
{
|
||
working->rr_lastResistor->rr_nextResistor = current;
|
||
}
|
||
if (current != NULL)
|
||
{
|
||
current->rr_lastResistor = working->rr_lastResistor;
|
||
}
|
||
ResAddResistorToList(working,&locallist);
|
||
}
|
||
|
||
|
||
*reslist = locallist;
|
||
while (*reslist != NULL && (*reslist)->rr_value < tolerance)
|
||
{
|
||
current = *reslist;
|
||
if (current->rr_nextResistor == NULL)
|
||
{
|
||
break;
|
||
}
|
||
working = NULL;
|
||
c1=0;
|
||
c2=0;
|
||
|
||
/* search for next smallest adjoining resistor */
|
||
for (rcell1 = current->rr_connection1->rn_re; rcell1 != NULL; rcell1 = rcell1->re_nextEl)
|
||
{
|
||
if (rcell1->re_thisEl != current)
|
||
{
|
||
c1++;
|
||
if (working == NULL)
|
||
{
|
||
working = rcell1->re_thisEl;
|
||
node1 = current->rr_connection1;
|
||
}
|
||
else
|
||
{
|
||
if (working->rr_value > rcell1->re_thisEl->rr_value)
|
||
{
|
||
node1 = current->rr_connection1;
|
||
working = rcell1->re_thisEl;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
for (rcell1 = current->rr_connection2->rn_re; rcell1 != NULL; rcell1 = rcell1->re_nextEl)
|
||
{
|
||
if (rcell1->re_thisEl != current)
|
||
{
|
||
c2++;
|
||
if (working == NULL)
|
||
{
|
||
working = rcell1->re_thisEl;
|
||
node1 = current->rr_connection2;
|
||
}
|
||
else
|
||
{
|
||
if (working->rr_value > rcell1->re_thisEl->rr_value)
|
||
{
|
||
node1 = current->rr_connection2;
|
||
working = rcell1->re_thisEl;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/*
|
||
if the current resistor isn't a deadend, add its value and
|
||
area to that of the next smallest one. If it is a deadend,
|
||
simply add its area to its node.
|
||
*/
|
||
if (c1 != 0 && c2 != 0)
|
||
{
|
||
working->rr_value += current->rr_value;
|
||
working->rr_float.rr_area += current->rr_float.rr_area;
|
||
}
|
||
else
|
||
{
|
||
node1->rn_float.rn_area += current->rr_float.rr_area;
|
||
}
|
||
/*
|
||
Move everything from from one end of the ressistor to the
|
||
other and eliminate the resistor.
|
||
*/
|
||
node2 = (current->rr_connection1 == node1) ? current->rr_connection2 : current->rr_connection1;
|
||
ResDeleteResPointer(current->rr_connection1,current);
|
||
ResDeleteResPointer(current->rr_connection2,current);
|
||
working->rr_lastResistor->rr_nextResistor = working->rr_nextResistor;
|
||
if (working->rr_nextResistor != NULL)
|
||
{
|
||
working->rr_nextResistor->rr_lastResistor = working->rr_lastResistor;
|
||
}
|
||
ResEliminateResistor(current,reslist);
|
||
ResAddResistorToList(working,reslist);
|
||
if (node2->rn_why & RES_NODE_ORIGIN)
|
||
{
|
||
ResMergeNodes(node2,node1,pendingList,biglist);
|
||
node1 = node2;
|
||
}
|
||
else
|
||
{
|
||
ResMergeNodes(node1,node2,pendingList,biglist);
|
||
}
|
||
|
||
/*
|
||
Try further simplification on net using ResDoneWithNode and
|
||
ResSimplifyNet.
|
||
*/
|
||
ResRemoveFromQueue(node1,biglist);
|
||
ResAddToQueue(node1,pendingList);
|
||
node1->rn_status &= ~(RES_DONE_ONCE | FINISHED);
|
||
ResDoneWithNode(node1);
|
||
while (*pendingList != NULL)
|
||
{
|
||
ResSimplifyNet(pendingList,biglist,reslist,tolerance);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResAddResistorToList-- Adds resistor to list according to its value
|
||
* (smallest first).
|
||
*
|
||
* Results:none
|
||
*
|
||
* Side Effects: modifies locallist.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
ResAddResistorToList(resistor,locallist)
|
||
resResistor *resistor,**locallist;
|
||
|
||
{
|
||
resResistor *local,*last=NULL;
|
||
|
||
for (local = *locallist; local != NULL; local = local->rr_nextResistor)
|
||
{
|
||
if (local->rr_value >= resistor->rr_value)
|
||
{
|
||
break;
|
||
}
|
||
last = local;
|
||
}
|
||
if (local != NULL)
|
||
{
|
||
resistor->rr_nextResistor = local;
|
||
resistor->rr_lastResistor = local->rr_lastResistor;
|
||
if (local->rr_lastResistor == NULL)
|
||
{
|
||
*locallist = resistor;
|
||
}
|
||
else
|
||
{
|
||
local->rr_lastResistor->rr_nextResistor = resistor;
|
||
}
|
||
local->rr_lastResistor = resistor;
|
||
}
|
||
else
|
||
{
|
||
if (last != NULL)
|
||
{
|
||
last->rr_nextResistor = resistor;
|
||
resistor->rr_lastResistor = last;
|
||
resistor->rr_nextResistor = NULL;
|
||
}
|
||
else
|
||
{
|
||
resistor->rr_nextResistor = NULL;
|
||
resistor->rr_lastResistor = NULL;
|
||
*locallist = resistor;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResdistributeSubstrateCapacitance--
|
||
*
|
||
* Results:
|
||
* None.
|
||
*
|
||
* Side Effects:
|
||
* takes total capacitance to VDD or GND in a node and distributes
|
||
* it onto the new nodes.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
ResDistributeCapacitance(nodelist,totalcap)
|
||
resNode *nodelist;
|
||
float totalcap;
|
||
|
||
{
|
||
float totalarea=0,capperarea;
|
||
resNode *workingNode;
|
||
resElement *rptr;
|
||
|
||
for (workingNode = nodelist; workingNode != NULL; workingNode=workingNode->rn_more)
|
||
{
|
||
for (rptr = workingNode->rn_re; rptr != NULL; rptr=rptr->re_nextEl)
|
||
{
|
||
if (rptr->re_thisEl->rr_float.rr_area != 0.0)
|
||
{
|
||
TxError("Nonnull resistor area\n");
|
||
}
|
||
/* workingNode->rn_float.rn_area += rptr->re_thisEl->rr_float.rr_area/2; */
|
||
}
|
||
totalarea += workingNode->rn_float.rn_area;
|
||
}
|
||
if (totalarea == 0)
|
||
{
|
||
TxError("Error: Node with no area.\n");
|
||
return;
|
||
}
|
||
capperarea = FEMTOTOATTO*totalcap/totalarea;
|
||
for (workingNode = nodelist; workingNode != NULL; workingNode=workingNode->rn_more)
|
||
{
|
||
workingNode->rn_float.rn_area *= capperarea;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResCalculateChildCapacitance-- calculates capacitance of this node and
|
||
* all downstream nodes.
|
||
*
|
||
* Results: Returns capacitance of this node and children nodes if connected
|
||
* to a tree- returns -1 If the subtree contains loops.
|
||
*
|
||
* Side Effects: Adds RCDelayStuff fields to nodes.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
float
|
||
ResCalculateChildCapacitance(me)
|
||
resNode *me;
|
||
|
||
{
|
||
RCDelayStuff *myC;
|
||
resElement *workingRes;
|
||
resDevice *dev;
|
||
float childcap;
|
||
tElement *tptr;
|
||
int t;
|
||
ExtDevice *devptr;
|
||
|
||
|
||
if (me->rn_client != (ClientData) NULL) /* we have a loop */
|
||
{
|
||
return(-1);
|
||
}
|
||
myC = (RCDelayStuff *) mallocMagic((unsigned) (sizeof(RCDelayStuff)));
|
||
me->rn_client = (ClientData) myC;
|
||
|
||
/* This following assumes that ResDistributeCapacitance has been run */
|
||
/* and the the resulting capacitance value is stored in the area field */
|
||
myC->rc_Cdownstream = me->rn_float.rn_area;
|
||
|
||
/* get capacitance for all connected gates */
|
||
for (tptr = me->rn_te; tptr != NULL; tptr = tptr->te_nextt)
|
||
{
|
||
dev = tptr->te_thist;
|
||
t = TiGetType(dev->rd_tile);
|
||
if (dev->rd_fet_gate == me)
|
||
{
|
||
devptr = ExtCurStyle->exts_device[t];
|
||
myC->rc_Cdownstream +=
|
||
dev->rd_length*
|
||
dev->rd_width*
|
||
devptr->exts_deviceGateCap+
|
||
(dev->rd_width+dev->rd_width)*
|
||
devptr->exts_deviceSDCap;
|
||
|
||
}
|
||
}
|
||
|
||
/* Calculate child Capacitance */
|
||
for (workingRes = me->rn_re; workingRes != NULL; workingRes=workingRes->re_nextEl)
|
||
{
|
||
if (workingRes->re_thisEl->rr_connection1 == me &&
|
||
(workingRes->re_thisEl->rr_status & RES_TDI_IGNORE) == 0)
|
||
{
|
||
childcap = ResCalculateChildCapacitance(workingRes->re_thisEl->rr_connection2);
|
||
if (childcap == -1)
|
||
{
|
||
return(-1);
|
||
}
|
||
myC->rc_Cdownstream +=childcap;
|
||
}
|
||
}
|
||
return(myC->rc_Cdownstream);
|
||
}
|
||
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResCalculateTDi- Calculates TDi numbers for all the nodes in the circuit.
|
||
*
|
||
* Results: none
|
||
*
|
||
* Side Effects: sets the rc_Tdi fields of the RCDelayStuff fields of the
|
||
* nodes.
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
ResCalculateTDi(node,resistor,resistorvalue)
|
||
resNode *node;
|
||
resResistor *resistor;
|
||
int resistorvalue;
|
||
|
||
{
|
||
resElement *workingRes;
|
||
RCDelayStuff *rcd = (RCDelayStuff *)node->rn_client;
|
||
RCDelayStuff *rcd2;
|
||
|
||
ASSERT(rcd != NULL,"ResCalculateTdi");
|
||
if (resistor == NULL)
|
||
{
|
||
rcd->rc_Tdi = rcd->rc_Cdownstream*(float)resistorvalue;
|
||
}
|
||
else
|
||
{
|
||
rcd2 = (RCDelayStuff *)resistor->rr_connection1->rn_client;
|
||
ASSERT(rcd2 != NULL,"ResCalculateTdi");
|
||
rcd->rc_Tdi=rcd->rc_Cdownstream*(float)resistor->rr_value +
|
||
rcd2->rc_Tdi;
|
||
|
||
}
|
||
|
||
for (workingRes = node->rn_re; workingRes != NULL; workingRes=workingRes->re_nextEl)
|
||
{
|
||
if (workingRes->re_thisEl->rr_connection1 == node &&
|
||
(workingRes->re_thisEl->rr_status & RES_TDI_IGNORE) == 0)
|
||
{
|
||
ResCalculateTDi(workingRes->re_thisEl->rr_connection2,
|
||
workingRes->re_thisEl,
|
||
workingRes->re_thisEl->rr_value);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
*-------------------------------------------------------------------------
|
||
*
|
||
* ResPruneTree-- Designed to be run just after ResCalculateTDi to prune all
|
||
* branches off the tree whose end node value of Tdi is less than the
|
||
* tolerance. This eliminates many resistors in nets with high fanout.
|
||
*
|
||
* Results: none
|
||
*
|
||
* Side Effects: May Eliminate Resistors and Merge Nodes
|
||
*
|
||
*-------------------------------------------------------------------------
|
||
*/
|
||
|
||
void
|
||
ResPruneTree(node,minTdi,nodelist1,nodelist2,resistorlist)
|
||
resNode *node,**nodelist1,**nodelist2;
|
||
float minTdi;
|
||
resResistor **resistorlist;
|
||
|
||
{
|
||
resResistor *currentRes;
|
||
resElement *current;
|
||
|
||
current = node->rn_re;
|
||
while(current != NULL)
|
||
{
|
||
currentRes = current->re_thisEl;
|
||
current = current->re_nextEl;
|
||
/* if branch points outward, call routine on subtrees */
|
||
if (currentRes->rr_connection1 == node)
|
||
{
|
||
ResPruneTree(currentRes->rr_connection2, minTdi,nodelist1,nodelist2,resistorlist);
|
||
}
|
||
}
|
||
/* We eliminate this branch if */
|
||
/* 1. It is a terminal node, i.e. it is the connected */
|
||
/* to only one resistor. */
|
||
/* 2. The direction of this resistor is toward the node */
|
||
/* (This prevents the root from being eliminated */
|
||
/* 3. The time constant TDI is less than the tolerance. */
|
||
if (node->rn_re != NULL &&
|
||
node->rn_re->re_nextEl == NULL &&
|
||
node->rn_re->re_thisEl->rr_connection2 == node)
|
||
{
|
||
if (node->rn_client == (ClientData)NULL)
|
||
{
|
||
TxError("Internal Error in Tree Pruning: Missing TDi value.\n");
|
||
}
|
||
else if (((RCDelayStuff *)(node->rn_client))->rc_Tdi < minTdi)
|
||
{
|
||
currentRes = node->rn_re->re_thisEl;
|
||
ResDeleteResPointer(currentRes->rr_connection1,currentRes);
|
||
ResDeleteResPointer(currentRes->rr_connection2,currentRes);
|
||
ResMergeNodes(currentRes->rr_connection1,currentRes->rr_connection2,nodelist2,nodelist1); /* Patched 7/5/94 */
|
||
ResEliminateResistor(currentRes,resistorlist);
|
||
}
|
||
}
|
||
}
|
||
|
||
int
|
||
ResDoSimplify(tolerance,rctol,goodies)
|
||
float tolerance;
|
||
float rctol;
|
||
ResGlobalParams *goodies;
|
||
|
||
{
|
||
resNode *node,*slownode;
|
||
float bigres = 0;
|
||
float millitolerance;
|
||
float totalcap;
|
||
resResistor *res;
|
||
|
||
|
||
resRemoveLoops = FALSE;
|
||
ResSetPathRes();
|
||
for (node = ResNodeList; node != NULL; node = node->rn_more)
|
||
{
|
||
bigres = MAX(bigres,node->rn_noderes);
|
||
}
|
||
|
||
bigres /= OHMSTOMILLIOHMS; /* convert from milliohms to ohms */
|
||
goodies->rg_maxres = bigres;
|
||
|
||
#ifdef PARANOID
|
||
ResSanityChecks("ExtractSingleNet",ResResList,ResNodeList,ResDevList);
|
||
#endif
|
||
|
||
/* Is extracted network still greater than the tolerance? */
|
||
/* Even if it isn't, we still let the next section run if */
|
||
/* we're calculating lumped values so that the capacitance */
|
||
/* values get calculated correctly. */
|
||
|
||
(void) ResDistributeCapacitance(ResNodeList,goodies->rg_nodecap);
|
||
|
||
if ((tolerance > bigres || (ResOptionsFlags &ResOpt_Simplify)==0) &&
|
||
(ResOptionsFlags &ResOpt_DoLumpFile)==0)
|
||
{
|
||
return(0);
|
||
}
|
||
res = ResResList;
|
||
while (res)
|
||
{
|
||
resResistor *oldres = res;
|
||
|
||
res = res->rr_nextResistor;
|
||
oldres->rr_status &= ~RES_HEAP;
|
||
if (oldres->rr_status & RES_TDI_IGNORE)
|
||
{
|
||
ResDeleteResPointer(oldres->rr_node[0],oldres);
|
||
ResDeleteResPointer(oldres->rr_node[1],oldres);
|
||
ResEliminateResistor(oldres,&ResResList);
|
||
}
|
||
}
|
||
|
||
if (ResOptionsFlags & ResOpt_Tdi)
|
||
{
|
||
if (goodies->rg_nodecap != -1 &&
|
||
(totalcap = ResCalculateChildCapacitance(ResOriginNode)) != -1)
|
||
{
|
||
RCDelayStuff *rc = (RCDelayStuff *) ResNodeList->rn_client;
|
||
|
||
goodies->rg_nodecap = totalcap;
|
||
ResCalculateTDi(ResOriginNode,(resResistor *)NULL,
|
||
goodies->rg_bigdevres);
|
||
if (rc != (RCDelayStuff *)NULL)
|
||
goodies->rg_Tdi = rc->rc_Tdi;
|
||
else
|
||
goodies->rg_Tdi = 0;
|
||
|
||
slownode = ResNodeList;
|
||
for (node = ResNodeList; node != NULL; node = node->rn_more)
|
||
{
|
||
rc = (RCDelayStuff *)node->rn_client;
|
||
if (rc && (goodies->rg_Tdi < rc->rc_Tdi))
|
||
{
|
||
slownode = node;
|
||
goodies->rg_Tdi = rc->rc_Tdi;
|
||
}
|
||
}
|
||
slownode->rn_status |= RN_MAXTDI;
|
||
}
|
||
else
|
||
{
|
||
goodies->rg_Tdi = -1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
goodies->rg_Tdi = 0;
|
||
}
|
||
if ((rctol+1)*goodies->rg_bigdevres*goodies->rg_nodecap >
|
||
rctol*goodies->rg_Tdi &&
|
||
(ResOptionsFlags & ResOpt_Tdi) &&
|
||
goodies->rg_Tdi != -1)
|
||
{
|
||
return(0);
|
||
}
|
||
|
||
/* Simplify network; resistors are still in milliohms, so use
|
||
millitolerance.
|
||
*/
|
||
|
||
if (ResOptionsFlags & ResOpt_Simplify)
|
||
{
|
||
|
||
millitolerance = tolerance*MILLIOHMSPEROHM;
|
||
|
||
/*
|
||
Start simplification at driver (R=0). Remove it from the done list
|
||
and add it to the pending list. Call ResSimplifyNet as long as nodes
|
||
remain in the pending list.
|
||
*/
|
||
for (node = ResNodeList; node != NULL ;node = node->rn_more)
|
||
{
|
||
if (node->rn_noderes == 0)
|
||
{
|
||
ResOriginNode = node;
|
||
}
|
||
node->rn_status |= FINISHED;
|
||
}
|
||
if (ResOriginNode != NULL)
|
||
{
|
||
|
||
/* if Tdi is enabled, prune all branches whose end nodes have */
|
||
/* time constants less than the tolerance. */
|
||
|
||
if ((ResOptionsFlags & ResOpt_Tdi) &&
|
||
goodies->rg_Tdi != -1 &&
|
||
rctol != 0)
|
||
{
|
||
ResPruneTree(ResOriginNode,
|
||
(rctol+1)*goodies->rg_bigdevres*goodies->rg_nodecap/rctol,
|
||
&ResNodeList,&ResNodeQueue,&ResResList);
|
||
}
|
||
ResOriginNode->rn_status &= ~MARKED;
|
||
if (ResOriginNode->rn_less == NULL)
|
||
{
|
||
ResNodeList = ResOriginNode->rn_more;
|
||
}
|
||
else
|
||
{
|
||
ResOriginNode->rn_less->rn_more = ResOriginNode->rn_more;
|
||
}
|
||
if (ResOriginNode->rn_more != NULL)
|
||
{
|
||
ResOriginNode->rn_more->rn_less = ResOriginNode->rn_less;
|
||
}
|
||
ResOriginNode->rn_more = NULL;
|
||
ResOriginNode->rn_less = NULL;
|
||
ResNodeQueue = ResOriginNode;
|
||
while (ResNodeQueue != NULL)
|
||
{
|
||
ResSimplifyNet(&ResNodeQueue,&ResNodeList,&ResResList,millitolerance);
|
||
}
|
||
|
||
/*
|
||
Call ResScrunchNet to eliminate any remaining under tolerance
|
||
resistors.
|
||
*/
|
||
ResScrunchNet(&ResResList,&ResNodeQueue,&ResNodeList,millitolerance);
|
||
}
|
||
}
|
||
return(0);
|
||
|
||
}
|
||
|
||
void
|
||
ResSetPathRes()
|
||
|
||
{
|
||
HeapEntry he;
|
||
resNode *node;
|
||
static int init=1;
|
||
|
||
if (init)
|
||
{
|
||
init = 0;
|
||
HeapInit(&ResistorHeap,128,FALSE,FALSE);
|
||
}
|
||
|
||
for (node = ResNodeList; node != NULL ;node = node->rn_more)
|
||
{
|
||
if (node->rn_noderes == 0)
|
||
{
|
||
ResOriginNode = node;
|
||
node->rn_status |= FINISHED;
|
||
}
|
||
else
|
||
{
|
||
node->rn_noderes = RES_INFINITY;
|
||
node->rn_status &= ~FINISHED;
|
||
}
|
||
}
|
||
if (ResOriginNode == NULL)
|
||
{
|
||
resDevice *res = ResGetDevice(gparams.rg_devloc);
|
||
ResOriginNode = res->rd_fet_source;
|
||
ResOriginNode->rn_why = RES_NODE_ORIGIN;
|
||
ResOriginNode->rn_noderes = 0;
|
||
}
|
||
ASSERT(ResOriginNode != NULL,"ResDoSimplify");
|
||
resPathNode(ResOriginNode);
|
||
while (HeapRemoveTop(&ResistorHeap,&he))
|
||
{
|
||
resPathRes((resResistor *) he.he_id);
|
||
}
|
||
}
|
||
|
||
void
|
||
resPathNode(node)
|
||
resNode *node;
|
||
|
||
{
|
||
resElement *re;
|
||
|
||
node->rn_status |= FINISHED;
|
||
for (re = node->rn_re;re;re=re->re_nextEl)
|
||
{
|
||
resResistor *res = re->re_thisEl;
|
||
resNode *node2;
|
||
|
||
if (res->rr_status & RES_HEAP) continue;
|
||
if ((node2=res->rr_node[0]) == node) node2 = res->rr_node[1];
|
||
if ((node2->rn_status & FINISHED) == 0)
|
||
{
|
||
HeapAddInt(&ResistorHeap, node->rn_noderes + res->rr_value,
|
||
(char *)res);
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
resPathRes(res)
|
||
resResistor *res;
|
||
|
||
{
|
||
resNode *node0,*node1;
|
||
int flag0,flag1;
|
||
|
||
res->rr_status |= RES_HEAP;
|
||
res->rr_status &= ~RES_MARKED;
|
||
node0 = res->rr_node[0];
|
||
node1 = res->rr_node[1];
|
||
flag0 = node0->rn_status & FINISHED;
|
||
flag1 = node1->rn_status & FINISHED;
|
||
if (flag0 && flag1)
|
||
{
|
||
res->rr_status |= RES_TDI_IGNORE;
|
||
if (resRemoveLoops)
|
||
{
|
||
ResDeleteResPointer(node0,res);
|
||
ResDeleteResPointer(node1,res);
|
||
ResEliminateResistor(res,&ResResList);
|
||
}
|
||
}
|
||
else if (flag0)
|
||
{
|
||
node1->rn_noderes = node0->rn_noderes+res->rr_value;
|
||
resPathNode(node1);
|
||
}
|
||
else
|
||
{
|
||
ASSERT(flag1,"ResPathRes");
|
||
res->rr_node[0] = node1;
|
||
res->rr_node[1] = node0;
|
||
node0->rn_noderes = node1->rn_noderes+res->rr_value;
|
||
resPathNode(node0);
|
||
}
|
||
}
|