DIRECTORY CCDUtils, CD, CDBasics, CDCells, CDOrient, CDRects, CDSimpleRules, CDSymbolicObjects, Core, CoreBlock, CoreFrame, CoreGlue, CoreLibrary, CoreName, IO, PW, PWC, Rope; CoreGlueXlate: CEDAR PROGRAM IMPORTS CCDUtils, CD, CDBasics, CDCells, CDOrient, CDRects, CDSimpleRules, CDSymbolicObjects, CoreBlock, CoreFrame, CoreLibrary, CoreName, IO, PW, PWC, Rope EXPORTS CoreGlue = BEGIN TrackSeq: TYPE = RECORD[cSz, tSz, length: INT, seq: SEQUENCE size: CARDINAL OF Track]; Track: TYPE = RECORD[pos, cost, chan: INT _ 0, pin: NWMML]; -- pos is center of track Side: TYPE = CoreFrame.Side; NWMML: TYPE = PWC.NWMML; ROPE: TYPE = Rope.ROPE; CellType: TYPE = Core.CellType; GND: ROPE _ CoreName.RopeNm["GND"]; VDD: ROPE _ CoreName.RopeNm["VDD"]; pol: CD.Layer _ CDSimpleRules.GetLayer[$cmosB, "poly"]; met: CD.Layer _ CDSimpleRules.GetLayer[$cmosB, "metal"]; met2: CD.Layer _ CDSimpleRules.GetLayer[$cmosB, "metal2"]; SideOrient: PROC[side: Side] RETURNS[orientation: CD.Orientation] = { orientation _ SELECT side FROM bottom => CDOrient.rotate270, right => CDOrient.original, top => CDOrient.rotate90X, left => CDOrient.mirrorX, ENDCASE => ERROR}; SideXfrm: PROC[from, to: Side] RETURNS[CD.Orientation] = {RETURN[CDOrient.ComposeOrient [CDOrient.InverseOrient[SideOrient[from]], SideOrient[to] ] ]}; LoadNormalPinList: PROC[cell: CellType, side: Side, minSep: INT] RETURNS[list: LIST OF NWMML] = { cellName: ROPE _ CoreName.CellNm[cell].n; list _ CCDUtils.SidePinList[cell, side]; FOR temp: LIST OF NWMML _ list, temp.rest WHILE temp.rest#NIL DO sep: INT _ temp.rest.first.min - temp.first.max; diff: INT _ minSep - sep; IF temp.first.wire # temp.rest.first.wire AND temp.first.layer = temp.rest.first.layer AND diff > 0 THEN { log.PutF["\nMinimum separation adjustment HACK; %g: %g %g", IO.rope[cellName], IO.rope[temp.first.name], IO.rope[temp.rest.first.name]]; temp.first.max _ temp.first.max - diff/2; temp.rest.first.min _ temp.rest.first.min + diff/2}; ENDLOOP; FOR temp: LIST OF NWMML _ list, temp.rest WHILE temp#NIL DO temp.first.max _ temp.first.max-temp.first.min; -- size temp.first.min _ temp.first.min+temp.first.max/2; -- center position ENDLOOP}; GetLayer: PROC[list: LIST OF NWMML, testRope: ROPE, case: BOOL _ FALSE] RETURNS[layer: CD.Layer] = { IF list=NIL THEN ERROR; FOR list _ list, list.rest WHILE list#NIL DO IF Rope.Find[testRope, list.first.name, 0, case]#-1 THEN RETURN[list.first.layer] ENDLOOP; ERROR}; DelOthers: PROC [list: LIST OF NWMML, testRopes: LIST OF ROPE, keepIfFound, case: BOOL _ FALSE] RETURNS[LIST OF NWMML] = { found: BOOL _ FALSE; IF list=NIL THEN RETURN[NIL]; FOR ropes: LIST OF ROPE _ testRopes, ropes.rest WHILE ropes#NIL DO found _ found OR (Rope.Find[list.first.name, ropes.first, 0, case]#-1) ENDLOOP; IF found=keepIfFound THEN {list.rest _ DelOthers[list.rest, testRopes, keepIfFound, case]; RETURN[list]} ELSE RETURN[ DelOthers[list.rest, testRopes, keepIfFound, case] ] }; Sort: PROC[list: LIST OF NWMML] RETURNS[LIST OF NWMML] = { SwapNWMMLs: TYPE = RECORD[r0, r1: NWMML]; IF list=NIL THEN RETURN[NIL]; list.rest _ Sort[list.rest]; FOR temp: LIST OF NWMML _ list, temp.rest WHILE temp.rest#NIL DO IF temp.first.min < temp.rest.first.min THEN RETURN[list]; [temp.first, temp.rest.first] _ SwapNWMMLs[temp.rest.first, temp.first]; ENDLOOP; RETURN[list]}; MakeTracks: PROC[cellSize, trackSize, trackGap, chanWidth: INT, vg: LIST OF NWMML] RETURNS[tracks: REF TrackSeq] = { TracksIn: PROC[top, bot: INT] RETURNS[INT] = {RETURN[MAX[(top-bot-trackGap)/(trackSize+trackGap), 0]]}; InsertSegment: PROC[nxt, top, bot: INT] RETURNS[INT]= { cnt: INT _ TracksIn[top, bot]; FOR nxt _ nxt, nxt+1 WHILE cnt>0 DO tracks[nxt].pos _ bot + trackGap + trackSize/2; bot _ bot + trackGap + trackSize; cnt _ cnt -1; ENDLOOP; RETURN[nxt]}; count: INT _ 0; next: INT _ 0; first: NWMML _ [min: 0, max: 0]; last: NWMML _ first; FOR list: LIST OF NWMML _ vg, list.rest WHILE list#NIL DO t: INT _ (list.first.min - list.first.max/2); b: INT _ (last.min + last.max/2); count _ count + TracksIn[t,b]; last _ list.first; REPEAT FINISHED => {count _ count + TracksIn[cellSize, last.min + last.max/2]} ENDLOOP; tracks _ NEW[TrackSeq[count+2]]; tracks.tSz _ trackSize + trackGap; tracks.cSz _ chanWidth; tracks.length _ cellSize; tracks[0].pos _ 0; tracks[0].cost _ cellSize*(cellSize/tracks.tSz); tracks[0].pin _ ["Dummy", NIL, 0, 0, 0]; next _ 1; last _ first; FOR list: LIST OF NWMML _ vg, list.rest WHILE list#NIL DO next _ InsertSegment[nxt: next, top: (list.first.min - list.first.max/2), bot: last.min + last.max/2]; last _ list.first; REPEAT FINISHED => {next _ InsertSegment[nxt: next, top: cellSize, bot: last.min+ last.max/2]} ENDLOOP; IF next+1#tracks.size THEN ERROR; tracks[next].pos _ 2*cellSize; tracks[next].cost _ 0; tracks[next].pin _ ["Dummy", NIL, cellSize, 0, 0]}; AssignPinsToTracks: PROC[list: LIST OF NWMML, tracks: REF TrackSeq] = { top: INT _ 0; cnt: INT _ 0; pin: NWMML _ tracks[tracks.size-1].pin; FOR temp: LIST OF NWMML _ list, temp.rest WHILE temp#NIL DO cnt _ cnt+1 ENDLOOP; IF cnt > tracks.size-2 THEN ERROR; FOR temp: LIST OF NWMML _ list, temp.rest WHILE temp#NIL DO bot: INT; FOR top _ top, top+1 DO currDist: INT _ ABS[temp.first.min - tracks[top].pos]; nextDist: INT _ ABS[temp.first.min - tracks[top+1].pos]; IF top+1 = tracks.size-1 THEN EXIT; IF nextDist - currDist > tracks[top].cost THEN EXIT; ENDLOOP; tracks[top+1].pin_temp.first; -- temporary FOR bot _ top, bot-1 WHILE tracks[bot].pin.name#NIL DO ENDLOOP; FOR bot _ bot, bot+1 WHILE bot<=top DO tracks[bot].pin _ tracks[bot+1].pin; tracks[bot].cost _ tracks[bot-1].cost + ABS[tracks[bot].pin.min-tracks[bot-1].pos] - ABS[tracks[bot].pin.min-tracks[bot].pos]; ENDLOOP; tracks[top+1].pin_ [ ]; ENDLOOP; tracks[tracks.size-1].pin _ pin}; AllignSparseTracks: PROC[tracks: REF TrackSeq] = { FOR track: INT IN [1..tracks.size-1) DO lastDist, nextDist: INT; IF tracks[track].pin.name=NIL THEN LOOP; lastDist _ ABS[tracks[track].pin.min - tracks[track-1].pos] /tracks.tSz; nextDist _ ABS[tracks[track].pin.min - tracks[track+1].pos] /tracks.tSz; IF lastDist=0 AND tracks[track-1].pin.name=NIL THEN {tracks[track].pos_tracks[track].pin.min; LOOP}; IF nextDist=0 AND tracks[track+1].pin.name=NIL THEN {tracks[track].pos_tracks[track].pin.min; LOOP}; ENDLOOP}; AssignChannelsToTrackPins: PROC[tracks: REF TrackSeq] = { chan: INT _ 0; FOR track: INT IN [1..tracks.size-1) DO IF tracks[track].pin.name=NIL THEN LOOP; IF ABS[tracks[track].pin.min - tracks[track-1].pos]=0 THEN tracks[track].chan _ -1 ENDLOOP; chan _ 0; FOR track: INT IN [1..tracks.size-1) DO IF tracks[track].pin.name=NIL THEN LOOP; IF tracks[track].pin.min <= tracks[track].pos THEN chan _ 0 ELSE { IF tracks[track].pin.min > tracks[track+1].pos - tracks.tSz OR tracks[track-1].pin.name#NIL AND (tracks[track-1].pin.min > tracks[track].pos - tracks.tSz) THEN {tracks[track].chan _ chan; chan _ chan +1} }; ENDLOOP; chan _ 0; FOR track: INT DECREASING IN [1..tracks.size-1) DO IF tracks[track].pin.name=NIL THEN LOOP; IF tracks[track].pin.min >= tracks[track].pos THEN chan _ 0 ELSE { IF tracks[track].pin.min < tracks[track-1].pos + tracks.tSz OR tracks[track+1].pin.name#NIL AND (tracks[track+1].pin.min < tracks[track].pos + tracks.tSz) THEN {tracks[track].chan _ chan; chan _ chan +1} }; ENDLOOP }; FlowRoute: PROC [vg: LIST OF NWMML, vLayer, gLayer: CD.Layer, tracks: REF TrackSeq] RETURNS[cell: CellType _ NIL] = {RETURN[CoreLibrary.ObjCell[ FlowRouteObj[vg, vLayer, gLayer, tracks], CoreName.ID["Flow"] ] ] }; FlowRouteObj: PROC [vg: LIST OF NWMML, vLayer, gLayer: CD.Layer, tracks: REF TrackSeq] RETURNS[obj: CD.Object] = { pinObj: CD.Object; pinInstLt: CD.Instance; pinInstRt: CD.Instance; cSize: INT _ CDSimpleRules.Contact[met, pol].size.x; chnSz: INT _ tracks.cSz; polSz: INT _ CDSimpleRules.MinWidth[pol]; nChans: INT _ 0; totSz: INT _ 0; obj _ PW.CreateEmptyCell[]; FOR track: INT IN [1..tracks.size-1) DO nChans _ MAX[nChans, tracks[track].chan+1] ENDLOOP; totSz _ ((nChans*chnSz + 2*polSz -1)/polSz)*polSz; -- +polSz +round up to next polSz FOR vg _ vg, vg.rest WHILE vg#NIL DO layer: CD.Layer _ IF Rope.Equal[vg.first.name, VDD, FALSE] THEN vLayer ELSE gLayer; pinSz: INT _ MIN[totSz/4, cSize]; [] _ PW.IncludeInCell[obj, CDRects.CreateRect[[totSz, vg.first.max], layer], [0, vg.first.min - vg.first.max/2]]; pinObj _ CDSymbolicObjects.CreatePin[[pinSz, vg.first.max]]; pinInstLt _ PW.IncludeInCell[obj, pinObj, [0, vg.first.min - vg.first.max/2]]; pinInstRt _ PW.IncludeInCell[obj, pinObj, [totSz-pinSz, vg.first.min - vg.first.max/2]]; CDSymbolicObjects.SetName[pinInstLt, vg.first.name]; CDSymbolicObjects.SetName[pinInstRt, vg.first.name]; CDSymbolicObjects.SetLayer[pinInstLt, layer]; CDSymbolicObjects.SetLayer[pinInstRt, layer]; ENDLOOP; pinObj _ CDSymbolicObjects.CreatePin[ [polSz, polSz] ]; FOR track: INT IN [1..tracks.size-1) DO t: Track _ tracks[track]; lsl: INT _ chnSz + t.chan*chnSz; -- left segment length rsl: INT _ totSz - lsl; -- right segment length IF t.pin.name=NIL THEN LOOP; [] _ PW.IncludeInCell[obj, CDRects.CreateRect[[rsl, polSz], pol], [lsl, t.pos - polSz/2]]; IF t.chan>-1 THEN { [] _ PW.IncludeInCell[obj, CDRects.CreateRect[[lsl, polSz], pol], [0, t.pin.min - polSz/2]]; [] _ PW.IncludeInCell[obj, CDRects.CreateRect[[polSz, ABS[t.pos-t.pin.min]+polSz], pol], [lsl-polSz, MIN[t.pos, t.pin.min] - polSz/2]]}; pinInstLt _ PW.IncludeInCell[obj, pinObj, [0, t.pin.min - polSz/2]]; pinInstRt _ PW.IncludeInCell[obj, pinObj, [totSz-pinObj.size.x, t.pos - polSz/2]]; CDSymbolicObjects.SetName[pinInstLt, t.pin.name]; CDSymbolicObjects.SetName[pinInstRt, t.pin.name]; CDSymbolicObjects.SetLayer[pinInstLt, pol]; CDSymbolicObjects.SetLayer[pinInstRt, pol]; ENDLOOP; CDCells.SetInterestRect[obj, [0, 0, totSz, tracks.length]]; IF NOT CDCells.RepositionCell[obj, NIL] THEN Signal[]}; Signal: SIGNAL = CODE; MergePwrPins: PUBLIC PROC [ vgTemplate: CellType, pinTemplate: CellType, pinTemplateSide: Side, routingLayerDes: ROPE _ NIL, minWidth: INT _ 0] RETURNS [cell: CellType _ NIL] = { chanWidth: INT _ CDSimpleRules.MinDist[pol, pol] + CDSimpleRules.MinWidth[pol]; trackSize: INT _ CDSimpleRules.Contact[met, met2].size.x; trackGap: INT _ CDSimpleRules.MinDist[met2, met2]; vgTemplateSide: Side _ CoreFrame.OppSide[ pinTemplateSide]; cellSize: INT; tracks: REF TrackSeq; offset: INT _ CDSimpleRules.MinDist[met2, met2]; pins: LIST OF NWMML _ LoadNormalPinList[pinTemplate, pinTemplateSide, offset]; vg: LIST OF NWMML _ LoadNormalPinList[vgTemplate, vgTemplateSide, offset]; vLayer: CD.Layer; gLayer: CD.Layer; flow: CellType; xfer: CellType; IF pins = NIL OR vg = NIL THEN {cell _ NIL; Signal[]; RETURN[cell]}; vLayer _ GetLayer[list: pins, testRope: VDD, case: FALSE]; gLayer _ GetLayer[list: pins, testRope: GND, case: FALSE]; vg _ DelOthers[list: vg, testRopes: LIST[GND, VDD], keepIfFound: TRUE, case: FALSE]; pins _ DelOthers[list: pins, testRopes: LIST[GND, VDD], keepIfFound: FALSE, case: FALSE]; vg _ Sort[vg]; pins _ Sort[pins]; IF routingLayerDes=NIL THEN routingLayerDes _ "metal2"; cellSize _ CDBasics.SizeOfRect[CDOrient.MapRect[ itemInCell: PWC.InterestRect[pinTemplate], cellSize: PWC.InterestSize[pinTemplate], cellInstOrient: SideXfrm[pinTemplateSide, right], cellInstPos: [0,0] ] ].y; tracks _ MakeTracks[cellSize, trackSize, trackGap, chanWidth, vg]; AssignPinsToTracks[pins, tracks]; AllignSparseTracks[tracks]; AssignChannelsToTrackPins[tracks]; flow _ FlowRoute[vg, vLayer, gLayer, tracks]; xfer _ XferPins[flow, right, routingLayerDes, MAX[0, minWidth-PWC.InterestSize[flow].x]]; cell _ CoreBlock.AbutCellList[CoreName.ID["MrgPwr"], left, LIST[flow, xfer]]; IF pinTemplateSide#right THEN cell _ CoreFrame.RotateCellType[cell, SideXfrm[right, pinTemplateSide]]}; XferPins: PUBLIC PROC [ template: CellType, objSide: Side, routingLayerDes: ROPE, minWidth: INT] RETURNS [cell: CellType _ NIL] = {RETURN[CoreLibrary.ObjCell[ XferPinsObj[template, objSide, routingLayerDes, minWidth], CoreName.ID["Xlt"] ] ] }; XferPinsObj: PROC [ template: CellType, objSide: Side, routingLayerDes: ROPE, minWidth: INT] RETURNS [obj: CD.Object _ NIL] = { IncludeXferPin: PROC[pin: NWMML] = { InclRct: PROC[layer: CD.Layer, size, pos: CD.Position] = {IF size.x>0 THEN [] _ PW.IncludeInCell[obj, CDRects.CreateRect[size, layer], pos]}; Stitch: PROC [layer1, layer2: CD.Layer, pos: CD.Position] = { contact: PW.Object _ CDSimpleRules.Contact[layer1, layer2]; additional: INT _ (sizeY-cSize)/(cSize+gap); fill: INT _ sizeY - (1+additional)*cSize; IF additional=0 THEN {[] _ PW.IncludeInCell[obj, contact, [pos.x, pos.y+fill/2]]; RETURN}; DO [] _ PW.IncludeInCell[obj, contact, pos]; IF additional=0 THEN RETURN; pos.y _ pos.y + cSize + fill/additional; fill _ fill - fill/additional; additional _ additional -1; ENDLOOP}; sizeY: INT _ MAX[pin.max, cSize]; posYLt: INT _ pin.min-pin.max/2; posYRt: INT _ pin.min-sizeY/2; pinObjLt: CD.Object _ CDSymbolicObjects.CreatePin[[MIN[pin.max, minWidth/2], pin.max]]; pinObjRt: CD.Object _ CDSymbolicObjects.CreatePin[[MIN[sizeY, minWidth/2], sizeY]]; pinInstLt: CD.Instance; pinInstRt: CD.Instance; route: CD.Layer _ (IF routeRef=CD.undefLayer THEN pin.layer ELSE routeRef); level3: BOOL _ pin.layer=pol AND route=met2 OR pin.layer=met2 AND route=pol; SELECT TRUE FROM pin.layer=route => { -- this one is one level InclRct[ route, [minWidth, pin.max], [0, posYLt]]; pinObjRt _ pinObjLt; posYRt _ posYLt}; level3 => { -- this one is three level sizeX: INT _ offset + cSize + gap + cSize; InclRct[ pin.layer, [offset + cSize, pin.max], [0, posYLt]]; Stitch[ pin.layer, met, [offset, posYRt]]; InclRct[ met, [2*cSize+gap, sizeY], [offset, posYRt]]; Stitch[ met, route, [sizeX-cSize, posYRt]]; InclRct[ route, [minWidth-sizeX+cSize, sizeY], [sizeX-cSize, posYRt]]}; maxLevel=3 => { -- this one is two level and maxLevel=3 sizeX: INT _ offset + cSize + gap + cSize; InclRct[ pin.layer, [offset+2*cSize+gap, pin.max], [0, posYLt]]; Stitch[ pin.layer, route, [sizeX-cSize, posYRt]]; InclRct[ route, [minWidth-sizeX+cSize, sizeY], [sizeX-cSize, posYRt]]}; ENDCASE => { -- this one is two level and maxLevel=2 sizeX: INT _ offset + cSize; InclRct[ pin.layer, [offset + cSize, pin.max], [0, posYLt]]; Stitch[ pin.layer, route, [sizeX-cSize, posYRt]]; InclRct[ route, [minWidth-sizeX+cSize, sizeY], [sizeX-cSize, posYRt]]}; pinInstLt _ PW.IncludeInCell[obj, pinObjLt, [0, posYLt]]; pinInstRt _ PW.IncludeInCell[obj, pinObjRt, [minWidth-pinObjRt.size.x, posYRt]]; CDSymbolicObjects.SetName[pinInstLt, pin.name]; CDSymbolicObjects.SetName[pinInstRt, pin.name]; CDSymbolicObjects.SetLayer[pinInstLt, pin.layer]; CDSymbolicObjects.SetLayer[pinInstRt, route] }; routeRef: CD.Layer _ IF routingLayerDes=NIL THEN CD.undefLayer ELSE CDSimpleRules.GetLayer[$cmosB, routingLayerDes]; offset: INT _ CDSimpleRules.MinDist[met2, met2]; gap: INT _ 2 * CD.FetchTechnology[$cmosB].lambda; cSize: INT _ CDSimpleRules.Contact[met, met2].size.x; lay1x: INT _ 0; lay2x: INT _ 0; lay3x: INT _ 0; maxLevel: INT _ 0; height: INT _ 0; pins: LIST OF NWMML; obj _ PW.CreateEmptyCell[]; IF template = NIL THEN ERROR; pins _ LoadNormalPinList[template, objSide, offset]; IF pins=NIL THEN Signal[]; FOR list: LIST OF NWMML _ pins, list.rest WHILE list#NIL DO maxLevel _ MAX[ maxLevel, SELECT TRUE FROM routeRef=CD.undefLayer => 1, routeRef=list.first.layer => 1, list.first.layer=pol AND routeRef=met2 => 3, list.first.layer=met2 AND routeRef=pol => 3, ENDCASE => 2 ] ENDLOOP; minWidth _ MAX[ minWidth, SELECT maxLevel FROM 1 => 0, 2 => offset + cSize, ENDCASE => offset + cSize + gap + cSize]; IF minWidth=0 THEN RETURN[NIL]; FOR list: LIST OF NWMML _ pins, list.rest WHILE list#NIL DO IncludeXferPin[list.first] ENDLOOP; height _ SELECT objSide FROM left, right => PWC.InterestSize[template].y, ENDCASE => PWC.InterestSize[template].x; CDCells.SetInterestRect[obj, [0, 0, minWidth, height]]; IF NOT CDCells.RepositionCell[obj, NIL] THEN Signal[]; IF objSide#right THEN obj _ PW.ChangeOrientation[obj, SideXfrm[right, objSide]]}; TestMergePwr: PROC = { design: CD.Design _ PW.OpenDesign["CoreGlueTest"]; vgTemplateO: CD.Object _ PW.Get[design, "VGTemplate"]; -- center pinsTemplateO: CD.Object _ PW.Get[design, "PinsTemplate"]; -- top side pads vgTemplate: CellType _ CoreLibrary.ObjCell[vgTemplateO, "VGTemplate"]; pinsTemplate: CellType _ CoreLibrary.ObjCell[pinsTemplateO, "PinsTemplate"]; merge: CellType _ MergePwrPins[vgTemplate, pinsTemplate, bottom, "metal2"]; mergeO: CD.Object _ PWC.Layout[merge]; combinedO: CD.Object _ PW.AbutListY[LIST[vgTemplateO, mergeO, pinsTemplateO]]; [ ] _ PW.Draw[combinedO]; Signal[]}; log: IO.STREAM _ CoreFrame.GetLog[]; END. œCoreGlueXlate.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Derived from: PWRoute.XferPins Last Edited by: Curry, August 18, 1986 4:50:36 pm PDT NormalPin: PROC[inst: CD.Instance, obj: CD.Object, from, to: Side] RETURNS[pin: NWMML] = { objIR: CD.Rect _ CDOrient.MapRect[ itemInCell: CD.InterestRect[obj], -- uses inst.orientation cellSize: obj.size, cellInstOrient: SideXfrm[from, to], cellInstPos: [0,0] ]; rect: CD.Rect _ CDOrient.MapRect[ itemInCell: CDInstances.InstRectO[inst], -- uses inst.orientation cellSize: obj.size, cellInstOrient: SideXfrm[from, to], cellInstPos: [0,0] ]; IF PWPins.GetSide[obj, inst].side#from THEN RETURN[NIL]; pin _ NEW[Pin _ [pos: 0, size: 0, layer: CDSymbolicObjects.GetLayer[inst], name: CDSymbolicObjects.GetName[inst] ] ]; pin.pos _ SELECT to FROM top, bottom => (rect.x2 + rect.x1)/2 - objIR.x1, ENDCASE => (rect.y2 + rect.y1)/2 - objIR.y1; pin.size _ SELECT to FROM top, bottom => rect.x2-rect.x1, ENDCASE => rect.y2-rect.y1}; LoadNormalPinList: PROC[cell: CellType, from, to: Side] RETURNS[list: LIST OF NWMML] = { pinProc: PWPins.InstanceEnumerator = { pin: NWMML _ NormalPin[inst, obj, from, to]; IF pin=NIL THEN RETURN; list _ CONS[pin, list]}; [] _ PWPins.EnumerateEdgePins[obj, pinProc]}; IF pinTemplateSide#right THEN merg _ PW.ChangeOrientation[merg, SideXfrm[right, pinTemplateSide]]; cell _ CoreLibrary.ObjCell[merg, CoreName.ID["MrgPwr"] ]}; Κ]˜– "Cedar" stylešœ™Icode– "Cedar" stylešœ Οmœ1™