All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.electric.plugins.irsim.Eval Maven / Gradle / Ivy

There is a newer version: 0.3
Show newest version
/* -*- 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;
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy