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

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

/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Sim.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.io.IOException;
import java.io.LineNumberReader;
import java.io.Reader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;

public class Sim implements SimAPI
{
	public static class Node implements SimAPI.Node
	{
		/** sundries list */									Node           nLink;
		/** charge sharing event */								Eval.Event     events;
		/** list of xtors w/ gates connected to this node */	List    nGateList;
		/** list of xtors w/ src/drn connected to this node */	List    nTermList;
		/** capacitance of node in pf */						float          nCap;
		/** low logic threshold for node, normalized units */	float          vLow;
		/** high logic threshold for node, normalized units */	float          vHigh;
		/** low to high transition time in DELTA's */			short          tpLH;
		/** high to low transition time in DELTA's */			short          tpHL;
//		/** signal in the waveform window (if displayed) */		Signal  sig;
		/** combines time, nindex, cap, and event */			private Object c;
		/** combines cause, punts, and tranT */					private Object t;
		/** combines thev, next, and tranN */					private Object n;

		/** current potential */								short          nPot;
		/** flag word (see defs below) */						long           nFlags;
		/** ascii name of node */								String         nName;

		/** first entry in transition history */				HistEnt        head;
		/** ptr. to current history entry */					HistEnt        curr;
		/** potential for pending AssertWhen */					short          awPot;
		/** pending asswertWhen list */							Runnable       awPending;
		/** index of this node (a unique value) */				int            index;
		/** Analyzer: window start */							HistEnt        wind;
		/** Analyzer: cursor value */							HistEnt        cursor;

		Node(Sim theSim)
		{
			head = new HistEnt();
			index = theSim.nodeIndexCounter++;
		}

		void setTime(long time) { c = new Long(time); }
		void setNIndex(long nIndex) { c = new Long(nIndex); }
		void setCap(float cap) { c = new Float(cap); }
		void setEvent(Eval.Event event) { c = event; }

		void setCause(Node cause) { t = cause; }
		void setPunts(HistEnt punts) { t = punts; }

		void setThev(Thev thev) { n = thev; }
		public void setNext(SimAPI.Node next) { n = (Node)next; }
		void setTrans(Trans trans) { n = trans; }

		public long getTime() { return ((Long)c).longValue(); }
		long getNIndex() { return ((Long)c).longValue(); }
		float getCap() { return ((Float)c).floatValue(); }
		Eval.Event getEvent() { return (Eval.Event)c; }

		public Node getCause() { return (Node)t; }
		HistEnt getPunts() { return (HistEnt)t; }

		Thev getThev() { return (Thev)n; }
		public Node getNext() { return (Node)n; }
		Trans getTrans() { return (Trans)n; }
        
        public String getName() { return nName; }
        public Node getLink() { return nLink; }
        public short getPot() { return nPot; }
        public char getPotChar() {
            return vChars.charAt(getPot());
        }
        public long getFlags() { return nFlags; }
        public long getFlags(long mask) { return nFlags & mask; }
        public void setFlags(long mask) { nFlags |= mask; }
        public void clearFlags(long mask) { nFlags &= ~mask; }
		/** first entry in transition history */
        public HistEnt getHead() { return head; }
		/** ptr. to current history entry */
        public HistEnt getCurr() { return curr; }
		/** Analyzer: window start */
        public HistEnt getWind() { return wind; }
		/** Analyzer: cursor value */
        public HistEnt getCursor() { return cursor; }
        public void setWind(SimAPI.HistEnt wind) { this.wind = (HistEnt)wind; }
        public void setCursor(SimAPI.HistEnt cursor) { this.cursor = (HistEnt)cursor; }
        
        public Collection getGates() {
            return Collections.unmodifiableCollection(nGateList);
        }
        public Collection getTerms() {
            return Collections.unmodifiableCollection(nTermList);
        }
        public String describeDelay() {
			String infstr = "";
			if (getFlags(INPUT) != 0)
				infstr += "[NOTE: node is an input] ";
			infstr += "(vl=" + vLow + " vh=" + vHigh + ") ";
			if (getFlags(USERDELAY) != 0)
				infstr += "(tpLH=" + tpLH + ", tpHL=" + tpHL + ") ";
			infstr += "(" + nCap + " pf) ";
            return infstr;
        }
        public String[] describePendingEvents() {
			if (events == null) return null;
            List list = new ArrayList();
            for(Eval.Event e = events; e != null; e = e.nLink) {
                list.add("   transition to " + Sim.vChars.charAt(e.eval) + " at " + Sim.deltaToNS(e.nTime)+ "ns");
			}
            return list.toArray(new String[list.size()]);
        }
        public Runnable getAssertWhen() {
            return awPending;
        }
        public void setAssertWhen(Runnable aw) {
            awPending = aw;
        }
        public void setAssertWhenPot(short pot) {
            awPot = pot;
        }
	};

	/**
	 * find node in network
	 */
	public Node findNode(String name)
	{
		return nodeHash.get(theAnalyzer.canonicString(name));
	}

	/**
	 * Get node structure.  If not found, create a new one.
	 * If create is TRUE a new node is created and NOT entered into the table.
	 */
	private Node getNode(String name)
	{
		Node n = findNode(name);
		if (n != null)
		{
			if (!name.equals(n.nName))
			{
				boolean skip = false;
				if (name.equalsIgnoreCase("vdd"))
				{
					skip = warnVdd; warnVdd = true;
				}
				if (name.equalsIgnoreCase("gnd"))
				{
					skip = warnGnd; warnGnd = true;
				}
				if (!skip)
					System.out.println("Warning: Aliasing nodes '" + name + "' and '" + n.nName + "'");
			}
			while ((n.nFlags & ALIAS) != 0)
				n = n.nLink;
			return n;
		}

		// allocate new node from free storage
		n = new Node(this);

		numNodes++;

		// insert node into hash table and list
		nodeHash.put(theAnalyzer.canonicString(name), n);
		nodeList.add(n);

		// initialize node entries
		n.nName = name;
		n.nGateList = new ArrayList();
		n.nTermList = new ArrayList();
		n.nFlags = 0;
		n.nCap = (float)MIN_CAP;
		n.vLow = (float)theConfig.lowThresh;
		n.vHigh = (float)theConfig.highThresh;
		n.setTime(0);
		n.tpLH = 0;
		n.tpHL = 0;
		n.setCause(null);
		n.nLink = null;
		n.events = null;
		n.nPot = X;
		n.awPending = null;

		n.head = new HistEnt();
		n.head.next = lastHist;
		n.head.hTime = 0;
		n.head.val = X;
		n.head.inp = false;
		n.head.punt = false;
		n.head.rTime = n.head.delay = 0;
		n.curr = n.head;

		return n;
	}

	/**
	 * Return a list of all nodes in the network.
	 */
	public List getNodeList()
	{
		return nodeList;
	}
    
    public List getNodes() {
        return Collections.unmodifiableList(nodeList);
    }
    
    public Node getGroundNode() { return groundNode; }
    public Node getPowerNode() { return powerNode; }
    public int getNumNodes() { return numNodes; }
    public int getNumAliases() { return numAliases; }
    public int getNumEdges() { return numEdges; }
    public int getNumPunted() { return numPunted; }
    public int getNumConsPunted() { return numConsPunted; }
    public long getNumEvents() { return nEvent; }
    
