2515 lines
124 KiB
Plaintext
2515 lines
124 KiB
Plaintext
|
|
================================================================================
|
|||
|
|
NGSPICE — JUSTIFICATION OF CHANGES TO EXISTING (UPSTREAM) CODE
|
|||
|
|
Derived from ng_diff.txt · 23 existing files modified
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
PURPOSE
|
|||
|
|
This document lists every change made to source that ALREADY EXISTED in the
|
|||
|
|
reference tree (/home/jfisher/Downloads/ngspice_2026_05_27), each with the
|
|||
|
|
rationale for why it was made. New files and new OSDI code added alongside
|
|||
|
|
existing code are NOT modifications of upstream logic and are inventoried in
|
|||
|
|
the Appendix instead.
|
|||
|
|
|
|||
|
|
SELECTION RULE
|
|||
|
|
A diff hunk is shown only if it removes or replaces at least one pre-existing
|
|||
|
|
line. Within a shown hunk, a standalone block of 20+ newly-added lines (new
|
|||
|
|
OSDI code that replaces nothing) is elided with a [...] marker; replacement
|
|||
|
|
additions that directly follow a removed line are kept in full. The complete
|
|||
|
|
unabridged diffs remain in ng_diff.txt.
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[1] src/frontend/com_measure2.c
|
|||
|
|
pre-existing lines changed: 2 (+24 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
In measure_extract_variables (the save-variable registration path called at
|
|||
|
|
deck load), the fallback that defaulted an omitted `.measure` analysis type to
|
|||
|
|
"TRAN" was replaced with deck-based inference (STEP 28). A new comment
|
|||
|
|
explains the HSPICE shorthand form `.measure <result> ...` where the analysis
|
|||
|
|
type is omitted and the second token is really the measurement function. It
|
|||
|
|
warns that blindly defaulting to TRAN silently broke .dc decks: the save list
|
|||
|
|
got tagged "tran", so the .dc plot matched none of those names and
|
|||
|
|
OUTpBeginPlot reported "Error: no data saved for D.C. Transfer curve
|
|||
|
|
analysis". The new code scans ci_deck (the main card list) for the first
|
|||
|
|
`.dc`/`.ac`/`.tran` directive to infer the type; the comment explicitly notes
|
|||
|
|
ci_commands must NOT be scanned because it only holds
|
|||
|
|
.print/.plot/.meas/.op/.tf/.four/.width/.sndprint/.sndparam, not analysis
|
|||
|
|
directives.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -331,8 +331,30 @@
|
|||
|
|
(strcasecmp(analysis, "TRAN") == 0)) {
|
|||
|
|
analysis = copy(analysis);
|
|||
|
|
} else {
|
|||
|
|
- /* sometimes operation is optional - for now just pick trans */
|
|||
|
|
- analysis = copy("TRAN");
|
|||
|
|
+ /* HSPICE-shorthand: `.measure <result> ...` with the
|
|||
|
|
+ * analysis type omitted. The second token (held here in
|
|||
|
|
+ * `analysis`) is actually the measurement function (find /
|
|||
|
|
+ * trig / when / avg / ...) — not what we just gettok'd.
|
|||
|
|
+ * Infer the analysis from whatever the deck declares.
|
|||
|
|
+ * Defaulting to TRAN (the old behaviour) silently broke
|
|||
|
|
+ * decks with a .dc analysis because the save list ended
|
|||
|
|
+ * up tagged "tran" and the .dc plot saw none of those
|
|||
|
|
+ * names — OUTpBeginPlot reported
|
|||
|
|
+ * Error: no data saved for D.C. Transfer curve analysis
|
|||
|
|
+ * Scan ci_deck (the main card list) — ci_commands holds
|
|||
|
|
+ * only .print/.plot/.meas/.op/.tf/.four/.width/.sndprint/
|
|||
|
|
+ * .sndparam, NOT analysis directives. */
|
|||
|
|
+ const char *inferred = "TRAN";
|
|||
|
|
+ struct card *c;
|
|||
|
|
+ for (c = ft_curckt ? ft_curckt->ci_deck : NULL;
|
|||
|
|
+ c; c = c->nextcard) {
|
|||
|
|
+ const char *w = c->line;
|
|||
|
|
+ if (!w || *w == '*') continue;
|
|||
|
|
+ if (ciprefix(".dc", w)) { inferred = "DC"; break; }
|
|||
|
|
+ if (ciprefix(".ac", w)) { inferred = "AC"; break; }
|
|||
|
|
+ if (ciprefix(".tran", w)) { inferred = "TRAN"; break; }
|
|||
|
|
+ }
|
|||
|
|
+ analysis = copy(inferred);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
do {
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[2] src/frontend/inp.c
|
|||
|
|
pre-existing lines changed: 7 (+37 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Three changes. (1) In two places, the `*ng_script` comfile detection (which
|
|||
|
|
marks user-supplied command files) now walks past leading blank/CR-LF cards
|
|||
|
|
before testing the first real line; a new comment explains a leading blank
|
|||
|
|
line otherwise defeated detection, referencing the identical logic in
|
|||
|
|
inpcom.c::inp_readall (STEP 14). (2) A new guard skips `*#.` lines silently:
|
|||
|
|
the added comment states that `*#.foo` lines are SPICE deck cards embedded for
|
|||
|
|
other tools' backward compatibility, not ngspice commands, so they are freed
|
|||
|
|
and skipped rather than being treated as the `pre_`/command dispatch below. It
|
|||
|
|
captures the `prefix("*#", s)` result in a `from_hash` bool so the new
|
|||
|
|
`.`-prefix test only fires for hash-comment lines. (3) A stray blank line was
|
|||
|
|
added near the agauss handling (cosmetic).
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -531,9 +531,20 @@
|
|||
|
|
if (fp || intfile) {
|
|||
|
|
deck = inp_readall(fp, dir_name, filename, comfile, intfile, &expr_w_temper);
|
|||
|
|
|
|||
|
|
- /* files starting with *ng_script are user supplied command files */
|
|||
|
|
- if (deck && ciprefix("*ng_script", deck->line))
|
|||
|
|
- comfile = TRUE;
|
|||
|
|
+ /* files starting with *ng_script are user supplied command files.
|
|||
|
|
+ * Walk past any leading blank cards (see same logic in
|
|||
|
|
+ * inpcom.c::inp_readall) so a leading blank line doesn't
|
|||
|
|
+ * defeat comfile detection. */
|
|||
|
|
+ if (deck) {
|
|||
|
|
+ struct card *probe = deck;
|
|||
|
|
+ while (probe && probe->line &&
|
|||
|
|
+ (probe->line[0] == '\0' ||
|
|||
|
|
+ probe->line[0] == '\n' ||
|
|||
|
|
+ (probe->line[0] == '\r' && probe->line[1] == '\n')))
|
|||
|
|
+ probe = probe->nextcard;
|
|||
|
|
+ if (probe && ciprefix("*ng_script", probe->line))
|
|||
|
|
+ comfile = TRUE;
|
|||
|
|
+ }
|
|||
|
|
/* save a copy of the deck for later reloading with 'mc_source' */
|
|||
|
|
if (deck && !comfile) {
|
|||
|
|
/* stored to new circuit ci_mcdeck in fcn */
|
|||
|
|
@@ -598,9 +609,18 @@
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- /* files starting with *ng_script are user supplied command files */
|
|||
|
|
- if (ciprefix("*ng_script", deck->line))
|
|||
|
|
- comfile = TRUE;
|
|||
|
|
+ /* files starting with *ng_script are user supplied command files.
|
|||
|
|
+ * Walk past any leading blank cards (see same logic above). */
|
|||
|
|
+ {
|
|||
|
|
+ struct card *probe = deck;
|
|||
|
|
+ while (probe && probe->line &&
|
|||
|
|
+ (probe->line[0] == '\0' ||
|
|||
|
|
+ probe->line[0] == '\n' ||
|
|||
|
|
+ (probe->line[0] == '\r' && probe->line[1] == '\n')))
|
|||
|
|
+ probe = probe->nextcard;
|
|||
|
|
+ if (probe && ciprefix("*ng_script", probe->line))
|
|||
|
|
+ comfile = TRUE;
|
|||
|
|
+ }
|
|||
|
|
|
|||
|
|
if (!comfile) {
|
|||
|
|
/* Extract the .option lines from the deck into 'options',
|
|||
|
|
@@ -780,9 +800,18 @@
|
|||
|
|
|
|||
|
|
/* Special control lines outside of .control section? */
|
|||
|
|
|
|||
|
|
- if (prefix("*#", s))
|
|||
|
|
+ bool from_hash = prefix("*#", s);
|
|||
|
|
+ if (from_hash)
|
|||
|
|
s = skip_ws(s + 2);
|
|||
|
|
|
|||
|
|
+ /* *#.foo lines are SPICE deck cards embedded for other tools'
|
|||
|
|
+ * backward compatibility, not ngspice commands. Skip silently. */
|
|||
|
|
+ if (from_hash && *s == '.') {
|
|||
|
|
+ ld->nextcard = dd->nextcard;
|
|||
|
|
+ line_free(dd, FALSE);
|
|||
|
|
+ continue;
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
if (ciprefix("pre_", s)) {
|
|||
|
|
/* Assemble all commands starting with pre_ after stripping
|
|||
|
|
* pre_, to be executed before circuit parsing.
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[3] src/frontend/inpcom.c
|
|||
|
|
pre-existing lines changed: 37 (+245 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Broad set of parser changes. (1) Added `#include "ngspice/hash.h"` and a new
|
|||
|
|
`NGHASHPTR fcn_hash` field to `struct function_env`, with
|
|||
|
|
find_function/add_function/new_function_env/delete_function_env updated to
|
|||
|
|
hash-lookup `.func` names in O(1) instead of a linear scan; comments explain
|
|||
|
|
foundry PDKs register hundreds-to-thousands of .funcs (BCD 14LPU ~1300)
|
|||
|
|
and find_function runs once per `(` per line, so the old scan was billions of
|
|||
|
|
strcmp (STEP 14). (2) inp_sort_params' two nested O(N^2) loops (duplicate
|
|||
|
|
detection and dependency scan) were rewritten around a single name->(i+1) hash
|
|||
|
|
with a new helper `identifier_char`; a long comment documents the encoding
|
|||
|
|
(index+1 so NULL means absent), the "latest wins" duplicate semantics, and the
|
|||
|
|
PDK scale (N~2700 per subckt, ~500 calls). (3) `*ng_script` comfile detection
|
|||
|
|
in inp_read and inp_readall now skips leading blank lines, with comments
|
|||
|
|
describing the doubled-parse symptom (two "Compatibility modes"/"Circuit:"
|
|||
|
|
headers, ~2x parse time) it prevents. (4) A new
|
|||
|
|
`inp_apply_subckt_scale(working)` call was added before inp_fix_for_numparam,
|
|||
|
|
with a comment on why HSPICE element scale must be wrapped before numparam
|
|||
|
|
preparation (STEP 31). (5) A new `.alter` handler silently skips the `.alter`
|
|||
|
|
directive and every line up to the next `.alter`/`.end` using two file-scope
|
|||
|
|
static bools, emitting a one-shot warning; the comment explains .alter re-runs
|
|||
|
|
with process-corner/param overrides (unimplemented) and why the whole block,
|
|||
|
|
not just the line, must be skipped (STEP 26). (6) The line lowercaser was made
|
|||
|
|
quote-aware via an `in_dq` flag so case-sensitive filenames inside
|
|||
|
|
double-quoted strings (e.g. HSPICE
|
|||
|
|
`table_param(str("./RF_COMPONENTS/foo.table"))`) survive lowercasing on Linux
|
|||
|
|
(STEP 14); a companion change in the keepquotes path preserves content inside
|
|||
|
|
`str("...")`. (7) In HS/HSA mode (`newcompat.hs`), `$` is now treated as an
|
|||
|
|
end-of-line comment regardless of the preceding character, matching foundry
|
|||
|
|
decks that write `...)'$ comment`; the original conservative space/comma/tab
|
|||
|
|
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).
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -1072,9 +1080,22 @@
|
|||
|
|
return cc;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- /* files starting with *ng_script are user supplied command files */
|
|||
|
|
- if (cc && ciprefix("*ng_script", cc->line))
|
|||
|
|
- comfile = TRUE;
|
|||
|
|
+ /* files starting with *ng_script are user supplied command files.
|
|||
|
|
+ * Walk past any leading blank cards so a `*ng_script` that follows
|
|||
|
|
+ * a blank top-of-file line is still recognised. Without this,
|
|||
|
|
+ * such a file gets treated as a spice deck and the inner
|
|||
|
|
+ * `.control { source ... }` triggers a second full inp_readall
|
|||
|
|
+ * pass — doubling parse time on big PDK decks. */
|
|||
|
|
+ {
|
|||
|
|
+ struct card *probe = cc;
|
|||
|
|
+ while (probe && probe->line &&
|
|||
|
|
+ (probe->line[0] == '\0' ||
|
|||
|
|
+ probe->line[0] == '\n' ||
|
|||
|
|
+ (probe->line[0] == '\r' && probe->line[1] == '\n')))
|
|||
|
|
+ probe = probe->nextcard;
|
|||
|
|
+ if (probe && ciprefix("*ng_script", probe->line))
|
|||
|
|
+ comfile = TRUE;
|
|||
|
|
+ }
|
|||
|
|
|
|||
|
|
/* The following processing of an input file is not required for command
|
|||
|
|
files like spinit or .spiceinit, so return command files here. */
|
|||
|
|
@@ -1406,11 +1433,28 @@
|
|||
|
|
/* OK -- now we have loaded the next line into 'buffer'. Process it.
|
|||
|
|
*/
|
|||
|
|
if (first) {
|
|||
|
|
- /* Files starting *ng_script are user supplied command files. */
|
|||
|
|
-
|
|||
|
|
- if (ciprefix("*ng_script", buffer))
|
|||
|
|
- comfile = TRUE;
|
|||
|
|
- first = FALSE;
|
|||
|
|
+ /* Files starting *ng_script are user supplied command files.
|
|||
|
|
+ *
|
|||
|
|
+ * Treat any leading blank lines as not-yet-first, so a
|
|||
|
|
+ * comfile that has a blank line above `*ng_script` is
|
|||
|
|
+ * still recognised. Without this, ngspice processes
|
|||
|
|
+ * such a file twice: once as the "main" deck (because
|
|||
|
|
+ * comfile stays FALSE, the !comfile branch runs full
|
|||
|
|
+ * parse + print_compat_mode + Circuit:), and again
|
|||
|
|
+ * when its `.control { source other.net }` triggers a
|
|||
|
|
+ * fresh inp_spsource for the inner netlist. Real
|
|||
|
|
+ * symptom: doubled "Compatibility modes selected" /
|
|||
|
|
+ * "Circuit:" headers and ~2x parse time. */
|
|||
|
|
+ bool buffer_blank = (strcmp(buffer, "\n") == 0) ||
|
|||
|
|
+ (strcmp(buffer, "\r\n") == 0) ||
|
|||
|
|
+ (buffer[0] == '\0');
|
|||
|
|
+ if (buffer_blank) {
|
|||
|
|
+ /* skip — re-check on the next line */
|
|||
|
|
+ } else {
|
|||
|
|
+ if (ciprefix("*ng_script", buffer))
|
|||
|
|
+ comfile = TRUE;
|
|||
|
|
+ first = FALSE;
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* If input line is blank, ignore it & continue looping. */
|
|||
|
|
@@ -1808,9 +1892,22 @@
|
|||
|
|
ciprefix("set sourcepath", buffer) ||
|
|||
|
|
ciprefix("strcmp", buffer) ||
|
|||
|
|
ciprefix("strstr", buffer))))) {
|
|||
|
|
- /* lower case for all other lines */
|
|||
|
|
- for (s = buffer; *s && (*s != '\n'); s++)
|
|||
|
|
- *s = tolower_c(*s);
|
|||
|
|
+ /* Lower case for all other lines, but preserve the
|
|||
|
|
+ * content INSIDE double-quoted strings. Foundry PDKs
|
|||
|
|
+ * embed case-sensitive filenames in .param/.subckt
|
|||
|
|
+ * lines via constructs like
|
|||
|
|
+ * `.param x = 'table_param(str("./RF_COMPONENTS/foo.table"), ...)'`.
|
|||
|
|
+ * Without quote preservation, the inner path gets
|
|||
|
|
+ * folded to lowercase and fails to open on Linux. */
|
|||
|
|
+ bool in_dq = false;
|
|||
|
|
+ for (s = buffer; *s && (*s != '\n'); s++) {
|
|||
|
|
+ if (*s == '"') {
|
|||
|
|
+ in_dq = !in_dq;
|
|||
|
|
+ continue;
|
|||
|
|
+ }
|
|||
|
|
+ if (!in_dq)
|
|||
|
|
+ *s = tolower_c(*s);
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
/* s points to end of buffer for all cases not treated so far
|
|||
|
|
@@ -3617,9 +3724,19 @@
|
|||
|
|
}
|
|||
|
|
/* outside of .control section, and not in PS mode */
|
|||
|
|
else if (!cs && (c == '$') && !newcompat.ps) {
|
|||
|
|
- /* The character before '&' has to be ',' or ' ' or tab.
|
|||
|
|
- A valid numerical expression directly before '$' is not yet
|
|||
|
|
- supported. */
|
|||
|
|
+ /* HSPICE treats '$' as an end-of-line comment regardless of
|
|||
|
|
+ * the preceding character — foundry decks
|
|||
|
|
+ * routinely write `...)'$ comment` or `...=10u$ ...`
|
|||
|
|
+ * with no separator. In ngbehavior=hs / hsa, accept that.
|
|||
|
|
+ *
|
|||
|
|
+ * Outside HS mode keep the original conservative rule (only
|
|||
|
|
+ * after space/comma/tab) so a literal `$` inside a token
|
|||
|
|
+ * (e.g. a parameter named `foo$bar`, environment vars, etc.)
|
|||
|
|
+ * isn't misread as a comment delimiter. */
|
|||
|
|
+ if (newcompat.hs) {
|
|||
|
|
+ d--;
|
|||
|
|
+ break;
|
|||
|
|
+ }
|
|||
|
|
if ((d - 2 >= s) &&
|
|||
|
|
((d[-2] == ' ') || (d[-2] == ',') || (d[-2] == '\t'))) {
|
|||
|
|
d--;
|
|||
|
|
@@ -4396,12 +4527,25 @@
|
|||
|
|
|
|||
|
|
static struct function *find_function(struct function_env *env, char *name)
|
|||
|
|
{
|
|||
|
|
- struct function *f;
|
|||
|
|
-
|
|||
|
|
- for (; env; env = env->up)
|
|||
|
|
- for (f = env->functions; f; f = f->next)
|
|||
|
|
- if (strcmp(f->name, name) == 0)
|
|||
|
|
+ /* Walk the env chain inner-to-outer. At each level, hash-lookup
|
|||
|
|
+ * the function name (O(1)). Total cost is O(env_chain_depth) per
|
|||
|
|
+ * call instead of O(total_funcs). See struct function_env comment
|
|||
|
|
+ * for why this matters. */
|
|||
|
|
+ for (; env; env = env->up) {
|
|||
|
|
+ if (env->fcn_hash) {
|
|||
|
|
+ struct function *f =
|
|||
|
|
+ (struct function *)nghash_find(env->fcn_hash, name);
|
|||
|
|
+ if (f)
|
|||
|
|
return f;
|
|||
|
|
+ } else {
|
|||
|
|
+ /* Fallback for envs created before the hash was added or
|
|||
|
|
+ * if hash init failed (out of memory). Same semantics as
|
|||
|
|
+ * the original linear scan. */
|
|||
|
|
+ for (struct function *f = env->functions; f; f = f->next)
|
|||
|
|
+ if (strcmp(f->name, name) == 0)
|
|||
|
|
+ return f;
|
|||
|
|
+ }
|
|||
|
|
+ }
|
|||
|
|
|
|||
|
|
return NULL;
|
|||
|
|
}
|
|||
|
|
@@ -5483,37 +5637,91 @@
|
|||
|
|
num_params++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- // look for duplicately defined parameters and mark earlier one to skip
|
|||
|
|
- // param list is ordered as defined in netlist
|
|||
|
|
+ /* Duplicate detection and dependency scanning via a single hash from
|
|||
|
|
+ * param_name -> (i+1). Replaces two nested-N^2 loops:
|
|||
|
|
+ * - duplicate-check: was O(N^2) strcmp pairs
|
|||
|
|
+ * - dependency-scan: was O(N^2 * L) search_plain_identifier calls,
|
|||
|
|
+ * 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 (BCD 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
|
|||
|
|
+ * return value from nghash_find unambiguously means "not present"
|
|||
|
|
+ * (param index 0 is a valid hit, encoded as the integer 1).
|
|||
|
|
+ *
|
|||
|
|
+ * For repeated names, the hash overwrites with the LATER index, so
|
|||
|
|
+ * the recorded index for any name is the latest occurrence. In the
|
|||
|
|
+ * duplicate-mark pass below, every entry whose own index differs
|
|||
|
|
+ * from the hash's recorded index gets skip=1, matching the original
|
|||
|
|
+ * semantics (earlier duplicates are skipped, the last one wins). */
|
|||
|
|
+ NGHASHPTR name_to_idx = nghash_init(num_params > 0 ? num_params : 1);
|
|||
|
|
+
|
|||
|
|
+ for (i = 0; i < num_params; i++)
|
|||
|
|
+ nghash_insert(name_to_idx, deps[i].param_name,
|
|||
|
|
+ (void *)(intptr_t)(i + 1));
|
|||
|
|
|
|||
|
|
skipped = 0;
|
|||
|
|
for (i = 0; i < num_params; i++) {
|
|||
|
|
- for (j = i + 1; j < num_params; j++)
|
|||
|
|
- if (strcmp(deps[i].param_name, deps[j].param_name) == 0)
|
|||
|
|
- break;
|
|||
|
|
- if (j < num_params) {
|
|||
|
|
+ int latest =
|
|||
|
|
+ (int)(intptr_t)nghash_find(name_to_idx, deps[i].param_name) - 1;
|
|||
|
|
+ if (latest != i) {
|
|||
|
|
deps[i].skip = 1;
|
|||
|
|
skipped++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- for (i = 0; i < num_params; i++)
|
|||
|
|
- if (!deps[i].skip) {
|
|||
|
|
- char *param = deps[i].param_name;
|
|||
|
|
- for (j = 0; j < num_params; j++)
|
|||
|
|
- if (j != i &&
|
|||
|
|
- search_plain_identifier(deps[j].param_str, param)) {
|
|||
|
|
- for (ind = 0; deps[j].depends_on[ind]; ind++)
|
|||
|
|
- ;
|
|||
|
|
- deps[j].depends_on[ind++] = param;
|
|||
|
|
- if (ind == DEPENDSON) {
|
|||
|
|
- fprintf(stderr, "Error in netlist: Too many parameter dependencies (> %d)\n", ind);
|
|||
|
|
+ /* Tokenize each non-skipped param's expression once; for each
|
|||
|
|
+ * identifier-shaped token, look up in name_to_idx. If the token
|
|||
|
|
+ * names another (non-skipped) param, record the dependency. Token
|
|||
|
|
+ * boundaries use the same identifier_char predicate as
|
|||
|
|
+ * search_plain_identifier, so behavior matches the old code. */
|
|||
|
|
+ for (j = 0; j < num_params; j++) {
|
|||
|
|
+ if (deps[j].skip) continue;
|
|||
|
|
+ char *expr = deps[j].param_str;
|
|||
|
|
+ if (!expr) continue;
|
|||
|
|
+
|
|||
|
|
+ char *p = expr;
|
|||
|
|
+ while (*p) {
|
|||
|
|
+ while (*p && !identifier_char(*p)) p++;
|
|||
|
|
+ if (!*p) break;
|
|||
|
|
+ char *tok_start = p;
|
|||
|
|
+ while (*p && identifier_char(*p)) p++;
|
|||
|
|
+
|
|||
|
|
+ char saved = *p;
|
|||
|
|
+ *p = '\0';
|
|||
|
|
+ int dep_idx =
|
|||
|
|
+ (int)(intptr_t)nghash_find(name_to_idx, tok_start) - 1;
|
|||
|
|
+ *p = saved;
|
|||
|
|
+
|
|||
|
|
+ if (dep_idx >= 0 && dep_idx != j && !deps[dep_idx].skip) {
|
|||
|
|
+ ...[16 more replacement lines — full listing in ng_diff.txt]...
|
|||
|
|
fprintf(stderr, " Please check your netlist.\n");
|
|||
|
|
controlled_exit(EXIT_BAD);
|
|||
|
|
}
|
|||
|
|
- deps[j].depends_on[ind] = NULL;
|
|||
|
|
+ deps[j].depends_on[ind + 1] = NULL;
|
|||
|
|
}
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ nghash_free(name_to_idx, NULL, NULL);
|
|||
|
|
|
|||
|
|
max_level = 0;
|
|||
|
|
for (i = 0; i < num_params; i++) {
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[4] src/frontend/measure.c
|
|||
|
|
pre-existing lines changed: 16 (+85 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
do_measure and get_measure2 gained HSPICE-shorthand handling for `.measure
|
|||
|
|
<result> <fn> ...` with no explicit analysis type (STEP 28). In do_measure's
|
|||
|
|
two parse passes, when the first token isn't a recognized analysis type,
|
|||
|
|
instead of hard-erroring ("unrecognized analysis type"), the code infers the
|
|||
|
|
type by scanning ci_deck for the first `.dc`/`.ac`/`.tran`, then re-classifies
|
|||
|
|
the three already-gettok'd tokens (an_type/resname/meastype are actually
|
|||
|
|
resname/meastype/first-arg) and splices the old meastype back into `line` with
|
|||
|
|
tprintf so the rest of the parser realigns. Comments explain the
|
|||
|
|
token-shift/splice logic and note ci_deck must be scanned (not ci_commands).
|
|||
|
|
get_measure2 adds a `shorthand_first` bool and peeks the first token; if it
|
|||
|
|
isn't DC/AC/TRAN/SP it synthesizes a leading inferred-analysis wordlist entry
|
|||
|
|
so the positional parser (mAnalysis[0], mName[1], mFunction[2]) lines up.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -301,16 +301,38 @@
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
+ /* HSPICE-shorthand: `.measure <result> <fn> ...` (no
|
|||
|
|
+ * explicit analysis type). When the first token doesn't
|
|||
|
|
+ * match a recognized analysis type, treat it as the
|
|||
|
|
+ * result name and infer the analysis from the deck.
|
|||
|
|
+ * Scan ci_deck — ci_commands holds only .print/.meas/etc.,
|
|||
|
|
+ * NOT .dc/.tran/.ac. */
|
|||
|
|
if (chkAnalysisType(an_type) != TRUE) {
|
|||
|
|
- if (!chk_only) {
|
|||
|
|
- fprintf(cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->linenum);
|
|||
|
|
- fprintf(cp_err, " %s\n", meas_card->line);
|
|||
|
|
+ const char *inferred = "tran";
|
|||
|
|
+ struct card *c;
|
|||
|
|
+ for (c = ft_curckt ? ft_curckt->ci_deck : NULL;
|
|||
|
|
+ c; c = c->nextcard) {
|
|||
|
|
+ const char *w = c->line;
|
|||
|
|
+ if (!w || *w == '*') continue;
|
|||
|
|
+ if (ciprefix(".dc", w)) { inferred = "dc"; break; }
|
|||
|
|
+ if (ciprefix(".ac", w)) { inferred = "ac"; break; }
|
|||
|
|
+ if (ciprefix(".tran", w)) { inferred = "tran"; break; }
|
|||
|
|
}
|
|||
|
|
-
|
|||
|
|
- txfree(an_type);
|
|||
|
|
- txfree(resname);
|
|||
|
|
- txfree(meastype);
|
|||
|
|
- continue;
|
|||
|
|
+ char *new_resname = an_type;
|
|||
|
|
+ char *new_meastype = resname;
|
|||
|
|
+ char *new_extra = meastype;
|
|||
|
|
+ an_type = copy(inferred);
|
|||
|
|
+ resname = new_resname;
|
|||
|
|
+ meastype = new_meastype;
|
|||
|
|
+ /* meastype was the third gettok; shift the rest of
|
|||
|
|
+ * the line back so the remaining parser sees the new
|
|||
|
|
+ * meastype's argument as the next token. The line
|
|||
|
|
+ * already advanced past `meastype` (now extra), so
|
|||
|
|
+ * we need to splice `extra` back into the front of
|
|||
|
|
+ * `line` (concatenate with a space). */
|
|||
|
|
+ char *new_line_buf = tprintf("%s %s", new_extra, line);
|
|||
|
|
+ line = new_line_buf;
|
|||
|
|
+ tfree(new_extra);
|
|||
|
|
}
|
|||
|
|
/* print header before evaluating first .meas line */
|
|||
|
|
else if (first_time) {
|
|||
|
|
@@ -431,16 +453,30 @@
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
+ /* HSPICE-shorthand: same path as the first-pass fix at
|
|||
|
|
+ * line 304. Treat the original first token as resname,
|
|||
|
|
+ * shift everything back one slot, and splice the old
|
|||
|
|
+ * meastype back into `line` for the rest of the parser. */
|
|||
|
|
if (chkAnalysisType(an_type) != TRUE) {
|
|||
|
|
- if (!chk_only) {
|
|||
|
|
- fprintf(cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->linenum);
|
|||
|
|
- fprintf(cp_err, " %s\n", meas_card->line);
|
|||
|
|
+ const char *inferred = "tran";
|
|||
|
|
+ struct card *c2;
|
|||
|
|
+ for (c2 = ft_curckt ? ft_curckt->ci_deck : NULL;
|
|||
|
|
+ c2; c2 = c2->nextcard) {
|
|||
|
|
+ const char *w = c2->line;
|
|||
|
|
+ if (!w || *w == '*') continue;
|
|||
|
|
+ if (ciprefix(".dc", w)) { inferred = "dc"; break; }
|
|||
|
|
+ if (ciprefix(".ac", w)) { inferred = "ac"; break; }
|
|||
|
|
+ if (ciprefix(".tran", w)) { inferred = "tran"; break; }
|
|||
|
|
}
|
|||
|
|
-
|
|||
|
|
- txfree(an_type);
|
|||
|
|
- txfree(resname);
|
|||
|
|
- txfree(meastype);
|
|||
|
|
- continue;
|
|||
|
|
+ char *new_resname2 = an_type;
|
|||
|
|
+ char *new_meastype2 = resname;
|
|||
|
|
+ char *new_extra2 = meastype;
|
|||
|
|
+ an_type = copy(inferred);
|
|||
|
|
+ resname = new_resname2;
|
|||
|
|
+ meastype = new_meastype2;
|
|||
|
|
+ char *new_line_buf2 = tprintf("%s %s", new_extra2, line);
|
|||
|
|
+ line = new_line_buf2;
|
|||
|
|
+ tfree(new_extra2);
|
|||
|
|
}
|
|||
|
|
if (strcmp(an_name, an_type) != 0) {
|
|||
|
|
txfree(an_type);
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[5] src/frontend/numparam/Makefile.am
|
|||
|
|
pre-existing lines changed: 1 (+3 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Added the new source/header pair `table_param.c` and `table_param.h` to the
|
|||
|
|
numparam build sources (STEP 15). These implement HSPICE's `table_param()`
|
|||
|
|
multi-dimensional external-table lookup function used heavily by foundry PDKs
|
|||
|
|
for self-heating/thermal-resistance lookups.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -8,9 +8,11 @@
|
|||
|
|
spicenum.c \
|
|||
|
|
xpressn.c \
|
|||
|
|
mystring.c \
|
|||
|
|
+ table_param.c \
|
|||
|
|
general.h \
|
|||
|
|
numpaif.h \
|
|||
|
|
- numparam.h
|
|||
|
|
+ numparam.h \
|
|||
|
|
+ table_param.h
|
|||
|
|
|
|||
|
|
AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/src/include
|
|||
|
|
AM_CFLAGS = $(STATIC)
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[6] src/frontend/numparam/xpressn.c
|
|||
|
|
pre-existing lines changed: 11 (+278 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Several changes. (1) Added `#include "table_param.h"`, added `table_param` to
|
|||
|
|
the fmathS function-name list, and added an XFU_TABLE_PARAM enum entry plus a
|
|||
|
|
large variadic dispatch branch in formula() that manually splits
|
|||
|
|
comma-separated args (up to 64), unwraps the `str(...)` filename wrapper and
|
|||
|
|
quotes, recursively evaluates the numeric args, derives a directory hint from
|
|||
|
|
dico->cardsource, and calls table_param_lookup() (STEP 15). (2) message() now
|
|||
|
|
returns immediately when dico->suppress_errors is set, and
|
|||
|
|
dico->suppress_errors is initialized false in the init path. (3) The `==` and
|
|||
|
|
`<>` operators in operate() were changed from strict IEEE equality to a hybrid
|
|||
|
|
relative+absolute tolerance (equal if difference <= 1e-9*max(|x|,|y|) or below
|
|||
|
|
a 1e-18 floor); the comment explains foundry-PDK expressions like
|
|||
|
|
`(l==0.014e-6)` fail with strict `==` because `l` arrives 1 ULP off (e.g. bin
|
|||
|
|
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
|
|||
|
|
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"
|
|||
|
|
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
|
|||
|
|
the OSDI deferred-eval path (STEP 10).
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -92,14 +93,15 @@
|
|||
|
|
"sqr sqrt sin cos exp ln arctan abs pow pwr max min int log log10 sinh cosh"
|
|||
|
|
" tanh ternary_fcn agauss sgn gauss unif aunif limit ceil floor"
|
|||
|
|
" asin acos atan asinh acosh atanh tan nint"
|
|||
|
|
- " vec var";
|
|||
|
|
+ " vec var table_param";
|
|||
|
|
|
|||
|
|
|
|||
|
|
enum {
|
|||
|
|
XFU_SQR = 1, XFU_SQRT, XFU_SIN, XFU_COS, XFU_EXP, XFU_LN, XFU_ARCTAN, XFU_ABS, XFU_POW, XFU_PWR, XFU_MAX, XFU_MIN, XFU_INT, XFU_LOG, XFU_LOG10, XFU_SINH, XFU_COSH,
|
|||
|
|
XFU_TANH, XFU_TERNARY_FCN, XFU_AGAUSS, XFU_SGN, XFU_GAUSS, XFU_UNIF, XFU_AUNIF, XFU_LIMIT, XFU_CEIL, XFU_FLOOR,
|
|||
|
|
XFU_ASIN, XFU_ACOS, XFU_ATAN, XFU_ASINH, XFU_ACOSH, XFU_ATANH, XFU_TAN, XFU_NINT,
|
|||
|
|
- XFU_VEC, XFU_VAR // String arguments.
|
|||
|
|
+ XFU_VEC, XFU_VAR, // String arguments.
|
|||
|
|
+ XFU_TABLE_PARAM // Variadic HSPICE table_param(file, n_int, ints..., n_real, reals..., out_col).
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
@@ -817,16 +823,34 @@
|
|||
|
|
x = ((x != 0.0) || (y != 0.0)) ? 1.0 : 0.0;
|
|||
|
|
break;
|
|||
|
|
case '=':
|
|||
|
|
- if (x == y)
|
|||
|
|
- x = u;
|
|||
|
|
- else
|
|||
|
|
- x = z;
|
|||
|
|
+ /* HSPICE-compatible tolerant equality. Strict IEEE `==`
|
|||
|
|
+ * breaks foundry-PDK expressions like `(l==0.014e-6)` where
|
|||
|
|
+ * `l` arrives as `14*1e-9 = 1.4000000000000001e-08` (1 ULP
|
|||
|
|
+ * off from `0.014e-6 = 1.4e-08`), or as the bin midpoint
|
|||
|
|
+ * `0.5*(1e-8+1.8e-8) = 1.3999999999999999e-08`. Use a
|
|||
|
|
+ * hybrid relative+absolute tolerance: differences smaller
|
|||
|
|
+ * than `1e-9 * max(|x|,|y|)` or below an absolute floor of
|
|||
|
|
+ * `1e-18` count as equal. Integer comparisons (mexp==2,
|
|||
|
|
+ * etc.) remain unambiguous — 1 vs 2 differ by 100% relative. */
|
|||
|
|
+ {
|
|||
|
|
+ double dxy = fabs(x - y);
|
|||
|
|
+ double mxy = fabs(x) > fabs(y) ? fabs(x) : fabs(y);
|
|||
|
|
+ if (dxy <= mxy * 1e-9 || dxy < 1e-18)
|
|||
|
|
+ x = u;
|
|||
|
|
+ else
|
|||
|
|
+ x = z;
|
|||
|
|
+ }
|
|||
|
|
break;
|
|||
|
|
case '#': /* <> */
|
|||
|
|
- if (x != y)
|
|||
|
|
- x = u;
|
|||
|
|
- else
|
|||
|
|
- x = z;
|
|||
|
|
+ /* Symmetric with `==` above. */
|
|||
|
|
+ {
|
|||
|
|
+ double dxy = fabs(x - y);
|
|||
|
|
+ double mxy = fabs(x) > fabs(y) ? fabs(x) : fabs(y);
|
|||
|
|
+ if (dxy <= mxy * 1e-9 || dxy < 1e-18)
|
|||
|
|
+ x = z;
|
|||
|
|
+ else
|
|||
|
|
+ x = u;
|
|||
|
|
+ }
|
|||
|
|
break;
|
|||
|
|
case '>':
|
|||
|
|
if (x > y)
|
|||
|
|
@@ -1029,7 +1206,27 @@
|
|||
|
|
const char *s_next = fetchid(s, s_end);
|
|||
|
|
fu = keyword(fmathS, s, s_next); /* numeric function? */
|
|||
|
|
if (fu > 0) {
|
|||
|
|
- state = S_init; /* S_init means: ignore for the moment */
|
|||
|
|
+ /* The identifier matched a function keyword (e.g. `var`,
|
|||
|
|
+ * `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
|
|||
|
|
+ * diode_rr.inc) use `var` as a subckt parameter, and
|
|||
|
|
+ * shadowing it with the built-in function broke
|
|||
|
|
+ * `vrb='var'` and similar chains. */
|
|||
|
|
+ const char *peek = s_next;
|
|||
|
|
+ while (peek < s_end && (*peek == ' ' || *peek == '\t'))
|
|||
|
|
+ peek++;
|
|||
|
|
+ if (peek >= s_end || *peek != '(') {
|
|||
|
|
+ /* Not a function call — treat as identifier. */
|
|||
|
|
+ ds_clear(&tstr);
|
|||
|
|
+ pscopy(&tstr, s, s_next);
|
|||
|
|
+ u = fetchnumentry(dico, ds_get_buf(&tstr), &error);
|
|||
|
|
+ state = S_atom;
|
|||
|
|
+ fu = 0;
|
|||
|
|
+ } else {
|
|||
|
|
+ state = S_init; /* wait for the `(` to be consumed */
|
|||
|
|
+ }
|
|||
|
|
} else {
|
|||
|
|
ds_clear(&tstr);
|
|||
|
|
pscopy(&tstr, s, s_next);
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[7] src/frontend/subckt.c
|
|||
|
|
pre-existing lines changed: 84 (+344 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Four feature blocks added, all commented. (1) Includes for inpdefs.h
|
|||
|
|
(INPtypelook) and numparam.h (nupa_skip_line). (2) New function
|
|||
|
|
`inp_apply_subckt_scale()` plus helpers `subckt_has_scale()` and
|
|||
|
|
`scale_geom_param()` implement HSPICE element scale: for any .subckt declaring
|
|||
|
|
a `scale` parameter, geometry value-expressions in its body are wrapped so
|
|||
|
|
lengths are multiplied by scale and areas (ad/as) by scale^2, replacing the
|
|||
|
|
old hard-coded `geoshrink` option; the comment stresses this must run before
|
|||
|
|
numparam preparation (hence called from inp_readall) and details which params
|
|||
|
|
are scaled/skipped (STEP 31). (3) In the undefined-subckt check, before
|
|||
|
|
erroring on an unknown X-instance the code now checks whether the target token
|
|||
|
|
names a registered OSDI device (INPtypelook >= 0) and, if so, comments the
|
|||
|
|
line out and calls nupa_skip_line; the long comment explains foundry PDKs
|
|||
|
|
embed Verilog-A diagnostic instances (ESD monitors) as X-calls per HSPICE
|
|||
|
|
convention, that dropping these diagnostic-only modules doesn't change circuit
|
|||
|
|
behavior, and why numparam's independent line state must also be reset (STEP
|
|||
|
|
16). (4) The binning/model-pruning logic was refactored to use a new helper
|
|||
|
|
`get_model_bins()` and, critically, to "fail open": bins are only pruned when
|
|||
|
|
at least one bin actually matches the instance w/l, otherwise all model cards
|
|||
|
|
are kept so INPgetModBin can bin later after numparam applies shrink; the
|
|||
|
|
comment explains PDKs that apply shrink inside the subckt would otherwise
|
|||
|
|
match no bin and lose all models (STEP 30). (5) In the e/f/g/h POLY
|
|||
|
|
translation, an optional HSPICE source-type marker
|
|||
|
|
(`vccs`/`vcvs`/`cccs`/`ccvs`) between output nodes and POLY is now peeked and
|
|||
|
|
skipped since the device letter already encodes the type; the POLY check was
|
|||
|
|
also guarded with `next_name &&` (STEP 29, fixes BCD thermal_branch.inc G-poly
|
|||
|
|
parsing).
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -359,9 +435,86 @@
|
|||
|
|
dynMaxckt++;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- /* Now check to see if there are still subckt instances undefined... */
|
|||
|
|
+ /* Now check to see if there are still subckt instances undefined.
|
|||
|
|
+ *
|
|||
|
|
+ * HSPICE convention: an `X<inst>` instance line can call EITHER a
|
|||
|
|
+ * `.subckt` OR a Verilog-A module loaded via OSDI. The target
|
|||
|
|
+ * 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
|
|||
|
|
+ * routinely embed VA-module diagnostic
|
|||
|
|
+ * instances like `xesd_monitor d g s b esd_nfet_monitor ...`
|
|||
|
|
+ * inside their FET subckts, expecting HSPICE-style dispatch.
|
|||
|
|
+ *
|
|||
|
|
+ * Compromise: if the target name is a registered OSDI device
|
|||
|
|
+ * (INPtypelook ≥ 0), comment out the line. ESD monitor modules in
|
|||
|
|
+ * foundry decks are diagnostic-only — `$strobe` messages for spec
|
|||
|
|
+ * violations, no current/charge contributions — so dropping them
|
|||
|
|
+ * doesn't change circuit behavior, only disables the diagnostics.
|
|||
|
|
+ * A proper fix would auto-generate a `.model + N-prefix` shim, but
|
|||
|
|
+ * needs ngspice's OSDI dispatch to accept generic-prefix instances
|
|||
|
|
+ * for non-MOSFET VA modules; that's a separate change. */
|
|||
|
|
for (c = deck; c; c = c->nextcard)
|
|||
|
|
if (ciprefix(invoke, c->line)) {
|
|||
|
|
+ ...[59 lines of new OSDI code inserted here — full listing in ng_diff.txt]...
|
|||
|
|
fprintf(cp_err, "Error: unknown subckt: %s\n", c->line);
|
|||
|
|
fprintf(cp_err, " in line no. %d from file %s\n", c->linenum_orig, c->linesource);
|
|||
|
|
|
|||
|
|
@@ -617,102 +890,73 @@
|
|||
|
|
if (sss) {
|
|||
|
|
// tprint(sss->su_def);
|
|||
|
|
struct card *su_deck = inp_deckcopy(sss->su_def);
|
|||
|
|
+
|
|||
|
|
+ /* (HSPICE element scale is applied earlier, in
|
|||
|
|
+ inp_subcktexpand(), by wrapping the subckt body's
|
|||
|
|
+ geometry expressions before the numparam passes.) */
|
|||
|
|
+
|
|||
|
|
/* If we have modern PDKs, we have to reduce the amount of memory required.
|
|||
|
|
We try to reduce the models to the one really used.
|
|||
|
|
Otherwise su_deck is full of unused binning models.*/
|
|||
|
|
if ((newcompat.hs || newcompat.spe) && c->w > 0 && c->l > 0) {
|
|||
|
|
- /* extract wmin, wmax, lmin, lmax */
|
|||
|
|
struct card* new_deck = su_deck;
|
|||
|
|
struct card* prev = NULL;
|
|||
|
|
- while (su_deck) {
|
|||
|
|
- if (!ciprefix(".model", su_deck->line)) {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
- continue;
|
|||
|
|
- }
|
|||
|
|
+ struct card* scan;
|
|||
|
|
+ int nmatch = 0;
|
|||
|
|
|
|||
|
|
- char* curr_line = su_deck->line;
|
|||
|
|
+ float csl = (float)scale * c->l;
|
|||
|
|
+ /* scale by nf */
|
|||
|
|
+ float csw = (float)scale * c->w / c->nf;
|
|||
|
|
+
|
|||
|
|
+ /* Does any bin contain the instance's w/l? These
|
|||
|
|
+ are the DRAWN dimensions off the X line; the bin
|
|||
|
|
+ ranges are post-shrink. For PDKs that apply the
|
|||
|
|
+ shrink inside the subcircuit (l_scale='l*shrink',
|
|||
|
|
+ as foundry HV devices do) the drawn dims do not
|
|||
|
|
+ map to the bins here, so nothing matches. In that
|
|||
|
|
+ case keep every model card and let INPgetModBin do
|
|||
|
|
+ the binning after numparam has applied the shrink
|
|||
|
|
+ to the MOS line. */
|
|||
|
|
+ for (scan = su_deck; scan; scan = scan->nextcard) {
|
|||
|
|
float fwmin, fwmax, flmin, flmax;
|
|||
|
|
- char *wmin = strstr(curr_line, " wmin=");
|
|||
|
|
- if (wmin) {
|
|||
|
|
- int err;
|
|||
|
|
- wmin = wmin + 6;
|
|||
|
|
- fwmin = (float)INPevaluate(&wmin, &err, 0);
|
|||
|
|
- if (err) {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
- continue;
|
|||
|
|
- }
|
|||
|
|
- }
|
|||
|
|
- else {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
+ if (!ciprefix(".model", scan->line))
|
|||
|
|
continue;
|
|||
|
|
- }
|
|||
|
|
- char *wmax = strstr(curr_line, " wmax=");
|
|||
|
|
- if (wmax) {
|
|||
|
|
- int err;
|
|||
|
|
- wmax = wmax + 6;
|
|||
|
|
- fwmax = (float)INPevaluate(&wmax, &err, 0);
|
|||
|
|
- if (err) {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
- continue;
|
|||
|
|
- }
|
|||
|
|
- }
|
|||
|
|
- else {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
+ if (!get_model_bins(scan->line, &fwmin, &fwmax,
|
|||
|
|
+ &flmin, &flmax))
|
|||
|
|
continue;
|
|||
|
|
- }
|
|||
|
|
+ if (csl >= flmin && csl < flmax &&
|
|||
|
|
+ csw >= fwmin && csw < fwmax)
|
|||
|
|
+ nmatch++;
|
|||
|
|
+ }
|
|||
|
|
|
|||
|
|
- char* lmin = strstr(curr_line, " lmin=");
|
|||
|
|
- if (lmin) {
|
|||
|
|
- int err;
|
|||
|
|
- lmin = lmin + 6;
|
|||
|
|
- flmin = (float)INPevaluate(&lmin, &err, 0);
|
|||
|
|
- if (err) {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
- continue;
|
|||
|
|
- }
|
|||
|
|
- }
|
|||
|
|
- else {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
- continue;
|
|||
|
|
- }
|
|||
|
|
- char* lmax = strstr(curr_line, " lmax=");
|
|||
|
|
- if (lmax) {
|
|||
|
|
- int err;
|
|||
|
|
- lmax = lmax + 6;
|
|||
|
|
- flmax = (float)INPevaluate(&lmax, &err, 0);
|
|||
|
|
- if (err) {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
- continue;
|
|||
|
|
- }
|
|||
|
|
- }
|
|||
|
|
- else {
|
|||
|
|
- prev = su_deck;
|
|||
|
|
- su_deck = su_deck->nextcard;
|
|||
|
|
- continue;
|
|||
|
|
- }
|
|||
|
|
+ /* Only when a bin matched do we drop the others (the
|
|||
|
|
+ memory optimization). Otherwise leave su_deck
|
|||
|
|
+ untouched. */
|
|||
|
|
+ while (nmatch > 0 && su_deck) {
|
|||
|
|
+ float fwmin, fwmax, flmin, flmax;
|
|||
|
|
|
|||
|
|
- float csl = (float)scale * c->l;
|
|||
|
|
- /* scale by nf */
|
|||
|
|
- float csw = (float)scale * c->w / c->nf;
|
|||
|
|
- /*fprintf(stdout, "Debug: nf = %f\n", c->nf);*/
|
|||
|
|
- if (csl >= flmin && csl < flmax && csw >= fwmin && csw < fwmax) {
|
|||
|
|
- /* use the current .model card */
|
|||
|
|
+ if (!ciprefix(".model", su_deck->line) ||
|
|||
|
|
+ !get_model_bins(su_deck->line, &fwmin, &fwmax,
|
|||
|
|
+ &flmin, &flmax) ||
|
|||
|
|
+ (csl >= flmin && csl < flmax &&
|
|||
|
|
+ csw >= fwmin && csw < fwmax)) {
|
|||
|
|
+ /* keep: non-model, unbinned, or matching */
|
|||
|
|
prev = su_deck;
|
|||
|
|
su_deck = su_deck->nextcard;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
- else {
|
|||
|
|
+
|
|||
|
|
+ /* out-of-bin model card: drop it */
|
|||
|
|
+ {
|
|||
|
|
struct card* tmpcard = su_deck->nextcard;
|
|||
|
|
- line_free_x(prev->nextcard, FALSE);
|
|||
|
|
- su_deck = prev->nextcard = tmpcard;
|
|||
|
|
+ if (prev) {
|
|||
|
|
+ line_free_x(prev->nextcard, FALSE);
|
|||
|
|
+ su_deck = prev->nextcard = tmpcard;
|
|||
|
|
+ }
|
|||
|
|
+ else {
|
|||
|
|
+ line_free_x(new_deck, FALSE);
|
|||
|
|
+ su_deck = new_deck = tmpcard;
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
su_deck = new_deck;
|
|||
|
|
@@ -1379,12 +1623,28 @@
|
|||
|
|
bxx_putc(&buffer, ' ');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- /* Next we handle the POLY (if any) */
|
|||
|
|
- /* get next token */
|
|||
|
|
+ /* HSPICE compat: skip optional source-type marker
|
|||
|
|
+ * (vccs/vcvs/cccs/ccvs) between output nodes and POLY.
|
|||
|
|
+ * The letter `g`/`e`/`f`/`h` already encodes the type, so
|
|||
|
|
+ * the marker is redundant; consume it so the downstream
|
|||
|
|
+ * POLY translator (and inp2g/e/f/h) sees a clean line. */
|
|||
|
|
t = s;
|
|||
|
|
next_name = gettok_noparens(&t);
|
|||
|
|
- if ((strcmp(next_name, "POLY") == 0) ||
|
|||
|
|
- (strcmp(next_name, "poly") == 0)) {
|
|||
|
|
+ if (next_name && (
|
|||
|
|
+ (dev_type == 'g' && cieq(next_name, "vccs")) ||
|
|||
|
|
+ (dev_type == 'e' && cieq(next_name, "vcvs")) ||
|
|||
|
|
+ (dev_type == 'f' && cieq(next_name, "cccs")) ||
|
|||
|
|
+ (dev_type == 'h' && cieq(next_name, "ccvs")))) {
|
|||
|
|
+ s = t;
|
|||
|
|
+ tfree(next_name);
|
|||
|
|
+ t = s;
|
|||
|
|
+ next_name = gettok_noparens(&t);
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ /* Next we handle the POLY (if any) */
|
|||
|
|
+ if (next_name &&
|
|||
|
|
+ ((strcmp(next_name, "POLY") == 0) ||
|
|||
|
|
+ (strcmp(next_name, "poly") == 0))) {
|
|||
|
|
|
|||
|
|
#ifdef TRACE
|
|||
|
|
printf("In translate, looking at e, f, g, h found poly\n");
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[8] src/maths/KLU/klusmp.c
|
|||
|
|
pre-existing lines changed: 11 (+27 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
The KLU diagonal-gmin loader is reworked to honor the gmin_skip map. The old
|
|||
|
|
static helper LoadGmin_CSC(diag, n, Gmin) is replaced by
|
|||
|
|
LoadGmin_CSC_skip(SMPmatrix *eMatrix, Gmin), and both KLU factor call sites
|
|||
|
|
(klu_refactor and the pivot-tol path) are updated to call it. A new block
|
|||
|
|
comment explains the pass adds Gmin to every diagonal EXCEPT the synthesized
|
|||
|
|
analog-operator implicit-equation state nodes (laplace, zi, idt,
|
|||
|
|
transition-delay) flagged in eMatrix->gmin_skip, because gmin there over-damps
|
|||
|
|
the integrator-state Newton update and breaks gmin stepping (cross-referencing
|
|||
|
|
spsmp.c LoadGmin). It documents that KLU column i is the post-collapse index
|
|||
|
|
and the original node number is NewToOld[i]+1 (KLU columns 0-based, node
|
|||
|
|
numbers 1-based), and that with gmin_skip NULL the code is byte-for-byte the
|
|||
|
|
legacy "gmin on every diagonal" behaviour. The body dereferences the KLU
|
|||
|
|
matrix, reads the skip array and the NodeCollapsingNewToOld map, early-returns
|
|||
|
|
when Gmin==0, and skips diagonals whose mapped node is flagged.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -35,7 +35,7 @@
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
-static void LoadGmin_CSC (double **diag, unsigned int n, double Gmin) ;
|
|||
|
|
+static void LoadGmin_CSC_skip (SMPmatrix *eMatrix, double Gmin) ;
|
|||
|
|
static void LoadGmin (SMPmatrix *eMatrix, double Gmin) ;
|
|||
|
|
|
|||
|
|
typedef struct sElement {
|
|||
|
|
@@ -585,7 +585,7 @@
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Matrix->SMPkluMatrix->KLUloadDiagGmin) {
|
|||
|
|
- LoadGmin_CSC (Matrix->SMPkluMatrix->KLUmatrixDiag, Matrix->SMPkluMatrix->KLUmatrixN, Gmin) ;
|
|||
|
|
+ LoadGmin_CSC_skip (Matrix, Gmin) ;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ret = klu_refactor (Matrix->SMPkluMatrix->KLUmatrixAp, Matrix->SMPkluMatrix->KLUmatrixAi, Matrix->SMPkluMatrix->KLUmatrixAx,
|
|||
|
|
@@ -764,7 +764,7 @@
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (Matrix->SMPkluMatrix->KLUloadDiagGmin) {
|
|||
|
|
- LoadGmin_CSC (Matrix->SMPkluMatrix->KLUmatrixDiag, Matrix->SMPkluMatrix->KLUmatrixN, Gmin) ;
|
|||
|
|
+ LoadGmin_CSC_skip (Matrix, Gmin) ;
|
|||
|
|
}
|
|||
|
|
Matrix->SMPkluMatrix->KLUmatrixCommon->tol = PivRel ;
|
|||
|
|
|
|||
|
|
@@ -1768,18 +1768,34 @@
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
|
|||
|
|
+/* KLU diagonal-gmin pass. Adds Gmin to every diagonal element EXCEPT the
|
|||
|
|
+ * synthesized analog-operator implicit-equation state nodes (laplace, zi, idt,
|
|||
|
|
+ * transition-delay) flagged in eMatrix->gmin_skip -- gmin there over-damps
|
|||
|
|
+ * the integrator-state Newton update and breaks gmin stepping (see spsmp.c
|
|||
|
|
+ * LoadGmin for the full rationale). KLU column i is the post-collapse index;
|
|||
|
|
+ * the original ngspice node number is NewToOld[i] + 1 (KLU columns are 0-based,
|
|||
|
|
+ * node numbers 1-based -- cf. the singular_col + 1 node reporting in this
|
|||
|
|
+ * file). When gmin_skip is NULL (no flagged nodes) this is byte-for-byte the
|
|||
|
|
+ * legacy "gmin on every diagonal" behaviour. */
|
|||
|
|
static void
|
|||
|
|
-LoadGmin_CSC (double **diag, unsigned int n, double Gmin)
|
|||
|
|
+LoadGmin_CSC_skip (SMPmatrix *eMatrix, double Gmin)
|
|||
|
|
{
|
|||
|
|
+ KLUmatrix *klu = eMatrix->SMPkluMatrix ;
|
|||
|
|
+ double **diag = klu->KLUmatrixDiag ;
|
|||
|
|
+ unsigned int n = klu->KLUmatrixN ;
|
|||
|
|
+ char *skip = eMatrix->gmin_skip ;
|
|||
|
|
+ unsigned int *new2old = klu->KLUmatrixNodeCollapsingNewToOld ;
|
|||
|
|
unsigned int i ;
|
|||
|
|
|
|||
|
|
- if (Gmin != 0.0) {
|
|||
|
|
- for (i = 0 ; i < n ; i++) {
|
|||
|
|
- if (diag [i] != NULL) {
|
|||
|
|
- // Not all the elements on the diagonal are present, when the circuit is parsed
|
|||
|
|
- *(diag [i]) += Gmin ;
|
|||
|
|
- }
|
|||
|
|
- }
|
|||
|
|
+ if (Gmin == 0.0)
|
|||
|
|
+ return ;
|
|||
|
|
+
|
|||
|
|
+ for (i = 0 ; i < n ; i++) {
|
|||
|
|
+ if (diag [i] == NULL)
|
|||
|
|
+ continue ; // not all diagonal elements are present after parsing
|
|||
|
|
+ if (skip && new2old && skip [new2old [i] + 1])
|
|||
|
|
+ continue ;
|
|||
|
|
+ *(diag [i]) += Gmin ;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[9] src/maths/ni/niconv.c
|
|||
|
|
pre-existing lines changed: 5 (+17 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
NIconvTest is extended to implement the Axis-4 dual-norm (residual)
|
|||
|
|
convergence gate. The NEWCONV branch is restructured so that when CKTconvTest
|
|||
|
|
returns nonzero it now returns that error inside a braced block (functionally
|
|||
|
|
as before), and the old `#else return(0)` fallthrough is removed. A new
|
|||
|
|
commented block ("Bürmen axis 4") then checks CKTresidConverged: dual-norm
|
|||
|
|
convergence requires the residual to also be stable, not just the solution
|
|||
|
|
vector, catching the SPICE3 false-convergence pathology where the iterate
|
|||
|
|
stops moving (damping/scaling artifact) but f(x) is still nonzero. If residual
|
|||
|
|
checking is not disabled and the residual has not converged, it clears
|
|||
|
|
CKTtroubleNode and returns 1 (not converged); otherwise returns 0. The flag is
|
|||
|
|
set by NIiter from CKTrhs right after CKTload and can be opted out with
|
|||
|
|
`.option noresidcheck`. (Note: per niiter.c, CKTresidConverged is currently
|
|||
|
|
forced to 1, so this gate is effectively a passed-through placeholder pending
|
|||
|
|
a future opt-in.)
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -90,10 +90,22 @@
|
|||
|
|
/* CKTconvTest early-returns nonzero 'i' on the first error
|
|||
|
|
* in evaluating convergence (such as parameter out of range) so
|
|||
|
|
* there may be untested devices that have not yet converged */
|
|||
|
|
- if (i)
|
|||
|
|
- ckt->CKTtroubleNode = 0;
|
|||
|
|
- return(i);
|
|||
|
|
-#else /* NEWCONV */
|
|||
|
|
- return(0);
|
|||
|
|
+ if (i) {
|
|||
|
|
+ ckt->CKTtroubleNode = 0;
|
|||
|
|
+ return(i);
|
|||
|
|
+ }
|
|||
|
|
#endif /* NEWCONV */
|
|||
|
|
+
|
|||
|
|
+ /* Bürmen axis 4: dual-norm convergence requires the residual to
|
|||
|
|
+ * also be stable, not just the solution vector. Catches the
|
|||
|
|
+ * SPICE3 false-convergence pathology where the iterate stops
|
|||
|
|
+ * moving (damping/scaling artifact) but f(x) is still nonzero.
|
|||
|
|
+ * The flag was set by NIiter from CKTrhs immediately after
|
|||
|
|
+ * CKTload. Opt-out via `.option noresidcheck`. */
|
|||
|
|
+ if (!ckt->CKTresidCheckDisabled && !ckt->CKTresidConverged) {
|
|||
|
|
+ ckt->CKTtroubleNode = 0;
|
|||
|
|
+ return 1;
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[10] src/maths/ni/nidest.c
|
|||
|
|
pre-existing lines changed: 3 (+3 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Cosmetic-only change. The three FREE() calls for CKTrhs, CKTrhsOld, and
|
|||
|
|
CKTrhsSpare were re-indented (extra alignment spaces added) so their FREE
|
|||
|
|
tokens line up. No behavioral change; the surrounding CKTirhs frees are
|
|||
|
|
unchanged.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -19,9 +19,9 @@
|
|||
|
|
if (ckt->CKTmatrix)
|
|||
|
|
SMPdestroy(ckt->CKTmatrix);
|
|||
|
|
FREE(ckt->CKTmatrix);
|
|||
|
|
- if(ckt->CKTrhs) FREE(ckt->CKTrhs);
|
|||
|
|
- if(ckt->CKTrhsOld) FREE(ckt->CKTrhsOld);
|
|||
|
|
- if(ckt->CKTrhsSpare) FREE(ckt->CKTrhsSpare);
|
|||
|
|
+ if(ckt->CKTrhs) FREE(ckt->CKTrhs);
|
|||
|
|
+ if(ckt->CKTrhsOld) FREE(ckt->CKTrhsOld);
|
|||
|
|
+ if(ckt->CKTrhsSpare) FREE(ckt->CKTrhsSpare);
|
|||
|
|
if(ckt->CKTirhs) FREE(ckt->CKTirhs);
|
|||
|
|
if(ckt->CKTirhsOld) FREE(ckt->CKTirhsOld);
|
|||
|
|
if(ckt->CKTirhsSpare) FREE(ckt->CKTirhsSpare);
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[11] src/osdi/Makefile.am
|
|||
|
|
pre-existing lines changed: 1 (+3 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Adds a new source file osdi_defer.c to the libosdi build (and an empty
|
|||
|
|
continuation line for readability). osdi_defer.c is the new helper module
|
|||
|
|
backing the "deferred expression evaluation" feature (HSPICE-style
|
|||
|
|
geometry-dependent model/instance parameter expressions like
|
|||
|
|
vsat1='(l==14e-9)*A + ...') that osdisetup.c/osdiparam.c now call into.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -15,7 +16,8 @@
|
|||
|
|
osdisetup.c \
|
|||
|
|
osditrunc.c \
|
|||
|
|
osdipzld.c \
|
|||
|
|
- osdicallbacks.c
|
|||
|
|
+ osdicallbacks.c \
|
|||
|
|
+ osdi_defer.c
|
|||
|
|
|
|||
|
|
|
|||
|
|
libosdi_la_LIBADD = $(XSPICEDLLIBS)
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[12] src/osdi/osdi.h
|
|||
|
|
pre-existing lines changed: 1 (+195 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Bumps OSDI_VERSION_MINOR_CURR from 3 to 5 and adds the whole v0.5 ABI surface
|
|||
|
|
(with comments referencing OSDI_0_5_DESIGN.md). New JACOBIAN_ENTRY_DELAY (16)
|
|||
|
|
flag for AC delay coupling. New eval-return flags: EVAL_RET_FLAG_REJECT_STEP
|
|||
|
|
(16, axis-3 step rejection when the model/simulator detects an ill-conditioned
|
|||
|
|
regime), EVAL_RET_FLAG_EVENT (32) and EVAL_RET_FLAG_CROSS (64, axis-4
|
|||
|
|
event-driven operators). New OSDI_EVENT_KIND_* discriminants
|
|||
|
|
(CROSS/TIMER/INITIAL_STEP/FINAL_STEP). New structs OsdiEventRequest,
|
|||
|
|
OsdiCrossExprMeta (direction/ttol/etol), and OsdiEventSlotMeta. OsdiSimInfo
|
|||
|
|
gains tail-appended ABI-safe fields: cross_expr, pending_events,
|
|||
|
|
at_scheduled_event, fired_event_id, scheduled_event_time (S3c sample-exact
|
|||
|
|
crossing time), at_final_step, newton_iter (axis-2 iteration-aware $limit),
|
|||
|
|
and the absdelay fields delay_input/delay_maxtd/delay_state/delay_read.
|
|||
|
|
OsdiDescriptor gains previously-undeclared v0.4 fields (given_flag_*,
|
|||
|
|
resistive/reactive jacobian entry counts, write_jacobian_array_*, inputs,
|
|||
|
|
load_jacobian_with_offset_*) — needed so the C struct mirrors the binary
|
|||
|
|
layout — plus v0.5 fields: num_cross_exprs/cross_expr_metadata,
|
|||
|
|
num_event_slots/event_slot_metadata, num_delay_sites,
|
|||
|
|
num_delay_jacobian_entries/write_jacobian_array_delay/delay_jacobian_sites,
|
|||
|
|
and persistent_state_offset/count.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -18,7 +18,7 @@
|
|||
|
|
|
|||
|
|
|
|||
|
|
#define OSDI_VERSION_MAJOR_CURR 0
|
|||
|
|
-#define OSDI_VERSION_MINOR_CURR 3
|
|||
|
|
+#define OSDI_VERSION_MINOR_CURR 5
|
|||
|
|
|
|||
|
|
#define PARA_TY_MASK 3
|
|||
|
|
#define PARA_TY_REAL 0
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[13] src/osdi/osdiload.c
|
|||
|
|
pre-existing lines changed: 10 (+670 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
The largest change; implements most of the v0.5 runtime. $limit synthesis
|
|||
|
|
support: NUM_SIM_PARAMS grows 10->15 adding osdi_vlim plus per-shape
|
|||
|
|
osdi_vlim_vds/vgs/vbs/nqs $simparams, with a cascade (per-shape > generic >
|
|||
|
|
0.1V default) that OpenVA's compiler-side $limit consumes to bound
|
|||
|
|
per-iteration Newton delta-v on V() probes (commented as
|
|||
|
|
voltage-range-agnostic, bounding step magnitude not rails). Adds
|
|||
|
|
osdi_event_prepare (lazy-alloc cross/event/delay buffers onto a per-eval
|
|||
|
|
thread-local SimInfo copy, and fire logic that pops the front scheduled event
|
|||
|
|
when abstime >= t_event, exposing sample-exact scheduled_event_time),
|
|||
|
|
osdi_cross_time (3-point quadratic crossing-time fit falling back to linear),
|
|||
|
|
and osdi_event_postprocess (drain model pending_events into the sorted queue;
|
|||
|
|
detect cross_expr sign flips per meta->direction and schedule at the
|
|||
|
|
interpolated crossing time; shift the 2-deep history). The comments explain
|
|||
|
|
the S3c "plan B": true back-step/bisection is impractical because dctran's /8
|
|||
|
|
reject path skips CKTtrunc, so they fire on the first eval at/past the
|
|||
|
|
interpolated t_event instead. Robustness: sanitize_residuals (zero only
|
|||
|
|
NaN/Inf residuals and signal noncon; comments state finite-magnitude clamping
|
|||
|
|
was REMOVED as physically baseless and harmful for power devices) and
|
|||
|
|
sanitize_jacobian (zero/replace NaN Jacobian entries — diagonal replaced by
|
|||
|
|
|I|/0.9 to bound the N-R update to ~VDD, huge-finite entries capped at 1e6
|
|||
|
|
except transient reactive-bearing entries where large ag0*dQ/dV is physical,
|
|||
|
|
and raising CKTosdiStepReject / CKThugeJThisIter). Transient charge fixes:
|
|||
|
|
kill the TRAP zeroth-order-predictor sign-flip oscillation when charge equals
|
|||
|
|
the previous accepted value, and seed the reactive state from the MODETRANOP
|
|||
|
|
charge so the first step's dq/dt=0 (mirrors capload.c, avoiding a phantom
|
|||
|
|
discharge transient). Deferred-commit persistent state: osdi_persist_restore
|
|||
|
|
(restore snapshot->live before each eval) and osdi_persist_commit, plus the
|
|||
|
|
new OSDIaccept hook that per accepted step commits persistent state and pushes
|
|||
|
|
delay-ring samples (osdi_delay_push, geometric growth + horizon pruning).
|
|||
|
|
INIT_LIM gating widened (MODEINITJCT|MODEINITFLOAT|MODEINITFIX, excluding
|
|||
|
|
transient init) so $limit's clamp is bypassed while PrevState is stale during
|
|||
|
|
DC OP finding. At the DC->transient boundary (MODEINITTRAN) prev_state is read
|
|||
|
|
from CKTstates[1] (the rotated-away OP state) so the limiter sees delta~0 and
|
|||
|
|
stays transparent. Also forwards newton_iter, sets at_final_step at
|
|||
|
|
CKTfinalTime, and on EVAL_RET_FLAG_LIM/REJECT_STEP marks CKTtroubleElt,
|
|||
|
|
increments noncon, and sets CKTosdiStepReject (gated by CKTosdiStepRejectOff).
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -17,21 +17,25 @@
|
|||
|
|
#include "osdi.h"
|
|||
|
|
#include "osdidefs.h"
|
|||
|
|
|
|||
|
|
+#include <math.h>
|
|||
|
|
#include <stdint.h>
|
|||
|
|
+
|
|||
|
|
#include <stdio.h>
|
|||
|
|
#include <stdlib.h>
|
|||
|
|
#include <string.h>
|
|||
|
|
#include <sys/types.h>
|
|||
|
|
|
|||
|
|
-#define NUM_SIM_PARAMS 10
|
|||
|
|
+#define NUM_SIM_PARAMS 15
|
|||
|
|
char *sim_params[NUM_SIM_PARAMS + 1] = {
|
|||
|
|
- "iniLim", "gmin", "gdev", "tnom",
|
|||
|
|
- "simulatorVersion", "sourceScaleFactor",
|
|||
|
|
- "epsmin", "reltol", "vntol", "abstol",
|
|||
|
|
+ "iniLim", "gmin", "gdev", "tnom",
|
|||
|
|
+ "simulatorVersion", "sourceScaleFactor",
|
|||
|
|
+ "epsmin", "reltol", "vntol", "abstol",
|
|||
|
|
+ "osdi_vlim",
|
|||
|
|
+ "osdi_vlim_vds", "osdi_vlim_vgs", "osdi_vlim_vbs", "osdi_vlim_nqs",
|
|||
|
|
NULL};
|
|||
|
|
char *sim_params_str[1] = {NULL};
|
|||
|
|
|
|||
|
|
-double sim_param_vals[NUM_SIM_PARAMS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|||
|
|
+double sim_param_vals[NUM_SIM_PARAMS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|||
|
|
|
|||
|
|
/* values returned by $simparam*/
|
|||
|
|
OsdiSimParas get_simparams(const CKTcircuit *ckt) {
|
|||
|
|
@@ -42,10 +46,25 @@
|
|||
|
|
: (ckt->CKTdiagGmin);
|
|||
|
|
double initializeLimiting = (ckt->CKTmode & MODEINITJCT) ? 1 : 0;
|
|||
|
|
|
|||
|
|
+ /* osdi_vlim*: per-iteration Δv bounds consumed by OpenVA's compiler-side
|
|||
|
|
+ * $limit synthesis. Each V() probe inside a branch contribution gets
|
|||
|
|
+ * wrapped with a clamp |V_new - V_prev| ≤ osdi_vlim_<shape>, where
|
|||
|
|
+ * <shape> is the MOSFET-style classification of the probe (vds/vgs/vbs),
|
|||
|
|
+ * with osdi_vlim as the generic fallback. Cascade: per-shape value if
|
|||
|
|
+ * user set it > 0, else generic osdi_vlim, else 0.1 V default. All
|
|||
|
|
+ * voltage-range-agnostic — bounds Newton step magnitude, not supply
|
|||
|
|
+ * rails. */
|
|||
|
|
+ double osdi_vlim_v = (ckt->CKTosdiVlim > 0.0) ? ckt->CKTosdiVlim : 0.1;
|
|||
|
|
+ double osdi_vlim_vds_v = (ckt->CKTosdiVlimVds > 0.0) ? ckt->CKTosdiVlimVds : osdi_vlim_v;
|
|||
|
|
+ double osdi_vlim_vgs_v = (ckt->CKTosdiVlimVgs > 0.0) ? ckt->CKTosdiVlimVgs : osdi_vlim_v;
|
|||
|
|
+ double osdi_vlim_vbs_v = (ckt->CKTosdiVlimVbs > 0.0) ? ckt->CKTosdiVlimVbs : osdi_vlim_v;
|
|||
|
|
+ double osdi_vlim_nqs_v = (ckt->CKTosdiVlimNqs > 0.0) ? ckt->CKTosdiVlimNqs : osdi_vlim_v;
|
|||
|
|
double sim_param_vals_[NUM_SIM_PARAMS] = {
|
|||
|
|
// Verilog-A tnom is in degrees Celsius
|
|||
|
|
- initializeLimiting, gmin, gdev, ckt->CKTnomTemp-CONSTCtoK, simulatorVersion, sourceScaleFactor,
|
|||
|
|
- ckt->CKTepsmin, ckt->CKTreltol, ckt->CKTvoltTol, ckt->CKTabstol };
|
|||
|
|
+ initializeLimiting, gmin, gdev, ckt->CKTnomTemp-CONSTCtoK, simulatorVersion, sourceScaleFactor,
|
|||
|
|
+ ckt->CKTepsmin, ckt->CKTreltol, ckt->CKTvoltTol, ckt->CKTabstol,
|
|||
|
|
+ osdi_vlim_v,
|
|||
|
|
+ osdi_vlim_vds_v, osdi_vlim_vgs_v, osdi_vlim_vbs_v, osdi_vlim_nqs_v };
|
|||
|
|
memcpy(&sim_param_vals, &sim_param_vals_, sizeof(double) * NUM_SIM_PARAMS);
|
|||
|
|
OsdiSimParas sim_params_ = {.names = sim_params,
|
|||
|
|
.vals = (double *)&sim_param_vals,
|
|||
|
|
@@ -54,14 +73,374 @@
|
|||
|
|
return sim_params_;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
+ ...[241 lines of new OSDI code inserted here — full listing in ng_diff.txt]...
|
|||
|
|
static void eval(const OsdiDescriptor *descr, const GENinstance *gen_inst,
|
|||
|
|
void *inst, OsdiExtraInstData *extra_inst_data,
|
|||
|
|
const void *model, const OsdiSimInfo *sim_info) {
|
|||
|
|
|
|||
|
|
OsdiNgspiceHandle handle =
|
|||
|
|
(OsdiNgspiceHandle){.kind = 3, .name = gen_inst->GENname};
|
|||
|
|
+
|
|||
|
|
+ /* OSDI v0.5 — per-instance scratch SimInfo with event-state
|
|||
|
|
+ * pointers wired up. Originals stay shared/read-only across the
|
|||
|
|
+ * OMP parallel region. */
|
|||
|
|
+ OsdiSimInfo sim_info_local = *sim_info;
|
|||
|
|
+ OsdiRegistryEntry *entry = osdi_reg_entry_inst(gen_inst);
|
|||
|
|
+ osdi_event_prepare(extra_inst_data, descr, entry->num_delay_sites,
|
|||
|
|
+ &sim_info_local);
|
|||
|
|
+
|
|||
|
|
/* TODO initial conditions? */
|
|||
|
|
- extra_inst_data->eval_flags = descr->eval(&handle, inst, model, sim_info);
|
|||
|
|
+ extra_inst_data->eval_flags =
|
|||
|
|
+ descr->eval(&handle, inst, model, &sim_info_local);
|
|||
|
|
+
|
|||
|
|
+ osdi_event_postprocess(extra_inst_data, descr, &sim_info_local);
|
|||
|
|
+}
|
|||
|
|
+
|
|||
|
|
+static void sanitize_residuals(CKTcircuit *ckt, void *inst,
|
|||
|
|
+ const OsdiDescriptor *descr, bool is_tran,
|
|||
|
|
+ const char *name, double t) {
|
|||
|
|
+ /* NaN/Inf in a residual means the model's eval produced a non-finite
|
|||
|
|
+ * value — always a real anomaly. Zero it out and signal non-
|
|||
|
|
+ * convergence so Newton retries with a smaller step.
|
|||
|
|
+ *
|
|||
|
|
+ * Finite-magnitude clamping was removed: it had no physical basis.
|
|||
|
|
+ * For body-diode-style "huge" residuals (V/Vt deep enough that
|
|||
|
|
+ * exp(V/Vt) blows up to 1e83+ A), the model's Jacobian scales with
|
|||
|
|
+ * the current itself, so Newton's update Δx = -F/J is naturally
|
|||
|
|
+ * bounded by the model. And in power designs (PMIC, LDMOS arrays,
|
|||
|
|
+ * battery switches) a single OSDI instance with m × nf in the
|
|||
|
|
+ * thousands can legitimately stamp hundreds-to-thousands of amps —
|
|||
|
|
+ * clipping those was breaking convergence. HSPICE and Spectre do
|
|||
|
|
+ * not magnitude-clip residuals either. */
|
|||
|
|
+ for (uint32_t i = 0; i < descr->num_nodes; i++) {
|
|||
|
|
+ if (descr->nodes[i].resist_residual_off != UINT32_MAX) {
|
|||
|
|
+ ...[87 more replacement lines — full listing in ng_diff.txt]...
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void load(CKTcircuit *ckt, const GENinstance *gen_inst, void *model,
|
|||
|
|
@@ -103,7 +494,29 @@
|
|||
|
|
NIintegrate(ckt, &dump, &dump, 0, state);
|
|||
|
|
|
|||
|
|
/* add the numeric derivative to the rhs */
|
|||
|
|
- ckt->CKTrhs[node_mapping[i]] -= ckt->CKTstate0[state + 1];
|
|||
|
|
+ double integrated = ckt->CKTstate0[state + 1];
|
|||
|
|
+ double stamp;
|
|||
|
|
+ if (!isfinite(integrated)) {
|
|||
|
|
+ /* NaN/inf: reset history to prevent propagation */
|
|||
|
|
+ ckt->CKTstate0[state + 1] = 0.0;
|
|||
|
|
+ stamp = 0.0;
|
|||
|
|
+ ckt->CKTnoncon++;
|
|||
|
|
+ } else if (residual_react == q_old_charge) {
|
|||
|
|
+ /* Charge is identical to the previous accepted value (zeroth-order
|
|||
|
|
+ predictor). TRAP gives q_dot_new = −q_dot_old, so the sign
|
|||
|
|
+ flips every timestep — an artificial oscillation that reverses
|
|||
|
|
+ the sign of the RHS contribution and derails the first N-R step.
|
|||
|
|
+ Zero both the stamp and the stored derivative to kill it. */
|
|||
|
|
+ ckt->CKTstate0[state + 1] = 0.0;
|
|||
|
|
+ stamp = 0.0;
|
|||
|
|
+ } else {
|
|||
|
|
+ /* Finite q_dot of any magnitude is stamped as-is. No
|
|||
|
|
+ magnitude clamp — see sanitize_residuals comment for why
|
|||
|
|
+ clipping was actively wrong for power devices. Genuine
|
|||
|
|
+ overflow surfaces as NaN/Inf and is caught above. */
|
|||
|
|
+ stamp = integrated;
|
|||
|
|
+ }
|
|||
|
|
+ ckt->CKTrhs[node_mapping[i]] -= stamp;
|
|||
|
|
|
|||
|
|
if (is_init_tran) {
|
|||
|
|
ckt->CKTstate1[state + 1] = ckt->CKTstate0[state + 1];
|
|||
|
|
@@ -132,7 +711,20 @@
|
|||
|
|
bool is_tran = ckt->CKTmode & (MODETRAN);
|
|||
|
|
bool is_tran_op = ckt->CKTmode & (MODETRANOP);
|
|||
|
|
bool is_init_tran = ckt->CKTmode & MODEINITTRAN;
|
|||
|
|
- bool is_init_junc = ckt->CKTmode & MODEINITJCT;
|
|||
|
|
+ /* INIT_LIM gating: fire whenever the simulator is in any DC-OP
|
|||
|
|
+ * initialization mode where PrevState may be stale (== 0). Includes
|
|||
|
|
+ * the literal first iter (MODEINITJCT), the floating-restart mode used
|
|||
|
|
+ * by gmin and source stepping when normal Newton fails (MODEINITFLOAT),
|
|||
|
|
+ * and the fixed-IC mode (MODEINITFIX). All three lead the simulator
|
|||
|
|
+ * to re-enter CKTload with PrevState still uninitialized, so the
|
|||
|
|
+ * compiler-side $limit synthesis must bypass its per-iter Δv clamp
|
|||
|
|
+ * (which would otherwise pin every V() to ±vlim of zero, derailing
|
|||
|
|
+ * the OP-finding machinery). Transient init modes (MODEINITTRAN /
|
|||
|
|
+ * MODEINITPRED) are intentionally EXCLUDED — by the time transient
|
|||
|
|
+ * starts the OP has converged and PrevState carries a valid value
|
|||
|
|
+ * from there; the clamp is desired throughout transient. */
|
|||
|
|
+ bool is_init_junc = ckt->CKTmode &
|
|||
|
|
+ (MODEINITJCT | MODEINITFLOAT | MODEINITFIX);
|
|||
|
|
|
|||
|
|
OsdiSimInfo sim_info = {
|
|||
|
|
.paras = get_simparams(ckt),
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[14] src/osdi/osdiregistry.c
|
|||
|
|
pre-existing lines changed: 6 (+48 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Relaxes and clarifies OSDI version acceptance and adds ABI-safe reads of new
|
|||
|
|
descriptor tail fields. The old strict "must equal current major.minor" check
|
|||
|
|
for original-OpenVAF binaries is replaced by an explicit "must be v0.3
|
|||
|
|
exactly" test with an improved message ("supports OSDI v0.3 (original OpenVAF)
|
|||
|
|
or v0.4+ (OpenVAF-reloaded / OpenVA)"), because VERSION_*_CURR now tracks 0.5.
|
|||
|
|
In the limiter table it now skips (continue) any lim entry whose func_ptr is
|
|||
|
|
already non-NULL, with a comment: models may ship their own limiter (either
|
|||
|
|
shadowing a standard name like pnjlim or a fully custom name+signature), so
|
|||
|
|
the simulator must not overwrite it or warn about an unknown name. Populates
|
|||
|
|
new registry-entry fields num_delay_sites, persistent_state_offset and
|
|||
|
|
persistent_state_count, each guarded by an offsetof-based descriptor_size
|
|||
|
|
bounds check so a pre-2C/pre-0.5 .osdi (smaller descriptor) is treated as zero
|
|||
|
|
rather than dereferenced past its published size.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -398,12 +398,16 @@
|
|||
|
|
}
|
|||
|
|
descriptor_size = *OSDI_DESCRIPTOR_SIZE;
|
|||
|
|
} else {
|
|||
|
|
- // Original OpenVAF, must be version==0.3
|
|||
|
|
- if (OSDI_VERSION_MAJOR != OSDI_VERSION_MAJOR_CURR ||
|
|||
|
|
- OSDI_VERSION_MINOR != OSDI_VERSION_MINOR_CURR) {
|
|||
|
|
- printf("NGSPICE only supports OSDI v%d.%d but \"%s\" uses v%d.%d!",
|
|||
|
|
- OSDI_VERSION_MAJOR_CURR, OSDI_VERSION_MINOR_CURR, path,
|
|||
|
|
- OSDI_VERSION_MAJOR, OSDI_VERSION_MINOR);
|
|||
|
|
+ /* Original OpenVAF binaries don't publish OSDI_DESCRIPTOR_SIZE
|
|||
|
|
+ * and must be v0.3 exactly. OSDI_VERSION_MAJOR_CURR /
|
|||
|
|
+ * OSDI_VERSION_MINOR_CURR now track the current
|
|||
|
|
+ * openvaf-reloaded/OpenVA version (0.5 as of S3a) so the
|
|||
|
|
+ * v0.3 check is hard-coded here. */
|
|||
|
|
+ if (OSDI_VERSION_MAJOR != 0 || OSDI_VERSION_MINOR != 3) {
|
|||
|
|
+ printf("NGSPICE only supports OSDI v0.3 (original OpenVAF) "
|
|||
|
|
+ "or v0.4+ (OpenVAF-reloaded / OpenVA) but \"%s\" uses "
|
|||
|
|
+ "v%d.%d!",
|
|||
|
|
+ path, OSDI_VERSION_MAJOR, OSDI_VERSION_MINOR);
|
|||
|
|
txfree(path);
|
|||
|
|
return INVALID_OBJECT;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[15] src/osdi/osditrunc.c
|
|||
|
|
pre-existing lines changed: 3 (+49 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Two timestep-control changes plus a license-header whitespace cleanup (and
|
|||
|
|
adds <stdio.h>). First (S3b): clamp the proposed timestep against pending
|
|||
|
|
scheduled events — for each instance with a non-empty sorted event queue, the
|
|||
|
|
earliest event time bounds dt so the simulator lands on the event time and the
|
|||
|
|
model's @(timer)/@(cross) body fires with at_scheduled_event=true. Second: cap
|
|||
|
|
timestep growth to 1.2x the most recently accepted step (CKTdeltaOld[0]). The
|
|||
|
|
comment documents the history — CKTterr is backward-looking and lets dt grow
|
|||
|
|
across a switching transition; the factor was 2.0x originally, tightened to
|
|||
|
|
1.5x and now to 1.2x for 22nm ULP
|
|||
|
|
driver_lv_2v5_tb to fix a residual pinb failure at t~1.369us, since 1.2x still
|
|||
|
|
grows ~6x over 10 accepted steps while keeping fast edges resolvable within
|
|||
|
|
itl4.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -1,16 +1,17 @@
|
|||
|
|
-/*
|
|||
|
|
+/*
|
|||
|
|
* This file is part of the OSDI component of NGSPICE.
|
|||
|
|
* Copyright© 2022 SemiMod GmbH.
|
|||
|
|
- *
|
|||
|
|
+ *
|
|||
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|||
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|||
|
|
- * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|||
|
|
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|||
|
|
*
|
|||
|
|
* Author: Pascal Kuthe <pascal.kuthe@semimod.de>
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include "ngspice/cktdefs.h"
|
|||
|
|
#include "osdidefs.h"
|
|||
|
|
+#include <stdio.h>
|
|||
|
|
|
|||
|
|
int OSDItrunc(GENmodel *in_model, CKTcircuit *ckt, double *timestep) {
|
|||
|
|
OsdiRegistryEntry *entry = osdi_reg_entry_model(in_model);
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[16] src/spicelib/devices/hisimhv2/hsmhv2ld.c
|
|||
|
|
pre-existing lines changed: 7 (+161 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Adds NaN-recovery ("sanity damping") guards to the HiSIM_HV LDMOS load
|
|||
|
|
function so a single NaN in the solution vector can't cascade through its
|
|||
|
|
5000+-line eval. Three guards, all heavily commented. (1) Off-mode
|
|||
|
|
INITJCT/INITFIX branch: instead of seeding all biases to 0 (a singular
|
|||
|
|
zero-bias point where 1/Vgs-shape terms blow up) it now seeds a small forward
|
|||
|
|
bias vgs=vges=vgse=0.1, vds=vdse=0.1 (body left at 0), matching the non-off
|
|||
|
|
INITJCT heuristic. (2) Input-side guard in the Newton branch: if any external
|
|||
|
|
bias (vbs/vgs/vds/vges/vdbd/vsbs/vbse/vgse/vdse/vsubs/deltemp) is NaN, snap
|
|||
|
|
each back to its state0 last-accepted value (with hard defaults if state0 is
|
|||
|
|
itself NaN or degenerate 0/0/0), and raise CKTosdiStepReject to request a
|
|||
|
|
smaller timestep. (3) Linear-branch guard: vddp/vggp/vssp/vbpdb/vbpb/vbpsb NaN
|
|||
|
|
-> 0 (vddp feeds rdrift which otherwise produces NaN Rd/Rs); the comment says
|
|||
|
|
this was the guard that finally broke the NaN feedback loop. (4) Output-side
|
|||
|
|
guard: after HSMHV2evaluate/rdrift/dio, any non-finite here->HSMHV2_* output
|
|||
|
|
(ids, all conductances/charges, isub, Rd/Rs, gm/gds/gmbs) is zeroed before it
|
|||
|
|
reaches the matrix ("defense in depth"), making the device stamp zero that
|
|||
|
|
iteration and raising CKTosdiStepReject so the framework retries. The comments
|
|||
|
|
frame this as the native-C analogue of BSIM-BULK's OpenVA $limit machinery
|
|||
|
|
(HiSIM_HV is native C so gets no OSDI help), validated on 14nm FinFET ld3nfet
|
|||
|
|
5V LDMOS.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -507,11 +507,18 @@
|
|||
|
|
}
|
|||
|
|
else if ( ( ckt->CKTmode & (MODEINITJCT | MODEINITFIX) ) &&
|
|||
|
|
here->HSMHV2_off ) {
|
|||
|
|
- vbs = vgs = vds = 0.0; vges = 0.0; vdbd = vsbs = 0.0;
|
|||
|
|
+ /* Off-mode initial condition. Setting everything to 0
|
|||
|
|
+ * lands the device at exactly zero-bias, which is a
|
|||
|
|
+ * singular point for HiSIM_HV's eval — `1/Vgs`-shape
|
|||
|
|
+ * terms blow up. Match the heuristic in the non-off
|
|||
|
|
+ * INITJCT branch (line ~499) and seed a small forward
|
|||
|
|
+ * bias instead. vbs/vsbs/vdbd stay at 0 (body biased to
|
|||
|
|
+ * source). */
|
|||
|
|
+ vbs = vsbs = vdbd = 0.0;
|
|||
|
|
+ vgs = vges = vgse = 0.1;
|
|||
|
|
+ vds = vdse = 0.1;
|
|||
|
|
if (flg_subNode > 0) vsubs = 0.0;
|
|||
|
|
if( here->HSMHV2_coselfheat > 0 ) deltemp=0.0;
|
|||
|
|
- vdse = vds ;
|
|||
|
|
- vgse = vgs ;
|
|||
|
|
Qi_nqs = Qb_nqs = 0.0 ;
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
@@ -1123,15 +1224,68 @@
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
/* call model evaluation */
|
|||
|
|
- if ( HSMHV2evaluate(ivdse,ivgse,ivbse,ivds, ivgs, ivbs, vbs_jct, vbd_jct, vsubs, vddp, deltemp, here, model, ckt) == HiSIM_ERROR )
|
|||
|
|
+ int ev_rc = HSMHV2evaluate(ivdse,ivgse,ivbse,ivds, ivgs, ivbs, vbs_jct, vbd_jct, vsubs, vddp, deltemp, here, model, ckt);
|
|||
|
|
+ if ( ev_rc == HiSIM_ERROR )
|
|||
|
|
return (HiSIM_ERROR);
|
|||
|
|
if ( here->HSMHV2_cordrift == 1 ) {
|
|||
|
|
- if ( HSMHV2rdrift(vddp, vds, vbs, vsubs, deltemp, here, model, ckt) == HiSIM_ERROR )
|
|||
|
|
- return (HiSIM_ERROR);
|
|||
|
|
+ int rd_rc = HSMHV2rdrift(vddp, vds, vbs, vsubs, deltemp, here, model, ckt);
|
|||
|
|
+ if (rd_rc == HiSIM_ERROR)
|
|||
|
|
+ return (HiSIM_ERROR);
|
|||
|
|
}
|
|||
|
|
- if ( HSMHV2dio(vbs_jct, vbd_jct, deltemp, here, model, ckt) == HiSIM_ERROR )
|
|||
|
|
+ int dio_rc = HSMHV2dio(vbs_jct, vbd_jct, deltemp, here, model, ckt);
|
|||
|
|
+ if (dio_rc == HiSIM_ERROR)
|
|||
|
|
return (HiSIM_ERROR);
|
|||
|
|
|
|||
|
|
+ ...[50 lines of new OSDI code inserted here — full listing in ng_diff.txt]...
|
|||
|
|
#ifdef DEBUG_HISIMHVCGG
|
|||
|
|
printf("HSMHV2_ids %e ", here->HSMHV2_ids ) ;
|
|||
|
|
printf("HSMHV2_cggb %e ", here->HSMHV2_cggb ) ;
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[17] src/spicelib/parser/inpdomod.c
|
|||
|
|
pre-existing lines changed: 1 (+8 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Adds support for MOS level 72 (BSIM-CMG, the FinFET CMC standard model, as an
|
|||
|
|
OSDI module). The new case 72 looks up device type "bsimcmg_va" and, if
|
|||
|
|
unavailable, emits a temp error telling the user to load bsimcmg.osdi before
|
|||
|
|
the .model card. The default-case "supported levels" message is updated from
|
|||
|
|
"...60,68,73,77" to include 72 ("...60,68,72,73,77"). Comment tags case 72 as
|
|||
|
|
"BSIM-CMG (OSDI) - FinFET CMC standard model".
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -389,7 +396,7 @@
|
|||
|
|
break;
|
|||
|
|
default: /* placeholder; use level xxx for the next model */
|
|||
|
|
err = INPmkTemp
|
|||
|
|
- ("Only MOS device levels 1-6,8-10,14,49,54-58,60,68,73,77 are supported in this binary\n");
|
|||
|
|
+ ("Only MOS device levels 1-6,8-10,14,49,54-58,60,68,72,73,77 are supported in this binary\n");
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
INPmakeMod(modname, type, image);
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[18] src/spicelib/parser/inpdoopt.c
|
|||
|
|
pre-existing lines changed: 7 (+90 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Makes unknown-.option handling non-fatal and less noisy for large PDK
|
|||
|
|
libraries, plus special-cases obsolete/artifact tokens. New static warned_opts
|
|||
|
|
table + unknown_opt_first_seen() dedupe so each unknown option name warns only
|
|||
|
|
once (prevents PDK decks that repeat .option lines from flooding stderr). New
|
|||
|
|
skip_opt_value() advances past an optional "=value" so a value isn't reparsed
|
|||
|
|
as the next keyword. "geoshrink" is now silently consumed (value ignored): the
|
|||
|
|
comment explains the MOSFET W/L shrink is now applied generically from the
|
|||
|
|
subcircuit's scale parameter during subckt expansion, matching HSPICE, so
|
|||
|
|
lingering .option geoshrink=<val> is accepted but ignored. numparm__________
|
|||
|
|
placeholder tokens (numparam string-literal artifacts) are discarded silently.
|
|||
|
|
Unknown options no longer set optCard->error (no longer fatal); a
|
|||
|
|
recognized-but-unimplemented HSPICE list (tmiflag, modmonte, tmipath,
|
|||
|
|
etmiusrinput) gets a softer "Warning:" prefix while genuine typos still get
|
|||
|
|
"Error:", so scripts can distinguish intentional pending features from
|
|||
|
|
mistakes.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -6,7 +6,7 @@
|
|||
|
|
|
|||
|
|
/* INPdoOpts(ckt,option card)
|
|||
|
|
* parse the options off of the given option card and add them to
|
|||
|
|
- * the given circuit
|
|||
|
|
+ * the given circuit
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
#include "ngspice/ngspice.h"
|
|||
|
|
@@ -26,7 +66,7 @@
|
|||
|
|
{
|
|||
|
|
char *line;
|
|||
|
|
char *token;
|
|||
|
|
- char *errmsg;
|
|||
|
|
+ char *errmsg; /* used for unimplemented-option and can't-set-option warnings */
|
|||
|
|
IFvalue *val;
|
|||
|
|
int error;
|
|||
|
|
int which;
|
|||
|
|
@@ -67,14 +107,57 @@
|
|||
|
|
}
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
- /* print err message only if it is not just a number */
|
|||
|
|
+
|
|||
|
|
+ /* geoshrink: obsolete. The MOSFET W/L shrink is now applied
|
|||
|
|
+ * generically from the subcircuit's `scale` parameter during
|
|||
|
|
+ * subckt expansion (see subckt.c), matching HSPICE. Decks that
|
|||
|
|
+ * still carry `.option geoshrink=<val>` are accepted but the
|
|||
|
|
+ * value is ignored — silently consume it so it isn't reparsed. */
|
|||
|
|
+ if (strcmp(token, "geoshrink") == 0) {
|
|||
|
|
+ while (isspace((unsigned char)*line)) line++;
|
|||
|
|
+ char *endp;
|
|||
|
|
+ (void) strtod(line, &endp);
|
|||
|
|
+ if (endp > line)
|
|||
|
|
+ line = endp;
|
|||
|
|
+ continue;
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ /* Skip '=value' so the value string is not re-parsed as an option. */
|
|||
|
|
+ skip_opt_value(&line);
|
|||
|
|
+
|
|||
|
|
+ /* Warn only for non-numeric tokens, and only the first time each
|
|||
|
|
+ * unknown option name is encountered. Do NOT set optCard->error:
|
|||
|
|
+ * unknown options are silently ignored, not fatal card errors.
|
|||
|
|
+ *
|
|||
|
|
+ * INPgetTok(gobble=1) consumes the '=' before returning, so
|
|||
|
|
+ * skip_opt_value above cannot see it and may leave a string value
|
|||
|
|
+ ...[5 more replacement lines — full listing in ng_diff.txt]...
|
|||
|
|
char* ctoken = token;
|
|||
|
|
while (*ctoken && strchr("0123456789.e+-", *ctoken))
|
|||
|
|
ctoken++;
|
|||
|
|
- if (*ctoken) {
|
|||
|
|
- errmsg = tprintf("Error: unknown option %s - ignored\n", token);
|
|||
|
|
- optCard->error = INPerrCat(optCard->error, errmsg);
|
|||
|
|
- fprintf(stderr, "%s\n", optCard->error);
|
|||
|
|
+ if (*ctoken && unknown_opt_first_seen(token)) {
|
|||
|
|
+ /* Recognised-but-unimplemented HSPICE options get a softer
|
|||
|
|
+ * "Warning:" prefix so user scripts that intentionally rely
|
|||
|
|
+ * on these features can distinguish them from real typos.
|
|||
|
|
+ * Add to this list when a new known-pending HSPICE option
|
|||
|
|
+ * appears in foundry decks. */
|
|||
|
|
+ static const char *const known_hspice_pending[] = {
|
|||
|
|
+ "tmiflag", "modmonte", "tmipath", "etmiusrinput",
|
|||
|
|
+ NULL
|
|||
|
|
+ };
|
|||
|
|
+ const char *const *p = known_hspice_pending;
|
|||
|
|
+ bool known_pending = false;
|
|||
|
|
+ for (; *p; p++) {
|
|||
|
|
+ if (strcasecmp(token, *p) == 0) { known_pending = true; break; }
|
|||
|
|
+ }
|
|||
|
|
+ if (known_pending)
|
|||
|
|
+ fprintf(stderr, "Warning: unknown option %s - ignored\n", token);
|
|||
|
|
+ else
|
|||
|
|
+ fprintf(stderr, "Error: unknown option %s - ignored\n", token);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[19] src/spicelib/parser/inpdpar.c
|
|||
|
|
pre-existing lines changed: 2 (+94 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Adds the parse-time binding registry that lets .dc sweep a bare .param (paired
|
|||
|
|
with inpeval.c and dctrcurv.c). New file-scope dpar_param_binding_t list
|
|||
|
|
records "this V/I source's DC value was set from .param <name> at parse time"
|
|||
|
|
as (param_name, dev_name, dev_type), with dpar_register_binding() plus opaque
|
|||
|
|
accessors (dpar_first_param_binding,
|
|||
|
|
dpar_binding_param_name/dev_name/dev_type/next) consumed by dctrcurv.c. In
|
|||
|
|
INPdevParse's leading-value path it clears the extern side-channel
|
|||
|
|
inpeval_last_param_name before calling INPevaluate, and if the leading value
|
|||
|
|
resolved from a bare .param identifier (waslead set and the side-channel
|
|||
|
|
populated) it registers a binding tagged with fast->GENname and dev type.
|
|||
|
|
Separately, for OSDI models (device->registry_entry != NULL) an unknown
|
|||
|
|
instance parameter is now skipped (consume its =value) rather than aborting,
|
|||
|
|
with a comment noting PDK subcircuits pass extra params like 'total' that the
|
|||
|
|
model doesn't define, matching HSPICE/Spectre. The "$" unknown-parameter case
|
|||
|
|
now errors out explicitly (goto quit).
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -125,10 +198,29 @@
|
|||
|
|
if (!p) {
|
|||
|
|
if (eq(parm, "$")) {
|
|||
|
|
errbuf = copy(" unknown parameter ($). Check the compatibility flag!\n");
|
|||
|
|
+ rtn = errbuf;
|
|||
|
|
+ goto quit;
|
|||
|
|
}
|
|||
|
|
- else {
|
|||
|
|
- errbuf = tprintf(" unknown parameter (%s) \n", parm);
|
|||
|
|
+ /* OSDI models may receive extra instance parameters from PDK
|
|||
|
|
+ * subcircuits (e.g. 'total' from 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) {
|
|||
|
|
+ while (isspace((unsigned char)**line)) (*line)++;
|
|||
|
|
+ if (**line == '=') {
|
|||
|
|
+ (*line)++;
|
|||
|
|
+ while (isspace((unsigned char)**line)) (*line)++;
|
|||
|
|
+ char *endp;
|
|||
|
|
+ strtod(*line, &endp);
|
|||
|
|
+ if (endp > *line)
|
|||
|
|
+ *line = endp;
|
|||
|
|
+ else
|
|||
|
|
+ while (**line && !isspace((unsigned char)**line)) (*line)++;
|
|||
|
|
+ }
|
|||
|
|
+ FREE(parm);
|
|||
|
|
+ continue;
|
|||
|
|
}
|
|||
|
|
+ errbuf = tprintf(" unknown parameter (%s) \n", parm);
|
|||
|
|
rtn = errbuf;
|
|||
|
|
goto quit;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[20] src/spicelib/parser/inpeval.c
|
|||
|
|
pre-existing lines changed: 133 (+114 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Two changes to INPevaluate(). (1) HSPICE-compat bare-.param fallback: when the
|
|||
|
|
token isn't numeric but is a valid identifier (letter/underscore start, then
|
|||
|
|
alnum/underscore, whole-token), it looks it up in the numparam dictionary via
|
|||
|
|
nupa_get_real and, on success, returns its value and stashes the resolved name
|
|||
|
|
in the new extern char *inpeval_last_param_name side-channel (a static buffer)
|
|||
|
|
so INPdevParse can register a .dc binding. This lets deck lines like "vgs n2
|
|||
|
|
n3 vgswp" / ".dc vgswp -0.1 vgmax 0.01" use bare param names where a number is
|
|||
|
|
expected, removing the previous requirement to quote/brace them. (2) Number
|
|||
|
|
parsing rewritten from the old digit-by-digit accumulator
|
|||
|
|
(mantis/expo1/expo2/expsgn + pow(10,expo)) to strtod, so results are
|
|||
|
|
correctly-rounded IEEE-754 and bit-match formula()/fetchnumber()'s sscanf
|
|||
|
|
"%lG", eliminating the 1-ULP drift that forced dctrcurv's brittle round-trip.
|
|||
|
|
Preserved: separate leading sign, Fortran 'D'/'d' exponents finished by hand
|
|||
|
|
via strtol, the SI scale suffixes (T/G/k/u/n/p/f/a and m/Meg/Mil with
|
|||
|
|
Mil=25.4e-6), the ternary ':' early-out, and the "suffix char left unconsumed"
|
|||
|
|
pointer semantics. Comments document each preserved behavior and why strtod
|
|||
|
|
matches the numparam path.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -16,11 +26,7 @@
|
|||
|
|
{
|
|||
|
|
char *token;
|
|||
|
|
char *here;
|
|||
|
|
- double mantis;
|
|||
|
|
- int expo1;
|
|||
|
|
- int expo2;
|
|||
|
|
int sign;
|
|||
|
|
- int expsgn;
|
|||
|
|
char *tmpline;
|
|||
|
|
|
|||
|
|
/* setup */
|
|||
|
|
@@ -37,11 +43,7 @@
|
|||
|
|
*error = 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- mantis = 0;
|
|||
|
|
- expo1 = 0;
|
|||
|
|
- expo2 = 0;
|
|||
|
|
sign = 1;
|
|||
|
|
- expsgn = 1;
|
|||
|
|
|
|||
|
|
/* loop through all of the input token */
|
|||
|
|
here = token;
|
|||
|
|
@@ -64,142 +118,69 @@
|
|||
|
|
return (0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- while (isdigit_c(*here)) {
|
|||
|
|
- /* digit, so accumulate it. */
|
|||
|
|
- mantis = 10 * mantis + *here - '0';
|
|||
|
|
- here++;
|
|||
|
|
- }
|
|||
|
|
-
|
|||
|
|
- if (*here == '\0') {
|
|||
|
|
- /* reached the end of token - done. */
|
|||
|
|
- if (gobble) {
|
|||
|
|
- FREE(token);
|
|||
|
|
- } else {
|
|||
|
|
- *line = here;
|
|||
|
|
+ /* Parse the numeric magnitude with strtod so the result is the
|
|||
|
|
+ * correctly-rounded IEEE-754 value. This matches formula()'s
|
|||
|
|
+ * fetchnumber() (xpressn.c, sscanf "%lG") to the bit and removes the
|
|||
|
|
+ * 1-ULP drift the old digit-by-digit accumulator + pow(10, expo)
|
|||
|
|
+ * produced for long mantissas. `here` is positioned just past any
|
|||
|
|
+ * leading sign, on a digit or '.', so strtod sees the unsigned
|
|||
|
|
+ * magnitude and `sign` is applied separately. A `:` (ternary) or any
|
|||
|
|
+ * other non-numeric character naturally terminates strtod, preserving
|
|||
|
|
+ * the old early-out behaviour. */
|
|||
|
|
+ {
|
|||
|
|
+ char *numend;
|
|||
|
|
+ double value = strtod(here, &numend);
|
|||
|
|
+
|
|||
|
|
+ /* Fortran-style 'D'/'d' exponent (e.g. 1.5D3) — strtod stops at
|
|||
|
|
+ * the 'D', so finish the exponent by hand to keep legacy decks
|
|||
|
|
+ * working (the old code accepted D/d as an exponent marker). */
|
|||
|
|
+ if (numend != here && (*numend == 'D' || *numend == 'd')) {
|
|||
|
|
+ char *expend;
|
|||
|
|
+ long fexp = strtol(numend + 1, &expend, 10);
|
|||
|
|
+ if (expend != numend + 1) {
|
|||
|
|
+ value *= pow(10.0, (double) fexp);
|
|||
|
|
+ numend = expend;
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
- return ((double) mantis * sign);
|
|||
|
|
- }
|
|||
|
|
|
|||
|
|
- if (*here == ':') {
|
|||
|
|
- /* ':' is no longer used for subcircuit node numbering
|
|||
|
|
- but is part of ternary function a?b:c
|
|||
|
|
- FIXME : subcircuit models still use ':' for model numbering
|
|||
|
|
- Will this hurt somewhere? */
|
|||
|
|
- if (gobble) {
|
|||
|
|
- FREE(token);
|
|||
|
|
- } else {
|
|||
|
|
- *line = here;
|
|||
|
|
- }
|
|||
|
|
- return ((double) mantis * sign);
|
|||
|
|
- }
|
|||
|
|
+ here = numend; /* at the scale-factor suffix, or token end */
|
|||
|
|
|
|||
|
|
- /* after decimal point! */
|
|||
|
|
- if (*here == '.') {
|
|||
|
|
- /* found a decimal point! */
|
|||
|
|
- here++; /* skip to next character */
|
|||
|
|
-
|
|||
|
|
- if (*here == '\0') {
|
|||
|
|
- /* number ends in the decimal point */
|
|||
|
|
- if (gobble) {
|
|||
|
|
- FREE(token);
|
|||
|
|
+ /* Scale factor (alphabetic suffix). As before, the suffix
|
|||
|
|
+ * character is NOT consumed from the line: `here` is left pointing
|
|||
|
|
+ * at it. */
|
|||
|
|
+ double scale = 1.0;
|
|||
|
|
+ switch (*here) {
|
|||
|
|
+ case 't': case 'T': scale = 1.0e12; break;
|
|||
|
|
+ case 'g': case 'G': scale = 1.0e9; break;
|
|||
|
|
+ case 'k': case 'K': scale = 1.0e3; break;
|
|||
|
|
+ case 'u': case 'U': scale = 1.0e-6; break;
|
|||
|
|
+ case 'n': case 'N': scale = 1.0e-9; break;
|
|||
|
|
+ case 'p': case 'P': scale = 1.0e-12; break;
|
|||
|
|
+ case 'f': case 'F': scale = 1.0e-15; break;
|
|||
|
|
+ case 'a': case 'A': scale = 1.0e-18; break;
|
|||
|
|
+ case 'm': case 'M':
|
|||
|
|
+ if (((here[1] == 'E') || (here[1] == 'e')) &&
|
|||
|
|
+ ((here[2] == 'G') || (here[2] == 'g'))) {
|
|||
|
|
+ scale = 1.0e6; /* Meg */
|
|||
|
|
+ } else if (((here[1] == 'I') || (here[1] == 'i')) &&
|
|||
|
|
+ ((here[2] == 'L') || (here[2] == 'l'))) {
|
|||
|
|
+ scale = 25.4e-6; /* Mil */
|
|||
|
|
} else {
|
|||
|
|
- *line = here;
|
|||
|
|
+ scale = 1.0e-3; /* m, milli */
|
|||
|
|
}
|
|||
|
|
- return ((double) mantis * sign);
|
|||
|
|
+ break;
|
|||
|
|
+ default:
|
|||
|
|
+ break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- while (isdigit_c(*here)) {
|
|||
|
|
- /* digit, so accumulate it. */
|
|||
|
|
- mantis = 10 * mantis + *here - '0';
|
|||
|
|
- expo1 = expo1 - 1;
|
|||
|
|
- here++;
|
|||
|
|
- }
|
|||
|
|
- }
|
|||
|
|
-
|
|||
|
|
- /* now look for "E","e",etc to indicate an exponent */
|
|||
|
|
- if ((*here == 'E') || (*here == 'e') || (*here == 'D') || (*here == 'd')) {
|
|||
|
|
-
|
|||
|
|
- /* have an exponent, so skip the e */
|
|||
|
|
- here++;
|
|||
|
|
-
|
|||
|
|
- /* now look for exponent sign */
|
|||
|
|
- if (*here == '+')
|
|||
|
|
- here++; /* just skip + */
|
|||
|
|
- else if (*here == '-') {
|
|||
|
|
- here++; /* skip over minus sign */
|
|||
|
|
- expsgn = -1; /* and make a negative exponent */
|
|||
|
|
- /* now look for the digits of the exponent */
|
|||
|
|
- }
|
|||
|
|
-
|
|||
|
|
- while (isdigit_c(*here)) {
|
|||
|
|
- expo2 = 10 * expo2 + *here - '0';
|
|||
|
|
- here++;
|
|||
|
|
- }
|
|||
|
|
- }
|
|||
|
|
-
|
|||
|
|
- /* now we have all of the numeric part of the number, time to
|
|||
|
|
- * look for the scale factor (alphabetic)
|
|||
|
|
- */
|
|||
|
|
- switch (*here) {
|
|||
|
|
- case 't':
|
|||
|
|
- case 'T':
|
|||
|
|
- expo1 = expo1 + 12;
|
|||
|
|
- break;
|
|||
|
|
- case 'g':
|
|||
|
|
- case 'G':
|
|||
|
|
- expo1 = expo1 + 9;
|
|||
|
|
- break;
|
|||
|
|
- case 'k':
|
|||
|
|
- case 'K':
|
|||
|
|
- expo1 = expo1 + 3;
|
|||
|
|
- break;
|
|||
|
|
- case 'u':
|
|||
|
|
- case 'U':
|
|||
|
|
- expo1 = expo1 - 6;
|
|||
|
|
- break;
|
|||
|
|
- case 'n':
|
|||
|
|
- case 'N':
|
|||
|
|
- expo1 = expo1 - 9;
|
|||
|
|
- break;
|
|||
|
|
- case 'p':
|
|||
|
|
- case 'P':
|
|||
|
|
- expo1 = expo1 - 12;
|
|||
|
|
- break;
|
|||
|
|
- case 'f':
|
|||
|
|
- case 'F':
|
|||
|
|
- expo1 = expo1 - 15;
|
|||
|
|
- break;
|
|||
|
|
- case 'a':
|
|||
|
|
- case 'A':
|
|||
|
|
- expo1 = expo1 - 18;
|
|||
|
|
- break;
|
|||
|
|
- case 'm':
|
|||
|
|
- case 'M':
|
|||
|
|
- if (((here[1] == 'E') || (here[1] == 'e')) &&
|
|||
|
|
- ((here[2] == 'G') || (here[2] == 'g')))
|
|||
|
|
- {
|
|||
|
|
- expo1 = expo1 + 6; /* Meg */
|
|||
|
|
- } else if (((here[1] == 'I') || (here[1] == 'i')) &&
|
|||
|
|
- ((here[2] == 'L') || (here[2] == 'l')))
|
|||
|
|
- {
|
|||
|
|
- expo1 = expo1 - 6;
|
|||
|
|
- mantis *= 25.4; /* Mil */
|
|||
|
|
+ if (gobble) {
|
|||
|
|
+ FREE(token);
|
|||
|
|
} else {
|
|||
|
|
- expo1 = expo1 - 3; /* m, milli */
|
|||
|
|
+ *line = here;
|
|||
|
|
}
|
|||
|
|
- break;
|
|||
|
|
- default:
|
|||
|
|
- break;
|
|||
|
|
- }
|
|||
|
|
|
|||
|
|
- if (gobble) {
|
|||
|
|
- FREE(token);
|
|||
|
|
- } else {
|
|||
|
|
- *line = here;
|
|||
|
|
+ return sign * value * scale;
|
|||
|
|
}
|
|||
|
|
-
|
|||
|
|
- return (sign * mantis *
|
|||
|
|
- pow(10.0, (double) (expo1 + expsgn * expo2)));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[21] src/spicelib/parser/inpgmod.c
|
|||
|
|
pre-existing lines changed: 20 (+164 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
Overhauls .model handling and W/L bin selection to support OSDI models
|
|||
|
|
(BSIM-CMG/BSIM-BULK). OSDI type-name extraction now stops at '(' (as well as
|
|||
|
|
'='/','/whitespace) so a no-space card like ".model m slew_probe(max_pos=...)"
|
|||
|
|
no longer swallows the first parameter as the type; comment explains
|
|||
|
|
INPgetNetTok doesn't treat '(' as a terminator. For OSDI nmos/pmos, a
|
|||
|
|
pmos/psoi model auto-injects TYPE=-1 before params parse (PDK can still
|
|||
|
|
override), since commercial simulators handle polarity implicitly.
|
|||
|
|
Unrecognized OSDI model params are silently skipped (not warned) because
|
|||
|
|
HSPICE-converted PDK cards carry simulator-specific params (tmemod, psatbmod,
|
|||
|
|
b0). Bin selection (INPgetModBin): now requires only L on the instance line (W
|
|||
|
|
optional, defaulting to 0) so FinFET PDKs that bin on lmin/lmax/nfin (no W)
|
|||
|
|
work. It accepts OSDI models as binnable (detected via dev->registry_entry)
|
|||
|
|
alongside the classic BSIM/HiSIM families, skips candidates with illegal type
|
|||
|
|
rather than aborting, treats wmin/wmax as optional (has_w_range), and compares
|
|||
|
|
per-finger W (w/nf) and L against the bin ranges. Comments emphasize: 'm'
|
|||
|
|
(parallel-instance multiplier) is deliberately NOT divided out (doing so
|
|||
|
|
pushed large multi-instance power devices below every bin), and W/L here are
|
|||
|
|
already EFFECTIVE/post-shrink (shrink applied upstream via subckt scale), so
|
|||
|
|
no shrink factor is applied. On a bin match it records the bin's (lmin,lmax)
|
|||
|
|
via osdi_defer_record_bin_range for OSDI deferred-eval. Adds a new second-pass
|
|||
|
|
nearest-bin fallback for OSDI: if no exact width match, pick the L-matching
|
|||
|
|
bin whose wmin/wmax is closest to the device W and warn "device W... is
|
|||
|
|
outside all bin width ranges; using nearest bin", mirroring HSPICE/Spectre.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -115,13 +116,39 @@
|
|||
|
|
INPgetNetTok(&line, &parm, 1); /* throw away 'modname' */
|
|||
|
|
tfree(parm);
|
|||
|
|
|
|||
|
|
+ bool is_osdi = false;
|
|||
|
|
#ifdef OSDI
|
|||
|
|
/* osdi models don't accept their device type as an argument */
|
|||
|
|
- bool is_osdi = false;
|
|||
|
|
- if (device->registry_entry){
|
|||
|
|
- INPgetNetTok(&line, &parm, 1); /* throw away osdi */
|
|||
|
|
+ if (device->registry_entry) {
|
|||
|
|
+ /* Skip the OSDI device type (the module name). We must stop at '('
|
|||
|
|
+ * as well as whitespace: a no-space card like
|
|||
|
|
+ * .model m slew_probe(max_pos=500, max_neg=-500)
|
|||
|
|
+ * is standard SPICE syntax, but INPgetNetTok does NOT treat '(' as a
|
|||
|
|
+ * token terminator, so it would grab "slew_probe(max_pos" as the
|
|||
|
|
+ * "type" and silently swallow the FIRST model parameter. Extract just
|
|||
|
|
+ * the type name, stopping at the first '(', '=', ',' or whitespace, and
|
|||
|
|
+ * leave `line` pointing at the parameter list. */
|
|||
|
|
+ while (*line == ' ' || *line == '\t')
|
|||
|
|
+ line++;
|
|||
|
|
+ char *type_start = line;
|
|||
|
|
+ while (*line && *line != ' ' && *line != '\t' && *line != '(' &&
|
|||
|
|
+ *line != '=' && *line != ',')
|
|||
|
|
+ line++;
|
|||
|
|
+ parm = copy_substring(type_start, line);
|
|||
|
|
+ bool is_pmos = (strcasecmp(parm, "pmos") == 0 || strcasecmp(parm, "psoi") == 0);
|
|||
|
|
tfree(parm);
|
|||
|
|
is_osdi = true;
|
|||
|
|
+ /* Commercial simulators handle nmos/pmos polarity implicitly. For OSDI
|
|||
|
|
+ * models with a 'type' parameter (e.g. BSIM-BULK TYPE=1/-1), inject -1
|
|||
|
|
+ * before card params are parsed so the PDK can still override explicitly. */
|
|||
|
|
+ if (is_pmos) {
|
|||
|
|
+ IFparm *type_parm = find_model_parameter("type", device);
|
|||
|
|
+ if (type_parm && (type_parm->dataType & IF_INTEGER)) {
|
|||
|
|
+ IFvalue type_val;
|
|||
|
|
+ type_val.iValue = -1;
|
|||
|
|
+ ft_sim->setModelParm(ckt, modtmp->INPmodfast, type_parm->id, &type_val, NULL);
|
|||
|
|
+ }
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
@@ -136,8 +163,8 @@
|
|||
|
|
|
|||
|
|
if (p) {
|
|||
|
|
#ifdef OSDI
|
|||
|
|
- if (is_osdi && (p->dataType & IF_VECTOR)){
|
|||
|
|
- // we need to get rid if the leading [ in order to make sure
|
|||
|
|
+ if (is_osdi && (p->dataType & IF_VECTOR)){
|
|||
|
|
+ // we need to get rid if the leading [ in order to make sure
|
|||
|
|
// that INPgetValue can parse the value properly
|
|||
|
|
// This is because, unlike other SPICEDev, OSDI models receive
|
|||
|
|
// array params in the syntax (param_name=[...])
|
|||
|
|
@@ -184,7 +211,11 @@
|
|||
|
|
perror("strtod");
|
|||
|
|
controlled_exit(EXIT_FAILURE);
|
|||
|
|
}
|
|||
|
|
- if (endptr == parm) /* it was no number - it is really a string */
|
|||
|
|
+ /* For OSDI models, PDK cards converted from HSPICE often contain
|
|||
|
|
+ * simulator-specific parameters (tmemod, psatbmod, b0, etc.) that
|
|||
|
|
+ * the public OSDI model binary does not implement. Silently skip
|
|||
|
|
+ * them rather than flooding the output with "Model issue" lines. */
|
|||
|
|
+ if (endptr == parm && !is_osdi)
|
|||
|
|
err = INPerrCat(err,
|
|||
|
|
tprintf("unrecognized parameter (%s) - ignored",
|
|||
|
|
parm));
|
|||
|
|
@@ -274,9 +305,22 @@
|
|||
|
|
|
|||
|
|
*model = NULL;
|
|||
|
|
|
|||
|
|
- /* read W and L. If not on the instance line, leave */
|
|||
|
|
- if (!parse_line(line, instance_tokens, 2, parse_values, parse_found))
|
|||
|
|
- return NULL;
|
|||
|
|
+ /* Read L (required) and W (optional). FinFET PDKs (BCD 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
|
|||
|
|
+ * soon as W was missing, which prevented bin selection for any
|
|||
|
|
+ * FinFET model. Now: require only L, and if W is absent set it
|
|||
|
|
+ * to 0 (any model with a real `wmin/wmax` range still gets a
|
|||
|
|
+ * sensible has_w_range==true check and rejects this instance,
|
|||
|
|
+ * which matches HSPICE's behaviour of "instance W=0 ≠ any
|
|||
|
|
+ * wmin/wmax range starting above 0"). */
|
|||
|
|
+ if (!parse_line(line, instance_tokens, 1, parse_values, parse_found))
|
|||
|
|
+ return NULL; /* No `l=` either — really not a MOSFET-shape line. */
|
|||
|
|
+
|
|||
|
|
+ /* Now try to read W; if absent, w stays at parse_values[1]=0. */
|
|||
|
|
+ parse_values[1] = 0.;
|
|||
|
|
+ parse_line(line, instance_tokens, 2, parse_values, parse_found);
|
|||
|
|
|
|||
|
|
/* This is for reading nf. If nf is not available, set to 1 if in HSPICE or Spectre compatibility mode */
|
|||
|
|
if (!parse_line(line, instance_tokens, 3, parse_values, parse_found)) {
|
|||
|
|
@@ -301,13 +345,30 @@
|
|||
|
|
l = parse_values[0] * scale;
|
|||
|
|
w = parse_values[1] / parse_values[2] * scale;
|
|||
|
|
|
|||
|
|
+ /* 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. Commercial
|
|||
|
|
+ * Foundries 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. */
|
|||
|
|
+
|
|||
|
|
for (modtmp = modtab; modtmp; modtmp = modtmp->INPnextModel) {
|
|||
|
|
|
|||
|
|
if (model_name_match(name, modtmp->INPmodName) < 2)
|
|||
|
|
continue;
|
|||
|
|
|
|||
|
|
- /* skip if not binnable */
|
|||
|
|
- if (modtmp->INPmodType != INPtypelook("BSIM3") &&
|
|||
|
|
+ /* if illegal device type, skip this candidate rather than aborting */
|
|||
|
|
+ if (modtmp->INPmodType < 0)
|
|||
|
|
+ continue;
|
|||
|
|
+
|
|||
|
|
+ /* skip if not binnable: accept OSDI models (detected via registry_entry)
|
|||
|
|
+ * as well as the classic BSIM/HiSIM families that have always been supported */
|
|||
|
|
+ IFdevice *dev = ft_sim->devices[modtmp->INPmodType];
|
|||
|
|
+ bool is_osdi = (dev->registry_entry != NULL);
|
|||
|
|
+ if (!is_osdi &&
|
|||
|
|
+ modtmp->INPmodType != INPtypelook("BSIM3") &&
|
|||
|
|
modtmp->INPmodType != INPtypelook("BSIM3v32") &&
|
|||
|
|
modtmp->INPmodType != INPtypelook("BSIM3v0") &&
|
|||
|
|
modtmp->INPmodType != INPtypelook("BSIM3v1") &&
|
|||
|
|
@@ -322,19 +383,38 @@
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
- /* if illegal device type */
|
|||
|
|
- if (modtmp->INPmodType < 0) {
|
|||
|
|
- *model = NULL;
|
|||
|
|
- return tprintf("Unknown device type for model %s\n", name);
|
|||
|
|
- }
|
|||
|
|
+ /* parse lmin/lmax from the model line; wmin/wmax are optional.
|
|||
|
|
+ * parse_line returns FALSE if any requested token is absent, so call
|
|||
|
|
+ * it unconditionally and inspect parse_found[] individually. */
|
|||
|
|
+ parse_line(modtmp->INPmodLine->line, model_tokens, 4, parse_values, parse_found);
|
|||
|
|
|
|||
|
|
- if (!parse_line(modtmp->INPmodLine->line, model_tokens, 4, parse_values, parse_found))
|
|||
|
|
- continue;
|
|||
|
|
+ if (!parse_found[0] || !parse_found[1])
|
|||
|
|
+ continue; /* lmin/lmax are required for bin selection */
|
|||
|
|
|
|||
|
|
lmin = parse_values[0]; lmax = parse_values[1];
|
|||
|
|
- wmin = parse_values[2]; wmax = parse_values[3];
|
|||
|
|
|
|||
|
|
- if (in_range(l, lmin, lmax) && in_range(w, wmin, wmax)) {
|
|||
|
|
+ bool has_w_range = parse_found[2] && parse_found[3];
|
|||
|
|
+ if (has_w_range) {
|
|||
|
|
+ wmin = parse_values[2]; wmax = parse_values[3];
|
|||
|
|
+ }
|
|||
|
|
+
|
|||
|
|
+ /* Compare the per-finger W (already w/nf above) and L against
|
|||
|
|
+ * the bin's wmin/wmax and lmin/lmax. Foundry .lib files
|
|||
|
|
+ * author bin boundaries in POST-SHRINK
|
|||
|
|
+ * (EFFECTIVE) dimensions: e.g. 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
|
|||
|
|
+ * the W/L parsed here are already EFFECTIVE — no shrink factor is
|
|||
|
|
+ * applied at this point.
|
|||
|
|
+ *
|
|||
|
|
+ * `m` is intentionally NOT in the calculation — it is a
|
|||
|
|
+ * parallel-instance multiplier (the whole device is replicated
|
|||
|
|
+ * m times), so per-finger geometry is unchanged by it. */
|
|||
|
|
+ double w_cmp = w;
|
|||
|
|
+ double l_cmp = l;
|
|||
|
|
+
|
|||
|
|
+ if (in_range(l_cmp, lmin, lmax) && (!has_w_range || in_range(w_cmp, wmin, wmax))) {
|
|||
|
|
/* create unless model is already defined */
|
|||
|
|
if (!modtmp->INPmodfast) {
|
|||
|
|
int error = create_model(ckt, modtmp, tab);
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[22] visualc/vngspice-fftw.vcxproj
|
|||
|
|
pre-existing lines changed: 18 (+33 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
MSVC project file for the FFTW-enabled ngspice build. Changes wire in the
|
|||
|
|
libsndfile/libsamplerate audio libraries and a new source file across the
|
|||
|
|
Debug and Release configurations: added
|
|||
|
|
`..\..\libsamplerate\include;..\..\libsndfile\include` to
|
|||
|
|
AdditionalIncludeDirectories; added `sndfile.lib;samplerate.lib` to
|
|||
|
|
AdditionalDependencies; added `../../libsamplerate/lib;../../libsndfile/lib`
|
|||
|
|
to AdditionalLibraryDirectories; and added PostBuildEvent commands copying
|
|||
|
|
`sndfile.dll` and `samplerate.dll` into the output dir. It also enables
|
|||
|
|
`/openmp:llvm` and, in the item lists, adds a new source file
|
|||
|
|
`..\src\spicelib\devices\vsrc\vsjack.c` (JACK audio voltage source) plus the
|
|||
|
|
`com_fileio.h`/`com_fileio.c` frontend file entries.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -305,7 +305,7 @@
|
|||
|
|
</Midl>
|
|||
|
|
<ClCompile>
|
|||
|
|
<Optimization>Disabled</Optimization>
|
|||
|
|
- <AdditionalIncludeDirectories>..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
+ <AdditionalIncludeDirectories>..\..\libsamplerate\include;..\..\libsndfile\include;..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;NGDEBUG;CONFIG64;HAVE_LIBFFTW3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|||
|
|
<MinimalRebuild>false</MinimalRebuild>
|
|||
|
|
<ExceptionHandling>
|
|||
|
|
@@ -323,7 +323,7 @@
|
|||
|
|
<AdditionalOptions>/openmp:llvm %(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
</ClCompile>
|
|||
|
|
<Link>
|
|||
|
|
- <AdditionalDependencies>psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
+ <AdditionalDependencies>sndfile.lib;samplerate.lib;psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|||
|
|
<SubSystem>Windows</SubSystem>
|
|||
|
|
<HeapReserveSize>0</HeapReserveSize>
|
|||
|
|
@@ -335,10 +335,12 @@
|
|||
|
|
</DataExecutionPrevention>
|
|||
|
|
<TargetMachine>MachineX64</TargetMachine>
|
|||
|
|
<LargeAddressAware>true</LargeAddressAware>
|
|||
|
|
- <AdditionalLibraryDirectories>KLU\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
+ <AdditionalLibraryDirectories>../../libsamplerate/lib;../../libsndfile/lib;KLU\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
</Link>
|
|||
|
|
<PostBuildEvent>
|
|||
|
|
<Command>
|
|||
|
|
+ copy /y "..\..\libsndfile\bin\sndfile.dll" "$(OutDir)"
|
|||
|
|
+ copy /y "..\..\libsamplerate\bin\samplerate.dll" "$(OutDir)"
|
|||
|
|
copy /y "..\..\fftw-3.3-dll64\libfftw3-3.dll" "$(OutDir)"
|
|||
|
|
make-install-vngspiced.bat $(OutDir) fftw 64
|
|||
|
|
</Command>
|
|||
|
|
@@ -358,7 +360,7 @@
|
|||
|
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|||
|
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
|||
|
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|||
|
|
- <AdditionalIncludeDirectories>..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
+ <AdditionalIncludeDirectories>..\..\libsamplerate\include;..\..\libsndfile\include;..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;CONFIG64;HAVE_LIBFFTW3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|||
|
|
<MinimalRebuild>false</MinimalRebuild>
|
|||
|
|
<ExceptionHandling>
|
|||
|
|
@@ -376,7 +378,7 @@
|
|||
|
|
<AdditionalOptions>/openmp:llvm %(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
</ClCompile>
|
|||
|
|
<Link>
|
|||
|
|
- <AdditionalDependencies>psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
+ <AdditionalDependencies>sndfile.lib;samplerate.lib;psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|||
|
|
<SubSystem>Windows</SubSystem>
|
|||
|
|
<HeapReserveSize>0</HeapReserveSize>
|
|||
|
|
@@ -393,10 +395,13 @@
|
|||
|
|
</DataExecutionPrevention>
|
|||
|
|
<TargetMachine>MachineX64</TargetMachine>
|
|||
|
|
<LargeAddressAware>true</LargeAddressAware>
|
|||
|
|
- <AdditionalLibraryDirectories>KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
+ <AdditionalLibraryDirectories>../../libsamplerate/lib;../../libsndfile/lib;KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
</Link>
|
|||
|
|
<PostBuildEvent>
|
|||
|
|
<Command>
|
|||
|
|
+
|
|||
|
|
+ copy /y "..\..\libsndfile\bin\sndfile.dll" "$(OutDir)"
|
|||
|
|
+ copy /y "..\..\libsamplerate\bin\samplerate.dll" "$(OutDir)"
|
|||
|
|
copy /y "..\..\fftw-3.3-dll64\libfftw3-3.dll" "$(OutDir)"
|
|||
|
|
make-install-vngspice.bat $(OutDir) fftw 64
|
|||
|
|
</Command>
|
|||
|
|
@@ -511,7 +516,7 @@
|
|||
|
|
</Midl>
|
|||
|
|
<ClCompile>
|
|||
|
|
<Optimization>Disabled</Optimization>
|
|||
|
|
- <AdditionalIncludeDirectories>..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
+ <AdditionalIncludeDirectories>..\..\libsamplerate\include;..\..\libsndfile\include;..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;NGDEBUG;CONSOLE;CONFIG64;HAVE_LIBFFTW3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|||
|
|
<MinimalRebuild>false</MinimalRebuild>
|
|||
|
|
<ExceptionHandling>
|
|||
|
|
@@ -529,7 +534,7 @@
|
|||
|
|
<AdditionalOptions>/openmp:llvm %(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
</ClCompile>
|
|||
|
|
<Link>
|
|||
|
|
- <AdditionalDependencies>psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
+ <AdditionalDependencies>sndfile.lib;samplerate.lib;psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|||
|
|
<SubSystem>Console</SubSystem>
|
|||
|
|
<HeapReserveSize>0</HeapReserveSize>
|
|||
|
|
@@ -541,10 +546,12 @@
|
|||
|
|
</DataExecutionPrevention>
|
|||
|
|
<TargetMachine>MachineX64</TargetMachine>
|
|||
|
|
<LargeAddressAware>true</LargeAddressAware>
|
|||
|
|
- <AdditionalLibraryDirectories>KLU\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
+ <AdditionalLibraryDirectories>../../libsamplerate/lib;../../libsndfile/lib;KLU\x64\Debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
</Link>
|
|||
|
|
<PostBuildEvent>
|
|||
|
|
<Command>
|
|||
|
|
+ copy /y "..\..\libsndfile\bin\sndfile.dll" "$(OutDir)"
|
|||
|
|
+ copy /y "..\..\libsamplerate\bin\samplerate.dll" "$(OutDir)"
|
|||
|
|
copy /y "..\..\fftw-3.3-dll64\libfftw3-3.dll" "$(OutDir)"
|
|||
|
|
make-install-vngspiced.bat $(OutDir) fftw 64
|
|||
|
|
</Command>
|
|||
|
|
@@ -564,7 +571,7 @@
|
|||
|
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|||
|
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
|||
|
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|||
|
|
- <AdditionalIncludeDirectories>..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
+ <AdditionalIncludeDirectories>..\..\libsamplerate\include;..\..\libsndfile\include;..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;CONSOLE;CONFIG64;HAVE_LIBFFTW3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|||
|
|
<MinimalRebuild>false</MinimalRebuild>
|
|||
|
|
<ExceptionHandling>
|
|||
|
|
@@ -582,7 +589,7 @@
|
|||
|
|
<AdditionalOptions>/openmp:llvm %(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
</ClCompile>
|
|||
|
|
<Link>
|
|||
|
|
- <AdditionalDependencies>psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
+ <AdditionalDependencies>sndfile.lib;samplerate.lib;psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|||
|
|
<SubSystem>Console</SubSystem>
|
|||
|
|
<HeapReserveSize>0</HeapReserveSize>
|
|||
|
|
@@ -597,7 +604,7 @@
|
|||
|
|
</DataExecutionPrevention>
|
|||
|
|
<TargetMachine>MachineX64</TargetMachine>
|
|||
|
|
<LargeAddressAware>true</LargeAddressAware>
|
|||
|
|
- <AdditionalLibraryDirectories>KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
+ <AdditionalLibraryDirectories>../../libsamplerate/lib;../../libsndfile/lib;KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
</Link>
|
|||
|
|
<PostBuildEvent>
|
|||
|
|
<Command>
|
|||
|
|
@@ -673,7 +680,7 @@
|
|||
|
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|||
|
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
|||
|
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|||
|
|
- <AdditionalIncludeDirectories>..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
+ <AdditionalIncludeDirectories>..\..\libsamplerate\include;..\..\libsndfile\include;..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;USE_OMP;CONFIG64;HAVE_LIBFFTW3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|||
|
|
<MinimalRebuild>false</MinimalRebuild>
|
|||
|
|
<ExceptionHandling>
|
|||
|
|
@@ -693,7 +700,7 @@
|
|||
|
|
<AdditionalOptions>/openmp:llvm %(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
</ClCompile>
|
|||
|
|
<Link>
|
|||
|
|
- <AdditionalDependencies>psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
+ <AdditionalDependencies>sndfile.lib;samplerate.lib;psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|||
|
|
<SubSystem>Windows</SubSystem>
|
|||
|
|
<HeapReserveSize>0</HeapReserveSize>
|
|||
|
|
@@ -710,10 +717,12 @@
|
|||
|
|
</DataExecutionPrevention>
|
|||
|
|
<TargetMachine>MachineX64</TargetMachine>
|
|||
|
|
<LargeAddressAware>true</LargeAddressAware>
|
|||
|
|
- <AdditionalLibraryDirectories>KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
+ <AdditionalLibraryDirectories>../../libsamplerate/lib;../../libsndfile/lib;KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
</Link>
|
|||
|
|
<PostBuildEvent>
|
|||
|
|
<Command>
|
|||
|
|
+ copy /y "..\..\libsndfile\bin\sndfile.dll" "$(OutDir)"
|
|||
|
|
+ copy /y "..\..\libsamplerate\bin\samplerate.dll" "$(OutDir)"
|
|||
|
|
copy /y "..\..\fftw-3.3-dll64\libfftw3-3.dll" "$(OutDir)"
|
|||
|
|
make-install-vngspice.bat $(OutDir) fftw 64
|
|||
|
|
</Command>
|
|||
|
|
@@ -785,7 +794,7 @@
|
|||
|
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|||
|
|
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
|||
|
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|||
|
|
- <AdditionalIncludeDirectories>..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
+ <AdditionalIncludeDirectories>..\..\libsamplerate\include;..\..\libsndfile\include;..\src\maths\poly;..\src\frontend;..\src\spicelib\devices;tmp-bison;src\include;..\src\spicelib\parser;..\src\include;..\src\include\cppduals;.;..\..\fftw-3.3-dll64;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|||
|
|
<PreprocessorDefinitions>_CRT_SECURE_NO_DEPRECATE;SIMULATOR;XSPICE;CONSOLE;CONFIG64;USE_OMP;HAVE_LIBFFTW3;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|||
|
|
<MinimalRebuild>false</MinimalRebuild>
|
|||
|
|
<ExceptionHandling>
|
|||
|
|
@@ -805,7 +814,7 @@
|
|||
|
|
<AdditionalOptions>/openmp:llvm %(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
</ClCompile>
|
|||
|
|
<Link>
|
|||
|
|
- <AdditionalDependencies>psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
+ <AdditionalDependencies>sndfile.lib;samplerate.lib;psapi.lib;KLU_COMPLEX.lib;$(IntDir)libfftw3-3.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|||
|
|
<SubSystem>Console</SubSystem>
|
|||
|
|
<HeapReserveSize>0</HeapReserveSize>
|
|||
|
|
@@ -820,10 +829,12 @@
|
|||
|
|
</DataExecutionPrevention>
|
|||
|
|
<TargetMachine>MachineX64</TargetMachine>
|
|||
|
|
<LargeAddressAware>true</LargeAddressAware>
|
|||
|
|
- <AdditionalLibraryDirectories>KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
+ <AdditionalLibraryDirectories>../../libsamplerate/lib;../../libsndfile/lib;KLU\x64\Release;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
</Link>
|
|||
|
|
<PostBuildEvent>
|
|||
|
|
<Command>
|
|||
|
|
+ copy /y "..\..\libsndfile\bin\sndfile.dll" "$(OutDir)"
|
|||
|
|
+ copy /y "..\..\libsamplerate\bin\samplerate.dll" "$(OutDir)"
|
|||
|
|
copy /y "..\..\fftw-3.3-dll64\libfftw3-3.dll" "$(OutDir)"
|
|||
|
|
make-install-vngspice.bat $(OutDir) fftw 64
|
|||
|
|
</Command>
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
[23] visualc/vngspice.vcxproj
|
|||
|
|
pre-existing lines changed: 4 (+15 additive OSDI lines in this file; standalone blocks omitted below, full diff in ng_diff.txt)
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
WHY:
|
|||
|
|
MSVC project file for the standard (non-FFTW) ngspice build, changed the same
|
|||
|
|
way as the FFTW variant. Across its multiple configurations it adds
|
|||
|
|
PostBuildEvent `copy` commands for `sndfile.dll` and `samplerate.dll` into the
|
|||
|
|
OutDir, adds `sndfile.lib;samplerate.lib` to link dependencies (with the
|
|||
|
|
corresponding libsamplerate/libsndfile lib directories), and turns on
|
|||
|
|
`/openmp:llvm` in one config's AdditionalOptions. In the item groups it adds
|
|||
|
|
the `..\src\frontend\com_fileio.h` include and `..\src\frontend\com_fileio.c`
|
|||
|
|
compile entries (repositioned in the list). These support the audio file I/O
|
|||
|
|
features.
|
|||
|
|
|
|||
|
|
CHANGE:
|
|||
|
|
@@ -383,7 +385,7 @@
|
|||
|
|
<CompileAs>Default</CompileAs>
|
|||
|
|
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
|||
|
|
<LanguageStandard>stdcpp14</LanguageStandard>
|
|||
|
|
- <AdditionalOptions>%(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
+ <AdditionalOptions>/openmp:llvm %(AdditionalOptions)</AdditionalOptions>
|
|||
|
|
</ClCompile>
|
|||
|
|
<Link>
|
|||
|
|
<AdditionalDependencies>sndfile.lib;samplerate.lib;psapi.lib;KLU_COMPLEX.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
|||
|
|
@@ -779,7 +787,8 @@
|
|||
|
|
<AdditionalLibraryDirectories>../../libsamplerate/lib;../../libsndfile/lib;KLU/Release/;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|||
|
|
</Link>
|
|||
|
|
<PostBuildEvent>
|
|||
|
|
- <Command>make-install-vngspice.bat $(OutDir)</Command>
|
|||
|
|
+ <Command>
|
|||
|
|
+ make-install-vngspice.bat $(OutDir)</Command>
|
|||
|
|
</PostBuildEvent>
|
|||
|
|
<Manifest>
|
|||
|
|
<AdditionalManifestFiles>$(ProjectDir)ngspice-x86.exe.manifest</AdditionalManifestFiles>
|
|||
|
|
@@ -885,7 +897,6 @@
|
|||
|
|
<ClInclude Include="..\src\frontend\com_shift.h" />
|
|||
|
|
<ClInclude Include="..\src\frontend\com_state.h" />
|
|||
|
|
<ClInclude Include="..\src\frontend\com_strcmp.h" />
|
|||
|
|
- <ClInclude Include="..\src\frontend\com_fileio.h" />
|
|||
|
|
<ClInclude Include="..\src\frontend\com_unset.h" />
|
|||
|
|
<ClInclude Include="..\src\frontend\com_wr_ic.h" />
|
|||
|
|
<ClInclude Include="..\src\frontend\control.h" />
|
|||
|
|
@@ -1498,7 +1510,6 @@
|
|||
|
|
<ClCompile Include="..\src\frontend\com_shift.c" />
|
|||
|
|
<ClCompile Include="..\src\frontend\com_state.c" />
|
|||
|
|
<ClCompile Include="..\src\frontend\com_strcmp.c" />
|
|||
|
|
- <ClCompile Include="..\src\frontend\com_fileio.c" />
|
|||
|
|
<ClCompile Include="..\src\frontend\com_sysinfo.c" />
|
|||
|
|
<ClCompile Include="..\src\frontend\com_unset.c" />
|
|||
|
|
<ClCompile Include="..\src\frontend\com_wr_ic.c" />
|
|||
|
|
|
|||
|
|
================================================================================
|
|||
|
|
APPENDIX — NEW OSDI CODE (excluded from the justification body above)
|
|||
|
|
================================================================================
|
|||
|
|
|
|||
|
|
A. New files (entire file is new OSDI code):
|
|||
|
|
- src/frontend/numparam/table_param.c (+507)
|
|||
|
|
New module implementing HSPICE/commercial-style `table_param()` for numparam
|
|||
|
|
expression evaluation — multi-dimensional parameter lookup from an external
|
|||
|
|
`.table` file with linear interpolation.
|
|||
|
|
- src/frontend/numparam/table_param.h (+71)
|
|||
|
|
Public header for the new table_param module.
|
|||
|
|
- src/include/ngspice/osdi_defer.h (+132)
|
|||
|
|
Public header for the new OSDI deferred-evaluation side table.
|
|||
|
|
- src/osdi/osdi_defer.c (+608)
|
|||
|
|
Implementation of the OSDI deferred-evaluation side table declared in
|
|||
|
|
osdi_defer.h.
|
|||
|
|
|
|||
|
|
B. Existing files that received ONLY additive OSDI code (no pre-existing line
|
|||
|
|
was changed — nothing to justify as a modification):
|
|||
|
|
- src/frontend/numparam/numparam.h (+47)
|
|||
|
|
Two new fields on `dico_t`: `const char* cardsource` (read-only, owned by the
|
|||
|
|
card; used by table_param() to resolve relative .table paths against the
|
|||
|
|
directory of the emitting .lib) and `bool suppress_errors` (when true,
|
|||
|
|
message() becomes a no-op).
|
|||
|
|
- src/frontend/numparam/spicenum.c (+94)
|
|||
|
|
Added `#include "ngspice/osdi_defer.h"` and five new functions with extensive
|
|||
|
|
comments.
|
|||
|
|
- src/frontend/subckt.h (+1)
|
|||
|
|
Added the prototype `void inp_apply_subckt_scale(struct card *deck);` so the
|
|||
|
|
new HSPICE element-scale pass in subckt.c can be called from inpcom.c before
|
|||
|
|
numparam preparation (STEP 31).
|
|||
|
|
- src/include/ngspice/cktdefs.h (+68)
|
|||
|
|
Two structs gain fields to support the OSDI convergence-rescue work.
|
|||
|
|
- src/include/ngspice/optdefs.h (+18)
|
|||
|
|
New entries are added to the options enum to expose the convergence-rescue and
|
|||
|
|
OSDI limiter machinery as `.option` settings.
|
|||
|
|
- src/include/ngspice/osdiitf.h (+12)
|
|||
|
|
The OsdiRegistryEntry struct gains three new tail-appended uint32_t fields for
|
|||
|
|
OSDI v0.5 features, each with an ABI-safety comment.
|
|||
|
|
- src/include/ngspice/smpdefs.h (+6)
|
|||
|
|
The sSMPmatrix struct gains a new member `char *gmin_skip`.
|
|||
|
|
- src/include/ngspice/trcvdefs.h (+17)
|
|||
|
|
Adds HSPICE-compatible `.param`-name sweeping to the .dc analysis.
|
|||
|
|
- src/include/ngspice/tskdefs.h (+13)
|
|||
|
|
The task struct gains new option flags and doubles mirroring the new `.option`
|
|||
|
|
settings, each commented with the keyword it backs.
|
|||
|
|
- src/maths/ni/niiter.c (+173)
|
|||
|
|
NIiter gains the bulk of the convergence-rescue logic, heavily commented.
|
|||
|
|
- src/maths/sparse/spsmp.c (+9)
|
|||
|
|
The classic sparse-matrix LoadGmin is updated to honor gmin_skip, and cleanup
|
|||
|
|
added.
|
|||
|
|
- src/osdi/osdiacld.c (+35)
|
|||
|
|
Adds AC-analysis stamping of absdelay's exact transport-delay term ("OSDI v0.5
|
|||
|
|
2C AC delay").
|
|||
|
|
- src/osdi/osdicallbacks.c (+65)
|
|||
|
|
Adds osdi_delay_read(), the absdelay exact-transport reader bound to
|
|||
|
|
SimInfo.delay_read and called by the model mid-eval to obtain x(abstime - td)
|
|||
|
|
by linear interpolation over the instance's per-site (time,value) ring.
|
|||
|
|
- src/osdi/osdidefs.h (+89)
|
|||
|
|
Greatly extends OsdiExtraInstData with per-instance scratch/state for the new
|
|||
|
|
v0.5 features, all lazily allocated on first eval and heavily commented.
|
|||
|
|
- src/osdi/osdiext.h (+1)
|
|||
|
|
Declares the new extern OSDIaccept(CKTcircuit*, GENmodel*) — the DEVaccept
|
|||
|
|
hook that records accepted-step delay inputs into the transport rings and
|
|||
|
|
commits deferred persistent state.
|
|||
|
|
- src/osdi/osdiinit.c (+1)
|
|||
|
|
Wires the new OSDIaccept function into the device info table
|
|||
|
|
(OSDIinfo->DEVaccept = OSDIaccept) so ngspice's CKTaccept calls it once per
|
|||
|
|
accepted transient step.
|
|||
|
|
- src/osdi/osdiparam.c (+4)
|
|||
|
|
Includes ngspice/fteext.h and adds a clarifying comment on the
|
|||
|
|
instance-parameter setter noting that W and L arrive ALREADY shrunk — the
|
|||
|
|
subcircuit's `scale` parameter (applied during subckt expansion) or the
|
|||
|
|
model's own dimension expressions have already multiplied the drawn geometry
|
|||
|
|
before it reaches osdi_write_param.
|
|||
|
|
- src/osdi/osdisetup.c (+212)
|
|||
|
|
Implements the deferred-expression (HSPICE-style geometry-dependent parameter)
|
|||
|
|
machinery.
|
|||
|
|
- src/spicelib/analysis/cktdojob.c (+8)
|
|||
|
|
CKTdojob() (which copies a finished task's settings into the live CKTcircuit)
|
|||
|
|
now propagates eight new task fields into the circuit: the three
|
|||
|
|
convergence-rescue opt-out flags (CKTresidCheckDisabled from TSKnoResidCheck,
|
|||
|
|
CKTosdiStepRejectOff from TSKnoOsdiStepReject, CKTdtClearOff from
|
|||
|
|
TSKnoDtClear) and the five OSDI per-iteration voltage-limit reals (CKTosdiVlim
|
|||
|
|
plus the per-probe-shape overrides CKTosdiVlimVds/Vgs/Vbs/Vnqs).
|
|||
|
|
- src/spicelib/analysis/cktntask.c (+16)
|
|||
|
|
CKTnewTask() gains initialization for the same eight new task fields.
|
|||
|
|
- src/spicelib/analysis/cktsopt.c (+37)
|
|||
|
|
CKTsetOpt() adds parsing/dispatch for eight new .option keywords.
|
|||
|
|
- src/spicelib/analysis/ckttroub.c (+5)
|
|||
|
|
CKTtrouble() (which builds the failure/trouble diagnostic message for DC-sweep
|
|||
|
|
aborts) gains a new branch handling TRCVvType == PARAM_CODE, i.e.
|
|||
|
|
- src/spicelib/analysis/dctran.c (+63)
|
|||
|
|
Two additions to the transient loop.
|
|||
|
|
- src/spicelib/analysis/dctrcurv.c (+242)
|
|||
|
|
Implements the new HSPICE-compatible ".dc <paramname>" sweep (PARAM_CODE).
|
|||
|
|
- visualc/make-install-vngspice.bat (+3)
|
|||
|
|
Windows/VisualC install-copy batch script (release build) that stages DLLs
|
|||
|
|
into the install tree.
|
|||
|
|
- visualc/make-install-vngspiced.bat (+3)
|
|||
|
|
Identical in purpose to make-install-vngspice.bat but for the debug ("d")
|
|||
|
|
build variant.
|