-- FILE: DprintImpl.mesa -- Last edited by Ousterhout, January 24, 1984 1:59 pm DIRECTORY DPrint, FS, Globals, IO, Model, Parse, Printout, Rope, StagePool; DPrintImpl: CEDAR PROGRAM IMPORTS FS, Globals, IO, Model, Parse, Printout, Rope, StagePool EXPORTS DPrint = BEGIN OPEN Globals, DPrint; -- The following arrays and values are used to record delay paths. -- In the arrays, lower indices correspond to shorter delays. NumPaths: PUBLIC ARRAY listType OF INT ← [5,5,5]; Threshold: PUBLIC ARRAY listType OF REAL ← [0.0, 0.0, 0.0]; Paths: ARRAY listType OF ARRAY[0..MaxPaths) OF Stage; -- The following values are used to record statistical information. numRecords: ARRAY listType OF INT ← [0,0,0]; numDups: INT ← 0; PrintEdgeSpeeds: PUBLIC BOOLEAN ← FALSE; copyPath: PROC[src: Stage] RETURNS [copy: Stage] = -- This local procedure simply makes a copy of a path (a -- chain of stages) and returns a pointer to the copy. BEGIN prev, cur: Stage; copy ← NIL; WHILE src # NIL DO prev ← StagePool.New[]; prev↑ ← src↑; IF copy = NIL THEN copy ← prev ELSE cur.prev ← prev; cur ← prev; src ← src.prev; ENDLOOP; RETURN [copy]; END; deletePath: PROC[stage: Stage] = -- Returns to the stage pool all of the stages linked to stage. BEGIN next: Stage; WHILE stage # NIL DO next ← stage.prev; StagePool.Free[stage]; stage ← next; ENDLOOP; END; Record: PUBLIC PROC[stage: Stage, list: listType] = BEGIN i, max: INT; cur: Stage; bot, top: REAL; done: BOOLEAN ← FALSE; IF stage.time < Threshold[list] THEN RETURN; numRecords[list] ← numRecords[list] + 1; max ← NumPaths[list]; -- See if this path's delay duplicates a delay already in the array. bot ← stage.time * .999; top ← stage.time * 1.001; FOR i IN [0..NumPaths[list]) DO cur ← Paths[list][i]; IF cur = NIL THEN LOOP; IF cur.time > top THEN EXIT; IF (cur.time >= bot) AND (cur.rise = stage.rise) THEN BEGIN numDups ← numDups + 1; RETURN; END; ENDLOOP; -- Find the slot in which to store the new path. deletePath[Paths[list][0]]; FOR i IN [1..max) DO cur ← Paths[list][i]; IF cur = NIL THEN LOOP; IF stage.time < cur.time THEN BEGIN Paths[list][i-1] ← copyPath[stage]; done ← TRUE; EXIT; END; Paths[list][i-1] ← cur; ENDLOOP; IF NOT done THEN Paths[list][max-1] ← copyPath[stage]; -- Find the new smallest times in the arrays for use as a threshold. IF Paths[list][0] = NIL THEN Threshold[list] ← 0 ELSE Threshold[list] ← 1.001 * Paths[list][0].time; END; PrintStage: PUBLIC PROC[stream: IO.STREAM, stage: Stage] = BEGIN first: BOOLEAN ← TRUE; i: INT; direction, format: Rope.ROPE; IF stage = NIL THEN BEGIN IO.PutRope[stream, "No such critical path.\n"]; RETURN; END; WHILE stage # NIL DO IF stage.rise THEN direction ← "high" ELSE direction ← "low"; IF first THEN BEGIN format ← "Node %s is driven %s at %.2fns"; first ← FALSE; END ELSE format ← " after\n %s is driven %s at %.2fns"; IO.PutF[stream, format, IO.rope[stage.piece2Node[stage.piece2Size-1].name], IO.rope[direction], IO.real[stage.time]]; IF PrintEdgeSpeeds THEN IO.PutF[stream, " with edge speed %.3f ns/volt", IO.real[stage.edgeSpeed]]; FOR i DECREASING IN [0..stage.piece2Size-2] DO IO.PutF[stream, "\n ...through fet at (%.1f, %.1f) to %s", IO.real[stage.piece2Fet[i].x/Printout.Units], IO.real[stage.piece2Fet[i].y/Printout.Units], IO.rope[stage.piece2Node[i].name]]; ENDLOOP; FOR i IN [0..stage.piece1Size) DO IO.PutF[stream, "\n ...through fet at (%.1f, %.1f) to %s", IO.real[stage.piece1Fet[i].x/Printout.Units], IO.real[stage.piece1Fet[i].y/Printout.Units], IO.rope[stage.piece1Node[i].name]]; ENDLOOP; stage ← stage.prev; ENDLOOP; IO.PutRope[stream, "\n"]; END; CriticalCmd: PUBLIC CmdProc = BEGIN list: listType ← any; ok: BOOLEAN; index: INT ← 1; fileName: Rope.ROPE ← NIL; stream: IO.STREAM; msg: Rope.ROPE ← NIL; -- Scan off all of the arguments. WHILE args # NIL DO IF (Rope.Fetch[args.rope, 0] >= '0) AND (Rope.Fetch[args.rope, 0] <= '9) THEN BEGIN IF Rope.Fetch[args.rope, Rope.Length[args.rope] - 1] = 'm THEN BEGIN list ← memory; args.rope ← Rope.Substr[args.rope, 0, Rope.Length[args.rope] - 1]; END ELSE IF Rope.Fetch[args.rope, Rope.Length[args.rope] - 1] = 'w THEN BEGIN list ← watched; args.rope ← Rope.Substr[args.rope, 0, Rope.Length[args.rope] - 1]; END ELSE list ← any; [ok, index] ← Parse.Int[args]; IF index < 1 OR NOT ok THEN BEGIN IO.PutF[StdOut, "Bad critical path number: %s\n", IO.rope[args.rope]]; RETURN; END; END ELSE IF Rope.Fetch[args.rope, 0] # '- THEN fileName ← args.rope ELSE IO.PutF[StdOut, "Unknown %s switch ignored.\n", IO.rope[args.rope]]; args ← args.next; ENDLOOP; -- Make sure the index is within the range of what we've recorded. IF index > NumPaths[list] THEN BEGIN IO.PutF[StdOut, "Sorry, but there are only %d paths.", IO.int[NumPaths[list]]]; IO.PutF[StdOut, " Using index %d.\n", IO.int[NumPaths[list]]]; index ← 0; END ELSE index ← NumPaths[list] - index; -- Open the file, if neccessary, then call the procedure to print out stuff. IF fileName # NIL THEN BEGIN stream ← FS.StreamOpen[fileName, $create ! FS.Error => IF error.group # bug THEN {msg ← error.explanation; CONTINUE}]; IF msg # NIL THEN BEGIN IO.PutF[StdOut, "Couldn't open %s: %s\n", IO.rope[fileName], IO.rope[msg]]; RETURN; END; END ELSE stream ← StdOut; PrintStage[stream, Paths[list][index]]; IF fileName # NIL THEN IO.Close[stream]; END; Clear: PUBLIC PROC[] = BEGIN FOR i:INT IN [0..MaxPaths) DO Paths[memory][i] ← NIL; Paths[any][i] ← NIL; Paths[watched][i] ← NIL; ENDLOOP; Threshold[memory] ← 0.0; Threshold[any] ← 0.0; Threshold[watched] ← 0.0; END; Stats: PUBLIC PROC[] = BEGIN IO.PutF[StdOut, "Number of calls to record delays: %d.\n", IO.int[numRecords[any]]]; IO.PutF[StdOut, "Number of calls to record memory delays: %d.\n", IO.int[numRecords[memory]]]; IO.PutF[StdOut, "Number of calls to record watched delays: %d.\n", IO.int[numRecords[watched]]]; IO.PutF[StdOut, "Number of dups eliminated in recording: %d.\n", IO.int[numDups]]; END; RecomputeCmd: PUBLIC CmdProc = BEGIN FOR i:INT IN [0..NumPaths[any]) DO recompute[Paths[any][i]]; ENDLOOP; FOR i:INT IN [0..NumPaths[memory]) DO recompute[Paths[memory][i]]; ENDLOOP; FOR i:INT IN [0..NumPaths[watched]) DO recompute[Paths[watched][i]]; ENDLOOP; END; recompute: PROC[stage: Stage] = BEGIN IF (stage = NIL) OR (stage.prev = NIL) THEN RETURN; recompute[stage.prev]; Model.Delay[stage]; END; dumpFet: PROC[stream: IO.STREAM, fet:Fet] = -- This is a local utility procedure that prints information about -- a transistor in ASCII form. BEGIN IO.PutF[stream, " fet %d 0 %f %f %f %f\n", IO.int[fet.type], IO.real[fet.area], IO.real[fet.aspect], IO.real[fet.x], IO.real[fet.y]]; END; dumpNode: PROC[stream: IO.STREAM, node: Node] = -- This is a local utility procedure that prints information about -- a node in ASCII form. BEGIN IO.PutF[stream, " node 0 %s %f %f %f %f\n", IO.rope[node.name], IO.real[node.cap], IO.real[node.res], IO.real[node.hiTime], IO.real[node.loTime]]; END; dumpStage: PROC[stream: IO.STREAM, stage: Stage] = -- This is a recursive local utility procedure that prints -- information about a path consisting of a chain of stages. -- The stages are printed in increasing order of delay time. BEGIN IF stage.prev # NIL THEN dumpStage[stream, stage.prev]; IO.PutF[stream, " stage %d %d ", IO.int[stage.piece1Size], IO.int[stage.piece2Size]]; IF stage.rise THEN IO.PutRope[stream, "1 "] ELSE IO.PutRope[stream, "0 "]; IO.PutF[stream, "%f %f\n", IO.real[stage.time], IO.real[stage.edgeSpeed]]; FOR i:INT IN [0..stage.piece1Size) DO dumpFet[stream, stage.piece1Fet[i]]; dumpNode[stream, stage.piece1Node[i]]; ENDLOOP; FOR i:INT IN [0..stage.piece2Size) DO IF i # stage.piece2Size-1 THEN dumpFet[stream, stage.piece2Fet[i]]; dumpNode[stream, stage.piece2Node[i]]; ENDLOOP; END; DumpCmd: PUBLIC CmdProc = BEGIN stream: IO.STREAM; msg: Rope.ROPE ← NIL; IF args = NIL THEN stream ← StdOut ELSE stream ← FS.StreamOpen[args.rope, $create ! FS.Error => IF error.group # bug THEN {msg ← error.explanation; CONTINUE}]; IF msg # NIL THEN BEGIN IO.PutF[StdOut, "Couldn't open %s for writing: %s.\n", IO.rope[args.rope], IO.rope[msg]]; RETURN; END; FOR i: INT IN [0..NumPaths[any]) DO IF Paths[any][i] = NIL THEN LOOP; IO.PutF[stream, "Any %d\n", IO.int[i]]; dumpStage[stream, Paths[any][i]]; ENDLOOP; FOR i: INT IN [0..NumPaths[memory]) DO IF Paths[memory][i] = NIL THEN LOOP; IO.PutF[stream, "Memory %d\n", IO.int[i]]; dumpStage[stream, Paths[memory][i]]; ENDLOOP; FOR i: INT IN [0..NumPaths[watched]) DO IF Paths[watched][i] = NIL THEN LOOP; IO.PutF[stream, "Watch %d\n", IO.int[i]]; dumpStage[stream, Paths[watched][i]]; ENDLOOP; IO.Close[stream]; END; undumpFet: PROC[stream: IO.STREAM] RETURNS [fet: Fet] = -- This is a local utility procedure that reads in the -- description of a transistor from an ASCII file, as dumped -- by dumpFet. BEGIN fet ← NEW[FetRec ← [NIL, NIL, NIL, 0.0, 0.0, 0.0, 0.0, 0, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NIL]]; [] ← IO.GetTokenRope[stream, Parse.WhiteSpace]; fet.type ← IO.GetInt[stream]; [] ← IO.GetTokenRope[stream, Parse.WhiteSpace]; fet.area ← IO.GetReal[stream]; fet.aspect ← IO.GetReal[stream]; fet.x ← IO.GetReal[stream]; fet.y ← IO.GetReal[stream]; RETURN [fet]; END; undumpNode: PROC[stream: IO.STREAM] RETURNS [node: Node] = -- This is a local routine that reads in the description of -- a node, as dumped by dumpNode, and returns a new node. BEGIN node ← NEW[NodeRec ← [NIL, 0, 0, 0, 0, NIL, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE]]; [] ← IO.GetTokenRope[stream, Parse.WhiteSpace]; [] ← IO.GetTokenRope[stream, Parse.WhiteSpace]; [token: node.name] ← IO.GetTokenRope[stream, Parse.WhiteSpace]; node.cap ← IO.GetReal[stream]; node.res ← IO.GetReal[stream]; node.hiTime ← IO.GetReal[stream]; node.loTime ← IO.GetReal[stream]; RETURN [node]; END; undumpStage: PROC[stream: IO.STREAM] RETURNS [stage: Stage] = -- Local procedure to read in ASCII description of stage. BEGIN i: INT; stage ← NEW[StageRec]; stage.piece1Size ← IO.GetInt[stream]; stage.piece2Size ← IO.GetInt[stream]; i ← IO.GetInt[stream]; IF i # 0 THEN stage.rise ← TRUE ELSE stage.rise ← FALSE; stage.time ← IO.GetReal[stream]; stage.edgeSpeed ← IO.GetReal[stream]; stage.prev ← NIL; FOR i IN [0..stage.piece1Size) DO stage.piece1Fet[i] ← undumpFet[stream]; stage.piece1Node[i] ← undumpNode[stream]; ENDLOOP; FOR i IN [0..stage.piece2Size) DO IF i = stage.piece2Size-1 THEN stage.piece2Fet[i] ← NIL ELSE stage.piece2Fet[i] ← undumpFet[stream]; stage.piece2Node[i] ← undumpNode[stream]; ENDLOOP; RETURN [stage]; END; UndumpCmd: PUBLIC CmdProc = BEGIN rope: Rope.ROPE; index: INT; prev, next: Stage; stream: IO.STREAM; msg: Rope.ROPE ← NIL; list: listType; {ENABLE ANY => CONTINUE; IF args = NIL THEN BEGIN IO.PutRope[StdOut, "No file name given.\n"]; RETURN; END; stream ← FS.StreamOpen[args.rope ! FS.Error => IF error.group # bug THEN {msg ← error.explanation; CONTINUE}]; IF msg # NIL THEN BEGIN IO.PutF[StdOut, "Couldn't open %s for reading: %s.\n", IO.rope[args.rope], IO.rope[msg]]; RETURN; END; FOR i: INT IN [0..MaxPaths) DO Paths[any][i] ← NIL; Paths[memory][i] ← NIL; Paths[watched][i] ← NIL; ENDLOOP; [token: rope] ← IO.GetTokenRope[stream, Parse.WhiteSpace]; WHILE TRUE DO index ← IO.GetInt[stream]; IF Rope.Equal[rope, "Memory"] THEN list ← memory ELSE IF Rope.Equal[rope, "Watch"] THEN list ← watched ELSE list ← any; prev ← NIL; WHILE TRUE DO [token: rope] ← IO.GetTokenRope[stream, Parse.WhiteSpace]; IF NOT Rope.Equal[rope, "stage"] THEN EXIT; next ← undumpStage[stream]; next.prev ← prev; Paths[list][index] ← next; prev ← next; ENDLOOP; ENDLOOP; }; IO.Close[stream]; END; END.