iverilog/vpi/sys_display.c

2496 lines
71 KiB
C
Raw Normal View History

/*
* Copyright (c) 1999 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CVS_IDENT
2007-04-18 04:40:20 +02:00
#ident "$Id: sys_display.c,v 1.79 2007/04/18 02:40:20 steve Exp $"
#endif
# include "vpi_config.h"
# include "vpi_user.h"
2006-08-12 05:38:12 +02:00
# include "sys_priv.h"
# include <assert.h>
# include <string.h>
# include <ctype.h>
2003-05-23 06:04:02 +02:00
# include <stdio.h>
# include <stdlib.h>
2003-02-06 18:40:02 +01:00
# include <math.h>
2003-05-23 06:04:02 +02:00
#define IS_MCD(mcd) !((mcd)>>31&1)
/* Printf wrapper to handle both MCD/FD */
static PLI_INT32 my_mcd_printf(PLI_UINT32 mcd, const char *fmt, ...)
{
int r = 0;
va_list ap;
va_start(ap, fmt);
if (IS_MCD(mcd)) {
r = vpi_mcd_vprintf(mcd, fmt, ap);
} else {
FILE *fp = vpi_get_file(mcd);
if (fp) r = vfprintf(fp, fmt, ap);
}
va_end(ap);
return r;
}
2002-05-31 06:26:54 +02:00
struct timeformat_info_s timeformat_info = { 0, 0, 0, 20 };
struct strobe_cb_info {
char*name;
int default_format;
vpiHandle scope;
vpiHandle*items;
unsigned nitems;
2003-08-26 05:51:05 +02:00
unsigned mcd;
};
int is_constant(vpiHandle obj)
{
if (vpi_get(vpiType, obj) == vpiConstant)
return vpiConstant;
if (vpi_get(vpiType, obj) == vpiParameter)
return vpiParameter;
return 0;
}
// The number of decimal digits needed to represent a
// nr_bits binary number is floor(nr_bits*log_10(2))+1,
// where log_10(2) = 0.30102999566398.... and I approximate
// this transcendental number as 146/485, to avoid the vagaries
// of floating-point. The smallest nr_bits for which this
// approximation fails is 2621,
// 2621*log_10(2)=789.9996, but (2621*146+484)/485=790 (exactly).
// In cases like this, all that happens is we allocate one
// unneeded char for the output. I add a "L" suffix to 146
// to make sure the computation is done as long ints, otherwise
// on a 16-bit int machine (allowed by ISO C) we would mangle
// this computation for bit-length of 224. I'd like to put
// in a test for nr_bits < LONG_MAX/146, but don't know how
// to fail, other than crashing.
//
// In an April 2000 thread in comp.unix.programmer, with subject
// "integer -> string", I <LRDoolittle@lbl.gov> give the 28/93
// approximation, but overstate its accuracy: that version first
// fails when the number of bits is 289, not 671.
//
// This result does not include space for a trailing '\0', if any.
//
inline static int calc_dec_size(int nr_bits, int is_signed)
{
int r;
if (is_signed) --nr_bits;
r = (nr_bits * 146L + 484) / 485;
if (is_signed) ++r;
return r;
}
static int vpi_get_dec_size(vpiHandle item)
{
return calc_dec_size(
vpi_get(vpiSize, item),
vpi_get(vpiSigned, item)==1
);
}
static void array_from_iterator(struct strobe_cb_info*info, vpiHandle argv)
{
2001-03-18 01:31:32 +01:00
if (argv) {
vpiHandle item;
unsigned nitems = 1;
vpiHandle*items = malloc(sizeof(vpiHandle));
items[0] = vpi_scan(argv);
2002-07-23 04:41:15 +02:00
if (items[0] == 0) {
free(items);
info->nitems = 0;
info->items = 0;
return;
}
2001-03-18 01:31:32 +01:00
for (item = vpi_scan(argv) ; item ; item = vpi_scan(argv)) {
items = realloc(items, (nitems+1)*sizeof(vpiHandle));
items[nitems] = item;
nitems += 1;
}
info->nitems = nitems;
info->items = items;
2001-03-18 01:31:32 +01:00
} else {
info->nitems = 0;
info->items = 0;
}
}
/*
* This function writes the time value into the mcd target with the
* proper format. The mcd is the destination file.
*
* The fsize is the width of the field to use. Normally, this is -1 to
* reflect the format string "%t". It may also be 0 for the format
* string "%0t". This formatter also allows for the nonstandard use of
* positive values to enforce a with to override the width given in
* the $timeformat system task.
*
* The value argument is the time value as a decimal string. There are
* no leading zeros in this string, and the units of the value are
* given in the units argument.
*/
static void format_time(unsigned mcd, int fsize,
const char*value, int time_units)
2002-05-31 06:26:54 +02:00
{
char buf[256];
const char*cp;
char*bp, *start_address;
int idx;
2003-12-19 02:27:10 +01:00
unsigned int fusize;
int fraction_chars, fraction_pad, value_chop, whole_fill;
2002-05-31 06:26:54 +02:00
/* This is the format precision expressed as the power of 10
of the desired precision. The following code uses this
format to be consistent with the units specifications. */
int format_precision = timeformat_info.units - timeformat_info.prec;
2002-05-31 06:26:54 +02:00
/* If the fsize is <0, then use the value from the
$timeformat. If the fsize is >=0, then it overrides the
$timeformat value. */
2003-12-19 02:27:10 +01:00
fusize = (fsize<0) ? timeformat_info.width : (unsigned) fsize;
2002-05-31 06:26:54 +02:00
2003-12-19 02:27:10 +01:00
assert(fusize < (sizeof buf - 1));
/* This is the number of characters to the right of the
decimal point. This is defined completely by the
timeformat. It is legal for the precision to be larger then
the units, and in this case there will be no fraction_chars
at all. */
fraction_chars = timeformat_info.units - format_precision;
if (fraction_chars < 0)
fraction_chars = 0;
/* This is the number of zeros I must add to the value to get
the desired precision within the fraction. If this value is
greater then 0, the value does not have enough characters,
so I will be adding zeros. */
fraction_pad = time_units - format_precision;
if (fraction_pad < 0)
fraction_pad = 0;
if (fraction_pad > fraction_chars)
fraction_pad = fraction_chars;
/* This is the number of characters of excess precision in the
supplied value. This many characters are chopped from the
least significant end. */
value_chop = format_precision - time_units;
if (value_chop < 0)
value_chop = 0;
/* This is the number of zeros that I must add to the integer
part of the output string to pad the value out to the
desired units. This will only have a non-zero value if the
units of the value is greater then the desired units.
Detect the special case where the value is 0. In this case,
do not do any integer filling ever. The output should be
padded with blanks in that case. */
whole_fill = time_units - timeformat_info.units;
if (whole_fill < 0)
whole_fill = 0;
if (strcmp(value,"0") == 0)
whole_fill = 0;
/* This is the expected start address of the output. It
2003-12-19 02:27:10 +01:00
accounts for the f(u)size value that is chosen. The output
will be filled out to complete the buffer. */
2003-12-19 02:27:10 +01:00
if (fusize == 0)
start_address = buf;
else
2003-12-19 02:27:10 +01:00
start_address = buf + sizeof buf - fusize - 1;
/* Now start the character pointers, ready to start copying
the value into the format. */
cp = value + strlen(value);
if (value_chop > (cp - value))
cp = value;
else
cp -= value_chop;
2002-05-31 06:26:54 +02:00
bp = buf + sizeof buf;
*--bp = 0;
/* Write the suffix. */
2002-05-31 06:26:54 +02:00
bp -= strlen(timeformat_info.suff);
strcpy(bp, timeformat_info.suff);
/* Write the padding needed to fill out the fraction. */
for (idx = 0 ; idx < fraction_pad ; idx += 1)
*--bp = '0';
/* Subtract the pad from the needed chars. */
assert(fraction_pad <= fraction_chars);
fraction_chars -= fraction_pad;
fraction_pad = 0;
2002-05-31 06:26:54 +02:00
/* Write the fraction chars. */
for (idx = 0 ; idx < fraction_chars ; idx += 1) {
if (cp > value)
*--bp = *--cp;
else
*--bp = '0';
assert(cp >= value);
}
2002-05-31 06:26:54 +02:00
/* Write the decimal point, if needed. */
if (timeformat_info.prec > 0)
*--bp = '.';
/* Fill the gap between the value and the decimal point. */
for (idx = 0 ; idx < whole_fill ; idx += 1)
*--bp = '0';
/* Write the integer part of the value. */
while (cp > value) {
*--bp = *--cp;
}
/* Fill the leading characters to make up the desired
width. This may require a '0' if the last character
written was the decimal point. This may also require a '0'
if there are no other characters at all in the ouput. */
2003-12-19 02:27:10 +01:00
if (fusize > 0) {
while (bp > start_address) {
if (*bp == '.' || strcmp(bp, timeformat_info.suff) == 0)
*--bp = '0';
else
*--bp = ' ';
2002-05-31 06:26:54 +02:00
}
} else {
if (*bp == '.' || strcmp(bp, timeformat_info.suff) == 0)
*--bp = '0';
2002-05-31 06:26:54 +02:00
}
2002-05-31 06:26:54 +02:00
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%s", bp);
2002-05-31 06:26:54 +02:00
}
1999-10-08 19:47:49 +02:00
2003-02-06 18:40:02 +01:00
static void format_time_real(unsigned mcd, int fsize,
double value, int time_units)
{
int width;
2003-02-06 18:40:02 +01:00
/* The time_units is the units of the current scope, and also
of the value. If the scope units are different from the
format specifier units, then scale the value. */
if (time_units != timeformat_info.units)
value *= pow(10.0, time_units - timeformat_info.units);
if (fsize == -1) {
width = timeformat_info.width - strlen(timeformat_info.suff);
} else {
width = fsize;
}
2003-02-06 18:40:02 +01:00
/* The timeformat_info.prec is the number of digits after the
decimal point, no matter what the units. */
my_mcd_printf(mcd, "%*.*f%s", width, timeformat_info.prec, value,
timeformat_info.suff);
2003-02-06 18:40:02 +01:00
}
static void format_strength(unsigned int mcd, s_vpi_value*value)
{
char str[4];
2003-04-20 04:49:07 +02:00
vpip_format_strength(str, value);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%s", str);
}
static void format_error_msg(const char*msg, int leading_zero,
int fsize, int ffsize, char fmt)
{
if ((fsize < 0) && (ffsize < 0)) {
if (leading_zero > 0)
vpi_printf("\nERROR: %s: %%0%c\n", msg, fmt);
else
vpi_printf("\nERROR: %s: %%%c\n", msg, fmt);
} else if (ffsize < 0) {
if ((leading_zero > 0) && (fsize > 0))
vpi_printf("\nERROR: %s: %%0%d%c\n", msg,
fsize, fmt);
else
vpi_printf("\nERROR: %s: %%%d%c\n", msg,
fsize, fmt);
} else {
vpi_printf("\nERROR: %s: %%%d.%d%c\n", msg,
fsize, ffsize, fmt);
}
}
/*
* The format_str uses this function to do the special job of
* interpreting the next item depending on the format code. The caller
* has already parsed the %x.yf format string.
*
* The return code is the number of arguments that were consumed.
*/
static int format_str_char(vpiHandle scope, unsigned int mcd,
int leading_zero, int fsize, int ffsize,
char fmt, int argc, vpiHandle*argv, int idx)
{
s_vpi_value value;
int use_count = 0;
/* Time units of the current scope. */
int time_units = vpi_get(vpiTimeUnit, scope);
switch (fmt) {
case 0:
return 0;
case '%':
if (fsize != -1 && ffsize != -1) {
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
fsize = -1;
ffsize = -1;
}
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%%");
use_count = 0;
break;
// new Verilog 2001 format specifiers...
case 'l':
case 'L':
case 'u':
case 'U':
case 'z':
case 'Z':
format_error_msg("Unsupported format", leading_zero,
fsize, ffsize, fmt);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%c", fmt);
use_count = 0;
break;
default:
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%c", fmt);
break;
/* Print numeric value in binary/hex/octal format. */
case 'b':
case 'B':
case 'h':
case 'H':
case 'o':
case 'O':
case 'x':
case 'X':
if (ffsize != -1) {
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
fsize = -1;
}
if (idx >= argc) {
format_error_msg("Missing Argument", leading_zero,
fsize, ffsize, fmt);
return 0;
}
switch (fmt) {
case 'b':
case 'B':
value.format = vpiBinStrVal;
break;
case 'h':
case 'H':
case 'x':
case 'X':
value.format = vpiHexStrVal;
break;
case 'o':
case 'O':
value.format = vpiOctStrVal;
break;
}
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
{ char* value_str = value.value.str;
if (leading_zero==1){
// Strip away all leading zeros from string
2003-12-19 02:27:10 +01:00
unsigned int i=0;
while(i< (strlen(value_str)-1) && value_str[i]=='0')
i++;
value_str += i;
}
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%*s", fsize, value_str);
}
use_count = 1;
break;
/* Print character */
case 'c':
case 'C':
if (fsize != -1 && ffsize != -1) {
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
fsize = -1;
ffsize = -1;
}
if (idx >= argc) {
format_error_msg("Missing Argument", leading_zero,
fsize, ffsize, fmt);
return 0;
}
value.format = vpiStringVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%c", value.value.str[strlen(value.value.str)-1]);
use_count = 1;
break;
/* Print numeric value is decimal integer format. */
case 'd':
case 'D':
if (ffsize != -1) {
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
fsize = -1;
}
if (idx >= argc) {
format_error_msg("Missing Argument", leading_zero,
fsize, ffsize, fmt);
return 0;
}
value.format = vpiDecStrVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
if (fsize==-1){
2003-03-05 03:58:04 +01:00
// simple %d parameter, or %0d parameter.
// Size is now determined by the width
2003-03-05 03:58:04 +01:00
// of the vector or integer. If %0d, then
// set the size to 0 so that the minimum
// size is used.
fsize = (leading_zero==1)
? 0
: vpi_get_dec_size(argv[idx]);
}
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%*s", fsize, value.value.str);
use_count = 1;
break;
case 'e':
case 'E':
if (idx >= argc) {
format_error_msg("Missing Argument", leading_zero,
fsize, ffsize, fmt);
return 0;
}
value.format = vpiRealVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
2007-04-18 04:40:20 +02:00
if (fmt == 'e')
my_mcd_printf(mcd, "%*.*e", fsize, ffsize, value.value.real);
else
my_mcd_printf(mcd, "%*.*E", fsize, ffsize, value.value.real);
use_count = 1;
break;
case 'f':
case 'F':
if (idx >= argc) {
format_error_msg("Missing Argument", leading_zero,
fsize, ffsize, fmt);
return 0;
}
value.format = vpiRealVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%*.*f", fsize, ffsize, value.value.real);
use_count = 1;
break;
case 'g':
case 'G':
if (idx >= argc) {
format_error_msg("Missing Argument", leading_zero,
fsize, ffsize, fmt);
return 0;
}
value.format = vpiRealVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
2007-04-18 04:40:20 +02:00
if (fmt == 'g')
my_mcd_printf(mcd, "%*.*g", fsize, ffsize, value.value.real);
else
my_mcd_printf(mcd, "%*.*G", fsize, ffsize, value.value.real);
use_count = 1;
break;
/* Print the current scope. */
case 'm':
case 'M':
if (ffsize != -1) {
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
fsize = -1;
}
if (fsize == -1)
fsize = 0;
assert(scope);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%*s",
fsize,
vpi_get_str(vpiFullName, scope));
break;
/* Print vector as a string value. */
case 's':
case 'S':
if (ffsize != -1) {
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
fsize = -1;
}
value.format = vpiStringVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
if (fsize==-1){
fsize = (vpi_get(vpiSize, argv[idx]) + 7) / 8;
my_mcd_printf(mcd, "%*s", fsize, value.value.str);
} else {
char* value_str = value.value.str;
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%*s", fsize, value_str);
}
use_count = 1;
break;
case 't':
case 'T':
if (ffsize != -1) {
format_error_msg("Illegal format", leading_zero,
fsize, ffsize, fmt);
fsize = -1;
}
2003-02-06 18:40:02 +01:00
if (idx >= argc) {
format_error_msg("Missing Argument", leading_zero,
fsize, ffsize, fmt);
2003-02-06 18:40:02 +01:00
return 0;
}
if (is_constant(argv[idx])
2003-02-06 18:40:02 +01:00
&& (vpi_get(vpiConstType, argv[idx]) == vpiRealConst)) {
value.format = vpiRealVal;
vpi_get_value(argv[idx], &value);
format_time_real(mcd, fsize, value.value.real, time_units);
} else {
value.format = vpiDecStrVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
format_time(mcd, fsize, value.value.str, time_units);
}
use_count = 1;
break;
case 'v':
case 'V':
value.format = vpiStrengthVal;
vpi_get_value(argv[idx], &value);
if (value.format == vpiSuppressVal){
format_error_msg("Incompatible value", leading_zero,
fsize, ffsize, fmt);
return 1;
}
format_strength(mcd, &value);
use_count = 1;
break;
}
return use_count;
}
/*
* If $display discovers a string as a parameter, this function is
* called to process it as a format string. I need the argv handle as
* well so that I can look for arguments as I move forward through the
* string.
*/
static int format_str(vpiHandle scope, unsigned int mcd,
char*format, int argc, vpiHandle*argv)
1999-11-06 23:16:50 +01:00
{
char buf[256], fmt[256];
1999-11-06 23:16:50 +01:00
char*cp = fmt;
int idx;
assert(format);
/*
* Copy format out of value buffer since it will be
* clobbered by successive vpi_get_value() calls.
*/
strncpy(fmt, format, sizeof fmt - 1);
fmt[sizeof fmt - 1] = 0;
1999-11-06 23:16:50 +01:00
idx = 0;
while (*cp) {
size_t cnt = strcspn(cp, "%");
1999-11-06 23:16:50 +01:00
if (cnt > 0) {
/* String of not-escape characters... */
1999-11-06 23:16:50 +01:00
if (cnt >= sizeof buf)
cnt = sizeof buf - 1;
strncpy(buf, cp, cnt);
buf[cnt] = 0;
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%s", buf);
1999-11-06 23:16:50 +01:00
cp += cnt;
} else {
2003-03-05 03:58:04 +01:00
int leading_zero = -1, ljust = 1, fsize = -1, ffsize = -1;
1999-11-06 23:16:50 +01:00
assert(*cp == '%');
1999-11-06 23:16:50 +01:00
cp += 1;
2003-03-05 03:58:04 +01:00
if (*cp == '-') {
ljust=-1;
cp += 1;
}
if (*cp == '0')
leading_zero=1;
if (isdigit((int)*cp))
2003-03-05 03:58:04 +01:00
fsize = ljust * strtoul(cp, &cp, 10);
2001-11-02 06:56:47 +01:00
if (*cp == '.') {
cp += 1;
ffsize = strtoul(cp, &cp, 10);
}
1999-11-06 23:16:50 +01:00
idx += format_str_char(scope, mcd, leading_zero,
fsize, ffsize, *cp,
argc, argv, idx);
cp += 1;
1999-11-06 23:16:50 +01:00
}
}
return idx;
}
2004-01-24 00:40:44 +01:00
static void do_display_numeric(unsigned int mcd,
struct strobe_cb_info*info,
vpiHandle item)
{
int size;
s_vpi_value value;
value.format = info->default_format;
vpi_get_value(item, &value);
switch(info->default_format){
case vpiDecStrVal:
size = vpi_get_dec_size(item);
my_mcd_printf(mcd, "%*s", size, value.value.str);
break;
default:
my_mcd_printf(mcd, "%s", value.value.str);
}
}
static void do_display(unsigned int mcd, struct strobe_cb_info*info)
{
char*fmt;
1999-11-06 23:16:50 +01:00
s_vpi_value value;
2003-12-19 02:27:10 +01:00
unsigned int idx;
1999-11-06 23:16:50 +01:00
for (idx = 0 ; idx < info->nitems ; idx += 1) {
vpiHandle item = info->items[idx];
switch (vpi_get(vpiType, item)) {
case 0:
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, " ");
1999-11-06 23:16:50 +01:00
break;
case vpiConstant:
case vpiParameter:
1999-11-06 23:16:50 +01:00
if (vpi_get(vpiConstType, item) == vpiStringConst) {
value.format = vpiStringVal;
vpi_get_value(item, &value);
fmt = strdup(value.value.str);
idx += format_str(info->scope, mcd, fmt,
1999-11-06 23:16:50 +01:00
info->nitems-idx-1,
1999-11-07 03:25:07 +01:00
info->items+idx+1);
free(fmt);
1999-11-06 23:16:50 +01:00
} else {
2004-01-24 00:40:44 +01:00
do_display_numeric(mcd, info, item);
1999-11-06 23:16:50 +01:00
}
break;
case vpiNet:
case vpiReg:
case vpiIntegerVar:
case vpiMemoryWord:
2004-01-24 00:40:44 +01:00
do_display_numeric(mcd, info, item);
1999-11-06 23:16:50 +01:00
break;
case vpiTimeVar:
value.format = vpiTimeVal;
vpi_get_value(item, &value);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%20u", value.value.time->low);
1999-11-06 23:16:50 +01:00
break;
case vpiRealVar:
value.format = vpiRealVal;
vpi_get_value(item, &value);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%f", value.value.real);
break;
2003-02-01 06:49:13 +01:00
case vpiSysFuncCall: {
char*tmp = vpi_get_str(vpiName, item);
vpiHandle scope = vpi_handle(vpiScope, item);
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
if (strcmp(tmp,"$time") == 0 ||
strcmp(tmp,"$simtime") == 0) {
2003-02-01 06:49:13 +01:00
value.format = vpiTimeVal;
vpi_get_value(item, &value);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%20u", value.value.time->low);
2003-02-01 06:49:13 +01:00
} else if (strcmp(tmp,"$realtime") == 0) {
int time_units = vpi_get(vpiTimeUnit, scope);
int time_prec = vpi_get(vpiTimePrecision, 0);
int use_prec = time_units - time_prec;
if (use_prec < 0)
use_prec = 0;
value.format = vpiRealVal;
vpi_get_value(item, &value);
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "%0.*f", use_prec,
2003-02-01 06:49:13 +01:00
value.value.real);
} else {
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "<%s>", tmp);
2003-02-01 06:49:13 +01:00
}
break;
}
1999-11-06 23:16:50 +01:00
default:
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "?");
1999-11-06 23:16:50 +01:00
break;
}
}
}
static int get_default_format(char *name)
{
int default_format;
switch(name[ strlen(name)-1 ]){
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
// writE/strobE or monitoR or displaY/fdisplaY or sformaT
case 'e':
case 'r':
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
case 't':
case 'y': default_format = vpiDecStrVal; break;
case 'h': default_format = vpiHexStrVal; break;
case 'o': default_format = vpiOctStrVal; break;
case 'b': default_format = vpiBinStrVal; break;
default:
assert(0);
}
return default_format;
}
static PLI_INT32 sys_display_calltf(PLI_BYTE8*name)
{
struct strobe_cb_info*info;
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
info = vpi_get_userdata(sys);
if (info == 0) {
vpiHandle scope = vpi_handle(vpiScope, sys);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
assert(scope);
info = malloc(sizeof (struct strobe_cb_info));
info->default_format = get_default_format(name);
info->scope = scope;
array_from_iterator(info, argv);
vpi_put_userdata(sys, info);
}
do_display(1, info);
if (strncmp(name,"$display",8) == 0)
2003-05-23 06:04:02 +02:00
my_mcd_printf(1, "\n");
return 0;
}
/*
* The strobe implementation takes the parameter handles that are
* passed to the calltf and puts them in to an array for safe
* keeping. That array (and other bookkeeping) is passed, via the
* struct_cb_info object, to the REadOnlySych function strobe_cb,
* where it is use to perform the actual formatting and printing.
*/
static PLI_INT32 strobe_cb(p_cb_data cb)
{
struct strobe_cb_info*info = (struct strobe_cb_info*)cb->user_data;
2003-08-26 05:51:05 +02:00
do_display(info->mcd, info);
my_mcd_printf(info->mcd, "\n");
1999-11-06 23:16:50 +01:00
free(info->name);
free(info->items);
free(info);
return 0;
}
static PLI_INT32 sys_strobe_calltf(PLI_BYTE8*name)
{
struct t_cb_data cb;
struct t_vpi_time time;
1999-11-06 23:16:50 +01:00
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle scope = vpi_handle(vpiScope, sys);
1999-11-06 23:16:50 +01:00
vpiHandle argv = vpi_iterate(vpiArgument, sys);
struct strobe_cb_info*info = calloc(1, sizeof(struct strobe_cb_info));
2003-08-26 05:51:05 +02:00
if(name[1] == 'f') {
vpiHandle item = vpi_scan(argv);
int type;
s_vpi_value value;
if (item == 0) {
vpi_printf("%s: mcd parameter missing.\n", name);
return 0;
}
type = vpi_get(vpiType, item);
switch (type) {
case vpiReg:
case vpiRealVar:
case vpiIntegerVar:
case vpiNet:
break;
case vpiConstant:
switch (vpi_get(vpiConstType, item)) {
case vpiDecConst:
case vpiBinaryConst:
case vpiOctConst:
case vpiHexConst:
break;
default:
vpi_printf("ERROR: %s mcd parameter must be integral", name);
vpi_printf(", got vpiType=vpiConstant, vpiConstType=%d\n",
vpi_get(vpiConstType, item));
vpi_free_object(argv);
return 0;
}
break;
2003-08-26 05:51:05 +02:00
default:
vpi_printf("ERROR: %s mcd parameter must be integral", name);
vpi_printf(", got vpiType=%d\n", type);
vpi_free_object(argv);
return 0;
}
value.format = vpiIntVal;
vpi_get_value(item, &value);
info->mcd = value.value.integer;
} else {
info->mcd = 1;
}
1999-11-06 23:16:50 +01:00
array_from_iterator(info, argv);
1999-11-06 23:16:50 +01:00
info->name = strdup(name);
info->default_format = get_default_format(name);
info->scope= scope;
time.type = vpiSimTime;
time.low = 0;
time.high = 0;
cb.reason = cbReadOnlySynch;
cb.cb_rtn = strobe_cb;
cb.time = &time;
cb.obj = 0;
cb.value = 0;
1999-11-06 23:16:50 +01:00
cb.user_data = (char*)info;
vpi_register_cb(&cb);
return 0;
}
1999-11-07 03:25:07 +01:00
/*
* The $monitor system task works by managing these static variables,
* and the cbValueChange callbacks associated with registers and
* nets. Note that it is proper to keep the state in static variables
* because there can only be one monitor at a time pending (even
* though that monitor may be watching many variables).
*/
static struct strobe_cb_info monitor_info = { 0, 0, 0, 0, 0 };
1999-11-07 03:25:07 +01:00
static vpiHandle *monitor_callbacks = 0;
static int monitor_scheduled = 0;
static int monitor_enabled = 1;
1999-11-07 03:25:07 +01:00
static PLI_INT32 monitor_cb_2(p_cb_data cb)
1999-11-07 03:25:07 +01:00
{
do_display(1, &monitor_info);
1999-11-07 03:25:07 +01:00
vpi_printf("\n");
monitor_scheduled = 0;
return 0;
}
/*
* The monitor_cb_1 callback is called when an even occurs somewhere
* in the simulation. All this function does is schedule the actual
* display to occur in a ReadOnlySync callback. The monitor_scheduled
* flag is used to allow only one monitor strobe to be scheduled.
*/
static PLI_INT32 monitor_cb_1(p_cb_data cause)
1999-11-07 03:25:07 +01:00
{
struct t_cb_data cb;
struct t_vpi_time time;
if (monitor_enabled == 0) return 0;
1999-11-07 03:25:07 +01:00
if (monitor_scheduled) return 0;
/* This this action caused the first trigger, then schedule
the monitor to happen at the end of the time slice and mark
it as scheduled. */
monitor_scheduled += 1;
time.type = vpiSimTime;
time.low = 0;
time.high = 0;
cb.reason = cbReadOnlySynch;
cb.cb_rtn = monitor_cb_2;
cb.time = &time;
cb.obj = 0;
cb.value = 0;
1999-11-07 03:25:07 +01:00
vpi_register_cb(&cb);
return 0;
}
static PLI_INT32 sys_monitor_calltf(PLI_BYTE8*name)
1999-10-29 05:37:22 +02:00
{
1999-11-07 03:25:07 +01:00
unsigned idx;
1999-10-29 05:37:22 +02:00
struct t_cb_data cb;
struct t_vpi_time time;
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle scope = vpi_handle(vpiScope, sys);
1999-10-29 05:37:22 +02:00
vpiHandle argv = vpi_iterate(vpiArgument, sys);
1999-11-07 03:25:07 +01:00
/* If there was a previous $monitor, then remove the calbacks
related to it. */
1999-11-07 03:25:07 +01:00
if (monitor_callbacks) {
for (idx = 0 ; idx < monitor_info.nitems ; idx += 1)
if (monitor_callbacks[idx])
vpi_remove_cb(monitor_callbacks[idx]);
free(monitor_callbacks);
monitor_callbacks = 0;
free(monitor_info.items);
free(monitor_info.name);
monitor_info.items = 0;
monitor_info.nitems = 0;
monitor_info.name = 0;
}
/* Make an array of handles from the argument list. */
1999-11-07 03:25:07 +01:00
array_from_iterator(&monitor_info, argv);
monitor_info.name = strdup(name);
monitor_info.default_format = get_default_format(name);
monitor_info.scope = scope;
1999-11-07 03:25:07 +01:00
/* Attach callbacks to all the parameters that might change. */
1999-11-07 03:25:07 +01:00
monitor_callbacks = calloc(monitor_info.nitems, sizeof(vpiHandle));
1999-10-29 05:37:22 +02:00
time.type = vpiSuppressTime;
1999-11-07 03:25:07 +01:00
cb.reason = cbValueChange;
cb.cb_rtn = monitor_cb_1;
cb.time = &time;
cb.value = NULL;
1999-11-07 03:25:07 +01:00
for (idx = 0 ; idx < monitor_info.nitems ; idx += 1) {
switch (vpi_get(vpiType, monitor_info.items[idx])) {
case vpiNet:
case vpiReg:
case vpiIntegerVar:
2003-02-10 06:20:48 +01:00
case vpiRealVar:
/* Monitoring reg and net values involves setting
a callback for value changes. pass the storage
pointer for the callback itself as user_data so
that the callback can refresh itself. */
cb.user_data = (char*)(monitor_callbacks+idx);
1999-11-07 03:25:07 +01:00
cb.obj = monitor_info.items[idx];
monitor_callbacks[idx] = vpi_register_cb(&cb);
break;
}
}
1999-10-29 05:37:22 +02:00
/* When the $monitor is called, it schedules a first display
for the end of the current time, like a $strobe. */
monitor_cb_1(0);
return 0;
}
static PLI_INT32 sys_monitoron_calltf(PLI_BYTE8*name)
{
monitor_enabled = 1;
monitor_cb_1(0);
return 0;
}
static PLI_INT32 sys_monitoroff_calltf(PLI_BYTE8*name)
{
monitor_enabled = 0;
1999-10-29 05:37:22 +02:00
return 0;
}
/* Implement $fdisplay and $fwrite.
* Perhaps this could be merged into sys_display_calltf.
*/
static PLI_INT32 sys_fdisplay_calltf(PLI_BYTE8*name)
{
struct strobe_cb_info info;
unsigned int mcd;
int type;
s_vpi_value value;
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
2001-11-02 06:56:47 +01:00
vpiHandle scope = vpi_handle(vpiScope, sys);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
vpiHandle item = vpi_scan(argv);
if (item == 0) {
vpi_printf("%s: mcd parameter missing.\n", name);
return 0;
}
type = vpi_get(vpiType, item);
switch (type) {
case vpiReg:
case vpiRealVar:
case vpiIntegerVar:
2003-08-03 05:54:02 +02:00
case vpiNet:
break;
case vpiConstant:
switch (vpi_get(vpiConstType, item)) {
case vpiDecConst:
case vpiBinaryConst:
case vpiOctConst:
case vpiHexConst:
break;
default:
vpi_printf("ERROR: %s mcd parameter must be integral", name);
vpi_printf(", got vpiType=vpiConstant, vpiConstType=%d\n",
vpi_get(vpiConstType, item));
vpi_free_object(argv);
return 0;
}
break;
default:
vpi_printf("ERROR: %s mcd parameter must be integral", name);
vpi_printf(", got vpiType=%d\n", type);
vpi_free_object(argv);
return 0;
}
value.format = vpiIntVal;
vpi_get_value(item, &value);
mcd = value.value.integer;
2001-11-02 06:56:47 +01:00
assert(scope);
info.default_format = get_default_format(name);
2001-11-02 06:56:47 +01:00
info.scope = scope;
array_from_iterator(&info, argv);
do_display(mcd, &info);
free(info.items);
if (strncmp(name,"$fdisplay",9) == 0)
2003-05-23 06:04:02 +02:00
my_mcd_printf(mcd, "\n");
return 0;
}
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
/* Build the format using the variables that control how the item will
* be printed. This is used in error messages and directly by the e/f/g
* format codes (minus the enclosing <>). The user needs to free the
* returned string. */
static char * format_as_string(int ljust, int plus, int ld_zero, int width,
int prec, char fmt)
{
char buf[256];
unsigned int size = 0;
buf[size++] = '<';
buf[size++] = '%';
if (ljust == 1) buf[size++] = '-';
if (plus == 1) buf[size++] = '+';
if (ld_zero == 1) buf[size++] = '0';
if (width != -1)
size += sprintf(&buf[size], "%d", width);
if (prec != -1)
size += sprintf(&buf[size], ".%d", prec);
buf[size++] = fmt;
/* Do not change this without also changing the e/f/g format code below! */
buf[size++] = '>';
buf[size] = '\0';
return strdup(buf);
}
static void get_time(char *rtn, const char *value, int prec,
PLI_INT32 time_units)
{
int head, tail;
int shift = time_units - timeformat_info.units;
/* Strip any leading zeros, but leave a single zero. */
while (value[0] == '0' && value[1] != '\0') value += 1;
/* We need to scale the number up. */
if (shift >= 0) {
strcpy(rtn, value);
/* Shift only non-zero values. */
while (shift > 0 && value[0] != '0') {
strcat(rtn, "0");
shift -= 1;
}
if (prec > 0) strcat(rtn, ".");
while(prec > 0) {
strcat(rtn, "0");
prec -= 1;
}
/* We need to scale the number down. */
} else {
head = strlen(value) + shift;
/* We have digits to the left of the decimal point. */
if (head > 0) {
strncpy(rtn, value, head);
strcat(rtn, ".");
strncat(rtn, &value[head], prec);
tail = prec + shift;
while (tail > 0) {
strcat(rtn, "0");
tail -= 1;
}
/* All digits are to the right of the decimal point. */
} else {
strcpy(rtn, "0.");
/* Add leading zeros as needed. */
head = -shift - 1;
if (head > prec) head = prec;
while (head > 0) {
strcat(rtn, "0");
head -= 1;
}
/* Add digits from the value if they fit. */
tail = prec + shift + 1;
strncat(rtn, value, tail);
/* Add trailing zeros to fill out the precision. */
tail = prec + shift + 1 - strlen(value);
while (tail > 0) {
strcat(rtn, "0");
tail -= 1;
}
}
}
strcat(rtn, timeformat_info.suff);
}
static void get_time_real(char *rtn, double value, int prec,
PLI_INT32 time_units)
{
/* Scale the value if it's time units differ from the format units. */
if (time_units != timeformat_info.units) {
value *= pow(10.0, time_units - timeformat_info.units);
}
sprintf(rtn, "%0.*f%s", prec, value, timeformat_info.suff);
}
static unsigned int get_format_char(char **rtn, int ljust, int plus,
int ld_zero, int width, int prec,
char fmt, struct strobe_cb_info *info,
unsigned int *idx)
{
s_vpi_value value;
char *result, *fmtb;
unsigned int size;
unsigned int max_size = 256; /* The initial size of the buffer. */
/* The default return value is the full format. */
result = malloc(max_size*sizeof(char));
fmtb = format_as_string(ljust, plus, ld_zero, width, prec, fmt);
strcpy(result, fmtb);
switch (fmt) {
case '%':
if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 ||
prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
strcpy(result, "%");
size = strlen(result) + 1;
break;
case 'b':
case 'B':
case 'o':
case 'O':
case 'h':
case 'H':
case 'x':
case 'X':
*idx += 1;
if (plus != 0 || prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
switch (fmt) {
case 'b':
case 'B':
value.format = vpiBinStrVal;
break;
case 'o':
case 'O':
value.format = vpiOctStrVal;
break;
case 'h':
case 'H':
case 'x':
case 'X':
value.format = vpiHexStrVal;
break;
}
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
char *cp;
/* If a width was not given use a width of zero. */
if (width == -1) width = 0;
/* Strip the leading zeros. */
cp = value.value.str;
if (ld_zero == 1) {
while (*cp == '0') cp++;
}
/* If the (default buffer is too small make it big enough. */
size = strlen(cp) + 1;
if (size > max_size) result = realloc(result, size*sizeof(char));
if (ljust == 0) sprintf(result, "%*s", width, cp);
else sprintf(result, "%-*s", width, cp);
}
}
size = strlen(result) + 1;
break;
case 'c':
case 'C':
*idx += 1;
if (plus != 0 || prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
value.format = vpiStringVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
/* If a width was not given use a width of zero. */
if (width == -1) width = 0;
if (ljust == 0) sprintf(result, "%*c", width,
value.value.str[strlen(value.value.str)-1]);
else sprintf(result, "%-*c", width,
value.value.str[strlen(value.value.str)-1]);
}
}
size = strlen(result) + 1;
break;
case 'd':
case 'D':
*idx += 1;
if (prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
value.format = vpiDecStrVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
char *tbuf;
/* If a width was not given use the default unless we have a
* leading zero (width of zero). */
if (width == -1) {
width = (ld_zero == 1) ? 0: vpi_get_dec_size(info->items[*idx]);
}
size = strlen(value.value.str) + 1 + plus;
/* Insert a plus sign if needed. */
tbuf = malloc(size*sizeof(char));
if (plus == 1 && value.value.str[0] != '-') {
tbuf[0] = '+';
strcpy(&tbuf[1], value.value.str);
} else strcpy(&tbuf[0], value.value.str);
/* If the (default buffer is too small make it big enough. */
if (size > max_size) result = realloc(result, size*sizeof(char));
if (ljust == 0) sprintf(result, "%*s", width, tbuf);
else sprintf(result, "%-*s", width, tbuf);
free(tbuf);
}
}
size = strlen(result) + 1;
break;
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
*idx += 1;
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
value.format = vpiRealVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
char *cp = fmtb;
if (fmt == 'F') {
while (*cp != 'F') cp++;
*cp = 'f';
}
while (*cp != '>') cp++;
*cp = '\0';
sprintf(result, fmtb+1, value.value.real);
}
}
size = strlen(result) + 1;
break;
/* This Verilog format specifier is not currently supported!
* vpiCell and vpiLibrary need to be implemented first. */
case 'l':
case 'L':
vpi_printf("WARNING: %%%c currently unsupported %s%s.\n", fmt,
info->name, fmtb);
size = strlen(result) + 1;
break;
case 'm':
case 'M':
if (plus != 0 || prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
/* If a width was not given use a width of zero. */
if (width == -1) width = 0;
if (ljust == 0) sprintf(result, "%*s", width,
vpi_get_str(vpiFullName, info->scope));
else sprintf(result, "%-*s", width,
vpi_get_str(vpiFullName, info->scope));
size = strlen(result) + 1;
break;
case 's':
case 'S':
*idx += 1;
if (plus != 0 || prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
value.format = vpiStringVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
/* If a width was not given use a width of zero. */
if (width == -1) width = 0;
/* If the (default buffer is too small make it big enough. */
size = strlen(value.value.str) + 1;
if (size > max_size) result = realloc(result, size*sizeof(char));
if (ljust == 0) sprintf(result, "%*s", width, value.value.str);
else sprintf(result, "%-*s", width, value.value.str);
}
}
size = strlen(result) + 1;
break;
case 't':
case 'T':
*idx += 1;
if (plus != 0) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
char tbuf[256];
PLI_INT32 type;
PLI_INT32 time_units = vpi_get(vpiTimeUnit, info->scope);
type = vpi_get(vpiType, info->items[*idx]);
if (width == -1) width = timeformat_info.width;
if (ld_zero == 1) width = 0;
if (prec == -1) prec = timeformat_info.prec;
/* Is it some type of real value? */
if (((type == vpiConstant || type == vpiParameter) &&
vpi_get(vpiConstType, info->items[*idx]) == vpiRealConst) ||
type == vpiRealVar ) {
value.format = vpiRealVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n",
info->name, fmtb);
} else {
get_time_real(tbuf, value.value.real, prec, time_units);
if (ljust == 0) sprintf(result, "%*s", width, tbuf);
else sprintf(result, "%-*s", width, tbuf);
}
} else {
value.format = vpiDecStrVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n",
info->name, fmtb);
} else {
get_time(tbuf, value.value.str, prec, time_units);
if (ljust == 0) sprintf(result, "%*s", width, tbuf);
else sprintf(result, "%-*s", width, tbuf);
}
}
}
size = strlen(result) + 1;
break;
case 'u':
case 'U':
*idx += 1;
size = 0; /* fallback value if errors */
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 ||
prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
value.format = vpiVectorVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
PLI_INT32 veclen, word, byte, bits;
char *cp;
veclen = (vpi_get(vpiSize, info->items[*idx])+31)/32;
size = veclen * 4 + 1;
/* If the (default buffer is too small make it big enough. */
if (size > max_size) result = realloc(result, size*sizeof(char));
cp = result;
for (word = 0; word < veclen; word += 1) {
bits = value.value.vector[word].aval &
~value.value.vector[word].bval;
#ifdef WORDS_BIGENDIAN
for (byte = 3; byte >= 0; byte -= 1) {
#else
for (byte = 0; byte <= 3; byte += 1) {
#endif
*cp = (bits >> byte*8) & 0xff;
cp += 1;
}
}
*cp = '\0';
}
}
/* size is defined above! We can't use strlen here since this can
* be a binary string (can contain NULLs). */
break;
case 'v':
case 'V':
*idx += 1;
if (plus != 0 || prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name,
fmtb);
} else {
value.format = vpiStrengthVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
char tbuf[4];
/* If a width was not given use a width of zero. */
if (width == -1) width = 0;
vpip_format_strength(tbuf, &value);
if (ljust == 0) sprintf(result, "%*s", width, tbuf);
else sprintf(result, "%-*s", width, tbuf);
}
}
size = strlen(result) + 1;
break;
case 'z':
case 'Z':
*idx += 1;
size = 0; /* fallback value if errors */
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
if (ljust != 0 || plus != 0 || ld_zero != 0 || width != -1 ||
prec != -1) {
vpi_printf("WARNING: invalid format %s%s.\n", info->name, fmtb);
}
if (*idx >= info->nitems) {
vpi_printf("WARNING: missing argument for %s%s.\n", info->name, fmtb);
} else {
value.format = vpiVectorVal;
vpi_get_value(info->items[*idx], &value);
if (value.format == vpiSuppressVal) {
vpi_printf("WARNING: incompatible value for %s%s.\n", info->name,
fmtb);
} else {
PLI_INT32 veclen, word, elem, bits, byte;
char *cp;
veclen = (vpi_get(vpiSize, info->items[*idx])+31)/32;
size = 2 * veclen * 4 + 1;
/* If the (default buffer is too small make it big enough. */
if (size > max_size) result = realloc(result, size*sizeof(char));
cp = result;
for (word = 0; word < veclen; word += 1) {
/* Write the aval followed by the bval in endian order. */
for (elem = 0; elem < 2; elem += 1) {
bits = *(&value.value.vector[word].aval+elem);
#ifdef WORDS_BIGENDIAN
for (byte = 3; byte >= 0; byte -= 1) {
#else
for (byte = 0; byte <= 3; byte += 1) {
#endif
*cp = (bits >> byte*8) & 0xff;
cp += 1;
}
}
}
*cp = '\0';
}
}
/* size is defined above! We can't use strlen here since this can
* be a binary string (can contain NULLs). */
break;
default:
vpi_printf("WARNING: unknown format %s%s.\n", info->name, fmtb);
size = strlen(result) + 1;
break;
}
free(fmtb);
/* We can't use strdup here since %u and %z can insert NULL
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
* characters into the stream. */
*rtn = malloc(size*sizeof(char));
memcpy(*rtn, result, size);
return size - 1;
}
/* We can't use the normal str functions on the return value since
* %u and %z can insert NULL characters into the stream. */
static unsigned int get_format(char **rtn, char *fmt,
struct strobe_cb_info *info, unsigned int *idx)
{
char *cp = fmt;
unsigned int size;
*rtn = strdup("");
size = 1;
while (*cp) {
size_t cnt = strcspn(cp, "%");
if (cnt > 0) {
*rtn = realloc(*rtn, (size+cnt)*sizeof(char));
memcpy(*rtn+size-1, cp, cnt);
size += cnt;
cp += cnt;
} else {
int cnt, ljust = 0, plus = 0, ld_zero = 0, width = -1, prec = -1;
char *result;
cp += 1;
while ((*cp == '-') || (*cp == '+')) {
if (*cp == '-') ljust = 1;
else plus = 1;
cp += 1;
}
if (*cp == '0') ld_zero = 1;
if (isdigit((int)*cp)) width = strtoul(cp, &cp, 10);
if (*cp == '.') {
cp += 1;
prec = strtoul(cp, &cp, 10);
}
cnt = get_format_char(&result, ljust, plus, ld_zero, width, prec, *cp,
info, idx);
*rtn = realloc(*rtn, (size+cnt)*sizeof(char));
memcpy(*rtn+size-1, result, cnt);
free(result);
size += cnt;
cp += 1;
}
}
*(*rtn+size-1) = '\0';
return size - 1;
}
static unsigned int get_numeric(char **rtn, struct strobe_cb_info *info,
vpiHandle item)
{
s_vpi_value value;
value.format = info->default_format;
vpi_get_value(item, &value);
*rtn = strdup(value.value.str);
return strlen(*rtn);
}
/* In many places we can't use the normal str functions since %u and %z
* can insert NULL characters into the stream. */
static char *get_display(unsigned int *rtnsz, struct strobe_cb_info *info)
{
char *result, *fmt, *rtn, *func_name;
s_vpi_value value;
unsigned int idx, size, width;
char buf[256];
rtn = strdup("");
size = 1;
for (idx = 0; idx < info->nitems; idx += 1) {
vpiHandle item = info->items[idx];
switch (vpi_get(vpiType, item)) {
case vpiConstant:
case vpiParameter:
if (vpi_get(vpiConstType, item) == vpiStringConst) {
value.format = vpiStringVal;
vpi_get_value(item, &value);
fmt = strdup(value.value.str);
width = get_format(&result, fmt, info, &idx);
free(fmt);
} else {
width = get_numeric(&result, info, item);
}
rtn = realloc(rtn, (size+width)*sizeof(char));
memcpy(rtn+size-1, result, width);
free(result);
break;
case vpiNet:
case vpiReg:
case vpiIntegerVar:
case vpiMemoryWord:
width = get_numeric(&result, info, item);
rtn = realloc(rtn, (size+width)*sizeof(char));
memcpy(rtn+size-1, result, width);
free(result);
break;
/* It appears that this is not currently used! A time variable is
passed as an integer and processed above. Hence this code has
only been visually checked. */
case vpiTimeVar:
value.format = vpiDecStrVal;
vpi_get_value(item, &value);
get_time(buf, value.value.str, timeformat_info.prec,
vpi_get(vpiTimeUnit, info->scope));
width = strlen(buf);
if (width < timeformat_info.width) width = timeformat_info.width;
rtn = realloc(rtn, (size+width)*sizeof(char));
sprintf(rtn+size-1, "%*s", width, buf);
break;
/* Realtime variables are also processed here. */
case vpiRealVar:
value.format = vpiRealVal;
vpi_get_value(item, &value);
sprintf(buf, "%g", value.value.real);
width = strlen(buf);
rtn = realloc(rtn, (size+width)*sizeof(char));
memcpy(rtn+size-1, buf, width);
break;
case vpiSysFuncCall:
func_name = vpi_get_str(vpiName, item);
/* This also gets the $stime function! */
if (strcmp(func_name, "$time") == 0) {
value.format = vpiDecStrVal;
vpi_get_value(item, &value);
get_time(buf, value.value.str, timeformat_info.prec,
vpi_get(vpiTimeUnit, info->scope));
width = strlen(buf);
if (width < timeformat_info.width) width = timeformat_info.width;
rtn = realloc(rtn, (size+width)*sizeof(char));
sprintf(rtn+size-1, "%*s", width, buf);
} else if (strcmp(func_name, "$simtime") == 0) {
value.format = vpiDecStrVal;
vpi_get_value(item, &value);
/* $simtime does not need to be scaled. */
get_time(buf, value.value.str, timeformat_info.prec,
timeformat_info.units);
width = strlen(buf);
if (width < timeformat_info.width) width = timeformat_info.width;
rtn = realloc(rtn, (size+width)*sizeof(char));
sprintf(rtn+size-1, "%*s", width, buf);
} else if (strcmp(func_name, "$realtime") == 0) {
value.format = vpiRealVal;
vpi_get_value(item, &value);
get_time_real(buf, value.value.real, timeformat_info.prec,
vpi_get(vpiTimeUnit, info->scope));
width = strlen(buf);
if (width < timeformat_info.width) width = timeformat_info.width;
rtn = realloc(rtn, (size+width)*sizeof(char));
sprintf(rtn+size-1, "%*s", width, buf);
} else {
vpi_printf("WARNING: %s does not support %s as an argument!\n",
info->name, func_name);
strcpy(buf, "<?>");
width = strlen(buf);
rtn = realloc(rtn, (size+width)*sizeof(char));
memcpy(rtn+size-1, buf, width);
}
break;
default:
vpi_printf("WARNING: unknown argument type (%d) given to %s!\n",
vpi_get(vpiType, item), info->name);
result = "<?>";
width = strlen(result);
rtn = realloc(rtn, (size+width)*sizeof(char));
memcpy(rtn+size-1, result, width);
break;
}
size += width;
}
rtn[size-1] = '\0';
*rtnsz = size - 1;
return rtn;
}
static PLI_INT32 sys_swrite_compiletf(PLI_BYTE8 *name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle reg;
/* Check that there are arguments. */
if (argv == 0) {
vpi_printf("ERROR: %s requires at least one argument.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
/* The first argument must be a register. */
reg = vpi_scan(argv); //* This should never be zero. */
if (vpi_get(vpiType, reg) != vpiReg) {
vpi_printf("ERROR: %s's first argument must be a register.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
vpi_free_object(argv);
return 0;
}
static PLI_INT32 sys_swrite_calltf(PLI_BYTE8 *name)
{
vpiHandle callh, argv, reg, scope;
struct strobe_cb_info info;
s_vpi_value val;
unsigned int size;
callh = vpi_handle(vpiSysTfCall, 0);
argv = vpi_iterate(vpiArgument, callh);
reg = vpi_scan(argv);
scope = vpi_handle(vpiScope, callh);
assert(scope);
/* We could use vpi_get_str(vpiName, callh) to get the task name, but
* name is already defined. */
info.name = name;
info.default_format = get_default_format(name);
info.scope = scope;
array_from_iterator(&info, argv);
/* Because %u and %z may put embedded NULL characters into the returned
* string strlen() may not match the real size! */
val.value.str = get_display(&size, &info);
val.format = vpiStringVal;
vpi_put_value(reg, &val, 0, vpiNoDelay);
if (size != strlen(val.value.str)) {
vpi_printf("WARNING: %s returned a value with an embedded NULL "
"(see %%u/%%z).\n", name);
}
free(info.items);
return 0;
}
static PLI_INT32 sys_sformat_compiletf(PLI_BYTE8 *name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
PLI_INT32 type;
/* Check that there are arguments. */
if (argv == 0) {
vpi_printf("ERROR: %s requires at least two argument.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
/* The first argument must be a register. */
arg = vpi_scan(argv); //* This should never be zero. */
if (vpi_get(vpiType, arg) != vpiReg) {
vpi_printf("ERROR: %s's first argument must be a register.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
/* The second argument must be a string or a register. */
arg = vpi_scan(argv);
if (arg == 0) {
vpi_printf("ERROR: %s requires at least two argument.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
type = vpi_get(vpiType, arg);
if (((type != vpiConstant && type != vpiParameter) ||
vpi_get(vpiConstType, arg) != vpiStringConst) && type != vpiReg) {
vpi_printf("ERROR: %s's second argument must be a string or a register.\n",
name);
vpi_control(vpiFinish, 1);
return 0;
}
vpi_free_object(argv);
return 0;
}
static PLI_INT32 sys_sformat_calltf(PLI_BYTE8 *name)
{
vpiHandle callh, argv, reg, scope;
struct strobe_cb_info info;
s_vpi_value val;
char *result, *fmt;
unsigned int idx, size;
callh = vpi_handle(vpiSysTfCall, 0);
argv = vpi_iterate(vpiArgument, callh);
reg = vpi_scan(argv);
val.format = vpiStringVal;
vpi_get_value(vpi_scan(argv), &val);
fmt = strdup(val.value.str);
scope = vpi_handle(vpiScope, callh);
assert(scope);
/* We could use vpi_get_str(vpiName, callh) to get the task name, but
* name is already defined. */
info.name = name;
info.default_format = get_default_format(name);
info.scope = scope;
array_from_iterator(&info, argv);
idx = -1;
size = get_format(&result, fmt, &info, &idx);
free(fmt);
if (idx+1< info.nitems) {
vpi_printf("WARNING: %s has %d extra argument(s).\n", name,
info.nitems-idx-1);
}
val.value.str = result;
val.format = vpiStringVal;
vpi_put_value(reg, &val, 0, vpiNoDelay);
if (size != strlen(val.value.str)) {
vpi_printf("WARNING: %s returned a value with an embedded NULL "
"(see %%u/%%z).\n", name);
}
free(info.items);
return 0;
}
2007-04-10 06:56:26 +02:00
static PLI_INT32 sys_end_of_compile(p_cb_data cb_data)
{
/* The default timeformat prints times in unit of simulation
precision. */
timeformat_info.suff = strdup("");
timeformat_info.units = vpi_get(vpiTimePrecision, 0);
timeformat_info.prec = 0;
timeformat_info.width = 20;
return 0;
}
static PLI_INT32 sys_timeformat_compiletf(PLI_BYTE8*xx)
2002-05-31 06:26:54 +02:00
{
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
vpiHandle tmp;
2007-04-10 06:56:26 +02:00
int lp;
2002-05-31 06:26:54 +02:00
2007-04-10 06:56:26 +02:00
if (argv) {
for (lp=0; lp<4; lp++) {
tmp = vpi_scan(argv);
if (tmp) {
if (vpi_get(vpiType, tmp) != vpiConstant) {
vpi_printf("$timeformat's arguments must be");
vpi_printf(" constant.\n");
vpi_control(vpiFinish, 1);
2007-04-10 06:56:26 +02:00
}
} else {
vpi_printf("$timeformat requires zero or four");
vpi_printf(" arguments!\n");
vpi_control(vpiFinish, 1);
2007-04-10 06:56:26 +02:00
return 0;
}
}
tmp = vpi_scan(argv);
if (tmp != 0) {
vpi_printf("ERROR: $timeformat takes at most four"
" arguments.\n");
vpi_control(vpiFinish, 1);
return 0;
}
2007-04-10 06:56:26 +02:00
}
2002-05-31 06:26:54 +02:00
return 0;
}
static PLI_INT32 sys_timeformat_calltf(PLI_BYTE8*xx)
2002-05-31 06:26:54 +02:00
{
s_vpi_value value;
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
2007-04-10 06:56:26 +02:00
if (argv) {
vpiHandle units = vpi_scan(argv);
vpiHandle prec = vpi_scan(argv);
vpiHandle suff = vpi_scan(argv);
vpiHandle wid = vpi_scan(argv);
2002-05-31 06:26:54 +02:00
2007-04-10 06:56:26 +02:00
vpi_free_object(argv);
2002-05-31 06:26:54 +02:00
2007-04-10 06:56:26 +02:00
value.format = vpiIntVal;
vpi_get_value(units, &value);
timeformat_info.units = value.value.integer;
2002-05-31 06:26:54 +02:00
2007-04-10 06:56:26 +02:00
value.format = vpiIntVal;
vpi_get_value(prec, &value);
timeformat_info.prec = value.value.integer;
2002-05-31 06:26:54 +02:00
2007-04-10 06:56:26 +02:00
value.format = vpiStringVal;
vpi_get_value(suff, &value);
timeformat_info.suff = strdup(value.value.str);
2002-05-31 06:26:54 +02:00
2007-04-10 06:56:26 +02:00
value.format = vpiIntVal;
vpi_get_value(wid, &value);
timeformat_info.width = value.value.integer;
} else {
/* If no arguments are given then use the default values. */
sys_end_of_compile(NULL);
}
2002-05-31 06:26:54 +02:00
return 0;
}
2007-04-12 04:50:51 +02:00
static char *pts_convert(int value)
{
char *string;
switch (value) {
case 0: string = "1s"; break;
2007-04-12 04:50:51 +02:00
case -1: string = "100ms"; break;
case -2: string = "10ms"; break;
case -3: string = "1ms"; break;
case -4: string = "100us"; break;
case -5: string = "10us"; break;
case -6: string = "1us"; break;
case -7: string = "100ns"; break;
case -8: string = "10ns"; break;
case -9: string = "1ns"; break;
case -10: string = "100ps"; break;
case -11: string = "10ps"; break;
case -12: string = "1ps"; break;
case -13: string = "100fs"; break;
case -14: string = "10fs"; break;
case -15: string = "1fs"; break;
default: assert(0);
}
return string;
}
static PLI_INT32 sys_printtimescale_calltf(PLI_BYTE8*xx)
{
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
vpiHandle scope;
if (!argv) {
vpiHandle parent = vpi_handle(vpiScope, sys);
scope = NULL; /* fallback value if parent is NULL */
2007-04-12 04:50:51 +02:00
while (parent) {
scope = parent;
parent = vpi_handle(vpiScope, scope);
}
} else {
scope = vpi_scan(argv);
vpi_free_object(argv);
}
2007-04-12 04:50:51 +02:00
vpi_printf("Time scale of (%s) is ", vpi_get_str(vpiFullName, scope));
vpi_printf("%s / ", pts_convert(vpi_get(vpiTimeUnit, scope)));
vpi_printf("%s\n", pts_convert(vpi_get(vpiTimePrecision, scope)));
return 0;
}
void sys_display_register()
{
2002-05-31 06:26:54 +02:00
s_cb_data cb_data;
s_vpi_systf_data tf_data;
//============================== display
tf_data.type = vpiSysTask;
tf_data.tfname = "$display";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$display";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$displayh";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$displayh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$displayo";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$displayo";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$displayb";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$displayb";
vpi_register_systf(&tf_data);
//============================== write
tf_data.type = vpiSysTask;
tf_data.tfname = "$write";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$write";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$writeh";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$writeh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$writeo";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$writeo";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$writeb";
tf_data.calltf = sys_display_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$writeb";
vpi_register_systf(&tf_data);
//============================== strobe
tf_data.type = vpiSysTask;
tf_data.tfname = "$strobe";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$strobe";
vpi_register_systf(&tf_data);
1999-10-29 05:37:22 +02:00
tf_data.type = vpiSysTask;
tf_data.tfname = "$strobeh";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$strobeh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$strobeo";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$strobeo";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$strobeb";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$strobeb";
vpi_register_systf(&tf_data);
2003-08-26 05:51:05 +02:00
//============================== fstrobe
tf_data.type = vpiSysTask;
tf_data.tfname = "$fstrobe";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fstrobe";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fstrobeh";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fstrobeh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fstrobeo";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fstrobeo";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fstrobeb";
tf_data.calltf = sys_strobe_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fstrobeb";
vpi_register_systf(&tf_data);
//============================== monitor
1999-10-29 05:37:22 +02:00
tf_data.type = vpiSysTask;
tf_data.tfname = "$monitor";
tf_data.calltf = sys_monitor_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$monitor";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$monitorh";
tf_data.calltf = sys_monitor_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$monitorh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$monitoro";
tf_data.calltf = sys_monitor_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$monitoro";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$monitorb";
tf_data.calltf = sys_monitor_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$monitorb";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$monitoron";
tf_data.calltf = sys_monitoron_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$monitoron";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$monitoroff";
tf_data.calltf = sys_monitoroff_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$monitoroff";
vpi_register_systf(&tf_data);
//============================== fdisplay
tf_data.type = vpiSysTask;
tf_data.tfname = "$fdisplay";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fdisplay";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fdisplayh";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fdisplayh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fdisplayo";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fdisplayo";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fdisplayb";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fdisplayb";
vpi_register_systf(&tf_data);
//============================== fwrite
tf_data.type = vpiSysTask;
tf_data.tfname = "$fwrite";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fwrite";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fwriteh";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fwriteh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fwriteo";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fwriteo";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$fwriteb";
tf_data.calltf = sys_fdisplay_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = "$fwriteb";
2001-03-22 03:23:17 +01:00
vpi_register_systf(&tf_data);
2002-05-31 06:26:54 +02:00
Implement the swrite* and sformat system tasks plus a few other fixes. This patch implements the swrite* and sformat system tasks. It also makes $simtime distinguishable from the other integer time tasks. This was needed to get the correct time units when $simtime was given as an argument to $swrite*. The string constant code was also modified to allow a string to be returned as a vector (0/1 bit pattern). Here are some more specifics about the swrite* changes. 1. They do not share formatting code with the other display functions, so they may/will produce different results. 2. All %{alpha} codes allow a width and justification. Others have been enhanced (%t allows the default width and precision to be overridden, time functions print with time formatting, better error checking and messages, etc.). 3. %u and %z formatting codes have been added. It is important to note that these two formats can produce embedded NULLs, since these functions are returning a string anything after the first NULL will not be reachable! memcpy is used instead of regular string processing where needed so that the original string will contain the total result. The size returned when the string is created is the true length. A warning will be printed if a string with embedded NULLs is produced (strlen() does not match the true length). 4. Real numbers are printed with %g instead of %f. Once this new formatting code has been evaluated we should incorporate the changes/fixes into the formatting code for the other functions or the other functions could be modified to use this new code. The true string length is available so we should be able to work around the embedded NULL problem.
2007-08-14 00:56:02 +02:00
//============================== swrite
tf_data.type = vpiSysTask;
tf_data.tfname = "$swrite";
tf_data.calltf = sys_swrite_calltf;
tf_data.compiletf = sys_swrite_compiletf;
tf_data.sizetf = 0;
tf_data.user_data = "$swrite";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$swriteh";
tf_data.calltf = sys_swrite_calltf;
tf_data.compiletf = sys_swrite_compiletf;
tf_data.sizetf = 0;
tf_data.user_data = "$swriteh";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$swriteo";
tf_data.calltf = sys_swrite_calltf;
tf_data.compiletf = sys_swrite_compiletf;
tf_data.sizetf = 0;
tf_data.user_data = "$swriteo";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$swriteb";
tf_data.calltf = sys_swrite_calltf;
tf_data.compiletf = sys_swrite_compiletf;
tf_data.sizetf = 0;
tf_data.user_data = "$swriteb";
vpi_register_systf(&tf_data);
tf_data.type = vpiSysTask;
tf_data.tfname = "$sformat";
tf_data.calltf = sys_sformat_calltf;
tf_data.compiletf = sys_sformat_compiletf;
tf_data.sizetf = 0;
tf_data.user_data = "$sformat";
vpi_register_systf(&tf_data);
//============================ timeformat
2002-05-31 06:26:54 +02:00
tf_data.type = vpiSysTask;
tf_data.tfname = "$timeformat";
tf_data.calltf = sys_timeformat_calltf;
tf_data.compiletf = sys_timeformat_compiletf;
tf_data.sizetf = 0;
vpi_register_systf(&tf_data);
2007-04-12 04:50:51 +02:00
tf_data.type = vpiSysTask;
tf_data.tfname = "$printtimescale";
tf_data.calltf = sys_printtimescale_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
vpi_register_systf(&tf_data);
2002-05-31 06:26:54 +02:00
cb_data.reason = cbEndOfCompile;
cb_data.cb_rtn = sys_end_of_compile;
cb_data.user_data = "system";
vpi_register_cb(&cb_data);
}