/********** Copyright 1990 Regents of the University of California. All rights reserved. Author: 1985 Thomas L. Quarles, 1991 David A. Gates Modified: 2001 Paolo Nenzi (Cider Integration) **********/ #include "ngspice/ngspice.h" #include "ngspice/inpdefs.h" #include "ngspice/ifsim.h" #include "ngspice/cpstd.h" #include "ngspice/fteext.h" #include "inp.h" #include #ifdef CIDER /* begin Cider Integration */ #include "ngspice/numcards.h" #include "ngspice/carddefs.h" #include "ngspice/numgen.h" #include "ngspice/suffix.h" extern IFcardInfo *INPcardTab[]; extern int INPnumCards; #define E_MISSING -1 #define E_AMBIGUOUS -2 static int INPparseNumMod( CKTcircuit* ckt, INPmodel *model, INPtables *tab, char **errMessage ); static int INPfindCard( char *name, IFcardInfo *table[], int numCards ); static int INPfindParm( char *name, IFparm *table, int numParms ); /* end Cider Integration */ #endif /* CIDER */ extern INPmodel *modtab; /* code moved from INPgetMod */ static int create_model( CKTcircuit* ckt, INPmodel* modtmp, INPtables* tab ) { IFvalue* val; char* err = NULL; char* line; char* parm; char* temp; int error = 0; int j; char *endptr; double dval; /* not already defined, so create & give parameters */ error = ft_sim->newModel (ckt, modtmp->INPmodType, &(modtmp->INPmodfast), modtmp->INPmodName); if (error) return error; /* parameter isolation, identification, binding */ #ifdef CIDER /* begin cider integration */ /* Handle Numerical Models Differently */ if ( modtmp->INPmodType == INPtypelook("NUMD") || modtmp->INPmodType == INPtypelook("NBJT") || modtmp->INPmodType == INPtypelook("NUMD2") || modtmp->INPmodType == INPtypelook("NBJT2") || modtmp->INPmodType == INPtypelook("NUMOS") ) { error = INPparseNumMod( ckt, modtmp, tab, &err ); if (error) return error; } else { /* It's an analytical model */ #endif /* CIDER */ line = modtmp->INPmodLine->line; #ifdef TRACE /* SDB debug statement */ printf("In INPgetMod, inserting new model into table. line = %s . . . \n", line); #endif INPgetTok(&line, &parm, 1); /* throw away '.model' */ tfree(parm); INPgetTok(&line, &parm, 1); /* throw away 'modname' */ tfree(parm); while (*line != 0) { INPgetTok(&line, &parm, 1); if (!*parm) continue; for (j = 0; j < *(ft_sim->devices[modtmp->INPmodType]->numModelParms); j++) { if (strcmp(parm, "txl") == 0) { if (strcmp("cpl", ft_sim->devices[modtmp->INPmodType]->modelParms[j].keyword) == 0) { strcpy(parm, "cpl"); } } if (strcmp(parm, ft_sim->devices[modtmp->INPmodType]->modelParms[j].keyword) == 0) { val = INPgetValue(ckt, &line, ft_sim->devices[modtmp->INPmodType]->modelParms[j].dataType, tab); error = ft_sim->setModelParm (ckt, modtmp->INPmodfast, ft_sim->devices[modtmp->INPmodType]->modelParms[j].id, val, NULL); if (error) return error; break; } } /* end for(j = 0 . . .*/ if (strcmp(parm, "level") == 0) { /* just grab the level number and throw away */ /* since we already have that info from pass1 */ val = INPgetValue(ckt, &line, IF_REAL, tab); } else if (j >= *(ft_sim->devices[modtmp->INPmodType]->numModelParms)) { /* want only the parameter names in output - not the values */ errno = 0; /* To distinguish success/failure after call */ dval = strtod(parm, &endptr); /* Check for various possible errors */ if ((errno == ERANGE && dval == HUGE_VAL) || errno != 0) { perror("strtod"); controlled_exit(EXIT_FAILURE); } if (endptr == parm) { /* it was no number - it is really a string */ temp = TMALLOC(char, 40 + strlen(parm)); (void) sprintf(temp, "unrecognized parameter (%s) - ignored", parm); err = INPerrCat(err, temp); } } FREE(parm); } #ifdef CIDER } /* analytical vs. numerical model parsing */ #endif modtmp->INPmodUsed = 1; modtmp->INPmodLine->error = err; return 0; } static bool parse_line( char* line, char* tokens[], int num_tokens, double values[], bool found[] ) { char* token = NULL; int get_index = -1; int i; bool flag = TRUE; int error; for ( i = 0; i < num_tokens; i++ ) found[i] = FALSE; while( *line != '\0' ) { if ( get_index != -1 ) { values[get_index] = INPevaluate( &line, &error, 1 ); found[get_index] = TRUE; get_index = -1; continue; } INPgetNetTok( &line, &token, 1 ); for ( i = 0; i < num_tokens; i++ ) if ( strcmp( tokens[i], token ) == 0 ) get_index = i; txfree(token); } for ( i = 0; i < num_tokens; i++ ) if ( found[i] == FALSE ) { flag = FALSE; break; } return flag; } static bool is_equal( double result, double expectedResult ) { if (fabs(result - expectedResult) < 1e-15) return TRUE; else return FALSE; } static bool in_range( double value, double min, double max ) { if ( (is_equal( value, min ) == TRUE) || /* the standard binning rule is: min <= value < max */ (min < value && value < max) ) return TRUE; else return FALSE; } char* INPgetModBin( CKTcircuit* ckt, char* name, INPmodel** model, INPtables* tab, char* line ) { INPmodel* modtmp; double l, w, lmin, lmax, wmin, wmax; double parse_values[4]; bool parse_found[4]; static char* instance_tokens[] = { "l", "w" }; static char* model_tokens[] = { "lmin", "lmax", "wmin", "wmax" }; int error; double scale; char *err = NULL; if (!cp_getvar("scale", CP_REAL, &scale)) scale = 1; *model = NULL; if ( parse_line( line, instance_tokens, 2, parse_values, parse_found ) != TRUE ) return NULL; l = parse_values[0]*scale; w = parse_values[1]*scale; for ( modtmp = modtab; modtmp != NULL; modtmp = modtmp->INPnextModel ) { if ( /* This is the list of binable models */ modtmp->INPmodType != INPtypelook ("BSIM3") && modtmp->INPmodType != INPtypelook ("BSIM3v32") && modtmp->INPmodType != INPtypelook ("BSIM3v0") && modtmp->INPmodType != INPtypelook ("BSIM3v1") && modtmp->INPmodType != INPtypelook ("BSIM4") && modtmp->INPmodType != INPtypelook ("BSIM4v4") && modtmp->INPmodType != INPtypelook ("BSIM4v5") && modtmp->INPmodType != INPtypelook ("BSIM4v6") && modtmp->INPmodType != INPtypelook ("HiSIM2") && modtmp->INPmodType != INPtypelook ("HiSIMHV") ) continue; /* We left the loop if the model is not in the list */ if (modtmp->INPmodType < 0) { /* First check for illegal model type */ /* illegal device type, so can't handle */ *model = NULL; err = TMALLOC(char, 35 + strlen(name)); (void) sprintf(err,"Unknown device type for model %s \n", name); return (err); } /* end of checking for illegal model */ if ( parse_line( modtmp->INPmodLine->line, model_tokens, 4, parse_values, parse_found ) != TRUE ) continue; lmin = parse_values[0]; lmax = parse_values[1]; wmin = parse_values[2]; wmax = parse_values[3]; if ( strncmp( modtmp->INPmodName, name, strlen( name ) ) == 0 && in_range( l, lmin, lmax ) && in_range( w, wmin, wmax ) ) { if ( !modtmp->INPmodUsed ) { error = create_model( ckt, modtmp, tab ); if ( error ) return NULL; } *model = modtmp; return NULL; } } return NULL; } char *INPgetMod(CKTcircuit *ckt, char *name, INPmodel ** model, INPtables * tab) { INPmodel *modtmp; char *err = NULL; int error; #ifdef TRACE /* SDB debug statement */ printf("In INPgetMod, examining model %s . . . \n", name); #endif for (modtmp = modtab; modtmp != NULL; modtmp = modtmp->INPnextModel) { #ifdef TRACE /* SDB debug statement */ printf("In INPgetMod, comparing %s against stored model %s . . . \n", name, modtmp->INPmodName); #endif if (strcmp(modtmp->INPmodName, name) == 0) { /* found the model in question - now instantiate if necessary */ /* and return an appropriate pointer to it */ if (modtmp->INPmodType < 0) { /* First check for illegal model type */ /* illegal device type, so can't handle */ *model = NULL; err = TMALLOC(char, 35 + strlen(name)); (void) sprintf(err,"Unknown device type for model %s \n", name); #ifdef TRACE /* SDB debug statement */ printf("In INPgetMod, illegal device type for model %s . . . \n", name); #endif return (err); } /* end of checking for illegal model */ if (! modtmp->INPmodUsed) { /* Check if model is already defined */ error = create_model( ckt, modtmp, tab ); if ( error ) return INPerror(error); } *model = modtmp; return (NULL); } } /* didn't find model - ERROR - return model */ *model = NULL; err = TMALLOC(char, 60 + strlen(name)); (void) sprintf(err, "Unable to find definition of model %s - default assumed \n", name); #ifdef TRACE /* SDB debug statement */ printf("In INPgetMod, didn't find model for %s, using default . . . \n", name); #endif return (err); } #ifdef CIDER /* * Parse a numerical model by running through the list of original * input cards which make up the model * Given: * 1. First card looks like: .model modname modtype * 2. Other cards look like: +? where ? tells us what * to do with the next card: * '#$*' = comment card * '+' = continue previous card * other = new card */ static int INPparseNumMod( CKTcircuit* ckt, INPmodel *model, INPtables *tab, char **errMessage ) { card *txtCard; /* Text description of a card */ GENcard *tmpCard; /* Processed description of a card */ IFcardInfo *info; /* Info about the type of card located */ char *line; char *cardName = NULL; /* name of a card */ char *parm; /* name of a parameter */ int cardType; /* type/index for the current card */ int cardNum = 0; /* number of this card in the overall line */ int lastType = E_MISSING; /* type of previous card */ char *err = NULL, *tmp; /* Strings for error messages */ IFvalue *value; int error, idx, invert; /* Chase down to the top of the list of actual cards */ txtCard = model->INPmodLine->actualLine; /* Skip the first card if it exists since there's nothing interesting */ /* txtCard will be empty if the numerical model is empty */ if (txtCard) txtCard = txtCard->nextcard; /* Now parse each remaining card */ while (txtCard) { line = txtCard->line; cardType = E_MISSING; cardNum++; /* Skip the initial '+' and any whitespace. */ line++; while (*line == ' ' || *line == '\t') line++; switch (*line) { case '*': case '$': case '#': case '\0': case '\n': /* comment or empty cards */ lastType = E_MISSING; break; case '+': /* continuation card */ if (lastType >= 0) { cardType = lastType; while (*line == '+') line++; /* Skip leading '+'s */ } else { tmp = TMALLOC(char, 55); (void) sprintf(tmp, "Error on card %d : illegal continuation \'+\' - ignored", cardNum); err = INPerrCat(err,tmp); lastType = E_MISSING; break; } /* FALL THRU when continuing a card */ default: if (cardType == E_MISSING) { /* new command card */ if (cardName) FREE(cardName); /* get rid of old card name */ INPgetTok(&line,&cardName,1); /* get new card name */ if (*cardName) { /* Found a name? */ cardType = INPfindCard(cardName,INPcardTab,INPnumCards); if (cardType >= 0) { /* Add card structure to model */ info = INPcardTab[cardType]; error = info->newCard ((void **) &tmpCard, model->INPmodfast ); if (error) return(error); /* Handle parameter-less cards */ } else if (cinprefix( cardName, "title", 3 ) ) { /* Do nothing */ } else if (cinprefix( cardName, "comment", 3 ) ) { /* Do nothing */ } else if (cinprefix( cardName, "end", 3 ) ) { /* Terminate parsing */ txtCard = ((card *) 0); cardType = E_MISSING; } else { /* Error */ tmp = TMALLOC(char, 55 + strlen(cardName)); (void) sprintf(tmp, "Error on card %d : unrecognized name (%s) - ignored", cardNum, cardName ); err = INPerrCat(err,tmp); } } } if (cardType >= 0) { /* parse the rest of this line */ while (*line) { /* Strip leading carat from booleans */ if (*line == '^') { invert = TRUE; line++; /* Skip the '^' */ } else { invert = FALSE; } INPgetTok(&line,&parm,1); if (!*parm) break; idx = INPfindParm(parm, info->cardParms, info->numParms); if (idx == E_MISSING) { /* parm not found */ tmp = TMALLOC(char, 60 + strlen(parm)); (void)sprintf(tmp, "Error on card %d : unrecognized parameter (%s) - ignored", cardNum, parm); err = INPerrCat(err, tmp); } else if (idx == E_AMBIGUOUS) { /* parm ambiguous */ tmp = TMALLOC(char, 58 + strlen(parm)); (void)sprintf(tmp, "Error on card %d : ambiguous parameter (%s) - ignored", cardNum, parm); err = INPerrCat(err, tmp); } else { value = INPgetValue( ckt, &line, ((info->cardParms)[idx]).dataType, tab ); if (invert) { /* invert if it's a boolean entry */ if (((((info->cardParms)[idx]).dataType)&IF_VARTYPES) == IF_FLAG) { value->iValue = 0; } else { tmp = TMALLOC(char, 63 + strlen(parm)); (void)sprintf(tmp, "Error on card %d : non-boolean parameter (%s) - \'^\' ignored", cardNum, parm); err = INPerrCat(err, tmp); } } error = info->setCardParm ( ((info->cardParms)[idx]).id, value, tmpCard ); if (error) return(error); } FREE(parm); } } lastType = cardType; break; } if (txtCard) txtCard = txtCard->nextcard; } *errMessage = err; return( 0 ); } /* * Locate the best match to a card name in an IFcardInfo table */ static int INPfindCard( char *name, IFcardInfo *table[], int numCards ) { int test; int match, bestMatch; int best; int length; length = (int) strlen(name); /* compare all the names in the card table to this name */ best = E_MISSING; bestMatch = 0; for ( test = 0; test < numCards; test++ ) { match = cimatch( name, table[test]->name ); if ((match == bestMatch ) && (match > 0)){ best = E_AMBIGUOUS; } else if ((match > bestMatch) && (match == length)) { best = test; bestMatch = match; } } return(best); } /* * Locate the best match to a parameter name in an IFparm table */ static int INPfindParm( char *name, IFparm *table, int numParms ) { int test, best; int match, bestMatch; int id, bestId; int length; length = (int) strlen(name); /* compare all the names in the parameter table to this name */ best = E_MISSING; bestId = -1; bestMatch = 0; for ( test = 0; test < numParms; test++ ) { match = cimatch( name, table[test].keyword ); if ( (match == length) && (match == (int) strlen(table[test].keyword)) ) { /* exact match */ best = test; /* all done */ break; } id = table[test].id; if ((match == bestMatch) && (match > 0) && (id != bestId)) { best = E_AMBIGUOUS; } else if ((match > bestMatch) && (match == length)) { bestMatch = match; bestId = id; best = test; } } return(best); } #endif /* CIDER */