DrcImpl.mesa
Copyright Ó 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
Rewritten by gbb, January 12, 1987 11:55:49 am PST
gbb March 27, 1987 4:49:20 pm PST
Genista is the grandson of Spinifex. It is a mostly hierarchical design rule checker that monkeys around in a Core design and tries to find all the ChipNDale geometry in order to check as many design rules as it possibly can.
La ginestra o il fiore del deserto (Giacomo Leopardi)
Gli uomini vollero piuttosto le tenebre che la luce (Giovanni, III, 19.)
...
 E tu, lenta ginestra,
che di selve odorate
queste campagne dispogliate adorni,
anche tu presto alla cruel possanza
soccomberai dal sotterraneo foco,
che ritornando al loco
gia noto, stendera l'avaro lembo
su tue molli foreste. E piegherai
sotto il fascio mortal non renitente
il tuo capo innocente:
ma non piegato insino allora indarno
codardamente supplicando innanzi
al futuro oppressor; ma non eretto
con forsennato orgoglio inver le stelle,
n e sul deserto, dove
e la sede e i natali
non per voler ma per fortuna avesti;
ma piu saggia, ma tanto
meno inferma dell'uom, quanto le frali
tue stirpi non credesti
o dal fato o da te fatte immortali.
DIRECTORY
CD USING [Number],
CDBasics USING [ComposeTransform, Extend, Intersect, MapRect],
CDProperties USING [RegisterProperty],
Core USING [Properties],
CoreClasses USING [CellInstance, recordCellClass, RecordCellType, transistorCellClass, unspecifiedCellClass],
CoreGeometry USING [CellInstance, Decoration, EachInstanceProc, EnumerateGeometry, GetObject, GetTrans, Instance, Instances, Layer, Object, Rect],
CoreOps USING [CopyWire, CreateWires, GetCellTypeName, GetShortWireName, SetShortWireName],
CoreProperties USING [GetProp, propPrint, PropPrintProc, Props, PutProp, RegisterProperty, StoreProperties],
Drc,
DrcDebug USING [break, Debug, debug, dLog, PrintWire],
FS USING [StreamOpen],
IO USING [card, char, Close, Error, Flush, int, Put1, PutF, PutF1, PutFR, PutFR1, PutRope, refAny, rope, STREAM, time],
RefTab USING [Create, EachPairAction, Fetch, Insert, Pairs, Ref, SeqIndex],
Rope USING [ROPE, IsEmpty],
Saguaro USING [ExtractedTransistor, ExtractTransistor],
TNT USING [BlowTNT, InitTNT, SweepTNT, TNT, UpdateTNT],
ViewerIO USING [CreateViewerStreams],
ViewerTools USING [FindExistingViewer, Viewer];
DrcImpl: CEDAR PROGRAM
IMPORTS CDBasics, CDProperties, CoreClasses, CoreGeometry, CoreOps, CoreProperties, DrcDebug, FS, IO, RefTab, Rope, TNT, ViewerIO, ViewerTools
EXPORTS Drc SHARES Drc
~ BEGIN OPEN Drc, TNT;
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
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 ~ 50; -- after this number of errors, a | is no longer displayed in the ChipNDale Terminal viewer (if it may be displayed at all).
WireSet: TYPE ~ REF WireSetRec; -- the wires of a set of cells
WireSetRec: TYPE ~ RECORD [elt: SEQUENCE size: NAT OF WireInstance];
Activation Procedure
CheckDesignRules: PUBLIC PROC [cell: CoreCell, external: Wire, tech: Tech, stopFlag: REF BOOLNIL, 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, termination: ROPE;
debugTorch: REF ANY;
rootCellName: ROPE ~ CoreOps.GetCellTypeName [cell];
logFile: ROPE ~ "[]<>Temp>DRC>Genista.Log";
busted: ROPE ~ "\nDRC failed. Your Core data structure may be busted!";
eLog ← FS.StreamOpen [fileName: logFile, accessOptions: create, keep: 5, extendFileProc: NIL, remoteCheck: FALSE, wDir: NIL]; -- keep is 5 because of incremental use
eLog.PutF ["Error log by Drc Genista. Root cell: %g, time: %g\n\n", IO.rope [rootCellName], IO.time []];
IF DrcDebug.debug THEN DrcDebug.dLog.PutF [format: "\n%g. cell: %l%g%l\n",
v1: IO.time [], v2: IO.rope ["b"],
v3: IF rootCellName = NIL THEN IO.refAny [cell] ELSE IO.rope [rootCellName],
v4: IO.rope ["B"]];
state.abort ← IF stopFlag # NIL THEN stopFlag ELSE NEW [BOOLFALSE];
IF useTNT THEN state.nt ← InitTNT [];
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}];
IF (state.tech.verifyCell # NIL) THEN state.tech.verifyCell [cell: cell, state: state];
IF useTNT THEN BlowTNT [state.nt];
IF messy THEN BEGIN
Clean up.
IF DrcDebug.debug THEN DrcDebug.break;
cell.properties ← CoreProperties.PutProp [cell.properties, state.tech.checkedBy, NIL];
Remember that we assigned returnedCell ← cell.
mLog.PutRope [busted]; eLog.PutRope [busted]; eLog.Close [];
quantity ← LAST [CARDINAL];
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.
eLog.PutF [format: "\nNumber of new design violations found: %l%g%l\n",
v1: IO.rope ["b"], v2: IO.card [quantity], v3: IO.rope ["B"]];
mLog.PutRope ["\n"];
termination ← IF state.abort^ THEN "\nDRC aborted."ELSE "\nDRC terminated normally.";
eLog.PutRope [termination]; eLog.Close [];
EXITS
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] ~ BEGIN
Returns the child's absolute coordinates.
RETURN [CDBasics.ComposeTransform [CoreGeometry.GetTrans [state.attributes, child], parent]]
END; -- ComposedTransf
Verification of Core Cells
CheckCell: TYPE ~ PROC [self: CoreCell, state: State, actual: Wire, transf: Transf];
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.
CheckCoreCell: CheckCell ~ BEGIN
SELECT self.class FROM
CoreClasses.recordCellClass => CheckRecord [self, state, actual, transf];
CoreClasses.transistorCellClass => CheckTransistor [self, state, actual, transf];
CoreClasses.unspecifiedCellClass => ERROR;
ENDCASE => ERROR
END; -- CheckCoreCell
CheckRecord: CheckCell ~ BEGIN
cellData: CoreClasses.RecordCellType;
bindingTable: RefTab.Ref;
boundInternal: WireInstance;
propagatedActuals: WireSet; -- one wire per subcell
ownName: ROPE ~ CoreOps.GetCellTypeName [self];
GracefulAbort: PROC [aborted: BOOLFALSE] ~ BEGIN
Places a message in the error Log.
IF aborted THEN eLog.PutF1 ["\n Verification of cell %g aborted.", IO.rope [ownName]]
END; -- GracefulAbort
IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR ((NOT DrcDebug.debug) AND (CoreProperties.GetProp[self.properties, state.tech.checkedBy]#NIL)) THEN RETURN;
mLog.Put1 [IO.char ['.]];
IF DrcDebug.debug THEN BEGIN
bb: Rect ~ CellHull [self, transf, state];
DrcDebug.dLog.PutF [format: "\nChecking cell %l%g%l ",
v1: IO.rope ["e"],
v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName],
v3: IO.rope ["E"]];
DrcDebug.dLog.PutF [" with bounding box [%g, %g, %g, %g].\n", IO.int [bb.x1/state.tech.lambda], IO.int [bb.y1/state.tech.lambda], IO.int [bb.x2/state.tech.lambda], IO.int [bb.y2/state.tech.lambda]]
END;
IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN};
ClearErrors [self, state];
cellData ← NARROW [self.data, CoreClasses.RecordCellType];
self.properties ← CoreProperties.PutProp [self.properties, state.tech.checkedBy, $DrcWasHere];
To use the connectivity informstion from the Core data structure, we have to pass down not the wires, but rather their instances. This implementation tries to be fast.
bindingTable ← CreateBindingTable [actual: actual, public: self.public];
boundInternal ← [cellData.internal, BindInternal [bindingTable, cellData.internal, state], transf];
IF debugBinding THEN BEGIN
DrcDebug.dLog.PutF1 ["Binding and propagating: %g. Binding table:\n", IO.rope [ownName]];
PrintBinding [bindingTable];
DrcDebug.dLog.PutF1 ["Bound internal of %g:\n", IO.rope [ownName]];
DrcDebug.PrintWire [boundInternal.global]
END;
The propagated actuals must be determined here once for all, because new actual wires will differ from call to call.
propagatedActuals ← NEW [WireSetRec[cellData.size]];
FOR sub: NAT IN [0 .. cellData.size) DO
propagatedActuals[sub] ← [NIL, -- depends on cell class
PropagateBinding [state, bindingTable, cellData.instances[sub].actual],
ComposedTransf [transf, cellData.instances[sub], state]]
ENDLOOP;
FlushBindingTable [bindingTable];
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];
CheckCoreCell [self: cellData.instances[sub].type,
actual: propagatedActuals[sub].global,
transf: propagatedActuals[sub].transf,
state: state];
IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN};
ENDLOOP;
Check the internal.
IF debugBinding THEN BEGIN
DrcDebug.dLog.PutF1 ["Verifying: %g\nbound internal:\n", IO.rope [ownName]];
DrcDebug.PrintWire [boundInternal.global]
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];
state.tech.verifyWire [self, w1.local, state];
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];
state.tech.verifyWirePair [self, w1, w2, state];
IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN};
ENDLOOP
ENDLOOP;
Check the separation between the internal of self and its subcells.
FOR sub: NAT IN [0 .. cellData.size) DO
FOR i: NAT IN [0 .. cellData.internal.size) DO
MaterialToCellSeparation [cell: cellData.instances[sub].type,
father: self, -- for error marking
cellWire: propagatedActuals[sub],
materialWire: [boundInternal.local[i], boundInternal.global[i], transf],
state: state];
IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN};
ENDLOOP
ENDLOOP;
Check the separation between the subcells of self.
IF useTNT THEN SweepTNT [state.nt];
FOR sub1: NAT IN [0 .. cellData.size) DO
obj1: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData.instances[sub1].type];
bloated1: Rect ~ CDBasics.Extend [CDBasics.MapRect [obj1.bbox, propagatedActuals[sub1].transf], state.tech.maxSeparation];
FOR sub2: NAT IN (sub1 .. cellData.size) DO
obj2: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData.instances[sub2].type];
IF CDBasics.Intersect [bloated1, CDBasics.MapRect [obj2.bbox, propagatedActuals[sub2].transf]] THEN LOOP; -- avoid polluting TNT
IF useTNT AND (state.nt.UpdateTNT [obj1, obj2, propagatedActuals[sub1].transf, propagatedActuals[sub2].transf, propagatedActuals[sub1].global, propagatedActuals[sub2].global].wasThere) THEN LOOP;
IF DrcDebug.debug THEN BEGIN
bb1: Rect ~ CellHull [cellData.instances[sub1].type, propagatedActuals[sub1].transf, state];
bb2: Rect ~ CellHull [cellData.instances[sub2].type, propagatedActuals[sub2].transf, state];
DrcDebug.dLog.PutF ["\nWill check the separation between the subcells %g with bounding box [%g, %g, %g, %g]", IO.rope [CoreOps.GetCellTypeName [cellData.instances[sub1].type]], IO.int [bb1.x1/state.tech.lambda], IO.int [bb1.y1/state.tech.lambda], IO.int [bb1.x2/state.tech.lambda], IO.int [bb1.y2/state.tech.lambda]];
DrcDebug.dLog.PutF [" and %g with bounding box [%g, %g, %g, %g].\n", IO.rope [CoreOps.GetCellTypeName [cellData.instances[sub2].type]], IO.int [bb2.x1/state.tech.lambda], IO.int [bb2.y1/state.tech.lambda], IO.int [bb2.x2/state.tech.lambda], IO.int [bb2.y2/state.tech.lambda]]
END;
CellToCellSeparation [state: state, father: self,
cell1: cellData.instances[sub1].type,
cell2: cellData.instances[sub2].type,
wire1: propagatedActuals[sub1],
wire2: propagatedActuals[sub2]];
IF state.abort^ THEN {GracefulAbort [state.abort^]; RETURN}
ENDLOOP
ENDLOOP
END; -- CheckRecord
CheckTransistor: CheckCell ~ BEGIN
[self: CoreCell, state: State, actual: Wire, transf: Transf];
Transistors are atomic ChipNDale objects and are supposed to be correct by construction.
e: Saguaro.ExtractedTransistor ~ Saguaro.ExtractTransistor;
DoNotCheck [self, state, actual, transf]
END; -- CheckTransistor
DoNotCheck: CheckCell ~ BEGIN
[self: CoreCell, state: State, actual: Wire, transf: Transf];
(self.class = CoreClasses.unspecifiedCellClass) OR (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL);
self.properties ← CoreProperties.PutProp [self.properties, state.tech.checkedBy, $DrcWasHere]
END; -- DoNotCheck
Pruning
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.
stored: REF Rect ← NARROW [CoreProperties.GetProp [w.local.properties, bbKey]];
EachRect: CoreGeometry.EachInstanceProc ~ BEGIN
PROC [instance: CdInsts] RETURNS [quit: BOOLFALSE]
r: Rect ~ instance.obj.bbox;
h.x1 ← MIN [h.x1, r.x1]; h.y1 ← MIN [h.y1, r.y1];
h.x2 ← MAX [h.x2, r.x2]; h.y2 ← MAX [h.y2, r.y2];
END; -- EachRect
IF w.local.size > 0 THEN DrcDebug.break; -- you may proceede
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 (stored = NIL) THEN BEGIN
[] ← state.attributes.EnumerateGeometry [w.local, EachRect];
stored ← NEW [Rect ← h];
w.local.properties ← CoreProperties.PutProp [w.local.properties, bbKey, stored]
END
ELSE h ← stored^;
h ← CDBasics.MapRect [h, w.transf]
END; -- AtomicWireHull
CellHull: PROC [self: CoreCell, transf: Transf, state: State] RETURNS [Rect] ~ BEGIN
Needed for recursion.
RETURN [CDBasics.MapRect [state.attributes.GetObject[self].bbox, transf]]
END; -- CellHull
Binding
CreateBindingTable: PROC [actual, public: Wire] RETURNS [bindingTable: RefTab.Ref] ~ BEGIN
tableSize: RefTab.SeqIndex ← public.size;
IF actual.size # public.size THEN BEGIN
IF DrcDebug.debug THEN BEGIN
DrcDebug.dLog.Put1 [IO.rope ["actual.size # public.size. Actual wire:\n"]];
DrcDebug.PrintWire [actual];
DrcDebug.dLog.Put1 [IO.rope ["Public wire:\n"]];
DrcDebug.PrintWire [public];
ERROR
END
ELSE coreMess ["actual.size # public.size (5)", actual]
END;
IF (tableSize MOD 2) = 0 THEN tableSize ← tableSize.SUCC;
bindingTable ← RefTab.Create [tableSize];
FOR i: NAT IN [0 .. public.size) DO
[] ← RefTab.Insert [x: bindingTable, key: public[i], val: actual[i]]
ENDLOOP
END; -- CreateBindingTable
FlushBindingTable: PROC [bindingTable: RefTab.Ref] ~ INLINE BEGIN
bindingTable ← NIL
END; -- FlushBindingTable
PropagateBinding: PROC [state: State, bindingTable: RefTab.Ref, actual: Wire] RETURNS [boundActual: Wire] ~ BEGIN
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 [RefTab.Fetch [bindingTable, actual].val];
IF boundActual = NIL THEN BEGIN
name: ROPE ~ IF DrcDebug.debug THEN IO.PutFR1 ["New signal # %g", IO.card [state.wireCreationCount]] ELSE "New signal by Drc";
boundActual ← CoreOps.CreateWires [size: 0, name: name];
state.wireCreationCount ← state.wireCreationCount.SUCC;
[] ← RefTab.Insert [x: bindingTable, key: actual, val: boundActual]
END
ELSE boundActual ← CoreOps.SetShortWireName [boundActual, CoreOps.GetShortWireName[actual]]
END; -- PropagateAtomic
boundActual ← CoreOps.CreateWires [actual.size];
FOR i: NAT IN [0 .. actual.size) DO
boundActual[i] ← PropagateAtomic [state, bindingTable, actual[i]]
ENDLOOP
END; -- PropagateBinding
BindInternal: PROC [bindingTable: RefTab.Ref, internal: Wire, state: State] RETURNS [boundInternal: Wire] ~ BEGIN
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 [RefTab.Fetch [bindingTable, 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, father: CoreCell, cellWire, materialWire: WireInstance, 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, father: CoreCell, wire1, wire2: WireInstance, 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.
MaterialToCellSeparation: MaterialToCellProc ~ BEGIN
For convenience.
SELECT cell.class FROM
CoreClasses.recordCellClass => MaterialToRecordCellSeparation [cell, father, cellWire, materialWire, state];
CoreClasses.transistorCellClass => MaterialToTransistorSeparation [cell, father, cellWire, materialWire, state];
CoreClasses.unspecifiedCellClass => MaterialToUnspecifiedSeparation [cell, father, cellWire, materialWire, state];
ENDCASE => ERROR
END; -- MaterialToCellSeparation
MaterialToTransistorSeparation: MaterialToCellProc ~ BEGIN
[cell, father: CoreCell, cellWire, materialWire: WireInstance, state: State]
wbb, tbb: Rect; -- bounding boxes
boundInternal: WireInstance;
atomicBoundInternal: WireSet;
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend [tbb, state.tech.maxSeparation], wbb]]
END; -- Intersect
IF state.abort^ THEN RETURN;
IF (CoreProperties.GetProp [cell.properties, doNotAnalyse] # NIL) THEN RETURN;
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 (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;
Check each element of the transistor wire of the cell against each element of wire.
FOR i: NAT IN [0 .. cell.public.size) DO
IF state.abort^ THEN RETURN;
state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal[i], w2: materialWire]
ENDLOOP
END; -- MaterialToTransistorSeparation
MaterialToUnspecifiedSeparation: MaterialToCellProc ~ BEGIN
[cell, father: CoreCell, cellWire, materialWire: WireInstance, state: State]
ERROR
END; -- MaterialToUnspecifiedSeparation
MaterialToRecordCellSeparation: MaterialToCellProc ~ BEGIN
[cell, father: CoreCell, cellWire, materialWire: WireInstance, state: State]
wbb, cbb: Rect;  -- bounding boxes
boundInternal: WireInstance;
propagatedActuals, atomicBoundInternal: WireSet;
bindingTable: RefTab.Ref;
cellData: CoreClasses.RecordCellType ← NARROW [cell.data];
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend [cbb, state.tech.maxSeparation], wbb]]
END; -- Intersect
IF state.abort^ THEN RETURN;
IF (CoreProperties.GetProp [cell.properties, doNotAnalyse] # NIL) OR (cellData.internal.size = 0) THEN RETURN;
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 (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;
FOR i: NAT IN [0 .. cellData.internal.size) DO
IF state.abort^ THEN RETURN;
state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal[i], w2: materialWire]
ENDLOOP;
The propagated actuals must be determined here once for all, because new actual wires will differ from call to call.
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];
IF state.abort^ THEN RETURN;
MaterialToCellSeparation [cell: cellData.instances[sub].type,
father: cell, -- for error marking
cellWire: propagatedActuals[sub],
materialWire: [boundInternal.local[sub], boundInternal.global[sub], cellWire.transf],
state: state]
ENDLOOP
END; -- MaterialToRecordCellSeparation
CellToCellSeparation: CellToCellProc ~ BEGIN
[cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, 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, father, wire1, wire2, state];
RETURN
END
ELSE BEGIN-- swap
cell1Z: CoreCell ~ cell1; wire1Z: WireInstance ~ wire1;
cell1 ← cell2; cell2 ← cell1Z;
wire1 ← wire2; wire2 ← wire1Z
END;
ENDCASE => ERROR;
SELECT cell1.class FROM
CoreClasses.recordCellClass => RecordCellToRecordCellSeparation [cell1, cell2, father, wire1, wire2, state];
CoreClasses.transistorCellClass => TransistorToRecordCellSeparation [cell1, cell2, father, wire1, wire2, state];
CoreClasses.unspecifiedCellClass => UnspecifiedToAnyClassSeparation [cell1, cell2, father, wire1, wire2, state];
ENDCASE => ERROR
END; -- CellToCellSeparation
TransistorToTransistorSeparation: CellToCellProc ~ BEGIN
[cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State]
cbb1, cbb2: Rect;  -- bounding boxes
boundInternal1, boundInternal2: WireInstance;
atomicBoundInternal1, atomicBoundInternal2: WireSet;
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.tech.maxSeparation], cbb2]]
END; -- Intersect
Preamble.
IF state.abort^ THEN RETURN;
IF (CoreProperties.GetProp [cell1.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [cell2.properties, doNotAnalyse]#NIL) THEN RETURN;
cbb1 ← CellHull [cell1, wire1.transf, state];
cbb2 ← CellHull [cell2, wire2.transf, state];
IF (NOT Intersect[]) THEN RETURN;
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;
FOR i: NAT IN [0 .. boundInternal2.local.size) DO
atomicBoundInternal2[i] ← [boundInternal2.local[i], boundInternal2.global[i], wire2.transf]
ENDLOOP;
FOR i: NAT IN [0 .. cell1.public.size) DO
FOR j: NAT IN [0 .. cell2.public.size) DO
IF state.abort^ THEN RETURN;
state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j]]
ENDLOOP
ENDLOOP
END; -- TransistorToTransistorSeparation
TransistorToRecordCellSeparation: CellToCellProc ~ BEGIN
[cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State]
cbb1, cbb2: Rect;  -- bounding boxes
cellData2: CoreClasses.RecordCellType ← NARROW [cell2.data];
bindingTable2: RefTab.Ref;
boundInternal1, boundInternal2: WireInstance;
atomicBoundInternal1, atomicBoundInternal2, propagatedActuals2: WireSet;
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend [cbb1, state.tech.maxSeparation], cbb2]]
END; -- Intersect
Preamble.
IF state.abort^ THEN RETURN;
IF (CoreProperties.GetProp [cell1.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [cell2.properties, doNotAnalyse]#NIL) THEN RETURN;
IF DrcDebug.debug THEN BEGIN
ownName: ROPE ~ CoreOps.GetCellTypeName [cell1];
otherName: ROPE ~ CoreOps.GetCellTypeName [cell2];
DrcDebug.dLog.PutF [format: "Checking transistor %l%g%l vs. %g. ",
v1: IO.rope ["e"],
v2: IF (ownName = NIL) THEN IO.refAny [cell1] ELSE IO.rope [ownName],
v3: IO.rope ["E"],
v4: IF (otherName = NIL) THEN IO.refAny [cell2] ELSE IO.rope [otherName]]
END;
cbb1 ← CellHull [cell1, wire1.transf, state];
cbb2 ← CellHull [cell2, wire2.transf, state];
IF (NOT Intersect[]) THEN RETURN;
Check the separation between the internal of transistor and cell2.
IF state.abort^ THEN ERROR ABORTED;
boundInternal1 ← [cell1.public, BindTransistor [cell1.public, wire1.global, state], wire1.transf];
atomicBoundInternal1 ← NEW [WireSetRec[cell1.public.size]];
FOR i: NAT IN [0 .. cell1.public.size) DO
atomicBoundInternal1[i] ← [boundInternal1.local[i], boundInternal1.global[i], wire1.transf]
ENDLOOP;
bindingTable2 ← CreateBindingTable [actual: wire2.global, public: cell2.public];
boundInternal2 ← [cellData2.internal, BindInternal [bindingTable2, cellData2.internal, state], wire2.transf];
atomicBoundInternal2 ← NEW [WireSetRec[cellData2.internal.size]];
FOR i: NAT IN [0 .. cellData2.internal.size) DO
atomicBoundInternal2[i] ← [boundInternal2.local[i], boundInternal2.global[i], wire2.transf]
ENDLOOP;
propagatedActuals2 ← NEW [WireSetRec[cellData2.internal.size]];
FOR sub: NAT IN [0 .. cellData2.internal.size) DO
propagatedActuals2[sub] ← [NIL, -- depends on cell class
PropagateBinding [state, bindingTable2, cellData2.instances[sub].actual],
ComposedTransf [wire2.transf, cellData2.instances[sub], state]]
ENDLOOP;
FlushBindingTable [bindingTable2];
FOR i: NAT IN [0 .. wire1.local.size) DO
FOR j: NAT IN [0 .. wire2.local.size) DO
IF state.abort^ THEN RETURN;
state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j]]
ENDLOOP
ENDLOOP;
Check intersections between subcells of transistor wires and cell2.
FOR i: NAT IN [0 .. wire1.local.size) DO
FOR sub: NAT IN [0 .. cellData2.size) DO
subTransf: Transf ~ ComposedTransf [wire2.transf, cellData2.instances[sub], state];
IF state.abort^ THEN RETURN;
MaterialToCellSeparation [cell: cellData2.instances[sub].type,
father: father, -- for error marking
cellWire: propagatedActuals2[sub],
materialWire: atomicBoundInternal1[i],
state: state]
ENDLOOP
ENDLOOP
END; -- TransistorToRecordCellSeparation
UnspecifiedToAnyClassSeparation: CellToCellProc ~ BEGIN
[cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State]
ERROR
END; -- UnspecifiedToAnyClassSeparation
RecordCellToRecordCellSeparation: CellToCellProc ~ BEGIN
[cell1, cell2, father: CoreCell, wire1, wire2: WireInstance, state: State]
cbb1, cbb2: Rect;  -- bounding boxes
cellData1: CoreClasses.RecordCellType ← NARROW [cell1.data];
cellData2: CoreClasses.RecordCellType ← NARROW [cell2.data];
bindingTable1, bindingTable2: RefTab.Ref;
boundInternal1, boundInternal2: WireInstance;
atomicBoundInternal1, atomicBoundInternal2, propagatedActuals1, propagatedActuals2: WireSet;
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.tech.maxSeparation], cbb2]]
END; -- Intersect
Preamble.
IF state.abort^ THEN RETURN;
IF (CoreProperties.GetProp [cell1.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [cell2.properties, doNotAnalyse]#NIL) THEN RETURN;
IF DrcDebug.debug THEN BEGIN
name1: ROPE ~ CoreOps.GetCellTypeName [cell1];
name2: ROPE ~ CoreOps.GetCellTypeName [cell2];
DrcDebug.dLog.PutF [format: "Checking record cell %l%g%l vs. %g.\n",
v1: IO.rope ["e"],
v2: IF name1.IsEmpty THEN IO.refAny [cell1] ELSE IO.rope [name1],
v3: IO.rope ["E"],
v4: IF name2.IsEmpty THEN IO.refAny [cell2] ELSE IO.rope [name2]]
END;
cbb1 ← CellHull [cell1, wire1.transf, state];
cbb2 ← CellHull [cell2, wire2.transf, state];
IF (NOT Intersect[]) THEN RETURN;
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.internal.size]];
FOR sub: NAT IN [0 .. cellData1.size) DO
propagatedActuals2[sub] ← [NIL, -- depends on cell class
PropagateBinding [state, bindingTable1, cellData1.instances[sub].actual],
ComposedTransf [wire1.transf, cellData1.instances[sub], state]]
ENDLOOP;
propagatedActuals2 ← NEW [WireSetRec[cellData2.internal.size]];
FOR sub: NAT IN [0 .. cellData2.size) DO
propagatedActuals2[sub] ← [NIL, -- depends on cell class
PropagateBinding [state, bindingTable2, cellData2.instances[sub].actual],
ComposedTransf [wire2.transf, cellData2.instances[sub], state]]
ENDLOOP;
FlushBindingTable [bindingTable1]; FlushBindingTable [bindingTable2];
FOR i: NAT IN [0 .. cellData1.internal.size) DO
FOR j: NAT IN [0 .. cellData2.internal.size) DO
state.tech.verifyWirePair [state: state, cell: father, w1: atomicBoundInternal1[i], w2: atomicBoundInternal2[j]]
ENDLOOP
ENDLOOP;
FOR sub: NAT IN [0 .. cellData2.size) DO
FOR i: NAT IN [0 .. cellData1.internal.size) DO
MaterialToCellSeparation [cell: cellData2.instances[sub].type,
father: father, -- for error marking
cellWire: propagatedActuals2[sub],
materialWire: atomicBoundInternal1[i],
state: state]
ENDLOOP
ENDLOOP;
FOR sub: NAT IN [0 .. cellData1.size) DO
FOR i: NAT IN [0 .. cellData2.internal.size) DO
MaterialToCellSeparation [cell: cellData1.instances[sub].type,
father: father, -- for error marking
cellWire: propagatedActuals1[sub],
materialWire: atomicBoundInternal2[i],
state: state]
ENDLOOP
ENDLOOP;
FOR sub1: NAT IN [0 .. cellData1.size) DO
obj1: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData1.instances[sub1].type];
bloated1: Rect ~ CDBasics.Extend [CDBasics.MapRect [obj1.bbox, propagatedActuals1[sub1].transf], state.tech.maxSeparation];
FOR sub2: NAT IN (sub1 .. cellData2.size) DO
obj2: CdObj ~ CoreGeometry.GetObject [state.attributes, cellData2.instances[sub2].type];
IF CDBasics.Intersect [bloated1, CDBasics.MapRect [obj2.bbox, propagatedActuals2[sub2].transf]] THEN LOOP; -- avoid polluting TNT
IF useTNT AND (state.nt.UpdateTNT [obj1, obj2, propagatedActuals1[sub1].transf, propagatedActuals2[sub2].transf, propagatedActuals1[sub1].global, propagatedActuals2[sub2].global].wasThere) THEN LOOP;
CellToCellSeparation [state: state, father: father,
cell1: cellData1.instances[sub1].type,
cell2: cellData2.instances[sub2].type,
wire1: propagatedActuals1[sub1],
wire2: propagatedActuals2[sub2]]
ENDLOOP
ENDLOOP
END; -- RecordCellToRecordCellSeparation
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.PutF1 ["%g\n", IO.rope [longMsg]]
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.