-- ChipNetImpl.mesa

-- A package of routines that maintains net interconnect
-- structures.

-- last modified by E. McCreight, October 1, 1984  3:35 PM
-- written by E. McCreight, November 5, 1981  10:24 AM

DIRECTORY
  ChipDRC,
  ChipExpand,
  ChipNetDefs,
  ChipUserInt,
  CWF,
  InlineDefs,
  ppdefs;

ChipNetImpl: PROGRAM
  IMPORTS ChipDRC, ChipExpand, ChipNetDefs, ChipUserInt, CWF
  EXPORTS ChipNetDefs =
  BEGIN OPEN ppdefs, ChipUserInt, ChipNetDefs;


  freeNets: LONG POINTER TO link Net ← NIL;
  freeIds: FreeNetIdPtr ← NIL;

  allNets: PUBLIC NetIdPtr ← NIL;

  NewNet: PUBLIC PROCEDURE[initRefs: CARDINAL ← 1]
    RETURNS[CanonNetPtr] =
    BEGIN
    cn: CanonNetPtr;
    IF freeNets=NIL THEN
      cn ← LOOPHOLE[netZ.NEW[net ←
        [refs: initRefs, rest: canon[id: NewNetId[]]]]]
    ELSE
      BEGIN
      cn ← LOOPHOLE[freeNets];
      freeNets ← LOOPHOLE[freeNets.nxt];
      cn↑ ← [refs: initRefs, rest: canon[id: NewNetId[]]];
      END;
    RETURN[cn];
    END;


  NewNetId: PROCEDURE[]
    RETURNS[id: NetIdPtr] =
    BEGIN
    call: CellCallPtr ← NIL;
    IF freeIds=NIL THEN
      BEGIN
      id ← netIdZ.NEW[NetId ← [next: allNets, details: normal[
        name: anonymous[]]]];
      allNets ← id;
      END
    ELSE
      BEGIN
      id ← freeIds; freeIds ← freeIds.nextFree;
      id↑ ← [next: id.next, details: normal[
        name: anonymous[]]];
      END
    END;


  RefCanonNet: PUBLIC PROCEDURE[n: netPtr,
    newRefs: INTEGER ← 1] RETURNS[CanonNetPtr] =
    BEGIN
    nInt: netIntPtr ← n;
    canonNet: CanonNetPtr;
    skipWeight: CARDINAL;
    IF nInt=NIL THEN RETURN[NewNet[newRefs+1]];
    DO
      WITH dn: nInt SELECT FROM
        link => nInt ← dn.nxt;
        canon => {canonNet ← @dn; EXIT};
        ENDCASE;
      ENDLOOP;
    nInt ← n;
    skipWeight ← 1;
    DO
      np: netIntPtr ← nInt;
      WITH dn: np SELECT FROM
        link =>
          BEGIN
          newSkipWeight: CARDINAL ← dn.refs;
          nInt ← dn.nxt;
          IF (dn.refs ← dn.refs-skipWeight)=0 THEN
            {dn.nxt ← freeNets; freeNets ← @dn}
          ELSE dn.nxt ← canonNet;
            -- the original incoming reference will now skip over
            -- this link.
          skipWeight ← newSkipWeight;
          END;
        canon =>
          BEGIN
          IF (dn.refs ← dn.refs+newRefs)=0 THEN
            BEGIN
            np.rest ← link[nxt: freeNets];
            freeNets ← LOOPHOLE[np];
            canonNet ← NIL;
            END;
          RETURN[canonNet];
          END;
        ENDCASE;
      ENDLOOP;
    END; -- of RefCanonNet


  GetNormalNetId: PUBLIC PROCEDURE[n: LONG POINTER TO netPtr]
    RETURNS[NormalNetIdPtr] =
    BEGIN
    cn: CanonNetPtr;
    IF n=NIL THEN GOTO Malformed;
    cn ← n↑ ← CanonNet[n↑];
    WITH nid: cn.id SELECT FROM
      normal => RETURN[@nid];
      ENDCASE => GOTO Malformed;
    EXITS
      Malformed =>
        BEGIN
        Explain["GetNormalNetId called with improper arguments"];
        RETURN[NIL]
        END;
    END;  -- of GetNormalNetId


  MergeNets: PUBLIC PROCEDURE[n1, n2: netPtr]
    RETURNS[CanonNetPtr] =
    BEGIN
    cn1: CanonNetPtr ← CanonNet[n1];
    cn2: CanonNetPtr ← CanonNet[n2];
    IF cn1#cn2 THEN
      BEGIN -- merging two canonical nets
      nInt2: netIntPtr;
      id: NetIdPtr ← JoinNetIds[cn1, cn2];
      IF cn1.refs<cn2.refs THEN -- reverse cn1 and cn2
        {cnTemp: CanonNetPtr ← cn1; cn1 ← cn2; cn2 ← cnTemp};
      cn1.refs ← cn1.refs+cn2.refs;
      cn1.id ← id;
      nInt2 ← cn2; -- decommit cn2's variant
      nInt2.rest ← link[nxt: cn1];
      cn2 ← CanonNet[nInt2];
        -- adjusts reference count of nInt2
      IF id.violations#NIL THEN
        ChipDRC.PurgeDRCViolations[cn1];
      END;
    RETURN[cn1];
    END; -- of MergeNets


  JoinNetIds: PROCEDURE[cn1, cn2: CanonNetPtr] RETURNS[n: NetIdPtr] =
    BEGIN
    n1: NetIdPtr = cn1.id;
    n2: NetIdPtr = cn2.id;
    IF n1=n2 THEN RETURN[n1];
    WITH nid1: n1 SELECT FROM
      well =>
        WITH nid2: n2 SELECT FROM
          well =>
            SELECT TRUE FROM
              nid1.attachedTo=NIL =>
                BEGIN
                n ← @nid2;
                n1.details ← free[nextFree: freeIds];
                freeIds ← LOOPHOLE[n1];
                END;
              ENDCASE =>
                BEGIN
                IF nid2.attachedTo#NIL THEN
                  BEGIN
                  cn: CanonNetPtr ← CanonNet[nid2.attachedTo];
                  AttachNetToWell[normal: cn, well: cn1];
                  cn ← DeRefNet[cn];
                  END;
                n ← @nid1;
                n2.details ← free[nextFree: freeIds];
                freeIds ← LOOPHOLE[n2];
                END;
          ENDCASE => ERROR;
      normal =>
        WITH nid2: n2 SELECT FROM
          normal => n ← JoinNormalNetIds[@nid1, @nid2];
          ENDCASE => ERROR;
      ENDCASE => ERROR;
    END; -- of JoinNetIds


  AttachNetToWell: PROC [normal: CanonNetPtr, well: CanonNetPtr] =
    BEGIN
    WITH w: well.id SELECT FROM
      well =>
        SELECT w.attachedTo FROM
          NIL => w.attachedTo ← RefCanonNet[normal];
          ENDCASE => -- not NIL
            SELECT (w.attachedTo ← CanonNet[w.attachedTo]) FROM
              normal => NULL; -- attaching to same net twice is NOP
              ENDCASE =>
                WITH n: normal.id SELECT FROM
                  normal =>
                    n.violations ← uz.NEW[ViolationList ← [
                      next: n.violations,
                      v: [
                        place: RefCoordPt[w.final.r],
                        info: differentNetsToWell[lev: w.final.lev,
                          wellNode: RefCanonNet[well],
                          n: RefCanonNet[normal]]]]];
                  ENDCASE => ERROR; -- normal doesn't point to normal NetId
      ENDCASE => ERROR; -- well doesn't point to well NetId
    END;


  JoinNormalNetIds: PROCEDURE[n1, n2: NormalNetIdPtr]
    RETURNS[n: NormalNetIdPtr] =
    BEGIN
    id: NetIdPtr;
    caps: ARRAY Conductors OF LayerCap;
    UpdateAreas[n1];
    UpdateAreas[n2];
    FOR cond: Conductors IN Conductors DO
      caps[cond].cutSides ← n1.caps[cond].cutSides+
        n2.caps[cond].cutSides;
      caps[cond].cutWidth ← n1.caps[cond].cutWidth+
        n2.caps[cond].cutWidth;
      caps[cond].perimeter ← n1.caps[cond].perimeter+
        n2.caps[cond].perimeter;
      caps[cond].area ← n1.caps[cond].area+
        n2.caps[cond].area;
      ENDLOOP;
    WITH dn1: n1 SELECT FROM
      anonymous => n ← n2;
      numeric =>
        WITH dn2: n2 SELECT FROM
          anonymous, numeric => n ← n1;
          ENDCASE => n ← n2;
      qualified =>
        WITH dn2: n2 SELECT FROM
          anonymous, numeric => n ← n1;
          qualified =>
            n ← IF CallDepth[n1.source]<=CallDepth[n2.source]
              THEN n1 ELSE n2; -- less qualified is better
          ENDCASE => n ← n2;
       ENDCASE => n ← n1;
    n.caps ← caps;
    n.final ←
      (IF n1.final.r.x2<n2.final.r.x2 THEN n2.final ELSE n1.final);

    n.hasPathToGnd ← n1.hasPathToGnd OR n2.hasPathToGnd;
    n.hasPathToVdd ← n1.hasPathToVdd OR n2.hasPathToVdd;
    n.hasDepletionPullup ← n1.hasDepletionPullup OR
      n2.hasDepletionPullup;
    n.couldBeLogo ← n1.couldBeLogo AND n2.couldBeLogo;

    IF n2.violations#NIL THEN
      BEGIN
      FOR v: ViolationListPtr ← n2.violations, v.next DO
        IF v.next=NIL THEN {v.next ← n1.violations; EXIT};
        ENDLOOP;
      n.violations ← n2.violations;
      END
    ELSE n.violations ← n1.violations;
    IF n=n1 THEN n1 ← n2;
    id ← n1;
    id.details ← free[nextFree: freeIds];
    freeIds ← LOOPHOLE[id];
    END; -- of JoinNormalNetIds


  UpdateAreas: PUBLIC PROCEDURE[n: NormalNetIdPtr] =
    BEGIN
    IF n.lastX<currentX THEN
     FOR cond: Conductors IN Conductors DO
      n.caps[cond].perimeter ← n.caps[cond].perimeter+
        n.caps[cond].cutSides*ScaleToChipmonk[currentX-n.lastX];
      n.caps[cond].area ← n.caps[cond].area+
        n.caps[cond].cutWidth*
          LONG[ScaleToChipmonk[currentX-n.lastX]];
      ENDLOOP;
    n.lastX ← currentX;
    END; -- of UpdateAreas


  CallDepth: PROCEDURE[c: InstancePtr]
    RETURNS[d: CARDINAL] =
    BEGIN
    d ← 0;
    FOR ancest: InstancePtr ← c, ancest.caller.head
      WHILE ancest#NIL DO
      d ← d+1;
      ENDLOOP;
    END;


  FeatureNet: PUBLIC PROC [f: FeaturePtr] RETURNS [CanonNetPtr] =
    BEGIN
    cn: CanonNetPtr;
    IF f.net=NIL THEN
      BEGIN
      cn ← f.net ← NewNet[1];
      cn.id.final ← [lev: f.lev, r: f.cover];
      SELECT f.lev FROM
        nwel, pwel => cn.id.details ← well[attachedTo: NIL];
        ENDCASE => cn.id.details ← 
          normal[source: ChipExpand.NearestCellInstance[f.caller.head],
            name: anonymous[]];
      END
    ELSE cn ← f.net ← CanonNet[f.net];
    cn.id.final ← [lev: f.lev, r: f.cover];
    RETURN[cn];
    END;


  MergeFeatureNets: PUBLIC PROC [f1, f2: FeaturePtr] =
    BEGIN
    -- Merge them unless they are of different types (e.g., node vs well).
    -- In that case, relate them.
    id: NetIdPtr;
    IF f1.lev=f2.lev THEN
      BEGIN
      cn: CanonNetPtr;
      SELECT f1.net FROM
        #NIL =>
          SELECT f2.net FROM
            #NIL => cn ← MergeNets[f1.net, f2.net];
            ENDCASE => cn ← RefCanonNet[f1.net];
        ENDCASE => cn ← RefCanonNet[FeatureNet[f2]];
      id ← (f1.net ← f2.net ← cn).id;
      END
    ELSE
      BEGIN
      cn1: CanonNetPtr ← FeatureNet[f1];
      cn2: CanonNetPtr ← FeatureNet[f2];
      IF cn1.id.type=cn2.id.type THEN
        id ← (f1.net ← f2.net ← MergeNets[f1.net, f2.net]).id
      ELSE
        BEGIN
        IF cn1.id.type#normal THEN -- exchange nets
          {t: CanonNetPtr ← cn1; cn1 ← cn2; cn2 ← t};
        IF NOT(cn1.id.type=normal AND cn2.id.type=well) THEN ERROR;
        AttachNetToWell[normal: cn1, well: cn2];
        RETURN;
        END;
      END;
    IF id.final.r.x2<f1.cover.x2 THEN
      id.final ← [lev: f1.lev, r: f1.cover];
    IF id.final.r.x2<f2.cover.x2 THEN
      id.final ← [lev: f2.lev, r: f2.cover];
    END; -- of MergeFeatureNets


  CountNetIds: PROCEDURE [examine: BOOLEAN ← FALSE] RETURNS
    [ids, frees, wells, anonymi, numerics, qualifieds, zeroAreas: LONG INTEGER ← 0] =
    BEGIN
    FOR n: NetIdPtr ← allNets, n.next WHILE n#NIL DO
      ids ← ids+1;
      WITH dn: n SELECT FROM
        free => frees ← frees+1;
        well => wells ← wells+1;
        normal =>
          BEGIN
          FOR c: Conductors IN Conductors DO
            IF dn.caps[c] # [0,0,0,0] THEN EXIT;
            REPEAT
              FINISHED =>
                IF dn.final.r.x2<currentX THEN
                  BEGIN
                  zeroAreas ← zeroAreas+1;
                  IF examine THEN
                    BEGIN
                    s: STRING ← [100];
                    CWF.SWF1[sto: s, s: "Zero-area node in %s here...", a: levelNames[dn.final.lev]];
                    RemarkAtPoint[RefCoordPt[dn.final.r], s];
                    END;
                  END;
            ENDLOOP;
          WITH ddn: dn SELECT FROM
            anonymous => anonymi ← anonymi+1;
            numeric => numerics ← numerics+1;
            qualified => qualifieds ← qualifieds+1;
            ENDCASE => ERROR;
          END;
        ENDCASE => ERROR;
      ENDLOOP;
    END;


  END. -- of ChipNetImpl