DIRECTORY Atom USING [GetPName], BasicTime USING [Now], CD, CDAtomicObjects, CDBasics, CDDirectory USING [Fetch], CDErrors USING [IncludeMessage, RemoveMessages], CDInstances, CDProperties, CDRects, CDSimpleRules USING [MaxWidth, MinDist, MinWidth, NotKnown], CDSymbolicObjects, Core USING [CellType, Properties, Wire], CoreClasses USING [recordCellClass, RecordCellType, transistorCellClass, unspecifiedCellClass], CoreGeometry, CoreOps USING [CreateWires, CopyWire, GetCellTypeName, GetShortWireName, SetShortWireName], CoreProperties, FS USING [StreamOpen], IO USING [card, char, Close, Error, ErrorCode, int, noWhereStream, Put1, PutF, PutF1, PutFR, PutFR1, PutRope, refAny, rope, STREAM, time], RefTab USING [Create, EachPairAction, Fetch, Insert, Pairs, Ref, SeqIndex], Rope USING [Cat, ROPE], SoS, SoSTNT USING [BlowTNT, InitTNT, InTNT, RememberTNT, SweepTNT, TNT], ViewerIO USING [CreateViewerStreams], ViewerTools USING [FindExistingViewer, Viewer]; SoSImpl: CEDAR PROGRAM IMPORTS Atom, BasicTime, CD, CDBasics, CDDirectory, CDErrors, CDInstances, CDProperties, CDRects, CDSimpleRules, CDSymbolicObjects, CoreClasses, CoreGeometry, CoreOps, CoreProperties, FS, IO, RefTab, Rope, SoSTNT, ViewerIO, ViewerTools EXPORTS SoS SHARES CDRects ~ BEGIN OPEN SoSTNT; debug: BOOL _ FALSE; -- to start debugging enter: _ SoSImpl.Debug[] occDebug: BOOL = FALSE; -- for occasional debugging useTNT: BOOL = TRUE; -- for timing analysis only persist: BOOL _ TRUE; -- depends on Sinix (try hard to flag CD design) fast: BOOL _ TRUE; -- if FALSE then SoS is purely "object oriented" break: SIGNAL = CODE; -- for debugging; related to property $SoSBreak panic: SIGNAL = CODE; -- cannot communicate violations coreMess: SIGNAL [reason: Rope.ROPE, torch: REF ANY] = CODE; -- clean up before issuing coreInconsistent coreInconsistent: PUBLIC ERROR [reason: Rope.ROPE, torch: REF ANY] = CODE; mLog: IO.STREAM; -- messages eLog: IO.STREAM; -- error log dLog: IO.STREAM _ IO.noWhereStream; -- debugging State: TYPE = SoS.State; -- REF StateRec; StateRec: TYPE = SoS.StateRec; Wire: TYPE = Core.Wire; DRV: TYPE = SoS.DRV; -- REF DesignRuleViolation DesignRuleViolation: TYPE = SoS.DesignRuleViolation; ErrorRect: TYPE = SoS.ErrorRect; checked: ATOM = CoreProperties.RegisterProperty [$SoSWasHere]; DRVkey: PUBLIC ATOM _ CoreProperties.RegisterProperty [$SoSError]; bbKey: ATOM _ CoreProperties.RegisterProperty [$SoSbb]; trace: ATOM = CoreProperties.RegisterProperty [$SoSSeparationChecked]; analysis: ATOM = CoreProperties.RegisterProperty [$SoSAnalysis]; cellHull: ATOM = CoreProperties.RegisterProperty [$SoSHull]; matToCell: ATOM = CoreProperties.RegisterProperty [$SoSmc]; cellToCell: ATOM = CoreProperties.RegisterProperty [$SoScc]; rectClass: ATOM = $Rect; pinClass: ATOM = $PinOb0; doNotAnalyse: ATOM = $DoNotDRC; previousTechnology: CD.Technology _ NIL; previousMaxSeparation: CD.Number _ 75 * 8; specialLayers: CD.Layer = MAX [CD.shadeLayer, CD.errorLayer, CD.backgroundLayer, CD.outlineLayer, CD.selectionLayer, CD.commentLayer] + 1; errorFeedbackCutOff: CARDINAL = 50; -- after this number of errors, a | is no longer displayed in the ChipNDale Terminal viewer (if it may be displayed at all). WireSet: TYPE = REF WireSetRec; -- the wires of a set of cells WireSetRec: TYPE = RECORD [elt: SEQUENCE size: NAT OF Wire]; PropSet: TYPE = REF PropSetRec; -- the properties of a set of cells PropSetRec: TYPE = RECORD [p: SEQUENCE size: NAT OF Core.Properties]; CheckDesignRules: PUBLIC PROC [cell: Core.CellType, design: CD.Design _ NIL, abortFlag: REF BOOL _ NIL, attributes: CoreGeometry.Decoration] RETURNS [quantity: CARDINAL _ 1] ~ BEGIN ENABLE BEGIN IO.Error => IF ec = StreamClosed THEN BEGIN s: IO.STREAM; m: BOOL = (stream = mLog); IF m THEN StartLog [] ELSE Debug []; s _ IF m THEN mLog ELSE dLog; CONTINUE END; panic => GOTO paranoia END; state: State _ NEW [StateRec]; messy, okToType: BOOL _ FALSE; abortionCause: Rope.ROPE; debugTorch: REF ANY; shadowCell: Core.CellType; -- to be implemented fakeActual: Wire; cellName: Rope.ROPE = CoreOps.GetCellTypeName [cell]; designName: Rope.ROPE = IF design#NIL THEN design.name ELSE "in the sky"; eLog _ FS.StreamOpen [fileName: "[]<>Temp>DRC>SoS.log", accessOptions: create, keep: 5, extendFileProc: NIL, remoteCheck: FALSE, wDir: NIL]; -- keep is 5 because of incremental use eLog.PutF1 ["Error log by SoS. Design: %g\n\n", IO.rope [designName]]; shadowCell _ CopyCell [cell]; -- to be implemented mLog.PutF [format: "\nChecking wire geometry in cell %l%g%l of design %g\n", v1: IO.rope ["b"], v2: IO.rope [cellName], v3: IO.rope ["B"], v4: IO.rope [designName]]; IF debug THEN IO.PutF [stream: dLog, format: "\n%g. design: %g, cell: %g\n", v1: IO.time [], v2: IO.rope [designName], v3: IF cellName = NIL THEN IO.refAny [shadowCell] ELSE IO.rope [cellName]]; state.design _ design; state.abort _ IF abortFlag # NIL THEN abortFlag ELSE NEW [BOOL _ FALSE]; IF useTNT THEN state.nt _ InitTNT []; IF (design = NIL) OR (design.technology = NIL) THEN BEGIN state.maxSeparation _ previousMaxSeparation _ 75 * 8; previousTechnology _ NIL END ELSE IF design.technology # previousTechnology THEN BEGIN previousMaxSeparation _ ComputeMaxSeparation [design.technology]; previousTechnology _ design.technology END; state.maxSeparation _ previousMaxSeparation; state.globalErrorCount _ 0; state.attributes _ attributes; fakeActual _ CoreOps.CopyWire [shadowCell.public]; CheckCoreCell [self: shadowCell, state: state, actual: fakeActual, loc: [0, 0], orient: original ! coreMess => {messy _ TRUE; abortionCause _ reason; debugTorch _ torch; CONTINUE}]; IF useTNT THEN BlowTNT [state.nt]; IF messy THEN BEGIN IF debug THEN break; cell.properties _ CoreProperties.PutProp [shadowCell.properties, checked, NIL]; eLog.PutRope ["\nDRC failed. Your Core data structure is busted! You lose, you lose."]; eLog.Close []; coreInconsistent [abortionCause, debugTorch] END; quantity _ state.globalErrorCount; mLog.PutF [format: "\nNumber of new design violations found: %l%g%l\n", v1: IO.rope ["b"], v2: IO.card [state.globalErrorCount], v3: IO.rope ["B"]]; eLog.PutRope ["\nDRC terminated normally."]; eLog.Close []; EXITS paranoia => -- cannot communicate with anybody {eLog.PutRope ["\nThis is paranoia."]; eLog.Close []} END; -- CheckDesignRules EnumerateGeometry: PROC [state: State, wire: Wire, eachRect: PROC [CD.Rect, CD.Layer]] = { FOR list: LIST OF CD.Instance _ CoreGeometry.GetGeometry[state.attributes, wire], list.rest WHILE list#NIL DO instance: CD.Instance = list.first; SELECT TRUE FROM instance.ob.class=CDRects.bareRectClass => IF instance.ob.layer>=specialLayers AND CD.LayerKey[instance.ob.layer]#NIL THEN eachRect[CDInstances.InstRectO[instance], instance.ob.layer]; -- Filter out the special layers. Also the extractor creates material at illegal levels. We try heuristically to filter it out here. CDSymbolicObjects.IsSymbolicOb[instance.ob] => {}; ENDCASE => FOR rList: CDAtomicObjects.DrawList _ CoreGeometry.FlattenAtomic[instance.ob], rList.rest WHILE rList#NIL DO eachRect[ CDBasics.MapRect[rList.first.r, instance.tran], rList.first.layer ]; ENDLOOP; ENDLOOP; }; GetObject: PROC [cell: Core.CellType, state: State] RETURNS [obj: CD.Object] ~ INLINE BEGIN obj _ NARROW [CoreProperties.GetProp [cell.properties, state.cdObjKey], CD.Object]; IF persist AND (obj=NIL) AND (state.design#NIL) THEN obj _ CDDirectory.Fetch [state.design, CoreOps.GetCellTypeName[cell]].object; RETURN [obj]; END; -- GetObject CopyCell: PROC [original: Core.CellType] RETURNS [copy: Core.CellType] ~ BEGIN copy _ original -- << Yuck. Terrible hack. >> To be implemented END; -- CopyCell CheckCell: TYPE = SoS.CheckCell; CheckCoreCell: CheckCell ~ BEGIN Send: PROC ~ INLINE BEGIN check: REF CheckCell _ NARROW [CoreProperties.GetProp [self.properties, analysis]]; IF check = NIL THEN check _ NARROW [CoreProperties.GetProp [self.class.properties, analysis]]; IF check = NIL THEN BEGIN obj: CD.Object = GetObject [self, state]; IF obj = NIL THEN RETURN; -- Cell contains no rectangles check _ NEW [CheckCell _ DoNotCheck]; MarkError [self, state, [obj.class.interestRect[obj], "Cell has no provisions to be checked"]] END; check^ [self, state, actual, loc, trans.orient] END; -- Send IF fast THEN SELECT self.class FROM CoreClasses.recordCellClass => CheckRecord [self, state, actual, loc, trans.orient]; CoreClasses.transistorCellClass => CheckTransistor [self, state, actual, loc, trans.orient]; CoreClasses.unspecifiedCellClass => NULL; ENDCASE => Send [] ELSE Send [] END; -- CheckCoreCell CheckRecord: CheckCell ~ BEGIN origin: CD.Position = [0, 0]; cellData: CoreClasses.RecordCellType; bindingTable: RefTab.Ref; boundInternal: Wire; propagatedActuals: WireSet; -- one wire per subcell savedProps: PropSet; -- holds properties during recursion ownName: Rope.ROPE = CoreOps.GetCellTypeName [self]; IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp[self.properties, checked]#NIL) THEN RETURN; mLog.Put1 [IO.char ['.]]; IF debug THEN IO.PutF [stream: dLog, format: "\nChecking cell %l%g%l .\n", v1: IO.rope ["e"], v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName], v3: IO.rope ["E"]]; IF state.abort^ THEN ERROR ABORTED; ClearErrors [self, state]; cellData _ NARROW [self.data, CoreClasses.RecordCellType]; savedProps _ NEW [PropSetRec[actual.size]]; FOR p: NAT IN [0 .. actual.size) DO savedProps[p] _ actual[p].properties; -- modified by BS on October 26, 1986 8:55:22 pm PST ENDLOOP; bindingTable _ CreateBindingTable [actual: actual, public: self.public]; boundInternal _ BindInternal [bindingTable, cellData.internal, state]; IF occDebug THEN BEGIN dLog.PutF1 ["Binding and propagating: %g. Binding table:\n", IO.rope [ownName]]; PrintBinding [bindingTable]; dLog.PutF1 ["Bound internal of %g:\n", IO.rope [ownName]]; PrintWire [boundInternal] END; propagatedActuals _ NEW [WireSetRec[cellData.size]]; FOR sub: NAT IN [0 .. cellData.size) DO propagatedActuals[sub] _ PropagateBinding [state, bindingTable, cellData.instances[sub].actual] ENDLOOP; FlushBindingTable [bindingTable]; FOR sub: NAT IN [0 .. cellData.size) DO cdInst: CD.Instance = CoreGeometry.GetTransf[state.attributes, cellData.instances[sub]]; IF (cdInst = NIL) THEN coreMess ["Core record cell has no geometry (1)", cellData]; CheckCoreCell [self: cellData.instances[sub].type, state: state, actual: propagatedActuals[sub], loc: CDBasics.AddPoints [loc, cdInst.trans.off], orient: CDBasics.ComposeOrient [trans.orient, cdInst.trans.orient]] ENDLOOP; IF occDebug THEN BEGIN dLog.PutF1 ["Verifying: %g\nbound internal:\n", IO.rope [ownName]]; PrintWire [boundInternal] END; FOR i: NAT IN [0 .. cellData.internal.size) DO FOR j: NAT IN [i .. cellData.internal.size) DO MaterialSeparation [state: state, cell: self, w1: boundInternal[i], w2: boundInternal[j], loc1: origin, loc2: origin, orient1: original, orient2: original] ENDLOOP; WidthCheck [self, state, boundInternal[i]] ENDLOOP; FOR sub: NAT IN [0 .. cellData.size) DO cdInst: CD.Instance = CoreGeometry.GetTransf[state.attributes, cellData.instances[sub]]; IF (cdInst = NIL) THEN coreMess ["Core record cell has no geometry (2)", cellData]; FOR i: NAT IN [0 .. cellData.internal.size) DO MaterialToCellSeparation [state: state, self: cellData.instances[sub].type, actual: propagatedActuals[sub], wire: cellData.internal[i], father: self, -- for error marking materialLoc: loc, cellLoc: cdInst.trans.off, materialOrient: trans.orient, cellOrient: cdInst.orientation] ENDLOOP ENDLOOP; IF useTNT THEN SweepTNT [state.nt]; FOR sub1: NAT IN [0 .. cellData.size) DO cdInst1: CD.Instance = CoreGeometry.GetTransf[state.attributes, cellData.instances[sub1]]; IF (cdInst1 = NIL) THEN coreMess ["Core record cell has no geometry (3)", cellData]; FOR sub2: NAT IN (sub1 .. cellData.size) DO cdInst2: CD.Instance = CoreGeometry.GetTransf[state.attributes, cellData.instances[sub2]]; IF (cdInst2 = NIL) THEN coreMess ["Core record cell has no geometry (4)", cellData]; IF useTNT AND (InTNT [state.nt, cdInst1, cdInst2, propagatedActuals[sub1], propagatedActuals[sub2]]) THEN LOOP; CellToCellSeparation [state: state, self: cellData.instances[sub1].type, otherCell: cellData.instances[sub2].type, selfActual: propagatedActuals[sub1], otherActual: propagatedActuals[sub2], father: self, -- for error marking selfLoc: CDBasics.AddPoints [loc, cdInst1.trans.off], otherLoc: CDBasics.AddPoints [loc, cdInst2.trans.off], selfOrient: CDBasics.ComposeOrient [trans.orient, cdInst1.orientation], otherOrient: CDBasics.ComposeOrient [trans.orient, cdInst2.orientation]]; IF useTNT THEN RememberTNT [state.nt, cdInst1, cdInst2, propagatedActuals[sub1], propagatedActuals[sub2]] ENDLOOP ENDLOOP; FOR p: NAT IN [0 .. self.public.size) DO actual[p].properties _ savedProps[p]; -- BS on October 26, 1986 8:55:47 pm PST ENDLOOP; self.properties _ CoreProperties.PutProp [self.properties, checked, checked] END; -- CheckRecord CheckTransistor: CheckCell ~ BEGIN DoNotCheck [self, state, actual, loc, trans.orient] END; -- CheckTransistor DoNotCheck: CheckCell ~ BEGIN self.properties _ CoreProperties.PutProp [self.properties, checked, checked] END; -- DoNotCheck RegisterAnalysisProcs: PROC ~ BEGIN CoreClasses.recordCellClass.properties _ CoreProperties.PutProp [ on: CoreClasses.recordCellClass.properties, prop: analysis, value: NEW [CheckCell _ CheckRecord]]; CoreClasses.transistorCellClass.properties _ CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, analysis, NEW [CheckCell _ CheckTransistor]]; CoreClasses.unspecifiedCellClass.properties _ CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, analysis, NEW [CheckCell _ DoNotCheck]]; END; -- RegisterAnalysisProcs CellHullProc: TYPE = SoS.CellHullProc; AtomicWireHull: PROC [w: Wire, state: State] RETURNS [h: CD.Rect _ CDBasics.empty] ~ BEGIN stored: REF CD.Rect _ NARROW [CoreProperties.GetProp [w.properties, bbKey]]; EachRect: PROC [r: CD.Rect, l: CD.Layer] = { h.x1 _ MIN [h.x1, r.x1]; h.y1 _ MIN [h.y1, r.y1]; h.x2 _ MAX [h.x2, r.x2]; h.y2 _ MAX [h.y2, r.y2]; }; IF w.size > 0 THEN BEGIN break -- you may proceede END; IF (stored # NIL) THEN RETURN [stored^]; EnumerateGeometry[state, w, EachRect]; stored _ NEW [CD.Rect _ h]; w.properties _ CoreProperties.PutProp [w.properties, bbKey, stored] END; -- AtomicWireHull CellHull: CellHullProc ~ BEGIN hull: REF CellHullProc _ NEW [CellHullProc]; SELECT self.class FROM CoreClasses.recordCellClass => hull^ _ RecordCellHull; CoreClasses.transistorCellClass => hull^ _ TransistorHull; CoreClasses.unspecifiedCellClass => hull^ _ UnspecifiedHull; ENDCASE => BEGIN hull _ NARROW [CoreProperties.GetProp [self.properties, cellHull]]; IF hull = NIL THEN hull _ NARROW [CoreProperties.GetProp [self.class.properties, cellHull]]; IF hull = NIL THEN hull^ _ UnspecifiedHull; END; RETURN [hull^ [self, state]] END; -- CellHull; RecordCellHull: CellHullProc ~ BEGIN stored: REF CD.Rect _ NARROW [CoreProperties.GetProp [self.properties, bbKey]]; IF (stored = NIL) THEN BEGIN cellData: CoreClasses.RecordCellType = NARROW [self.data, CoreClasses.RecordCellType]; IF cellData.internal.size = 0 THEN break; -- you may proceede FOR i: NAT IN [0 .. cellData.internal.size) DO a: CD.Rect = AtomicWireHull [cellData.internal[i], state]; h.x1 _ MIN [h.x1, a.x1]; h.y1 _ MIN [h.y1, a.y1]; h.x2 _ MAX [h.x2, a.x2]; h.y2 _ MAX [h.y2, a.y2] ENDLOOP; FOR sub: NAT IN [0 .. cellData.size) DO r: CD.Rect = CellHull [cellData.instances[sub].type, state]; h.x1 _ MIN [h.x1, r.x1]; h.y1 _ MIN [h.y1, r.y1]; h.x2 _ MAX [h.x2, r.x2]; h.y2 _ MAX [h.y2, r.y2] ENDLOOP; stored _ NEW [CD.Rect _ h]; self.properties _ CoreProperties.PutProp [self.properties, bbKey, stored] END END; -- RecordCellHull TransistorHull: CellHullProc ~ BEGIN stored: REF CD.Rect _ NARROW [CoreProperties.GetProp [self.properties, bbKey]]; IF (stored = NIL) THEN BEGIN wire: Wire = self.public; IF wire.size = 0 THEN break; -- you may proceede FOR i: NAT IN [0 .. wire.size) DO a: CD.Rect = AtomicWireHull [wire[i], state]; h.x1 _ MIN [h.x1, a.x1]; h.y1 _ MIN [h.y1, a.y1]; h.x2 _ MAX [h.x2, a.x2]; h.y2 _ MAX [h.y2, a.y2] ENDLOOP; stored _ NEW [CD.Rect _ h]; self.properties _ CoreProperties.PutProp [self.properties, bbKey, stored] END END; -- TransistorHull UnspecifiedHull: CellHullProc ~ BEGIN stored: REF CD.Rect _ NARROW [CoreProperties.GetProp [self.properties, bbKey]]; IF (stored = NIL) THEN BEGIN stored _ NEW [CD.Rect _ h]; self.properties _ CoreProperties.PutProp [self.properties, bbKey, stored] END END; -- UnspecifiedHull RegisterPruningProcs: PROC ~ BEGIN CoreClasses.recordCellClass.properties _ CoreProperties.PutProp [ on: CoreClasses.recordCellClass.properties, prop: cellHull, value: NEW [CellHullProc _ RecordCellHull]]; CoreClasses.transistorCellClass.properties _ CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, cellHull, NEW [CellHullProc _ TransistorHull]]; CoreClasses.unspecifiedCellClass.properties _ CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, cellHull, NEW [CellHullProc _ UnspecifiedHull]] END; -- RegisterPruningProcs CreateBindingTable: PROC [actual, public: Wire] RETURNS [bindingTable: RefTab.Ref] ~ BEGIN tableSize: RefTab.SeqIndex _ public.size; IF actual.size # public.size THEN BEGIN IF debug THEN BEGIN dLog.Put1 [IO.rope ["actual.size # public.size. Actual wire:\n"]]; PrintWire [actual]; dLog.Put1 [IO.rope ["Public wire:\n"]]; PrintWire [public]; ERROR END ELSE coreMess ["actual.size # public.size (5)", actual] END; IF (tableSize MOD 2) = 0 THEN tableSize _ tableSize.SUCC; bindingTable _ RefTab.Create [tableSize]; FOR i: NAT IN [0 .. public.size) DO [] _ RefTab.Insert [x: bindingTable, key: public[i], val: actual[i]] ENDLOOP END; -- CreateBindingTable FlushBindingTable: PROC [bindingTable: RefTab.Ref] ~ INLINE BEGIN bindingTable _ NIL END; -- FlushBindingTable PropagateBinding: PROC [state: State, bindingTable: RefTab.Ref, actual: Wire] RETURNS [boundActual: Wire] ~ BEGIN PropagateAtomic: PROC [state: State, bindingTable: RefTab.Ref, actual: Wire] RETURNS [boundActual: Wire] ~ INLINE BEGIN boundActual _ NARROW [RefTab.Fetch [bindingTable, actual].val]; IF boundActual = NIL THEN BEGIN name: Rope.ROPE = IF debug THEN IO.PutFR1 ["New signal # %g", IO.card [state.wireCreationCount]] ELSE "New signal by SoS"; boundActual _ CoreOps.CreateWires [size: 0, name: name]; state.wireCreationCount _ state.wireCreationCount.SUCC; [] _ RefTab.Insert [x: bindingTable, key: actual, val: boundActual] END ELSE boundActual _ CoreOps.SetShortWireName [boundActual, CoreOps.GetShortWireName[actual]] END; -- PropagateAtomic boundActual _ CoreOps.CreateWires [actual.size]; FOR i: NAT IN [0 .. actual.size) DO boundActual[i] _ PropagateAtomic [state, bindingTable, actual[i]] ENDLOOP END; -- PropagateBinding BindInternal: PROC [bindingTable: RefTab.Ref, internal: Wire, state: State] RETURNS [boundInternal: Wire] ~ BEGIN boundInternal _ CoreOps.CopyWire [internal]; FOR i: NAT IN [0 .. internal.size) DO b: Wire = NARROW [RefTab.Fetch [bindingTable, internal[i]].val]; IF b # NIL THEN BEGIN boundInternal[i] _ b; boundInternal[i].properties _ internal[i].properties; -- BS on October 26, 1986 8:56:07 pm PST END ENDLOOP END; -- BindInternal BindTransistor: PROC [public, actual: Wire, state: State] RETURNS [tw: Wire] ~ BEGIN tw _ CoreOps.CopyWire [public]; FOR i: NAT IN [0 .. public.size) DO tw[i] _ actual[i]; tw[i].properties _ public[i].properties; -- BS on October 26, 1986 8:56:37 pm PST ENDLOOP END; -- BindTransistor MaterialToCellProc: TYPE = SoS.MaterialToCellProc; CellToCellProc: TYPE = SoS.CellToCellProc; WidthCheck: PROC [c: Core.CellType, state: State, w: Wire] ~ BEGIN EachRect: PROC [r: CD.Rect, l: CD.Layer] = { min: CD.Number _ CDSimpleRules.MinWidth [l]; max: CD.Number _ CDSimpleRules.MaxWidth [l]; a: CD.Number _ MAX [(r.x2 - r.x1), (r.y2 - r.y1)]; b: CD.Number _ MIN [(r.x2 - r.x1), (r.y2 - r.y1)]; -- a and b are such that a > b IF (a < min) OR (b < min) OR (a > max) OR (b > max) THEN BEGIN splitA: CD.Number = 3 * min; splitB: CD.Number = min; -- yuk, spit IF (CD.LayerKey[l] = $cut) AND (a = splitA) AND (b = splitB) THEN NULL -- mumble ELSE MarkError [c, state, [r, Rope.Cat ["Width violation on layer ", Atom.GetPName [CD.LayerKey [l]], " (wire ", CoreOps.GetShortWireName[w], ")"]]] END }; EnumerateGeometry[state, w, EachRect]; END; -- WidthCheck IsCut: PROC [layer: CD.Layer] RETURNS [BOOL] ~ INLINE BEGIN key: ATOM = CD.LayerKey[layer]; RETURN [key = $cut OR key = $cut2] END; MaterialSeparation: PROC [cell: Core.CellType, state: State, w1, w2: Wire, loc1, loc2: CD.Position, orient1, orient2: CD.Orientation] ~ BEGIN EachRect1: PROC [r1: CD.Rect, l1: CD.Layer] = { EachRect2: PROC [r2: CD.Rect, l2: CD.Layer] = { r, s: CD.Rect; sep: CD.Number; IF state.abort^ THEN ERROR ABORTED; IF w1 = w2 AND NOT (IsCut[l1] AND IsCut[l2]) THEN RETURN; sep _ CDSimpleRules.MinDist [l1, l2 ! CDSimpleRules.NotKnown => sep _ 0]; IF sep = 0 THEN RETURN; r2 _ CDBasics.MapRect [ r2, CDBasics.SizeOfRect[r2], -- This parameter looks wrong to me [BS] orient2, loc2]; IF w1 = w2 AND l1 = l2 AND r1 = r2 THEN RETURN; r _ CDBasics.Extend [r1, sep / 2]; s _ CDBasics.Extend [r2, sep / 2]; IF ((r.x1 MaterialToRecordCellSeparation [self, state, actual, wire, father, materialLoc, cellLoc, materialOrient, cellOrient]; CoreClasses.transistorCellClass => MaterialToTransistorSeparation [self, state, actual, wire, father, materialLoc, cellLoc, materialOrient, cellOrient]; CoreClasses.unspecifiedCellClass => MaterialToUnspecifiedSeparation [self, state, actual, wire, father, materialLoc, cellLoc, materialOrient, cellOrient]; ENDCASE => Send [] ELSE Send [] END; -- MaterialToCellSeparation MaterialToTransistorSeparation: MaterialToCellProc ~ BEGIN wbb, tbb: CD.Rect; -- bounding boxes tw: Wire; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend[tbb,state.maxSeparation], wbb]] END; -- Intersect IF state.abort^ THEN ERROR ABORTED; IF (CoreProperties.GetProp [self.properties, doNotAnalyse] # NIL) THEN RETURN; wbb _ AtomicWireHull [wire, state]; wbb _ CDBasics.MapRect [itemInCell: wbb, cellSize: CDBasics.SizeOfRect [wbb], cellInstOrient: materialOrient, cellInstPos: materialLoc]; tbb _ TransistorHull [self, state]; tbb _ CDBasics.MapRect [itemInCell: tbb, cellSize: CDBasics.SizeOfRect [tbb], cellInstOrient: cellOrient, cellInstPos: cellLoc]; IF (NOT Intersect[]) THEN RETURN; tw _ BindTransistor [self.public, actual, state]; FOR i: NAT IN [0 .. tw.size) DO MaterialSeparation [state: state, cell: father, w1: tw[i], w2: wire, loc1: cellLoc, loc2: materialLoc, orient1: cellOrient, orient2: materialOrient] ENDLOOP END; -- MaterialToTransistorSeparation MaterialToUnspecifiedSeparation: MaterialToCellProc ~ BEGIN NULL END; -- MaterialToUnspecifiedSeparation MaterialToRecordCellSeparation: MaterialToCellProc ~ BEGIN wbb, cbb: CD.Rect; -- bounding boxes boundInternal: Wire; propagatedActuals: WireSet; -- one wire per subcell bindingTable: RefTab.Ref; cellData: CoreClasses.RecordCellType _ NARROW [self.data]; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend[cbb,state.maxSeparation], wbb]] END; -- Intersect IF state.abort^ THEN ERROR ABORTED; IF (CoreProperties.GetProp [self.properties, doNotAnalyse] # NIL) OR (cellData.internal.size = 0) THEN RETURN; wbb _ AtomicWireHull [wire, state]; wbb _ CDBasics.MapRect [itemInCell: wbb, cellSize: CDBasics.SizeOfRect [wbb], cellInstOrient: materialOrient, cellInstPos: materialLoc]; cbb _ RecordCellHull [self, state]; cbb _ CDBasics.MapRect [itemInCell: cbb, cellSize: CDBasics.SizeOfRect [cbb], cellInstOrient: cellOrient, cellInstPos: cellLoc]; IF (NOT Intersect[]) THEN RETURN; bindingTable _ CreateBindingTable [actual: actual, public: self.public]; boundInternal _ BindInternal [bindingTable, cellData.internal, state]; FOR i: NAT IN [0 .. boundInternal.size) DO MaterialSeparation [state: state, cell: father, w1: boundInternal[i], w2: wire, loc1: cellLoc, loc2: materialLoc, orient1: cellOrient, orient2: materialOrient] ENDLOOP; propagatedActuals _ NEW [WireSetRec[cellData.size]]; FOR sub: NAT IN [0 .. cellData.size) DO propagatedActuals[sub] _ PropagateBinding [state, bindingTable, cellData.instances[sub].actual] ENDLOOP; FlushBindingTable [bindingTable]; FOR sub: NAT IN [0 .. cellData.size) DO cdInst: CD.Instance = CoreGeometry.GetTransf[state.attributes, cellData.instances[sub]]; IF (cdInst = NIL) THEN coreMess ["Core record subcell has no geometry (6)", cellData]; MaterialToCellSeparation [self: cellData.instances[sub].type, state: state, actual: propagatedActuals[sub], wire: wire, father: self, materialLoc: materialLoc, cellLoc: CDBasics.AddPoints [cellLoc, cdInst.trans.off], materialOrient: materialOrient, cellOrient: CDBasics.ComposeOrient [cellOrient, cdInst.orientation]] ENDLOOP END; -- MaterialToRecordCellSeparation CellToCellSeparation: CellToCellProc ~ BEGIN Send: PROC ~ INLINE BEGIN check: REF CellToCellProc _ NARROW [CoreProperties.GetProp [self.properties, cellToCell]]; IF check = NIL THEN check _ NARROW [CoreProperties.GetProp [self.class.properties, cellToCell]]; IF check = NIL THEN BEGIN obj: CD.Object = GetObject [self, state]; IF obj = NIL THEN RETURN; -- Cell contains no rectangles check _ NEW [CellToCellProc _ UnspecifiedToAnyClassSeparation]; MarkError [self, state, [obj.class.interestRect[obj], "Cell has no provisions to be checked"]] END; check^ [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient] END; -- Send SELECT otherCell.class FROM CoreClasses.unspecifiedCellClass => RETURN; CoreClasses.recordCellClass => NULL; -- default case CoreClasses.transistorCellClass => -- nasty case IF self.class = CoreClasses.transistorCellClass THEN BEGIN IF fast THEN TransistorToTransistorSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient] ELSE Send []; RETURN END ELSE BEGIN -- swap selfZ: Core.CellType = self; selfActualZ: Wire = selfActual; selfLocZ: CD.Position = selfLoc; selfOrientZ: CD.Orientation = selfOrient; self _ otherCell; otherCell _ selfZ; selfActual _ otherActual; otherActual _ selfActualZ; selfLoc _ otherLoc; otherLoc _ selfLocZ; selfOrient _ otherOrient; otherOrient _ selfOrientZ END; ENDCASE => ERROR; IF fast THEN SELECT self.class FROM CoreClasses.recordCellClass => RecordCellToRecordCellSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient]; CoreClasses.transistorCellClass => TransistorToRecordCellSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient]; CoreClasses.unspecifiedCellClass => UnspecifiedToAnyClassSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient]; ENDCASE => Send [] ELSE Send [] END; -- CellToCellSeparation TransistorToRecordCellSeparation: CellToCellProc ~ BEGIN cbb1, cbb2: CD.Rect; -- bounding boxes othersCellData: CoreClasses.RecordCellType _ NARROW [otherCell.data]; othersBindingTable: RefTab.Ref; tw: Wire; othersBoundInternal: Wire; ownName: Rope.ROPE = CoreOps.GetCellTypeName [self]; otherName: Rope.ROPE = CoreOps.GetCellTypeName [otherCell]; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.maxSeparation], cbb2]] END; -- Intersect IF state.abort^ THEN ERROR ABORTED; IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [otherCell.properties, doNotAnalyse]#NIL) THEN RETURN; IF debug THEN BEGIN IO.PutF [stream: dLog, format: "Checking transistor %l%g%l vs. %g. ", v1: IO.rope ["e"], v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName], v3: IO.rope ["E"], v4: IF otherName = NIL THEN IO.refAny [otherCell] ELSE IO.rope [otherName]]; IO.PutF [stream: dLog, format: "Rel. trans.orient: %g, dist: (%g, %g).\n", v1: IO.int [CDBasics.DecomposeOrient [otherOrient, selfOrient]], v2: IO.int [(otherLoc.x - selfLoc.x) / previousTechnology.lambda], v3: IO.int [(otherLoc.y - selfLoc.y) / previousTechnology.lambda]] END; cbb1 _ RecordCellHull [self, state]; cbb1 _ CDBasics.MapRect [itemInCell: cbb1, cellSize: CDBasics.SizeOfRect [cbb1], cellInstOrient: selfOrient, cellInstPos: selfLoc]; cbb2 _ RecordCellHull [otherCell, state]; cbb2 _ CDBasics.MapRect [itemInCell: cbb2, cellSize: CDBasics.SizeOfRect [cbb2], cellInstOrient: otherOrient, cellInstPos: otherLoc]; IF (NOT Intersect[]) THEN RETURN; IF state.abort^ THEN ERROR ABORTED; othersBindingTable _ CreateBindingTable [actual: otherActual, public: otherCell.public]; othersBoundInternal _ BindInternal [othersBindingTable, othersCellData.internal, state]; tw _ BindTransistor [self.public, selfActual, state]; FlushBindingTable [othersBindingTable]; FOR i: NAT IN [0 .. tw.size) DO FOR j: NAT IN [0 .. othersCellData.internal.size) DO MaterialSeparation [state: state, cell: father, -- the cell getting the error flag -- w1: tw[i], w2: othersBoundInternal[j], loc1: selfLoc, loc2: otherLoc, orient1: selfOrient, orient2: otherOrient] ENDLOOP ENDLOOP END; -- TransistorToRecordCellSeparation TransistorToTransistorSeparation: CellToCellProc ~ BEGIN cbb1, cbb2: CD.Rect; -- bounding boxes tw1, tw2: Wire; ownName: Rope.ROPE = CoreOps.GetCellTypeName [self]; otherName: Rope.ROPE = CoreOps.GetCellTypeName [otherCell]; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.maxSeparation], cbb2]] END; -- Intersect IF state.abort^ THEN ERROR ABORTED; IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [otherCell.properties, doNotAnalyse]#NIL) THEN RETURN; cbb1 _ RecordCellHull [self, state]; cbb1 _ CDBasics.MapRect [itemInCell: cbb1, cellSize: CDBasics.SizeOfRect [cbb1], cellInstOrient: selfOrient, cellInstPos: selfLoc]; cbb2 _ RecordCellHull [otherCell, state]; cbb2 _ CDBasics.MapRect [itemInCell: cbb2, cellSize: CDBasics.SizeOfRect [cbb2], cellInstOrient: otherOrient, cellInstPos: otherLoc]; IF (NOT Intersect[]) THEN RETURN; IF state.abort^ THEN ERROR ABORTED; tw1 _ BindTransistor [self.public, selfActual, state]; tw2 _ BindTransistor [otherCell.public, otherActual, state]; FOR i: NAT IN [0 .. tw1.size) DO FOR j: NAT IN [0 .. tw2.size) DO MaterialSeparation [state: state, cell: father, -- the cell getting the error flag -- w1: tw1[i], w2: tw2[j], loc1: selfLoc, loc2: otherLoc, orient1: selfOrient, orient2: otherOrient] ENDLOOP ENDLOOP END; -- TransistorToTransistorSeparation UnspecifiedToAnyClassSeparation: CellToCellProc ~ BEGIN NULL END; -- UnspecifiedToAnyClassSeparation RecordCellToRecordCellSeparation: CellToCellProc ~ BEGIN cbb1, cbb2: CD.Rect; -- bounding boxes ownCellData: CoreClasses.RecordCellType _ NARROW [self.data]; othersCellData: CoreClasses.RecordCellType _ NARROW [otherCell.data]; ownBindingTable, othersBindingTable: RefTab.Ref; ownBoundInternal, othersBoundInternal: Wire; ownName: Rope.ROPE = CoreOps.GetCellTypeName [self]; otherName: Rope.ROPE = CoreOps.GetCellTypeName [otherCell]; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.maxSeparation], cbb2]] END; -- Intersect IF state.abort^ THEN ERROR ABORTED; IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [otherCell.properties, doNotAnalyse]#NIL) THEN RETURN; IF debug THEN BEGIN IO.PutF [stream: dLog, format: "Checking cell %l%g%l vs. %g. ", v1: IO.rope ["e"], v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName], v3: IO.rope ["E"], v4: IF otherName = NIL THEN IO.refAny [otherCell] ELSE IO.rope [otherName]]; IO.PutF [stream: dLog, format: "Rel. trans.orient: %g, dist: (%g, %g).\n", v1: IO.int [CDBasics.DecomposeOrient [otherOrient, selfOrient]], v2: IO.int [(otherLoc.x - selfLoc.x) / previousTechnology.lambda], v3: IO.int [(otherLoc.y - selfLoc.y) / previousTechnology.lambda]] END; cbb1 _ RecordCellHull [self, state]; cbb1 _ CDBasics.MapRect [itemInCell: cbb1, cellSize: CDBasics.SizeOfRect [cbb1], cellInstOrient: selfOrient, cellInstPos: selfLoc]; cbb2 _ RecordCellHull [otherCell, state]; cbb2 _ CDBasics.MapRect [itemInCell: cbb2, cellSize: CDBasics.SizeOfRect [cbb2], cellInstOrient: otherOrient, cellInstPos: otherLoc]; IF (NOT Intersect[]) THEN RETURN; IF state.abort^ THEN ERROR ABORTED; ownBindingTable _ CreateBindingTable [actual: selfActual, public: self.public]; othersBindingTable _ CreateBindingTable [actual: otherActual, public: otherCell.public]; ownBoundInternal _ BindInternal [ownBindingTable, ownCellData.internal, state]; othersBoundInternal _ BindInternal [othersBindingTable, othersCellData.internal, state]; FlushBindingTable [ownBindingTable]; FlushBindingTable [othersBindingTable]; FOR i: NAT IN [0 .. ownCellData.internal.size) DO FOR j: NAT IN [0 .. othersCellData.internal.size) DO MaterialSeparation [state: state, cell: father, -- the cell getting the error flag -- w1: ownBoundInternal[i], w2: othersBoundInternal[j], loc1: selfLoc, loc2: otherLoc, orient1: selfOrient, orient2: otherOrient] ENDLOOP ENDLOOP END; -- RecordCellToRecordCellSeparation ComputeMaxSeparation: PROC [technology: CD.Technology] RETURNS [maxSeparation: CD.Number] ~ BEGIN maxSeparation _ 0; FOR s1: CD.Layer IN CD.Layer DO IF CD.LayerTechnology[s1] = technology THEN FOR s2: CD.Layer IN CD.Layer DO IF CD.LayerTechnology[s2] = technology THEN BEGIN sep: CD.Number = CDSimpleRules.MinDist [s1, s2 ! CDSimpleRules.NotKnown => LOOP]; -- try to filter out inappropriate layers maxSeparation _ MAX [maxSeparation, sep] END ENDLOOP ENDLOOP END; -- ComputeMaxSeparation RegisterSeparationProcs: PROC ~ BEGIN CoreClasses.recordCellClass.properties _ CoreProperties.PutProp [ on: CoreClasses.recordCellClass.properties, prop: matToCell, value: NEW [MaterialToCellProc _ MaterialToRecordCellSeparation]]; CoreClasses.transistorCellClass.properties _ CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, matToCell, NEW [MaterialToCellProc _ MaterialToTransistorSeparation]]; CoreClasses.unspecifiedCellClass.properties _ CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, matToCell, NEW [MaterialToCellProc _ MaterialToUnspecifiedSeparation]]; CoreClasses.recordCellClass.properties _ CoreProperties.PutProp [ on: CoreClasses.recordCellClass.properties, prop: cellToCell, value: NEW [CellToCellProc _ RecordCellToRecordCellSeparation]]; CoreClasses.transistorCellClass.properties _ CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, cellToCell, NEW [CellToCellProc _ TransistorToRecordCellSeparation]]; CoreClasses.unspecifiedCellClass.properties _ CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, cellToCell, NEW [CellToCellProc _ UnspecifiedToAnyClassSeparation]]; END; -- RegisterSeparationProcs ClearErrors: PROC [obj: Core.CellType, state: State] ~ BEGIN IF state.shy THEN RETURN; IF (state.design # NIL) THEN CDErrors.RemoveMessages [design: state.design, ob: GetObject [obj, state], owner: DRVkey]; obj.properties _ CoreProperties.PutProp [obj.properties, DRVkey, NIL] END; -- ClearErrors MarkError: PROC [obj: Core.CellType, state: State, e: ErrorRect] ~ BEGIN objName: Rope.ROPE = CoreOps.GetCellTypeName [obj]; cdObj: CD.Object _ GetObject [obj, state]; violations: DRV _ NARROW [CoreProperties.GetProp [obj.properties, DRVkey]]; longMsg: Rope.ROPE _ IO.PutFR ["%g: %g", IF objName=NIL THEN IO.refAny [obj] ELSE IO.rope [objName], IO.rope[e.msg]]; done: BOOL _ FALSE; -- initialization of done important eLog.PutF1 ["%g\n", IO.rope [longMsg]]; IF (violations = NIL) THEN BEGIN violations _ NEW [DesignRuleViolation]; violations.count _ 1; violations.places _ LIST [e] END ELSE BEGIN violations.count _ violations.count.SUCC; violations.places _ CONS [e, violations.places] END; state.globalErrorCount _ state.globalErrorCount.SUCC; IF state.abort^ THEN ERROR ABORTED; IF state.globalErrorCount < errorFeedbackCutOff THEN mLog.Put1 [IO.char ['|]]; obj.properties _ CoreProperties.PutProp [on: obj.properties, prop: DRVkey, value: violations]; IF (state.design # NIL) AND (cdObj # NIL) THEN done _ CDErrors.IncludeMessage [ design: state.design, ob: cdObj, rect: CDBasics.Extend [e.r, 4], message: e.msg, owner: DRVkey].done; IF NOT done THEN longMsg _ Rope.Cat [r1: longMsg, r2: " (not flagged in the layout)"]; IF debug THEN dLog.PutF1 ["%g\n", IO.rope [longMsg]] END; -- MarkError PrintError: CoreProperties.PropPrintProc ~ BEGIN IO.PutF1 [stream: to, format: "SoS error count = %g ", value: IO.int [NARROW[val,DRV].count]] END; -- PrintError PrintChecked: CoreProperties.PropPrintProc ~ BEGIN IO.Put1 [stream: to, value: IO.rope ["Design rules checked"]] END; -- PrintChecked RegisterProperties: PROC ~ BEGIN IF (NOT CDProperties.RegisterProperty [DRVkey, $gbb]) OR (NOT CDProperties.RegisterProperty [checked, $gbb]) OR (NOT CDProperties.RegisterProperty [bbKey, $gbb]) OR (NOT CDProperties.RegisterProperty [cellHull, $gbb]) OR (NOT CDProperties.RegisterProperty [trace, $gbb]) THEN IF NOT debug THEN mLog.Put1 [IO.rope["SoS probably is being reexecuted.\n"]]; CoreProperties.StoreProperties [prop: DRVkey, properties: CoreProperties.Props [[CoreProperties.propPrint, NEW [CoreProperties.PropPrintProc _ PrintError]]]]; CoreProperties.StoreProperties [prop: checked, properties: CoreProperties.Props [[CoreProperties.propPrint, NEW [CoreProperties.PropPrintProc _ PrintChecked]]]]; END; -- RegisterProperties for the interesting ones (modified by BS on October 26, 1986 8:58:33 pm PST) StartLog: PROC ~ BEGIN viewer: ViewerTools.Viewer; dummy: IO.STREAM; name: Rope.ROPE; viewer _ ViewerTools.FindExistingViewer ["Terminal"]; name _ IF viewer # NIL THEN "Terminal" ELSE "Son of Spinifex"; [in: dummy, out: mLog] _ ViewerIO.CreateViewerStreams [name, viewer] END; -- StartLog Debug: PROC ~ BEGIN viewer: ViewerTools.Viewer; dummy: IO.STREAM; debug _ TRUE; viewer _ ViewerTools.FindExistingViewer ["SoS debug"]; [in: dummy, out: dLog] _ ViewerIO.CreateViewerStreams ["SoS debug", viewer]; IF dLog = NIL THEN dLog _ mLog END; -- Debug PrintWire: PROC [w: Wire] ~ BEGIN dLog.PutF ["%g %g %g\n", IO.rope [CoreOps.GetShortWireName[w]], IO.card [LOOPHOLE[w]], IO.refAny [w]]; FOR i: NAT IN [0 .. w.size) DO dLog.PutF ["\t(%g) %g %g %g\n", IO.card [i], IO.rope [CoreOps.GetShortWireName[w[i]]], IO.card [LOOPHOLE[w[i]]], IO.refAny [w[i]]] ENDLOOP END; -- PrintWire PrintEntryShort: RefTab.EachPairAction ~ BEGIN dLog.PutF ["public: %g\t actual: %g\n", IO.card [LOOPHOLE[key]], IO.card [LOOPHOLE[val]]]; RETURN [FALSE] END; -- PrintEntryShort PrintEntryLong: RefTab.EachPairAction ~ BEGIN PrintWire [NARROW [key, Wire]]; PrintWire [NARROW [val, Wire]]; dLog.Put1 [IO.char['\n]]; RETURN [FALSE] END; -- PrintEntryLong PrintBinding: PROC [bindingTable: RefTab.Ref] ~ BEGIN [] _ RefTab.Pairs [bindingTable, PrintEntryShort] END; -- PrintBinding StartLog []; -- Should be first, so problems can be communicated IF debug THEN Debug[]; RegisterProperties []; -- CD & Core RegisterPruningProcs []; RegisterAnalysisProcs []; RegisterSeparationProcs [] -- Core END. =SoSImpl.mesa Agewmetrhtos Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Giordano Bruno Beretta, October 17, 1985 4:37:14 pm PDT gbb January 9, 1987 2:30:29 pm PST Louis Monier June 5, 1986 8:21:05 pm PDT Bertrand Serlet October 26, 1986 11:16:17 pm PST SoS is the Son of Spinifex. It is a hierarchical design rule checker that monkeys around in a Core design and tries to find all the ChipNDale geometry in order to check as many design rules as it possibly can. Gli uomini vollero piuttosto le tenebre che la luce (Giovanni, III, 19.) Note: Output to noWhereStream passes all I/O code, and hence is very slow ! RECORD [count: INT _ 0, places: LIST OF ErrorRect] RECORD [r: CD.Rect, msg: Rope.ROPE] SoS keys. For simplicity they are registered both with ChipNDale and Core and have the same name Attached to CellTypes. Error report may be processed by clients. Cache for the bounding box. Debugging only. Attached to ChipNDale objects processed. SoS analysis procedures (one for each ChpiNDale object class and one for each Core cell class) ChipNDale keys used by the Son of Spinifex Core keys used by the Son of Spinifex undefLayer, highLightShade, highLightError, pinRepresentation, outlineLayer, selectionLayer, commentLayer Warning: CD.undefLayer could not be included in the list because of a minor bug in the compiler, but it should so once the bug is fixed. Activation Procedure The external interface. We now create a shadow cell on which we work. If we do not abort, we replace the original cell by the shadow cell, if we abort then the original cell is left untouched. returnedCell _ cell; -- to be implemented I hope this figure is large enough. I did not make it too large for speed reasons. DRC. Clean up. Remember that we assigned returnedCell _ cell. If the Core data struckture is busted, we have returned with (quantity = 1), because 1 > 0 and we do not know whether what we found in it is meaningful. The above statement is obviously wrong, in that state.globalErrorCount tallies only newly found errors. The right thing to do is to traverse the Core data structure and count the errors. However this has to be done by the client, since there are people that do not want the data structures to be modified but just want a boolean answer. Those clients who want a detailed error report have to do it by themselves, as they have always done. returnedCell _ shadowCell; -- to be implemented Remember that we assigned returnedCell _ cell. Actions on ChipNDale Objects Actions on Core Objects Used to create a shadow cell on which SoS operates. This allows to preserve the virginity of the cell we are handed over in the case of an abortion. PROC [self: Core.CellType, state: State, actual: Wire, loc: CD.Position, trans.orient: CD.Orientation]; [self: Core.CellType, state: State, actual: Wire, loc: CD.Position, trans.orient: CD.Orientation] To be able to compare wires by comparing refs, the bound internal is the actual wire with the properties of the internal wire. Note that in this way the properties of the internal wire are propagated up to the actual wire; hence the actual wire has to be restored when popping up. The propagated actuals must be determined here once for all, because new actual wires will differ from call to call. The catechism states, that the recursion on the subcells has to take place at the end of the check among the internal of self and its subcells, in the procedure MaterialToCellSeparation. Nomen est omen, so I put the recursion step in here where it makes more sense to me. Furthermore the catechism states that the world is top-down, and that hence the recursion step is done at the end. Beside yielding a better program structure, this allows for more agressive (i.e. geometric) pruning. Check the internal. Since in this case the error will go into the cell itself, we produce cell relative coordinates by appropriately setting loc1, loc2, orient1, and orient2. Although the separation rules do not have to hold for aequipotential pieces of material, we have to artificially check the geometry of each wire against itself in order to be able to verify the separation of cuts. Check the separation between the internal of self and its subcells. Check the separation between the subcells of self. Wires that were propagated down under recursion now contain all the geometry of the leaf cells. Here we restore the status quo ante. [self: Core.CellType, state: State, actual: Wire, loc: CD.Position, trans.orient: CD.Orientation] Transistors are atomic ChipNDale objects and are supposed to be correct by construction. [self: Core.CellType, state: State, actual: Wire, loc: CD.Position, trans.orient: CD.Orientation] (self.class = CoreClasses.unspecifiedCellClass) OR (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL); Called in the module initialisation part. Pruning PROC [self: Core.CellType, state: State] RETURNS [h: CD.Rect _ CDBasics.empty] Coputes the bounding box of the rectangles of material that make up a wire. Wire is not atomic. The extractor might have been fed something he did not like. [] _ Msg ["Your Core data structure is busted! You do not lose yet."]; PROC [self: Core.CellType, state: State] RETURNS [h: CD.Rect _ CDBasics.empty] Needed for recursion. [self: Core.CellType, state: State] RETURNS [h: CD.Rect _ CDBasics.empty] Atomic wire not expected here. The extractor might have been fed something he did not like. Note: because of the program flow a run time this recursion does not really propagate, since at this point stored # NIL. [self: Core.CellType, state: State] RETURNS [h: CD.Rect _ CDBasics.empty] Atomic wire not expected here. The extractor might have been fed something he did not like. [self: Core.CellType, state: State] RETURNS [h: CD.Rect _ CDBasics.empty] Called in the module initialisation part. Binding Called to propagate the binding on level down. Applied to the actual wire. For elements of sequence. If an actual wire of a subcell is not in the binding table of the containing cell, this wire starts in the containing cell. Hence we create a new wire. It is inserted into the binding table because two cells may share it. For full sequence. If the wire is not in the binding table, it does not interface, hence it has not to be bound. To be able to compare wires by comparing refs, the bound internal is the actual wire with the properties of the internal wire. Note that in this way the properties of the internal wire are propagated up to the actual wire; hence the actual wire has to be restored when popping up. We have to preserve the geometry that is hanging on the public subset of the wire. boundInternal[i] _ CoreOps.SetShortWireName [wire: boundInternal[i], name: CoreOps.GetShortWireName[internal[i]]]; boundInternal[i].properties _ CopyGeomProps [internal[i].properties, boundInternal[i].properties, state] Please read comments in BindInternal. Separation and Width Check Procedures PROC [self: Core.CellType, state: State, actual, wire: Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation]; PROC [self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation]; The trans.off and orientation are needed place the error rectangle. At the moment our simplicistic approach does not have any knowledge of the topology. Therefore we cannot really test this rule. << Total Hack. Yuk. >> The following is a cochonnerie to avoid flagging large split contacts that may occur in CMOS-B designs. The heuristics is there because of two reasons: a) I cannot know from which contact class the cut is coming, b) I do not want to put in here an even worse cochonnerie by introducing a dependency of SoS on technologies by importing a specific technology. Note that I am using an atom because many users hide the technology from SoS. If the splits are flagged then the atom in ChipNDale may have changed. Note that I am using atoms because many users hide the technology from SoS. If violations from cuts arenot flagged then the atom in ChipNDale may have changed. The arguments of this function look very strange to me: I would expect, instead of loc1, orient1 to have a CoreGeometry.Transformation (which is now a CD.Instance). But anyway this will have to be fixed for CD24. [BS at October 26, 1986 10:29:05 pm PST] If two unrelated rectangles of material intersect, an error is flagged. The parameter cell selects the cell receiving possible error messages. It must be the father of the cell containing w1 and the cell containing w2. The order of the next two statements is very important. Probe 2: Set a break point after this line to see why a wrong violation is flagged. We cannot skip the verification in the w1 = w2. In fact, in Core and Sinix, cuts are represented as material rectangles in wires. So to SoS two cuts violating the separation rule will alway look as two aequipotential rectangles. [self: Core.CellType, state: State, actual, wire: Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation] For convenience. [self: Core.CellType, state: State, actual, wire: Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation] Find the bounding box of wire and check whether it has a non-empty intersection with the internal wire of the cell. Check each element of the transistor wire of the cell against each element of wire. [self: Core.CellType, state: State, actual, wire: Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation] [self: Core.CellType, state: State, actual, wire: Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation] Find the bounding box of wire and check whether it has a non-empty intersection with the internal wire of the cell. Check each element of the internal wire of the cell against each element of wire. The propagated actuals must be determined here once for all, because new actual wires will differ from call to call. Check intersections between subcells of cell and wire. [self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation] For convenience. [self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation] Preamble. Check the separation between the internal of transistor and cell2. [self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation] Preamble. Check the separation between the internal of cell1 and cell2. [self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation] [self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation] Preamble. Check the separation between the internal of cell1 and cell2. Initialises the separation table. This is where the technology comes in ! Called in the module initialisation part. Design Rule Violation Flagging Removes the error rectangles from a given ChipNDale object (e.g. a cell). Puts an error rectangle r into the ChipNDale object obj, and maintains an error counter in the Core structure. IF state.shy AND (NOT Msg [longMsg]) THEN panic Note that when the shadow cell will have been implemented and we will have handed it over to the client, we can delegate this paranoia to the client. Property Registrations Registers all properties. ChipNDale somebody is already using it. This check is necessary because in some future ChipNDale and Core may get the same property scope and they will then need different keys. Core Logging & Debugging Initialises the log streams. Should cater for errors IO.Error [$Failure, NIL], IO.Error [StreamClosed]. Callable from the interpreter. Creates and enebles the degug log. For debugging purposes. [key: Key, val: Val] RETURNS [quit: BOOLEAN] [key: Key, val: Val] RETURNS [quit: BOOLEAN] For debugging purposes. Initialisation gbb April 3, 1986 3:21:47 pm PST Increased extension of error highlight rectangles from 1 to 4 units. changes to: MarkError gbb June 5, 1986 7:17:56 pm PDT Although the separation rules do not have to hold for aequipotential pieces of material, we have to artificially check the geometry of each wire against itself in order to be able to verify the separation of cuts. We cannot skip the verification in the w1 = w2. In fact, in Core and Sinix, cuts are represented as material rectangles in wires. So to SoS two cuts violating the separation rule will alway look as two aequipotential rectangles. changes to: DIRECTORY: added GetLayer from CDSimpleRules to find the cut layers independently of the technology, CheckDesignRules: added two fields to the state record in order to hold the fileds of material that may not violate the separation rules even if according to the contents of Core it is electrically connected, CheckRecord: each wire is now verified also against himself, MaterialSeparation: the aequipotentiality test has been moved inside the inner loop in order to be able to combine it with a "not okIfConnected", NotCuts (local of MaterialSeparation): new. gbb June 9, 1986 6:02:28 pm PDT Made more robust for the case of a missing design. changes to: CheckDesignRules: added new parameter technology, GetObject: detect design=NIL gbb June 10, 1986 10:07:56 am PDT Tracked change in Sinix changes to: ClearErrors: uses heuristics to find corresponding ChipNDale cell gbb June 14, 1986 4:22:26 pm PDT Added debugging aids to exceptions changes to: EXPORTS, CheckDesignRules, CheckRecord, CreateBindingTable, MaterialToRecordCellSeparation, DIRECTORY, IMPORTS, FindGeometry, FindCDRect, Send (local of FindCDRect), FindRectGeom, MarkError, PrintChecked, StartLog, Msg1, MsgF, Debug gbb June 15, 1986 10:53:50 am PDT Added some piggy heuristics to avoid flagging large split contacts that may occur in CMOS-B designs. changes to: WidthCheck gbb June 15, 1986 11:49:06 am PDT If SoS is asked to be shy and the appropriate software is loaded, SoS never writes anything on the screen. If the software is not there it uses the screen anyway to save those preciuos 23 GFIs. changes to: DIRECTORY, IMPORTS, EXPORTS, CheckDesignRules, Send (local of FindCDRect), CheckRecord, CreateBindingTable, WidthCheck, MaterialToRecordCellSeparation, MarkError, StartLog, Msg gbb June 16, 1986 1:13:56 pm PDT Cleaned up client interface before casting in concrete. changes to: DIRECTORY, EXPORTS, CheckDesignRules, CheckRecord, AtomicWireHull, CreateBindingTable, WidthCheck, NotCuts (local of MaterialSeparation), MaterialSeparation, MaterialToRecordCellSeparation, MarkError, StartLog, Msg gbb June 19, 1986 3:05:21 pm PDT Worked around bug in Voice server's software. changes to: CheckDesignRules. gbb June 23, 1986 11:00:40 am PDT Tracked changes in Finch. changes to: DIRECTORY, Msg gbb August 29, 1986 8:04:36 pm PDT Added an error log. changes to: DIRECTORY, IMPORTS, EXPORTS, CheckDesignRules: Open & Close, MarkError: puts long message in error log. Κ+Ϊ˜codešœΠeg ™KšœH™HKšœ7™7K™"K™(K™0K˜—Icode2™ΡIquotešΟsH™HšΟk ˜ KšœŸœ ˜Kšœ Ÿœ˜KšŸœ˜K˜Kšœ ˜ Kšœ Ÿœ ˜Kšœ Ÿœ"˜0K˜ Kšœ ˜ K˜KšœŸœ)˜Kšœ £ œ™—šΟnœŸœŸœ/˜BK™)—šœŸœ,˜7K™—šœŸœ;˜FKšœ9™9—L™^Kšœ Ÿœ2˜@Kšœ Ÿœ.˜Kš œ ŸœŸœŸœŸœŸœ˜JšΟb™KšœXŸœŠ™ζKšœŸœŸœ‘ ˜CK™•Kš ŸœŸœŸœŸœŸœŸœ‘ ˜PKšŸœPŸœ>˜”KšŸ˜—N˜—Kšœ&˜&K˜KšŸœ‘ ˜—š €œŸœ ŸœŸœŸœŸœŸ˜;KšœŸœŸœ˜K™‘KšŸœ Ÿœ ˜"KšŸœ˜—Lšœώ™ώš €œŸœ?ŸœŸœŸ˜K™GKšœ£œb£œ£œ™’š€ œŸœŸœ Ÿœ ˜/š€ œŸœŸœ Ÿœ ˜/KšœŸœ˜KšœŸœ˜KšŸœŸœŸœŸœ˜#K™7Kš Ÿœ ŸœŸœ Ÿœ ŸœŸœ˜9KšœI˜IKšŸœ ŸœŸœ˜šœ˜Kšœ˜Kšœ‘(˜AKšœ ˜ Kšœ˜—Kš Ÿœ Ÿœ Ÿœ ŸœŸœ˜/KšœE˜Eš ŸœŸœ Ÿœ ŸœŸœŸ˜KKšœ ŸœŸœ?˜mKšœ ŸœŸœ?˜mKš§œK™SKšœx˜xKšŸœ˜—K˜—KšœJ‘)œ,˜ŸKšœ(˜(Nšœ˜—NšŸœŸœŸœŸœ˜#Kšœ'£œ£œΈ™ζKš Ÿœ2ŸœŸœŸœ‘˜cKš Ÿœ2ŸœŸœŸœ‘˜cKš ŸœŸœqŸœŸœ‘&˜«Kšœ(˜(KšŸœ‘˜—š€œŸ˜4KšœeŸœ'Ÿœ ™Kšœ™š€œŸœŸœŸ˜KšœŸœŸœ7˜]KšŸœ ŸœŸœ Ÿœ=˜_šŸœ ŸœŸœŸ˜KšœŸœ"˜)Kš ŸœŸœŸœŸœ‘˜8KšœŸœ8˜CKšœ^˜^KšŸœ˜—Kšœ\˜\KšŸœ‘˜ —šŸœŸ˜ šŸœ Ÿ˜Kšœ”˜”Kšœ˜˜˜Kšœš˜šKšŸœ ˜——KšŸœ˜ KšŸœ‘˜ —š€œŸ˜:KšœeŸœ'Ÿœ ™Kšœ Ÿœ‘˜%Kšœ ˜ š € œŸœŸœŸœŸœŸ˜-KšŸœE˜KKšŸœ‘ ˜—NšŸœŸœŸœŸœ˜#KšŸœ;ŸœŸœŸœ˜NNšœ£œV™sKšœ#˜#Kšœˆ˜ˆKšœ#˜#Kšœ€˜€KšŸœŸœŸœŸœ˜!Nšœ1˜1NšœN£œ™SšŸœŸœŸœŸ˜Kšœ•˜•KšŸ˜—KšŸœ‘!˜&—š€œŸ˜;KšœeŸœ'Ÿœ ™KšŸ˜KšŸœ‘"˜'—š€œŸ˜;KšœeŸœ'Ÿœ ™Kšœ Ÿœ‘˜%Kšœ˜Kšœ‘˜3Kšœ˜Kšœ'Ÿœ ˜:š € œŸœŸœŸœŸœŸ˜-KšŸœE˜KKšŸœ‘ ˜—NšŸœŸœŸœŸœ˜#Kš Ÿœ;ŸœŸœŸœŸœ˜nNšœ£œV™sKšœ#˜#Kšœˆ˜ˆKšœ#˜#Kšœ€˜€KšŸœŸœŸœŸœ˜!NšœL£œ™QKšœH˜HKšœF˜FšŸœŸœŸœŸ˜*Kšœ ˜ KšŸœ˜—N™tKšœŸœ˜4šŸœŸœŸœŸ˜'Kšœ_˜_KšŸœ˜—Nšœ!˜!Nšœ(£œ£œ™6šŸœŸœŸœŸ˜'KšœŸœN˜XKšŸœ ŸœŸœ@˜VKšœ½˜½KšŸ˜—KšŸœ‘!˜&—š€œŸ˜,Lšœ‡Ÿœ$Ÿœ ™ΌK™š€œŸœŸœŸ˜KšœŸœŸœ8˜ZKšŸœ ŸœŸœ Ÿœ>˜`šŸœ ŸœŸœŸ˜KšœŸœ"˜)Kš ŸœŸœŸœŸœ‘˜8KšœŸœ4˜?Kšœ^˜^KšŸœ˜—Kšœl˜lKšŸœ‘˜ —šŸœŸ˜Kšœ$Ÿœ˜+KšœŸœ‘˜4šœ#‘ ˜0šŸœ.ŸœŸ˜:šŸœŸ˜ Kšœ†˜†—KšŸœ ˜ KšŸ˜KšŸ˜—šŸœŸœ‘˜Kšœ˜Kšœ˜Kšœ Ÿœ˜ Kšœ Ÿœ˜)Kšœ%˜%Kšœ4˜4Kšœ)˜)Kšœ4˜4KšŸœ˜——KšŸœŸœ˜—šŸœŸ˜ šŸœ Ÿ˜Kšœ¦˜¦Kšœͺ˜ͺKšœͺ˜ͺKšŸœ ˜——KšŸœ˜ KšŸœ‘˜—š€ œŸ˜8Lšœ‡Ÿœ$Ÿœ ™ΌKšœ Ÿœ‘˜'Kšœ-Ÿœ˜EKšœ˜Kšœ ˜ Kšœ˜KšœŸœ"˜4KšœŸœ'˜;š € œŸœŸœŸœŸœŸ˜-KšŸœG˜MKšŸœ‘ ˜—N™ KšŸœŸœŸœŸœ˜#Kš Ÿœ9ŸœŸœ>ŸœŸœŸœ˜‘šŸœŸœŸ˜KšŸœIŸœŸœ ŸœŸœŸœŸœŸœŸœŸœ ŸœŸœŸœŸœŸœ˜όKšŸœMŸœ?ŸœAŸœ<˜‘KšŸœ˜—Kšœ$˜$Kšœƒ˜ƒKšœ)˜)Kšœ…˜…KšŸœŸœŸœŸœ˜!Nšœ-£ œ£œ™BKšŸœŸœŸœŸœ˜#KšœX˜XKšœX˜XKšœ5˜5Kšœ'˜'šŸœŸœŸœŸ˜šŸœŸœŸœ%Ÿ˜4Kšœ0‘%œq˜ΖKšŸ˜—KšŸ˜—KšŸœ‘#˜(—š€ œŸ˜8Lšœ‡Ÿœ$Ÿœ ™ΌKšœ Ÿœ‘˜'Kšœ˜KšœŸœ"˜4KšœŸœ'˜;š € œŸœŸœŸœŸœŸ˜-KšŸœG˜MKšŸœ‘ ˜—N™ KšŸœŸœŸœŸœ˜#Kš Ÿœ9ŸœŸœ>ŸœŸœŸœ˜‘Kšœ$˜$Kšœƒ˜ƒKšœ)˜)Kšœ…˜…KšŸœŸœŸœŸœ˜!Nšœ-£œ£œ™=KšŸœŸœŸœŸœ˜#Kšœ6˜6Kšœ<˜<šŸœŸœŸœŸ˜ šŸœŸœŸœŸ˜ Kšœ0‘%œb˜·KšŸ˜—KšŸ˜—KšŸœ‘#˜(—š€œŸ˜7Lšœ‡Ÿœ$Ÿœ ™ΌKšŸ˜KšŸœ‘"˜'—š€ œŸ˜8Lšœ‡Ÿœ$Ÿœ ™ΌKšœ Ÿœ‘˜'Kšœ*Ÿœ ˜=Kšœ-Ÿœ˜EKšœ0˜0Kšœ,˜,KšœŸœ"˜4KšœŸœ'˜;š € œŸœŸœŸœŸœŸ˜-KšŸœG˜MKšŸœ‘ ˜—N™ KšŸœŸœŸœŸœ˜#Kš Ÿœ9ŸœŸœ>ŸœŸœŸœ˜‘šŸœŸœŸ˜KšŸœCŸœŸœ ŸœŸœŸœŸœŸœŸœŸœ ŸœŸœŸœŸœŸœ˜φKšŸœMŸœ?ŸœAŸœ<˜‘KšŸœ˜—Kšœ$˜$Kšœƒ˜ƒKšœ)˜)Kšœ…˜…KšŸœŸœŸœŸœ˜!Nšœ-£œ£œ™=KšŸœŸœŸœŸœ˜#KšœO˜OKšœX˜XKšœO˜OKšœX˜XKšœM˜MšŸœŸœŸœ"Ÿ˜1šŸœŸœŸœ%Ÿ˜4Kšœ0‘%œ˜ΤKšŸ˜—KšŸ˜—KšŸœ‘#˜(—š €œŸœŸœ ŸœŸœ Ÿ˜aK™IKšœ˜š ŸœŸœŸœŸœŸ˜šŸœŸœ"Ÿ˜+š ŸœŸœŸœŸœŸ˜šŸœŸœ"ŸœŸ˜1KšœŸœDŸœ‘)˜{KšœŸœ˜(KšŸ˜—KšŸ˜——KšŸ˜—KšŸœ‘˜—š€œŸœŸ˜%K™)Nšœt£ œ Ÿœ8˜ΑKšœq£ œŸœ8˜·Kšœs£ œŸœ9˜ΊNšœt£ œ Ÿœ6˜ΐKšœq£ œŸœ6˜ΆKšœs£ œŸœ5˜·KšŸœ‘˜——™š€ œŸœ&Ÿ˜KšœD˜DKšŸœ‘ ˜—š€œŸœŸ˜K™AKšœ˜KšœŸœŸœ˜KšœŸœ˜ Kšœ6˜6KšœL˜LKšŸœŸœŸœ ˜KšŸœ‘˜ —š€ œŸœ Ÿ˜!K™Kš œŸœ%ŸœŸœŸœ ˜fšŸœŸœŸœŸ˜Kš œ Ÿœ Ÿœ(ŸœŸœ Ÿœ˜‚KšŸ˜—KšŸœ‘ ˜—š€œŸ˜.KšœŸœŸœ™,Kš œ(ŸœŸœŸœŸœ˜ZKšŸœŸœ˜KšŸœ‘˜—š€œŸ˜-KšœŸœŸœ™,Kšœ ŸœŸœ˜?Kšœ Ÿœ ˜KšŸœŸœ˜KšŸœ‘˜—š€ œŸœŸ˜5K™Nšœ1˜1KšŸœ‘˜——šœ™Kšœ ‘3˜@KšŸœŸœ ˜Kšœ‘ ˜#KšœN‘˜U—LšŸœ˜™ K™EKšœ Οr ™—™Kšœώ£œ£œΈ™½Kšœ © œ£œ£ œ £œ'©œΐ© œ0©œ©œ|© œ$™½—™K™2Kšœ ©œ£ © œ™Z—™!K™Kšœ © œ6™M—™ K™"Kšœ ©Žœ©D™τ—™!KšœUŸœ ™dKšœ © ™—™!K™ΒKšœ ©3œ©g™Ό—™ K™7Kšœ ©jœ©N™β—™ K™-Kšœ ©œ™—™!K™Kšœ ©™—™"K™Kšœ ©-œ© œ!™s—K™—…— ώ