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

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

/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: NewRStep.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;

/**
 * Event-driven timing simulation step for irsim.
 *
 * Use diamond shape region in R-V plane for dc voltage computation.
 * Use 2-pole 1-zero model for pure charge sharing delay calculations.
 * Use 2-pole zero-at-origin model for driven spike analysis.
 * Details of the models can be found in Chorng-Yeong Chu's thesis:
 * "Improved Models for Switch-Level Simulation" also available as a
 * Stanford Technical Report CSL-TR-88-368.
 */
public class NewRStep extends Eval
{
	/* flags used in thevenin structure */
	/** set for a definite rooted path */			private static final int	T_DEFINITE	= 0x01;
	/** set if user specified delay encountered */	private static final int	T_UDELAY	= 0x02;
	/** set if charge-sharing spike possible */		private static final int	T_SPIKE		= 0x04;
	/** set if this branch is driven */				private static final int	T_DRIVEN	= 0x08;
	/** set for the reference node in pure c-s */	private static final int	T_REFNODE	= 0x10;
	/** set if connecting through an X trans. */	private static final int	T_XTRAN		= 0x20;
	/** set if we should consider input slope */	private static final int	T_INT		= 0x40;
	/** set if branch driven to dominant voltage */	private static final int	T_DOMDRIVEN	= 0x80;
	/** nmos-pmos ratio for spike analysis */		private static final double	NP_RATIO	= 0.7;

	private static final int SPIKETBLSIZE    = 10;

	private static final int NLSPKMIN        = 0;
	private static final int NLSPKMAX        = 1;
	private static final int LINEARSPK       = 2;

	/**
	 * one for each possible dominant voltage
	 */
	private static class Dominant
	{
		/** list of nodes driven to this potential */	Sim.Node  nd;
		/** TRUE if this pot needs spike analysis */	boolean   spike;
	};

	private static class SpikeRec
	{
		/** charging delay */	double  chDelay;
		/** driven delay */		double  drDelay;
		/** spike peak */		float   peak;
		/** spike charge */		int     charge;
	}

	/** dominant voltage structure */		private Dominant  [] domPot;
	/** 1 if debug and node is watched */	private int          incLevel;

	private Sim.Thev  [] inputThev;

	private static float spikeTable[][][] =
	{
		/* non-linear nmos driven low / pmos driven high */
		new float[][] {
		/* .01 */  new float[] {0.005f,  0.051f,  0.106f,  0.163f, 0.225f,  0.293f,  0.367f,  0.452f, 0.552f,  0.683f,  0.899f},
		/* 0.1 */  new float[] {0.005f,  0.051f,  0.105f,  0.162f, 0.223f,  0.288f,  0.360f,  0.441f, 0.537f,  0.661f,  0.852f},
		/* 0.2 */  new float[] {0.005f,  0.051f,  0.104f,  0.159f, 0.217f,  0.278f,  0.345f,  0.419f, 0.505f,  0.614f,  0.768f},
		/* 0.3 */  new float[] {0.005f,  0.051f,  0.102f,  0.154f, 0.208f,  0.265f,  0.325f,  0.390f, 0.464f,  0.555f,  0.676f},
		/* 0.4 */  new float[] {0.005f,  0.050f,  0.099f,  0.148f, 0.197f,  0.248f,  0.300f,  0.355f, 0.417f,  0.490f,  0.583f},
		/* 0.5 */  new float[] {0.005f,  0.049f,  0.096f,  0.140f, 0.184f,  0.226f,  0.270f,  0.315f, 0.363f,  0.419f,  0.487f},
		/* 0.6 */  new float[] {0.005f,  0.048f,  0.090f,  0.129f, 0.166f,  0.200f,  0.234f,  0.269f, 0.304f,  0.344f,  0.392f},
		/* 0.7 */  new float[] {0.005f,  0.046f,  0.083f,  0.114f, 0.142f,  0.168f,  0.192f,  0.216f, 0.240f,  0.266f,  0.295f},
		/* 0.8 */  new float[] {0.005f,  0.042f,  0.071f,  0.093f, 0.112f,  0.128f,  0.142f,  0.156f, 0.169f,  0.182f,  0.198f},
		/* 0.9 */  new float[] {0.005f,  0.033f,  0.050f,  0.061f, 0.069f,  0.076f,  0.081f,  0.086f, 0.090f,  0.093f,  0.099f},
		/* .99 */  new float[] {0.003f,  0.008f,  0.009f,  0.009f, 0.009f,  0.010f,  0.010f,  0.010f, 0.010f,  0.010f,  0.010f},
		},

		/* non-linear nmos driven high / pmos driven low */
		new float[][] {
		/* .01 */  new float[] {0.100f,  0.313f,  0.441f,  0.540f, 0.623f,  0.696f,  0.762f,  0.824f, 0.882f,  0.937f,  0.984f},
		/* 0.1 */  new float[] {0.097f,  0.292f,  0.404f,  0.489f, 0.560f,  0.624f,  0.682f,  0.736f, 0.789f,  0.830f,  0.893f},
		/* 0.2 */  new float[] {0.094f,  0.272f,  0.370f,  0.443f, 0.503f,  0.557f,  0.606f,  0.652f, 0.698f,  0.745f,  0.793f},
		/* 0.3 */  new float[] {0.091f,  0.252f,  0.337f,  0.398f, 0.449f,  0.494f,  0.534f,  0.573f, 0.612f,  0.652f,  0.694f},
		/* 0.4 */  new float[] {0.087f,  0.232f,  0.304f,  0.355f, 0.396f,  0.432f,  0.465f,  0.496f, 0.527f,  0.560f,  0.594f},
		/* 0.5 */  new float[] {0.083f,  0.209f,  0.269f,  0.310f, 0.342f,  0.370f,  0.396f,  0.420f, 0.444f,  0.468f,  0.496f},
		/* 0.6 */  new float[] {0.078f,  0.184f,  0.231f,  0.262f, 0.286f,  0.307f,  0.325f,  0.343f, 0.360f,  0.377f,  0.397f},
		/* 0.7 */  new float[] {0.071f,  0.155f,  0.189f,  0.210f, 0.227f,  0.241f,  0.253f,  0.264f, 0.275f,  0.286f,  0.298f},
		/* 0.8 */  new float[] {0.061f,  0.120f,  0.140f,  0.153f, 0.162f,  0.169f,  0.176f,  0.182f, 0.187f,  0.193f,  0.199f},
		/* 0.9 */  new float[] {0.045f,  0.073f,  0.081f,  0.085f, 0.088f,  0.091f,  0.093f,  0.095f, 0.096f,  0.098f,  0.100f},
		/* .99 */  new float[] {0.009f,  0.010f,  0.010f,  0.010f, 0.010f,  0.010f,  0.010f,  0.010f, 0.010f,  0.010f,  0.010f},
		},

		/* linear RC (nmos-pmos mix)*/
		new float[][] {
		/* .01 */  new float[] {0.010f,  0.099f,  0.198f,  0.296f, 0.394f,  0.491f,  0.589f,  0.688f, 0.787f,  0.887f,  0.979f},
		/* 0.1 */  new float[] {0.010f,  0.095f,  0.185f,  0.272f, 0.357f,  0.441f,  0.525f,  0.610f, 0.699f,  0.792f,  0.887f},
		/* 0.2 */  new float[] {0.010f,  0.091f,  0.173f,  0.250f, 0.324f,  0.396f,  0.468f,  0.541f, 0.617f,  0.699f,  0.787f},
		/* 0.3 */  new float[] {0.010f,  0.087f,  0.162f,  0.230f, 0.294f,  0.355f,  0.416f,  0.477f, 0.541f,  0.610f,  0.688f},
		/* 0.4 */  new float[] {0.010f,  0.083f,  0.150f,  0.209f, 0.264f,  0.315f,  0.365f,  0.416f, 0.468f,  0.525f,  0.589f},
		/* 0.5 */  new float[] {0.010f,  0.078f,  0.137f,  0.188f, 0.233f,  0.275f,  0.315f,  0.355f, 0.396f,  0.441f,  0.491f},
		/* 0.6 */  new float[] {0.009f,  0.072f,  0.123f,  0.164f, 0.200f,  0.233f,  0.264f,  0.294f, 0.324f,  0.357f,  0.394f},
		/* 0.7 */  new float[] {0.009f,  0.065f,  0.106f,  0.138f, 0.164f,  0.188f,  0.209f,  0.230f, 0.250f,  0.272f,  0.296f},
		/* 0.8 */  new float[] {0.009f,  0.055f,  0.085f,  0.106f, 0.123f,  0.137f,  0.150f,  0.162f, 0.173f,  0.185f,  0.198f},
		/* 0.9 */  new float[] {0.008f,  0.039f,  0.055f,  0.065f, 0.072f,  0.078f,  0.083f,  0.087f, 0.091f,  0.095f,  0.099f},
		/* .99 */  new float[] {0.004f,  0.008f,  0.009f,  0.009f, 0.009f,  0.010f,  0.010f,  0.010f, 0.010f,  0.010f,  0.010f},
		}
	};

