DIRECTORY CD USING [Number], CDBasics USING [ComposeTransform, Extend, Intersect, MapRect], CDProperties USING [RegisterProperty], Core USING [Properties], CoreClasses USING [CellInstance, recordCellClass, RecordCellType, transistorCellClass, unspecifiedCellClass], CoreGeometry USING [CellInstance, Decoration, EachInstanceProc, EnumerateGeometry, GetObject, GetTrans, Instance, Instances, Layer, Object, Rect], CoreOps USING [CopyWire, CreateWires, GetCellTypeName, GetShortWireName, SetShortWireName], CoreProperties USING [GetProp, propPrint, PropPrintProc, Props, PutProp, RegisterProperty, StoreProperties], Drc, DrcDebug USING [break, Debug, debug, dLog, PrintWire], FS USING [StreamOpen], IO USING [card, char, Close, Error, Flush, int, Put1, PutF, PutF1, PutFR, PutFR1, PutRope, refAny, rope, STREAM, time], RefTab USING [Create, EachPairAction, Fetch, Insert, Pairs, Ref, SeqIndex], Rope USING [ROPE, IsEmpty], TNT USING [BlowTNT, InitTNT, SweepTNT, TNT, UpdateTNT], ViewerIO USING [CreateViewerStreams], ViewerTools USING [FindExistingViewer, Viewer]; DrcImpl: CEDAR PROGRAM IMPORTS CDBasics, CDProperties, CoreClasses, CoreGeometry, CoreOps, CoreProperties, DrcDebug, FS, IO, RefTab, Rope, TNT, ViewerIO, ViewerTools EXPORTS Drc SHARES Drc ~ BEGIN OPEN Drc, TNT; debugBinding: BOOL _ FALSE; -- for debugging the binding only useTNT: BOOL ~ TRUE; -- for timing analysis only panic: SIGNAL ~ CODE; -- cannot communicate violations coreInconsistent: PUBLIC ERROR [reason: ROPE, torch: REF ANY] ~ CODE; coreMess: SIGNAL [reason: ROPE, torch: REF ANY] ~ CODE; -- clean up before issuing coreInconsistent mLog, eLog: IO.STREAM; -- messages and error log CdInsts: TYPE ~ CoreGeometry.Instances; CdObj: TYPE ~ CoreGeometry.Object; CoreInst: TYPE ~ CoreGeometry.CellInstance; FakeInst: TYPE ~ CoreGeometry.Instance; Layer: TYPE ~ CoreGeometry.Layer; ROPE: TYPE ~ Rope.ROPE; DRVkey: PUBLIC ATOM _ CoreProperties.RegisterProperty [$DrcError]; bbKey: ATOM _ CoreProperties.RegisterProperty [$DrcBB]; rectClass: ATOM ~ $Rect; pinClass: ATOM ~ $PinOb0; doNotAnalyse: ATOM ~ $DoNotDRC; identity: Transf; -- READONLY it is the default 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 WireInstance]; CheckDesignRules: PUBLIC PROC [cell: CoreCell, external: Wire, tech: Tech, stopFlag: REF BOOL _ NIL, layout: Layout] RETURNS [quantity: CARDINAL _ 0] ~ BEGIN ENABLE BEGIN IO.Error => IF ec = StreamClosed THEN BEGIN s: IO.STREAM; m: BOOL = (stream = mLog); IF m THEN StartLog [] ELSE DrcDebug.Debug []; s _ IF m THEN mLog ELSE DrcDebug.dLog; CONTINUE END; panic => GOTO paranoia END; state: State _ NEW [StateRec]; messy: BOOL _ FALSE; abortionCause, termination: ROPE; debugTorch: REF ANY; rootCellName: ROPE ~ CoreOps.GetCellTypeName [cell]; logFile: ROPE ~ "[]<>Temp>DRC>Genista.Log"; busted: ROPE ~ "\nDRC failed. Your Core data structure may be busted!"; eLog _ FS.StreamOpen [fileName: logFile, accessOptions: create, keep: 5, extendFileProc: NIL, remoteCheck: FALSE, wDir: NIL]; -- keep is 5 because of incremental use eLog.PutF ["Error log by Drc Genista. Root cell: %g, time: %g\n\n", IO.rope [rootCellName], IO.time []]; IF DrcDebug.debug THEN DrcDebug.dLog.PutF [format: "\n%g. cell: %l%g%l\n", v1: IO.time [], v2: IO.rope ["b"], v3: IF rootCellName = NIL THEN IO.refAny [cell] ELSE IO.rope [rootCellName], v4: IO.rope ["B"]]; state.abort _ IF stopFlag # NIL THEN stopFlag ELSE NEW [BOOL _ FALSE]; IF useTNT THEN state.nt _ InitTNT []; state.tech _ tech; IF (tech.maxSeparation = 0) OR (tech.maxSeparation = LAST [CD.Number]) THEN ERROR; state.attributes _ layout; CheckCoreCell [self: cell, state: state, actual: external, transf: identity ! coreMess => {messy _ TRUE; abortionCause _ reason; debugTorch _ torch; CONTINUE}]; IF (state.tech.verifyCell # NIL) THEN state.tech.verifyCell [cell: cell, state: state]; IF useTNT THEN BlowTNT [state.nt]; IF messy THEN BEGIN IF DrcDebug.debug THEN DrcDebug.break; cell.properties _ CoreProperties.PutProp [cell.properties, state.tech.checkedBy, NIL]; mLog.PutRope [busted]; eLog.PutRope [busted]; eLog.Close []; quantity _ LAST [CARDINAL]; coreInconsistent [abortionCause, debugTorch] END; quantity _ state.globalErrorCount; eLog.PutF [format: "\nNumber of new design violations found: %l%g%l\n", v1: IO.rope ["b"], v2: IO.card [quantity], v3: IO.rope ["B"]]; mLog.PutRope ["\n"]; termination _ IF state.abort^ THEN "\nDRC aborted."ELSE "\nDRC terminated normally."; eLog.PutRope [termination]; eLog.Close []; EXITS paranoia => -- cannot communicate with anybody {eLog.PutRope ["\nThis is paranoia."]; eLog.Close []} END; -- CheckDesignRules ComposedTransf: PROC [parent: Transf, child: CoreInst, state: State] RETURNS [Transf] ~ BEGIN RETURN [CDBasics.ComposeTransform [CoreGeometry.GetTrans [state.attributes, child], parent]] END; -- ComposedTransf CheckCell: TYPE ~ PROC [self: CoreCell, state: State, actual: Wire, transf: Transf]; CheckCoreCell: CheckCell ~ BEGIN SELECT self.class FROM CoreClasses.recordCellClass => CheckRecord [self, state, actual, transf]; CoreClasses.transistorCellClass => CheckTransistor [self, state, actual, transf]; CoreClasses.unspecifiedCellClass => ERROR; ENDCASE => ERROR END; -- CheckCoreCell CheckRecord: CheckCell ~ BEGIN cellData: CoreClasses.RecordCellType; bindingTable: RefTab.Ref; boundInternal: WireInstance; propagatedActuals: WireSet; -- one wire per subcell ownName: ROPE ~ CoreOps.GetCellTypeName [self]; GracefulAbort: PROC [aborted: BOOL _ FALSE] ~ BEGIN IF aborted THEN eLog.PutF1 ["\n Verification of cell %g aborted.", IO.rope [ownName]] END; -- GracefulAbort IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR ((NOT DrcDebug.debug) AND (CoreProperties.GetProp[self.properties, state.tech.checkedBy]#NIL)) THEN RETURN; mLog.Put1 [IO.char ['.]]; IF DrcDebug.debug THEN BEGIN bb: Rect ~ CellHull [self, transf, state]; DrcDebug.dLog.PutF [format: "\nChecking cell %l%g%l ", v1: IO.rope ["e"], v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName], v3: IO.rope ["E"]]; DrcDebug.dLog.PutF [" with bounding box [%g, %g, %g, %g].\n", IO.int [bb.x1/state.tech.lambda], IO.int [bb.y1/state.tech.lambda], IO.int [bb.x2/state.tech.lambda], IO.int [bb.y2/state.tech.lambda]] END; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN}; ClearErrors [self, state]; cellData _ NARROW [self.data, CoreClasses.RecordCellType]; self.properties _ CoreProperties.PutProp [self.properties, state.tech.checkedBy, $DrcWasHere]; bindingTable _ CreateBindingTable [actual: actual, public: self.public]; boundInternal _ [cellData.internal, BindInternal [bindingTable, cellData.internal, state], transf]; IF debugBinding THEN BEGIN DrcDebug.dLog.PutF1 ["Binding and propagating: %g. Binding table:\n", IO.rope [ownName]]; PrintBinding [bindingTable]; DrcDebug.dLog.PutF1 ["Bound internal of %g:\n", IO.rope [ownName]]; DrcDebug.PrintWire [boundInternal.global] END; propagatedActuals _ NEW [WireSetRec[cellData.size]]; FOR sub: NAT IN [0 .. cellData.size) DO propagatedActuals[sub] _ [NIL, -- depends on cell class PropagateBinding [state, bindingTable, cellData.instances[sub].actual], ComposedTransf [transf, cellData.instances[sub], state]] ENDLOOP; FlushBindingTable [bindingTable]; FOR sub: NAT IN [0 .. cellData.size) DO subTransf: Transf ~ ComposedTransf [transf, cellData.instances[sub], state]; CheckCoreCell [self: cellData.instances[sub].type, actual: propagatedActuals[sub].global, transf: propagatedActuals[sub].transf, state: state]; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN}; ENDLOOP; IF debugBinding THEN BEGIN DrcDebug.dLog.PutF1 ["Verifying: %g\nbound internal:\n", IO.rope [ownName]]; DrcDebug.PrintWire [boundInternal.global] END; FOR i: NAT IN [0 .. cellData.internal.size) DO w1: WireInstance ~ [boundInternal.local[i], boundInternal.global[i], identity]; state.tech.verifyWire [self, w1.local, state]; FOR j: NAT IN [i .. cellData.internal.size) DO w2: WireInstance ~ [boundInternal.local[j], boundInternal.global[j], identity]; state.tech.verifyWirePair [self, w1, w2, state]; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN}; ENDLOOP ENDLOOP; FOR sub: NAT IN [0 .. cellData.size) DO FOR i: NAT IN [0 .. cellData.internal.size) DO MaterialToCellSeparation [cell: cellData.instances[sub].type, father: self, -- for error marking cellWire: propagatedActuals[sub], materialWire: [boundInternal.local[i], boundInternal.global[i], transf], state: state]; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN}; ENDLOOP ENDLOOP; IF useTNT THEN SweepTNT [state.nt]; FOR sub1: NAT IN [0 .. cellData.size) DO obj1: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData.instances[sub1].type]; bloated1: Rect ~ CDBasics.Extend [CDBasics.MapRect [obj1.bbox, propagatedActuals[sub1].transf], state.tech.maxSeparation]; FOR sub2: NAT IN (sub1 .. cellData.size) DO obj2: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData.instances[sub2].type]; IF CDBasics.Intersect [bloated1, CDBasics.MapRect [obj2.bbox, propagatedActuals[sub2].transf]] THEN LOOP; -- avoid polluting TNT IF useTNT AND (state.nt.UpdateTNT [obj1, obj2, propagatedActuals[sub1].transf, propagatedActuals[sub2].transf, propagatedActuals[sub1].global, propagatedActuals[sub2].global].wasThere) THEN LOOP; IF DrcDebug.debug THEN BEGIN bb1: Rect ~ CellHull [cellData.instances[sub1].type, propagatedActuals[sub1].transf, state]; bb2: Rect ~ CellHull [cellData.instances[sub2].type, propagatedActuals[sub2].transf, state]; DrcDebug.dLog.PutF ["\nWill check the separation between the subcells %g with bounding box [%g, %g, %g, %g]", IO.rope [CoreOps.GetCellTypeName [cellData.instances[sub1].type]], IO.int [bb1.x1/state.tech.lambda], IO.int [bb1.y1/state.tech.lambda], IO.int [bb1.x2/state.tech.lambda], IO.int [bb1.y2/state.tech.lambda]]; DrcDebug.dLog.PutF [" and %g with bounding box [%g, %g, %g, %g].\n", IO.rope [CoreOps.GetCellTypeName [cellData.instances[sub2].type]], IO.int [bb2.x1/state.tech.lambda], IO.int [bb2.y1/state.tech.lambda], IO.int [bb2.x2/state.tech.lambda], IO.int [bb2.y2/state.tech.lambda]] END; CellToCellSeparation [state: state, father: self, cell1: cellData.instances[sub1].type, cell2: cellData.instances[sub2].type, wire1: propagatedActuals[sub1], wire2: propagatedActuals[sub2]]; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN} ENDLOOP ENDLOOP END; -- CheckRecord CheckTransistor: CheckCell ~ BEGIN END; -- CheckTransistor DoNotCheck: CheckCell ~ BEGIN self.properties _ CoreProperties.PutProp [self.properties, state.tech.checkedBy, $DrcWasHere] END; -- DoNotCheck AtomicWireHull: PUBLIC PROC [w: WireInstance, state: State] RETURNS [h: Rect] ~ BEGIN stored: REF Rect _ NARROW [CoreProperties.GetProp [w.local.properties, bbKey]]; EachRect: CoreGeometry.EachInstanceProc ~ BEGIN r: Rect ~ instance.obj.bbox; 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]; END; -- EachRect IF w.local.size > 0 THEN DrcDebug.break; -- you may proceede IF (stored = NIL) THEN BEGIN [] _ state.attributes.EnumerateGeometry [w.local, EachRect]; stored _ NEW [Rect _ h]; w.local.properties _ CoreProperties.PutProp [w.local.properties, bbKey, stored] END ELSE h _ stored^; h _ CDBasics.MapRect [h, w.transf] END; -- AtomicWireHull CellHull: PROC [self: CoreCell, transf: Transf, state: State] RETURNS [Rect] ~ BEGIN RETURN [CDBasics.MapRect [state.attributes.GetObject[self].bbox, transf]] END; -- CellHull CreateBindingTable: PROC [actual, public: Wire] RETURNS [bindingTable: RefTab.Ref] ~ BEGIN tableSize: RefTab.SeqIndex _ public.size; IF actual.size # public.size THEN BEGIN IF DrcDebug.debug THEN BEGIN DrcDebug.dLog.Put1 [IO.rope ["actual.size # public.size. Actual wire:\n"]]; DrcDebug.PrintWire [actual]; DrcDebug.dLog.Put1 [IO.rope ["Public wire:\n"]]; DrcDebug.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 ~ IF DrcDebug.debug THEN IO.PutFR1 ["New signal # %g", IO.card [state.wireCreationCount]] ELSE "New signal by Drc"; 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 boundInternal[i] _ b 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] ENDLOOP END; -- BindTransistor MaterialToCellProc: TYPE ~ PROC [cell, father: CoreCell, cellWire, materialWire: WireInstance, state: State]; CellToCellProc: TYPE ~ PROC [cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State]; MaterialToCellSeparation: MaterialToCellProc ~ BEGIN SELECT cell.class FROM CoreClasses.recordCellClass => MaterialToRecordCellSeparation [cell, father, cellWire, materialWire, state]; CoreClasses.transistorCellClass => MaterialToTransistorSeparation [cell, father, cellWire, materialWire, state]; CoreClasses.unspecifiedCellClass => MaterialToUnspecifiedSeparation [cell, father, cellWire, materialWire, state]; ENDCASE => ERROR END; -- MaterialToCellSeparation MaterialToTransistorSeparation: MaterialToCellProc ~ BEGIN wbb, tbb: Rect; -- bounding boxes boundInternal: WireInstance; atomicBoundInternal: WireSet; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend [tbb, state.tech.maxSeparation], wbb]] END; -- Intersect IF state.abort^ THEN RETURN; IF (CoreProperties.GetProp [cell.properties, doNotAnalyse] # NIL) THEN RETURN; wbb _ AtomicWireHull [materialWire, state]; tbb _ CellHull [cell, cellWire.transf, state]; IF (NOT Intersect []) THEN RETURN; boundInternal _ [cell.public, BindTransistor [cell.public, cellWire.global, state], cellWire.transf]; atomicBoundInternal _ NEW [WireSetRec[cell.public.size]]; FOR i: NAT IN [0 .. cell.public.size) DO atomicBoundInternal[i] _ [boundInternal.local[i], boundInternal.global[i], cellWire.transf] ENDLOOP; FOR i: NAT IN [0 .. cell.public.size) DO IF state.abort^ THEN RETURN; state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal[i], w2: materialWire] ENDLOOP END; -- MaterialToTransistorSeparation MaterialToUnspecifiedSeparation: MaterialToCellProc ~ BEGIN ERROR END; -- MaterialToUnspecifiedSeparation MaterialToRecordCellSeparation: MaterialToCellProc ~ BEGIN wbb, cbb: Rect; -- bounding boxes boundInternal: WireInstance; propagatedActuals, atomicBoundInternal: WireSet; bindingTable: RefTab.Ref; cellData: CoreClasses.RecordCellType _ NARROW [cell.data]; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend [cbb, state.tech.maxSeparation], wbb]] END; -- Intersect IF state.abort^ THEN RETURN; IF (CoreProperties.GetProp [cell.properties, doNotAnalyse] # NIL) OR (cellData.internal.size = 0) THEN RETURN; wbb _ AtomicWireHull [materialWire, state]; cbb _ CellHull [cell, cellWire.transf, state]; IF (NOT Intersect[]) THEN RETURN; bindingTable _ CreateBindingTable [actual: cellWire.global, public: cell.public]; boundInternal _ [cellData.internal, BindInternal [bindingTable, cellData.internal, state], cellWire.transf]; atomicBoundInternal _ NEW [WireSetRec[cellData.internal.size]]; FOR i: NAT IN [0 .. cellData.internal.size) DO atomicBoundInternal[i] _ [boundInternal.local[i], boundInternal.global[i], cellWire.transf] ENDLOOP; FOR i: NAT IN [0 .. cellData.internal.size) DO IF state.abort^ THEN RETURN; state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal[i], w2: materialWire] ENDLOOP; propagatedActuals _ NEW [WireSetRec[cellData.size]]; FOR sub: NAT IN [0 .. cellData.size) DO propagatedActuals[sub] _ [NIL, -- depends on cell class PropagateBinding [state, bindingTable, cellData.instances[sub].actual], ComposedTransf [cellWire.transf, cellData.instances[sub], state]] ENDLOOP; FlushBindingTable [bindingTable]; FOR sub: NAT IN [0 .. cellData.size) DO subTransf: Transf ~ ComposedTransf [cellWire.transf, cellData.instances[sub], state]; IF state.abort^ THEN RETURN; MaterialToCellSeparation [cell: cellData.instances[sub].type, father: cell, -- for error marking cellWire: propagatedActuals[sub], materialWire: [boundInternal.local[sub], boundInternal.global[sub], cellWire.transf], state: state] ENDLOOP END; -- MaterialToRecordCellSeparation CellToCellSeparation: CellToCellProc ~ BEGIN SELECT cell2.class FROM CoreClasses.unspecifiedCellClass => ERROR; CoreClasses.recordCellClass => NULL; -- default case CoreClasses.transistorCellClass => -- nasty case IF cell1.class = CoreClasses.transistorCellClass THEN BEGIN TransistorToTransistorSeparation [cell1, cell2, father, wire1, wire2, state]; RETURN END ELSE BEGIN -- swap cell1Z: CoreCell ~ cell1; wire1Z: WireInstance ~ wire1; cell1 _ cell2; cell2 _ cell1Z; wire1 _ wire2; wire2 _ wire1Z END; ENDCASE => ERROR; SELECT cell1.class FROM CoreClasses.recordCellClass => RecordCellToRecordCellSeparation [cell1, cell2, father, wire1, wire2, state]; CoreClasses.transistorCellClass => TransistorToRecordCellSeparation [cell1, cell2, father, wire1, wire2, state]; CoreClasses.unspecifiedCellClass => UnspecifiedToAnyClassSeparation [cell1, cell2, father, wire1, wire2, state]; ENDCASE => ERROR END; -- CellToCellSeparation TransistorToTransistorSeparation: CellToCellProc ~ BEGIN cbb1, cbb2: Rect; -- bounding boxes boundInternal1, boundInternal2: WireInstance; atomicBoundInternal1, atomicBoundInternal2: WireSet; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.tech.maxSeparation], cbb2]] END; -- Intersect IF state.abort^ THEN RETURN; IF (CoreProperties.GetProp [cell1.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [cell2.properties, doNotAnalyse]#NIL) THEN RETURN; cbb1 _ CellHull [cell1, wire1.transf, state]; cbb2 _ CellHull [cell2, wire2.transf, state]; IF (NOT Intersect[]) THEN RETURN; IF state.abort^ THEN RETURN; boundInternal1 _ [cell1.public, BindTransistor [cell1.public, wire1.global, state], wire1.transf]; boundInternal2 _ [cell2.public, BindTransistor [cell2.public, wire2.global, state], wire2.transf]; atomicBoundInternal1 _ NEW [WireSetRec[boundInternal1.local.size]]; atomicBoundInternal2 _ NEW [WireSetRec[boundInternal2.local.size]]; FOR i: NAT IN [0 .. boundInternal1.local.size) DO atomicBoundInternal1[i] _ [boundInternal1.local[i], boundInternal1.global[i], wire1.transf] ENDLOOP; FOR i: NAT IN [0 .. boundInternal2.local.size) DO atomicBoundInternal2[i] _ [boundInternal2.local[i], boundInternal2.global[i], wire2.transf] ENDLOOP; FOR i: NAT IN [0 .. cell1.public.size) DO FOR j: NAT IN [0 .. cell2.public.size) DO IF state.abort^ THEN RETURN; state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j]] ENDLOOP ENDLOOP END; -- TransistorToTransistorSeparation TransistorToRecordCellSeparation: CellToCellProc ~ BEGIN cbb1, cbb2: Rect; -- bounding boxes cellData2: CoreClasses.RecordCellType _ NARROW [cell2.data]; bindingTable2: RefTab.Ref; boundInternal1, boundInternal2: WireInstance; atomicBoundInternal1, atomicBoundInternal2, propagatedActuals2: WireSet; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend [cbb1, state.tech.maxSeparation], cbb2]] END; -- Intersect IF state.abort^ THEN RETURN; IF (CoreProperties.GetProp [cell1.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [cell2.properties, doNotAnalyse]#NIL) THEN RETURN; IF DrcDebug.debug THEN BEGIN ownName: ROPE ~ CoreOps.GetCellTypeName [cell1]; otherName: ROPE ~ CoreOps.GetCellTypeName [cell2]; DrcDebug.dLog.PutF [format: "Checking transistor %l%g%l vs. %g. ", v1: IO.rope ["e"], v2: IF (ownName = NIL) THEN IO.refAny [cell1] ELSE IO.rope [ownName], v3: IO.rope ["E"], v4: IF (otherName = NIL) THEN IO.refAny [cell2] ELSE IO.rope [otherName]] END; cbb1 _ CellHull [cell1, wire1.transf, state]; cbb2 _ CellHull [cell2, wire2.transf, state]; IF (NOT Intersect[]) THEN RETURN; IF state.abort^ THEN ERROR ABORTED; boundInternal1 _ [cell1.public, BindTransistor [cell1.public, wire1.global, state], wire1.transf]; atomicBoundInternal1 _ NEW [WireSetRec[cell1.public.size]]; FOR i: NAT IN [0 .. cell1.public.size) DO atomicBoundInternal1[i] _ [boundInternal1.local[i], boundInternal1.global[i], wire1.transf] ENDLOOP; bindingTable2 _ CreateBindingTable [actual: wire2.global, public: cell2.public]; boundInternal2 _ [cellData2.internal, BindInternal [bindingTable2, cellData2.internal, state], wire2.transf]; atomicBoundInternal2 _ NEW [WireSetRec[cellData2.internal.size]]; FOR i: NAT IN [0 .. cellData2.internal.size) DO atomicBoundInternal2[i] _ [boundInternal2.local[i], boundInternal2.global[i], wire2.transf] ENDLOOP; propagatedActuals2 _ NEW [WireSetRec[cellData2.internal.size]]; FOR sub: NAT IN [0 .. cellData2.internal.size) DO propagatedActuals2[sub] _ [NIL, -- depends on cell class PropagateBinding [state, bindingTable2, cellData2.instances[sub].actual], ComposedTransf [wire2.transf, cellData2.instances[sub], state]] ENDLOOP; FlushBindingTable [bindingTable2]; FOR i: NAT IN [0 .. wire1.local.size) DO FOR j: NAT IN [0 .. wire2.local.size) DO IF state.abort^ THEN RETURN; state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j]] ENDLOOP ENDLOOP; FOR i: NAT IN [0 .. wire1.local.size) DO FOR sub: NAT IN [0 .. cellData2.size) DO subTransf: Transf ~ ComposedTransf [wire2.transf, cellData2.instances[sub], state]; IF state.abort^ THEN RETURN; MaterialToCellSeparation [cell: cellData2.instances[sub].type, father: father, -- for error marking cellWire: propagatedActuals2[sub], materialWire: atomicBoundInternal1[i], state: state] ENDLOOP ENDLOOP END; -- TransistorToRecordCellSeparation UnspecifiedToAnyClassSeparation: CellToCellProc ~ BEGIN ERROR END; -- UnspecifiedToAnyClassSeparation RecordCellToRecordCellSeparation: CellToCellProc ~ BEGIN cbb1, cbb2: Rect; -- bounding boxes cellData1: CoreClasses.RecordCellType _ NARROW [cell1.data]; cellData2: CoreClasses.RecordCellType _ NARROW [cell2.data]; bindingTable1, bindingTable2: RefTab.Ref; boundInternal1, boundInternal2: WireInstance; atomicBoundInternal1, atomicBoundInternal2, propagatedActuals1, propagatedActuals2: WireSet; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.tech.maxSeparation], cbb2]] END; -- Intersect IF state.abort^ THEN RETURN; IF (CoreProperties.GetProp [cell1.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [cell2.properties, doNotAnalyse]#NIL) THEN RETURN; IF DrcDebug.debug THEN BEGIN name1: ROPE ~ CoreOps.GetCellTypeName [cell1]; name2: ROPE ~ CoreOps.GetCellTypeName [cell2]; DrcDebug.dLog.PutF [format: "Checking record cell %l%g%l vs. %g.\n", v1: IO.rope ["e"], v2: IF name1.IsEmpty THEN IO.refAny [cell1] ELSE IO.rope [name1], v3: IO.rope ["E"], v4: IF name2.IsEmpty THEN IO.refAny [cell2] ELSE IO.rope [name2]] END; cbb1 _ CellHull [cell1, wire1.transf, state]; cbb2 _ CellHull [cell2, wire2.transf, state]; IF (NOT Intersect[]) THEN RETURN; IF state.abort^ THEN RETURN; bindingTable1 _ CreateBindingTable [actual: wire1.global, public: cell1.public]; bindingTable2 _ CreateBindingTable [actual: wire2.global, public: cell2.public]; boundInternal1 _ [cellData1.internal, BindInternal [bindingTable1, cellData1.internal, state], wire1.transf]; boundInternal2 _ [cellData2.internal, BindInternal [bindingTable2, cellData2.internal, state], wire2.transf]; atomicBoundInternal1 _ NEW [WireSetRec[boundInternal1.local.size]]; atomicBoundInternal2 _ NEW [WireSetRec[boundInternal2.local.size]]; FOR i: NAT IN [0 .. boundInternal1.local.size) DO atomicBoundInternal1[i] _ [boundInternal1.local[i], boundInternal1.global[i], wire1.transf] ENDLOOP; FOR i: NAT IN [0 .. boundInternal2.local.size) DO atomicBoundInternal2[i] _ [boundInternal2.local[i], boundInternal2.global[i], wire2.transf] ENDLOOP; propagatedActuals1 _ NEW [WireSetRec[cellData1.internal.size]]; FOR sub: NAT IN [0 .. cellData1.size) DO propagatedActuals2[sub] _ [NIL, -- depends on cell class PropagateBinding [state, bindingTable1, cellData1.instances[sub].actual], ComposedTransf [wire1.transf, cellData1.instances[sub], state]] ENDLOOP; propagatedActuals2 _ NEW [WireSetRec[cellData2.internal.size]]; FOR sub: NAT IN [0 .. cellData2.size) DO propagatedActuals2[sub] _ [NIL, -- depends on cell class PropagateBinding [state, bindingTable2, cellData2.instances[sub].actual], ComposedTransf [wire2.transf, cellData2.instances[sub], state]] ENDLOOP; FlushBindingTable [bindingTable1]; FlushBindingTable [bindingTable2]; FOR i: NAT IN [0 .. cellData1.internal.size) DO FOR j: NAT IN [0 .. cellData2.internal.size) DO state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j]] ENDLOOP ENDLOOP; FOR sub: NAT IN [0 .. cellData2.size) DO FOR i: NAT IN [0 .. cellData1.internal.size) DO MaterialToCellSeparation [cell: cellData2.instances[sub].type, father: father, -- for error marking cellWire: propagatedActuals2[sub], materialWire: atomicBoundInternal1[i], state: state] ENDLOOP ENDLOOP; FOR sub: NAT IN [0 .. cellData1.size) DO FOR i: NAT IN [0 .. cellData2.internal.size) DO MaterialToCellSeparation [cell: cellData1.instances[sub].type, father: father, -- for error marking cellWire: propagatedActuals1[sub], materialWire: atomicBoundInternal2[i], state: state] ENDLOOP ENDLOOP; FOR sub1: NAT IN [0 .. cellData1.size) DO obj1: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData1.instances[sub1].type]; bloated1: Rect ~ CDBasics.Extend [CDBasics.MapRect [obj1.bbox, propagatedActuals1[sub1].transf], state.tech.maxSeparation]; FOR sub2: NAT IN (sub1 .. cellData2.size) DO obj2: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData2.instances[sub2].type]; IF CDBasics.Intersect [bloated1, CDBasics.MapRect [obj2.bbox, propagatedActuals2[sub2].transf]] THEN LOOP; -- avoid polluting TNT IF useTNT AND (state.nt.UpdateTNT [obj1, obj2, propagatedActuals1[sub1].transf, propagatedActuals2[sub2].transf, propagatedActuals1[sub1].global, propagatedActuals2[sub2].global].wasThere) THEN LOOP; CellToCellSeparation [state: state, father: father, cell1: cellData1.instances[sub1].type, cell2: cellData2.instances[sub2].type, wire1: propagatedActuals1[sub1], wire2: propagatedActuals2[sub2]] ENDLOOP ENDLOOP END; -- RecordCellToRecordCellSeparation ClearErrors: PROC [obj: CoreCell, state: State] ~ BEGIN obj.properties _ CoreProperties.PutProp [obj.properties, DRVkey, NIL] END; -- ClearErrors MarkError: PUBLIC PROC [obj: CoreCell, state: State, e: ErrorRect] ~ BEGIN objName: ROPE = CoreOps.GetCellTypeName [obj]; violations: DRV _ NARROW [CoreProperties.GetProp [obj.properties, DRVkey]]; longMsg: 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]]; eLog.Flush; state.globalErrorCount _ state.globalErrorCount.SUCC; IF state.globalErrorCount < errorFeedbackCutOff THEN mLog.Put1 [IO.char ['|]]; 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; obj.properties _ CoreProperties.PutProp [on: obj.properties, prop: DRVkey, value: violations]; IF DrcDebug.debug THEN DrcDebug.dLog.PutF1 ["%g\n", IO.rope [longMsg]] END; -- MarkError PrintError: CoreProperties.PropPrintProc ~ BEGIN to.PutF1 [format: "Design rule violations: %g. ", value: IO.int [NARROW[val,DRV].count]] END; -- PrintError RegisterProperties: PROC ~ BEGIN IF (NOT CDProperties.RegisterProperty [DRVkey, $gbb]) OR (NOT CDProperties.RegisterProperty [bbKey, $gbb]) THEN IF NOT DrcDebug.debug THEN mLog.Put1 [IO.rope["Drc probably is being reexecuted.\n"]]; CoreProperties.StoreProperties [prop: DRVkey, properties: CoreProperties.Props [[CoreProperties.propPrint, NEW [CoreProperties.PropPrintProc _ PrintError]]]] END; -- RegisterProperties StartLog: PROC ~ BEGIN viewer: ViewerTools.Viewer; name: ROPE; viewer _ ViewerTools.FindExistingViewer ["Terminal Viewer"]; name _ IF (viewer # NIL) THEN "Terminal Viewer" ELSE "Genista (DRC)"; mLog _ ViewerIO.CreateViewerStreams [name, viewer].out END; -- StartLog PrintEntryShort: RefTab.EachPairAction ~ BEGIN DrcDebug.dLog.PutF ["public: %g\t actual: %g\n", IO.card [LOOPHOLE[key]], IO.card [LOOPHOLE[val]]]; RETURN [FALSE] END; -- PrintEntryShort PrintEntryLong: RefTab.EachPairAction ~ BEGIN DrcDebug.PrintWire [NARROW [key, Wire]]; DrcDebug.PrintWire [NARROW [val, Wire]]; DrcDebug.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 RegisterProperties [] -- CD & Core END. ÜDrcImpl.mesa Copyright Ó 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Rewritten by gbb, January 12, 1987 11:55:49 am PST gbb March 27, 1987 4:49:20 pm PST Genista is the grandson of Spinifex. It is a mostly 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. La ginestra o il fiore del deserto (Giacomo Leopardi) Gli uomini vollero piuttosto le tenebre che la luce (Giovanni, III, 19.) ... E tu, lenta ginestra, che di selve odorate queste campagne dispogliate adorni, anche tu presto alla cruel possanza soccomberai dal sotterraneo foco, che ritornando al loco gia noto, stendera l'avaro lembo su tue molli foreste. E piegherai sotto il fascio mortal non renitente il tuo capo innocente: ma non piegato insino allora indarno codardamente supplicando innanzi al futuro oppressor; ma non eretto con forsennato orgoglio inver le stelle, n e sul deserto, dove e la sede e i natali non per voler ma per fortuna avesti; ma piu saggia, ma tanto meno inferma dell'uom, quanto le frali tue stirpi non credesti o dal fato o da te fatte immortali. Saguaro USING [ExtractedTransistor, ExtractTransistor], Debugging, signals and errors Note that aborting by the means of a signal busts the Core data structure. Logs Type redifinitions Drc keys. For simplicity they are registered both with ChipNDale and Core and have the same name Error report may be processed by clients. Cache for the bounding box. ChipNDale keys used by the Genista Core keys used by the Genista Global constants and types Activation Procedure The external interface. DRC. Clean up. Remember that we assigned returnedCell _ cell. 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. Remember that we assigned returnedCell _ cell. ChipNDale Stuff Returns the child's absolute coordinates. Verification of Core Cells Design rule checks a Core cell. Must remove existing error information and check the intenal geometry. It has to perform both the binding and the propagation of the binding, making sure the Core data structure is not altered. Places a message in the error Log. To use the connectivity informstion from the Core data structure, we have to pass down not the wires, but rather their instances. This implementation tries to be fast. 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. WARNING: The order of the calls of the technology dependent procedures is important and may not be changed. Since in this case the error will go into the cell itself, we produce cell relative coordinates by appropriately setting transf. 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. [self: CoreCell, state: State, actual: Wire, transf: Transf]; Transistors are atomic ChipNDale objects and are supposed to be correct by construction. e: Saguaro.ExtractedTransistor ~ Saguaro.ExtractTransistor; DoNotCheck [self, state, actual, transf] [self: CoreCell, state: State, actual: Wire, transf: Transf]; (self.class = CoreClasses.unspecifiedCellClass) OR (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL); Pruning Coputes the bounding box of the rectangles of material that make up a wire. PROC [instance: CdInsts] RETURNS [quit: BOOL _ FALSE] Wire is not atomic. The extractor might have been fed something he did not like. The graceful way to abort is to execute the abort command (e.g., ESC DEL in the ChipNDale viewer) and click on proceed in the event viewer. Needed for recursion. 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. It is assumed that wires have depth 1. If the wire is not in the binding table, it does not interface, hence it has not to be bound. Separation and Width Check Procedures Checks a rectangle of material against all rectangles of material in a cell. It has to perform both the binding and the propagation of the binding, because the structure of the internal depends on the cell class. Checks all rectangles of material in a cell against those in another cell. It has to perform both the binding and the propagation of the binding, because the structure of the internal depends on the cell class. For convenience. [cell, father: CoreCell, cellWire, materialWire: WireInstance, state: State] 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. [cell, father: CoreCell, cellWire, materialWire: WireInstance, state: State] [cell, father: CoreCell, cellWire, materialWire: WireInstance, state: State] 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. [cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State] For convenience. [cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State] Preamble. Check the separation between the internal of cell1 and cell2. [cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State] Preamble. Check the separation between the internal of transistor and cell2. Check intersections between subcells of transistor wires and cell2. [cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State] [cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State] Preamble. Check the separation between the internal of cell1 and cell2. Design Rule Violation Flagging Removes the error rectangles from a given object (e.g. a cell). Puts an error rectangle r and maintains an error counter in the Core structure. 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]. [key: Key, val: Val] RETURNS [quit: BOOLEAN] [key: Key, val: Val] RETURNS [quit: BOOLEAN] For debugging purposes. Initialisation ÊZ˜codešœ ™ KšœH™HKšœ2™2K™!K˜—code2šœá™áquotešÑios"Ðos™5MšžH™HMšž™——šÏk ˜ LšŸœŸœ ˜Kšœ Ÿœ0˜>Kšœ Ÿœ˜&KšœŸœ˜Kšœ Ÿœ\˜mKšœ Ÿœ€˜’KšœŸœN˜[KšœŸœX˜lKšœ˜Kšœ Ÿœ(˜6KšŸœŸœ˜KšŸœŸœaŸœ˜wKšœŸœ?˜KKšœŸœŸœ ˜KšœŸœ*™7KšŸœŸœŸœ ˜7Kšœ Ÿœ˜%Kšœ Ÿœ˜/—LšÐlnœŸœŸ˜LšŸœWŸœŸœŸœ˜ŽLšŸœŸœ˜šœŸœŸœŸœ˜head™N™JKšœŸœŸœÏc!˜=KšœŸœŸœ¡˜1KšœŸœŸœ¡ ˜7Kš œŸœŸœ Ÿœ ŸœŸœŸœ˜EKš œ Ÿœ Ÿœ ŸœŸœŸœ¡+˜c—™Lšœ ŸœŸœ¡˜1—™Lšœ Ÿœ˜'LšœŸœ˜"Lšœ Ÿœ˜+Lšœ Ÿœ˜'LšœŸœ˜!LšŸœŸœŸœ˜—šœ ™ NšœV™VšœŸœŸœ/˜BK™)—šœŸœ,˜7K™——™"Kšœ Ÿœ ˜Kšœ Ÿœ ˜—™KšœŸœ ˜—šœ™Lšœ¡Ÿœ¡˜/LšœŸœ¡|˜ Lšœ ŸœŸœ ¡˜>Kš œ ŸœŸœŸœŸœŸœ˜D——™šÏnœŸœŸœ8ŸœŸœŸœŸœ ŸœŸ˜K™unitšŸœŸ˜ šŸœ ŸœŸœŸ˜+KšœŸœŸœ˜ KšœŸœ˜KšŸœŸœ Ÿœ˜-KšœŸœŸœŸœ˜&KšŸ˜KšŸœ˜—Kšœ Ÿœ ˜KšŸœ˜—OšœŸœ ˜KšœŸœŸœ˜KšœŸœ˜!Kšœ ŸœŸœ˜KšœŸœ"˜4Kšœ Ÿœ˜+KšœŸœ<˜HOš œŸœPŸœŸœŸœ¡'˜¥KšœEŸœŸœ ˜iOšŸœŸœ:ŸœŸœŸœŸœŸœŸœŸœŸœŸœ ˜ÏKšœŸœ ŸœŸœ ŸœŸœŸœŸœ˜FKšŸœŸœ˜%Kšœ˜Kš ŸœŸœŸœŸœ ŸœŸœ˜RKšœ˜O™KšœcŸœ.Ÿœ˜ KšŸœŸœŸœ2˜WKšŸœŸœ˜"šŸœŸœŸ˜K™ KšŸœŸœ˜&KšœQŸœ˜VKšœÏeœ™.Kšœ<˜˜TK™K™Â—š¢ œŸ˜ šŸœ Ÿ˜KšœI˜IKšœQ˜QKšœ$Ÿœ˜*KšŸœŸ˜—KšŸœ¡˜—š¢ œŸ˜Kšœ%˜%Kšœ˜Kšœ˜Kšœ¡˜3Kšœ Ÿœ"˜/š ¢ œŸœ ŸœŸœŸ˜3K™"KšŸœ Ÿœ4Ÿœ˜UKšŸœ¡˜—OšŸœ9ŸœŸœŸœŸœ@ŸœŸœÐbkœ˜®Kšœ Ÿœ ˜šŸœŸœŸ˜Kšœ*˜*Kšœ;ŸœŸœ ŸœŸœŸœŸœŸœŸœ ˜ Kš œ>Ÿœ Ÿœ Ÿœ Ÿœ˜ÅKšŸœ˜—KšŸœŸœ!¤œ˜=Kšœ˜Kšœ Ÿœ)˜:Kšœ^˜^O™§OšœH˜HKšœc˜cšŸœŸœŸ˜KšœFŸœ˜YKšœ˜Kšœ0Ÿœ˜CKšœ)˜)KšŸœ˜—O™tKšœŸœ˜4šŸœŸœŸœŸ˜'KšœŸœ¡œ€˜¸KšŸœ˜—Ošœ!˜!Ošœy£œ$£œ±™êšŸœŸœŸœŸ˜'KšœL˜LKšœ˜KšŸœŸœ!¤œ˜=KšŸœ˜—O™šŸœŸœŸ˜Kšœ9Ÿœ˜LKšœ)˜)KšŸœ˜—KšÏbœd™kšŸœŸœŸœŸ˜.Kšœy£œ™€KšœO˜OKšœ Ïl œŸ˜.šŸœŸœŸœŸ˜.K™ÕKšœO˜OKšœ ¦œ˜0KšŸœŸœ!¤œ˜=KšŸ˜—KšŸœ˜—Ošœ-£œ™CšŸœŸœŸœŸ˜'šŸœŸœŸœŸ˜.KšœL¡œz˜ÚKšŸœŸœ!¤œ˜=KšŸ˜—KšŸœ˜—Ošœ-£œ™2KšŸœŸœ˜#šŸœŸœŸœŸ˜(KšœW˜WKšœz˜zšŸœŸœŸœŸ˜+KšœW˜WKšŸœ]ŸœŸœ¡˜€KšŸœŸœ¬ŸœŸœ˜ÃšŸœŸœŸ˜Kšœ\˜\Kšœ\˜\Kš œnŸœAŸœ!Ÿœ!Ÿœ!Ÿœ!˜½Kš œEŸœAŸœ!Ÿœ!Ÿœ!Ÿœ ˜“KšŸœ˜—Lšœ¾˜¾KšŸœŸœ!¤œ˜