コード例 #1
0
ファイル: mesfetc.py プロジェクト: AndySze/cardoon
    def eval_cqs(self, vPort, saveOP=False):
        """
        Calculates gate and drain current. Input is a vector as follows:
        vPort = [vgs(t), vgd(t), vgs(t-tau), vgd(t-tau)]

        saveOP has no effect for now
        """
        # Calculate junction currents
        igs = self.diogs.get_id(vPort[0])
        qgs = self.diogs.get_qd(vPort[0])
        igd = self.diogd.get_id(vPort[1])
        qgd = self.diogd.get_qd(vPort[1])

        # Add breakdown current
        igs -= self.ib0 * np.exp(-(vPort[0] + self.vbd) / self._k6)
        igd -= self.ib0 * np.exp(-(vPort[1] + self.vbd) / self._k6)

        DtoSswap = ad.condassign(vPort[0] - vPort[1], 1.0, -1.0)
        vds = DtoSswap * (vPort[0] - vPort[1])
        vgsi = ad.condassign(DtoSswap, vPort[0], vPort[1])
        vgsiT = ad.condassign(DtoSswap, vPort[2], vPort[3])
        # Calculate ids.
        vx = vgsiT * (1.0 + self._Beta * (self.vds0 - vds))
        ids = (self.a0 + vx * (self.a1 + vx * (self.a2 + vx * self.a3))) * np.tanh(self.gama * vds) * self._idsFac
        # vgsiT makes more sense than vgsi below? (vgsi in original doc)
        ids = ad.condassign((vgsi - self._Vt0), ids, 0.0)
        # Must ensure ids > 0 for power conservation
        ids = ad.condassign(ids, ids, 0.0)

        # Return numpy array with one element per current source.
        iVec = np.array([igs, igd, ids * DtoSswap]) * self.area
        qVec = np.array([qgs, qgd]) * self.area

        return (iVec, qVec)
コード例 #2
0
ファイル: mesfetc.py プロジェクト: manasdas17/cardoon
    def eval_cqs(self, vPort, getOP=False):
        """
        Calculates gate and drain current. Input is a vector as follows:
        vPort = [vgs(t), vgd(t), vgs(t-tau), vgd(t-tau)]

        getOP has no effect for now
        """
        # Calculate junction currents
        igs = self.diogs.get_id(vPort[0])
        qgs = self.diogs.get_qd(vPort[0])
        igd = self.diogd.get_id(vPort[1])
        qgd = self.diogd.get_qd(vPort[1])

        # Add breakdown current
        igs -= self.ib0 * np.exp(-(vPort[0] + self.vbd) / self._k6)
        igd -= self.ib0 * np.exp(-(vPort[1] + self.vbd) / self._k6)

        DtoSswap = ad.condassign(vPort[0] - vPort[1], 1., -1.)
        vds = DtoSswap * (vPort[0] - vPort[1])
        vgsi = ad.condassign(DtoSswap, vPort[0], vPort[1])
        vgsiT = ad.condassign(DtoSswap, vPort[2], vPort[3])
        # Calculate ids.
        vx = vgsiT * (1. + self._Beta * (self.vds0 - vds))
        ids = (self.a0 + vx * (self.a1 + vx * (self.a2 + vx  * self.a3))) \
            * np.tanh(self.gama * vds) * self._idsFac
        # vgsiT makes more sense than vgsi below? (vgsi in original doc)
        ids = ad.condassign((vgsi - self._Vt0), ids, 0.)
        # Must ensure ids > 0 for power conservation
        ids = ad.condassign(ids, ids, 0.)

        # Return numpy array with one element per current source.
        iVec = np.array([igs, igd, ids * DtoSswap]) * self.area
        qVec = np.array([qgs, qgd]) * self.area

        return (iVec, qVec)
コード例 #3
0
ファイル: mosBSIM3v3.py プロジェクト: manasdas17/cardoon
def log1pexp(x):
    """
    Safe calculation of log(1 + exp(x))
    """
    y1 = ad.condassign(x - EXP_THRESHOLD,
                       x,
                       np.log(1.+np.exp(x)))
    return ad.condassign(-x -  EXP_THRESHOLD,
                          np.exp(x),
                          y1)
コード例 #4
0
ファイル: mosBSIM3v3.py プロジェクト: AndySze/cardoon
def log1pexp(x):
    """
    Safe calculation of log(1 + exp(x))
    """
    y1 = ad.condassign(x - EXP_THRESHOLD,
                       x,
                       np.log(1.+np.exp(x)))
    return ad.condassign(-x -  EXP_THRESHOLD,
                          np.exp(x),
                          y1)
