/// <summary> /// Bind behavior. /// </summary> /// <param name="simulation">The simulation.</param> /// <param name="context">Data provider</param> public override void Bind(Simulation simulation, BindingContext context) { base.Bind(simulation, context); // Get parameters BaseParameters = context.GetParameterSet <CommonBehaviors.IndependentSourceParameters>(); // Setup the waveform BaseParameters.Waveform?.Setup(); // Give some warnings if no value is given if (!BaseParameters.DcValue.Given) { // no DC value - either have a transient value or none CircuitWarning.Warning(this, BaseParameters.Waveform != null ? "{0} has no DC value, transient time 0 value used".FormatString(Name) : "{0} has no value, DC 0 assumed".FormatString(Name)); } if (context is ComponentBindingContext cc) { PosNode = cc.Pins[0]; NegNode = cc.Pins[1]; } _state = ((BaseSimulation)simulation).RealState; var solver = _state.Solver; PosPtr = solver.GetRhsElement(PosNode); NegPtr = solver.GetRhsElement(NegNode); }
/// <summary> /// Setup the behavior /// </summary> /// <param name="simulation">Simulation</param> /// <param name="provider">Data provider</param> public override void Setup(Simulation simulation, SetupDataProvider provider) { if (provider == null) { throw new ArgumentNullException(nameof(provider)); } // Get parameters BaseParameters = provider.GetParameterSet <CommonBehaviors.IndependentBaseParameters>(); // Setup the waveform BaseParameters.Waveform?.Setup(); if (!BaseParameters.DcValue.Given) { // No DC value: either have a transient value or none if (BaseParameters.Waveform != null) { CircuitWarning.Warning(this, "{0}: No DC value, transient time 0 value used".FormatString(Name)); BaseParameters.DcValue.RawValue = BaseParameters.Waveform.Value; } else { CircuitWarning.Warning(this, "{0}: No value, DC 0 assumed".FormatString(Name)); } } }
/// <summary> /// Setup the device /// </summary> /// <param name="ckt">The circuit</param> public override void Setup(Circuit ckt) { var model = Model as BSIM3v30Model; pParam = null; // Allocate nodes var nodes = BindNodes(ckt); BSIM3dNode = nodes[0].Index; BSIM3gNode = nodes[1].Index; BSIM3sNode = nodes[2].Index; BSIM3bNode = nodes[3].Index; // Allocate states BSIM3states = ckt.State.GetState(17); /* allocate a chunk of the state vector */ /* perform the parameter defaulting */ if (!BSIM3acnqsMod.Given) { BSIM3acnqsMod.Value = model.BSIM3acnqsMod; } else if ((BSIM3acnqsMod != 0) && (BSIM3acnqsMod != 1)) { BSIM3acnqsMod.Value = model.BSIM3acnqsMod; CircuitWarning.Warning(this, $"Warning: acnqsMod has been set to its global value {model.BSIM3acnqsMod}."); } /* process drain series resistance */ if ((model.BSIM3sheetResistance > 0.0) && (BSIM3drainSquares > 0.0)) { BSIM3dNodePrime = CreateNode(ckt, Name.Grow("#drain")).Index; } else { BSIM3dNodePrime = BSIM3dNode; } /* process source series resistance */ if ((model.BSIM3sheetResistance > 0.0) && (BSIM3sourceSquares > 0.0)) { BSIM3sNodePrime = CreateNode(ckt, Name.Grow("#source")).Index; } else { BSIM3sNodePrime = BSIM3sNode; } /* internal charge node */ if ((BSIM3nqsMod > 0) && (BSIM3qNode == 0)) { BSIM3qNode = CreateNode(ckt, Name.Grow("#charge")).Index; } else { BSIM3qNode = 0; } }
/// <summary> /// Iterates to a solution slowly ramping up independent voltages and currents. /// </summary> /// <param name="maxIterations">The maximum number of iterations per step.</param> /// <param name="steps">The number of steps.</param> /// <returns></returns> protected bool IterateSourceStepping(int maxIterations, int steps) { var state = RealState; // We will slowly ramp up voltages starting at 0 to make sure it converges CircuitWarning.Warning(this, Properties.Resources.StartSourceStepping); // We could've ended up with some crazy value, so let's reset it for (var i = 0; i < RealState.Solution.Length; i++) { RealState.Solution[i] = 0.0; } // Start SRC stepping bool success = true; state.Init = InitializationModes.Junction; for (var i = 0; i <= steps; i++) { state.SourceFactor = i / (double)steps; if (!Iterate(maxIterations)) { state.SourceFactor = 1.0; CircuitWarning.Warning(this, Properties.Resources.SourceSteppingFailed); success = false; break; } } return(success); }
/// <summary> /// Sets up the convergence aid for a specific simulation. /// </summary> /// <param name="simulation">The simulation.</param> public virtual void Initialize(BaseSimulation simulation) { // Get the unknown variables Variables = simulation.Variables; // Get the real solver var state = simulation.RealState; Solver = state.Solver; // Get the node if (!simulation.Variables.TryGetNode(Name, out var node)) { CircuitWarning.Warning(this, "Could not set convergence aid: variable {0} not found.".FormatString(Name)); Node = null; return; } Node = node; // Get the necessary elements Diagonal = Solver.GetMatrixElement(node.Index, node.Index); Rhs = Value.Equals(0.0) ? null : Solver.GetRhsElement(node.Index); // Update the current solution to reflect our convergence aid value state.Solution[node.Index] = Value; }
/// <summary> /// Perform temperature-dependent calculations. /// </summary> void ITemperatureBehavior.Temperature() { if (_mbp.NominalTemperature.Given) { _mbp.NominalTemperature.RawValue = ((BaseSimulation)Simulation).RealState.NominalTemperature; } var vtnom = Constants.KOverQ * _mbp.NominalTemperature; var fact1 = _mbp.NominalTemperature / Constants.ReferenceTemperature; var kt1 = Constants.Boltzmann * _mbp.NominalTemperature; var egfet1 = 1.16 - (7.02e-4 * _mbp.NominalTemperature * _mbp.NominalTemperature) / (_mbp.NominalTemperature + 1108); var arg1 = -egfet1 / (kt1 + kt1) + 1.1150877 / (Constants.Boltzmann * 2 * Constants.ReferenceTemperature); var pbfact1 = -2 * vtnom * (1.5 * Math.Log(fact1) + Constants.Charge * arg1); Pbo = (_mbp.GatePotential - pbfact1) / fact1; var gmaold = (_mbp.GatePotential - Pbo) / Pbo; Cjfact = 1 / (1 + .5 * (4e-4 * (_mbp.NominalTemperature - Constants.ReferenceTemperature) - gmaold)); if (_mbp.DepletionCapCoefficient > 0.95) { CircuitWarning.Warning(this, "{0}: Depletion capacitance coefficient too large, limited to 0.95".FormatString(Name)); _mbp.DepletionCapCoefficient.Value = .95; } Xfc = Math.Log(1 - _mbp.DepletionCapCoefficient); F2 = Math.Exp((1 + 0.5) * Xfc); F3 = 1 - _mbp.DepletionCapCoefficient * (1 + 0.5); /* Modification for Sydney University JFET model */ BFactor = (1 - _mbp.B) / (_mbp.GatePotential - _mbp.Threshold); }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public void Temperature(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } double factor; double resistance = BaseParameters.Resistance; // Default Value Processing for Resistor Instance if (!BaseParameters.Temperature.Given) { BaseParameters.Temperature.RawValue = simulation.RealState.Temperature; } if (!BaseParameters.Width.Given) { BaseParameters.Width.RawValue = ModelParameters?.DefaultWidth ?? 0.0; } if (ModelParameters != null) { if (!BaseParameters.Resistance.Given) { if (ModelParameters.SheetResistance.Given && ModelParameters.SheetResistance > 0 && BaseParameters.Length > 0) { resistance = ModelParameters.SheetResistance * (BaseParameters.Length - ModelParameters.Narrow) / (BaseParameters.Width - ModelParameters.Narrow); } else { CircuitWarning.Warning(this, "{0}: resistance=0, set to 1000".FormatString(Name)); resistance = 1000; } } var difference = BaseParameters.Temperature - ModelParameters.NominalTemperature; if (ModelParameters.ExponentialCoefficient.Given) { factor = Math.Pow(1.01, ModelParameters.ExponentialCoefficient * difference); } else { factor = 1.0 + ModelParameters.TemperatureCoefficient1 * difference + ModelParameters.TemperatureCoefficient2 * difference * difference; } } else { factor = 1.0; } if (resistance < MinimumResistance) { resistance = MinimumResistance; } Conductance = 1.0 / (resistance * factor); }
/// <summary> /// Specify a parameter for this object /// </summary> /// <param name="id">The parameter identifier</param> /// <param name="value">The value</param> /// <param name="ckt">The circuit if applicable</param> public virtual void Set(string name, object value = null, Circuit ckt = null) { // Use reflection to find the member associated with the name if (!parameters.ContainsKey(name)) { CircuitWarning.Warning(this, $"{Name}: Parameter '{name}' does not exist"); return; } parameters[name].Set(this, value, ckt); }
/// <summary> /// Specify a parameter for this object /// </summary> /// <param name="name"></param> /// <param name="value"></param> public virtual void Set(string name, string value) { // Set the parameter if (ssetter.ContainsKey(name)) { ssetter[name].Invoke(me, value); } else { CircuitWarning.Warning(this, $"Unrecognized parameter \"{name}\" of type string"); } }
/// <summary> /// Execute behavior /// </summary> /// <param name="simulation">Base simulation</param> public override void Temperature(BaseSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } double factor; double resist = _bp.Resistance; // Default Value Processing for Resistor Instance if (!_bp.Temperature.Given) { _bp.Temperature.RawValue = simulation.RealState.Temperature; } if (!_bp.Width.Given) { _bp.Width.RawValue = _mbp?.DefaultWidth ?? 0.0; } if (_mbp != null) { if (!_bp.Resistance.Given) { if (_mbp.SheetResistance.Given && _mbp.SheetResistance > 0 && _bp.Length > 0) { resist = _mbp.SheetResistance * (_bp.Length - _mbp.Narrow) / (_bp.Width - _mbp.Narrow); } else { CircuitWarning.Warning(this, "{0}: resistance=0, set to 1000".FormatString(Name)); resist = 1000; } } var difference = _bp.Temperature - _mbp.NominalTemperature; factor = 1.0 + _mbp.TemperatureCoefficient1 * difference + _mbp.TemperatureCoefficient2 * difference * difference; } else { factor = 1.0; } if (resist < 1e-12) { resist = 1e-12; } Conductance = 1.0 / (resist * factor); }
/// <summary> /// Iterates to a solution while adding a conductive path to ground on all nodes. /// </summary> /// <param name="maxIterations">The maximum number of iterations per step.</param> /// <param name="steps">The number of steps.</param> /// <returns></returns> protected bool IterateDiagonalGminStepping(int maxIterations, int steps) { var state = RealState; // We will add a DC path to ground to all nodes to aid convergence CircuitWarning.Warning(this, Properties.Resources.StartDiagonalGminStepping); // We'll hack into the loading algorithm to apply our diagonal contributions _diagonalGmin = state.Gmin; if (_diagonalGmin <= 0) { _diagonalGmin = 1e-12; } void ApplyGminStep(object sender, LoadStateEventArgs args) => state.Solver.ApplyDiagonalGmin(_diagonalGmin); AfterLoad += ApplyGminStep; // We could've ended up with some crazy value, so let's reset it for (var i = 0; i < RealState.Solution.Length; i++) { RealState.Solution[i] = 0.0; } // Let's make it a bit easier for our iterations to converge for (var i = 0; i < steps; i++) { _diagonalGmin *= 10.0; } // Start GMIN stepping state.Init = InitializationModes.Junction; for (var i = 0; i <= steps; i++) { state.IsConvergent = false; if (!Iterate(maxIterations)) { _diagonalGmin = 0.0; CircuitWarning.Warning(this, Properties.Resources.GminSteppingFailed); break; } _diagonalGmin /= 10.0; state.Init = InitializationModes.Float; } // Try one more time without the gmin stepping AfterLoad -= ApplyGminStep; _diagonalGmin = 0.0; return(Iterate(maxIterations)); }
/// <summary> /// Specify a parameter for this object /// </summary> /// <param name="id">The parameter identifier</param> /// <param name="value">The value</param> public virtual void Set(string name, double value) { // Set the parameter if (dsetter.ContainsKey(name)) { dsetter[name].Invoke(me, value); } else if (pgetter.ContainsKey(name)) { pgetter[name].Invoke(me).Set(value); } else { CircuitWarning.Warning(this, $"Unrecognized parameter \"{name}\" of type double"); } }
/// <summary> /// Applies the rounding operator. /// </summary> /// <param name="arguments">The arguments.</param> /// <returns>The rounded result.</returns> public static Derivatives <Func <double> > ApplyRound(Derivatives <Func <double> >[] arguments) { arguments.ThrowIfEmpty(nameof(arguments)); var arg = arguments[0]; if (arguments.Length == 1) { var result = new DoubleDerivatives(); var a0 = arg[0]; result[0] = () => Math.Round(a0()); // The derivative of Round() is always 0 (horizontal), but doesn't exist depending on where it is rounded for (var i = 1; i < arg.Count; i++) { if (arg[i] != null) { CircuitWarning.Warning(null, "Trying to derive Round() for which the derivative may not exist in some points"); } } return(result); } if (arguments.Length == 2) { var result = new DoubleDerivatives(); var a0 = arg[0]; var b0 = arguments[1][0]; result[0] = () => Math.Round(a0(), (int)Math.Round(b0())); // The derivative of Round() is always 0 (horizontal), but doesn't exist depending on where it is rounded for (var i = 1; i < arg.Count; i++) { if (arg[i] != null) { CircuitWarning.Warning(null, "Trying to derive Round() to the first argument for which the derivative may not exist in some points"); } } for (var i = 1; i < arguments[1].Count; i++) { if (arguments[1][i] != null) { CircuitWarning.Warning(null, "Trying to derive Round() to the second argument for which the derivative may not exist in some points"); } } return(result); } throw new CircuitException("Invalid number of arguments for Round()"); }
/// <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; } VtNominal = Circuit.KOverQ * _mbp.NominalTemperature; // limit grading coeff to max of 0.9 if (_mbp.GradingCoefficient > 0.9) { _mbp.GradingCoefficient.RawValue = 0.9; CircuitWarning.Warning(this, "{0}: grading coefficient too large, limited to 0.9".FormatString(Name)); } // limit activation energy to min of 0.1 if (_mbp.ActivationEnergy < 0.1) { _mbp.ActivationEnergy.RawValue = 0.1; CircuitWarning.Warning(this, "{0}: activation energy too small, limited to 0.1".FormatString(Name)); } // limit depletion cap coeff to max of .95 if (_mbp.DepletionCapCoefficient > 0.95) { _mbp.DepletionCapCoefficient.RawValue = 0.95; CircuitWarning.Warning(this, "{0}: coefficient Fc too large, limited to 0.95".FormatString(Name)); } if (_mbp.Resistance > 0) { Conductance = 1 / _mbp.Resistance; } else { Conductance = 0; } Xfc = Math.Log(1 - _mbp.DepletionCapCoefficient); F2 = Math.Exp((1 + _mbp.GradingCoefficient) * Xfc); F3 = 1 - _mbp.DepletionCapCoefficient * (1 + _mbp.GradingCoefficient); }
/// <summary> /// Iterates to a solution while shunting PN-junctions with a conductance. /// </summary> /// <param name="maxIterations">The maximum number of iterations per step.</param> /// <param name="steps">The number of steps.</param> /// <returns></returns> protected bool IterateGminStepping(int maxIterations, int steps) { var state = RealState; // We will shunt all PN-junctions with a conductance (should be implemented by the components) CircuitWarning.Warning(this, Properties.Resources.StartGminStepping); // We could've ended up with some crazy value, so let's reset it for (var i = 0; i < RealState.Solution.Length; i++) { RealState.Solution[i] = 0.0; } // Let's make it a bit easier for our iterations to converge var original = state.Gmin; if (state.Gmin <= 0) { state.Gmin = 1e-12; } for (var i = 0; i < steps; i++) { state.Gmin *= 10.0; } // Start GMIN stepping state.Init = InitializationModes.Junction; for (var i = 0; i <= steps; i++) { state.IsConvergent = false; if (!Iterate(maxIterations)) { state.Gmin = original; CircuitWarning.Warning(this, Properties.Resources.GminSteppingFailed); break; } // Success! Let's try to get more accurate now state.Gmin /= 10.0; state.Init = InitializationModes.Float; } // Try one more time with the original gmin state.Gmin = original; return(Iterate(maxIterations)); }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="ckt">The circuit</param> public override void Temperature(Circuit ckt) { var state = ckt.State; CurrentTemperature = state.Temperature; if (!DIOnomTemp.Given) { DIOnomTemp.Value = state.NominalTemperature; } vtnom = Circuit.CONSTKoverQ * DIOnomTemp; // limit grading coeff to max of .9 if (DIOgradingCoeff > .9) { CircuitWarning.Warning(this, string.Format("Model {0}: Grading coefficient too large, limited to 0.9", Name)); DIOgradingCoeff.Value = 0.9; } // limit activation energy to min of .1 if (DIOactivationEnergy < 0.1) { CircuitWarning.Warning(this, string.Format("Model {0}: Activation energy too small, limited to 0.1", Name)); DIOactivationEnergy.Value = 0.1; } // limit depletion cap coeff to max of .95 if (DIOdepletionCapCoeff > .95) { CircuitWarning.Warning(this, string.Format("Model {0}: Coefficient Fc too large, limited to 0.95", Name)); DIOdepletionCapCoeff.Value = 0.95; } if (!DIOresist.Given || DIOresist == 0) { DIOconductance = 0; } else { DIOconductance = 1.0 / DIOresist; } xfc = Math.Log(1.0 - DIOdepletionCapCoeff); DIOf2 = Math.Exp((1.0 + DIOgradingCoeff) * xfc); DIOf3 = 1.0 - DIOdepletionCapCoeff * (1.0 + DIOgradingCoeff); }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="ckt">The circuit</param> public override void Temperature(Circuit ckt) { double factor; double difference; ResistorModel model = Model as ResistorModel ?? defaultmodel; // Default Value Processing for Resistor Instance if (!REStemp.Given) { REStemp.Value = ckt.State.Temperature; } if (!RESwidth.Given) { RESwidth.Value = model?.RESdefWidth ?? 0.0; } if (!RESlength.Given) { RESlength.Value = 0; } if (!RESresist.Given) { if (model.RESsheetRes.Given && (model.RESsheetRes != 0) && (RESlength != 0)) { RESresist.Value = model.RESsheetRes * (RESlength - model.RESnarrow) / (RESwidth - model.RESnarrow); } else { CircuitWarning.Warning(this, string.Format("{0}: resistance=0, set to 1000", Name ?? "NULL")); RESresist.Value = 1000; } } if (model != null) { difference = REStemp - model.REStnom; factor = 1.0 + (model.REStempCoeff1) * difference + (model.REStempCoeff2) * difference * difference; } else { difference = REStemp - 300.15; factor = 1.0; } RESconduct = 1.0 / (RESresist * factor); }
/// <summary> /// Bind the behavior. /// </summary> /// <param name="simulation">The simulation.</param> /// <param name="context">The context.</param> public override void Bind(Simulation simulation, BindingContext context) { base.Bind(simulation, context); // Get parameters BaseParameters = context.GetParameterSet <CommonBehaviors.IndependentSourceParameters>(); // Setup the waveform BaseParameters.Waveform?.Setup(); if (!BaseParameters.DcValue.Given) { // No DC value: either have a transient value or none if (BaseParameters.Waveform != null) { CircuitWarning.Warning(this, "{0}: No DC value, transient time 0 value used".FormatString(Name)); BaseParameters.DcValue.RawValue = BaseParameters.Waveform.Value; } else { CircuitWarning.Warning(this, "{0}: No value, DC 0 assumed".FormatString(Name)); } } if (context is ComponentBindingContext cc) { PosNode = cc.Pins[0]; NegNode = cc.Pins[1]; } _state = ((BaseSimulation)simulation).RealState; var solver = _state.Solver; var variables = simulation.Variables; BranchEq = variables.Create(Name.Combine("branch"), VariableType.Current).Index; // Get matrix elements PosBranchPtr = solver.GetMatrixElement(PosNode, BranchEq); BranchPosPtr = solver.GetMatrixElement(BranchEq, PosNode); NegBranchPtr = solver.GetMatrixElement(NegNode, BranchEq); BranchNegPtr = solver.GetMatrixElement(BranchEq, NegNode); // Get rhs elements BranchPtr = solver.GetRhsElement(BranchEq); }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="ckt"></param> public override void Temperature(Circuit ckt) { if (!ISRCdcValue.Given) { // no DC value - either have a transient value or none if (ISRCwaveform != null) { CircuitWarning.Warning(this, $"{Name} has no DC value, transient time 0 value used"); } else { CircuitWarning.Warning(this, $"{Name} has no value, DC 0 assumed"); } } double radians = ISRCacPhase * Circuit.CONSTPI / 180.0; ISRCac = new Complex(ISRCacMag * Math.Cos(radians), ISRCacMag * Math.Sin(radians)); }
/// <summary> /// Do temperature-dependent calculations /// </summary> /// <param name="ckt">The circuit</param> public override void Temperature(Circuit ckt) { // Calculate the voltage source's complex value if (!VSRCdcValue.Given) { // No DC value: either have a transient value or none if (VSRCwaveform.Given) { CircuitWarning.Warning(this, $"{Name}: No DC value, transient time 0 value used"); } else { CircuitWarning.Warning(this, $"{Name}: No value, DC 0 assumed"); } } double radians = VSRCacPhase * Circuit.CONSTPI / 180.0; VSRCac = new Complex(VSRCacMag * Math.Cos(radians), VSRCacMag * Math.Sin(radians)); }
/// <summary> /// Do temperature-dependent calculations /// </summary> void ITemperatureBehavior.Temperature() { if (!_mbp.NominalTemperature.Given) { _mbp.NominalTemperature.RawValue = ((BaseSimulation)Simulation).RealState.NominalTemperature; } VtNominal = Constants.KOverQ * _mbp.NominalTemperature; // limit grading coeff to max of 0.9 if (_mbp.GradingCoefficient > 0.9) { _mbp.GradingCoefficient.RawValue = 0.9; CircuitWarning.Warning(this, "{0}: grading coefficient too large, limited to 0.9".FormatString(Name)); } // limit activation energy to min of 0.1 if (_mbp.ActivationEnergy < 0.1) { _mbp.ActivationEnergy.RawValue = 0.1; CircuitWarning.Warning(this, "{0}: activation energy too small, limited to 0.1".FormatString(Name)); } // limit depletion cap coeff to max of .95 if (_mbp.DepletionCapCoefficient > 0.95) { _mbp.DepletionCapCoefficient.RawValue = 0.95; CircuitWarning.Warning(this, "{0}: coefficient Fc too large, limited to 0.95".FormatString(Name)); } if (_mbp.Resistance > 0) { Conductance = 1 / _mbp.Resistance; } else { Conductance = 0; } Xfc = Math.Log(1 - _mbp.DepletionCapCoefficient); F2 = Math.Exp((1 + _mbp.GradingCoefficient) * Xfc); F3 = 1 - _mbp.DepletionCapCoefficient * (1 + _mbp.GradingCoefficient); }
/// <summary> /// Setup behavior /// </summary> /// <param name="provider">Data provider</param> public override void Setup(SetupDataProvider provider) { if (provider == null) { throw new ArgumentNullException(nameof(provider)); } // Get parameters _bp = provider.GetParameterSet <BaseParameters>("entity"); // Give some warnings if no value is given if (!_bp.DcValue.Given) { // no DC value - either have a transient value or none CircuitWarning.Warning(this, _bp.Waveform != null ? "{0} has no DC value, transient time 0 value used".FormatString(Name) : "{0} has no value, DC 0 assumed".FormatString(Name)); } }
/// <summary> /// Setup behavior /// </summary> /// <param name="simulation">Simulation</param> /// <param name="provider">Data provider</param> public override void Setup(Simulation simulation, SetupDataProvider provider) { provider.ThrowIfNull(nameof(provider)); // Get parameters BaseParameters = provider.GetParameterSet <CommonBehaviors.IndependentSourceParameters>(); // Setup the waveform BaseParameters.Waveform?.Setup(); // Give some warnings if no value is given if (!BaseParameters.DcValue.Given) { // no DC value - either have a transient value or none CircuitWarning.Warning(this, BaseParameters.Waveform != null ? "{0} has no DC value, transient time 0 value used".FormatString(Name) : "{0} has no value, DC 0 assumed".FormatString(Name)); } }
/// <summary> /// Setup the behavior /// </summary> /// <param name="simulation">Simulation</param> /// <param name="provider">Data provider</param> public override void Setup(Simulation simulation, SetupDataProvider provider) { if (provider == null) { throw new ArgumentNullException(nameof(provider)); } // Get parameters _bp = provider.GetParameterSet <BaseParameters>(); // Setup the waveform _bp.Waveform?.Setup(); // Calculate the voltage source's complex value if (!_bp.DcValue.Given) { // No DC value: either have a transient value or none CircuitWarning.Warning(this, _bp.Waveform != null ? "{0}: No DC value, transient time 0 value used".FormatString(Name) : "{0}: No value, DC 0 assumed".FormatString(Name)); } }
/// <summary> /// Applies the maximum. /// </summary> /// <param name="arguments">The arguments.</param> /// <returns>The maximum result.</returns> public static Derivatives <Expression> ApplyMax(Derivatives <Expression>[] arguments) { arguments.ThrowIfEmpty(nameof(arguments)); var size = 1; for (var i = 0; i < arguments.Length; i++) { size = Math.Max(size, arguments[i].Count); } var result = new ExpressionTreeDerivatives(size); if (arguments.Length == 1) { result[0] = arguments[0][0]; return(result); } { // First two arguments var a = arguments[0][0]; var b = arguments[1][0]; result[0] = Expression.Call(MaxMethod, a, b); for (var k = 1; k < size; k++) { if (arguments[0][k] != null || arguments[1][k] != null) { CircuitWarning.Warning(null, "Trying to derive Min() for which the derivative may not exist in some points"); var tmpda = arguments[0][k]; var tmpdb = arguments[1][k]; var funca = arguments[0][0]; var funcb = arguments[1][0]; if (tmpda != null && tmpdb != null) { result[k] = Expression.Condition(Expression.GreaterThan(funca, funcb), tmpda, tmpdb); // Use the derivative of the function that is currently the smallest } else if (tmpda != null) { result[k] = Expression.Condition(Expression.GreaterThan(funca, funcb), tmpda, Expression.Constant(0.0)); } else if (tmpdb != null) { result[k] = Expression.Condition(Expression.GreaterThan(funca, funcb), Expression.Constant(0.0), tmpdb); } } } } for (var i = 2; i < arguments.Length; i++) { // First two arguments var a = result[0]; var b = arguments[i][0]; result[0] = Expression.Call(MaxMethod, a, b); for (var k = 1; k < size; k++) { if (arguments[0][k] != null || arguments[1][k] != null) { CircuitWarning.Warning(null, "Trying to derive Min() for which the derivative may not exist in some points"); var tmpda = result[k]; var tmpdb = arguments[i][k]; var funca = a; var funcb = arguments[i][0]; if (tmpda != null && tmpdb != null) { result[k] = Expression.Condition(Expression.GreaterThan(funca, funcb), tmpda, tmpdb); // Use the derivative of the function that is currently the smallest } else if (tmpda != null) { result[k] = Expression.Condition(Expression.GreaterThan(funca, funcb), tmpda, Expression.Constant(0.0)); } else if (tmpdb != null) { result[k] = Expression.Condition(Expression.GreaterThan(funca, funcb), Expression.Constant(0.0), tmpdb); } } } } return(result); }
/// <summary> /// Perform temperature-dependent calculations. /// </summary> /// <param name="simulation">The base simulation.</param> public void Temperature(BaseSimulation simulation) { // Update the width and length if they are not given and if the model specifies them if (!BaseParameters.Width.Given && ModelParameters.Width.Given) { BaseParameters.Width.RawValue = ModelParameters.Width.Value; } if (!BaseParameters.Length.Given && ModelParameters.Length.Given) { BaseParameters.Length.RawValue = ModelParameters.Length.Value; } if (!BaseParameters.Temperature.Given) { BaseParameters.Temperature.RawValue = simulation.RealState.Temperature; } Vt = BaseParameters.Temperature * Circuit.KOverQ; var ratio = BaseParameters.Temperature / ModelParameters.NominalTemperature; var fact2 = BaseParameters.Temperature / Circuit.ReferenceTemperature; var kt = BaseParameters.Temperature * Circuit.Boltzmann; var egfet = 1.16 - 7.02e-4 * BaseParameters.Temperature * BaseParameters.Temperature / (BaseParameters.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 (ModelParameters.DrainResistance.Given) { if (!ModelParameters.DrainResistance.Value.Equals(0.0)) { DrainConductance = 1 / ModelParameters.DrainResistance; } else { DrainConductance = 0; } } else if (ModelParameters.SheetResistance.Given) { if (!ModelParameters.SheetResistance.Value.Equals(0.0)) { DrainConductance = 1 / (ModelParameters.SheetResistance * BaseParameters.DrainSquares); } else { DrainConductance = 0; } } else { DrainConductance = 0; } if (ModelParameters.SourceResistance.Given) { if (!ModelParameters.SourceResistance.Value.Equals(0.0)) { SourceConductance = 1 / ModelParameters.SourceResistance; } else { SourceConductance = 0; } } else if (ModelParameters.SheetResistance.Given) { if (!ModelParameters.SheetResistance.Value.Equals(0.0)) { SourceConductance = 1 / (ModelParameters.SheetResistance * BaseParameters.SourceSquares); } else { SourceConductance = 0; } } else { SourceConductance = 0; } if (BaseParameters.Length - 2 * ModelParameters.LateralDiffusion <= 0) { CircuitWarning.Warning(this, "{0}: effective channel length less than zero".FormatString(Name)); } var ratio4 = ratio * Math.Sqrt(ratio); TempTransconductance = ModelParameters.Transconductance / ratio4; TempSurfaceMobility = ModelParameters.SurfaceMobility / ratio4; var phio = (ModelParameters.Phi - ModelTemperature.PbFactor1) / ModelTemperature.Factor1; TempPhi = fact2 * phio + pbfact; TempVoltageBi = ModelParameters.Vt0 - ModelParameters.MosfetType * (ModelParameters.Gamma * Math.Sqrt(ModelParameters.Phi)) + .5 * (ModelTemperature.EgFet1 - egfet) + ModelParameters.MosfetType * .5 * (TempPhi - ModelParameters.Phi); TempVt0 = TempVoltageBi + ModelParameters.MosfetType * ModelParameters.Gamma * Math.Sqrt(TempPhi); var tempSaturationCurrent = ModelParameters.JunctionSatCur * Math.Exp(-egfet / Vt + ModelTemperature.EgFet1 / ModelTemperature.VtNominal); var tempSaturationCurrentDensity = ModelParameters.JunctionSatCurDensity * Math.Exp(-egfet / Vt + ModelTemperature.EgFet1 / ModelTemperature.VtNominal); var pbo = (ModelParameters.BulkJunctionPotential - ModelTemperature.PbFactor1) / ModelTemperature.Factor1; TempBulkPotential = fact2 * pbo + pbfact; if (tempSaturationCurrentDensity <= 0 || BaseParameters.DrainArea.Value <= 0 || BaseParameters.SourceArea.Value <= 0) { SourceVCritical = DrainVCritical = Vt * Math.Log(Vt / (Circuit.Root2 * tempSaturationCurrent)); } else { DrainVCritical = Vt * Math.Log(Vt / (Circuit.Root2 * tempSaturationCurrentDensity * BaseParameters.DrainArea)); SourceVCritical = Vt * Math.Log(Vt / (Circuit.Root2 * tempSaturationCurrentDensity * BaseParameters.SourceArea)); } if (tempSaturationCurrentDensity.Equals(0) || BaseParameters.DrainArea.Value <= 0 || BaseParameters.SourceArea.Value <= 0) { DrainSatCurrent = tempSaturationCurrent; SourceSatCurrent = tempSaturationCurrent; } else { DrainSatCurrent = tempSaturationCurrentDensity * BaseParameters.DrainArea; SourceSatCurrent = tempSaturationCurrentDensity * BaseParameters.SourceArea; } }
/// <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 (_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; TempSurfMob = _mbp.SurfaceMobility / ratio4; var phio = (_mbp.Phi - _modeltemp.PbFactor1) / _modeltemp.Fact1; 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.Fact1; 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; 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; } }
/// <summary> /// Execute behaviour /// </summary> /// <param name="ckt"></param> public override void Noise(Circuit ckt) { var here = ComponentTyped <BSIM3v24>(); var model = here.Model as BSIM3v24Model; var state = ckt.State; var noise = state.Noise; BSIM3noise.Generators[BSIM3RDNOIZ].Set(here.BSIM3drainConductance); BSIM3noise.Generators[BSIM3RSNOIZ].Set(here.BSIM3sourceConductance); // Calculate shot noise factor switch (model.BSIM3noiMod.Value) { case 1.0: case 3.0: BSIM3noise.Generators[BSIM3IDNOIZ].Set(2.0 / 3.0 * Math.Abs(here.BSIM3gm + here.BSIM3gds + here.BSIM3gmbs)); break; case 2.0: case 4.0: BSIM3noise.Generators[BSIM3IDNOIZ].Set(here.BSIM3ueff * Math.Abs(here.BSIM3qinv) / (here.pParam.BSIM3leff * here.pParam.BSIM3leff + here.BSIM3ueff * Math.Abs(here.BSIM3qinv) * here.BSIM3rds)); break; default: CircuitWarning.Warning(model, $"Invalid noise model {model.BSIM3noiMod.Value}"); break; } // Calculate flicker noise factor switch (model.BSIM3noiMod.Value) { case 1.0: case 4.0: BSIM3noise.Generators[BSIM3FLNOIZ].Set(model.BSIM3kf * Math.Exp(model.BSIM3af * Math.Log(Math.Max(Math.Abs(here.BSIM3cd), 1e-38))) / (Math.Pow(noise.Freq, model.BSIM3ef) * here.pParam.BSIM3leff * here.pParam.BSIM3leff * model.BSIM3cox)); break; case 2.0: case 3.0: double vds = state.States[0][here.BSIM3states + BSIM3v24.BSIM3vds]; if (vds < 0.0) { vds = -vds; } double Ssi = StrongInversionNoiseEval(vds, model, here, noise.Freq, state.Temperature); double T10 = model.BSIM3oxideTrapDensityA * 8.62e-5 * state.Temperature; double T11 = here.pParam.BSIM3weff * here.pParam.BSIM3leff * Math.Pow(noise.Freq, model.BSIM3ef) * 4.0e36; double Swi = T10 / T11 * here.BSIM3cd * here.BSIM3cd; double T1 = Swi + Ssi; if (T1 > 0.0) { BSIM3noise.Generators[BSIM3FLNOIZ].Set((Ssi * Swi) / T1); } else { BSIM3noise.Generators[BSIM3FLNOIZ].Set(0.0); } break; } // Evaluate noise sources BSIM3noise.Evaluate(ckt); }
/// <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)); } var xcbv = 0.0; // loop through all the instances if (!BaseParameters.Temperature.Given) { BaseParameters.Temperature.RawValue = simulation.RealState.Temperature; } Vt = Circuit.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 / Circuit.ReferenceTemperature; var egfet = 1.16 - 7.02e-4 * BaseParameters.Temperature * BaseParameters.Temperature / (BaseParameters.Temperature + 1108); var arg = -egfet / (2 * Circuit.Boltzmann * BaseParameters.Temperature) + 1.1150877 / (Circuit.Boltzmann * (Circuit.ReferenceTemperature + Circuit.ReferenceTemperature)); var pbfact = -2 * Vt * (1.5 * Math.Log(fact2) + Circuit.Charge * arg); var egfet1 = 1.16 - 7.02e-4 * ModelParameters.NominalTemperature * ModelParameters.NominalTemperature / (ModelParameters.NominalTemperature + 1108); var arg1 = -egfet1 / (Circuit.Boltzmann * 2 * ModelParameters.NominalTemperature) + 1.1150877 / (2 * Circuit.Boltzmann * Circuit.ReferenceTemperature); var fact1 = ModelParameters.NominalTemperature / Circuit.ReferenceTemperature; var pbfact1 = -2 * ModelTemperature.VtNominal * (1.5 * Math.Log(fact1) + Circuit.Charge * arg1); var pbo = (ModelParameters.JunctionPotential - pbfact1) / fact1; var gmaold = (ModelParameters.JunctionPotential - pbo) / pbo; TempJunctionCap = ModelParameters.JunctionCap / (1 + ModelParameters.GradingCoefficient * (400e-6 * (ModelParameters.NominalTemperature - Circuit.ReferenceTemperature) - gmaold)); TempJunctionPot = pbfact + fact2 * pbo; var gmanew = (TempJunctionPot - pbo) / pbo; TempJunctionCap *= 1 + ModelParameters.GradingCoefficient * (400e-6 * (BaseParameters.Temperature - Circuit.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 / (Circuit.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> /// Perform parameter checking /// </summary> /// <param name="ckt"></param> /// <returns></returns> internal static bool BSIM3checkModel(this BSIM3v30 bsim3) { var model = bsim3.Model as BSIM3v30Model; bool Fatal_Flag = false; using (StreamWriter sw = new StreamWriter("b3v3check.log")) { sw.WriteLine("BSIM3v3.3.0 Parameter Checking."); if (model.BSIM3version != "3.3.0") { sw.WriteLine("Warning: This model is BSIM3v3.3.0; you specified a wrong version number."); CircuitWarning.Warning(bsim3, "Warning: This model is BSIM3v3.3.0; you specified a wrong version number."); } sw.WriteLine($"Model = {model.Name}"); if (bsim3.pParam.BSIM3nlx < -bsim3.pParam.BSIM3leff) { sw.WriteLine($"Fatal: Nlx = {bsim3.pParam.BSIM3nlx} is less than -Leff."); CircuitWarning.Warning(bsim3, $"Fatal: Nlx = {bsim3.pParam.BSIM3nlx} is less than -Leff."); Fatal_Flag = true; } if (model.BSIM3tox <= 0.0) { sw.WriteLine($"Fatal: Tox = {model.BSIM3tox} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Tox = {model.BSIM3tox} is not positive."); Fatal_Flag = true; } if (model.BSIM3toxm <= 0.0) { sw.WriteLine($"Fatal: Toxm = {model.BSIM3toxm} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Toxm = {model.BSIM3toxm} is not positive."); Fatal_Flag = true; } if (model.BSIM3lintnoi > bsim3.pParam.BSIM3leff / 2) { sw.WriteLine($"Fatal: Lintnoi = {model.BSIM3lintnoi} is too large - Leff for noise is negative."); CircuitWarning.Warning(bsim3, $"Fatal: Lintnoi = {model.BSIM3lintnoi} is too large - Leff for noise is negative."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3npeak <= 0.0) { sw.WriteLine($"Fatal: Nch = {bsim3.pParam.BSIM3npeak} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Nch = {bsim3.pParam.BSIM3npeak} is not positive."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3nsub <= 0.0) { sw.WriteLine($"Fatal: Nsub = {bsim3.pParam.BSIM3nsub} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Nsub = {bsim3.pParam.BSIM3nsub} is not positive."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3ngate < 0.0) { sw.WriteLine($"Fatal: Ngate = {bsim3.pParam.BSIM3ngate} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Ngate = {bsim3.pParam.BSIM3ngate} Ngate is not positive."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3ngate > 1.0e25) { sw.WriteLine($"Fatal: Ngate = {bsim3.pParam.BSIM3ngate} is too high."); CircuitWarning.Warning(bsim3, $"Fatal: Ngate = {bsim3.pParam.BSIM3ngate} Ngate is too high"); Fatal_Flag = true; } if (bsim3.pParam.BSIM3xj <= 0.0) { sw.WriteLine($"Fatal: Xj = {bsim3.pParam.BSIM3xj} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Xj = {bsim3.pParam.BSIM3xj} is not positive."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3dvt1 < 0.0) { sw.WriteLine($"Fatal: Dvt1 = {bsim3.pParam.BSIM3dvt1} is negative."); CircuitWarning.Warning(bsim3, $"Fatal: Dvt1 = {bsim3.pParam.BSIM3dvt1} is negative."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3dvt1w < 0.0) { sw.WriteLine($"Fatal: Dvt1w = {bsim3.pParam.BSIM3dvt1w} is negative."); CircuitWarning.Warning(bsim3, $"Fatal: Dvt1w = {bsim3.pParam.BSIM3dvt1w} is negative."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3w0 == -bsim3.pParam.BSIM3weff) { sw.WriteLine("Fatal: (W0 + Weff) = 0 causing divided-by-zero."); CircuitWarning.Warning(bsim3, "Fatal: (W0 + Weff) = 0 causing divided-by-zero."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3dsub < 0.0) { sw.WriteLine($"Fatal: Dsub = {bsim3.pParam.BSIM3dsub} is negative."); CircuitWarning.Warning(bsim3, $"Fatal: Dsub = {bsim3.pParam.BSIM3dsub} is negative."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3b1 == -bsim3.pParam.BSIM3weff) { sw.WriteLine("Fatal: (B1 + Weff) = 0 causing divided-by-zero."); CircuitWarning.Warning(bsim3, "Fatal: (B1 + Weff) = 0 causing divided-by-zero."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3u0temp <= 0.0) { sw.WriteLine($"Fatal: u0 at current temperature = {bsim3.pParam.BSIM3u0temp} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: u0 at current temperature = {bsim3.pParam.BSIM3u0temp} is not positive."); Fatal_Flag = true; } /* Check delta parameter */ if (bsim3.pParam.BSIM3delta < 0.0) { sw.WriteLine($"Fatal: Delta = {bsim3.pParam.BSIM3delta} is less than zero."); CircuitWarning.Warning(bsim3, $"Fatal: Delta = {bsim3.pParam.BSIM3delta} is less than zero."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3vsattemp <= 0.0) { sw.WriteLine($"Fatal: Vsat at current temperature = {bsim3.pParam.BSIM3vsattemp} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Vsat at current temperature = {bsim3.pParam.BSIM3vsattemp} is not positive."); Fatal_Flag = true; } /* Check Rout parameters */ if (bsim3.pParam.BSIM3pclm <= 0.0) { sw.WriteLine($"Fatal: Pclm = {bsim3.pParam.BSIM3pclm} is not positive."); CircuitWarning.Warning(bsim3, $"Fatal: Pclm = {bsim3.pParam.BSIM3pclm} is not positive."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3drout < 0.0) { sw.WriteLine($"Fatal: Drout = {bsim3.pParam.BSIM3drout} is negative."); CircuitWarning.Warning(bsim3, $"Fatal: Drout = {bsim3.pParam.BSIM3drout} is negative."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3pscbe2 <= 0.0) { sw.WriteLine($"Warning: Pscbe2 = {bsim3.pParam.BSIM3pscbe2} is not positive."); CircuitWarning.Warning(bsim3, $"Warning: Pscbe2 = {bsim3.pParam.BSIM3pscbe2} is not positive."); } if (model.BSIM3unitLengthSidewallJctCap > 0.0 || model.BSIM3unitLengthGateSidewallJctCap > 0.0) { if (bsim3.BSIM3drainPerimeter < bsim3.pParam.BSIM3weff) { sw.WriteLine($"Warning: Pd = {bsim3.BSIM3drainPerimeter} is less than W."); CircuitWarning.Warning(bsim3, $"Warning: Pd = {bsim3.BSIM3drainPerimeter} is less than W."); } if (bsim3.BSIM3sourcePerimeter < bsim3.pParam.BSIM3weff) { sw.WriteLine($"Warning: Ps = {bsim3.BSIM3sourcePerimeter} is less than W."); CircuitWarning.Warning(bsim3, $"Warning: Ps = {bsim3.BSIM3sourcePerimeter} is less than W."); } } if (bsim3.pParam.BSIM3noff < 0.1) { sw.WriteLine($"Warning: Noff = {bsim3.pParam.BSIM3noff} is too small."); CircuitWarning.Warning(bsim3, $"Warning: Noff = {bsim3.pParam.BSIM3noff} is too small."); } if (bsim3.pParam.BSIM3noff > 4.0) { sw.WriteLine($"Warning: Noff = {bsim3.pParam.BSIM3noff} is too large."); CircuitWarning.Warning(bsim3, $"Warning: Noff = {bsim3.pParam.BSIM3noff} is too large."); } if (bsim3.pParam.BSIM3voffcv < -0.5) { sw.WriteLine($"Warning: Voffcv = {bsim3.pParam.BSIM3voffcv} is too small."); CircuitWarning.Warning(bsim3, $"Warning: Voffcv = {bsim3.pParam.BSIM3voffcv} is too small."); } if (bsim3.pParam.BSIM3voffcv > 0.5) { sw.WriteLine($"Warning: Voffcv = {bsim3.pParam.BSIM3voffcv} is too large."); CircuitWarning.Warning(bsim3, $"Warning: Voffcv = {bsim3.pParam.BSIM3voffcv} is too large."); } if (model.BSIM3ijth < 0.0) { sw.WriteLine($"Fatal: Ijth = {model.BSIM3ijth} cannot be negative."); CircuitWarning.Warning(bsim3, $"Fatal: Ijth = {model.BSIM3ijth} cannot be negative."); Fatal_Flag = true; } /* Check capacitance parameters */ if (bsim3.pParam.BSIM3clc < 0.0) { sw.WriteLine($"Fatal: Clc = {bsim3.pParam.BSIM3clc} is negative."); CircuitWarning.Warning(bsim3, $"Fatal: Clc = {bsim3.pParam.BSIM3clc} is negative."); Fatal_Flag = true; } if (bsim3.pParam.BSIM3moin < 5.0) { sw.WriteLine($"Warning: Moin = {bsim3.pParam.BSIM3moin} is too small."); CircuitWarning.Warning(bsim3, $"Warning: Moin = {bsim3.pParam.BSIM3moin} is too small."); } if (bsim3.pParam.BSIM3moin > 25.0) { sw.WriteLine($"Warning: Moin = {bsim3.pParam.BSIM3moin} is too large."); CircuitWarning.Warning(bsim3, $"Warning: Moin = {bsim3.pParam.BSIM3moin} is too large."); } if (model.BSIM3capMod == 3) { if (bsim3.pParam.BSIM3acde < 0.4) { sw.WriteLine($"Warning: Acde = {bsim3.pParam.BSIM3acde} is too small."); CircuitWarning.Warning(bsim3, $"Warning: Acde = {bsim3.pParam.BSIM3acde} is too small."); } if (bsim3.pParam.BSIM3acde > 1.6) { sw.WriteLine($"Warning: Acde = {bsim3.pParam.BSIM3acde} is too large."); CircuitWarning.Warning(bsim3, $"Warning: Acde = {bsim3.pParam.BSIM3acde} is too large."); } } if (model.BSIM3paramChk == 1) { /* Check L and W parameters */ if (bsim3.pParam.BSIM3leff <= 5.0e-8) { sw.WriteLine($"Warning: Leff = {bsim3.pParam.BSIM3leff} may be too small."); CircuitWarning.Warning(bsim3, $"Warning: Leff = {bsim3.pParam.BSIM3leff} may be too small."); } if (bsim3.pParam.BSIM3leffCV <= 5.0e-8) { sw.WriteLine($"Warning: Leff for CV = {bsim3.pParam.BSIM3leffCV} may be too small."); CircuitWarning.Warning(bsim3, $"Warning: Leff for CV = {bsim3.pParam.BSIM3leffCV} may be too small."); } if (bsim3.pParam.BSIM3weff <= 1.0e-7) { sw.WriteLine($"Warning: Weff = {bsim3.pParam.BSIM3weff} may be too small."); CircuitWarning.Warning(bsim3, $"Warning: Weff = {bsim3.pParam.BSIM3weff} may be too small."); } if (bsim3.pParam.BSIM3weffCV <= 1.0e-7) { sw.WriteLine($"Warning: Weff for CV = {bsim3.pParam.BSIM3weffCV} may be too small."); CircuitWarning.Warning(bsim3, $"Warning: Weff for CV = {bsim3.pParam.BSIM3weffCV} may be too small."); } /* Check threshold voltage parameters */ if (bsim3.pParam.BSIM3nlx < 0.0) { sw.WriteLine($"Warning: Nlx = {bsim3.pParam.BSIM3nlx} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Nlx = {bsim3.pParam.BSIM3nlx} is negative."); } if (model.BSIM3tox < 1.0e-9) { sw.WriteLine($"Warning: Tox = {model.BSIM3tox} is less than 10A."); CircuitWarning.Warning(bsim3, $"Warning: Tox = {model.BSIM3tox} is less than 10A."); } if (bsim3.pParam.BSIM3npeak <= 1.0e15) { sw.WriteLine($"Warning: Nch = {bsim3.pParam.BSIM3npeak} may be too small."); CircuitWarning.Warning(bsim3, $"Warning: Nch = {bsim3.pParam.BSIM3npeak} may be too small."); } else if (bsim3.pParam.BSIM3npeak >= 1.0e21) { sw.WriteLine($"Warning: Nch = {bsim3.pParam.BSIM3npeak} may be too large."); CircuitWarning.Warning(bsim3, $"Warning: Nch = {bsim3.pParam.BSIM3npeak} may be too large."); } if (bsim3.pParam.BSIM3nsub <= 1.0e14) { sw.WriteLine($"Warning: Nsub = {bsim3.pParam.BSIM3nsub} may be too small."); CircuitWarning.Warning(bsim3, $"Warning: Nsub = {bsim3.pParam.BSIM3nsub} may be too small."); } else if (bsim3.pParam.BSIM3nsub >= 1.0e21) { sw.WriteLine($"Warning: Nsub = {bsim3.pParam.BSIM3nsub} may be too large."); CircuitWarning.Warning(bsim3, $"Warning: Nsub = {bsim3.pParam.BSIM3nsub} may be too large."); } if ((bsim3.pParam.BSIM3ngate > 0.0) && (bsim3.pParam.BSIM3ngate <= 1.0e18)) { sw.WriteLine($"Warning: Ngate = {bsim3.pParam.BSIM3ngate} is less than 1.E18cm^-3."); CircuitWarning.Warning(bsim3, $"Warning: Ngate = {bsim3.pParam.BSIM3ngate} is less than 1.E18cm^-3."); } if (bsim3.pParam.BSIM3dvt0 < 0.0) { sw.WriteLine($"Warning: Dvt0 = {bsim3.pParam.BSIM3dvt0} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Dvt0 = {bsim3.pParam.BSIM3dvt0} is negative."); } if (Math.Abs(1.0e-6 / (bsim3.pParam.BSIM3w0 + bsim3.pParam.BSIM3weff)) > 10.0) { sw.WriteLine("Warning: (W0 + Weff) may be too small."); CircuitWarning.Warning(bsim3, "Warning: (W0 + Weff) may be too small."); } /* Check subthreshold parameters */ if (bsim3.pParam.BSIM3nfactor < 0.0) { sw.WriteLine($"Warning: Nfactor = {bsim3.pParam.BSIM3nfactor} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Nfactor = {bsim3.pParam.BSIM3nfactor} is negative."); } if (bsim3.pParam.BSIM3cdsc < 0.0) { sw.WriteLine($"Warning: Cdsc = {bsim3.pParam.BSIM3cdsc} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Cdsc = {bsim3.pParam.BSIM3cdsc} is negative."); } if (bsim3.pParam.BSIM3cdscd < 0.0) { sw.WriteLine($"Warning: Cdscd = {bsim3.pParam.BSIM3cdscd} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Cdscd = {bsim3.pParam.BSIM3cdscd} is negative."); } /* Check DIBL parameters */ if (bsim3.pParam.BSIM3eta0 < 0.0) { sw.WriteLine($"Warning: Eta0 = {bsim3.pParam.BSIM3eta0} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Eta0 = {bsim3.pParam.BSIM3eta0} is negative."); } /* Check Abulk parameters */ if (Math.Abs(1.0e-6 / (bsim3.pParam.BSIM3b1 + bsim3.pParam.BSIM3weff)) > 10.0) { sw.WriteLine("Warning: (B1 + Weff) may be too small."); CircuitWarning.Warning(bsim3, "Warning: (B1 + Weff) may be too small."); } /* Check Saturation parameters */ if (bsim3.pParam.BSIM3a2 < 0.01) { sw.WriteLine($"Warning: A2 = {bsim3.pParam.BSIM3a2} is too small. Set to 0.01."); CircuitWarning.Warning(bsim3, $"Warning: A2 = {bsim3.pParam.BSIM3a2} is too small. Set to 0.01."); bsim3.pParam.BSIM3a2 = 0.01; } else if (bsim3.pParam.BSIM3a2 > 1.0) { sw.WriteLine($"Warning: A2 = {bsim3.pParam.BSIM3a2} is larger than 1. A2 is set to 1 and A1 is set to 0."); CircuitWarning.Warning(bsim3, $"Warning: A2 = {bsim3.pParam.BSIM3a2} is larger than 1. A2 is set to 1 and A1 is set to 0."); bsim3.pParam.BSIM3a2 = 1.0; bsim3.pParam.BSIM3a1 = 0.0; } if (bsim3.pParam.BSIM3rdsw < 0.0) { sw.WriteLine($"Warning: Rdsw = {bsim3.pParam.BSIM3rdsw} is negative. Set to zero."); CircuitWarning.Warning(bsim3, $"Warning: Rdsw = {bsim3.pParam.BSIM3rdsw} is negative. Set to zero."); bsim3.pParam.BSIM3rdsw = 0.0; bsim3.pParam.BSIM3rds0 = 0.0; } if (bsim3.pParam.BSIM3rds0 < 0.0) { sw.WriteLine($"Warning: Rds at current temperature = {bsim3.pParam.BSIM3rds0} is negative. Set to zero."); CircuitWarning.Warning(bsim3, $"Warning: Rds at current temperature = {bsim3.pParam.BSIM3rds0} is negative. Set to zero."); bsim3.pParam.BSIM3rds0 = 0.0; } if (bsim3.pParam.BSIM3vsattemp < 1.0e3) { sw.WriteLine($"Warning: Vsat at current temperature = {bsim3.pParam.BSIM3vsattemp} may be too small."); CircuitWarning.Warning(bsim3, $"Warning: Vsat at current temperature = {bsim3.pParam.BSIM3vsattemp} may be too small."); } if (bsim3.pParam.BSIM3pdibl1 < 0.0) { sw.WriteLine($"Warning: Pdibl1 = {bsim3.pParam.BSIM3pdibl1} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Pdibl1 = {bsim3.pParam.BSIM3pdibl1} is negative."); } if (bsim3.pParam.BSIM3pdibl2 < 0.0) { sw.WriteLine($"Warning: Pdibl2 = {bsim3.pParam.BSIM3pdibl2} is negative."); CircuitWarning.Warning(bsim3, $"Warning: Pdibl2 = {bsim3.pParam.BSIM3pdibl2} is negative."); } /* Check overlap capacitance parameters */ if (model.BSIM3cgdo < 0.0) { sw.WriteLine($"Warning: cgdo = {model.BSIM3cgdo} is negative. Set to zero."); CircuitWarning.Warning(bsim3, $"Warning: cgdo = {model.BSIM3cgdo} is negative. Set to zero."); model.BSIM3cgdo.Value = 0.0; } if (model.BSIM3cgso < 0.0) { sw.WriteLine($"Warning: cgso = {model.BSIM3cgso} is negative. Set to zero."); CircuitWarning.Warning(bsim3, $"Warning: cgso = {model.BSIM3cgso} is negative. Set to zero."); model.BSIM3cgso.Value = 0.0; } if (model.BSIM3cgbo < 0.0) { sw.WriteLine($"Warning: cgbo = {model.BSIM3cgbo} is negative. Set to zero."); CircuitWarning.Warning(bsim3, $"Warning: cgbo = {model.BSIM3cgbo} is negative. Set to zero."); model.BSIM3cgbo.Value = 0.0; } }/* loop for the parameter check for warning messages */ } return(Fatal_Flag); }