DIRECTORY CoreFlat, CoreOps, HashTable, Histograms, Icons, IO, LinearSystem, Mint, PlotGraph, Real, Rope, Schedule, TerminalIO, TypeScript, ViewerIO; MintImpl: CEDAR PROGRAM IMPORTS CoreFlat, CoreOps, HashTable, Histograms, Icons, IO, LinearSystem, PlotGraph, Schedule, TerminalIO, TypeScript, ViewerIO EXPORTS Mint SHARES Schedule ~ BEGIN OPEN Mint; Path: TYPE = LIST OF RECORD[node: Node, fet: Fet]; PathList: TYPE = LIST OF Path; Delay: TYPE = REF DelayRec; DelayRec: TYPE = RECORD [tup, tdown: ps]; 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 _ -0.800; nVtVal: mVolt _ 0.780; 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; debugCircuit: Circuit _ NIL; debugNode: Node _ NIL; debugNodeList: NodeList _ NIL; debugHList: LIST OF Schedule.History _ NIL; 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, "\n\n%5d ~~~>",IO.real[t]]; PrintSetType[set.type]; IO.PutF[StdOut, "\nInputs :"];}; nodeList _ ScanHistoriesOfNodeList[set.inList, t, agenda, FALSE]; FOR inode: NodeList _ nodeList, inode.rest UNTIL inode=NIL DO IF verbose THEN IO.PutF[StdOut,"\n Unknown Input: %g ", IO.rope[RopeFromNode[inode.first, circuit]]] ENDLOOP; nodeList _ ScanHistoriesOfNodeList[set.lNodes, t, agenda]; 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:"]; timeList _ Schedule.Schedule[inputHistories]; 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; KillSetList[subsets, TRUE]; } 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; FOR inode: NodeList _ set.lNodes, inode.rest UNTIL inode=NIL DO FOR setList: SetList _ inode.first.setList, setList.rest UNTIL setList=NIL DO Schedule.InsertInAgenda[agenda, setList.first, Schedule.NextTimeOfHistory[inode.first.history, t+1]]; setList.first.done _ FALSE; ENDLOOP; ENDLOOP; circuit.info.nbOfSimulations _ circuit.info.nbOfSimulations+1; IF verbose THEN IF (circuit.info.nbOfSimulations MOD nSimInc) = 0 THEN IO.PutF[StdOut, "\n%5d t : %5d, No of events : %7d", IO.int[circuit.info.nbOfSimulations], IO.real[agenda.list.first.t], IO.int[agenda.nbOfEvents]]; }; 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 _ CONS[inode.first, uninitialized]; inode.first.history _ Schedule.CreateHistory[t, GndVal+0.0314159]; } ELSE { IF ~inode.first.watched THEN inode.first.history _ Schedule.ForgetBeginings[inode.first.history, t]; IF cut THEN [] _ CutHistory[inode.first]; }; ENDLOOP; }; 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 _ CONS[ifet.first.gate, set.inList]; }; ENDLOOP; }; NextFetList2: PROC [set: Set, 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 THEN IF fet.gate # iNodeList.first THEN { fetList _ CONS[fet, fetList]; set.lFets _ CONS[fet, set.lFets]; fet.done _ TRUE; }; ENDLOOP; ENDLOOP; }; --NextFetList2 NextNodeList2: PROC [set: Set, fetList: FetList] RETURNS [nodeList: NodeList _ NIL] ~ { FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList = NIL DO IF NOT iFetList.first.ch1.done THEN { nodeList _ CONS[iFetList.first.ch1, nodeList]; set.lNodes _ CONS[iFetList.first.ch1, set.lNodes]; iFetList.first.ch1.done _ TRUE; }; IF NOT iFetList.first.ch2.done THEN { nodeList _ CONS[iFetList.first.ch2, nodeList]; set.lNodes _ CONS[iFetList.first.ch2, set.lNodes]; iFetList.first.ch2.done _ TRUE; }; ENDLOOP; }; --NextNodeList2 ClipSet: PROC [totalSet: Set, cutNodes: NodeList _ NIL] RETURNS [setList: SetList _ NIL] ~ { set: Set; nodeList: NodeList; fetList: FetList; 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; FOR iNodeList: NodeList _ cutNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ TRUE; ENDLOOP; FOR inodeList: NodeList _ totalSet.lNodes, inodeList.rest UNTIL inodeList=NIL DO IF inodeList.first.done THEN LOOP; set _ NEW[SetRec]; nodeList _ LIST[inodeList.first]; set.lNodes _ nodeList; inodeList.first.done _ TRUE; UNTIL nodeList=NIL DO fetList _ NextFetList2[set, nodeList]; nodeList _ NextNodeList2[set, fetList]; ENDLOOP; FOR iNodeList: NodeList _ totalSet.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ FALSE; ENDLOOP; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO IF iFetList.first.ch1.input THEN IF ~iFetList.first.ch1.done AND iFetList.first.switch THEN { iFetList.first.ch1.done _ TRUE; set.fixedV _ CONS[iFetList.first.ch1, set.fixedV]; }; IF iFetList.first.ch2.input THEN IF ~iFetList.first.ch2.done AND iFetList.first.switch THEN { iFetList.first.ch2.done _ TRUE; set.fixedV _ CONS[iFetList.first.ch2, set.fixedV]; }; ENDLOOP; FOR iNodeList: NodeList _ totalSet.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ TRUE; ENDLOOP; setList _ CONS[set, setList]; ENDLOOP; }; 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] ~ { matrix, subMatrix: LinearSystem.MatrixN; cstTerm, vInit, vFinal, tau, vFinal2: LinearSystem.ColumnN; tFinal: ps _ 0.0; halfVddVal: REAL _ VddVal/2.0; triState: BOOLEAN _ TRUE; nNodes, nActiveNodes, lastAct: CARDINAL _ 0; IF set.fixedV=NIL THEN RETURN[t]; -- 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.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO vInit[iNodeList.first.index] _ Schedule.VFromHistory[iNodeList.first.history, t]; ENDLOOP; 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; FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.history _ Schedule.AddToHistory[iNodeList.first.history, t, vInit[iNodeList.first.index]]; 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 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; }; Initialize: PROC [circuit: Circuit] RETURNS [t: ps _ 1e30] ~ { SetEvent: HashTable.EachPairAction ~ { node: Node _ NARROW[value]; 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]; [] _ HashTable.Pairs[circuit.nodeTable, SetEvent]; [] _ VisitLibrary[circuit.library, ResetSets]; 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]; [t, lastNodes] _ LastTime[circuit, 1]; 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] ~ { 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 watchList _ WatchedListOf[circuit]; tPlot.plot _ DrawNodeList[watchList, circuit]; circuit.agenda _ Schedule.CreateAgenda[SimulateAndPlot, tPlot]; t _ Initialize[circuit]; t _ Schedule.ExecuteAgenda[circuit.agenda]; }; NextSets: PROC [c: CaminoCell, setCache: HashTable.Table] RETURNS [camino: Camino] ~ { set: Set ~ c.set; node: Node ~ c.node; value: HashTable.Value; boolVal: BOOLEAN; subSets: SetList; [boolVal, value] _ HashTable.Fetch[setCache, set]; IF boolVal THEN RETURN[[0.0, NARROW[value, LIST OF CaminoCell]]]; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO QuickSetFet[iFetList.first]; ENDLOOP; subSets _ ClipSet[set]; FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO subSet: Set _ iSetList.first; boolVal _ FALSE; FOR iFetList: FetList _ subSet.lFets, iFetList.rest UNTIL iFetList=NIL DO IF iFetList.first.gate=node THEN {boolVal _ TRUE; EXIT}; ENDLOOP; IF boolVal THEN { FOR iNodeList: NodeList _ subSet.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO IF iNodeList.first.ignoreMe THEN LOOP; IF iNodeList.first.watched THEN camino.data _ CONS[[NEW[SetRec _ [inList: LIST[iNodeList.first]]], iNodeList.first], camino.data]; FOR iSetList2: SetList _ iNodeList.first.setList, iSetList2.rest UNTIL iSetList2=NIL DO camino.data _ CONS[[iSetList2.first, iNodeList.first], camino.data]; ENDLOOP; ENDLOOP; }; ENDLOOP; KillSetList[subSets, TRUE]; [] _ HashTable.Store[setCache, set, camino.data]; }; SimulateOrRegister: PROC [ref: REF ANY, t: ps, data: REF ANY] ~ { set: Set ~ NARROW[ref]; extdCirc: ExtendedCircuit _ NARROW[data]; startTable: HashTable.Table _ NARROW[extdCirc.data]; FOR inode: NodeList _ set.inList, inode.rest UNTIL inode=NIL DO IF inode.first.history=NIL THEN { [] _ HashTable.Store[startTable, set, $Start]; RETURN; }; ENDLOOP; [] _ HashTable.Delete[startTable, set]; --in case we registered it too early SimulateSet[ref, t, extdCirc.circuit]; }; MaxFreqEvaluate: PUBLIC PROC [circuit: Circuit, clkList: NodeList, numberOfPaths: CARD, from: ps _ 0.0, histOk: BOOLEAN _ TRUE] RETURNS [worst: ps _ 0.0, caminos: Caminos]~ { EraseHistory: HashTable.EachPairAction ~ { node: Node _ NARROW[value]; 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: HashTable.EachPairAction ~ { set: Set _ NARROW[key]; liveCaminos _ CONS[[0.0, LIST[[set, set.inList.first]]], liveCaminos]; }; RecordOrForget: PROC [camino: Camino] ~ { delay: Delay _ ThruTime[camino.data.rest, camino.data.first.node, delayCache, delayListCache, circuit]; camino.length _ MAX[delay.tup, delay.tdown]; IF histOk THEN Histograms.ChangeTransformed[hist, camino.length]; nTotal _ nTotal+1; SELECT alreadyIn FROM 0 => { caminos _ LIST[camino]; nthTime _ camino.length; worst _ camino.length; alreadyIn _ 1; }; IN [1..numberOfPaths) => { caminos _ CONS[camino, caminos]; alreadyIn _ alreadyIn+1; IF camino.length>nthTime THEN nthTime _ camino.length; IF camino.length>worst THEN worst _ camino.length }; ENDCASE => IF camino.length>nthTime THEN { IF camino.length>worst THEN worst _ camino.length; FOR iCaminos: Caminos _ caminos, iCaminos.rest DO IF camino.length>iCaminos.first.length THEN { iCaminos.first _ camino; nthTime _ camino.length; RETURN; }; ENDLOOP; }; }; level, nTotal, alreadyIn: CARD _ 0; nthTime: REAL _ 0.0; found, finished: BOOLEAN _ FALSE; gndNode: Node = NodeFromRope[gndName, circuit]; vddNode: Node = NodeFromRope[vddName, circuit]; startTable: HashTable.Table _ HashTable.Create[]; liveCaminos: Caminos; extdCirc: ExtendedCircuit _ NEW[ExtendedCircuitRec _ [circuit, startTable]]; setCache: HashTable.Table _ HashTable.Create[]; delayCache: HashTable.Table _ HashTable.Create[]; delayListCache: HashTable.Table _ HashTable.Create[]; setListCache: HashTable.Table _ HashTable.Create[]; hist: Histograms.Histogram; --in ns circuit.info.nbOfSimulations _ 0; circuit.agenda _ Schedule.CreateAgenda[SimulateOrRegister, extdCirc]; [] _ HashTable.Pairs[circuit.nodeTable, EraseHistory]; gndNode.history _ Schedule.CreateHistory[from, GndVal]; vddNode.history _ Schedule.CreateHistory[from, VddVal]; [] _ VisitLibrary[circuit.library, ResetSets]; FOR inode: NodeList _ clkList, inode.rest UNTIL inode=NIL DO IF inode.first.history=NIL THEN inode.first.history _ Schedule.CreateHistory[from, VddVal]; 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]; [] _ HashTable.Pairs[startTable, CreateStart]; IF histOk THEN hist _ Histograms.Create1D[100.0, 0.0]; UNTIL liveCaminos=NIL DO n2: INT _ 0; oldCaminos: Caminos _ liveCaminos; liveCaminos _ NIL; level _ level+1; FOR iCaminos: Caminos _ oldCaminos, iCaminos.rest UNTIL iCaminos=NIL DO thisCamino: Camino _ iCaminos.first; nextOnes: Camino _ NextSets[thisCamino.data.first, setCache]; FOR iNext: LIST OF CaminoCell _ nextOnes.data, iNext.rest UNTIL iNext=NIL DO found _ FALSE; FOR iCamino: LIST OF CaminoCell _ thisCamino.data, iCamino.rest UNTIL iCamino=NIL DO IF iNext.first.set=iCamino.first.set THEN { newCamino: Camino _ [0.0, CONS[iNext.first, thisCamino.data]]; RecordOrForget[newCamino]; found _ TRUE; EXIT } ENDLOOP; IF ~found THEN { newCamino: Camino _ [0.0, CONS[iNext.first, thisCamino.data]]; liveCaminos _ CONS[newCamino, liveCaminos]; n2 _ n2+1; }; ENDLOOP; IF nextOnes.data=NIL THEN { RecordOrForget[thisCamino]; }; ENDLOOP; KillCaminos[oldCaminos]; 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; FOR iCaminos: Caminos _ caminos, iCaminos.rest UNTIL iCaminos=NIL DO IF iCaminos.first.length=worst THEN { camino: Camino _ iCaminos.first; iCaminos.first _ caminos.first; caminos.first _ camino; EXIT; }; ENDLOOP; IF histOk THEN [] _ Histograms.Show[hist]; }; ThruTime: PROC [caminoData: LIST OF CaminoCell, node: Node, setCache, setListCache: HashTable.Table, circuit: Circuit] RETURNS [delay: Delay] ~ { setDelay, prevDelay: Delay; delay _ NEW[DelayRec _ [0.0, 0.0]]; IF caminoData=NIL THEN RETURN; setDelay _ DelayOfSet[caminoData.first.set, node, setCache, circuit]; prevDelay _ ThruTime[caminoData.rest, caminoData.first.node, setCache, setListCache, circuit]; delay.tup _ overlapFactor*setDelay.tup+prevDelay.tdown; delay.tdown _ overlapFactor*setDelay.tdown+prevDelay.tup; }; DelayOfSet: PROC [set: Set, node: Node, setCache: HashTable.Table, circuit: Circuit] RETURNS [delay: Delay] ~ { value: HashTable.Value; found: BOOLEAN; tup, tdown: ps _ 0.0; [found, value] _ HashTable.Fetch[setCache, set]; IF found THEN RETURN[NARROW[value, Delay]]; delay _ NEW[DelayRec _ [0.0, 0.0]]; FOR iNList: NodeList _ set.inList, iNList.rest UNTIL iNList=NIL DO iNList.first.done _ iNList.first.history#NIL; ENDLOOP; FOR iNList: NodeList _ set.fixedV, iNList.rest UNTIL iNList=NIL DO iNList.first.done _ TRUE; ENDLOOP; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO QuickSetFet[iFetList.first]; ENDLOOP; [tup, tdown] _ EvalRC[node, set, circuit]; delay.tup _ tup; delay.tdown _ tdown; FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.history _ NIL; ENDLOOP; [] _ HashTable.Store[setCache, set, delay]; }; FindSetOfNode: PROC [node: Node] RETURNS [set: Set] ~ { found: BOOLEAN _ FALSE; FOR i: NAT IN [0..node.fetSeq.nUsed) DO fet: Fet = node.fetSeq[i]; FOR iSetList: SetList _ fet.gate.setList, iSetList.rest UNTIL iSetList=NIL DO FOR iNodeList: NodeList _ iSetList.first.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO IF iNodeList.first=node THEN RETURN[iSetList.first]; ENDLOOP; ENDLOOP; ERROR; -- who did some hand editing on the internal data structure ? 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[LIST[[node, NIL]]]; 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.first.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[ CONS[[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; }; EvalRC: PROC [node: Node, set: Set, circuit: Circuit] RETURNS [tup, tdown: ps _ 0] ~ { subSets: SetList; pathList: PathList; pathToPower: Path; found: BOOLEAN _ FALSE; tempup, tempdown: ps _ 0.0; n: NAT _ 0; FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO IF iNodeList.first=node THEN { found _ TRUE; EXIT; }; ENDLOOP; IF ~found THEN RETURN; IF set.fixedV=NIL THEN RETURN; pathList _ FindAllPaths[node, set]; FOR iPathList: PathList _ pathList, iPathList.rest UNTIL iPathList=NIL DO IF iPathList.first.first.node.input THEN { n _ n+1; pathToPower _ iPathList.first; }; ENDLOOP; SELECT n FROM 0 => ERROR; 1 => { t: ps _ 0.0; r: REAL _ 0.0; FOR iPath: Path _ pathToPower, iPath.rest UNTIL iPath.rest=NIL DO r _ r+1.0/iPath.first.fet.type.rOnInv; t _ t+iPath.rest.first.node.cap*r; ENDLOOP; IF GetNode[pathToPower.first.node] THEN tup _ approx*t ELSE tdown _ approx*t; }; ENDCASE => { FindInputs2[set]; FOR iNodeList: NodeList _ set.inList, iNodeList.rest UNTIL iNodeList=NIL DO IF ~iNodeList.first.done THEN { iNodeList.first.done _ TRUE; FOR bool: BOOLEAN IN BOOLEAN DO FOR i: NAT IN [0..node.fetSeq.nUsed) DO fet: Fet = node.fetSeq[i]; fet.switch _ IF bool THEN fet.type.type=nE ELSE fet.type.type=pE; ENDLOOP; subSets _ ClipSet[set]; FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO [tempup, tempdown] _ EvalRC[node, iSetList.first, circuit]; tup _ MAX[tempup, tup]; tdown _ MAX[tempdown, tdown]; ENDLOOP; KillSetList[subSets, TRUE]; ENDLOOP; }; ENDLOOP; }; IF tup=0.0 AND tdown=0.0 THEN { t: ps; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO iFetList.first.switch _ TRUE; ENDLOOP; [] _ ScanHistoriesOfNodeList[set.lNodes, 0.0, NIL, FALSE]; t _ RCSolve[set, 0.0, circuit]; IF GetNode[node] THEN tup _ t ELSE tdown _ t; }; }; MaxCapa: PUBLIC PROC [circuit: Circuit, n: NAT _ 1] RETURNS [fatest: REAL, fatNodes: NodeList]~ { FindFatOnes: PROC[set: Set] RETURNS [ok: BOOLEAN] ~ { ok _ TRUE; FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO node _ iNodeList.first; totalC _ totalC+node.cap; IF node.cap>data.table[0].x THEN FOR ind: NAT IN [0..n) DO IF ind=n-1 OR node.cap PutFL }; PrintNode: PUBLIC PROC [node: Node, circuit: Circuit] ~ { IF node=NIL THEN RETURN; IO.PutF[StdOut,"\n %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; }; 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; }; PrintCamino: PUBLIC PROC [camino: Camino, circuit: Circuit] ~ { IO.PutF[StdOut,"\n %d ", IO.real[camino.length]]; FOR iCamino: LIST OF CaminoCell _ camino.data, iCamino.rest UNTIL iCamino=NIL DO PrintNode[ iCamino.first.node, circuit]; ENDLOOP; }; PrintCaminos: PUBLIC PROC [caminos: Caminos, circuit: Circuit] ~ { IO.PutF[StdOut,"\n **"]; FOR iCaminos: Caminos _ caminos, iCaminos.rest UNTIL iCaminos=NIL DO PrintCamino[ iCaminos.first, circuit]; ENDLOOP; }; SetDoneInNodeList: PROC [nodeList: NodeList, done: BOOLEAN] ~ { FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO inodeList.first.done _ done; ENDLOOP; }; WatchedListOf: PUBLIC PROC [circuit: Circuit] RETURNS [wList: NodeList] ~ { SearchWatched: HashTable.EachPairAction ~ { node: Node _ NARROW[value]; IF node.watched THEN wList _ CONS[node, wList]; }; [] _ HashTable.Pairs[circuit.nodeTable, SearchWatched] }; 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: HashTable.EachPairAction ~ { node: Node _ NARROW[value]; 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]]; }; [] _ HashTable.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[HashTable.Fetch[circuit.nodeTable, flatWire].value]; }; 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; }; GetNode: PUBLIC PROC [node: Node] RETURNS [val: BOOLEAN] ~ { v: REAL _ Schedule.LastValueOfHistory[node.history]; val _ v>VddVal/2.0; }; QuickSetFet: PROC [fet: Fet] ~ { IF fet.gate.history#NIL THEN { v: REAL _ Schedule.LastValueOfHistory[fet.gate.history]; fet.switch _ SELECT fet.type.type FROM nE => v>nVtVal, pE => v ERROR; } ELSE fet.switch _ TRUE; }; 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]; }; EditNodeInputBool: PUBLIC PROC [node: Node, forcedInput: BOOLEAN] ~ { node.input _ forcedInput; }; KillCaminos: PUBLIC PROC [caminos: Caminos] ~ { cam: Caminos; UNTIL caminos=NIL DO cam _ caminos; caminos _ caminos.rest; cam.rest _ NIL; ENDLOOP; }; 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: HashTable.EachPairAction ~ { node: Node _ NARROW[value]; 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; [] _ HashTable.Pairs[circuit.nodeTable, KillNode]; HashTable.Erase[circuit.nodeTable]; 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, data: REF ANY _ NIL] RETURNS [invalidEnumeration: BOOL _ FALSE] ~ { Action: Schedule.HistoryProc ~ { quit _ eachPoint[t, v, data]; }; 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; quit _ 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, FALSE]; IF separateView THEN { typeScript: TypeScript.TS _ TypeScript.Create[info: [name: "Mint", iconic: TRUE, icon: Icons.NewIconFromFile["Mint.icons", 1]]]; StdOut _ ViewerIO.CreateViewerStreams[name: "Mint", viewer: typeScript].out; } ELSE StdOut _ TerminalIO.CreateStream[]; IO.PutRope[StdOut, "\nMint loaded\n"]; END. âMintImpl.mesa Copyright Ó 1986, 1987 by Xerox Corporation. All rights reserved. Christian LeCocq April 21, 1987 9:09:51 pm PDT RealFns, RealFns, MintPrivateData: PUBLIC TYPE = REF MintPrivateDataRec; MintPrivateDataRec: TYPE = RECORD[ matrix: LinearSystem.MatrixN, cstTerm: LinearSystem.ColumnN, vInit: LinearSystem.ColumnN, tau: LinearSystem.ColumnN, copyMatrix: LinearSystem.MatrixN ]; some usefull definitions Simulation Computations evaluates the time interval and verifies inputs Propagate: PROC [node: Node, action: PROC[node: Node] RETURNS [quit: BOOLEAN _ FALSE]] RETURNS [quit: BOOLEAN _ FALSE] ~ { Calls action for all the nodes of the sets activated by node. FOR iSetList: SetList _ node.setList, iSetList.rest UNTIL iSetList=NIL DO FOR inodeList: NodeList _ iSetList.first.lNodes, inodeList.rest UNTIL inodeList=NIL DO quit _ action[inodeList.first]; IF quit THEN RETURN; ENDLOOP; ENDLOOP; }; 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-t0matrixCacheSize THEN RETURN[LinearSystem.Copy[matrix]]; FOR i: CARDINAL IN [0..n) DO FOR j: CARDINAL IN [0..n) DO circuit.private.subMatrix[i][j] _ matrix[i][j]; ENDLOOP; ENDLOOP; RETURN[circuit.private.subMatrix]; }; FOR j: CARDINAL IN [0..nActiveNodes) DO circuit.private.copyMatrix[i][j] _ matrix[i][j]; ENDLOOP; Initialisations (no ALL[0.0] for sequences...) FOR iColumn: CARDINAL IN [0..nNodes) DO matrix[iRow][iColumn] _ 0.0; ENDLOOP; If fixed voltages are not connected to the set then ERROR. IF triState THEN RETURN[t]; Fixed voltages are removed from the system to solve. mark the point where the value begins to change Solve the RC system, which as a side effect destroys cstTerm. mark the point where the value stabilizes Execution PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE]; Timing Evaluation Detection of circularities: never twice the same set. If circularities then add the set (if not zero length circularity) and give up this camino. no circularities so add the set to the current Camino, and the Camino to the next round candidates no further ways available so record it in the finished caminos. value: HashTable.Value; found: BOOLEAN; [found, value] _ HashTable.Fetch[setListCache, caminoData]; IF found THEN RETURN[NARROW[value, Delay]]; [] _ HashTable.Store[setListCache, caminoData, delay]; does this set contain the specified node ? does this set contain one of the power node ? is there only one path to a power node ? either no further input specification reduces the poblem, or the set has only one path to power PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE]; PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE]; Utilities PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE]; add node to fixed voltages and remove it from lNodes add node to lNodes and remove it from fixed voltages list PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE]; PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE]; Plot Management Ê:˜codešœ ™ KšœB™BK™.K™K™—šÏk ˜ K˜ K˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜K˜ Kšœ˜Kšœ™Kšœ˜Kšœ ˜ K˜ Kšœ ˜ Kšœ ˜ K˜—KšÏnœœ˜š˜K˜ K˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ ˜ K˜ Kšœ™Kšœ ˜ K˜ Kšœ ˜ Kšœ˜K˜—š˜K˜—š˜Kšœ˜—šœœœ˜Kšœœœœ™6šœœœ™"Kšœ™Kšœ™Kšœ™Kšœ™Kšœ ™ Kšœ™—Kš œœœœœ˜2Kšœ œœœ˜Kšœœœ ˜Kšœ œœ˜)Kšœœœ˜/Kš œœœœœ˜DKšœ œœ˜#Kšœœœœ˜IK˜K™Kšœœ˜"Kšœœ˜"Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜KšœœÏc+˜GKšœ˜Kšœ œ ˜K˜K˜Kšœœ˜Kšœœ˜K˜K˜Kšœœ˜ Kšœ œ˜Kšœœœ˜Kš œœœœœ˜Kšœœœœ˜Kšœ œœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ œœœ˜+Kšœœ˜'Kšœ œ˜K˜—head™š ž œœœœœœ˜:Kšœœ˜ Kšœ œ˜Kšœ)˜)Kšœ˜K˜Kšœœœ˜%Kšœœœ˜)Kšœ œœ˜Kšœ!˜!Kšœœœœ˜Kšœ œœ˜Kšœ œ˜Kš œœœœ#œ˜sKšœ8œ˜Ašœ(œœ˜=Kšœ œœ&œ*˜dKšœ˜—Kšœ:˜:Kšœœ˜šœ*œœ˜?Kšœœ!˜.Kšœœ&˜;Kšœ˜—šœ*œœ˜?Kšœœ˜7Kšœ˜—šœ*œœ˜?Kšœœ˜4Kšœ˜—Kšœœœ˜,Kšœ-˜-Kšœ ˜ šœœœ˜2Kšœ˜Kšœ˜—Kšœ˜Kšœœœ-˜Gš œ ˜Kšœ;˜;Kšœ œ œ˜#Kšœ œ˜šœ œ˜Kšœ˜Kšœ˜šœ*œ œ˜AKšœ-˜-Kšœ œ˜Kšœ œ˜šœœ œ˜Kšœ,œ œ ˜LJšœ-˜-K˜—Kšœ˜—Kšœœ˜K˜—šœ˜Kšœ˜šœœœ˜1Kšœ˜Kšœ˜—Kšœœ#œ œ˜?Kšœ œœœ ˜EK˜—Kš˜—šœ*œœ˜?šœ6œ œ˜MKšœe˜eKšœœ˜Kšœ˜—Kšœ˜—Kšœ>˜>Kšœœœœœ6œ$œœ˜ÞK˜K˜—š žœœ;œœœ˜š ž œœ œœœ˜@Kšœœœœ˜ Kšœ œœ˜Kšœ6˜6šœœ-œ˜>Kšœ`˜`šœ1œ œ˜IKšœ4˜4Kšœœ˜šœ=œ œ˜VKšœ#˜#Kšœ˜—Kšœ˜—K˜—K˜—K˜šœ(œœ˜=K™/šœœœ˜!Kšœœ˜1KšœB˜BK˜—šœ˜KšœœH˜dKšœœ˜)K˜—Kšœ˜—K˜K˜—šž œœœ œœœœœœ™zK™=šœ1œ œ™Išœ=œ œ™VKšœ™Kšœœœ™Kšœ™—Kšœ™—K™K™—šž œœ˜ Kšœœ˜šœ&œœ˜:Kšœœ˜šœ*œœ˜?šœœ˜%Kšœœ˜ Kšœ˜K˜—Kšœ˜—šœœ˜Kšœœ˜šœ*œœ˜?šœœ˜%Kšœœ˜ Kšœ˜K˜—Kšœ˜—Kšœœœ˜>K˜—Kšœ˜—K˜K˜—šž œœ œœ˜Všœ0œ œ˜Kšœœœ#˜2Kšœ%˜%šœœ œ˜šœœ˜$Kšœ œ˜Kšœ œ˜!Kšœ œ˜K˜—Kšœ˜——Kšœ˜—KšœŸ˜K˜K˜—šž œœœœ˜Wšœ,œ œ˜Fšœœœ˜%Kšœ œ˜.Kšœ œ!˜2Kšœœ˜K˜—šœœœ˜%Kšœ œ˜.Kšœ œ!˜2Kšœœ˜K˜—Kšœ˜—KšœŸ˜K˜—š žœœ&œœœ˜\Kšœ ˜ Kšœ˜Kšœ˜šœ3œ œ˜MKšœ-˜-Kšœ˜—šœ7œ œ˜PKšœœ˜Kšœ˜—šœ7œ œ˜PKšœœ˜Kšœ˜—šœ0œ œ˜IKšœœ˜Kšœ˜—šœ7œ œ˜PKšœœœ˜"Kšœœ ˜Kšœ œ˜!Kšœ˜Kšœœ˜šœ œ˜K˜&K˜'Kšœ˜—šœ7œ œ˜PKšœœ˜Kšœ˜—šœ.œ œ˜Fš œœœœœ˜]Kšœœ˜Kšœ œ!˜2K˜—š œœœœœ˜^Kšœœ˜Kšœ œ!˜2K˜—Kšœ˜—šœ7œ œ˜PKšœœ˜Kšœ˜—Kšœ œ˜Kšœ˜—K˜K˜—š ž œœœ œœ™_Kšœ;™;Kšœ)™)Kšœ™Kšœœ™Kšœ4™4Kšœ2™2Kšœ2™2šœ œ%™6Kšœ$™(—šœ ™™Kšœ™Kšœ œ ™Kšœ œ ™Kšœ@™@Kšœ™šœœ™Kšœœ™!Kšœœ™šœ™ Kšœ@™@Kšœ™K™——Kšœ™—™Kšœ™Kšœ œ ™Kšœ œ ™KšœK™KKšœ™šœœ™Kšœœ™!Kšœœ™šœ™ KšœK™KKšœ™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˜—š ž œœ œ œœ˜bK˜Kšœœ˜Kšœœ˜Kšœ ˜ šœ,œ œ˜DKšœ2˜2šœœ˜K˜/Kšœ œ˜šœœ˜šœ3œ œ˜LKšœ1˜1Kšœ˜—Kšœœ˜Kšœ˜Kšœ ˜ K˜—Kšœœ˜2K˜—Kšœ˜—K˜K˜—šž œœ œœ˜3Kšœ œ˜šœ.œ œ˜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˜—šž œ œ˜1Kšœœ˜*Kšœœ*˜FKšœœ'˜DKšœœ'˜BKšœœ'˜@Kšœœ*˜JKšœœ*˜Išœœœ˜-Kšœœ'˜IKšœ#œ'˜MKšœ"œ'˜LKšœ˜—K˜K˜K˜—šžœœ#œœ"™tKšœœœ™<šœœœ™šœœœ™Kšœ/™/Kšœ™—Kšœ™—Kšœ™"K™K™—šžœœ#œœ"˜tKšœœœ˜<šœœœ˜šœ˜ Jš œœœœœ$˜?Jš œ œœœœ&˜CJš œ œœœœ˜/š˜Jšœ˜Jšœœœ˜Jšœœœ˜Jšœœœ˜!Jšœ˜—Jšœ˜—Kšœ˜—Kšœ˜"K˜K˜—šžœœMœœ#˜¡KšœœœH˜sšœœœ˜'šœœœ™'Kšœ0™0Kšœ™—šœ˜ Jš œœœœœ%˜AJš œ œœœœ2˜OJš œ œœœœ˜/š˜Jšœ˜Jšœœœ˜Jšœœœ˜Jšœœœ˜!Jšœ˜—Jšœ˜—Kšœ˜—KšœH˜NK˜K˜—šžœœ%œ˜LKšœ(˜(Kšœ;˜;Kšœ˜Kšœ œ˜Kšœ œœ˜Kšœœ˜,Kš œ œœœŸ$˜Fšœ2œ œ˜KKšœ˜Kšœ˜Kšœ˜—Kšœ˜šœ2œ œ˜KKšœ˜Kšœ˜Kšœ˜—šœœ˜ Kšœ œ!˜-Kšœ œ˜+Kšœœ˜)Kšœœ˜'K˜—šœ˜Kšœ ˜ Kšœ"˜"Kšœ˜Kšœ˜K˜K˜—šœ2œ œ˜KKšœQ˜QKšœ˜—šœ2œ œ˜KKšœQ˜QKšœ˜—šœ.œ œ˜HKšœœ˜Kšœ˜—K™.šœœœ ˜$Kšœœœœ˜IKšœ˜šœ œœ ™'Kšœ™Kšœ™—šœ˜ Jš œ œœœœ˜8Jš œ œœœœ˜2š˜Jšœ˜Jšœœœ˜ Jšœœœ˜!Jšœ˜—Jšœ˜—Kšœ˜—šœ.œ œ˜HKšœœœ˜UKšœ˜—Kšœ4œ™:šœœœ˜,šœœ˜Kšœ œ˜Kšœ˜K˜—Kšœ˜—Kšœ œœ˜Kšœ œœ™K™5šœœœ˜'šœœœ˜,Kšœ0˜0Kšœ˜—Kšœ˜—K™/šœ2œ œ˜KKšœj˜jKšœ˜—Kšœ=™=šœœ˜Kšœ œ$˜0šœœœ˜'Kšœ ˜ Kšœ˜—K˜—Kšœ;˜?Kšœ˜š œ œœœœ˜9Kšœ œœœ˜šžœ˜&Kšœ>™>Kšœ œ˜K˜Kšœ œœ˜Kšœ/˜/Kšœœ˜šœ/œ œ˜FKšœ;˜;Kšœ˜—K˜—Kšœ(œ ˜4Kšœ2˜2Kšœ.˜.Kšœ˜K˜K˜—šžœœœ#œ ˜QK˜K˜Kšœ!˜!Kšœ)˜)Kšœ7˜7Kšœ)˜)Kšœ7˜7Kšœ=˜=Kšœ˜Kšœ+˜+Kšœ&˜&KšœEœ$œ œ/˜ªKšœŸ˜K˜—š Ïbœœœœœœ˜>Kšœœ˜ Kšœ˜Kšœ8œ ˜[Kšœ%˜%KšœBœ˜IKšœ˜K˜K˜—šžœœœ*œ˜bKšœ˜Kšœ˜Kšœœ˜%Kšœ!˜!Kšœ)˜)Kšœ7˜7Kšœ)˜)Kšœ7˜7Kšœ œœ$˜9Kšœ.˜.Kšœ?˜?Kšœ˜Kšœ+˜+K˜——™šžœœ,œ˜VKšœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜Kšœ2˜2Kš œ œœœœœ˜Ašœ.œ œ˜FKšœ˜Kšœ˜—K˜šœ,œ œ˜DKšœ˜Kšœ œ˜šœ1œ œ˜IKšœœ œœ˜8Kšœ˜—šœ œ˜šœ5œ œ˜NKšœœœ˜&Kš œœœœœ4˜‚šœ>œ œ˜WKšœœ2˜DKšœ˜—Kš˜Kšœ˜——Kšœ˜—Kšœœ˜Kšœ1˜1K˜K˜—š  œœœœœœ˜AKšœ œ˜Kšœœ˜)Kšœœ˜4šœ*œœ˜?šœœœ˜!Kšœ.˜.Kšœ˜K˜—Kšœ˜—Kšœ(Ÿ$˜LKšœ&˜&K˜K˜—šžœœœ6œœœœ'˜®šž œ˜*Kšœ œ˜šœ/œ œ˜HKšœœœ˜$Kšœ˜—Kšœœ˜Kšœ œœ˜šœ/œ œ˜FKšœ=˜=Kšœ˜—K˜—šž œ˜)Kšœ œ˜Kšœœœ)˜FK˜—šžœœ˜)Kšœg˜gJšœœ˜,Jšœœ3˜AJšœ˜šœ ˜šœ˜Kšœ œ ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœ˜Jšœ œ˜ Jšœ˜Kšœœ˜6Kšœœ˜1Jšœ˜—šœœœ˜*Kšœœ˜2šœ,˜1šœ$œ˜-Kšœ˜Kšœ˜Kšœ˜K˜—Kšœ˜—Jšœ˜——K˜—Kšœœ˜#Kšœ œ˜Kšœœ˜!Kšœ/˜/Kšœ/˜/Kšœ1˜1Kšœ˜Kšœœ-˜LKšœ/˜/Kšœ1˜1Kšœ5˜5Kšœ3˜3KšœŸ˜#Kšœ!˜!KšœE˜EKšœ6˜6Kšœ7˜7Kšœ7˜7Kšœ.˜.šœ'œœ˜Kšœ˜Kšœœ˜ Kš˜Kšœ˜—Kšœ˜—šœœ˜Kšœb™bKšœœ ˜>Kšœœ˜+K˜ K˜—Kšœ˜—šœœœ˜Kšœ?™?Kšœ˜K˜—Kšœ˜—Kšœ˜Kšœ œœ<œ œœ œ˜Kšœ˜—šœ,œ œ˜Dšœœ˜%Jšœ ˜ Jšœ˜Jšœ˜Jš˜J˜—Kšœ˜—Kšœœ˜*K˜K˜—š žœœœœTœ˜‘Kšœ™Kšœœ™Kšœ˜Kšœ;™;Kšœœœœ™+Kšœœ˜#Jšœ œœœ˜JšœE˜EKšœ^˜^Kšœ7˜7Kšœ9˜9Kšœ6™6K˜K˜—šž œœEœ˜oKšœ˜Kšœœ˜K˜Kšœ0˜0Kšœœœœ˜+Kšœœ˜#šœ,œœ˜BKšœ)œ˜-Kšœ˜—šœ,œœ˜BKšœœ˜Kšœ˜—šœ.œ œ˜HKšœ˜Kšœ˜—Kšœ*˜*Kšœ˜Kšœ˜šœ2œ œ˜KKšœœ˜Kšœ˜—Kšœ,˜,K˜K˜—šž œœœ˜7Kšœœœ˜šœœœ˜'K˜šœ5œ œ˜Mšœ=œ œ˜VKšœœœ˜4Kšœ˜—Kšœ˜—KšœŸ=˜DKšœ˜—K˜K˜—šž œœœ˜JKšœ œœ˜Kšœ˜Kšœœ˜šœ2œ œ˜KKšœ˜Kšœ˜—šœ2œ œ˜KKšœ˜Kšœ˜—Kšœœœœ˜&Kšœ˜šœ ˜Kšœ˜KšœV˜VKšœ˜—K˜K˜—šžœœEœœ œœ!œœ˜±šœ3œ œ˜LKšœ˜Kšœ!˜!Kšœ œœ˜š œœœ,œ œ˜\Kšœ˜Kšœ˜šœ ˜Kšœ&˜&Kšœ&˜&Kšœœ˜—šœœœ˜3Kšœœœ*˜BKšœœ˜/Kšœ œ˜Kšœ œ˜K˜—Kšœ˜—Kšœ œœ˜CKšœ˜—Kšœ)˜)K˜K˜—šžœœ*œ˜VKšœ˜Kšœ˜K˜Kšœœ˜K˜Kšœœ˜ Kšœ*™*šœ2œ œ˜Kšœœ˜Kšœœ˜ Kšœ˜Kšœ˜—Kšœ˜—Kšœœœ˜Kšœ-™-Kšœ œœœ˜K™(Kšœ#˜#šœ0œ œ˜Išœ!œ˜*Kšœ˜Kšœ˜K˜—Kšœ˜—šœ˜ Kšœœ˜ ˜K˜ Kšœœ˜šœ'œ œ˜AKšœ&˜&Kšœ"˜"Kšœ˜—Kšœ!œœ˜MK˜—šœ˜ Kšœ˜šœ2œ œ˜Kšœœ˜Kšœœ˜š œœœœ˜šœœœ˜'K˜Kšœ œœœ˜AKšœ˜—K˜šœ,œ œ˜DKšœ;˜;Kšœœ˜Kšœœ˜Kšœ˜—Kšœœ˜—Kšœ˜K˜—Kšœ˜K˜———šœ œ œ˜K™_Kšœ˜šœ.œ œ˜HKšœœ˜Kšœ˜—Kšœ.œœ˜:Kšœ˜Kšœœ œ ˜-K˜—K˜K˜—š žœœœœœ œ˜aš  œœ œœ˜5Kšœ>™>Jšœœ˜ šœ2œ œ˜KKšœ˜Kšœ˜šœœ˜!šœœœ˜šœ œœ˜1šœœœ ˜Kšœ&˜&Kšœ˜—Kšœ-˜-Kšœ˜K˜—Kšœ˜——Kšœ˜—K˜K˜—Kšœ œœ ˜!Kšœ œœ œœœœœ˜dKšœœ˜%K˜ Kšœœ˜šœœœ˜Kšœ˜Kšœ˜—Kšœ0˜0K˜~Kšœ ˜ Kšœ˜šœœœ˜šœœœ˜"šœBœ œ˜[Kšœœ˜Kšœ˜—šœBœ œ˜[Kšœœ˜Kšœ˜—šœ>œ œ˜XKšœœ˜Kšœ˜—Kšœd˜dKšœÝ˜ÝKšœ œœ0˜@Kšœ œ!˜0Kšœ˜—Kšœ˜—K˜K˜—š žœœœœœ+˜gš  œ˜*Kšœ>™>Kšœœ˜ Kšœ œ˜Kšœœœœ˜ Kšœ œœ˜Kšœ#˜#Kšœœœ˜"šœœœ˜šœ œœ˜*šœœœ ˜Kšœ&˜&Kšœ˜—Kšœ˜Kšœ˜K˜—Kšœ˜—K˜K˜—Kšœ œœ ˜!Kš œ œœ œœœœ˜UKšœœ˜%šœœœ˜Kšœ˜Kšœ˜—Kšœ6˜6šœœœ˜Kšœœœ œ"˜SKšœ˜—Kšœ!˜!K˜K˜——™ šž œœ˜*Kšœœœ˜TKš œ%œœœœ˜‹K˜K˜—šžœ œ!˜6Kšœœœœ˜Kšœ6œœœœœœœ œœ œœ(œ'œ)Ÿœ˜×K˜K˜—šž œœœ#˜9Kšœœœœ˜Kšœ œ$œ˜[šœ.œœ˜@Kšœ œœ˜LKšœ˜—˜K˜——šž œ œ)˜Bšœ,œ œ˜DKšœ"˜"Kšœ˜—K˜K˜—šž œ œ+˜EKšœ˜šœ0œ œ˜IKšœ$˜$Kšœ˜—K˜K˜—šž œ œ'˜?Kšœ1˜3š œ œœ(œ œ˜PKšœ(˜(Kšœ˜—K˜K˜—šž œ œ)˜BKšœ˜šœ,œ œ˜DKšœ&˜&Kšœ˜—K˜K˜—šžœœœ˜?šœ0œ œ˜IKšœ˜Kšœ˜—K˜K˜—šž œœœœ˜Kš  œ˜+Kšœ>™>Kšœ œ˜Kšœœ œ˜/K˜—Kšœ6˜6K˜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šœ3˜3K˜K˜—š ž œœœ œœ˜TKšœœF˜gKšœœ5˜BK˜K˜—šž œ œ œ œ˜TKšœ=˜=K˜K˜—š žœœœœœ˜NKš œœœœœ˜)Kš œœœœœ+˜tKšœ˜K˜K˜—šžœ œœœ˜Kšœœœ,˜DKšœ:˜>K˜K˜—šžœœœœ˜EKšœ˜K˜K˜—šÐbn œœœ˜/Kšœ ˜ šœ œ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜—K˜K˜—š¡ œœœ˜/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šœ2˜2Kšœ#˜#Kšœ$˜$šœœ˜Jšœ+œ˜1Jšœœ˜!Jšœœ˜Jšœ˜Kšœ'˜'Kšœ œ˜Kšœ˜—Kšœœ˜K˜——™šžœœsœœœœœœ˜¾šžœ˜ Kšœ˜K˜—Kšœœœ˜Kšœ œ ˜ Kš œœœœœ˜