DIRECTORY CoreFlat, CoreOps, IO, LinearSystem, Mint, MintList, MintPrivate, PlotGraph, Real, RefTab, Rope, Schedule; MintImpl: CEDAR MONITOR IMPORTS CoreFlat, CoreOps, IO, LinearSystem, PlotGraph, RefTab, Schedule EXPORTS Mint, MintList, MintPrivate SHARES Schedule ~ BEGIN OPEN Mint; plaDetect: INT = 8; freeNodeListCells: NodeList _ NIL; nFreeNodeListCells: CARD _ 0; freeFetListCells: FetList _ NIL; nFreeFetListCells: CARD _ 0; freeSetListCells: SetList _ NIL; nFreeSetListCells: CARD _ 0; Path: TYPE = REF PathRec; PathRec: TYPE = RECORD[node: Node, fet: Fet, refcnt: CARD _ 0, rest: Path]; PathList: TYPE = LIST OF Path; freePaths: Path _ NIL; --holds the first element of the chain of unused PathRec nFreePaths: CARD _ 0; --depth of freePaths (mostly debugging purposes) Delay: TYPE = REF DelayRec; DelayRec: TYPE = RECORD [tup, tdown: ps]; Try: TYPE = REF TryRec; TryRec: TYPE = RECORD [nodesUp, nodesDown: NodeChain _ NIL, rest: Try]; freeTries: Try _ NIL; --holds the first element of the chain of unused TryRec nFreeTries: CARD _ 0; --depth of freeTries (mostly debugging purposes) NodeChain: TYPE = REF NodeChainRec; -- Same as NodeList except for the reference count NodeChainRec: TYPE = RECORD[node: Node, refcnt: CARD _ 0, rest: NodeChain]; freeNodeChains: NodeChain _ NIL; nFreeNodeChains: CARD _ 0; ExtendedCircuit: TYPE = REF ExtendedCircuitRec; ExtendedCircuitRec: TYPE = RECORD [circuit: Circuit, data: REF ANY]; TimedPlot: TYPE = REF TimedPlotRec; TimedPlotRec: TYPE = RECORD [plot: PlotGraph.Plot _ NIL, oldt: ps _ 0.0]; gndName: Rope.ROPE = "public.Gnd"; vddName: Rope.ROPE = "public.Vdd"; VddVal: mVolt = 5000.0; GndVal: mVolt = 0.0; pVtVal: mVolt = -800.0; nVtVal: mVolt = 780.0; threshold: mVolt = 2500.0; gamma: REAL = 0.4*31.62277; -- 31.62277 = SqRt[1000.0] due to unit = mV phi: mVolt = 1000.0; sqrtPhi: REAL = 31.62277; timeSpecs: ps = 5.0; tInc: ps = 100.0; approx: REAL = 1.4; overlapFactor: REAL = 0.5; matrixCacheSize: CARDINAL = 100; totalSets: CARDINAL _ 0; separateView: BOOL _ FALSE; StdOut: PUBLIC IO.STREAM _ NIL; debug: PUBLIC BOOL _ FALSE; verbose: PUBLIC BOOL _ FALSE; statsNumberOfSimulations: CARDINAL _ 0; nSimInc: CARDINAL = 50; SimulateSet: PROC [ref: REF ANY, t: ps, data: REF ANY] ~ { circuit: Circuit ~ NARROW[data]; set: Set ~ NARROW[ref]; agenda: Schedule.Agenda ~ circuit.agenda; subsets: SetList; nodeList: NodeList; finished, firstTime: BOOLEAN _ FALSE; inputHistories: LIST OF Schedule.History; timeList: LIST OF ps; tmin, tmax, tmaxmin, tmaxmax: ps; IF set=NIL THEN RETURN; IF set.done THEN RETURN; set.done _ TRUE; IF debug THEN {IO.PutF[StdOut, "%5d ~~~>",IO.real[t]]; PrintSetType[set.type]; IO.PutF[StdOut, "\nInputs :"];}; nodeList _ ScanHistoriesOfNodeList[set.inList, t, agenda, FALSE]; IF verbose THEN FOR inode: NodeList _ nodeList, inode.rest UNTIL inode=NIL DO IO.PutF[StdOut," Unknown Input: %g \n", IO.rope[RopeFromNode[inode.first, circuit]]] ENDLOOP; FreeNodeList[nodeList]; nodeList _ ScanHistoriesOfNodeList[set.lNodes, t, agenda]; FOR inode: NodeList _ nodeList, inode.rest UNTIL inode=NIL DO inode.first.history _ Schedule.CreateHistory[t, GndVal+0.0314159]; --strange value to trace ENDLOOP; FreeNodeList[nodeList]; firstTime _ nodeList#NIL; FOR inode: NodeList _ set.inList, inode.rest UNTIL inode=NIL DO IF debug THEN PrintNode[inode.first, circuit]; inputHistories _ CONS[inode.first.history, inputHistories]; ENDLOOP; FOR inode: NodeList _ set.fixedV, inode.rest UNTIL inode=NIL DO IF ~inode.first.input THEN ResetInput[inode.first, set] ENDLOOP; FOR inode: NodeList _ set.lNodes, inode.rest UNTIL inode=NIL DO IF inode.first.input THEN SetInput[inode.first, set] ENDLOOP; IF debug THEN IO.PutF[StdOut, "\nOutputs:\n"]; timeList _ Schedule.Schedule[inputHistories]; IF timeList=NIL THEN timeList _ LIST[t]; tmin _ t; UNTIL timeList.first>=tmin OR timeList.rest=NIL DO timeList _ timeList.rest; ENDLOOP; tmaxmin _ timeList.first; IF set.type.solve#NIL THEN tmaxmin _ set.type.solve[set, tmin, circuit] ELSE UNTIL finished DO [finished , tmin] _ FlipSwitches[set.lFets, tmin, tmaxmin]; IF firstTime THEN finished _ FALSE; firstTime _ FALSE; IF ~finished THEN { tmaxmax _ tmaxmin; subsets _ ClipSet[set]; FOR isubset: SetList _ subsets, isubset.rest UNTIL isubset=NIL DO tmax _ RCSolve[isubset.first, tmin, circuit]; tmaxmin _ MIN[tmaxmin, tmax]; tmaxmax _ MAX[tmaxmax, tmax]; IF debug AND verbose THEN { IO.PutF[StdOut, "\nsubset between %g and %g", IO.real[tmin], IO.real[tmax]]; PrintNodeList[isubset.first.lNodes, circuit]; }; ENDLOOP; FreeSetList[subsets]; } ELSE { tmin _ tmaxmin; UNTIL timeList.first>tmin OR timeList.rest=NIL DO timeList _ timeList.rest; ENDLOOP; IF MAX[tmaxmax, timeList.first]> tmaxmin THEN finished _ FALSE; tmaxmin _ IF timeList.first>tmaxmin THEN timeList.first ELSE tmaxmax; }; ENDLOOP; WriteAgendaItems[set.lNodes, agenda, t]; circuit.info.nbOfSimulations _ circuit.info.nbOfSimulations+1; IF verbose THEN IF (circuit.info.nbOfSimulations MOD nSimInc) = 0 THEN IO.PutF[StdOut, "%5d t : %5d, No of events : %7d\n", IO.int[circuit.info.nbOfSimulations], IO.real[t], IO.int[agenda.nbOfEvents]]; }; WriteAgendaItems: PROC [nodeList: NodeList, agenda: Schedule.Agenda, t: ps] ~ { FOR inode: NodeList _ nodeList, inode.rest UNTIL inode=NIL DO nextNode: Node ~ inode.first; FOR setList: SetList _ nextNode.setList, setList.rest UNTIL setList=NIL DO nextSet: Set ~ setList.first; nextT: ps _ Schedule.NextTimeOfHistory[nextNode.history, t]; IF nextT>t THEN Schedule.InsertInAgenda[agenda, nextSet, nextT]; nextSet.done _ FALSE; ENDLOOP; ENDLOOP; }; ScanHistoriesOfNodeList: PROC [nodeList: NodeList, t: ps, agenda: Schedule.Agenda, cut: BOOLEAN _ TRUE] RETURNS [uninitialized: NodeList] ~ { CutHistory: PROC[node: Node] RETURNS [quit: BOOLEAN _ FALSE] ~ { IF node.history=NIL THEN RETURN; IF node.input THEN RETURN; t1 _ Schedule.NextTimeOfHistory[node.history, t+tInc]; IF t1>t AND Schedule.LastTimeOfHistory[node.history]>t1 THEN { node.history _ Schedule.AddToHistory[node.history, t1, Schedule.VFromHistory[node.history, t1]]; FOR iSetList: SetList _ node.setList, iSetList.rest UNTIL iSetList=NIL DO Schedule.InsertInAgenda[agenda, iSetList.first, t1]; iSetList.first.done _ FALSE; FOR inodeList: NodeList _ iSetList.first.lNodes, inodeList.rest UNTIL inodeList=NIL DO quit _ CutHistory[inodeList.first]; ENDLOOP; ENDLOOP; }; }; t1: ps _ t+tInc; FOR inode: NodeList _ nodeList, inode.rest UNTIL inode=NIL DO IF inode.first.history=NIL THEN { uninitialized _ NewNodeListCell[inode.first, uninitialized]; } ELSE { IF ~inode.first.watched THEN inode.first.history _ Schedule.ForgetBeginings[inode.first.history, t]; IF cut THEN [] _ CutHistory[inode.first]; }; ENDLOOP; }; NewSetListCell: ENTRY PROC [prevSetList: SetList _ NIL] RETURNS [newSetList: SetList] ~ { ENABLE UNWIND => NULL; IF freeSetListCells=NIL THEN newSetList _ CONS[NEW[SetRec], prevSetList] ELSE { newSetList _ freeSetListCells; freeSetListCells _ freeSetListCells.rest; newSetList.rest _ prevSetList; newSetList.first^ _ []; nFreeSetListCells _ nFreeSetListCells-1; }; }; FreeSetListCell: ENTRY PROC [setList: SetList] ~ { ENABLE UNWIND => NULL; setList.rest _ freeSetListCells; --we keep the set too freeSetListCells _ setList; nFreeSetListCells _ nFreeSetListCells+1; }; FreeSetList: PROC [setList: SetList] ~ { UNTIL setList=NIL DO sList: SetList _ setList; FreeFetList[setList.first.lFets]; FreeNodeList[setList.first.lNodes]; FreeNodeList[setList.first.inList]; FreeNodeList[setList.first.fixedV]; setList _ setList.rest; FreeSetListCell[sList]; ENDLOOP; }; NewNodeListCell: ENTRY PROC [node: Node _ NIL, prevNodeList: NodeList _ NIL] RETURNS [newNodeList: NodeList] ~ { ENABLE UNWIND => NULL; IF freeNodeListCells=NIL THEN newNodeList _ CONS[node, prevNodeList] ELSE { newNodeList _ freeNodeListCells; freeNodeListCells _ freeNodeListCells.rest; newNodeList^ _ [node, prevNodeList]; nFreeNodeListCells _ nFreeNodeListCells-1; }; }; FreeNodeListCell: ENTRY PROC [nodeList: NodeList] ~ { ENABLE UNWIND => NULL; nodeList^ _ [NIL, freeNodeListCells]; freeNodeListCells _ nodeList; nFreeNodeListCells _ nFreeNodeListCells+1; }; FreeNodeList: PROC [nodeList: NodeList] ~ { UNTIL nodeList=NIL DO nList: NodeList _ nodeList; nodeList _ nodeList.rest; FreeNodeListCell[nList]; ENDLOOP; }; NextNodeList2: PROC [fetList: FetList] RETURNS [nodeList: NodeList _ NIL] ~ { FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList = NIL DO IF NOT iFetList.first.ch1.done THEN { nodeList _ NewNodeListCell[iFetList.first.ch1, nodeList]; iFetList.first.ch1.done _ TRUE; }; IF NOT iFetList.first.ch2.done THEN { nodeList _ NewNodeListCell[iFetList.first.ch2, nodeList]; iFetList.first.ch2.done _ TRUE; }; ENDLOOP; }; --NextNodeList2 IsNodeInList: PUBLIC PROC [node: Node, nodeList: NodeList] RETURNS [found: BOOLEAN _ FALSE]~ { FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO IF inodeList.first=node THEN RETURN [TRUE]; ENDLOOP; }; AppendNodeLists: PUBLIC PROC [top, bottom: NodeList] RETURNS [nodeList: NodeList _ NIL] ~ { IF top=NIL THEN nodeList _ bottom ELSE FOR iNodeList: NodeList _ top, iNodeList.rest DO IF iNodeList.rest = NIL THEN { nodeList _ top; iNodeList.rest _ bottom; RETURN }; ENDLOOP; }; NewFetListCell: ENTRY PROC [fet: Fet _ NIL, prevFetList: FetList _ NIL] RETURNS [newFetList: FetList] ~ { ENABLE UNWIND => NULL; IF freeFetListCells=NIL THEN newFetList _ CONS[fet, prevFetList] ELSE { newFetList _ freeFetListCells; freeFetListCells _ freeFetListCells.rest; newFetList^ _ [fet, prevFetList]; nFreeFetListCells _ nFreeFetListCells-1; }; }; FreeFetListCell: ENTRY PROC [fetList: FetList] ~ { ENABLE UNWIND => NULL; fetList^ _ [NIL, freeFetListCells]; freeFetListCells _ fetList; nFreeFetListCells _ nFreeFetListCells+1; }; FreeFetList: PROC [fetList: FetList] ~ { UNTIL fetList=NIL DO fList: FetList _ fetList; fetList _ fetList.rest; FreeFetListCell[fList]; ENDLOOP; }; NextFetList2: PROC [nodeList: NodeList] RETURNS [fetList: FetList _ NIL] ~ { FOR iNodeList: NodeList _ nodeList, iNodeList.rest UNTIL iNodeList = NIL DO FOR i: NAT IN [0..iNodeList.first.fetSeq.nUsed) DO fet: Fet = iNodeList.first.fetSeq[i]; IF (NOT fet.done) AND (fet.gate#iNodeList.first) AND NOT (fet.directional AND fet.ch2=iNodeList.first) THEN { fetList _ NewFetListCell[fet, fetList]; fet.done _ TRUE; }; ENDLOOP; ENDLOOP; }; --NextFetList2 AppendFetLists: PUBLIC PROC [top, bottom: FetList] RETURNS [fetList: FetList _ NIL] ~ { IF top=NIL THEN fetList _ bottom ELSE FOR iFetList: FetList _ top, iFetList.rest DO IF iFetList.rest = NIL THEN { fetList _ top; iFetList.rest _ bottom; RETURN }; ENDLOOP; }; ClipSet: PROC [totalSet: Set] RETURNS [setList: SetList _ NIL] ~ { PrepareToClipSet[totalSet]; FOR inodeList: NodeList _ totalSet.lNodes, inodeList.rest UNTIL inodeList=NIL DO set: Set; IF inodeList.first.done THEN LOOP; setList _ NewSetListCell[setList]; set _ setList.first; FillSet[set, inodeList.first]; FindFixedV[set, totalSet.fixedV]; ENDLOOP; }; PrepareToClipSet: PROC [totalSet: Set] ~ { FOR iFetList: FetList _ totalSet.lFets, iFetList.rest UNTIL iFetList = NIL DO iFetList.first.done _ ~iFetList.first.switch; ENDLOOP; FOR iNodeList: NodeList _ totalSet.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ FALSE; ENDLOOP; FOR iNodeList: NodeList _ totalSet.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ TRUE; ENDLOOP; }; FillSet: PROC [set: Set, rootNode: Node] ~ { nodeList: NodeList; nodeList _ NewNodeListCell[rootNode]; set.lNodes _ nodeList; rootNode.done _ TRUE; UNTIL nodeList=NIL DO fetList: FetList _ NextFetList2[nodeList]; set.lFets _ AppendFetLists[set.lFets, fetList]; nodeList _ NextNodeList2[fetList]; set.lNodes _ AppendNodeLists[set.lNodes, nodeList]; ENDLOOP; }; FindFixedV: PROC [set: Set, fixedV: NodeList] ~ { FOR iNodeList: NodeList _ fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ FALSE; ENDLOOP; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO ch1: Node _ iFetList.first.ch1; ch2: Node _ iFetList.first.ch2; IF ch1.input THEN IF ~ch1.done AND iFetList.first.switch THEN { ch1.done _ TRUE; set.fixedV _ NewNodeListCell[ch1, set.fixedV]; }; IF ch2.input THEN IF ~ch2.done AND iFetList.first.switch THEN { ch2.done _ TRUE; set.fixedV _ NewNodeListCell[ch2, set.fixedV]; }; ENDLOOP; FOR iNodeList: NodeList _ fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ TRUE; ENDLOOP; }; FLStats: PROC RETURNS [n, f, s, p, t, nc: CARD] ~ { n _ nFreeNodeListCells; f _ nFreeFetListCells; s _ nFreeSetListCells; p _ nFreePaths; t _ nFreeTries; nc _ nFreeNodeChains; }; CheckSwitch: PROC [fet: Fet, t0, t1: ps] RETURNS [change: BOOLEAN _ FALSE, changet: ps _ 0] ~ { status: BOOLEAN; vGate1: mVolt _ Schedule.VFromHistory[fet.gate.history, t1]; SELECT fet.type.type FROM nE => status _ (vGate1 >= threshold); pE => status _ (vGate1 <= threshold); ENDCASE => ERROR; IF status#fet.switch THEN { UNTIL t1-t0matrixCacheSize THEN RETURN[LinearSystem.Copy[matrix]]; FOR i: CARDINAL IN [0..n) DO TRUSTED { subij: LONG POINTER TO REAL _ @circuit.private.subMatrix[i][0]; limitIJ: LONG POINTER TO REAL _ @circuit.private.subMatrix[i][n-1]; matrixij: LONG POINTER TO REAL _ @matrix[i][0]; DO subij^ _ matrixij^; IF subij = limitIJ THEN EXIT; subij _ subij + SIZE[REAL]; matrixij _ matrixij + SIZE[REAL]; ENDLOOP; }; ENDLOOP; RETURN[circuit.private.subMatrix]; }; MySolveN: PROC [matrix: LinearSystem.MatrixN, cstTerm: LinearSystem.ColumnN, nActiveNodes: CARDINAL, circuit: Circuit] RETURNS [vFinal: LinearSystem.ColumnN] ~ { IF nActiveNodes>matrixCacheSize THEN RETURN[LinearSystem.SolveN[LinearSystem.Copy[matrix], cstTerm, nActiveNodes]]; FOR i: CARDINAL IN [0..nActiveNodes) DO TRUSTED { copyij: LONG POINTER TO REAL _ @circuit.private.copyMatrix[i][0]; limitIJ: LONG POINTER TO REAL _ @circuit.private.copyMatrix[i][nActiveNodes-1]; matrixij: LONG POINTER TO REAL _ @matrix[i][0]; DO copyij^ _ matrixij^; IF copyij = limitIJ THEN EXIT; copyij _ copyij + SIZE[REAL]; matrixij _ matrixij + SIZE[REAL]; ENDLOOP; }; ENDLOOP; RETURN[LinearSystem.SolveN[circuit.private.copyMatrix, cstTerm, nActiveNodes]] }; RCSolve: PROC [set: Set, t: ps, circuit: Circuit] RETURNS [nextTime: ps] ~ { vFinal: LinearSystem.ColumnN; tFinal: ps; FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.exactValue _ TRUE; IF iNodeList.first.history#NIL THEN iNodeList.first.history _ Schedule.AddToHistory[iNodeList.first.history, t, Schedule.VFromHistory[iNodeList.first.history, t]]; ENDLOOP; [tFinal, vFinal] _ RCSolveInner[set, t, circuit]; IF vFinal=NIL THEN RETURN [t]; nextTime _ t+tFinal; FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.history _ Schedule.AddToHistory[iNodeList.first.history, nextTime, vFinal[iNodeList.first.index]]; IF debug THEN IO.PutF[StdOut," %g : %dmV @ %dps\n", IO.rope[RopeFromNode[iNodeList.first, circuit]], IO.real[vFinal[iNodeList.first.index]], IO.real[nextTime]]; ENDLOOP; }; RCSolveInner: PROC [set: Set, t: ps, circuit: Circuit] RETURNS [tFinal: ps _ 0.0, vFinal: LinearSystem.ColumnN] ~ { matrix, subMatrix: LinearSystem.MatrixN; cstTerm, vInit, tau, vFinal2: LinearSystem.ColumnN; halfVddVal: REAL _ VddVal/2.0; triState: BOOLEAN _ TRUE; nNodes, nActiveNodes, lastAct: CARDINAL _ 0; IF set.fixedV=NIL THEN RETURN[0.0, NIL]; -- this cell is isolated (triState). FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.index _ nNodes; nNodes _ nNodes+1; ENDLOOP; nActiveNodes _ nNodes; FOR iNodeList: NodeList _ set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.index _ nNodes; nNodes _ nNodes+1; ENDLOOP; IF nNodes>matrixCacheSize THEN { matrix _ NEW[LinearSystem.MatrixSeq[nNodes]]; cstTerm _ NEW[LinearSystem.VecSeq[nNodes]]; vInit _ NEW[LinearSystem.VecSeq[nNodes]]; tau _ NEW[LinearSystem.VecSeq[nNodes]]; } ELSE { matrix _ circuit.private.matrix; cstTerm _ circuit.private.cstTerm; vInit _ circuit.private.vInit; tau _ circuit.private.tau; }; FOR iNodeList: NodeList _ set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO vInit[iNodeList.first.index] _ Schedule.VFromHistory[iNodeList.first.history, t]; ENDLOOP; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO iFetList.first.done _ FALSE; ENDLOOP; FOR iRow: CARDINAL IN [0..nNodes) DO IF matrix[iRow]=NIL THEN matrix[iRow] _ NEW[LinearSystem.VecSeq[nNodes]]; cstTerm[iRow] _ 0.0; TRUSTED { limitIJ: LONG POINTER TO REAL _ @matrix[iRow][nNodes-1]; matrixij: LONG POINTER TO REAL _ @matrix[iRow][0]; DO matrixij^ _ 0.0; IF matrixij = limitIJ THEN EXIT; matrixij _ matrixij + SIZE[REAL]; ENDLOOP; }; ENDLOOP; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO IF ~iFetList.first.done AND iFetList.first.switch THEN Coeff[matrix, iFetList.first]; ENDLOOP; FOR i: CARDINAL IN [nActiveNodes..nNodes) DO IF matrix[i][i]#0.0 THEN { triState _ FALSE; EXIT; }; ENDLOOP; IF triState THEN ERROR; FOR i: CARDINAL IN [0..nActiveNodes) DO FOR j: CARDINAL IN [nActiveNodes..nNodes) DO cstTerm[i] _ cstTerm[i] - matrix[i][j]*vInit[j]; ENDLOOP; ENDLOOP; IF nNodes=nActiveNodes+1 THEN { vFinal _ NEW[LinearSystem.VecSeq[nActiveNodes]]; FOR i: CARDINAL IN [0..nActiveNodes) DO vFinal[i] _ vInit[nActiveNodes]; ENDLOOP; } ELSE vFinal _ MySolveN[matrix, cstTerm, nActiveNodes, circuit]; lastAct _ nActiveNodes-1; IF lastAct#0 THEN FOR i: CARDINAL IN [0..nActiveNodes) DO vTemp: ps _ IF vFinal[i]>halfVddVal THEN GndVal ELSE VddVal; subMatrix _ XchIndexes[matrix, i, lastAct, nNodes, circuit]; vInit[lastAct] _ vTemp; --this node becomes a V source FOR k: CARDINAL IN [0..lastAct) DO cstTerm[k] _ 0.0; FOR j: CARDINAL IN [lastAct..nNodes) DO cstTerm[k] _ cstTerm[k] - subMatrix[k][j]*vInit[j]; ENDLOOP; ENDLOOP; vFinal2 _ MySolveN[subMatrix, cstTerm, lastAct, circuit]; cstTerm[lastAct] _ 0.0; FOR j: CARDINAL IN [0..lastAct) DO cstTerm[lastAct] _ cstTerm[lastAct] + subMatrix[lastAct][j]*(vTemp-vFinal2[j]); ENDLOOP; FOR j: CARDINAL IN (lastAct..nNodes) DO cstTerm[lastAct] _ cstTerm[lastAct] + subMatrix[lastAct][j]*(vTemp-vInit[j]); ENDLOOP; tau[i] _ (vTemp-vFinal[i])/cstTerm[lastAct]; tFinal _ tFinal+tau[i]; ENDLOOP ELSE { tau[0] _ -1.0/matrix[0][0]; tFinal _ tau[0] }; tFinal _ approx*tFinal; --performs the estimation of the pt where v[t] = vFinal }; Initialize: PROC [circuit: Circuit] RETURNS [t: ps _ 1e30] ~ { SetEvent: RefTab.EachPairAction ~ { node: Node _ NARROW[val]; t0: ps; IF ~node.input THEN RETURN; t0 _ Schedule.FirstTimeOfHistory[node.history]; t _ MIN[t, t0]; FOR setList: SetList _ node.setList, setList.rest UNTIL setList=NIL DO Schedule.InsertInAgenda[circuit.agenda, setList.first, t0]; ENDLOOP; }; Schedule.InsertInAgenda[circuit.agenda, NIL, -1e30]; [] _ RefTab.Pairs[circuit.nodeTable, SetEvent]; [] _ VisitLibrary[circuit.library, ResetSet]; SetTime[t, circuit]; }; CircuitSimulate: PUBLIC PROC[circuit: Circuit, from: ps _ 0.0] RETURNS [t: ps] ~{ gndNode, vddNode: Node; lastNodes: NodeList; circuit.info.nbOfSimulations _ 0; gndNode _ NodeFromRope[gndName, circuit]; gndNode.history _ Schedule.CreateHistory[from, GndVal]; vddNode _ NodeFromRope[vddName, circuit]; vddNode.history _ Schedule.CreateHistory[from, VddVal]; circuit.agenda _ Schedule.CreateAgenda[SimulateSet, circuit]; t _ Initialize[circuit]; t _ Schedule.ExecuteAgenda[circuit.agenda]; IO.PutF[StdOut, "\nfinished after %d set simulations at %d ps(%g)\n", IO.int[circuit.info.nbOfSimulations], IO.real[t], IO.rope[RopeFromNode[lastNodes.first, circuit]]]; }; -- CircuitSimulate SimulateAndPlot: PROC [ref: REF ANY, t: ps, data: REF ANY] ~ { tPlot: TimedPlot ~ NARROW[data]; deltat: ps _ t-tPlot.oldt; rect: PlotGraph.Rectangle _ [tPlot.oldt-deltat, GndVal, MAX[4*deltat, 1.0], VddVal-GndVal]; SimulateSet[ref, t, tPlot.plot.data]; PlotGraph.RefreshPlot[plot: tPlot.plot, within: rect, eraseFirst: FALSE]; tPlot.oldt _ t; }; InteractiveSimulate: PUBLIC PROC [circuit: Circuit, watchList: NodeList _ NIL, from: ps _ 0.0] ~ { SearchWatched: RefTab.EachPairAction ~ { node: Node _ NARROW[val]; IF node.watched THEN watchList _ CONS[node, watchList]; }; t: ps; gndNode, vddNode: Node; tPlot: TimedPlot _ NEW[TimedPlotRec]; circuit.info.nbOfSimulations _ 0; gndNode _ NodeFromRope[gndName, circuit]; gndNode.history _ Schedule.CreateHistory[from, GndVal]; vddNode _ NodeFromRope[vddName, circuit]; vddNode.history _ Schedule.CreateHistory[from, VddVal]; IF watchList=NIL THEN [] _ RefTab.Pairs[circuit.nodeTable, SearchWatched]; tPlot.plot _ DrawNodeList[watchList, circuit]; circuit.agenda _ Schedule.CreateAgenda[SimulateAndPlot, tPlot]; t _ Initialize[circuit]; t _ Schedule.ExecuteAgenda[circuit.agenda]; }; QuickClip: PROC [set: Set] RETURNS [subSets: SetList] ~ { QuickSetFetList[set.lFets]; subSets _ ClipSet[set]; }; NextNodesOfSet: PROC [node: Node, set: Set, prevList: NodeList _ NIL] RETURNS [nextOnes: NodeList] ~ { IsInputOf: PROC [set: Set, inputNode: Node] RETURNS [isInput: BOOLEAN _ FALSE] ~ { FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO fet _ iFetList.first; IF fet.gate=inputNode THEN RETURN[TRUE]; ENDLOOP; }; fet: Fet; subSets: SetList _ QuickClip[set]; nextOnes _ prevList; FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO subSet: Set _ iSetList.first; IF IsInputOf[subSet, node] THEN { IF fet.directional THEN { --keep only the ch2 side of subset subSubSets: SetList _ NewSetListCell[NIL]; subSubSet: Set _ subSubSets.first; PrepareToClipSet[subSet]; FillSet[subSubSet, fet.ch2]; subSubSets.first _ subSet; iSetList.first _ subSubSet; subSet _ subSubSet; FreeSetList[subSubSets]; }; FOR iNodeList: NodeList _ subSet.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO nextNode: Node ~ iNodeList.first; IF nextNode.ignoreMe THEN LOOP; IF nextNode.watched OR nextNode.setList#NIL THEN nextOnes _ NewNodeListCell[nextNode, nextOnes]; ENDLOOP; }; ENDLOOP; FreeSetList[subSets]; }; CheckForUnknownInputs: PROC [set: Set] RETURNS [unknown: BOOLEAN _ FALSE] ~ { FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO IF NOT iFetList.first.gate.exactValue THEN RETURN[TRUE]; ENDLOOP; }; CheckMultiplePowers: PROC [set: Set] RETURNS [multiple: BOOLEAN _ TRUE] ~ { IF set.fixedV#NIL THEN IF set.fixedV.rest=NIL THEN RETURN[FALSE]; }; SimulateOrRegister: PROC [ref: REF ANY, t: ps, data: REF ANY] ~ { set: Set ~ NARROW[ref]; extdCirc: ExtendedCircuit _ NARROW[data]; startTable: RefTab.Ref _ NARROW[extdCirc.data]; IF CheckForUnknownInputs[set] THEN { subSets: SetList _ QuickClip[set]; FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO hasAnOutput: BOOLEAN _ FALSE; subSet: Set _ iSetList.first; FOR iNodeList: NodeList _ subSet.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO node: Node ~ iNodeList.first; IF node.setList#NIL OR node.watched THEN {--is node an output ? hasAnOutput _ TRUE; EXIT; }; ENDLOOP; IF hasAnOutput AND CheckMultiplePowers[subSet] THEN { [] _ RefTab.Store[startTable, set, $Start]; RETURN; }; ENDLOOP; }; [] _ RefTab.Delete[startTable, set]; --in case we registered it too early SimulateSet[ref, t, extdCirc.circuit]; }; GetTupTdown: PROC [node: Node] RETURNS [tup, tdown: ps] ~ { IF node.history=NIL OR node.exactValue THEN RETURN[0.0, 0.0]; SELECT node.history.first.v FROM VddVal => { tup _ node.history.first.t; tdown _ node.history.rest.first.t; }; GndVal => { tup _ node.history.rest.first.t; tdown _ node.history.first.t; }; ENDCASE => ERROR; }; PutTupTdown: PROC [node: Node, tup, tdown: ps] ~ { IF tupnodeTup AND CheckNoCircularities[node, prevNode, TRUE] THEN { valid _ TRUE; nodeTup _ tup; node.prevNodeU _ prevNode; }; IF tdown>nodeTdown AND CheckNoCircularities[node, prevNode, FALSE] THEN { valid _ TRUE; nodeTdown _ tdown; node.prevNodeD _ prevNode; }; IF valid THEN PutTupTdown[node, nodeTup, nodeTdown]; }; RecordPaths: PUBLIC PROC [circuit: Circuit, clkList: NodeList] RETURNS [worst: ps _ 0.0]~ { EraseHistory: RefTab.EachPairAction ~ { node: Node _ NARROW[val]; FOR inodeList: NodeList _ clkList, inodeList.rest UNTIL inodeList=NIL DO IF inodeList.first=node THEN RETURN; ENDLOOP; node.history _ NIL; IF ~node.input THEN RETURN; FOR setList: SetList _ node.setList, setList.rest UNTIL setList=NIL DO Schedule.InsertInAgenda[circuit.agenda, setList.first, from]; ENDLOOP; }; CreateStart: RefTab.EachPairAction ~ { inputNode: Node; set: Set _ NARROW[key]; nextOnes: NodeList; FOR inList: NodeList _ set.inList, inList.rest UNTIL inList=NIL DO IF inList.first.exactValue OR inList.first.input THEN {inputNode _ inList.first; EXIT}; ENDLOOP; nextOnes _ NextNodesOfSet[inputNode, set]; inputNode.prevNodeU _ NIL; inputNode.prevNodeD _ NIL; FOR iNodeList: NodeList _ nextOnes, iNodeList.rest UNTIL iNodeList=NIL DO [] _ RecordOrForget[iNodeList.first, inputNode, set]; liveBorder _ NewNodeListCell[iNodeList.first, liveBorder]; alreadyIn _ alreadyIn+1; ENDLOOP; FreeNodeList[nextOnes]; }; RecordOrForget: PROC [newNode, prevNode: Node, set: Set] RETURNS [valid: BOOLEAN] ~ { length, prevTup, prevTdown, tup, tdown: ps; delay: Delay _ DelayOfSet[set: set, from: prevNode, to: newNode, setCache: setCache, circuit: circuit]; IF delay.tup=0.0 OR delay.tdown=0.0 THEN RETURN[FALSE]; --switching the input has no effect [prevTup, prevTdown] _ GetTupTdown[prevNode]; tup _ overlapFactor*delay.tup+prevTdown; tdown _ overlapFactor*delay.tdown+prevTup; valid _ UpdateNode[newNode, prevNode, tup, tdown]; IF valid THEN { length _ MAX[tup, tdown]; IF length>worst THEN worst _ length; }; nTotal _ nTotal+1; }; level, nTotal, alreadyIn: CARD _ 0; from, nthTime: REAL _ 0.0; found, finished: BOOLEAN _ FALSE; gndNode: Node = NodeFromRope[gndName, circuit]; vddNode: Node = NodeFromRope[vddName, circuit]; startTable: RefTab.Ref _ RefTab.Create[]; liveBorder: NodeList; extdCirc: ExtendedCircuit _ NEW[ExtendedCircuitRec _ [circuit, startTable]]; setCache: RefTab.Ref _ RefTab.Create[]; delayCache: RefTab.Ref _ RefTab.Create[]; circuit.info.nbOfSimulations _ 0; circuit.agenda _ Schedule.CreateAgenda[SimulateOrRegister, extdCirc]; [] _ RefTab.Pairs[circuit.nodeTable, EraseHistory]; SetNode[gndNode, FALSE]; SetNode[vddNode, TRUE]; [] _ VisitLibrary[circuit.library, ResetSet]; FOR inode: NodeList _ clkList, inode.rest UNTIL inode=NIL DO IF inode.first.history=NIL THEN SetNode[inode.first, TRUE]; FOR setList: SetList _ inode.first.setList, setList.rest UNTIL setList=NIL DO Schedule.InsertInAgenda[circuit.agenda, setList.first, from]; ENDLOOP; ENDLOOP; [] _ Schedule.ExecuteAgenda[circuit.agenda]; [] _ RefTab.Pairs[startTable, CreateStart]; IF verbose THEN IO.PutF[StdOut, "level: 0, starting from: %d, worst: %d\n", IO.int[alreadyIn], IO.real[worst/1000.0]]; UNTIL liveBorder=NIL DO n2: INT _ 0; oldBorder: NodeList _ liveBorder; liveBorder _ NIL; level _ level+1; FOR border: NodeList _ oldBorder, border.rest UNTIL border=NIL DO thisNode: Node _ border.first; FOR iSList: SetList _ thisNode.setList, iSList.rest UNTIL iSList=NIL DO set: Set ~ iSList.first; nextOnes: NodeList _ NextNodesOfSet[thisNode, set]; FOR iNext: NodeList _ nextOnes, iNext.rest UNTIL iNext=NIL DO newNode: Node _ iNext.first; IF RecordOrForget[newNode: newNode, prevNode: thisNode, set: set] THEN { liveBorder _ NewNodeListCell[newNode, liveBorder]; n2 _ n2+1; }; ENDLOOP; FreeNodeList[nextOnes]; ENDLOOP; ENDLOOP; FreeNodeList[oldBorder]; IF verbose THEN IO.PutF[StdOut, "level: %d, total %d, in use %d, worst: %d\n", IO.int[level], IO.int[nTotal], IO.int[n2], IO.real[worst/1000.0]] ENDLOOP; }; DelayOfSet: PROC [set: Set, from, to: Node, setCache: RefTab.Ref, circuit: Circuit] RETURNS [delay: Delay] ~ { val: RefTab.Val; found: BOOLEAN; [found, val] _ RefTab.Fetch[setCache, set]; IF found THEN RETURN[NARROW[val, Delay]]; FOR iNList: NodeList _ set.inList, iNList.rest UNTIL iNList=NIL DO iNList.first.done _ iNList.first.exactValue; ENDLOOP; FOR iNList: NodeList _ set.fixedV, iNList.rest UNTIL iNList=NIL DO iNList.first.done _ TRUE; ENDLOOP; delay _ IF set.type.nInput>plaDetect OR set.selfInputs THEN QuickDelayOfSet[set, from, to, circuit] ELSE TrueDelayOfSet[set, from, to, circuit]; [] _ RefTab.Store[setCache, set, delay]; }; QuickDelayOfSet: PROC [set: Set, from, to: Node, circuit: Circuit] RETURNS [delay: Delay] ~ { pathList: PathList _ FindAllPaths[to, set]; delay _ NEW[DelayRec _ [0.0, 0.0]]; IF verbose THEN {IO.PutRope[StdOut, "Quick delay on "]; PrintSetType[set.type]; IO.PutRope[StdOut, " ***\n"];}; FOR iPathList: PathList _ pathList, iPathList.rest UNTIL iPathList=NIL DO thisPath: Path _ iPathList.first; endNode: Node _ thisPath.node; IF endNode.exactValue THEN { logicalValue: BOOLEAN _ GetNode[endNode]; t: ps _ EvalPath[thisPath]; IF logicalValue THEN delay.tup _ MAX[delay.tup, t] ELSE delay.tdown _ MAX[delay.tdown, t]; }; ENDLOOP; EmptyPathList[pathList]; }; TrueDelayOfSet: PROC [set: Set, from, to: Node, circuit: Circuit] RETURNS [delay: Delay] ~ { nextTryList: Try _ NewTry[]; freeInputs: NodeList _ set.inList; nTries: CARD _ 0; delay _ NEW[DelayRec _ [0.0, 0.0]]; WHILE nextTryList#NIL DO nextInput: Node; lastRound: BOOLEAN; currentTryList: Try _ nextTryList; WHILE freeInputs#NIL AND nextInput=NIL DO IF NOT freeInputs.first.done THEN nextInput _ freeInputs.first; freeInputs _ freeInputs.rest; ENDLOOP; lastRound _ nextInput=NIL; nextTryList _ NIL; WHILE currentTryList#NIL DO thisTry: Try _ currentTryList; subSets: SetList _ NewSetListCell[NIL]; subSet: Set _ subSets.first; tup, tdown: ps _ 0.0; quit, found: BOOLEAN _ FALSE; nTries _ nTries+1; SetFetsToTry[set.lFets, thisTry]; PrepareToClipSet[set]; FillSet[subSet, to]; FindFixedV[subSet, set.fixedV]; [tup, tdown, quit] _ IF lastRound THEN FullEvalRC[to, subSet, circuit] ELSE FastEvalRC[to, subSet, circuit]; IF NOT quit THEN { nextTryList _ ExtendCurrentTry[thisTry, nextInput, nextTryList]; } ELSE { EmptyNodeChain[thisTry.nodesUp]; EmptyNodeChain[thisTry.nodesDown]; }; delay.tup _ MAX[delay.tup, tup]; delay.tdown _ MAX[delay.tdown, tdown]; currentTryList _ currentTryList.rest; FreeTry[thisTry]; FreeSetList[subSets]; ENDLOOP; ENDLOOP; }; ExtendCurrentTry: PROC [try: Try, node: Node, prevTryList: Try] RETURNS [tryList: Try] ~ { tryList _ NewTry[NewNodeChainCell[node, try.nodesUp], try.nodesDown, prevTryList]; tryList _ NewTry[try.nodesUp, NewNodeChainCell[node, try.nodesDown], tryList]; }; SetFetsToTry: PROC [fetList: FetList, try: Try] ~ { FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList = NIL DO thisFet: Fet _ iFetList.first; IF thisFet.gate.done THEN QuickSetFet[thisFet] ELSE thisFet.switch _ SELECT TRUE FROM IsNodeInChain[thisFet.gate, try.nodesUp] => thisFet.type.type=nE, IsNodeInChain[thisFet.gate, try.nodesDown] => thisFet.type.type=pE, ENDCASE => TRUE; ENDLOOP; }; IsNodeInChain: PROC [node: Node, nodeChain: NodeChain] RETURNS [found: BOOLEAN _ FALSE] ~ { FOR iNC: NodeChain _ nodeChain, iNC.rest UNTIL iNC=NIL DO IF iNC.node=node THEN RETURN [TRUE]; ENDLOOP; }; NewTry: ENTRY PROC [nodesUp, nodesDown: NodeChain _ NIL, nextTry: Try _ NIL] RETURNS [t: Try] ~ { ENABLE UNWIND => NULL; IF freeTries=NIL THEN t _ NEW[TryRec _ [nodesUp, nodesDown, nextTry]] ELSE { t _ freeTries; freeTries _ freeTries.rest; t^ _ [nodesUp, nodesDown, nextTry]; nFreeTries _ nFreeTries-1; }; }; FreeTry: ENTRY PROC [t: Try] ~ { ENABLE UNWIND => NULL; t^ _ [NIL, NIL, freeTries]; freeTries _ t; nFreeTries _ nFreeTries+1; }; PrintTries: PROC [t: Try, full: BOOLEAN _ FALSE] ~ { WHILE t#NIL DO tt: Try _ t; t _ t.rest; IO.PutF[StdOut, "---[nodesUp: %g, nodesDown: %g, rest: %g]\n", IO.refAny[t.nodesUp], IO.refAny[t.nodesDown], IO.refAny[t.rest]]; IF full THEN { IO.PutF[StdOut, " nodesUp:\n"]; PrintNodeChain[t.nodesUp]; IO.PutF[StdOut, " nodesDown:\n"]; PrintNodeChain[t.nodesDown]; }; ENDLOOP; }; NewNodeChainCell: ENTRY PROC [node: Node _ NIL, nextNodeChain: NodeChain _ NIL] RETURNS [nc: NodeChain] ~ { ENABLE UNWIND => NULL; IF freeNodeChains=NIL THEN nc _ NEW[NodeChainRec _ [node, 1, nextNodeChain]] ELSE { nc _ freeNodeChains; freeNodeChains _ freeNodeChains.rest; IF nc.refcnt#0 THEN ERROR; --The free list is linked to an active element !!!! => STOP. nc^ _ [node, 1, nextNodeChain]; nFreeNodeChains _ nFreeNodeChains-1; }; IF nextNodeChain#NIL THEN nextNodeChain.refcnt _ nextNodeChain.refcnt+1; }; FreeNodeChainCell: ENTRY PROC [nc: NodeChain] ~ { ENABLE UNWIND => NULL; IF nc.rest#NIL THEN nc.rest.refcnt _ nc.rest.refcnt-1; nc^ _ [NIL, 0, freeNodeChains]; freeNodeChains _ nc; nFreeNodeChains _ nFreeNodeChains+1; }; EmptyNodeChain: PROC [nodeChain: NodeChain] ~ { IF nodeChain#NIL THEN nodeChain.refcnt _ nodeChain.refcnt-1; WHILE nodeChain#NIL AND nodeChain.refcnt=0 DO nc: NodeChain _ nodeChain; nodeChain _ nodeChain.rest; FreeNodeChainCell[nc]; ENDLOOP; }; PrintNodeChain: PROC [nodeChain: NodeChain] ~ { WHILE nodeChain#NIL DO nc: NodeChain _ nodeChain; nodeChain _ nodeChain.rest; IO.PutF[StdOut, " [node: %g, refcnt: %2g, rest: %g]\n", IO.refAny[nc.node], IO.int[nc.refcnt], IO.refAny[nc.rest]]; ENDLOOP; }; NewPath: ENTRY PROC [node: Node _ NIL, fet: Fet _ NIL, nextPath: Path _ NIL] RETURNS [p: Path] ~ { ENABLE UNWIND => NULL; IF freePaths=NIL THEN p _ NEW[PathRec _ [node, fet, 1, nextPath]] ELSE { p _ freePaths; freePaths _ freePaths.rest; p^ _ [node, fet, 1, nextPath]; nFreePaths _ nFreePaths-1; }; IF nextPath#NIL THEN nextPath.refcnt _ nextPath.refcnt+1; }; FreePath: ENTRY PROC [p: Path] ~ { ENABLE UNWIND => NULL; IF p.refcnt#1 THEN ERROR; IF p.rest#NIL THEN p.rest.refcnt _ p.rest.refcnt-1; p^ _ [NIL, NIL, 0, freePaths]; freePaths _ p; nFreePaths _ nFreePaths+1; }; EmptyPathList: PROC [pathList: PathList] ~ { FOR iPathList: PathList _ pathList, iPathList.rest UNTIL iPathList=NIL DO thisPath: Path _ iPathList.first; WHILE thisPath#NIL AND thisPath.refcnt=1 DO p: Path _ thisPath; thisPath _ thisPath.rest; FreePath[p]; ENDLOOP; ENDLOOP; }; CheckRatios: PUBLIC PROC [circuit: Circuit] ~ { FOR il: Library _ circuit.library, il.rest UNTIL il=NIL DO thisSetList: SetList _ il.first.setList; FOR iSetList: SetList _ thisSetList, iSetList.rest UNTIL iSetList=NIL DO thisSet: Set _ iSetList.first; CheckRatiosOfSet[thisSet, circuit]; ENDLOOP; ENDLOOP; }; CheckRatiosOfSet: PROC [set: Set, circuit: Circuit] ~ { FOR nlist: NodeList _ set.lNodes, nlist.rest UNTIL nlist=NIL DO to: Node _ nlist.first; IF to.setList#NIL THEN { pathList: PathList _ FindAllPaths[to, set]; FOR iPathList: PathList _ pathList, iPathList.rest UNTIL iPathList=NIL DO thisPath: Path _ iPathList.first; endNode: Node _ thisPath.node; IF endNode.exactValue THEN { notOneYet: BOOLEAN _ TRUE; conflict: BOOLEAN _ FALSE; endValue: BOOLEAN _ GetNode[endNode]; subSets: SetList _ NewSetListCell[NIL]; subSet: Set _ subSets.first; QuickSetFetList[set.lFets, FALSE]; FOR iPath: Path _ thisPath, iPath.rest UNTIL iPath=NIL DO fet: Fet _ iPath.fet; IF fet#NIL AND NOT fet.gate.exactValue THEN { gateIsUp: BOOLEAN _ fet.type.type=nE; FOR i: NAT IN [0..fet.gate.fetSeq.nUsed) DO f: Fet _ fet.gate.fetSeq[i]; f.switch _ IF gateIsUp THEN f.type.type=nE ELSE f.type.type=pE; ENDLOOP; }; ENDLOOP; PrepareToClipSet[set]; FillSet[subSet, to]; FindFixedV[subSet, set.fixedV]; FOR iNodeL: NodeList _ subSet.lNodes, iNodeL.rest UNTIL iNodeL=NIL DO IF iNodeL.first.exactValue THEN { IF notOneYet THEN notOneYet _ FALSE ELSE {conflict _ TRUE; EXIT}; }; ENDLOOP; IF NOT conflict THEN FOR iNodeL: NodeList _ subSet.fixedV, iNodeL.rest UNTIL iNodeL=NIL DO IF iNodeL.first.exactValue THEN { IF notOneYet THEN notOneYet _ FALSE ELSE {conflict _ TRUE; EXIT}; }; ENDLOOP; IF conflict THEN { vFinal: LinearSystem.ColumnN _ RCSolveInner[subSet, 0.0, circuit].vFinal; v: mVolt _ vFinal[to.index]; IF (endValue AND v<4000.0) OR (NOT endValue AND v>1000.0) THEN { IO.PutF[StdOut, "the node(s):\n"]; FOR iPath: Path _ thisPath, iPath.rest UNTIL iPath=NIL DO fet: Fet _ iPath.fet; IF fet#NIL AND NOT fet.gate.exactValue THEN IO.PutF[StdOut, " %g\n", [rope[RopeFromNode[fet.gate, circuit]]]]; ENDLOOP; IO.PutF[StdOut, "may not contribute to %g (ratio n/p:%g)\n", [rope[RopeFromNode[to, circuit]]], [real[v/(5000.0-v)]]]; } }; FreeSetList[subSets]; }; ENDLOOP; EmptyPathList[pathList]; }; ENDLOOP; }; FindAllPaths: PROC [node: Node, set: Set] RETURNS [pathList: PathList] ~ { finished: BOOLEAN _ FALSE; oldPathList: PathList; level: NAT _ 1; FOR iNodeList: NodeList _ set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.index _ 0; ENDLOOP; FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.index _ 0; ENDLOOP; oldPathList _ LIST[NewPath[node]]; node.index _ 1; UNTIL finished DO level _ level+1; [finished, oldPathList, pathList] _ IncPaths[set.lFets, oldPathList, pathList, level]; ENDLOOP; }; IncPaths: PROC [fetList: FetList, oldPathList, completedPathList: PathList, level: NAT] RETURNS [finished: BOOLEAN _ TRUE, newPathList, newCompletedPathList: PathList _ NIL] ~ { FOR iPathList: PathList _ oldPathList, iPathList.rest UNTIL iPathList=NIL DO path: Path _ iPathList.first; thisNode: Node _ path.node; terminal: BOOLEAN _ TRUE; IF ~thisNode.input THEN FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList=NIL DO thisFet: Fet _ iFetList.first; nextNode: Node; SELECT thisNode FROM thisFet.ch1 => nextNode _ thisFet.ch2; thisFet.ch2 => nextNode _ thisFet.ch1; ENDCASE => LOOP; IF nextNode.index=0 OR nextNode.index>=level THEN { newPathList _ CONS[ NewPath[nextNode, thisFet, path], newPathList]; IF ~nextNode.input THEN nextNode.index _ level; terminal _ FALSE; finished _ FALSE; }; ENDLOOP; IF terminal THEN completedPathList _ CONS[path, completedPathList]; ENDLOOP; newCompletedPathList _ completedPathList; }; FastEvalRC: PROC [node: Node, set: Set, circuit: Circuit] RETURNS [tup, tdown: ps _ 0, quit: BOOLEAN _ TRUE] ~ { pathList: PathList; pathToPower: Path; n: NAT _ 0; IF set.fixedV=NIL THEN RETURN; IF set.fixedV.rest#NIL THEN RETURN[0.0, 0.0, FALSE]; pathList _ FindAllPaths[node, set]; FOR iPathList: PathList _ pathList, iPathList.rest UNTIL iPathList=NIL DO IF iPathList.first.node.input THEN { n _ n+1; pathToPower _ iPathList.first; }; ENDLOOP; SELECT n FROM 0 => ERROR; 1 => { t: ps _ EvalPath[pathToPower]; IF GetNode[pathToPower.node] THEN tup _ t ELSE tdown _ t; }; ENDCASE => { quit _ FALSE; }; EmptyPathList[pathList]; }; EvalPath: PROC [path: Path] RETURNS [t: ps _ 0.0] ~ { r: REAL _ 0.0; FOR iPath: Path _ path, iPath.rest UNTIL iPath.rest=NIL DO r _ r+1.0/iPath.fet.type.rOnInv; t _ t+iPath.rest.node.cap*r; ENDLOOP; t _ approx*t; }; FullEvalRC: PROC [node: Node, set: Set, circuit: Circuit] RETURNS [tup, tdown: ps _ 0, quit: BOOLEAN _ TRUE] ~ { tFinal: ps; vFinal: LinearSystem.ColumnN; IF set.fixedV=NIL THEN RETURN; [tFinal, vFinal] _ RCSolveInner[set, 0.0, circuit]; IF vFinal[node.index]>threshold THEN tup _ tFinal ELSE tdown _ tFinal; }; SamePaths: PROC [n1, n2: Node] RETURNS [same: BOOLEAN _ TRUE] ~ { IF GetNode[n1] THEN { IF GetTupTdown[n1].tup#GetTupTdown[n2].tup THEN RETURN[FALSE]; n1 _ n1.prevNodeU; n2 _ n2.prevNodeU; IF n1=NIL THEN RETURN; }; DO IF GetTupTdown[n1].tdown#GetTupTdown[n2].tdown THEN RETURN[FALSE]; n1 _ n1.prevNodeD; n2 _ n2.prevNodeD; IF n1=NIL THEN RETURN; IF GetTupTdown[n1].tup#GetTupTdown[n2].tup THEN RETURN[FALSE]; n1 _ n1.prevNodeU; n2 _ n2.prevNodeU; IF n1=NIL THEN RETURN; ENDLOOP; }; IsInPath: PROC [n, pathRoot: Node] RETURNS [inPath: BOOLEAN _ FALSE] ~ { IF GetNode[pathRoot] THEN { IF SamePaths[n, pathRoot] THEN RETURN[TRUE]; pathRoot _ pathRoot.prevNodeU; IF pathRoot=NIL THEN RETURN; }; DO IF SamePaths[n, pathRoot] THEN RETURN[TRUE]; pathRoot _ pathRoot.prevNodeD; IF pathRoot=NIL THEN RETURN; IF SamePaths[n, pathRoot] THEN RETURN[TRUE]; pathRoot _ pathRoot.prevNodeU; IF pathRoot=NIL THEN RETURN; ENDLOOP; }; FindSlowestPaths: PUBLIC PROC [circuit: Circuit, n: NAT _ 1, noExactValues: BOOLEAN _ FALSE] RETURNS [veryLastTime: ps, slowNodes: PathArray]~ { FindSlowOnes: RefTab.EachPairAction ~ { t, v: REAL; node: Node ~ NARROW[val]; IF node.history=NIL THEN RETURN; IF node.exactValue AND noExactValues THEN RETURN; IF node.input THEN RETURN; [t, v] _ SettledValuesOfNode[node]; IF t PutFL }; PrintNode: PUBLIC PROC [node: Node, circuit: Circuit] ~ { IF node=NIL THEN RETURN; IO.PutF[StdOut," %g(%g) -> ", IO.rope[RopeFromNode[node, circuit]], IO.real[node.cap]]; FOR ih: Schedule.History _ node.history, ih.rest UNTIL ih=NIL DO IO.PutF[StdOut, "t:%d, V:%5d; ", IO.real[ih.first.t], IO.real[ih.first.v]]; ENDLOOP; IO.PutRope[StdOut,"\n"] }; PrintFetSeq: PUBLIC PROC [fetSeq: FetSeq, circuit: Circuit] ~ { FOR i: NAT IN [0..fetSeq.nUsed) DO PrintFet[fetSeq[i], circuit]; ENDLOOP; }; PrintFetList: PUBLIC PROC [fetList: FetList, circuit: Circuit] ~ { FOR ifetList: FetList _ fetList, ifetList.rest UNTIL ifetList=NIL DO PrintFet[ifetList.first, circuit]; ENDLOOP; }; PrintNodeList: PUBLIC PROC [nodeList: NodeList, circuit: Circuit] ~ { IO.PutF[StdOut," --\n"]; FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO PrintNode[inodeList.first, circuit]; ENDLOOP; }; PrintPathArray: PUBLIC PROC [pA: PathArray, circuit: Circuit] ~ { FOR n: NAT IN [0..pA.size) DO SELECT pA[n].nOfPaths FROM 0 => LOOP; 1 => IO.PutF[StdOut,"1 path :"]; ENDCASE => IO.PutF[StdOut,"%d paths of the form :\n", IO.int[pA[n].nOfPaths]]; PrintPath[pA[n].node, circuit]; ENDLOOP; }; PrintPath: PUBLIC PROC [to: Node, circuit: Circuit] ~ { length : ps _ to.history.rest.first.t; IO.PutF[StdOut," %2.1f ns \n", IO.real[length/1000.0]]; IF GetNode[to] THEN PrintPathUp[to, circuit] ELSE PrintPathDown[to, circuit]; }; PrintPathUp: PUBLIC PROC [to: Node, circuit: Circuit] ~ { prevTime: ps _ 0.0; DO delta, tup, tdown: ps; [tup, tdown] _ GetTupTdown[to]; delta _ prevTime-tup; IF prevTime#0.0 THEN IO.PutF[StdOut, ", delta: %4.2fns\n", IO.real[delta/1000.0]]; prevTime _ tup; IO.PutF[StdOut," up : %4.2fns at %g (%2.2f pF)", IO.real[tup/1000.0], IO.rope[RopeFromNode[to, circuit]], IO.real[to.cap]]; to _ to.prevNodeU; IF to=NIL THEN EXIT; [tup, tdown] _ GetTupTdown[to]; delta _ prevTime-tdown; IO.PutF[StdOut, ", delta: %4.2fns\n", IO.real[delta/1000.0]]; prevTime _ tdown; IO.PutF[StdOut," down: %4.2fns at %g (%2.2f pF)", IO.real[tdown/1000.0], IO.rope[RopeFromNode[to, circuit]], IO.real[to.cap]]; to _ to.prevNodeD; IF to=NIL THEN EXIT; ENDLOOP; IO.PutF[StdOut, "\n"]; }; PrintPathDown: PUBLIC PROC [to: Node, circuit: Circuit] ~ { prevTime: ps _ 0.0; DO delta, tup, tdown: ps; [tup, tdown] _ GetTupTdown[to]; delta _ prevTime-tdown; IF prevTime#0.0 THEN IO.PutF[StdOut, ", delta: %4.2fns\n", IO.real[delta/1000.0]]; prevTime _ tdown; IO.PutF[StdOut," down: %4.2fns at %g (%2.2f pF)", IO.real[tdown/1000.0], IO.rope[RopeFromNode[to, circuit]], IO.real[to.cap]]; to _ to.prevNodeD; IF to=NIL THEN EXIT; [tup, tdown] _ GetTupTdown[to]; delta _ prevTime-tup; IO.PutF[StdOut, ", delta: %4.2fns\n", IO.real[delta/1000.0]]; prevTime _ tup; IO.PutF[StdOut," up : %4.2fns at %g (%2.2f pF)", IO.real[tup/1000.0], IO.rope[RopeFromNode[to, circuit]], IO.real[to.cap]]; to _ to.prevNodeU; IF to=NIL THEN EXIT; ENDLOOP; IO.PutF[StdOut, "\n"]; }; Show: PUBLIC PROC [name: Rope.ROPE, circuit: Circuit] ~ { node: Node _ NodeFromRope[name, circuit ! CoreFlat.PathError => GO TO fail]; PrintNode[node, circuit]; PrintPath[node, circuit]; PrintFetSeq[node.fetSeq, circuit]; EXITS fail => IO.PutF[StdOut,"%g not found\n", IO.rope[name]]; }; SetInput: PROC [node: Node, set: Set] ~ { prev: NodeList; node.input _ TRUE; node.done _ TRUE; set.fixedV _ CONS[node, set.fixedV]; IF node=set.lNodes.first THEN { set.lNodes _ set.lNodes.rest; RETURN; }; prev _ set.lNodes; FOR nodeList: NodeList _ set.lNodes.rest, nodeList.rest DO IF nodeList.first=node THEN { prev.rest _ nodeList.rest; EXIT; }; prev _ nodeList; ENDLOOP; }; ResetInput: PROC [node: Node, set: Set] ~ { prev: NodeList; node.input _ FALSE; node.done _ FALSE; set.lNodes _ CONS[node, set.lNodes]; IF node=set.fixedV.first THEN { set.lNodes _ set.fixedV.rest; RETURN; }; prev _ set.fixedV; FOR nodeList: NodeList _ set.fixedV.rest, nodeList.rest DO IF nodeList.first=node THEN { prev.rest _ nodeList.rest; EXIT; }; prev _ nodeList; ENDLOOP; }; VisitLibrary: PROC [lib: Library, action: PROC[set: Set] RETURNS [ok: BOOLEAN]] RETURNS [ok: BOOLEAN] ~ { UNTIL lib=NIL DO FOR iSetList: SetList _ lib.first.setList, iSetList.rest UNTIL iSetList=NIL DO ok _ action[iSetList.first]; IF ~ok THEN RETURN; ENDLOOP; lib _ lib.rest ENDLOOP; }; SetTime: PUBLIC PROC [t: ps, circuit: Circuit] ~ { CutHistory: RefTab.EachPairAction ~ { node: Node _ NARROW[val]; IF node.history=NIL THEN RETURN; IF node.input THEN RETURN; IF Schedule.FirstTimeOfHistory[node.history]>t THEN node.history _ NIL ELSE node.history _ Schedule.AddToHistory[node.history, t, Schedule.VFromHistory[node.history, t]]; }; [] _ RefTab.Pairs[circuit.nodeTable, CutHistory] }; NodeFromRope: PUBLIC PROC [id: Rope.ROPE, circuit: Circuit] RETURNS [node: Node] ~ { flatWire: CoreFlat.FlatWire _ NEW[CoreFlat.FlatWireRec _ CoreFlat.ParseWirePath[circuit.rootCell, id]]; node _ NARROW[RefTab.Fetch[circuit.nodeTable, flatWire].val]; }; RopeFromNode: PUBLIC PROC [node: Node, circuit: Circuit] RETURNS [id: Rope.ROPE] ~ { id _ CoreFlat.WirePathRope[circuit.rootCell, node.flatWire^]; }; SetNode: PUBLIC PROC [node: Node, val, input: BOOLEAN _ TRUE, t: ps _ 0.0] ~ { v: REAL _ IF val THEN VddVal ELSE GndVal; node.history _ IF node.history=NIL THEN Schedule.CreateHistory[t, v] ELSE Schedule.AddToHistory[node.history, t, v]; node.input _ input; node.exactValue _ TRUE; }; GetNode: PUBLIC PROC [node: Node] RETURNS [val: BOOLEAN] ~ { v: REAL _ Schedule.LastValueOfHistory[node.history]; val _ v>VddVal/2.0; }; QuickSetFetList: PROC [fetList: FetList, defaultVal: BOOLEAN _ TRUE] ~ { FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList=NIL DO QuickSetFet[iFetList.first, defaultVal]; ENDLOOP; }; QuickSetFet: PROC [fet: Fet, defaultVal: BOOLEAN _ TRUE] ~ { IF fet.gate.exactValue THEN { v: REAL _ Schedule.LastValueOfHistory[fet.gate.history]; fet.switch _ SELECT fet.type.type FROM nE => v>nVtVal, pE => v ERROR; } ELSE fet.switch _ defaultVal; }; SettledValuesOfNode: PUBLIC PROC [node: Node] RETURNS [t: ps, v: mVolt] ~ { t _ Schedule.LastTimeOfHistory[node.history]; v _ Schedule.LastValueOfHistory[node.history]; }; EditNodeHistory: PUBLIC PROC [node: Node, t: ps, v: mVolt] ~ { IF node.history=NIL THEN node.history _ Schedule.CreateHistory[t, v] ELSE node.history _ Schedule.AddToHistory[node.history, t, v]; node.exactValue _ TRUE; }; EditNodeInputBool: PUBLIC PROC [node: Node, forcedInput: BOOLEAN] ~ { node.input _ forcedInput; }; KillFetList: PUBLIC PROC [fetList: FetList] ~ { fList: FetList; UNTIL fetList=NIL DO fetList.first _ NIL; fList _ fetList; fetList _ fetList.rest; fList.rest _ NIL; ENDLOOP; }; KillNodeList: PUBLIC PROC [nodeList: NodeList] ~ { nList: NodeList; UNTIL nodeList=NIL DO nodeList.first _ NIL; nList _ nodeList; nodeList _ nodeList.rest; nList.rest _ NIL; ENDLOOP; }; KillSetList: PUBLIC PROC [setList: SetList, killSets: BOOLEAN _ TRUE] ~ { sList: SetList; UNTIL setList=NIL DO IF killSets THEN { KillFetList[setList.first.lFets]; KillNodeList[setList.first.lNodes]; KillNodeList[setList.first.inList]; KillNodeList[setList.first.fixedV]; setList.first.type _ NIL; }; setList.first _ NIL; sList _ setList; setList _ setList.rest; sList.rest _ NIL; ENDLOOP; }; KillNode: RefTab.EachPairAction ~ { node: Node _ NARROW[val]; node.flatWire _ NIL; Schedule.KillHistory[node.history]; FOR i: NAT IN [0..node.fetSeq.nUsed) DO node.fetSeq[i] _ NIL; ENDLOOP; KillSetList[node.setList, FALSE]; }; KillCircuit: PUBLIC PROC [circuit: Circuit] ~ { lib: Library; circuit.rootCell _ NIL; [] _ RefTab.Pairs[circuit.nodeTable, KillNode]; RefTab.Erase[circuit.nodeTable]; IF circuit.agenda#NIL THEN Schedule.KillAgenda[circuit.agenda]; UNTIL circuit.library=NIL DO KillSetList[circuit.library.first.setList, TRUE]; circuit.library.first.type _ NIL; circuit.library.first _ NIL; lib _ circuit.library; circuit.library _ circuit.library.rest; lib.rest _ NIL; ENDLOOP; circuit.info _ NIL; }; MintEnum: PROC [plot: PlotGraph.Plot, graph: PlotGraph.Graph, bounds: PlotGraph.Rectangle, eachPoint: PlotGraph.PointProc] RETURNS [invalidEnumeration: BOOL _ FALSE] ~ { Action: Schedule.HistoryProc ~ { quit _ eachPoint[t, v]; }; quit: BOOLEAN _ FALSE; node: Node _ NARROW[graph.data]; to: REAL _ IF bounds.w=0.0 THEN 1e24 ELSE bounds.x+bounds.w; from: REAL _ IF bounds.w=0.0 THEN -1e24 ELSE bounds.x; invalidEnumeration _ Schedule.EnumerateHistory[node.history, from, to, Action]; }; AddNodeToPlot: PUBLIC PROC [node: Node, plot: PlotGraph.Plot, r: Rope.ROPE _ NIL] ~ { circuit: Circuit _ NARROW[plot.data]; rect: PlotGraph.Rectangle _ [plot.lowerBounds.x, 0.0, plot.upperBounds.x - plot.lowerBounds.x, 5000.0]; IF r=NIL THEN r _ RopeFromNode[node, circuit]; PlotGraph.LockPlot[plot]; plot.axis _ CONS[ NEW[PlotGraph.AxisRec _ [ graphs: LIST[NEW[PlotGraph.GraphRec _ [ class: mintGClass, data: node, name: "" ]]], bounds: rect, name: IF r#NIL THEN r ELSE RopeFromNode[node, circuit], style: analog, axisData: [mintAxisData, mintAxisData] ]], plot.axis]; PlotGraph.UnlockPlot[plot]; PlotGraph.RefreshPlot[plot: plot, within: rect, eraseFirst: TRUE]; }; DrawNodeList: PUBLIC PROC [nodeList: NodeList, circuit: Circuit] RETURNS[plot: PlotGraph.Plot] ~ { rect: PlotGraph.Rectangle; plot _ PlotGraph.CreatePlot[CoreOps.GetCellTypeName[circuit.rootCell]]; PlotGraph.LockPlot[plot]; plot.data _ circuit; plot.lowerBounds _ [1.0e24, 0.0]; plot.upperBounds _ [-1.0e24, 5000.0]; FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO IF inodeList.first.history#NIL THEN { plot.lowerBounds.x _ MIN[plot.lowerBounds.x, Schedule.FirstTimeOfHistory[inodeList.first.history]]; plot.upperBounds.x _ MAX[plot.upperBounds.x, Schedule.LastTimeOfHistory[inodeList.first.history]]; }; ENDLOOP; plot.upperBounds.x _ MAX[plot.upperBounds.x, 1.0+plot.lowerBounds.x]; rect _ [plot.lowerBounds.x, 0.0, plot.upperBounds.x - plot.lowerBounds.x, 5000.0]; FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO plot.axis _ CONS[ NEW[PlotGraph.AxisRec _ [ graphs: LIST[NEW[PlotGraph.GraphRec _ [ class: mintGClass, data: inodeList.first, name: "" ]]], bounds: rect, name: RopeFromNode[inodeList.first, circuit], style: analog, axisData: [mintAxisData, mintAxisData] ]], plot.axis]; ENDLOOP; PlotGraph.UnlockPlot[plot]; PlotGraph.RefreshPlot[plot: plot, within: rect, eraseFirst: TRUE]; }; mintGClass: PlotGraph.GraphClass _ NEW[PlotGraph.GraphClassRec _ [ insert: NIL, delete: NIL, enumerate: MintEnum ]]; mintAxisData: PlotGraph.AxisData _ [1000.0, TRUE]; END. 8MintImpl.mesa Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved. Christian Le Cocq January 27, 1988 4:35:18 pm PST some usefull definitions debugCircuit: Circuit _ NIL; debugNode: Node _ NIL; debugNodeList: NodeList _ NIL; debugHList: LIST OF Schedule.History _ NIL; Simulation Computations evaluates the time interval and verifies inputs Set List management Node List management Fet List management we add a fet if and only if : It is not already done It is connected to iNodeList.first by ch1 or ch2 and not gate If it is a directional fet then it is connected to iNodeList.first by ch1 Set List creation FindInputs2: PROC [set: Set] ~ { found: BOOLEAN; FOR ifet: FetList _ set.lFets, ifet.rest UNTIL ifet=NIL DO found _ FALSE; FOR inode: NodeList _ set.lNodes, inode.rest UNTIL inode=NIL DO IF inode.first=ifet.first.gate THEN { found _ TRUE; EXIT; }; ENDLOOP; IF ~found THEN { found _ FALSE; FOR inode: NodeList _ set.inList, inode.rest UNTIL inode=NIL DO IF inode.first=ifet.first.gate THEN { found _ TRUE; EXIT; }; ENDLOOP; IF ~found THEN set.inList _ NewNodeListCell[ifet.first.gate, set.inList]; }; ENDLOOP; }; fills the set which contains rootNode. Free Lists Statistics: easier to use than having to type 6 lines in the CmdTool CheckSwitch: PROC [fet: Fet, t0, t1: ps] RETURNS [change: BOOLEAN _ FALSE, changet: ps _ 0] ~ { status is TRUE if fet is on. Version with substrate effect. vSource, vTran, vGate, vCh1, vCh2: mVolt; chMin, chMax, chActive: Node; status: BOOLEAN; vGate _ Schedule.VFromHistory[fet.gate.history, t1]; vCh1 _ Schedule.VFromHistory[fet.ch1.history, t1]; vCh2 _ Schedule.VFromHistory[fet.ch2.history, t1]; IF vCh1 { chActive _ chMin; vSource _ MIN[vCh1, vCh2]; vDrain _ MAX[vCh1, vCh2]; vTran _ vSource+VtVal+gamma*(RealFns.SqRt[vSource+phi]-sqrtPhi); status _ (vGate > vTran); SELECT TRUE FROM vGate<=vSource => status _ FALSE; vGate>=vDrain => status _ TRUE; ENDCASE => { vTran _ vSource+VtVal+gamma*(RealFns.SqRt[vSource+phi]-sqrtPhi); status _ (vGate > vTran); }; }; pE => { chActive _ chMax; vSource _ MAX[vCh1, vCh2]; vDrain _ MIN[vCh1, vCh2]; vTran _ vSource-(VtVal+gamma*(RealFns.SqRt[VddVal-vSource+phi] - sqrtPhi)); status _ (vGate < vTran); SELECT TRUE FROM vGate>=vSource => status _ FALSE; vGate<=vDrain => status _ TRUE; ENDCASE => { vTran _ vSource-(VtVal+gamma*(RealFns.SqRt[VddVal-vSource+phi] - sqrtPhi)); status _ (vGate < vTran); }; }; ENDCASE => ERROR; IF status#fet.switch THEN { UNTIL t1-t0˜>Kšœœœœœ6œ$œ œ˜ÌK˜K˜—š œœ9˜Ošœ(œœ˜=Kšœ˜šœ3œ œ˜JKšœ˜Kšœ<˜Kšœ`˜`šœ1œ œ˜IKšœ4˜4Kšœœ˜šœ=œ œ˜VKšœ#˜#Kšœ˜—Kšœ˜—K˜—K˜—K˜šœ(œœ˜=K™/šœœœ˜!Kšœ<˜™>Kšœœ˜Kšœ<˜<šœ˜Kšœ%˜%Kšœ%˜%Kšœœ˜—šœœ˜šœ˜K˜Kšœ!œ˜/Kšœ ˜ Kšœ˜ —Kšœ œ˜Kšœ ˜ K˜—K˜K˜—š   œœ œ œœ˜bK˜Kšœœ˜Kšœœ˜Kšœ ˜ šœ,œ œ˜DKšœ˜Kšœœœ˜9šœ˜Kšœ+˜+šœœ˜Kšœ!˜!Kšœ œ˜šœœ˜šœ3œ œ˜LKšœ1˜1Kšœ˜—Kšœœ˜Kšœ˜Kšœ ˜ K˜—Kšœ5˜5K˜—K˜—Kšœ˜—Kšœ˜K˜K˜—š œœ œœ˜2Kšœ œ˜šœ.œ œ˜FKšœœ˜Kšœ˜—Kšœœ˜ K˜K˜—š œœ-˜8Kšœœ˜Kšœœ˜'Kšœœ˜'Kšœ˜Kšœ˜Kšœ%˜%Kšœ%˜%Kšœ%˜%Kšœ%˜%Kšœ œ˜K˜K˜—š  œœ) œœ#˜Kšœ˜Kšœœ˜ Kšœ$˜$Kšœ˜Kšœ˜Kšœ˜šœœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜K˜—š  œœœœ+˜MKšœ œ!˜.Kšœœ*˜>Kšœœ'˜š œ˜#Kšœ:™:Kšœ œ˜K˜Kšœ œœ˜Kšœ/˜/Kšœœ˜šœ/œ œ˜FKšœ;˜;Kšœ˜—K˜—Kšœ(œ ˜4Kšœ/˜/Kšœ-˜-Kšœ˜K˜K˜—š œœœ#œ ˜QK˜K˜Kšœ!˜!Kšœ)˜)Kšœ7˜7Kšœ)˜)Kšœ7˜7Kšœ=˜=Kšœ˜Kšœ+˜+KšœEœ$œ œ/˜ªKšœŸ˜K˜—š Ïbœœœœœœ˜>Kšœœ˜ Kšœ˜Kšœ8œ ˜[Kšœ%˜%KšœBœ˜IKšœ˜K˜K˜—š œœœ*œ˜bš¡ œ˜(Kšœ:™:Kšœ œ˜Kšœœ œ˜7K˜—Kšœ˜Kšœ˜Kšœœ˜%Kšœ!˜!Kšœ)˜)Kšœ7˜7Kšœ)˜)Kšœ7˜7Kšœ œœ5˜JKšœ.˜.Kšœ?˜?Kšœ˜Kšœ+˜+K˜——™š  œœ œ˜9Kšœ˜K˜K˜K˜—š œœ-œœ˜fš   œœœ œœ˜Ršœ.œ œ˜FKšœ˜Kšœœœœ˜(Kšœ˜—K˜K˜—K˜ Kšœ"˜"Kšœ˜šœ,œ œ˜DKšœ˜šœœ˜!šœœŸ"˜Kšœ˜Kšœ˜Kšœœœœ˜K˜—š˜Kšœ-œœœ˜BKšœ˜Kšœ˜Kšœœœœ˜Kšœ)œœœ˜>Kšœ˜Kšœ˜Kšœœœœ˜Kšœ˜—K˜K˜—š  œœœ œœ˜Hšœœ˜Kšœœœœ˜,Kšœ˜Kšœ œœœ˜K˜—š˜Kšœœœœ˜,Kšœ˜Kšœ œœœ˜Kšœœœœ˜,Kšœ˜Kšœ œœœ˜Kšœ˜—K˜K˜—š œœœœœœœ,˜š¡ œ˜'Kšœ:™:Kšœœ˜ Kšœ œ˜Kšœœœœ˜ Kšœœœœ˜1Kšœ œœ˜Kšœ#˜#Kšœœœ˜'šœœœ˜šœ œœ˜/Kšœ˜šœœ,œ˜QKšœ@˜@Kšœ˜K˜—šœœœ ˜Jšœ,œœ˜:Kšœ˜—Kšœ˜šœœ œ ˜'Kšœ(˜(Kšœ œœ˜Kš œœœ,œ œ˜eKšœ˜Kšœ œœ˜Kšœ ˜ Kšœ˜—Kšœ$˜$Kšœ˜K˜—Kšœ˜—K˜K˜—Kšœ œ˜!šœœœ˜Kšœ˜Kšœ˜—Kšœ3˜3Kšœ&˜&K˜K˜——™ š  œœ˜*Kšœœœ˜TKš œ%œœœœ˜‹K˜K˜—š œ œ!˜6Kšœœœœ˜Kšœ9œœœœœœœ œœ œœ(œ'œ)Ÿœ˜ÚK˜K˜—š  œœœ#˜9Kšœœœœ˜Kšœœ$œ˜Yšœ.œœ˜@Kšœ œœ˜LKšœ˜—Kšœ˜šœ˜K˜——š ¡œ œ'˜?šœœœ˜"Kšœ˜Kšœ˜—K˜K˜—š ¡œ œ)˜Bšœ,œ œ˜DKšœ"˜"Kšœ˜—K˜K˜—š ¡œ œ+˜EKšœ˜šœ0œ œ˜IKšœ$˜$Kšœ˜—K˜K˜—š œ œ&˜Ašœœœ˜šœ˜Kšœœ˜ Kšœœ˜!Kšœœ)œ˜N—Kšœ˜Kšœ˜—K˜K˜—š  œ œ!˜7Kšœ&˜&Kšœ7˜9Kšœ œ˜,Kšœ˜ K˜K˜—š  œ œ!˜9Kšœ˜š˜Kšœ˜Kšœ˜Kšœ˜Kšœœœ$œ˜RKšœ˜Kšœ3œœ"œ˜Kšœ˜Kšœœœœ˜Kšœ˜Kšœ˜Kšœ$œ˜=Kšœ˜Kšœ2œœ"œ˜€Kšœ˜Kšœœœœ˜Kšœ˜—Kšœ˜K˜K˜—š  œ œ!˜;Kšœ˜š˜Kšœ˜Kšœ˜Kšœ˜Kšœœœ$œ˜RKšœ˜Kšœ2œœ"œ˜€Kšœ˜Kšœœœœ˜Kšœ˜Kšœ˜Kšœ$œ˜=Kšœ˜Kšœ3œœ"œ˜Kšœ˜Kšœœœœ˜Kšœ˜—Kšœ˜K˜K˜—š œœœ œ˜9Kšœ@œœ˜LJ˜Jšœ˜J˜"Kšœ œœ ˜>šœ˜K˜——š œœœ™?šœ0œ œ™IKšœ™Kšœ™—K™K™—š  œœœœ™Kš¡ œ™(Kšœ:™:Kšœ œ™Kšœœ œ™/K™—Kšœ3™3K™K™—š œœ˜)Kšœ˜Kšœ œ˜Kšœ œ˜Kšœ™Kšœ œ˜$Kšœ™šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜šœ5˜:šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜Kšœ˜—K˜K˜—š  œœ˜+Kšœ˜Kšœ œ˜Kšœ œ˜Kšœ™Kšœ œ˜$Kšœ&™&šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜šœ5˜:šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜Kšœ˜—K˜K˜—š  œœœ œœœœ˜išœœ˜šœ6œ œ˜NKšœ˜Kšœœœ˜Kšœ˜—Kšœ˜Kšœ˜—K˜K˜—š œœœ˜2š  œ˜%Kšœ:™:Kšœ œ˜Kšœœœœ˜ Kšœ œœ˜Kšœ-œ˜FKšœ`˜dK˜—K˜Kšœ0˜0K˜K˜—š   œœœ œœ˜TKšœœF˜gKšœœ0˜=K˜K˜—š  œ œ œ œ˜TKšœ=˜=K˜K˜—š  œœœœœ˜NKš œœœœœ˜)Kš œœœœœ+˜tKšœ˜Kšœœ˜K˜K˜—š œ œœœ˜Kšœœœ,˜DKšœ:˜>Kšœœ˜K˜K˜—š œœœœ˜EKšœ˜K˜K˜—šÐbn œœœ˜/Kšœ˜šœ œ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜—K˜K˜—š¢ œœœ˜2Kšœ˜šœ œ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜—K˜K˜—š   œœœœœ˜IKšœ˜šœ œ˜šœ œ˜Kšœ!˜!Kšœ#˜#Kšœ#˜#Kšœ#˜#Kšœœ˜K˜—Kšœœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜—K˜K˜—š œ˜#Kšœ:™:Kšœ œ˜Kšœœ˜Kšœ œ™Kšœ#˜#šœœœ˜'Kšœœ˜Kšœ˜—Kšœœ˜!K˜K˜—š  œœœ˜/K˜ Kšœœ˜Kšœ/˜/Kšœ ˜ Kšœœœ%˜?šœœ˜Jšœ+œ˜1Jšœœ˜!Jšœœ˜Jšœ˜Kšœ'˜'Kšœ œ˜Kšœ˜—Kšœœ˜K˜——™š  œœmœœœ˜©š œ˜ Kšœ˜K˜—Kšœœœ˜Kšœ œ ˜ Kš œœœœœ˜