コード例 #5
0
ファイル: svdiode.py プロジェクト: AndySze/cardoon
    def get_idvd(self, x):
        """
        Returns junction a tuple (current, voltage)

        x: state variable
        """
        # Static current
        b = self._svth - x
        c = self._t_is * (np.exp(self._alpha * x) - 1.0)
        d = self._t_is * self._kexp * (1.0 + self._alpha * (x - self._svth)) - self._t_is
        iD = ad.condassign(b, c, d)
        # Diode voltage
        d = self._svth + np.log(1.0 + self._alpha * (x - self._svth)) / self._alpha
        vD = ad.condassign(b, x, d)
        return (iD, vD)
コード例 #6
0
ファイル: mosACM.py プロジェクト: cechrist/cardoon
def inv_f(f):
    """
    Solve f^(-1)(i_f(r)) to get i_f and i_r using Newton's method

    Uses a fixed number of iterations for easy taping and a different
    formulation for f>0 and f<0 to ensure convergence in few
    iterations. Solution has good accuracy for all values.
    """
    # Obtain a good guess first using EKV's simple interpolation function
    i1 = 4. * f_simple(f)
    i2 = i1
    for counter in xrange(4):
        # (f > 0) => (i1 >= 3.) The 1e-15 term needed to prevent AD
        # library from choking as without it we have log(0) later
        sqi1p = np.sqrt(1. + i1 + 1e-15) 
        sqi11 = sqi1p - 1. 
        fodfp = (sqi1p - 2. + np.log(sqi11) - f) * 2. * sqi11
        i1 = abs(i1 - fodfp)
        # f < 0
        sqi1 = np.sqrt(1. + i2) 
        fodfn = (sqi1 - 1. - np.exp(f - sqi1 + 2.)) * 2. * sqi1 \
            / (np.exp(f - sqi1 + 2.) + 1.)
        i2 = i2 - fodfn

    return ad.condassign(f, i1, i2)
コード例 #7
0
ファイル: mosEKV.py プロジェクト: manasdas17/cardoon
    def set_temp_vars(self, temp):
        """
        Calculate temperature-dependent variables, given temp in deg. C
        """
        # Delete AD tape (if any)
        ad.delete_tape(self)
        # Absolute temperature (note self.temp is in deg. C)
        T = const.T0 + temp
        # Thermal voltage
        self._Vt = const.k * T / const.q
        # threshold voltage
        vt0T = self._vt0 - self._tcv * (T - self._Tn)
        self._vt0a = vt0T + self.avto / self._sga
        kpT = self.kp * pow(T / self._Tn, self.bex)
        self.kpa = kpT * (1 + self.akp / self._sga)
        # Clip to zero if negative
        self.kpa = ad.condassign(self.kpa, self.kpa, 0.)
        self._t_ucrit = self.ucrit * pow(T / self._Tn, self.ucex)
        # Energy gap
        egT = 1.16 - 0.000702 * T * T / (T + 1108.)
        self.phiT = self.phi * T / self.Tref \
            - 3. * self._Vt * np.log(T / self.Tref) \
            - self.egTref * T / self.Tref + egT
        self._t_ibb = self.ibb * (1. + self.ibbt * (T - self.Tref))

        self._vc = self._t_ucrit * self._leff * self.ns
        self._qb0 = self._gammaa * np.sqrt(self.phiT)
        # Noise variables
        self._kSt = 4. * const.k * T
コード例 #8
0
ファイル: mosEKV.py プロジェクト: cechrist/cardoon
    def set_temp_vars(self, temp):
        """
        Calculate temperature-dependent variables, given temp in deg. C
        """
        # Delete AD tape (if any)
        ad.delete_tape(self)
        # Absolute temperature (note self.temp is in deg. C)
        T = const.T0 + temp
        # Thermal voltage
        self._Vt = const.k * T / const.q
        # threshold voltage
        vt0T = self._vt0 - self._tcv * (T - self._Tn)
        self._vt0a = vt0T + self.avto / self._sga
        kpT = self.kp * pow(T / self._Tn, self.bex)
        self.kpa = kpT * (1 + self.akp / self._sga)
        # Clip to zero if negative
        self.kpa = ad.condassign(self.kpa, self.kpa, 0.0)
        self._t_ucrit = self.ucrit * pow(T / self._Tn, self.ucex)
        # Energy gap
        egT = 1.16 - 0.000702 * T * T / (T + 1108.0)
        self.phiT = (
            self.phi * T / self.Tref - 3.0 * self._Vt * np.log(T / self.Tref) - self.egTref * T / self.Tref + egT
        )
        self._t_ibb = self.ibb * (1.0 + self.ibbt * (T - self.Tref))

        self._vc = self._t_ucrit * self._leff * self.ns
        self._qb0 = self._gammaa * np.sqrt(self.phiT)
        # Noise variables
        self._kSt = 4.0 * const.k * T
