-- FILE: ModelImpl.mesa
-- Last edited by Ousterhout, November 22, 1983 3:32 pm

-- This file contains the implementation of various routines
-- and constants for dealing with technology-specific electrical
-- information.

DIRECTORY
    Globals,
    IO,
    Model,
    Parse,
    RealFns,
    Rope,
    SlopeModel;

ModelImpl: CEDAR PROGRAM
IMPORTS
    Globals,
    IO,
    Parse,
    RealFns,
    Rope,
    SlopeModel
EXPORTS Model =
BEGIN OPEN Model, Globals;

-- The model-independent parameters, and tables used to access them.

diffCPerArea: PUBLIC REF REAL ← NEW[REAL ← .00029];
polyCPerArea: PUBLIC REF REAL ← NEW[REAL ← .00004];
metalCPerArea: PUBLIC REF REAL ← NEW[REAL ← .000026];
diffCPerPerim: PUBLIC REF REAL ← NEW[REAL ← .00023];
diffR: PUBLIC REF REAL ← NEW[REAL ← 60];
polyR: PUBLIC REF REAL ← NEW[REAL ← 3];
metalR: PUBLIC REF REAL ← NEW[REAL ← .04];

vdd: PUBLIC REF REAL ← NEW[REAL ← 5.0];
vinv: PUBLIC REF REAL ← NEW[REAL ← 2.5];

rUpFactor: PUBLIC REF REAL ← NEW[REAL ← 1.0];
rDownFactor: PUBLIC REF REAL ← NEW[REAL ← 1.0];

-- Gives names of various parasitic parameters.

parmNames: ARRAY[0..9) OF Rope.ROPE ←
    [
    "diffcperarea",
    "diffcperperim",
    "diffresistance",
    "metalcperarea",
    "metalresistance",
    "polycperarea",
    "polyresistance",
    "vdd",
    "vinv"
    ];

-- Gives units for various parmaters (used in printing them out).

parmUnits: ARRAY[0..9) OF Rope.ROPE ←
    [
    "pf/sq. micron",
    "pf/micron",
    "ohms/square",
    "pf/sq. micron",
    "ohms/square",
    "pf/sq. micron",
    "ohms/square",
    "volts",
    "volts"
    ];

parmLocs: ARRAY[0..9) OF REF REAL ← 
    [
    diffCPerArea,
    diffCPerPerim,
    diffR,
    metalCPerArea,
    metalR,
    polyCPerArea,
    polyR,
    vdd,
    vinv
    ];

-- Information about different models:

nModels: INT = 2;
curModel: INT ← 1;

modelNames: ARRAY[0..nModels) OF Rope.ROPE ←
    [
    "rc (simple lumped RC approximation)",
    "slope (not implemented yet)"
    ];

modelDelayProcs: ARRAY[0..nModels) OF DelayProc ←
    [
    RCDelay,
    SlopeModel.SlopeDelay
    ];

TypeTable: PUBLIC ARRAY[0..maxFetTypes) OF TType;

-- Names of type table fields:

fieldNames: ARRAY[0..9) OF Rope.ROPE ←
    [
    "cperarea",
    "cperwidth",
    "histrength",
    "lowstrength",
    "on",
    "rdown",
    "rup",
    "slopeparmsdown",
    "slopeparmsup"
    ];


ModelCmd: PUBLIC CmdProc =
    BEGIN
    index: INT;
    
    -- If no arguments, just print out information about the available
    -- models.
    
    IF args = NIL THEN
        BEGIN
        FOR i:INT IN [0..nModels) DO
            IF i = curModel THEN
                IO.PutF[StdOut, "**** %s\n", IO.rope[modelNames[i]]]
            ELSE IO.PutF[StdOut, "    %s\n", IO.rope[modelNames[i]]];
            ENDLOOP;
         RETURN;
         END;
     
     TRUSTED {index ← Parse.Lookup[args.rope, DESCRIPTOR[modelNames]]};
     IF index = -1 THEN
         BEGIN
         IO.PutF[StdOut, "%s isn't a model name.\n", IO.rope[args.rope]];
         RETURN;
         END
     ELSE IF index = -2 THEN
         BEGIN
         IO.PutF[StdOut, "%s is an ambiguous model abbreviation.\n",
             IO.rope[args.rope]];
         RETURN;
         END;
     curModel ← index;
     END;    


