DIRECTORY CD USING [Number, Transformation], CDBasics USING [Extend, Intersect, Intersection, NonEmpty], CDBasicsInline USING [ComposeTransform, MapRect], CDProperties USING [GetObjectProp, RegisterProperty], Core USING [Properties], CoreClasses USING [CellInstance, recordCellClass, RecordCellType, transistorCellClass, unspecifiedCellClass], CoreGeometry USING [CellInstance, Decoration, EachInstanceProc, EnumerateGeometry, GetObject, GetTrans, HasGeometry, InlineBBox, Instance, Instances, Layer, Object], CoreOps USING [CopyWire, CreateWires, GetCellTypeName, GetShortWireName, SetShortWireName], CoreProperties USING [GetProp, propPrint, PropPrintProc, Props, PutProp, RegisterProperty, StoreProperties], Drc, DrcDebug USING [break, GeometryInWire, 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], Rope USING [Concat, Equal, IsEmpty, ROPE], SimpleMailer USING [SendMessage], TNT USING [BlowTNT, InitTNT, SweepTNT, TNT, UpdateTNT], UserCredentials USING [Get], ViewerIO USING [CreateViewerStreams], ViewerTools USING [FindExistingViewer, Viewer]; DrcImpl: CEDAR PROGRAM IMPORTS CDBasics, CDBasicsInline, CDProperties, CoreClasses, CoreGeometry, CoreOps, CoreProperties, DrcDebug, FS, IO, RefTab, Rope, SimpleMailer, TNT, UserCredentials, 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 isnotgbb: BOOL ~ NOT UserCredentials.Get[].name.Equal ["Beretta.pa", FALSE]; notify: BOOL ~ FALSE; 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 ~ 150; -- after this number of errors, a | is no longer displayed in the ChipNDale Terminal viewer (if it may be displayed at all). BoundingBox: TYPE ~ REF BoundingBoxRec; -- the wires of a set of cells BoundingBoxRec: TYPE ~ RECORD [bb: SEQUENCE size: NAT OF Rect]; WireSetList: TYPE ~ LIST OF WireSet _ NIL; Marks: TYPE ~ RECORD [cell: SEQUENCE size: NAT OF Rect]; CheckDesignRules: PUBLIC PROC [cell: CoreCell, external: Wire, tech: Tech, viaFlatness: BOOL _ FALSE, stopFlag: REF BOOL, lap: REF CARDINAL, currentCell: REF Rope.ROPE, 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, logMsg: 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.viaFlatness _ viaFlatness; IF viaFlatness THEN mLog.PutRope ["Will verify via flatness.\n"]; state.abort _ IF stopFlag # NIL THEN stopFlag ELSE NEW [BOOL _ FALSE]; state.globalErrorCount _ lap; state.globalErrorCount^ _ 0; state.currentCell _ currentCell; state.currentCell^ _ rootCellName; IF useTNT THEN BEGIN state.nt _ InitTNT []; state.ttNt _ InitTNT []; state.ctNt _ InitTNT [] END; 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}]; eLog.Flush; mLog.Put1 [IO.char [':]]; IF (state.tech.verifyCell # NIL) THEN state.tech.verifyCell [cell: cell, state: state] ELSE mLog.PutRope ["\nWells and diffusion sex NOT fully verified."]; IF useTNT THEN BEGIN BlowTNT [state.nt]; BlowTNT [state.ttNt]; BlowTNT [state.ctNt] END; 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]; IF isnotgbb THEN [] _ SimpleMailer.SendMessage [from: "Genista", to: LIST ["DAToolsImplementors^.PA"], subject: "Genista Aborted", body: IO.PutFR ["Root cell: %g\n%g\nAbortionCause:%g\nViolations so far:%g", IO.rope [rootCellName], IO.refAny [cell], IO.rope [abortionCause], IO.card [state.globalErrorCount^]]]; coreInconsistent [abortionCause, debugTorch] END; quantity _ state.globalErrorCount^; logMsg _ IF viaFlatness THEN "\nVia flatness verified.\n" ELSE "\nVia flatness NOT verified.\n"; 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 [logMsg]; eLog.PutRope [logMsg]; IF (state.anathemas[0] + state.anathemas[1] + state.anathemas[2] # 0) THEN BEGIN logMsg _ "Religion of pairwise correctness at every level was broken but is forgiven (exponentially)\n"; mLog.PutRope [logMsg]; eLog.PutRope [logMsg]; IF (state.anathemas[0] # 0) THEN BEGIN logMsg _ IO.PutFR1 ["\tCell not correct in isolation but correct in context: %g\n", IO.card [state.anathemas[0]]]; mLog.PutRope [logMsg]; eLog.PutRope [logMsg] END; IF (state.anathemas[1] # 0) THEN BEGIN logMsg _ IO.PutFR1 ["\tRectangle pair not correct in isolation but correct in context: %g\n", IO.card [state.anathemas[1]]]; mLog.PutRope [logMsg]; eLog.PutRope [logMsg] END; IF (state.anathemas[2] # 0) THEN BEGIN logMsg _ IO.PutFR1 ["\tRectangle pair not permitted but allowed by technology: %g\n", IO.card [state.anathemas[2]]]; mLog.PutRope [logMsg]; eLog.PutRope [logMsg] END; IF (state.anathemas[3] # 0) THEN BEGIN logMsg _ IO.PutFR1 ["\tRule is an exception: %g\n", IO.card [state.anathemas[3]]]; mLog.PutRope [logMsg]; eLog.PutRope [logMsg] END END; logMsg _ IF state.abort^ THEN "\nDRC aborted."ELSE "\nDRC terminated normally."; eLog.PutRope [logMsg]; eLog.Close []; mLog.PutF1 ["\nViolations logged on %g\n", IO.rope [logFile]]; IF notify AND isnotgbb THEN [] _ SimpleMailer.SendMessage [from: "Genista", to: LIST ["Beretta.PA"], subject: "User Statistics", body: IO.PutFR ["Root cell: %g\n%g\nViolations:%g\nTermination: %g", IO.rope [rootCellName], IO.refAny [cell], IO.card [quantity], IO.rope [logMsg]]]; 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] ~ INLINE BEGIN RETURN [CDBasicsInline.ComposeTransform [CoreGeometry.GetTrans [state.attributes, child], parent]] END; -- ComposedTransf BloatedIntersection: PROC [r1, r2: Rect, e1, e2: CD.Number] RETURNS [Rect] ~ INLINE BEGIN RETURN [CDBasics.Intersection [CDBasics.Extend [r1, e1 / 2], CDBasics.Extend [r2, e2 / 2]]] END; -- BloatedIntersection CheckCell: TYPE ~ PROC [self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet _ NIL]; CheckCoreCell: CheckCell ~ BEGIN SELECT self.class FROM CoreClasses.recordCellClass => RETURN [CheckRecord [self, state, actual, transf]]; CoreClasses.transistorCellClass => RETURN [CheckTransistor [self, state, actual, transf]]; CoreClasses.unspecifiedCellClass => ERROR; ENDCASE => BEGIN WHILE (self.class # CoreClasses.recordCellClass) DO self _ self.class.recast [self] ENDLOOP; [] _ CheckRecord [self, state, actual, transf] END END; -- CheckCoreCell CheckRecord: CheckCell ~ BEGIN cellData: CoreClasses.RecordCellType; bindingTable: RefTab.Ref; boundInternal: WireInstance; propagatedActuals, atomicBoundInternal: WireSet; -- one wire per subcell ownName: ROPE _ CoreOps.GetCellTypeName [self]; bb: Rect ~ CellHull [self, transf, state]; subBB: BoundingBox; channelPeekList: WireSetList; GracefulAbort: PROC [aborted: BOOL _ FALSE] ~ BEGIN IF aborted THEN BEGIN eLog.PutF1 ["\n Verification of cell %g aborted.", IO.rope [ownName]]; self.properties _ CoreProperties.PutProp [self.properties, state.tech.checkedBy, NIL] END END; -- GracefulAbort IF (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, self], doNotAnalyse]#NIL) OR ((NOT DrcDebug.debug) AND (CoreProperties.GetProp[self.properties, state.tech.checkedBy]#NIL)) THEN RETURN; mLog.Put1 [IO.char ['.]]; IF (ownName = NIL) THEN ownName _ IO.PutFR1 ["%g", IO.refAny [self]]; IF DrcDebug.debug THEN BEGIN DrcDebug.dLog.PutF [format: "\nChecking cell %l%g%l ", v1: IO.rope ["e"], v2: 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}; state.currentCell^ _ ownName; 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]; 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; boundInternal _ [cellData.internal, BindInternal [bindingTable, cellData.internal, state], transf]; atomicBoundInternal _ NEW [WireSetRec[boundInternal.local.size]]; FOR i: NAT IN [0 .. boundInternal.local.size) DO atomicBoundInternal[i] _ [boundInternal.local[i], boundInternal.global[i], transf] ENDLOOP; 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; FlushBindingTable [bindingTable]; FOR sub: NAT IN [0 .. cellData.size) DO subTransf: Transf ~ ComposedTransf [transf, cellData.instances[sub], state]; channelPeek: WireSet ~ CheckCoreCell [self: cellData.instances[sub].type, actual: propagatedActuals[sub].global, transf: propagatedActuals[sub].transf, state: state]; IF (channelPeek # NIL) THEN BEGIN FOR i: NAT IN [0 .. cellData.instances[sub].type.public.size) DO channelPeek[i].transf _ subTransf ENDLOOP; channelPeekList _ CONS [channelPeek, channelPeekList] END; 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; IF DrcDebug.debug THEN BEGIN FOR elle: WireSetList _ channelPeekList, elle.rest WHILE elle # NIL DO DrcDebug.dLog.PutRope ["channelPeekList.*.elt[1]: \n"]; DrcDebug.GeometryInWire [elle.first.elt[1].local, elle.first.elt[1].transf, state.attributes] ENDLOOP; END; FOR i: NAT IN [0 .. cellData.internal.size) DO w1: WireInstance ~ [boundInternal.local[i], boundInternal.global[i], transf]; state.tech.verifyWire [self, w1.local, state]; IF DrcDebug.debug THEN BEGIN DrcDebug.dLog.PutF ["In cell %g:\n", IO.rope [ownName]]; DrcDebug.GeometryInWire [w1.local, w1.transf, state.attributes] END; FOR j: NAT IN [i .. cellData.internal.size) DO w2: WireInstance ~ [boundInternal.local[j], boundInternal.global[j], transf]; state.tech.verifyWirePair [[self, transf], w1, w2, channelPeekList, bb, state]; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN}; ENDLOOP ENDLOOP; subBB _ NEW [BoundingBoxRec[cellData.size]]; IF DrcDebug.debug THEN DrcDebug.dLog.PutF1 ["Verifying the separation between the internal of %g and its subcells.\n", IO.rope [ownName]]; FOR sub: NAT IN [0 .. cellData.size) DO subBB[sub] _ CellHull [cellData.instances[sub].type, propagatedActuals[sub].transf, state] ENDLOOP; FOR i: NAT IN [0 .. cellData.internal.size) DO IF NOT state.attributes.HasGeometry [boundInternal.local[i]] THEN LOOP; FOR sub: NAT IN [0 .. cellData.size) DO IF DrcDebug.debug THEN DrcDebug.dLog.PutF ["Subcell %g: %g with wire %g: local %g, global %g.\n", IO.int [sub], IO.rope [CoreOps.GetCellTypeName [cellData.instances[sub].type]], IO.int [i], IO.card [LOOPHOLE [boundInternal.local[i]]], IO.card [LOOPHOLE [boundInternal.global[i]]]]; MaterialToCellSeparation [cell: cellData.instances[sub].type, where: [self, transf], -- for error marking cellWire: propagatedActuals[sub], materialWire: [boundInternal.local[i], boundInternal.global[i], transf], window: subBB[sub], patches: channelPeekList, state: state]; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN}; ENDLOOP ENDLOOP; mLog.Put1 [IO.char [' ]]; IF useTNT THEN {SweepTNT [state.nt]; SweepTNT [state.ttNt]; SweepTNT [state.ctNt]}; FOR sub1: NAT IN [0 .. cellData.size) DO maxSep1, maxSep2: CD.Number; nt: TNT; obj1: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData.instances[sub1].type]; bloated1: Rect _ CDBasics.Extend [subBB[sub1], state.tech.maxSeparation]; -- quick FOR sub2: NAT IN (sub1 .. cellData.size) DO obj2: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData.instances[sub2].type]; areaOfInterest: Rect; IF NOT CDBasics.Intersect [bloated1, subBB[sub2]] THEN LOOP; -- quick [maxSep1, maxSep2, nt] _ SepAndNT [cellData.instances[sub1].type, cellData.instances[sub2].type, state]; areaOfInterest _ BloatedIntersection [subBB[sub1], subBB[sub2], maxSep1, maxSep2]; IF NOT CDBasics.NonEmpty [areaOfInterest] THEN LOOP; IF useTNT AND (nt.UpdateTNT [obj1, obj2, propagatedActuals[sub1].transf, propagatedActuals[sub2].transf, propagatedActuals[sub1].global, propagatedActuals[sub2].global].wasThere) THEN LOOP; IF DrcDebug.debug THEN BEGIN 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 [subBB[sub1].x1/state.tech.lambda], IO.int [subBB[sub1].y1/state.tech.lambda], IO.int [subBB[sub1].x2/state.tech.lambda], IO.int [subBB[sub1].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 [subBB[sub2].x1/state.tech.lambda], IO.int [subBB[sub2].y1/state.tech.lambda], IO.int [subBB[sub2].x2/state.tech.lambda], IO.int [subBB[sub2].y2/state.tech.lambda]] END; CellToCellSeparation [state: state, where: [self, transf], window: areaOfInterest, cell1: cellData.instances[sub1].type, cell2: cellData.instances[sub2].type, wire1: propagatedActuals[sub1], wire2: propagatedActuals[sub2], patches: channelPeekList]; IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN} ENDLOOP ENDLOOP END; -- CheckRecord CheckTransistor: CheckCell ~ BEGIN boundInternal: WireInstance; boundInternal _ [self.public, BindTransistor [self.public, actual, state], transf]; peek _ NEW [WireSetRec[self.public.size]]; FOR i: NAT IN [0 .. self.public.size) DO peek[i] _ [boundInternal.local[i], boundInternal.global[i], transf] ENDLOOP; peek[0].gateHint _ peek[1].gateHint _ peek[2].gateHint _ NEW [WireInstance _ peek[0]] END; -- CheckTransistor DoNotCheck: CheckCell ~ BEGIN self.properties _ CoreProperties.PutProp [self.properties, state.tech.checkedBy, $DrcWasHere] END; -- DoNotCheck noHull: Rect ~ [CD.Number.FIRST, CD.Number.FIRST, CD.Number.FIRST, CD.Number.FIRST]; AtomicWireHull: PUBLIC PROC [w: WireInstance, state: State] RETURNS [h: Rect] ~ BEGIN EachRect: CoreGeometry.EachInstanceProc ~ BEGIN r: Rect ~ CoreGeometry.InlineBBox [instance]; 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 state.attributes.HasGeometry [w.local] THEN BEGIN stored: REF Rect _ NARROW [CoreProperties.GetProp [w.local.properties, bbKey]]; IF (stored = NIL) THEN BEGIN h _ [CD.Number.LAST, CD.Number.LAST, CD.Number.FIRST, CD.Number.FIRST]; [] _ 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 _ CDBasicsInline.MapRect [h, w.transf] END ELSE h _ noHull END; -- AtomicWireHull CellHull: PROC [self: CoreCell, transf: Transf, state: State] RETURNS [Rect] ~ BEGIN RETURN [CDBasicsInline.MapRect [state.attributes.GetObject[self].bbox, transf]] END; -- CellHull SepAndNT: PROC [cell1, cell2: CoreCell, state: State] RETURNS [maxSep1, maxSep2: CD.Number, nt: TNT] ~ BEGIN SELECT cell1.class FROM CoreClasses.recordCellClass => BEGIN maxSep1 _ state.tech.maxSeparation; SELECT cell2.class FROM CoreClasses.recordCellClass => {maxSep2 _ state.tech.maxSeparation; nt _ state.nt}; CoreClasses.transistorCellClass => {maxSep2 _ state.tech.ctMaxSep; nt _ state.ctNt}; ENDCASE => {maxSep2 _ state.tech.maxSeparation; nt _ state.nt} END; CoreClasses.transistorCellClass => BEGIN maxSep1 _ state.tech.ctMaxSep; SELECT cell2.class FROM CoreClasses.recordCellClass => {maxSep2 _ state.tech.ctMaxSep; nt _ state.ctNt}; CoreClasses.transistorCellClass => {maxSep1 _ maxSep2 _ state.tech.ttMaxSep; nt _ state.ttNt}; ENDCASE => {maxSep2 _ state.tech.ctMaxSep; nt _ state.ctNt} END; ENDCASE => {maxSep1 _ maxSep2 _ state.tech.maxSeparation; nt _ state.nt}; END; -- SepAndNT CreateBindingTable: PROC [actual, public: Wire] RETURNS [bindingTable: RefTab.Ref] ~ BEGIN tableSize: NAT _ 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 [bindingTable.Fetch [actual].val]; IF boundActual = NIL THEN BEGIN name: ROPE ~ IF DrcDebug.debug THEN IO.PutFR1 ["NewSignal-%g", IO.card [state.wireCreationCount]] ELSE CoreOps.GetShortWireName[actual].Concat[" (NewSignal)"]; boundActual _ CoreOps.CreateWires [size: 0, name: name]; state.wireCreationCount _ state.wireCreationCount.SUCC; [] _ bindingTable.Insert [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 [bindingTable.Fetch [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: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State]; CellToCellProc: TYPE ~ PROC [cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State]; MaterialToCellSeparation: MaterialToCellProc ~ BEGIN SELECT cell.class FROM CoreClasses.recordCellClass => MaterialToRecordCellSeparation [cell, where, cellWire, materialWire, window, patches, state]; CoreClasses.transistorCellClass => MaterialToTransistorSeparation [cell, where, cellWire, materialWire, window, patches, state]; CoreClasses.unspecifiedCellClass => MaterialToUnspecifiedSeparation [cell, where, cellWire, materialWire, window, patches, state]; ENDCASE => BEGIN WHILE (cell.class # CoreClasses.recordCellClass) DO cell _ cell.class.recast [cell] ENDLOOP; MaterialToRecordCellSeparation [cell, where, cellWire, materialWire, window, patches, state] END END; -- MaterialToCellSeparation MaterialToTransistorSeparation: MaterialToCellProc ~ BEGIN wbb, tbb, int: Rect; -- bounding boxes boundInternal: WireInstance; atomicBoundInternal: WireSet; Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [CDBasics.Intersect [CDBasics.Extend [tbb, state.tech.ctMaxSep], wbb]] END; -- Intersect IF state.abort^ THEN RETURN; IF (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell], doNotAnalyse] # NIL) THEN RETURN; wbb _ AtomicWireHull [materialWire, state]; tbb _ CellHull [cell, cellWire.transf, state]; IF (wbb = noHull) OR (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; atomicBoundInternal[0].gateHint _ atomicBoundInternal[1].gateHint _ atomicBoundInternal[2].gateHint _ NEW [WireInstance _ atomicBoundInternal[0]]; IF (materialWire.global = boundInternal.global[1]) THEN materialWire.gateHint _ atomicBoundInternal[1].gateHint; IF (materialWire.global = boundInternal.global[2]) THEN materialWire.gateHint _ atomicBoundInternal[2].gateHint; int _ BloatedIntersection [wbb, tbb, state.tech.maxSeparation, state.tech.ctMaxSep]; FOR i: NAT IN [0 .. cell.public.size) DO IF state.abort^ THEN RETURN; state.tech.verifyWirePair [state: state, where: where, w1: atomicBoundInternal[i], w2: materialWire, relatedWires: patches, window: int] ENDLOOP END; -- MaterialToTransistorSeparation MaterialToUnspecifiedSeparation: MaterialToCellProc ~ BEGIN ERROR END; -- MaterialToUnspecifiedSeparation MaterialToRecordCellSeparation: MaterialToCellProc ~ BEGIN wbb, cbb, int: 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 (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell], doNotAnalyse] # NIL) OR (cellData.internal.size = 0) THEN RETURN; wbb _ AtomicWireHull [materialWire, state]; cbb _ CellHull [cell, cellWire.transf, state]; IF (wbb = noHull) OR (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; int _ BloatedIntersection [wbb, cbb, state.tech.maxSeparation, state.tech.maxSeparation]; FOR i: NAT IN [0 .. cellData.internal.size) DO state.tech.verifyWirePair [state: state, where: where, w1: atomicBoundInternal[i], w2: materialWire, relatedWires: patches, window: int] ENDLOOP; IF state.abort^ THEN RETURN; 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 w: Rect ~ BloatedIntersection [CellHull [cellData.instances[sub].type, propagatedActuals[sub].transf, state], wbb, state.tech.maxSeparation, state.tech.maxSeparation]; IF NOT CDBasics.NonEmpty [w] THEN LOOP; FOR i: NAT IN [0 .. boundInternal.local.size) DO IF state.abort^ THEN RETURN; MaterialToCellSeparation [cell: cellData.instances[sub].type, where: where, -- for error marking cellWire: propagatedActuals[sub], materialWire: [boundInternal.local[i], boundInternal.global[i], cellWire.transf], window: w, patches: patches, state: state] ENDLOOP 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, where, wire1, wire2, window, patches, state]; RETURN END ELSE BEGIN -- swap cell1Z: CoreCell ~ cell1; wire1Z: WireInstance ~ wire1; cell1 _ cell2; cell2 _ cell1Z; wire1 _ wire2; wire2 _ wire1Z END; ENDCASE => WHILE (cell2.class # CoreClasses.recordCellClass) DO cell2 _ cell2.class.recast [cell2] ENDLOOP; SELECT cell1.class FROM CoreClasses.recordCellClass => RecordCellToRecordCellSeparation [cell1, cell2, where, wire1, wire2, window, patches, state]; CoreClasses.transistorCellClass => TransistorToRecordCellSeparation [cell1, cell2, where, wire1, wire2, window, patches, state]; CoreClasses.unspecifiedCellClass => UnspecifiedToAnyClassSeparation [cell1, cell2, where, wire1, wire2, window, patches, state]; ENDCASE => BEGIN WHILE (cell1.class # CoreClasses.recordCellClass) DO cell1 _ cell1.class.recast [cell1] ENDLOOP; RecordCellToRecordCellSeparation [cell1, cell2, where, wire1, wire2, window, patches, state] END END; -- CellToCellSeparation TransistorToTransistorSeparation: CellToCellProc ~ BEGIN cbb1, cbb2, w: Rect; -- bounding boxes & window boundInternal1, boundInternal2: WireInstance; atomicBoundInternal1, atomicBoundInternal2: WireSet; IF state.abort^ THEN RETURN; IF (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell1], doNotAnalyse] # NIL) OR (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell2], doNotAnalyse] # NIL) THEN RETURN; cbb1 _ CellHull [cell1, wire1.transf, state]; cbb2 _ CellHull [cell2, wire2.transf, state]; IF (NOT CDBasics.Intersect [CDBasics.Extend[cbb1,state.tech.ttMaxSep], cbb2]) 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; atomicBoundInternal1[0].gateHint _ atomicBoundInternal1[1].gateHint _ atomicBoundInternal1[2].gateHint _ NEW [WireInstance _ atomicBoundInternal1[0]]; FOR i: NAT IN [0 .. boundInternal2.local.size) DO atomicBoundInternal2[i] _ [boundInternal2.local[i], boundInternal2.global[i], wire2.transf] ENDLOOP; atomicBoundInternal2[0].gateHint _ atomicBoundInternal2[1].gateHint _ atomicBoundInternal2[2].gateHint _ NEW [WireInstance _ atomicBoundInternal2[0]]; w _ BloatedIntersection [cbb1, cbb2, state.tech.ctMaxSep, state.tech.ctMaxSep]; 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, where: where, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j], relatedWires: patches, window: w] ENDLOOP ENDLOOP END; -- TransistorToTransistorSeparation TransistorToRecordCellSeparation: CellToCellProc ~ BEGIN cbb1, cbb2, bloated1, int: Rect; -- bounding boxes cellData2: CoreClasses.RecordCellType _ NARROW [cell2.data]; bindingTable2: RefTab.Ref; boundInternal1, boundInternal2: WireInstance; atomicBoundInternal1, atomicBoundInternal2, propagatedActuals2: WireSet; IF state.abort^ THEN RETURN; IF (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell1], doNotAnalyse] # NIL) OR (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell2], 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]; bloated1 _ CDBasics.Extend [cbb1, state.tech.ctMaxSep]; IF (NOT CDBasics.Intersect [bloated1, cbb2]) THEN RETURN; IF state.abort^ THEN RETURN; 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; atomicBoundInternal1[0].gateHint _ atomicBoundInternal1[1].gateHint _ atomicBoundInternal1[2].gateHint _ NEW [WireInstance _ atomicBoundInternal1[0]]; 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.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 [bindingTable2]; IF state.abort^ THEN RETURN; int _ BloatedIntersection [cbb1, cbb2, state.tech.ctMaxSep, state.tech.maxSeparation]; FOR i: NAT IN [0 .. wire1.global.size) DO FOR j: NAT IN [0 .. wire2.global.size) DO state.tech.verifyWirePair [state: state, where: where, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j], relatedWires: patches, window: int] ENDLOOP ENDLOOP; IF state.abort^ THEN RETURN; FOR i: NAT IN [0 .. wire1.global.size) DO FOR sub: NAT IN [0 .. cellData2.size) DO subTransf: Transf ~ ComposedTransf [wire2.transf, cellData2.instances[sub], state]; bb: Rect ~ CellHull [cellData2.instances[sub].type, subTransf, state]; IF NOT CDBasics.Intersect [bloated1, bb] THEN LOOP; MaterialToCellSeparation [cell: cellData2.instances[sub].type, where: where, -- for error marking cellWire: propagatedActuals2[sub], materialWire: atomicBoundInternal1[i], window: BloatedIntersection [cbb1, bb, state.tech.ctMaxSep, state.tech.maxSeparation], patches: NIL, state: state] ENDLOOP ENDLOOP END; -- TransistorToRecordCellSeparation UnspecifiedToAnyClassSeparation: CellToCellProc ~ BEGIN ERROR END; -- UnspecifiedToAnyClassSeparation RecordCellToRecordCellSeparation: CellToCellProc ~ BEGIN cbb1, cbb2, bloated1, int: Rect; -- bounding boxes subBB1, subBB2: BoundingBox; cellData1: CoreClasses.RecordCellType _ NARROW [cell1.data]; cellData2: CoreClasses.RecordCellType _ NARROW [cell2.data]; bindingTable1, bindingTable2: RefTab.Ref; boundInternal1, boundInternal2: WireInstance; atomicBoundInternal1, atomicBoundInternal2, propagatedActuals1, propagatedActuals2: WireSet; IF state.abort^ THEN RETURN; IF (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell1], doNotAnalyse] # NIL) OR (CDProperties.GetObjectProp [CoreGeometry.GetObject [state.attributes, cell2], 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]; bloated1 _ CDBasics.Extend [cbb1, state.tech.maxSeparation]; IF (NOT CDBasics.Intersect [bloated1, cbb2]) 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.size]]; FOR sub: NAT IN [0 .. cellData1.size) DO propagatedActuals1[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.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]; int _ BloatedIntersection [cbb1, cbb2, state.tech.maxSeparation, state.tech.maxSeparation]; FOR i: NAT IN [0 .. cellData1.internal.size) DO FOR j: NAT IN [0 .. cellData2.internal.size) DO state.tech.verifyWirePair [state: state, where: where, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j], relatedWires: patches, window: int] ENDLOOP ENDLOOP; subBB2 _ NEW [BoundingBoxRec[cellData2.size]]; FOR sub: NAT IN [0 .. cellData2.size) DO subBB2[sub] _ CellHull [cellData2.instances[sub].type, propagatedActuals2[sub].transf, state]; int _ BloatedIntersection [cbb1, subBB2[sub], state.tech.maxSeparation, state.tech.maxSeparation]; IF NOT CDBasics.NonEmpty [int] THEN LOOP; FOR i: NAT IN [0 .. cellData1.internal.size) DO MaterialToCellSeparation [cell: cellData2.instances[sub].type, where: where, -- for error marking cellWire: propagatedActuals2[sub], materialWire: atomicBoundInternal1[i], window: int, patches: patches, state: state] ENDLOOP ENDLOOP; subBB1 _ NEW [BoundingBoxRec[cellData1.size]]; FOR sub: NAT IN [0 .. cellData1.size) DO subBB1[sub] _ CellHull [cellData1.instances[sub].type, propagatedActuals1[sub].transf, state]; int _ BloatedIntersection [subBB1[sub], cbb2, state.tech.maxSeparation, state.tech.maxSeparation]; IF NOT CDBasics.NonEmpty [int] THEN LOOP; FOR i: NAT IN [0 .. cellData2.internal.size) DO MaterialToCellSeparation [cell: cellData1.instances[sub].type, where: where, -- for error marking cellWire: propagatedActuals1[sub], materialWire: atomicBoundInternal2[i], window: int, patches: patches, state: state] ENDLOOP ENDLOOP; FOR sub1: NAT IN [0 .. cellData1.size) DO obj1: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData1.instances[sub1].type]; maxSep1, maxSep2: CD.Number; nt: TNT; FOR sub2: NAT IN (sub1 .. cellData2.size) DO obj2: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData2.instances[sub2].type]; areaOfInterest: Rect; IF NOT CDBasics.Intersect [bloated1, CDBasicsInline.MapRect [obj2.bbox, propagatedActuals2[sub2].transf]] THEN LOOP; -- quick [maxSep1, maxSep2, nt] _ SepAndNT [cellData1.instances[sub1].type, cellData2.instances[sub2].type, state]; areaOfInterest _ BloatedIntersection [subBB1[sub1], subBB2[sub2], maxSep1, maxSep2]; IF NOT CDBasics.NonEmpty [areaOfInterest] THEN LOOP; 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, where: where, window: areaOfInterest, cell1: cellData1.instances[sub1].type, cell2: cellData2.instances[sub2].type, wire1: propagatedActuals1[sub1], wire2: propagatedActuals2[sub2], patches: patches] 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.PutF ["%g at [%g, %g, %g, %g]\n", IO.rope [longMsg], IO.int [e.r.x1], IO.int [e.r.y1], IO.int [e.r.x2], IO.int [e.r.y2]] 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. 4ÜDrcImpl.mesa Copyright Ó 1985, 1986, 1987, 1988 by Xerox Corporation. All rights reserved. Rewritten by gbb, January 12, 1987 11:55:49 am PST gbb March 3, 1988 4:56:49 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. 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. 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. Returns the intersection of the two rectangles each bloated by half the maximum separation. The rectangler are supposed to intersect. 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. Transistors return their geometry. [self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet _ NIL] [self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet _ NIL] Places a message in the error Log. To use the connectivity information 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 propagation has to be carried out before the internal wire is bound, because the propagation will establish which signals originate from the current level, and the result would be different if the binding is done first (this is a glitch in the implementation of the binding). 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. subTransf _ CoreGeometry.GetTrans [state.attributes, cellData.instances[sub]]; Obviously this is a hack put in afterwards, so it is as yucky as you can imagine: the sub cells are absolute, the internal is relative to its cell, and the peek list is relative to its parent. 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. w1: WireInstance ~ [boundInternal.local[i], boundInternal.global[i], identity]; 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. w2: WireInstance ~ [boundInternal.local[j], boundInternal.global[j], identity]; Check the separation between the internal of self and its subcells. On the outer-most level, the internal can be very large but have no layout. To catch this patological case, the nesting order of the two loops is inverted. Check the separation between the subcells of self. [self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet _ NIL] Transistors are atomic ChipNDale objects and are supposed to be correct by construction. Extraction of transistors moved to extractor. Order of wire is fixed by CoreClasses to gate, ch1, ch2, IF type=pE THEN Vdd. The gate is also marked, in order to allow the channels of two transistors to completely overlap. [self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet _ NIL] (self.class = CoreClasses.unspecifiedCellClass) OR (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL); Pruning Note that CD.Number = INT, hence the empty hull is not about the origin, to avoid the frequent degenerate case of a cell about the origin. 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. Nothing magic, just a table. 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. patches are a hint used for the case a design rule violation is fixed at the next lower hierarchichy level, as it often is the case with transistors. For convenience. [cell: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State] Find the bounding box of wire and check whether it has a non-empty intersection with the internal wire of the cell. Order of wire is fixed by CoreClasses to gate, ch1, ch2, IF type=pE THEN Vdd. The gate is also marked, in order to allow the channels of two transistors to completely overlap. Check each element of the transistor wire of the cell against each element of wire. [cell: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State] [cell: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList _ NIL, 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. subTransf: Transf ~ ComposedTransf [cellWire.transf, cellData.instances[sub], state]; [cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State] For convenience. [cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State] Preamble. Check the separation between the internal of cell1 and cell2. Order of wire is fixed by CoreClasses to gate, ch1, ch2, IF type=pE THEN Vdd. The gate is also marked, in order to allow the channels of two transistors to completely overlap. Order of wire is fixed by CoreClasses to gate, ch1, ch2, IF type=pE THEN Vdd. The gate is also marked, in order to allow the channels of two transistors to completely overlap. [cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State] Preamble. Check the separation between the internal of transistor and cell2. Order of wire is fixed by CoreClasses to gate, ch1, ch2, IF type=pE THEN Vdd. The gate is also marked, in order to allow the channels of two transistors to completely overlap. Check intersections between subcells of transistor wires and cell2. [cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList _ NIL, state: State] [cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList _ NIL, 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 gbb June 8, 1987 5:52:42 pm PDT The property $DoNotDRC is now looked up in the ChipNDale instead of the Core data structure by order from Bertrand Serlet. changes to: DIRECTORY, CheckRecord, MaterialToTransistorSeparation, MaterialToRecordCellSeparation, TransistorToTransistorSeparation, TransistorToRecordCellSeparation, RecordCellToRecordCellSeparation gbb June 9, 1987 1:02:40 pm PDT Added a hint on wire instances for speeding up the processing of transistors. changes to: MaterialToTransistorSeparation, TransistorToTransistorSeparation, TransistorToRecordCellSeparation gbb June 29, 1987 6:06:29 pm PDT While chasing a bug... changes to: DIRECTORY, CheckRecord: inverted the order of two loops to catch a patological case of the outer-most level's internal, noHull: new constant to avoid intersecting undefined hulls, AtomicWireHull: the initialization was lost, MaterialToTransistorSeparation: take advantage of noHull, MaterialToRecordCellSeparationtake advantage of noHull. gbb July 7, 1987 3:07:11 pm PDT Finished implementing the more agressive pruning for transistors. changes to: DIRECTORY, BloatedIntersection, CheckRecord, CellHull, SepAndNT, Intersect (local of MaterialToTransistorSeparation), MaterialToRecordCellSeparation, TransistorToTransistorSeparation, TransistorToRecordCellSeparation, RecordCellToRecordCellSeparation gbb September 18, 1987 2:41:12 pm PDT Transistors are atomic objects and used as such in ChipNDale, but cells in Core. This means that the requirement that cells be correct in isolation cannot be enforced for transistors. Therefore, internal wires are made accessible one level down in the hierarchy. changes to: ~, CheckRecord, CellToCellProc, CellToCellSeparation, TransistorToTransistorSeparation, TransistorToRecordCellSeparation, UnspecifiedToAnyClassSeparation, RecordCellToRecordCellSeparation gbb September 18, 1987 5:11:39 pm PDT Worked around a small glitch that prevented correct checking across hierarchy levels. changes to: CheckRecord: the propagation has to be carried out before the internal wire is bound, because the propagation will establish which signals originate from the current level, and the result would be different if the binding is done first (this is a glitch in the implementation of the binding)., CheckRecord gbb September 21, 1987 11:08:15 am PDT The gate is also marked with a gate hint, in order to allow the channels of two transistors to completely overlap. changes to: MaterialToTransistorSeparation, TransistorToTransistorSeparation, TransistorToRecordCellSeparation gbb September 25, 1987 4:06:26 pm PDT Added logging of anathemas changes to: CheckDesignRules gbb January 15, 1988 4:35:47 pm PST Introduced peeking one level down in the hierarchy for contacts on transistor channels, because designers (mostly for the standard cell library) are to lazy to design cells correct in isolation in this case. changes to: CheckRecord, CheckTransistor, DoNotCheck, MaterialToTransistorSeparation. gbb January 20, 1988 11:17:37 am PST A million of details for more peeking. changes to: CheckRecord Ê'˜codešœ ™ KšœN™NKšœ2™2K™ K˜—code2šœá™áquotešÑios"Ðos™5MšžH™HMšž™——šÏk ˜ LšŸœŸœ˜"Kšœ Ÿœ-˜;KšœŸœ˜1Kšœ Ÿœ#˜5KšœŸœ˜Kšœ Ÿœ\˜mKšœ Ÿœ“˜¥KšœŸœN˜[KšœŸœX˜lKšœ˜Kšœ Ÿœ8˜FKšŸœŸœ˜KšŸœŸœaŸœ˜wKšœŸœ5˜AKšœŸœŸœ˜*Kšœ Ÿœ˜!KšŸœŸœŸœ ˜7KšœŸœ˜Kšœ Ÿœ˜%Kšœ Ÿœ˜/—LšÐlnœŸœŸ˜LšŸœgŸœŸœŸœ(˜½LšŸœŸœ˜šœŸœŸœŸœ˜head™N™JKšœŸœŸœÏc!˜=KšœŸœŸœ¡˜1KšœŸœŸœ¡ ˜7Kš œŸœŸœ Ÿœ ŸœŸœŸœ˜EKš œ Ÿœ Ÿœ ŸœŸœŸœ¡+˜cKšœ ŸœŸœ1Ÿœ˜LKšœŸœŸœ˜—™Lšœ ŸœŸœ¡˜1—™Lšœ Ÿœ˜'LšœŸœ˜"Lšœ Ÿœ˜+Lšœ Ÿœ˜'LšœŸœ˜!LšŸœŸœŸœ˜—šœ ™ NšœV™VšœŸœŸœ/˜BK™)—šœŸœ,˜7K™——™"Kšœ Ÿœ ˜Kšœ Ÿœ ˜—™KšœŸœ ˜—šœ™Lšœ¡Ÿœ¡˜/LšœŸœ¡|˜¡Lšœ ŸœŸœ¡˜FKš œŸœŸœŸœŸœŸœ˜?Kš œ ŸœŸœŸœ Ÿœ˜*Kš œŸœŸœŸœŸœŸœ˜8——™šÏnœŸœŸœ;ŸœŸœ ŸœŸœŸœŸœŸœŸœŸœ ŸœŸ˜áK™unitšŸœŸ˜ šŸœ ŸœŸœŸ˜+KšœŸœŸœ˜ KšœŸœ˜KšŸœŸœ Ÿœ˜-KšœŸœŸœŸœ˜&KšŸ˜KšŸœ˜—Kšœ Ÿœ ˜KšŸœ˜—OšœŸœ ˜KšœŸœŸœ˜KšœŸœ˜Kšœ ŸœŸœ˜KšœŸœ"˜4Kšœ Ÿœ˜+KšœŸœ<˜HOš œŸœPŸœŸœŸœ¡'˜¥KšœEŸœŸœ ˜iOšÐksÏs£¤:£¤£¤£¤£¤£¤£¤£¤£¤£¤ ˜ÏKšœ ˜ KšŸœ Ÿœ.˜AKšœŸœ ŸœŸœ ŸœŸœŸœŸœ˜FKšœ:˜:KšœC˜CšŸœŸœŸ˜KšœG˜GKšŸœ˜—Kšœ˜Kš ŸœŸœŸœŸœ ŸœŸœ˜RKšœ˜O™KšœhŸœ.Ÿœ˜¥KšœŸœ ˜%KšŸœŸœŸœ1˜VKšŸœ@˜DšŸœŸœŸ˜Kšœ>˜>KšŸœ˜—šŸœŸœŸ˜K™ Kš£¤£¤˜&KšœQŸœ˜VKšœ<˜KšŸœŸœ Ÿœ5Ÿœ3Ÿœ=ŸœŸœŸœŸœ˜—šŸ˜KšœÏeœ™.šœ ¡"˜/Kšœ5˜5——KšŸœ¡˜——™š ¢œŸœ1Ÿœ ŸœŸ˜dIabsolute™)KšŸœ\˜bKšŸœ¡˜—š ¢œŸœŸœ Ÿœ ŸœŸ˜YP™…KšŸœU˜[KšŸœ¡˜——™š œ ŸœŸœ>ŸœŸœ˜rK™K™ÂK™#—š¢ œŸ˜ Kšœ=ŸœŸœ™ZšŸœ Ÿ˜KšœŸœ-˜RKšœ#Ÿœ1˜ZKšœ$Ÿœ˜*šŸœŸ˜šŸœ,Ÿ˜3Kšœ˜KšŸœ˜—Kšœ.˜.KšŸ˜——KšŸœ¡˜—š¢ œŸ˜Kšœ=ŸœŸœ™ZKšœ%˜%Kšœ˜Kšœ˜Kšœ1¡˜HKšœ Ÿœ"˜/Kšœ*˜*Kšœ˜Kšœ˜š ¢ œŸœ ŸœŸœŸ˜3K™"šŸœ ŸœŸ˜Kšœ3Ÿœ˜FKšœQŸœ˜UKšŸ˜—KšŸœ¡˜—OšŸœ]ŸœŸœŸœŸœ@ŸœŸœÐbkœ˜ÒKšœ Ÿœ ˜Kš Ÿœ ŸœŸœ ŸœŸœ˜Eš£¤£¤£˜Kš¤;£¤£¤£¤ ˜tKš ¤>£¤ £¤ £¤ £¤˜ÅKš£¤˜—KšŸœŸœ!¦œ˜=Kšœ˜Kšœ˜Kšœ Ÿœ)˜:Kšœ^˜^O™§KšœH˜HO™ŒKšœŸœ˜4šŸœŸœŸœŸ˜'KšœŸœ¡œ€˜¸KšŸœ˜—Kšœc˜cKšœŸœ(˜AšŸœŸœŸœ!Ÿ˜0KšœR˜RKšŸœ˜—š£¤£¤£˜Kš¤F£¤˜YKš¤˜Kš¤0£¤˜CKš¤)˜)Kš£¤˜—Ošœ!˜!Ošœy¥œ$¥œ±™êšŸœŸœŸœŸ˜'KšœL˜LKšœ¦˜¦KšœN™NšŸœŸœŸœŸ˜!šŸœŸœŸœ1Ÿ˜@K™ÀKšœ!˜!KšŸœ˜—KšœŸœ˜5KšŸœ˜—KšŸœŸœ!¦œ˜=KšŸœ˜—O™š£¤£¤£˜Kš¤9£¤˜LKš¤)˜)Kš£¤˜—š£¤£¤£˜š£¤0£¤£¤£˜FJš¤7˜7Kš¤]˜]Jš£¤˜—Kš£¤˜—KšÏbœd™kšŸœŸœŸœŸ˜.Kšœy¥œ™€KšœO™OKšœM˜MKšœ Ðlz œŸ˜.š£¤£¤£˜Kš¤%£¤˜8Kš¤?˜?Kš£¤˜—šŸœŸœŸœŸ˜.K™ÕKšœO™OKšœM˜MKšœ ¨œ6˜OKšŸœŸœ!¦œ˜=KšŸ˜—KšŸœ˜—Ošœ-¥œ™CKšœŸœ!˜,š£¤£˜Kš¤`£¤˜s—šŸœŸœŸœŸ˜'Kšœ[˜[KšŸœ˜ —K™›šŸœŸœŸœŸ˜.KšŸœŸœ7ŸœŸœ˜GšŸœŸœŸœŸ˜'š£¤£˜Kš¤K£¤ £¤@£¤ £¤£¤£¤£¤˜‚—KšœU¡œ¨˜‘KšŸœŸœ!¦œ˜=KšŸ˜—KšŸœ˜—Ošœ-¥œ™2Kšœ Ÿœ ˜KšŸœŸœE˜SšŸœŸœŸœŸ˜(KšœŸœ Ÿœ˜%KšœW˜WKšœJ¡˜RšŸœŸœŸœŸ˜+KšœW˜WKšœ˜Kš ŸœŸœ,ŸœŸœ¡˜EKšœh˜hKšœR˜RKšŸœŸœ$ŸœŸœ˜4KšŸœŸœ¦ŸœŸœ˜½š£¤£¤£˜Kš ¤n£¤A£¤)£¤)£¤)£¤)˜ÝKš ¤E£¤A£¤)£¤)£¤)£¤(˜³Kš£¤˜—Lšœù˜ùKšŸœŸœ!¦œ˜—KšŸœ˜—šœ#Ÿ˜(Kšœ˜šŸœ Ÿ˜šœ˜Kšœ1˜1—šœ"˜"Kšœ;˜;—KšŸœ4˜;—KšŸœ˜—KšŸœB˜I—KšŸœ¡ ˜——™š¢œŸœŸœŸ˜ZKšœ Ÿœ˜šŸœŸœŸ˜'š£¤£¤£˜Kš¤£¤5˜KKš¤˜Kš¤£¤˜0Kš¤˜Kš£˜Kš£˜—KšŸœ3˜7KšŸœ˜—KšŸœ ŸœŸœŸœ˜9Kšœ)˜)šŸœŸœŸœŸ˜#KšœD˜DKšŸ˜—KšŸœ¡˜—š¢œŸœŸœŸ˜AKšœŸ˜KšŸœ¡˜—š¢œŸœ8ŸœŸ˜qK™Jš ¢œŸœ8ŸœŸœŸ˜wK™÷KšœŸœ#˜7šŸœŸœŸœŸ˜šœŸœŸœŸ˜#KšŸœŸœ ˜=KšŸœ9˜=—Kšœ8˜8Kšœ2Ÿœ˜7Kšœ8˜8KšŸ˜—KšŸœW˜[KšŸœ¡˜—Ošœ0˜0šŸœŸœŸœŸ˜#KšœA˜AKšŸ˜—KšŸœ¡˜—š¢ œŸœ:ŸœŸ˜qK™9Kšœ,˜,šŸœŸœŸœŸ˜%Kšœ Ÿœ(˜8K™]KšŸœŸœŸœ˜&KšŸ˜—KšŸœ¡˜—š¢œŸœ&ŸœŸ˜TKšœ˜Kš ŸœŸœŸœŸœŸ˜=KšŸœ¡˜——™%šœŸœŸœmŸœ˜ŸLšœÔ™Ô—šœŸœŸœkŸœ˜™KšœÔ¥œŽ™é—š¢œŸ˜4Kšœ™šŸœ Ÿ˜Kšœ|˜|Kšœ€˜€Kšœ‚˜‚šŸœŸ˜šŸœ,Ÿ˜3Kšœ˜KšŸœ˜—Kšœ\˜\KšŸ˜——KšŸœ¡˜ —š¢œŸ˜:LšœlŸœ™~Kšœ¡˜&Kšœ˜Kšœ˜š ¢ œŸœŸœŸœŸœŸ˜-KšŸœG˜MKšŸœ¡ ˜—OšŸœŸœ¦œ˜KšŸœ_ŸœŸœ¦œ˜rOšœ¥œV™sKšœ+˜+Kšœ.˜.Kš ŸœŸœŸœŸœ¦œ˜4Ošœe˜eKšœŸœ ˜9šŸœŸœŸœŸ˜(Kšœ[˜[KšŸœ˜—Kšœ¯™¯KšœfŸœ)˜’šŸœ0Ÿ˜7Kšœ8˜8—šŸœ0Ÿ˜7Kšœ8˜8—OšœN¥œ™SKšœT˜TšŸœŸœŸœŸ˜(KšŸœŸœŸœ˜Kšœ ¨œo˜ˆKšŸ˜—KšŸœ¡!˜&—š¢œŸ˜;KšœlŸœ™~KšŸ˜KšŸœ¡"˜'—š¢œŸ˜;KšœlŸœ™~Kšœ¡˜&Kšœ˜Kšœ0˜0Kšœ˜Kšœ'Ÿœ ˜:š ¢ œŸœŸœŸœŸœŸ˜-KšŸœL˜RKšŸœ¡ ˜—OšŸœŸœ¦œ˜Kš Ÿœ_ŸœŸœŸœ¦œ˜’Ošœ¥œV™sKšœ+˜+Kšœ.˜.Kš ŸœŸœŸœŸœ¦œ˜3OšœL¥œ™QKšœQ˜QKšœl˜lKšœŸœ&˜?šŸœŸœŸœŸ˜.Kšœ[˜[KšŸœ˜—KšœY˜YšŸœŸœŸœŸ˜.Kšœ ¨œo˜ˆKšŸœ˜—O™tKšŸœŸœ¦œ˜KšœŸœ˜4šŸœŸœŸœŸ˜'KšœŸœ¡œ‰˜ÁKšŸœ˜—Ošœ!˜!Ošœ(¥œ¥œ™6šŸœŸœŸœŸ˜'KšœU™UKšœ§˜§KšŸœŸœŸœŸœ˜'šŸœŸœŸœ!Ÿ˜0KšŸœŸœŸœ˜KšœL¡œŸ˜ÿKšŸ˜—KšŸ˜—KšŸœ¡!˜&—š¢œŸ˜,LšœjŸœ™|K™šŸœ Ÿ˜Kšœ$Ÿœ˜*KšœŸœ¡˜4šœ#¡ ˜0šŸœ/ŸœŸ˜;Kšœ]˜]Kš¦˜KšŸ˜—šŸœŸœ¡˜Kšœ7˜7Kšœ˜Kšœ˜KšŸœ˜——KšŸœŸœ-Ÿœ$Ÿœ˜k—šŸœ Ÿ˜Kšœ|˜|Kšœ€˜€Kšœ€˜€šŸœŸ˜šŸœ-Ÿ˜4Kšœ"˜"KšŸœ˜—Kšœ\˜\KšŸ˜——KšŸœ¡˜—š¢ œŸ˜8LšœjŸœ™|Kšœ¡˜/Kšœ-˜-Kšœ4˜4O™ KšŸœŸœ¦œ˜Kš Ÿœ`ŸœŸœ`ŸœŸœ¦œ˜ÚKšœ-˜-Kšœ-˜-KšŸœŸœGŸœ¦œ˜ZOšœ-¥œ¥œ™=KšŸœŸœ¦œ˜Kšœb˜bKšœb˜bKšœŸœ)˜CKšœŸœ)˜CšŸœŸœŸœ"Ÿ˜1Kšœ[˜[KšŸœ˜—Kšœ¯™¯KšœiŸœ*˜–šŸœŸœŸœ"Ÿ˜1Kšœ[˜[KšŸœ˜—Kšœ¯™¯KšœiŸœ+˜—KšœO˜OšŸœŸœŸœŸ˜)šŸœŸœŸœŸ˜)KšŸœŸœ¦œ˜Kšœ ¨œy˜’KšŸ˜—KšŸ˜—KšŸœ¡#˜(—š¢ œŸ˜8LšœjŸœ™|Kšœ!¡˜2Kšœ(Ÿœ˜