/// <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 == 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 */ 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; } }
/// <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 = _modeltemp.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 == 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 */ /* 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 */ } else { /* DETAILPROF */ /* 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; // TODO: 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; } } /* 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 */ { /* * subroutine moseq3(vds, vbs, vgs, gm, gds, gmbs, * qg, qc, qb, cggb, cgdb, cgsb, cbgb, cbdb, cbsb) */ /* * this routine evaluates the drain current, its derivatives and * the charges associated with the gate, channel and bulk * for mosfets based on semi - empirical equations */ /* * common / mosarg / vto, beta, gamma, phi, phib, cox, xnsub, xnfs, xd, xj, xld, * 1 xlamda, uo, uexp, vbp, utra, vmax, xneff, xl, xw, vbi, von, vdsat, qspof, * 2 beta0, beta1, cdrain, xqco, xqc, fnarrw, fshort, lev * common / status / omega, time, delta, delold(7), ag(7), vt, xni, egfet, * 1 xmu, sfactr, mode, modedc, icalc, initf, method, iord, maxord, noncon, * 2 iterno, itemno, nosolv, modac, ipiv, ivmflg, ipostp, iscrch, iofile * common / knstnt / twopi, xlog2, xlog10, root2, rad, boltz, charge, ctok, * 1 gmin, reltol, abstol, vntol, trtol, chgtol, eps0, epssil, epsox, * 2 pivtol, pivrel */ /* equivalence (xlamda, alpha), (vbp, theta), (uexp, eta), (utra, xkappa) */ double coeff0 = 0.0631353e0; double coeff1 = 0.8013292e0; double coeff2 = -0.01110777e0; double phibs; /* phi - vbs */ double sqphbs; /* square root of phibs */ double dsqdvb; /* */ double arga, argb, argc; double dfsdvb; double dxndvb = 0.0, dvodvb = 0.0, dvodvd = 0.0, dvsdvg, dvsdvb, dvsdvd, xn = 0.0; double onvdsc = 0.0; double fdrain = 0.0; double dfddvg = 0.0, dfddvb = 0.0, dfddvd = 0.0, delxl, dldvd, ddldvg, ddldvd, ddldvb, gds0 = 0.0; double fshort; /* * bypasses the computation of charges */ /* * reference cdrain equations to source and * charge equations to bulk */ vdsat = 0.0; var oneoverxl = 1.0 / effectiveLength; var eta = _mbp.Eta * 8.15e-22 / (_modeltemp.OxideCapFactor * effectiveLength * effectiveLength * effectiveLength); /* * .....square root term */ if ((Mode == 1 ? vbs : vbd) <= 0.0) { phibs = _temp.TempPhi - (Mode == 1 ? vbs : vbd); sqphbs = Math.Sqrt(phibs); dsqdvb = -0.5 / sqphbs; } else { var sqphis = Math.Sqrt(_temp.TempPhi); /* square root of phi */ var sqphs3 = _temp.TempPhi * sqphis; /* square root of phi cubed */ sqphbs = sqphis / (1.0 + (Mode == 1 ? vbs : vbd) / (_temp.TempPhi + _temp.TempPhi)); phibs = sqphbs * sqphbs; dsqdvb = -phibs / (sqphs3 + sqphs3); } /* * .....short channel effect factor */ if (_mbp.JunctionDepth > 0 && _modeltemp.CoefficientDepletionLayerWidth > 0.0) { var wps = _modeltemp.CoefficientDepletionLayerWidth * sqphbs; var oneoverxj = 1.0 / _mbp.JunctionDepth; /* 1 / junction depth */ var xjonxl = _mbp.JunctionDepth * oneoverxl; /* junction depth / effective length */ var djonxj = _mbp.LateralDiffusion * oneoverxj; var wponxj = wps * oneoverxj; var wconxj = coeff0 + coeff1 * wponxj + coeff2 * wponxj * wponxj; arga = wconxj + djonxj; argc = wponxj / (1.0 + wponxj); argb = Math.Sqrt(1.0 - argc * argc); fshort = 1.0 - xjonxl * (arga * argb - djonxj); var dwpdvb = _modeltemp.CoefficientDepletionLayerWidth * dsqdvb; var dadvb = (coeff1 + coeff2 * (wponxj + wponxj)) * dwpdvb * oneoverxj; var dbdvb = -argc * argc * (1.0 - argc) * dwpdvb / (argb * wps); dfsdvb = -xjonxl * (dadvb * argb + arga * dbdvb); } else { fshort = 1.0; dfsdvb = 0.0; } /* * .....body effect */ var gammas = _mbp.Gamma * fshort; var fbodys = 0.5 * gammas / (sqphbs + sqphbs); var fbody = fbodys + _mbp.NarrowFactor / _bp.Width; var onfbdy = 1.0 / (1.0 + fbody); var dfbdvb = -fbodys * dsqdvb / sqphbs + fbodys * dfsdvb / fshort; var qbonco = gammas * sqphbs + _mbp.NarrowFactor * phibs / _bp.Width; var dqbdvb = gammas * dsqdvb + _mbp.Gamma * dfsdvb * sqphbs - _mbp.NarrowFactor / _bp.Width; /* * .....static feedback effect */ var vbix = _temp.TempVoltageBi * _mbp.MosfetType - eta * (Mode * vds); /* * .....threshold voltage */ var vth = vbix + qbonco; var dvtdvd = -eta; var dvtdvb = dqbdvb; /* * .....joint weak inversion and strong inversion */ von = vth; if (_mbp.FastSurfaceStateDensity > 0.0) { var csonco = Circuit.Charge * _mbp.FastSurfaceStateDensity * 1e4 /* (cm * * 2 / m * * 2) */ * effectiveLength * _bp.Width / oxideCap; var cdonco = qbonco / (phibs + phibs); xn = 1.0 + csonco + cdonco; von = vth + vt * xn; dxndvb = dqbdvb / (phibs + phibs) - qbonco * dsqdvb / (phibs * sqphbs); dvodvd = dvtdvd; dvodvb = dvtdvb + vt * dxndvb; } else { /* * .....cutoff region */ if ((Mode == 1 ? vgs : vgd) <= von) { cdrain = 0.0; Transconductance = 0.0; CondDs = 0.0; TransconductanceBs = 0.0; goto innerline1000; } } /* * .....device is on */ var vgsx = Math.Max(Mode == 1 ? vgs : vgd, von); /* * .....mobility modulation by gate voltage */ var onfg = 1.0 + _mbp.Theta * (vgsx - vth); var fgate = 1.0 / onfg; var us = _temp.TempSurfaceMobility * 1e-4 /*(m**2/cm**2)*/ * fgate; var dfgdvg = -_mbp.Theta * fgate * fgate; var dfgdvd = -dfgdvg * dvtdvd; var dfgdvb = -dfgdvg * dvtdvb; /* * .....saturation voltage */ vdsat = (vgsx - vth) * onfbdy; if (_mbp.MaxDriftVelocity <= 0.0) { dvsdvg = onfbdy; dvsdvd = -dvsdvg * dvtdvd; dvsdvb = -dvsdvg * dvtdvb - vdsat * dfbdvb * onfbdy; } else { var vdsc = effectiveLength * _mbp.MaxDriftVelocity / us; onvdsc = 1.0 / vdsc; arga = (vgsx - vth) * onfbdy; argb = Math.Sqrt(arga * arga + vdsc * vdsc); vdsat = arga + vdsc - argb; var dvsdga = (1.0 - arga / argb) * onfbdy; dvsdvg = dvsdga - (1.0 - vdsc / argb) * vdsc * dfgdvg * onfg; dvsdvd = -dvsdvg * dvtdvd; dvsdvb = -dvsdvg * dvtdvb - arga * dvsdga * dfbdvb; } /* * .....current factors in linear region */ var vdsx = Math.Min(Mode * vds, vdsat); if (vdsx.Equals(0.0)) { goto line900; } var cdo = vgsx - vth - 0.5 * (1.0 + fbody) * vdsx; var dcodvb = -dvtdvb - 0.5 * dfbdvb * vdsx; /* * .....normalized drain current */ var cdnorm = cdo * vdsx; Transconductance = vdsx; CondDs = vgsx - vth - (1.0 + fbody + dvtdvd) * vdsx; TransconductanceBs = dcodvb * vdsx; /* * .....drain current without velocity saturation effect */ var cd1 = beta * cdnorm; beta = beta * fgate; cdrain = beta * cdnorm; Transconductance = beta * Transconductance + dfgdvg * cd1; CondDs = beta * CondDs + dfgdvd * cd1; TransconductanceBs = beta * TransconductanceBs; /* * .....velocity saturation factor */ if (_mbp.MaxDriftVelocity > 0.0) { fdrain = 1.0 / (1.0 + vdsx * onvdsc); var fd2 = fdrain * fdrain; arga = fd2 * vdsx * onvdsc * onfg; dfddvg = -dfgdvg * arga; dfddvd = -dfgdvd * arga - fd2 * onvdsc; dfddvb = -dfgdvb * arga; /* * .....drain current */ Transconductance = fdrain * Transconductance + dfddvg * cdrain; CondDs = fdrain * CondDs + dfddvd * cdrain; TransconductanceBs = fdrain * TransconductanceBs + dfddvb * cdrain; cdrain = fdrain * cdrain; } /* * .....channel length modulation */ if (Mode * vds <= vdsat) { goto line700; } if (_mbp.MaxDriftVelocity <= 0.0) { goto line510; } if (_modeltemp.Alpha.Equals(0.0)) { goto line700; } var cdsat = cdrain; var gdsat = cdsat * (1.0 - fdrain) * onvdsc; gdsat = Math.Max(1.0e-12, gdsat); var gdoncd = gdsat / cdsat; var gdonfd = gdsat / (1.0 - fdrain); var gdonfg = gdsat * onfg; var dgdvg = gdoncd * Transconductance - gdonfd * dfddvg + gdonfg * dfgdvg; var dgdvd = gdoncd * CondDs - gdonfd * dfddvd + gdonfg * dfgdvd; var dgdvb = gdoncd * TransconductanceBs - gdonfd * dfddvb + gdonfg * dfgdvb; var emax = _mbp.Kappa * cdsat * oneoverxl / gdsat; var emoncd = emax / cdsat; var emongd = emax / gdsat; var demdvg = emoncd * Transconductance - emongd * dgdvg; var demdvd = emoncd * CondDs - emongd * dgdvd; var demdvb = emoncd * TransconductanceBs - emongd * dgdvb; arga = 0.5 * emax * _modeltemp.Alpha; argc = _mbp.Kappa * _modeltemp.Alpha; argb = Math.Sqrt(arga * arga + argc * (Mode * vds - vdsat)); delxl = argb - arga; dldvd = argc / (argb + argb); var dldem = 0.5 * (arga / argb - 1.0) * _modeltemp.Alpha; ddldvg = dldem * demdvg; ddldvd = dldem * demdvd - dldvd; ddldvb = dldem * demdvb; goto line520; line510: delxl = Math.Sqrt(_mbp.Kappa * (Mode * vds - vdsat) * _modeltemp.Alpha); dldvd = 0.5 * delxl / (Mode * vds - vdsat); ddldvg = 0.0; ddldvd = -dldvd; ddldvb = 0.0; /* * .....punch through approximation */ line520: if (delxl > 0.5 * effectiveLength) { delxl = effectiveLength - effectiveLength * effectiveLength / (4.0 * delxl); arga = 4.0 * (effectiveLength - delxl) * (effectiveLength - delxl) / (effectiveLength * effectiveLength); ddldvg = ddldvg * arga; ddldvd = ddldvd * arga; ddldvb = ddldvb * arga; dldvd = dldvd * arga; } /* * .....saturation region */ var dlonxl = delxl * oneoverxl; var xlfact = 1.0 / (1.0 - dlonxl); cdrain = cdrain * xlfact; var diddl = cdrain / (effectiveLength - delxl); Transconductance = Transconductance * xlfact + diddl * ddldvg; gds0 = CondDs * xlfact + diddl * ddldvd; TransconductanceBs = TransconductanceBs * xlfact + diddl * ddldvb; Transconductance = Transconductance + gds0 * dvsdvg; TransconductanceBs = TransconductanceBs + gds0 * dvsdvb; CondDs = gds0 * dvsdvd + diddl * dldvd; /* * .....finish strong inversion case */ line700: if ((Mode == 1 ? vgs : vgd) < von) { /* * .....weak inversion */ var onxn = 1.0 / xn; var ondvt = onxn / vt; var wfact = Math.Exp(((Mode == 1 ? vgs : vgd) - von) * ondvt); cdrain = cdrain * wfact; var gms = Transconductance * wfact; var gmw = cdrain * ondvt; Transconductance = gmw; if (Mode * vds > vdsat) { Transconductance = Transconductance + gds0 * dvsdvg * wfact; } CondDs = CondDs * wfact + (gms - gmw) * dvodvd; TransconductanceBs = TransconductanceBs * wfact + (gms - gmw) * dvodvb - gmw * ((Mode == 1 ? vgs : vgd) - von) * onxn * dxndvb; } /* * .....charge computation */ goto innerline1000; /* * .....special case of vds = 0.0d0 */ line900: beta = beta * fgate; cdrain = 0.0; Transconductance = 0.0; CondDs = beta * (vgsx - vth); TransconductanceBs = 0.0; if (_mbp.FastSurfaceStateDensity > 0.0 && (Mode == 1 ? vgs : vgd) < von) { CondDs *= Math.Exp(((Mode == 1 ? vgs : vgd) - von) / (vt * xn)); } innerline1000 :; /* * .....done */ } /* DETAILPROF */ /* 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; /* DETAILPROF */ /* * meyer's capacitor model */ /* DETAILPROF */ /* * 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); }
/// <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 = _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; } }