DIRECTORY CD, CDBasics, CDCells, CDOrient, CDPinObjects, CDProperties, CDRects, CDSymbolicObjects, Convert, PW, PWPins, PWRoute, Rope, Route, RouteUtil, RTBasic, SC, SCChanUtil, SCInstUtil, SCPrivate, SCRowUtil, SCUtil; SCRouteImpl: CEDAR PROGRAM IMPORTS CD, CDBasics, CDCells, CDPinObjects, CDProperties, CDRects, CDSymbolicObjects, Convert, PW, PWPins, PWRoute, Rope, RouteUtil, RTBasic, SCChanUtil, SCInstUtil, SCRowUtil, SCUtil EXPORTS SCPrivate SHARES SC = BEGIN RowCellArray: TYPE = ARRAY [1..SCPrivate.maxLgRows] OF PW.Object; RowCells: TYPE = REF RowCellArray; NumSeqRec: TYPE = RECORD [ c: SEQUENCE size: NAT OF REF SC.Number]; NumSeq: TYPE = REF NumSeqRec; EachPinProc: TYPE = PROC [instance: SCPrivate.Instance, netPin: SCPrivate.PinNet, side: SC.Side]; LRSide: TYPE = SCPrivate.LRSide; TBSide: TYPE = SCPrivate.TBSide; LayoutData: TYPE = SCPrivate.LayoutData; ROPE: TYPE = Rope.ROPE; PQPos: TYPE = RTBasic.PQPos; SideObjs: TYPE = REF SideObjsRec; SideObjsRec: TYPE = ARRAY LRSide OF PW.ListOb; IRSize: PROC [obj: PW.Object] RETURNS [size: CD.Position] = {size _ CDBasics.SizeOfRect[CD.InterestRect[obj]]}; CreatePinsForChannel: PROC [handle: SC.Handle, chan: NAT, rowCells: RowCells] = BEGIN side: SC.Side; row: NAT; externOnly: BOOLEAN; layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; lgRows: SCPrivate.LgRows _ layoutData.lgRows; MakeNetPin: SCInstUtil.EachPinProc = { IF netPin.net #NIL AND (~externOnly OR (externOnly AND netPin.net.externNet = externalNet)) THEN { IF side = SCInstUtil.PosOf[instance, netPin.pin].sideOn THEN { rect: CD.Rect _ SCInstUtil.RotateRect[instance, netPin.pin.rect]; pinOb: CD.Object _ CDPinObjects.CreatePinOb[CDBasics.SizeOfRect[rect]]; position: CD.Position _ SCUtil.PQToXY[handle, [p: instance.offset + rect.x1, q: rect.y1]]; pinInst: CD.Instance; IF ~externOnly AND netPin.net.pinsOnChan <= 1 THEN RETURN; pinInst _ PW.IncludeInCell[cell: rowCells[row], obj: pinOb, position: position]; CDPinObjects.SetName[pinInst, netPin.net.name]; CDPinObjects.SetLayer[pinInst, netPin.pin.layer]}}; }; ResetInstCount: EachPinProc = { IF netPin.net # NIL AND side = SCInstUtil.PosOf[instance, netPin.pin].sideOn THEN netPin.net.pinsOnChan _ 0; }; CountInsts: EachPinProc = { IF netPin.net # NIL AND side = SCInstUtil.PosOf[instance, netPin.pin].sideOn THEN netPin.net.pinsOnChan _ netPin.net.pinsOnChan + 1; }; ForEachExtInstance: SCRowUtil.EachInstProc = { externOnly _ TRUE; [] _ SCInstUtil.EnumeratePinsOnInst[instance, MakeNetPin]}; ForEachIntInstance: SCRowUtil.EachInstProc = { externOnly _ FALSE; [] _ SCInstUtil.EnumeratePinsOnInst[instance, MakeNetPin]}; IF chan = 1 THEN { side _ bottom; row _ 1; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, row, ForEachExtInstance]} ELSE IF chan = lgRows.count + 1 THEN { side _ top; row _ lgRows.count; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, row, ForEachExtInstance]} ELSE { EnumeratePinsOnChan[handle, chan, CountInsts]; side _ top; row _ chan - 1; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, row, ForEachIntInstance]; side _ bottom; row _ chan; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, row, ForEachIntInstance]; EnumeratePinsOnChan[handle, chan, ResetInstCount]; }; END; EnumeratePinsOnChan: PROC [handle: SC.Handle, chan: NAT, action: EachPinProc] = { side: SC.Side; row: NAT; ForEachPin: SCInstUtil.EachPinProc = { action[instance, netPin, side]}; ForEachIntInstance: SCRowUtil.EachInstProc = { [] _ SCInstUtil.EnumeratePinsOnInst[instance, ForEachPin]}; side _ top; row _ chan - 1; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, row, ForEachIntInstance]; side _ bottom; row _ chan; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, row, ForEachIntInstance]; }; CreateExitsForChannel: PROC [handle: SC.Handle, rowChan: SCPrivate.RowChan, exitCells: SideObjs] = BEGIN cell: CD.Object; trunkWidth: SC.Number _ handle.rules.rowRules.trunkWidth; CreateNewExit: SCChanUtil.EachExitProc = { pinOb: CD.Object _ CDPinObjects.CreatePinOb[size: [trunkWidth, trunkWidth]]; pinInst: CD.Instance _ PW.IncludeInCell[cell: cell, obj: pinOb]; CDPinObjects.SetName[pinInst, exit.net.name]; CDPinObjects.SetLayer[pinInst, exit.layer]; exit.net.pinsOnChan _ exit.net.pinsOnChan + 1; }; FOR side: LRSide IN LRSide DO rect: CD.Rect; cell _ PW.CreateEmptyCell[]; [] _ SCChanUtil.EnumerateExits[handle, rowChan, side, CreateNewExit]; SELECT side FROM -- pw needs a better way of finding sides left => rect _ [-trunkWidth, -trunkWidth, trunkWidth, 2*trunkWidth]; right => rect _ [0, -trunkWidth, 2*trunkWidth, 2*trunkWidth]; ENDCASE; CDCells.SetInterestRect[cell, rect]; -- set interestRect of cell PW.RepositionCell[cell]; exitCells[side] _ CONS[cell, exitCells[side]]; ENDLOOP; END; CreateWireAndContact: PROC [handle: SC.Handle, pin: PW.Instance, size: Route.Position, length: CD.Number, trunkLayer: CD.Layer] RETURNS [obj: CD.Object] = { branchLayer: CD.Layer _ CDPinObjects.GetLayer[pin]; obj _ PW.CreateEmptyCell[]; [] _ PW.IncludeInCell[obj, CreateWireFromPin[handle, pin, length]]; RouteUtil.AddVia[obj, CDPinObjects.GetName[pin], [length-size.x/2, size.y/2], size, trunkLayer, branchLayer]; PW.RepositionCell[obj]; }; CreateWireFromPin: PROC [handle: SC.Handle, pin: PW.Instance, length: CD.Number] RETURNS [wire: CD.Object] = BEGIN wx: CD.Number _ length; wy: CD.Number _ pin.ob.size.y; wSize: CD.Position _ SCUtil.PQToXY[handle, [wx, wy]]; layer: CD.Layer _ CDPinObjects.GetLayer[pin]; wire _ CDRects.CreateRect[wSize, layer]; END; AlwaysExtendPin: PROC [handle: SC.Handle, inst: PW.Instance, extLen: CD.Number] RETURNS [obj: CD.Object _ NIL] = BEGIN IF CDSymbolicObjects.IsPin[inst.ob] THEN { pinInst: CD.Instance; pinSize: CD.Position _ SCUtil.PQToXY[handle, [inst.ob.size.x, inst.ob.size.y]]; wire: CD.Object _ CreateWireFromPin[handle, inst, extLen]; pinOb: CD.Object _ CDPinObjects.CreatePinOb[pinSize]; obj _ PW.CreateEmptyCell[]; pinInst _ PW.IncludeInCell[obj, pinOb, [extLen-inst.ob.size.x, 0]]; [] _ PW.IncludeInCell[obj, wire]; CDPinObjects.SetName[pinInst, CDPinObjects.GetName[inst]]; CDPinObjects.SetLayer[pinInst, CDPinObjects.GetLayer[inst]]; PW.RepositionCell[obj]}; END; ExtendPowerBuses: PROC [handle: SC.Handle, rowList: PW.ListOb] RETURNS [rows: PW.ListOb _ NIL] = BEGIN extLen: CD.Number; side: LRSide; rl: PW.ListOb _ NIL; abutOb: PW.Object; pwSide: PWPins.Side; rowNum: NAT _ 0; layout: LayoutData _ NARROW[handle.layoutData]; maxRowLength: CD.Number _ SCRowUtil.FindMaxRow[handle].maxRowWidth; ExtendPin: PW.ForEachPinProc = { IF Rope.Equal[CDPinObjects.GetName[inst], layout.powerBuses[side].name] THEN obj _ AlwaysExtendPin[handle, inst, extLen]}; FOR rl _ rowList, rl.rest UNTIL rl = NIL DO rowLength: CD.Number _ IRSize[rl.first].x; rowNum _ rowNum + 1; IF rowLength < maxRowLength THEN { ext: ARRAY LRSide OF PW.Object _ ALL[NIL]; FOR side IN LRSide DO extLen _ SELECT side FROM left => (maxRowLength - rowLength)/2, right => maxRowLength - rowLength - (maxRowLength - rowLength)/2, ENDCASE => ERROR; pwSide _ SELECT side FROM right => right, left => left, ENDCASE => ERROR; ext[side] _ TransferCell[template: rl.first, objSide: pwSide, width: extLen, objProc: ExtendPin, stopEnumerateDeepPins: FALSE]; ENDLOOP; abutOb _ PW.AbutListX[LIST[ext[left], rl.first, ext[right]]]} ELSE abutOb _ rl.first; rows _ CONS[IncludeCell[abutOb, Rope.Cat["Row",Convert.RopeFromInt[rowNum]], FALSE], rows]; ENDLOOP; END; CreateRowCell: PROC [handle: SC.Handle, row: NAT, rowList: PW.ListOb, rowCells: RowCells] RETURNS [PW.ListOb] = BEGIN rowOb: PW.Object _ NIL; listOb: PW.ListOb _ NIL; rCell: CD.Object _ NIL; -- cell obtained from rowOb ForEachInstance: SCRowUtil.EachInstProc = { thisObj: CD.Object _ instance.object.cdOb; listOb _ CONS[thisObj, listOb]}; [] _ SCRowUtil.EnumerateAllInstsOnRow[handle, row, ForEachInstance]; listOb _ PW.Reverse[listOb]; rowOb _ PW.AbutListX[listOb]; rCell _ IncludeCell[rowOb, Rope.Cat["Row", Convert.RopeFromInt[row]], TRUE]; rowList _ CONS[rCell, rowList]; rowCells[row] _ rCell; RETURN[rowList]; END; LogicRoute: PROC [handle: SC.Handle] RETURNS [logicOb: PW.Object] = BEGIN exitCells: SideObjs _ NEW[SideObjsRec _ ALL[NIL]]; rowList: PW.ListOb _ NIL; rowCells: RowCells _ NEW[RowCellArray]; layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; rowChans: SCPrivate.RowChans _ layoutData.rowChans; parms: SCPrivate.Parms _ NARROW[handle.parms]; routerParams: PWRoute.RouterParams _ NEW[ PWRoute.RouterParamsRec _ [handle.rules.horizLayer, handle.rules.vertLayer, parms.cdDesign.technology.key]]; abutOb: PW.Object; ForEachRow: SCRowUtil.EachRowProc = { rowList _ CreateRowCell[handle, row, rowList, rowCells]; }; ForEachChannel: SCChanUtil.EachRowChanProc = { IF 1 < rowChan.chanNum AND rowChan.chanNum < rowChans.count THEN CreateExitsForChannel[handle: handle, rowChan: rowChan, exitCells: exitCells]; CreatePinsForChannel[handle: handle, chan: chan, rowCells: rowCells]; }; SCInstUtil.AllOffsets[handle]; [] _ SCRowUtil.EnumerateRows[handle: handle, eachRow: ForEachRow]; [] _ SCChanUtil.EnumerateRowChans[handle: handle, eachRowChan: ForEachChannel]; rowList _ ExtendPowerBuses[handle, rowList]; exitCells[left] _ PW.Reverse[exitCells[left]]; exitCells[right] _ PW.Reverse[exitCells[right]]; abutOb _ PWRoute.AbutChRouteListY[ listOb: rowList, leftListOb: exitCells[left], rightListOb: exitCells[right], params: routerParams -- ! Route.Signal => RESUME -- ]; logicOb _ IncludeCell[abutOb, Rope.Cat[handle.name, "Logic"], FALSE]; END; IncludeTrunkPin: PROC [handle: SC.Handle, cell: CD.Object, name: ROPE, tbSide: TBSide, trunkSize, trunkPos: PQPos, layer: CD.Layer] = BEGIN pin: CD.Object _ CDPinObjects.CreatePinOb[[trunkSize.p, trunkSize.p]]; pos: CD.Position; pinInst: CD.Instance; trunkPos.q _ trunkPos.q + (IF tbSide = top THEN trunkSize.q - trunkSize.p ELSE 0); pos _ SCUtil.PQToXY[handle, trunkPos]; pinInst _ PW.IncludeInCell[cell, pin, pos]; CDPinObjects.SetName[pinInst, name]; CDPinObjects.SetLayer[pinInst, layer]; END; PowerRoute: PROC [handle: SC.Handle, pwOb: PW.Object] RETURNS [ob: PW.Object] = BEGIN ext: ARRAY LRSide OF PW.Object _ ALL[NIL]; trunkLayer: CD.Layer _ handle.rules.sideRules.trunkLayer; pwSide: PWPins.Side; pBus: SCPrivate.PowerBus; side: LRSide; trunk, abutOb: CD.Object; trunkSize, trunkPos: PQPos; extLen, width, trunkWidth: SC.Number; layout: LayoutData _ NARROW[handle.layoutData]; ExtendPin: PW.ForEachPinProc = { name: ROPE _ CDPinObjects.GetName[inst]; otherPBus: SCPrivate.PowerBus = layout.powerBuses[RTBasic.OtherSide[side]]; SELECT TRUE FROM Rope.Equal[name, pBus.name] => obj _ CreateWireAndContact[handle, inst, [trunkWidth, IRSize[inst.ob].y], width, trunkLayer]; Rope.Equal[name, otherPBus.name] => NULL; ENDCASE => obj _ AlwaysExtendPin[handle, inst, width]}; FOR side IN LRSide DO pBus _ layout.powerBuses[side]; width _ pBus.width; trunkWidth _ pBus.net.trunkWidth; extLen _ width - trunkWidth; trunkSize _ [p: trunkWidth, q: IRSize[pwOb].y]; trunkPos _ [IF side = right THEN extLen ELSE 0, 0]; pwSide _ SELECT side FROM right => right, left => left, ENDCASE => ERROR; ext[side] _ TransferCell[template: pwOb, objSide: pwSide, width: width, objProc: ExtendPin, name: IF side = right THEN "RPower" ELSE "LPower", stopEnumerateDeepPins: FALSE]; trunk _ MakeTrunk[handle: handle, trunkSize: trunkSize, lev: trunkLayer]; [] _ PW.IncludeInCell[ext[side], trunk, SCUtil.PQToXY[handle, trunkPos]]; FOR tbSide: TBSide IN TBSide DO IncludeTrunkPin[handle, ext[side], pBus.name, tbSide, trunkSize, trunkPos , trunkLayer]; ENDLOOP; ENDLOOP; abutOb _ PW.AbutListX[LIST [ext[left], pwOb, ext[right]]]; ob _ IncludeCell[abutOb, Rope.Cat[handle.name, "WPower"], FALSE]; END; MakeTrunk: PROC [handle: SC.Handle, trunkSize: PQPos, lev: CD.Layer] RETURNS [obj: CD.Object] = BEGIN obj _ CDRects.CreateRect[SCUtil.PQToXY[handle, trunkSize], lev]; END; AcBusForName: PROC [handle: SC.Handle, side: LRSide, name: ROPE] RETURNS [acBus: SCPrivate.AcBus _ NIL, i: NAT] = BEGIN layout: LayoutData _ NARROW[handle.layoutData]; sigs: SCPrivate.AcBusSigs _ layout.acBuses[side].sigs; FOR i IN [0..sigs.size) DO IF Rope.Equal[name, sigs[i].name] THEN RETURN [sigs[i], i]; ENDLOOP; END; ComputeTrunkEnd: PROC [trunkEnd: NumSeq, q: SC.Number, index: NAT, fromTB: TBSide] = BEGIN prev: REF SC.Number _ trunkEnd[index]; trunkEnd[index] _ NEW[SC.Number]; trunkEnd[index]^ _ IF prev = NIL THEN q ELSE SELECT fromTB FROM top => MIN[q, prev^], bottom => MAX[q, prev^], ENDCASE => ERROR; END; CreateAcTrunks: PROC [handle: SC.Handle, cell: CD.Object, acBusSide: SCPrivate.AcBusSides, trunkEnd: NumSeq, layer: CD.Layer] = BEGIN side: LRSide; trunk: CD.Object; trunkSize, trunkPos: PQPos; bus: SCPrivate.AcBus; FOR i: NAT IN [0..acBusSide.sigs.size) DO bus _ acBusSide.sigs[i]; side _ bus.onSide; trunkPos.p _ SELECT side FROM left => IRSize[cell].x - bus.depth, right => bus.depth, ENDCASE => ERROR; trunkPos.q _ SELECT bus.fromTB FROM bottom => 0, top => trunkEnd[i]^, ENDCASE => ERROR; trunkSize.p _ bus.net.trunkWidth; trunkSize.q _ SELECT bus.fromTB FROM bottom => trunkEnd[i]^, top => IRSize[cell].y - trunkEnd[i]^, ENDCASE => ERROR; trunk _ MakeTrunk[handle: handle, trunkSize: trunkSize, lev: layer]; [] _ PW.IncludeInCell[cell, trunk, SCUtil.PQToXY[handle, trunkPos]]; IncludeTrunkPin[handle, cell, bus.name, bus.fromTB, trunkSize, trunkPos, layer]; ENDLOOP; END; ClockRoute: PROC [handle: SC.Handle, pwOb: PW.Object] RETURNS [ob: PW.Object] = BEGIN trunkEnd: NumSeq; -- "q" coordinate of trunk end point ext: ARRAY LRSide OF PW.Object _ ALL[NIL]; trunkLayer: CD.Layer _ handle.rules.sideRules.trunkLayer; side: LRSide; abutOb: CD.Object; width: SC.Number; layout: LayoutData _ NARROW[handle.layoutData]; ExtendPin: PW.ForEachPinProc = { pinName: ROPE _ CDPinObjects.GetName[inst]; acBus: SCPrivate.AcBus; sigIndex: NAT; [acBus, sigIndex] _ AcBusForName[handle, side, pinName]; IF acBus = NIL THEN obj _ AlwaysExtendPin[handle, inst, width] ELSE { ComputeTrunkEnd[trunkEnd: trunkEnd, q: inst.location.y, index: sigIndex, fromTB: acBus.fromTB]; obj _ CreateWireAndContact[handle, inst, [acBus.net.trunkWidth, acBus.net.branchWidth], acBus.depth, trunkLayer]}}; FOR side IN LRSide DO pwSide: PWPins.Side; acBusSide: SCPrivate.AcBusSides _ layout.acBuses[side]; IF acBusSide = NIL OR acBusSide.sigs = NIL THEN LOOP; trunkEnd _ NEW[NumSeqRec[acBusSide.sigs.size]]; FOR i: NAT IN [0..trunkEnd.size) DO trunkEnd[i] _ NIL; ENDLOOP; pwSide _ SELECT side FROM right => right, left => left, ENDCASE => ERROR; width _ acBusSide.width; ext[side] _ TransferCell[template: pwOb, objSide: pwSide, width: width, objProc: ExtendPin, stopEnumerateDeepPins: FALSE]; CreateAcTrunks[handle, ext[side], acBusSide, trunkEnd, trunkLayer]; ENDLOOP; abutOb _ PW.AbutListX[LIST [ext[left], pwOb, ext[right]]]; ob _ IncludeCell[abutOb, Rope.Cat[handle.name, "WClocks"], FALSE]; END; IORoute: PROC [handle: SC.Handle, pwOb: PW.Object] RETURNS [ob: PW.Object] = BEGIN layout: LayoutData _ NARROW[handle.layoutData]; bpRows: SCPrivate.BpRows _ layout.bpRows; FOR side: SC.Side IN SC.Side DO IF bpRows[side].fnlBpFxd THEN { }; ENDLOOP; END; DetailRoute: PUBLIC PROC [handle: SC.Handle] RETURNS [result: SC.Result] = BEGIN pwOb: PW.Object _ NIL; pwOb _ LogicRoute[handle]; pwOb _ PowerRoute[handle, pwOb]; pwOb _ ClockRoute[handle, pwOb]; result _ NEW[SC.ResultRec _ []]; result.object _ pwOb; result.handle _ handle; END; IncludeCell: PROC [rowOb: PW.Object, name: Rope.ROPE _ NIL, StopEnumerateDeepPins: BOOLEAN _ TRUE] RETURNS [rCell: CD.Object] = BEGIN rCellPtr: CD.CellPtr; inst: CD.Instance; rCell _ PW.CreateEmptyCell[]; rCellPtr _ NARROW [rCell.specificRef]; inst _ NEW [CD.InstanceRep _ [ob: rowOb]]; IF StopEnumerateDeepPins THEN CDProperties.PutPropOnInstance[inst, $StopEnumerateDeepPins, $StopEnumerateDeepPins]; rCellPtr.contents _ CONS [inst, rCellPtr.contents]; CDCells.SetInterestRect[rCell, CD.InterestRect[rowOb]]; [] _ PW.RepositionCell[rCell]; END; TransferCell: PROC [template: PW.Object, objSide: PWPins.Side, width: INT, objProc: PW.ForEachPinProc, name: Rope.ROPE _ "Transfer", stopEnumerateDeepPins: BOOLEAN _ TRUE] RETURNS [cell: PW.Object] = BEGIN KeepPinOnEdge: PWPins.InstanceEnumerator = BEGIN newObj: PW.Object; side: PWPins.Side _ PWPins.GetSide[template, inst].side; IF side=objSide THEN { newObj _ objProc[inst]; IF newObj=NIL THEN RETURN; [] _ PW.IncludeInCell[cell, newObj, Position[inst, template, objSide], SideToOrient[objSide]]; }; END; iRect: CD.Rect; IF objSide=none THEN ERROR; cell _ PW.CreateEmptyCell[]; iRect _ CD.InterestRect[template]; -- copy interestRect of obj CDCells.SetInterestRect[cell, IRect[iRect, width, objSide]]; -- set interestRect of cell [] _ PWPins.EnumerateEdgePins[template, KeepPinOnEdge, stopEnumerateDeepPins]; PW.RepositionCell[cell]; END; IRect: PROC [templateRect: CD.Rect, otherDim: INT, side: PWPins.Side] RETURNS [iRect: CD.Rect] = BEGIN iRect _ SELECT side FROM top, bottom => [0, 0, templateRect.x2-templateRect.x1, otherDim], left, right => [0, 0, otherDim, templateRect.y2-templateRect.y1], ENDCASE => ERROR; END; SideToOrient: PROC [side: PWPins.Side] RETURNS [orient: CD.Orientation] ~ { orient _ SELECT side FROM bottom => CDOrient.rotate270, right => CDOrient.original, top => CDOrient.rotate90, left => CDOrient.rotate180, ENDCASE => ERROR; }; Position: PROC [inst: CD.Instance, template: PW.Object, side: PWPins.Side] RETURNS [position: CD.Position] = BEGIN position _ PW.GetLocation[inst, template]; SELECT side FROM top, bottom => position.y _ 0; left, right => position.x _ 0; ENDCASE => ERROR; END; END. °SCRouteImpl.mesa: Implementation of SC.DetailedRoute Frank Bowers February 15, 1986 12:40:54 pm PST Preas December 6, 1985 5:35:17 pm PST : PowerBus width, and transfercell call in PowerRoute Bryan Preas March 24, 1986 12:12:38 pm PST [instance: SCPrivate.Instance, pin: NAT, netPin: SCPrivate.PinNet] create wire and copy pin to end of wire extend power buses to length of longest row ext end power pins on edges and put in power rails [inst: Instance] RETURNS [obj: Object]; expand bprow to length of corresponding cell's side. PWRoute.AbutRoute to pwOb SCInstutil.BpPos right side => position in bp bottom => origin lower left hand side top => lower right hand side origin left => upper left side handle: for each side for which side.fnlBpFxd invoke PWRoute.AbutRoute else extend exit pins to edge of cell pwOb _ IORoute[handle, pwOb]; -- Start with an empty cell of appropriate interestRect (origin in 0,0) -- Parse the pins ΚD˜šœ5™5Icodešœ.™.K™[K™*K™—šΟk ˜ Kšœ˜K˜ K˜K˜ K˜ K˜ K˜K˜K˜Kšœ˜K˜K˜K˜K˜K˜ K˜Kšœ˜Kšœ ˜ K˜ K˜ Kšœ ˜ Kšœ˜—J˜šΟn œœ˜JšœœVœW˜ΉJšœ ˜Jšœœ˜—J˜Jš œœœœœ˜AJšœ œœ˜"šœ œœ˜Jš œœœœœœ ˜(—Jšœœœ ˜šœ œœ@œ˜aJ˜—Jšœœ˜ Jšœœ˜ Jšœ œ˜(Jšœœœ˜Jšœœ˜Jšœ œœ ˜!š œ œœœœ˜.J˜—š žœœœ œœ ˜;Jšœœ˜3J˜—šžœœ œœ˜OJš˜Jšœœ˜Jšœœ˜ Jšœ œ˜Kšœ#œ˜=Kšœ-˜-K˜šž œ˜&JšΟi$ΠikŸ™BJ™šœ œœ˜Jšœ œ œ&œ˜Mšœ6œ˜>Jšœœ9˜AJšœœ>˜GJšœ œN˜ZJšœ œ ˜Jšœ œœœ˜:Jšœ œD˜PJšœ/˜/Jšœ3˜3——J˜J˜—šžœ˜Jšœœœ6œ˜lJ˜J˜—šž œ˜Jšœœœ6œ3˜„J˜J˜—šžœ˜.Jšœ œ˜Jšœ;˜;J˜—šžœ˜.Jšœ œ˜Jšœ;˜;J˜—šœ œ˜J˜JšœG˜G—šœœœ˜&J˜JšœG˜G—šœ˜Jšœ.˜.J˜JšœG˜GJ˜JšœG˜GJšœ2˜2Jšœ˜—Jšœ˜J˜—šžœœ œœ˜RJšœœ˜Jšœœ˜ J˜šž œ˜&Jšœ!˜!—J˜šžœ˜.Jšœ;˜;—J˜J˜JšœG˜GJ˜JšœG˜GJšœ˜J˜J˜—šžœœ œ;˜bJš˜Jšœœ˜Jšœ œ+˜9J˜šž œ˜*KšœœC˜LJšœ œ œ'˜@Jšœ-˜-Jšœ+˜+J˜.J˜J˜—šœœ˜Jšœœ˜Jšœœ˜KšœE˜EšœœΟc)˜:JšœD˜DJšœ=˜=Jšœ˜—Jšœ%‘˜@Jšœ˜Kšœœ˜.Kšœ˜—Jšœ˜J˜—š žœœ œœ)œœ˜Jšœœ ˜J˜Jšœ œ$˜3Jšœœ˜Jšœœ<˜CJšœm˜mJšœ˜Jšœ˜J˜—š žœœ œœœ˜PJšœœ ˜Jš˜Jšœœ˜Jšœœ˜Jšœœ,˜5Jšœœ$˜-Jšœ(˜(Jšœ˜J˜—š žœœ œœœ ˜PJšœœ œ˜ Jš˜J™'J˜šœ"œ˜*Jšœ œ ˜Jšœ œD˜OJšœœ2˜:Jšœœ,˜5Jšœœ˜Jšœ œ7˜CJšœœ˜!Jšœ:˜:Jšœ<˜Jšœœ œ˜!Jš˜Jšœ+™+J˜Jšœœ˜J˜ Jšœœ œ˜Jšœœ˜J˜Jšœœ˜Jšœœ˜/Jšœœ3˜CJ˜šž œœ˜ šœF˜LJšœ-˜-——J˜šœœœ˜+Kšœ œ˜*K˜šœœ˜"Jš œœ œœ œœ˜+šœœ˜šœ œ˜Kšœ%˜%KšœA˜AKšœœ˜—Kš œ œœœœ˜IKšœxœ˜Kšœ˜—Jšœ œ œ#˜=—Jšœ˜JšœœBœ ˜[Kšœ˜—Jšœ˜J˜—š ž œœ œœ œ˜YJšœœ ˜Jš˜Jšœœ œ˜Jšœœ œ˜Jšœœ œ‘˜4J˜šžœ˜+Jšœ œ˜*Jšœ œ˜ —J˜JšœD˜DJšœ œ˜Jšœœ˜JšœFœ˜LJ˜Jšœ œ˜Jšœ˜Jšœ ˜Jšœ˜J˜—š ž œœ œ œ œ ˜DJš˜Jšœœœœ˜2Jšœ œ œ˜Jšœœ˜'Kšœ#œ˜=Kšœ3˜3Jšœœ˜/Jšœ%œn˜–Jšœœ˜J˜šž œ˜%J˜8Jšœ˜J˜—šžœ ˜.šœœ"˜@J˜N—J˜EJ˜—J˜Jšœ˜JšœB˜BJšœO˜OJ˜,Jšœœ˜.Jšœœ˜0šœ"˜"Jšœb˜bJš‘œ˜!—Jšœ>œ˜EJšœ˜J˜—š žœœ œœœ5œ ˜…Jš˜Jšœœ?˜FJšœœ ˜Jšœ œ ˜Jšœœœœ˜RJšœ&˜&Jšœ œ˜+Jšœ$˜$Jšœ&˜&Jšœ˜J˜—šž œœ œœ˜5Jšœœ ˜Jš˜Jšœ3™3J™Jš œœ œœ œœ˜+Jšœ œ+˜9J˜J˜J˜ Jšœœ˜Jšœ˜Jšœœ˜%Jšœœ˜/J˜šž œœ˜ Jšœœ˜(JšœK˜Kšœœ˜šœ˜Jšœ^˜^—Jšœ$œ˜)Jšœ0˜7—J˜—šœœ˜Kšœ˜Kšœ˜K˜!K˜Kšœ/˜/Kšœ œœœ˜3Kš œ œœœœ˜IKš œbœœ œ"œ˜­KšœI˜IKšœœB˜Išœœ˜KšœX˜XKšœ˜—Kšœ˜—Jšœ œ œ ˜:Jšœ:œ˜AJšœ˜J˜—šž œœ œ œ˜DJšœœ ˜Jš˜Kšœ@˜@Jšœ˜J˜—šž œœ œœœœœ˜qJš˜Jšœœ˜/Jšœ6˜6šœœ˜Jšœ œœ˜;Jšœ˜—Jšœ˜J˜—šžœœœœ˜TJš˜Jšœœœ˜&Jšœœœ ˜!J˜š œœœœœœ˜?Jšœœ ˜Jšœ œ ˜Jšœœ˜—Jšœ˜J˜—š žœœ œœCœ ˜Jš˜J˜ Jšœœ˜Jšœ˜J˜J˜šœœœ˜)Jšœ˜J˜šœ œ˜Jšœ8œœ˜I—šœ œ ˜#Jšœ"œœ˜3—J˜!šœœ ˜$Jšœ>œœ˜O—JšœD˜DJšœœ=˜DJšœP˜PKšœ˜—Jšœ˜J˜—šž œœ œœ˜5Jšœœ ˜Jš˜Jšœ‘$˜6Jš œœœœ œœ˜*Jšœ œ+˜9J˜ Jšœœ˜Jšœœ˜Jšœœ˜/J˜šž œœ˜ Jšœœ™'J™Jšœ œ˜+Jšœ˜Jšœ œ˜Jšœ8˜8Jšœ œœ+˜>šœ˜Jšœ_˜_Jšœs˜s——J˜šœœ˜J˜Jšœ7˜7Jš œ œœœœœ˜5Jšœ œ!˜/Jš œœœœœœ˜?Kš œ œœœœ˜IJšœ˜Jšœsœ˜zJ˜CJšœ˜—Jšœ œ œ ˜:Jšœ;œ˜BJšœ˜J˜—šžœœ œœ˜2Jšœœ ˜Jš˜Jšœœ˜/J˜)J˜š œœœœ˜Jšœœ˜J™5J™J™w™J˜—Jšœ˜J˜—JšœK™KJšœ ™ Jšœ˜J˜—š ž œœœ œ œ œ ˜KJš˜Jšœœ œ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ œœ˜ J˜J˜Jšœ™Jšœ˜J˜—šž œœ œœœžœœœœ œ ˜€Jš˜Jšœ œ ˜Jšœœ ˜J˜Jšœœ˜Jšœ œ˜&Jšœœœ˜*šœ˜JšœU˜U—Jšœœ˜3Jšœœ˜7Jšœœ˜J˜Jšœ˜J˜—šž œœ œ&œ œœ&œœœœ ˜ΘJš˜šž œ˜+Jš˜Jšœœ˜Jšœ8˜8šœœ˜Jš œœœœœ˜2JšœœW˜^J˜—Jšœ˜—Jšœœ˜Jšœ+‘™GJšœœœ˜Jšœœ˜Jšœœ‘˜@Jšœ=‘˜XJ™JšœN˜NJšœ˜Jšœ˜J˜—š žœœœœœ œ ˜aJš˜šœœ˜JšœA˜AJšœA˜AJšœœ˜—Jšœ˜J˜—šž œœœ œ˜Kšœ œ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ˜—K˜J˜—š žœœœœœ œ ˜mJš˜Jšœ œ˜*šœ˜Jšœ˜Jšœ˜Jšœœ˜—Jšœ˜—šœ˜J˜J˜J˜J˜J˜J˜—J˜—…—D†\z