BrandyCIFter.mesa  A module to output CIF files from ChipNDale
Copyright © 1984, 1985 by Xerox Corporation. All rights reserved.
Last Edited by: Kimr, October 15, 1984 5:15:13 pm PDT
Last Edited by: Jacobi, October 30, 1984 12:10:38 pm PST
Last Edited by McCreight, October 17, 1984 7:02:18 pm PDT
Last Edited by Jacobi, April 12, 1985 5:59:37 pm PST
DIRECTORY
Atom USING [GetPName],
CD,
CDApplications USING [ARectO],
CDBasics,
CDCommandOps,
CDDirectory,
CDEnvironment,
CDExtras,
CDImports,
CDIO,
CDMenus,
CDOps USING [DrawDesign],
CDOrient,
CDPolygons,
CDProperties,
CDSequencer USING [Command, ImplementCommand],
CDViewer USING [ShowArrow, RemoveArrow],
CornerStitching,
FS USING [StreamOpen, Error],
IO USING [ STREAM, time, PutRope, PutChar, CR, PutF, PutFR, int, rope, refAny, Close],
List USING [Append],
Rope,
TerminalIO;
BrandyCIFter: CEDAR PROGRAM
IMPORTS Atom, CD, CDApplications, CDBasics, CDCommandOps, CDDirectory, CDEnvironment, CDExtras, CDImports, CDIO, CDMenus, CDOps, CDOrient, CDProperties, CDSequencer, CDViewer, CornerStitching, FS, IO, List, Rope, TerminalIO =
BEGIN
--TYPES
CIFUnits: TYPE = CD.Number;
CIFPos: TYPE = CD.Position -- in CIFUnits -- ;
CIFRect: TYPE = CD.Rect -- in CIFUnits -- ;
Rational: TYPE = RECORD [num, denom: INT ← 1];
CIFDest: TYPE = REF CIFDestRec ← NIL;
CIFDestRec: TYPE = RECORD [
cifDest: Rope.ROPE,
deltaRadius: INT -- nanometers, + means cif is bigger
];
CIFDirective: TYPE = REF CIFDirectiveRec ← NIL;
CIFDirectiveRec: TYPE = RECORD [
cdSource: CD.Layer,
cifDest: Rope.ROPE,
deltaRadius: INT -- nanometers, + means cif is bigger
];
MaskStateRef: TYPE = REF MaskState;
MaskState: TYPE = RECORD [
areas, unbloated: REF CornerStitching.Tesselation ← NIL,
partClip: CIFRect ← [0,0,0,0]
];
--Global Variables
cellIDKey: REF ATOM = NEW[ATOM ← $CellIDNo]; --not accessible
debugging: BOOLEANFALSE; -- used for printing out debugging messages
design: CD.Design ; -- Chipndale data structure for which CIF will be written
topLayerOb: CD.ObPtr ← NEW[CD.ObjectDefinition];
stopFlag: REF BOOLEANNEW[BOOLEAN];
clearIDsRec: CD.DrawRef;
cifMeasureR: CD.DrawRef;
cifDrawRec: CD.DrawRef; --for CIFOrArea
infinity: CD.DesignNumber ← LAST[CD.DesignNumber]; --used to initialize sizes and positions
cellCount: INTEGER ← 0; -- used to assign consecutive CIF ID numbers
cifLayerAnnounced: BOOLEANFALSE;
--layer assumed to be unchanged until the next layer is announced
lambda: INTCD.lambda;
cifPerLambda: INT ← 100; -- CIF units per lambda
nmPerCIF: INT = 10; -- nanometers per CIF unit, given 0.01 microns per CIF unit
cifPerCD: INT; -- cifPerLambda/l, since l is defined in CD units per lambda
cifScaling: Rational;
--..output numbers*cifScaling = CIFUnits
--..CIFUnits/cifScaling = output numbers
cifFile: IO.STREAM; -- file where output will be stored
comment, base, name, lastCellName: Rope.ROPE;
mainRect: CD.DesignRect ← [x1: infinity, y1: infinity, x2: -infinity, y2: -infinity];
--set to design size in CIFMeasureArea, used to switch between Chipndale and CIF coordinate systems
cifDirectives: LIST OF REF ANY -- CIFDirective --NIL;
currentDirective: CIFDirective;
partWidth: CIFUnits ← 800000; -- window for flat CIF
partHeight: CIFUnits ← 800000;
--Procedures
FreeStorage: PROC [] =
BEGIN
cifMeasureR ← clearIDsRec ← cifDrawRec ← NIL; --help the garbage collector
design ← NIL;
cifFile ← NIL;
END;
SimpleRect: PROC [ob: CD.ObPtr] RETURNS [yes: BOOL] =
INLINE BEGIN
yes ← ob.p.wireTyped AND (ob.p.objectType=$Rect OR ob.p.objectType=$SaveRect)
END;
ClearCellIDs: PROC [aptr: CD.ApplicationPtr, pos: CD.DesignPosition, orient: CD.Orientation, pr: REF CD.DrawInformation] -- CD.DrawProc -- =
--used to set all CellIDNo's to NIL at the start
BEGIN
CDProperties.PutPropOnObject[onto: aptr.ob, prop: cellIDKey, val: NIL];
IF aptr.ob.p.inDirectory THEN
aptr.ob.p.drawMe[aptr: aptr, pos: aptr.location, orient: aptr.orientation, pr: clearIDsRec];
END;
SymHeader: PROC [obj: CD.ObPtr]=
--start outputting a new cell and assign its cellID
BEGIN
cellID: REF INTNEW[INT ← (cellCount ← cellCount + 1)];
cifFile.PutF["DS %d %d %d;\n",
IO.int[cellCount], IO.int[cifScaling.num], IO.int[cifScaling.denom]];
CDProperties.PutPropOnObject[obj, cellIDKey, cellID];
END;
SymTrailer: PROC =
--finish off cell description
BEGIN
cifFile.PutRope["DF;\n"];
END;
--logically local to GenerateCompositeOb
CallChildren: CD.DrawProc =
--PROC [aptr: ApplicationPtr, pos: DesignPosition, orient: Orientation, pr: REF DrawInformation]
BEGIN
IF ~SimpleRect[aptr.ob] THEN {
cifPos: CIFPos ← CDPosToCIF[CDOrient.MapPoint[
pointInCell: [0, 0],
cellSize: aptr.ob.size,
cellInstOrient: orient,
cellInstPos: pos
]];
CIFSymbolCall[aptr.ob, orient, cifPos];
};
END;
--logically local to GenerateCompositeOb
DrawRectChilds: CD.DrawProc =
--PROC [aptr: ApplicationPtr, pos: DesignPosition, orient: Orientation, pr: REF DrawInformation]
BEGIN
IF SimpleRect[aptr.ob] THEN {
aptr.ob.p.drawMe[aptr: aptr, pos: pos, orient: orient, pr: cifDrawRec];
};
END;
GenerateCompositeOb: PROC [obj: CD.ObPtr] =
--output composite objects
--the children must be already defined
BEGIN
pr: CD.DrawRef ← CD.CreateDrawRef[design];
dummyApPtr: CD.ApplicationPtr = NEW[CD.Application];
--match the type of drawMe parameter
dummyApPtr^.ob ← obj;
pr.stopFlag ← stopFlag;
SymHeader[obj];
CIFDrawCellName[CDDirectory.Name[obj]];
--call the children
pr.drawChild ← CallChildren;
obj.p.drawMe[dummyApPtr, [0, 0], CD.original, pr];
--draw the rectangles
pr.drawChild ← DrawRectChilds;
pr.drawRect ← pr.saveRect ← CIFOrArea;
FOR directives: LIST OF REF ANY ← cifDirectives, directives.rest WHILE directives#NIL DO
currentDirective ← NARROW[directives.first];
cifLayerAnnounced ← FALSE;
IF currentDirective.deltaRadius>=0 THEN
obj.p.drawMe[dummyApPtr, [0, 0], CD.original, pr];
ENDLOOP;
SymTrailer[];
END;
GenerateGeometricOb: PROC [obj: CD.ObPtr] =
--output CIF for structures only identified as geometries
BEGIN
dummyApPtr: CD.ApplicationPtr = NEW[CD.Application];
--match the type of drawMe parameter
dummyApPtr^.ob ← obj;
SymHeader[obj];
FOR directives: LIST OF REF ANY ← cifDirectives, directives.rest WHILE directives#NIL DO
currentDirective ← NARROW[directives.first];
cifLayerAnnounced ← FALSE;
IF currentDirective.deltaRadius>=0 THEN
obj.p.drawMe[aptr: dummyApPtr, pos: [0, 0], orient: 0, pr: cifDrawRec];
ENDLOOP;
SymTrailer[];
END;
GeneratePolygon: PROC [obj: CD.ObPtr] =
--output CIF for structures for polygons
BEGIN
CifPolygonOut: PROC [points: LIST OF CD.DesignPosition] =
--points are in design coords, not yet CIF coords
BEGIN
IF NOT cifLayerAnnounced THEN {
cifFile.PutF["L %g;\n", IO.rope[currentDirective.cifDest]];
cifLayerAnnounced ← TRUE;
};
cifFile.PutRope["P"];
FOR p: LIST OF CD.DesignPosition ← points, p.rest WHILE p#NIL DO
cifPos: CD.Position = CDPosToCIF[p.first];
cifFile.PutRope[" "];
CIFOutPoint[cifPos.x, cifPos.y];
ENDLOOP;
cifFile.PutRope[";\n"];
END;
polygon: CDPolygons.PolygonPtr = NARROW[obj.specificRef];
bad: BOOLFALSE;
SymHeader[obj];
FOR directives: LIST OF REF ANY ← cifDirectives, directives.rest WHILE directives#NIL DO
currentDirective ← NARROW[directives.first];
cifLayerAnnounced ← FALSE;
IF currentDirective.cdSource=obj.layer THEN {
IF currentDirective.deltaRadius#0 THEN bad ← TRUE;
CifPolygonOut[polygon.points];
}
ENDLOOP;
IF bad THEN {
TerminalIO.WriteRope["**polygon shrink or bloat failed\n"];
cifFile.PutRope["( polygon has incorrect sizing );\n"];
};
SymTrailer[];
END;
CIFDefineObject: PROC [obj: CD.ObPtr] =
--Print out CIF pertaining to object starting with the furthest object from the top; After leaving procedure, object will have been assigned a CIF ID # in its properties field
BEGIN
CheckChildren: CDDirectory.EnumerateObjectsProc = {
IF me.p.inDirectory AND CDEnvironment.Propagates[me] THEN {
hasChildWhichPropagates ← TRUE
}
ELSE CIFDefineObject[me]
};
hasChildWhichPropagates: BOOLFALSE;
IF debugging THEN TerminalIO.WriteRope["Entering CIFDefineObject\n"];
IF CDProperties.GetPropFromObject[obj, cellIDKey]#NIL THEN RETURN;
--non-NIL value indicates that object has already been assigned an ID # and is therefore already defined
--We should also make a test here to decide whether any hierarchical geometry results from this object, and assign a special value to this object's attribute $CellIDNo if not.
IF obj.p.inDirectory THEN {
IF CDEnvironment.Propagates[obj] THEN ERROR;
CDDirectory.EnumerateChildObjects[obj, CheckChildren, NIL];
};
IF hasChildWhichPropagates THEN {
ob1: CD.ObPtr ← CDDirectory.ExpandHard[me: obj, from: design, to: NIL];
IF ob1#NIL
AND ~CDEnvironment.Propagates[ob1]
AND ~CDEnvironment.SomeChildPropagates[ob1] THEN {
CIFDefineObject[ob1];
CDProperties.PutPropOnObject[
onto: obj,
prop: cellIDKey,
val: CDProperties.GetPropFromObject[ob1, cellIDKey]
];
}
ELSE GenerateGeometricOb[obj: obj];
}
ELSE
WITH obj.specificRef SELECT FROM
refPtr: CDImports.ReferencePtr => {
IF refPtr.boundApp=NIL THEN ERROR;
CIFDefineObject[refPtr.boundApp.ob];
SymHeader[obj];
CIFDrawCellName[Rope.Cat[refPtr.designName, ".", refPtr.objectName]];
CIFSymbolCall[refPtr.boundApp.ob, CD.original, [0, 0]];
SymTrailer[];
};
cellptr: CD.CellPtr => {
SymHeader[obj];
CIFDrawCellName[cellptr.name];
--For each member of the application list, determine whether obj is a cell. If so, determine transformations
FOR aplist: CD.ApplicationList ← cellptr.contents, aplist.rest WHILE aplist#NIL DO
IF ~SimpleRect[aplist.first.ob] THEN {
cifPos: CIFPos ← CDPosToCIF[CDOrient.MapPoint[
pointInCell: [0, 0],
cellSize: aplist.first.ob.size,
cellInstOrient: aplist.first.orientation,
cellInstPos: aplist.first.location
]];
CIFSymbolCall[aplist.first.ob, aplist.first.orientation, cifPos];
};
ENDLOOP;
--for each layer, pick out the rectangles belonging to that layer
--and output them
FOR directives: LIST OF REF ANY ← cifDirectives, directives.rest WHILE directives#NIL DO
currentDirective ← NARROW[directives.first];
cifLayerAnnounced ← FALSE;
IF currentDirective.deltaRadius>=0 THEN
FOR aplist: CD.ApplicationList ← cellptr.contents, aplist.rest WHILE aplist#NIL DO
IF SimpleRect[aplist.first.ob] THEN {
aplist.first.ob.p.drawMe[aptr: aplist.first, pos: aplist.first.location, orient: aplist.first.orientation, pr: cifDrawRec];
};
ENDLOOP;
ENDLOOP;
--generate all signal names
FOR aplist: CD.ApplicationList ← cellptr.contents, aplist.rest WHILE aplist#NIL DO
value: REF = CDProperties.GetPropFromObject[aplist.first.ob, $signalName];
IF value#NIL THEN
IF SimpleRect[aplist.first.ob] THEN
CIFLabelTerminalIO[aplist.first, NARROW[value], aplist.first.ob.layer];
ENDLOOP;
SymTrailer[];
}; -- of cell
ENDCASE => -- crazy or transistors or geometry or rects
IF obj.p.inDirectory THEN GenerateCompositeOb[obj]
ELSE IF ~SimpleRect[obj] THEN {
IF ISTYPE[obj.specificRef, CDPolygons.PolygonPtr] THEN GeneratePolygon[obj]
ELSE GenerateGeometricOb[obj];
};
IF debugging THEN TerminalIO.WriteRope["Leaving CIFDefineObject\n"];
END;
CIFSymbolCall: PROC[ob: CD.ObPtr, orient: CD.Orientation�Orient.original, pos: CD.Position← [0,0]] =
--pos in CIFUnits
--indicate which cell is called using its ID number
BEGIN
WITH CDProperties.GetPropFromObject[ob, cellIDKey] SELECT FROM
iDNo: REF INT => {
cifFile.PutF["C %d", IO.int[iDNo^]];
IF orient#CD.original THEN {
xrotation: CD.Orientation ← CDOrient.ConcentrateOnRotate90[orient];
IF xrotation#CD.original THEN {
cifFile.PutRope[" R "];
cifFile.PutRope[SELECT xrotation FROM
CDOrient.rotate90 => "0,1" ,
CDOrient.rotate180=> "-1,0",
CDOrient.rotate270=> "0,-1",
ENDCASE=> "1,0" -- never use this one
];
};
IF CDOrient.IncludesMirrorX[orient] THEN cifFile.PutRope[" M X "];
};
IF pos#[0, 0] THEN {
cifFile.PutRope[" T "];
CIFOutPoint[pos.x, pos.y];
};
cifFile.PutRope[";\n"];
};
ENDCASE => ERROR;
END;
--OUTPUT PROCEDURES
CIFMeasureArea: PROC [r: CD.DesignRect, l: CD.Layer, pr: CD.DrawRef] =
-- CD.DrawRectProc
-- Sets mainRect to be a large as is necessary to contain design.
BEGIN
r ← CDBasics.NormalizeRect[r];
IF CDBasics.NonEmpty[r] THEN mainRect ← CDBasics.Surround[mainRect, r];
END;
CIFOrArea: PROC [r: CD.DesignRect, l: CD.Layer, pr: CD.DrawRef] =
BEGIN
rd: CD.DesignRect;
IF (l # currentDirective.cdSource) OR NOT CDBasics.NonEmpty[r] THEN RETURN;
rd ← CDBasics.NormalizeRect[r];
CIFRectOut[CDBasics.Extend[r: CDRectToCIF[CDBasics.NormalizeRect[r]], ext: currentDirective.deltaRadius/nmPerCIF]];
END;
CIFRectOut: PROC [r: CIFRect] =
BEGIN
IF NOT CDBasics.NonEmpty[r] THEN RETURN;
IF NOT cifLayerAnnounced THEN {
cifFile.PutF["L %g;\n", IO.rope[currentDirective.cifDest]];
cifLayerAnnounced ← TRUE;
};
cifFile.PutF["B %d %d %d %d;\n",-- BOX command, in prescaled CIF units
IO.int[PrescaleCIF[[r.x2-r.x1, 1]]] -- length -- ,
IO.int[PrescaleCIF[[r.y2-r.y1, 1]]] -- width -- ,
IO.int[PrescaleCIF[[r.x1+r.x2, 2]]] -- center x-coord -- ,
IO.int[PrescaleCIF[[r.y1+r.y2, 2]]] -- center y-coord -- ];
END;
CIFOutPoint: PROC[x, y: CIFUnits] =
BEGIN
cifFile.PutF["%d,%d", IO.int[PrescaleCIF[[x,1]]], IO.int[PrescaleCIF[[y,1]]]]
END;
CIFLabelTerminalIO: PROC [ap: CD.ApplicationPtr, s: Rope.ROPE, lev: CD.Layer] =
BEGIN
pt: CD.Position ← CDBasics.Center[CDRectToCIF[CDApplications.ARectO[ap]]];
cifFile.PutF["94 %s ", IO.rope[s]];
CIFOutPoint[pt.x, pt.y];
cifFile.PutChar[' ];
FOR directives: LIST OF REF ANY ← cifDirectives, directives.rest WHILE directives#NIL DO
d: CIFDirective = NARROW[directives.first];
IF d.cdSource = lev THEN {cifFile.PutRope[d.cifDest]; EXIT};
ENDLOOP;
cifFile.PutRope[";\n"];
END;
CIFDrawCellName: PROC[s: Rope.ROPE] =
BEGIN
lastCellName ← s;
IF ~Rope.IsEmpty[s] THEN cifFile.PutF["9 %s;\n", IO.rope[s]];
END;
RopeNeeded: SIGNAL [ ref: REF REF ] = CODE;
ToRope: PROC [ ref: REF ] RETURNS [ rope: Rope.ROPE ] =
BEGIN
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 =>
IF ref=NIL THEN rope ← NIL
ELSE {
refRef: REF REF = NEW[REF ← ref];
SIGNAL RopeNeeded[refRef];
rope ← ToRope[refRef^ ! RopeNeeded => GOTO NoHelp];
EXITS NoHelp => ERROR;
};
END;
CantRepresentExactly: ERROR = CODE;
SetCIFScaling: PROC =
BEGIN
cifScaling ← ReduceTerms[[num: cifPerLambda, denom: 2*lambda]]; -- the "2" allows for box centers
FOR directives: LIST OF REF ANY ← cifDirectives, directives.rest WHILE directives#NIL DO
d: CIFDirective = NARROW[directives.first];
cif: CIFUnits;
IF ABS[d.deltaRadius] MOD nmPerCIF # 0 THEN
ERROR CantRepresentExactly;
cif ← ABS[d.deltaRadius]/nmPerCIF;
cifScaling ← ReduceTerms[[
num: cifScaling.num,
denom: (cifScaling.num*cifScaling.denom)/GCD[cif*cifScaling.denom, cifScaling.num]]];
--.. adds the condition that cif/cifScaling is guaranteed to be an integer, so cif*cifScaling.denom MOD cifScaling.num = 0
ENDLOOP;
END;
CollectDirectives: PROC =
BEGIN
GetDirectives: PROC [p: REF, lev: CD.Layer] RETURNS [l: LIST OF REF ANYNIL] =
BEGIN
IF p#NIL THEN {
WITH p SELECT FROM
d: CIFDest => l ← LIST[NEW[CIFDirectiveRec ← [lev, d.cifDest, d.deltaRadius]]];
list: LIST OF REF ANY => l ← List.Append[GetDirectives[list.first, lev], GetDirectives[list.rest, lev]];
ENDCASE => l ← LIST[NEW[CIFDirectiveRec ← [lev, ToRope[p], 0]]];
};
END;
cifDirectives ← NIL;
FOR layers: LIST OF CD.Layer ← design.technology.usedLayers, layers.rest WHILE layers#NIL DO
cifDirectives ← List.Append[GetDirectives[CDProperties.GetPropFromLayer[layers.first, $CDxCIFName], layers.first ], cifDirectives];
ENDLOOP;
END;
WriteCIF: PROC[ comm: CDSequencer.Command] =
BEGIN
ENABLE BEGIN
ABORTED => {TerminalIO.WriteRope[" CIF generation aborted\n"]; GOTO Exit};
RopeNeeded => {
explanation: Rope.ROPE = IO.PutFR[format: "Please enter a string corresponding to the value %g: ", v1: IO.refAny[ref^]];
ref^ ← TerminalIO.RequestRope[explanation];
RESUME;
};
UNWIND => NULL;
END; --enable
--assign world variable from sequencer
mainOb: CD.ObPtr;
cifKey: Rope.ROPE;
design ← comm.design;
cifKey ← ToRope[CDProperties.GetPropFromTechnology[design.technology, $CDxCIFName]];
IF Rope.IsEmpty[cifKey] THEN {
TerminalIO.WriteRope["The CIF layers for this technology are not yet defined\n"];
RETURN
};
TerminalIO.WriteRope["Use CIF layer definitions ["];
TerminalIO.WriteRope[cifKey];
TerminalIO.WriteRope["]\n"];
--initialize main object, layer names, resolution, cellCount, and draw records
mainOb ← CDExtras.CreateDummyObject[design];
cellCount ← 0;
stopFlag^ ← FALSE;
cifMeasureR ← CD.CreateDrawRef[design];
cifMeasureR.interestClip ← CDBasics.universe;
cifMeasureR.drawRect ← cifMeasureR.saveRect ← CIFMeasureArea;
cifMeasureR.stopFlag ← stopFlag;
clearIDsRec ← CD.CreateDrawRef[design];
clearIDsRec.interestClip ← CDBasics.universe;
clearIDsRec.drawChild ← ClearCellIDs;
clearIDsRec.stopFlag ← stopFlag;
cifDrawRec ← CD.CreateDrawRef[design];
cifDrawRec.interestClip ← CDBasics.universe;
cifDrawRec.drawRect ← cifDrawRec.saveRect ← CIFOrArea;
cifDrawRec.stopFlag ← stopFlag;
--Search world data structure: normalize coordinates,
--set mainRect to design size, mark used layers
CDOps.DrawDesign[design, cifMeasureR];
--get file name
TerminalIO.WriteRope["Input name of CIF file ("];
TerminalIO.WriteRope[CDIO.GetWorkingDirectory[design]];
TerminalIO.WriteRope[") "];
base ← TerminalIO.RequestRope[">> "];
IF Rope.IsEmpty[base] THEN base ← design.name;
IF Rope.IsEmpty[base] THEN base ← "foo";
name ← CDIO.MakeName[base: base, ext: ".cif", wDir: CDIO.GetWorkingDirectory[design]];
cifFile ← FS.StreamOpen[name, create !
FS.Error =>
IF error.group#bug THEN {
TerminalIO.WriteRope[error.explanation];
GOTO FileIOOpenFailed;  
}
];
TerminalIO.WriteRope["How many CIF units per lambda?\n"];
cifPerLambda ← TerminalIO.RequestInt[" (1 CIF unit = 0.01 microns) \n", ">> "];
IF cifPerLambda<2 THEN {
TerminalIO.WriteRope["cif Per Lambda value not reasonable\n"];
RETURN
};
IF debugging THEN {
TerminalIO.WriteRope["cif Per Lambda = "];
TerminalIO.WriteInt[cifPerLambda];
TerminalIO.WriteRope["\n"];
};
cifPerCD ← cifPerLambda/lambda;
--output header
cifFile.PutF["( %s );\n", IO.rope[design.name]];
cifFile.PutF["( generated %g by Xerox PARC ChipNDale and BrandyCIFter);\n", IO.time[]];
cifFile.PutF["( with lambda = %d CIF units );\n", IO.int[cifPerLambda]];
cifFile.PutF["( origin = [x: 0, y: 0], size = [x: %d, y: %d] CIF units );\n", IO.int[cifPerCD*(mainRect.x2-mainRect.x1)], IO.int[cifPerCD*(mainRect.y2-mainRect.y1)]];
cifFile.PutF["( CIF KEY: %s );\n", IO.rope[cifKey]];
cifFile.PutRope["(BRANDYCIFTER NOT YET TESTED);\n"];
--ask for comment lines
TerminalIO.WriteRope["Comment line: \n"];
comment ← TerminalIO.RequestRope["(parens must balance, CR for no comment)\n", ">> "];
WHILE NOT Rope.IsEmpty[comment] DO
cifFile.PutF["( %s )\n", IO.rope[comment]];
comment ← NIL;
TerminalIO.WriteRope["Another comment line:\n"];
comment←TerminalIO.RequestRope[
"(parens must balance, CR for no further comments)\n", ">> "];
ENDLOOP;
IO.PutChar[cifFile, IO.CR];
CollectDirectives[];
SetCIFScaling[];
ClearCellIDs[design.actual.first.dummyCell, [0,0], 0, NIL];
CIFDefineObject[mainOb];
--define hybrid top layer object
SymHeader[topLayerOb];
CIFSymbolCall[mainOb];
FOR directives: LIST OF REF ANY ← cifDirectives, directives.rest WHILE directives#NIL DO
currentDirective ← NARROW[directives.first];
IF currentDirective.deltaRadius<0 THEN
BEGIN
cifLayerAnnounced ← FALSE;
MakeFlatCIF[rectProc: CIFRectOut];
END;
ENDLOOP;
SymTrailer[];
CIFSymbolCall[topLayerOb, CDOrient.original, [x: -cifPerCD*mainRect.x1, y: -cifPerCD*mainRect.y2]];
cifFile.PutRope[";\nEnd ... \n"];
cifFile.Close[];
TerminalIO.WriteLn[];
TerminalIO.WriteRope["CIF file on "];
TerminalIO.WriteRope[name];
TerminalIO.WriteLn[];
FreeStorage[]
EXITS
Exit => FreeStorage[];
FileIOOpenFailed => {
TerminalIO.WriteRope["Open Failed\n"];
FreeStorage[]
};
END; -- of WriteCIF
--Flat CIF code
MakeFlatCIF: PROC [rectProc: PROC[rect: CIFRect]] =
BEGIN
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 currentDirective.deltaRadius#0 THEN {
t: REF CornerStitching.Tesselation = ms.unbloated;
ms.unbloated ← ms.areas;
ms.areas ← t;
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
IF currentDirective.deltaRadius>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;
OutputTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY] =
-- CornerStitching.PerTileProc
BEGIN -- only called on tiles with non-NIL values
ms: MaskStateRef = NARROW[data];
cr: CIFRect = CDBasics.Intersection[tile.Area, ms.partClip];
rectProc[cr];
END;
designClip: CD.DesignRect;
cifClip: CD.Rect;
nRows, nCols: NAT;
ms: MaskStateRef = NEW[MaskState ← [
areas: CornerStitching.NewTesselation[],
unbloated: CornerStitching.NewTesselation[]
]];
dr: CD.DrawRef ← CD.CreateDrawRef[design];
dr.minimalSize ← 0;
dr.drawRect ← dr.saveRect ← NoteBoundingBox;
dr.devicePrivate ← NEW[CD.DesignRect ← CDBasics.empty];
dr.interestClip ← CDBasics.universe;
dr.stopFlag ← stopFlag;
CDOps.DrawDesign[design, dr]; -- measure design
designClip ← NARROW[dr.devicePrivate, REF CD.DesignRect]^;
cifClip ← Bloat[CDRectToCIF[designClip], ABS[currentDirective.deltaRadius]/nmPerCIF];
nRows ← Ceiling[num: cifClip.y2-cifClip.y1, denom: partHeight];
nCols ← Ceiling[num: cifClip.x2-cifClip.x1, denom: partWidth];
TerminalIO.WriteRope[IO.PutFR[format: ".. in %d rows and %d columns..", v1: IO.int[nRows], v2: IO.int[nCols]]];
dr.drawRect ← dr.saveRect ← NoteRectangle;
dr.devicePrivate ← ms;
TerminalIO.WriteRope[IO.PutFR["%s -> %s", IO.rope[ToRope[CD.LayerKey[currentDirective.cdSource]]], IO.rope[currentDirective.cifDest]]];
FOR col: NAT IN [0..nCols) DO
FOR row: NAT IN [0..nRows) DO
ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL];
ms.partClip ← [ -- 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.interestClip ← CIFRectToCD[CDBasics.Extend[ms.partClip, 1+ABS[currentDirective.deltaRadius]/nmPerCIF]];
CDViewer.ShowArrow[design: design,
pos: [x: (dr.interestClip.x1+dr.interestClip.x2)/2, y: (dr.interestClip.y1+dr.interestClip.y2)/2]]; -- keep user happy
CDOps.DrawDesign[design, dr]; -- build tesselation of the relevant design rectangle
AnalyzeTesselation[ms]; -- sends the current part to s
ENDLOOP;
ENDLOOP;
TerminalIO.WriteRope["+"];
CDViewer.RemoveArrow[design: design];
dr ← NIL;
END;
NoteBoundingBox: PROC [ r: CD.DesignRect, l: CD.Layer, pr: CD.DrawRef ] =
BEGIN
bb: REF CD.DesignRect = NARROW[pr.devicePrivate];
bb^ ← CDBasics.Surround[bb^, r];
END;
NoteRectangle: PROC [r: CD.DesignRect, l: CD.Layer, pr: CD.DrawRef] =
BEGIN
ms: MaskStateRef = NARROW[pr.devicePrivate];
IF l=currentDirective.cdSource AND CDBasics.NonEmpty[r] THEN
ms.areas.ChangeRect[
rect: CDRectToCIF[r],
newValue: $covered
];
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[currentDirective.deltaRadius/nmPerCIF]]];
IF CDBasics.NonEmpty[cr] THEN
ms.areas.ChangeRect[rect: cr, newValue: tile.Value];
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;
CDRectToCIF: PROC [ cdr: CD.DesignRect ] RETURNS [ cifr: CIFRect ] =
BEGIN
cifr ← CDBasics.NormalizeRect[[
x1: cifPerCD*cdr.x1,
y1: cifPerCD*cdr.y1,
x2: cifPerCD*cdr.x2,
y2: cifPerCD*cdr.y2
]]
END;
CDPosToCIF: PROC [ cdp: CD.DesignPosition ] RETURNS [ cifp: CIFPos ] =
BEGIN
cifp ← [x: cifPerCD*cdp.x, y: cifPerCD*cdp.y]
END;
CIFRectToCD: PROC [ cifr: CIFRect ] RETURNS [ cdr: CD.DesignRect ] =
{cdr ← CDBasics.NormalizeRect[
[x1: cifr.x1/cifPerCD,
y1: cifr.y1/cifPerCD,
x2: cifr.x2/cifPerCD,
y2: cifr.y2/cifPerCD]]
};
PrescaleCIF: PROC [ cif: Rational ] RETURNS [ INT ] =
BEGIN
n: INT = cif.num*cifScaling.denom;
d: INT = cif.denom*cifScaling.num;
IF ABS[n] MOD ABS[d] # 0 THEN ERROR CantRepresentExactly;
RETURN[n/d];
END;
Ceiling: PROC [num, denom: CD.Number] RETURNS [ c: INT ] =
{c ← (num+denom-1)/denom};
ReduceTerms: PROC [ r: Rational ] RETURNS [ Rational ] =
BEGIN
gcd: INT = GCD[r.num, r.denom];
RETURN[[num: r.num/gcd, denom: r.denom/gcd]];
END;
GCD: PROC [ m, n: INT ] RETURNS [ INT ] =
BEGIN
r: INT;
SELECT m FROM
<0 => m ← -m;
ENDCASE => NULL;
SELECT n FROM
<0 => n ← -n;
>0 => NULL;
ENDCASE => RETURN[m];
r ← m MOD n;
WHILE r>0 DO m ← n; n ← r; r ← m MOD n; ENDLOOP;
RETURN[n];
END;
WriteCIFComm: PROC [comm: CDSequencer.Command] =
BEGIN
TerminalIO.WriteRope["Hierarchical CIF output\n"];
TerminalIO.WriteRope["New version February 26, 1985 4:32:03 pm PST; which does no more mirror in Y\n"];
TerminalIO.WriteRope["***************\n"];
TerminalIO.WriteRope["NOT YET TESTED\n"];
TerminalIO.WriteRope["***************\n"];
TerminalIO.WriteRope["If you generate masks from this crab, it's your own risk\n"];
IF CDCommandOps.IsPushedIn[comm.design] THEN {
TerminalIO.WriteRope["**Design is pushed in\n"];
RETURN
};
IF CDImports.HasUnloadedImports[comm.design] THEN {
TerminalIO.WriteRope["**Design has non bound imports\n"];
RETURN
};
[] ← CDCommandOps.CallWithResource[WriteCIF, comm, $CIF, stopFlag];
END;
CDSequencer.ImplementCommand[a: $WriteCif, p: WriteCIFComm, queue: doQueue];
CDMenus.CreateEntry[menu: $ProgramMenu, entry: "Hierarchical CIF", key: $WriteCif];
END.