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; PathList: TYPE = LIST OF NodeList; Caminos: TYPE = LIST OF Camino; Camino: TYPE = LIST OF RECORD[set: Set, node: Node]; Delay: TYPE = REF DelayRec; DelayRec: TYPE = RECORD [tup, tdown: ps]; ExtendedCircuit: TYPE = REF ExtendedCircuitRec; ExtendedCircuitRec: TYPE = RECORD [circuit: Circuit, data: REF ANY]; gndName: Rope.ROPE _ "public.Gnd"; vddName: Rope.ROPE _ "public.Vdd"; VddVal: mVolt _ 5000.0; GndVal: mVolt _ 0.0; VtVal: mVolt _ 2000.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 _ 50.0; tInc: ps _ 100.0; 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 iFetList: FetList _ iNodeList.first.fetList, iFetList.rest UNTIL iFetList = NIL DO IF NOT iFetList.first.done THEN IF iFetList.first.gate # iNodeList.first THEN { fetList _ CONS[iFetList.first, fetList]; set.lFets _ CONS[iFetList.first, set.lFets]; iFetList.first.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 FROM nE => status _ (vGate1 >= threshold); pE => status _ (vGate1 <= threshold); ENDCASE => ERROR; IF status#fet.switch THEN { UNTIL t1-t0<timeSpecs DO ti: ps _ (t0 + t1)/2; IF CheckSwitch[fet, t0, ti].change THEN t1 _ ti ELSE t0 _ ti; ENDLOOP; change _ TRUE; changet _ t1; }; }; FlipSwitches: PROC [fetList: FetList, t0, t1: ps] RETURNS [finished: BOOLEAN _ TRUE, newt: ps] ~ { t: ps; change: BOOLEAN; previousFets: FetList _ NIL; newt _ t1; FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList=NIL DO [change, t] _ CheckSwitch[iFetList.first, t0, t1]; IF change THEN { iFetList.first.switch _ ~iFetList.first.switch; finished _ FALSE; IF t<newt-timeSpecs THEN { FOR iprevList: FetList _ previousFets, iprevList.rest UNTIL iprevList=NIL DO iprevList.first.switch _ ~iprevList.first.switch; ENDLOOP; previousFets _ NIL; t1 _ t; newt _ t; }; previousFets _ CONS[iFetList.first, previousFets]; }; ENDLOOP; }; ResetSets: PROC [set: Set] RETURNS [ok: BOOLEAN]~ { set.done _ FALSE; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO iFetList.first.switch _ TRUE; ENDLOOP; RETURN[TRUE]; }; Coeff: PROC [matrix: LinearSystem.MatrixN, fet: Fet] ~ { i1, i2: CARDINAL; i1 _ fet.ch1.index; i2 _ fet.ch2.index; matrix[i1][i1] _ matrix[i1][i1] - fet.rOnInv/fet.ch1.cap; matrix[i1][i2] _ matrix[i1][i2] + fet.rOnInv/fet.ch1.cap; matrix[i2][i2] _ matrix[i2][i2] - fet.rOnInv/fet.ch2.cap; matrix[i2][i1] _ matrix[i2][i1] + fet.rOnInv/fet.ch2.cap; fet.done _ TRUE; }; XchIndexes: PROC [matrix: LinearSystem.MatrixN, i, j: CARDINAL] RETURNS [xChged: LinearSystem.MatrixN] ~ { rowN: LinearSystem.RowN; elt: REAL; xChged _ LinearSystem.Copy[matrix]; rowN _ xChged[i]; xChged[i] _ xChged[j]; xChged[j] _ rowN; FOR k: INTEGER IN [0..xChged.nrows) DO elt _ xChged[k][i]; xChged[k][i] _ xChged[k][j]; xChged[k][j] _ elt; ENDLOOP; }; 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; vTemp: 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; matrix _ NEW[LinearSystem.MatrixSeq[nNodes]]; cstTerm _ NEW[LinearSystem.VecSeq[nNodes]]; vInit _ NEW[LinearSystem.VecSeq[nNodes]]; tau _ NEW[LinearSystem.VecSeq[nNodes]]; 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 matrix[iRow] _ NEW[LinearSystem.VecSeq[nNodes]]; cstTerm[iRow] _ 0.0; FOR iColumn: CARDINAL IN [0..nNodes) DO matrix[iRow][iColumn] _ 0.0; 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 _ LinearSystem.SolveN[LinearSystem.Copy[matrix], cstTerm, nActiveNodes]; lastAct _ nActiveNodes-1; IF lastAct#0 THEN FOR i: CARDINAL IN [0..nActiveNodes) DO subMatrix _ XchIndexes[matrix, i, lastAct]; 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 _ LinearSystem.SolveN[LinearSystem.Copy[subMatrix], cstTerm, lastAct]; 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] }; 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] ~ { plot: PlotGraph.Plot ~ NARROW[data]; rect: PlotGraph.Rectangle _ [plot.lowerBounds.x, 0.0, plot.upperBounds.x - plot.lowerBounds.x, 5000.0]; SimulateSet[ref, t, plot.data]; PlotGraph.RefreshPlot[plot: plot, within: rect, eraseFirst: TRUE]; }; InteractiveSimulate: PUBLIC PROC [circuit: Circuit, watchList: NodeList, from: ps _ 0.0] ~ { t: ps; gndNode, vddNode: Node; plot: PlotGraph.Plot; circuit.info.nbOfSimulations _ 0; gndNode _ NodeFromRope[gndName, circuit]; gndNode.history _ Schedule.CreateHistory[from, GndVal]; vddNode _ NodeFromRope[vddName, circuit]; vddNode.history _ Schedule.CreateHistory[from, VddVal]; plot _ DrawNodeList[watchList, circuit]; circuit.agenda _ Schedule.CreateAgenda[SimulateAndPlot, plot]; t _ Initialize[circuit]; t _ Schedule.ExecuteAgenda[circuit.agenda]; }; NextSets: PROC [c: RECORD[set: Set, node: Node], 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[NARROW[value, Camino]]; FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO IF iFetList.first.gate.history#NIL THEN { boolVal _ GetNode[iFetList.first.gate]; iFetList.first.switch _ IF boolVal THEN iFetList.first.type=pE ELSE iFetList.first.type=nE; } ELSE iFetList.first.switch _ TRUE; ENDLOOP; subSets _ ClipSet[set]; FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO boolVal _ FALSE; FOR iFetList: FetList _ iSetList.first.lFets, iFetList.rest UNTIL iFetList=NIL DO IF iFetList.first.gate=node THEN {boolVal _ TRUE; EXIT}; ENDLOOP; IF boolVal THEN { FOR iNodeList: NodeList _ iSetList.first.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO FOR iSetList2: SetList _ iNodeList.first.setList, iSetList2.rest UNTIL iSetList2=NIL DO camino _ CONS[[iSetList2.first, iNodeList.first], camino]; ENDLOOP; ENDLOOP; }; ENDLOOP; KillSetList[subSets, TRUE]; [] _ HashTable.Store[setCache, set, camino]; }; 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; SimulateSet[ref, t, extdCirc.circuit]; }; MaxFreqEvaluate: PUBLIC PROC [circuit: Circuit, clkList: NodeList, from: ps _ 0.0] RETURNS [worst: ps _ 0.0, setList: SetList]~ { 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[LIST[[set, set.inList.first]], liveCaminos]; }; n: INT _ 0; found, finished: BOOLEAN _ FALSE; gndNode: Node = NodeFromRope[gndName, circuit]; vddNode: Node = NodeFromRope[vddName, circuit]; startTable: HashTable.Table _ HashTable.Create[]; caminos, liveCaminos: Caminos _ NIL; camino: Camino _ NIL; extdCirc: ExtendedCircuit _ NEW[ExtendedCircuitRec _ [circuit, startTable]]; setCache: HashTable.Table _ HashTable.Create[]; setListCache: HashTable.Table _ HashTable.Create[]; hist: Histograms.Histogram _ Histograms.Create1D[100.0, 0.0]; --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, GndVal]; 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]; UNTIL liveCaminos=NIL DO n2: INT _ 0; oldCaminos: Caminos _ liveCaminos; liveCaminos _ NIL; FOR iCaminos: Caminos _ oldCaminos, iCaminos.rest UNTIL iCaminos=NIL DO thisCamino: Camino _ iCaminos.first; nextOnes: Camino _ NextSets[thisCamino.first, setCache]; oneAdded: BOOLEAN _ FALSE; FOR iNext: Camino _ nextOnes, iNext.rest UNTIL iNext=NIL DO found _ FALSE; FOR iCamino: Camino _ iCaminos.first, iCamino.rest UNTIL iCamino=NIL DO IF iNext.first=iCamino.first THEN {found _ TRUE; EXIT} ENDLOOP; IF ~found THEN { oneAdded _ TRUE; liveCaminos _ CONS[CONS[iNext.first, thisCamino], liveCaminos]; n2 _ n2+1; }; ENDLOOP; IF ~oneAdded THEN { caminos _ CONS[thisCamino, caminos]; n _ n+1; }; ENDLOOP; KillCaminos[oldCaminos]; IO.PutF[StdOut, "total %d, in use %d\n", IO.int[n], IO.int[n2]] ENDLOOP; setCache _ HashTable.Create[HashTable.GetSize[setCache]]; FOR iCaminos: Caminos _ caminos, iCaminos.rest UNTIL iCaminos=NIL DO delay: Delay _ ThruTime[iCaminos.first, setCache, setListCache]; IF delay.tup>worst OR delay.tdown>worst THEN { camino _ iCaminos.first; worst _ MAX[delay.tup, delay.tdown]; }; Histograms.ChangeTransformed[hist, delay.tup]; Histograms.ChangeTransformed[hist, delay.tdown]; ENDLOOP; [] _ Histograms.Show[hist]; setList _ LIST[camino.first.set]; camino _ camino.rest; {iSetList3: SetList _ setList; UNTIL camino=NIL DO iSetList3.rest _ LIST[camino.first.set]; camino _ camino.rest; iSetList3 _ iSetList3.rest; ENDLOOP;} }; ThruTime: PROC [camino: Camino, setCache, setListCache: HashTable.Table] RETURNS [delay: Delay] ~ { value: HashTable.Value; found: BOOLEAN; setDelay, prevDelay: Delay; [found, value] _ HashTable.Fetch[setListCache, camino]; IF found THEN RETURN[NARROW[value, Delay]]; delay _ NEW[DelayRec _ [0.0, 0.0]]; setDelay _ DelayOfSet[camino.first.set, setCache]; IF camino.rest=NIL THEN RETURN; prevDelay _ ThruTime[camino.rest, setCache, setListCache]; delay.tup _ setDelay.tup+prevDelay.tdown; delay.tdown _ setDelay.tdown+prevDelay.tup; [] _ HashTable.Store[setListCache, camino, delay]; }; DelayOfSet: PROC [set: Set, setCache: HashTable.Table] 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 iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO IF iNodeList.first.setList#NIL THEN { FOR iNList: NodeList _ set.inList, iNList.rest UNTIL iNList=NIL DO iNList.first.done _ FALSE; 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 iFetList.first.switch _ TRUE; ENDLOOP; [tup, tdown] _ EvalRC[iNodeList.first, set]; delay.tup _ MAX[delay.tup, tup]; delay.tdown _ MAX[delay.tdown, tdown]; } ENDLOOP; [] _ HashTable.Store[setCache, set, delay]; }; FindSetOfNode: PROC [node: Node] RETURNS [set: Set] ~ { found: BOOLEAN _ FALSE; FOR iFetList: FetList _ node.fetList, iFetList.rest UNTIL iFetList=NIL DO FOR iSetList: SetList _ iFetList.first.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; pathList _ LIST[LIST[node]]; node.index _ 1; UNTIL finished DO level _ level+1; oldPathList _ pathList; [finished, pathList] _ IncPaths[set.lFets, oldPathList, level]; ENDLOOP; }; IncPaths: PROC [fetList: FetList, oldPathList: PathList, level: NAT] RETURNS [finished: BOOLEAN _ TRUE, newPathList: PathList _ NIL] ~ { nodeList: NodeList; terminal: BOOLEAN; FOR iPathList: LIST OF NodeList _ oldPathList, iPathList.rest UNTIL iPathList=NIL DO nodeList _ iPathList.first; terminal _ TRUE; IF ~nodeList.first.input THEN FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList=NIL DO IF iFetList.first.ch1=nodeList.first THEN IF iFetList.first.ch2.index=0 OR iFetList.first.ch2.index>=level THEN { newPathList _ CONS[ CONS[iFetList.first.ch2, nodeList], newPathList]; iFetList.first.ch2.index _ level; terminal _ FALSE; finished _ FALSE; }; IF iFetList.first.ch2=nodeList.first THEN IF iFetList.first.ch1.index=0 OR iFetList.first.ch1.index>=level THEN { newPathList _ CONS[ CONS[iFetList.first.ch1, nodeList], newPathList]; iFetList.first.ch1.index _ level; finished _ FALSE; terminal _ FALSE; }; ENDLOOP; IF terminal THEN newPathList _ CONS[nodeList, newPathList]; ENDLOOP; }; EvalRC: PROC [node: Node, set: Set] RETURNS [tup, tdown: ps _ 0] ~ { subSets: SetList; pathList: PathList; 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.input THEN n _ n+1; ENDLOOP; IF n=0 THEN ERROR; IF n#1 THEN { 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 iFetList: FetList _ iNodeList.first.fetList, iFetList.rest UNTIL iFetList = NIL DO iFetList.first.switch _ IF bool THEN iFetList.first.type=pE ELSE iFetList.first.type=nE; ENDLOOP; subSets _ ClipSet[set]; FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO [tempup, tempdown] _ EvalRC[node, iSetList.first]; 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, NIL]; 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<data.table[ind+1].x THEN { FOR ind2: NAT IN [0..ind) DO data.table[ind2] _ data.table[ind2+1]; ENDLOOP; data.table[ind] _ [node.cap, 0.0, node, set]; EXIT; }; ENDLOOP; ENDLOOP; }; DataType: TYPE = REF DataTypeRec; DataTypeRec: TYPE = RECORD [table: SEQUENCE size: NAT OF RECORD [x, y: REAL, node: Node, set: Set]]; data: DataType _ NEW[DataTypeRec[n]]; node: Node; totalC: REAL _ 0.0; FOR ind: NAT IN [0..n) DO data.table[ind].x _ -1e30; ENDLOOP; [] _ VisitLibrary[circuit.library, FindFatOnes]; IO.PutF[StdOut, "\ntotal Capacitance :%5.2fpf (C*V*V/f=%g W @ 25MHz)", IO.real[totalC], IO.real[totalC*VddVal*VddVal*25e-12]]; fatest _ 0.0; SetTime[0.0, circuit]; FOR ind: NAT IN [0..n) DO IF data.table[ind].node#NIL THEN { FOR iNodeList: NodeList _ data.table[ind].set.inList, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ FALSE; ENDLOOP; FOR iNodeList: NodeList _ data.table[ind].set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO iNodeList.first.done _ TRUE; ENDLOOP; FOR iFetList: FetList _ data.table[ind].set.lFets, iFetList.rest UNTIL iFetList = NIL DO iFetList.first.switch _ TRUE; ENDLOOP; [data.table[ind].x, data.table[ind].y] _ EvalRC[data.table[ind].node, data.table[ind].set]; IO.PutF[StdOut, "\n%g (%5.3fpf), tup: %4.1fns, tdown: %4.1fns", IO.rope[RopeFromNode[data.table[ind].node, circuit]], IO.real[data.table[ind].node.cap], IO.real[data.table[ind].x*0.001], IO.real[data.table[ind].y*0.001]]; fatest _ MAX[MAX[data.table[ind].x, data.table[ind].y], fatest]; fatNodes _ CONS[data.table[ind].node, fatNodes]; }; ENDLOOP; }; LastTime: PUBLIC PROC [circuit: Circuit, n: NAT _ 1] RETURNS [veryLastTime: ps, slowNodes: NodeList]~ { FindSlowOnes: HashTable.EachPairAction ~ { t, v: REAL; node: Node ~ NARROW[value]; IF node.history=NIL THEN RETURN; IF node.input THEN RETURN; [t, v] _ SettledValuesOfNode[node]; IF t <data.table[0].t THEN RETURN; FOR ind: NAT IN [0..n) DO IF ind=n-1 OR t<data.table[ind+1].t THEN { FOR ind2: NAT IN [0..ind) DO data.table[ind2] _ data.table[ind2+1]; ENDLOOP; data.table[ind] _ [t, node]; RETURN; }; ENDLOOP; }; DataType: TYPE = REF DataTypeRec; DataTypeRec: TYPE = RECORD [table: SEQUENCE size: NAT OF RECORD [t: ps, node: Node]]; data: DataType _ NEW[DataTypeRec[n]]; FOR ind: NAT IN [0..n) DO data.table[ind].t _ -1e30; ENDLOOP; [] _ HashTable.Pairs[circuit.nodeTable, FindSlowOnes]; FOR ind: NAT IN [0..n) DO IF data.table[ind].node#NIL THEN slowNodes _ CONS[data.table[ind].node, slowNodes]; ENDLOOP; veryLastTime _ data.table[n-1].t; }; PrintSetType: PROC [setType: SetType] ~ { IO.PutF[StdOut, " %4d, %4d,", IO.int[setType.nFets[nE]], IO.int[setType.nFets[pE]]]; IO.PutF[StdOut, " %4d, %4d, %4d, %4d", IO.int[setType.nVddTran], IO.int[setType.nGndTran], IO.int[setType.nNodes], IO.int[setType.nInput]]; }; PrintFet: PUBLIC PROC [fet: Fet, circuit: Circuit] ~ { IF fet=NIL THEN RETURN; IO.PutFL[StdOut, "\n%g %g(%1.2d) g:%g, ch1:%g, ch2:%g", LIST[IO.rope[IF fet.type=pE THEN "P" ELSE "N"], IO.rope[IF fet.switch THEN "ON" ELSE "OFF"], IO.real[1.0/fet.rOnInv], IO.rope[RopeFromNode[fet.gate, circuit]], IO.rope[RopeFromNode[fet.ch1, circuit]], IO.rope[RopeFromNode[fet.ch2, circuit]]]]; -- more than 5 IO.Values => 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: PROC [camino: Camino, circuit: Circuit] ~ { IO.PutF[StdOut,"\n ~~"]; FOR iCamino: Camino _ camino, iCamino.rest UNTIL iCamino=NIL DO PrintNode[ iCamino.first.node, circuit]; ENDLOOP; }; PrintCaminos: 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; }; Watch: PUBLIC PROC [watchList: NodeList] ~ { FOR inodeList: NodeList _ watchList, inodeList.rest UNTIL inodeList=NIL DO inodeList.first.watched _ TRUE; ENDLOOP; }; 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>2500.0; }; 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 caminos.first _ NIL; 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]; KillFetList[node.fetList]; 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 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.TOS[]; IO.PutRope[StdOut, "\nMint loaded\n"]; END. ��v��MintImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Christian LeCocq January 14, 1987 4:32:25 pm PST RealFns, RealFns, 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; }; Swap: PROC [A: LinearSystem.MatrixN, b:LinearSystem.ColumnN, i, j, n: CARDINAL] ~ { tempRow: LinearSystem.RowN; tempReal: REAL; tempRow _ A[i]; A[i] _ A[j]; A[j] _ tempRow; tempReal _ b[i]; b[i] _ b[j]; b[j] _ tempReal; FOR k:CARDINAL IN [0..n) DO tempReal _ A[k][i]; A[k][i] _ A[k][j]; A[k][j] _ tempReal; 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<vCh2 THEN { chMin _ fet.ch1; chMax _ fet.ch2} ELSE {chMin _ fet.ch2; chMax _ fet.ch1}; SELECT fet.type FROM nE => { 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<timeSpecs DO ti: ps _ (t0 + t1)/2; IF CheckSwitch[fet, t0, ti].change THEN t1 _ ti ELSE t0 _ ti; ENDLOOP; change _ TRUE; changet _ t1; }; }; status is TRUE if fet is on. Version without substrate effect. Initialisations (no ALL[0.0] for sequences...) FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO FOR iFetList: FetList _ iNodeList.first.fetList, iFetList.rest UNTIL iFetList = NIL DO IF ~iFetList.first.done AND iFetList.first.switch THEN { IF iNodeList.first#iFetList.first.gate THEN Coeff[matrix, iFetList.first]; What about a gate connected to ch1 or ch2 ? }; ENDLOOP; ENDLOOP; If fixed voltages are not connected to the set then return. IF triState THEN RETURN[t]; Fixed voltages are removed from the system to solve. tFinal _ tFinal-1.0/matrix[i][i]; 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 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 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 Ê4j��˜�codešœ ™ Kšœ<™<K™0K™�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šœ œœœ ˜"Kšœ œœœ˜Kš œœœœœ˜4Kšœœœ ˜Kšœ œœ˜)Kšœœœ˜/Kš œœœœœ˜DK˜�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˜�—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šœ<œœ˜Všœœœ˜ šœ'œ˜/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˜�—šœS™SKšœ™Kšœ™Kšœ™K™Kšœ™Kšœ™Kšœ™Kšœ™šœ™Kšœ™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šœ9˜9Kšœ9˜9Kšœ9˜9Kšœ9˜9Kšœœ˜K˜K˜�—šž œœ&œœ#˜jKšœ˜Kšœœ˜ Kšœ#˜#Kšœ˜Kšœ˜Kšœ˜šœœœ˜&Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜K˜�—šžœœ%œ˜LKšœ(˜(Kšœ;˜;Kšœ˜Kšœœ˜Kšœ œœ˜Kšœœ˜,Kš œœœœŸ$˜Fšœ2œœ˜KKšœ˜Kšœ˜Kšœ˜—Kšœ˜šœ2œœ˜KKšœ˜Kšœ˜Kšœ˜—Kšœ œ!˜-Kšœ œ˜+Kšœœ˜)Kšœœ˜'šœ2œœ˜KKšœQ˜QKšœ˜—šœ2œœ˜KKšœQ˜QKšœ˜—šœ.œœ˜HKšœœ˜Kšœ˜—K™.šœœœ ˜$Kšœœ˜0Kšœ˜šœ œœ ˜'Kšœ˜Kšœ˜—Kšœ˜—šœ2œœ™Kšœ<œœ™Všœœœ™8Kšœ%œ™JK™+K™—Kšœ™—Kšœ™—šœ.œœ˜HKšœœœ˜UKšœ˜—K™;šœœœ˜,šœœ˜Kšœœ˜Kšœ˜K˜—Kšœ˜—Kšœ œœ˜Kšœ œœ™K™5šœœœ˜'Kšœ!™!šœœœ˜,Kšœ0˜0Kšœ˜—Kšœ˜—K™/šœ2œœ˜KKšœj˜jKšœ˜—Kšœ=™=šœœ˜Kšœ œ$˜0šœœœ˜'Kšœ ˜ Kšœ˜—K˜—KšœP˜TKšœ˜šœœœœœ˜9Kšœ+˜+KšœŸ˜6šœœœ˜"Kšœ˜šœœœ˜'Kšœ3˜3Kšœ˜—Kšœ˜—KšœN˜NKšœ˜šœœœ˜"KšœO˜OKšœ˜—šœœœ˜'KšœM˜MKšœ˜—Kšœ,˜,Kšœ˜Kš˜—Kšœ1˜5K™)Kšœ˜šœ2œœ˜KKšœr˜rKšœœœ˜ŸKšœ˜—K˜K˜�——™ šž œœœ˜>šžœ˜&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šœg˜gKšœ˜Kšœ<œ˜BK˜K˜�—šžœœ<˜\Kšœ˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ)˜)Kšœ7˜7Kšœ)˜)Kšœ7˜7Kšœ(˜(Kšœ>˜>Kšœ˜Kšœ+˜+K˜——™šžœœœ3œ˜hKšœ˜Kšœ˜Kšœ˜Kšœ œ˜Kšœ˜Kšœ2˜2Kšœ œœœ˜.šœ.œ œ˜Fšœœœ˜)Kšœ'˜'Kšœœ œœ˜[K˜—Kšœœ˜"Kšœ˜—K˜šœ,œ œ˜DKšœ œ˜šœ9œ œ˜QKšœœœœ˜8Kšœ˜—šœ œ˜šœ=œœ˜Všœ>œœ˜WKšœ œ-˜:Kšœ˜—Kš˜Kšœ˜——Kšœ˜—Kšœœ˜Kšœ,˜,K˜K˜�—š œœœœœœ˜AKšœœ˜Kšœœ˜)Kšœœ˜4šœ*œœ˜?šœœœ˜!Kšœ.˜.Kšœ˜K˜—Kšœ˜—Kšœ&˜&K˜K˜�—šžœœœ7œ'˜šžœ˜*Kšœ œ˜šœ/œœ˜HKšœœœ˜$Kšœ˜—Kšœœ˜Kšœ œœ˜šœ/œ œ˜FKšœ=˜=Kšœ˜—K˜—šžœ˜)Kšœœ˜Kšœœœ(˜?K˜—Kšœœ˜Kšœœ˜!Kšœ/˜/Kšœ/˜/Kšœ1˜1Kšœ œ˜$Kšœœ˜Kšœœ-˜LKšœ/˜/Kšœ3˜3Kšœ>Ÿ˜EKšœ!˜!KšœE˜EKšœ6˜6Kšœ7˜7Kšœ7˜7Kšœ.˜.šœ'œœ˜<Kšœœœ<˜[šœ6œ œ˜MKšœ=˜=Kšœ˜—Kšœ˜—Kšœ,˜,Kšœ.˜.šœ œ˜Kšœœ˜Kšœ"˜"Kšœœ˜šœ/œ œ˜GKšœ$˜$Kšœ8˜8Kšœ œœ˜šœ&œœ˜;Kšœœ˜šœ0œ œ˜GKšœœ œœ˜6Kšœ˜—šœœ˜Kšœœ˜Kšœœœ(˜?K˜ K˜—Kšœ˜—šœœ˜Kšœ œ˜$K˜K˜—Kšœ˜—Kšœ˜Kšœ=˜?Kšœ˜—K•StartOfExpansionZ[mod: HashTable.SeqIndex _ 17, equal: HashTable.EqualProc, hash: HashTable.HashProc]šœ9˜9šœ,œ œ˜DKšœ@˜@šœœœ˜.Jšœ˜Jšœœ˜$J˜—Jšœ.˜.Jšœ0˜0Kšœ˜—Kšœ˜Kšœ œ˜!Kšœ˜Kšœ˜šœœ˜Kšœœ˜(Kšœ˜Kšœ˜Kšœ˜ —K˜K˜�—šžœœ;œ˜cKšœ˜Kšœœ˜Kšœ˜Kšœ7˜7Kšœœœœ˜+Kšœœ˜#Jšœ2˜2Kšœ œœœ˜Kšœ:˜:Kšœ)˜)Kšœ+˜+Kšœ2˜2K˜K˜�—šž œœ'œ˜QKšœ˜Kšœœ˜K˜Kšœ0˜0Kšœœœœ˜+Kšœœ˜#šœ2œœ˜Kšœœœ˜%šœ,œœ˜BKšœœ˜Kšœ˜—šœ,œœ˜BKšœœ˜Kšœ˜—šœ.œœ˜HKšœœ˜Kšœ˜—Kšœ,˜,Kšœœ˜ Kšœœ˜&K˜—Kšœ˜—Kšœ+˜+K˜K˜�—šž œœœ˜7Kšœœœ˜šœ1œ œ˜Išœ@œ œ˜Xšœ=œœ˜VKšœœœ˜4Kšœ˜—Kšœ˜—KšœŸ=˜DKšœ˜—K˜K˜�—šžœœœ˜JKšœ œœ˜Kšœ˜Kšœœ˜šœ2œœ˜KKšœ˜Kšœ˜—šœ2œœ˜KKšœ˜Kšœ˜—Kšœœœ˜Kšœ˜šœ ˜Kšœ˜Kšœ˜Kšœ?˜?Kšœ˜—K˜K˜�—šžœœ2œœœœœœ˜ˆKšœ˜Kšœ œ˜šœœœ(œœ˜TKšœ˜Kšœœ˜šœœœ,œ œ˜bšœ#œ˜*šœœ!œ˜GKšœœœ-˜EKšœ!˜!Kšœœ˜Kšœœ˜K˜——šœ#œ˜*šœœ!œ˜GKšœœœ-˜EKšœ!˜!Kšœœ˜Kšœœ˜K˜——Kšœ˜—Kšœ œœ˜;Kšœ˜—K˜K˜�—šžœœœ˜DKšœ˜Kšœ˜Kšœœ˜K˜Kšœœ˜Kšœ*™*šœ2œœ˜Kšœœ˜Kšœœ˜ Kšœ˜Kšœ˜—Kšœ˜—Kšœœœ˜Kšœ-™-Kšœœœœ˜K™(Kšœ#˜#šœ0œœ˜IKšœœ ˜,Kšœ˜—Kšœœœ˜šœœ˜ Kšœ˜šœ2œœ˜Kšœœ˜Kšœœ˜š œœœœ˜šœ<œœ˜VKšœœœœ˜XKšœ˜—K˜šœ,œ œ˜DKšœ2˜2Kšœœ˜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šœ[˜[KšœÝ˜Ý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˜�—šžœœ'˜8Kšœ˜šœ(œ œ˜?Kšœ(˜(Kšœ˜—K˜K˜�—šžœœ)˜;Kšœ˜šœ,œ œ˜DKšœ&˜&Kšœ˜—K˜K˜�—šžœœœ˜?šœ0œœ˜IKšœ˜Kšœ˜—K˜K˜�—šžœœœ˜,šœ1œœ˜JKšœœ˜Kšœ˜—K˜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šœœ-˜4Kšœ˜K˜K˜�—šžœœœœ˜KKšœ-˜-Kšœ.˜.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˜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šœ2˜2Kšœ#˜#Kšœ$˜$šœœ˜Jšœ+œ˜1Jšœœ˜!Jšœœ˜Jšœ˜Kšœ'˜'Kšœœ˜Kšœ˜—Kšœœ˜K˜——™šžœœsœœœœœœ˜¾šžœ˜ Kšœ˜K˜—Kšœœœ˜Kšœ œ ˜ Kš œœœœœ˜<Kš œœœœœ ˜6KšœA˜AK˜K˜�—š ž œœœ,œœ˜UKšœœ˜%Kšœg˜gKšœœœ!˜.Kšœ˜šœœ˜šœ˜šœœœ˜'Kšœ˜Kšœ˜Kšœ˜K˜—Kšœ ˜ Kš œœœœœ˜7Kšœ˜Kšœ&˜&Kšœ˜—Kšœ˜—Kšœ˜Kšœ<œ˜BK˜K˜�—šžœœœ(œ˜bKšœ˜KšœG˜GKšœ˜Kšœ˜Kšœ!˜!Kšœ%˜%šœ0œœ˜IKšœœK˜cKšœœJ˜bKšœ˜—Kšœœ-˜EKšœR˜Ršœ0œœ˜Išœœ˜šœ˜šœœœ˜'Kšœ˜Kšœ˜Kšœ˜K˜—Kšœ ˜ Kšœ-˜-Kšœ˜Kšœ&˜&Kšœ˜—Kšœ˜—Kšœ˜—Kšœ˜Kšœ<œ˜BK˜K˜�—šœ#œ˜BKšœœ˜Kšœœ˜Kšœ˜K˜—Kšœ,œœ˜9šœ œ˜Kšœœ2œ1˜€KšœL˜LK˜—Kšœœ˜Kšœ$˜&—Kšœ˜—�…—����–‚��Úb��