From b414733f34785add32d61686b467aecc36918ae8 Mon Sep 17 00:00:00 2001 From: Maciej Suminski Date: Fri, 18 Sep 2015 11:23:46 +0200 Subject: [PATCH] vhdlpp: std.textio & ieee.std_logic_textio functions implemented using VPI. --- driver/main.c | 1 + main.cc | 1 + verilog.spec | 2 + vpi/Makefile.in | 18 +- vpi/vhdl_textio.c | 943 ++++++++++++++++++++++++++++++++++++++++++++ vpi/vhdl_textio.sft | 9 + 6 files changed, 972 insertions(+), 2 deletions(-) create mode 100644 vpi/vhdl_textio.c create mode 100644 vpi/vhdl_textio.sft diff --git a/driver/main.c b/driver/main.c index 810a2de90..2990d4706 100644 --- a/driver/main.c +++ b/driver/main.c @@ -1072,6 +1072,7 @@ int main(int argc, char **argv) how to handle them. */ 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_textio.sft\n", base, sep); /* If verilog-2005/09/12 is enabled or icarus-misc or verilog-ams, * then include the v2005_math library. */ diff --git a/main.cc b/main.cc index 1b11f4f87..66dd10765 100644 --- a/main.cc +++ b/main.cc @@ -832,6 +832,7 @@ int main(int argc, char*argv[]) // Start the module list with the base system module. add_vpi_module("system"); add_vpi_module("vhdl_sys"); + add_vpi_module("vhdl_textio"); flags["-o"] = strdup("a.out"); min_typ_max_flag = TYP; diff --git a/verilog.spec b/verilog.spec index 97c962b14..97a0e3c1a 100644 --- a/verilog.spec +++ b/verilog.spec @@ -102,6 +102,8 @@ rm -rf $RPM_BUILD_ROOT %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.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}/cadpli.vpl %attr(-,root,root) %{_libdir}/libvpi%{suff}.a diff --git a/vpi/Makefile.in b/vpi/Makefile.in index 30e537784..4a5502902 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -77,9 +77,11 @@ V2009 = v2009_table.o v2009_array.o v2009_enum.o v2009_string.o VHDL_SYS = vhdl_table.o +VHDL_TEXTIO = vhdl_textio.o sys_priv.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 @@ -88,7 +90,7 @@ clean: 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_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 rm -f Makefile config.log @@ -163,6 +165,9 @@ va_math.vpi: $V ../vvp/libvpi.a vhdl_sys.vpi: $(VHDL_SYS) ../vvp/libvpi.a $(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 $(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)/v2009.vpi $(vpidir)/v2009.sft \ $(vpidir)/vhdl_sys.vpi $(vpidir)/vhdl_sys.sft \ + $(vpidir)/vhdl_textio.vpi $(vpidir)/vhdl_textio.sft \ $(vpidir)/vpi_debug.vpi $(vpidir)/system.vpi: ./system.vpi @@ -209,6 +215,12 @@ $(vpidir)/vhdl_sys.vpi: ./vhdl_sys.vpi $(vpidir)/vhdl_sys.sft: vhdl_sys.sft $(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 $(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)/vhdl_sys.vpi" 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" -include $(patsubst %.o, dep/%.d, $O) diff --git a/vpi/vhdl_textio.c b/vpi/vhdl_textio.c new file mode 100644 index 000000000..f770123a8 --- /dev/null +++ b/vpi/vhdl_textio.c @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2015 CERN + * @author Maciej Suminski + * + * 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 +# include +# include +# 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 +}; diff --git a/vpi/vhdl_textio.sft b/vpi/vhdl_textio.sft new file mode 100644 index 000000000..f2fe46cf1 --- /dev/null +++ b/vpi/vhdl_textio.sft @@ -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