コード例 #9
0
def inv_f(f):
    """
    Solve f^(-1)(i_f(r)) to get i_f and i_r using Newton's method

    Uses a fixed number of iterations for easy taping and a different
    formulation for f>0 and f<0 to ensure convergence in few
    iterations. Solution has good accuracy for all values.
    """
    # Obtain a good guess first using EKV's simple interpolation function
    i1 = 4. * f_simple(f)
    i2 = i1
    for counter in xrange(4):
        # (f > 0) => (i1 >= 3.) The 1e-15 term needed to prevent AD
        # library from choking as without it we have log(0) later
        sqi1p = np.sqrt(1. + i1 + 1e-15)
        sqi11 = sqi1p - 1.
        fodfp = (sqi1p - 2. + np.log(sqi11) - f) * 2. * sqi11
        i1 = abs(i1 - fodfp)
        # f < 0
        sqi1 = np.sqrt(1. + i2)
        fodfn = (sqi1 - 1. - np.exp(f - sqi1 + 2.)) * 2. * sqi1 \
            / (np.exp(f - sqi1 + 2.) + 1.)
        i2 = i2 - fodfn

    return ad.condassign(f, i1, i2)
コード例 #10
0
ファイル: svdiode.py プロジェクト: manasdas17/cardoon
    def get_idvd(self, x):
        """
        Returns junction a tuple (current, voltage)

        x: state variable
        """
        # Static current
        b = self._svth - x
        c = self._t_is * (np.exp(self._alpha * x) - 1.)
        d = self._t_is * self._kexp * \
            (1. + self._alpha * (x - self._svth)) - self._t_is
        iD = ad.condassign(b, c, d)
        # Diode voltage
        d = self._svth + \
            np.log(1. + self._alpha * (x - self._svth)) / self._alpha
        vD = ad.condassign(b, x, d)
        return (iD, vD)
コード例 #11
0
ファイル: svdiode.py プロジェクト: AndySze/cardoon
    def get_qd(self, vd):
        """
        Returns junction depletion charge

        vd: diode voltage
        """
        b = self.fc * self._t_vj - vd
        c = self._k5 * (1.0 - pow(1.0 - vd / self._t_vj, self._k4))
        d = self._k6 * (
            (1.0 - self.fc * (1.0 + self.m)) * vd + 0.5 * self.m * vd * vd / self._t_vj - self._k7
        ) + self._k5 * (1.0 - pow(1.0 - self.fc, self._k4))
        return ad.condassign(b, c, d)
コード例 #12
0
ファイル: svdiode.py プロジェクト: manasdas17/cardoon
    def get_qd(self, vd):
        """
        Returns junction depletion charge

        vd: diode voltage
        """
        b = self.fc * self._t_vj - vd
        c = self._k5 * (1. - pow(1. - vd / self._t_vj, self._k4))
        d = self._k6 * ((1. - self.fc * (1. + self.m))
                        * vd + .5 * self.m * vd * vd / self._t_vj
                        - self._k7) + self._k5 \
                        * (1. - pow(1. - self.fc, self._k4))
        return ad.condassign(b, c, d)
コード例 #13
0
ファイル: mosEKV.py プロジェクト: manasdas17/cardoon
def f_simple(v):
    """
    Simple interpolation function for F(v)

    Not accurate for moderate inversion
    """
    # Have to treat the function for large negative v specially
    # otherwise exp(.5*v) << 1 and we get log(1) = 0
    b = v + 20.
    d = np.exp(.5 * v)
    c = np.log(1. + d)
    # f = (b > -20) ? c : d
    f = ad.condassign(b, c, d)
    return f**2
