Added the new '.relan' analysis to let the user perform Aging Calculation and Aged Analysis in a row

Added BSIM4 support
This commit is contained in:
Francesco Lannutti 2015-07-04 23:45:51 +02:00
parent 542d7677e7
commit 51e50f2c2d
26 changed files with 1698 additions and 49 deletions

View File

@ -293,6 +293,14 @@ struct comm spcp_coms[] = {
"[.pss line args] : Do a periodic state analysis." } ,
/* SP */
#endif
//#ifdef RELAN
{ "relan", com_relan, TRUE, TRUE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 0, LOTS,
NULL,
"[.relan line args] : Do a Reliability Analysis." } ,
//#endif
{ "ac", com_ac, TRUE, TRUE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 0, LOTS,
NULL,
@ -724,6 +732,14 @@ struct comm nutcp_coms[] = {
"[.pss line args] : Do a periodic steady state analysis." } ,
/* SP */
#endif
//#ifdef RELAN
{ "relan", NULL, TRUE, TRUE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 0, LOTS,
NULL,
"[.relan line args] : Do a Reliability Analysis." } ,
//#endif
{ "ac", NULL, TRUE, TRUE,
{ 0, 0, 0, 0 }, E_DEFHMASK, 0, LOTS,
NULL,

View File

@ -185,6 +185,15 @@ com_pss(wordlist *wl)
#endif
//#ifdef RELAN
void
com_relan (wordlist *wl)
{
dosim ("relan", wl) ;
}
//#endif
static int
dosim(
char *what, /* in: command (pz,op,dc,ac,tf,tran,sens,disto,noise,run) */

View File

@ -16,6 +16,11 @@ void com_tran(wordlist *wl);
/* SP: Stady State Analysis */
void com_pss(wordlist *wl);
/* SP */
//#ifdef RELAN
void com_relan (wordlist *wl) ;
//#endif
void com_sens(wordlist *wl);
void com_disto(wordlist *wl);
void com_noise(wordlist *wl);

View File

@ -314,6 +314,70 @@ if_sens_run(CKTcircuit *ckt, wordlist *args, INPtables *tab)
}
#endif
//#ifdef RELAN
if (strcmp (token, "relan") == 0)
{
JOB *relanJob ;
which = ft_find_analysis ("RELAN") ;
if (which == -1)
{
current->error = INPerrCat (current->error, INPmkTemp ("Reliability Analysis Unsupported\n")) ;
return (0) ; /* temporary */
}
err = ft_sim->newAnalysis (ft_curckt->ci_ckt, which, "relan", &relanJob, ft_curckt->ci_specTask) ;
if (err)
{
ft_sperror(err, "createRELAN") ;
return (0) ;
}
parm = INPgetValue (ckt, &line, IF_REAL, tab) ;
error = INPapName (ckt, which, relanJob, "relan_aging_step", parm) ;
if (error)
{
current->error = INPerrCat (current->error, INPerror (error)) ;
}
parm = INPgetValue (ckt, &line, IF_REAL, tab) ;
error = INPapName (ckt, which, relanJob, "relan_aging_stop", parm) ;
if (error)
{
current->error = INPerrCat (current->error, INPerror (error)) ;
}
if (*line)
{
if (*line == 'd')
{
goto next ;
} else if (*line == 'u') {
goto uic_relan ;
}
parm = INPgetValue (ckt, &line, IF_REAL, tab) ; /* AgingStart */
error = INPapName (ckt, which, relanJob, "relan_aging_start", parm) ;
if (error)
{
current->error = INPerrCat (current->error, INPerror (error)) ;
}
uic_relan:
if (*line == 'u')
{
INPgetTok (&line, &name, 1) ;
if (strcmp (name, "uic") == 0)
{
ptemp.iValue = 1 ;
error = INPapName (ckt, which, relanJob, "uic", &ptemp) ;
if (error)
{
current->error = INPerrCat (current->error, INPerror (error)) ;
}
}
}
}
}
//#endif
next:
while (*line) { /* read the entire line */

View File

@ -221,6 +221,11 @@ if_run(CKTcircuit *ckt, char *what, wordlist *args, INPtables *tab)
|| eq(what, "pss")
/* SP */
#endif
//#ifdef RELAN
|| eq (what, "relan")
//#endif
)
{
s = wl_flatten(args); /* va: tfree char's tmalloc'ed in wl_flatten */
@ -336,6 +341,11 @@ if_run(CKTcircuit *ckt, char *what, wordlist *args, INPtables *tab)
(eq(what, "pss")) ||
/* SP */
#endif
//#ifdef RELAN
(eq (what, "relan")) ||
//#endif
(eq(what, "run")))
{
/*CDHW Run the analysis pointed to by ci_curTask CDHW*/

View File

@ -82,6 +82,11 @@ static struct plotab plotabs[NUMPLOTTYPES] = {
{ "harm", "harm" },
{ "spect", "spect" },
{ "pss", "periodic" },
//#ifdef RELAN
{ "relan", "reliability" },
//#endif
};

View File

@ -125,6 +125,7 @@ pkginclude_HEADERS = \
FastNorm3.h \
fftext.h \
wallace.h \
wstdio.h
wstdio.h \
relandefs.h
MAINTAINERCLEANFILES = Makefile.in

View File

@ -423,7 +423,13 @@ extern int DCpss(CKTcircuit *, int);
/* SP */
#endif
//#ifdef RELAN
extern int RELANaskQuest (CKTcircuit *, JOB *, int, IFvalue *) ;
extern int RELANsetParm (CKTcircuit *, JOB *, int, IFvalue *) ;
extern int RELANinit (CKTcircuit *, JOB *) ;
extern int RELANanalysis (CKTcircuit *, int) ;
extern int CKTreliability (CKTcircuit *, unsigned int) ;
//#endif
extern int NaskQuest(CKTcircuit *, JOB *, int, IFvalue *);
extern int NsetParm(CKTcircuit *, JOB *, int, IFvalue *);

View File

@ -0,0 +1,29 @@
/**********
Author: Francesco Lannutti - July 2015
**********/
#ifndef ngspice_RELANDEFS_H
#define ngspice_RELANDEFS_H
#include "ngspice/cktdefs.h"
#include "ngspice/jobdefs.h"
#include "ngspice/tskdefs.h"
typedef struct {
int JOBtype ;
JOB *JOBnextJob ;
char *JOBname ;
double RELANfinalTime ;
double RELANstep ;
double RELANmaxStep ;
double RELANinitTime ;
long RELANmode ;
runDesc *RELANplot ;
} RELANan ;
#define RELAN_TSTART 1
#define RELAN_TSTOP 2
#define RELAN_TSTEP 3
#define RELAN_TMAX 4
#define RELAN_UIC 5
#endif

View File

@ -96,7 +96,11 @@ libckt_la_SOURCES = \
traninit.c \
transetp.c \
cluster.c \
cktreliability.c
cktreliability.c \
relananalysis.c \
relanaskq.c \
relaninit.c \
relansetp.c
if PSS_WANTED

View File

@ -5,27 +5,29 @@
#include "analysis.h"
extern SPICEanalysis OPTinfo;
extern SPICEanalysis ACinfo;
extern SPICEanalysis DCTinfo;
extern SPICEanalysis DCOinfo;
extern SPICEanalysis TRANinfo;
extern SPICEanalysis PZinfo;
extern SPICEanalysis TFinfo;
extern SPICEanalysis DISTOinfo;
extern SPICEanalysis NOISEinfo;
extern SPICEanalysis SENSinfo;
extern SPICEanalysis OPTinfo ;
extern SPICEanalysis RELANinfo ;
extern SPICEanalysis ACinfo ;
extern SPICEanalysis DCTinfo ;
extern SPICEanalysis DCOinfo ;
extern SPICEanalysis TRANinfo ;
extern SPICEanalysis PZinfo ;
extern SPICEanalysis TFinfo ;
extern SPICEanalysis DISTOinfo ;
extern SPICEanalysis NOISEinfo ;
extern SPICEanalysis SENSinfo ;
#ifdef WITH_PSS
extern SPICEanalysis PSSinfo;
extern SPICEanalysis PSSinfo ;
#endif
#ifdef WANT_SENSE2
extern SPICEanalysis SEN2info;
extern SPICEanalysis SEN2info ;
#endif
SPICEanalysis *analInfo[] = {
SPICEanalysis *analInfo [] = {
&OPTinfo,
&RELANinfo,
&ACinfo,
&DCTinfo,
&DCOinfo,
@ -41,25 +43,24 @@ SPICEanalysis *analInfo[] = {
#ifdef WANT_SENSE2
&SEN2info,
#endif
};
} ;
char *spice_analysis_get_name(int index)
char *spice_analysis_get_name (int index)
{
return analInfo[index]->if_analysis.name;
return analInfo [index]->if_analysis.name ;
}
char *spice_analysis_get_description(int index)
char *spice_analysis_get_description (int index)
{
return analInfo[index]->if_analysis.description;
return analInfo [index]->if_analysis.description ;
}
int spice_num_analysis(void)
int spice_num_analysis (void)
{
return NUMELEMS(analInfo);
return NUMELEMS (analInfo) ;
}
SPICEanalysis **spice_analysis_ptr(void)
SPICEanalysis **spice_analysis_ptr (void)
{
return analInfo;
return analInfo ;
}

View File

@ -224,7 +224,17 @@ CKTdoJob(CKTcircuit *ckt, int reset, TSKtask *task)
/* txl, cpl addition */
if (error == 1111) break;
CKTreliability (ckt, 1) ;
if (i == 1)
{
/* In case of Reliability Analysis, perform the final CKTreliability */
printf ("\n\nFinal Aging...\n") ;
CKTreliability (ckt, 1) ;
error = CKTtemp (ckt) ;
if (error)
{
return (error) ;
}
}
}
if (error)

View File

@ -828,17 +828,6 @@ 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)){

View File

@ -0,0 +1,994 @@
/**********
Copyright 1990 Regents of the University of California. All rights reserved.
Author: 1985 Thomas L. Quarles
Modified: 2000 AlansFixes
**********/
/* subroutine to do DC TRANSIENT analysis
--- ONLY, unlike spice2 routine with the same name! */
#include "ngspice/ngspice.h"
#include "ngspice/cktdefs.h"
#include "cktaccept.h"
#include "ngspice/relandefs.h"
#include "ngspice/sperror.h"
#include "ngspice/fteext.h"
#include "ngspice/missing_math.h"
/* for setting breakpoints required by dbs data base */
extern struct dbcomm *dbs;
#include "ngspice/ftedebug.h"
#ifdef XSPICE
/* gtri - add - wbk - Add headers */
#include "ngspice/miftypes.h"
#include "ngspice/evt.h"
#include "ngspice/mif.h"
#include "ngspice/evtproto.h"
#include "ngspice/ipctiein.h"
/* gtri - end - wbk - Add headers */
#endif
#ifdef CLUSTER
#include "ngspice/cluster.h"
#endif
#ifdef SHARED_MODULE
extern int add_bkpt(void);
extern int sharedsync(double*, double*, double, double, double, int, int*, int);
extern int ng_ident; /* for debugging */
static double del_before; /* for debugging */
#endif
#define INIT_STATS() \
do { \
startTime = SPfrontEnd->IFseconds(); \
startIters = ckt->CKTstat->STATnumIter; \
startdTime = ckt->CKTstat->STATdecompTime; \
startsTime = ckt->CKTstat->STATsolveTime; \
startlTime = ckt->CKTstat->STATloadTime; \
startkTime = ckt->CKTstat->STATsyncTime; \
} while(0)
#define UPDATE_STATS(analysis) \
do { \
ckt->CKTcurrentAnalysis = analysis; \
ckt->CKTstat->STATtranTime += SPfrontEnd->IFseconds() - startTime; \
ckt->CKTstat->STATtranIter += ckt->CKTstat->STATnumIter - startIters; \
ckt->CKTstat->STATtranDecompTime += ckt->CKTstat->STATdecompTime - startdTime; \
ckt->CKTstat->STATtranSolveTime += ckt->CKTstat->STATsolveTime - startsTime; \
ckt->CKTstat->STATtranLoadTime += ckt->CKTstat->STATloadTime - startlTime; \
ckt->CKTstat->STATtranSyncTime += ckt->CKTstat->STATsyncTime - startkTime; \
} while(0)
int
RELANanalysis (CKTcircuit *ckt,
int restart) /* forced restart flag */
{
RELANan *job = (RELANan *) ckt->CKTcurJob;
int i;
double olddelta;
double delta;
double newdelta;
double *temp;
double startdTime;
double startsTime;
double startlTime;
double startkTime;
double startTime;
int startIters;
int converged;
int firsttime;
int error;
#ifdef WANT_SENSE2
int save, save2, size;
long save1;
#endif
int save_order;
long save_mode;
IFuid timeUid;
IFuid *nameList;
int numNames;
double maxstepsize = 0.0;
int ltra_num;
CKTnode *node;
#ifdef XSPICE
/* gtri - add - wbk - 12/19/90 - Add IPC stuff */
Ipc_Boolean_t ipc_firsttime = IPC_TRUE;
Ipc_Boolean_t ipc_secondtime = IPC_FALSE;
Ipc_Boolean_t ipc_delta_cut = IPC_FALSE;
double ipc_last_time = 0.0;
double ipc_last_delta = 0.0;
/* gtri - end - wbk - 12/19/90 - Add IPC stuff */
#endif
#if defined CLUSTER || defined SHARED_MODULE
int redostep;
#endif
if(restart || ckt->CKTtime == 0) {
delta=MIN(ckt->CKTfinalTime/100,ckt->CKTstep)/10;
#ifdef STEPDEBUG
printf("delta = %g finalTime/100: %g CKTstep: %g\n",delta,ckt->CKTfinalTime/100,ckt->CKTstep);
#endif
/* begin LTRA code addition */
if (ckt->CKTtimePoints != NULL)
FREE(ckt->CKTtimePoints);
if (ckt->CKTstep >= ckt->CKTmaxStep)
maxstepsize = ckt->CKTstep;
else
maxstepsize = ckt->CKTmaxStep;
ckt->CKTsizeIncr = 10;
ckt->CKTtimeIndex = -1; /* before the DC soln has been stored */
ckt->CKTtimeListSize = (int) ceil( ckt->CKTfinalTime / maxstepsize );
ltra_num = CKTtypelook("LTRA");
if (ltra_num >= 0 && ckt->CKThead[ltra_num] != NULL)
ckt->CKTtimePoints = TMALLOC(double, ckt->CKTtimeListSize);
/* end LTRA code addition */
if(ckt->CKTbreaks) FREE(ckt->CKTbreaks);
ckt->CKTbreaks = TMALLOC(double, 2);
if(ckt->CKTbreaks == NULL) return(E_NOMEM);
ckt->CKTbreaks[0] = 0;
ckt->CKTbreaks[1] = ckt->CKTfinalTime;
ckt->CKTbreakSize = 2;
#ifdef SHARED_MODULE
add_bkpt();
#endif
#ifdef XSPICE
/* gtri - begin - wbk - 12/19/90 - Modify setting of CKTminBreak */
/* Set to 10 times delmin for ATESSE 1 compatibity */
if(ckt->CKTminBreak==0) ckt->CKTminBreak = 10.0 * ckt->CKTdelmin;
/* gtri - end - wbk - 12/19/90 - Modify setting of CKTminBreak */
#else
if(ckt->CKTminBreak==0) ckt->CKTminBreak=ckt->CKTmaxStep*5e-5;
#endif
#ifdef XSPICE
/* gtri - add - wbk - 12/19/90 - Add IPC stuff and set anal_init and anal_type */
/* Tell the beginPlot routine what mode we're in */
g_ipc.anal_type = IPC_ANAL_TRAN;
/* Tell the code models what mode we're in */
g_mif_info.circuit.anal_type = MIF_DC;
g_mif_info.circuit.anal_init = MIF_TRUE;
/* gtri - end - wbk */
#endif
error = CKTnames(ckt,&numNames,&nameList);
if(error) return(error);
SPfrontEnd->IFnewUid (ckt, &timeUid, NULL, "time", UID_OTHER, NULL);
error = SPfrontEnd->OUTpBeginPlot (ckt, ckt->CKTcurJob,
ckt->CKTcurJob->JOBname,
timeUid, IF_REAL,
numNames, nameList, IF_REAL,
&(job->RELANplot));
tfree(nameList);
if(error) return(error);
/* initialize CKTsoaCheck `warn' counters */
if (ckt->CKTsoaCheck)
error = CKTsoaInit();
ckt->CKTtime = 0;
ckt->CKTdelta = 0;
ckt->CKTbreak = 1;
firsttime = 1;
save_mode = (ckt->CKTmode&MODEUIC) | MODETRANOP | MODEINITJCT;
save_order = ckt->CKTorder;
/* Add breakpoints here which have been requested by the user setting the
stop command as 'stop when time = xx'.
Get data from the global dbs data base.
*/
if (dbs) {
struct dbcomm *d;
for (d = dbs; d; d = d->db_next)
if ((d->db_type == DB_STOPWHEN) && cieq(d->db_nodename1,"time")
&& (d->db_value2 > 0)) {
CKTsetBreak(ckt, d->db_value2);
if (ft_ngdebug)
printf("breakpoint set to time = %g\n", d->db_value2);
}
}
#ifdef XSPICE
/* gtri - begin - wbk - set a breakpoint at end of supply ramping time */
/* must do this after CKTtime set to 0 above */
if(ckt->enh->ramp.ramptime > 0.0)
CKTsetBreak(ckt, ckt->enh->ramp.ramptime);
/* gtri - end - wbk - set a breakpoint at end of supply ramping time */
/* gtri - begin - wbk - Call EVTop if event-driven instances exist */
if(ckt->evt->counts.num_insts != 0) {
/* use new DCOP algorithm */
converged = EVTop(ckt,
(ckt->CKTmode & MODEUIC) | MODETRANOP | MODEINITJCT,
(ckt->CKTmode & MODEUIC) | MODETRANOP | MODEINITFLOAT,
ckt->CKTdcMaxIter,
MIF_TRUE);
EVTdump(ckt, IPC_ANAL_DCOP, 0.0);
EVTop_save(ckt, MIF_FALSE, 0.0);
/* gtri - end - wbk - Call EVTop if event-driven instances exist */
} else
#endif
converged = CKTop(ckt,
(ckt->CKTmode & MODEUIC) | MODETRANOP | MODEINITJCT,
(ckt->CKTmode & MODEUIC) | MODETRANOP | MODEINITFLOAT,
ckt->CKTdcMaxIter);
if(converged != 0) {
fprintf(stdout,"\nTransient solution failed -\n");
CKTncDump(ckt);
/* CKTnode *node;
double new, old, tol;
int i=1;
fprintf(stdout,"\nTransient solution failed -\n\n");
fprintf(stdout,"Last Node Voltages\n");
fprintf(stdout,"------------------\n\n");
fprintf(stdout,"%-30s %20s %20s\n", "Node", "Last Voltage",
"Previous Iter");
fprintf(stdout,"%-30s %20s %20s\n", "----", "------------",
"-------------");
for(node=ckt->CKTnodes->next;node;node=node->next) {
if (strstr(node->name, "#branch") || !strstr(node->name, "#")) {
new = ckt->CKTrhsOld [i] ;
old = ckt->CKTrhs [i] ;
fprintf(stdout,"%-30s %20g %20g", node->name, new, old);
if(node->type == SP_VOLTAGE) {
tol = ckt->CKTreltol * (MAX(fabs(old),fabs(new))) +
ckt->CKTvoltTol;
} else {
tol = ckt->CKTreltol * (MAX(fabs(old),fabs(new))) +
ckt->CKTabstol;
}
if (fabs(new-old) >tol ) {
fprintf(stdout," *");
}
fprintf(stdout,"\n");
}
i++;
} */
fprintf(stdout,"\n");
fflush(stdout);
} else if (!ft_noacctprint && !ft_noinitprint) {
fprintf(stdout,"\nInitial Transient Solution\n");
fprintf(stdout,"--------------------------\n\n");
fprintf(stdout,"%-30s %15s\n", "Node", "Voltage");
fprintf(stdout,"%-30s %15s\n", "----", "-------");
for(node=ckt->CKTnodes->next;node;node=node->next) {
if (strstr(node->name, "#branch") || !strstr(node->name, "#"))
fprintf(stdout,"%-30s %15g\n", node->name,
ckt->CKTrhsOld[node->number]);
}
fprintf(stdout,"\n");
fflush(stdout);
}
if(converged != 0) return(converged);
#ifdef XSPICE
/* gtri - add - wbk - 12/19/90 - Add IPC stuff */
/* Send the operating point results for Mspice compatibility */
if(g_ipc.enabled) {
ipc_send_dcop_prefix();
CKTdump(ckt, 0.0, job->RELANplot);
ipc_send_dcop_suffix();
}
/* gtri - end - wbk */
/* gtri - add - wbk - 12/19/90 - set anal_init and anal_type */
g_mif_info.circuit.anal_init = MIF_TRUE;
/* Tell the code models what mode we're in */
g_mif_info.circuit.anal_type = MIF_TRAN;
/* gtri - end - wbk */
/* gtri - begin - wbk - Add Breakpoint stuff */
/* Initialize the temporary breakpoint variables to infinity */
g_mif_info.breakpoint.current = 1.0e30;
g_mif_info.breakpoint.last = 1.0e30;
/* gtri - end - wbk - Add Breakpoint stuff */
#endif
ckt->CKTstat->STATtimePts ++;
ckt->CKTorder = 1;
for(i=0;i<7;i++) {
ckt->CKTdeltaOld[i]=ckt->CKTmaxStep;
}
ckt->CKTdelta = delta;
#ifdef STEPDEBUG
(void)printf("delta initialized to %g\n",ckt->CKTdelta);
#endif
ckt->CKTsaveDelta = ckt->CKTfinalTime/50;
#ifdef WANT_SENSE2
if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN)){
#ifdef SENSDEBUG
printf("\nTransient Sensitivity Results\n\n");
CKTsenPrint(ckt);
#endif /* SENSDEBUG */
save = ckt->CKTsenInfo->SENmode;
ckt->CKTsenInfo->SENmode = TRANSEN;
save1 = ckt->CKTmode;
save2 = ckt->CKTorder;
ckt->CKTmode = save_mode;
ckt->CKTorder = save_order;
error = CKTsenDCtran(ckt);
if (error)
return(error);
ckt->CKTmode = save1;
ckt->CKTorder = save2;
}
#endif
ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN;
/* modeinittran set here */
ckt->CKTag[0]=ckt->CKTag[1]=0;
bcopy(ckt->CKTstate0, ckt->CKTstate1,
(size_t) ckt->CKTnumStates * sizeof(double));
#ifdef WANT_SENSE2
if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN)){
size = SMPmatSize(ckt->CKTmatrix);
for(i = 1; i<=size ; i++)
ckt->CKTrhsOp[i] = ckt->CKTrhsOld[i];
}
#endif
INIT_STATS();
#ifdef CLUSTER
CLUsetup(ckt);
#endif
} else {
/* saj As traninit resets CKTmode */
ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITPRED;
/* saj */
INIT_STATS();
if(ckt->CKTminBreak==0) ckt->CKTminBreak=ckt->CKTmaxStep*5e-5;
firsttime=0;
/* To get rawfile working saj*/
error = SPfrontEnd->OUTpBeginPlot (NULL, NULL,
NULL,
NULL, 0,
666, NULL, 666,
&(job->RELANplot));
if(error) {
fprintf(stderr, "Couldn't relink rawfile\n");
return error;
}
/* end saj*/
goto resume;
}
/* 650 */
nextTime:
/* begin LTRA code addition */
if (ckt->CKTtimePoints) {
ckt->CKTtimeIndex++;
if (ckt->CKTtimeIndex >= ckt->CKTtimeListSize) {
/* need more space */
int need;
need = (int) ceil( (ckt->CKTfinalTime - ckt->CKTtime) / maxstepsize );
if (need < ckt->CKTsizeIncr)
need = ckt->CKTsizeIncr;
ckt->CKTtimeListSize += need;
ckt->CKTtimePoints = TREALLOC(double, ckt->CKTtimePoints, ckt->CKTtimeListSize);
ckt->CKTsizeIncr = (int) ceil(1.4 * ckt->CKTsizeIncr);
}
ckt->CKTtimePoints[ckt->CKTtimeIndex] = ckt->CKTtime;
}
/* end LTRA code addition */
error = CKTaccept(ckt);
/* check if current breakpoint is outdated; if so, clear */
if (ckt->CKTtime > ckt->CKTbreaks[0]) CKTclrBreak(ckt);
if (ckt->CKTsoaCheck)
error = CKTsoaCheck(ckt);
/*
* Breakpoint handling scheme:
* When a timepoint t is accepted (by CKTaccept), clear all previous
* breakpoints, because they will never be needed again.
*
* t may itself be a breakpoint, or indistinguishably close. DON'T
* clear t itself; recognise it as a breakpoint and act accordingly
*
* if t is not a breakpoint, limit the timestep so that the next
* breakpoint is not crossed
*/
#ifdef STEPDEBUG
printf("Delta %g accepted at time %g (finaltime: %g)\n",ckt->CKTdelta,ckt->CKTtime,ckt->CKTfinalTime);
fflush(stdout);
#endif /* STEPDEBUG */
ckt->CKTstat->STATaccepted ++;
ckt->CKTbreak = 0;
/* XXX Error will cause single process to bail. */
if(error) {
UPDATE_STATS(DOING_TRAN);
return(error);
}
#ifdef XSPICE
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
if(g_ipc.enabled) {
/* Send event-driven results */
EVTdump(ckt, IPC_ANAL_TRAN, 0.0);
/* Then follow with analog results... */
/* Test to see if delta was cut by a breakpoint, */
/* a non-convergence, or a too large truncation error */
if(ipc_firsttime)
ipc_delta_cut = IPC_FALSE;
else if(ckt->CKTtime < (ipc_last_time + (0.999 * ipc_last_delta)))
ipc_delta_cut = IPC_TRUE;
else
ipc_delta_cut = IPC_FALSE;
/* Record the data required to check for delta cuts */
ipc_last_time = ckt->CKTtime;
ipc_last_delta = MIN(ckt->CKTdelta, ckt->CKTmaxStep);
/* Send results data if time since last dump is greater */
/* than 'mintime', or if first or second timepoints, */
/* or if delta was cut */
if( (ckt->CKTtime >= (g_ipc.mintime + g_ipc.last_time)) ||
ipc_firsttime || ipc_secondtime || ipc_delta_cut ) {
ipc_send_data_prefix(ckt->CKTtime);
CKTdump(ckt, ckt->CKTtime, job->RELANplot);
ipc_send_data_suffix();
if(ipc_firsttime) {
ipc_firsttime = IPC_FALSE;
ipc_secondtime = IPC_TRUE;
} else if(ipc_secondtime) {
ipc_secondtime = IPC_FALSE;
}
g_ipc.last_time = ckt->CKTtime;
}
} else
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
#endif
#ifdef CLUSTER
CLUoutput(ckt);
#endif
if(ckt->CKTtime >= ckt->CKTinitTime)
CKTdump(ckt, ckt->CKTtime, job->RELANplot);
#ifdef XSPICE
/* gtri - begin - wbk - Update event queues/data for accepted timepoint */
/* Note: this must be done AFTER sending results to SI so it can't */
/* go next to CKTaccept() above */
if(ckt->evt->counts.num_insts > 0)
EVTaccept(ckt, ckt->CKTtime);
/* gtri - end - wbk - Update event queues/data for accepted timepoint */
#endif
ckt->CKTstat->STAToldIter = ckt->CKTstat->STATnumIter;
if(check_autostop("tran") ||
fabs(ckt->CKTtime - ckt->CKTfinalTime) < ckt->CKTminBreak ||
AlmostEqualUlps( ckt->CKTtime, ckt->CKTfinalTime, 100 ) ) {
#ifdef STEPDEBUG
printf(" done: time is %g, final time is %g, and tol is %g\n",
ckt->CKTtime, ckt->CKTfinalTime, ckt->CKTminBreak);
#endif
SPfrontEnd->OUTendPlot (job->RELANplot);
job->RELANplot = NULL;
UPDATE_STATS(0);
#ifdef WANT_SENSE2
if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN)){
ckt->CKTsenInfo->SENmode = save;
}
#endif
return(OK);
}
if(SPfrontEnd->IFpauseTest()) {
/* user requested pause... */
UPDATE_STATS(DOING_TRAN);
return(E_PAUSE);
}
resume:
#ifdef STEPDEBUG
if( (ckt->CKTdelta <= ckt->CKTfinalTime/50) &&
(ckt->CKTdelta <= ckt->CKTmaxStep)) {
;
} else {
if(ckt->CKTfinalTime/50<ckt->CKTmaxStep) {
(void)printf("limited by Tstop/50\n");
} else {
(void)printf("limited by Tmax == %g\n",ckt->CKTmaxStep);
}
}
#endif
#ifdef HAS_PROGREP
if (ckt->CKTtime == 0.)
SetAnalyse( "tran init", 0);
else
SetAnalyse( "tran", (int)((ckt->CKTtime * 1000.) / ckt->CKTfinalTime + 0.5));
#endif
ckt->CKTdelta =
MIN(ckt->CKTdelta,ckt->CKTmaxStep);
#ifdef XSPICE
/* gtri - begin - wbk - Cut integration order if first timepoint after breakpoint */
/* if(ckt->CKTtime == g_mif_info.breakpoint.last) */
if ( AlmostEqualUlps( ckt->CKTtime, g_mif_info.breakpoint.last, 100 ) )
ckt->CKTorder = 1;
/* gtri - end - wbk - Cut integration order if first timepoint after breakpoint */
#endif
/* are we at a breakpoint, or indistinguishably close? */
/* if ((ckt->CKTtime == ckt->CKTbreaks[0]) || (ckt->CKTbreaks[0] - */
if ( AlmostEqualUlps( ckt->CKTtime, ckt->CKTbreaks[0], 100 ) ||
ckt->CKTbreaks[0] - ckt->CKTtime <= ckt->CKTdelmin) {
/* first timepoint after a breakpoint - cut integration order */
/* and limit timestep to .1 times minimum of time to next breakpoint,
* and previous timestep
*/
ckt->CKTorder = 1;
#ifdef STEPDEBUG
if( (ckt->CKTdelta > .1*ckt->CKTsaveDelta) ||
(ckt->CKTdelta > .1*(ckt->CKTbreaks[1] - ckt->CKTbreaks[0])) ) {
if(ckt->CKTsaveDelta < (ckt->CKTbreaks[1] - ckt->CKTbreaks[0])) {
(void)printf("limited by pre-breakpoint delta (saveDelta: %g, nxt_breakpt: %g, curr_breakpt: %g\n",
ckt->CKTsaveDelta, ckt->CKTbreaks[1], ckt->CKTbreaks[0]);
} else {
(void)printf("limited by next breakpoint\n");
}
}
#endif
ckt->CKTdelta = MIN(ckt->CKTdelta, .1 * MIN(ckt->CKTsaveDelta,
ckt->CKTbreaks[1] - ckt->CKTbreaks[0]));
if(firsttime) {
/* set a breakpoint to reduce ringing of current in devices */
if (ckt->CKTmode & MODEUIC)
CKTsetBreak(ckt, ckt->CKTstep);
ckt->CKTdelta /= 10;
#ifdef STEPDEBUG
(void)printf("delta cut for initial timepoint\n");
#endif
}
#ifndef XSPICE
/* don't want to get below delmin for no reason */
ckt->CKTdelta = MAX(ckt->CKTdelta, ckt->CKTdelmin*2.0);
#endif
}
#ifndef XSPICE
else if(ckt->CKTtime + ckt->CKTdelta >= ckt->CKTbreaks[0]) {
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = ckt->CKTbreaks[0] - ckt->CKTtime;
#ifdef STEPDEBUG
(void)printf("delta cut to %g to hit breakpoint\n",ckt->CKTdelta);
fflush(stdout);
#endif
ckt->CKTbreak = 1; /* why? the current pt. is not a bkpt. */
}
#endif /* !XSPICE */
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
if(ckt->CKTtime + ckt->CKTdelta >= g_mif_info.breakpoint.current) {
/* If next time > temporary breakpoint, force it to the breakpoint */
/* And mark that timestep was set by temporary breakpoint */
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - ckt->CKTtime;
g_mif_info.breakpoint.last = ckt->CKTtime + ckt->CKTdelta;
} else {
/* Else, mark that timestep was not set by temporary breakpoint */
g_mif_info.breakpoint.last = 1.0e30;
}
/* gtri - end - wbk - Add Breakpoint stuff */
/* gtri - begin - wbk - Modify Breakpoint stuff */
/* Throw out any permanent breakpoint times <= current time */
for (;;) {
#ifdef STEPDEBUG
printf(" brk_pt: %g ckt_time: %g ckt_min_break: %g\n",ckt->CKTbreaks[0], ckt->CKTtime, ckt->CKTminBreak);
#endif
if(AlmostEqualUlps(ckt->CKTbreaks[0], ckt->CKTtime, 100) ||
ckt->CKTbreaks[0] <= ckt->CKTtime + ckt->CKTminBreak) {
#ifdef STEPDEBUG
printf("throwing out permanent breakpoint times <= current time (brk pt: %g)\n",ckt->CKTbreaks[0]);
printf(" ckt_time: %g ckt_min_break: %g\n",ckt->CKTtime, ckt->CKTminBreak);
#endif
CKTclrBreak(ckt);
} else {
break;
}
}
/* Force the breakpoint if appropriate */
if(ckt->CKTtime + ckt->CKTdelta > ckt->CKTbreaks[0]) {
ckt->CKTbreak = 1;
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = ckt->CKTbreaks[0] - ckt->CKTtime;
}
/* gtri - end - wbk - Modify Breakpoint stuff */
#ifdef SHARED_MODULE
/* Either directly go to next time step, or modify ckt->CKTdelta depending on
synchronization requirements. sharedsync() returns 0. */
sharedsync(&ckt->CKTtime, &ckt->CKTdelta, 0, ckt->CKTfinalTime,
ckt->CKTdelmin, 0, &ckt->CKTstat->STATrejected, 0);
#endif
/* gtri - begin - wbk - Do event solution */
if(ckt->evt->counts.num_insts > 0) {
/* if time = 0 and op_alternate was specified as false during */
/* dcop analysis, call any changed instances to let them */
/* post their outputs with their associated delays */
if((ckt->CKTtime == 0.0) && (! ckt->evt->options.op_alternate))
EVTiter(ckt);
/* while there are events on the queue with event time <= next */
/* projected analog time, process them */
while((g_mif_info.circuit.evt_step = EVTnext_time(ckt))
<= (ckt->CKTtime + ckt->CKTdelta)) {
/* Initialize temp analog bkpt to infinity */
g_mif_info.breakpoint.current = 1e30;
/* Pull items off queue and process them */
EVTdequeue(ckt, g_mif_info.circuit.evt_step);
EVTiter(ckt);
/* If any instances have forced an earlier */
/* next analog time, cut the delta */
if(ckt->CKTbreaks[0] < g_mif_info.breakpoint.current)
if(ckt->CKTbreaks[0] > ckt->CKTtime + ckt->CKTminBreak)
g_mif_info.breakpoint.current = ckt->CKTbreaks[0];
if(g_mif_info.breakpoint.current < ckt->CKTtime + ckt->CKTdelta) {
/* Breakpoint must be > last accepted timepoint */
/* and >= current event time */
if(g_mif_info.breakpoint.current > ckt->CKTtime + ckt->CKTminBreak &&
g_mif_info.breakpoint.current >= g_mif_info.circuit.evt_step) {
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - ckt->CKTtime;
g_mif_info.breakpoint.last = ckt->CKTtime + ckt->CKTdelta;
}
}
} /* end while next event time <= next analog time */
} /* end if there are event instances */
/* gtri - end - wbk - Do event solution */
#else
#ifdef CLUSTER
if(!CLUsync(ckt->CKTtime,&ckt->CKTdelta,0)) {
printf("Sync error!\n");
exit(0);
}
#endif /* CLUSTER */
#ifdef SHARED_MODULE
/* Either directly go to next time step, or modify ckt->CKTdelta depending on
synchronization requirements. sharedsync() returns 0.
*/
sharedsync(&ckt->CKTtime, &ckt->CKTdelta, 0, ckt->CKTfinalTime,
ckt->CKTdelmin, 0, &ckt->CKTstat->STATrejected, 0);
#endif
#endif
for(i=5; i>=0; i--)
ckt->CKTdeltaOld[i+1] = ckt->CKTdeltaOld[i];
ckt->CKTdeltaOld[0] = ckt->CKTdelta;
temp = ckt->CKTstates[ckt->CKTmaxOrder+1];
for(i=ckt->CKTmaxOrder;i>=0;i--) {
ckt->CKTstates[i+1] = ckt->CKTstates[i];
}
ckt->CKTstates[0] = temp;
/* 600 */
for (;;) {
#if defined CLUSTER || defined SHARED_MODULE
redostep = 1;
#endif
#ifdef XSPICE
/* gtri - add - wbk - 4/17/91 - Fix Berkeley bug */
/* This is needed here to allow CAPask to output currents */
/* during Transient analysis. A grep for CKTcurrentAnalysis */
/* indicates that it should not hurt anything else ... */
ckt->CKTcurrentAnalysis = DOING_TRAN;
/* gtri - end - wbk - 4/17/91 - Fix Berkeley bug */
#endif
olddelta=ckt->CKTdelta;
/* time abort? */
ckt->CKTtime += ckt->CKTdelta;
#ifdef CLUSTER
CLUinput(ckt);
#endif
ckt->CKTdeltaOld[0]=ckt->CKTdelta;
NIcomCof(ckt);
#ifdef PREDICTOR
error = NIpred(ckt);
#endif /* PREDICTOR */
save_mode = ckt->CKTmode;
save_order = ckt->CKTorder;
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
/* Initialize temporary breakpoint to infinity */
g_mif_info.breakpoint.current = 1.0e30;
/* gtri - end - wbk - Add Breakpoint stuff */
/* gtri - begin - wbk - add convergence problem reporting flags */
/* delta is forced to equal delmin on last attempt near line 650 */
if(ckt->CKTdelta <= ckt->CKTdelmin)
ckt->enh->conv_debug.last_NIiter_call = MIF_TRUE;
else
ckt->enh->conv_debug.last_NIiter_call = MIF_FALSE;
/* gtri - begin - wbk - add convergence problem reporting flags */
/* gtri - begin - wbk - Call all hybrids */
/* gtri - begin - wbk - Set evt_step */
if(ckt->evt->counts.num_insts > 0) {
g_mif_info.circuit.evt_step = ckt->CKTtime;
}
/* gtri - end - wbk - Set evt_step */
#endif
converged = NIiter(ckt,ckt->CKTtranMaxIter);
#ifdef XSPICE
if(ckt->evt->counts.num_insts > 0) {
g_mif_info.circuit.evt_step = ckt->CKTtime;
EVTcall_hybrids(ckt);
}
/* gtri - end - wbk - Call all hybrids */
#endif
ckt->CKTstat->STATtimePts ++;
ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITPRED;
if(firsttime) {
for(i=0;i<ckt->CKTnumStates;i++) {
ckt->CKTstate2[i] = ckt->CKTstate1[i];
ckt->CKTstate3[i] = ckt->CKTstate1[i];
}
}
/* txl, cpl addition */
if (converged == 1111) {
return(converged);
}
if(converged != 0) {
#ifndef CLUSTER
#ifndef SHARED_MODULE
ckt->CKTtime = ckt->CKTtime -ckt->CKTdelta;
ckt->CKTstat->STATrejected ++;
#else
redostep = 1;
#endif
#endif
ckt->CKTdelta = ckt->CKTdelta/8;
#ifdef STEPDEBUG
(void)printf("delta cut to %g for non-convergance\n",ckt->CKTdelta);
fflush(stdout);
#endif
if(firsttime) {
ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN;
}
ckt->CKTorder = 1;
#ifdef XSPICE
/* gtri - begin - wbk - Add Breakpoint stuff */
/* Force backup if temporary breakpoint is < current time */
} else if(g_mif_info.breakpoint.current < ckt->CKTtime) {
ckt->CKTsaveDelta = ckt->CKTdelta;
ckt->CKTtime -= ckt->CKTdelta;
ckt->CKTdelta = g_mif_info.breakpoint.current - ckt->CKTtime;
g_mif_info.breakpoint.last = ckt->CKTtime + ckt->CKTdelta;
if(firsttime) {
ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITTRAN;
}
ckt->CKTorder = 1;
/* gtri - end - wbk - Add Breakpoint stuff */
#endif
} else {
/** 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.
*/
CKTreliability (ckt, 0) ;
if (firsttime) {
#ifdef WANT_SENSE2
if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN)){
save1 = ckt->CKTmode;
save2 = ckt->CKTorder;
ckt->CKTmode = save_mode;
ckt->CKTorder = save_order;
error = CKTsenDCtran (ckt);
if (error)
return(error);
ckt->CKTmode = save1;
ckt->CKTorder = save2;
}
#endif
firsttime = 0;
#if !defined CLUSTER && !defined SHARED_MODULE
goto nextTime; /* no check on
* first time point
*/
#else
redostep = 0;
goto chkStep;
#endif
}
newdelta = ckt->CKTdelta;
error = CKTtrunc(ckt,&newdelta);
if(error) {
UPDATE_STATS(DOING_TRAN);
return(error);
}
if(newdelta > .9 * ckt->CKTdelta) {
if((ckt->CKTorder == 1) && (ckt->CKTmaxOrder > 1)) { /* don't rise the order for backward Euler */
newdelta = ckt->CKTdelta;
ckt->CKTorder = 2;
error = CKTtrunc(ckt,&newdelta);
if(error) {
UPDATE_STATS(DOING_TRAN);
return(error);
}
if(newdelta <= 1.05 * ckt->CKTdelta) {
ckt->CKTorder = 1;
}
}
/* time point OK - 630 */
ckt->CKTdelta = newdelta;
#ifdef NDEV
/* show a time process indicator, by Gong Ding, gdiso@ustc.edu */
if(ckt->CKTtime/ckt->CKTfinalTime*100<10.0)
printf("%%%3.2lf\b\b\b\b\b",ckt->CKTtime/ckt->CKTfinalTime*100);
else if(ckt->CKTtime/ckt->CKTfinalTime*100<100.0)
printf("%%%4.2lf\b\b\b\b\b\b",ckt->CKTtime/ckt->CKTfinalTime*100);
else
printf("%%%5.2lf\b\b\b\b\b\b\b",ckt->CKTtime/ckt->CKTfinalTime*100);
fflush(stdout);
#endif
#ifdef STEPDEBUG
(void)printf(
"delta set to truncation error result: %g. Point accepted at CKTtime: %g\n",
ckt->CKTdelta,ckt->CKTtime);
fflush(stdout);
#endif
#ifdef WANT_SENSE2
if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN)){
save1 = ckt->CKTmode;
save2 = ckt->CKTorder;
ckt->CKTmode = save_mode;
ckt->CKTorder = save_order;
error = CKTsenDCtran(ckt);
if (error)
return (error);
ckt->CKTmode = save1;
ckt->CKTorder = save2;
}
#endif
#if !defined CLUSTER && !defined SHARED_MODULE
/* go to 650 - trapezoidal */
goto nextTime;
#else
redostep = 0;
goto chkStep;
#endif
} else {
#ifndef CLUSTER
#ifndef SHARED_MODULE
ckt->CKTtime = ckt->CKTtime -ckt->CKTdelta;
ckt->CKTstat->STATrejected ++;
#else
redostep = 1;
#endif
#endif
ckt->CKTdelta = newdelta;
#ifdef STEPDEBUG
(void)printf(
"delta set to truncation error result:point rejected\n");
#endif
}
}
if (ckt->CKTdelta <= ckt->CKTdelmin) {
if (olddelta > ckt->CKTdelmin) {
ckt->CKTdelta = ckt->CKTdelmin;
#ifdef STEPDEBUG
(void)printf("delta at delmin\n");
#endif
} else {
UPDATE_STATS(DOING_TRAN);
errMsg = CKTtrouble(ckt, "Timestep too small");
return(E_TIMESTEP);
}
}
#ifdef XSPICE
/* gtri - begin - wbk - Do event backup */
if(ckt->evt->counts.num_insts > 0)
EVTbackup(ckt, ckt->CKTtime + ckt->CKTdelta);
/* gtri - end - wbk - Do event backup */
#endif
#ifdef CLUSTER
chkStep:
if(CLUsync(ckt->CKTtime,&ckt->CKTdelta,redostep)){
goto nextTime;
} else {
ckt->CKTtime -= olddelta;
ckt->CKTstat->STATrejected ++;
}
#endif
#ifdef SHARED_MODULE
/* redostep == 0:
Either directly go to next time step, or modify ckt->CKTdelta depending on
synchronization requirements. sharedsync() returns 0.
redostep == 1:
No convergence, or too large truncation error.
Redo the last time step by subtracting olddelta, and modify ckt->CKTdelta
depending on synchronization requirements. sharedsync() returns 1.
User-supplied redo request:
sharedsync() may return 1 if the user has decided to do so in the callback
function.
*/
chkStep:
if(sharedsync(&ckt->CKTtime, &ckt->CKTdelta, olddelta, ckt->CKTfinalTime,
ckt->CKTdelmin, redostep, &ckt->CKTstat->STATrejected, 1) == 0)
goto nextTime;
#endif
}
/* NOTREACHED */
}

