FILE: ModelImpl.mesa
Last edited by Ousterhout, November 22, 1983 3:32 pm
Christian LeCocq April 11, 1986 2:19:41 pm PST
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 REALNEW[REAL ← .00029];
polyCPerArea: PUBLIC REF REALNEW[REAL ← .00004];
metalCPerArea: PUBLIC REF REALNEW[REAL ← .000026];
diffCPerPerim: PUBLIC REF REALNEW[REAL ← .00023];
diffR: PUBLIC REF REALNEW[REAL ← 60];
polyR: PUBLIC REF REALNEW[REAL ← 3];
metalR: PUBLIC REF REALNEW[REAL ← .04];
vdd: PUBLIC REF REALNEW[REAL ← 5.0];
vinv: PUBLIC REF REALNEW[REAL ← 2.5];
rUpFactor: PUBLIC REF REALNEW[REAL ← 1.0];
rDownFactor: PUBLIC REF REALNEW[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"
];
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: BOOLEANFALSE;
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, globalVars];
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.