File: ParquetImplA.mesa   
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Created by: Mayo, July 16, 1984 4:44:25 pm PDT
Last Edited by: Mayo, November 5, 1984 7:07:45 pm PST
Last Edited by: Jacobi, February 8, 1985 1:27:44 pm PST
DIRECTORY
Parquet,
ParquetInternal,
AlignmentMarks USING [Mark, FindList, MakeMarkAptr],
CheckMarks USING [FindList, MakeCheckAptr],
StretchLines,
Stretch USING [Direction, DoStretch],
TerminalIO USING [WriteRope, WriteInt],
ExprRead,
TileSetExpressions USING [GetParams],
CD,
CDApplications USING [NewApplicationI, BoundingRectI],
CDRects USING [CreateBareRect],
CDDirectory USING [Fetch, Include, Another],
CDBasics USING [AddPoints, SubPoints, MinPoint, MaxPoint, AddSize],
CDOrient USING [IncludesOddRot90, MapPoint, DeMapRect, InverseOrient, ComposeOrient, OrientedSize],
CDCells USING [CreateEmptyCell, RemoveApplication],
CDProperties USING [PutPropOnApplication, GetPropFromObject, CopyProps],
CDImports USING [GetReference, ReferencePtr],
CDImportsExtras USING [OneLevelIncludedCopy],
Rope USING [ROPE, Concat],
PrincOpsUtils USING [BITAND],
AMBridge USING [SomeRefFromTV],
AMTypes USING [TypedVariable, TV],
Interpreter USING [Evaluate],
SymTab USING [Ref, Create, Fetch, Store, Pairs, EachPairAction];
ParquetImplA: CEDAR PROGRAM    
IMPORTS Parquet, ParquetInternal, AlignmentMarks, CheckMarks, Stretch, ExprRead, TileSetExpressions, TerminalIO, CDDirectory, CDImportsExtras, CDBasics, Rope, CDApplications, CDRects, CDOrient, CDCells, CDProperties, CDImports, SymTab, StretchLines, Interpreter, AMBridge EXPORTS Parquet, ParquetInternal SHARES CDDirectory =  
BEGIN OPEN Parquet, ParquetInternal;
HighPosO: PROC [aptr: CD.ApplicationPtr] RETURNS [CD.DesignPosition] =
INLINE {RETURN [CDBasics.AddSize[aptr.location,
CDOrient.OrientedSize[aptr.ob.size, aptr.orientation]]]};
Error: PUBLIC ERROR [ec: ErrorCode] = CODE;
errorSize: INT = 20;
-- Orientations
MirrorY: PUBLIC Orientation ← ComposeOrient[ComposeOrient[Rotate270, MirrorX], Rotate90];
InverseOrient: PUBLIC PROC [orient: Orientation] RETURNS [inverse: Orientation] ~ BEGIN
RETURN[CDOrient.InverseOrient[orient]];
END;
ComposeOrient: PUBLIC PROC [first, second: Orientation] RETURNS [Orientation] ~ BEGIN
RETURN[CDOrient.ComposeOrient[first, second]];
END;
-- Utilities
ShiftedCellIncludeApplication: PROC [shifted: ShiftedCell, aptr: CD.ApplicationPtr] = BEGIN
shifted.llCorner ← CDBasics.MinPoint[shifted.llCorner, aptr.location];
shifted.urCorner ← CDBasics.MaxPoint[shifted.urCorner, HighPosO[aptr]];
shifted.contents ← CONS[aptr, shifted.contents];
END;
-- A check mark was in a tile just placed, record it in the module.
AddCheck: PROC [mod: Mod, pos: CD.Position] = BEGIN
hash: INT;
IF mod.checkTable = NIL THEN mod.checkTable ← NEW[VerficationTableRec];
hash ← ABS[(pos.y * 10021 + pos.x) MOD verificationTableSize];
FOR pm: PlacedCheckList ← mod.checkTable[hash], pm.rest WHILE pm # NIL DO
IF pos.x = pm.first.pos.x AND pos.y = pm.first.pos.y THEN {
pm.first.duplicate ← TRUE;
RETURN;
};
ENDLOOP;
mod.checkTable[hash] ← CONS[[FALSE, pos], mod.checkTable[hash]];
END;
-- is an object already included in a design?
Owner: PROC [ob: CD.ObPtr] RETURNS [REF ANY] = BEGIN
RETURN[CDProperties.GetPropFromObject[ob, $Owner]];
END;
-- is a module still valid?
CheckValid: PUBLIC PROC[module: Module] RETURNS [Mod] = BEGIN
IF module = NIL OR ~module.valid OR ~ISTYPE[module.data, Mod] THEN
ERROR Error[InvalidModule];
RETURN[NARROW[module.data]];
END;
-- get a pointer to a cell object, dereferencing through imports if needed
DerefCell: PROC [obj: CD.ObPtr] RETURNS [CD.ObPtr] = BEGIN
IF obj = NIL THEN RETURN[NIL];
SELECT obj.p.objectType FROM
$Cell => RETURN [obj];
$Import => {
rp: CDImports.ReferencePtr ~ NARROW[obj.specificRef];
IF rp.boundApp = NIL THEN {
ERROR; -- import should be bound, if not something is wrong
}
ELSE
RETURN [rp.boundApp.ob];
};
ENDCASE => ERROR;
END;
-- Accessing Marks and Tiles
-- expands (stretches) the objects in a cell on a certain level (layer), does not copy the cell
DoExpand: PROC [ob: CD.ObPtr, design: CD.Design, level: CD.Level, place, min, max: INT, dir: StretchLines.LineDirections, amount: INT] RETURNS [Rope.ROPE] = BEGIN
cell: CD.CellPtr ← NARROW[ob.specificRef];
FOR a: CD.ApplicationList ← cell.contents, a.rest WHILE a # NIL DO
ap: CD.ApplicationPtr ← a.first;
urPos: CD.DesignPosition ← HighPosO[a.first];
llPos: CD.DesignPosition ← a.first.location;
IF ap.ob.level # level THEN LOOP;
-- stretch object
IF ((dir = $up OR dir = $down) AND llPos.y <= place AND urPos.y >= place AND llPos.x <= max AND urPos.x >= min) OR ((dir = $left OR dir = $right) AND llPos.x <= place AND urPos.x >= place AND llPos.y <= max AND urPos.y >= min) THEN {
msg: Rope.ROPE;
newOb: CD.ObPtr;
lower, upper: INT;
newDir: Stretch.Direction;
newPlace: INT;
rect, mapRect: CD.Rect;
-- map a vector down into the object
IF dir = $up OR dir = $right THEN { -- left is left side of stretch mark
lower ← place - 1; upper ← place + 1;
}
ELSE {
lower ← place + 1; upper ← place - 1; -- stretch mark is upside down
};
IF dir = $left OR dir = $right THEN
rect ← [x1: lower, x2: upper, y1: 0, y2: 0]
ELSE
rect ← [x1: 0, x2: 0, y1: lower, y2: upper];
mapRect ← CDOrient.DeMapRect[rect, ap.ob.size, ap.orientation, ap.location];
-- now use the vector to determine location to stretch at
IF mapRect.x1 = mapRect.x2 THEN {
newPlace ← (mapRect.y1 + mapRect.y2) / 2;
IF mapRect.y1 < mapRect.y2 THEN newDir ← $up ELSE newDir ← $down;
}
ELSE {
newPlace ← (mapRect.x1 + mapRect.x2) / 2;
IF mapRect.x1 < mapRect.x2 THEN newDir ← $right ELSE newDir ← $left;
};
[newOb, msg] ← Stretch.DoStretch[ap.ob, design, newPlace, newDir, amount];
IF newOb = NIL THEN RETURN[msg];
ap.ob ← newOb;
SELECT dir FROM
$down => ap.location.y ← ap.location.y - amount;
$left => ap.location.x ← ap.location.x - amount;
ENDCASE;
};
ENDLOOP;
RETURN[NIL];
END;
-- Copies a cell object and it's applications (recursively). Installs new cell into the directory of 'design'.
CopyCellTree: PROC [oldOb: CD.ObPtr, design: CD.Design] RETURNS [CD.ObPtr] = BEGIN
oldCrec: CD.CellPtr ~ NARROW[oldOb.specificRef];
newOb: CD.ObPtr ← NEW[CD.ObjectDefinition ← oldOb^];
newCrec: CD.CellPtr ← NEW[CD.CellRecord ← oldCrec^];
newOb.specificRef ← newCrec;
newOb.properties ← CDProperties.CopyProps[oldOb.properties];
newCrec.contents ← NIL;
FOR a: CD.ApplicationList ← oldCrec.contents, a.rest UNTIL a = NIL DO
ap: CD.ApplicationPtr ← NEW[CD.Application ← a.first^];
ap.properties ← CDProperties.CopyProps[a.first.properties];
IF ap.ob.p.objectType = $Cell THEN
ap.ob ← CopyCellTree[ap.ob, design];
newCrec.contents ← CONS[ap, newCrec.contents];
ENDLOOP;
IF design # NIL AND ~CDDirectory.Include[design, newOb] THEN ERROR;
RETURN[newOb];
END;
-- Creates a new cell by scanning through a cell, stretching along each stretch line found and processing any other sorts of parameterization found.
-- Creates a new cell only if there was parameterization of the cell. New cell (if any) is entered into the directory of the design passed (if any).
InstantiateCell: PROC [design: CD.Design, ob: CD.ObPtr, symbols: SymTab.Ref] RETURNS[CD.ObPtr] = BEGIN
AddDefaultParameters: PROC [tab: SymTab.Ref] = BEGIN
-- fill in default variables in symbol table
ExprRead.StoreInt[tab, "SizeX", ob.size.x/l];
ExprRead.StoreInt[tab, "SizeY", ob.size.x/l];
END;
EvalIntegerExpr: PROC [expr: Rope.ROPE, tab: SymTab.Ref] RETURNS [INT, Rope.ROPE] = BEGIN
none: BOOL;
msg: Rope.ROPENIL;
value: INT ← 0;
tv: AMTypes.TypedVariable;
[tv, msg, none] ← Interpreter.Evaluate[rope: expr, symTab: tab];
IF none AND msg = NIL THEN
msg ← "Expression does not return a value.";
IF msg = NIL THEN {
ref: REF ANY;
TRUSTED {ref ← AMBridge.SomeRefFromTV[tv];};
WITH ref SELECT FROM
i: REF INT => value ← i^;
ENDCASE => msg ← "Expression did not return an integer.";
};
RETURN[value, msg];
END;
stretchedOb, derefOb: CD.ObPtr;
alist: CD.ApplicationList;
copied: BOOLFALSE;
passType: StretchLines.LineTypes;
cellName: Rope.ROPE;
IF ob.p.objectType # $Cell AND ob.p.objectType # $Import THEN ERROR;
stretchedOb ← ob;
cellName ← NARROW[DerefCell[ob].specificRef, CD.CellPtr].name;
IF symbols = NIL THEN symbols ← SymTab.Create[];
AddDefaultParameters[symbols];
-- first pass: stretch cell, second pass: stretch rectangles on certain layers.
FOR pass: INT IN [0..1] DO
IF pass = 0 THEN passType ← pointed ELSE passType ← blunt;
derefOb ← DerefCell[stretchedOb];
alist ← NARROW[derefOb.specificRef, CD.CellPtr].contents;
WHILE alist # NIL DO
derefOb ← DerefCell[stretchedOb];
FOR alist ← NARROW[derefOb.specificRef, CD.CellPtr].contents, alist.rest UNTIL (alist = NIL OR (alist.first.ob.p.objectType = StretchLines.objAtom AND StretchLines.AptrToLine[alist.first].type = passType)) DO
NULL;
ENDLOOP;
IF alist # NIL -- alist.first.ob.p.objectType = StretchLines.objAtom -- THEN {
stretch along this stretch line
min, place, amount: INT;
evalError: Rope.ROPE;
line: REF StretchLines.LineData;
IF ~copied THEN {
-- OLD CODE:
-- copy the cell and start over
stretchedOb ← CopyCellTree[derefOb, design];
SELECT stretchedOb.p.objectType FROM
$Cell => stretchedOb ← CDDirectory.Another[stretchedOb, design, design];
$Import => stretchedOb ← CDImportsExtras.OneLevelIncludedCopy[stretchedOb, design];
ENDCASE => ERROR;
IF stretchedOb.p.objectType # $Cell THEN ERROR;
copied ← TRUE;
LOOP;
};
line ← StretchLines.AptrToLine[alist.first];
IF line.type # passType THEN ERROR;
SELECT line.direction FROM
up => {
min ← line.point.x;
place ← line.point.y;
};
down => {
min ← line.point.x;
place ← line.point.y;
};
left => {
min ← line.point.y;
place ← line.point.x;
};
right => {
min ← line.point.y;
place ← line.point.x;
};
ENDCASE => ERROR;
-- evaluate expression
[amount, evalError] ← EvalIntegerExpr[expr: line.label, tab: symbols];
-- remove the stretch mark from the cell so that we don't see it again or try to stretch it. Also, we don't want it to appear in resulting cell.
IF CDCells.RemoveApplication[design, stretchedOb, alist.first].removed = NIL THEN ERROR;
IF evalError # NIL THEN {
TerminalIO.WriteRope["Could not evaluate stretch amount '"];
TerminalIO.WriteRope[line.label];
TerminalIO.WriteRope["' for cell '"];
TerminalIO.WriteRope[cellName];
TerminalIO.WriteRope["', stretch line ignored.\n"];
TerminalIO.WriteRope["Error was '"];
TerminalIO.WriteRope[evalError];
TerminalIO.WriteRope["'.\n"];
}
ELSE {
IF line.type = pointed THEN {
new: CD.ObPtr;
msg: Rope.ROPE;
TerminalIO.WriteRope["Stretch amount"];
TerminalIO.WriteInt[amount];
TerminalIO.WriteRope[" for cell '"];
TerminalIO.WriteRope[crec.name];
TerminalIO.WriteRope["'.\n"];
[new, msg] ← Stretch.DoStretch[stretchedOb, design, place, line.direction, amount * l];
IF new = NIL THEN {
TerminalIO.WriteRope["Could not stretch cell '"];
TerminalIO.WriteRope[cellName];
TerminalIO.WriteRope["' by amount '"];
TerminalIO.WriteRope[line.label];
TerminalIO.WriteRope["', error was '"];
TerminalIO.WriteRope[msg];
TerminalIO.WriteRope["'\n"];
}
ELSE
stretchedOb ← new;
}
ELSE {
msg: Rope.ROPE;
TerminalIO.WriteRope["Expand amount"];
TerminalIO.WriteInt[amount];
TerminalIO.WriteRope[" in cell '"];
TerminalIO.WriteRope[cellName];
TerminalIO.WriteRope["'.\n"];
msg ← DoExpand[stretchedOb, design, line.level, place, min, min + line.length, line.direction, amount * l];
IF msg # NIL THEN {
TerminalIO.WriteRope["Could not expand part of cell '"];
TerminalIO.WriteRope[cellName];
TerminalIO.WriteRope["' by amount '"];
TerminalIO.WriteRope[line.label];
TerminalIO.WriteRope["', error was '"];
TerminalIO.WriteRope[msg];
TerminalIO.WriteRope["'\n"];
}
};
}
};
ENDLOOP;
ENDLOOP;
RETURN[stretchedOb];
END;
-- Convert a ChipNDale cell into a tile, copy only if needed. If copied, enter into design specified (if any).
CellIntoTile: PROC [design: CD.Design, obj: CD.ObPtr, fast: BOOLFALSE, parameters: SymTab.Ref ← NIL, moduleWithDefaultMarks: Mod ← NIL] RETURNS [Tile] = BEGIN
tile: Tile ← NEW[TileRec];
-- stretch the cell
IF ~fast THEN obj ← InstantiateCell[design, obj, parameters];
tile.obj ← obj;
-- find marks in the cell
IF ~fast THEN {
deref: CD.ObPtr ← DerefCell[obj];
FOR ml: LIST OF AlignmentMarks.Mark ← AlignmentMarks.FindList[deref], ml.rest WHILE ml#NIL DO
mark: Mark ← NEW[MarkRec];
mark.name ← ml.first.name;
mark.pos ← ml.first.pos;
StoreMark[tile, mark];
ENDLOOP;
FOR cl: LIST OF CD.Position ← CheckMarks.FindList[deref], cl.rest WHILE cl#NIL DO
tile.checkList ← CONS[cl.first, tile.checkList];
ENDLOOP;
};
DefaultMarks[tile, moduleWithDefaultMarks];
RETURN[tile]
END;
-- Combine the user parameters (from the context variable) with any additional parameters supplied by the generator, with the latter overriding. Then take the parameters from the tile set and put them in the table if they are not already there.
CombineParameters: PUBLIC PROC [context: Context, generatorParameters: SymTab.Ref] = BEGIN
IF context = NIL THEN ERROR Error[BadArgs];
context.parameters ← ExprRead.CombineTabs[context.parameters, generatorParameters];
IF context.tiles # NIL THEN
context.parameters ← ExprRead.CombineTabs[TileSetExpressions.GetParams[context.tiles], context.parameters]
END;
-- retrieve a cell given it's name
InstantiateTile: PUBLIC PROC [context: Context, cellName: Rope.ROPE] RETURNS [Tile] = BEGIN
obj: CD.ObPtr;
found: BOOL;
IF context.tiles = NIL THEN ERROR Error[NoSuchTileSet];
IF context.eventualOwner = context.tiles THEN {
[found, obj] ← CDDirectory.Fetch[context.eventualOwner, cellName];
IF ~found THEN RETURN[NIL];
}
ELSE {
-- start of hack, until Christian fixes CDImports.GetReference so that it returns NIL if the importee does not exist. AFTER THAT VERSION IS RELEASED THIS CODE SHOULD BE CHANGED. ...... Bob Mayo, 9/84.
[found, obj] ← CDDirectory.Fetch[context.tiles, cellName];
IF ~found THEN RETURN[NIL];
-- end of hack
obj ← CDImports.GetReference[context.eventualOwner, cellName, context.tiles.name];
};
IF obj = NIL THEN RETURN[NIL];
RETURN[CellIntoTile[context.eventualOwner, obj, FALSE, context.parameters]]
END;
-- Accessing Designs
-- load design from a file
ReadTileSet: PUBLIC PROC [fileName: Rope.ROPE, userParams: SymTab.Ref ← NIL] RETURNS [TileSet, Technology, SymTab.Ref] = BEGIN
params: SymTab.Ref ← NIL;
tiles: TileSet ← NIL;
tiles ← CDIO.ReadDesign[from: fileName];
IF tiles # NIL THEN params ← TileSetExpressions.GetParams[tiles];
params ← ExprRead.CombineTabs[params, userParams];
IF tiles = NIL THEN RETURN[NIL, NIL, params];
RETURN[tiles, tiles.technology, params];
END;
-- also returns an empty PlacedTile with which to align things via AlignTile
NewModule: PUBLIC PROC [context: Context] RETURNS [Module, PlacedTile] = BEGIN
pc: PlacedTile ← NEW[PlacedTileRec];
module: Module ← NEW[ModuleRec];
mod: Mod ← NEW[ModRec];
mod.current ← mod.topCell ← NEW[ShiftedCellRec];
mod.eventualOwner ← context.eventualOwner;
module.data ← mod;
module.valid ← TRUE;
pc.pos ← [0, 0];
pc.tile ← NEW[TileRec];
pc.tile.obj ← NEW[CD.ObjectDefinition ← [p: NIL, size: [0, 0]]];
DefaultMarks[pc.tile];
pc.owner ← module;
RETURN[module, pc];
END;
-- Enter a cell and all of its subcells (recursively) into the directory of a design.
-- Returns TRUE if it worked, FALSE if it failed. If it fails, only some of the cells may have been entered.
EnterCellTreeIntoDirectory: PUBLIC PROC [design: CD.Design, obj: CD.ObPtr] RETURNS [BOOL] = BEGIN
cell: CD.CellPtr;
found: BOOL;
foundObj: CD.ObPtr;
cell ← NARROW[obj.specificRef];
[found, foundObj] ← CDDirectory.Fetch[design, cell.name];
IF ~found OR foundObj # obj THEN {
IF Owner[obj] # NIL THEN RETURN[FALSE];
IF ~CDDirectory.Include[design, obj] THEN RETURN[FALSE];
};
FOR w: CD.ApplicationList ← cell.contents, w.rest WHILE w#NIL DO
IF w.first.ob.p.objectType = $Cell THEN {
IF ~EnterCellTreeIntoDirectory[design, w.first.ob] THEN RETURN[FALSE];
};
ENDLOOP;
RETURN[TRUE];
END;
-- Recursively turn a ShiftedCell into a legal ChipNDale cell object. TRASHES THE ORIGINAL SHIFTED CELL. The position returned is where the new cell should be placed in order for the shifting to have no effect. If design # NIL THEN new cells are entered into that design's directory.
ShiftedCellIntoCell: PROC [design: CD.Design, shifted: ShiftedCell, name: Rope.ROPE ← "UnnamedParquetCell", applicationsPerScreenDot: INT ← -1] RETURNS [CD.ObPtr, CD.Position] = BEGIN
obj: CD.ObPtr ← CDCells.CreateEmptyCell[];
cell: CD.CellPtr;
oldLL: CD.Position;
numAps: INT ← 0;
cell ← NARROW[obj.specificRef];
-- convert children and add them in
FOR sc: LIST OF ShiftedCell ← shifted.children, sc.rest WHILE sc # NIL DO
ap: CD.ApplicationPtr ← NEW[CD.Application];
[ap.ob, ap.location] ← ShiftedCellIntoCell[design, sc.first, sc.first.name, applicationsPerScreenDot];
ap.orientation ← CD.original; ap.properties ← NIL;
ShiftedCellIncludeApplication[shifted, ap];
IF applicationsPerScreenDot > 0 THEN
TerminalIO.WriteRope["c"];
ENDLOOP;
-- relocate module so that ll corner is at (0, 0)
oldLL ← shifted.llCorner;
FOR w: CD.ApplicationList ← shifted.contents, w.rest WHILE w#NIL DO
w.first.location ← CDBasics.SubPoints[w.first.location, shifted.llCorner];
w.first.selected ← FALSE;
numAps ← numAps + 1;
IF applicationsPerScreenDot > 0 AND numAps MOD applicationsPerScreenDot = 0 THEN
TerminalIO.WriteRope["."];
ENDLOOP;
shifted.urCorner ← CDBasics.SubPoints[shifted.urCorner, shifted.llCorner];
shifted.llCorner ← [0, 0];
-- make into an object
obj.size ← shifted.urCorner;
cell.contents ← shifted.contents;
cell.name ← name;
cell.ir ← CDApplications.BoundingRectI[cell.contents];
IF design # NIL AND ~CDDirectory.Include[design, obj, name] THEN ERROR;
RETURN[obj, oldLL];
END;
-- Turn a module into a Cell object, does not make a copy of anything in the module. TRASHES THE ORIGINAL MODULE, except for the default marks, which are relocated.
-- If module.eventualOwner # NIL THEN the cells in the module are entered into that design's directory.
ModuleIntoCell: PUBLIC PROC [module: Module, name: Rope.ROPENIL, applicationsPerScreenDot: INT ← -1] RETURNS [CD.ObPtr] = BEGIN
mod: Mod ← CheckValid[module];
oldLL: CD.Position;
obj: CD.ObPtr;
FixOrphans: SymTab.EachPairAction -- [key: Key, val: Val] RETURNS [quit: BOOL] -- = BEGIN
sCell: ShiftedCell ← NARROW[val];
IF sCell.parent = NIL THEN {
sCell.parent ← mod.topCell;
mod.topCell.children ← CONS[sCell, mod.topCell.children];
};
RETURN[FALSE];
END;
IF mod.cellTab # NIL THEN [] ← SymTab.Pairs[mod.cellTab, FixOrphans];
[obj, oldLL] ← ShiftedCellIntoCell[mod.eventualOwner, mod.topCell, name, applicationsPerScreenDot]; -- trashes mod.topCell
-- shift our default marks by the same amount that the contents shifted
IF mod.ul # NIL THEN mod.ul^ ← CDBasics.SubPoints[mod.ul^, oldLL];
IF mod.ur # NIL THEN mod.ur^ ← CDBasics.SubPoints[mod.ur^, oldLL];
IF mod.ll # NIL THEN mod.ll^ ← CDBasics.SubPoints[mod.ll^, oldLL];
IF mod.lr # NIL THEN mod.lr^ ← CDBasics.SubPoints[mod.lr^, oldLL];
IF mod.top # NIL THEN mod.top^ ← CDBasics.SubPoints[mod.top^, oldLL];
IF mod.bottom # NIL THEN mod.bottom^ ← CDBasics.SubPoints[mod.bottom^, oldLL];
IF mod.left # NIL THEN mod.left^ ← CDBasics.SubPoints[mod.left^, oldLL];
IF mod.right # NIL THEN mod.right^ ← CDBasics.SubPoints[mod.right^, oldLL];
-- just for safety's sake, clear out some stuff
module.valid ← FALSE;
mod.topCell ← NIL;
mod.current ← NIL;
mod.cellTab ← NIL;
mod.checkTable ← NIL;
RETURN[obj];
END;
-- turn a module into a design. TRASHES THE ORIGINAL MODULE.
ModuleIntoDesign: PUBLIC PROC [module: Module, name: Rope.ROPENIL, applicationsPerScreenDot: INT ← -1] RETURNS [CD.Design] = BEGIN
mod: Mod ← CheckValid[module];
obj: CD.ObPtr;
newDesign: CD.Design ← CDOps.CreateDesign[mod.technology];
obj ← ModuleIntoCell[module, name, applicationsPerScreenDot];
-- put it into a design
IF ~EnterCellTreeIntoDirectory[newDesign, obj] THEN ERROR;
CDOps.AddAnObject[newDesign, obj, [0, 0]];
RETURN[newDesign];
END;
-- turn a module into a tile, TRASHES THE ORIGINAL MODULE.
ModuleIntoTile: PUBLIC PROC [module: Module, name: Rope.ROPE, context: Context, fast: BOOL, bbox: BOOLFALSE] RETURNS [numErrors: INT, tile: Tile] = BEGIN
mod: Mod ← CheckValid[module];
obj: CD.ObPtr;
IF ~fast THEN
numErrors ← CheckModule[module]
ELSE
numErrors ← 0;
IF bbox THEN
mod.top ← mod.bottom ← mod.left ← mod.right ← mod.ul ← mod.ur ← mod.ll ← mod.lr ← NIL;
obj ← ModuleIntoCell[module, name];
tile ← CellIntoTile[mod.eventualOwner, obj, fast, context.parameters, mod];
tile.inheritedErrors ← numErrors;
RETURN[numErrors, tile];
END;
-- Tile Placement Procedures
-- Place a cell next to another, using the "top", "bottom", "left", and "right" alignment marks.
AdjacentTile: PUBLIC PROC [oldTile: PlacedTile, newTile: Tile, dir: Direction, orient: Orientation] RETURNS [PlacedTile] = BEGIN
l1, r1, t1, b1, l2, r2, t2, b2: Mark;
mark1, mark2: Mark;
[l1, r1, t1, b1, , , , ] ← OrientStdMark[oldTile.tile, oldTile.orient];
[l2, r2, t2, b2, , , , ] ← OrientStdMark[newTile, orient];
SELECT dir FROM
above => BEGIN mark1 ← t1; mark2 ← b2 END;
below => BEGIN mark1 ← b1; mark2 ← t2 END;
leftOf => BEGIN mark1 ← l1; mark2 ← r2 END;
rightOf => BEGIN mark1 ← r1; mark2 ← l2 END;
ENDCASE;
RETURN[AlignTile[oldTile, mark1, newTile, mark2, orient]];
END;
-- Place a cell next to another, using the "ll" and "lr" alignment marks (for rightOf and leftOf), or the "ll" and "ul" alignment marks (for above and below).
AdjacentTileCorners: PUBLIC PROC [oldTile: PlacedTile, newTile: Tile, dir: Direction, orient: Orientation] RETURNS [PlacedTile] = BEGIN
ul1, lr1, ll1, ul2, lr2, ll2: Mark;
mark1, mark2: Mark;
[ul: ul1, lr: lr1, ll: ll1] ← OrientStdMark[oldTile.tile, oldTile.orient];
[ul: ul2, lr: lr2, ll: ll2] ← OrientStdMark[newTile, orient];
SELECT dir FROM
above => BEGIN mark1 ← ul1; mark2 ← ll2 END;
below => BEGIN mark1 ← ll1; mark2 ← ul2 END;
leftOf => BEGIN mark1 ← ll1; mark2 ← lr2 END;
rightOf => BEGIN mark1 ← lr1; mark2 ← ll2 END;
ENDCASE;
RETURN[AlignTile[oldTile, mark1, newTile, mark2, orient]];
END;
PlacedTileLoc: PUBLIC PROC [tile: PlacedTile] RETURNS [xmin, ymin, xmax, ymax: INT] = BEGIN
mappedSize: CD.Position;
mappedSize ← CDOrient.OrientedSize[tile.tile.obj.size, tile.orient];
RETURN[tile.pos.x, tile.pos.y, tile.pos.x + mappedSize.x, tile.pos.y + mappedSize.y];
END;
-- place a cell into a design using alignment marks
AlignTile: PUBLIC PROC [oldTile: PlacedTile, oldMark: Mark, newTile: Tile, newMark: Mark, orient: Orientation ← CD.original] RETURNS [PlacedTile] = BEGIN
pos: Position;
pos ← CDBasics.SubPoints[PlacedMarkLoc[oldTile, oldMark], MapPoint[newMark.pos, newTile.obj.size, orient]];
RETURN[PlaceTile[into: oldTile.owner, tile: newTile, lowerLeftCorner: pos, orient: orient]];
END;
-- Alignment Marks
-- place a new alignment mark into a design on top of an existing subcell's mark
AlignMark: PUBLIC PROC [oldTile: PlacedTile, oldMark: Mark, newMarkName: Rope.ROPE] = BEGIN
PlaceMark[oldTile.owner, newMarkName, PlacedMarkLoc[oldTile, oldMark]];
END;
-- Hierarchy
-- Find a block, or create a new one if it is not there.
BlockFor: PROC [mod: Mod, blockName: Rope.ROPE] RETURNS [ShiftedCell] = BEGIN
found: BOOLFALSE;
ref: REF ANY;
cell: ShiftedCell;
IF blockName = NIL THEN {
IF mod.topCell = NIL THEN ERROR;
RETURN[mod.topCell];
};
IF mod.cellTab = NIL THEN mod.cellTab ← SymTab.Create[13];
[found, ref] ← SymTab.Fetch[mod.cellTab, blockName];
IF found THEN {
cell ← NARROW[ref];
RETURN[cell];
}
ELSE {
cell ← NEW[ShiftedCellRec];
cell.name ← blockName;
IF ~SymTab.Store[mod.cellTab, blockName, cell] THEN ERROR;
RETURN[cell];
};
END;
-- Switch to a new block in the hierarchy. Subsequent cells will appear in this block. When the generator is done, each block will be put into a chipndale cell. blockName does not have to be declared: if that block doesn't exist a new one is created. The topmost block can be switched to by passing a NIL blockName.
SwitchBlock: PUBLIC PROC [module: Module, blockName: Rope.ROPE] = BEGIN
mod: Mod ← CheckValid[module];
mod.current ← BlockFor[mod, blockName];
END;
-- Returns the last rope passed to SwitchBlock for this module.
GetCurrentBlock: PUBLIC PROC [module: Module] RETURNS [Rope.ROPE] = BEGIN
mod: Mod ← CheckValid[module];
RETURN[mod.current.name];
END;
-- Nest one block inside of another. If the block is already nested or if the nesting creates a circularity then the call is ignored and FALSE is returned. The topmost block is refered to by NIL. Unnested blocks will be put in the topmost block.
NestBlock: PUBLIC PROC [module: Module, child, parent: Rope.ROPE] RETURNS [done: BOOL] = BEGIN
mod: Mod ← CheckValid[module];
par, chi: ShiftedCell;
chi ← BlockFor[mod, child];
par ← BlockFor[mod, parent];
IF chi.parent # NIL THEN RETURN[FALSE]; -- already nested
FOR b: ShiftedCell ← par, par.parent WHILE b # NIL DO
IF b = chi THEN RETURN[FALSE]; -- would create loop in hierarchy
ENDLOOP;
chi.parent ← par;
par.children ← CONS[chi, par.children];
RETURN[TRUE];
END;
-- Checking procedures
VerTabProc: TYPE = PROC [pc: PlacedCheck] RETURNS [quit: BOOLFALSE];
-- list check marks in the VerficationTable. Returns TRUE if any callback proc returned TRUE.
EnumVerTab: PROC [verTab: VerficationTable, proc: VerTabProc] RETURNS [BOOL] ~ BEGIN
IF verTab = NIL THEN RETURN[FALSE];
FOR i: INT IN [0..verificationTableSize) DO
FOR c: PlacedCheckList ← verTab[i], c.rest WHILE c # NIL DO
IF proc[c.first] THEN RETURN[TRUE];
ENDLOOP;
ENDLOOP;
RETURN[FALSE];
END;
-- Checks alignment marks to make sure that the ones placed by the tiler match up properly, as indicated by the syntax of the mark name.
-- Return result is the number of errors found.
CheckModule: PUBLIC PROC [module: Module] RETURNS [INT] = BEGIN
mod: Mod ← CheckValid[module];
errors: INT ← 0;
PaintErrorRect: PROCEDURE [mod: Mod, pos: Position] = BEGIN
appl: CD.ApplicationPtr ← CDApplications.NewApplicationI[
ob: CDRects.CreateBareRect[[errorSize, errorSize], CD.highLightError],
location: [pos.x - errorSize / 2, pos.y - errorSize / 2],
orientation: IdentityOrient];
CDProperties.PutPropOnApplication[onto: appl, prop: $SignalName, val: Rope.Concat["Parquet Check-", "Mark MisAlignment"]]; -- don't pass a constant rope because they are really of type Text and ChipNDale can't copy those properly
ShiftedCellIncludeApplication[mod.topCell, appl];
END;
CheckMatched: VerTabProc -- PROC [pc: PlacedCheck] RETURNS [quit: BOOL ← FALSE] -- = BEGIN
IF ~pc.duplicate THEN BEGIN
errors ← errors + 1;
PaintErrorRect[mod, pc.pos];
END;
END;
[] ← EnumVerTab[mod.checkTable, CheckMatched];
RETURN[errors + mod.inheritedErrors];
END;
-- Low-level stuff
-- cell operations
-- place a tile into a module at absolute coordinates
PlaceTile: PUBLIC PROC [into: Module, tile: Tile, lowerLeftCorner: Position, orient: Orientation ← CD.original] RETURNS [PlacedTile] = BEGIN
mod: Mod ← CheckValid[into];
pc: PlacedTile ← NEW[PlacedTileRec];
ap: CD.ApplicationPtr ← NEW[CD.Application];
pc.owner ← into;
pc.tile ← tile;
pc.orient ← orient;
pc.pos ← lowerLeftCorner;
ap.ob ← tile.obj;
ap.location ← lowerLeftCorner;
ap.orientation ← orient;
ShiftedCellIncludeApplication[mod.current, ap];
-- add in inherited errors from erroneous tiles
mod.inheritedErrors ← mod.inheritedErrors + tile.inheritedErrors;
-- export marks
FOR m: MarkList ← tile.exportMarks, m.rest WHILE m # NIL DO
pos: CD.Position;
pos ← CDBasics.AddPoints[lowerLeftCorner,
CDOrient.MapPoint[
pointInCell: m.first.pos,
cellSize: tile.obj.size,
cellInstOrient: orient]
];
PlaceMark[into, m.first.name, pos];
ENDLOOP;
-- default alignment marks
SetModDefaults[mod, pc];
-- record check mark positions for later verification 
FOR c: CheckList ← tile.checkList, c.rest WHILE c # NIL DO
pos: CD.Position;
pos ← CDBasics.AddPoints[lowerLeftCorner,
CDOrient.MapPoint[
pointInCell: c.first,
cellSize: tile.obj.size,
cellInstOrient: orient]
];
AddCheck[mod, pos];
ENDLOOP;
RETURN[pc];
END;
-- find out the size of a cell
TileSize: PUBLIC PROC [tile: Tile] RETURNS [Position] = BEGIN
RETURN[tile.obj.size];
END;
-- mark operations
-- Add a mark to a Module at absolute coordinates. Always goes in the top level of the module.
PlaceCheckMark: PUBLIC PROC [into: Module, markPos: Position] = BEGIN
mod: Mod ← CheckValid[into];
ShiftedCellIncludeApplication[mod.topCell, CheckMarks.MakeCheckAptr[markPos]];
END;
-- Add a mark to a Module at absolute coordinates. Always goes in the top level of the module.
PlaceMark: PUBLIC PROC [into: Module, markName: Rope.ROPE, markPos: Position] = BEGIN
mod: Mod ← CheckValid[into];
ShiftedCellIncludeApplication[mod.topCell, AlignmentMarks.MakeMarkAptr[[name: markName, pos: markPos]] ];
END;
-- Add a stretch line to a Module at absolute coordinates. Always goes in the top level of the module.
PlaceStretch: PUBLIC PROC [into: Module, data: StretchData] = BEGIN
mod: Mod ← CheckValid[into];
aptr: CD.ApplicationPtr ← StretchLines.MakeLineAptr[data, TRUE];
IF aptr = NIL THEN ERROR Error[BadArgs];
ShiftedCellIncludeApplication[mod.topCell, aptr];
END;
-- General loophole: place arbitrary application into Module
PlaceApplication: PUBLIC PROC [into: Module, aptr: CD.ApplicationPtr] = BEGIN
mod: Mod ← CheckValid[into];
ShiftedCellIncludeApplication[mod.topCell, aptr];
END;
END.
Edited on February 8, 1985 1:28:14 pm PST, by Jacobi
Bug correction: cell.ir ← CDApplications.BoundingRectI[cell.contents]; included in ShiftedCellIntoCell