BrandyCIFter.mesa  A package to output CIF files from ChipNDale
Copyright © 1984, 1986, 1987 by Xerox Corporation. All rights reserved.
Last Edited by: Kimr, October 15, 1984 5:15:13 pm PDT
Last Edited by: Christian Jacobi, October 30, 1984 12:10:38 pm PST
Last Edited by: Christian Jacobi, April 20, 1987 1:31:27 pm PDT
Last Edited by McCreight, October 17, 1984 7:02:18 pm PDT
DIRECTORY
CD,
CDBasics,
CDCells,
CDCommandOps,
CDCurves,
CDDirectory,
CDImports,
CDInstances,
CDIO,
CDOps,
CDProperties,
CDPropertyTools,
CDSatellites,
CDSequencer,
CDTexts,
CDVArrow,
CStitching,
FS,
IO,
List USING [Append],
Rope,
TerminalIO;
BrandyCIFter: CEDAR PROGRAM
IMPORTS CD, CDBasics, CDCells, CDCurves, CDCommandOps, CDDirectory, CDImports, CDInstances, CDIO, CDOps, CDProperties, CDPropertyTools, CDSatellites, CDSequencer, CDTexts, CDVArrow, CStitching, FS, IO, List, Rope, TerminalIO =
BEGIN
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: CStitching.Tesselation ← NIL,
partClip: CIFRect ← [0,0,0,0]
];
--Global Variables
cellIDKey: REF ATOM = NEW[ATOM ← $CellIDNo]; --not accessible
debugging: BOOLFALSE; -- for printing out debugging messages
satellitesSignals: BOOLTRUE; -- whether satellites should contribute signal names
design: CD.Design;
errorFound: REFNIL;
stopFlag: REF BOOL;
topLayerOb: CD.Object ← NEW[CD.ObjectRep];
clearIDsRec: CD.DrawRef;
cifMeasureR: CD.DrawRef;
cifDrawRec: CD.DrawRef; --for CIFOrArea
contextFilter: REF CD.ContextFilter ← NEW[CD.ContextFilter←ALL[FALSE]]; --used in cifDrawRec
cellCount: INT ← 0; -- to assign consecutive CIF ID numbers
cifLayerAnnounced: BOOLFALSE;
--layer assumed to be unchanged until the next layer is announced
lambda: INT ← 0;
cifPerLambda: INT ← 100; -- CIF units per lambda
nmPerCIF: INT = 10; -- nanometers per CIF unit, given 0.01 microns per CIF unit
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.Rect ← CDBasics.empty;
--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;
FreeStorage: PROC [] = {
cifMeasureR ← clearIDsRec ← cifDrawRec ← NIL; --help the garbage collector
design ← NIL;
cifFile ← NIL;
};
SimpleRect: PROC [ob: CD.Object] RETURNS [yes: BOOL] = INLINE {
yes ← ob.class.wireTyped AND ob.class.objectType=$Rect
};
flattenAtomics: BOOLFALSE;
MakeCall: PROC [ob: CD.Object] RETURNS [yes: BOOL] = {
IF SimpleRect[ob] THEN RETURN [FALSE];
IF ob.class.symbolic THEN RETURN [FALSE];
IF flattenAtomics THEN {
IF ob.class.wireTyped THEN RETURN [FALSE];
IF ob.class.technology#NIL THEN RETURN [FALSE];
};
IF CDTexts.IsText[ob] THEN {
FOR dir: LIST OF REF ANY ← cifDirectives, dir.rest WHILE dir#NIL DO
directive: CIFDirective ← NARROW[dir.first];
IF directive.cdSource=ob.layer THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
RETURN [TRUE];
};
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
specialProperties: CDProperties.PropRef ← CDProperties.InitPropRef[];
SetUpSpecialClasses: PROC [] = {
x: REF ← CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFTechnology];
IF design.technology.key#x AND design.technology#x THEN {
IF x=NIL THEN TerminalIO.PutRope["CIF commandfile not set up\n"]
ELSE TerminalIO.PutRope["CIF commandfile set up for different technology\n"];
ERROR ABORTED
};
WITH CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFSpecials] SELECT FROM
pRef: CDProperties.PropRef => specialProperties ← pRef;
ENDCASE => specialProperties ← CDProperties.InitPropRef[];
flattenAtomics ← CDProperties.GetProp[$CDxCIFRegistrations, $CDxFlattenAtomic]=$TRUE;
};
IsSpecialCase: PROC [ob: CD.Object] RETURNS [yes: BOOLFALSE] = {
yes ← CDProperties.GetListProp[specialProperties^, ob.class.objectType]#NIL
};
HandleSpecialCase: PROC [ob: CD.Object] = {
WITH CDProperties.GetListProp[specialProperties^, ob.class.objectType] SELECT FROM
rp: REF PROC [ob: CD.Object] RETURNS [Rope.ROPE] => {
r: Rope.ROPE ← rp^[ob];
IO.PutRope[cifFile, r];
};
r: Rope.ROPE => {
IO.PutRope[cifFile, r];
};
rt: REF TEXT => {
IO.PutText[cifFile, rt];
};
ENDCASE => {
TerminalIO.PutRope["object class handled specialy is inconsistent\n"];
ERROR
};
};
--%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ClearCellIDs: CD.DrawProc = {
--used to set all CellIDNo's to NIL at the start
CDProperties.PutObjectProp[onto: ob, prop: cellIDKey, val: NIL];
IF ob.class.composed THEN ob.class.drawMe[pr: clearIDsRec, ob: ob];
};
SymHeader: PROC [obj: CD.Object] = {
--start outputting a new cell and assign its cellID
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.PutObjectProp[obj, cellIDKey, cellID];
};
UnfinishedSymHeader: PROC [obj: CD.Object] = {
--start outputting a new cell and assign its cellID
--but don't output the scaling
cellID: REF INTNEW[INT ← (cellCount ← cellCount + 1)];
cifFile.PutF["DS %d ", IO.int[cellCount]];
CDProperties.PutObjectProp[obj, cellIDKey, cellID];
};
SymTrailer: PROC = {
--finish off cell description
cifFile.PutRope["DF;\n"];
};
--logically local to GenerateCompositeOb
CallChildren: CD.DrawProc = {
IF MakeCall[ob] THEN
CIFSymbolCall[ob, trans.orient, CDPosToCIF[trans.off]];
};
--logically local to GenerateCompositeOb
DrawRectChilds: CD.DrawProc = {
IF ~MakeCall[ob] THEN
CD.DrawOb[ob: ob, trans: trans, pr: pr, readOnlyInstProps: readOnlyInstProps];
};
GenerateCompositeOb: PROC [obj: CD.Object] = {
--output composite objects
--the children must be already defined
cf: REF CD.ContextFilter ← NEW[CD.ContextFilter←ALL[FALSE]];
pr: CD.DrawRef ← CD.CreateDrawRef[[
stopFlag: stopFlag,
design: design
]];
SymHeader[obj];
CIFDrawCellName[CDDirectory.Name[obj, design]];
--call the children
pr.contextFilter ← NIL;
pr.drawChild ← CallChildren;
pr.drawRect ← IgnoreRect;
CD.DrawOb[pr, obj];
--draw the rectangles
pr.drawChild ← DrawRectChilds;
pr.contextFilter ← cf;
pr.drawRect ← 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 redundant but easier to understand
cf[currentDirective.cdSource] ← TRUE;
CD.DrawOb[pr, obj];
cf[currentDirective.cdSource] ← FALSE;
ENDLOOP;
SymTrailer[];
};
GenerateGeometricOb: PROC [obj: CD.Object] = {
--output CIF for structures only identified as geometries
IF IsSpecialCase[obj] THEN {
UnfinishedSymHeader[obj];
HandleSpecialCase[obj]
}
ELSE {
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 redundant but easier to understand
cifDrawRec.contextFilter ← contextFilter;
contextFilter[currentDirective.cdSource] ← TRUE;
CD.DrawOb[cifDrawRec, obj];
contextFilter[currentDirective.cdSource] ← FALSE;
ENDLOOP;
};
SymTrailer[];
};
MustCompensate: PROC [layer: CD.Layer] RETURNS [BOOL] = {
FOR dList: LIST OF REF ANY ← cifDirectives, dList.rest WHILE dList#NIL DO
cdir: CIFDirective ← NARROW[dList.first];
IF cdir.cdSource=layer AND cdir.deltaRadius#0 THEN RETURN [TRUE];
ENDLOOP;
RETURN [FALSE];
};
GeneratePolygon: PROC [obj: CD.Object] = {
--output CIF for structures for polygons
CifPolygonOut: PROC [points: LIST OF CD.Position] = {
--points are in design coords, not yet CIF coords
IF NOT cifLayerAnnounced THEN {
cifFile.PutF["L %g;\n", IO.rope[currentDirective.cifDest]];
cifLayerAnnounced ← TRUE;
};
cifFile.PutRope["P"];
FOR p: LIST OF CD.Position ← points, p.rest WHILE p#NIL DO
cifPos: CD.Position = CDPosToCIF[p.first];
cifFile.PutRope[" "];
CIFOutPoint[cifPos.x, cifPos.y];
ENDLOOP;
cifFile.PutRope[";\n"];
};
polygon: CDCurves.CurveSpecific = NARROW[obj.specific];
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.PutRope["**polygon shrink or bloat failed\n"];
cifFile.PutRope["( polygon has incorrect sizing );\n"];
};
SymTrailer[];
};
CIFDefineObject: PROC [obj: CD.Object] = {
--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
CheckChildren: CDDirectory.EachObjectProc = {
IF MakeCall[me] THEN CIFDefineObject[me]
};
IF debugging THEN TerminalIO.PutRope["Entering CIFDefineObject\n"];
IF CDProperties.GetObjectProp[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.layer=CD.errorLayer THEN errorFound ← obj;
IF obj.class.composed THEN {
[] ← CDDirectory.EnumerateChildObjects[obj, CheckChildren, NIL];
};
WITH obj.specific SELECT FROM
rp: CDImports.ImportSpecific => {
IF rp.boundOb=NIL THEN ERROR;
CIFDefineObject[rp.boundOb];
SymHeader[obj];
CIFDrawCellName[Rope.Cat[rp.designName, ".", rp.objectName]];
CIFSymbolCall[rp.boundOb];
SymTrailer[];
};
cp: CD.CellSpecific => {
IF satellitesSignals THEN [] ← CDSatellites.GetSatellites[obj];
SymHeader[obj];
CIFDrawCellName[CDDirectory.Name[obj, design]];
--For each member of the instance list, determine whether obj is a cell. If so, determine transformations
BEGIN
EachInst: CDCells.InstEnumerator = {
IF MakeCall[inst.ob] THEN
CIFSymbolCall[inst.ob, inst.trans.orient, CDPosToCIF[inst.trans.off]];
};
[] ← CDCells.EnumerateInstances[obj, EachInst];
END;
--for each layer, pick out the rectangles belonging to that layer
--and output them
FOR dir: LIST OF REF ANY ← cifDirectives, dir.rest WHILE dir#NIL DO
currentDirective ← NARROW[dir.first];
cifLayerAnnounced ← FALSE;
--IF currentDirective.deltaRadius>=0 THEN removed for simplicity
BEGIN
EachInst: CDCells.InstEnumerator = {
IF ~MakeCall[inst.ob] THEN
inst.ob.class.drawMe[ob: inst.ob, trans: inst.trans, pr: cifDrawRec];
};
[] ← CDCells.EnumerateInstances[obj, EachInst];
END;
ENDLOOP;
--generate all signal names
BEGIN
EachInst: CDCells.InstEnumerator = {
--we don't deal with signal names on p-diffusion;
--sorry, but I have more important things to do right now
IF ~MakeCall[inst.ob] THEN {
WITH CDProperties.GetInstanceProp[inst, $SignalName] SELECT FROM
r: Rope.ROPE => CIFLabelTerminal[inst, r, inst.ob.layer];
ENDCASE => NULL;
IF satellitesSignals THEN {
FOR list: LIST OF Rope.ROPE ← CDSatellites.GetSatelliteRopes[inst], list.rest WHILE list#NIL DO
CIFLabelTerminal[inst, list.first, inst.ob.layer];
ENDLOOP;
};
}
};
[] ← CDCells.EnumerateInstances[obj, EachInst];
END;
SymTrailer[];
}; -- of cell
ENDCASE => -- crazy or transistors or geometry or rects
IF obj.class.composed THEN GenerateCompositeOb[obj]
ELSE IF MakeCall[obj] THEN {
IF CDCurves.IsPolygon[obj] AND ~MustCompensate[obj.layer] THEN GeneratePolygon[obj]
ELSE GenerateGeometricOb[obj];
};
IF debugging THEN TerminalIO.PutRope["Leaving CIFDefineObject\n"];
};
CIFSymbolCall: PROC[ob: CD.Object, orient: CD.Orientation←original, pos: CD.Position← [0,0]] = {
--pos in CIFUnits
--indicate which cell is called using its ID number
WITH CDProperties.GetObjectProp[ob, cellIDKey] SELECT FROM
iDNo: REF INT => {
cifFile.PutF["C %d", IO.int[iDNo^]];
IF orient#original THEN {
xrotation: CD.Orientation ← CDBasics.ConcentrateOnRotate90[orient];
IF xrotation#original THEN {
cifFile.PutRope[" R "];
cifFile.PutRope[SELECT xrotation FROM
rotate90 => "0,1" ,
rotate180=> "-1,0",
rotate270=> "0,-1",
ENDCASE=> "1,0" -- never use this one
];
};
IF CDBasics.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;
};
--OUTPUT PROCEDURES
CIFMeasureArea: CD.DrawRectProc = {
-- Sets mainRect to be a large as is necessary to contain design.
r ← CDBasics.ReInterpreteRect[r];
IF CDBasics.NonEmpty[r] THEN mainRect ← CDBasics.Surround[mainRect, r];
};
IgnoreRect: CD.DrawRectProc = {
};
CIFOrArea: CD.DrawRectProc = {
IF CDBasics.NonEmpty[r] THEN {
IF l=CD.errorLayer AND errorFound=NIL THEN errorFound ← $TRUE;
IF l=currentDirective.cdSource THEN {
rd: CIFRect ← CDBasics.Extend[CDRectToCIF[CDBasics.ReInterpreteRect[r]], currentDirective.deltaRadius/nmPerCIF];
IF CDBasics.NonEmpty[rd] THEN CIFRectOut[rd];
}
}
};
CIFRectOut: PROC [r: CIFRect] = {
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 -- ];
};
CIFOutPoint: PROC[x, y: CIFUnits] = {
cifFile.PutF["%d,%d", IO.int[PrescaleCIF[[x,1]]], IO.int[PrescaleCIF[[y,1]]]]
};
CIFLabelTerminal: PROC [ap: CD.Instance, s: Rope.ROPE, lev: CD.Layer] = {
pt: CD.Position ← CDBasics.Center[CDRectToCIF[CDInstances.InstRectO[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"];
};
CIFDrawCellName: PROC[s: Rope.ROPE] = {
lastCellName ← s;
IF ~Rope.IsEmpty[s] THEN cifFile.PutF["9 %s;\n", IO.rope[s]];
};
CantRepresentExactly: ERROR = CODE;
SetCIFScaling: PROC = {
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;
};
CollectDirectives: PROC = {
GetDirectives: PROC [p: REF, lev: CD.Layer] RETURNS [l: LIST OF REF ANYNIL] = {
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, CDOps.ToRope[p, $Interactive], 0]]];
};
};
cifDirectives ← NIL;
FOR layers: LIST OF CD.Layer ← design.technology.usedLayers, layers.rest WHILE layers#NIL DO
cifDirectives ← List.Append[GetDirectives[CDProperties.GetLayerProp[layers.first, $CDxCIFRegistrations], layers.first ], cifDirectives];
ENDLOOP;
};
GetCifPerLambda: PROC [] = {
WITH CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFUnitsPerLambda] SELECT FROM
ri: REF INT => cifPerLambda ← ri^;
ENDCASE => cifPerLambda ← 0;
TerminalIO.PutF["using %g cif units per lambda; (1 CIF unit = 0.01 microns)\n", IO.int[cifPerLambda]] ;
IF cifPerLambda<2 THEN {
TerminalIO.PutRope["cif units per lambda not reasonable\n"];
ERROR ABORTED
};
};
WriteCIF: PROC[ comm: CDSequencer.Command] = {
ENABLE {
ABORTED => {TerminalIO.PutRope[" CIF generation aborted\n"]; GOTO Exit};
UNWIND => NULL;
}; --enable
mainOb: CD.Object; mainInst: CD.Instance;
cifKey: Rope.ROPE;
stopFlag ← NEW[BOOLFALSE]; errorFound ← NIL;
design ← comm.design; lambda ← design.technology.lambda;
CDSequencer.UseAbortFlag[design, stopFlag];
SetUpSpecialClasses[];
cifKey ← CDOps.ToRope[CDProperties.GetProp[$CDxCIFRegistrations, $CDxCIFName], $Interactive];
IF Rope.IsEmpty[cifKey] THEN {
TerminalIO.PutRope["CIF layers for this technology are not defined\n"];
RETURN
};
TerminalIO.PutRopes["Use CIF layer definitions [", cifKey, "]\n"];
--initialize main object, layer names, resolution, cellCount, and draw records
mainInst ← CDOps.TheInstance[design, "cif generation\n"];
IF mainInst=NIL THEN {
TerminalIO.PutRope["failed: CIF generation needs single selected object\n"];
RETURN
};
mainOb ← mainInst.ob;
TerminalIO.PutF["generate CIF for %g\n", IO.rope[CD.Describe[mainOb, mainInst.properties, design]]];
cellCount ← 0;
stopFlag^ ← FALSE;
cifMeasureR ← CD.CreateDrawRef[[
interestClip: CDBasics.universe,
drawRect: CIFMeasureArea,
stopFlag: stopFlag,
design: design
]];
clearIDsRec ← CD.CreateDrawRef[[
interestClip: CDBasics.universe,
drawChild: ClearCellIDs,
stopFlag: stopFlag,
design: design
]];
cifDrawRec ← CD.CreateDrawRef[[
interestClip: CDBasics.universe,
drawRect: CIFOrArea,
stopFlag: stopFlag,
design: design
]];
--Search world data structure: normalize coordinates,
--set mainRect to design size, mark used layers
CD.DrawOb[pr: cifMeasureR, ob: mainOb, readOnlyInstProps: mainInst.properties];
GetCifPerLambda[];
--get file name
TerminalIO.PutRopes["Input name of CIF file (", CDIO.GetWorkingDirectory[design], ") "];
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.PutRope[error.explanation];
GOTO FileIOOpenFailed;  
}
];
--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[CDCIFUp[mainRect.x2-mainRect.x1]], IO.int[CDCIFUp[mainRect.y2-mainRect.y1]]];
cifFile.PutF["( CIF KEY: %s );\n", IO.rope[cifKey]];
--ask for comment lines
TerminalIO.PutRope["Comment line: \n"];
comment ← TerminalIO.RequestRope["(parens are changed, CR for no comment)\n> "];
WHILE NOT Rope.IsEmpty[comment] DO
comment ← EliminateParens[comment];
cifFile.PutF["( %s );\n", IO.rope[comment]];
comment ← NIL;
TerminalIO.PutRope["Another comment line:\n"];
comment ← TerminalIO.RequestRope["(parens are changed, CR for no further comments)\n> "];
ENDLOOP;
IO.PutChar[cifFile, IO.CR];
CollectDirectives[];
SetCIFScaling[];
ClearCellIDs[NIL, design.actual.first.dummyCell.ob];
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 {
cifLayerAnnounced ← FALSE;
MakeFlatCIF[mainInst: mainInst, rectProc: CIFRectOut];
};
ENDLOOP;
SymTrailer[];
CIFSymbolCall[topLayerOb, original, [x: -cifPerLambda*mainRect.x1/lambda, y: -cifPerLambda*mainRect.y2/lambda]];
cifFile.PutRope[";\nEnd ... \n"];
cifFile.Close[];
TerminalIO.PutF["\nCIF file on %g\n", IO.rope[name]];
IF errorFound#NIL THEN
TerminalIO.PutRope[" Warning: the design contained some error message(s)\n"];
FreeStorage[]
EXITS
Exit => FreeStorage[];
FileIOOpenFailed => {
TerminalIO.PutRope["Open Failed\n"];
FreeStorage[]
};
}; -- of WriteCIF
--Flat CIF code
MakeFlatCIF: PROC [mainInst: CD.Instance, rectProc: PROC[rect: CIFRect]] = {
mainOb: CD.Object ← mainInst.ob;
AnalyzeTesselation: PROC [ms: MaskStateRef] = {
--the tesselation is in cif coordinates
active: CD.Rect = ms.areas.BBox[];
IF CDBasics.NonEmpty[active] THEN {
IF currentDirective.deltaRadius#0 THEN {
t: CStitching.Tesselation = ms.unbloated;
ms.unbloated ← ms.areas;
ms.areas ← t;
CStitching.ChangeRect[plane: ms.areas, rect: CDBasics.universe, new: NIL];
IF currentDirective.deltaRadius>0 THEN
CStitching.EnumerateArea[plane: ms.unbloated, rect: CDBasics.universe, eachTile: BloatTile, data: ms]
ELSE {
CStitching.ChangeRect[plane: ms.areas, rect: active, new: $covered];
CStitching.EnumerateArea[plane: ms.unbloated, rect: CDBasics.universe, eachTile: BloatTile, data: ms,
skip: $covered];
};
};
CStitching.EnumerateArea[plane: ms.areas, rect: CDBasics.universe,
eachTile: OutputTile, data: ms];
TerminalIO.PutRope["."];
};
};
OutputTile: PROCEDURE [tile: CStitching.Tile, data: REF ANY] = {
-- CStitching.PerTileProc
only called on tiles with non-NIL values
ms: MaskStateRef = NARROW[data];
cr: CIFRect = CDBasics.Intersection[tile.Area, ms.partClip];
rectProc[cr];
};
designClip: CD.Rect;
cifClip: CD.Rect;
nRows, nCols: NAT;
ms: MaskStateRef = NEW[MaskState ← [
areas: CStitching.NewTesselation[],
unbloated: CStitching.NewTesselation[]
]];
dr: CD.DrawRef ← CD.CreateDrawRef[[
drawRect: NoteBoundingBox,
devicePrivate: NEW[CD.Rect ← CDBasics.empty],
interestClip: CDBasics.universe,
stopFlag: stopFlag,
design: design
]];
CD.DrawOb[pr: dr, ob: mainOb, readOnlyInstProps: mainInst.properties]; -- measure design
designClip ← NARROW[dr.devicePrivate, REF CD.Rect]^;
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.PutF[".. in %d rows and %d columns..", IO.int[nRows], IO.int[nCols]];
dr.drawRect ← NoteRectangle;
dr.devicePrivate ← ms;
TerminalIO.PutF["%s -> %s", IO.rope[CDOps.ToRope[CD.LayerKey[currentDirective.cdSource], $Interactive]], IO.rope[currentDirective.cifDest]];
FOR col: NAT IN [0..nCols) DO
FOR row: NAT IN [0..nRows) DO
ms.areas.ChangeRect[rect: CDBasics.universe, new: 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]];
CDVArrow.ShowArrow[design: design,
pos: [x: (dr.interestClip.x1+dr.interestClip.x2)/2, y: (dr.interestClip.y1+dr.interestClip.y2)/2]]; -- keep user happy
CD.DrawOb[pr: dr, ob: mainOb, readOnlyInstProps: mainInst.properties];
--build tesselation of the relevant design rectangle
AnalyzeTesselation[ms]; -- sends the current part to s
ENDLOOP;
ENDLOOP;
TerminalIO.PutRope["+"];
CDVArrow.RemoveArrow[design: design];
dr ← NIL;
};
NoteBoundingBox: CD.DrawRectProc = {
bb: REF CD.Rect = NARROW[pr.devicePrivate];
bb^ ← CDBasics.Surround[bb^, r];
};
NoteRectangle: CD.DrawRectProc = {
ms: MaskStateRef = NARROW[pr.devicePrivate];
IF l=currentDirective.cdSource AND CDBasics.NonEmpty[r] THEN
ms.areas.ChangeRect[rect: CDRectToCIF[r], new: $covered];
};
BloatTile: PROCEDURE [tile: CStitching.Tile, data: REF ANY] = {
-- CStitching.PerTileProc --
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, new: tile.value];
};
Bloat: PROC [r: CD.Rect, delta: CD.Number] RETURNS [br: CD.Rect] = INLINE {
--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]
};
CDRectToCIF: PROC [ cdr: CD.Rect ] RETURNS [ cifr: CIFRect ] = {
cifr ← CDBasics.ReInterpreteRect[[
x1: cifPerLambda*cdr.x1/lambda,
y1: cifPerLambda*cdr.y1/lambda,
x2: cifPerLambda*cdr.x2/lambda,
y2: cifPerLambda*cdr.y2/lambda
]]
};
CDPosToCIF: PROC [ cdp: CD.Position ] RETURNS [ cifp: CIFPos ] = {
cifp ← [x: cifPerLambda*cdp.x/lambda, y: cifPerLambda*cdp.y/lambda]
};
CDCIFUp: PROC [ n: CD.Number ] RETURNS [ cif: CIFUnits ] = {
cif ← (cifPerLambda*n+lambda-1)/lambda
};
CIFRectToCD: PROC [ cifr: CIFRect ] RETURNS [ cdr: CD.Rect ] =
--rounds to rect beeing bigger
BEGIN
cdr ← CDBasics.ReInterpreteRect[[
x1: lambda*cifr.x1/cifPerLambda,
y1: lambda*cifr.y1/cifPerLambda,
x2: (lambda*cifr.x2+cifPerLambda-1)/cifPerLambda,
y2: (lambda*cifr.y2+cifPerLambda-1)/cifPerLambda
]]
END;
PrescaleCIF: PROC [ cif: Rational ] RETURNS [ INT ] = {
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];
};
Ceiling: PROC [num, denom: CD.Number] RETURNS [ c: INT ] = {
c ← (num+denom-1)/denom
};
ReduceTerms: PROC [ r: Rational ] RETURNS [ Rational ] = {
gcd: INT = GCD[r.num, r.denom];
RETURN[[num: r.num/gcd, denom: r.denom/gcd]];
};
GCD: PROC [ m, n: INT ] RETURNS [ INT ] = {
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];
};
WriteCIFComm: PROC [comm: CDSequencer.Command] = {
TerminalIO.PutRope["Hierarchical CIF output\n"];
TerminalIO.PutRope["Version of December 6, 1985 11:17:55 am PST\n"];
TerminalIO.PutRope["better read back the cif file and look at it\n"];
TerminalIO.PutRope["**WARNING not tested in release 2.4\n"];
IF CDCells.IsPushedIn[comm.design] THEN {
TerminalIO.PutRope["**Design is pushed in\n"];
RETURN
};
IF CDImports.HasUnloadedImports[comm.design].yes THEN {
TerminalIO.PutRope["**Design has non bound imports\n"];
RETURN
};
[] ← CDCommandOps.DoWithResource[WriteCIF, comm, $CIF];
};
EliminateParens: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = {
Translator: Rope.TranslatorType = {
RETURN [SELECT old FROM
'( => '[,
') => '],
ENDCASE => old
]
};
RETURN [Rope.Translate[base: r, translator: Translator]]
};
CDPropertyTools.Associate[$CDxCIFRegistrations, $CDxCIFName];
CDPropertyTools.Associate[$CDxCIFRegistrations, $CDxCIFTechnology];
CDPropertyTools.Associate[$CDxCIFRegistrations, $CDxCIFSpecials];
CDPropertyTools.Associate[$CDxCIFRegistrations, $CDxCIFUnitsPerLambda];
CDPropertyTools.Associate[$CDxCIFRegistrations, $CDxFlattenAtomic];
CDSequencer.ImplementCommand[key: $WriteCif, proc: WriteCIFComm, queue: doQueue];
CDCommandOps.RegisterWithMenu[menu: $ProgramMenu, entry: "Hierarchical CIF", key: $WriteCif, proc: WriteCIFComm];
END.