From a70297e87a436168ec2a5a387edce8bf1687135e Mon Sep 17 00:00:00 2001 From: Brian Taylor Date: Tue, 16 Jan 2024 15:29:58 -0800 Subject: [PATCH] Several PSPICE 9.1 evaluation digital libraries contain timing .model statements at the global level for subckts with U* instances that reference those models. By specifying "set ps_global_tmodels=1" in .spiceinit an extra pass inside u_instances() will collect those global timing models for use in subckts. Report errors detected when ngspice parses a LOGICEXP but has not added support for operator precendence. Include a hint of how to fix those errors by inserting parentheses. This error only occurs in 10 of 585 cases in the libraries. Note that inpcompat.c has been saved as a unix filetype. --- src/frontend/inpcompat.c | 3770 ++++++++++++++++---------------- src/frontend/logicexp.c | 104 +- src/frontend/udevices.c | 462 ++-- src/include/ngspice/udevices.h | 4 +- 4 files changed, 2288 insertions(+), 2052 deletions(-) diff --git a/src/frontend/inpcompat.c b/src/frontend/inpcompat.c index 4c0ce979a..b0dfd43c4 100644 --- a/src/frontend/inpcompat.c +++ b/src/frontend/inpcompat.c @@ -1,1868 +1,1902 @@ -/********** -Copyright 2023 The ngspice team. All rights reserved. -License: Three-clause BCD -Author: 2023 Holger Vogt -**********/ - -/* - For dealing with compatibility transformations - - PSICE, LTSPICE and others -*/ - -#include "ngspice/ngspice.h" - -#include "ngspice/compatmode.h" -#include "ngspice/cpdefs.h" -#include "ngspice/dstring.h" -#include "ngspice/dvec.h" -#include "ngspice/ftedefs.h" -#include "ngspice/fteext.h" -#include "ngspice/fteinp.h" -#include "numparam/general.h" - -#include -#include - -#include -#include - -#if !defined(__MINGW32__) && !defined(_MSC_VER) -#include -#endif - -#include "../misc/util.h" /* ngdirname() */ -#include "inpcom.h" -#include "ngspice/stringskip.h" -#include "ngspice/stringutil.h" -#include "ngspice/wordlist.h" -#include "subckt.h" -#include "variable.h" - -#define INTEGRATE_UDEVICES -#ifdef INTEGRATE_UDEVICES -#include "ngspice/udevices.h" -#endif - -void print_compat_mode(void); -void set_compat_mode(void); -struct card* pspice_compat(struct card* newcard); -void pspice_compat_a(struct card* oldcard); -struct card* ltspice_compat(struct card* oldcard); -void ltspice_compat_a(struct card* oldcard); - - - -/* Set a compatibility flag. -Currently available are flags for: -- LTSPICE, HSPICE, Spice3, PSPICE, KiCad, Spectre, XSPICE -*/ -struct compat newcompat; -void set_compat_mode(void) -{ - char behaviour[80]; - newcompat.hs = FALSE; - newcompat.ps = FALSE; - newcompat.xs = FALSE; - newcompat.lt = FALSE; - newcompat.ki = FALSE; - newcompat.a = FALSE; - newcompat.spe = FALSE; - newcompat.isset = FALSE; - newcompat.s3 = FALSE; - newcompat.mc = FALSE; - if (cp_getvar("ngbehavior", CP_STRING, behaviour, sizeof(behaviour))) { - if (strstr(behaviour, "hs")) - newcompat.isset = newcompat.hs = TRUE; /*HSPICE*/ - if (strstr(behaviour, "ps")) - newcompat.isset = newcompat.ps = TRUE; /*PSPICE*/ - if (strstr(behaviour, "xs")) - newcompat.isset = newcompat.xs = TRUE; /*XSPICE*/ - if (strstr(behaviour, "lt")) - newcompat.isset = newcompat.lt = TRUE; /*LTSPICE*/ - if (strstr(behaviour, "ki")) - newcompat.isset = newcompat.ki = TRUE; /*KiCad*/ - if (strstr(behaviour, "a")) - newcompat.isset = newcompat.a = TRUE; /*complete netlist, used in conjuntion with other mode*/ - if (strstr(behaviour, "ll")) - newcompat.isset = newcompat.ll = TRUE; /*all (currently not used)*/ - if (strstr(behaviour, "s3")) - newcompat.isset = newcompat.s3 = TRUE; /*spice3 only*/ - if (strstr(behaviour, "eg")) - newcompat.isset = newcompat.eg = TRUE; /*EAGLE*/ - if (strstr(behaviour, "spe")) { - newcompat.isset = newcompat.spe = TRUE; /*Spectre*/ - newcompat.ps = newcompat.lt = newcompat.ki = newcompat.eg = FALSE; - } - if (strstr(behaviour, "mc")) { - newcompat.isset = FALSE; - newcompat.mc = TRUE; /*make check*/ - } - } - if (newcompat.hs && newcompat.ps) { - fprintf(stderr, "Warning: hs and ps compatibility are mutually exclusive, switch to ps!\n"); - newcompat.hs = FALSE; - } - /* reset everything for 'make check' */ - if (newcompat.mc) - newcompat.eg = newcompat.hs = newcompat.spe = newcompat.ps = newcompat.xs = - newcompat.ll = newcompat.lt = newcompat.ki = newcompat.a = FALSE; -} - -/* Print the compatibility flags */ -void print_compat_mode(void) { - if (newcompat.mc) /* make check */ - return; - if (newcompat.isset) { - fprintf(stdout, "\n"); - fprintf(stdout, "Note: Compatibility modes selected:"); - if (newcompat.hs) - fprintf(stdout, " hs"); - if (newcompat.ps) - fprintf(stdout, " ps"); - if (newcompat.xs) - fprintf(stdout, " xs"); - if (newcompat.lt) - fprintf(stdout, " lt"); - if (newcompat.ki) - fprintf(stdout, " ki"); - if (newcompat.ll) - fprintf(stdout, " ll"); - if (newcompat.s3) - fprintf(stdout, " s3"); - if (newcompat.eg) - fprintf(stdout, " eg"); - if (newcompat.spe) - fprintf(stdout, " spe"); - if (newcompat.a) - fprintf(stdout, " a"); - fprintf(stdout, "\n\n"); - } - else { - fprintf(stdout, "\n"); - fprintf(stdout, "Note: No compatibility mode selected!\n\n"); - } -} - - -/* replace the E and G source TABLE function by a B source pwl - * (used by ST OpAmps and comparators of Infineon models). - * E_RO_3 VB_3 VB_4 VALUE={ TABLE( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 - * )*I(VreadIo)} - * will become - * BE_RO_3_1 TABLE_NEW_1 0 v = pwl( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10) - * E_RO_3 VB_3 VB_4 VALUE={ V(TABLE_NEW_1)*I(VreadIo)} - */ -static void replace_table(struct card *startcard) -{ - struct card *card; - static int numb = 0; - for (card = startcard; card; card = card->nextcard) { - char *cut_line = card->line; - if (*cut_line == 'e' || *cut_line == 'g') { - char *valp = search_plain_identifier(cut_line, "value"); - char *valp2 = search_plain_identifier(cut_line, "cur"); - if (valp || (valp2 && *cut_line == 'g')) { - char *ftablebeg = strstr(cut_line, "table("); - while (ftablebeg) { - /* get the beginning of the line */ - char *begline = copy_substring(cut_line, ftablebeg); - /* get the table function */ - char *tabfun = gettok_char(&ftablebeg, ')', TRUE, TRUE); - /* the new e, g line */ - char *neweline = tprintf("%s v(table_new_%d)%s", - begline, numb, ftablebeg); - char *newbline = - tprintf("btable_new_%d table_new_%d 0 v=pwl%s", - numb, numb, tabfun + 5); - numb++; - tfree(tabfun); - tfree(begline); - tfree(card->line); - card->line = cut_line = neweline; - insert_new_line(card, newbline, 0, card->linenum_orig); - /* read next TABLE function in cut_line */ - ftablebeg = strstr(cut_line, "table("); - } - continue; - } - } - } -} - -/* find the model requested by ako:model and do the replacement */ -static struct card *find_model(struct card *startcard, - struct card *changecard, char *searchname, char *newmname, - char *newmtype, char *endstr) -{ - struct card *nomod, *returncard = changecard; - char *origmname, *origmtype; - char *beginline = startcard->line; - if (ciprefix(".subckt", beginline)) - startcard = startcard->nextcard; - - int nesting2 = 0; - for (nomod = startcard; nomod; nomod = nomod->nextcard) { - char *origmodline = nomod->line; - if (ciprefix(".subckt", origmodline)) - nesting2++; - if (ciprefix(".ends", origmodline)) - nesting2--; - /* skip any subcircuit */ - if (nesting2 > 0) - continue; - if (nesting2 == -1) { - returncard = changecard; - break; - } - if (ciprefix(".model", origmodline)) { - origmodline = nexttok(origmodline); - origmname = gettok(&origmodline); - origmtype = gettok_noparens(&origmodline); - if (cieq(origmname, searchname)) { - if (!eq(origmtype, newmtype)) { - fprintf(stderr, - "Error: Original (%s) and new (%s) type for AKO " - "model disagree\n", - origmtype, newmtype); - controlled_exit(1); - } - /* we have got it */ - char *newmodcard = tprintf(".model %s %s %s%s", - newmname, newmtype, origmodline, endstr); - char *tmpstr = strstr(newmodcard, ")("); - if (tmpstr) { - tmpstr[0] = ' '; - tmpstr[1] = ' '; - } - tfree(changecard->line); - changecard->line = newmodcard; - tfree(origmname); - tfree(origmtype); - returncard = NULL; - break; - } - tfree(origmname); - tfree(origmtype); - } - else - returncard = changecard; - } - return returncard; -} - -/* Process any .distribution cards for PSPICE's Monte-Carlo feature. - * A .distribution card defines a probability distribution by a PWL - * density function. This could be rewritten as a function that - * returns a random value following that distribution. - * For now, just comment it away. - */ -static void do_distribution(struct card *oldcard) { - while (oldcard) { - char *line = oldcard->line; - - if (line && ciprefix(".distribution", line)) - *line = '*'; - oldcard = oldcard->nextcard; - } -} - -/* Do the .model replacement required by ako (a kind of) - * PSPICE does not support nested .subckt definitions, so - * a simple structure is needed: search for ako:modelname, - * then for modelname in the subcircuit or in the top level. - * .model qorig npn (BF=48 IS=2e-7) - * .model qbip1 ako:qorig NPN (BF=60 IKF=45m) - * after the replacement we have - * .model qbip1 NPN (BF=48 IS=2e-7 BF=60 IKF=45m) - * and we benefit from the fact that if parameters have - * doubled, the last entry of a parameter (e.g. BF=60) - * overwrites the previous one (BF=48). - */ -static struct card *ako_model(struct card *startcard) -{ - char *newmname, *newmtype; - struct card *card, *returncard = NULL, *subcktcard = NULL; - for (card = startcard; card; card = card->nextcard) { - char *akostr, *searchname; - char *cut_line = card->line; - - if (ciprefix(".subckt", cut_line)) - subcktcard = card; - else if (ciprefix(".ends", cut_line)) - subcktcard = NULL; - if (ciprefix(".model", cut_line)) { - if ((akostr = strstr(cut_line, "ako:")) != NULL && - isspace_c(akostr[-1])) { - akostr += 4; - searchname = gettok(&akostr); - cut_line = nexttok(cut_line); - newmname = gettok(&cut_line); - newmtype = gettok_noparens(&akostr); - - /* Find the model and do the replacement. */ - - if (subcktcard) - returncard = find_model(subcktcard, card, searchname, - newmname, newmtype, akostr); - if (returncard || !subcktcard) - returncard = find_model(startcard, card, searchname, - newmname, newmtype, akostr); - tfree(searchname); - tfree(newmname); - tfree(newmtype); - - /* Replacement not possible, bail out. */ - - if (returncard) - break; - } - } - } - return returncard; -} - -struct vsmodels { - char *modelname; - char *subcktline; - struct vsmodels *nextmodel; -}; - -/* insert a new model, just behind the given model */ -static struct vsmodels *insert_new_model( - struct vsmodels *vsmodel, char *name, char *subcktline) -{ - struct vsmodels *x = TMALLOC(struct vsmodels, 1); - - x->nextmodel = vsmodel ? vsmodel->nextmodel : NULL; - x->modelname = copy(name); - x->subcktline = copy(subcktline); - if (vsmodel) - vsmodel->nextmodel = x; - else - vsmodel = x; - - return vsmodel; -} - -/* find the model */ -static bool find_a_model( - struct vsmodels *vsmodel, char *name, char *subcktline) -{ - struct vsmodels *x; - for (x = vsmodel; vsmodel; vsmodel = vsmodel->nextmodel) - if (eq(vsmodel->modelname, name) && - eq(vsmodel->subcktline, subcktline)) - return TRUE; - return FALSE; -} - -/* delete the vsmodels list */ -static bool del_models(struct vsmodels *vsmodel) -{ - struct vsmodels *x; - - if (!vsmodel) - return FALSE; - - while (vsmodel) { - x = vsmodel->nextmodel; - tfree(vsmodel->modelname); - tfree(vsmodel->subcktline); - tfree(vsmodel); - vsmodel = x; - } - - return TRUE; -} - -/* Check for double '{', replace the inner '{', '}' by '(', ')' - in .subckt or .model (which both may stem from external sources) */ -static void rem_double_braces(struct card* newcard) -{ - struct card* card; - int slevel = 0; - - for (card = newcard; card; card = card->nextcard) { - char* cut_line = card->line; - if (ciprefix(".subckt", cut_line)) - slevel++; - else if (ciprefix(".ends", cut_line)) - slevel--; - if (ciprefix(".model", cut_line) || slevel > 0) { - cut_line = strchr(cut_line, '{'); - if (cut_line) { - int level = 1; - cut_line++; - while (*cut_line != '\0') { - if (*cut_line == '{') { - level++; - if (level > 1) - *cut_line = '('; - } - else if (*cut_line == '}') { - if (level > 1) - *cut_line = ')'; - level--; - } - cut_line++; - } - } - } - } -} - -#ifdef INTEGRATE_UDEVICES -static void list_the_cards(struct card *startcard, char *prefix) -{ - struct card *card; - if (!startcard) { return; } - for (card = startcard; card; card = card->nextcard) { - char* cut_line = card->line; - printf("%s %s\n", prefix, cut_line); - } -} - -static struct card *the_last_card(struct card *startcard) -{ - struct card *card, *lastcard = NULL; - if (!startcard) { return NULL; } - for (card = startcard; card; card = card->nextcard) { - lastcard = card; - } - return lastcard; -} - static void remove_old_cards(struct card *first, struct card *stop) -{ - struct card *x, *y, *next = NULL, *nexta = NULL; - if (!first || !stop || (first == stop)) { return; } - for (x = first; (x && (x != stop)); x = next) { - if (x->line) { tfree(x->line); } - if (x->error) { tfree(x->error); } - for (y = x->actualLine; y; y = nexta) { - if (y->line) { tfree(y->line); } - if (y->error) { tfree(y->error); } - nexta = y->nextcard; - tfree(y); - } - next = x->nextcard; - tfree(x); - } - -} - -static struct card *u_instances(struct card *startcard) -{ - struct card *card, *returncard = NULL, *subcktcard = NULL; - struct card *newcard = NULL, *last_newcard = NULL; - int models_ok = 0, models_not_ok = 0; - int udev_ok = 0, udev_not_ok = 0; - BOOL create_called = FALSE, repeat_pass = FALSE; - BOOL skip_next = FALSE; - - card = startcard; - while (card) { - char *cut_line = card->line; - - skip_next = FALSE; - if (ciprefix(".subckt", cut_line)) { - models_ok = models_not_ok = 0; - udev_ok = udev_not_ok = 0; - subcktcard = card; - if (!repeat_pass) { - if (create_called) { - cleanup_udevice(); - } - initialize_udevice(subcktcard->line); - create_called = TRUE; - } - } else if (ciprefix(".ends", cut_line)) { - if (repeat_pass) { - newcard = replacement_udevice_cards(); - if (newcard) { - char *tmp = NULL, *pos, *posp, *new_str = NULL, *cl; - /* Pspice definition of .subckt card: - .SUBCKT [node]* - + [OPTIONAL: < = >*] - + [PARAMS: < = >* ] - + [TEXT: < = >* ] - ... - .ENDS - */ - cl = subcktcard->line; - tmp = TMALLOC(char, strlen(cl) + 1); - (void) memcpy(tmp, cl, strlen(cl) + 1); - pos = strstr(tmp, "optional:"); - posp = strstr(tmp, "params:"); - /* If there is an optional: and a param: then posp > pos */ - if (pos) { - /* Remove the optional: section if present */ - *pos = '\0'; - if (posp) { - strcat(tmp, posp); - } - } - new_str = copy(tmp); - tfree(tmp); - remove_old_cards(subcktcard->nextcard, card); - subcktcard->nextcard = newcard; - tfree(subcktcard->line); - subcktcard->line = new_str; - if (ft_ngdebug) { - printf("%s\n", new_str); - list_the_cards(newcard, "Replacement:"); - } - last_newcard = the_last_card(newcard); - if (last_newcard) { - last_newcard->nextcard = card; // the .ends card - } - } else { - models_ok = models_not_ok = 0; - udev_ok = udev_not_ok = 0; - } - } - if (models_not_ok > 0 || udev_not_ok > 0) { - repeat_pass = FALSE; - cleanup_udevice(); - create_called = FALSE; - } else if (udev_ok > 0) { - repeat_pass = TRUE; - card = subcktcard; - skip_next = TRUE; - } else { - repeat_pass = FALSE; - cleanup_udevice(); - create_called = FALSE; - } - subcktcard = NULL; - } else if (ciprefix(".model", cut_line)) { - if (subcktcard && !repeat_pass) { - if (!u_process_model_line(cut_line)) { - models_not_ok++; - } else { - models_ok++; - } - } - } else if (ciprefix("u", cut_line)) { - if (subcktcard) { - if (repeat_pass) { - if (!u_process_instance(cut_line)) { - repeat_pass = FALSE; - cleanup_udevice(); - create_called = FALSE; - subcktcard = NULL; - models_ok = models_not_ok = 0; - udev_ok = udev_not_ok = 0; - skip_next = FALSE; - } - } else { - if (u_check_instance(cut_line)) { - udev_ok++; - } else { - udev_not_ok++; - } - } - } - } else { - if (!ciprefix("*", cut_line)) { - udev_not_ok++; - } - } - - if (!skip_next) { - card = card->nextcard; - } - } - if (create_called) { - cleanup_udevice(); - } - return returncard; -} -#endif - -/**** PSPICE to ngspice ************** -* .model replacement in ako (a kind of) model descriptions -* replace the E source TABLE function by a B source pwl -* add predefined params TEMP, VT, GMIN to beginning of deck -* add predefined params TEMP, VT, GMIN to beginning of each .subckt call -* add .functions limit, pwr, pwrs, stp, if, int -* replace vswitch part S - S1 D S DG GND SWN - .MODEL SWN VSWITCH(VON = { 0.55 } VOFF = { 0.49 } - RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) -* by - as1 %vd(DG GND) % gd(D S) aswn - .model aswn aswitch(cntl_off={0.49} cntl_on={0.55} r_off={1G} - + r_on={ 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } log = TRUE) -* replace vswitch part S_ST - S1 D S DG GND S_ST - .MODEL S_ST VSWITCH(VT = { 1.5 } VH = { 0.3 } - RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) -* by the classical voltage controlled ngspice switch - S1 D S DG GND SWN - .MODEL S_ST SW(VT = { 1.5 } VH = { 0.3 } - RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) - switch parameter td is not yet supported -* replace & by && -* replace | by || -* in R instance, replace TC = xx1, xx2 by TC1=xx1 TC2=xx2 -* replace T_ABS by temp and T_REL_GLOBAL by dtemp in .model cards -* get the area factor for diodes and bipolar devices -* in subcircuit .subckt and X lines with 'params:' statement - replace comma separator by space. Do nothing if comma is inside of {}. -* in .model, if double curly braces {{}}, replace the inner by {()} */ -struct card *pspice_compat(struct card *oldcard) -{ - struct card *card, *newcard, *nextcard; - struct vsmodels *modelsfound = NULL; - int skip_control = 0; - - /* .model replacement in ako (a kind of) model descriptions - * in first .subckt and top level only */ - struct card *errcard; - if ((errcard = ako_model(oldcard)) != NULL) { - fprintf(stderr, "Error: no model found for %s\n", errcard->line); - controlled_exit(1); - } - - /* Process .distribution cards. */ - do_distribution(oldcard); - - /* replace TABLE function in E source */ - replace_table(oldcard); - - /* remove double braces */ - rem_double_braces(oldcard); - - /* add predefined params TEMP, VT, GMIN to beginning of deck */ - char *new_str = copy(".param temp = 'temper'"); - newcard = insert_new_line(NULL, new_str, 1, 0); - new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'"); - nextcard = insert_new_line(newcard, new_str, 2, 0); - new_str = copy(".param gmin = 1e-12"); - nextcard = insert_new_line(nextcard, new_str, 3, 0); - /* add funcs limit, pwr, pwrs, stp, if, int */ - /* LIMIT( Output Expression, Limit1, Limit2) - Output will stay between the two limits given. */ - new_str = copy(".func limit(x, a, b) { ternary_fcn(a > b, max(min(x, a), b), max(min(x, b), a)) }"); - nextcard = insert_new_line(nextcard, new_str, 4, 0); - new_str = copy(".func pwr(x, a) { pow(x, a) }"); - nextcard = insert_new_line(nextcard, new_str, 5, 0); - new_str = copy(".func pwrs(x, a) { sgn(x) * pow(x, a) }"); - nextcard = insert_new_line(nextcard, new_str, 6, 0); - new_str = copy(".func stp(x) { u(x) }"); - nextcard = insert_new_line(nextcard, new_str, 7, 0); - new_str = copy(".func if(a, b, c) {ternary_fcn( a , b , c )}"); - nextcard = insert_new_line(nextcard, new_str, 8, 0); - new_str = copy(".func int(x) { sign(x)*floor(abs(x)) }"); - nextcard = insert_new_line(nextcard, new_str, 9, 0); - nextcard->nextcard = oldcard; - -#ifdef INTEGRATE_UDEVICES - { - struct card *ucard; -#ifdef TRACE - list_the_cards(newcard, "Before udevices"); -#endif - ucard = u_instances(newcard); -#ifdef TRACE - list_the_cards(newcard, "After udevices"); -#endif - } -#endif - - - /* add predefined parameters TEMP, VT after each subckt call */ - /* FIXME: This should not be necessary if we had a better sense of - hierarchy during the evaluation of TEMPER */ - for (card = newcard; card; card = card->nextcard) { - char *cut_line = card->line; - if (ciprefix(".subckt", cut_line)) { - new_str = copy(".param temp = 'temper'"); - nextcard = insert_new_line(card, new_str, 0, 0); - new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'"); - nextcard = insert_new_line(nextcard, new_str, 1, 0); - /* params: replace comma separator by space. - Do nothing if you are inside of { }. */ - char* parastr = strstr(cut_line, "params:"); - int brace = 0; - if (parastr) { - parastr += 8; - while (*parastr) { - if (*parastr == '{') - brace++; - else if (*parastr == '}') - brace--; - if (brace == 0 && *parastr == ',') - *parastr = ' '; - parastr++; - } - } - } - } - - /* .model xxx NMOS/PMOS level=6 --> level = 8, version=3.2.4 - .model xxx NMOS/PMOS level=7 --> level = 8, version=3.2.4 - .model xxx NMOS/PMOS level=5 --> level = 44 - .model xxx NMOS/PMOS level=8 --> level = 14, version=4.5.0 - .model xxx NPN/PNP level=2 --> level = 6 - .model xxx LPNP level=n --> level = 1 subs=-1 - Remove any Monte - Carlo variation parameters from .model cards.*/ - for (card = newcard; card; card = card->nextcard) { - char* cut_line = card->line; - if (ciprefix(".model", cut_line)) { - char* modname, *modtype, *curr_line; - int i; - char *cut_del = curr_line = cut_line = inp_remove_ws(copy(cut_line)); - cut_line = nexttok(cut_line); /* skip .model */ - modname = gettok(&cut_line); /* save model name */ - if (!modname) { - fprintf(stderr, "Error: No model name given for %s\n", curr_line); - controlled_exit(EXIT_BAD); - } - modtype = gettok_noparens(&cut_line); /* save model type */ - if (!modtype) { - fprintf(stderr, "Error: No model type given for %s\n", curr_line); - controlled_exit(EXIT_BAD); - } - if (cieq(modtype, "NMOS") || cieq(modtype, "PMOS")) { - char* lv = strstr(cut_line, "level="); - if (lv) { - int ll; - lv = lv + 6; - char* ntok = gettok(&lv); - ll = atoi(ntok); - switch (ll) { - case 5: - { - /* EKV 2.6 in the adms branch */ - char* newline = tprintf(".model %s %s level=44 %s", modname, modtype, lv); - tfree(card->line); - card->line = curr_line = newline; - } - break; - case 6: - case 7: - { - /* BSIM3 version 3.2.4 */ - char* newline = tprintf(".model %s %s level=8 version=3.2.4 %s", modname, modtype, lv); - tfree(card->line); - card->line = curr_line = newline; - } - break; - case 8: - { - /* BSIM4 version 4.5.0 */ - char* newline = tprintf(".model %s %s level=14 version=4.5.0 %s", modname, modtype, lv); - tfree(card->line); - card->line = curr_line = newline; - } - break; - default: - break; - } - tfree(ntok); - } - } - else if (cieq(modtype, "NPN") || cieq(modtype, "PNP")) { - char* lv = strstr(cut_line, "level="); - if (lv) { - int ll; - lv = lv + 6; - char* ntok = gettok(&lv); - ll = atoi(ntok); - switch (ll) { - case 2: - { - /* MEXTRAM 504.12.1 in the adms branch */ - char* newline = tprintf(".model %s %s level=6 %s", modname, modtype, lv); - tfree(card->line); - card->line = curr_line = newline; - } - break; - default: - break; - } - tfree(ntok); - } - } - else if (cieq(modtype, "LPNP")) { - /* lateral PNP enabled */ - char* newline = tprintf(".model %s PNP level=1 subs=-1 %s", modname, cut_line); - tfree(card->line); - card->line = curr_line = newline; - } - tfree(modname); - tfree(modtype); - - /* Remove any Monte-Carlo variation parameters. They qualify - * a previous parameter, so there must be at least 3 tokens. - * There are two keywords "dev" (different values for each device), - * and "lot" (all devices of this model share a value). - * The keyword may be optionally followed by '/' and - * a probability distribution name, then there must be '=' and - * a value, then an optional '%' indicating relative rather than - * absolute variation. Allow muliple lot and dev on a single .model line. - */ - bool remdevlot = FALSE; - cut_line = curr_line; - for (i = 0; i < 3; i++) - cut_line = nexttok(cut_line); - while (cut_line) { - if (!strncmp(cut_line, "dev=", 4) || - !strncmp(cut_line, "lot=", 4)) { - while (*cut_line && !isspace_c(*cut_line)) { - *cut_line++ = ' '; - } - remdevlot = TRUE; - cut_line = skip_ws(cut_line); - continue; - } - cut_line = nexttok(cut_line); - } - if (remdevlot) { - tfree(card->line); - card->line = curr_line; - } - else - tfree(cut_del); - } // if .model - } // for loop through all cards - - /* x ... params: p1=val1, p2=val2 replace comma separator by space. - Do nothing if you are inside of { }. */ - for (card = newcard; card; card = card->nextcard) { - char* cut_line = card->line; - if (ciprefix("x", cut_line)) { - char* parastr = strstr(cut_line, "params:"); - int brace = 0; - if (parastr) { - parastr += 8; - while (*parastr) { - if (*parastr == '{') - brace++; - else if (*parastr == '}') - brace--; - if (brace == 0 && *parastr == ',') - *parastr = ' '; - parastr++; - } - } - } - } - - /* in R instance, replace TC = xx1, xx2 by TC1=xx1 TC2=xx2 */ - for (card = newcard; card; card = card->nextcard) { - char *cut_line = card->line; - - /* exclude any command inside .control ... .endc */ - if (ciprefix(".control", cut_line)) { - skip_control++; - continue; - } - else if (ciprefix(".endc", cut_line)) { - skip_control--; - continue; - } - else if (skip_control > 0) { - continue; - } - - if (*cut_line == 'r' || *cut_line == 'l' || *cut_line == 'c') { - /* Skip name and two nodes */ - char *ntok = nexttok(cut_line); - ntok = nexttok(ntok); - ntok = nexttok(ntok); - if (!ntok || *ntok == '\0') { - fprintf(stderr, "Error: Missing token in line %d:\n%s\n", - card->linenum, cut_line); - fprintf(stderr, " Please correct the input file\n"); - if (ft_stricterror) - controlled_exit(1); - else - continue; - } - char *tctok = search_plain_identifier(ntok, "tc"); - if (tctok) { - char *tc1, *tc2; - char *tctok1 = strchr(tctok, '='); - if (tctok1) - /* skip '=' */ - tctok1 += 1; - else - /* no '=' found, skip 'tc' */ - tctok1 = tctok + 2; - /* tc1 may be an expression, enclosed in {} */ - if (*tctok1 == '{') { - tc1 = gettok_char(&tctok1, '}', TRUE, TRUE); - } - else { - tc1 = gettok_node(&tctok1); - } - /* skip spaces and commas */ - while (isspace_c(*tctok1) || (*tctok1 == ',')) - tctok1++; - /* tc2 may be an expression, enclosed in {} */ - if (*tctok1 == '{') { - tc2 = gettok_char(&tctok1, '}', TRUE, TRUE); - } - else { - tc2 = gettok_node(&tctok1); - } - tctok[-1] = '\0'; - char *newstring; - if (tc1 && tc2) - newstring = tprintf("%s tc1=%s tc2=%s", - cut_line, tc1, tc2); - else if (tc1) - newstring = tprintf("%s tc1=%s", cut_line, tc1); - else { - fprintf(stderr, - "Warning: tc without parameters removed in line " - "\n %s\n", - cut_line); - continue; - } - tfree(card->line); - card->line = newstring; - tfree(tc1); - tfree(tc2); - } - } - } - - /* replace & with && , | with || , *# with * # , and ~ with ! */ - for (card = newcard; card; card = card->nextcard) { - char *t; - char *cut_line = card->line; - - /* we don't have command lines in a PSPICE model */ - if (ciprefix("*#", cut_line)) { - char *tmpstr = tprintf("* #%s", cut_line + 2); - tfree(card->line); - card->line = tmpstr; - continue; - } - - if (*cut_line == '*') - continue; - - if (*cut_line == '\0') - continue; - - /* exclude any command inside .control ... .endc */ - if (ciprefix(".control", cut_line)) { - skip_control++; - continue; - } - else if (ciprefix(".endc", cut_line)) { - skip_control--; - continue; - } - else if (skip_control > 0) { - continue; - } - if ((t = strstr(card->line, "&")) != NULL) { - while (t && (t[1] != '&')) { - char *tt = NULL; - char *tn = copy(t + 1); /*skip |*/ - char *strbeg = copy_substring(card->line, t); - tfree(card->line); - card->line = tprintf("%s&&%s", strbeg, tn); - tfree(strbeg); - tfree(tn); - t = card->line; - while ((t = strstr(t, "&&")) != NULL) - tt = t = t + 2; - if (!tt) - break; - else - t = strstr(tt, "&"); - } - } - if ((t = strstr(card->line, "|")) != NULL) { - while (t && (t[1] != '|')) { - char *tt = NULL; - char *tn = copy(t + 1); /*skip |*/ - char *strbeg = copy_substring(card->line, t); - tfree(card->line); - card->line = tprintf("%s||%s", strbeg, tn); - tfree(strbeg); - tfree(tn); - t = card->line; - while ((t = strstr(t, "||")) != NULL) - tt = t = t + 2; - if (!tt) - break; - else - t = strstr(tt, "|"); - } - } - /* We may have '~' in path names or A devices */ - if (ciprefix(".inc", card->line) || ciprefix(".lib", card->line) || - ciprefix("A", card->line)) - continue; - - if ((t = strstr(card->line, "~")) != NULL) { - while (t) { - *t = '!'; - t = strstr(t, "~"); - } - } - } - - /* replace T_ABS by temp, T_REL_GLOBAL by dtemp, and T_MEASURED by TNOM - in .model cards. What about T_REL_LOCAL ? T_REL_LOCAL is used in - conjunction with AKO and is not yet implemented. */ - for (card = newcard; card; card = card->nextcard) { - char *cut_line = card->line; - if (ciprefix(".model", cut_line)) { - char *t_str; - if ((t_str = strstr(cut_line, "t_abs")) != NULL) - memcpy(t_str, " temp", 5); - else if ((t_str = strstr(cut_line, "t_rel_global")) != NULL) - memcpy(t_str, " dtemp", 12); - else if ((t_str = strstr(cut_line, "t_measured")) != NULL) - memcpy(t_str, " tnom", 10); - } - } - - /* get the area factor for diodes and bipolar devices - d1 n1 n2 dmod 7 --> d1 n1 n2 dmod area=7 - q2 n1 n2 n3 [n4] bjtmod 1.35 --> q2 n1 n2 n3 n4 bjtmod area=1.35 - q3 1 2 3 4 bjtmod 1.45 --> q2 1 2 3 4 bjtmod area=1.45 - */ - for (card = newcard; card; card = card->nextcard) { - char *cut_line = card->line; - if (*cut_line == '*') - continue; - // exclude any command inside .control ... .endc - if (ciprefix(".control", cut_line)) { - skip_control++; - continue; - } - else if (ciprefix(".endc", cut_line)) { - skip_control--; - continue; - } - else if (skip_control > 0) { - continue; - } - if (*cut_line == 'q') { - /* According to PSPICE Reference Guide the fourth (substrate) node - has to be put into [] if it is not just a number */ - cut_line = nexttok(cut_line); //.model - cut_line = nexttok(cut_line); // node1 - cut_line = nexttok(cut_line); // node2 - cut_line = nexttok(cut_line); // node3 - if (!cut_line || *cut_line == '\0') { - fprintf(stderr, "Line no. %d, %s, missing tokens\n", - card->linenum_orig, card->line); - if (ft_stricterror) - controlled_exit(1); - else - continue; - } - if (*cut_line == '[') { // node4 not a number - *cut_line = ' '; - cut_line = strchr(cut_line, ']'); - *cut_line = ' '; - cut_line = skip_ws(cut_line); - cut_line = nexttok(cut_line); // model name - } - else { // if an integer number, it is node4 - bool is_node4 = TRUE; - while (*cut_line && !isspace_c(*cut_line)) - if (!isdigit_c(*cut_line++)) - is_node4 = FALSE; // already model name - if (is_node4) { - cut_line = nexttok(cut_line); // model name - } - } - if (cut_line && *cut_line && - atof(cut_line) > 0.0) { // size of area is a real number - char *tmpstr1 = copy_substring(card->line, cut_line); - char *tmpstr2 = tprintf("%s area=%s", tmpstr1, cut_line); - tfree(tmpstr1); - tfree(card->line); - card->line = tmpstr2; - } - else if (cut_line && *cut_line && - *(skip_ws(cut_line)) == - '{') { // size of area is parametrized inside {} - char *tmpstr1 = copy_substring(card->line, cut_line); - char *tmpstr2 = gettok_char(&cut_line, '}', TRUE, TRUE); - char *tmpstr3 = - tprintf("%s area=%s %s", tmpstr1, tmpstr2, cut_line); - tfree(tmpstr1); - tfree(tmpstr2); - tfree(card->line); - card->line = tmpstr3; - } - } - else if (*cut_line == 'd') { - cut_line = nexttok(cut_line); //.model - cut_line = nexttok(cut_line); // node1 - cut_line = nexttok(cut_line); // node2 - if (!cut_line || *cut_line == '\0') { - fprintf(stderr, "Line no. %d, %s, missing tokens\n", - card->linenum_orig, card->line); - if (ft_stricterror) - controlled_exit(1); - else - continue; - } - cut_line = nexttok(cut_line); // model name - if (cut_line && *cut_line && - atof(cut_line) > 0.0) { // size of area - char *tmpstr1 = copy_substring(card->line, cut_line); - char *tmpstr2 = tprintf("%s area=%s", tmpstr1, cut_line); - tfree(tmpstr1); - tfree(card->line); - card->line = tmpstr2; - } - } - } - - /* if vswitch part s, replace - * S1 D S DG GND SWN - * .MODEL SWN VSWITCH ( VON = {0.55} VOFF = {0.49} - * RON={1/(2*M*(W/LE)*(KPN/2)*10)} ROFF={1G} ) - * by - * a1 %v(DG) %gd(D S) swa - * .MODEL SWA aswitch(cntl_off=0.49 cntl_on=0.55 r_off=1G - * r_on={1/(2*M*(W/LE)*(KPN/2)*10)} log=TRUE) - * - * if vswitch part s_st, don't replace instance, only model - * replace - * S1 D S DG GND S_ST - * .MODEL S_ST VSWITCH(VT = { 1.5 } VH = { 0.s } - RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) - * by the classical voltage controlled ngspice switch - * S1 D S DG GND S_ST - * .MODEL S_ST SW(VT = { 1.5 } VH = { 0.s } - RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) - * vswitch delay parameter td is not yet supported - - * simple hierachy, as nested subcircuits are not allowed in PSPICE */ - - /* first scan: find the vswitch models, transform them and put the S models - into a list */ - for (card = newcard; card; card = card->nextcard) { - char *str; - static struct card *subcktline = NULL; - static int nesting = 0; - char *cut_line = card->line; - if (ciprefix(".subckt", cut_line)) { - subcktline = card; - nesting++; - } - if (ciprefix(".ends", cut_line)) - nesting--; - - if (ciprefix(".model", card->line) && strstr(card->line, "vswitch")) { - char *modname; - - str = card->line = inp_remove_ws(card->line); - str = nexttok(str); /* throw away '.model' */ - INPgetNetTok(&str, &modname, 0); /* model name */ - if (!ciprefix("vswitch", str)) { - tfree(modname); - continue; - } - str = nexttok_noparens(str); /* throw away 'vswitch' */ - /* S_ST switch (parameters ron, roff, vt, vh) - * we have to find 0 to 4 parameters, identified by 'vh=' etc. - * Parameters not found have to be replaced by their default values. */ - if (strstr(str, "vt=") || strstr(str, "vh=")) { - char* newstr; - char* lstr = copy(str); - char* partstr = strstr(lstr, "ron="); - if (!partstr) { - newstr = tprintf("%s %s", "ron=1.0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - partstr = strstr(lstr, "roff="); - if (!partstr) { - newstr = tprintf("%s %s", "roff=1.0e12", lstr); //default value - tfree(lstr); - lstr = newstr; - } - partstr = strstr(lstr, "vt="); - if (!partstr) { - newstr = tprintf("%s %s", "vt=0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - partstr = strstr(lstr, "vh="); - if (!partstr) { - newstr = tprintf("%s %s", "vh=0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - tfree(card->line); - if (lstr[strlen(lstr) - 1] == ')') - card->line = tprintf(".model %s sw ( %s", modname, lstr); - else - card->line = tprintf(".model %s sw %s", modname, lstr); - tfree(lstr); - tfree(modname); - } - /* S vswitch (parameters ron, roff, von, voff) */ - /* We have to find 0 to 4 parameters, identified by 'von=' etc. and - * replace them by the pswitch code model parameters - * replace VON by cntl_on, VOFF by cntl_off, RON by r_on, and ROFF by r_off. - * Parameters not found have to be replaced by their default values. */ - else if (strstr(str, "von=") || strstr(str, "voff=")) { - char* newstr, *begstr; - char* lstr = copy(str); - /* ron */ - char* partstr = strstr(lstr, "ron="); - if (!partstr) { - newstr = tprintf("%s %s", "r_on=1.0", lstr); //default value - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s r_on%s", begstr, partstr + 3); - tfree(begstr); - } - tfree(lstr); - lstr = newstr; - /* roff */ - partstr = strstr(lstr, "roff="); - if (!partstr) { - newstr = tprintf("%s %s", "r_off=1.0e6", lstr); //default value - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s r_off%s", begstr, partstr + 4); - tfree(begstr); - } - tfree(lstr); - lstr = newstr; - /* von */ - partstr = strstr(lstr, "von="); - if (!partstr) { - newstr = tprintf("%s %s", "cntl_on=1", lstr); //default value - tfree(lstr); - lstr = newstr; - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s cntl_on%s", begstr, partstr + 3); - tfree(begstr); - } - tfree(lstr); - lstr = newstr; - /* voff */ - partstr = strstr(lstr, "voff="); - if (!partstr) { - newstr = tprintf("%s %s", "cntl_off=0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s cntl_off%s", begstr, partstr + 4); - tfree(begstr); - } - tfree(lstr); - lstr = newstr; - tfree(card->line); - if (lstr[strlen(lstr) - 1] == ')') - card->line = tprintf(".model a%s pswitch( log=TRUE %s", modname, lstr); - else - card->line = tprintf(".model a%s pswitch(%s log=TRUE)", modname, lstr); - tfree(lstr); - /* add to list, to change vswitch instance to code model line */ - if (nesting > 0) - modelsfound = insert_new_model( - modelsfound, modname, subcktline->line); - else - modelsfound = insert_new_model(modelsfound, modname, "top"); - tfree(modname); - } - else { - fprintf(stderr, "Error: Bad switch model in line %s\n", card->line); - } - } - } - - /* no need to continue if no vswitch is found */ - if (!modelsfound) - goto iswi; - - /* second scan: find the switch instances s calling a vswitch model and - * transform them */ - for (card = newcard; card; card = card->nextcard) { - static struct card *subcktline = NULL; - static int nesting = 0; - char *cut_line = card->line; - if (*cut_line == '*') - continue; - // exclude any command inside .control ... .endc - if (ciprefix(".control", cut_line)) { - skip_control++; - continue; - } - else if (ciprefix(".endc", cut_line)) { - skip_control--; - continue; - } - else if (skip_control > 0) { - continue; - } - if (ciprefix(".subckt", cut_line)) { - subcktline = card; - nesting++; - } - if (ciprefix(".ends", cut_line)) - nesting--; - - if (ciprefix("s", cut_line)) { - /* check for the model name */ - int i; - bool good = TRUE; - char *stoks[6]; - for (i = 0; i < 6; i++) { - stoks[i] = gettok_node(&cut_line); - if (!stoks[i]) { - fprintf(stderr, "Error: Bad syntax in line:\n %s\n", card->line); - good = FALSE; - break; - } - } - if (!good) { - for (i = 0; i < 6; i++) - tfree(stoks[i]); - continue; - } - /* rewrite s line and replace it if a model is found */ - if ((nesting > 0) && - find_a_model(modelsfound, stoks[5], subcktline->line)) { - tfree(card->line); - card->line = tprintf("a%s %%gd(%s %s) %%gd(%s %s) a%s", - stoks[0], stoks[3], stoks[4], stoks[1], stoks[2], - stoks[5]); - } - /* if model is not within same subcircuit, search at top level */ - else if (find_a_model(modelsfound, stoks[5], "top")) { - tfree(card->line); - card->line = tprintf("a%s %%gd(%s %s) %%gd(%s %s) a%s", - stoks[0], stoks[3], stoks[4], stoks[1], stoks[2], - stoks[5]); - } - for (i = 0; i < 6; i++) - tfree(stoks[i]); - } - } - del_models(modelsfound); - modelsfound = NULL; - -iswi:; - - /* if iswitch part s, replace - * W1 D S VC SWN - * .MODEL SWN ISWITCH ( ION = {0.55} IOFF = {0.49} - * RON={1/(2*M*(W/LE)*(KPN/2)*10)} ROFF={1G} ) - * by - * a1 %v(DG) %gd(D S) swa - * .MODEL SWA aswitch(cntl_off=0.49 cntl_on=0.55 r_off=1G - * r_on={1/(2*M*(W/LE)*(KPN/2)*10)} log=TRUE) - * - * if iswitch part s_st (short transition), don't replace instance, but only model - * replace - * W1 D S VC S_ST - * .MODEL S_ST ISWITCH(IT = { 1.5 } IH = { 0.2 } - RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) - * by the classical current controlled ngspice switch - * W1 D S DG GND S_ST - * .MODEL S_ST CSW(IT = { 1.5 } IH = { 0.2 } - RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) - * iswitch delay parameter td is not yet supported - - * simple hierachy, as nested subcircuits are not allowed in PSPICE */ - - /* first scan: find the iswitch models, transform them and put them into a - * list */ - for (card = newcard; card; card = card->nextcard) { - char* str; - static struct card* subcktline = NULL; - static int nesting = 0; - char* cut_line = card->line; - if (ciprefix(".subckt", cut_line)) { - subcktline = card; - nesting++; - } - if (ciprefix(".ends", cut_line)) - nesting--; - - if (ciprefix(".model", card->line) && strstr(card->line, "iswitch")) { - char* modname; - - card->line = str = inp_remove_ws(card->line); - str = nexttok(str); /* throw away '.model' */ - INPgetNetTok(&str, &modname, 0); /* model name */ - if (!ciprefix("iswitch", str)) { - tfree(modname); - continue; - } - str = nexttok_noparens(str); /* throw away 'iswitch' */ - /* S_ST switch (parameters ron, roff, it, ih) - * we have to find 0 to 4 parameters, identified by 'ih=' etc. - * Parameters not found have to be replaced by their default values. */ - if (strstr(str, "it=") || strstr(str, "ih=")) { - char* newstr; - char* lstr = copy(str); - char* partstr = strstr(lstr, "ron="); - if (!partstr) { - newstr = tprintf("%s %s", "ron=1.0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - partstr = strstr(lstr, "roff="); - if (!partstr) { - newstr = tprintf("%s %s", "roff=1.0e12", lstr); //default value - tfree(lstr); - lstr = newstr; - } - partstr = strstr(lstr, "it="); - if (!partstr) { - newstr = tprintf("%s %s", "it=0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - partstr = strstr(lstr, "ih="); - if (!partstr) { - newstr = tprintf("%s %s", "ih=0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - tfree(card->line); - if (lstr[strlen(lstr) - 1] == ')') - card->line = tprintf(".model %s csw ( %s", modname, lstr); - else - card->line = tprintf(".model %s csw %s", modname, lstr); - tfree(lstr); - tfree(modname); - } - /* S vswitch (parameters ron, roff, ion, ioff) */ - /* We have to find 0 to 4 parameters, identified by 'ion=' etc. and - * replace them by the pswitch code model parameters - * replace VON by cntl_on, VOFF by cntl_off, RON by r_on, and ROFF by r_off. - * Parameters not found have to be replaced by their default values. */ - else if (strstr(str, "ion=") || strstr(str, "ioff=")) { - char* newstr, * begstr; - char* lstr = copy(str); - /* ron */ - char* partstr = strstr(lstr, "ron="); - if (!partstr) { - newstr = tprintf("%s %s", "r_on=1.0", lstr); //default value - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s r_on%s", begstr, partstr + 3); - } - tfree(lstr); - lstr = newstr; - /* roff */ - partstr = strstr(lstr, "roff="); - if (!partstr) { - newstr = tprintf("%s %s", "r_off=1.0e6", lstr); //default value - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s r_off%s", begstr, partstr + 4); - } - tfree(lstr); - lstr = newstr; - /* von */ - partstr = strstr(lstr, "ion="); - if (!partstr) { - newstr = tprintf("%s %s", "cntl_on=1", lstr); //default value - tfree(lstr); - lstr = newstr; - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s cntl_on%s", begstr, partstr + 3); - } - tfree(lstr); - lstr = newstr; - /* voff */ - partstr = strstr(lstr, "ioff="); - if (!partstr) { - newstr = tprintf("%s %s", "cntl_off=0", lstr); //default value - tfree(lstr); - lstr = newstr; - } - else { - begstr = copy_substring(lstr, partstr); - newstr = tprintf("%s cntl_off%s", begstr, partstr + 4); - } - tfree(lstr); - lstr = newstr; - tfree(card->line); - if (lstr[strlen(lstr) - 1] == ')') - card->line = tprintf(".model a%s aswitch( log=TRUE limit=TRUE %s", modname, lstr); - else - card->line = tprintf(".model a%s aswitch(%s log=TRUE limit=TRUE)", modname, lstr); - tfree(lstr); - /* add to list, to change vswitch instance to code model line */ - if (nesting > 0) - modelsfound = insert_new_model( - modelsfound, modname, subcktline->line); - else - modelsfound = insert_new_model(modelsfound, modname, "top"); - tfree(modname); - } - else { - fprintf(stderr, "Error: Bad switch model in line %s\n", card->line); - } - } - } - -#if(0) - /* we have to find 4 parameters, identified by '=', separated by - * spaces */ - char* equalptr[4]; - equalptr[0] = strstr(str, "="); - if (!equalptr[0]) { - fprintf(stderr, - "Error: not enough parameters in iswitch model\n " - "%s\n", - card->line); - controlled_exit(1); - } - for (i = 1; i < 4; i++) { - equalptr[i] = strstr(equalptr[i - 1] + 1, "="); - if (!equalptr[i]) { - fprintf(stderr, - "Error: not enough parameters in iswitch model\n " - " %s\n", - card->line); - controlled_exit(1); - } - } - for (i = 0; i < 4; i++) { - equalptr[i] = skip_back_ws(equalptr[i], str); - while (*(equalptr[i]) != '(' && !isspace_c(*(equalptr[i])) && - *(equalptr[i]) != ',') - (equalptr[i])--; - (equalptr[i])++; - } - for (i = 0; i < 3; i++) - modpar[i] = copy_substring(equalptr[i], equalptr[i + 1] - 1); - if (strrchr(equalptr[3], ')')) - modpar[3] = copy_substring( - equalptr[3], strrchr(equalptr[3], ')')); - else - /* iswitch defined without parens */ - modpar[3] = copy(equalptr[3]); - - /* check if we have parameters IT and IH */ - for (i = 0; i < 4; i++) { - if (ciprefix("ih", modpar[i])) - have_ih = TRUE; - if (ciprefix("it", modpar[i])) - have_it = TRUE; - } - if (have_ih && have_it) { - /* replace iswitch by csw */ - char* vs = strstr(card->line, "iswitch"); - memmove(vs, " csw", 7); - } - else { - /* replace ION by cntl_on, IOFF by cntl_off, RON by r_on, and - * ROFF by r_off */ - tfree(card->line); - rep_spar(modpar); - card->line = tprintf( - /* FIXME: a new switch derived from pswitch with vnam input is due */ - ".model a%s aswitch(%s %s %s %s log=TRUE limit=TRUE)", modname, - modpar[0], modpar[1], modpar[2], modpar[3]); - } - for (i = 0; i < 4; i++) - tfree(modpar[i]); - if (nesting > 0) - modelsfound = insert_new_model( - modelsfound, modname, subcktline->line); - else - modelsfound = insert_new_model(modelsfound, modname, "top"); - tfree(modname); - } - } -#endif - /* no need to continue if no iswitch is found */ - if (!modelsfound) - return newcard; - - /* second scan: find the switch instances s calling an iswitch model and - * transform them */ - for (card = newcard; card; card = card->nextcard) { - static struct card* subcktline = NULL; - static int nesting = 0; - char* cut_line = card->line; - if (*cut_line == '*') - continue; - // exclude any command inside .control ... .endc - if (ciprefix(".control", cut_line)) { - skip_control++; - continue; - } - else if (ciprefix(".endc", cut_line)) { - skip_control--; - continue; - } - else if (skip_control > 0) { - continue; - } - if (ciprefix(".subckt", cut_line)) { - subcktline = card; - nesting++; - } - if (ciprefix(".ends", cut_line)) - nesting--; - - if (ciprefix("w", cut_line)) { - /* check for the model name */ - int i; - char* stoks[5]; - for (i = 0; i < 5; i++) - stoks[i] = gettok_node(&cut_line); - /* rewrite w line and replace it if a model is found */ - if ((nesting > 0) && - find_a_model(modelsfound, stoks[4], subcktline->line)) { - tfree(card->line); - card->line = tprintf("a%s %%vnam(%s) %%gd(%s %s) a%s", - stoks[0], stoks[3], stoks[1], stoks[2], - stoks[4]); - } - /* if model is not within same subcircuit, search at top level */ - else if (find_a_model(modelsfound, stoks[4], "top")) { - tfree(card->line); - card->line = tprintf("a%s %%vnam(%s) %%gd(%s %s) a%s", - stoks[0], stoks[3], stoks[1], stoks[2], - stoks[4]); - } - for (i = 0; i < 5; i++) - tfree(stoks[i]); - } - } - del_models(modelsfound); - - return newcard; -} - - - -/* do not modify oldcard address, insert everything after first line only */ -void pspice_compat_a(struct card *oldcard) -{ - oldcard->nextcard = pspice_compat(oldcard->nextcard); -} - - -/**** LTSPICE to ngspice ************** - * add functions uplim, dnlim - * Replace - * D1 A K SDMOD - * .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 - * Epsilon=0.2 Ilimit=7 Revilimit=7) - * by - * ad1 a k asdmod - * .model asdmod sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 - * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) - * Remove '.backanno' - */ -struct card *ltspice_compat(struct card *oldcard) -{ - struct card *card, *newcard, *nextcard; - struct vsmodels *modelsfound = NULL; - int skip_control = 0; - - - /* remove double braces only if not yet done in pspice_compat() */ - if (!newcompat.ps) - rem_double_braces(oldcard); - - /* add funcs uplim, dnlim to beginning of deck */ - char *new_str = - copy(".func uplim(x, pos, z) { min(x, pos - z) + (1 - " - "(min(max(0, x - pos + z), 2 * z) / 2 / z - 1)**2)*z }"); - newcard = insert_new_line(NULL, new_str, 1, 0); - new_str = copy(".func dnlim(x, neg, z) { max(x, neg + z) - (1 - " - "(min(max(0, -x + neg + z), 2 * z) / 2 / z - 1)**2)*z }"); - nextcard = insert_new_line(newcard, new_str, 2, 0); - new_str = copy(".func uplim_tanh(x, pos, z) { min(x, pos - z) + " - "tanh(max(0, x - pos + z) / z)*z }"); - nextcard = insert_new_line(nextcard, new_str, 3, 0); - new_str = copy(".func dnlim_tanh(x, neg, z) { max(x, neg + z) - " - "tanh(max(0, neg + z - x) / z)*z }"); - nextcard = insert_new_line(nextcard, new_str, 4, 0); - nextcard->nextcard = oldcard; - - /* remove .backanno, replace 'noiseless' by 'moisy=0' */ - for (card = nextcard; card; card = card->nextcard) { - char* cut_line = card->line; - if (ciprefix(".backanno", cut_line)) { - *cut_line = '*'; - } - else if (*cut_line == 'r') { - char* noi = strstr(cut_line, "noiseless"); - /* only if 'noiseless' is an unconnected token */ - if (noi && isspace_c(noi[-1]) && (isspace_c(noi[9]) || !isprint_c(noi[9]))) { - memcpy(noi, "noisy=0 ", 9); - } - } - } - - /* replace - * D1 A K SDMOD - * .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 - * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) - * by - * a1 a k SDMOD - * .model SDMOD sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 - * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) - * Do this if one of the parameters, which are uncommon to standard diode - * model, has been found. - - * simple hierachy, as nested subcircuits are not allowed in PSPICE */ - - /* first scan: find the d models, transform them and put them into a list - */ - for (card = nextcard; card; card = card->nextcard) { - char *str; - static struct card *subcktline = NULL; - static int nesting = 0; - char *cut_line = card->line; - if (*cut_line == '*' || *cut_line == '\0') - continue; - else if (ciprefix(".subckt", cut_line)) { - subcktline = card; - nesting++; - } - else if (ciprefix(".ends", cut_line)) - nesting--; - - else if (ciprefix(".model", card->line) && - search_plain_identifier(card->line, "d")) { - if (search_plain_identifier(card->line, "roff") || - search_plain_identifier(card->line, "ron") || - search_plain_identifier(card->line, "rrev") || - search_plain_identifier(card->line, "vfwd") || - search_plain_identifier(card->line, "vrev") || - search_plain_identifier(card->line, "revepsilon") || - search_plain_identifier(card->line, "epsilon") || - search_plain_identifier(card->line, "revilimit") || - search_plain_identifier(card->line, "ilimit")) { - char *modname; - - /* remove parameter 'noiseless' (the model is noiseless anyway) */ - char *nonoise = search_plain_identifier(card->line, "noiseless"); - if (nonoise) { - size_t iii; - for (iii = 0; iii < 9; iii++) - nonoise[iii] = ' '; - } - card->line = str = inp_remove_ws(card->line); - str = nexttok(str); /* throw away '.model' */ - INPgetNetTok(&str, &modname, 0); /* model name */ - if (!ciprefix("d", str)) { - tfree(modname); - continue; - } - /* skip d */ - str++; - /* we take all the existing parameters */ - char *newstr = copy(str); - tfree(card->line); - card->line = tprintf(".model a%s sidiode%s", modname, newstr); - if (nesting > 0) - modelsfound = insert_new_model( - modelsfound, modname, subcktline->line); - else - modelsfound = - insert_new_model(modelsfound, modname, "top"); - tfree(modname); - tfree(newstr); - } - } - else - continue; - } - - /* no need to continue if no d is found */ - if (!modelsfound) - return newcard; - - /* second scan: find the diode instances d calling a simple diode model - * and transform them */ - for (card = nextcard; card; card = card->nextcard) { - static struct card *subcktline = NULL; - static int nesting = 0; - char *cut_line = card->line; - if (*cut_line == '*') - continue; - if (*cut_line == '\0') - continue; - // exclude any command inside .control ... .endc - if (ciprefix(".control", cut_line)) { - skip_control++; - continue; - } - else if (ciprefix(".endc", cut_line)) { - skip_control--; - continue; - } - else if (skip_control > 0) { - continue; - } - if (ciprefix(".subckt", cut_line)) { - subcktline = card; - nesting++; - } - if (ciprefix(".ends", cut_line)) - nesting--; - - if (ciprefix("d", cut_line)) { - /* check for the model name */ - int i; - char *stoks[4]; - for (i = 0; i < 4; i++) { - stoks[i] = gettok_node(&cut_line); - if (stoks[i] == NULL) { - fprintf(stderr, "Error in line %d: buggy diode instance line\n %s\n", card->linenum_orig, card->line); - fprintf(stderr, "At least 'Dxx n1 n2 d' is required.\n"); - controlled_exit(EXIT_BAD); - } - } - /* rewrite d line and replace it if a model is found */ - if ((nesting > 0) && - find_a_model(modelsfound, stoks[3], subcktline->line)) { - tfree(card->line); - card->line = tprintf("a%s %s %s a%s", - stoks[0], stoks[1], stoks[2], stoks[3]); - } - /* if model is not within same subcircuit, search at top level */ - else if (find_a_model(modelsfound, stoks[3], "top")) { - tfree(card->line); - card->line = tprintf("a%s %s %s a%s", - stoks[0], stoks[1], stoks[2], stoks[3]); - } - for (i = 0; i < 4; i++) - tfree(stoks[i]); - } - } - del_models(modelsfound); - - return newcard; -} - -/* do not modify oldcard address, insert everything after first line only */ -void ltspice_compat_a(struct card *oldcard) -{ - oldcard->nextcard = ltspice_compat(oldcard->nextcard); -} +/********** +Copyright 2023 The ngspice team. All rights reserved. +License: Three-clause BCD +Author: 2023 Holger Vogt +**********/ + +/* + For dealing with compatibility transformations + + PSPICE, LTSPICE and others +*/ + +#include "ngspice/ngspice.h" + +#include "ngspice/compatmode.h" +#include "ngspice/cpdefs.h" +#include "ngspice/dstring.h" +#include "ngspice/dvec.h" +#include "ngspice/ftedefs.h" +#include "ngspice/fteext.h" +#include "ngspice/fteinp.h" +#include "numparam/general.h" + +#include +#include + +#include +#include + +#if !defined(__MINGW32__) && !defined(_MSC_VER) +#include +#endif + +#include "../misc/util.h" /* ngdirname() */ +#include "inpcom.h" +#include "ngspice/stringskip.h" +#include "ngspice/stringutil.h" +#include "ngspice/wordlist.h" +#include "subckt.h" +#include "variable.h" + +#define INTEGRATE_UDEVICES +#ifdef INTEGRATE_UDEVICES +#include "ngspice/udevices.h" +#endif + +void print_compat_mode(void); +void set_compat_mode(void); +struct card* pspice_compat(struct card* newcard); +void pspice_compat_a(struct card* oldcard); +struct card* ltspice_compat(struct card* oldcard); +void ltspice_compat_a(struct card* oldcard); + + + +/* Set a compatibility flag. +Currently available are flags for: +- LTSPICE, HSPICE, Spice3, PSPICE, KiCad, Spectre, XSPICE +*/ +struct compat newcompat; +void set_compat_mode(void) +{ + char behaviour[80]; + newcompat.hs = FALSE; + newcompat.ps = FALSE; + newcompat.xs = FALSE; + newcompat.lt = FALSE; + newcompat.ki = FALSE; + newcompat.a = FALSE; + newcompat.spe = FALSE; + newcompat.isset = FALSE; + newcompat.s3 = FALSE; + newcompat.mc = FALSE; + if (cp_getvar("ngbehavior", CP_STRING, behaviour, sizeof(behaviour))) { + if (strstr(behaviour, "hs")) + newcompat.isset = newcompat.hs = TRUE; /*HSPICE*/ + if (strstr(behaviour, "ps")) + newcompat.isset = newcompat.ps = TRUE; /*PSPICE*/ + if (strstr(behaviour, "xs")) + newcompat.isset = newcompat.xs = TRUE; /*XSPICE*/ + if (strstr(behaviour, "lt")) + newcompat.isset = newcompat.lt = TRUE; /*LTSPICE*/ + if (strstr(behaviour, "ki")) + newcompat.isset = newcompat.ki = TRUE; /*KiCad*/ + if (strstr(behaviour, "a")) + newcompat.isset = newcompat.a = TRUE; /*complete netlist, used in conjuntion with other mode*/ + if (strstr(behaviour, "ll")) + newcompat.isset = newcompat.ll = TRUE; /*all (currently not used)*/ + if (strstr(behaviour, "s3")) + newcompat.isset = newcompat.s3 = TRUE; /*spice3 only*/ + if (strstr(behaviour, "eg")) + newcompat.isset = newcompat.eg = TRUE; /*EAGLE*/ + if (strstr(behaviour, "spe")) { + newcompat.isset = newcompat.spe = TRUE; /*Spectre*/ + newcompat.ps = newcompat.lt = newcompat.ki = newcompat.eg = FALSE; + } + if (strstr(behaviour, "mc")) { + newcompat.isset = FALSE; + newcompat.mc = TRUE; /*make check*/ + } + } + if (newcompat.hs && newcompat.ps) { + fprintf(stderr, "Warning: hs and ps compatibility are mutually exclusive, switch to ps!\n"); + newcompat.hs = FALSE; + } + /* reset everything for 'make check' */ + if (newcompat.mc) + newcompat.eg = newcompat.hs = newcompat.spe = newcompat.ps = newcompat.xs = + newcompat.ll = newcompat.lt = newcompat.ki = newcompat.a = FALSE; +} + +/* Print the compatibility flags */ +void print_compat_mode(void) { + if (newcompat.mc) /* make check */ + return; + if (newcompat.isset) { + fprintf(stdout, "\n"); + fprintf(stdout, "Note: Compatibility modes selected:"); + if (newcompat.hs) + fprintf(stdout, " hs"); + if (newcompat.ps) + fprintf(stdout, " ps"); + if (newcompat.xs) + fprintf(stdout, " xs"); + if (newcompat.lt) + fprintf(stdout, " lt"); + if (newcompat.ki) + fprintf(stdout, " ki"); + if (newcompat.ll) + fprintf(stdout, " ll"); + if (newcompat.s3) + fprintf(stdout, " s3"); + if (newcompat.eg) + fprintf(stdout, " eg"); + if (newcompat.spe) + fprintf(stdout, " spe"); + if (newcompat.a) + fprintf(stdout, " a"); + fprintf(stdout, "\n\n"); + } + else { + fprintf(stdout, "\n"); + fprintf(stdout, "Note: No compatibility mode selected!\n\n"); + } +} + + +/* replace the E and G source TABLE function by a B source pwl + * (used by ST OpAmps and comparators of Infineon models). + * E_RO_3 VB_3 VB_4 VALUE={ TABLE( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 + * )*I(VreadIo)} + * will become + * BE_RO_3_1 TABLE_NEW_1 0 v = pwl( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10) + * E_RO_3 VB_3 VB_4 VALUE={ V(TABLE_NEW_1)*I(VreadIo)} + */ +static void replace_table(struct card *startcard) +{ + struct card *card; + static int numb = 0; + for (card = startcard; card; card = card->nextcard) { + char *cut_line = card->line; + if (*cut_line == 'e' || *cut_line == 'g') { + char *valp = search_plain_identifier(cut_line, "value"); + char *valp2 = search_plain_identifier(cut_line, "cur"); + if (valp || (valp2 && *cut_line == 'g')) { + char *ftablebeg = strstr(cut_line, "table("); + while (ftablebeg) { + /* get the beginning of the line */ + char *begline = copy_substring(cut_line, ftablebeg); + /* get the table function */ + char *tabfun = gettok_char(&ftablebeg, ')', TRUE, TRUE); + /* the new e, g line */ + char *neweline = tprintf("%s v(table_new_%d)%s", + begline, numb, ftablebeg); + char *newbline = + tprintf("btable_new_%d table_new_%d 0 v=pwl%s", + numb, numb, tabfun + 5); + numb++; + tfree(tabfun); + tfree(begline); + tfree(card->line); + card->line = cut_line = neweline; + insert_new_line(card, newbline, 0, card->linenum_orig); + /* read next TABLE function in cut_line */ + ftablebeg = strstr(cut_line, "table("); + } + continue; + } + } + } +} + +/* find the model requested by ako:model and do the replacement */ +static struct card *find_model(struct card *startcard, + struct card *changecard, char *searchname, char *newmname, + char *newmtype, char *endstr) +{ + struct card *nomod, *returncard = changecard; + char *origmname, *origmtype; + char *beginline = startcard->line; + if (ciprefix(".subckt", beginline)) + startcard = startcard->nextcard; + + int nesting2 = 0; + for (nomod = startcard; nomod; nomod = nomod->nextcard) { + char *origmodline = nomod->line; + if (ciprefix(".subckt", origmodline)) + nesting2++; + if (ciprefix(".ends", origmodline)) + nesting2--; + /* skip any subcircuit */ + if (nesting2 > 0) + continue; + if (nesting2 == -1) { + returncard = changecard; + break; + } + if (ciprefix(".model", origmodline)) { + origmodline = nexttok(origmodline); + origmname = gettok(&origmodline); + origmtype = gettok_noparens(&origmodline); + if (cieq(origmname, searchname)) { + if (!eq(origmtype, newmtype)) { + fprintf(stderr, + "Error: Original (%s) and new (%s) type for AKO " + "model disagree\n", + origmtype, newmtype); + controlled_exit(1); + } + /* we have got it */ + char *newmodcard = tprintf(".model %s %s %s%s", + newmname, newmtype, origmodline, endstr); + char *tmpstr = strstr(newmodcard, ")("); + if (tmpstr) { + tmpstr[0] = ' '; + tmpstr[1] = ' '; + } + tfree(changecard->line); + changecard->line = newmodcard; + tfree(origmname); + tfree(origmtype); + returncard = NULL; + break; + } + tfree(origmname); + tfree(origmtype); + } + else + returncard = changecard; + } + return returncard; +} + +/* Process any .distribution cards for PSPICE's Monte-Carlo feature. + * A .distribution card defines a probability distribution by a PWL + * density function. This could be rewritten as a function that + * returns a random value following that distribution. + * For now, just comment it away. + */ +static void do_distribution(struct card *oldcard) { + while (oldcard) { + char *line = oldcard->line; + + if (line && ciprefix(".distribution", line)) + *line = '*'; + oldcard = oldcard->nextcard; + } +} + +/* Do the .model replacement required by ako (a kind of) + * PSPICE does not support nested .subckt definitions, so + * a simple structure is needed: search for ako:modelname, + * then for modelname in the subcircuit or in the top level. + * .model qorig npn (BF=48 IS=2e-7) + * .model qbip1 ako:qorig NPN (BF=60 IKF=45m) + * after the replacement we have + * .model qbip1 NPN (BF=48 IS=2e-7 BF=60 IKF=45m) + * and we benefit from the fact that if parameters have + * doubled, the last entry of a parameter (e.g. BF=60) + * overwrites the previous one (BF=48). + */ +static struct card *ako_model(struct card *startcard) +{ + char *newmname, *newmtype; + struct card *card, *returncard = NULL, *subcktcard = NULL; + for (card = startcard; card; card = card->nextcard) { + char *akostr, *searchname; + char *cut_line = card->line; + + if (ciprefix(".subckt", cut_line)) + subcktcard = card; + else if (ciprefix(".ends", cut_line)) + subcktcard = NULL; + if (ciprefix(".model", cut_line)) { + if ((akostr = strstr(cut_line, "ako:")) != NULL && + isspace_c(akostr[-1])) { + akostr += 4; + searchname = gettok(&akostr); + cut_line = nexttok(cut_line); + newmname = gettok(&cut_line); + newmtype = gettok_noparens(&akostr); + + /* Find the model and do the replacement. */ + + if (subcktcard) + returncard = find_model(subcktcard, card, searchname, + newmname, newmtype, akostr); + if (returncard || !subcktcard) + returncard = find_model(startcard, card, searchname, + newmname, newmtype, akostr); + tfree(searchname); + tfree(newmname); + tfree(newmtype); + + /* Replacement not possible, bail out. */ + + if (returncard) + break; + } + } + } + return returncard; +} + +struct vsmodels { + char *modelname; + char *subcktline; + struct vsmodels *nextmodel; +}; + +/* insert a new model, just behind the given model */ +static struct vsmodels *insert_new_model( + struct vsmodels *vsmodel, char *name, char *subcktline) +{ + struct vsmodels *x = TMALLOC(struct vsmodels, 1); + + x->nextmodel = vsmodel ? vsmodel->nextmodel : NULL; + x->modelname = copy(name); + x->subcktline = copy(subcktline); + if (vsmodel) + vsmodel->nextmodel = x; + else + vsmodel = x; + + return vsmodel; +} + +/* find the model */ +static bool find_a_model( + struct vsmodels *vsmodel, char *name, char *subcktline) +{ + struct vsmodels *x; + for (x = vsmodel; vsmodel; vsmodel = vsmodel->nextmodel) + if (eq(vsmodel->modelname, name) && + eq(vsmodel->subcktline, subcktline)) + return TRUE; + return FALSE; +} + +/* delete the vsmodels list */ +static bool del_models(struct vsmodels *vsmodel) +{ + struct vsmodels *x; + + if (!vsmodel) + return FALSE; + + while (vsmodel) { + x = vsmodel->nextmodel; + tfree(vsmodel->modelname); + tfree(vsmodel->subcktline); + tfree(vsmodel); + vsmodel = x; + } + + return TRUE; +} + +/* Check for double '{', replace the inner '{', '}' by '(', ')' + in .subckt or .model (which both may stem from external sources) */ +static void rem_double_braces(struct card* newcard) +{ + struct card* card; + int slevel = 0; + + for (card = newcard; card; card = card->nextcard) { + char* cut_line = card->line; + if (ciprefix(".subckt", cut_line)) + slevel++; + else if (ciprefix(".ends", cut_line)) + slevel--; + if (ciprefix(".model", cut_line) || slevel > 0) { + cut_line = strchr(cut_line, '{'); + if (cut_line) { + int level = 1; + cut_line++; + while (*cut_line != '\0') { + if (*cut_line == '{') { + level++; + if (level > 1) + *cut_line = '('; + } + else if (*cut_line == '}') { + if (level > 1) + *cut_line = ')'; + level--; + } + cut_line++; + } + } + } + } +} + +#ifdef INTEGRATE_UDEVICES +static void list_the_cards(struct card *startcard, char *prefix) +{ + struct card *card; + if (!startcard) { return; } + for (card = startcard; card; card = card->nextcard) { + char* cut_line = card->line; + printf("%s %s\n", prefix, cut_line); + } +} + +static struct card *the_last_card(struct card *startcard) +{ + struct card *card, *lastcard = NULL; + if (!startcard) { return NULL; } + for (card = startcard; card; card = card->nextcard) { + lastcard = card; + } + return lastcard; +} + static void remove_old_cards(struct card *first, struct card *stop) +{ + struct card *x, *y, *next = NULL, *nexta = NULL; + if (!first || !stop || (first == stop)) { return; } + for (x = first; (x && (x != stop)); x = next) { + if (x->line) { tfree(x->line); } + if (x->error) { tfree(x->error); } + for (y = x->actualLine; y; y = nexta) { + if (y->line) { tfree(y->line); } + if (y->error) { tfree(y->error); } + nexta = y->nextcard; + tfree(y); + } + next = x->nextcard; + tfree(x); + } + +} + +static struct card *u_instances(struct card *startcard) +{ + struct card *card, *returncard = NULL, *subcktcard = NULL; + struct card *newcard = NULL, *last_newcard = NULL; + int models_ok = 0, models_not_ok = 0; + int udev_ok = 0, udev_not_ok = 0; + BOOL create_called = FALSE, repeat_pass = FALSE; + BOOL skip_next = FALSE; + struct card *c = startcard; + BOOL insub = FALSE; + int ps_global_tmodels = 0; + + if (!cp_getvar("ps_global_tmodels", CP_NUM, &ps_global_tmodels, 0)) { + ps_global_tmodels = 0; + } + if (ps_global_tmodels) { + initialize_udevice(NULL); + /* First scan for global timing models */ + while (c) { + char *line = c->line; + if (ciprefix(".subckt", line)) { + insub = TRUE; + } else if (ciprefix(".ends", line)) { + insub = FALSE; + } + if (!insub && ciprefix(".model", line)) { + (void) u_process_model_line(line, TRUE); + } + c = c->nextcard; + } + } + + /* Now scan for subckts containing U* instances and local timing models */ + card = startcard; + while (card) { + char *cut_line = card->line; + + skip_next = FALSE; + if (ciprefix(".subckt", cut_line)) { + models_ok = models_not_ok = 0; + udev_ok = udev_not_ok = 0; + subcktcard = card; + if (!repeat_pass) { + if (create_called) { + cleanup_udevice(FALSE); + } + initialize_udevice(subcktcard->line); + create_called = TRUE; + } + } else if (ciprefix(".ends", cut_line)) { + if (repeat_pass) { + newcard = replacement_udevice_cards(); + if (newcard) { + char *tmp = NULL, *pos, *posp, *new_str = NULL, *cl; + DS_CREATE(ds_tmp, 128); + /* Pspice definition of .subckt card: + .SUBCKT [node]* + + [OPTIONAL: < = >*] + + [PARAMS: < = >* ] + + [TEXT: < = >* ] + ... + .ENDS + */ + cl = subcktcard->line; + tmp = TMALLOC(char, strlen(cl) + 1); + (void) memcpy(tmp, cl, strlen(cl) + 1); + pos = strstr(tmp, "optional:"); + posp = strstr(tmp, "params:"); + ds_clear(&ds_tmp); + /* If there is an optional: and a param: then posp > pos */ + if (pos) { + /* Remove the optional: section if present */ + *pos = '\0'; + if (posp) { + ds_cat_str(&ds_tmp, tmp); + ds_cat_str(&ds_tmp, posp); + new_str = copy(ds_get_buf(&ds_tmp)); + } else { + new_str = copy(tmp); + } + } else { + new_str = copy(tmp); + } + ds_free(&ds_tmp); + tfree(tmp); + remove_old_cards(subcktcard->nextcard, card); + subcktcard->nextcard = newcard; + tfree(subcktcard->line); + subcktcard->line = new_str; + if (ft_ngdebug) { + printf("%s\n", new_str); + list_the_cards(newcard, "Replacement:"); + } + last_newcard = the_last_card(newcard); + if (last_newcard) { + last_newcard->nextcard = card; // the .ends card + } + } else { + models_ok = models_not_ok = 0; + udev_ok = udev_not_ok = 0; + } + } + if (models_not_ok > 0 || udev_not_ok > 0) { + repeat_pass = FALSE; + cleanup_udevice(FALSE); + create_called = FALSE; + } else if (udev_ok > 0) { + repeat_pass = TRUE; + card = subcktcard; + skip_next = TRUE; + } else { + repeat_pass = FALSE; + cleanup_udevice(FALSE); + create_called = FALSE; + } + subcktcard = NULL; + } else if (ciprefix(".model", cut_line)) { + if (subcktcard && !repeat_pass) { + // Add .model local to subckt + if (!u_process_model_line(cut_line, FALSE)) { + models_not_ok++; + } else { + models_ok++; + } + } + } else if (ciprefix("u", cut_line)) { + if (subcktcard) { + if (repeat_pass) { + if (!u_process_instance(cut_line)) { + repeat_pass = FALSE; + cleanup_udevice(FALSE); + create_called = FALSE; + subcktcard = NULL; + models_ok = models_not_ok = 0; + udev_ok = udev_not_ok = 0; + skip_next = FALSE; + } + } else { + if (u_check_instance(cut_line)) { + udev_ok++; + } else { + udev_not_ok++; + } + } + } + } else { + if (!ciprefix("*", cut_line)) { + udev_not_ok++; + } + } + + if (!skip_next) { + card = card->nextcard; + } + } + if (create_called) { + cleanup_udevice(FALSE); + } + cleanup_udevice(TRUE); + return returncard; +} +#endif + +/**** PSPICE to ngspice ************** +* .model replacement in ako (a kind of) model descriptions +* replace the E source TABLE function by a B source pwl +* add predefined params TEMP, VT, GMIN to beginning of deck +* add predefined params TEMP, VT, GMIN to beginning of each .subckt call +* add .functions limit, pwr, pwrs, stp, if, int +* replace vswitch part S + S1 D S DG GND SWN + .MODEL SWN VSWITCH(VON = { 0.55 } VOFF = { 0.49 } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) +* by + as1 %vd(DG GND) % gd(D S) aswn + .model aswn aswitch(cntl_off={0.49} cntl_on={0.55} r_off={1G} + + r_on={ 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } log = TRUE) +* replace vswitch part S_ST + S1 D S DG GND S_ST + .MODEL S_ST VSWITCH(VT = { 1.5 } VH = { 0.3 } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) +* by the classical voltage controlled ngspice switch + S1 D S DG GND SWN + .MODEL S_ST SW(VT = { 1.5 } VH = { 0.3 } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) + switch parameter td is not yet supported +* replace & by && +* replace | by || +* in R instance, replace TC = xx1, xx2 by TC1=xx1 TC2=xx2 +* replace T_ABS by temp and T_REL_GLOBAL by dtemp in .model cards +* get the area factor for diodes and bipolar devices +* in subcircuit .subckt and X lines with 'params:' statement + replace comma separator by space. Do nothing if comma is inside of {}. +* in .model, if double curly braces {{}}, replace the inner by {()} */ +struct card *pspice_compat(struct card *oldcard) +{ + struct card *card, *newcard, *nextcard; + struct vsmodels *modelsfound = NULL; + int skip_control = 0; + + /* .model replacement in ako (a kind of) model descriptions + * in first .subckt and top level only */ + struct card *errcard; + if ((errcard = ako_model(oldcard)) != NULL) { + fprintf(stderr, "Error: no model found for %s\n", errcard->line); + controlled_exit(1); + } + + /* Process .distribution cards. */ + do_distribution(oldcard); + + /* replace TABLE function in E source */ + replace_table(oldcard); + + /* remove double braces */ + rem_double_braces(oldcard); + + /* add predefined params TEMP, VT, GMIN to beginning of deck */ + char *new_str = copy(".param temp = 'temper'"); + newcard = insert_new_line(NULL, new_str, 1, 0); + new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'"); + nextcard = insert_new_line(newcard, new_str, 2, 0); + new_str = copy(".param gmin = 1e-12"); + nextcard = insert_new_line(nextcard, new_str, 3, 0); + /* add funcs limit, pwr, pwrs, stp, if, int */ + /* LIMIT( Output Expression, Limit1, Limit2) + Output will stay between the two limits given. */ + new_str = copy(".func limit(x, a, b) { ternary_fcn(a > b, max(min(x, a), b), max(min(x, b), a)) }"); + nextcard = insert_new_line(nextcard, new_str, 4, 0); + new_str = copy(".func pwr(x, a) { pow(x, a) }"); + nextcard = insert_new_line(nextcard, new_str, 5, 0); + new_str = copy(".func pwrs(x, a) { sgn(x) * pow(x, a) }"); + nextcard = insert_new_line(nextcard, new_str, 6, 0); + new_str = copy(".func stp(x) { u(x) }"); + nextcard = insert_new_line(nextcard, new_str, 7, 0); + new_str = copy(".func if(a, b, c) {ternary_fcn( a , b , c )}"); + nextcard = insert_new_line(nextcard, new_str, 8, 0); + new_str = copy(".func int(x) { sign(x)*floor(abs(x)) }"); + nextcard = insert_new_line(nextcard, new_str, 9, 0); + nextcard->nextcard = oldcard; + +#ifdef INTEGRATE_UDEVICES + { + struct card *ucard; +#ifdef TRACE + list_the_cards(newcard, "Before udevices"); +#endif + ucard = u_instances(newcard); +#ifdef TRACE + list_the_cards(newcard, "After udevices"); +#endif + } +#endif + + + /* add predefined parameters TEMP, VT after each subckt call */ + /* FIXME: This should not be necessary if we had a better sense of + hierarchy during the evaluation of TEMPER */ + for (card = newcard; card; card = card->nextcard) { + char *cut_line = card->line; + if (ciprefix(".subckt", cut_line)) { + new_str = copy(".param temp = 'temper'"); + nextcard = insert_new_line(card, new_str, 0, 0); + new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'"); + nextcard = insert_new_line(nextcard, new_str, 1, 0); + /* params: replace comma separator by space. + Do nothing if you are inside of { }. */ + char* parastr = strstr(cut_line, "params:"); + int brace = 0; + if (parastr) { + parastr += 8; + while (*parastr) { + if (*parastr == '{') + brace++; + else if (*parastr == '}') + brace--; + if (brace == 0 && *parastr == ',') + *parastr = ' '; + parastr++; + } + } + } + } + + /* .model xxx NMOS/PMOS level=6 --> level = 8, version=3.2.4 + .model xxx NMOS/PMOS level=7 --> level = 8, version=3.2.4 + .model xxx NMOS/PMOS level=5 --> level = 44 + .model xxx NMOS/PMOS level=8 --> level = 14, version=4.5.0 + .model xxx NPN/PNP level=2 --> level = 6 + .model xxx LPNP level=n --> level = 1 subs=-1 + Remove any Monte - Carlo variation parameters from .model cards.*/ + for (card = newcard; card; card = card->nextcard) { + char* cut_line = card->line; + if (ciprefix(".model", cut_line)) { + char* modname, *modtype, *curr_line; + int i; + char *cut_del = curr_line = cut_line = inp_remove_ws(copy(cut_line)); + cut_line = nexttok(cut_line); /* skip .model */ + modname = gettok(&cut_line); /* save model name */ + if (!modname) { + fprintf(stderr, "Error: No model name given for %s\n", curr_line); + controlled_exit(EXIT_BAD); + } + modtype = gettok_noparens(&cut_line); /* save model type */ + if (!modtype) { + fprintf(stderr, "Error: No model type given for %s\n", curr_line); + controlled_exit(EXIT_BAD); + } + if (cieq(modtype, "NMOS") || cieq(modtype, "PMOS")) { + char* lv = strstr(cut_line, "level="); + if (lv) { + int ll; + lv = lv + 6; + char* ntok = gettok(&lv); + ll = atoi(ntok); + switch (ll) { + case 5: + { + /* EKV 2.6 in the adms branch */ + char* newline = tprintf(".model %s %s level=44 %s", modname, modtype, lv); + tfree(card->line); + card->line = curr_line = newline; + } + break; + case 6: + case 7: + { + /* BSIM3 version 3.2.4 */ + char* newline = tprintf(".model %s %s level=8 version=3.2.4 %s", modname, modtype, lv); + tfree(card->line); + card->line = curr_line = newline; + } + break; + case 8: + { + /* BSIM4 version 4.5.0 */ + char* newline = tprintf(".model %s %s level=14 version=4.5.0 %s", modname, modtype, lv); + tfree(card->line); + card->line = curr_line = newline; + } + break; + default: + break; + } + tfree(ntok); + } + } + else if (cieq(modtype, "NPN") || cieq(modtype, "PNP")) { + char* lv = strstr(cut_line, "level="); + if (lv) { + int ll; + lv = lv + 6; + char* ntok = gettok(&lv); + ll = atoi(ntok); + switch (ll) { + case 2: + { + /* MEXTRAM 504.12.1 in the adms branch */ + char* newline = tprintf(".model %s %s level=6 %s", modname, modtype, lv); + tfree(card->line); + card->line = curr_line = newline; + } + break; + default: + break; + } + tfree(ntok); + } + } + else if (cieq(modtype, "LPNP")) { + /* lateral PNP enabled */ + char* newline = tprintf(".model %s PNP level=1 subs=-1 %s", modname, cut_line); + tfree(card->line); + card->line = curr_line = newline; + } + tfree(modname); + tfree(modtype); + + /* Remove any Monte-Carlo variation parameters. They qualify + * a previous parameter, so there must be at least 3 tokens. + * There are two keywords "dev" (different values for each device), + * and "lot" (all devices of this model share a value). + * The keyword may be optionally followed by '/' and + * a probability distribution name, then there must be '=' and + * a value, then an optional '%' indicating relative rather than + * absolute variation. Allow muliple lot and dev on a single .model line. + */ + bool remdevlot = FALSE; + cut_line = curr_line; + for (i = 0; i < 3; i++) + cut_line = nexttok(cut_line); + while (cut_line) { + if (!strncmp(cut_line, "dev=", 4) || + !strncmp(cut_line, "lot=", 4)) { + while (*cut_line && !isspace_c(*cut_line)) { + *cut_line++ = ' '; + } + remdevlot = TRUE; + cut_line = skip_ws(cut_line); + continue; + } + cut_line = nexttok(cut_line); + } + if (remdevlot) { + tfree(card->line); + card->line = curr_line; + } + else + tfree(cut_del); + } // if .model + } // for loop through all cards + + /* x ... params: p1=val1, p2=val2 replace comma separator by space. + Do nothing if you are inside of { }. */ + for (card = newcard; card; card = card->nextcard) { + char* cut_line = card->line; + if (ciprefix("x", cut_line)) { + char* parastr = strstr(cut_line, "params:"); + int brace = 0; + if (parastr) { + parastr += 8; + while (*parastr) { + if (*parastr == '{') + brace++; + else if (*parastr == '}') + brace--; + if (brace == 0 && *parastr == ',') + *parastr = ' '; + parastr++; + } + } + } + } + + /* in R instance, replace TC = xx1, xx2 by TC1=xx1 TC2=xx2 */ + for (card = newcard; card; card = card->nextcard) { + char *cut_line = card->line; + + /* exclude any command inside .control ... .endc */ + if (ciprefix(".control", cut_line)) { + skip_control++; + continue; + } + else if (ciprefix(".endc", cut_line)) { + skip_control--; + continue; + } + else if (skip_control > 0) { + continue; + } + + if (*cut_line == 'r' || *cut_line == 'l' || *cut_line == 'c') { + /* Skip name and two nodes */ + char *ntok = nexttok(cut_line); + ntok = nexttok(ntok); + ntok = nexttok(ntok); + if (!ntok || *ntok == '\0') { + fprintf(stderr, "Error: Missing token in line %d:\n%s\n", + card->linenum, cut_line); + fprintf(stderr, " Please correct the input file\n"); + if (ft_stricterror) + controlled_exit(1); + else + continue; + } + char *tctok = search_plain_identifier(ntok, "tc"); + if (tctok) { + char *tc1, *tc2; + char *tctok1 = strchr(tctok, '='); + if (tctok1) + /* skip '=' */ + tctok1 += 1; + else + /* no '=' found, skip 'tc' */ + tctok1 = tctok + 2; + /* tc1 may be an expression, enclosed in {} */ + if (*tctok1 == '{') { + tc1 = gettok_char(&tctok1, '}', TRUE, TRUE); + } + else { + tc1 = gettok_node(&tctok1); + } + /* skip spaces and commas */ + while (isspace_c(*tctok1) || (*tctok1 == ',')) + tctok1++; + /* tc2 may be an expression, enclosed in {} */ + if (*tctok1 == '{') { + tc2 = gettok_char(&tctok1, '}', TRUE, TRUE); + } + else { + tc2 = gettok_node(&tctok1); + } + tctok[-1] = '\0'; + char *newstring; + if (tc1 && tc2) + newstring = tprintf("%s tc1=%s tc2=%s", + cut_line, tc1, tc2); + else if (tc1) + newstring = tprintf("%s tc1=%s", cut_line, tc1); + else { + fprintf(stderr, + "Warning: tc without parameters removed in line " + "\n %s\n", + cut_line); + continue; + } + tfree(card->line); + card->line = newstring; + tfree(tc1); + tfree(tc2); + } + } + } + + /* replace & with && , | with || , *# with * # , and ~ with ! */ + for (card = newcard; card; card = card->nextcard) { + char *t; + char *cut_line = card->line; + + /* we don't have command lines in a PSPICE model */ + if (ciprefix("*#", cut_line)) { + char *tmpstr = tprintf("* #%s", cut_line + 2); + tfree(card->line); + card->line = tmpstr; + continue; + } + + if (*cut_line == '*') + continue; + + if (*cut_line == '\0') + continue; + + /* exclude any command inside .control ... .endc */ + if (ciprefix(".control", cut_line)) { + skip_control++; + continue; + } + else if (ciprefix(".endc", cut_line)) { + skip_control--; + continue; + } + else if (skip_control > 0) { + continue; + } + if ((t = strstr(card->line, "&")) != NULL) { + while (t && (t[1] != '&')) { + char *tt = NULL; + char *tn = copy(t + 1); /*skip |*/ + char *strbeg = copy_substring(card->line, t); + tfree(card->line); + card->line = tprintf("%s&&%s", strbeg, tn); + tfree(strbeg); + tfree(tn); + t = card->line; + while ((t = strstr(t, "&&")) != NULL) + tt = t = t + 2; + if (!tt) + break; + else + t = strstr(tt, "&"); + } + } + if ((t = strstr(card->line, "|")) != NULL) { + while (t && (t[1] != '|')) { + char *tt = NULL; + char *tn = copy(t + 1); /*skip |*/ + char *strbeg = copy_substring(card->line, t); + tfree(card->line); + card->line = tprintf("%s||%s", strbeg, tn); + tfree(strbeg); + tfree(tn); + t = card->line; + while ((t = strstr(t, "||")) != NULL) + tt = t = t + 2; + if (!tt) + break; + else + t = strstr(tt, "|"); + } + } + /* We may have '~' in path names or A devices */ + if (ciprefix(".inc", card->line) || ciprefix(".lib", card->line) || + ciprefix("A", card->line)) + continue; + + if ((t = strstr(card->line, "~")) != NULL) { + while (t) { + *t = '!'; + t = strstr(t, "~"); + } + } + } + + /* replace T_ABS by temp, T_REL_GLOBAL by dtemp, and T_MEASURED by TNOM + in .model cards. What about T_REL_LOCAL ? T_REL_LOCAL is used in + conjunction with AKO and is not yet implemented. */ + for (card = newcard; card; card = card->nextcard) { + char *cut_line = card->line; + if (ciprefix(".model", cut_line)) { + char *t_str; + if ((t_str = strstr(cut_line, "t_abs")) != NULL) + memcpy(t_str, " temp", 5); + else if ((t_str = strstr(cut_line, "t_rel_global")) != NULL) + memcpy(t_str, " dtemp", 12); + else if ((t_str = strstr(cut_line, "t_measured")) != NULL) + memcpy(t_str, " tnom", 10); + } + } + + /* get the area factor for diodes and bipolar devices + d1 n1 n2 dmod 7 --> d1 n1 n2 dmod area=7 + q2 n1 n2 n3 [n4] bjtmod 1.35 --> q2 n1 n2 n3 n4 bjtmod area=1.35 + q3 1 2 3 4 bjtmod 1.45 --> q2 1 2 3 4 bjtmod area=1.45 + */ + for (card = newcard; card; card = card->nextcard) { + char *cut_line = card->line; + if (*cut_line == '*') + continue; + // exclude any command inside .control ... .endc + if (ciprefix(".control", cut_line)) { + skip_control++; + continue; + } + else if (ciprefix(".endc", cut_line)) { + skip_control--; + continue; + } + else if (skip_control > 0) { + continue; + } + if (*cut_line == 'q') { + /* According to PSPICE Reference Guide the fourth (substrate) node + has to be put into [] if it is not just a number */ + cut_line = nexttok(cut_line); //.model + cut_line = nexttok(cut_line); // node1 + cut_line = nexttok(cut_line); // node2 + cut_line = nexttok(cut_line); // node3 + if (!cut_line || *cut_line == '\0') { + fprintf(stderr, "Line no. %d, %s, missing tokens\n", + card->linenum_orig, card->line); + if (ft_stricterror) + controlled_exit(1); + else + continue; + } + if (*cut_line == '[') { // node4 not a number + *cut_line = ' '; + cut_line = strchr(cut_line, ']'); + *cut_line = ' '; + cut_line = skip_ws(cut_line); + cut_line = nexttok(cut_line); // model name + } + else { // if an integer number, it is node4 + bool is_node4 = TRUE; + while (*cut_line && !isspace_c(*cut_line)) + if (!isdigit_c(*cut_line++)) + is_node4 = FALSE; // already model name + if (is_node4) { + cut_line = nexttok(cut_line); // model name + } + } + if (cut_line && *cut_line && + atof(cut_line) > 0.0) { // size of area is a real number + char *tmpstr1 = copy_substring(card->line, cut_line); + char *tmpstr2 = tprintf("%s area=%s", tmpstr1, cut_line); + tfree(tmpstr1); + tfree(card->line); + card->line = tmpstr2; + } + else if (cut_line && *cut_line && + *(skip_ws(cut_line)) == + '{') { // size of area is parametrized inside {} + char *tmpstr1 = copy_substring(card->line, cut_line); + char *tmpstr2 = gettok_char(&cut_line, '}', TRUE, TRUE); + char *tmpstr3 = + tprintf("%s area=%s %s", tmpstr1, tmpstr2, cut_line); + tfree(tmpstr1); + tfree(tmpstr2); + tfree(card->line); + card->line = tmpstr3; + } + } + else if (*cut_line == 'd') { + cut_line = nexttok(cut_line); //.model + cut_line = nexttok(cut_line); // node1 + cut_line = nexttok(cut_line); // node2 + if (!cut_line || *cut_line == '\0') { + fprintf(stderr, "Line no. %d, %s, missing tokens\n", + card->linenum_orig, card->line); + if (ft_stricterror) + controlled_exit(1); + else + continue; + } + cut_line = nexttok(cut_line); // model name + if (cut_line && *cut_line && + atof(cut_line) > 0.0) { // size of area + char *tmpstr1 = copy_substring(card->line, cut_line); + char *tmpstr2 = tprintf("%s area=%s", tmpstr1, cut_line); + tfree(tmpstr1); + tfree(card->line); + card->line = tmpstr2; + } + } + } + + /* if vswitch part s, replace + * S1 D S DG GND SWN + * .MODEL SWN VSWITCH ( VON = {0.55} VOFF = {0.49} + * RON={1/(2*M*(W/LE)*(KPN/2)*10)} ROFF={1G} ) + * by + * a1 %v(DG) %gd(D S) swa + * .MODEL SWA aswitch(cntl_off=0.49 cntl_on=0.55 r_off=1G + * r_on={1/(2*M*(W/LE)*(KPN/2)*10)} log=TRUE) + * + * if vswitch part s_st, don't replace instance, only model + * replace + * S1 D S DG GND S_ST + * .MODEL S_ST VSWITCH(VT = { 1.5 } VH = { 0.s } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) + * by the classical voltage controlled ngspice switch + * S1 D S DG GND S_ST + * .MODEL S_ST SW(VT = { 1.5 } VH = { 0.s } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) + * vswitch delay parameter td is not yet supported + + * simple hierachy, as nested subcircuits are not allowed in PSPICE */ + + /* first scan: find the vswitch models, transform them and put the S models + into a list */ + for (card = newcard; card; card = card->nextcard) { + char *str; + static struct card *subcktline = NULL; + static int nesting = 0; + char *cut_line = card->line; + if (ciprefix(".subckt", cut_line)) { + subcktline = card; + nesting++; + } + if (ciprefix(".ends", cut_line)) + nesting--; + + if (ciprefix(".model", card->line) && strstr(card->line, "vswitch")) { + char *modname; + + str = card->line = inp_remove_ws(card->line); + str = nexttok(str); /* throw away '.model' */ + INPgetNetTok(&str, &modname, 0); /* model name */ + if (!ciprefix("vswitch", str)) { + tfree(modname); + continue; + } + str = nexttok_noparens(str); /* throw away 'vswitch' */ + /* S_ST switch (parameters ron, roff, vt, vh) + * we have to find 0 to 4 parameters, identified by 'vh=' etc. + * Parameters not found have to be replaced by their default values. */ + if (strstr(str, "vt=") || strstr(str, "vh=")) { + char* newstr; + char* lstr = copy(str); + char* partstr = strstr(lstr, "ron="); + if (!partstr) { + newstr = tprintf("%s %s", "ron=1.0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + partstr = strstr(lstr, "roff="); + if (!partstr) { + newstr = tprintf("%s %s", "roff=1.0e12", lstr); //default value + tfree(lstr); + lstr = newstr; + } + partstr = strstr(lstr, "vt="); + if (!partstr) { + newstr = tprintf("%s %s", "vt=0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + partstr = strstr(lstr, "vh="); + if (!partstr) { + newstr = tprintf("%s %s", "vh=0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + tfree(card->line); + if (lstr[strlen(lstr) - 1] == ')') + card->line = tprintf(".model %s sw ( %s", modname, lstr); + else + card->line = tprintf(".model %s sw %s", modname, lstr); + tfree(lstr); + tfree(modname); + } + /* S vswitch (parameters ron, roff, von, voff) */ + /* We have to find 0 to 4 parameters, identified by 'von=' etc. and + * replace them by the pswitch code model parameters + * replace VON by cntl_on, VOFF by cntl_off, RON by r_on, and ROFF by r_off. + * Parameters not found have to be replaced by their default values. */ + else if (strstr(str, "von=") || strstr(str, "voff=")) { + char* newstr, *begstr; + char* lstr = copy(str); + /* ron */ + char* partstr = strstr(lstr, "ron="); + if (!partstr) { + newstr = tprintf("%s %s", "r_on=1.0", lstr); //default value + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s r_on%s", begstr, partstr + 3); + tfree(begstr); + } + tfree(lstr); + lstr = newstr; + /* roff */ + partstr = strstr(lstr, "roff="); + if (!partstr) { + newstr = tprintf("%s %s", "r_off=1.0e6", lstr); //default value + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s r_off%s", begstr, partstr + 4); + tfree(begstr); + } + tfree(lstr); + lstr = newstr; + /* von */ + partstr = strstr(lstr, "von="); + if (!partstr) { + newstr = tprintf("%s %s", "cntl_on=1", lstr); //default value + tfree(lstr); + lstr = newstr; + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s cntl_on%s", begstr, partstr + 3); + tfree(begstr); + } + tfree(lstr); + lstr = newstr; + /* voff */ + partstr = strstr(lstr, "voff="); + if (!partstr) { + newstr = tprintf("%s %s", "cntl_off=0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s cntl_off%s", begstr, partstr + 4); + tfree(begstr); + } + tfree(lstr); + lstr = newstr; + tfree(card->line); + if (lstr[strlen(lstr) - 1] == ')') + card->line = tprintf(".model a%s pswitch( log=TRUE %s", modname, lstr); + else + card->line = tprintf(".model a%s pswitch(%s log=TRUE)", modname, lstr); + tfree(lstr); + /* add to list, to change vswitch instance to code model line */ + if (nesting > 0) + modelsfound = insert_new_model( + modelsfound, modname, subcktline->line); + else + modelsfound = insert_new_model(modelsfound, modname, "top"); + tfree(modname); + } + else { + fprintf(stderr, "Error: Bad switch model in line %s\n", card->line); + } + } + } + + /* no need to continue if no vswitch is found */ + if (!modelsfound) + goto iswi; + + /* second scan: find the switch instances s calling a vswitch model and + * transform them */ + for (card = newcard; card; card = card->nextcard) { + static struct card *subcktline = NULL; + static int nesting = 0; + char *cut_line = card->line; + if (*cut_line == '*') + continue; + // exclude any command inside .control ... .endc + if (ciprefix(".control", cut_line)) { + skip_control++; + continue; + } + else if (ciprefix(".endc", cut_line)) { + skip_control--; + continue; + } + else if (skip_control > 0) { + continue; + } + if (ciprefix(".subckt", cut_line)) { + subcktline = card; + nesting++; + } + if (ciprefix(".ends", cut_line)) + nesting--; + + if (ciprefix("s", cut_line)) { + /* check for the model name */ + int i; + bool good = TRUE; + char *stoks[6]; + for (i = 0; i < 6; i++) { + stoks[i] = gettok_node(&cut_line); + if (!stoks[i]) { + fprintf(stderr, "Error: Bad syntax in line:\n %s\n", card->line); + good = FALSE; + break; + } + } + if (!good) { + for (i = 0; i < 6; i++) + tfree(stoks[i]); + continue; + } + /* rewrite s line and replace it if a model is found */ + if ((nesting > 0) && + find_a_model(modelsfound, stoks[5], subcktline->line)) { + tfree(card->line); + card->line = tprintf("a%s %%gd(%s %s) %%gd(%s %s) a%s", + stoks[0], stoks[3], stoks[4], stoks[1], stoks[2], + stoks[5]); + } + /* if model is not within same subcircuit, search at top level */ + else if (find_a_model(modelsfound, stoks[5], "top")) { + tfree(card->line); + card->line = tprintf("a%s %%gd(%s %s) %%gd(%s %s) a%s", + stoks[0], stoks[3], stoks[4], stoks[1], stoks[2], + stoks[5]); + } + for (i = 0; i < 6; i++) + tfree(stoks[i]); + } + } + del_models(modelsfound); + modelsfound = NULL; + +iswi:; + + /* if iswitch part s, replace + * W1 D S VC SWN + * .MODEL SWN ISWITCH ( ION = {0.55} IOFF = {0.49} + * RON={1/(2*M*(W/LE)*(KPN/2)*10)} ROFF={1G} ) + * by + * a1 %v(DG) %gd(D S) swa + * .MODEL SWA aswitch(cntl_off=0.49 cntl_on=0.55 r_off=1G + * r_on={1/(2*M*(W/LE)*(KPN/2)*10)} log=TRUE) + * + * if iswitch part s_st (short transition), don't replace instance, but only model + * replace + * W1 D S VC S_ST + * .MODEL S_ST ISWITCH(IT = { 1.5 } IH = { 0.2 } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) + * by the classical current controlled ngspice switch + * W1 D S DG GND S_ST + * .MODEL S_ST CSW(IT = { 1.5 } IH = { 0.2 } + RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) + * iswitch delay parameter td is not yet supported + + * simple hierachy, as nested subcircuits are not allowed in PSPICE */ + + /* first scan: find the iswitch models, transform them and put them into a + * list */ + for (card = newcard; card; card = card->nextcard) { + char* str; + static struct card* subcktline = NULL; + static int nesting = 0; + char* cut_line = card->line; + if (ciprefix(".subckt", cut_line)) { + subcktline = card; + nesting++; + } + if (ciprefix(".ends", cut_line)) + nesting--; + + if (ciprefix(".model", card->line) && strstr(card->line, "iswitch")) { + char* modname; + + card->line = str = inp_remove_ws(card->line); + str = nexttok(str); /* throw away '.model' */ + INPgetNetTok(&str, &modname, 0); /* model name */ + if (!ciprefix("iswitch", str)) { + tfree(modname); + continue; + } + str = nexttok_noparens(str); /* throw away 'iswitch' */ + /* S_ST switch (parameters ron, roff, it, ih) + * we have to find 0 to 4 parameters, identified by 'ih=' etc. + * Parameters not found have to be replaced by their default values. */ + if (strstr(str, "it=") || strstr(str, "ih=")) { + char* newstr; + char* lstr = copy(str); + char* partstr = strstr(lstr, "ron="); + if (!partstr) { + newstr = tprintf("%s %s", "ron=1.0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + partstr = strstr(lstr, "roff="); + if (!partstr) { + newstr = tprintf("%s %s", "roff=1.0e12", lstr); //default value + tfree(lstr); + lstr = newstr; + } + partstr = strstr(lstr, "it="); + if (!partstr) { + newstr = tprintf("%s %s", "it=0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + partstr = strstr(lstr, "ih="); + if (!partstr) { + newstr = tprintf("%s %s", "ih=0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + tfree(card->line); + if (lstr[strlen(lstr) - 1] == ')') + card->line = tprintf(".model %s csw ( %s", modname, lstr); + else + card->line = tprintf(".model %s csw %s", modname, lstr); + tfree(lstr); + tfree(modname); + } + /* S vswitch (parameters ron, roff, ion, ioff) */ + /* We have to find 0 to 4 parameters, identified by 'ion=' etc. and + * replace them by the pswitch code model parameters + * replace VON by cntl_on, VOFF by cntl_off, RON by r_on, and ROFF by r_off. + * Parameters not found have to be replaced by their default values. */ + else if (strstr(str, "ion=") || strstr(str, "ioff=")) { + char* newstr, * begstr; + char* lstr = copy(str); + /* ron */ + char* partstr = strstr(lstr, "ron="); + if (!partstr) { + newstr = tprintf("%s %s", "r_on=1.0", lstr); //default value + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s r_on%s", begstr, partstr + 3); + } + tfree(lstr); + lstr = newstr; + /* roff */ + partstr = strstr(lstr, "roff="); + if (!partstr) { + newstr = tprintf("%s %s", "r_off=1.0e6", lstr); //default value + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s r_off%s", begstr, partstr + 4); + } + tfree(lstr); + lstr = newstr; + /* von */ + partstr = strstr(lstr, "ion="); + if (!partstr) { + newstr = tprintf("%s %s", "cntl_on=1", lstr); //default value + tfree(lstr); + lstr = newstr; + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s cntl_on%s", begstr, partstr + 3); + } + tfree(lstr); + lstr = newstr; + /* voff */ + partstr = strstr(lstr, "ioff="); + if (!partstr) { + newstr = tprintf("%s %s", "cntl_off=0", lstr); //default value + tfree(lstr); + lstr = newstr; + } + else { + begstr = copy_substring(lstr, partstr); + newstr = tprintf("%s cntl_off%s", begstr, partstr + 4); + } + tfree(lstr); + lstr = newstr; + tfree(card->line); + if (lstr[strlen(lstr) - 1] == ')') + card->line = tprintf(".model a%s aswitch( log=TRUE limit=TRUE %s", modname, lstr); + else + card->line = tprintf(".model a%s aswitch(%s log=TRUE limit=TRUE)", modname, lstr); + tfree(lstr); + /* add to list, to change vswitch instance to code model line */ + if (nesting > 0) + modelsfound = insert_new_model( + modelsfound, modname, subcktline->line); + else + modelsfound = insert_new_model(modelsfound, modname, "top"); + tfree(modname); + } + else { + fprintf(stderr, "Error: Bad switch model in line %s\n", card->line); + } + } + } + +#if(0) + /* we have to find 4 parameters, identified by '=', separated by + * spaces */ + char* equalptr[4]; + equalptr[0] = strstr(str, "="); + if (!equalptr[0]) { + fprintf(stderr, + "Error: not enough parameters in iswitch model\n " + "%s\n", + card->line); + controlled_exit(1); + } + for (i = 1; i < 4; i++) { + equalptr[i] = strstr(equalptr[i - 1] + 1, "="); + if (!equalptr[i]) { + fprintf(stderr, + "Error: not enough parameters in iswitch model\n " + " %s\n", + card->line); + controlled_exit(1); + } + } + for (i = 0; i < 4; i++) { + equalptr[i] = skip_back_ws(equalptr[i], str); + while (*(equalptr[i]) != '(' && !isspace_c(*(equalptr[i])) && + *(equalptr[i]) != ',') + (equalptr[i])--; + (equalptr[i])++; + } + for (i = 0; i < 3; i++) + modpar[i] = copy_substring(equalptr[i], equalptr[i + 1] - 1); + if (strrchr(equalptr[3], ')')) + modpar[3] = copy_substring( + equalptr[3], strrchr(equalptr[3], ')')); + else + /* iswitch defined without parens */ + modpar[3] = copy(equalptr[3]); + + /* check if we have parameters IT and IH */ + for (i = 0; i < 4; i++) { + if (ciprefix("ih", modpar[i])) + have_ih = TRUE; + if (ciprefix("it", modpar[i])) + have_it = TRUE; + } + if (have_ih && have_it) { + /* replace iswitch by csw */ + char* vs = strstr(card->line, "iswitch"); + memmove(vs, " csw", 7); + } + else { + /* replace ION by cntl_on, IOFF by cntl_off, RON by r_on, and + * ROFF by r_off */ + tfree(card->line); + rep_spar(modpar); + card->line = tprintf( + /* FIXME: a new switch derived from pswitch with vnam input is due */ + ".model a%s aswitch(%s %s %s %s log=TRUE limit=TRUE)", modname, + modpar[0], modpar[1], modpar[2], modpar[3]); + } + for (i = 0; i < 4; i++) + tfree(modpar[i]); + if (nesting > 0) + modelsfound = insert_new_model( + modelsfound, modname, subcktline->line); + else + modelsfound = insert_new_model(modelsfound, modname, "top"); + tfree(modname); + } + } +#endif + /* no need to continue if no iswitch is found */ + if (!modelsfound) + return newcard; + + /* second scan: find the switch instances s calling an iswitch model and + * transform them */ + for (card = newcard; card; card = card->nextcard) { + static struct card* subcktline = NULL; + static int nesting = 0; + char* cut_line = card->line; + if (*cut_line == '*') + continue; + // exclude any command inside .control ... .endc + if (ciprefix(".control", cut_line)) { + skip_control++; + continue; + } + else if (ciprefix(".endc", cut_line)) { + skip_control--; + continue; + } + else if (skip_control > 0) { + continue; + } + if (ciprefix(".subckt", cut_line)) { + subcktline = card; + nesting++; + } + if (ciprefix(".ends", cut_line)) + nesting--; + + if (ciprefix("w", cut_line)) { + /* check for the model name */ + int i; + char* stoks[5]; + for (i = 0; i < 5; i++) + stoks[i] = gettok_node(&cut_line); + /* rewrite w line and replace it if a model is found */ + if ((nesting > 0) && + find_a_model(modelsfound, stoks[4], subcktline->line)) { + tfree(card->line); + card->line = tprintf("a%s %%vnam(%s) %%gd(%s %s) a%s", + stoks[0], stoks[3], stoks[1], stoks[2], + stoks[4]); + } + /* if model is not within same subcircuit, search at top level */ + else if (find_a_model(modelsfound, stoks[4], "top")) { + tfree(card->line); + card->line = tprintf("a%s %%vnam(%s) %%gd(%s %s) a%s", + stoks[0], stoks[3], stoks[1], stoks[2], + stoks[4]); + } + for (i = 0; i < 5; i++) + tfree(stoks[i]); + } + } + del_models(modelsfound); + + return newcard; +} + + + +/* do not modify oldcard address, insert everything after first line only */ +void pspice_compat_a(struct card *oldcard) +{ + oldcard->nextcard = pspice_compat(oldcard->nextcard); +} + + +/**** LTSPICE to ngspice ************** + * add functions uplim, dnlim + * Replace + * D1 A K SDMOD + * .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 Revepsilon=0.2 + * Epsilon=0.2 Ilimit=7 Revilimit=7) + * by + * ad1 a k asdmod + * .model asdmod sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 + * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) + * Remove '.backanno' + */ +struct card *ltspice_compat(struct card *oldcard) +{ + struct card *card, *newcard, *nextcard; + struct vsmodels *modelsfound = NULL; + int skip_control = 0; + + + /* remove double braces only if not yet done in pspice_compat() */ + if (!newcompat.ps) + rem_double_braces(oldcard); + + /* add funcs uplim, dnlim to beginning of deck */ + char *new_str = + copy(".func uplim(x, pos, z) { min(x, pos - z) + (1 - " + "(min(max(0, x - pos + z), 2 * z) / 2 / z - 1)**2)*z }"); + newcard = insert_new_line(NULL, new_str, 1, 0); + new_str = copy(".func dnlim(x, neg, z) { max(x, neg + z) - (1 - " + "(min(max(0, -x + neg + z), 2 * z) / 2 / z - 1)**2)*z }"); + nextcard = insert_new_line(newcard, new_str, 2, 0); + new_str = copy(".func uplim_tanh(x, pos, z) { min(x, pos - z) + " + "tanh(max(0, x - pos + z) / z)*z }"); + nextcard = insert_new_line(nextcard, new_str, 3, 0); + new_str = copy(".func dnlim_tanh(x, neg, z) { max(x, neg + z) - " + "tanh(max(0, neg + z - x) / z)*z }"); + nextcard = insert_new_line(nextcard, new_str, 4, 0); + nextcard->nextcard = oldcard; + + /* remove .backanno, replace 'noiseless' by 'moisy=0' */ + for (card = nextcard; card; card = card->nextcard) { + char* cut_line = card->line; + if (ciprefix(".backanno", cut_line)) { + *cut_line = '*'; + } + else if (*cut_line == 'r') { + char* noi = strstr(cut_line, "noiseless"); + /* only if 'noiseless' is an unconnected token */ + if (noi && isspace_c(noi[-1]) && (isspace_c(noi[9]) || !isprint_c(noi[9]))) { + memcpy(noi, "noisy=0 ", 9); + } + } + } + + /* replace + * D1 A K SDMOD + * .MODEL SDMOD D (Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 + * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) + * by + * a1 a k SDMOD + * .model SDMOD sidiode(Roff=1000 Ron=0.7 Rrev=0.2 Vfwd=1 Vrev=10 + * Revepsilon=0.2 Epsilon=0.2 Ilimit=7 Revilimit=7) + * Do this if one of the parameters, which are uncommon to standard diode + * model, has been found. + + * simple hierachy, as nested subcircuits are not allowed in PSPICE */ + + /* first scan: find the d models, transform them and put them into a list + */ + for (card = nextcard; card; card = card->nextcard) { + char *str; + static struct card *subcktline = NULL; + static int nesting = 0; + char *cut_line = card->line; + if (*cut_line == '*' || *cut_line == '\0') + continue; + else if (ciprefix(".subckt", cut_line)) { + subcktline = card; + nesting++; + } + else if (ciprefix(".ends", cut_line)) + nesting--; + + else if (ciprefix(".model", card->line) && + search_plain_identifier(card->line, "d")) { + if (search_plain_identifier(card->line, "roff") || + search_plain_identifier(card->line, "ron") || + search_plain_identifier(card->line, "rrev") || + search_plain_identifier(card->line, "vfwd") || + search_plain_identifier(card->line, "vrev") || + search_plain_identifier(card->line, "revepsilon") || + search_plain_identifier(card->line, "epsilon") || + search_plain_identifier(card->line, "revilimit") || + search_plain_identifier(card->line, "ilimit")) { + char *modname; + + /* remove parameter 'noiseless' (the model is noiseless anyway) */ + char *nonoise = search_plain_identifier(card->line, "noiseless"); + if (nonoise) { + size_t iii; + for (iii = 0; iii < 9; iii++) + nonoise[iii] = ' '; + } + card->line = str = inp_remove_ws(card->line); + str = nexttok(str); /* throw away '.model' */ + INPgetNetTok(&str, &modname, 0); /* model name */ + if (!ciprefix("d", str)) { + tfree(modname); + continue; + } + /* skip d */ + str++; + /* we take all the existing parameters */ + char *newstr = copy(str); + tfree(card->line); + card->line = tprintf(".model a%s sidiode%s", modname, newstr); + if (nesting > 0) + modelsfound = insert_new_model( + modelsfound, modname, subcktline->line); + else + modelsfound = + insert_new_model(modelsfound, modname, "top"); + tfree(modname); + tfree(newstr); + } + } + else + continue; + } + + /* no need to continue if no d is found */ + if (!modelsfound) + return newcard; + + /* second scan: find the diode instances d calling a simple diode model + * and transform them */ + for (card = nextcard; card; card = card->nextcard) { + static struct card *subcktline = NULL; + static int nesting = 0; + char *cut_line = card->line; + if (*cut_line == '*') + continue; + if (*cut_line == '\0') + continue; + // exclude any command inside .control ... .endc + if (ciprefix(".control", cut_line)) { + skip_control++; + continue; + } + else if (ciprefix(".endc", cut_line)) { + skip_control--; + continue; + } + else if (skip_control > 0) { + continue; + } + if (ciprefix(".subckt", cut_line)) { + subcktline = card; + nesting++; + } + if (ciprefix(".ends", cut_line)) + nesting--; + + if (ciprefix("d", cut_line)) { + /* check for the model name */ + int i; + char *stoks[4]; + for (i = 0; i < 4; i++) { + stoks[i] = gettok_node(&cut_line); + if (stoks[i] == NULL) { + fprintf(stderr, "Error in line %d: buggy diode instance line\n %s\n", card->linenum_orig, card->line); + fprintf(stderr, "At least 'Dxx n1 n2 d' is required.\n"); + controlled_exit(EXIT_BAD); + } + } + /* rewrite d line and replace it if a model is found */ + if ((nesting > 0) && + find_a_model(modelsfound, stoks[3], subcktline->line)) { + tfree(card->line); + card->line = tprintf("a%s %s %s a%s", + stoks[0], stoks[1], stoks[2], stoks[3]); + } + /* if model is not within same subcircuit, search at top level */ + else if (find_a_model(modelsfound, stoks[3], "top")) { + tfree(card->line); + card->line = tprintf("a%s %s %s a%s", + stoks[0], stoks[1], stoks[2], stoks[3]); + } + for (i = 0; i < 4; i++) + tfree(stoks[i]); + } + } + del_models(modelsfound); + + return newcard; +} + +/* do not modify oldcard address, insert everything after first line only */ +void ltspice_compat_a(struct card *oldcard) +{ + oldcard->nextcard = ltspice_compat(oldcard->nextcard); +} diff --git a/src/frontend/logicexp.c b/src/frontend/logicexp.c index 13042bde9..95f495c37 100644 --- a/src/frontend/logicexp.c +++ b/src/frontend/logicexp.c @@ -25,6 +25,7 @@ /* Turn off/on debug tracing */ #define PRINT_ALL FALSE //#define PRINT_ALL TRUE +static char *get_pindly_instance_name(void); /* Start of btree symbol table */ #define SYM_INPUT 1 @@ -1222,7 +1223,18 @@ static BOOL gen_gates(PTABLE gate_tab, SYM_TAB parser_symbols) if (tok_count != 2) goto gen_error; } else if (lex_gate_op(val)) { if (gate_op != 0) { - if (val != gate_op) goto gen_error; + if (val != gate_op) { + fprintf(stderr, + "\nERROR operator precedence parsing is not implemented\n" + " at expression %s\n" + " Please modify the LOGICEXP by inserting parentheses.\n" + " For example change a & b | c & d & e | f\n" + " to (a & b) | (c & d & e) | f\n" + " to get the desired PSPice precedence rules\n", + t->line + ); + goto gen_error; + } } gate_op = val; } else { @@ -1570,6 +1582,7 @@ BOOL f_logicexp(char *line) int t, num_ins = 0, num_outs = 0, i; char *endp; BOOL ret_val = TRUE; + char *uname = NULL; lex_init(line); current_lexer = parse_lexer; @@ -1577,6 +1590,8 @@ BOOL f_logicexp(char *line) &parse_lexer->lexer_sym_tab); t = lex_scan(); // U* if (!expect_token(t, LEX_ID, NULL, TRUE, 1)) goto error_return; + uname = (char *)TMALLOC(char, strlen(parse_lexer->lexer_buf) + 1); + strcpy(uname, parse_lexer->lexer_buf); /* logicexp ( int , int ) */ t = lex_scan(); if (!expect_token(t, LEX_ID, "logicexp", TRUE, 2)) goto error_return; @@ -1653,14 +1668,16 @@ BOOL f_logicexp(char *line) current_lexer = NULL; if (!ret_val) { fprintf(stderr, "ERROR parsing logicexp\n"); - fprintf(stderr, "ERROR in \"%s\"\n", line); + fprintf(stderr, "ERROR in instance %s\n", uname); cleanup_parser(); } + if (uname) tfree(uname); return ret_val; error_return: delete_lexer(parse_lexer); current_lexer = NULL; + if (uname) tfree(uname); return FALSE; } @@ -1877,7 +1894,8 @@ static char *get_typ_estimate(char *min, char *typ, char *max, DSTRING *pds) { char *tmpmax = NULL, *tmpmin = NULL; float valmin, valmax, average; - char *units1, *units2; + char *unitsmin, *unitsmax; + char *instance = NULL; ds_clear(pds); if (typ && strlen(typ) > 0 && typ[0] != '-') { @@ -1892,12 +1910,33 @@ static char *get_typ_estimate(char *min, char *typ, char *max, DSTRING *pds) } if (tmpmin && tmpmax) { if (strlen(tmpmin) > 0 && strlen(tmpmax) > 0) { - valmin = strtof(tmpmin, &units1); - valmax = strtof(tmpmax, &units2); - average = (valmin + valmax) / (float)2.0; - ds_cat_printf(pds, "%.2f%s", average, units2); - if (!eq(units1, units2)) { - printf("WARNING units do not match\n"); + valmin = strtof(tmpmin, &unitsmin); + valmax = strtof(tmpmax, &unitsmax); + if (!eq(unitsmin, unitsmax)) { + printf("WARNING typ_estimate units do not match" + " min %s max %s", tmpmin, tmpmax); + if (unitsmin[0] == unitsmax[0]) { + average = (valmin + valmax) / (float)2.0; + ds_cat_printf(pds, "%.2f%cs", average, unitsmin[0]); + } else if (unitsmin[0] == 'p' && unitsmax[0] == 'n') { + valmax = (float)1000.0 * valmax; + average = (valmin + valmax) / (float)2.0; + ds_cat_printf(pds, "%.2fps", average); + } else if (unitsmin[0] == 'n' && unitsmax[0] == 'p') { + ds_cat_printf(pds, "%.2fns", valmin); + } else { + ds_cat_printf(pds, "%.2f%s", valmin, unitsmin); + } + instance = get_pindly_instance_name(); + printf(" using delay %s", ds_get_buf(pds)); + if (instance) { + printf(" pindly %s\n", instance); + } else { + printf("\n"); + } + } else { + average = (valmin + valmax) / (float)2.0; + ds_cat_printf(pds, "%.2f%s", average, unitsmax); } return ds_get_buf(pds); } @@ -2145,6 +2184,7 @@ static BOOL new_gen_output_models(LEXER lx) } else if (eq(lx->lexer_buf, "setup_hold") || eq(lx->lexer_buf, "width") || eq(lx->lexer_buf, "freq") + || eq(lx->lexer_buf, "boolean") || eq(lx->lexer_buf, "general")) { in_pindly = FALSE; in_tristate = FALSE; @@ -2160,6 +2200,9 @@ static BOOL new_gen_output_models(LEXER lx) goto err_return; } val = lexer_scan(lx); + if (val == ',') { + val = lexer_scan(lx); + } } if (!extract_delay(lx, val, pline_arr, idx, FALSE)) goto err_return; for (i = 0; i < arrlen; i++) { @@ -2178,11 +2221,15 @@ static BOOL new_gen_output_models(LEXER lx) if (prit) { printf("tristate enable %s ", lx->lexer_buf); } val = lexer_scan(lx); if (val != '=') { - goto err_return; - } - val = lexer_scan(lx); - if (val != LEX_ID) { - goto err_return; + // if there is no '=' it must be an enable id + if (val != LEX_ID) { + goto err_return; + } + } else { // enable id follows '=' + val = lexer_scan(lx); + if (val != LEX_ID) { + goto err_return; + } } if (prit) { printf("ena \"%s\"\n", lx->lexer_buf); } ds_clear(&enable_name); @@ -2215,6 +2262,9 @@ static BOOL new_gen_output_models(LEXER lx) goto err_return; } val = lexer_scan(lx); + if (val == ',') { + val = lexer_scan(lx); + } } if (!extract_delay(lx, val, pline_arr, idx, TRUE)) goto err_return; for (i = 0; i < arrlen; i++) { @@ -2236,6 +2286,24 @@ err_return: return FALSE; } +static char *pindly_instance_name = NULL; +static void set_pindly_instance_name(char *name) +{ + if (pindly_instance_name) { + tfree(pindly_instance_name); + pindly_instance_name = NULL; + } + if (name) { + pindly_instance_name = (char *)TMALLOC(char, strlen(name) + 1); + strcpy(pindly_instance_name, name); + } +} + +static char *get_pindly_instance_name(void) +{ + return pindly_instance_name; +} + BOOL f_pindly(char *line) { int t, num_ios = 0, num_ena = 0, num_refs = 0, i; @@ -2249,6 +2317,7 @@ BOOL f_pindly(char *line) current_lexer = lxr; t = lexer_scan(lxr); // U* if (!expect_token(t, LEX_ID, NULL, TRUE, 50)) goto error_return; + set_pindly_instance_name(lxr->lexer_buf); /* pindly ( int , int, int ) */ t = lexer_scan(lxr); @@ -2330,20 +2399,25 @@ BOOL f_pindly(char *line) } if (!new_gen_output_models(lxr)) { + char *i_name = get_pindly_instance_name(); fprintf(stderr, "ERROR generating models for pindly\n"); - fprintf(stderr, "ERROR in \"%s\"\n", line); + if (i_name) { + fprintf(stderr, "ERROR in instance %s\n", i_name); + } goto error_return;; } gen_pindly_buffers(); delete_lexer(lxr); cleanup_pindly_tab(); current_lexer = NULL; + set_pindly_instance_name(NULL); return TRUE; error_return: delete_lexer(lxr); cleanup_pindly_tab(); current_lexer = NULL; + set_pindly_instance_name(NULL); return FALSE; } diff --git a/src/frontend/udevices.c b/src/frontend/udevices.c index 69675b66d..cfe25c88d 100644 --- a/src/frontend/udevices.c +++ b/src/frontend/udevices.c @@ -59,12 +59,11 @@ #include "ngspice/udevices.h" #include "ngspice/logicexp.h" #include "ngspice/dstring.h" +#include "ngspice/hash.h" extern struct card* insert_new_line( struct card* card, char* line, int linenum, int linenum_orig); -//#define TRACE - /* device types */ #define D_AND 0 #define D_AO 1 @@ -169,22 +168,22 @@ struct dltch_instance { }; /* structs for instances and timing models which have been translated */ -typedef struct s_xlate *Xlatep; +typedef struct s_xlate *Xlate_datap; typedef struct s_xlate { - Xlatep next; + Xlate_datap next; char *translated; // the translated instance line char *delays; // the delays from the pspice timing model char *utype; // pspice model type ugate, utgate, ueff, ugff, udly char *xspice; // xspice device type such as d_and, d_dff, etc. char *tmodel; // timing model name of pspice instance or model char *mname; // name of the xspice timing model of the instance -} Xlate; +} Xlate_data; typedef struct s_xlator *Xlatorp; typedef struct s_xlator { - Xlatep head; - Xlatep tail; - Xlatep iter; + Xlate_datap head; + Xlate_datap tail; + Xlate_datap iter; } Xlator; /* For timing model extraction */ @@ -211,10 +210,7 @@ struct name_entry { static char *get_zero_rise_fall(void); static char *get_name_hilo(char *tok_str); - -#ifdef TRACE -static void print_name_list(NAME_ENTRY nelist); -#endif +static char *get_current_tmodel(void); static NAME_ENTRY new_name_entry(char *name) { @@ -261,18 +257,10 @@ static NAME_ENTRY find_name_entry(char *name, NAME_ENTRY nelist) return NULL; } -static void clear_name_list(NAME_ENTRY nelist, char *msg) +static void clear_name_list(NAME_ENTRY nelist) { - NG_IGNORE(msg); - NAME_ENTRY x = NULL, next = NULL; if (!nelist) { return; } -#ifdef TRACE - printf("%s\n", msg); - print_name_list(nelist); -#else - (void)msg; -#endif for (x = nelist; x; x = next) { next = x->next; if (x->name) { tfree(x->name); } @@ -280,25 +268,13 @@ static void clear_name_list(NAME_ENTRY nelist, char *msg) } } -#ifdef TRACE -static void print_name_list(NAME_ENTRY nelist) -{ - NAME_ENTRY x = NULL; - int count = 0; - if (!nelist) { return; } - for (x = nelist; x; x = x->next) { - printf("%s\n", x->name); - count++; - } - printf("NAME_COUNT %d\n", count); -} -#endif - /* static data cleared and reset by initialize_udevice(), cleared by cleanup_udevice(). */ -static int ps_port_directions = 0; // If non-zero list subckt port directions +static int ps_global_tmodels = 0; // If non-zero collect global tmodels +static int ps_global_hash_table = 1; // Use hash table for global tmodel names +static int ps_ports_and_pins = 0; // If non-zero list subckt port directions static int ps_udevice_msgs = 0; // Controls the verbosity of U* warnings /* If ps_udevice_exit is non-zero then exit when u_process_instance fails @@ -461,9 +437,9 @@ static void add_all_port_names(char *subckt_line) if (!subckt_line) { return; } - if (ps_port_directions & 4) { + if (ps_ports_and_pins & 4) { printf("TRANS_IN %s\n", subckt_line); - } else if (ps_port_directions & 1) { + } else if (ps_ports_and_pins & 1) { printf("%s\n", subckt_line); } copy_line = tprintf("%s", subckt_line); @@ -618,15 +594,15 @@ static char *find_xspice_for_delay(char *itype) } /* NOTE - Xlator and Xlate - Xlate struct data is stored in an Xlator list struct. + Xlator and Xlate_data + Xlate_data struct is stored in an Xlator list struct. Used to save translated instance and model statements. - After an Xlator has been created, Xlate data is generated via + After an Xlator has been created, Xlate_data is generated via create_xlate() and entered into the Xlator by add_xlator(). A first_xlator() ... next_xlator() iteration loop will retrieve - the Xlate data entries in an Xlator. + the Xlate_data entries in an Xlator. */ -static void delete_xlate(Xlatep p) +static void delete_xlate(Xlate_datap p) { if (p) { if (p->translated) { tfree(p->translated); } @@ -640,13 +616,13 @@ static void delete_xlate(Xlatep p) return; } -static Xlatep create_xlate(char *translated, char *delays, char *utype, +static Xlate_datap create_xlate(char *translated, char *delays, char *utype, char *xspice, char *tmodel, char *mname) { /* Any unused parameter is called with an empty string "" */ - Xlatep xlp; + Xlate_datap xlp; - xlp = TMALLOC(Xlate, 1); + xlp = TMALLOC(Xlate_data, 1); xlp->next = NULL; xlp->translated = TMALLOC(char, strlen(translated) + 1); strcpy(xlp->translated, translated); @@ -663,12 +639,12 @@ static Xlatep create_xlate(char *translated, char *delays, char *utype, return xlp; } -static Xlatep create_xlate_translated(char *translated) +static Xlate_datap create_xlate_translated(char *translated) { return create_xlate(translated, "", "", "", "", ""); } -static Xlatep create_xlate_instance( +static Xlate_datap create_xlate_instance( char *translated, char *xspice, char *tmodel, char *mname) { return create_xlate(translated, "", "", xspice, tmodel, mname); @@ -687,7 +663,7 @@ static Xlatorp create_xlator(void) static void delete_xlator(Xlatorp xp) { - Xlatep x, next; + Xlate_datap x, next; if (xp) { if (xp->head) { @@ -705,9 +681,9 @@ static void delete_xlator(Xlatorp xp) return; } -static Xlatorp add_xlator(Xlatorp xp, Xlatep x) +static Xlatorp add_xlator(Xlatorp xp, Xlate_datap x) { - Xlatep prev; + Xlate_datap prev; if (!xp || !x) { return NULL; } if (!xp->head) { @@ -724,9 +700,9 @@ static Xlatorp add_xlator(Xlatorp xp, Xlatep x) return xp; } -static Xlatep first_xlator(Xlatorp xp) +static Xlate_datap first_xlator(Xlatorp xp) { - Xlatep xret; + Xlate_datap xret; if (!xp) { return NULL; } xp->iter = xp->head; @@ -739,9 +715,9 @@ static Xlatep first_xlator(Xlatorp xp) } } -static Xlatep next_xlator(Xlatorp xp) +static Xlate_datap next_xlator(Xlatorp xp) { - Xlatep ret; + Xlate_datap ret; if (!xp) { return NULL; } ret = xp->iter; @@ -754,7 +730,7 @@ static Xlatep next_xlator(Xlatorp xp) static Xlatorp append_xlator(Xlatorp dest, Xlatorp src) { - Xlatep x1, copy; + Xlate_datap x1, copy; if (!dest || !src) { return NULL; } for (x1 = first_xlator(src); x1; x1 = next_xlator(src)) { @@ -765,9 +741,14 @@ static Xlatorp append_xlator(Xlatorp dest, Xlatorp src) return dest; } -/* static Xlatorp for collecting timing model delays */ +/* static Xlatorp for collecting subckt timing model delays */ static Xlatorp model_xlatorp = NULL; +/* static Xlatorp for collecting global timing model delays */ +static Xlatorp global_model_xlatorp = NULL; +/* global model hash table */ +static NGHASHPTR global_model_hash_table = NULL; + /* static Xlatorp for default zero delay models */ static Xlatorp default_models = NULL; @@ -794,7 +775,7 @@ struct card *replacement_udevice_cards(void) { struct card *newcard = NULL, *nextcard = NULL; char *new_str = NULL; - Xlatep x; + Xlate_datap x; int count = 0; if (!translated_p) { return NULL; } @@ -831,25 +812,33 @@ struct card *replacement_udevice_cards(void) translated_p = add_xlator(translated_p, x); } - if (current_subckt && (ps_port_directions & 2)) { + if (current_subckt && (ps_ports_and_pins & 2)) { + DS_CREATE(ds_tmp, 128); char *tmp = NULL, *pos = NULL, *posp = NULL; tmp = TMALLOC(char, strlen(current_subckt) + 1); (void) memcpy(tmp, current_subckt, strlen(current_subckt) + 1); pos = strstr(tmp, "optional:"); posp = strstr(tmp, "params:"); + ds_clear(&ds_tmp); /* If there is an optional: and a param: then posp > pos */ if (pos) { /* Remove the optional: section if present */ *pos = '\0'; if (posp) { - strcat(tmp, posp); + ds_cat_str(&ds_tmp, tmp); + ds_cat_str(&ds_tmp, posp); + printf("\nTRANS_OUT %s\n", ds_get_buf(&ds_tmp)); + } else { + printf("\nTRANS_OUT %s\n", tmp); } + } else { + printf("\nTRANS_OUT %s\n", tmp); } - printf("\nTRANS_OUT %s\n", tmp); tfree(tmp); + ds_free(&ds_tmp); } for (x = first_xlator(translated_p); x; x = next_xlator(translated_p)) { - if (ps_port_directions & 2) { + if (ps_ports_and_pins & 2) { printf("TRANS_OUT %s\n", x->translated); } new_str = copy(x->translated); @@ -864,7 +853,7 @@ struct card *replacement_udevice_cards(void) nextcard = insert_new_line(nextcard, new_str, 0, 0); } } - if (current_subckt && (ps_port_directions & 2)) { + if (current_subckt && (ps_ports_and_pins & 2)) { char *p1 = NULL, *p2 = NULL; DS_CREATE(tmpds, 64); p1 = strstr(current_subckt, ".subckt"); @@ -881,7 +870,7 @@ struct card *replacement_udevice_cards(void) void u_add_instance(char *str) { - Xlatep x; + Xlate_datap x; if (str && strlen(str) > 0) { x = create_xlate_translated(str); @@ -904,8 +893,48 @@ void u_add_logicexp_model(char *tmodel, char *xspice_gate, char *model_name) void initialize_udevice(char *subckt_line) { - Xlatep xdata; + /* If this has been called before, make sure you have called + * the corresponding cleanup_udevice. + * After call with NULL subckt_line, cleanup_udevice(TRUE). + * After call with non-NULL subckt_line, cleanup_udevice(FALSE). + */ + Xlate_datap xdata; + if (!cp_getvar("ps_global_tmodels", CP_NUM, &ps_global_tmodels, 0)) { + ps_global_tmodels = 0; + } + if (!cp_getvar("ps_global_hash_table", CP_NUM, &ps_global_hash_table, 0)) { + ps_global_hash_table = 1; + } + /* + Use tpzh.., tpzl.., tphz.., tplz.. for tristates + if they are the only delays. + */ + if (!cp_getvar("ps_tpz_delays", CP_NUM, &ps_tpz_delays, 0)) { + ps_tpz_delays = 1; // default: use tpz... delays if necessary + } + if (!cp_getvar("ps_use_mntymx", CP_NUM, &ps_use_mntymx, 0)) { + ps_use_mntymx = 4; // default: typ + shorter delays + } + set_u_devices_info(ps_use_mntymx); + + /* if subckt_line is NULL only create the global model xlator */ + if (!subckt_line) { + global_model_xlatorp = NULL; + global_model_hash_table = NULL; + if (!ps_global_tmodels) return; + + global_model_xlatorp = create_xlator(); + if (ps_global_hash_table) { + global_model_hash_table = nghash_init(nghash_table_size(101)); + } + if (ps_global_hash_table) { + printf("NOTE using global models hash table search\n"); + } else { + printf("NOTE using global models linear list search\n"); + } + return; + } new_names_list = NULL; input_names_list = NULL; output_names_list = NULL; @@ -913,14 +942,14 @@ void initialize_udevice(char *subckt_line) port_names_list = NULL; num_name_collisions = 0; /* - Variable ps_port_directions != 0 to turn on pins and ports. - If (ps_port_directions & 4) also print the Pspice input lines with + Variable ps_ports_and_pins != 0 to turn on pins and ports. + If (ps_ports_and_pins & 4) also print the Pspice input lines with prefix TRANS_IN. - If (ps_port_directions & 2) print translated Xspice equivalent lines + If (ps_ports_and_pins & 2) print translated Xspice equivalent lines with prefix TRANS_OUT. */ - if (!cp_getvar("ps_port_directions", CP_NUM, &ps_port_directions, 0)) { - ps_port_directions = 0; + if (!cp_getvar("ps_ports_and_pins", CP_NUM, &ps_ports_and_pins, 0)) { + ps_ports_and_pins = 0; } /* Set ps_udevice_msgs to print warnings about PSpice device types. @@ -936,13 +965,6 @@ void initialize_udevice(char *subckt_line) if (!cp_getvar("ps_udevice_exit", CP_NUM, &ps_udevice_exit, 0)) { ps_udevice_exit = 0; } - /* - Use tpzh.., tpzl.., tphz.., tplz.. for tristates - if they are the only delays. - */ - if (!cp_getvar("ps_tpz_delays", CP_NUM, &ps_tpz_delays, 0)) { - ps_tpz_delays = 1; // default: use tpz... delays if necessary - } /* If non-zero use inverters with ff/latch control inputs */ if (!cp_getvar("ps_with_inverters", CP_NUM, &ps_with_inverters, 0)) { ps_with_inverters = 0; @@ -951,10 +973,6 @@ void initialize_udevice(char *subckt_line) if (!cp_getvar("ps_with_tri_inverters", CP_NUM, &ps_with_tri_inverters, 0)) { ps_with_tri_inverters = 0; } - if (!cp_getvar("ps_use_mntymx", CP_NUM, &ps_use_mntymx, 0)) { - ps_use_mntymx = 4; // default: typ + shorter delays - } - set_u_devices_info(ps_use_mntymx); if (subckt_line && strncmp(subckt_line, ".subckt", 7) == 0) { add_all_port_names(subckt_line); @@ -992,41 +1010,19 @@ void initialize_udevice(char *subckt_line) add_drive_hilo = FALSE; } -static void determine_port_type(void) +void cleanup_udevice(BOOL global) { - BOOL inp = FALSE, outp = FALSE, tri = FALSE; - char *port_type = NULL; - NAME_ENTRY x = NULL; - - for (x = port_names_list; x; x = x->next) { - inp = (find_name_entry(x->name, input_names_list) ? TRUE : FALSE); - outp = (find_name_entry(x->name, output_names_list) ? TRUE : FALSE); - tri = (find_name_entry(x->name, tristate_names_list) ? TRUE : FALSE); - port_type = "UNKNOWN"; - if (tri) { - if (outp && inp) { - port_type = "INOUT"; - } else if (outp) { - port_type = "OUT"; - } - } else { - if (outp && inp) { - port_type = "OUT"; - } else if (outp) { - port_type = "OUT"; - } else if (inp) { - port_type = "IN"; - } + if (global) { + if (global_model_xlatorp) { + delete_xlator(global_model_xlatorp); } - if (ps_port_directions & 1) { - printf("port: %s %s\n", x->name, port_type); + global_model_xlatorp = NULL; + if (global_model_hash_table) { + nghash_free(global_model_hash_table, NULL, NULL); } + global_model_hash_table = NULL; + return; } -} - -void cleanup_udevice(void) -{ - determine_port_type(); cleanup_translated_xlator(); delete_xlator(model_xlatorp); model_xlatorp = NULL; @@ -1034,15 +1030,15 @@ void cleanup_udevice(void) default_models = NULL; add_zero_delay_inverter_model = FALSE; add_drive_hilo = FALSE; - clear_name_list(input_names_list, "INPUT_PINS"); + clear_name_list(input_names_list); input_names_list = NULL; - clear_name_list(output_names_list, "OUTPUT_PINS"); + clear_name_list(output_names_list); output_names_list = NULL; - clear_name_list(tristate_names_list, "TRISTATE_PINS"); + clear_name_list(tristate_names_list); tristate_names_list = NULL; - clear_name_list(port_names_list, "PORT_NAMES"); + clear_name_list(port_names_list); port_names_list = NULL; - clear_name_list(new_names_list, "NEW_NAMES"); + clear_name_list(new_names_list); new_names_list = NULL; if (current_subckt) { tfree(current_subckt); @@ -1051,16 +1047,16 @@ void cleanup_udevice(void) subckt_msg_count = 0; } -static Xlatep create_xlate_model(char *delays, +static Xlate_datap create_xlate_model(char *delays, char *utype, char *xspice, char *tmodel) { return create_xlate("", delays, utype, xspice, tmodel, ""); } -static Xlatep find_tmodel_in_xlator(Xlatep x, Xlatorp xlp) +static Xlate_datap find_tmodel_in_xlator(Xlate_datap x, Xlatorp xlp) { /* Only for timing model xlators */ - Xlatep x1; + Xlate_datap x1; if (!x) { return NULL; } if (!xlp) { return NULL; } @@ -1074,37 +1070,109 @@ static Xlatep find_tmodel_in_xlator(Xlatep x, Xlatorp xlp) return NULL; } -static Xlatep find_in_model_xlator(Xlatep x) +static Xlate_datap find_global_model_from_table(Xlate_datap x) { - Xlatep x1; + Xlate_datap xglobal = NULL; - x1 = find_tmodel_in_xlator(x, model_xlatorp); - if (x1) { return x1; } + if (global_model_hash_table) { + DS_CREATE(ds_key, 32); + + ds_cat_printf(&ds_key, "%s", x->tmodel); + if (x->xspice && strlen(x->xspice) > 0) { + ds_cat_printf(&ds_key, "___%s", x->xspice); + } + xglobal = (Xlate_datap) nghash_find(global_model_hash_table, + ds_get_buf(&ds_key)); + ds_free(&ds_key); + } + return xglobal; +} + +static void *insert_global_model_in_table(Xlate_datap x) +{ + void *inserted = NULL; + + if (global_model_hash_table) { + Xlate_datap xglobal = NULL; + DS_CREATE(ds_key, 32); + + ds_clear(&ds_key); + ds_cat_printf(&ds_key, "%s", x->tmodel); + if (x->xspice && strlen(x->xspice) > 0) { + ds_cat_printf(&ds_key, "___%s", x->xspice); + } + xglobal = (Xlate_datap) nghash_find(global_model_hash_table, + ds_get_buf(&ds_key)); + if (!xglobal) { + inserted = nghash_insert(global_model_hash_table, + ds_get_buf(&ds_key), x); + } else { + inserted = NULL; + } + ds_free(&ds_key); + } + return inserted; +} + +static Xlate_datap find_in_model_xlator(Xlate_datap x, BOOL global) +{ + Xlate_datap x1 = NULL; + + /* for global timing models do not search the default models */ + if (global) { + if (!ps_global_tmodels) return NULL; + + if (ps_global_hash_table) { + if (global_model_hash_table) { + x1 = find_global_model_from_table(x); + return x1; + } + } else { + if (global_model_xlatorp) { + x1 = find_tmodel_in_xlator(x, global_model_xlatorp); + return x1; + } + } + return NULL; + } else { + if (model_xlatorp) { + x1 = find_tmodel_in_xlator(x, model_xlatorp); + if (x1) { return x1; } + } + } + /* If the timing model is not found local to the subckt, + * search for a default model + */ x1 = find_tmodel_in_xlator(x, default_models); return x1; } static void add_delays_to_model_xlator(char *delays, - char *utype, char *xspice, char *tmodel) + char *utype, char *xspice, char *tmodel, BOOL global) { /* Specify xspice as "d_dlatch" or "d_srlatch" for ugff otherwise xspice == "" */ - Xlatep x = NULL, x1 = NULL; + Xlate_datap x = NULL, x1 = NULL; + Xlatorp xlp = NULL; - if (!model_xlatorp) { return; } + if (global) { + xlp = global_model_xlatorp; + } else { + xlp = model_xlatorp; + } + if (!xlp) { return; } x = create_xlate_model(delays, utype, xspice, tmodel); - x1 = find_in_model_xlator(x); + x1 = find_in_model_xlator(x, global); if (x1) { -/* - printf("Already found timing model %s utype %s\n", - x1->tmodel, x1->utype); -*/ delete_xlate(x); return; } - (void) add_xlator(model_xlatorp, x); + (void) add_xlator(xlp, x); + if (global && ps_global_hash_table) { + (void) insert_global_model_in_table(x); + } } /* classify gate variants */ @@ -1661,7 +1729,7 @@ static char *new_inverter(char *iname, char *node, Xlatorp xlp) /* Return the name of the output of the new inverter */ /* tfree the returned string after it has been used by the caller */ char *tmp = NULL, *instance_name = NULL, *not_node = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; instance_name = tprintf("a%s_%s", iname, node); not_node = tprintf("not_%s", instance_name); @@ -1691,7 +1759,7 @@ static BOOL gen_timing_model( The new .model statement is added to the Xlatorp of the translated xspice instance and model lines (not to be confused with model_xlatorp. */ - Xlatep xin = NULL, xout = NULL, newdata; + Xlate_datap xin = NULL, xout = NULL, newdata; char *s1; BOOL retval; @@ -1700,7 +1768,10 @@ static BOOL gen_timing_model( } else { xin = create_xlate_model("", utype, "", tmodel); } - xout = find_in_model_xlator(xin); + xout = find_in_model_xlator(xin, FALSE); // local timing model + if (!xout) { + xout = find_in_model_xlator(xin, TRUE); // global timing model + } if (xout) { /* Don't delete xout or the model_xlatorp will be corrupted */ if (xout->delays && strlen(xout->delays) > 0) { @@ -1727,7 +1798,7 @@ static Xlatorp gen_dff_instance(struct dff_instance *ip, int withinv) int i, num_gates; char *modelnm, *s1; Xlatorp xxp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; BOOL need_preb_inv = FALSE, need_clrb_inv = FALSE; DS_CREATE(tmpdstr, 128); @@ -1839,7 +1910,7 @@ static Xlatorp gen_jkff_instance(struct jkff_instance *ip, int withinv) int i, num_gates; char *modelnm, *s1; Xlatorp xxp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; BOOL need_preb_inv = FALSE, need_clrb_inv = FALSE; DS_CREATE(tmpdstr, 128); @@ -1958,7 +2029,7 @@ static Xlatorp gen_dltch_instance(struct dltch_instance *ip, int withinv) int i, num_gates; char *modelnm, *s1, *s2, *s3; Xlatorp xxp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; BOOL need_preb_inv = FALSE, need_clrb_inv = FALSE; if (!ip) { return NULL; } @@ -2073,7 +2144,7 @@ static Xlatorp gen_srff_instance(struct srff_instance *srffp, int withinv) int i, num_gates; char *modelnm, *s1, *s2, *s3; Xlatorp xxp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; BOOL need_preb_inv = FALSE, need_clrb_inv = FALSE; if (!srffp) { return NULL; } @@ -2196,7 +2267,7 @@ static Xlatorp gen_compound_instance(struct compound_instance *compi) char *high_name = NULL, *low_name = NULL; char *zero_delay_str = NULL; Xlatorp xxp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; DS_CREATE(tmp_dstr, 128); if (!compi) { @@ -2347,7 +2418,7 @@ static Xlatorp gen_gate_instance(struct gate_instance *gip) char *instance_name = NULL; int i, j, k, width, num_gates, num_ins, num_outs; Xlatorp xxp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; int withinv = ps_with_tri_inverters; BOOL inv3_to_buf3 = FALSE; BOOL inv3a_to_buf3a = FALSE; @@ -2551,9 +2622,9 @@ static Xlatorp gen_gate_instance(struct gate_instance *gip) to the model of a trailing tristate buffer. */ if (inv3a_to_buf3a) { - primary_model = tprintf("d_a%s_%s", iname, "buf3a"); + primary_model = tprintf("d_a%s_%s", iname, "buf3a"); } else { - primary_model = tprintf("d_a%s_%s", iname, itype); + primary_model = tprintf("d_a%s_%s", iname, itype); } for (i = 0; i < num_gates; i++) { // start of for each gate /* inputs */ @@ -2764,7 +2835,7 @@ static void estimate_typ(struct timing_data *tdp) char *tmpmax = NULL, *tmpmin = NULL; char *min, *typ, *max; float valmin, valmax, average; - char *units1, *units2; + char *unitsmin, *unitsmax, *tmodel = NULL; if (!tdp) { return; } min = tdp->min; @@ -2783,12 +2854,32 @@ static void estimate_typ(struct timing_data *tdp) } if (tmpmin && tmpmax) { if (strlen(tmpmin) > 0 && strlen(tmpmax) > 0) { - valmin = strtof(tmpmin, &units1); - valmax = strtof(tmpmax, &units2); - average = (float)((valmin + valmax) / 2.0); - tdp->ave = tprintf("%.2f%s", average, units2); - if (!eq(units1, units2)) { - printf("WARNING units do not match\n"); + valmin = strtof(tmpmin, &unitsmin); + valmax = strtof(tmpmax, &unitsmax); + if (!eq(unitsmin, unitsmax)) { + printf("WARNING estimate_typ units do not match" + " min %s max %s", tmpmin, tmpmax); + if (unitsmin[0] == unitsmax[0]) { + average = (valmin + valmax) / (float)2.0; + tdp->ave = tprintf("%.2f%cs", average, unitsmin[0]); + } else if (unitsmin[0] == 'p' && unitsmax[0] == 'n') { + valmax = (float)1000.0 * valmax; + average = (valmin + valmax) / (float)2.0; + tdp->ave = tprintf("%.2fps", average); + } else if (unitsmin[0] == 'n' && unitsmax[0] == 'p') { + tdp->ave = tprintf("%.2fns", valmin); + } else { + tdp->ave = tprintf("%.2f%s", valmin, unitsmin); + } + tmodel = get_current_tmodel(); + if (tmodel) { + printf(" using delay %s tmodel %s\n", tdp->ave, tmodel); + } else { + printf(" using delay %s\n", tdp->ave); + } + } else { + average = (float)((valmin + valmax) / 2.0); + tdp->ave = tprintf("%.2f%s", average, unitsmax); } tdp->estimate = EST_AVE; return; @@ -2854,21 +2945,38 @@ static char *select_delay(char *delay1, char *delay2) */ float val1, val2; char *units1, *units2; + char *tmodel = NULL; + BOOL warns = FALSE; val1 = strtof(delay1, &units1); val2 = strtof(delay2, &units2); if (!eq(units1, units2)) { - printf("WARNING units do not match\n"); + printf("WARNING select_delay units do not match" + " min %s max %s", delay1, delay2); + warns = TRUE; + tmodel = get_current_tmodel(); + if (tmodel) { + printf(" tmodel %s", tmodel); + } } if (mntymx_shorter.shorter_delays) { if (val1 <= val2) { + if (warns) { + printf(" using delay %s\n", delay1); + } return delay1; } } else { if (val1 >= val2) { + if (warns) { + printf(" using delay %s\n", delay1); + } return delay1; } } + if (warns) { + printf(" using delay %s\n", delay2); + } return delay2; } @@ -3196,7 +3304,25 @@ static char *get_delays_ugff(char *rem, char *d_name) return delays; } -static BOOL u_process_model(char *nline, char *original) +static char *current_tmodel = NULL; +static void set_current_tmodel(char *tmodel) +{ + if (current_tmodel) { + tfree(current_tmodel); + current_tmodel = NULL; + } + if (tmodel) { + current_tmodel = (char *)TMALLOC(char, strlen(tmodel) + 1); + strcpy(current_tmodel,tmodel); + } +} + +static char *get_current_tmodel(void) +{ + return current_tmodel; +} + +static BOOL u_process_model(char *nline, char *original, BOOL global) { char *tok, *remainder, *delays = NULL, *utype, *tmodel; BOOL retval = TRUE; @@ -3209,6 +3335,7 @@ static BOOL u_process_model(char *nline, char *original) if (!tok) { return FALSE; } tmodel = TMALLOC(char, strlen(tok) + 1); memcpy(tmodel, tok, strlen(tok) + 1); + set_current_tmodel(tmodel); /* model utype */ tok = strtok(NULL, " \t("); if (!tok) { tfree(tmodel); return FALSE; } @@ -3221,26 +3348,26 @@ static BOOL u_process_model(char *nline, char *original) if (eq(utype, "ugate")) { delays = get_delays_ugate(remainder); add_delays_to_model_xlator((delays ? delays : ""), - utype, "", tmodel); + utype, "", tmodel, global); if (delays) { tfree(delays); } } else if (eq(utype, "utgate")) { delays = get_delays_utgate(remainder); add_delays_to_model_xlator((delays ? delays : ""), - utype, "", tmodel); + utype, "", tmodel, global); if (delays) { tfree(delays); } } else if (eq(utype, "ueff")) { delays = get_delays_ueff(remainder); add_delays_to_model_xlator((delays ? delays : ""), - utype, "", tmodel); + utype, "", tmodel, global); if (delays) { tfree(delays); } } else if (eq(utype, "ugff")) { delays = get_delays_ugff(remainder, "d_dlatch"); add_delays_to_model_xlator((delays ? delays : ""), - utype, "d_dlatch", tmodel); + utype, "d_dlatch", tmodel, global); if (delays) { tfree(delays); } delays = get_delays_ugff(remainder, "d_srlatch"); add_delays_to_model_xlator((delays ? delays : ""), - utype, "d_srlatch", tmodel); + utype, "d_srlatch", tmodel, global); if (delays) { tfree(delays); } } else if (eq(utype, "uio")) { /* skip uio models */ @@ -3249,7 +3376,7 @@ static BOOL u_process_model(char *nline, char *original) } else if (eq(utype, "udly")) { delays = get_delays_udly(remainder); add_delays_to_model_xlator((delays ? delays : ""), - utype, "", tmodel); + utype, "", tmodel, global); if (delays) { tfree(delays); } } else { retval = FALSE; @@ -3258,6 +3385,7 @@ static BOOL u_process_model(char *nline, char *original) } else { retval = FALSE; } + set_current_tmodel(NULL); tfree(tmodel); tfree(utype); return retval; @@ -3931,7 +4059,7 @@ static Xlatorp translate_pull(struct instance_hdr *hdr, char *start) char *model_name = NULL, *inst_stmt = NULL, *model_stmt = NULL; int i, numpulls; Xlatorp xp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; itype = hdr->instance_type; iname = hdr->instance_name; @@ -3974,7 +4102,7 @@ static Xlatorp translate_dlyline(struct instance_hdr *hdr, char *start) char *itype, *iname, *newline = NULL, *tok; char *model_name = NULL, *tmodel = NULL; Xlatorp xp = NULL; - Xlatep xdata = NULL; + Xlate_datap xdata = NULL; DS_CREATE(statement, 128); itype = hdr->instance_type; @@ -4168,7 +4296,7 @@ BOOL u_process_instance(char *nline) if (!xspice) { if (eq(itype, "logicexp")) { delete_instance_hdr(hdr); - if (ps_port_directions & 4) { + if (ps_ports_and_pins & 4) { printf("TRANS_IN %s\n", nline); } behav_ret = f_logicexp(nline); @@ -4183,7 +4311,7 @@ BOOL u_process_instance(char *nline) return behav_ret; } else if (eq(itype, "pindly")) { delete_instance_hdr(hdr); - if (ps_port_directions & 4) { + if (ps_ports_and_pins & 4) { printf("TRANS_IN %s\n", nline); } behav_ret = f_pindly(nline); @@ -4204,7 +4332,7 @@ BOOL u_process_instance(char *nline) return FALSE; } } - if (ps_port_directions & 4) { + if (ps_ports_and_pins & 4) { printf("TRANS_IN %s\n", nline); } /* Skip past instance name, type, pwr, gnd */ @@ -4261,7 +4389,7 @@ BOOL u_process_instance(char *nline) a Pspice .model timing model statement with all the '+' continuations added minus the '+'. */ -BOOL u_process_model_line(char *line) +BOOL u_process_model_line(char *line, BOOL global) { /* Translate a .model line to find the delays */ /* Return TRUE if ok */ @@ -4271,12 +4399,12 @@ BOOL u_process_model_line(char *line) if (n > 0 && line[n] == '\n') line[n] = '\0'; if (strncmp(line, ".model ", strlen(".model ")) == 0) { - if (ps_port_directions & 4) { + if (ps_ports_and_pins & 4) { printf("TRANS_IN %s\n", line); } newline = TMALLOC(char, strlen(line) + 1); (void) memcpy(newline, line, strlen(line) + 1); - retval = u_process_model(newline, line); + retval = u_process_model(newline, line, global); tfree(newline); return retval; } else { diff --git a/src/include/ngspice/udevices.h b/src/include/ngspice/udevices.h index 7fe19a881..265b578de 100644 --- a/src/include/ngspice/udevices.h +++ b/src/include/ngspice/udevices.h @@ -7,11 +7,11 @@ struct udevices_info { }; BOOL u_process_instance(char *line); -BOOL u_process_model_line(char *line); +BOOL u_process_model_line(char *line, BOOL global); BOOL u_check_instance(char *line); void initialize_udevice(char *subckt_line); struct card *replacement_udevice_cards(void); -void cleanup_udevice(void); +void cleanup_udevice(BOOL global); void u_add_instance(char *str); void u_add_logicexp_model(char *tmodel, char *xspice_gate, char *model_name); void u_remember_pin(char *name, int type);