DIRECTORY Basics, CD, CDBasics, DABasics, List, Rope, Route, RouteChannel, RoutePrivate, RouteUtil, RTBasic; RouteChannelInitImpl: CEDAR PROGRAM IMPORTS CDBasics, List, Rope, Route, RouteChannel, RouteUtil, RTBasic EXPORTS RouteChannel = { 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 PROC [extSidesData: RoutePrivate.RoutingAreaSides, parms: RoutePrivate.RoutingAreaParms, rules: Route.DesignRules, netTab: RoutePrivate.NetTab, signalSinglePinNets, signalCoincidentPins, okToDiddleLLPins, okToDiddleURPins: BOOLEAN] RETURNS [chanData: RouteChannel.ChannelData] = { nRect: DABasics.Rect _ CDBasics.ReInterpreteRect[parms.routingRect]; lowPos: RTBasic.PQPos _ RouteUtil.XYToPQ[rules, [nRect.x1, nRect.y1]]; upPos: RTBasic.PQPos _ RouteUtil.XYToPQ[rules, [nRect.x2, nRect.y2]]; rRect: RTBasic.PQRect _ [lowPos, upPos]; chanData _ NEW[RouteChannel.ChannelDataRec]; chanData.constraints _ NIL; chanData.chanSides[chanTop] _ NEW[RouteChannel.RoutingChannelSidesRec]; chanData.chanSides[chanBottom] _ NEW[RouteChannel.RoutingChannelSidesRec]; chanData.chanSides[chanLeft] _ NEW[RouteChannel.RoutingChannelSidesRec]; chanData.chanSides[chanRight] _ NEW[RouteChannel.RoutingChannelSidesRec]; chanData.chanTracks _ NEW[RouteChannel.RoutingChannelTracksRec]; chanData.chanPins _ NEW[RouteChannel.RoutingChannelPinsRec]; chanData.chanParms _ NEW[RouteChannel.ChanParmsRec]; chanData.chanParms.emptyTrackLimit _ MAX[2, RouteChannel.InfluenceTracks[rules, parms.widestTrunk]]; chanData.chanParms.maxToConvert _ 10*rules.branchToBranch; ConvertSide[chanData, extSidesData, rules, chanBottom, rRect]; ConvertSide[chanData, extSidesData, rules, chanTop, rRect]; ConvertEnd[chanData, extSidesData, rules, chanLeft, rRect]; ConvertEnd[chanData, extSidesData, rules, chanRight, rRect]; FixOrigins[chanData, rRect]; FOR netIndex: RoutePrivate.ZMaxNets IN [1 .. netTab.count] DO InitNet[chanData, parms, rules, netTab, netTab.n[netIndex], signalSinglePinNets, signalCoincidentPins]; ENDLOOP; SELECT parms.routerUsed FROM switchBox => MakeSBTracks[chanData, rules, okToDiddleLLPins, okToDiddleURPins]; channel => MakeCRTracks[chanData.chanTracks, rules, parms.numTracksToUse]; ENDCASE => ERROR; --may be channel should be the default ? DoTBBarriers[chanData, parms, rules, chanBottom]; DoTBBarriers[chanData, parms, rules, chanTop]; DoLRBarriers[chanData, chanLeft]; DoLRBarriers[chanData, chanRight]; }; ConvertSide: PROC [chanData: RouteChannel.ChannelData, extSidesData: RoutePrivate.RoutingAreaSides, rules: Route.DesignRules, chanSide: RouteChannel.ChanSide, routingRect: RTBasic.PQRect] = { activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; extSide: DABasics.Side _ RouteChannel.IntSideToExtSide[rules, chanSide]; extSideData: RoutePrivate.RoutingAreaSide _ extSidesData[extSide]; activeSide.enclosingBarrier _ [[0, 0], [0, 0]]; activeSide.extSide _ extSide; chanPins.cEnd1 _ routingRect.c1.p; chanPins.cEnd2 _ routingRect.c2.p; IF extSideData # NIL THEN { encloseBranch: RTBasic.PQRect _ TranslateBarriers[extSidesData, rules, activeSide, branch]; encloseTrunk: RTBasic.PQRect _ TranslateBarriers[extSidesData, rules, activeSide, trunk]; activeSide.enclosingBarrier _ Surround[encloseBranch, encloseTrunk]; }; }; TranslateBarriers: PROC [extSidesData: RoutePrivate.RoutingAreaSides, rules: Route.DesignRules, activeSide: RouteChannel.RoutingChannelSides, rLayer: RoutePrivate.RoutingLayer] RETURNS [enclosingRect: RTBasic.PQRect _ [[0, 0], [0, 0]]] = { extSide: DABasics.Side _ activeSide.extSide; extSideData: RoutePrivate.RoutingAreaSide _ extSidesData[extSide]; IF extSideData # NIL THEN { FOR bList: RoutePrivate.RoutingBarrierList _ extSideData.barrierList, bList.rest WHILE bList # NIL DO barrierItem: RoutePrivate.RoutingBarrier _ bList.first; generalLayer: CD.Layer _ barrierItem.layer; IF generalLayer = CD.undefLayer OR RouteUtil.LayerToRoutingLayer[rules, generalLayer] = rLayer THEN { FOR rList: LIST OF DABasics.Rect _ barrierItem.barrier, rList.rest WHILE rList # NIL DO cleanRectangle: RTBasic.PQRectRef _ CheckRect[rules, rList.first, activeSide]; IF cleanRectangle # NIL THEN { enclosingRect _ Surround[enclosingRect, cleanRectangle^]; activeSide.barrierList[rLayer] _ CONS[cleanRectangle^, activeSide.barrierList[rLayer]]}; ENDLOOP; } ENDLOOP; }; }; CheckRect: PROC [rules: Route.DesignRules, rect: DABasics.Rect, activeSide: RouteChannel.RoutingChannelSides] RETURNS[cleanRect: RTBasic.PQRectRef] = { c1: RTBasic.PQPos _ RouteUtil.XYToPQ[rules, [rect.x1, rect.y1]]; c2: RTBasic.PQPos _ RouteUtil.XYToPQ[rules, [rect.x2, rect.y2]]; RETURN[NEW[RTBasic.PQRect _ [c1, c2]]]; }; ConvertEnd: PROC [chanData: RouteChannel.ChannelData, extSidesData: RoutePrivate.RoutingAreaSides, rules: Route.DesignRules, chanSide: RouteChannel.ChanSide, routingRect: RTBasic.PQRect] = { activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; extSide: DABasics.Side _ RouteChannel.IntSideToExtSide[rules, chanSide]; extSideData: RoutePrivate.RoutingAreaSide _ extSidesData[extSide]; activeSide.extSide _ extSide; activeSide.enclosingBarrier _ [[0, 0], [0, 0]]; IF extSideData # NIL THEN { encloseBranch: RTBasic.PQRect _ TranslateBarriers[extSidesData, rules, activeSide, branch]; encloseTrunk: RTBasic.PQRect _ TranslateBarriers[extSidesData, rules, activeSide, trunk]; activeSide.enclosingBarrier _ Surround[encloseBranch, encloseTrunk]; }; }; FixOrigins: PROC [chanData: RouteChannel.ChannelData, routingRect: RTBasic.PQRect] = { 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: DABasics.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: PROC [chanData: RouteChannel.ChannelData, parms: RoutePrivate.RoutingAreaParms, rules: Route.DesignRules, netTab: RoutePrivate.NetTab, net: RoutePrivate.Net, signalSinglePinNets, signalCoincidentPins: BOOLEAN] = { PPinCompare: List.CompareProc = { p1: DABasics.Number _ NARROW[ref1, RouteChannel.InternPin].location.p; p2: DABasics.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; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; net.internPinList _ FixPins[chanData, parms, rules, net.pinList]; mungedPinList _ ConvertToLORA[NARROW[net.internPinList]]; sortedPinList _ List.Sort[mungedPinList, PPinCompare]; IF CheckChanPins[chanPins, rules, netTab, net, sortedPinList, signalSinglePinNets, signalCoincidentPins] THEN BuildChanPinForDogleg[chanPins, parms, rules, net, sortedPinList]; }; FixPins: PROC [chanData: RouteChannel.ChannelData, parms: RoutePrivate.RoutingAreaParms, rules: Route.DesignRules, pList: Route.PinList] RETURNS [fixedPins: -- List.LORA _ NIL -- RouteChannel.InternPinList _ NIL ] = { chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; routingRect: DABasics.Rect _ parms.routingRect; FOR list: Route.PinList _ pList, list.rest WHILE list # NIL DO old: Route.Pin _ list.first; loc: RTBasic.PQPos; new: RouteChannel.InternPin; chanSide: RouteChannel.ChanSide _ RouteChannel.ExtSideToIntSide[rules, old.side]; activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; rLayer: RoutePrivate.RoutingLayer _ RouteUtil.LayerToRoutingLayer[rules, old.layer]; width: DABasics.Number _ old.max - old.min; oldLoc: DABasics.Position _ SELECT old.side FROM bottom => [old.min + width/2, routingRect.y1 - old.depth], top => [old.min + width/2, routingRect.y2 + old.depth], left => [routingRect.x1 - old.depth, old.min + width/2], right => [routingRect.x2 + old.depth, old.min + width/2], ENDCASE => Route.Error[programmingError, "not suppose to happen."]; SELECT chanSide FROM chanBottom, chanTop => { loc _ RouteUtil.XYToPQ[rules, oldLoc]; loc.q _ loc.q - activeSide.routeAreaCoord}; chanLeft => { SELECT parms.routerUsed FROM channel => { loc _ [chanPins.cEnd1, 0]; width _ 0; }; switchBox => { loc _ RouteUtil.XYToPQ[rules, oldLoc]; loc.p _ chanPins.cEnd1}; ENDCASE => ERROR; }; chanRight => { SELECT parms.routerUsed FROM channel => { loc _ [chanPins.cEnd2, 0]; width _ 0; }; switchBox => { loc _ RouteUtil.XYToPQ[rules, oldLoc]; loc.p _ chanPins.cEnd2; }; ENDCASE => ERROR; }; ENDCASE; new _ NEW[RouteChannel.InternPinRec _ [NIL, chanSide, loc, width, rLayer, old]]; fixedPins _ CONS[new, fixedPins]; ENDLOOP; }; CheckChanPins: PROC [chanPins: RouteChannel.RoutingChannelPins, rules: Route.DesignRules, netTab: RoutePrivate.NetTab, net: RoutePrivate.Net, pinList: List.LORA -- RouteChannel.InternPinList --, signalSinglePinNets, signalCoincidentPins: BOOLEAN] RETURNS [ok: BOOLEAN _ TRUE] = { numPins, numOverlaps: NAT _ 0; leftExit, rightExit: BOOLEAN _ FALSE; branchLayer: CD.Layer _ rules.branchLayer; trunkLayer: CD.Layer _ 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]; numPins _ numPins + 1; IF previousPin # NIL THEN { -- check for overlapping pins. IF previousPin.chanSide = this.chanSide THEN { rLast, rThis: RTBasic.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 RTBasic.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: DABasics.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 this.pin.layer # branchLayer THEN { Route.Signal[callingError, Rope.Cat["Pin not on branch layer, net: ", net.name, ", pin: ", this.name]]; ok _ FALSE}}; chanLeft => { IF this.pin.layer # 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 this.pin.layer # 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}; }; BuildChanPinForDogleg: PROC [chanPins: RouteChannel.RoutingChannelPins, parms: RoutePrivate.RoutingAreaParms, rules: Route.DesignRules, net: RoutePrivate.Net, pinList: List.LORA -- RouteChannel.InternPinList -- ] = { previousSeg: RouteChannel.Segment _ NIL; previousPin: RouteChannel.InternPin _ NARROW[pinList.first]; previousPinPosition: RouteChannel.PinPosition _ chanPins.sides[FindPinPos[chanPins, previousPin.location.p]]; previousChanPin: RouteChannel.ChanPin _ BuildPin[parms, previousPin, previousPinPosition, rules.branchWidth]; SELECT previousPin.chanSide FROM chanBottom, chanTop => IF ~PinViolation[chanPins, parms, rules, previousChanPin, net] THEN previousPinPosition.pins[previousChanPin.pinSide] _ previousChanPin; chanLeft, chanRight => previousPinPosition.innerPins _ CONS[previousChanPin, previousPinPosition.innerPins]; ENDCASE; FOR pList: List.LORA -- RouteChannel.InternPinList -- _ pinList.rest, pList.rest WHILE pList # NIL DO this: RouteChannel.InternPin _ NARROW[pList.first]; allPinsOverlap: BOOLEAN _ TRUE; useThisOne: BOOLEAN _ TRUE; rLast, rThis: RTBasic.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 RTBasic.Overlaps[rLast, rThis] THEN useThisOne _ FALSE; IF this.chanSide = chanLeft OR this.chanSide = chanRight THEN allPinsOverlap _ FALSE ELSE allPinsOverlap _ allPinsOverlap AND RTBasic.Equal[rLast, rThis]; IF useThisOne THEN { -- this pin is not a duplicate seg: RouteChannel.Segment _ NEW[RouteChannel.SegmentRec _ [net: net]]; pinPosition: RouteChannel.PinPosition _ chanPins.sides[FindPinPos[chanPins, this.location.p]]; chanPin: RouteChannel.ChanPin _ BuildPin[parms, this, pinPosition, rules.branchWidth]; SELECT this.chanSide FROM chanBottom, chanTop => IF ~PinViolation[chanPins, parms, rules, chanPin, net] THEN pinPosition.pins[chanPin.pinSide] _ chanPin; chanLeft, chanRight => pinPosition.innerPins _ CONS[chanPin, pinPosition.innerPins]; ENDCASE; previousChanPin.conctSeg[chanLeft] _ previousSeg; previousChanPin.conctSeg[chanRight] _ seg; chanPin.conctSeg[chanLeft] _ seg; chanPin.conctSeg[chanRight] _ NIL; seg.exteriorPins[chanLeft] _ previousChanPin; seg.exteriorPins[chanRight] _ chanPin; seg.interiorPins _ NIL; seg.qWidth _ IF allPinsOverlap THEN rules.trunkWidth ELSE net.trunkWidth; previousChanPin _ chanPin; previousSeg _ seg; }; previousPin _ this; ENDLOOP; }; BuildChanPin: PROC [chanPins: RouteChannel.RoutingChannelPins, parms: RoutePrivate.RoutingAreaParms, rules: Route.DesignRules, net: RoutePrivate.Net, pinList: List.LORA -- RouteChannel.InternPinList -- ] = { 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 { rLast, rThis: RTBasic.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 RTBasic.Overlaps[rLast, rThis] THEN useThisOne _ FALSE; IF this.chanSide = chanLeft OR this.chanSide = chanRight THEN allPinsOverlap _ FALSE ELSE -- make a narrow trunk if pins overlap allPinsOverlap _ allPinsOverlap AND RTBasic.Equal[rLast, rThis] }; IF useThisOne THEN { pinIndex _ FindPinPos[chanPins, this.location.p]; pinPosition _ chanPins.sides[pinIndex]; chanPin _ BuildPin[parms, this, pinPosition, rules.branchWidth]; SELECT this.chanSide FROM chanBottom, chanTop => IF ~PinViolation[chanPins, parms, rules, 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 rules.trunkWidth ELSE net.trunkWidth; }; FindPinPos: PUBLIC PROC [chanPins: RouteChannel.RoutingChannelPins, thisPLoc: DABasics.Number] RETURNS [index: RouteChannel.ZMPinsOnCh _ 0] = { found: BOOLEAN _ FALSE; FOR pIndex: RouteChannel.ZMPinsOnCh IN [1 .. chanPins.count] WHILE ~found DO pLoc: DABasics.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 [parms: RoutePrivate.RoutingAreaParms, iPin: RouteChannel.InternPin, pinPosition: RouteChannel.PinPosition, pinWidth: DABasics.Number] RETURNS [pin: RouteChannel.ChanPin] = { kindOfPin: RouteChannel.ChanPinType _ IF iPin.chanSide = chanLeft OR iPin.chanSide = chanRight THEN exitPin ELSE compPin; thisPinWidth: DABasics.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 [chanPins: RouteChannel.RoutingChannelPins, parms: RoutePrivate.RoutingAreaParms, rules: Route.DesignRules, pin: RouteChannel.ChanPin, net: RoutePrivate.Net] RETURNS [violation: BOOLEAN _ FALSE] = { LimitProc: RouteChannel.EachPinActionProc = { IF pin # NIL THEN IF pin.pinSide = pinSide THEN {width: DABasics.Number _ RouteUtil.GetWidthWithContact[rules, pin.pWidth]/2; pinPos: DABasics.Number _ pin.pinPosition.pLoc; IF pinPos < pLoc THEN quit _ pinPos + width + rules.branchSpacing > pLoc - pWidth ELSE -- pinPos >= pLoc quit _ pLoc + pWidth + rules.branchSpacing > pinPos - width}; IF quit THEN violationPin _ pin.pin}; pinSide: RouteChannel.ChanSide _ pin.pinSide; pinIndex: RouteChannel.ZMPinsOnCh _ pin.pinPosition.pinIndex; pLoc: DABasics.Number _ pin.pinPosition.pLoc; pWidth: DABasics.Number _ RouteUtil.GetWidthWithContact[rules, pin.pWidth]/2; testWidth: DABasics.Number _ RouteUtil.GetWidthWithContact[rules, parms.widestPin] + rules.branchSpacing; violationPin: RouteChannel.InternPin _ NIL; iPos: DABasics.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[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[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 [chanPins: RouteChannel.RoutingChannelPins] 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; }; }; ReallyGetExits[chanPins.sides[1]]; IF chanPins.count > 0 THEN ReallyGetExits[chanPins.sides[chanPins.count]]; }; MakeSBTracks: PROC [chanData: RouteChannel.ChannelData, rules: Route.DesignRules, okToDiddleLLPins, okToDiddleURPins: BOOLEAN] = { ExitPinCompare: List.CompareProc = { q1: DABasics.Number _ NARROW[ref1, RouteChannel.ChanPin].pin.location.q; q2: DABasics.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: DABasics.Number]~ { FOR trackPos: DABasics.Number _ lastPos + trackDist, trackPos + trackDist WHILE trackPos <= thisPos - trackDist DO [] _ AddTrack[chanTracks, trackPos]; lastPos _ trackPos; ENDLOOP; }; ClosestTrack: PROC [thisPos: DABasics.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; }; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; chanTracks: RouteChannel.RoutingChannelTracks _ chanData.chanTracks; exitPinList: List.LORA _ GetExitPins[chanPins]; sortedExitPinList: List.LORA _ List.Sort[exitPinList, ExitPinCompare]; lastPos: DABasics.Number _ chanData.chanSides[chanBottom].routeAreaCoord; previousPin: RouteChannel.ChanPin _ NIL; trackDist: DABasics.Number _ 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: DABasics.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[chanTracks, thisPos]} ELSE { Route.Signal[callingError, Rope.Cat["Pins too close on ends of switchbox, pin 1: ", this.pin.name, ", pin 2: ", pin2Name]]; track _ AddTrack[chanTracks, 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[chanTracks, thisPos]} ELSE { thisPos _ lastPos + trackDist; track _ AddTrack[chanTracks, 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[chanTracks, 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[chanTracks, 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: DABasics.Number _ lastPos + trackDist, trackPos + trackDist WHILE trackPos < chanData.chanSides[chanTop].routeAreaCoord - trackDist DO [] _ AddTrack[chanTracks, 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: DABasics.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 [chanTracks: RouteChannel.RoutingChannelTracks, rules: Route.DesignRules, maxTracksRequired: NAT] = { trackLimit: RouteChannel.ZMaxTracks _ MIN[chanTracks.size, maxTracksRequired]; loc: DABasics.Number _ rules.trunkToEdge; FOR trackIndex: RouteChannel.ZMaxTracks IN [1 .. trackLimit] DO [] _ AddTrack[chanTracks, loc]; loc _ loc + rules.trunkToTrunk; ENDLOOP; chanTracks.maxCount _ chanTracks.count; }; AddTrack: PROC [chanTracks: RouteChannel.RoutingChannelTracks, trackPos: DABasics.Number] RETURNS [track: RouteChannel.Track] = { 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 [chanData: RouteChannel.ChannelData, parms: RoutePrivate.RoutingAreaParms, rules: Route.DesignRules, chanSide: RouteChannel.ChanSide] = { chanTracks: RouteChannel.RoutingChannelTracks _ chanData.chanTracks; activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; chanPins: RouteChannel.RoutingChannelPins _ chanData.chanPins; trackDist: DABasics.Number _ rules.trunkToTrunk; FOR rlayer: RoutePrivate.RoutingLayer IN [trunk .. branch] DO FOR bList: RTBasic.PQRectList _ activeSide.barrierList[rlayer], bList.rest WHILE bList # NIL DO BlockProc: RouteChannel.EachTrackActionProc = { trackPos: DABasics.Number _ RouteChannel.TrackLoc[chanTracks, parms, rules, trackIndex]; IF minQ < trackPos - trackDist AND trackPos + trackDist < maxQ THEN track.blocked _ TRUE}; barrierItem: RTBasic.PQRect _ bList.first; minQ: DABasics.Number _ MIN[barrierItem.c1.q, barrierItem.c2.q]; maxQ: DABasics.Number _ MAX[barrierItem.c1.q, barrierItem.c2.q]; [] _ RouteChannel.EnumTracks[chanTracks, BlockProc]; ENDLOOP; ENDLOOP; }; DoLRBarriers: PROC [chanData: RouteChannel.ChannelData, chanSide: RouteChannel.ChanSide] = { activeSide: RouteChannel.RoutingChannelSides _ chanData.chanSides[chanSide]; FOR rlayer: RoutePrivate.RoutingLayer IN [trunk .. branch] DO FOR bList: RTBasic.PQRectList _ activeSide.barrierList[rlayer], bList.rest WHILE bList # NIL DO BlockProc: RouteChannel.EachTrackActionProc = { NULL }; barrierItem: RTBasic.PQRect _ bList.first; minQ: DABasics.Number _ MIN[barrierItem.c1.q, barrierItem.c2.q]; maxQ: DABasics.Number _ MAX[barrierItem.c1.q, barrierItem.c2.q]; [] _ RouteChannel.EnumTracks[chanData.chanTracks, BlockProc]; ENDLOOP; ENDLOOP; }; Surround: PROC [r1, r2: RTBasic.PQRect] RETURNS [RTBasic.PQRect] = INLINE { RETURN [RTBasic.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 }. ΨRouteChannelInitImpl.mesa Copyright Σ 1985, 1987, 1988 by Xerox Corporation. All rights reserved. by Bryan Preas July 10, 1985 6:57:00 pm PDT last edited by Bryan Preas July 26, 1988 5:24:09 pm PDT Christian Le Cocq January 19, 1988 9:52:19 am 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 IF parms.routerUsed = switchBox THEN MakeSBTracks[routingArea, okToDiddleLLPins, okToDiddleURPins] ELSE MakeCRTracks[routingArea, parms.numTracksToUse]; Convert a channel side to p-q space and set up side data convert routing barriers for a side chanDirection: DABasics.Direction _ rules.trunkDirection; clean up the rectangle and return it. *** CHECK THAT PROC... *** chanDirection: DABasics.Direction _ rules.trunkDirection; 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 chanDirection: DABasics.Direction _ routingArea.rules.trunkDirection; BuildChanPin[chanPins, parms, rules, net, sortedPinList]; 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 NOTE: this is an experimental version for DogLeg testing. Must either be made permanent or discarded. Functionally replaces BuildChanPin. BTP July 26, 1988 build the channel pins data structure for this net. NOTE: this assumes that CheckChanPins has been invoked and that the inputs are valid!!! set up for the leftmost pin in the net now update the segment make a narrow trunk if pins overlap 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 loc: DABasics.Number _ rules.trunkToEdge + (trackIndex - 1) * rules.trunkToTrunk; 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šœH™HKšœ*Οkœ™.Kšœ9™9K™1—K˜š ˜ KšœœX˜bK˜—šΠlnœœ˜#Kšœ>˜EKšœ˜K˜Kš œœœœœ6˜sKš œœœœœ˜[š œ œœœœ"˜bK˜——šΟn œœœ-˜FKšœ%˜%Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœœ˜Kšœ)˜0Kšœ™K™KšœD˜DKšœF˜FKšœE˜EKšœ(˜(K˜Kšœb™bKšœ œ˜,Kšœœ˜Kšœœ&˜GKšœ!œ&˜JKšœœ&˜HKšœ œ&˜IKšœœ'˜@Kšœœ%˜˜>Kšœ;˜;Kšœ;˜;šœ<˜™bšœ1™5K™—Kšœ1˜1Kšœ.˜.Kšœ!˜!Kšœ"˜"Kšœ˜K˜—šŸ œœ$˜6Kšœ,˜,Kšœ˜Kšœ!˜!Kšœ ˜ K™8K™KšœL˜LKšœ>˜>KšœH˜HKšœB˜BKšœ/˜/Kšœ˜šœE˜EK˜—šœœœ˜Kšœ[˜[KšœY˜YKšœD˜DKšœ˜—Kšœ˜K˜—šŸœœ.˜FKšœ˜Kšœ.˜.Kšœ#˜#Kšœ7˜>™#K™—Kšœ,˜,KšœB˜BKšœ9™9šœœœ˜šœNœ œ˜eKšœ7˜7Kšœœ˜+šœœ œ=œ˜eš œœœ1œ œ˜WKšœO˜Ošœœœ˜Kšœ9˜9Kšœ!œ3˜X—Kš˜—Kšœ˜—Kš˜—Kšœ˜—Kšœ˜K˜—šŸ œœ_œ"˜—K™%K™K™Kšœ9™9Kšœ@˜@Kšœ@˜@Kšœœ˜'Kšœ˜K˜—šŸ œœ$˜5Kšœ-˜-Kšœ˜Kšœ!˜!Kšœ ˜ K™7K™KšœL˜LKšœH˜HKšœB˜BK˜Kšœ˜Kšœ/˜/šœœœ˜Kšœ[˜[KšœY˜YKšœD˜DKšœ˜—Kšœ˜K˜—šŸ œœE˜VK™=K™Kšœ>˜>KšœJ˜JKšœL˜LKšœN˜NKšœH˜HKšœ ˜*K˜Kšœ™Kšœ)˜)Kšœ*˜*Kšœ"œ'˜LKšœ"œ(˜MKšœ-˜-Kšœ*˜*Kšœ˜K˜—šŸœœ$˜2Kšœ%˜%Kšœ˜Kšœ˜Kšœ˜Kšœ+œ˜7šœ™K™—šŸ œ˜!Kšœœ*˜FKšœœ*˜Fšœœ œ˜-Kšœœ œ˜,Kšœ˜ —Kšœ˜K˜—Kšœ#œ˜(KšœE™EKšœ>˜>KšœA˜AKšœœ˜9Kšœ6˜6šœg˜mKšœB˜BKšœ9™9—Kšœ˜K˜—šŸœœ&˜3Kšœ%˜%Kšœ˜Kšœ˜Kšœ  œœ˜P™:K˜—Kšœ>˜>Kšœ/˜/šœ(œœ˜>Kšœ˜Kšœ˜Kšœ˜KšœQ˜QKšœL˜LKšœT˜Tšœ+˜+K˜—šœœ ˜0Kšœ:˜:Kšœ7˜7Kšœ8˜8Kšœ9˜9Kšœ<˜CK˜—šœ ˜šœ˜Kšœ&˜&Kšœ+˜+—šœ ˜ šœ˜šœ ˜ Kšœ˜Kšœ ˜ Kšœ˜—šœ˜Kšœ&˜&Kšœ˜—Kšœœ˜K˜——šœ˜šœ˜šœ ˜ Kšœ˜Kšœ ˜ Kšœ˜—šœ˜Kšœ&˜&Kšœ˜Kšœ˜—Kšœœ˜K˜——Kšœ˜—Kšœœœ&˜PKšœ œ˜!Kš˜—šœ˜K˜——šŸ œœ,˜?Kšœ˜Kšœ˜Kšœ˜Kšœœ  œ˜6Kšœ+œ˜4Kšœœœ˜ ™Kš ™Kš ™Kš ™Kš ™Kš ™K™—Kšœœ˜Kšœœœ˜%Kšœ œ˜*Kšœ œ˜(šœ&œ˜*K˜—š œ œ  œœ œ˜`Kšœœ˜3K˜šœœœ ˜<šœ&œ˜.Kšœ˜šœ#œ œ˜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œ˜@—Kšœ˜K˜—šŸœœ,˜GKšœ%˜%Kšœ˜Kšœ˜Kšœœ  œ˜9šœzŸ œ™K˜—K™3šœS™WK˜—Kšœ&™&Kšœ$œ˜(Kšœ&œ˜Kšœ%˜%Kšœ˜Kšœ˜Kšœœ  œ˜9K™3šœS™WK˜—Kšœ)œ˜-Kšœ)œ˜NKšœ&œ˜*Kšœ*œ˜.Kšœœœ˜šœœ'˜FK˜—š œ œ  œœ œ˜`Kšœ"˜"Kšœ&˜&Kšœ˜Kšœœ˜3Kšœ œœ˜šœœœ˜Kšœ˜šœ#œ œ˜MKšœg˜gKšœK˜K—šœ .˜5Kšœg˜gKšœL˜L—šœ&œ œ˜PKšœ œ˜—šœœ˜=Kšœ˜—š˜K˜&Kšœ œ˜?—Kšœ˜K˜—šœ œ˜Kšœ1˜1Kšœ'˜'šœ@˜@K˜—šœ˜šœœ5˜RKšœ,˜,—šœ˜Kšœœ!˜=—Kšœ˜K˜—šœ œœ˜Kšœ0œ$˜W—šœœœ˜$Kšœ2œ#˜X—šœ˜Kšœœ_˜r—K˜—Kšœ˜Kšœ˜K˜—Kšœ™Kšœ%˜%Kšœ'˜'Kšœ ˜ Kšœ œœœ˜IKšœ˜K˜—šŸ œœœHœ)˜Kšœ>™>KšœS™WK˜Kšœœœ˜K˜šœ!œœ˜LKšœ4˜4šœ˜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˜—šŸœœˆœ ˜½K™6K˜šœ%˜%Kšœœœ˜EKšœ ˜ —šœ˜Kšœœ ˜'Kšœœ˜ —Kšœœ ˜5šœœ˜%Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜—Kšœ˜K˜—šŸ œœ-˜?Kšœ&˜&Kšœ˜Kšœ˜Kšœ˜Kšœ œœ˜(™ K˜—šŸ œ$˜-šœœ˜šœ˜KšœM˜MKšœ/˜/šœ˜Kšœ<˜<—šœ ˜Kšœ=˜=—Kšœœ˜%——K˜—Kšœ-˜-Kšœ=˜=Kšœ-˜-KšœM˜MKšœj˜jKšœ'œ˜+K™KšœA™AKšœ6˜6š œ$ œœœ4œ ˜KšœI˜Išœ ˜Kšœž˜ž—Kšœ˜—K˜Kšœ?™?Kšœ%˜%š œ$œœ4œ ˜‘KšœI˜Išœ ˜Kšœž˜ž—Kš˜—Kšœ˜K˜—š Ÿ œœ-œœœ˜eK™KšœS™WK˜šŸœœ'˜;šœ œœ˜šœAœ œ˜XKšœ)˜)šœ˜Kšœ"œ˜7Kšœ˜—Kš˜—Kšœ˜—šœ˜K˜——Kšœ"˜"Kšœœ0˜JKšœ˜K˜—šŸ œœ&˜8Kšœ˜Kšœ$œ˜0K™K˜šŸœ˜$Kšœœ,˜HKšœœ,˜Hšœœ œ˜-Kšœœ œ˜,Kšœ˜ —šœ˜K˜——šŸœœ˜(K˜šœGœ!˜rKšœ$˜$Kšœ˜Kš˜—Kšœ˜K˜—šŸ œœœœ˜[Kšœœœœ˜šœ œœ˜/Kšœ=˜=Kšœ œœ ˜4šœœ˜Kšœ˜Kšœ˜—Kšœ˜—K˜K˜—Kšœ>˜>KšœD˜DKšœœ˜/Kšœœ*˜FKšœI˜IKšœ$œ˜(Kšœ0˜0Kšœ œ˜$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šœ˜—Kšœ˜—K˜šœ "˜9šœœ ˜Ašœ œ ˜;Kšœ ˜ —šœ ˜Kšœ{˜{—Kšœ&˜&Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜K˜—Kšœ˜K˜—Kšœ*™*šœœœBœ˜`Kšœk˜k—šœGœC˜”Kšœ$˜$Kšœ˜Kšœ˜—Kšœ'˜'K˜šœ%œ˜-Kšœ™š œ œ  œ"œ œ˜kKšœœ˜1Kšœ/˜/Kš œœœœœœ˜QKšœ˜K˜šœœ˜šœ !˜8šœœ ˜AK˜Kšœ˜—Kšœ˜—šœ "˜9šœœ ˜CK˜Kšœ˜Kšœ˜——Kšœ˜—Kš˜—Kšœ˜—Kšœ˜K˜—šŸ œœ1˜CKšœ˜Kšœœ˜K™K˜Kšœ&œ%˜NKšœ)˜)šœ%œ˜?KšœQ™QKšœ˜Kšœ˜Kšœ˜—Kšœ'˜'Kšœ˜K˜—šŸœœLœ ˜K™K˜šœ+˜1KšœE˜E—Kšœ(˜(Kšœ.œL˜}K˜—šŸœœ;˜OK˜šœœœ˜KšœA™AKšœA˜AKšœ%˜%šœ3œœ˜IKšœ'˜'šœœœ˜Kšœœ%˜DKšœ+˜/—Kšœ˜—Kšœ˜—Kšœ˜K˜—šŸ œœ&˜8Kšœ%˜%Kšœ˜Kšœ$˜$K™&K™KšœD˜DKšœL˜LKšœ>˜>Kšœ0˜0šœ#œ˜=šœHœ œ˜_šŸ œ&˜/KšœX˜Xšœœ˜CKšœœ˜—K˜—Kšœ*˜*Kšœœ%˜@Kšœœ%˜@Kšœ4˜4Kšœ˜—Kš˜—Kšœ˜K˜—šŸ œœ&˜8Kšœ$˜$K™&K™KšœL˜Lšœ#œ˜=šœHœ œ˜_šŸ œ'œ˜7K˜—Kšœ*˜*Kšœœ%˜@Kšœœ%˜@Kšœ=˜=Kšœ˜—Kš˜—Kšœ˜K˜—šŸœœœ˜Bšœ˜Kšœ˜Kšœœœ˜/Kšœœœ˜2—K˜—šŸ œœ$œ œ˜RKšœœ˜ šœœ˜Kšœœ˜K˜Kšœ˜—Kšœ˜ Kšœ ˜K˜—Kšœ˜—…—wP