/// <summary> /// Transient behavior /// </summary> /// <param name="simulation"></param> public override void Transient(TimeSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } var state = simulation.RealState; double sargsw; var vbs = _load.VoltageBs; var vbd = _load.VoltageBd; var vgs = _load.VoltageGs; var vgd = _load.VoltageGs - _load.VoltageDs; var vgb = _load.VoltageGs - _load.VoltageBs; var von = _mbp.MosfetType * _load.Von; var vdsat = _mbp.MosfetType * _load.SaturationVoltageDs; VoltageDs.Current = _load.VoltageDs; VoltageBs.Current = vbs; VoltageGs.Current = vgs; double gbd = 0.0; double cbd = 0.0; double gbs = 0.0; double cbs = 0.0; var effectiveLength = _bp.Length - 2 * _mbp.LateralDiffusion; var gateSourceOverlapCap = _mbp.GateSourceOverlapCapFactor * _bp.Width; var gateDrainOverlapCap = _mbp.GateDrainOverlapCapFactor * _bp.Width; var gateBulkOverlapCap = _mbp.GateBulkOverlapCapFactor * effectiveLength; var oxideCap = _modeltemp.OxideCapFactor * effectiveLength * _bp.Width; /* * now we do the hard part of the bulk - drain and bulk - source * diode - we evaluate the non - linear capacitance and * charge * * the basic equations are not hard, but the implementation * is somewhat long in an attempt to avoid log / exponential * evaluations */ /* * charge storage elements * * .. bulk - drain and bulk - source depletion capacitances */ if (vbs < _temp.TempDepletionCap) { double arg = 1 - vbs / _temp.TempBulkPotential, sarg; /* * the following block looks somewhat long and messy, * but since most users use the default grading * coefficients of .5, and sqrt is MUCH faster than an * Math.Exp(Math.Log()) we use this special case code to buy time. * (as much as 10% of total job time!) */ if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(_mbp.BulkJunctionSideGradingCoefficient.Value)) { if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5)) { sarg = sargsw = 1 / Math.Sqrt(arg); } else { sarg = sargsw = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); } } else { if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5)) { sarg = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sarg = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); } if (_mbp.BulkJunctionSideGradingCoefficient.Value.Equals(0.5)) { sargsw = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sargsw = Math.Exp(-_mbp.BulkJunctionSideGradingCoefficient * Math.Log(arg)); } } // NOSQRT ChargeBs.Current = _temp.TempBulkPotential * (_temp.CapBs * (1 - arg * sarg) / (1 - _mbp.BulkJunctionBotGradingCoefficient) + _temp.CapBsSidewall * (1 - arg * sargsw) / (1 - _mbp.BulkJunctionSideGradingCoefficient)); CapBs = _temp.CapBs * sarg + _temp.CapBsSidewall * sargsw; } else { ChargeBs.Current = _temp.F4S + vbs * (_temp.F2S + vbs * (_temp.F3S / 2)); CapBs = _temp.F2S + _temp.F3S * vbs; } if (vbd < _temp.TempDepletionCap) { double arg = 1 - vbd / _temp.TempBulkPotential, sarg; /* * the following block looks somewhat long and messy, * but since most users use the default grading * coefficients of .5, and sqrt is MUCH faster than an * Math.Exp(Math.Log()) we use this special case code to buy time. * (as much as 10% of total job time!) */ if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5) && _mbp.BulkJunctionSideGradingCoefficient.Value.Equals(0.5)) { sarg = sargsw = 1 / Math.Sqrt(arg); } else { if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5)) { sarg = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sarg = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); } if (_mbp.BulkJunctionSideGradingCoefficient.Value.Equals(0.5)) { sargsw = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sargsw = Math.Exp(-_mbp.BulkJunctionSideGradingCoefficient * Math.Log(arg)); } } /* NOSQRT */ ChargeBd.Current = _temp.TempBulkPotential * (_temp.CapBd * (1 - arg * sarg) / (1 - _mbp.BulkJunctionBotGradingCoefficient) + _temp.CapBdSidewall * (1 - arg * sargsw) / (1 - _mbp.BulkJunctionSideGradingCoefficient)); CapBd = _temp.CapBd * sarg + _temp.CapBdSidewall * sargsw; } else { ChargeBd.Current = _temp.F4D + vbd * (_temp.F2D + vbd * _temp.F3D / 2); CapBd = _temp.F2D + vbd * _temp.F3D; } /* (above only excludes tranop, since we're only at this * point if tran or tranop) */ /* * calculate equivalent conductances and currents for * depletion capacitors */ // integrate the capacitors and save results ChargeBd.Integrate(); gbd += ChargeBd.Jacobian(CapBd); cbd += ChargeBd.Derivative; ChargeBs.Integrate(); gbs += ChargeBs.Jacobian(CapBs); cbs += ChargeBs.Derivative; /* * calculate meyer's capacitors */ double icapgs, icapgd, icapgb; if (_load.Mode > 0) { Transistor.MeyerCharges(vgs, vgd, von, vdsat, out icapgs, out icapgd, out icapgb, _temp.TempPhi, oxideCap); } else { Transistor.MeyerCharges(vgd, vgs, von, vdsat, out icapgd, out icapgs, out icapgb, _temp.TempPhi, oxideCap); } CapGs.Current = icapgs; CapGd.Current = icapgd; CapGb.Current = icapgb; var vgs1 = VoltageGs[1]; var vgd1 = vgs1 - VoltageDs[1]; var vgb1 = vgs1 - VoltageBs[1]; var capgs = CapGs.Current + CapGs[1] + gateSourceOverlapCap; var capgd = CapGd.Current + CapGd[1] + gateDrainOverlapCap; var capgb = CapGb.Current + CapGb[1] + gateBulkOverlapCap; /* * store small - signal parameters (for meyer's model) * all parameters already stored, so done... */ ChargeGs.Current = (vgs - vgs1) * capgs + ChargeGs[1]; ChargeGd.Current = (vgd - vgd1) * capgd + ChargeGd[1]; ChargeGb.Current = (vgb - vgb1) * capgb + ChargeGb[1]; /* * calculate equivalent conductances and currents for * meyer"s capacitors */ ChargeGs.Integrate(); var gcgs = ChargeGs.Jacobian(capgs); var ceqgs = ChargeGs.RhsCurrent(gcgs, vgs); ChargeGd.Integrate(); var gcgd = ChargeGd.Jacobian(capgd); var ceqgd = ChargeGd.RhsCurrent(gcgd, vgd); ChargeGb.Integrate(); var gcgb = ChargeGb.Jacobian(capgb); var ceqgb = ChargeGb.RhsCurrent(gcgb, vgb); /* * store charge storage info for meyer's cap in lx table */ // Load current vector var ceqbs = _mbp.MosfetType * (cbs - (gbs - state.Gmin) * vbs); var ceqbd = _mbp.MosfetType * (cbd - (gbd - state.Gmin) * vbd); GatePtr.Value -= _mbp.MosfetType * (ceqgs + ceqgb + ceqgd); BulkPtr.Value -= ceqbs + ceqbd - _mbp.MosfetType * ceqgb; DrainPrimePtr.Value += ceqbd + _mbp.MosfetType * ceqgd; SourcePrimePtr.Value += ceqbs + _mbp.MosfetType * ceqgs; // load Y-matrix GateGatePtr.Value += gcgd + gcgs + gcgb; BulkBulkPtr.Value += gbd + gbs + gcgb; DrainPrimeDrainPrimePtr.Value += gbd + gcgd; SourcePrimeSourcePrimePtr.Value += gbs + gcgs; GateBulkPtr.Value -= gcgb; GateDrainPrimePtr.Value -= gcgd; GateSourcePrimePtr.Value -= gcgs; BulkGatePtr.Value -= gcgb; BulkDrainPrimePtr.Value -= gbd; BulkSourcePrimePtr.Value -= gbs; DrainPrimeGatePtr.Value += -gcgd; DrainPrimeBulkPtr.Value += -gbd; SourcePrimeGatePtr.Value += -gcgs; SourcePrimeBulkPtr.Value += -gbs; }
/// <summary> /// Transient behavior /// </summary> /// <param name="simulation">Time-based simulation</param> public override void Transient(TimeSimulation simulation) { if (simulation == null) { throw new ArgumentNullException(nameof(simulation)); } double arg, sarg, sargsw; // Get voltages var vbd = _load.VoltageBd; var vbs = _load.VoltageBs; var vgs = _load.VoltageGs; var vds = _load.VoltageDs; var vgd = vgs - vds; var vgb = vgs - vbs; var effectiveLength = _bp.Length - 2 * _mbp.LateralDiffusion; var gateSourceOverlapCap = _mbp.GateSourceOverlapCapFactor * _bp.Width; var gateDrainOverlapCap = _mbp.GateDrainOverlapCapFactor * _bp.Width; var gateBulkOverlapCap = _mbp.GateBulkOverlapCapFactor * effectiveLength; var oxideCap = _modeltemp.OxideCapFactor * effectiveLength * _bp.Width; var gbd = 0.0; var cbd = 0.0; var gbs = 0.0; var cbs = 0.0; // Store these voltages VoltageGs.Current = vgs; VoltageDs.Current = vds; VoltageBs.Current = vbs; /* * now we do the hard part of the bulk - drain and bulk - source * diode - we evaluate the non - linear capacitance and * charge * * the basic equations are not hard, but the implementation * is somewhat long in an attempt to avoid log / exponential * evaluations */ /* * charge storage elements * * .. bulk - drain and bulk - source depletion capacitances */ /* CAPBYPASS */ /* can't bypass the diode capacitance calculations */ /* CAPZEROBYPASS */ if (vbs < _temp.TempDepletionCap) { arg = 1 - vbs / _temp.TempBulkPotential; /* * the following block looks somewhat long and messy, * but since most users use the default grading * coefficients of .5, and sqrt is MUCH faster than an * Math.Exp(Math.Log()) we use this special case code to buy time. * (as much as 10% of total job time!) */ if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(_mbp.BulkJunctionSideGradingCoefficient.Value)) { if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5)) { sarg = sargsw = 1 / Math.Sqrt(arg); } else { sarg = sargsw = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); } } else { if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5)) { sarg = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sarg = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); } if (_mbp.BulkJunctionSideGradingCoefficient.Value.Equals(0.5)) { sargsw = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sargsw = Math.Exp(-_mbp.BulkJunctionSideGradingCoefficient * Math.Log(arg)); } } /* NOSQRT */ ChargeBs.Current = _temp.TempBulkPotential * (_temp.CapBs * (1 - arg * sarg) / (1 - _mbp.BulkJunctionBotGradingCoefficient) + _temp.CapBsSidewall * (1 - arg * sargsw) / (1 - _mbp.BulkJunctionSideGradingCoefficient)); CapBs = _temp.CapBs * sarg + _temp.CapBsSidewall * sargsw; } else { ChargeBs.Current = _temp.F4S + vbs * (_temp.F2S + vbs * (_temp.F3S / 2)); CapBs = _temp.F2S + _temp.F3S * vbs; } /* can't bypass the diode capacitance calculations */ /* CAPZEROBYPASS */ if (vbd < _temp.TempDepletionCap) { arg = 1 - vbd / _temp.TempBulkPotential; /* * the following block looks somewhat long and messy, * but since most users use the default grading * coefficients of .5, and sqrt is MUCH faster than an * Math.Exp(Math.Log()) we use this special case code to buy time. * (as much as 10% of total job time!) */ if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5) && _mbp.BulkJunctionSideGradingCoefficient.Value.Equals(0.5)) { sarg = sargsw = 1 / Math.Sqrt(arg); } else { if (_mbp.BulkJunctionBotGradingCoefficient.Value.Equals(0.5)) { sarg = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sarg = Math.Exp(-_mbp.BulkJunctionBotGradingCoefficient * Math.Log(arg)); } if (_mbp.BulkJunctionSideGradingCoefficient.Value.Equals(0.5)) { sargsw = 1 / Math.Sqrt(arg); } else { /* NOSQRT */ sargsw = Math.Exp(-_mbp.BulkJunctionSideGradingCoefficient * Math.Log(arg)); } } /* NOSQRT */ ChargeBd.Current = _temp.TempBulkPotential * (_temp.CapBd * (1 - arg * sarg) / (1 - _mbp.BulkJunctionBotGradingCoefficient) + _temp.CapBdSidewall * (1 - arg * sargsw) / (1 - _mbp.BulkJunctionSideGradingCoefficient)); CapBd = _temp.CapBd * sarg + _temp.CapBdSidewall * sargsw; } else { ChargeBd.Current = _temp.F4D + vbd * (_temp.F2D + vbd * _temp.F3D / 2); CapBd = _temp.F2D + vbd * _temp.F3D; } // integrate the capacitors and save results ChargeBd.Integrate(); gbd += ChargeBd.Jacobian(CapBd); cbd += ChargeBd.Derivative; // NOTE: The derivative of Qbd should be added to Cd (drain current). Figure out a way later. ChargeBs.Integrate(); gbs += ChargeBs.Jacobian(CapBs); cbs += ChargeBs.Derivative; /* * calculate meyer's capacitors */ /* * new cmeyer - this just evaluates at the current time, * expects you to remember values from previous time * returns 1 / 2 of non-constant portion of capacitance * you must add in the other half from previous time * and the constant part */ double icapgs, icapgd, icapgb; if (_load.Mode > 0) { Transistor.MeyerCharges(vgs, vgd, _mbp.MosfetType * _load.Von, _mbp.MosfetType * _load.SaturationVoltageDs, out icapgs, out icapgd, out icapgb, _temp.TempPhi, oxideCap); } else { Transistor.MeyerCharges(vgd, vgs, _mbp.MosfetType * _load.Von, _mbp.MosfetType * _load.SaturationVoltageDs, out icapgd, out icapgs, out icapgb, _temp.TempPhi, oxideCap); } CapGs.Current = icapgs; CapGd.Current = icapgd; CapGb.Current = icapgb; var vgs1 = VoltageGs[1]; var vgd1 = vgs1 - VoltageDs[1]; var vgb1 = vgs1 - VoltageBs[1]; var capgs = CapGs.Current + CapGs[1] + gateSourceOverlapCap; var capgd = CapGd.Current + CapGd[1] + gateDrainOverlapCap; var capgb = CapGb.Current + CapGb[1] + gateBulkOverlapCap; ChargeGs.Current = (vgs - vgs1) * capgs + ChargeGs[1]; ChargeGd.Current = (vgd - vgd1) * capgd + ChargeGd[1]; ChargeGb.Current = (vgb - vgb1) * capgb + ChargeGb[1]; /* NOTE: We can't reset derivatives! * if (capgs == 0) * state.States[0][States + Cqgs] = 0; * if (capgd == 0) * state.States[0][States + Cqgd] = 0; * if (capgb == 0) * state.States[0][States + Cqgb] = 0; */ /* NOTE: The formula with the method.Slope is to make it work for nonlinear capacitances! * The correct formula is: ceq = dQ/dt - geq * vq where geq = slope * dQ/dvq * The formula in Spice 3f5 is: ceq = dQ/dt - slope * Q where it assumes a linear capacitance * method.Integrate(state, out gcgs, out ceqgs, States + Qgs, capgs); * method.Integrate(state, out gcgd, out ceqgd, States + Qgd, capgd); * method.Integrate(state, out gcgb, out ceqgb, States + Qgb, capgb); * ceqgs = ceqgs - gcgs * vgs + method.Slope * state.States[0][States + Qgs]; * ceqgd = ceqgd - gcgd * vgd + method.Slope * state.States[0][States + Qgd]; * ceqgb = ceqgb - gcgb * vgb + method.Slope * state.States[0][States + Qgb]; */ ChargeGs.Integrate(); var gcgs = ChargeGs.Jacobian(capgs); var ceqgs = ChargeGs.RhsCurrent(gcgs, vgs); ChargeGd.Integrate(); var gcgd = ChargeGd.Jacobian(capgd); var ceqgd = ChargeGd.RhsCurrent(gcgd, vgd); ChargeGb.Integrate(); var gcgb = ChargeGb.Jacobian(capgb); var ceqgb = ChargeGb.RhsCurrent(gcgb, vgb); // Load current vector var ceqbs = _mbp.MosfetType * (cbs - gbs * vbs); var ceqbd = _mbp.MosfetType * (cbd - gbd * vbd); GatePtr.Value -= _mbp.MosfetType * (ceqgs + ceqgb + ceqgd); BulkPtr.Value -= ceqbs + ceqbd - _mbp.MosfetType * ceqgb; DrainPrimePtr.Value += ceqbd + _mbp.MosfetType * ceqgd; SourcePrimePtr.Value += ceqbs + _mbp.MosfetType * ceqgs; // Load Y-matrix GateGatePtr.Value += gcgd + gcgs + gcgb; BulkBulkPtr.Value += gbd + gbs + gcgb; DrainPrimeDrainPrimePtr.Value += gbd + gcgd; SourcePrimeSourcePrimePtr.Value += gbs + gcgs; GateBulkPtr.Value -= gcgb; GateDrainPrimePtr.Value -= gcgd; GateSourcePrimePtr.Value -= gcgs; BulkGatePtr.Value -= gcgb; BulkDrainPrimePtr.Value -= gbd; BulkSourcePrimePtr.Value -= gbs; DrainPrimeGatePtr.Value -= gcgd; DrainPrimeBulkPtr.Value -= gbd; SourcePrimeGatePtr.Value -= gcgs; SourcePrimeBulkPtr.Value -= gbs; }