DIRECTORY Basics, Cabbage, CabbagePrivate, CD, CDBasics, CDCells, CDSimpleRules, CDSymbolicObjects, Connections, Convert, List, PW, PWRoute, Rope, Route, RTBasic, TerminalIO; CabbageProcsImpl: CEDAR PROGRAM IMPORTS Basics, Cabbage, CD, CDBasics, CDCells, CDSimpleRules, CDSymbolicObjects, Connections, Convert, List, PW, PWRoute, Rope, Route, RTBasic, TerminalIO EXPORTS CabbagePrivate = BEGIN sideName: ARRAY RTBasic.Side OF Rope.ROPE _ ["bottom", "top", "left", "right"]; 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.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]; 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]; designRules _ NEW[CabbagePrivate.DesignRulesRec]; designRules.horizParms.parms _ NEW[PWRoute.RouterParamsRec _ [trunkLayer: parms.horizLayer, branchLayer: parms.vertLayer, technologyKey: parms.technologyKey, wireWidthProc: NIL, signalIncomplete: parms.signalIncomplete, signalBreakAtExit: FALSE, signalSinglePinNets: parms.signalSinglePinNets]]; designRules.vertParms.parms _ NEW[PWRoute.RouterParamsRec _ [trunkLayer: parms.vertLayer, branchLayer: parms.horizLayer, technologyKey: parms.technologyKey, wireWidthProc: NIL, signalIncomplete: parms.signalIncomplete, signalBreakAtExit: FALSE, signalSinglePinNets: parms.signalSinglePinNets]]; designRules.horizParms.rules _ Route.CreateDesignRules[parms.technologyKey, hLayer, vLayer, horizontal]; designRules.vertParms.rules _ Route.CreateDesignRules[parms.technologyKey, hLayer, vLayer, vertical]}; 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 _ 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.orgin _ [handle.bottomLeft.size.x + (handle.size.x - hBottom)/2, 0]; handle.right.orgin _ [handle.size.x - handle.right.size.x, handle.bottomRight.size.y + (handle.size.y - vRight)/2]; handle.top.orgin _ [handle.topLeft.size.x + (handle.size.x - hTop)/2, handle.size.y - handle.top.size.y]; handle.left.orgin _ [0, handle.bottomLeft.size.y + (handle.size.y - vLeft)/2]; DoCorners[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 ~ { IF UseSegment[handle, segment, net] THEN newNet.width _ MAX[newNet.width, segment.range.max - segment.range.min] ELSE TerminalIO.WriteRope[Rope.Cat[Rope.Cat["Segment ignored on net: ", net.name, ", side: ", sideName[RTBasic.OtherSide[segment.side]], ", range: ["], Rope.Cat[Convert.RopeFromInt[segment.range.min], ", ", Convert.RopeFromInt[segment.range.max], "]\n"]]]}; 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.orgin.y + handle.bottom.size.y; upperY: INT _ handle.top.orgin.y - handle.inner.size.y; lowerX: INT _ handle.left.orgin.x + handle.left.size.x; upperX: INT _ handle.right.orgin.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.orgin _ actualInnerPos}; 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]}}; IF handle.connections # NIL THEN InitWires[handle]; AdjustPositions[handle]; [] _ Connections.EnumerateNets[handle.connections, EachNet]}; SortSegments: PROC [handle: CabbagePrivate.Handle, net: Connections.Net] RETURNS [sortedSegments: CabbagePrivate.SegmentSeq _ NIL] ~ { CountSegments: Connections.EachSegmentAction ~ { IF UseSegment[handle, segment, net] THEN numSegments _ numSegments + 1}; PinCompare: List.CompareProc ~ { pos1, pos2: INT; TRUSTED{ pos1 _ PosOf[handle, net, LOOPHOLE[ref1]]; pos2 _ PosOf[handle, net, 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]}; IF UseSegment[handle, segment, net] THEN { 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, net, startSeg] + PosOf[handle, net, endSeg]} ELSE { endSeg _ sortedSegments[index]; startSeg _ sortedSegments[0]; length _ PosOf[handle, net, endSeg] - PosOf[handle, net, 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, net, segmentPair.seg1]; pos2: INT _ PosOf[handle, net, 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.orgin.x, handle.inner.orgin.x + handle.inner.size.x]; vertRange: Connections.Range _ [handle.inner.orgin.y, handle.inner.orgin.y + handle.inner.size.y]; IF handle.routeType = normal THEN { handle.detailedRouting.channels[bottom] _ RouteChannel[handle, handle.bottom, handle.inner, bottom, bottomLeft, bottomRight, horizRange, horizParms]; handle.detailedRouting.channels[right] _ RouteChannel[handle, handle.inner, handle.right, right, rightBottom, rightTop, vertRange, vertParms]; handle.detailedRouting.channels[top] _ RouteChannel[handle, handle.inner, handle.top, top, topLeft, topRight, horizRange, horizParms]; handle.detailedRouting.channels[left] _ RouteChannel[handle, handle.left, handle.inner, left, leftBottom, leftTop, vertRange, vertParms]} ELSE { -- routeType = padLimited handle.detailedRoutingPL.channels[right] _ RouteChannel[handle, handle.inner, handle.right, right, rightBottom, rightTop, vertRange, vertParms]; handle.detailedRoutingPL.channels[left] _ RouteChannel[handle, handle.left, handle.inner, left, leftBottom, leftTop, vertRange, vertParms]}}; RouteChannel: PROC [handle: CabbagePrivate.Handle, llObject, urObject: CabbagePrivate.ObjectDescription, side: Route.Side, llDiv, urDiv: CabbagePrivate.Division, range: Connections.Range, parms: CabbagePrivate.ParmSet] RETURNS [channel: CabbagePrivate.Channel] ~ { result: Route.RoutingResult; retrieveRect: Route.RefRect; rect: CD.Rect; llSide, blSide: Route.Side; isX: BOOLEAN; dist, size, offset: INT; obj1, obj2, bottomOrLeftObj, topOrRightObj: Cabbage.Object; SELECT side FROM bottom => { isX _ FALSE; llSide _ top; blSide _ left}; right => { isX _ TRUE; llSide _ right; blSide _ bottom}; top => { isX _ FALSE; llSide _ top; blSide _ left}; left => { isX _ TRUE; llSide _ right; blSide _ bottom}; ENDCASE; obj1 _ BuildObject[handle, llObject, llSide, range, parms.rules]; obj2 _ BuildObject[handle, urObject, RTBasic.OtherSide[llSide], range, parms.rules]; bottomOrLeftObj _ BuildEnd[handle, llDiv, blSide, parms.rules]; topOrRightObj _ BuildEnd[handle, urDiv, RTBasic.OtherSide[blSide], parms.rules]; parms.parms.wireWidthProc _ GetWireWidth; parms.parms.context _ handle.widthTable; result _ PWRoute.DoRoute[obj1, obj2, bottomOrLeftObj, topOrRightObj, parms.parms, isX, channel]; rect _ result.routingRect; offset _ result.routingArea.rules.trunkToTrunk; SELECT side FROM bottom => { size _ urObject.orgin.y - (llObject.orgin.y + llObject.size.y); dist _ MIN[rect.y1, rect.y2 - size + offset]; retrieveRect _ NEW[CD.Rect _ [rect.x1, dist, rect.x2, rect.y2 + offset]]}; right => { size _ urObject.orgin.x - (llObject.orgin.x + llObject.size.x); dist _ MAX[rect.x2, size - offset]; retrieveRect _ NEW[CD.Rect _ [rect.x1 - offset, rect.y1, dist, rect.y2]]}; top => { size _ urObject.orgin.y - (llObject.orgin.y + llObject.size.y); dist _ MAX[rect.y2, size - offset]; retrieveRect _ NEW[CD.Rect _ [rect.x1, rect.y1 - offset, rect.x2, dist]]}; left => { size _ urObject.orgin.x - (llObject.orgin.x + llObject.size.x); dist _ MIN[rect.x1, rect.x2 - size + offset]; retrieveRect _ NEW[CD.Rect _ [dist, rect.y1, rect.x2 + offset, rect.y2]]}; ENDCASE; channel.object _ PWRoute.GetRouting[result, retrieveRect]; channel.size _ IRSize[channel.object]}; BuildObject: PROC [handle: CabbagePrivate.Handle, objectDes: CabbagePrivate.ObjectDescription, side: Route.Side, range: Connections.Range, rules: Route.DesignRules] RETURNS [shell: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { EachNet: Connections.EachNetAction ~ { EachSegment: Connections.EachSegmentAction ~ { IF (segment.object = objectDes.object AND segment.side = side) THEN { orgin: INT _ SELECT side FROM bottom, top => objectDes.orgin.x, right, left => objectDes.orgin.y, ENDCASE => Cabbage.Error[programmingError, "Not suppose to happen."]; adjSeg: Connections.Range _ [orgin + segment.range.min, orgin + segment.range.max]; useThisOne: BOOLEAN _ objectDes.object # handle.inner.object AND UseSegment[handle, segment, net]; IF useThisOne AND CrossesBoundry[adjSeg, range] THEN Cabbage.Signal[callingError, Rope.Cat["Segment on net: ", net.name, " crosses vertical seam"]]; IF (useThisOne OR objectDes.object = handle.inner.object) AND ProperSubset[adjSeg, range] THEN { numPins _ numPins + 1; SELECT side FROM bottom => IncludePin[object: shell, name: net.name, denotes: [adjSeg.min, oldIR.y1, adjSeg.max, oldIR.y1+rules.trunkWidth], layer: segment.layer]; right => IncludePin[object: shell, name: net.name, denotes: [oldIR.x2-rules.trunkWidth, adjSeg.min, oldIR.x2, adjSeg.max], layer: segment.layer]; top => IncludePin[object: shell, name: net.name, denotes: [adjSeg.min, oldIR.y2-rules.trunkWidth, adjSeg.max, oldIR.y2], layer: segment.layer]; left => IncludePin[object: shell, name: net.name, denotes: [oldIR.x1, adjSeg.min, oldIR.x1+rules.trunkWidth, adjSeg.max], layer: segment.layer]; ENDCASE}}}; [] _ Connections.EnumerateSegments[net, EachSegment]}; numPins: INT _ 0; oldIR: CD.Rect _ CD.InterestRect[objectDes.object]; interestRect: CD.Rect; [] _ Connections.EnumerateNets[handle.connections, EachNet]; SELECT side FROM bottom, top => interestRect _ [range.min, oldIR.y1, range.max, oldIR.y2]; left, right => interestRect _ [oldIR.x1, range.min, oldIR.x2, range.max]; ENDCASE; CDCells.SetInterestRect[shell, interestRect]; IF numPins > 0 THEN [] _ CDCells.RepositionCell[shell, NIL]; }; BuildEnd: PROC [handle: CabbagePrivate.Handle, division: CabbagePrivate.Division, side: Route.Side, rules: Route.DesignRules] RETURNS [shell: Cabbage.Object _ CDCells.CreateEmptyCell[]] ~ { EachExit: EachExitAction ~ { width: INT _ MAX[rules.trunkWidth, net.width]; numPins _ numPins + 1; 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; }; numPins: INT _ 0; dist: INT _ 0; interestRect: CD.Rect; [] _ EnumerateExits[handle.globalRouting.exitLists[division], division, EachExit]; SELECT side FROM bottom, top => interestRect _ [0, 0, dist + rules.trunkWidth, 2*rules.trunkWidth]; left, right => interestRect _ [0, 0, 2*rules.trunkWidth, dist + rules.trunkWidth]; ENDCASE; CDCells.SetInterestRect[shell, interestRect]; IF numPins > 0 THEN [] _ CDCells.RepositionCell[shell, NIL]; }; AdjustPositions: PROC [handle: CabbagePrivate.Handle] ~ { routeType: CabbagePrivate.RouteType _ handle.routeType; sizeBottomArea: INT _ handle.inner.orgin.y - (handle.bottom.orgin.y + handle.bottom.size.y); sizeBottomRouting: INT _ IF handle.routeType = normal THEN handle.detailedRouting.channels[bottom].size.y ELSE handle.detailedRoutingPL.switchBoxes[bottom].size.y; adjBottom: INT _ sizeBottomRouting - sizeBottomArea; sizeLeftArea: INT _ handle.inner.orgin.x - (handle.left.orgin.x + handle.left.size.x); sizeLeftRouting: INT _ IF handle.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.orgin _ CDBasics.AddPoints[handle.inner.orgin, [MAX[0, adjLeft], MAX[0, adjBottom]]]; handle.bottom.orgin _ CDBasics.AddPoints[handle.bottom.orgin, [MAX[0, adjLeft], 0]]; handle.right.orgin _ CDBasics.AddPoints[handle.right.orgin, [MAX[0, adjLeft], MAX[0, adjBottom]]]; handle.top.orgin _ CDBasics.AddPoints[handle.top.orgin, [MAX[0, adjLeft], MAX[0, adjBottom]]]; handle.left.orgin _ CDBasics.AddPoints[handle.left.orgin, [0, MAX[0, adjBottom]]]; sizeTopArea _ handle.top.orgin.y - (handle.inner.orgin.y + handle.inner.size.y); sizeTopRouting _ IF handle.routeType = normal THEN handle.detailedRouting.channels[top].size.y ELSE handle.detailedRoutingPL.switchBoxes[top].size.y; adjTop _ sizeTopRouting - sizeTopArea; sizeRightArea _ handle.right.orgin.x - (handle.inner.orgin.x + handle.inner.size.x); sizeRightRouting _ IF handle.routeType = normal THEN handle.detailedRouting.channels[right].size.x ELSE handle.detailedRoutingPL.channels[right].size.x; adjRight _ sizeRightRouting - sizeRightArea; handle.top.orgin _ CDBasics.AddPoints[handle.top.orgin, [0, MAX[0, adjTop]]]; handle.right.orgin _ CDBasics.AddPoints[handle.right.orgin, [MAX[0, adjRight], 0]]; [] _ GetSize[handle]; DoCorners[handle]; IF handle.routeType = normal THEN { handle.detailedRouting.channels[bottom].orgin _ [handle.inner.orgin.x, handle.inner.orgin.y - handle.detailedRouting.channels[bottom].size.y]; handle.detailedRouting.channels[right].orgin _ [handle.inner.orgin.x + handle.inner.size.x, handle.inner.orgin.y]; handle.detailedRouting.channels[top].orgin _ [handle.inner.orgin.x, handle.inner.orgin.y + handle.inner.size.y]; handle.detailedRouting.channels[left].orgin _ [handle.inner.orgin.x - handle.detailedRouting.channels[left].size.x, handle.inner.orgin.y]; handle.detailedRouting.switchBoxes[bottomLeft].orgin _ [handle.left.orgin.x + handle.left.size.x, handle.bottom.orgin.y + handle.bottom.size.y]; handle.detailedRouting.switchBoxes[bottomRight].orgin _ [handle.inner.orgin.x + handle.inner.size.x, handle.bottom.orgin.y + handle.bottom.size.y]; handle.detailedRouting.switchBoxes[topRight].orgin _ [handle.inner.orgin.x + handle.inner.size.x, handle.inner.orgin.y + handle.inner.size.y]; handle.detailedRouting.switchBoxes[topLeft].orgin _ [handle.left.orgin.x + handle.left.size.x, handle.inner.orgin.y + handle.inner.size.y]} ELSE { -- routeType = padLimited handle.detailedRoutingPL.channels[right].orgin _ [handle.inner.orgin.x + handle.inner.size.x, handle.inner.orgin.y]; handle.detailedRoutingPL.channels[left].orgin _ [handle.inner.orgin.x - handle.detailedRoutingPL.channels[left].size.x, handle.inner.orgin.y]; handle.detailedRoutingPL.switchBoxes[bottom].orgin _ [handle.left.orgin.x + handle.left.size.x, handle.bottom.orgin.y + handle.bottom.size.y]; handle.detailedRoutingPL.switchBoxes[top].orgin _ [handle.left.orgin.x + handle.left.size.x, handle.inner.orgin.y + handle.inner.size.y]}; }; RouteSwitchBoxes: PROC [handle: CabbagePrivate.Handle] ~ { horizParms: CabbagePrivate.ParmSet _ handle.rules.horizParms; leftHorizRange: Connections.Range _ [handle.left.orgin.x + handle.left.size.x, handle.inner.orgin.x]; rightHorizRange: Connections.Range _ [handle.inner.orgin.x + handle.inner.size.x, handle.right.orgin.x]; bottomVertRange: Connections.Range _ [handle.bottom.orgin.y + handle.bottom.size.y, handle.inner.orgin.y]; topVertRange: Connections.Range _ [handle.inner.orgin.y + handle.inner.size.y, handle.top.orgin.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; horizRange: Connections.Range _ [handle.left.orgin.x + handle.left.size.x, handle.right.orgin.x]; bottomVertRange: Connections.Range _ [handle.bottom.orgin.y + handle.bottom.size.y, handle.inner.orgin.y]; topVertRange: Connections.Range _ [handle.inner.orgin.y + handle.inner.size.y, handle.top.orgin.y]; 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}; GetWireWidth: PWRoute.WireWidthProc ~ { table: Connections.Table _ NARROW[context]; net: Connections.Net _ Connections.Fetch[table, netName].net; wireWidth _ net.width}; RouteSwitchBoxPL: PROC [handle: CabbagePrivate.Handle, side: RTBasic.TBSide, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet] RETURNS [switchBox: CabbagePrivate.SwitchBox] ~ { bottomObject, topObject: Cabbage.Object; leftObject: Cabbage.Object _ BuildObject[handle, handle.left, right, vertRange, parms.rules]; rightObject: Cabbage.Object _ BuildObject[handle, handle.right, left, vertRange, parms.rules]; innerRange: Connections.Range _ [handle.inner.orgin.x, handle.inner.orgin.x + handle.inner.size.x]; SELECT side FROM bottom => { innerObject: Cabbage.Object _ BuildObject[handle, handle.inner, bottom, innerRange, parms.rules]; bottomObject _ BuildObject[handle, handle.bottom, top, horizRange, parms.rules]; topObject _ PW.AbutListX[LIST[handle.detailedRoutingPL.channels[left].object, innerObject, handle.detailedRoutingPL.channels[right].object]]}; top => { innerObject: Cabbage.Object _ BuildObject[handle, handle.inner, top, innerRange, parms.rules]; bottomObject _ PW.AbutListX[LIST[handle.detailedRoutingPL.channels[left].object, innerObject, handle.detailedRoutingPL.channels[right].object]]; topObject _ BuildObject[handle, handle.top, bottom, horizRange, 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 _ IRSize[switchBox.object]}; RouteSwitchBox: PROC [handle: CabbagePrivate.Handle, corner: CabbagePrivate.Corners, horizRange, vertRange: Connections.Range, parms: CabbagePrivate.ParmSet] RETURNS [switchBox: CabbagePrivate.SwitchBox] ~ { bottomObject, topObject, leftObject, rightObject: Cabbage.Object; SELECT corner FROM bottomLeft => { bottomObject _ BuildObject[handle, handle.bottom, top, horizRange, parms.rules]; topObject _ handle.detailedRouting.channels[left].object; leftObject _ BuildObject[handle, handle.left, right, vertRange, parms.rules]; rightObject _ handle.detailedRouting.channels[bottom].object}; bottomRight => { bottomObject _ BuildObject[handle, handle.bottom, top, horizRange, parms.rules]; topObject _ handle.detailedRouting.channels[right].object; rightObject _ BuildObject[handle, handle.right, left, vertRange, parms.rules]; leftObject _ handle.detailedRouting.channels[bottom].object}; topRight => { bottomObject _ handle.detailedRouting.channels[right].object; topObject _ BuildObject[handle, handle.top, bottom, horizRange, parms.rules]; rightObject _ BuildObject[handle, handle.right, left, vertRange, parms.rules]; leftObject _ handle.detailedRouting.channels[top].object}; topLeft => { topObject _ BuildObject[handle, handle.top, bottom, horizRange, parms.rules]; bottomObject _ handle.detailedRouting.channels[left].object; leftObject _ BuildObject[handle, handle.left, right, vertRange, parms.rules]; rightObject _ handle.detailedRouting.channels[top].object}; 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 _ IRSize[switchBox.object]; }; MakeChip: PUBLIC PROC [handle: CabbagePrivate.Handle] RETURNS [chip: Cabbage.Object] ~ { chip _ CDCells.CreateEmptyCell[]; IncludeObject[handle.inner.object, handle.inner.orgin, chip]; IncludeObject[handle.bottomLeft.object, handle.bottomLeft.orgin, chip]; IncludeObject[handle.bottom.object, handle.bottom.orgin, chip]; IncludeObject[handle.bottomRight.object, handle.bottomRight.orgin, chip]; IncludeObject[handle.right.object, handle.right.orgin, chip]; IncludeObject[handle.topRight.object, handle.topRight.orgin, chip]; IncludeObject[handle.top.object, handle.top.orgin, chip]; IncludeObject[handle.topLeft.object, handle.topLeft.orgin, chip]; IncludeObject[handle.left.object, handle.left.orgin, chip]; IF handle.routeType = normal THEN { IncludeObject[handle.detailedRouting.channels[bottom].object, handle.detailedRouting.channels[bottom].orgin, chip]; IncludeObject[handle.detailedRouting.channels[right].object, handle.detailedRouting.channels[right].orgin, chip]; IncludeObject[handle.detailedRouting.channels[top].object, handle.detailedRouting.channels[top].orgin, chip]; IncludeObject[handle.detailedRouting.channels[left].object, handle.detailedRouting.channels[left].orgin, chip]; IncludeObject[handle.detailedRouting.switchBoxes[bottomLeft].object, handle.detailedRouting.switchBoxes[bottomLeft].orgin, chip]; IncludeObject[handle.detailedRouting.switchBoxes[bottomRight].object, handle.detailedRouting.switchBoxes[bottomRight].orgin, chip]; IncludeObject[handle.detailedRouting.switchBoxes[topRight].object, handle.detailedRouting.switchBoxes[topRight].orgin, chip]; IncludeObject[handle.detailedRouting.switchBoxes[topLeft].object, handle.detailedRouting.switchBoxes[topLeft].orgin, chip]} ELSE { IncludeObject[handle.detailedRoutingPL.channels[right].object, handle.detailedRoutingPL.channels[right].orgin, chip]; IncludeObject[handle.detailedRoutingPL.channels[left].object, handle.detailedRoutingPL.channels[left].orgin, chip]; IncludeObject[handle.detailedRoutingPL.switchBoxes[bottom].object, handle.detailedRoutingPL.switchBoxes[bottom].orgin, chip]; IncludeObject[handle.detailedRoutingPL.switchBoxes[top].object, handle.detailedRoutingPL.switchBoxes[top].orgin, chip]}; [] _ CDCells.RepositionCell[chip, NIL]; }; IncludeObject: PROC [object: Cabbage.Object, orgin: CD.Position, chip: Cabbage.Object] ~ { IF object # NIL THEN [] _ CDCells.IncludeOb[design: NIL, cell: chip, ob: object, position: orgin, orientation: CD.original, cellCSystem: interrestCoords, obCSystem: interrestCoords, mode: dontPropagate]}; PosOf: PROC [handle: CabbagePrivate.Handle, net: Connections.Net, segment: Connections.Segment] RETURNS [INT] ~ { RETURN[MiddleOfRange[RangeOf[handle, net, segment]]]}; RangeOf: PROC [handle: CabbagePrivate.Handle, net: Connections.Net, segment: Connections.Segment] RETURNS [range: Connections.Range] ~ { SELECT TRUE FROM segment.object = handle.inner.object => range _ InnerRange[handle, net, segment]; segment.object = handle.bottom.object => range _ OuterRange[handle, net, segment, bottom]; segment.object = handle.right.object => range _ OuterRange[handle, net, segment, right]; segment.object = handle.top.object => range _ OuterRange[handle, net, segment, top]; segment.object = handle.left.object => range _ OuterRange[handle, net, segment, left]; ENDCASE => Cabbage.Signal[callingError, Rope.Cat["Invalid object in net: ", net.name]]}; MiddleOfRange: PROC [range: Connections.Range] RETURNS [INT] ~ { RETURN[(range.min+range.max)/2]}; InnerRange: PROC [handle: CabbagePrivate.Handle, net: Connections.Net, segment: Connections.Segment] RETURNS [range: Connections.Range] ~ { orgin: INT; SELECT segment.side FROM bottom => {orgin _ handle.inner.orgin.x; range _ [orgin + segment.range.min, orgin + segment.range.max]}; right => {orgin _ handle.size.x + handle.inner.orgin.y; range _ [orgin + segment.range.min, orgin + segment.range.max]}; top => {orgin _ handle.size.x +handle.size.y + (handle.size.x - handle.inner.orgin.x); range _ [orgin - segment.range.max, orgin - segment.range.min]}; left => {orgin _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.orgin.y); range _ [orgin - segment.range.max, orgin - segment.range.min]}; ENDCASE}; OuterRange: PROC [handle: CabbagePrivate.Handle, net: Connections.Net, segment: Connections.Segment, side: Route.Side] RETURNS [range: Connections.Range] ~ { orgin: INT; IF segment.side # RTBasic.OtherSide[side] THEN Cabbage.Signal[callingError, Rope.Cat["Invalid side in net: ", net.name]]; SELECT side FROM bottom => {orgin _ handle.bottom.orgin.x; range _ [orgin + segment.range.min, orgin + segment.range.max]}; right => {orgin _ handle.size.x + handle.right.orgin.y; range _ [orgin + segment.range.min, orgin + segment.range.max]}; top => {orgin _ handle.size.x +handle.size.y + (handle.size.x - handle.top.orgin.x); range _ [orgin - segment.range.max, orgin - segment.range.min]}; left => {orgin _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.left.orgin.y); range _ [orgin - segment.range.max, orgin - segment.range.min]}; ENDCASE}; PosOfDivision: PROC [handle: CabbagePrivate.Handle, division: CabbagePrivate.Division] RETURNS [pos: INT] ~ { SELECT division FROM bottomLeft => pos _ handle.inner.orgin.x; bottomRight => pos _ handle.inner.orgin.x + handle.inner.size.x; rightBottom => pos _ handle.size.x + handle.inner.orgin.y; rightTop => pos _ handle.size.x + handle.inner.orgin.y + handle.inner.size.y; topRight => pos _ handle.size.x +handle.size.y + (handle.size.x - handle.inner.orgin.x - handle.inner.size.x); topLeft => pos _ handle.size.x +handle.size.y + (handle.size.x - handle.inner.orgin.x); leftTop => pos _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.orgin.y - handle.inner.size.y); leftBottom => pos _ 2*handle.size.x +handle.size.y + (handle.size.y - handle.inner.orgin.y); ENDCASE; }; IRSize: PROC [obj: Cabbage.Object] RETURNS [size: CD.Position] = {size _ CDBasics.SizeOfRect[CD.InterestRect[obj]]}; 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; IF handle.routeType = normal THEN { vMiddle _ handle.bottom.size.y + handle.detailedRouting.channels[bottom].size.y + handle.inner.size.y + handle.detailedRouting.channels[top].size.y + handle.top.size.y; hMiddle _ handle.left.size.x + handle.detailedRouting.channels[left].size.x + handle.inner.size.x + handle.detailedRouting.channels[right].size.x + handle.right.size.x} ELSE { hCenter: INT _ handle.detailedRoutingPL.channels[left].size.x + handle.inner.size.x + handle.detailedRoutingPL.channels[right].size.x; hInner: INT _ MAX[hCenter, handle.detailedRoutingPL.switchBoxes[top].size.x, handle.detailedRoutingPL.switchBoxes[bottom].size.x]; vMiddle _ handle.bottom.size.y + handle.detailedRoutingPL.switchBoxes[bottom].size.y + handle.inner.size.y + handle.detailedRoutingPL.switchBoxes[top].size.y + 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.orgin _ [0, 0]; handle.bottomRight.orgin _ [handle.size.x - handle.bottomRight.size.x, 0]; handle.topRight.orgin _ [handle.size.x - handle.topRight.size.x, handle.size.y - handle.topRight.size.y]; handle.topLeft.orgin _ [0, handle.size.y - handle.topLeft.size.y]}; 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 _ CDCells.IncludeOb[design: NIL, cell: object, ob: pin, position: CDBasics.BaseOfRect[denotes], orientation: CD.original, cellCSystem: interrestCoords, obCSystem: interrestCoords, mode: dontPropagate].newInst; CDSymbolicObjects.SetName[pinInstance, name]; CDSymbolicObjects.SetLayer[pinInstance, layer]; }; UseSegment: PROC [handle: CabbagePrivate.Handle, segment: Connections.Segment, net: Connections.Net] RETURNS [useIt: BOOLEAN _ TRUE] ~ { IF net = handle.vddNet AND segment.object # handle.inner.object AND SegsOverlap[handle, segment, net, handle.gndNet] THEN useIt _ FALSE ELSE IF net = handle.gndNet AND segment.object # handle.inner.object AND SegsOverlap[handle, segment, net, handle.vddNet] THEN useIt _ FALSE}; SegsOverlap: PROC [handle: CabbagePrivate.Handle, testSegment: Connections.Segment, testNet, net: Connections.Net] RETURNS [overlaps: BOOLEAN] ~ { EachSegment: Connections.EachSegmentAction ~ { IF Overlaps[expandedRange, RangeOf[handle, net, segment]] THEN quit _ TRUE}; testRange: Connections.Range _ RangeOf[handle, testNet, testSegment]; spacing: INT _ IF testSegment.side = top OR testSegment.side = bottom THEN handle.rules.vertParms.rules.branchToBranch ELSE handle.rules.horizParms.rules.branchToBranch; expandedRange: Connections.Range _ [testRange.min - spacing, testRange.max + spacing]; overlaps _ Connections.EnumerateSegments[net, EachSegment]}; Overlaps: PUBLIC PROC [r1, r2: Connections.Range] RETURNS [BOOL] = { RETURN [(r1.min<=r2.max) AND (r2.min<=r1.max)]; }; END. BCabbageProcsImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reversed. Created by Bryan Preas, June 4, 1986 4:02:08 pm PDT Louis Monier June 13, 1986 6:36:51 pm PDT Initialization Define the routing design rules. technologyKey values are predefinded for now. horizLayer, vertLayer should be "poly", "metal" or "metal2". Global Routing 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 adjust he positions of everything to account for the actual channel widths adjust for the vertical dimension route all of the switchBoxes (the corners) route the bottom and top switchBoxes PROC [netName: ROPE, context: REF ANY] RETURNS [wireWidth: INT] Object Generation Utility Procedures Find the position of a segment projected to the periphery. The lower left corner is the orgin. Find the range of a segment projected to the periphery. The lower left corner is the orgin. Given a segment, compute the middle of the connection point returns TRUE if either end but not both ends of r1 is within r2 returns TRUE if r1 is within r2 Κ\˜– "Cedar" stylešœ™Jšœ Οmœ1™K˜—šŸœ˜&šŸ œ#˜.šžœ"ž˜(Kšœžœ5˜G—Kšžœώ˜‚—K˜Kšœžœ:˜Wšžœžœ˜Kšœ5˜5—Kšœ=˜=K˜—K˜1KšœE˜EKšœ=˜=K˜—šŸ œžœžœ+žœ˜UK˜Kšœžœ˜'Kšœžœ0˜;Kšœžœ,˜7Kšœžœ,˜7Kšœžœ.˜9š žœžœžœžœžœ˜pKšœc˜cKšœ=˜=—Kšœ%˜%K˜—Lš ™šŸ œžœžœ$˜™>š Ÿœžœbžœ-žœžœ˜ΎK˜Kšœ žœžœžœ˜Kšœ žœ%˜3Kšœžœ˜ Kšœ&˜&šžœžœžœ#ž˜6šžœ(žœ˜0Kšœ#˜#Kšœ˜KšœO˜O—šžœ˜Kšœ˜Kšœ˜KšœD˜D—šžœžœ˜Kšœ˜Kšœ˜Kšœ˜—Kšžœ˜—š žœžœžœžœžœ˜9K˜:——K˜Kšœ ™ šŸœžœc˜|J˜Kšœžœ(˜1Kšœžœ(˜1šžœ žœ˜šžœžœž˜<šžœ"žœ!ž˜LKšœ$žœ(˜P—Kšžœ˜——šžœ˜šžœžœž˜<šžœ!žœ!ž˜JKšœ$žœ(˜P—Kšžœ˜ ——K˜K˜—Lš ™šŸ œžœžœ$˜>K˜K˜K˜Kšžœžœ˜:Kšžœ˜ K˜K˜—KšœT™TšŸ œžœ$˜7K˜Kšœ=˜=Kšœ;˜;Kšœc˜cKšœb˜bK˜šžœžœ˜#Kšœ•˜•KšœŽ˜ŽKšœ†˜†Kšœ‰˜‰—šžœ‘˜ Kšœ˜Kšœ˜—K˜—Kšœ™šŸ œžœ ˜2Kšœ5˜5Kšœ˜Kšœ@˜@Kšœ˜Kšžœ&˜-K˜Jšœ˜Jšœ˜Jšœžœ˜Jšœ˜Jšœžœ˜ Jšœžœ˜Kšœ;˜;K˜šžœž˜šœ ˜ Jšœžœ˜*—šœ ˜ Kšœžœ#˜-—šœ˜Jšœžœ˜*—šœ ˜ Kšœžœ#˜-—Kšžœ˜—J˜JšœA˜AJšœT˜TJšœ?˜?JšœP˜PJ˜J˜)J˜(Jšœ`˜`K˜K˜Kšœ/˜/šžœž˜šœ ˜ Kšœ?˜?Kšœžœ#˜-Kšœžœžœ5˜J—šœ ˜ Kšœ?˜?Kšœžœ˜#Kšœžœžœ5˜J—šœ˜Kšœ?˜?Kšœžœ˜#Kšœžœžœ5˜J—šœ ˜ Kšœ?˜?Kšœžœ#˜-Kšœžœžœ5˜J—Kšžœ˜K˜—Jšœ:˜:Jšœ'˜'K˜—šŸ œžœ”žœ8˜δK˜šŸœ˜&šŸ œ#˜.šžœ$žœžœ˜Ešœžœ˜ šžœž˜Kšœ!˜!Kšœ!˜!Kšžœ>˜E——KšœS˜SKšœ žœ*žœ"˜bšžœ žœžœ˜5K˜_—šžœ žœ)žœžœ˜aKšœ˜šžœž˜Kšœ’˜’Kšœ‘˜‘Kšœ˜Kšœ˜Kšžœ˜ ————K˜Kšœ6˜6K˜—Kšœ žœ˜Kšœžœžœ ˜3Kšœžœ˜Kšœ<˜<šžœž˜KšœI˜IKšœI˜IKšžœ˜—Jšœ-˜-šžœ žœ˜Jšœ#žœ˜(—K˜K˜—šŸœžœpžœ8˜½K˜šŸœ˜˜Kšœžœžœ˜.Kšœ˜Kšœ˜šžœž˜Kšœ’˜’Kšœ€˜€Kšœ~˜~Kšœ˜Kšžœ˜—Kšœ˜Kšœ˜K˜——Kšœ žœ˜Kšœžœ˜Kšœžœ˜KšœR˜Ršžœž˜KšœR˜RKšœR˜RKšžœ˜—Jšœ-˜-šžœ žœ˜Jšœ#žœ˜(—K˜K˜—KšœJ™JšŸœžœ$˜9Kšœ!™!K˜Kšœ7˜7KšœžœI˜\šœžœžœžœ/˜iKšžœ5˜9—Kšœ žœ&˜4KšœžœE˜Všœžœžœžœ-˜eKšžœ0˜4—Kšœ žœ"˜.KšœPžœ˜TK˜Kšœ=žœžœ˜bKšœ?žœ˜TKšœ=žœžœ˜bKšœ9žœžœ˜^Kšœ>žœ˜RK˜KšœP˜Pšœžœžœ,˜^Kšžœ2˜6—Kšœ&˜&KšœT˜Tšœžœžœ.˜bKšžœ1˜5—Kšœ,˜,Kšœ<žœ˜MKšœ=žœ˜SJ˜J˜J˜šžœžœ˜#JšœŽ˜ŽJšœr˜rJšœp˜pJšœŠ˜ŠJ˜Jšœ˜Jšœ“˜“JšœŽ˜ŽJšœ‹˜‹—šžœ‘˜ Jšœt˜tJšœŽ˜ŽJšœŽ˜ŽJšœŠ˜Š—Jšœ˜K˜—Kšœ*™*šŸœžœ$˜:K˜Kšœ=˜=K˜Kšœe˜eKšœh˜hKšœj˜jKšœc˜cK˜Kšœ$žœ&žœ˜TKšœ˜K˜Kšœ$žœ&žœ˜TKšœ„˜„K˜Kšœ$žœ&žœ˜TKšœ{˜{K˜Kšœ$žœ&žœ˜TKšœx˜xKšœ$žœ&žœ˜VK˜—Kšœ$™$šŸœžœ$˜˜>K˜—šœ˜KšœP˜PKšœ:˜:KšœN˜NKšœ=˜=K˜—šœ ˜ Kšœ=˜=KšœM˜MKšœN˜NKšœ:˜:K˜—šœ ˜ KšœM˜MKšœ<˜