-- ChipNetImpl.mesa

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

-- last modified by E. McCreight, December 21, 1982  4:58 PM
-- written by E. McCreight, November 5, 1981  10:24 AM

DIRECTORY
  ChipDRC,
  ChipNetDefs,
  ChipUserInt,
  InlineDefs,
  ppdefs;

ChipNetImpl: PROGRAM
  IMPORTS ChipDRC, ChipNetDefs, ChipUserInt
  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.id, cn2.id];
      IF cn1.refs<cn2.refs THEN -- reverse cn1 and cn2
        BEGIN
        cnTemp: CanonNetPtr ← cn1;
        cn1 ← cn2;
        cn2 ← cnTemp;
        END;
      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[id];
      END;
    RETURN[cn1];
    END; -- of MergeNets


  JoinNetIds: PROCEDURE[n1, n2: NetIdPtr]
    RETURNS[n: NetIdPtr] =
    BEGIN
    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;
              nid2.attachedTo=NIL =>
                BEGIN
                n ← @nid1;
                n2.details ← free[nextFree: freeIds];
                freeIds ← LOOPHOLE[n2];
                END;
              ENDCASE =>
                BEGIN
                aid1: NormalNetIdPtr;
                IF (aid1 ← GetNormalNetId[@nid1.attachedTo])#
                  GetNormalNetId[@nid2.attachedTo]
                  THEN
                  BEGIN
                  aid1.violations ← uz.NEW[ConditionalViolation ← [
                    next: aid1.violations,
                    otherNet:
                      (nid2.attachedTo ← CanonNet[nid2.attachedTo]),
                    v: [
                      place: RefCoordPt[nid1.final.r],
                      type: differentNetsToWell,
                      lev1: nid1.final.lev
                      ]]];
                    END;
                n ← @nid1;
                n2.details ← free[nextFree: freeIds];
                freeIds ← LOOPHOLE[n2];
                END;
        ENDCASE;
      normal =>
        WITH nid2: n2 SELECT FROM
          normal =>
            n ← JoinNormalNetIds[@nid1, @nid2];
          ENDCASE;
      ENDCASE;
    END; -- of JoinNetIds


  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;
          qualified => n ← n2;
          ENDCASE;
      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;
       ENDCASE;
    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: ConditionalViolationPtr ← 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 JoinNetIds


  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;

  END. -- of ChipNetImpl