View File

@ -0,0 +1,44 @@
/**********
Author: Francesco Lannutti - July 2015
**********/
#include "ngspice/ngspice.h"
#include "ngspice/ifsim.h"
#include "ngspice/iferrmsg.h"
#include "ngspice/relandefs.h"
#include "ngspice/cktdefs.h"
int
RELANaskQuest (CKTcircuit *ckt, JOB *anal, int which, IFvalue *value)
{
RELANan *job = (RELANan *) anal ;
NG_IGNORE (ckt) ;
switch (which)
{
case RELAN_TSTART:
value->rValue = job->RELANinitTime ;
break ;
case RELAN_TSTOP:
value->rValue = job->RELANfinalTime ;
break ;
case RELAN_TSTEP:
value->rValue = job->RELANstep ;
break ;
case RELAN_TMAX:
value->rValue = job->RELANmaxStep ;
break ;
case RELAN_UIC:
if (job->RELANmode & MODEUIC) {
value->iValue = 1 ;
} else {
value->iValue = 0 ;
}
break ;
default:
return (E_BADPARM) ;
}
return (OK) ;
}

View File

@ -0,0 +1,34 @@
/**********
Author: Francesco Lannutti - July 2015
**********/
#include "ngspice/ngspice.h"
#include "ngspice/cktdefs.h"
#include "ngspice/relandefs.h"
#include "ngspice/iferrmsg.h"
int
RELANinit (CKTcircuit *ckt, JOB *anal)
{
RELANan *job = (RELANan *) anal ;
ckt->CKTinitTime = job->RELANinitTime ;
ckt->CKTfinalTime = job->RELANfinalTime ;
ckt->CKTstep = job->RELANstep ;
ckt->CKTmaxStep = job->RELANmaxStep ;
if (ckt->CKTmaxStep == 0)
{
if (ckt->CKTstep < (ckt->CKTfinalTime - ckt->CKTinitTime) / 50.0)
{
ckt->CKTmaxStep = ckt->CKTstep ;
} else {
ckt->CKTmaxStep = (ckt->CKTfinalTime - ckt->CKTinitTime) / 50.0 ;
}
}
ckt->CKTdelmin = 1e-11 * ckt->CKTmaxStep ;
ckt->CKTmode = job->RELANmode ;
return (OK) ;
}

