vhdlpp: std.textio & ieee.std_logic_textio functions implemented using VPI.

This commit is contained in:
Maciej Suminski 2015-09-18 11:23:46 +02:00
parent 6fee37a640
commit b414733f34
6 changed files with 972 additions and 2 deletions

View File

@ -1072,6 +1072,7 @@ int main(int argc, char **argv)
how to handle them. */ how to handle them. */
fprintf(iconfig_file, "sys_func:%s%csystem.sft\n", base, sep); fprintf(iconfig_file, "sys_func:%s%csystem.sft\n", base, sep);
fprintf(iconfig_file, "sys_func:%s%cvhdl_sys.sft\n", base, sep); fprintf(iconfig_file, "sys_func:%s%cvhdl_sys.sft\n", base, sep);
fprintf(iconfig_file, "sys_func:%s%cvhdl_textio.sft\n", base, sep);
/* If verilog-2005/09/12 is enabled or icarus-misc or verilog-ams, /* If verilog-2005/09/12 is enabled or icarus-misc or verilog-ams,
* then include the v2005_math library. */ * then include the v2005_math library. */

View File

@ -832,6 +832,7 @@ int main(int argc, char*argv[])
// Start the module list with the base system module. // Start the module list with the base system module.
add_vpi_module("system"); add_vpi_module("system");
add_vpi_module("vhdl_sys"); add_vpi_module("vhdl_sys");
add_vpi_module("vhdl_textio");
flags["-o"] = strdup("a.out"); flags["-o"] = strdup("a.out");
min_typ_max_flag = TYP; min_typ_max_flag = TYP;

View File

@ -102,6 +102,8 @@ rm -rf $RPM_BUILD_ROOT
%attr(-,root,root) %{_libdir}/ivl%{suff}/v2009.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/v2009.vpi
%attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.sft
%attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_sys.vpi
%attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_textio.sft
%attr(-,root,root) %{_libdir}/ivl%{suff}/vhdl_textio.vpi
%attr(-,root,root) %{_libdir}/ivl%{suff}/vpi_debug.vpi %attr(-,root,root) %{_libdir}/ivl%{suff}/vpi_debug.vpi
%attr(-,root,root) %{_libdir}/ivl%{suff}/cadpli.vpl %attr(-,root,root) %{_libdir}/ivl%{suff}/cadpli.vpl
%attr(-,root,root) %{_libdir}/libvpi%{suff}.a %attr(-,root,root) %{_libdir}/libvpi%{suff}.a

View File

