|
|
|
@ -1,11 +1,11 @@ |
|
|
|
/* Routines to evaluate the .measure cards. |
|
|
|
Entry point is function do_measure(), called by fcn dosim() |
|
|
|
Entry point is function do_measure(), called by fcn dosim() |
|
|
|
from runcoms.c:335, after simulation is finished. |
|
|
|
|
|
|
|
In addition it contains the fcn com_meas(), which provide the |
|
|
|
|
|
|
|
In addition it contains the fcn com_meas(), which provide the |
|
|
|
interactive 'meas' command. |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
#include "ngspice/ngspice.h" |
|
|
|
#include "ngspice/cpdefs.h" |
|
|
|
#include "ngspice/ftedefs.h" |
|
|
|
@ -38,404 +38,428 @@ extern bool rflag; |
|
|
|
*/ |
|
|
|
|
|
|
|
void |
|
|
|
com_meas(wordlist *wl) { |
|
|
|
/* wl: in, input line of meas command */ |
|
|
|
char *line_in, *outvar, newvec[1000]; |
|
|
|
wordlist * wl_count, *wl_let; |
|
|
|
|
|
|
|
char *vec_found, *token, *equal_ptr, newval[256]; |
|
|
|
wordlist *wl_index; |
|
|
|
struct dvec *d; |
|
|
|
int err=0; |
|
|
|
|
|
|
|
int fail; |
|
|
|
double result = 0; |
|
|
|
|
|
|
|
if (!wl) { |
|
|
|
com_display(NULL); |
|
|
|
return; |
|
|
|
} |
|
|
|
wl_count = wl; |
|
|
|
|
|
|
|
/* check each wl entry, if it contain '=' and if the following token is |
|
|
|
a single valued vector. If yes, replace this vector by its value. |
|
|
|
Vectors may stem from other meas commands, or be generated elsewhere |
|
|
|
within the .control .endc script. All other right hand side vectors are |
|
|
|
treated in com_measure2.c. */ |
|
|
|
wl_index = wl; |
|
|
|
|
|
|
|
while ( wl_index) { |
|
|
|
token = wl_index->wl_word; |
|
|
|
/* find the vector vec_found, next token after each '=' sign. |
|
|
|
May be in the next wl_word */ |
|
|
|
if ( *(token + strlen(token) - 1) == '=' ) { |
|
|
|
wl_index = wl_index->wl_next; |
|
|
|
vec_found = wl_index->wl_word; |
|
|
|
/* token may be already a value, maybe 'LAST', which we have to keep, or maybe a vector */ |
|
|
|
if (!cieq(vec_found, "LAST")) { |
|
|
|
INPevaluate( &vec_found, &err, 1 ); |
|
|
|
/* if not a valid number */ |
|
|
|
if (err) { |
|
|
|
/* check if vec_found is a valid vector */ |
|
|
|
d = vec_get(vec_found); |
|
|
|
/* Only if we have a single valued vector, replacing |
|
|
|
of the rigt hand side does make sense */ |
|
|
|
if (d && (d->v_length == 1) && (d->v_numdims == 1)) { |
|
|
|
/* get its value */ |
|
|
|
sprintf(newval, "%e", d->v_realdata[0]); |
|
|
|
tfree(vec_found); |
|
|
|
wl_index->wl_word = copy(newval); |
|
|
|
} |
|
|
|
com_meas(wordlist *wl) |
|
|
|
{ |
|
|
|
/* wl: in, input line of meas command */ |
|
|
|
char *line_in, *outvar, newvec[1000]; |
|
|
|
wordlist * wl_count, *wl_let; |
|
|
|
|
|
|
|
char *vec_found, *token, *equal_ptr, newval[256]; |
|
|
|
wordlist *wl_index; |
|
|
|
struct dvec *d; |
|
|
|
int err=0; |
|
|
|
|
|
|
|
int fail; |
|
|
|
double result = 0; |
|
|
|
|
|
|
|
if (!wl) { |
|
|
|
com_display(NULL); |
|
|
|
return; |
|
|
|
} |
|
|
|
wl_count = wl; |
|
|
|
|
|
|
|
/* check each wl entry, if it contain '=' and if the following token is |
|
|
|
a single valued vector. If yes, replace this vector by its value. |
|
|
|
Vectors may stem from other meas commands, or be generated elsewhere |
|
|
|
within the .control .endc script. All other right hand side vectors are |
|
|
|
treated in com_measure2.c. */ |
|
|
|
wl_index = wl; |
|
|
|
|
|
|
|
while ( wl_index) { |
|
|
|
token = wl_index->wl_word; |
|
|
|
/* find the vector vec_found, next token after each '=' sign. |
|
|
|
May be in the next wl_word */ |
|
|
|
if ( *(token + strlen(token) - 1) == '=' ) { |
|
|
|
wl_index = wl_index->wl_next; |
|
|
|
vec_found = wl_index->wl_word; |
|
|
|
/* token may be already a value, maybe 'LAST', which we have to keep, or maybe a vector */ |
|
|
|
if (!cieq(vec_found, "LAST")) { |
|
|
|
INPevaluate( &vec_found, &err, 1 ); |
|
|
|
/* if not a valid number */ |
|
|
|
if (err) { |
|
|
|
/* check if vec_found is a valid vector */ |
|
|
|
d = vec_get(vec_found); |
|
|
|
/* Only if we have a single valued vector, replacing |
|
|
|
of the rigt hand side does make sense */ |
|
|
|
if (d && (d->v_length == 1) && (d->v_numdims == 1)) { |
|
|
|
/* get its value */ |
|
|
|
sprintf(newval, "%e", d->v_realdata[0]); |
|
|
|
tfree(vec_found); |
|
|
|
wl_index->wl_word = copy(newval); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
/* may be inside the same wl_word */ |
|
|
|
else if ( (equal_ptr = strstr( token, "=" )) != NULL ) { |
|
|
|
vec_found = equal_ptr + 1; |
|
|
|
if (!cieq(vec_found, "LAST")) { |
|
|
|
INPevaluate( &vec_found, &err, 1 ); |
|
|
|
if (err) { |
|
|
|
d = vec_get(vec_found); |
|
|
|
/* Only if we have a single valued vector, replacing |
|
|
|
of the rigt hand side does make sense */ |
|
|
|
if (d && (d->v_length == 1) && (d->v_numdims == 1)) { |
|
|
|
*equal_ptr = '\0'; |
|
|
|
sprintf(newval, "%s=%e", token, d->v_realdata[0]); |
|
|
|
} |
|
|
|
/* may be inside the same wl_word */ |
|
|
|
else if ( (equal_ptr = strstr( token, "=" )) != NULL ) { |
|
|
|
vec_found = equal_ptr + 1; |
|
|
|
if (!cieq(vec_found, "LAST")) { |
|
|
|
INPevaluate( &vec_found, &err, 1 ); |
|
|
|
if (err) { |
|
|
|
d = vec_get(vec_found); |
|
|
|
/* Only if we have a single valued vector, replacing |
|
|
|
of the rigt hand side does make sense */ |
|
|
|
if (d && (d->v_length == 1) && (d->v_numdims == 1)) { |
|
|
|
*equal_ptr = '\0'; |
|
|
|
sprintf(newval, "%s=%e", token, d->v_realdata[0]); |
|
|
|
// memory leak with first part of vec_found ? |
|
|
|
tfree(token); |
|
|
|
wl_index->wl_word = copy(newval); |
|
|
|
} |
|
|
|
tfree(token); |
|
|
|
wl_index->wl_word = copy(newval); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
;// nothing |
|
|
|
} |
|
|
|
wl_index = wl_index->wl_next; |
|
|
|
} |
|
|
|
|
|
|
|
line_in = wl_flatten(wl); |
|
|
|
|
|
|
|
/* get output var name */ |
|
|
|
wl_count = wl_count->wl_next; |
|
|
|
outvar = wl_count->wl_word; |
|
|
|
|
|
|
|
fail = get_measure2(wl, &result, NULL, FALSE) ; |
|
|
|
|
|
|
|
if (fail) { |
|
|
|
fprintf(stdout, " meas %s failed!\n\n", line_in); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
sprintf(newvec, "%s = %e", outvar, result); |
|
|
|
wl_let = wl_cons(copy(newvec), NULL); |
|
|
|
com_let(wl_let); |
|
|
|
wl_free(wl_let); |
|
|
|
} else { |
|
|
|
;// nothing |
|
|
|
} |
|
|
|
wl_index = wl_index->wl_next; |
|
|
|
} |
|
|
|
|
|
|
|
line_in = wl_flatten(wl); |
|
|
|
|
|
|
|
/* get output var name */ |
|
|
|
wl_count = wl_count->wl_next; |
|
|
|
outvar = wl_count->wl_word; |
|
|
|
|
|
|
|
fail = get_measure2(wl, &result, NULL, FALSE) ; |
|
|
|
|
|
|
|
if (fail) { |
|
|
|
fprintf(stdout, " meas %s failed!\n\n", line_in); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
sprintf(newvec, "%s = %e", outvar, result); |
|
|
|
wl_let = wl_cons(copy(newvec), NULL); |
|
|
|
com_let(wl_let); |
|
|
|
wl_free(wl_let); |
|
|
|
// fprintf(stdout, "in: %s\n", line_in); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static bool |
|
|
|
chkAnalysisType( char *an_type ) { |
|
|
|
/* only support tran, dc, ac, sp analysis type for now */ |
|
|
|
if ( strcmp( an_type, "tran" ) != 0 && strcmp( an_type, "ac" ) != 0 && |
|
|
|
strcmp( an_type, "dc" ) != 0 && strcmp( an_type, "sp" ) != 0) |
|
|
|
return FALSE; |
|
|
|
chkAnalysisType( char *an_type ) |
|
|
|
{ |
|
|
|
/* only support tran, dc, ac, sp analysis type for now */ |
|
|
|
if ( strcmp( an_type, "tran" ) != 0 && strcmp( an_type, "ac" ) != 0 && |
|
|
|
strcmp( an_type, "dc" ) != 0 && strcmp( an_type, "sp" ) != 0) |
|
|
|
return FALSE; |
|
|
|
// else if (ft_batchmode == TRUE) return FALSE; |
|
|
|
else return TRUE; |
|
|
|
else return TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Gets pointer to double value after 'xxx=' and advances pointer of *line. |
|
|
|
On error returns FALSE. */ |
|
|
|
/* Gets pointer to double value after 'xxx=' and advances pointer of *line. |
|
|
|
On error returns FALSE. */ |
|
|
|
static bool |
|
|
|
get_double_value( |
|
|
|
char **line, /*in|out: pointer to line to be parsed */ |
|
|
|
char *name, /*in: xxx e.g. 'val' from 'val=0.5' */ |
|
|
|
double *value /*out: return value (e.g. 0.5) from 'val=0.5'*/ |
|
|
|
) { |
|
|
|
char *token = gettok(line); |
|
|
|
bool return_val = TRUE; |
|
|
|
char *equal_ptr, *junk; |
|
|
|
int err=0; |
|
|
|
|
|
|
|
if ( name && ( strncmp( token, name, strlen(name) ) != 0 ) ) { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; expecting next field to be '%s'.\n", name ); |
|
|
|
return_val = FALSE; |
|
|
|
} else { |
|
|
|
/* see if '=' is last char of current token -- implies we need to read value in next token */ |
|
|
|
if ( *(token + strlen(token) - 1) == '=' ) { |
|
|
|
txfree(token); |
|
|
|
junk = token = gettok(line); |
|
|
|
|
|
|
|
*value = INPevaluate( &junk, &err, 1 ); |
|
|
|
get_double_value( |
|
|
|
char **line, /*in|out: pointer to line to be parsed */ |
|
|
|
char *name, /*in: xxx e.g. 'val' from 'val=0.5' */ |
|
|
|
double *value /*out: return value (e.g. 0.5) from 'val=0.5'*/ |
|
|
|
) |
|
|
|
{ |
|
|
|
char *token = gettok(line); |
|
|
|
bool return_val = TRUE; |
|
|
|
char *equal_ptr, *junk; |
|
|
|
int err=0; |
|
|
|
|
|
|
|
if ( name && ( strncmp( token, name, strlen(name) ) != 0 ) ) { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; expecting next field to be '%s'.\n", name ); |
|
|
|
return_val = FALSE; |
|
|
|
} else { |
|
|
|
if ( (equal_ptr = strstr( token, "=" )) != NULL ) { |
|
|
|
equal_ptr += 1; |
|
|
|
*value = INPevaluate( &equal_ptr, &err, 1 ); |
|
|
|
} else { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; missing '='!\n" ); |
|
|
|
return_val = FALSE; |
|
|
|
} |
|
|
|
/* see if '=' is last char of current token -- implies we need to read value in next token */ |
|
|
|
if ( *(token + strlen(token) - 1) == '=' ) { |
|
|
|
txfree(token); |
|
|
|
junk = token = gettok(line); |
|
|
|
|
|
|
|
*value = INPevaluate( &junk, &err, 1 ); |
|
|
|
} else { |
|
|
|
if ( (equal_ptr = strstr( token, "=" )) != NULL ) { |
|
|
|
equal_ptr += 1; |
|
|
|
*value = INPevaluate( &equal_ptr, &err, 1 ); |
|
|
|
} else { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: syntax error for measure statement; missing '='!\n" ); |
|
|
|
return_val = FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
if ( err ) { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: Bad value.\n" ); |
|
|
|
return_val = FALSE; |
|
|
|
} |
|
|
|
} |
|
|
|
if ( err ) { if ( just_chk_meas != TRUE ) fprintf( cp_err, "Error: Bad value.\n" ); return_val = FALSE; } |
|
|
|
} |
|
|
|
txfree(token); |
|
|
|
txfree(token); |
|
|
|
|
|
|
|
return return_val; |
|
|
|
return return_val; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* Entry point for .meas evaluation. |
|
|
|
/* Entry point for .meas evaluation. |
|
|
|
Called in fcn dosim() from runcoms.c:335, after simulation is finished |
|
|
|
with chk_only set to FALSE. |
|
|
|
Called from fcn check_autostop() |
|
|
|
with chk_only set to TRUE (no printouts, no params set). */ |
|
|
|
void |
|
|
|
do_measure( |
|
|
|
char *what, /*in: analysis type*/ |
|
|
|
bool chk_only /*in: TRUE if checking for "autostop", FALSE otherwise*/ |
|
|
|
/*global variable measures_passed |
|
|
|
out: set to FALSE if .meas syntax is violated (used with autostop)*/ |
|
|
|
) { |
|
|
|
struct line *meas_card, *meas_results = NULL, *end = NULL, *newcard; |
|
|
|
char *line, *an_name, *an_type, *resname, *meastype, *str_ptr, out_line[1000]; |
|
|
|
int idx = 0, ok = 0; |
|
|
|
int fail; |
|
|
|
double result = 0; |
|
|
|
bool first_time = TRUE; |
|
|
|
wordlist *measure_word_list ; |
|
|
|
int precision = measure_get_precision() ; |
|
|
|
|
|
|
|
just_chk_meas = chk_only; |
|
|
|
|
|
|
|
an_name = strdup( what ); /* analysis type, e.g. "tran" */ |
|
|
|
strtolower( an_name ); |
|
|
|
measure_word_list = NULL ; |
|
|
|
|
|
|
|
/* don't allow .meas if batchmode is set by -b and -r rawfile given */ |
|
|
|
if (ft_batchmode && rflag) { |
|
|
|
fprintf(cp_err, "\nNo .measure possible in batch mode (-b) with -r rawfile set!\n"); |
|
|
|
fprintf(cp_err, "Remove rawfile and use .print or .plot or\n"); |
|
|
|
fprintf(cp_err, "select interactive mode (optionally with .control section) instead.\n\n"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* Evaluating the linked list of .meas cards, assembled from the input deck |
|
|
|
by fcn inp_spsource() in inp.c:575. |
|
|
|
A typical .meas card will contain: |
|
|
|
parameter value |
|
|
|
nameof card .meas(ure) |
|
|
|
analysis type tran only tran available currently |
|
|
|
result name myout defined by user |
|
|
|
measurement type trig|delay|param|expr|avg|mean|max|min|rms|integ(ral)|when |
|
|
|
|
|
|
|
The measurement type determines how to continue the .meas card. |
|
|
|
param|expr are skipped in first pass through .meas cards and are treated in second pass, |
|
|
|
all others are treated in fcn get_measure2() (com_measure2.c). |
|
|
|
*/ |
|
|
|
|
|
|
|
/* first pass through .meas cards: evaluate everything except param|expr */ |
|
|
|
for ( meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->li_next ) { |
|
|
|
line = meas_card->li_line; |
|
|
|
|
|
|
|
txfree(gettok(&line)); /* discard .meas */ |
|
|
|
|
|
|
|
an_type = gettok(&line); resname = gettok(&line); meastype = gettok(&line); |
|
|
|
|
|
|
|
if ( chkAnalysisType( an_type ) != TRUE ) { |
|
|
|
if ( just_chk_meas != TRUE ) { |
|
|
|
fprintf( cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->li_linenum ); |
|
|
|
fprintf( cp_err, " %s\n", meas_card->li_line ); |
|
|
|
} |
|
|
|
|
|
|
|
txfree(an_type); txfree(resname); txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
/* print header before evaluating first .meas line */ |
|
|
|
else if ( first_time ) { |
|
|
|
first_time = FALSE; |
|
|
|
|
|
|
|
if ( just_chk_meas != TRUE && strcmp( an_type, "tran" ) == 0 ) { |
|
|
|
fprintf( stdout, " Transient Analysis\n\n" ); |
|
|
|
// plot_cur = setcplot("tran"); |
|
|
|
} |
|
|
|
do_measure( |
|
|
|
char *what, /*in: analysis type*/ |
|
|
|
bool chk_only /*in: TRUE if checking for "autostop", FALSE otherwise*/ |
|
|
|
/*global variable measures_passed |
|
|
|
out: set to FALSE if .meas syntax is violated (used with autostop)*/ |
|
|
|
) |
|
|
|
{ |
|
|
|
struct line *meas_card, *meas_results = NULL, *end = NULL, *newcard; |
|
|
|
char *line, *an_name, *an_type, *resname, *meastype, *str_ptr, out_line[1000]; |
|
|
|
int idx = 0, ok = 0; |
|
|
|
int fail; |
|
|
|
double result = 0; |
|
|
|
bool first_time = TRUE; |
|
|
|
wordlist *measure_word_list ; |
|
|
|
int precision = measure_get_precision() ; |
|
|
|
|
|
|
|
just_chk_meas = chk_only; |
|
|
|
|
|
|
|
an_name = strdup( what ); /* analysis type, e.g. "tran" */ |
|
|
|
strtolower( an_name ); |
|
|
|
measure_word_list = NULL ; |
|
|
|
|
|
|
|
/* don't allow .meas if batchmode is set by -b and -r rawfile given */ |
|
|
|
if (ft_batchmode && rflag) { |
|
|
|
fprintf(cp_err, "\nNo .measure possible in batch mode (-b) with -r rawfile set!\n"); |
|
|
|
fprintf(cp_err, "Remove rawfile and use .print or .plot or\n"); |
|
|
|
fprintf(cp_err, "select interactive mode (optionally with .control section) instead.\n\n"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* skip param|expr measurement types for now -- will be done after other measurements */ |
|
|
|
if ( strncmp( meastype, "param", 5 ) == 0 || strncmp( meastype, "expr", 4 ) == 0 ) continue; |
|
|
|
/* Evaluating the linked list of .meas cards, assembled from the input deck |
|
|
|
by fcn inp_spsource() in inp.c:575. |
|
|
|
A typical .meas card will contain: |
|
|
|
parameter value |
|
|
|
nameof card .meas(ure) |
|
|
|
analysis type tran only tran available currently |
|
|
|
result name myout defined by user |
|
|
|
measurement type trig|delay|param|expr|avg|mean|max|min|rms|integ(ral)|when |
|
|
|
|
|
|
|
The measurement type determines how to continue the .meas card. |
|
|
|
param|expr are skipped in first pass through .meas cards and are treated in second pass, |
|
|
|
all others are treated in fcn get_measure2() (com_measure2.c). |
|
|
|
*/ |
|
|
|
|
|
|
|
/* first pass through .meas cards: evaluate everything except param|expr */ |
|
|
|
for ( meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->li_next ) { |
|
|
|
line = meas_card->li_line; |
|
|
|
|
|
|
|
txfree(gettok(&line)); /* discard .meas */ |
|
|
|
|
|
|
|
an_type = gettok(&line); |
|
|
|
resname = gettok(&line); |
|
|
|
meastype = gettok(&line); |
|
|
|
|
|
|
|
if ( chkAnalysisType( an_type ) != TRUE ) { |
|
|
|
if ( just_chk_meas != TRUE ) { |
|
|
|
fprintf( cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->li_linenum ); |
|
|
|
fprintf( cp_err, " %s\n", meas_card->li_line ); |
|
|
|
} |
|
|
|
|
|
|
|
/* skip .meas line, if analysis type from line and name of analysis performed differ */ |
|
|
|
if ( strcmp( an_name, an_type ) != 0 ) { |
|
|
|
txfree(an_type); txfree(resname); txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
txfree(an_type); |
|
|
|
txfree(resname); |
|
|
|
txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
/* print header before evaluating first .meas line */ |
|
|
|
else if ( first_time ) { |
|
|
|
first_time = FALSE; |
|
|
|
|
|
|
|
/* New way of processing measure statements using common code |
|
|
|
in fcn get_measure2() (com_measure2.c)*/ |
|
|
|
out_line[0] = '\0' ; |
|
|
|
measure_word_list = measure_parse_line( meas_card->li_line) ; |
|
|
|
if( measure_word_list ){ |
|
|
|
fail = get_measure2(measure_word_list,&result,out_line,chk_only) ; |
|
|
|
if( fail ){ |
|
|
|
measure_valid[idx++] = FALSE; |
|
|
|
measures_passed = FALSE; |
|
|
|
fprintf(stdout, " %s failed!\n\n", meas_card->li_line); |
|
|
|
} else { |
|
|
|
if(!(just_chk_meas)){ |
|
|
|
nupa_add_param( resname, result ); |
|
|
|
} |
|
|
|
measure_valid[idx++] = TRUE; |
|
|
|
} |
|
|
|
wl_free( measure_word_list ) ; |
|
|
|
} else { |
|
|
|
measure_valid[idx++] = FALSE; |
|
|
|
measures_passed = FALSE; |
|
|
|
} |
|
|
|
if ( just_chk_meas != TRUE && strcmp( an_type, "tran" ) == 0 ) { |
|
|
|
fprintf( stdout, " Transient Analysis\n\n" ); |
|
|
|
// plot_cur = setcplot("tran"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
newcard = alloc(struct line); |
|
|
|
newcard->li_line = strdup(out_line); |
|
|
|
newcard->li_next = NULL; |
|
|
|
/* skip param|expr measurement types for now -- will be done after other measurements */ |
|
|
|
if ( strncmp( meastype, "param", 5 ) == 0 || strncmp( meastype, "expr", 4 ) == 0 ) continue; |
|
|
|
|
|
|
|
if ( meas_results == NULL ) meas_results = end = newcard; |
|
|
|
else { |
|
|
|
end->li_next = newcard; |
|
|
|
end = newcard; |
|
|
|
} |
|
|
|
/* skip .meas line, if analysis type from line and name of analysis performed differ */ |
|
|
|
if ( strcmp( an_name, an_type ) != 0 ) { |
|
|
|
txfree(an_type); |
|
|
|
txfree(resname); |
|
|
|
txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
txfree(an_type); txfree(resname); txfree(meastype); |
|
|
|
/* New way of processing measure statements using common code |
|
|
|
in fcn get_measure2() (com_measure2.c)*/ |
|
|
|
out_line[0] = '\0' ; |
|
|
|
measure_word_list = measure_parse_line( meas_card->li_line) ; |
|
|
|
if( measure_word_list ) { |
|
|
|
fail = get_measure2(measure_word_list,&result,out_line,chk_only) ; |
|
|
|
if( fail ) { |
|
|
|
measure_valid[idx++] = FALSE; |
|
|
|
measures_passed = FALSE; |
|
|
|
fprintf(stdout, " %s failed!\n\n", meas_card->li_line); |
|
|
|
} else { |
|
|
|
if(!(just_chk_meas)) { |
|
|
|
nupa_add_param( resname, result ); |
|
|
|
} |
|
|
|
measure_valid[idx++] = TRUE; |
|
|
|
} |
|
|
|
wl_free( measure_word_list ) ; |
|
|
|
} else { |
|
|
|
measure_valid[idx++] = FALSE; |
|
|
|
measures_passed = FALSE; |
|
|
|
} |
|
|
|
|
|
|
|
/* see if number of measurements exceeds fixed array size of 20,000 */ |
|
|
|
if ( idx >= 20000 ) { |
|
|
|
fprintf( stderr, "ERROR: number of measurements exceeds 20,000!\nAborting...\n" ); |
|
|
|
controlled_exit(EXIT_FAILURE); |
|
|
|
} |
|
|
|
newcard = alloc(struct line); |
|
|
|
newcard->li_line = strdup(out_line); |
|
|
|
newcard->li_next = NULL; |
|
|
|
|
|
|
|
} /* end of for loop (first pass through .meas lines) */ |
|
|
|
if ( meas_results == NULL ) meas_results = end = newcard; |
|
|
|
else { |
|
|
|
end->li_next = newcard; |
|
|
|
end = newcard; |
|
|
|
} |
|
|
|
|
|
|
|
txfree(an_type); |
|
|
|
txfree(resname); |
|
|
|
txfree(meastype); |
|
|
|
|
|
|
|
/* second pass through .meas cards: now do param|expr .meas statements */ |
|
|
|
newcard = meas_results; |
|
|
|
for ( meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->li_next ) { |
|
|
|
line = meas_card->li_line; |
|
|
|
/* see if number of measurements exceeds fixed array size of 20,000 */ |
|
|
|
if ( idx >= 20000 ) { |
|
|
|
fprintf( stderr, "ERROR: number of measurements exceeds 20,000!\nAborting...\n" ); |
|
|
|
controlled_exit(EXIT_FAILURE); |
|
|
|
} |
|
|
|
|
|
|
|
txfree(gettok(&line)); /* discard .meas */ |
|
|
|
} /* end of for loop (first pass through .meas lines) */ |
|
|
|
|
|
|
|
an_type = gettok(&line); resname = gettok(&line); meastype = gettok(&line); |
|
|
|
|
|
|
|
if ( chkAnalysisType( an_type ) != TRUE ) { |
|
|
|
if ( just_chk_meas != TRUE ) { |
|
|
|
fprintf( cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->li_linenum ); |
|
|
|
fprintf( cp_err, " %s\n", meas_card->li_line ); |
|
|
|
} |
|
|
|
/* second pass through .meas cards: now do param|expr .meas statements */ |
|
|
|
newcard = meas_results; |
|
|
|
for ( meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->li_next ) { |
|
|
|
line = meas_card->li_line; |
|
|
|
|
|
|
|
txfree(an_type); txfree(resname); txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ( strcmp( an_name, an_type ) != 0 ) { |
|
|
|
txfree(an_type); txfree(resname); txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
txfree(gettok(&line)); /* discard .meas */ |
|
|
|
|
|
|
|
if ( strncmp( meastype, "param", 5 ) != 0 && strncmp( meastype, "expr", 4 ) != 0 ) { |
|
|
|
an_type = gettok(&line); |
|
|
|
resname = gettok(&line); |
|
|
|
meastype = gettok(&line); |
|
|
|
|
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, "%s", newcard->li_line ); |
|
|
|
end = newcard; |
|
|
|
newcard = newcard->li_next; |
|
|
|
if ( chkAnalysisType( an_type ) != TRUE ) { |
|
|
|
if ( just_chk_meas != TRUE ) { |
|
|
|
fprintf( cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->li_linenum ); |
|
|
|
fprintf( cp_err, " %s\n", meas_card->li_line ); |
|
|
|
} |
|
|
|
|
|
|
|
txfree( end->li_line ); |
|
|
|
txfree( end ); |
|
|
|
txfree(an_type); |
|
|
|
txfree(resname); |
|
|
|
txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ( strcmp( an_name, an_type ) != 0 ) { |
|
|
|
txfree(an_type); |
|
|
|
txfree(resname); |
|
|
|
txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
txfree(an_type); txfree(resname); txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
if ( strncmp( meastype, "param", 5 ) != 0 && strncmp( meastype, "expr", 4 ) != 0 ) { |
|
|
|
|
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, "%-20s=", resname ); |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, "%s", newcard->li_line ); |
|
|
|
end = newcard; |
|
|
|
newcard = newcard->li_next; |
|
|
|
|
|
|
|
if ( just_chk_meas != TRUE ) { |
|
|
|
ok = nupa_eval( meas_card->li_line, meas_card->li_linenum, meas_card->li_linenum_orig ); |
|
|
|
txfree( end->li_line ); |
|
|
|
txfree( end ); |
|
|
|
|
|
|
|
if ( ok ) { |
|
|
|
str_ptr = strstr( meas_card->li_line, meastype ); |
|
|
|
if ( !get_double_value( &str_ptr, meastype, &result ) ) { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, " failed\n" ); |
|
|
|
txfree(an_type); |
|
|
|
txfree(resname); |
|
|
|
txfree(meastype); |
|
|
|
continue; |
|
|
|
} |
|
|
|
else { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, " %.*e\n", precision, result ); |
|
|
|
nupa_add_param( resname, result ); |
|
|
|
|
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, "%-20s=", resname ); |
|
|
|
|
|
|
|
if ( just_chk_meas != TRUE ) { |
|
|
|
ok = nupa_eval( meas_card->li_line, meas_card->li_linenum, meas_card->li_linenum_orig ); |
|
|
|
|
|
|
|
if ( ok ) { |
|
|
|
str_ptr = strstr( meas_card->li_line, meastype ); |
|
|
|
if ( !get_double_value( &str_ptr, meastype, &result ) ) { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, " failed\n" ); |
|
|
|
} else { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, " %.*e\n", precision, result ); |
|
|
|
nupa_add_param( resname, result ); |
|
|
|
} |
|
|
|
} else { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, " failed\n" ); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, " failed\n" ); |
|
|
|
} |
|
|
|
txfree(an_type); |
|
|
|
txfree(resname); |
|
|
|
txfree(meastype); |
|
|
|
} |
|
|
|
txfree(an_type); txfree(resname); txfree(meastype); |
|
|
|
} |
|
|
|
|
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, "\n" ); |
|
|
|
if ( just_chk_meas != TRUE ) fprintf( stdout, "\n" ); |
|
|
|
|
|
|
|
txfree(an_name); |
|
|
|
txfree(an_name); |
|
|
|
|
|
|
|
fflush( stdout ); |
|
|
|
fflush( stdout ); |
|
|
|
|
|
|
|
//nupa_list_params(); |
|
|
|
//nupa_list_params(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/* called from dctran.c:470, if timepoint is accepted. |
|
|
|
Returns TRUE if measurement (just a check, no output) has been successful. |
|
|
|
Returns TRUE if measurement (just a check, no output) has been successful. |
|
|
|
If TRUE is returned, transient simulation is stopped. |
|
|
|
Returns TRUE if "autostop" has been set as an option and if measures_passed not |
|
|
|
set to FALSE during calling do_measure. 'what' is set to "tran".*/ |
|
|
|
|
|
|
|
bool |
|
|
|
check_autostop( char* what ) { |
|
|
|
bool flag = FALSE; |
|
|
|
check_autostop( char* what ) |
|
|
|
{ |
|
|
|
bool flag = FALSE; |
|
|
|
|
|
|
|
measures_passed = TRUE; |
|
|
|
if ( cp_getvar( "autostop", CP_BOOL, NULL) ) { |
|
|
|
do_measure( what, TRUE ); |
|
|
|
measures_passed = TRUE; |
|
|
|
if ( cp_getvar( "autostop", CP_BOOL, NULL) ) { |
|
|
|
do_measure( what, TRUE ); |
|
|
|
|
|
|
|
if ( measures_passed == TRUE ) flag = TRUE; |
|
|
|
} |
|
|
|
if ( measures_passed == TRUE ) flag = TRUE; |
|
|
|
} |
|
|
|
|
|
|
|
return flag; |
|
|
|
return flag; |
|
|
|
} |
|
|
|
|
|
|
|
/* parses the .meas line into a wordlist (without leading .meas) */ |
|
|
|
static wordlist *measure_parse_line( char *line ) |
|
|
|
{ |
|
|
|
size_t len ; /* length of string */ |
|
|
|
wordlist *wl ; /* build a word list - head of list */ |
|
|
|
wordlist *new_item ; /* single item of a list */ |
|
|
|
char *item ; /* parsed item */ |
|
|
|
char *long_str ; /* concatenated string */ |
|
|
|
char *extra_item ; /* extra item */ |
|
|
|
|
|
|
|
wl = NULL ; |
|
|
|
(void) gettok(&line) ; |
|
|
|
do { |
|
|
|
item = gettok(&line) ; |
|
|
|
if(!(item)){ |
|
|
|
break ; |
|
|
|
} |
|
|
|
len = strlen(item) ; |
|
|
|
if( item[len-1] == '=' ){ |
|
|
|
/* We can't end on an equal append the next piece */ |
|
|
|
extra_item = gettok(&line) ; |
|
|
|
if(!(extra_item)){ |
|
|
|
break ; |
|
|
|
} |
|
|
|
len += strlen( extra_item ) + 2 ; |
|
|
|
long_str = TMALLOC(char, len) ; |
|
|
|
sprintf( long_str, "%s%s", item, extra_item ) ; |
|
|
|
txfree( item ) ; |
|
|
|
txfree( extra_item ) ; |
|
|
|
item = long_str ; |
|
|
|
} |
|
|
|
new_item = wl_cons(item, NULL); |
|
|
|
wl = wl_append(wl, new_item) ; |
|
|
|
} while( line && *line ) ; |
|
|
|
|
|
|
|
return(wl) ; |
|
|
|
|
|
|
|
{ |
|
|
|
size_t len ; /* length of string */ |
|
|
|
wordlist *wl ; /* build a word list - head of list */ |
|
|
|
wordlist *new_item ; /* single item of a list */ |
|
|
|
char *item ; /* parsed item */ |
|
|
|
char *long_str ; /* concatenated string */ |
|
|
|
char *extra_item ; /* extra item */ |
|
|
|
|
|
|
|
wl = NULL ; |
|
|
|
(void) gettok(&line) ; |
|
|
|
do { |
|
|
|
item = gettok(&line) ; |
|
|
|
if(!(item)) { |
|
|
|
break ; |
|
|
|
} |
|
|
|
len = strlen(item) ; |
|
|
|
if( item[len-1] == '=' ) { |
|
|
|
/* We can't end on an equal append the next piece */ |
|
|
|
extra_item = gettok(&line) ; |
|
|
|
if(!(extra_item)) { |
|
|
|
break ; |
|
|
|
} |
|
|
|
len += strlen( extra_item ) + 2 ; |
|
|
|
long_str = TMALLOC(char, len) ; |
|
|
|
sprintf( long_str, "%s%s", item, extra_item ) ; |
|
|
|
txfree( item ) ; |
|
|
|
txfree( extra_item ) ; |
|
|
|
item = long_str ; |
|
|
|
} |
|
|
|
new_item = wl_cons(item, NULL); |
|
|
|
wl = wl_append(wl, new_item) ; |
|
|
|
} while( line && *line ) ; |
|
|
|
|
|
|
|
return(wl) ; |
|
|
|
|
|
|
|
} /* end measure_parse_line() */ |