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