fixed 555-timer-2 convergence issue

This commit is contained in:
Justin Fisher 2026-08-02 15:15:06 +02:00
parent 347c51c523
commit 96551df137
8 changed files with 62 additions and 6 deletions

View File

@ -151,6 +151,14 @@ struct CKTcircuit {
/* `.option nostaterestore` opt-out of the dctran failed-attempt /* `.option nostaterestore` opt-out of the dctran failed-attempt
* device-state restore (CKTstate0 <- CKTstate1 on timepoint retry). */ * device-state restore (CKTstate0 <- CKTstate1 on timepoint retry). */
int CKTstateRestoreOff; int CKTstateRestoreOff;
/* `.option noorderguard` opt-out of the dctran non-uniform-history
* predictor guard (forced CKTorder=1 on >10x dt-history ratio). */
int CKTorderGuardOff;
/* `.option currentsettle` opt-IN to honoring device convTest
* CKTnoncon reports in NIconvTest (upstream trunk 0638aaa16
* current-settling enforcement). Default 0 = ngspice-46
* solution-only convergence semantics. */
int CKTcurrentSettle;
/* Per-iteration count of huge-finite Jacobian entries clipped by /* Per-iteration count of huge-finite Jacobian entries clipped by
* sanitize_jacobian during CKTload. When > 0, the model evaluation * sanitize_jacobian during CKTload. When > 0, the model evaluation
* is in a numerical regime (e.g. BSIM-BULK near a singular operating * is in a numerical regime (e.g. BSIM-BULK near a singular operating

View File

@ -97,6 +97,8 @@ enum {
* SPICE3 solution-only behaviour. */ * SPICE3 solution-only behaviour. */
OPT_NOSTATERESTORE, /* `.option nostaterestore` opts out of the dctran OPT_NOSTATERESTORE, /* `.option nostaterestore` opts out of the dctran
* failed-attempt device-state restore. */ * failed-attempt device-state restore. */
OPT_NOORDERGUARD, /* `.option noorderguard` opts out of the dctran
* non-uniform-history predictor order guard. */
OPT_EQNS, OPT_EQNS,
OPT_REORDTIME, OPT_REORDTIME,
OPT_METHOD, OPT_METHOD,
@ -156,6 +158,14 @@ enum {
OPT_NODTCLEAR, /* `.option nodtclear` disables the small-dt OPT_NODTCLEAR, /* `.option nodtclear` disables the small-dt
* CKTnoncon clear in niiter.c. See CKTdtClearOff * CKTnoncon clear in niiter.c. See CKTdtClearOff
* comment in cktdefs.h. */ * comment in cktdefs.h. */
OPT_CURRENTSETTLE, /* `.option currentsettle` opts IN to NIconvTest
* honoring device convTest CKTnoncon reports.
* NOTE: appended at the enum TAIL deliberately
* the region between OPT_GMIN=1 and
* OPT_SYNCTIME=58 is full; inserting there
* collides auto-numbered members with the
* OPT_SYNCTIME anchor (duplicate-case build
* error in cktacct.c). */
}; };
#ifdef XSPICE #ifdef XSPICE

View File