View File

@ -0,0 +1,89 @@
/**********
Author: Francesco Lannutti - July 2015
**********/
#include "ngspice/ngspice.h"
#include "ngspice/ifsim.h"
#include "ngspice/iferrmsg.h"
#include "ngspice/relandefs.h"
#include "ngspice/cktdefs.h"
#include "analysis.h"
int
RELANsetParm (CKTcircuit *ckt, JOB *anal, int which, IFvalue *value)
{
RELANan *job = (RELANan *) anal ;
NG_IGNORE (ckt) ;
switch (which)
{
case RELAN_TSTART:
if (value->rValue >= job->RELANfinalTime)
{
errMsg = copy ("TSTART is invalid, must be less than TSTOP.") ;
job->RELANinitTime = 0.0 ;
return (E_PARMVAL) ;
}
job->RELANinitTime = value->rValue ;
break ;
case RELAN_TSTOP:
if (value->rValue <= 0.0)
{
errMsg = copy ("TST0P is invalid, must be greater than zero.") ;
job->RELANfinalTime = 1.0 ;
return (E_PARMVAL) ;
}
job->RELANfinalTime = value->rValue ;
break ;
case RELAN_TSTEP:
if (value->rValue <= 0.0)
{
errMsg = copy ("TSTEP is invalid, must be greater than zero.") ;
job->RELANstep = 1.0 ;
return (E_PARMVAL) ;
}
job->RELANstep = value->rValue ;
break ;
case RELAN_TMAX:
job->RELANmaxStep = value->rValue ;
break ;
case RELAN_UIC:
if (value->iValue)
{
job->RELANmode |= MODEUIC ;
}
break ;
default:
return (E_BADPARM) ;
}
return (OK) ;
}
static IFparm RELANparms [] = {
{ "relan_aging_start", RELAN_TSTART, IF_SET|IF_REAL, "starting time" },
{ "relan_aging_stop", RELAN_TSTOP, IF_SET|IF_REAL, "ending time" },
{ "relan_aging_step", RELAN_TSTEP, IF_SET|IF_REAL, "time step" },
{ "relan_aging_max", RELAN_TMAX, IF_SET|IF_REAL, "maximum time step" },
{ "uic", RELAN_UIC, IF_SET|IF_FLAG, "use initial conditions" },
} ;
SPICEanalysis RELANinfo = {
{
"RELAN",
"Reliability analysis",
NUMELEMS(RELANparms),
RELANparms
},
sizeof(RELANan),
TIMEDOMAIN,
1,
RELANsetParm,
RELANaskQuest,
RELANinit,
RELANanalysis
} ;

