1133 lines
31 KiB
C
1133 lines
31 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;
|
|
|
|
/* 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;
|
|
int 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_subs == node1)
|
|
device->rd_fet_subs = 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");
|
|
|
|
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;
|
|
myC->rc_Tdi = 0.0;
|
|
|
|
/* get capacitance for all connected gates */
|
|
for (tptr = me->rn_te; tptr != NULL; tptr = tptr->te_nextt)
|
|
{
|
|
dev = tptr->te_thist;
|
|
/* Hack for non-Manhattan geometry. Only one side of a split */
|
|
/* tile should correspond to a device type. */
|
|
if (IsSplit(dev->rd_tile))
|
|
{
|
|
t = TiGetLeftType(dev->rd_tile);
|
|
if (ExtCurStyle->exts_device[t] == NULL)
|
|
t = TiGetRightType(dev->rd_tile);
|
|
}
|
|
else
|
|
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;
|
|
/* Ignore previously found loops */
|
|
if (!(currentRes->rr_status & RES_TDI_IGNORE))
|
|
/* 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);
|
|
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;
|
|
|
|
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;
|
|
|
|
/*------ NOTE: resistors marked with RES_TDI_IGNORE are
|
|
* part of loops but should NOT be removed.
|
|
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 != NULL) && (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, gparams.rg_ttype);
|
|
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);
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
* resPathNode ---
|
|
*
|
|
* Given node "node", add every resistor connected to the node, and
|
|
* for which the node on the other side has not been processed, to
|
|
* the heap. Node is marked with FINISHED to prevent going 'round
|
|
* and 'round loops.
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/*
|
|
*-------------------------------------------------------------------------
|
|
*
|
|
* resPathRes ---
|
|
*
|
|
* Given resistor "res" pulled from the heap (in heap order, which
|
|
* is the highest valued resistor currently in the heap):
|
|
*
|
|
* If both ends of the resistor have been processed, then we have
|
|
* a loop, and should return.
|
|
*
|
|
* Otherwise, call resPathNode() on the node of whichever end of
|
|
* the resistor has not yet been processed, thus continuing through
|
|
* the path from the origin.
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
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;
|
|
/* Loop found---return without calling resPathNode() */
|
|
}
|
|
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);
|
|
}
|
|
}
|