@ -77,9 +77,11 @@ V2009 = v2009_table.o v2009_array.o v2009_enum.o v2009_string.o
VHDL_SYS = vhdl_table.o VHDL_SYS = vhdl_table.o
VHDL_TEXTIO = vhdl_textio.o sys_priv.o
VPI_DEBUG = vpi_debug.o VPI_DEBUG = vpi_debug.o
all: dep system.vpi va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vpi_debug.vpi $(ALL32) all: dep system.vpi va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vhdl_textio.vpi vpi_debug.vpi $(ALL32)
check: all check: all
@ -88,7 +90,7 @@ clean:
rm -f sdf_lexor.c sdf_parse.c sdf_parse.output sdf_parse.h rm -f sdf_lexor.c sdf_parse.c sdf_parse.output sdf_parse.h
rm -f table_mod_parse.c table_mod_parse.h table_mod_parse.output rm -f table_mod_parse.c table_mod_parse.h table_mod_parse.output
rm -f table_mod_lexor.c rm -f table_mod_lexor.c
rm -f va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vpi_debug.vpi rm -f va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vhdl_textio.vpi vpi_debug.vpi
distclean: clean distclean: clean
rm -f Makefile config.log rm -f Makefile config.log
@ -163,6 +165,9 @@ va_math.vpi: $V ../vvp/libvpi.a
vhdl_sys.vpi: $(VHDL_SYS) ../vvp/libvpi.a vhdl_sys.vpi: $(VHDL_SYS) ../vvp/libvpi.a
$(CC) @shared@ -o $@ $(VHDL_SYS) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) $(CC) @shared@ -o $@ $(VHDL_SYS) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS)
vhdl_textio.vpi: $(VHDL_TEXTIO) ../vvp/libvpi.a
$(CC) @shared@ -o $@ $(VHDL_TEXTIO) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS)
vpi_debug.vpi: $(VPI_DEBUG) ../vvp/libvpi.a vpi_debug.vpi: $(VPI_DEBUG) ../vvp/libvpi.a
$(CC) @shared@ -o $@ $(VPI_DEBUG) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) $(CC) @shared@ -o $@ $(VPI_DEBUG) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS)
@ -177,6 +182,7 @@ install: all installdirs \
$(vpidir)/v2005_math.vpi $(vpidir)/v2005_math.sft \ $(vpidir)/v2005_math.vpi $(vpidir)/v2005_math.sft \
$(vpidir)/v2009.vpi $(vpidir)/v2009.sft \ $(vpidir)/v2009.vpi $(vpidir)/v2009.sft \
$(vpidir)/vhdl_sys.vpi $(vpidir)/vhdl_sys.sft \ $(vpidir)/vhdl_sys.vpi $(vpidir)/vhdl_sys.sft \
$(vpidir)/vhdl_textio.vpi $(vpidir)/vhdl_textio.sft \
$(vpidir)/vpi_debug.vpi $(vpidir)/vpi_debug.vpi
$(vpidir)/system.vpi: ./system.vpi $(vpidir)/system.vpi: ./system.vpi
@ -209,6 +215,12 @@ $(vpidir)/vhdl_sys.vpi: ./vhdl_sys.vpi
$(vpidir)/vhdl_sys.sft: vhdl_sys.sft $(vpidir)/vhdl_sys.sft: vhdl_sys.sft
$(INSTALL_DATA) $< "$(DESTDIR)$@" $(INSTALL_DATA) $< "$(DESTDIR)$@"
$(vpidir)/vhdl_textio.vpi: ./vhdl_textio.vpi
$(INSTALL_PROGRAM) ./vhdl_textio.vpi "$(DESTDIR)$(vpidir)/vhdl_textio.vpi"
$(vpidir)/vhdl_textio.sft: vhdl_textio.sft
$(INSTALL_DATA) $< "$(DESTDIR)$@"
$(vpidir)/vpi_debug.vpi: ./vpi_debug.vpi $(vpidir)/vpi_debug.vpi: ./vpi_debug.vpi
$(INSTALL_PROGRAM) ./vpi_debug.vpi "$(DESTDIR)$(vpidir)/vpi_debug.vpi" $(INSTALL_PROGRAM) ./vpi_debug.vpi "$(DESTDIR)$(vpidir)/vpi_debug.vpi"
@ -226,6 +238,8 @@ uninstall:
rm -f "$(DESTDIR)$(vpidir)/v2009.sft" rm -f "$(DESTDIR)$(vpidir)/v2009.sft"
rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.vpi" rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.vpi"
rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.sft" rm -f "$(DESTDIR)$(vpidir)/vhdl_sys.sft"
rm -f "$(DESTDIR)$(vpidir)/vhdl_textio.vpi"
rm -f "$(DESTDIR)$(vpidir)/vhdl_textio.sft"
rm -f "$(DESTDIR)$(vpidir)/vpi_debug.vpi" rm -f "$(DESTDIR)$(vpidir)/vpi_debug.vpi"
-include $(patsubst %.o, dep/%.d, $O) -include $(patsubst %.o, dep/%.d, $O)

943
vpi/vhdl_textio.c Normal file
View File

