SoSImpl.mesa    Agewmetrhtos
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
Giordano Bruno Beretta, October 17, 1985 4:37:14 pm PDT
gbb June 16, 1986 1:14:53 pm PDT
Louis Monier June 5, 1986 8:21:05 pm PDT
SoS is the Son of Spinifex. It is a 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.
Gli uomini vollero piuttosto le tenebre che la luce (Giovanni, III, 19.)
DIRECTORY
Atom USING [GetPName],
BasicTime USING [Now],
CD USING [backgroundLayer, commentLayer, Design, errorLayer, FetchObjectClass, Instance, InstanceList, Layer, LayerKey, LayerTechnology, Number, Object, Orientation, original, outlineLayer, Position, Rect, selectionLayer, shadeLayer, Technology, undefLayer],
CDBasics USING [AddPoints, empty, Extend, Intersect, Intersection, SizeOfRect],
CDDirectory USING [Fetch],
CDErrors USING [IncludeMessage, RemoveMessages],
CDOrient USING [ComposeOrient, DecomposeOrient, MapRect, original],
CDProperties USING [GetProp, PutProp, PutObjectProp, RegisterProperty],
CDSimpleRules USING [MaxWidth, MinDist, MinWidth, NotKnown],
Core USING [CellType, Properties, Wire],
CoreClasses USING [recordCellClass, RecordCellType, transistorCellClass, unspecifiedCellClass],
CoreOps USING [CreateWires, CopyWire, GetCellTypeName, GetShortWireName, SetShortWireName],
CoreProperties USING [CopyProps, GetProp, propCompare, propCopy, PropDoCopy, PropIntCompare, propPrint, PropPrintProc, Props, PutProp, RegisterProperty, StoreProperties],
FinchSmarts USING [GetProcs, Procs, RecordReason],
IO USING [card, char, Error, ErrorCode, int, noWhereStream, Put1, PutF, PutF1, PutFR, PutFR1, refAny, rope, STREAM, time],
RefTab USING [Create, EachPairAction, Fetch, Insert, Pairs, Ref, SeqIndex],
Rope USING [Cat, ROPE],
SoS USING [CellHullProc, CellToCellProc, CheckCell, DRV, DesignRuleViolation, ErrorRect, FindGeometry, MaterialToCellProc, State, StateRec],
SoSTNT USING [BlowTNT, InitTNT, InTNT, RememberTNT, SweepTNT, TNT],
ViewerIO USING [CreateViewerStreams],
ViewerTools USING [FindExistingViewer, Viewer];
SoSImpl: CEDAR PROGRAM
IMPORTS Atom, BasicTime, CD, CDBasics, CDDirectory, CDErrors, CDOrient, CDProperties, CDSimpleRules, CoreClasses, CoreOps, CoreProperties, FinchSmarts, IO, RefTab, Rope, SoSTNT, ViewerIO, ViewerTools
EXPORTS SoS ~ BEGIN
OPEN SoSTNT;
debug: BOOLFALSE;  -- to start debugging enter: ← SoSImpl.Debug[]
occDebug: BOOL = FALSE; -- for occasional debugging
useTNT: BOOL = TRUE;  -- for timing analysis only
persist: BOOLTRUE;  -- depends on Sinix (try hard to flag CD design)
fast: BOOLTRUE;  -- if FALSE then SoS is purely "object oriented"
break: SIGNAL = CODE;  -- for debugging; related to property $SoSBreak
panic: SIGNAL = CODE;  -- cannot communicate violations
coreMess: SIGNAL [reason: Rope.ROPE, torch: REF ANY] = CODE; -- clean up before issuing coreInconsistent
coreInconsistent: PUBLIC ERROR [reason: Rope.ROPE, torch: REF ANY] = CODE;
mLog: IO.STREAM;  -- messages
dLog: IO.STREAMIO.noWhereStream; -- debugging
Note: Output to noWhereStream passes all I/O code, and hence is very slow !
State: TYPE = SoS.State; -- REF StateRec;
StateRec: TYPE = SoS.StateRec;
StateRec: TYPE = RECORD [design: CD.Design,
abort: REF BOOL,
nt: SoSTNT.TNT,  -- neighbourhood table
maxSeparation: CD.Number ← 200, -- a large number
globalErrorCount: CARDINAL,
verbose: BOOL,  -- warn about unreportable violations
shy: BOOL,   -- ok to go into paranoia
wireCreationCount: CARDINAL ← 1, -- for debugging
cdObjKey: ATOM,  -- key of ChipNDale object
cdInstKey: ATOM,  -- key of ChipNDale instance
cdInstListKey: ATOM];  -- key of list of ChipNDale instances
DRV: TYPE = SoS.DRV; -- REF DesignRuleViolation
DesignRuleViolation: TYPE = SoS.DesignRuleViolation;
RECORD [count: INT ← 0, places: LIST OF ErrorRect]
ErrorRect: TYPE = SoS.ErrorRect;
RECORD [r: CD.Rect, msg: Rope.ROPE]
SoS keys. For simplicity they are registered both with ChipNDale and Core and have the same name
checked: ATOM = CoreProperties.RegisterProperty [$SoSWasHere];
Attached to CellTypes.
DRVkey: PUBLIC ATOM ← CoreProperties.RegisterProperty [$SoSError];
Error report may be processed by clients.
bbKey: ATOM ← CoreProperties.RegisterProperty [$SoSbb];
Cache for the bounding box.
trace: ATOM = CoreProperties.RegisterProperty [$SoSSeparationChecked];
Debugging only. Attached to ChipNDale objects processed.
SoS analysis procedures (one for each ChpiNDale object class and one for each Core cell class)
analysis: ATOM = CoreProperties.RegisterProperty [$SoSAnalysis];
cellHull: ATOM = CoreProperties.RegisterProperty [$SoSHull];
matToCell: ATOM = CoreProperties.RegisterProperty [$SoSmc];
cellToCell: ATOM = CoreProperties.RegisterProperty [$SoScc];
ChipNDale keys used by the Son of Spinifex
rectClass: ATOM = $Rect;
pinClass: ATOM = $PinOb0;
Core keys used by the Son of Spinifex
doNotAnalyse: ATOM = $DoNotDRC;
previousTechnology: CD.Technology ← NIL;
previousMaxSeparation: CD.Number ← 75 * 8;
specialLayers: CD.Layer = MAX [CD.shadeLayer, CD.errorLayer, CD.backgroundLayer, CD.outlineLayer, CD.selectionLayer, CD.commentLayer] + 1;
undefLayer, highLightShade, highLightError, pinRepresentation, outlineLayer, selectionLayer, commentLayer
Warning: CD.undefLayer could not be included in the list because of a minor bug in the compiler, but it should so once the bug is fixed.
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 Core.Wire];
PropSet: TYPE = REF PropSetRec; -- the properties of a set of cells
PropSetRec: TYPE = RECORD [p: SEQUENCE size: NAT OF Core.Properties];
Activation Procedure
CheckDesignRules: PUBLIC PROC [cell: Core.CellType, design: CD.Design ← NIL, abortFlag: REF BOOLNIL, verbose, shy, placebo: BOOLFALSE, cdObjKey, cdInstKey, cdInstListKey: ATOM] RETURNS [quantity: CARDINAL ← 1] ~ 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 Debug [];
s ← IF m THEN mLog ELSE dLog;
CONTINUE
END;
panic => GOTO paranoia
END;
state: State ← NEW [StateRec];
messy, okToType: BOOLFALSE;
abortionCause: Rope.ROPE;
debugTorch: REF ANY;
fakeActual: Core.Wire;
cellName: Rope.ROPE = CoreOps.GetCellTypeName [cell];
designName: Rope.ROPE = IF design#NIL THEN design.name ELSE "in the sky";
state.shy ← shy; -- must be first statement
IF placebo THEN BEGIN
FOR wait: CARD IN [0 .. CARD.LAST / 100) DO
[] ← IO.PutFR ["Yes, you are really a very bright, smart and intelligent guy. Your Core structure is so great, it really gives me the frills. I feel so great looking at it. I'll propose you for the next Turing Award."]
ENDLOOP;
Wow, did that CPU look busy !
RETURN
END;
okToType ← IF state.shy THEN NOT (Msg [cellName]) ELSE TRUE;
IF okToType THEN IO.PutF [stream: mLog,
format: "\nChecking wire geometry in cell %l%g%l of design %g\n",
v1: IO.rope ["b"], v2: IO.rope [cellName], v3: IO.rope ["B"], v4: IO.rope [designName]];
IF debug THEN IO.PutF [stream: dLog,
format: "\n%g. design: %g, cell: %g\n",
v1: IO.time [],
v2: IO.rope [designName],
v3: IF cellName = NIL THEN IO.refAny [cell] ELSE IO.rope [cellName]];
state.design ← design;
state.abort ← IF abortFlag # NIL THEN abortFlag ELSE NEW [BOOLFALSE];
IF useTNT THEN state.nt ← InitTNT [];
IF (design = NIL) OR (design.technology = NIL) THEN BEGIN
state.maxSeparation ← previousMaxSeparation ← 75 * 8;
I hope this figure is large enough. I did not make it too large for speed reasons.
previousTechnology ← NIL
END
ELSE
IF design.technology # previousTechnology THEN BEGIN
previousMaxSeparation ← ComputeMaxSeparation [design.technology];
previousTechnology ← design.technology
END;
state.maxSeparation ← previousMaxSeparation;
state.globalErrorCount ← 0;
state.verbose ← verbose;
state.cdObjKey ← cdObjKey;
state.cdInstKey ← cdInstKey;
state.cdInstListKey ← cdInstListKey;
DRC.
fakeActual ← CoreOps.CopyWire [cell.public];
CheckCoreCell [self: cell,
state: state,
actual: fakeActual,
loc: [0, 0],
orient: CDOrient.original
! coreMess => {messy ← TRUE; abortionCause ← reason; debugTorch ← torch; CONTINUE}];
IF useTNT THEN BlowTNT [state.nt];
quantity ← state.globalErrorCount;
IF messy THEN BEGIN
Clean up.
IF debug THEN break;
cell.properties ← CoreProperties.PutProp [cell.properties, checked, NIL];
[] ← Msg ["Your Core data structure is busted! You lose, you lose."];
coreInconsistent [abortionCause, debugTorch]
END;
quantity ← state.globalErrorCount;
IF state.shy THEN [] ← Msg [IO.PutFR1 ["Number of design violations found: %g", IO.card [state.globalErrorCount]]]
ELSE IO.PutF [stream: mLog,
format: "\nNumber of design violations found: %l%g%l\n",
v1: IO.rope ["b"], v2: IO.card [state.globalErrorCount], v3: IO.rope ["B"]];
EXITS
paranoia => NULL-- cannot communicate with anybody
END; -- CheckDesignRules
Actions on ChipNDale Objects
We have to consider only the objects of the rectangle class. All others are converted to rectangles in the flattening the extractor causes to allow the simulator to highlight nodes.
FindGeometry: TYPE = SoS.FindGeometry;
PROC [self: CD.Instance] RETURNS [r: CD.Rect ← CDBasics.empty, l: CD.Layer ← CD.undefLayer]
FindCDRect: FindGeometry ~ BEGIN
PROC [self: CD.Instance] RETURNS [r: CD.Rect ← CDBasics.empty, l: CD.Layer ← CD.undefLayer]
Send: PROC ~ INLINE BEGIN
getRect: REF FindGeometry ← NARROW [CDProperties.GetProp [from: self.ob, prop: analysis]];
IF getRect = NIL THEN getRect ← NARROW [CDProperties.GetProp [self.ob.class, analysis]];
IF getRect = NIL THEN BEGIN
SIGNAL break; -- Unexpected object class. You may proceed, but should get your stuff updated.
getRect ← NEW [FindGeometry ← FindNoGeom]
END;
[r, l] ← getRect [self]
END; -- Send
IF fast THEN
SELECT self.ob.class.objectType FROM
rectClass => [r, l] ← FindRectGeom [self];
pinClass => [r, l] ← FindNoGeom [self];
ENDCASE => Send []
ELSE Send []
END; -- FindCDRect
FindRectGeom: FindGeometry ~ BEGIN
PROC [self: CD.Instance] RETURNS [r: CD.Rect ← CDBasics.empty, l: CD.Layer ← CD.undefLayer]
For objects of the class $Rect.
IF debug THEN CDProperties.PutObjectProp [self.ob, trace, trace];
r ← CDOrient.MapRect [itemInCell: [0, 0, self.ob.size.x, self.ob.size.y],
cellSize: self.ob.size,
cellInstOrient: self.orientation,
cellInstPos: self.location];
l ← self.ob.layer
END; -- FindRectGeom
FindNoGeom: FindGeometry ~ BEGIN
The object contributes no geometry.
IF debug THEN CDProperties.PutObjectProp [self.ob, trace, trace]
END; -- FindNoGeom
RegisterGeomProcs: PROC ~ BEGIN
At this place foer ease of maintenance. Called in the module initialisation part.
[] ← CDProperties.PutProp [
onto: CD.FetchObjectClass [rectClass],
prop: analysis,
val: NEW [FindGeometry ← FindRectGeom]];
[] ← CDProperties.PutProp [
onto: CD.FetchObjectClass [pinClass],
prop: analysis,
val: NEW [FindGeometry ← FindNoGeom]]
END; -- RegisterGeomProcs
GetObject: PROC [cell: Core.CellType, state: State] RETURNS [obj: CD.Object] ~ INLINE BEGIN
obj ← NARROW [CoreProperties.GetProp [cell.properties, state.cdObjKey], CD.Object];
IF persist AND (obj=NIL) AND (state.design#NIL) THEN obj ← CDDirectory.Fetch [state.design, CoreOps.GetCellTypeName[cell]].object;
RETURN [obj];
END; -- GetObject
Actions on Core Objects
CheckCell: TYPE = SoS.CheckCell;
PROC [self: Core.CellType, state: State, actual: Core.Wire, loc: CD.Position, orient: CD.Orientation];
CheckCoreCell: CheckCell ~ BEGIN
Send: PROC ~ INLINE BEGIN
check: REF CheckCell ← NARROW [CoreProperties.GetProp [self.properties, analysis]];
IF check = NIL THEN check ← NARROW [CoreProperties.GetProp [self.class.properties, analysis]];
IF check = NIL THEN BEGIN
obj: CD.Object = GetObject [self, state];
IF obj = NIL THEN RETURN; -- Cell contains no rectangles
check ← NEW [CheckCell ← DoNotCheck];
MarkError [self, state, [obj.class.interestRect[obj], "Cell has no provisions to be checked"]]
END;
check^ [self, state, actual, loc, orient]
END; -- Send
IF fast THEN
SELECT self.class FROM
CoreClasses.recordCellClass => CheckRecord [self, state, actual, loc, orient];
CoreClasses.transistorCellClass => CheckTransistor [self, state, actual, loc, orient];
CoreClasses.unspecifiedCellClass => NULL;
ENDCASE => Send []
ELSE Send []
END; -- CheckCoreCell
CheckRecord: CheckCell ~ BEGIN
[self: Core.CellType, state: State, actual: Core.Wire, loc: CD.Position, orient: CD.Orientation]
origin: CD.Position = [0, 0];
cellData: CoreClasses.RecordCellType;
bindingTable: RefTab.Ref;
boundInternal: Core.Wire;
propagatedActuals: WireSet; -- one wire per subcell
savedProps: PropSet;  -- holds properties during recursion
ownName: Rope.ROPE = CoreOps.GetCellTypeName [self];
IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp[self.properties, checked]#NIL) THEN RETURN;
IF state.shy THEN [] ← Msg [ownName] ELSE IO.Put1 [mLog, IO.char ['.]];
IF debug THEN IO.PutF [stream: dLog,
format: "\nChecking cell %l%g%l .\n",
v1: IO.rope ["e"],
v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName],
v3: IO.rope ["E"]];
IF state.abort^ THEN ERROR ABORTED;
ClearErrors [self, state];
cellData ← NARROW [self.data, CoreClasses.RecordCellType];
To be able to compare wires by comparing refs, the bound internal is the actual wire with the properties of the internal wire. Note that in this way the properties of the internal wire are propagated up to the actual wire; hence the actual wire has to be restored when popping up.
savedProps ← NEW [PropSetRec[actual.size]];
FOR p: NAT IN [0 .. actual.size) DO
savedProps[p] ← CoreProperties.CopyProps [actual[p].properties];
ENDLOOP;
bindingTable ← CreateBindingTable [actual: actual, public: self.public];
boundInternal ← BindInternal [bindingTable, cellData.internal, state];
IF occDebug THEN BEGIN
dLog.PutF1 ["Binding and propagating: %g. Binding table:\n", IO.rope [ownName]];
PrintBinding [bindingTable];
dLog.PutF1 ["Bound internal of %g:\n", IO.rope [ownName]];
PrintWire [boundInternal]
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] ← PropagateBinding [state, bindingTable, cellData.instances[sub].actual]
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
cdInst: CD.Instance = NARROW [CoreProperties.GetProp [cellData.instances[sub].properties, state.cdInstKey]];
IF (cdInst = NIL) THEN coreMess ["Core record cell has no geometry (1)", cellData];
CheckCoreCell [self: cellData.instances[sub].type,
state: state,
actual: propagatedActuals[sub],
loc: CDBasics.AddPoints [loc, cdInst.location],
orient: CDOrient.ComposeOrient [orient, cdInst.orientation]]
ENDLOOP;
Check the internal.
IF occDebug THEN BEGIN
dLog.PutF1 ["Verifying: %g\nbound internal:\n", IO.rope [ownName]];
PrintWire [boundInternal]
END;
Since in this case the error will go into the cell itself, we produce cell relative coordinates by appropriately setting loc1, loc2, orient1, and orient2.
FOR i: NAT IN [0 .. cellData.internal.size) DO
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.
MaterialSeparation [state: state,
cell: self,
w1: boundInternal[i],
w2: boundInternal[j],
loc1: origin, loc2: origin,
orient1: CD.original, orient2: CD.original]
ENDLOOP;
WidthCheck [self, state, boundInternal[i]]
ENDLOOP;
Check the separation between the internal of self and its subcells.
FOR sub: NAT IN [0 .. cellData.size) DO
cdInst: CD.Instance = NARROW [CoreProperties.GetProp [cellData.instances[sub].properties, state.cdInstKey]];
IF (cdInst = NIL) THEN coreMess ["Core record cell has no geometry (2)", cellData];
FOR i: NAT IN [0 .. cellData.internal.size) DO
MaterialToCellSeparation [state: state,
self: cellData.instances[sub].type,
actual: propagatedActuals[sub],
wire: cellData.internal[i],
father: self, -- for error marking
materialLoc: loc,
cellLoc: cdInst.location,
materialOrient: orient,
cellOrient: cdInst.orientation]
ENDLOOP
ENDLOOP;
Check the separation between the subcells of self.
IF useTNT THEN SweepTNT [state.nt];
FOR sub1: NAT IN [0 .. cellData.size) DO
cdInst1: CD.Instance = NARROW [CoreProperties.GetProp [cellData.instances[sub1].properties, state.cdInstKey]];
IF (cdInst1 = NIL) THEN coreMess ["Core record cell has no geometry (3)", cellData];
FOR sub2: NAT IN (sub1 .. cellData.size) DO
cdInst2: CD.Instance = NARROW [CoreProperties.GetProp [cellData.instances[sub2].properties, state.cdInstKey]];
IF (cdInst2 = NIL) THEN coreMess ["Core record cell has no geometry (4)", cellData];
IF useTNT AND (InTNT [state.nt, cdInst1, cdInst2, propagatedActuals[sub1], propagatedActuals[sub2]]) THEN LOOP;
CellToCellSeparation [state: state,
self: cellData.instances[sub1].type,
otherCell: cellData.instances[sub2].type,
selfActual: propagatedActuals[sub1],
otherActual: propagatedActuals[sub2],
father: self, -- for error marking
selfLoc: CDBasics.AddPoints [loc, cdInst1.location],
otherLoc: CDBasics.AddPoints [loc, cdInst2.location],
selfOrient: CDOrient.ComposeOrient [orient, cdInst1.orientation],
otherOrient: CDOrient.ComposeOrient [orient, cdInst2.orientation]];
IF useTNT THEN RememberTNT [state.nt, cdInst1, cdInst2, propagatedActuals[sub1], propagatedActuals[sub2]]
ENDLOOP
ENDLOOP;
Wires that were propagated down under recursion now contain all the geometry of the leaf cells. Here we restore the status quo ante.
FOR p: NAT IN [0 .. self.public.size) DO
actual[p].properties ← CoreProperties.CopyProps [savedProps[p]];
ENDLOOP;
self.properties ← CoreProperties.PutProp [self.properties, checked, checked]
END; -- CheckRecord
CheckTransistor: CheckCell ~ BEGIN
[self: Core.CellType, state: State, actual: Core.Wire, loc: CD.Position, orient: CD.Orientation]
Transistors are atomic ChipNDale objects and are supposed to be correct by construction.
DoNotCheck [self, state, actual, loc, orient]
END; -- CheckTransistor
DoNotCheck: CheckCell ~ BEGIN
[self: Core.CellType, state: State, actual: Core.Wire, loc: CD.Position, orient: CD.Orientation]
(self.class = CoreClasses.unspecifiedCellClass) OR (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL);
self.properties ← CoreProperties.PutProp [self.properties, checked, checked]
END; -- DoNotCheck
RegisterAnalysisProcs: PROC ~ BEGIN
Called in the module initialisation part.
CoreClasses.recordCellClass.properties ← CoreProperties.PutProp [
on: CoreClasses.recordCellClass.properties,
prop: analysis,
value: NEW [CheckCell ← CheckRecord]];
CoreClasses.transistorCellClass.properties ← CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, analysis, NEW [CheckCell ← CheckTransistor]];
CoreClasses.unspecifiedCellClass.properties ← CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, analysis, NEW [CheckCell ← DoNotCheck]];
END; -- RegisterAnalysisProcs
Pruning
CellHullProc: TYPE = SoS.CellHullProc;
PROC [self: Core.CellType, state: State] RETURNS [h: CD.Rect ← CDBasics.empty]
AtomicWireHull: PROC [w: Core.Wire, state: State] RETURNS [h: CD.Rect ← CDBasics.empty] ~ BEGIN
Coputes the bounding box of the rectangles of material that make up a wire.
stored: REF CD.Rect ← NARROW [CoreProperties.GetProp [w.properties, bbKey]];
geom: CD.InstanceList;
IF w.size > 0 THEN BEGIN-- wire is not atomic
[] ← Msg ["Your Core data structure is busted! You do not lose yet."];
break -- wire is not atomic
END;
IF (stored # NIL) THEN RETURN [stored^];
geom ← NARROW [CoreProperties.GetProp [w.properties, state.cdInstListKey]];
FOR g: CD.InstanceList ← geom, g.rest WHILE g # NIL DO
r: CD.Rect = FindCDRect [g.first].r;
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]
ENDLOOP;
stored ← NEW [CD.Rect ← h];
w.properties ← CoreProperties.PutProp [w.properties, bbKey, stored]
END; -- AtomicWireHull
CellHull: CellHullProc ~ BEGIN
PROC [self: Core.CellType, state: State] RETURNS [h: CD.Rect ← CDBasics.empty]
Needed for recursion.
hull: REF CellHullProc ← NEW [CellHullProc];
SELECT self.class FROM
CoreClasses.recordCellClass => hull^ ← RecordCellHull;
CoreClasses.transistorCellClass => hull^ ← TransistorHull;
CoreClasses.unspecifiedCellClass => hull^ ← UnspecifiedHull;
ENDCASE => BEGIN
hull ← NARROW [CoreProperties.GetProp [self.properties, cellHull]];
IF hull = NIL THEN hull ← NARROW [CoreProperties.GetProp [self.class.properties, cellHull]];
IF hull = NIL THEN hull^ ← UnspecifiedHull;
END;
RETURN [hull^ [self, state]]
END; -- CellHull;
RecordCellHull: CellHullProc ~ BEGIN
[self: Core.CellType, state: State] RETURNS [h: CD.Rect ← CDBasics.empty]
stored: REF CD.Rect ← NARROW [CoreProperties.GetProp [self.properties, bbKey]];
IF (stored = NIL) THEN BEGIN
cellData: CoreClasses.RecordCellType = NARROW [self.data, CoreClasses.RecordCellType];
IF cellData.internal.size = 0 THEN break; -- atomic wire not expected here
FOR i: NAT IN [0 .. cellData.internal.size) DO
a: CD.Rect = AtomicWireHull [cellData.internal[i], state];
h.x1 ← MIN [h.x1, a.x1]; h.y1 ← MIN [h.y1, a.y1];
h.x2 ← MAX [h.x2, a.x2]; h.y2 ← MAX [h.y2, a.y2]
ENDLOOP;
FOR sub: NAT IN [0 .. cellData.size) DO
Note: because of the program flow a run time this recursion does not really propagate, since at this point stored # NIL.
r: CD.Rect = CellHull [cellData.instances[sub].type, state];
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]
ENDLOOP;
stored ← NEW [CD.Rect ← h];
self.properties ← CoreProperties.PutProp [self.properties, bbKey, stored]
END
END; -- RecordCellHull
TransistorHull: CellHullProc ~ BEGIN
[self: Core.CellType, state: State] RETURNS [h: CD.Rect ← CDBasics.empty]
stored: REF CD.Rect ← NARROW [CoreProperties.GetProp [self.properties, bbKey]];
IF (stored = NIL) THEN BEGIN
wire: Core.Wire = self.public;
IF wire.size = 0 THEN break; -- atomic wire not expected here
FOR i: NAT IN [0 .. wire.size) DO
a: CD.Rect = AtomicWireHull [wire[i], state];
h.x1 ← MIN [h.x1, a.x1]; h.y1 ← MIN [h.y1, a.y1];
h.x2 ← MAX [h.x2, a.x2]; h.y2 ← MAX [h.y2, a.y2]
ENDLOOP;
stored ← NEW [CD.Rect ← h];
self.properties ← CoreProperties.PutProp [self.properties, bbKey, stored]
END
END; -- TransistorHull
UnspecifiedHull: CellHullProc ~ BEGIN
[self: Core.CellType, state: State] RETURNS [h: CD.Rect ← CDBasics.empty]
stored: REF CD.Rect ← NARROW [CoreProperties.GetProp [self.properties, bbKey]];
IF (stored = NIL) THEN BEGIN
stored ← NEW [CD.Rect ← h];
self.properties ← CoreProperties.PutProp [self.properties, bbKey, stored]
END
END; -- UnspecifiedHull
RegisterPruningProcs: PROC ~ BEGIN
Called in the module initialisation part.
CoreClasses.recordCellClass.properties ← CoreProperties.PutProp [
on: CoreClasses.recordCellClass.properties,
prop: cellHull,
value: NEW [CellHullProc ← RecordCellHull]];
CoreClasses.transistorCellClass.properties ← CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, cellHull, NEW [CellHullProc ← TransistorHull]];
CoreClasses.unspecifiedCellClass.properties ← CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, cellHull, NEW [CellHullProc ← UnspecifiedHull]]
END; -- RegisterPruningProcs
Binding
CreateBindingTable: PROC [actual, public: Core.Wire] RETURNS [bindingTable: RefTab.Ref] ~ BEGIN
tableSize: RefTab.SeqIndex ← public.size;
IF actual.size # public.size THEN BEGIN
IF debug THEN BEGIN
dLog.Put1 [IO.rope ["actual.size # public.size. Actual wire:\n"]]; PrintWire [actual];
dLog.Put1 [IO.rope ["Public wire:\n"]]; 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: Core.Wire] RETURNS [boundActual: Core.Wire] ~ BEGIN
Called to propagate the binding on level down. Applied to the actual wire.
PropagateAtomic: PROC [state: State, bindingTable: RefTab.Ref, actual: Core.Wire] RETURNS [boundActual: Core.Wire] ~ INLINE BEGIN
For elements of sequence.
boundActual ← NARROW [RefTab.Fetch [bindingTable, actual].val];
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.
IF boundActual = NIL THEN BEGIN
name: Rope.ROPE = IF debug THEN IO.PutFR1 ["New signal # %g", IO.card [state.wireCreationCount]] ELSE "New signal by SoS";
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: Core.Wire, state: State] RETURNS [boundInternal: Core.Wire] ~ BEGIN
For full sequence.
boundInternal ← CoreOps.CopyWire [internal];
FOR i: NAT IN [0 .. internal.size) DO
b: Core.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 BEGIN
To be able to compare wires by comparing refs, the bound internal is the actual wire with the properties of the internal wire. Note that in this way the properties of the internal wire are propagated up to the actual wire; hence the actual wire has to be restored when popping up.
boundInternal[i] ← b;
We have to preserve the geometry that is hanging on the public subset of the wire.
boundInternal[i] ← CoreOps.SetShortWireName [wire: boundInternal[i],
name: CoreOps.GetShortWireName[internal[i]]];
boundInternal[i].properties ← CopyGeomProps [internal[i].properties, boundInternal[i].properties, state]
boundInternal[i].properties ← CoreProperties.CopyProps [internal[i].properties]
END
ENDLOOP
END; -- BindInternal
BindTransistor: PROC [public, actual: Core.Wire, state: State] RETURNS [tw: Core.Wire] ~ BEGIN
Please read comments in BindInternal.
tw ← CoreOps.CopyWire [public];
FOR i: NAT IN [0 .. public.size) DO
tw[i] ← actual[i];
tw[i] ← CoreOps.SetShortWireName [wire: tw[i],
name: CoreOps.GetShortWireName[public[i]]];
tw[i].properties ← CopyGeomProps [public[i].properties, tw[i].properties, state]
tw[i].properties ← CoreProperties.CopyProps [public[i].properties]
ENDLOOP
END; -- BindTransistor
Separation and Width Check Procedures
MaterialToCellProc: TYPE = SoS.MaterialToCellProc;
PROC [self: Core.CellType, state: State, actual, wire: Core.Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation];
CellToCellProc: TYPE = SoS.CellToCellProc;
PROC [self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Core.Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation];
WidthCheck: PROC [c: Core.CellType, s: State, w: Core.Wire] ~ BEGIN
The location and orientation are needed place the error rectangle.
At the moment our simplicistic approach does not have any knowledge of the topology. Therefore we cannot really test this rule.
min, max, a, b: CD.Number; -- a and b are such that a > b
r: CD.Rect; l: CD.Layer; key: ATOM;
il: CD.InstanceList = NARROW [CoreProperties.GetProp [w.properties, s.cdInstListKey]];
FOR i: CD.InstanceList ← il, i.rest WHILE i # NIL DO
[r, l] ← FindCDRect [i.first];
key ← CD.LayerKey [l];
IF (l < specialLayers) OR (key = NIL) THEN LOOP;
min ← CDSimpleRules.MinWidth [l]; max ← CDSimpleRules.MaxWidth [l];
a ← MAX [(r.x2 - r.x1), (r.y2 - r.y1)]; b ← MIN [(r.x2 - r.x1), (r.y2 - r.y1)];
IF (a < min) OR (b < min) OR (a > max) OR (b > max) THEN BEGIN
<< Total Hack. Yuk. >>
The following is a cochonnerie to avoid flagging large split contacts that may occur in CMOS-B designs. The heuristics is there because of two reasons: a) I cannot know from which contact class the cut is coming, b) I do not want to put in here an even worse cochonnerie by introducing a dependency of SoS on technologies by importing a specific technology.
splitA: CD.Number = 3 * min; splitB: CD.Number = min; -- yuk, spit
Note that I am using an atom because many users hide the technology from SoS. If the splits are flagged then the atom in ChipNDale may have changed.
IF (CD.LayerKey[l] = $cut) AND (a = splitA) AND (b = splitB) THEN NULL-- mumble
ELSE MarkError [c, s, [r, Rope.Cat ["Width violation on layer ", Atom.GetPName [CD.LayerKey [l]], " (wire ", CoreOps.GetShortWireName[w], ")"]]]
END
ENDLOOP
END; -- WidthCheck
MaterialSeparation: PROC [cell: Core.CellType, state: State, w1, w2: Core.Wire, loc1, loc2: CD.Position, orient1, orient2: CD.Orientation] ~ BEGIN
If two unrelated rectangles of material intersect, an error is flagged.
The parameter cell selects the cell receiving possible error messages. It must be the father of the cell containing w1 and the cell containing w2.
aequipotential: BOOL = (w1 = w2);
cd1, cd2: CD.InstanceList; obj1, obj2: CD.Object;
r1, r2, r, s: CD.Rect;
l1, l2: CD.Layer; key1, key2: ATOM;
sep: CD.Number;
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[AtomicWireHull[w1,state],state.maxSeparation], AtomicWireHull[w2,state]]]
END; -- Intersect
NotCuts: PROC RETURNS [BOOL] ~ INLINE BEGIN
Note that I am using atoms because many users hide the technology from SoS. If violations from cuts arenot flagged then the atom in ChipNDale may have changed.
RETURN [NOT (((key1 = $cut) OR (key1 = $cut2)) AND ((key2 = $cut) OR (key2 = $cut2)))]
END; -- NotCuts
SameRect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [(l1 = l2) AND (r1.x1 = r2.x1) AND (r1.y1 = r2.y1) AND (r1.x2 = r2.x2) AND (r1.y2 = r2.y2)]
END; -- SameRect
IF state.abort^ THEN ERROR ABORTED;
We cannot skip the verification in the w1 = w2. In fact, in Core and Sinix, cuts are represented as material rectangles in wires. So to SoS two cuts violating the separation rule will alway look as two aequipotential rectangles.
cd1 ← NARROW [CoreProperties.GetProp [w1.properties, state.cdInstListKey]];
cd2 ← NARROW [CoreProperties.GetProp [w2.properties, state.cdInstListKey]];
IF (cd1 = NIL) OR (cd2 = NIL) THEN RETURN; -- Skip wires without geometry
IF (NOT Intersect[]) THEN RETURN;  -- or whose geometry is too far apart.
FOR outer: CD.InstanceList ← cd1, outer.rest WHILE outer # NIL DO
obj1 ← outer.first.ob;
[r1, l1] ← FindCDRect [outer.first];
key1 ← CD.LayerKey [l1];
Filter out the special layers. Also the extractor creates material at illegal levels. We try heuristically to filter it out here.
IF (l1 < specialLayers) OR (key1 = NIL) THEN LOOP;
r1 ← CDOrient.MapRect [itemInCell: r1,
cellSize: outer.first.ob.size,
cellInstOrient: orient1,
cellInstPos: loc1];
FOR inner: CD.InstanceList ← cd2, inner.rest WHILE inner # NIL DO
IF state.abort^ THEN ERROR ABORTED;
obj2 ← inner.first.ob;
Probe 1: Call the debugger. Give property $SoSBreak to two pieces of material that violate a rule but are not flagged.
IF debug AND (obj1#NIL) AND (obj2#NIL) AND (CDProperties.GetProp[obj1,$SoSBreak]#NIL) AND (CDProperties.GetProp[obj1,$SoSBreak]#NIL) THEN SIGNAL break;
[r2, l2] ← FindCDRect [inner.first];
The order of the next two statements is very important.
key2 ← CD.LayerKey [l2];
IF aequipotential AND NotCuts[] THEN LOOP;
IF (l2 < specialLayers) OR (key2 = NIL) THEN LOOP;
sep ← CDSimpleRules.MinDist [l1, l2 ! CDSimpleRules.NotKnown => sep ← 0];
IF sep = 0 THEN LOOP;
r2 ← CDOrient.MapRect [r2, inner.first.ob.size, orient2, loc2];
IF aequipotential AND SameRect[] THEN LOOP;
r ← CDBasics.Extend [r1, sep / 2]; s ← CDBasics.Extend [r2, sep / 2];
IF ((r.x1<s.x2) AND (s.x1<r.x2) AND (r.y1<s.y2) AND (s.y1<r.y2)) THEN BEGIN
rect1: Rope.ROPE = Rope.Cat [Atom.GetPName [key1], " (wire ", CoreOps.GetShortWireName[w1], ")"];
rect2: Rope.ROPE = Rope.Cat [Atom.GetPName [key2], " (wire ", CoreOps.GetShortWireName[w2], ")"];
Probe 2: Set a break point after this line to see why a wrong violation is flagged.
MarkError [cell, state, [CDBasics.Intersection[r,s], Rope.Cat["Separation violation between ", rect1, " and ", rect2]]];
END
ENDLOOP-- inner
ENDLOOP-- outer
END; -- MaterialSeparation
MaterialToCellSeparation: MaterialToCellProc ~ BEGIN
[self: Core.CellType, state: State, actual, wire: Core.Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation]
For convenience.
Send: PROC ~ INLINE BEGIN
check: REF MaterialToCellProc ← NARROW [CoreProperties.GetProp [self.properties, matToCell]];
IF check = NIL THEN check ← NARROW [CoreProperties.GetProp [self.class.properties, matToCell]];
IF check = NIL THEN BEGIN
obj: CD.Object = GetObject [self, state];
IF obj = NIL THEN RETURN; -- Cell contains no rectangles
check ← NEW [MaterialToCellProc ← MaterialToUnspecifiedSeparation];
MarkError [self, state, [obj.class.interestRect[obj], "Cell has no provisions to be checked"]]
END;
check^ [self, state, actual, wire, father, materialLoc, cellLoc, materialOrient, cellOrient]
END; -- Send
IF fast THEN
SELECT self.class FROM
CoreClasses.recordCellClass => MaterialToRecordCellSeparation [self, state, actual, wire, father, materialLoc, cellLoc, materialOrient, cellOrient];
CoreClasses.transistorCellClass => MaterialToTransistorSeparation [self, state, actual, wire, father, materialLoc, cellLoc, materialOrient, cellOrient];
CoreClasses.unspecifiedCellClass => MaterialToUnspecifiedSeparation [self, state, actual, wire, father, materialLoc, cellLoc, materialOrient, cellOrient];
ENDCASE => Send []
ELSE Send []
END; -- MaterialToCellSeparation
MaterialToTransistorSeparation: MaterialToCellProc ~ BEGIN
[self: Core.CellType, state: State, actual, wire: Core.Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation]
wbb, tbb: CD.Rect;  -- bounding boxes
tw: Core.Wire;
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[tbb,state.maxSeparation], wbb]]
END; -- Intersect
IF state.abort^ THEN ERROR ABORTED;
IF (CoreProperties.GetProp [self.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 [wire, state];
wbb ← CDOrient.MapRect [itemInCell: wbb,
cellSize: CDBasics.SizeOfRect [wbb],
cellInstOrient: materialOrient,
cellInstPos: materialLoc];
tbb ← TransistorHull [self, state];
tbb ← CDOrient.MapRect [itemInCell: tbb,
cellSize: CDBasics.SizeOfRect [tbb],
cellInstOrient: cellOrient,
cellInstPos: cellLoc];
IF (NOT Intersect[]) THEN RETURN;
tw ← BindTransistor [self.public, actual, state];
Check each element of the transistor wire of the cell against each element of wire.
FOR i: NAT IN [0 .. tw.size) DO
MaterialSeparation [state: state,
cell: father,
w1: tw[i],
w2: wire,
loc1: cellLoc,
loc2: materialLoc,
orient1: cellOrient,
orient2: materialOrient]
ENDLOOP
END; -- MaterialToTransistorSeparation
MaterialToUnspecifiedSeparation: MaterialToCellProc ~ BEGIN
[self: Core.CellType, state: State, actual, wire: Core.Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation]
NULL
END; -- MaterialToUnspecifiedSeparation
MaterialToRecordCellSeparation: MaterialToCellProc ~ BEGIN
[self: Core.CellType, state: State, actual, wire: Core.Wire, father: Core.CellType, materialLoc, cellLoc: CD.Position, materialOrient, cellOrient: CD.Orientation]
wbb, cbb: CD.Rect;  -- bounding boxes
boundInternal: Core.Wire;
propagatedActuals: WireSet; -- one wire per subcell
bindingTable: RefTab.Ref;
cellData: CoreClasses.RecordCellType ← NARROW [self.data];
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[cbb,state.maxSeparation], wbb]]
END; -- Intersect
IF state.abort^ THEN ERROR ABORTED;
IF (CoreProperties.GetProp [self.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 [wire, state];
wbb ← CDOrient.MapRect [itemInCell: wbb,
cellSize: CDBasics.SizeOfRect [wbb],
cellInstOrient: materialOrient,
cellInstPos: materialLoc];
cbb ← RecordCellHull [self, state];
cbb ← CDOrient.MapRect [itemInCell: cbb,
cellSize: CDBasics.SizeOfRect [cbb],
cellInstOrient: cellOrient,
cellInstPos: cellLoc];
IF (NOT Intersect[]) THEN RETURN;
Check each element of the internal wire of the cell against each element of wire.
bindingTable ← CreateBindingTable [actual: actual, public: self.public];
boundInternal ← BindInternal [bindingTable, cellData.internal, state];
FOR i: NAT IN [0 .. boundInternal.size) DO
MaterialSeparation [state: state,
cell: father,
w1: boundInternal[i],
w2: wire,
loc1: cellLoc,
loc2: materialLoc,
orient1: cellOrient,
orient2: materialOrient]
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] ← PropagateBinding [state, bindingTable, cellData.instances[sub].actual]
ENDLOOP;
FlushBindingTable [bindingTable];
Check intersections between subcells of cell and wire.
FOR sub: NAT IN [0 .. cellData.size) DO
cdInst: CD.Instance = NARROW [CoreProperties.GetProp [cellData.instances[sub].properties, state.cdInstKey]];
IF (cdInst = NIL) THEN coreMess ["Core record subcell has no geometry (6)", cellData];
MaterialToCellSeparation [self: cellData.instances[sub].type,
state: state,
actual: propagatedActuals[sub],
wire: wire,
father: self,
materialLoc: materialLoc,
cellLoc: CDBasics.AddPoints [cellLoc, cdInst.location],
materialOrient: materialOrient,
cellOrient: CDOrient.ComposeOrient [cellOrient, cdInst.orientation]]
ENDLOOP
END; -- MaterialToRecordCellSeparation
CellToCellSeparation: CellToCellProc ~ BEGIN
[self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Core.Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation]
For convenience.
Send: PROC ~ INLINE BEGIN
check: REF CellToCellProc ← NARROW [CoreProperties.GetProp [self.properties, cellToCell]];
IF check = NIL THEN check ← NARROW [CoreProperties.GetProp [self.class.properties, cellToCell]];
IF check = NIL THEN BEGIN
obj: CD.Object = GetObject [self, state];
IF obj = NIL THEN RETURN; -- Cell contains no rectangles
check ← NEW [CellToCellProc ← UnspecifiedToAnyClassSeparation];
MarkError [self, state, [obj.class.interestRect[obj], "Cell has no provisions to be checked"]]
END;
check^ [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient]
END; -- Send
SELECT otherCell.class FROM
CoreClasses.unspecifiedCellClass => RETURN;
CoreClasses.recordCellClass => NULL; -- default case
CoreClasses.transistorCellClass => -- nasty case
IF self.class = CoreClasses.transistorCellClass THEN BEGIN
IF fast THEN
TransistorToTransistorSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient]
ELSE Send [];
RETURN
END
ELSE BEGIN-- swap
selfZ: Core.CellType = self;
selfActualZ: Core.Wire = selfActual;
selfLocZ: CD.Position = selfLoc;
selfOrientZ: CD.Orientation = selfOrient;
self ← otherCell;  otherCell ← selfZ;
selfActual ← otherActual; otherActual ← selfActualZ;
selfLoc ← otherLoc;  otherLoc ← selfLocZ;
selfOrient ← otherOrient;  otherOrient ← selfOrientZ
END;
ENDCASE => ERROR;
IF fast THEN
SELECT self.class FROM
CoreClasses.recordCellClass => RecordCellToRecordCellSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient];
CoreClasses.transistorCellClass => TransistorToRecordCellSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient];
CoreClasses.unspecifiedCellClass => UnspecifiedToAnyClassSeparation [self, state, otherCell, selfActual, otherActual, father, selfLoc, otherLoc, selfOrient, otherOrient];
ENDCASE => Send []
ELSE Send []
END; -- CellToCellSeparation
TransistorToRecordCellSeparation: CellToCellProc ~ BEGIN
[self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Core.Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation]
cbb1, cbb2: CD.Rect;  -- bounding boxes
othersCellData: CoreClasses.RecordCellType ← NARROW [otherCell.data];
othersBindingTable: RefTab.Ref;
tw: Core.Wire;
othersBoundInternal: Core.Wire;
ownName: Rope.ROPE = CoreOps.GetCellTypeName [self];
otherName: Rope.ROPE = CoreOps.GetCellTypeName [otherCell];
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.maxSeparation], cbb2]]
END; -- Intersect
Preamble.
IF state.abort^ THEN ERROR ABORTED;
IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [otherCell.properties, doNotAnalyse]#NIL) THEN RETURN;
IF debug THEN BEGIN
IO.PutF [stream: dLog,
format: "Checking transistor %l%g%l vs. %g. ",
v1: IO.rope ["e"],
v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName],
v3: IO.rope ["E"],
v4: IF otherName = NIL THEN IO.refAny [otherCell] ELSE IO.rope [otherName]];
IO.PutF [stream: dLog,
format: "Rel. orient: %g, dist: (%g, %g).\n",
v1: IO.int [CDOrient.DecomposeOrient [otherOrient, selfOrient]],
v2: IO.int [(otherLoc.x - selfLoc.x) / previousTechnology.lambda],
v3: IO.int [(otherLoc.y - selfLoc.y) / previousTechnology.lambda]]
END;
cbb1 ← RecordCellHull [self, state];
cbb1 ← CDOrient.MapRect [itemInCell: cbb1,
cellSize: CDBasics.SizeOfRect [cbb1],
cellInstOrient: selfOrient,
cellInstPos: selfLoc];
cbb2 ← RecordCellHull [otherCell, state];
cbb2 ← CDOrient.MapRect [itemInCell: cbb2,
cellSize: CDBasics.SizeOfRect [cbb2],
cellInstOrient: otherOrient,
cellInstPos: otherLoc];
IF (NOT Intersect[]) THEN RETURN;
Check the separation between the internal of transistor and cell2.
IF state.abort^ THEN ERROR ABORTED;
othersBindingTable ← CreateBindingTable [actual: otherActual, public: otherCell.public];
othersBoundInternal ← BindInternal [othersBindingTable, othersCellData.internal, state];
tw ← BindTransistor [self.public, selfActual, state];
FlushBindingTable [othersBindingTable];
FOR i: NAT IN [0 .. tw.size) DO
FOR j: NAT IN [0 .. othersCellData.internal.size) DO
MaterialSeparation [state: state,
cell: father, -- the cell getting the error flag --
w1: tw[i],
w2: othersBoundInternal[j],
loc1: selfLoc,
loc2: otherLoc,
orient1: selfOrient,
orient2: otherOrient]
ENDLOOP
ENDLOOP
END; -- TransistorToRecordCellSeparation
TransistorToTransistorSeparation: CellToCellProc ~ BEGIN
[self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Core.Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation]
cbb1, cbb2: CD.Rect;  -- bounding boxes
tw1, tw2: Core.Wire;
ownName: Rope.ROPE = CoreOps.GetCellTypeName [self];
otherName: Rope.ROPE = CoreOps.GetCellTypeName [otherCell];
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.maxSeparation], cbb2]]
END; -- Intersect
Preamble.
IF state.abort^ THEN ERROR ABORTED;
IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [otherCell.properties, doNotAnalyse]#NIL) THEN RETURN;
cbb1 ← RecordCellHull [self, state];
cbb1 ← CDOrient.MapRect [itemInCell: cbb1,
cellSize: CDBasics.SizeOfRect [cbb1],
cellInstOrient: selfOrient,
cellInstPos: selfLoc];
cbb2 ← RecordCellHull [otherCell, state];
cbb2 ← CDOrient.MapRect [itemInCell: cbb2,
cellSize: CDBasics.SizeOfRect [cbb2],
cellInstOrient: otherOrient,
cellInstPos: otherLoc];
IF (NOT Intersect[]) THEN RETURN;
Check the separation between the internal of cell1 and cell2.
IF state.abort^ THEN ERROR ABORTED;
tw1 ← BindTransistor [self.public, selfActual, state];
tw2 ← BindTransistor [otherCell.public, otherActual, state];
FOR i: NAT IN [0 .. tw1.size) DO
FOR j: NAT IN [0 .. tw2.size) DO
MaterialSeparation [state: state,
cell: father, -- the cell getting the error flag --
w1: tw1[i],
w2: tw2[j],
loc1: selfLoc,
loc2: otherLoc,
orient1: selfOrient,
orient2: otherOrient]
ENDLOOP
ENDLOOP
END; -- TransistorToTransistorSeparation
UnspecifiedToAnyClassSeparation: CellToCellProc ~ BEGIN
[self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Core.Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation]
NULL
END; -- UnspecifiedToAnyClassSeparation
RecordCellToRecordCellSeparation: CellToCellProc ~ BEGIN
[self: Core.CellType, state: State, otherCell: Core.CellType, selfActual, otherActual: Core.Wire, father: Core.CellType, selfLoc, otherLoc: CD.Position, selfOrient, otherOrient: CD.Orientation]
cbb1, cbb2: CD.Rect;  -- bounding boxes
ownCellData: CoreClasses.RecordCellType ← NARROW [self.data];
othersCellData: CoreClasses.RecordCellType ← NARROW [otherCell.data];
ownBindingTable, othersBindingTable: RefTab.Ref;
ownBoundInternal, othersBoundInternal: Core.Wire;
ownName: Rope.ROPE = CoreOps.GetCellTypeName [self];
otherName: Rope.ROPE = CoreOps.GetCellTypeName [otherCell];
Intersect: PROC RETURNS [BOOL] ~ INLINE BEGIN
RETURN [CDBasics.Intersect [CDBasics.Extend[cbb1,state.maxSeparation], cbb2]]
END; -- Intersect
Preamble.
IF state.abort^ THEN ERROR ABORTED;
IF (CoreProperties.GetProp [self.properties, doNotAnalyse]#NIL) OR (CoreProperties.GetProp [otherCell.properties, doNotAnalyse]#NIL) THEN RETURN;
IF debug THEN BEGIN
IO.PutF [stream: dLog,
format: "Checking cell %l%g%l vs. %g. ",
v1: IO.rope ["e"],
v2: IF ownName = NIL THEN IO.refAny [self] ELSE IO.rope [ownName],
v3: IO.rope ["E"],
v4: IF otherName = NIL THEN IO.refAny [otherCell] ELSE IO.rope [otherName]];
IO.PutF [stream: dLog,
format: "Rel. orient: %g, dist: (%g, %g).\n",
v1: IO.int [CDOrient.DecomposeOrient [otherOrient, selfOrient]],
v2: IO.int [(otherLoc.x - selfLoc.x) / previousTechnology.lambda],
v3: IO.int [(otherLoc.y - selfLoc.y) / previousTechnology.lambda]]
END;
cbb1 ← RecordCellHull [self, state];
cbb1 ← CDOrient.MapRect [itemInCell: cbb1,
cellSize: CDBasics.SizeOfRect [cbb1],
cellInstOrient: selfOrient,
cellInstPos: selfLoc];
cbb2 ← RecordCellHull [otherCell, state];
cbb2 ← CDOrient.MapRect [itemInCell: cbb2,
cellSize: CDBasics.SizeOfRect [cbb2],
cellInstOrient: otherOrient,
cellInstPos: otherLoc];
IF (NOT Intersect[]) THEN RETURN;
Check the separation between the internal of cell1 and cell2.
IF state.abort^ THEN ERROR ABORTED;
ownBindingTable ← CreateBindingTable [actual: selfActual, public: self.public];
othersBindingTable ← CreateBindingTable [actual: otherActual, public: otherCell.public];
ownBoundInternal ← BindInternal [ownBindingTable, ownCellData.internal, state];
othersBoundInternal ← BindInternal [othersBindingTable, othersCellData.internal, state];
FlushBindingTable [ownBindingTable]; FlushBindingTable [othersBindingTable];
FOR i: NAT IN [0 .. ownCellData.internal.size) DO
FOR j: NAT IN [0 .. othersCellData.internal.size) DO
MaterialSeparation [state: state,
cell: father, -- the cell getting the error flag --
w1: ownBoundInternal[i],
w2: othersBoundInternal[j],
loc1: selfLoc,
loc2: otherLoc,
orient1: selfOrient,
orient2: otherOrient]
ENDLOOP
ENDLOOP
END; -- RecordCellToRecordCellSeparation
ComputeMaxSeparation: PROC [technology: CD.Technology] RETURNS [maxSeparation: CD.Number] ~ BEGIN
Initialises the separation table. This is where the technology comes in !
maxSeparation ← 0;
FOR s1: CD.Layer IN CD.Layer DO
IF CD.LayerTechnology[s1] = technology THEN
FOR s2: CD.Layer IN CD.Layer DO
IF CD.LayerTechnology[s2] = technology THEN BEGIN
sep: CD.Number = CDSimpleRules.MinDist [s1, s2 ! CDSimpleRules.NotKnown => LOOP]; -- try to filter out inappropriate layers
maxSeparation ← MAX [maxSeparation, sep]
END
ENDLOOP
ENDLOOP
END; -- ComputeMaxSeparation
RegisterSeparationProcs: PROC ~ BEGIN
Called in the module initialisation part.
CoreClasses.recordCellClass.properties ← CoreProperties.PutProp [
on: CoreClasses.recordCellClass.properties,
prop: matToCell,
value: NEW [MaterialToCellProc ← MaterialToRecordCellSeparation]];
CoreClasses.transistorCellClass.properties ← CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, matToCell, NEW [MaterialToCellProc ← MaterialToTransistorSeparation]];
CoreClasses.unspecifiedCellClass.properties ← CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, matToCell, NEW [MaterialToCellProc ← MaterialToUnspecifiedSeparation]];
CoreClasses.recordCellClass.properties ← CoreProperties.PutProp [
on: CoreClasses.recordCellClass.properties,
prop: cellToCell,
value: NEW [CellToCellProc ← RecordCellToRecordCellSeparation]];
CoreClasses.transistorCellClass.properties ← CoreProperties.PutProp [CoreClasses.transistorCellClass.properties, cellToCell, NEW [CellToCellProc ← TransistorToRecordCellSeparation]];
CoreClasses.unspecifiedCellClass.properties ← CoreProperties.PutProp [CoreClasses.unspecifiedCellClass.properties, cellToCell, NEW [CellToCellProc ← UnspecifiedToAnyClassSeparation]];
END; -- RegisterSeparationProcs
Design Rule Violation Flagging
ClearErrors: PROC [obj: Core.CellType, state: State] ~ BEGIN
Removes the error rectangles from a given ChipNDale object (e.g. a cell).
IF state.shy THEN RETURN;
IF (state.design # NIL) THEN
CDErrors.RemoveMessages [design: state.design, ob: GetObject [obj, state], owner: DRVkey];
obj.properties ← CoreProperties.PutProp [obj.properties, DRVkey, NIL]
END; -- ClearErrors
MarkError: PROC [obj: Core.CellType, state: State, e: ErrorRect] ~ BEGIN
Puts an error rectangle r into the ChipNDale object obj, and maintains an error counter in the Core structure.
objName: Rope.ROPE = CoreOps.GetCellTypeName [obj];
cdObj: CD.Object ← GetObject [obj, state];
violations: DRVNARROW [CoreProperties.GetProp [obj.properties, DRVkey]];
longMsg: Rope.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
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;
state.globalErrorCount ← state.globalErrorCount.SUCC;
IF state.abort^ THEN ERROR ABORTED;
IF state.shy AND (NOT Msg [longMsg]) THEN panic
ELSE BEGIN
IF state.globalErrorCount < errorFeedbackCutOff THEN IO.Put1 [mLog, IO.char ['|]];
obj.properties ← CoreProperties.PutProp [on: obj.properties, prop: DRVkey, value: violations];
IF (state.design # NIL) AND (cdObj # NIL) THEN
done ← CDErrors.IncludeMessage [
design: state.design,
ob: cdObj,
rect: CDBasics.Extend [e.r, 4],
message: e.msg,
owner: DRVkey].done;
IF NOT done THEN longMsg ← Rope.Cat [r1: longMsg, r2: " (not flagged in the layout)"];
IF debug THEN IO.PutF1 [dLog, "%g\n", IO.rope [longMsg]]
END;
IF (state.verbose) THEN
IO.PutF1 [mLog, "\n%g\n", IO.rope [longMsg]]
END; -- MarkError
Property Registrations
PrintError: CoreProperties.PropPrintProc ~ BEGIN
IO.PutF1 [stream: to,
format: "SoS error count = %g ",
value: IO.int [NARROW[val,DRV].count]]
END; -- PrintError
PrintChecked: CoreProperties.PropPrintProc ~ BEGIN
IO.Put1 [stream: to, value: IO.rope ["Design rules checked"]]
END; -- PrintChecked
RegisterProperties: PROC ~ BEGIN
Registers all properties.
ChipNDale
IF (NOT CDProperties.RegisterProperty [DRVkey, $gbb])
OR (NOT CDProperties.RegisterProperty [checked, $gbb])
OR (NOT CDProperties.RegisterProperty [bbKey, $gbb])
OR (NOT CDProperties.RegisterProperty [cellHull, $gbb])
OR (NOT CDProperties.RegisterProperty [trace, $gbb])
THEN IF NOT debug THEN ERROR; -- 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.propCopy, CoreProperties.PropDoCopy],
[CoreProperties.propCompare, CoreProperties.PropIntCompare],
[CoreProperties.propPrint, NEW [CoreProperties.PropPrintProc ← PrintError]]]];
CoreProperties.StoreProperties [prop: checked, properties: CoreProperties.Props [[CoreProperties.propCopy, CoreProperties.PropDoCopy],
[CoreProperties.propPrint, NEW [CoreProperties.PropPrintProc ← PrintChecked]]]];
CoreProperties.StoreProperties [prop: bbKey, properties: CoreProperties.Props [[CoreProperties.propCopy, CoreProperties.PropDoCopy]]];
CoreProperties.StoreProperties [prop: cellHull, properties: CoreProperties.Props [[CoreProperties.propCopy, CoreProperties.PropDoCopy]]];
CoreProperties.StoreProperties [prop: trace, properties: CoreProperties.Props [[CoreProperties.propCopy, CoreProperties.PropDoCopy]]];
CoreProperties.StoreProperties [prop: analysis, properties: CoreProperties.Props [[CoreProperties.propCopy, CoreProperties.PropDoCopy]]];
CoreProperties.StoreProperties [prop: matToCell, properties: CoreProperties.Props [[CoreProperties.propCopy, CoreProperties.PropDoCopy]]];
CoreProperties.StoreProperties [prop: cellToCell, properties: CoreProperties.Props [[CoreProperties.propCopy, CoreProperties.PropDoCopy]]];
END; -- RegisterProperties
Logging & Debugging
StartLog: PROC ~ BEGIN
Iniitalises the log streams. Should cater for errors IO.Error [$Failure, NIL], IO.Error [StreamClosed].
viewer: ViewerTools.Viewer;
dummy: IO.STREAM;
name: Rope.ROPE;
viewer ← ViewerTools.FindExistingViewer ["Terminal"];
name ← IF viewer # NIL THEN "Terminal" ELSE "Son of Spinifex";
[in: dummy, out: mLog] ← ViewerIO.CreateViewerStreams [name, viewer]
END; -- StartLog
Msg: PROC [msg: Rope.ROPE] RETURNS [ok: BOOL] ~ BEGIN
This code is in a separate procedure because the server is very fleaky. Once the server is reliable, a process should be forked; this would bring a considerable speed-up.
Note the side-effect implemented by changing the state. The think that it makes the code on the caller side a little bit simpler than it would be by returning a failure result.
sLogProcs: FinchSmarts.Procs ← FinchSmarts.GetProcs [];
IF NOT (sLogProcs.finchIsRunning []) THEN RETURN [FALSE];
sLogProcs.FinchIsRunning is guaranteed to exist if you ran FinchSmartsRegisterImpl.
Moreover, it's reasonable to start from scratch each time. A previous failure does not automatically guarantee you'll fail this time.
RETURN [sLogProcs.textToSpeech [msg] = ok]
END; -- Msg
Debug: PROC ~ BEGIN
Callable from the interpreter. Creates and enebles the degug log.
viewer: ViewerTools.Viewer;
dummy: IO.STREAM;
debug ← TRUE;
viewer ← ViewerTools.FindExistingViewer ["SoS debug"];
[in: dummy, out: dLog] ← ViewerIO.CreateViewerStreams ["SoS debug", viewer];
IF dLog = NIL THEN dLog ← mLog
END; -- Debug
PrintWire: PROC [w: Core.Wire] ~ BEGIN
For debugging purposes.
dLog.PutF ["%g %g %g\n", IO.rope [CoreOps.GetShortWireName[w]], IO.card [LOOPHOLE[w]], IO.refAny [w]];
FOR i: NAT IN [0 .. w.size) DO
dLog.PutF ["\t(%g) %g %g %g\n", IO.card [i], IO.rope [CoreOps.GetShortWireName[w[i]]], IO.card [LOOPHOLE[w[i]]], IO.refAny [w[i]]]
ENDLOOP
END; -- PrintWire
PrintEntryShort: RefTab.EachPairAction ~ BEGIN
[key: Key, val: Val] RETURNS [quit: BOOLEAN]
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]
PrintWire [NARROW [key, Core.Wire]]; PrintWire [NARROW [val, Core.Wire]];
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
IF debug THEN Debug[];
RegisterProperties []; -- CD & Core
RegisterGeomProcs []; -- ChipNDale
RegisterPruningProcs []; RegisterAnalysisProcs []; RegisterSeparationProcs [] -- Core
END.
gbb April 3, 1986 3:21:47 pm PST
Increased extension of error highlight rectangles from 1 to 4 units.
changes to: MarkError
gbb June 5, 1986 7:17:56 pm PDT
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. We cannot skip the verification in the w1 = w2. In fact, in Core and Sinix, cuts are represented as material rectangles in wires. So to SoS two cuts violating the separation rule will alway look as two aequipotential rectangles.
changes to: DIRECTORY: added GetLayer from CDSimpleRules to find the cut layers independently of the technology, CheckDesignRules: added two fields to the state record in order to hold the fileds of material that may not violate the separation rules even if according to the contents of Core it is electrically connected, CheckRecord: each wire is now verified also against himself, MaterialSeparation: the aequipotentiality test has been moved inside the inner loop in order to be able to combine it with a "not okIfConnected", NotCuts (local of MaterialSeparation): new.
gbb June 9, 1986 6:02:28 pm PDT
Made more robust for the case of a missing design.
changes to: CheckDesignRules: added new parameter technology, GetObject: detect design=NIL
gbb June 10, 1986 10:07:56 am PDT
Tracked change in Sinix
changes to: ClearErrors: uses heuristics to find corresponding ChipNDale cell
gbb June 14, 1986 4:22:26 pm PDT
Added debugging aids to exceptions
changes to: EXPORTS, CheckDesignRules, CheckRecord, CreateBindingTable, MaterialToRecordCellSeparation, DIRECTORY, IMPORTS, FindGeometry, FindCDRect, Send (local of FindCDRect), FindRectGeom, MarkError, PrintChecked, StartLog, Msg1, MsgF, Debug
gbb June 15, 1986 10:53:50 am PDT
Added some piggy heuristics to avoid flagging large split contacts that may occur in CMOS-B designs.
changes to: WidthCheck
gbb June 15, 1986 11:49:06 am PDT
If SoS is asked to be shy and the appropriate software is loaded, SoS never writes anything on the screen. If the software is not there it uses the screen anyway to save those preciuos 23 GFIs.
changes to: DIRECTORY, IMPORTS, EXPORTS, CheckDesignRules, Send (local of FindCDRect), CheckRecord, CreateBindingTable, WidthCheck, MaterialToRecordCellSeparation, MarkError, StartLog, Msg
gbb June 16, 1986 1:13:56 pm PDT
Cleaned up client interface before casting in concrete.
changes to: DIRECTORY, EXPORTS, CheckDesignRules, CheckRecord, AtomicWireHull, CreateBindingTable, WidthCheck, NotCuts (local of MaterialSeparation), MaterialSeparation, MaterialToRecordCellSeparation, MarkError, StartLog, Msg