Made various updates and corrections to the "extresist" code in

support of devices with terminals on different plances, such as
capacitors, diodes, and bipolar transistors.  Output now appears
to give meaningful results for flattened layouts, although
numerous issues remain for hierarchical layouts.
This commit is contained in:
Tim Edwards 2021-09-08 16:41:36 -04:00
parent 0ebdf3e513
commit d63a102515
9 changed files with 586 additions and 392 deletions

View File

@ -1 +1 @@
8.3.203
8.3.204

View File

@ -109,12 +109,43 @@ resAllPortNodes(tile, list)
}
}
/*
*--------------------------------------------------------------------------
*
* ResMultiPlaneFunc---
*
* If device is found overlapping one of its source/drain types, then
* generate a new device at the center of the tile and add to ResNodeQueue.
*
* Results:
* Always 0 to keep the search going.
*
* Side effects:
* Adds to ResNodeQueue
*
*--------------------------------------------------------------------------
*/
int
ResMultiPlaneFunc(tile, tpptr)
Tile *tile, **tpptr;
{
Tile *tp = *tpptr;
int xj, yj;
xj = (LEFT(tile) + RIGHT(tile)) / 2;
yj = (TOP(tile) + BOTTOM(tile)) / 2;
ResNewSDDevice(tp, tile, xj, yj, OTHERPLANE, &ResNodeQueue);
return 0;
}
/*
*--------------------------------------------------------------------------
*
* ResEachTile--for each tile, make a list of all possible current sources/
* sinks including contacts, devices, and junctions. Once this
* list is made, calculate the resistor nextwork for the tile.
* list is made, calculate the resistor network for the tile.
*
* Results: returns TRUE or FALSE depending on whether a node was
* involved in a merge.
@ -197,35 +228,6 @@ ResEachTile(tile, startpoint)
}
}
#ifdef ARIEL
if (i = ExtCurStyle->exts_plugSignalNum[t1])
{
tcell = (tElement *) mallocMagic((unsigned)(sizeof(tElement)));
tcell->te_thist= ResImageAddPlug(tile, i, resCurrentNode);
tcell->te_nextt = resCurrentNode->rn_te;
resCurrentNode->rn_te = tcell;
}
if (TTMaskHasType(&ResSubsTypeBitMask,t1) &&
(ResOptionsFlags & ResOpt_DoSubstrate))
{
int pNum;
Rect tileArea;
TileTypeBitMask *mask = &ExtCurStyle->exts_subsTransistorTypes[t1];
TiToRect(tile,&tileArea);
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
if (TTMaskIntersect(&DBPlaneTypes[pNum], mask))
{
(void)DBSrPaintArea((Tile *) NULL,
ResUse->cu_def->cd_planes[pNum],
&tileArea, mask, resSubDevFunc, (ClientData) tile);
}
}
}
#endif
/* Process all the contact points */
ce = tstructs->contactList;
while (ce != (cElement *) NULL)
@ -252,7 +254,7 @@ ResEachTile(tile, startpoint)
{
t2 = TiGetRightType(tp);
devptr = ExtCurStyle->exts_device[t2];
if(TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2) &&
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2) &&
TTMaskHasType(&(devptr->exts_deviceSDTypes[0]), t1))
/* found device */
{
@ -275,7 +277,7 @@ ResEachTile(tile, startpoint)
{
t2 = TiGetLeftType(tp);
devptr = ExtCurStyle->exts_device[t2];
if(TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2) &&
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2) &&
TTMaskHasType(&(devptr->exts_deviceSDTypes[0]), t1))
/* found device */
{
@ -298,13 +300,13 @@ ResEachTile(tile, startpoint)
{
t2 = TiGetBottomType(tp);
devptr = ExtCurStyle->exts_device[t2];
if(TTMaskHasType(&(ExtCurStyle->exts_deviceMask),t2) &&
TTMaskHasType(&(devptr->exts_deviceSDTypes[0]),t1))
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2) &&
TTMaskHasType(&(devptr->exts_deviceSDTypes[0]), t1))
/* found device */
{
yj = TOP(tile);
xj = (LEFT(tp)+RIGHT(tp))>>1;
ResNewSDDevice(tile,tp,xj,yj,BOTTOMEDGE, &ResNodeQueue);
ResNewSDDevice(tile, tp, xj, yj, BOTTOMEDGE, &ResNodeQueue);
}
if TTMaskHasType(&ExtCurStyle->exts_nodeConn[t1],t2)
/* tile is junction */
@ -320,7 +322,7 @@ ResEachTile(tile, startpoint)
{
t2 = TiGetTopType(tp);
devptr = ExtCurStyle->exts_device[t2];
if(TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2) &&
if (TTMaskHasType(&(ExtCurStyle->exts_deviceMask), t2) &&
TTMaskHasType(&(devptr->exts_deviceSDTypes[0]), t1))
/* found device */
{
@ -336,6 +338,38 @@ ResEachTile(tile, startpoint)
ResProcessJunction(tile, tp, xj, yj, &ResNodeQueue);
}
}
/* Check for source/drain on other planes (e.g., capacitors, bipolars, ...) */
if (TTMaskHasType(&ResSDTypesBitMask, t1))
{
Rect r;
int pNum;
TileTypeBitMask devMask;
TiToRect(tile, &r);
for (pNum = 0; pNum < DBNumPlanes; pNum++)
{
if (DBTypeOnPlane(t1, pNum)) continue;
/* NOTE: This is ridiculously inefficient and should be done
* in a different way.
*/
TTMaskZero(&devMask);
for (t2 = TT_TECHDEPBASE; t2 < DBNumUserLayers; t2++)
for (devptr = ExtCurStyle->exts_device[t2]; devptr;
devptr = devptr->exts_next)
for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
if (TTMaskHasType(&devptr->exts_deviceSDTypes[i], t1))
TTMaskSetType(&devMask, t2);
DBSrPaintArea((Tile *)NULL, ResUse->cu_def->cd_planes[pNum],
&r, &devMask, ResMultiPlaneFunc, (ClientData)&tile);
}
}
tstructs->tj_status |= RES_TILE_DONE;
resAllPortNodes(tile, &ResNodeQueue);
@ -361,31 +395,29 @@ ResEachTile(tile, startpoint)
int
resSubDevFunc(tile,tp)
Tile *tile,*tp;
Tile *tile, *tp;
{
tileJunk *junk = (tileJunk *)(tile->ti_client);
resNode *resptr;
tElement *tcell;
int x,y;
tileJunk *junk = (tileJunk *)(tile->ti_client);
resNode *resptr;
tElement *tcell;
int x, y;
if (junk->deviceList->rd_fet_subs == NULL)
{
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
junk->deviceList->rd_fet_subs = resptr;
junk->tj_status |= RES_TILE_DEV;
tcell = (tElement *) mallocMagic((unsigned)(sizeof(tElement)));
tcell->te_thist = junk->deviceList;
tcell->te_nextt = NULL;
x = (LEFT(tile)+RIGHT(tile))>>1;
y = (TOP(tile)+BOTTOM(tile))>>1;
if (junk->deviceList->rd_fet_subs == NULL)
{
resptr = (resNode *) mallocMagic((unsigned)(sizeof(resNode)));
junk->deviceList->rd_fet_subs = resptr;
junk->tj_status |= RES_TILE_DEV;
tcell = (tElement *) mallocMagic((unsigned)(sizeof(tElement)));
tcell->te_thist = junk->deviceList;
tcell->te_nextt = NULL;
x = (LEFT(tile) + RIGHT(tile)) >> 1;
y = (TOP(tile) + BOTTOM(tile)) >> 1;
InitializeNode(resptr,x,y,RES_NODE_JUNCTION);
resptr->rn_te = tcell;
ResAddToQueue(resptr,&ResNodeQueue);
InitializeNode(resptr, x, y, RES_NODE_JUNCTION);
resptr->rn_te = tcell;
ResAddToQueue(resptr, &ResNodeQueue);
NEWBREAK(resptr,tp,x,y,NULL);
NEWBREAK(resptr, tp, x, y, NULL);
}
return 0;
}

View File

@ -43,6 +43,7 @@ extern Tile *FindStartTile();
extern int ResEachTile();
extern int ResLaplaceTile();
extern ResSimNode *ResInitializeNode();
TileTypeBitMask ResSDTypesBitMask;
extern HashTable ResNodeTable;
@ -139,43 +140,33 @@ ResGetReCell()
*/
void
ResDissolveContacts(contacts)
ResContactPoint *contacts;
ResContactPoint *contacts;
{
TileType t, oldtype;
Tile *tp;
TileTypeBitMask residues;
for (; contacts != (ResContactPoint *) NULL; contacts = contacts->cp_nextcontact)
{
oldtype=contacts->cp_type;
#ifdef PARANOID
if (oldtype == TT_SPACE)
{
TxError("Error in Contact Dissolving for %s \n",ResCurrentNode);
}
#endif
DBFullResidueMask(oldtype, &residues);
DBErase(ResUse->cu_def, &(contacts->cp_rect), oldtype);
for (t = TT_TECHDEPBASE; t < DBNumTypes; t++)
{
if (TTMaskHasType(&residues, t))
{
if (TTMaskHasType(&ExtCurStyle->exts_deviceMask, t))
continue;
DBPaint(ResUse->cu_def, &(contacts->cp_rect), t);
}
}
tp = ResDef->cd_planes[DBPlane(contacts->cp_type)]->pl_hint;
GOTOPOINT(tp, &(contacts->cp_rect.r_ll));
#ifdef PARANOID
if (TiGetTypeExact(tp) == contacts->cp_type)
{
TxError("Error in Contact Preprocess Routines\n");
}
#endif
}
}
@ -494,7 +485,7 @@ ResProcessTiles(goodies, origin)
if ((j->tj_status & RES_TILE_DONE) == 0)
{
resCurrentNode = resptr2;
merged |= (*tilefunc)(tile,(Point *)NULL);
merged |= (*tilefunc)(tile, (Point *)NULL);
}
if (merged & ORIGIN) break;
}
@ -505,9 +496,9 @@ ResProcessTiles(goodies, origin)
/* Next, Process all contacts. */
for (workingc = resptr2->rn_ce;workingc != NULL;workingc = workingc->ce_nextc)
for (workingc = resptr2->rn_ce; workingc != NULL; workingc = workingc->ce_nextc)
{
ResContactPoint *cp = workingc->ce_thisc;
ResContactPoint *cp = workingc->ce_thisc;
if (merged & ORIGIN) break;
if (cp->cp_status == FALSE)
@ -627,7 +618,7 @@ ResCalcPerimOverlap(tile, dev)
if TTMaskHasType(omask, TiGetType(tp))
overlap += MIN(RIGHT(tile), RIGHT(tp)) - MAX(LEFT(tile), LEFT(tp));
}
dev->overlap = overlap;
dev->overlap += overlap;
}
/*
*-------------------------------------------------------------------------
@ -635,7 +626,7 @@ ResCalcPerimOverlap(tile, dev)
* resMakeDevFunc --
*
* Callback function from ResExtractNet. For each device in a node's
* device list pulled from the .sim file, find the tile corresponding
* device list pulled from the .sim file, find the tile(s) corresponding
* to the device in the source tree, and fill out the complete device
* record (namely the full device area).
*
@ -656,7 +647,6 @@ resMakeDevFunc(tile, cx)
TiToRect(tile, &devArea);
GeoTransRect(&cx->tc_scx->scx_trans, &devArea, &thisDev->area);
ResCalcPerimOverlap(tile, thisDev);
if (IsSplit(tile))
ttype = (SplitSide(tile)) ? SplitRightType(tile) : SplitLeftType(tile);
@ -675,10 +665,137 @@ resMakeDevFunc(tile, cx)
return 0; /* Completely different device? */
thisDev->type = ttype;
}
return 1;
}
/*
*-------------------------------------------------------------------------
*
* resExpandDevFunc --
*
* Do a boundary search on the first tile found in the search context
* path belonging to a device, including all tiles that belong to the
* device. For each compatible tile found, paint the device tile
* type into ResUse and calculate the overlap.
*
* Returns:
* 1 to stop the search (only the first tile of a device needs to be
* found).
*
* Side effects:
* Paints into ResUse and recalculates values of thisDev.
*-------------------------------------------------------------------------
*/
#define DEV_PROCESSED 1
int
resExpandDevFunc(tile, cx)
Tile *tile;
TreeContext *cx;
{
ResDevTile *thisDev = (ResDevTile *)cx->tc_filter->tf_arg;
static Stack *devExtentsStack = NULL;
static Stack *devResetStack = NULL;
TileTypeBitMask *rMask;
Tile *tp, *tp2;
TileType ttype;
int pNum;
Rect area;
pNum = DBPlane(thisDev->type);
if (devExtentsStack == NULL)
devExtentsStack = StackNew(8);
if (devResetStack == NULL)
devResetStack = StackNew(8);
tile->ti_client = (ClientData)DEV_PROCESSED;
STACKPUSH((ClientData)tile, devExtentsStack);
while (!StackEmpty(devExtentsStack))
{
tp = (Tile *) STACKPOP(devExtentsStack);
STACKPUSH((ClientData)tp, devResetStack);
TiToRect(tp, &area);
/* Paint type thisDev->type into ResUse over area of tile "tp" */
DBNMPaintPlane(ResUse->cu_def->cd_planes[pNum], TiGetTypeExact(tp),
&area, DBStdPaintTbl(thisDev->type, pNum), (PaintUndoInfo *)NULL);
/* Add source/drain perimeter overlap to the device for this tile */
ResCalcPerimOverlap(tp, thisDev);
/* Search boundary of the device tile for more tiles belonging */
/* to the device. If contacts are found, replace them with the */
/* device type. */
/* top */
for (tp2 = RT(tp); RIGHT(tp2) > LEFT(tp); tp2 = BL(tp2))
{
if (tp2->ti_client == (ClientData)DEV_PROCESSED) continue;
ttype = TiGetBottomType(tp2);
if ((ttype == thisDev->type) || (DBIsContact(ttype)
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
{
tp2->ti_client = (ClientData)DEV_PROCESSED;
STACKPUSH((ClientData)tp2, devExtentsStack);
}
}
/* bottom */
for (tp2 = LB(tp); LEFT(tp2) < RIGHT(tp); tp2 = TR(tp2))
{
if (tp2->ti_client == (ClientData)DEV_PROCESSED) continue;
ttype = TiGetTopType(tp2);
if ((ttype == thisDev->type) || (DBIsContact(ttype)
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
{
tp2->ti_client = (ClientData)DEV_PROCESSED;
STACKPUSH((ClientData)tp2, devExtentsStack);
}
}
/* right */
for (tp2 = TR(tp); TOP(tp2) > BOTTOM(tp); tp2 = LB(tp2))
{
if (tp2->ti_client == (ClientData)DEV_PROCESSED) continue;
ttype = TiGetLeftType(tp2);
if ((ttype == thisDev->type) || (DBIsContact(ttype)
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
{
tp2->ti_client = (ClientData)DEV_PROCESSED;
STACKPUSH((ClientData)tp2, devExtentsStack);
}
}
/* left */
for (tp2 = BL(tp); BOTTOM(tp2) < TOP(tp); tp2 = RT(tp2))
{
if (tp2->ti_client == (ClientData)DEV_PROCESSED) continue;
ttype = TiGetRightType(tp2);
if ((ttype == thisDev->type) || (DBIsContact(ttype)
&& TTMaskHasType(DBResidueMask(ttype), thisDev->type)))
{
tp2->ti_client = (ClientData)DEV_PROCESSED;
STACKPUSH((ClientData)tp2, devExtentsStack);
}
}
}
/* Reset the device tile client records */
while (!StackEmpty(devResetStack))
{
tp = (Tile *) STACKPOP(devResetStack);
tp->ti_client = (ClientData)CLIENTDEFAULT;
}
/* Return 1 to stop the search; we only need to run this from */
/* the first device tile. */
return 1;
}
/*
*-------------------------------------------------------------------------
*
@ -700,14 +817,16 @@ ResExtractNet(node, goodies, cellname)
char *cellname;
{
SearchContext scx;
int pNum;
TileTypeBitMask FirstTileMask;
TileTypeBitMask tMask;
Point startpoint;
static int first = 1;
ResDevTile *DevTiles, *thisDev;
ResFixPoint *fix;
devPtr *tptr;
int pNum;
int resMakeDevFunc();
int resExpandDevFunc();
/* Make sure all global network variables are reset */
@ -783,15 +902,20 @@ ResExtractNet(node, goodies, cellname)
DBTreeCopyConnect(&scx, &FirstTileMask, 0, ResCopyMask, &TiPlaneRect,
SEL_DO_LABELS, ResUse);
TTMaskZero(&ResSDTypesBitMask);
/* Add devices to ResUse from list in node */
DevTiles = NULL;
for (tptr = node->firstDev; tptr; tptr = tptr->nextDev)
{
int result;
int i;
ExtDevice *devptr;
thisDev = (ResDevTile *)mallocMagic(sizeof(ResDevTile));
thisDev->devptr = tptr->thisDev->rs_devptr;
thisDev->type = tptr->thisDev->rs_ttype;
thisDev->overlap = 0;
scx.scx_area.r_ll.p_x = tptr->thisDev->location.p_x;
scx.scx_area.r_ll.p_y = tptr->thisDev->location.p_y;
scx.scx_area.r_xtop = scx.scx_area.r_xbot + 1;
@ -810,10 +934,18 @@ ResExtractNet(node, goodies, cellname)
thisDev->nextDev = DevTiles;
DevTiles = thisDev;
/* Paint the type into ResUse */
pNum = DBPlane(thisDev->type);
DBPaintPlane(ResUse->cu_def->cd_planes[pNum], &thisDev->area,
DBStdPaintTbl(thisDev->type, pNum), (PaintUndoInfo *)NULL);
/* Paint the entire device into ResUse */
TTMaskSetOnlyType(&tMask, thisDev->type);
DBTreeSrTiles(&scx, &tMask, 0, resExpandDevFunc, (ClientData)thisDev);
/* If the device has source/drain types in a different plane than */
/* the device identifier type, then add the source/drain types to */
/* the mask ResSDTypesBitMask. */
for (devptr = ExtCurStyle->exts_device[thisDev->type]; devptr;
devptr = devptr->exts_next)
for (i = 0; !TTMaskIsZero(&devptr->exts_deviceSDTypes[i]); i++)
TTMaskSetMask(&ResSDTypesBitMask, &devptr->exts_deviceSDTypes[i]);
}
DBReComputeBbox(ResUse->cu_def);

View File

@ -46,7 +46,6 @@ bool ResCalcEastWest();
* Side Effects: Resistor structures are produced. Some nodes may be
* eliminated.
*--------------------------------------------------------------------------
*
*/
bool
@ -136,7 +135,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
height = TOP(tile) - BOTTOM(tile);
/*
* One Breakpoint? No resistors need to be made. Free up the first
* One Breakpoint? No resistors need to be made. Free up the first
* breakpoint, then return.
*/
@ -149,7 +148,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
return(merged);
}
/* re-sort nodes left to right. */
/* Re-sort nodes left to right. */
ResSortBreaks(&junk->breakList, TRUE);
@ -160,9 +159,9 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
p2= junk->breakList;
/* add extra left area to leftmost node */
/* Add extra left area to leftmost node */
p2->br_this->rn_float.rn_area += height*(p2->br_loc.p_x-LEFT(tile));
p2->br_this->rn_float.rn_area += height * (p2->br_loc.p_x - LEFT(tile));
while (p2->br_next != NULL)
{
p1 = p2;
@ -179,7 +178,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
else if (p2->br_this == resCurrentNode)
{
currNode = p1->br_this;
ResMergeNodes(p2->br_this,p1->br_this,pendingList,doneList);
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
merged = TRUE;
freeMagic((char *)p1);
}
@ -187,7 +186,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
{
currNode = p2->br_this;
p1->br_next = p2->br_next;
ResMergeNodes(p1->br_this,p2->br_this,pendingList,doneList);
ResMergeNodes(p1->br_this, p2->br_this, pendingList, doneList);
merged = TRUE;
freeMagic((char *)p2);
p2 = p1;
@ -195,7 +194,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
else
{
currNode = p1->br_this;
ResMergeNodes(p2->br_this,p1->br_this,pendingList,doneList);
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
freeMagic((char *)p1);
}
@ -203,13 +202,13 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
* Was the node used in another junk or breakpoint?
* If so, replace the old node with the new one.
*/
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
{
p3->br_this = p2->br_this;
}
p3 = p3->br_next;
}
}
@ -221,18 +220,18 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
else
{
resistor = (resResistor *) mallocMagic((unsigned) (sizeof(resResistor)));
resistor = (resResistor *)mallocMagic((unsigned)sizeof(resResistor));
resistor->rr_nextResistor = (*resList);
resistor->rr_lastResistor = NULL;
if ((*resList) != NULL) (*resList)->rr_lastResistor = resistor;
(*resList) = resistor;
resistor->rr_connection1 = p1->br_this;
resistor->rr_connection2 = p2->br_this;
element = (resElement *) mallocMagic((unsigned) (sizeof(resElement)));
element = (resElement *)mallocMagic((unsigned)sizeof(resElement));
element->re_nextEl = p1->br_this->rn_re;
element->re_thisEl = resistor;
p1->br_this->rn_re = element;
element = (resElement *) mallocMagic((unsigned) (sizeof(resElement)));
element = (resElement *)mallocMagic((unsigned)sizeof(resElement));
element->re_nextEl = p2->br_this->rn_re;
element->re_thisEl = resistor;
p2->br_this->rn_re = element;
@ -246,7 +245,6 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
resistor->rr_status = RES_DIAGONAL;
resistor->rr_status |= (SplitDirection(tile)) ? RES_NS
: RES_EW;
}
else
{
@ -268,10 +266,11 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
freeMagic((char *)p1);
}
}
p2->br_this->rn_float.rn_area += height * (RIGHT(tile) - p2->br_loc.p_x);
freeMagic((char *)p2);
junk->breakList = NULL;
return(merged);
return merged;
}
@ -303,7 +302,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
tileJunk *junk = (tileJunk *)tile->ti_client;
merged = FALSE;
width = RIGHT(tile)-LEFT(tile);
width = RIGHT(tile) - LEFT(tile);
/*
* One Breakpoint? No resistors need to be made. Free up the first
@ -319,7 +318,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
return(merged);
}
/* re-sort nodes south to north. */
/* Re-sort nodes south to north. */
ResSortBreaks(&junk->breakList, FALSE);
/*
@ -329,7 +328,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
p2 = junk->breakList;
/* add extra left area to leftmost node */
/* Add extra left area to leftmost node */
p2->br_this->rn_float.rn_area += width * (p2->br_loc.p_y - BOTTOM(tile));
while (p2->br_next != NULL)
@ -348,7 +347,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
else if (p2->br_this == resCurrentNode)
{
currNode = p1->br_this;
ResMergeNodes(p2->br_this,p1->br_this,pendingList,doneList);
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
freeMagic((char *)p1);
merged = TRUE;
}
@ -356,7 +355,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
{
currNode = p2->br_this;
p1->br_next = p2->br_next;
ResMergeNodes(p1->br_this,p2->br_this,pendingList,doneList);
ResMergeNodes(p1->br_this, p2->br_this, pendingList, doneList);
merged = TRUE;
freeMagic((char *)p2);
p2 = p1;
@ -364,7 +363,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
else
{
currNode = p1->br_this;
ResMergeNodes(p2->br_this,p1->br_this,pendingList,doneList);
ResMergeNodes(p2->br_this, p1->br_this, pendingList, doneList);
freeMagic((char *)p1);
}
@ -372,13 +371,12 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
* Was the node used in another junk or breakpoint?
* If so, replace the old node with the new one.
*/
p3 = p2->br_next;
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
{
p3->br_this = p2->br_this;
}
p3 = p3->br_next;
}
}
@ -426,7 +424,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
#endif
resistor->rr_value =
(ExtCurStyle->exts_sheetResist[resistor->rr_tt]
* (p2->br_loc.p_y-p1->br_loc.p_y)) / width;
* (p2->br_loc.p_y - p1->br_loc.p_y)) / width;
rArea = ((p2->br_loc.p_y-p1->br_loc.p_y) * width) / 2;
resistor->rr_connection1->rn_float.rn_area += rArea;
resistor->rr_connection2->rn_float.rn_area += rArea;
@ -466,301 +464,294 @@ ResCalcNearDevice(tile, pendingList, doneList, resList)
resResistor **resList;
{
bool merged;
int devcount,devedge,deltax,deltay;
Breakpoint *p1,*p2,*p3;
tileJunk *junk = (tileJunk *)tile->ti_client;
bool merged;
int devcount, devedge, deltax, deltay;
Breakpoint *p1, *p2, *p3;
tileJunk *junk = (tileJunk *)tile->ti_client;
merged = FALSE;
merged = FALSE;
/*
One Breakpoint? No resistors need to be made. Free up the first
breakpoint, then return.
/*
* One Breakpoint? No resistors need to be made. Free up the first
* breakpoint, then return.
*/
if (junk->breakList->br_next == NULL)
{
freeMagic((char *)junk->breakList);
junk->breakList = NULL;
return(merged);
}
/* count the number of device breakpoints */
/* mark which edge they connect to */
devcount = 0;
devedge = 0;
for (p1=junk->breakList; p1 != NULL;p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE)
{
devcount++;
if (p1->br_loc.p_x == LEFT(tile)) devedge |= LEFTEDGE;
else if (p1->br_loc.p_x == RIGHT(tile)) devedge |= RIGHTEDGE;
else if (p1->br_loc.p_y == TOP(tile)) devedge |= TOPEDGE;
else if (p1->br_loc.p_y == BOTTOM(tile)) devedge |= BOTTOMEDGE;
}
}
/* use distance from device to next breakpoint as determinant */
/* if there is only one device or if all the devices are along */
/* the same edge. */
if (devcount == 1 ||
if (junk->breakList->br_next == NULL)
{
freeMagic((char *)junk->breakList);
junk->breakList = NULL;
return(merged);
}
/* Count the number of device breakpoints */
/* Mark which edge they connect to */
devcount = 0;
devedge = 0;
for (p1 = junk->breakList; p1 != NULL; p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE)
{
devcount++;
if (p1->br_loc.p_x == LEFT(tile)) devedge |= LEFTEDGE;
else if (p1->br_loc.p_x == RIGHT(tile)) devedge |= RIGHTEDGE;
else if (p1->br_loc.p_y == TOP(tile)) devedge |= TOPEDGE;
else if (p1->br_loc.p_y == BOTTOM(tile)) devedge |= BOTTOMEDGE;
}
}
/* Use distance from device to next breakpoint as determinant */
/* If there is only one device or if all the devices are along */
/* the same edge. */
if (devcount == 1 ||
(devedge & LEFTEDGE) == devedge ||
(devedge & RIGHTEDGE) == devedge ||
(devedge & RIGHTEDGE) == devedge ||
(devedge & TOPEDGE) == devedge ||
(devedge & BOTTOMEDGE) == devedge)
{
ResSortBreaks(&junk->breakList,TRUE);
p2 = NULL;
for (p1=junk->breakList; p1 != NULL;p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE)
{
break;
}
if (p1->br_next != NULL &&
{
ResSortBreaks(&junk->breakList,TRUE);
p2 = NULL;
for (p1 = junk->breakList; p1 != NULL; p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE)
break;
if (p1->br_next != NULL &&
(p1->br_loc.p_x != p1->br_next->br_loc.p_x ||
p1->br_loc.p_y != p1->br_next->br_loc.p_y))
{
p2 = p1;
}
}
deltax=INFINITY;
for (p3 = p1->br_next;
p3 != NULL &&
p2 = p1;
}
deltax = INFINITY;
for (p3 = p1->br_next; p3 != NULL &&
p3->br_loc.p_x == p1->br_loc.p_x &&
p3->br_loc.p_y == p1->br_loc.p_y; p3 = p3->br_next);
if (p3 != NULL)
{
if (p3->br_crect)
{
if (p3->br_crect->r_ll.p_x > p1->br_loc.p_x)
{
deltax = p3->br_crect->r_ll.p_x-p1->br_loc.p_x;
}
else if (p3->br_crect->r_ur.p_x < p1->br_loc.p_x)
{
deltax = p1->br_loc.p_x-p3->br_crect->r_ur.p_x;
}
else
{
deltax=0;
}
}
else
{
deltax = abs(p1->br_loc.p_x-p3->br_loc.p_x);
}
}
if (p2 != NULL)
{
if (p2->br_crect)
{
if (p2->br_crect->r_ll.p_x > p1->br_loc.p_x)
{
deltax = MIN(deltax,p2->br_crect->r_ll.p_x-p1->br_loc.p_x);
}
else if (p2->br_crect->r_ur.p_x < p1->br_loc.p_x)
{
deltax = MIN(deltax,p1->br_loc.p_x-p2->br_crect->r_ur.p_x);
}
else
{
deltax=0;
}
}
else
{
deltax = MIN(deltax,abs(p1->br_loc.p_x-p2->br_loc.p_x));
}
}
if (p3 != NULL)
{
if (p3->br_crect)
{
if (p3->br_crect->r_ll.p_x > p1->br_loc.p_x)
{
deltax = p3->br_crect->r_ll.p_x - p1->br_loc.p_x;
}
else if (p3->br_crect->r_ur.p_x < p1->br_loc.p_x)
{
deltax = p1->br_loc.p_x - p3->br_crect->r_ur.p_x;
}
else
{
deltax = 0;
}
}
else
{
deltax = abs(p1->br_loc.p_x - p3->br_loc.p_x);
}
}
if (p2 != NULL)
{
if (p2->br_crect)
{
if (p2->br_crect->r_ll.p_x > p1->br_loc.p_x)
{
deltax = MIN(deltax, p2->br_crect->r_ll.p_x - p1->br_loc.p_x);
}
else if (p2->br_crect->r_ur.p_x < p1->br_loc.p_x)
{
deltax = MIN(deltax, p1->br_loc.p_x - p2->br_crect->r_ur.p_x);
}
else
{
deltax = 0;
}
}
else
{
deltax = MIN(deltax, abs(p1->br_loc.p_x - p2->br_loc.p_x));
}
}
/* re-sort nodes south to north. */
ResSortBreaks(&junk->breakList,FALSE);
p2 = NULL;
for (p1=junk->breakList; p1 != NULL;p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE)
{
break;
}
if (p1->br_next != NULL &&
/* Re-sort nodes south to north. */
ResSortBreaks(&junk->breakList, FALSE);
p2 = NULL;
for (p1 = junk->breakList; p1 != NULL; p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE)
{
break;
}
if (p1->br_next != NULL &&
(p1->br_loc.p_x != p1->br_next->br_loc.p_x ||
p1->br_loc.p_y != p1->br_next->br_loc.p_y))
{
p2 = p1;
}
}
deltay=INFINITY;
for (p3 = p1->br_next;
p3 != NULL &&
{
p2 = p1;
}
}
deltay = INFINITY;
for (p3 = p1->br_next; p3 != NULL &&
p3->br_loc.p_x == p1->br_loc.p_x &&
p3->br_loc.p_y == p1->br_loc.p_y; p3 = p3->br_next);
if (p3 != NULL)
{
if (p3->br_crect)
{
if (p3->br_crect->r_ll.p_y > p1->br_loc.p_y)
{
deltay = p3->br_crect->r_ll.p_y-p1->br_loc.p_y;
}
else if (p3->br_crect->r_ur.p_y < p1->br_loc.p_y)
{
deltay = p1->br_loc.p_y-p3->br_crect->r_ur.p_y;
}
else
{
deltay=0;
}
}
else
{
deltay = abs(p1->br_loc.p_y-p3->br_loc.p_y);
}
}
if (p2!= NULL)
{
if (p2->br_crect)
{
if (p2->br_crect->r_ll.p_y > p1->br_loc.p_y)
{
deltay = MIN(deltay,p2->br_crect->r_ll.p_y-p1->br_loc.p_y);
}
else if (p2->br_crect->r_ur.p_y < p1->br_loc.p_y)
{
deltay = MIN(deltay,p1->br_loc.p_y-p2->br_crect->r_ur.p_y);
}
else
{
deltay=0;
}
}
else
{
deltay = MIN(deltay,abs(p1->br_loc.p_y-p2->br_loc.p_y));
}
}
if (deltay > deltax)
{
return(ResCalcNorthSouth(tile,pendingList,doneList,resList));
}
else
{
return(ResCalcEastWest(tile,pendingList,doneList,resList));
}
if (p3 != NULL)
{
if (p3->br_crect)
{
if (p3->br_crect->r_ll.p_y > p1->br_loc.p_y)
{
deltay = p3->br_crect->r_ll.p_y - p1->br_loc.p_y;
}
else if (p3->br_crect->r_ur.p_y < p1->br_loc.p_y)
{
deltay = p1->br_loc.p_y - p3->br_crect->r_ur.p_y;
}
else
{
deltay=0;
}
}
else
{
deltay = abs(p1->br_loc.p_y - p3->br_loc.p_y);
}
}
if (p2 != NULL)
{
if (p2->br_crect)
{
if (p2->br_crect->r_ll.p_y > p1->br_loc.p_y)
{
deltay = MIN(deltay,p2->br_crect->r_ll.p_y - p1->br_loc.p_y);
}
else if (p2->br_crect->r_ur.p_y < p1->br_loc.p_y)
{
deltay = MIN(deltay,p1->br_loc.p_y - p2->br_crect->r_ur.p_y);
}
else
{
deltay=0;
}
}
else
{
deltay = MIN(deltay, abs(p1->br_loc.p_y - p2->br_loc.p_y));
}
}
if (deltay > deltax)
{
return ResCalcNorthSouth(tile, pendingList, doneList, resList);
}
else
{
return ResCalcEastWest(tile, pendingList, doneList, resList);
}
}
}
/* multiple devices connected to the partition */
else
{
if (devedge == 0)
{
TxError("Error in device current direction routine\n");
return(merged);
}
/* check to see if the current flow is north-south */
/* possible north-south conditions: */
/* 1. there are devices along the top and bottom edges */
/* but not along the left or right */
/* 2. there are devices along two sides at right angles, */
/* and the tile is wider than it is tall. */
/* Multiple devices connected to the partition */
if ((devedge & TOPEDGE) &&
(devedge & BOTTOMEDGE) &&
!(devedge & LEFTEDGE) &&
!(devedge & RIGHTEDGE) ||
else
{
if (devedge == 0)
{
TxError("Error in device current direction routine\n");
return(merged);
}
/* Check to see if the current flow is north-south */
/* Possible north-south conditions: */
/* 1. There are devices along the top and bottom edges */
/* but not along the left or right */
/* 2. There are devices along two sides at right angles, */
/* and the tile is wider than it is tall. */
if ((devedge & TOPEDGE) && (devedge & BOTTOMEDGE) &&
!(devedge & LEFTEDGE) && !(devedge & RIGHTEDGE) ||
(devedge & TOPEDGE || devedge & BOTTOMEDGE) &&
(devedge & LEFTEDGE || devedge & RIGHTEDGE) &&
RIGHT(tile)-LEFT(tile) > TOP(tile)-BOTTOM(tile))
{
/* re-sort nodes south to north. */
ResSortBreaks(&junk->breakList,FALSE);
(RIGHT(tile) - LEFT(tile)) > (TOP(tile) - BOTTOM(tile)))
{
/* re-sort nodes south to north. */
ResSortBreaks(&junk->breakList, FALSE);
/* eliminate duplicate S/D pointers */
for (p1 = junk->breakList; p1 != NULL; p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE &&
/* eliminate duplicate S/D pointers */
for (p1 = junk->breakList; p1 != NULL; p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE &&
(p1->br_loc.p_y == BOTTOM(tile) ||
p1->br_loc.p_y == TOP(tile)))
{
p3 = NULL;
p2 = junk->breakList;
while ( p2 != NULL)
{
if (p2->br_this == p1->br_this &&
p2 != p1 &&
{
p3 = NULL;
p2 = junk->breakList;
while (p2 != NULL)
{
if (p2->br_this == p1->br_this && p2 != p1 &&
p2->br_loc.p_y != BOTTOM(tile) &&
p2->br_loc.p_y != TOP(tile))
{
if (p3 == NULL)
{
junk->breakList = p2->br_next;
freeMagic((char *) p2);
p2 = junk->breakList;
}
else
{
p3->br_next = p2->br_next;
freeMagic((char *) p2);
p2 = p3->br_next;
}
}
else
{
p3 = p2;
p2 = p2->br_next;
}
{
if (p3 == NULL)
{
junk->breakList = p2->br_next;
freeMagic((char *) p2);
p2 = junk->breakList;
}
else
{
p3->br_next = p2->br_next;
freeMagic((char *) p2);
p2 = p3->br_next;
}
}
}
}
return(ResCalcNorthSouth(tile,pendingList,doneList,resList));
}
else
{
/* eliminate duplicate S/D pointers */
for (p1 = junk->breakList; p1 != NULL; p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE &&
else
{
p3 = p2;
p2 = p2->br_next;
}
}
}
}
return ResCalcNorthSouth(tile, pendingList, doneList, resList);
}
else
{
/* Eliminate duplicate S/D pointers */
for (p1 = junk->breakList; p1 != NULL; p1 = p1->br_next)
{
if (p1->br_this->rn_why == RES_NODE_DEVICE &&
(p1->br_loc.p_x == LEFT(tile) ||
p1->br_loc.p_x == RIGHT(tile)))
{
p3 = NULL;
p2 = junk->breakList;
while ( p2 != NULL)
{
if (p2->br_this == p1->br_this &&
p2 != p1 &&
{
p3 = NULL;
p2 = junk->breakList;
while (p2 != NULL)
{
if (p2->br_this == p1->br_this && p2 != p1 &&
p2->br_loc.p_x != LEFT(tile) &&
p2->br_loc.p_x != RIGHT(tile))
{
if (p3 == NULL)
{
junk->breakList = p2->br_next;
freeMagic((char *) p2);
p2 = junk->breakList;
}
else
{
p3->br_next = p2->br_next;
freeMagic((char *) p2);
p2 = p3->br_next;
}
}
else
{
p3 = p2;
p2 = p2->br_next;
}
{
if (p3 == NULL)
{
junk->breakList = p2->br_next;
freeMagic((char *) p2);
p2 = junk->breakList;
}
else
{
p3->br_next = p2->br_next;
freeMagic((char *) p2);
p2 = p3->br_next;
}
}
}
}
return(ResCalcEastWest(tile,pendingList,doneList,resList));
}
}
else
{
p3 = p2;
p2 = p2->br_next;
}
}
}
}
return ResCalcEastWest(tile, pendingList, doneList, resList);
}
}
}
/*
*-------------------------------------------------------------------------
*
@ -779,7 +770,7 @@ void
ResDoContacts(contact, nodes, resList)
ResContactPoint *contact;
resNode **nodes;
resResistor **resList;
resResistor **resList;
{
resNode *resptr;
cElement *ccell;
@ -799,19 +790,19 @@ ResDoContacts(contact, nodes, resList)
int y = contact->cp_center.p_y;
resptr = (resNode *) mallocMagic((unsigned) (sizeof(resNode)));
InitializeNode(resptr,x,y,RES_NODE_CONTACT);
ResAddToQueue(resptr,nodes);
InitializeNode(resptr, x, y, RES_NODE_CONTACT);
ResAddToQueue(resptr, nodes);
ccell = (cElement *) mallocMagic((unsigned) (sizeof(cElement)));
ccell->ce_nextc = resptr->rn_ce;
resptr->rn_ce = ccell;
ccell->ce_thisc = contact;
/* add 1 celement for each layer of contact */
/* Add 1 celement for each layer of contact */
for (tilenum=0; tilenum < contact->cp_currentcontact; tilenum++)
for (tilenum = 0; tilenum < contact->cp_currentcontact; tilenum++)
{
Tile *tile = contact->cp_tile[tilenum];
Tile *tile = contact->cp_tile[tilenum];
contact->cp_cnode[tilenum] = resptr;
NEWBREAK(resptr, tile, contact->cp_center.p_x,
@ -847,17 +838,17 @@ ResDoContacts(contact, nodes, resList)
squaresy = (int)squaresf;
squaresy++;
}
for (tilenum=0; tilenum < contact->cp_currentcontact; tilenum++)
for (tilenum = 0; tilenum < contact->cp_currentcontact; tilenum++)
{
int x = contact->cp_center.p_x;
int y = contact->cp_center.p_y;
Tile *tile = contact->cp_tile[tilenum];
resptr = (resNode *) mallocMagic((unsigned) (sizeof(resNode)));
InitializeNode(resptr,x,y,RES_NODE_CONTACT);
ResAddToQueue(resptr,nodes);
InitializeNode(resptr, x, y, RES_NODE_CONTACT);
ResAddToQueue(resptr, nodes);
/* add contact pointer to node */
/* Add contact pointer to node */
ccell = (cElement *) mallocMagic((unsigned) (sizeof(cElement)));
ccell->ce_nextc = resptr->rn_ce;
@ -868,7 +859,7 @@ ResDoContacts(contact, nodes, resList)
NEWBREAK(resptr, tile, contact->cp_center.p_x,
contact->cp_center.p_y, &contact->cp_rect);
/* add resistors here */
/* Add resistors here */
if (tilenum > 0)
{
@ -877,13 +868,13 @@ ResDoContacts(contact, nodes, resList)
resistor->rr_lastResistor = NULL;
if ((*resList) != NULL) (*resList)->rr_lastResistor = resistor;
(*resList) = resistor;
resistor->rr_connection1 = contact->cp_cnode[tilenum-1];
resistor->rr_connection1 = contact->cp_cnode[tilenum - 1];
resistor->rr_connection2 = contact->cp_cnode[tilenum];
element = (resElement *) mallocMagic((unsigned) (sizeof(resElement)));
element->re_nextEl = contact->cp_cnode[tilenum-1]->rn_re;
element->re_nextEl = contact->cp_cnode[tilenum - 1]->rn_re;
element->re_thisEl = resistor;
contact->cp_cnode[tilenum-1]->rn_re = element;
contact->cp_cnode[tilenum - 1]->rn_re = element;
element = (resElement *) mallocMagic((unsigned)(sizeof(resElement)));
element->re_nextEl = contact->cp_cnode[tilenum]->rn_re;
element->re_thisEl = resistor;
@ -909,7 +900,7 @@ ResDoContacts(contact, nodes, resList)
(squaresx * squaresy);
#endif
resistor->rr_tt = contact->cp_type;
resistor->rr_float.rr_area= 0;
resistor->rr_float.rr_area = 0;
resistor->rr_status = 0;
}
}
@ -919,6 +910,11 @@ ResDoContacts(contact, nodes, resList)
/*
*-------------------------------------------------------------------------
*
* ResSortBreaks --
*
* Results:
* None
*
*-------------------------------------------------------------------------
*/
@ -928,7 +924,7 @@ ResSortBreaks(masterlist, xsort)
int xsort;
{
Breakpoint *p1, *p2, *p3, *p4;
bool changed;
bool changed;
changed = TRUE;
while (changed == TRUE)

View File

@ -168,8 +168,10 @@ ResPrintExtDev(outextfile, devices)
if (devices->subs != NULL)
fprintf(outextfile, " \"%s\"", devices->subs->name);
else
else if (subsName != NULL)
fprintf(outextfile, " \"%s\"", subsName);
else
fprintf(outextfile, " \"None\"");
if (devices->gate != NULL)
fprintf(outextfile, " \"%s\" %d %s",

View File

@ -511,6 +511,8 @@ ResSimDevice(line, rpersquare, devptr)
device->gate = device->source = device->drain = device->subs = NULL;
device->rs_ttype = extGetDevType(devptr->exts_deviceName);
/* sim attributes look like g=a1,a2 */
/* ext attributes are "a1","a2" */
/* do conversion from one to the other here */

View File

@ -1288,7 +1288,7 @@ ResFixUpConnections(simDev, layoutDev, simNode, nodename)
}
else
TxError("missing SD connection\n");
TxError("Missing SD connection\n");
}
}
else if (simDev->drain == simNode)
@ -1335,7 +1335,7 @@ ResFixUpConnections(simDev, layoutDev, simNode, nodename)
}
}
else
TxError("missing SD connection\n");
TxError("Missing SD connection\n");
}
else
resNodeNum--;