	private static float delayTable[][] =
	{
	/* .01 */  new float[] {9.12006e+00f,  6.31441e-01f,  2.57972e-01f,  1.44287e-01f,
	           9.08200e-02f,  6.01663e-02f,  4.03907e-02f,  2.65355e-02f,
	           1.61530e-02f,  7.81327e-03f,  9.32511e-04f},
	/* 0.1 */  new float[] {6.75920e+01f,  4.23025e+00f,  1.67348e+00f,  9.22844e-01f,
	           5.78068e-01f,  3.83611e-01f,  2.59448e-01f,  1.72782e-01f,
	           1.07510e-01f,  5.40007e-02f,  7.10297e-03f},
	/* 0.2 */  new float[] {1.19421e+02f,  7.15202e+00f,  2.80304e+00f,  1.54448e+00f,
	           9.70528e-01f,  6.47718e-01f,  4.41433e-01f,  2.96820e-01f,
	           1.86968e-01f,  9.55642e-02f,  1.30529e-02f},
	/* 0.3 */  new float[] {1.63622e+02f,  9.51381e+00f,  3.72112e+00f,  2.05847e+00f,
	           1.30175e+00f,  8.75381e-01f,  6.01600e-01f,  4.08196e-01f,
	           2.59717e-01f,  1.34386e-01f,  1.87625e-02f},
	/* 0.4 */  new float[] {2.01466e+02f,  1.14592e+01f,  4.49666e+00f,  2.50681e+00f,
	           1.59963e+00f,  1.08575e+00f,  7.53099e-01f,  5.15657e-01f,
	           3.31075e-01f,  1.72965e-01f,  2.44792e-02f},
	/* 0.5 */  new float[] {2.33031e+02f,  1.30456e+01f,  5.16491e+00f,  2.91360e+00f,
	           1.88135e+00f,  1.29123e+00f,  9.04789e-01f,  6.25272e-01f,
	           4.04824e-01f,  2.13117e-01f,  3.03870e-02f},
	/* 0.6 */  new float[] {2.57614e+02f,  1.42986e+01f,  5.75449e+00f,  3.30168e+00f,
	           2.16446e+00f,  1.50508e+00f,  1.06642e+00f,  7.43858e-01f,
	           4.85264e-01f,  2.56919e-01f,  3.66949e-02f},
	/* 0.7 */  new float[] {2.73495e+02f,  1.52373e+01f,  6.30568e+00f,  3.70558e+00f,
	           2.47626e+00f,  1.74816e+00f,  1.25340e+00f,  8.82200e-01f,
	           5.79179e-01f,  3.07615e-01f,  4.37233e-02f},
	/* 0.8 */  new float[] {2.76873e+02f,  1.59248e+01f,  6.91424e+00f,  4.20379e+00f,
	           2.87725e+00f,  2.06596e+00f,  1.49889e+00f,  1.06318e+00f,
	           7.00760e-01f,  3.71884e-01f,  5.21156e-02f},
	/* 0.9 */  new float[] {2.57710e+02f,  1.67902e+01f,  7.96238e+00f,  5.07908e+00f,
	           3.57464e+00f,  2.60911e+00f,  1.90987e+00f,  1.35912e+00f,
	           8.94002e-01f,  4.70028e-01f,  6.37819e-02f},
	/* .99 */  new float[] {1.96679e+02f,  2.57710e+01f,  1.38437e+01f,  9.11648e+00f,
	           6.44036e+00f,  4.66063e+00f,  3.35777e+00f,  2.33745e+00f,
	           1.49276e+00f,  7.51022e-01f,  9.21218e-02f}
	};