View File

@ -27,7 +27,8 @@ libbsim4_la_SOURCES = \
bsim4ext.h \
bsim4init.c \
bsim4init.h \
bsim4itf.h
bsim4itf.h \
b4reliability.c

View File

@ -0,0 +1,241 @@
/**********
Author: 2015 Francesco Lannutti
**********/
#include "ngspice/ngspice.h"
#include "ngspice/devdefs.h"
#include "bsim4def.h"
#include "ngspice/sperror.h"
static int
calculate_aging
(
BSIM4instance *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->BSIM4reliability->t_star) / tau_0), beta)) * 1e9 <= 2)
{
here->BSIM4reliability->deltaVth = Nt * A * pow (log (1 + pow (((t_aging + here->BSIM4reliability->t_star) / tau_0), beta)), 2) ;
} else {
here->BSIM4reliability->deltaVth = pow ((q / (4 * eps_0 * eps_hk)) * Nt * T_hk, 2) ;
}
} else {
here->BSIM4reliability->deltaVth = here->BSIM4reliability->deltaVth * log (1 + (1.718 / (1 + pow ((t_aging / tau_e), beta1)))) ;
}
if (!stress_or_recovery)
{
here->BSIM4reliability->t_star = pow ((exp (sqrt (here->BSIM4reliability->deltaVth / (Nt * A))) - 1), (1 / beta)) * tau_0 ;
}
return 0 ;
}
int
BSIM4reliability (GENmodel *inModel, CKTcircuit *ckt, unsigned int mode)
{
BSIM4model *model = (BSIM4model *)inModel ;
BSIM4instance *here ;
double delta, vds, vgs, von ;
int NowIsON ;
/* loop through all the BSIM4 device models */
for ( ; model != NULL ; model = model->BSIM4nextModel)
{
/* loop through all the instances of the model */
for (here = model->BSIM4instances ; here != NULL ; here=here->BSIM4nextInstance)
{
if (model->BSIM4type == -1)
{
// Determine if the transistor is ON or OFF
vds = ckt->CKTstate0 [here->BSIM4vds] ;
vgs = ckt->CKTstate0 [here->BSIM4vgs] ;
von = model->BSIM4type * here->BSIM4von ;
if (vds >= 0)
{
// printf ("VDS >= 0\tBSIM4type: %d\tBSIM4instance: %s\tVgs: %-.9g\tVon: %-.9g\t", model->BSIM4type, here->BSIM4name, vgs, von) ;
if (vgs > von)
{
if (here->BSIM4rgateMod == 3)
{
double vges, vgms ;
vges = ckt->CKTstate0 [here->BSIM4vges] ;
vgms = ckt->CKTstate0 [here->BSIM4vgms] ;
if ((vges > von) && (vgms > von))
{
// printf ("Acceso!\n") ;
NowIsON = 1 ;
} else {
// printf ("Spento!\n") ;
NowIsON = 0 ;
}
} else if ((here->BSIM4rgateMod == 1) || (here->BSIM4rgateMod == 2)) {
double vges ;
vges = ckt->CKTstate0 [here->BSIM4vges] ;
if (vges > von)
{
// printf ("Acceso!\n") ;
NowIsON = 1 ;
} else {
// printf ("Spento!\n") ;
NowIsON = 0 ;
}
} else {
// printf ("Acceso!\n") ;
NowIsON = 1 ;
}
} else {
// printf ("Spento!\n") ;
NowIsON = 0 ;
}
} else {
double vgd ;
vgd = vgs - vds ;
// printf ("VDS < 0\tBSIM4type: %d\tBSIM4instance: %s\tVgd: %-.9g\tVon: %-.9g\t", model->BSIM4type, here->BSIM4name, vgd, von) ;
if (vgd > von)
{
if (here->BSIM4rgateMod == 3)
{
double vges, vged, vgms, vgmd ;
vges = ckt->CKTstate0 [here->BSIM4vges] ;
vged = vges - vds ;
vgms = ckt->CKTstate0 [here->BSIM4vgms] ;
vgmd = vgms - vds ;
if ((vged > von) && (vgmd > von))
{
// printf ("Acceso!\n") ;
NowIsON = 1 ;
} else {
// printf ("Spento!\n") ;
NowIsON = 0 ;
}
} else if ((here->BSIM4rgateMod == 1) || (here->BSIM4rgateMod == 2)) {
double vges, vged ;
vges = ckt->CKTstate0 [here->BSIM4vges] ;
vged = vges - vds ;
if (vged > von)
{
// printf ("Acceso!\n") ;
NowIsON = 1 ;
} else {
// printf ("Spento!\n") ;
NowIsON = 0 ;
}
} else {
// printf ("Acceso!\n") ;
NowIsON = 1 ;
}
} else {
// printf ("Spento!\n") ;
NowIsON = 0 ;
}
}
// If it's the first time, initialize 'here->BSIM4reliability->IsON'
if (here->BSIM4reliability->IsON == -1)
{
here->BSIM4reliability->IsON = NowIsON ;
}
if (mode == 0)
{
if (NowIsON)
{
if (here->BSIM4reliability->IsON == 1)
{
// Until now, the device was ON - Do NOTHING
delta = -1 ;
} else if (here->BSIM4reliability->IsON == 0) {
// Until now, the device was OFF - Calculate recovery
delta = ckt->CKTtime - here->BSIM4reliability->time ;
// Calculate Aging - Giogio Liatis' Model
calculate_aging (here, delta, 0) ;
// Update time and flag - Stress begins
here->BSIM4reliability->time = ckt->CKTtime ;
here->BSIM4reliability->IsON = 1 ;
} else {
fprintf (stderr, "Reliability Analysis Error\n") ;
}
} else {
if (here->BSIM4reliability->IsON == 1)
{
// Until now, the device was ON - Calculate stress
delta = ckt->CKTtime - here->BSIM4reliability->time ;
// Calculate Aging - Giorgio Liatis' Model
calculate_aging (here, delta, 1) ;
// Update time and flag - Recovery begins
here->BSIM4reliability->time = ckt->CKTtime ;
here->BSIM4reliability->IsON = 0 ;
} else if (here->BSIM4reliability->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->BSIM4reliability->IsON == 1)
{
// Calculate stress
delta = ckt->CKTtime - here->BSIM4reliability->time ;
calculate_aging (here, delta, 1) ;
// Update time and flag - Maybe Optional
here->BSIM4reliability->time = ckt->CKTtime ;
here->BSIM4reliability->IsON = 1 ;
} else if (here->BSIM4reliability->IsON == 0) {
// Calculate recovery
delta = ckt->CKTtime - here->BSIM4reliability->time ;
calculate_aging (here, delta, 0) ;
// Update time and flag - Maybe Optional
here->BSIM4reliability->time = ckt->CKTtime ;
here->BSIM4reliability->IsON = 0 ;
} else {
fprintf (stderr, "Reliability Analysis Error\n") ;
}
printf ("\tTime: %-.9gs\t\t", ckt->CKTtime) ;
printf ("DeltaVth: %-.9gmV\t\t", here->BSIM4reliability->deltaVth * 1000) ;
printf ("Device Name: %s\t\t", here->BSIM4name) ;
printf ("Device Type: %s\n\n", model->BSIM4modName) ;
} else {
fprintf (stderr, "Reliability Analysis Error\n") ;
}
}
}
}
return (OK) ;
}

View File

@ -2653,6 +2653,15 @@ do { if((here->ptr = SMPmakeElt(matrix,here->first,here->second))==(double *)NUL
TSTALLOC(BSIM4SgpPtr, BSIM4sNode, BSIM4gNodePrime);
TSTALLOC(BSIM4SbpPtr, BSIM4sNode, BSIM4bNodePrime);
}
if (model->BSIM4type == -1)
{
here->BSIM4reliability = TMALLOC (BSIM4relStruct, 1) ;
here->BSIM4reliability->time = 0 ;
here->BSIM4reliability->deltaVth = 0 ;
here->BSIM4reliability->t_star = 0 ;
here->BSIM4reliability->IsON = -1 ;
}
}
}