ParmCmd: PUBLIC CmdProc =
    BEGIN
    name: Rope.ROPE;
    valueArg: Arg;
    value: REAL;
    index: INT;
    ok: BOOLEAN;
    
    -- If no arguments, then just print out all current values.
    
    IF args = NIL THEN
        BEGIN
        FOR i:INTEGER IN [0..LENGTH[parmNames]) DO
            IO.PutF[StdOut, "%s = %f %s\n", IO.rope[parmNames[i]],
                IO.real[parmLocs[i]↑], IO.rope[parmUnits[i]]];
            ENDLOOP;
        RETURN;
        END;
    
    -- Arguments must come in <name value> pairs.
    
    WHILE args # NIL DO
        name ← args.rope;
        args ← args.next;
        IF args = NIL THEN
            BEGIN
            IO.PutF[StdOut, "No value given for %s.\n",
                IO.rope[name]];
            EXIT;
            END;
        valueArg ← args;
        args ← args.next; 
        
        TRUSTED {index ← Parse.Lookup[name, DESCRIPTOR[parmNames]]};
        IF index = -2 THEN
            BEGIN
            IO.PutF[StdOut, "%s isn't a model parameter.\n",
                IO.rope[name]];
            LOOP;
            END;
        IF index = -1 THEN
            BEGIN
            IO.PutF[StdOut, "%s is an ambiguous abbreviation.\n",
                IO.rope[name]];
            LOOP;
            END;
        [ok, value] ← Parse.Real[valueArg];
        IF (value <= 0.0) OR (NOT ok) THEN
            BEGIN
            IO.PutF[StdOut, "Bad value given for %s.\n",
                IO.rope[name]];
            LOOP;
            END;
        parmLocs[index]↑ ← value;
        ENDLOOP;
    END;


getSlope: PROC[args: Arg, ratio, rEff, outES: REF ParmArray]
    RETURNS [Arg] =
    
    -- This is a utility routine used to read in slope table
    -- information.  The arguments are processed in batches
    -- of three, filling up the tables.  The return result is
    -- the next argument after the last one used.
    
    BEGIN
    ok: BOOLEAN;
    val: REAL;
    i: INT;
    
    FOR i IN [0..maxSlopePoints) DO
        
        [ok, val] ← Parse.Real[args];
        IF NOT ok THEN EXIT;
        args ← args.next;
        IF val < 0.0 THEN
            BEGIN
            IO.PutF[StdOut,
                "Can't have negative value %f in slope table.\n", IO.real[val]];
            EXIT;
            END;
        ratio[i] ← val;
        
        [ok, val] ← Parse.Real[args];
        IF NOT ok THEN
            BEGIN
            IO.PutRope[StdOut,
                "Only one number supplied for last slope table entry.\n"];
            EXIT;
            END;
        args ← args.next;
        rEff[i] ← val;
        
        [ok, val] ← Parse.Real[args];
        IF NOT ok THEN
            BEGIN
            IO.PutRope[StdOut,
                "Only two numbers supplied for last slope table entry.\n"];
            EXIT;
            END;
        args ← args.next;
        outES[i] ← val;
        
        -- Watch out for decreasing ratio values.
         
        IF i > 0 THEN
            BEGIN
            IF ratio[i] < ratio[i-1] THEN
                BEGIN
                IO.PutF[StdOut, "Warning: edge speed ratio %f", IO.real[ratio[i]]];
                IO.PutRope[StdOut, " isn't monotonically increasing.\n"];
                IO.PutRope[StdOut, "    Interpolation table truncated.\n"];
                EXIT;
                END;
            END;
        ENDLOOP;
    
    -- Mark the end of the table with a negative ratio.  If there's
    -- only a single entry, add a second one so that interpolation
    -- and extrapolation will work.
    
    IF i = 0 THEN
        BEGIN
        ratio[0] ← 0;
        rEff[0] ← 1000;
        outES[0] ← 0;
        i ← 1;
        END;
    IF i = 1 THEN
        BEGIN
        ratio[1] ← ratio[0] + 1000;
        rEff[1] ← rEff[0];
        outES[1] ← outES[0];
        ratio[2] ← -1;
        END
    ELSE IF i < maxSlopePoints THEN ratio[i] ← -1;
    RETURN [args];
    END;
    


