From ad5bb9eb8d038e9e52995df3d5ac12a71e6bca3f Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Mon, 3 Apr 2023 19:12:51 +0100 Subject: [PATCH] Fix Bug #607 - "DC Source with Pulse stops pulsing half way through simulation". Do not require breakpoints to be hit almost exactly before scheduling the next one. That may cause the next breakpoint to be lost. --- src/spicelib/devices/vsrc/vsrcacct.c | 274 +++++++++++++-------------- src/spicelib/devices/vsrc/vsrcdefs.h | 1 + src/spicelib/devices/vsrc/vsrcload.c | 65 +++---- src/spicelib/devices/vsrc/vsrcset.c | 1 + 4 files changed, 165 insertions(+), 176 deletions(-) diff --git a/src/spicelib/devices/vsrc/vsrcacct.c b/src/spicelib/devices/vsrc/vsrcacct.c index 1ec386681..0be45f8e2 100644 --- a/src/spicelib/devices/vsrc/vsrcacct.c +++ b/src/spicelib/devices/vsrc/vsrcacct.c @@ -19,9 +19,6 @@ extern void fftFree(void); extern bool ft_ngdebug; /* some additional debug info printed */ -#define SAMETIME(a,b) (fabs((a)-(b))<= TIMETOL * PW) -#define TIMETOL 1e-7 - int VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) /* set up the breakpoint table. */ @@ -51,7 +48,6 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) case PULSE: { double TD, TR, TF, PW, PER; - double tshift; double time = 0.; double basetime = 0; double tmax = 1e99; @@ -79,7 +75,6 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) /* offset time by delay */ time = ckt->CKTtime - TD; - tshift = TD; if (newcompat.xs) { /* normalize phase to 0 - 360° */ @@ -90,71 +85,55 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) while (deltat > 0) deltat -= PER; time += deltat; - tshift = TD - deltat; - } - else if (PHASE > 0.0) { + } else if (PHASE > 0.0) { tmax = PHASE * PER; + if (time > tmax) + break; } - if (!newcompat.xs && time > tmax) { - /* Do nothing */ - } - else { + if (ckt->CKTtime >= here->VSRCbreak_time) { + double wait; + if (time >= PER) { - /* repeating signal - figure out where we are */ - /* in period */ + /* Repeating signal: where in period are we? */ + basetime = PER * floor(time / PER); time -= basetime; } - if (time <= 0.0 || time >= TR + PW + TF) { - if (ckt->CKTbreak && SAMETIME(time, 0.0)) { - error = CKTsetBreak(ckt, basetime + TR + tshift); - if (error) return(error); - } - else if (ckt->CKTbreak && SAMETIME(TR + PW + TF, time)) { - error = CKTsetBreak(ckt, basetime + PER + tshift); - if (error) return(error); - } - else if (ckt->CKTbreak && (time == -tshift)) { - error = CKTsetBreak(ckt, basetime + tshift); - if (error) return(error); - } - else if (ckt->CKTbreak && SAMETIME(PER, time)) { - error = CKTsetBreak(ckt, basetime + tshift + TR + PER); - if (error) return(error); - } - } - else if (time >= TR && time <= TR + PW) { - if (ckt->CKTbreak && SAMETIME(time, TR)) { - error = CKTsetBreak(ckt, basetime + tshift + TR + PW); - if (error) return(error); - } - else if (ckt->CKTbreak && SAMETIME(TR + PW, time)) { - error = CKTsetBreak(ckt, basetime + tshift + TR + PW + TF); - if (error) return(error); - } - } - else if (time > 0 && time < TR) { - if (ckt->CKTbreak && SAMETIME(time, 0)) { - error = CKTsetBreak(ckt, basetime + tshift + TR); - if (error) return(error); - } - else if (ckt->CKTbreak && SAMETIME(time, TR)) { - error = CKTsetBreak(ckt, basetime + tshift + TR + PW); - if (error) return(error); - } - } - else { /* time > TR + PW && < TR + PW + TF */ - if (ckt->CKTbreak && SAMETIME(time, TR + PW)) { - error = CKTsetBreak(ckt, basetime + tshift + TR + PW + TF); - if (error) return(error); - } - else if (ckt->CKTbreak && SAMETIME(time, TR + PW + TF)) { - error = CKTsetBreak(ckt, basetime + tshift + PER); - if (error) return(error); - } + /* Set next breakpoint. */ + + if (time < 0.0) { + /* Await first pulse */ + + wait = -time; + } else if (time < TR) { + /* Wait for end of rise. */ + + wait = TR - time; + } else if (time < TR + PW) { + /* Wait for fall. */ + + wait = TR + PW - time; + } else if (time < TR + PW + TF) { + /* Wait for end of fall. */ + + wait = TR + PW + TF - time; + } else { + /* Wait for next pulse. */ + wait = PER - time; } + here->VSRCbreak_time = ckt->CKTtime + wait; + error = CKTsetBreak(ckt, here->VSRCbreak_time); + if (error) + return error; + + /* If a timestep ends just before the break time, + * the break request may be ignored. + * Set threshold for requesting following break. + */ + + here->VSRCbreak_time -= ckt->CKTminBreak; } } break; @@ -179,25 +158,46 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) } break; - case PWL: { - int i; - if(ckt->CKTtime < *(here->VSRCcoeffs)) { - if(ckt->CKTbreak) { - error = CKTsetBreak(ckt,*(here->VSRCcoeffs)); - break; + case PWL: + if (ckt->CKTtime >= here->VSRCbreak_time) { + double time, end, period; + int i; + + time = ckt->CKTtime - here->VSRCrdelay; + end = + here->VSRCcoeffs[here->VSRCfunctionOrder - 2]; + if (time > end) { + if (here->VSRCrGiven) { + /* Repeating. */ + + period = end - + here->VSRCcoeffs[here->VSRCrBreakpt]; + time -= period * floor(time / period); + } else { + here->VSRCbreak_time = ckt->CKTfinalTime; + break; + } } - } - for(i=0;i<(here->VSRCfunctionOrder/2)-1;i++) { - if ( ckt->CKTbreak && AlmostEqualUlps(*(here->VSRCcoeffs+2*i), ckt->CKTtime, 3 ) ) { - error = CKTsetBreak(ckt, *(here->VSRCcoeffs+2*i+2)); - if(error) return(error); - goto bkptset; + + for (i = 0; + i < here->VSRCfunctionOrder; + i += 2) { + if (here->VSRCcoeffs[i] > time) { + here->VSRCbreak_time = + ckt->CKTtime + + here->VSRCcoeffs[i] - time; + error = CKTsetBreak(ckt, + here->VSRCbreak_time); + if (error) + return error; + here->VSRCbreak_time -= ckt->CKTminBreak; + break; + } } } break; - } - /**** tansient noise routines: + /**** transient noise routines: VNoi2 2 0 DC 0 TRNOISE(10n 0.5n 0 0n) : generate gaussian distributed noise rms value, time step, 0 0 VNoi1 1 0 DC 0 TRNOISE(0n 0.5n 1 10n) : generate 1/f noise @@ -205,6 +205,7 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) */ case TRNOISE: { + struct trnoise_state *state = here -> VSRCtrnoise_state; double TS = state -> TS; double RTSAM = state ->RTSAM; @@ -220,61 +221,55 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) fftFree(); } #endif - - if(ckt->CKTbreak) { - - int n = (int) floor(ckt->CKTtime / TS + 0.5); - volatile double nearest = n * TS; - - if(AlmostEqualUlps(nearest, ckt->CKTtime, 3)) { - /* carefull calculate `next' - * make sure it is really identical - * with the next calculated `nearest' value - */ - volatile double next = (n+1) * TS; - error = CKTsetBreak(ckt, next); - if(error) - return(error); - } + if (TS > 0 && ckt->CKTtime >= here->VSRCbreak_time) { + if (here->VSRCbreak_time < 0.0) + here->VSRCbreak_time = TS; + else + here->VSRCbreak_time += TS; + error = CKTsetBreak(ckt, here->VSRCbreak_time); + if (error) + return(error); + here->VSRCbreak_time -= ckt->CKTminBreak; } - if (RTSAM > 0) { - double RTScapTime = state->RTScapTime; - double RTSemTime = state->RTSemTime; - double RTSCAPT = state->RTSCAPT; - double RTSEMT = state->RTSEMT; + if (RTSAM <= 0) + break; /* No shot noise. */ - if (ckt->CKTtime == 0) { - /* initialzing here again needed for repeated calls to tran command */ - state->RTScapTime = RTScapTime = exprand(RTSCAPT); - state->RTSemTime = RTSemTime = RTScapTime + exprand(RTSEMT); + if (ckt->CKTtime == 0) { + /* initialzing here again needed for repeated calls to tran command */ + state->RTScapTime = exprand(state->RTSCAPT); + state->RTSemTime = + state->RTScapTime + exprand(state->RTSEMT); + error = CKTsetBreak(ckt, state->RTScapTime); + if(error) + return(error); + break; + } - if (ckt->CKTbreak) { - error = CKTsetBreak(ckt, RTScapTime); - if(error) - return(error); - } - } + /* Break handling code ends a timestep close to + * the requested time. + */ - if(AlmostEqualUlps(RTScapTime, ckt->CKTtime, 3)) { - if (ckt->CKTbreak) { - error = CKTsetBreak(ckt, RTSemTime); - if(error) - return(error); - } - } + if (ckt->CKTtime >= + state->RTScapTime - ckt->CKTminBreak && + ckt->CKTtime <= + state->RTScapTime + ckt->CKTminBreak) { + error = CKTsetBreak(ckt, state->RTSemTime); + if(error) + return(error); + } - if(AlmostEqualUlps(RTSemTime, ckt->CKTtime, 3)) { - /* new values */ - RTScapTime = here -> VSRCtrnoise_state ->RTScapTime = ckt->CKTtime + exprand(RTSCAPT); - here -> VSRCtrnoise_state ->RTSemTime = RTScapTime + exprand(RTSEMT); + if (ckt->CKTtime >= + state->RTSemTime - ckt->CKTminBreak) { + /* new values */ - if (ckt->CKTbreak) { - error = CKTsetBreak(ckt, RTScapTime); - if(error) - return(error); - } - } + state->RTScapTime = + ckt->CKTtime + exprand(state->RTSCAPT); + state->RTSemTime = + state->RTScapTime + exprand(state->RTSEMT); + error = CKTsetBreak(ckt, state->RTScapTime); + if(error) + return(error); } } break; @@ -286,30 +281,22 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) if (ckt->CKTtime == 0 && TD > 0) { error = CKTsetBreak(ckt, TD); + here->VSRCbreak_time = TD; if (error) return(error); + break; } - double time = ckt->CKTtime - TD; - - if (time < 0) break; - - if(ckt->CKTbreak) { - - int n = (int) floor(time / TS + 0.5); - volatile double nearest = n * TS; - - if(AlmostEqualUlps(nearest, time, 10)) { - /* carefully calculate `next' - * make sure it is really identical - * with the next calculated `nearest' value - */ - volatile double next = (n+1) * TS + TD; - error = CKTsetBreak(ckt, next); - if(error) - return(error); - state->value = trrandom_state_get(state); - } + if (ckt->CKTtime >= here->VSRCbreak_time) { + if (here->VSRCbreak_time < 0.0) + here->VSRCbreak_time = TS; + else + here->VSRCbreak_time += TS; + error = CKTsetBreak(ckt, here->VSRCbreak_time); + if (error) + return(error); + here->VSRCbreak_time -= ckt->CKTminBreak; + state->value = trrandom_state_get(state); } } break; @@ -323,7 +310,6 @@ VSRCaccept(CKTcircuit *ckt, GENmodel *inModel) } // switch } // if ... else -bkptset: ; } // for } // for diff --git a/src/spicelib/devices/vsrc/vsrcdefs.h b/src/spicelib/devices/vsrc/vsrcdefs.h index 15c8b6951..346e75552 100644 --- a/src/spicelib/devices/vsrc/vsrcdefs.h +++ b/src/spicelib/devices/vsrc/vsrcdefs.h @@ -50,6 +50,7 @@ typedef struct sVSRCinstance { int VSRCfunctionType; /* code number of function type for source */ int VSRCfunctionOrder; /* order of the function for the source */ int VSRCrBreakpt; /* pwl repeat breakpoint index */ + double VSRCbreak_time; /* time of most-recent breakpoint */ double *VSRCcoeffs; /* pointer to array of coefficients */ double VSRCdcValue; /* DC and TRANSIENT value of source */ diff --git a/src/spicelib/devices/vsrc/vsrcload.c b/src/spicelib/devices/vsrc/vsrcload.c index beee1c99c..a4d4f73c6 100644 --- a/src/spicelib/devices/vsrc/vsrcload.c +++ b/src/spicelib/devices/vsrc/vsrcload.c @@ -298,43 +298,45 @@ VSRCload(GENmodel *inModel, CKTcircuit *ckt) break; case PWL: { - int i = 0, num_repeat = 0, ii = 0; - double foo, repeat_time = 0, end_time, breakpt_time, itime; + int i; + double end_time, itime; time -= here->VSRCrdelay; - - if(time < *(here->VSRCcoeffs)) { - foo = *(here->VSRCcoeffs + 1) ; - value = foo; - goto loadDone; + if (time < here->VSRCcoeffs[0]) { + value = here->VSRCcoeffs[1]; + value = value; + break; } - do { - for(i=ii ; i<(here->VSRCfunctionOrder/2)-1; i++ ) { - itime = *(here->VSRCcoeffs+2*i); - if ( AlmostEqualUlps(itime+repeat_time, time, 3 )) { - foo = *(here->VSRCcoeffs+2*i+1); - value = foo; - goto loadDone; - } else if ( (*(here->VSRCcoeffs+2*i)+repeat_time < time) - && (*(here->VSRCcoeffs+2*(i+1))+repeat_time > time) ) { - foo = *(here->VSRCcoeffs+2*i+1) + (((time-(*(here->VSRCcoeffs+2*i)+repeat_time))/ - (*(here->VSRCcoeffs+2*(i+1)) - *(here->VSRCcoeffs+2*i))) * - (*(here->VSRCcoeffs+2*i+3) - *(here->VSRCcoeffs+2*i+1))); - value = foo; - goto loadDone; - } + end_time = + here->VSRCcoeffs[here->VSRCfunctionOrder - 2]; + if (time > end_time) { + double period; + + if (here->VSRCrGiven) { + /* Repeating. */ + + period = end_time - + here->VSRCcoeffs[here->VSRCrBreakpt]; + time -= period * floor(time / period); + } else { + break; } - foo = *(here->VSRCcoeffs+ here->VSRCfunctionOrder-1) ; - value = foo; + } - if ( !here->VSRCrGiven ) goto loadDone; - - end_time = *(here->VSRCcoeffs + here->VSRCfunctionOrder-2); - breakpt_time = *(here->VSRCcoeffs + here->VSRCrBreakpt); - repeat_time = end_time + (end_time - breakpt_time)*num_repeat++ - breakpt_time; - ii = here->VSRCrBreakpt/2; - } while ( here->VSRCrGiven ); + for (i = 2; i < here->VSRCfunctionOrder; i += 2) { + itime = here->VSRCcoeffs[i]; + if (itime >= time) { + time -= here->VSRCcoeffs[i - 2]; + time /= here->VSRCcoeffs[i] - + here->VSRCcoeffs[i - 2]; + value = here->VSRCcoeffs[i - 1]; + value += time * + ( here->VSRCcoeffs[i + 1] - + here->VSRCcoeffs[i - 1]); + break; + } + } break; } @@ -418,7 +420,6 @@ VNoi3 3 0 DC 0 TRNOISE(0 0 0 0 15m 22u 50u) : generate RTS noise } // switch } // else (line 48) -loadDone: /* gtri - begin - wbk - modify for supply ramping option */ #ifdef XSPICE_EXP diff --git a/src/spicelib/devices/vsrc/vsrcset.c b/src/spicelib/devices/vsrc/vsrcset.c index 18d7764b7..3659e9755 100644 --- a/src/spicelib/devices/vsrc/vsrcset.c +++ b/src/spicelib/devices/vsrc/vsrcset.c @@ -31,6 +31,7 @@ VSRCsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt, int *state) for (here = VSRCinstances(model); here != NULL ; here=VSRCnextInstance(here)) { + here->VSRCbreak_time = -1.0; // To set initial breakpoint if(here->VSRCposNode == here->VSRCnegNode) { SPfrontEnd->IFerrorf (ERR_FATAL, "instance %s is a shorted VSRC", here->VSRCname);