コード例 #1
0
ファイル: svdiode.py プロジェクト: AndySze/cardoon
    def eval_cqs(self, vPort):
        """
        Models intrinsic diode and charge.  

        vPort[0]: x as in Rizolli's equations

        Returns a tuple with 2 or 3 elements: current, voltage and
        charge. Charge is ommited if both cj0 and tt are zero.
        """
        # Calculate state-variable PN junction current, voltage and charge
        (iD, vD) = self.jtn.get_idvd(vPort[0])
        if self.cj0:
            qD = self.jtn.get_qd(vD)
        if self.tt:
            qD += self.tt * iD

        # add breakdown current
        if self.bv < np.inf:
            iD -= self.ibv * ad.safe_exp(-(vD + self.bv) / self.n / self.vt)

        # area effect
        iD *= self.area
        # Scale voltage (gyrator gain)
        vD *= glVar.gyr

        iVec = np.array([iD, vD])
        if self._qd:
            # area effect for charge
            qD *= self.area
            qVec = np.array([qD])
            return (iVec, qVec)
        else:
            return (iVec, np.array([]))
コード例 #2
0
ファイル: diode.py プロジェクト: AndySze/cardoon
    def eval_cqs(self, vPort):
        """
        Models intrinsic diode and charge.  

        vPort is a vector with 1 element (diode voltage)

        Returns one vector for current and another for charge. Charge
        vector is empty if both cj0 and tt are zero
        """
        # Calculate regular PN junction current and charge
        iD = self.jtn.get_id(vPort[0])

        if self.cj0:
            qD = self.jtn.get_qd(vPort[0])
        else:
            qD = 0.

        if self.tt:
            qD += self.tt * iD

        # add breakdown current
        if (self.bv < np.inf):
            iD -= self.ibv * \
                ad.safe_exp(-(vPort[0] + self.bv) / self.n / self.vt)

        iD *= self.area
        idV = np.array([iD])
        if self._qd:
            qD *= self.area
            qdV = np.array([qD])
            return (idV, qdV)
        else:
            return (idV, np.array([]))
コード例 #3
0
    def eval_cqs(self, vPort):
        """
        Models intrinsic diode and charge.  

        vPort is a vector with 1 element (diode voltage)

        Returns one vector for current and another for charge. Charge
        vector is empty if both cj0 and tt are zero
        """
        # Calculate regular PN junction current and charge
        iD = self.jtn.get_id(vPort[0])

        if self.cj0 != 0.:
            qD = self.jtn.get_qd(vPort[0])
        else:
            qD = 0.

        if self.tt != 0.:
            qD += self.tt * iD

        # add breakdown current
        if (self.bv < np.inf):
            iD -= self.ibv * \
                ad.safe_exp(-(vPort[0] + self.bv) / self.n / self.vt)

        iD *= self.area
        idV = np.array([iD])
        if self._qd:
            qD *= self.area
            qdV = np.array([qD])
            return (idV, qdV)
        else:
            return (idV, np.array([]))
コード例 #4
0
ファイル: svdiode.py プロジェクト: manasdas17/cardoon
    def eval_cqs(self, vPort):
        """
        Models intrinsic diode and charge.  

        vPort[0]: x as in Rizolli's equations

        Returns a tuple with 2 or 3 elements: current, voltage and
        charge. Charge is ommited if both cj0 and tt are zero.
        """
        # Calculate state-variable PN junction current, voltage and charge
        (iD, vD) = self.jtn.get_idvd(vPort[0])
        if self.cj0 != 0.:
            qD = self.jtn.get_qd(vD)
        if self.tt != 0.:
            qD += self.tt * iD

        # add breakdown current
        if (self.bv < np.inf):
            iD -= self.ibv * \
                ad.safe_exp(-(vD + self.bv) / self.n / self.vt)

        # area effect
        iD *= self.area
        # Scale voltage (gyrator gain)
        vD *= glVar.gyr

        iVec = np.array([iD, vD])
        if self._qd:
            # area effect for charge
            qD *= self.area
            qVec = np.array([qD])
            return (iVec, qVec)
        else:
            return (iVec, np.array([]))
コード例 #5
0
ファイル: diode.py プロジェクト: AndySze/cardoon
    def get_id(self, vd):
        """
        Returns junction current

        vd: diode voltage
        """
        # Regular junction current
        return self._t_is * (ad.safe_exp(vd / self._kexp) - 1.)
コード例 #6
0
    def get_id(self, vd):
        """
        Returns junction current

        vd: diode voltage
        """
        # Regular junction current
        return self._t_is * (ad.safe_exp(vd / self._kexp) - 1.)
コード例 #7
0
ファイル: bjt.py プロジェクト: AndySze/cardoon
    def eval_cqs(self, vPort):
        """
        Calculates currents/charges

        Input is a vector may be one of the following, depending on
        parameter values::

          vPort = [vbe, vbc]
          vPort = [vbie, vbic, v1_i] (gyrator voltage, rb != 0)

        Output also depends on parameter values. Charges only present
        if parameters make them different than 0 (i.e., cje, tf, cjc,
        etc. are set to nonzero values)::
        
          iVec = [ibe, ibc, ice]
          iVec = [ibe, ibc, ice, gyr*ib*Rb] (rb != 0)

          qVec = [qbe, qbc]
          qVec = [qbe, qbc, qbx] (rb != 0 and cjc != 1)
        """
        # Invert control voltages if needed
        vPort1 = self._typef * vPort

        # Calculate regular PN junctions currents and charges
        ibf = self.jif.get_id(vPort1[0])
        ibr = self.jif.get_id(vPort1[1])
        if self.ise:
            ile = self.jile.get_id(vPort1[0])
        else:
            ile = 0.
        if self.isc:
            ilc = self.jilc.get_id(vPort1[1])
        else:
            ilc = 0.
        # Kqb
        q1m1 = 1.
        if self.var:
            q1m1 -= vPort1[0] / self.var
        if self.vaf:
            q1m1 -= vPort1[1] / self.vaf
        kqb = 1. / q1m1
        q2 = 0.
        if self.ikf:
            q2 += ibf / self.ikf 
        if self.ikr:
            q2 += ibr / self.ikr
        if q2:
            kqb *= .5 * (1. + np.sqrt(1. + 4. * q2))

        # Create output vector [ibe, ibc, ice, ...]
        iVec = np.zeros(self.ncurrents, dtype = type(ibf))
        qVec = np.zeros(self.ncharges, dtype = type(ibf))
        # ibe
        iVec[0] = ibf / self._bf_t + ile
        # ibc
        iVec[1] = ibr / self._br_t + ilc
        # ice
        iVec[2] = (ibf - ibr) / kqb

        # RB
        if self.rb:
            # Using gyrator
            # vPort1[2] not defined if rb == 0
            # ib has area effect included (removed by _ck1 and _ck2)
            ib = vPort1[2] * glVar.gyr
            if self.irb:
                ib1 = np.abs(ib)
                x = np.sqrt(1. + self._ck1 * ib1) - 1.
                x *= self._ck2 / np.sqrt(ib1)
                tx = np.tan(x)
                c = self.rbm + 3. * (self.rb - self.rbm) \
                    * (tx - x) / (x * tx * tx)
                rb = ad.condassign(ib1, c, self.rb)
            else:
                rb = self.rbm + (self.rb - self.rbm) / kqb
            # Output is gyr * ib * rb.  It is divided by area^2 to
            # compensate that the whole vector is multiplied by area
            # at the end
            iVec[3] = glVar.gyr * ib * rb / pow(self.area, 2)
            vbcx = ib * rb / self.area + vPort1[1]

        # Charges ----------------------------------------------- 

        # Note that if tf == 0 and cje == 0, nothing is calculated and
        # nothing is assigned to the output vector.

        # qbe is the first charge (0)
        if self.tf:
            # Effective tf
            tfeff = self.tf
            if self.vtf:
                x = ibf / (ibf + self.itf)
                tfeff *= (1. + self.xtf * x*x * 
                          ad.safe_exp(vPort1[1] /1.44 /self.vtf))
            qVec[0] = tfeff * ibf
        if self.cje:
            qVec[0] += self.jif.get_qd(vPort1[0]) 

        # qbc 
        if self._qbx:
            if self.tr:
                qVec[-2] = self.tr * ibr
            if self.cjc:
                qVec[-2] += self.jir.get_qd(vPort1[1]) * self.xcjc 
                # qbx
                qVec[-1] = self.jir.get_qd(vbcx) * (1. - self.xcjc)
        else:
            if self.tr:
                qVec[-1] = self.tr * ibr
            if self.cjc:
                qVec[-1] += self.jir.get_qd(vPort1[1]) 

        # Consider area effect and invert currents if needed
        iVec *= self.area * self._typef
        qVec *= self.area * self._typef

        return (iVec, qVec)