    public List getShortedTransistors() {
        ArrayList result = new ArrayList();
		for(Trans t = tCap.getSTrans(); t != tCap; t = t.getSTrans()) {
            result.add(t);
		}
        return result;
    }
    
    public int getReport() { return tReport; }
    public void setReport(int mask) { tReport |= mask; }
    public void clearReport() { tReport = 0; }
    public long getMaxTime() { return maxTime; }

	public static class Trans implements SimAPI.Trans
	{
		/** nodes to which trans is connected */	Object   gate;
		/** nodes to which trans is connected */	Node     source, drain;
		/** caches to remember src/drn values */	Object   sCache, dCache;
		/** type of transistor */					byte     tType;
		/** cache to remember current state */		byte     state;
		/** transistor flags */						byte     tFlags;
		/** index into parallel list */				byte     nPar;
		/** transistor resistances */				Resists  r;
		/** next txtor in position hash table */	Trans    tLink;
		/** position in the layout */				int      x, y;

		void setSThev(Thev r) { sCache = r; }
		void setDThev(Thev r) { dCache = r; }
		void setSTrans(Trans t) { sCache = t; }
		void setDTrans(Trans t) { dCache = t; }
		void setSI(int i) { sCache = new Integer(i); }
		void setDI(int i) { dCache = new Integer(i); }

		Thev getSThev() { return (Thev)sCache; }
		Thev getDThev() { return (Thev)dCache; }
		public Trans getSTrans() { return (Trans)sCache; }
		Trans getDTrans() { return (Trans)dCache; }
		int getSI() { return ((Integer)sCache).intValue(); }
		int getDI() { return ((Integer)dCache).intValue(); }

		int hashTerms() { return source.index ^ drain.index; }
        
        public int getBaseType() { return tType & 0x07; }
        public String describeBaseType() { return transistorType[getBaseType()]; }
        public String describeState() { return states[state]; }
        public Node getGate() { return (Node)gate; }
        public Node getSource() { return source; }
        public Node getDrain() { return drain; }
        public int getX() { return x; }
        public int getY() { return y; }
        public double[] getResists() {
            return new double[] { r.rStatic, r.dynRes[R_HIGH], r.dynRes[R_LOW] };
        }
        public long getLength() { return r.length; }
        public long getWidth() { return r.width; }
        /**
         * figure what's on the *other* terminal node of a transistor
         */
        public Node getOtherNode(SimAPI.Node n) { return drain == n ? source : drain; }
        public Trans getLink() { return tLink; }
        public Collection getGateList() {
            Collection result = null;
    		if ((tType & GATELIST) != 0) {
                result = new ArrayList();
    			for(Trans t = (Trans)gate; t != null; t = t.getSTrans())
                    result.add(t);
			}
            return result;
        }
	}
    
    /**
     * current simulated time
     */
    public long getCurDelta() { return curDelta; }
    public void setCurDelta(long curDelta) { this.curDelta = curDelta; }
    public void clearCurNode() { curNode = null; }
    
	/**
	 * Set the firstCall flags.  Used when moving back to time 0.
	 */
	public void reInit() {
        getModel().reInit();
    }

	/**
	 * Back the event queues up to time 'bTime'.  This is the opposite of
	 * advancing the simulation time.  Mark all pending events as PENDING,
	 * and re-enqueue them according to their creation-time (nTime - delay).
	 */
	public void backSimTime(long bTime, int isInc) {
        getModel().backSimTime(bTime, isInc);
    }
    
    public void printPendingEvents() {
        getModel().printPendingEvents();
    }

	public boolean step(long stopTime,
            Collection xInputs,
            Collection hInputs,
            Collection lInputs,
            Collection uInputs) {
        return getModel().step(stopTime, xInputs, hInputs, lInputs, uInputs);
    }
    
    public long getLambdaCM() {
        return getConfig().lambdaCM;
    }
    
    public long getDecay() { return tDecay; }
    public void setDecay(long decay) { tDecay = decay; }
    public int getUnitDelay() { return tUnitDelay; }
    public void setUnitDelay(int unitDelay) { tUnitDelay = unitDelay; }
    public void setDebug(int irDebug)  { this.irDebug = irDebug; }

	private static class TranResist
	{
		/** dynamic resistances [R_LOW - R_MAX] */	float  [] dynRes;
		/** static resistance of transistor */		float  rStatic;

		TranResist() { dynRes = new float[2]; }
	};

	public static class Resists extends TranResist
	{
		/** transistor size in centimicrons */		long  width, length;
	};

	public static class HistEnt implements SimAPI.HistEnt
	{
		/** next transition in history */			HistEnt  next;
		/** delay from input */						short    delay;
		/** rise/fall time */						short    rTime;
		/** punt time */							short    pTime;
		/** time of transition */					long     hTime;
		/** 1 if node became an input */			boolean  inp;
		/** 1 if this event was punted */			boolean  punt;
		/** value: HIGH, LOW, or X */				byte     val;

		public HistEnt getNextHist()
		{
			HistEnt h;
			HistEnt p = this;
			for(h = p.next; h.punt; h = h.next) ;
			return h;
		}
        
        public long getTime() { return hTime; }
        public byte getVal() { return val; }
	};

	/* resists are in ohms, caps in pf */
	public static class Thev
	{
														Object   link;
		/** flags defined above */						int      flags;
		/** capacitance charged low */					Range    cLow;
		/** capacitance charged high */					Range    cHigh;
		/** resistance pulling up to Vdd */				Range    rUp;
		/** resistance pulling down to GND */			Range    rDown;
		/** resist. of present (parallel) xtor(s) */	Range    req;
		/** normalized voltage range (0-1) */			Range    v;
		/** minimum resistance to any driver */			double   rMin;
		/** minimum resistance to dominant driver */	double   rDom;
		/** maximum resistance to dominant driver */	double   rMax;
		/** Adjusted non-switching capacitance */		double   cA;
		/** Adjusted total capacitance */				double   cD;
		/** Elmore delay (psec) */						double   tauD;
		/** 1st order time-constant (psec) */			double   tauA;
		/** 2nd order time-constant (psec) */			double   tauP;
		/** input transition = (input_tau) * Rin */		double   tIn;
		/** user specified low->high delay (DELTA) */	short    tpLH;
		/** user specified high->low delay (DELTA) */	short    tpHL;
		/** steady-state value calculated (H, L, X) */	char     finall;
		/** if tau calculated, == dominant voltage */	char     tauDone;
		/** if tauP calculated, == dominant voltage */	char     tauPDone;

		Thev()
		{
			cLow = new Range(0, 0);
			cHigh = new Range(0, 0);
			rUp = new Range(Sim.LARGE, Sim.LARGE);
			rDown = new Range(Sim.LARGE, Sim.LARGE);
			req = new Range(Sim.LARGE, Sim.LARGE);
			v = new Range(1, 0);
			setN(null);
			flags		= 0;
			rMin		= Sim.LARGE;
			rDom		= Sim.LARGE;
			rMax		= Sim.LARGE;
			cA			= 0.0;
			cD			= 0.0;
			tauD		= 0.0;
			tauA		= 0.0;
			tauP		= 0.0;
			tIn			= Sim.SMALL;
			tpLH		= 0;
			tpHL		= 0;
			finall		= X;
			tauDone		= N_POTS;
			tauPDone	= N_POTS;
		}

