1 /* Routines to evaluate the .measure cards.
2 Entry point is function do_measure(), called by fcn dosim()
3 from runcoms.c:335, after simulation is finished.
4
5 In addition it contains the fcn com_meas(), which provide the
6 interactive 'meas' command.
7 */
8
9 #include "ngspice/ngspice.h"
10 #include "ngspice/cpdefs.h"
11 #include "ngspice/ftedefs.h"
12 #include "ngspice/dvec.h"
13
14 #include "rawfile.h"
15 #include "variable.h"
16 #include "numparam/numpaif.h"
17 #include "ngspice/missing_math.h"
18 #include "com_measure2.h"
19 #include "com_let.h"
20 #include "com_commands.h"
21 #include "com_display.h"
22
23
24 static wordlist *measure_parse_line(char *line);
25
26 extern bool ft_batchmode;
27 extern bool rflag;
28
29
30 /* measure in interactive mode:
31 meas command inside .control ... .endc loop or manually entered.
32 meas has to be followed by the standard tokens (see measure_extract_variables()).
33 The result is put into a vector with name "result"
34 */
35
36 void
com_meas(wordlist * wl)37 com_meas(wordlist *wl)
38 {
39 /* wl: in, input line of meas command */
40 char *line_in, *outvar;
41 wordlist *wl_count, *wl_let;
42
43 char *vec_found, *token, *equal_ptr;
44 wordlist *wl_index;
45 struct dvec *d;
46 int err = 0;
47
48 int fail;
49 double result = 0;
50
51 if (!wl) {
52 com_display(NULL);
53 return;
54 }
55 wl_count = wl;
56
57 /* check each wl entry, if it contain '=' and if the following token is
58 a single valued vector. If yes, replace this vector by its value.
59 Vectors may stem from other meas commands, or be generated elsewhere
60 within the .control .endc script. All other right hand side vectors are
61 treated in com_measure2.c. */
62 wl_index = wl;
63
64 while (wl_index) {
65 token = wl_index->wl_word;
66 /* find the vector vec_found, next token after each '=' sign.
67 May be in the next wl_word */
68 if (token[strlen(token) - 1] == '=') {
69 wl_index = wl_index->wl_next;
70 if (wl_index == NULL) {
71 line_in = wl_flatten(wl);
72 fprintf(stderr, "\nError: meas failed due to missing token in \n meas %s \n\n", line_in);
73 tfree(line_in);
74 return;
75 }
76 vec_found = wl_index->wl_word;
77 /* token may be already a value, maybe 'LAST', which we have to keep, or maybe a vector */
78 if (!cieq(vec_found, "LAST")) {
79 INPevaluate(&vec_found, &err, 1);
80 /* if not a valid number */
81 if (err) {
82 /* check if vec_found is a valid vector */
83 d = vec_get(vec_found);
84 /* Only if we have a single valued vector, replacing
85 of the rigt hand side does make sense */
86 if (d && (d->v_length == 1) && (d->v_numdims == 1)) {
87 /* get its value */
88 wl_index->wl_word = tprintf("%e", d->v_realdata[0]);
89 tfree(vec_found);
90 }
91 }
92 }
93 }
94 /* may be inside the same wl_word */
95 else if ((equal_ptr = strchr(token, '=')) != NULL) {
96 vec_found = equal_ptr + 1;
97 if (!cieq(vec_found, "LAST")) {
98 INPevaluate(&vec_found, &err, 1);
99 if (err) {
100 d = vec_get(vec_found);
101 /* Only if we have a single valued vector, replacing
102 of the rigt hand side does make sense */
103 if (d && (d->v_length == 1) && (d->v_numdims == 1)) {
104 int lhs_len = (int)(equal_ptr - token);
105 wl_index->wl_word =
106 tprintf("%.*s=%e", lhs_len, token, d->v_realdata[0]);
107 tfree(token);
108 }
109 }
110 }
111 } else {
112 ; // nothing
113 }
114 wl_index = wl_index->wl_next;
115 }
116
117 line_in = wl_flatten(wl);
118
119 /* get output var name */
120 wl_count = wl_count->wl_next;
121 if (!wl_count) {
122 fprintf(stdout,
123 " meas %s failed!\n"
124 " unspecified output var name\n\n", line_in);
125 tfree(line_in);
126 return;
127 }
128 outvar = wl_count->wl_word;
129
130 fail = get_measure2(wl, &result, NULL, FALSE);
131
132 if (fail) {
133 fprintf(stdout, " meas %s failed!\n\n", line_in);
134 tfree(line_in);
135 return;
136 }
137
138 wl_let = wl_cons(tprintf("%s = %e", outvar, result), NULL);
139 com_let(wl_let);
140 wl_free(wl_let);
141 tfree(line_in);
142 }
143
144
145 static bool
chkAnalysisType(char * an_type)146 chkAnalysisType(char *an_type)
147 {
148 /* only support tran, dc, ac, sp analysis type for now */
149 if (strcmp(an_type, "tran") != 0 && strcmp(an_type, "ac") != 0 &&
150 strcmp(an_type, "dc") != 0 && strcmp(an_type, "sp") != 0)
151 return FALSE;
152 else
153 return TRUE;
154 }
155
156
157 /* Gets pointer to double value after 'xxx=' and advances pointer of *line.
158 On error returns FALSE. */
159 static bool
get_double_value(char ** line,char * name,double * value,bool just_chk_meas)160 get_double_value(
161 char **line, /*in|out: pointer to line to be parsed */
162 char *name, /*in: xxx e.g. 'val' from 'val=0.5' */
163 double *value, /*out: return value (e.g. 0.5) from 'val=0.5'*/
164 bool just_chk_meas /* in: just check measurement if true */
165 )
166 {
167 char *token = gettok(line);
168 bool return_val = TRUE;
169 char *equal_ptr, *junk;
170 int err = 0;
171
172 if (name && (strncmp(token, name, strlen(name)) != 0)) {
173 if (just_chk_meas != TRUE) fprintf(cp_err, "Error: syntax error for measure statement; expecting next field to be '%s'.\n", name);
174 return_val = FALSE;
175 } else {
176 /* see if '=' is last char of current token -- implies we need to read value in next token */
177 if (token[strlen(token) - 1] == '=') {
178 txfree(token);
179 junk = token = gettok(line);
180
181 *value = INPevaluate(&junk, &err, 1);
182 } else {
183 if ((equal_ptr = strchr(token, '=')) != NULL) {
184 equal_ptr += 1;
185 *value = INPevaluate(&equal_ptr, &err, 1);
186 } else {
187 if (just_chk_meas != TRUE)
188 fprintf(cp_err, "Error: syntax error for measure statement; missing '='!\n");
189 return_val = FALSE;
190 }
191 }
192 if (err) {
193 if (just_chk_meas != TRUE)
194 fprintf(cp_err, "Error: Bad value.\n");
195 return_val = FALSE;
196 }
197 }
198 txfree(token);
199
200 return return_val;
201 }
202
203
204 /* Entry point for .meas evaluation.
205 Called in fcn dosim() from runcoms.c:335, after simulation is finished
206 with chk_only set to FALSE.
207 Called from fcn check_autostop(),
208 with chk_only set to TRUE (no printouts, no params set).
209 This function returns TRUE if all measurements are ready and complete;
210 FALSE otherwise. If called with chk_only, we can exit early if we
211 fail a test in order to reduce execution time. */
212 bool
do_measure(char * what,bool chk_only)213 do_measure(
214 char *what, /*in: analysis type*/
215 bool chk_only /*in: TRUE if checking for "autostop", FALSE otherwise*/
216 )
217 {
218 struct card *meas_card, *meas_results = NULL, *end = NULL, *newcard;
219 char *line, *an_name, *an_type, *resname, *meastype, *str_ptr, out_line[1000];
220 int ok = 0;
221 int fail;
222 int num_failed = 0;
223 double result = 0;
224 bool first_time = TRUE;
225 bool measures_passed;
226 wordlist *measure_word_list;
227 int precision = measure_get_precision();
228
229 #ifdef HAS_PROGREP
230 if (!chk_only)
231 SetAnalyse("meas", 0);
232 #endif
233
234 an_name = copy(what); /* analysis type, e.g. "tran" */
235 strtolower(an_name);
236 measure_word_list = NULL;
237 measures_passed = TRUE;
238
239 /* don't allow .meas if batchmode is set by -b and -r rawfile given */
240 if (ft_batchmode && rflag) {
241 fprintf(cp_err, "\nNo .measure possible in batch mode (-b) with -r rawfile set!\n");
242 fprintf(cp_err, "Remove rawfile and use .print or .plot or\n");
243 fprintf(cp_err, "select interactive mode (optionally with .control section) instead.\n\n");
244 return (measures_passed);
245 }
246
247 /* don't allow autostop if no .meas commands are given in the input file */
248 if ((cp_getvar("autostop", CP_BOOL, NULL, 0)) && (ft_curckt->ci_meas == NULL)) {
249 fprintf(cp_err, "\nWarning: No .meas commands found!\n");
250 fprintf(cp_err, " Option autostop is not available, ignored!\n\n");
251 cp_remvar("autostop");
252 return (FALSE);
253 }
254
255 /* Evaluating the linked list of .meas cards, assembled from the input deck
256 by fcn inp_spsource() in inp.c:575.
257 A typical .meas card will contain:
258 parameter value
259 nameof card .meas(ure)
260 analysis type tran only tran available currently
261 result name myout defined by user
262 measurement type trig|delay|param|expr|avg|mean|max|min|rms|integ(ral)|when
263
264 The measurement type determines how to continue the .meas card.
265 param|expr are skipped in first pass through .meas cards and are treated in second pass,
266 all others are treated in fcn get_measure2() (com_measure2.c).
267 */
268
269 /* first pass through .meas cards: evaluate everything except param|expr */
270 for (meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->nextcard) {
271 line = meas_card->line;
272
273 line = nexttok(line); /* discard .meas */
274
275 an_type = gettok(&line);
276 resname = gettok(&line);
277 meastype = gettok(&line);
278
279 if (chkAnalysisType(an_type) != TRUE) {
280 if (!chk_only) {
281 fprintf(cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->linenum);
282 fprintf(cp_err, " %s\n", meas_card->line);
283 }
284
285 txfree(an_type);
286 txfree(resname);
287 txfree(meastype);
288 continue;
289 }
290 /* print header before evaluating first .meas line */
291 else if (first_time) {
292 first_time = FALSE;
293
294 if (!chk_only && strcmp(an_type, "tran") == 0) {
295 fprintf(stdout, "\n Measurements for Transient Analysis\n\n");
296 }
297 }
298
299 /* skip param|expr measurement types for now -- will be done after other measurements */
300 if (strncmp(meastype, "param", 5) == 0 || strncmp(meastype, "expr", 4) == 0) {
301 txfree(an_type);
302 txfree(resname);
303 txfree(meastype);
304 continue;
305 }
306
307 /* skip .meas line, if analysis type from line and name of analysis performed differ */
308 if (strcmp(an_name, an_type) != 0) {
309 txfree(an_type);
310 txfree(resname);
311 txfree(meastype);
312 continue;
313 }
314
315 /* New way of processing measure statements using common code
316 in fcn get_measure2() (com_measure2.c)*/
317 out_line[0] = '\0';
318 measure_word_list = measure_parse_line(meas_card->line);
319 if (measure_word_list) {
320 fail = get_measure2(measure_word_list, &result, out_line, chk_only);
321 if (fail) {
322 measures_passed = FALSE;
323 if (!chk_only)
324 fprintf(stderr, " %s failed!\n\n", meas_card->line);
325 num_failed++;
326 if (chk_only) {
327 /* added for speed - cleanup last parse and break */
328 txfree(an_type);
329 txfree(resname);
330 txfree(meastype);
331 wl_free(measure_word_list);
332 break;
333 }
334 } else {
335 if (!chk_only)
336 nupa_add_param(resname, result);
337 }
338 wl_free(measure_word_list);
339 } else {
340 measures_passed = FALSE;
341 num_failed++;
342 }
343
344 if (!chk_only) {
345 newcard = TMALLOC(struct card, 1);
346 newcard->line = copy(out_line);
347 newcard->nextcard = NULL;
348
349 if (meas_results == NULL) {
350 meas_results = end = newcard;
351 } else {
352 end->nextcard = newcard;
353 end = newcard;
354 }
355 }
356
357 txfree(an_type);
358 txfree(resname);
359 txfree(meastype);
360
361 } /* end of for loop (first pass through .meas lines) */
362
363 if (chk_only) {
364 tfree(an_name);
365 return (measures_passed);
366 }
367 /* second pass through .meas cards: now do param|expr .meas statements */
368 newcard = meas_results;
369 for (meas_card = ft_curckt->ci_meas; meas_card != NULL; meas_card = meas_card->nextcard) {
370 line = meas_card->line;
371
372 line = nexttok(line); /* discard .meas */
373
374 an_type = gettok(&line);
375 resname = gettok(&line);
376 meastype = gettok(&line);
377
378 if (chkAnalysisType(an_type) != TRUE) {
379 if (!chk_only) {
380 fprintf(cp_err, "Error: unrecognized analysis type '%s' for the following .meas statement on line %d:\n", an_type, meas_card->linenum);
381 fprintf(cp_err, " %s\n", meas_card->line);
382 }
383
384 txfree(an_type);
385 txfree(resname);
386 txfree(meastype);
387 continue;
388 }
389 if (strcmp(an_name, an_type) != 0) {
390 txfree(an_type);
391 txfree(resname);
392 txfree(meastype);
393 continue;
394 }
395
396 if (strncmp(meastype, "param", 5) != 0 && strncmp(meastype, "expr", 4) != 0) {
397
398 if (!chk_only)
399 fprintf(stdout, "%s", newcard->line);
400 end = newcard;
401 newcard = newcard->nextcard;
402
403 txfree(end->line);
404 txfree(end);
405
406 txfree(an_type);
407 txfree(resname);
408 txfree(meastype);
409 continue;
410 }
411
412 if (!chk_only)
413 fprintf(stdout, "%-20s=", resname);
414
415 if (!chk_only) {
416 ok = nupa_eval(meas_card);
417
418 if (ok) {
419 str_ptr = strstr(meas_card->line, meastype);
420 if (!get_double_value(&str_ptr, meastype, &result, chk_only)) {
421 if (!chk_only)
422 fprintf(stdout, " failed\n");
423 } else {
424 if (!chk_only)
425 fprintf(stdout, " %.*e\n", precision, result);
426 nupa_add_param(resname, result);
427 }
428 } else {
429 if (!chk_only)
430 fprintf(stdout, " failed\n");
431 }
432 }
433 txfree(an_type);
434 txfree(resname);
435 txfree(meastype);
436 }
437
438 if (!chk_only)
439 fprintf(stdout, "\n");
440
441 txfree(an_name);
442
443 fflush(stdout);
444
445 return(measures_passed);
446 }
447
448
449 /* called from dctran.c:470, if timepoint is accepted.
450 Returns TRUE if measurement (just a check, no output) has been successful.
451 If TRUE is returned, transient simulation is stopped.
452 Returns TRUE if "autostop" has been set as an option and if do_measure
453 passes all tests and thereby returns TRUE. 'what' is set to "tran". */
454
455 bool
check_autostop(char * what)456 check_autostop(char* what)
457 {
458 bool flag = FALSE;
459
460 if (cp_getvar("autostop", CP_BOOL, NULL, 0))
461 flag = do_measure(what, TRUE);
462
463 return flag;
464 }
465
466
467 /* parses the .meas line into a wordlist (without leading .meas) */
468 static wordlist *
measure_parse_line(char * line)469 measure_parse_line(char *line)
470 {
471 size_t len; /* length of string */
472 wordlist *wl; /* build a word list - head of list */
473 wordlist *new_item; /* single item of a list */
474 char *item; /* parsed item */
475 char *long_str; /* concatenated string */
476 char *extra_item; /* extra item */
477
478 wl = NULL;
479 line = nexttok(line);
480 do {
481 item = gettok(&line);
482 if (!(item))
483 break;
484
485 len = strlen(item);
486 if (item[len-1] == '=') {
487 /* We can't end on an equal append the next piece */
488 extra_item = gettok(&line);
489 if (!(extra_item))
490 break;
491
492 len += strlen(extra_item) + 2;
493 long_str = TMALLOC(char, len);
494 sprintf(long_str, "%s%s", item, extra_item);
495 txfree(item);
496 txfree(extra_item);
497 item = long_str;
498 }
499 new_item = wl_cons(item, NULL);
500 wl = wl_append(wl, new_item);
501 } while (line && *line);
502
503 return (wl);
504 }
505