コード例 #8
0
ファイル: mosEKV.py プロジェクト: cechrist/cardoon
    def eval_cqs(self, vPort, getOP=False):
        """
        Calculates Ids, Idb, Isb currents and D, G, S, charges. 

        Input:  vPort = [vdb , vgb , vsb]

        Output: vector with Ids, Idb, Isb currents and vector with D,
        G, S charges.
        
        If getOP = True, return normal output vector plus operating
        point variables in tuple: (iVec, qVec, opV)
        """
        # Invert all voltages in case of a P-channel device
        vPort1 = self._tf * vPort
        # If vds is negative, swap Vd and Vs and (also swap charges
        # later)
        DtoSswap = ad.condassign(vPort1[0] - vPort1[2], 1.0, -1.0)
        # perform the swap (need tmp variable)
        tmp = ad.condassign(DtoSswap, vPort1[0], vPort1[2])
        vPort1[2] = ad.condassign(DtoSswap, vPort1[2], vPort1[0])
        vPort1[0] = tmp

        # Effective gate voltage including reverse short channel effect
        vgprime = vPort1[1] - self._vt0a - self._deltavRSCE + self.phiT + self._gammaa * np.sqrt(self.phiT)
        # Effective substrate factor including charge-sharing for
        # short and narrow channels.  Pinch-off voltage for
        # narrow-channel effect
        vp0 = (
            vgprime
            - self.phiT
            - self._gammaa * (np.sqrt(vgprime + self._gammaa * self._gammaa / 4.0) - self._gammaa / 2.0)
        )
        vp0 = ad.condassign(vgprime, vp0, -self.phiT)

        # Effective substrate factor accounting for charge-sharing
        tmp = 16.0 * self._Vt * self._Vt
        vsprime = 0.5 * (vPort1[2] + self.phiT + np.sqrt(pow(vPort1[2] + self.phiT, 2) + tmp))
        vdprime = 0.5 * (vPort1[0] + self.phiT + np.sqrt(pow(vPort1[0] + self.phiT, 2) + tmp))

        tmp = self.leta / self._leff * (np.sqrt(vsprime) + np.sqrt(vdprime))
        tmp -= 3.0 * self.weta * np.sqrt(vp0 + self.phiT) / self._weff
        gammao = self._gammaa - const.epSi / self.cox * tmp

        gammaprime = 0.5 * (gammao + np.sqrt(gammao * gammao + 0.1 * self._Vt))

        # Pinch-off voltage including short- and narrow-channel effect
        vp = vgprime - self.phiT
        vp -= gammaprime * (np.sqrt(vgprime + pow(0.5 * gammaprime, 2)) - gammaprime / 2.0)
        vp = ad.condassign(vgprime, vp, -self.phiT)

        # Slope factor
        n = 1 + self._gammaa / (2.0 * np.sqrt(vp + self.phiT + 4.0 * self._Vt))

        # Forward normalized current
        i_f = (vp - vPort1[2]) / self._Vt
        i_f = self.interp(i_f)

        # Velocity saturation voltage
        vdss = np.sqrt(0.25 + self._Vt * np.sqrt(i_f) / self._vc) - 0.5
        vdss *= self._vc

        # Drain-to-source saturation voltage for reverse normalized current
        tmp = np.sqrt(i_f) - 0.75 * np.log(i_f)
        vdssprime = np.sqrt(0.25 + self._Vt * tmp / self._vc) - 0.5
        vdssprime *= self._vc
        vdssprime += self._Vt * (np.log(self._vc / (2.0 * self._Vt)) - 0.6)

        # Channel-length modulation
        tmp = np.sqrt(i_f) - vdss / self._Vt
        deltav = np.sqrt(self.Lambda * tmp + 1.0 / 64)
        deltav *= 4.0 * self._Vt
        vds = 0.5 * (vPort1[0] - vPort1[2])
        vip = np.sqrt(vdss * vdss + deltav * deltav)
        vip -= np.sqrt(pow(vds - vdss, 2) + deltav * deltav)
        deltal = np.log(1.0 + (vds - vip) / self._lc / self._t_ucrit)
        deltal *= self.Lambda * self._lc

        # Equivalent channel length including channel-length
        # modulation and velocity saturation
        lprime = self.ns * self._leff - deltal + (vds + vip) / self._t_ucrit
        leq = 0.5 * (lprime + np.sqrt(lprime * lprime + self._lmin * self._lmin))

        # Reverse normalized current
        tmp = vp - vds - vPort1[2]
        tmp -= np.sqrt(vdssprime * vdssprime + deltav * deltav)
        tmp += np.sqrt(pow(vds - vdssprime, 2) + deltav * deltav)
        irprime = self.interp(tmp / self._Vt)

        # Reverse normalized currect for mobility model, intrinsic
        # charges/capacitances, thermal noise model and NQS time-constant ???
        ir = self.interp((vp - vPort1[0]) / self._Vt)

        # Quasi-static model equations
        # Dynamic model for the intrinsic node charges
        sqvpphi = np.sqrt(vp + self.phiT + 1.0e-6)
        nq = 1.0 + self._gammaa / 2.0 / sqvpphi

        # Normalized intrinsic node charges
        xf = np.sqrt(0.25 + i_f)
        xr = np.sqrt(0.25 + ir)
        tmp = pow(xf + xr, 2)

        qd = 3.0 * xr ** 3 + 6.0 * xr * xr * xf + 4.0 * xr * xf * xf + 2.0 * xf ** 3
        qd *= 4.0 / 15.0 / tmp
        qd = -nq * (qd - 0.5)

        qs = 3.0 * xf ** 3 + 6.0 * xf * xf * xr + 4.0 * xf * xr * xr + 2.0 * xr ** 3
        qs *= 4.0 / 15.0 / tmp
        qs = -nq * (qs - 0.5)

        qi = qs + qd

        qb1 = -self._gammaa * sqvpphi / self._Vt
        qb1 -= (nq - 1.0) * qi / nq
        qb = ad.condassign(vgprime, qb1, -vgprime / self._Vt)
        # qg = -qi - qox - qb, but qox == 0, so:
        qg = -qi - qb - self.qox

        # Transconductance factor and mobility reduction due to vertical field
        betao = self.kpa * self.np * self._weff / leq
        if self.theta != 0.0:
            # Simple mobility reduction model
            vpprime = 0.5 * (vp + np.sqrt(vp * vp + 2.0 * self._Vt * self._Vt))
            beta = betao / (1.0 + theta * vpprime)
        else:
            # Rigorous mobility reduction model
            betaoprime = 1.0 + self.cox * self._qb0 / self.e0 / const.epSi
            betaoprime *= betao
            tmp = np.abs(qb + self.eta * qi)
            tmp = 1.0 + self.cox * self._Vt * tmp / self.e0 / const.epSi
            beta = betaoprime / tmp

        # Specific current
        IS = 2.0 * n * beta * self._Vt * self._Vt

        # Drain-to-source current
        ids = IS * (i_f - irprime)

        # import pdb; pdb.set_trace()

        # Impact ionization current
        vib = vPort1[0] - vPort1[2] - 2.0 * self.ibn * vdss
        idb1 = ids * self.iba * vib / self._t_ibb
        idb1 *= ad.safe_exp(-self._t_ibb * self._lc / vib)
        idb = ad.condassign(vib, idb1, 0.0)

        # -------------------------------------------------------------
        # Create output vectors
        qVec = np.array([0.0, qg, 0.0], dtype=type(idb))
        # have to switch charges if Drain and Source voltages switched
        qVec[0] = ad.condassign(DtoSswap, qd, qs)
        qVec[2] = ad.condassign(DtoSswap, qs, qd)
        # De-normalize charge and invert if needed
        qVec *= self._Cox * self._Vt * self._tf

        iVec = np.array([0.0, 0.0, 0.0], dtype=type(idb))
        iVec[0] = DtoSswap * ids
        iVec[1] = ad.condassign(DtoSswap, idb, 0.0)
        iVec[2] = ad.condassign(DtoSswap, 0.0, idb)
        # Revert currents if needed
        iVec *= self._tf

        # --------------------------------------------------------------
        # Operating point information
        if getOP:
            # Vth
            Vth = self._vt0a + self._deltavRSCE + gammaprime * np.sqrt(vsprime) - self._gammaa * np.sqrt(self.phiT)
            # Non quasi-static equations
            tau0 = 0.5 * self._Cox / self._Vt / beta
            tmp = (xf ** 2 + 3.0 * xf * xr + xr ** 2) / pow(xf + xr, 3)
            tau = tau0 * 4.0 / 15.0 * tmp
            # Create operating point variables dictionary
            return {
                "Vp": vp,
                "n": n,
                "Beta": beta,
                "IS": IS,
                "IF": i_f,
                "IR": ir,
                "IRprime": irprime,
                "tef": 1.0 / (np.sqrt(0.25 + i_f) + 0.5),
                "Vth": Vth,
                "Vov": self._tf * n * (vp - vPort[2]),
                "Vdsat": self._tf * self._Vt * (2.0 * np.sqrt(i_f) + 4.0),
                "tau0": tau0,
                "tau": tau,
                "Sthermal": self._kSt * beta * np.abs(qi),
                "Reversed": DtoSswap < 0.0,
            }
        else:
            return (iVec, qVec)
