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];
Activation Procedure
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
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: 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;
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
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:
BOOL ←
FALSE] ~
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
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