From a5b8aa535fd5f94da24f26bf4f4f6eef41b341eb Mon Sep 17 00:00:00 2001 From: Francesco Lannutti Date: Sun, 28 Jun 2015 21:49:23 +0200 Subject: [PATCH] Alpha Version of the new Reliability Analysis, based upon Giorgio Liatis' Model --- src/include/ngspice/cktdefs.h | 2 + src/include/ngspice/devdefs.h | 2 + src/spicelib/analysis/Makefile.am | 3 +- src/spicelib/analysis/cktdojob.c | 3 + src/spicelib/analysis/cktreliability.c | 24 +++ src/spicelib/analysis/dctran.c | 11 ++ src/spicelib/devices/cap/capinit.c | 5 +- src/spicelib/devices/mos1/Makefile.am | 3 +- src/spicelib/devices/mos1/mos1defs.h | 9 + src/spicelib/devices/mos1/mos1ext.h | 2 + src/spicelib/devices/mos1/mos1init.c | 5 +- src/spicelib/devices/mos1/mos1reliability.c | 182 ++++++++++++++++++++ src/spicelib/devices/mos1/mos1set.c | 5 + src/spicelib/devices/vsrc/vsrcinit.c | 5 +- 14 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 src/spicelib/analysis/cktreliability.c create mode 100644 src/spicelib/devices/mos1/mos1reliability.c diff --git a/src/include/ngspice/cktdefs.h b/src/include/ngspice/cktdefs.h index eeff067e8..96b52d2d3 100644 --- a/src/include/ngspice/cktdefs.h +++ b/src/include/ngspice/cktdefs.h @@ -423,6 +423,8 @@ extern int DCpss(CKTcircuit *, int); /* SP */ #endif +extern int CKTreliability (CKTcircuit *, unsigned int) ; + extern int NaskQuest(CKTcircuit *, JOB *, int, IFvalue *); extern int NsetParm(CKTcircuit *, JOB *, int, IFvalue *); extern int NIacIter(CKTcircuit *); diff --git a/src/include/ngspice/devdefs.h b/src/include/ngspice/devdefs.h index f9b89f989..c1d0d5937 100644 --- a/src/include/ngspice/devdefs.h +++ b/src/include/ngspice/devdefs.h @@ -114,6 +114,8 @@ typedef struct SPICEdev { int *DEVinstSize; /* size of an instance */ int *DEVmodSize; /* size of a model */ + int (*DEVreliability)(GENmodel *, CKTcircuit *, unsigned int) ; + } SPICEdev; /* instance of structure for each possible type of device */ diff --git a/src/spicelib/analysis/Makefile.am b/src/spicelib/analysis/Makefile.am index 86d6b7371..2cdceb11b 100644 --- a/src/spicelib/analysis/Makefile.am +++ b/src/spicelib/analysis/Makefile.am @@ -95,7 +95,8 @@ libckt_la_SOURCES = \ tranaskq.c \ traninit.c \ transetp.c \ - cluster.c + cluster.c \ + cktreliability.c if PSS_WANTED diff --git a/src/spicelib/analysis/cktdojob.c b/src/spicelib/analysis/cktdojob.c index f233ea628..93cbd3b39 100644 --- a/src/spicelib/analysis/cktdojob.c +++ b/src/spicelib/analysis/cktdojob.c @@ -223,6 +223,9 @@ CKTdoJob(CKTcircuit *ckt, int reset, TSKtask *task) error = analInfo[i]->an_func (ckt, reset); /* txl, cpl addition */ if (error == 1111) break; + + CKTreliability (ckt, 1) ; + } if (error) error2 = error; diff --git a/src/spicelib/analysis/cktreliability.c b/src/spicelib/analysis/cktreliability.c new file mode 100644 index 000000000..23a22649f --- /dev/null +++ b/src/spicelib/analysis/cktreliability.c @@ -0,0 +1,24 @@ +/********** +Author: 2015 Francesco Lannutti +**********/ + +#include "ngspice/ngspice.h" +#include "ngspice/cktdefs.h" +#include "ngspice/devdefs.h" +#include "ngspice/sperror.h" + +int +CKTreliability (CKTcircuit *ckt, unsigned int mode) +{ + int error, i ; + + for (i = 0 ; i < DEVmaxnum ; i++) + { + if (DEVices [i] && DEVices [i]->DEVreliability && ckt->CKThead [i]) + { + error = DEVices [i]->DEVreliability (ckt->CKThead [i], ckt, mode) ; + } + } + + return (OK) ; +} diff --git a/src/spicelib/analysis/dctran.c b/src/spicelib/analysis/dctran.c index f43189898..3d0b1d263 100644 --- a/src/spicelib/analysis/dctran.c +++ b/src/spicelib/analysis/dctran.c @@ -828,6 +828,17 @@ resume: #endif } else { + + printf ("CKTtime: %-.9g\n", ckt->CKTtime) ; + CKTreliability (ckt, 0) ; +/** Nel RHSold, ogni device deve accedere ai propri valori per vedere se esso stesso è acceso o spento. + Nel caso del BSIM4, vale la regola Vgs > Vth, dove Vgs = ckt->CKTrhsOld [here->BSIM4...] - ckt->CKTrhsOld [here->BSIM4...] e Vth = here->BSIM4vth . + In caso il transistor sia acceso, si alza un flag, privato del device, che indica che è acceso. Se è spento, lo stesso flag sarà basso. + Il tempo corrente CKTtime deve essere memorizzato insieme, in modo tale da poter poi calcolare il delta di tempo necessario al modello. + QUI, deve essere controllato che all'istante precedente il device sia acceso (o spento). Se si manifesta un cambio, allora la fase di stress (o di recovery) + è finita e bisogna calcolare il delta_vth attraverso il modello. +*/ + if (firsttime) { #ifdef WANT_SENSE2 if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN)){ diff --git a/src/spicelib/devices/cap/capinit.c b/src/spicelib/devices/cap/capinit.c index 1c8bb5126..1d0499b6d 100644 --- a/src/spicelib/devices/cap/capinit.c +++ b/src/spicelib/devices/cap/capinit.c @@ -72,7 +72,10 @@ SPICEdev CAPinfo = { /* DEVacct */ NULL, #endif /* DEVinstSize */ &CAPiSize, - /* DEVmodSize */ &CAPmSize + /* DEVmodSize */ &CAPmSize, + + NULL + }; diff --git a/src/spicelib/devices/mos1/Makefile.am b/src/spicelib/devices/mos1/Makefile.am index e523c113c..a0f1bc48d 100644 --- a/src/spicelib/devices/mos1/Makefile.am +++ b/src/spicelib/devices/mos1/Makefile.am @@ -31,7 +31,8 @@ libmos1_la_SOURCES = \ mos1sset.c \ mos1supd.c \ mos1temp.c \ - mos1trun.c + mos1trun.c \ + mos1reliability.c diff --git a/src/spicelib/devices/mos1/mos1defs.h b/src/spicelib/devices/mos1/mos1defs.h index c51cc2eb9..074ea736f 100644 --- a/src/spicelib/devices/mos1/mos1defs.h +++ b/src/spicelib/devices/mos1/mos1defs.h @@ -13,6 +13,13 @@ Modified: 2000 AlansFixes #include "ngspice/complex.h" #include "ngspice/noisedef.h" +typedef struct sMOS1relStruct { + double time ; + double deltaVth ; + double t_star ; + int IsON ; +} MOS1relStruct ; + /* declarations for level 1 MOSFETs */ /* information needed for each instance */ @@ -264,6 +271,8 @@ typedef struct sMOS1instance { #define MOS1dphibs_dw MOS1sens + 68 #define MOS1dphibd_dw MOS1sens + 69 + MOS1relStruct *MOS1reliability ; + } MOS1instance ; #define MOS1vbd MOS1states+ 0 /* bulk-drain voltage */ diff --git a/src/spicelib/devices/mos1/mos1ext.h b/src/spicelib/devices/mos1/mos1ext.h index b1ed69d52..064198591 100644 --- a/src/spicelib/devices/mos1/mos1ext.h +++ b/src/spicelib/devices/mos1/mos1ext.h @@ -28,3 +28,5 @@ extern int MOS1convTest(GENmodel*,CKTcircuit*); extern int MOS1disto(int,GENmodel*,CKTcircuit*); extern int MOS1noise(int,int,GENmodel*,CKTcircuit*,Ndata*,double*); extern int MOS1dSetup(GENmodel*,CKTcircuit*); + +extern int MOS1reliability (GENmodel *, CKTcircuit *, unsigned int) ; diff --git a/src/spicelib/devices/mos1/mos1init.c b/src/spicelib/devices/mos1/mos1init.c index a537f411c..63f25dcf1 100644 --- a/src/spicelib/devices/mos1/mos1init.c +++ b/src/spicelib/devices/mos1/mos1init.c @@ -73,7 +73,10 @@ SPICEdev MOS1info = { /* DEVacct */ NULL, #endif /* DEVinstSize */ &MOS1iSize, - /* DEVmodSize */ &MOS1mSize + /* DEVmodSize */ &MOS1mSize, + + MOS1reliability + }; diff --git a/src/spicelib/devices/mos1/mos1reliability.c b/src/spicelib/devices/mos1/mos1reliability.c new file mode 100644 index 000000000..94f6d6f43 --- /dev/null +++ b/src/spicelib/devices/mos1/mos1reliability.c @@ -0,0 +1,182 @@ +/********** +Author: 2015 Francesco Lannutti +**********/ + +#include "ngspice/ngspice.h" +#include "ngspice/devdefs.h" +#include "mos1defs.h" +#include "ngspice/sperror.h" + +static int +calculate_aging +( + MOS1instance *here, + double t_aging, + unsigned int stress_or_recovery +) +{ + double K_b, T, h_cut, q, Nts, T_hk, Nt, eps_0, eps_hk, eps_SiO2, m_star, W, tau_0, beta, tau_e, beta1 ; + + double A ; + + K_b = 8.6e-5 ; + T = 300 ; + h_cut = 1.05e-34 ; + q = 1.6e-19 ; + Nts = 2e13 ; + T_hk = 2 ; + Nt = pow ((sqrt (Nts)), 3) * 1e-21 ; + eps_0 = 8.85e-21 ; + eps_hk = 25 ; + eps_SiO2 = 3.9 ; + m_star = 0.1 * 9.11e-31 ; + W = 1.5 * 1.6e-19 ; + tau_0 = 1e-11 ; + beta = 0.373 ; + tau_e = 0.85e-9 ; + beta1 = 0.112 ; + + A = (q / (4 * eps_0 * eps_hk)) * pow ((h_cut / (2 * sqrt (2 * m_star * W)) * 1e9), 2) ; + + if (stress_or_recovery) + { + if (h_cut / (2 * sqrt (2 * m_star * W)) * log (1 + pow (((t_aging + here->MOS1reliability->t_star) / tau_0), beta)) * 1e9 <= 2) + { + here->MOS1reliability->deltaVth = Nt * A * pow (log (1 + pow (((t_aging + here->MOS1reliability->t_star) / tau_0), beta)), 2) ; + } else { + here->MOS1reliability->deltaVth = pow ((q / (4 * eps_0 * eps_hk)) * Nt * T_hk, 2) ; + } + } else { + here->MOS1reliability->deltaVth = here->MOS1reliability->deltaVth * log (1 + (1.718 / (1 + pow ((t_aging / tau_e), beta1)))) ; + } + + if (!stress_or_recovery) + { + here->MOS1reliability->t_star = pow ((exp (sqrt (here->MOS1reliability->deltaVth / (Nt * A))) - 1), (1 / beta)) * tau_0 ; + } + + return 0 ; +} + +int +MOS1reliability (GENmodel *inModel, CKTcircuit *ckt, unsigned int mode) +{ + MOS1model *model = (MOS1model *)inModel ; + MOS1instance *here ; + double delta, vds, vgs, von ; + int NowIsON ; + + /* loop through all the MOS1 device models */ + for ( ; model != NULL ; model = model->MOS1nextModel) + { + /* loop through all the instances of the model */ + for (here = model->MOS1instances ; here != NULL ; here=here->MOS1nextInstance) + { + vds = ckt->CKTstate0 [here->MOS1vds] ; + vgs = ckt->CKTstate0 [here->MOS1vgs] ; + von = model->MOS1type * here->MOS1von ; + if (vds >= 0) + { + printf ("VDS >= 0\tMOS1type: %d\tMOS1instance: %s\tVgs: %-.9g\tVon: %-.9g\t", model->MOS1type, here->MOS1name, vgs, von) ; + if (vgs > von) + { + printf ("Acceso!\n") ; + NowIsON = 1 ; + } else { + printf ("Spento!\n") ; + NowIsON = 0 ; + } + } else { + double vgd ; + vgd = vgs - vds ; + printf ("VDS < 0\tMOS1type: %d\tMOS1instance: %s\tVgd: %-.9g\tVon: %-.9g\t", model->MOS1type, here->MOS1name, vgd, von) ; + if (vgd > von) + { + printf ("Acceso!\n") ; + NowIsON = 1 ; + } else { + printf ("Spento!\n") ; + NowIsON = 0 ; + } + } + + // If it's the first time, initialize 'here->MOS1reliability->IsON' + if (here->MOS1reliability->IsON == -1) + { + here->MOS1reliability->IsON = NowIsON ; + } + + if (mode == 0) + { + if (NowIsON) + { + if (here->MOS1reliability->IsON == 1) + { + // Until now, the device was ON - Do NOTHING + delta = -1 ; + } else if (here->MOS1reliability->IsON == 0) { + // Until now, the device was OFF - Calculate recovery + delta = ckt->CKTtime - here->MOS1reliability->time ; + + // Calculate Aging - Giogio Liatis' Model + calculate_aging (here, delta, 0) ; + + // Update time and flag - Stress begins + here->MOS1reliability->time = ckt->CKTtime ; + here->MOS1reliability->IsON = 1 ; + } else { + fprintf (stderr, "Reliability Analysis Error\n") ; + } + } else { + if (here->MOS1reliability->IsON == 1) + { + // Until now, the device was ON - Calculate stress + delta = ckt->CKTtime - here->MOS1reliability->time ; + + // Calculate Aging - Giorgio Liatis' Model + calculate_aging (here, delta, 1) ; + + // Update time and flag - Recovery begins + here->MOS1reliability->time = ckt->CKTtime ; + here->MOS1reliability->IsON = 0 ; + } else if (here->MOS1reliability->IsON == 0) { + // Until now, the device was OFF - Do NOTHING + delta = -1 ; + } else { + fprintf (stderr, "Reliability Analysis Error\n") ; + } + } + } else if (mode == 1) { + // In this mode, it doesn't matter if NOW the device is in stress or in recovery, since it's the last timestep + if (here->MOS1reliability->IsON == 1) + { + // Calculate stress + delta = ckt->CKTtime - here->MOS1reliability->time ; + calculate_aging (here, delta, 1) ; + + // Update time and flag - Maybe Optional + here->MOS1reliability->time = ckt->CKTtime ; + here->MOS1reliability->IsON = 1 ; + } else if (here->MOS1reliability->IsON == 0) { + // Calculate recovery + delta = ckt->CKTtime - here->MOS1reliability->time ; + calculate_aging (here, delta, 0) ; + + // Update time and flag - Maybe Optional + here->MOS1reliability->time = ckt->CKTtime ; + here->MOS1reliability->IsON = 0 ; + } else { + fprintf (stderr, "Reliability Analysis Error\n") ; + } + } else { + fprintf (stderr, "Reliability Analysis Error\n") ; + } + printf ("Time: %-.9gs\t", here->MOS1reliability->time) ; + printf ("DeltaVth: %-.9gmV\t", here->MOS1reliability->deltaVth * 1000) ; + printf ("IsON: %u\t", here->MOS1reliability->IsON) ; + printf ("t_star: %-.9gs\n\n\n", here->MOS1reliability->t_star) ; + } + } + + return (OK) ; +} diff --git a/src/spicelib/devices/mos1/mos1set.c b/src/spicelib/devices/mos1/mos1set.c index 521ff3fbf..dd7ce1698 100644 --- a/src/spicelib/devices/mos1/mos1set.c +++ b/src/spicelib/devices/mos1/mos1set.c @@ -206,6 +206,11 @@ do { if((here->ptr = SMPmakeElt(matrix, here->first, here->second)) == NULL){\ TSTALLOC(MOS1SPbPtr,MOS1sNodePrime,MOS1bNode); TSTALLOC(MOS1SPdpPtr,MOS1sNodePrime,MOS1dNodePrime); + here->MOS1reliability = TMALLOC (MOS1relStruct, 1) ; + here->MOS1reliability->time = 0 ; + here->MOS1reliability->deltaVth = 0 ; + here->MOS1reliability->t_star = 0 ; + here->MOS1reliability->IsON = -1 ; } } return(OK); diff --git a/src/spicelib/devices/vsrc/vsrcinit.c b/src/spicelib/devices/vsrc/vsrcinit.c index 3c8285851..f699cf81f 100644 --- a/src/spicelib/devices/vsrc/vsrcinit.c +++ b/src/spicelib/devices/vsrc/vsrcinit.c @@ -73,7 +73,10 @@ SPICEdev VSRCinfo = { /* DEVacct */ NULL, #endif /* DEVinstSize */ &VSRCiSize, - /* DEVmodSize */ &VSRCmSize + /* DEVmodSize */ &VSRCmSize, + + NULL + };