<> <> <> <> DIRECTORY Atom, CD, CDInline, CDOps, CDSequencer, CDProperties, CDViewer, CornerStitching, FS, IO, Process, Rope, TerminalIO; ScaledFlatCif: CEDAR MONITOR IMPORTS Atom, CD, CDInline, 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 _ 20000; --cif space partHeight: CD.Number _ 20000; --cif space localFileName: Rope.ROPE _ "///temp/FlatCif.cif"; abortMask: BOOL _ FALSE; Init: PROC ~ { CDSequencer.ImplementCommand[a~$FlatCif , p~FlatCIFCommand]; CDSequencer.ImplementCommand[a~$FlatCIF , p~FlatCIFCommand]; TerminalIO.WriteRope["ChipNDale flat CIF mask generator loaded\n"]; }; <<>> FlatCIFCommand: ENTRY 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; TerminalIO.WriteRope["Flat CIF mask generation called\n"]; 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: INTERNAL 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 "]; TRUSTED {Process.SetPriority[Process.priorityBackground]}; dr.minimalSize _ 0; dr.drawRect _ dr.saveRect _ NoteLevel; dr.devicePrivate _ dd; dr.worldClip _ CDInline.universe; CDOps.DrawDesign[design, dr]; -- mark used levels and measure design designClip _ dd.bbox^; FOR lev: CD.Level IN CD.Level DO IF dd.used[lev] THEN { x: REF _ CDProperties.GetPropFromLevel[from: lev, prop: $CDxCIFMakeMask]; IF x#NIL THEN dd.used[lev] _ NARROW[x, REF BOOL]^; }; IF dd.used[lev] THEN { x: REF _ CDProperties.GetPropFromLevel[from: lev, prop: $CDxCIFBloatActive]; levelBloat: CD.Number_0; IF x#NIL THEN levelBloat _ NARROW[x, REF INT]^/nmPerCIFUnit; maskCount _ maskCount+1; masks _ CONS[lev, masks]; maxBloat _ MAX[maxBloat, ABS[levelBloat]]; -- in cif units }; ENDLOOP; temClip _ Bloat[ScaleCDToCIF[ms, designClip], maxBloat]; ms.offset _ CDInline.NegOffset[CDInline.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]]]; 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; FOR col: NAT IN [0..nCols) DO FOR row: NAT IN [0..nRows) DO IF abortMask THEN GOTO AbortMask; ms.areas.ChangeRect[rect: CDInline.universe, newvalue: NIL, checkOldvalue: FALSE]; 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,CDInline.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 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^ _ CDInline.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 CDInline.NonEmpty[r] THEN ms.areas.ChangeRect[ rect: ScaleCDToCIF[ms, r], newvalue: $covered, checkOldvalue: FALSE ]; END; AnalyzeTesselation: PROC [ms: MaskStateRef] = BEGIN <<--the tesselation is in cif coordinates>> active: CD.Rect = ms.areas.ContentsBound[rect: CDInline.universe]; IF CDInline.NonEmpty[active] THEN { CIFOutLevel[ms.s, ms.level]; IF ms.bloat#0 THEN { t: REF CornerStitching.Tesselation = ms.unbloated; ms.unbloated _ ms.areas; ms.areas _ t; ms.areas.ChangeRect[rect: CDInline.universe, newvalue: NIL, checkOldvalue: FALSE]; IF ms.bloat>0 THEN [] _ ms.unbloated.EnumerateArea[rect: CDInline.universe, perTile: BloatTile, data: ms] ELSE { ms.areas.ChangeRect[rect: active, newvalue: $covered, checkOldvalue: FALSE]; [] _ ms.unbloated.EnumerateArea[rect: CDInline.universe, perTile: BloatTile, data: ms, backgroundValue: $covered]; }; }; [] _ ms.areas.EnumerateArea[rect: CDInline.universe, perTile: OutputTile, data: ms, backgroundValue: NIL]; TerminalIO.WriteRope["."]; -- end of stripe }; END; BloatTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY] RETURNS [REF ANY] -- CornerStitching.PerTileProc -- = BEGIN ms: MaskStateRef = NARROW[data]; cr: CD.Rect = CDInline.Intersection[CDInline.universe, Bloat[tile.Area, ABS[ms.bloat]]]; IF CDInline.NonEmpty[cr] THEN ms.areas.ChangeRect[rect: cr, newvalue: tile.Value, checkOldvalue: FALSE]; RETURN[ms]; END; OutputTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY] RETURNS [REF ANY] -- CornerStitching.PerTileProc -- = BEGIN -- only called on tiles with non-NIL values ms: MaskStateRef = NARROW[data]; cr: CD.Rect = CDInline.Intersection[tile.Area, ms.partClip]; <<--in cif coordinates>> IF CDInline.NonEmpty[cr] THEN CIFOutBox[ms.s, cr]; RETURN [ms]; 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; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ScaleCDToCIF: PROC [ ms: MaskStateRef, cdr: CD.DesignRect ] RETURNS [ cifr: CD.Rect ] = BEGIN -- scales and offsets cifr _ CDInline.NormalizeRect[CDInline.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 = CDInline.MoveRect[cifr, CDInline.NegOffset[ms.offset]]; cdr _ CDInline.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.