541 lines
18 KiB
C
541 lines
18 KiB
C
/**********
|
|
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 <errno.h>
|
|
|
|
#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");
|
|
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) || (is_equal( value, max ) == TRUE) ||
|
|
(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 <level=val>
|
|
* 2. Other cards look like: +<whitespace>? 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 */
|