		Thev(Thev old)
		{
			link = old.link;
			flags = old.flags;
			cLow = new Range(old.cLow);
			cHigh = new Range(old.cHigh);
			rUp = new Range(old.rUp);
			rDown = new Range(old.rDown);
			req = new Range(old.req);
			v = new Range(old.v);
			rMin = old.rMin;
			rDom = old.rDom;
			rMax = old.rMax;
			cA = old.cA;
			cD = old.cD;
			tauD = old.tauD;
			tauA = old.tauA;
			tauP = old.tauP;
			tIn = old.tIn;
			tpLH = old.tpLH;
			tpHL = old.tpHL;
			finall = old.finall;
			tauDone = old.tauDone;
			tauPDone = old.tauPDone;
		}

		void setT(Thev t) { link = t; }
		void setN(Node n) { link = n; }

		Thev getT() { return (Thev)link; }
		Node getN() { return (Node)link; }
	};

	public static class Range
	{
		double  min;
		double  max;

		Range() {}

		Range(double min, double max)
		{
			this.min = min;
			this.max = max;
		}

		Range(Range r)
		{
			min = r.min;
			max = r.max;
		}
	};


	/** a huge time */								private static final long MAX_TIME	= 0x0FFFFFFFFFFFFFFFL;

	/** A small number */							public static final double SMALL	= 1E-15;
	/** A large number */							public static final double LARGE	= 1E15;
	/** R > LIMIT are considered infinite */		public static final double LIMIT	= 1E8;

	/** number of transistor types defined */		public static final int NTTYPES     = 4;
	static String [] transistorType =
	{
		"n-channel",
		"p-channel",
		"depletion",
		"resistor"
	};

	public static String    vChars = "0XX1";
	public static String [] states = { "OFF", "ON", "UKNOWN", "WEAK" };

	public static final int	MAX_ERRS	= 20;

	/** this is probably sufficient per stage */							private static final int	MAX_PARALLEL	= 30;

	/** power supply node */												public  Node    powerNode;
	/** ground supply node */												public  Node    groundNode;

	/** number of actual nodes */											public  int     numNodes;
	/** number of aliased nodes */											public  int     numAliases;
	/** number of txtors indexed by type */									private int []  numTrans = new int[NTTYPES];
	/** number of transistors "or"ed */										private int []  numOred = new int[NTTYPES];

	/** list of capacitor-transistors */									public  Trans   tCap = null;

	/** # of errors found in sim file */									private int     numErrors = 0;

	/** list of transistors just read */									private List readTransistorList;
	public  Trans [] parallelTransistors = new Trans[MAX_PARALLEL];

	/** pointer to dummy hist-entry that serves as tail for all nodes */	private HistEnt lastHist;
	public  int      numEdges;
	public  int      numPunted;
	public  int      numConsPunted;
	public  long     maxTime;

	/** current simulated time */											public  long    curDelta;
	/** node that belongs to current event */								public  Node    curNode;
	/** number of current event */											public  long    nEvent;

	/** if nonzero, all transactions take this DELAY-units */				public  int     tUnitDelay = 0;
	/** number of DELAY-units after which undriven nodes decay to X */		public  long    tDecay = 0;

	private boolean  parallelWarning = false;
	private HashMap  nodeHash;
	private List nodeList;
	private int      nodeIndexCounter;
	private boolean  warnVdd, warnGnd;
	public  int      tReport = 0;

	private Eval     theModel;
	private Config   theConfig;
    SimAPI.Analyzer theAnalyzer;
    int irDebug;
    /** true if using the delayed X model, false if using the old fast-propagating X model. */
    boolean isDelayedX;


	public Sim(int irDebug, String steppingModel, boolean isDelayedX)
	{
        this.irDebug = irDebug;
        this.isDelayedX = isDelayedX;

		// initialize the model
		if (steppingModel.equals("Linear")) theModel = new SStep(this); else
		{
			if (!steppingModel.equals("RC"))
				System.out.println("Unknown stepping model: " + steppingModel + " using RC");
			theModel = new NewRStep(this);
		}
        theConfig = new Config();
	}
    
    public void loadConfig(URL parameterURL, SimAPI.Analyzer analyzer) {
        theConfig.loadConfig(parameterURL, analyzer);
    }
    
    public void setAnalyzer(SimAPI.Analyzer analyzer) {
        theAnalyzer = analyzer;
    }

	public Config getConfig() { return theConfig; }

	public Eval getModel() { return theModel; }

	public void setModel(boolean rc)
	{
		if (rc) theModel = new NewRStep(this); else
			theModel = new SStep(this);
		theModel.initEvent();
	}

	private void badArgCount(String fileName, LineNumberReader lineReader, String [] strings)
	{
		reportError(fileName, lineReader.getLineNumber(), "Wrong number of args for '" + strings[0] + "'");
		for(int i=0; i MAX_ERRS)
		{
			System.out.println("Too many errors in sim file <" + fileName + ">");
			return true;
		}
		return false;
	}

	/**
	 * Convert deltas to ns.
	 */
	static double deltaToNS(long d) { return (double)d / (double)resolutionScale; }

	/**
	 * Convert deltas to ps.
	 */
	static double deltaToPS(long d) { return (double)(d * 1000) / resolutionScale; }

	/**
	 * Convert ns to deltas.
	 */
	static long nsToDelta(double d) { return (long)(d * resolutionScale); }

	/**
	 * Convert ps to deltas.
	 */
	static long psToDelta(double d) { return (long)(d / 1000 * resolutionScale); }
	/**
	 * Convert ps to ns
	 */
	static double psToNS(double d) { return d * 0.001; }

	/**
	 * figure what's on the *other* terminal node of a transistor
	 */
	static Node otherNode(Trans t, Node n) { return t.drain == n ? t.source : t.drain; }

	static int baseType(int t) { return t & 0x07; }

	static int inputNumber(int flg) { return ((flg & INPUT_MASK) >> 12); }

	/**
	 * combine 2 resistors in parallel
	 */
	static double combine(double r1, double r2) { return (r1 * r2) / (r1 + r2); }