コード例 #14
0
def inv_f1(f):
    """
    Approximately solve f^(-1)(i_f(r)) to get i_f and i_r using relaxation 
    """
    # Obtain a good guess first using EKV's simple interpolation function
    i_f = 4. * f_simple(f)
    # Use fixed number of iterations for easy taping
    for i in xrange(30):
        sqi1 = np.sqrt(1. + i_f + 1e-15)
        # Uses different formulation for f>0 and f<0 for convergence
        i_fnew = ad.condassign(f, (f + 2. - np.log(sqi1 - 1.))**2 - 1.,
                               (np.exp(f - sqi1 + 2.) + 1.)**2 - 1.)
        i_f = .5 * i_f + .5 * i_fnew
    return i_f
コード例 #15
0
ファイル: mosEKV.py プロジェクト: cechrist/cardoon
def f_simple(v):
    """
    Simple interpolation function for F(v)

    Not accurate for moderate inversion
    """
    # Have to treat the function for large negative v specially
    # otherwise exp(.5*v) << 1 and we get log(1) = 0
    b = v + 20.0
    d = np.exp(0.5 * v)
    c = np.log(1.0 + d)
    # f = (b > -20) ? c : d
    f = ad.condassign(b, c, d)
    return f ** 2
コード例 #16
0
ファイル: mosACM.py プロジェクト: cechrist/cardoon
def inv_f1(f):
    """
    Approximately solve f^(-1)(i_f(r)) to get i_f and i_r using relaxation 
    """
    # Obtain a good guess first using EKV's simple interpolation function
    i_f = 4. * f_simple(f)
    # Use fixed number of iterations for easy taping
    for i in xrange(30):
        sqi1 = np.sqrt(1. + i_f + 1e-15)
        # Uses different formulation for f>0 and f<0 for convergence
        i_fnew = ad.condassign(f, 
                               (f + 2. - np.log(sqi1 - 1.))**2 - 1.,
                               (np.exp(f - sqi1 + 2.) + 1.)**2 - 1.)
        i_f = .5 * i_f + .5 * i_fnew
    return i_f
コード例 #17
0
ファイル: diode.py プロジェクト: AndySze/cardoon
    def get_qd(self, vd):
        """
        Returns junction depletion charge

        vd: diode voltage
        """
        if self.cj0:
            b = self.fc * self._t_vj - vd
            c = self._k5 * (1. - pow(1. - vd / self._t_vj, self._k4))
            d = self._k6 * ((1. - self.fc * (1. + self.m)) 
                            * vd + .5 * self.m * vd * vd / self._t_vj 
                            - self._k7) + self._k5 \
                            * (1. - pow(1. - self.fc, self._k4))
            return ad.condassign(b, c, d)
        else:
            return 0. * vd
コード例 #18
0
ファイル: mosEKV.py プロジェクト: cechrist/cardoon
def f_accurate(v):
    """
    Calculate a more accurate value of F(v) 

    Eq. (41) and (42) in [1]
    
    Refine f_simple with a few Newton iterations. This function
    performs the Newton iterations even if not needed on purpose to
    store the operations in the AD tape.
    """
    # get initial estimate
    i = f_simple(v)
    # Apply 3 Newton iterations to refine approximation
    for j in range(3):
        k1 = np.sqrt(0.25 + i)
        f = v - (2.0 * k1 - 1.0 + np.log(k1 - 0.5))
        vprime = -1.0 / (2.0 * k1 * (0.5 - k1)) + 1.0 / k1
        i += f / vprime
    b = v + 20.0
    # If b is very negative we need this to avoid the singularity that
    # happens when i is very small: k1 = 0.5 and the log goes to infinity
    i = ad.condassign(b, i, np.exp(v))
    # print v, i, np.exp(v)
    return i
コード例 #19
0
ファイル: mosEKV.py プロジェクト: manasdas17/cardoon
def f_accurate(v):
    """
    Calculate a more accurate value of F(v) 

    Eq. (41) and (42) in [1]
    
    Refine f_simple with a few Newton iterations. This function
    performs the Newton iterations even if not needed on purpose to
    store the operations in the AD tape.
    """
    # get initial estimate
    i = f_simple(v)
    # Apply 3 Newton iterations to refine approximation
    for j in range(3):
        k1 = np.sqrt(0.25 + i)
        f = v - (2. * k1 - 1. + np.log(k1 - .5))
        vprime = -1. / (2. * k1 * (0.5 - k1)) + 1. / k1
        i += f / vprime
    b = v + 20.
    # If b is very negative we need this to avoid the singularity that
    # happens when i is very small: k1 = 0.5 and the log goes to infinity
    i = ad.condassign(b, i, np.exp(v))
    #print v, i, np.exp(v)
    return i
