From 4f5c23ba44e488c386303a748287beda06ad6faf Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Sat, 27 Sep 2025 12:16:39 -0700 Subject: [PATCH 01/10] Add a correction to the previous change of inppas4.c, which freed devname and left instance name pointers in DEVnameHash pointing at freed memory. This commit adds the devname string to the symbol tables which are eventually cleared by INPtabEnd. Also, if a capacitor in the spice netlist has the same name (unlikely, but possible) as a shunt capacitor, then no shunt is created, and a warning is issued. --- src/spicelib/parser/inppas4.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/spicelib/parser/inppas4.c b/src/spicelib/parser/inppas4.c index dd6096577..d30283c8b 100644 --- a/src/spicelib/parser/inppas4.c +++ b/src/spicelib/parser/inppas4.c @@ -57,8 +57,24 @@ void INPpas4(CKTcircuit *ckt, INPtables *tab) int nn = node->number; char* devname = tprintf("capac%dshunt", nn); - (*(ft_sim->newInstance))(ckt, tab->defCmod, &fast, devname); - txfree(devname); + fast = (*(ft_sim->findInstance))(ckt, devname); + if (fast) { + fprintf(stderr, + "WARNING: non-cshunt instance %s already exists\n", + devname); + tfree(devname); + continue; + } + error = (*(ft_sim->newInstance))(ckt, tab->defCmod, &fast, devname); + if (error) { + fprintf(stderr, + "ERROR: cshunt newInstance status %d devname %s\n", + error, devname); + tfree(devname); + continue; + } + /* devname is eventually freed when INPtabEnd is called */ + INPinsert(&devname, tab); /* the top node, second node is gnd automatically */ (*(ft_sim->bindNode))(ckt, fast, 1, node); From 7ab6c6f4af940e9321246e993fc3075589af72ef Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Sun, 28 Sep 2025 11:59:17 -0700 Subject: [PATCH 02/10] Update and clarify the comments at the top of udevices.c, which should make it easier when someone in the future has to make changes. --- src/frontend/udevices.c | 63 +++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/src/frontend/udevices.c b/src/frontend/udevices.c index fe406cb7c..c2f14ec47 100644 --- a/src/frontend/udevices.c +++ b/src/frontend/udevices.c @@ -4,10 +4,16 @@ Notes: To translate Pspice U* devices in a subcircuit containing U* instance and Pspice .model cards, two passes through the subcircuit are necessary. The first pass is to translate the timing models from - the .model cards. This timing delay information is stored. The second - pass is for translating the U* instance cards to generate equivalent - Xspice digital device instances and their timing delay .model cards - using the previously stored delays. + the .model cards. This timing delay information is stored. Also, during + the first pass each U* instance in the subcircuit is checked to see + if it can be translated. + + The second pass is for translating the U* instance cards to generate + equivalent Xspice digital device instances and their timing delay + .model cards using the previously stored delays. + + The two pass algorithm is implemented by the function u_instances() + in frontend/inpcompat.c. Some limitations are: No support for CONSTRAINT, RAM, ROM, STIM, PLAs. @@ -21,24 +27,45 @@ f_logicexp and f_pindly. First pass through a subcircuit. Call initialize_udevice() and read the - .model cards by calling u_process_model_line() (or similar) for each card, - The delays for the different types (ugate, utgate, ueff, ugff, udly) are stored - by get_delays_...() and add_delays_to_model_xlator(). Also, during the - first pass, check that each U* instance can be translated to Xspice. + .model cards by calling u_process_model_line() for each model card. + The delays for the different types (ugate, utgate, ueff, ugff, udly) + are stored by: + get_delays_ugate(), get_delays_utgate(), get_delays_ueff(), + get_delays_ugff(), get_delays_udly(). + + Also, during the first pass, verify that each U* instance can be translated + to Xspice by calling u_check_instance(). If there are any .model or U* instance cards that cannot be processed in the .subckt, then there is no second pass and the .subckt is skipped. Second pass through a subcircuit. To translate each U* instance call - u_process_instance_line() (or similar). This calls translate_...() - functions for gates, tristate, flip-flops and latches. translate_...() - calls add_..._inout_timing_model() to parse the U* card, and then calls - gen_..._instance(). Creating new cards to replace the U* and .model - cards is done by calling replacement_udevice_cards(), which returns a - list of new cards. The list of cards is then to be inserted after the - .subckt card and before the .ends card. This occurs in the driver - function u_instances() in frontend/inpcom.c. - Finally, call cleanup_udevice() before repeating the sequence for - another subcircuit. + u_process_instance(). This calls: + translate_gate(), translate_ff_latch(), translate_pull(), + translate_dlyline() + functions for gates, tristate, flip-flops, latches etc. + + The translate functions call the corresponding: + add_gate_inout_timing_model(), add_array_inout_timing_model(), + add_compound_inout_timing_model(), add_dff_inout_timing_model(), + add_dltch_inout_timing_model(), add_jkff_inout_timing_model(), + add_srff_inout_timing_model() + when parsing the U* card to add inputs, outputs, and timing model + to an Xspice instance. + + Finally, the translate functions call: + gen_gate_instance(), gen_compound_instance(), gen_dff_instance(), + gen_jkff_instance(), gen_dltch_instance(), gen_srff_instance(). + + Each gen_..._instance() creates new cards to replace the U* and .model + cards. + + If all the U* instances and timing models in the subcircuit can be + translated, the driver function u_instances() (in inpcompat.c) will call + replacement_udevice_cards(), + which returns a list of new cards. The list of cards is then inserted + after the .subckt card and before the .ends card. + Finally, the driver calls cleanup_udevice() before repeating the sequence + for another subcircuit. More explanations are provided below in comments with NOTE. */ From cec426292f550ced2cf47ac883d00d3f14c1e6f4 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 3 Oct 2025 15:45:47 +0200 Subject: [PATCH 03/10] Improve reading the source code sequence by extra comments: While searching for 'Parsing the circuit', the sequence of major functions is revealed. --- src/frontend/inp.c | 16 +++++++++++++--- src/frontend/inpcom.c | 4 ++++ src/frontend/spiceif.c | 20 ++++++++++++++------ src/main.c | 5 ++++- src/spicelib/parser/inppas1.c | 5 ++--- src/winmain.c | 4 +++- 6 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index c12489ef8..b857a1462 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -530,7 +530,9 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) char *dir_name = ngdirname(filename ? filename : "."); startTime = seconds(); - /* inp_source() called with fp: load from file, */ + /* Parsing the circuit 2. + This is the next major step: + inp_source() called with fp: load circuit netlist from file, */ /* called with *fp == NULL and intfile: we want to load circuit from circarray */ if (fp || intfile) { deck = inp_readall(fp, dir_name, filename, comfile, intfile, &expr_w_temper); @@ -931,7 +933,9 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) } } - /* Now expand subcircuit macros and substitute numparams.*/ + /* Parsing the circuit 4. + This is the next major step: + Expand subcircuit macros and substitute numparams.*/ if (!cp_getvar("nosubckt", CP_BOOL, NULL, 0)) if ((deck->nextcard = inp_subcktexpand(deck->nextcard)) == NULL) { line_free(realdeck, TRUE); @@ -1084,7 +1088,9 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) if (newcompat.hs || newcompat.spe) rem_unused_mos_models(deck->nextcard); #endif - /* now load deck into ft_curckt -- the current circuit. */ + /* Parsing the circuit 5. + This is the next major step: + load deck into ft_curckt -- the current circuit. */ if(inp_dodeck(deck, tt, wl_first, FALSE, options, filename) != 0) return 1; @@ -1411,6 +1417,10 @@ inp_dodeck( *---------------------------------------------------*/ if (!noparse) { startTime = seconds(); + /* Parsing the circuit 6. + This is the next major step: + Input a single deck, and return a pointer to the circuit. + Parse all models and instances */ ckt = if_inpdeck(deck, &tab); ft_curckt->FTEstats->FTESTATnetParseTime = seconds() - startTime; /* if .probe, rename the current measurement node vcurr_ */ diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index ff693b115..0e8367926 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1054,6 +1054,10 @@ struct card *inp_readall(FILE *fp, const char *dir_name, const char* file_name, /* set the members of the compatibility structure */ set_compat_mode(); + /* Parsing the circuit 3. + This is the next major step: + Reading the netlist line by line, handle .include and .lib, + line continuation and upper/lower casing */ rv = inp_read(fp, 0, dir_name, file_name, comfile, intfile); cc = rv.cc; diff --git a/src/frontend/spiceif.c b/src/frontend/spiceif.c index 2615a5978..9697d7298 100644 --- a/src/frontend/spiceif.c +++ b/src/frontend/spiceif.c @@ -93,8 +93,9 @@ static int finddev(CKTcircuit *ckt, char *name, GENinstance **devptr, GENmodel * /* espice fix integration */ static int finddev_special(CKTcircuit *ckt, char *name, GENinstance **devptr, GENmodel **modptr, int *device_or_model); -/* Input a single deck, and return a pointer to the circuit. */ - +/* Input a single deck, and return a pointer to the circuit. + Parse all models in function INPpas1, instances (devices) in INPpas2, + consider initial conditions (INPpas3), and shunt capacitors (INPpas4). */ CKTcircuit * if_inpdeck(struct card *deck, INPtables **tab) { @@ -162,16 +163,23 @@ if_inpdeck(struct card *deck, INPtables **tab) ft_curckt->ci_curTask = ft_curckt->ci_defTask; - /* Parse the .model lines. Enter the model into the global model table modtab. */ modtab = NULL; modtabhash = NULL; - /* Parse .model lines, put them into 'tab' */ + /* Parsing the circuit 7. + This is the next major step: + Parse the .model lines. + Enter the model into the global model table modtab + and into the corresponding hash table modtabhash. + The role of 'tab' is unclear (not used any more?). */ INPpas1(ckt, deck->nextcard, *tab); - /* store the new model table in the current circuit */ + /* store the new model tables in the current circuit */ ft_curckt->ci_modtab = modtab; ft_curckt->ci_modtabhash = modtabhash; - /* Scan through the instance lines and parse the circuit. */ + /* Parsing the circuit 8. + This is the next major step: + Scan through the instance lines and parse the circuit. + Set up the circuit matrix. */ INPpas2(ckt, deck->nextcard, *tab, ft_curckt->ci_defTask); #ifdef XSPICE if (!Evtcheck_nodes(ckt, *tab)) { diff --git a/src/main.c b/src/main.c index a5a83c011..986714e72 100644 --- a/src/main.c +++ b/src/main.c @@ -1490,7 +1490,10 @@ int main(int argc, char **argv) gotone = FALSE; // Re-use if (tempfile && (!err || !ft_batchmode)) { - /* Copy the input file name for becoming another file search path */ + /* Parsing the circuit 1. + This is the next major step: + Source the input file, then parse the data and create the circuit. + Copy the input file name for becoming another file search path */ if (inp_spsource(tempfile, FALSE, dname, FALSE) != 0) { fprintf(stderr, " Simulation interrupted due to error!\n\n"); if (ft_stricterror || (oflag && !cp_getvar("interactive", CP_BOOL, NULL, 0))) diff --git a/src/spicelib/parser/inppas1.c b/src/spicelib/parser/inppas1.c index b990cadf6..c5048d134 100644 --- a/src/spicelib/parser/inppas1.c +++ b/src/spicelib/parser/inppas1.c @@ -8,9 +8,8 @@ Author: 1985 Thomas L. Quarles #include "inppas1.h" /* - * The first pass of the circuit parser just looks for '.model' lines - */ - + The first pass of the circuit parser just looks for '.model' lines, + and sticks model into model table tab. */ void INPpas1(CKTcircuit *ckt, struct card *deck, INPtables * tab) { struct card *current; diff --git a/src/winmain.c b/src/winmain.c index d380ff753..a851f4242 100644 --- a/src/winmain.c +++ b/src/winmain.c @@ -1340,7 +1340,9 @@ wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR /* Wait until everything is settled */ WaitForIdle(); - /* Go to main() */ + /* Parsing the command line. + This is the next major step: + Go to main() for reading the start command line and preparing the simulator. */ nReturnCode = xmain(argc, argv); THE_END: From 875aaaf53056235a220a5a2e3c25021db86fb24b Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 4 Oct 2025 19:58:22 +0200 Subject: [PATCH 04/10] Add info for line source --- src/xspice/enh/enhtrans.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/xspice/enh/enhtrans.c b/src/xspice/enh/enhtrans.c index 5e2bf8339..2eb23aa2a 100644 --- a/src/xspice/enh/enhtrans.c +++ b/src/xspice/enh/enhtrans.c @@ -124,6 +124,9 @@ ENHtranslate_poly( l1->linenum = d->linenum; l2->linenum = d->linenum; + l1->linesource = copy("internal"); + l2->linesource = copy("internal"); + /* Create the translated cards */ d->error = two2three_translate(d->line, &(l1->line), &(l2->line)); From fafb8cc272e6bbb53258beee02b273437a135144 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 4 Oct 2025 19:59:19 +0200 Subject: [PATCH 05/10] Improve on truncation when printing out the .model lines during debug-out --- src/frontend/inp.c | 5 ++++- src/frontend/inpcom.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index b857a1462..56951b617 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -989,8 +989,11 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) continue; /* Only truncated .model lines */ if (ciprefix(".model", tc->line)) { - fprintf(fdo, "%6d %.100s ...\n", + fprintf(fdo, "%6d %.100s ", tc->linenum, tc->line); + if (strlen(tc->line) > 100) + fprintf(fdo, " ... (truncated)"); + fprintf(fdo, "\n"); } else { fprintf(fdo, "%6d %s\n", diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 0e8367926..0c6f5b283 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1245,8 +1245,11 @@ struct card *inp_readall(FILE *fp, const char *dir_name, const char* file_name, continue; /* Only truncated .model lines */ if (ciprefix(".model", t->line)) { - fprintf(fd, "%6d %.100s ...\n", + fprintf(fd, "%6d %.100s ", t->linenum, t->line); + if (strlen(t->line) > 100) + fprintf(fd, " ... (truncated)"); + fprintf(fd, "\n"); } else { fprintf(fd, "%6d %s\n", From 85853836546f08b4f2b1449171463a229c3c9f2e Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 6 Oct 2025 17:07:27 +0200 Subject: [PATCH 06/10] Improve error messages. Towards better locating of errors: Remove entry to avoid false file readings. Still to do: Return correct fline numbers, when reading large PDKs. --- src/frontend/inpcom.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 0c6f5b283..af7e2c0d1 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -1609,11 +1609,8 @@ static struct inp_read_t inp_read(FILE* fp, int call_depth, const char* dir_name char* tmpstr = copy(nexttok(buffer)); wl_append_word(&sourceinfo, &sourceinfo, tmpstr); - /* Add source of netlist data, for use in verbose error messages. - Set the compatibility mode flag to 1, if pslt is read. */ + /* Set the compatibility mode flag to 1, if pslt is read. */ for (tmpcard = newcard; tmpcard; tmpcard = tmpcard->nextcard) { - /* skip *include */ - tmpcard->linesource = tmpstr; if (compset) tmpcard->compmod = 1; else From 236967a21e0819682e441e29f6d16866d77b7ea9 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Mon, 6 Oct 2025 17:30:38 +0200 Subject: [PATCH 07/10] Improve error message: add source type --- src/frontend/inpcom.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index af7e2c0d1..1f90d2732 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -9042,7 +9042,7 @@ static void inp_check_syntax(struct card *deck) acline = nexttok(acline); if (!acline) { fprintf(stderr, "Error in line %s\n", cut_line); - fprintf(stderr, " Not enough parameters\n"); + fprintf(stderr, " Not enough parameters for %c source\n", *cut_line); fprintf(stderr, " line no. %d from file %s\n", card->linenum_orig, card->linesource); From bbceec30d082b26b3a780c2485a03134cb57c26d Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Tue, 7 Oct 2025 15:36:27 +0200 Subject: [PATCH 08/10] Re-enable warning note when rusage xxx is not implemented. --- src/frontend/resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/resource.c b/src/frontend/resource.c index 2008efc48..47b4ae400 100644 --- a/src/frontend/resource.c +++ b/src/frontend/resource.c @@ -339,9 +339,9 @@ printres(char *name) ft_curckt->ci_ckt->CKTstat->devTimes[i]/(double)(ft_curckt->ci_ckt->CKTstat->devCounts[i]) ); } + yy = TRUE; } - yy = TRUE; - + #ifdef CIDER /* begin cider integration */ if (!name || eq(name, "circuit") || eq(name, "task")) From 172fb6d66826d780a048cb443fd3bd790d4344ed Mon Sep 17 00:00:00 2001 From: Giles Atkinson <“gatk555@gmail.com”> Date: Wed, 8 Oct 2025 18:19:17 +0100 Subject: [PATCH 09/10] Fix Bug 817 - "Global models are not visible from with subcircuits". Remove code that can miscount the number of nodes to be renamed. --- src/frontend/subckt.c | 188 +++++------------------------------------- 1 file changed, 20 insertions(+), 168 deletions(-) diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c index 4cb7be417..52eb230f5 100644 --- a/src/frontend/subckt.c +++ b/src/frontend/subckt.c @@ -97,11 +97,10 @@ struct bxx_buffer; static void finishLine(struct bxx_buffer *dst, char *src, char *scname); static int settrans(char *formal, int flen, char *actual, const char *subname); static char *gettrans(const char *name, const char *name_end, bool *isglobal); -static int numnodes(const char *line, struct subs *subs, wordlist const *modnames); +static int numnodes(const char *line); static int numdevs(char *s); static wordlist *modtranslate(struct card *deck, char *subname, wordlist *new_modnames); static void devmodtranslate(struct card *deck, char *subname, wordlist * const orig_modnames); -static int inp_numnodes(char c); /* hash table to store the global nodes * For now its use is limited to avoid double entries in global_nodes[] */ @@ -1384,9 +1383,11 @@ translate(struct card *deck, char *formal, int flen, char *actual, char *scname, tfree(name); bxx_putc(&buffer, ' '); - /* Next iterate over all nodes (netnames) found and translate them. */ - nnodes = numnodes(c->line, subs, modnames); + /* Next iterate over all nodes (netnames) found and translate them. + * Ignore controlling nodes as they get special treatment for POLY. + */ + nnodes = numnodes(c->line); while (--nnodes >= 0) { name = gettok_node(&s); if (name == NULL) { @@ -1476,13 +1477,7 @@ translate(struct card *deck, char *formal, int flen, char *actual, char *scname, tfree(name); bxx_putc(&buffer, ' '); - /* FIXME anothet hack: if no models found for m devices, set number of nodes to 4 */ - if (!modnames && *(c->line) == 'm') - nnodes = get_number_terminals(c->line); - else if (*(c->line) == 'n') - nnodes = get_number_terminals(c->line); - else - nnodes = numnodes(c->line, subs, modnames); + nnodes = numnodes(c->line); while (--nnodes >= 0) { name = gettok_node(&s); if (name == NULL) { @@ -1692,91 +1687,23 @@ gettrans(const char *name, const char *name_end, bool *isglobal) } -/*-------------------------------------------------------------------*/ -/*-------------------------------------------------------------------*/ +/* Control nodes for E and G sources are not counted as they vary in + * the case of POLY. The count returned by get_number_terminals() includes + * devices for K (mutual inductors) and W (current-controlled switch). + */ + static int -numnodes(const char *line, struct subs *subs, wordlist const *modnames) +numnodes(const char *line) { - /* gtri - comment - wbk - 10/23/90 - Do not modify this routine for */ - /* 'A' type devices since the callers will not know how to find the */ - /* nodes even if they know how many there are. Modify the callers */ - /* instead. */ - /* gtri - end - wbk - 10/23/90 */ - char c; - int n; - - line = skip_ws(line); - - c = tolower_c(*line); - - if (c == 'x') { /* Handle this ourselves. */ - const char *xname_e = skip_back_ws(strchr(line, '\0'), line); - const char *xname = skip_back_non_ws(xname_e, line); - for (; subs; subs = subs->su_next) - if (eq_substr(xname, xname_e, subs->su_name)) - return subs->su_numargs; - /* - * number of nodes not known so far. - * lets count the nodes ourselves, - * assuming `buf' looks like this: - * xname n1 n2 ... nn subname - */ - { - int nodes = -2; - while (*line) { - nodes++; - line = skip_ws(skip_non_ws(line)); - } - return (nodes); - } - } - /* if we use option skywaterpdk, MOS has four nodes. Required if number of devices is large */ - if (ft_skywaterpdk && c == 'm') - return 4; - - n = inp_numnodes(c); - - /* Added this code for variable number of nodes on certain devices. */ - /* The consequence of this code is that the value returned by the */ - /* inp_numnodes(c) call must be regarded as "maximum number of nodes */ - /* for a given device type. */ - /* Paolo Nenzi Jan-2001 */ - - /* If model names equal node names, this code will fail! */ - if ((c == 'm') || (c == 'p') || (c == 'q') || (c == 'd')) { /* IF this is a mos, cpl, bjt or diode */ - char *s = nexttok(line); /* Skip the instance name */ - int gotit = 0; - int i = 0; - - while ((i <= n) && (*s) && !gotit) { - char *t = gettok_node(&s); /* get nodenames . . . */ - const wordlist *wl; - for (wl = modnames; wl; wl = wl->wl_next) - if (model_name_match(t, wl->wl_word)) { - gotit = 1; - break; - } - i++; - tfree(t); - } - - /* Note: node checks must be done on #_of_node-1 because the */ - /* "while" cycle increments the counter even when a model is */ - /* recognized. This code may be better! */ - - if ((i < 4) && ((c == 'm') || (c == 'q'))) { - fprintf(cp_err, "Error: too few nodes for MOS or BJT: %s\n", line); - return (0); - } - if ((i < 5) && (c == 'p')) { - fprintf(cp_err, "Error: too few nodes for CPL: %s\n", line); - return (0); - } - return (i-1); /* compensate the unnecessary increment in the while cycle */ - } else { - /* for all other elements */ - return (n); + switch (*line) { + case 'e': + case 'g': + case 'w': + return 2; + case 'k': + return 0; } + return get_number_terminals((char *)line); } @@ -2286,78 +2213,3 @@ devmodtranslate(struct card *s, char *subname, wordlist * const orig_modnames) bxx_free(&buffer); } - - -/*----------------------------------------------------------------------* - * inp_numnodes returns the maximum number of nodes (netnames) attached - * to the component. - * This is a spice-dependent thing. It should probably go somewhere - * else, but... Note that we pretend that dependent sources and mutual - * inductors have more nodes than they really do... - *----------------------------------------------------------------------*/ -static int -inp_numnodes(char c) -{ - if (isupper_c(c)) - c = tolower_c(c); - switch (c) { - case ' ': - case '\t': - case '.': - case 'x': - case '*': - case '$': - return (0); - - case 'b': - return (2); - case 'c': - return (2); - case 'd': - return (3); - case 'e': - return (2); /* changed from 4 to 2 by SDB on 4.22.2003 to enable POLY */ - case 'f': - return (2); - case 'g': - return (2); /* changed from 4 to 2 by SDB on 4.22.2003 to enable POLY */ - case 'h': - return (2); - case 'i': - return (2); - case 'j': - return (3); - case 'k': - return (0); - case 'l': - return (2); - case 'm': - return (7); /* This means that 7 is the maximun number of nodes */ - case 'o': - return (4); - case 'p': - return (18);/* 16 lines + 2 gnd is the maximum number of nodes for CPL */ - case 'q': - return (5); - case 'r': - return (2); - case 's': - return (4); - case 't': - return (4); - case 'u': - return (3); - case 'v': - return (2); - case 'w': - return (2); /* change 3 to 2 here to fix w bug, NCF 1/31/95 */ - case 'y': - return (4); - case 'z': - return (3); - - default: - fprintf(cp_err, "Warning: unknown device type: %c\n", c); - return (2); - } -} From a1de300a82e5043eca788b473d75ef2a24724311 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Fri, 10 Oct 2025 20:44:21 +0200 Subject: [PATCH 10/10] Add breakpoints to achieve quadratic smoothing. Shift 50%-detection for breakpoints into the INIT section, to do it only once. --- src/xspice/icm/analog/pwlts/cfunc.mod | 57 +++++++++++++++++++-------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/src/xspice/icm/analog/pwlts/cfunc.mod b/src/xspice/icm/analog/pwlts/cfunc.mod index 8aaf26d45..c26cbd89e 100644 --- a/src/xspice/icm/analog/pwlts/cfunc.mod +++ b/src/xspice/icm/analog/pwlts/cfunc.mod @@ -13,6 +13,7 @@ AUTHORS Pwl with time input and smoothing: pwlts 9 Sep 2022 Holger Vogt + 10 Oct 2025 Holger Vogt SUMMARY @@ -211,6 +212,46 @@ void cm_pwlts(ARGS) /* structure holding parms, y[0] = 2. * y[1] - y[2]; y[size - 1] = 2. * y[size - 2] - y[size - 3]; } + + /* See if input_domain is absolute...if so, test against */ + /* breakpoint segments for violation of 50% rule... */ + if (PARAM(fraction) == MIF_FALSE) { + if ( 3 < size ) { + for (i=1; i<(size-2); i++) { + /* Test for overlap...0.999999999 factor is to */ + /* prevent floating point problems with comparison. */ + if ( (test1 = x[i+1] - x[i]) < + (test2 = 0.999999999 * (2.0 * input_domain)) ) { + cm_message_send(limit_error); + } + } + } + } + + /* add permanent breakpoints */ + if (PARAM(fraction) == MIF_FALSE) { + for (i=1; i= dthi) ? dthi : dtlo) * input_domain; + if (x[i] - dt <= 0) + continue; + cm_analog_set_perm_bkpt(x[i] - dt); + cm_analog_set_perm_bkpt(x[i]); + cm_analog_set_perm_bkpt(x[i] + dt); + } + } + /* debug printout for (i = 0; i < size; i++) fprintf(stderr, "%e ", y[i]); @@ -229,22 +270,6 @@ void cm_pwlts(ARGS) /* structure holding parms, } - /* See if input_domain is absolute...if so, test against */ - /* breakpoint segments for violation of 50% rule... */ - if (PARAM(fraction) == MIF_FALSE) { - if ( 3 < size ) { - for (i=1; i<(size-2); i++) { - /* Test for overlap...0.999999999 factor is to */ - /* prevent floating point problems with comparison. */ - if ( (test1 = x[i+1] - x[i]) < - (test2 = 0.999999999 * (2.0 * input_domain)) ) { - cm_message_send(limit_error); - } - } - } - - } - /* Retrieve x_input value as current simulation time. */ x_input = TIME;