<> <> <> <> <> 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:>> <> <> <> <<-- Pins will be renamed as follows (aasuming an item kBus)>> <> <> <> <<-- 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: INT _ CD.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: BOOL _ FALSE] 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: INT _ IF reverse THEN seq.size-i-1 ELSE i; compositeName: ROPE _ IF 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: ROPE _ SELECT 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"]; <> VddCornerPad _ PW.Get[padsDesign, "VddCornerPad"]; GndCornerPad _ PW.Get[padsDesign, "GndCornerPad"]; EmptyPad _ PW.Get[padsDesign, "EmptyPad"]; END; <<>> END. <<>>