com.sun.electric.plugins.irsim.Eval Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of electric-irsim Show documentation
Show all versions of electric-irsim Show documentation
IRSIM simulator rewritten to Java.
/* -*- tab-width: 4 -*-
*
* Electric(tm) VLSI Design System
*
* File: Eval.java
* IRSIM simulator
* Translated by Steven M. Rubin, Sun Microsystems.
*
* Copyright (C) 1988, 1990 Stanford University.
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. Stanford University
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
package com.sun.electric.plugins.irsim;
import java.util.Collection;
public class Eval
{
// private static final boolean DEBUG = false;
private boolean firstCall; /* reset when calling init_vdd_gnd */
protected Sim theSim;
private int nPending; /* number of pending events */
public static class Event
{
/** pointers in doubly-linked list */ Event fLink, bLink;
/** link for list of events for this node */ Event nLink;
/** node this event is all about */ Sim.Node eNode;
Sim.Node cause;
/** time, in DELTAs, of this event */ long nTime;
/** delay associated with this event */ long delay;
/** rise/fall time, in DELTAs */ short rTime;
/** new value */ byte eval;
/** type of event */ byte type;
};
/**
* table to convert transistor type and gate node value into switch state
* indexed by [transistor-type][gate-node-value].
*/
private static int [][] switchState = new int[][]
{
/** NCHAH */ new int[] {Sim.OFF, Sim.UNKNOWN, Sim.UNKNOWN, Sim.ON},
/** PCHAN */ new int[] {Sim.ON, Sim.UNKNOWN, Sim.UNKNOWN, Sim.OFF},
/** RESIST */ new int[] {Sim.WEAK, Sim.WEAK, Sim.WEAK, Sim.WEAK},
/** DEP */ new int[] {Sim.WEAK, Sim.WEAK, Sim.WEAK, Sim.WEAK}
};
public Eval(Sim sim)
{
firstCall = true;
theSim = sim;
}
protected void modelEvaluate(Sim.Node node) {}
/**
* Set the firstCall flags. Used when moving back to time 0.
*/
public void reInit()
{
firstCall = true;
}
public boolean step(long stopTime,
Collection xInputs,
Collection hInputs,
Collection lInputs,
Collection uInputs)
{
boolean retCode = false;
// look through input lists updating any nodes which just become inputs
MarkNOinputs(xInputs); // nodes no longer inputs
SetInputs(hInputs, Sim.HIGH); // HIGH inputs
hInputs.clear();
SetInputs(lInputs, Sim.LOW); // LOW inputs
lInputs.clear();
SetInputs(uInputs, Sim.X); // X inputs
uInputs.clear();
/*
* On the first call to step, make sure transistors with gates
* of vdd and gnd are set up correctly. Mark initial inputs first!
*/
if (firstCall)
{
/*
* find transistors with gates of VDD or GND and calculate values for source
* and drain nodes just in case event driven calculations don't get to them.
*/
enqueueInput(theSim.powerNode, Sim.HIGH);
enqueueInput(theSim.groundNode, Sim.LOW);
firstCall = false;
}
for(;;)
{
// process events until we reach specified stop time or events run out.
for(;;)
{
Event evList = getNextEvent(stopTime);
if (evList == null) break;
MarkNodes(evList);
if (xInputs.size() > 0) EvalNOinputs(xInputs);
long brkFlag = EvalNodes(evList);
// if (stopping(STOPREASONSIMULATE))
// {
// if (RSim.analyzerON)
// Analyzer.updateWindow(Sched.curDelta);
// return retCode;
// }
SimAPI.Analyzer analyzer = theSim.theAnalyzer;
if ((brkFlag & (Sim.WATCHVECTOR | Sim.STOPONCHANGE | Sim.STOPVECCHANGE)) != 0 && analyzer != null)
{
if ((brkFlag & (Sim.WATCHVECTOR | Sim.STOPVECCHANGE)) != 0)
analyzer.dispWatchVec(brkFlag);
if ((brkFlag & (Sim.STOPONCHANGE | Sim.STOPVECCHANGE)) != 0)
{
analyzer.updateWindowIfAnalyzerOn(theSim.curDelta);
return true;
}
}
}
if (xInputs.size() > 0)
{
EvalNOinputs(xInputs);
continue;
}
break;
}
theSim.curDelta = stopTime;
SimAPI.Analyzer analyzer = theSim.theAnalyzer;
if (analyzer != null)
analyzer.updateWindowIfAnalyzerOn(theSim.curDelta);
return retCode;
}
/**
* Print watched node event.
*/
private void pr_watched(Event e, Sim.Node n)
{
if ((n.nFlags & Sim.INPUT) != 0)
{
System.out.println(" @ " + Sim.deltaToNS(e.nTime) + "ns input " + n.nName + ": -> " + Sim.vChars.charAt(e.eval));
return;
}
System.out.print(" @ " + Sim.deltaToNS(e.nTime) + "ns " + n.nName + ": " +
Sim.vChars.charAt(n.nPot) + " -> " + Sim.vChars.charAt(e.eval));
int tmp = (theSim.irDebug & Sim.DEBUG_EV) != 0 ? (Sim.REPORT_TAU | Sim.REPORT_DELAY) : theSim.tReport;
switch (tmp & (Sim.REPORT_TAU | Sim.REPORT_DELAY))
{
case Sim.REPORT_TAU:
System.out.println(" (tau=" + Sim.deltaToNS(e.rTime));
break;
case Sim.REPORT_DELAY:
System.out.println(" (delay=" + Sim.deltaToNS(e.delay) + "ns)");
break;
default:
System.out.println(" (tau=" + Sim.deltaToNS(e.rTime) + "ns, delay=" + Sim.deltaToNS(e.delay) + "ns)");
break;
}
}
/**
* Run through the event list, marking all nodes that need to be evaluated.
*/
private void MarkNodes(Event evList)
{
Event e = evList;
long allFlags = 0;
do
{
Sim.Node n = e.eNode;
allFlags |= n.nFlags;
if (e.type == Sim.DECAY_EV && ((theSim.tReport & Sim.REPORT_DECAY) != 0 ||
(n.nFlags & (Sim.WATCHED | Sim.STOPONCHANGE)) != 0))
{
System.out.println(" @ " + Sim.deltaToNS(e.nTime) + "ns " + n.nName+ ": decay " +
Sim.vChars.charAt(n.nPot) + " . X");
} else if ((n.nFlags & (Sim.WATCHED | Sim.STOPONCHANGE)) != 0)
pr_watched(e, n);
n.nPot = e.eval;
// Add the new value to the history list (if they differ)
if ((n.nFlags & Sim.INPUT) == 0 && (n.curr.val != n.nPot))
theSim.addHist(n, n.nPot, false, e.nTime, e.delay, e.rTime);
if (n.awPending != null && n.awPot == n.nPot && n.awPending != null)
n.awPending.run();
// theAnalyzer.evalAssertWhen(n);
/* for each transistor controlled by event node, mark
* source and drain nodes as needing recomputation.
*
* Added MOSSIMs speed up by first checking if the
* node needs to be rechecked mh
*
* Fixed it so nodes with pending events also get
* re_evaluated. Kevin Karplus
*/
for(Sim.Trans t : n.nGateList)
{
t.state = (byte)computeTransState(t);
if ((t.source.nFlags & Sim.INPUT) == 0)
t.source.nFlags |= Sim.VISITED;
if ((t.drain.nFlags & Sim.INPUT) == 0)
t.drain.nFlags |= Sim.VISITED;
}
freeFromNode(e, n); // remove to avoid punting this event
e = e.fLink;
}
while(e != null);
// run through event list again, marking src/drn of input nodes
if ((allFlags & Sim.INPUT) != 0)
{
for(e = evList; e != null; e = e.fLink)
{
Sim.Node n = e.eNode;
if ((n.nFlags & (Sim.INPUT | Sim.POWER_RAIL)) != Sim.INPUT)
continue;
for(Sim.Trans t : n.nTermList)
{
if (t.state != Sim.OFF)
{
Sim.Node other = Sim.otherNode(t, n);
if ((other.nFlags & (Sim.INPUT | Sim.VISITED)) == 0)
other.nFlags |= Sim.VISITED;
}
}
}
}
}
private long EvalNodes(Event evList)
{
long brkFlag = 0;
Event event = evList;
do
{
theSim.nEvent += 1; // advance counter to that of this event
Sim.Node n = theSim.curNode = event.eNode;
n.setTime(event.nTime); // set up the cause stuff
n.setCause(event.cause);
//if (DEBUG) System.out.println("Removing event at " + event.nTime + " in EvalNodes");
nPending--;
/*
* now calculate new value for each marked node. Some nodes marked
* above may become unmarked by earlier calculations before we get
* to them in this loop...
*/
for(Sim.Trans t : n.nGateList)
{
if ((t.source.nFlags & Sim.VISITED) != 0)
modelEvaluate(t.source);
if ((t.drain.nFlags & Sim.VISITED) != 0)
modelEvaluate(t.drain);
}
if ((n.nFlags & (Sim.INPUT | Sim.POWER_RAIL)) == Sim.INPUT)
{
for(Sim.Trans t : n.nTermList)
{
Sim.Node other = Sim.otherNode(t, n);
if ((other.nFlags & Sim.VISITED) != 0)
modelEvaluate(other);
}
}
// see if we want to halt if this node changes value
brkFlag |= n.nFlags;
event = event.fLink;
}
while(event != null);
return brkFlag;
}
/**
* Change the state of the nodes in the given input list to their new value,
* setting their INPUT flag and enqueueing the event.
*/
private void SetInputs(Collection list, int val)
{
for(SimAPI.Node nn : list)
{
Sim.Node n = (Sim.Node)nn;
n.nPot = (short)val;
n.nFlags &= ~Sim.INPUT_MASK;
n.nFlags |= Sim.INPUT;
// enqueue event so consequences are computed.
enqueueInput(n, val);
if (n.curr.val != val || !n.curr.inp)
theSim.addHist(n, val, true, theSim.curDelta, 0L, 0L);
}
}
private void MarkNOinputs(Collection xInputs)
{
for(SimAPI.Node n : xInputs)
{
n.clearFlags(SimAPI.INPUT_MASK | SimAPI.INPUT);
n.setFlags(SimAPI.VISITED);
}
}
/**
* nodes which are no longer inputs
*/
private void EvalNOinputs(Collection xInputs)
{
for(SimAPI.Node nn : xInputs)
{
Sim.Node n = (Sim.Node)nn;
theSim.curNode = n;
theSim.addHist(n, n.curr.val, false, theSim.curDelta, 0L, 0L);
if ((n.nFlags & Sim.VISITED) != 0)
modelEvaluate(n);
}
xInputs.clear();
}
/**
* compute state of transistor. If gate is a simple node, state is determined
* by type of implant and value of node. If gate is a list of nodes, then
* this transistor represents a stack of transistors in the original network,
* and we perform the logical AND of all the gate node values to see if
* transistor is on.
*/
public int computeTransState(Sim.Trans t)
{
if ((t.tType & Sim.GATELIST) == 0)
return switchState[Sim.baseType(t.tType)][((Sim.Node)t.gate).nPot];
switch(Sim.baseType(t.tType))
{
case Sim.NCHAN:
int result = Sim.ON;
for(Sim.Trans l = (Sim.Trans) t.gate; l != null; l = l.getSTrans())
{
Sim.Node n = (Sim.Node)l.gate;
if (n.nPot == Sim.LOW)
return Sim.OFF;
else if (n.nPot == Sim.X)
result = Sim.UNKNOWN;
}
return result;
case Sim.PCHAN:
result = Sim.ON;
for(Sim.Trans l = (Sim.Trans) t.gate; l != null; l = l.getSTrans())
{
Sim.Node n = (Sim.Node)l.gate;
if (n.nPot == Sim.HIGH)
return Sim.OFF;
else if (n.nPot == Sim.X)
result = Sim.UNKNOWN;
}
return result;
case Sim.DEP:
case Sim.RESIST:
return Sim.WEAK;
}
System.out.println("**** internal error: unrecongized transistor type (" + Sim.baseType(t.tType) + ")");
return Sim.UNKNOWN;
}
/***************************************** SCHED *****************************************/
/** size of event array, must be power of 2 */ private static final int TSIZE = 1024;
private static final int TMASK = (TSIZE - 1);
/** used as head of doubly-linked lists */ private Event [] evArray = new Event[TSIZE];
private Event getEVArray(long t) { return evArray[(int)(t & TMASK)]; }
/**
* find the next event to be processed by scanning event wheel. Return
* the list of events to be processed at this time, removing it first
* from the time wheel.
*/
private Event getNextEvent(long stopTime)
{
if (nPending == 0) return null;
//if (DEBUG) System.out.println("Find events up to " + stopTime);
Event event = null;
boolean eventValid = false;
long time = theSim.maxTime;
for(long i = theSim.curDelta, limit = i + TSIZE; i < limit; i++)
{
event = getEVArray(i);
if (event != event.fLink)
{
if (event.fLink.nTime < limit) // common case
{
eventValid = true;
break;
}
if (event.fLink.nTime < time)
time = (event.fLink).nTime;
}
}
if (!eventValid)
{
if (time == theSim.maxTime)
{
System.out.println("*** internal error: no events but npending set");
return null;
}
event = getEVArray(time);
}
Event evList = event.fLink;
time = evList.nTime;
if (time >= stopTime)
{
//if (DEBUG) System.out.println("Time="+time+" which is beyond stop time="+stopTime);
return null;
}
theSim.curDelta = time; // advance simulation time
if (event.bLink.nTime != time) // check tail of list
{
do
event = event.fLink;
while(event.nTime == time);
event = event.bLink; // grab part of the list
evList.bLink.fLink = event.fLink;
event.fLink.bLink = evList.bLink;
evList.bLink = event;
event.fLink = null;
} else
{
event = evList.bLink; // grab the entire list
event.bLink.fLink = null;
evList.bLink = event.bLink;
event.fLink = event.bLink = event;
}
//if (DEBUG)
//{
// System.out.print("FOUND EVENTS:");
// Event xx = evList;
// do
// {
// System.out.print(" time="+xx.nTime);
// xx = xx.fLink;
// }
// while(xx != null);
// System.out.println();
//}
return evList;
}
/**
* remove event from all structures it belongs to and return it to free pool
*/
private void freeEvent(Event event)
{
// unhook from doubly-linked event list
event.bLink.fLink = event.fLink;
event.fLink.bLink = event.bLink;
nPending--;
freeFromNode(event, event.eNode);
}
/**
* Add an event to event list, specifying transition delay and new value.
* 0 delay transitions are converted into unit delay transitions (0.01 ns).
*/
public void enqueueEvent(Sim.Node n, int newValue, long delta, long rTime)
{
Event newEV = new Event();
// remember facts about this event
long eTime = theSim.curDelta + delta;
newEV.nTime = eTime;
newEV.rTime = (short)rTime;
newEV.eNode = n;
newEV.cause = theSim.curNode;
newEV.delay = delta;
if (newValue == Sim.DECAY) // change value to X here
{
newEV.eval = Sim.X;
newEV.type = Sim.DECAY_EV;
} else
{
newEV.eval = (byte)newValue;
newEV.type = Sim.REVAL; // for incremental simulation
}
/* add the new event to the event list at the appropriate entry
* in event wheel. Event lists are kept sorted by increasing
* event time.
*/
Event marker = getEVArray(eTime);
// Check whether we need to insert-sort in the list
if ((marker.bLink != marker) && ((marker.bLink).nTime > eTime))
{
do { marker = marker.fLink; } while (marker.nTime <= eTime);
}
// insert event right before event pointed to by marker
newEV.fLink = marker;
newEV.bLink = marker.bLink;
marker.bLink.fLink = newEV;
marker.bLink = newEV;
nPending++;
//if (DEBUG) System.out.println("Adding event at " + newEV.nTime + " in enqueueEvent (cur="+theSim.curDelta+" delta="+delta);
/*
* thread event onto list of events for this node, keeping it
* in sorted order
*/
if ((n.events != null) && (n.events.nTime > eTime))
{
for(marker = n.events; (marker.nLink != null) &&
(marker.nLink.nTime > eTime); marker = marker.nLink);
newEV.nLink = marker.nLink;
marker.nLink = newEV;
} else
{
newEV.nLink = n.events;
n.events = newEV;
}
}
/**
* same as enqueueEvent, but assumes 0 delay and rtise/fall time
*/
private void enqueueInput(Sim.Node n, int newValue)
{
// Punt any pending events for this node.
while(n.events != null)
freeEvent(n.events);
Event newEV = new Event();
// remember facts about this event
long eTime = theSim.curDelta;
newEV.nTime = eTime;
newEV.rTime = 0; newEV.delay = 0;
newEV.eNode = n;
newEV.cause = n;
newEV.eval = (byte)newValue;
newEV.type = Sim.REVAL; // anything, doesn't matter
// Add new event to HEAD of list at appropriate entry in event wheel
Event marker = getEVArray(eTime);
newEV.fLink = marker.fLink;
newEV.bLink = marker;
marker.fLink.bLink = newEV;
marker.fLink = newEV;
nPending++;
//if (DEBUG) System.out.println("Adding event at " + newEV.nTime + " in enqueueInput");
// thread event onto (now empty) list of events for this node
newEV.nLink = null;
n.events = newEV;
}
/**
* Initialize event structures
*/
public void initEvent()
{
for(int i = 0; i < TSIZE; i++)
{
Event event = new Event();
evArray[i] = event;
event.fLink = event.bLink = event;
}
nPending = 0;
theSim.nEvent = 0;
}
protected void puntEvent(Sim.Node node, Event ev)
{
if ((node.nFlags & Sim.WATCHED) != 0)
System.out.println(" punting transition of " + node.nName + " . " +
Sim.vChars.charAt(ev.eval) + " scheduled for " + Sim.deltaToNS(ev.nTime) + "ns");
if (ev.type != Sim.DECAY_EV) // don't save punted decay events
theSim.addPunted(ev.eNode, ev, theSim.curDelta);
freeEvent(ev);
}
private void requeueEvents(Event evList, boolean thread)
{
nPending = 0;
Event next = null;
for(Event ev = evList; ev != null; ev = next)
{
next = ev.fLink;
nPending++;
long eTime = ev.nTime;
//if (DEBUG) System.out.println("Adding of event at time "+eTime + " in requeueEvents");
Event target = getEVArray(eTime);
if ((target.bLink != target) && ((target.bLink).nTime > eTime))
{
do { target = target.fLink; } while(target.nTime <= eTime);
}
ev.fLink = target;
ev.bLink = target.bLink;
target.bLink.fLink = ev;
target.bLink = ev;
if (thread)
{
Sim.Node n = ev.eNode;
Event marker;
if ((n.events != null) && (n.events.nTime > eTime))
{
for(marker = n.events; (marker.nLink != null) &&
(marker.nLink.nTime > eTime); marker = marker.nLink);
ev.nLink = marker.nLink;
marker.nLink = ev;
} else
{
ev.nLink = n.events;
n.events = ev;
}
}
}
}
public void printPendingEvents()
{
if (nPending == 0) return;
System.out.println("Warning: there are " + nPending + " pending events:");
// for(int i=0; i= bTime)
{
freeFromNode(ev, ev.eNode);
} else
{
ev.fLink = tmpList; // move it to tmp list
tmpList = ev;
nEvents++;
}
}
}
if (isInc == 0)
{
requeueEvents(tmpList, false);
return null;
}
if (isInc != 1) // only for fault simulation (isInc == 2)
{
nPending = 0;
return tmpList;
}
// now move the temporary list to the time wheel
Event next = null;
for(Event ev = tmpList; ev != null; ev = next)
{
next = ev.fLink;
ev.nTime -= ev.delay;
ev.type = Sim.PENDING;
long eTime = ev.nTime;
Event target = getEVArray(eTime);
if ((target.bLink != target) && (target.bLink.nTime > eTime))
{
do { target = target.fLink; } while(target.nTime <= eTime);
}
ev.fLink = target;
ev.bLink = target.bLink;
target.bLink.fLink = ev;
target.bLink = ev;
}
nPending = nEvents;
return null;
}
private void freeFromNode(Event ev, Sim.Node nd)
{
if (nd.events == ev)
nd.events = ev.nLink;
else
{
Event evp = null;
for(evp = nd.events; evp.nLink != ev; evp = evp.nLink);
evp.nLink = ev.nLink;
}
}
}