DIRECTORY Basics, Cabbage, CabbagePrivate, CD, CDBasics, CDCells, CDOps, CDProperties, CDRects, CDSimpleRules, CDSymbolicObjects, Connections, Core, CoreGeometry, CoreOps, Convert, HashTable, List, PW, PWRoute, Rope, Route, RouteUtil, RTBasic, Sinix, SinixOps; CabbageProcsImpl: CEDAR PROGRAM IMPORTS Basics, Cabbage, CD, CDBasics, CDCells, CDOps, CDProperties, CDRects, CDSimpleRules, CDSymbolicObjects, Connections, CoreGeometry, CoreOps, Convert, HashTable, List, PW, PWRoute, Rope, Route, RouteUtil, RTBasic, Sinix, SinixOps EXPORTS CabbagePrivate SHARES Route = { cabbageXPos: ATOM _ $CabbageXPos; cabbageYPos: ATOM _ $CabbageYPos; CreateHandle: PUBLIC PROC [inner, bottomLeft, bottom, bottomRight, right, topRight, top, topLeft, left: Cabbage.Object, connections: Connections.Table, parms: Cabbage.PadRingParams, name: Rope.ROPE, routeType: CabbagePrivate.RouteType] RETURNS [handle: CabbagePrivate.Handle]~ { handle _ NEW[CabbagePrivate.HandleRec _ [name: name, connections: connections, parms: parms]]; handle.routeType _ routeType; handle.rules _ CreateRouterParms[parms]; handle.powerDistribution _ NEW[CabbagePrivate.PowerDistributionRec]; handle.globalRouting _ NEW[CabbagePrivate.GlobalRoutingRec]; IF routeType = normal THEN handle.detailedRouting _ NEW[CabbagePrivate.DetailedRoutingRec] ELSE handle.detailedRoutingPL _ NEW[CabbagePrivate.DetailedRoutingPLRec]; GetSizes[handle, inner, bottomLeft, bottom, bottomRight, right, topRight, top, topLeft, left]; IF handle.connections # NIL THEN InitWires[handle]; AssignPositions[handle]}; CreateRouterParms: PROC [parms: Cabbage.PadRingParams] RETURNS [designRules: CabbagePrivate.DesignRules] = { hLayer: CD.Layer _ CDSimpleRules.GetLayer[parms.technologyKey, parms.horizLayer]; vLayer: CD.Layer _ CDSimpleRules.GetLayer[parms.technologyKey, parms.vertLayer]; viaTable: HashTable.Table _ IF parms.viaTable # NIL THEN parms.viaTable ELSE HashTable.Create[equal: EqualProc, hash: HashProc]; designRules _ NEW[CabbagePrivate.DesignRulesRec]; designRules.horizParms.parms _ NEW[PWRoute.RouterParamsRec _ [trunkLayer: parms.horizLayer, branchLayer: parms.vertLayer, technologyKey: parms.technologyKey, wireWidthProc: parms.wireWidthProc, signalIncomplete: parms.signalIncomplete, signalBreakAtExit: FALSE, signalSinglePinNets: parms.signalSinglePinNets, viaTable: viaTable]]; designRules.vertParms.parms _ NEW[PWRoute.RouterParamsRec _ [trunkLayer: parms.vertLayer, branchLayer: parms.horizLayer, technologyKey: parms.technologyKey, wireWidthProc: parms.wireWidthProc, signalIncomplete: parms.signalIncomplete, signalBreakAtExit: FALSE, signalSinglePinNets: parms.signalSinglePinNets, viaTable: viaTable]]; designRules.horizParms.rules _ Route.CreateDesignRules[parms.technologyKey, hLayer, vLayer, horizontal]; designRules.vertParms.rules _ Route.CreateDesignRules[parms.technologyKey, hLayer, vLayer, vertical]; designRules.horizParms.rules.trunkToEdge _ designRules.horizParms.rules.trunkToTrunk; designRules.vertParms.rules.trunkToEdge _ designRules.vertParms.rules.trunkToTrunk}; GetSizes: PROC [handle: CabbagePrivate.Handle, inner, bottomLeft, bottom, bottomRight, right, topRight, top, topLeft, left: Cabbage.Object] ~ { lambda: INT _ handle.rules.horizParms.rules.CDLambda; handle.inner _ GetObjectDescription[inner, lambda, lambda]; handle.bottom _ GetObjectDescription[bottom, lambda, lambda]; handle.right _ GetObjectDescription[right, lambda, lambda]; handle.top _ GetObjectDescription[top, lambda, lambda]; handle.left _ GetObjectDescription[left, lambda, lambda]; handle.bottomLeft _ GetObjectDescription[bottomLeft, handle.left.size.x, handle.bottom.size.y]; handle.bottomRight _ GetObjectDescription[bottomRight, handle.right.size.x, handle.bottom.size.y]; handle.topRight _ GetObjectDescription[topRight, handle.right.size.x, handle.top.size.y]; handle.topLeft _ GetObjectDescription[topLeft, handle.left.size.x, handle.top.size.y]}; GetObjectDescription: PROC [object: Cabbage.Object, minX, minY: INT] RETURNS [desc: CabbagePrivate.ObjectDescription] ~ { desc.object _ object; IF object = NIL THEN desc.size _ [minX, minY] ELSE { desc.size _ RTBasic.IRSize[object]; desc.size.x _ MAX[minX, desc.size.x]; desc.size.y _ MAX[minY, desc.size.y]}}; AssignPositions: PROC [handle: CabbagePrivate.Handle] ~ { hBottom, hTop, vLeft, vRight: INT; [hBottom, hTop, vLeft, vRight] _ GetSize[handle]; handle.bottom.origin _ [handle.bottomLeft.size.x + (handle.size.x - hBottom)/2, 0]; handle.right.origin _ [handle.size.x - handle.right.size.x, handle.bottomRight.size.y + (handle.size.y - vRight)/2]; handle.top.origin _ [handle.topLeft.size.x + (handle.size.x - hTop)/2, handle.size.y - handle.top.size.y]; handle.left.origin _ [0, handle.bottomLeft.size.y + (handle.size.y - vLeft)/2]; DoCorners[handle]; DoRoutingAreas[handle]}; InitWires: PROC [handle: CabbagePrivate.Handle] ~ { LookForGndAndVdd: Connections.EachNetAction ~ { IF Rope.Equal[net.name, "Vdd"] THEN handle.vddNet _ net ELSE IF Rope.Equal[net.name, "Gnd"] THEN handle.gndNet _ net}; EachNet: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { newNet.width _ MAX[newNet.width, segment.range.max - segment.range.min]}; newNet: Connections.Net _ NEW[Connections.NetRec _ [name: net.name, width: net.width]]; IF net.width <= 0 THEN [] _ Connections.EnumerateSegments[net, EachSegment]; [] _ Connections.Store[handle.widthTable, net.name, newNet]}; handle.widthTable _ Connections.CreateForRopes[]; [] _ Connections.EnumerateNets[handle.connections, LookForGndAndVdd]; [] _ Connections.EnumerateNets[handle.connections, EachNet]}; CheckInnerPos: PUBLIC PROC [handle: CabbagePrivate.Handle, innerPos: CD.Position] ~ { actualInnerPos: CD.Position _ innerPos; lowerY: INT _ handle.bottom.origin.y + handle.bottom.size.y; upperY: INT _ handle.top.origin.y - handle.inner.size.y; lowerX: INT _ handle.left.origin.x + handle.left.size.x; upperX: INT _ handle.right.origin.x - handle.inner.size.x; IF ~((lowerX <= innerPos.x AND innerPos.x <= upperX) AND (lowerY <= innerPos.y AND innerPos.y <= upperY)) THEN { Cabbage.Signal[callingError, "The position specified for the inner object (innerPos) is invalid."]; actualInnerPos _ [(lowerX + upperX)/2, (lowerY + lowerY)/2]}; handle.inner.origin _ actualInnerPos}; DistributePower: PUBLIC PROC [handle: CabbagePrivate.Handle] ~ { horizParms: CabbagePrivate.ParmSet _ handle.rules.horizParms; vertParms: CabbagePrivate.ParmSet _ handle.rules.vertParms; space: INT _ 2*horizParms.rules.trunkToTrunk + horizParms.rules.trunkSpacing + vertParms.rules.contactSize + vertParms.rules.trunkSpacing + 2*vertParms.rules.trunkToTrunk; trunkWidth: INT _ (handle.parms.powerCellWidth - space)/2; RouteOutsideChannels[handle, trunkWidth]; AssignPositions[handle]; ConstructPowerChannels[handle, trunkWidth]; AssignPositions[handle]; RouteOutsideSwitchBoxes[handle]}; RouteOutsideChannels: PROC [handle: CabbagePrivate.Handle, viaSize: INT] ~ { outerChanWidth: INT _ handle.parms.outerChanWidth; powerWidth: INT _ outerChanWidth + handle.parms.powerCellWidth; horizParms: CabbagePrivate.ParmSet _ handle.rules.horizParms; vertParms: CabbagePrivate.ParmSet _ handle.rules.vertParms; horizInnerRange: Connections.Range _ [handle.inner.origin.x, handle.inner.origin.x + handle.inner.size.x]; horizOuterRange: Connections.Range _ [handle.left.origin.x + handle.left.size.x + powerWidth, handle.right.origin.x - powerWidth]; vertInnerRange: Connections.Range _ [handle.inner.origin.y, handle.inner.origin.y + handle.inner.size.y]; vertOuterRange: Connections.Range _ [handle.bottom.origin.y + handle.bottom.size.y + powerWidth, handle.top.origin.y - powerWidth]; handle.powerDistribution.channels[bottom] _ RouteOutsideChannel[handle, handle.bottom, bottom, horizOuterRange, horizOuterRange, outerChanWidth, viaSize, vertParms]; handle.powerDistribution.channels[right] _ RouteOutsideChannel[handle, handle.right, right, vertInnerRange, vertOuterRange, outerChanWidth, viaSize, horizParms]; handle.powerDistribution.channels[top] _ RouteOutsideChannel[handle, handle.top, top, horizOuterRange, horizOuterRange, outerChanWidth, viaSize, vertParms]; handle.powerDistribution.channels[left] _ RouteOutsideChannel[handle, handle.left, left, vertInnerRange, vertOuterRange, outerChanWidth, viaSize, horizParms]}; ConstructPowerChannels: PROC [handle: CabbagePrivate.Handle, trunkWidth: INT] ~ { powerCellWidth: INT _ handle.parms.powerCellWidth; powerWidth: INT _ handle.parms.outerChanWidth + powerCellWidth; horizParms: CabbagePrivate.ParmSet _ handle.rules.horizParms; vertParms: CabbagePrivate.ParmSet _ handle.rules.vertParms; horizInnerRange: Connections.Range _ [handle.inner.origin.x, handle.inner.origin.x + handle.inner.size.x]; horizOuterRange: Connections.Range _ [handle.left.origin.x + handle.left.size.x + powerWidth, handle.right.origin.x - powerWidth]; vertInnerRange: Connections.Range _ [handle.inner.origin.y, handle.inner.origin.y + handle.inner.size.y]; vertOuterRange: Connections.Range _ [handle.bottom.origin.y + handle.bottom.size.y + powerWidth, handle.top.origin.y - powerWidth]; handle.powerDistribution.power[bottom] _ RoutePower[handle, handle.powerDistribution.channels[bottom], bottom, horizOuterRange, powerCellWidth, trunkWidth, horizParms, vertParms]; handle.powerDistribution.power[right] _ RoutePower[handle, handle.powerDistribution.channels[right], right, vertOuterRange, powerCellWidth, trunkWidth, vertParms, horizParms]; handle.powerDistribution.power[top] _ RoutePower[handle, handle.powerDistribution.channels[top], top, horizOuterRange, powerCellWidth, trunkWidth, horizParms, vertParms]; handle.powerDistribution.power[left] _ RoutePower[handle, handle.powerDistribution.channels[left], left, vertOuterRange, powerCellWidth, trunkWidth, vertParms, horizParms]}; RouteOutsideChannel: PROC [handle: CabbagePrivate.Handle, outerObject: CabbagePrivate.ObjectDescription, -- outer Cabbage object side: Route.Side, -- the side of chip for which this channel is being constructed innerRange, outerRange: Connections.Range, -- innerRange is the span of the inner cell, outerRange the span of the appropriate power routing cell; innerRange may equal outerRange chWidth: INT, -- the only permitted width for channel viaSize: INT, -- the length of the via at the end of the channel parms: CabbagePrivate.ParmSet] RETURNS [channel: CabbagePrivate.Channel] ~ { result: Route.RoutingResult; retrieveRect: Route.RefRect; rect: CD.Rect; otherSide: Route.Side _ RTBasic.OtherSide[side]; blSide: Route.Side _ IF side=bottom OR side=top THEN left ELSE bottom; topOrRight: BOOLEAN _ side= right OR side=top; direction: RTBasic.Direction _ IF side= right OR side=left THEN vertical ELSE horizontal; obj1: Cabbage.Object _ IF topOrRight THEN MappedShellFromObject[handle, outerObject, otherSide, innerRange, outerRange, viaSize, parms.rules] ELSE ShellFromObject[handle, outerObject, otherSide, outerRange, parms.rules, TRUE]; obj2: Cabbage.Object _ IF topOrRight THEN ShellFromObject[handle, outerObject, otherSide, outerRange, parms.rules, TRUE] ELSE MappedShellFromObject[handle, outerObject, otherSide, innerRange, outerRange, viaSize, parms.rules]; bottomOrLeftObj: Cabbage.Object _ ShellFromOutsideEnd[handle, outerObject, otherSide, blSide, innerRange, outerRange, parms.rules]; topOrRightObj: Cabbage.Object _ ShellFromOutsideEnd[handle, outerObject, otherSide, RTBasic.OtherSide[blSide], innerRange, outerRange, parms.rules]; parms.parms.wireWidthProc _ GetWireWidth; parms.parms.makeTabKeyProc _ PinName; parms.parms.context _ NEW[PWRouteContext _ [direction: direction, table: handle.widthTable]]; result _ PWRoute.DoRoute[obj1, obj2, bottomOrLeftObj, topOrRightObj, parms.parms, side= right OR side=left, channel]; rect _ result.routingRect; SELECT side FROM bottom => { dist: INT _ MIN[rect.y1, rect.y2 - chWidth]; retrieveRect _ NEW[CD.Rect _ [rect.x1, dist, rect.x2, rect.y2]]}; right => { dist: INT _ MAX[rect.x2, rect.x1 + chWidth]; retrieveRect _ NEW[CD.Rect _ [rect.x1, rect.y1, dist, rect.y2]]}; top => { dist: INT _ MAX[rect.y2, rect.y1 + chWidth]; retrieveRect _ NEW[CD.Rect _ [rect.x1, rect.y1, rect.x2, dist]]}; left => { dist: INT _ MIN[rect.x1, rect.x2 - chWidth]; retrieveRect _ NEW[CD.Rect _ [dist, rect.y1, rect.x2, rect.y2]]}; ENDCASE; channel.object _ PWRoute.GetRouting[result, retrieveRect, parms.parms]; channel.size _ RTBasic.IRSize[channel.object]; channel.cellType _ ExtractChannel[channel, parms]}; RoutePower: PROC [handle: CabbagePrivate.Handle, channel: CabbagePrivate.Channel, -- inner or outer channel as determined by side chipSide: Route.Side, -- the side of the chip for which this channel is being constructed range: Connections.Range, -- the span along the appropriate power routing cell powerWidth: INT, -- the only permitted width for the power cell trunkWidth: INT, -- the width of the power trunk insideParms, outsideParms: CabbagePrivate.ParmSet] RETURNS [power: CabbagePrivate.Channel] ~ { ChanPublics: CoreGeometry.EachWirePinProc ~ { OuterPowerBranch: PROC [branchLength: INT, branchLayer, trunkLayer: CD.Layer] ~ { pos: CD.Position _ SELECT chipSide FROM bottom => [adjustedRange.min, 0], top => [adjustedRange.min, powerWidth - branchLength], left => [0, adjustedRange.min], right => [powerWidth - branchLength, adjustedRange.min], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; SELECT chipSide FROM bottom, top => CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[max-min, branchLength], branchLayer], pos], $SignalName, name]; left, right => CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[branchLength, max-min], branchLayer], pos], $SignalName, name]; ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; IF branchLayer # trunkLayer THEN { via: CD.Object _ SELECT chipSide FROM bottom, top => RouteUtil.StitchVias[[max-min, trunkWidth], trunkLayer, branchLayer, insideParms.rules.CDLambda, NIL], left, right => RouteUtil.StitchVias[[trunkWidth, max-min], trunkLayer, branchLayer, insideParms.rules.CDLambda, NIL], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; irSize: CD.Position _ RTBasic.IRSize[via]; xOffset: INT _ SELECT chipSide FROM bottom, top => (max-min - irSize.x)/2, left, right => (trunkWidth - irSize.x)/2, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; yOffset: INT _ SELECT chipSide FROM bottom, top => (trunkWidth - irSize.y)/2, left, right => (max-min - irSize.y)/2, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; viaPos: CD.Position _ SELECT chipSide FROM bottom => [adjustedRange.min + xOffset, branchLength - trunkWidth + yOffset], top => [adjustedRange.min + xOffset, powerWidth - branchLength + yOffset], left => [branchLength - trunkWidth + xOffset, adjustedRange.min + yOffset], right => [powerWidth - branchLength + xOffset, adjustedRange.min + yOffset], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; CDProperties.PutProp[RouteUtil.Include[object, via, viaPos], $SignalName, name]}}; SignalBranch: PROC [] ~ { outsideLength: INT _ outsideParms.rules.trunkSpacing + distToOuter; insideLength: INT _ insideParms.rules.trunkSpacing + distToInner; SELECT chipSide FROM bottom => { CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[max-min, outsideLength], outsideLayer], [adjustedRange.min, 0]], $SignalName, name]; CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[max-min, insideLength], insideLayer], [adjustedRange.min, powerWidth - insideLength]], $SignalName, name]; [] _ RouteUtil.Include[object, RouteUtil.StitchVias[[max-min, insideParms.rules.contactSize], insideLayer, outsideLayer, insideParms.rules.CDLambda, NIL], [adjustedRange.min, outsideLength]]}; top => { CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[max-min, outsideLength], outsideLayer], [adjustedRange.min, powerWidth - outsideLength]], $SignalName, name]; CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[max-min, insideLength], insideLayer], [adjustedRange.min, 0]], $SignalName, name]; [] _ RouteUtil.Include[object, RouteUtil.StitchVias[[max-min, insideParms.rules.contactSize], insideLayer, outsideLayer, insideParms.rules.CDLambda, NIL], [adjustedRange.min, insideLength]]}; left => { CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[outsideLength, max-min], outsideLayer], [0, adjustedRange.min]], $SignalName, name]; CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[insideLength, max-min], insideLayer], [powerWidth - insideLength, adjustedRange.min]], $SignalName, name]; [] _ RouteUtil.Include[object, RouteUtil.StitchVias[[insideParms.rules.contactSize, max-min], insideLayer, outsideLayer, insideParms.rules.CDLambda, NIL], [outsideLength, adjustedRange.min]]}; right => { CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[outsideLength, max-min], outsideLayer], [powerWidth - outsideLength, adjustedRange.min]], $SignalName, name]; CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[insideLength, max-min], insideLayer], [0, adjustedRange.min]], $SignalName, name]; [] _ RouteUtil.Include[object, RouteUtil.StitchVias[[insideParms.rules.contactSize, max-min], insideLayer, outsideLayer, insideParms.rules.CDLambda, NIL], [insideLength, adjustedRange.min]]}; ENDCASE}; name: Rope.ROPE _ CoreOps.GetShortWireName[wire]; adjustedRange: Connections.Range _ [channelOrigin + min, channelOrigin + max]; IF side = otherSide THEN { SELECT TRUE FROM Rope.Equal[name, "Vdd"] => OuterPowerBranch[distToOuter, outsideLayer, insideLayer]; Rope.Equal[name, "Gnd"] => OuterPowerBranch[powerWidth - 2*insideParms.rules.trunkToTrunk, outsideLayer, outsideLayer]; ENDCASE => SignalBranch[]}}; EachNet: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { InnerPowerBranch: PROC [branchLength: INT, branchLayer, trunkLayer: CD.Layer] ~ { rectPos: CD.Position _ SELECT chipSide FROM bottom => [adjustedRange.min, powerWidth - branchLength], top => [adjustedRange.min, 0], left => [powerWidth - branchLength, adjustedRange.min], right => [0, adjustedRange.min], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; SELECT chipSide FROM bottom, top => CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[segment.range.max-segment.range.min, branchLength], insideLayer], rectPos], $SignalName, net.name]; left, right => CDProperties.PutProp[RouteUtil.Include[object, CDRects.CreateRect[[branchLength, segment.range.max-segment.range.min], insideLayer], rectPos], $SignalName, net.name]; ENDCASE; IF branchLayer # trunkLayer THEN { via: CD.Object _ SELECT chipSide FROM bottom, top => RouteUtil.StitchVias[[segment.range.max-segment.range.min, trunkWidth], trunkLayer, branchLayer, insideParms.rules.CDLambda, NIL], left, right => RouteUtil.StitchVias[[trunkWidth, segment.range.max-segment.range.min], trunkLayer, branchLayer, insideParms.rules.CDLambda, NIL], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; irSize: CD.Position _ RTBasic.IRSize[via]; xOffset: INT _ SELECT chipSide FROM bottom, top => (segment.range.max-segment.range.min - irSize.x)/2, left, right => (trunkWidth - irSize.x)/2, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; yOffset: INT _ SELECT chipSide FROM bottom, top => (trunkWidth - irSize.y)/2, left, right => (segment.range.max-segment.range.min - irSize.y)/2, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; viaPos: CD.Position _ SELECT chipSide FROM bottom => [adjustedRange.min + xOffset, powerWidth - branchLength + yOffset], top => [adjustedRange.min + xOffset, branchLength - trunkWidth + yOffset], left => [powerWidth - branchLength + xOffset, adjustedRange.min + yOffset], right => [branchLength - trunkWidth + xOffset, adjustedRange.min + yOffset], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; CDProperties.PutProp[RouteUtil.Include[object, via, viaPos], $SignalName, net.name]}}; adjustedRange: Connections.Range _ [innerOrigin + segment.range.min, innerOrigin + segment.range.max]; IF segment.object = handle.inner.object AND segment.side = chipSide THEN { SELECT TRUE FROM Rope.Equal[net.name, "Vdd"] => InnerPowerBranch[powerWidth - distToOuter, insideLayer, insideLayer]; Rope.Equal[net.name, "Gnd"] => InnerPowerBranch[distToInner, insideLayer, outsideLayer]; ENDCASE}}; [] _ Connections.EnumerateSegments[net, EachSegment]}; PowerTrunk: PROC [position: CD.Position, layer, otherLayer: CD.Layer, name: Rope.ROPE, addVias: BOOLEAN] ~ { trunkSize: CD.Position _ SELECT chipSide FROM bottom, top => [range.max-range.min, trunkWidth], left, right => [trunkWidth, range.max-range.min], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; trunk: CD.Object _ CDRects.CreateRect[trunkSize, layer]; CDProperties.PutProp[RouteUtil.Include[object, trunk, position], $SignalName, name]; IF addVias THEN { via: CD.Object _ RouteUtil.StitchVias[[trunkWidth, trunkWidth], layer, otherLayer, insideParms.rules.CDLambda, NIL]; irSize: CD.Position _ RTBasic.IRSize[via]; viaPosition: CD.Position _ SELECT chipSide FROM bottom, top => [range.max-range.min - irSize.x, (trunkWidth-irSize.y)/2], left, right => [(trunkWidth-irSize.x)/2, range.max-range.min - irSize.y], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; branch: CD.Object _ CDRects.CreateRect[[trunkWidth, trunkWidth], otherLayer]; branchPosition: CD.Position _ SELECT chipSide FROM bottom, top => [range.max-range.min - trunkWidth, 0], left, right => [0, range.max-range.min - trunkWidth], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; CDProperties.PutProp[RouteUtil.Include[object, branch, position], $SignalName, name]; CDProperties.PutProp[RouteUtil.Include[object, branch, CDBasics.AddPoints[position, branchPosition]], $SignalName, name]; CDProperties.PutProp[RouteUtil.Include[object, via, position], $SignalName, name]; CDProperties.PutProp[RouteUtil.Include[object, via, CDBasics.AddPoints[position, viaPosition]], $SignalName, name]}}; insideLayer: CD.Layer _ insideParms.rules.branchLayer; outsideLayer: CD.Layer _ outsideParms.rules.branchLayer; distToInner: INT _ trunkWidth + 2*insideParms.rules.trunkToTrunk; distToOuter: INT _ trunkWidth + 2*outsideParms.rules.trunkToTrunk; object: Cabbage.Object _ CDCells.CreateEmptyCell[]; mode: Sinix.Mode = SinixOps.GetExtractMode[insideParms.rules.technology]; otherSide: CoreGeometry.Side _ SELECT chipSide FROM bottom => top, right => left, top => bottom, left=> right, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; innerOrigin: INT _ SELECT chipSide FROM bottom, top => handle.inner.origin.x, right, left => handle.inner.origin.y, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; channelOrigin: INT _ SELECT chipSide FROM bottom, top => channel.origin.x, right, left => channel.origin.y, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; interestRect: CD.Rect _ SELECT chipSide FROM bottom, top => [range.min, 0 , range.max, powerWidth], left, right => [0, range.min , powerWidth, range.max], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; innerTrunkPos: CD.Position _ SELECT chipSide FROM bottom => [range.min, powerWidth - distToInner], top => [range.min, 2*insideParms.rules.trunkToTrunk], left => [powerWidth - distToInner, range.min], right => [2*insideParms.rules.trunkToTrunk, range.min], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; outerTrunkPos: CD.Position _ SELECT chipSide FROM bottom => [range.min, 2*outsideParms.rules.trunkToTrunk], top => [range.min, powerWidth - distToOuter], left => [2*outsideParms.rules.trunkToTrunk, range.min], right => [powerWidth - distToOuter, range.min], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; PowerTrunk[outerTrunkPos, insideLayer, outsideLayer, "Vdd", FALSE]; PowerTrunk[innerTrunkPos, outsideLayer, insideLayer, "Gnd", TRUE]; [] _ CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, channel.cellType, ChanPublics]; [] _ Connections.EnumerateNets[handle.connections, EachNet]; power.object _ object; CDCells.SetInterestRect[design: NIL, cell: power.object, r: interestRect]; RTBasic.RepositionCell[power.object]; power.size _ RTBasic.IRSize[power.object]; power.cellType _ ExtractChannel[power, insideParms]}; RouteOutsideSwitchBoxes: PROC [handle: CabbagePrivate.Handle] ~ { vertParms: CabbagePrivate.ParmSet _ handle.rules.vertParms; powerWidth: INT _ handle.parms.outerChanWidth + handle.parms.powerCellWidth; leftHorizRange: Connections.Range _ [handle.left.origin.x + handle.left.size.x, handle.left.origin.x + handle.left.size.x + powerWidth]; rightHorizRange: Connections.Range _ [handle.right.origin.x - powerWidth, handle.right.origin.x]; bottomVertRange: Connections.Range _ [handle.bottom.origin.y + handle.bottom.size.y, handle.bottom.origin.y + handle.bottom.size.y + powerWidth]; topVertRange: Connections.Range _ [handle.top.origin.y- powerWidth, handle.top.origin.y]; vertParms.parms.okToDiddleLLPins _ TRUE; handle.powerDistribution.switchBoxes[bottomLeft] _ RouteOutsideSwitchBox[handle, bottomLeft, leftHorizRange, bottomVertRange, vertParms]; handle.powerDistribution.switchBoxes[topLeft] _ RouteOutsideSwitchBox[handle, topLeft, leftHorizRange, topVertRange, vertParms]; vertParms.parms.okToDiddleLLPins _ FALSE; vertParms.parms.okToDiddleURPins _ TRUE; handle.powerDistribution.switchBoxes[bottomRight] _ RouteOutsideSwitchBox[handle, bottomRight, rightHorizRange, bottomVertRange, vertParms]; handle.powerDistribution.switchBoxes[topRight] _ RouteOutsideSwitchBox[handle, topRight, rightHorizRange, topVertRange, vertParms]; vertParms.parms.okToDiddleLLPins _ FALSE; vertParms.parms.okToDiddleURPins _ FALSE}; RouteOutsideSwitchBox: PROC [handle: CabbagePrivate.Handle, corner: CabbagePrivate.Corners, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet] RETURNS [switchBox: CabbagePrivate.Channel] ~ { bottomObject, topObject, leftObject, rightObject: Cabbage.Object; SELECT corner FROM bottomLeft => { topObject _ PW.AbutListX[LIST[ShellFromFullChannel[handle.powerDistribution.channels[left], bottom, parms.rules.branchLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.power[left], bottom, parms.rules.branchLayer, parms.rules]]]; bottomObject _ ShellFromObject[handle, handle.bottom, top, horizRange, parms.rules, FALSE]; leftObject _ ShellFromObject[handle, handle.left, right, vertRange, parms.rules, FALSE]; rightObject _ PW.AbutListY[LIST[ShellFromFullChannel[handle.powerDistribution.channels[bottom], left, parms.rules.trunkLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.power[bottom], left, parms.rules.trunkLayer, parms.rules]]]}; bottomRight => { topObject _ PW.AbutListX[LIST[ShellFromFullChannel[handle.powerDistribution.power[right], bottom, parms.rules.branchLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.channels[right], bottom, parms.rules.branchLayer, parms.rules]]]; bottomObject _ ShellFromObject[handle, handle.bottom, top, horizRange, parms.rules, FALSE]; leftObject _ PW.AbutListY[LIST[ShellFromFullChannel[handle.powerDistribution.channels[bottom], right, parms.rules.trunkLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.power[bottom], right, parms.rules.trunkLayer, parms.rules]]]; rightObject _ ShellFromObject[handle, handle.right, left, vertRange, parms.rules, FALSE]}; topRight => { topObject _ ShellFromObject[handle, handle.top, bottom, horizRange, parms.rules, FALSE]; bottomObject _ PW.AbutListX[LIST[ShellFromFullChannel[handle.powerDistribution.power[right], top, parms.rules.branchLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.channels[right], top, parms.rules.branchLayer, parms.rules]]]; leftObject _ PW.AbutListY[LIST[ShellFromFullChannel[handle.powerDistribution.power[top], right, parms.rules.trunkLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.channels[top], right, parms.rules.trunkLayer, parms.rules]]]; rightObject _ ShellFromObject[handle, handle.right, left, vertRange, parms.rules, FALSE]}; topLeft => { topObject _ ShellFromObject[handle, handle.top, bottom, horizRange, parms.rules, FALSE]; bottomObject _ PW.AbutListX[LIST[ShellFromFullChannel[handle.powerDistribution.channels[left], top, parms.rules.branchLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.power[left], top, parms.rules.branchLayer, parms.rules]]]; leftObject _ ShellFromObject[handle, handle.left, right, vertRange, parms.rules, FALSE]; rightObject _ PW.AbutListY[LIST[ShellFromFullChannel[handle.powerDistribution.power[top], left, parms.rules.trunkLayer, parms.rules], ShellFromFullChannel[handle.powerDistribution.channels[top], left, parms.rules.trunkLayer, parms.rules]]]}; ENDCASE; parms.parms.wireWidthProc _ NIL; parms.parms.makeTabKeyProc _ NIL; parms.parms.context _ NEW[PWRouteContext _ [direction: parms.rules.trunkDirection, table: handle.widthTable]]; switchBox.object _ PWRoute.MakeChannel[bottomObject, topObject, leftObject, rightObject, NIL, parms.parms, FALSE, switchBox]; switchBox.size _ RTBasic.IRSize[switchBox.object]; switchBox.cellType _ ExtractChannel[switchBox, parms]}; GlobalRoute: PUBLIC PROC [handle: CabbagePrivate.Handle] ~ { EachNet: Connections.EachNetAction ~ { sortedSegments: CabbagePrivate.SegmentSeq _ SortSegments[handle, net]; IF sortedSegments # NIL THEN { segmentPair: CabbagePrivate.SegmentPair _ FindStartEndSegments[handle, net, sortedSegments]; AddNetToGlobalRoute[handle, net, segmentPair]}}; newConnections: Connections.Table _ MakeNewConnections[handle]; [] _ Connections.EnumerateNets[newConnections, EachNet]}; MakeNewConnections: PROC [handle: CabbagePrivate.Handle] RETURNS [newConnections: Connections.Table _ Connections.CreateForRopes[]] ~ { InsertPinsFromInner[handle, newConnections]; InsertPinsFromPower[handle, newConnections, bottom]; InsertPinsFromPower[handle, newConnections, right]; InsertPinsFromPower[handle, newConnections, top]; InsertPinsFromPower[handle, newConnections, left]}; InsertPinsFromInner: PROC [handle: CabbagePrivate.Handle, newConnections: Connections.Table] ~ { EachNet: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { IF segment.object = handle.inner.object THEN { newSegment: Connections.Segment _ NEW[Connections.SegmentRec _ [name: segment.name, object: segment.object, range: segment.range, side: segment.side, layer: segment.layer]]; newNet.segments _ CONS[newSegment, newNet.segments]; [] _ Connections.Store[newConnections, net.name, newNet]}}; newNet: Connections.Net _ NEW[Connections.NetRec _ [name: net.name, width: net.width]]; [] _ Connections.Store[newConnections, net.name, newNet]; [] _ Connections.EnumerateSegments[net, EachSegment]}; [] _ Connections.EnumerateNets[handle.connections, EachNet]}; InsertPinsFromPower: PROC [handle: CabbagePrivate.Handle, newConnections: Connections.Table, chipSide: RTBasic.Side] ~ { ChanPublics: CoreGeometry.EachWirePinProc ~ { name: Rope.ROPE _ CoreOps.GetShortWireName[wire]; segmentSide: Route.Side _ SELECT side FROM bottom => bottom, right => right, top => top, left=> left, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; IF segmentSide = otherSide THEN { segment: Connections.Segment _ NEW[Connections.SegmentRec _ [name: name, object: handle.powerDistribution.power[chipSide].object, range: [min, max], side: segmentSide, layer: layer]]; net: Connections.Net _ Connections.Fetch[newConnections, name].net; net.segments _ CONS[segment, net.segments]; [] _ Connections.Store[newConnections, name, net]}; }; otherSide: Route.Side _ RTBasic.OtherSide[chipSide]; mode: Sinix.Mode = SinixOps.GetExtractMode[handle.rules.horizParms.rules.technology]; [] _ CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, handle.powerDistribution.power[chipSide].cellType, ChanPublics]}; SortSegments: PROC [handle: CabbagePrivate.Handle, net: Connections.Net] RETURNS [sortedSegments: CabbagePrivate.SegmentSeq _ NIL] ~ { CountSegments: Connections.EachSegmentAction ~ {numSegments _ numSegments + 1}; PinCompare: List.CompareProc ~ { pos1, pos2: INT; TRUSTED{ pos1 _ PosOf[handle, LOOPHOLE[ref1]]; pos2 _ PosOf[handle, LOOPHOLE[ref2]]}; RETURN [Basics.CompareINT[pos1, pos2]]}; numSegments, index: INT _ 0; mungedSegmentList, sortedSegmentList: List.LORA; [] _ Connections.EnumerateSegments[net, CountSegments]; IF numSegments > 1 THEN { TRUSTED{mungedSegmentList _ List.Reverse[LOOPHOLE[net.segments]]}; sortedSegmentList _ List.Sort[mungedSegmentList, PinCompare]; sortedSegments _ NEW[CabbagePrivate.SegmentSeqRec[numSegments]]; FOR each: List.LORA _ sortedSegmentList, each.rest UNTIL each = NIL DO segment: Connections.Segment; TRUSTED{segment _ LOOPHOLE[each.first]}; sortedSegments[index] _ segment; index _ index + 1; ENDLOOP}}; FindStartEndSegments: PROC [handle: CabbagePrivate.Handle, net: Connections.Net, sortedSegments: CabbagePrivate.SegmentSeq] RETURNS [segmentPair: CabbagePrivate.SegmentPair _ [NIL, NIL]] ~ { minLength: INT _ LAST[INT]; perimeter: INT _ 2*(handle.size.x + handle.size.y); length: INT; startSeg, endSeg: Connections.Segment; FOR index: NAT IN [0 .. sortedSegments.numSegments) DO IF index < sortedSegments.numSegments - 1 THEN { startSeg _ sortedSegments[index+1]; endSeg _ sortedSegments[index]; length _ perimeter - PosOf[handle, startSeg] + PosOf[handle, endSeg]} ELSE { endSeg _ sortedSegments[index]; startSeg _ sortedSegments[0]; length _ PosOf[handle, endSeg] - PosOf[handle, startSeg]}; IF length < minLength THEN { minLength _ length; segmentPair.seg1 _ startSeg; segmentPair.seg2 _ endSeg} ENDLOOP; IF segmentPair.seg1 = NIL OR segmentPair.seg2 = NIL THEN Cabbage.Error[programmingError, "Not suppose to happen"]}; AddNetToGlobalRoute: PROC [handle: CabbagePrivate.Handle, net: Connections.Net, segmentPair: CabbagePrivate.SegmentPair] ~ { pos1: INT _ PosOf[handle, segmentPair.seg1]; pos2: INT _ PosOf[handle, segmentPair.seg2]; IF pos1 < pos2 THEN { FOR d: CabbagePrivate.Division IN CabbagePrivate.Division DO IF pos1 < PosOfDivision[handle, d] AND PosOfDivision[handle, d] < pos2 THEN handle.globalRouting.exitLists[d] _ CONS[net, handle.globalRouting.exitLists[d]] ENDLOOP} ELSE { FOR d: CabbagePrivate.Division IN CabbagePrivate.Division DO IF PosOfDivision[handle, d] > pos1 OR PosOfDivision[handle, d] < pos2 THEN handle.globalRouting.exitLists[d] _ CONS[net, handle.globalRouting.exitLists[d]] ENDLOOP}}; DetailedRoute: PUBLIC PROC [handle: CabbagePrivate.Handle] ~ { RouteChannels[handle]; AdjustPositions[handle]; IF handle.routeType = normal THEN RouteSwitchBoxes[handle] ELSE RouteSwitchBoxesPL[handle]; AdjustPositions[handle]}; RouteChannels: PROC [handle: CabbagePrivate.Handle] ~ { horizParms: CabbagePrivate.ParmSet _ handle.rules.horizParms; vertParms: CabbagePrivate.ParmSet _ handle.rules.vertParms; horizRange: Connections.Range _ [handle.inner.origin.x, handle.inner.origin.x + handle.inner.size.x]; vertRange: Connections.Range _ [handle.inner.origin.y, handle.inner.origin.y + handle.inner.size.y]; IF handle.routeType = normal THEN { handle.detailedRouting.channels[bottom] _ RouteChannel[handle, handle.powerDistribution.power[bottom], bottom, bottomLeft, bottomRight, horizRange, horizParms]; handle.detailedRouting.channels[right] _ RouteChannel[handle, handle.powerDistribution.power[right], right, rightBottom, rightTop, vertRange, vertParms]; handle.detailedRouting.channels[top] _ RouteChannel[handle, handle.powerDistribution.power[top], top, topLeft, topRight, horizRange, horizParms]; handle.detailedRouting.channels[left] _ RouteChannel[handle, handle.powerDistribution.power[left], left, leftBottom, leftTop, vertRange, vertParms]} ELSE { -- routeType = padLimited handle.detailedRoutingPL.channels[right] _ RouteChannel[handle, handle.powerDistribution.power[right], right, rightBottom, rightTop, vertRange, vertParms]; handle.detailedRoutingPL.channels[left] _ RouteChannel[handle, handle.powerDistribution.power[left], left, leftBottom, leftTop, vertRange, vertParms]}}; RouteChannel: PROC [handle: CabbagePrivate.Handle, boundingChannel: CabbagePrivate.Channel, -- the power channel to use chipSide: Route.Side, -- the side of chip for which this channel is being constructed llDiv, urDiv: CabbagePrivate.Division, -- the lower and upper boundries of the channel range: Connections.Range, -- the range of interest along the channel side parms: CabbagePrivate.ParmSet] RETURNS [channel: CabbagePrivate.Channel] ~ { result: Route.RoutingResult; retrieveRect: Route.RefRect; rect: CD.Rect; offset: INT _ parms.rules.trunkToTrunk; otherSide: Route.Side _ RTBasic.OtherSide[chipSide]; direction: RTBasic.Direction _ IF chipSide= right OR chipSide=left THEN vertical ELSE horizontal; blSide: Route.Side _ IF chipSide=bottom OR chipSide=top THEN left ELSE bottom; topOrRight: BOOLEAN _ chipSide= right OR chipSide=top; obj1: Cabbage.Object _ IF topOrRight THEN ShellFromObject[handle, handle.inner, chipSide, range, parms.rules, TRUE] ELSE ShellFromChannel[boundingChannel, otherSide, parms.rules.branchLayer, range, parms.rules]; obj2: Cabbage.Object _ IF topOrRight THEN ShellFromChannel[boundingChannel, otherSide, parms.rules.branchLayer, range, parms.rules] ELSE ShellFromObject[handle, handle.inner, chipSide, range, parms.rules, TRUE]; bottomOrLeftObj: Cabbage.Object _ ShellFromEnd[handle, llDiv, blSide, parms.rules]; topOrRightObj: Cabbage.Object _ ShellFromEnd[handle, urDiv, RTBasic.OtherSide[blSide], parms.rules]; parms.parms.wireWidthProc _ GetWireWidth; parms.parms.makeTabKeyProc _ PinName; parms.parms.context _ NEW[PWRouteContext _ [direction: direction, table: handle.widthTable]]; result _ PWRoute.DoRoute[obj1, obj2, bottomOrLeftObj, topOrRightObj, parms.parms, chipSide=right OR chipSide=left, channel]; rect _ result.routingRect; SELECT chipSide FROM bottom => { size: INT _ handle.inner.origin.y - (boundingChannel.origin.y + boundingChannel.size.y); retrieveRect _ NEW[CD.Rect _ [rect.x1, MIN[rect.y1, rect.y2 - size + offset], rect.x2, rect.y2 + offset]]}; right => { size: INT _ boundingChannel.origin.x - (handle.inner.origin.x + handle.inner.size.x); retrieveRect _ NEW[CD.Rect _ [rect.x1 - offset, rect.y1, MAX[rect.x2, rect.x1 + size - offset], rect.y2]]}; top => { size: INT _ boundingChannel.origin.y - (handle.inner.origin.y + handle.inner.size.y); retrieveRect _ NEW[CD.Rect _ [rect.x1, rect.y1 - offset, rect.x2, MAX[rect.y2, rect.x1 + size - offset]]]}; left => { size: INT _ handle.inner.origin.x - (boundingChannel.origin.x + boundingChannel.size.x); retrieveRect _ NEW[CD.Rect _ [MIN[rect.x1, rect.x2 - size + offset], rect.y1, rect.x2 + offset, rect.y2]]}; ENDCASE; channel.object _ PWRoute.GetRouting[result, retrieveRect, parms.parms]; channel.size _ RTBasic.IRSize[channel.object]; channel.cellType _ ExtractChannel[channel, parms]}; RouteSwitchBoxes: PROC [handle: CabbagePrivate.Handle] ~ { horizParms: CabbagePrivate.ParmSet _ handle.rules.horizParms; leftHorizRange: Connections.Range _ [handle.left.origin.x + handle.left.size.x, handle.inner.origin.x]; rightHorizRange: Connections.Range _ [handle.inner.origin.x + handle.inner.size.x, handle.right.origin.x]; bottomVertRange: Connections.Range _ [handle.bottom.origin.y + handle.bottom.size.y, handle.inner.origin.y]; topVertRange: Connections.Range _ [handle.inner.origin.y + handle.inner.size.y, handle.top.origin.y]; horizParms.parms.okToDiddleLLPins _ TRUE; horizParms.parms.okToDiddleURPins _ FALSE; handle.detailedRouting.switchBoxes[bottomLeft] _ RouteSwitchBox[handle, bottomLeft, leftHorizRange, bottomVertRange, horizParms]; horizParms.parms.okToDiddleLLPins _ FALSE; horizParms.parms.okToDiddleURPins _ TRUE; handle.detailedRouting.switchBoxes[bottomRight] _ RouteSwitchBox[handle, bottomRight, rightHorizRange, bottomVertRange, horizParms]; horizParms.parms.okToDiddleLLPins _ FALSE; horizParms.parms.okToDiddleURPins _ TRUE; handle.detailedRouting.switchBoxes[topRight] _ RouteSwitchBox[handle, topRight, rightHorizRange, topVertRange, horizParms]; horizParms.parms.okToDiddleLLPins _ TRUE; horizParms.parms.okToDiddleURPins _ FALSE; handle.detailedRouting.switchBoxes[topLeft] _ RouteSwitchBox[handle, topLeft, leftHorizRange, topVertRange, horizParms]; horizParms.parms.okToDiddleLLPins _ FALSE; horizParms.parms.okToDiddleURPins _ FALSE}; RouteSwitchBoxesPL: PROC [handle: CabbagePrivate.Handle] ~ { horizParms: CabbagePrivate.ParmSet _ handle.rules.horizParms; powerWidth: INT _ handle.parms.outerChanWidth + handle.parms.powerCellWidth; horizRange: Connections.Range _ [handle.left.origin.x + handle.left.size.x + powerWidth, handle.right.origin.x - powerWidth]; bottomVertRange: Connections.Range _ [handle.bottom.origin.y + handle.bottom.size.y + powerWidth, handle.inner.origin.y]; topVertRange: Connections.Range _ [handle.inner.origin.y + handle.inner.size.y, handle.top.origin.y - powerWidth]; horizParms.parms.okToDiddleLLPins _ TRUE; horizParms.parms.okToDiddleURPins _ TRUE; handle.detailedRoutingPL.switchBoxes[bottom] _ RouteSwitchBoxPL[handle, bottom, horizRange, bottomVertRange, horizParms]; handle.detailedRoutingPL.switchBoxes[top] _ RouteSwitchBoxPL[handle, top, horizRange, topVertRange, horizParms]; horizParms.parms.okToDiddleLLPins _ FALSE; horizParms.parms.okToDiddleURPins _ FALSE}; RouteSwitchBoxPL: PROC [handle: CabbagePrivate.Handle, side: RTBasic.TBSide, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet] RETURNS [switchBox: CabbagePrivate.Channel] ~ { bottomObject, topObject: Cabbage.Object; leftObject: Cabbage.Object _ ShellFromChannel[handle.powerDistribution.power[left], right, parms.rules.trunkLayer, vertRange, parms.rules]; rightObject: Cabbage.Object _ ShellFromChannel[handle.powerDistribution.power[right], left, parms.rules.trunkLayer, vertRange, parms.rules]; innerRange: Connections.Range _ [handle.inner.origin.x, handle.inner.origin.x + handle.inner.size.x]; direction: RTBasic.Direction _ IF side= right OR side=left THEN vertical ELSE horizontal; SELECT side FROM bottom => { innerObject: Cabbage.Object _ ShellFromObject[handle, handle.inner, bottom, innerRange, parms.rules, TRUE]; topObject _ PW.AbutListX[LIST[ShellFromFullChannel[handle.detailedRoutingPL.channels[left], bottom, parms.rules.branchLayer, parms.rules], innerObject, ShellFromFullChannel[handle.detailedRoutingPL.channels[right], bottom, parms.rules.branchLayer, parms.rules]]]; bottomObject _ ShellFromChannel[handle.powerDistribution.power[bottom], top, parms.rules.branchLayer, horizRange, parms.rules]}; top => { innerObject: Cabbage.Object _ ShellFromObject[handle, handle.inner, top, innerRange, parms.rules, TRUE]; bottomObject _ PW.AbutListX[LIST[ShellFromFullChannel[handle.detailedRoutingPL.channels[left], top, parms.rules.branchLayer, parms.rules], innerObject, ShellFromFullChannel[handle.detailedRoutingPL.channels[right], top, parms.rules.branchLayer, parms.rules]]]; topObject _ ShellFromChannel[handle.powerDistribution.power[top], bottom, parms.rules.branchLayer, horizRange, parms.rules]}; ENDCASE; parms.parms.wireWidthProc _ GetWireWidth; parms.parms.makeTabKeyProc _ PinName; parms.parms.context _ NEW[PWRouteContext _ [direction: direction, table: handle.widthTable]]; switchBox.object _ PWRoute.MakeChannel[bottomObject, topObject, leftObject, rightObject, NIL, parms.parms, FALSE, switchBox]; switchBox.size _ RTBasic.IRSize[switchBox.object]; switchBox.cellType _ ExtractChannel[switchBox, parms]}; RouteSwitchBox: PROC [handle: CabbagePrivate.Handle, corner: CabbagePrivate.Corners, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet] RETURNS [switchBox: CabbagePrivate.Channel] ~ { bottomObject, topObject, leftObject, rightObject: Cabbage.Object; SELECT corner FROM bottomLeft => { topObject _ ShellFromFullChannel[handle.detailedRouting.channels[left], bottom, parms.rules.branchLayer, parms.rules]; bottomObject _ ShellFromChannel[handle.powerDistribution.power[bottom], top, parms.rules.branchLayer, horizRange, parms.rules]; leftObject _ ShellFromChannel[handle.powerDistribution.power[left], right, parms.rules.trunkLayer, vertRange, parms.rules]; rightObject _ ShellFromFullChannel[handle.detailedRouting.channels[bottom], left, parms.rules.trunkLayer, parms.rules]}; bottomRight => { topObject _ ShellFromFullChannel[handle.detailedRouting.channels[right], bottom, parms.rules.branchLayer, parms.rules]; bottomObject _ ShellFromChannel[handle.powerDistribution.power[bottom], top, parms.rules.branchLayer, horizRange, parms.rules]; rightObject _ ShellFromChannel[handle.powerDistribution.power[right], left, parms.rules.trunkLayer, vertRange, parms.rules]; leftObject _ ShellFromFullChannel[handle.detailedRouting.channels[bottom], right, parms.rules.trunkLayer, parms.rules]}; topRight => { bottomObject _ ShellFromFullChannel[handle.detailedRouting.channels[right], top, parms.rules.branchLayer, parms.rules]; topObject _ ShellFromChannel[handle.powerDistribution.power[top], bottom, parms.rules.branchLayer, horizRange, parms.rules]; rightObject _ ShellFromChannel[handle.powerDistribution.power[right], left, parms.rules.trunkLayer, vertRange, parms.rules]; leftObject _ ShellFromFullChannel[handle.detailedRouting.channels[top], right, parms.rules.trunkLayer, parms.rules]}; topLeft => { bottomObject _ ShellFromFullChannel[handle.detailedRouting.channels[left], top, parms.rules.branchLayer, parms.rules]; topObject _ ShellFromChannel[handle.powerDistribution.power[top], bottom, parms.rules.branchLayer, horizRange, parms.rules]; leftObject _ ShellFromChannel[handle.powerDistribution.power[left], right, parms.rules.trunkLayer, vertRange, parms.rules]; rightObject _ ShellFromFullChannel[handle.detailedRouting.channels[top], left, parms.rules.trunkLayer, parms.rules]}; ENDCASE; parms.parms.wireWidthProc _ GetWireWidth; parms.parms.context _ handle.widthTable; switchBox.object _ PWRoute.MakeChannel[bottomObject, topObject, leftObject, rightObject, NIL, parms.parms, FALSE, switchBox]; switchBox.size _ RTBasic.IRSize[switchBox.object]; switchBox.cellType _ ExtractChannel[switchBox, parms]}; MakeChip: PUBLIC PROC [handle: CabbagePrivate.Handle] RETURNS [chip: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { IncludeObject[handle.inner.object, handle.inner.origin, chip]; IncludeObject[handle.bottomLeft.object, handle.bottomLeft.origin, chip]; IncludeObject[handle.bottom.object, handle.bottom.origin, chip]; IncludeObject[handle.bottomRight.object, handle.bottomRight.origin, chip]; IncludeObject[handle.right.object, handle.right.origin, chip]; IncludeObject[handle.topRight.object, handle.topRight.origin, chip]; IncludeObject[handle.top.object, handle.top.origin, chip]; IncludeObject[handle.topLeft.object, handle.topLeft.origin, chip]; IncludeObject[handle.left.object, handle.left.origin, chip]; IF handle.routeType = normal THEN { FOR side: Cabbage.Side IN Cabbage.Side DO IncludeObject[handle.detailedRouting.channels[side].object, handle.detailedRouting.channels[side].origin, chip]; ENDLOOP; FOR corner: CabbagePrivate.Corners IN CabbagePrivate.Corners DO IncludeObject[handle.detailedRouting.switchBoxes[corner].object, handle.detailedRouting.switchBoxes[corner].origin, chip]; ENDLOOP} ELSE { FOR side: Cabbage.Side IN RTBasic.LRSide DO IncludeObject[handle.detailedRoutingPL.channels[side].object, handle.detailedRoutingPL.channels[side].origin, chip]; ENDLOOP; FOR side: Cabbage.Side IN RTBasic.TBSide DO IncludeObject[handle.detailedRoutingPL.switchBoxes[side].object, handle.detailedRoutingPL.switchBoxes[side].origin, chip]; ENDLOOP}; FOR side: Cabbage.Side IN Cabbage.Side DO IncludeObject[handle.powerDistribution.channels[side].object, handle.powerDistribution.channels[side].origin, chip]; IncludeObject[handle.powerDistribution.power[side].object, handle.powerDistribution.power[side].origin, chip]; ENDLOOP; FOR corner: CabbagePrivate.Corners IN CabbagePrivate.Corners DO IncludeObject[handle.powerDistribution.switchBoxes[corner].object, handle.powerDistribution.switchBoxes[corner].origin, chip]; ENDLOOP; [] _ RTBasic.RepositionCell[chip]}; MappedShellFromObject: PROC [handle: CabbagePrivate.Handle, objectDes: CabbagePrivate.ObjectDescription, pinSide: Route.Side, -- the side of the object on which pins are to be considered innerRange, outerRange: Connections.Range, -- innerRange is the span of the inner cell, outerRangethe span of the appropriate power routing cell viaSize: INT, -- the size of the via alont the lenght of the cell rules: Route.DesignRules] RETURNS [shell: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { EnterPowerOnInner: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { IF (segment.object = handle.inner.object AND segment.side = otherSide) AND (Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net.name, "Gnd"]) THEN Insert[obstacles, [innerOrigin + segment.range.min, innerOrigin + segment.range.max], rules.branchToBranch]}; [] _ Connections.EnumerateSegments[net, EachSegment]}; EnterPowerOnOuter: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { IF (segment.object = objectDes.object AND segment.side = pinSide) AND (Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net.name, "Gnd"]) THEN Insert[obstacles, [outerOrigin + segment.range.min, outerOrigin + segment.range.max], rules.branchToBranch]}; [] _ Connections.EnumerateSegments[net, EachSegment]}; EachOuterNet: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { IF (segment.object = objectDes.object AND segment.side = pinSide) THEN { useThisPin: BOOLEAN _ TRUE; newRange: Connections.Range; powerNet: BOOLEAN _ Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net. name, "Gnd"]; adjustedRange: Connections.Range _ [outerOrigin + segment.range.min, outerOrigin + segment.range.max]; IF ProperSubset[adjustedRange, outerRange] THEN { IF powerNet THEN newRange _ adjustedRange ELSE IF HashTable.Insert[netTable, net.name, NIL] THEN newRange _ FindMiddleRange[adjustedRange, obstacles] ELSE useThisPin _ FALSE} ELSE IF ProperSubset[adjustedRange, [FIRST[INT], outerRange.min]] THEN { IF powerNet THEN { Cabbage.Signal[callingError, Rope.Cat[net.name, " pin on pad frame is in invalid position (within lower power bus corner)"]]; useThisPin _ FALSE} ELSE IF HashTable.Insert[netTable, net.name, NIL] THEN { lowerRange: Connections.Range _ [outerRange.min, outerRange.min + (adjustedRange.max-adjustedRange.min)]; newRange _ FindLowerRange[lowerRange, obstacles]} ELSE useThisPin _ FALSE} ELSE IF ProperSubset[adjustedRange, [outerRange.max, LAST[INT]]] THEN { IF powerNet THEN { Cabbage.Signal[callingError, Rope.Cat[net.name, " pin on pad frame is in invalid position (within upper power bus corner)"]]; useThisPin _ FALSE} ELSE IF HashTable.Insert[netTable, net.name, NIL] THEN { upperRange: Connections.Range _ [outerRange.max - (adjustedRange.max-adjustedRange.min), outerRange.max]; newRange _ FindUpperRange[upperRange, obstacles]} ELSE useThisPin _ FALSE} ELSE { Cabbage.Signal[callingError, Rope.Cat["Segment on net: ", net.name, " crosses boundary of power routing area"]]; useThisPin _ FALSE}; IF useThisPin THEN { denotes: CD.Rect _ SELECT pinSide FROM bottom => [newRange.min, oldIR.y2-rules.trunkWidth, newRange.max, oldIR.y2], right => [oldIR.x1, newRange.min, oldIR.x1+rules.trunkWidth, newRange.max], top => [newRange.min, oldIR.y1, newRange.max, oldIR.y1+rules.trunkWidth], left => [oldIR.x2-rules.trunkWidth, newRange.min, oldIR.x2, newRange.max], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; IncludePin[object: shell, name: net.name, denotes: denotes, layer: rules.branchLayer]; IF ~powerNet THEN -- power nets were inserted first Insert[obstacles, newRange, rules.branchToBranch]; }}}; [] _ Connections.EnumerateSegments[net, EachSegment]}; netTable: HashTable.Table _ HashTable.Create[equal: HashTable.RopeEqual, hash: HashTable.HashRope]; oldIR: CD.Rect _ CD.InterestRect[objectDes.object]; interestRect: CD.Rect _ SELECT pinSide FROM bottom, top => interestRect _ [outerRange.min, oldIR.y1, outerRange.max, oldIR.y2], left, right => [oldIR.x1, outerRange.min, oldIR.x2, outerRange.max], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; otherSide: Route.Side _ RTBasic.OtherSide[pinSide]; innerOrigin: INT _ SELECT pinSide FROM bottom, top => handle.inner.origin.x, right, left => handle.inner.origin.y, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; outerOrigin: INT _ SELECT pinSide FROM bottom, top => objectDes.origin.x, right, left => objectDes.origin.y, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; obstacles: BitTable _ CreateBitTable[outerRange.min, outerRange.max, rules.CDLambda]; Insert[obstacles, [outerRange.min, outerRange.min + viaSize], rules.branchToBranch]; Insert[obstacles, [outerRange.max - viaSize, outerRange.max], rules.branchToBranch]; Insert[obstacles, [innerRange.min - 3*rules.branchToBranch, innerRange.min + 3*rules.branchToBranch], rules.branchToBranch]; Insert[obstacles, [innerRange.max - 3*rules.branchToBranch, innerRange.max + 3*rules.branchToBranch], rules.branchToBranch]; [] _ Connections.EnumerateNets[handle.connections, EnterPowerOnInner]; [] _ Connections.EnumerateNets[handle.connections, EnterPowerOnOuter]; [] _ Connections.EnumerateNets[handle.connections, EachOuterNet]; CDCells.SetInterestRect[design: NIL, cell: shell, r: interestRect]; RTBasic.RepositionCell[shell]}; ShellFromOutsideEnd: PROC [handle: CabbagePrivate.Handle, objectDes: CabbagePrivate.ObjectDescription, pinsOnSide, endOfChannel: Route.Side, innerRange, outerRange: Connections.Range, -- the span along the appropriate padring cell rules: Route.DesignRules] RETURNS [shell: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { EachNet: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { IF (segment.object = objectDes.object AND segment.side = pinsOnSide) THEN { adjustedRange: Connections.Range _ [origin + segment.range.min, origin + segment.range.max]; IF CrossesBoundry[adjustedRange, interestingRange] THEN Cabbage.Signal[callingError, Rope.Cat["Segment on net: ", net.name, " crosses boundary of power routing areas"]]; IF ProperSubset[adjustedRange, interestingRange] AND ~Member[net.name, netNameList] THEN { width: INT _ MAX[rules.trunkWidth, net.width]; dist _ dist + rules.trunkWidth; SELECT endOfChannel FROM bottom => IncludePin[object: shell, name: net.name, denotes: [dist, rules.trunkWidth, dist + width, 2*rules.trunkWidth], layer: rules.trunkLayer]; right => IncludePin[object: shell, name: net.name, denotes: [0, dist, rules.trunkWidth, dist + width], layer: rules.trunkLayer]; top => IncludePin[object: shell, name: net.name, denotes: [dist, 0, dist + width, rules.trunkWidth], layer: rules.trunkLayer]; left => IncludePin[object: shell, name: net.name, denotes: [rules.trunkWidth, dist, 2*rules.trunkWidth, dist + width], layer: rules.trunkLayer]; ENDCASE; netNameList _ CONS[net.name, netNameList]; dist _ dist + width}}}; [] _ Connections.EnumerateSegments[net, EachSegment]}; dist: INT _ rules.trunkWidth; netNameList: LIST OF Rope.ROPE _ LIST["Vdd", "Gnd"]; -- pre load with power names interestRect: CD.Rect; interestingRange: Connections.Range _ SELECT endOfChannel FROM left, bottom => [FIRST[INT], outerRange.min], right, top => [outerRange.max, LAST[INT]], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; origin: INT _ SELECT pinsOnSide FROM bottom, top => objectDes.origin.x, right, left => objectDes.origin.y, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; [] _ Connections.EnumerateNets[handle.connections, EachNet]; interestRect _ SELECT endOfChannel FROM bottom, top => [0, 0, dist + rules.trunkWidth, 2*rules.trunkWidth], left, right => [0, 0, 2*rules.trunkWidth, dist + 2*rules.trunkWidth], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; CDCells.SetInterestRect[design: NIL, cell: shell, r: interestRect]; RTBasic.RepositionCell[shell]}; ShellFromObject: PROC [handle: CabbagePrivate.Handle, objectDes: CabbagePrivate.ObjectDescription, side: Route.Side, -- the side of the object on which pins are to be considered range: Connections.Range, -- the span along objectDes to consider rules: Route.DesignRules, includePowerNets: BOOLEAN] RETURNS [shell: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { EachNet: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { IF (segment.object = objectDes.object AND segment.side = side) THEN { adjustedRange: Connections.Range _ [origin + segment.range.min, origin + segment.range.max]; powerNet: BOOLEAN _ Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net. name, "Gnd"]; IF CrossesBoundry[adjustedRange, range] THEN Cabbage.Signal[callingError, Rope.Cat["Segment on net: ", net.name, " crosses a boundary of the power routing areas"]]; IF ProperSubset[adjustedRange, range] AND (includePowerNets OR ~powerNet) THEN { SELECT side FROM bottom => IncludePin[object: shell, name: net.name, denotes: [adjustedRange.min, oldIR.y1, adjustedRange.max, oldIR.y1+rules.trunkWidth], layer: segment.layer]; right => IncludePin[object: shell, name: net.name, denotes: [oldIR.x2-rules.trunkWidth, adjustedRange.min, oldIR.x2, adjustedRange.max], layer: segment.layer]; top => IncludePin[object: shell, name: net.name, denotes: [adjustedRange.min, oldIR.y2-rules.trunkWidth, adjustedRange.max, oldIR.y2], layer: segment.layer]; left => IncludePin[object: shell, name: net.name, denotes: [oldIR.x1, adjustedRange.min, oldIR.x1+rules.trunkWidth, adjustedRange.max], layer: segment.layer]; ENDCASE}}}; [] _ Connections.EnumerateSegments[net, EachSegment]}; oldIR: CD.Rect _ CD.InterestRect[objectDes.object]; interestRect: CD.Rect; origin: INT _ SELECT side FROM bottom, top => objectDes.origin.x, right, left => objectDes.origin.y, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; [] _ Connections.EnumerateNets[handle.connections, EachNet]; interestRect _ SELECT side FROM bottom, top => interestRect _ [range.min, oldIR.y1, range.max, oldIR.y2], left, right => [oldIR.x1, range.min, oldIR.x2, range.max], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; CDCells.SetInterestRect[design: NIL, cell: shell, r: interestRect]; RTBasic.RepositionCell[shell]}; ShellFromEnd: PROC [handle: CabbagePrivate.Handle, division: CabbagePrivate.Division, side: Route.Side, rules: Route.DesignRules] RETURNS [shell: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { EachExit: EachExitAction ~ { IF ~(Rope.Equal[net.name, "Vdd"] OR Rope.Equal[net. name, "Gnd"]) THEN { width: INT _ MAX[rules.trunkWidth, net.width]; dist _ dist + rules.trunkWidth; SELECT side FROM bottom => IncludePin[object: shell, name: net.name, denotes: [dist, rules.trunkWidth, dist + width, 2*rules.trunkWidth], layer: rules.trunkLayer]; right => IncludePin[object: shell, name: net.name, denotes: [0, dist, rules.trunkWidth, dist + width], layer: rules.trunkLayer]; top => IncludePin[object: shell, name: net.name, denotes: [dist, 0, dist + width, rules.trunkWidth], layer: rules.trunkLayer]; left => IncludePin[object: shell, name: net.name, denotes: [rules.trunkWidth, dist, 2*rules.trunkWidth, dist + width], layer: rules.trunkLayer]; ENDCASE; dist _ dist + width}}; dist: INT _ 0; interestRect: CD.Rect; [] _ EnumerateExits[handle.globalRouting.exitLists[division], division, EachExit]; interestRect _ SELECT side FROM bottom, top => [0, 0, dist + rules.trunkWidth, 2*rules.trunkWidth], left, right => [0, 0, 2*rules.trunkWidth, dist + rules.trunkWidth], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; CDCells.SetInterestRect[design: NIL, cell: shell, r: interestRect]; RTBasic.RepositionCell[shell]}; ShellFromFullChannel: PROC [channel: CabbagePrivate.Channel, side: Route.Side, pinLayer: CD.Layer, rules: Route.DesignRules] RETURNS [shell: Cabbage.Object] ~ { rect: CD.Rect _ CD.InterestRect[channel.object]; range: Connections.Range _ SELECT side FROM bottom, top => [rect.x1, rect.x2], left, right => [rect.y1, rect.y2], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; RETURN [ShellFromChannel[channel, side, pinLayer, range, rules]]}; ShellFromChannel: PROC [channel: CabbagePrivate.Channel, pinSide: Route.Side, pinLayer: CD.Layer, range: Connections.Range, rules: Route.DesignRules] RETURNS [shell: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { ChanPublics: CoreGeometry.EachWirePinProc ~ { name: Rope.ROPE _ CoreOps.GetShortWireName[wire]; IF layer = pinLayer THEN { SELECT side FROM bottom => IF range.min <= rect.x1+max AND rect.x1+max <= range.max AND pinSide = bottom THEN IncludePin[object: shell, name: name, denotes: [rect.x1+min, rect.y1, rect.x1+max, rect.y1+depth], layer: layer]; right => IF range.min <= rect.y1+max AND rect.y1+max <= range.max AND pinSide = right THEN IncludePin[object: shell, name: name, denotes: [rect.x2-depth, rect.y1+min, rect.x2, rect.y1+max], layer: layer]; top => IF range.min <= rect.x1+max AND rect.x1+max <= range.max AND pinSide = top THEN IncludePin[object: shell, name: name, denotes: [rect.x1+min, rect.y2-depth, rect.x1+max, rect.y2], layer: layer]; left => IF range.min <= rect.y1+max AND rect.y1+max <= range.max AND pinSide = left THEN IncludePin[object: shell, name: name, denotes: [rect.x1, rect.y1+min, rect.x1+depth, rect.y1+max], layer: layer]; ENDCASE}}; mode: Sinix.Mode = SinixOps.GetExtractMode[rules.technology]; depth: Route.Number _ rules.trunkWidth; rect: CD.Rect _ CD.InterestRect[channel.object]; newRect: CD.Rect _ SELECT pinSide FROM bottom, top => [range.min, rect.y1, range.max, rect.y2], left, right => [rect.x1, range.min, rect.x2, range.max], ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen"]; [] _ CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, channel.cellType, ChanPublics]; CDCells.SetInterestRect[design: NIL, cell: shell, r: newRect]; RTBasic.RepositionCell[shell]}; AdjustPositions: PROC [handle: CabbagePrivate.Handle] ~ { powerWidth: INT _ handle.parms.outerChanWidth + handle.parms.powerCellWidth; routeType: CabbagePrivate.RouteType _ handle.routeType; sizeBottomArea: INT _ handle.inner.origin.y - (handle.bottom.origin.y + handle.bottom.size.y); sizeBottomRouting: INT _ powerWidth + (IF routeType = normal THEN handle.detailedRouting.channels[bottom].size.y ELSE handle.detailedRoutingPL.switchBoxes[bottom].size.y); adjBottom: INT _ sizeBottomRouting - sizeBottomArea; sizeLeftArea: INT _ handle.inner.origin.x - (handle.left.origin.x + handle.left.size.x); sizeLeftRouting: INT _ powerWidth + (IF routeType = normal THEN handle.detailedRouting.channels[left].size.x ELSE handle.detailedRoutingPL.channels[left].size.x); adjLeft: INT _ sizeLeftRouting - sizeLeftArea; adjTop, adjRight, sizeTopArea, sizeTopRouting, sizeRightArea, sizeRightRouting: INT; handle.inner.origin _ CDBasics.AddPoints[handle.inner.origin, [MAX[0, adjLeft], MAX[0, adjBottom]]]; handle.bottom.origin _ CDBasics.AddPoints[handle.bottom.origin, [MAX[0, adjLeft], 0]]; handle.right.origin _ CDBasics.AddPoints[handle.right.origin, [MAX[0, adjLeft], MAX[0, adjBottom]]]; handle.top.origin _ CDBasics.AddPoints[handle.top.origin, [MAX[0, adjLeft], MAX[0, adjBottom]]]; handle.left.origin _ CDBasics.AddPoints[handle.left.origin, [0, MAX[0, adjBottom]]]; sizeTopArea _ handle.top.origin.y - (handle.inner.origin.y + handle.inner.size.y); sizeTopRouting _ powerWidth + (IF routeType = normal THEN handle.detailedRouting.channels[top].size.y ELSE handle.detailedRoutingPL.switchBoxes[top].size.y); adjTop _ sizeTopRouting - sizeTopArea; sizeRightArea _ handle.right.origin.x - (handle.inner.origin.x + handle.inner.size.x); sizeRightRouting _ powerWidth + (IF routeType = normal THEN handle.detailedRouting.channels[right].size.x ELSE handle.detailedRoutingPL.channels[right].size.x); adjRight _ sizeRightRouting - sizeRightArea; handle.top.origin _ CDBasics.AddPoints[handle.top.origin, [0, MAX[0, adjTop]]]; handle.right.origin _ CDBasics.AddPoints[handle.right.origin, [MAX[0, adjRight], 0]]; [] _ GetSize[handle]; DoCorners[handle]; DoRoutingAreas[handle]}; IncludeObject: PROC [object: Cabbage.Object, origin: CD.Position, chip: Cabbage.Object] ~ { IF object # NIL THEN [] _ RouteUtil.Include[cell: chip, ob: object, position: CDOps.FitObjectI[ob: object, location: origin, orientation: original].off, orientation: original]}; ExtractChannel: PROC [channel: CabbagePrivate.Channel, parms: CabbagePrivate.ParmSet] RETURNS [cellType: Core.CellType] ~ { mode: Sinix.Mode = SinixOps.GetExtractMode[parms.rules.technology]; cellType _ NARROW [Sinix.Extract[channel.object, mode].result]}; PosOf: PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment] RETURNS [INT] ~ { range: Connections.Range _ RangeOf[handle, segment]; RETURN[(range.min+range.max)/2]}; RangeOf: PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment] RETURNS [range: Connections.Range] ~ { range _ SELECT TRUE FROM segment.object = handle.inner.object => InnerRange[handle, segment], segment.object = handle.bottom.object => range _ OuterRange[handle, segment], segment.object = handle.right.object => OuterRange[handle, segment], segment.object = handle.top.object => OuterRange[handle, segment], segment.object = handle.left.object => OuterRange[handle, segment], segment.object = handle.powerDistribution.power[bottom].object => PowerRange[handle, segment], segment.object = handle.powerDistribution.power[right].object => PowerRange[handle, segment], segment.object = handle.powerDistribution.power[top].object => PowerRange[handle, segment], segment.object = handle.powerDistribution.power[left].object => PowerRange[handle, segment], ENDCASE => Cabbage.Error[callingError, Rope.Cat["Invalid object in segment: ", segment.name]]}; InnerRange: PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment] RETURNS [range: Connections.Range] ~ { SELECT segment.side FROM bottom => {origin: INT _ handle.inner.origin.x; range _ [origin + segment.range.min, origin + segment.range.max]}; right => {origin: INT _ handle.size.x + handle.inner.origin.y; range _ [origin + segment.range.min, origin + segment.range.max]}; top => {origin: INT _ handle.size.x +handle.size.y + (handle.size.x - handle.inner.origin.x); range _ [origin - segment.range.max, origin - segment.range.min]}; left => {origin: INT _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.origin.y); range _ [origin - segment.range.max, origin - segment.range.min]}; ENDCASE}; OuterRange: PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment] RETURNS [range: Connections.Range] ~ { SELECT RTBasic.OtherSide[segment.side] FROM bottom => {origin: INT _ handle.bottom.origin.x; range _ [origin + segment.range.min, origin + segment.range.max]}; right => {origin: INT _ handle.size.x + handle.right.origin.y; range _ [origin + segment.range.min, origin + segment.range.max]}; top => {origin: INT _ handle.size.x +handle.size.y + (handle.size.x - handle.top.origin.x); range _ [origin - segment.range.max, origin - segment.range.min]}; left => {origin: INT _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.left.origin.y); range _ [origin - segment.range.max, origin - segment.range.min]}; ENDCASE}; PowerRange: PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment] RETURNS [range: Connections.Range] ~ { SELECT RTBasic.OtherSide[segment.side] FROM bottom => {origin: INT _ handle.powerDistribution.power[bottom].origin.x; range _ [origin + segment.range.min, origin + segment.range.max]}; right => {origin: INT _ handle.size.x + handle.powerDistribution.power[right].origin.y; range _ [origin + segment.range.min, origin + segment.range.max]}; top => {origin: INT _ handle.size.x +handle.size.y + (handle.size.x - handle.powerDistribution.power[top].origin.x); range _ [origin - segment.range.max, origin - segment.range.min]}; left => {origin: INT _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.powerDistribution.power[left].origin.y); range _ [origin - segment.range.max, origin - segment.range.min]}; ENDCASE}; PosOfDivision: PROC [handle: CabbagePrivate.Handle, division: CabbagePrivate.Division] RETURNS [pos: INT] ~ { SELECT division FROM bottomLeft => pos _ handle.inner.origin.x; bottomRight => pos _ handle.inner.origin.x + handle.inner.size.x; rightBottom => pos _ handle.size.x + handle.inner.origin.y; rightTop => pos _ handle.size.x + handle.inner.origin.y + handle.inner.size.y; topRight => pos _ handle.size.x +handle.size.y + (handle.size.x - handle.inner.origin.x - handle.inner.size.x); topLeft => pos _ handle.size.x +handle.size.y + (handle.size.x - handle.inner.origin.x); leftTop => pos _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.origin.y - handle.inner.size.y); leftBottom => pos _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.origin.y); ENDCASE}; EachExitAction: TYPE = PROC [division: CabbagePrivate.Division, net: Connections.Net] RETURNS [quit: BOOLEAN _ FALSE]; EnumerateExits: PROC [exitList: CabbagePrivate.ExitList, division: CabbagePrivate.Division, eachExitAction: EachExitAction] RETURNS [quit: BOOLEAN _ FALSE] ~ { FOR list: CabbagePrivate.ExitList _ exitList, list.rest WHILE ~quit AND list # NIL DO exit: Connections.Net _ list.first; quit _ eachExitAction[division, exit]; ENDLOOP}; CrossesBoundry: PROC [r1, r2: Connections.Range] RETURNS [crosses: BOOLEAN] ~ { crosses _(r1.min <= r2.min AND r2.min <= r1.max) OR (r1.min <= r2.max AND r2.max <= r1.max); }; ProperSubset: PROC [r1, r2: Connections.Range] RETURNS [subset: BOOLEAN] ~ { subset _((r2.min <= r1.min AND r1.min <= r2.max) AND (r2.min <= r1.max AND r1.max <= r2.max)); }; GetSize: PROC [handle: CabbagePrivate.Handle] RETURNS [hBottom, hTop, vLeft, vRight: INT] ~ { vMiddle, hMiddle: INT; powerWidth: INT _ handle.parms.outerChanWidth + handle.parms.powerCellWidth; IF handle.routeType = normal THEN { vMiddle _ handle.bottom.size.y + powerWidth + handle.detailedRouting.channels[bottom].size.y + handle.inner.size.y + handle.detailedRouting.channels[top].size.y + powerWidth + handle.top.size.y; hMiddle _ handle.left.size.x + powerWidth + handle.detailedRouting.channels[left].size.x + handle.inner.size.x + handle.detailedRouting.channels[right].size.x + powerWidth + handle.right.size.x} ELSE { hCenter: INT _ powerWidth + handle.detailedRoutingPL.channels[left].size.x + handle.inner.size.x + handle.detailedRoutingPL.channels[right].size.x + powerWidth; hInner: INT _ MAX[hCenter, handle.detailedRoutingPL.switchBoxes[top].size.x, handle.detailedRoutingPL.switchBoxes[bottom].size.x]; vInner: INT _ powerWidth + handle.detailedRoutingPL.switchBoxes[bottom].size.y + handle.inner.size.y + handle.detailedRoutingPL.switchBoxes[top].size.y + powerWidth; vMiddle _ handle.bottom.size.y + vInner + handle.top.size.y; hMiddle _ handle.left.size.x + hInner + handle.right.size.x}; vLeft _ handle.bottomLeft.size.y + handle.left.size.y + handle.topLeft.size.y; vRight _ handle.bottomRight.size.y + handle.right.size.y + handle.topRight.size.y; hBottom _ handle.bottomLeft.size.x + handle.bottom.size.x + handle.bottomRight.size.x; hTop _ handle.topLeft.size.x + handle.top.size.x + handle.topRight.size.x; handle.size.y _ MAX[vLeft, vMiddle, vRight]; handle.size.x _ MAX[hBottom, hMiddle, hTop]}; DoCorners: PROC [handle: CabbagePrivate.Handle] ~ { handle.bottomLeft.origin _ [0, 0]; handle.bottomRight.origin _ [handle.size.x - handle.bottomRight.size.x, 0]; handle.topRight.origin _ [handle.size.x - handle.topRight.size.x, handle.size.y - handle.topRight.size.y]; handle.topLeft.origin _ [0, handle.size.y - handle.topLeft.size.y]}; DoRoutingAreas: PROC [handle: CabbagePrivate.Handle] ~ { outerChanWidth: INT _ handle.parms.outerChanWidth; powerWidth: INT _ outerChanWidth + handle.parms.powerCellWidth; routeType: CabbagePrivate.RouteType _ handle.routeType; leftInterior: INT _ handle.left.origin.x + handle.left.size.x; -- right of left pads rightInterior: INT _ handle.right.origin.x; -- left of right pads bottomInterior: INT _ handle.bottom.origin.y + handle.bottom.size.y; -- top of bottom pads topInterior: INT _ handle.top.origin.y; -- bottom of top pads leftOfInner: INT _ handle.inner.origin.x; -- left of inner object rightOfInner: INT _ handle.inner.origin.x + handle.inner.size.x; -- right of inner object bottomOfInner: INT _ handle.inner.origin.y; -- bottom of inner object topOfInner: INT _ handle.inner.origin.y + handle.inner.size.y; -- top of inner object IF routeType = normal THEN { handle.detailedRouting.channels[bottom].origin _ [leftOfInner, bottomOfInner - handle.detailedRouting.channels[bottom].size.y]; handle.detailedRouting.channels[right].origin _ [rightOfInner, bottomOfInner]; handle.detailedRouting.channels[top].origin _ [leftOfInner, topOfInner]; handle.detailedRouting.channels[left].origin _ [leftOfInner - handle.detailedRouting.channels[left].size.x, bottomOfInner]; handle.detailedRouting.switchBoxes[bottomLeft].origin _ [leftInterior + powerWidth, bottomInterior + powerWidth]; handle.detailedRouting.switchBoxes[bottomRight].origin _ [rightOfInner, bottomInterior + powerWidth]; handle.detailedRouting.switchBoxes[topRight].origin _ [rightOfInner, topOfInner]; handle.detailedRouting.switchBoxes[topLeft].origin _ [leftInterior + powerWidth, topOfInner]} ELSE { -- routeType = padLimited handle.detailedRoutingPL.channels[right].origin _ [rightOfInner, bottomOfInner]; handle.detailedRoutingPL.channels[left].origin _ [leftOfInner - handle.detailedRoutingPL.channels[left].size.x, bottomOfInner]; handle.detailedRoutingPL.switchBoxes[bottom].origin _ [leftInterior + powerWidth, bottomInterior + powerWidth]; handle.detailedRoutingPL.switchBoxes[top].origin _ [leftInterior + powerWidth, topOfInner]}; handle.powerDistribution.channels[bottom].origin _ [leftInterior + powerWidth, bottomInterior]; handle.powerDistribution.channels[right].origin _ [rightInterior - outerChanWidth, bottomInterior + powerWidth]; handle.powerDistribution.channels[top].origin _ [leftInterior + powerWidth, topInterior - outerChanWidth]; handle.powerDistribution.channels[left].origin _ [leftInterior, bottomInterior + powerWidth]; handle.powerDistribution.power[bottom].origin _ [leftInterior + powerWidth, bottomInterior + outerChanWidth]; handle.powerDistribution.power[right].origin _ [rightInterior - powerWidth, bottomInterior + powerWidth]; handle.powerDistribution.power[top].origin _ [leftInterior + powerWidth, topInterior - powerWidth]; handle.powerDistribution.power[left] .origin_ [leftInterior + outerChanWidth, bottomInterior + powerWidth]; handle.powerDistribution.switchBoxes[bottomLeft].origin _ [leftInterior, bottomInterior]; handle.powerDistribution.switchBoxes[bottomRight].origin _ [rightInterior - powerWidth, bottomInterior]; handle.powerDistribution.switchBoxes[topRight].origin _ [rightInterior - powerWidth, topInterior - powerWidth]; handle.powerDistribution.switchBoxes[topLeft].origin _ [leftInterior, topInterior - powerWidth]}; IncludePin: PROC [object: CD.Object, name: Rope.ROPE, denotes: CD.Rect, layer: CD.Layer] ~ { pin: CD.Object _ CDSymbolicObjects.CreatePin[CDBasics.SizeOfRect[denotes]]; pinInstance: CD.Instance _ RouteUtil.Include[cell: object, ob: pin, position: CDBasics.BaseOfRect[denotes], orientation: original]; CDProperties.PutInstanceProp[pinInstance, cabbageXPos, NEW[INT _ denotes.x1]]; CDProperties.PutInstanceProp[pinInstance, cabbageYPos, NEW[INT _ denotes.y1]]; CDSymbolicObjects.SetName[pinInstance, name]; CDSymbolicObjects.SetLayer[pinInstance, layer]}; Member: PROC [item: Rope.ROPE, list: LIST OF Rope.ROPE] RETURNS [BOOLEAN] ~ { UNTIL list = NIL DO IF Rope.Equal[list.first, item] THEN RETURN [TRUE]; list _ list.rest; ENDLOOP; RETURN [FALSE]}; EqualProc: PROC [k1, k2: HashTable.Key] RETURNS [eq: BOOL] = { p1: Route.Position _ NARROW[k1, REF Route.Position]^; p2: Route.Position _ NARROW[k2, REF Route.Position]^; eq _ p1.x = p2.x AND p1.y = p2.y}; HashProc: PROC [k: HashTable.Key] RETURNS [hash: CARDINAL] = { size: Route.Position _ NARROW[k, REF Route.Position]^; hash _ size.x + size.y}; BitTable: TYPE = REF BitTableRec; BitTableRec: TYPE = RECORD[ bitSize: NAT _ 8, -- CD.Number per lambdas min, max: INT _ 0, -- outerRange bits: PACKED SEQUENCE size: NAT OF BOOL]; -- TRUE=busy Translate: PROC [obstacles: BitTable, pos: INT] RETURNS [bitIndex: NAT] ~ INLINE { bitIndex _ (pos-obstacles.min)/obstacles.bitSize; IF bitIndex NOT IN [0..obstacles.size) THEN ERROR}; CreateBitTable: PROC [min, max: INT, bitSize: NAT] RETURNS [bitTable: BitTable] ~ { size: NAT _ (max/bitSize)-(min/bitSize)+1; bitTable _ NEW[BitTableRec[size]]; bitTable.bitSize _ bitSize; bitTable.min _ min; bitTable.max _ max; FOR i: NAT IN [0..size) DO bitTable[i] _ FALSE; ENDLOOP}; PositionOccupied: PROC [obstacles: BitTable, pos: INT] RETURNS [BOOL] ~ INLINE { RETURN[obstacles[Translate[obstacles, pos]]]}; NextPos: PROC [obstacles: BitTable, from: INT, goingUp: BOOL] RETURNS [next: INT] ~ INLINE { next _ IF goingUp THEN from+1 ELSE from-1}; Edge: PROC [obstacles: BitTable, goingUp: BOOL] RETURNS [pos: INT] ~ INLINE { pos _ IF goingUp THEN obstacles.max ELSE obstacles.min}; SetOccupied: PROC [obstacles: BitTable, pos: INT] ~ INLINE { IF pos NOT IN [obstacles.min..obstacles.max] THEN RETURN; obstacles[Translate[obstacles, pos]] _ TRUE}; Insert: PROC [obstacles: BitTable, range: Connections.Range, spacing: INT] ~ { FOR pos: INT IN [range.min-spacing..range.max+spacing] DO SetOccupied[obstacles, pos]; ENDLOOP}; IsFree: PROC [range: Connections.Range, obstacles: BitTable, goingUp: BOOL] RETURNS [isFree: BOOL, nextTry: INT] ~ { FOR pos: INT IN [range.min..range.max] DO IF PositionOccupied[obstacles, pos] THEN RETURN[FALSE, FindNextFree[obstacles, pos, goingUp]]; ENDLOOP; RETURN[TRUE, range.min]}; FindNextFree: PROC [obstacles: BitTable, from: INT, goingUp: BOOL] RETURNS [free: INT] ~ { free _ from; WHILE PositionOccupied[obstacles, free] AND free#Edge[obstacles, goingUp] DO free _ NextPos[obstacles, free, goingUp]; ENDLOOP}; FindLowerRange: PROC [adjustedRange: Connections.Range, obstacles: BitTable] RETURNS [newRange: Connections.Range] ~ { isFree: BOOL _ FALSE; nextTry: INT; newRange _ adjustedRange; WHILE ~isFree DO [isFree, nextTry] _ IsFree[newRange, obstacles, TRUE]; IF nextTry>=obstacles.max THEN Cabbage.Signal[noResource, "No space for pin on power bus side."]; newRange _ [nextTry + obstacles.bitSize, nextTry + obstacles.bitSize + (adjustedRange.max-adjustedRange.min)]; -- noop if isFree ENDLOOP}; FindUpperRange: PROC [adjustedRange: Connections.Range, obstacles: BitTable] RETURNS [newRange: Connections.Range] ~ { isFree: BOOL _ FALSE; nextTry: INT; newRange _ adjustedRange; WHILE ~isFree DO [isFree, nextTry] _ IsFree[newRange, obstacles, FALSE]; IF nextTry<=obstacles.min THEN Cabbage.Signal[noResource, "No space for pin on power bus side."]; newRange _ [nextTry - obstacles.bitSize - (adjustedRange.max-adjustedRange.min), nextTry - obstacles.bitSize]; -- noop if isFree ENDLOOP}; FindMiddleRange: PROC [adjustedRange: Connections.Range, obstacles: BitTable] RETURNS [newRange: Connections.Range] ~ { IF IsFree[adjustedRange, obstacles, FALSE].isFree THEN RETURN[adjustedRange] ELSE { fellOffLower, fellOffUpper: BOOL _ FALSE; lower, upper: Connections.Range; -- CEDAR Bug! Signals in initialization upper _ FindLowerRange[adjustedRange, obstacles ! Cabbage.Signal => IF errorType=noResource THEN {fellOffUpper _ TRUE; CONTINUE}]; lower _ FindUpperRange[adjustedRange, obstacles ! Cabbage.Signal => IF errorType=noResource THEN {fellOffLower _ TRUE; CONTINUE}]; IF fellOffUpper AND fellOffLower THEN Cabbage.Signal[noResource, "No space for pin on power bus side."]; IF fellOffUpper THEN RETURN[lower]; IF fellOffLower THEN RETURN[upper]; IF adjustedRange.min-lower.min<upper.min-adjustedRange.min THEN RETURN[lower]; RETURN[upper]}}; PWRouteContext: TYPE = RECORD [ direction: RTBasic.Direction, table: Connections.Table]; GetWireWidth: PWRoute.WireWidthProc ~ { table: Connections.Table _ NARROW[context, REF PWRouteContext].table; net: Connections.Net _ Connections.Fetch[table, netName].net; wireWidth _ net.width}; PinName: PWRoute.MakeTabKeyProc ~ { name: Rope.ROPE _ CDSymbolicObjects.GetName[pinInst]; IF Rope.Equal[name, "Vdd"] OR Rope.Equal[name, "Gnd"] THEN { direction: RTBasic.Direction _ NARROW[context, REF PWRouteContext].direction; refPosition: REF ANY _ IF direction = horizontal THEN CDProperties.GetInstanceProp[pinInst, cabbageXPos] ELSE CDProperties.GetInstanceProp[pinInst, cabbageYPos]; position: INT _ NARROW[refPosition, REF INT]^; tabIndex _ Rope.Cat[name, Convert.RopeFromInt[position]]} ELSE tabIndex _ name}; }. ��!Ä��CabbageProcsImpl.mesa Copyright Ó 1986, 1987 by Xerox Corporation. All rights reversed. Created by Bryan Preas, June 4, 1986 4:02:08 pm PDT Bryan Preas April 2, 1987 6:03:17 pm PST Last Edited by: Louis Monier April 23, 1987 4:18:11 pm PDT Cabbage needs to be fixed when PWRoute removes PWPins. Initialization Define the routing design rules. technologyKey values are predefinded for now. horizLayer, vertLayer should be "poly", "metal" or "metal2". put in wider spacings at sides of channels PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] Power Distribution make the power distribution channels route the outside channels; move signal pins on the outer from the shadow of the power pins on the inner. horizParms and vertParms specify the routing for the specified direction; horizRange and vertRange describe the projection of the inner onto the pad ring route the outside channels construct the power routing channels; route signal pins on the outer straight accross with vias as necessary. horizParms and vertParms specify the routing for the specified direction; horizRange and vertRange describe the projection of the inner onto the pad ring construct the power distribution cells route one of the outside channels construct the bonding objects to pass to the channel router build the power routing cell for the indicated side connect the Vdd and Gnd pin via a bus connection and transfer the signal pins across with appropriate vias inside and outside pins are put on the [inside|outside]Parms.rules.branchLayer PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE]; store the pins on the appropriate side of the adjacent channel add the via at the right place -- irSize stuff should be remove in Cabbage25 PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] store the power pins on the appropriate side of the inner object PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] add the via at the right place irSize to go away in Cabbage25 REMOVE branch stuff in Cabbage25 construct power trunks build the branches route the switchboxes in the outside corners. these switchboxes connect the power busses; any signal pins on the padring get transfered to the adjacent channel construct a switchbox for one of the power distribution corners Global Routing find the strategic paths for all nets PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] Make a new set of connections to account for new power ring store the net and the segments on the net now do the power objects insert all of the pins from the inner object into the new table PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] make a new copy of the segment and put it in the table store the net and the segments on the net insert the pins on the appropriate side into the new table PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE]; store the pins on the appropriate side of the adjacent channel Sort the segments on this net in order arround the periphery Use Reverse to get a new copy of the list because Sort is destructive Find the start and end segments of a net arround the periphery Add this net to the global route Detailed Routing route all of the channels or just the left and right channels depending on routeType route one of the channels construct the bonding objects to pass to the channel router adjust the positions of everything to account for the actual channel widths route all of the switchBoxes (the corners) route the bottom and top switchBoxes Object Generation construct the chip; all of the peices have been constructed first include the input cells next include the signal routing finally the power cells, outside routing channels and switchboxes Shell Construction build a shell for inner edge of the outside channel and the outer edge of the power cell; input to PWRoute; pins are on the branch layer of rules (rules.branchLayer). map signal pins outside outerRange to just inside outerRange map Vdd and Gnd pins in outerRange to their same positions; map only a single pin per wire outside innerRange but inside outerRange PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] store the power pins on the appropriate side of the inner object PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] store the power pins on the appropriate side of the inner object PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] examine the nets on the outer object: objectDes PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] keep the segments of interest on objectDes PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] pin is within middle range IF net = Vdd OR net = Gnd OR net has not been processed for this side THEN useThisPin pin is within lower range IF ~(net = Vdd OR net = Gnd) AND net has not been processed for this side THEN useThisPin pin is within upper range IF ~(net = Vdd OR net = Gnd) AND net has not been processed for this side THEN useThisPin insert obstacles for vias on end of power bus and at boundries of routing areas all obstacles (pins, inner power publics, ...) are contained in a sorted table; the data is a Connections.Segment; the key is the origin, i.e. data.range.min process the inner power obstacles an the find places for the outer signals build a shell for end of the channel on the periprery; input to PWRoute transfer pins on objectDes outside the range (on side endOfChannel) to shell. pins go on rules.trunkLayer PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] build a shell for side of channel or switchbox; input to router PROC [key: Key, net: Net] RETURNS [quit: BOOLEAN _ FALSE] PROC [net: Net, segment: Segment] RETURNS [quit: BOOLEAN _ FALSE] build a shell for end of channel; input to router PROC [division: CabbagePrivate.Division, net: Connections.Net] RETURNS [quit: BOOLEAN _ FALSE]; build a PWPins shell for the channel; use the full side of channel with pins on side build a PWPins shell for the channel; use the pins on side; range is the span along that side PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE]; add decoration for public; wire is an unbound wire Utility Procedures adjust the vertical and horizontal dimension extract connectivity for the channel Find the position of a segment projected to the periphery. The lower left corner is the origin. Find the range of a segment projected to the periphery. The lower left corner is the origin. returns TRUE if either end but not both ends of r1 is within r2 returns TRUE if r1 is within r2 short hand to make the following easier to understand the outside routing channels the power channels the outside corners include a pin(name, denotes, layer) in an object properties are use to pass position info because of restrictions in PWPins Return TRUE if item is on list HashTable Procedures call back procs for HashTable Obstacle manipulation Procedures... -- Assertion: obstacles do not overlap: if you insert a new obstacle and it overlaps an existing one in the table, the two are fused and the resulting union is put in the table; this should be maintained at all time -- The table contains the projection of the pins with the design rule space on each side -- [outerRange.min..outerRange.max] must map into [0..obstacles.size-1] -- Assertion: pos must be in (obstacles.min..obstacles.max)!!! -- Bloat range by a spacing on each side on insertion; there is space here, you know it! -- when isFree, then nextTry=range.min -- range must be valid (within outerRange) find a neRange were pin is not hidden by a pin in obstacles and does not interfere with existing pin pin should be close to outerRange.min find a newRange were pin is not hidden by a pin in obstacles and does not interfere with existing pin pin should be close to outerRange.max find a neRange were pin is not hidden by a pin in obstacles and does not interfere with existing pin PWRoute CallBacks context to pass to PWRoute for wire widths (GetWireWidth) and pin names (PinName) call back procs for PWRoute PROC [netName: ROPE, context: REF ANY] RETURNS [wireWidth: INT] PROC [pinInst: CD.Instance, context: REF ANY] RETURNS [tabIndex: Rope.ROPE] call back Proc for PWRoute; this separates Vdd and Gnd into two pin nets Ê8Î��˜�code– "Cedar" stylešœ™KšœB™BKšœ0Ïk™3Kšœ%™(Kšœ7™:K™�K™6—K˜�Kš œ"œ™œ<˜„K˜�šÏnœœ˜Kšœœ“œ;˜ëKšœ˜Kšœ ˜—headšÏl™Kšœ œ˜!Kšœ œ˜!K˜�š žœœœ¨œ'œ#˜–Kšœ œR˜^Kšœ˜Kšœ(˜(Kšœœ&˜DKšœœ"˜<šœœ˜Kšœœ#˜?—šœ˜Kšœœ&˜D—Kšœ^˜^Kšœœœ˜3Kšœ˜K˜�—KšœŒ™Œšžœœ œ.˜lK˜�KšœœG˜QKšœœF˜PKš œœœœœ4˜€Kšœœ ˜1K˜�KšœœÝœG˜ËKšœœÝœG˜ÊK˜�Kšœh˜hKšœe˜eKšœ*™*KšœU˜UKšœT˜TK˜�—šžœœ˜K˜�Kšœœ*˜5Kšœ;˜;K˜�Kšœ=˜=Kšœ;˜;Kšœ7˜7Kšœ9˜9K˜�Kšœ_˜_Kšœb˜bKšœY˜YKšœW˜WK˜�—šžœœ&œœ-˜yK˜�Kšœ˜Kšœ œœ˜-šœ˜Kšœ#˜#Kšœœ˜%Kšœœ˜'K˜�——šžœœ$˜9K˜�Kšœœ˜"Kšœ1˜1K˜�KšœS˜SKšœt˜tKšœj˜jKšœO˜OK˜�Kšœ˜Kšœ˜K˜�—šž œœ$˜3K˜�šžœ˜/Kšœœœœ™9Kšœœ˜7Kšœœœ˜>K˜�—šžœ˜&Kšœœœœ™9šžœ#˜.Kšœœœœ™AKšœœ7˜I—K˜�Kšœœ:˜Wšœœ˜Kšœ5˜5—Kšœ=˜=K˜�—K˜1KšœE˜EKšœ=˜=K˜�—šž œœœ+œ˜UK˜�Kšœœ˜'Kšœœ1˜<Kšœœ-˜8Kšœœ-˜8Kšœœ/˜:š œœœœœ˜pKšœc˜cKšœ=˜=—Kšœ&˜&——šŸ™šžœœœ$˜@Kšœ$™$Kšœ=˜=Kšœ;˜;Kšœœ¡˜«Kšœœ+˜:Kšœ)˜)Kšœ˜Kšœ+˜+Kšœ˜Kšœ!˜!K˜�—šžœœ*œ˜LKšœi™iKšœ™™™Kšœœ˜2Kšœœ0˜?Kšœ=˜=Kšœ;˜;Kšœj˜jKšœ‚˜‚Kšœi˜išœƒ˜ƒK™�—Kšœ™Kšœ¥˜¥Kšœ¡˜¡Kšœœ˜œKšœŸ˜ŸK˜�—šžœœ-œ˜QKšœm™mšœ™™™K˜�—Kšœœ˜2Kšœœ0˜?Kšœ=˜=Kšœ;˜;Kšœj˜jKšœ‚˜‚Kšœi˜iKšœƒ˜ƒK˜�Kšœ&™&Kšœ³˜³Kšœ¯˜¯Kšœª˜ªKšœ˜K˜�—Kšœ!™!šžœœ ˜9Kšœ/Ïc˜FKšœ ?˜QKšœ, ‡˜³Kšœ œ '˜5Kšœ œ 2˜@Kšœ˜Kšœ&˜-K˜�Kšœ˜Kšœ˜Kšœœ˜Kšœ0˜0Kš œœ œ œœ˜FKšœœœ ˜.Kš œœ œœ œ˜YK˜�Kšœ;™;šœœœd˜KšœJœ˜T—šœœœJœ˜xKšœe˜i—Kšœƒ˜ƒKšœ”˜”K˜�K˜)Kšœ%˜%KšœœD˜]Kšœ^œ˜uK˜�K˜šœ˜šœ˜Kšœœœ˜,Kšœœœ,˜A—šœ ˜ Kšœœœ˜,Kšœœœ,˜A—šœ˜Kšœœœ˜,Kšœœœ,˜A—šœ ˜ Kšœœœ˜,Kšœœœ,˜A—Kšœ˜K˜�—KšœG˜GKšœ.˜.Kšœ3˜3K˜�—šž œœ ˜0Kšœ! /˜PKšœ C˜YKšœ 4˜OKšœœ .˜?Kšœœ ˜0Kšœ2˜2Kšœ$˜+K™3Kšœj™jKšœO™OK˜�šžœ"˜-Kšœœœœœœ™[K™>K˜�šžœœœœ˜Qšœœœ ˜'Kšœ!˜!Kšœ6˜6Kšœ˜Kšœ8˜8Kšœ=˜D—šœ ˜Kšœ‘˜‘Kšœ‘˜‘Kšœ=˜D—šœœ˜"Kšœ™Kš -™-šœœ œ ˜%Kšœpœ˜uKšœpœ˜vKšœ=˜D—Kšœœ ˜*šœ œœ ˜#Kšœ&˜&Kšœ)˜)Kšœ=˜D—šœ œœ ˜#Kšœ)˜)Kšœ&˜&Kšœ=˜D—šœœœ ˜*KšœM˜MKšœJ˜JKšœK˜KKšœL˜LKšœ=˜D—KšœR˜R—K˜�—šžœœ˜K˜�Kšœœ1˜CKšœœ0˜Ašœ ˜šœ˜Kšœ—˜—Kšœ˜Kšœ•œ(˜À—šœ˜Kšœ°˜°Kšœ•˜•Kšœ•œ'˜¿—šœ ˜ Kšœ—˜—Kšœ˜Kšœ•œ(˜À—šœ ˜ Kšœ°˜°Kšœ•˜•Kšœ•œ'˜¿—Kšœ˜ —K˜�—Kšœœ"˜1KšœN˜Nšœœ˜šœœ˜KšœT˜TKšœw˜wKšœ˜——K˜�—šžœ˜&Kšœœœœ™9K™@šžœ#˜.Kšœœœœ™AK˜�šžœœœœ˜Qšœ œœ ˜+Kšœ9˜9Kšœ˜Kšœ7˜7Kšœ ˜ Kšœ=˜D—šœ ˜Kšœµ˜µKšœµ˜µKšœ˜—šœœ˜"Kšœ™šœœ œ ˜%KšœŒœ˜‘KšœŒœ˜’Kšœ=˜D—K˜�Kšœ™K˜�Kšœœ ˜*šœ œœ ˜#KšœB˜BKšœ)˜)Kšœ=˜D—šœ œœ ˜#Kšœ)˜)KšœC˜CKšœ=˜D—šœœœ ˜*KšœM˜MKšœJ˜JKšœK˜KKšœL˜LKšœ=˜D—KšœV˜V—K˜�—Kšœf˜fšœ&œœ˜Jšœœ˜Kšœd˜dKšœX˜XKšœ˜ ———K˜�Kšœ6˜6K˜�—šž œœœœœœ˜lšœœœ ˜-Kšœ1˜1Kšœ1˜1Kšœ=˜D—Kšœœ/˜8KšœT˜Tšœ œ˜Kšœœhœ˜tKšœœ ˜*šœ œœ ˜/KšœI˜IKšœI˜IKšœ=˜D—K˜�Kšœ ™ K˜�KšœœC˜Mšœœœ ˜2Kšœ5˜5Kšœ5˜5Kšœ=˜D—KšœU˜UKšœy˜yKšœR˜RKšœu˜u—K˜�—Kšœ œ'˜6Kšœœ(˜8Kšœ œ1˜AKšœ œ2˜BKšœ3˜3JšœI˜Išœœ ˜3Kšœ:˜:Kšœ=˜D—šœ œœ ˜'Kšœ%˜%Kšœ%˜%Kšœ>˜E—šœœœ ˜)Kšœ ˜ Kšœ ˜ Kšœ>˜E—šœœœ ˜,Kšœ6˜6Kšœ6˜6Kšœ=˜D—K™�Kšœ™šœœœ ˜1Kšœ0˜0Kšœ5˜5Kšœ.˜.Kšœ7˜7Kšœ=˜D—šœœœ ˜1Kšœ9˜9Kšœ-˜-Kšœ7˜7Kšœ/˜/Kšœ=˜D—Kšœ<œ˜CKšœ<œ˜BK˜�Kšœ™Kšœ_˜_Kšœ<˜<K˜�Kšœ˜Kšœ œ'˜JKšœ%˜%Kšœ*˜*Kšœ5˜5K˜�—šžœœ$˜AKšœ ™ Kšœ;˜;K˜�Kšœœ=˜LKšœˆ˜ˆKšœa˜aKšœ‘˜‘KšœY˜YK˜�Kšœ#œ˜(Kšœ‰˜‰Kšœ€˜€Kšœ#œ%œ˜RKšœŒ˜ŒKšœƒ˜ƒKšœ#œ%œ˜TK˜�—šžœœ‹œ(˜ÕKšœ?™?K˜�KšœA˜AK˜�šœ˜šœ˜KšœœœÙ˜öKšœTœ˜[KšœQœ˜XKšœœœØ˜÷K˜�—šœ˜KšœœœÜ˜ùKšœTœ˜[Kšœ œœÙ˜÷KšœRœ˜ZK˜�—šœ ˜ KšœQœ˜XKšœœœÕ˜õKšœ œœÓ˜ñKšœRœ˜ZK˜�—šœ˜KšœQœ˜XKšœœœÓ˜óKšœQœ˜XKšœœœÒ˜ñK˜�—Kšœ˜K˜�—Kšœœ˜ Kšœœ˜!KšœœU˜nKšœYœœ ˜}Kšœ2˜2Kšœ7˜7——šŸ™šžœœœ$˜<Kšœ%™%šžœ˜&Kšœœœœ™9KšœF˜Fšœœœ˜Kšœ\˜\Kšœ0˜0—K˜�—K˜?Kšœ9˜9K˜�—šžœœ!œG˜‡Kšœ;™;K˜�Kšœ)™)Kšœ,˜,K˜�Kšœ™Kšœ4˜4Kšœ3˜3Kšœ1˜1Kšœ3˜3K˜�—šžœœG˜`Kšœ?™?šžœ˜&Kšœœœœ™9šžœ#˜.Kšœœœœ™Ašœ&œ˜.Kšœ6™6Kšœ"œˆ˜Jšœœ˜4Jšœ;˜;K˜�——Kšœœ:˜WKšœ9˜9Kšœ6˜6K˜�—Kšœ)™)Kšœ=˜=K˜�—šžœœ_˜xKšœ:™:šžœ"˜-Kšœœœœœœ™[K™>Kšœœ"˜1šœœ˜*Kšœ:˜:Kšœ=˜D—šœœ˜!Kšœœ•˜·KšœC˜CJšœœ˜+Jšœ3˜3—K˜—J˜�Kšœ4˜4JšœU˜UKšœ˜K˜�—šžœœ7œ.œ˜†Kšœ=™=K˜�šž œB˜OK˜�—šž œ˜ Kšœœ˜šœ˜Kšœœ˜%Kšœœ ˜&—Kšœ"˜(K˜�—Kšœœ˜Kšœ+œ˜0Kšœ7˜7šœœ˜KšœE™EKšœ"œ˜BKšœ=˜=Kšœœ,˜@š œœ œœ˜FK˜Kšœœ˜(Kšœ ˜ Kšœ˜Kšœ˜ ——K˜�—Kšœ>™>š žœœbœ-œœ˜¾K˜�Kšœœœœ˜Kšœœ%˜3Kšœœ˜Kšœ&˜&šœœœ#˜6šœ(œ˜0Kšœ#˜#Kšœ˜KšœE˜E—šœ˜Kšœ˜Kšœ˜Kšœ:˜:—šœœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—š œœœœœ˜9K˜:—K˜�—Kšœ ™ šžœœc˜|K˜�Kšœœ#˜,Kšœœ#˜,šœ œ˜šœœ˜<šœ"œ!˜LKšœ$œ(˜P—Kšœ˜——šœ˜šœœ˜<šœ!œ!˜JKšœ$œ(˜P—Kšœ˜ ————šŸ™šž œœœ$˜>K˜�Kšœ˜K˜Kšœœ˜:Kšœ˜ K˜K˜�—KšœT™Tšž œœ$˜7K˜�Kšœ=˜=Kšœ;˜;Kšœe˜eKšœd˜dK˜�šœœ˜#Kšœ ˜ Kšœ™˜™Kšœ‘˜‘Kšœ”˜”—šœ ˜ Kšœ›˜›Kšœ˜˜˜—K˜�—Kšœ™šžœœ ˜2Kšœ) ˜DKšœ ?˜WKšœ' /˜VKšœ /˜JKšœ˜Kšœ&˜-K˜�Kšœ˜Kšœ˜Kšœœ˜Kšœœ˜'Kšœ4˜4Kš œœœœ œ˜aKš œœœœœ˜NKšœœœ˜6Kšœ;™;šœœœEœ˜sKšœ[˜_—šœœœZ˜ƒKšœEœ˜O—KšœS˜SKšœd˜dK˜�K˜)Kšœ%˜%KšœœD˜]Kšœaœ˜|K˜�K˜šœ ˜šœ˜KšœœO˜XKšœœœœA˜k—šœ ˜ KšœœL˜UKšœœœ$œ/˜k—šœ˜KšœœL˜UKšœœœ-œ&˜k—šœ ˜ KšœœO˜XKšœœœ œJ˜k—Kšœ˜K˜�—KšœG˜GKšœ.˜.Kšœ3˜3K˜�—KšœK™KKšœ*™*šžœœ$˜:K˜�Kšœ=˜=K˜�Kšœg˜gKšœj˜jKšœl˜lKšœe˜eK˜�Kšœ$œ&œ˜TKšœ˜K˜�Kšœ$œ&œ˜TKšœ„˜„K˜�Kšœ$œ&œ˜TKšœ{˜{K˜�Kšœ$œ&œ˜TKšœx˜xKšœ$œ&œ˜VK˜�—Kšœ$™$šžœœ$˜<K˜�Kšœ=˜=Kšœœ=˜LK˜�Kšœ}˜}Kšœy˜yKšœr˜rK˜�Kšœ$œ&œ˜SKšœy˜yKšœp˜pKšœ$œ&œ˜VK˜�—šžœœ€œ(˜ÅK˜�Kšœ(˜(Kšœ‹˜‹KšœŒ˜ŒKšœe˜eKš œœ œœ œ˜YK˜�šœ˜šœ˜Kšœeœ˜kKšœœœê˜‡Kšœ€˜€K˜�—šœ˜Kšœbœ˜hKšœœœä˜„Kšœ}˜}K˜�—Kšœ˜K˜�—Kšœ)˜)Kšœ%˜%KšœœD˜]KšœYœœ ˜}Kšœ2˜2Kšœ7˜7K˜�—šžœœŠœ(˜ÍK˜�KšœA˜Ašœ˜šœ˜Kšœv˜vKšœ˜Kšœ{˜{Kšœx˜xK˜�—šœ˜Kšœw˜wKšœ˜Kšœ|˜|Kšœx˜xK˜�—šœ ˜ Kšœw˜wKšœ|˜|Kšœ|˜|Kšœu˜uK˜�—šœ˜Kšœv˜vKšœ|˜|Kšœ{˜{Kšœu˜uK˜�—Kšœ˜K˜�—Kšœ)˜)Kšœ(˜(KšœYœœ ˜}Kšœ2˜2Kšœ7˜7——šŸ™šžœœœ!œ7˜tKšœ<™<Kšœ™Kšœ>˜>KšœH˜HKšœ@˜@KšœJ˜JKšœ>˜>KšœD˜DKšœ:˜:KšœB˜BKšœ<˜<K˜�Kšœ™šœœ˜#šœœ˜)Kšœp˜pJšœ˜—šœ œ˜?Kšœz˜zJšœ˜——K˜�šœ˜šœœ˜+Kšœt˜tJšœ˜—šœœ˜+Kšœz˜zJšœ˜ —K˜�—KšœA™Ašœœ˜)Kšœt˜tKšœn˜nJšœ˜—šœ œ˜?Kšœ~˜~Jšœ˜J˜�—Kšœ#˜#——šŸ™šžœœ ˜;Kšœ,˜,Kšœ <˜QKšœ, e˜‘Kšœ œ 3˜AKšœ˜Kšœ8˜?Kšœ§™§K™<K™;™GK˜�—šžœ˜0Kšœœœœ™9K™@šžœ#˜.Kšœœœœ™Aš œ'œœœ˜ŒKšœm˜m——K˜�Kšœ6˜6K˜�—šžœ˜0Kšœœœœ™9K™@šžœ#˜.Kšœœœœ™Aš œ$œœœ˜‡Kšœm˜m——K˜�Kšœ6˜6K˜�—šžœ˜+Kšœ0™0Kšœœœœ™9šžœ#˜.Kšœ+™+Kšœœœœ™Ašœ$œœ˜HKšœœœ˜Kšœ˜Kšœ œœ˜PKšœf˜fšœ)œ˜2Kšœ™Kšœœœ*œ™UKšœ œ˜)šœœ&œœ˜7Kšœ4˜4—šœœ˜K˜�——š œœœœœ˜HKšœ™Kšœ œœ*œ™Yšœ œ˜Kšœ}˜}Kšœ œ˜—šœœ&œœ˜9Kšœi˜iKšœ1˜1—Kšœœ˜K˜�—š œœ.œœœ˜GKšœ™Kšœ œœ*œ™Yšœ œ˜Kšœ}˜}Kšœ œ˜—šœœ&œœ˜8Kšœi˜iKšœ1˜1—Kšœœ˜K˜�—šœ˜Kšœp˜pKšœ œ˜K˜�—šœœ˜šœ œœ ˜&KšœL˜LKšœK˜KKšœI˜IKšœJ˜JKšœ>˜E—KšœV˜Všœœ !˜4Kšœ2˜2—Kšœ˜———K˜�Kšœ6˜6K˜�—Kšœc˜cKšœœœ ˜3šœœœ ˜+KšœS˜SKšœD˜DKšœ>˜E—Kšœ3˜3šœ œœ ˜&Kšœ%˜%Kšœ%˜%Kšœ>˜E—šœ œœ ˜&Kšœ"˜"Kšœ"˜"Kšœ>˜E—K˜�KšœO™OKšœ™KšœU˜UKšœT˜TKšœT˜TKšœ|˜|Kšœ|˜|K˜�KšœJ™JKšœF˜FKšœF˜FKšœA˜AKšœ œ ˜CKšœ˜K˜�—šžœœ ˜9Kšœ,˜,Kšœ%˜%Kšœ, .˜ZKšœ˜Kšœ8˜?KšœG™GKšœM™MKšœ™Kšœ™šžœ˜&Kšœœœœ™9šžœ#˜.Kšœœœœ™Ašœ$œœ˜KKšœ\˜\šœ1œ˜8Kšœq˜q—šœ/œ œ˜[Kšœœœ˜.Kšœ˜šœ˜Kšœ’˜’Kšœ€˜€Kšœ~˜~Kšœ˜Kšœ˜—Kšœœ˜*Kšœ˜———K˜�Kšœ6˜6K˜�—Kšœœ˜Kš œ œœœœ ˜RKšœœ˜šœ&œ˜>Kšœœœ˜-Kšœœœ˜*Kšœ>˜E—šœœœ˜$Kšœ"˜"Kšœ"˜"Kšœ>˜E—Kšœ<˜<šœœ˜'KšœC˜CKšœE˜EKšœ=˜D—Kšœ œ ˜CKšœ˜K˜�—šžœœ ˜5Kšœ,˜,Kšœ <˜OKšœ '˜AKšœ˜Kšœœ˜Kšœ8˜?šœ?™?K˜�—šžœ˜&Kšœœœœ™9šžœ#˜.Kšœœœœ™Ašœ$œœ˜EKšœ\˜\Kšœ œœ˜Pšœ&œ˜-K˜w—šœ$œœœ˜Qšœ˜Kšœ ˜ KšœŸ˜ŸKšœ˜Kšœž˜žKšœ˜————K˜�Kšœ6˜6K˜�—Kšœœœ ˜3Kšœœ˜šœœœ˜Kšœ"˜"Kšœ"˜"Kšœ>˜E—Kšœ<˜<šœœ˜KšœI˜IKšœ:˜:Kšœ>˜E—Kšœ œ ˜CKšœ˜K˜�—šžœœpœ8˜Ášœ1™1K˜�—šžœ˜Kšœ;œœœ™_šœœœ˜HKšœœœ˜.Kšœ˜šœ˜Kšœ’˜’Kšœ€˜€Kšœ~˜~Kšœ˜Kšœ˜—šœ˜K˜�———Kšœœ˜Kšœœ˜KšœR˜Ršœœ˜KšœC˜CKšœC˜CKšœ=˜D—Kšœ œ ˜CKšœ˜K˜�—šžœœ?œ"œ˜ KšœT™TJ˜�Jšœœœ˜0šœœ˜+Kšœ"˜"Kšœ"˜"Kšœ=˜D—Kšœ<˜BK˜�—šžœœBœ<œ8˜ÕKšœ]™]K˜�šžœ"˜-Kšœœœœœœ™[Kšœ2™2Kšœœ"˜1šœœ˜šœ˜Kš œ œœœœr˜ÎKš œ œœœœr˜ÌKš œœœœœr˜ÈKš œœœœœr˜ÊKšœ˜ ——K˜�—Jšœ=˜=Kšœ'˜'Kšœœœ˜0šœ œœ ˜&Kšœ8˜8Kšœ8˜8Kšœ=˜D—Kšœ_˜_Kšœ œ˜>Kšœ˜——šŸ™šžœœ$˜9Kšœ,™,K˜�Kšœœ=˜LKšœ7˜7KšœœK˜^šœœœœ/˜pKšœ6˜:—Kšœœ&˜4KšœœG˜Xšœœœœ-˜lKšœ1˜5—Kšœ œ"˜.KšœPœ˜TK˜�Kšœ?œœ˜dKšœAœ˜VKšœ?œœ˜dKšœ;œœ˜`Kšœ@œ˜TK˜�KšœR˜Ršœœœ,˜eKšœ3˜7—Kšœ&˜&KšœV˜Všœ!œœ.˜iKšœ2˜6—Kšœ,˜,Kšœ>œ˜OKšœ?œ˜UK˜�K˜K˜Kšœ˜K˜�—šž œœ"œ$˜[šœ œ˜Kšœœ˜œK˜�——šžœœBœ˜{Kšœ$™$K˜�JšœC˜CJšœœ/˜@K˜�—Kšœ`™`šžœœ?œœ˜[K˜�Kšœ4˜4Kšœ˜!K˜�—Kšœ]™]šžœœ?œ˜rK˜�šœœœ˜KšœD˜DKšœM˜MKšœD˜DKšœB˜BKšœC˜CKšœ^˜^Kšœ]˜]Kšœ[˜[Kšœ\˜\KšœX˜_—K˜�—šž œœ?œ˜uK˜�šœ˜šœ ˜ Kšœ œ˜%KšœB˜B—šœ˜Kšœ œ)˜5KšœB˜B—šœ˜Kšœ œJ˜VKšœB˜B—šœ˜Kšœ œL˜XKšœB˜B—Kšœ˜ —K˜�—šž œœ?œ˜uK˜�šœ!˜+šœ ˜ Kšœ œ˜&KšœB˜B—šœ˜Kšœ œ)˜5KšœB˜B—šœ˜Kšœ œH˜TKšœB˜B—šœ˜Kšœ œK˜WKšœB˜B—Kšœ˜ K˜�——šž œœ?œ˜uK˜�šœ!˜+šœ ˜ Kšœ œ3˜?KšœB˜B—šœ˜Kšœ œB˜NKšœB˜B—šœ˜Kšœ œa˜mKšœB˜B—šœ˜Kšœ œd˜pKšœB˜B—Kšœ˜ K˜�——šž œœDœœ˜mK˜�šœ ˜Kšœ*˜*KšœA˜AKšœ;˜;KšœN˜NKšœo˜oKšœX˜XKšœp˜pKšœ]˜]Kšœ˜ —K˜�—šœœœ;œœœ˜vK˜�—š žœœhœœœ˜ŸK˜�š œ5œœœ˜UKšœ#˜#Kšœ&˜&Kšœ˜ —K˜�—Kšœœ3™?šžœœœœ˜Ošœœ˜4Kšœœ˜)—K˜K˜�—Kšœœ™šžœœœ œ˜Lšœœ˜5Kšœœ˜*—K˜K˜�—šžœœ!œ œ˜]K˜�Kšœœ˜Kšœœ=˜Lšœœ˜#KšœÂ˜ÂKšœÂ˜Â—šœ˜Kšœ œ”˜ Kšœœœq˜‚Kšœœš˜¥Kšœ<˜<Kšœ=˜=K˜�—KšœN˜NKšœR˜RKšœV˜VKšœJ˜JK˜�Kšœœ˜,Kšœœ˜-K˜�—šž œœ$˜3K˜�K˜"KšœK˜KKšœj˜jKšœD˜DK˜�—šžœœ$˜8K˜�Kšœœ˜2Kšœœ0˜?Kšœ7˜7K™�Kšœ5™5Kšœœ. ˜TKšœœ ˜BKšœœ2 ˜ZKšœ œ ˜?Kšœ œ ˜BKšœœ0 ˜YKšœœ ˜FKšœœ0 ˜UK˜�šœœ˜Kšœ˜KšœN˜NKšœH˜HKšœ{˜{K˜�Kšœq˜qKšœe˜eKšœQ˜QKšœ]˜]—šœ ˜ KšœP˜PKšœ˜Kšœo˜oKšœ\˜\—K˜�Kšœ™Kšœ_˜_Kšœp˜pKšœj˜jKšœ]˜]K˜�Kšœ™Kšœm˜mKšœi˜iKšœc˜cKšœk˜kK˜�Kšœ™KšœY˜YKšœh˜hKšœo˜oKšœa˜aK˜�—šž œœ œœœœ˜\K˜�Kšœ0™0KšœJ™JKšœœD˜KKšœ œt˜ƒKšœ7œœ˜NKšœ7œœ˜NKšœ-˜-Kšœ0˜0K˜�—šžœœ œœœœœœ˜MKšœœ™šœœ˜Kšœœœœ˜3Kšœ˜Kšœ˜—Kšœœ˜——šŸ™Kšœ™šž œœœœ˜>Kšœœœ˜5Kšœœœ˜5Kšœœ˜"K˜�—šžœœœœ˜>Kšœœœ˜6Kšœ˜——šŸ#™#Kš ×™×Kš X™XKšœ œœ ˜!šœ œœ˜Kšœ œ ˜-Kšœ œ ˜#Kšœœœœœœ ˜6—Kš G™Gšž œœœœœœ˜RKšœ1˜1Kš œ œœœœ˜3K˜�—šžœœœœ˜SKšœœ!˜*Kšœœ˜"Kšœ˜Kšœ˜Kšœ˜šœœœ˜Kšœœ˜Kšœ˜ K˜�——šžœœœœœœ˜PKšœ(˜.K˜�—Kš >™>šžœœœœœœœ˜\Kšœœ œœ ˜+K˜�—šžœœ œœœœ˜MKšœœ œœ˜8K˜�—šžœœœœ˜<Kš œœœ œœ˜9Kšœ'œ˜-K˜�—Kš X™Xšžœœ:œ˜Nšœœœ(˜9Kšœ˜Kšœ˜ —K˜�—Kš &™&Kš *™*šžœœ:œœ œœ˜tšœœœ˜)Kšœ"œœœ)˜^Kšœ˜—Kšœœ˜K˜�—šžœœœœœœ˜ZKšœ˜šœ#œ˜LKšœ)˜)Kšœ˜ —K˜�—šžœœ9œ"˜vKšœd™dKšœ%™%Kšœœœ˜Kšœ œ˜ Kšœ˜šœ ˜Kšœ0œ˜6KšœœC˜aKšœo ˜€Kšœ˜ —K˜�—šžœœ9œ"˜vKšœe™eKšœ%™%Kšœœœ˜Kšœ œ˜ Kšœ˜šœ ˜Kšœ0œ˜7KšœœC˜aKšœo ˜€Kšœ˜ —K˜�—šžœœ9œ"˜wKšœd™dKšœ"œ œœ˜Lšœ˜Kšœœœ˜)Kšœ! '˜HKš œDœœœœ˜‚Kš œDœœœœ˜‚KšœœœC˜hKšœœœ˜#Kšœœœ˜#Kšœ9œœ˜NKšœ ˜———šœŸ ™KšœQ™Qšœœœ˜Kšœ˜Kšœ˜K˜�—Kšœ™šžœ˜'Kšœœœœœ œ™?Kšœœ œ˜EK˜=Kšœ˜K˜�—šžœ˜#Kšœœœœœœ™KKšœI™IKšœœ&˜5šœœœ˜<Kšœœ œ˜Mš œ œœœœ3˜hKšœ4˜8—Kš œ œœœœ˜.Kšœ9˜9—š˜Kšœ˜—K˜�—Kšœ˜—K˜�—�…—����<@�–Ò