-- 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