fixed memory leaks

This commit is contained in:
Justin Fisher 2026-08-02 12:52:54 +02:00
parent e50dee9ccc
commit 13d55715fc
11 changed files with 85 additions and 12 deletions

View File

@ -497,18 +497,24 @@ com_measure_when(
}
if (has_d2) {
/* The loop index runs over d's length; d2 may be shorter
* (e.g. a length-1 measure-result vector on the right hand
* side of WHEN v(x)=NAME). Clamp to d2's last element so
* a short vector reads as a held constant instead of
* running past its allocation. */
int i2 = (i < d2->v_length) ? i : d2->v_length - 1;
if (ac_check) {
if (d2->v_compdata)
value2 = get_value(meas, d2, i); //d->v_compdata[i].cx_real;
value2 = get_value(meas, d2, i2); //d->v_compdata[i].cx_real;
else
value2 = d2->v_realdata[i];
value2 = d2->v_realdata[i2];
} else if (sp_check) {
if (d2->v_compdata)
value2 = get_value(meas, d2, i); //d->v_compdata[i].cx_real;
value2 = get_value(meas, d2, i2); //d->v_compdata[i].cx_real;
else
value2 = d2->v_realdata[i];
value2 = d2->v_realdata[i2];
} else {
value2 = d2->v_realdata[i];
value2 = d2->v_realdata[i2];
}
} else {
value2 = NAN;

View File

@ -27,6 +27,10 @@ extern bool ft_batchmode;
extern bool rflag;
/* Set by INPevaluate's HSPICE-compat bare-.param resolution path
(inpeval.c); see the substitution pre-pass below. */
extern char *inpeval_last_param_name;
/* measure in interactive mode:
meas command inside .control ... .endc loop or manually entered.
meas has to be followed by the standard tokens (see measure_extract_variables()).
@ -44,6 +48,7 @@ com_meas(wordlist *wl)
wordlist *wl_index;
struct dvec *d;
int err = 0;
double subst_val;
int fail;
double result = 0;
@ -76,7 +81,8 @@ com_meas(wordlist *wl)
vec_found = wl_index->wl_word;
/* token may be already a value, maybe 'LAST', which we have to keep, or maybe a vector */
if (!cieq(vec_found, "LAST")) {
INPevaluate(&vec_found, &err, 1);
char *orig_word = wl_index->wl_word;
subst_val = INPevaluate(&vec_found, &err, 1);
/* if not a valid number */
if (err) {
/* check if vec_found is a valid vector */
@ -86,8 +92,18 @@ com_meas(wordlist *wl)
if (d && (d->v_length == 1) && (d->v_numdims == 1)) {
/* get its value */
wl_index->wl_word = tprintf("%e", d->v_realdata[0]);
tfree(vec_found);
tfree(orig_word);
}
} else if (inpeval_last_param_name) {
/* INPevaluate resolved a bare .param identifier (e.g.
a prior .measure result registered via
nupa_add_param). Write the value back so the
measure parser sees a number otherwise the
identifier survives as text and com_measure2
treats it as a full vector, which mis-measures
(and over-reads a length-1 result vector). */
wl_index->wl_word = tprintf("%e", subst_val);
tfree(orig_word);
}
}
}
@ -95,7 +111,7 @@ com_meas(wordlist *wl)
else if ((equal_ptr = strchr(token, '=')) != NULL) {
vec_found = equal_ptr + 1;
if (!cieq(vec_found, "LAST")) {
INPevaluate(&vec_found, &err, 1);
subst_val = INPevaluate(&vec_found, &err, 1);
if (err) {
d = vec_get(vec_found);
/* Only if we have a single valued vector, replacing
@ -106,6 +122,13 @@ com_meas(wordlist *wl)
tprintf("%.*s=%e", lhs_len, token, d->v_realdata[0]);
tfree(token);
}
} else if (inpeval_last_param_name) {
/* bare .param identifier resolved — write the value
back (see whole-token case above) */
int lhs_len = (int)(equal_ptr - token);
wl_index->wl_word =
tprintf("%.*s=%e", lhs_len, token, subst_val);
tfree(token);
}
}
} else {

View File

@ -143,6 +143,14 @@ struct CKTcircuit {
* opt-out. */
int CKTosdiStepReject;
int CKTosdiStepRejectOff;
/* Set by OSDIsetup when the circuit contains at least one OSDI
* (Verilog-A) device. Gates OSDI-motivated Newton machinery the
* per-node Δv limiter in NIiter so that circuits built purely
* from native SPICE devices keep stock SPICE3 iteration behaviour. */
int CKTosdiPresent;
/* `.option nostaterestore` opt-out of the dctran failed-attempt
* device-state restore (CKTstate0 <- CKTstate1 on timepoint retry). */
int CKTstateRestoreOff;
/* Per-iteration count of huge-finite Jacobian entries clipped by
* sanitize_jacobian during CKTload. When > 0, the model evaluation
* is in a numerical regime (e.g. BSIM-BULK near a singular operating

View File

@ -95,6 +95,8 @@ enum {
OPT_NORESIDCHECK, /* `.option noresidcheck` opts out of axis-4 dual-norm
* convergence (residual-vector check); revert to
* SPICE3 solution-only behaviour. */
OPT_NOSTATERESTORE, /* `.option nostaterestore` opts out of the dctran
* failed-attempt device-state restore. */
OPT_EQNS,
OPT_REORDTIME,
OPT_METHOD,

View File

@ -72,6 +72,9 @@ struct TSKtask {
unsigned int TSKnoDtClear:1; /* `.option nodtclear` disables the
* small-dt CKTnoncon clear in
* niiter.c */
unsigned int TSKnoStateRestore:1; /* `.option nostaterestore` disables
* the dctran failed-attempt state
* restore (CKTstate0 <- CKTstate1) */
unsigned int TSKtryToCompact:1; /* flag for LTRA lines */
unsigned int TSKbadMos3:1; /* flag for MOS3 models */
unsigned int TSKkeepOpInfo:1; /* flag for small signal analyses */

View File

@ -411,7 +411,7 @@ NIiter(CKTcircuit *ckt, int maxIter)
ckt->CKTrhsOld[0] = 0;
/* Newton-step limiter (simulator-side $limit substitute).
* Always-on during transient (and DC OP) to clamp per-node
* Active during transient (and DC OP) to clamp per-node
* |Δv| to ±CKTabsDv between Newton iterations. Matches
* the default behaviour of Spectre/HSPICE DEVlimvds-style
* limiters which fire on every iteration as a model-
@ -419,6 +419,17 @@ NIiter(CKTcircuit *ckt, int maxIter)
* 0.5 V) a model-parameter-agnostic tolerance, not a
* voltage rail.
*
* OSDI circuits only (CKTosdiPresent): the limiter exists
* as a substitute for model-supplied $limit calls that
* OSDI/Verilog-A models may lack. Applying it to every
* circuit regressed non-OSDI decks whose behavioral
* macromodels legitimately need multi-kV Newton steps
* (PSpice opamp libs: TABLE sources swing to ±3.5 kV)
* the clamp forced a crawl whose small |Δx| the axis-4
* residual check then correctly refused, so OP, gmin and
* source stepping all failed. Native SPICE devices carry
* their own pnjlim/limvds limiting and never needed this.
*
* Skipped on iteration 1 (no previous iterate to compare).
* Skipped when CKTnodes is NULL (matrix not yet built).
*
@ -426,7 +437,7 @@ NIiter(CKTcircuit *ckt, int maxIter)
* $limit synthesis pass, the model's own limiters apply
* inside descr->eval() this simulator-side limiter then
* sees already-limited Δv and does nothing additional. */
if (iterno > 1 && ckt->CKTnodes != NULL) {
if (ckt->CKTosdiPresent && iterno > 1 && ckt->CKTnodes != NULL) {
double dv_max = (ckt->CKTabsDv > 0) ? ckt->CKTabsDv : 0.5;
/* Compute current iteration's max|Δv|. Needed both
* for the Stage A scalar scaling and for the Stage B

View File

@ -319,6 +319,11 @@ int OSDIsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt,
OsdiSimParas sim_params_ = get_simparams(ckt);
OsdiSimParas *sim_params = &sim_params_;
/* Mark the circuit as containing OSDI devices — gates OSDI-motivated
* Newton machinery (per-node Δv limiter in NIiter) so purely native
* circuits keep stock iteration behaviour. */
ckt->CKTosdiPresent = 1;
/* setup a temporary buffer */
uint32_t *node_ids = TMALLOC(uint32_t, descr->num_nodes);

View File

@ -100,6 +100,7 @@ CKTdoJob(CKTcircuit* ckt, int reset, TSKtask* task)
ckt->CKTresidCheckDisabled = task->TSKnoResidCheck;
ckt->CKTosdiStepRejectOff = task->TSKnoOsdiStepReject;
ckt->CKTdtClearOff = task->TSKnoDtClear;
ckt->CKTstateRestoreOff = task->TSKnoStateRestore;
ckt->CKTosdiVlim = task->TSKosdiVlim;
ckt->CKTosdiVlimVds = task->TSKosdiVlimVds;
ckt->CKTosdiVlimVgs = task->TSKosdiVlimVgs;

View File

@ -50,6 +50,9 @@ CKTsetOpt(CKTcircuit *ckt, JOB *anal, int opt, IFvalue *val)
case OPT_NODTCLEAR:
task->TSKnoDtClear = (val->iValue != 0);
break;
case OPT_NOSTATERESTORE:
task->TSKnoStateRestore = (val->iValue != 0);
break;
case OPT_GMIN:
task->TSKgmin = val->rValue;
break;
@ -300,6 +303,7 @@ static IFparm OPTtbl[] = {
{ "noresidcheck", OPT_NORESIDCHECK, IF_SET|IF_FLAG, "Disable axis-4 residual-vector convergence check; SPICE3-only behaviour" },
{ "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" },
{ "nostaterestore", OPT_NOSTATERESTORE, IF_SET|IF_FLAG, "Disable dctran failed-attempt device-state restore (CKTstate0 <- CKTstate1 on retry)" },
{ "gmin", OPT_GMIN,IF_SET|IF_REAL,"Minimum conductance" },
{ "gshunt", OPT_GSHUNT,IF_SET|IF_REAL,"Shunt conductance" },
{ "reltol", OPT_RELTOL,IF_SET|IF_REAL ,"Relative error tolerence"},

View File

@ -773,8 +773,14 @@ resume:
* retries) until the state explodes and dt collapses to
* delmin. Re-prime the working state from the last ACCEPTED
* point so every retry starts from physical values, exactly
* like a first attempt does. */
if (ckt->CKTstate0 && ckt->CKTstate1)
* like a first attempt does.
*
* Opt-out via `.option nostaterestore` (suspected of
* regressing non-OSDI example decks on raw-transient-start
* paths uic / optran / mid-run retries where CKTstate1
* may not hold a meaningfully accepted point). */
if (!ckt->CKTstateRestoreOff &&
ckt->CKTstate0 && ckt->CKTstate1)
memcpy(ckt->CKTstate0, ckt->CKTstate1,
(size_t) ckt->CKTnumStates * sizeof(double));

View File

@ -30,6 +30,10 @@ INPevaluate(char **line, int *error, int gobble)
char *tmpline;
/* setup */
/* Clear the bare-.param side channel on every entry, as its
* consumers (INPdevParse, com_meas) assume a non-NULL value must
* only ever mean THIS call resolved a bare identifier. */
inpeval_last_param_name = NULL;
tmpline = *line;
if (gobble) {