	/**
	 * Traverse the transistor list and add the node connection-list.  We have
	 * to be careful with ALIASed nodes.  Note that transistors with source/drain
	 * connected VDD and GND nodes are not linked.
	 */
	private Node connectTransistors()
	{
		Node ndList = null;

		for(Trans t : readTransistorList)
		{
			Node gate = null, src = null, drn = null;
			for(gate = (Node)t.gate; (gate.nFlags & ALIAS) != 0; gate = gate.nLink) ;
			for(src = t.source; (src.nFlags & ALIAS) != 0; src = src.nLink) ;
			for(drn = t.drain; (drn.nFlags & ALIAS) != 0; drn = drn.nLink) ;

			t.gate = gate;
			t.source = src;
			t.drain = drn;

			int type = t.tType;
			t.state = (byte)((type & ALWAYSON) != 0 ? WEAK : UNKNOWN);
			t.tFlags = 0;

			numTrans[baseType(type)]++;
			if (src == drn || (src.nFlags & drn.nFlags & POWER_RAIL) != 0)
			{
				// transistor is just a capacitor.
				// Transistors that have their drain/source shorted are NOT connected
				// to the network, they are instead linked as a doubly linked list
				// using the scache/dcache fields.
				t.tType |= TCAP;
				t.setDTrans(tCap);
				t.setSTrans(tCap.getSTrans());
				tCap.getSTrans().setDTrans(t);
				tCap.setSTrans(t);
				tCap.x++;
			} else
			{
				// do not connect gate if ALWAYSON since they do not matter
				if ((t.tType & ALWAYSON) == 0)
				{
					((Node)t.gate).nGateList.add(t);
				}

				if ((src.nFlags & POWER_RAIL) == 0)
				{
					src.nTermList.add(t);
					ndList = linkToList(src, ndList);
				}
				if ((drn.nFlags & POWER_RAIL) == 0)
				{
					drn.nTermList.add(t);
					ndList = linkToList(drn, ndList);
				}
			}
		}

		return ndList;
	}

	/**
	 * if VISITED is not set in in n.nFlags, Link n to the head of list
	 * using the temporary entry in the node structure.  This is
	 * used during net read-in/change to build lists of affected nodes.
	 */
	private Node linkToList(Node n, Node list)
	{
		if ((n.nFlags & VISITED) == 0)
		{
			n.nFlags |= VISITED;
			n.setNext(list);
			list = n;
		}
		return list;
	}

	/**
	 * node area and perimeter info (N sim command).
	 */
	private void nodeInfo(String [] targ, String fileName, LineNumberReader lineReader)
	{
		if (targ.length != 8)
		{
			badArgCount(fileName, lineReader, targ);
			return;
		}

		Node n = getNode(targ[1]);

		n.nCap += theAnalyzer.atof(targ[4]) * (theConfig.CMA * theConfig.lambdaSquared) +
			theAnalyzer.atof(targ[5]) * (theConfig.CPA * theConfig.lambdaSquared) +
			theAnalyzer.atof(targ[6]) * (theConfig.CDA * theConfig.lambdaSquared) +
			theAnalyzer.atof(targ[7]) * 2.0f * (theConfig.CDP * theConfig.lambda);
	}

	/**
	 * new format node area and perimeter info (M sim command).
	 */
	private void nNodeInfo(String [] targ, String fileName, LineNumberReader lineReader)
	{
		if (targ.length != 14)
		{
			badArgCount(fileName, lineReader, targ);
			return;
		}

		Node n = getNode(targ[1]);

		n.nCap += theAnalyzer.atof(targ[4]) * (theConfig.CM2A * theConfig.lambdaSquared) +
			theAnalyzer.atof(targ[5]) * 2.0 * (theConfig.CM2P * theConfig.lambda) +
			theAnalyzer.atof(targ[6]) * (theConfig.CMA * theConfig.lambdaSquared) +
			theAnalyzer.atof(targ[7]) * 2.0 * (theConfig.CMP * theConfig.lambda) +
			theAnalyzer.atof(targ[8]) * (theConfig.CPA * theConfig.lambdaSquared) +
			theAnalyzer.atof(targ[9]) * 2.0 * (theConfig.CPP * theConfig.lambda) +
			theAnalyzer.atof(targ[10]) * (theConfig.CDA * theConfig.lambda) +
			theAnalyzer.atof(targ[11]) * 2.0 * (theConfig.CDP * theConfig.lambda) +
			theAnalyzer.atof(targ[12]) * (theConfig.CPDA * theConfig.lambdaSquared) +
			theAnalyzer.atof(targ[13]) * 2.0 * (theConfig.CPDP * theConfig.lambda);
	}

	/**
	 * new transistor.  Implant specifies type.
	 * AreaPos specifies the argument number that contains the area (if any).
	 */
	private void newTrans(int implant, String [] targ, String fileName, LineNumberReader lineReader)
	{
		// create new transistor
		Trans t = new Trans();
		t.tType = (byte)implant;

		if (implant == RESIST)
		{
			if (targ.length != 4)
			{
				badArgCount(fileName, lineReader, targ);
				return;
			}

			t.gate = powerNode;
			t.source = getNode(targ[1]);
			t.drain = getNode(targ[2]);

			long length = (long)(theAnalyzer.atof(targ[3]) * theConfig.lambdaCM);
			t.r = theConfig.rEquiv(implant, 0, length);

		} else
		{
			if (targ.length != 11)
			{
				badArgCount(fileName, lineReader, targ);
				return;
			}

			t.gate = getNode(targ[1]);
			t.source = getNode(targ[2]);
			t.drain = getNode(targ[3]);

			long length = (long)(theAnalyzer.atof(targ[4]) * theConfig.lambdaCM);
			long width = (long)(theAnalyzer.atof(targ[5]) * theConfig.lambdaCM);
			if (width <= 0 || length <= 0)
			{
				reportError(fileName, lineReader.getLineNumber(),
					"Bad transistor width=" + width + " or length=" + length);
				return;
			}
			((Node)t.gate).nCap += length * width * theConfig.CTGA;
			t.r = theConfig.rEquiv(implant, width, length);

			t.x = theAnalyzer.atoi(targ[6]);
			t.y = theAnalyzer.atoi(targ[7]);

			// parse area and perimeter
			for (int i = 8; i < targ.length; i++)
			{
				int aIsPos = targ[i].indexOf("A_");
				int pIsPos = targ[i].indexOf("P_");
				if (aIsPos >= 0 && pIsPos >= 0)
				{
					int a = theAnalyzer.atoi(targ[i].substring(aIsPos+2));
					int p = theAnalyzer.atoi(targ[i].substring(pIsPos+2));
					char type = targ[i].charAt(0);
					int asrc = 0, adrn = 0, psrc = 0, pdrn = 0;
					if (type == 's')
					{
						asrc = a;
						psrc = p;
					} else if (type == 'd')
					{
						adrn = a;
						pdrn = p;
					}
					if (implant == PCHAN)
					{
						t.source.nCap += asrc * theConfig.lambda * theConfig.lambda * theConfig.CPDA +
							psrc * theConfig.lambda * theConfig.CPDP;
						t.drain.nCap += adrn * theConfig.lambda * theConfig.lambda * theConfig.CPDA +
							pdrn * theConfig.lambda * theConfig.CPDP;
					} else if (implant == NCHAN || implant == DEP)
					{
						t.source.nCap += asrc * theConfig.lambda * theConfig.lambda * theConfig.CDA +
							psrc * theConfig.lambda * theConfig.CDP;
						t.drain.nCap += adrn * theConfig.lambda * theConfig.lambda * theConfig.CDA +
							pdrn * theConfig.lambda * theConfig.CDP;
					}
				}
			}
		}

		// link it to the list
		readTransistorList.add(t);
	}

