From 777cfcf47753e96347e21bfaa845c0e85e5306ff Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Thu, 10 Dec 2020 14:13:33 +0100 Subject: [PATCH] Reduce the memory requirements if there is a PDK with a lot of binning models. This is a hack and needs testing! inpcom.c: If an x line, add w and l to the netlist card, if available. subckt.c: select a suitable model bin, discard the rest for each subcircuit, depending on w and l from above. inpgmod.c: less restrictive equal for real numbers, allow both min and max boundaries (problem of equating real numbers), when the selected device has w or l on the boundary between two model bins. --- src/frontend/inp.c | 24 ++++- src/frontend/inpcom.c | 96 ++++++++++++++++--- src/frontend/subckt.c | 169 +++++++++++++++++++++++++++++++++- src/frontend/subckt.h | 1 + src/include/ngspice/inpdefs.h | 2 + src/spicelib/parser/inpgmod.c | 4 +- 6 files changed, 280 insertions(+), 16 deletions(-) diff --git a/src/frontend/inp.c b/src/frontend/inp.c index 139f6237b..6c6d878df 100644 --- a/src/frontend/inp.c +++ b/src/frontend/inp.c @@ -41,7 +41,7 @@ Author: 1985 Wayne A. Christopher #include "numparam/numpaif.h" #include "ngspice/stringskip.h" #include "ngspice/randnumb.h" - +#include "ngspice/compatmode.h" #define line_free(line, flag) \ do { \ @@ -749,6 +749,28 @@ inp_spsource(FILE *fp, bool comfile, char *filename, bool intfile) for (ii = 0; ii < 5; ii++) eval_agauss(deck, statfcn[ii]); } + + /* If we have large PDK deck, search for scale option and set + the variable 'scale'*/ + if (newcompat.hs || newcompat.spe) { + struct card* scan; + double dscale = 1; + struct card* scoptions = line_reverse(line_nconc(options, inp_deckcopy(com_options))); + for (scan = scoptions; scan; scan = scan->nextcard) { + char* tmpscale = strstr(scan->line, "scale="); + if (tmpscale) { + int err; + tmpscale = tmpscale + 6; + dscale = INPevaluate(&tmpscale, &err, 1); + if (err == 0) + cp_vset("scale", CP_REAL, &dscale); + else + fprintf(stderr, "\nError: Could not set 'scale' variable\n"); + break; + } + } + } + /* Now expand subcircuit macros and substitute numparams.*/ if (!cp_getvar("nosubckt", CP_BOOL, NULL, 0)) if ((deck->nextcard = inp_subcktexpand(deck->nextcard)) == NULL) { diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index a20fa6e80..0315b5dbc 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -180,6 +180,7 @@ static struct card *ltspice_compat(struct card *oldcard); static void ltspice_compat_a(struct card *oldcard); static void inp_repair_dc_ps(struct card* oldcard); +static void inp_get_w_l_x(struct card* oldcard); #ifndef EXT_ASC static void utf8_syntax_check(struct card *deck); @@ -563,6 +564,66 @@ static void new_compat_mode(void) } } +/* We check x lines for w= and l= and fill in their values. + To be used when expanding subcircuits with binned model cards. */ +void inp_get_w_l_x(struct card* card) { + for (; card; card = card->nextcard) { + char* curr_line = card->line; + int skip_control = 0; + char* w = NULL, * l = NULL; + + /* exclude any command inside .control ... .endc */ + if (ciprefix(".control", curr_line)) { + skip_control++; + continue; + } + else if (ciprefix(".endc", curr_line)) { + skip_control--; + continue; + } + else if (skip_control > 0) { + continue; + } + /* only subcircuit invocations */ + if (*curr_line != 'x' && !newcompat.hs && !newcompat.spe) { + card->w = card->l = 0; + continue; + } + + w = strstr(curr_line, " w="); + if (w) { + int err; + w = w + 3; + card->w = (float)INPevaluate(&w, &err, 0); + if(err) { + card->w = card->l = 0; + continue; + } + } + else { + card->w = card->l = 0; + continue; + } + + + l = strstr(curr_line, " l="); + if (l) { + int err; + l = l + 3; + card->l = (float)INPevaluate(&l, &err, 0); + if(err) { + card->w = card->l = 0; + continue; + } + } + else { + card->w = card->l = 0; + continue; + } + } +} + + /*------------------------------------------------------------------------- Read the entire input file and return a pointer to the first line of the linked list of 'card' records in data. The pointer is stored in @@ -680,6 +741,10 @@ struct card *inp_readall(FILE *fp, const char *dir_name, inp_reorder_params(subckt_w_params, cc); + /* Special handling for large PDKs: We need to know W and L of + transistor subcircuits by checking their x invokcation */ + inp_get_w_l_x(working); + inp_fix_inst_calls_for_numparam(subckt_w_params, working); // tprint(working); delete_names(subckt_w_params); @@ -2107,7 +2172,6 @@ static const char *nlist_find(const struct nlist *nlist, const char *name) return NULL; } -#if 0 static const char *nlist_model_find( const struct nlist *nlist, const char *name) { @@ -2117,7 +2181,6 @@ static const char *nlist_model_find( return nlist->names[i]; return NULL; } -#endif static void nlist_adjoin(struct nlist *nlist, char *name) { @@ -2247,7 +2310,7 @@ static void comment_out_unused_subckt_models(struct card *start_card) char *line = card->line; /* no models embedded in these lines */ - if (strchr("*vibefghkt", *line)) + if (strchr(".*vibefghkt", *line)) continue; /* there is no .subckt, .model or .param inside .control ... .endc */ @@ -2323,26 +2386,34 @@ static void comment_out_unused_subckt_models(struct card *start_card) if (remove_subckt) *line = '*'; /* make line a comment */ -#if 0 - else if (has_models && - (ciprefix(".model", line) || ciprefix(".cmodel", line))) { - char *model_type = get_model_type(line); - char *model_name = get_subckt_model_name(line); + } + + /* comment out any unused models */ + for (card = start_card; card; card = card->nextcard) { + + char* line = card->line; + + if (*line == '*') + continue; + + if (has_models && + (ciprefix(".model", line) || ciprefix(".cmodel", line))) { + char* model_type = get_model_type(line); + char* model_name = get_subckt_model_name(line); /* keep R, L, C models because in addition to no. of terminals the value may be given, as in RE1 1 2 800 newres dtemp=5, so model name may be token no. 4 or 5, and, if 5, will not be detected by get_subckt_model_name()*/ if (!cieq(model_type, "c") && !cieq(model_type, "l") && - !cieq(model_type, "r") && - !nlist_model_find(used_models, model_name)) { + !cieq(model_type, "r") && + !nlist_model_find(used_models, model_name)) { *line = '*'; } tfree(model_type); tfree(model_name); } -#endif } nlist_destroy(used_subckts); @@ -8058,6 +8129,9 @@ static struct card *pspice_compat(struct card *oldcard) card->line = tprintf( ".model a%s aswitch(%s %s %s %s log=TRUE limit=TRUE)", modname, modpar[0], modpar[1], modpar[2], modpar[3]); +// card->line = tprintf( +// ".model a%s pswitch(%s %s %s %s log=TRUE)", modname, +// modpar[0], modpar[1], modpar[2], modpar[3]); } for (i = 0; i < 4; i++) tfree(modpar[i]); diff --git a/src/frontend/subckt.c b/src/frontend/subckt.c index 3ab62bae6..51a233880 100644 --- a/src/frontend/subckt.c +++ b/src/frontend/subckt.c @@ -60,6 +60,7 @@ Modified: 2000 AlansFixes #include "ngspice/ftedefs.h" #include "ngspice/fteinp.h" #include "ngspice/stringskip.h" +#include "ngspice/compatmode.h" #include @@ -557,6 +558,10 @@ doit(struct card *deck, wordlist *modnames) { } #endif + double scale; + if (!cp_getvar("scale", CP_REAL, &scale, 0)) + scale = 1; + error = 0; /* Second pass: do the replacements. */ do { /* while (!error && numpasses-- && gotone) */ @@ -608,8 +613,114 @@ doit(struct card *deck, wordlist *modnames) { * instance of a subckt that is defined above at higher level. */ if (sss) { +// tprint(sss->su_def); + struct card *su_deck = inp_deckcopy_ln(sss->su_def); + /* 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.*/ +// struct card* su_deck = NULL; + 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; + } + + char* curr_line = su_deck->line; + 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; + 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; + continue; + } + + 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; + } + + float csl = (float)scale * c->l; + float csw = (float)scale * c->w; + if (csl >= flmin && csl < flmax && csw >= fwmin && csw < fwmax) { + /* use the current .model card */ + prev = su_deck; + su_deck = su_deck->nextcard; + continue; + } + else { + struct card* tmpcard = su_deck->nextcard; + line_free_x(prev->nextcard, FALSE); + su_deck = prev->nextcard = tmpcard; + } + } + su_deck = new_deck; + } + + if (!su_deck) { + fprintf(stderr, "\nError: Could not find a model for device %s in subcircuit %s\n", + scname, sss->su_name); + controlled_exit(1); + } - struct card *su_deck = inp_deckcopy(sss->su_def); struct card *rest_of_c = c->nextcard; /* Now we have to replace this line with the @@ -711,6 +822,8 @@ struct card * inp_deckcopy(struct card *deck) { nd = d = TMALLOC(struct card, 1); } d->linenum = deck->linenum; + d->w = deck->w; + d->l = deck->l; d->line = copy(deck->line); if (deck->error) d->error = copy(deck->error); @@ -720,7 +833,6 @@ struct card * inp_deckcopy(struct card *deck) { return (nd); } - /* * Copy a deck, without the ->actualLine lines, without comment lines, and * without .control section(s). @@ -754,6 +866,8 @@ struct card *inp_deckcopy_oc(struct card * deck) else { /* This is the first card */ nd = d = TMALLOC(struct card, 1); } + d->w = deck->w; + d->l = deck->l; d->linenum_orig = deck->linenum; d->linenum = i++; d->line = copy(deck->line); @@ -770,6 +884,57 @@ struct card *inp_deckcopy_oc(struct card * deck) return nd; } /* end of function inp_deckcopy_oc */ +/* + * Copy a deck, without the ->actualLine lines, without comment lines, and + * without .control section(s). + * First line is always copied (except being .control). + * Keep the line numbers. + */ +struct card* inp_deckcopy_ln(struct card* deck) +{ + struct card* d = NULL, * nd = NULL; + int skip_control = 0; + + while (deck) { + /* exclude any command inside .control ... .endc */ + if (ciprefix(".control", deck->line)) { + skip_control++; + deck = deck->nextcard; + continue; + } + else if (ciprefix(".endc", deck->line)) { + skip_control--; + deck = deck->nextcard; + continue; + } + else if (skip_control > 0) { + deck = deck->nextcard; + continue; + } + if (nd) { /* First card already found */ + /* d is the card at the end of the deck */ + d = d->nextcard = TMALLOC(struct card, 1); + } + else { /* This is the first card */ + nd = d = TMALLOC(struct card, 1); + } + d->w = deck->w; + d->l = deck->l; + d->linenum_orig = deck->linenum_orig; + d->linenum = deck->linenum; + d->line = copy(deck->line); + if (deck->error) { + d->error = copy(deck->error); + } + d->actualLine = NULL; + deck = deck->nextcard; + while (deck && *(deck->line) == '*') { /* skip comments */ + deck = deck->nextcard; + } + } /* end of loop over cards in the source deck */ + + return nd; +} /* end of function inp_deckcopy_ln */ /*------------------------------------------------------------------- diff --git a/src/frontend/subckt.h b/src/frontend/subckt.h index 73f155842..3d93bf36f 100644 --- a/src/frontend/subckt.h +++ b/src/frontend/subckt.h @@ -9,5 +9,6 @@ struct card *inp_subcktexpand(struct card *deck); struct card *inp_deckcopy(struct card *deck); struct card *inp_deckcopy_oc(struct card *deck); +struct card *inp_deckcopy_ln(struct card *deck); #endif diff --git a/src/include/ngspice/inpdefs.h b/src/include/ngspice/inpdefs.h index a5a6fb8bc..5eab39d4f 100644 --- a/src/include/ngspice/inpdefs.h +++ b/src/include/ngspice/inpdefs.h @@ -82,6 +82,8 @@ struct card { struct card *nextcard; struct card *actualLine; struct nscope *level; + float w; + float l; }; /* structure used to save models in after they are read during pass 1 */ diff --git a/src/spicelib/parser/inpgmod.c b/src/spicelib/parser/inpgmod.c index 292f7f7ca..cd5aa8d9f 100644 --- a/src/spicelib/parser/inpgmod.c +++ b/src/spicelib/parser/inpgmod.c @@ -208,7 +208,7 @@ parse_line(char *line, char *tokens[], int num_tokens, double values[], bool fou static bool is_equal(double result, double expectedResult) { - return fabs(result - expectedResult) < 1e-15; + return fabs(result - expectedResult) < 1e-9; } @@ -216,7 +216,7 @@ static bool in_range(double value, double min, double max) { /* the standard binning rule is: min <= value < max */ - return is_equal(value, min) || (min < value && value < max); + return is_equal(value, min) || is_equal(value, max) || (min < value && value < max); }