DAUserHacks.mesa
Copyright Ó 1987 by Xerox Corporation. All rights reserved.
Jean-Marc Frailong December 30, 1987 8:46:27 pm PST
Various hacks that are useful debuging tools in some painful cases
DIRECTORY
CD, CDBasics, CDBasicsInline, CDCells, CDCommandOps, CDDirectory, CDImports, CDInstances, CDOps, CDProperties, CDSatellites, CDSequencer, CDViewer, Core, CoreCDUser, CoreClasses, CoreFlat, CoreGeometry, CoreOps, ExtractOps, InstanceTable, IO, PW, PWCore, RefTab, Rope, Sinix, Sisyph, TerminalIO, ViewerClasses, ViewerOps;
DAUserHacks: CEDAR PROGRAM
IMPORTS CD, CDBasics, CDCells, CDCommandOps, CDDirectory, CDImports, CDInstances, CDOps, CDProperties, CDSatellites, CDSequencer, CDViewer, CoreCDUser, CoreClasses, CoreFlat, CoreGeometry, CoreOps, ExtractOps, InstanceTable, IO, PW, PWCore, RefTab, Rope, Sinix, Sisyph, TerminalIO, ViewerOps
~ BEGIN
hackMenu: ATOM ← $SpecialMenu; -- where DAUserHacks commands are forcefully registered
Wire name mismatch helper
This section helps understanding why two wires with different names are fused. It works exclusively if no structured wires are present (i.e. most probably on layout...)
It implements 3 commands, all of which should be called when pushed in the faulty cell:
Select wire roots ==> select all instances that would enfore a given wire name (through CD satellites, instance properties or otherwise).
Grow wire one step ==> assuming that all selected instances are part of a single wire, add to the selection all instances that connect directly to one of the initially selected instances (i.e. a 1-step neighbour operation). Complain loudly if a name inconsistency crops up.
Select wire ==> if nothing is selected, do first a 'Select wire roots' operations. Then repeat a 'Grow wire one step' operation until either nothing more gets connected, or an error occurs.
All the commands assume that instances within the pushed-in cell are themselves extractible correctly.
Instances that extract as wires are treated directly. Instances that extract as celltypes are added only insofar as one of their public wires connects to an already selected wire (independently of names), and only the geometry for such wires is recorded for future conections.
Beware, this is not fast... but it's still much faster than doing it by hand.
Types
CoreCDItem: TYPE ~ RECORD [core: REF ANY, inst: CD.Instance];
Context: TYPE ~ REF ContextRep;
ContextRep: TYPE ~ RECORD [
design: CD.Design ← NIL,
mode: Sinix.Mode ← NIL,
ob: CD.Object, -- the top-level pushed-in object
name: Rope.ROPENIL, -- of current wire, there may be only one
highlight: CD.InstanceList ← NIL,
userData: REF ANYNIL,
rects: SEQUENCE nbOfLayers: NAT OF InstanceTable.Table
];
Context management
NewContext: PROC [design: CD.Design] RETURNS [cx: Context] ~ {
Create a new context object, initialize all fields, enfore all invariants and check that all sub-objects extracts with atomic wires. Returns NIL if something went wrong.
CheckInstance: CDCells.InstEnumerator ~ {
subResult: REF; subProps: Core.Properties;
badInst: BOOLFALSE;
CDProperties.PutInstanceProp[inst, Sinix.satellitesProp, CDSatellites.GetSatelliteRopes[inst]];
[subResult, subProps] ← Sinix.Extract[inst.ob, cx.mode, inst.properties, cx.userData];
IF subResult#NIL THEN WITH subResult SELECT FROM
subWire: Core.Wire => badInst ← badInst OR subWire.size#0;
subWires: Core.Wires => UNTIL subWires=NIL DO
badInst ← badInst OR subWires.first.size#0;
subWires ← subWires.rest;
ENDLOOP;
subCellType: Core.CellType => FOR i: NAT IN [0..subCellType.public.size) DO
badInst ← badInst OR subCellType.public[i].size#0;
ENDLOOP;
ENDCASE => ERROR; -- Extract returned incorrect value...
IF badInst THEN cx.highlight ← CONS [ExtractOps.HighlightInstance[[inst.ob, inst.trans]], cx.highlight];
};
ob: CD.Object = CDOps.PushedTopCell[design];
mode: Sinix.Mode = ExtractOps.GetExtractMode[design, ob];
cx ← NEW [ContextRep[mode.nbOfLayers]];
cx.design ← design;
cx.ob ← ob;
IF CDOps.RealTopCell[design]=ob THEN ERROR; -- not pushed into a cell
[] ← CDSatellites.GetSatellites[ob]; -- enforce satellites links !!!
cx.mode ← mode;
cx.userData ← IF mode.userData#NIL THEN mode.userData[design] ELSE NIL;
[] ← CDCells.EnumerateInstances[ob, CheckInstance];
ExtractOps.HighlightDesignList[cx.design, cx.highlight]; -- in any case
IF cx.highlight#NIL THEN { -- something went wrong
TerminalIO.PutF["Highlighted instances have structured wires, not supported...\n"];
cx ← NIL; -- notifies of error return
};
};
DestroyContext: PROC [cx: Context] ~ {
Cleanup context data structure
FOR i: NAT IN [0..cx.mode.nbOfLayers) DO
InstanceTable.DeleteOutside[cx.rects[i]];
ENDLOOP;
};
Select: PROC [inst: CD.Instance, cx: Context] RETURNS [new: BOOL] ~ {
Select instance and add it to context if not already selected
IF inst.selected THEN RETURN [new: FALSE];
inst.selected ← TRUE;
CDOps.RedrawInstance[cx.design, inst];
RETURN [new: TRUE];
};
CheckName: PROC [wire: Core.Wire, trans: CoreGeometry.Transformation, cx: Context] RETURNS [ok: BOOL] ~ {
wireName: Rope.ROPE = CoreOps.GetShortWireName[wire];
IF cx.name=NIL THEN cx.name ← wireName;
ok ← wireName=NIL OR Rope.Equal[cx.name, wireName];
IF NOT ok THEN {
TerminalIO.PutF["Wire named %g connected to %g\n", IO.rope[CoreOps.GetShortWireName[wire]], IO.rope[cx.name]];
FOR pins: CoreGeometry.Instances ← CoreGeometry.GetPins[cx.mode.decoration, wire], pins.rest UNTIL pins=NIL DO
pin: CoreGeometry.Instance = CoreGeometry.Transform[trans, pins.first];
cx.highlight ← CONS [ExtractOps.HighlightInstance[pin], cx.highlight];
ENDLOOP;
};
};
AddGeometry: PROC [wire: Core.Wire, trans: CoreGeometry.Transformation, cx: Context] ~ {
FOR pins: CoreGeometry.Instances ← CoreGeometry.GetPins[cx.mode.decoration, wire], pins.rest UNTIL pins=NIL DO
pin: CoreGeometry.Instance = CoreGeometry.Transform[trans, pins.first];
layers: Sinix.LayerRange = cx.mode.instanceLayer[pin];
FOR i: NAT IN [layers.min .. layers.max] DO
InstanceTable.Insert[cx.rects[i], pin, NIL];
ENDLOOP;
ENDLOOP;
};
Connects: PROC [wire: Core.Wire, trans: CoreGeometry.Transformation, cx: Context] RETURNS [connected: BOOLFALSE] ~ {
FOR pins: CoreGeometry.Instances ← CoreGeometry.GetPins[cx.mode.decoration, wire], pins.rest UNTIL pins=NIL DO
CheckOne: PROC [instance: CoreGeometry.Instance, value: InstanceTable.Value] ~ {
IF cx.mode.touchProc[cx.mode.touchProc, instance, pin] THEN connected ← TRUE;
};
pin: CoreGeometry.Instance = CoreGeometry.Transform[trans, pins.first];
layers: Sinix.LayerRange = cx.mode.instanceLayer[pin];
rect: CD.Rect = CoreGeometry.InlineBBox[pin];
FOR i: NAT IN [layers.min .. layers.max] DO
InstanceTable.Enumerate[cx.rects[i], CheckOne, rect];
ENDLOOP;
ENDLOOP;
};
SelectionFromName: PROC [cx: Context] ~ {
Initialize the context from all instances that carry a wire with the specified cx.name
SelectIfNamed: CDCells.InstEnumerator ~ {
newSel: BOOLFALSE;
subResult: REF; subProps: Core.Properties;
[subResult, subProps] ← Sinix.Extract[inst.ob, cx.mode, inst.properties, cx.userData];
IF subResult#NIL THEN WITH subResult SELECT FROM
subWire: Core.Wire => IF Rope.Equal[CoreOps.GetShortWireName[subWire], cx.name] THEN newSel ← TRUE;
subWires: Core.Wires => UNTIL subWires=NIL DO
IF Rope.Equal[CoreOps.GetShortWireName[subWires.first], cx.name] THEN newSel ← TRUE;
subWires ← subWires.rest;
ENDLOOP;
subCellType: Core.CellType => NULL; -- never forces a wire name
ENDCASE => ERROR; -- Extract returned incorrect value...
IF inst.selected#newSel THEN {
inst.selected ← newSel;
CDOps.RedrawInstance[cx.design, inst];
};
IF inst.selected THEN newSels ← newSels+1;
};
newSels: INT ← 0;
cx.name ← NIL;
WHILE Rope.IsEmpty[cx.name] DO
cx.name ← TerminalIO.RequestRope["Wire name:"];
ENDLOOP;
[] ← CDCells.EnumerateInstances[cx.ob, SelectIfNamed];
CDOps.DoTheDelayedRedraws[cx.design]; -- to show progressive selection
TerminalIO.PutF["Selected %g instance(s) specifying wire name '%g'\n", IO.int[newSels], IO.rope[cx.name]];
};
InitializeContext: PROC [cx: Context] RETURNS [failed: BOOL] ~ {
Initialize the context with the set of currently selected instances
EachInstance: CDCells.InstEnumerator ~ {
subResult: REF; subProps: Core.Properties;
IF NOT inst.selected THEN RETURN; -- not interesting
[subResult, subProps] ← Sinix.Extract[inst.ob, cx.mode, inst.properties, cx.userData];
IF subResult#NIL THEN WITH subResult SELECT FROM
subWire: Core.Wire => IF CheckName[subWire, inst.trans, cx] THEN AddGeometry[subWire, inst.trans, cx];
subWires: Core.Wires => UNTIL subWires=NIL DO
IF CheckName[subWires.first, inst.trans, cx] THEN AddGeometry[subWires.first, inst.trans, cx];
subWires ← subWires.rest;
ENDLOOP;
subCellType: Core.CellType => pending ← CONS [[subCellType, inst], pending];
ENDCASE => ERROR; -- Extract returned incorrect value...
};
pending: LIST OF CoreCDItem ← NIL;
FOR i: NAT IN [0..cx.mode.nbOfLayers) DO
cx.rects[i] ← InstanceTable.Create[range: cx.ob.bbox, logSize: 8];
ENDLOOP;
[] ← CDCells.EnumerateInstances[cx.ob, EachInstance];
UNTIL pending=NIL DO -- cells are handled in a delayed fashion...
cell: Core.CellType = NARROW [pending.first.core];
inst: CD.Instance = pending.first.inst;
pending ← pending.rest;
FOR i: NAT IN [0..cell.public.size) DO
IF Connects[cell.public[i], inst.trans, cx] THEN AddGeometry[cell.public[i], inst.trans, cx];
ENDLOOP;
ENDLOOP;
ExtractOps.HighlightDesignList[cx.design, cx.highlight]; -- in any case
failed ← cx.highlight#NIL;
IF failed THEN TerminalIO.PutF["Wire specification has conflicting names\n"];
};
GrowContext: PROC [cx: Context] RETURNS [finished: BOOL] ~ {
Takes current information from context & extend object by a single connection step
InstanceTables are assumed to contain all the geometry for selected instances. Upon return, new instances may have ben selected, the InstanceTables will contain all the geometry for selected instances, cx.highlight will be non-NIL if something went wrong (but the highlight has not been done yet). May raise the signal Bug if an instance does something wrong (e.g. having non-atomic wires). Returns TRUE iff no need to call again (error or nothing added).
EachInstance: CDCells.InstEnumerator ~ {
AddIfOK: PROC [wire: Core.Wire, named: BOOL] ~ {
IF wire.size#0 THEN ERROR; -- should have been checked earlier ...
IF inst.selected OR NOT Connects[wire, inst.trans, cx] THEN RETURN;
IF named AND NOT CheckName[wire, inst.trans, cx] THEN RETURN;
addToSet ← CONS [[wire, inst], addToSet];
};
subResult: REF; subProps: Core.Properties;
IF inst.selected THEN RETURN;
[subResult, subProps] ← Sinix.Extract[inst.ob, cx.mode, inst.properties, cx.userData];
IF subResult#NIL THEN WITH subResult SELECT FROM
subWire: Core.Wire => AddIfOK[subWire, TRUE];
subWires: Core.Wires => UNTIL subWires=NIL DO
AddIfOK[subWires.first, TRUE];
subWires ← subWires.rest;
ENDLOOP;
subCellType: Core.CellType => FOR i: NAT IN [0..subCellType.public.size) DO
AddIfOK[subCellType.public[i], FALSE];
ENDLOOP;
ENDCASE => ERROR; -- Extract returned incorrect value...
};
addToSet: LIST OF CoreCDItem ← NIL;
newSels: INT ← 0;
[] ← CDCells.EnumerateInstances[cx.ob, EachInstance];
UNTIL addToSet=NIL DO
wire: Core.Wire = NARROW [addToSet.first.core];
inst: CD.Instance = addToSet.first.inst;
addToSet ← addToSet.rest;
IF Select[inst, cx] THEN newSels ← newSels+1;
AddGeometry[wire, inst.trans, cx];
ENDLOOP;
ExtractOps.HighlightDesignList[cx.design, cx.highlight]; -- in any case
CDOps.DoTheDelayedRedraws[cx.design]; -- to show progressive selection
SELECT TRUE FROM
newSels#0 => TerminalIO.PutF["Added %g instance(s) to wire '%g'\n", IO.int[newSels], IO.rope[IF Rope.IsEmpty[cx.name] THEN "<Unnamed>" ELSE cx.name]];
cx.highlight=NIL => TerminalIO.PutF["No more instances to add to wire '%g'\n", IO.rope[IF Rope.IsEmpty[cx.name] THEN "<Unnamed>" ELSE cx.name]];
ENDCASE => NULL;
IF cx.highlight#NIL THEN TerminalIO.PutF["Incorrect connections highlighted\n"];
finished ← cx.highlight#NIL OR newSels=0;
};
Commands
DoSelectWireRoots: PROC [comm: CDSequencer.Command] ~ {
cx: Context;
newSels: INT ← 0;
cx ← NewContext[comm.design];
IF cx=NIL THEN RETURN;
SelectionFromName[cx];
};
DoGrowWireOneStep: PROC [comm: CDSequencer.Command] ~ {
cx: Context;
cx ← NewContext[comm.design];
IF cx=NIL OR InitializeContext[cx] THEN RETURN;
[] ← GrowContext[cx];
};
DoGrowWireToMax: PROC [comm: CDSequencer.Command] ~ {
cx: Context;
cx ← NewContext[comm.design];
IF cx=NIL THEN RETURN;
IF CDInstances.OnlySelected[CDOps.InstList[cx.design]]=NIL THEN SelectionFromName[cx];
IF InitializeContext[cx] THEN RETURN;
UNTIL GrowContext[cx] DO NULL ENDLOOP;
};
Layout sanity checker
This section may be used as a preliminary pass to Lichen for large cells, in order to discover trivial errors that would make Lichen more confused. The following tests are performed:
- Transistors in which connections are not all different
- Transistor gates connected to Gnd/Vdd
- p-transistor well must be connected to Vdd
None of these tests is really an error, they just seem to be strange...
Effective code
SanityCheck: PROC [ct: Core.CellType] ~ {
The technique consists in doing a CoreFlat enumeration of the connectivity and building a connection table transistor -> canonical flat wires.
Explore: CoreFlat.BoundFlatCellProc ~ {
IF cell.class=CoreClasses.transistorCellClass THEN { -- note transistor and its connections
Check: PROC [p1, p2: CoreClasses.TransistorPort] ~ {
Check that the two specified terminals are not connected together
fw1: CoreFlat.FlatWire = NARROW [RefTab.Fetch[bindings, public[p1.ORD]].val];
fw2: CoreFlat.FlatWire = NARROW [RefTab.Fetch[bindings, public[p2.ORD]].val];
IF fw1.wire=fw2.wire THEN TerminalIO.PutF["Transistor %g: %g and %g connected together\n", IO.rope[CoreFlat.CellTypePathRope[ct, flatCell]], IO.rope[transistorNames[p1]], IO.rope[transistorNames[p2]]];
};
CheckPower: PROC [p: CoreClasses.TransistorPort, to: Core.Wire, msg: Rope.ROPE] ~ {
Check that the specified terminal is not connected to power
fw: CoreFlat.FlatWire = NARROW [RefTab.Fetch[bindings, public[p.ORD]].val];
IF fw#NIL AND fw.wire=to THEN TerminalIO.PutF["Transistor %g: %g connected to %g\n", IO.rope[CoreFlat.CellTypePathRope[ct, flatCell]], IO.rope[transistorNames[p]], IO.rope[msg]];
};
transistor: CoreClasses.Transistor = NARROW [cell.data];
public: Core.Wire = cell.public;
Check[gate, ch1]; -- check source, drain & gate all different
Check[gate, ch2];
Check[ch1, ch2];
IF vdd#NIL THEN CheckPower[gate, vdd, "Vdd"];
IF gnd#NIL THEN CheckPower[gate, gnd, "Gnd"];
IF vdd#NIL AND transistor.type=pE THEN {
well: CoreFlat.FlatWire = NARROW [RefTab.Fetch[bindings, public[vddPos]].val];
IF well.wire#vdd THEN TerminalIO.PutF["Transistor %g: Well is not connected to Vdd\n", IO.rope[CoreFlat.CellTypePathRope[ct, flatCell]]];
};
}
ELSE CoreFlat.NextBoundCellType[cell, target, flatCell, instance, index, parent, flatParent, data, bindings, Explore];
};
transistorNames: ARRAY CoreClasses.TransistorPort OF Rope.ROPE ← [ch1: "ch1", ch2: "ch2", gate: "gate", Vdd: "Vdd"]; -- for printing
vdd: Core.Wire = CoreOps.FindWire[ct.public, "Vdd"];
gnd: Core.Wire = CoreOps.FindWire[ct.public, "Gnd"];
vddPos: NAT = CoreClasses.TransistorPort.Vdd.ORD;
IF vdd=NIL THEN TerminalIO.PutF["Vdd is not a public of the cell...\n"];
IF gnd=NIL THEN TerminalIO.PutF["Gnd is not a public of the cell...\n"];
Explore[cell: ct];
};
Command
DoSanityCheck: PROC [comm: CDSequencer.Command] ~ {
EachCell: CoreCDUser.EachRootCellTypeProc ~ {
TerminalIO.PutF["DAUserHacks: Sanity check on %g\n", IO.rope[CoreOps.GetCellTypeName[root]]];
SanityCheck[root];
TerminalIO.PutF["DAUserHacks: Done\n"];
};
[] ← CoreCDUser.EnumerateSelectedCellTypes[comm.design, EachCell];
};
Who uses an object
Return the list of 1st-order ancestors of a given object. Useful to know who will be disturbed if an object is modified...
Effective code
WhoUses: PROC [obj: CD.Object, design: CD.Design] ~ {
Print the list of objects that include this guy at 1st level
Describe: PROC [me: CD.Object] RETURNS [id: Rope.ROPE] ~ {
id ← CDDirectory.Name[me, design];
IF Rope.Length[id]=0 THEN id ← CD.Describe[ob: me, design: design];
};
EachObject: CDDirectory.EachObjectProc ~ {
PROC [me: CD.Object, data: REFNIL] RETURNS [quit: BOOLFALSE]
EachInstance: CDCells.InstEnumerator ~ {
PROC [inst: CD.Instance] RETURNS [quit: BOOLFALSE]
IF inst.ob=obj THEN {
TerminalIO.PutF[" %g", IO.rope[Describe[me]]];
nOccurrences ← nOccurrences+1;
quit ← TRUE; -- stop enumerating children of me
};
};
IF CDCells.IsCell[me] THEN { -- only interesting case...
[] ← CDCells.EnumerateInstances[me, EachInstance];
};
};
nOccurrences: NAT ← 0;
TerminalIO.PutF["%g is used in cells", IO.rope[Describe[obj]]];
[] ← CDDirectory.EnumerateDesign[design: design, proc: EachObject];
TerminalIO.PutF[" (%g occurences)\n", IO.int[nOccurrences]];
};
Command
DoWhoUses: PROC [comm: CDSequencer.Command] ~ {
selected: CD.InstanceList ← CDInstances.OnlySelected[CDOps.InstList[comm. design]];
IF selected#NIL THEN {
FOR il: CD.InstanceList ← selected, il.rest UNTIL il=NIL DO
WhoUses[il.first.ob, comm.design];
ENDLOOP;
}
ELSE {
name: Rope.ROPENIL;
obj: CD.Object ← NIL;
name ← TerminalIO.RequestRope[prompt: "Search who uses cell "];
obj ← CDDirectory.Fetch[comm.design, name];
IF obj=NIL THEN TerminalIO.PutF["Not found\n"] ELSE WhoUses[obj, comm.design];
};
};
Usable layout
Generate the layout for an object, recast all non-cells to cells and include the result into the well-know design "LayoutWorkArea".
Same function provided for layout shells.
Effective code
cellVisibility: REAL ← 100.0;
FlatObject: TYPE ~ REF FlatObjectRep;
FlatObjectRep: TYPE ~ RECORD [obj: CD.Object, orientation: CD.Orientation];
FlattenLayout: PROC [initial: CD.Object, cache: RefTab.Ref, into: CD.Design] RETURNS [newObj: CD.Object, newOrient: CD.Orientation] ~ {
newObj ← initial; newOrient ← original;
IF newObj.class.composed THEN {
EachInstance: CDCells.InstEnumerator ~ {
subOrient: CD.Orientation;
[inst.ob, subOrient] ← FlattenLayout[inst.ob, cache, into];
inst.trans.orient ← CDBasics.ComposeOrient[subOrient, inst.trans.orient];
};
entry: FlatObject ← NARROW [RefTab.Fetch[cache, newObj].val];
SELECT TRUE FROM
entry#NIL => RETURN [newObj: entry.obj, newOrient: entry.orientation];
newObj.class=PW.rotationClass => { -- name inherited from underlying
rotSpec: PW.RotationSpecific ← NARROW [newObj.specific];
subOrient: CD.Orientation;
[newObj, subOrient] ← FlattenLayout[rotSpec.obj, cache, into];
newOrient ← CDBasics.ComposeOrient[CDBasics.ComposeOrient[subOrient, rotSpec.orientation], newOrient];
};
newObj.class=PW.indirectClass => { -- name inherited from underlying
indSpec: CD.Object ← NARROW [newObj.specific];
subOrient: CD.Orientation;
[newObj, subOrient] ← FlattenLayout[indSpec, cache, into];
newOrient ← CDBasics.ComposeOrient[subOrient, newOrient];
};
CDImports.IsImport[newObj] => { -- no need for naming
import: CDImports.ImportSpecific ← NARROW [newObj.specific];
newObj ← CDImports.CreateImportFromCache[into: into, objectName: import.objectName, importeeName: import.designName];
};
CDCells.IsCell[newObj] => { -- enforce parent name
newObj ← CDDirectory.Another1[newObj, NIL, NIL, FALSE].new;
newObj.immutable ← FALSE; -- Horrible hack, but CDCells.Another does not do it...
PW.SetName[newObj, PW.Name[initial]];
};
ENDCASE => { -- enforce parent name
newObj ← CDDirectory.Expand1[newObj, NIL, NIL, FALSE].new;
PW.SetName[newObj, PW.Name[initial]];
};
IF CDCells.IsCell[newObj] THEN {
[] ← CDCells.EnumerateInstances[newObj, EachInstance];
CDCells.SetBorderMode[newObj, TRUE];
CDCells.SetSimplificationTreshhold[newObj, cellVisibility, TRUE];
};
[] ← RefTab.Store[cache, initial, NEW [FlatObjectRep ← [newObj, newOrient]]];
};
};
Command
GetWorkingDesign: PROC [] RETURNS [work: CD.Design, viewer: ViewerClasses.Viewer] ~ {
vl: CDViewer.ViewerList;
work ← CDViewer.FindDesign["LayoutWorkArea"];
viewer ← NIL;
IF work=NIL THEN { -- create it if not found
work ← CDOps.CreateDesign[CD.FetchTechnology[$cmosB]];
CDOps.SetMutability[work, editable];
CDProperties.PutDesignProp[work, $CDxDontBackgroundSave, $TRUE]; -- prevent background saving
[] ← CDOps.RenameDesign[work, "LayoutWorkArea"];
};
vl ← CDViewer.ViewersOf[work];
IF vl=NIL THEN {
viewer ← CDViewer.CreateViewer[work, FALSE];
CDSequencer.ExecuteCommand[queue: doQueue, comm: NEW [CDSequencer.CommandRec ← [key: $ViewBord, design: work, ref: viewer]]];
ViewerOps.SetViewer[viewer: viewer, data: NEW [INT←work.technology.lambda/2], op: $Grid]; -- force grid to l/2; works because previous resyncs design...
}
ELSE viewer ← vl.first;
};
DoFlattenLayout: PROC [comm: CDSequencer.Command] ~ {
EachRoot: CoreCDUser.EachRootCellTypeProc ~ {
Generate layout object for root, flatten it & include it
layout: CD.Object; orient: CD.Orientation; inst: CD.Instance;
name: Rope.ROPE = CoreOps.GetCellTypeName[root];
into: CD.Design; viewer: ViewerClasses.Viewer;
TerminalIO.PutF["Generating layout for %g.\n", IO.rope[name]];
layout ← PWCore.Layout[root];
TerminalIO.PutF["Flattening layout for %g.\n", IO.rope[name]];
[into, viewer] ← GetWorkingDesign[];
[layout, orient] ← FlattenLayout[layout, RefTab.Create[], into];
inst ← CDOps.PlaceInst[into, layout, NIL];
inst.trans.orient ← orient; inst.selected ← TRUE;
CDViewer.ShowAndScale[viewer, CDInstances.InstRectO[inst]];
};
[] ← CoreCDUser.EnumerateSelectedCellTypes[comm.design, EachRoot];
};
DoShellIntoWork: PROC [comm: CDSequencer.Command] ~ {
EachRoot: CoreCDUser.EachRootCellTypeProc ~ {
Generate layout object for root, flatten it & include it
shell: CD.Object; mode: Rope.ROPE ← "Layout"; inst: CD.Instance;
name: Rope.ROPE = CoreOps.GetCellTypeName[root];
into: CD.Design; viewer: ViewerClasses.Viewer;
TerminalIO.PutF["Generating shell for %g.\n", IO.rope[name]];
IF CoreCDUser.GetRootCellTypeDecoration[root]=Sisyph.mode.decoration THEN {
[] ← PWCore.Layout[root];
mode ← "Schematic";
};
[into, viewer] ← GetWorkingDesign[];
shell ← CoreGeometry.CreateShell[PWCore.extractMode.decoration, root, FALSE];
PW.SetName[shell, IO.PutFR["%g-Shell-%g", IO.rope[mode], IO.rope[name]]];
inst ← CDOps.PlaceInst[into, shell, NIL];
inst.selected ← TRUE;
CDViewer.ShowAndScale[viewer, CDInstances.InstRectO[inst]];
};
[] ← CoreCDUser.EnumerateSelectedCellTypes[comm.design, EachRoot];
};
Remove name hints
Remove the $Describe CD property either from the whole design if nothing is selected, otherwise from whoever is selected.
Command
DoRemoveDescribe: PROC [comm: CDSequencer.Command] ~ {
RemoveDescribe: CDDirectory.EachEntryAction ~ {
IF CDProperties.GetObjectProp[ob, $Describe]#NIL THEN CDProperties.PutObjectProp[ob, $Describe, NIL];
};
iList: CD.InstanceList ← CDInstances.OnlySelected[CDOps.InstList[comm.design]];
IF iList#NIL THEN {
UNTIL iList=NIL DO
[] ← RemoveDescribe[NIL, iList.first.ob];
iList ← iList.rest;
ENDLOOP;
TerminalIO.PutF["Removed $Describe from selected object(s)\n"];
}
ELSE {
[] ← CDDirectory.Enumerate[comm.design, RemoveDescribe];
TerminalIO.PutF["Removed $Describe from all object(s) in directory\n"];
};
};
Initializations
CDCommandOps.RegisterWithMenu[hackMenu, "Select wire roots", "Select all instances that extract as a wire with a given name", $DAUserHacksSelectWireRoots, DoSelectWireRoots];
CDCommandOps.RegisterWithMenu[hackMenu, "Grow wire one step", "Extend the currently selected piece of wire by one connection step", $DAUserHacksGrowWireOneStep, DoGrowWireOneStep];
CDCommandOps.RegisterWithMenu[hackMenu, "Grow wire to max", "Select all geometry for the wire until done or error", $DAUserHacksGrowWireToMax, DoGrowWireToMax];
CDCommandOps.RegisterWithMenu[hackMenu, "Layout sanity check", "Make some simple consistency checks for handcrafted layout", $DAUserHackSanityCheck, DoSanityCheck];
CDCommandOps.RegisterWithMenu[hackMenu, "Who uses", "Locate all cells using selected/named object", $DAUserHackWhoUses, DoWhoUses];
CDCommandOps.RegisterWithMenu[hackMenu, "Flattened Layout", "Generate flattened layout for selected CTs", $DAUserHackFlattenLayout, DoFlattenLayout];
CDCommandOps.RegisterWithMenu[hackMenu, "Usable shell", "Generate shell into work area", $DAUserHacksShell, DoShellIntoWork];
CDCommandOps.RegisterWithMenu[hackMenu, "Remove $Describe", "Remove name hint property from selected objects or directory", $DAUserHackRemoveDescribe, DoRemoveDescribe];
END.