コード例 #9
0
ファイル: mosBSIM3v3.py プロジェクト: AndySze/cardoon
    def eval_cqs(self, vPort, saveOP = False):
        """
        Calculates currents and charges

        Input: vPort = [vdb , vgb , vsb]
        Output: iVec = [ids, idb, isb], qVec = [qd, qg, qs]

        If saveOP = True, return normal output vector plus operating
        point variables in tuple: (iVec, qVec, opV)
        """
        # import pdb; pdb.set_trace()
        # Invert all voltages in case of a P-channel device
        vPort1 = self._tf * vPort
        # If vds is negative, swap Vd and Vs and (also swap charges
        # later)
        DtoSswap = ad.condassign(vPort1[0] - vPort1[2], 1., -1.)
        # perform the swap (need tmp variable)
        tmp = ad.condassign(DtoSswap, vPort1[0], vPort1[2])
        vPort1[2] = ad.condassign(DtoSswap, vPort1[2], vPort1[0])
        vPort1[0] = tmp

        # Calculate VDS, VGS and VBS for bsim model
        VDS = vPort1[0] - vPort1[2]
        VGS = vPort1[1] - vPort1[2]
        VBS = -vPort1[2]

        # ----------------------------------------------------------------
        T0 = VBS - self.vbsc - 0.001 
        T1 = np.sqrt(T0 * T0 - 0.004 * self.vbsc)
        Vbseff = self.vbsc + .5 * (T0 + T1)
        Vbseff = ad.condassign(-Vbseff + VBS,
                                VBS,
                                Vbseff)
        
        #Calculation of Phis, sqrtPhis and Xdep
        Phis = ad.condassign(Vbseff,
                             self.phi**2 / (self.phi + Vbseff),
                             self.phi - Vbseff)
        
        sqrtPhis = ad.condassign(Vbseff,
                                 self.phis3 / (self.phi + 0.5 * Vbseff),
                                 np.sqrt(Phis))
        
        Xdep = self.Xdep0 * sqrtPhis / self.sqrtPhi
        
        #Calculation of Threshold voltage-vth
        T3 = np.sqrt(Xdep)
        T1 = ad.condassign(self.dvt2 * Vbseff + .5,
                           1. + self.dvt2 * Vbseff,
                           (1. + 3. * self.dvt2 * Vbseff) 
                           / (3. + 8. * self.dvt2 * Vbseff))

        ltl = self.factor1 * T3 * T1

        T1 = ad.condassign(self.dvt2w * Vbseff + .5,
                           1. + self.dvt2w * Vbseff,
                           (1. + 3. * self.dvt2w * Vbseff) 
                           / (3. + 8. * self.dvt2w * Vbseff))

        ltw = self.factor1 * T3 * T1
        
        # Alternative to prevent overflow (apparently not needed)
        #T2 = ad.safe_exp(-.5 * self.dvt1 * self.leff / ltl)
        k_temp = -.5 * self.dvt1 * self.leff / ltl
        T2 = ad.condassign(k_temp + EXP_THRESHOLD,
                           ad.safe_exp(k_temp),
                           MIN_EXP)
        Theta0 =  T2 * (1. + 2. * T2)

        thetavth = self.dvt0 * Theta0
        Delt_vth = thetavth * self.V0

        # Alternative to prevent overflow (apparently not needed)
        #T2 = ad.safe_exp(-.5 * self.dvt1w * self.weff * self.leff / ltw)
        # Modified from freeda's source to prevent using uninitialized
        # variable
        k_temp = -.5 * self.dvt1w * self.weff * self.leff / ltw
        T2 = ad.condassign(k_temp + EXP_THRESHOLD,
                           ad.safe_exp(k_temp),
                           MIN_EXP)
        T2 *= (1. + 2. * T2)
                
        T0 = self.dvt0w * T2
        T2 = T0 * self.V0

        T0 = np.sqrt(1. + self.nlx / self.leff)
        T1 = self.k1ox * (T0 - 1.) * self.sqrtPhi \
            + (self.kt1 + self.kt1l / self.leff + self.kt2 * Vbseff) \
            * self._ToTnm1
        TMP2 = self.tox * self.phi / (self.weff + self.w0)
        
        T3 = self.eta0 + self.etab * Vbseff
        T4 = ad.condassign(-T3 + 1.0e-4,
                            1. / (3. - 2e4 * self.eta0 + self.etab * Vbseff),
                            1.)

        dDIBL_Sft_dVd = T3 * self.theta0vb0
        DIBL_Sft = dDIBL_Sft_dVd * VDS
        
        Vth = self._vth0 - self._k1 * self.sqrtPhi + self.k1ox * sqrtPhis \
            - self.k2ox * Vbseff - Delt_vth - T2 \
            + (self.k3 + self.k3b * Vbseff) * TMP2 + T1 - DIBL_Sft
        
        #Calculate n
        temp_tmp2 = self.nfactor * const.epSi / Xdep
        temp_tmp3 = self.cdsc + self.cdscb * Vbseff + self.cdscd * VDS
        temp_tmp4 = (temp_tmp2 + temp_tmp3 * Theta0 + self.cit) / self.cox
        
        n = ad.condassign(temp_tmp4 + .5,
                          1. + temp_tmp4,
                          (1. + 3. * temp_tmp4) \
                              * (1. / (3. + 8. * temp_tmp4)))
                          
        #Poly Gate Si Depletion Effect
        Vgs_eff = VGS
        
        Vgst = Vgs_eff - Vth # not in Nikhil's code
        
        #Effective Vgst (Vgsteff) Calculation
        T10 = 2. * n * self._Vt
        VgstNVt = Vgst / T10
        ExpArg = -(2. * 0.08 + Vgst) / T10
        
        T1 = T10 * log1pexp(VgstNVt)
        T2 = 1. + T10 * self.cox * ad.safe_exp(ExpArg) / self._Vt / self.cdep0
        
        Vgsteff = ad.condassign(VgstNVt - EXP_THRESHOLD,
                                Vgst,
                                T1 / T2)
        Vgsteff = ad.condassign(
            ExpArg - EXP_THRESHOLD,
            self._Vt * self.cdep0 \
                / self.cox / ad.safe_exp((Vgst + 0.08) / n / self._Vt),
            T1 / T2)
        
        T3 = T2 * T2
        
        # Calculate Effective Channel Geometry
        T9 = sqrtPhis - self.sqrtPhi
        k_temp = self.weff - 2. * (self.dwg * Vgsteff + self.dwb * T9)
        
        Weff = ad.condassign(-k_temp + 2.0e-8,
                              2e-8 * (4.0e-8 - k_temp) * T0,
                              k_temp)

        T0 = self.prwg * Vgsteff + self.prwb * (sqrtPhis - self.sqrtPhi)
        Rds = ad.condassign(T0 + 0.9,
                            self.rds0 * (1. + T0),
                            self.rds0 * (.8 +T0) / (17. + 20. * T0))

        #Calculate Abulk
        T1 = 0.5 * self.k1ox / sqrtPhis
        T9 = np.sqrt(self.xj * Xdep)
        T5 = self.leff / (self.leff + 2. * T9)
        T2 = (self.a0 * T5) + self.b0 / (self.weff + self.b1)
        T6 = T5 * T5
        T7 = T5 * T6
        
        Abulk0 = 1. + T1 * T2
        
        T8 = self.ags * self.a0 * T7
        Abulk = Abulk0 - T1 * T8 * Vgsteff

        Abulk0 = ad.condassign(-Abulk0 + .1,
                                (.2 - Abulk0) / (3. - 20. * Abulk0),
                                Abulk0)

        Abulk = ad.condassign(-Abulk + .1,
                               (.2 - Abulk) / (3. - 20. * Abulk),
                               Abulk)
        
        T2 = self.keta * Vbseff
        
        T0 = ad.condassign(T2 + 0.9,
                           1. / (1. + T2),
                           (17. + 20. * T2) / (0.8 + T2))

        Abulk *= T0
        Abulk0 *= T0
        
        T0 = Vgsteff + 2. * Vth
        T2 = self._ua + self._uc * Vbseff
        T3 = T0 / self.tox
        T5 = T3 * (T2 + self._ub * T3)
        
        Denomi =  ad.condassign(T5 + .8,
                                1. + T5,
                                (.6 + T5) / (7. + 10. * T5))

        ueff = self.u0temp / Denomi
        
        Esat = 2. * self.vsattemp / ueff
        
        # Saturation Drain Voltage Vdsat
        WVCox = Weff * self.vsattemp * self.cox
        WVCoxRds = WVCox * Rds
        
        EsatL = Esat * self.leff
        Lambda = self.a2
        
        Vgst2Vtm = Vgsteff + 2. * self._Vt
        
        T0 = 1. / (Abulk * EsatL + Vgst2Vtm)
        T3 = EsatL * Vgst2Vtm
        Vdsat = T3 * T0
        
        # Effective Vds(Vdseff) Calculation
        T1 = Vdsat - VDS - self.delta
        T2 = np.sqrt(T1**2 + 4. * self.delta * Vdsat)
        #T0 = T1 / T2
        #T3 = 2. * self.delta / T2
        k_temp = Vdsat - .5 * (T1 + T2)
        Vdseff = ad.condassign(k_temp - VDS,
                               VDS,
                               k_temp)

        # The following seems unnnecessary:
        # Added to eliminate non-zero Vdseff at Vds=0.0
        #Vdseff = ad.condassign(abs(VDS),
        #                       Vdseff,
        #                       0.)
        
        # Calculate Vasat
        T6 = 1. - .5 * Abulk * Vdsat / Vgst2Vtm #T6=tmp4
        T9 = WVCoxRds * Vgsteff #expanded
        T0 = EsatL + Vdsat + 2. * T9 * T6
        
        T9 = WVCoxRds * Abulk
        T1 = 2. / Lambda - 1. + T9
        
        Vasat = T0 / T1
        
        diffVds = VDS - Vdseff
        
        #Calculate VACLM
        VACLM = ad.condassign(
            (diffVds - 1.0e-10) * self.pclm,
            self.leff * (Abulk + Vgsteff / EsatL) * diffVds \
                / (self.pclm * Abulk * self.litl),
            MAX_EXP)

        #Calculate VADIBL
        T1 = np.sqrt(const.epSi / const.epOx * self.tox * self.Xdep0)
        
        T0 = ad.safe_exp(-.5 * self.drout * self.leff / T1)
        T2 = T0 + 2. * T0**2
        thetaRout = self.pdibl1 * T2 + self.pdibl2 #drout, pdibl1,
                                                   #pdibl2 are given
        VADIBL = ad.condassign(
            thetaRout,
            (Vgst2Vtm - Vgst2Vtm * Abulk * Vdsat / (Vgst2Vtm + Abulk * Vdsat)) \
                / thetaRout,
            MAX_EXP)
            
        VADIBL = ad.condassign(self.pdiblb * Vbseff + 0.9,
                               VADIBL / (1. + self.pdiblb * Vbseff),
                               VADIBL * (17. + 20. * self.pdiblb * Vbseff) \
                                   / (.8 + self.pdiblb * Vbseff))
                               
        #Calculate Va
        T8 = self.pvag / EsatL
        T9 = T8 * Vgsteff
        
        T0 = ad.condassign(T9 + 0.9,
                           1. + T9,
                           (.8 + T9) / (17. + 20. * T9))
        
        T3 = VACLM + VADIBL #tmp3 = T3
        T1 = VACLM * VADIBL / T3
        Va = Vasat + T0 * T1
        
        #Calculate VASCBE
        if self.pscbe1:
            rcpVASCBE = ad.condassign(
                abs(diffVds),
                self.pscbe2 * ad.safe_exp(-self.pscbe1 * self.litl/diffVds) \
                    / self.leff,
                0.)
        else:
            rcpVASCBE = self.pscbe2 / self.leff

        # Original:
        #VASCBE = ad.condassign(
        #    diffVds - self.pscbe1 * self.litl / EXP_THRESHOLD,
        #    self.leff * np.exp(self.pscbe1 * self.litl/diffVds) / self.pscbe2,
        #    MAX_EXP * self.leff / self.pscbe2)

        #Calculate Ids
        CoxWovL = self.cox * Weff / self.leff
        beta = ueff * CoxWovL
        
        T0 = 1. - .5 * Abulk * Vdseff / Vgst2Vtm
        
        fgche1 = Vgsteff * T0
        
        T9 = Vdseff / EsatL
        fgche2 = 1. + T9
        
        gche = beta * fgche1 / fgche2
        
        T0 = 1. + gche * Rds
        T9 = Vdseff / T0
        Idl = gche * T9
        
        T9 = diffVds / Va
        T0 = 1. + T9
        Idsa = Idl * T0
        
        T9 = diffVds * rcpVASCBE
        T0 = 1. + T9
        Ids = Idsa * T0
        
        #Substrate current begins
        T1 = self.alpha0 + self.alpha1 * self.leff
        k_temp = T1 / self.leff * diffVds
        #T2 = k_temp * ad.safe_exp(-self.beta0 / diffVds)
        # Original T2:
        ad.condassign(
            diffVds - self.beta0 / EXP_THRESHOLD,
            k_temp * ad.safe_exp(-self.beta0 / diffVds),
            k_temp * MIN_EXP)

        k_temp = ad.condassign(T1, T2 * Idsa, 0.)
        Isub = ad.condassign(self.beta0, k_temp, 0.)
        
        # ********* Output current vector **************
        iVec = np.array([0., 0., 0.], dtype=type(Ids))
        iVec[0] = DtoSswap * Ids
        iVec[1] = ad.condassign(DtoSswap, Isub, 0.)
        iVec[2] = ad.condassign(DtoSswap, 0., Isub)
        # Revert currents if needed
        iVec *= self._tf
        # **********************************************

        #--------------------------------------------------------------
        # Charge calculation follows (does not work):

        #calculation of vfbzb
        T0 = -.5 * self.dvt1w * self.weff * self.leff \
            / self.factor1 / np.sqrt(self.Xdep0)
        
        #T2 = ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0))
        T2 = ad.condassign(T0 + EXP_THRESHOLD,
                           ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0)),
                           MIN_1)
        T0 = self.dvt0w * T2
        T2 = T0 * (self.vbi - self.phi)
        
        T0 = -.5 * self.dvt1 * self.leff / (self.factor1 * np.sqrt(self.Xdep0))

        #T3 = ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0))
        T3 = ad.condassign(T0 + EXP_THRESHOLD,
                           ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0)),
                           MIN_1)
        
        T3 = self.dvt0 * T3 * (self.vbi - self.phi)
        
        T4 = self.tox * self.phi / (self.weff + self.w0)
        T5 = self.k1ox * (T0 - 1.) * self.sqrtPhi \
            + (self.kt1 + self.kt1l / self.leff) * self._ToTnm1

        T0 = np.sqrt(1. + self.nlx / self.leff)
        T6 = self._vth0 - T2 -T3 + self.k3 * T4 + T5
        vfbzb = T6 - self.phi - self._k1 * self.sqrtPhi
        
        #Calculation for VbseffCV
        VbseffCV = ad.condassign(Vbseff,
                                 self.phi - Phis,
                                 Vbseff)
        
        #Calculation for VgsteffCV
        T0 = n * self.noff * self._Vt
        T1 = (Vgs_eff - Vth) / T0

        Vgsteff = ad.condassign(T1 - EXP_THRESHOLD,
                                Vgs_eff - Vth - self.voffcv,
                                T0 * log1pexp(T1))
                
        # This (after the previous) can not be right:
        #Vgsteff = ad.condassign(-T1 - EXP_THRESHOLD,
        #                         T0 * log(1. + MIN_EXP),
        #                         T0 * log(1. + np.exp(T1)))

        #Calculation for Vfbeff
        V3 = vfbzb - Vgs_eff + VbseffCV - .02
        T0 = np.sqrt(V3**2 + .08 * abs(vfbzb))
        Vfbeff = vfbzb - 0.5 * (V3 + T0)
        
        T0 = (Vgs_eff - VbseffCV - vfbzb) / self._Tox
        
        #Calculation for Tcen
        T1 = T0 * self.acde
        
        Tcen = ad.condassign(EXP_THRESHOLD + T1,
                             self.ldeb * np.exp(T1),
                             self.ldeb * MIN_EXP)
        Tcen = ad.condassign(-EXP_THRESHOLD + T1,
                             self.ldeb * MAX_EXP,
                             Tcen)
        
        V3 = self.ldeb - Tcen - 1e-3 * self.tox
        V4 = np.sqrt(V3**2 + 4e-3 * self.tox * self.ldeb)
        Tcen = self.ldeb - .5 * (V3 + V4)
        Ccen = const.epSi / Tcen
        Coxeff = Ccen * self.cox / (Ccen + self.cox)
        
        #Calculation for QoverlapCox
        CoxWLcen = Weff * self.leff * Coxeff 
        Qac0 = CoxWLcen * (Vfbeff - vfbzb)
        # QovCox = Qac0 / Coxeff
        
        T0 = .5 * self.k1ox
        T3 = Vgs_eff - Vfbeff - VbseffCV - Vgsteff
        T1 = np.sqrt(T0 * T0 + T3)
        T2 = CoxWLcen * T0 / T1

        T1 = ad.condassign(-T3,
                            T0 + T3 / self.k1ox,
                            np.sqrt(T0**2 + T3))

        T2 = ad.condassign(-T3,
                            CoxWLcen,
                            CoxWLcen * T0 / T1)
        
        Qsub0 = CoxWLcen * self.k1ox * (T1 - T0)
        # QovCox = Qsub0 / Coxeff
        
        #Calculation for Delta_phis
        T2 = ad.condassign(self.k1ox,
                           self.moin * self._Vt * self.k1ox**2,
                           .25 * self.moin * self._Vt)

        T0 = ad.condassign(self.k1ox,
                           self.k1ox * np.sqrt(self.phi),
                           .5 * np.sqrt(self.phi))

        T1 = 2. * T0 + Vgsteff
        DeltaPhi = self._Vt * np.log(1. + T1 * Vgsteff / T2)
        
        #The calculation for Tcen must be done once more
        # fREEDA:
        #T0 = (Vgsteff + 4.*(self._vth0 - self.vfb - self.phi))/ (2. * self._Tox)
        # ngspice:
        T3 =  4. * (Vth - vfbzb - self.phi)
        T0 = ad.condassign(T3,
                           .5 * (Vgsteff + T3) / self._Tox,
                           .5 * (Vgsteff + 1.0e-20) / self._Tox)

        k_temp = 2.01375270747048 * T0  # was np.exp(.7 * log(T0))
        T1 = 1. + k_temp
        T2 = 0.35 * k_temp / (T0 * self._Tox)
        Tcen = 1.9e-9 / T1
        
        Ccen = const.epSi / Tcen
        Coxeff = Ccen * self.cox / (Ccen + self.cox)
        CoxWLcen = Weff * self.leff * Coxeff 

        AbulkCV = Abulk0 * self.AbulkCVfactor
        VdsatCV = (Vgsteff - DeltaPhi) / AbulkCV

        T0 = VdsatCV - VDS - .02
        T1 = np.sqrt(T0**2 + .08 * VdsatCV)

        # From ngspice: internal version BSIM3v32V32
        VdseffCV = VdsatCV - .5 * (T0 + T1)
        # From freeda:
