/********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles Modified: 2000 AlansFixes Modified: 2023 XSPICE breakpoint fix for shared ngspice by Vyacheslav Shevchuk **********/ /* subroutine to do DC TRANSIENT analysis */ #include "ngspice/ngspice.h" #include "ngspice/cktdefs.h" #include "cktaccept.h" #include "ngspice/trandefs.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 #include "ngspice/miftypes.h" #include "ngspice/evt.h" #include "ngspice/enh.h" #include "ngspice/mif.h" #include "ngspice/evtproto.h" #include "ngspice/ipctiein.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 */ #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 DCtran(CKTcircuit *ckt, int restart) /* forced restart flag */ { TRANan *job = (TRANan *) 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; bool have_autostop = FALSE, flag_autostop = FALSE; int ltra_num; CKTnode *node; #ifdef XSPICE /* 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; // Fix for sharedsync olddelta: When DCTran processes // either analog or XSPICE breakpoint, then it subtracts delta from // ckt->CKTtime. It sends 0 as olddelta after analog breakpoint // processing. Still, for XSPICE breakpoints it subtracts delta (see code // 'else if(g_mif_info.breakpoint.current < ckt->CKTtime)' branch) and // then sends non zero olddelta to sharedsync at the end of the function // (see chkStep: label). Thus olddelta is subtracted twice. Then // ckt->CKTtime becomes less than last_accepted_time. // xspice_breakpoints_processed 0: // XSPICE models didn't have breakpoints in [last_accepted_time, CKTtime]. // xspice_breakpoints_processed 1: // convergence criteria are satisfied but XSPICE breakpoint(s) is in the // time interval [last_accepted_time, CKTtime]. int xspice_breakpoints_processed = 0; #ifdef SHARED_MODULE double olddelta_for_shared_sync = 0.0; #endif #endif #if defined SHARED_MODULE int redostep; #endif if(restart || ckt->CKTtime == 0) { /* set the first step time */ 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 = 100; 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 */ /* Reset the breakpoint list. Add breakpoints at 0 time and TSTOP (final) time */ 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 /* Modify setting of CKTminBreak Set to 10 times delmin (minimum step time). */ if(ckt->CKTminBreak == 0) ckt->CKTminBreak = 10.0 * ckt->CKTdelmin; #else if(ckt->CKTminBreak == 0) ckt->CKTminBreak = ckt->CKTmaxStep * 5e-5; #endif #ifdef XSPICE /* 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; #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->TRANplot)); 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; have_autostop = cp_getvar("autostop", CP_BOOL, NULL, 0); /* 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 /* set a breakpoint at end of supply ramping time. Must do this after CKTtime is set to 0 above */ if(ckt->enh->ramp.ramptime > 0.0) CKTsetBreak(ckt, ckt->enh->ramp.ramptime); /* 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); } else #endif /* Get operating point for analogue circuit */ converged = CKTop(ckt, (ckt->CKTmode & MODEUIC) | MODETRANOP | MODEINITJCT, (ckt->CKTmode & MODEUIC) | MODETRANOP | MODEINITFLOAT, ckt->CKTdcMaxIter); if(converged != 0) { fprintf(stderr,"\nError: Finding the operating point for transient simulation failed \n"); CKTncDump(ckt); fprintf(stderr,"\n"); fflush(stderr); } else if (ckt->CKTmode & MODEUIC && !ft_ngdebug) { fprintf(stdout,"Operating point simulation skipped by 'uic',\n"); fprintf(stdout," now using transient initial conditions.\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") || !strchr(node->name, '#')) fprintf(stdout,"%-30s %15g\n", node->name, ckt->CKTrhsOld[node->number]); } fprintf(stdout,"\n"); fflush(stdout); } /* return upon failure to converge during op */ if (converged != 0) { SPfrontEnd->OUTendPlot(job->TRANplot); return(converged); } #ifdef XSPICE /* IPC stuff: Send the operating point results */ if(g_ipc.enabled) { ipc_send_dcop_prefix(); CKTdump(ckt, 0.0, job->TRANplot); ipc_send_dcop_suffix(); } /* 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; /* Breakpoint stuff */ /* Initialize the temporary breakpoint variables to infinity */ g_mif_info.breakpoint.current = 1.0e30; g_mif_info.breakpoint.last = 1.0e30; #endif ckt->CKTstat->STATtimePts ++; ckt->CKTorder = 1; /* Initialze CKTdeltaOld with maximum value */ for(i=0;i<7;i++) { ckt->CKTdeltaOld[i]=ckt->CKTmaxStep; } ckt->CKTdelta = delta; /* delta set in line 130 */ #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 /* modeinittran set here */ ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN; /* Reset the gear variable coefficient matrix. */ ckt->CKTag[0]=ckt->CKTag[1]=0; if (ckt->CKTstate1 && ckt->CKTstate0) { memcpy(ckt->CKTstate1, ckt->CKTstate0, (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(); /* End of (restart || ckt->CKTtime == 0) */ } else { /* traninit resets CKTmode */ ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITPRED; INIT_STATS(); if(ckt->CKTminBreak == 0) ckt->CKTminBreak = ckt->CKTmaxStep * 5e-5; firsttime=0; /* To get rawfile working */ error = SPfrontEnd->OUTpBeginPlot (NULL, NULL, NULL, NULL, 0, 666, NULL, 666, &(job->TRANplot)); if(error) { fprintf(stderr, "Couldn't relink rawfile\n"); return error; } goto resume; /* line 517 */ } /* 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 /* Send IPC stuff */ if ((g_ipc.enabled) || wantevtdata) { /* 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 ) { if (wantevtdata) CKTdump(ckt, ckt->CKTtime, job->TRANplot); else { ipc_send_data_prefix(ckt->CKTtime); CKTdump(ckt, ckt->CKTtime, job->TRANplot); 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; } /* End of Send IPC stuff*/ } else #endif if((ckt->CKTmode&MODEUIC && ckt->CKTtime > 0 && ckt->CKTtime >= ckt->CKTinitTime) || (!(ckt->CKTmode&MODEUIC) && ckt->CKTtime >= ckt->CKTinitTime)) CKTdump(ckt, ckt->CKTtime, job->TRANplot); #ifdef XSPICE /* 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); #endif ckt->CKTstat->STAToldIter = ckt->CKTstat->STATnumIter; /* Check for the end of the tran simulation, either by stop time given, or final time has been reached. */ if (have_autostop) /* time consuming autostop check only, when variable 'autostop' has been set before tran is started.*/ flag_autostop = check_autostop("tran"); /* If CKTtime and CKTfinalTime are almost equal, then finish */ if (flag_autostop || 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->TRANplot); job->TRANplot = NULL; UPDATE_STATS(0); #ifdef WANT_SENSE2 if(ckt->CKTsenInfo && (ckt->CKTsenInfo->SENmode & TRANSEN)){ ckt->CKTsenInfo->SENmode = save; } #endif if (flag_autostop) fprintf(stdout, "\nNote: Autostop after %e s, all measurement conditions are fulfilled.\n", ckt->CKTtime); /* Final return from tran upon success */ 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/50CKTmaxStep) { (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 ( 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 /* More 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; } /* Throw out any permanent breakpoint with time <= current time or in the very near future, unless it is the final stop break. */ #ifdef STEPDEBUG printf(" brk_pt: %g ckt_time: %g ckt_min_break: %g\n", ckt->CKTbreaks[0], ckt->CKTtime, ckt->CKTminBreak); #endif while ((ckt->CKTbreaks[0] <= ckt->CKTtime + ckt->CKTminBreak || AlmostEqualUlps(ckt->CKTbreaks[0], ckt->CKTtime, 100)) && ckt->CKTbreaks[0] < ckt->CKTfinalTime) { #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); } /* 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; } #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 /* 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 */ #else /* no XSPICE */ #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 /* no XSPICE */ 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 SHARED_MODULE redostep = 1; #endif #ifdef XSPICE /* This is needed here to allow CAPask to output currents during Transient analysis. */ ckt->CKTcurrentAnalysis = DOING_TRAN; xspice_breakpoints_processed = 0; #endif olddelta=ckt->CKTdelta; /* time abort? */ ckt->CKTtime += ckt->CKTdelta; ckt->CKTdeltaOld[0]=ckt->CKTdelta; NIcomCof(ckt); #ifdef PREDICTOR error = NIpred(ckt); #endif /* PREDICTOR */ save_mode = ckt->CKTmode; save_order = ckt->CKTorder; #ifdef XSPICE /* Initialize temporary breakpoint to infinity */ g_mif_info.breakpoint.current = 1.0e30; /* 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; /* Call all hybrids */ /* Set evt_step */ if(ckt->evt->counts.num_insts > 0) { g_mif_info.circuit.evt_step = ckt->CKTtime; } #endif /* Central solver step */ converged = NIiter(ckt,ckt->CKTtranMaxIter); ckt->CKTstat->STATtimePts ++; ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITPRED; if(firsttime && ckt->CKTstate1 && ckt->CKTstate2 && ckt->CKTstate3) { memcpy(ckt->CKTstate2, ckt->CKTstate1, (size_t) ckt->CKTnumStates * sizeof(double)); memcpy(ckt->CKTstate3, ckt->CKTstate1, (size_t) ckt->CKTnumStates * sizeof(double)); } /* txl, cpl addition */ if (converged == 1111) { return(converged); } /* If no convergence in Central solver step */ if(converged != 0) { #ifndef SHARED_MODULE ckt->CKTtime = ckt->CKTtime -ckt->CKTdelta; ckt->CKTstat->STATrejected ++; #else redostep = 1; #endif ckt->CKTdelta = ckt->CKTdelta/8; #ifdef STEPDEBUG (void)printf("delta cut to %g for non-convergence\n",ckt->CKTdelta); fflush(stdout); #endif if(firsttime) { ckt->CKTmode = (ckt->CKTmode&MODEUIC) | MODETRAN | MODEINITTRAN; } ckt->CKTorder = 1; #ifdef XSPICE /* Add Breakpoint stuff */ } else if(g_mif_info.breakpoint.current < ckt->CKTtime) { /* Force backup if temporary breakpoint is < current time: - save old delta - retract time by old delta - new delta by difference between by retracted time and breakpoint */ past_breakpoint: 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; xspice_breakpoints_processed = 1; if(firsttime) { ckt->CKTmode = (ckt->CKTmode&MODEUIC)|MODETRAN | MODEINITTRAN; } ckt->CKTorder = 1; #endif } else { /* If converged: - go to next time step if this was the first time. - If not the first time step, don not accept, but check the truncation errors, and reduce delta accordingly, thenm redo the step, to bound the error. */ 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 SHARED_MODULE /* no check on first time point */ goto nextTime; /* line 373 */ #else redostep = 0; goto chkStep; #endif } newdelta = ckt->CKTdelta; /* Scan through all devices, estimate the truncation error, and reduce the time step, if necessary.*/ error = CKTtrunc(ckt,&newdelta); if(error) { UPDATE_STATS(DOING_TRAN); return(error); } if (newdelta > .9 * ckt->CKTdelta) { #if defined(XSPICE) /* The timestep has succeeded. XSPICE instances with * both analog and event ports ("hybrids") and others * that have called cm_irreversible() receive an EVENT * call here that allows them to capture their final * port values and advance co-simulations. As this is an EVENT * call, they are not expected to do any integrations, * so there is no need for a further convergence test. */ if (ckt->evt->counts.num_hybrids > 0) { g_mif_info.circuit.evt_step = ckt->CKTtime; EVTcall_hybrids(ckt); if (g_mif_info.breakpoint.current < ckt->CKTtime) { /* A hybrid requested a breakpoint in the past. */ goto past_breakpoint; } } #endif /* don't raise the order for backward Euler */ if ((ckt->CKTorder == 1) && (ckt->CKTmaxOrder > 1)) { 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 if (!ft_norefprint) { /* 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 SHARED_MODULE /* trapezoidal */ goto nextTime; /* line 373 */ #else redostep = 0; goto chkStep; #endif } else { /* not (newdelta > .9 * ckt->CKTdelta): reject the step - redo the time - apply the new (reduced) delta */ #ifndef SHARED_MODULE ckt->CKTtime = ckt->CKTtime -ckt->CKTdelta; ckt->CKTstat->STATrejected ++; #else redostep = 1; #endif ckt->CKTdelta = newdelta; #ifdef STEPDEBUG (void)printf( "delta set to truncation error result: point rejected\n"); #endif } } /* Set the new delta to delmin (minimum delta allowed). However: If the new delta has been less than the minimum delta for the second time, bail out with 'Timestep too small'. */ 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"); SPfrontEnd->OUTendPlot(job->TRANplot); job->TRANplot = NULL; UPDATE_STATS(0); /* return upon convergence failure */ return(E_TIMESTEP); } } #ifdef XSPICE /* Do event backup */ if(ckt->evt->counts.num_insts > 0) { #ifdef SHARED_MODULE double discard_start_time = ckt->CKTtime + ckt->CKTdelta; // ngspice in executable mode subtracts olddelta from the time // before new delta calculation, but it keeps delta in CKTtime and // postpones subtraction in library mode. Delayed subtraction leads // to incorrect points dropping because ckt->CKTdelta is almost always // less than olddelta if there are convergence issues, and EVTbackup // may drop valid events that need to be processed within // [last_accepted_time, last_accepted_time + ckt->CKTdelta] range // after delta adjustment. if (redostep && xspice_breakpoints_processed == 0) discard_start_time -= olddelta; EVTbackup(ckt, discard_start_time); #else EVTbackup(ckt, ckt->CKTtime + ckt->CKTdelta); #endif } #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: #ifdef XSPICE // There is no need to subtract olddelta from ckt->CKTtime one more time // if it has been subtracted during XSPICE breakpoint processing. // olddelta will be reinitialized on // the new iteration, so it reassigning here should be safe. It can't be // zeroed during breakpoint processing because it takes part in the // "timestep too small" check. olddelta_for_shared_sync = olddelta; if (xspice_breakpoints_processed) olddelta_for_shared_sync = 0.0; if(sharedsync(&ckt->CKTtime, &ckt->CKTdelta, olddelta_for_shared_sync, ckt->CKTfinalTime, ckt->CKTdelmin, redostep, &ckt->CKTstat->STATrejected, 1) == 0) goto nextTime; #else if(sharedsync(&ckt->CKTtime, &ckt->CKTdelta, olddelta, ckt->CKTfinalTime, ckt->CKTdelmin, redostep, &ckt->CKTstat->STATrejected, 1) == 0) goto nextTime; #endif // XSPICE #endif // SHARED_MODULE } /* NOTREACHED */ }