-- ChipExternalWiring.mesa

-- A module to augment the geometric wiring with external wiring from
-- a file, and to check the consistency of the two.

-- Last Edited by: McCreight, October 25, 1984  1:45 PM
-- written by McCreight, 11 Sept 83

DIRECTORY
  Ascii,
  ChipNetDefs,
  ChipUserInt,
  CWF,
  ppdefs,
  StreamDefs;

ChipExternalWiring: PROGRAM
  IMPORTS ChipNetDefs, ChipUserInt, CWF, ppdefs, StreamDefs
  EXPORTS ChipNetDefs =
  BEGIN OPEN ppdefs, ChipNetDefs;

  ExternalNodePtr: TYPE = LONG POINTER TO ExternalNode ← NIL;
  ExternalNode: TYPE = RECORD [
    next: ExternalNodePtr ← NIL,
    name: Atom,
    net: netPtr ← NIL];

  TerminalSpecPtr: TYPE = LONG POINTER TO TerminalSpec ← NIL;
  TerminalSpec: TYPE = RECORD [
    next: TerminalSpecPtr ← NIL,
    levelSpec: Atom];

  AddExternalWiring: PUBLIC PROC [fileName: STRING, root: CellCallPtr] =
    BEGIN
    wirIn, wirOut: StreamDefs.DiskHandle ← NIL;
    s: STRING ← [200]; -- for error messages
    node: ExternalNodePtr ← NIL;
    nodeName: Atom ← NIL;
    token: Atom ← NIL;
    opens, shorts: NAT ← 0;

    Connect: PROC [cell: CellCallPtr, spec: TerminalSpecPtr] =
      BEGIN
      SELECT TRUE FROM
        spec=NIL => NULL;
        spec.next=NIL => -- ...z
          MatchTerminals[spec.levelSpec, cell.clusters];
        spec.levelSpec = asterisk =>
          BEGIN -- ...*.x.y.z
          FOR son: InstancePtr ← cell.offspring, son.sibling
            WHILE son#NIL DO
            WITH cs: son SELECT FROM
              cell =>
                BEGIN
                Connect[@cs, spec]; -- son = ...*.x.y.z ?
                Connect[@cs, spec.next]; -- son = ...x.y.z ?
                END;
              ENDCASE => NULL;
            ENDLOOP;
          END;
        ENDCASE => -- ...x.y.z
          BEGIN
          FOR son: InstancePtr ← cell.offspring, son.sibling
            WHILE son#NIL DO
            WITH cs: son SELECT FROM
              cell =>
                IF spec.levelSpec=
                  FindPropValue[ItemRefToLp[cs.caller], instanceName] THEN
                  Connect[@cs, spec.next];
              ENDCASE => NULL;
            ENDLOOP;
          END;
      END; -- of Connect

    MatchTerminals: PROC [name: Atom, cl: ClusterPtr] =
      BEGIN
      WHILE cl#NIL DO
        IF cl.netName=name THEN
          FOR mc: MustConnectPtr ← @cl.first, mc.next WHILE mc#NIL DO
            SELECT TRUE FROM
              node.net=NIL =>
                BEGIN
                node.net ← mc.net ← RefCanonNet[mc.net];
                IF node.name#NIL THEN
                  GetNormalNetId[@node.net].name ← named[name: node.name];
                END;
              (mc.net ← CanonNet[mc.net])#node.net =>
                BEGIN
                AppendLongString[s, "Had to join terminal "];
                AppendTerminalName[s, mc.source];
                AppendLongString[s, " to node "];
                AppendAtom[s, node.name];
                opens ← opens+1;
                Failure[];
                mc.net ← node.net ← MergeNets[mc.net, node.net];
                END;
              ENDCASE => NULL;
            ENDLOOP; -- mc
        cl ← cl.next;
        ENDLOOP;
      END;  -- of MatchTerminals

    Failure: PROC =
      BEGIN
      IF wirOut=NIL THEN
        wirOut ← StreamDefs.NewByteStream[
          (fileName ← ChipUserInt.FixExtension[fileName, ".werr"]),
          StreamDefs.WriteAppend];
      FOR i: NAT IN [0..s.length) DO
        wirOut.put[wirOut, s[i]];
        ENDLOOP;
      wirOut.put[wirOut, Ascii.CR];
      s.length ← 0;
      END; -- of Failure

    s.length ← 0;

      BEGIN -- for EXITS
      wirIn ← StreamDefs.NewByteStream[
        (fileName ← ChipUserInt.FixExtension[fileName, ".wspec"]), StreamDefs.Read
        ! StreamDefs.FileNameError => GOTO BadFileName];

      FOR token ← ReadAtom[wirIn], ReadAtom[wirIn] WHILE token#endOfFile DO
        spec: TerminalSpecPtr ← NIL;
        IF token#colon THEN {nodeName ← token; token ← ReadAtom[wirIn]}
        ELSE nodeName ← NIL;
        IF token#colon THEN GOTO FormatError;
        node ← netZ.NEW[ExternalNode ← [
          next: node,
          name: nodeName]];
        token ← comma;
        WHILE token#semiColon AND token#endOfFile DO
          spec, tail: TerminalSpecPtr;
          IF token=comma THEN token ← ReadAtom[wirIn];
          spec ← tail ← netZ.NEW[TerminalSpec ← [levelSpec: token]];
          token ← ReadAtom[wirIn];
          WHILE token=period DO
             nextSpec: TerminalSpecPtr =
               netZ.NEW[TerminalSpec ← [levelSpec: ReadAtom[wirIn]]];
             tail.next ← nextSpec;
             tail ← nextSpec;
             token ← ReadAtom[wirIn];
             ENDLOOP;
          Connect[root, spec];
          WHILE spec#NIL DO
            t: TerminalSpecPtr ← spec;
            spec ← spec.next;
            netZ.FREE[@t];
            ENDLOOP;
          ENDLOOP; -- reading terminal specs for a node
        IF token#semiColon THEN GOTO FormatError;
        ENDLOOP; -- reading nodes

      -- Make sure each node is unique
      FOR n1: ExternalNodePtr ← node, n1.next WHILE n1#NIL DO
        FOR n2: ExternalNodePtr ← n1.next,
          n2.next WHILE n2#NIL DO
          IF (n1.net ← CanonNet[n1.net])=
            (n2.net ← CanonNet[n2.net]) THEN
            BEGIN
            AppendLongString[s, "Shorted nodes: "];
            AppendAtom[s, n1.name];
            AppendLongString[s, " and "];
            AppendAtom[s, n2.name];
            shorts ← shorts+1;
            Failure[];
            EXIT;
            END;
          ENDLOOP;
        ENDLOOP;

      EXITS
        BadFileName =>
          BEGIN
          CWF.SWF1[s, "File %s doesn't exist.", fileName];
          ChipUserInt.Explain[s, "(punting wiring spec)"];
          END;
        FormatError =>
          BEGIN
          pos: LONG CARDINAL ← StreamDefs.GetPosition[wirIn];
          CWF.SWF1[s, "Wiring spec error just before character %ld.",
             @pos];
          ChipUserInt.Explain[s, "(punting wiring spec)"];
          END;
      END;

    wirIn.destroy[wirIn];
    WHILE node#NIL DO
      t: ExternalNodePtr ← node;
      node ← node.next;
      netZ.FREE[@t];
      ENDLOOP;
    IF wirOut#NIL THEN
      BEGIN
      wirOut.destroy[wirOut];
      wirOut ← NIL;
      CWF.SWF2[s, "There are %d shorts and %d opens in the geometry according",
        @shorts, @opens];
      ChipUserInt.Explain[s, "to the wiring specification. See", fileName];
      END;
    END; -- of AddExternalWiring


  ReadAtom: PROC [file: StreamDefs.DiskHandle] RETURNS [atom: Atom] =
    BEGIN
    s: STRING ← [100];
    c: CHARACTER ← ' ;
    s.length ← 0;
    DO
      ENABLE StreamDefs.StreamError => GOTO NoMore;
      SELECT c FROM
        Ascii.NUL, Ascii.LF => NULL;
        ':, '., ',, '; =>
          IF s.length>0 THEN
            {StreamDefs.SetPosition[file, StreamDefs.GetPosition[file]-1]; EXIT}
          ELSE RETURN[SELECT c FROM
            ': => colon,
            '; => semiColon,
            '. => period,
            ', => comma,
            ENDCASE => NIL];
        ' , Ascii.TAB, Ascii.CR  => IF s.length>0 THEN EXIT;
        Ascii.ControlZ => -- get rid of Bravo formatting
          FOR c ← file.get[file], file.get[file] WHILE c#Ascii.CR DO
            ENDLOOP;
        '- => -- could be a comment
          BEGIN
          minusCount: [0..2] ← 0;
          s[s.length] ← '-;
          s.length ← s.length+1;
          c ← file.get[file];
          IF c#'- THEN LOOP;
          -- It is a comment. Wait it out.
          s.length ← s.length-1;
          c ← file.get[file];
          FOR c ← file.get[file], file.get[file]
            WHILE c#Ascii.CR AND minusCount<2 DO
            minusCount ← IF c='- THEN minusCount+1 ELSE 0;
            ENDLOOP;
          END;
        ENDCASE =>
          BEGIN
          s[s.length] ← c;
          s.length ← s.length+1;
          END;
      c ← file.get[file];
      REPEAT
        NoMore => IF s.length=0 THEN RETURN[endOfFile];
      ENDLOOP;
    RETURN[MakeAtom[s]];
    END;

  comma: Atom = MakeAtom[","];
  semiColon: Atom = MakeAtom[";"];
  colon: Atom = MakeAtom[":"];
  period: Atom = MakeAtom["."];
  endOfFile: Atom = MakeAtom["!!! End Of File !!!"];
  asterisk: Atom = MakeAtom["*"];

  END.