<> <> <> <> <> <> <<>> DIRECTORY Ascii, BasicTime, CD, CDBasics, CDCells, CDCleanUp, CDDirectory, CDEvents, CDGenerate, CDInstances, CDIO, CDMenus, CDOps, CDOrient, CDProperties, CDRects, CDSequencer, CDSymbolicObjects, CDValue, CDViewer, Commander, GList, HashTable, IO, PW, PWObjects, PWPins, RedBlackTree, Rope, RopeList, TerminalIO, ViewerClasses; PWImpl: CEDAR PROGRAM IMPORTS Ascii, BasicTime, CD, CDBasics, CDCells, CDCleanUp, CDDirectory, CDEvents, CDGenerate, CDIO, CDInstances, CDMenus, CDOps, CDOrient, CDProperties, CDRects, CDSequencer, CDSymbolicObjects, CDValue, CDViewer, Commander, GList, HashTable, IO, PW, PWPins, PWObjects, RedBlackTree, Rope, RopeList, TerminalIO EXPORTS PW = BEGIN OPEN PW; <> AbutProblem: PUBLIC SIGNAL [what: ROPE, isX: BOOL, ob1, ob2: Object] = CODE; <> <<-- Reverse a list of objects>> Reverse: PUBLIC PROC [listObj: ListOb] RETURNS [result: ListOb _ NIL] = { result _ NARROW [GList.Reverse[listObj]]; }; <> <<-- 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 RETURN[less]; kk.loc>key.loc => RETURN[greater]; kk.size RETURN[less]; kk.size>key.size => RETURN[greater]; kk.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; <> FOR x: INT DECREASING IN [lx .. ux) DO row _ CONS [function[x, y], row]; ENDLOOP; <> rows _ CONS [AbutListX[row], rows]; ENDLOOP; <> 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 ROPE _ ALL [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; <> 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 _ CDInstances.Copy[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; <> rows _ CONS [AbutListX[row], rows]; ENDLOOP; <> 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: REF _ NIL] 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 ROPE _ NIL, removeNamed: BOOL _ TRUE] 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: BOOL _ FALSE; 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 ROPE _ NIL, removeNamed: BOOL _ TRUE]; 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 NOT CDCells.IsCell[cell] 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 NOT CDCells.IsCell[ob] 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"]]; }; <> <> 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]; }; <> propertiesToFlushOnEdit: LIST OF ATOM _ NIL; RegisterProp: PUBLIC PROC [prop: ATOM, copy: BOOL _ FALSE, flushOnEdit: BOOL _ FALSE] 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: ROPE _ NIL] = {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: REF _ NIL, 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; }; <> 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; <> Draw: PUBLIC PROC [obj: CD.Object, technologyName: ATOM _ NIL] 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]]; viewer _ CDViewer.CreateViewer[design]; }; <> <<-- 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] = <> {RETURN [IF x<0 THEN FALSE ELSE (n/TwoToThe[x]) MOD 2 =1]}; <> <<-- 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]; CDCleanUp.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; <> ListMatchingCells: PROC [comm: CDSequencer.Command] = { EachEntry: CDDirectory.EachEntryAction = { count _ count+1; IF NOT Rope.Match[match, name, FALSE] 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.ROPE _ NIL; 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, FALSE] 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.ROPE _ NIL; 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]]; }; <> PatchWorkCommand: Commander.CommandProc = { IO.PutF[cmd.out, "PatchWork loaded.\n"]; }; <> 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 ROPE _ IF xname=NIL THEN NIL ELSE LIST [xname]; -- names is going to be the transitive closure of all cells depending on xname foundANewOne: BOOL _ TRUE; FindInvalidNames: CDDirectory.EachEntryAction = { <<[name: ROPE, ob: CD.Object] RETURNS [quit: BOOL _ FALSE]>> someChildIsName: BOOL _ FALSE; 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: BOOL _ FALSE; 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. <<>>