printType: PROC[index: INTEGER] =
    -- This is a utility routine used to print out all the information for
    -- a transistor of a given type.
    
    BEGIN
    IO.PutF[StdOut, "%s: ", IO.rope[TypeTable[index].name]];
    IF TypeTable[index].on0 THEN
        IO.PutRope[StdOut, "on gate0"];
    IF TypeTable[index].on1 THEN
        IO.PutRope[StdOut, "on gate1"];
    IF TypeTable[index].onAlways THEN
        IO.PutRope[StdOut, "on always"];
    IO.PutF[StdOut, " histrength %d", IO.int[TypeTable[index].strengthHi]];
    IO.PutF[StdOut, " lowstrength %d", IO.int[TypeTable[index].strengthLo]];
    IO.PutF[StdOut, "\n    cperarea %.3f cperwidth %.3f",
        IO.real[TypeTable[index].cPerArea], IO.real[TypeTable[index].cPerWidth]];
    IO.PutF[StdOut, " rup %.1f rdown %.1f\n",
        IO.real[TypeTable[index].rUp], IO.real[TypeTable[index].rDown]];
    IO.PutRope[StdOut, "    Slope parameters up (ratio, rEff, outES) =\n"];
    printSlope[TypeTable[index].upESRatio, TypeTable[index].upREff,
        TypeTable[index].upOutES, maxSlopePoints];
    IO.PutRope[StdOut, "    Slope parameters down (ratio, rEff, outES) =\n"];
    printSlope[TypeTable[index].downESRatio, TypeTable[index].downREff,
        TypeTable[index].downOutES, maxSlopePoints];
    END;
        
printSlope: PROC[esRatio, rEff, outES: REF ParmArray, maxPoints: INT] =
    -- This is a utility routine used to print out the values in slope
    -- tables.  The values in the tables are printed in a reasonably
    -- pretty fashion, two entries from each table per line.
    
    BEGIN
    lineHasStuff: BOOLEAN ← FALSE;
    
    FOR i:INT IN [0..maxPoints) DO
        IF esRatio[i] < 0 THEN EXIT;
        IO.PutF[StdOut, "    %8.3f, %8.1f, %8.3f;",
            IO.real[esRatio[i]], IO.real[rEff[i]], IO.real[outES[i]]];
        IF lineHasStuff THEN
            BEGIN
            lineHasStuff ← FALSE;
            IO.PutRope[StdOut, "\n"];
            END
        ELSE lineHasStuff ← TRUE;
        ENDLOOP;
    
    IF lineHasStuff THEN IO.PutRope[StdOut, "\n"];
    END;       


