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 January 22, 1988 10:15:42 am 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.
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;
Debugging, signals and errors
Note that aborting by the means of a signal busts the Core data structure.
debugBinding: BOOLFALSE; -- 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];
Logs
mLog, eLog: IO.STREAM;  -- messages and error log
Type redifinitions
CdInsts: TYPE ~ CoreGeometry.Instances;
CdObj: TYPE ~ CoreGeometry.Object;
CoreInst: TYPE ~ CoreGeometry.CellInstance;
FakeInst: TYPE ~ CoreGeometry.Instance;
Layer: TYPE ~ CoreGeometry.Layer;
ROPE: TYPE ~ Rope.ROPE;
Drc keys.
For simplicity they are registered both with ChipNDale and Core and have the same name
DRVkey: PUBLIC ATOM ← CoreProperties.RegisterProperty [$DrcError];
Error report may be processed by clients.
bbKey: ATOM ← CoreProperties.RegisterProperty [$DrcBB];
Cache for the bounding box.
ChipNDale keys used by the Genista
rectClass: ATOM ~ $Rect;
pinClass: ATOM ~ $PinOb0;
Core keys used by the Genista
doNotAnalyse: ATOM ~ $DoNotDRC;
Global constants and types
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];
Activation Procedure
CheckDesignRules: PUBLIC PROC [cell: CoreCell, external: Wire, tech: Tech, viaFlatness: BOOLFALSE, stopFlag: REF BOOL, lap: REF CARDINAL, currentCell: REF Rope.ROPE, layout: Layout] RETURNS [quantity: CARDINAL ← 0] ~ BEGIN
The external interface.
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: BOOLFALSE;
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 [BOOLFALSE];
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;
DRC.
[] ← 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
Clean up.
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 ["Beretta.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^;
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.
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 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
Remember that we assigned returnedCell ← cell.
paranoia =>  -- cannot communicate with anybody
{eLog.PutRope ["\nThis is paranoia."]; eLog.Close []}
END; -- CheckDesignRules
ChipNDale Stuff
ComposedTransf: PROC [parent: Transf, child: CoreInst, state: State] RETURNS [Transf] ~ INLINE BEGIN
Returns the child's absolute coordinates.
RETURN [CDBasicsInline.ComposeTransform [CoreGeometry.GetTrans [state.attributes, child], parent]]
END; -- ComposedTransf
BloatedIntersection: PROC [r1, r2: Rect, e1, e2: CD.Number] RETURNS [Rect] ~ INLINE BEGIN
Returns the intersection of the two rectangles each bloated by half the maximum separation. The rectangler are supposed to intersect.
RETURN [CDBasics.Intersection [CDBasics.Extend [r1, e1 / 2], CDBasics.Extend [r2, e2 / 2]]]
END; -- BloatedIntersection
Verification of Core Cells
CheckCell: TYPE ~ PROC [self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet ← NIL];
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.
CheckCoreCell: CheckCell ~ BEGIN
[self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet ← NIL]
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
[self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet ← NIL]
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: BOOLFALSE] ~ BEGIN
Places a message in the error Log.
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];
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.
bindingTable ← CreateBindingTable [actual: actual, public: self.public];
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).
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];
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.
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];
subTransf ← CoreGeometry.GetTrans [state.attributes, cellData.instances[sub]];
IF (channelPeek # NIL) THEN BEGIN
FOR i: NAT IN [0 .. cellData.instances[sub].type.public.size) DO
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.
channelPeek[i].transf ← subTransf
ENDLOOP;
channelPeekList ← CONS [channelPeek, channelPeekList]
END;
IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN};
ENDLOOP;
Check the internal.
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;
WARNING: The order of the calls of the technology dependent procedures is important and may not be changed.
FOR i: NAT IN [0 .. cellData.internal.size) DO
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];
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
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];
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;
Check the separation between the internal of self and its subcells.
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;
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.
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;
Check the separation between the subcells of self.
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
[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.
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;
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.
peek[0].gateHint ← peek[1].gateHint ← peek[2].gateHint ← NEW [WireInstance ← peek[0]]
END; -- CheckTransistor
DoNotCheck: CheckCell ~ BEGIN
[self: CoreCell, state: State, actual: Wire, transf: Transf] RETURNS [peek: WireSet ← NIL]
(self.class = CoreClasses.unspecifiedCellClass) OR (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL);
self.properties ← CoreProperties.PutProp [self.properties, state.tech.checkedBy, $DrcWasHere]
END; -- DoNotCheck
Pruning
noHull: Rect ~ [CD.Number.FIRST, CD.Number.FIRST, CD.Number.FIRST, CD.Number.FIRST];
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.
AtomicWireHull: PUBLIC PROC [w: WireInstance, state: State] RETURNS [h: Rect] ~ BEGIN
Coputes the bounding box of the rectangles of material that make up a wire.
EachRect: CoreGeometry.EachInstanceProc ~ BEGIN
PROC [instance: CdInsts] RETURNS [quit: BOOLFALSE]
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
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.
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
Needed for recursion.
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
Nothing magic, just a table.
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
Binding
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
Called to propagate the binding on level down. Applied to the actual wire.
PropagateAtomic: PROC [state: State, bindingTable: RefTab.Ref, actual: Wire] RETURNS [boundActual: Wire] ~ INLINE BEGIN
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.
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
For full sequence. It is assumed that wires have depth 1.
boundInternal ← CoreOps.CopyWire [internal];
FOR i: NAT IN [0 .. internal.size) DO
b: Wire ~ NARROW [bindingTable.Fetch [internal[i]].val];
If the wire is not in the binding table, it does not interface, hence it has not to be bound.
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
Separation and Width Check Procedures
MaterialToCellProc: TYPE ~ PROC [cell: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State];
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.
CellToCellProc: TYPE ~ PROC [cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State];
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.
MaterialToCellSeparation: MaterialToCellProc ~ BEGIN
For convenience.
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
[cell: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
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;
Find the bounding box of wire and check whether it has a non-empty intersection with the internal wire of the cell.
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;
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.
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;
Check each element of the transistor wire of the cell against each element of wire.
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
[cell: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
ERROR
END; -- MaterialToUnspecifiedSeparation
MaterialToRecordCellSeparation: MaterialToCellProc ~ BEGIN
[cell: CoreCell, where: Context, cellWire, materialWire: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
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;
Find the bounding box of wire and check whether it has a non-empty intersection with the internal wire of the cell.
wbb ← AtomicWireHull [materialWire, state];
cbb ← CellHull [cell, cellWire.transf, state];
IF (wbb = noHull) OR (NOT Intersect[]) THEN RETURN;
Check each element of the internal wire of the cell against each element of wire.
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;
The propagated actuals must be determined here once for all, because new actual wires will differ from call to call.
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];
Check intersections between subcells of cell and wire.
FOR sub: NAT IN [0 .. cellData.size) DO
subTransf: Transf ~ ComposedTransf [cellWire.transf, cellData.instances[sub], state];
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
[cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
For convenience.
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
[cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
cbb1, cbb2, w: Rect; -- bounding boxes & window
boundInternal1, boundInternal2: WireInstance;
atomicBoundInternal1, atomicBoundInternal2: WireSet;
Preamble.
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;
Check the separation between the internal of cell1 and cell2.
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;
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.
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;
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.
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
[cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
cbb1, cbb2, bloated1, int: Rect; -- bounding boxes
cellData2: CoreClasses.RecordCellType ← NARROW [cell2.data];
bindingTable2: RefTab.Ref;
boundInternal1, boundInternal2: WireInstance;
atomicBoundInternal1, atomicBoundInternal2, propagatedActuals2: WireSet;
Preamble.
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;
Check the separation between the internal of transistor and cell2.
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;
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.
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;
Check intersections between subcells of transistor wires and cell2.
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
[cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
ERROR
END; -- UnspecifiedToAnyClassSeparation
RecordCellToRecordCellSeparation: CellToCellProc ~ BEGIN
[cell1, cell2: CoreCell, where: Context, wire1, wire2: WireInstance, window: Rect, patches: WireSetList ← NIL, state: State]
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;
Preamble.
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;
Check the separation between the internal of cell1 and cell2.
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
Design Rule Violation Flagging
ClearErrors: PROC [obj: CoreCell, state: State] ~ BEGIN
Removes the error rectangles from a given object (e.g. a cell).
obj.properties ← CoreProperties.PutProp [obj.properties, DRVkey, NIL]
END; -- ClearErrors
MarkError: PUBLIC PROC [obj: CoreCell, state: State, e: ErrorRect] ~ BEGIN
Puts an error rectangle r and maintains an error counter in the Core structure.
objName: ROPE = CoreOps.GetCellTypeName [obj];
violations: DRVNARROW [CoreProperties.GetProp [obj.properties, DRVkey]];
longMsg: ROPEIO.PutFR ["%g: %g",
IF objName=NIL THEN IO.refAny [obj] ELSE IO.rope [objName], IO.rope[e.msg]];
done: BOOLFALSE; -- 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
Property Registrations
PrintError: CoreProperties.PropPrintProc ~ BEGIN
to.PutF1 [format: "Design rule violations: %g. ", value: IO.int [NARROW[val,DRV].count]]
END; -- PrintError
RegisterProperties: PROC ~ BEGIN
Registers all properties.
ChipNDale
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"]];
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
CoreProperties.StoreProperties [prop: DRVkey, properties: CoreProperties.Props [[CoreProperties.propPrint, NEW [CoreProperties.PropPrintProc ← PrintError]]]]
END; -- RegisterProperties
Logging & Debugging
StartLog: PROC ~ BEGIN
Initialises the log streams. Should cater for errors IO.Error [$Failure, NIL], IO.Error [StreamClosed].
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
[key: Key, val: Val] RETURNS [quit: BOOLEAN]
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
[key: Key, val: Val] RETURNS [quit: BOOLEAN]
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
For debugging purposes.
[] ← RefTab.Pairs [bindingTable, PrintEntryShort]
END; -- PrintBinding
Initialisation
StartLog []; -- Should be first, so problems can be communicated
RegisterProperties [] -- CD & Core
END.
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