-- Sim-estep.mesa
-- last edited by Suzuki: September 8, 1981 9:38 AM
DIRECTORY
InlineDefs,
JaMFnsDefs USING [GetJaMBreak, SetJaMBreak],
MOSSIMFns USING [UnderJaM],
SimStep,
SimTsim,
stdio,
WF;
SimEstep: PROGRAM
IMPORTS InlineDefs, JaMFnsDefs, MOSSIMFns, SimTsim, stdio, WF
EXPORTS MOSSIMFns, SimStep = { OPEN InlineDefs, SimTsim, stdio, WF;
--#include "tsim.h"
numsteps: CARDINAL ← 0;
warning: BOOLEAN;
warningDisplay: BOOLEAN ← FALSE;
incdbg: CARDINAL ← 1; -- define this symbol IF you want debugging info
-- event driven simulation step for tsim Chris Terman 2/80
-- added timing stuff - cjt - 4/80
-- transTbl[gate][source][drain] RETURNs new value for source node. Use
-- gateTbl to map node potential to one of the three allowable gate
-- 'potentials'. S'posed to tells what effect transistor with the given
-- initial potentials has on source node. Since transistor is symmetrical,
-- just reverse second and third subscripts to get affect on drain node.
-- extern char transTbl[3][NPOTS][NPOTS];
transTblRESIST: POINTER TO ARRAY Potential OF ARRAY Potential OF Potential ← @transTbl[RESIST];
-- extern nptr hinputs[],linputs[],xinputs[];
-- extern int nhinputs,nlinputs,nxinputs;
-- extern int debug;
-- extern char *potential[];
-- initial accumulator value indexed by previous node value
initTbl: ARRAY Potential OF Potential = [ CX, CX, CHIGH, CLOW, CX, CHIGH, CLOW, CHIGH, CX, CX, CX ];
-- translation of potential to effect as a gate
gateTbl: ARRAY Potential OF GateState = [
RESIST, RESIST, ON, OFF, RESIST, ON, OFF, ON, RESIST, RESIST, RESIST
];
-- table for translating potentials into function values
funTbl: ARRAY Potential OF FunctionValue = [ NotFX, NotFX, NotFH, NotFL, NotFX, NotFH, NotFL, NotFH, NotFX, NotFX, NotFX ];
ansTbl: ARRAY FunctionValue OF Potential = [ DLOW, DXLOW, DXLOW, P ];
elist: nptr; -- event list
elast: nptr; -- pointer to last node on event list
clist: nptr; -- list of nodes connected to node being processed
nevent: INTEGER; -- number of current event
-- add a node to event list
--#ifdef incdbg
enque: PROC[m: nptr] = {
IF NOT queued[m] AND NOT (m).input THEN {
IF (debug=1 AND m.watched) THEN
FWF3[stdout,"enquing %s [event %d: %s]*n",
pnode[m],nevent,pnode[elist]];
IF (elast#NIL) THEN { elast.elink ← (m); elast ← (m); }
ELSE { elist ← elast ← (m); }; };
};
xenque: PROC[m: nptr] = {
IF (NOT queued[m]) THEN {
IF (debug=1 AND m.watched) THEN
FWF1[stdout,"xenquing %s*n",pnode[m]];
m.xqueued ← TRUE;
IF (elast#NIL) THEN { elast.elink ← (m); elast ← (m); }
ELSE { elist ← elast ← (m); }; };
};
--#endif
--#ifndef incdbg
--#define enque(m) IF (NOT queued(m) AND NOT ((m).nflags&INPUT)) THEN {\
-- IF (elast) THEN { elast.elink ← (m); elast ← (m); };\
-- ELSE { elist ← elast ← (m); }; };
--
--#define xenque(m) IF (NOT queued(m)) {\
-- m.nflags ← BITOR[m.nflags, XQUEUED];\
-- IF (elast) THEN { elast.elink ← (m); elast ← (m); }\
-- ELSE { elist ← elast ← (m); }; };
--#endif
queued: PROC[m: nptr] RETURNS[BOOLEAN] = INLINE {
RETURN[((m).elink#NIL OR elast=(m))]
};
-- do a simulation step after initializing queue from changed input nodes
step: PUBLIC PROC RETURNS[CARDINAL] =
{ eptr: nptr; -- current node from event list
ac: INTEGER;
newpot: Potential;
v: CARDINAL;
-- initialize event list with all input nodes whose input values
-- differ from their current value.
numsteps ← numsteps + 1;
warning ← FALSE;
elist ← elast ← NIL;
FOR ac IN [0..nxinputs) DO
eptr ← xinputs[ac]; newpot ← initTbl[eptr.npot];
OutTrace[eptr, newpot];
xenque[eptr];
ENDLOOP;
nxinputs ← 0;
FOR ac IN [0..nhinputs) DO
eptr ← hinputs[ac];
IF (eptr.npot # DHIGH) THEN {
OutTrace[eptr, DHIGH];
eptr.fpot ← NotFH;
xenque[eptr];
};
ENDLOOP;
FOR ac IN [0..nlinputs) DO
eptr ← linputs[ac];
IF (eptr.npot # DLOW) THEN {
OutTrace[eptr, DLOW];
eptr.fpot ← NotFL;
xenque[eptr];
};
ENDLOOP;
v ← simStep[];
IF warningDisplay AND warning THEN WF0[";*n"];
RETURN[v];
};
-- IF node is a "storage node", i.e. is not pulled up or an input, initialize
-- its value to CLOW and xenque it (pretend it's an input).
esetup: PROC[n: nptr] RETURNS [CARDINAL] =
{ IF n.npot=CX AND NOT n.pullup AND NOT n.input THEN {
OutTrace[n, CLOW];
n.fpot ← NotFL;
xenque[n];
};
RETURN[0];
};
-- trial network initialization routine
einit: PUBLIC PROC RETURNS[INTEGER] = {
v: CARDINAL;
elist ← elast ← NIL; -- initialize event list
warning ← FALSE;
numsteps ← numsteps + 1;
[] ← walkNet[esetup]; -- initialize and enque all storage nodes
v ← simStep[];
IF warningDisplay AND warning THEN WF0[";*n"];
RETURN[v];
};
-- actual simulation step, assumes event list has some events on it!
simStep: PROC RETURNS[CARDINAL] =
{ ac: Potential; -- potential accumulator
eptr: nptr; -- current node from event list
t: tptr;
n: nptr;
IF traceNumber > 0 THEN FWF0[log, "Simulation step:*n"];
nevent ← 0;
-- start thru event list, doing calculation for each node in turn
WHILE (eptr ← elist)#NIL DO
IF MOSSIMFns.UnderJaM AND JaMFnsDefs.GetJaMBreak[] THEN {
JaMFnsDefs.SetJaMBreak[FALSE];
RETURN[nevent]};
nevent←nevent+1; -- this is the only place where nevent is increased
--#ifdef incdbg
IF (debug=1 AND (nevent>10000 OR eptr.watched)) THEN {
FWF1[stdout,"event %d: ",nevent];
pvalue[eptr];
FWF0[stdout,"*n"];
};
--#endif
-- calculate new value and construct connection list for current node
clist ← LOOPHOLE[LONG[-1], nptr];
ac ← calcVal[eptr,INIT];
-- IF current node is an input, force its value
IF eptr.input THEN ac ← eptr.npot;
--#ifdef incdbg
IF (debug=1 AND eptr.watched) THEN {
FWF0[stdout,"clist: "];
FOR eptr←clist, eptr.nlink WHILE eptr#LOOPHOLE[LONG[-1], nptr] DO
FWF1[stdout,"%s ",pnode[eptr]];
ENDLOOP;
FWF1[stdout,"*nnew value ← %s*n",potential[ac]];
};
--#endif
-- IF new value indicates charge sharing, a second calculation is needed
-- which takes into account the relative capacitances of the involved nodes.
IF (ac = CSHARE) THEN {
cap: REAL;
hac: REAL ← 0.0; -- charged high accumulator
lac: REAL ← 0.0; -- charged low accumulator
xac: REAL ← 0.0; -- other accumulator
FOR eptr ← clist, eptr.nlink WHILE eptr#LOOPHOLE[LONG[-1], nptr] DO
IF ((cap ← eptr.ncap) = 0.0) THEN { ac ← CX; GOTO sDone; };
IF ((ac ← eptr.npot) = CHIGH) THEN hac ← hac + cap
ELSE IF (ac = CLOW) THEN lac ← lac + cap
ELSE xac ← xac + cap;
ENDLOOP;
-- for charge sharing to work, ratio must be at least 3:1 in favor of
-- majority carrier.
IF (lac + xac = 0.0 OR hac/(lac + xac) >= 3.0) THEN ac ← CHIGH
ELSE IF (hac+xac = 0.0 OR lac/(hac+xac) >= 3.0) THEN ac ← CLOW
ELSE ac ← CX;
GOTO sDone;
EXITS
sDone =>
--#ifdef incdbg
IF (debug=1 AND elist.watched) THEN
FWF1[stdout,"charge-shared value ← %s*n",potential[ac]];
--#endif
};
-- save away new value, enquing nodes affected by change (if any)
eptr ← clist;
WHILE eptr#LOOPHOLE[LONG[-1], nptr] DO { -- We need block to enable goto's: N.S.
IF (elist#eptr AND eptr.input) THEN GOTO nextvalue;
FOR t ← eptr.nsource, t.slink WHILE t#NIL DO
IF (gateTbl[t.gate.npot] = RESIST) THEN
IF (transTblRESIST[t.drain.npot][ac] # t.drain.npot) THEN
enque[t.drain];
ENDLOOP;
FOR t ← eptr.ndrain, t.dlink WHILE t#NIL DO
IF (gateTbl[t.gate.npot] = RESIST) THEN
IF (transTblRESIST[t.source.npot][ac] # t.source.npot) THEN
enque[t.source];
ENDLOOP;
IF (gateTbl[ac]#gateTbl[eptr.npot] OR eptr.xqueued) THEN {
IF (gateTbl[ac] = ON) THEN
FOR t ← eptr.ngate, t.glink WHILE t#NIL DO
IF NOT t.drain.input THEN { enque[t.drain]; }
ELSE enque[t.source];
ENDLOOP
ELSE
FOR t ← eptr.ngate, t.glink WHILE t#NIL DO
chain[t.drain];
chain[t.source];
ENDLOOP;
FOR t ← eptr.nfuns, t.vlink WHILE t#NIL DO
enque[t.output]
ENDLOOP;
};
OutTrace[eptr, ac];
eptr.fpot ← funTbl[ac];
eptr.xqueued ← FALSE;
IF (queued[eptr] AND eptr#elist) THEN {
FOR n ← elist, n.elink WHILE n.elink#NIL DO
IF (n.elink = eptr) THEN {
IF ((n.elink ← eptr.elink) = NIL) THEN elast ← n ELSE eptr.elink ← NIL;
EXIT;
};
ENDLOOP;
};
GOTO nextvalue;
EXITS
nextvalue => {
n ← eptr.nlink;
eptr.nlink ← NIL;
eptr ← n};
}
ENDLOOP;
-- move on to next event on event list
eptr ← elist;
elist ← eptr.elink;
eptr.elink ← NIL;
ENDLOOP;
RETURN[nevent];
};
-- calculate function value
calcFun: PROC[n: nptr] RETURNS[Potential] =
{ p: LONG POINTER TO POINTER TO ARRAY OF CHARACTER;
q: LONG POINTER TO ARRAY OF CHARACTER;
temp,ac: FunctionValue;
-- calculate function value, term by term
ac ← FH;
p ← LOOPHOLE[n.ndef, LONG POINTER TO POINTER TO ARRAY OF CHARACTER];
WHILE p↑#NIL DO {
temp ← FH;
WHILE (q ← p↑)#NIL DO p←p+1; temp ← BITAND[temp, BITNOT[q[0]]] ENDLOOP;
IF ((ac ← BITAND[ac, BITNOT[temp]]) = FL) THEN EXIT;
};
ENDLOOP;
RETURN[ansTbl[ac]];
};
-- add current node and all connected to it by 'on' transistors to clist then
-- merge current value of node into accumulator, RETURNing new answer
calcVal: PROC[n: nptr, ac: Potential] RETURNS [Potential] =
{ t: tptr;
pot: Potential;
p: LONG POINTER TO POINTER TO ARRAY OF CHARACTER;
q: LONG POINTER TO ARRAY OF CHARACTER;
fv, temp: FunctionValue;
gate: GateState;
n.nlink ← clist;
clist ← n;
IF n.input THEN pot ← n.npot
ELSE IF (n.ndef#NIL) THEN {
-- calculate function value, term by term
fv ← FH;
p ← LOOPHOLE[n.ndef, LONG POINTER TO POINTER TO ARRAY OF CHARACTER];
WHILE (p↑#NIL) DO {
temp ← FH;
WHILE (q ← p↑)#NIL DO p←p+1; temp ← BITAND[temp, BITNOT[q[0]]] ENDLOOP;
IF ((fv ← BITAND[fv, BITNOT[temp]]) = FL) THEN EXIT;
};
ENDLOOP;
pot ← ansTbl[fv];
} ELSE pot ← (IF n.pullup THEN P ELSE initTbl[n.npot]);
ac ← transTbl[ON][ac][pot];
IF (elist#n AND n.input) THEN RETURN[ac];
FOR t ← n.nsource, t.slink WHILE t#NIL DO
IF ((gate ← gateTbl[t.gate.npot])=ON AND t.drain.nlink=NIL) THEN
ac ← calcVal[t.drain,ac]
ELSE IF (gate = RESIST) THEN ac ← transTblRESIST[ac][t.drain.npot];
ENDLOOP;
FOR t ← n.ndrain, t.dlink WHILE t#NIL DO
IF ((gate ← gateTbl[t.gate.npot])=ON AND t.source.nlink=NIL) THEN
ac ← calcVal[t.source,ac]
ELSE IF (gate = RESIST) THEN ac ← transTblRESIST[ac][t.source.npot];
ENDLOOP;
RETURN[ac];
};
ToggleWarning: PUBLIC PROC = {
warningDisplay ← NOT warningDisplay};
OutTrace: PROC[n: nptr, pot: Potential] = {
IF n.npot = pot THEN RETURN;
IF pchars[n.npot] = pchars[pot] THEN
{n.npot ← pot; RETURN};
n.changecount ← n.changecount+1;
IF warningDisplay AND pchars[pot]#'X THEN {
IF n.stepcount=numsteps THEN {
IF n.lastpot#pot AND NOT n.warned THEN {
IF warning THEN WF1[",%s", pnode[n]]
ELSE {
warning ← TRUE;
WF1["Warning: the following nodes changed more than once: %s", pnode[n]];
n.lastpot ← pot;
n.warned ← TRUE}}}
ELSE {n.stepcount ← numsteps; n.lastpot ← pot; n.warned ← FALSE}};
IF NOT n.traced THEN {n.npot ← pot; RETURN};
FWF3[log, "Potential changed. Node = %s.*nThe old value is %c. The new value is %c*n*n", pnode[n], pchars[n.npot], pchars[pot]];
n.npot ← pot;
};
chain: PROC[n:nptr] =
{ t: tptr;
temp: nptr;
newpot: Potential;
IF (n.input) THEN RETURN;
newpot ← initTbl[n.npot];
OutTrace[n, newpot];
enque[n];
FOR t ← n.nsource, t.slink WHILE t#NIL DO
IF (gateTbl[t.gate.npot]#OFF AND NOT queued[temp ← t.drain]) THEN chain[temp];
ENDLOOP;
FOR t ← n.ndrain, t.dlink WHILE t#NIL DO
IF (gateTbl[t.gate.npot]#OFF AND NOT queued[temp ← t.source]) THEN chain[temp];
ENDLOOP;
};
}.