CoreGlueImpl.mesa
Copyright c 1985 by Xerox Corporation. All rights reserved.
Last Edited by Curry, August 23, 1986 12:35:05 pm PDT
Last Edited by: Louis Monier February 10, 1987 7:40:20 pm PST
DIRECTORY CDDirectory, CDViewer, CDSimpleOps, CDOps, CardTable, CCDUtils, CD, CDCells, CDRects, CDSimpleRules, CDSymbolicObjects, Convert, Core, CoreBlock, CoreFrame, CoreGlue, CoreLibrary, CoreName, CoreOps, HashTable, IFUCoreData, IO, PWCLayoutCheckpoint, PW, PWC, PWRoute, Rope, Route;
CoreGlueImpl: CEDAR PROGRAM
IMPORTS CDDirectory, CDViewer, CDSimpleOps, CDOps, CardTable, CCDUtils, CD, CDCells, CDRects, CDSimpleRules, CDSymbolicObjects, Convert, CoreBlock, CoreFrame, CoreGlue, CoreLibrary, CoreName, CoreOps, HashTable, IFUCoreData, IO, PWCLayoutCheckpoint, PW, PWC, PWRoute, Rope, Route
EXPORTS CoreGlue =
BEGIN OPEN CCD: CCDUtils;
ROPE:    TYPE = Core.ROPE;
GlueClass:  TYPE = CoreGlue.GlueClass;
GlueSideType: TYPE = CoreGlue.GlueSideType;
Glue:    TYPE = CoreGlue.Glue;
GlueSpec:   TYPE = CoreGlue.GlueSpec;
Node:    TYPE = CoreGlue.Node;
NodeRec:   TYPE = CoreGlue.NodeRec;
CellType:   TYPE = Core.CellType;
Frame:   TYPE = CoreFrame.Frame;
FrameRec:  TYPE = CoreFrame.FrameRec;
FrameSeq:  TYPE = CoreFrame.FrameSeq;
Side:    TYPE = CoreFrame.Side;
Status:    TYPE = CoreGlue.Status;
Ph:     TYPE = CoreName.Ph;
NWMML:    TYPE = PWC.NWMML;
FCT:     PROC[ct: CellType] RETURNS[Frame] = {RETURN[CoreFrame.FCT[ct]]};
StatusRope: ARRAY Status OF ROPE
= ["rejustify", "progress", "delayed", "complete1", "complete2"];
RouteCheckPointWritten: PUBLIC SIGNAL[name: ROPE] = CODE;
glueCellClass: PUBLIC Core.CellClass ←
CoreOps.SetClassPrintProc[
NEW[Core.CellClassRec ← [name: "CoreGlue", recast: NIL]],
ClassPrintProc];
ClassPrintProc: CoreOps.PrintClassProc = {
glueData: Glue ← NARROW[data];
IO.PutRope[out, "\nGlue: "];
GlueDescription[glueData, out]};
GlueSideTypeRp: ARRAY GlueSideType OF ROPE = [
cap:  CoreName.RopeNm["cap"],
conn: CoreName.RopeNm["conn"],
pwr: CoreName.RopeNm["pwr"],
ext: CoreName.RopeNm["ext"],
chan: CoreName.RopeNm["chan"],
sum: CoreName.RopeNm["sum"],
diff: CoreName.RopeNm["diff"] ];
GlueClassRp: ARRAY GlueClass OF ROPE = [
fill: CoreName.RopeNm["Fill-"],
ext: CoreName.RopeNm["Ext-"],
xlt : CoreName.RopeNm["Xlt-"],
chan: CoreName.RopeNm["Chan-"],
pwr: CoreName.RopeNm["Pwr-"],
sb : CoreName.RopeNm["SBox-"],
bad: CoreName.RopeNm["Bad-"] ];
GlueDescription: PROC[glue: Glue, out: IO.STREAM] = {
out.PutF[" Subclass: %g b:%g l:%g t:%g r:%g ",
IO.rope[ glue.subClass ],
IO.rope[ GlueSideTypeRp[ glue.type[bottom] ] ],
IO.rope[ GlueSideTypeRp[ glue.type[left] ]] ,
IO.rope[ GlueSideTypeRp[ glue.type[top] ] ],
IO.rope[ GlueSideTypeRp[ glue.type[right] ] ] ] };
defaultOptimization: PWRoute.Optimization ← full;
CellProc: PUBLIC PROC[
subClass:     ROPE    ← NIL,
name:      ROPE    ← NIL,
l,r,b,t:      GlueSideType ← cap,  -- cap conn ext chan sum diff
bCell,rCell,tCell,lCell:  Core.CellType ← NIL,
tDir:      PWRoute.HorV ← vertical, -- vertical horizontal
tLayer:     ROPE    ← NIL,  -- metal metal2 poly
bLayer:     ROPE    ← NIL,
xlate:      BOOL    ← FALSE,
data:      REF    ← NIL ] -- metal2 poly metal
RETURNS [cellType: CellType] = {
glue: REF GlueSpec ← NEW[GlueSpec ← [
subClass:CoreName.RopeNm[subClass],
tDir:  tDir,
type:  [b,r,t,l],
cell:  [bCell,rCell,tCell,lCell],
data:  data,
params: NEW[PWRoute.RouterParamsRec ← [
opt:    defaultOptimization,
trunkLayer:  tLayer,
branchLayer: bLayer]]]];
met: ROPE ← CoreName.RopeNm["metal"];
met2: ROPE ← CoreName.RopeNm["metal2"];
tLayer ← CoreName.RopeNm[tLayer];
bLayer ← CoreName.RopeNm[bLayer];
SetGlueClass[glue];
glue.params.trunkLayer ← tLayer ← SELECT tLayer FROM
NIL => SELECT bLayer FROM
NIL  => IF tDir=horizontal THEN met ELSE met2,
met   => met2,
met2  => met,
ENDCASE => ERROR,
ENDCASE => tLayer;
glue.params.branchLayer ← bLayer ← SELECT bLayer FROM
NIL => SELECT tLayer FROM
met   => met2,
met2  => met,
ENDCASE => ERROR,
ENDCASE => bLayer;
IF xlate THEN IF glue.class#ext THEN Signal[] ELSE glue.class ← xlt;
IF name=NIL OR name.Length[]=0 THEN name ← CoreName.ID[ GlueClassRp[glue.class] ];
cellType ← CoreOps.SetCellTypeName[
NEW [ Core.CellTypeRec ← [
class:  glueCellClass,
public: CoreOps.CreateWires[0],
data:  glue] ],
name];
CoreFrame.SetFrameExpandProc[hard, cellType, NEW[CoreFrame.ExpandProc ← ExpandHard] ];
CoreFrame.SetFrameExpandProc[soft, cellType, NEW[CoreFrame.ExpandProc ← ExpandSoft] ]};
ExpandSoft: CoreFrame.ExpandProc = {
frameCT.data  ← NEW[FrameRec ← [data: frameCT.data, seq: NEW[FrameSeq[0]] ]];
frameCT.class ← CoreFrame.frameCellClass;
CoreFrame.SetFrameExpandProc[soft, frameCT, NIL]};
ExpandHard: CoreFrame.ExpandProc = {
frameCT.data  ← NEW[FrameRec ← [data: frameCT.data, seq: NEW[FrameSeq[0]] ]];
frameCT.class ← CoreFrame.frameCellClass;
CoreFrame.SetFrameExpandProc[hard, frameCT, NIL]};
SetGlueClass: PROC[glue: Glue] = {
OPEN glue;
Type: PROC[side: CoreFrame.Side] RETURNS[GlueSideType] = {
SELECT type[side] FROM
pwr  => RETURN[conn];
sum, diff => RETURN[chan];
ENDCASE => RETURN[type[side]]};
eqS: BOOL ← (Type[left] = Type[right]) OR (Type[top] = Type[bottom]);
cnt: ARRAY GlueSideType OF INTALL[0];
FOR side: CoreFrame.Side IN CoreFrame.Side DO
cnt[type[side]] ← cnt[type[side]] +1;
IF type[side]=ext AND type[CoreFrame.OppSide[side]]#conn
THEN {class ← bad; Signal[]; RETURN} ENDLOOP;
IF cnt[pwr]>1 THEN {class ← bad; Signal[]; RETURN};
SELECT
(((cnt[conn]+cnt[pwr]) *10 +
cnt[ext]) *10 +
cnt[cap]) *10 +
cnt[chan] + cnt[sum] + cnt[diff] FROM
4000 => {class ← sb};
3100 => {class ← sb};
3010 => {class ← sb};
2200 => {class ← IF eqS THEN bad ELSE sb};
2110 => {class ← IF eqS THEN bad ELSE sb};
2020 => {class ← IF ~eqS THEN sb ELSE chan};
2011 => {class ← IF ~eqS THEN bad ELSE chan};
2002 => {class ← IF ~eqS THEN bad ELSE chan};
1120 => {class ← IF ~eqS THEN bad ELSE  ext};
1111 => {class ←         chan};
1102 => {class ← IF ~eqS THEN bad ELSE  chan};
1021 => {class ← IF eqS THEN bad ELSE chan};
1012 => {class ← IF ~eqS THEN bad ELSE chan};
0040 => {class ←        fill};
0022 => {class ← IF ~eqS THEN bad ELSE chan}; -- pass through gap
ENDCASE => {class ←       bad};
IF class=chan THEN {
IF Type[left]=conn OR Type[right]=conn OR
Type[top]=chan OR Type[bottom]=chan THEN tDir←vertical ELSE tDir←horizontal;
IF cnt[chan]+cnt[sum]+cnt[diff]=0
THEN {IF (tDir=vertical) = (type[left] = cap AND type[right] = cap) THEN Signal[]}
ELSE {IF (tDir=vertical) = (Type[left] = chan OR Type[right] = chan) THEN Signal[]}};
IF class=chan AND cnt[pwr]#0 THEN class←pwr;
IF class=bad THEN Signal[]};
CheckExpandedFrame: PROC[frameCT: CellType] = {
checkProc: CoreFrame.EnumProc = {
frame: Frame ← FCT[fCT];
name: ROPE ← CoreName.CellNm[fCT].n;
IF frame.seq.size#0 THEN RETURN;
IF frame.cell#NILTHEN RETURN;
IF frame.data=NILTHEN {Signal[]; RETURN};
IF NOT ISTYPE [frame.data, Glue] THEN Signal[];
IF frame.size.x<1 OR frame.size.y<1 THEN Signal[]};
[ ] ← CoreFrame.EnumFrame[frameCT, checkProc]};
Instantiate: PROC[frameCT: CellType] RETURNS[status: Status] = {
frame: Frame ← FCT[frameCT];
name: ROPE ← CoreName.CellNm[frameCT].n;
IF frame.cell#NIL THEN RETURN[complete2];
IF frame.data#NIL AND frame.seq.size#0 THEN {Signal[]; frame.data ← NIL};
IF frame.seq.size=0 THEN {
IF frame.data = NIL THEN {Signal[]; RETURN[complete2]};
status ← InstantiateGlue[frameCT];
IF status=complete2 THEN ERROR;
RETURN[status]};
status ← complete2;
FOR kid: INT IN [0..frame.seq.size) WHILE status#rejustify DO
stat: Status ← Instantiate[frame.seq[kid]];
status ← MIN[status, stat];
ENDLOOP;
IF status=complete2
THEN {
log.PutF["\n Completed: %g %g", IO.rope[name], IO.time[]];
[ ] ← CoreFrame.RecastFrameHard[frameCT];
IF CoreFrame.IsFrameCheckPoint[framCT] THEN {
frame: CoreFrame.Frame ← CoreFrame.FCT[frameCT];
CoreFrame.WriteFrameCheckPoint[frameCT];
frame.seq  ← NEW[CoreFrame.FrameSeq[0]];
frame.data ← NIL;
CoreFrame.ReadFrameCheckPoint[frameCT, shallow]};
RETURN[progress]} };
InstantiateGlue: PROC[frameCT: CellType] RETURNS[status: Status] = {
frame: Frame ← FCT[frameCT];
name: ROPE ← CoreName.CellNm[frameCT].n;
glue: Glue ← NARROW[frame.data];
status ← complete1;
FOR side: Side IN Side DO
SELECT glue.type[side] FROM
chan => {IF glue.cell[side]=NIL THEN Signal[]; LOOP};
sum => LOOP;
diff => LOOP;
cap => LOOP;
ext => {
IF glue.cell[side]#NIL THEN LOOP;
IF glue.cell[CoreFrame.OppSide[side]]#NIL
THEN {
IF glue.class=xlt THEN LOOP;
glue.cell[side] ← IF side=left OR side=right
THEN CoreFrame.RotateCellType
[ glue.cell[CoreFrame.OppSide[side]], mirrorX]
ELSE CoreFrame.RotateCellType
[ glue.cell[CoreFrame.OppSide[side]], rotate180X];
status ← MIN[status, progress]}
ELSE {
log.PutF["\n Glue %g %g side waiting on %g side.",
IO.rope[name],
IO.rope[CoreFrame.SideRope[side]],
IO.rope[CoreFrame.SideRope[CoreFrame.OppSide[side]]]];
status ← MIN[status, delayed]};
LOOP};
conn, pwr  => {
neighborCT: CellType;
found:  BOOL;
IF glue.cell[side]#NIL THEN LOOP;
[neighborCT, found] ← CoreFrame.Neighbor[frameCT, side];
IF ~found
THEN {status ← MIN[status, delayed]; Signal[]} -- Drop To printout
ELSE {
neighbor: Frame ← FCT[neighborCT];
IF neighbor.cell#NIL THEN {
glue.cell[side] ← neighbor.cell;
status ← MIN[status, progress];
LOOP};
IF neighbor.data#NIL THEN WITH neighbor.data SELECT FROM -- ELSE Drop
neighborGlue: Glue => {
IF neighborGlue.type[CoreFrame.OppSide[side]]=ext
AND neighborGlue.cell[side]#NIL
AND neighborGlue.class#xlt
AND neighborGlue.class#pwr
THEN {
glue.cell[side] ← neighborGlue.cell[side];
status ← MIN[status, progress];
LOOP} };
ELSE Drop To printout
ENDCASE => {Signal[]; LOOP} };
log.PutF["\n Glue %g can not yet%guse %g as %g %g.",
IO.rope[name],
IO.rope[IF found THEN " " ELSE " (never?) "],
IO.rope[IF neighborCT=NIL THEN "???" ELSE CoreName.CellNm[neighborCT].n],
IO.rope[CoreFrame.SideRope[side]],
IO.rope[IF glue.type[side]=conn THEN "conn" ELSE "pwr"]];
status ← MIN[status, delayed]; LOOP};
ENDCASE => ERROR ENDLOOP;
IF status < complete1 THEN RETURN[status];
status ← GlueIt[frameCT]};
GlueIt: PROC[frameCT: CellType] RETURNS[status: Status] = {
frame: Frame ← FCT[frameCT];
name:  ROPE ← CoreName.CellNm[frameCT].n;
mainNm: ROPE ← name;
msg:   IO.ROPE;
initSize:  CD.Position ← [frame.size.x, frame.size.y];
cell:   CellType ← NIL;
glue:   Glue ← NARROW[frame.data];
channels:  INTIF glue.data=NIL THEN 6 ELSE NARROW[glue.data, REF INT]^;
pwrSide:  Side;
variable:  BOOL ← MAX[frame.size.xfree, frame.size.yfree]#fixed;
variable    MAX[frame.size.xfree, frame.size.yfree]#fixed;
IF variable AND glue.class IN [fill..xlt] THEN RETURN[complete1];
IF glue.class=chan
AND ((FCT[frame.father].first=top OR FCT[frame.father].first=bottom) = (glue.tDir=vertical))
THEN Signal[];
AdjustLayerAssignments[glue];
IF Rope.Equal[Rope.Substr[name, 0, 3], "IFU"] AND CoreFrame.CellCached[name] THEN
{cell ← PWCLayoutCheckpoint.Retrieve[name]};
IF cell#NIL
THEN log.PutF["\nRead routing checkpoint: %g", IO.rope[name]]
ELSE {
msg ← IO.PutFR["Routing %g, %g %g Frame: %g",
IO.time[],
IO.rope[IF ~variable THEN "fixed" ELSE IF (glue.class=chan OR glue.class=pwr)
THEN "firm" ELSE "variable"],
IO.rope[GlueClassRp[glue.class]],
IO.rope[name] ];
log.PutF["\n%g", IO.rope[msg]]; TerminalIO.PutF[msg]; TerminalIO.PutF["\n"];
SELECT glue.class FROM
fill   => cell ← FCT[CCD.BlankCell[initSize]].cell;
ext, xlt  => {
cell ← SELECT TRUE FROM
glue.type[top]  = ext => FillerCell [glue, top,  initSize.y],
glue.type[bottom] = ext => FillerCell [glue, bottom, initSize.y],
glue.type[left]  = ext => FillerCell [glue, left,  initSize.x],
glue.type[right]  = ext => FillerCell [glue, right,  initSize.x],
ENDCASE    => ERROR};
chan, pwr, sb  => {
ENABLE {
Route.Signal => {log.PutF["\n Route Signal: %g", IO.rope[explanation]];
RESUME};
Route.Error => {log.PutF["\n Route ERROR: %g", IO.rope[explanation]];
cell ← FCT[CCD.BlankCell[initSize]].cell; CONTINUE}};
pwrCell: CellType ← NIL;
routeType: PWRoute.RouteType ← IF glue.class=sb THEN switchBox ELSE channel;
sizeSpec: REF CD.Rect ← NEW[CD.Rect ←
[0, 0, CoreFrame.minSize.x, CoreFrame.minSize.y]];
IF glue.class=pwr THEN {
OPEN glue;
width: INT ← 0;
SELECT TRUE FROM
type[bottom] = pwr => {pwrSide ← bottom}; -- width ← sizeSpec.y2;
type[top]  = pwr => {pwrSide ← top};  -- width ← sizeSpec.y2;
type[left]  = pwr => {pwrSide ← left}; -- width ← sizeSpec.x2;
type[right] = pwr => {pwrSide ← right}; -- width ← sizeSpec.x2;
ENDCASE    => ERROR;
pwrCell ← MergePwr [glue, pwrSide, width]};
IF NOT variable THEN {
sizeSpec.x2 ← initSize.x; sizeSpec.y2 ← initSize.y;
IF pwrCell#NIL THEN
IF glue.tDir=horizontal
THEN sizeSpec.y2 ← MAX[0, sizeSpec.y2 - PWC.InterestSize[pwrCell].y]
ELSE sizeSpec.x2 ← MAX[0, sizeSpec.x2 - PWC.InterestSize[pwrCell].x] };
IF glue.class=pwr THEN {mainNm ← name.Cat["Main"]; glue.cell[pwrSide] ← pwrCell};
IF glue.subClass = CoreName.RopeNm["ThreeLevel"]
THEN cell ← ThreeLevel  [glue, channels]
ELSE cell ← MakeChannel [glue, mainNm, sizeSpec, routeType];
IF glue.class=pwr THEN cell ← CoreBlock.AbutCellList[
name:   NIL,
first:   CoreFrame.SideSides[pwrSide],
cellTypes: LIST[pwrCell, cell] ];
IF Rope.Equal[Rope.Substr[name, 0, 3], "IFU"] THEN {
[ ] ← CoreName.CellNm[cell, name];
PWCLayoutCheckpoint.Store[cell];
SIGNAL RouteCheckPointWritten[name];
cell ← PWCLayoutCheckpoint.Retrieve[name]} };
ENDCASE  => Signal[];
msg ← IO.PutFR["Routing %g, %g complete", IO.rope[name], IO.time[] ];
log.PutF["\n%g", IO.rope[msg]]; TerminalIO.PutF[msg]; TerminalIO.PutF["\n"] };
frame.cell ← cell;
frame.size ← CoreFrame.FixedSize[PWC.InterestSize[cell]];
IF variable AND (glue.class=chan OR glue.class=pwr)
THEN {
IF glue.tDir=vertical
THEN {IF frame.size.y # initSize.y THEN Signal[]; frame.size.xfree ← tame}
ELSE {IF frame.size.x # initSize.x THEN Signal[]; frame.size.yfree ← tame};
IF glue.subClass # CoreName.RopeNm["ThreeLevel"]
THEN CoreFrame.AddExtention[frameCT];
RETURN[rejustify]}
ELSE {
IF initSize # [frame.size.x, frame.size.y] THEN Signal[];
RETURN[progress] } };
CellBad: PROC[obj: CD.Object] RETURNS[bad: BOOL] = {
bad ← obj=NIL OR obj.specificRef=NIL;
IF NOT bad THEN WITH obj.specificRef SELECT FROM
cellPtr: CD.CellPtr   => bad ← cellPtr.contents=NIL;
list:  LIST OF CD.Object => bad ← list=NIL; -- redundant
ENDCASE       => bad ← TRUE;
RETURN[bad OR CD.InterestSize[obj].x<1 OR CD.InterestSize[obj].y<1]};
ChanSideCell: PUBLIC PROC
[side: Side, list: LIST OF ROPE, layer: CD.Layer] RETURNS[cell: CellType] = {
object: CD.Object;
nwmmls: LIST OF NWMML;
FOR list ← list, list.rest WHILE list#NIL DO
name: ROPE ← Rope.Substr[list.first, 0, Rope.Index[list.first, 0, "/"]];
size: INT ← IF list.first.Length[] > name.Length[]
THEN Convert.IntFromRope[Rope.Substr[list.first, Rope.Index[list.first, 0, "/"]+1]]
ELSE 4;
nwmmls ← CONS[[
name: CoreName.RopeNm[name],
min: CCD.lambda, -- move away from beginning edge
max: size*CCD.lambda],
nwmmls]
ENDLOOP;
object ← SideObject[side, nwmmls, layer, 0, FALSE];
cell ← CoreLibrary.ObjCell[object, CoreName.ID["ChanSide"]]};
SideObject: PUBLIC PROC
[side: Side, list: LIST OF NWMML, layer: CD.Layer, length: INT ← 0, sameLayerOnly: BOOLFALSE] -- max => size
RETURNS[cell: CD.Object] = {
dim: INT ← CD.FetchTechnology[$cmosB].lambda;
pos: CD.Position ← [0, 0];
size: CD.Position ← [0, 0];
cell ← CDCells.CreateEmptyCell[];
IF list#NIL AND list.rest=NIL AND length=0 AND list.first.min=0 THEN SELECT side FROM
top, bottom => {pos.x ← pos.x + dim}; -- keep single pin off one end
ENDCASE  => {pos.y ← pos.y + dim}; -- keep single pin off one end
FOR list ← list, list.rest WHILE list#NIL DO
pinApl: CD.Instance;
size  ← SELECT side FROM
top, bottom => [list.first.max, dim]
ENDCASE  => [dim, list.first.max];
IF sameLayerOnly AND (layer#list.first.layer) THEN LOOP;
-- to filter contacts (pins on diff layers)
SELECT side FROM
top, bottom => pos.x ← IF list.first.min < pos.x THEN pos.x+dim*4 ELSE list.first.min;
ENDCASE  => pos.y ← IF list.first.min < pos.y THEN pos.y+dim*4 ELSE list.first.min;
[ ]   ← PW.IncludeInCell[cell, CDRects.CreateRect[size, layer],  pos];
pinApl ← PW.IncludeInCell[cell, CDSymbolicObjects.CreatePin[size], pos];
CDSymbolicObjects.SetName[pinApl, list.first.name];
CDSymbolicObjects.SetLayer[pinApl, layer];
SELECT side FROM
top, bottom => {length ← MAX[length, pos.x ← pos.x+list.first.max]};
ENDCASE  => {length ← MAX[length, pos.y ← pos.y+list.first.max]};
ENDLOOP;
CDCells.SetInterestRect[cell, SELECT side FROM
left   => [-dim, 0, +dim,  length ],
bottom  => [0,  -dim, length, +dim  ],
right   => [0,   0, 2*dim, length ],
top   => [0,   0, length, 2*dim  ],
ENDCASE  => ERROR ];
[ ] ← CDCells.RepositionCell[cell, NIL]};
GND: ROPE ← CoreName.RopeNm["GND"];
VDD: ROPE ← CoreName.RopeNm["VDD"];
GlueSideObject: PUBLIC PROC[glue: Glue, side: Side] RETURNS[obj: CD.Object] = {
cell:  CellType ← glue.cell[side];
layer:  CD.Layer ← CDSimpleRules.GetLayer[$cmosB, GlueSideLayer[glue, side]];
length: INT;
list:  LIST OF NWMML;
vddIdx: INT ← 0;
gndIdx: INT ← 0;
IF cell=NIL THEN RETURN[NIL];
length ← SELECT side FROM
top, bottom => PWC.InterestSize[cell].x
ENDCASE  => PWC.InterestSize[cell].y;
list ← CCD.SidePinList[cell, CoreFrame.OppSide[side]];
FOR lst: LIST OF NWMML ← list, lst.rest WHILE lst#NIL DO
IF lst.first.layer=layer AND glue.class=pwr THEN
SELECT CoreName.RopeNm[lst.first.name] FROM
GND => {
lst.first.name ← IO.PutFR["%g-%g", IO.rope[GND], IO.int[gndIdx]];
gndIdx ← gndIdx +1};
VDD => {
lst.first.name ← IO.PutFR["%g-%g", IO.rope[VDD], IO.int[vddIdx]];
vddIdx ← vddIdx +1};
ENDCASE;
lst.first.max ← lst.first.max - lst.first.min; -- max ← size
ENDLOOP;
obj ← SideObject[side, list, layer, length, TRUE]};
MakeChannel: PUBLIC PROC
[glue: Glue, name: ROPE, sizeSpec: REF CD.Rect, routeType: PWRoute.RouteType]
RETURNS [cell: CellType] = {
sumDiffType: BOOL ← FALSE;
sum, diff: LIST OF NWMML;
objs:  ARRAY Side OF CD.Object ← [
bottom: GlueSideObject[glue, bottom ],
right:  GlueSideObject[glue, right  ],
top:  GlueSideObject[glue, top  ],
left:  GlueSideObject[glue, left  ] ];
obj:  CD.Object; 
FOR side: Side IN Side WHILE NOT sumDiffType
DO IF glue.type[side]=sum OR glue.type[side]=diff THEN sumDiffType ← TRUE ENDLOOP;
IF sumDiffType THEN {
FOR side: Side IN Side DO
nwmmls: LIST OF NWMML;
IF glue.type[side]#conn AND glue.type[side]#chan AND glue.type[side]#pwr THEN LOOP;
nwmmls ← CCD.SidePinList[glue.cell[side], CoreFrame.OppSide[side]];
FOR list: LIST OF NWMML ← nwmmls, list.rest WHILE list#NIL DO
list.first.max ← list.first.max - list.first.min; -- max ← size
list.first.min ← 0 ENDLOOP;   -- throw away position info for channel sides
FOR list: LIST OF NWMML ← nwmmls, list.rest WHILE list#NIL DO
unique: BOOL;
[sum, unique] ← AddIfUnique[list.first, sum];
IF unique
THEN diff ← CONS[list.first, diff]
ELSE diff ← DelIfPresent[list.first, diff].new;
ENDLOOP;
ENDLOOP;
FOR side: Side IN Side DO
layer: CD.Layer ← CDSimpleRules.GetLayer[$cmosB, GlueSideLayer[glue, side]];
IF glue.type[side]=sum THEN objs[side] ← SideObject[side, sum, layer, 0, FALSE];
IF glue.type[side]=diff THEN objs[side] ← SideObject[side, diff, layer, 0, FALSE];
ENDLOOP};
obj ← IF glue.tDir=horizontal
THEN PWRoute.MakeChannel[objs[bottom], objs[top], objs[left], objs[right],
sizeSpec, glue.params, FALSE, routeType]
ELSE PWRoute.MakeChannel[objs[left], objs[right], objs[bottom], objs[top],
sizeSpec, glue.params, TRUE, routeType];
TraceChannelRoute[name, objs, obj];
IF glue.class=pwr THEN {
insts: CD.InstanceList ← NARROW [obj.specificRef, CD.CellPtr].contents;
FOR insts ← insts, insts.rest WHILE insts # NIL DO
name: ROPE;
IF NOT CDSymbolicObjects.IsPin[insts.first.ob] THEN LOOP;
name ← CDSymbolicObjects.GetName[insts.first];
name ← CoreName.RopeNm[name.Substr[0, 3]];
SELECT name FROM
VDD, GND => CDSymbolicObjects.SetName[insts.first, name]; ENDCASE;
ENDLOOP};
cell ← CoreLibrary.ObjCell[name: name, obj: obj]};
TraceChannelRouteOn: BOOLFALSE;
TraceChannelRoute: PROC[name: ROPE, objs: ARRAY Side OF CD.Object, obj: CD.Object] = {
IF TraceChannelRouteOn THEN {
design: CD.Design ← CDOps.CreateDesign[CD.FetchTechnology[$cmosB]];
OS: PROC[object: CD.Object, xory: {x, y}] RETURNS[INT] = {
RETURN[IF object=NIL THEN 0 ELSE IF xory=x
THEN CCDUtils.ObjSize[object].x
ELSE CCDUtils.ObjSize[object].y]};
Add: PROC[object: CD.Object, name: ROPE, pos: CD.Position] = {
IF object=NIL THEN RETURN;
[] ← CDDirectory.Include[design, object, name];
CDOps.IncludeObjectI[design, object, pos]};
Add[objs[bottom], "B", [OS[objs[left],x],    0         ]];
Add[objs[left],  "L", [0,        OS[objs[bottom],y]    ]];
Add[obj,    "M", [OS[objs[left],x],    OS[objs[bottom],y]    ]];
Add[objs[right],  "R", [OS[objs[left],x]+OS[obj,x], OS[objs[bottom],y]    ]];
Add[objs[top],  "T", [OS[objs[left],x],    OS[objs[bottom],y]+OS[obj,y] ]];
CDSimpleOps.RenameDesign[design, name];
[ ] ← CDViewer.CreateViewer[design] }};
AddIfUnique: PROC[item: NWMML, list: LIST OF NWMML]
RETURNS[new: LIST OF NWMML, done: BOOL] = {
FOR l: LIST OF NWMML ← list, l.rest WHILE l#NIL DO
IF item.name = l.first.name THEN
{l.first.max ← MAX[l.first.max, item.max]; RETURN[list, FALSE]} ENDLOOP;
RETURN[CONS[item, list], TRUE]};
DelIfPresent: PROC[item: NWMML, list: LIST OF NWMML]
RETURNS[new: LIST OF NWMML, done: BOOL] = {
IF list=NIL THEN RETURN[NIL, FALSE];
IF item.name = list.first.name THEN RETURN[list.rest, TRUE];
FOR l: LIST OF NWMML ← list, l.rest WHILE l.rest#NIL DO
IF item.name = list.rest.first.name THEN
{l.rest ← l.rest.rest; RETURN[list, TRUE]} ENDLOOP;
RETURN[list, FALSE]};
ChanObj: PUBLIC PROC [
layer:  CD.Layer,
side:  Side,
ref:  REF   ← NIL,
ph:  Ph    ← unk,
dim:  INT   ← 0 ]
RETURNS [cell: CD.Object] ~ {
pos: CD.Position ← [0, 0];
form: REFBit.Format ← REFBit.Desc[ref].bitForm;
list: LIST OF NWMML;
dim ← MAX[dim, 4*CD.FetchTechnology[$cmosB].lambda];
FOR i: CARDINAL DECREASING IN [0..form.size) DO
name:  ROPE ← CoreName.BitRopeToSigRope[form[i].name];
nameInv: ROPE ← CoreName.BitRopeToSigRope[form[i].nameInv];
list ← CONS[ NWMML[
name: CoreName.SignalName[FALSE, name, nameInv, ph],
wire: NIL, min: 0, max: dim, layer: layer], list];
ENDLOOP;
cell ← SideObject[side, list, layer]};
FillerCell: PROC [glue: Glue, objSide: Side, width: INT] RETURNS [cell: CellType] = {
templateCT:  CellType  ← glue.cell[CoreFrame.OppSide[objSide]];
templateSize:  CD.Position ← PWC.InterestSize[templateCT];
resultSize:  CD.Position;
deslayerRope: IO.ROPE  ← GlueSideLayer[glue, objSide];
IF glue.class=xlt
THEN cell ← CoreGlue.XferPins[templateCT, objSide, deslayerRope, width]
ELSE cell ← CoreGlue.XferPins[templateCT, objSide, NIL,    width];
resultSize ← PWC.InterestSize[cell];
IF (SELECT objSide FROM
left, right => resultSize.x#width OR resultSize.y#templateSize.y,
top, bottom => resultSize.y#width OR resultSize.x#templateSize.x,
ENDCASE  => ERROR) THEN Signal[] };
MergePwr: PROC [glue: Glue, objSide: Side, width: INT] RETURNS [cell: CellType] = {
pinSide:   Side   ← CoreFrame.OppSide[objSide];
pinCell:   CellType  ← glue.cell[objSide];
vgCell:   CellType  ← glue.cell[pinSide];
size1:    CD.Position ← PWC.InterestSize[pinCell];
size2:    CD.Position ← PWC.InterestSize[vgCell];
size3:    CD.Position;
deslayerRope: IO.ROPE ← GlueSideLayer[glue, objSide];
cell ← CoreGlue.MergePwrPins[vgCell, pinCell, pinSide, deslayerRope, width];
size3 ← PWC.InterestSize[cell];
IF (SELECT objSide FROM
left, right => size3.y#size1.y OR size3.y#size2.y OR size3.x < width,
top, bottom => size3.x#size1.x OR size3.x#size2.x OR size3.y < width,
ENDCASE  => ERROR) THEN Signal[] };
AdjustLayerAssignments: PROC[glue: Glue] = {
reversed: BOOLFALSE;
FOR side: Side IN Side DO
temp:  ROPE;
nwmmls: LIST OF NWMML;
layer:  CD.Layer;
IF glue.type[side]#conn THEN LOOP;
nwmmls ← CCD.SidePinList[glue.cell[side], CoreFrame.OppSide[side]];
layer  ← CDSimpleRules.GetLayer[$cmosB, GlueSideLayer[glue, side]];
IF nwmmls=NIL THEN Signal[];
IF (layer=nwmmls.first.layer)#(glue.class=xlt) THEN LOOP;
IF reversed THEN Signal[];
reversed ← TRUE;
temp       ← glue.params.trunkLayer;
glue.params.trunkLayer  ← glue.params.branchLayer;
glue.params.branchLayer ← temp;
layer  ← CDSimpleRules.GetLayer[$cmosB, GlueSideLayer[glue, side]];
IF layer#nwmmls.first.layer THEN Signal[];
log.PutF["\nAdjusting Glue layers; %g side to be %g ",
IO.rope[CoreFrame.SideRope[side]], IO.rope[GlueSideLayer[glue, side]]];
ENDLOOP};
GlueSideLayer: PROC[glue: Glue, side: Side] RETURNS[layer: ROPE] = {
tLayer: BOOLSELECT side FROM
left, right => glue.tDir=horizontal,
top, bottom => glue.tDir=vertical,
ENDCASE => ERROR;
RETURN[IF tLayer THEN glue.params.trunkLayer ELSE glue.params.branchLayer]};
ThreeLevel: PROC[glue: Glue, channels: INT] RETURNS[cell: Core.CellType] = {
cellWidth: INT ← IFUCoreData.CellWidth[channels];
rowWidth: INT ← PWC.InterestSize[glue.cell[top]].x;
range:   INT ← rowWidth/cellWidth;
left:   INT ← CCD.metW/2-CCD.leftTail;
right:   INT ← CCD.metW/2-CCD.leftTail+rowWidth;
polyMax:  INT ← rowWidth/8;
left, right, channels etc are all needed to position VDD and GND; Change this;
GetNode: PROC[name: ROPE] RETURNS[node: Node] = {
IF IsPwr[name] THEN RETURN[NIL];
IF name=NIL THEN RETURN[NIL];
node ← NARROW[HashTable.Fetch[nodeTable, name].value];
IF node=NIL THEN {
node ← NEW[NodeRec ← [name: name, minX: right, maxX: 0, type: CCD.cmosMet2]];
[ ] ← HashTable.Store[nodeTable, name, node] } };
MarkPolys: HashTable.EachPairAction = {
node: Node ← NARROW[value];
node.type ← IF node.maxX-node.minX <= polyMax THEN CCD.cmosPoly ELSE CCD.cmosMet2;
RETURN[FALSE] };
nwmmls: LIST OF NWMML;
nodeTable: HashTable.Table ←
HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope];
nwmmls ← CCD.SidePinList[glue.cell[top], bottom];
FOR nwmmls ← nwmmls, nwmmls.rest WHILE nwmmls#NIL DO
node: Node ← GetNode[nwmmls.first.name];
loc: INT ← nwmmls.first.min+left;
IF node=NIL THEN LOOP;
node.top  ← CONS [loc, node.top];
node.minX ← MIN [loc, node.minX];
node.maxX ← MAX [loc, node.maxX] ENDLOOP;
nwmmls ← CCD.SidePinList[glue.cell[bottom], top];
FOR nwmmls ← nwmmls, nwmmls.rest WHILE nwmmls#NIL DO
node: Node ← GetNode[nwmmls.first.name];
loc:  INT ← nwmmls.first.min+left;
IF node=NIL THEN LOOP;
node.bot  ← CONS [loc, node.bot];
node.minX ← MIN [loc, node.minX];
node.maxX ← MAX [loc, node.maxX] ENDLOOP;
IF glue.cell[left]#NIL THEN {
nwmmls ← CCD.SidePinList[glue.cell[left], right];
FOR nwmmls ← nwmmls, nwmmls.rest WHILE nwmmls#NIL DO
node: Node ← GetNode[nwmmls.first.name];
IF node=NIL THEN LOOP;
node.minX ← left ENDLOOP};
IF glue.cell[right]#NIL THEN {
nwmmls ← CCD.SidePinList[glue.cell[right], left];
FOR nwmmls ← nwmmls, nwmmls.rest WHILE nwmmls#NIL DO
node: Node ← GetNode[nwmmls.first.name];
IF node=NIL THEN LOOP;
node.maxX ← right ENDLOOP };
[ ] ← HashTable.Pairs[nodeTable,  MarkPolys];
cell ← ThreeLevelRoute[nodeTable, range, left, right, cellWidth, channels]};
ThreeLevelRoute: PUBLIC PROC
[ nodeTable: HashTable.Table, range, left, right, cellWidth, channels: INT]
RETURNS[cell: CellType] = {
obj: CD.Object;
pitch: CD.Position ← [CCD.metPitch, CCD.met2Pitch/2]; -- y pitch - met2 pol met2 pol . . .
pass:  LIST OF Node ← NIL;
conn:  LIST OF Node ← NIL;
assigned: LIST OF Node ← NIL;
iSize:   CD.Rect ← [left, 0, right, 0];
MarkNodeDependencies[nodeTable];
[pass, conn] ← BuildNodeLists[nodeTable, iSize];
Assign Node Rows
DO
progress:  BOOLFALSE;
workToDo: BOOLFALSE;
FOR nodePtr: LIST OF Node ← conn, nodePtr.rest WHILE nodePtr#NIL DO
polyRow: BOOL ← nodePtr.first.type = CCD.cmosPoly;
rockMax: INT ← pitch.y*(IF polyRow THEN -1 ELSE -2);
IF polyRow THEN Signal[];
IF nodePtr.first.row>-1 THEN LOOP;
workToDo ← TRUE;
FOR rocks: LIST OF Node ← nodePtr.first.rocks, rocks.rest WHILE rocks#NIL DO
IF rocks.first.row=-1 THEN GOTO unAssignedRocks;
rockMax ← MAX[rockMax, rocks.first.row];
REPEAT unAssignedRocks => LOOP ENDLOOP;
progress ← TRUE;
assigned ← AddUnassignedNodeToAssigned
[polyRow, pitch.y, nodePtr.first, assigned, rockMax];
ENDLOOP;
IF NOT workToDo THEN EXIT;
IF NOT progress THEN Signal[]; -- circular
ENDLOOP;
Determine Y Size
iSize.y2 ← 0;
FOR nodePtr: LIST OF Node ← assigned, nodePtr.rest WHILE nodePtr#NIL DO
iSize.y2 ← MAX[iSize.y2, nodePtr.first.row] ENDLOOP;
iSize.y2 ← iSize.y2 + CCD.cnctSize/2 + CCD.topTail;
iSize.y1 ←  0   - CCD.cnctSize/2  - CCD.botTail - CCD.lambda;
obj ← CDCells.CreateEmptyCell[];
Draw Power extension wires
FOR jj: INT IN [0..1] DO
name: ROPE ← CoreName.RopeNm[IF jj=0 THEN "GND" ELSE "VDD"];
FOR ii: INT IN [0..range) DO
xx: INT ← ii*cellWidth + (channels+jj)*pitch.x;
CCD.PutPin[obj,
[CCD.metW, CCD.metW],   [xx, iSize.y2-CCD.metW],   CCD.cmosMet, name];
CCD.PutPin[obj,
[CCD.metW, CCD.metW],   [xx, iSize.y1],      CCD.cmosMet, name];
CCD.AddRet[obj,
[CCD.pwrW, iSize.y2-iSize.y1], [xx-(CCD.pwrW-CCD.metW)/2, iSize.y1], CCD.cmosMet];
ENDLOOP ENDLOOP;
Draw Other extension wires
FOR nodePtr: LIST OF Node ← pass, nodePtr.rest WHILE nodePtr#NIL DO-- pass
name:  ROPE ← nodePtr.first.name;
vgNode: BOOL   ← IsPwr[name];
xx:   INT   ← nodePtr.first.minX;
xxx:  INT   ← xx;
wireW: INT   ← CCD.metW;
IF vgNode THEN {wireW ← CCD.pwrW; xxx ← xx-(CCD.pwrW-CCD.metW)/2};
IF nodePtr.first.top=NIL OR nodePtr.first.bot=NIL THEN {
log.PutF["\nSignal %g is not connected anywhere", IO.rope[nodePtr.first.name]];
Signal[]};
CCD.PutPin [obj, [wireW, CCD.metW], [xx, iSize.y2-CCD.metW], CCD.cmosMet, name];
CCD.PutPin [obj, [wireW, CCD.metW], [xx, iSize.y1],    CCD.cmosMet, name];
CCD.AddRet [obj, [wireW, iSize.y2-iSize.y1], [xxx, iSize.y1], CCD.cmosMet];
ENDLOOP;
DrawAssigndedNodes[obj, assigned, iSize];
CDCells.SetInterestRect[obj, iSize];
[ ] ← CDCells.RepositionCell[obj, NIL];
cell ← CoreLibrary.ObjCell[obj, CoreName.ID["SB"]];
RETURN[cell]};
Branch:  TYPE = REF BranchRec;
BranchRec: TYPE = RECORD[loc: INT, top, bot: Node];
InsertBranch: PROC [branchTab: CardTable.Ref, s: {top,bot}, loc: INT, node: Node] ~ {
branch: Branch ← NARROW[CardTable.Fetch[branchTab, loc].val];
IF branch=NIL THEN {
branch ← NEW[BranchRec ← [loc: loc]];
[ ] ← CardTable.Store[branchTab, loc, branch]};
SELECT s FROM
top => {IF branch.top#NIL THEN Signal[]; branch.top ← node};
bot => {IF branch.bot#NIL THEN Signal[]; branch.bot ← node}; ENDCASE};
MarkNodeDependencies: PROC[nodeTable: HashTable.Table] = {
InsertNodes: HashTable.EachPairAction = {
node: Node ← NARROW[value];
FOR list: LIST OF INT ← node.top, list.rest WHILE list#NIL DO
InsertBranch[branchTab, top, list.first, node] ENDLOOP;
FOR list: LIST OF INT ← node.bot, list.rest WHILE list#NIL DO
InsertBranch[branchTab, bot, list.first, node] ENDLOOP;
RETURN[FALSE] };
MarkNodes: CardTable.EachPairAction = {
branch: Branch ← NARROW[val];
IF branch.top=NIL OR branch.bot=NIL OR branch.top=branch.bot THEN RETURN[FALSE];
FOR list: LIST OF Node ← branch.top.rocks, list.rest WHILE list#NIL DO
IF list.first=branch.bot THEN EXIT;
REPEAT FINISHED => branch.top.rocks ← CONS[branch.bot, branch.top.rocks] ENDLOOP;
FOR list: LIST OF Node ← branch.bot.clouds, list.rest WHILE list#NIL DO
IF list.first=branch.top THEN EXIT;
REPEAT FINISHED => branch.bot.clouds ← CONS[branch.top, branch.bot.clouds] ENDLOOP;
RETURN[FALSE] };
branchTab: CardTable.Ref ← CardTable.Create[];
[ ] ← HashTable.Pairs[nodeTable,  InsertNodes];
[ ] ← CardTable.Pairs[branchTab, MarkNodes]};
BuildNodeLists: PROC[nodeTable: HashTable.Table, iSize: CD.Rect]
RETURNS[pass, conn: LIST OF Node] = {
SortNodes: HashTable.EachPairAction = {
node: Node ← NARROW[value];
SELECT TRUE FROM
node.maxX = node.minX => pass  ← OrderedInsertion[pass, node];
ENDCASE      => conn  ← OrderedInsertion[conn, node];
RETURN[FALSE] };
[ ] ← HashTable.Pairs[nodeTable, SortNodes]};
OrderedInsertion: PROC[list: LIST OF Node, node: Node] RETURNS [LIST OF Node] = {
test: INT;
IF list = NIL THEN RETURN[CONS[node, list]];
test ← (list.first.maxX-list.first.minX)-(node.maxX-node.minX); -- largest segemet first
IF test = 0 THEN test ← (node.minX)-(list.first.minX);    -- smallest position first
IF test < 0  
THENRETURN[CONS[node, list]]
ELSE {list.rest ← OrderedInsertion[list.rest, node]; RETURN[list]}};
AddUnassignedNodeToAssigned: PROC
[oddRow: BOOL, step: INT, node: Node, orig: LIST OF Node, rockMax: INT]
RETURNS [LIST OF Node] = {
Next: PROC[row: INT] RETURNS[next: INT] =
{ next ← row + (IF (((row/step+2) MOD 2)=1)=oddRow THEN 2*step ELSE step) };
minOkRow: INT ← Next[rockMax+step];
IF orig=NIL OR minOkRow < orig.first.row
THEN {node.row ← minOkRow; RETURN[CONS[node, orig] ]};
IF minOkRow = orig.first.row AND node.maxX < orig.first.minX
THEN {node.row ← minOkRow; RETURN[CONS[node, orig] ]};
FOR list: LIST OF Node ← orig, list.rest DO
thisRow: INT  ← list.first.row;
nextRow: INT ← IF list.rest#NIL
THEN list.rest.first.row
ELSE Next[MAX[thisRow, rockMax+step]];
nextMinX: INT ← IF list.rest#NIL
THEN list.rest.first.minX
ELSELAST[INT];
minOkRow ← Next[MAX[thisRow-step, rockMax+step]]; -- thisRow or thisRow+step
SELECT TRUE FROM
thisRow = nextRow     => SELECT TRUE FROM
minOkRow   < thisRow  => ERROR;
minOkRow  > thisRow  => LOOP;
list.first.maxX >= node.minX => LOOP;
node.maxX  >= nextMinX  => LOOP;
ENDCASE        => node.row ← thisRow;
thisRow+step = nextRow   => SELECT TRUE FROM
minOkRow < thisRow    => ERROR;
minOkRow = thisRow    => SELECT TRUE FROM
list.first.maxX >= node.minX => LOOP;
ENDCASE        => node.row ← thisRow;
minOkRow = nextRow    => SELECT TRUE FROM
node.maxX  >= nextMinX  => LOOP;
ENDCASE        => node.row ← nextRow;
minOkRow > thisRow    => LOOP;
ENDCASE        => ERROR;
thisRow+2*step = nextRow   => SELECT TRUE FROM
minOkRow < thisRow    => ERROR;
minOkRow = thisRow    => SELECT TRUE FROM
list.first.maxX < node.minX  => node.row ← thisRow;
node.maxX  < nextMinX  => node.row ← nextRow;
ENDCASE        => LOOP;
minOkRow < nextRow    => node.row ← minOkRow; -- between
minOkRow = nextRow    => SELECT TRUE FROM
node.maxX  < nextMinX  => node.row ← nextRow;
ENDCASE        => LOOP;
ENDCASE        => LOOP;
ENDCASE        => ERROR;
list.rest ← CONS[node, list.rest];
IF oddRow#(((node.row)/step MOD 2)=1) THEN Signal[];
EXIT ENDLOOP;
RETURN[orig]};
IsPwr: PROC[name: ROPE] RETURNS[BOOL] =
{RETURN[Rope.Find[name,"GND"]#-1 OR Rope.Find[name,"VDD"]#-1]};
DrawAssigndedNodes: PROC[cell: CD.Object, assigned: LIST OF Node, iSize: CD.Rect] = {
FOR nodePtr: LIST OF Node ← assigned, nodePtr.rest WHILE nodePtr#NIL DO -- top bot
name:  ROPE  ← nodePtr.first.name;
yMid:  INT  ← nodePtr.first.row;
minX:  INT  ← nodePtr.first.minX;
maxX:  INT  ← nodePtr.first.maxX;
rowType: CD.Layer ← nodePtr.first.type;
pFrng: INT  ← (CCD.pwrW-CCD.metW)/2;
vgNode: BOOL  ← IsPwr[name];
rowWW: INT  ← IF rowType = CCD.cmosPoly THEN CCD.polW ELSE CCD.met2W;
ctct:  CD.Object ← CCD.Contact[rowType];
IF minX=iSize.x1 THEN
CCD.PutPin[cell, [rowWW, rowWW], [minX,    yMid-rowWW/2], rowType, name];
IF maxX=iSize.x2 THEN
CCD.PutPin[cell, [rowWW, rowWW], [maxX-rowWW, yMid-rowWW/2], rowType, name];
CCD.AddRet [cell, [maxX-minX, rowWW], [minX, yMid-rowWW/2], rowType];
FOR nodeTop: LIST OF INT ← nodePtr.first.top, nodeTop.rest WHILE nodeTop#NIL DO
xx:  INT ← nodeTop.first;
IF vgNode
THEN CCD.AddRet[cell, [CCD.pwrW, iSize.y2-yMid],  [xx-pFrng, iSize.y1], CCD.cmosMet]
ELSE CCD.AddRet[cell, [CCD.metW, iSize.y2-yMid],  [xx,     yMid], CCD.cmosMet];
CCD.PutPin [cell, [CCD.metW, CCD.metW],      [xx,iSize.y2-CCD.metW], CCD.cmosMet, name];
[] ← PW.IncludeInCell[cell, ctct, [xx-(CCD.cnctSize-CCD.metW)/2, yMid-CCD.cnctSize/2]];
ENDLOOP;
FOR nodeBot: LIST OF INT ← nodePtr.first.bot, nodeBot.rest WHILE nodeBot#NIL DO
xx:  INT ← nodeBot.first;
IF vgNode
THEN CCD.AddRet[cell, [CCD.pwrW, yMid-iSize.y1], [xx-pFrng, iSize.y1], CCD.cmosMet]
ELSE CCD.AddRet[cell, [CCD.metW, yMid-iSize.y1], [xx,   iSize.y1], CCD.cmosMet];
CCD.PutPin [cell, [CCD.metW, CCD.metW],      [xx,   iSize.y1], CCD.cmosMet, name];
[] ← PW.IncludeInCell[cell, ctct, [xx-(CCD.cnctSize-CCD.metW)/2, yMid-CCD.cnctSize/2]];
ENDLOOP;
ENDLOOP};
RouteSoft: PUBLIC CoreGlue.FrameRouter = {RETURN[complete2]};
RouteHard: PUBLIC CoreGlue.FrameRouter = {
PROC[frameCT: CellType] RETURNS[status: Status]
name: ROPE ← CoreName.CellNm[frameCT].n;
status ← rejustify;
log.PutF["\n\n Building Frame: %g", IO.rope[name]];
   CoreFrame.RotateFrame   [frameCT];
   CoreFrame.TellKidsAboutDad [frameCT];
   CheckExpandedFrame   [frameCT];
FOR pass: INT ← 1, pass+1 WHILE status<complete2 DO
log.PutF["\n\n Pass %g %g\n", IO.int[pass], IO.time[]]; log.Flush[];
IF status=rejustify THEN {
log.PutRope[" Rejustify\n"]; log.Flush[];
CoreFrame.GetSize  [frameCT];
CoreFrame.ReSize  [frameCT];
CoreFrame.RePos  [frameCT, [0,0]];
IF pass<2 THEN {
log.PutRope["\n\n"];
CoreFrame.LogFCT[frameCT, 0, 1, log];
log.Flush[]} };
log.PutRope["\n"];
status ← Instantiate[frameCT];
log.PutF["\n Status: %g", IO.rope[StatusRope[status]]];
SELECT status FROM
rejustify  => {LOOP};
progress  => {LOOP};
delayed  => {IF NOT CoreFrame.FixOneSize[frameCT] THEN Signal[]};
complete1  => {IF NOT CoreFrame.FixOneSize[frameCT] THEN Signal[]};
complete2  => {EXIT};
ENDCASE  => {ERROR};
ENDLOOP;
log.PutRope["\n\n"];
CoreFrame.LogFCT[frameCT, 0, 1, log];
log.Flush[]};
CoreFrame.GetPins[new];
log:  IO.STREAM ← CoreFrame.GetLog[];
Signal: SIGNAL = CODE;
END.