#        VdseffCV = ad.condassign(T0,
#                                 VdsatCV - .5 * (T0 + T1),
#                                 VdsatCV * (1. - .04/(T1-T0)))
        # Need this to prevent NaN in charge Jacobian
        VdseffCV += 1e-200
                                 
        # Seems not needed
        #VdseffCV = ad.condassign(abs(VDS),
        #                         Vdseff,
        #                         0.)
        
        T0 = AbulkCV * VdseffCV
        T1 = Vgsteff - DeltaPhi
        T2 = 12. * (T1 - .5 * T0 + 1e-20)
        T3 = T0 / T2
        T4 = 1. - 12. * T3**2
        T5 = AbulkCV * (6. * T0 * (4. * T1 - T0) / (T2**2) - .5)
        T6 = T5 * VdseffCV / AbulkCV

        qgate = CoxWLcen * (T1 - T0 * (.5 - T3))
        qbulk = CoxWLcen * (1. - AbulkCV) * (.5*VdseffCV - T0*VdseffCV/T2)
        
        #QovCox = qbulk / Coxeff
        
        T2 = T2 / 12.
        T3 = .5 * CoxWLcen / (T2**2)
        T4 = T1 * (2. * T0**2 / 3. + T1*(T1 - 4. * T0 / 3.)) - 2. * T0**3 / 15.
        
        qsrc = -T3 * T4
        qgate += Qac0 + Qsub0 - qbulk
        qbulk -= (Qac0 + Qsub0)
        qdrn = -(qbulk + qgate + qsrc)
        
        # ************ Output charge vector ****************
        qVec = np.array([0., qgate, 0.], dtype=type(qsrc))
        # have to switch charges if Drain and Source voltages switched
        qVec[0] = ad.condassign(DtoSswap, qdrn, qsrc)
        qVec[2] = ad.condassign(DtoSswap, qsrc, qdrn)
        # invert sign if needed
        qVec *= self._tf

        # Return numpy array with one element per current source.
        return (iVec, qVec)
コード例 #10
0
ファイル: mosBSIM3v3.py プロジェクト: AndySze/cardoon
    def process_params(self, thermal = False):
        # Called once the external terminals have been connected and
        # the non-default parameters have been set. Make sanity checks
        # here. Internal terminals/devices should also be defined
        # here.  Raise cir.CircuitError if a fatal error is found.
        ad.delete_tape(self)
        if self.type == 'n':
            self._tf = 1.
        elif self.type == 'p':
            self._tf = -1.
            # Change parameter default values
            if not self.is_set('u0'):
                self.u0 = 250
        else:
            raise cir.CircuitError(
                '{0}: unrecognized type: {1}. Valid types are "n" or "p"'.format(self.nodeName, self.type))

        # Nominal abs temperature
        self._Tn = const.T0 + self.tnom
        # Nominal Thermal voltage
        self._Vtn = const.k * self._Tn / const.q

        self.factor1 = np.sqrt(const.epSi / const.epOx * self.tox)
        Eg0 = 1.16 - 7.02e-4 * (self._Tn**2) / (self._Tn + 1108.0)
        ni = 1.45e10 * (self._Tn / 300.15) * np.sqrt(self._Tn / 300.15) \
            * np.exp(21.5565981 - Eg0 / (2. * self._Vtn))
        #self.esi = 11.7 * const.epsilon0 (replaced by const.epSi)
        self.ldrn = self.l
        self.wdrn = self.w
        
        t0 = pow(self.ldrn, self.lln)
        t1 = pow(self.wdrn, self.lwn)
        tmp1 = self.ll / t0 + self.lw / t1 + self.lwl / (t0 * t1)
        self.dl = self.lint + tmp1
        #tmp2 = llc / t0 + lwc / t1 + lwlc / (t0 * t1)
        #self.dlc = dlc + tmp2  # ???
        
        t2 = pow(self.ldrn, self.wln)
        t3 = pow(self.wdrn, self.wwn)
        tmp3 = self.wl / t2 + self.ww / t3 + self.wwl / (t2 * t3)
        self.dw = self.wint + tmp3
        #tmp4 = wlc / t2 + wwc / t3 + wwlc / (t2 * t3)
        #self.dwc = dwc + tmp4 # ???
        
        self.leff = self.l - 2.0 * self.dl
        self.weff = self.w - 2.0 * self.dw

        self.AbulkCVfactor = (1. + pow(self.clc/self.leff, self.cle))

        #self.leffCV = l - 2.0 * dlc
        #self.t11 = leffCV * leffCV

        # was epOx = 3.453133e-11
        self.cox = const.epOx / self.tox

        self.phi = 2. * self._Vtn * np.log(self.nch / ni)
        self.sqrtPhi = np.sqrt(self.phi)
        self.phis3 = self.sqrtPhi * self.phi

        self.Xdep0 = np.sqrt(2. * const.epSi / 
                          (const.q * self.nch * 1e6)) * self.sqrtPhi
        self.litl = np.sqrt(3. * self.xj * self.tox)
        self.vbi = self._Vtn * np.log(1.0e20 * self.nch / (ni**2))
        self.cdep0 = np.sqrt(const.q * const.epSi * self.nch * 1e6 
                          / 2. / self.phi)

        if not self.is_set('toxm'):
            self.toxm = self.tox
        if not self.is_set('dsub'):
            self.dsub = self.drout

        self.ldeb = np.sqrt(const.epSi * self._Vtn
                           / (const.q * self.nch * 1e6)) / 3.
        
        #import pdb; pdb.set_trace()
        if self.k1enable and not (self.is_set('k1') or self.is_set('k2')):
            vbx = self.phi - 7.7348e-4  * self.nch * self.xt**2
            # From ngspice
            vbx = -abs(vbx)
            Vbm = -abs(self.vbm)
            gamma1 = 5.753e-12 * np.sqrt(self.nch) / self.cox
            gamma2 = 5.753e-12 * np.sqrt(self.nsub) / self.cox
            T0 = gamma1 - gamma2
            T1 = np.sqrt(self.phi - vbx) - self.sqrtPhi
            T2 = np.sqrt(self.phi * (self.phi - Vbm)) - self.phi
            self._k2 = T0 * T1 / (2. * T2 + Vbm)
            self._k1 = gamma2 - 2. * self._k2 * np.sqrt(self.phi - Vbm)
            # print self._k1, self._k2
        else:
            self._k1 = self.k1
            self._k2 = self.k2
        
        if not self.is_set('vth0'):
            self._vth0 = self.vfb + self.phi + self._k1 * self.sqrtPhi
        else:
            self._vth0 = abs(self.vth0)

        self.k1ox = self._k1 * self.tox / self.toxm
        self.k2ox = self._k2 * self.tox / self.toxm

        t1 = np.sqrt(const.epSi / const.epOx * self.tox * self.Xdep0)
        t0 = ad.safe_exp(-0.5 * self.dsub * self.leff / t1)
        self.theta0vb0 = (t0 + 2.0 * t0**2)
        # From freeda, ngspice:
        self._Tox = 1e8 * self.tox
        
        #Calculation of vbsc(Vbc) and Vbseff
        if self._k2 < 0.:
            self.vbsc = .9 * (self.phi - (.5 * self._k1 / self._k2)**2)
            if self.vbsc > -3.:
                self.vbsc = -3.
            elif self.vbsc < -30.:
                self.vbsc = -30.
        else:
            self.vbsc = -30.

        if self.u0 > 1.:
            self._u0 = self.u0 * 1e-4
        else:
            self._u0 = self.u0 

        if not thermal:
            # Calculate temperature-dependent variables
            self.set_temp_vars(self.temp)