View File

@ -1715,6 +1715,13 @@ int Size_Not_Found, i;
}
/* adding delvto */
if (model->BSIM4type == -1)
{
if (here->BSIM4reliability->IsON != -1)
{
here->BSIM4delvto = here->BSIM4reliability->deltaVth ;
}
}
here->BSIM4vth0 += here->BSIM4delvto;
here->BSIM4vfb = pParam->BSIM4vfb + model->BSIM4type * here->BSIM4delvto;

View File

@ -29,6 +29,13 @@ Modified by Pankaj Kumar Thakur, 07/23/2012
#include "ngspice/complex.h"
#include "ngspice/noisedef.h"
typedef struct sBSIM4relStruct {
double time ;
double deltaVth ;
double t_star ;
int IsON ;
} BSIM4relStruct ;
typedef struct sBSIM4instance
{
struct sBSIM4model *BSIM4modPtr;
@ -574,6 +581,8 @@ typedef struct sBSIM4instance
double **BSIM4nVar;
#endif /* NONOISE */
BSIM4relStruct *BSIM4reliability ;
} BSIM4instance ;
struct bsim4SizeDependParam

View File

@ -31,3 +31,5 @@ extern int BSIM4trunc(GENmodel*,CKTcircuit*,double*);
extern int BSIM4noise(int,int,GENmodel*,CKTcircuit*,Ndata*,double*);
extern int BSIM4unsetup(GENmodel*,CKTcircuit*);
extern int BSIM4soaCheck(CKTcircuit *, GENmodel *);
extern int BSIM4reliability (GENmodel *, CKTcircuit *, unsigned int) ;

