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