DIRECTORY Ascii, Atom, CD, CDCommandOps, CDViewer, CDBasics, CDMenus, CDOps, CDSequencer, CDProperties, CornerStitching, FS, IO, List, PriorityQueue, Process, Rope, TapeStreams, TapeOps, TerminalIO, BasicTime; EBESMaskImpl: CEDAR PROGRAM IMPORTS Ascii, Atom, BasicTime, CD, CDCommandOps, CDBasics, CDMenus, CDOps, CDProperties, CDSequencer, CDViewer, CornerStitching, FS, IO, List, PriorityQueue, Process, Rope, TapeOps, TapeStreams, TerminalIO EXPORTS = BEGIN Nm: TYPE = CD.Number; EBESPixels: TYPE = CD.Number; Rational: TYPE = RECORD [num, denom: INT _ 1]; MaskStateRef: TYPE = REF MaskState; MaskState: TYPE = RECORD [ maskSetName, patternFileName: Rope.ROPE _ NIL, s: IO.STREAM, -- EBES stream where rectangles are to be drawn scale: Rational, drawCommand: REF DrawRectangle, level: ARRAY CD.Level OF MaskDirective, hasNegativeBloat: BOOL _ FALSE, areas, nbAreas: REF CornerStitching.Tesselation _ NIL, -- EBES space stripeClip: CD.Rect _ [0,0,0,0], -- EBES space reticleClip: CD.Rect _ [0,0,0,0], -- EBES space stripe: [0..maxStripes] _ 0, stripeRectCount: INT _ 0, scribe: Scribe, data: REF _ NIL ]; DesignDataRef: TYPE = REF DesignData; DesignData: TYPE = RECORD [ bbox: REF CD.DesignRect ]; MaskDest: TYPE = REF MaskDestRec _ NIL; MaskDestRec: TYPE = RECORD [ name: Rope.ROPE, complement: BOOL _ FALSE, deltaDiameter: EBESPixels _ 0 -- + means mask feature is bigger ]; MaskDirective: TYPE = REF MaskDirectiveRec _ NIL; MaskDirectiveRec: TYPE = RECORD [ cdSource: CD.Level, dest: MaskDest ]; Scribe: TYPE = REF ScribeRec _ NIL; ScribeRec: TYPE = RECORD [ maskName: Rope.ROPE, dieEdgeToLineCenter, lineWidth: Nm _ 0 ]; stripeHeight: INT = 256; -- in EBES units maxStripes: INT = 255; maskHeight: INT = maxStripes*stripeHeight; maskWidth: INT = 32768; StartDrawing: TYPE = MACHINE DEPENDENT RECORD [ nFields (0: 0..7): [0..256) _ 2, addressCode (0: 8..15): AddressCode _ Nm500, cx (1): CARDINAL[0..maskWidth], -- 0 is illegal cy (2): CARDINAL[0..maskHeight], -- 0 is illegal moDayYr (3): Date, field1Size (6): CARDINAL _ 6, patternFileName (7): FileName, field2Size (13): CARDINAL _ 2, maskInfo (14): PACKED ARRAY [0..4) OF CHAR _ ALL[' ] ]; AddressCode: TYPE = MACHINE DEPENDENT {Nm1000(0), Nm500(3), Nm250(6)}; Date: TYPE = PACKED ARRAY [0..6) OF CHAR _ ALL[' ]; FileName: TYPE = PACKED ARRAY [0..12) OF CHAR _ ALL[' ]; DrawRectangle: TYPE = MACHINE DEPENDENT RECORD [ h (0: 0..9): [1..stripeHeight], -- Mesa biases subrange so 1 is represented as 0 commandCode (0: 10..15): [0..64) _ 16, w (1): CARDINAL[0..maskWidth], -- 0 is illegal x (2): CARDINAL[0..maskWidth), unused (3: 0..7): [0..256) _ 0, y (3: 8..15): [0..stripeHeight)]; StartStripe: TYPE = MACHINE DEPENDENT RECORD [ stripeNumber (0: 0..7): [0..maxStripes] _ 0, -- 0 is illegal commandCode (0: 8..15): [0..256) _ 7]; BasicCommand: TYPE = MACHINE DEPENDENT RECORD [commandCode (0): NAT _]; endStripe: BasicCommand = [commandCode: 8]; endRecord: BasicCommand = [commandCode: 9]; endDrawing: BasicCommand = [commandCode: 4]; nmPerEBESPixel: INT _ 250; stripesPerClump: INT _ 10; wDir: Rope.ROPE _ "///"; tapeServerName: Rope.ROPE _ "Maggie"; Init: PROC ~ { CDSequencer.ImplementCommand[a~$EBESMask , p~StartEBESMask, queue~doQueue]; CDMenus.CreateEntry[menu~$ProgramMenu, entry~"EBES mask generation", key~$EBESMask]; TerminalIO.WriteRope["ChipNDale EBES mask generator loaded\n"]; }; abortFlag: REF BOOL _ NEW[BOOL]; StartEBESMask: PROC [comm: CDSequencer.Command] = TRUSTED BEGIN TerminalIO.WriteRope[" EBES-mask generation\n"]; TerminalIO.WriteRope[" not yet debugged\n"]; TerminalIO.WriteRope["Releases lock of design, but design MUST NOT be changed\n"]; abortFlag^ _ FALSE; [] _ CDCommandOps.CallWithResource[proc: EBESMask, comm: comm, resource: $EBESMask, abortFlag: abortFlag]; END; EBESMask: PROC [ comm: CDSequencer.Command ] = BEGIN NotePunt: PROC = {TerminalIO.WriteRope[IO.PutFR["\n*** EBES mask generation aborted at %g\n", IO.time[]]]}; BEGIN ENABLE BEGIN ABORTED => GOTO Punt; TerminalIO.UserAbort => GOTO Punt; UNWIND => NotePunt[]; END; fileNoRef: REF INT = NEW[INT _ 0]; SELECT TerminalIO.RequestSelection[label: "Destination", choice: LIST[" Local files", " Tape 0", " Tape 1"] ] FROM 1 => MakeMask[comm: comm, streamGen: SimpleStreamGen]; 2 => MakeTapeMasks[comm: comm, drive: 0]; 3 => MakeTapeMasks[comm: comm, drive: 1]; ENDCASE => {NotePunt[]; RETURN}; EXITS Punt => NotePunt[]; END; END; SimpleStreamGen: PROC [ ms: MaskStateRef ] RETURNS [ dest: IO.STREAM ] = BEGIN dest _ FS.StreamOpen[ fileName: IO.PutFR[format: "%g%g/%g", v1: IO.rope[wDir], v2: IO.rope[ms.maskSetName], v3: IO.rope[ms.patternFileName] ], accessOptions: create ]; END; MakeTapeMasks: PROC [comm: CDSequencer.Command, drive: [0..4]] = BEGIN tape: TapeOps.TapeHandle_NIL; CloseTape: PROC = BEGIN IF tape#NIL THEN { tape.CloseDrive[ ! TapeOps.TapeOpsError => {TerminalIO.WriteRope[IO.PutFR[" ..trouble closing tape connection: %s..", IO.rope[ec]]]; CONTINUE} ]; tape _ NIL }; END; BEGIN ENABLE {TapeOps.TapeOpsError => BEGIN TerminalIO.WriteRope[ec]; GOTO TapePunt; END; TapeStreams.Error => BEGIN TerminalIO.WriteRope[error.explanation]; GOTO TapePunt; END; UNWIND => CloseTape[]; }; tape _ TapeOps.OpenDrive[serverName: tapeServerName, driveNumber: drive, density: PE1600]; [] _ tape.Rewind[]; MakeMask[comm: comm, streamGen: TapeSubStreamGen, data: tape]; [] _ tape.Unload[]; CloseTape[]; EXITS TapePunt => {CloseTape[]; ERROR TerminalIO.UserAbort}; END; END; TapeSubStreamGen: PROC [ ms: MaskStateRef ] RETURNS [ dest: IO.STREAM ] = BEGIN tape: TapeOps.TapeHandle = NARROW[ms.data]; dest _ TapeStreams.StreamOpen[tape: tape, access: write, blocking: 2048, padding: zeroes]; END; MakeMask: PROC [comm: CDSequencer.Command, streamGen: PROC [ ms: MaskStateRef ] RETURNS [ dest: IO.STREAM ], data: REF _ NIL ] = BEGIN Prop: PROC [prop: REF, default: REF _ NIL, nilOK: BOOL _ FALSE] RETURNS [value: REF] = BEGIN value _ CDProperties.GetPropFromDesign[design, prop]; IF value = NIL THEN value _ CDProperties.GetPropFromTechnology[design.technology, prop]; IF value = NIL THEN value _ CDProperties.GetPropFromAtom[$EBESMask, prop]; IF value = NIL THEN value _ default; IF value = NIL AND NOT nilOK THEN BEGIN TerminalIO.WriteRope[IO.PutFR[" .. no value for property %g.. ", IO.rope[ToRope[prop]]]]; ERROR TerminalIO.UserAbort; END; END; NumericProp: PROC [prop: REF] RETURNS [value: INT] = {value _ NARROW[Prop[prop], REF INT]^}; design: CD.Design = comm.design; nmPerLambda: INT; ms: MaskStateRef = NEW[ MaskState _ [ maskSetName: ToRope[Prop[$CDxEBESMaskSetName]], areas: CornerStitching.NewTesselation[], nbAreas: CornerStitching.NewTesselation[], drawCommand: NEW[DrawRectangle], data: data]]; dr: CD.DrawRef = CD.NewNullDeviceDrawRef[design]; dd: DesignDataRef = NEW[DesignData _ [bbox: NIL]]; designClip: CD.DesignRect; ebesSize: CD.Position; ebesActiveToScribeCenter: EBESPixels; maskNames: LIST OF Rope.ROPE; scribeLines: LIST OF REF ANY -- Scribe --; maskDirectives: LIST OF REF ANY -- MaskDirective --; maskCount: INT; maxBloat: EBESPixels _ 0; -- >= 0 TRUSTED {Process.SetPriority[Process.priorityBackground]}; TerminalIO.WriteRope[IO.PutFR["\nEBES Mask Generation\nPreliminary analysis begun at %g.\n", IO.time[]]]; nmPerLambda _ NumericProp[$CDxEBESNmPerLambda]; nmPerEBESPixel _ NumericProp[$CDxEBESNmPerEBESPixel]; ms.scale _ ReduceRational[[num: nmPerLambda, denom: CD.lambda*nmPerEBESPixel]]; ebesSize.x _ NumericProp[$CDxEBESXNmPerDie]/nmPerEBESPixel; ebesSize.y _ NumericProp[$CDxEBESYNmPerDie]/nmPerEBESPixel; scribeLines _ NARROW[Prop[$CDxEBESScribeLines]]; ebesActiveToScribeCenter _ NumericProp[$CDxEBESActiveToScribeCenter]/nmPerEBESPixel; maskDirectives _ CollectDirectives[design, nmPerEBESPixel]; [maskNames: maskNames, maskCount: maskCount, maxBloat: maxBloat] _ FigureOutMasks[maskDirectives]; dr.minimalSize _ 0; dr.drawRect _ dr.saveRect _ NoteLevel; dr.devicePrivate _ dd; dr.worldClip _ CDBasics.universe; dr.stopFlag _ abortFlag; CDOps.DrawDesign[design, dr]; -- measure design designClip _ dd.bbox^; BEGIN ENABLE -- for ERRORs BEGIN UNWIND => ms.s _ AbortFile[ms.s]; END; ComputeReticleClip: PROC RETURNS [c: CD.Rect] = BEGIN AdjustReticleSize: PROC [coord: Rope.ROPE, z1, z2, zSize: CD.Number] RETURNS [newZ1, newZ2: CD.Number] = BEGIN IF z2-z1 > zSize THEN {TerminalIO.WriteRope[IO.PutFR[" .. design %g size of %d EBES units is larger than allowed die size of %d EBES units ..", IO.rope[coord], IO.int[c.x2-c.x1+1], IO.int[zSize]]]; ERROR TerminalIO.UserAbort}; newZ1 _ (z1+z2-zSize)/2; newZ2 _ newZ1+zSize; END; c _ CDBasics.Extend[Bloat[ScaleCDToEBES[ms, designClip], maxBloat], ebesActiveToScribeCenter]; [c.x1, c.x2] _ AdjustReticleSize["x", c.x1, c.x2, ebesSize.x]; [c.y1, c.y2] _ AdjustReticleSize["y", c.y1, c.y2, ebesSize.y]; END; today: Date = Today[]; reticleClip: CD.Rect _ ms.reticleClip _ ComputeReticleClip[]; nRows: NAT = Ceiling[[num: reticleClip.y2-reticleClip.y1, denom: maskHeight]]; nCols: NAT = Ceiling[[num: reticleClip.x2-reticleClip.x1, denom: maskWidth]]; TerminalIO.WriteRope[IO.PutFR["Preliminary analysis finished at %g. Making %d masks at %d (x) x %d (y) EBES pixels.\n", IO.time[], IO.int[maskCount], IO.int[reticleClip.x2-reticleClip.x1], IO.int[reticleClip.y2-reticleClip.y1]]]; WriteTapeDirectory[streamGen, ms, nRows, nCols, maskNames, today]; dr.drawRect _ dr.saveRect _ NoteRectangle; dr.devicePrivate _ ms; FOR m: LIST OF Rope.ROPE _ maskNames, m.rest WHILE m#NIL DO maskName: Rope.ROPE = m.first; ms.hasNegativeBloat _ FALSE; FOR levels: LIST OF CD.Level _ design.technology.usedLevels, levels.rest WHILE levels # NIL DO ms.level[levels.first] _ NIL; ENDLOOP; FOR directives: LIST OF REF ANY _ maskDirectives, directives.rest WHILE directives#NIL DO directive: MaskDirective = NARROW[directives.first]; IF Rope.Equal[directive.dest.name, maskName] THEN BEGIN ms.level[directive.cdSource] _ directive; IF directive.dest.deltaDiameter < 0 THEN ms.hasNegativeBloat _ TRUE; END; ENDLOOP; FOR scribes: LIST OF REF ANY _ scribeLines, scribes.rest WHILE scribes#NIL DO ms.scribe _ NARROW[scribes.first]; IF Rope.Equal[ms.scribe.maskName, maskName] THEN EXIT; REPEAT FINISHED => ms.scribe _ NIL; ENDLOOP; FOR col: NAT IN [0..nCols) DO FOR row: NAT IN [0..nRows) DO ms.patternFileName _ PatternFileName[maskSetName: ms.maskSetName, maskName: maskName, row: row, col: col]; ms.s _ EBESOpen[streamGen[ms]]; TerminalIO.WriteRope[IO.PutFR["\nStarting mask %g at %g ", IO.rope[ms.patternFileName], IO.time[]]]; SendCommand[s: ms.s, comm: NEW[StartDrawing _ [ patternFileName: RopeToFileName[ms.patternFileName], cx: MIN[maskWidth, reticleClip.x2-reticleClip.x1-col*maskWidth], cy: MIN[maskHeight, reticleClip.y2-reticleClip.y1-row*maskHeight], moDayYr: today, addressCode: (SELECT nmPerEBESPixel FROM 1000 => Nm1000, 500 => Nm500, 250 => Nm250, ENDCASE => Nm500)]]]; FOR stripeBase: INT -- [0..maxStripes) -- _0, stripeBase+stripesPerClump WHILE stripeBase < MIN[maxStripes, Ceiling[[num: reticleClip.y2-reticleClip.y1-maskHeight*row, denom: stripeHeight]]] DO designStripeClip: CD.Rect; IF abortFlag^ THEN GOTO AbortMask; ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL]; designStripeClip _ ScaleEBESToCD[ms, [ x1: ms.reticleClip.x1+col*maskWidth-1-maxBloat, y1: ms.reticleClip.y1+row*maskHeight+stripeBase*stripeHeight-1-maxBloat, x2: ms.reticleClip.x1+(col+1)*maskWidth+1+maxBloat, y2: ms.reticleClip.y1+row*maskHeight+(stripeBase+stripesPerClump)*stripeHeight+1+maxBloat ]]; CDViewer.ShowArrow[design: design, pos: CDBasics.Center[designStripeClip]]; dr.worldClip _ CDBasics.Intersection[designClip, designStripeClip]; CDOps.DrawDesign[design, dr]; -- build tesselation of the relevant design rectangle IF ms.hasNegativeBloat THEN ShrinkTiles[ms]; IF ms.scribe # NIL THEN -- insert scribe lines BEGIN r: CD.Rect = ms.reticleClip; outer: EBESPixels = MAX[ms.scribe.dieEdgeToLineCenter-ms.scribe.lineWidth/2, 0]/nmPerEBESPixel; inner: EBESPixels = (ms.scribe.dieEdgeToLineCenter+ms.scribe.lineWidth/2)/nmPerEBESPixel; ms.areas.ChangeRect[[x1: r.x1+outer, y1: r.y1+outer, x2: r.x1+inner, y2: r.y2-outer], $scribe]; ms.areas.ChangeRect[[x1: r.x2-inner, y1: r.y1+outer, x2: r.x2-outer, y2: r.y2-outer], $scribe]; ms.areas.ChangeRect[[x1: r.x1+outer, y1: r.y1+outer, x2: r.x2-outer, y2: r.y1+inner], $scribe]; ms.areas.ChangeRect[[x1: r.x1+outer, y1: r.y2-inner, x2: r.x2-outer, y2: r.y2-outer], $scribe]; END; FOR stripe: INT -- [0..maxStripes) -- IN [stripeBase..MIN[stripeBase+stripesPerClump, maxStripes, Ceiling[[num: reticleClip.y2-reticleClip.y1-maskHeight*row, denom: stripeHeight]]]) DO ms.stripeClip _ CDBasics.Intersection[ms.reticleClip, [ x1: ms.reticleClip.x1+col*maskWidth, y1: ms.reticleClip.y1+row*maskHeight+stripe*stripeHeight, x2: ms.reticleClip.x1+(col+1)*maskWidth, y2: ms.reticleClip.y1+row*maskHeight+(stripe+1)*stripeHeight ]]; ms.stripeRectCount _ 0; SendCommand[s: ms.s, comm: NEW[StartStripe _ [stripeNumber: stripe+1]]]; [] _ ms.areas.EnumerateArea[rect: ms.stripeClip, perTile: OutputTile, data: ms]; SendCommand[s: ms.s, comm: NEW[BasicCommand _ endStripe]]; TerminalIO.WriteRope[IO.PutFR[".%d", IO.int[ms.stripeRectCount]]]; -- end of stripe ENDLOOP; ENDLOOP; SendCommand[s: ms.s, comm: NEW[BasicCommand _ endDrawing]]; ms.s.Close[]; ms.s _ NIL; ENDLOOP; ENDLOOP; ENDLOOP; TerminalIO.WriteRope[IO.PutFR["\nEBES mask generation finished at %g\n", IO.time[]]]; EXITS AbortMask => {ms.s _ AbortFile[ms.s]; TerminalIO.WriteRope["aborted\n"]}; END; 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]; END; NoteRectangle: PROC [ r: CD.DesignRect, l: CD.Level, pr: CD.DrawRef ] = BEGIN ms: MaskStateRef = NARROW[pr.devicePrivate]; IF ms.level[l]#NIL AND CDBasics.NonEmpty[r] THEN BEGIN er: CD.Rect _ ScaleCDToEBES[ms: ms, cdr: r]; IF ms.level[l].dest.deltaDiameter >= 0 THEN BEGIN er _ Bloat[er, ms.level[l].dest.deltaDiameter]; ms.areas.ChangeRect[rect: er, newValue: ms.level[l]]; END ELSE ms.areas.FuncChangeRect[rect: er, perTile: PreferMorePositiveBloat, data: ms.level[l]]; END; END; PreferMorePositiveBloat: PROC [plane: REF CornerStitching.Tesselation, rect: CornerStitching.Rect, oldValue: REF ANY, data: REF ANY] -- CornerStitching.PerTileChangeProc -- = BEGIN IF oldValue=NIL THEN plane.ChangeRect[rect, data] ELSE BEGIN exist: MaskDirective = NARROW[oldValue]; new: MaskDirective = NARROW[data]; IF new.dest.deltaDiameter > exist.dest.deltaDiameter THEN plane.ChangeRect[rect, data]; END; END; ScaleCDToEBES: PROC [ ms: MaskStateRef, cdr: CD.DesignRect ] RETURNS [ ebr: CD.Rect ] = BEGIN ebr _ CDBasics.NormalizeRect[ [x1: RatMul[ms.scale, cdr.x1], y1: RatMul[ms.scale, cdr.y1], x2: RatMul[ms.scale, cdr.x2], y2: RatMul[ms.scale, cdr.y2]]]; END; ScaleEBESToCD: PROC [ ms: MaskStateRef, ebr: CD.Rect ] RETURNS [ cdr: CD.DesignRect ] = BEGIN cdr _ CDBasics.NormalizeRect[ [x1: RatDiv[ms.scale, ebr.x1], y1: RatDiv[ms.scale, ebr.y1], x2: RatDiv[ms.scale, ebr.x2], y2: RatDiv[ms.scale, ebr.y2]]]; END; ShrinkTiles: PROC [ ms: MaskStateRef ] = BEGIN t: REF CornerStitching.Tesselation = ms.areas; -- swap ms.areas and ms.nbAreas ms.areas _ ms.nbAreas; ms.nbAreas _ t; ms.areas.ChangeRect[rect: CDBasics.universe, newValue: NIL]; [] _ ms.nbAreas.EnumerateArea[rect: CDBasics.universe, perTile: ShrinkIfNecessary, data: ms]; ms.nbAreas.ChangeRect[rect: CDBasics.universe, newValue: NIL]; END; examineEverything: REF INT _ NEW[INT _ 0]; -- a REF that doesn't match anything ShrinkIfNecessary: PROC [tile: CornerStitching.TilePtr, data: REF ANY] -- CornerStitching.PerTileProc -- = BEGIN IF tile.Value#NIL THEN BEGIN ms: MaskStateRef = NARROW[data]; r: CornerStitching.Rect = tile.Area; v: REF = tile.Value; ms.areas.ChangeRect[r, v]; -- start by filling in the rectangle WITH v SELECT FROM m: MaskDirective => BEGIN IF m.dest.deltaDiameter<0 THEN BEGIN ShrinkFromEmptyNeighbor: PROC [tile: CornerStitching.TilePtr, data: REF ANY] -- CornerStitching.PerTileProc -- = BEGIN IF tile.Value = NIL THEN ms.areas.ChangeRect[CDBasics.Intersection[r, Bloat[tile.Area, -m.dest.deltaDiameter]], $shrunken]; END; [] _ ms.nbAreas.EnumerateArea[rect: [x1: r.x1-1, y1: r.y1, x2: r.x1, y2: r.y2], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything]; [] _ ms.nbAreas.EnumerateArea[rect: [x1: r.x1, y1: r.y1-1, x2: r.x2, y2: r.y1], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything]; [] _ ms.nbAreas.EnumerateArea[rect: [x1: r.x2, y1: r.y1, x2: r.x2+1, y2: r.y2], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything]; [] _ ms.nbAreas.EnumerateArea[rect: [x1: r.x1, y1: r.y2, x2: r.x2, y2: r.y2+1], perTile: ShrinkFromEmptyNeighbor, backgroundValue: examineEverything]; END; END; ENDCASE => ERROR; END; END; OutputTile: PROCEDURE [tile: CornerStitching.TilePtr, data: REF ANY] -- CornerStitching.PerTileProc -- = BEGIN -- only called on tiles with non-NIL values DoOutput: PROC = BEGIN ms: MaskStateRef = NARROW[data]; cr: CD.Rect = CDBasics.Intersection[tile.Area, ms.stripeClip]; IF CDBasics.NonEmpty[cr] THEN BEGIN ms.drawCommand^ _ [x: cr.x1-ms.stripeClip.x1, y: cr.y1-ms.stripeClip.y1, w: cr.x2-cr.x1, h: cr.y2-cr.y1]; SendCommand[s: ms.s, comm: ms.drawCommand]; ms.stripeRectCount _ ms.stripeRectCount+1; END; END; WITH tile.Value SELECT FROM d: MaskDirective => DoOutput[]; a: ATOM => SELECT a FROM $scribe => DoOutput[]; $shrunken => NULL; ENDCASE => ERROR; ENDCASE => ERROR; END; Today: PROC RETURNS [ d: Date ] = TRUSTED BEGIN time: BasicTime.Unpacked = BasicTime.Unpack[BasicTime.Now[]]; moDayYr: Rope.ROPE = IO.PutFR[format: "%02d%02d%02d", v1: IO.card[LOOPHOLE[time.month, CARDINAL]+1], v2: IO.int[time.day], v3: IO.int[time.year MOD 100]]; FOR i: [0..6) IN [0..6) DO d[i] _ Rope.Fetch[moDayYr, i] ENDLOOP; END; PatternFileName: PROC [ maskSetName, maskName: Rope.ROPE, row, col: NAT ] RETURNS [ rfn: Rope.ROPE ] = BEGIN r: Rope.ROPE = IO.PutFR[format: "%s%s.%g%g", v1: IO.rope[Rope.Substr[base: Rope.Cat[maskSetName, "xxxxxxx"], len: 7]], v2: IO.rope[Rope.Substr[base: Rope.Cat[maskName, "xx"], len: 2]], v3: IO.char[LOOPHOLE['A+col]], v4: IO.char[LOOPHOLE['A+row]]]; rfn _ Rope.Translate[base: r, translator: ]; END; ToUpperCase: PROC [old: CHAR] RETURNS [new: CHAR] -- Rope.TranslatorType -- = {new _ Ascii.Upper[old]}; RopeToFileName: PROC [ rfn: Rope.ROPE ] RETURNS [ fn: FileName ] = {FOR i: [0..12) IN [0..12) DO fn[i] _ Rope.Fetch[rfn, i] ENDLOOP}; SortNoDuplicates: PROC [ in: LIST OF Rope.ROPE ] RETURNS [ out: LIST OF Rope.ROPE ] = BEGIN q: PriorityQueue.Ref = PriorityQueue.Create[pred: CompareRopes]; last: Rope.ROPE _ NIL; FOR m: LIST OF Rope.ROPE _ in, m.rest WHILE m#NIL DO q.Insert[m.first]; ENDLOOP; out _ NIL; WHILE NOT q.Empty[] DO r: Rope.ROPE = NARROW[q.Remove[], Rope.ROPE]; IF last = NIL OR NOT Rope.Compare[s1: r, s2: last, case: FALSE] = equal THEN out _ CONS[r, out]; last _ r; ENDLOOP; END; CompareRopes: PROC [ x, y: PriorityQueue.Item, data: REF _ NIL ] RETURNS [ BOOL ] -- PriorityQueue.SortPred -- = BEGIN rx: Rope.ROPE = NARROW[x]; ry: Rope.ROPE = NARROW[y]; RETURN[Rope.Compare[s1: rx, s2: ry, case: FALSE] = greater]; END; WriteTapeDirectory: PROC [ streamGen: PROC [ms: MaskStateRef] RETURNS [dest: IO.STREAM], ms: MaskStateRef, nRows, nCols: NAT, masks: LIST OF Rope.ROPE, today: Date] = BEGIN s: IO.STREAM; nMasks: NAT _ 0; FOR m: LIST OF Rope.ROPE _ masks, m.rest WHILE m#NIL DO nMasks _ nMasks+1; ENDLOOP; ms.patternFileName _ IO.PutFR["%g.arch", IO.rope[ms.maskSetName]]; s _ EBESOpen[dest: streamGen[ms], eor: FALSE]; SendCommand[s, NEW[CARDINAL _ 0]]; -- marked "reserved" in Varian Figure A-1 SendCommand[s, NEW[CARDINAL _ nRows*nCols*nMasks]]; -- count of files FOR m: LIST OF Rope.ROPE _ masks, m.rest WHILE m#NIL DO FOR col: NAT IN [0..nCols) DO FOR row: NAT IN [0..nRows) DO s.PutF[format: "%s ", v1: IO.rope[PatternFileName[ms.maskSetName, m.first, row, col]]]; ENDLOOP; ENDLOOP; ENDLOOP; SendCommand[s, NEW[Date _ today]]; s.Close[]; END; AbortFile: PROC [s: IO.STREAM ] RETURNS [ IO.STREAM ] = {IF s#NIL THEN s.Close[abort: TRUE]; RETURN[NIL]}; SendCommand: PROC [ s: IO.STREAM, comm: REF ] = BEGIN sizeInWords: NAT; WITH comm SELECT FROM c: REF DrawRectangle => sizeInWords _ SIZE[DrawRectangle]; c: REF BasicCommand => sizeInWords _ SIZE[BasicCommand]; c: REF StartStripe => sizeInWords _ SIZE[StartStripe]; c: REF StartDrawing => sizeInWords _ SIZE[StartDrawing]; c: REF Date => sizeInWords _ SIZE[Date]; c: REF FileName => sizeInWords _ SIZE[FileName]; c: REF CARDINAL => sizeInWords _ SIZE[CARDINAL]; ENDCASE => ERROR; TRUSTED {s.UnsafePutBlock[[base: LOOPHOLE[comm], startIndex: 0, count: 2*sizeInWords]]}; END; EBESStreamStateRef: TYPE = REF EBESStreamState; EBESStreamState: TYPE = RECORD [ dest: IO.STREAM, eor: BOOL, buf: REF TEXT]; ebesStreamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[ variety: output, class: $EBESOutputStream, putChar: PutEBESChar, unsafePutBlock: PutEBESBlock, flush: FlushEBES, close: CloseEBES ]; EBESOpen: PROC [ dest: IO.STREAM, eor: BOOL _ TRUE ] RETURNS [ self: IO.STREAM ] = BEGIN state: EBESStreamStateRef = NEW[EBESStreamState _ [dest: dest, eor: eor, buf: NEW[TEXT[2048]]]]; state.buf.length _ 0; self _ IO.CreateStream[ streamProcs: ebesStreamProcs, streamData: state ]; END; PutEBESChar: PROC [ self: IO.STREAM, char: CHAR ] = TRUSTED BEGIN state: EBESStreamStateRef = NARROW[self.streamData]; FlushEBESIfNecessary[state, 1]; state.buf[state.buf.length] _ char; state.buf.length _ state.buf.length+1; END; PutEBESBlock: PROC [ self: IO.STREAM, block: IO.UnsafeBlock ] = TRUSTED BEGIN state: EBESStreamStateRef = NARROW[self.streamData]; FlushEBESIfNecessary[state, block.count]; FOR i: INT IN [0..block.count) DO CharArrayPtr: TYPE = LONG POINTER TO PACKED ARRAY [0..0) OF CHAR; state.buf[state.buf.length+i] _ LOOPHOLE[block.base, CharArrayPtr][block.startIndex+i]; ENDLOOP; state.buf.length _ state.buf.length+block.count; END; FlushEBESIfNecessary: PROC [state: EBESStreamStateRef, newBytes: NAT] = INLINE BEGIN IF state.buf.maxLength0 THEN BEGIN IF state.eor THEN BEGIN IF state.buf.length+2<=state.buf.maxLength THEN TRUSTED BEGIN -- append an end-of-record command to the buffer endRec: PACKED ARRAY [0..2) OF CHAR = LOOPHOLE[endRecord]; state.buf[state.buf.length] _ endRec[0]; state.buf[state.buf.length+1] _ endRec[1]; state.buf.length _ state.buf.length+2; END ELSE ERROR; -- can't terminate buffer properly END; FOR i: CARDINAL IN [state.buf.length..state.buf.maxLength) DO state.buf[i] _ 000C; -- pad out with 0's ENDLOOP; state.buf.length _ state.buf.maxLength; state.dest.PutBlock[state.buf]; END; state.buf.length _ 0; END; CloseEBES: PROC [ self: IO.STREAM, abort: BOOL _ FALSE ] = BEGIN state: EBESStreamStateRef = NARROW[self.streamData]; self.Flush[]; state.dest.Close[]; END; CollectDirectives: PROC [design: CD.Design, nmPerEBESPixel: INT] RETURNS [maskDirectives: LIST OF REF ANY -- MaskDirective -- ] = BEGIN GetDirectives: PROC [p: REF, lev: CD.Level] RETURNS [l: LIST OF REF ANY _ NIL] = BEGIN IF p#NIL THEN BEGIN WITH p SELECT FROM d: MaskDest => l _ LIST[NEW[MaskDirectiveRec _ [lev, NEW[MaskDestRec _ [ name: d.name, complement: d.complement, deltaDiameter: d.deltaDiameter/nmPerEBESPixel ]]]]]; list: LIST OF REF ANY => l _ List.Append[GetDirectives[list.first, lev], GetDirectives[list.rest, lev]]; ENDCASE => l _ LIST[NEW[MaskDirectiveRec _ [lev, NEW[MaskDestRec _ [name: ToRope[p]]]]]]; END; END; maskDirectives _ NIL; FOR levels: LIST OF CD.Level _ design.technology.usedLevels, levels.rest WHILE levels # NIL DO maskDirectives _ List.Append[GetDirectives[CDProperties.GetPropFromLevel[levels.first, $CDxEBESName], levels.first ], maskDirectives]; ENDLOOP; END; FigureOutMasks: PROC [maskDirectives: LIST OF REF ANY -- MaskDirective --] RETURNS [maskNames: LIST OF Rope.ROPE, maskCount: INT, maxBloat: EBESPixels] = BEGIN maskNames _ NIL; maxBloat _ 0; FOR ml: LIST OF REF ANY _ maskDirectives, ml.rest WHILE ml#NIL DO md: MaskDirective = NARROW[ml.first]; maskNames _ CONS[md.dest.name, maskNames]; maxBloat _ MAX[maxBloat, ABS[md.dest.deltaDiameter]]; ENDLOOP; maskNames _ SortNoDuplicates[maskNames]; maskCount _ 0; FOR m: LIST OF Rope.ROPE _ maskNames, m.rest WHILE m#NIL DO maskCount _ maskCount+1; ENDLOOP; 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, deltaDiameter: EBESPixels ] RETURNS [ br: CD.Rect ] = BEGIN b0: NAT = deltaDiameter/2; -- split in "half" for radius b1: NAT = deltaDiameter-b0; br _ [x1: MAX[FIRST[CD.Number]+b0, r.x1]-b0, y1: MAX[FIRST[CD.Number]+b0, r.y1]-b0, x2: MIN[LAST[CD.Number]-b1, r.x2]+b1, y2: MIN[LAST[CD.Number]-b1, r.y2]+b1]; END; Ceiling: PROC [ r: Rational ] RETURNS [ c: INT ] = BEGIN c _ r.num/r.denom; IF r.num*r.denom>0 AND r.num MOD r.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; 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; Init[]; END. -- of EBESMaskImpl †EBESMaskImpl.mesa A package to output a ChipnDale design in EBES format for a Varian Lithographic System. The format produced herein is described in the proprietary document "Pattern Data Formats for Varian Lithography Systems", dated May, 1982, and available from Varian/Semiconductor Equipment Group Lithography Products Division Blackburn Industrial Park Gloucester, Mass. 01930 It also conforms, more or less, to the specification in the MEBES Software Manual, document A900-0102C, Data Files, document A900-0024D, publication of Perkin-Elmer Electron Beam Technology 3392 Investment Boulevard Hayward, CA 94545 written by E. McCreight, November 2, 1983 6:03 pm Last Edited by: Jacobi, October 19, 1984 10:09:54 am PDT Last Edited by: Mccreight, November 7, 1984 7:01:24 pm PST EBES command formats: month [01..12], day [01..31], year [00..99] [9] must be '. , [10] is column ['A..'Z], [11] is row ['A..'Z] Global Variables .. keep user happy examine neighbors on all sides Be careful not to exceed the limits of a CD.Number, even temporarily c = SGN[r.num]*SGN[r.denom]*FLOOR[ABS[r.num]/ABS[r.denom]] if r.denom#0 Module START code... ส$˜Jšœ™J˜šœ๘™๘Jšœ$™$Jšœ™Jšœ™Jšœ™J™—J™—™J™ J™J™J™—J˜Jšœ1™1J™9J™:J™šฯk ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜Jšœ ˜ Jšœ ˜ J˜—šฯb œœ˜Jšœœ`œœF˜ฮJšœ˜ Jš˜J˜Jšœœœ˜Jšœ œœ˜Jšœ œœœ˜.J˜Jšœœœ ˜#šœ œœ˜Jšœ#œœ˜.Jšœœœฯc/˜=Jšœ˜Jšœ œ˜Jšœœœœ˜'Jšœœœ˜JšœœœŸ ˜DJšœ œŸ ˜.Jšœ œŸ ˜/Jšœ˜Jšœœ˜Jšœ˜Jšœœ˜Jšœ˜J˜—Jšœœœ ˜%šœ œœ˜Jšœœœ ˜Jšœ˜—J˜Jšœ œœœ˜'šœ œœ˜Jšœ œ˜Jšœ œœ˜JšœŸ!˜?Jšœ˜J˜—Jšœœœœ˜1šœœœ˜!Jšœ œ˜Jšœ˜J˜—J˜Jšœœœ œ˜#šœ œœ˜Jšœœ˜Jšœ&˜&J˜—J˜šœ™J˜JšœœŸ˜)Jšœ œ˜Jšœ œ˜*Jšœ œ ˜J˜š œœœ œœ˜/J˜ J˜,JšœœŸ˜/JšœœŸ˜0Jšœ˜Jšœœ˜Jšœ˜Jšœœ˜Jš œœœœœœ˜4J˜—Jšœ œœ œ!˜Fš œœœœœœœ˜3Jšœ+™+—š œ œœœ œœœ˜8Jšœ>™>—š œœœ œœ˜0Jšœ Ÿ0˜PJ˜&JšœœŸ˜.Jšœœ˜Jšœ˜Jšœ!˜!—š œ œœ œœ˜.Jšœ-Ÿ˜˜>Jšœ˜Jšœ ˜ Jšœœ˜˜>Jšœ>˜>Jšœ˜J˜—Jšœ˜Jšœ œ.˜=JšœœD˜NJšœœC˜MJ˜Jš œœbœ œœ%œ&˜ๆJ˜šœB˜BJ˜—Jšœ*˜*J˜J˜š œœœœœœ˜;Jšœœ ˜J˜Jšœœ˜š œ œœœ3œ œ˜^Jšœœ˜Jšœ˜—šœ œœœœ#œ œ˜YJšœœ˜4šœ+˜1Jš˜Jšœ)˜)Jšœ"œœ˜DJšœ˜—Jšœ˜J˜—šœ œœœœœ œ˜MJšœ œ˜"Jšœ*œœ˜6š˜Jšœœ˜—Jšœ˜J˜—šœœœ ˜šœœœ ˜Jšœj˜jJšœ˜Jšœœ$œœ ˜dšœœ˜/Jšœ4˜4Jšœœ9˜@Jšœœ;˜BJšœ˜šœœ˜(Jšœ˜Jšœ ˜ Jšœ ˜ Jšœ˜J˜——š œ œŸœ œœ`˜มJšœœ˜J˜šœ œœ ˜"J˜—Jšœ7œ˜<šœ&˜&Jšœ/˜/JšœH˜HJšœ3˜3JšœY˜YJšœ˜J˜—šœK˜KJšœ™J˜—JšœC˜CJ˜JšœŸ5˜SJ˜šœœ˜,J˜—šœ œœŸ˜.Jš˜Jšœœ˜JšœœH˜_JšœY˜YJšœ_˜_Jšœ_˜_Jšœ_˜_Jšœ_˜_Jšœ˜J˜—š œ œŸœœœ}˜ธšœ7˜7Jšœ$˜$Jšœ9˜9Jšœ(˜(Jšœ<˜Jšœ˜J˜J˜—Jš œœœœœŸ%˜PJ˜š  œœ'œœŸ!œ˜jJš˜šœ œ˜Jš˜Jšœœ˜ Jšœ$˜$Jšœœ˜JšœŸ$˜?J˜šœœ˜šœ˜Jš˜šœ˜Jš˜J˜š  œœ'œœŸ!œ˜pJš˜šœœ˜Jšœb˜b—Jšœ˜J˜—Jšœ™Jšœ–˜–Jšœ–˜–Jšœ–˜–Jšœ–˜–Jšœ˜—Jšœ˜—Jšœœ˜—Jšœ˜—Jšœ˜J˜J˜—š   œ œ'œœŸ!œ˜hJšœŸ+˜1J˜š œœ˜Jš˜Jšœœ˜ Jšœœ8˜>šœ˜Jš˜Jšœi˜iJšœ+˜+Jšœ*˜*Jšœ˜—Jšœ˜J˜—šœ œ˜Jšœ˜šœœ˜ šœ˜ Jšœ˜Jšœ œ˜Jšœœ˜——Jšœœ˜—Jšœ˜J˜—J˜š œœœ˜!Jšœ˜ Jšœ=˜=Jšœœœ#œœ œ œœœ˜šJšœ œœœ˜AJšœ˜J˜J˜—š  œœœ œœ œ˜fJš˜Jšœœœ œHœ@œœœœ ˜๗Jšœ,˜,Jšœ˜J˜J˜—š   œœœœœŸœ˜MJšœ˜J˜J˜—š œœ œœ˜BJš œœ œ œœ˜BJ˜J˜—š œœœœœœœœœ˜UJš˜Jšœ@˜@Jšœ œœ˜š œœœœœœ˜4Jšœ˜Jšœ˜—Jšœœ˜ šœœ ˜Jšœœœœ˜-š œœœœ%œ ˜LJšœœ ˜—Jšœ ˜ Jšœ˜—Jšœ˜J˜J˜—š  œœ#œœœœŸœ˜pJš˜Jšœ œœ˜Jšœ œœ˜Jšœ$œ ˜Jš œœœœœœ ˜:Jšœ(˜(Jšœ*˜*Jšœ&˜&Jš˜—JšœœŸ"˜.Jšœ˜—šœœœ)˜=JšœŸ˜(Jšœ˜—Jšœ'˜'Jšœ˜Jšœ˜—Jšœ˜Jšœ˜J˜J˜—š   œœ œœ œœ˜:Jš˜Jšœœ˜4Jšœ ˜ Jšœ˜Jšœ˜J˜J˜—š œœ œœœœœœœŸœ˜Jš˜J˜š  œœœœœœœœœœ˜PJš˜šœœ˜ Jš˜šœœ˜šœ˜šœœœœ˜9Jšœ ˜ Jšœ˜Jšœ-˜-Jšœ˜——š œœœœœ˜JšœO˜O—šœ˜ Jšœœœœ%˜N——Jšœ˜—Jšœ˜J˜—Jšœœ˜š œ œœœ3œ œ˜^Jšœ†˜†Jšœ˜—Jšœ˜J˜—J˜š œœœœœœŸœœ œœœ œ˜™Jš˜Jšœ œ˜Jšœ ˜ šœœœœœœœ˜AJšœœ ˜%Jšœ œ˜*Jšœ œ œ˜5Jšœ˜—Jšœ(˜(Jšœ˜š œœœœœœ˜;Jšœ˜Jšœ˜—Jšœ˜J˜J˜—Jš œ œœœœ˜+J˜š  œœœœœ˜7Jš˜Jšœœœ˜šœœœ˜Jšœœ ˜Jšœœœ ˜,Jšœœ˜#Jš œœœ œœ ˜>šœ˜ Jš˜Jš œœœœœ˜!Jšœ˜Jšœ&œ ˜3š˜Jšœ œ˜—Jšœ˜——Jšœ˜J˜J˜—š  œœœ#œœ ˜PJš˜JšœœŸ˜9Jšœœ˜JšœD™DJšœ œœœœœœœœœœœœ˜ Jšœ˜J˜J˜—š œœœœ˜2Jš˜šœ˜JšœG™G—Jšœœœ œ ˜:Jšœ˜J˜—š œœœ˜;Jš˜Jš œœœ œ œœ˜