/* Routines to evaluate the .measure cards. 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 interactive 'meas' command. $Id$ */ #include "ngspice.h" #include "cpdefs.h" #include "ftedefs.h" #include "dvec.h" #include "rawfile.h" #include "variable.h" #include "numparam/numpaif.h" #include "missing_math.h" #include "com_measure2.h" #include "error.h" /* controlled_exit() */ #define EOS '\0' static wordlist *measure_parse_line( char *line ) ; static bool measure_valid[20000];/* TRUE: if measurement no. [xxx] has been done successfully (not used anywhere)*/ static bool just_chk_meas; /* TRUE: only check if measurement can be done successfully, no output generated (if option autostop is set)*/ static bool measures_passed; /* TRUE: stop simulation (if option autostop is set)*/ extern bool ft_batchmode; extern bool rflag; /* measure in interactive mode: meas command inside .control ... .endc loop or manually entered. meas has to be followed by the standard tokens (see measure_extract_variables()). The result is put into a vector with name "result" */ void com_meas(wordlist *wl) { /* wl: in, input line of meas command */ char *line_in, *outvar, newvec[1000], *vec_found, *token, *equal_ptr, newval[256]; wordlist * wl_count, *wl_let, *wl_index; int fail, err=0; double result = 0; struct dvec *d; if (!wl) { com_display(NULL); return; } wl_count = wl; /* check each wl entry, if it contain '=' and if the following token is a vector. If yes, replace this vector by its value */ 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); if (d) { /* 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, "=" )) ) { vec_found = equal_ptr + 1; if (!cieq(vec_found, "LAST")) { INPevaluate( &vec_found, &err, 1 ); if (err) { d = vec_get(vec_found); if (d) { *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); } } } } 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", line_in); return; } sprintf(newvec, "%s = %e", outvar, result); wl_let = alloc(struct wordlist); wl_let->wl_next = NULL; wl_let->wl_word = copy(newvec); 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; // else if (ft_batchmode == TRUE) return FALSE; else return TRUE; } /* 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 ); } else { if ( (equal_ptr = strstr( token, "=" )) ) { 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; } } txfree(token); return return_val; } /* 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"); } } /* 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; /* 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; } /* New way of processing measure statements using common code in fcn get_measure2() (com_measure2.c)*/ out_line[0] = EOS ; 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; } 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; } newcard = alloc(struct line); newcard->li_line = strdup(out_line); newcard->li_next = NULL; if ( meas_results == NULL ) meas_results = end = newcard; else { end->li_next = newcard; end = newcard; } txfree(an_type); txfree(resname); txfree(meastype); /* 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); } } /* end of for loop (first pass through .meas lines) */ /* 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(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; } if ( strcmp( an_name, an_type ) != 0 ) { 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, "%s", newcard->li_line ); end = newcard; newcard = newcard->li_next; txfree( end->li_line ); txfree( end ); txfree(an_type); txfree(resname); txfree(meastype); continue; } 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" ); } } txfree(an_type); txfree(resname); txfree(meastype); } if ( just_chk_meas != TRUE ) fprintf( stdout, "\n" ); txfree(an_name); fflush( stdout ); //nupa_list_params(); } /* called from dctran.c:470, if timepoint is accepted. 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; bool autostop; measures_passed = TRUE; if ( cp_getvar( "autostop", VT_BOOL, (bool *) &autostop ) ) { do_measure( what, TRUE ); if ( measures_passed == TRUE ) flag = TRUE; } return flag; } /* parses the .meas line into a wordlist (without leading .meas) */ static wordlist *measure_parse_line( char *line ) { int 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 = MALLOC(len) ; sprintf( long_str, "%s%s", item, extra_item ) ; txfree( item ) ; txfree( extra_item ) ; item = long_str ; } new_item = alloc(struct wordlist) ; new_item->wl_word = item ; new_item->wl_next = NULL ; new_item->wl_prev = NULL ; wl = wl_append(wl, new_item) ; } while( line && *line ) ; return(wl) ; } /* end measure_parse_line() */