/// <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;
                }
            }
Example #2
0
        /// <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);
        }
Example #3
0
        /// <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);
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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;
                }
            }