PGA144Impl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Created by: Monier, May 3, 1985 3:28:40 pm PDT
Last edited by: Monier, July 3, 1985 3:10:27 pm PDT
Bertrand Serlet February 6, 1986 11:43:13 am PST
DIRECTORY
CD, CDBasics, CDCells, CDCreateLabels, CDPinObjects, CDProperties, CMos, ImagerFont, PGA144, PW, PWDescr, PWPins, Rope;
PGA144Impl: CEDAR PROGRAM
IMPORTS CD, CDBasics, CDCells, CDCreateLabels, CDPinObjects, CDProperties, CMos, ImagerFont, PW, PWDescr, PWPins, Rope
EXPORTS PGA144 =
BEGIN OPEN PGA144;
ROPE: TYPE = PW.ROPE;
-- This generator produces a pad frame for the PGA144, and assigns the power pads automatically
-- Assumptions:
-- We can put at most 36 pins per side, 3 of them being already assigned
-- If we number "1" the first pin on a side, going counter-clockwise (e.g. top pin on left side), then we have 1=(Gnd on left and right, Vdd on top and bottom), 18=PadGnd, and 19=PadVdd.
-- The first pad (Vdd or Gnd) of a side is located in the corner
-- Pads are on a 200 microns minimum pitch, except the power pads which are on a 400 microns pitch and are double-bonded. Corner pads are even wider.
-- The pins expected on pads are the following:
on IOTristatePad: dataOut, enableWrite, dataIn
on OutputPad: dataOut, enableWrite
on InputPad and ClockPad: data
-- Pins will be renamed as follows (aasuming an item kBus)
on IOTristatePad: kBus[1].dataOut, kBus.enableWrite, kBus[1].dataIn
on OutputPad: kBus[1].dataOut, kBus.enableWrite
on InputPad and ClockPad: kBus[1]
-- If an item (say kBus) comprises more than one pad, PGA144 will do a renaming of the pins and generate labels as kBus[0].dataIn, kBus[1].dataIn, . . . The index grows with the coordinate, i.e. from left to right and from bottom to top. If the item is composed of only one pad, then no index is added to the name.
PadFrame: TYPE = ARRAY[1..144] OF PW.Object;
Side: TYPE = PWPins.Side;
-- Prepares a pad frame for routing by PWOnion
MakeOuter: PUBLIC PROC [obj: PW.Object, renameProc: PWPins.RenameProc] RETURNS [cell: PW.Object] =
BEGIN
height: INTCD.InterestSize[IOTristatePad].y-6; -- sniff, sniff: hack!!!
CDCells.SetInterestRect[obj, CDBasics.Extend[CD.InterestRect[obj], -height]];
cell ← PWPins.RenamePins[obj, renameProc];
END;
-- Build the pad frame
MakePadFrame: PUBLIC PROC [padDescr: PWDescr.Descriptor, innerSize: CD.Position ← [0, 0], pack: BOOLFALSE] RETURNS [frame: PW.Object] =
BEGIN
pos: CD.Position;
dx1, dx2, dy1, dy2: INT;
pads: PadFrame;
ThePad: PROC [atom: ATOM] RETURNS [obj: PW.Object] =
BEGIN
obj ← SELECT atom FROM
$IOTst  => IOTristatePad,
$In   => InputPad,
$Out   => OutputPad,
$Clock  => ClockPad,
$Vdd   => VddPad,
$Gnd   => GndPad,
$PadVdd  => PadVddPad,
$PadGnd  => PadGndPad,
$Empty  => EmptyPad,
ENDCASE  => NIL;
END;
-- Every pin is renamed: "data" -> "Signal[12]", "enableWrite" -> "Signal.enableWrite", and the other pins are just pushed up without renaming.
font: ImagerFont.Font ← ImagerFont.Find["Xerox/TiogaFonts/Helvetica10B"];
PlaceEachItem: PWDescr.ForEachItemProc =
BEGIN
seq: PWDescr.PadRef ← NARROW[item.specificRef];
-- Place pads in slots, if they are free, and they should be
side: Side ← IndexToSide[seq[0]]; -- first pin of the item
reverse: BOOL ← side=top OR side=left;
FOR i: INT IN [0..seq.size) DO
index: INTIF reverse THEN seq.size-i-1 ELSE i;
compositeName: ROPEIF seq.size>1 THEN PWPins.Index[item.name, index] ELSE item.name;
KeepPin: PWPins.InstanceEnumerator -- [inst: CD.Instance] RETURNS [quit: BOOL ← FALSE] -- = {
oldRope: ROPE ← CDPinObjects.GetName[inst];
newApp: CD.Instance;
IF Rope.Equal[oldRope, "label"] THEN {
name: ROPE ← compositeName;
label: PW.Object;
posx, posy: INT ← -1;
WHILE posx<0 OR posy<0 DO
label ← CDCreateLabels.CreateTextCell[NIL, compositeName, font, 2, CMos.met];
posx ← (inst.ob.size.x - label.size.x)/2;
posy ← (inst.ob.size.y - label.size.y)/2;
name ← Rope.Substr[name, 0, Rope.Length[name]-1];
ENDLOOP;
CDCells.SetInterestRect[label, [-posx, -posy, inst.ob.size.x-posx, inst.ob.size.y-posy]];
newApp ← NEW[CD.InstanceRep ← [
ob: label, location: [inst.location.x+posx, inst.location.y+posy], orientation: inst.orientation]];
} ELSE {
newRope: ROPESELECT TRUE FROM
Rope.Equal[oldRope, "enableWrite"] => Rope.Concat[item.name, ".enableWrite"],
Rope.Equal[oldRope, "dataOut"] => Rope.Concat[compositeName, ".dataOut"],
Rope.Equal[oldRope, "dataIn"] => Rope.Concat[compositeName, ".dataIn"],
Rope.Equal[oldRope, "data"] => compositeName,  -- data
ENDCASE => oldRope;     -- anything else
newApp ← NEW[CD.InstanceRep ← [
ob: CDPinObjects.CreatePinOb[inst.ob.size],
location: inst.location, orientation: inst.orientation,
properties: CDProperties.DangerousCopyProps[inst.properties]]];
CDPinObjects.SetName[newApp, newRope];
};
cellPtr.contents ← CONS[newApp, cellPtr.contents];
};
cell: PW.Object ← CDCells.CreateEmptyCell[];
pad: PW.Object ← ThePad[NARROW[item.data, ATOM]];
cellPtr: CD.CellPtr;
app: CD.Instance ← NEW[CD.InstanceRep ← [ob: pad]];
CDProperties.PutPropOnInstance[app, $StopEnumerateDeepPins, $StopEnumerateDeepPins];
cellPtr ← NARROW[cell.specificRef];
cellPtr.contents ← CONS[app, cellPtr.contents];
[] ← PWPins.EnumerateDeepPins[pad, KeepPin];
CDCells.SetInterestRect[cell, CD.InterestRect[pad]];
[] ← CDCells.RepositionCell[cell, NIL];
IF pads[seq[i]]=NIL THEN pads[seq[i]] ← cell ELSE ERROR; -- no conflict please
ENDLOOP;
END;
topPadRow, leftPadRow, botPadRow, rightPadRow: PW.Object;
-- Create a new pad frame and initialize it, i.e. place the power pins
PW.WriteF["Generating the Pad Frame\n"];
InitPGA144[];
pads ← InitPadFrame[]; 
-- Place the pads in their slot
PWDescr.EnumerateItems[padDescr, PlaceEachItem];
-- If packing is required, compute the size of the pad frame
IF pack THEN
BEGIN
innerSize.x ← MAX[
SideLength[pads, top].totalPadsLength,
SideLength[pads, bottom].totalPadsLength];
innerSize.y ← MAX[
SideLength[pads, left].totalPadsLength,
SideLength[pads, right].totalPadsLength];
END;
-- Make the four sides
topPadRow ← PWPins.RenamePins[PadFrameSide[pads, innerSize, top]];
leftPadRow ← PWPins.RenamePins[PW.Rot90[PadFrameSide[pads, innerSize, left]]];
botPadRow ← PWPins.RenamePins[PW.Rot180[PadFrameSide[pads, innerSize, bottom]]];
rightPadRow ← PWPins.RenamePins[PW.Rot270[PadFrameSide[pads, innerSize, right]]];
frame ← PW.CreateEmptyCell[];
-- Place them
pos ← CD.InterestSize[topPadRow];
dy1 ← CD.InterestSize[botPadRow].y;
dy2 ← CD.InterestSize[rightPadRow].y;
dx1 ← CD.InterestSize[leftPadRow].x;
dx2 ← CD.InterestSize[botPadRow].x ;
[] ← PW.IncludeInCell[frame, botPadRow, [0, 0]];
[] ← PW.IncludeInCell[frame, topPadRow, [dx1, dy2]];
[] ← PW.IncludeInCell[frame, leftPadRow, [0, dy1]];
[] ← PW.IncludeInCell[frame, rightPadRow, [dx2, 0]];
END;
-- Build a side, of length size, made of the object pads[from] until pads[to];
-- The idea is to keep the bonding wires from angling too much, so the filling material is distributed proportionnally among the empty slots. If no slot is empty (i.e. 36 pads on the side), then the empty space is assigned to the corners.
-- Every side owns one corner: counter-clockwise, a side puts its first pad in the corner.
PadFrameSide: PROC [pads: PadFrame, innerSize: CD.Position, side: Side] RETURNS [padFrameSide: PW.Object] =
BEGIN
Filler: PROC [fillingWidth: INT] RETURNS [cell: PW.Object] =
{cell ← PW.LeftFillerCell[IOTristatePad, fillingWidth]};
listOb: PW.ListOb ← NIL;
fillingCell: PW.Object;
totalPadsLength, toSpend, nbEmpty, fillingWidth, leftOver: INT ← 0;
low, hi: INT;
[low, hi] ← SideToInterval[side];
[totalPadsLength, nbEmpty] ← SideLength[pads, side];
-- Figure out how much space is wasted, and find if there is an empty slot
toSpend ← (IF IsVertical[side] THEN innerSize.y ELSE innerSize.x) - totalPadsLength;
IF toSpend<0 THEN ERROR;  -- proposed inner size is too small
-- Make the filling cell
IF nbEmpty#0 AND toSpend#0 THEN fillingWidth ← toSpend/nbEmpty;
leftOver ← toSpend-fillingWidth*nbEmpty; -- the remainder, for the corner
IF nbEmpty=0 OR fillingWidth=0 THEN fillingCell ← NIL
ELSE fillingCell ← Filler[fillingWidth];
-- Start with the corner and the extra space, if any
listOb ← LIST[pads[low]];
IF leftOver#0 THEN listOb ← CONS[Filler[leftOver], listOb];
-- Assemble: the list is reversed, but abut works left to right, and the orientation in the pad frame is counter-clockwise, so it is correct.
FOR i: INT IN (low..hi] DO
IF pads[i]#NIL
THEN listOb ← CONS[pads[i], listOb]
ELSE IF fillingCell#NIL THEN listOb ← CONS[fillingCell, listOb];
ENDLOOP;
-- Final assembly
padFrameSide ← PW.AbutListX[listOb];
END;
SideToInterval: PROC [side: Side] RETURNS [low, hi: INT] =
BEGIN
SELECT side FROM
left => {low ← 1; hi ← 36};
bottom => {low ← 37; hi ← 72};
right => {low ← 73; hi ← 108};
top => {low ← 109; hi ← 144};
ENDCASE => ERROR;
END;
IndexToSide: PROC [index: INT] RETURNS [side: Side] =
BEGIN
side ← SELECT index FROM
<37 => left,
<73 => bottom,
<109 => right,
<145 => top,
ENDCASE => ERROR;
END;
IsVertical: PROC[side: Side] RETURNS [BOOL] = {RETURN[side=left OR side=right]};
SideLength: PROC [pads: PadFrame, side: Side] RETURNS [totalPadsLength, nbEmpty: INT ← 0] =
BEGIN
low, hi: INT;
[low, hi] ← SideToInterval[side];
FOR i: INT IN [low+1..hi] DO-- we skip the corner
IF pads[i]#NIL THEN totalPadsLength ← totalPadsLength+CD.InterestSize[pads[i]].x
ELSE nbEmpty ← nbEmpty+1;
ENDLOOP;
END;
-- Dealing with the mapping pad->bonding pin
InitPadFrame: PROC [] RETURNS [pads: PadFrame] =
BEGIN
-- the four corners get a power pad for the chip, with a number to help bonding
pads[1] ← PW.Inst[GndCornerPad, LIST["1"], FALSE];
pads[73] ← PW.Inst[GndCornerPad, LIST["73"], FALSE];
pads[37] ← PW.Inst[VddCornerPad, LIST["37"], FALSE];
pads[109] ← PW.Inst[VddCornerPad, LIST["109"], FALSE];
-- the centers get a pair of power pads for pad drivers
pads[18] ← pads[54] ← pads[90] ← pads[126] ← PadGndPad;
pads[19] ← pads[55] ← pads[91] ← pads[127] ← PadVddPad;
END;
Segment: PUBLIC PROC [first, howMany: INT] RETURNS [padRef: PWDescr.PadRef] =
BEGIN
OccupiedOnPGA144: PROC [index: INT] RETURNS [busy: BOOL] =
{busy ← SELECT index FROM
1, 18, 19, 37, 54, 55, 73, 90, 91, 109, 126, 127 => TRUE,
ENDCASE => FALSE;};
index: INT ← first;
padRef ← NEW[PWDescr.PadRec[howMany]];
FOR i: INT IN [0..howMany) DO
WHILE OccupiedOnPGA144[index] DO index ← index+1; ENDLOOP; -- search next free slot
padRef[i] ← index;
index ← index+1;
ENDLOOP;
END;
Single: PUBLIC PROC [where: INT] RETURNS [padRef: PWDescr.PadRef] =
{padRef ← Segment[where, 1];};
-- Standard pads
InputPad, OutputPad, IOTristatePad, VddPad, GndPad, PadVddPad, PadGndPad, ClockPad, EmptyPad: PW.Object;
VddCornerPad, GndCornerPad: PW.Object;
InitPGA144: PROC [] =
BEGIN
-- Fetch the pads from the source design
padsDesign: CD.Design ← PW.OpenDesign["///cmospadlibrarypga144.dale"];
InputPad ← PW.Get[padsDesign, "InputPad"];
OutputPad ← PW.Get[padsDesign, "OutputPad"];
IOTristatePad ← PW.Get[padsDesign, "IOTristatePad"];
VddPad ← PW.Get[padsDesign, "VddPad"];
GndPad ← PW.Get[padsDesign, "GndPad"];
PadVddPad ← PW.Get[padsDesign, "PadVddPad"];
PadGndPad ← PW.Get[padsDesign, "PadGndPad"];
ClockPad ← PW.Get[padsDesign, "ClockPad"];
CornerPad ← PW.Get[padsDesign, "CornerPad"];
VddCornerPad ← PW.Get[padsDesign, "VddCornerPad"];
GndCornerPad ← PW.Get[padsDesign, "GndCornerPad"];
EmptyPad ← PW.Get[padsDesign, "EmptyPad"];
END;
END.