/********** Copyright 2023 The ngspice team. All rights reserved. License: Three-clause BCD Author: 2023 Holger Vogt **********/ /* For dealing with compatibility transformations PSPICE, LTSPICE and others */ #include "ngspice/ngspice.h" #include "ngspice/compatmode.h" #include "ngspice/cpdefs.h" #include "ngspice/dstring.h" #include "ngspice/dvec.h" #include "ngspice/ftedefs.h" #include "ngspice/fteext.h" #include "ngspice/fteinp.h" #include "numparam/general.h" #include #include #include #include #if !defined(__MINGW32__) && !defined(_MSC_VER) #include #endif #include "../misc/util.h" /* ngdirname() */ #include "inpcom.h" #include "ngspice/stringskip.h" #include "ngspice/stringutil.h" #include "ngspice/wordlist.h" #include "subckt.h" #include "variable.h" #define INTEGRATE_UDEVICES #ifdef INTEGRATE_UDEVICES #include "ngspice/udevices.h" #endif void print_compat_mode(void); void set_compat_mode(void); struct card* pspice_compat(struct card* newcard); void pspice_compat_a(struct card* oldcard); struct card* ltspice_compat(struct card* oldcard); void ltspice_compat_a(struct card* oldcard); /* Set a compatibility flag. Currently available are flags for: - LTSPICE, HSPICE, Spice3, PSPICE, KiCad, Spectre, XSPICE */ struct compat newcompat; void set_compat_mode(void) { char behaviour[80]; newcompat.hs = FALSE; newcompat.ps = FALSE; newcompat.xs = FALSE; newcompat.lt = FALSE; newcompat.ki = FALSE; newcompat.a = FALSE; newcompat.spe = FALSE; newcompat.isset = FALSE; newcompat.s3 = FALSE; newcompat.mc = FALSE; if (cp_getvar("ngbehavior", CP_STRING, behaviour, sizeof(behaviour))) { if (strstr(behaviour, "hs")) newcompat.isset = newcompat.hs = TRUE; /*HSPICE*/ if (strstr(behaviour, "ps")) newcompat.isset = newcompat.ps = TRUE; /*PSPICE*/ if (strstr(behaviour, "xs")) newcompat.isset = newcompat.xs = TRUE; /*XSPICE*/ if (strstr(behaviour, "lt")) newcompat.isset = newcompat.lt = TRUE; /*LTSPICE*/ if (strstr(behaviour, "ki")) newcompat.isset = newcompat.ki = TRUE; /*KiCad*/ if (strstr(behaviour, "a")) newcompat.isset = newcompat.a = TRUE; /*complete netlist, used in conjuntion with other mode*/ if (strstr(behaviour, "ll")) newcompat.isset = newcompat.ll = TRUE; /*all (currently not used)*/ if (strstr(behaviour, "s3")) newcompat.isset = newcompat.s3 = TRUE; /*spice3 only*/ if (strstr(behaviour, "eg")) newcompat.isset = newcompat.eg = TRUE; /*EAGLE*/ if (strstr(behaviour, "spe")) { newcompat.isset = newcompat.spe = TRUE; /*Spectre*/ newcompat.ps = newcompat.lt = newcompat.ki = newcompat.eg = FALSE; } if (strstr(behaviour, "mc")) { newcompat.isset = FALSE; newcompat.mc = TRUE; /*make check*/ } } if (newcompat.hs && newcompat.ps) { fprintf(stderr, "Warning: hs and ps compatibility are mutually exclusive, switch to ps!\n"); newcompat.hs = FALSE; } /* reset everything for 'make check' */ if (newcompat.mc) newcompat.eg = newcompat.hs = newcompat.spe = newcompat.ps = newcompat.xs = newcompat.ll = newcompat.lt = newcompat.ki = newcompat.a = FALSE; } /* Print the compatibility flags */ void print_compat_mode(void) { if (newcompat.mc) /* make check */ return; if (newcompat.isset) { fprintf(stdout, "\n"); fprintf(stdout, "Note: Compatibility modes selected:"); if (newcompat.hs) fprintf(stdout, " hs"); if (newcompat.ps) fprintf(stdout, " ps"); if (newcompat.xs) fprintf(stdout, " xs"); if (newcompat.lt) fprintf(stdout, " lt"); if (newcompat.ki) fprintf(stdout, " ki"); if (newcompat.ll) fprintf(stdout, " ll"); if (newcompat.s3) fprintf(stdout, " s3"); if (newcompat.eg) fprintf(stdout, " eg"); if (newcompat.spe) fprintf(stdout, " spe"); if (newcompat.a) fprintf(stdout, " a"); fprintf(stdout, "\n\n"); } else { fprintf(stdout, "\n"); fprintf(stdout, "Note: No compatibility mode selected!\n\n"); } } /* replace the E and G source TABLE function by a B source pwl * (used by ST OpAmps and comparators of Infineon models). * E_RO_3 VB_3 VB_4 VALUE={ TABLE( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10 * )*I(VreadIo)} * will become * BE_RO_3_1 TABLE_NEW_1 0 v = pwl( V(VCCP,VCCN), 2 , 35 , 3.3 , 15 , 5 , 10) * E_RO_3 VB_3 VB_4 VALUE={ V(TABLE_NEW_1)*I(VreadIo)} */ static void replace_table(struct card *startcard) { struct card *card; static int numb = 0; for (card = startcard; card; card = card->nextcard) { char *cut_line = card->line; if (*cut_line == 'e' || *cut_line == 'g') { char *valp = search_plain_identifier(cut_line, "value"); char *valp2 = search_plain_identifier(cut_line, "cur"); if (valp || (valp2 && *cut_line == 'g')) { char *ftablebeg = strstr(cut_line, "table("); while (ftablebeg) { /* get the beginning of the line */ char *begline = copy_substring(cut_line, ftablebeg); /* get the table function */ char *tabfun = gettok_char(&ftablebeg, ')', TRUE, TRUE); /* the new e, g line */ char *neweline = tprintf("%s v(table_new_%d)%s", begline, numb, ftablebeg); char *newbline = tprintf("btable_new_%d table_new_%d 0 v=pwl%s", numb, numb, tabfun + 5); numb++; tfree(tabfun); tfree(begline); tfree(card->line); card->line = cut_line = neweline; insert_new_line(card, newbline, 0, card->linenum_orig, card->linesource); /* 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, .model, or .param (which all three 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 || ciprefix(".param", cut_line)) { cut_line = strchr(cut_line, '{'); if (cut_line) { int level = 1; cut_line++; while (*cut_line != '\0') { if (*cut_line == '{') { level++; if (level > 1) *cut_line = '('; } else if (*cut_line == '}') { if (level > 1) *cut_line = ')'; level--; } cut_line++; } } } } } #ifdef INTEGRATE_UDEVICES static void list_the_cards(struct card *startcard, char *prefix) { struct card *card; if (!startcard) { return; } for (card = startcard; card; card = card->nextcard) { char* cut_line = card->line; printf("%s %s\n", prefix, cut_line); } } static struct card *the_last_card(struct card *startcard) { struct card *card, *lastcard = NULL; if (!startcard) { return NULL; } for (card = startcard; card; card = card->nextcard) { lastcard = card; } return lastcard; } static void remove_old_cards(struct card *first, struct card *stop) { struct card *x, *y, *next = NULL, *nexta = NULL; if (!first || !stop || (first == stop)) { return; } for (x = first; (x && (x != stop)); x = next) { if (x->line) { tfree(x->line); } if (x->error) { tfree(x->error); } for (y = x->actualLine; y; y = nexta) { if (y->line) { tfree(y->line); } if (y->error) { tfree(y->error); } nexta = y->nextcard; tfree(y); } next = x->nextcard; tfree(x); } } static struct card *u_instances(struct card *startcard) { struct card *card, *returncard = NULL, *subcktcard = NULL; struct card *newcard = NULL, *last_newcard = NULL; int models_ok = 0, models_not_ok = 0; int udev_ok = 0, udev_not_ok = 0; BOOL create_called = FALSE, repeat_pass = FALSE; BOOL skip_next = FALSE; struct card *c = startcard; BOOL insub = FALSE; int ps_global_tmodels = 0; if (!cp_getvar("ps_global_tmodels", CP_NUM, &ps_global_tmodels, 0)) { ps_global_tmodels = 0; } if (ps_global_tmodels) { initialize_udevice(NULL); /* First scan for global timing models */ while (c) { char *line = c->line; if (ciprefix(".subckt", line)) { insub = TRUE; } else if (ciprefix(".ends", line)) { insub = FALSE; } if (!insub && ciprefix(".model", line)) { (void) u_process_model_line(line, TRUE); } c = c->nextcard; } } /* Now scan for subckts containing U* instances and local timing models */ card = startcard; while (card) { char *cut_line = card->line; skip_next = FALSE; if (ciprefix(".subckt", cut_line)) { models_ok = models_not_ok = 0; udev_ok = udev_not_ok = 0; subcktcard = card; if (!repeat_pass) { if (create_called) { cleanup_udevice(FALSE); } initialize_udevice(subcktcard->line); create_called = TRUE; } } else if (ciprefix(".ends", cut_line)) { if (repeat_pass) { newcard = replacement_udevice_cards(); if (newcard) { char *tmp = NULL, *pos, *posp, *new_str = NULL, *cl; struct card* tmpc; /* replace linenum_orig and linesource */ for (tmpc = newcard; tmpc; tmpc = tmpc->nextcard) { tmpc->linenum_orig = subcktcard->linenum_orig; tmpc->linesource = subcktcard->linesource; } DS_CREATE(ds_tmp, 128); /* Pspice definition of .subckt card: .SUBCKT [node]* + [OPTIONAL: < = >*] + [PARAMS: < = >* ] + [TEXT: < = >* ] ... .ENDS */ cl = subcktcard->line; tmp = TMALLOC(char, strlen(cl) + 1); (void) memcpy(tmp, cl, strlen(cl) + 1); pos = strstr(tmp, "optional:"); posp = strstr(tmp, "params:"); ds_clear(&ds_tmp); /* If there is an optional: and a param: then posp > pos */ if (pos) { /* Remove the optional: section if present */ *pos = '\0'; if (posp) { ds_cat_str(&ds_tmp, tmp); ds_cat_str(&ds_tmp, posp); new_str = copy(ds_get_buf(&ds_tmp)); } else { new_str = copy(tmp); } } else { new_str = copy(tmp); } ds_free(&ds_tmp); tfree(tmp); remove_old_cards(subcktcard->nextcard, card); subcktcard->nextcard = newcard; tfree(subcktcard->line); subcktcard->line = new_str; if (ft_ngdebug) { printf("%s\n", new_str); list_the_cards(newcard, "Replacement:"); } last_newcard = the_last_card(newcard); if (last_newcard) { last_newcard->nextcard = card; // the .ends card } } else { models_ok = models_not_ok = 0; udev_ok = udev_not_ok = 0; } } if (models_not_ok > 0 || udev_not_ok > 0) { repeat_pass = FALSE; cleanup_udevice(FALSE); create_called = FALSE; } else if (udev_ok > 0) { repeat_pass = TRUE; card = subcktcard; skip_next = TRUE; } else { repeat_pass = FALSE; cleanup_udevice(FALSE); create_called = FALSE; } subcktcard = NULL; } else if (ciprefix(".model", cut_line)) { if (subcktcard && !repeat_pass) { // Add .model local to subckt if (!u_process_model_line(cut_line, FALSE)) { models_not_ok++; } else { models_ok++; } } } else if (ciprefix("u", cut_line) || ciprefix("x", cut_line)) { /* U* device instance or X* instance of a subckt */ if (subcktcard) { if (repeat_pass) { if (!u_process_instance(cut_line)) { repeat_pass = FALSE; cleanup_udevice(FALSE); create_called = FALSE; subcktcard = NULL; models_ok = models_not_ok = 0; udev_ok = udev_not_ok = 0; skip_next = FALSE; } } else { if (u_check_instance(cut_line)) { udev_ok++; } else { udev_not_ok++; } } } } else { if (!ciprefix("*", cut_line)) { udev_not_ok++; } } if (!skip_next) { card = card->nextcard; } } if (create_called) { cleanup_udevice(FALSE); } cleanup_udevice(TRUE); return returncard; } #endif /**** PSPICE to ngspice ************** * .model replacement in ako (a kind of) model descriptions * replace the E source TABLE function by a B source pwl * add predefined params TEMP, VT, GMIN to beginning of deck * add predefined params TEMP, VT, GMIN to beginning of each .subckt call * add .functions limit, pwr, pwrs, stp, if, int * replace vswitch part S S1 D S DG GND SWN .MODEL SWN VSWITCH(VON = { 0.55 } VOFF = { 0.49 } RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) * by as1 %vd(DG GND) % gd(D S) aswn .model aswn aswitch(cntl_off={0.49} cntl_on={0.55} r_off={1G} + r_on={ 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } log = TRUE) * replace vswitch part S_ST S1 D S DG GND S_ST .MODEL S_ST VSWITCH(VT = { 1.5 } VH = { 0.3 } RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) * by the classical voltage controlled ngspice switch S1 D S DG GND SWN .MODEL S_ST SW(VT = { 1.5 } VH = { 0.3 } RON = { 1 / (2 * M*(W / LE)*(KPN / 2) * 10) } ROFF = { 1G }) switch parameter td is not yet supported * replace & by && * replace | by || * in R instance, replace TC = xx1, xx2 by TC1=xx1 TC2=xx2 * replace T_ABS by temp and T_REL_GLOBAL by dtemp in .model cards * get the area factor for diodes and bipolar devices * in subcircuit .subckt and X lines with 'params:' statement replace comma separator by space. Do nothing if comma is inside of {}. * in .model, if double curly braces {{}}, replace the inner by {()} */ struct card *pspice_compat(struct card *oldcard) { struct card *card, *newcard, *nextcard; struct vsmodels *modelsfound = NULL; int skip_control = 0; /* .model replacement in ako (a kind of) model descriptions * in first .subckt and top level only */ struct card *errcard; if ((errcard = ako_model(oldcard)) != NULL) { fprintf(stderr, "Error: no model found for %s\n", errcard->line); controlled_exit(1); } /* Process .distribution cards. */ do_distribution(oldcard); /* replace TABLE function in E source */ replace_table(oldcard); /* remove double braces */ rem_double_braces(oldcard); /* add predefined params TEMP, VT, GMIN to beginning of deck */ char *new_str = copy(".param temp = 'temper'"); newcard = insert_new_line(NULL, new_str, 1, 0, "internal"); new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'"); nextcard = insert_new_line(newcard, new_str, 2, 0, "internal"); new_str = copy(".param gmin = 1e-12"); nextcard = insert_new_line(nextcard, new_str, 3, 0, "internal"); /* 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, "internal"); new_str = copy(".func pwr(x, a) { pow(x, a) }"); nextcard = insert_new_line(nextcard, new_str, 5, 0, "internal"); new_str = copy(".func pwrs(x, a) { sgn(x) * pow(x, a) }"); nextcard = insert_new_line(nextcard, new_str, 6, 0, "internal"); new_str = copy(".func stp(x) { u(x) }"); nextcard = insert_new_line(nextcard, new_str, 7, 0, "internal"); new_str = copy(".func if(a, b, c) {ternary_fcn( a , b , c )}"); nextcard = insert_new_line(nextcard, new_str, 8, 0, "internal"); new_str = copy(".func int(x) { sign(x)*floor(abs(x)) }"); nextcard = insert_new_line(nextcard, new_str, 9, 0, "internal"); 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, card->linenum_orig, card->linesource); new_str = copy(".param vt = '(temper + 273.15) * 8.6173303e-5'"); nextcard = insert_new_line(nextcard, new_str, 1, card->linenum_orig, card->linesource); /* 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"); controlled_exit(1); } 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 %d\n %s\n" "from file\n" " %s\n", card->linenum_orig, card->line, card->linesource); 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, "internal"); 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, "internal"); 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, "internal"); 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, "internal"); 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->linesource); 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); }