File: ParquetImpl.mesa   
Copyright © 1984 by Xerox Corporation. All rights reserved.
Created by: Mayo, July 16, 1984 4:44:25 pm PDT
Last Edited by: Mayo, August 6, 1984 1:57:54 pm PDT
DIRECTORY
Parquet,
TilerMenu USING [Register],
AlignmentMarks USING [FindOrigin, FindName, markAtom],
CheckMarks USING [Find, atom],
StretchLines,
Stretch USING [Direction, DoStretch],
Commander USING [CommandProc, Register],
TerminalIO USING [WriteRope, WriteInt, RequestRope, UserAbort, UserSaysYes],
ExprRead,
CDIO USING [ReadDesign, WriteDesign],
CDSequencer USING [Command, ImplementCommand],
CD,
CDApplications USING [NewApplicationI, HighPosO],
CDRects USING [CreateBareRect],
CDDirectory USING [Fetch, Include],
CDInline USING [AddPoints, SubPoints, MinPoint, MaxPoint],
CDOps USING [CreateDesign, AddAnObject],
CDOrient USING [ConcentrateOnRotate90, IncludesOddRot90, rotate180, rotate270, IncludesMirrorX, MirrorX, MapPosition, DeMapRect],
CDCells USING [CreateEmptyCell, RemoveApplication],
CDProperties USING [PutPropOnApplication, CopyProps],
Rope USING [ROPE, Equal, Concat],
PrincOpsUtils USING [BITAND],
AMBridge USING [SomeRefFromTV, TVForReferent],
AMTypes USING [TypedVariable, TV],
Interpreter USING [Evaluate],
FS USING [FileInfo, Error],
SymTab USING [Ref, Create, Fetch, Store, Pairs, EachPairAction];
ParquetImpl: CEDAR PROGRAM    
IMPORTS CD, AlignmentMarks, CheckMarks, TilerMenu, Stretch, ExprRead, Commander, TerminalIO, CDIO, CDDirectory, CDOps, CDInline, Rope, CDSequencer, CDApplications, CDRects, CDOrient, CDCells, CDProperties, SymTab, StretchLines, Interpreter, AMBridge, FS EXPORTS Parquet =  
BEGIN OPEN Parquet;
Error: PUBLIC ERROR [ec: ErrorCode] = CODE;
ShiftedCell: TYPE = REF ShiftedCellRec;
ShiftedCellRec: TYPE = RECORD [ -- like a ChipNDale cell but origin is not at lower-left corner
llCorner: CD.Position ← [LAST[CD.Number], LAST[CD.Number]],
urCorner: CD.Position ← [FIRST[CD.Number], FIRST[CD.Number]],
contents: CD.ApplicationList ← NIL,
name: Rope.ROPENIL,
parent: ShiftedCell ← NIL,
children: LIST OF ShiftedCell ← NIL
];
ModuleRef: TYPE = REF ModuleRec;
-- we guarentee that nothing in this record is in any design's directory, and nothing is referenced by two ModuleRecs.
ModuleRec: TYPE = RECORD [
technology: Technology ← ,
current: ShiftedCell ← NIL,
top: ShiftedCell ← NEW[ShiftedCellRec],
cellTab: SymTab.Ref ← SymTab.Create[97],
checkTable: VerficationTable
];
generatorTable: SymTab.Ref;
verificationTableSize: INT = 5047;  -- hash table size
VerficationTable: TYPE = REF VerficationTableRec ← NEW[VerficationTableRec];
VerficationTableRec: TYPE = ARRAY [0..verificationTableSize) OF PlacedCheckList ← ALL[NIL];
PlacedCheckList: TYPE = LIST OF PlacedCheck;
PlacedCheck: TYPE = RECORD [
duplicate: BOOLFALSE,
pos: CD.Position
];
errorSize: INT = 20;
markScale: INT = 2;
markWidth: INT = 16;
markHeight: INT = 6;
arrowSize: INT = 4;
-- structures used to deal with 4 standard marks
standardMarksArray: TYPE = ARRAY NAT[1..4] OF RECORD [
pos: Position,  -- location on a prototypical 2 X 2 cell
name: Rope.ROPE
];
standardMarks: standardMarksArray ←   
[ [[0, 1], "left"], [[2, 1], "right"], [[1, 0], "bottom"], [[1, 2], "top"] ];
-- [[0, 0], "llCorner"], [[0, 2], "ulCorner"], [[2, 0], "lrCorner"], [[2, 2], "urCorner"]
-- Command Procs & internal utilities
-- register a module generator with the tiler
RegisterGenerator: PUBLIC PROC[generatorName: Rope.ROPE, proc: ModuleGeneratorProc] RETURNS [BOOL] = BEGIN
ptr: REF ModuleGeneratorProc ~ NEW[ModuleGeneratorProc ← proc];
RETURN[SymTab.Store[generatorTable, generatorName, ptr]];
END;
-- apply an orientation to a point
MapPoint: PROC [p: Position, cellSize: Position, orient: Orientation] RETURNS [Position] = INLINE BEGIN
RETURN[CDOrient.MapPosition[
itemInCell: [x1: p.x, y1: p.y, x2: p.x, y2: p.y],
cellSize: cellSize, cellInstOrient: orient]];
END;
-- check a command for wierdness as it is possible to screw up the TIP table.
OKCommand: PROC [comm: CDSequencer.Command] RETURNS[BOOL] = BEGIN
IF comm.pos.x > 10000 OR comm.pos.y > 10000 OR comm.pos.x < -10000 OR comm.pos.y < -10000 THEN BEGIN
TerminalIO.WriteRope["Warning: Huge position passed to Parquet from ChipNDale, maybe the TIP table is screwed up?\n"];
TerminalIO.WriteRope["comm.pos.x ="]; TerminalIO.WriteInt[comm.pos.x];
TerminalIO.WriteRope[" comm.pos.y ="]; TerminalIO.WriteInt[comm.pos.y];
[] ← TerminalIO.RequestRope["\nOperation will be aborted, type return ......"];
RETURN[FALSE];
END;
RETURN[TRUE];
END;
ShiftedCellIncludeApplication: PROC [shifted: ShiftedCell, aptr: CD.ApplicationPtr] = BEGIN
shifted.llCorner ← CDInline.MinPoint[shifted.llCorner, aptr.location];
shifted.urCorner ← CDInline.MaxPoint[shifted.urCorner, CDInline.AddPoints[aptr.location, aptr.ob.size]];
shifted.contents ← CONS[aptr, shifted.contents];
END;
-- add a check mark (point) to the verification table.
AddVerifyTab: PROC [verTab: VerficationTable, pos: CD.Position] = BEGIN
hash: INT;
hash ← ABS[(pos.y * 10021 + pos.x) MOD verificationTableSize];
FOR pm: PlacedCheckList ← verTab[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;
verTab[hash] ← CONS[[FALSE, pos], verTab[hash]];
END;
-- Accessing Marks and Tiles
-- retrieve a mark given it's name
GetMark: PUBLIC PROC [fromTile: Tile, markName: Rope.ROPE] RETURNS [Mark] = BEGIN
FOR marks: MarkList ← fromTile.markList, marks.rest WHILE marks#NIL DO
IF Rope.Equal[markName, marks.first.name] THEN RETURN[marks.first];
ENDLOOP;
RETURN[NIL];
END;
-- expands the objects in a cell on a certain level, does not copy the cell
DoExpand: PROC [ob: CD.ObPtr, 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 ← CDApplications.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, 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)
CopyCellTree: PROC [oldOb: CD.ObPtr] 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]
ELSE {
ap.ob ← NEW[CD.ObjectDefinition ← ap.ob^];
ap.ob.properties ← CDProperties.CopyProps[ap.ob.properties];
};
newCrec.contents ← CONS[ap, newCrec.contents];
ENDLOOP;
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.
-- Always creates a new cell, and neither the new cell nor its children is not entered into any design.
InstantiateCell: PROC [ob: CD.ObPtr, symbols: SymTab.Ref] RETURNS[CD.ObPtr] = BEGIN
AddDefaultParameters: PROC [tab: SymTab.Ref] = BEGIN
-- fill in default variables in symbol table
x, y: AMTypes.TV;
TRUSTED {
x ← AMBridge.TVForReferent[NEW[INT ← ob.size.x/CD.lambda]];
y ← AMBridge.TVForReferent[NEW[INT ← ob.size.y/CD.lambda]];
};
[] ← SymTab.Store[tab, "SizeX", x];
[] ← SymTab.Store[tab, "SizeY", y];
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: CD.ObPtr;
crec: CD.CellPtr;
alist: CD.ApplicationList;
copiedAlready: BOOLFALSE;
passType: StretchLines.LineTypes;
IF ob.p.objectType # $Cell THEN RETURN[ob];
stretchedOb ← CopyCellTree[ob];
crec ← NARROW[stretchedOb.specificRef];
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;
alist ← crec.contents;
WHILE alist # NIL DO
FOR alist ← crec.contents, alist.rest UNTIL (alist = NIL OR (alist.first.ob.p.objectType = StretchLines.objAtom AND StretchLines.Fetch[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: StretchLines.LineData;
line ← StretchLines.Fetch[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.
[, ] ← CDCells.RemoveApplication[NIL, stretchedOb, alist.first];
IF evalError # NIL THEN {
TerminalIO.WriteRope["Could not evaluate stretch amount '"];
TerminalIO.WriteRope[line.label];
TerminalIO.WriteRope["' for cell '"];
TerminalIO.WriteRope[crec.name];
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, place, line.direction, amount * CD.lambda];
IF new = NIL THEN {
TerminalIO.WriteRope["Could not stretch cell '"];
TerminalIO.WriteRope[crec.name];
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[crec.name];
TerminalIO.WriteRope["'.\n"];
msg ← DoExpand[stretchedOb, line.level, place, min, min + line.length, line.direction, amount * CD.lambda];
IF msg # NIL THEN {
TerminalIO.WriteRope["Could not expand part of cell '"];
TerminalIO.WriteRope[crec.name];
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
CellIntoTile: PROC [cell: CD.ObPtr, parameters: SymTab.Ref ← NIL] RETURNS [Tile] = BEGIN
StoreMark: PROC [t: Tile, m: Mark] = BEGIN
m.owner ← LOOPHOLE[t]; -- a unique id for the tile
t.markList ← CONS[m, t.markList];
SELECT TRUE FROM
Rope.Equal[m.name, "left"] => t.left ← m;
Rope.Equal[m.name, "right"] => t.right ← m;
Rope.Equal[m.name, "top"] => t.top ← m;
Rope.Equal[m.name, "bottom"] => t.bottom ← m;
ENDCASE;
END;
crec: CD.CellPtr;
tile: Tile ← NEW[TileRec];
-- stretch the cell
cell ← InstantiateCell[cell, parameters];
crec ← NARROW[cell.specificRef];
tile.obj ← cell;
-- find marks in the cell
FOR alist: CD.ApplicationList ← crec.contents, alist.rest WHILE alist#NIL DO
SELECT alist.first.ob.p.objectType FROM
= CheckMarks.atom => {
pos: CD.Position;
pos ← CheckMarks.Find[alist.first];
tile.checkList ← CONS[pos, tile.checkList];
};
= AlignmentMarks.markAtom => {
mark: Mark ← NEW[MarkRec];
mark.name ← AlignmentMarks.FindName[alist.first];
mark.pos ← AlignmentMarks.FindOrigin[alist.first];
StoreMark[tile, mark];
};
ENDCASE;
ENDLOOP;
-- fabricate default marks
FOR i: INT IN [1..4] DO
IF (tile.left = NIL AND Rope.Equal[standardMarks[i].name, "left"]) OR
(tile.right = NIL AND Rope.Equal[standardMarks[i].name, "right"]) OR
(tile.top = NIL AND Rope.Equal[standardMarks[i].name, "top"]) OR
(tile.bottom = NIL AND Rope.Equal[standardMarks[i].name, "bottom"]) THEN BEGIN
mark: Mark ← NEW[MarkRec];
mark.name ← standardMarks[i].name;
mark.pos.x ← ((cell.size.x * standardMarks[i].pos.x) / 2);
mark.pos.y ← ((cell.size.y * standardMarks[i].pos.y) / 2);
StoreMark[tile, mark];
END;
ENDLOOP;
RETURN[tile]
END;
-- retrieve a cell given it's name
InstantiateTile: PUBLIC PROC [fromDesign: Design, cellName: Rope.ROPE, parameters: SymTab.Ref ← NIL] RETURNS [Tile] = BEGIN
obj: CD.ObPtr;
found: BOOL;
[found, obj] ← CDDirectory.Fetch[fromDesign, cellName];
IF ~found THEN RETURN[NIL];
RETURN[CellIntoTile[obj, parameters]]
END;
-- rotate a mark
OrientMark: PUBLIC PROC [markName: Rope.ROPE, orient: Orientation] RETURNS [Rope.ROPE] = BEGIN
FOR i: INT IN [1..4] DO
IF Rope.Equal[markName, standardMarks[i].name] THEN BEGIN
oldPos, newPos: Position;
simpleOrient: Orientation ← CDOrient.ConcentrateOnRotate90[orient];
oldPos ← standardMarks[i].pos;
IF CDOrient.IncludesMirrorX[orient] THEN simpleOrient ← CDOrient.MirrorX[simpleOrient];
newPos ← MapPoint[oldPos, [2,2], simpleOrient];
TerminalIO.WriteRope["Mark rotated to ("];
TerminalIO.WriteInt[newPos.x];
TerminalIO.WriteRope[", "];
TerminalIO.WriteInt[newPos.y];
TerminalIO.WriteRope[")\n"];
FOR j: INT IN [1..4] DO
IF standardMarks[j].pos = newPos THEN BEGIN
RETURN[standardMarks[j].name];
END;
ENDLOOP;
END;
ENDLOOP;
RETURN[markName];
END;
-- Accessing Designs
-- load design from a file
ReadDesign: PUBLIC PROC [fileName: Rope.ROPE] RETURNS [Design, Technology, SymTab.Ref] = BEGIN
design: Design ← NIL;
design ← CDIO.ReadDesign[from: fileName];
IF design = NIL THEN RETURN[NIL, NIL, NIL];
RETURN[design, design.technology, NIL];
END;
-- write design into a file
WriteDesign: PROC [design: Design, fileName: Rope.ROPE] RETURNS [BOOL] = BEGIN
RETURN[CDIO.WriteDesign[design: design, to: fileName]];
END;
-- create a new empty module in a particular technology
-- also returns an empty PlacedTile with which to align things via AlignTile
NewModule: PUBLIC PROC [tech: Technology] RETURNS [Module, PlacedTile] = BEGIN
pc: PlacedTile ← NEW[PlacedTileRec];
module: ModuleRef ← NEW[ModuleRec ← [technology: tech]];
module.current ← module.top;
pc.pos ← [0, 0];
pc.tile ← NIL;
pc.owner ← module;
RETURN[module, pc];
END;
-- Enter a cell and all of its subcells (recursively) into the directory of a design. It is assumed that if any of the cells are already in the directory, then all subcells of those cells are also.
-- Returns TRUE if it worked, FALSE if it failed. If it fails, only some of the cells may have been entered.
EnterCellTreeIntoDirectory: 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 AND foundObj = obj THEN RETURN[TRUE];
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.
ShiftedCellIntoCell: PROC [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[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)
oldLLshifted.llCorner;
FOR w: CD.ApplicationList ← shifted.contents, w.rest WHILE w#NIL DO
w.first.location ← CDInline.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 ← CDInline.SubPoints[shifted.urCorner, shifted.llCorner];
shifted.llCorner ← [0, 0];
-- make into an object
obj.size ← shifted.urCorner;
cell.contents ← shifted.contents;
cell.name ← name;
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.
ModuleIntoCell: PROC [module: Module, name: Rope.ROPENIL, applicationsPerScreenDot: INT ← -1] RETURNS [CD.ObPtr] = BEGIN
mod: ModuleRef ~ NARROW[module];
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.top;
mod.top.children ← CONS[sCell, mod.top.children];
};
RETURN[FALSE];
END;
[] ← SymTab.Pairs[mod.cellTab, FixOrphans];
[obj, ] ← ShiftedCellIntoCell[mod.top, name, applicationsPerScreenDot]; -- trashes mod.top
-- just for safety's sake
mod.technology ← NIL;
mod.top ← NIL;
mod.current ← NIL;
mod.cellTab ← NIL;
mod.checkTable ← NIL;
RETURN[obj];
END;
-- turn a module into a design. TRASHES THE ORIGINAL MODULE.
ModuleIntoDesign: PROC [module: Module, name: Rope.ROPENIL, applicationsPerScreenDot: INT ← -1] RETURNS [CD.Design] = BEGIN
mod: ModuleRef ~ NARROW[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] RETURNS [Tile] = BEGIN
name: Rope.ROPE ~ "ParquetModule";
tile: Tile;
internalDesign: Design;
internalDesign ← ModuleIntoDesign[module, name];
-- put it into a design and fetch it as a tile
tile ← InstantiateTile[internalDesign, name];
RETURN[tile];
END;
-- Tile Placement Procedures
-- internal procedure used by AdjacentTile to rotate standard marks
OrientStdMark: PROC [cell: Tile, orient: Orientation] RETURNS [left, right, top, bottom: Mark] = BEGIN
swapX, swapY, rotate: BOOLFALSE;
l, r, t, b: Mark;
x: Mark;
IF CDOrient.IncludesOddRot90[orient] THEN rotate ← ~rotate;
IF CDOrient.IncludesMirrorX[orient] THEN swapX ← ~swapX;
IF CDOrient.ConcentrateOnRotate90[orient] = CDOrient.rotate180 OR CDOrient.ConcentrateOnRotate90[orient] = CDOrient.rotate270 THEN
BEGIN swapX ← ~swapX; swapY ← ~swapY END;
l ← cell.left;
r ← cell.right;
t ← cell.top;
b ← cell.bottom;
IF rotate THEN BEGIN
x ← r; r ← b; b ← l; l ← t; t ← x;
END;
IF swapX THEN BEGIN
x ← l; l ← r; r ← x;
END;
IF swapY THEN BEGIN
x ← t; t ← b; b ← x;
END;
RETURN[l, r, t, b];
END;
-- place a cell next to another, using the "left", "right", "top", and "bottom" alignment marks.
AdjacentTile: PUBLIC PROC [oldTile: PlacedTile, newTile: Tile, dir: Direction, orient: Orientation] RETURNS [PlacedTile] = BEGIN
pos: Position;
IF oldTile.tile = NIL THEN
pos ← oldTile.pos
ELSE 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;
pos ← CDInline.SubPoints[
CDInline.AddPoints[
oldTile.pos,
MapPoint[mark1.pos, oldTile.tile.obj.size, oldTile.orient]],
MapPoint[mark2.pos, newTile.obj.size, orient]];
END;
RETURN[PlaceTile[into: oldTile.owner, tile: newTile, lowerLeftCorner: pos, orient: orient]];
END;
PlacedMarkLoc: PUBLIC PROC [oldTile: PlacedTile, oldMark: Mark] RETURNS [Position]= BEGIN
IF oldMark.owner # LOOPHOLE[oldTile.tile, INT] THEN ERROR Error[MarkNotInTile];
RETURN[CDInline.AddPoints[oldTile.pos, oldMark.pos]];
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;
IF oldTile.tile = NIL THEN
pos ← oldTile.pos
ELSE
pos ← CDInline.SubPoints[PlacedMarkLoc[oldTile, oldMark], newMark.pos];
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
-- create a new block
NewBlock: PROC [mod: ModuleRef, blockName: Rope.ROPE] RETURNS [ShiftedCell] = BEGIN
cell: ShiftedCell ← 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: ModuleRef ~ NARROW[module];
ref: REF ANY;
found: BOOL;
IF blockName = NIL THEN {
mod.current ← mod.top;
RETURN;
};
[found, ref] ← SymTab.Fetch[mod.cellTab, blockName];
IF ~found THEN ref ← NewBlock[mod, blockName];
mod.current ← NARROW[ref];
END;
-- Returns the last rope passed to SwitchBlock for this module.
GetCurrentBlock: PUBLIC PROC [module: Module] RETURNS [Rope.ROPE] = BEGIN
mod: ModuleRef ~ NARROW[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: ModuleRef ~ NARROW[module];
par, chi: ShiftedCell;
IF child = NIL THEN
chi ← mod.top
ELSE
chi ← NARROW[SymTab.Fetch[mod.cellTab, child].val];
IF parent = NIL THEN
par ← mod.top
ELSE
par ← NARROW[SymTab.Fetch[mod.cellTab, parent].val];
IF chi = NIL THEN chi ← NewBlock[mod, child];
IF par = NIL THEN par ← NewBlock[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
-- 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: ModuleRef ~ NARROW[module];
errors: INT ← 0;
numBuckets: INT ← 0;
numMarks: INT ← 0;
maxBucket: INT ← 0;
PaintErrorRect: PROCEDURE [mod: ModuleRef, 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.top, appl];
END;
DoHashBucket: PROC[checks: PlacedCheckList] = BEGIN
bucketSize: INT ← 0;
FOR c: PlacedCheckList ← checks, c.rest WHILE c # NIL DO
bucketSize ← bucketSize + 1;
IF ~c.first.duplicate THEN BEGIN
errors ← errors + 1;
PaintErrorRect[mod, c.first.pos];
END;
ENDLOOP;
numMarks ← numMarks + bucketSize;
maxBucket ← MAX[maxBucket, bucketSize];
END;
FOR i: INT IN [0..verificationTableSize) DO
IF mod.checkTable[i] # NIL THEN {
numBuckets ← numBuckets + 1;
DoHashBucket[mod.checkTable[i]];
};
ENDLOOP;
TerminalIO.WriteRope["("];
TerminalIO.WriteInt[numMarks]; TerminalIO.WriteRope[" check marks in"];
TerminalIO.WriteInt[numBuckets];
TerminalIO.WriteRope[" buckets. Largest bucket contained"];
TerminalIO.WriteInt[maxBucket]; TerminalIO.WriteRope[" check marks.) "];
RETURN[errors];
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: ModuleRef ~ NARROW[into];
pc: PlacedTile ← NEW[PlacedTileRec];
ap: CD.ApplicationPtr ← NEW[CD.Application];
IF tile.placedInto = NIL THEN tile.placedInto ← into;
IF into # tile.placedInto THEN Error[TileInTwoModules];
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];
-- now, record check mark positions for later verification 
FOR c: CheckList ← tile.checkList, c.rest WHILE c # NIL DO
pos: CD.Position;
pos ← CDInline.AddPoints[lowerLeftCorner,
CDOrient.MapPosition[
itemInCell: [x1: c.first.x, y1: c.first.y, x2: c.first.x, y2: c.first.y],
cellSize: tile.obj.size, cellInstOrient: orient]
];
AddVerifyTab[mod.checkTable, 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 or Tile at absolute coordinates
PlaceMark: PUBLIC PROC [into: Module, markName: Rope.ROPE, markPos: Position] = BEGIN
AlignmentMarks.PlaceMark[into, markName, markPos];
TerminalIO.WriteRope["PlaceMark not implemented\n"];
END;
-- Top-level command routines
ProduceModule: PROC[comm: CDSequencer.Command, fromCD: BOOL, fromFile: BOOL] = BEGIN   
ENABLE {
TerminalIO.UserAbort => GOTO abort;
CD.Error => BEGIN
TerminalIO.WriteRope["\nChipNDale raised an error flag: '"];
TerminalIO.WriteRope[explanation];
TerminalIO.WriteRope["'\n"];
-- let the system slide into the debugger
END;
};
PrintGenerator: SymTab.EachPairAction = BEGIN
-- PROC [key: Key, val: Val] RETURNS [quit: BOOL]
TerminalIO.WriteRope[" "];
TerminalIO.WriteRope[key];
TerminalIO.WriteRope["\n"];
RETURN[FALSE];
END;
generatorName: Rope.ROPE;
found: BOOL;
generatorProc: REF ModuleGeneratorProc;
module: Module;
mod: ModuleRef;
cellName: Rope.ROPE;
fileName: Rope.ROPE;
errs: INT;
genRef: REF ANY;
symTab: SymTab.Ref ← NIL;
IF fromCD AND ~OKCommand[comm] THEN RETURN[];
IF fromFile THEN {
ok: BOOLFALSE;
badFile: BOOLFALSE;
genNameTV: AMTypes.TV;
errorMsg: Rope.ROPE;
inFile: Rope.ROPE;
-- get file name
inFile ← TerminalIO.RequestRope["\nEnter name of a file describing the module: "];
[] ← FS.FileInfo[inFile !FS.Error => IF error.group = user THEN {
badFile ← TRUE; CONTINUE }];
IF badFile THEN BEGIN
TerminalIO.WriteRope["Could not open that file.\n"];
RETURN[];
END;
-- read file
errorMsg ← NIL;
symTab ← SymTab.Create[50];
ExprRead.ReadFile[inFile, symTab ! ExprRead.Error => {errorMsg ← msg; CONTINUE}];
IF errorMsg # NIL THEN {
TerminalIO.WriteRope["Error while reading file: '"];
TerminalIO.WriteRope[errorMsg];
TerminalIO.WriteRope["'.\n"];
RETURN[];
};
-- get ModuleGenerator name
[ok, genNameTV] ← SymTab.Fetch[symTab, "ModuleGenerator"];
IF ~ok THEN {
TerminalIO.WriteRope["File does not include a 'ModuleGenerator ← xxx' line.\n"];
RETURN[];
};
generatorName ← ExprRead.TVToRope[genNameTV];
IF generatorName = NIL THEN {
TerminalIO.WriteRope["ModuleGenerator name in file '"];
TerminalIO.WriteRope[inFile];
TerminalIO.WriteRope["' must be a rope.\n"];
RETURN[];
};
}
ELSE {
generatorName ← TerminalIO.RequestRope["\nEnter name of a module generator: (hit return for options) "];
IF Rope.Equal[generatorName, ""] THEN BEGIN
TerminalIO.WriteRope["These generators are currently running:\n"];
[] ← SymTab.Pairs[generatorTable, PrintGenerator];
TerminalIO.WriteRope[" --------\n"];
RETURN[];
END;
};
[found, genRef] ← SymTab.Fetch[generatorTable, generatorName];
IF ~found THEN BEGIN
TerminalIO.WriteRope["Module generator '"];
TerminalIO.WriteRope[generatorName];
TerminalIO.WriteRope["' is not currently running.\n"];
RETURN[];
END;
generatorProc ← NARROW[genRef];
cellName ← TerminalIO.RequestRope["What do you want to call the resulting cell? "];
IF ~fromCD THEN
fileName ← TerminalIO.RequestRope["Enter filename for result: "];
module ← generatorProc[symTab];
IF module = NIL THEN BEGIN
TerminalIO.WriteRope["No module was produced by the generator.\n"];
RETURN[];
END;
IF ~ISTYPE[module, ModuleRef] THEN BEGIN
TerminalIO.WriteRope["Something other than a module was produced by the generator!\n"];
RETURN[];
END;
mod ← NARROW[module];
TerminalIO.WriteRope["Checking module . . . "];
errs ← CheckModule[module];
IF errs > 0 THEN BEGIN
TerminalIO.WriteInt[errs];
TerminalIO.WriteRope[" ERRORS were found in the generated module, suspect areas are indicated by highlighting.\n"]
END
ELSE
TerminalIO.WriteRope["looks OK to me.\n"];
TerminalIO.WriteRope["Creating ChipNDale cell '"];
TerminalIO.WriteRope[cellName];
TerminalIO.WriteRope["' ..."];
IF fromCD THEN BEGIN
ob: CD.ObPtr;
ob ← ModuleIntoCell[module: module, name: cellName, applicationsPerScreenDot: 100];
IF ob = NIL THEN ERROR;
IF ~EnterCellTreeIntoDirectory[comm.design, ob] THEN
TerminalIO.WriteRope["\nSome cells could NOT be included in the design's directory.\n"]
ELSE
TerminalIO.WriteRope[" ok.\n"];
CDOps.AddAnObject[comm.design, ob, comm.pos];
END
ELSE BEGIN
tempDesign: CD.Design ← ModuleIntoDesign[module: module, name: cellName, applicationsPerScreenDot: 100];
TerminalIO.WriteRope[" ok.\nCreating file '"];
TerminalIO.WriteRope[fileName];
TerminalIO.WriteRope["'..."];
IF WriteDesign[tempDesign, fileName] THEN
TerminalIO.WriteRope["ok.\n"]
ELSE
TerminalIO.WriteRope["file could NOT be written.\n"];
END;
EXITS
abort => TerminalIO.WriteRope["Operation aborted, no module was generated.\n"];
END;
RunTilerDirectly: Commander.CommandProc = BEGIN   
fromFile: BOOL ← TerminalIO.UserSaysYes["From file?", "Do you want to run a module generator from a file (alternate way is to run interactively)?", TRUE];
ProduceModule[NIL, FALSE, fromFile];
END;
RunTiler: PROC [comm: CDSequencer.Command] = BEGIN   
fromFile: BOOL ← TerminalIO.UserSaysYes["From file?", "Do you want to run a module generator from a file (alternative is to run interactively)?", TRUE];
ProduceModule[comm, TRUE, fromFile];
END;
RunTilerFromFile: PROC [comm: CDSequencer.Command] = BEGIN   
ProduceModule[comm, TRUE, TRUE];
END;
RunTilerWithoutFile: PROC [comm: CDSequencer.Command] = BEGIN   
ProduceModule[comm, TRUE, FALSE];
END;
NoOp: Commander.CommandProc = BEGIN   
END;
Init: PROC[] = BEGIN
generatorTable ← SymTab.Create[case: FALSE];
CDSequencer.ImplementCommand[$RunTiler, RunTiler];
CDSequencer.ImplementCommand[$RunTilerFromFile, RunTilerFromFile];
CDSequencer.ImplementCommand[$RunTilerWithoutFile, RunTilerWithoutFile];
TilerMenu.Register["Run module generator interactively", $RunTilerWithoutFile];
TilerMenu.Register["Run module generator from file", $RunTilerFromFile];
END;
-- Main body
Init[];
-- register a command.
Commander.Register[key: "GenerateModule", proc: RunTilerDirectly, doc: "Run a module generator"];
-- register a command so that we won't get ".load file failed to register command"
Commander.Register[key: "Parquet", proc: NoOp, doc: "Does nothing"];
END.