1 /*
2 * Copyright (c) 1999-2020 Stephen Williams (steve@icarus.com)
3 *
4 * This source code is free software; you can redistribute it
5 * and/or modify it in source code form under the terms of the GNU
6 * General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 # include "sys_priv.h"
21 # include <assert.h>
22 # include <string.h>
23 # include <errno.h>
24 # include <ctype.h>
25 # include <stdio.h>
26 # include <stdlib.h>
27 # include <math.h>
28 # include "ivl_alloc.h"
29
30 // Flag to enable better compatibility with other simulators
31 static unsigned compatible_flag = 0;
32
check_command_line_args(void)33 static void check_command_line_args(void)
34 {
35 struct t_vpi_vlog_info vlog_info;
36
37 vpi_get_vlog_info(&vlog_info);
38
39 for (int idx = 0 ; idx < vlog_info.argc ; idx += 1) {
40 if (strcmp(vlog_info.argv[idx],"-compatible") == 0) {
41 compatible_flag = 1;
42
43 }
44 }
45 }
46
47 /* Printf wrapper to handle both MCD/FD */
my_mcd_printf(PLI_UINT32 mcd,const char * fmt,...)48 static PLI_INT32 my_mcd_printf(PLI_UINT32 mcd, const char *fmt, ...)
49 {
50 int r = 0;
51
52 va_list ap;
53 va_start(ap, fmt);
54
55 if (IS_MCD(mcd)) {
56 r = vpi_mcd_vprintf(mcd, fmt, ap);
57 } else {
58 FILE *fp = vpi_get_file(mcd);
59 if (fp) r = vfprintf(fp, fmt, ap);
60 }
61
62 va_end(ap);
63 return r;
64 }
65
my_mcd_rawwrite(PLI_UINT32 mcd,const char * buf,size_t count)66 static void my_mcd_rawwrite(PLI_UINT32 mcd, const char*buf, size_t count)
67 {
68 if (IS_MCD(mcd)) {
69 vpip_mcd_rawwrite(mcd, buf, count);
70 } else {
71 FILE*fp = vpi_get_file(mcd);
72 if (fp) {
73 while (count > 0) {
74 size_t rc = fwrite(buf, 1, count, fp);
75 if (rc == 0) break;
76 count -= rc;
77 buf += rc;
78 }
79 }
80 }
81 }
82
83 struct timeformat_info_s timeformat_info = { 0, 0, 0, 20 };
84
85 struct strobe_cb_info {
86 const char*name;
87 char*filename;
88 int lineno;
89 int default_format;
90 vpiHandle scope;
91 vpiHandle*items;
92 unsigned nitems;
93 unsigned fd_mcd;
94 };
95
96 /*
97 * The number of decimal digits needed to represent a
98 * nr_bits binary number is floor(nr_bits*log_10(2))+1,
99 * where log_10(2) = 0.30102999566398.... and I approximate
100 * this transcendental number as 146/485, to avoid the vagaries
101 * of floating-point. The smallest nr_bits for which this
102 * approximation fails is 2621,
103 * 2621*log_10(2)=789.9996, but (2621*146+484)/485=790 (exactly).
104 * In cases like this, all that happens is we allocate one
105 * unneeded char for the output. I add a "L" suffix to 146
106 * to make sure the computation is done as long ints, otherwise
107 * on a 16-bit int machine (allowed by ISO C) we would mangle
108 * this computation for bit-length of 224. I'd like to put
109 * in a test for nr_bits < LONG_MAX/146, but don't know how
110 * to fail, other than crashing.
111 *
112 * In an April 2000 thread in comp.unix.programmer, with subject
113 * "integer -> string", I <LRDoolittle@lbl.gov> give the 28/93
114 * approximation, but overstate its accuracy: that version first
115 * fails when the number of bits is 289, not 671.
116 *
117 * This result does not include space for a trailing '\0', if any.
118 */
calc_dec_size(int nr_bits,int is_signed)119 __inline__ static int calc_dec_size(int nr_bits, int is_signed)
120 {
121 int r;
122 if (is_signed) --nr_bits;
123 r = (nr_bits * 146L + 484) / 485;
124 if (is_signed) ++r;
125 return r;
126 }
127
vpi_get_dec_size(vpiHandle item)128 static int vpi_get_dec_size(vpiHandle item)
129 {
130 return calc_dec_size(
131 vpi_get(vpiSize, item),
132 vpi_get(vpiSigned, item)==1
133 );
134 }
135
array_from_iterator(struct strobe_cb_info * info,vpiHandle argv)136 static void array_from_iterator(struct strobe_cb_info*info, vpiHandle argv)
137 {
138 if (argv) {
139 vpiHandle item;
140 unsigned nitems = 1;
141 vpiHandle*items = malloc(sizeof(vpiHandle));
142 items[0] = vpi_scan(argv);
143 if (items[0] == 0) {
144 free(items);
145 info->nitems = 0;
146 info->items = 0;
147 return;
148 }
149
150 for (item = vpi_scan(argv) ; item ; item = vpi_scan(argv)) {
151 items = realloc(items, (nitems+1)*sizeof(vpiHandle));
152 items[nitems] = item;
153 nitems += 1;
154 }
155
156 info->nitems = nitems;
157 info->items = items;
158
159 } else {
160 info->nitems = 0;
161 info->items = 0;
162 }
163 }
164
get_default_format(const char * name)165 static int get_default_format(const char *name)
166 {
167 int default_format;
168
169 switch(name[ strlen(name)-1 ]){
170 /* writE/strobE or monitoR or displaY/fdisplaY or sformaT/sformatF */
171 case 'e':
172 case 'r':
173 case 't':
174 case 'f':
175 case 'y': default_format = vpiDecStrVal; break;
176 case 'h': default_format = vpiHexStrVal; break;
177 case 'o': default_format = vpiOctStrVal; break;
178 case 'b': default_format = vpiBinStrVal; break;
179 default:
180 default_format = -1;
181 assert(0);
182 }
183
184 return default_format;
185 }
186
187 /* Build the format using the variables that control how the item will
188 * be printed. This is used in error messages and directly by the e/f/g
189 * format codes (minus the enclosing <>). The user needs to free the
190 * returned string. */
format_as_string(int ljust,int plus,int ld_zero,int width,int prec,char fmt)191 static char * format_as_string(int ljust, int plus, int ld_zero, int width,
192 int prec, char fmt)
193 {
194 char buf[256];
195 unsigned int size = 0;
196
197 /* Do not remove/change the "<" without also changing the e/f/g format
198 * code below! */
199 buf[size++] = '<';
200 buf[size++] = '%';
201 if (ljust == 1) buf[size++] = '-';
202 if (plus == 1) buf[size++] = '+';
203 if (ld_zero == 1) buf[size++] = '0';
204 if (width != -1)
205 size += sprintf(&buf[size], "%d", width);
206 if (prec != -1)
207 size += sprintf(&buf[size], ".%d", prec);
208 if (fmt) buf[size++] = fmt;
209 /* The same goes here ">"! */
210 buf[size++] = '>';
211 buf[size] = '\0';
212 return strdup(buf);
213 }
214
get_time(char * rtn,const char * value,int prec,PLI_INT32 time_units)215 static void get_time(char *rtn, const char *value, int prec,
216 PLI_INT32 time_units)
217 {
218 int shift = time_units - timeformat_info.units;
219
220 /* Strip any leading zeros, but leave a single zero. */
221 while (value[0] == '0' && value[1] != '\0') value += 1;
222 /* We need to scale the number up. */
223 if (shift >= 0) {
224 strcpy(rtn, value);
225 /* Shift only non-zero values. */
226 while (shift > 0 && value[0] != '0') {
227 strcat(rtn, "0");
228 shift -= 1;
229 }
230 if (prec > 0) strcat(rtn, ".");
231 while (prec > 0) {
232 strcat(rtn, "0");
233 prec -= 1;
234 }
235
236 /* We need to scale the number down. */
237 } else {
238 int len = strlen(value);
239 int head = len + shift;
240 int tail;
241 /* We have digits to the left of the decimal point. */
242 if (head > 0) {
243 strncpy(rtn, value, head);
244 *(rtn+head) = '\0';
245 if (prec > 0) {
246 strcat(rtn, ".");
247 strncat(rtn, &value[head], prec);
248 tail = prec + shift;
249 while (tail > 0) {
250 strcat(rtn, "0");
251 tail -= 1;
252 }
253 }
254 /* All digits are to the right of the decimal point. */
255 } else {
256 strcpy(rtn, "0");
257 if (prec > 0) strcat(rtn, ".");
258 /* Add leading zeros as needed. */
259 head = -head;
260 if (head > prec) head = prec;
261 while (head > 0) {
262 strcat(rtn, "0");
263 head -= 1;
264 }
265 /* Add digits from the value if they fit. */
266 tail = prec + len + shift;
267 if (tail > 0) {
268 strncat(rtn, value, tail);
269 /* Add trailing zeros to fill out the precision. */
270 tail = prec + shift + 1 - len;
271 while (tail > 0) {
272 strcat(rtn, "0");
273 tail -= 1;
274 }
275 }
276 }
277 }
278
279 strcat(rtn, timeformat_info.suff);
280 }
281
get_time_real(char * rtn,double value,int prec,PLI_INT32 time_units)282 static void get_time_real(char *rtn, double value, int prec,
283 PLI_INT32 time_units)
284 {
285 /* Scale the value from its time units to the format time units. */
286 if (time_units >= timeformat_info.units) {
287 value *= pow(10.0, time_units - timeformat_info.units);
288 } else {
289 value /= pow(10.0, timeformat_info.units - time_units);
290 }
291 sprintf(rtn, "%0.*f%s", prec, value, timeformat_info.suff);
292 }
293
get_format_char(char ** rtn,int ljust,int plus,int ld_zero,int width,int prec,char fmt,const struct strobe_cb_info * info,unsigned int * idx)294 static unsigned int get_format_char(char **rtn, int ljust, int plus,
295 int ld_zero, int width, int prec,
296 char fmt, const struct strobe_cb_info *info,
297 unsigned int *idx)
298 {
299 s_vpi_value value;
300 char *result, *fmtb;
301 unsigned int size;
302 unsigned int ini_size = 512; /* The initial size of the buffer. */
303
304 /* Make sure the width fits in the initial buffer. */
305 assert(width >= -1);
306 if ((unsigned int)(width+1) > ini_size) ini_size = width + 1;
307
308 /* The default return value is the full format. */
309 result = malloc(ini_size*sizeof(char));
310 fmtb = format_as_string(ljust, plus, ld_zero, width, prec, fmt);
311 strcpy(result, fmtb);
312 size = strlen(result) + 1; /* fallback value if errors */
313 switch (fmt) {
314
315 case '%':
316 case '\0':
317 if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 ||
318 prec != -1) {
319 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
320 info->filename, info->lineno, info->name, fmtb);
321 }
322 strcpy(result, "%");
323 size = strlen(result) + 1;
324 break;
325
326 case 'b':
327 case 'B':
328 case 'o':
329 case 'O':
330 case 'h':
331 case 'H':
332 case 'x':
333 case 'X':
334 *idx += 1;
335 if (plus != 0 || prec != -1) {
336 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
337 info->filename, info->lineno, info->name, fmtb);
338 }
339 if (*idx >= info->nitems) {
340 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
341 info->filename, info->lineno, info->name, fmtb);
342 } else {
343 switch (fmt) {
344 case 'b':
345 case 'B':
346 value.format = vpiBinStrVal;
347 break;
348 case 'o':
349 case 'O':
350 value.format = vpiOctStrVal;
351 break;
352 case 'h':
353 case 'H':
354 case 'x':
355 case 'X':
356 value.format = vpiHexStrVal;
357 break;
358 }
359 vpi_get_value(info->items[*idx], &value);
360 if (value.format == vpiSuppressVal) {
361 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
362 info->filename, info->lineno, info->name, fmtb);
363 } else {
364 unsigned swidth = strlen(value.value.str), free_flag = 0;;
365 char *cp = value.value.str;
366
367 if (ld_zero == 1) {
368 /* Strip the leading zeros if a width is not given. */
369 if (width == -1) while (*cp == '0' && *(cp+1) != '\0') cp++;
370 /* Pad with leading zeros. */
371 else if (ljust == 0 && (signed)swidth < width) {
372 unsigned pad = (unsigned)width - swidth;
373 cp = malloc((width+1)*sizeof(char));
374 memset(cp, '0', pad);
375 strcpy(cp+pad, value.value.str);
376 free_flag = 1;
377 /* For a left aligned value also strip the leading zeros. */
378 } else if (ljust != 0) while (*cp == '0' && *(cp+1) != '\0') cp++;
379 }
380
381 /* If a width was not given, use a width of zero. */
382 if (width == -1) width = 0;
383
384 /* If the default buffer is too small, make it big enough. */
385 size = strlen(cp) + 1;
386 if ((signed)size < (width+1)) size = width+1;
387 if (size > ini_size) result = realloc(result, size*sizeof(char));
388
389 if (ljust == 0) sprintf(result, "%*s", width, cp);
390 else sprintf(result, "%-*s", width, cp);
391 if (free_flag) free(cp);
392 size = strlen(result) + 1;
393 }
394 }
395 break;
396
397 case 'c':
398 case 'C':
399 *idx += 1;
400 if (plus != 0 || prec != -1) {
401 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
402 info->filename, info->lineno, info->name, fmtb);
403 }
404 if (*idx >= info->nitems) {
405 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
406 info->filename, info->lineno, info->name, fmtb);
407 } else {
408 value.format = vpiIntVal;
409 vpi_get_value(info->items[*idx], &value);
410 if (value.format == vpiSuppressVal) {
411 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
412 info->filename, info->lineno, info->name, fmtb);
413 } else {
414 char ch = value.value.integer;
415
416 /* If the width is less than one then use a width of one. */
417 if (width < 1) width = 1;
418 size = width + 1;
419 assert(size <= ini_size);
420
421 if (ljust == 0) {
422 if (width > 1) {
423 memset(result, (ld_zero == 1 ? '0': ' '), width-1);
424 }
425 result[width-1] = ch;
426 } else {
427 result[0] = ch;
428 if (width > 1) {
429 memset(result+1, ' ', width-1);
430 }
431 }
432 result[width] = '\0';
433 }
434 }
435 break;
436
437 case 'd':
438 case 'D':
439 *idx += 1;
440 if (prec != -1) {
441 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
442 info->filename, info->lineno, info->name, fmtb);
443 }
444 if (*idx >= info->nitems) {
445 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
446 info->filename, info->lineno, info->name, fmtb);
447 } else {
448 value.format = vpiDecStrVal;
449 vpi_get_value(info->items[*idx], &value);
450 if (value.format == vpiSuppressVal) {
451 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
452 info->filename, info->lineno, info->name, fmtb);
453 } else {
454 unsigned pad = 0;
455 unsigned swidth = strlen(value.value.str) +
456 (value.value.str[0] == '-' ? 0 : (unsigned)plus);
457 char *tbuf, *cpb, *cp = value.value.str;
458
459 /* Allocate storage and calculate the pad if needed. */
460 if (ljust == 0 && ld_zero == 1 && (signed)swidth < width) {
461 tbuf = malloc((width+1)*sizeof(char));
462 pad = (unsigned)width - swidth;
463 } else {
464 tbuf = malloc((swidth+1)*sizeof(char));
465 }
466 cpb = tbuf;
467
468 /* Insert the sign if needed. */
469 if (plus == 1 && *cp != '-') {
470 *cpb = '+';
471 cpb += 1;
472 } else if (*cp == '-') {
473 *cpb = '-';
474 cpb += 1;
475 cp += 1;
476 }
477
478 /* Now add padding if it is needed and then add the value. */
479 memset(cpb, '0', pad);
480 strcpy(cpb+pad, cp);
481
482 /* If a width was not given, use the default, unless we have a
483 * leading zero (width of zero). Because the width of a real in
484 * Icarus is 1 the string length will set the width of a real
485 * displayed using %d. */
486 if (width == -1) {
487 width = (ld_zero == 1) ? 0 : vpi_get_dec_size(info->items[*idx]);
488 }
489
490 /* If the default buffer is too small make it big enough. */
491 size = strlen(tbuf) + 1;
492 if ((signed)size < (width+1)) size = width+1;
493 if (size > ini_size) result = realloc(result, size*sizeof(char));
494
495 if (ljust == 0) sprintf(result, "%*s", width, tbuf);
496 else sprintf(result, "%-*s", width, tbuf);
497 free(tbuf);
498 size = strlen(result) + 1;
499 }
500 }
501 break;
502
503 case 'e':
504 case 'E':
505 case 'f':
506 case 'F':
507 case 'g':
508 case 'G':
509 *idx += 1;
510 if (*idx >= info->nitems) {
511 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
512 info->filename, info->lineno, info->name, fmtb);
513 } else {
514 value.format = vpiRealVal;
515 vpi_get_value(info->items[*idx], &value);
516 if (value.format == vpiSuppressVal) {
517 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
518 info->filename, info->lineno, info->name, fmtb);
519 } else {
520 char *cp = fmtb;
521
522 if (fmt == 'F') {
523 while (*cp != 'F') cp++;
524 *cp = 'f';
525 }
526 while (*cp != '>') cp++;
527 *cp = '\0';
528
529 /* If the default buffer is too small make it big enough.
530 *
531 * This should always give enough space. The maximum double
532 * is approximately 1.8*10^308 this means we could need 310
533 * characters plus the precision. We'll use 320 to give some
534 * extra buffer space. The initial buffers size should work
535 * for most cases, but to be safe we add the precision to
536 * the maximum size (think %6.300f when passed 1.2*10^308). */
537 size = width + 1;
538 if (size < 320) size = 320;
539 size += prec;
540 if (size > ini_size) result = realloc(result, size*sizeof(char));
541 #if !defined(__GNUC__)
542 if (isnan(value.value.real))
543 sprintf(result, "%s", "nan");
544 else
545 sprintf(result, fmtb+1, value.value.real);
546 #else
547 sprintf(result, fmtb+1, value.value.real);
548 #endif
549 size = strlen(result) + 1;
550 }
551 }
552 break;
553
554 /* This Verilog format specifier is not currently supported!
555 * vpiCell and vpiLibrary need to be implemented first. */
556 case 'l':
557 case 'L':
558 vpi_printf("WARNING: %s:%d: %%%c currently unsupported %s%s.\n",
559 info->filename, info->lineno, fmt, info->name, fmtb);
560 break;
561
562 case 'm':
563 case 'M':
564 if (plus != 0 || prec != -1) {
565 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
566 info->filename, info->lineno, info->name, fmtb);
567 }
568 /* If a width was not given, use a width of zero. */
569 if (width == -1) width = 0;
570
571 {
572 char *cp = vpi_get_str(vpiFullName, info->scope);
573 /* If the default buffer is too small, make it big enough. */
574 size = strlen(cp) + 1;
575 if ((signed)size < (width+1)) size = width+1;
576 if (size > ini_size) result = realloc(result, size*sizeof(char));
577
578 if (ljust == 0) sprintf(result, "%*s", width, cp);
579 else sprintf(result, "%-*s", width, cp);
580 }
581 size = strlen(result) + 1;
582 break;
583
584 case 's':
585 case 'S':
586 /* Strings are not numeric and are not zero filled, so %08s => %8s. */
587 *idx += 1;
588 if (plus != 0 || prec != -1) {
589 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
590 info->filename, info->lineno, info->name, fmtb);
591 }
592 if (*idx >= info->nitems) {
593 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
594 info->filename, info->lineno, info->name, fmtb);
595 } else {
596 value.format = vpiStringVal;
597 vpi_get_value(info->items[*idx], &value);
598 if (value.format == vpiSuppressVal) {
599 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
600 info->filename, info->lineno, info->name, fmtb);
601 } else {
602 if (width == -1) {
603 /* If all we have is a leading zero then we want a zero width. */
604 if (ld_zero == 1) width = 0;
605 /* Otherwise if a width was not given, use the value width. */
606 else width = (vpi_get(vpiSize, info->items[*idx])+7) / 8;
607 }
608 /* If the default buffer is too small make it big enough. */
609 size = strlen(value.value.str) + 1;
610 if ((signed)size < (width+1)) size = width+1;
611 if (size > ini_size) result = realloc(result, size*sizeof(char));
612 if (ljust == 0) sprintf(result, "%*s", width, value.value.str);
613 else sprintf(result, "%-*s", width, value.value.str);
614 size = strlen(result) + 1;
615 }
616 }
617 break;
618
619 case 't':
620 case 'T':
621 *idx += 1;
622 if (*idx >= info->nitems) {
623 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
624 info->filename, info->lineno, info->name, fmtb);
625 } else {
626 PLI_INT32 type;
627
628 /* Get the argument type and value. */
629 type = vpi_get(vpiType, info->items[*idx]);
630 if (((type == vpiConstant || type == vpiParameter) &&
631 vpi_get(vpiConstType, info->items[*idx]) == vpiRealConst) ||
632 type == vpiRealVar || (type == vpiSysFuncCall &&
633 vpi_get(vpiFuncType, info->items[*idx]) == vpiRealFunc)) {
634 value.format = vpiRealVal;
635 } else {
636 value.format = vpiDecStrVal;
637 }
638 vpi_get_value(info->items[*idx], &value);
639 if (value.format == vpiSuppressVal) {
640 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
641 info->filename, info->lineno, info->name, fmtb);
642 } else {
643 char *tbuf, *prev_suff = 0;
644 PLI_INT32 time_units = vpi_get(vpiTimeUnit, info->scope);
645
646 if (plus != 0) {
647 /* Icarus-specific extension to print out time units.
648 * It is needed by vhdlpp to correctly implement time'image(). */
649 PLI_INT32 time_prec = vpi_get(vpiTimePrecision, info->scope);
650
651 /* We are about to override the suffix string set with $timeformat(),
652 * therefore we need to restore it after the call. */
653 prev_suff = timeformat_info.suff;
654
655 if (time_units < -12)
656 timeformat_info.suff = strdup(" fs");
657 else if (time_units < -9)
658 timeformat_info.suff = strdup(" ps");
659 else if (time_units < -6)
660 timeformat_info.suff = strdup(" ns");
661 else if (time_units < -3)
662 timeformat_info.suff = strdup(" us");
663 else if (time_units < 0)
664 timeformat_info.suff = strdup(" ms");
665 else
666 timeformat_info.suff = strdup(" s");
667
668 /* Adjust shift for get_time(), so the number indeed matches the unit */
669 time_units += (3 + (time_units % 3)) % 3 + (time_prec - time_units);
670 }
671
672 unsigned swidth, free_flag = 0;
673 unsigned suff_len = strlen(timeformat_info.suff);
674 char *cp;
675
676 /* The 512 (513-1 for EOL) is more than enough for any double
677 * value (309 digits plus a decimal point maximum). Because of
678 * scaling this could be larger. For decimal values you can
679 * have an arbitrary value so you can overflow the buffer, but
680 * for now we will assume the user will use this as intended
681 * (pass a time variable or the result of a time function). */
682 tbuf = malloc((513+suff_len)*sizeof(char));
683 if (prec == -1) prec = timeformat_info.prec;
684 if (value.format == vpiRealVal) {
685 get_time_real(tbuf, value.value.real, prec, time_units);
686 } else {
687 get_time(tbuf, value.value.str, prec, time_units);
688 }
689 cp = tbuf;
690 swidth = strlen(tbuf);
691
692 if (plus != 0) {
693 /* Restore the previous suffix string, overridden by
694 * a vhdlpp-specific $sformatf() call. */
695 free(timeformat_info.suff);
696 timeformat_info.suff = prev_suff;
697 }
698
699 if (ld_zero == 1) {
700 /* No leading zeros are created by this conversion so just make
701 * the width 0 for this case. */
702 if (width == -1) width = 0;
703 /* Pad with leading zeros. */
704 else if (ljust == 0 && (signed)swidth < width) {
705 unsigned pad = (unsigned)width - swidth;
706 cp = malloc((width+1)*sizeof(char));
707 memset(cp, '0', pad);
708 strcpy(cp+pad, tbuf);
709 free_flag = 1;
710 }
711 }
712 if (width == -1) width = timeformat_info.width;
713
714 /* If the default buffer is too small make it big enough. */
715 size = strlen(tbuf) + 1;
716 if ((signed)size < (width+1)) size = width+1;
717 if (size > ini_size) result = realloc(result, size*sizeof(char));
718
719 if (ljust == 0) sprintf(result, "%*s", width, cp);
720 else sprintf(result, "%-*s", width, cp);
721 if (free_flag) free(cp);
722 free(tbuf);
723 size = strlen(result) + 1;
724 }
725 }
726 break;
727
728 case 'u':
729 case 'U':
730 *idx += 1;
731 if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 ||
732 prec != -1) {
733 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
734 info->filename, info->lineno, info->name, fmtb);
735 }
736 if (*idx >= info->nitems) {
737 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
738 info->filename, info->lineno, info->name, fmtb);
739 } else {
740 value.format = vpiVectorVal;
741 vpi_get_value(info->items[*idx], &value);
742 if (value.format == vpiSuppressVal) {
743 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
744 info->filename, info->lineno, info->name, fmtb);
745 } else {
746 PLI_INT32 veclen, word, byte;
747 char *cp;
748
749 veclen = (vpi_get(vpiSize, info->items[*idx])+31)/32;
750 size = veclen * 4 + 1;
751 /* If the default buffer is too small, make it big enough. */
752 if (size > ini_size) result = realloc(result, size*sizeof(char));
753 cp = result;
754 for (word = 0; word < veclen; word += 1) {
755 PLI_INT32 bits = value.value.vector[word].aval &
756 ~value.value.vector[word].bval;
757 #ifdef WORDS_BIGENDIAN
758 for (byte = 3; byte >= 0; byte -= 1) {
759 #else
760 for (byte = 0; byte <= 3; byte += 1) {
761 #endif
762 *cp = (bits >> byte*8) & 0xff;
763 cp += 1;
764 }
765 }
766 *cp = '\0';
767 }
768 }
769 /* size is defined above! We can't use strlen here since this can
770 * be a binary string (can contain NULLs). */
771 break;
772
773 case 'v':
774 case 'V':
775 *idx += 1;
776 if (plus != 0 || prec != -1) {
777 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
778 info->filename, info->lineno, info->name, fmtb);
779 }
780 if (*idx >= info->nitems) {
781 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
782 info->filename, info->lineno, info->name, fmtb);
783 } else {
784 value.format = vpiStrengthVal;
785 vpi_get_value(info->items[*idx], &value);
786 if (value.format == vpiSuppressVal) {
787 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
788 info->filename, info->lineno, info->name, fmtb);
789 } else {
790 char tbuf[4], *rbuf;
791 PLI_INT32 nbits;
792 int bit;
793
794 /* If a width was not given use a width of zero. */
795 if (width == -1) width = 0;
796 nbits = vpi_get(vpiSize, info->items[*idx]);
797 /* This is 4 chars for all but the last bit (strength + "_")
798 * which only needs three chars (strength), but then you need
799 * space for the EOS '\0', so it is just number of bits * 4. */
800 size = nbits*4;
801 rbuf = malloc(size*sizeof(char));
802 if ((signed)size < (width+1)) size = width+1;
803 if (size > ini_size) result = realloc(result, size*sizeof(char));
804 strcpy(rbuf, "");
805 for (bit = nbits-1; bit >= 0; bit -= 1) {
806 vpip_format_strength(tbuf, &value, bit);
807 strcat(rbuf, tbuf);
808 if (bit > 0) strcat(rbuf, "_");
809 }
810 if (ljust == 0) sprintf(result, "%*s", width, rbuf);
811 else sprintf(result, "%-*s", width, rbuf);
812 free(rbuf);
813 size = strlen(result) + 1;
814 }
815 }
816 break;
817
818 case 'z':
819 case 'Z':
820 *idx += 1;
821 size = strlen(result) + 1; /* fallback value if errors */
822 if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 ||
823 prec != -1) {
824 vpi_printf("WARNING: %s:%d: invalid format %s%s.\n",
825 info->filename, info->lineno, info->name, fmtb);
826 }
827 if (*idx >= info->nitems) {
828 vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n",
829 info->filename, info->lineno, info->name, fmtb);
830 } else {
831 value.format = vpiVectorVal;
832 vpi_get_value(info->items[*idx], &value);
833 if (value.format == vpiSuppressVal) {
834 vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n",
835 info->filename, info->lineno, info->name, fmtb);
836 } else {
837 PLI_INT32 veclen, word, elem, bits, byte;
838 char *cp;
839
840 veclen = (vpi_get(vpiSize, info->items[*idx])+31)/32;
841 size = 2 * veclen * 4 + 1;
842 /* If the default buffer is too small, make it big enough. */
843 if (size > ini_size) result = realloc(result, size*sizeof(char));
844 cp = result;
845 for (word = 0; word < veclen; word += 1) {
846 /* Write the aval followed by the bval in endian order. */
847 for (elem = 0; elem < 2; elem += 1) {
848 bits = *(&value.value.vector[word].aval+elem);
849 #ifdef WORDS_BIGENDIAN
850 for (byte = 3; byte >= 0; byte -= 1) {
851 #else
852 for (byte = 0; byte <= 3; byte += 1) {
853 #endif
854 *cp = (bits >> byte*8) & 0xff;
855 cp += 1;
856 }
857 }
858 }
859 *cp = '\0';
860 }
861 }
862 /* size is defined above! We can't use strlen here since this can
863 * be a binary string (can contain NULLs). */
864 break;
865
866 default:
867 vpi_printf("WARNING: %s:%d: unknown format %s%s.\n",
868 info->filename, info->lineno, info->name, fmtb);
869 size = strlen(result) + 1;
870 break;
871 }
872 free(fmtb);
873 /* We can't use strdup here since %u and %z can insert NULL
874 * characters into the stream. */
875 *rtn = malloc(size*sizeof(char));
876 memcpy(*rtn, result, size);
877 free(result);
878 return size - 1;
879 }
880
881 /* We can't use the normal str functions on the return value since
882 * %u and %z can insert NULL characters into the stream. */
883 static unsigned int get_format(char **rtn, char *fmt,
884 const struct strobe_cb_info *info, unsigned int *idx)
885 {
886 char *cp = fmt;
887 unsigned int size;
888
889 *rtn = strdup("");
890 size = 1;
891 while (*cp) {
892 size_t cnt = strcspn(cp, "%");
893
894 if (cnt > 0) {
895 *rtn = realloc(*rtn, (size+cnt)*sizeof(char));
896 memcpy(*rtn+size-1, cp, cnt);
897 size += cnt;
898 cp += cnt;
899 } else {
900 int ljust = 0, plus = 0, ld_zero = 0, width = -1, prec = -1;
901 char *result;
902
903 cp += 1;
904 while ((*cp == '-') || (*cp == '+')) {
905 if (*cp == '-') ljust = 1;
906 else plus = 1;
907 cp += 1;
908 }
909 if (*cp == '0') {
910 ld_zero = 1;
911 cp += 1;
912 }
913 if (isdigit((int)*cp)) width = strtoul(cp, &cp, 10);
914 if (*cp == '.') {
915 cp += 1;
916 prec = strtoul(cp, &cp, 10);
917 }
918 cnt = get_format_char(&result, ljust, plus, ld_zero, width, prec, *cp,
919 info, idx);
920 *rtn = realloc(*rtn, (size+cnt)*sizeof(char));
921 memcpy(*rtn+size-1, result, cnt);
922 free(result);
923 size += cnt;
924 if (*cp) cp += 1;
925 }
926 }
927 *(*rtn+size-1) = '\0';
928 return size - 1;
929 }
930
931 static unsigned int get_numeric(char **rtn, const struct strobe_cb_info *info,
932 vpiHandle item)
933 {
934 int size, min;
935 s_vpi_value val;
936
937 val.format = info->default_format;
938 vpi_get_value(item, &val);
939
940 switch(info->default_format){
941 case vpiDecStrVal:
942 size = vpi_get_dec_size(item);
943 /* -1 can be represented as a one bit signed value. This returns
944 * a size of 1 which is too small for the -1 string value so make
945 * the string width the minimum display width. */
946 min = strlen(val.value.str);
947 if (size < min) size = min;
948 *rtn = malloc((size+1)*sizeof(char));
949 sprintf(*rtn, "%*s", size, val.value.str);
950 break;
951 default:
952 *rtn = strdup(val.value.str);
953 }
954
955 return strlen(*rtn);
956 }
957
958 /* In many places we can't use the normal str functions since %u and %z
959 * can insert NULL characters into the stream. */
960 static char *get_display(unsigned int *rtnsz, const struct strobe_cb_info *info)
961 {
962 char *result, *fmt, *rtn, *func_name;
963 const char *cresult;
964 s_vpi_value value;
965 unsigned int idx, size, width;
966 char buf[256];
967
968 rtn = strdup("");
969 size = 1;
970 for (idx = 0; idx < info->nitems; idx += 1) {
971 vpiHandle item = info->items[idx];
972
973 switch (vpi_get(vpiType, item)) {
974
975 case vpiConstant:
976 case vpiParameter:
977 if (vpi_get(vpiConstType, item) == vpiStringConst) {
978 value.format = vpiStringVal;
979 vpi_get_value(item, &value);
980 fmt = strdup(value.value.str);
981 width = get_format(&result, fmt, info, &idx);
982 free(fmt);
983 } else if (vpi_get(vpiConstType, item) == vpiRealConst) {
984 value.format = vpiRealVal;
985 vpi_get_value(item, &value);
986 #if !defined(__GNUC__)
987 if (compatible_flag)
988 sprintf(buf, "%g", value.value.real);
989 else {
990 if (value.value.real == 0.0 || value.value.real == -0.0)
991 sprintf(buf, "%.05f", value.value.real);
992 else
993 sprintf(buf, "%#g", value.value.real);
994 }
995 #else
996 sprintf(buf, compatible_flag ? "%g" : "%#g", value.value.real);
997 #endif
998 result = strdup(buf);
999 width = strlen(result);
1000 } else {
1001 width = get_numeric(&result, info, item);
1002 }
1003 rtn = realloc(rtn, (size+width)*sizeof(char));
1004 memcpy(rtn+size-1, result, width);
1005 free(result);
1006 break;
1007
1008 case vpiNet:
1009 case vpiReg:
1010 case vpiBitVar:
1011 case vpiByteVar:
1012 case vpiShortIntVar:
1013 case vpiIntVar:
1014 case vpiLongIntVar:
1015 case vpiIntegerVar:
1016 case vpiMemoryWord:
1017 case vpiPartSelect:
1018 width = get_numeric(&result, info, item);
1019 rtn = realloc(rtn, (size+width)*sizeof(char));
1020 memcpy(rtn+size-1, result, width);
1021 free(result);
1022 break;
1023
1024 /* It appears that this is not currently used! A time variable is
1025 passed as an integer and processed above. Hence this code has
1026 only been visually checked. */
1027 case vpiTimeVar:
1028 value.format = vpiDecStrVal;
1029 vpi_get_value(item, &value);
1030 get_time(buf, value.value.str, timeformat_info.prec,
1031 vpi_get(vpiTimeUnit, info->scope));
1032 width = strlen(buf);
1033 if (width < timeformat_info.width) width = timeformat_info.width;
1034 rtn = realloc(rtn, (size+width)*sizeof(char));
1035 sprintf(rtn+size-1, "%*s", width, buf);
1036 break;
1037
1038 /* Realtime variables are also processed here. */
1039 case vpiRealVar:
1040 value.format = vpiRealVal;
1041 vpi_get_value(item, &value);
1042 #if !defined(__GNUC__)
1043 if (compatible_flag)
1044 sprintf(buf, "%g", value.value.real);
1045 else {
1046 if (value.value.real == 0.0 || value.value.real == -0.0)
1047 sprintf(buf, "%.05f", value.value.real);
1048 else
1049 sprintf(buf, "%#g", value.value.real);
1050 }
1051 #else
1052 sprintf(buf, compatible_flag ? "%g" : "%#g", value.value.real);
1053 #endif
1054 width = strlen(buf);
1055 rtn = realloc(rtn, (size+width)*sizeof(char));
1056 memcpy(rtn+size-1, buf, width);
1057 break;
1058
1059 /* Process string variables like string constants: interpret
1060 the contained strings like format strings. */
1061 case vpiStringVar:
1062 value.format = vpiStringVal;
1063 vpi_get_value(item, &value);
1064 fmt = strdup(value.value.str);
1065 width = get_format(&result, fmt, info, &idx);
1066 free(fmt);
1067 rtn = realloc(rtn, (size+width)*sizeof(char));
1068 memcpy(rtn+size-1, result, width);
1069 free(result);
1070 break;
1071
1072 case vpiSysFuncCall:
1073 func_name = vpi_get_str(vpiName, item);
1074 if (strcmp(func_name, "$time") == 0) {
1075 value.format = vpiDecStrVal;
1076 vpi_get_value(item, &value);
1077 width = strlen(value.value.str);
1078 if (width < 20) width = 20;
1079 rtn = realloc(rtn, (size+width)*sizeof(char));
1080 sprintf(rtn+size-1, "%*s", width, value.value.str);
1081
1082 } else if (strcmp(func_name, "$stime") == 0) {
1083 value.format = vpiDecStrVal;
1084 vpi_get_value(item, &value);
1085 width = strlen(value.value.str);
1086 if (width < 10) width = 10;
1087 rtn = realloc(rtn, (size+width)*sizeof(char));
1088 sprintf(rtn+size-1, "%*s", width, value.value.str);
1089
1090 } else if (strcmp(func_name, "$simtime") == 0) {
1091 value.format = vpiDecStrVal;
1092 vpi_get_value(item, &value);
1093 width = strlen(value.value.str);
1094 if (width < 20) width = 20;
1095 rtn = realloc(rtn, (size+width)*sizeof(char));
1096 sprintf(rtn+size-1, "%*s", width, value.value.str);
1097
1098 } else if (strcmp(func_name, "$realtime") == 0) {
1099 /* Use the local scope precision. */
1100 int use_prec = vpi_get(vpiTimeUnit, info->scope) -
1101 vpi_get(vpiTimePrecision, info->scope);
1102 assert(use_prec >= 0);
1103 value.format = vpiRealVal;
1104 vpi_get_value(item, &value);
1105 sprintf(buf, "%.*f", use_prec, value.value.real);
1106 width = strlen(buf);
1107 rtn = realloc(rtn, (size+width)*sizeof(char));
1108 sprintf(rtn+size-1, "%*s", width, buf);
1109
1110 } else {
1111 vpi_printf("WARNING: %s:%d: %s does not support %s as an argument!\n",
1112 info->filename, info->lineno, info->name, func_name);
1113 strcpy(buf, "<?>");
1114 width = strlen(buf);
1115 rtn = realloc(rtn, (size+width)*sizeof(char));
1116 memcpy(rtn+size-1, buf, width);
1117 }
1118 break;
1119
1120 default:
1121 vpi_printf("WARNING: %s:%d: unknown argument type (%s) given to %s!\n",
1122 info->filename, info->lineno, vpi_get_str(vpiType, item),
1123 info->name);
1124 cresult = "<?>";
1125 width = strlen(cresult);
1126 rtn = realloc(rtn, (size+width)*sizeof(char));
1127 memcpy(rtn+size-1, cresult, width);
1128 break;
1129 }
1130 size += width;
1131 }
1132 rtn[size-1] = '\0';
1133 *rtnsz = size - 1;
1134 return rtn;
1135 }
1136
1137 #ifdef BR916_STOPGAP_FIX
1138 static char br916_hint_issued = 0;
1139 #endif
1140
1141 static int sys_check_args(vpiHandle callh, vpiHandle argv, const PLI_BYTE8*name,
1142 int no_auto, int is_monitor)
1143 {
1144 vpiHandle arg;
1145 int ret = 0;
1146
1147 /* If there are no arguments, just return. */
1148 if (argv == 0) return ret;
1149
1150 for (arg = vpi_scan(argv); arg; arg = vpi_scan(argv)) {
1151 if (no_auto && vpi_get(vpiAutomatic, arg)) {
1152 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1153 (int)vpi_get(vpiLineNo, callh));
1154 vpi_printf("%s argument \"%s\" is an automatic variable.\n",
1155 name, vpi_get_str(vpiName, arg));
1156 ret = 1;
1157 }
1158
1159 switch (vpi_get(vpiType, arg)) {
1160 case vpiMemoryWord:
1161 case vpiPartSelect:
1162 if (is_monitor && vpi_get(vpiConstantSelect, arg) == 0) {
1163 vpi_printf("SORRY: %s:%d: ",
1164 vpi_get_str(vpiFile, callh),
1165 (int)vpi_get(vpiLineNo, callh));
1166 vpi_printf("%s must have a constant %s select.\n",
1167 name, vpi_get_str(vpiType, arg));
1168 ret = 1;
1169 }
1170 // fallthrough
1171 case vpiConstant:
1172 case vpiParameter:
1173 case vpiNet:
1174 case vpiReg:
1175 case vpiIntegerVar:
1176 case vpiBitVar:
1177 case vpiByteVar:
1178 case vpiShortIntVar:
1179 case vpiIntVar:
1180 case vpiLongIntVar:
1181 case vpiTimeVar:
1182 case vpiRealVar:
1183 case vpiStringVar:
1184 #ifdef BR916_STOPGAP_FIX
1185 // no_auto implies either $strobe or $monitor
1186 if (no_auto) {
1187 switch (vpi_get(_vpiFromThr, arg)) {
1188 case _vpiVThr:
1189 case _vpiWord:
1190 case _vpiString:
1191 vpi_printf("SORRY: %s:%d: ",
1192 vpi_get_str(vpiFile, callh),
1193 (int)vpi_get(vpiLineNo, callh));
1194 vpi_printf("currently only simple signals or constant "
1195 "expressions may be passed to %s.\n", name);
1196 if (!br916_hint_issued) {
1197 vpi_printf("NOTE: You can work around this by "
1198 "assigning the desired expression "
1199 "to an\n"
1200 " intermediate net (using a "
1201 "continuous assignment) and passing "
1202 "that net\n"
1203 " to %s.\n", name);
1204 br916_hint_issued = 1;
1205 }
1206 ret = 1;
1207 default:
1208 break;
1209 }
1210 }
1211 #endif
1212 case vpiSysFuncCall:
1213 break;
1214
1215 default:
1216 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1217 (int)vpi_get(vpiLineNo, callh));
1218 vpi_printf("%s does not support argument type (%s).\n", name,
1219 vpi_get_str(vpiType, arg));
1220 ret = 1;
1221 break;
1222 }
1223 }
1224
1225 return ret;
1226 }
1227
1228 /* Common compiletf routine. */
1229 static PLI_INT32 sys_common_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name, int no_auto,
1230 int is_monitor)
1231 {
1232 vpiHandle callh, argv;
1233
1234 callh = vpi_handle(vpiSysTfCall, 0);
1235 argv = vpi_iterate(vpiArgument, callh);
1236
1237 if (name[1] == 'f') {
1238 /* Check that there is a fd/mcd and that it is numeric. */
1239 if (argv == 0) {
1240 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1241 (int)vpi_get(vpiLineNo, callh));
1242 vpi_printf("%s requires at least a file descriptor/MCD.\n",
1243 name);
1244 vpi_control(vpiFinish, 1);
1245 return 0;
1246 }
1247
1248 if (! is_numeric_obj(vpi_scan(argv))) {
1249 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1250 (int)vpi_get(vpiLineNo, callh));
1251 vpi_printf("%s's file descriptor/MCD must be numeric.\n",
1252 name);
1253 vpi_control(vpiFinish, 1);
1254 }
1255 }
1256
1257 if (sys_check_args(callh, argv, name, no_auto, is_monitor)) {
1258 vpi_control(vpiFinish, 1);
1259 }
1260 return 0;
1261 }
1262
1263 /* Check the $display, $write, $fdisplay and $fwrite based tasks. */
1264 static PLI_INT32 sys_display_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
1265 {
1266 /* These tasks can have automatic variables and are not monitor. */
1267 return sys_common_compiletf(name, 0, 0);
1268 }
1269
1270 /* This implements the $sformatf, $display/$fdisplay
1271 * and the $write/$fwrite based tasks. */
1272 static PLI_INT32 sys_display_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1273 {
1274 vpiHandle callh, argv, scope;
1275 struct strobe_cb_info info;
1276 char* result;
1277 unsigned int size;
1278 PLI_UINT32 fd_mcd;
1279 s_vpi_value val;
1280
1281 callh = vpi_handle(vpiSysTfCall, 0);
1282 argv = vpi_iterate(vpiArgument, callh);
1283
1284 /* Get the file/MC descriptor and verify it is valid. */
1285 if (name[1] == 'f') {
1286 vpiHandle fd = vpi_scan(argv);
1287 if (get_fd_mcd_from_arg(&fd_mcd, fd, callh, name)) {
1288 vpi_free_object(argv);
1289 return 0;
1290 }
1291 } else if (strncmp(name, "$sformatf", 9) == 0) {
1292 /* return as a string */
1293 fd_mcd = 0;
1294 } else {
1295 /* stdout */
1296 fd_mcd = 1;
1297 }
1298
1299 scope = vpi_handle(vpiScope, callh);
1300 assert(scope);
1301 /* We could use vpi_get_str(vpiName, callh) to get the task name,
1302 * but name is already defined. */
1303 info.name = name;
1304 info.filename = strdup(vpi_get_str(vpiFile, callh));
1305 info.lineno = (int)vpi_get(vpiLineNo, callh);
1306 info.default_format = get_default_format(name);
1307 info.scope = scope;
1308 array_from_iterator(&info, argv);
1309
1310 /* Because %u and %z may put embedded NULL characters into the
1311 * returned string strlen() may not match the real size! */
1312 result = get_display(&size, &info);
1313
1314 if (fd_mcd > 0) {
1315 my_mcd_rawwrite(fd_mcd, result, size);
1316 if ((strncmp(name,"$display",8) == 0) ||
1317 (strncmp(name,"$fdisplay",9) == 0)) my_mcd_rawwrite(fd_mcd, "\n", 1);
1318 } else {
1319 /* Return as a string ($sformatf) */
1320 val.format = vpiStringVal;
1321 val.value.str = result;
1322 vpi_put_value(callh, &val, 0, vpiNoDelay);
1323 }
1324
1325 free(result);
1326 free(info.filename);
1327 free(info.items);
1328 return 0;
1329 }
1330
1331 /*
1332 * The strobe implementation takes the parameter handles that are
1333 * passed to the calltf and puts them in to an array for safe
1334 * keeping. That array (and other bookkeeping) is passed, via the
1335 * struct_cb_info object, to the REadOnlySych function strobe_cb,
1336 * where it is used to perform the actual formatting and printing.
1337 */
1338 static PLI_INT32 strobe_cb(p_cb_data cb)
1339 {
1340 struct strobe_cb_info*info = (struct strobe_cb_info*)cb->user_data;
1341
1342 /* We really need to cancel any $fstrobe() calls for a file when it
1343 * is closed, but for now we will just skip processing the result.
1344 * Which has the same basic effect. */
1345 if (is_valid_fd_mcd(info->fd_mcd)) {
1346 char* result;
1347 unsigned int size;
1348 /* Because %u and %z may put embedded NULL characters into the
1349 * returned string strlen() may not match the real size! */
1350 result = get_display(&size, info);
1351 my_mcd_rawwrite(info->fd_mcd, result, size);
1352 my_mcd_rawwrite(info->fd_mcd, "\n", 1);
1353 free(result);
1354 }
1355
1356 free(info->filename);
1357 free(info->items);
1358 free(info);
1359 return 0;
1360 }
1361
1362 /* Check both the $strobe and $fstrobe based tasks. */
1363 static PLI_INT32 sys_strobe_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1364 {
1365 /* These tasks can not have automatic variables and are not monitor. */
1366 return sys_common_compiletf(name, 1, 0);
1367 }
1368
1369 /* This implements both the $strobe and $fstrobe based tasks. */
1370 static PLI_INT32 sys_strobe_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
1371 {
1372 vpiHandle callh, argv, scope;
1373 struct t_cb_data cb;
1374 struct t_vpi_time timerec;
1375 struct strobe_cb_info*info;
1376 PLI_UINT32 fd_mcd;
1377
1378 callh = vpi_handle(vpiSysTfCall, 0);
1379 argv = vpi_iterate(vpiArgument, callh);
1380
1381 /* Get the file/MC descriptor and verify it is valid. */
1382 if (name[1] == 'f') {
1383 vpiHandle fd = vpi_scan(argv);
1384 if (get_fd_mcd_from_arg(&fd_mcd, fd, callh, name)) {
1385 vpi_free_object(argv);
1386 return 0;
1387 }
1388
1389 } else {
1390 fd_mcd = 1;
1391 }
1392
1393 scope = vpi_handle(vpiScope, callh);
1394 assert(scope);
1395
1396 info = calloc(1, sizeof(struct strobe_cb_info));
1397 info->fd_mcd = fd_mcd;
1398 /* We could use vpi_get_str(vpiName, callh) to get the task name,
1399 * but name is already defined. */
1400 info->name = name;
1401 info->filename = strdup(vpi_get_str(vpiFile, callh));
1402 info->lineno = (int)vpi_get(vpiLineNo, callh);
1403 info->default_format = get_default_format(name);
1404 info->scope= scope;
1405 array_from_iterator(info, argv);
1406
1407 timerec.type = vpiSimTime;
1408 timerec.low = 0;
1409 timerec.high = 0;
1410
1411 cb.reason = cbReadOnlySynch;
1412 cb.cb_rtn = strobe_cb;
1413 cb.time = &timerec;
1414 cb.obj = 0;
1415 cb.value = 0;
1416 cb.user_data = (char*)info;
1417 vpi_register_cb(&cb);
1418
1419 return 0;
1420 }
1421
1422 /*
1423 * The $monitor system task works by managing these static variables,
1424 * and the cbValueChange callbacks associated with registers and
1425 * nets. Note that it is proper to keep the state in static variables
1426 * because there can only be one monitor at a time pending (even
1427 * though that monitor may be watching many variables).
1428 */
1429
1430 static struct strobe_cb_info monitor_info = { 0, 0, 0, 0, 0, 0, 0, 0 };
1431 static vpiHandle *monitor_callbacks = 0;
1432 static int monitor_scheduled = 0;
1433 static int monitor_enabled = 1;
1434
1435 static PLI_INT32 monitor_cb_2(p_cb_data cb)
1436 {
1437 char* result;
1438 unsigned int size;
1439
1440 (void)cb; /* Parameter is not used. */
1441
1442 /* Because %u and %z may put embedded NULL characters into the
1443 * returned string strlen() may not match the real size! */
1444 result = get_display(&size, &monitor_info);
1445 my_mcd_rawwrite(monitor_info.fd_mcd, result, size);
1446 my_mcd_rawwrite(monitor_info.fd_mcd, "\n", 1);
1447 monitor_scheduled = 0;
1448 free(result);
1449 return 0;
1450 }
1451
1452 /*
1453 * The monitor_cb_1 callback is called when an event occurs somewhere
1454 * in the simulation. All this function does is schedule the actual
1455 * display to occur in a ReadOnlySync callback. The monitor_scheduled
1456 * flag is used to allow only one monitor strobe to be scheduled.
1457 */
1458 static PLI_INT32 monitor_cb_1(p_cb_data cause)
1459 {
1460 struct t_cb_data cb;
1461 struct t_vpi_time timerec;
1462
1463 (void)cause; /* Parameter is not used. */
1464
1465 if (monitor_enabled == 0) return 0;
1466 if (monitor_scheduled) return 0;
1467
1468 /* This this action caused the first trigger, then schedule
1469 the monitor to happen at the end of the time slice and mark
1470 it as scheduled. */
1471 monitor_scheduled += 1;
1472 timerec.type = vpiSimTime;
1473 timerec.low = 0;
1474 timerec.high = 0;
1475
1476 cb.reason = cbReadOnlySynch;
1477 cb.cb_rtn = monitor_cb_2;
1478 cb.time = &timerec;
1479 cb.obj = 0;
1480 cb.value = 0;
1481 vpi_register_cb(&cb);
1482
1483 return 0;
1484 }
1485
1486 static PLI_INT32 sys_monitor_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1487 {
1488 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
1489 vpiHandle argv = vpi_iterate(vpiArgument, callh);
1490
1491 if (sys_check_args(callh, argv, name, 1, 1)) vpi_control(vpiFinish, 1);
1492 return 0;
1493 }
1494
1495 static PLI_INT32 sys_monitor_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
1496 {
1497 vpiHandle callh, argv, scope;
1498 unsigned idx;
1499 struct t_cb_data cb;
1500 struct t_vpi_time timerec;
1501
1502 (void)name; /* Parameter is not used. */
1503
1504 callh = vpi_handle(vpiSysTfCall, 0);
1505 argv = vpi_iterate(vpiArgument, callh);
1506
1507 /* If there was a previous $monitor, then remove the callbacks
1508 related to it. */
1509 if (monitor_callbacks) {
1510 for (idx = 0 ; idx < monitor_info.nitems ; idx += 1)
1511 if (monitor_callbacks[idx])
1512 vpi_remove_cb(monitor_callbacks[idx]);
1513
1514 free(monitor_callbacks);
1515 monitor_callbacks = 0;
1516
1517 free(monitor_info.filename);
1518 free(monitor_info.items);
1519 monitor_info.items = 0;
1520 monitor_info.nitems = 0;
1521 monitor_info.name = 0;
1522 }
1523
1524 scope = vpi_handle(vpiScope, callh);
1525 assert(scope);
1526 /* Make an array of handles from the argument list. */
1527 array_from_iterator(&monitor_info, argv);
1528 monitor_info.name = name;
1529 monitor_info.filename = strdup(vpi_get_str(vpiFile, callh));
1530 monitor_info.lineno = (int)vpi_get(vpiLineNo, callh);
1531 monitor_info.default_format = get_default_format(name);
1532 monitor_info.scope = scope;
1533 monitor_info.fd_mcd = 1;
1534
1535 /* Attach callbacks to all the parameters that might change. */
1536 monitor_callbacks = calloc(monitor_info.nitems, sizeof(vpiHandle));
1537
1538 timerec.type = vpiSuppressTime;
1539 cb.reason = cbValueChange;
1540 cb.cb_rtn = monitor_cb_1;
1541 cb.time = &timerec;
1542 cb.value = NULL;
1543 for (idx = 0 ; idx < monitor_info.nitems ; idx += 1) {
1544
1545 switch (vpi_get(vpiType, monitor_info.items[idx])) {
1546 case vpiMemoryWord:
1547 /*
1548 * We only support constant selections. Make this
1549 * better when we add a real compiletf routine.
1550 */
1551 assert(vpi_get(vpiConstantSelect, monitor_info.items[idx]));
1552 case vpiNet:
1553 case vpiReg:
1554 case vpiIntegerVar:
1555 case vpiBitVar:
1556 case vpiByteVar:
1557 case vpiShortIntVar:
1558 case vpiIntVar:
1559 case vpiLongIntVar:
1560 case vpiRealVar:
1561 case vpiPartSelect:
1562 /* Monitoring reg and net values involves setting
1563 a callback for value changes. Pass the storage
1564 pointer for the callback itself as user_data so
1565 that the callback can refresh itself. */
1566 cb.user_data = (char*)(monitor_callbacks+idx);
1567 cb.obj = monitor_info.items[idx];
1568 monitor_callbacks[idx] = vpi_register_cb(&cb);
1569 break;
1570
1571 }
1572 }
1573
1574 /* When the $monitor is called, it schedules a first display
1575 for the end of the current time, like a $strobe. */
1576 monitor_cb_1(0);
1577
1578 return 0;
1579 }
1580
1581 static PLI_INT32 sys_monitoron_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
1582 {
1583 (void)name; /* Parameter is not used. */
1584 monitor_enabled = 1;
1585 monitor_cb_1(0);
1586 return 0;
1587 }
1588
1589 static PLI_INT32 sys_monitoroff_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
1590 {
1591 (void)name; /* Parameter is not used. */
1592 monitor_enabled = 0;
1593 return 0;
1594 }
1595
1596 static PLI_INT32 sys_swrite_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1597 {
1598 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
1599 vpiHandle argv = vpi_iterate(vpiArgument, callh);
1600 vpiHandle arg;
1601 PLI_INT32 type;
1602
1603 /* Check that there are arguments. */
1604 if (argv == 0) {
1605 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1606 (int)vpi_get(vpiLineNo, callh));
1607 vpi_printf("%s requires at least one argument.\n", name);
1608 vpi_control(vpiFinish, 1);
1609 return 0;
1610 }
1611
1612 /* The first argument must be a register or a SV string. */
1613 arg = vpi_scan(argv); /* This should never be zero. */
1614 type = vpi_get(vpiType, arg);
1615 if (type != vpiReg && type != vpiStringVar) {
1616 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1617 (int)vpi_get(vpiLineNo, callh));
1618 vpi_printf("%s's first argument must be a register or SV string.\n", name);
1619 vpi_control(vpiFinish, 1);
1620 return 0;
1621 }
1622
1623 if (sys_check_args(callh, argv, name, 0, 0)) vpi_control(vpiFinish, 1);
1624 return 0;
1625 }
1626
1627 static PLI_INT32 sys_swrite_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1628 {
1629 vpiHandle callh, argv, reg, scope;
1630 struct strobe_cb_info info;
1631 s_vpi_value val;
1632 unsigned int size;
1633
1634 callh = vpi_handle(vpiSysTfCall, 0);
1635 argv = vpi_iterate(vpiArgument, callh);
1636 reg = vpi_scan(argv);
1637
1638 scope = vpi_handle(vpiScope, callh);
1639 assert(scope);
1640 /* We could use vpi_get_str(vpiName, callh) to get the task name, but
1641 * name is already defined. */
1642 info.name = name;
1643 info.filename = strdup(vpi_get_str(vpiFile, callh));
1644 info.lineno = (int)vpi_get(vpiLineNo, callh);
1645 info.default_format = get_default_format(name);
1646 info.scope = scope;
1647 array_from_iterator(&info, argv);
1648
1649 /* Because %u and %z may put embedded NULL characters into the returned
1650 * string strlen() may not match the real size! */
1651 val.value.str = get_display(&size, &info);
1652 val.format = vpiStringVal;
1653 vpi_put_value(reg, &val, 0, vpiNoDelay);
1654 if (size != strlen(val.value.str)) {
1655 vpi_printf("WARNING: %s:%d: %s returned a value with an embedded NULL "
1656 "(see %%u/%%z).\n", info.filename, info.lineno, name);
1657 }
1658
1659 free(val.value.str);
1660 free(info.filename);
1661 free(info.items);
1662 return 0;
1663 }
1664
1665 static PLI_INT32 sys_sformat_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1666 {
1667 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
1668 vpiHandle argv = vpi_iterate(vpiArgument, callh);
1669 vpiHandle arg;
1670 PLI_INT32 type;
1671
1672 /* Check that there are arguments. */
1673 if (argv == 0) {
1674 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1675 (int)vpi_get(vpiLineNo, callh));
1676 vpi_printf("%s requires at least two arguments.\n", name);
1677 vpi_control(vpiFinish, 1);
1678 return 0;
1679 }
1680
1681 /* The first argument must be a register or a SV string. */
1682 arg = vpi_scan(argv); /* This should never be zero. */
1683 type = vpi_get(vpiType, arg);
1684 if (type != vpiReg && type != vpiStringVar) {
1685 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1686 (int)vpi_get(vpiLineNo, callh));
1687 vpi_printf("%s's first argument must be a register or SV string.\n", name);
1688 vpi_control(vpiFinish, 1);
1689 return 0;
1690 }
1691
1692 /* The second argument must be a string, a register or a SV string. */
1693 arg = vpi_scan(argv);
1694 if (arg == 0) {
1695 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1696 (int)vpi_get(vpiLineNo, callh));
1697 vpi_printf("%s requires at least two arguments.\n", name);
1698 vpi_control(vpiFinish, 1);
1699 return 0;
1700 }
1701 type = vpi_get(vpiType, arg);
1702 if (((type != vpiConstant && type != vpiParameter) ||
1703 vpi_get(vpiConstType, arg) != vpiStringConst) &&
1704 type != vpiReg && type != vpiStringVar) {
1705 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1706 (int)vpi_get(vpiLineNo, callh));
1707 vpi_printf("%s's second argument must be a string or a register.\n", name);
1708 vpi_control(vpiFinish, 1);
1709 return 0;
1710 }
1711
1712 if (sys_check_args(callh, argv, name, 0, 0)) vpi_control(vpiFinish, 1);
1713 return 0;
1714 }
1715
1716 static PLI_INT32 sys_sformat_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1717 {
1718 vpiHandle callh, argv, reg, scope;
1719 struct strobe_cb_info info;
1720 s_vpi_value val;
1721 char *result, *fmt;
1722 unsigned int idx, size;
1723
1724 callh = vpi_handle(vpiSysTfCall, 0);
1725 argv = vpi_iterate(vpiArgument, callh);
1726 reg = vpi_scan(argv);
1727 val.format = vpiStringVal;
1728 vpi_get_value(vpi_scan(argv), &val);
1729 fmt = strdup(val.value.str);
1730
1731 scope = vpi_handle(vpiScope, callh);
1732 assert(scope);
1733 /* We could use vpi_get_str(vpiName, callh) to get the task name, but
1734 * name is already defined. */
1735 info.name = name;
1736 info.filename = strdup(vpi_get_str(vpiFile, callh));
1737 info.lineno = (int)vpi_get(vpiLineNo, callh);
1738 info.default_format = get_default_format(name);
1739 info.scope = scope;
1740 array_from_iterator(&info, argv);
1741 idx = -1;
1742 size = get_format(&result, fmt, &info, &idx);
1743 free(fmt);
1744
1745 if (idx+1< info.nitems) {
1746 vpi_printf("WARNING: %s:%d: %s has %d extra argument(s).\n",
1747 info.filename, info.lineno, name,
1748 info.nitems-idx-1);
1749 }
1750
1751 val.value.str = result;
1752 val.format = vpiStringVal;
1753 vpi_put_value(reg, &val, 0, vpiNoDelay);
1754 if (size != strlen(val.value.str)) {
1755 vpi_printf("WARNING: %s:%d: %s returned a value with an embedded NULL "
1756 "(see %%u/%%z).\n", info.filename, info.lineno, name);
1757 }
1758
1759 free(val.value.str);
1760 free(info.filename);
1761 free(info.items);
1762 return 0;
1763 }
1764
1765 static PLI_INT32 sys_sformatf_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
1766 {
1767 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
1768 vpiHandle argv = vpi_iterate(vpiArgument, callh);
1769 vpiHandle arg;
1770 PLI_INT32 type;
1771
1772 /* Check that there are arguments. */
1773 if (argv == 0) {
1774 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1775 (int)vpi_get(vpiLineNo, callh));
1776 vpi_printf("%s requires at least one argument.\n", name);
1777 vpi_control(vpiFinish, 1);
1778 return 0;
1779 }
1780
1781 /* The first argument must be a string, a register or a SV string. */
1782 arg = vpi_scan(argv);
1783 if (arg == 0) {
1784 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1785 (int)vpi_get(vpiLineNo, callh));
1786 vpi_printf("%s requires at least one argument.\n", name);
1787 vpi_control(vpiFinish, 1);
1788 return 0;
1789 }
1790 type = vpi_get(vpiType, arg);
1791 if (((type != vpiConstant && type != vpiParameter) ||
1792 vpi_get(vpiConstType, arg) != vpiStringConst) &&
1793 type != vpiReg && type != vpiStringVar) {
1794 vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh),
1795 (int)vpi_get(vpiLineNo, callh));
1796 vpi_printf("%s's first argument must be a string or a register.\n", name);
1797 vpi_control(vpiFinish, 1);
1798 return 0;
1799 }
1800
1801 if (sys_check_args(callh, argv, name, 0, 0)) vpi_control(vpiFinish, 1);
1802 return 0;
1803 }
1804
1805 static PLI_INT32 sys_end_of_compile(p_cb_data cb_data)
1806 {
1807 (void)cb_data; /* Parameter is not used. */
1808 /* The default timeformat prints times in unit of simulation
1809 precision. */
1810 free(timeformat_info.suff);
1811 timeformat_info.suff = strdup("");
1812 timeformat_info.units = vpi_get(vpiTimePrecision, 0);
1813 timeformat_info.prec = 0;
1814 timeformat_info.width = 20;
1815 return 0;
1816 }
1817
1818 static PLI_INT32 sys_timeformat_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
1819 {
1820 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
1821 vpiHandle argv = vpi_iterate(vpiArgument, callh);
1822
1823 if (argv) {
1824 vpiHandle arg;
1825
1826 /* Check that the unit argument is numeric. */
1827 if (! is_numeric_obj(vpi_scan(argv))) {
1828 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1829 (int)vpi_get(vpiLineNo, callh));
1830 vpi_printf("%s's units argument must be numeric.\n", name);
1831 vpi_control(vpiFinish, 1);
1832 }
1833
1834 /* Check that the precision argument is given and is numeric. */
1835 arg = vpi_scan(argv);
1836 if (! arg) {
1837 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1838 (int)vpi_get(vpiLineNo, callh));
1839 vpi_printf("%s requires zero or four arguments.\n", name);
1840 vpi_control(vpiFinish, 1);
1841 return 0;
1842 }
1843
1844 if (! is_numeric_obj(arg)) {
1845 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1846 (int)vpi_get(vpiLineNo, callh));
1847 vpi_printf("%s's precision argument must be numeric.\n",
1848 name);
1849 vpi_control(vpiFinish, 1);
1850 }
1851
1852 /* Check that the suffix argument is given and is a string. */
1853 arg = vpi_scan(argv);
1854 if (! arg) {
1855 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1856 (int)vpi_get(vpiLineNo, callh));
1857 vpi_printf("%s requires zero or four arguments.\n", name);
1858 vpi_control(vpiFinish, 1);
1859 return 0;
1860 }
1861
1862 if (! is_string_obj(arg)) {
1863 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1864 (int)vpi_get(vpiLineNo, callh));
1865 vpi_printf("%s's suffix argument must be a string.\n", name);
1866 vpi_control(vpiFinish, 1);
1867 }
1868
1869 /* Check that the min. width argument is given and is numeric. */
1870 arg = vpi_scan(argv);
1871 if (! arg) {
1872 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1873 (int)vpi_get(vpiLineNo, callh));
1874 vpi_printf("%s requires zero or four arguments.\n", name);
1875 vpi_control(vpiFinish, 1);
1876 return 0;
1877 }
1878
1879 if (! is_numeric_obj(arg)) {
1880 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1881 (int)vpi_get(vpiLineNo, callh));
1882 vpi_printf("%s's minimum width argument must be numeric.\n",
1883 name);
1884 vpi_control(vpiFinish, 1);
1885 }
1886
1887 /* Make sure there are no extra arguments. */
1888 check_for_extra_args(argv, callh, name, "four arguments", 0);
1889 }
1890
1891 return 0;
1892 }
1893
1894 static PLI_INT32 sys_timeformat_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
1895 {
1896 s_vpi_value value;
1897 vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
1898 vpiHandle argv = vpi_iterate(vpiArgument, sys);
1899
1900 (void)name; /* Parameter is not used. */
1901
1902 if (argv) {
1903 vpiHandle units = vpi_scan(argv);
1904 vpiHandle prec = vpi_scan(argv);
1905 vpiHandle suff = vpi_scan(argv);
1906 vpiHandle wid = vpi_scan(argv);
1907
1908 vpi_free_object(argv);
1909
1910 value.format = vpiIntVal;
1911 vpi_get_value(units, &value);
1912 timeformat_info.units = value.value.integer;
1913
1914 value.format = vpiIntVal;
1915 vpi_get_value(prec, &value);
1916 timeformat_info.prec = value.value.integer;
1917
1918 value.format = vpiStringVal;
1919 vpi_get_value(suff, &value);
1920 free(timeformat_info.suff);
1921 timeformat_info.suff = strdup(value.value.str);
1922
1923 value.format = vpiIntVal;
1924 vpi_get_value(wid, &value);
1925 timeformat_info.width = value.value.integer;
1926 } else {
1927 /* If no arguments are given then use the default values. */
1928 sys_end_of_compile(NULL);
1929 }
1930
1931 return 0;
1932 }
1933
1934 static const char *pts_convert(int value)
1935 {
1936 const char *string;
1937 switch (value) {
1938 case 2: string = "100s"; break;
1939 case 1: string = "10s"; break;
1940 case 0: string = "1s"; break;
1941 case -1: string = "100ms"; break;
1942 case -2: string = "10ms"; break;
1943 case -3: string = "1ms"; break;
1944 case -4: string = "100us"; break;
1945 case -5: string = "10us"; break;
1946 case -6: string = "1us"; break;
1947 case -7: string = "100ns"; break;
1948 case -8: string = "10ns"; break;
1949 case -9: string = "1ns"; break;
1950 case -10: string = "100ps"; break;
1951 case -11: string = "10ps"; break;
1952 case -12: string = "1ps"; break;
1953 case -13: string = "100fs"; break;
1954 case -14: string = "10fs"; break;
1955 case -15: string = "1fs"; break;
1956 default: string = "invalid"; assert(0);
1957 }
1958 return string;
1959 }
1960
1961 static PLI_INT32 sys_printtimescale_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
1962 {
1963 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
1964 vpiHandle argv = vpi_iterate(vpiArgument, callh);
1965
1966 if (argv) {
1967 vpiHandle arg = vpi_scan(argv);
1968 switch (vpi_get(vpiType, arg)) {
1969 case vpiFunction:
1970 case vpiGenScope:
1971 case vpiIntegerVar:
1972 case vpiBitVar:
1973 case vpiByteVar:
1974 case vpiShortIntVar:
1975 case vpiIntVar:
1976 case vpiLongIntVar:
1977 case vpiMemory:
1978 case vpiMemoryWord:
1979 case vpiModule:
1980 case vpiNamedBegin:
1981 case vpiNamedEvent:
1982 case vpiNamedFork:
1983 case vpiNet:
1984 case vpiNetArray:
1985 // case vpiNetBit: // Unused and unavailable in Icarus
1986 case vpiParameter:
1987 case vpiPartSelect:
1988 case vpiRealVar:
1989 case vpiReg:
1990 // case vpiRegBit: // Unused and unavailable in Icarus
1991 case vpiTask:
1992 case vpiTimeVar: // Unused in Icarus
1993 break;
1994
1995 default:
1996 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
1997 (int)vpi_get(vpiLineNo, callh));
1998 vpi_printf("%s's argument must have a module, given a %s.\n",
1999 name, vpi_get_str(vpiType, arg));
2000 vpi_control(vpiFinish, 1);
2001 }
2002
2003 /* Make sure there are no extra arguments. */
2004 check_for_extra_args(argv, callh, name, "one argument", 1);
2005 }
2006
2007 return 0;
2008 }
2009
2010 static PLI_INT32 sys_printtimescale_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
2011 {
2012 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
2013 vpiHandle argv = vpi_iterate(vpiArgument, callh);
2014 vpiHandle item, scope;
2015
2016 (void)name; /* Parameter is not used. */
2017
2018 if (!argv) {
2019 item = sys_func_module(callh);
2020 } else {
2021 item = vpi_scan(argv);
2022 vpi_free_object(argv);
2023 }
2024 scope = sys_func_module(item);
2025
2026 vpi_printf("Time scale of (%s) is ", vpi_get_str(vpiFullName, item));
2027 vpi_printf("%s / ", pts_convert(vpi_get(vpiTimeUnit, scope)));
2028 vpi_printf("%s\n", pts_convert(vpi_get(vpiTimePrecision, scope)));
2029
2030 return 0;
2031 }
2032
2033 static PLI_INT32 sys_fatal_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
2034 {
2035 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
2036 vpiHandle argv = vpi_iterate(vpiArgument, callh);
2037
2038 if (argv) {
2039 vpiHandle arg = vpi_scan(argv);
2040
2041 /* Check that finish_number (1st argument) is numeric */
2042 if (! is_numeric_obj(arg)) {
2043 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
2044 (int)vpi_get(vpiLineNo, callh));
2045 vpi_printf("%s's finish number must be numeric.\n", name);
2046 vpi_control(vpiFinish, 1);
2047 }
2048
2049 if (sys_check_args(callh, argv, name, 0, 0)) {
2050 vpi_control(vpiFinish, 1);
2051 }
2052 }
2053
2054 return 0;
2055 }
2056
2057 static PLI_INT32 sys_severity_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
2058 {
2059 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
2060 vpiHandle argv = vpi_iterate(vpiArgument, callh);
2061 vpiHandle scope;
2062 struct strobe_cb_info info;
2063 struct t_vpi_time now;
2064 PLI_UINT64 now64;
2065 char *sstr, *t, *dstr;
2066 unsigned int size, location=0;
2067 s_vpi_value finish_number;
2068
2069 /* Set the default finish number for $fatal. */
2070 finish_number.value.integer = 1;
2071
2072 /* Check that the finish number is in range. */
2073 if (strncmp(name,"$fatal", 6) == 0 && argv) {
2074 vpiHandle arg = vpi_scan(argv);
2075 finish_number.format = vpiIntVal;
2076 vpi_get_value(arg, &finish_number);
2077 if ((finish_number.value.integer < 0) ||
2078 (finish_number.value.integer > 2)) {
2079 vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
2080 (int)vpi_get(vpiLineNo, callh));
2081 vpi_printf("$fatal called with finish_number of %d, "
2082 "but it must be 0, 1, or 2.\n",
2083 (int)finish_number.value.integer);
2084 finish_number.value.integer = 1;
2085 }
2086 }
2087
2088 /* convert name to upper and drop $ to get severity string */
2089 sstr = strdup(name) + 1;
2090 for (t=sstr; *t; t+=1) *t = toupper((int)*t);
2091
2092 scope = vpi_handle(vpiScope, callh);
2093 assert(scope);
2094 info.name = name;
2095 info.filename = strdup(vpi_get_str(vpiFile, callh));
2096 info.lineno = (int)vpi_get(vpiLineNo, callh);
2097 info.default_format = vpiDecStrVal;
2098 info.scope = scope;
2099 array_from_iterator(&info, argv);
2100
2101 vpi_printf("%s: %s:%d: ", sstr, info.filename, info.lineno);
2102
2103 dstr = get_display(&size, &info);
2104 while (location < size) {
2105 if (dstr[location] == '\0') {
2106 my_mcd_printf(1, "%c", '\0');
2107 location += 1;
2108 } else {
2109 my_mcd_printf(1, "%s", &dstr[location]);
2110 location += strlen(&dstr[location]);
2111 }
2112 }
2113
2114 now.type = vpiSimTime;
2115 vpi_get_time(0, &now);
2116 now64 = timerec_to_time64(&now);
2117
2118 vpi_printf("\n%*s Time: %" PLI_UINT64_FMT " Scope: %s\n",
2119 (int)strlen(sstr), " ", now64,
2120 vpi_get_str(vpiFullName, scope));
2121
2122 free(--sstr); /* Get the $ back. */
2123 free(info.filename);
2124 free(info.items);
2125 free(dstr);
2126
2127 if (strncmp(name,"$fatal",6) == 0) {
2128 /* Set the exit code from vvp as an error code. */
2129 vpip_set_return_value(1);
2130 /* Now tell the simulator to finish. */
2131 vpi_control(vpiFinish, finish_number.value.integer);
2132 }
2133
2134 return 0;
2135 }
2136
2137
2138 static PLI_INT32 sys_end_of_simulation(p_cb_data cb_data)
2139 {
2140 (void)cb_data; /* Parameter is not used. */
2141 free(monitor_callbacks);
2142 monitor_callbacks = 0;
2143 free(monitor_info.filename);
2144 free(monitor_info.items);
2145 monitor_info.items = 0;
2146 monitor_info.nitems = 0;
2147 monitor_info.name = 0;
2148
2149 free(timeformat_info.suff);
2150 timeformat_info.suff = 0;
2151 return 0;
2152 }
2153
2154 void sys_display_register(void)
2155 {
2156 s_cb_data cb_data;
2157 s_vpi_systf_data tf_data;
2158 vpiHandle res;
2159
2160 check_command_line_args();
2161
2162 /*============================== display */
2163 tf_data.type = vpiSysTask;
2164 tf_data.tfname = "$display";
2165 tf_data.calltf = sys_display_calltf;
2166 tf_data.compiletf = sys_display_compiletf;
2167 tf_data.sizetf = 0;
2168 tf_data.user_data = "$display";
2169 res = vpi_register_systf(&tf_data);
2170 vpip_make_systf_system_defined(res);
2171
2172 tf_data.type = vpiSysTask;
2173 tf_data.tfname = "$displayh";
2174 tf_data.calltf = sys_display_calltf;
2175 tf_data.compiletf = sys_display_compiletf;
2176 tf_data.sizetf = 0;
2177 tf_data.user_data = "$displayh";
2178 res = vpi_register_systf(&tf_data);
2179 vpip_make_systf_system_defined(res);
2180
2181 tf_data.type = vpiSysTask;
2182 tf_data.tfname = "$displayo";
2183 tf_data.calltf = sys_display_calltf;
2184 tf_data.compiletf = sys_display_compiletf;
2185 tf_data.sizetf = 0;
2186 tf_data.user_data = "$displayo";
2187 res = vpi_register_systf(&tf_data);
2188 vpip_make_systf_system_defined(res);
2189
2190 tf_data.type = vpiSysTask;
2191 tf_data.tfname = "$displayb";
2192 tf_data.calltf = sys_display_calltf;
2193 tf_data.compiletf = sys_display_compiletf;
2194 tf_data.sizetf = 0;
2195 tf_data.user_data = "$displayb";
2196 res = vpi_register_systf(&tf_data);
2197 vpip_make_systf_system_defined(res);
2198
2199 /*============================== write */
2200 tf_data.type = vpiSysTask;
2201 tf_data.tfname = "$write";
2202 tf_data.calltf = sys_display_calltf;
2203 tf_data.compiletf = sys_display_compiletf;
2204 tf_data.sizetf = 0;
2205 tf_data.user_data = "$write";
2206 res = vpi_register_systf(&tf_data);
2207 vpip_make_systf_system_defined(res);
2208
2209 tf_data.type = vpiSysTask;
2210 tf_data.tfname = "$writeh";
2211 tf_data.calltf = sys_display_calltf;
2212 tf_data.compiletf = sys_display_compiletf;
2213 tf_data.sizetf = 0;
2214 tf_data.user_data = "$writeh";
2215 res = vpi_register_systf(&tf_data);
2216 vpip_make_systf_system_defined(res);
2217
2218 tf_data.type = vpiSysTask;
2219 tf_data.tfname = "$writeo";
2220 tf_data.calltf = sys_display_calltf;
2221 tf_data.compiletf = sys_display_compiletf;
2222 tf_data.sizetf = 0;
2223 tf_data.user_data = "$writeo";
2224 res = vpi_register_systf(&tf_data);
2225 vpip_make_systf_system_defined(res);
2226
2227 tf_data.type = vpiSysTask;
2228 tf_data.tfname = "$writeb";
2229 tf_data.calltf = sys_display_calltf;
2230 tf_data.compiletf = sys_display_compiletf;
2231 tf_data.sizetf = 0;
2232 tf_data.user_data = "$writeb";
2233 res = vpi_register_systf(&tf_data);
2234 vpip_make_systf_system_defined(res);
2235
2236 /*============================== strobe */
2237 tf_data.type = vpiSysTask;
2238 tf_data.tfname = "$strobe";
2239 tf_data.calltf = sys_strobe_calltf;
2240 tf_data.compiletf = sys_strobe_compiletf;
2241 tf_data.sizetf = 0;
2242 tf_data.user_data = "$strobe";
2243 res = vpi_register_systf(&tf_data);
2244 vpip_make_systf_system_defined(res);
2245
2246 tf_data.type = vpiSysTask;
2247 tf_data.tfname = "$strobeh";
2248 tf_data.calltf = sys_strobe_calltf;
2249 tf_data.compiletf = sys_strobe_compiletf;
2250 tf_data.sizetf = 0;
2251 tf_data.user_data = "$strobeh";
2252 res = vpi_register_systf(&tf_data);
2253 vpip_make_systf_system_defined(res);
2254
2255 tf_data.type = vpiSysTask;
2256 tf_data.tfname = "$strobeo";
2257 tf_data.calltf = sys_strobe_calltf;
2258 tf_data.compiletf = sys_strobe_compiletf;
2259 tf_data.sizetf = 0;
2260 tf_data.user_data = "$strobeo";
2261 res = vpi_register_systf(&tf_data);
2262 vpip_make_systf_system_defined(res);
2263
2264 tf_data.type = vpiSysTask;
2265 tf_data.tfname = "$strobeb";
2266 tf_data.calltf = sys_strobe_calltf;
2267 tf_data.compiletf = sys_strobe_compiletf;
2268 tf_data.sizetf = 0;
2269 tf_data.user_data = "$strobeb";
2270 res = vpi_register_systf(&tf_data);
2271 vpip_make_systf_system_defined(res);
2272
2273 /*============================== fstrobe */
2274 tf_data.type = vpiSysTask;
2275 tf_data.tfname = "$fstrobe";
2276 tf_data.calltf = sys_strobe_calltf;
2277 tf_data.compiletf = sys_strobe_compiletf;
2278 tf_data.sizetf = 0;
2279 tf_data.user_data = "$fstrobe";
2280 res = vpi_register_systf(&tf_data);
2281 vpip_make_systf_system_defined(res);
2282
2283 tf_data.type = vpiSysTask;
2284 tf_data.tfname = "$fstrobeh";
2285 tf_data.calltf = sys_strobe_calltf;
2286 tf_data.compiletf = sys_strobe_compiletf;
2287 tf_data.sizetf = 0;
2288 tf_data.user_data = "$fstrobeh";
2289 res = vpi_register_systf(&tf_data);
2290 vpip_make_systf_system_defined(res);
2291
2292 tf_data.type = vpiSysTask;
2293 tf_data.tfname = "$fstrobeo";
2294 tf_data.calltf = sys_strobe_calltf;
2295 tf_data.compiletf = sys_strobe_compiletf;
2296 tf_data.sizetf = 0;
2297 tf_data.user_data = "$fstrobeo";
2298 res = vpi_register_systf(&tf_data);
2299 vpip_make_systf_system_defined(res);
2300
2301 tf_data.type = vpiSysTask;
2302 tf_data.tfname = "$fstrobeb";
2303 tf_data.calltf = sys_strobe_calltf;
2304 tf_data.compiletf = sys_strobe_compiletf;
2305 tf_data.sizetf = 0;
2306 tf_data.user_data = "$fstrobeb";
2307 res = vpi_register_systf(&tf_data);
2308 vpip_make_systf_system_defined(res);
2309
2310 /*============================== monitor */
2311 tf_data.type = vpiSysTask;
2312 tf_data.tfname = "$monitor";
2313 tf_data.calltf = sys_monitor_calltf;
2314 tf_data.compiletf = sys_monitor_compiletf;
2315 tf_data.sizetf = 0;
2316 tf_data.user_data = "$monitor";
2317 res = vpi_register_systf(&tf_data);
2318 vpip_make_systf_system_defined(res);
2319
2320 tf_data.type = vpiSysTask;
2321 tf_data.tfname = "$monitorh";
2322 tf_data.calltf = sys_monitor_calltf;
2323 tf_data.compiletf = sys_monitor_compiletf;
2324 tf_data.sizetf = 0;
2325 tf_data.user_data = "$monitorh";
2326 res = vpi_register_systf(&tf_data);
2327 vpip_make_systf_system_defined(res);
2328
2329 tf_data.type = vpiSysTask;
2330 tf_data.tfname = "$monitoro";
2331 tf_data.calltf = sys_monitor_calltf;
2332 tf_data.compiletf = sys_monitor_compiletf;
2333 tf_data.sizetf = 0;
2334 tf_data.user_data = "$monitoro";
2335 res = vpi_register_systf(&tf_data);
2336 vpip_make_systf_system_defined(res);
2337
2338 tf_data.type = vpiSysTask;
2339 tf_data.tfname = "$monitorb";
2340 tf_data.calltf = sys_monitor_calltf;
2341 tf_data.compiletf = sys_monitor_compiletf;
2342 tf_data.sizetf = 0;
2343 tf_data.user_data = "$monitorb";
2344 res = vpi_register_systf(&tf_data);
2345 vpip_make_systf_system_defined(res);
2346
2347 tf_data.type = vpiSysTask;
2348 tf_data.tfname = "$monitoron";
2349 tf_data.calltf = sys_monitoron_calltf;
2350 tf_data.compiletf = sys_no_arg_compiletf;
2351 tf_data.sizetf = 0;
2352 tf_data.user_data = "$monitoron";
2353 res = vpi_register_systf(&tf_data);
2354 vpip_make_systf_system_defined(res);
2355
2356 tf_data.type = vpiSysTask;
2357 tf_data.tfname = "$monitoroff";
2358 tf_data.calltf = sys_monitoroff_calltf;
2359 tf_data.compiletf = sys_no_arg_compiletf;
2360 tf_data.sizetf = 0;
2361 tf_data.user_data = "$monitoroff";
2362 res = vpi_register_systf(&tf_data);
2363 vpip_make_systf_system_defined(res);
2364
2365 /*============================== fdisplay */
2366 tf_data.type = vpiSysTask;
2367 tf_data.tfname = "$fdisplay";
2368 tf_data.calltf = sys_display_calltf;
2369 tf_data.compiletf = sys_display_compiletf;
2370 tf_data.sizetf = 0;
2371 tf_data.user_data = "$fdisplay";
2372 res = vpi_register_systf(&tf_data);
2373 vpip_make_systf_system_defined(res);
2374
2375 tf_data.type = vpiSysTask;
2376 tf_data.tfname = "$fdisplayh";
2377 tf_data.calltf = sys_display_calltf;
2378 tf_data.compiletf = sys_display_compiletf;
2379 tf_data.sizetf = 0;
2380 tf_data.user_data = "$fdisplayh";
2381 res = vpi_register_systf(&tf_data);
2382 vpip_make_systf_system_defined(res);
2383
2384 tf_data.type = vpiSysTask;
2385 tf_data.tfname = "$fdisplayo";
2386 tf_data.calltf = sys_display_calltf;
2387 tf_data.compiletf = sys_display_compiletf;
2388 tf_data.sizetf = 0;
2389 tf_data.user_data = "$fdisplayo";
2390 res = vpi_register_systf(&tf_data);
2391 vpip_make_systf_system_defined(res);
2392
2393 tf_data.type = vpiSysTask;
2394 tf_data.tfname = "$fdisplayb";
2395 tf_data.calltf = sys_display_calltf;
2396 tf_data.compiletf = sys_display_compiletf;
2397 tf_data.sizetf = 0;
2398 tf_data.user_data = "$fdisplayb";
2399 res = vpi_register_systf(&tf_data);
2400 vpip_make_systf_system_defined(res);
2401
2402 /*============================== fwrite */
2403 tf_data.type = vpiSysTask;
2404 tf_data.tfname = "$fwrite";
2405 tf_data.calltf = sys_display_calltf;
2406 tf_data.compiletf = sys_display_compiletf;
2407 tf_data.sizetf = 0;
2408 tf_data.user_data = "$fwrite";
2409 res = vpi_register_systf(&tf_data);
2410 vpip_make_systf_system_defined(res);
2411
2412 tf_data.type = vpiSysTask;
2413 tf_data.tfname = "$fwriteh";
2414 tf_data.calltf = sys_display_calltf;
2415 tf_data.compiletf = sys_display_compiletf;
2416 tf_data.sizetf = 0;
2417 tf_data.user_data = "$fwriteh";
2418 res = vpi_register_systf(&tf_data);
2419 vpip_make_systf_system_defined(res);
2420
2421 tf_data.type = vpiSysTask;
2422 tf_data.tfname = "$fwriteo";
2423 tf_data.calltf = sys_display_calltf;
2424 tf_data.compiletf = sys_display_compiletf;
2425 tf_data.sizetf = 0;
2426 tf_data.user_data = "$fwriteo";
2427 res = vpi_register_systf(&tf_data);
2428 vpip_make_systf_system_defined(res);
2429
2430 tf_data.type = vpiSysTask;
2431 tf_data.tfname = "$fwriteb";
2432 tf_data.calltf = sys_display_calltf;
2433 tf_data.compiletf = sys_display_compiletf;
2434 tf_data.sizetf = 0;
2435 tf_data.user_data = "$fwriteb";
2436 res = vpi_register_systf(&tf_data);
2437 vpip_make_systf_system_defined(res);
2438
2439 /*============================== swrite */
2440 tf_data.type = vpiSysTask;
2441 tf_data.tfname = "$swrite";
2442 tf_data.calltf = sys_swrite_calltf;
2443 tf_data.compiletf = sys_swrite_compiletf;
2444 tf_data.sizetf = 0;
2445 tf_data.user_data = "$swrite";
2446 res = vpi_register_systf(&tf_data);
2447 vpip_make_systf_system_defined(res);
2448
2449 tf_data.type = vpiSysTask;
2450 tf_data.tfname = "$swriteh";
2451 tf_data.calltf = sys_swrite_calltf;
2452 tf_data.compiletf = sys_swrite_compiletf;
2453 tf_data.sizetf = 0;
2454 tf_data.user_data = "$swriteh";
2455 res = vpi_register_systf(&tf_data);
2456 vpip_make_systf_system_defined(res);
2457
2458 tf_data.type = vpiSysTask;
2459 tf_data.tfname = "$swriteo";
2460 tf_data.calltf = sys_swrite_calltf;
2461 tf_data.compiletf = sys_swrite_compiletf;
2462 tf_data.sizetf = 0;
2463 tf_data.user_data = "$swriteo";
2464 res = vpi_register_systf(&tf_data);
2465 vpip_make_systf_system_defined(res);
2466
2467 tf_data.type = vpiSysTask;
2468 tf_data.tfname = "$swriteb";
2469 tf_data.calltf = sys_swrite_calltf;
2470 tf_data.compiletf = sys_swrite_compiletf;
2471 tf_data.sizetf = 0;
2472 tf_data.user_data = "$swriteb";
2473 res = vpi_register_systf(&tf_data);
2474 vpip_make_systf_system_defined(res);
2475
2476 tf_data.type = vpiSysTask;
2477 tf_data.tfname = "$sformat";
2478 tf_data.calltf = sys_sformat_calltf;
2479 tf_data.compiletf = sys_sformat_compiletf;
2480 tf_data.sizetf = 0;
2481 tf_data.user_data = "$sformat";
2482 res = vpi_register_systf(&tf_data);
2483 vpip_make_systf_system_defined(res);
2484
2485 tf_data.type = vpiSysFunc;
2486 tf_data.sysfunctype = vpiStringFunc;
2487 tf_data.tfname = "$sformatf";
2488 tf_data.calltf = sys_display_calltf;
2489 tf_data.compiletf = sys_sformatf_compiletf;
2490 tf_data.sizetf = 0;
2491 tf_data.user_data = "$sformatf";
2492 res = vpi_register_systf(&tf_data);
2493 vpip_make_systf_system_defined(res);
2494
2495 /*============================ timeformat */
2496 tf_data.type = vpiSysTask;
2497 tf_data.tfname = "$timeformat";
2498 tf_data.calltf = sys_timeformat_calltf;
2499 tf_data.compiletf = sys_timeformat_compiletf;
2500 tf_data.sizetf = 0;
2501 tf_data.user_data = "$timeformat";
2502 res = vpi_register_systf(&tf_data);
2503 vpip_make_systf_system_defined(res);
2504
2505 tf_data.type = vpiSysTask;
2506 tf_data.tfname = "$printtimescale";
2507 tf_data.calltf = sys_printtimescale_calltf;
2508 tf_data.compiletf = sys_printtimescale_compiletf;
2509 tf_data.sizetf = 0;
2510 tf_data.user_data = "$printtimescale";
2511 res = vpi_register_systf(&tf_data);
2512 vpip_make_systf_system_defined(res);
2513
2514 /*============================ severity tasks */
2515 tf_data.type = vpiSysTask;
2516 tf_data.tfname = "$fatal";
2517 tf_data.calltf = sys_severity_calltf;
2518 tf_data.compiletf = sys_fatal_compiletf;
2519 tf_data.sizetf = 0;
2520 tf_data.user_data = "$fatal";
2521 res = vpi_register_systf(&tf_data);
2522 vpip_make_systf_system_defined(res);
2523
2524 tf_data.type = vpiSysTask;
2525 tf_data.tfname = "$error";
2526 tf_data.calltf = sys_severity_calltf;
2527 tf_data.compiletf = sys_display_compiletf;
2528 tf_data.sizetf = 0;
2529 tf_data.user_data = "$error";
2530 res = vpi_register_systf(&tf_data);
2531 vpip_make_systf_system_defined(res);
2532
2533 tf_data.type = vpiSysTask;
2534 tf_data.tfname = "$warning";
2535 tf_data.calltf = sys_severity_calltf;
2536 tf_data.compiletf = sys_display_compiletf;
2537 tf_data.sizetf = 0;
2538 tf_data.user_data = "$warning";
2539 res = vpi_register_systf(&tf_data);
2540 vpip_make_systf_system_defined(res);
2541
2542 tf_data.type = vpiSysTask;
2543 tf_data.tfname = "$info";
2544 tf_data.calltf = sys_severity_calltf;
2545 tf_data.compiletf = sys_display_compiletf;
2546 tf_data.sizetf = 0;
2547 tf_data.user_data = "$info";
2548 res = vpi_register_systf(&tf_data);
2549 vpip_make_systf_system_defined(res);
2550
2551 cb_data.reason = cbEndOfCompile;
2552 cb_data.time = 0;
2553 cb_data.cb_rtn = sys_end_of_compile;
2554 cb_data.user_data = "system";
2555 vpi_register_cb(&cb_data);
2556
2557 cb_data.reason = cbEndOfSimulation;
2558 cb_data.cb_rtn = sys_end_of_simulation;
2559 cb_data.user_data = "system";
2560 vpi_register_cb(&cb_data);
2561 }
2562