-- Wiring.mesa -- last modified by C. Jacobi, January 26, 1983 9:51 am DIRECTORY DoradoBoard, Environment USING [bytesPerWord], FileIO USING [Open, OpenFailed], IO USING [BreakProc, Close, EndOf, GetChar, GetInt, GetRope, GetToken, SkipOver, STREAM, UnsafeGetBlock, WhiteSpace, PutFR, int,rope,PutRope,CreateViewerStreams], MessageWindow USING [Append], Rope USING [Concat, Equal, ROPE]; Wiring: CEDAR PROGRAM IMPORTS FileIO, IO, MessageWindow, Rope, DoradoBoard EXPORTS DoradoBoard = BEGIN OPEN DoradoBoard; ------ global module values ------ crCount: INTEGER _ 0; -- klugey way to count double CR's CR: CHAR = '\n; SP: CHAR = ' ; ReadASIFile: PROC [state: Board, boardName: Rope.ROPE] = -- Initialize 'pads' array with information from .asi file. TRUSTED BEGIN count: PadIndex; fileName: Rope.ROPE = Rope.Concat[boardName, ".asi"]; state.padCount _ 0; state.asiFile _ FileIO.Open[fileName, read ! FileIO.OpenFailed => {state.asiFile _ NIL; CONTINUE}]; IF state.asiFile = NIL THEN {FileError[state,fileName, NeedFile]; RETURN}; IF GetCardinal[state] # ASISeal THEN {FileError[state, fileName, ASISealError]; RETURN}; count _ GetCardinal[state]; [] _ GetCardinal[state]; FOR i: CARDINAL IN [0 .. count) DO state.pads[i].p _ GetPoint[state]; state.pads[i].con _ GetCardinal[state]; state.pads[i].type _ ASItoFluke[ state.pads[i].p.x, state.pads[i].p.y]; IF state.pads[i].con >= canonWithoutSegs THEN --if the pad is canonical, give it a SegTab BEGIN state.pads[i].segs _ NEW[SegTab]; IF state.pads[i].con = canonWithSegs THEN FOR layer: WiringLayer IN WiringLayer DO state.pads[i].segs.l[layer] _ GetInt[state]; --get the filepos's for the segments on each layer ENDLOOP; state.pads[i].con _ i; --and point the pad to itself END ELSE BEGIN can: PadIndex _ state.pads[i].con; --canonical pad for the current pad state.pads[i].segs _ NIL; --no SegTab for this pad state.pads[i].con _ state.pads[can].con; --put this pad on the list for its canonical pad state.pads[can].con _ i; state.pads[can].segs.padCount _ state.pads[can].segs.padCount + 1; END ENDLOOP; state.padCount _ count; END; -- of ReadASIFile -- GetCardinal: PUBLIC PROC [state: Board] RETURNS [v: CARDINAL] = TRUSTED BEGIN IF state.asiFile.UnsafeGetBlock[[base: @v, startIndex: 0, stopIndexPlusOne: SIZE[CARDINAL]*Environment.bytesPerWord]] # SIZE[CARDINAL]*Environment.bytesPerWord THEN ERROR; END; -- of GetCardinal -- GetInt: PUBLIC PROC [state: Board] RETURNS [v: INT] = TRUSTED BEGIN IF state.asiFile.UnsafeGetBlock[[base: @v, startIndex: 0, stopIndexPlusOne: SIZE[INT]*Environment.bytesPerWord]] # SIZE[INT]*Environment.bytesPerWord THEN ERROR; END; -- of GetInt -- GetPoint: PUBLIC PROC [state: Board] RETURNS [v: Point] = TRUSTED BEGIN IF state.asiFile.UnsafeGetBlock[[base: @v, startIndex: 0, stopIndexPlusOne: SIZE[Point]*Environment.bytesPerWord]] # SIZE[Point]*Environment.bytesPerWord THEN ERROR; END; -- of GetPoint -- ReadBoardData: PUBLIC PROC [state: Board, boardName: Rope.ROPE] = -- The purpose of this procedure is to read the portion of the ASI file that describes --the pads, and to associate net names (from the associated .wl file) with each of the --nets in the ASI file. Because ASI was allowed to reposition terminators in the course --of laying out the board, this is nontrivial. First, the ASI file is read, then the .wl file --is read one signal at a time. For each pad in a net in the .wl file, the corresponding --canonical pad in the ASI file is found, and gets one vote. When all the pads in --a net have been done, the canonical pad with the most votes gets the name from the .wl file --if there is confusion, a message is output. TRUSTED BEGIN ch: CHAR; fileHandle: IO.STREAM; votes, mismatches: CARDINAL _ 0; fileName: Rope.ROPE = Rope.Concat[boardName, ".wl"]; ReadASIFile[state, boardName]; --read the ASI file fileHandle _ FileIO.Open[fileName, read ! FileIO.OpenFailed => {fileHandle _ NIL; CONTINUE}]; IF fileHandle = NIL THEN {FileError[state, fileName, NeedFile]; RETURN}; IO.SkipOver[fileHandle, AtBreak]; -- skip over to @ IF IO.EndOf[fileHandle] THEN {FileError[state, fileName, EndOfFile]; RETURN}; [] _ IO.GetChar[fileHandle]; -- get @ IO.SkipOver[fileHandle, IO.WhiteSpace]; -- skipping over first double CR after @ IO.SkipOver[fileHandle, EatDoubleCR]; -- skipping over 'CALIBRATE' IO.SkipOver[fileHandle, IO.WhiteSpace]; UNTIL IO.EndOf[fileHandle] DO -- go through this loop once for each signal in the .wl file PollRec: TYPE = RECORD [ index, votes: CARDINAL ]; signal: Rope.ROPE; candidates, bestCandidate, highestVote: CARDINAL _ 0; maxCandidates: CARDINAL = 20; poll: ARRAY [0 .. maxCandidates) OF PollRec; IO.SkipOver[fileHandle, IO.WhiteSpace]; signal _ NextSignal[fileHandle]; --this is the signal name IO.SkipOver[fileHandle, CrBreak]; UNTIL IO.EndOf[fileHandle] DO -- go through this loop once per node asiIndex: CARDINAL; wl: Point; found: BOOL; [] _ IO.GetRope[fileHandle, IO.WhiteSpace]; [] _ IO.GetToken[fileHandle]; -- get { wl.x _ IO.GetInt[fileHandle]; IO.SkipOver[fileHandle, CommaBreak]; wl.y _ IO.GetInt[fileHandle]; [] _ IO.GetToken[fileHandle]; -- get } [found, asiIndex] _ Search[state, [x: 4200+25*wl.x, y: 14410-25*wl.y] -- .wl point transformed to .asi space -- ]; --Search returns the index of the canonical pad for the pad with the given coordinates IF found THEN FOR i: CARDINAL IN [0 .. candidates) DO IF poll[i].index = asiIndex THEN {poll[i].votes _ poll[i].votes + 1; EXIT}; REPEAT FINISHED => IF candidates < maxCandidates THEN BEGIN poll[candidates] _ [index: asiIndex, votes: 1]; candidates _ candidates + 1; END; ENDLOOP ELSE mismatches _ mismatches+1; IF NOT IO.EndOf[fileHandle] THEN ch _ IO.GetChar[fileHandle]; IF ch = CR THEN IF NOT IO.EndOf[fileHandle] THEN ch _ IO.GetChar[fileHandle]; IF ch = CR THEN EXIT ELSE IO.SkipOver[fileHandle, IO.WhiteSpace]; ENDLOOP; -- determine which pad should be given the signal name IF candidates>0 THEN BEGIN confusion: BOOL_FALSE; FOR i: CARDINAL IN [0 .. candidates) DO { IF poll[i].votes = highestVote THEN confusion _ TRUE; IF poll[i].votes > highestVote THEN { [index: bestCandidate, votes: highestVote] _ poll[i]; confusion _FALSE}; } ENDLOOP; IF confusion THEN BEGIN PutError[state,IO.PutFR[format: "*nConfusion about name %g for index %g ", v1: IO.rope[signal], v2: IO.int[bestCandidate]]]; FOR i: CARDINAL IN [0..candidates) DO PutError[state, IO.PutFR[format: "*n*tindex: %g, votes: %g, x: %g, y: %g, count: %g", v1: IO.int[poll[i].index], v2: IO.int[poll[i].votes], v3: IO.int[state.pads[poll[i].index].p.x], v4: IO.int[state.pads[poll[i].index].p.y], v5: IO.int[state.pads[poll[i].index].segs.padCount]]] ENDLOOP; END; IF state.pads[bestCandidate].segs = NIL THEN state.pads[bestCandidate].segs _ NEW[SegTab _ []]; state.pads[bestCandidate].segs.sigName _ signal; END; ENDLOOP; -- get next signal IF mismatches>0 THEN PutError[state, IO.PutFR[format: "*n*nWire list and ASI file mismatched at %g pads.",v1: IO.int[mismatches]]]; IO.Close[fileHandle]; END; -- of ReadFile -- PutError: PROC[state: Board,r: Rope.ROPE] = BEGIN IF state.errorOutStream = NIL THEN [state.errorInStream, state.errorOutStream] _ IO.CreateViewerStreams["DisplayBoardErrors"]; IO.PutRope[state.errorOutStream,r]; END; Search: PROC [state: Board, target: Point] RETURNS [found: BOOLEAN, asiIndex: CARDINAL] = -- Binary search procedure. BEGIN lower: INTEGER _ 0; upper: INTEGER _ state.padCount-1; WHILE lower <= upper DO i: INTEGER _ (lower+upper)/2; SELECT state.pads[i].p.x FROM lower _ i+1; >target.x => upper _ i-1; ENDCASE => SELECT state.pads[i].p.y FROM lower _ i+1; >target.y => upper _ i-1; ENDCASE => RETURN[TRUE, GetCanonicalPad[state,i]]; ENDLOOP; RETURN[FALSE, 0]; END; -- of Search -- FileError: PUBLIC PROC [state: Board, file: Rope.ROPE, error: ErrorType] = TRUSTED BEGIN PutError[state,"*n-- ERROR in file: "]; PutError[state,file]; PutError[state, SELECT error FROM ASISealError => " Unknown ASISeal", EndOfFile =>" (Internal error) Attempt to read beyond end of file", NeedFile =>" File needed on disk", IllegalFile =>" Illegal file name", InvalidSignal =>" Attempt to read invalid signal (or character)", ENDCASE => " (Internal error) Undetermined! "]; END; -- of FileError -- RemoveSignal: PUBLIC PROC [state: Board, signal: Rope.ROPE] RETURNS [BOOLEAN _ TRUE] = -- Remove a signal from the signalPads list. TRUSTED BEGIN IF state.signalPads = NIL THEN RETURN[FALSE]; IF Rope.Equal[state.pads[state.signalPads.first].segs.sigName, signal, FALSE] THEN BEGIN state.signalPads _ state.signalPads.rest; RETURN[TRUE]; END; FOR s: SigPadList _ state.signalPads, s.rest WHILE s.rest # NIL DO IF Rope.Equal[state.pads[s.rest.first].segs.sigName, signal, FALSE] THEN BEGIN s.rest _ s.rest.rest; RETURN[TRUE]; END; ENDLOOP; RETURN[FALSE]; END; -- of RemoveSignal -- AddSignal: PUBLIC PROC [state: Board, signal: Rope.ROPE] RETURNS [BOOLEAN _ TRUE] = -- Add given signal (or the hit pad) to the signalPads list. TRUSTED BEGIN IF signal=NIL OR Rope.Equal[signal, "? ? ?"] THEN --use the hit pad if there is no valid signal name BEGIN IF state.hitPadIndex >=state.padCount THEN RETURN[FALSE]; state.signalPads _ CONS[GetCanonicalPad[state,state.hitPadIndex], state.signalPads]; RETURN[TRUE]; END ELSE --search for the name and put its pad on the signalPads list FOR count: CARDINAL IN [0 ..state.padCount) DO IF state.pads[count].segs # NIL AND Rope.Equal[state.pads[count].segs.sigName, signal, FALSE] THEN BEGIN state.signalPads _ CONS[count, state.signalPads]; RETURN[TRUE]; END; ENDLOOP; RETURN[FALSE]; END; -- of AddSignal -- FindClosestPad: PUBLIC PROC [state: Board, p: Point] RETURNS [pad: PadIndex, sigName: Rope.ROPE] = TRUSTED BEGIN canon: PadIndex; x,y: INTEGER; sc,pin: INTEGER _ 0; pt: PadType; ty: Rope.ROPE; startIndex: PadIndex = MinPadGE[state: state, x: p.x]; bestDist: INT _ LAST[INT]; IF state.padCount <= 0 THEN RETURN[0, NIL]; pad _ 0; -- any legal pad FOR i: PadIndex IN [startIndex..state.padCount) WHILE IntSquare[p.x-state.pads[i].p.x] -- best distance possible at this x -- < bestDist DO IF Distance[state.pads[i].p, p] < bestDist THEN BEGIN pad _ i; bestDist _ Distance[state.pads[i].p, p]; END; ENDLOOP; FOR i: PadIndex DECREASING IN [0..startIndex) WHILE IntSquare[p.x-state.pads[i].p.x] -- best distance possible at this x -- < bestDist DO IF Distance[state.pads[i].p, p] < bestDist THEN BEGIN pad _ i; bestDist _ Distance[state.pads[i].p, p]; END; ENDLOOP; x _ state.pads[pad].p.x; y_state.pads[pad].p.y; pt _ state.pads[pad].type; IF pt.t # noProbe THEN {sc_pt.n/128 + 1; pin _ pt.n MOD 128 + 1}; --format channel no. as ScanCard - Pin ty _ SELECT pt.t FROM noProbe => "noProbe", signal => "Signal", gnd => "Gnd", vdd => "Vdd", vee => "Vee", vtt => "Vtt", vcc => "Vcc", ENDCASE => "??"; MessageWindow.Append [ message: IO.PutFR[format: "Pad has ASI coordinates [x: %g, y: %g] (Fluke %g-%g {%g})", v1: IO.int[x], v2: IO.int[y], v3: IO.int[sc], v4: IO.int[pin], v5: IO.rope[ty]], clearFirst: TRUE ]; canon _ GetCanonicalPad[state,pad]; sigName _ IF state.pads[canon].segs.sigName # NIL THEN state.pads[canon].segs.sigName ELSE "? ? ?"; END; -- FindClosestPad -- GetCanonicalPad: PUBLIC PROC [state: Board, index: PadIndex] RETURNS [pindex: PadIndex] = BEGIN WHILE state.pads[index].segs = NIL DO index _ state.pads[index].con ENDLOOP; RETURN[index]; END; MinPadGE: PUBLIC PROC [state: Board, x: Mils] RETURNS [minIndex: PadIndex] = -- Returns smallest index minIndex such that x<=state.pads[minIndex].p.x. BEGIN upper: INTEGER _ state.padCount-1; lower: INTEGER _ 0; WHILE lower <= upper DO i: INTEGER _ (lower+upper)/2; SELECT state.pads[i].p.x FROM lower _ i+1; ENDCASE => upper _ i-1; ENDLOOP; RETURN[lower]; END; -- of MinPadGE -- IntSquare: PROC [x: INT] RETURNS [INT] = INLINE {RETURN[x*x]}; Distance: PROC [first, second: Point] RETURNS [distance: INT] = INLINE {RETURN[IntSquare[second.x - first.x] + IntSquare[second.y - first.y]]}; NextSignal: PROC [fileHandle: IO.STREAM] RETURNS [signal: Rope.ROPE] = TRUSTED BEGIN IF NOT IO.EndOf[fileHandle] THEN signal _ IO.GetRope[fileHandle, ColonBreak, NIL] ELSE signal _ Rope.Concat[signal, "****"]; END; -- of NextSignal -- AtBreak: IO.BreakProc = TRUSTED BEGIN IF c = '@ THEN RETURN[KeepGoing] ELSE RETURN[StopAndPutBackChar]; END; -- of AtBreak -- ColonBreak: IO.BreakProc = TRUSTED BEGIN IF c = ': THEN RETURN[StopAndTossChar] ELSE RETURN[KeepGoing]; END; -- of ColonBreak -- CommaBreak: IO.BreakProc = TRUSTED BEGIN IF c = ', THEN RETURN[StopAndTossChar] ELSE RETURN[KeepGoing]; END; -- of CommaBreak -- CrBreak: IO.BreakProc = TRUSTED BEGIN IF c = CR THEN RETURN[KeepGoing] ELSE RETURN[StopAndTossChar]; END; -- of CrBreak -- EatDoubleCR: IO.BreakProc = TRUSTED BEGIN IF c = CR THEN BEGIN crCount _ crCount + 1; IF crCount = 2 THEN BEGIN crCount _ 0; RETURN[KeepGoing]; END ELSE RETURN[StopAndTossChar]; END ELSE {crCount _ 0; RETURN[StopAndTossChar]}; END; -- of EatDoubleCR -- END. -- of Wiring -- CHANGE LOG Created by L. Hilton, September 9, 1982 7:05 pm