PWImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Created by: Monier, June 24, 1985 4:30:52 pm PDT
Louis Monier August 22, 1985 12:39:11 pm PDT
Bertrand Serlet June 3, 1986 0:59:58 am PDT
-- This implemetation module contains the "safe" primitives accessible to the user, e.g. Abut.
DIRECTORY
Ascii, BasicTime,
CD, CDBasics, CDCells, CDDirectory, CDEvents, CDGenerate, CDIO, CDMenus, CDOps, CDOrient, CDProperties, CDRects, CDSequencer, CDSymbolicObjects, CDValue, CDViewer,
Commander,
HashTable,
IO,
PW,
PWObjects, PWPins,
RedBlackTree, Rope, RopeList,
TerminalIO,
ViewerClasses;
PWImpl: CEDAR PROGRAM
IMPORTS Ascii, BasicTime, CD, CDBasics, CDCells, CDDirectory, CDEvents, CDGenerate, CDIO, CDMenus, CDOps, CDOrient, CDProperties, CDRects, CDSequencer, CDSymbolicObjects, CDValue, CDViewer, Commander, HashTable, IO, PW, PWPins, PWObjects, RedBlackTree, Rope, RopeList, TerminalIO
EXPORTS PW =
BEGIN
OPEN PW;
Types and signals
AbutProblem: PUBLIC SIGNAL [what: ROPE, isX: BOOL, ob1, ob2: Object] = CODE;
Utilities
-- Reverse a list of objects
Reverse: PUBLIC PROC [listObj: ListOb] RETURNS [result: ListOb ← NIL] = {
WHILE listObj#NIL DO result ← CONS [listObj.first, result]; listObj ← listObj.rest; ENDLOOP;
};
Safe subset
-- All flavors of Abut
-- Clean the list and check that they match in size
CleanAndCheck: PROC [listObj: ListOb, isX: BOOL] RETURNS [newListObj: ListOb ← NIL] =
BEGIN
size, prSize: INT ← -1;
prObj, obj: Object;
IF listObj=NIL THEN RETURN[NIL]; -- the list was NIL
-- Clean the list and compare the sizes
WHILE listObj#NIL DO
obj ← listObj.first;
IF obj=NIL THEN {listObj ← listObj.rest; LOOP}; -- skip NIL objects
size ← IF isX THEN CD.InterestSize[obj].y ELSE CD.InterestSize[obj].x; -- obj is garanteed to be non-NIL
newListObj ← CONS [obj, newListObj];    -- build clean list (reversed)
IF prSize<0 THEN {prSize ← size; prObj ← obj; listObj ← listObj.rest; LOOP}; -- first time
IF prSize#size THEN AbutProblem["These two objects do not match\n", isX, prObj, obj];
-- just before looping
prSize ← size; prObj ← obj; listObj ← listObj.rest;
ENDLOOP;
newListObj ← Reverse[newListObj];
END;
AbutX: PUBLIC PROC [t1,t2,t3,t4,t5,t6: Object ← NIL] RETURNS [obj: Object] =
{listOb: ListOb ← NIL;
RETURN [AbutListX[LIST[t1,t2,t3,t4,t5,t6]]]};
AbutY: PUBLIC PROC [t1,t2,t3,t4,t5,t6: Object ← NIL] RETURNS [obj: Object] = {
listOb: ListOb ← NIL; RETURN [AbutListY[LIST[t1,t2,t3,t4,t5,t6]]];
};
AbutListX: PUBLIC PROC [listOb: ListOb] RETURNS [obj: Object] = {
listOb ← CleanAndCheck[listOb, TRUE];
obj ← IF listOb=NIL THEN NIL ELSE PWObjects.CreateNewAbutX[listOb];
};
AbutListY: PUBLIC PROC [listOb: ListOb] RETURNS [obj: Object] = {
listOb ← CleanAndCheck[listOb, FALSE];
obj ← IF listOb=NIL THEN NIL ELSE PWObjects.CreateNewAbutY[listOb];
};
Lexico: TYPE = REF LexicoRec;
LexicoRec: TYPE = RECORD [loc: INT, size: INT, layer: CD.Layer];
-- key from data
GetKey: RedBlackTree.GetKey = {
[data: RedBlackTree.UserData] RETURNS [RedBlackTree.Key]
RETURN[data];
};
-- order is: coordinate, size, layer; name is not used
Compare: RedBlackTree.Compare = {
[k: RedBlackTree.Key, data: RedBlackTree.UserData] RETURNS [Basics.Comparison]
key: Lexico ← NARROW[data];
kk: Lexico ← NARROW[k];
SELECT TRUE FROM
kk.loc<key.loc => RETURN[less];
kk.loc>key.loc => RETURN[greater];
kk.size<key.size => RETURN[less];
kk.size>key.size => RETURN[greater];
kk.layer<key.layer => RETURN[less];
kk.layer>key.layer => RETURN[greater];
ENDCASE => RETURN[equal]; -- should this be an error???
};
CheckPair: PROC [t1, t2: Object ← NIL, isX: BOOL] =
BEGIN
table1, table2: RedBlackTree.Table;
-- key from Instance
MakeLexico: PROC[inst: Instance, obj: Object] RETURNS [Lexico] = {
k: Lexico ← NEW [LexicoRec ← [
IF isX THEN GetLocation[inst, obj].y ELSE GetLocation[inst, obj].x,
IF isX THEN CDOrient.OrientedSize[inst.ob.size, inst.orientation].y
ELSE CDOrient.OrientedSize[inst.ob.size, inst.orientation].x,
CDSymbolicObjects.GetLayer[inst]]];
RETURN[k];
};
ParseT1: PWPins.InstanceEnumerator = {
[inst: CD.Instance] RETURNS [quit: BOOL ← FALSE]
IF PWPins.GetSide[t1, inst].side=(IF isX THEN right ELSE top) THEN
{k: Lexico ← MakeLexico[inst, t1]; RedBlackTree.Insert[table1, k, k]};
};
ParseT2: PWPins.InstanceEnumerator = {
-- [inst: CD.Instance] RETURNS [quit: BOOL ← FALSE]
IF PWPins.GetSide[t2, inst].side=(IF isX THEN left ELSE bottom) THEN
{k: Lexico ← MakeLexico[inst, t2]; RedBlackTree.Insert[table2, k, k]};
};
CheckIfMatch: RedBlackTree.EachNode = {
[data: RedBlackTree.UserData] RETURNS [stop: BOOL ← FALSE]
-- scream if mismatch: this is not a complete test yet!!!
IF RedBlackTree.Lookup[table2, NARROW[data]]=NIL
THEN AbutProblem["The pins of these two objects do not match\n", isX, t1, t2];
};
-- create dictionnaries
table1 ← RedBlackTree.Create[GetKey, Compare];
table2 ← RedBlackTree.Create[GetKey, Compare];
-- parse right edge of t1 and left edge of t2, and fill up the dictionnaries
[] ← PWPins.EnumerateEdgePins[t1, ParseT1];
[] ← PWPins.EnumerateEdgePins[t2, ParseT2];
-- find pairs which match by position, size, and layer
RedBlackTree.EnumerateIncreasing[table1, CheckIfMatch];
END;
AbutCheckX: PUBLIC PROC [t1,t2,t3,t4,t5,t6: Object ← NIL] RETURNS [obj: Object] =
{RETURN [AbutCheckListX[LIST[t1, t2, t3, t4, t5, t6]]]};
AbutCheckY: PUBLIC PROC [t1,t2,t3,t4,t5,t6: Object ← NIL] RETURNS [obj: Object] =
{RETURN [AbutCheckListY[LIST[t1, t2, t3, t4, t5, t6]]]};
AbutCheckListX: PUBLIC PROC [listOb: ListOb] RETURNS [obj: Object] =
{t1, t2: Object;
saveListOb: ListOb;
listOb ← CleanAndCheck[listOb, FALSE]; -- no more NILs
IF listOb=NIL THEN RETURN[NIL];
saveListOb ← listOb;
t1 ← listOb.first; listOb ← listOb.rest; 
WHILE listOb#NIL DO-- check adjacent pairs of edges
t2 ← listOb.first;
CheckPair[t1, t2, TRUE]; -- will raise an error if mismatch
t1 ← t2; listOb ← listOb.rest; 
ENDLOOP;
obj ← PWObjects.CreateNewAbutX[saveListOb];
};
AbutCheckListY: PUBLIC PROC [listOb: ListOb] RETURNS [obj: Object] =
{t1, t2: Object;
saveListOb: ListOb;
listOb ← CleanAndCheck[listOb, FALSE]; -- no more NILs
IF listOb=NIL THEN RETURN[NIL];
saveListOb ← listOb;
t1 ← listOb.first; listOb ← listOb.rest; 
WHILE listOb#NIL DO
t2 ← listOb.first;
CheckPair[t1, t2, FALSE]; -- will raise an error if mismatch
t1 ← t2; listOb ← listOb.rest; 
ENDLOOP;
obj ← PWObjects.CreateNewAbutY[saveListOb];
};
-- Arrays and other repetitions, using simple Abut. (other flavors of Abut ???)
MapFunctionX: PUBLIC PROC [function: XYFunction, lx: INT ← 0, ux: INT] RETURNS [new: Object] =
BEGIN  
row: ListOb ← NIL;
IF lx>=ux THEN RETURN[NIL];
FOR x: INT DECREASING IN [lx .. ux) DO
row ← CONS [function[x, 0], row];
ENDLOOP;
RETURN [AbutListX[row]];
END;
MapFunctionY: PUBLIC PROC [function: XYFunction, ly: INT ← 0, uy: INT] RETURNS [new: Object] =
BEGIN  
row: ListOb ← NIL;
IF ly>=uy THEN RETURN[NIL];
FOR y: INT DECREASING IN [ly .. uy) DO
row ← CONS [function[0, y], row];
ENDLOOP;
RETURN [AbutListY[row]];
END;
MapFunction: PUBLIC PROC [function: XYFunction, lx: INT ← 0, ux: INT, ly: INT ← 0, uy: INT] RETURNS [new: Object]=
BEGIN  
rows: ListOb ← NIL;
IF lx>=ux OR ly>=uy THEN RETURN[NIL];
FOR y: INT DECREASING IN [ly .. uy) DO
row: ListOb ← NIL;
make a row
FOR x: INT DECREASING IN [lx .. ux) DO
row ← CONS [function[x, y], row];
ENDLOOP;
create the object corresponding to the row
rows ← CONS [AbutListX[row], rows];
ENDLOOP;
create the tile corresponding to the set of rows
RETURN [AbutListY[rows]];
END;
MapFunctionIndexPins: PUBLIC PROC [function: XYFunction, lx: INT ← 0, ux: INT, ly: INT ← 0, uy: INT, indexedPins: ARRAY PWPins.Side OF LIST OF ROPEALL [NIL]] RETURNS [new: Object] = {
rows: ListOb ← NIL;
IF lx>=ux OR ly>=uy THEN RETURN[NIL];
FOR y: INT DECREASING IN [ly .. uy) DO
row: ListOb ← NIL;
make a row
FOR x: INT DECREASING IN [lx .. ux) DO
ob: Object ← function[x, y];
ChangePin: PWPins.ChangePinProc = {
side: PWPins.Side ← PWPins.GetSide[ob, oldPin];
name: ROPE ← CDSymbolicObjects.GetName[oldPin];
IF (SELECT side FROM
bottom => y#ly,
right  => x#ux-1,
top  => y#uy-1,
left  => x#lx,
ENDCASE => ERROR) THEN RETURN;
newPin ← PWPins.CopyInstance[oldPin];
IF ~RopeList.Memb[indexedPins[side], name] THEN RETURN;
CDSymbolicObjects.SetName[newPin, IO.PutFR["%g[%g]", IO.rope[name], IO.int[SELECT side FROM bottom, top => x, right, left => y, ENDCASE => ERROR]]];
};
row ← CONS [PWPins.ChangePins[ob, ChangePin], row];
ENDLOOP;
create the object corresponding to the row
rows ← CONS [AbutListX[row], rows];
ENDLOOP;
create the tile corresponding to the set of rows
RETURN [AbutListY[rows]];
};
ArrayX: PUBLIC PROC [ob: Object, nx: INT ← 1] RETURNS [new: Object] =
BEGIN  
row: LIST OF Object ← NIL;
IF nx=0 OR ob=NIL THEN RETURN[NIL];
FOR x: INT IN [0 .. nx) DO
row ← CONS [ob, row];
ENDLOOP;
RETURN [AbutListX[row]];
END;
ArrayY: PUBLIC PROC [ob: Object, ny: INT ← 1] RETURNS [new: Object] =
BEGIN  
row: LIST OF Object ← NIL;
IF ny=0 OR ob=NIL THEN RETURN[NIL];
FOR x: INT IN [0 .. ny) DO
row ← CONS [ob, row];
ENDLOOP;
RETURN [AbutListY[row]];
END;
Array: PUBLIC PROC [ob: Object, nx,ny: INT ← 1] RETURNS [new: Object] =
{new ← ArrayY[ArrayX[ob, nx], ny];};
-- This is a particular case where we want to attach to an object a property logically attached to an application: orientation, instance name, ...
-- For mapping rectangles
MapRectProc: TYPE = PROC [rect: CD.Rect] RETURNS [CD.Rect];
-- Planar tranformations
ChangeOrientation: PUBLIC PROC [obj: CD.Object, orientation: CDOrient.Orientation] RETURNS [cell: CD.Object] =
BEGIN
cell ← CreateEmptyCell[];
[] ← IncludeInCell[cell: cell, obj: obj, orientation: orientation];
RepositionCell[cell];
END;
-- Create a cell with obj as only application, and InstName as instance name
InstanceName: PUBLIC PROC [obj: Object, instanceName: ROPE] RETURNS [cell: Object] =
BEGIN
appl: CD.Instance;
cell ← CreateEmptyCell[];
appl ← IncludeInCell[cell: cell, obj: obj];
CDProperties.PutInstanceProp[appl, $InstanceName, instanceName];
RepositionCell[cell];
END;
-- Copy and conditional objects
-- Make a copy of the cell "ob" where every instance ap is replaced by p[ap]; if p=NIL, the instance is unchanged
CopyAndProcess: PROC [ob: CD.Object, p: ForEachInstProc ← NIL, data: REFNIL] RETURNS [new: CD.Object] =
BEGIN
-- Copies the application, and inserts it in "new"
Insert: PROC [inst: CD.Instance, location: CD.Position] =
BEGIN
IF inst#NIL THEN {
newInst: CD.Instance ← IncludeInCell[new, inst.ob, location, inst.orientation];
newInst.properties ← CDProperties.DCopyProps[inst.properties];};
END;
ExpandInCell: PROC [ob: Object] RETURNS [cell: Object] =
{cell ← IF CDCells.IsCell[ob] THEN ob ELSE CDDirectory.Expand[ob, NIL, NIL].new};
location: CD.Position;
WHILE ~ISTYPE[ob.specificRef, CD.CellPtr] DO ob ← CDDirectory.Expand[ob, NIL, NIL].new ENDLOOP;
new ← CreateEmptyCell[];
CDCells.SetInterestRect[new, CD.InterestRect[ob]]; -- copy the IRect
FOR instances: CD.InstanceList ← NARROW [ExpandInCell[ob].specificRef, CD.CellPtr].contents, instances.rest WHILE instances # NIL DO
inst: CD.Instance ← instances.first;
location ← GetLocation[inst, ob];
IF p#NIL THEN Insert[p[inst, data], location] ELSE Insert[inst, location];
ENDLOOP;
AppendProps[new, ob];
RepositionCell[new];
END;
Copy: PUBLIC PROC [ob: Object] RETURNS [new: Object] =
{new ← CopyAndProcess[ob]};
-- How about modif in IRect!!!
Inst: PUBLIC PROC [ob: Object, conds: LIST OF ROPENIL, removeNamed: BOOLTRUE] RETURNS [new: Object] =
BEGIN
ConditionalObjProc: PW.ForEachInstProc =
BEGIN
ref: REF ← CDProperties.GetInstanceProp[inst, $PWCond];
IF ref = NIL THEN RETURN[inst] -- this instance is not conditional
ELSE BEGIN
rec: Data ← NARROW[data];
WITH ref SELECT FROM
prop: ROPE =>
BEGIN  -- it is a conditionnal object; see if it is listed
listed: BOOLFALSE;
FOR list: LIST OF ROPE ← rec.conds, list.rest WHILE list # NIL DO
IF Rope.Equal[list.first, prop] THEN listed ← TRUE;
ENDLOOP; -- standard in PWNames !!!
-- We remove an object if it is listed and removeNamed=TRUE or if removeNamed=FALSE and it is not listed
IF listed # rec.removeNamed THEN RETURN[inst] ELSE RETURN[NIL];
END;
ENDCASE => RETURN[inst];
END;
END;
Data: TYPE = REF DataRec;
DataRec: TYPE = RECORD[conds: LIST OF ROPENIL, removeNamed: BOOLTRUE];
data: Data ← NEW[DataRec ← [conds, removeNamed]];
new ← CopyAndProcess[ob, ConditionalObjProc, data];
END;
Flatten: PUBLIC PROC [cell: Object] RETURNS [new: Object] =
BEGIN
IF CDProperties.GetObjectProp[cell, $DontFlatten]#NIL THEN RETURN [cell];
IF ~ISTYPE[cell.specificRef, CD.CellPtr] THEN {
new ← CDDirectory.Expand[cell, NIL, NIL].new;
RETURN [IF new=NIL THEN cell ELSE Flatten[new]];
};
new ← CDCells.CreateEmptyCell[];
FOR appls: CD.InstanceList ← NARROW [cell.specificRef, CD.CellPtr].contents, appls.rest WHILE appls # NIL DO
inst: CD.Instance;
ap: CD.Instance ← appls.first;
ob: Object ← Flatten[ap.ob];
location: CD.Position ← GetLocation[ap, cell]; -- Interest Coord of ap.ob in the Interest Coord system of cell
IF ~ISTYPE[ob.specificRef, CD.CellPtr] OR CDProperties.GetObjectProp[ob, $DontFlatten]#NIL
THEN {-- it's not a cell: copy it
inst ← PW.IncludeInCell[new, ob, location, ap.orientation];
inst.properties ← CDProperties.DCopyProps[ap.properties];
}
ELSE FOR l: CD.InstanceList ← NARROW [ob.specificRef, CD.CellPtr].contents, l.rest WHILE l # NIL DO
inst ← PW.IncludeInCell[
new, l.first.ob,
CDBasics.BaseOfRect[CDOrient.MapRect[
CDOrient.RectAt[GetLocation[l.first, ob], CD.InterestSize[l.first.ob], l.first.orientation],
CD.InterestSize[ob], ap.orientation, location
]],
CDOrient.ComposeOrient[l.first.orientation, ap.orientation]];
inst.properties ← CDProperties.DCopyProps[l.first.properties];
ENDLOOP;
ENDLOOP;
SetInterestRect[new, CD.InterestSize[cell]]; -- copy the IRect
AppendProps[new, cell];
RepositionCell[new];
END;
-- Other functions
--Fetching a cell from a design
-- Not perfectly safe, because if someone imports a cell, edits it, and imports it again, the version stamp is unchanged, so no conflict is detected.
Get: PUBLIC PROC [design: Design, name: ROPE] RETURNS [ob: Object] = {
IF design=NIL THEN ERROR;
ob ← CDDirectory.Fetch[design, name].object;
IF ob=NIL THEN {WriteF["Object %g not found in the design.\n", IO.rope[name]]; ERROR};
};
-- Open a design, given a file name
OpenDesign: PUBLIC PROC [fileName: ROPE] RETURNS [design: CD.Design] =
BEGIN
-- for now, nothing is checked
design ← CDIO.ReadDesign[fileName, NIL, CDIO.GetWorkingDirectory[]];
CDValue.Store[design, $KeepObjects, $KeepObjects]; -- to avoid finalization
END;
CopyRecursive: PROC [old: Object] RETURNS [new: Object] = {
new ← CDDirectory.Another[old, NIL, NIL].new;
IF CDCells.IsCell[old] THEN {
cellPtr: CD.CellPtr ← NARROW [new.specificRef];
FOR list: CD.InstanceList ← cellPtr.contents, list.rest WHILE list#NIL DO
list.first.ob ← CopyRecursive[list.first.ob];
ENDLOOP;
};
};
-- Registration of UserProc: this creates the entry "Run MyWondeful generator" in the menu
Register: PUBLIC PROC [userProc: UserProc, name: ROPE] = {
WriteF["Generator program %g %g.\n", IO.rope[name], IO.rope[
IF CDGenerate.Register[table: CDGenerate.AssertTable["PatchWork"], key: name, generator: userProc, cache: FALSE] THEN "recorded" ELSE "overwritten"]];
};
Shared abuts
Novice users, please skip this section
sharedAbutXCache: HashTable.Table; -- Object -> HashTable(Object -> Object)
sharedAbutYCache: HashTable.Table; -- Object -> HashTable(Object -> Object)
SharedAbutX: PUBLIC PROC [t1, t2: Object ← NIL] RETURNS [obj: Object] = {
subTable: HashTable.Table ← NARROW [HashTable.Fetch[sharedAbutXCache, t1].value];
IF subTable=NIL THEN {subTable ← HashTable.Create[2]; [] ← HashTable.Store[sharedAbutXCache, t1, subTable]};
obj ← NARROW [HashTable.Fetch[subTable, t2].value];
IF obj#NIL THEN RETURN;
obj ← AbutX[t1, t2];
[] ← HashTable.Store[subTable, t2, obj];
};
SharedAbutY: PUBLIC PROC [t1, t2: Object ← NIL] RETURNS [obj: Object] = {
subTable: HashTable.Table ← NARROW [HashTable.Fetch[sharedAbutYCache, t1].value];
IF subTable=NIL THEN {subTable ← HashTable.Create[2]; [] ← HashTable.Store[sharedAbutYCache, t1, subTable]};
obj ← NARROW [HashTable.Fetch[subTable, t2].value];
IF obj#NIL THEN RETURN;
obj ← AbutY[t1, t2];
[] ← HashTable.Store[subTable, t2, obj];
};
SharedAbutListX: PUBLIC PROC [listOb: ListOb] RETURNS [obj: Object] = {
revList: ListOb ← NIL;
IF listOb=NIL THEN RETURN [NIL];
IF listOb.rest=NIL THEN RETURN [listOb.first];
WHILE listOb#NIL AND listOb.rest#NIL DO
revList ← CONS [SharedAbutX[listOb.first, listOb.rest.first], revList];
listOb ← listOb.rest.rest;
ENDLOOP;
IF listOb#NIL THEN revList ← CONS [listOb.first, revList];
obj ← SharedAbutListX[Reverse[revList]];
};
SharedAbutListY: PUBLIC PROC [listOb: ListOb] RETURNS [obj: Object] = {
revList: ListOb ← NIL;
IF listOb=NIL THEN RETURN [NIL];
IF listOb.rest=NIL THEN RETURN [listOb.first];
WHILE listOb#NIL AND listOb.rest#NIL DO
revList ← CONS [SharedAbutY[listOb.first, listOb.rest.first], revList];
listOb ← listOb.rest.rest;
ENDLOOP;
IF listOb#NIL THEN revList ← CONS [listOb.first, revList];
obj ← SharedAbutListY[Reverse[revList]];
};
FlushSharedCache: PUBLIC PROC = {
sharedAbutXCache ← HashTable.Create[2];
sharedAbutYCache ← HashTable.Create[2];
};
You are on your own
propertiesToFlushOnEdit: LIST OF ATOMNIL;
RegisterProp: PUBLIC PROC [prop: ATOM, copy: BOOLFALSE, flushOnEdit: BOOLFALSE] RETURNS [sameAtom: ATOM] = {
[] ← CDProperties.RegisterProperty[prop, $PW];
CDProperties.InstallProcs[prop, [makeCopy: IF copy THEN CDProperties.CopyVal ELSE CDProperties.DontCopy]];
IF flushOnEdit THEN propertiesToFlushOnEdit ← CONS [prop, propertiesToFlushOnEdit];
sameAtom ← prop;
};
-- Include an object in a cell; position is relative to the InterestRect
IncludeInCell: PUBLIC PROC [cell: Object, obj: Object, position: CD.Position ← [0, 0], orientation: CD.Orientation ← CD.original] RETURNS [newInst: Instance] =
BEGIN
newInst ← CDCells.IncludeOb[design: NIL,
cell: cell, ob: obj, position: position, orientation: orientation,
cellCSystem: interrestCoords, obCSystem: interrestCoords, mode: dontPropagate].newInst
END;
-- Adds the properties of obj which are copiable to the ones of newObj
AppendProps: PUBLIC PROC [newObj, obj: CD.Object] = {
FOR l: CDProperties.PropList ← obj.properties, l.rest WHILE l#NIL DO
IF ISTYPE[l.first.key, ATOM] AND CDProperties.GetObjectProp[newObj, l.first.key]=NIL THEN {
p: CDProperties.PropertyProcs = CDProperties.FetchProcs[l.first.key];
IF p#NIL AND p.makeCopy#NIL THEN
CDProperties.PutObjectProp[
newObj, l.first.key, p.makeCopy[l.first.key, l.first.val]];
};
ENDLOOP;
};
IncludeNoReposition: PROC [design: CD.Design, obj: Object, name: ROPENIL] =
{IF design#NIL THEN [] ← CDDirectory.Include[design, obj, name]};
-- The following primitive allows the use of the interestRectangle coordinates only
GetLocation: PUBLIC PROC [inst: CD.Instance, obj: CD.Object] RETURNS [location: CD.Position] =
{location ← CDBasics.SubPoints[
CDBasics.BaseOfRect[
CDOrient.MapRect[CD.InterestRect[inst.ob], inst.ob.size, inst.orientation, inst.location]],
CDBasics.BaseOfRect[CD.InterestRect[obj]]]};
Send: PUBLIC PROC [obj: Object, prop: ATOM] RETURNS [value: REFNIL, expandedObj: Object] = {
expandedObj ← obj;
WHILE value=NIL DO
IF expandedObj=NIL THEN RETURN;
value ← CDProperties.GetObjectProp[expandedObj, prop];
IF value#NIL THEN RETURN;
value ← CDProperties.GetProp[expandedObj.class, prop];
IF value#NIL THEN RETURN;
expandedObj ← CDDirectory.Expand[expandedObj, NIL, NIL].new;
ENDLOOP;
};
High-level constructors (safe if your procs are safe)
TransferCell: PUBLIC PROC [template: Object, objSide: PWPins.Side, width: INT, objProc: ForEachPinProc, selectNameProc: SelectNamesProc ← KeepAll] RETURNS [cell: Object] =
BEGIN
KeepPinOnEdge: PWPins.InstanceEnumerator =
BEGIN
newObj: Object;
side: PWPins.Side ← PWPins.GetSide[template, inst].side;
IF side=objSide AND selectNameProc[CDSymbolicObjects.GetName[inst]] THEN {
newObj ← objProc[inst]; IF newObj=NIL THEN RETURN;
[] ← IncludeInCell[cell, newObj, Position[inst, template, objSide], SideToOrient[objSide]];
};
END;
iRect: CD.Rect;
-- Start with an empty cell of appropriate interestRect (origin in 0,0)
IF objSide=none THEN ERROR;
cell ← CreateEmptyCell[];
iRect ← CD.InterestRect[template];   -- copy interestRect of obj
CDCells.SetInterestRect[cell, IRect[iRect, width, objSide]]; -- set interestRect of cell
-- Parse the pins
[] ← PWPins.EnumerateEdgePins[template, KeepPinOnEdge];
RepositionCell[cell];
END;
IRect: PROC [templateRect: CD.Rect, otherDim: INT, side: PWPins.Side] RETURNS [iRect: CD.Rect] =
BEGIN
iRect ← SELECT side FROM
top, bottom => [0, 0, templateRect.x2-templateRect.x1, otherDim],
left, right => [0, 0, otherDim, templateRect.y2-templateRect.y1],
ENDCASE => ERROR;
END;
WireSize: PROC [inst: CD.Instance, otherDim: INT, side: PWPins.Side] RETURNS [size: CD.Position] =
BEGIN
size ← CDOrient.OrientedSize[inst.ob.size, inst.orientation]; -- may be inst.ob.size should be CD.InterestSize[inst.ob] for pdif ???
SELECT side FROM
top, bottom => size ← [otherDim, size.x];
left, right => size ← [otherDim, size.y];
ENDCASE => ERROR;
END;
Position: PROC [inst: CD.Instance, template: Object, side: PWPins.Side] RETURNS [position: CD.Position] =
BEGIN
position ← GetLocation[inst, template];
SELECT side FROM
top, bottom => position.y ← 0;
left, right => position.x ← 0;
ENDCASE => ERROR;
END;
SideToOrient: PROC [side: PWPins.Side] RETURNS [orient: CD.Orientation] ~ {
orient ← SELECT side FROM
bottom => CDOrient.rotate270,
right => CDOrient.original,
top => CDOrient.rotate90,
left => CDOrient.rotate180,
ENDCASE => ERROR;
};
KeepAll: PUBLIC SelectNamesProc = {keepIt ← TRUE};
TopFillerCell: PUBLIC PROC [template: Object, height: INT, selectNameProc: SelectNamesProc ← KeepAll] RETURNS [cell: Object] =
{cell ← FillerCell[template, top, height, selectNameProc]};
BottomFillerCell: PUBLIC PROC [template: Object, height: INT, selectNameProc: SelectNamesProc ← KeepAll] RETURNS [cell: Object] =
{cell ← FillerCell[template, bottom, height, selectNameProc]};
LeftFillerCell: PUBLIC PROC [template: Object, width: INT, selectNameProc: SelectNamesProc ← KeepAll] RETURNS [cell: Object] =
{cell ← FillerCell[template, left, width, selectNameProc]};
RightFillerCell: PUBLIC PROC [template: Object, width: INT, selectNameProc: SelectNamesProc ← KeepAll] RETURNS [cell: Object] =
{cell ← FillerCell[template, right, width, selectNameProc]};
FillerCell: PROC [template: Object, objSide: PWPins.Side, width: INT, selectNameProc: SelectNamesProc ← KeepAll] RETURNS [cell: Object] =
BEGIN
ObjProc: ForEachPinProc = {
obj ← CDRects.CreateRect[WireSize[inst, width, objSide], CDSymbolicObjects.GetLayer[inst]];
};
cell ← TransferCell[template, objSide, width, ObjProc, selectNameProc];
END;
Tools for helping the designer debug his PW/Core code
Draw: PUBLIC PROC [obj: CD.Object, technologyName: ATOMNIL] RETURNS [design: CD.Design] = {
viewer: ViewerClasses.Viewer;
design ← CDOps.CreateDesign[CD.FetchTechnology[IF technologyName=NIL THEN $cmosB ELSE technologyName]];
CDValue.Store[design, $KeepObjects, $KeepObjects]; -- to avoid finalization
CDOps.IncludeObjectI[design, obj, [0, 0]];
CleanUp[design]; -- If you do it, all your objects are modified!
viewer ← CDViewer.CreateViewer[design];
};
Goodies
-- Input and output from the terminal
-- The arithmetic stuff that people always want
PowersOfTwo: ARRAY [0 .. 33] OF INT = [
1B, 2B, 4B, 1B1, 2B1, 4B1,
1B2, 2B2, 4B2, 1B3, 2B3, 4B3,
1B4, 2B4, 4B4, 1B5, 2B5, 4B5,
1B6, 2B6, 4B6, 1B7, 2B7, 4B7,
1B8, 2B8, 4B8, 1B8, 2B8, 4B8,
1B9, 2B9, 4B9, 1B10];
ODD: PUBLIC PROC [i: INT] RETURNS [BOOL] =
{RETURN [(i MOD 2 # 0)]};
EVEN: PUBLIC PROC [i: INT] RETURNS [BOOL] =
{RETURN [(i MOD 2 = 0)]};
Log2: PUBLIC PROC [n: INT] RETURNS [INT] =
{RETURN [IF n<=1 THEN 0 ELSE 1 + Log2[n / 2]]};
TwoToThe: PUBLIC PROC [x: INT] RETURNS [INT] =
{RETURN[PowersOfTwo[x]]};
TwoToTheLog2: PUBLIC PROC [n: INT] RETURNS [INT] =
{RETURN [TwoToThe[Log2[n]]]};
XthBitOfN: PUBLIC PROC [x, n: INT] RETURNS [BOOL] =
Warning: bit 0 is the LOW order bit (lsb)
{RETURN [IF x<0 THEN FALSE ELSE (n/TwoToThe[x]) MOD 2 =1]};
Commands
-- PW heart: this one really make abuts by calling the user proc.
RunGenerator: PROC [comm: CDSequencer.Command] = {
design: CD.Design ← comm.design;
pos: CD.Position ← comm.pos;
table: CDGenerate.Table ← CDGenerate.AssertTable["PatchWork"];
key: Rope.ROPE;
ob: CD.Object;
time: BasicTime.GMT ← BasicTime.Now[]; -- Start the stop watch
min, sec: INT;
WriteF["PatchWork menu selected\n"];
key ← CDGenerate.SelectOneOf[table, "select generate"];
IF Rope.IsEmpty[key] THEN {WriteF["no generator selected\n"]; RETURN};
-- Generate the object
ob ← CDGenerate.FetchNCall[table, design, key ! AbutProblem => {AbutError[design, ob1, ob2, what, isX, pos]; CONTINUE}];
-- Now figure out how long it took to generate this wonderful piece of layout
sec ← BasicTime.Period[time, BasicTime.Now[]];
min ← sec/60; sec ← sec MOD 60;
WriteF["PW completed in "];
IF min#0 THEN WriteF["%g min ", IO.int[min]]; WriteF["%g sec\n", IO.int[sec]];
IF ob=NIL THEN {WriteF["No returned object\n"]; RETURN};
CDOps.IncludeObjectI[design, ob, pos];
CleanUp[design];
};
AbutError: PROC [design: CD.Design, obj1, obj2: CD.Object, what: ROPE, isX: BOOL, where: CD.Position ← [0, 0]] =
BEGIN
translation: CD.Position ← CD.InterestSize[obj1];
IF isX THEN translation.y ← 0 ELSE translation.x ← 0;
-- Scream
WriteF["PW Error while abuting '%g' and object '%g': %g\n", IO.rope[CDDirectory.Name[obj1]], IO.rope[CDDirectory.Name[obj2]], IO.rope[what]];
WriteF["Repaint the viewer to see the objects\n"];
-- Show the objects
CDOps.IncludeObjectI[design, obj1, where];
IF isX THEN translation.y ← 0 ELSE translation.x ← 0;
CDOps.IncludeObjectI[design, obj2, CDBasics.AddPoints[where, translation]];
IF TerminalIO.Confirm[choice: "Do you want to open an error window for StackWalking?", label: "Open EventViewer?", onTimeOut: FALSE] THEN ERROR;
END;
Directory list
ListMatchingCells: PROC [comm: CDSequencer.Command] = {
EachEntry: CDDirectory.EachEntryAction = {
count ← count+1;
IF NOT Rope.Match[match, name] THEN RETURN;
displayed ← displayed+1;
list ← CONS [
WITH ob.specificRef SELECT FROM cp: CD.CellPtr => cp.name, ENDCASE => IO.PutFR["%g[%g]", IO.rope[name], IO.rope[CDOps.ObjectInfo[ob]]],
list];
};
match: ROPE;
list: LIST OF Rope.ROPENIL; count: INT ← 0; displayed: INT ← 0;
WriteF["List matching objects\n"];
match ← RequestRope["Pattern : "];
[] ← CDDirectory.Enumerate[comm.design, EachEntry];
list ← RopeList.Sort[list, RopeList.Compare];
FOR l: LIST OF Rope.ROPE ← list, l.rest WHILE l#NIL DO
WriteF["  %g", IO.rope[l.first]];
ENDLOOP;
WriteF["\n %g objects counted %g displayed\n", IO.int[count], IO.int[displayed]];
};
ListIcons: PROC [comm: CDSequencer.Command] = {
EachEntry: CDDirectory.EachEntryAction = {
count ← count+1;
IF NOT Rope.Match["*.icon", name] THEN RETURN;
IF NOT Ascii.Letter[Rope.Fetch[name]] AND NOT Ascii.Digit[Rope.Fetch[name]] THEN RETURN;
displayed ← displayed+1;
list ← CONS [
WITH ob.specificRef SELECT FROM cp: CD.CellPtr => cp.name, ENDCASE => IO.PutFR["%g[%g]", IO.rope[name], IO.rope[CDOps.ObjectInfo[ob]]],
list];
};
list: LIST OF Rope.ROPENIL; count: INT ← 0; displayed: INT ← 0;
WriteF["List icons\n"];
[] ← CDDirectory.Enumerate[comm.design, EachEntry];
list ← RopeList.Sort[list, RopeList.Compare];
FOR l: LIST OF Rope.ROPE ← list, l.rest WHILE l#NIL DO
WriteF["  %g", IO.rope[l.first]];
ENDLOOP;
WriteF["\n %g objects counted %g displayed\n", IO.int[count], IO.int[displayed]];
};
Initialization
PatchWorkCommand: Commander.CommandProc = {
IO.PutF[cmd.out, "PatchWork loaded.\n"];
};
Mechanism for flushing during edits
FlushPropertiesAfterReplace: CDEvents.EventProc = {
[event: REF ANY, design: CD.Design, x: REF ANY] RETURNS [dont: BOOL ← FALSE]
xname: ROPE ← CDDirectory.Name[NARROW [x]];
names: LIST OF ROPEIF xname=NIL THEN NIL ELSE LIST [xname]; -- names is going to be the transitive closure of all cells depending on xname
foundANewOne: BOOLTRUE;
FindInvalidNames: CDDirectory.EachEntryAction = {
[name: ROPE, ob: CD.Object] RETURNS [quit: BOOL ← FALSE]
someChildIsName: BOOLFALSE;
IsSomeChildName: CDDirectory.EnumerateObjectsProc = {
[me: CD.Object, x: REF ANY]
IF RopeList.Memb[names, CDDirectory.Name[me]] THEN someChildIsName ← TRUE;
};
CDDirectory.EnumerateChildObjects[ob, IsSomeChildName];
IF someChildIsName AND ~RopeList.Memb[names, name] THEN {names ← CONS [name, names]; foundANewOne ← TRUE};
};
InvalidateNames: CDDirectory.EachEntryAction = {
[name: ROPE, ob: CD.Object] RETURNS [quit: BOOL ← FALSE]
IF RopeList.Memb[names, name] THEN {
flushed: BOOLFALSE;
FOR props: LIST OF ATOM ← propertiesToFlushOnEdit, props.rest WHILE props#NIL DO
flushed ← flushed OR (CDProperties.GetObjectProp[ob, props.first]#NIL);
CDProperties.PutObjectProp[ob, props.first, NIL];
ENDLOOP;
IF flushed THEN TerminalIO.WriteRope[Rope.Cat["Flush ", name, "\n"]];
};
};
WHILE foundANewOne DO
foundANewOne ← FALSE; [] ← CDDirectory.Enumerate[design, FindInvalidNames];
ENDLOOP;
[] ← CDDirectory.Enumerate[design, InvalidateNames];
};
CDMenus.ImplementEntryCommand[$DirectoryMenu, "list matching cells", ListMatchingCells];
CDMenus.ImplementEntryCommand[$DirectoryMenu, "list icons", ListIcons];
CDEvents.RegisterEventProc[$AfterCellReplacement, FlushPropertiesAfterReplace];
CDSequencer.ImplementCommand[$PatchWork, RunGenerator];
CDMenus.CreateEntry[$RectProgramMenu, "PatchWork generator", $PatchWork];
WriteF["PatchWork generator recorded.\n"];
Commander.Register["PW", PatchWorkCommand, "PatchWork, Silicon Assembler on top of ChipNDale, see PWDoc.tioga for more info!\n"];
[] ← PW.RegisterProp[$DontFlatten, TRUE];
FlushSharedCache[];
END.