From 797795e7c07c01b552b9a0ae219686da894b8a73 Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 8 Apr 2023 15:24:00 +0200 Subject: [PATCH] Shift all compatibility handlinmg from inpcom.c into its own source files inpcompat.c and inpcompat.h --- src/frontend/Makefile.am | 2 + src/frontend/inpcom.c | 1830 +------------------------------------ src/frontend/inpcom.h | 4 + src/frontend/inpcompat.c | 1868 ++++++++++++++++++++++++++++++++++++++ src/frontend/inpcompat.h | 20 + 5 files changed, 1898 insertions(+), 1826 deletions(-) create mode 100644 src/frontend/inpcompat.c create mode 100644 src/frontend/inpcompat.h diff --git a/src/frontend/Makefile.am b/src/frontend/Makefile.am index 9c765ab27..14ae122ef 100644 --- a/src/frontend/Makefile.am +++ b/src/frontend/Makefile.am @@ -130,6 +130,8 @@ libfte_la_SOURCES = \ inp.h \ inpcom.c \ inpcom.h \ + inpcompat.c \ + inpcompat.h \ inpc_probe.c \ interp.c \ interp.h \ diff --git a/src/frontend/inpcom.c b/src/frontend/inpcom.c index 750a3d5f2..e2344177a 100644 --- a/src/frontend/inpcom.c +++ b/src/frontend/inpcom.c @@ -46,6 +46,8 @@ Author: 1985 Wayne A. Christopher #include "subckt.h" #include "variable.h" +#include "inpcompat.h" + #ifdef XSPICE /* gtri - add - 12/12/90 - wbk - include new stuff */ #include "ngspice/enh.h" @@ -53,11 +55,6 @@ Author: 1985 Wayne A. Christopher /* gtri - end - 12/12/90 */ #endif -#define INTEGRATE_UDEVICES -#ifdef INTEGRATE_UDEVICES -#include "ngspice/udevices.h" -#endif - /* SJB - Uncomment this line for debug tracing */ /*#define TRACE*/ @@ -139,7 +136,7 @@ static void inp_reorder_params( static int inp_split_multi_param_lines(struct card *deck, int line_number); static void inp_sort_params(struct card *param_cards, struct card *card_bf_start, struct card *s_c, struct card *e_c); -static char *inp_remove_ws(char *s); +char *inp_remove_ws(char *s); static void inp_compat(struct card *deck); static void inp_bsource_compat(struct card *deck); static bool inp_temper_compat(struct card *card); @@ -180,12 +177,6 @@ static struct modellist *inp_find_model( struct nscope *scope, const char *name); void tprint(struct card *deck); -static void print_compat_mode(void); -static void set_compat_mode(void); -static struct card *pspice_compat(struct card *newcard); -static void pspice_compat_a(struct card *oldcard); -static struct card *ltspice_compat(struct card *oldcard); -static void ltspice_compat_a(struct card *oldcard); static void inp_repair_dc_ps(struct card* oldcard); static void inp_get_w_l_x(struct card* oldcard); @@ -772,98 +763,6 @@ char *find_back_assignment(const char *p, const char *start) } -/* Set a compatibility flag. -Currently available are flags for: -- LTSPICE, HSPICE, Spice3, PSPICE, KiCad, Spectre, XSPICE -*/ -struct compat newcompat; -static 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 */ -static 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"); - } -} - - /* We check x lines for nf=, w= and l= and fill in their values. To be used when expanding subcircuits with binned model cards. @@ -3182,7 +3081,7 @@ static char *inp_fix_subckt(struct names *subckt_w_params, char *s) * thats odd and very naive business */ -static char *inp_remove_ws(char *s) +char *inp_remove_ws(char *s) { char *x = s; char *d = s; @@ -8082,1727 +7981,6 @@ static void inp_meas_current(struct card *deck) } } -/* 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 {()} */ -static 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 */ -static 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' - */ -static 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 */ -static void ltspice_compat_a(struct card *oldcard) -{ - oldcard->nextcard = ltspice_compat(oldcard->nextcard); -} /* syntax check: Check if we have a .control ... .endc pair, diff --git a/src/frontend/inpcom.h b/src/frontend/inpcom.h index fa8d243f8..e7cf5eaec 100644 --- a/src/frontend/inpcom.h +++ b/src/frontend/inpcom.h @@ -9,4 +9,8 @@ struct card *insert_new_line(struct card *card, char *line, int linenum, int linenum_orig); char *inp_pathresolve(const char *name); + +extern char* inp_remove_ws(char* s); +extern char* search_plain_identifier(char* str, const char* identifier); + #endif diff --git a/src/frontend/inpcompat.c b/src/frontend/inpcompat.c new file mode 100644 index 000000000..4c0ce979a --- /dev/null +++ b/src/frontend/inpcompat.c @@ -0,0 +1,1868 @@ +/********** +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); +} diff --git a/src/frontend/inpcompat.h b/src/frontend/inpcompat.h new file mode 100644 index 000000000..d657a8b58 --- /dev/null +++ b/src/frontend/inpcompat.h @@ -0,0 +1,20 @@ +/********** +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 +*/ + +extern void print_compat_mode(void); +extern void set_compat_mode(void); +extern void pspice_compat_a(struct card* oldcard); +extern void ltspice_compat_a(struct card* oldcard); +extern struct card* pspice_compat(struct card* newcard); +extern struct card* ltspice_compat(struct card* oldcard); + +