DIRECTORY CD, CDBasics, CDCells, CDProperties, CDRects, CDSymbolicObjects, Convert, Core, CoreGeometry, CoreOps, DABasics, GC, GCPrivate, IP, IPCTG, IPTop, RefTab, Rope, Route, RTBasic, CoreRouteFlat, Sinix, SinixOps, SymTab; GCDetailRouteImpl: CEDAR PROGRAM IMPORTS CD, CDBasics, CDCells, CDProperties, CDRects, CDSymbolicObjects, Convert, CoreGeometry, CoreOps, GC, GCPrivate, IPCTG, Route, RTBasic, RefTab, Rope, CoreRouteFlat, Sinix, SinixOps, SymTab EXPORTS GCPrivate = BEGIN NetInChan: TYPE = REF NetInChanRec; -- routing description for a net on a channel NetInChanRec: TYPE = RECORD [ segmentCount: INT _ 0, -- count of existing segments segsInNet: LIST OF Segment _ NIL]; -- segments for this net on this channel Segment: TYPE = REF SegmentRec; SegmentRec: TYPE = RECORD[ name: Rope.ROPE _ NIL, trunkSize: INT _ 0, leftOrBottomExit: BOOL _ FALSE, -- used for channels only (not SB's) rightOrTopExit: BOOL _ FALSE, -- used for channels only (not SB's) pinsInSeg: LIST OF Pin _ NIL]; -- pins for this net on this channel Pin: TYPE = REF PinRec; -- pin description for pins in a net on a channel PinRec: TYPE = RECORD[ min: CD.Number _ 0, max: CD.Number _ 0, layer: CD.Layer, side: DABasics.Side]; DoDetailRoute: PUBLIC PROC [context: GC.Context] RETURNS [result: GC.Result] = { MakeChannel: GCPrivate.EachChannelAction ~ { InitNet: SymTab.EachPairAction = {net: CoreRouteFlat.Net _ NARROW[val]; net.any _ NIL}; rulesParameters: Route.DesignRulesParameters _ IF channel.direction = horizontal THEN context.rules.horizParams ELSE context.rules.vertParams; rules: Route.DesignRules _ IF channel.direction = horizontal THEN context.rules.horizRules ELSE context.rules.vertRules; lower, upper: RTBasic.PQPos; [] _ SymTab.Pairs[context.structure.nets, InitNet]; GCPrivate.AssignCoordinates[context]; lower _ GCPrivate.LowerChannelPQ[channel]; upper _ GCPrivate.UpperChannelPQ[channel]; SELECT TRUE FROM channel.ch = bottomChannel => { channel.topOrRightShell _ MakeSide[context, rules, channel, top, TRUE]; channel.bottomOrLeftShell _ MakeSide[context, rules, channel, bottom, FALSE]}; channel.ch = rightChannel => { channel.bottomOrLeftShell _ MakeSide[context, rules, channel, left, TRUE]; channel.topOrRightShell _ MakeSide[context, rules, channel, right, FALSE]}; channel.ch = topChannel => { channel.bottomOrLeftShell _ MakeSide[context, rules, channel, bottom, TRUE]; channel.topOrRightShell _ MakeSide[context, rules, channel, top, FALSE]}; channel.ch = leftChannel => { channel.topOrRightShell _ MakeSide[context, rules, channel, right, TRUE]; channel.bottomOrLeftShell _ MakeSide[context, rules, channel, left, FALSE]}; ENDCASE => { channel.bottomOrLeftShell _ MakeSide[context, rules, channel, ChanSide[channel, bottom], TRUE]; channel.topOrRightShell _ MakeSide[context, rules, channel, ChanSide[channel, top], TRUE]}; MakeEnd[context, rules, channel, ChanSide[channel, left]]; -- lower end of channel MakeEnd[context, rules, channel, ChanSide[channel, right]]; -- upper end of channel channel.intermediateResult _ Route.ChannelRoute[enumerateNets: EnumerateChannelNets, min: lower.p, max: upper.p, rulesParameters: rulesParameters, rules: rules, name: NIL, enumerateObstructions: NIL, channelData: context, optimization: context.parms.opt, signalSinglePinNets: context.parms.signalSinglePinNets]; channel.cdObject _ Route.ChannelRetrieve[intermediateResult: channel.intermediateResult, brokenNets: NIL, channelData: channel, retrieveRect: NIL].object; channel.width _ IF channel.direction = horizontal THEN CDBasics.SizeOfRect[channel.intermediateResult.routingRect].y ELSE CDBasics.SizeOfRect[channel.intermediateResult.routingRect].x; IPCTG.SetWidth[channel.ch, channel.width]}; IncludeChannel: GCPrivate.EachChannelAction ~ { IF channel.cdObject # NIL THEN { chanInst: CD.Instance; chanInst _ GCPrivate.IncludeOb[cell: object, ob: channel.cdObject, position: channel.position]; CDProperties.PutInstanceProp[chanInst, $InstanceName, Rope.Cat["Channel", channel.name]]}; SELECT TRUE FROM channel.ch = bottomChannel => { topPos: CD.Position _ [channel.position.x, channel.position.y + channel.width]; [] _ GCPrivate.IncludeOb[cell: object, ob: channel.topOrRightShell, position: topPos]}; channel.ch = rightChannel => { bottomPos: CD.Position _ [channel.position.x - CD.InterestSize[channel.bottomOrLeftShell].x, channel.position.y]; [] _ GCPrivate.IncludeOb[cell: object, ob: channel.bottomOrLeftShell, position: bottomPos]}; channel.ch = topChannel => { bottomPos: CD.Position _ [channel.position.x, channel.position.y - CD.InterestSize[channel.bottomOrLeftShell].y]; [] _ GCPrivate.IncludeOb[cell: object, ob: channel.bottomOrLeftShell, position: bottomPos]}; channel.ch = leftChannel => { topPos: CD.Position _ [channel.position.x + channel.width, channel.position.y]; [] _ GCPrivate.IncludeOb[cell: object, ob: channel.topOrRightShell, position: topPos]}; ENDCASE => { topPos: CD.Position _ IF channel.direction = horizontal THEN [channel.position.x, channel.position.y + channel.width] ELSE [channel.position.x + channel.width, channel.position.y]; bottomPos: CD.Position _ IF channel.direction = horizontal THEN [channel.position.x, channel.position.y - CD.InterestSize[channel.bottomOrLeftShell].y] ELSE [channel.position.x - CD.InterestSize[channel.bottomOrLeftShell].x, channel.position.y]; [] _ GCPrivate.IncludeOb[cell: object, ob: channel.topOrRightShell, position: topPos]; [] _ GCPrivate.IncludeOb[cell: object, ob: channel.bottomOrLeftShell, position: bottomPos]}}; topology: IPTop.Ref _ NARROW[context.topology]; bottomChannel, rightChannel, topChannel, leftChannel: IPCTG.Channel; object: CD.Object _ CDCells.CreateEmptyCell[]; [bottomChannel, rightChannel, topChannel, leftChannel] _ IPCTG.GetBoundingChannels[topology.ctg]; [] _ GCPrivate.EnumerateChannelsInTopologicalOrder[context, MakeChannel]; GCPrivate.AssignCoordinates[context]; [] _ GCPrivate.EnumerateChannelsInTopologicalOrder[context, IncludeChannel]; FOR insts: LIST OF CoreRouteFlat.Instance _ context.structure.instances, insts.rest WHILE insts#NIL DO IF insts.first.layObject # NIL THEN { cdInst: CD.Instance _ GCPrivate.IncludeOb [object, insts.first.layObject, insts.first.position, insts.first.orientation]; CDProperties.PutInstanceProp[cdInst, $InstanceName, insts.first.name]} ENDLOOP; RTBasic.RepositionCell[object]; result _ NEW[GC.ResultRec _ [context: context, object: object]]}; MakeSide: PROC [context: GC.Context, rules: Route.DesignRules, channel: GCPrivate.Channel, chanSide: RTBasic.Side, internal: BOOLEAN] RETURNS [shell: CD.Object _ CDCells.CreateEmptyCell[]] ~ { DoExternalPins: IPCTG.EachComponentAction ~ { IF co # NIL THEN { instance: CoreRouteFlat.Instance _ NARROW[co.any]; FOR nns: LIST OF CoreRouteFlat.NetONode _ instance.netONodes, nns.rest WHILE nns#NIL DO IF ~ExternalNet[context.structure, nns.first.net] THEN LOOP; FOR pins: LIST OF CoreRouteFlat.Pin _ nns.first.oNode.pins, pins.rest WHILE pins#NIL DO IF pins.first.side = otherSide AND internal OR pins.first.side = chanSide AND ~internal THEN { netDesc: GCPrivate.NetDesc _ NARROW [RefTab.Fetch[channel.connections, nns.first.net].val]; phyPin: GCPrivate.PhyPinDesc _ NEW[GCPrivate.PhyPinDescRec _ [instance, nns.first.oNode, pins.first]]; tabIndex: Rope.ROPE _ IF GCPrivate.PinOnList[netDesc, phyPin] THEN nns.first.net.name ELSE Rope.Cat[nns.first.net.name, "@", instance.name, "@", Convert.RopeFromInt[pins.first.range.min]]; netDat: NetInChan _ IF nns.first.net.any = NIL THEN NEW[NetInChanRec] ELSE NARROW[nns.first.net.any]; range: CoreRouteFlat.Range _ CoreRouteFlat.TranslateRange[instance, pins.first]; AddPin[nns.first.net, netDat, tabIndex, range, rules.branchLayer, chanSide,0]; maxWidth _ MAX[maxWidth, IncludePin[ phyPin: NEW[GCPrivate.PhyPinDescRec _ [instance, nns.first.oNode, pins.first]], name: tabIndex, side: chanSide, lower: lower, upper: upper, shell: shell ] ] }; ENDLOOP; ENDLOOP } }; EachNet: RefTab.EachPairAction ~ { netDesc: GCPrivate.NetDesc _ NARROW[val]; FOR list: LIST OF GCPrivate.PhyPinDesc _ netDesc.pinList, list.rest WHILE list # NIL DO net: CoreRouteFlat.Net _ NARROW[key]; exteriorPublic: BOOLEAN _ IPCTG.IsBoundingChannel[topology.ctg, channel.ch] AND ExternalNet[context.structure, net]; -- External pins already included phyPin: GCPrivate.PhyPinDesc _ list.first; IF phyPin.pPin.side = otherSide AND ~exteriorPublic THEN { netDat: NetInChan _ IF net.any = NIL THEN NEW[NetInChanRec] ELSE NARROW[net.any]; range: CoreRouteFlat.Range _ CoreRouteFlat.TranslateRange [phyPin.instance, phyPin.pPin]; AddPin[net, netDat, net.name, range, rules.branchLayer, chanSide, 0]; maxWidth _ MAX[maxWidth, IncludePin [list.first, net.name, chanSide, lower, upper, shell]]}; ENDLOOP}; DoChannelIntersection: IPCTG.EachIntersectionAction ~ { EachExit: PublicEnumerator ~ { net: CoreRouteFlat.Net _ NARROW[SymTab.Fetch[context.structure.nets, name].val]; IF (internal AND side = otherSide) OR (~internal AND ExternalNet[context.structure, net] AND side = chanSide) THEN { range: CoreRouteFlat.Range _ SELECT chanSide FROM bottom, top => [otherChannel.position.x + min, otherChannel.position.x + max], left, right => [otherChannel.position.y + min, otherChannel.position.y + max], ENDCASE => GC.Error[programmingError, "Call maintainer"]; netDat: NetInChan _ IF net.any = NIL THEN NEW[NetInChanRec] ELSE NARROW[net.any]; AddPin[net, netDat, name, range, rules.branchLayer, chanSide, 0]}}; otherChannel: GCPrivate.Channel _ NARROW[i.ch.any]; [] _ EnumerateEdgePins[context, otherChannel, EachExit]}; rect: CD.Rect; maxWidth: INT _ rules.branchWidth; otherSide: RTBasic.Side _ RTBasic.OtherSide[chanSide]; bottomOrLeft: BOOLEAN _ chanSide = bottom OR chanSide = left; topOrRight: BOOLEAN _ ~bottomOrLeft; lower: RTBasic.PQPos _ GCPrivate.LowerChannelPQ[channel]; upper: RTBasic.PQPos _ GCPrivate.UpperChannelPQ[channel]; topology: IPTop.Ref _ NARROW[context.topology]; polarity: IP.PolarityTypes _ IF (internal AND bottomOrLeft) OR (~internal AND topOrRight) THEN neg ELSE pos; IF IPCTG.IsBoundingChannel[topology.ctg, channel.ch] THEN [] _ IPCTG.Components[channel.ch, polarity, DoExternalPins]; [] _ RefTab.Pairs[channel.connections, EachNet]; [] _ IPCTG.Intersections[channel.ch, polarity, DoChannelIntersection]; IF channel.lowerIntType = inverted THEN [] _ DoChannelIntersection[IPCTG.End[channel.ch, neg]]; IF channel.upperIntType = inverted THEN [] _ DoChannelIntersection[IPCTG.End[channel.ch, pos]]; rect _ SELECT chanSide FROM bottom => [lower.p, lower.q - maxWidth, upper.p, lower.q], right => [upper.q, lower.p, upper.q + maxWidth, upper.p], top => [lower.p, upper.q, upper.p, upper.q + maxWidth], left => [lower.q - maxWidth, lower.p, lower.q, upper.p], ENDCASE => GC.Error[programmingError, "Call maintainer"]; CDCells.SetInterestRect[design: NIL, cell: shell, r: rect]; -- set interestRect of shell RTBasic.RepositionCell[shell]}; MakeEnd: PROC [context: GC.Context, rules: Route.DesignRules, channel: GCPrivate.Channel, side: RTBasic.Side] ~ { EachNet: RefTab.EachPairAction ~ { netDesc: GCPrivate.NetDesc _ NARROW[val]; net: CoreRouteFlat.Net _ NARROW[key]; publicNet: BOOLEAN _ CoreRouteFlat.IsPublic[net]; needsExit: BOOLEAN _ (side = bottom OR side = left) AND netDesc.bottomOrLeftExit OR (side = top OR side = right) AND netDesc.topOrRightExit; IF needsExit AND (ordinaryIntersection OR publicNet) THEN { netDat: NetInChan _ IF net.any = NIL THEN NEW[NetInChanRec] ELSE NARROW[net.any]; AddEnd[net, netDat, net.name, side, 0]}}; ordinaryIntersection: BOOLEAN _ (channel.lowerIntType = normal AND (side = left OR side = bottom)) OR (channel.upperIntType = normal AND (side = right OR side = top)); [] _ RefTab.Pairs[channel.connections, EachNet]}; SideToOrientation: PROC [side: RTBasic.Side] RETURNS [orientation: CD.Orientation] ~ { orientation _ SELECT side FROM top => CDSymbolicObjects.OrientFromDirection[south], left => CDSymbolicObjects.OrientFromDirection[east], bottom => CDSymbolicObjects.OrientFromDirection[north], right => CDSymbolicObjects.OrientFromDirection[west], ENDCASE => GC.Error[programmingError, "Call maintainer"]}; ChanSide: PROC [channel: GCPrivate.Channel, side: DABasics.Side] RETURNS [sideToUse: DABasics.Side] ~ { sideToUse _ IF channel.direction = horizontal THEN side ELSE SELECT side FROM bottom => left, right => top, top => right, left => bottom, ENDCASE => GC.Error[programmingError, "Call maintainer"]}; ExternalNet: PROC [struc: CoreRouteFlat.Structure, net: CoreRouteFlat.Net] RETURNS [isExternal: BOOLEAN _ FALSE] ~ { FOR nns: LIST OF CoreRouteFlat.NetONode _ struc.outerInstance.netONodes, nns.rest WHILE nns#NIL DO IF nns.first.oNode.public THEN RETURN[TRUE] ENDLOOP}; IncludePin: PROC [phyPin: GCPrivate.PhyPinDesc, name: Rope.ROPE, side: RTBasic.Side, lower, upper: RTBasic.PQPos, shell: CD.Object] RETURNS[width: INT]~ { instance: CoreRouteFlat.Instance _ phyPin.instance; pPin: CoreRouteFlat.Pin _ phyPin.pPin; range: CoreRouteFlat.Range _ CoreRouteFlat.TranslateRange[instance, pPin]; rangeSize: INT _ range.max - range.min; wireLoc: CD.Position; wireSize: CD.Position; SELECT side FROM top => { width _ instance.position.y - upper.q; -- + pPin.depth wireSize _ [rangeSize, width]; wireLoc _ [range.min, upper.q]}; left => { sidePos: INT _ instance.position.x + CD.InterestSize[instance.layObject].x; width _ lower.q - sidePos; -- + pPin.depth; wireSize _ [width, rangeSize]; wireLoc _ [sidePos -- -pPin.depth--, range.min]}; bottom => { sidePos: INT _ instance.position.y + CD.InterestSize[instance.layObject].y; width _ lower.q - sidePos; -- + pPin.depth; wireSize _ [rangeSize, width]; wireLoc _ [range.min, sidePos -- -pPin.depth--]}; right => { width _ instance.position.x - upper.q; -- + pPin.depth; wireSize _ [width, rangeSize]; wireLoc _ [upper.q, range.min]}; ENDCASE; IF width # 0 THEN { wire: CD.Object _ CDRects.CreateRect[wireSize, pPin.layer]; CDProperties.PutProp[wire, $SignalName, name]; [] _ GCPrivate.IncludeOb[cell: shell, ob: wire, position: wireLoc]}}; EnumerateEdgePins: PROC [context: GC.Context, channel: GCPrivate.Channel, eachEdgeEntity: PublicEnumerator] RETURNS [quit: BOOL _ FALSE] ~ { ChanPublics: CoreGeometry.EachWirePinProc ~ { quit _ eachEdgeEntity[CoreOps.GetShortWireName[wire], min, max, side, layer]}; mode: Sinix.Mode = SinixOps.GetExtractMode[context.rules.technology]; cellType: Core.CellType _ NARROW [Sinix.Extract[channel.cdObject, mode].result]; [] _ CoreGeometry.EnumerateNonOverlappingSides[mode.decoration, cellType, ChanPublics]}; PublicEnumerator: TYPE = PROC[name: Rope.ROPE, min, max: INT, side: DABasics.Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE]; AddPin: PROC [net: CoreRouteFlat.Net, netDat: NetInChan, name: Rope.ROPE, range: CoreRouteFlat.Range, layer: CD.Layer, side: DABasics.Side, trunkWidth: INT] ~ { pin: Pin _ NEW[PinRec _ [min: range.min, max: range.max, --depth: 0,-- layer: layer, side: side]]; segment: Segment _ LookUpSegment[netDat, name]; segment.pinsInSeg _ CONS[pin, segment.pinsInSeg]; segment.trunkSize _ trunkWidth; net.any _ netDat}; AddEnd: PROC [net: CoreRouteFlat.Net, netDat: NetInChan, name: Rope.ROPE, side: DABasics.Side, trunkWidth: INT] ~ { segment: Segment _ LookUpSegment[netDat, name]; SELECT side FROM bottom, left => segment.leftOrBottomExit _ TRUE; right, top => segment.rightOrTopExit _ TRUE; ENDCASE; segment.trunkSize _ trunkWidth; net.any _ netDat}; LookUpSegment: PROC [netDat: NetInChan, name: Rope.ROPE] RETURNS [segment: Segment _ NIL] ~ { FOR segList: LIST OF Segment _ netDat.segsInNet, segList.rest WHILE segList # NIL AND segment = NIL DO IF Rope.Equal[segList.first.name, name] THEN segment _ segList.first; ENDLOOP; IF segment = NIL THEN { -- not found, make a NEW one segment _ NEW[SegmentRec _ [name: name]]; netDat.segmentCount _ netDat.segmentCount + 1; netDat.segsInNet _ CONS[segment, netDat.segsInNet]}}; EnumerateChannelNets: Route.EnumerateChannelNetsProc ~ { ChannelNet: SymTab.EachPairAction ~ { net: CoreRouteFlat.Net _ NARROW[val]; IF net.any # NIL THEN { netDat: NetInChan _ NARROW[net.any]; FOR segs: LIST OF Segment _ netDat.segsInNet, segs.rest UNTIL segs = NIL DO seg: Segment _ segs.first; eachNet[name: seg.name, enumeratePins: ChannelSignalPins, exitLeftOrBottom: seg.leftOrBottomExit, exitRightOrTop: seg.rightOrTopExit, trunkSize: seg.trunkSize, channelData: channelData, netData: seg]; ENDLOOP}}; structure: CoreRouteFlat.Structure _ NARROW[channelData, GC.Context].structure; [] _ SymTab.Pairs[structure.nets, ChannelNet]}; ChannelSignalPins: Route.EnumerateChannelPinsProc ~ { seg: Segment _ NARROW[netData]; FOR pins: LIST OF Pin _ seg.pinsInSeg, pins.rest UNTIL pins = NIL DO pin: Pin _ pins.first; bottomOrLeftSide: BOOL _ pin.side = bottom OR pin.side = left; eachPin[bottomOrLeftSide: bottomOrLeftSide, min: pin.min, max: pin.max, --depth: pin.depth,-- layer: pin.layer]; ENDLOOP}; END. nGCDetailRouteImpl.mesa Copyright Σ 1986, 1987 by Xerox Corporation. All rights reserved. Bryan Preas, February 19, 1987 9:06:53 pm PST Don Curry January 15, 1988 7:27:49 pm PST Local Types depth: CD.Number _ 0, DetailRoute Determine actual wiring paths. PROC[context: GC.Context, channel: Channel] RETURNS [quit: BOOLEAN _ FALSE]; construct a channel PROC[context: GC.Context, channel: Channel] RETURNS [quit: BOOLEAN _ FALSE]; Include a previously routed channel into the object being constructed first the channel RTBasic.SetCDCellName[channel.cdObject, Rope.Cat[context.name, "Channel", channel.name]]; next include the cells containing the wires to get from the channel edges to cells Construction Utilities construct a shell for the chanSide of a channel and insert global routing info for this channel PROC[co: Component] RETURNS[quit: BOOL _ FALSE]; add the external pins accross the channel for boundry channels PROC [key: Key, val: Val] RETURNS [quit: BOOL _ FALSE]; add the pins from the nets PROC[i: Intersection] RETURNS[quit: BOOL _ FALSE]; add the pins from the channel intersections [name: Rope.ROPE, min, max: INT, side: DABasics.Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE] construct the end object of a channel PROC [key: Key, val: Val] RETURNS [quit: BOOLEAN _ FALSE]; translate a side to an orientation include a pin in a shell; also extend a wire if necessary Routing Area Manipulation apply EachEdgeEntity to entities of ob HACK, This needs to enumerate routing class entries directly PROC [wire: Wire, min, max: INT, side: Side, layer: CD.Layer] RETURNS [quit: BOOL _ FALSE]; Detail Route Entry add a pin to the appropriate segment in net add a channel end connection find an existing segment by name; if none, make a NEW one Router Callbacks PROC [channelData: REF, eachNet: EachChannelNetProc]; PROC [channelData, netData: REF, eachPin: EachChannelPinProc]; Κ\– "cedar" style˜šœ™JšœB™BJšœ*Οkœ™.Jšœ)™)—J˜š ˜ Jšœoœ œœN˜ΧJ˜—šΟnœœœ˜!Jšœœ_œ œF˜ΓJšœ ˜—headšΟl ™ Jšœ œœ<˜Ršœœœ˜Jšœœ Οc˜:Jšœ œœ œ (˜MJ˜—Jšœ œœ ˜!šœ œœ˜Jšœœœ˜Jšœ œ˜Jšœœœ $˜GJšœœœ $˜FJšœ œœœ $˜FJ˜—Jšœœœ:˜Lšœ œœ˜Jšœœ ˜Jšœœ ˜Jšœœ ™Jšœœ˜Jšœ˜——šœ ™ š ž œœœ œ œ œ ˜Pšœ™J™—šž œ!˜,š œ œœœœ™LJšœ™J˜—šžœ˜ Jšœœœ˜6—J˜šœ/œ˜PJšœ˜Jšœ˜—šœœ˜˜tJšœ?˜C—Jšœ&˜+J˜—šžœ!˜/š œ œœœœ™LJšœE™EJ˜—Jšœ™šœœœ˜ Jšœ œ ˜Jšœ_˜_JšœY™YJšœZ˜Z—J˜JšœR™Ršœœ˜šœ˜JšœœE˜OJšœX˜X—šœ˜Jšœ œ"œ@˜qJšœ]˜]—šœ˜Jšœ œ6œ,˜qJšœ]˜]—šœ˜JšœœE˜OJšœX˜X—šœ˜ šœœ œ œ9˜uJšœ;˜?—š œ œ œ œ+œ+˜—Jšœœ@˜^—JšœW˜WJšœ^˜^———Jšœœ˜/Jšœ6œ ˜DJšœœ$˜.Jšœ9œ#˜aJ˜IJšœ%˜%JšœL˜Lš œœœBœœ˜fšœœœ˜%šœœ˜)JšœO˜O—JšœGœ˜O——Jšœ˜Jšœ œœ2˜A——šŸ™š žœœ œbœœ œ(˜ΐJšœ_™_J˜šžœœ˜-Jšœœœœ™0Jšœ?™?šœœœ˜Jšœ$œ ˜3š œœœ7œœ˜WJšœ0œœ˜<š œœœ5œœ˜Wšœœ œœ ˜Wšœ˜šœ˜$Jšœ7˜7—šœœ˜Jšœœ ˜š œœœ œœ˜DJšœ˜Jšœœœ˜>Jšœp˜pJšœ˜ ——J˜Jšœ˜—J˜J˜J˜J˜—…—B4Yώ