	/**
	 * accept a bunch of aliases for a node (= sim command).
	 */
	private void alias(String [] targ, String fileName, LineNumberReader lineReader)
	{
		if (targ.length < 3)
		{
			badArgCount(fileName, lineReader, targ);
			return;
		}

		Node n = getNode(targ[1]);

		for(int i = 2; i < targ.length; i++)
		{
			Node m = getNode(targ[i]);
			if (m == n) continue;

			if ((m.nFlags & POWER_RAIL) != 0)
			{
				Node swap = m;   m = n;   n = swap;
			}

			if ((m.nFlags & POWER_RAIL) != 0)
			{
				reportError(fileName, lineReader.getLineNumber(), "Can't alias the power supplies");
				continue;
			}

			n.nCap += m.nCap;
			m.nLink = n;
			m.nFlags |= ALIAS;
			m.nCap = 0;
			numNodes--;
			numAliases++;
		}
	}

	/**
	 * node threshold voltages (t sim command).
	 */
	private void nThresh(String [] targ, String fileName, LineNumberReader lineReader)
	{
		if (targ.length != 4)
		{
			badArgCount(fileName, lineReader, targ);
			return;
		}

		Node n = getNode(targ[1]);
		n.vLow = (float)theAnalyzer.atof(targ[2]);
		n.vHigh = (float)theAnalyzer.atof(targ[3]);
	}

	/**
	 * User delay for a node (D sim command).
	 */
	private void nDelay(String [] targ, String fileName, LineNumberReader lineReader)
	{
		if (targ.length != 4)
		{
			badArgCount(fileName, lineReader, targ);
			return;
		}

		Node n = getNode(targ[1]);
		n.nFlags |= USERDELAY;
		n.tpLH = (short)nsToDelta(theAnalyzer.atof(targ[2]));
		n.tpHL = (short)nsToDelta(theAnalyzer.atof(targ[3]));
	}

	/**
	 * add capacitance to a node (c sim command).
	 */
	private void nCap(String [] targ, String fileName, LineNumberReader lineReader)
	{
		if (targ.length == 3)
		{
			Node n = getNode(targ[1]);
			n.nCap += (float)theAnalyzer.atof(targ[2]);
		} else if (targ.length == 4)
		{
			// two terminal caps
			float cap = (float)(theAnalyzer.atof(targ[3]) / 1000);		// ff to pf conversion
			Node n = getNode(targ[1]);
			Node m = getNode(targ[2]);
			if (n != m)
			{
				// add cap to both nodes
				if (m != groundNode)	m.nCap += cap;
				if (n != groundNode)	n.nCap += cap;
			} else if (n == groundNode)
			{
				// same node, only GND makes sense
				n.nCap += cap;
			}
		} else
		{
			badArgCount(fileName, lineReader, targ);
		}
	}

    public String[] parseLine(String line) {
        return parseLine(line, false, theAnalyzer);
    }
    
	/**
	 * parse input line into tokens, filling up carg and setting targc
	 * @param expand true to expand iterators.  For example, the
	 * string "out.{1:10}" expands into ten arguments "out.1", ..., "out.10".
	 * The string can contain multiple iterators which will be expanded
	 * independently, e.g., "out{1:10}{1:20:2}" expands into 100 arguments.
	 */
	public static String [] parseLine(String line, boolean expand, SimAPI.Analyzer analyzer)
	{
		StringTokenizer st = new StringTokenizer(line, " \t");
		int total = st.countTokens();
		String [] strings = new String[total];
		boolean iteratorFound = false;
		for(int i=0; i= 0) iteratorFound = true;
		}

