<> <> <> <> <> DIRECTORY BasicTime USING [GMT, Now, Period], CD USING [CreateDrawRef, Design, DrawProc, DrawRectProc, DrawRef, Error, FetchObjectClass, Instance, InstanceList, Layer, Number, Object, ObjectClass, Orientation, Position, Rect, Technology], CDAtomicObjects USING [AtomicObsPtr, DrawList], CDBasics USING [Extend, Intersect, Intersection, NonEmpty], CDCommandOps USING [CallWithResource], CDErrors USING [IncludeMessage, RemoveMessages], CDEvents USING [EventProc, RegisterEventProc], CDMenus USING [CreateEntry], CDOps USING [ObjectInfo, InstList], CDOrient USING [MapRect], CDProperties USING [CopyVal, GetProp, GetObjectProp, InstallProcs, PutProp, PutObjectProp, RegisterProperty], CDSequencer USING [Command, ImplementCommand], CDSimpleRules USING [GetLayer], CMosB USING [cmosB, cut2, lambda], CStitching USING [all, Area, ChangeRect, DumpCache, Rect, EnumerateArea, ResetTesselation, ChangeEnumerateArea, ListArea, NewTesselation, RectProc, TileProc, Region, Tesselation, Tile], CSMonitor USING [Monitor, PaintPredicate, Reset], FS USING [StreamOpen], IO USING [Close, int, PutF1, PutFR, PutFR1, PutRope, rope, STREAM], PrincOpsUtils USING [], Process USING [priorityBackground, SetPriority], Rope USING [Cat, ROPE], TerminalIO USING [WriteChar, WriteRope], ViaFlatness USING [ErrorList, Rule], ViewerClasses USING [Viewer]; ViaFlatnessImpl: CEDAR PROGRAM IMPORTS BasicTime, CD, CDBasics, CDCommandOps, CDErrors, CDEvents, CDMenus, CDOps, CDOrient, CDProperties, CDSequencer, CDSimpleRules, CMosB, CStitching, CSMonitor, FS, IO, Process, Rope, TerminalIO EXPORTS ViaFlatness ~ BEGIN debug: BOOL _ FALSE; timing: BOOL _ FALSE; Region: TYPE ~ LIST OF REF CStitching.Region; Tess: TYPE ~ CStitching.Tesselation; Tile: TYPE ~ CStitching.Tile; empty: REF ~ NIL; nothing: REF INT ~ NEW [INT]; externalKey: ATOM ~ $GismoVF; -- used in traffic with external packages checkedKey: ATOM ~ $GismoVFChecked; -- used to mark checked cells errorKey: ATOM ~ $GismoVFError; doNotAnalyse: ATOM = $DoNotDRC; State: TYPE ~ REF StateRec; StateRec: TYPE ~ RECORD [design: CD.Design, abort: REF BOOL, viaTess, materialTess: Tess, errors: ErrorList, errorTotal: INT _ 0, errorLog: IO.STREAM]; ErrorList: TYPE ~ ViaFlatness.ErrorList; <> ViaTileData: TYPE ~ REF ViaTileDataRec; ViaTileDataRec: TYPE ~ RECORD [ hasDiff: BOOL _ FALSE, -- diffusion hasPoly: BOOL _ FALSE, -- polysilicide hasFOx: BOOL _ FALSE]; -- field oxide MatTileData: TYPE ~ REF MatTileDataRec; MatTileDataRec: TYPE ~ RECORD [layerKey: ATOM, via: ViaTileData]; diffLayers: CD.Layer ~ 4; -- DANGER: if you changes this, see ConvertMat. diffKey: ARRAY [0 .. diffLayers) OF ATOM ~ [$ndif, $pdif, $nwelCont, $pwelCont]; genericDiff: ATOM ~ $diff; polyKey: ATOM ~ $pol; diffLayer: ARRAY [0 .. diffLayers) OF CD.Layer; polyLayer: CD.Layer; fieldOxide: REF ANY ~ NIL; <> <> Rule: TYPE ~ ViaFlatness.Rule; <> viaOnFieldOxideAvoidsDiff: Rule ~ [2 * viaOnFieldOxideAvoidsPoly: Rule ~ [2 * fieldOxideSurroundsViaOnFieldOxide: Rule ~ [MAX [viaOnFieldOxideAvoidsDiff.extent, viaOnFieldOxideAvoidsPoly.extent], "separation to poly or diff"]; diffSurroundsViaOnDiff: Rule ~ [2 * polySurroundsViaOnPoly: Rule ~ [3 * <> viaOnPolyAndDiff: Rule ~ [1 * viaSeparation: Rule ~ [1 * viaOverGate: Rule ~ [1 * viaOverPoly: Rule ~ [1 * <> Verify: PUBLIC PROC [c: CD.Instance, d: CD.Design _ NIL, abort: REF BOOL _ NIL] RETURNS [errors: ErrorList, errorTotal: INT] ~ BEGIN <> state: State; o: CD.Object = c.ob; checked: BOOL = (CDProperties.GetObjectProp [o, checkedKey] # NIL); oldErrors: ErrorList = NARROW [CDProperties.GetObjectProp [o, externalKey]]; designName: Rope.ROPE = IF d#NIL THEN d.name ELSE "in the sky"; viaViewer, matViewer: ViewerClasses.Viewer; IF checked THEN BEGIN TerminalIO.WriteChar [':]; RETURN [oldErrors, -1] END; IF (CDProperties.GetObjectProp [o, doNotAnalyse] # NIL) THEN BEGIN TerminalIO.WriteChar [' ]; RETURN [oldErrors, -1] END; IF (d.technology # CMosB.cmosB) THEN BEGIN TerminalIO.WriteRope [Rope.Cat ["Technology is not ", CMosB.cmosB.name, ".\n"]]; RETURN [NIL, -1] END; IF (d # NIL) THEN CDErrors.RemoveMessages [d, o, errorKey]; state _ NEW [StateRec _ [design: d]]; state.abort _ IF (abort # NIL) THEN abort ELSE NEW [BOOL _ FALSE]; state.errorLog _ FS.StreamOpen [fileName: "[]<>Temp>DRC>ViaFlatness.log", accessOptions: create, keep: 5, extendFileProc: NIL, remoteCheck: FALSE, wDir: NIL]; -- keep is 5 because of incremental use state.errorLog.PutF1 ["Error log by ViaFlatness. Design: %g\n\n", IO.rope [designName]]; CreateTess [state]; IF debug THEN BEGIN viaViewer _ CSMonitor.Monitor [state.viaTess, "Via Tesselation", NIL]; matViewer _ CSMonitor.Monitor [state.materialTess, "Material Tesselation", DarkPoly] END; <> EnumerateDesign [c, state, TRUE]; IF debug THEN CSMonitor.Reset [viaViewer]; <> EnumerateDesign [c, state, FALSE]; IF debug THEN CSMonitor.Reset [matViewer]; <> CStitching.EnumerateArea [plane: state.viaTess, rect: CStitching.all, eachTile: LookUnderneath, data: state, skip: empty]; IF debug THEN {CSMonitor.Reset [viaViewer]; CSMonitor.Reset [matViewer]} ELSE DestroyTess [state]; CDProperties.PutObjectProp [o, checkedKey, checkedKey]; CDProperties.PutObjectProp [o, externalKey, state.errors]; state.errorLog.PutRope ["\nDRC terminated normally."]; state.errorLog.Close []; RETURN [state.errors, state.errorTotal] END; -- Verify InteractiveCall: PROC [comm: CDSequencer.Command] ~ BEGIN <> abort: REF BOOL _ NEW [BOOL _ FALSE]; startTime, stopTime: BasicTime.GMT; -- for internal use errorSummary: LIST OF RECORD [c: Rope.ROPE, n: INT]; VerifySelected: PROC [comm: CDSequencer.Command] ~ BEGIN <> TRUSTED {Process.SetPriority [Process.priorityBackground]}; FOR all: CD.InstanceList _ CDOps.InstList [comm.design], all.rest WHILE all # NIL DO IF all.first.selected THEN BEGIN IF debug THEN CDProperties.PutObjectProp [all.first.ob, checkedKey, NIL]; errorSummary _ CONS [[CDOps.ObjectInfo[all.first.ob], Verify [all.first, comm.design, abort].errorTotal], errorSummary]; TerminalIO.WriteChar ['.] END ENDLOOP END; -- VerifySelected TerminalIO.WriteRope ["Checking via flatness.\n"]; startTime _ BasicTime.Now []; [] _ CDCommandOps.CallWithResource [VerifySelected, comm, externalKey, abort]; stopTime _ BasicTime.Now []; IF (errorSummary # NIL) THEN BEGIN TerminalIO.WriteRope ["\nError summary (via flatness):\n"]; FOR e: LIST OF RECORD [c: Rope.ROPE, n: INT] _ errorSummary, e.rest WHILE e # NIL DO <> <> IF (e.first.n > 0) THEN TerminalIO.WriteRope [IO.PutFR ["%g: %g.\n", IO.rope[e.first.c], IO.int[e.first.n]]] ENDLOOP END; -- write error summary TerminalIO.WriteRope ["Via flatness verification done.\n"]; IF timing THEN TerminalIO.WriteRope [Rope.Cat ["Total elapsed time: ", TimeToRope [startTime, stopTime], "\n"]] END; -- InteractiveCall TimeToRope: PROC [from, to: BasicTime.GMT] RETURNS [time: Rope.ROPE] ~ BEGIN <> tmp: Rope.ROPE; sec: INT = BasicTime.Period [from, to]; min: INT = sec / 60; h: INT = min / 60; tmp _ IO.PutFR1 [value: IO.int [h]]; time _ SELECT h FROM = 0 => "00", < 10 => Rope.Cat ["0", tmp], ENDCASE => tmp; tmp _ IO.PutFR1 [value: IO.int [min MOD 60]]; time _ Rope.Cat [time, ":", SELECT min FROM = 0 => "00", < 10 => Rope.Cat ["0", tmp], ENDCASE => tmp]; tmp _ IO.PutFR1 [value: IO.int [sec MOD 60]]; time _ Rope.Cat [time, ":", SELECT sec FROM = 0 => "00", < 10 => Rope.Cat ["0", tmp], ENDCASE => tmp] END; -- TimeToRope <> ObjConversionProc: TYPE = PROCEDURE [inst: CD.Instance, pos: CD.Position, orient: CD.Orientation, state: State]; ExchangeCDInfo: PROC ~ BEGIN <> tech: CD.Technology = CMosB.cmosB; Register: PROC [tech: CD.Technology, class: ATOM, proc: ObjConversionProc] ~ BEGIN cl: CD.ObjectClass _ CD.FetchObjectClass [objectType: class, technology: tech]; IF cl = NIL THEN CD.Error [explanation: "Version mismatch."]; CDProperties.PutProp [onto: cl.properties, prop: externalKey, val: NEW [ObjConversionProc_proc]] END; -- Register Register [tech, $C2Via, ConvertVia]; Register [tech, $C2LargeVia, ConvertVia]; FOR l: CD.Layer IN [0 .. diffLayers) DO diffLayer[l] _ CDSimpleRules.GetLayer [tech, diffKey[l]] ENDLOOP; polyLayer _ CDSimpleRules.GetLayer [tech, polyKey] END; -- ExchangeCDInfo ConvertVia: ObjConversionProc = BEGIN <<[inst: CD.Instance, pos: CD.Position, orient: CD.Orientation, state: State]>> data: ViaTileData _ NEW [ViaTileDataRec]; FOR geom: CDAtomicObjects.DrawList _ NARROW [inst.ob.specificRef, CDAtomicObjects.AtomicObsPtr].rList, geom.rest WHILE geom # NIL DO SELECT geom.first.lev FROM CMosB.cut2 => BEGIN r: CD.Rect = CDOrient.MapRect [itemInCell: geom.first.r, cellSize: inst.ob.size, cellInstOrient: orient, cellInstPos: pos]; InsertVia [r: r, state: state, data: data] END; ENDCASE => NULL ENDLOOP END; -- ConvertVia ConvertMat: PROC [r: CD.Rect, layer: CD.Layer, state: State] = BEGIN <> bloatedRect: CD.Rect; vias: Region; IF NOT CDBasics.NonEmpty[r] THEN RETURN; -- empty rectangle IF state.abort^ THEN ERROR ABORTED; SELECT layer FROM diffLayer[0], diffLayer[1], diffLayer[2], diffLayer[3] => BEGIN -- DANGER bloatedRect _ CDBasics.Extend [r, viaOnFieldOxideAvoidsDiff.extent]; vias _ CStitching.ListArea [plane: state.viaTess, rect: bloatedRect]; FOR v: Region _ vias, v.rest WHILE v # NIL DO via: ViaTileData _ NARROW [v.first.value]; IF via.hasPoly THEN BEGIN FlagViolation [viaOverGate, v.first.rect, state]; DeleteRect [state.viaTess, v.first.rect]; RETURN END ENDLOOP; bloatedRect _ CDBasics.Extend [r, diffSurroundsViaOnDiff.extent]; vias _ CStitching.ListArea [plane: state.viaTess, rect: bloatedRect]; FOR v: Region _ vias, v.rest WHILE v # NIL DO via: ViaTileData = NARROW [v.first.value]; IF CDBasics.Intersect [v.first.rect, r] THEN via.hasDiff _ TRUE; <> bloatedRect _ CDBasics.Extend [v.first.rect, diffSurroundsViaOnDiff.extent]; InsertMat [genericDiff, CDBasics.Intersection[bloatedRect, r], via, state] <> ENDLOOP END; polyLayer => BEGIN vias _ CStitching.ListArea [plane: state.viaTess, rect: r]; FOR v: Region _ vias, v.rest WHILE v # NIL DO via: ViaTileData = NARROW [v.first.value]; IF (via # empty) THEN BEGIN FlagViolation [viaOverPoly, v.first.rect, state]; DeleteRect [state.viaTess, v.first.rect]; RETURN END ENDLOOP; bloatedRect _ CDBasics.Extend [r, viaOnFieldOxideAvoidsPoly.extent]; vias _ CStitching.ListArea [plane: state.viaTess, rect: bloatedRect]; FOR v: Region _ vias, v.rest WHILE v # NIL DO via: ViaTileData = NARROW [v.first.value]; IF via.hasDiff THEN BEGIN FlagViolation [viaOverGate, v.first.rect, state]; DeleteRect [state.viaTess, v.first.rect]; RETURN END ENDLOOP; bloatedRect _ CDBasics.Extend [r, polySurroundsViaOnPoly.extent]; vias _ CStitching.ListArea [plane: state.viaTess, rect: bloatedRect]; FOR v: Region _ vias, v.rest WHILE v # NIL DO via: ViaTileData = NARROW [v.first.value]; IF CDBasics.Intersect [v.first.rect, r] THEN via.hasPoly _ TRUE; <> bloatedRect _ CDBasics.Extend [v.first.rect, polySurroundsViaOnPoly.extent]; InsertMat [polyKey, CDBasics.Intersection[bloatedRect, r], via, state] <> ENDLOOP END; ENDCASE => NULL END; -- ConvertMat ExtractObject: CD.DrawProc ~ BEGIN <<[inst: Instance, pos: Position, orient: Orientation, pr: REF DrawInformation]>> convProc: REF ObjConversionProc; state: State = NARROW [pr.devicePrivate]; IF state.abort^ THEN ERROR ABORTED; SELECT inst.ob.class.objectType FROM $Rect => NULL; $Cell => inst.ob.class.drawMe [inst, pos, orient , pr]; ENDCASE => BEGIN convProc _ NARROW [CDProperties.GetProp [from: inst.ob.class.properties, prop: externalKey], REF ObjConversionProc]; IF convProc # NIL THEN convProc^[inst, pos, orient, state] ELSE inst.ob.class.drawMe [inst, pos, orient, pr] END END; -- ExtractObject ExtractRect: CD.DrawRectProc ~ BEGIN <<[r: Rect, l: Layer, pr: DrawRef]>> state: State ~ NARROW [pr.devicePrivate, State]; ConvertMat [r, l, state] END; -- ExtractRect EnumerateDesign: PROC [inst: CD.Instance, state: State, vias: BOOL] ~ BEGIN <> dr: CD.DrawRef = CD.CreateDrawRef [[]]; IF vias THEN dr.drawChild _ ExtractObject ELSE dr.drawRect _ ExtractRect; dr.devicePrivate _ state; dr.stopFlag _ state.abort; inst.ob.class.drawMe [inst, inst.location, inst.orientation, dr] END; -- EnumerateDesign <> CreateTess: PROC [state: State] ~ BEGIN <> state.viaTess _ CStitching.NewTesselation [stopFlag: state.abort]; state.materialTess _ CStitching.NewTesselation [stopFlag: state.abort] END; -- CreateTess DestroyTess: PROC [state: State] ~ BEGIN <> CStitching.ResetTesselation [state.viaTess]; CStitching.ResetTesselation [state.materialTess]; CStitching.DumpCache []; state.viaTess _ state.materialTess _ NIL END; -- DestroyTess InsertVia: PROC [r: CD.Rect, state: State, data: ViaTileData] ~ BEGIN <> viaSpacingViolation, splitVia: BOOL _ FALSE; errorRect: CD.Rect; OccupyByVia: CStitching.RectProc = BEGIN <<[plane: REF Tesselation, rect: Rect, oldValue: REF, data: REF]>> Split: PROC RETURNS [BOOL] ~ INLINE BEGIN RETURN [(rect.x1= r.x1) AND (rect.y1= r.y1) AND (rect.x2= r.x2) AND (rect.y2= r.y2)] END; -- Split WITH oldValue SELECT FROM v: ViaTileData => IF Split [] THEN splitVia _ TRUE ELSE {viaSpacingViolation _ TRUE; errorRect _ rect}; ENDCASE => IF NOT (viaSpacingViolation OR splitVia) THEN CStitching.ChangeRect [plane: state.viaTess, rect: rect, new: data] END; -- OccupyByVia CStitching.ChangeEnumerateArea [state.viaTess, r, OccupyByVia, data, nothing]; IF viaSpacingViolation THEN BEGIN DeleteRect [state.viaTess, r]; FlagViolation [viaSeparation, errorRect, state] END END; -- InsertVia InsertMat: PROC [mat: ATOM, rect: CD.Rect, via: ViaTileData, state: State] ~ BEGIN <> data: MatTileData = NEW [MatTileDataRec _ [mat, via]]; viaOverGateViolation: BOOL _ FALSE; errorRect: CD.Rect; OccupyByMaterial: CStitching.RectProc = BEGIN <<[plane: REF Tesselation, rect: Rect, oldValue: REF, data: REF]>> WITH oldValue SELECT FROM m: MatTileData => IF (m.layerKey # mat) THEN BEGIN viaOverGateViolation _ TRUE; errorRect _ rect END; ENDCASE => IF NOT viaOverGateViolation THEN CStitching.ChangeRect [plane: state.materialTess, rect: rect, new: data] END; -- OccupyByMaterial CStitching.ChangeEnumerateArea [state.materialTess, rect, OccupyByMaterial, data, nothing]; IF viaOverGateViolation THEN BEGIN DeleteRect [state.viaTess, rect]; FlagViolation [viaOnPolyAndDiff, errorRect, state] END END; -- InsertMat DeleteRect: PROC [plane: Tess, rect: CD.Rect] ~ BEGIN plane.ChangeRect [rect, empty] END; -- DeleteRect <> LookUnderneath: CStitching.TileProc ~ BEGIN -- PROC [tile: REF Tile, data: REF] <> state: State ~ NARROW [data]; via: ViaTileData ~ NARROW [tile.value]; rect: CStitching.Rect ~ CStitching.Area [tile]; IsOnFieldOxide: PROC RETURNS [d: BOOL _ TRUE] ~ INLINE BEGIN <> FOR cut: Region _ CStitching.ListArea [state.materialTess, rect], cut.rest WHILE cut # NIL DO IF (cut.first.value # NIL) THEN RETURN [FALSE] ENDLOOP END; -- IsOnFieldOxide Accumulate: CStitching.TileProc ~ BEGIN via: ViaTileData ~ NARROW [data]; mat: MatTileData ~ NARROW [tile.value]; IF (mat = fieldOxide) THEN via.hasFOx _ TRUE -- [field oxide is represented by ELSE BEGIN IF (mat.via # via) THEN ERROR; -- Consistency test: Tesselation screwed up SELECT mat.layerKey FROM genericDiff => via.hasDiff _ TRUE; polyKey => via.hasPoly _ TRUE; ENDCASE => ERROR END END; -- Accumulate IF state.abort^ THEN ERROR ABORTED; <> via.hasFOx _ NOT (via.hasDiff OR via.hasPoly); SELECT TRUE FROM via.hasDiff => BEGIN CStitching.EnumerateArea [plane: state.materialTess, rect: CDBasics.Extend [rect, diffSurroundsViaOnDiff.extent], eachTile: Accumulate, data: via, skip: $doNotSkipAnything]; IF via.hasFOx THEN IF IsOnFieldOxide[] THEN FlagViolation [viaOnFieldOxideAvoidsDiff, rect, state] ELSE FlagViolation [diffSurroundsViaOnDiff, rect, state] END; via.hasPoly => BEGIN CStitching.EnumerateArea [plane: state.materialTess, rect: CDBasics.Extend [rect, polySurroundsViaOnPoly.extent], eachTile: Accumulate, data: via, skip: $doNotSkipAnything]; IF via.hasFOx THEN IF IsOnFieldOxide[] THEN FlagViolation [viaOnFieldOxideAvoidsPoly, rect, state] ELSE FlagViolation [polySurroundsViaOnPoly, rect, state] END; via.hasFOx => BEGIN CStitching.EnumerateArea [plane: state.materialTess, rect: CDBasics.Extend [rect, fieldOxideSurroundsViaOnFieldOxide.extent], eachTile: Accumulate, data: via, skip: $doNotSkipAnything]; IF via.hasDiff THEN FlagViolation [viaOnFieldOxideAvoidsDiff, rect, state]; IF via.hasPoly THEN FlagViolation [viaOnFieldOxideAvoidsPoly, rect, state] END; ENDCASE => NULL -- no violation END; -- LookUnderneath FlagViolation: PROC [rule: Rule, rect: CD.Rect, state: State] ~ BEGIN <> longMsg: Rope.ROPE = IO.PutFR ["Error: %g at [%g, %g].\n", IO.rope [rule.msg], IO.int [rect.x1], IO.int [rect.y1]]; state.errorLog.PutF1 ["%g\n", IO.rope [longMsg]]; state.errors _ CONS [[rule, rect], state.errors]; state.errorTotal _ state.errorTotal.SUCC; IF (state.design = NIL) THEN TerminalIO.WriteRope [longMsg] ELSE BEGIN [] _ CDErrors.IncludeMessage [design: state.design, ob: NIL, rect: CDBasics.Extend [rect, rule.extent], message: rule.msg, owner: errorKey]; IF (state.errorTotal < 30) THEN TerminalIO.WriteChar ['|] END END; -- FlagViolation DarkPoly: CSMonitor.PaintPredicate ~ BEGIN RETURN [(ISTYPE [val, MatTileData]) AND (NARROW[val,MatTileData].layerKey = polyKey)] END; -- DarkPoly ClearVFData: CDEvents.EventProc ~ BEGIN <<[event: REF, design: CD.Design, x: REF] RETURNS [dont: BOOL _ FALSE]>> <> cell: CD.Object = NARROW [x, CD.Object]; CDProperties.PutObjectProp [onto: cell, prop: checkedKey, val: NIL]; CDProperties.PutObjectProp [onto: cell, prop: externalKey, val: NIL] END; -- ClearVFData <> [] _ CDProperties.RegisterProperty [externalKey, $gbb]; CDProperties.InstallProcs [prop: externalKey, new: [makeCopy: CDProperties.CopyVal]]; [] _ CDProperties.RegisterProperty [errorKey, $gbb]; [] _ CDProperties.RegisterProperty [checkedKey, $gbb]; ExchangeCDInfo; CDEvents.RegisterEventProc [proc: ClearVFData, event: $AfterCellReplacement]; CDEvents.RegisterEventProc [proc: ClearVFData, event: $AfterChange]; CDSequencer.ImplementCommand [key: $GismoVFSel, proc: InteractiveCall, queue: doQueue]; CDMenus.CreateEntry [menu: $ProgramMenu, entry: "Via Flatness (Gismo)", key: $GismoVFSel]; TerminalIO.WriteRope ["Via flatness checker loaded.\n"]; END. <> <> <> <> <> <> <> <> <> <> <> <> <> <<>>