View File

@ -87,6 +87,27 @@ ResFirst(tile, arg)
return((Region *) NULL);
}
/*
*--------------------------------------------------------------------------
*
* resMultiPlaneTerm --
*
* Callback function to set a junk field
*
*--------------------------------------------------------------------------
*/
int
resMultiPlaneTerm(Tile *tile, tileJunk *junk2)
{
tileJunk *Junk;
Junk = resAddField(tile);
Junk->tj_status |= RES_TILE_SD;
junk2->sourceEdge |= OTHERPLANE;
return 0;
}
/*
*--------------------------------------------------------------------------
*
@ -263,13 +284,22 @@ ResAddPlumbing(tile, arg)
break;
}
}
/* other plane (in ResUse) */
if (source == NULL)
{
int pNum;
Rect r;
TiToRect(tile, &r);
for (pNum = PL_TECHDEPBASE; pNum < DBNumPlanes; pNum++)
{
/* XXX */
if (TTMaskIntersect(&DBPlaneTypes[pNum],
&(devptr->exts_deviceSDTypes[i])))
DBSrPaintArea((Tile *)NULL,
ResUse->cu_def->cd_planes[pNum],
&r, &(devptr->exts_deviceSDTypes[i]),
resMultiPlaneTerm, (ClientData)junk2);
}
}
@ -303,7 +333,7 @@ ResAddPlumbing(tile, arg)
if (TiGetBottomType(tp2) == t1)
{
tileJunk *j = resAddField(tp2);
if ((j->tj_status & RES_TILE_SD) ==0)
if ((j->tj_status & RES_TILE_SD) == 0)
{
j->tj_status |= RES_TILE_SD;
STACKPUSH((ClientData)tp2, resDevStack);

View File

@ -603,7 +603,7 @@ extern ResFixPoint *ResFixList;
extern int ResTileCount;
extern ResSimNode **ResNodeArray;
extern CellDef *mainDef;
extern TileTypeBitMask ResSubsTypeBitMask;
extern TileTypeBitMask ResSDTypesBitMask;
extern HashTable ResDevTable;
extern TileTypeBitMask ResNoMergeMask[NT];
extern ResGlobalParams gparams;