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