DIRECTORY Basics, CD, CDBasics, CDSymbolicObjects, List, Rope, Route, RouteChannel, RoutePrivate, RouteUtil; RouteChannelInitImpl: CEDAR PROGRAM IMPORTS CDBasics, CDSymbolicObjects, List, Rope, Route, RouteChannel, RouteUtil EXPORTS RouteChannel SHARES Route = BEGIN ChanSideName: PUBLIC ARRAY RouteChannel.ChanSide OF Rope.ROPE _ ["chanBottom", "chanTop", "chanLeft", "chanRight"]; AboveOrBelowName: PUBLIC ARRAY RouteChannel.AboveOrBelow OF Rope.ROPE _ ["above", "below"]; GoingName: PUBLIC ARRAY RouteChannel.GoingDirection OF Rope.ROPE _ ["leftToRight", "rightToLeft"]; InitChannel: PUBLIC PROCEDURE[routingArea: Route.RoutingArea, sideOrgins: Route.PositionVec, routingRect: Route.Rect, routerUsed: RoutePrivate.RouterUsed, signalSinglePinNets, signalCoincidentPins, okToDiddleLLPins, okToDiddleURPins: BOOLEAN] = { netTab: RoutePrivate.NetTab; parms: RoutePrivate.RoutingAreaParms _ NARROW[routingArea.parms]; nRect: Route.Rect _ CDBasics.ReInterpreteRect[routingRect]; lowPos: RoutePrivate.PQPosition _ RouteUtil.XYToPQ[routingArea, [nRect.x1, nRect.y1]]; upPos: RoutePrivate.PQPosition _ RouteUtil.XYToPQ[routingArea, [nRect.x2, nRect.y2]]; rRect: RoutePrivate.PQRect _ [lowPos, upPos]; channelData: RouteChannel.ChannelData _ NEW[RouteChannel.ChannelDataRec]; channelData.constraints _ NIL; channelData.chanSides[chanTop] _ NEW[RouteChannel.RoutingChannelSidesRec]; channelData.chanSides[chanBottom] _ NEW[RouteChannel.RoutingChannelSidesRec]; channelData.chanSides[chanLeft] _ NEW[RouteChannel.RoutingChannelSidesRec]; channelData.chanSides[chanRight] _ NEW[RouteChannel.RoutingChannelSidesRec]; channelData.chanTracks _ NEW[RouteChannel.RoutingChannelTracksRec]; channelData.chanPins _ NEW[RouteChannel.RoutingChannelPinsRec]; channelData.chanParms _ NEW[RouteChannel.ChanParmsRec]; routingArea.privateData _ channelData; parms.routerUsed _ routerUsed; channelData.chanParms.emptyTrackLimit _ MAX[2, RouteChannel.InfluenceTracks[routingArea, parms.widestTrunk]]; channelData.chanParms.maxToConvert _ 10*routingArea.rules.branchToBranch; ConvertSide[routingArea, chanBottom, sideOrgins, rRect]; ConvertSide[routingArea, chanTop, sideOrgins, rRect]; ConvertEnd[routingArea, chanLeft, sideOrgins, rRect]; ConvertEnd[routingArea, chanRight, sideOrgins, rRect]; FixOrgins[routingArea, rRect]; netTab _ NARROW[routingArea.nets, RoutePrivate.NetTab]; FOR netIndex: RoutePrivate.ZMaxNets IN [1 .. netTab.count] DO InitNet[routingArea, netTab.n[netIndex], signalSinglePinNets, signalCoincidentPins]; ENDLOOP; IF parms.routerUsed = switchBox THEN MakeSBTracks[routingArea, okToDiddleLLPins, okToDiddleURPins] ELSE MakeCRTracks[routingArea, parms.numTracksToUse]; DoTBBarriers[routingArea, chanBottom]; DoTBBarriers[routingArea, chanTop]; DoLRBarriers[routingArea, chanLeft]; DoLRBarriers[routingArea, chanRight]}; ConvertSide: PROCEDURE[routingArea: Route.RoutingArea, chanSide: RouteChannel.ChanSide, sideOrgins: Route.PositionVec, routingRect: RoutePrivate.PQRect] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; extSide: Route.Side _ RouteChannel.IntSideToExtSide[routingArea, chanSide]; extSidesData: RoutePrivate.RoutingAreaSides _ NARROW[routingArea.routingSides]; extSideData: RoutePrivate.RoutingAreaSide _ extSidesData[extSide]; activeSide.enclosingBarrier _ [[0, 0], [0, 0]]; activeSide.extSide _ extSide; activeSide.sideOrg _ RouteUtil.XYToPQ[routingArea, sideOrgins[extSide]]; chanPins.cEnd1 _ routingRect.c1.p; chanPins.cEnd2 _ routingRect.c2.p; IF extSideData # NIL THEN { encloseBranch: RoutePrivate.PQRect _ TranslateBarriers[routingArea, activeSide, branch]; encloseTrunk: RoutePrivate.PQRect _ TranslateBarriers[routingArea, activeSide, trunk]; activeSide.enclosingBarrier _ Surround[encloseBranch, encloseTrunk]; activeSide.sideFiducial _ RouteUtil.XYToPQ[routingArea, extSideData.sideFiducial]; activeSide.properties _ extSideData.properties}}; TranslateBarriers: PROCEDURE[routingArea: Route.RoutingArea, activeSide: RouteChannel.RoutingChannelSides, rLayer: RoutePrivate.RoutingLayer] RETURNS [enclosingRect: RoutePrivate.PQRect _ [[0, 0], [0, 0]]] = { extSidesData: RoutePrivate.RoutingAreaSides _ NARROW[routingArea.routingSides]; extSide: Route.Side _ activeSide.extSide; extSideData: RoutePrivate.RoutingAreaSide _ extSidesData[extSide]; chanDirection: Route.Direction _ routingArea.rules.trunkDirection; IF extSideData # NIL THEN { FOR bList: Route.RoutingBarrierList _ extSideData.barrierList, bList.rest WHILE bList # NIL DO barrierItem: Route.RoutingBarrier _ bList.first; generalLayer: Route.Layer _ barrierItem.layer; IF generalLayer = CD.undefLayer OR RouteUtil.LayerToRoutingLayer[routingArea, generalLayer] = rLayer THEN { FOR rList: Route.RectList _ barrierItem.barrier, rList.rest WHILE rList # NIL DO cleanRectangle: RoutePrivate.PQRectRef _ CheckRect[routingArea, rList.first, activeSide]; IF cleanRectangle # NIL THEN { enclosingRect _ Surround[enclosingRect, cleanRectangle^]; activeSide.barrierList[rLayer] _ CONS[cleanRectangle^, activeSide.barrierList[rLayer]]}; ENDLOOP} ENDLOOP}}; CheckRect: PROC [routingArea: Route.RoutingArea, rect: Route.Rect, activeSide: RouteChannel.RoutingChannelSides] RETURNS[cleanRect: RoutePrivate.PQRectRef] = { chanDirection: Route.Direction _ routingArea.rules.trunkDirection; c1: RoutePrivate.PQPosition _ RouteUtil.XYToPQ[routingArea, [rect.x1, rect.y1]]; c2: RoutePrivate.PQPosition _ RouteUtil.XYToPQ[routingArea, [rect.x2, rect.y2]]; t1: RoutePrivate.PQPosition _ [c1.p - activeSide.sideFiducial.p + activeSide.sideOrg.p, c1.q - activeSide.sideFiducial.q + activeSide.sideOrg.q]; t2: RoutePrivate.PQPosition _ [c2.p - activeSide.sideFiducial.p + activeSide.sideOrg.p, c2.q - activeSide.sideFiducial.q + activeSide.sideOrg.q]; RETURN[NEW[RoutePrivate.PQRect _ [t1, t2]]]}; ConvertEnd: PROCEDURE[routingArea: Route.RoutingArea, chanSide: RouteChannel.ChanSide, sideOrgins: Route.PositionVec, routingRect: RoutePrivate.PQRect] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; extSide: Route.Side _ RouteChannel.IntSideToExtSide[routingArea, chanSide]; extSidesData: RoutePrivate.RoutingAreaSides _ NARROW[routingArea.routingSides]; extSideData: RoutePrivate.RoutingAreaSide _ extSidesData[extSide]; activeSide.extSide _ extSide; activeSide.enclosingBarrier _ [[0, 0], [0, 0]]; activeSide.sideOrg _ RouteUtil.XYToPQ[routingArea, sideOrgins[extSide]]; IF extSideData # NIL THEN { encloseBranch: RoutePrivate.PQRect _ TranslateBarriers[routingArea, activeSide, branch]; encloseTrunk: RoutePrivate.PQRect _ TranslateBarriers[routingArea, activeSide, trunk]; activeSide.sideFiducial _ RouteUtil.XYToPQ[routingArea, extSideData.sideFiducial]; activeSide.enclosingBarrier _ Surround[encloseBranch, encloseTrunk]; activeSide.properties _ extSideData.properties}}; FixOrgins: PROCEDURE[routingArea: Route.RoutingArea, routingRect: RoutePrivate.PQRect] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; leftSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanLeft]; rightSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanRight]; bottomSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanBottom]; topSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanTop]; zero: Route.Number _ 0; -- don't laugh leftSide.routeAreaCoord _ chanPins.cEnd1; rightSide.routeAreaCoord _ chanPins.cEnd2; chanPins.cEnd1 _ chanPins.cEnd1 + MAX[zero, leftSide.enclosingBarrier.c2.p]; chanPins.cEnd2 _ chanPins.cEnd2 + MAX[zero, rightSide.enclosingBarrier.c1.p]; bottomSide.routeAreaCoord _ routingRect.c1.q; topSide.routeAreaCoord _ routingRect.c2.q}; InitNet: PROCEDURE[routingArea: Route.RoutingArea, net: RoutePrivate.Net, signalSinglePinNets, signalCoincidentPins: BOOLEAN] = { PPinCompare: List.CompareProc = { p1: Route.Number _ NARROW[ref1, RouteChannel.InternPin].location.p; p2: Route.Number _ NARROW[ref2, RouteChannel.InternPin].location.p; RETURN[IF p1 < p2 THEN Basics.Comparison.less ELSE IF p1 = p2 THEN Basics.Comparison.equal ELSE Basics.Comparison.greater]}; mungedPinList, sortedPinList: List.LORA; parms: RoutePrivate.RoutingAreaParms _ NARROW[routingArea.parms]; chanDirection: Route.Direction _ routingArea.rules.trunkDirection; net.internPinList _ FixPins[routingArea, net.pinList, chanDirection]; mungedPinList _ ConvertToLORA[NARROW[net.internPinList]]; sortedPinList _ List.Sort[mungedPinList, PPinCompare]; IF CheckChanPins[routingArea, net, sortedPinList, signalSinglePinNets, signalCoincidentPins] THEN BuildChanPin[routingArea, net, sortedPinList]}; FixPins: PROC [routingArea: Route.RoutingArea, pList: Route.PinList, chanDirection: Route.Direction] RETURNS [fixedPins: -- List.LORA _ NIL -- RouteChannel.InternPinList _ NIL ] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; parms: RoutePrivate.RoutingAreaParms _ NARROW[routingArea.parms]; FOR list: Route.PinList _ pList, list.rest WHILE list # NIL DO old: Route.Pin _ list.first; loc: RoutePrivate.PQPosition; oldWidth, width: Route.Number; new: RouteChannel.InternPin; oldPinSize: CD.Position _ CDBasics.OrientedSize[CDBasics.SizeOfRect[old.pin.ob.bbox], old.pin.trans.orient]; oldPinPos: CD.Position _ CDBasics.BaseOfRect[CDBasics.MapRect[old.pin.ob.bbox, old.pin.trans]]; chanSide: RouteChannel.ChanSide _ RouteChannel.ExtSideToIntSide[routingArea, old.side]; activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; layer: Route.Layer _ CDSymbolicObjects.GetLayer[old.pin]; rLayer: RoutePrivate.RoutingLayer _ RouteUtil.LayerToRoutingLayer[routingArea, layer]; name: Rope.ROPE _ CDSymbolicObjects.GetName[old.pin]; sideFiducial: RoutePrivate.PQPosition _ activeSide.sideFiducial; sideOrg: RoutePrivate.PQPosition _ activeSide.sideOrg; oldLoc: Route.Position; SELECT old.side FROM bottom => { oldWidth _ oldPinSize.x; oldLoc _ [oldPinPos.x + oldPinSize.x/2, oldPinPos.y + oldPinSize.y]}; top => { oldWidth _ oldPinSize.x; oldLoc _ [oldPinPos.x + oldPinSize.x/2, oldPinPos.y]}; left => { oldWidth _ oldPinSize.y; oldLoc _ [oldPinPos.x + oldPinSize.x, oldPinPos.y + oldPinSize.y/2]}; right => { oldWidth _ oldPinSize.y; oldLoc _ [oldPinPos.x, oldPinPos.y + oldPinSize.y/2]}; ENDCASE; SELECT chanSide FROM chanBottom, chanTop => { loc _ RouteChannel.SubPoints[RouteUtil.XYToPQ[routingArea, oldLoc], sideFiducial]; loc _ RouteChannel.AddPoints[loc, sideOrg]; loc.q _ loc.q - activeSide.routeAreaCoord; width _ oldWidth}; chanLeft => { IF parms.routerUsed = channel THEN {loc _ [chanPins.cEnd1, 0]; width _ 0} ELSE IF parms.routerUsed = switchBox THEN { loc _ RouteChannel.SubPoints[RouteUtil.XYToPQ[routingArea, oldLoc], sideFiducial]; loc _ RouteChannel.AddPoints[loc, sideOrg]; loc.p _ chanPins.cEnd1; width _ oldWidth}}; chanRight => { IF parms.routerUsed = channel THEN {loc _ [chanPins.cEnd2, 0]; width _ 0} ELSE IF parms.routerUsed = switchBox THEN {loc _ RouteChannel.SubPoints[RouteUtil.XYToPQ[routingArea, oldLoc], sideFiducial]; loc _ RouteChannel.AddPoints[loc, sideOrg]; loc.p _ chanPins.cEnd2; width _ oldWidth}}; ENDCASE; new _ NEW[RouteChannel.InternPinRec _ [name, chanSide, loc, width, rLayer, old]]; fixedPins _ CONS[new, fixedPins]; ENDLOOP}; CheckChanPins: PROC [routingArea: Route.RoutingArea, net: RoutePrivate.Net, pinList: List.LORA -- RouteChannel.InternPinList --, signalSinglePinNets, signalCoincidentPins: BOOLEAN] RETURNS [ok: BOOLEAN _ TRUE] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; netTab: RoutePrivate.NetTab _ NARROW[routingArea.nets, RoutePrivate.NetTab]; numPins, numOverlaps: NAT _ 0; leftExit, rightExit: BOOLEAN _ FALSE; branchLayer: CD.Layer _ routingArea.rules.branchLayer; trunkLayer: CD.Layer _ routingArea.rules.trunkLayer; previousPin: RouteChannel.InternPin _ NIL; FOR pList: List.LORA -- RouteChannel.InternPinList -- _ pinList, pList.rest WHILE pList # NIL DO this: RouteChannel.InternPin _ NARROW[pList.first]; pinLayer: CD.Layer _ CDSymbolicObjects.GetLayer[this.pin.pin]; numPins _ numPins + 1; IF previousPin # NIL THEN { -- check for overlapping pins. IF previousPin.chanSide = this.chanSide THEN { rLast, rThis: RoutePrivate.Range; IF previousPin.chanSide = chanBottom OR previousPin.chanSide = chanTop THEN { rLast _ [previousPin.location.p - previousPin.pWidth/2, previousPin.location.p + previousPin.pWidth/2]; rThis _ [this.location.p - this.pWidth/2, this.location.p + this.pWidth/2]} ELSE { -- chanSide = chanLeft OR chanSide = chanRight rLast _ [previousPin.location.q - previousPin.pWidth/2, previousPin.location.q + previousPin.pWidth/2]; rThis _ [this.location.q - this.pWidth/2, this.location.q + this.pWidth/2]}; IF RouteChannel.Overlaps[rLast, rThis] THEN { numOverlaps _ numOverlaps + 1; IF signalCoincidentPins OR ~(previousPin.location = this.location AND previousPin.pWidth = this.pWidth) THEN Route.Signal[callingError, Rope.Cat[Rope.Cat["Pins overlap, net: ", net.name, ", pins: "], Rope.Cat[this.name, ", ", previousPin.name]]]; ok _ FALSE}} }; SELECT this.chanSide FROM chanBottom, chanTop => { pos: Route.Number _ this.location.p; IF chanPins.cEnd1 > pos OR chanPins.cEnd2 < pos THEN { Route.Signal[callingError, Rope.Cat["Pin location out of range, net: ", net.name, ", pin: ", this.name]]; ok _ FALSE}; IF pinLayer # branchLayer THEN { Route.Signal[callingError, Rope.Cat["Pin not on branch layer, net: ", net.name, ", pin: ", this.name]]; ok _ FALSE}}; chanLeft => { IF pinLayer # trunkLayer THEN { Route.Signal[callingError, Rope.Cat["Pin not on trunk layer, net: ", net.name, ", pin: ", this.name]]; ok _ FALSE}; IF leftExit THEN { Route.Signal[callingError, Rope.Cat["Multiple channel exits: ", net.name, ", pin: ", this.name]]; ok _ FALSE}; leftExit _ TRUE}; chanRight => { IF pinLayer # trunkLayer THEN { Route.Signal[callingError, Rope.Cat["Pin not on trunk layer, net: ", net.name, ", pin: ", this.name]]; ok _ FALSE}; IF rightExit THEN { Route.Signal[callingError, Rope.Cat["Multiple channel exits: ", net.name, ", pin: ", this.name]]; ok _ FALSE}; rightExit _ TRUE}; ENDCASE; previousPin _ this; ENDLOOP; IF numPins - numOverlaps <= 1 THEN { IF signalSinglePinNets THEN Route.Signal[callingError, Rope.Cat["Too few pins for net: ", net.name]]; ok _ FALSE}; IF netTab.count + numPins > netTab.size THEN { Route.Signal[noResource, "Net table is too small"]; ok _ FALSE}}; BuildChanPin: PROC [routingArea: Route.RoutingArea, net: RoutePrivate.Net, pinList: List.LORA -- RouteChannel.InternPinList -- ] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; interiorPins: RouteChannel.ChanPinList _ NIL; rightInternPin: RouteChannel.InternPin _ NARROW[List.NthElement[pinList, -1]]; previousPin: RouteChannel.InternPin _ NIL; leftPin, rightPin: RouteChannel.ChanPin _ NIL; allPinsOverlap: BOOLEAN _ TRUE; seg: RouteChannel.Segment _ NEW[RouteChannel.SegmentRec _ [net: net]]; FOR pList: List.LORA -- RouteChannel.InternPinList -- _ pinList, pList.rest WHILE pList # NIL DO pinIndex: RouteChannel.ZMPinsOnCh; pinPosition: RouteChannel.PinPosition; chanPin: RouteChannel.ChanPin; this: RouteChannel.InternPin _ NARROW[pList.first]; useThisOne: BOOLEAN _ TRUE; IF previousPin # NIL THEN { -- This is a crock! If Bertrand checked for overlapping pins wouldn't be necessary!! rLast, rThis: RoutePrivate.Range; IF previousPin.chanSide = chanBottom OR previousPin.chanSide = chanTop THEN { rLast _ [previousPin.location.p - previousPin.pWidth/2, previousPin.location.p + previousPin.pWidth/2]; rThis _ [this.location.p - this.pWidth/2, this.location.p + this.pWidth/2]} ELSE { -- chanSide = chanLeft OR chanSide = chanRight rLast _ [previousPin.location.q - previousPin.pWidth/2, previousPin.location.q + previousPin.pWidth/2]; rThis _ [this.location.q - this.pWidth/2, this.location.q + this.pWidth/2]}; IF previousPin.chanSide = this.chanSide AND RouteChannel.Overlaps[rLast, rThis] THEN useThisOne _ FALSE; IF this.chanSide = chanLeft OR this.chanSide = chanRight THEN allPinsOverlap _ FALSE; IF this.chanSide # chanLeft AND this.chanSide # chanRight THEN allPinsOverlap _ allPinsOverlap AND RouteChannel.Equal[rLast, rThis] }; IF useThisOne THEN { pinIndex _ FindPinPos[routingArea, this.location.p]; pinPosition _ chanPins.sides[pinIndex]; chanPin _ BuildPin[routingArea, this, pinPosition, routingArea.rules.branchWidth]; SELECT this.chanSide FROM chanBottom, chanTop => IF ~PinViolation[routingArea, chanPin, net] THEN pinPosition.pins[chanPin.pinSide] _ chanPin; chanLeft, chanRight => pinPosition.innerPins _ CONS[chanPin, pinPosition.innerPins]; ENDCASE; IF leftPin = NIL THEN { leftPin _ chanPin; chanPin.conctSeg[chanLeft] _ NIL; chanPin.conctSeg[chanRight] _ seg} ELSE IF this = rightInternPin THEN { rightPin _ chanPin; chanPin.conctSeg[chanRight] _ NIL; chanPin.conctSeg[chanLeft] _ seg} ELSE { interiorPins _ CONS[chanPin, interiorPins]; chanPin.conctSeg[chanRight] _ seg; chanPin.conctSeg[chanLeft] _ seg}}; previousPin _ this; ENDLOOP; seg.exteriorPins[chanLeft] _ leftPin; seg.exteriorPins[chanRight] _ rightPin; seg.interiorPins _ interiorPins; seg.qWidth _ IF allPinsOverlap THEN routingArea.rules.trunkWidth ELSE net.trunkWidth}; FindPinPos: PUBLIC PROC [routingArea: Route.RoutingArea, thisPLoc: Route.Number] RETURNS [index: RouteChannel.ZMPinsOnCh _ 0] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; found: BOOLEAN _ FALSE; FOR pIndex: RouteChannel.ZMPinsOnCh IN [1 .. chanPins.count] WHILE ~found DO pLoc: Route.Number _ chanPins.sides[pIndex].pLoc; IF pLoc > thisPLoc THEN { -- add a new pinPosition found _ TRUE; MovePins[chanPins, pIndex]; index _ pIndex; chanPins.sides[pIndex] _ NEW[RouteChannel.PinPositionRec _ [index, thisPLoc]]} ELSE IF pLoc = thisPLoc THEN { -- pin position already taken, use it found _ TRUE; index _ pIndex}; ENDLOOP; IF ~found THEN { -- this pin goes after the last pin chanPins.count _ chanPins.count +1; index _ chanPins.count; chanPins.sides[index] _ NEW[RouteChannel.PinPositionRec _ [index, thisPLoc]]}; }; MovePins: PROC [chanPins: RouteChannel.RoutingChannelPins, pIndex: RouteChannel.ZMPinsOnCh] = { chanPins.count _ chanPins.count +1; FOR index: RouteChannel.ZMPinsOnCh DECREASING IN [pIndex+1 .. chanPins.count] DO pinPos: RouteChannel.PinPosition _ chanPins.sides[index-1]; pinPos.pinIndex _ index; chanPins.sides[index] _ pinPos; ENDLOOP}; BuildPin: PROC [routingArea: Route.RoutingArea, iPin: RouteChannel.InternPin, pinPosition: RouteChannel.PinPosition, pinWidth: Route.Number] RETURNS [pin: RouteChannel.ChanPin] = { parms: RoutePrivate.RoutingAreaParms _ NARROW[routingArea.parms]; kindOfPin: RouteChannel.ChanPinType _ IF iPin.chanSide = chanLeft OR iPin.chanSide = chanRight THEN exitPin ELSE compPin; thisPinWidth: Route.Number _ IF kindOfPin = exitPin THEN iPin.pWidth ELSE MAX[pinWidth, iPin.pWidth]; parms.widestPin _ MAX[thisPinWidth, parms.widestPin]; pin _ NEW[RouteChannel.ChanPinRec _ [pinSide: iPin.chanSide, qLoc: iPin.location.q, pWidth: thisPinWidth, kindOfPin: kindOfPin, pinPosition: pinPosition, pin: iPin]]}; PinViolation: PROC [routingArea: Route.RoutingArea, pin: RouteChannel.ChanPin, net: RoutePrivate.Net] RETURNS [violation: BOOLEAN _ FALSE] = { LimitProc: RouteChannel.EachPinActionProc = { IF pin # NIL THEN IF pin.pinSide = pinSide THEN {width: Route.Number _ RouteUtil.GetWidthWithContact[routingArea, pin.pWidth]/2; pinPos: Route.Number _ pin.pinPosition.pLoc; IF pinPos < pLoc THEN quit _ pinPos + width + routingArea.rules.branchSpacing > pLoc - pWidth ELSE -- pinPos >= pLoc quit _ pLoc + pWidth + routingArea.rules.branchSpacing > pinPos - width}; IF quit THEN violationPin _ pin.pin}; chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; parms: RoutePrivate.RoutingAreaParms _ NARROW[routingArea.parms]; pinSide: RouteChannel.ChanSide _ pin.pinSide; pinIndex: RouteChannel.ZMPinsOnCh _ pin.pinPosition.pinIndex; pLoc: Route.Number _ pin.pinPosition.pLoc; pWidth: Route.Number _ RouteUtil.GetWidthWithContact[routingArea, pin.pWidth]/2; testWidth: Route.Number _ RouteUtil.GetWidthWithContact[routingArea, parms.widestPin] + routingArea.rules.branchSpacing; violationPin: RouteChannel.InternPin _ NIL; iPos: Route.Number _ chanPins.sides[pinIndex].pLoc; FOR limitIndex: RouteChannel.MPinsOnCh DECREASING IN [1 .. pinIndex] WHILE chanPins.sides[limitIndex].pLoc + testWidth > iPos AND ~violation DO violation _ RouteChannel.EnumPins[routingArea, chanPins.sides[limitIndex], LimitProc]; IF violation THEN Route.Signal[callingError, Rope.Cat[Rope.Cat["Pins are too close. net 1: ", net.name, ", pin 1: ", pin.pin.name], Rope.Cat[ ", pin 2: ", violationPin.name]]]; ENDLOOP; iPos _ chanPins.sides[pinIndex].pLoc; FOR limitIndex: RouteChannel.MPinsOnCh IN [pinIndex .. chanPins.count] WHILE chanPins.sides[limitIndex].pLoc - testWidth < iPos AND ~violation DO violation _ RouteChannel.EnumPins[routingArea, chanPins.sides[limitIndex], LimitProc]; IF violation THEN Route.Signal[callingError, Rope.Cat[Rope.Cat["Pins are too close. net 1: ", net.name, ", pin 1: ", pin.pin.name], Rope.Cat[ ", pin 2: ", violationPin.name]]]; ENDLOOP}; GetExitPins: PROC [routingArea: Route.RoutingArea] RETURNS [exitPins: List.LORA _ NIL] = { ReallyGetExits: PROC [pinPos: RouteChannel.PinPosition] = { IF pinPos # NIL THEN { FOR pList: RouteChannel.ChanPinList _ pinPos.innerPins, pList.rest WHILE pList # NIL DO this: RouteChannel.ChanPin _ pList.first; SELECT this.pin.chanSide FROM chanLeft, chanRight => exitPins _ CONS[this, exitPins]; ENDCASE; ENDLOOP}}; chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; ReallyGetExits[chanPins.sides[1]]; IF chanPins.count > 0 THEN ReallyGetExits[chanPins.sides[chanPins.count]]}; MakeSBTracks: PROC [routingArea: Route.RoutingArea, okToDiddleLLPins, okToDiddleURPins: BOOLEAN] = { ExitPinCompare: List.CompareProc = { q1: Route.Number _ NARROW[ref1, RouteChannel.ChanPin].pin.location.q; q2: Route.Number _ NARROW[ref2, RouteChannel.ChanPin].pin.location.q; RETURN[IF q1 < q2 THEN Basics.Comparison.less ELSE IF q1 = q2 THEN Basics.Comparison.equal ELSE Basics.Comparison.greater]}; DoUp: PROC [thisPos: Route.Number]~ { FOR trackPos: Route.Number _ lastPos + trackDist, trackPos + trackDist WHILE trackPos <= thisPos - trackDist DO [] _ AddTrack[routingArea, trackPos]; lastPos _ trackPos; ENDLOOP}; ClosestTrack: PROC [thisPos: Route.Number] RETURNS [track: RouteChannel.Track _ NIL] ~ { dist: INT _ LAST[INT]; FOR trackNum: INT IN [1 .. chanTracks.count] DO trialTrack: RouteChannel.Track _ chanTracks.tracks[trackNum]; trialDist: INT _ ABS[trialTrack.trackPos - thisPos]; IF trialDist < dist THEN { track _ trialTrack; dist _ trialDist} ENDLOOP; }; chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanTracks: RouteChannel.RoutingChannelTracks _ chanData.chanTracks; exitPinList: List.LORA _ GetExitPins[routingArea]; sortedExitPinList: List.LORA _ List.Sort[exitPinList, ExitPinCompare]; lastPos: Route.Number _ chanData.chanSides[chanBottom].routeAreaCoord; previousPin: RouteChannel.ChanPin _ NIL; trackDist: Route.Number _ routingArea.rules.trunkToTrunk; lastTrack: RouteChannel.Track _ NIL; FOR pList: List.LORA -- RouteChannel.InternPinList -- _ sortedExitPinList, pList.rest WHILE pList # NIL DO this: RouteChannel.ChanPin _ NARROW[pList.first]; thisPos: Route.Number _ this.pin.location.q; pin2Name: Rope.ROPE _ IF previousPin = NIL THEN "none" ELSE previousPin.pin.name; track: RouteChannel.Track _ NIL; SELECT TRUE FROM ~okToDiddleLLPins AND ~okToDiddleURPins => { -- can't move any pins IF thisPos = lastPos THEN -- already have a track here track _ lastTrack ELSE IF thisPos >= lastPos + trackDist THEN { -- lots of room here DoUp[thisPos]; track _ AddTrack[routingArea, thisPos]} ELSE { Route.Signal[callingError, Rope.Cat["Pins too close on ends of switchbox, pin 1: ", this.pin.name, ", pin 2: ", pin2Name]]; track _ AddTrack[routingArea, thisPos]}; BindPinToTrack[this, track]; previousPin _ this; lastTrack _ track; lastPos _ thisPos}; okToDiddleLLPins AND okToDiddleURPins => { -- can move pins on both sides IF thisPos = lastPos THEN -- already have a track here track _ lastTrack ELSE IF thisPos >= lastPos + trackDist THEN { -- lots of room here DoUp[thisPos]; track _ AddTrack[routingArea, thisPos]} ELSE { thisPos _ lastPos + trackDist; track _ AddTrack[routingArea, thisPos]}; BindPinToTrack[this, track]; previousPin _ this; lastTrack _ track; lastPos _ thisPos}; okToDiddleLLPins => { -- can move only pins on the left IF this.pin.chanSide = chanRight THEN { -- this pin is on the right IF thisPos >= lastPos + trackDist THEN -- lots of room here DoUp[thisPos] ELSE -- not enough room, error Route.Signal[callingError, Rope.Cat["Pins too close on ends of switchbox, pin 1: ", this.pin.name, ", pin 2: ", pin2Name]]; track _ AddTrack[routingArea, thisPos]; BindPinToTrack[this, track]; previousPin _ this; lastTrack _ track; lastPos _ thisPos}}; okToDiddleURPins => { -- can move only pins on the right IF this.pin.chanSide = chanLeft THEN { -- this pin is on the left IF thisPos >= lastPos + trackDist THEN -- lots of room here DoUp[thisPos] ELSE -- not enough room, error Route.Signal[callingError, Rope.Cat["Pins too close on ends of switchbox, pin 1: ", this.pin.name, ", pin 2: ", pin2Name]]; track _ AddTrack[routingArea, thisPos]; BindPinToTrack[this, track]; previousPin _ this; lastTrack _ track; lastPos _ thisPos}}; ENDCASE; ENDLOOP; IF previousPin # NIL AND lastPos > chanData.chanSides[chanTop].routeAreaCoord - trackDist THEN { Route.Signal[callingError, Rope.Cat["Pins too close on ends of switchbox, pin : ", previousPin.pin.name]]}; FOR trackPos: Route.Number _ lastPos + trackDist, trackPos + trackDist WHILE trackPos < chanData.chanSides[chanTop].routeAreaCoord - trackDist DO [] _ AddTrack[routingArea, trackPos]; lastPos _ trackPos; ENDLOOP; chanTracks.maxCount _ chanTracks.count; IF okToDiddleLLPins # okToDiddleURPins THEN { FOR pList: List.LORA -- RouteChannel.InternPinList -- _ sortedExitPinList, pList.rest WHILE pList # NIL DO this: RouteChannel.ChanPin _ NARROW[pList.first]; thisPos: Route.Number _ this.pin.location.q; pin2Name: Rope.ROPE _ IF previousPin = NIL THEN "none" ELSE previousPin.pin.name; track: RouteChannel.Track; SELECT TRUE FROM okToDiddleLLPins => { -- can move only pins on the left IF this.pin.chanSide = chanLeft THEN { -- this pin is on the left track _ ClosestTrack[thisPos]; BindPinToTrack[this, track]}; }; okToDiddleURPins => { -- can move only pins on the right IF this.pin.chanSide = chanRight THEN { -- this pin is on the right track _ ClosestTrack[thisPos]; BindPinToTrack[this, track]}; }; ENDCASE; ENDLOOP}; }; MakeCRTracks: PROC [routingArea: Route.RoutingArea, maxTracksRequired: NAT] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanTracks: RouteChannel.RoutingChannelTracks _ chanData.chanTracks; trackLimit: RouteChannel.ZMaxTracks _ MIN[chanTracks.size, maxTracksRequired]; FOR trackIndex: RouteChannel.ZMaxTracks IN [1 .. trackLimit] DO loc: Route.Number _ routingArea.rules.trunkToEdge + (trackIndex - 1) * routingArea.rules.trunkToTrunk; [] _ AddTrack[routingArea, loc]; ENDLOOP; chanTracks.maxCount _ chanTracks.count}; AddTrack: PROC [routingArea: Route.RoutingArea, trackPos: Route.Number] RETURNS [track: RouteChannel.Track] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; chanTracks: RouteChannel.RoutingChannelTracks _ chanData.chanTracks; IF chanTracks.count >= RouteChannel.maxTrack THEN Route.Signal[noResource, "Too many tracks needed, call implementor"]; chanTracks.count _ chanTracks.count + 1; track _ chanTracks.tracks[chanTracks.count] _ NEW[RouteChannel.TrackRec _ [trackNum: chanTracks.count, trackPos: trackPos]]}; BindPinToTrack: PROC [pin: RouteChannel.ChanPin, track: RouteChannel.Track] ~ { IF pin # NIL THEN { sList: RouteChannel.SegmentList _ RouteChannel.GetSegsOnPin[pin]; pin.trackConstraint _ track.trackNum; FOR segs: RouteChannel.SegmentList _ sList, segs.rest WHILE segs # NIL DO seg: RouteChannel.Segment _ segs.first; IF seg # NIL THEN { IF seg.trackConstraint = 0 THEN seg.trackConstraint _ track.trackNum ELSE seg.secTrackConstraint _ track.trackNum}; ENDLOOP; }}; DoTBBarriers: PROC [routingArea: Route.RoutingArea, chanSide: RouteChannel.ChanSide] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; trackDist: Route.Number _ routingArea.rules.trunkToTrunk; FOR rlayer: RoutePrivate.RoutingLayer IN [trunk .. branch] DO FOR bList: RoutePrivate.PQRectList _ activeSide.barrierList[rlayer], bList.rest WHILE bList # NIL DO BlockProc: RouteChannel.EachTrackActionProc = { trackPos: Route.Number _ RouteChannel.TrackLoc[routingArea, trackIndex]; IF minQ < trackPos - trackDist AND trackPos + trackDist < maxQ THEN track.blocked _ TRUE}; barrierItem: RoutePrivate.PQRect _ bList.first; minQ: Route.Number _ MIN[barrierItem.c1.q, barrierItem.c2.q]; maxQ: Route.Number _ MAX[barrierItem.c1.q, barrierItem.c2.q]; [] _ RouteChannel.EnumTracks[routingArea, BlockProc]; ENDLOOP; ENDLOOP}; DoLRBarriers: PROC [routingArea: Route.RoutingArea, chanSide: RouteChannel.ChanSide] = { chanData: RouteChannel.ChannelData _ NARROW[routingArea.privateData]; activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; FOR rlayer: RoutePrivate.RoutingLayer IN [trunk .. branch] DO FOR bList: RoutePrivate.PQRectList _ activeSide.barrierList[rlayer], bList.rest WHILE bList # NIL DO BlockProc: RouteChannel.EachTrackActionProc = { NULL }; barrierItem: RoutePrivate.PQRect _ bList.first; minQ: Route.Number _ MIN[barrierItem.c1.q, barrierItem.c2.q]; maxQ: Route.Number _ MAX[barrierItem.c1.q, barrierItem.c2.q]; [] _ RouteChannel.EnumTracks[routingArea, BlockProc]; ENDLOOP; ENDLOOP}; Surround: PROC [r1, r2: RoutePrivate.PQRect] RETURNS [RoutePrivate.PQRect] = INLINE { RETURN [RoutePrivate.PQRect[ [MIN[r1.c1.p, r2.c1.p], MIN[r1.c1.q, r2.c1.q]], [MAX[r1.c2.p, r2.c2.p], MAX[r1.c2.q, r2.c2.q]]]]}; ConvertToLORA: PROC [list: RouteChannel.InternPinList] RETURNS[val: List.LORA] = { val _ NIL; UNTIL list = NIL DO val _ CONS[list.first, val]; list _ list.rest; ENDLOOP; RETURN[val]; }; -- of ConvertToLORA END. RouteChannelInitImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. by Bryan Preas July 10, 1985 6:57:00 pm PDT last edited by Bryan Preas December 18, 1986 2:33:43 pm PST Initialize for channel routing build the channel routing area; privatePart.constraints initialized in RouteChannelConstraintsImpl convert sides to p-q space and build side data structures fix up the routing area orgins convert the nets from x-y space to p-q space Convert a channel side to p-q space and set up side data convert routing barriers for a side clean up the rectangle and return it. convert a channel end to p-q space and set up side data now that all side data is available, compute the routing area fix up the routing area Initialize for channel routing Translate the pin coordinates from x-y space to p-q space. Check the pins for this net. -- number of pins > 1 -- pins within range -- pin overlap -- pin spacing -- max number of pins build the channel pins data structure for this net. NOTE: this assumes that CheckChanPins has been invoked and that the inputs are valid!!! now update the segment build the PinPosition data structure for this pin at thisPLoc. NOTE: this assumes that CheckChanPins has been invoked and that the inputs are valid!!! build the channel segment data structure for this net. check for pin to pin violations. count down from index to find the pin that can still effect index count up from index to find the pin that can still effect index get the exit pins for this net. NOTE: this assumes that CheckChanPins has been invoked and that the inputs are valid!!! find the track positions the complexity of this code comes from the requirement to move pins on the end of a switchbox under certain conditions (okToDiddleLLPins or okToDiddleURPins). run the tracks to the top of the switchbox fix up the other side now find the track positions insert a track at the position update the track constraints for the segments attached to the pin Block the tracks covered by the layer. Block the tracks covered by the layer. Κ“˜codešœ™Kšœ Οmœ1™˜bšžœ1˜5K˜—Kšœ&˜&Kšœ#˜#Kšœ$˜$Kšœ&˜&K˜—šŸ œž œ†˜œK™8K™Kšœ%žœ˜EKšœL˜LKšœ>˜>KšœK˜KKšœ.žœ˜OKšœB˜BKšœ/˜/Kšœ˜šœH˜HK˜—šœE˜EK˜—šžœžœžœ˜KšœX˜XKšœV˜VKšœD˜DKšœR˜RKšœ1˜1—K˜—šŸœž œržœ<˜Ρ™#K™—Kšœ.žœ˜OKšœ)˜)KšœB˜BKšœB˜Bšžœžœžœ˜šžœGžœ žœž˜^Kšœ0˜0Kšœ.˜.šžœžœ žœCžœ˜kšžœ9žœ žœž˜PKšœZ˜Zšžœžœžœ˜Kšœ9˜9Kšœ!žœ3˜X—Kšžœ˜——Kšžœ˜ ——K˜—šŸ œžœbžœ'˜ŸK™%K™KšœB˜BKšœP˜PKšœP˜PKšœ‘˜‘Kšœ‘˜‘Kšžœžœ#˜-K˜—šŸ œž œ†˜›K™7K™Kšœ%žœ˜EKšœL˜LKšœK˜KKšœ.žœ˜OKšœB˜BK˜Kšœ˜Kšœ/˜/KšœH˜Hšžœžœžœ˜KšœX˜XKšœV˜VKšœR˜RKšœD˜DKšœ1˜1—K˜—šŸ œž œF˜ZK™=K™Kšœ%žœ˜EKšœ>˜>KšœJ˜JKšœL˜LKšœN˜NKšœH˜HKšœΟc˜'K˜Kšœ™Kšœ)˜)Kšœ*˜*Kšœ"žœ'˜LKšœ"žœ(˜MKšœ-˜-Kšœ+˜+K˜—šŸœž œcžœ˜šœ™K™—šŸ œ˜!Kšœžœ*˜CKšœžœ*˜Cšžœžœ žœ˜-Kšžœžœ žœ˜,Kšžœ˜!—K˜—Kšœ#žœ˜(Kšœ'žœ˜AKšœB˜BKšœE˜EKšœžœ˜9Kšœ6˜6šžœ[ž˜aKšœ/˜/—K˜—š ŸœžœXžœ  œžœ˜΅™:K˜—Kšœ%žœ˜EKšœ>˜>Kšœ'žœ˜Ašžœ(žœžœž˜>Kšœ˜Kšœ˜K˜Kšœ˜Kšœ žœ^˜lKšœ žœR˜_KšœW˜WKšœL˜LKšœ9˜9KšœV˜VKšœ žœ&˜5Kšœ@˜@Kšœ6˜6šœ˜K˜—šžœ ž˜šœ ˜ Kšœ˜KšœE˜E—šœ˜Kšœ˜Kšœ6˜6—šœ ˜ Kšœ˜KšœE˜E—šœ ˜ Kšœ˜Kšœ6˜6—Kšžœ˜K˜—šžœ ž˜šœ˜KšœR˜RKšœ+˜+Kšœ*˜*Kšœ˜—šœ ˜ Kšžœžœ'˜Išžœžœžœ˜+KšœR˜RKšœ+˜+Kšœ˜Kšœ˜——šœ˜Kšžœžœ'˜Išžœžœž˜)KšœS˜SKšœ+˜+Kšœ˜Kšœ˜——Kšžœ˜—KšœžœH˜QKšœ žœ˜!Kšžœ˜ K˜——šŸ œžœHžœ  œ-žœžœžœžœ˜Φ™Kšœ™K™Kšœ™Kšœ™Kšœ™K™—Kšœ%žœ˜EKšœ>˜>Kšœžœ(˜LKšœžœ˜Kšœžœžœ˜%Kšœ žœ'˜6Kšœ žœ&˜4šœ&žœ˜*K˜—š žœ žœ  œžœ žœž˜`Kšœžœ˜3Kšœ žœ2˜>K˜šžœžœžœ ˜<šžœ&žœ˜.Kšœ!˜!šžœEžœ˜MKšœg˜gKšœK˜K—šžœ .˜5Kšœg˜gKšœL˜L—šžœ%žœ˜-Kšœ˜šžœžœ)žœ#ž˜mKšœžœ˜–———Kšœ˜K˜—šžœž˜šœ˜Kšœ$˜$šžœžœžœ˜6Kšœožœ˜v—šžœžœ˜ Kšœmžœ˜u——šœ ˜ šžœžœ˜Kšœlžœ˜s—šžœ žœ˜Kšœgžœ˜n—Kšœ žœ˜—šœ˜šžœžœ˜Kšœlžœ˜s—šžœ žœ˜Kšœgžœ˜n—Kšœ žœ˜—Kšžœ˜—Kšœ˜Kšžœ˜K˜—šžœžœ˜$KšžœžœJ˜eKšœžœ˜ —šžœ&žœ˜.Kšœ9žœ˜A—K˜—šŸ œžœGžœ  œ˜„K™3™WK˜—Kšœ%žœ˜EKšœ>˜>Kšœ)žœ˜-Kšœ)žœ˜NKšœ&žœ˜*Kšœ*žœ˜.Kšœžœžœ˜šœžœ'˜FK˜—š žœ žœ  œžœ žœž˜`Kšœ"˜"Kšœ&˜&Kšœ˜Kšœžœ˜3Kšœ žœžœ˜šžœžœžœ T˜qKšœ!˜!šžœEžœ˜MKšœg˜gKšœK˜K—šžœ .˜5Kšœg˜gKšœL˜L—šžœ&žœ#žœ˜UKšœ ž˜—šžœžœž˜=Kšœ˜—šžœžœž˜>Kšœ žœ!˜D—Kšœ˜K˜—šžœ žœ˜Kšœ4˜4Kšœ'˜'šœR˜RK˜—šžœž˜šœžœ*ž˜GKšœ,˜,—šœ˜Kšœžœ!˜=—Kšžœ˜K˜—šžœ žœžœ˜Kšœ0žœ$˜W—šžœžœžœ˜$Kšœ2žœ#˜X—šžœ˜Kšœžœ_˜r——Kšœ˜Kšžœ˜K˜—Kšœ™Kšœ%˜%Kšœ'˜'Kšœ ˜ Kšœ žœžœžœ˜VK˜—šŸ œžœžœ:žœ)˜Kšœ>™>KšœW™WK˜Kšœ%žœ˜EKšœ>˜>Kšœžœžœ˜K˜šžœ!žœžœž˜LKšœ1˜1šžœž˜Kšœ ˜Kšœžœ˜ Kšœ˜Kšœ˜Kšœžœ2˜N—šžœžœž˜Kšœ %˜'Kšœžœ˜ Kšœ˜—Kšžœ˜—K˜šžœžœ #˜4Kšœ#˜#Kšœ˜Kšœžœ3˜N—Kšœ˜K˜—šŸœžœQ˜_K˜Kšœ#˜#šžœ ž œžœž˜PKšœ;˜;Kšœ˜Kšœ˜Kšžœ˜ —K˜—šŸœžœžœ ˜΄K™6K˜Kšœ'žœ˜Ašœ%˜%Kšžœžœžœ˜EKšžœ ˜ —šœ˜Kšžœžœ ˜'Kšžœžœ˜ —Kšœžœ ˜5Kšœžœž˜§K˜—š Ÿ œžœTžœ žœžœ˜Ž™ K˜—šŸ œ$˜-šžœžœž˜šžœž˜KšœP˜PK˜,šžœž˜KšœH˜H—šžœ ˜KšœI˜I—Kšžœžœ˜%——K˜—Kšœ%žœ˜EKšœ>˜>Kšœ'žœ˜AKšœ-˜-Kšœ=˜=Kšœ*˜*KšœP˜PKšœy˜yKšœ'žœ˜+K™KšœA™AKšœ3˜3š žœ$ž œžœžœ4žœ ž˜KšœV˜Všžœ ž˜Kšœž˜ž—Kšžœ˜—K˜Kšœ?™?Kšœ%˜%š žœ$žœžœ4žœ ž˜‘KšœV˜Všžœ ž˜Kšœž˜ž—Kšžœ˜ —K˜—š Ÿ œžœ"žœžœžœ˜ZK™K™WK˜šŸœžœ'˜;šžœ žœžœ˜šžœAžœ žœž˜XKšœ)˜)K˜šžœž˜Kšœ"žœ˜7Kšžœ˜—Kšžœ˜ K˜———Kšœ%žœ˜EKšœ>˜>Kšœ"˜"Kšžœžœ1˜KK˜—šŸ œžœFžœ˜dK™K˜šŸœ˜$Kšœžœ,˜EKšœžœ,˜Ešžœžœ žœ˜-Kšžœžœ žœ˜,Kšžœ˜!K˜——šŸœžœ˜%K˜šžœDžœ!ž˜oKšœ%˜%Kšœ˜Kšžœ˜ —K˜—šŸ œžœžœžœ˜XKšœžœžœžœ˜šžœ žœžœž˜/Kšœ=˜=Kšœ žœžœ ˜4šžœžœ˜Kšœ˜Kšœ˜—Kšžœ˜—K˜K˜—Kšœ%žœ˜EKšœD˜DKšœžœ˜2Kšœžœ*˜FKšœF˜FKšœ$žœ˜(Kšœ9˜9Kšœ žœ˜$K˜Kšœž™žK™š žœ žœ  œ"žœ žœž˜kKšœžœ˜1Kšœ,˜,Kš œžœžœžœžœžœ˜QKšœžœ˜ K˜šžœžœž˜šœžœ ˜Dšžœžœ ˜6K˜—šžœžœ žœ ˜BKšœ˜Kšœ'˜'—šž˜Kšœ{˜{Kšœ(˜(—Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜šœžœ ˜Išžœžœ ˜6K˜—šžœžœ žœ ˜BKšœ˜Kšœ'˜'—šž˜Kšœ˜Kšœ(˜(—Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜šœ !˜8šžœžœ ˜Cšžœ žœ ˜;Kšœ ˜ —šžœ ˜Kšœ{˜{—Kšœ'˜'Kšœ˜Kšœ˜Kšœ˜Kšœ˜——K˜šœ "˜9šžœžœ ˜Ašžœ žœ ˜;Kšœ ˜ —šžœ ˜Kšœ{˜{—Kšœ'˜'Kšœ˜Kšœ˜Kšœ˜Kšœ˜——Kšžœ˜K˜—Kšžœ˜K˜—Kšœ*™*šžœžœžœBžœ˜`Kšœk˜k—šžœDžœCž˜‘Kšœ%˜%Kšœ˜Kšžœ˜—Kšœ'˜'K˜šžœ%žœ˜-Kšœ™K˜š žœ žœ  œ"žœ žœž˜kKšœžœ˜1Kšœ,˜,Kš œžœžœžœžœžœ˜QKšœ˜K˜šžœžœž˜K˜šœ !˜8šžœžœ ˜AK˜Kšœ˜—Kšœ˜—K˜šœ "˜9šžœžœ ˜CK˜Kšœ˜Kšœ˜——Kšžœ˜—K˜Kšžœ˜ ——Kšœ˜K˜—šŸ œžœ5žœ˜OK™K˜Kšœ%žœ˜EKšœD˜DKšœ&žœ%˜Nšžœ%žœž˜?Kšœf˜fKšœ ˜ Kšžœ˜—Kšœ(˜(K˜—šŸœžœ:žœ ˜oK™K˜Kšœ%žœ˜EKšœD˜Dšžœ+ž˜1KšœE˜E—Kšœ(˜(Kšœ.žœL˜}K˜—šŸœžœ;˜OK˜šžœžœžœ˜KšœA™AKšœA˜AKšœ%˜%šžœ3žœžœž˜IKšœ'˜'šžœžœžœ˜Kšžœžœ%˜DKšžœ+˜/—Kšžœ˜—Kšœ˜—K˜—šŸ œžœF˜XK™&K™Kšœ%žœ˜EKšœL˜LKšœ>˜>Kšœ9˜9šžœ#žœž˜=šžœMžœ žœž˜dšŸ œ&˜/KšœH˜Hšžœžœž˜CKšœžœ˜—K˜—Kšœ/˜/Kšœžœ%˜=Kšœžœ%˜=Kšœ5˜5Kšžœ˜—Kšžœ˜ —K˜—šŸ œžœF˜XK™&K™Kšœ%žœ˜EKšœL˜Lšžœ#žœž˜=šžœMžœ žœž˜dšŸ œ'žœ˜7K˜—Kšœ/˜/Kšœžœ%˜=Kšœžœ%˜=Kšœ5˜5Kšžœ˜—Kšžœ˜ —K˜—šŸœžœžœ˜Lšžœ˜Kšžœ˜Kšœžœžœ˜/Kšœžœžœ˜2—K˜—šŸ œžœ$žœ žœ˜RKšœžœ˜ šžœžœž˜Kšœžœ˜K˜Kšžœ˜—Kšžœ˜ Kšœ ˜K˜—Kšžœ˜——…—xςš›