コード例 #20
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)
コード例 #21
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)
コード例 #22
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)
コード例 #23
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)
コード例 #24
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)
コード例 #25
0
ファイル: svbjt.py プロジェクト: cechrist/cardoon
    def eval_cqs(self, vPort):
        """
        Calculates currents/charges

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

          vPort = [xbe, xbc]
          vPort = [xbe, xbc, v4_i] (gyrator voltage, irb != 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, vbe, ibc, vbc, ice]
          iVec = [ibe, vbe, ibc, vbc, ice, gyr*ib*Rb] (rb != 0)

          qVec = [qbe, qbc]
          qVec = [qbe, qbc, qbx] (rb != 0 and cjc != 1)

        """
        # Invert state variables if needed
        vPort1 = self._typef * vPort

        # Calculate junctions currents and voltages
        (ibf, vbe) = self.jif.get_idvd(vPort1[0])
        (ibr, vbc) = self.jir.get_idvd(vPort1[1])
        if self.ise != 0.:
            ile = self.jile.get_id(vbe)
        else:
            ile = 0.
        if self.isc != 0.:
            ilc = self.jilc.get_id(vbc)
        else:
            ilc = 0.
        # Kqb
        q1m1 = 1.
        if self.var != 0.:
            q1m1 -= vbe / self.var
        if self.vaf != 0.:
            q1m1 -= vbc / 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, vbe
        iVec[0] = ibf / self._bf_t + ile
        iVec[1] = glVar.gyr * vbe
        # ibc, vbc
        iVec[2] = ibr / self._br_t + ilc
        iVec[3] = glVar.gyr * vbc
        # ice
        iVec[4] = (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[5] = glVar.gyr * ib * rb / pow(self.area, 2)
            vbcx = ib * rb / self.area + vbc

        # 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)
                # safe_exp() not needed since positive vbc grows
                # logarithmically
                tfeff *= (1. + self.xtf * x*x * 
                          np.exp(vbc /1.44 /self.vtf))
            qVec[0] = tfeff * ibf
        if self.cje != 0.:
            qVec[0] += self.jif.get_qd(vbe) 

        # qbc 
        if self._qbx:
            if self.tr != 0.:
                qVec[-2] = self.tr * ibr
            if self.cjc != 0.:
                qVec[-2] += self.jir.get_qd(vbc) * 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(vbc) 

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

        return (iVec, qVec)
コード例 #26
0
ファイル: svbjt.py プロジェクト: manasdas17/cardoon
    def eval_cqs(self, vPort):
        """
        Calculates currents/charges

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

          vPort = [xbe, xbc]
          vPort = [xbe, xbc, v4_i] (gyrator voltage, irb != 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, vbe, ibc, vbc, ice]
          iVec = [ibe, vbe, ibc, vbc, ice, gyr*ib*Rb] (rb != 0)

          qVec = [qbe, qbc]
          qVec = [qbe, qbc, qbx] (rb != 0 and cjc != 1)

        """
        # Invert state variables if needed
        vPort1 = self._typef * vPort

        # Calculate junctions currents and voltages
        (ibf, vbe) = self.jif.get_idvd(vPort1[0])
        (ibr, vbc) = self.jir.get_idvd(vPort1[1])
        if self.ise != 0.:
            ile = self.jile.get_id(vbe)
        else:
            ile = 0.
        if self.isc != 0.:
            ilc = self.jilc.get_id(vbc)
        else:
            ilc = 0.
        # Kqb
        q1m1 = 1.
        if self.var != 0.:
            q1m1 -= vbe / self.var
        if self.vaf != 0.:
            q1m1 -= vbc / 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, vbe
        iVec[0] = ibf / self._bf_t + ile
        iVec[1] = glVar.gyr * vbe
        # ibc, vbc
        iVec[2] = ibr / self._br_t + ilc
        iVec[3] = glVar.gyr * vbc
        # ice
        iVec[4] = (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[5] = glVar.gyr * ib * rb / pow(self.area, 2)
            vbcx = ib * rb / self.area + vbc

        # 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)
                # safe_exp() not needed since positive vbc grows
                # logarithmically
                tfeff *= (1. +
                          self.xtf * x * x * np.exp(vbc / 1.44 / self.vtf))
            qVec[0] = tfeff * ibf
        if self.cje != 0.:
            qVec[0] += self.jif.get_qd(vbe)

        # qbc
        if self._qbx:
            if self.tr != 0.:
                qVec[-2] = self.tr * ibr
            if self.cjc != 0.:
                qVec[-2] += self.jir.get_qd(vbc) * 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(vbc)

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

        return (iVec, qVec)