FlatCif.mesa
A package to output a ChipnDale design in flat CIF format.
Copyright © 1984 by Xerox Corporation. All rights reserved.
based on EBESMaskImpl by E. McCreight, November 2, 1983 6:03 pm
by: Ch. Jacobi, June 13, 1984 3:12:53 pm
last edited by: Ch. Jacobi, November 5, 1984 2:06:50 pm PST
DIRECTORY
Atom,
CD,
CDBasics,
CDCommandOps,
CDMenus,
CDOps,
CDSequencer,
CDProperties,
CDViewer,
CornerStitching,
FS,
IO,
Process,
Rope,
TerminalIO;
FlatCif: CEDAR PROGRAM
IMPORTS Atom, CD, CDBasics, CDCommandOps, CDMenus, CDOps, CDSequencer, CDProperties, CDViewer, CornerStitching, FS, IO, Process, Rope, TerminalIO
EXPORTS =
BEGIN
Rational: TYPE = RECORD [num, denom: INT ← 1];
MaskStateRef: TYPE = REF MaskState;
MaskState: TYPE = RECORD [
s: IO.STREAM, -- stream where rectangles are to be drawn
scale: Rational,
level: CD.Level ← CD.combined,
bloat: CD.Number ← 0,
areas, unbloated: REF CornerStitching.Tesselation ← NIL,
partClip: CD.Rect ← [0,0,0,0], -- in cif coordinates
offset: CD.Position ← [0, 0] -- in cif coordinates
];
DesignDataRef: TYPE = REF DesignData;
DesignData: TYPE = RECORD [
bbox: REF CD.DesignRect,
used: REF PACKED ARRAY CD.Level OF BOOL
];
partWidth: CD.Number ← 50000; --cif space
partHeight: CD.Number ← 50000; --cif space
localFileName: Rope.ROPE ← "///temp/FlatCif.cif";
abortMask: REF BOOL = NEW[BOOLFALSE];
Init: PROC ~ {
CDSequencer.ImplementCommand[a~$FlatCif , p~FlatCIFCommand];
CDSequencer.ImplementCommand[a~$FlatCIF , p~FlatCIFCommand];
CDMenus.CreateEntry[menu~$ProgramMenu, entry~"Flat CIF output", key~$FlatCIF];
TerminalIO.WriteRope["ChipNDale flat CIF mask generator loaded\n"];
};
FlatCIFCommand: PROC [comm: CDSequencer.Command] =
BEGIN
TerminalIO.WriteRope["Flat CIF mask generation\n"];
[] ← CDCommandOps.CallWithResource[ProtectedFlatCIFCommand, comm, $FlatCif, abortMask];
END;
ProtectedFlatCIFCommand: PROC [comm: CDSequencer.Command] =
BEGIN ENABLE
BEGIN
ABORTED => {TerminalIO.WriteRope[" flat CIF mask generation aborted\n"]; GOTO Exit};
RopeNeeded =>
BEGIN
explanation: Rope.ROPE = IO.PutFR[format: "Please enter a string corresponding to the value %g: ", v1: IO.refAny[ref^]];
ref^ ← TerminalIO.RequestRope[explanation];
RESUME;
END;
UNWIND => NULL;
END;
tape: IO.STREAMNIL;
tape ← FS.StreamOpen[localFileName, $create !
FS.Error => IF error.group#bug THEN {
TerminalIO.WriteRope[" file not opened\n"];
GOTO Exit
};
];
MakeMask[comm.design, tape];
tape.Close[];
TerminalIO.WriteRope["Cif file written to "];
TerminalIO.WriteRope[localFileName];
TerminalIO.WriteLn[];
EXITS Exit => NULL;
END;
MakeMask: PROC [design: CD.Design, tape: IO.STREAMNIL] =
BEGIN
designClip: CD.DesignRect;
cifClip, temClip: CD.Rect;
nRows, nCols: NAT;
maskCount: NAT ← 0;
masks: LIST OF CD.Level ← NIL;
maxBloat: CD.Number ← 0; -- CIF space
ms: MaskStateRef = NEW[ MaskState ← [
areas: CornerStitching.NewTesselation[],
unbloated: CornerStitching.NewTesselation[],
offset: [0, 0],
s: tape
]];
dr: CD.DrawRef = CD.NewNullDeviceDrawRef[design];
dd: DesignDataRef = NEW[DesignData ←
[bbox: NIL, used: NEW[PACKED ARRAY CD.Level OF BOOLALL[FALSE]]]];
nmPerLambda, nmPerCIFUnit: INT;
abortMask^ ← FALSE;
nmPerCIFUnit ← 10;
SELECT TerminalIO.RequestSelection[label: "Microns per lambda",
choice: LIST[" 2.0", " 1.5", " 1.0"]
] FROM
1 => {nmPerLambda ← 2000; TerminalIO.WriteRope[" 2.0\n"]};
2 => {nmPerLambda ← 1500; TerminalIO.WriteRope[" 1.5\n"]};
3 => {nmPerLambda ← 1000; TerminalIO.WriteRope[" 1.0\n"]};
ENDCASE => nmPerLambda ←
TerminalIO.RequestInt["OK, wise guy, how many nanometers per lambda? "];
ms.scale ← ReduceRational[[num: nmPerLambda, denom: CD.lambda*nmPerCIFUnit]];
TerminalIO.WriteRope["analyzing design\n"];
TRUSTED {Process.SetPriority[Process.priorityBackground]};
CIFOutComment[tape, IO.PutFR[format: "internal design name: %g", v1: IO.rope[design.name]]];
CIFOutComment[tape, IO.PutFR[format: "cif produced: %t", v1: IO.time[]]];
CIFOutComment[tape, "using Chipndale's flat CIF generator; Xerox PARC"];
CIFOutComment[tape, IO.PutFR[format: "nm per lambda: %d", v1: IO.int[nmPerLambda]]];
dr.minimalSize ← 0;
dr.drawRect ← dr.saveRect ← NoteLevel;
dr.devicePrivate ← dd;
dr.worldClip ← CDBasics.universe;
CDOps.DrawDesign[design, dr]; -- mark used levels and measure design
designClip ← dd.bbox^;
FOR lev: CD.Level IN CD.Level DO
layerMsg: Rope.ROPENIL;
IF dd.used[lev] THEN {
x: REF ← CDProperties.GetPropFromLevel[from: lev, prop: $CDxCIFMakeMask];
IF x#NIL THEN dd.used[lev] ← NARROW[x, REF BOOL]^;
layerMsg ← layerMsg.Cat["layer ",
Atom.GetPName[CD.LevelKey[lev]],
(IF dd.used[lev] THEN " used" ELSE " suppressed")
];
};
IF dd.used[lev] THEN {
x: REF ← CDProperties.GetPropFromLevel[from: lev, prop: $CDxCIFBloatActive];
levelBloat: CD.Number𡤀 -- in nm
IF x#NIL THEN levelBloat ← NARROW[x, REF INT]^;
IF levelBloat#0 THEN layerMsg ← layerMsg.Cat[IO.PutFR[format: "; blown up by %d nm", v1: IO.int[levelBloat]]];
maskCount ← maskCount+1;
masks ← CONS[lev, masks];
maxBloat ← MAX[maxBloat, ABS[levelBloat/nmPerCIFUnit]]; -- in cif units
};
IF layerMsg#NIL THEN {
TerminalIO.WriteRope[layerMsg];
TerminalIO.WriteLn[];
layerMsg ← layerMsg.Cat["; cif name: ", CifLevelName[lev]];
CIFOutComment[tape, layerMsg]
}
ENDLOOP;
temClip ← Bloat[ScaleCDToCIF[ms, designClip], maxBloat];
ms.offset ← CDBasics.NegOffset[CDBasics.BaseOfRect[temClip]]; -- [0, 0] at lower left corner
cifClip ← Bloat[ScaleCDToCIF[ms, designClip], maxBloat];
nRows ← Ceiling[num: cifClip.y2-cifClip.y1, denom: partHeight];
nCols ← Ceiling[num: cifClip.x2-cifClip.x1, denom: partWidth];
TerminalIO.WriteRope[IO.PutFR[format: "..making %d layers in %d rows and %d columns..", v1: IO.int[maskCount], v2: IO.int[nRows], v3: IO.int[nCols]]];
CIFOutComment[tape, IO.PutFR[format: "making %d layers in %d rows and %d columns", v1: IO.int[maskCount], v2: IO.int[nRows], v3: IO.int[nCols]]];
CIFOutComment[tape, IO.PutFR[format: "Chipndale's origin is mapped to x: %d y: %d in cif units", v1: IO.int[ms.offset.x], v2: IO.int[ms.offset.y]]];
CIFOutComment[tape, IO.PutFR[format: " ... or to x: %d y: %d in nanometers", v1: IO.int[ms.offset.x*nmPerCIFUnit], v2: IO.int[ms.offset.y*nmPerCIFUnit]]];
CIFOutComment[tape, IO.PutFR[format: "feature space minx: %d miny: %d maxx: %d maxy: %d in cif units", v1: IO.int[cifClip.x1], v2: IO.int[cifClip.y1], v3: IO.int[cifClip.x2], v4: IO.int[cifClip.y2]]];
dr.drawRect ← dr.saveRect ← NoteRectangle;
dr.devicePrivate ← ms;
FOR m: LIST OF CD.Level ← masks, m.rest WHILE m#NIL DO
ms.level ← m.first;
ms.bloat ← 0;
BEGIN
x: REF ← CDProperties.GetPropFromLevel[from: ms.level, prop: $CDxCIFBloatActive];
IF x#NIL THEN ms.bloat ← NARROW[x, REF INT]^/nmPerCIFUnit;
END;
TerminalIO.WriteRope[Atom.GetPName[CD.LevelKey[ms.level]]];
CIFOutLevel[ms.s, ms.level];
FOR col: NAT IN [0..nCols) DO
FOR row: NAT IN [0..nRows) DO
IF abortMask^ THEN GOTO AbortMask;
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
ms.partClip ← [ -- is in cif space
x1: cifClip.x1+col*partWidth,
y1: cifClip.y1+row*partHeight,
x2: cifClip.x1+col*partWidth+MIN[partWidth, cifClip.x2-cifClip.x1-partWidth*col],
y2: cifClip.y1+row*partHeight+MIN[partHeight, cifClip.y2-cifClip.y1-partHeight*row]
];
dr.worldClip ← ScaleCIFToCD[ms,CDBasics.Extend[ms.partClip, 1+ABS[ms.bloat]]];
CDViewer.ShowArrow[design: design,
pos: [x: (dr.worldClip.x1+dr.worldClip.x2)/2, y: (dr.worldClip.y1+dr.worldClip.y2)/2]]; -- keep user happy
CDSequencer.CheckAborted[design];
CDOps.DrawDesign[design, dr]; -- build tesselation of the relevant design rectangle
AnalyzeTesselation[ms]; -- sends the current part to s
ENDLOOP;
ENDLOOP;
TerminalIO.WriteRope["+"];
ENDLOOP;
CIFOutEnd[tape];
TerminalIO.WriteRope["finished\n"];
EXITS
AbortMask => {TerminalIO.WriteRope["aborted\n"]};
END;
NoteLevel: PROC [ r: CD.DesignRect, l: CD.Level, pr: CD.DrawRef ] =
BEGIN
dd: DesignDataRef = NARROW[pr.devicePrivate];
IF dd.bbox=NIL THEN dd.bbox ← NEW[CD.DesignRect ← r];
dd.bbox^ ← CDBasics.Surround[dd.bbox^, r];
dd.used[l] ← TRUE;
END;
NoteRectangle: PROC [r: CD.DesignRect, l: CD.Level, pr: CD.DrawRef] =
BEGIN
ms: MaskStateRef = NARROW[pr.devicePrivate];
IF l=ms.level AND CDBasics.NonEmpty[r] THEN
ms.areas.ChangeRect[
rect: ScaleCDToCIF[ms, r],
newValue: $covered
];
END;
AnalyzeTesselation: PROC [ms: MaskStateRef] =
BEGIN
--the tesselation is in cif coordinates
active: CD.Rect = ms.areas.ContentsBound[rect: CDBasics.universe];
IF CDBasics.NonEmpty[active] THEN {
IF ms.bloat#0 THEN {
t: REF CornerStitching.Tesselation = ms.unbloated;
ms.unbloated ← ms.areas;
ms.areas ← t;
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
IF ms.bloat>0 THEN
[] ← ms.unbloated.EnumerateArea[rect: CDBasics.universe, perTile: BloatTile, data: ms]
ELSE {
ms.areas.ChangeRect[rect: active, newValue: $covered];
[] ← ms.unbloated.EnumerateArea[rect: CDBasics.universe, perTile: BloatTile, data: ms,
backgroundValue: $covered];
};
};
[] ← ms.areas.EnumerateArea[rect: CDBasics.universe,
perTile: OutputTile, data: ms, backgroundValue: NIL];
TerminalIO.WriteRope["."]; -- end of stripe
};
END;
BloatTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY] =
-- CornerStitching.PerTileProc --
BEGIN
ms: MaskStateRef = NARROW[data];
cr: CD.Rect = CDBasics.Intersection[CDBasics.universe, Bloat[tile.Area, ABS[ms.bloat]]];
IF CDBasics.NonEmpty[cr] THEN
ms.areas.ChangeRect[rect: cr, newValue: tile.Value];
END;
OutputTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY] =
-- CornerStitching.PerTileProc
BEGIN -- only called on tiles with non-NIL values
ms: MaskStateRef = NARROW[data];
cr: CD.Rect = CDBasics.Intersection[tile.Area, ms.partClip];
--in cif coordinates
IF CDBasics.NonEmpty[cr] THEN CIFOutBox[ms.s, cr];
END;
RopeNeeded: SIGNAL [ ref: REF REF ] = CODE;
ToRope: PROC [ ref: REF ] RETURNS [ rope: Rope.ROPE ] =
BEGIN
IF ref = NIL THEN rope ← NIL
ELSE WITH ref SELECT FROM
r: Rope.ROPE => rope ← r;
rt: REF TEXT => rope ← Rope.FromRefText[rt];
a: ATOM => rope ← Atom.GetPName[a];
ri: REF INT => rope ← IO.PutFR[format: "%d", v1: IO.int[ri^]];
ENDCASE =>
BEGIN
refRef: REF REF = NEW[REF ← ref];
SIGNAL RopeNeeded[refRef];
rope ← ToRope[refRef^ ! RopeNeeded => GOTO NoHelp];
EXITS
NoHelp => ERROR;
END;
END;
Bloat: PROC [r: CD.Rect, delta: CD.Number] RETURNS [br: CD.Rect] = INLINE
BEGIN
--Be careful not to exceed the limits of a CD.Number, even temporarily
br ← [x1: MAX[FIRST[CD.Number]+delta, r.x1]-delta,
y1: MAX[FIRST[CD.Number]+delta, r.y1]-delta,
x2: MIN[LAST[CD.Number]-delta, r.x2]+delta,
y2: MIN[LAST[CD.Number]-delta, r.y2]+delta]
END;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
CifLevelName: PROC[level: CD.Level] RETURNS [Rope.ROPE] =
BEGIN
name: Rope.ROPE ← ToRope[CDProperties.GetPropFromLevel[from: level, prop: $CDxCIFName]];
IF name=NIL THEN {
TerminalIO.WriteRope[Rope.Cat["Enter CIF level name for ", Atom.GetPName[CD.LevelKey[level]]]];
name ← TerminalIO.RequestRope[" > "];
CDProperties.PutPropOnLevel[onto: level, prop: $CDxCIFName, val: name];
};
RETURN [name]
END;
CIFOutEnd: PROC [cifFile: IO.STREAM] =
BEGIN
IO.PutRope[cifFile, "End ... \n"];
END;
CIFOutLevel: PROC [cifFile: IO.STREAM, level: CD.Level] =
BEGIN
IO.PutRope[cifFile, "L "];
IO.Put[cifFile, IO.rope[CifLevelName[level]]];
IO.PutChar[cifFile, ';];
IO.PutChar[cifFile, IO.CR];
END;
CIFOutBox: PROC [cifFile: IO.STREAM, rect: CD.Rect] =
BEGIN
IF (rect.x2+rect.x1) MOD 2 # 0 OR (rect.y2+rect.y1) MOD 2 # 0 THEN
TerminalIO.WriteRope["Scaling problem\n"];
IO.PutRope[cifFile, "B "];
IO.Put[cifFile, IO.int[(rect.x2-rect.x1)]];
IO.PutChar[cifFile, ' ];
IO.Put[cifFile, IO.int[(rect.y2-rect.y1)]];
IO.PutChar[cifFile, ' ];
IO.Put[cifFile, IO.int[(rect.x2+rect.x1)/2]];
IO.PutChar[cifFile, ' ];
IO.Put[cifFile, IO.int[(rect.y2+rect.y1)/2]];
IO.PutChar[cifFile, ';];
IO.PutChar[cifFile, IO.CR];
END;
CIFOutComment: PROC [cifFile: IO.STREAM, comment: Rope.ROPE] =
BEGIN
Translator: Rope.TranslatorType --PROC [old: CHAR] RETURNS [new: CHAR]-- = {
new ← IF old='( THEN'< ELSE IF old=') THEN '> ELSE old
};
IO.PutRope[cifFile, "( "];
IO.PutRope[cifFile, Rope.Translate[base: comment, translator: Translator]];
IO.PutRope[cifFile, " );\n"];
END;
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
ScaleCDToCIF: PROC [ ms: MaskStateRef, cdr: CD.DesignRect ] RETURNS [ cifr: CD.Rect ] =
BEGIN -- scales and offsets
cifr ← CDBasics.NormalizeRect[CDBasics.MoveRect[
[x1: RatMul[ms.scale, cdr.x1],
y1: RatMul[ms.scale, cdr.y1],
x2: RatMul[ms.scale, cdr.x2],
y2: RatMul[ms.scale, cdr.y2]],
ms.offset]];
END;
ScaleCIFToCD: PROC [ ms: MaskStateRef, cifr: CD.Rect ] RETURNS [ cdr: CD.DesignRect ] =
BEGIN -- scales and offsets
r: CD.Rect = CDBasics.MoveRect[cifr, CDBasics.NegOffset[ms.offset]];
cdr ← CDBasics.NormalizeRect[
[x1: RatDiv[ms.scale, r.x1],
y1: RatDiv[ms.scale, r.y1],
x2: RatDiv[ms.scale, r.x2],
y2: RatDiv[ms.scale, r.y2]]];
END;
Ceiling: PROC [num, denom: CD.Number] RETURNS [ c: INT ] =
BEGIN
c ← num/denom;
--c = SGN[num]*SGN[denom]*FLOOR[ABS[num]/ABS[denom]] if denom#0
IF num*denom>0 AND num MOD denom # 0 THEN c ← c+1;
END;
ReduceRational: PROC [ r: Rational ] RETURNS [ Rational ] =
BEGIN
gcd: INT = IF r.num=0 THEN r.denom ELSE GCD[r.num, r.denom];
RETURN[[num: r.num/gcd, denom: r.denom/gcd]];
END;
RatMul: PROC [ mul: Rational, z: INT ] RETURNS [ INT ] =
INLINE {RETURN[(mul.num*z)/mul.denom]};
RatDiv: PROC [ div: Rational, z: INT ] RETURNS [ INT ] =
INLINE {RETURN[(div.denom*z)/div.num]};
GCD: PROC [ m, n: INT ] RETURNS [ INT ] =
BEGIN
r: INT;
SELECT m FROM
<0 => m ← -m;
>0 => NULL;
ENDCASE => ERROR;
SELECT n FROM
<0 => n ← -n;
>0 => NULL;
ENDCASE => ERROR;
r ← m MOD n;
WHILE r>0 DO m ← n; n ← r; r ← m MOD n; ENDLOOP;
RETURN[n];
END;
Init[];
END.