コード例 #11
0
ファイル: mosEKV.py プロジェクト: manasdas17/cardoon
    def eval_cqs(self, vPort, getOP=False):
        """
        Calculates Ids, Idb, Isb currents and D, G, S, charges. 

        Input:  vPort = [vdb , vgb , vsb]

        Output: vector with Ids, Idb, Isb currents and vector with D,
        G, S charges.
        
        If getOP = True, return normal output vector plus operating
        point variables in tuple: (iVec, qVec, opV)
        """
        # Invert all voltages in case of a P-channel device
        vPort1 = self._tf * vPort
        # If vds is negative, swap Vd and Vs and (also swap charges
        # later)
        DtoSswap = ad.condassign(vPort1[0] - vPort1[2], 1., -1.)
        # perform the swap (need tmp variable)
        tmp = ad.condassign(DtoSswap, vPort1[0], vPort1[2])
        vPort1[2] = ad.condassign(DtoSswap, vPort1[2], vPort1[0])
        vPort1[0] = tmp

        # Effective gate voltage including reverse short channel effect
        vgprime =  vPort1[1] - self._vt0a - self._deltavRSCE \
            + self.phiT + self._gammaa * np.sqrt(self.phiT)
        # Effective substrate factor including charge-sharing for
        # short and narrow channels.  Pinch-off voltage for
        # narrow-channel effect
        vp0 = vgprime - self.phiT - \
            self._gammaa * (np.sqrt(vgprime + self._gammaa*self._gammaa / 4.)
                            - self._gammaa / 2.)
        vp0 = ad.condassign(vgprime, vp0, -self.phiT)

        # Effective substrate factor accounting for charge-sharing
        tmp = 16. * self._Vt * self._Vt
        vsprime = 0.5 * (vPort1[2] + self.phiT +
                         np.sqrt(pow(vPort1[2] + self.phiT, 2) + tmp))
        vdprime = 0.5 * (vPort1[0] + self.phiT +
                         np.sqrt(pow(vPort1[0] + self.phiT, 2) + tmp))

        tmp = self.leta / self._leff * (np.sqrt(vsprime) + np.sqrt(vdprime))
        tmp -= 3. * self.weta * np.sqrt(vp0 + self.phiT) / self._weff
        gammao = self._gammaa - const.epSi / self.cox * tmp

        gammaprime = 0.5 * (gammao + np.sqrt(gammao * gammao + 0.1 * self._Vt))

        # Pinch-off voltage including short- and narrow-channel effect
        vp = vgprime - self.phiT
        vp -= gammaprime * (np.sqrt(vgprime + pow(.5 * gammaprime, 2)) -
                            gammaprime / 2.)
        vp = ad.condassign(vgprime, vp, -self.phiT)

        # Slope factor
        n = 1 + self._gammaa / (2. * np.sqrt(vp + self.phiT + 4. * self._Vt))

        # Forward normalized current
        i_f = (vp - vPort1[2]) / self._Vt
        i_f = self.interp(i_f)

        # Velocity saturation voltage
        vdss = np.sqrt(0.25 + self._Vt * np.sqrt(i_f) / self._vc) - 0.5
        vdss *= self._vc

        # Drain-to-source saturation voltage for reverse normalized current
        tmp = np.sqrt(i_f) - 0.75 * np.log(i_f)
        vdssprime = np.sqrt(0.25 + self._Vt * tmp / self._vc) - 0.5
        vdssprime *= self._vc
        vdssprime += self._Vt * (np.log(self._vc / (2. * self._Vt)) - 0.6)

        # Channel-length modulation
        tmp = np.sqrt(i_f) - vdss / self._Vt
        deltav = np.sqrt(self.Lambda * tmp + 1. / 64)
        deltav *= 4. * self._Vt
        vds = .5 * (vPort1[0] - vPort1[2])
        vip = np.sqrt(vdss * vdss + deltav * deltav)
        vip -= np.sqrt(pow(vds - vdss, 2) + deltav * deltav)
        deltal = np.log(1. + (vds - vip) / self._lc / self._t_ucrit)
        deltal *= self.Lambda * self._lc

        # Equivalent channel length including channel-length
        # modulation and velocity saturation
        lprime = self.ns * self._leff - deltal + (vds + vip) / self._t_ucrit
        leq = 0.5 * (lprime +
                     np.sqrt(lprime * lprime + self._lmin * self._lmin))

        # Reverse normalized current
        tmp = vp - vds - vPort1[2]
        tmp -= np.sqrt(vdssprime * vdssprime + deltav * deltav)
        tmp += np.sqrt(pow(vds - vdssprime, 2) + deltav * deltav)
        irprime = self.interp(tmp / self._Vt)

        # Reverse normalized currect for mobility model, intrinsic
        # charges/capacitances, thermal noise model and NQS time-constant ???
        ir = self.interp((vp - vPort1[0]) / self._Vt)

        # Quasi-static model equations
        # Dynamic model for the intrinsic node charges
        sqvpphi = np.sqrt(vp + self.phiT + 1.e-6)
        nq = 1. + self._gammaa / 2. / sqvpphi

        # Normalized intrinsic node charges
        xf = np.sqrt(0.25 + i_f)
        xr = np.sqrt(0.25 + ir)
        tmp = pow(xf + xr, 2)

        qd = 3. * xr**3 + 6. * xr * xr * xf + 4. * xr * xf * xf + 2. * xf**3
        qd *= 4. / 15. / tmp
        qd = -nq * (qd - .5)

        qs = 3. * xf**3 + 6. * xf * xf * xr + 4. * xf * xr * xr + 2. * xr**3
        qs *= 4. / 15. / tmp
        qs = -nq * (qs - .5)

        qi = qs + qd

        qb1 = -self._gammaa * sqvpphi / self._Vt
        qb1 -= (nq - 1.) * qi / nq
        qb = ad.condassign(vgprime, qb1, -vgprime / self._Vt)
        # qg = -qi - qox - qb, but qox == 0, so:
        qg = -qi - qb - self.qox

        # Transconductance factor and mobility reduction due to vertical field
        betao = self.kpa * self.np * self._weff / leq
        if self.theta != 0.:
            # Simple mobility reduction model
            vpprime = 0.5 * (vp + np.sqrt(vp * vp + 2. * self._Vt * self._Vt))
            beta = betao / (1. + theta * vpprime)
        else:
            # Rigorous mobility reduction model
            betaoprime = 1. + self.cox * self._qb0 / self.e0 / const.epSi
            betaoprime *= betao
            tmp = np.abs(qb + self.eta * qi)
            tmp = 1. + self.cox * self._Vt * tmp / self.e0 / const.epSi
            beta = betaoprime / tmp

        # Specific current
        IS = 2. * n * beta * self._Vt * self._Vt

        # Drain-to-source current
        ids = IS * (i_f - irprime)

        # import pdb; pdb.set_trace()

        # Impact ionization current
        vib = vPort1[0] - vPort1[2] - 2. * self.ibn * vdss
        idb1 = ids * self.iba * vib / self._t_ibb
        idb1 *= ad.safe_exp(-self._t_ibb * self._lc / vib)
        idb = ad.condassign(vib, idb1, 0.)

        # -------------------------------------------------------------
        # Create output vectors
        qVec = np.array([0., qg, 0.], dtype=type(idb))
        # have to switch charges if Drain and Source voltages switched
        qVec[0] = ad.condassign(DtoSswap, qd, qs)
        qVec[2] = ad.condassign(DtoSswap, qs, qd)
        # De-normalize charge and invert if needed
        qVec *= self._Cox * self._Vt * self._tf

        iVec = np.array([0., 0., 0.], dtype=type(idb))
        iVec[0] = DtoSswap * ids
        iVec[1] = ad.condassign(DtoSswap, idb, 0.)
        iVec[2] = ad.condassign(DtoSswap, 0., idb)
        # Revert currents if needed
        iVec *= self._tf

        #--------------------------------------------------------------
        # Operating point information
        if getOP:
            # Vth
            Vth = self._vt0a + self._deltavRSCE \
                + gammaprime * np.sqrt(vsprime) \
                - self._gammaa * np.sqrt(self.phiT)
            # Non quasi-static equations
            tau0 = .5 * self._Cox / self._Vt / beta
            tmp = (xf**2 + 3. * xf * xr + xr**2) / pow(xf + xr, 3)
            tau = tau0 * 4. / 15. * tmp
            # Create operating point variables dictionary
            return {
                'Vp': vp,
                'n': n,
                'Beta': beta,
                'IS': IS,
                'IF': i_f,
                'IR': ir,
                'IRprime': irprime,
                'tef': 1. / (np.sqrt(.25 + i_f) + .5),
                'Vth': Vth,
                'Vov': self._tf * n * (vp - vPort[2]),
                'Vdsat': self._tf * self._Vt * (2. * np.sqrt(i_f) + 4.),
                'tau0': tau0,
                'tau': tau,
                'Sthermal': self._kSt * beta * np.abs(qi),
                'Reversed': DtoSswap < 0.
            }
        else:
            return (iVec, qVec)
