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