@ -0,0 +1,943 @@
/*
* Copyright (c) 2015 CERN
* @author Maciej Suminski <maciej.suminski@cern.ch>
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* The following VPI module implements some of the functions available
* in std.textio library.
*
* Type counterparts:
* VHDL SystemVerilog
* ------------------------------
* LINE string (line of text, in VHDL it is a pointer to string)
* TEXT int (file handle)
*
* Some of functions offered by std.textio library are not implemented here,
* as they can be directly replaced with SystemVerilog system functions.
*
* VHDL SystemVerilog
* --------------------------------------
* FILE_CLOSE(file F: TEXT) $fclose(fd)
* ENDFILE(file F: TEXT) $feof(fd)
*
* Procedures:
* HREAD (L: inout LINE; VALUE: out BIT_VECTOR)
* HWRITE (L: inout LINE; VALUE: out BIT_VECTOR)
* are handled with $ivlh_read/write() using FORMAT_HEX parameter (see format_t enum).
*/
# include "sys_priv.h"
# include "vpi_config.h"
# include "vpi_user.h"
# include <assert.h>
# include <string.h>
# include <ctype.h>
# include "ivl_alloc.h"
/* additional parameter values to distinguish between integer, boolean and
* time types or to use hex format */
enum format_t { FORMAT_STD, FORMAT_BOOL, FORMAT_TIME, FORMAT_HEX, FORMAT_STRING };
enum file_mode_t { FILE_MODE_READ, FILE_MODE_WRITE, FILE_MODE_APPEND, FILE_MODE_LAST };
/* bits per vector, in a single s_vpi_vecval struct */
static const size_t BPW = 8 * sizeof(PLI_INT32);
static int is_integer_var(vpiHandle obj)
{
PLI_INT32 type = vpi_get(vpiType, obj);
return (type == vpiIntegerVar || type == vpiShortIntVar ||
type == vpiIntVar || type == vpiLongIntVar);
}
static void show_error_line(vpiHandle callh) {
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
}
static void show_warning_line(vpiHandle callh) {
vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh),
(int)vpi_get(vpiLineNo, callh));
}
/* sets a single bit value in a bit/logic vector */
static int set_vec_val(s_vpi_vecval* vector, char value, int idx) {
s_vpi_vecval*v = &vector[idx / BPW];
PLI_INT32 bit = idx % BPW;
switch(toupper(value)) {
case '0':
v->bval &= ~(1 << bit);
v->aval &= ~(1 << bit);
break;
case '1':
v->bval &= ~(1 << bit);
v->aval |= (1 << bit);
break;
case 'Z':
v->bval |= (1 << bit);
v->aval &= ~(1 << bit);
break;
case 'X':
v->bval |= (1 << bit);
v->aval |= (1 << bit);
break;
default:
return 1;
}
return 0;
}
/* Converts a string of characters to a vector in s_vpi_value struct.
* Returns number of processed characters, 0 in case of failure.
* string is the data to be converted.
* val is the target s_vpi_value struct.
* var is the variable that is converted (to obtain size & type [2/4-state]).
*/
static int read_vector(const char *string, s_vpi_value *val, vpiHandle var)
{
#if 0
/* It could be easier to simply use val.format = vpiBinStrVal
* but there is no way to check if processing went fine */
val.format = vpiBinStrVal;
val.value.str = string;
processed_chars = size;
#endif
/* Vector size (==1 for scalars) */
int size = vpi_get(vpiSize, var);
/* Number of required s_vpi_vecval structs to store the result */
int words = (size + BPW - 1) / BPW; /* == ceil(size / BPW) */
int len = strlen(string);
val->format = vpiVectorVal; /* it also covers scalars */
val->value.vector = calloc(words, sizeof(s_vpi_vecval));
/* Skip spaces in the beginning */
int skipped = 0;
while(*string && *string == ' ') {
--len;
++string;
++skipped;
}
/* Process bits */
int p;
for(p = 0; p < size && p < len; ++p) {
if(set_vec_val(val->value.vector, string[p], size - p - 1)) {
free(val->value.vector); /* error */
return 0;
}
}
/* 2-logic variables cannot hold X or Z values, so change them to 0 */
if(vpi_get(vpiType, var) == vpiBitVar) {
for(int i = 0; i < words; ++i) {
val->value.vector[i].aval &= ~val->value.vector[i].bval;
val->value.vector[i].bval = 0;
}
}
return p + skipped;
}
/* Converts a string of characters to a time value, stored in vector filed of
* s_vpi_value struct.
* Returns number of processed characters, 0 in case of failure.
* string is the data to be converted.
* val is the target s_vpi_value struct.
* scope_unit is the time unit used in the scope (-3 for millisecond,
* -6 for microsecond, etc.)
*/
static int read_time(const char *string, s_vpi_value *val, PLI_INT32 scope_unit) {
PLI_UINT64 period;
char units[2];
int time_unit, processed_chars;
if(sscanf(string, "%ld %2s%n", &period, units, &processed_chars) != 2)
return 0;
if(!strncasecmp(units, "fs", 2))
time_unit = -15;
else if(!strncasecmp(units, "ps", 2))
time_unit = -12;
else if(!strncasecmp(units, "ns", 2))
time_unit = -9;
else if(!strncasecmp(units, "us", 2))
time_unit = -6;
else if(!strncasecmp(units, "ms", 2))
time_unit = -3;
else if(!strncasecmp(units, "s", 1))
time_unit = 0;
else
return 0;
/* Scale the time units to the one used in the scope */
int scale_diff = time_unit - scope_unit;
if(scale_diff > 0) {
for(int i = 0; i < scale_diff; ++i)
period *= 10;
} else {
for(int i = 0; i < -scale_diff; ++i)
period /= 10;
}
/* vpiTimeVal format is not handled at the moment,
* so return the read value as a vector*/
val->format = vpiVectorVal;
val->value.vector = calloc(2, sizeof(s_vpi_vecval));
memset(val->value.vector, 0, 2 * sizeof(s_vpi_vecval));
val->value.vector[1].aval = (PLI_UINT32) (period >> 32);
val->value.vector[0].aval = (PLI_UINT32) period;
return processed_chars;
}
static int read_string(const char *string, s_vpi_value *val) {
char buf[1024];
int processed_chars;
if(sscanf(string, "%1024s%n", buf, &processed_chars) != 1)
return 0;
val->format = vpiStringVal;
val->value.str = strndup(buf, processed_chars);
return processed_chars;
}
static int write_time(char *string, const s_vpi_value* val,
size_t width, PLI_INT32 scope_unit) {
char prefix = 0;
PLI_UINT64 period;
switch(val->format) {
case vpiIntVal:
period = val->value.integer;
break;
case vpiVectorVal:
period = val->value.vector[0].aval;
if(width > BPW)
period |= (PLI_UINT64)(val->value.vector[1].aval) << 32;
break;
default:
return 1;
}
/* Handle the case when the time unit base is 10 or 100 */
int remainder = scope_unit % -3;
if(remainder) {
remainder += 3;
scope_unit -= remainder;
while(remainder--)
period *= 10;
}
switch(scope_unit) {
case -15: prefix = 'f'; break;
case -12: prefix = 'p'; break;
case -9: prefix = 'n'; break;
case -6: prefix = 'u'; break;
case -3: prefix = 'm'; break;
}
if(prefix)
sprintf(string, "%ld %cs", period, prefix);
else
sprintf(string, "%ld s", period);
return 0;
}
/* slightly modified sys_fopen_compiletf */
static PLI_INT32 ivlh_file_open_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv;
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) {
show_error_line(callh);
vpi_printf("%s requires a string file name argument.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
if(!is_integer_var(vpi_scan(argv))) {
show_error_line(callh);
vpi_printf("%s's first argument has to be an integer variable (file handle).\n", name);
vpi_control(vpiFinish, 1);
}
if(!is_string_obj(vpi_scan(argv))) {
show_error_line(callh);
vpi_printf("%s's second argument argument must be a string (file name).\n", name);
vpi_control(vpiFinish, 1);
}
/* When provided, the type argument must be a string. */
if(!vpi_scan(argv)) {
show_error_line(callh);
vpi_printf("%s's third argument must be an integer (open mode).\n", name);
vpi_control(vpiFinish, 1);
}
/* Make sure there are no extra arguments. */
check_for_extra_args(argv, callh, name, "three arguments", 1);
return 0;
}
/* procedure FILE_MODE(file F: TEXT; External_Name; in STRING;
Open_Kind: in FILE_MODE_KIND := READ_MODE); */
/* slightly modified sys_fopen_calltf */
static PLI_INT32 ivlh_file_open_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
s_vpi_value val;
int mode;
char *fname;
vpiHandle fhandleh = vpi_scan(argv);
vpiHandle fnameh = vpi_scan(argv);
vpiHandle modeh = vpi_scan(argv);
/* Get the mode handle */
val.format = vpiIntVal;
vpi_get_value(modeh, &val);
mode = val.value.integer;
vpi_free_object(argv);
if(mode < 0 || mode >= FILE_MODE_LAST) {
show_error_line(callh);
vpi_printf("%s's file open mode argument is invalid.\n", name);
return 0;
}
fname = get_filename(callh, name, fnameh);
if(fname == 0) {
show_error_line(callh);
vpi_printf("%s's could not obtain the file name.\n", name);
return 0;
}
/* Open file and save the handle */
switch(mode) {
case FILE_MODE_READ:
val.value.integer = vpi_fopen(fname, "r");
break;
case FILE_MODE_WRITE:
val.value.integer = vpi_fopen(fname, "w");
break;
case FILE_MODE_APPEND:
val.value.integer = vpi_fopen(fname, "a");
break;
}
val.format = vpiIntVal;
vpi_put_value(fhandleh, &val, 0, vpiNoDelay);
free(fname);
return 0;
}
static PLI_INT32 ivlh_readwriteline_compiletf(ICARUS_VPI_CONST 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 an
integer (file handle) and that the second is string. */
if(argv == 0) {
show_error_line(callh);
vpi_printf("%s requires two arguments.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
arg = vpi_scan(argv);
if(!arg || !is_integer_var(arg)) {
show_error_line(callh);
vpi_printf("%s's first argument must be an integer variable (file handle).\n", name);
vpi_control(vpiFinish, 1);
}
arg = vpi_scan(argv);
if(!arg || !is_string_obj(arg)) {
show_error_line(callh);
vpi_printf("%s's second 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 arguments", 0);
return 0;
}
/* procedure READLINE (file F: TEXT; L: inout LINE); */
/* slightly modified sys_fgets_calltf */
static PLI_INT32 ivlh_readline_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle stringh, arg;
s_vpi_value val;
PLI_UINT32 fd;
FILE *fp;
char *text;
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
/* Get the file descriptor. */
arg = vpi_scan(argv);
val.format = vpiIntVal;
vpi_get_value(arg, &val);
fd = val.value.integer;
/* Get the string handle. */
stringh = vpi_scan(argv);
vpi_free_object(argv);
/* Return zero if this is not a valid fd. */
fp = vpi_get_file(fd);
if(!fp) {
show_warning_line(callh);
vpi_printf("invalid file descriptor (0x%x) given to %s.\n",
(unsigned int)fd, name);
return 0;
}
/* Read in the bytes. Return 0 if there was an error. */
if(fgets(buf, BUF_SIZE, fp) == 0) {
show_error_line(callh);
vpi_printf("%s reading past the end of file.\n", name);
return 0;
}
int len = strlen(buf);
if(len == 0) {
show_error_line(callh);
vpi_printf("%s read 0 bytes.\n", name);
return 0;
} else if(len == BUF_SIZE - 1) {
show_warning_line(callh);
vpi_printf("%s has reached the buffer limit, part of the "
"processed string might have been skipped.\n", name);
}
/* Return the characters to the register. */
text = strndup(buf, len - 1); /* skip the newline character */
val.format = vpiStringVal;
val.value.str = text;
vpi_put_value(stringh, &val, 0, vpiNoDelay);
free(text);
return 0;
}
/* procedure WRITELINE (file F: TEXT; L: inout LINE); */
/* slightly modified sys_fgets_calltf */
static PLI_INT32 ivlh_writeline_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle stringh, arg;
s_vpi_value val;
PLI_UINT32 fd;
FILE *fp;
char *empty;
/* Get the file descriptor. */
arg = vpi_scan(argv);
val.format = vpiIntVal;
vpi_get_value(arg, &val);
fd = val.value.integer;
/* Get the string contents. */
stringh = vpi_scan(argv);
val.format = vpiStringVal;
vpi_get_value(stringh, &val);
vpi_free_object(argv);
/* Return zero if this is not a valid fd. */
fp = vpi_get_file(fd);
if(!fp) {
show_warning_line(callh);
vpi_printf("invalid file descriptor (0x%x) given to %s.\n",
(unsigned int)fd, name);
return 0;
}
fprintf(fp, "%s\n", val.value.str);
/* Clear the written string */
empty = strdup("");
val.format = vpiStringVal;
val.value.str = empty;
vpi_put_value(stringh, &val, 0, vpiNoDelay);
free(empty);
return 0;
}
static PLI_INT32 ivlh_read_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
if(argv == 0) {
show_error_line(callh);
vpi_printf("%s requires three arguments.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
arg = vpi_scan(argv);
if(!arg || !is_string_obj(arg)) {
show_error_line(callh);
vpi_printf("%s's first argument must be a string.\n", name);
vpi_control(vpiFinish, 1);
}
arg = vpi_scan(argv);
if(!arg || is_constant_obj(arg)) {
show_error_line(callh);
vpi_printf("%s's second argument must be a variable.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
arg = vpi_scan(argv);
if(!arg) {
show_error_line(callh);
vpi_printf("%s's third argument must be an integer.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
/* Make sure there are no extra arguments. */
check_for_extra_args(argv, callh, name, "three arguments", 0);
return 0;
}
/* procedure READ (L: inout LINE;
VALUE: out BIT/BIT_VECTOR/BOOLEAN/CHARACTER/INTEGER/REAL/STRING/TIME); */
static PLI_INT32 ivlh_read_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle stringh, varh, formath;
s_vpi_value val;
PLI_INT32 type, format;
char *string = 0;
int processed_chars = 0, fail = 0;
/* Get the string */
stringh = vpi_scan(argv);
val.format = vpiStringVal;
vpi_get_value(stringh, &val);
/* Get the destination variable */
varh = vpi_scan(argv);
type = vpi_get(vpiType, varh);
if(strlen(val.value.str) == 0) {
show_error_line(callh);
vpi_printf("%s cannot read from an empty string.\n", name);
return 0;
}
string = strdup(val.value.str);
/* Get the format (see enum format_t) */
formath = vpi_scan(argv);
val.format = vpiIntVal;
vpi_get_value(formath, &val);
format = val.value.integer;
vpi_free_object(argv);
switch(format) {
case FORMAT_STD:
switch(type) {
/* TODO longint is 64-bit, so it has to be handled by vector */
/*case vpiLongIntVar:*/
case vpiShortIntVar:
case vpiIntVar:
case vpiByteVar:
case vpiIntegerVar:
val.format = vpiIntVal;
if(sscanf(string, "%d%n", &val.value.integer, &processed_chars) != 1)
fail = 1;
break;
case vpiBitVar: /* bit, bit vector */
case vpiLogicVar: /* ==vpiReg time, logic, logic vector */
processed_chars = read_vector(string, &val, varh);
break;
case vpiRealVar:
val.format = vpiRealVal;
if(sscanf(string, "%lf%n", &val.value.real, &processed_chars) != 1)
fail = 1;
break;
case vpiStringVar:
processed_chars = read_string(string, &val);
break;
default:
fail = 1;
show_warning_line(callh);
vpi_printf("%s does not handle such type (%d).\n", name, type);
break;
}
break;
case FORMAT_BOOL:
{
char buf[5];
val.format = vpiIntVal;
if(sscanf(string, "%5s%n", buf, &processed_chars) == 1)
{
if(!strncasecmp(buf, "true", 4))
val.value.integer = 1;
else if(!strncasecmp(buf, "false", 5))
val.value.integer = 0;
else
fail = 1;
}
}
break;
case FORMAT_TIME:
val.format = vpiIntVal;
processed_chars = read_time(string, &val, vpi_get(vpiTimeUnit, callh));
break;
case FORMAT_HEX:
val.format = vpiIntVal;
if(sscanf(string, "%x%n", &val.value.integer, &processed_chars) != 1)
fail = 1;
break;
case FORMAT_STRING:
processed_chars = read_string(string, &val);
break;
}
if(processed_chars == 0) {
show_error_line(callh);
vpi_printf("%s could not read a valid value.\n", name);
fail = 1;
} else if(val.format == vpiStringVar && processed_chars == 1024) {
show_warning_line(callh);
vpi_printf("%s has reached the buffer limit, part of the "
"processed string might have been skipped.\n", name);
}
if(!fail) {
assert(processed_chars > 0);
/* Store the read value */
vpi_put_value(varh, &val, 0, vpiNoDelay);
/* Clean up */
if(val.format == vpiStringVar)
free(val.value.str);
else if(val.format == vpiVectorVal)
free(val.value.vector);
/* Strip the read token from the string */
char* tmp = strdup(&string[processed_chars]);
val.format = vpiStringVal;
val.value.str = tmp;
vpi_put_value(stringh, &val, 0, vpiNoDelay);
free(tmp);
} else {
show_error_line(callh);
vpi_printf("%s failed.\n", name);
/*vpi_control(vpiFinish, 1);*/
}
free(string);
return 0;
}
static PLI_INT32 ivlh_write_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle arg;
if(argv == 0) {
show_error_line(callh);
vpi_printf("%s requires three arguments.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
arg = vpi_scan(argv);
if(!arg || !is_string_obj(arg)) {
show_error_line(callh);
vpi_printf("%s's first argument must be a string.\n", name);
vpi_control(vpiFinish, 1);
}
arg = vpi_scan(argv);
if(!arg) {
show_error_line(callh);
vpi_printf("%s requires three arguments.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
arg = vpi_scan(argv);
if(!arg) {
show_error_line(callh);
vpi_printf("%s's third argument must be an integer.\n", name);
vpi_control(vpiFinish, 1);
return 0;
}
/* Make sure there are no extra arguments. */
check_for_extra_args(argv, callh, name, "three arguments", 0);
return 0;
}
/*procedure WRITE (L: inout LINE;
VALUE: in BIT/BIT_VECTOR/BOOLEAN/CHARACTER/INTEGER/REAL/STRING/TIME);
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0); */
/* JUSTIFIED & FIELD are not handled at the moment */
static PLI_INT32 ivlh_write_calltf(ICARUS_VPI_CONST PLI_BYTE8*name)
{
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, callh);
vpiHandle stringh, varh, formath;
s_vpi_value val;
PLI_INT32 type, format;
char *string = 0;
int fail = 0, res = 0;
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
/* Get the string */
stringh = vpi_scan(argv);
val.format = vpiStringVal;
vpi_get_value(stringh, &val);
string = strdup(val.value.str);
/* Get the destination variable */
varh = vpi_scan(argv);
type = vpi_get(vpiType, varh);
/* Get the format (see enum format_t) */
formath = vpi_scan(argv);
val.format = vpiIntVal;
vpi_get_value(formath, &val);
format = val.value.integer;
vpi_free_object(argv);
/* Convert constant types to variable types */
if(type == vpiConstant) {
type = vpi_get(vpiConstType, varh);
switch(type) {
case vpiRealConst:
type = vpiRealVar;
break;
case vpiStringConst:
type = vpiStringVar;
break;
case vpiDecConst:
case vpiBinaryConst:
case vpiOctConst:
case vpiHexConst:
type = vpiIntVar;
break;
}
}
switch(format) {
case FORMAT_STD:
switch(type) {
/* TODO longint is 64-bit, so it has to be handled by vector */
/*case vpiLongIntVar:*/
case vpiShortIntVar:
case vpiIntVar:
case vpiByteVar:
case vpiIntegerVar:
val.format = vpiIntVal;
vpi_get_value(varh, &val);
res = snprintf(buf, BUF_SIZE, "%s%d", string, val.value.integer);
break;
case vpiBitVar: /* bit, bit vector */
case vpiLogicVar: /* ==vpiReg time, logic, logic vector */
val.format = vpiBinStrVal;
vpi_get_value(varh, &val);
/* VHDL stores X/Z values uppercase, so follow the rule */
for(size_t i = 0; i< strlen(val.value.str); ++i)
val.value.str[i] = toupper(val.value.str[i]);
res = snprintf(buf, BUF_SIZE, "%s%s", string, val.value.str);
break;
case vpiRealVar:
val.format = vpiRealVal;
vpi_get_value(varh, &val);
res = snprintf(buf, BUF_SIZE, "%s%lf", string, val.value.real);
break;
case vpiStringVar:
val.format = vpiStringVal;
vpi_get_value(varh, &val);
res = snprintf(buf, BUF_SIZE, "%s%s", string, val.value.str);
break;
default:
fail = 1;
show_warning_line(callh);
vpi_printf("%s does not handle such type (%d).\n", name, type);
break;
}
break;
case FORMAT_BOOL:
val.format = vpiIntVal;
vpi_get_value(varh, &val);
res = snprintf(buf, BUF_SIZE, "%s%s", string,
val.value.integer ? "TRUE" : "FALSE");
break;
case FORMAT_TIME:
{
char tmp[64];
val.format = vpiIntVal;
vpi_get_value(varh, &val);
if(write_time(tmp, &val, vpi_get(vpiSize, varh), vpi_get(vpiTimeUnit, callh))) {
fail = 1;
break;
}
res = snprintf(buf, BUF_SIZE, "%s%s", string, tmp);
}
break;
case FORMAT_HEX:
val.format = vpiIntVal;
vpi_get_value(varh, &val);
res = snprintf(buf, BUF_SIZE, "%s%X", string, val.value.integer);
break;
case FORMAT_STRING:
val.format = vpiStringVal;
vpi_get_value(varh, &val);
res = snprintf(buf, BUF_SIZE, "%s%s", string, val.value.str);
break;
}
if(res > BUF_SIZE)
fail = 1;
if(!fail) {
/* Strip the read token from the string */
char* tmp = strndup(buf, BUF_SIZE);
val.format = vpiStringVal;
val.value.str = tmp;
vpi_put_value(stringh, &val, 0, vpiNoDelay);
free(tmp);
} else {
show_error_line(callh);
vpi_printf("%s failed.\n", name);
/*vpi_control(vpiFinish, 1);*/
}
free(string);
return 0;
}
static void vhdl_register(void)
{
vpiHandle res;
s_vpi_systf_data tf_data[] = {
{ vpiSysTask, 0, "$ivlh_file_open",
ivlh_file_open_calltf, ivlh_file_open_compiletf, 0,
"$ivlh_file_open" },
{ vpiSysTask, 0, "$ivlh_readline",
ivlh_readline_calltf, ivlh_readwriteline_compiletf, 0,
"$ivlh_readline" },
{ vpiSysTask, 0, "$ivlh_writeline",
ivlh_writeline_calltf, ivlh_readwriteline_compiletf, 0,
"$ivlh_writeline" },
{ vpiSysTask, 0, "$ivlh_read",
ivlh_read_calltf, ivlh_read_compiletf, 0,
"$ivlh_read" },
{ vpiSysTask, 0, "$ivlh_write",
ivlh_write_calltf, ivlh_write_compiletf, 0,
"$ivlh_write" },
};
for(unsigned int i = 0; i < sizeof(tf_data) / sizeof(s_vpi_systf_data); ++i) {
res = vpi_register_systf(&tf_data[i]);
vpip_make_systf_system_defined(res);
}
}
void (*vlog_startup_routines[])(void) = {
vhdl_register,
0
};

9
vpi/vhdl_textio.sft Normal file
View File

@ -0,0 +1,9 @@
$ivlh_file_open vpiSysFuncVoid
$ivlh_readline vpiSysFuncVoid
$ivlh_writeline vpiSysFuncVoid
$ivlh_read vpiSysFuncVoid
$ivlh_write vpiSysFuncVoid
$ivlh_hread vpiSysFuncVoid
$ivlh_hwrite vpiSysFuncVoid