	public NewRStep(Sim sim)
	{
		super(sim);
		initThevs();
		theSim.tUnitDelay = 0;
		theSim.tDecay = 0;
		domPot = new Dominant[Sim.N_POTS];
		for(int i=0; i r.rDown.min) {
                                reff = 1 / ( (1/r.rDown.min) - (1/r.rUp.min));
                            } else {
                                reff = 1 / ( (1/r.rUp.min) - (1/r.rDown.min));
                            }
                            tau = reff * r.cA;
                        } else
						    tau = r.rMin * r.cA;
					} else if ((r.flags & T_DEFINITE) != 0)
					{
						tau = r.rMax * r.cA;
					} else
					{
						tau = r.rDom * r.cA;
					}

					if ((r.flags & T_INT) != 0 && r.tIn > 0.5)
					{
						delay = Math.sqrt(tau * tau + Sim.deltaToPS((long)r.tIn) * r.cA);
					} else
					{
						delay = tau;
					}
				}

				queueFVal(nd, r.finall, tau, delay);
			}

			if (domPot[dom].spike)
			{
				for(Sim.Node nd = domPot[dom].nd; nd != null; nd = nd.getThev().getN())
				{
					r = nd.getThev();
					if ((r.flags & T_SPIKE) == 0) continue;

					incLevel = ((theSim.irDebug & (Sim.DEBUG_TAUP | Sim.DEBUG_TW)) == (Sim.DEBUG_TAUP | Sim.DEBUG_TW) &&
						(nd.nFlags & Sim.WATCHED) != 0) ? 1 : 0;

					r.tauP = getTauP(nd, (Sim.Trans) null, dom, incLevel);

					r.tauP *= r.rDom / r.tauA;

					queueSpike(nd, computeSpike(nd, r, dom));
				}
			}
		}
	}

	private void schedulePureCS(Sim.Node nList)
	{
		Sim.Thev r = nList.getThev();

		int dom = r.finall;
		r.flags |= T_REFNODE;

		double tauP = 0.0;
		for(Sim.Node nd = nList; nd != null; nd = nd.nLink)
		{
			incLevel = ((theSim.irDebug & (Sim.DEBUG_TAU | Sim.DEBUG_TW)) == (Sim.DEBUG_TAU | Sim.DEBUG_TW) &&
				(nd.nFlags & Sim.WATCHED) != 0) ? 1 : 0;

			r = getTau(nd, (Sim.Trans) null, dom, incLevel);

			r.tauD = r.rDom * r.cA;

			switch(dom)
			{
				case Sim.LOW:
					r.tauA = r.rDom * (r.cA - r.cD * r.v.max);
					break;
				case Sim.HIGH:
					r.tauA = r.rDom * (r.cD * (1 - r.v.min) - r.cA);
					break;
				case Sim.X:			// approximate Vf = 0.5
					r.tauA = r.rDom * (r.cA - r.cD * 0.5);
					break;
			}
			tauP += r.tauA * nd.nCap;
		}

		r = nList.getThev();
		tauP = tauP / (r.cLow.min + r.cHigh.max);		// tauP = tauP / CT

		for(Sim.Node  nd = nList; nd != null; nd = nd.nLink)
		{
			r = nd.getThev();
			double delay = 0, tau = 0;
			if (r.finall != nd.nPot)
			{
				switch (r.finall)
				{
					case Sim.LOW:  tau = (r.tauA - tauP) / (1.0 - r.v.max);	 break;
					case Sim.HIGH: tau = (tauP - r.tauA) / r.v.min;		     break;
					case Sim.X:    tau = (r.tauA - tauP) * 2.0;		         break;
				}
				if (tau < 0.0) tau = 0.0;
				if (theSim.tUnitDelay != 0)
				{
					delay = theSim.tUnitDelay;   tau = 0.0;
				} else
					delay = tau;
			}

			queueFVal(nd, r.finall, tau, delay);
		}
	}

	/**
	 * Compute the final value for each node on the connection list.
	 * This routine will update V.min and V.max and add the node to the
	 * corresponding dom_driver entry.  Return TRUE if any node changes value.
	 */
	private boolean computeDC(Sim.Node nList)
	{
		boolean anyChange = false;
		for(Sim.Node thisOne = nList; thisOne != null; thisOne = thisOne.nLink)
		{
			incLevel = ((theSim.irDebug & (Sim.DEBUG_DC | Sim.DEBUG_TW)) == (Sim.DEBUG_DC | Sim.DEBUG_TW) &&
				(thisOne.nFlags & Sim.WATCHED) != 0) ? 1 : 0;

			Sim.Thev r = getDCVal(thisOne, null);
			thisOne.setThev(r);

			if (theSim.withDriven)
			{
				if (r.rDown.min >= Sim.LIMIT)
					r.v.min = 1;
				else
					r.v.min = r.rDown.min / (r.rDown.min + r.rUp.max);
				if (r.rUp.min >= Sim.LIMIT)
					r.v.max = 0;
				else
					r.v.max = r.rDown.max / (r.rDown.max + r.rUp.min);
			} else		// use charge/total charge if undriven
			{
				r.v.min = r.cHigh.min / (r.cHigh.min + r.cLow.max);
				r.v.max = r.cHigh.max / (r.cHigh.max + r.cLow.min);
			}

			if (r.v.min >= thisOne.vHigh)
				r.finall = Sim.HIGH;
			else if (r.v.max <= thisOne.vLow)
				r.finall = Sim.LOW;
			else
				r.finall = Sim.X;

			if (theSim.withDriven)
			{
				/*
				 * if driven and indefinite, driven value must equal
				 * charging value otherwise the final value is X
				 */
				if (r.finall != Sim.X && (r.flags & T_DEFINITE) == 0)
				{
					char cs_val = Sim.X;// always X
					if (r.cHigh.min >= thisOne.vHigh * (r.cHigh.min + r.cLow.max))
						cs_val = Sim.HIGH;
					else if (r.cHigh.max <= thisOne.vLow * (r.cHigh.max + r.cLow.min))
						cs_val = Sim.LOW;

					if (cs_val != r.finall)
						r.finall = Sim.X;
				}

				r.setN(domPot[r.finall].nd);		// add it to list
				domPot[r.finall].nd = thisOne;

				// possible spike if no transition and opposite charge exists
				if (r.finall == thisOne.nPot && (
					(r.finall == Sim.LOW && r.cHigh.min > Sim.SMALL) ||
					(r.finall == Sim.HIGH && r.cLow.min > Sim.SMALL)))
				{
					r.flags |= T_SPIKE;
					domPot[r.finall].spike = true;
					anyChange = true;
				}
			}

			if (r.finall != thisOne.nPot)
				anyChange = true;

			if (((theSim.irDebug & Sim.DEBUG_DC) == Sim.DEBUG_DC &&
				(thisOne.nFlags & Sim.WATCHED) != 0))
					printFVal(thisOne, r);
		}
		return anyChange;
	}

	/**
	 * Compute the parameters used to calculate the final value (cHigh, cLow,
	 * rUp, rDown) by doing a depth-first traversal of the tree rooted at node
	 * 'n'.  The traversal is done by a recursive walk through the tree. Note that
	 * the stage is already a simple tree; loops are broken by buildConnList.  As
	 * a side effect also compute Req of the present transistor and all other
	 * transistors in parallel with it, and any specified user delays.
	 * The parameters are:
	 *
	 * n      : the node whose dc parameters we want.
	 * tran   : the transistor that leads to 'n' (null if none).
	 * level  : level of recursion if we are debugging this node, else 0.
	 */
	private Sim.Thev getDCVal(Sim.Node n, Sim.Trans tran)
	{
		if ((n.nFlags & Sim.INPUT) != 0)
		{
			Sim.Thev r = new Sim.Thev(inputThev[n.nPot]);
			return r;
		}

		Sim.Thev r = new Sim.Thev();
		switch (n.nPot)
		{
			case Sim.LOW:   r.cLow.min = r.cLow.max = n.nCap;	break;
			case Sim.X:     r.cLow.max = r.cHigh.max = n.nCap;	break;
			case Sim.HIGH:  r.cHigh.min = r.cHigh.max = n.nCap;	break;
		}

		for(Sim.Trans t : n.nTermList)
		{
			// ignore path going back or through a broken loop
			if (t == tran || t.state == Sim.OFF || (t.tFlags & (Sim.BROKEN | Sim.PBROKEN)) != 0)
				continue;

			Sim.Thev cache;
			Sim.Node other;
			if (n == t.source)
			{
				other = t.drain;	cache = t.getDThev();
			} else
			{
				other = t.source;	cache = t.getSThev();
			}

			// if cache is not empty use the value found there, otherwise
			// compute what is on the other side of the transistor and
			// transmit the result through a series operation.
			if (cache == null)
			{
				cache = seriesOP(getDCVal(other, t), t);
				if (n == t.source)
				{
					t.setDThev(cache);
				} else
				{
					t.setSThev(cache);
				}
			}
			parallelOP(r, cache);
		}

		if ((n.nFlags & Sim.USERDELAY) != 0)		// record user delays, if any
		{
			r.tpLH = n.tpLH; r.tpHL = n.tpHL;
			r.flags |= T_UDELAY;
		}

		return r;
	}

	/**
	 * The following methods set Req to the appropriate dynamic resistance.
	 * If the transistor state is UNKNOWN then also set the T_XTRAN flag.
	 */
	private void getReq(Sim.Thev r, Sim.Trans t, int type)
	{
		if ((t.tFlags & Sim.PARALLEL) != 0)
			getParallel(r, t, type);
		else
		{
			r.req.min = t.r.dynRes[type];
			if (t.state == Sim.UNKNOWN)
			{
				r.flags |= T_XTRAN;
			} else
			{
				r.req.max = t.r.dynRes[type];
			}
		}
	}

	private void getMinR(Sim.Thev r, Sim.Trans t)
	{
		if ((t.tFlags & Sim.PARALLEL) != 0)
			getMinParallel(r, t);
		else
		{
			r.req.min = Math.min(t.r.dynRes[Sim.R_HIGH], t.r.dynRes[Sim.R_LOW]);
			if (t.state == Sim.UNKNOWN)
			{
				r.flags |= T_XTRAN;
			} else
			{
				r.req.max = r.req.min;
			}
		}
	}

	/**
	 * Do the same as getReq but deal with parallel transistors.
	 */
	private void getParallel(Sim.Thev r, Sim.Trans t, int restype)
	{
		Sim.Resists rp = t.r;
		double gMin = 1.0 / rp.dynRes[restype];
		double gMax = (t.state == Sim.UNKNOWN) ? 0.0 : gMin;

		for(t = theSim.parallelTransistors[t.nPar]; t != null; t = t.getDTrans())
		{
			rp = t.r;
			gMin += 1.0 / rp.dynRes[restype];
			if (t.state != Sim.UNKNOWN)
				gMax += 1.0 / rp.dynRes[restype];
		}
		r.req.min = 1.0 / gMin;
		if (gMax == 0.0)
		{
			r.flags |= T_XTRAN;
		} else
		{
			r.req.max = 1.0 / gMax;
		}
	}

	/**
	 * Do the same as getParallel but use the minimum dynamic resistance.
	 */
	private void getMinParallel(Sim.Thev r, Sim.Trans t)
	{
		Sim.Resists rp = t.r;
		double gMin = 1.0 / Math.min(rp.dynRes[Sim.R_LOW], rp.dynRes[Sim.R_HIGH]);
		double gMax = (t.state == Sim.UNKNOWN) ? 0.0 : gMin;

		for(t = theSim.parallelTransistors[t.nPar]; t != null; t = t.getDTrans())
		{
			rp = t.r;
			double tmp = 1.0 / Math.min(rp.dynRes[Sim.R_LOW], rp.dynRes[Sim.R_HIGH]);
			gMin += tmp;
			if (t.state != Sim.UNKNOWN)
				gMax += tmp;
		}
		r.req.min = 1.0 / gMin;
		if (gMax == 0.0)
		{
			r.flags |= T_XTRAN;
		} else
		{
			r.req.max = 1.0 / gMax;
		}
	}

	/**
	 * Add transistor 't' in series with thevenin struct 'r'.  As a side effect
	 * set Req for 't'.  The midpoint voltage is used to determine whether to
	 * use the dynamic-high or dynamic-low resistance.  If the branch connecting
	 * to 't' is not driven by an input then use the charge information.  The
	 * current estimates of both resistance or capacitance is used.
	 */
	private Sim.Thev seriesOP(Sim.Thev r, Sim.Trans t)
	{
		if ((r.flags & T_DRIVEN) == 0)
		{
			if (r.cHigh.min > r.cLow.max)
				getReq(r, t, Sim.R_HIGH);
			else if (r.cHigh.max < r.cLow.min)
				getReq(r, t, Sim.R_LOW);
			else
				getMinR(r, t);
			return r;		// no driver, so just set Req
		}

		if (r.rDown.min > r.rUp.max)
			getReq(r, t, Sim.R_HIGH);
		else if (r.rDown.max < r.rUp.min)
			getReq(r, t, Sim.R_LOW);
		else
			getMinR(r, t);

		double upMin = r.rUp.min;
		double downMin = r.rDown.min;

		if (upMin < Sim.LIMIT)
			r.rUp.min += r.req.min * (1.0 + upMin / r.rDown.max);
		if (downMin < Sim.LIMIT)
			r.rDown.min += r.req.min * (1.0 + downMin / r.rUp.max);
		if ((r.flags & T_XTRAN) != 0)
		{
			r.flags &= ~T_DEFINITE;
			r.rUp.max = r.rDown.max = Sim.LARGE;
		} else
		{
			if (r.rUp.max < Sim.LIMIT)
				r.rUp.max += r.req.max * (1.0 + r.rUp.max / downMin);
			if (r.rDown.max < Sim.LIMIT)
				r.rDown.max += r.req.max * (1.0 + r.rDown.max / upMin);
		}
		return r;
	}

	/**
	 * make oldR = (oldR || newR), but watch out for infinite resistances.
	 */
	private double doParallel(double oldR, double newR)
	{
		if (oldR > Sim.LIMIT) return newR;
		if (newR > Sim.LIMIT) return oldR;
		return Sim.combine(oldR, newR);
	}

	/**
	 * Combine the new resistance block of the tree walk with the current one.
	 * Accumulate the low and high capacitances, the user-specified
	 * delay (if any), and the driven flag of the resulting structure.
	 */
	private void parallelOP(Sim.Thev r, Sim.Thev newRB)
	{
		r.cLow.max += newRB.cLow.max;
		r.cHigh.max += newRB.cHigh.max;
		if ((newRB.flags & T_XTRAN) == 0)
		{
			r.cLow.min += newRB.cLow.min;
			r.cHigh.min += newRB.cHigh.min;
		}

		// Accumulate the minimum user-specified delay only if the new block
		// has some drive associated with it.
		if ((newRB.flags & (T_DEFINITE | T_UDELAY)) == (T_DEFINITE | T_UDELAY))
		{
			if ((r.flags & T_UDELAY) != 0)
			{
				r.tpLH = (short)Math.min(r.tpLH, newRB.tpLH);
				r.tpHL = (short)Math.min(r.tpHL, newRB.tpHL);
			} else
			{
				r.tpLH = newRB.tpLH;
				r.tpHL = newRB.tpHL;
				r.flags |= T_UDELAY;
			}
		}

		if ((newRB.flags & T_DRIVEN) == 0)
			return;				// undriven, just update caps

		r.flags |= T_DRIVEN;		// combined result is driven

		r.rUp.min = doParallel(r.rUp.min, newRB.rUp.min);
		r.rDown.min = doParallel(r.rDown.min, newRB.rDown.min);

		if ((r.flags & newRB.flags & T_DEFINITE) != 0)	// both definite blocks
		{
			r.rUp.max = doParallel(r.rUp.max, newRB.rUp.max);
			r.rDown.max = doParallel(r.rDown.max, newRB.rDown.max);
		}
		else if ((newRB.flags & T_DEFINITE) != 0)		// only new is definite
		{
			r.rUp.max = newRB.rUp.max;
			r.rDown.max = newRB.rDown.max;
			r.flags |= T_DEFINITE;			    // result is definite
		} else				// new (perhaps r) is indefinite
		{
			if (newRB.rUp.max < r.rUp.max)	r.rUp.max = newRB.rUp.max;
			if (newRB.rDown.max < r.rDown.max) r.rDown.max = newRB.rDown.max;
		}
	}

	/**
	 * Determine the input time-constant (input-slope * rStatic).  We are only
	 * interested in transistors that just turned on (its gate has a transition
	 * at time == Sched.curDelta).  We must be careful not to report as a transition
	 * nodes that stop being inputs (hist.delay == 0 and hist.inp == 0).
	 */
	private boolean isCurrTransition(Sim.HistEnt h)
	{
		return h.hTime == theSim.curDelta && (h.inp || h.delay != 0);
	}

	/**
	 * Return TRUE if we should consider the input slope of this transistor.  As
	 * a side-effect, return the input time constant in 'ptin'.
	 */
	private boolean getTin(Sim.Trans t, Electric.MutableDouble ptin)
	{
		Sim.HistEnt  h;
		boolean isInt = false;

		if (t.state != Sim.ON)
			return false;

		if ((t.tType & Sim.GATELIST) == 0)
		{
			h = ((Sim.Node)t.gate).curr;
			if (isCurrTransition(h))
			{
				ptin.setValue(h.rTime * t.r.rStatic);
				isInt = true;
			}
		} else
		{
			double tmp = 0.0;

			for(t = (Sim.Trans) t.gate; t != null; t = t.getSTrans())
			{
				h = ((Sim.Node)t.gate).curr;
				if (isCurrTransition(h))
				{
					isInt = true;
					tmp += h.rTime * t.r.rStatic;
				}
			}
			ptin.setValue(tmp);
		}
		return isInt;
	}

	/** combine 2 resistors in parallel, watch out for zero resistance */
	private double combineR(double a, double b) { return ((a + b <= Sim.SMALL) ? 0 : Sim.combine(a, b)); }

	private boolean getParallelTin(Sim.Trans t, Electric.MutableDouble iTau)
	{
		Electric.MutableDouble tin = new Electric.MutableDouble(0);
		boolean isInt = getTin(t, tin);

		for(t = theSim.parallelTransistors[t.nPar]; t != null; t = t.getDTrans())
		{
			Electric.MutableDouble tmp = new Electric.MutableDouble(0);
			if (getTin(t, tmp))
			{
				tin.setValue(isInt ? combineR(tin.doubleValue(), tmp.doubleValue()) : tmp.doubleValue());
				isInt = true;
			}
			iTau.setValue(tin.doubleValue());
		}
		return isInt;
	}

	/**
	 * Compute the parameters needed to calculate the 1st order time-constants
	 * (rMin, rDom, rMax, Ca, Cd, Tin) by doing a depth-first traversal of the
	 * tree rooted at node 'n'.  The parameters are gathered by performing a
	 * recursive tree walk similar to computeDC.  As a side effect, the tauP
	 * field will contain the multiplication factor to move a capacitor across
	 * a transistor using 'current distribution', this field may be required
	 * later when computing tauP.  The parameters are:
	 *
	 * n      : the node whose time-constant parameters we want.
	 * dom    : the value of the dominant driver for this stage.
	 * tran   : the transistor that leads to 'n' (null if none).
	 *
	 * This routine can be called more than once if the stage is dominated by
	 * more than 1 potential, hence the tauDone flag keeps track of the potential
	 * for which the parameters stored in the cache were computed.  If the flag
	 * value and the current dominant potential do not match, we go ahead and
	 * recompute the values.
	 */
	private Sim.Thev getTau(Sim.Node n, Sim.Trans tran, int dom, int level)
	{
		Sim.Thev r;
		if (tran == null)
			r = n.getThev();
		else
			r = (tran.source == n) ? tran.getSThev() : tran.getDThev();

		r.tauDone = (char)dom;

		if ((n.nFlags & Sim.INPUT) != 0)
		{
			r.tIn = r.rMin = r.cA = r.cD = 0.0;
			if (n.nPot == dom)
			{
				r.rDom = r.rMax = 0.0;
				r.flags |= T_DOMDRIVEN;
			} else
			{
				r.flags &= ~(T_DOMDRIVEN | T_INT);
				if (dom == Sim.X)
					r.rDom = r.rMax = 0.0;
				else
					r.rDom = r.rMax = Sim.LARGE;
			}
			return r;
		}

		if ((n.getThev().flags & T_REFNODE) != 0)	    // reference node in pure CS
		{
			r.rMin = r.rDom = r.rMax = 0.0;
			r.cA = r.cD = 0.0;
			return r;
		}

		r.rMin = r.rDom = r.rMax = Sim.LARGE;
		r.cD = n.nCap;
		if (dom == Sim.X)			// assume X nodes are charged high
		{
            if (theSim.isDelayedX) {
                // X nodes are charged to X through Rup and Rdown fighting
                r.cA = n.nCap;
            } else
			    r.cA = (n.nPot == Sim.LOW) ? 0.0 : n.nCap;
		} else
		{
			r.cA = (n.nPot == dom) ? 0.0 : n.nCap;
		}

		r.tIn = 0.0;
		r.flags &= ~(T_DOMDRIVEN | T_INT);

		for(Sim.Trans t : n.nTermList)
		{
			if (t.state == Sim.OFF || t == tran || (t.tFlags & (Sim.BROKEN | Sim.PBROKEN)) != 0)
				continue;
			Sim.Node  other;
			Sim.Thev cache;
			if (n == t.source)
			{
				other = t.drain;	cache = t.getDThev();
			} else
			{
				other = t.source;	cache = t.getSThev();
			}
			if (cache.tauDone != dom)
			{
				Electric.MutableDouble oldR = new Electric.MutableDouble(0);

				cache = getTau(other, t, dom, level + incLevel);

				// Only use input slope for xtors on the dominant (driven) path
				if ((cache.flags & T_DOMDRIVEN) != 0)
				{
					boolean inputTau = (t.tFlags & Sim.PARALLEL) != 0 ? getParallelTin(t, oldR) : getTin(t, oldR);
					if (inputTau)
					{
						cache.flags |= T_INT;
						cache.tIn += oldR.doubleValue();
					}
				}

				oldR.setValue(cache.rDom);

				cache.rMin += cache.req.min;
				cache.rDom += cache.req.min;
				if ((cache.flags & T_XTRAN) != 0)
				{
					cache.rMax = Sim.LARGE;
				} else
				{
					cache.rMax += cache.req.max;
				}

				// Exclude capacitors if the other side of X transistor == dom
				if ((cache.flags & T_XTRAN) != 0 && other.nPot == dom)
				{
					cache.tauP = cache.cA = cache.cD = 0.0;
				} else if (oldR.doubleValue() > Sim.LIMIT)
				{
					cache.tauP = 1.0;
				} else
				{
					cache.tauP = oldR.doubleValue() / cache.rDom;
					cache.cA *= cache.tauP;
					cache.cD *= cache.tauP;
				}
			}

			r.cA += cache.cA;
			r.cD += cache.cD;
			r.rMin = Sim.combine(r.rMin, cache.rMin);
			if (r.rDom > Sim.LIMIT)
			{
				r.rDom = cache.rDom;
				r.rMax = cache.rMax;
			} else if (cache.rDom < Sim.LIMIT)
			{
				r.rDom = Sim.combine(r.rDom, cache.rDom);
				r.rMax = Sim.combine(r.rMax, cache.rMax);
			}
			if ((cache.flags & T_DOMDRIVEN) != 0)
				r.flags |= T_DOMDRIVEN;	// at least 1 dominant driven path

			if ((cache.flags & T_INT) != 0)
			{
				if ((r.flags & T_INT) != 0)
					r.tIn = combineR(r.tIn, cache.tIn);
				else
				{
					r.tIn = cache.tIn;
					r.flags |= T_INT;
				}
			}
		}

		if (level > 0)
			printTau(n, r, level);

		return r;
	}

	/**
	 * Calculate the 2nd order time constant (tauP) for the net configuration
	 * as seen through node 'n'.  The net traversal and the parameters are
	 * similar to 'getTau'.  Note that at this point we have not have calculated
	 * tauA for nodes not driven to the dominant potential, hence we need to
	 * compute those by first calling getTau.  This routine will update the tauP
	 * entry as well as the tauPDone flag.
	 */
	private double getTauP(Sim.Node n, Sim.Trans tran, int dom, int level)
	{
		if ((n.nFlags & Sim.INPUT) != 0) return 0.0;

		Sim.Thev r = n.getThev();
		if (r.tauDone != dom)		// compute tauA for the node
		{
			r = getTau(n, (Sim.Trans) null, dom, 0);
			r.tauA = r.rDom * r.cA;
			r.tauD = r.rDom * r.cD;
		}

		double taup = r.tauA * n.nCap;

		for(Sim.Trans t : n.nTermList)
		{
			if (t.state == Sim.OFF || t == tran || (t.tFlags & (Sim.BROKEN | Sim.PBROKEN)) != 0)
				continue;

			Sim.Node other;
			if (t.source == n)
			{
				other = t.drain;	r = t.getDThev();
			} else
			{
				other = t.source;	r = t.getSThev();
			}

			if (r.tauPDone != dom)
			{
				r.tauP *= getTauP(other, t, dom, level + incLevel);
				r.tauPDone = (char)dom;
			}
			taup += r.tauP;
		}
		if (level > 0)
			printTauP(n, level, taup);

		return taup;
	}

	/**
	 * Compute the size of spike.  If the spike is too small return null, else
	 * fill in the appropriate structure and return a pointer to it.  In order
	 * to determine in which table to lookup the spike peak we look at the
	 * conductivity of all ON transistors connected to node 'nd'; the type with
	 * the largest conductivity determines whether it is mostly an nmos or pmos
	 * network.  This simple scheme should work for most simple nets.
	 */
	private SpikeRec computeSpike(Sim.Node nd, Sim.Thev r, int dom)
	{
		if (r.tauP <= Sim.SMALL)		// no capacitance, no spike
		{
			if ((theSim.irDebug & Sim.DEBUG_SPK) != 0 && (nd.nFlags & Sim.WATCHED) != 0)
				System.out.println(" spike(" + nd.nName + ") ignored (taup=0)");
			return null;
		}

		int rType = (dom == Sim.LOW) ? Sim.R_LOW : Sim.R_HIGH;
		float nmos = 0, pmos = 0;
		for(Sim.Trans t : nd.nTermList)
		{
			if (t.state == Sim.OFF || (t.tFlags & Sim.BROKEN) != 0)
				continue;
			if (Sim.baseType(t.tType) == Sim.PCHAN)
				pmos += 1.0f / t.r.dynRes[rType];
			else
				nmos += 1.0f / t.r.dynRes[rType];
		}
		int tabIndex = 0;
		if (nmos > NP_RATIO * (pmos + nmos))		// mostly nmos
			tabIndex = (rType == Sim.R_LOW) ? NLSPKMIN : NLSPKMAX;
		else if (pmos > NP_RATIO * (pmos + nmos))		// mostly pmos
			tabIndex = (rType == Sim.R_LOW) ? NLSPKMAX : NLSPKMIN;
		else
			tabIndex = LINEARSPK;

		int alpha = (int) (SPIKETBLSIZE * r.tauA / (r.tauA + r.tauP - r.tauD));
		if (alpha < 0) alpha = 0; else
			if (alpha > SPIKETBLSIZE) alpha = SPIKETBLSIZE;

		int beta = (int) (SPIKETBLSIZE * (r.tauD - r.tauA) / r.tauD);
		if (beta < 0) beta = 0; else
			if (beta > SPIKETBLSIZE) beta = SPIKETBLSIZE;

		SpikeRec spk = new SpikeRec();
		spk.peak = spikeTable[tabIndex][beta][alpha];
		spk.chDelay = delayTable[beta][alpha];

		if (dom == Sim.LOW)
		{
			if (spk.peak <= nd.vLow)		// spike is too small
			{
				if ((theSim.irDebug & Sim.DEBUG_SPK) != 0 && (nd.nFlags & Sim.WATCHED) != 0)
					printSpk(nd, r, tabIndex, dom, alpha, beta, spk, false);
				return null;
			}
			spk.charge = (spk.peak >= nd.vHigh) ? Sim.HIGH : Sim.X;
		} else	// dom == HIGH
		{
			if (spk.peak <= 1.0 - nd.vHigh)
			{
				if ((theSim.irDebug & Sim.DEBUG_SPK) != 0 && (nd.nFlags & Sim.WATCHED) != 0)
					printSpk(nd, r, tabIndex, dom, alpha, beta, spk, false);
				return null;
			}
			spk.charge = (spk.peak >= 1.0 - nd.vLow) ? Sim.LOW : Sim.X;
		}

		spk.chDelay *= r.tauA * r.tauD / r.tauP;

		if (r.rMax < Sim.LARGE)
			spk.drDelay = r.rMax * r.cA;
		else
			spk.drDelay = r.rDom * r.cA;

		if ((theSim.irDebug & Sim.DEBUG_SPK) != 0 && (nd.nFlags & Sim.WATCHED) != 0)
			printSpk(nd, r, tabIndex, dom, alpha, beta, spk, true);
		return spk;
	}

	private void printFVal(Sim.Node n, Sim.Thev r)
	{
		System.out.print(" final_value(" + n.nName + ")  V=[" + r.v.min + ", " + r.v.max + "]  => " +
			Sim.vChars.charAt(r.finall));
		System.out.println((r.flags & T_SPIKE) != 0 ? "  (spk)" : "");
	}

	private void printFinal(Sim.Node nd, boolean queued, double tau, long delay)
	{
		Sim.Thev r = nd.getThev();
		long dtau = Sim.psToDelta(tau);

		System.out.print(" [event " + theSim.curNode.nName + "->" +
		Sim.vChars.charAt(theSim.curNode.nPot) + " @ " + Sim.deltaToNS(theSim.curDelta) + "ns] ");

		System.out.print(queued ? ("causes " + (theSim.withDriven ? "" : "CS") + "transition for") : "evaluates");

		System.out.print(" " + nd.nName + ": " + Sim.vChars.charAt(nd.nPot) + " -> " + Sim.vChars.charAt(r.finall));
		System.out.println(" (tau=" + Sim.deltaToPS(dtau) + "ps, delay=" + Sim.deltaToPS(delay) + "ps)");
	}

	private void printSpike(Sim.Node nd, SpikeRec spk, long chDelay, long drDelay)
	{
		System.out.print("  [event " + theSim.curNode.nName + "->" +
				Sim.vChars.charAt(theSim.curNode.nPot) + "@ " + Sim.deltaToNS(theSim.curDelta) + "ns] causes ");
		if (drDelay <= chDelay)
			System.out.print("suppressed ");

		System.out.print("spike for " + nd.nName + ": " + Sim.vChars.charAt(nd.nPot) + " -> " +
			Sim.vChars.charAt(spk.charge) + " -> " + Sim.vChars.charAt(nd.nPot));
		System.out.println(" (peak=" + spk.peak + " delay: ch=" + Sim.deltaToPS(chDelay) + "ps, dr=" + Sim.deltaToPS(drDelay) + "ps)");
	}

	private void printTauP(Sim.Node n, int level, double taup)
	{
		System.out.println("tauP(" + n.nName + ") = " + Sim.psToNS(taup) + " ns");
	}

	private void printTau(Sim.Node n, Sim.Thev r, int level)
	{
		System.out.println(" ...............compute_tau(" + n.nName + ")");
		System.out.print  ("                {Rmin=" + rToAscii(r.rMin) + "  Rdom=" + rToAscii(r.rDom) + "  Rmax=" + rToAscii(r.rMax) + "}");
		System.out.println(" {Ca=" + r.cA + "  Cd=" + r.cD + "}");

		System.out.print  ("                tauA=" + Sim.psToNS(r.rDom * r.cA) + "  tauD=" + Sim.psToNS(r.rDom * r.cD) + " ns, RTin=");
		if ((r.flags & T_INT) != 0)
			System.out.println(Sim.deltaToNS((long)r.tIn) + " ohm*ns");
		else
			System.out.println("-");
	}

	private String rToAscii(double r)
	{
		if (r >= Sim.LIMIT) return " - ";
		if (r > 1.0)
		{
			int exp = 0;
			for( ; r >= 1000.0; exp++, r *= 0.001) ;
			return theSim.theAnalyzer.formatDouble(r) + " KMG".charAt(exp);
		}
		return theSim.theAnalyzer.formatDouble(r);
	}


	private void printSpk(Sim.Node nd, Sim.Thev r, int tab, int dom, int alpha,
		int beta, SpikeRec spk, boolean isSpk)
	{
		System.out.print(" spike_analysis(" + nd.nName + "):");
		String net_type = "";
		if (tab == LINEARSPK)
			net_type = "n-p mix";
		else if (tab == NLSPKMIN)
			net_type = (dom == Sim.LOW) ? "nmos" : "pmos";
		else
			net_type = (dom == Sim.LOW) ? "pmos" : "nmos";

		System.out.print(" " + net_type + " driven " + ((dom == Sim.LOW) ? "low" : "high"));
		System.out.print("{tauA=" + Sim.psToNS(r.tauA) + "  tauD=" + Sim.psToNS(r.tauD) + "  tauP=" + Sim.psToNS(r.tauP) + "} ns  ");
		System.out.print("alpha=" + alpha + "  beta=" + beta + " => peak=" + spk.peak);
		if (isSpk)
			System.out.println(" v=" + Sim.vChars.charAt(spk.charge));
		else
			System.out.println(" (too small)");
	}

	/**
	 * Initialize pre-initialized thevenin structs.
	 */
	private void initThevs()
	{
		inputThev = new Sim.Thev[Sim.N_POTS];
		for(int i=0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy