-- ChipDRCImpl2.mesa

-- last modified by E. McCreight, November 22, 1982  4:37 PM
-- written by E. McCreight, April 14, 1982  10:25 AM

DIRECTORY
  ChipDRC,
  ChipExpand,
  ChipNetDefs,
  ChipUserInt,
  CWF,
  ppdefs,
  ppdddefs,
  ppMainDefs,
  StreamDefs;

ChipDRCImpl2: PROGRAM
  IMPORTS ChipExpand, ChipNetDefs, ChipUserInt,
    CWF,
    ppdddefs, ppdefs, ppMainDefs,
    StreamDefs
  EXPORTS ChipDRC =
  BEGIN OPEN StreamDefs, CWF, ppdefs, ChipUserInt,
    ChipNetDefs;

  holdViolations: PUBLIC BOOLEAN ← FALSE;
  violations: ViolationListPtr ← NIL;

  NoteViolation: PUBLIC PROCEDURE[v: Violation] =
    BEGIN
    IF NOT holdViolations THEN
      TellUserAboutViolation[v: v, tell: RemarkAtPoint];
    violations ← uz.NEW[ViolationList ←
      [next: violations,
      v: v]];
    END; -- of NoteViolations


  EnumerateViolations: PUBLIC PROCEDURE[] =
    BEGIN
    IF violations#NIL THEN
      BEGIN
      IF HeSaysYes[IF holdViolations
        THEN "There are design errors. Want to see them?"L
        ELSE "Want to see the design errors again?"L]
        THEN DO
        FOR vl: ViolationListPtr ← violations, vl.next WHILE vl#NIL DO
          TellUserAboutViolation[v: vl.v, tell: RemarkAtPoint
            ! Punt => GOTO NoMore];
          REPEAT
            NoMore => NULL;
          ENDLOOP;
        IF NOT HeSaysYes[
          "Want to see the design errors again?"L] THEN EXIT;
        ENDLOOP;
      IF HeSaysYes["Write design errors to a file?"L] THEN
        BEGIN

        ViolationToShowFile: PROCEDURE[p: Point, s: STRING] =
          BEGIN OPEN ppdddefs;
          cifX: LONG INTEGER ←
            LONG[(ppdddefs.pCifScale/Lambda)]*p.x;
          cifY: LONG INTEGER ←
            -LONG[(ppdddefs.pCifScale/Lambda)]*p.y;
            -- I have no idea why Chipmonk wants this y
            -- co-ordinate inverted!
          FWF3[showFile, "%ld %ld   %s*n", @cifX, @cifY, s];
          END;

        showFile: DiskHandle;
        name: STRING ← RequestString["Error file name?"L];
        IF name=NIL OR name.length=0 THEN
          name ← newString[ppMainDefs.fileName];
        name ← FixExtension[name, ".drc"];
        showFile ← NewByteStream[name, WriteAppend];
        FOR vl: ViolationListPtr ← violations, vl.next WHILE vl#NIL DO
          TellUserAboutViolation[v: vl.v, tell: ViolationToShowFile];
          ENDLOOP;
        FreeString[name];
        TruncateDiskStream[showFile];
        END;
      END;
    END; -- of EnumerateViolations


  TellUserAboutViolation: PROCEDURE[v: Violation,
    tell: PROCEDURE[p: Point, s: STRING]] =
    BEGIN
    s: STRING ← [100];
    SELECT v.type FROM
      tooNarrow =>
        SWF1[s, "%s geometry too small here"L, levelNames[v.lev1]];
      tooClose =>
        IF v.lev1=v.lev2 THEN
          SWF1[s, "%s features are too close here."L,
          levelNames[v.lev1]]
        ELSE SWF2[s, "%s and %s features too close here."L,
          levelNames[v.lev1], levelNames[v.lev2]];
      noNetToWell =>
        SWF1[s, "%s is floating here."L,
          levelNames[v.lev1]];
      differentNetsToWell =>
        SWF1[s, "%s connects to two different nets."L,
          levelNames[v.lev1]];
      terminalsUnconnected =>
        BEGIN
        s2: STRING ← [100];
        s2.length ← 0;
        AppendTerminalName[s: s2, c: v.caller];
        SWF1[s, """%s"" terminals failed to connect."L, s2];
        END;
      coverageNeeded =>
        SWF1[s, "%s coverage needed here."L, levelNames[v.lev1]];
      coverageForbidden =>
        SWF1[s, "%s coverage is forbidden here."L,
          levelNames[v.lev1]];
      bogusTransistor =>
        SWF1[s, "Illegal Chipmonk %s transistor here."L,
          levelNames[v.lev1]];
      cantCheck =>
        SWF0[s, "Bug in extractor prevented some checking here"L];
      ENDCASE =>
        SWF0[s, "Unknown design error"L];
    tell[p: v.place, s: s];
    END; -- of TellUserAboutViolation


  CheckTerminalConnections: PUBLIC PROCEDURE[
    call: CellCallPtr] =
    BEGIN
    ChipExpand.CheckClusters[call];
    FOR cluster: ClusterPtr ← call.clusters, cluster.next
      WHILE cluster#NIL DO
      IF cluster.first.next#NIL THEN
        BEGIN
        NoteViolation[[
          place: RefCoordPt[ItemInWorld[cluster.first.source]],
          caller: cluster.first.source,
          type: terminalsUnconnected]];
        FOR mc: MustConnectPtr ← cluster.first.next, mc.next
          WHILE mc#NIL DO 
          NoteViolation[[
            place: RefCoordPt[ItemInWorld[mc.source]],
            caller: mc.source,
            type: terminalsUnconnected]];
          ENDLOOP;
        END;
      ENDLOOP;

    FOR c: InstancePtr ← call.offspring, c.sibling
      WHILE c#NIL DO
      WITH dc: c SELECT FROM
        cell => CheckTerminalConnections[@dc];
        ENDCASE => NULL;
      ENDLOOP;
    END; -- of CheckTerminalConnections

  PurgeDRCViolations: PUBLIC PROCEDURE[id: NetIdPtr] =
    BEGIN
    remaining, ncv: ConditionalViolationPtr ← NIL;
    cv: ConditionalViolationPtr;

    Reconsider: PROCEDURE[certain, stillPossible: BOOLEAN] = INLINE
      BEGIN
      IF certain THEN NoteViolation[cv.v];
      IF certain OR NOT stillPossible THEN
        BEGIN -- remove from conditional violations
        IF cv.otherNet#NIL THEN
          cv.otherNet ← DeRefNet[cv.otherNet];
        uz.FREE[@cv];
        END
      ELSE {cv.next ← remaining; remaining ← cv};
      END;

    WITH did: id SELECT FROM
      normal =>
        BEGIN
        FOR cv ← id.violations, ncv WHILE cv#NIL DO
          ncv ← cv.next;
          SELECT cv.v.type FROM
            tooNarrow =>
              Reconsider[certain: NOT did.couldBeLogo,
               stillPossible: currentX<=id.final.r.x2];
            tooClose, coverageForbidden =>
              BEGIN
              otherId: NormalNetIdPtr =
                GetNormalNetId[@cv.otherNet];
              Reconsider[
                certain: id#otherId AND NOT
                  (did.couldBeLogo AND otherId.couldBeLogo)
                  AND (otherId.final.r.x2<currentX OR
                    id.final.r.x2<currentX),
                stillPossible: id#otherId AND
                  (currentX<=otherId.final.r.x2 OR
                  currentX<=id.final.r.x2)];
              END;
            ENDCASE => ERROR;
          ENDLOOP;
        id.violations ← remaining;
        END;
      well =>
        IF id.final.r.x2<currentX AND did.attachedTo=NIL THEN
          NoteViolation[[
            place: RefCoordPt[id.final.r],
            type: noNetToWell,
            lev1: id.final.lev]];
      ENDCASE => NULL;
    END; -- of PurgeDRCViolations

  END. -- of ChipDRCImpl2