/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="simulation">Base simulation</param> public void Temperature(BaseSimulation simulation) { simulation.ThrowIfNull(nameof(simulation)); var xcbv = 0.0; // loop through all the instances if (!BaseParameters.Temperature.Given) { BaseParameters.Temperature.RawValue = simulation.RealState.Temperature; } Vt = Constants.KOverQ * BaseParameters.Temperature; Vte = ModelParameters.EmissionCoefficient * Vt; // this part gets really ugly - I won't even try to explain these equations var fact2 = BaseParameters.Temperature / Constants.ReferenceTemperature; var egfet = 1.16 - 7.02e-4 * BaseParameters.Temperature * BaseParameters.Temperature / (BaseParameters.Temperature + 1108); var arg = -egfet / (2 * Constants.Boltzmann * BaseParameters.Temperature) + 1.1150877 / (Constants.Boltzmann * (Constants.ReferenceTemperature + Constants.ReferenceTemperature)); var pbfact = -2 * Vt * (1.5 * Math.Log(fact2) + Constants.Charge * arg); var egfet1 = 1.16 - 7.02e-4 * ModelParameters.NominalTemperature * ModelParameters.NominalTemperature / (ModelParameters.NominalTemperature + 1108); var arg1 = -egfet1 / (Constants.Boltzmann * 2 * ModelParameters.NominalTemperature) + 1.1150877 / (2 * Constants.Boltzmann * Constants.ReferenceTemperature); var fact1 = ModelParameters.NominalTemperature / Constants.ReferenceTemperature; var pbfact1 = -2 * ModelTemperature.VtNominal * (1.5 * Math.Log(fact1) + Constants.Charge * arg1); var pbo = (ModelParameters.JunctionPotential - pbfact1) / fact1; var gmaold = (ModelParameters.JunctionPotential - pbo) / pbo; TempJunctionCap = ModelParameters.JunctionCap / (1 + ModelParameters.GradingCoefficient * (400e-6 * (ModelParameters.NominalTemperature - Constants.ReferenceTemperature) - gmaold)); TempJunctionPot = pbfact + fact2 * pbo; var gmanew = (TempJunctionPot - pbo) / pbo; TempJunctionCap *= 1 + ModelParameters.GradingCoefficient * (400e-6 * (BaseParameters.Temperature - Constants.ReferenceTemperature) - gmanew); TempSaturationCurrent = ModelParameters.SaturationCurrent * Math.Exp((BaseParameters.Temperature / ModelParameters.NominalTemperature - 1) * ModelParameters.ActivationEnergy / (ModelParameters.EmissionCoefficient * Vt) + ModelParameters.SaturationCurrentExp / ModelParameters.EmissionCoefficient * Math.Log(BaseParameters.Temperature / ModelParameters.NominalTemperature)); // the defintion of f1, just recompute after temperature adjusting all the variables used in it TempFactor1 = TempJunctionPot * (1 - Math.Exp((1 - ModelParameters.GradingCoefficient) * ModelTemperature.Xfc)) / (1 - ModelParameters.GradingCoefficient); // same for Depletion Capacitance TempDepletionCap = ModelParameters.DepletionCapCoefficient * TempJunctionPot; // and Vcrit var vte = ModelParameters.EmissionCoefficient * Vt; TempVCritical = vte * Math.Log(vte / (Constants.Root2 * TempSaturationCurrent)); // and now to copute the breakdown voltage, again, using temperature adjusted basic parameters if (ModelParameters.BreakdownVoltage.Given) { double cbv = ModelParameters.BreakdownCurrent; double xbv; if (cbv < TempSaturationCurrent * ModelParameters.BreakdownVoltage / Vt) { cbv = TempSaturationCurrent * ModelParameters.BreakdownVoltage / Vt; CircuitWarning.Warning(this, "{0}: breakdown current increased to {1:g} to resolve incompatability with specified saturation current".FormatString(Name, cbv)); xbv = ModelParameters.BreakdownVoltage; } else { var tol = BaseConfiguration.RelativeTolerance * cbv; xbv = ModelParameters.BreakdownVoltage - Vt * Math.Log(1 + cbv / TempSaturationCurrent); int iter; for (iter = 0; iter < 25; iter++) { xbv = ModelParameters.BreakdownVoltage - Vt * Math.Log(cbv / TempSaturationCurrent + 1 - xbv / Vt); xcbv = TempSaturationCurrent * (Math.Exp((ModelParameters.BreakdownVoltage - xbv) / Vt) - 1 + xbv / Vt); if (Math.Abs(xcbv - cbv) <= tol) { break; } } if (iter >= 25) { CircuitWarning.Warning(this, "{0}: unable to match forward and reverse diode regions: bv = {1:g}, ibv = {2:g}".FormatString(Name, xbv, xcbv)); } } TempBreakdownVoltage = xbv; } }
/// <summary> /// Loads the Y-matrix and Rhs-vector. /// </summary> /// <param name="simulation">The base simulation.</param> /// <exception cref="NotImplementedException"></exception> public void Load(BaseSimulation simulation) { simulation.ThrowIfNull(nameof(simulation)); bool currentState; var state = simulation.RealState; // decide the state of the switch if (state.Init == InitializationModes.Fix || state.Init == InitializationModes.Junction) { if (BaseParameters.ZeroState) { // Switch specified "on" CurrentState = true; currentState = true; } else { // Switch specified "off" CurrentState = false; currentState = false; } } else { // Get the previous state var ctrl = Method.GetValue(state); if (UseOldState) { // Calculate the current state if (ctrl > ModelParameters.Threshold + ModelParameters.Hysteresis) { currentState = true; } else if (ctrl < ModelParameters.Threshold - ModelParameters.Hysteresis) { currentState = false; } else { currentState = PreviousState; } CurrentState = currentState; UseOldState = false; } else { PreviousState = CurrentState; // Calculate the current state if (ctrl > ModelParameters.Threshold + ModelParameters.Hysteresis) { CurrentState = true; currentState = true; } else if (ctrl < ModelParameters.Threshold - ModelParameters.Hysteresis) { CurrentState = false; currentState = false; } else { currentState = PreviousState; } // Ensure one more iteration if (currentState != PreviousState) { state.IsConvergent = false; } } // Store the current state CurrentState = currentState; // If the state changed, ensure one more iteration if (currentState != PreviousState) { state.IsConvergent = false; } } // Get the current conduction var gNow = currentState ? ModelParameters.OnConductance : ModelParameters.OffConductance; Conductance = gNow; // Load the Y-matrix PosPosPtr.Value += gNow; PosNegPtr.Value -= gNow; NegPosPtr.Value -= gNow; NegNegPtr.Value += gNow; }
/// <summary> /// Loads the Y-matrix and Rhs-vector. /// </summary> /// <param name="simulation">The base simulation.</param> public abstract void Load(BaseSimulation simulation);
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="simulation">Base simulation</param> public override void Temperature(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } if (!_mbp.NominalTemperature.Given) { _mbp.NominalTemperature.RawValue = simulation.RealState.NominalTemperature; } Factor1 = _mbp.NominalTemperature / Circuit.ReferenceTemperature; if (!_mbp.LeakBeCurrent.Given) { if (_mbp.C2.Given) { _mbp.LeakBeCurrent.RawValue = _mbp.C2 * _mbp.SatCur; } else { _mbp.LeakBeCurrent.RawValue = 0; } } if (!_mbp.LeakBcCurrent.Given) { if (_mbp.C4.Given) { _mbp.LeakBcCurrent.RawValue = _mbp.C4 * _mbp.SatCur; } else { _mbp.LeakBcCurrent.RawValue = 0; } } if (!_mbp.MinimumBaseResistance.Given) { _mbp.MinimumBaseResistance.RawValue = _mbp.BaseResist; } /* * COMPATABILITY WARNING! * special note: for backward compatability to much older models, spice 2G * implemented a special case which checked if B-E leakage saturation * current was >1, then it was instead a the B-E leakage saturation current * divided by IS, and multiplied it by IS at this point. This was not * handled correctly in the 2G code, and there is some question on its * reasonability, since it is also undocumented, so it has been left out * here. It could easily be added with 1 line. (The same applies to the B-C * leakage saturation current). TQ 6/29/84 */ if (_mbp.EarlyVoltageForward.Given && !_mbp.EarlyVoltageForward.Value.Equals(0.0)) { InverseEarlyVoltForward = 1 / _mbp.EarlyVoltageForward; } else { InverseEarlyVoltForward = 0; } if (_mbp.RollOffForward.Given && !_mbp.RollOffForward.Value.Equals(0.0)) { InverseRollOffForward = 1 / _mbp.RollOffForward; } else { InverseRollOffForward = 0; } if (_mbp.EarlyVoltageReverse.Given && !_mbp.EarlyVoltageReverse.Value.Equals(0.0)) { InverseEarlyVoltReverse = 1 / _mbp.EarlyVoltageReverse; } else { InverseEarlyVoltReverse = 0; } if (_mbp.RollOffReverse.Given && !_mbp.RollOffReverse.Value.Equals(0.0)) { InverseRollOffReverse = 1 / _mbp.RollOffReverse; } else { InverseRollOffReverse = 0; } if (_mbp.CollectorResistance.Given && !_mbp.CollectorResistance.Value.Equals(0.0)) { CollectorConduct = 1 / _mbp.CollectorResistance; } else { CollectorConduct = 0; } if (_mbp.EmitterResistance.Given && !_mbp.EmitterResistance.Value.Equals(0.0)) { EmitterConduct = 1 / _mbp.EmitterResistance; } else { EmitterConduct = 0; } if (_mbp.TransitTimeForwardVoltageBc.Given && !_mbp.TransitTimeForwardVoltageBc.Value.Equals(0.0)) { TransitTimeVoltageBcFactor = 1 / (_mbp.TransitTimeForwardVoltageBc * 1.44); } else { TransitTimeVoltageBcFactor = 0; } ExcessPhaseFactor = _mbp.ExcessPhase / (180.0 / Math.PI) * _mbp.TransitTimeForward; if (_mbp.DepletionCapCoefficient.Given) { if (_mbp.DepletionCapCoefficient > 0.9999) { _mbp.DepletionCapCoefficient.RawValue = 0.9999; throw new CircuitException("BJT model {0}, parameter fc limited to 0.9999".FormatString(Name)); } } else { _mbp.DepletionCapCoefficient.RawValue = 0.5; } Xfc = Math.Log(1 - _mbp.DepletionCapCoefficient); F2 = Math.Exp((1 + _mbp.JunctionExpBe) * Xfc); F3 = 1 - _mbp.DepletionCapCoefficient * (1 + _mbp.JunctionExpBe); F6 = Math.Exp((1 + _mbp.JunctionExpBc) * Xfc); F7 = 1 - _mbp.DepletionCapCoefficient * (1 + _mbp.JunctionExpBc); }
public void Load(BaseSimulation simulation) { }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; double vd; double cd, gd; /* * this routine loads diodes for dc and transient analyses. */ var csat = _temp.TempSaturationCurrent * _bp.Area; var gspr = _modeltemp.Conductance * _bp.Area; var vt = Circuit.KOverQ * _bp.Temperature; var vte = _mbp.EmissionCoefficient * vt; // Initialization var check = false; if (state.Init == InitializationModes.Junction) { vd = _bp.Off ? 0.0 : _temp.TempVCritical; } else if (state.Init == InitializationModes.Fix && _bp.Off) { vd = 0.0; } else { // Get voltage over the diode (without series resistance) vd = state.Solution[PosPrimeNode] - state.Solution[_negNode]; // limit new junction voltage if (_mbp.BreakdownVoltage.Given && vd < Math.Min(0, -_temp.TempBreakdownVoltage + 10 * vte)) { var vdtemp = -(vd + _temp.TempBreakdownVoltage); vdtemp = Semiconductor.LimitJunction(vdtemp, -(InternalVoltage + _temp.TempBreakdownVoltage), vte, _temp.TempVCritical, ref check); vd = -(vdtemp + _temp.TempBreakdownVoltage); } else { vd = Semiconductor.LimitJunction(vd, InternalVoltage, vte, _temp.TempVCritical, ref check); } } // compute dc current and derivatives if (vd >= -3 * vte) { // Forward bias var evd = Math.Exp(vd / vte); cd = csat * (evd - 1) + _baseConfig.Gmin * vd; gd = csat * evd / vte + _baseConfig.Gmin; } else if (!_mbp.BreakdownVoltage.Given || vd >= -_temp.TempBreakdownVoltage) { // Reverse bias var arg = 3 * vte / (vd * Math.E); arg = arg * arg * arg; cd = -csat * (1 + arg) + _baseConfig.Gmin * vd; gd = csat * 3 * arg / vd + _baseConfig.Gmin; } else { // Reverse breakdown var evrev = Math.Exp(-(_temp.TempBreakdownVoltage + vd) / vte); cd = -csat * evrev + _baseConfig.Gmin * vd; gd = csat * evrev / vte + _baseConfig.Gmin; } // Check convergence if (state.Init != InitializationModes.Fix || !_bp.Off) { if (check) { state.IsConvergent = false; } } // Store for next time InternalVoltage = vd; Current = cd; Conduct = gd; // Load Rhs vector var cdeq = cd - gd * vd; NegPtr.Value += cdeq; PosPrimePtr.Value -= cdeq; // Load Y-matrix PosPosPtr.Value += gspr; NegNegPtr.Value += gd; PosPrimePosPrimePtr.Value += gd + gspr; PosPosPrimePtr.Value -= gspr; PosPrimePosPtr.Value -= gspr; NegPosPrimePtr.Value -= gd; PosPrimeNegPtr.Value -= gd; }
protected void SetTempVariable(ICircuitContext context, double? operatingTemperatureInKelvins, BaseSimulation simulation) { double temp = 0; if (operatingTemperatureInKelvins.HasValue) { temp = operatingTemperatureInKelvins.Value - Constants.CelsiusKelvin; } else { temp = Constants.ReferenceTemperature - Constants.CelsiusKelvin; } simulation.BeforeTemperature += (object sender, LoadStateEventArgs e) => { context.Evaluator.SetParameter(simulation, "TEMP", temp); }; }
/// <summary> /// Perform temperature-dependent calculations. /// </summary> /// <param name="simulation">The base simulation.</param> public abstract void Temperature(BaseSimulation simulation);
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; double drainSatCur, sourceSatCur, vgs, vds, vbs, vbd, vgd; double von; double vdsat, cdrain, cdreq; int xnrm, xrev; var vt = Circuit.KOverQ * _bp.Temperature; var check = 1; /* DETAILPROF */ /* first, we compute a few useful values - these could be * pre - computed, but for historical reasons are still done * here. They may be moved at the expense of instance size */ var effectiveLength = _bp.Length - 2 * _mbp.LateralDiffusion; if (_temp.TempSaturationCurrentDensity.Equals(0.0) || _bp.DrainArea.Value.Equals(0.0) || _bp.SourceArea.Value.Equals(0.0)) { drainSatCur = _temp.TempSaturationCurrent; sourceSatCur = _temp.TempSaturationCurrent; } else { drainSatCur = _temp.TempSaturationCurrentDensity * _bp.DrainArea; sourceSatCur = _temp.TempSaturationCurrentDensity * _bp.SourceArea; } var beta = _temp.TempTransconductance * _bp.Width / effectiveLength; var oxideCap = _mbp.OxideCapFactor * effectiveLength * _bp.Width; /* DETAILPROF */ /* * ok - now to do the start - up operations * * we must get values for vbs, vds, and vgs from somewhere * so we either predict them or recover them from last iteration * These are the two most common cases - either a prediction * step or the general iteration step and they * share some code, so we put them first - others later on */ if (state.Init == InitializationModes.Float || (simulation is TimeSimulation tsim && tsim.Method.BaseTime.Equals(0.0)) || state.Init == InitializationModes.Fix && !_bp.Off) { // General iteration vbs = _mbp.MosfetType * (state.Solution[_bulkNode] - state.Solution[SourceNodePrime]); vgs = _mbp.MosfetType * (state.Solution[_gateNode] - state.Solution[SourceNodePrime]); vds = _mbp.MosfetType * (state.Solution[DrainNodePrime] - state.Solution[SourceNodePrime]); /* now some common crunching for some more useful quantities */ /* DETAILPROF */ vbd = vbs - vds; vgd = vgs - vds; var vgdo = VoltageGs - VoltageDs; von = _mbp.MosfetType * Von; /* * limiting * we want to keep device voltages from changing * so fast that the exponentials churn out overflows * and similar rudeness */ // NOTE: Spice 3f5 does not write out Vgs during DC analysis, so DEVfetlim may give different results in Spice 3f5 if (VoltageDs >= 0) { vgs = Transistor.LimitFet(vgs, VoltageGs, von); vds = vgs - vgd; vds = Transistor.LimitVoltageDs(vds, VoltageDs); } else { vgd = Transistor.LimitFet(vgd, vgdo, von); vds = vgs - vgd; vds = -Transistor.LimitVoltageDs(-vds, -VoltageDs); vgs = vgd + vds; } if (vds >= 0) { vbs = Transistor.LimitJunction(vbs, VoltageBs, vt, _temp.SourceVCritical, out check); } else { vbd = Transistor.LimitJunction(vbd, VoltageBd, vt, _temp.DrainVCritical, out check); vbs = vbd + vds; } /* NODELIMITING */ }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="simulation">Base simulation</param> public void Temperature(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } // Perform model defaulting if (!ModelParameters.NominalTemperature.Given) { ModelParameters.NominalTemperature.RawValue = simulation.RealState.NominalTemperature; } Factor1 = ModelParameters.NominalTemperature / Circuit.ReferenceTemperature; VtNominal = ModelParameters.NominalTemperature * Circuit.KOverQ; var kt1 = Circuit.Boltzmann * ModelParameters.NominalTemperature; EgFet1 = 1.16 - 7.02e-4 * ModelParameters.NominalTemperature * ModelParameters.NominalTemperature / (ModelParameters.NominalTemperature + 1108); var arg1 = -EgFet1 / (kt1 + kt1) + 1.1150877 / (Circuit.Boltzmann * (Circuit.ReferenceTemperature + Circuit.ReferenceTemperature)); PbFactor1 = -2 * VtNominal * (1.5 * Math.Log(Factor1) + Circuit.Charge * arg1); if (ModelParameters.OxideThickness.Given && ModelParameters.OxideThickness > 0.0) { if (ModelParameters.SubstrateDoping.Given) { if (ModelParameters.SubstrateDoping * 1e6 > 1.45e16) { if (!ModelParameters.Phi.Given) { ModelParameters.Phi.RawValue = 2 * VtNominal * Math.Log(ModelParameters.SubstrateDoping * 1e6 / 1.45e16); ModelParameters.Phi.RawValue = Math.Max(.1, ModelParameters.Phi); } var fermis = ModelParameters.MosfetType * .5 * ModelParameters.Phi; var wkfng = 3.2; if (!ModelParameters.GateType.Given) { ModelParameters.GateType.RawValue = 1; } if (!ModelParameters.GateType.RawValue.Equals(0)) { var fermig = ModelParameters.MosfetType * ModelParameters.GateType * .5 * EgFet1; wkfng = 3.25 + .5 * EgFet1 - fermig; } var wkfngs = wkfng - (3.25 + .5 * EgFet1 + fermis); if (!ModelParameters.Gamma.Given) { ModelParameters.Gamma.RawValue = Math.Sqrt(2 * 11.70 * 8.854214871e-12 * Circuit.Charge * ModelParameters.SubstrateDoping * 1e6) / ModelParameters.OxideCapFactor; } if (!ModelParameters.Vt0.Given) { if (!ModelParameters.SurfaceStateDensity.Given) { ModelParameters.SurfaceStateDensity.RawValue = 0; } var vfb = wkfngs - ModelParameters.SurfaceStateDensity * 1e4 * Circuit.Charge / ModelParameters.OxideCapFactor; ModelParameters.Vt0.RawValue = vfb + ModelParameters.MosfetType * (ModelParameters.Gamma * Math.Sqrt(ModelParameters.Phi) + ModelParameters.Phi); } } else { ModelParameters.SubstrateDoping.RawValue = 0; throw new CircuitException("{0}: Nsub < Ni".FormatString(Name)); } } } }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { PosControlBranchPtr.Value += _bp.Coefficient.Value; NegControlBranchPtr.Value -= _bp.Coefficient.Value; }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; double cd, gd; // Get the current voltages Initialize(simulation, out double vd, out bool check); /* * this routine loads diodes for dc and transient analyses. */ var csat = TempSaturationCurrent * BaseParameters.Area; var gspr = ModelTemperature.Conductance * BaseParameters.Area; // compute dc current and derivatives if (vd >= -3 * Vte) { // Forward bias var evd = Math.Exp(vd / Vte); cd = csat * (evd - 1) + BaseConfiguration.Gmin * vd; gd = csat * evd / Vte + BaseConfiguration.Gmin; } else if (!ModelParameters.BreakdownVoltage.Given || vd >= -TempBreakdownVoltage) { // Reverse bias var arg = 3 * Vte / (vd * Math.E); arg = arg * arg * arg; cd = -csat * (1 + arg) + BaseConfiguration.Gmin * vd; gd = csat * 3 * arg / vd + BaseConfiguration.Gmin; } else { // Reverse breakdown var evrev = Math.Exp(-(TempBreakdownVoltage + vd) / Vte); cd = -csat * evrev + BaseConfiguration.Gmin * vd; gd = csat * evrev / Vte + BaseConfiguration.Gmin; } // Check convergence if (state.Init != InitializationModes.Fix || !BaseParameters.Off) { if (check) { state.IsConvergent = false; } } // Store for next time Voltage = vd; Current = cd; Conductance = gd; // Load Rhs vector var cdeq = cd - gd * vd; NegPtr.Value += cdeq; PosPrimePtr.Value -= cdeq; // Load Y-matrix PosPosPtr.Value += gspr; NegNegPtr.Value += gd; PosPrimePosPrimePtr.Value += gd + gspr; PosPosPrimePtr.Value -= gspr; PosPrimePosPtr.Value -= gspr; NegPosPrimePtr.Value -= gd; PosPrimeNegPtr.Value -= gd; }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="simulation">Base simulation</param> public override void Temperature(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } if (!_mbp.NominalTemperature.Given) { _mbp.NominalTemperature.RawValue = simulation.RealState.NominalTemperature; } Fact1 = _mbp.NominalTemperature / Circuit.ReferenceTemperature; VtNominal = _mbp.NominalTemperature * Circuit.KOverQ; var kt1 = Circuit.Boltzmann * _mbp.NominalTemperature; EgFet1 = 1.16 - 7.02e-4 * _mbp.NominalTemperature * _mbp.NominalTemperature / (_mbp.NominalTemperature + 1108); var arg1 = -EgFet1 / (kt1 + kt1) + 1.1150877 / (Circuit.Boltzmann * (Circuit.ReferenceTemperature + Circuit.ReferenceTemperature)); PbFactor1 = -2 * VtNominal * (1.5 * Math.Log(Fact1) + Circuit.Charge * arg1); OxideCapFactor = 3.9 * 8.854214871e-12 / _mbp.OxideThickness; if (!_mbp.SurfaceMobility.Given) { _mbp.SurfaceMobility.RawValue = 600; } if (!_mbp.Transconductance.Given) { _mbp.Transconductance.RawValue = _mbp.SurfaceMobility * OxideCapFactor * 1e-4; } if (_mbp.SubstrateDoping.Given) { if (_mbp.SubstrateDoping * 1e6 /* (cm**3 / m**3) */ > 1.45e16) { if (!_mbp.Phi.Given) { _mbp.Phi.RawValue = 2 * VtNominal * Math.Log(_mbp.SubstrateDoping * 1e6 /* (cm * * 3 / m * * 3) */ / 1.45e16); _mbp.Phi.RawValue = Math.Max(.1, _mbp.Phi); } var fermis = _mbp.MosfetType * .5 * _mbp.Phi; var wkfng = 3.2; if (!_mbp.GateType.Given) { _mbp.GateType.RawValue = 1; } if (!_mbp.GateType.RawValue.Equals(0.0)) { var fermig = _mbp.MosfetType * _mbp.GateType * .5 * EgFet1; wkfng = 3.25 + .5 * EgFet1 - fermig; } var wkfngs = wkfng - (3.25 + .5 * EgFet1 + fermis); if (!_mbp.Gamma.Given) { _mbp.Gamma.RawValue = Math.Sqrt(2 * Transistor.EpsilonSilicon * Circuit.Charge * _mbp.SubstrateDoping * 1e6 /* (cm**3 / m**3) */) / OxideCapFactor; } if (!_mbp.Vt0.Given) { if (!_mbp.SurfaceStateDensity.Given) { _mbp.SurfaceStateDensity.RawValue = 0; } var vfb = wkfngs - _mbp.SurfaceStateDensity * 1e4 * Circuit.Charge / OxideCapFactor; _mbp.Vt0.RawValue = vfb + _mbp.MosfetType * (_mbp.Gamma * Math.Sqrt(_mbp.Phi) + _mbp.Phi); } Alpha = (Transistor.EpsilonSilicon + Transistor.EpsilonSilicon) / (Circuit.Charge * _mbp.SubstrateDoping * 1e6 /* (cm**3 / m**3) */); CoefficientDepletionLayerWidth = Math.Sqrt(Alpha); } else { _mbp.SubstrateDoping.RawValue = 0; throw new CircuitException("{0}: Nsub < Ni".FormatString(Name)); } } /* now model parameter preprocessing */ _mbp.NarrowFactor = _mbp.Delta * 0.5 * Math.PI * Transistor.EpsilonSilicon / OxideCapFactor; }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; double vbe; double vbc; double gben; double cben; double gbcn; double cbcn; var vt = _bp.Temperature * Circuit.KOverQ; // DC model parameters var csat = _temp.TempSaturationCurrent * _bp.Area; var rbpr = _mbp.MinimumBaseResistance / _bp.Area; var rbpi = _mbp.BaseResist / _bp.Area - rbpr; var gcpr = _modeltemp.CollectorConduct * _bp.Area; var gepr = _modeltemp.EmitterConduct * _bp.Area; var oik = _modeltemp.InverseRollOffForward / _bp.Area; var c2 = _temp.TempBeLeakageCurrent * _bp.Area; var vte = _mbp.LeakBeEmissionCoefficient * vt; var oikr = _modeltemp.InverseRollOffReverse / _bp.Area; var c4 = _temp.TempBcLeakageCurrent * _bp.Area; var vtc = _mbp.LeakBcEmissionCoefficient * vt; var xjrb = _mbp.BaseCurrentHalfResist * _bp.Area; // Initialization if (state.Init == InitializationModes.Junction && (simulation is TimeSimulation) && state.UseDc && state.UseIc) { vbe = _mbp.BipolarType * _bp.InitialVoltageBe; var vce = _mbp.BipolarType * _bp.InitialVoltageCe; vbc = vbe - vce; } else if (state.Init == InitializationModes.Junction && !_bp.Off) { vbe = _temp.TempVCritical; vbc = 0; } else if (state.Init == InitializationModes.Junction || state.Init == InitializationModes.Fix && _bp.Off) { vbe = 0; vbc = 0; } else { // Compute new nonlinear branch voltages vbe = _mbp.BipolarType * (state.Solution[BasePrimeNode] - state.Solution[EmitterPrimeNode]); vbc = _mbp.BipolarType * (state.Solution[BasePrimeNode] - state.Solution[CollectorPrimeNode]); // Limit nonlinear branch voltages var limited = false; vbe = Semiconductor.LimitJunction(vbe, VoltageBe, vt, _temp.TempVCritical, ref limited); vbc = Semiconductor.LimitJunction(vbc, VoltageBc, vt, _temp.TempVCritical, ref limited); if (limited) { state.IsConvergent = false; } } // Determine dc current and derivitives var vtn = vt * _mbp.EmissionCoefficientForward; if (vbe > -5 * vtn) { var evbe = Math.Exp(vbe / vtn); CurrentBe = csat * (evbe - 1) + _baseConfig.Gmin * vbe; CondBe = csat * evbe / vtn + _baseConfig.Gmin; if (c2.Equals(0)) // Avoid Exp() { cben = 0; gben = 0; } else { var evben = Math.Exp(vbe / vte); cben = c2 * (evben - 1); gben = c2 * evben / vte; } } else { CondBe = -csat / vbe + _baseConfig.Gmin; CurrentBe = CondBe * vbe; gben = -c2 / vbe; cben = gben * vbe; } vtn = vt * _mbp.EmissionCoefficientReverse; if (vbc > -5 * vtn) { var evbc = Math.Exp(vbc / vtn); CurrentBc = csat * (evbc - 1) + _baseConfig.Gmin * vbc; CondBc = csat * evbc / vtn + _baseConfig.Gmin; if (c4.Equals(0)) // Avoid Exp() { cbcn = 0; gbcn = 0; } else { var evbcn = Math.Exp(vbc / vtc); cbcn = c4 * (evbcn - 1); gbcn = c4 * evbcn / vtc; } } else { CondBc = -csat / vbc + _baseConfig.Gmin; CurrentBc = CondBc * vbc; gbcn = -c4 / vbc; cbcn = gbcn * vbc; } // Determine base charge terms var q1 = 1 / (1 - _modeltemp.InverseEarlyVoltForward * vbc - _modeltemp.InverseEarlyVoltReverse * vbe); if (oik.Equals(0) && oikr.Equals(0)) // Avoid computations { BaseCharge = q1; Dqbdve = q1 * BaseCharge * _modeltemp.InverseEarlyVoltReverse; Dqbdvc = q1 * BaseCharge * _modeltemp.InverseEarlyVoltForward; } else { var q2 = oik * CurrentBe + oikr * CurrentBc; var arg = Math.Max(0, 1 + 4 * q2); double sqarg = 1; if (!arg.Equals(0)) // Avoid Sqrt() { sqarg = Math.Sqrt(arg); } BaseCharge = q1 * (1 + sqarg) / 2; Dqbdve = q1 * (BaseCharge * _modeltemp.InverseEarlyVoltReverse + oik * CondBe / sqarg); Dqbdvc = q1 * (BaseCharge * _modeltemp.InverseEarlyVoltForward + oikr * CondBc / sqarg); } // Excess phase calculation var ep = new ExcessPhaseEventArgs { CollectorCurrent = 0.0, ExcessPhaseCurrent = CurrentBe, ExcessPhaseConduct = CondBe, BaseCharge = BaseCharge }; ExcessPhaseCalculation?.Invoke(this, ep); var cc = ep.CollectorCurrent; var cex = ep.ExcessPhaseCurrent; var gex = ep.ExcessPhaseConduct; // Determine dc incremental conductances cc = cc + (cex - CurrentBc) / BaseCharge - CurrentBc / _temp.TempBetaReverse - cbcn; var cb = CurrentBe / _temp.TempBetaForward + cben + CurrentBc / _temp.TempBetaReverse + cbcn; var gx = rbpr + rbpi / BaseCharge; if (!xjrb.Equals(0)) // Avoid calculations { var arg1 = Math.Max(cb / xjrb, 1e-9); var arg2 = (-1 + Math.Sqrt(1 + 14.59025 * arg1)) / 2.4317 / Math.Sqrt(arg1); arg1 = Math.Tan(arg2); gx = rbpr + 3 * rbpi * (arg1 - arg2) / arg2 / arg1 / arg1; } if (!gx.Equals(0)) // Do not divide by 0 { gx = 1 / gx; } var gpi = CondBe / _temp.TempBetaForward + gben; var gmu = CondBc / _temp.TempBetaReverse + gbcn; var go = (CondBc + (cex - CurrentBc) * Dqbdvc / BaseCharge) / BaseCharge; var gm = (gex - (cex - CurrentBc) * Dqbdve / BaseCharge) / BaseCharge - go; VoltageBe = vbe; VoltageBc = vbc; CollectorCurrent = cc; BaseCurrent = cb; ConductancePi = gpi; ConductanceMu = gmu; Transconductance = gm; OutputConductance = go; ConductanceX = gx; // Load current excitation vector var ceqbe = _mbp.BipolarType * (cc + cb - vbe * (gm + go + gpi) + vbc * go); var ceqbc = _mbp.BipolarType * (-cc + vbe * (gm + go) - vbc * (gmu + go)); CollectorPrimePtr.Value += ceqbc; BasePrimePtr.Value += -ceqbe - ceqbc; EmitterPrimePtr.Value += ceqbe; // Load y matrix CollectorCollectorPtr.Value += gcpr; BaseBasePtr.Value += gx; EmitterEmitterPtr.Value += gepr; CollectorPrimeCollectorPrimePtr.Value += gmu + go + gcpr; BasePrimeBasePrimePtr.Value += gx + gpi + gmu; EmitterPrimeEmitterPrimePtr.Value += gpi + gepr + gm + go; CollectorCollectorPrimePtr.Value += -gcpr; BaseBasePrimePtr.Value += -gx; EmitterEmitterPrimePtr.Value += -gepr; CollectorPrimeCollectorPtr.Value += -gcpr; CollectorPrimeBasePrimePtr.Value += -gmu + gm; CollectorPrimeEmitterPrimePtr.Value += -gm - go; BasePrimeBasePtr.Value += -gx; BasePrimeCollectorPrimePtr.Value += -gmu; BasePrimeEmitterPrimePtr.Value += -gpi; EmitterPrimeEmitterPtr.Value += -gepr; EmitterPrimeCollectorPrimePtr.Value += -go; EmitterPrimeBasePrimePtr.Value += -gpi - gm; }
/// <summary> /// Loads the Y-matrix and Rhs-vector. /// </summary> /// <param name="simulation">The base simulation.</param> public void Load(BaseSimulation simulation) { simulation.ThrowIfNull(nameof(simulation)); var state = simulation.RealState; // DC model parameters var beta = ModelParameters.Beta * BaseParameters.Area; var gdpr = ModelParameters.DrainConductance * BaseParameters.Area; var gspr = ModelParameters.SourceConductance * BaseParameters.Area; var csat = TempSaturationCurrent * BaseParameters.Area; double ggs, cg; double ggd, cgd; double cdrain, gm, gds, betap, bfac; // Get the current voltages Initialize(simulation, out double vgs, out double vgd, out bool check); var vds = vgs - vgd; // Determine dc current and derivatives if (vgs <= -5 * BaseParameters.Temperature * Constants.KOverQ) { ggs = -csat / vgs + BaseConfiguration.Gmin; cg = ggs * vgs; } else { var evgs = Math.Exp(vgs / (BaseParameters.Temperature * Constants.KOverQ)); ggs = csat * evgs / (BaseParameters.Temperature * Constants.KOverQ) + BaseConfiguration.Gmin; cg = csat * (evgs - 1) + BaseConfiguration.Gmin * vgs; } if (vgd <= -5 * (BaseParameters.Temperature * Constants.KOverQ)) { ggd = -csat / vgd + BaseConfiguration.Gmin; cgd = ggd * vgd; } else { var evgd = Math.Exp(vgd / (BaseParameters.Temperature * Constants.KOverQ)); ggd = csat * evgd / (BaseParameters.Temperature * Constants.KOverQ) + BaseConfiguration.Gmin; cgd = csat * (evgd - 1) + BaseConfiguration.Gmin * vgd; } cg += cgd; // Modification for Sydney University JFET model var vto = ModelParameters.Threshold; if (vds >= 0) { var vgst = vgs - vto; // Compute drain current and derivatives for normal mode if (vgst <= 0) { // Normal mode, cutoff region cdrain = 0; gm = 0; gds = 0; } else { betap = beta * (1 + ModelParameters.LModulation * vds); bfac = ModelTemperature.BFactor; if (vgst >= vds) { // Normal mode, linear region var apart = 2 * ModelParameters.B + 3 * bfac * (vgst - vds); var cpart = vds * (vds * (bfac * vds - ModelParameters.B) + vgst * apart); cdrain = betap * cpart; gm = betap * vds * (apart + 3 * bfac * vgst); gds = betap * (vgst - vds) * apart + beta * ModelParameters.LModulation * cpart; } else { bfac = vgst * bfac; gm = betap * vgst * (2 * ModelParameters.B + 3 * bfac); // Normal mode, saturation region var cpart = vgst * vgst * (ModelParameters.B + bfac); cdrain = betap * cpart; gds = ModelParameters.LModulation * beta * cpart; } } } else { var vgdt = vgd - vto; // Compute drain current and derivatives for inverse mode if (vgdt <= 0) { // Inverse mode, cutoff region cdrain = 0; gm = 0; gds = 0; } else { betap = beta * (1 - ModelParameters.LModulation * vds); bfac = ModelTemperature.BFactor; if (vgdt + vds >= 0) { // Inverse mode, linear region var apart = 2 * ModelParameters.B + 3 * bfac * (vgdt + vds); var cpart = vds * (-vds * (-bfac * vds - ModelParameters.B) + vgdt * apart); cdrain = betap * cpart; gm = betap * vds * (apart + 3 * bfac * vgdt); gds = betap * (vgdt + vds) * apart - beta * ModelParameters.LModulation * cpart - gm; } else { bfac = vgdt * bfac; gm = -betap * vgdt * (2 * ModelParameters.B + 3 * bfac); // Inverse mode, saturation region var cpart = vgdt * vgdt * (ModelParameters.B + bfac); cdrain = -betap * cpart; gds = ModelParameters.LModulation * beta * cpart - gm; } } } var cd = cdrain - cgd; Vgs = vgs; Vgd = vgd; Cg = cg; Cd = cd; Cgd = cgd; Gm = gm; Gds = gds; Ggs = ggs; Ggd = ggd; // Check convergence if (state.Init != InitializationModes.Fix || !state.UseIc) { if (check) { state.IsConvergent = false; } } // Load current vector var ceqgd = ModelParameters.JFETType * (cgd - ggd * vgd); var ceqgs = ModelParameters.JFETType * (cg - cgd - ggs * vgs); var cdreq = ModelParameters.JFETType * (cd + cgd - gds * vds - gm * vgs); GateNodePtr.Value += -ceqgs - ceqgd; DrainPrimeNodePtr.Value += -cdreq + ceqgd; SourcePrimeNodePtr.Value += cdreq + ceqgs; // Load Y-matrix DrainDrainPrimePtr.Value += -gdpr; GateDrainPrimePtr.Value += -ggd; GateSourcePrimePtr.Value += -ggs; SourceSourcePrimePtr.Value += -gspr; DrainPrimeDrainPtr.Value += -gdpr; DrainPrimeGatePtr.Value += gm - ggd; DrainPrimeSourcePrimePtr.Value += -gds - gm; SourcePrimeGatePtr.Value += -ggs - gm; SourcePrimeSourcePtr.Value += -gspr; SourcePrimeDrainPrimePtr.Value += -gds; DrainDrainPtr.Value += gdpr; GateGatePtr.Value += ggd + ggs; SourceSourcePtr.Value += gspr; DrainPrimeDrainPrimePtr.Value += gdpr + gds + ggd; SourcePrimeSourcePrimePtr.Value += gspr + gds + gm + ggs; }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="simulation">Base simulation</param> public void Temperature(BaseSimulation simulation) { simulation.ThrowIfNull(nameof(simulation)); // Perform model defaulting if (!ModelParameters.NominalTemperature.Given) { ModelParameters.NominalTemperature.RawValue = simulation.RealState.NominalTemperature; } Factor1 = ModelParameters.NominalTemperature / Constants.ReferenceTemperature; VtNominal = ModelParameters.NominalTemperature * Constants.KOverQ; var kt1 = Constants.Boltzmann * ModelParameters.NominalTemperature; EgFet1 = 1.16 - 7.02e-4 * ModelParameters.NominalTemperature * ModelParameters.NominalTemperature / (ModelParameters.NominalTemperature + 1108); var arg1 = -EgFet1 / (kt1 + kt1) + 1.1150877 / (Constants.Boltzmann * (Constants.ReferenceTemperature + Constants.ReferenceTemperature)); PbFactor1 = -2 * VtNominal * (1.5 * Math.Log(Factor1) + Constants.Charge * arg1); if (ModelParameters.SubstrateDoping.Given) { if (ModelParameters.SubstrateDoping * 1e6 /* (cm**3 / m**3) */ > 1.45e16) { if (!ModelParameters.Phi.Given) { ModelParameters.Phi.RawValue = 2 * VtNominal * Math.Log(ModelParameters.SubstrateDoping * 1e6 /* (cm^3/m^3) */ / 1.45e16); ModelParameters.Phi.RawValue = Math.Max(0.1, ModelParameters.Phi); } var fermis = ModelParameters.MosfetType * 0.5 * ModelParameters.Phi; var wkfng = 3.2; if (!ModelParameters.GateType.Given) { ModelParameters.GateType.RawValue = 1; } if (!ModelParameters.GateType.RawValue.Equals(0.0)) { var fermig = ModelParameters.MosfetType * ModelParameters.GateType * 0.5 * EgFet1; wkfng = 3.25 + 0.5 * EgFet1 - fermig; } var wkfngs = wkfng - (3.25 + 0.5 * EgFet1 + fermis); if (!ModelParameters.Gamma.Given) { ModelParameters.Gamma.RawValue = Math.Sqrt(2 * EpsilonSilicon * Constants.Charge * ModelParameters.SubstrateDoping * 1e6 /* (cm**3 / m**3) */) / ModelParameters.OxideCapFactor; } if (!ModelParameters.Vt0.Given) { if (!ModelParameters.SurfaceStateDensity.Given) { ModelParameters.SurfaceStateDensity.RawValue = 0; } var vfb = wkfngs - ModelParameters.SurfaceStateDensity * 1e4 * Constants.Charge / ModelParameters.OxideCapFactor; ModelParameters.Vt0.RawValue = vfb + ModelParameters.MosfetType * (ModelParameters.Gamma * Math.Sqrt(ModelParameters.Phi) + ModelParameters.Phi); } Alpha = (EpsilonSilicon + EpsilonSilicon) / (Constants.Charge * ModelParameters.SubstrateDoping * 1e6 /* (cm**3 / m**3) */); CoefficientDepletionLayerWidth = Math.Sqrt(Alpha); } else { ModelParameters.SubstrateDoping.RawValue = 0; throw new CircuitException("{0}: Nsub < Ni".FormatString(Name)); } } }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public void Load(BaseSimulation simulation) { simulation.ThrowIfNull(nameof(simulation)); double gben; double cben; double gbcn; double cbcn; // DC model parameters var csat = TempSaturationCurrent * BaseParameters.Area; var rbpr = ModelParameters.MinimumBaseResistance / BaseParameters.Area; var rbpi = ModelParameters.BaseResist / BaseParameters.Area - rbpr; var gcpr = ModelTemperature.CollectorConduct * BaseParameters.Area; var gepr = ModelTemperature.EmitterConduct * BaseParameters.Area; var oik = ModelTemperature.InverseRollOffForward / BaseParameters.Area; var c2 = TempBeLeakageCurrent * BaseParameters.Area; var vte = ModelParameters.LeakBeEmissionCoefficient * Vt; var oikr = ModelTemperature.InverseRollOffReverse / BaseParameters.Area; var c4 = TempBcLeakageCurrent * BaseParameters.Area; var vtc = ModelParameters.LeakBcEmissionCoefficient * Vt; var xjrb = ModelParameters.BaseCurrentHalfResist * BaseParameters.Area; // Get the current voltages Initialize(simulation, out var vbe, out var vbc); // Determine dc current and derivitives var vtn = Vt * ModelParameters.EmissionCoefficientForward; if (vbe > -5 * vtn) { var evbe = Math.Exp(vbe / vtn); CurrentBe = csat * (evbe - 1) + BaseConfiguration.Gmin * vbe; CondBe = csat * evbe / vtn + BaseConfiguration.Gmin; if (c2.Equals(0)) // Avoid Exp() { cben = 0; gben = 0; } else { var evben = Math.Exp(vbe / vte); cben = c2 * (evben - 1); gben = c2 * evben / vte; } } else { CondBe = -csat / vbe + BaseConfiguration.Gmin; CurrentBe = CondBe * vbe; gben = -c2 / vbe; cben = gben * vbe; } vtn = Vt * ModelParameters.EmissionCoefficientReverse; if (vbc > -5 * vtn) { var evbc = Math.Exp(vbc / vtn); CurrentBc = csat * (evbc - 1) + BaseConfiguration.Gmin * vbc; CondBc = csat * evbc / vtn + BaseConfiguration.Gmin; if (c4.Equals(0)) // Avoid Exp() { cbcn = 0; gbcn = 0; } else { var evbcn = Math.Exp(vbc / vtc); cbcn = c4 * (evbcn - 1); gbcn = c4 * evbcn / vtc; } } else { CondBc = -csat / vbc + BaseConfiguration.Gmin; CurrentBc = CondBc * vbc; gbcn = -c4 / vbc; cbcn = gbcn * vbc; } // Determine base charge terms var q1 = 1 / (1 - ModelTemperature.InverseEarlyVoltForward * vbc - ModelTemperature.InverseEarlyVoltReverse * vbe); if (oik.Equals(0) && oikr.Equals(0)) // Avoid computations { BaseCharge = q1; Dqbdve = q1 * BaseCharge * ModelTemperature.InverseEarlyVoltReverse; Dqbdvc = q1 * BaseCharge * ModelTemperature.InverseEarlyVoltForward; } else { var q2 = oik * CurrentBe + oikr * CurrentBc; var arg = Math.Max(0, 1 + 4 * q2); double sqarg = 1; if (!arg.Equals(0)) // Avoid Sqrt() { sqarg = Math.Sqrt(arg); } BaseCharge = q1 * (1 + sqarg) / 2; Dqbdve = q1 * (BaseCharge * ModelTemperature.InverseEarlyVoltReverse + oik * CondBe / sqarg); Dqbdvc = q1 * (BaseCharge * ModelTemperature.InverseEarlyVoltForward + oikr * CondBc / sqarg); } // Excess phase calculation var cc = 0.0; var cex = CurrentBe; var gex = CondBe; ExcessPhaseCalculation(ref cc, ref cex, ref gex); // Determine dc incremental conductances cc = cc + (cex - CurrentBc) / BaseCharge - CurrentBc / TempBetaReverse - cbcn; var cb = CurrentBe / TempBetaForward + cben + CurrentBc / TempBetaReverse + cbcn; var gx = rbpr + rbpi / BaseCharge; if (!xjrb.Equals(0)) // Avoid calculations { var arg1 = Math.Max(cb / xjrb, 1e-9); var arg2 = (-1 + Math.Sqrt(1 + 14.59025 * arg1)) / 2.4317 / Math.Sqrt(arg1); arg1 = Math.Tan(arg2); gx = rbpr + 3 * rbpi * (arg1 - arg2) / arg2 / arg1 / arg1; } if (!gx.Equals(0)) // Do not divide by 0 { gx = 1 / gx; } var gpi = CondBe / TempBetaForward + gben; var gmu = CondBc / TempBetaReverse + gbcn; var go = (CondBc + (cex - CurrentBc) * Dqbdvc / BaseCharge) / BaseCharge; var gm = (gex - (cex - CurrentBc) * Dqbdve / BaseCharge) / BaseCharge - go; VoltageBe = vbe; VoltageBc = vbc; CollectorCurrent = cc; BaseCurrent = cb; ConductancePi = gpi; ConductanceMu = gmu; Transconductance = gm; OutputConductance = go; ConductanceX = gx; // Load current excitation vector var ceqbe = ModelParameters.BipolarType * (cc + cb - vbe * (gm + go + gpi) + vbc * go); var ceqbc = ModelParameters.BipolarType * (-cc + vbe * (gm + go) - vbc * (gmu + go)); CollectorPrimePtr.Value += ceqbc; BasePrimePtr.Value += -ceqbe - ceqbc; EmitterPrimePtr.Value += ceqbe; // Load y matrix CollectorCollectorPtr.Value += gcpr; BaseBasePtr.Value += gx; EmitterEmitterPtr.Value += gepr; CollectorPrimeCollectorPrimePtr.Value += gmu + go + gcpr; BasePrimeBasePrimePtr.Value += gx + gpi + gmu; EmitterPrimeEmitterPrimePtr.Value += gpi + gepr + gm + go; CollectorCollectorPrimePtr.Value += -gcpr; BaseBasePrimePtr.Value += -gx; EmitterEmitterPrimePtr.Value += -gepr; CollectorPrimeCollectorPtr.Value += -gcpr; CollectorPrimeBasePrimePtr.Value += -gmu + gm; CollectorPrimeEmitterPrimePtr.Value += -gm - go; BasePrimeBasePtr.Value += -gx; BasePrimeCollectorPrimePtr.Value += -gmu; BasePrimeEmitterPrimePtr.Value += -gpi; EmitterPrimeEmitterPtr.Value += -gepr; EmitterPrimeCollectorPrimePtr.Value += -go; EmitterPrimeBasePrimePtr.Value += -gpi - gm; }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } bool currentState; var state = simulation.RealState; // decide the state of the switch if (state.Init == RealState.InitializationStates.InitFix || state.Init == RealState.InitializationStates.InitJunction) { if (_bp.ZeroState) { // Switch specified "on" CurrentState = true; currentState = true; } else { // Switch specified "off" CurrentState = false; currentState = false; } } else { // Get the previous state var previousState = UseOldState ? PreviousState : CurrentState; var iCtrl = state.Solution[ControllingBranch]; if (UseOldState) { // Calculate the current state if (iCtrl > _mbp.Threshold + _mbp.Hysteresis) { currentState = true; } else if (iCtrl < _mbp.Threshold - _mbp.Hysteresis) { currentState = false; } else { currentState = previousState; } CurrentState = currentState; UseOldState = false; } else { PreviousState = CurrentState; // Calculate the current state if (iCtrl > _mbp.Threshold + _mbp.Hysteresis) { CurrentState = true; currentState = true; } else if (iCtrl < _mbp.Threshold - _mbp.Hysteresis) { CurrentState = false; currentState = false; } else { currentState = PreviousState; } // Ensure one more iteration if (currentState != PreviousState) { state.IsConvergent = false; } } // Store the current state CurrentState = currentState; // If the state changed, ensure one more iteration if (currentState != previousState) { state.IsConvergent = false; } } // Get the current conduction var gNow = currentState ? _modelload.OnConductance : _modelload.OffConductance; Cond = gNow; // Load the Y-matrix PosPosPtr.Value += gNow; PosNegPtr.Value -= gNow; NegPosPtr.Value -= gNow; NegNegPtr.Value += gNow; }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="simulation">Base simulation</param> public override void Temperature(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } double czbd, czbdsw, czbs, czbssw; /* perform the parameter defaulting */ if (!_bp.Temperature.Given) { _bp.Temperature.RawValue = simulation.RealState.Temperature; } var vt = _bp.Temperature * Circuit.KOverQ; var ratio = _bp.Temperature / _mbp.NominalTemperature; var fact2 = _bp.Temperature / Circuit.ReferenceTemperature; var kt = _bp.Temperature * Circuit.Boltzmann; var egfet = 1.16 - 7.02e-4 * _bp.Temperature * _bp.Temperature / (_bp.Temperature + 1108); var arg = -egfet / (kt + kt) + 1.1150877 / (Circuit.Boltzmann * (Circuit.ReferenceTemperature + Circuit.ReferenceTemperature)); var pbfact = -2 * vt * (1.5 * Math.Log(fact2) + Circuit.Charge * arg); if (_mbp.DrainResistance.Given) { if (!_mbp.DrainResistance.Value.Equals(0.0)) { DrainConductance = 1 / _mbp.DrainResistance; } else { DrainConductance = 0; } } else if (_mbp.SheetResistance.Given) { if (!_mbp.SheetResistance.Value.Equals(0.0)) { DrainConductance = 1 / (_mbp.SheetResistance * _bp.DrainSquares); } else { DrainConductance = 0; } } else { DrainConductance = 0; } if (_mbp.SourceResistance.Given) { if (!_mbp.SourceResistance.Value.Equals(0.0)) { SourceConductance = 1 / _mbp.SourceResistance; } else { SourceConductance = 0; } } else if (_mbp.SheetResistance.Given) { if (!_mbp.SheetResistance.Value.Equals(0.0)) { SourceConductance = 1 / (_mbp.SheetResistance * _bp.SourceSquares); } else { SourceConductance = 0; } } else { SourceConductance = 0; } if (_bp.Length - 2 * _mbp.LateralDiffusion <= 0) { CircuitWarning.Warning(this, "{0}: effective channel length less than zero".FormatString(Name)); } var ratio4 = ratio * Math.Sqrt(ratio); TempTransconductance = _mbp.Transconductance / ratio4; TempSurfaceMobility = _mbp.SurfaceMobility / ratio4; var phio = (_mbp.Phi - _modeltemp.PbFactor1) / _modeltemp.Factor1; TempPhi = fact2 * phio + pbfact; TempVoltageBi = _mbp.Vt0 - _mbp.MosfetType * (_mbp.Gamma * Math.Sqrt(_mbp.Phi)) + .5 * (_modeltemp.EgFet1 - egfet) + _mbp.MosfetType * .5 * (TempPhi - _mbp.Phi); TempVt0 = TempVoltageBi + _mbp.MosfetType * _mbp.Gamma * Math.Sqrt(TempPhi); TempSaturationCurrent = _mbp.JunctionSatCur * Math.Exp(-egfet / vt + _modeltemp.EgFet1 / _modeltemp.VtNominal); TempSaturationCurrentDensity = _mbp.JunctionSatCurDensity * Math.Exp(-egfet / vt + _modeltemp.EgFet1 / _modeltemp.VtNominal); var pbo = (_mbp.BulkJunctionPotential - _modeltemp.PbFactor1) / _modeltemp.Factor1; var gmaold = (_mbp.BulkJunctionPotential - pbo) / pbo; var capfact = 1 / (1 + _mbp.BulkJunctionBotGradingCoefficient * (4e-4 * (_mbp.NominalTemperature - Circuit.ReferenceTemperature) - gmaold)); TempCapBd = _mbp.CapBd * capfact; TempCapBs = _mbp.CapBs * capfact; TempJunctionCap = _mbp.BulkCapFactor * capfact; capfact = 1 / (1 + _mbp.BulkJunctionSideGradingCoefficient * (4e-4 * (_mbp.NominalTemperature - Circuit.ReferenceTemperature) - gmaold)); TempJunctionCapSidewall = _mbp.SidewallCapFactor * capfact; TempBulkPotential = fact2 * pbo + pbfact; var gmanew = (TempBulkPotential - pbo) / pbo; capfact = 1 + _mbp.BulkJunctionBotGradingCoefficient * (4e-4 * (_bp.Temperature - Circuit.ReferenceTemperature) - gmanew); TempCapBd *= capfact; TempCapBs *= capfact; TempJunctionCap *= capfact; capfact = 1 + _mbp.BulkJunctionSideGradingCoefficient * (4e-4 * (_bp.Temperature - Circuit.ReferenceTemperature) - gmanew); TempJunctionCapSidewall *= capfact; TempDepletionCap = _mbp.ForwardCapDepletionCoefficient * TempBulkPotential; if (TempSaturationCurrentDensity <= 0 || _bp.DrainArea.Value <= 0 || _bp.SourceArea.Value <= 0) { SourceVCritical = DrainVCritical = vt * Math.Log(vt / (Circuit.Root2 * TempSaturationCurrent)); } else { DrainVCritical = vt * Math.Log(vt / (Circuit.Root2 * TempSaturationCurrentDensity * _bp.DrainArea)); SourceVCritical = vt * Math.Log(vt / (Circuit.Root2 * TempSaturationCurrentDensity * _bp.SourceArea)); } if (_mbp.CapBd.Given) { czbd = TempCapBd; } else { if (_mbp.BulkCapFactor.Given) { czbd = TempJunctionCap * _bp.DrainArea; } else { czbd = 0; } } if (_mbp.SidewallCapFactor.Given) { czbdsw = TempJunctionCapSidewall * _bp.DrainPerimeter; } else { czbdsw = 0; } arg = 1 - _mbp.ForwardCapDepletionCoefficient; var sarg = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); var sargsw = Math.Exp(-_mbp.BulkJunctionSideGradingCoefficient * Math.Log(arg)); CapBd = czbd; CapBdSidewall = czbdsw; F2D = czbd * (1 - _mbp.ForwardCapDepletionCoefficient * (1 + _mbp.BulkJunctionBotGradingCoefficient)) * sarg / arg + czbdsw * (1 - _mbp.ForwardCapDepletionCoefficient * (1 + _mbp.BulkJunctionSideGradingCoefficient)) * sargsw / arg; F3D = czbd * _mbp.BulkJunctionBotGradingCoefficient * sarg / arg / TempBulkPotential + czbdsw * _mbp.BulkJunctionSideGradingCoefficient * sargsw / arg / TempBulkPotential; F4D = czbd * TempBulkPotential * (1 - arg * sarg) / (1 - _mbp.BulkJunctionBotGradingCoefficient) + czbdsw * TempBulkPotential * (1 - arg * sargsw) / (1 - _mbp.BulkJunctionSideGradingCoefficient) - F3D / 2 * (TempDepletionCap * TempDepletionCap) - TempDepletionCap * F2D; if (_mbp.CapBs.Given) { czbs = TempCapBs; } else { if (_mbp.BulkCapFactor.Given) { czbs = TempJunctionCap * _bp.SourceArea; } else { czbs = 0; } } if (_mbp.SidewallCapFactor.Given) { czbssw = TempJunctionCapSidewall * _bp.SourcePerimeter; } else { czbssw = 0; } arg = 1 - _mbp.ForwardCapDepletionCoefficient; sarg = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); sargsw = Math.Exp(-_mbp.BulkJunctionSideGradingCoefficient * Math.Log(arg)); CapBs = czbs; CapBsSidewall = czbssw; F2S = czbs * (1 - _mbp.ForwardCapDepletionCoefficient * (1 + _mbp.BulkJunctionBotGradingCoefficient)) * sarg / arg + czbssw * (1 - _mbp.ForwardCapDepletionCoefficient * (1 + _mbp.BulkJunctionSideGradingCoefficient)) * sargsw / arg; F3S = czbs * _mbp.BulkJunctionBotGradingCoefficient * sarg / arg / TempBulkPotential + czbssw * _mbp.BulkJunctionSideGradingCoefficient * sargsw / arg / TempBulkPotential; F4S = czbs * TempBulkPotential * (1 - arg * sarg) / (1 - _mbp.BulkJunctionBotGradingCoefficient) + czbssw * TempBulkPotential * (1 - arg * sargsw) / (1 - _mbp.BulkJunctionSideGradingCoefficient) - F3S / 2 * (TempDepletionCap * TempDepletionCap) - TempDepletionCap * F2S; }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { OnConductance = 1.0 / _mbp.OnResistance; OffConductance = 1.0 / _mbp.OffResistance; }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; double drainSatCur, sourceSatCur, vgs, vds, vbs, vbd, vgd; double von; double vdsat, cdrain, cdreq; int xnrm, xrev; var vt = Circuit.KOverQ * _bp.Temperature; var check = 1; /* DETAILPROF */ /* first, we compute a few useful values - these could be * pre - computed, but for historical reasons are still done * here. They may be moved at the expense of instance size */ var effectiveLength = _bp.Length - 2 * _mbp.LateralDiffusion; // This is how Spice 3f5 implements it... There may be better ways to check for 0.0 if (_temp.TempSaturationCurrentDensity.Equals(0) || _bp.DrainArea.Value.Equals(0) || _bp.SourceArea.Value.Equals(0)) { drainSatCur = _temp.TempSaturationCurrent; sourceSatCur = _temp.TempSaturationCurrent; } else { drainSatCur = _temp.TempSaturationCurrentDensity * _bp.DrainArea; sourceSatCur = _temp.TempSaturationCurrentDensity * _bp.SourceArea; } var beta = _temp.TempTransconductance * _bp.Width / effectiveLength; /* * ok - now to do the start - up operations * * we must get values for vbs, vds, and vgs from somewhere * so we either predict them or recover them from last iteration * These are the two most common cases - either a prediction * step or the general iteration step and they * share some code, so we put them first - others later on */ if (state.Init == RealState.InitializationStates.InitFloat || state.Init == RealState.InitializationStates.InitTransient || state.Init == RealState.InitializationStates.InitFix && !_bp.Off) { // general iteration vbs = _mbp.MosfetType * (state.Solution[_bulkNode] - state.Solution[SourceNodePrime]); vgs = _mbp.MosfetType * (state.Solution[_gateNode] - state.Solution[SourceNodePrime]); vds = _mbp.MosfetType * (state.Solution[DrainNodePrime] - state.Solution[SourceNodePrime]); /* now some common crunching for some more useful quantities */ vbd = vbs - vds; vgd = vgs - vds; var vgdo = VoltageGs - VoltageDs; von = _mbp.MosfetType * Von; /* * limiting * we want to keep device voltages from changing * so fast that the exponentials churn out overflows * and similar rudeness */ if (VoltageDs >= 0) { vgs = Transistor.LimitFet(vgs, VoltageGs, von); vds = vgs - vgd; vds = Transistor.LimitVoltageDs(vds, VoltageDs); } else { vgd = Transistor.LimitFet(vgd, vgdo, von); vds = vgs - vgd; vds = -Transistor.LimitVoltageDs(-vds, -VoltageDs); vgs = vgd + vds; } if (vds >= 0) { vbs = Transistor.LimitJunction(vbs, VoltageBs, vt, _temp.SourceVCritical, out check); } else { vbd = Transistor.LimitJunction(vbd, VoltageBd, vt, _temp.DrainVCritical, out check); vbs = vbd + vds; } } else { /* ok - not one of the simple cases, so we have to * look at all of the possibilities for why we were * called. We still just initialize the three voltages */ if (state.Init == RealState.InitializationStates.InitJunction && !_bp.Off) { vds = _mbp.MosfetType * _bp.InitialVoltageDs; vgs = _mbp.MosfetType * _bp.InitialVoltageGs; vbs = _mbp.MosfetType * _bp.InitialVoltageBs; // This is what Spice 3f5 does, but I'm not sure how valid this still is if (vds.Equals(0) && vgs.Equals(0) && vbs.Equals(0) && (state.UseDc || state.Domain == RealState.DomainType.None || !state.UseIc)) { vbs = -1; vgs = _mbp.MosfetType * _temp.TempVt0; vds = 0; } } else { vbs = vgs = vds = 0; } } /* DETAILPROF */ /* * now all the preliminaries are over - we can start doing the * real work */ vbd = vbs - vds; vgd = vgs - vds; /* * bulk - source and bulk - drain diodes * here we just evaluate the ideal diode current and the * corresponding derivative (conductance). */ if (vbs <= 0) { CondBs = sourceSatCur / vt; BsCurrent = CondBs * vbs; CondBs += state.Gmin; } else { var evbs = Math.Exp(Math.Min(Transistor.MaximumExponentArgument, vbs / vt)); CondBs = sourceSatCur * evbs / vt + state.Gmin; BsCurrent = sourceSatCur * (evbs - 1); } if (vbd <= 0) { CondBd = drainSatCur / vt; BdCurrent = CondBd * vbd; CondBd += state.Gmin; } else { var evbd = Math.Exp(Math.Min(Transistor.MaximumExponentArgument, vbd / vt)); CondBd = drainSatCur * evbd / vt + state.Gmin; BdCurrent = drainSatCur * (evbd - 1); } /* now to determine whether the user was able to correctly * identify the source and drain of his device */ if (vds >= 0) { /* normal mode */ Mode = 1; } else { /* inverse mode */ Mode = -1; } /* DETAILPROF */ { /* * this block of code evaluates the drain current and its * derivatives using the shichman - hodges model and the * charges associated with the gate, channel and bulk for * mosfets */ /* the following 4 variables are local to this code block until * it is obvious that they can be made global */ double arg; double sarg; if ((Mode > 0 ? vbs : vbd) <= 0) { sarg = Math.Sqrt(_temp.TempPhi - (Mode > 0 ? vbs : vbd)); } else { sarg = Math.Sqrt(_temp.TempPhi); sarg = sarg - (Mode > 0 ? vbs : vbd) / (sarg + sarg); sarg = Math.Max(0, sarg); } von = _temp.TempVoltageBi * _mbp.MosfetType + _mbp.Gamma * sarg; var vgst = (Mode > 0 ? vgs : vgd) - von; vdsat = Math.Max(vgst, 0); if (sarg <= 0) { arg = 0; } else { arg = _mbp.Gamma / (sarg + sarg); } if (vgst <= 0) { /* * cutoff region */ cdrain = 0; Transconductance = 0; CondDs = 0; TransconductanceBs = 0; } else { /* * saturation region */ var betap = beta * (1 + _mbp.Lambda * (vds * Mode)); if (vgst <= vds * Mode) { cdrain = betap * vgst * vgst * .5; Transconductance = betap * vgst; CondDs = _mbp.Lambda * beta * vgst * vgst * .5; TransconductanceBs = Transconductance * arg; } else { /* * linear region */ cdrain = betap * (vds * Mode) * (vgst - .5 * (vds * Mode)); Transconductance = betap * (vds * Mode); CondDs = betap * (vgst - vds * Mode) + _mbp.Lambda * beta * (vds * Mode) * (vgst - .5 * (vds * Mode)); TransconductanceBs = Transconductance * arg; } } /* * finished */ } /* now deal with n vs p polarity */ Von = _mbp.MosfetType * von; SaturationVoltageDs = _mbp.MosfetType * vdsat; /* line 490 */ /* * COMPUTE EQUIVALENT DRAIN CURRENT SOURCE */ DrainCurrent = Mode * cdrain - BdCurrent; /* * check convergence */ if (!_bp.Off || state.Init != RealState.InitializationStates.InitFix) { if (check == 1) { state.IsConvergent = false; } } /* DETAILPROF */ /* save things away for next time */ VoltageBs = vbs; VoltageBd = vbd; VoltageGs = vgs; VoltageDs = vds; /* * load current vector */ var ceqbs = _mbp.MosfetType * (BsCurrent - (CondBs - state.Gmin) * vbs); var ceqbd = _mbp.MosfetType * (BdCurrent - (CondBd - state.Gmin) * vbd); if (Mode >= 0) { xnrm = 1; xrev = 0; cdreq = _mbp.MosfetType * (cdrain - CondDs * vds - Transconductance * vgs - TransconductanceBs * vbs); } else { xnrm = 0; xrev = 1; cdreq = -_mbp.MosfetType * (cdrain - CondDs * -vds - Transconductance * vgd - TransconductanceBs * vbd); } BulkPtr.Value -= ceqbs + ceqbd; DrainPrimePtr.Value += ceqbd - cdreq; SourcePrimePtr.Value += cdreq + ceqbs; /* * load y matrix */ DrainDrainPtr.Value += _temp.DrainConductance; SourceSourcePtr.Value += _temp.SourceConductance; BulkBulkPtr.Value += CondBd + CondBs; DrainPrimeDrainPrimePtr.Value += _temp.DrainConductance + CondDs + CondBd + xrev * (Transconductance + TransconductanceBs); SourcePrimeSourcePrimePtr.Value += _temp.SourceConductance + CondDs + CondBs + xnrm * (Transconductance + TransconductanceBs); DrainDrainPrimePtr.Value += -_temp.DrainConductance; SourceSourcePrimePtr.Value += -_temp.SourceConductance; BulkDrainPrimePtr.Value -= CondBd; BulkSourcePrimePtr.Value -= CondBs; DrainPrimeDrainPtr.Value += -_temp.DrainConductance; DrainPrimeGatePtr.Value += (xnrm - xrev) * Transconductance; DrainPrimeBulkPtr.Value += -CondBd + (xnrm - xrev) * TransconductanceBs; DrainPrimeSourcePrimePtr.Value += -CondDs - xnrm * (Transconductance + TransconductanceBs); SourcePrimeGatePtr.Value += -(xnrm - xrev) * Transconductance; SourcePrimeSourcePtr.Value += -_temp.SourceConductance; SourcePrimeBulkPtr.Value += -CondBs - (xnrm - xrev) * TransconductanceBs; SourcePrimeDrainPrimePtr.Value += -CondDs - xrev * (Transconductance + TransconductanceBs); }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; var rstate = state; double drainSatCur, sourceSatCur, vgs, vds, vbs, vbd, vgd; double von; double vdsat, cdrain = 0.0, cdreq; int xnrm, xrev; var vt = Circuit.KOverQ * _bp.Temperature; var check = 1; var effectiveLength = _bp.Length - 2 * _mbp.LateralDiffusion; if (_temp.TempSaturationCurrentDensity.Equals(0) || _bp.DrainArea.Value <= 0 || _bp.SourceArea.Value <= 0) { drainSatCur = _temp.TempSaturationCurrent; sourceSatCur = _temp.TempSaturationCurrent; } else { drainSatCur = _temp.TempSaturationCurrentDensity * _bp.DrainArea; sourceSatCur = _temp.TempSaturationCurrentDensity * _bp.SourceArea; } var beta = _temp.TempTransconductance * _bp.Width / effectiveLength; var oxideCap = _modeltemp.OxideCapFactor * effectiveLength * _bp.Width; if (state.Init == RealState.InitializationStates.InitFloat || state.Init == RealState.InitializationStates.InitTransient || state.Init == RealState.InitializationStates.InitFix && !_bp.Off) { // general iteration vbs = _mbp.MosfetType * (rstate.Solution[_bulkNode] - rstate.Solution[SourceNodePrime]); vgs = _mbp.MosfetType * (rstate.Solution[_gateNode] - rstate.Solution[SourceNodePrime]); vds = _mbp.MosfetType * (rstate.Solution[DrainNodePrime] - rstate.Solution[SourceNodePrime]); // now some common crunching for some more useful quantities vbd = vbs - vds; vgd = vgs - vds; var vgdo = VoltageGs - VoltageDs; von = _mbp.MosfetType * Von; /* * limiting * We want to keep device voltages from changing * so fast that the exponentials churn out overflows * and similar rudeness */ if (VoltageDs >= 0) { vgs = Transistor.LimitFet(vgs, VoltageGs, von); vds = vgs - vgd; vds = Transistor.LimitVoltageDs(vds, VoltageDs); } else { vgd = Transistor.LimitFet(vgd, vgdo, von); vds = vgs - vgd; vds = -Transistor.LimitVoltageDs(-vds, -VoltageDs); vgs = vgd + vds; } if (vds >= 0) { vbs = Transistor.LimitJunction(vbs, VoltageBs, vt, _temp.SourceVCritical, out check); } else { vbd = Transistor.LimitJunction(vbd, VoltageBd, vt, _temp.DrainVCritical, out check); vbs = vbd + vds; } } else { /* ok - not one of the simple cases, so we have to * look at other possibilities */ if (state.Init == RealState.InitializationStates.InitJunction && !_bp.Off) { vds = _mbp.MosfetType * _bp.InitialVoltageDs; vgs = _mbp.MosfetType * _bp.InitialVoltageGs; vbs = _mbp.MosfetType * _bp.InitialVoltageBs; // TODO: At some point, check what this is supposed to do if (vds.Equals(0.0) && vgs.Equals(0.0) && vbs.Equals(0.0) && (state.UseDc || state.Domain == RealState.DomainType.None || !state.UseIc)) { vbs = -1; vgs = _mbp.MosfetType * _temp.TempVt0; vds = 0; } } else { vbs = vgs = vds = 0; } } /* now all the preliminaries are over - we can start doing the * real work */ vbd = vbs - vds; vgd = vgs - vds; /* bulk - source and bulk - drain doides * here we just evaluate the ideal diode current and the * correspoinding derivative (conductance). */ if (vbs <= 0) { CondBs = sourceSatCur / vt; BsCurrent = CondBs * vbs; CondBs += state.Gmin; } else { var evbs = Math.Exp(vbs / vt); CondBs = sourceSatCur * evbs / vt + state.Gmin; BsCurrent = sourceSatCur * (evbs - 1); } if (vbd <= 0) { CondBd = drainSatCur / vt; BdCurrent = CondBd * vbd; CondBd += state.Gmin; } else { var evbd = Math.Exp(vbd / vt); CondBd = drainSatCur * evbd / vt + state.Gmin; BdCurrent = drainSatCur * (evbd - 1); } if (vds >= 0) { /* normal mode */ Mode = 1; } else { /* inverse mode */ Mode = -1; } { /* moseq2(vds, vbs, vgs, gm, gds, gmbs, qg, qc, qb, * cggb, cgdb, cgsb, cbgb, cbdb, cbsb) */ /* note: cgdb, cgsb, cbdb, cbsb never used */ /* * this routine evaluates the drain current, its derivatives and * the charges associated with the gate, channel and bulk * for mosfets * */ double arg; double sarg; double[] a4 = new double[4], b4 = new double[4], x4 = new double[8], poly4 = new double[8]; double dsrgdb, d2Sdb2; double sphi = 0.0; /* square root of phi */ double sphi3 = 0.0; /* square root of phi cubed */ double barg, d2Bdb2, dbrgdb, argd = 0.0, args = 0.0; double argxs = 0.0, argxd = 0.0; double dgddb2, dgddvb, dgdvds, gamasd; double xn = 0.0, argg = 0.0, vgst, dodvds = 0.0, dxndvd = 0.0, dxndvb = 0.0, dudvgs, dudvds, dudvbs; double argv, ufact, ueff, dsdvgs, dsdvbs; double xvalid = 0.0, bsarg = 0.0; double bodys = 0.0, gdbdvs = 0.0; double dldvgs = 0.0, dldvds = 0.0, dldvbs = 0.0; double xlamda = _mbp.Lambda; /* 'local' variables - these switch d & s around appropriately * so that we don't have to worry about vds < 0 */ double lvbs = Mode > 0 ? vbs : vbd; double lvds = Mode * vds; double lvgs = Mode > 0 ? vgs : vgd; double phiMinVbs = _temp.TempPhi - lvbs; double tmp; /* a temporary variable, not used for more than */ /* about 10 lines at a time */ /* * compute some useful quantities */ if (lvbs <= 0.0) { sarg = Math.Sqrt(phiMinVbs); dsrgdb = -0.5 / sarg; d2Sdb2 = 0.5 * dsrgdb / phiMinVbs; } else { sphi = Math.Sqrt(_temp.TempPhi); sphi3 = _temp.TempPhi * sphi; sarg = sphi / (1.0 + 0.5 * lvbs / _temp.TempPhi); tmp = sarg / sphi3; dsrgdb = -0.5 * sarg * tmp; d2Sdb2 = -dsrgdb * tmp; } if (lvds - lvbs >= 0) { barg = Math.Sqrt(phiMinVbs + lvds); dbrgdb = -0.5 / barg; d2Bdb2 = 0.5 * dbrgdb / (phiMinVbs + lvds); } else { barg = sphi / (1.0 + 0.5 * (lvbs - lvds) / _temp.TempPhi); tmp = barg / sphi3; dbrgdb = -0.5 * barg * tmp; d2Bdb2 = -dbrgdb * tmp; } /* * calculate threshold voltage (von) * narrow - channel effect */ /* XXX constant per device */ var factor = 0.125 * _mbp.NarrowFactor * 2.0 * Math.PI * Transistor.EpsilonSilicon / oxideCap * effectiveLength; /* XXX constant per device */ var eta = 1.0 + factor; var vbin = _temp.TempVoltageBi * _mbp.MosfetType + factor * phiMinVbs; if (_mbp.Gamma > 0.0 || _mbp.SubstrateDoping > 0.0) { var xwd = _modeltemp.Xd * barg; var xws = _modeltemp.Xd * sarg; /* * short - channel effect with vds .ne. 0.0 */ var argss = 0.0; var argsd = 0.0; var dbargs = 0.0; var dbargd = 0.0; dgdvds = 0.0; dgddb2 = 0.0; if (_mbp.JunctionDepth > 0) { tmp = 2.0 / _mbp.JunctionDepth; argxs = 1.0 + xws * tmp; argxd = 1.0 + xwd * tmp; args = Math.Sqrt(argxs); argd = Math.Sqrt(argxd); tmp = .5 * _mbp.JunctionDepth / effectiveLength; argss = tmp * (args - 1.0); argsd = tmp * (argd - 1.0); } gamasd = _mbp.Gamma * (1.0 - argss - argsd); var dbxwd = _modeltemp.Xd * dbrgdb; var dbxws = _modeltemp.Xd * dsrgdb; if (_mbp.JunctionDepth > 0) { tmp = 0.5 / effectiveLength; dbargs = tmp * dbxws / args; dbargd = tmp * dbxwd / argd; var dasdb2 = -_modeltemp.Xd * (d2Sdb2 + dsrgdb * dsrgdb * _modeltemp.Xd / (_mbp.JunctionDepth * argxs)) / (effectiveLength * args); var daddb2 = -_modeltemp.Xd * (d2Bdb2 + dbrgdb * dbrgdb * _modeltemp.Xd / (_mbp.JunctionDepth * argxd)) / (effectiveLength * argd); dgddb2 = -0.5 * _mbp.Gamma * (dasdb2 + daddb2); } dgddvb = -_mbp.Gamma * (dbargs + dbargd); if (_mbp.JunctionDepth > 0) { var ddxwd = -dbxwd; dgdvds = -_mbp.Gamma * 0.5 * ddxwd / (effectiveLength * argd); } } else { gamasd = _mbp.Gamma; dgddvb = 0.0; dgdvds = 0.0; dgddb2 = 0.0; } von = vbin + gamasd * sarg; var vth = von; vdsat = 0.0; if (!_mbp.FastSurfaceStateDensity.Value.Equals(0.0) && !oxideCap.Equals(0.0)) { /* XXX constant per model */ var cfs = Circuit.Charge * _mbp.FastSurfaceStateDensity * 1e4; var cdonco = -(gamasd * dsrgdb + dgddvb * sarg) + factor; xn = 1.0 + cfs / oxideCap * _bp.Width * effectiveLength + cdonco; tmp = vt * xn; von = von + tmp; argg = 1.0 / tmp; vgst = lvgs - von; } else { vgst = lvgs - von; if (lvgs <= von) { /* * cutoff region */ CondDs = 0.0; goto line1050; } } /* * compute some more useful quantities */ var sarg3 = sarg * sarg * sarg; /* XXX constant per model */ var sbiarg = Math.Sqrt(_temp.TempBulkPotential); var gammad = gamasd; var dgdvbs = dgddvb; var body = barg * barg * barg - sarg3; var gdbdv = 2.0 * gammad * (barg * barg * dbrgdb - sarg * sarg * dsrgdb); var dodvbs = -factor + dgdvbs * sarg + gammad * dsrgdb; if (_mbp.FastSurfaceStateDensity.Value.Equals(0.0)) { goto line400; } if (oxideCap.Equals(0.0)) { goto line410; } dxndvb = 2.0 * dgdvbs * dsrgdb + gammad * d2Sdb2 + dgddb2 * sarg; dodvbs = dodvbs + vt * dxndvb; dxndvd = dgdvds * dsrgdb; dodvds = dgdvds * sarg + vt * dxndvd; /* * evaluate effective mobility and its derivatives */ line400: if (oxideCap <= 0.0) { goto line410; } var udenom = vgst; tmp = _mbp.CriticalField * 100 /* cm / m */ * Transistor.EpsilonSilicon / _modeltemp.OxideCapFactor; if (udenom <= tmp) { goto line410; } ufact = Math.Exp(_mbp.CriticalFieldExp * Math.Log(tmp / udenom)); ueff = _mbp.SurfaceMobility * 1e-4 /* (m * * 2 / cm * * 2) */ * ufact; dudvgs = -ufact * _mbp.CriticalFieldExp / udenom; dudvds = 0.0; dudvbs = _mbp.CriticalFieldExp * ufact * dodvbs / vgst; goto line500; line410: ufact = 1.0; ueff = _mbp.SurfaceMobility * 1e-4 /* (m * * 2 / cm * * 2) */; dudvgs = 0.0; dudvds = 0.0; dudvbs = 0.0; /* * evaluate saturation voltage and its derivatives according to * grove - frohman equation */ line500: var vgsx = lvgs; gammad = gamasd / eta; dgdvbs = dgddvb; if (!_mbp.FastSurfaceStateDensity.Value.Equals(0.0) && !oxideCap.Equals(0.0)) { vgsx = Math.Max(lvgs, von); } if (gammad > 0) { var gammd2 = gammad * gammad; argv = (vgsx - vbin) / eta + phiMinVbs; if (argv <= 0.0) { vdsat = 0.0; dsdvgs = 0.0; dsdvbs = 0.0; } else { arg = Math.Sqrt(1.0 + 4.0 * argv / gammd2); vdsat = (vgsx - vbin) / eta + gammd2 * (1.0 - arg) / 2.0; vdsat = Math.Max(vdsat, 0.0); dsdvgs = (1.0 - 1.0 / arg) / eta; dsdvbs = (gammad * (1.0 - arg) + 2.0 * argv / (gammad * arg)) / eta * dgdvbs + 1.0 / arg + factor * dsdvgs; } } else { vdsat = (vgsx - vbin) / eta; vdsat = Math.Max(vdsat, 0.0); dsdvgs = 1.0; dsdvbs = 0.0; } if (_mbp.MaxDriftVelocity > 0) { /* * evaluate saturation voltage and its derivatives * according to baum's theory of scattering velocity * saturation */ var v1 = (vgsx - vbin) / eta + phiMinVbs; var v2 = phiMinVbs; var xv = _mbp.MaxDriftVelocity * effectiveLength / ueff; var a1 = gammad / 0.75; var b1 = -2.0 * (v1 + xv); var c1 = -2.0 * gammad * xv; var d1 = 2.0 * v1 * (v2 + xv) - v2 * v2 - 4.0 / 3.0 * gammad * sarg3; var a = -b1; var b = a1 * c1 - 4.0 * d1; var c = -d1 * (a1 * a1 - 4.0 * b1) - c1 * c1; var r = -a * a / 3.0 + b; var s = 2.0 * a * a * a / 27.0 - a * b / 3.0 + c; var r3 = r * r * r; var s2 = s * s; var p = s2 / 4.0 + r3 / 27.0; var p0 = Math.Abs(p); var p2 = Math.Sqrt(p0); double y3; if (p < 0) { var ro = Math.Sqrt(s2 / 4.0 + p0); ro = Math.Log(ro) / 3.0; ro = Math.Exp(ro); var fi = Math.Atan(-2.0 * p2 / s); y3 = 2.0 * ro * Math.Cos(fi / 3.0) - a / 3.0; } else { var p3 = -s / 2.0 + p2; p3 = Math.Exp(Math.Log(Math.Abs(p3)) / 3.0); var p4 = -s / 2.0 - p2; p4 = Math.Exp(Math.Log(Math.Abs(p4)) / 3.0); y3 = p3 + p4 - a / 3.0; } var iknt = 0; var a3 = Math.Sqrt(a1 * a1 / 4.0 - b1 + y3); var b3 = Math.Sqrt(y3 * y3 / 4.0 - d1); for (int i = 1; i <= 4; i++) { a4[i - 1] = a1 / 2.0 + Sig1[i - 1] * a3; b4[i - 1] = y3 / 2.0 + Sig2[i - 1] * b3; var delta4 = a4[i - 1] * a4[i - 1] / 4.0 - b4[i - 1]; if (delta4 < 0) { continue; } iknt = iknt + 1; tmp = Math.Sqrt(delta4); x4[iknt - 1] = -a4[i - 1] / 2.0 + tmp; iknt = iknt + 1; x4[iknt - 1] = -a4[i - 1] / 2.0 - tmp; } var jknt = 0; for (int j = 1; j <= iknt; j++) { if (x4[j - 1] <= 0) { continue; } /* XXX implement this sanely */ poly4[j - 1] = x4[j - 1] * x4[j - 1] * x4[j - 1] * x4[j - 1] + a1 * x4[j - 1] * x4[j - 1] * x4[j - 1]; poly4[j - 1] = poly4[j - 1] + b1 * x4[j - 1] * x4[j - 1] + c1 * x4[j - 1] + d1; if (Math.Abs(poly4[j - 1]) > 1.0e-6) { continue; } jknt = jknt + 1; if (jknt <= 1) { xvalid = x4[j - 1]; } if (x4[j - 1] > xvalid) { continue; } xvalid = x4[j - 1]; } if (jknt > 0) { vdsat = xvalid * xvalid - phiMinVbs; } } /* * evaluate effective channel length and its derivatives */ if (!lvds.Equals(0.0)) { gammad = gamasd; double dbsrdb; if (lvbs - vdsat <= 0) { bsarg = Math.Sqrt(vdsat + phiMinVbs); dbsrdb = -0.5 / bsarg; } else { bsarg = sphi / (1.0 + 0.5 * (lvbs - vdsat) / _temp.TempPhi); dbsrdb = -0.5 * bsarg * bsarg / sphi3; } bodys = bsarg * bsarg * bsarg - sarg3; gdbdvs = 2.0 * gammad * (bsarg * bsarg * dbsrdb - sarg * sarg * dsrgdb); double xlfact; double dldsat; if (_mbp.MaxDriftVelocity <= 0) { if (_mbp.SubstrateDoping.Value.Equals(0.0)) { goto line610; } if (xlamda > 0.0) { goto line610; } argv = (lvds - vdsat) / 4.0; var sargv = Math.Sqrt(1.0 + argv * argv); arg = Math.Sqrt(argv + sargv); xlfact = _modeltemp.Xd / (effectiveLength * lvds); xlamda = xlfact * arg; dldsat = lvds * xlamda / (8.0 * sargv); } else { argv = (vgsx - vbin) / eta - vdsat; var xdv = _modeltemp.Xd / Math.Sqrt(_mbp.ChannelCharge); var xlv = _mbp.MaxDriftVelocity * xdv / (2.0 * ueff); var vqchan = argv - gammad * bsarg; var dqdsat = -1.0 + gammad * dbsrdb; var vl = _mbp.MaxDriftVelocity * effectiveLength; var dfunds = vl * dqdsat - ueff * vqchan; var dfundg = (vl - ueff * vdsat) / eta; var dfundb = -vl * (1.0 + dqdsat - factor / eta) + ueff * (gdbdvs - dgdvbs * bodys / 1.5) / eta; dsdvgs = -dfundg / dfunds; dsdvbs = -dfundb / dfunds; if (_mbp.SubstrateDoping.Value.Equals(0.0)) { goto line610; } if (xlamda > 0.0) { goto line610; } argv = lvds - vdsat; argv = Math.Max(argv, 0.0); var xls = Math.Sqrt(xlv * xlv + argv); dldsat = xdv / (2.0 * xls); xlfact = xdv / (effectiveLength * lvds); xlamda = xlfact * (xls - xlv); dldsat = dldsat / effectiveLength; } dldvgs = dldsat * dsdvgs; dldvds = -xlamda + dldsat; dldvbs = dldsat * dsdvbs; } // Edited to work goto line610_finish; line610: dldvgs = 0.0; dldvds = 0.0; dldvbs = 0.0; line610_finish: /* * limit channel shortening at punch - through */ var xwb = _modeltemp.Xd * sbiarg; var xld = effectiveLength - xwb; var clfact = 1.0 - xlamda * lvds; dldvds = -xlamda - dldvds; var xleff = effectiveLength * clfact; var deltal = xlamda * lvds * effectiveLength; if (_mbp.SubstrateDoping.Value.Equals(0.0)) { xwb = 0.25e-6; } if (xleff < xwb) { xleff = xwb / (1.0 + (deltal - xld) / xwb); clfact = xleff / effectiveLength; var dfact = xleff * xleff / (xwb * xwb); dldvgs = dfact * dldvgs; dldvds = dfact * dldvds; dldvbs = dfact * dldvbs; } /* * evaluate effective beta (effective kp) */ var beta1 = beta * ufact / clfact; /* * test for mode of operation and branch appropriately */ gammad = gamasd; dgdvbs = dgddvb; if (lvds <= 1.0e-10) { if (lvgs <= von) { if (_mbp.FastSurfaceStateDensity.Value.Equals(0.0) || oxideCap.Equals(0.0)) { CondDs = 0.0; goto line1050; } CondDs = beta1 * (von - vbin - gammad * sarg) * Math.Exp(argg * (lvgs - von)); goto line1050; } CondDs = beta1 * (lvgs - vbin - gammad * sarg); goto line1050; } if (lvgs > von) { goto line900; } /* * subthreshold region */ if (vdsat <= 0) { CondDs = 0.0; if (lvgs > vth) { goto doneval; } goto line1050; } var vdson = Math.Min(vdsat, lvds); if (lvds > vdsat) { barg = bsarg; body = bodys; gdbdv = gdbdvs; } var cdson = beta1 * ((von - vbin - eta * vdson * 0.5) * vdson - gammad * body / 1.5); var didvds = beta1 * (von - vbin - eta * vdson - gammad * barg); var gdson = -cdson * dldvds / clfact - beta1 * dgdvds * body / 1.5; if (lvds < vdsat) { gdson = gdson + didvds; } var gbson = -cdson * dldvbs / clfact + beta1 * (dodvbs * vdson + factor * vdson - dgdvbs * body / 1.5 - gdbdv); if (lvds > vdsat) { gbson = gbson + didvds * dsdvbs; } var expg = Math.Exp(argg * (lvgs - von)); cdrain = cdson * expg; var gmw = cdrain * argg; Transconductance = gmw; if (lvds > vdsat) { Transconductance = gmw + didvds * dsdvgs * expg; } tmp = gmw * (lvgs - von) / xn; CondDs = gdson * expg - Transconductance * dodvds - tmp * dxndvd; TransconductanceBs = gbson * expg - Transconductance * dodvbs - tmp * dxndvb; goto doneval; line900: if (lvds <= vdsat) { /* * linear region */ cdrain = beta1 * ((lvgs - vbin - eta * lvds / 2.0) * lvds - gammad * body / 1.5); arg = cdrain * (dudvgs / ufact - dldvgs / clfact); Transconductance = arg + beta1 * lvds; arg = cdrain * (dudvds / ufact - dldvds / clfact); CondDs = arg + beta1 * (lvgs - vbin - eta * lvds - gammad * barg - dgdvds * body / 1.5); arg = cdrain * (dudvbs / ufact - dldvbs / clfact); TransconductanceBs = arg - beta1 * (gdbdv + dgdvbs * body / 1.5 - factor * lvds); } else { /* * saturation region */ cdrain = beta1 * ((lvgs - vbin - eta * vdsat / 2.0) * vdsat - gammad * bodys / 1.5); arg = cdrain * (dudvgs / ufact - dldvgs / clfact); Transconductance = arg + beta1 * vdsat + beta1 * (lvgs - vbin - eta * vdsat - gammad * bsarg) * dsdvgs; CondDs = -cdrain * dldvds / clfact - beta1 * dgdvds * bodys / 1.5; arg = cdrain * (dudvbs / ufact - dldvbs / clfact); TransconductanceBs = arg - beta1 * (gdbdvs + dgdvbs * bodys / 1.5 - factor * vdsat) + beta1 * (lvgs - vbin - eta * vdsat - gammad * bsarg) * dsdvbs; } /* * compute charges for "on" region */ goto doneval; /* * finish special cases */ line1050: cdrain = 0.0; Transconductance = 0.0; TransconductanceBs = 0.0; /* * finished */ } doneval: Von = _mbp.MosfetType * von; SaturationVoltageDs = _mbp.MosfetType * vdsat; /* * COMPUTE EQUIVALENT DRAIN CURRENT SOURCE */ DrainCurrent = Mode * cdrain - BdCurrent; /* * check convergence */ if (!_bp.Off || state.Init != RealState.InitializationStates.InitFix) { if (check == 1) { state.IsConvergent = false; } } VoltageBs = vbs; VoltageBd = vbd; VoltageGs = vgs; VoltageDs = vds; /* * load current vector */ var ceqbs = _mbp.MosfetType * (BsCurrent - (CondBs - state.Gmin) * vbs); var ceqbd = _mbp.MosfetType * (BdCurrent - (CondBd - state.Gmin) * vbd); if (Mode >= 0) { xnrm = 1; xrev = 0; cdreq = _mbp.MosfetType * (cdrain - CondDs * vds - Transconductance * vgs - TransconductanceBs * vbs); } else { xnrm = 0; xrev = 1; cdreq = -_mbp.MosfetType * (cdrain - CondDs * -vds - Transconductance * vgd - TransconductanceBs * vbd); } BulkPtr.Value -= ceqbs + ceqbd; DrainPrimePtr.Value += ceqbd - cdreq; SourcePrimePtr.Value += cdreq + ceqbs; // load Y-matrix DrainDrainPtr.Value += _temp.DrainConductance; SourceSourcePtr.Value += _temp.SourceConductance; BulkBulkPtr.Value += CondBd + CondBs; DrainPrimeDrainPrimePtr.Value += _temp.DrainConductance + CondDs + CondBd + xrev * (Transconductance + TransconductanceBs); SourcePrimeSourcePrimePtr.Value += _temp.SourceConductance + CondDs + CondBs + xnrm * (Transconductance + TransconductanceBs); DrainDrainPrimePtr.Value += -_temp.DrainConductance; SourceSourcePrimePtr.Value += -_temp.SourceConductance; BulkDrainPrimePtr.Value -= CondBd; BulkSourcePrimePtr.Value -= CondBs; DrainPrimeDrainPtr.Value += -_temp.DrainConductance; DrainPrimeGatePtr.Value += (xnrm - xrev) * Transconductance; DrainPrimeBulkPtr.Value += -CondBd + (xnrm - xrev) * TransconductanceBs; DrainPrimeSourcePrimePtr.Value += -CondDs - xnrm * (Transconductance + TransconductanceBs); SourcePrimeGatePtr.Value += -(xnrm - xrev) * Transconductance; SourcePrimeSourcePtr.Value += -_temp.SourceConductance; SourcePrimeBulkPtr.Value += -CondBs - (xnrm - xrev) * TransconductanceBs; SourcePrimeDrainPrimePtr.Value += -CondDs - xrev * (Transconductance + TransconductanceBs); }
protected void SetSweepSimulation(ICircuitContext context, List <KeyValuePair <Parameter, double> > parameterValues, BaseSimulation simulation) { ParameterUpdater.Update(simulation, context, parameterValues); }
/// <summary> /// Test convergence on device-level /// </summary> /// <param name="simulation">Base simulation</param> /// <returns></returns> public virtual bool IsConvergent(BaseSimulation simulation) { return(true); }
/// <summary> /// Loads the Y-matrix and Rhs-vector. /// </summary> /// <param name="simulation">The base simulation.</param> public void Load(BaseSimulation simulation) { simulation.ThrowIfNull(nameof(simulation)); var state = simulation.RealState; // Get the current voltages Initialize(simulation, out var vgs, out var vds, out var vbs, out var check); var vbd = vbs - vds; var vgd = vgs - vds; /* * bulk - source and bulk - drain diodes * here we just evaluate the ideal diode current and the * corresponding derivative (conductance). */ if (vbs <= 0) { CondBs = SourceSatCurrent / Vt; BsCurrent = CondBs * vbs; CondBs += BaseConfiguration.Gmin; } else { var evbs = Math.Exp(Math.Min(MaximumExponentArgument, vbs / Vt)); CondBs = SourceSatCurrent * evbs / Vt + BaseConfiguration.Gmin; BsCurrent = SourceSatCurrent * (evbs - 1); } if (vbd <= 0) { CondBd = DrainSatCurrent / Vt; BdCurrent = CondBd * vbd; CondBd += BaseConfiguration.Gmin; } else { var evbd = Math.Exp(Math.Min(MaximumExponentArgument, vbd / Vt)); CondBd = DrainSatCurrent * evbd / Vt + BaseConfiguration.Gmin; BdCurrent = DrainSatCurrent * (evbd - 1); } /* * Now to determine whether the user was able to correctly * identify the source and drain of his device */ if (vds >= 0) { // normal mode Mode = 1; } else { // inverse mode Mode = -1; } // Update VoltageBs = vbs; VoltageBd = vbd; VoltageGs = vgs; VoltageDs = vds; // Evaluate the currents and derivatives var cdrain = Mode > 0 ? Evaluate(vgs, vds, vbs) : Evaluate(vgd, -vds, vbd); DrainCurrent = Mode * cdrain - BdCurrent; // Check convergence if (!BaseParameters.Off || state.Init != InitializationModes.Fix) { if (check) { state.IsConvergent = false; } } // Load current vector double xnrm, xrev, cdreq; var ceqbs = ModelParameters.MosfetType * (BsCurrent - (CondBs - BaseConfiguration.Gmin) * vbs); var ceqbd = ModelParameters.MosfetType * (BdCurrent - (CondBd - BaseConfiguration.Gmin) * vbd); if (Mode >= 0) { xnrm = 1; xrev = 0; cdreq = ModelParameters.MosfetType * (cdrain - CondDs * vds - Transconductance * vgs - TransconductanceBs * vbs); } else { xnrm = 0; xrev = 1; cdreq = -ModelParameters.MosfetType * (cdrain - CondDs * -vds - Transconductance * vgd - TransconductanceBs * vbd); } BulkPtr.Value -= ceqbs + ceqbd; DrainPrimePtr.Value += ceqbd - cdreq; SourcePrimePtr.Value += cdreq + ceqbs; // Load Y-matrix DrainDrainPtr.Value += DrainConductance; SourceSourcePtr.Value += SourceConductance; BulkBulkPtr.Value += CondBd + CondBs; DrainPrimeDrainPrimePtr.Value += DrainConductance + CondDs + CondBd + xrev * (Transconductance + TransconductanceBs); SourcePrimeSourcePrimePtr.Value += SourceConductance + CondDs + CondBs + xnrm * (Transconductance + TransconductanceBs); DrainDrainPrimePtr.Value += -DrainConductance; SourceSourcePrimePtr.Value += -SourceConductance; BulkDrainPrimePtr.Value -= CondBd; BulkSourcePrimePtr.Value -= CondBs; DrainPrimeDrainPtr.Value += -DrainConductance; DrainPrimeGatePtr.Value += (xnrm - xrev) * Transconductance; DrainPrimeBulkPtr.Value += -CondBd + (xnrm - xrev) * TransconductanceBs; DrainPrimeSourcePrimePtr.Value += -CondDs - xnrm * (Transconductance + TransconductanceBs); SourcePrimeGatePtr.Value += -(xnrm - xrev) * Transconductance; SourcePrimeSourcePtr.Value += -SourceConductance; SourcePrimeBulkPtr.Value += -CondBs - (xnrm - xrev) * TransconductanceBs; SourcePrimeDrainPrimePtr.Value += -CondDs - xrev * (Transconductance + TransconductanceBs); }
/// <summary> /// Perform temperature-dependent calculations. /// </summary> /// <param name="simulation">The base simulation.</param> public void Temperature(BaseSimulation simulation) { // Calculate coupling factor Factor = BaseParameters.Coupling * Math.Sqrt(BaseParameters1.Inductance * BaseParameters2.Inductance); }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } bool currentState; var state = simulation.RealState; if (state.Init == InitializationModes.Fix || state.Init == InitializationModes.Junction) { if (_bp.ZeroState) { // Switch specified "on" CurrentState = true; currentState = true; } else { // Switch specified "off" CurrentState = false; currentState = false; } } else { // First iteration after a new timepoint! var vCtrl = state.Solution[_contPosourceNode] - state.Solution[_contNegateNode]; if (UseOldState) { // Calculate the current state if (vCtrl > _mbp.Threshold + _mbp.Hysteresis) { currentState = true; } else if (vCtrl < _mbp.Threshold - _mbp.Hysteresis) { currentState = false; } else { currentState = PreviousState; } CurrentState = currentState; UseOldState = false; } else { // Continue from last iteration PreviousState = CurrentState; // Calculate the current state if (vCtrl > _mbp.Threshold + _mbp.Hysteresis) { CurrentState = true; currentState = true; } else if (vCtrl < _mbp.Threshold - _mbp.Hysteresis) { CurrentState = false; currentState = false; } else { currentState = PreviousState; } // Ensure one more iteration if (currentState != PreviousState) { state.IsConvergent = false; } } } var gNow = currentState ? _modelload.OnConductance : _modelload.OffConductance; Cond = gNow; // Load the Y-matrix PosPosPtr.Value += gNow; PosNegPtr.Value -= gNow; NegPosPtr.Value -= gNow; NegNegPtr.Value += gNow; }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="simulation">Base simulation</param> public override void Temperature(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } /* now model parameter preprocessing */ if (!_mbp.NominalTemperature.Given) { _mbp.NominalTemperature.RawValue = simulation.RealState.NominalTemperature; } Factor1 = _mbp.NominalTemperature / Circuit.ReferenceTemperature; VtNominal = _mbp.NominalTemperature * Circuit.KOverQ; var kt1 = Circuit.Boltzmann * _mbp.NominalTemperature; EgFet1 = 1.16 - 7.02e-4 * _mbp.NominalTemperature * _mbp.NominalTemperature / (_mbp.NominalTemperature + 1108); var arg1 = -EgFet1 / (kt1 + kt1) + 1.1150877 / (Circuit.Boltzmann * (Circuit.ReferenceTemperature + Circuit.ReferenceTemperature)); PbFactor1 = -2 * VtNominal * (1.5 * Math.Log(Factor1) + Circuit.Charge * arg1); if (_mbp.SubstrateDoping.Given) { if (_mbp.SubstrateDoping * 1e6 > 1.45e16) { if (!_mbp.Phi.Given) { _mbp.Phi.RawValue = 2 * VtNominal * Math.Log(_mbp.SubstrateDoping * 1e6 / 1.45e16); _mbp.Phi.RawValue = Math.Max(.1, _mbp.Phi); } var fermis = _mbp.MosfetType * .5 * _mbp.Phi; var wkfng = 3.2; if (!_mbp.GateType.Given) { _mbp.GateType.RawValue = 1; } if (!_mbp.GateType.RawValue.Equals(0)) { var fermig = _mbp.MosfetType * _mbp.GateType * .5 * EgFet1; wkfng = 3.25 + .5 * EgFet1 - fermig; } var wkfngs = wkfng - (3.25 + .5 * EgFet1 + fermis); if (!_mbp.Gamma.Given) { _mbp.Gamma.RawValue = Math.Sqrt(2 * 11.70 * 8.854214871e-12 * Circuit.Charge * _mbp.SubstrateDoping * 1e6) / _mbp.OxideCapFactor; } if (!_mbp.Vt0.Given) { if (!_mbp.SurfaceStateDensity.Given) { _mbp.SurfaceStateDensity.RawValue = 0; } var vfb = wkfngs - _mbp.SurfaceStateDensity * 1e4 * Circuit.Charge / _mbp.OxideCapFactor; _mbp.Vt0.RawValue = vfb + _mbp.MosfetType * (_mbp.Gamma * Math.Sqrt(_mbp.Phi) + _mbp.Phi); } Xd = Math.Sqrt((Transistor.EpsilonSilicon + Transistor.EpsilonSilicon) / (Circuit.Charge * _mbp.SubstrateDoping * 1e6)); } else { _mbp.SubstrateDoping.RawValue = 0; throw new CircuitException("{0}: Nsub < Ni".FormatString(Name)); } } if (!_mbp.BulkCapFactor.Given) { _mbp.BulkCapFactor.RawValue = Math.Sqrt(Transistor.EpsilonSilicon * Circuit.Charge * _mbp.SubstrateDoping * 1e6 /* cm**3/m**3 */ / (2 * _mbp.BulkJunctionPotential)); } }
/// <summary> /// Tests convergence at the device-level. /// </summary> /// <param name="simulation">The base simulation.</param> /// <returns> /// <c>true</c> if the device determines the solution converges; otherwise, <c>false</c>. /// </returns> public bool IsConvergent(BaseSimulation simulation) => true;
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Load(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; var rstate = state; double drainSatCur, sourceSatCur, vgs, vds, vbs, vbd, vgd; double von; double vdsat, cdrain = 0.0, cdreq; int xnrm, xrev; var vt = Circuit.KOverQ * _bp.Temperature; var check = 1; var effectiveLength = _bp.Length - 2 * _mbp.LateralDiffusion; if (_temp.TempSaturationCurrentDensity.Equals(0) || _bp.DrainArea.Value <= 0 || _bp.SourceArea.Value <= 0) { drainSatCur = _temp.TempSaturationCurrent; sourceSatCur = _temp.TempSaturationCurrent; } else { drainSatCur = _temp.TempSaturationCurrentDensity * _bp.DrainArea; sourceSatCur = _temp.TempSaturationCurrentDensity * _bp.SourceArea; } var beta = _temp.TempTransconductance * _bp.Width / effectiveLength; var oxideCap = _mbp.OxideCapFactor * effectiveLength * _bp.Width; if (state.Init == InitializationModes.Float || (simulation is TimeSimulation tsim && tsim.Method.BaseTime.Equals(0.0)) || state.Init == InitializationModes.Fix && !_bp.Off) { // general iteration vbs = _mbp.MosfetType * (rstate.Solution[_bulkNode] - rstate.Solution[SourceNodePrime]); vgs = _mbp.MosfetType * (rstate.Solution[_gateNode] - rstate.Solution[SourceNodePrime]); vds = _mbp.MosfetType * (rstate.Solution[DrainNodePrime] - rstate.Solution[SourceNodePrime]); // now some common crunching for some more useful quantities vbd = vbs - vds; vgd = vgs - vds; var vgdo = VoltageGs - VoltageDs; von = _mbp.MosfetType * Von; /* * limiting * We want to keep device voltages from changing * so fast that the exponentials churn out overflows * and similar rudeness */ if (VoltageDs >= 0) { vgs = Transistor.LimitFet(vgs, VoltageGs, von); vds = vgs - vgd; vds = Transistor.LimitVoltageDs(vds, VoltageDs); } else { vgd = Transistor.LimitFet(vgd, vgdo, von); vds = vgs - vgd; vds = -Transistor.LimitVoltageDs(-vds, -VoltageDs); vgs = vgd + vds; } if (vds >= 0) { vbs = Transistor.LimitJunction(vbs, VoltageBs, vt, _temp.SourceVCritical, out check); } else { vbd = Transistor.LimitJunction(vbd, VoltageBd, vt, _temp.DrainVCritical, out check); vbs = vbd + vds; } }