@ -75,6 +75,12 @@ struct TSKtask {
unsigned int TSKnoStateRestore:1; /* `.option nostaterestore` disables unsigned int TSKnoStateRestore:1; /* `.option nostaterestore` disables
* the dctran failed-attempt state * the dctran failed-attempt state
* restore (CKTstate0 <- CKTstate1) */ * restore (CKTstate0 <- CKTstate1) */
unsigned int TSKnoOrderGuard:1; /* `.option noorderguard` disables
* the dctran non-uniform-history
* predictor order guard */
unsigned int TSKcurrentSettle:1; /* `.option currentsettle` enables
* device-current settling
* enforcement in NIconvTest */
unsigned int TSKtryToCompact:1; /* flag for LTRA lines */ unsigned int TSKtryToCompact:1; /* flag for LTRA lines */
unsigned int TSKbadMos3:1; /* flag for MOS3 models */ unsigned int TSKbadMos3:1; /* flag for MOS3 models */
unsigned int TSKkeepOpInfo:1; /* flag for small signal analyses */ unsigned int TSKkeepOpInfo:1; /* flag for small signal analyses */

View File

@ -82,8 +82,17 @@ NIconvTest(CKTcircuit *ckt)
#ifdef NEWCONV #ifdef NEWCONV
i = CKTconvTest(ckt); i = CKTconvTest(ckt);
/* The individual testers, called by CKTconvTest, set /* The individual testers, called by CKTconvTest, set
* ckt->CKTnoncon and ckt->CKTtroubleElt appropriately. */ * ckt->CKTnoncon and ckt->CKTtroubleElt appropriately.
if (ckt->CKTnoncon != 0) { *
* Honoring those reports here (upstream trunk 0638aaa16,
* "Continue Newton iteration until currents settle") activates a
* device-current settling test that had been dead code since
* spice3f5. It regresses legacy decks whose devices genuinely
* oscillate at tight tolerances yet simulated acceptably for
* decades under the solution-only test (e.g. the bundled
* 555-timer-2 example at its own RELTOL=1e-4). Enforce only on
* `.option currentsettle`; the default matches ngspice-46. */
if (ckt->CKTcurrentSettle && ckt->CKTnoncon != 0) {
ckt->CKTtroubleNode = 0; ckt->CKTtroubleNode = 0;
return(1); return(1);
} }

View File

@ -68,8 +68,8 @@ int OSDItrunc(GENmodel *in_model, CKTcircuit *ckt, double *timestep) {
* the most recently accepted step so that fast edges are resolved * the most recently accepted step so that fast edges are resolved
* gradually. * gradually.
* *
* Was 2.0× originally, tightened to 1.5× for pinb/net_7 oscillation * Was 2.0× originally, tightened to 1.5× for pinb/net_7 oscillation,
* (commit 143a0805f) and now to 1.2× for foundry_a ULP driver_lv_2v5_tb * and now to 1.2× for foundry_a ULP driver_lv_2v5_tb
* to address residual pinb failure at t 1.369 µs. At 1.5×, dt * to address residual pinb failure at t 1.369 µs. At 1.5×, dt
* could grow from ~10 ps post-breakpoint to ~5 ns (the user's max * could grow from ~10 ps post-breakpoint to ~5 ns (the user's max
* step) in ~15 accepted steps; once Newton had to land a 0.5 V swing * step) in ~15 accepted steps; once Newton had to land a 0.5 V swing

View File

@ -101,6 +101,8 @@ CKTdoJob(CKTcircuit* ckt, int reset, TSKtask* task)
ckt->CKTosdiStepRejectOff = task->TSKnoOsdiStepReject; ckt->CKTosdiStepRejectOff = task->TSKnoOsdiStepReject;
ckt->CKTdtClearOff = task->TSKnoDtClear; ckt->CKTdtClearOff = task->TSKnoDtClear;
ckt->CKTstateRestoreOff = task->TSKnoStateRestore; ckt->CKTstateRestoreOff = task->TSKnoStateRestore;
ckt->CKTorderGuardOff = task->TSKnoOrderGuard;
ckt->CKTcurrentSettle = task->TSKcurrentSettle;
ckt->CKTosdiVlim = task->TSKosdiVlim; ckt->CKTosdiVlim = task->TSKosdiVlim;
ckt->CKTosdiVlimVds = task->TSKosdiVlimVds; ckt->CKTosdiVlimVds = task->TSKosdiVlimVds;
ckt->CKTosdiVlimVgs = task->TSKosdiVlimVgs; ckt->CKTosdiVlimVgs = task->TSKosdiVlimVgs;

View File

@ -53,6 +53,12 @@ CKTsetOpt(CKTcircuit *ckt, JOB *anal, int opt, IFvalue *val)
case OPT_NOSTATERESTORE: case OPT_NOSTATERESTORE:
task->TSKnoStateRestore = (val->iValue != 0); task->TSKnoStateRestore = (val->iValue != 0);
break; break;
case OPT_NOORDERGUARD:
task->TSKnoOrderGuard = (val->iValue != 0);
break;
case OPT_CURRENTSETTLE:
task->TSKcurrentSettle = (val->iValue != 0);
break;
case OPT_GMIN: case OPT_GMIN:
task->TSKgmin = val->rValue; task->TSKgmin = val->rValue;
break; break;
@ -304,6 +310,8 @@ static IFparm OPTtbl[] = {
{ "noosdistepreject", OPT_NOOSDISTEPREJECT, IF_SET|IF_FLAG, "Disable OSDI axis-3 step rejection (osdiload.c sanitize_jacobian + REJECT_STEP)" }, { "noosdistepreject", OPT_NOOSDISTEPREJECT, IF_SET|IF_FLAG, "Disable OSDI axis-3 step rejection (osdiload.c sanitize_jacobian + REJECT_STEP)" },
{ "nodtclear", OPT_NODTCLEAR, IF_SET|IF_FLAG, "Disable small-dt CKTnoncon clear (niiter.c); revert to abort-from-spurious-noncon at dt<1ps" }, { "nodtclear", OPT_NODTCLEAR, IF_SET|IF_FLAG, "Disable small-dt CKTnoncon clear (niiter.c); revert to abort-from-spurious-noncon at dt<1ps" },
{ "nostaterestore", OPT_NOSTATERESTORE, IF_SET|IF_FLAG, "Disable dctran failed-attempt device-state restore (CKTstate0 <- CKTstate1 on retry)" }, { "nostaterestore", OPT_NOSTATERESTORE, IF_SET|IF_FLAG, "Disable dctran failed-attempt device-state restore (CKTstate0 <- CKTstate1 on retry)" },
{ "noorderguard", OPT_NOORDERGUARD, IF_SET|IF_FLAG, "Disable dctran non-uniform-history predictor order guard (forced order-1 on >10x dt ratio)" },
{ "currentsettle", OPT_CURRENTSETTLE, IF_SET|IF_FLAG, "Enable device-current settling enforcement in NIconvTest (trunk 0638aaa16 behaviour; default is ngspice-46 solution-only)" },
{ "gmin", OPT_GMIN,IF_SET|IF_REAL,"Minimum conductance" }, { "gmin", OPT_GMIN,IF_SET|IF_REAL,"Minimum conductance" },
{ "gshunt", OPT_GSHUNT,IF_SET|IF_REAL,"Shunt conductance" }, { "gshunt", OPT_GSHUNT,IF_SET|IF_REAL,"Shunt conductance" },
{ "reltol", OPT_RELTOL,IF_SET|IF_REAL ,"Relative error tolerence"}, { "reltol", OPT_RELTOL,IF_SET|IF_REAL ,"Relative error tolerence"},

View File

@ -710,8 +710,21 @@ resume:
* *
* Threshold of 10× chosen so smooth dt-growth sequences * Threshold of 10× chosen so smooth dt-growth sequences
* (e.g. the 1.5× cap, factor 1.5 per step) never trigger * (e.g. the 1.5× cap, factor 1.5 per step) never trigger
* only genuine breakpoint-induced discontinuities. */ * only genuine breakpoint-induced discontinuities.
if (ckt->CKTorder > 1 && *
* OSDI circuits only (CKTosdiPresent), like the NIiter Δv
* limiter: the failure mode this guards against was observed
* on OSDI/BSIM-BULK drivers, while for native-device decks
* the forced order-1 steps inflate LTE at breakpoint-dense
* switching (smaller dt), and combined with the axis-4
* residual check this walled the bundled 555-timer-2 example
* at t=3.27ms (RELTOL=1e-4). Stock predictor behaviour is
* restored for non-OSDI circuits.
*
* Opt-out via `.option noorderguard` (diagnostic: lets a
* suspect deck run with the stock always-order-2 predictor). */
if (ckt->CKTosdiPresent &&
!ckt->CKTorderGuardOff && ckt->CKTorder > 1 &&
ckt->CKTdeltaOld[1] > 0.0 && ckt->CKTdeltaOld[2] > 0.0) { ckt->CKTdeltaOld[1] > 0.0 && ckt->CKTdeltaOld[2] > 0.0) {
double r = ckt->CKTdeltaOld[1] / ckt->CKTdeltaOld[2]; double r = ckt->CKTdeltaOld[1] / ckt->CKTdeltaOld[2];
if (r > 10.0 || r < 0.1) { if (r > 10.0 || r < 0.1) {