<> <> <> <> 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.ROPE _ NIL, 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: BOOL _ FALSE, 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.ROPE _ NIL; 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: BOOL _ FALSE; 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]; <> <> <> <> <> 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)>> oldLL _ shifted.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.ROPE _ NIL, 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.ROPE _ NIL, 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: BOOL _ FALSE; 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; <> <> <> <> <> 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 <> 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: BOOL _ FALSE; badFile: BOOL _ FALSE; 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.