EUtilsImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Monier, July 8, 1985 4:37:36 pm PDT
DIRECTORY
CD, CDCells, CMos, CDPinObjects, Convert, EuControl, EuGen, EUtils, PW, PWCmos, PWDescr, PWPins, Rope;
EUtilsImpl: CEDAR PROGRAM
IMPORTS CD, CDCells, CMos, CDPinObjects, Convert, EuControl, EuGen, PW, PWCmos, PWDescr, PWPins, Rope
EXPORTS EUtils =
BEGIN
OPEN EUtils;
-- Cells for making registers
ctTopCell, ctOpRBus, ctOpLBus, ctBSBus, ctAPBus, ctKBus, ctCBus, ctRBus, ctAnyBusComplement, basicTstDriver: PW.ObPtr;
-- Control descriptors
regsInitialised: BOOLFALSE;
-- A special filler for the control: if there is enough space, inserts contacts poly/m2 on every poly wire
CtTopFiller : PROC [design: CD.Design, obj: PW.ObPtr, height: INT, selectNameProc: PW.SelectNamesProc ← PW.KeepAll] RETURNS [filler: PW.ObPtr] =
BEGIN
PutContact: PW.ForEachPinProc =
BEGIN
layer: CD.Layer ← CDPinObjects.GetLayer[app];
SELECT TRUE FROM
~selectNameProc[CDPinObjects.GetName[app]] => obj ← NIL;
layer=CMos.pol => obj ← contactAndPolyWire;
ENDCASE => obj ← PWCmos.Rect[layer, [app.ob.size.x, height]];
END;
minDy: INT;
polyM2Contact, contactAndPolyWire: PW.ObPtr;
polyM2Contact ← PW.Get[design, "polyM2Contact"];
minDy ← PW.Size[polyM2Contact].y;
contactAndPolyWire ← PW.AbutY[design, polyM2Contact, PWCmos.Rect[CMos.pol, [4, height-minDy]]];
minDy ← PW.Size[polyM2Contact].y;
SELECT height FROM
<0 => ERROR;
=0 => filler ← NIL;
<minDy => filler ← PW.TopFillerCell[design, obj, height, selectNameProc];
ENDCASE => filler ← PW.TransferCell[design, obj, PWPins.top, height, PutContact];
END;
ContactFiller: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc ← PW.KeepAll] RETURNS [filler: PW.ObPtr] =
BEGIN
dy: INTPW.Size[data].y-PW.Size[ctrl].y;
filler ← CtTopFiller[design, ctrl, dy, selectNameProc];
END;
ContactFiller: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc ← PW.KeepAll] RETURNS [filler: PW.ObPtr] =
BEGIN
PutContact: PW.ForEachPinProc =
BEGIN
layer: CD.Layer ← CDPinObjects.GetLayer[app];
SELECT TRUE FROM
~selectNameProc[CDPinObjects.GetName[app]] => obj ← NIL;
layer=CMos.pol => obj ← contactAndPolyWire;
ENDCASE => obj ← PWCmos.Rect[layer, [app.ob.size.x, dy]];
END;
minDy: INT = 20;
polyM2Contact: PW.ObPtr ← PW.Get[design, "polyM2Contact"];
contactAndPolyWire: PW.ObPtr ← PW.AbutY[design, polyM2Contact, PWCmos.Rect[CMos.pol, [4, dy-minDy]]];
dy: INTPW.Size[data].y-PW.Size[ctrl].y;
SELECT dy FROM
<0 => ERROR;
=0 => filler ← NIL;
<minDy => filler ← PW.TopFillerCell[design, ctrl, dy, selectNameProc];
ENDCASE => filler ← PW.TransferCell[design, ctrl, PWPins.top, dy, PutContact];
END;
-- Returns a filler for the control: ctrl is the template, data shows the height;
CtrlFiller: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc ← PW.KeepAll] RETURNS [filler: PW.ObPtr] =
BEGIN
filler ← ContactFiller[design, ctrl, data, selectNameProc];
END;
CtrlFiller: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc ← PW.KeepAll] RETURNS [filler: PW.ObPtr] =
BEGIN
dy: INTPW.Size[data].y-PW.Size[ctrl].y;
IF dy<0 THEN ERROR;
filler ← IF dy=0 THEN NIL ELSE PW.TopFillerCell[design, ctrl, dy, selectNameProc];
END;
-- Makes a very common assembly: control, data, filler(s) and routing;
Assemble: PUBLIC PROC [design: CD.Design, ctrl, data: PW.ObPtr, selectNameProc: PW.SelectNamesProc ← PW.KeepAll, fillerPos: Position ← Center] RETURNS [hSlice: PW.ObPtr] =
BEGIN
filler, wholeCtrl: PW.ObPtr;
SELECT fillerPos FROM
Top => {filler ← CtrlFiller[design, ctrl, data, selectNameProc];
wholeCtrl ← IF filler=NIL THEN ctrl ELSE PW.AbutY[design, ctrl, filler];
};
Bottom => {filler ← CtrlFiller[design, ctrl, data, selectNameProc];
wholeCtrl ← IF filler=NIL THEN ctrl ELSE PW.AbutY[design, filler, ctrl];
};
Center => {topFiller, botFiller: PW.ObPtr; dyTop, dyBot: INT;
dy: INTPW.Size[data].y-PW.Size[ctrl].y;
IF dy<0 THEN ERROR;
dyBot ← dy/2; dyTop ← dy-dyBot;
topFiller ← IF dyTop=0 THEN NIL ELSE CtTopFiller[design, ctrl, dyTop, selectNameProc];
botFiller ← IF dyBot=0 THEN NIL ELSE PW.TopFillerCell[design, ctrl, dyBot, selectNameProc];
wholeCtrl ← PW.AbutY[design, botFiller, ctrl, topFiller];
};
ENDCASE => ERROR;
hSlice ← PW.AbutX[design,
wholeCtrl,
QadCR[design, wholeCtrl, data, EuGen.glueWidth],
data];
END;
-- Must be run before making any register
InitMakeReg: PUBLIC PROC [design: CD.Design] =
BEGIN
IF regsInitialised THEN RETURN ELSE regsInitialised ← TRUE;
ctAnyBusComplement ← PW.Get[design, "ctAnyBusComplement"];
ctTopCell ← PW.AbutX[design, PW.Get[design, "ctTopCell"], ctAnyBusComplement];
ctOpRBus ← PW.AbutX[design, PW.Get[design, "ctOpRBus"], ctAnyBusComplement];
ctOpLBus ← PW.AbutX[design, PW.Get[design, "ctOpLBus"], ctAnyBusComplement];
ctBSBus ← PW.AbutX[design, PW.Get[design, "ctBSBus"], ctAnyBusComplement];
ctAPBus ← PW.AbutX[design, PW.Get[design, "ctAPBus"], ctAnyBusComplement];
ctKBus ← PW.AbutX[design, PW.Get[design, "ctKBus"], ctAnyBusComplement];
ctCBus ← PW.AbutX[design, PW.Get[design, "ctCBus"], ctAnyBusComplement];
ctRBus ← PW.AbutX[design, PW.Get[design, "ctRBus"], ctAnyBusComplement];
basicTstDriver ← PW.Get[design, "basicTstDriver"];
END;
BusNameToMuxObj: PROC [name: ROPE] RETURNS [obj: PW.ObPtr] =
BEGIN
obj ← SELECT TRUE FROM
Rope.Equal[name, "r"] => ctRBus,
Rope.Equal[name, "c"] => ctCBus,
Rope.Equal[name, "k"] => ctKBus,
Rope.Equal[name, "ap"] => ctAPBus,
Rope.Equal[name, "bs"] => ctBSBus,
Rope.Equal[name, "opL"] => ctOpLBus,
Rope.Equal[name, "opR"] => ctOpRBus,
Rope.Equal[name, "top"] => ctTopCell,
ENDCASE => ERROR;
END;
-- Register only, no control
MakeRegDP: PUBLIC PROC [design: CD.Design, regD: RegDescr] RETURNS [reg: PW.ObPtr] =
BEGIN
basicReg, interruptBuses: PW.ObPtr;
revListOb, listOb: PW.ListOb ← NIL;
flip: BOOLFALSE;
keepInReg: LIST OF ROPECONS[regD.out];
InitMakeReg[design];
-- Keep only one contact to output
IF ~regD.cutTopBot THEN keepInReg ← CONS["topBus", keepInReg];
basicReg ← PW.Inst[design, PW.Get[design, "basicRegister"], keepInReg, FALSE]; 
-- Add a short cell which interrupts some of the buses, if required
IF regD.interruptBuses#NIL THEN {
interruptBuses ← PW.Inst[design,
PW.Get[design, "interruptBusses"],
regD.interruptBuses];
basicReg ← PW.AbutY[design, interruptBuses, basicReg];}; 
-- Form the multiplexer
FOR l: LIST OF ROPE ← regD.inListOb, l.rest WHILE l#NIL DO
revListOb ← CONS[BusNameToMuxObj[l.first], revListOb];
ENDLOOP; -- revListOb is the list of objects, from bottom to top, no flip
FOR l: PW.ListOb ← revListOb, l.rest WHILE l#NIL DO
listOb ← CONS[IF flip THEN PW.FlipY[design, l.first] ELSE l.first, listOb];
flip ← ~flip;
ENDLOOP; -- listOb is the list of objects, from top to bottom, correct flip
revListOb ← CONS[basicReg, PW.Reverse[listOb]]; -- add the register
reg ← PW.ArrayX[design, PW.AbutListY[design, revListOb], EuGen.nbSlices];
END;
-- "out" should be "r", "c", "k", "ap", "bs", "opL", "opR", or "down"
-- "inListOb" and "ctrlListOb" must be specified from TOP to BOTTOM
-- Returns a register with its control
MakeReg: PUBLIC PROC [design: CD.Design, regD: RegDescr] RETURNS [reg: PW.ObPtr] =
BEGIN
regDP, ctrl: PW.ObPtr;
regDP ← MakeRegDP[design, regD];
-- Control
ctrl ← PW.AbutListY[design, regD.ctrlListOb];
-- The assembly
reg ← Assemble[design, ctrl, regDP,, regD.fillerPos];
END;
MakeTstDriver: PUBLIC PROC [design: CD.Design, in: ROPE, out: ROPE, ctrlListOb: PW.ListOb ← NIL] RETURNS [tstDriver: PW.ObPtr] =
BEGIN
driver, ctrl: PW.ObPtr;
InitMakeReg[design];
-- Make a cell with the correct input bus, and output on kBus
driver ← PW.Inst[design, basicTstDriver, LIST[in, out], FALSE]; 
-- Control
ctrl ← PW.AbutListY[design, ctrlListOb];
-- The assembly
tstDriver ← Assemble[design, ctrl, PW.ArrayX[design, driver, EuGen.nbSlices]];
END;
MakeDBus: PUBLIC PROC [design: CD.Design, in: ROPE, dStAd: INT] RETURNS [dBus: PW.ObPtr] =
BEGIN
dBusGadget, ctrl: PW.ObPtr;
regscdpd, regscdpu: PWDescr.Descriptor;
-- Make a cell with the correct input bus, and output on kBus
dBusGadget ← PW.Inst[design, basicTstDriver, LIST[in, "kOut"], FALSE]; 
-- Control
regscdpd ← EuGen.EmptyDescrRegsPD[];
regscdpu ← EuGen.EmptyDescrRegsPU[];
PWDescr.SetBit[regscdpu, "nPhB", TRUE];
PWDescr.SetBit[regscdpu, "Vbias", TRUE];
PWDescr.SetBit[regscdpd, "DExecute", TRUE];
PWDescr.SetBit[regscdpd, "DHold", TRUE];
PWDescr.SetInt[regscdpd, "DStateAddress", dStAd];
ctrl ← EuControl.Nand[design, regscdpd, regscdpu];
-- The assembly
dBus ← Assemble[design, ctrl, PW.ArrayX[design, dBusGadget, EuGen.nbSlices]];
END;
MakeRegWithDBus: PUBLIC PROC [design: CD.Design, regD: RegDescr, dStAd: INT] RETURNS [both: PW.ObPtr] =
BEGIN
reg, dBus: PW.ObPtr;
reg ← MakeReg[design, regD];
dBus ← MakeDBus[design, regD.out, dStAd];
both ← PW.AbutY[design, reg, dBus];
END;
-- Route the control to the datapath;
QadCR, QuickandDirtyChannelRouter: PUBLIC PROC [design: CD.Design, left, right: PW.ObPtr, width: INT, useGnd: BOOLTRUE] RETURNS [channel: PW.ObPtr] =
BEGIN
ParseLeft: PWPins.AppEnumerator =
BEGIN
side: PWPins.Side ← PWPins.GetSide[left, app].side;
IF side#PWPins.right THEN RETURN;
IF Rope.Equal[CDPinObjects.GetName[app], powerName] THEN RETURN;
IF Rope.Equal[CDPinObjects.GetName[app], otherPower] THEN RETURN;
leftPins[nbL] ← app; nbL ← nbL+1;
END;
ParseRightAndGnd: PWPins.AppEnumerator =
BEGIN
side: PWPins.Side ← PWPins.GetSide[left, app].side;
IF side#PWPins.left THEN RETURN;
IF Rope.Equal[CDPinObjects.GetName[app], otherPower] THEN RETURN;
IF Rope.Equal[CDPinObjects.GetName[app], powerName]
THEN {gndPins[nbG] ← app; nbG ← nbG+1}
ELSE {rightPins[nbR] ← app; nbR ← nbR+1};
END;
SortArray: PROC [pins: ArrayPins, nb: INT] RETURNS [sortedPins: ArrayPins] =
BEGIN
MinY: PROC RETURNS [minPin: CD.ApplicationPtr] =
BEGIN
minY: INTLAST[INT]; -- y-coord of minPin
minN: INT ← 0;    -- location of minPin in array
FOR i: INT IN [0..nb) DO
IF pins[i]#NIL THEN IF pins[i].location.y<minY THEN
{minPin ← pins[i]; minN ← i; minY ← minPin.location.y};
ENDLOOP;
pins[minN] ← NIL;
END;
FOR i: INT IN [0..nb) DO
sortedPins[i] ← MinY[];
ENDLOOP;
END;
RouteTrack: PROC [leftPin, rightPin: CD.ApplicationPtr] =
BEGIN
reset: BOOLFALSE;
prevRightY ← rightY; prevLeftY ← leftY;
rightY ← rightPin.location.y-iRectR.y1;
leftY ← leftPin.location.y-iRectL.y1;
-- Initialize
IF firstTime THEN
{firstTime ← FALSE;
wasGoingUp ← goingUp ← leftY<rightY;
prevRightY ← rightY; prevLeftY ← leftY;
IF goingUp THEN trackX ← initTackX ELSE trackX ← 6;}
ELSE
{wasGoingUp ← goingUp; goingUp ← leftY<rightY;};
-- Decide if we reset the track position, and if so do it
reset ← (wasGoingUp AND leftY>=prevRightY+okDY) OR (~wasGoingUp AND rightY>=prevLeftY+okDY);
IF reset THEN {IF goingUp THEN trackX ← initTackX ELSE trackX ← 6}
ELSE {IF goingUp THEN trackX ← trackX-incrTrackX ELSE trackX ← trackX+incrTrackX};
-- Draw the m2 wire on the right side
PW.IncludeInCell[design,
channel,
PWCmos.Rect[CMos.met2, [6, 8]],
[width-6, rightY]];
-- via: centered on the m2 wire
PW.IncludeInCell[design,
channel,
PWCmos.Contact[CMos.met2, CMos.met],
[width-16, rightY-1]];
-- "top" horizontal m1 wire
PW.IncludeInCell[design,
channel,
PWCmos.Rect[CMos.met, [width-trackX-16, 8]],
[trackX, rightY]];
-- vertical m1 wire (track)
PW.IncludeInCell[design,
channel,
PWCmos.Rect[CMos.met, [8, ABS[rightY-leftY]+8]],
[trackX, MIN[leftY, rightY]]];
-- "bottom" horizontal m1 wire
PW.IncludeInCell[design,
channel,
PWCmos.Rect[CMos.met, [trackX, leftPin.ob.size.y]],
[0, leftY]];
END;
ArrayPins: TYPE = ARRAY[0..50) OF CD.ApplicationPtr;
leftPins, rightPins, gndPins: ArrayPins;
nbL, nbR, nbG: INT ← 0;
initTackX: INT ← width-24;
incrTrackX: INT ← 16;  -- 8 lambdas
okDY: INT ← 16;  -- 8 lambdas
trackX, prevLeftY, prevRightY, leftY, rightY: INT;
powerName, otherPower: ROPE;
goingUp, wasGoingUp, firstTime: BOOL ← TRUE;
iRectL, iRectR: CD.Rect;
IF useGnd THEN {powerName ← "gnd"; otherPower ← "vdd"}
ELSE {powerName ← "vdd"; otherPower ← "gnd"};
IF PW.Size[left].y#PW.Size[right].y THEN ERROR;
channel ← PW.CreateEmptyCell[];
iRectL ← CD.InterestRect[left];
iRectR ← CD.InterestRect[right];
CDCells.SetInterestRect[channel, [0, 0, width, PW.Size[left].y]]; -- set interestRect of channel
-- Get the three sets of pins in their respective arrays
[] ← PWPins.EnumerateEdgePins[left, ParseLeft];
[] ← PWPins.EnumerateEdgePins[right, ParseRightAndGnd];
-- Sort them by y-coordinate
IF nbL#nbR THEN ERROR;
leftPins ← SortArray[leftPins, nbL];
rightPins ← SortArray[rightPins, nbR];
-- The gnd pins: a straight m2 wire
FOR i: INT IN [0..nbG) DO
PW.IncludeInCell[design,
channel,
PWCmos.Rect[CMos.met2, [width, gndPins[i].ob.size.y]],
[0, gndPins[i].location.y-iRectR.y1]];   -- where it lands
ENDLOOP;
-- For each pair, put 3 wires of m1, one via, one wire m2, and update the x-coord of the vertical track
FOR i: INT IN [0..nbL) DO
RouteTrack[leftPins[i], rightPins[i]];
ENDLOOP;
PW.IncludeInDirectory[design, channel, "channel"];
END;
-- Used by the carry propagator to route cells in a tree-like topology
TreeRouter: PUBLIC PROC [design: CD.Design, template: PW.ObPtr, height: INT] RETURNS [channel: PW.ObPtr] =
BEGIN
-- The level is the position of the rightmost zero in the binary representation of the index
-- level starts at zero
-- a pin at level n will use track p=3*n+d where d=0 (g), d=1 (p), d=2 (c)
Level: PROC [index: INT] RETURNS [level: INT] =
BEGIN
FOR i: INT IN [0..31) DO
IF PW.XthBitOfN[i, index] THEN {level ← i; EXIT};
ENDLOOP;
END;
-- Routes the busses and stick the pins of the tree in two arrays
ParsePins: PWPins.AppEnumerator =
BEGIN
side: PWPins.Side ← PWPins.GetSide[template, app].side;
IF side#PWPins.top THEN RETURN;
IF Rope.Equal[CDPinObjects.GetName[app], "bus"]
THEN PW.IncludeInCell[design, channel, bus, [app.location.x-IRect.x1, 0]]
ELSE {
val, d, index, lr: INT;
[val, d, lr] ← ParseName[app];
index ← 6*val+2*d+lr; -- just a unique id
IF otherPins[index]#NIL THEN ERROR;
IF firstPins[index]=NIL THEN firstPins[index] ← app ELSE otherPins[index] ← app;};
END;
-- A pin named "p1[17]" returns val=17, d=1 (p), and lr=1
ParseName: PROC [pin: CD.ApplicationPtr] RETURNS [val, d, lr: INT] =
BEGIN
s, length: INT;
name: ROPE;
IF pin=NIL THEN RETURN;
name ← CDPinObjects.GetName[pin];
-- Find the opening bracket
s ← Rope.Index[name, 0, "["];
-- Distinguish g, p, and c
SELECT TRUE FROM
Rope.Match["g*", name] => {d ← 0};
Rope.Match["p*", name] => {d ← 1};
Rope.Match["c*", name] => {d ← 2};
ENDCASE => ERROR;
-- Distinguish g0 from g1
SELECT TRUE FROM
Rope.Match["*0[*", name] => {lr ← 0};
Rope.Match["*1[*", name] => {lr ← 1};
ENDCASE => ERROR;
-- Recover the index 17 from "p1[17]"
length ← Rope.Length[name];
name ← Rope.Substr[name, s+1, length-s-2]; -- now only the number is left
val ← Convert.IntFromRope[name];
END;
-- Both pins carry the same name, and correspond to a track
RouteTrack: PROC [pin1, pin2: CD.ApplicationPtr] =
BEGIN
n, p, val, d, x1, x2, y: INT;
m1Track, m2Track, ct: PW.ObPtr;
IF pin1=NIL OR pin2=NIL THEN RETURN;
[val, d] ← ParseName[pin1];
n ← Level[val];
p ← 3*n+d; -- p is the number of the track, start from the ground
x1 ← pin1.location.x-IRect.x1;
x2 ← pin2.location.x-IRect.x1;
y ← (p-3 )*spacing;
m1Track ← PWCmos.Rect[CMos.met, [pin1.ob.size.x, y]];
ct ← PWCmos.Contact[CMos.met, CMos.met2];
m2Track ← PWCmos.Rect[CMos.met2, [ABS[x1-x2], 8]];
PW.IncludeInCell[design, channel, m1Track, [x1, 0]];
PW.IncludeInCell[design, channel, m1Track, [x2, 0]];
PW.IncludeInCell[design, channel, ct, [x1, y]];
PW.IncludeInCell[design, channel, ct, [x2, y]];
PW.IncludeInCell[design, channel, m2Track, [MIN[x1, x2], y]];
END;
spacing: INT = 18; -- use PWCmos to make it more meta
ArrayPins: TYPE = ARRAY[0..400) OF CD.ApplicationPtr;
firstPins, otherPins: ArrayPins; -- pairs to route together
IRect: CD.Rect ← CD.InterestRect[template];
bus: PW.ObPtr ← PWCmos.Rect[CMos.met, [6, height]];
-- Prepare empty channel
channel ← PW.CreateEmptyCell[];
CDCells.SetInterestRect[channel, [0, 0, PW.Size[template].x, height]];
-- Get the pins, and place the vertical busses unrelated to the routing
[] ← PWPins.EnumerateEdgePins[template, ParsePins];
-- For each pair of pins, put 2 wires of m1, two vias, and one wire m2
FOR i: INT IN [0..200) DO
RouteTrack[firstPins[i], otherPins[i]];
ENDLOOP;
PW.IncludeInDirectory[design, channel, "channel"];
END;
END.