View File

@ -73,7 +73,10 @@ SPICEdev BSIM4info = {
NULL, /* DEVacct */
#endif
&BSIM4iSize, /* DEVinstSize */
&BSIM4mSize /* DEVmodSize */
&BSIM4mSize, /* DEVmodSize */
BSIM4reliability
};

View File

@ -79,25 +79,25 @@ MOS1reliability (GENmodel *inModel, CKTcircuit *ckt, unsigned int mode)
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) ;
// printf ("VDS >= 0\tMOS1type: %d\tMOS1instance: %s\tVgs: %-.9g\tVon: %-.9g\t", model->MOS1type, here->MOS1name, vgs, von) ;
if (vgs > von)
{
printf ("Acceso!\n") ;
// printf ("Acceso!\n") ;
NowIsON = 1 ;
} else {
printf ("Spento!\n") ;
// 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) ;
// printf ("VDS < 0\tMOS1type: %d\tMOS1instance: %s\tVgd: %-.9g\tVon: %-.9g\t", model->MOS1type, here->MOS1name, vgd, von) ;
if (vgd > von)
{
printf ("Acceso!\n") ;
// printf ("Acceso!\n") ;
NowIsON = 1 ;
} else {
printf ("Spento!\n") ;
// printf ("Spento!\n") ;
NowIsON = 0 ;
}
}
@ -170,13 +170,13 @@ MOS1reliability (GENmodel *inModel, CKTcircuit *ckt, unsigned int mode)
} else {
fprintf (stderr, "Reliability Analysis Error\n") ;
}
printf ("\tTime: %-.9gs\t\t", ckt->CKTtime) ;
printf ("DeltaVth: %-.9gmV\t\t", here->MOS1reliability->deltaVth * 1000) ;
printf ("Device Name: %s\t\t", here->MOS1name) ;
printf ("Device Type: %s\n\n", model->MOS1modName) ;
} 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) ;
}
}
}

