-- ChipAlignImpl.mesa -- A package to generate the alignment patterns needed by the -- GCA Mann Model 4800 DSW wafer stepper. -- last modified by E. McCreight, August 6, 1982 2:56 PM -- written by E. McCreight, May 13, 1982 5:20 PM DIRECTORY ChipNetDefs, ChipReticle, ChipUserInt, CWF, LeftFeaturePQ, ppdefs, StringDefs, TimeDefs, WriteStringDefs; ChipAlignImpl: PROGRAM IMPORTS ChipNetDefs, ChipReticle, ChipUserInt, CWF, ppdefs, StringDefs, TimeDefs, WriteStringDefs EXPORTS ChipReticle SHARES ChipReticle = BEGIN OPEN ppdefs, ChipUserInt, ChipNetDefs, ChipReticle, LeftFeaturePQ; CircumscribeAndBeyond: PUBLIC PROCEDURE[ q: LeftFeaturePQHandle, design: CoordRect] = BEGIN OPEN StringDefs; clipOffset: CoordPoint ← [ x: halfScribeWidth-minXYScribeWidth, y: halfScribeWidth-minXYScribeWidth]; primaryAlign, secondaryAlign: CoordPoint; s1: STRING ← [100]; s2: STRING ← [100]; breathingRoom: Coord ← 25000--nm--; DrawFiducials[q]; PrintTitles[q ! WriteStringDefs.NoFont => {Explain[ "Font ""font.chip"" (from [indigo]<Chipmonk>WriteString", "is not on disk. Will continue without titles."]; CONTINUE}]; [alignedDesign: alignedDesign, primaryCenter: primaryAlign, secondaryCenter: secondaryAlign] ← PlaceAlignmentMarks[q, [x1: design.x1-breathingRoom-minXYScribeWidth, y1: design.y1-breathingRoom-minXYScribeWidth, x2: design.x2+breathingRoom+ 2*halfScribeWidth-minXYScribeWidth, y2: design.y2+breathingRoom+ 2*halfScribeWidth-minXYScribeWidth- alignMarkSize.y]]; FOR mask: Masks IN Masks DO offset, width: Coord; [offset, width] ← HalfScribeLines[mask]; maskStates[mask].fieldRect ← IF offset=0 AND width>0 AND maskStates[mask].fieldPolarity=transparent THEN [x1: alignedDesign.x1-clipOffset.x+width, y1: alignedDesign.y1-clipOffset.y+width, x2: alignedDesign.x2-clipOffset.x-width, y2: alignedDesign.y2-clipOffset.y-width] ELSE alignedDesign; ENDLOOP; DrawScribeLines[q: q, outline: [ x1: alignedDesign.x1-clipOffset.x, x2: alignedDesign.x2-clipOffset.x, y1: alignedDesign.y1-clipOffset.y, y2: alignedDesign.y2-clipOffset.y ]]; s1.length ← 0; AppendCoordPoint[s: s1, id: "Step"L, pt: [ x: alignedDesign.x2-alignedDesign.x1, y: alignedDesign.y2-alignedDesign.y1]]; s2.length ← 0; AppendCoordPoint[s: s2, id: "Align pos"L, pt: [ x: primaryAlign.x-reticleCenter.x, y: primaryAlign.y-reticleCenter.y]]; AppendString[to: s2, from: ", inter-mark dist ="]; AppendCoord[s: s2, n: secondaryAlign.x-primaryAlign.x]; AppendString[to: s2, from: " nm"]; WriteStringDefs.ReleaseFont[]; Explain[s1, s2]; END; -- of CircumscribeAndBeyond AppendCoordPoint: PROCEDURE[s: STRING, id: STRING, pt: CoordPoint] = BEGIN OPEN StringDefs; AppendString[to: s, from: id]; AppendString[to: s, from: " = [x:"L]; AppendCoord[s: s, n: pt.x]; AppendString[to: s, from: ", y:"L]; AppendCoord[s: s, n: pt.y]; AppendString[to: s, from: "]"L]; END; -- of AppendCoordPoint DrawScribeLines: PROCEDURE[q: LeftFeaturePQHandle, outline: CoordRect] = BEGIN mask: Masks; offset, width: Coord; scribedSize: CoordPoint ← [ x: outline.x2-outline.x1, y: outline.y2-outline.y1]; ScribeLine: PROCEDURE[p: CoordPoint, designLen: Coord, orient: orientationIndex] = BEGIN r, clippedR: CoordRect; r ← [x1: offset, y1: offset, x2: designLen-offset, y2: width+offset]; FOR o: orientationIndex ← 0, o+4 WHILE o<orient DO temp: CoordRect ← [x1: -r.y2, y1: r.x1, x2: -r.y1, y2: r.x2]; r ← temp; ENDLOOP; clippedR ← [ x1: MAX[p.x+r.x1, maskStates[mask].fieldRect.x1], x2: MIN[p.x+r.x2, maskStates[mask].fieldRect.x2], y1: MAX[p.y+r.y1, maskStates[mask].fieldRect.y1], y2: MIN[p.y+r.y2, maskStates[mask].fieldRect.y2]]; IF clippedR.x1<clippedR.x2 AND clippedR.y1<clippedR.y2 THEN [] ← NewReticleFeature[cover: clippedR, mask: mask, lq: q]; END; -- of ScribeLine ScribeAround: PROCEDURE[r: CoordRect] = BEGIN Corner: PROCEDURE[i: [0..3]] RETURNS[p: CoordPoint] = {RETURN[(SELECT i FROM 0 => [x: r.x1, y: r.y1], 1 => [x: r.x2, y: r.y1], 2 => [x: r.x2, y: r.y2], ENDCASE => [x: r.x1, y: r.y2])]}; FOR i: [0..3] IN [0..3] DO c, cNxt: CoordPoint; c ← Corner[i]; cNxt ← Corner[(i+1) MOD 4]; ScribeLine[p: c, designLen: ABS[c.x-cNxt.x]+ABS[c.y-cNxt.y], orient: 4*i]; ENDLOOP; END; -- of ScribeAround FOR mask IN Masks DO [offset, width] ← HalfScribeLines[mask]; IF width>0 THEN FOR dx: Coord ← -scribedSize.x, dx+scribedSize.x WHILE dx<=scribedSize.x DO FOR dy: Coord ← -scribedSize.y, dy+scribedSize.y WHILE dy<=scribedSize.y DO ScribeAround[[ x1: outline.x1+dx, x2: outline.x2+dx, y1: outline.y1+dy, y2: outline.y2+dy]]; ENDLOOP; ENDLOOP; ENDLOOP; END; -- of DrawScribeLines minXYScribeWidth: Coord ← 6000+14000--nm--; halfScribeWidth: Coord ← 75000+minXYScribeWidth--nm--; HalfScribeLines: PROCEDURE[mask: Masks] RETURNS[offset, width: NanoMeters] = BEGIN -- This procedure interprets somewhat liberally -- the specifications on page 34 -- of revision 3A, February, 1980, of the Xerox NSIL-II -- Electrical Specifications and Layout Design Rules SELECT mask FROM pad => BEGIN offset ← 0; -- from center of scribe line width ← 75000--nm--; END; thinOx, nImplant, nBuriedContact, cut, via => BEGIN offset ← 0; width ← 75000+13000--nm--; END; poly => BEGIN offset ← 75000+6000--nm--; width ← 14000--nm--; END; ENDCASE => offset ← width ← 0; END; -- of ScribeLines StepGrid: PROCEDURE[x: Coord, grid: Coord ← 10000 -- 10 um --, direction: {larger, smaller} ← larger] RETURNS[Coord] = BEGIN -- returns next "direction" Coord on grid RETURN[IF (x MOD grid = 0) THEN x ELSE (x+grid-(x MOD grid)- (IF direction=smaller THEN grid ELSE 0))]; END; PlaceAlignmentMarks: PUBLIC PROCEDURE[ q: LeftFeaturePQHandle, scribedDesign: CoordRect] RETURNS[alignedDesign: CoordRect, primaryCenter, secondaryCenter: CoordPoint] = BEGIN OPEN StringDefs; primaryPlace, secondaryPlace: CoordPoint; lensSpacing: Coord = 63500000; -- 2.500 inches in nm forbiddenSpacing: Coord ← 2*alignMarkSize.x+50000; -- 50 micrometers plus two alignment mark sizes xStep: Coord ← StepGrid[ MAX[scribedDesign.x2-scribedDesign.x1, 2*forbiddenSpacing]]; alignmentPairSpacing: Coord ← ((lensSpacing+(xStep/2)) MOD xStep)-(xStep/2); primaryPlace ← [x: scribedDesign.x1+14000+6000--nm--, y: scribedDesign.y2]; DO IF alignmentPairSpacing NOT IN (-forbiddenSpacing..forbiddenSpacing) THEN EXIT; xStep ← StepGrid[xStep+1+ (alignmentPairSpacing+forbiddenSpacing)/ (lensSpacing/xStep)]; alignmentPairSpacing ← ((lensSpacing+(xStep/2)) MOD xStep)-(xStep/2); ENDLOOP; IF alignmentPairSpacing<0 THEN primaryPlace.x ← primaryPlace.x-alignmentPairSpacing; secondaryPlace ← [x: primaryPlace.x+alignmentPairSpacing, y: primaryPlace.y]; primaryCenter ← [x: primaryPlace.x+alignMarkSize.x/2, y: primaryPlace.y+alignMarkSize.y/2]; secondaryCenter ← [x: secondaryPlace.x+alignMarkSize.x/2, y: secondaryPlace.y+alignMarkSize.y/2]; DrawAlignmentMark[q: q, place: primaryPlace, shape: primary, id: "P"]; DrawAlignmentMark[q: q, place: [ x: primaryPlace.x+alignMarkSize.x, y: primaryPlace.y], shape: primary, polarity: inverted, id: "P"]; DrawAlignmentMark[q: q, place: secondaryPlace, shape: secondary, id: "S"]; DrawAlignmentMark[q: q, place: [ x: secondaryPlace.x+alignMarkSize.x, y: secondaryPlace.y], shape: secondary, polarity: inverted, id: "S"]; alignedDesign ← [x1: scribedDesign.x1, y1: scribedDesign.y1, x2: scribedDesign.x1+xStep, y2: scribedDesign.y2+alignMarkSize.y]; END; -- of PlaceAlignmentMarks alignMarkSize: CoordPoint = [x: 150000--nm--, y: 150000--nm--]; DrawAlignmentMark: PROCEDURE[ q: LeftFeaturePQHandle, place: CoordPoint, shape: {primary, secondary}, polarity: {normal, inverted} ← normal, id: STRING ← NIL] = BEGIN firstMask: Masks ← IF ppdefs.cMos THEN nWell ELSE thinOx; alignBox: CoordRect ← [x1: place.x, y1: place.y, x2: place.x+alignMarkSize.x, y2: place.y+alignMarkSize.y]; alignCenter: CoordPoint ← [x: (alignBox.x1+alignBox.x2)/2, y: (alignBox.y1+alignBox.y2)/2]; PlaceInAllOctants: PROCEDURE[type: {active, field}, r: CoordRect] = BEGIN IF (type=active AND polarity=normal) OR (type=field AND polarity=inverted) THEN FOR quadrant: [0..3] IN [0..3] DO t: CoordRect ← [ x1: -r.y2 , x2: -r.y1, y1: r.x1, y2: r.x2]; [] ← NewReticleFeature[ cover: [x1: alignCenter.x+r.x1, x2: alignCenter.x+r.x2, y1: alignCenter.y+r.y1, y2: alignCenter.y+r.y2], mask: firstMask, lq: q]; [] ← NewReticleFeature[ cover: [x1: alignCenter.x+r.y1, x2: alignCenter.x+r.y2, y1: alignCenter.y+r.x1, y2: alignCenter.y+r.x2], mask: firstMask, lq: q]; r ← t; ENDLOOP; END; -- of PlaceInAllOctants IF id#NIL AND id.length>0 THEN BEGIN idSize: Coord ← 20000 -- nm --; scale: Coord; offset: CoordPoint; box: CoordRect ← [ x1: alignCenter.x-idSize, x2: alignCenter.x+idSize, y1: alignCenter.y-idSize, y2: alignCenter.y+idSize]; [scale: scale, offset: offset] ← Center[s: id, box: box ! WriteStringDefs.NoFont => GOTO NoText]; DrawString[s: id, q: q, mask: firstMask, place: [x: box.x1+offset.x, y: box.y1+offset.y], scale: scale ! WriteStringDefs.NoFont => GOTO NoText]; EXITS NoText => NULL; END; PlaceInAllOctants[field, [x1: 1000, x2: alignMarkSize.x/2, y1: 16000, y2: 25000]]; PlaceInAllOctants[active, [x1: 0, x2: 1000, y1: 16000, y2: 16000+46000]]; SELECT shape FROM primary => BEGIN FOR d: Coord ← 25000, d+1000 UNTIL d>=67000 DO PlaceInAllOctants[active, [x1: d, x2: d+1800, y1: d, y2: d+1800]]; PlaceInAllOctants[field, [x1: (IF d<16000+46000 THEN 1000 ELSE 0), x2: d, y1: (IF d=25000 THEN d ELSE d+800), y2: d+1800]]; ENDLOOP; PlaceInAllOctants[field, [x1: 0, x2: alignMarkSize.x/2, y1: 66000+1800, y2: alignMarkSize.y/2]]; END; secondary => BEGIN PlaceInAllOctants[field, [x1: 1000, x2: alignMarkSize.x/2, y1: 25000, y2: 16000+46000]]; PlaceInAllOctants[field, [x1: 0, x2: alignMarkSize.x/2, y1: 16000+46000, y2: alignMarkSize.y/2]]; END; ENDCASE; FOR mask: Masks IN Masks DO SELECT mask FROM cut, via => [] ← NewReticleFeature[cover: alignBox, mask: mask, lq: q]; thinOx => IF ppdefs.cMos THEN [] ← NewReticleFeature[cover: alignBox, mask: mask, lq: q]; ENDCASE => NULL; ENDLOOP; END; -- of DrawAlignment DrawFiducials: PUBLIC PROCEDURE[q: LeftFeaturePQHandle] = BEGIN DrawAFiducial[q, minus, -- on the left, centered in an 2mm box on the reticle CoordBox[ center: [x: reticleCenter.x- CoordReticleMeasure[microMetersOnReticle: 51500], y: reticleCenter.y], diameter: [x: CoordReticleMeasure[microMetersOnReticle: 2000], y: CoordReticleMeasure[microMetersOnReticle: 2000]]]]; DrawAFiducial[q, plus, -- on the right, centered in an 800um box on the reticle CoordBox[ center: [x: reticleCenter.x+ CoordReticleMeasure[microMetersOnReticle: 51500], y: reticleCenter.y], diameter: [x: CoordReticleMeasure[microMetersOnReticle: 2000], y: CoordReticleMeasure[microMetersOnReticle: 2000]]]]; END; -- of DrawFiducials DrawAFiducial: PROCEDURE[q: LeftFeaturePQHandle, shape: {minus, plus}, box: CoordRect] = BEGIN -- In this procedure we draw transparent (clear) rectangles -- on a opaque fieldPolarity, so we are actually drawing the -- box around the fiducial mark. DrawXStripe: PROCEDURE[y1, y2: Coord, centerWidth: Coord ← 0] = BEGIN IF y2-y1>0 -- [y1..y2) -- THEN BEGIN IF centerWidth=0 THEN [] ← NewReticleFeature[ cover: [x1: box.x1, x2: box.x2, y1: y1, y2: y2], mask: mask, lq: q] ELSE BEGIN centerLeft: Coord ← box.x1+(box.x2-box.x1-centerWidth)/2; centerRight: Coord ← centerLeft+centerWidth; [] ← NewReticleFeature[ cover: [x1: box.x1, x2: centerLeft, y1: y1, y2: y2], mask: mask, lq: q]; [] ← NewReticleFeature[ cover: [x1: centerRight, x2: box.x2, y1: y1, y2: y2], mask: mask, lq: q]; END; END; END; -- of DrawXStripe -- The fiducial shapes and placement are taken from -- a page labelled Dwg. No. A-SI-86-15 Rev. 3, obtained from -- Paul Martin on 28 April 1982. mask: Masks; fiducialLength: Coord ← (box.x2-box.x1)- CoordReticleMeasure[microMetersOnReticle: 100]; fiducialWidth: Coord ← CoordReticleMeasure[microMetersOnReticle: 25]; boxHeight: Coord ← box.y2-box.y1; plusTop: Coord ← box.y1+(boxHeight-fiducialLength)/2; minusTop: Coord ← box.y1+(boxHeight-fiducialWidth)/2; minusBottom: Coord ← minusTop+fiducialWidth; plusBottom: Coord ← plusTop+fiducialLength; FOR mask IN Masks DO DrawXStripe[y1: minusTop, y2: minusBottom, centerWidth: fiducialLength]; SELECT shape FROM plus => BEGIN DrawXStripe[y1: box.y1, y2: plusTop]; DrawXStripe[y1: plusTop, y2: minusTop, centerWidth: fiducialWidth]; DrawXStripe[y1: minusBottom, y2: plusBottom, centerWidth: fiducialWidth]; DrawXStripe[y1: plusBottom, y2: box.y2]; END; minus => BEGIN DrawXStripe[y1: box.y1, y2: minusTop]; DrawXStripe[y1: minusBottom, y2: box.y2]; END; ENDCASE; ENDLOOP; END; -- of DrawFiducial CoordReticleMeasure: PROCEDURE[ microMetersOnReticle: LONG INTEGER] RETURNS[Coord] = BEGIN lensReduction: INTEGER = 10; RETURN[(1000*microMetersOnReticle)/lensReduction]; END; -- of CoordReticleMeasure PrintTitles: PROCEDURE[ q: LeftFeaturePQ.LeftFeaturePQHandle] = BEGIN OPEN TimeDefs, CWF; PrintATitle: PROCEDURE[lineNo: LONG INTEGER] = {DrawString[s: s, place: [x: bottomLine.x, y: bottomLine.y+lineNo*titleSpacing], scale: titleScale, mask: mask, q: q]}; bottomLine: CoordPoint ← [ x: reticleCenter.x-CoordReticleMeasure[ microMetersOnReticle: 40000], y: reticleCenter.y+CoordReticleMeasure[ microMetersOnReticle: 50000]]; titleHeight: Coord ← CoordReticleMeasure[ microMetersOnReticle: 1000]; titleSpacing: Coord ← (3*titleHeight)/2; titleScale: Coord ← Center[s: "A", box: [x1: 0, y1: 0, x2: LAST[Coord], y2: titleHeight]].scale; mask: Masks; s: STRING ← [100]; time: STRING ← [20]; project: STRING ← RequestString[ s1: "Reticle project identification:"L, lowerCaseOK: TRUE]; submitter: STRING ← RequestString[ s1: "Reticle submitter identification:"L, lowerCaseOK: TRUE]; time.length ← 0; AppendDayTime[time, UnpackDT[CurrentDayTime[]]]; SWF1[s, "Reticle time stamp: %s"L, time]; Explain[s]; mask ← unknown; -- indicates all masks SWF3[s, "Xerox PARC Project: %s Submitter: %s Date: %s"L, project, submitter, time]; PrintATitle[-1]; FOR mask IN Masks DO IF mask#unknown AND maskStates[mask].inReticleSet THEN BEGIN SWF1[s, " Mask: %s"L, levelNames[mask]]; PrintATitle[0]; END; ENDLOOP; FreeString[project]; FreeString[submitter]; WriteStringDefs.ReleaseFont[]; END; -- of PrintTitles DrawString: PROCEDURE[s: STRING, place: CoordPoint, scale: Coord, mask: Masks, q: LeftFeaturePQ.LeftFeaturePQHandle] = BEGIN PlaceStringFeatures: PROCEDURE[r: Rect] = BEGIN [] ← NewReticleFeature[ cover: [x1: place.x+scale*r.x1, x2: place.x+scale*r.x2, y1: place.y+scale*r.y1, y2: place.y+scale*r.y2], mask: mask, lq: q]; END; WriteStringDefs.StringToRectangles[s: s, proc: PlaceStringFeatures]; END; -- of DrawString Center: PROCEDURE[s: STRING, box: CoordRect] RETURNS[scale: Coord, offset: CoordPoint -- from [.x1, .y1] --] = BEGIN bdry: Rect ← MeasureSize[s]; scale ← StepGrid[x: MIN[ (box.x2-box.x1)/(bdry.x2-bdry.x1), (box.y2-box.y1)/(bdry.y2-bdry.y1)], grid: 500/4 --nm--, direction: smaller]; offset ← [x: ((box.x2-box.x1)-scale*(bdry.x2+bdry.x1))/2, y: ((box.y2-box.y1)-scale*(bdry.y2+bdry.y1))/2]; END; -- of Center MeasureSize: PROCEDURE[s: STRING] RETURNS[bdry: Rect] = BEGIN RectSize: PROCEDURE[r: Rect] = {bdry ← [x1: MIN[bdry.x1, r.x1], x2: MAX[bdry.x2, r.x2], y1: MIN[bdry.y1, r.y1], y2: MAX[bdry.y2, r.y2]]}; bdry ← [x1: LAST[locNum], x2: FIRST[locNum], y1: LAST[locNum], y2: FIRST[locNum]]; WriteStringDefs.StringToRectangles[s: s, proc: RectSize]; END; -- of MeasureSize END. -- of ChipAlignImpl