-- FILE: SlopeModelImpl.mesa
-- Last edited by Ousterhout, November 29, 1983 2:50 pm

-- This file implements the slope model for Xerox Crystal.  To understand
-- how the slope model works, see either the Berkeley code or the paper
-- by Ousterhout on the Crystal models.  This model uses slopes and the
-- Penfield-Rubinstein model for distributed capacitance.  It uses intrinsic
-- rise-times everywhere (the outES tables are not used).

DIRECTORY
    Globals,
    IO,
    Model,
    Printout,
    SlopeModel;

SlopeModelImpl:  CEDAR PROGRAM
IMPORTS
    Globals,
    IO,
    Model,
    Printout
EXPORTS SlopeModel =
BEGIN
OPEN Globals, Model;

-- The following stuff is used to keep from printing zillions of
-- messages when the tables overflow a lot.

maxUpRatio:  ARRAY[0..maxFetTypes) OF REAL ← ALL[0];
maxDownRatio: ARRAY[0..maxFetTypes) OF REAL ← ALL[0];


SlopeDelay: PUBLIC DelayProc =
    BEGIN
    f: Fet;
    r, c, resVolts, rFactor, rcIntegral, tmp: REAL;
    rEff, ratio: REF ParmArray;
    thisC, thisR, tmp2: REAL;
    esIntegral, es: REAL;
    isLoad: BOOLEAN;
    type: TType;
    i: INT;
    
    -- Make sure that there is stuff in the stage.
    
    stage.time ← -1.0;
    stage.edgeSpeed ← 0;
    IF (stage.piece1Size = 0) AND (stage.piece2Size <= 1) THEN
        BEGIN
        IO.PutRope[StdOut, "Crystal bug:  nothing in stage!\n"];
        RETURN;
        END;
    
    -- Precompute the voltage drop used to compute resistor edge speeds.
    
    IF stage.rise THEN
        BEGIN
        resVolts ← vdd↑ - vinv↑;
        rFactor ← rUpFactor↑;
        END
    ELSE
        BEGIN
        resVolts ← vinv↑;
        rFactor ← rDownFactor↑;
        END;
    
    -- Figure out if there's a load driving this stage.  We know there's a
    -- load if there's no piece1 and the first node in piece2 is fixed in value.
    
    IF (stage.piece1Size = 0) AND
        (stage.piece2Node[0].always0 OR stage.piece2Node[0].always1) THEN
        isLoad ← TRUE
    ELSE isLoad ← FALSE;
    
    -- Go through the two pieces of the path, computing the integral of RdC
    -- through the path.  There are a couple of hitches in this.  First of all,
    -- don't count any capacitance in piece1:  we assume it's been discharged
    -- already.  Also, we have to leave out any resistance associated with the
    -- trigger transistor for now (it will be interpolated and added later).  To
    -- add in its value later, we have to remember the total capacitance of
    -- the path.  Also, leave out the capacitance and resistance of the node
    -- that is signal source:  it is supposed to have infinite capacitance and
    -- zero resistance.
    
    r ← 0.0;
    c ← 0.0;
    es ← 0.0;
    rcIntegral ← 0.0;
    esIntegral ← 0.0;
    FOR i IN [0..stage.piece1Size) DO
        IF i # 0 THEN
            BEGIN
            f ← stage.piece1Fet[i];
            type ← TypeTable[f.type];
            IF stage.rise THEN
                BEGIN
                tmp ← type.upREff[0];
                es ← es + type.upOutES[0] * f.aspect;
                END
            ELSE
                BEGIN
                tmp ← type.downREff[0];
                es ← es + type.downOutES[0] * f.aspect;
                END;
            IF tmp = 0 THEN RETURN;
            r ← r + (tmp * f.aspect);
            END;
        
        -- The last node in piece1 is the signal source, so don't
        -- consider it.
        
        IF i # (stage.piece1Size - 1) THEN
            BEGIN
            tmp ← stage.piece1Node[i].res;
            r ← r + (tmp * rFactor);
            es ← es + tmp/(resVolts*1000);
            END;
        ENDLOOP;
    
    FOR i IN [0..stage.piece2Size) DO
        -- When the stage is load-driven, we don't consider the
        -- first transistor (it will be interpolated below), or the
        -- first node in piece2, since it is the signal source.
        
        IF (i=0) AND isLoad THEN LOOP;
        
        -- If no piece1, then first node in piece2 supplies signal
        -- so don't use the capacitance or resistance associated
        -- with the node.
        
        IF (i # 0) OR (stage.piece1Size # 0) THEN
            BEGIN
            thisR ← stage.piece2Node[i].res;
            thisC ← stage.piece2Node[i].cap;
            r ← r + (thisR*rFactor);
            c ← c + thisC;
            rcIntegral ← rcIntegral + (r * thisC);
            es ← es + thisR/(resVolts*1000);
            esIntegral ← esIntegral + (es * thisC);
            END;
        f ← stage.piece2Fet[i];
        IF f # NIL THEN
            BEGIN
            type ← TypeTable[f.type];
            IF stage.rise THEN
                BEGIN
                thisR ← type.upREff[0];
                es ← es + type.upOutES[0] * f.aspect;
                END
            ELSE
                BEGIN
                thisR ← type.downREff[0];
                es ← es + type.downOutES[0] * f.aspect;
                END;
            IF thisR = 0 THEN RETURN;
            r ← r + thisR*f.aspect;
            thisC ← f.area * type.cPerArea;
            c ← c + thisC;
            rcIntegral ← rcIntegral + (thisC * r);
            esIntegral ← esIntegral + (thisC * es);
            END;
        ENDLOOP;
        
    -- If this stage is a carry-over from a bus (no piece1 and not a load), then
    -- we're done;  return the RC integral as the delay, and sum the RC integral
    -- and the edgeSpeed from the last stage to produce the new edgeSpeed for
    -- this stage.  If this isn't a bus carryover, then figure out which is the
    -- transistor that is to be used for interpolation.
    
    IF stage.piece1Size = 0 THEN
        BEGIN
        IF NOT isLoad THEN
            BEGIN
            stage.time ← rcIntegral/1000.0 + stage.prev.time;
            stage.edgeSpeed ← esIntegral;
            RETURN;
            END;
        f ← stage.piece2Fet[0];
        END
    ELSE f ← stage.piece1Fet[0];
    
    -- Now do linear interpolation in the slope tables to compute the
    -- effective resistance of the trigger or load transistor.
    
    type ← TypeTable[f.type];
    IF stage.rise THEN
        BEGIN
        ratio ← type.upESRatio;
        rEff ← type.upREff;
        esIntegral ← esIntegral + (c * type.upOutES[0] * f.aspect);
        END
    ELSE
        BEGIN
        ratio ← type.downESRatio;
        rEff ← type.downREff;
        esIntegral ← esIntegral + (c * type.downOutES[0] * f.aspect);
        END;
    
    -- Zero resistance means this transistor can't drive in this direction, so
    -- return immediately.  Zero capacitance means the node transits instantly.
    
    IF rEff[0] = 0 THEN RETURN;
    IF c = 0 THEN
        BEGIN
        IO.PutF[StdOut, "Zero capacitance in stage leading to %s!\n",
            IO.rope[Printout.NodeRope[stage.piece2Node[stage.piece2Size-1]]]];
        stage.time ← stage.prev.time;
        stage.edgeSpeed ← 0;
        RETURN;
        END;
    
    tmp ← stage.prev.edgeSpeed/esIntegral;
    
    FOR i IN [0..maxSlopePoints-1) DO
        IF tmp < ratio[i+1] THEN EXIT;
        IF ratio[i+1] < ratio[i] THEN EXIT;
        ENDLOOP;
    
    -- If we ran off the top of the table, then use the last two values in
    -- the table for extrapolation.
    
    IF tmp >= ratio[i+1] THEN
        BEGIN
        IF stage.rise THEN
            BEGIN
            IF tmp > maxUpRatio[f.type] THEN
                BEGIN
                maxUpRatio[f.type] ← tmp;
                IO.PutF[StdOut, "Warning: ran off end of %s up slope table with\n",
                    IO.rope[type.name]];
                IO.PutF[StdOut, "    edge speed ratio %f.  Maybe you should ",
                    IO.real[tmp]];
                IO.PutRope[StdOut, "expand\n    the range of the table.\n"];
                END;
            END
        ELSE
            BEGIN
            IF tmp > maxDownRatio[f.type] THEN
                BEGIN
                maxDownRatio[f.type] ← tmp;
                IO.PutF[StdOut, "Warning: ran off end of %s down slope table with\n",
                    IO.rope[type.name]];
                IO.PutF[StdOut, "    edge speed ratio %f.  Maybe you should ",
                    IO.real[tmp]];
                IO.PutRope[StdOut, "expand\n    the range of the table.\n"];
                END;
            END;
        i ← i-1;
        END;
    
    tmp ← (tmp - ratio[i])/(ratio[i+1] - ratio[i]);
    tmp2 ← f.aspect * (rEff[i] + (tmp * (rEff[i+1] - rEff[i])));
    rcIntegral ← rcIntegral + tmp2*c;
    stage.time ← rcIntegral/1000.0 + stage.prev.time;
    stage.edgeSpeed ← esIntegral;
    END;

END.