View File

@ -612,6 +612,66 @@ dot_pss(char *line, void *ckt, INPtables *tab, card *current,
/* SP */
#endif
//#ifdef RELAN
static int
dot_relan (char *line, void *ckt, INPtables *tab, card *current, void *task, void *gnode, JOB *foo)
{
int error ; /* error code temporary */
IFvalue ptemp ; /* a value structure to package resistance into */
IFvalue *parm ; /* a pointer to a value struct for function returns */
int which ; /* which analysis we are performing */
char *word ; /* something to stick a word of input into */
double dtemp ; /* random double precision temporary */
NG_IGNORE (gnode) ;
/* .relan AgingStep AgingStop <AgingStart> <tmax> <UIC> */
which = ft_find_analysis ("RELAN") ;
if (which == -1)
{
LITERR ("Reliability Analysis unsupported.\n") ;
return (0) ;
}
IFC (newAnalysis, (ckt, which, "Reliability Analysis", &foo, task)) ;
parm = INPgetValue (ckt, &line, IF_REAL, tab) ;
GCA (INPapName, (ckt, which, foo, "relan_aging_step", parm)) ;
parm = INPgetValue (ckt, &line, IF_REAL, tab) ;
GCA (INPapName, (ckt, which, foo, "relan_aging_stop", parm)) ;
if (*line)
{
dtemp = INPevaluate (&line, &error, 1) ; /* AgingStart */
if (error == 0)
{
ptemp.rValue = dtemp ;
GCA (INPapName, (ckt, which, foo, "relan_aging_start", &ptemp)) ;
dtemp = INPevaluate (&line, &error, 1) ; /* tmax */
if (error == 0)
{
ptemp.rValue = dtemp ;
GCA (INPapName, (ckt, which, foo, "relan_aging_max", &ptemp)) ;
}
}
}
if (*line)
{
INPgetTok (&line, &word, 1) ; /* UIC */
if (strcmp (word, "uic") == 0)
{
ptemp.iValue = 1 ;
GCA (INPapName, (ckt, which, foo, "uic", &ptemp)) ;
} else {
fprintf (stderr,"Error: unknown parameter %s on .relan - ignored\n", word) ;
}
}
return (0) ;
}
//#endif
static int
dot_options(char *line, CKTcircuit *ckt, INPtables *tab, card *current,
TSKtask *task, CKTnode *gnode, JOB *foo)
@ -698,6 +758,13 @@ INP2dot(CKTcircuit *ckt, INPtables *tab, card *current, TSKtask *task, CKTnode *
goto quit;
/* SP */
#endif
//#ifdef RELAN
} else if ((strcmp (token, ".relan") == 0)) {
rtn = dot_relan (line, ckt, tab, current, task, gnode, foo) ;
goto quit ;
//#endif
} else if ((strcmp(token, ".subckt") == 0) ||
(strcmp(token, ".ends") == 0)) {
/* not yet implemented - warn & ignore */