		// expand iterated keywords if requested
		if (iteratorFound && expand)
		{
			// expand iterators
			List listOfStrings = new ArrayList();
			for(int i=0; i expanded, SimAPI.Analyzer analyzer)
	{
		int itStart = arg.indexOf('{');
		if (itStart < 0)
		{
			// no iterator here: just add the string
			expanded.add(arg);
			return false;
		}
		int itEnd = arg.indexOf('}', itStart);
		if (itEnd < 0) return true;

		// parse range of values
		String iterator = arg.substring(itStart+1, itEnd);
		int firstColon = iterator.indexOf(':');
		if (firstColon < 0) return true;
		int secondColon = iterator.indexOf(':', firstColon);
		int start = analyzer.atoi(iterator);
		int stop = analyzer.atoi(iterator.substring(firstColon+1));
		int step = 1;
		if (secondColon >= 0) step = analyzer.atoi(iterator.substring(secondColon+1));

		// figure out correct step size
		if (step == 0) step = 1; else
			if (step < 0) step = -step;
		if (start > stop) step = -step;

		// expand the iterator
		String prefix = arg.substring(0, itStart);
		String suffix = arg.substring(itEnd+1);
		while ((step > 0 && start <= stop) || (step < 0 && start >= stop))
		{
			String full = prefix + start + suffix;
			if (expand(full, expanded, analyzer)) return true;
			start += step;
		}
		return false;
	}
    
    /**
     * Put triansitor into the circuit
     * @param gateName name of transistor gate network
     * @param sourceName name of transistor gate network
     * @param drainName drain name of transistor gate network
     * @param gateLength gate length (lambda)
     * @param gateWidth gate width (lambda)
     * @param activeArea active area (lambda^2)
     * @param activePerim active perim (lambda^2)
     * @param centerX x-coordinate of center (lambda)
     * @param centerY y coordinate of cneter (lambda)
     * @param isNTypeTransistor true if this is N-type transistor
     */
    public void putTransistor(String gateName, String sourceName, String drainName,
            double gateLength, double gateWidth,
            double activeArea, double activePerim,
            double centerX, double centerY,
            boolean isNTypeTransistor) {
        Trans t = new Trans();

        t.gate = getNode(gateName);
        t.source = getNode(sourceName);
        t.drain = getNode(drainName);
        long length = (long) (gateLength * theConfig.lambdaCM);
        long width = (long) (gateWidth * theConfig.lambdaCM);
        if (width <= 0 || length <= 0) {
            System.out.println("Bad transistor width=" + width + " or length=" + length);
            return;
        }
        t.x = (int) centerX;
        t.y = (int) centerY;

        ((Node) t.gate).nCap += length * width * theConfig.CTGA;

        if (isNTypeTransistor) {
            t.tType = NCHAN;

            t.source.nCap += activeArea * theConfig.lambda * theConfig.lambda * theConfig.CDA
                    + activePerim * theConfig.lambda * theConfig.CDP;
            t.drain.nCap += activeArea * theConfig.lambda * theConfig.lambda * theConfig.CDA
                    + activePerim * theConfig.lambda * theConfig.CDP;
            t.r = theConfig.rEquiv(NCHAN, width, length);
        } else {
            t.tType = PCHAN;

            t.source.nCap += activeArea * theConfig.lambda * theConfig.lambda * theConfig.CPDA
                    + activePerim * theConfig.lambda * theConfig.CPDP;
            t.drain.nCap += activeArea * theConfig.lambda * theConfig.lambda * theConfig.CPDA
                    + activePerim * theConfig.lambda * theConfig.CPDP;
            t.r = theConfig.rEquiv(PCHAN, width, length);
        }
        readTransistorList.add(t);

    }
    
    /**
     * Put resistor into the circuit
     * @param net1 name of first terminal network
     * @param net2 name of second terminal network
     * @param resistance resistance (ohm)
     */
    public void putResistor(String net1, String net2, double resistance) {
        Trans t = new Trans();
        t.tType = RESIST;
        t.gate = powerNode;
        t.source = getNode(net1);
        t.drain = getNode(net2);
        t.r = theConfig.rEquiv(RESIST, 0, (long) (resistance * theConfig.lambdaCM));

        // link it to the list
        readTransistorList.add(t);
    }

    /**
     * Put capacitor into the circuit
     * @param net1 name of first terminal network
     * @param net2 name of second terminal network
     * @param capacitance capacitance (pf)
     */
    public void putCapacitor(String net1, String net2, double capacitance) {
        float cap = (float) (capacitance / 1000);		// ff to pf conversion
        Node n = getNode(net1);
        Node m = getNode(net2);
        if (n != m) {
            // add cap to both nodes
            if (m != groundNode) {
                m.nCap += cap;
            }
            if (n != groundNode) {
                n.nCap += cap;
            }
        } else if (n == groundNode) {
            // same node, only GND makes sense
            n.nCap += cap;
        }
    }

	/**
	 * Load a .sim file into memory.
	 *
	 * A .sim file consists of a series of lines, each of which begins with a key letter.
	 * The key letter beginning a line determines how the remainder of the line is interpreted.
	 * The following are the list of key letters understood.
	 *
	 *   | units: s tech: tech format: MIT|LBL|SU
	 *     If present, this must be the first line in the .sim file.
	 *     It identifies the technology of this circuit as tech and gives a scale factor for units of linear dimension as s.
	 *     All linear dimensions appearing in the .sim file are multiplied by s to give centimicrons.
	 *     The format field signifies the sim variant. Electric only recognizes SU format. 
	 *   type g s d l w x y g=gattrs s=sattrs d=dattrs
	 *     Defines a transistor of type type. Currently, type may be e or d for NMOS, or p or n for CMOS.
	 *     The name of the node to which the gate, source, and drain of the transistor are connected are given by g, s, and d respectively.
	 *     The length and width of the transistor are l and w. The next two tokens, x and y, are optional.
	 *     If present, they give the location of a point inside the gate region of the transistor.
	 *     The last three tokens are the attribute lists for the transistor gate, source, and drain.
	 *     If no attributes are present for a particular terminal, the corresponding attribute list may be absent
	 *     (i.e, there may be no g= field at all).
	 *     The attribute lists gattrs, etc. are comma-separated lists of labels.
	 *     The label names should not include any spaces, although some tools can accept label names with
	 *     spaces if they are enclosed in double quotes. In version 6.4.5 and later the default format
	 *     produced by ext2sim is SU. In this format the attribute of the gate starting with S_ is the substrate node of the fet.
	 *     The attributes of the gate, and source and substrate starting with A_, P_ are the area and perimeter
	 *     (summed for that node only once) of the source and drain respectively. This addition to the format is backwards compatible. 
	 *   C n1 n2 cap
	 *     Defines a capacitor between nodes n1 and n2. The value of the capacitor is cap femtofarads.
	 *     NOTE: since many analysis tools compute transistor gate capacitance themselves from the
	 *     transistor's area and perimeter, the capacitance between a node and substrate (GND!)
	 *     normally does not include the capacitance from transistor gates connected to that node.
	 *     If the .sim file was produced by ext2sim(1), check the technology file that was used to
	 *     produce the original .ext files to see whether transistor gate capacitance is included or excluded;
	 *     see "Magic Maintainer's Manual 2 - The Technology File for details. 
	 *   R node res
	 *     Defines the lumped resistance of node node to be res ohms.
	 *   r node1 node2 res
	 *     Defines an explicit resistor between nodes node1 and node2 of resistance res ohms.
	 *   N node darea dperim parea pperim marea mperim
	 *     As an alternative to computed capacitances, some tools expect the total perimeter and area
	 *     of the polysilicon, diffusion, and metal in each node to be reported in the .sim file.
	 *     The N construct associates diffusion area darea (in square centimicrons) and diffusion
	 *     perimeter dperim (in centimicrons) with node node, polysilicon area parea and perimeter pperim,
	 *     and metal area marea and perimeter mperim. This construct is technology dependent and obsolete. 
	 *   = node1 node2
	 *     Each node in a .sim file is named implicitly by having it appear in a transistor definition.
	 *     All node names appearing in a .sim file are assumed to be distinct.
	 *     Some tools, such as esim(1), recognize aliases for node names.
	 *     The = construct allows the name node2 to be defined as an alias for the name node1.
	 *     Aliases defined by means of this construct may not appear anywhere else in the .sim file.
     * @param simReader Reader of .sim file
     * @param fileName file name for error messages
     * @return number of errors
     */
    public int inputSim(Reader simReader, String fileName) throws IOException
	{
		// read the file
		boolean rError = false;
		boolean aError = false;
//		String fileName = simFileURL.getFile();
//		Electric.startProgressDialog("import", fileName);
//		try
//		{
//			URLConnection urlCon = simFileURL.openConnection();
//			String contentLength = urlCon.getHeaderField("content-length");
//			long fileLength = -1;
//			try {
//				fileLength = Long.parseLong(contentLength);
//			} catch (Exception e) {}
//			long readSoFar = 0;
//			InputStream inputStream = urlCon.getInputStream();
//			InputStreamReader is = new InputStreamReader(inputStream);
//			LineNumberReader lineReader = new LineNumberReader(is);
            LineNumberReader lineReader = new LineNumberReader(simReader);
			for(;;)
			{
				String line = lineReader.readLine();
				if (line == null) break;
//				readSoFar += line.length() + 1;
//				Electric.setProgressValue((int)(readSoFar * 100 / fileLength));
				String [] targ = parseLine(line, false, theAnalyzer);
				if (targ.length == 0) continue;
				char firstCh = targ[0].charAt(0);
				switch (firstCh)
				{
					case '|':
						if (lineReader.getLineNumber() > 1) break;
						if (targ.length >= 2)
						{
							double lmbd = theAnalyzer.atof(targ[2]) / 100.0;
							if (lmbd != theConfig.lambda)
							{
								System.out.println("WARNING: sim file lambda (" + lmbd + "u) != config lambda (" +
									theConfig.lambda + "u), using config lambda");
							}
						}
						if (targ.length >= 6)
						{
							if (theConfig.CDA == 0.0 || theConfig.CDP == 0.0 ||
								theConfig.CPDA == 0.0 || theConfig.CPDP == 0.0)
							{
								System.out.println("Warning: missing area/perim cap values are zero");
							}
						}
						break;
					case 'e':
					case 'n':
						newTrans(NCHAN, targ, fileName, lineReader);
						break;
					case 'p':
						newTrans(PCHAN, targ, fileName, lineReader);
						break;
					case 'd':
						newTrans(DEP, targ, fileName, lineReader);
						break;
					case 'r':
						newTrans(RESIST, targ, fileName, lineReader);
						break;
					case 'N':
						nodeInfo(targ, fileName, lineReader);
						break;
					case 'M':
						nNodeInfo(targ, fileName, lineReader);
						break;
					case 'c':
					case 'C':
						nCap(targ, fileName, lineReader);
						break;
					case '=':
						alias(targ, fileName, lineReader);
						break;
					case 't':
						nThresh(targ, fileName, lineReader);
						break;
					case 'D':
						nDelay(targ, fileName, lineReader);
						break;
					case 'R':
						if (!rError)	// only warn about this 1 time
						{
							System.out.println(fileName + "Ignoring lumped-resistance ('R' construct)");
							rError = true;
						}
						break;
					case 'A':
						if (!aError)	// only warn about this 1 time
						{
							System.out.println(fileName + "Ignoring attribute-line ('A' construct)");
							aError = true;
						}
						break;
					default:
						reportError(fileName, lineReader.getLineNumber(), "Unrecognized input line (" + targ[0] + ")");
						if (checkErrs(fileName)) return numErrors;
				}
			}
//			inputStream.close();
//		} catch (IOException e)
//		{
//			System.out.println("Error reading file");
//		}
//        Electric.stopProgressDialog();
//		System.out.println("Loaded circuit, lambda=" + theConfig.lambda + "u");
        return numErrors;
	}

	public void initNetwork()
	{
		readTransistorList = new ArrayList();
		nodeHash = new HashMap();
		nodeList = new ArrayList();
		nodeIndexCounter = 1;
		warnVdd = warnGnd = false;
		maxTime = MAX_TIME;

		// initialize counts
		for(int i = 0; i < NTTYPES; i++)
			numTrans[i] = 0;
		numNodes = numAliases = 0;
		initHist();

		// initialize globals
		numEdges = 0;
		numPunted = 0;
		numConsPunted = 0;

		powerNode = getNode("Vdd");
		powerNode.nPot = HIGH;
		powerNode.nFlags |= (INPUT | POWER_RAIL);
		powerNode.head.inp = true;
		powerNode.head.val = HIGH;
		powerNode.head.punt = false;
		powerNode.head.hTime = 0;
		powerNode.head.rTime = powerNode.head.delay = 0;
		powerNode.head.next = lastHist;
		powerNode.curr = powerNode.head;

		groundNode = getNode("Gnd");
		groundNode.nPot = LOW;
		groundNode.nFlags |= (INPUT | POWER_RAIL);
		groundNode.head.inp = true;
		groundNode.head.val = LOW;
		groundNode.head.punt = false;
		groundNode.head.hTime = 0;
		groundNode.head.rTime = groundNode.head.delay = 0;
		groundNode.head.next = lastHist;
		groundNode.curr = groundNode.head;

		tCap = new Trans();
		tCap.source = null;
		tCap.drain = null;
		tCap.setSTrans(tCap);
		tCap.setDTrans(tCap);
		tCap.x = 0;

		numErrors = 0;
    }
    
    public void finishNetwork() {

		// connect all txtors to corresponding nodes
		connectNetwork();

		// sort the signal names
		Collections.sort(getNodeList(), new NodesByName());

		theModel.initEvent();
	}

    /**
     * Get lambda value in nanometers
     * @return lambda in nanometers
     */
    public double getLambda() {
        return theConfig.lambda;
    }
    
	private static class NodesByName implements Comparator
	{
		public int compare(Node n1, Node n2)
		{
			return n1.nName.compareToIgnoreCase(n2.nName);
		}
	}

	private void connectNetwork()
	{
		Node ndList = connectTransistors();
		makeParallel(ndList);

		// display information about circuit
		String infstr = numNodes + " nodes";
		if (numAliases != 0)
			infstr += ", " + numAliases + " aliases";
		for(int i = 0; i < NTTYPES; i++)
		{
			if (numTrans[i] == 0) continue;
			infstr += ", " + numTrans[i] + " " + transistorType[i] + " transistors";
			if (numOred[i] != 0)
			{
				infstr += " (" + numOred[i] + " parallel)";
			}
		}
		if (tCap.x != 0)
			infstr += " (" + tCap.x + " shorted)";
		System.out.println(infstr);
	}

	public boolean      withDriven;		/* TRUE if stage is driven by some input */

	/**
	 * Build a linked-list of nodes (using nLink entry in Node structure)
	 * which are electrically connected to node 'n'.  No special order
	 * is required so tree walk is performed non-recursively by doing a
	 * breath-first traversal.  The value caches for each transistor we
	 * come across are reset here.  Loops are broken at an arbitrary point
	 * and parallel transistors are identified.
	 */
	public void buildConnList(Node n)
	{
		int nPar = 0;

		n.nFlags &= ~VISITED;
		withDriven = false;

		Node next = n;
		Node thisOne = n.nLink = n;
		do
		{
			for(Trans t : thisOne.nTermList)
			{
				if (t.state == OFF) continue;
				if ((t.tFlags & CROSSED) != 0)	// Each transistor is crossed twice
				{
					t.tFlags &= ~CROSSED;
					continue;
				}
				t.setSThev(null);
				t.setDThev(null);

				Node other = otherNode(t, thisOne);
				if ((other.nFlags & INPUT) != 0)
				{
					withDriven = true;
					continue;
				}

				t.tFlags |= CROSSED;		// Crossing trans 1st time

				if (other.nLink == null)		// New node in this stage
				{
					other.nFlags &= ~VISITED;
					other.nLink = n;
					next.nLink = other;
					next = other;
					other.setTrans(t);		// we reach other through t
				}
				else if (!(theModel instanceof NewRStep))
					continue;
				else if (other.getTrans().hashTerms() == t.hashTerms())
				{					    // parallel transistors
					Trans tran = other.getTrans();
					if ((tran.tFlags & PARALLEL) != 0)
						t.setDTrans(parallelTransistors[tran.nPar]);
					else
					{
						if (nPar >= MAX_PARALLEL)
						{
							if (!parallelWarning)
							{
								System.out.println("There are too many transistors in parallel (> " + MAX_PARALLEL + ")");
								System.out.println("Simulation results may be inaccurate, to fix this you may have to");
								System.out.println("increase 'MAX_PARALLEL' in 'Sim.java'.");
								System.out.println("Note: This condition often occurs when Vdd or Gnd are not connected to all cells.");
								if (thisOne.nName != null && other.nName != null)
									System.out.println("      Check the vicinity of the following 2 nodes: " + thisOne.nName + " " + other.nName);
								parallelWarning = true;
							}
							t.tFlags |= PBROKEN;		// simply ignore it
							continue;
						}
						tran.nPar = (byte)(nPar++);
						tran.tFlags |= PARALLEL;
					}
					parallelTransistors[tran.nPar] = t;
					t.tFlags |= PBROKEN;
				} else
				{					// we have a loop, break it
					t.tFlags |= BROKEN;
				}
			}
		}
		while((thisOne = thisOne.nLink) != n);

		next.nLink = null;			// terminate connection list
	}

	/********************************************** HISTORY *******************************************/

	private void initHist()
	{
		HistEnt dummy = new HistEnt();
		lastHist = dummy;
		dummy.next = lastHist;
		dummy.hTime = maxTime;
		dummy.val = X;
		dummy.inp = true;
		dummy.punt = false;
		dummy.delay = dummy.rTime = 0;
	}

	/**
	 * Add a new entry to the history list.  Update curr to point to this change.
	 */
	public void addHist(Node node, int value, boolean inp, long time, long delay, long rTime)
	{
		numEdges++;
		HistEnt curr = node.curr;

		while(curr.next.punt)		// skip past any punted events
			curr = curr.next;

		HistEnt newH = new HistEnt();
		if (newH == null) return;

		newH.next = curr.next;
		newH.hTime = time;
		newH.val = (byte)value;
		newH.inp = inp;
		newH.punt = false;
		newH.delay = (short)delay;
		newH.rTime = (short)rTime;
		node.curr = curr.next = newH;
	}

	/**
	 * Add a punted event to the history list for the node.  Consecutive punted
	 * events are kept in punted-order, so that h.pTime < h.next.pTime.
	 * Adding a punted event does not change the current pointer, which always
	 * points to the last "effective" node change.
	 */
	public void addPunted(Node node, Eval.Event ev, long tim)
	{
		HistEnt h = node.curr;

		numPunted++;

		HistEnt newP = new HistEnt();

		newP.hTime = ev.nTime;
		newP.val = ev.eval;
		newP.inp = false;
		newP.punt = true;
		newP.delay = (short)ev.delay;
		newP.rTime = ev.rTime;
		newP.pTime = (short)(newP.hTime - tim);

		if (h.next.punt)		// there are some punted events already
		{
			numConsPunted++;
			do { h = h.next; } while(h.next.punt);
		}

		newP.next = h.next;
		h.next = newP;
	}

	public void backToTime(SimAPI.Node nd_)
	{
        Node nd = (Node)nd_;
		if ((nd.nFlags & (ALIAS | MERGED)) != 0) return;

		HistEnt h = nd.head;
		HistEnt p = h.getNextHist();
		while(p.hTime < curDelta)
		{
			h = p;
			p = p.getNextHist();
		}
		nd.curr = h;

		// queue pending events
		for(p = h, h = p.next; ; p = h, h = h.next)
		{
			long qTime;

			if (h.punt)
			{
				// if already punted, skip it
				long puntTime = h.hTime - h.pTime;
				if (puntTime < curDelta) continue;

				qTime = h.hTime - h.delay;	// pending, enqueue it
				if (qTime < curDelta)
				{
					long tmp = curDelta;
					curDelta = qTime;
					theModel.enqueueEvent(nd, h.val, h.delay, h.rTime);
					curDelta = tmp;
				}
				p.next = h.next;
				h = p;
			} else
			{
				// time at which history entry was enqueued
				qTime = h.hTime - h.delay;
				if (qTime < curDelta)		// pending, enqueue it
				{
					long tmp = curDelta;
					curDelta = qTime;
					theModel.enqueueEvent(nd, h.val, h.delay, h.rTime);
					curDelta = tmp;

					p.next = h.next;		// and free it
					h = p;
				}
				else
					break;
			}
		}

		p.next = lastHist;
		p = h;
		// p now points to the 1st event in the future (to be deleted)
		if (p != lastHist)
		{
			while(h.next != lastHist)
				h = h.next;
		}

		h = nd.curr;
		nd.nPot = h.val;
		nd.setTime(h.hTime);
		if (h.inp)
			nd.nFlags |= INPUT;

		if (nd.nGateList.size() != 0)		// recompute transistor states
		{
			for(Trans t : nd.nGateList)
			{
				t.state = (byte)theModel.computeTransState(t);
			}
		}
	}

	/************************************ PARALLEL *******************************************/

	/**
	 * Run through the list of nodes, collapsing all transistors with the same
	 * gate/source/drain into a compound transistor.
	 */
	private void makeParallel(Node nList)
	{
		for( ; nList != null; nList.nFlags &= ~VISITED, nList = nList.getNext())
		{
			for(int l1 = 0; l1 < nList.nTermList.size(); l1++)
			{
				Trans t1 = nList.nTermList.get(l1);
				int type = t1.tType;
				if ((type & (GATELIST | ORED)) != 0)
					continue;	// ORED implies processed, so skip as well

				long hval = t1.hashTerms();
				for(int l2 = l1+1; l2 < nList.nTermList.size(); l2++)
				{
					Trans t2 = nList.nTermList.get(l2);
					if (t1.gate != t2.gate || t2.hashTerms() != hval ||
						type != (t2.tType & ~ORED))
							continue;

					if ((t1.tType & ORED) == 0)
					{
						Trans t3 = new Trans();
						t3.r = new Resists();
						t3.r.dynRes[R_LOW] = t1.r.dynRes[R_LOW];
						t3.r.dynRes[R_HIGH] = t1.r.dynRes[R_HIGH];
						t3.r.rStatic = t1.r.rStatic;
						t3.gate = t1.gate;
						t3.source = t1.source;
						t3.drain = t1.drain;
						t3.tType = (byte)((t1.tType & ~ORLIST) | ORED);
						t3.state = t1.state;
						t3.tFlags = t1.tFlags;
						t3.tLink = t1;
						t1.setSTrans(null);
						t1.setDTrans(t3);
						int oldGateI = ((Node)t1.gate).nGateList.indexOf(t1);
						if (oldGateI >= 0) ((Node)t1.gate).nGateList.set(oldGateI, t3);
						int oldSourceI = t1.source.nTermList.indexOf(t1);
						if (oldSourceI >= 0) t1.source.nTermList.set(oldSourceI, t3);
						int oldDrainI = t1.drain.nTermList.indexOf(t1);
						if (oldDrainI >= 0) t1.drain.nTermList.set(oldDrainI, t3);
						t1.tType |= ORLIST;
						t1 = t3;
						numOred[baseType(t1.tType)]++;
					}

					Resists r1 = t1.r, r2 = t2.r;
					r1.rStatic = (float)combine(r1.rStatic, r2.rStatic);
					r1.dynRes[R_LOW] = (float)combine(r1.dynRes[R_LOW], r2.dynRes[R_LOW]);
					r1.dynRes[R_HIGH] = (float)combine(r1.dynRes[R_HIGH], r2.dynRes[R_HIGH]);

					((Node)t2.gate).nGateList.remove(t2);	// disconnect gate
					if (t2.source == nList)		// disconnect term1
					{
						t2.drain.nTermList.remove(t2);
					} else
					{
						t2.source.nTermList.remove(t2);
					}

					// disconnect term2
					nList.nTermList.remove(t2);

					if ((t2.tType & ORED) != 0)
					{
						Trans  t;

						for(t = t2.tLink; t.getSTrans() != null; t = t.getSTrans())
							t.setDTrans(t1);
						t.setSTrans(t1.tLink);
						t1.tLink = t2.tLink;
					} else
					{
						t2.tType |= ORLIST;		// mark as part of or
						t2.setDTrans(t1);		// this is the real txtor
						t2.setSTrans(t1.tLink);	// link unto t1 list
						t1.tLink = t2;
						numOred[baseType(t1.tType)]++;
					}
				}
			}
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy