SinixHighlightImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Jean-Marc Frailong May 30, 1986 11:00:50 pm PDT
Bertrand Serlet June 1, 1986 3:42:11 pm PDT
DIRECTORY
CD, CDCells, CDDirectory, CDInstances, CDMenus, CDOps, CDProperties, CDRects, CDSequencer, CDViewer, CDViewHighlight,
Core, CoreClasses, CoreOps, CoreProperties,
HashTable,
IO,
Rope, Sinix, SinixHighlight,
TerminalIO, ViewerClasses;
SinixHighlightImpl: CEDAR PROGRAM
IMPORTS CDCells, CDDirectory, CDInstances, CDMenus, CDOps, CDProperties, CDRects, CDViewer, CDViewHighlight, CoreClasses, CoreOps, CoreProperties, HashTable, IO, Sinix, TerminalIO
EXPORTS SinixHighlight =
BEGIN
OPEN Core, SinixHighlight;
WireSet: TYPE ~ Core.Wires;
inboundProp: ATOM ← CoreProperties.RegisterProperty[$SinixInBinding, CoreProperties.Props[[CoreProperties.propPrint, CoreProperties.PropDontPrint]]];
outboundProp: ATOM ← CoreProperties.RegisterProperty[$SinixOutBinding, CoreProperties.Props[[CoreProperties.propPrint, CoreProperties.PropDontPrint]]];
Wire utilities
Add a wire to a set of wires.
AddWireToSet: PROC [w: Wire, set: WireSet] RETURNS [WireSet] ~ INLINE {
RETURN [IF CoreOps.Member[set, w] THEN set ELSE CONS[w, set]];
};
Merge a set of wires into another set of wires
MergeWiresSets: PROC [set, into: WireSet] RETURNS [WireSet]~ {
WHILE set#NIL DO
into ← AddWireToSet[set.first, into];
set ← set.rest;
ENDLOOP;
RETURN [into];
};
Return all the atomic wires referred to in the set uniquely
FlattenWires: PROC [in: WireSet] RETURNS [out: WireSet ← NIL] ~ {
ForEachAtomicWire: PROC [wire: Wire] ~ { out ← AddWireToSet[wire, out]; };
WHILE in#NIL DO
wire: Wire = in.first;
IF wire.size=0 THEN out ← AddWireToSet[wire, out]
ELSE [] ← CoreOps.VisitAtomicWires[wire, ForEachAtomicWire];
in ← in.rest;
ENDLOOP;
};
Enumerate the parents relative to root of all elements of the list
FindParents: PROC [set: WireSet, root: Wire] RETURNS [parents: WireSet ← NIL] ~ {
AddIfParent: PROC [wire: Wire] RETURNS [isParent: BOOLFALSE] ~ {
IF wire.size=0 THEN RETURN [CoreOps.Member[set, wire]];
FOR i: INT IN [0 .. wire.size) DO
IF AddIfParent[wire.elements[i]] THEN isParent ← TRUE;
ENDLOOP;
IF isParent THEN parents ← AddWireToSet[wire, parents];
};
[] ← AddIfParent[root];
};
Create the hash tables that gives the match from actual to public and back. The wires are assumed to match...
CreateBindingTables: PROC [inst: CoreClasses.CellInstance] RETURNS [inBound, outBound: HashTable.Table] ~ {
EachPair: CoreOps.EachWirePairProc ~ {
wires: Wires;
[] ← HashTable.Insert[table: outBound, key: publicWire, value: actualWire];
wires ← NARROW [HashTable.Fetch[table: inBound, key: actualWire].value];
IF NOT CoreOps.Member[wires, actualWire] THEN wires ← CONS [publicWire, wires];
[] ← HashTable.Store[table: inBound, key: actualWire, value: wires];
};
actual: Wire = inst.actual; public: Wire = inst.type.public;
inBound ← HashTable.Create[];
outBound ← HashTable.Create[];
FOR i: NAT IN [0 .. actual.size) DO
[] ← CoreOps.VisitBinding[actual: actual.elements[i], public: public.elements[i], eachWirePair: EachPair];
ENDLOOP;
CoreProperties.PutCellInstanceProp[on: inst, prop: inboundProp, value: inBound];
CoreProperties.PutCellInstanceProp[on: inst, prop: outboundProp, value: outBound];
};
Propagate a set of wires into a core instance
WireSetIntoInstance: PROC [out: WireSet, inst: CoreClasses.CellInstance] RETURNS [in: WireSet ← NIL] ~ {
table: HashTable.Table ← NARROW [CoreProperties.GetCellInstanceProp[from: inst, prop: inboundProp]];
IF table=NIL THEN table ← CreateBindingTables[inst].inBound;
WHILE out#NIL DO
found: BOOLEAN; binding: HashTable.Value;
[found, binding] ← HashTable.Fetch[table: table, key: out.first];
IF found THEN FOR wires: Wires ← NARROW[binding], wires.rest WHILE wires#NIL DO
in ← AddWireToSet[wires.first, in];
ENDLOOP;
out ← out.rest;
ENDLOOP;
};
Propagate a set of wires from a core instance
WireSetFromInstance: PROC [in: WireSet, inst: CoreClasses.CellInstance] RETURNS [out: WireSet ← NIL] ~ {
table: HashTable.Table ← NARROW [CoreProperties.GetCellInstanceProp[from: inst, prop: outboundProp]];
IF table=NIL THEN table ← CreateBindingTables[inst].outBound;
WHILE in#NIL DO
found: BOOLEAN; binding: HashTable.Value;
[found, binding] ← HashTable.Fetch[table: table, key: in.first];
IF found THEN out ← AddWireToSet[NARROW[binding], out];
in ← in.rest;
ENDLOOP;
};
CD coordinates utilities
Check that two CD instances are logically the same
SameCDInstance: PROC [i,j: CD.Instance] RETURNS [BOOL] ~ INLINE {
RETURN [i.ob=j.ob AND i.location=j.location AND i.orientation=j.orientation]
};
Given inInst and outInst in world coordinates, return instance of inInst if it were in outInst. The basics code for this is found in CDInstances.DeComposed
RelocateInInstance: PROC [inInst, outInst: CD.Instance] RETURNS [CD.Instance] ~ {
RETURN [CDInstances.DeComposed[inst: inInst, cellPos: outInst.location, cellSize: outInst.ob.size, cellOrient: outInst.orientation]];
};
Links between Core and ChipNDale
Given in, an instance in out, recover the celltype for out and the right instance for in in it.
InstanceInRecordCell: PROC [in, out: CD.Instance, mode: Sinix.Mode] RETURNS [coreInst: CoreClasses.CellInstance ← NIL, cell: CellType ← NIL] ~ {
record: CoreClasses.RecordCellType;
wire: Wire; category: ATOM;
[cell, wire, category] ← Extract[out.ob, mode];
IF cell=NIL OR cell.class#CoreClasses.recordCellClass THEN BEGIN
TerminalIO.WriteF["*** Cell %g does not extract to record cell\n", IO.rope[CDDirectory.Name[out.ob]]];
RETURN;
END;
record ← NARROW [cell.data];
FOR i: INT IN [0 .. record.size) DO
possibleInst: CD.Instance = Sinix.GetInstanceTransformationProp[mode, record[i]];
IF SameCDInstance[possibleInst, in] THEN {coreInst ← record[i]; EXIT};
ENDLOOP;
};
This is a local version of Sinix.Extract that always gets value from the cache, as the top cell is supposed to have been extracted.
Extract: PROC [obj: CD.Object, mode: Sinix.Mode] RETURNS [cellType: CellType ← NIL, wire: Wire ← NIL, category: ATOM ← $Device] ~ {
ref: REF ← CDProperties.GetObjectProp[obj, mode.cacheProp];
WITH ref SELECT FROM
ct: CellType => cellType ← ct;
w: Wire => {wire ← w; category ← $Wire};
ENDCASE => ERROR;
};
Generation of highlighted objects
Convert object to highlighted object
HighLightOb: PROC [ob: CD.Object] RETURNS [hob: CD.Object] = {
SELECT ob.class.objectType FROM
$Rect  => hob ← CDRects.CreateRect[ob.size, CD.shadeLayer];
$Cell  => {
cellPtr, hcellPtr: CD.CellPtr;
hob ← CDCells.CreateEmptyCell[];
cellPtr ← NARROW [ob.specificRef];
hcellPtr ← NARROW [hob.specificRef];
hcellPtr.contents ← HighLightListInst[cellPtr.contents];
hob.size ← ob.size;
};
ENDCASE => {
hob ← CDDirectory.Expand[ob, NIL, NIL].new;
IF hob=NIL THEN RETURN[CDRects.CreateRect[ob.size, CD.undefLayer]];
hob ← HighLightOb[hob];
};
};
Convert instance to highlighted instance
HighLightInst: PROC [inst: CD.Instance] RETURNS [hinst: CD.Instance] = {
hinst ← NEW [CD.InstanceRep ← [
ob: IF inst.ob.class.objectType=$PinOb0 THEN CDRects.CreateRect[inst.ob.size, CD.shadeLayer] ELSE HighLightOb[inst.ob],
location: inst.location, orientation: inst.orientation]];
};
Convert instance list to highlighted instance list
HighLightListInst: PROC [instances: LIST OF CD.Instance] RETURNS [hlist: LIST OF CD.Instance ← NIL] = {
WHILE instances#NIL DO
hlist ← CONS[HighLightInst[instances.first], hlist]; instances ← instances.rest;
ENDLOOP;
};
Create a single object relative to an instance that contains a list of highlighted instances
CreateHighlightInstance: PROC [hinsts: CD.InstanceList, in: CD.Instance] RETURNS [highlight: CD.Instance ← NIL] ~ {
IF hinsts#NIL THEN BEGIN
hob: CD.Object ← CDCells.CreateEmptyCell[];
hcellPtr: CD.CellPtr ← NARROW [hob.specificRef];
hob.size ← in.ob.size;
hcellPtr.contents ← hinsts;
hcellPtr.simplifyOn ← 0.0;
highlight ← NEW [CD.InstanceRep ← [ob: hob, location: in.location, orientation: in.orientation, properties: NIL]];
END;
};
Locating selected things
Return all instances that are selected in the topmost pushed-in instance, in its local reference frame. Actually, only a copy of the instances is returned as cells pushed-in change their coordinate system in CD... If the cell has been modified, you may get garbage.
SelectedInstancesInTopPushedInstance: PROC [design: CD.Design] RETURNS [selectedList: CD.InstanceList ← NIL] ~ {
closed: CD.Instance = design.actual.first.mightReplace;
open: CD.Instance = design.actual.first.dummyCell;
IF design.actual.rest=NIL THEN RETURN;
FOR list: CD.InstanceList ← NARROW [open.ob.specificRef, CD.CellPtr].contents, list.rest WHILE list#NIL DO
IF list.first.selected THEN
selectedList ← CONS [RelocateInInstance[list.first, closed], selectedList];
ENDLOOP;
};
For all the selected instances in the currently pushed-in CD instance, find all wires that touch and return the set. For instances that extract to wire, explore against all wires internal to the cell to find the matching ones. For instances that extract to a device, include all the actuals for the instance.
SelectedWires: PROC [mode: Sinix.Mode, design: CD.Design] RETURNS [cellType: CellType, wires: WireSet] ~ {
top: CD.Instance = design.actual.first.mightReplace;
wireList: WireSet ← NIL;
dummyWire: Wire; dummyCellType: CellType; category: ATOM;
rec: CoreClasses.RecordCellType;
[cellType, dummyWire, category] ← Extract[top.ob, mode];
IF cellType=NIL OR cellType.class#CoreClasses.recordCellClass THEN BEGIN
TerminalIO.WriteF["*** Extraction must return a record cell\n"];
RETURN;
END;
rec ← NARROW [cellType.data];
FOR selectedList: CD.InstanceList ← SelectedInstancesInTopPushedInstance[design], selectedList.rest WHILE selectedList#NIL DO
[dummyCellType, dummyWire, category] ← Extract[selectedList.first.ob, mode];
SELECT category FROM
$Wire => BEGIN -- Relocate the wire geometry inside the instance, connect to internals
TouchesWire: CoreOps.EachWireProc ~ {
FOR client: CD.InstanceList ← Sinix.GetWireGeometryProp[mode,wire], client.rest WHILE client#NIL DO
FOR list: CD.InstanceList ← target, list.rest WHILE list#NIL DO
IF SameCDInstance[list.first, client.first] THEN {
wireList ← AddWireToSet[wire, wireList];
RETURN [quit: TRUE];
};
ENDLOOP;
ENDLOOP;
};
target: CD.InstanceList ← CDInstances.ComposedList[
il: NARROW [Sinix.GetPinsProp[mode, dummyWire]],
cellPos: selectedList.first.location,
cellSize: selectedList.first.ob.size,
cellOrient: selectedList.first.orientation];
[] ← CoreOps.VisitWire[rec.internal, TouchesWire];
END;
$Device => BEGIN -- Add all the actuals to the list of selected wires
sel: CoreClasses.CellInstance;
cell: CellType;
[sel, cell] ← InstanceInRecordCell[in: selectedList.first, out: top, mode: mode];
IF sel#NIL THEN FOR i: NAT IN [0 .. sel.actual.size) DO
wireList ← AddWireToSet[sel.actual.elements[i], wireList];
ENDLOOP;
END;
ENDCASE => {};
ENDLOOP;
wires ← FlattenWires[wireList];
};
Highlight wires top-down
Add to hinsts the highlight for all the requested wires, return new highlight list.
HighlightWires: PROC [wires: WireSet, hinsts: CD.InstanceList, mode: Sinix.Mode] RETURNS [CD.InstanceList] ~ {
WHILE wires#NIL DO
FOR l: CD.InstanceList ← Sinix.GetWireGeometryProp[mode, wires.first], l.rest WHILE l#NIL DO
hinsts ← CONS [HighLightInst[l.first], hinsts];
ENDLOOP;
wires ← wires.rest;
ENDLOOP;
RETURN [hinsts];
};
Add to hinsts the highlight for all the requested pins, return new highlight list.
HighlightPins: PROC [wires: WireSet, hinsts: CD.InstanceList, mode: Sinix.Mode] RETURNS [CD.InstanceList] ~ {
WHILE wires#NIL DO
FOR l: CD.InstanceList ← Sinix.GetPinsProp[mode, wires.first], l.rest WHILE l#NIL DO
hinsts ← CONS [HighLightInst[l.first], hinsts];
ENDLOOP;
wires ← wires.rest;
ENDLOOP;
RETURN [hinsts];
};
Return a CD Instance that is a highlight for the given list of wires in the cellType, which is exactly the extraction of the cdInst. Everything is explored downwards, except for the special instance cell from which the net originated (if applicable). Hinsts is a list of pre-built highlighted instances to be included in the final object. The CD instance is used to remap the highlighted object at the right location.
HighlightWiresDownwards: PROC [cdInst: CD.Instance, cell: CellType, wires: WireSet, except: CoreClasses.CellInstance ← NIL, hinsts: CD.InstanceList ← NIL, mode: Sinix.Mode] RETURNS [highlight: CD.Instance ← NIL] ~ {
IF wires#NIL THEN DO
SELECT cell.class FROM
CoreClasses.recordCellClass => {
rec: CoreClasses.RecordCellType ← NARROW[cell.data];
-- Highlight in included cells
FOR i: NAT IN [0 .. rec.size) DO
coreInst: CoreClasses.CellInstance = rec.instances[i];
IF coreInst#except THEN BEGIN
highlight: CD.Instance = HighlightWiresDownwards[
cdInst: Sinix.GetInstanceTransformationProp[mode, coreInst],
cell: coreInst.type,
wires: WireSetIntoInstance[wires, coreInst],
mode: mode];
IF highlight#NIL THEN hinsts ← CONS[highlight, hinsts];
END;
ENDLOOP;
-- Highlight atomic wires
hinsts ← HighlightWires[wires, hinsts, mode];
-- Highlight parents in record cell
hinsts ← HighlightWires[FindParents[wires, rec.internal], hinsts, mode];
EXIT;
};
CoreClasses.transistorCellClass,
CoreClasses.identityCellClass => {
hinsts ← HighlightPins[wires, hinsts, mode];
EXIT;
};
ENDCASE => cell ← CoreOps.Recast[cell];
ENDLOOP;
highlight ← CreateHighlightInstance[hinsts, cdInst];
};
Propagate selected wires to the top and highlight
Given the stack and a list of wires in the topmost cell, highlight locally then proceed upwards to obtain finally the completely highlighted net. As wire connectivity is computed by Sinix only on a bottom-up basis, bottom-up exploration does not work straightforwardly: wires that are connected only through an enclosing cell will not be detected correctly! Hence, the process consists of computing wire propagation to the top, then going down to merge propagations downwards, finally redisplaying up...
HighlightWiresUpwards: PROC [stack: LIST OF CD.PushRec, cell: CellType, wires: WireSet, mode: Sinix.Mode] RETURNS [highlight: CD.Instance] ~ {
HighlightContext: TYPE ~ REF HighlightContextRep;
HighlightContextRep: TYPE ~ RECORD [
from: CoreClasses.CellInstance,
in: CD.Instance,
cell: CellType,
wires: WireSet];
request, initialRequest: LIST OF HighlightContext;
First, build initialRequest by unwinding the CD stack
currentInst: CD.Instance ← stack.first.mightReplace;
stack ← stack.rest;
initialRequest ← LIST [NEW[HighlightContextRep ← [NIL, currentInst, cell, wires]]];
WHILE stack.rest#NIL DO
previousInst: CD.Instance = currentInst;
previousCoreInst: CoreClasses.CellInstance;
currentInst ← stack.first.mightReplace;
[previousCoreInst, cell] ← InstanceInRecordCell[
in: RelocateInInstance[previousInst, currentInst],
out: currentInst,
mode: mode];
IF previousCoreInst=NIL THEN RETURN [NIL];
wires ← WireSetFromInstance[wires, previousCoreInst];
initialRequest ← CONS [
NEW[HighlightContextRep ← [previousCoreInst, currentInst, cell, wires]],
initialRequest];
stack ← stack.rest;
ENDLOOP;
Now, build request from initialRequest by propagating wires down again
request ← NIL;
wires ← NIL;
WHILE initialRequest#NIL DO
currentRequest: HighlightContext = initialRequest.first;
IF wires#NIL THEN currentRequest.wires ← MergeWiresSets[wires, currentRequest.wires];
request ← CONS [currentRequest, request];
wires ← IF currentRequest.from#NIL THEN WireSetIntoInstance[currentRequest.wires, currentRequest.from] ELSE NIL;
initialRequest ← initialRequest.rest;
ENDLOOP;
Finally, do the highlighting from request
highlight ← HighlightWiresDownwards[cdInst: request.first.in, cell: request.first.cell, wires: request.first.wires, mode: mode];
request ← request.rest;
WHILE request#NIL DO
highlight ← HighlightWiresDownwards[
cdInst: request.first.in,
cell: request.first.cell,
wires: request.first.wires,
mode: mode,
except: request.first.from,
hinsts: LIST [RelocateInInstance[highlight, request.first.in]]];
request ← request.rest;
ENDLOOP;
};
The real thing
Go up the stack, extract the topmost instance. From now on, refer to extraction only by using the cache... Return TRUE if in error, FALSE if all is OK.
ExtractOuterInstance: PROC [design: CD.Design, context: ExtractContext] RETURNS [BOOL] ~ {
result: REF;
extractibleInstance: CD.Instance ← NIL;
FOR stack: LIST OF CD.PushRec ← design.actual, stack.rest WHILE stack.rest#NIL DO
IF stack.first.changed THEN {
TerminalIO.WriteF["*** Impossible to highlight, cell %g has been changed and not saved\n", IO.rope[CDDirectory.Name[stack.first.mightReplace.ob]]];
RETURN [TRUE];
};
extractibleInstance ← stack.first.mightReplace;
ENDLOOP;
IF extractibleInstance=NIL THEN {
TerminalIO.WriteF["*** No currently pushed-in cell\n"];
RETURN[TRUE];
};
[result] ← Sinix.Extract[
obj: extractibleInstance.ob,
mode: context.mode,
properties: extractibleInstance.properties,
userData: context.userData];
IF NOT ISTYPE [result, CellType] THEN BEGIN
TerminalIO.WriteF["*** Top cell does not extract to device\n"];
RETURN[TRUE];
END;
RETURN[FALSE];
};
Recover the extract context from the technology, or from the key if not found in technology.
GetExtractContext: PROC [command: CDSequencer.Command] RETURNS [ExtractContext] ~ {
contextCreator: REF ExtractContextCreator;
contextCreator ← NARROW [CDProperties.GetProp[command.design.technology, command.key]];
IF contextCreator=NIL THEN contextCreator ← NARROW [CDProperties.GetProp[$Context, command.key]];
RETURN [IF contextCreator#NIL THEN contextCreator^[command.design] ELSE NIL]
};
DeepHighLightSelected: PROC [command: CDSequencer.Command] ~ {
highlight: CD.Instance;
context: ExtractContext;
selWires: WireSet; inCell: CellType;
mode: Sinix.Mode;
context ← GetExtractContext[command];
IF context=NIL THEN BEGIN
TerminalIO.WriteF["*** Command not supported for this technology\n"];
RETURN;
END;
mode ← context.mode;
IF ExtractOuterInstance[command.design, context] THEN RETURN;
[inCell, selWires] ← SelectedWires[mode, command.design];
IF selWires=NIL THEN BEGIN
TerminalIO.WriteF["*** No wires selected\n"];
RETURN;
END;
highlight ← HighlightWiresUpwards[command.design.actual, inCell, selWires, mode];
IF highlight#NIL THEN FOR viewers: LIST OF ViewerClasses.Viewer ← CDViewer.ViewersOf[command.design], viewers.rest WHILE viewers#NIL DO
CDViewHighlight.ShowInstance[viewers.first, highlight];
ENDLOOP;
};
ExtractSelected: PROC [command: CDSequencer.Command] = {
context: ExtractContext;
first: CD.Instance; multiple: BOOL;
result: REF;
context ← GetExtractContext[command];
[first, multiple] ← CDOps.SelectedInstance[command.design];
IF multiple THEN {TerminalIO.WriteF["Multiple instances selected. No action.\n"]; RETURN};
IF first=NIL THEN {TerminalIO.WriteF["No instance selected. No action.\n"]; RETURN};
[result] ← Sinix.Extract[first.ob, context.mode, first.properties, context.userData];
IF result=NIL
THEN TerminalIO.WriteF["*** Extraction returned NIL\n"]
ELSE CoreOps.Print[result, TerminalIO.TOS[]];
};
Registration
RegisterOtherProgramWithContext: PROC [program: PROC [CDSequencer.Command], technology: CD.Technology, contextCreator: ExtractContextCreator, prompt: Rope.ROPE, key: ATOM] ~ {
[] ← CDProperties.RegisterProperty[key, $HighlightProperty];
CDProperties.PutProp[IF technology#NIL THEN technology ELSE $Context, key, NEW [ExtractContextCreator ← contextCreator]];
CDMenus.ImplementEntryCommand[menu: $OtherProgramMenu, entry: prompt, p: program, key: key, technology: technology];
};
RegisterHighlightCommand: PUBLIC PROC [technology: CD.Technology ← NIL, contextCreator: ExtractContextCreator, prompt: Rope.ROPE, key: ATOM] ~ {
RegisterOtherProgramWithContext[DeepHighLightSelected, technology, contextCreator, prompt, key];
};
RegisterExtractCommand: PUBLIC PROC [technology: CD.Technology ← NIL, contextCreator: ExtractContextCreator, prompt: Rope.ROPE, key: ATOM] ~ {
RegisterOtherProgramWithContext[ExtractSelected, technology, contextCreator, prompt, key];
};
RegisterDefaultLayoutMode: PUBLIC PROC [technology: CD.Technology, contextCreator: ExtractContextCreator] ~ {
IF technology=NIL THEN ERROR;
RegisterExtractCommand[technology, contextCreator, "Extract layout", $ExtractLayout];
RegisterHighlightCommand[technology, contextCreator, "Highlight net in layout", $HighlightNetInLayout];
};
END.