コード例 #12
0
ファイル: mosBSIM3v3.py プロジェクト: manasdas17/cardoon
    def eval_cqs(self, vPort, getOP = False):
        """
        Calculates currents and charges

        Input: vPort = [vdb , vgb , vsb]
        Output: iVec = [ids, idb, isb], qVec = [qd, qg, qs]

        If getOP = True, return normal output vector plus operating
        point variables in tuple: (iVec, qVec, opV)
        """
        # import pdb; pdb.set_trace()
        # Invert all voltages in case of a P-channel device
        vPort1 = self._tf * vPort
        # If vds is negative, swap Vd and Vs and (also swap charges
        # later)
        DtoSswap = ad.condassign(vPort1[0] - vPort1[2], 1., -1.)
        # perform the swap (need tmp variable)
        tmp = ad.condassign(DtoSswap, vPort1[0], vPort1[2])
        vPort1[2] = ad.condassign(DtoSswap, vPort1[2], vPort1[0])
        vPort1[0] = tmp

        # Calculate VDS, VGS and VBS for bsim model
        VDS = vPort1[0] - vPort1[2]
        VGS = vPort1[1] - vPort1[2]
        VBS = -vPort1[2]

        # ----------------------------------------------------------------
        T0 = VBS - self.vbsc - 0.001 
        T1 = np.sqrt(T0 * T0 - 0.004 * self.vbsc)
        Vbseff = self.vbsc + .5 * (T0 + T1)
        Vbseff = ad.condassign(-Vbseff + VBS,
                                VBS,
                                Vbseff)
        
        #Calculation of Phis, sqrtPhis and Xdep
        Phis = ad.condassign(Vbseff,
                             self.phi**2 / (self.phi + Vbseff),
                             self.phi - Vbseff)
        
        sqrtPhis = ad.condassign(Vbseff,
                                 self.phis3 / (self.phi + 0.5 * Vbseff),
                                 np.sqrt(Phis))
        
        Xdep = self.Xdep0 * sqrtPhis / self.sqrtPhi
        
        #Calculation of Threshold voltage-vth
        T3 = np.sqrt(Xdep)
        T1 = ad.condassign(self.dvt2 * Vbseff + .5,
                           1. + self.dvt2 * Vbseff,
                           (1. + 3. * self.dvt2 * Vbseff) 
                           / (3. + 8. * self.dvt2 * Vbseff))

        ltl = self.factor1 * T3 * T1

        T1 = ad.condassign(self.dvt2w * Vbseff + .5,
                           1. + self.dvt2w * Vbseff,
                           (1. + 3. * self.dvt2w * Vbseff) 
                           / (3. + 8. * self.dvt2w * Vbseff))

        ltw = self.factor1 * T3 * T1
        
        # Alternative to prevent overflow (apparently not needed)
        #T2 = ad.safe_exp(-.5 * self.dvt1 * self.leff / ltl)
        k_temp = -.5 * self.dvt1 * self.leff / ltl
        T2 = ad.condassign(k_temp + EXP_THRESHOLD,
                           ad.safe_exp(k_temp),
                           MIN_EXP)
        Theta0 =  T2 * (1. + 2. * T2)

        thetavth = self.dvt0 * Theta0
        Delt_vth = thetavth * self.V0

        # Alternative to prevent overflow (apparently not needed)
        #T2 = ad.safe_exp(-.5 * self.dvt1w * self.weff * self.leff / ltw)
        # Modified from freeda's source to prevent using uninitialized
        # variable
        k_temp = -.5 * self.dvt1w * self.weff * self.leff / ltw
        T2 = ad.condassign(k_temp + EXP_THRESHOLD,
                           ad.safe_exp(k_temp),
                           MIN_EXP)
        T2 *= (1. + 2. * T2)
                
        T0 = self.dvt0w * T2
        T2 = T0 * self.V0

        T0 = np.sqrt(1. + self.nlx / self.leff)
        T1 = self.k1ox * (T0 - 1.) * self.sqrtPhi \
            + (self.kt1 + self.kt1l / self.leff + self.kt2 * Vbseff) \
            * self._ToTnm1
        TMP2 = self.tox * self.phi / (self.weff + self.w0)
        
        T3 = self.eta0 + self.etab * Vbseff
        T4 = ad.condassign(-T3 + 1.0e-4,
                            1. / (3. - 2e4 * self.eta0 + self.etab * Vbseff),
                            1.)

        dDIBL_Sft_dVd = T3 * self.theta0vb0
        DIBL_Sft = dDIBL_Sft_dVd * VDS
        
        Vth = self._vth0 - self._k1 * self.sqrtPhi + self.k1ox * sqrtPhis \
            - self.k2ox * Vbseff - Delt_vth - T2 \
            + (self.k3 + self.k3b * Vbseff) * TMP2 + T1 - DIBL_Sft
        
        #Calculate n
        temp_tmp2 = self.nfactor * const.epSi / Xdep
        temp_tmp3 = self.cdsc + self.cdscb * Vbseff + self.cdscd * VDS
        temp_tmp4 = (temp_tmp2 + temp_tmp3 * Theta0 + self.cit) / self.cox
        
        n = ad.condassign(temp_tmp4 + .5,
                          1. + temp_tmp4,
                          (1. + 3. * temp_tmp4) \
                              * (1. / (3. + 8. * temp_tmp4)))
                          
        #Poly Gate Si Depletion Effect
        Vgs_eff = VGS
        
        Vgst = Vgs_eff - Vth # not in Nikhil's code
        
        #Effective Vgst (Vgsteff) Calculation
        T10 = 2. * n * self._Vt
        VgstNVt = Vgst / T10
        ExpArg = -(2. * 0.08 + Vgst) / T10
        
        T1 = T10 * log1pexp(VgstNVt)
        T2 = 1. + T10 * self.cox * ad.safe_exp(ExpArg) / self._Vt / self.cdep0
        
        Vgsteff = ad.condassign(VgstNVt - EXP_THRESHOLD,
                                Vgst,
                                T1 / T2)
        Vgsteff = ad.condassign(
            ExpArg - EXP_THRESHOLD,
            self._Vt * self.cdep0 \
                / self.cox / ad.safe_exp((Vgst + 0.08) / n / self._Vt),
            T1 / T2)
        
        T3 = T2 * T2
        
        # Calculate Effective Channel Geometry
        T9 = sqrtPhis - self.sqrtPhi
        k_temp = self.weff - 2. * (self.dwg * Vgsteff + self.dwb * T9)
        
        Weff = ad.condassign(-k_temp + 2.0e-8,
                              2e-8 * (4.0e-8 - k_temp) * T0,
                              k_temp)

        T0 = self.prwg * Vgsteff + self.prwb * (sqrtPhis - self.sqrtPhi)
        Rds = ad.condassign(T0 + 0.9,
                            self.rds0 * (1. + T0),
                            self.rds0 * (.8 +T0) / (17. + 20. * T0))

        #Calculate Abulk
        T1 = 0.5 * self.k1ox / sqrtPhis
        T9 = np.sqrt(self.xj * Xdep)
        T5 = self.leff / (self.leff + 2. * T9)
        T2 = (self.a0 * T5) + self.b0 / (self.weff + self.b1)
        T6 = T5 * T5
        T7 = T5 * T6
        
        Abulk0 = 1. + T1 * T2
        
        T8 = self.ags * self.a0 * T7
        Abulk = Abulk0 - T1 * T8 * Vgsteff

        Abulk0 = ad.condassign(-Abulk0 + .1,
                                (.2 - Abulk0) / (3. - 20. * Abulk0),
                                Abulk0)

        Abulk = ad.condassign(-Abulk + .1,
                               (.2 - Abulk) / (3. - 20. * Abulk),
                               Abulk)
        
        T2 = self.keta * Vbseff
        
        T0 = ad.condassign(T2 + 0.9,
                           1. / (1. + T2),
                           (17. + 20. * T2) / (0.8 + T2))

        Abulk *= T0
        Abulk0 *= T0
        
        T0 = Vgsteff + 2. * Vth
        T2 = self._ua + self._uc * Vbseff
        T3 = T0 / self.tox
        T5 = T3 * (T2 + self._ub * T3)
        
        Denomi =  ad.condassign(T5 + .8,
                                1. + T5,
                                (.6 + T5) / (7. + 10. * T5))

        ueff = self.u0temp / Denomi
        
        Esat = 2. * self.vsattemp / ueff
        
        # Saturation Drain Voltage Vdsat
        WVCox = Weff * self.vsattemp * self.cox
        WVCoxRds = WVCox * Rds
        
        EsatL = Esat * self.leff
        Lambda = self.a2
        
        Vgst2Vtm = Vgsteff + 2. * self._Vt
        
        T0 = 1. / (Abulk * EsatL + Vgst2Vtm)
        T3 = EsatL * Vgst2Vtm
        Vdsat = T3 * T0
        
        # Effective Vds(Vdseff) Calculation
        T1 = Vdsat - VDS - self.delta
        T2 = np.sqrt(T1**2 + 4. * self.delta * Vdsat)
        #T0 = T1 / T2
        #T3 = 2. * self.delta / T2
        k_temp = Vdsat - .5 * (T1 + T2)
        Vdseff = ad.condassign(k_temp - VDS,
                               VDS,
                               k_temp)

        # The following seems unnnecessary:
        # Added to eliminate non-zero Vdseff at Vds=0.0
        #Vdseff = ad.condassign(abs(VDS),
        #                       Vdseff,
        #                       0.)
        
        # Calculate Vasat
        T6 = 1. - .5 * Abulk * Vdsat / Vgst2Vtm #T6=tmp4
        T9 = WVCoxRds * Vgsteff #expanded
        T0 = EsatL + Vdsat + 2. * T9 * T6
        
        T9 = WVCoxRds * Abulk
        T1 = 2. / Lambda - 1. + T9
        
        Vasat = T0 / T1
        
        diffVds = VDS - Vdseff
        
        #Calculate VACLM
        VACLM = ad.condassign(
            (diffVds - 1.0e-10) * self.pclm,
            self.leff * (Abulk + Vgsteff / EsatL) * diffVds \
                / (self.pclm * Abulk * self.litl),
            MAX_EXP)

        #Calculate VADIBL
        T1 = np.sqrt(const.epSi / const.epOx * self.tox * self.Xdep0)
        
        T0 = ad.safe_exp(-.5 * self.drout * self.leff / T1)
        T2 = T0 + 2. * T0**2
        thetaRout = self.pdibl1 * T2 + self.pdibl2 #drout, pdibl1,
                                                   #pdibl2 are given
        VADIBL = ad.condassign(
            thetaRout,
            (Vgst2Vtm - Vgst2Vtm * Abulk * Vdsat / (Vgst2Vtm + Abulk * Vdsat)) \
                / thetaRout,
            MAX_EXP)
            
        VADIBL = ad.condassign(self.pdiblb * Vbseff + 0.9,
                               VADIBL / (1. + self.pdiblb * Vbseff),
                               VADIBL * (17. + 20. * self.pdiblb * Vbseff) \
                                   / (.8 + self.pdiblb * Vbseff))
                               
        #Calculate Va
        T8 = self.pvag / EsatL
        T9 = T8 * Vgsteff
        
        T0 = ad.condassign(T9 + 0.9,
                           1. + T9,
                           (.8 + T9) / (17. + 20. * T9))
        
        T3 = VACLM + VADIBL #tmp3 = T3
        T1 = VACLM * VADIBL / T3
        Va = Vasat + T0 * T1
        
        #Calculate VASCBE
        if self.pscbe1 != 0.:
            rcpVASCBE = ad.condassign(
                abs(diffVds),
                self.pscbe2 * ad.safe_exp(-self.pscbe1 * self.litl/diffVds) \
                    / self.leff,
                0.)
        else:
            rcpVASCBE = self.pscbe2 / self.leff

        # Original:
        #VASCBE = ad.condassign(
        #    diffVds - self.pscbe1 * self.litl / EXP_THRESHOLD,
        #    self.leff * np.exp(self.pscbe1 * self.litl/diffVds) / self.pscbe2,
        #    MAX_EXP * self.leff / self.pscbe2)

        #Calculate Ids
        CoxWovL = self.cox * Weff / self.leff
        beta = ueff * CoxWovL
        
        T0 = 1. - .5 * Abulk * Vdseff / Vgst2Vtm
        
        fgche1 = Vgsteff * T0
        
        T9 = Vdseff / EsatL
        fgche2 = 1. + T9
        
        gche = beta * fgche1 / fgche2
        
        T0 = 1. + gche * Rds
        T9 = Vdseff / T0
        Idl = gche * T9
        
        T9 = diffVds / Va
        T0 = 1. + T9
        Idsa = Idl * T0
        
        T9 = diffVds * rcpVASCBE
        T0 = 1. + T9
        Ids = Idsa * T0
        
        #Substrate current begins
        T1 = self.alpha0 + self.alpha1 * self.leff
        k_temp = T1 / self.leff * diffVds
        #T2 = k_temp * ad.safe_exp(-self.beta0 / diffVds)
        # Original T2:
        ad.condassign(
            diffVds - self.beta0 / EXP_THRESHOLD,
            k_temp * ad.safe_exp(-self.beta0 / diffVds),
            k_temp * MIN_EXP)

        k_temp = ad.condassign(T1, T2 * Idsa, 0.)
        Isub = ad.condassign(self.beta0, k_temp, 0.)
        
        # ********* Output current vector **************
        iVec = np.array([0., 0., 0.], dtype=type(Ids))
        iVec[0] = DtoSswap * Ids
        iVec[1] = ad.condassign(DtoSswap, Isub, 0.)
        iVec[2] = ad.condassign(DtoSswap, 0., Isub)
        # Revert currents if needed
        iVec *= self._tf
        # **********************************************

        #--------------------------------------------------------------
        # Charge calculation follows (does not work):

        #calculation of vfbzb
        T0 = -.5 * self.dvt1w * self.weff * self.leff \
            / self.factor1 / np.sqrt(self.Xdep0)
        
        #T2 = ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0))
        T2 = ad.condassign(T0 + EXP_THRESHOLD,
                           ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0)),
                           MIN_1)
        T0 = self.dvt0w * T2
        T2 = T0 * (self.vbi - self.phi)
        
        T0 = -.5 * self.dvt1 * self.leff / (self.factor1 * np.sqrt(self.Xdep0))

        #T3 = ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0))
        T3 = ad.condassign(T0 + EXP_THRESHOLD,
                           ad.safe_exp(T0) * (1. + 2. * ad.safe_exp(T0)),
                           MIN_1)
        
        T3 = self.dvt0 * T3 * (self.vbi - self.phi)
        
        T4 = self.tox * self.phi / (self.weff + self.w0)
        T5 = self.k1ox * (T0 - 1.) * self.sqrtPhi \
            + (self.kt1 + self.kt1l / self.leff) * self._ToTnm1

        T0 = np.sqrt(1. + self.nlx / self.leff)
        T6 = self._vth0 - T2 -T3 + self.k3 * T4 + T5
        vfbzb = T6 - self.phi - self._k1 * self.sqrtPhi
        
        #Calculation for VbseffCV
        VbseffCV = ad.condassign(Vbseff,
                                 self.phi - Phis,
                                 Vbseff)
        
        #Calculation for VgsteffCV
        T0 = n * self.noff * self._Vt
        T1 = (Vgs_eff - Vth) / T0

        Vgsteff = ad.condassign(T1 - EXP_THRESHOLD,
                                Vgs_eff - Vth - self.voffcv,
                                T0 * log1pexp(T1))
                
        # This (after the previous) can not be right:
        #Vgsteff = ad.condassign(-T1 - EXP_THRESHOLD,
        #                         T0 * log(1. + MIN_EXP),
        #                         T0 * log(1. + np.exp(T1)))

        #Calculation for Vfbeff
        V3 = vfbzb - Vgs_eff + VbseffCV - .02
        T0 = np.sqrt(V3**2 + .08 * abs(vfbzb))
        Vfbeff = vfbzb - 0.5 * (V3 + T0)
        
        T0 = (Vgs_eff - VbseffCV - vfbzb) / self._Tox
        
        #Calculation for Tcen
        T1 = T0 * self.acde
        
        Tcen = ad.condassign(EXP_THRESHOLD + T1,
                             self.ldeb * np.exp(T1),
                             self.ldeb * MIN_EXP)
        Tcen = ad.condassign(-EXP_THRESHOLD + T1,
                             self.ldeb * MAX_EXP,
                             Tcen)
        
        V3 = self.ldeb - Tcen - 1e-3 * self.tox
        V4 = np.sqrt(V3**2 + 4e-3 * self.tox * self.ldeb)
        Tcen = self.ldeb - .5 * (V3 + V4)
        Ccen = const.epSi / Tcen
        Coxeff = Ccen * self.cox / (Ccen + self.cox)
        
        #Calculation for QoverlapCox
        CoxWLcen = Weff * self.leff * Coxeff 
        Qac0 = CoxWLcen * (Vfbeff - vfbzb)
        # QovCox = Qac0 / Coxeff
        
        T0 = .5 * self.k1ox
        T3 = Vgs_eff - Vfbeff - VbseffCV - Vgsteff
        T1 = np.sqrt(T0 * T0 + T3)
        T2 = CoxWLcen * T0 / T1

        T1 = ad.condassign(-T3,
                            T0 + T3 / self.k1ox,
                            np.sqrt(T0**2 + T3))

        T2 = ad.condassign(-T3,
                            CoxWLcen,
                            CoxWLcen * T0 / T1)
        
        Qsub0 = CoxWLcen * self.k1ox * (T1 - T0)
        # QovCox = Qsub0 / Coxeff
        
        #Calculation for Delta_phis
        T2 = ad.condassign(self.k1ox,
                           self.moin * self._Vt * self.k1ox**2,
                           .25 * self.moin * self._Vt)

        T0 = ad.condassign(self.k1ox,
                           self.k1ox * np.sqrt(self.phi),
                           .5 * np.sqrt(self.phi))

        T1 = 2. * T0 + Vgsteff
        DeltaPhi = self._Vt * np.log(1. + T1 * Vgsteff / T2)
        
        #The calculation for Tcen must be done once more
        # fREEDA:
        #T0 = (Vgsteff + 4.*(self._vth0 - self.vfb - self.phi))/ (2. * self._Tox)
        # ngspice:
        T3 =  4. * (Vth - vfbzb - self.phi)
        T0 = ad.condassign(T3,
                           .5 * (Vgsteff + T3) / self._Tox,
                           .5 * (Vgsteff + 1.0e-20) / self._Tox)

        k_temp = 2.01375270747048 * T0  # was np.exp(.7 * log(T0))
        T1 = 1. + k_temp
        T2 = 0.35 * k_temp / (T0 * self._Tox)
        Tcen = 1.9e-9 / T1
        
        Ccen = const.epSi / Tcen
        Coxeff = Ccen * self.cox / (Ccen + self.cox)
        CoxWLcen = Weff * self.leff * Coxeff 

        AbulkCV = Abulk0 * self.AbulkCVfactor
        VdsatCV = (Vgsteff - DeltaPhi) / AbulkCV

        T0 = VdsatCV - VDS - .02
        T1 = np.sqrt(T0**2 + .08 * VdsatCV)

        # From ngspice: internal version BSIM3v32V32
        VdseffCV = VdsatCV - .5 * (T0 + T1)
        # From freeda:
#        VdseffCV = ad.condassign(T0,
#                                 VdsatCV - .5 * (T0 + T1),
#                                 VdsatCV * (1. - .04/(T1-T0)))
        # Need this to prevent NaN in charge Jacobian
        VdseffCV += 1e-200
                                 
        # Seems not needed
        #VdseffCV = ad.condassign(abs(VDS),
        #                         Vdseff,
        #                         0.)
        
        T0 = AbulkCV * VdseffCV
        T1 = Vgsteff - DeltaPhi
        T2 = 12. * (T1 - .5 * T0 + 1e-20)
        T3 = T0 / T2
        T4 = 1. - 12. * T3**2
        T5 = AbulkCV * (6. * T0 * (4. * T1 - T0) / (T2**2) - .5)
        T6 = T5 * VdseffCV / AbulkCV

        qgate = CoxWLcen * (T1 - T0 * (.5 - T3))
        qbulk = CoxWLcen * (1. - AbulkCV) * (.5*VdseffCV - T0*VdseffCV/T2)
        
        #QovCox = qbulk / Coxeff
        
        T2 = T2 / 12.
        T3 = .5 * CoxWLcen / (T2**2)
        T4 = T1 * (2. * T0**2 / 3. + T1*(T1 - 4. * T0 / 3.)) - 2. * T0**3 / 15.
        
        qsrc = -T3 * T4
        qgate += Qac0 + Qsub0 - qbulk
        qbulk -= (Qac0 + Qsub0)
        qdrn = -(qbulk + qgate + qsrc)
        
        # ************ Output charge vector ****************
        qVec = np.array([0., qgate, 0.], dtype=type(qsrc))
        # have to switch charges if Drain and Source voltages switched
        qVec[0] = ad.condassign(DtoSswap, qdrn, qsrc)
        qVec[2] = ad.condassign(DtoSswap, qsrc, qdrn)
        # invert sign if needed
        qVec *= self._tf

        # Return numpy array with one element per current source.
        return (iVec, qVec)
