-- 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.