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