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.ROPE ← NIL, -- of current wire, there may be only one
highlight: CD.InstanceList ← NIL,
userData: REF ANY ← NIL,
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: BOOL ← FALSE;
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:
BOOL ←
FALSE] ~ {
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: BOOL ← FALSE;
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: REF←NIL] RETURNS [quit: BOOL←FALSE]
EachInstance: CDCells.InstEnumerator ~ {
PROC [inst: CD.Instance] RETURNS [quit: BOOL←FALSE]
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.ROPE ← NIL;
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.