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