-- ChipExpandImpl.mesa

-- A package to expand Chipmonk geometries for circuit
-- extraction.

-- last modified by E. McCreight, January 10, 1983  6:15 PM
-- written by E. McCreight, December 11, 1981  11:46 AM

DIRECTORY
  CellInstPQ,
  ChipDRC,
  ChipExpand,
  ChipFeature,
  ChipNetDefs,
  ChipOrient,
  ChipReticle,
  ChipWire,
  LeftFeaturePQ,
  MiscDefs,
  ppdddefs,
  ppdefs,
  RightFeaturePQ;

ChipExpandImpl: PROGRAM
  IMPORTS CellInstPQ, ChipDRC, ChipExpand,
    ChipFeature, ChipNetDefs,
    ChipOrient, ChipReticle,
    MiscDefs, ppdddefs, ppdefs
  EXPORTS ChipNetDefs, ChipExpand, ppdefs
  SHARES ChipExpand =
  BEGIN OPEN ppdddefs, ppdefs,
    ChipNetDefs, ChipDRC, ChipExpand;

  aux: PUBLIC TYPE = CellProto;

  levIsConductor: ARRAY level OF BOOLEAN ← wireOK;
  joinTo: PUBLIC LONG POINTER TO netPtr ← NIL;
  xstrInst: PUBLIC XstrCallPtr ← NIL;

  ExtractorFeature: PUBLIC ExtractorFeatureProc;

  MakeInstance: PUBLIC PROCEDURE[item: ItemRef,
    slice: ChipWire.SlicePtr,
    futureFeatures: LeftFeaturePQ.LeftFeaturePQHandle,
    presentFeatures: RightFeaturePQ.RightFeaturePQHandle,
    cellQ, deadCellQ: CellInstPQ.CellInstPQHandle] =
    BEGIN OPEN ChipOrient;
    WITH caller: item.head SELECT FROM
      cell =>
        BEGIN
        firstX1: Coord;
        itemCount: CARDINAL ← caller.proto.itemCount;
        FOR idx: CellIndex ← item.idx, idx+1
          WHILE idx<itemCount DO

          ExplodeFeatures: PROCEDURE[p: drProc,
            joinByMagic: BOOLEAN ← FALSE,
            useText: BOOLEAN ← TRUE,
            cutPolyGapRequired: BOOLEAN ← FALSE] =
            BEGIN

            ExtractorNextFeature: PROCEDURE[
              x1, y1, x2, y2: locNum, lev: ExtractLevel] =
              BEGIN
              IF x1<=x2 AND y1<=y2 THEN
                BEGIN
                innerR: Rect ←  ChipOrient.MapRect[
                  itemInCell: [x1: x1, y1: y1, x2: x2, y2: y2],
                  cellInstOrient: callLp.idx,
                  cellSize: [x: callLp.ob.size[0], y: callLp.ob.size[1]],
                  cellInstPos: [x: callLp.lx, y: callLp.ly]
                  ];
                ob: obPtr ← caller.proto.ob;
                r: Rect ← ChipOrient.MapRect[
                    itemInCell: innerR,
                    cellInstOrient: caller.orient,
                    cellSize: [x: ob.size[0], y: ob.size[1]]];
                min: CoordPoint ← caller.min;
                scale: Coord ← coordScale[caller.proto.locNumScale];
                cr: CoordRect ← [
                  x1: min.x+scale*r.x1,
                  y1: min.y+scale*r.y1,
                  x2: min.x+scale*r.x2,
                  y2: min.y+scale*r.y2];

                [] ← ChipFeature.NewFeature[cover: cr, lev: lev,
                  caller: nextItem, lq: futureFeatures];
                END;
              END; -- of ExtractorNextFeature

            NextFeature: PROCEDURE[x1, y1, x2, y2: locNum,
              l: level, p: POINTER TO Rect] =
              BEGIN

              IF x1<=x2 AND y1<=y2 THEN
                BEGIN
                AddFeature: PROCEDURE[lev: ExtractLevel,
                  grow: Coord ← 0, net: netPtr ← NIL] =
                  BEGIN
                  f ← ChipFeature.NewFeature[
                    cover: [x1: cr.x1-grow,
                      y1: cr.y1-grow,
                      x2: cr.x2+grow,
                      y2: cr.y2+grow],
                      lev: lev, caller: nextItem,
                      lq: futureFeatures];
                  IF net#NIL THEN f.net ← RefCanonNet[net];
                  END;

                f: FeaturePtr;
                innerR: Rect ←  ChipOrient.MapRect[
                  itemInCell: [x1: x1, y1: y1, x2: x2, y2: y2],
                  cellInstOrient: callLp.idx,
                  cellSize: [x: callLp.ob.size[0], y: callLp.ob.size[1]],
                  cellInstPos: [x: callLp.lx, y: callLp.ly]
                  ];
                ob: obPtr ← caller.proto.ob;
                r: Rect ← ChipOrient.MapRect[
                    itemInCell: innerR,
                    cellInstOrient: caller.orient,
                    cellSize: [x: ob.size[0], y: ob.size[1]]];
                min: CoordPoint ← caller.min;
                scale: Coord ← coordScale[caller.proto.locNumScale];
                cr: CoordRect ← [
                  x1: min.x+scale*r.x1,
                  y1: min.y+scale*r.y1,
                  x2: min.x+scale*r.x2,
                  y2: min.y+scale*r.y2];

                IF l#unknown THEN
                  BEGIN
                  f ← ChipFeature.NewFeature[cover: cr, lev: l,
                    caller: nextItem, lq: futureFeatures];
                  f.net ← (SELECT TRUE FROM
                      ChipReticle.makingReticles => NIL,
                      NOT levIsConductor[l] => NIL,
                      net=NIL AND useText AND
                        getTextProp[callLp]#NIL =>
                        (net ← NewQualifiedNet[f: f]),
                      joinByMagic =>
                        (net ← JoinNet[net: net, f: f]),
                      ENDCASE => NIL);

                  IF joinTo#NIL THEN
                    BEGIN
                    f.net ← joinTo↑ ← (IF f.net=NIL
                      THEN JoinNet[net: joinTo↑, f: f]
                      ELSE MergeNets[n1: joinTo↑, n2: f.net]);
                    joinTo ← NIL;
                    END;

                  IF doingDRC AND f.net#NIL AND
                    (joinByMagic -- a contact -- OR
                    (SELECT f.lev FROM
                      metal, metal2 => FALSE,
                      ENDCASE => levIsConductor[l]))
                    THEN
                    GetNormalNetId[@f.net].couldBeLogo ← FALSE;

                  END;

                IF ChipReticle.makingReticles THEN
                  SELECT l FROM
                    dif =>
                      BEGIN
                      AddFeature[lev: nImplant,
                        grow: implantBeyondThinOx];
                      AddFeature[lev: pWell,
                        grow: wellBeyondDiffusion];
                      END;
                    pdif =>
                      BEGIN
                      AddFeature[lev: pImplant,
                        grow: implantBeyondThinOx];
                      AddFeature[lev: nWell,
                        grow: wellBeyondDiffusion];
                      END;
                    pwelCont =>
                      BEGIN
                      AddFeature[lev: pImplant,
                        grow: implantBeyondThinOx];
                      AddFeature[lev: pWell,
                        grow: wellBeyondDiffusion];
                      END;
                    nwelCont =>
                      BEGIN
                      AddFeature[lev: nImplant,
                        grow: implantBeyondThinOx];
                      AddFeature[lev: nWell,
                        grow: wellBeyondDiffusion];
                      END;
                    ENDCASE => NULL
                ELSE IF doingDRC THEN
                  SELECT l FROM
                    nwel =>
                      AddFeature[lev: nPlusForbidden,
                        grow: nWellToNPlus];
                    nwelCont =>
                      BEGIN
                      AddFeature[lev: pPlusForbidden,
                        grow: nPlusToPPlus];
                      AddFeature[lev: nWell,
                        grow: wellBeyondDiffusion];
                      net ← WellNet[net: net, f: f];
                      AddFeature[lev: nPlusForbidden,
                        grow: wellBeyondDiffusion+nWellToNPlus];
                      END;
                    pwelCont =>
                      BEGIN
                      AddFeature[lev: nPlusForbidden,
                        grow: nPlusToPPlus];
                      AddFeature[lev: pWell,
                        grow: wellBeyondDiffusion];
                      net ← WellNet[net: net, f: f];
                      AddFeature[lev: pPlusForbidden,
                        grow: wellBeyondDiffusion+pWellToPPlus];
                      END;
                    cut => IF cutPolyGapRequired THEN
                      AddFeature[lev: polyForbidden, grow: cutPolyGap,
                        net: net];
                    via => AddFeature[lev: cutForbidden,
                        grow: viaCutGap, net: net];

                    ENDCASE => NULL;
                  END;
                END; -- of NextFeature

            nullOutline: PROCEDURE[a,b,c,d: INTEGER, col: color,
              r: POINTER TO Rect] = {NULL};

            nullText: PROCEDURE[a,b,c,d: INTEGER, s: STRING,
              r: POINTER TO Rect] = {NULL};

            featureProcs: drRecord ← [
              r: [x1: FIRST[locNum], x2: LAST[locNum],
                y1: FIRST[locNum], y2: LAST[locNum]],
              bigr: [x1: FIRST[locNum], x2: LAST[locNum],
                y1: FIRST[locNum], y2: LAST[locNum]],
              orArea: NextFeature, saveArea: NextFeature,
              outl: nullOutline, dtxt: nullText, minSize: 0];

            net: CanonNetPtr ← NIL;
            ExtractorFeature.p ← ExtractorNextFeature;
            joinTo ← NIL;
            IF ChipReticle.makingReticles THEN
              callLp.ob.p.drawme[0]
                [ob: callLp.ob, x: 0, y: 0, pr: @featureProcs]
            ELSE p[ob: callLp.ob, x: 0, y: 0, pr: @featureProcs];
            IF net#NIL THEN net ← DeRefNet[net];
            END; -- of ExplodeFeatures

          callLp: listPtr ← ProtoSeq[@caller][idx];
          nextItem: ItemRef ← [@caller, idx];
          r: CoordRect ← ItemInWorld[nextItem];

          SELECT TRUE FROM
            idx=item.idx => firstX1 ← r.x1;
            r.x1=firstX1 => NULL;
            ENDCASE =>
              BEGIN -- re-insert remainder of caller
              CellInstPQ.InsertCellInstPQ[p: cellQ,
                item: [x: ItemInWorld[[@caller, idx]].x1,
                call: [@caller, idx]]];
              EXIT;
              END;

          IF caller.caller.head#NIL OR callLp.selected OR
            ChipReticle.makingReticles THEN -- extract it!
           WITH ob: callLp.ob SELECT FROM
            cell =>
              BEGIN
              inst: InstancePtr ← instanceZ.NEW[cell Instance ←
                [min: [x: r.x1, y: r.y1],
                orient: ComposeOrient[cellOrientInWorld: caller.orient,
                  itemOrientInCell: callLp.idx],
                proto: MakeProto[@ob, caller.proto.locNumScale],
                caller: nextItem,
                sibling: caller.offspring,
                nets:  cell[]]];
              IF NOT ChipReticle.makingReticles THEN
                caller.offspring ← inst;
              [] ← ProtoSeq[inst]; -- make sure it's sorted in the
                -- orientation in which it's called
              CellInstPQ.InsertCellInstPQ[p: cellQ,
                item: [x: ItemInWorld[[inst, 0]].x1, call: [inst, 0]]];
              CellInstPQ.InsertCellInstPQ[p: deadCellQ,
                item: [x: ItemInWorld[inst.caller].x2, call: [inst, 0]]];
              END;
            xstr =>
              BEGIN
              IF NOT ChipReticle.makingReticles THEN
                BEGIN -- put transistor instance in structure tree
                xstrInst ← instanceZ.NEW[xstr Instance ←
                  [min: [x: r.x1, y: r.y1],
                  orient: ComposeOrient[
                    cellOrientInWorld: caller.orient,
                    itemOrientInCell: callLp.idx],
                  proto: MakeProto[@ob, caller.proto.locNumScale],
                  caller: nextItem,
                  sibling: caller.offspring,
                  nets:  xstr[]]];
                caller.offspring ← xstrInst;
                END;
              ExplodeFeatures[SELECT TRUE FROM
                ob.pullup => exPu0,
                ob.angle => exAXstr0,
                ENDCASE => exXstr0];
              END;
            cont => ExplodeFeatures[
              p: (SELECT ob.typ FROM
                butt => exBC0,
                burr => exBuC0,
                ENDCASE => ob.p.drawme[0]),
              joinByMagic: TRUE, useText: TRUE,
              cutPolyGapRequired: (SELECT ob.typ FROM
                mDif, mPDif, mmDif, mmPDif => TRUE,
                ENDCASE => FALSE)];
            bus =>
              ExplodeFeatures[p: ob.p.drawme[0]];
            wire, rect =>
              ExplodeFeatures[p: ob.p.drawme[0], useText: TRUE];
            ENDCASE => NULL;
          ENDLOOP; -- on idx
        END; -- of cell
      ENDCASE;
    END; -- of MakeInstance


  ExitInstance: PUBLIC PROCEDURE[inst: InstancePtr] =
    BEGIN
    IF NOT ChipReticle.makingReticles THEN
      WITH dinst: inst SELECT FROM
        cell =>
          IF ChipReticle.makingReticles
          THEN instanceZ.FREE[@inst]
            -- reticle making doesn't keep the structure tree
          ELSE CheckClusters[@dinst];
        ENDCASE => NULL;
    END; -- of ExitCellCall


  NewQualifiedNet: PROCEDURE[f: FeaturePtr] RETURNS[CanonNetPtr] =
    BEGIN
    net: netPtr ← NewNet[2];
    id: NormalNetIdPtr = GetNormalNetId[@net];
    id.source ← NearestCellInstance[f.caller.head];
    id.final ← [lev: f.lev, r: f.cover];
    id.name ← qualified[see: f.caller.idx];
    RETURN[CanonNet[net]];
    END;


  JoinNet: PROCEDURE[net: NetPtr, f: FeaturePtr]
    RETURNS[cn: CanonNetPtr] =
    BEGIN
    IF net=NIL THEN
      BEGIN
      cn ← NewNet[2];
      WITH did: cn.id SELECT FROM
        normal => did.source ← NearestCellInstance[f.caller.head];
        ENDCASE => NULL;
      END
    ELSE cn ← RefCanonNet[net];
    IF cn.id.final.r.x2<f.cover.x2 THEN
      cn.id.final ← [lev: f.lev, r: f.cover];
    END; -- of JoinNet


  WellNet: PROCEDURE[net: NetPtr, f: FeaturePtr]
    RETURNS[cn: CanonNetPtr] =
    BEGIN
    wn: NetIdPtr;
    IF net=NIL THEN
      BEGIN
      cn ← NewNet[2];
      WITH did: cn.id SELECT FROM
        normal => did.source ← NearestCellInstance[f.caller.head];
        ENDCASE => NULL;
      END
    ELSE cn ← RefCanonNet[net];
    wn ← (f.net ← NewNet[1]).id;
    wn.details ← well[attachedTo: cn];
    wn.final ← [lev: f.lev, r: f.cover];
    END; -- of WellNet


  NearestCellInstance: PUBLIC PROCEDURE[p: InstancePtr]
    RETURNS[CellCallPtr] =
    BEGIN
    WHILE p#NIL DO
      WITH dp: p SELECT FROM
        cell => RETURN[@dp];
        ENDCASE => p ← dp.caller.head;
      ENDLOOP;
    RETURN[NIL];
    END;


  CheckClusters: PUBLIC PROCEDURE[call: CellCallPtr] =
    BEGIN
    callX2: Coord ←
      (IF call.caller.head#NIL THEN ItemInWorld[call.caller].x2
      ELSE LAST[Coord]);
    FOR cluster: ClusterPtr ← call.clusters, cluster.next
      WHILE cluster#NIL DO
      CheckCluster[call, callX2, @cluster.first];
      ENDLOOP;
    END; -- of CheckClusters


  CheckCluster: PROCEDURE[call: CellCallPtr, callX2: Coord,
      cluster: MustConnectPtr] =
    BEGIN
    mustConnect, nextMustConnect: MustConnectPtr;

    PreviousNet: PROCEDURE[net: netPtr] RETURNS[BOOLEAN] =
      BEGIN
      FOR mcp: MustConnectPtr ← cluster, mcp.next
        WHILE mcp#NIL DO
        IF net=mcp.net THEN RETURN[TRUE];
        ENDLOOP;
      RETURN[FALSE];
      END;

    IF cluster.next=NIL THEN RETURN; -- singleton

    -- First, try to locate a still-active net for the
    -- cluster head.
    FOR mustConnect ← cluster, mustConnect.next WHILE
      mustConnect#NIL DO
      IF callX2<=
        (mustConnect.net ← CanonNet[mustConnect.net]).id.final.r.x2
        THEN
        BEGIN
        copyCluster: MustConnect ← cluster↑;
        copyMustConnect: MustConnect ← mustConnect↑;
        copyCluster.next ← mustConnect.next;
        copyMustConnect.next ← cluster.next;
        cluster↑ ← copyMustConnect;
        mustConnect↑ ← copyCluster;
        EXIT;
        END;
      ENDLOOP;

    nextMustConnect ← cluster.next;
    cluster.next ← NIL;
    FOR mustConnect ← nextMustConnect, nextMustConnect
      WHILE mustConnect#NIL DO
      mustConnectNet: CanonNetPtr ← mustConnect.net ←
        CanonNet[mustConnect.net];
      nextMustConnect ← mustConnect.next;
      SELECT TRUE FROM
        PreviousNet[mustConnectNet] =>
          FreeMustConnect[call, mustConnect];
          -- same as some previous constraint
        ENDCASE =>
          BEGIN
          mustConnect.next ← cluster.next;
          cluster.next ← mustConnect;
          END;
      ENDLOOP;
    END; -- of CheckCluster


  FreeMustConnect: PROCEDURE[call: CellCallPtr,
    mustConnect: MustConnectPtr] =
    BEGIN
    mustConnect.net ← DeRefNet[mustConnect.net];
    clusterZ.FREE[@mustConnect];
    END; -- of FreeMustConnect

  ProtoSeq: PUBLIC PROCEDURE[inst: InstancePtr]
    RETURNS[ListPtrSeqPtr] =
    BEGIN
    sortClass: SortClass ← orientToSortClass[inst.orient];
    proto: ProtoPtr ← inst.proto;
    IF proto.seq[sortClass]=NIL THEN
      BEGIN
      i: CellIndex ← 0;
      lps: ListPtrSeqPtr ← uz.NEW[ListPtrSeq[proto.itemCount]];
      FOR lpp: listPtr ← proto.lp, lpp.nxt WHILE lpp#NIL DO
        lps[i] ← lpp;
        i ← i+1;
        ENDLOOP;
      SortListPtrSeq[lps, SortOrder[sortClass]];
      proto.seq[sortClass] ← lps;
      END;
    RETURN[proto.seq[sortClass]];
    END;

  MakeProto: PUBLIC PROCEDURE[ob: obPtr,
    locNumScale: LocNumScale ← lambdaRelative, name: STRING ← NIL]
    RETURNS[c: ProtoPtr] =
    BEGIN
    IF ob.auxPnt=NIL THEN
      BEGIN -- We'll have to make one up.
      scale: Coord ←  coordScale[locNumScale];
      cp: ProtoPtr ← ob.auxPnt ← uz.NEW[CellProto ←
        [ob: ob,
        locNumScale: locNumScale,
        stretchMasks: locNumScale=lambdaRelative,
        size: [x: scale*ob.size[0],
          y: scale*ob.size[1]]]];
      count: CARDINAL ← 0;
      WITH o: ob SELECT FROM
        cell =>
          BEGIN
          FOR lp: listPtr ← o.ptr, lp.nxt WHILE lp#NIL DO
            count ← count+1
            ENDLOOP;
          cp.lp ← o.ptr;
          END;
        xstr => cp.lp ← NIL;
        ENDCASE =>
          MiscDefs.CallDebugger["Can't expand this ob type"];
      cp.itemCount ← count;
      IF name#NIL THEN cp.name ← name 
      ELSE FOR cl: LONG POINTER TO cList ← cellList, cl.nxt
        WHILE cl#NIL DO
        IF cl.ob=ob THEN {cp.name ← cl.name; EXIT};
        ENDLOOP;
      END;
    RETURN[ob.auxPnt];
    END; -- of MakeProto

  origCifScale: PUBLIC INTEGER; -- centiMicrometers per lambda
  lambdaRelativeCoordScale: PUBLIC Coord;
    -- Coords per ppdefs.locNum
  coordScale: PUBLIC ARRAY LocNumScale OF Coord;

  SetCoordScale: PROCEDURE[] =
    BEGIN -- from Chipmonk feedback window
    origCifScale ← ppdddefs.pCifScale;
      -- centiMicrometers per lambda
    lambdaRelativeCoordScale ← -- to Coords = nanometers
      (origCifScale*10)/Lambda;
    coordScale ← -- to nanometers
      [
      lambdaRelative: lambdaRelativeCoordScale,
      absolute: 250 -- looks like 0.5 um lambda
		  -- this may be overwritten later
      ];
    END; -- of SetCoordScale


  NullLpAux: PROCEDURE[lp: listPtr] =
    BEGIN
    WHILE lp#NIL DO
      NullObAux[lp.ob];
      lp ← lp.nxt;
      ENDLOOP;
    END;

  NullObAux: PROCEDURE[ob: obPtr] =
    BEGIN
    ob.auxPnt ← NIL;
    WITH o: ob SELECT FROM
        cell => NullLpAux[o.ptr];
        ENDCASE => NULL;
    END;

  -- Module START code

  implantBeyondThinOx, wellBeyondDiffusion, cutPolyGap,
    nPlusToPPlus, nWellToNPlus, pWellToPPlus,
    viaCutGap: Coord;

  NullLpAux[masterList];
  SetCoordScale[];
  implantBeyondThinOx ← ScaleFromChipmonk[(3*Lambda)/2];
  wellBeyondDiffusion ← ScaleFromChipmonk[3*Lambda];
  cutPolyGap ← ScaleFromChipmonk[2*Lambda];
  viaCutGap ← ScaleFromChipmonk[3*Lambda];
  nPlusToPPlus ← ScaleFromChipmonk[3*Lambda];
  nWellToNPlus ← ScaleFromChipmonk[3*Lambda];
  pWellToPPlus ← ScaleFromChipmonk[3*Lambda];

  END. -- of ChipExpandImpl