コード例 #13
0
ファイル: mosBSIM3v3.py プロジェクト: manasdas17/cardoon
    def process_params(self, thermal = False):
        # Called once the external terminals have been connected and
        # the non-default parameters have been set. Make sanity checks
        # here. Internal terminals/devices should also be defined
        # here.  Raise cir.CircuitError if a fatal error is found.
        ad.delete_tape(self)
        if self.type == 'n':
            self._tf = 1.
        elif self.type == 'p':
            self._tf = -1.
            # Change parameter default values
            if not self.is_set('u0'):
                self.u0 = 250
        else:
            raise cir.CircuitError(
                '{0}: unrecognized type: {1}. Valid types are "n" or "p"'.format(self.instanceName, self.type))

        # Nominal abs temperature
        self._Tn = const.T0 + self.tnom
        # Nominal Thermal voltage
        self._Vtn = const.k * self._Tn / const.q

        self.factor1 = np.sqrt(const.epSi / const.epOx * self.tox)
        Eg0 = 1.16 - 7.02e-4 * (self._Tn**2) / (self._Tn + 1108.0)
        ni = 1.45e10 * (self._Tn / 300.15) * np.sqrt(self._Tn / 300.15) \
            * np.exp(21.5565981 - Eg0 / (2. * self._Vtn))
        #self.esi = 11.7 * const.epsilon0 (replaced by const.epSi)
        self.ldrn = self.l
        self.wdrn = self.w
        
        t0 = pow(self.ldrn, self.lln)
        t1 = pow(self.wdrn, self.lwn)
        tmp1 = self.ll / t0 + self.lw / t1 + self.lwl / (t0 * t1)
        self.dl = self.lint + tmp1
        #tmp2 = llc / t0 + lwc / t1 + lwlc / (t0 * t1)
        #self.dlc = dlc + tmp2  # ???
        
        t2 = pow(self.ldrn, self.wln)
        t3 = pow(self.wdrn, self.wwn)
        tmp3 = self.wl / t2 + self.ww / t3 + self.wwl / (t2 * t3)
        self.dw = self.wint + tmp3
        #tmp4 = wlc / t2 + wwc / t3 + wwlc / (t2 * t3)
        #self.dwc = dwc + tmp4 # ???
        
        self.leff = self.l - 2.0 * self.dl
        self.weff = self.w - 2.0 * self.dw

        self.AbulkCVfactor = (1. + pow(self.clc/self.leff, self.cle))

        #self.leffCV = l - 2.0 * dlc
        #self.t11 = leffCV * leffCV

        # was epOx = 3.453133e-11
        self.cox = const.epOx / self.tox

        self.phi = 2. * self._Vtn * np.log(self.nch / ni)
        self.sqrtPhi = np.sqrt(self.phi)
        self.phis3 = self.sqrtPhi * self.phi

        self.Xdep0 = np.sqrt(2. * const.epSi / 
                          (const.q * self.nch * 1e6)) * self.sqrtPhi
        self.litl = np.sqrt(3. * self.xj * self.tox)
        self.vbi = self._Vtn * np.log(1.0e20 * self.nch / (ni**2))
        self.cdep0 = np.sqrt(const.q * const.epSi * self.nch * 1e6 
                          / 2. / self.phi)

        if not self.is_set('toxm'):
            self.toxm = self.tox
        if not self.is_set('dsub'):
            self.dsub = self.drout

        self.ldeb = np.sqrt(const.epSi * self._Vtn
                           / (const.q * self.nch * 1e6)) / 3.
        
        #import pdb; pdb.set_trace()
        if (self.k1enable != 0.) and \
                not (self.is_set('k1') or self.is_set('k2')):
            vbx = self.phi - 7.7348e-4  * self.nch * self.xt**2
            # From ngspice
            vbx = -abs(vbx)
            Vbm = -abs(self.vbm)
            gamma1 = 5.753e-12 * np.sqrt(self.nch) / self.cox
            gamma2 = 5.753e-12 * np.sqrt(self.nsub) / self.cox
            T0 = gamma1 - gamma2
            T1 = np.sqrt(self.phi - vbx) - self.sqrtPhi
            T2 = np.sqrt(self.phi * (self.phi - Vbm)) - self.phi
            self._k2 = T0 * T1 / (2. * T2 + Vbm)
            self._k1 = gamma2 - 2. * self._k2 * np.sqrt(self.phi - Vbm)
            # print self._k1, self._k2
        else:
            self._k1 = self.k1
            self._k2 = self.k2
        
        if not self.is_set('vth0'):
            self._vth0 = self.vfb + self.phi + self._k1 * self.sqrtPhi
        else:
            self._vth0 = abs(self.vth0)

        self.k1ox = self._k1 * self.tox / self.toxm
        self.k2ox = self._k2 * self.tox / self.toxm

        t1 = np.sqrt(const.epSi / const.epOx * self.tox * self.Xdep0)
        t0 = ad.safe_exp(-0.5 * self.dsub * self.leff / t1)
        self.theta0vb0 = (t0 + 2.0 * t0**2)
        # From freeda, ngspice:
        self._Tox = 1e8 * self.tox
        
        #Calculation of vbsc(Vbc) and Vbseff
        if self._k2 < 0.:
            self.vbsc = .9 * (self.phi - (.5 * self._k1 / self._k2)**2)
            if self.vbsc > -3.:
                self.vbsc = -3.
            elif self.vbsc < -30.:
                self.vbsc = -30.
        else:
            self.vbsc = -30.

        if self.u0 > 1.:
            self._u0 = self.u0 * 1e-4
        else:
            self._u0 = self.u0 

        if not thermal:
            # Calculate temperature-dependent variables
            self.set_temp_vars(self.temp)
コード例 #14
0
    def eval_cqs(self, vPort):
        """
        Calculates currents/charges

        Input is a vector may be one of the following, depending on
        parameter values::

          vPort = [vbe, vbc]
          vPort = [vbie, vbic, v1_i] (gyrator voltage, rb != 0)

        Output also depends on parameter values. Charges only present
        if parameters make them different than 0 (i.e., cje, tf, cjc,
        etc. are set to nonzero values)::
        
          iVec = [ibe, ibc, ice]
          iVec = [ibe, ibc, ice, gyr*ib*Rb] (rb != 0)

          qVec = [qbe, qbc]
          qVec = [qbe, qbc, qbx] (rb != 0 and cjc != 1)
        """
        # Invert control voltages if needed
        vPort1 = self._typef * vPort

        # Calculate regular PN junctions currents and charges
        ibf = self.jif.get_id(vPort1[0])
        ibr = self.jif.get_id(vPort1[1])
        if self.ise != 0.:
            ile = self.jile.get_id(vPort1[0])
        else:
            ile = 0.
        if self.isc != 0.:
            ilc = self.jilc.get_id(vPort1[1])
        else:
            ilc = 0.
        # Kqb
        q1m1 = 1.
        if self.var != 0.:
            q1m1 -= vPort1[0] / self.var
        if self.vaf != 0.:
            q1m1 -= vPort1[1] / self.vaf
        kqb = 1. / q1m1
        # We need extra checking to consider the following
        # possibilities to create the AD tape:
        #
        # 1. both ikf and ikr are zero -> no tape generated
        # 2. One of them is nonzero but both ibf and ibr are zero -> want tape
        #    but only for the nonzero parameter
        if self.ikf + self.ikr != 0.:
            q2 = 0.
            if self.ikf != 0.:
                q2 += ibf / self.ikf
            if self.ikr != 0.:
                q2 += ibr / self.ikr
            kqb *= .5 * (1. + np.sqrt(1. + 4. * q2))

        # Create output vector [ibe, ibc, ice, ...]
        iVec = np.zeros(self.ncurrents, dtype=type(ibf))
        qVec = np.zeros(self.ncharges, dtype=type(ibf))
        # ibe
        iVec[0] = ibf / self._bf_t + ile
        # ibc
        iVec[1] = ibr / self._br_t + ilc
        # ice
        iVec[2] = (ibf - ibr) / kqb

        # RB
        if self.rb != 0.:
            # Using gyrator
            # vPort1[2] not defined if rb == 0
            # ib has area effect included (removed by _ck1 and _ck2)
            ib = vPort1[2] * glVar.gyr
            if self.irb != 0.:
                ib1 = np.abs(ib)
                x = np.sqrt(1. + self._ck1 * ib1) - 1.
                x *= self._ck2 / np.sqrt(ib1)
                tx = np.tan(x)
                c = self.rbm + 3. * (self.rb - self.rbm) \
                    * (tx - x) / (x * tx * tx)
                rb = ad.condassign(ib1, c, self.rb)
            else:
                rb = self.rbm + (self.rb - self.rbm) / kqb
            # Output is gyr * ib * rb.  It is divided by area^2 to
            # compensate that the whole vector is multiplied by area
            # at the end
            iVec[3] = glVar.gyr * ib * rb / pow(self.area, 2)
            vbcx = ib * rb / self.area + vPort1[1]

        # Charges -----------------------------------------------

        # Note that if tf == 0 and cje == 0, nothing is calculated and
        # nothing is assigned to the output vector.

        # qbe is the first charge (0)
        if self.tf != 0.:
            # Effective tf
            tfeff = self.tf
            if self.vtf != 0.:
                x = ibf / (ibf + self.itf)
                tfeff *= (1. + self.xtf * x * x *
                          ad.safe_exp(vPort1[1] / 1.44 / self.vtf))
            qVec[0] = tfeff * ibf
        if self.cje != 0.:
            qVec[0] += self.jif.get_qd(vPort1[0])

        # qbc
        if self._qbx:
            if self.tr != 0.:
                qVec[-2] = self.tr * ibr
            if self.cjc != 0.:
                qVec[-2] += self.jir.get_qd(vPort1[1]) * self.xcjc
                # qbx
                qVec[-1] = self.jir.get_qd(vbcx) * (1. - self.xcjc)
        else:
            if self.tr != 0.:
                qVec[-1] = self.tr * ibr
            if self.cjc != 0.:
                qVec[-1] += self.jir.get_qd(vPort1[1])

        # Consider area effect and invert currents if needed
        iVec *= self.area * self._typef
        qVec *= self.area * self._typef

        return (iVec, qVec)