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[BOOL_FALSE]; 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.STREAM _ NIL; 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.STREAM _ NIL] = 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 BOOL _ ALL[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.ROPE _ NIL; 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_0; -- 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 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]; 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 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; 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. ϊFlatCif.mesa A package to output a ChipnDale design in flat CIF format. Copyright c 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 --the tesselation is in cif coordinates --in cif coordinates --Be careful not to exceed the limits of a CD.Number, even temporarily --c = SGN[num]*SGN[denom]*FLOOR[ABS[num]/ABS[denom]] if denom#0 Κw˜šœ ™ Jšœ<™ ˜\J˜Jšœ8˜8J˜Jšœ?˜?J˜>J˜Jš œžœEžœžœžœ˜–Jš œžœAžœžœžœ˜‘JšœžœPžœžœ˜•Jšœžœ?žœ$žœ!˜žJš œžœWžœžœžœžœ˜ΚJšœ*˜*J˜J˜š žœžœžœžœžœžœž˜6Jšœ˜Jšœ ˜ šž˜JšœžœK˜QJš žœžœžœ žœžœžœ˜;Jšžœ˜—Jšœ#žœ˜;Jšœ˜šžœžœžœ ž˜šžœžœžœ ž˜Jšžœ žœžœ ˜"Jšœ7žœ˜<šœ ˜"Jšœ˜Jšœ˜Jšœžœ1˜QJšœžœ2˜SJšœ˜—Jšœ>žœ ˜Nšœ"˜"JšœX ˜j—Jšœ!˜!Jšœ 5˜SJšœ ˜6Jšžœ˜—Jšžœ˜—Jšœ˜Jšžœ˜—Jšœ˜Jšœ#˜#šž˜Jšœ1˜1—Jšžœ˜J˜—š ‘ œžœžœžœ žœ ˜CJšž˜Jšœžœ˜-Jš žœ žœžœ žœžœ˜5Jšœ*˜*Jšœ žœ˜Jšžœ˜J˜—š ‘ œžœžœžœ žœ ˜EJšž˜Jšœžœ˜,šžœ žœž˜+šœ˜Jšœ˜Jšœ˜Jšœ˜——Jšžœ˜J˜—š‘œžœ˜-Jšž˜Jšœ'™'Jšœžœ8˜Bšžœž˜#šžœ žœ˜Jšœžœ,˜2Jšœ˜Jšœ ˜ Jšœ7žœ˜<šžœ ž˜JšœV˜V—šžœ˜Jšœ6˜6Jšœr˜rJšœ˜—Jšœ˜—Jšœežœ˜jJšœ ˜+Jšžœ˜—Jšžœ˜J˜—š ‘ œž œ'žœžœžœ !œ˜hJšž˜Jšœžœ˜ JšœžœBžœ ˜Xšžœž˜Jšœ4˜4—Jšžœ˜J˜—š ‘ œž œ'žœžœžœ ˜fJšžœ +˜1Jšœžœ˜ Jšœžœ6˜šžœ˜ Jšž˜Jš œžœžœžœžœ˜!Jšžœ˜Jšœ&žœ ˜3šž˜Jšœ žœ˜—Jšžœ˜——Jšžœ˜J˜—š ‘œžœžœžœ žœžœ ž˜IJšž˜JšœF™FJšœ žœžœžœ!žœžœžœ!žœžœžœ!žœžœžœ˜·Jšžœ˜—J˜J˜0J˜š ‘ œžœžœžœžœ˜9Jšž˜Jšœ žœI˜Xšžœžœžœ˜JšœIžœ˜_Jšœ%˜%JšœG˜GJšœ˜—Jšžœ˜ Jšžœ˜—J˜š‘ œžœ žœžœ˜&Jšž˜J˜"Jšžœ˜J˜—š ‘ œžœ žœžœ žœ ˜9Jšž˜Jšžœ˜Jšžœžœ˜.Jšžœ˜Jšžœžœžœ˜Jšžœ˜J˜—š ‘ œžœ žœžœžœ˜5Jšž˜š žœžœžœžœžœ˜CJšœ*˜*—Jšžœ˜Jšžœžœ˜,Jšžœ˜Jšžœžœ˜+Jšžœ˜Jšžœžœ˜-Jšžœ˜Jšžœžœ˜-Jšžœ˜Jšžœžœžœ˜Jšžœ˜J˜—š ‘ œžœ žœžœžœ˜>Jšž˜šŸ œ (œ˜LJš œžœžœžœžœžœžœ˜6Jšœ˜—Jšžœ˜JšžœI˜KJšžœ˜Jšžœ˜J˜—J˜0J˜š ‘ œžœžœžœ žœ ˜WJšžœ ˜šœ0˜0Jšœy˜yJšœ ˜ —Jšžœ˜J˜—š ‘ œžœžœžœžœ˜WJšžœ ˜Jšœžœ?˜Dšœ˜Jšœr˜r—Jšžœ˜—J˜š ‘œžœžœ žœžœ˜:Jšž˜šœ˜Jšœ?™?—Jšžœ žœžœ žœ ˜2Jšžœ˜J˜—š‘œžœžœ˜;Jšž˜Jš œžœžœ žœ žœžœ˜