TransistorCmd: PUBLIC CmdProc =
    BEGIN
    type: INT;
    field: Rope.ROPE;
    valueArg: Arg;
    index, intVal: INT;
    realVal: REAL;
    ok: BOOLEAN;
    
    -- If there are no arguments, print out all fields of all transistor types.
    -- If there is only one argument print out all fields for that transistor
    -- type.
    
    IF args = NIL THEN
        BEGIN
        FOR i:INTEGER IN [0..maxFetTypes) DO
            IF TypeTable[i] = NIL THEN RETURN;
            printType[i];
            ENDLOOP;
            RETURN;
        END
    ELSE IF args.next = NIL THEN
        BEGIN
        FOR i: INTEGER IN [0..maxFetTypes) DO
            IF TypeTable[i] = NIL THEN EXIT;
            IF Rope.Equal[args.rope, TypeTable[i].name, FALSE] THEN
                BEGIN
                printType[i];
                RETURN;
                END;
            ENDLOOP;
        IO.PutF[StdOut, "Transistor type %s doesn't exist.\n", IO.rope[args.rope]];
        RETURN;
        END;
    
    -- Make sure that the type name exists.  Create a new one
    -- if necessary.
    
    type ← -1;
    FOR i: INTEGER IN [0..maxFetTypes) DO
        IF TypeTable[i] = NIL THEN
            BEGIN
            type ← i;
            TypeTable[type] ← NEW[TTypeRec ←
                [name: args.rope, strengthHi: 0, strengthLo: 0,
                cPerArea: 0, cPerWidth: 0, rUp: 0, rDown: 0,
                on0: FALSE, on1: TRUE, onAlways: FALSE,
                upESRatio: NEW[ParmArray ←
                    [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
                upREff: NEW[ParmArray ←
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                upOutES: NEW[ParmArray ←
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                downESRatio: NEW[ParmArray ←
                    [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
                downREff: NEW[ParmArray ←
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
                downOutES: NEW[ParmArray ←
                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]];
            EXIT;
            END;
        IF Rope.Equal[args.rope, TypeTable[i].name, FALSE] THEN
            BEGIN
            type ← i;
            EXIT;
            END;
        ENDLOOP;
    
    -- Process <field value> pairs.
    
    args ← args.next;
    WHILE args # NIL DO
        field ← args.rope;
        args ← args.next;
        IF args = NIL THEN
            BEGIN
            IO.PutF[StdOut, "No value given for %s.\n",
                IO.rope[field]];
            EXIT;
            END;
        valueArg ← args;
        args ← args.next; 
        
        TRUSTED {index ← Parse.Lookup[field, DESCRIPTOR[fieldNames]]};
        IF index = -2 THEN
            BEGIN
            IO.PutF[StdOut, "%s isn't a field name.\n",
                IO.rope[field]];
            LOOP;
            END;
        IF index = -1 THEN
            BEGIN
            IO.PutF[StdOut, "%s is an ambiguous abbreviation.\n",
                IO.rope[field]];
            LOOP;
            END;
        
        SELECT index FROM
            0 =>							-- cperarea
                BEGIN
                [ok, realVal] ← Parse.Real[valueArg];
                IF (NOT ok) OR (realVal < 0) THEN
                    IO.PutRope[StdOut, "Bad capacitance value for cperarea.\n"]
                ELSE TypeTable[type].cPerArea ← realVal;
                END;
            
            1 =>							-- cperwidth
                BEGIN
                [ok, realVal] ← Parse.Real[valueArg];
                IF (NOT ok) OR (realVal < 0) THEN
                    IO.PutRope[StdOut, "Bad capacitance value for cperwidth.\n"]
                ELSE TypeTable[type].cPerWidth ← realVal;
                END;
                
            2 =>							-- histrength
                BEGIN
                [ok, intVal] ← Parse.Int[valueArg];
                IF (NOT ok) OR (intVal < 0) THEN
                    IO.PutRope[StdOut, "Bad value for histrength.\n"]
                ELSE TypeTable[type].strengthHi ← intVal;
                END;
            
            3 =>							-- lowstrength
                BEGIN
                [ok, intVal] ← Parse.Int[valueArg];
                IF (NOT ok) OR (intVal < 0) THEN
                    BEGIN
                    IO.PutRope[StdOut, "Bad value for lowstrength.\n"];
                    EXIT;
                    END;
                TypeTable[type].strengthLo ← intVal;
                END;

            4 =>							-- on
                BEGIN
                IF Rope.Equal[valueArg.rope, "gate1", TRUE] THEN
                    BEGIN
                    TypeTable[type].on1 ← TRUE;
                    TypeTable[type].on0 ← FALSE;
                    TypeTable[type].onAlways ← FALSE;
                    END
                ELSE IF Rope.Equal[valueArg.rope, "gate0", TRUE] THEN
                    BEGIN
                    TypeTable[type].on0 ← TRUE;
                    TypeTable[type].on1 ← FALSE;
                    TypeTable[type].onAlways ← FALSE;
                    END
                ELSE IF Rope.Equal[valueArg.rope, "always", TRUE] THEN
                    BEGIN
                    TypeTable[type].on0 ← FALSE;
                    TypeTable[type].on1 ← FALSE;
                    TypeTable[type].onAlways ← TRUE;
                    END
                ELSE IO.PutF[StdOut, "Unknown \"on\" condition: %s\n",
                    IO.rope[valueArg.rope]];
                END;
            
            5 =>							-- rdown
                BEGIN
                [ok, realVal] ← Parse.Real[valueArg];
                IF (NOT ok) OR (realVal < 0) THEN
                    IO.PutRope[StdOut, "Bad value for rdown.\n"]
                ELSE TypeTable[type].rDown ← realVal;
                END;
                
            6 =>							-- rup
                BEGIN
                [ok, intVal] ← Parse.Int[valueArg];
                IF (NOT ok) OR (realVal < 0) THEN
                    IO.PutRope[StdOut, "Bad value for rup.\n"]
                ELSE TypeTable[type].rUp ← intVal;
                END;
            
            7 =>							-- slopeparmsdown
                BEGIN
                args ← getSlope[valueArg,
                    TypeTable[type].downESRatio,
                    TypeTable[type].downREff,
                    TypeTable[type].downOutES];
                END;
            
            8 =>							-- slopeparmsup
                BEGIN
                args ← getSlope[valueArg,
                    TypeTable[type].upESRatio,
                    TypeTable[type].upREff,
                    TypeTable[type].upOutES];
                END;
            ENDCASE;
        ENDLOOP;
    
    rUpFactor↑ ← - RealFns.Ln[(vdd↑ - vinv↑)/vdd↑];
    rDownFactor↑ ← - RealFns.Ln[vinv↑/vdd↑];
    END;


NameToIndex: PUBLIC PROC[name: Rope.ROPE] RETURNS [index: INT] =
    BEGIN
    FOR i:INTEGER IN [0..maxFetTypes) DO
        IF TypeTable[i] = NIL THEN RETURN [-1];
        IF Rope.Equal[TypeTable[i].name, name, FALSE] THEN RETURN [i];
        ENDLOOP;
    RETURN [-1];
    END;


Delay: PUBLIC DelayProc =
    BEGIN
    (modelDelayProcs[curModel])[stage];
    END;
    
RCDelay: PUBLIC DelayProc =
    -- This procedure updates the time value in the given stage by
    -- computing the delay in the stage and combining that with the
    -- time from the previous stage.  A simple RC model is used to
    -- compute the delay.
    
    BEGIN
    f: Fet;
    r, c, tmp, rFactor: REAL;
    i: INT;
    
    -- Make sure that there is stuff in the path.
    
    stage.time ← -1.0;
    IF (stage.piece1Size = 0) AND (stage.piece2Size = 0) THEN
        BEGIN
        IO.PutRope[StdOut, "Crystal bug:  nothing in stage!"];
        RETURN;
        END;
        
    -- Go through the two pieces of the path, summing all the
    -- resistances in both pieces and the capacitances in piece2
    -- (this code assumes that the capacitance in piece1 has already
    -- been drained away before the driving transistor turns on).
    -- If a zero FET resistance is found, it means that the transistor
    -- can't drive in the given direction, so quit.
    
    r ← 0.0;
    c ← 0.0;
    IF stage.rise THEN rFactor ← rUpFactor↑
    ELSE rFactor ← rDownFactor↑;
    FOR i IN [0..stage.piece1Size) DO
        f ← stage.piece1Fet[i];
        IF stage.rise THEN tmp ← TypeTable[f.type].rUp
        ELSE tmp ← TypeTable[f.type].rDown;
        IF tmp = 0 THEN RETURN;
        r ← r + tmp*f.aspect + (stage.piece1Node[i].res*rFactor);
        ENDLOOP;
    FOR i IN [0..stage.piece2Size) DO
        f ← stage.piece2Fet[i];
        IF f # NIL THEN
            BEGIN
            IF stage.rise THEN tmp ← TypeTable[f.type].rUp
            ELSE tmp ← TypeTable[f.type].rDown;
            IF tmp = 0 THEN RETURN;
            r ← r + tmp*f.aspect;
            c ← c + f.area*TypeTable[f.type].cPerArea;
            END;
        r ← r + (stage.piece2Node[i].res*rFactor);
        c ← c + stage.piece2Node[i].cap;
        ENDLOOP;
    
    -- Throw out the capacitance and resistance from the node that
    -- is supplying the signal (either the first node in piece1, or the
    -- first node in piece2 if there isn't a piece1).  This node is treated
    -- here as an infinite source of charge.
    
    IF stage.piece1Size # 0 THEN
        r ← r - (stage.piece1Node[stage.piece1Size-1].res*rFactor)
    ELSE
        BEGIN
        r ← r - (stage.piece2Node[0].res*rFactor);
        c ← c - stage.piece2Node[0].cap;
        END;
    
    stage.time ← (r*c)/1000.0 + stage.prev.time;
    END;

 
TypeTable[fetNEnh] ← NEW[TTypeRec ←
    [name: "NETran", strengthHi: 2, strengthLo: 3, cPerArea: .0004,
        cPerWidth: .00025, rUp: 24800, rDown: 9720, on0: FALSE,
        on1: TRUE, onAlways: FALSE,
        upESRatio: NEW[ParmArray ←
            [0, .0014, .0026, .017, .16, 1.6, 16, -1, 0, 0]],
        upREff: NEW[ParmArray ←
            [24800, 24800, 24600, 25800, 38400, 153000, 1070000, 0, 0, 0]],
        upOutES: NEW[ParmArray ←
            [37.2, 37.2, 37.5, 38, 42, 162, 1340, 0, 0, 0]],
        downESRatio: NEW[ParmArray ←
            [0, .018, .086, .74, 7.3, 73, -1, 0, 0, 0]],
        downREff: NEW[ParmArray ←
            [9720, 9720, 10100, 14000, 36300, 164000, 0, 0, 0, 0]],
        downOutES: NEW[ParmArray ←
            [4.0, 4.0, 4.0, 4.3, 10.2, 41.0, 0, 0, 0, 0]]]];
TypeTable[fetNEnhP] ← NEW[TTypeRec ←
    [name: "nenhp", strengthHi: 2, strengthLo: 3, cPerArea: .0004,
        cPerWidth: .00025, rUp: 200000, rDown: 200000, on0: FALSE,
        on1: TRUE, onAlways: FALSE,
        upESRatio: NEW[ParmArray ←
            [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
        upREff: NEW[ParmArray ←
            [200000, 200000, 0, 0, 0, 0, 0, 0, 0, 0]],
        upOutES: NEW[ParmArray ←
            [100, 100, 0, 0, 0, 0, 0, 0, 0, 0]],
        downESRatio: NEW[ParmArray ←
            [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
        downREff: NEW[ParmArray ←
            [200000, 200000, 0, 0, 0, 0, 0, 0, 0, 0]],
        downOutES: NEW[ParmArray ←
            [100, 100, 0, 0, 0, 0, 0, 0, 0, 0]]]];
TypeTable[fetNDep] ← NEW[TTypeRec ←
    [name: "NDTran", strengthHi: 2, strengthLo: 3, cPerArea: .0004,
        cPerWidth: .00025, rUp: 50000, rDown: 23000, on0: FALSE,
        on1: FALSE, onAlways: TRUE,
        upESRatio: NEW[ParmArray ←
            [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
        upREff: NEW[ParmArray ←
            [50000, 50000, 0, 0, 0, 0, 0, 0, 0, 0]],
        upOutES: NEW[ParmArray ←
            [75, 75, 0, 0, 0, 0, 0, 0, 0, 0]],
        downESRatio: NEW[ParmArray ←
            [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
        downREff: NEW[ParmArray ←
            [23000, 23000, 0, 0, 0, 0, 0, 0, 0, 0]],
        downOutES: NEW[ParmArray ←
            [9.5, 9.5, 0, 0, 0, 0, 0, 0, 0, 0]]]];
TypeTable[fetNLoad] ← NEW[TTypeRec ←
    [name: "nload", strengthHi: 2, strengthLo: 0, cPerArea: 0,
        cPerWidth: .00025, rUp: 21900, rDown: 0, on0: FALSE,
        on1: FALSE, onAlways: TRUE,
        upESRatio: NEW[ParmArray ←
            [0, .406, 1.344, 4.027, 13.4, 40.3, -1, 0, 0, 0]],
        upREff: NEW[ParmArray ←
            [21900, 21000, 23800, 31400, 43300, 57800, 0, 0, 0, 0]],
        upOutES: NEW[ParmArray ←
            [13.3, 13.3, 13.0, 21, 44.7, 107, 0, 0, 0, 0]],
        downESRatio: NEW[ParmArray ←
            [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
        downREff: NEW[ParmArray ←
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
        downOutES: NEW[ParmArray ←
            [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]]];
TypeTable[fetNSuper] ← NEW[TTypeRec ←
    [name: "nsuper", strengthHi: 2, strengthLo: 0, cPerArea: .0004,
        cPerWidth: .00025, rUp: 4800, rDown: 5100, on0: FALSE,
        on1: FALSE, onAlways: TRUE,
        upESRatio: NEW[ParmArray ←
            [0, .214, .464, 1.139, 3.58, 10.96, 36.2, 108, -1, 0]],
        upREff: NEW[ParmArray ←
            [4800, 4700, 5400, 6800, 8500, 8200, 1900, -17600, 0, 0]],
        upOutES: NEW[ParmArray ←
            [3.5, 3.5, 4.2, 6.7, 15.8, 42.9, 140, 413, 0, 0]],
        downESRatio: NEW[ParmArray ←
            [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
        downREff: NEW[ParmArray ←
            [5100, 5100, 0, 0, 0, 0, 0, 0, 0, 0]],
        downOutES: NEW[ParmArray ←
            [2.5, 2.5, 0, 0, 0, 0, 0, 0, 0, 0]]]];
TypeTable[fetNChan] ← NEW[TTypeRec ←
    [name: "nchan", strengthHi: 2, strengthLo: 3, cPerArea: .0004,
        cPerWidth: .00025, rUp: 24800, rDown: 9720, on0: FALSE,
        on1: TRUE, onAlways: FALSE,
        upESRatio: NEW[ParmArray ←
            [0, .0014, .0026, .017, .16, 1.6, 16, -1, 0, 0]],
        upREff: NEW[ParmArray ←
            [24800, 24800, 24600, 25800, 38400, 153000, 1070000, 0, 0, 0]],
        upOutES: NEW[ParmArray ←
            [37.2, 37.2, 37.5, 38, 42, 162, 1340, 0, 0, 0]],
        downESRatio: NEW[ParmArray ←
            [0, .018, .086, .74, 7.3, 73, -1, 0, 0, 0]],
        downREff: NEW[ParmArray ←
            [9720, 9720, 10100, 14000, 36300, 164000, 0, 0, 0, 0]],
        downOutES: NEW[ParmArray ←
            [4.0, 4.0, 4.0, 4.3, 10.2, 41.0, 0, 0, 0, 0]]]];
TypeTable[fetPChan] ← NEW[TTypeRec ←
    [name: "PETran", strengthHi: 2, strengthLo: 2, cPerArea: .0004,
        cPerWidth: .00025, rUp: 19050, rDown: 200000, on0: TRUE,
        on1: FALSE, onAlways: FALSE,
        upESRatio: NEW[ParmArray ←
            [0, .017, .028, .118, 1.05, 10.3, 31, 103, -1, 0]],
        upREff: NEW[ParmArray ←
            [19050, 19050, 19100, 20100, 27900, 42400, 23400, -114000, 0, 0]],
        upOutES: NEW[ParmArray ←
            [8.9, 8.9, 8.9, 8.9, 10.0, 25, 46.3, 97, 0, 0]],
        downESRatio: NEW[ParmArray ←
            [0, 1000, -1, 0, 0, 0, 0, 0, 0, 0]],
        downREff: NEW[ParmArray ←
            [200000, 200000, 0, 0, 0, 0, 0, 0, 0, 0]],
        downOutES: NEW[ParmArray ←
            [100, 100, 0, 0, 0, 0, 0, 0, 0, 0]]]];

rUpFactor↑ ← - RealFns.Ln[(vdd↑ - vinv↑)/vdd↑];
rDownFactor↑ ← - RealFns.Ln[vinv↑/vdd↑];
          
END.