1211 lines
36 KiB
C
1211 lines
36 KiB
C
/*
|
|
* Copyright (c) 2003-2009 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
|
|
*/
|
|
|
|
# include "vpi_user.h"
|
|
# include "sys_priv.h"
|
|
# include <assert.h>
|
|
# include <ctype.h>
|
|
# include <errno.h>
|
|
# include <string.h>
|
|
# include <stdio.h>
|
|
# include <stdlib.h>
|
|
|
|
#define IS_MCD(mcd) !((mcd)>>31&1)
|
|
|
|
/*
|
|
* Implement the $fopen system function.
|
|
*/
|
|
static PLI_INT32 sys_fopen_compiletf(PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv;
|
|
vpiHandle arg;
|
|
assert(callh != 0);
|
|
argv = vpi_iterate(vpiArgument, callh);
|
|
|
|
/* Check that there is a file name argument and that it is a string. */
|
|
if (argv == 0) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires a string file name argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
if (! is_string_obj(vpi_scan(argv))) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's file name argument must be a string.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* The type argument is optional. */
|
|
arg = vpi_scan(argv);
|
|
if (arg == 0) return 0;
|
|
|
|
/* When provided, the type argument must be a string. */
|
|
if (! is_string_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's type argument must be a string.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "two string arguments", 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fopen_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
s_vpi_value val;
|
|
int fail = 0;
|
|
char *mode_string = 0;
|
|
unsigned idx;
|
|
vpiHandle fileh = vpi_scan(argv);
|
|
char *fname;
|
|
vpiHandle mode = vpi_scan(argv);
|
|
errno = 0;
|
|
|
|
/* Get the mode handle if it exists. */
|
|
if (mode) {
|
|
char *esc_md;
|
|
val.format = vpiStringVal;
|
|
vpi_get_value(mode, &val);
|
|
/* Verify that we have a string and that it is not NULL. */
|
|
if (val.format != vpiStringVal || !*(val.value.str)) {
|
|
vpi_printf("WARNING: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's mode argument is not a valid string.\n",
|
|
name);
|
|
fail = 1;
|
|
}
|
|
|
|
/* Make sure the mode string is correct. */
|
|
if (strlen(val.value.str) > 3) {
|
|
vpi_printf("WARNING: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
esc_md = as_escaped(val.value.str);
|
|
vpi_printf("%s's mode argument (%s) is too long.\n",
|
|
name, esc_md);
|
|
free(esc_md);
|
|
fail = 1;
|
|
} else {
|
|
unsigned bin = 0, plus = 0;
|
|
switch (val.value.str[0]) {
|
|
case 'r':
|
|
case 'w':
|
|
case 'a':
|
|
for (idx = 1; idx < 3 ; idx++) {
|
|
if (val.value.str[idx] == '\0') break;
|
|
switch (val.value.str[idx]) {
|
|
case 'b':
|
|
if (bin) fail = 1;
|
|
bin = 1;
|
|
break;
|
|
case '+':
|
|
if (plus) fail = 1;
|
|
plus = 1;
|
|
break;
|
|
default:
|
|
fail = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (! fail) break;
|
|
|
|
default:
|
|
vpi_printf("WARNING: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
esc_md = as_escaped(val.value.str);
|
|
vpi_printf("%s's mode argument (%s) is invalid.\n",
|
|
name, esc_md);
|
|
free(esc_md);
|
|
fail = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
mode_string = strdup(val.value.str);
|
|
|
|
vpi_free_object(argv);
|
|
}
|
|
|
|
fname = get_filename(callh, name, fileh);
|
|
|
|
/* If either the mode or file name are not valid just return. */
|
|
if (fail || fname == 0) {
|
|
free(fname);
|
|
if (mode) free(mode_string);
|
|
return 0;
|
|
}
|
|
|
|
val.format = vpiIntVal;
|
|
if (mode) {
|
|
val.value.integer = vpi_fopen(fname, mode_string);
|
|
free(mode_string);
|
|
} else
|
|
val.value.integer = vpi_mcd_open(fname);
|
|
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
free(fname);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Implement the $fopenr(), $fopenw() and $fopena() system functions
|
|
* from Chris Spear's File I/O for Verilog.
|
|
*/
|
|
|
|
static PLI_INT32 sys_fopenrwa_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
s_vpi_value val;
|
|
char *mode, *fname;
|
|
errno = 0;
|
|
|
|
/* Get the mode. */
|
|
mode = name + strlen(name) - 1;
|
|
|
|
/* Get the file name. */
|
|
fname = get_filename(callh, name, vpi_scan(argv));
|
|
vpi_free_object(argv);
|
|
if (fname == 0) return 0;
|
|
|
|
/* Open the file and return the result. */
|
|
val.format = vpiIntVal;
|
|
val.value.integer = vpi_fopen(fname, mode);
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
free(fname);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Implement $fclose system function
|
|
*/
|
|
static PLI_INT32 sys_fclose_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle fd = vpi_scan(argv);
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
char *str = ""; /* This prevents the compiler from complaining. */
|
|
errno = 0;
|
|
|
|
vpi_free_object(argv);
|
|
|
|
/* Get the file/MC descriptor and verify that it is valid. */
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(fd, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) ||
|
|
( IS_MCD(fd_mcd) && vpi_mcd_printf(fd_mcd, str) == EOF) ||
|
|
(! fd_mcd)) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor/MCD (0x%x) given to %s.\n",
|
|
fd_mcd, name);
|
|
errno = EBADF;
|
|
return 0;
|
|
}
|
|
|
|
/* We need to cancel any active $fstrobe()'s for this FD/MCD.
|
|
* For now we check in the strobe callback and skip the output
|
|
* generation when needed. */
|
|
vpi_mcd_close(fd_mcd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Implement $fflush system function
|
|
*/
|
|
static PLI_INT32 sys_fflush_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
FILE *fp;
|
|
char *str = ""; /* This prevents the compiler from complaining. */
|
|
errno = 0;
|
|
|
|
/* If we have no argument then flush all the streams. */
|
|
if (argv == 0) {
|
|
fflush(NULL);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the file/MC descriptor and verify that it is valid. */
|
|
arg = vpi_scan(argv);
|
|
vpi_free_object(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) ||
|
|
( IS_MCD(fd_mcd) && vpi_mcd_printf(fd_mcd, str) == EOF) ||
|
|
(! fd_mcd)) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor/MCD (0x%x) given to %s.\n",
|
|
fd_mcd, name);
|
|
errno = EBADF;
|
|
return 0;
|
|
}
|
|
|
|
if (IS_MCD(fd_mcd)) {
|
|
vpi_mcd_flush(fd_mcd);
|
|
} else {
|
|
/* If we have a valid file descriptor flush the file. */
|
|
fp = vpi_get_file(fd_mcd);
|
|
if (fp) fflush(fp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fputc_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
FILE *fp;
|
|
unsigned char chr;
|
|
errno = 0;
|
|
|
|
/* Get the character. */
|
|
arg = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
chr = val.value.integer;
|
|
|
|
|
|
/* Get the file/MC descriptor. */
|
|
arg = vpi_scan(argv);
|
|
vpi_free_object(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
/* Put the character and return the result. */
|
|
fp = vpi_get_file(fd_mcd);
|
|
val.format = vpiIntVal;
|
|
if (!fp) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd,
|
|
name);
|
|
errno = EBADF;
|
|
val.value.integer = EOF;
|
|
} else {
|
|
val.value.integer = fputc(chr, fp);
|
|
if (val.value.integer != EOF) val.value.integer = 0;
|
|
}
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fgets_compiletf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
|
|
/*
|
|
* Check that there are two arguments and that the first is a
|
|
* register and that the second is numeric.
|
|
*/
|
|
if (argv == 0) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires two arguments.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
if (vpi_get(vpiType, vpi_scan(argv)) != vpiReg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's first argument must be a reg.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires a second (numeric) argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
if (! is_numeric_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's second argument must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "two arguments", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fgets_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle regh;
|
|
vpiHandle arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
FILE *fp;
|
|
PLI_INT32 reg_size;
|
|
char*text;
|
|
errno = 0;
|
|
|
|
/* Get the register handle. */
|
|
regh = vpi_scan(argv);
|
|
|
|
/* Get the file/MCD descriptor. */
|
|
arg = vpi_scan(argv);
|
|
vpi_free_object(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
/* Return zero if this is not a valid fd. */
|
|
fp = vpi_get_file(fd_mcd);
|
|
if (!fp) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd,
|
|
name);
|
|
errno = EBADF;
|
|
val.format = vpiIntVal;
|
|
val.value.integer = 0;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the register size in bytes and allocate the buffer. */
|
|
reg_size = vpi_get(vpiSize, regh) / 8;
|
|
text = malloc(reg_size + 1);
|
|
|
|
/* Read in the bytes. Return 0 if there was an error. */
|
|
if (fgets(text, reg_size+1, fp) == 0) {
|
|
val.format = vpiIntVal;
|
|
val.value.integer = 0;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
free(text);
|
|
return 0;
|
|
}
|
|
|
|
/* Return the number of character read. */
|
|
val.format = vpiIntVal;
|
|
val.value.integer = strlen(text);
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
|
|
/* Return the characters to the register. */
|
|
val.format = vpiStringVal;
|
|
val.value.str = text;
|
|
vpi_put_value(regh, &val, 0, vpiNoDelay);
|
|
free(text);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fread_compiletf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
PLI_INT32 type;
|
|
|
|
/* We must have at least two arguments. */
|
|
if (argv == 0) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires two arguments.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Check that the first required argument is a register or memory. */
|
|
type = vpi_get(vpiType, vpi_scan(argv));
|
|
if (type != vpiReg && type != vpiMemory) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's first argument must be a reg or memory.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Check that the second required argument is numeric (a fd). */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires a second (file descriptor) argument.\n",
|
|
name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
if (! is_numeric_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's second argument must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/*
|
|
* If given check that the third argument is numeric (start).
|
|
*
|
|
* Technically you can give the fourth argument (count) with
|
|
* out a third argument (start), but Icarus does not currently
|
|
* support missing function arguments!
|
|
*/
|
|
arg = vpi_scan(argv);
|
|
if (arg) {
|
|
if (! is_numeric_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's third argument must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* If given check that the fourth argument is numeric (count). */
|
|
arg = vpi_scan(argv);
|
|
if (arg) {
|
|
if (! is_numeric_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's fourth argument must be numeric.\n",
|
|
name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "four arguments", 1);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* The pattern here is get the current vector, load the new bits on
|
|
* top of the old ones and then put the modified vector. We need the
|
|
* "get" first so that if we run out of bits in the file we keep the
|
|
* original ones.
|
|
*/
|
|
static unsigned fread_word(FILE *fp, vpiHandle word,
|
|
unsigned words, unsigned bpe, s_vpi_vecval *vector)
|
|
{
|
|
unsigned rtn, clr_mask, bnum;
|
|
int bidx, byte;
|
|
s_vpi_value val;
|
|
struct t_vpi_vecval *cur = &vector[words-1];
|
|
|
|
rtn = 0;
|
|
|
|
/* Get the current bits from the register and copy them to
|
|
* my local vector. */
|
|
val.format = vpiVectorVal;
|
|
vpi_get_value(word, &val);
|
|
for (bidx = 0; bidx < words; bidx += 1) {
|
|
vector[bidx].aval = val.value.vector[bidx].aval;
|
|
vector[bidx].bval = val.value.vector[bidx].bval;
|
|
}
|
|
|
|
/* Copy the bytes to the local vector MSByte first. */
|
|
for (bidx = bpe-1; bidx >= 0; bidx -= 1) {
|
|
byte = fgetc(fp);
|
|
if (byte == EOF) break;
|
|
/* Clear the current byte and load the new value. */
|
|
bnum = bidx % 4;
|
|
clr_mask = ~(0xff << bnum*8);
|
|
cur->aval &= clr_mask;
|
|
cur->bval &= clr_mask;
|
|
cur->aval |= byte << bnum*8;
|
|
rtn += 1;
|
|
if (bnum == 0) cur -= 1;
|
|
}
|
|
|
|
/* Put the updated bits into the register. */
|
|
val.value.vector = vector;
|
|
vpi_put_value(word, &val, 0, vpiNoDelay);
|
|
|
|
return rtn;
|
|
}
|
|
|
|
static PLI_INT32 sys_fread_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg, mem_reg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
PLI_INT32 start, count, width, rtn;
|
|
unsigned is_mem, idx, bpe, words;
|
|
FILE *fp;
|
|
s_vpi_vecval *vector;
|
|
errno = 0;
|
|
|
|
/* Get the register/memory. */
|
|
mem_reg = vpi_scan(argv);
|
|
|
|
/* Get the file descriptor. */
|
|
arg = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
/* Return 0 if this is not a valid fd. */
|
|
fp = vpi_get_file(fd_mcd);
|
|
if (!fp) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd,
|
|
name);
|
|
errno = EBADF;
|
|
val.format = vpiIntVal;
|
|
val.value.integer = 0;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
vpi_free_object(argv);
|
|
return 0;
|
|
}
|
|
|
|
/* Are we reading into a memory? */
|
|
if (vpi_get(vpiType, mem_reg) == vpiReg) is_mem = 0;
|
|
else is_mem = 1;
|
|
|
|
/* We only need to get these for memories. */
|
|
if (is_mem) {
|
|
PLI_INT32 left, right, max, min;
|
|
|
|
/* Get the left and right memory address. */
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(vpi_handle(vpiLeftRange, mem_reg), &val);
|
|
left = val.value.integer;
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(vpi_handle(vpiRightRange, mem_reg), &val);
|
|
right = val.value.integer;
|
|
max = (left > right) ? left : right;
|
|
min = (left < right) ? left : right;
|
|
|
|
/* Get the starting address (optional). */
|
|
arg = vpi_scan(argv);
|
|
if (arg) {
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
start = val.value.integer;
|
|
if (start < min || start > max) {
|
|
vpi_printf("WARNING: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's start argument (%d) is outside "
|
|
"memory range [%d:%d].\n", name, start,
|
|
left, right);
|
|
val.format = vpiIntVal;
|
|
val.value.integer = 0;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
vpi_free_object(argv);
|
|
return 0;
|
|
}
|
|
|
|
/* Get the count (optional). */
|
|
arg = vpi_scan(argv);
|
|
if (arg) {
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
count = val.value.integer;
|
|
if (count > max-start) {
|
|
vpi_printf("WARNING: %s:%d: ",
|
|
vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's count argument (%d) is too "
|
|
"large for start (%d) and memory "
|
|
"range [%d:%d].\n", name, count,
|
|
start, left, right);
|
|
count = max - start + 1;
|
|
}
|
|
vpi_free_object(argv);
|
|
} else {
|
|
count = max - start + 1;
|
|
}
|
|
} else {
|
|
start = min;
|
|
count = max - min + 1;
|
|
}
|
|
width = vpi_get(vpiSize, vpi_handle_by_index(mem_reg, start));
|
|
} else {
|
|
start = 0;
|
|
count = 1;
|
|
width = vpi_get(vpiSize, mem_reg);
|
|
vpi_free_object(argv);
|
|
}
|
|
|
|
words = (width+31)/32;
|
|
vector = calloc(words, sizeof(s_vpi_vecval));
|
|
bpe = (width+7)/8;
|
|
|
|
if (is_mem) {
|
|
rtn = 0;
|
|
for (idx = 0; idx < count; idx += 1) {
|
|
vpiHandle word;
|
|
word = vpi_handle_by_index(mem_reg, start+(signed)idx);
|
|
rtn += fread_word(fp, word, words, bpe, vector);
|
|
if (feof(fp)) break;
|
|
}
|
|
} else {
|
|
rtn = fread_word(fp, mem_reg, words, bpe, vector);
|
|
}
|
|
free(vector);
|
|
|
|
/* Return the number of bytes read. */
|
|
val.format = vpiIntVal;
|
|
val.value.integer = rtn;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_ungetc_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
FILE *fp;
|
|
int chr;
|
|
errno = 0;
|
|
|
|
/* Get the character. */
|
|
arg = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
chr = val.value.integer;
|
|
|
|
/* Get the file/MC descriptor. */
|
|
arg = vpi_scan(argv);
|
|
vpi_free_object(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
/* Return EOF if this is not a valid fd. */
|
|
fp = vpi_get_file(fd_mcd);
|
|
if (!fp) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd,
|
|
name);
|
|
errno = EBADF;
|
|
val.format = vpiIntVal;
|
|
val.value.integer = EOF;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
return 0;
|
|
}
|
|
|
|
/* ungetc the character and return the result. */
|
|
val.format = vpiIntVal;
|
|
val.value.integer = ungetc(chr, fp);
|
|
if (val.value.integer != EOF) val.value.integer = 0;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fseek_compiletf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
|
|
/* Check that there are three numeric arguments. */
|
|
if (argv == 0) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires three arguments.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
/* Check that the first argument is numeric. */
|
|
if (! is_numeric_obj(vpi_scan(argv))) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's first argument must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Check that the second argument exists and is numeric. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires a second (numeric) argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
if (! is_numeric_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's second argument must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Check that the third argument exists and is numeric. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires a third (numeric) argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
if (! is_numeric_obj(arg)) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's third argument must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "three arguments", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_fseek_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
PLI_INT32 offset, oper;
|
|
FILE *fp;
|
|
errno = 0;
|
|
|
|
/* Get the file pointer. */
|
|
arg = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
/* Get the offset. */
|
|
arg = vpi_scan(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
offset = val.value.integer;
|
|
|
|
/* Get the operation. */
|
|
arg = vpi_scan(argv);
|
|
vpi_free_object(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
oper = val.value.integer;
|
|
|
|
/* Check that the operation is in the valid range. */
|
|
switch (oper) {
|
|
case 0:
|
|
oper = SEEK_SET;
|
|
break;
|
|
case 1:
|
|
oper = SEEK_CUR;
|
|
break;
|
|
case 2:
|
|
oper = SEEK_END;
|
|
break;
|
|
default:
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's operation must be 0, 1 or 2 given %d.\n",
|
|
name, oper);
|
|
oper = -1; /* An invalid argument value. */
|
|
}
|
|
|
|
/* Return EOF if this is not a valid fd. */
|
|
fp = vpi_get_file(fd_mcd);
|
|
if (!fp) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd,
|
|
name);
|
|
errno = EBADF;
|
|
val.format = vpiIntVal;
|
|
val.value.integer = EOF;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
return 0;
|
|
}
|
|
|
|
val.format = vpiIntVal;
|
|
val.value.integer = fseek(fp, offset, oper);
|
|
vpi_put_value(callh, &val, 0 , vpiNoDelay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_common_fd_calltf(PLI_BYTE8*name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle arg;
|
|
s_vpi_value val;
|
|
PLI_UINT32 fd_mcd;
|
|
FILE *fp;
|
|
errno = 0;
|
|
|
|
/* Get the file pointer. */
|
|
arg = vpi_scan(argv);
|
|
vpi_free_object(argv);
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(arg, &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
/* Return EOF if this is not a valid fd. */
|
|
fp = vpi_get_file(fd_mcd);
|
|
if (!fp) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd,
|
|
name);
|
|
errno = EBADF;
|
|
val.format = vpiIntVal;
|
|
val.value.integer = EOF;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
return 0;
|
|
}
|
|
|
|
val.format = vpiIntVal;
|
|
switch (name[4]) {
|
|
case 'l': /* $ftell() */
|
|
val.value.integer = ftell(fp);
|
|
break;
|
|
case 'f': /* $feof() is from 1264-2005*/
|
|
val.value.integer = feof(fp);
|
|
break;
|
|
case 'i': /* $rewind() */
|
|
val.value.integer = fseek(fp, 0L, SEEK_SET);
|
|
break;
|
|
case 't': /* $fgetc() */
|
|
val.value.integer = fgetc(fp);
|
|
break;
|
|
default:
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s cannot be processed with this routine.\n", name);
|
|
assert(0);
|
|
break;
|
|
}
|
|
vpi_put_value(callh, &val, 0 , vpiNoDelay);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Implement the $ferror system function.
|
|
*/
|
|
static PLI_INT32 sys_ferror_compiletf(PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv;
|
|
vpiHandle arg;
|
|
|
|
argv = vpi_iterate(vpiArgument, callh);
|
|
|
|
/*
|
|
* Check that there are two arguments and that the first is
|
|
* numeric and that the second is a 640 bit or larger register.
|
|
*
|
|
* The parser requires that a function have at least one argument,
|
|
* so argv should always be defined with one argument.
|
|
*/
|
|
assert(argv);
|
|
|
|
if (! is_numeric_obj(vpi_scan(argv))) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's fd (first) argument must be numeric.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Check that the second argument is given and that it is a 640 bit
|
|
* or larger register. */
|
|
arg = vpi_scan(argv);
|
|
if (! arg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s requires a second (register) argument.\n", name);
|
|
vpi_control(vpiFinish, 1);
|
|
return 0;
|
|
}
|
|
|
|
if (vpi_get(vpiType, arg) != vpiReg) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's second argument must be a reg (>=640 bits).\n",
|
|
name);
|
|
vpi_control(vpiFinish, 1);
|
|
} else if (vpi_get(vpiSize, arg) < 640) {
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("%s's second argument must have 640 bit or more.\n",
|
|
name);
|
|
vpi_control(vpiFinish, 1);
|
|
}
|
|
|
|
/* Make sure there are no extra arguments. */
|
|
check_for_extra_args(argv, callh, name, "two arguments", 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PLI_INT32 sys_ferror_calltf(PLI_BYTE8 *name)
|
|
{
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
vpiHandle reg;
|
|
s_vpi_value val;
|
|
char *msg;
|
|
PLI_INT32 size;
|
|
unsigned chars;
|
|
PLI_UINT32 fd_mcd;
|
|
|
|
/* Get the file pointer. */
|
|
val.format = vpiIntVal;
|
|
vpi_get_value(vpi_scan(argv), &val);
|
|
fd_mcd = val.value.integer;
|
|
|
|
/* Get the register to put the string result and figure out how many
|
|
* characters it will hold. */
|
|
reg = vpi_scan(argv);
|
|
size = vpi_get(vpiSize, reg);
|
|
chars = size / 8;
|
|
vpi_free_object(argv);
|
|
|
|
/* If we do not already have an error check that the fd is valid.
|
|
* The assumption is that the other routines have set errno to
|
|
* EBADF when they encounter a bad file descriptor, so we do not
|
|
* need to check here. We also need to special case this since
|
|
* $fopen() will return 0 (a bad file descriptor) when it has a
|
|
* problem (sets errno). */
|
|
if (!errno && !vpi_get_file(fd_mcd) ) {
|
|
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
|
|
(int)vpi_get(vpiLineNo, callh));
|
|
vpi_printf("invalid file descriptor (0x%x) given to %s.\n", fd_mcd,
|
|
name);
|
|
errno = EBADF;
|
|
}
|
|
|
|
/* Return the error code. */
|
|
val.format = vpiIntVal;
|
|
val.value.integer = errno;
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
|
|
/* Only return the number of characters that will fit in the reg. */
|
|
msg = (char *) malloc(chars);
|
|
if (errno != 0) strncpy(msg, strerror(errno), chars-1);
|
|
else strncpy(msg, "", chars-1);
|
|
msg[chars-1] = '\0';
|
|
val.format = vpiStringVal;
|
|
val.value.str = msg;
|
|
vpi_put_value(reg, &val, 0, vpiNoDelay);
|
|
free(msg);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sys_fileio_register()
|
|
{
|
|
s_vpi_systf_data tf_data;
|
|
|
|
/*============================== fopen */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fopen";
|
|
tf_data.calltf = sys_fopen_calltf;
|
|
tf_data.compiletf = sys_fopen_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fopen";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fopenr */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fopenr";
|
|
tf_data.calltf = sys_fopenrwa_calltf;
|
|
tf_data.compiletf = sys_one_string_arg_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fopenr";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fopenw */
|
|
tf_data.tfname = "$fopenw";
|
|
tf_data.user_data = "$fopenw";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fopena */
|
|
tf_data.tfname = "$fopena";
|
|
tf_data.user_data = "$fopena";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fclose */
|
|
tf_data.type = vpiSysTask;
|
|
tf_data.tfname = "$fclose";
|
|
tf_data.calltf = sys_fclose_calltf;
|
|
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fclose";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fflush */
|
|
tf_data.type = vpiSysTask;
|
|
tf_data.tfname = "$fflush";
|
|
tf_data.calltf = sys_fflush_calltf;
|
|
tf_data.compiletf = sys_one_opt_numeric_arg_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fflush";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fgetc */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fgetc";
|
|
tf_data.calltf = sys_common_fd_calltf;
|
|
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fgetc";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fgets */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fgets";
|
|
tf_data.calltf = sys_fgets_calltf;
|
|
tf_data.compiletf = sys_fgets_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fgets";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fread */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fread";
|
|
tf_data.calltf = sys_fread_calltf;
|
|
tf_data.compiletf = sys_fread_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fread";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== ungetc */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$ungetc";
|
|
tf_data.calltf = sys_ungetc_calltf;
|
|
tf_data.compiletf = sys_two_numeric_args_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$ungetc";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== ftell */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$ftell";
|
|
tf_data.calltf = sys_common_fd_calltf;
|
|
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$ftell";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== fseek */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fseek";
|
|
tf_data.calltf = sys_fseek_calltf;
|
|
tf_data.compiletf = sys_fseek_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fseek";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== rewind */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$rewind";
|
|
tf_data.calltf = sys_common_fd_calltf;
|
|
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$rewind";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/*============================== ferror */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$ferror";
|
|
tf_data.calltf = sys_ferror_calltf;
|
|
tf_data.compiletf = sys_ferror_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$ferror";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/* $feof() is from 1364-2005. */
|
|
/*============================== feof */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$feof";
|
|
tf_data.calltf = sys_common_fd_calltf;
|
|
tf_data.compiletf = sys_one_numeric_arg_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$feof";
|
|
vpi_register_systf(&tf_data);
|
|
|
|
/* Icarus specific. */
|
|
/*============================== fputc */
|
|
tf_data.type = vpiSysFunc;
|
|
tf_data.sysfunctype = vpiIntFunc;
|
|
tf_data.tfname = "$fputc";
|
|
tf_data.calltf = sys_fputc_calltf;
|
|
tf_data.compiletf = sys_two_numeric_args_compiletf;
|
|
tf_data.sizetf = 0;
|
|
tf_data.user_data = "$fputc";
|
|
vpi_register_systf(&tf_data);
|
|
}
|