fixed memory leaks
This commit is contained in:
parent
8fd86075c1
commit
347c51c523
|
|
@ -443,9 +443,9 @@ RCROSS2 B0 A24 0.001
|
|||
**
|
||||
**INCLUDING FILE: ./proj1/process.models....
|
||||
*
|
||||
* Typical N Typical P - from process corners (taken from tsmc025_corners.bsim3 fron NCSU)
|
||||
* Typical N Typical P - from process corners (taken from foundry_a.bsim3 fron NCSU)
|
||||
*
|
||||
* TSMC 0.25u 5M 1P process. 2.5V transistor models
|
||||
* foundry_a 0.25u 5M 1P process. 2.5V transistor models
|
||||
|
||||
|
||||
.MODEL Nmod NMOS LEVEL=8
|
||||
|
|
|
|||
|
|
@ -441,9 +441,9 @@ RCROSS2 B0 A24 0.001
|
|||
**
|
||||
**INCLUDING FILE: ./proj1/process.models....
|
||||
*
|
||||
* Typical N Typical P - from process corners (taken from tsmc025_corners.bsim3 fron NCSU)
|
||||
* Typical N Typical P - from process corners (taken from foundry_a.bsim3 fron NCSU)
|
||||
*
|
||||
* TSMC 0.25u 5M 1P process. 2.5V transistor models
|
||||
* foundry_a 0.25u 5M 1P process. 2.5V transistor models
|
||||
|
||||
|
||||
.MODEL Nmod NMOS LEVEL=8
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ WHY:
|
|||
rule is kept outside HS mode. (8) The single-to-brace quote conversion now
|
||||
skips `.del`, `.include`, and `.inc ` lines (which take literal file paths),
|
||||
matching the existing `.lib` skip, to avoid handing paths to numparam as
|
||||
expressions (STEP 24, observed on GF55 bcd55 .alter decks).
|
||||
expressions (STEP 24, observed on foundry_c bcd55 .alter decks).
|
||||
|
||||
CHANGE:
|
||||
@@ -1072,9 +1080,22 @@
|
||||
|
|
@ -582,10 +582,10 @@ WHY:
|
|||
midpoint 1.3999999999999999e-08), while integer comparisons remain unambiguous
|
||||
(STEP 12). (4) When an identifier matches a function keyword, it is now
|
||||
treated as a function call ONLY if followed by `(`; otherwise it is treated as
|
||||
a parameter name, so GF55 decks that use `var` as a subckt parameter aren't
|
||||
a parameter name, so foundry_c decks that use `var` as a subckt parameter aren't
|
||||
shadowed by the built-in `var` function (STEP 17). (5) A unary `+` following a
|
||||
binary operator (e.g. `0.67*+2e-8`) is now accepted as a no-op sign, symmetric
|
||||
with the existing unary-minus handling, fixing GF55 "Misplaced operator"
|
||||
with the existing unary-minus handling, fixing foundry_c "Misplaced operator"
|
||||
failures. (6) Added the `nupa_eval_with_scope()` implementation: pushes a
|
||||
fresh symbol-table scope, populates it via attrib(), runs formula(), then
|
||||
frees the scope with del_attrib WITHOUT promoting locals to globals, used by
|
||||
|
|
@ -662,7 +662,7 @@ CHANGE:
|
|||
+ * `vec`, `min`, `max`, `pow`, `table_param`). Only treat
|
||||
+ * it as a function call if it's followed by `(` (after
|
||||
+ * optional whitespace). Otherwise fall back to treating
|
||||
+ * it as a parameter name — foundry decks (GF55 bcd55
|
||||
+ * it as a parameter name — foundry decks (foundry_c bcd55
|
||||
+ * diode_rr.inc) use `var` as a subckt parameter, and
|
||||
+ * shadowing it with the built-in function broke
|
||||
+ * `vrb='var'` and similar chains. */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ struct function_env
|
|||
const char *accept;
|
||||
} *functions;
|
||||
/* Hash on `name` for O(1) lookup in find_function. Foundry PDKs
|
||||
* register hundreds-to-thousands of .funcs (Samsung 14LPU's TT
|
||||
* register hundreds-to-thousands of .funcs (foundry_b 14LPU's TT
|
||||
* corner has ~1300), and find_function is called once per `(` in
|
||||
* every line during macro expansion — the linear scan was billions
|
||||
* of strcmp on real decks. */
|
||||
|
|
@ -3725,8 +3725,8 @@ static void inp_stripcomments_line(char *s, bool cs, bool inc)
|
|||
/* outside of .control section, and not in PS mode */
|
||||
else if (!cs && (c == '$') && !newcompat.ps) {
|
||||
/* HSPICE treats '$' as an end-of-line comment regardless of
|
||||
* the preceding character — foundry decks (Samsung 14LPU,
|
||||
* TSMC, GF) routinely write `...)'$ comment` or `...=10u$ ...`
|
||||
* the preceding character — foundry decks (foundry_b 14LPU,
|
||||
* foundry_a, GF) routinely write `...)'$ comment` or `...=10u$ ...`
|
||||
* with no separator. In ngbehavior=hs / hsa, accept that.
|
||||
*
|
||||
* Outside HS mode keep the original conservative rule (only
|
||||
|
|
@ -4000,7 +4000,7 @@ static void inp_fix_for_numparam(
|
|||
* conversion that would otherwise hand the path to
|
||||
* numparam as an expression and trigger
|
||||
* Number format error: "../path...} <section>"
|
||||
* Observed on GF55 bcd55 sample_netlist/isoednfet_5p0_lr.sp
|
||||
* Observed on foundry_c bcd55 sample_netlist/isoednfet_5p0_lr.sp
|
||||
* which uses `.del lib '../models/design_wrapper.lib'` in
|
||||
* .alter blocks. Matches the existing `.lib` skip above. */
|
||||
if (ciprefix(".del", c->line) ||
|
||||
|
|
@ -5644,7 +5644,7 @@ static void inp_sort_params(struct card *param_cards,
|
|||
* scanning every other param's expression for occurrences of each
|
||||
* param's name
|
||||
* Both passes are now O(N) + O(total expression chars), enabling real
|
||||
* PDK use (Samsung 14LPU's TT corner had N~2700 per subckt, called
|
||||
* PDK use (foundry_b 14LPU's TT corner had N~2700 per subckt, called
|
||||
* ~500 times, totalling many billions of ops in the old code).
|
||||
*
|
||||
* Encoding: hash stores `(void *)(intptr_t)(i + 1)` so that the NULL
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -749,7 +749,7 @@ nupa_eval(struct card *card)
|
|||
} else if (c == 'B') { /* substitute braces line */
|
||||
/* nupa_substitute() may reallocate line buffer. */
|
||||
|
||||
/* HSPICE-style OSDI model cards (Samsung 14LPU et al.) embed
|
||||
/* HSPICE-style OSDI model cards (foundry_b 14LPU et al.) embed
|
||||
* `{...}` expressions on the right-hand side of `.model`
|
||||
* params that reference per-instance geometry symbols (`l`,
|
||||
* `w`, `nf`, `m`, `xnf`). These cannot be evaluated at
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ static Table *load_table_file(const char *path) {
|
|||
* a malloc'd absolute path the caller must free, or a copy of the
|
||||
* input if no search is needed.
|
||||
*
|
||||
* Foundry PDKs (Samsung 14LPU) reference tables by paths relative to
|
||||
* Foundry PDKs (foundry_b 14LPU) reference tables by paths relative to
|
||||
* the .lib file that uses them — `./RF_COMPONENTS/foo.table` is
|
||||
* relative to the directory of fets_rf.lib (or wherever the call
|
||||
* originated), NOT to the user's cwd or ngspice's sourcepath.
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/*
|
||||
* HSPICE table_param() implementation.
|
||||
*
|
||||
* Foundry PDKs (Samsung 14LPU, TSMC, GF) use HSPICE's table_param()
|
||||
* Foundry PDKs (foundry_b 14LPU, foundry_a, GF) use HSPICE's table_param()
|
||||
* extensively for table-file-based parameter lookup — self-heating
|
||||
* thermal resistance, RF parasitics, etc. Samsung's TT corner alone
|
||||
* thermal resistance, RF parasitics, etc. foundry_b's TT corner alone
|
||||
* references it ~1300 times.
|
||||
*
|
||||
* Syntax:
|
||||
|
|
|
|||
|
|
@ -1210,7 +1210,7 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
|
|||
* `vec`, `min`, `max`, `pow`, `table_param`). Only treat
|
||||
* it as a function call if it's followed by `(` (after
|
||||
* optional whitespace). Otherwise fall back to treating
|
||||
* it as a parameter name — foundry decks (GF55 bcd55
|
||||
* it as a parameter name — foundry decks (foundry_c bcd55
|
||||
* diode_rr.inc) use `var` as a subckt parameter, and
|
||||
* shadowing it with the built-in function broke
|
||||
* `vrb='var'` and similar chains. */
|
||||
|
|
@ -1260,7 +1260,7 @@ formula(dico_t *dico, const char *s, const char *s_end, bool *perror)
|
|||
/* Symmetric to the `c == '-'` case above: a unary `+` directly
|
||||
* following a binary operator (e.g. `0.67*+2e-8`) is a no-op
|
||||
* sign — drop it and re-read the next token. Foundry decks
|
||||
* (GF55 bcd55 fixed_corner_bcdlite.inc) use this idiom for
|
||||
* (foundry_c bcd55 fixed_corner_bcdlite.inc) use this idiom for
|
||||
* explicit-sign literals: `(sw5)*(0.67*+2e-8)`. Without this,
|
||||
* the unary-plus form failed with "Misplaced operator" while
|
||||
* the unary-minus form parsed fine. */
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ inp_subcktexpand(struct card *deck) {
|
|||
* name (typically the 5th token, after the 4 node terminals) names
|
||||
* the type. ngspice's subckt expander only handles the .subckt
|
||||
* case; if no .subckt matches, we'd error here. But foundry PDKs
|
||||
* (Samsung 14LPU, TSMC, GF) routinely embed VA-module diagnostic
|
||||
* (foundry_b 14LPU, foundry_a, GF) routinely embed VA-module diagnostic
|
||||
* instances like `xesd_monitor d g s b esd_nfet_monitor ...`
|
||||
* inside their FET subckts, expecting HSPICE-style dispatch.
|
||||
*
|
||||
|
|
@ -605,7 +605,7 @@ get_model_bins(char *curr_line, float *fwmin, float *fwmax,
|
|||
parameter. HSPICE applies a subcircuit's `scale` parameter as the
|
||||
element scale factor for the MOSFETs inside it, multiplying their
|
||||
W/L before the model bins and simulates. Foundry MOS macro subckts
|
||||
rely on this (e.g. TSMC `nch_mac ... scale='scale_mos'`). Detected
|
||||
rely on this (e.g. foundry_a `nch_mac ... scale='scale_mos'`). Detected
|
||||
by a word-boundary "scale" immediately followed (after optional
|
||||
whitespace) by '=', so names like `l_scale=` / `noscale=` don't
|
||||
match. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* OSDI deferred-evaluation side table.
|
||||
*
|
||||
* HSPICE-style PDK model cards (Samsung 14LPU et al.) embed expressions
|
||||
* HSPICE-style PDK model cards (foundry_b 14LPU et al.) embed expressions
|
||||
* like
|
||||
* cgbn={((l<=1e-07)*(1e-012)+(l>1e-07)*(...))}
|
||||
* directly on the right-hand side of `.model` parameters. The expression
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
* `snap_*` capture the subckt-instance scope visible at register time.
|
||||
* HSPICE-style PDKs put `.model` cards INSIDE a `.subckt` body and let
|
||||
* the model's expressions reference subckt-scope `.params` (e.g.
|
||||
* Samsung 14LPU's `vsat1=...*(1+vsat_nfet/...)*velsat_mult` where
|
||||
* foundry_b 14LPU's `vsat1=...*(1+vsat_nfet/...)*velsat_mult` where
|
||||
* `vsat_nfet`/`velsat_mult`/`xl_nfet` are passed to the subckt via
|
||||
* its `params:` list). By register time the subckt scope has been
|
||||
* resolved per-instance (subckt expansion produced model names like
|
||||
|
|
@ -90,7 +90,7 @@ void osdi_defer_clear(void);
|
|||
* INPgetModBin once the model line's lmin/lmax tokens have been
|
||||
* parsed, so that OSDIsetup's pre-eval pass can pick a default
|
||||
* L within the bin's range (midpoint). Without this, default
|
||||
* L=30nm makes Samsung-PDK expressions like
|
||||
* L=30nm makes foundry_b-PDK expressions like
|
||||
* `vsat1='(l==14n)*X + (l==16n)*Y'` evaluate to 0 and BSIM-CMG
|
||||
* rejects "vsat1 = 0". Matched against the runtime model name
|
||||
* (after subckt-path prefix is stripped). */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -160,6 +160,71 @@ NIiter(CKTcircuit *ckt, int maxIter)
|
|||
return (E_ITERLIM);
|
||||
}
|
||||
|
||||
/* Axis 4 — row-relative KCL residual convergence check.
|
||||
* f = G*x - b is computed here, in the load->factor window
|
||||
* where the matrix still holds the device Jacobian, and each
|
||||
* row is compared against its own current scale
|
||||
* s_n = sum_j |G_nj*x_j| + |b_n| (plus the gmin-stepping
|
||||
* diagonal term for rows that receive it, so continuation
|
||||
* systems are measured against what is actually solved):
|
||||
*
|
||||
* |f_n| <= 10*reltol * s_n + abstol
|
||||
*
|
||||
* The row-relative form is what makes this enforceable where
|
||||
* the earlier absolute-norm attempt was not: lenient SPICE3
|
||||
* |dx|-only accepts that real PDK operating points depend on
|
||||
* carry roundoff-scale RELATIVE residuals and pass untouched,
|
||||
* while a false solution (e.g. a multi-million-fin OSDI
|
||||
* device whose hundreds-of-siemens row satisfies |dx|<vntol
|
||||
* long before KCL holds — dynamic gmin stepping was observed
|
||||
* "completing" 1.6 V off the rail at high temperature) shows
|
||||
* an O(1) relative residual and is sent back for further
|
||||
* Newton iterations instead of being accepted. Threshold
|
||||
* 10*reltol: KCL must close to within an order of magnitude
|
||||
* of the voltage-convergence precision — dimensionless, no
|
||||
* voltage-range or technology assumptions. NIconvTest gates
|
||||
* on CKTresidConverged (niconv.c). Sparse-matrix path only;
|
||||
* `.option noresidcheck` opts out. */
|
||||
ckt->CKTresidConverged = 1;
|
||||
#ifdef KLU
|
||||
if (!ckt->CKTresidCheckDisabled && !ckt->CKTmatrix->CKTkluMODE)
|
||||
#else
|
||||
if (!ckt->CKTresidCheckDisabled)
|
||||
#endif
|
||||
{
|
||||
int rn, rsize = SMPmatSize(ckt->CKTmatrix);
|
||||
double *rf = TMALLOC(double, (size_t) rsize + 1);
|
||||
double *rs = TMALLOC(double, (size_t) rsize + 1);
|
||||
double *rg = TMALLOC(double, (size_t) rsize + 1);
|
||||
double rtol = 10.0 * ckt->CKTreltol;
|
||||
double rvtol = 10.0 * ckt->CKTvoltTol;
|
||||
char *rskip = ckt->CKTmatrix->gmin_skip;
|
||||
SMPmultiplyAbs(ckt->CKTmatrix, rf, rs, rg, ckt->CKTrhsOld);
|
||||
for (rn = 1; rn <= rsize; rn++) {
|
||||
double gterm = (rskip && rskip[rn]) ? 0.0 :
|
||||
ckt->CKTdiagGmin * ckt->CKTrhsOld[rn];
|
||||
double fr = rf[rn] + gterm - ckt->CKTrhs[rn];
|
||||
/* Tolerance = what a solution within per-unknown
|
||||
* tolerance could produce on this row: the relative
|
||||
* part scales with the row's own current magnitude,
|
||||
* the row-norm part (|G|_1 * vntol) is the attainable
|
||||
* precision of a stiff row — an LU solve cannot push
|
||||
* a row residual below rownorm * solution-roundoff,
|
||||
* and demanding it burns iterations at breakpoint
|
||||
* corners where row values pass through zero. */
|
||||
double tol_ = rtol * (rs[rn] + fabs(gterm) +
|
||||
fabs(ckt->CKTrhs[rn]))
|
||||
+ rvtol * rg[rn] + ckt->CKTabstol;
|
||||
if (fabs(fr) > tol_) {
|
||||
ckt->CKTresidConverged = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FREE(rf);
|
||||
FREE(rs);
|
||||
FREE(rg);
|
||||
}
|
||||
|
||||
/* printf("after loading, before solving\n"); */
|
||||
/* CKTdump(ckt); */
|
||||
|
||||
|
|
@ -322,18 +387,9 @@ NIiter(CKTcircuit *ckt, int maxIter)
|
|||
memcpy(OldCKTstate0, ckt->CKTstate0,
|
||||
(size_t) ckt->CKTnumStates * sizeof(double));
|
||||
|
||||
/* Axis 4 placeholder — the |f|-magnitude (residual-norm) half
|
||||
* of dual-norm convergence stays disabled. A correct true-KCL
|
||||
* residual check (f = G*x - b via SMPmultiply in the pre-factor
|
||||
* window) was implemented and verified CORRECT, but enforcing it
|
||||
* by default over-rejects real PDK operating points that rely on
|
||||
* ngspice's lenient SPICE3 |dx|-only convergence (TSMC22 OP
|
||||
* diverged, Samsung slowed badly) while a clean circuit (0.9V
|
||||
* inverter) was byte-identical. So treat residual as always-
|
||||
* passed; NIconvTest gates on |dx| only. The CKTresidConverged
|
||||
* / CKTresidCheckDisabled fields + niconv.c gate + `.option
|
||||
* noresidcheck` plumbing remain for a future opt-in form. */
|
||||
ckt->CKTresidConverged = 1;
|
||||
/* Axis 4: CKTresidConverged was computed in the load->factor
|
||||
* window above (row-relative KCL residual); by this point the
|
||||
* matrix is factored and no longer holds the Jacobian. */
|
||||
|
||||
startTime = SPfrontEnd->IFseconds();
|
||||
SMPsolve(ckt->CKTmatrix, ckt->CKTrhs, ckt->CKTrhsSpare);
|
||||
|
|
@ -355,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-
|
||||
|
|
@ -363,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).
|
||||
*
|
||||
|
|
@ -370,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
|
||||
|
|
@ -403,7 +470,7 @@ NIiter(CKTcircuit *ckt, int maxIter)
|
|||
* Stagnation = current max|Δv| not dropping by at
|
||||
* least 30% from prev iteration. Unconditional
|
||||
* halving past iter 3 was preventing convergence
|
||||
* on the TSMC22 ULP driver_lv_2v5_tb VSN node: the
|
||||
* on the foundry_a ULP driver_lv_2v5_tb VSN node: the
|
||||
* inductor-coupled supply (L1 = 2.7 nH) naturally
|
||||
* needs ~100 mV swings to track each switching
|
||||
* transition, but dv_max would collapse to 8 mV by
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ static int is_ident_cont(int c) {
|
|||
*
|
||||
* Reserved set:
|
||||
* l, w, nf, m, xnf — standard HSPICE instance params
|
||||
* l_calc — Samsung/foundry "computed length" (= l + p_la
|
||||
* l_calc — foundry_b/foundry "computed length" (= l + p_la
|
||||
* in the foundry subckt; defaults to l alone) */
|
||||
static bool expr_refs_instance_geom(const char *expr) {
|
||||
static const char *const RES[] = {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -338,12 +343,12 @@ int OSDIsetup(SMPmatrix *matrix, GENmodel *inModel, CKTcircuit *ckt,
|
|||
* ranges and would otherwise reject the `0` placeholders that
|
||||
* osdi_defer_preprocess_line writes onto deferred-param slots.
|
||||
*
|
||||
* Per-model default L: BSIM-CMG/Samsung 14LPU expressions look like
|
||||
* Per-model default L: BSIM-CMG/foundry_b 14LPU expressions look like
|
||||
* vsat1 = '((l==0.014e-6)*X + (l==0.016e-6)*Y) * ...'
|
||||
* For these to evaluate to a NON-ZERO valid value at pre-eval time
|
||||
* (so setup_model accepts), the chosen L must satisfy at least one
|
||||
* of the `(l==...)` checks. Read the model's lmin/lmax (already
|
||||
* populated by the .model parse) and pick the midpoint. Samsung's
|
||||
* populated by the .model parse) and pick the midpoint. foundry_b's
|
||||
* 14LPU nfet.0 has lmin=10nm, lmax=18nm → midpoint=14nm, exactly
|
||||
* the L the expression looks for. For models without lmin/lmax,
|
||||
* fall back to 30nm (the original constant default). */
|
||||
|
|
@ -528,7 +533,7 @@ extern int OSDItemp(GENmodel *inModel, CKTcircuit *ckt) {
|
|||
* setup_instance so the model sees the actual computed values.
|
||||
* Skipped entirely (no map lookup, no syscalls) when this
|
||||
* model has no deferred entries — the common case for non-
|
||||
* HSPICE-style OSDI PDKs like TSMC22 BSIM-BULK. */
|
||||
* HSPICE-style OSDI PDKs like foundry_a BSIM-BULK. */
|
||||
if (has_deferred) {
|
||||
defer_apply_ctx ctx = {
|
||||
.descr = descr,
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ int OSDItrunc(GENmodel *in_model, CKTcircuit *ckt, double *timestep) {
|
|||
* gradually.
|
||||
*
|
||||
* Was 2.0× originally, tightened to 1.5× for pinb/net_7 oscillation
|
||||
* (commit 143a0805f) and now to 1.2× for TSMC22 ULP driver_lv_2v5_tb
|
||||
* (commit 143a0805f) 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
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"},
|
||||
|
|
|
|||
|
|
@ -695,7 +695,7 @@ resume:
|
|||
* non-uniform x-spacings produces a nonsensical slope at
|
||||
* the extrapolation end of the fit window, and Newton then
|
||||
* has to undo a 0.5-1 V starting offset on the affected
|
||||
* nodes within itl4 iterations. Observed on TSMC22 ULP
|
||||
* nodes within itl4 iterations. Observed on foundry_a ULP
|
||||
* driver_lv_2v5_tb: VSN (an L1-coupled supply driving
|
||||
* 500-finger BSIM-BULK drivers) catches a ~-0.7 V predictor
|
||||
* over-shoot and Newton can't undo it inside Stage A's
|
||||
|
|
@ -762,6 +762,28 @@ resume:
|
|||
/* If no convergence in Central solver step */
|
||||
if(converged != 0) {
|
||||
|
||||
/* A nonconverged attempt returns with CKTstate0 still holding
|
||||
* the last failed Newton iterate's device states (limiter
|
||||
* voltage history, charge states, OSDI LimitState slots) —
|
||||
* NIiter does not restore them. The retry then evaluates
|
||||
* devices against that garbage reference, lands further from
|
||||
* the solution, and fails worse; the contamination compounds
|
||||
* geometrically across retries at a single timepoint
|
||||
* (observed: v6#branch −32 A → −58 → −310 → 3e10 A over ~25
|
||||
* 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.
|
||||
*
|
||||
* 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));
|
||||
|
||||
#ifndef SHARED_MODULE
|
||||
ckt->CKTtime = ckt->CKTtime -ckt->CKTdelta;
|
||||
ckt->CKTstat->STATrejected ++;
|
||||
|
|
|
|||
|
|
@ -672,7 +672,7 @@ DCtrCurv(CKTcircuit *ckt, int restart)
|
|||
* On the LAST accepted iteration (the one whose NEXT
|
||||
* would trigger the stop check), snap cur_val to stop
|
||||
* exactly so the saved row's X-value is bit-exact.
|
||||
* GF55 bcd55 isoednfet's `.measure when v(n2)=3.3`
|
||||
* foundry_c bcd55 isoednfet's `.measure when v(n2)=3.3`
|
||||
* relies on this. */
|
||||
job->TRCVstepCount[i]++;
|
||||
double cur_val = job->TRCVvStart[i] +
|
||||
|
|
|
|||
|
|
@ -651,7 +651,7 @@ int HSMHV2load(
|
|||
|
||||
/* NaN-recovery (sanity damping).
|
||||
*
|
||||
* Symptom: on big PDKs (Samsung 14LPU 3.3 V LDMOS as `ld3nfet`)
|
||||
* Symptom: on big PDKs (foundry_b 14LPU 3.3 V LDMOS as `ld3nfet`)
|
||||
* the initial DC operating-point Newton iteration occasionally
|
||||
* lands on bias values that make the HiSIM_HV physics produce
|
||||
* NaN currents/conductances. Those NaN entries are written
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ INPdevParse(char **line, CKTcircuit *ckt, int dev, GENinstance *fast,
|
|||
goto quit;
|
||||
}
|
||||
/* OSDI models may receive extra instance parameters from PDK
|
||||
* subcircuits (e.g. 'total' from TSMC nch_mac) that the model
|
||||
* subcircuits (e.g. 'total' from foundry_a nch_mac) that the model
|
||||
* does not define. Skip the parameter and its value rather than
|
||||
* aborting; this matches HSPICE/Spectre behaviour. */
|
||||
if (device->registry_entry != NULL) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ INPgetModBin(CKTcircuit *ckt, char *name, INPmodel **model, INPtables *tab, char
|
|||
|
||||
*model = NULL;
|
||||
|
||||
/* Read L (required) and W (optional). FinFET PDKs (Samsung 14LPU
|
||||
/* Read L (required) and W (optional). FinFET PDKs (foundry_b 14LPU
|
||||
* et al.) have no W on the instance line — only `l=` and
|
||||
* `nfin=` — and their `.model` cards bin on `lmin/lmax/nfinmin/
|
||||
* nfinmax`, not on W. Previously this function bailed out as
|
||||
|
|
@ -348,8 +348,8 @@ INPgetModBin(CKTcircuit *ckt, char *name, INPmodel **model, INPtables *tab, char
|
|||
/* OSDI bin selection: compare per-finger W (w / nf, already applied
|
||||
* above) against the bin's wmin/wmax. Do NOT divide by m — `m` is a
|
||||
* parallel-instance multiplier (the whole device is replicated m
|
||||
* times), so the per-finger geometry is unchanged by it. TSMC and
|
||||
* GlobalFoundries BSIM-BULK decks author their wmin/wmax tables
|
||||
* times), so the per-finger geometry is unchanged by it. foundry_a and
|
||||
* foundry_c BSIM-BULK decks author their wmin/wmax tables
|
||||
* against the HSPICE/Spectre convention of per-finger W only, so an
|
||||
* additional /m here would push the effective W below every bin's
|
||||
* lower bound for any moderately-large multi-instance device. */
|
||||
|
|
@ -399,9 +399,9 @@ INPgetModBin(CKTcircuit *ckt, char *name, INPmodel **model, INPtables *tab, char
|
|||
}
|
||||
|
||||
/* Compare the per-finger W (already w/nf above) and L against
|
||||
* the bin's wmin/wmax and lmin/lmax. Foundry .lib files (TSMC,
|
||||
* GlobalFoundries, Samsung) author bin boundaries in POST-SHRINK
|
||||
* (EFFECTIVE) dimensions: e.g. TSMC 22nm ULP nch.1 wmax=2.5651µm
|
||||
* the bin's wmin/wmax and lmin/lmax. Foundry .lib files (foundry_a,
|
||||
* foundry_c, foundry_b) author bin boundaries in POST-SHRINK
|
||||
* (EFFECTIVE) dimensions: e.g. foundry_a 22nm ULP nch.1 wmax=2.5651µm
|
||||
* = drawn 3µm × shrink 0.855. The shrink is applied upstream in
|
||||
* subckt expansion (the subcircuit's `scale` parameter multiplies
|
||||
* the device W/L) or by the model's own dimension expressions, so
|
||||
|
|
@ -425,7 +425,7 @@ INPgetModBin(CKTcircuit *ckt, char *name, INPmodel **model, INPtables *tab, char
|
|||
*model = modtmp;
|
||||
/* Stash the bin's (lmin, lmax) for OSDIsetup's deferred-eval
|
||||
* pre-pass, which needs a representative L within the bin's
|
||||
* range to make Samsung-PDK expressions like
|
||||
* range to make foundry_b-PDK expressions like
|
||||
* `(l==14n)*X + (l==16n)*Y` evaluate to non-zero. */
|
||||
if (is_osdi)
|
||||
osdi_defer_record_bin_range(modtmp->INPmodName, lmin, lmax);
|
||||
|
|
|
|||
Loading…
Reference in New Issue