DIRECTORY Atom USING [GetPName], Basics USING [CompareINT, Comparison], CD USING [backgroundLayer, commentLayer, CreateDrawRef, DrawProc, DrawRectProc, DrawRef, errorLayer, Instance, InstanceList, InstanceRep, Layer, Number, Object, Orientation, outlineLayer, Position, Rect, selectionLayer, shadeLayer], CDInstances USING [NewInst], CDBasics USING [Center, NonEmpty, RectAt, SubPoints], CDOps USING [LayerName], CDOrient USING [MapPoint, MapRect, original], CDProperties USING [DCopyProps, GetProp, GetInstanceProp, GetListProp, PutInstanceProp], Convert USING [RopeFromInt], PropertyLists USING [GetProp, PropList, PutProp], RedBlackTree USING [Compare, Create, DuplicateKey, GetKey, Insert, LookupNextLarger, LookupSmallest, Table], RefTab USING [Create, EachPairAction, Fetch, GetSize, Insert, Pairs, Ref, Store], Rope USING [Cat, FromRefText, Equal, ROPE], SX USING [AdjustmentMode, AreaPerimRec, AttachedNode, BoxMapProc, Circuit, CircuitNode, Constraint, ConversionProc, LogicalCell, MapRec, MergeRec, MergeRecList, NodeLinkage, SignalName, SpinifexLayerIndex], SXAccess USING [stopFlag, sxTech], SXAccessInternal USING [GetLogicalCell, PutError], SXAtoms USING [export, InstanceName, Rect, SignalName, spinifex], SXQuadTree USING [AreaSplit, Store, QuadTree, QuadTreeRoot, Rectangle, RectDelta]; SXImpl: CEDAR PROGRAM IMPORTS Atom, Basics, CD, CDInstances, CDBasics, CDOps, CDOrient, CDProperties, Convert, PropertyLists, RedBlackTree, RefTab, Rope, SXAccess, SXAccessInternal, SXAtoms, SXQuadTree EXPORTS SX = BEGIN AreaPerimRec: TYPE = SX.AreaPerimRec; AttachedNode: TYPE = SX.AttachedNode; Circuit: TYPE = SX.Circuit; Constraint: TYPE = SX.Constraint; LogicalCell: TYPE = SX.LogicalCell; MapRec: TYPE = SX.MapRec; MergeRec: TYPE = SX.MergeRec; NodeLinkage: TYPE = SX.NodeLinkage; SignalName: TYPE = SX.SignalName; SpinifexLayerIndex: TYPE = SX.SpinifexLayerIndex; IllegalConstruct: PUBLIC ERROR [rect: CD.Rect, reason: Rope.ROPE] = CODE; IllegalLayer: PUBLIC ERROR [rect: CD.Rect, lev: CD.Layer] = CODE; specialLayers: CD.Layer = MAX [CD.shadeLayer, CD.errorLayer, CD.backgroundLayer, CD.outlineLayer, CD.selectionLayer, CD.commentLayer]; repetitionInstanceNamePrefix: Rope.ROPE _ ""; repetitionInstanceNamePostfix: Rope.ROPE _ ""; IsSimpleRect: PROC [ob: CD.Object] RETURNS [BOOL] = INLINE BEGIN RETURN [ob.class.objectType = SXAtoms.Rect] END; -- IsSimpleRect TranslateChild: CD.DrawProc = BEGIN sxThrough: REF LogicalCell = NARROW [pr.devicePrivate]; cir: REF Circuit = sxThrough.circuit; sx: REF LogicalCell; IF IsSimpleRect[inst.ob] THEN BEGIN IF inst.ob.layer > specialLayers THEN BEGIN box: CD.Rect = CDOrient.MapRect [ itemInCell: CDBasics.RectAt[pos: [0, 0], size: inst.ob.size], cellSize: inst.ob.size, cellInstOrient: orient, cellInstPos: pos]; node: REF SX.CircuitNode = AddRect [ cir: cir, lev: inst.ob.layer, dim: box ! IllegalLayer => {SXAccessInternal.PutError[sxThrough.cellObj, rect, Rope.Cat["Material on layer ", CDOps.LayerName[lev], " may not appear as an isolated rectangle.\n"]]; GOTO BadRect}]; IF node # NIL THEN CopyNodeProperties [cir, node, inst.properties]; EXITS BadRect => NULL END; RETURN END; -- end simple rectangle. sx _ SXAccessInternal.GetLogicalCell [inst.ob]; IF (sx # NIL) AND (sx.analysisState = useCircuit) THEN BEGIN subcircuit: REF Circuit = sx.circuit; newAppl: CD.Instance = NEW [CD.InstanceRep _ [ ob: inst.ob, location: pos, orientation: orient, properties: CDProperties.DCopyProps [propList: inst.properties]]]; numLayers: SpinifexLayerIndex = SXAccess.sxTech.numSpinifexLayers; FOR i: SpinifexLayerIndex IN [0..numLayers) DO IF subcircuit.spinifexLayers[i].private = NIL THEN LOOP; -- no geom. IF (cir.subcircuits = NIL) OR (cir.subcircuits.first # newAppl) THEN BEGIN cir.subcircuits _ CONS [newAppl, cir.subcircuits]; END; [] _ AddBox [ cir: cir, inst: newAppl, pos: pos, orient: orient, spinifexLayer: i, dim: subcircuit.spinifexLayers[i].size, value: newAppl]; ENDLOOP; cir.linkageCount.inChildren _ cir.linkageCount.inChildren + subcircuit.linkageCount.inSelf + subcircuit.linkageCount.inChildren; END -- (sx # NIL) AND (sx.analysisState = useCircuit) ELSE BEGIN -- expand special objects (transistors) and conditional objects convProc: REF ANY = CDProperties.GetProp [from: inst.ob.class.properties, prop: SXAtoms.spinifex]; IF convProc #NIL THEN NARROW [convProc, REF SX.ConversionProc]^[inst, pos, orient, cir ! IllegalConstruct => { SXAccessInternal.PutError [sxThrough.cellObj, CDOrient.MapRect [itemInCell: rect, cellSize: inst.ob.size, cellInstOrient: orient, cellInstPos: pos], reason]; CONTINUE}] ELSE inst.ob.class.drawMe[inst, pos, orient, pr] -- analysis state: notYetDone END END; -- TranslateChild TranslateRect: CD.DrawRectProc = BEGIN IF NOT CDBasics.NonEmpty[r] THEN RETURN; IF l # CD.errorLayer THEN BEGIN sxc: REF LogicalCell = NARROW [pr.devicePrivate]; [] _ AddRect [cir: sxc.circuit, lev: l, dim: r ! IllegalLayer => { SXAccessInternal.PutError [sxc.cellObj, rect, Rope.Cat ["Rectangle on layer ", CDOps.LayerName [lev], " cannot be analyzed in this context.\n"]]; CONTINUE}] END END; -- TranslateRect SKey: TYPE = REF CD.Position; -- RedBlackTree.Key SRec: TYPE = RECORD [key: SKey, instance: CD.Instance]; SData: TYPE = REF SRec; -- RedBlackTree.UserData InstKey: RedBlackTree.GetKey = BEGIN RETURN [NARROW [data, SData].key] END; -- InstKey InstComp: RedBlackTree.Compare = BEGIN c: Basics.Comparison; sk: SKey = NARROW [k]; d: SData = NARROW [data]; c _ Basics.CompareINT [sk.x, d.key.x]; IF c = equal THEN c _ Basics.CompareINT [sk.y, d.key.y]; RETURN [c] END; -- InstComp SInsert: PROCEDURE [s: RedBlackTree.Table, inst: CD.Instance] RETURNS [duplicate: BOOL] = BEGIN ENABLE RedBlackTree.DuplicateKey => GOTO dupl; sk: SKey _ NEW [CD.Position _ inst.location]; data: SData _ NEW [SRec _ [key: sk, instance: inst]]; RedBlackTree.Insert [s, data, sk]; RETURN [FALSE]; EXITS dupl => RETURN [TRUE] END; -- SInsert TranslateGeometry: PUBLIC PROC [cell: REF LogicalCell] = BEGIN cir: REF Circuit = cell.circuit; dr: CD.DrawRef = CD.CreateDrawRef [[]]; fakeAppl: CD.Instance = CDInstances.NewInst [cell.cellObj]; fakeAppl.location _ [0, 0]; dr.drawRect _ TranslateRect; dr.drawChild _ TranslateChild; dr.devicePrivate _ cell; dr.stopFlag _ SXAccess.stopFlag; cell.cellObj.class.drawMe [fakeAppl, [0, 0], CDOrient.original, dr]; FOR link: LIST OF REF NodeLinkage _ cir.linkages, link.rest WHILE link#NIL DO cir.linkageCount.inSelf _ cir.linkageCount.inSelf + 1 -- Number of devices. ENDLOOP; IF cir.subcircuits#NIL AND cir.subcircuits.rest#NIL AND cir.subcircuits.rest.rest#NIL THEN BEGIN FOR sub: CD.InstanceList _ cir.subcircuits, sub.rest WHILE sub#NIL DO IF cir.subcircuits.first.orientation # sub.first.orientation THEN EXIT; IF cir.subcircuits.first.ob # sub.first.ob THEN EXIT; REPEAT FINISHED => BEGIN sTab: RedBlackTree.Table = RedBlackTree.Create [InstKey, InstComp]; lastApp, currApp: SData; delta: CD.Position; FOR sub: CD.InstanceList _ cir.subcircuits, sub.rest WHILE sub#NIL DO IF SInsert [sTab, sub.first] THEN GOTO EqualAppls; ENDLOOP; lastApp _ NARROW [RedBlackTree.LookupSmallest[sTab]]; currApp _ NARROW [RedBlackTree.LookupNextLarger[sTab,lastApp.key]]; delta _ CDBasics.SubPoints[currApp.instance.location, lastApp.instance.location]; lastApp _ currApp; WHILE (currApp _ NARROW[RedBlackTree.LookupNextLarger[sTab, lastApp.key]]) # NIL DO IF delta # CDBasics.SubPoints[currApp.instance.location, lastApp.instance.location] THEN EXIT; lastApp _ currApp; REPEAT FINISHED => BEGIN index: INT _ 1; FOR a: SData _ NARROW[RedBlackTree.LookupSmallest[sTab]], NARROW[RedBlackTree.LookupNextLarger[sTab, a.key]] WHILE a # NIL DO IF CDProperties.GetInstanceProp[from: a.instance, prop: SXAtoms.InstanceName]=NIL THEN BEGIN instanceName: Rope.ROPE _ repetitionInstanceNamePrefix.Cat[Convert.RopeFromInt[index], repetitionInstanceNamePostfix]; CDProperties.PutInstanceProp [a.instance, SXAtoms.InstanceName, instanceName]; END; index _ index+1 ENDLOOP END; ENDLOOP; EXITS EqualAppls => NULL END ENDLOOP; END END; -- TranslateGeometry AddRect: PUBLIC PROC [ cir: REF Circuit, lev: CD.Layer, dim: CD.Rect, inst: CD.Instance _ NIL, pos: CD.Position _ [0, 0], orient: CD.Orientation _ CDOrient.original, value: REF SX.CircuitNode _ NIL] RETURNS [cirNode: REF SX.CircuitNode _ NIL] = BEGIN UniformBloat: PROC [d: INT] RETURNS [SXQuadTree.RectDelta] = INLINE BEGIN RETURN [[d,d,d,d]] END; -- UniformBloat IF SXAccess.sxTech.illegalLayer[lev] THEN ERROR IllegalLayer [(IF inst=NIL THEN dim ELSE CDOrient.MapRect [itemInCell: dim, cellSize: inst.ob.size, cellInstOrient: orient, cellInstPos: pos]), lev]; IF CDBasics.NonEmpty[dim] THEN -- Silently ignore empty boxes FOR map: LIST OF MapRec _ SXAccess.sxTech.cdLayerMapping[lev], map.rest WHILE map#NIL DO cn: REF SX.CircuitNode; WITH map.first.value SELECT FROM mappingFunc: REF SX.BoxMapProc => cn _ mappingFunc^[ cir: cir, dim: dim, inst: inst, pos: pos, orient: orient, node: IF cirNode#NIL THEN cirNode ELSE value]; ENDCASE => cn _ AddBox [ cir: cir, spinifexLayer: map.first.spinifexLayer, dim: dim, inst: inst, pos: pos, orient: orient, interestBloat: UniformBloat[map.first.bloatFactor], value: (IF map.first.value#NIL THEN map.first.value ELSE value)]; IF cn#NIL THEN cirNode _ cn ENDLOOP; END; -- AddRect AddEmptyBox: ERROR = CODE; AddBox: PUBLIC PROC [ cir: REF Circuit, spinifexLayer: SpinifexLayerIndex, dim: CD.Rect, inst: CD.Instance _ NIL, pos: CD.Position _ [0, 0], orient: CD.Orientation _ CDOrient.original, interestBloat: SXQuadTree.RectDelta _ [0,0,0,0], value: REF ANY _ NIL] RETURNS [cirNode: REF SX.CircuitNode _ NIL] = BEGIN A: PROC [r: CD.Rect] RETURNS [area: INT] = INLINE BEGIN area _ (r.x2-r.x1) * (r.y2-r.y1) END; -- A P: PROC [r: CD.Rect] RETURNS [perim: INT] = INLINE BEGIN perim _ ((r.x2-r.x1) + (r.y2-r.y1)) * 2 END; -- P IF CDBasics.NonEmpty [dim] THEN BEGIN bloatedDim, bloatedMapped, mappedDim: CD.Rect; newRect: REF SXQuadTree.Rectangle; bloatedDim _ [x1: dim.x1-interestBloat.dx1, y1: dim.y1-interestBloat.dy1, x2: dim.x2+interestBloat.dx2, y2: dim.y2+interestBloat.dy2]; IF inst = NIL THEN {bloatedMapped _ bloatedDim; mappedDim _ dim} ELSE BEGIN bloatedMapped _ CDOrient.MapRect [itemInCell: bloatedDim, cellSize: inst.ob.size, cellInstOrient: orient, cellInstPos: pos]; mappedDim _ CDOrient.MapRect [itemInCell: dim, cellSize: inst.ob.size, cellInstOrient: orient, cellInstPos: pos] END; -- appl # NIL WITH value SELECT FROM cn: REF SX.CircuitNode => BEGIN cirNode _ cn; AdjustNode [cirNode, spinifexLayer, A[dim], P[dim]] END; ENDCASE => IF value = NIL THEN BEGIN value _ cirNode _ NEW [SX.CircuitNode _ [dim: LIST[[layer: spinifexLayer, area: A[dim], perim: P[dim]]], loc: [CDBasics.Center[mappedDim], spinifexLayer]]]; AddCircuitNode [cir, cirNode] END; newRect _ NEW [SXQuadTree.Rectangle _ [interestBound: bloatedMapped, dimDecr: [ dx1: mappedDim.x1-bloatedMapped.x1, dy1: mappedDim.y1-bloatedMapped.y1, dx2: bloatedMapped.x2-mappedDim.x2, dy2: bloatedMapped.y2-mappedDim.y2], nodeInformation: value]]; cir.spinifexLayers[spinifexLayer] _ SXQuadTree.Store [quadTree: cir.spinifexLayers[spinifexLayer], rect: newRect] END -- if dim not empty ELSE ERROR AddEmptyBox END; -- AddBox MergeNode: PUBLIC PROC [circuit: REF Circuit, to, from: REF SX.CircuitNode] = BEGIN IF from.superceded#NIL THEN from _ LookupNode [from]; IF to.superceded#NIL THEN to _ LookupNode [to]; IF from=to THEN RETURN; FOR fromList: LIST OF AreaPerimRec _ from.dim, fromList.rest WHILE fromList#NIL DO AdjustNode [to, fromList.first.layer, fromList.first.area, fromList.first.perim] ENDLOOP; CopyNodeProperties [circuit, to, from.properties]; from.properties _ NIL; from.superceded _ to END; -- MergeNode CopyNodeProperties: PROC [ circuit: REF Circuit, node: REF SX.CircuitNode, properties: PropertyLists.PropList, qual: LIST OF CD.Instance _ NIL] = BEGIN CopySignal: PROC [sig: REF ANY, depth: INTEGER] RETURNS [REF SignalName] = BEGIN IF sig=NIL THEN RETURN [NIL]; WITH sig SELECT FROM r: Rope.ROPE => RETURN [NEW[SignalName _ [depth: depth, name: r]]]; t: REF TEXT => RETURN [NEW[SignalName _ [depth: depth, name: Rope.FromRefText[t]]]]; a: ATOM => RETURN [NEW[SignalName _ [depth: depth, name: Atom.GetPName[a]]]]; s: REF SignalName => BEGIN copyHead, copyTail: REF SignalName; copyHead _ NEW [SignalName _ [depth: s.depth+depth, name: s.name, makePort: s.makePort]]; copyTail _ copyHead; FOR source: REF SignalName _ s.alias, source.alias WHILE source#NIL DO copyTail.alias _ NEW [SignalName _ [depth: source.depth+depth, name: source.name]]; copyTail _ copyTail.alias; ENDLOOP; RETURN [copyHead]; END; ENDCASE => ERROR; END; -- CopySignal MergeAliases: PROC [to, from: REF SignalName] = BEGIN WHILE from # NIL DO FOR existingAlias: REF SignalName _ to, existingAlias.alias WHILE existingAlias#NIL DO IF from.name.Equal[existingAlias.name] THEN BEGIN IF from.depth < existingAlias.depth THEN existingAlias.depth _ from.depth; from _ from.alias; EXIT END REPEAT FINISHED => BEGIN tmp: REF SignalName = from; from _ from.alias; tmp.alias _ to.alias; to.alias _ tmp END ENDLOOP ENDLOOP END; -- MergeAliases fromSigProp: REF ANY ~ PropertyLists.GetProp [properties, SXAtoms.SignalName]; exportProp: REF ANY = PropertyLists.GetProp [properties, SXAtoms.export]; toSigProp: REF ANY ~ PropertyLists.GetProp [node.properties, SXAtoms.SignalName]; IF SXAccess.sxTech.combineNodeProperties#NIL AND properties#NIL THEN node.properties _ SXAccess.sxTech.combineNodeProperties[circuit, node.properties, properties, qual]; BEGIN fromSignal: REF SignalName; toSignal: REF SignalName; depth: INTEGER _ 0; FOR q: LIST OF CD.Instance _ qual, q.rest WHILE q#NIL DO depth _ depth.SUCC ENDLOOP; fromSignal _ CopySignal [fromSigProp, depth]; IF fromSignal # NIL THEN fromSignal.makePort _ (fromSignal.makePort OR exportProp=$TRUE); toSignal _ NARROW[toSigProp]; IF (toSignal # NIL) AND (fromSignal # NIL) THEN BEGIN IF toSignal.depth > fromSignal.depth THEN BEGIN node.properties _ PropertyLists.PutProp [node.properties, SXAtoms.SignalName, fromSignal]; MergeAliases [fromSignal, toSignal] END ELSE MergeAliases [toSignal, fromSignal] END ELSE IF (fromSignal # NIL) THEN node.properties _ PropertyLists.PutProp[node.properties, SXAtoms.SignalName, fromSignal] END; node.properties _ PropertyLists.PutProp [node.properties, UnMerged]; END; -- CopyNodeProperties AdjustNode: PUBLIC PROC [ node: REF SX.CircuitNode, layer: SpinifexLayerIndex, area: INT, perim: INT, mode: SX.AdjustmentMode _ relative] = BEGIN apRec: LIST OF AreaPerimRec _ node.dim; IF node.superceded # NIL THEN node _ LookupNode[node]; -- added Ch. J. Jan 30, 1985 WHILE apRec # NIL DO IF apRec.first.layer = layer THEN EXIT; apRec _ apRec.rest; REPEAT FINISHED => BEGIN node.dim _ CONS [AreaPerimRec [layer: layer, area: area, perim: perim], node.dim]; RETURN END ENDLOOP; SELECT mode FROM relative => BEGIN apRec.first.area _ apRec.first.area + area; apRec.first.perim _ apRec.first.perim + perim END; absolute => BEGIN apRec.first.area _ area; apRec.first.perim _ perim END; ENDCASE END; -- AdjustNode NormalizeCircuit: PUBLIC PROC [cir: REF Circuit] = BEGIN CompressCircuitNodeList: PROC = BEGIN mySpecialCN: REF SX.CircuitNode = NEW [SX.CircuitNode]; indirectList: LIST OF REF SX.CircuitNode ~ CONS [mySpecialCN, cir.nodes]; cn: LIST OF REF SX.CircuitNode; IF cir.nodes = NIL THEN RETURN; mySpecialCN.superceded _ NIL; mySpecialCN.properties _ NIL; cn _ indirectList; WHILE cn.rest # NIL DO node: REF SX.CircuitNode ~ cn.rest.first; IF (node.superceded # NIL) OR (CDProperties.GetListProp [propList: node.properties, prop: UnMerged] # NIL) THEN cn.rest _ cn.rest.rest ELSE cn _ cn.rest ENDLOOP; IF cir.nodes = NIL THEN ERROR; -- Impossible! We would have returned at start if NIL list, and at least some of the nodes must be non-superceded. cn _ indirectList; WHILE cn.rest # NIL DO IF cn.rest.first.superceded = mySpecialCN THEN cn.rest _ cn.rest.rest ELSE {cn _ cn.rest; cn.first.superceded _ mySpecialCN} ENDLOOP; cn _ cir.nodes _ indirectList.rest; WHILE cn # NIL DO cn.first.superceded _ NIL; cn _ cn.rest ENDLOOP; END; -- CompressCircuitNodeList CompressLinkages: PROC = BEGIN FOR linkages: LIST OF REF NodeLinkage _ cir.linkages, linkages.rest WHILE linkages#NIL DO FOR attached: LIST OF REF AttachedNode _ linkages.first.nodes, attached.rest WHILE attached#NIL DO IF attached.first.node#NIL THEN attached.first.node _ LookupNode[attached.first.node]; ENDLOOP ENDLOOP END; -- CompressLinkages CompressMergeDirectoryEntries: PROC = BEGIN IF cir.mergeDirectory # NIL THEN BEGIN dummy: SX.MergeRecList = CONS [[], NIL]; CountUnMerged: RefTab.EachPairAction = BEGIN IF CDProperties.GetListProp [NARROW[key, REF SX.CircuitNode].properties, UnMerged] # NIL THEN oldSize _ oldSize.PRED; RETURN [FALSE] END; -- CountUnMerged CompressMerge: RefTab.EachPairAction = BEGIN mergeList: SX.MergeRecList; IF CDProperties.GetListProp[NARROW[key, REF SX.CircuitNode].properties, UnMerged] = NIL THEN BEGIN dummy.rest _ NARROW [val]; mergeList _ dummy; WHILE mergeList.rest # NIL DO mergeList.rest.first.becomes _ LookupNode [mergeList.rest.first.becomes]; IF CDProperties.GetListProp [mergeList.rest.first.becomes.properties, UnMerged] # NIL THEN mergeList.rest _ mergeList.rest.rest ELSE mergeList _ mergeList.rest ENDLOOP; IF dummy.rest # NIL THEN IF NOT newTab.Insert [key, dummy.rest] THEN ERROR END; RETURN [FALSE] END; -- CompressMerge oldSize: INT _ RefTab.GetSize [cir.mergeDirectory]; newTab: RefTab.Ref; [] _ RefTab.Pairs [cir.mergeDirectory, CountUnMerged]; IF oldSize <= 0 THEN {cir.mergeDirectory _ NIL; RETURN}; newTab _ RefTab.Create [((oldSize/3)*4)+1]; [] _ RefTab.Pairs [cir.mergeDirectory, CompressMerge]; cir.mergeDirectory _ newTab; IF (RefTab.GetSize [newTab] = 0) THEN cir.mergeDirectory _ NIL END -- if cir.mergeDirectory # NIL END; -- CompressMergeDirectoryEntries CompressBoxPaths: PROC [qt: REF SXQuadTree.QuadTree] = BEGIN FOR boxes: LIST OF REF SXQuadTree.Rectangle _ qt.boxes, boxes.rest WHILE boxes#NIL DO WITH boxes.first.nodeInformation SELECT FROM inst: CD.Instance => NULL; cn: REF SX.CircuitNode => boxes.first.nodeInformation _ LookupNode [cn]; cc: REF Constraint => NULL; ENDCASE => ERROR; ENDLOOP; FOR quad: SXQuadTree.AreaSplit IN SXQuadTree.AreaSplit DO IF qt.subTrees[quad] # NIL THEN CompressBoxPaths [qt.subTrees[quad]] ENDLOOP END; -- CompressBoxPaths CompressCircuitNodeList []; CompressLinkages []; CompressMergeDirectoryEntries []; FOR layer: SpinifexLayerIndex IN [0..SXAccess.sxTech.numSpinifexLayers) DO IF (cir.spinifexLayers[layer].geometry # NIL) THEN CompressBoxPaths [cir.spinifexLayers[layer].geometry] ENDLOOP END; -- Normalise Circuit LookupNode: PUBLIC PROC [node: REF SX.CircuitNode] RETURNS [REF SX.CircuitNode] = BEGIN IF node.superceded#NIL THEN { n1: REF SX.CircuitNode _ node; WHILE node.superceded#NIL DO node _ node.superceded ENDLOOP; WHILE n1.superceded#NIL DO n2: REF SX.CircuitNode _ n1.superceded; n1.superceded _ node; n1 _ n2; ENDLOOP; }; RETURN [node] END; -- LookupNode LookupMergeDirectory: PROC [ dir: RefTab.Ref, cn: REF SX.CircuitNode, qual: LIST OF CD.Instance] RETURNS [merge: SX.MergeRecList, newQual: LIST OF CD.Instance] = BEGIN found: BOOL; val: REF ANY; [found, val] _ RefTab.Fetch [dir, cn]; IF found THEN BEGIN FOR mergeList: SX.MergeRecList _ NARROW[val], mergeList.rest WHILE mergeList#NIL DO qApplChain: LIST OF CD.Instance _ qual; FOR applChain: LIST OF CD.Instance _ mergeList.first.applChain, applChain.rest DO IF applChain = NIL THEN RETURN [mergeList, qApplChain]; IF (qApplChain = NIL) OR (qApplChain.first # applChain.first) THEN EXIT; qApplChain _ qApplChain.rest ENDLOOP ENDLOOP END; -- found RETURN [NIL, qual] END; -- LookupMergeDirectory UnMerged: ATOM = $SXPrivateUnMerged; FindRootNode: PUBLIC PROC [ circuit: REF Circuit, subcircuitNode: REF SX.CircuitNode, qualifier: LIST OF CD.Instance, insertIfNotInCircuit: BOOL _ FALSE] RETURNS [node: REF SX.CircuitNode, rootQualifier: LIST OF CD.Instance] = BEGIN AddCircuitNodeMerge: PROC [sourceNode: REF SX.CircuitNode, qual: LIST OF CD.Instance] RETURNS [newNode: REF SX.CircuitNode] = BEGIN found: BOOL; mergeList: SX.MergeRecList _ NIL; IF circuit.mergeDirectory = NIL THEN BEGIN IF insertIfNotInCircuit THEN BEGIN circuit.mergeDirectory _ RefTab.Create [521]; -- A prime. found _ FALSE END END ELSE BEGIN -- circuit.mergeDirectory # NIL val: REF ANY; [found, val] _ RefTab.Fetch [circuit.mergeDirectory, sourceNode]; IF found THEN BEGIN mergeList _ NARROW [val]; FOR m: SX.MergeRecList _ mergeList, m.rest WHILE m#NIL DO q: LIST OF CD.Instance _ qual; FOR applChain: LIST OF CD.Instance _ m.first.applChain, applChain.rest DO IF (applChain = NIL) OR (q = NIL) THEN IF (applChain = NIL) AND (q = NIL) THEN RETURN [newNode _ m.first.becomes] ELSE EXIT ELSE IF (applChain.first # q.first) THEN EXIT; q _ q.rest ENDLOOP ENDLOOP END -- found END; -- circuit.mergeDirectory # NIL IF ~insertIfNotInCircuit THEN RETURN [newNode _ NIL]; newNode _ NEW [SX.CircuitNode _ [NIL, NIL, sourceNode.loc, NIL]]; FOR qa: LIST OF CD.Instance _ qual, qa.rest WHILE qa # NIL DO inst: CD.Instance = qa.first; newNode.loc.xy _ CDOrient.MapPoint [ pointInCell: newNode.loc.xy, cellSize: inst.ob.size, cellInstOrient: inst.orientation, cellInstPos: inst.location] ENDLOOP; CopyNodeProperties [circuit, newNode, sourceNode.properties, qual]; newNode.properties _ PropertyLists.PutProp [newNode.properties, UnMerged, UnMerged]; mergeList _ CONS [[applChain: qual, becomes: newNode], mergeList]; IF RefTab.Store[circuit.mergeDirectory, sourceNode, mergeList] = found THEN ERROR; AddCircuitNode [circuit, newNode] END; -- AddCircuitNodeMerge IF qualifier=NIL THEN RETURN [subcircuitNode, NIL]; FOR q: LIST OF CD.Instance _ qualifier.rest, q.rest WHILE q#NIL DO subcircuit: REF Circuit _ SXAccessInternal.GetLogicalCell[q.first.ob].circuit; IF subcircuit.mergeDirectory # NIL THEN BEGIN tmp: SX.MergeRecList; oldQual: LIST OF CD.Instance _ qualifier; [merge: tmp, newQual: qualifier] _ LookupMergeDirectory [subcircuit.mergeDirectory, subcircuitNode, qualifier]; IF tmp # NIL THEN BEGIN IF oldQual = NIL THEN ERROR; IF (qualifier # NIL) AND (qualifier.first.ob # q.first.ob) THEN ERROR; subcircuitNode _ tmp.first.becomes END END ENDLOOP; IF (node _ AddCircuitNodeMerge[sourceNode: subcircuitNode, qual: qualifier])=NIL THEN {node _ subcircuitNode; rootQualifier _ qualifier} ELSE rootQualifier _ NIL END; -- FindRootNode AddCircuitNode: PROC [cir: REF Circuit, cn: REF SX.CircuitNode] = INLINE BEGIN cir.nodes _ CONS[cn, cir.nodes] END; -- AddCircuitNode CreateLinkage: PUBLIC PROC [cir: REF Circuit, source: CD.Instance, length, width: CD.Number] RETURNS [REF NodeLinkage] = BEGIN cir.linkages _ CONS [NEW[NodeLinkage _ [source: source, l: length, w: width]], cir.linkages]; RETURN [cir.linkages.first]; END; -- CreateLinkage LinkageAttach: PUBLIC PROC [ link: REF NodeLinkage, attachType: ATOM, node: REF SX.CircuitNode _ NIL] = BEGIN link.nodes _ CONS [NEW [AttachedNode _ [attachType, node]], link.nodes] END; -- LinkageAttach END. %øSXImpl.mesa Creates a representation of the layout suitable for hierarchical analysis. Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Written by Mark Shand, September 12, 1983 11:40 pm Last Edited by: Shand, March 13, 1985 2:57:23 pm PST Last Edited by: Spreitzer, January 15, 1985 2:09:09 pm PST Last Edited by: Jacobi, April 8, 1985 5:07:49 pm PST Last edited by: gbb June 9, 1986 4:55:58 pm PDT TYPES from Interface. Implementation. Warning: CD.undefLayer should be included in this list. However, its value is zero and the compiler erroneously issues a warning, which prevents MakeDo from binding the package. Hence be alert when the values of these layers change. [inst: Instance, pos: Position, orient: Orientation, pr: REF DrawInformation] This object is a logical subcircuit. Preserving properties added by Spreitzer November 23, 1984 8:06:02 pm PST to get cell instance names; why didn't Shand have this here? It is the 1st non-empty geom-layer. object has special layout analysis code. PROC [r: DesignRect, l: Layer, pr: DrawRef]  Stuff for RedBlackTree (used in TranslateGeometry) Raises DuplicateKey if appl with same location already added.  Translates ChipNDale layers into Spinifex layers (e.g. only explicit contacts [which must be correct and hence are not checked] are allowed. Also a node number is assigned, which is used to identify the nodes. The translation is performed using the drawRect mechanism of ChipNDale. The ChipNDale design is translated into a quad-tree. It is not possible to use a tesselation because of overlaps. The quad-tree contains only cells already analysed. There are three types of rectangles in the Quad-Tree: 1. constraints; 2. material; 3. bounding boxes for subcell rectangles. Translation of the geometry. There are at least three subcircuits. They all have the same orientation, and are all are of the same type. They are sorted. Hence they are logically a repetition. Provide default instance names for repetition elements. Added by Spreitzer November 24, 1984 12:08:57 pm PST. Genralized by Shand, March 13, 1985 2:20:08 pm PST. In this context `appl = NIL' means that transformation has not to be applied. calling LookupNode and equal test introduced by Ch. J. Jan 30, 1985 Combine areas and perimeters. Combine properties. Creates a SignalName RECORD with appropriate depth from a ChipNDale Signal name (ROPE) or an existing SignalName RECORD. Insert in list. Copy Technology Dependent PropertyLists Copy SignalName property Delete UnMerged property. Search for a AreaPerimRec on this layer. This is an important step ... It results in the deallocation of superceded SX.CircuitNodes, and the removal of duplicates, superceded and UnMerged nodes from cir.nodes, and Discard superceded & UnMerged nodes. Discard duplicated nodes. Normally no SX.CircuitNodes in subcells will be unmerged at this point, however this is not true in the case of Normalization following SpinifexOutput. SpinifexOutput introduces extra intermediate nodes, such merge directory entries should be deleted. Main of CompressMergeDirectoryEntries: Ensure size is not a power of 2 (seems like a good idea). Main of Normalize Circuit Takes the node and returns the most recent version of it. Side effect: cleans all intermediate version to point to the most recent one. Mark called this "(partial) Galler-Fisher path compression", had a recursive solution which was more elegant but slower. LookupNode: PROC [l: REF SX.CircuitNode] RETURNS [REF SX.CircuitNode] ~ { --Implements (partial) Galler-Fisher path compression. RETURN [IF l.superceded#NIL THEN (l.superceded _ LookupNode[l.superceded]) ELSE l] }; --find the actual node --now node is the result --but as side effect, set all superceded fields to point to the result node Search mergeList for qual Compare mergeList.first.applChain to qual This is a renaming of cn, qual gets to be what's left, and we continue looking for higher intervening renamings. UnMerged: Private property Key to flag unmerged nodes for removal during circuit NormalizeCircuit. This key is deleted by CopyNodeProperties, which is called when ever nodes are merged. Make it big, it will get cleaned up in NormalizeCircuit anyway. Check we don't already know about it. FindRootNode Check each intermediate cell to see if subcircuitNode is renamed, return a SX.CircuitNode based on the uppermost found. Note qualifier always has at least one CD.Instance. Add to mergeDirectory. For use in cells further up in hierarchy. Edited on January 4, 1985 6:23:42 pm PST, by Jacobi NewInstanceRepI does offset position; Spinifex does not want that Edited on January 22, 1985 4:00:55 pm PST, by Beretta Unneseted SortBoxes; added comments. Edited on January 30, 1985 8:25:40 pm PST, by Jacobi MergeNode; calling LookupNode and equal test introduced AdjustNode; calling LookupNode introduced Edited on February 19, 1985 3:58:48 pm PST, by Shand Fixed serious bug in ansiotropic interest bloats when box is transformed changes to: AddBox to transform box dimension and interest bloat., TranslateGeometry, SortGeometry Edited on February 21, 1985 6:19:44 pm PST, by Beretta changes to: DIRECTORY, PaintErrorRect, TranslateChild, TranslateRect Edited on March 4, 1985 3:24:49 pm PST, by Shand Spinifex DRC errors now reported through CDErrors module. Changed role for errorContext field in LogicalCell RECORD. changes to: DIRECTORY CDErrors, IMPORTS CDErrors, PaintErrorRect calls CDErrors.IncludeMessage, DataForChildren deleted fields which handled deletion of old errors, TranslateGeometry Replaced enumeration of old errors by call to CDErrors.RemoveMessages, TranslateGeometry, IMPORTS, TranslateChild, PaintErrorRect Edited on March 7, 1985 12:58:15 pm PST, by Shand changes to: DIRECTORY, CopySignal (local of CopyNodeProperties), s (local of CopySignal, local of CopyNodeProperties) Fixed bug in reading ChipMonk files- allowed case of signal name being ATOM, PaintErrorRect added bloating of error rect by half lambda, EnumerateGeometry Improved search pruning, FlattenTree (local of EnumerateGeometry) Improved search pruning Edited on March 8, 1985 1:37:29 pm PST, by Shand Changed name of CircuitConstraint in SX to Constraint changes to: Constraint, CompressBoxPaths (local of NormalizeCircuit) Edited on March 9, 1985 10:57:10 pm PST, by Shand Two things: 1st: Extensions to NormalizeCircuit to remove nodes which were inserted by FindRootNode but never actually merged into other nodes in this cell. This is done using a special property UnMerged which is attached to nodes created for subcell nodes and then deleted during merging by CopyNodeProperties. If node is never merged it still has UnMerged property during NormalizeCircuit, and should be removed from both node list and mergeDirectory. 2nd: When first created mergeDirectory is made large (521 entries). During NormalizeCircuit it is remade with slightly more space than required number of entries. This is to improve performance on large designs. changes to: DIRECTORY, CopySignal (local of CopyNodeProperties), CopyNodeProperties, NormalizeCircuit, CompressCircuitNodeList (local of NormalizeCircuit), CompressLinkages (local of NormalizeCircuit), CompressMergeDirectoryEntries (local of NormalizeCircuit), CompressMerge (local of CompressMergeDirectoryEntries, local of NormalizeCircuit), LookupNode, UnMerged, FindRootNode, AddCircuitNodeMerge (local of FindRootNode) Edited on March 10, 1985 7:11:03 pm PST, by Shand Deleted obsolete field attached from SX.CircuitNode. Added code to keep some point on the node in the SX.CircuitNode RECORD for each node. Deleted obsolete parameters from LinkageAttach. More work on MergeDirectory Normalization. changes to: MergeNode, LinkageAttach, DIRECTORY, AddBox, FindRootNode, AddCircuitNodeMerge (local of FindRootNode), DIRECTORY, CountUnMerged (local of CompressMergeDirectoryEntries, local of NormalizeCircuit), CompressMerge (local of CompressMergeDirectoryEntries, local of NormalizeCircuit), CompressMergeDirectoryEntries (local of NormalizeCircuit), SortGeometry, AddBox Edited on March 11, 1985 7:11:03 pm PST, by Shand Deleted obsolete field unsortedBoxes from SX.QuadTreeRoot. changes to: SortGeometry, AddBox cleaned up so as not to use unsortedBoxes. Edited on March 13, 1985 2:28:03 pm PST, by Shand Detection and labelling of repetitions. changes to: DIRECTORY, IMPORTS, TranslateChild, TranslateGeometry, ApplComp Edited on May 6, 1985 11:26:55 am PDT, by Beretta Converted to ChipNDale CD20 Edited on July 8, 1985 4:45:21 pm PDT, by Beretta If an object has the property export and if the value is $TRUE and the cell is at the root of analysis, then the signal name of the node with this property will be used as a parameter (port). changes to: CopyNodeProperties: added handling of new property. Last edited by: gbb July 17, 1985 3:56:55 pm PDT Replaced ordered symbol table stuff by red-black trees for Cedar 6.0 Last edited by: gbb July 23, 1985 4:37:38 pm PDT Added storage of length, width for transistors. changes to: CreateLinkage: new parameters, NodeLinkage: new fields. gbb January 10, 1986 6:01:50 pm PST Clean-up while chasing a bug. gbb May 19, 1986 9:10:23 am PDT Instead of ignoring only material in the error layer, now all special layers are ignored. changes to: specialLayers: max value of special layers, TranslateChild: changed layer test. gbb May 20, 1986 3:04:36 pm PDT Circumvented a temporary bug in the compiler. changes to: specialLayers: omitted. gbb June 9, 1986 4:55:58 pm PDT Added provisions for signal names that are of type REF TEXT instead of Rope.ROPE. changes to: CopySignal (local of CopyNodeProperties): additional case., DIRECTORY Ê L˜codešœ ™ KšœJ™JIcode2šœ Ïmœ=™HK™2K™4K™:K™4K™/—šÏk ˜ Kšœžœ ˜Kšœžœ˜&Kšžœžœà˜èKšœ žœ ˜Kšœ žœ'˜5Kšœžœ ˜Kšœ žœ˜-Kšœ žœF˜XKšœžœ˜Kšœžœ˜1Kšœ žœZ˜lKšœžœE˜QKšœžœžœ˜+KšžœžœÆ˜ÎKšœ žœ˜"Kšœžœ˜2Kšœžœ4˜AKšœ žœB˜R—šÐalœžœž˜Lšžœ žœ›˜³Lšžœžœ˜ —Lšž˜™Kšœžœžœ˜%Kšœžœžœ˜%Kšœ žœžœ ˜Kšœ žœžœ ˜!Kšœ žœžœ ˜#Kšœžœžœ˜Kšœ žœžœ ˜Kšœ žœžœ ˜#Kšœ žœžœ ˜!Kšœžœžœ˜1—L™Kš œžœžœžœžœžœ˜IKš œžœžœžœ žœ žœ˜AKšœžœ žœžœ žœ žœžœžœžœ˜†KšÏbœÐekÏe œÔ™êKšœ#žœ˜-Kšœ$žœ˜.š Ïn œžœžœ žœžœžœž˜@Kšžœ%˜+KšžœÏc˜—š œžœ ž˜#Kšœ9žœ™NKšœ žœžœ˜8Kšœžœ˜%Kšœžœ˜šžœžœž˜#šžœžœž˜+Kšœžœ›˜¢Kšœžœžœìžœ ˜‡Kšžœžœžœ1˜CKšžœ ž˜Kšžœ˜—Kšž˜Kšžœ¤Ðce¤˜—Iunitšœ/˜/š žœžœžœ!žœž˜Kšœ¼™¼Kšœžœ˜ Kšœžœ žœ˜'Kšœ žœ/˜;Mšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ ˜ KšœD˜DM™š žœžœžœžœ'žœžœž˜MKšœ6¤˜KKšž˜—šžœžœžœžœžœžœžœž˜`Kšœ%™%š žœžœ*žœžœž˜EKšžœ;žœžœ˜GKšžœ)žœžœ˜5šžœžœž˜KšœE™EKšœC˜CKšœ˜Kšœžœ ˜š žœžœ*žœžœž˜EKšžœžœžœ ˜3Kšžœ˜—Kšœ žœ%˜5Kšœ žœ3˜CKšœQ˜QKšœ˜šžœ žœ6žœž˜SKšžœRžœžœ˜^Kšœ˜šžœžœž˜K™8Kšœžœ˜š žœ žœ%žœ-žœžœž˜}šžœLžœžœž˜\K™7K™5K™3Kšœžœ_˜vKšœN˜NKšžœ˜—Kšœ˜Kšž˜—Kšžœ˜—Kšžœ˜—Kšžœž˜Kšž˜—Kšžœ˜—Kšž˜—Kšžœ¤¥˜—š#£œžœžœžœžœ žœ žœ žœžœžœ)žœžœžœžœ žœžœžœž˜úš £ œžœžœžœžœž˜IKšžœ žœ¤˜'—šžœ#ž˜)Kš žœžœžœžœžœm˜›—Mšžœžœ¤˜>š žœžœžœ8žœžœž˜XKšœžœžœ ˜šžœžœž˜ Kš œ žœžœbžœ žœžœ žœ˜Kš žœ°žœžœžœžœ ˜ð—Kšžœžœžœ ˜Kšžœ˜—Kšžœ¤¥˜—Lšœ žœžœ˜š!£œžœžœ žœ3žœžœ žœžœžœ[žœžœžœžœ žœžœžœž˜¹K™MšÑbklœžœžœžœžœžœžœ˜8Kšœ!žœ¤˜*—š¦œžœžœžœ žœžœžœ˜9Kšœ(žœ¤˜1—šžœžœž˜%Kšœ&žœ˜.Kšœ žœ˜"Mšœ†˜†Kšžœž œ.˜@šžœž˜ Kšœ|˜|Kšœp˜pKšžœ¤ Ðcs˜—šžœžœž˜šœžœžœž˜KšœA˜AKšžœ˜—š žœžœ žœžœž˜$Kšœžœžœžœj˜œKšœ˜Kšžœ˜——Mšœ žœñ˜þKšœq˜qKšžœ¤˜—Kšžœžœ ˜Kšžœ¤¥˜—š £ œžœžœ žœžœž˜SšœC™CKšžœžœžœ˜5Kšžœžœžœ˜/Kšžœ žœžœ˜—Kšœ™š žœ žœžœ(žœ žœž˜RKšœP˜PKšžœ˜—Kšœ™Kšœ2˜2Kšœžœ˜+Kšžœ¤ ˜—š£œžœ žœžœžœ9žœžœžœ žœž˜šš£ œžœžœžœ žœžœžœž˜PKšœžœVžœ™xKš žœžœžœžœžœ˜šžœžœž˜Kšœžœžœžœ(˜CKš œžœžœžœžœ:˜TKšœžœžœžœ7˜Mšœžœž˜Kšœžœ ˜#Kšœ žœK˜YKšœ˜š žœ žœ$žœžœž˜FKšœžœ?˜SKšœ˜Kšžœ˜—Kšžœ ˜Kšžœ˜—Kšžœžœ˜—Kšžœ¤¥ ˜—š£ œžœ žœž˜5šžœžœž˜š žœžœ&žœžœž˜Všžœ%žœž˜1Kšžœ"žœ"˜JKšœ˜Kšž˜Kšž˜—šžœžœž˜K™Kšœžœ˜Kšœ7˜7Kšž˜—Kšž˜—Kšž˜—Kšžœ¤¥ ˜—Mšœ žœžœ:˜NKšœ žœžœ6˜IKšœ žœžœ?˜QMšœ'™'š žœ'žœžœ žœž˜DKšœd˜d—Mšœ™šž˜Kšœ žœ ˜Kšœ žœ ˜Kšœžœ˜š žœžœžœžœžœžœž˜8Kšœž˜Kšžœ˜—Kšœ-˜-Kšžœžœžœ,žœ˜YKšœ žœ ˜š žœ žœžœžœžœž˜5šžœ#žœž˜/KšœZ˜ZKšœ#˜#Kšž˜—šž˜Kšœ#˜#—Kšž˜—šžœžœžœž˜KšœX˜X—Kšžœ˜—Kšœ™KšœD˜DKšžœ¤¥˜—š£ œžœžœ žœžœ/žœ žœžœž˜‘Kšœ(™(Kšœžœžœ˜'Kšžœžœžœ¤˜Sšžœ žœž˜Kšžœžœžœ˜'K˜šžœžœž˜Kšœ žœC˜RKšž˜Kšž˜—Kšžœ˜—šžœž˜šœ ž˜Kšœ+˜+Kšœ-˜-Kšžœ˜—šœ ž˜Kšœ2˜2Kšžœ˜—Kšž˜—Kšžœ¤¥ ˜—š £œžœžœžœ ž˜8šœ™Kšœl¢œ™—š£œžœž˜%Kš œ žœžœžœžœ˜7Kš œžœžœžœžœžœ˜IKš œžœžœžœžœ ˜Mšžœ žœžœžœ˜Kšœ¢œ™$Kšœžœžœ˜;Kšœ˜šžœ žœž˜Kšœžœžœ˜)Kš žœžœžœIžœžœ˜†Kšžœ ˜Kšžœ˜—M™Kš žœ žœžœžœ¤s˜“Kšœ˜šžœ žœž˜Kšžœ(žœ˜EKšžœ3˜7Kšžœ˜—Kšœ#˜#šžœžœž˜Kšœžœ˜'Kšžœ˜—Kšžœ¤¥˜—š£œžœž˜š žœ žœžœžœ+žœ žœž˜Yš žœ žœžœžœ4žœ žœž˜bšžœžœž˜Kšœ6˜6—Kšž˜—Kšž˜—Kšžœ¤˜—š£œžœž˜+šžœžœžœž˜&Kšœžœžœžœ˜(Kšœü™üš  œž˜,Kšžœžœžœžœ&žœžœžœ˜uKšžœžœ˜Kšžœ¤˜—š  œž˜,Kšœ žœ˜š žœžœžœžœ&žœžœž˜bKšœ žœ˜Kšœ˜šžœžœž˜KšœI˜IKšžœPžœžœ%˜Kšžœ˜Kšžœ˜—šžœžœž˜Kšžœžœ!žœž˜2—Kšžœ˜—Kšžœžœ˜Kšžœ¤¥ ˜—Mšœ¢™&K™9Kšœ žœ'˜3Kšœ˜Mšœ6˜6Kšžœžœžœžœ˜8Kšœ+˜+Kšœ6˜6Kšœ˜Kšžœžœž˜>Kšžœ¤˜"—Kšžœ¤¥˜%—š£œžœžœž˜<š žœžœžœžœ-žœžœž˜Ušžœžœž˜,Kšœžœ žœ˜Kšœžœžœ>˜HKšœžœžœ˜Kšžœžœ˜—Kšžœ˜—šžœžœž˜9Kšžœžœžœ%˜DKšž˜—Kšžœ¤˜—Mšœ™Kšœ˜K˜K˜!šžœžœ(ž˜Jšžœ'žœž˜2Kšœ5˜5—Kšž˜—Kšžœ¤¥˜—š£ œžœžœžœžœžœžœžœž˜WKšœ9™9Kš  œA™MKšœL™Lšœ+™+š  œ?™IKšœ6™6KšœR™RKšœ™——šžœžœžœ˜Kšœžœžœ˜Kšœ™Kšžœžœžœžœ˜