diff --git a/Makefile.in b/Makefile.in index 986427846..78c2fc808 100644 --- a/Makefile.in +++ b/Makefile.in @@ -117,7 +117,7 @@ O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ net_udp.o pad_to_width.o parse.o parse_misc.o pform.o pform_analog.o \ pform_disciplines.o pform_dump.o pform_package.o pform_pclass.o \ pform_class_type.o pform_string_type.o pform_struct_type.o pform_types.o \ - symbol_search.o sync.o sys_funcs.o verinum.o verireal.o target.o \ + symbol_search.o sync.o sys_funcs.o verinum.o verireal.o vpi_modules.o target.o \ Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PFunction.o \ PGate.o PGenerate.o PModport.o PNamedItem.o PPackage.o PScope.o PSpec.o \ PTask.o PUdp.o PWire.o Statement.o AStatement.o $M $(FF) $(TT) diff --git a/compiler.h b/compiler.h index 593955e65..441ad11d9 100644 --- a/compiler.h +++ b/compiler.h @@ -290,9 +290,16 @@ struct sfunc_return_type { bool override_flag; }; +extern void add_sys_func(const struct sfunc_return_type&ret_type); extern const struct sfunc_return_type* lookup_sys_func(const char*name); extern int load_sys_func_table(const char*path); extern void cleanup_sys_func_table(); +/* + * This temporarily loads a VPI module, to determine the return values + * of system functions provided by that module, and adds the return values + * to the system function table. + */ +extern bool load_vpi_module(const char*path); /* * In system Verilog it is allowed with a warning to call a function diff --git a/driver/main.c b/driver/main.c index 427c9d499..e80df6e21 100644 --- a/driver/main.c +++ b/driver/main.c @@ -40,7 +40,7 @@ const char NOTICE[] = const char HELP[] = "Usage: iverilog [-EiSuvV] [-B base] [-c cmdfile|-f cmdfile]\n" " [-g1995|-g2001|-g2005|-g2005-sv|-g2009|-g2012] [-g]\n" -" [-D macro[=defn]] [-I includedir]\n" +" [-D macro[=defn]] [-I includedir] [-L moduledir]\n" " [-M [mode=]depfile] [-m module]\n" " [-N file] [-o filename] [-p flag=value]\n" " [-s topmodule] [-t target] [-T min|typ|max]\n" @@ -169,6 +169,9 @@ char*compiled_defines_path = 0; static char iconfig_common_path[4096] = ""; +static const char**vpi_path_list = 0; +static unsigned vpi_path_list_size = 0; + int synth_flag = 0; int verbose_flag = 0; @@ -673,14 +676,19 @@ void process_timescale(const char*ts_string) /* * This function is called while processing a file name in a command * file, or a file name on the command line. Look to see if there is a - * .sft suffix, and if so pass that as a sys_func file. Otherwise, it - * is a Verilog source file to be written into the file list. + * .sft or .vpi suffix, and if so pass that as a sys_func or module + * file. Otherwise, it is a Verilog source file to be written into the + * file list. */ void process_file_name(const char*name, int lib_flag) { if (strlen(name) > 4 && strcasecmp(".sft", name+strlen(name)-4) == 0) { + fprintf(stderr, "SFT files are deprecated. Please pass the VPI module instead.\n"); fprintf(iconfig_file,"sys_func:%s\n", name); + } else if (strlen(name) > 4 && strcasecmp(".vpi", name+strlen(name)-4) == 0) { + fprintf(iconfig_file,"module:%s\n", name); + } else { fprintf(source_file, "%s\n", name); source_count += 1; @@ -857,24 +865,54 @@ static int process_depfile(const char*name) } /* - * If it exists add the SFT file for the given module. + * If it exists add the VPI file for the given module. */ -static void add_sft_file(const char *module) +static void add_vpi_file(const char *name) { - char *file; + char path[4096]; - file = (char *) malloc(strlen(base)+1+strlen(module)+4+1); - // If the module name has at least one directory character - // in it, assume it includes the path, otherwise look in - // the base directory. - if (strchr(module, sep)) - sprintf(file, "%s.sft", module); - else - sprintf(file, "%s%c%s.sft", base, sep, module); - - if (access(file, R_OK) == 0) - fprintf(iconfig_file, "sys_func:%s\n", file); - free(file); + int found = 0; + if (strchr(name, sep)) { + /* If the name has at least one directory character in it + then assume it is a complete name, maybe including any + possible .vpi or .vpl suffix. */ + found = access(name, R_OK) == 0; + if (!found) { + snprintf(path, sizeof(path), "%s.vpi", name); + found = access(path, R_OK) == 0; + if (!found) { + snprintf(path, sizeof(path), "%s.vpl", name); + found = access(path, R_OK) == 0; + } + if (!found) + fprintf(stderr, "Unable to find VPI module '%s'\n", name); + } else { + strncpy(path, name, sizeof(path) - 1); + } + } else { + for (unsigned idx = 0; idx < vpi_path_list_size; idx += 1) { + snprintf(path, sizeof(path), "%s%c%s.vpi", + vpi_path_list[idx], sep, name); + found = access(path, R_OK) == 0; + if (found) break; + snprintf(path, sizeof(path), "%s%c%s.vpl", + vpi_path_list[idx], sep, name); + found = access(path, R_OK) == 0; + if (found) break; + } + if (!found) { + snprintf(path, sizeof(path), "%s%c%s.vpi", base, sep, name); + found = access(path, R_OK) == 0; + } + if (!found) { + snprintf(path, sizeof(path), "%s%c%s.vpl", base, sep, name); + found = access(path, R_OK) == 0; + } + if (!found) + fprintf(stderr, "Unable to find VPI module '%s' on the search path.\n", name); + } + if (found) + fprintf(iconfig_file, "module:%s\n", path); } static void find_ivl_root_failed(const char *reason) @@ -1039,7 +1077,7 @@ int main(int argc, char **argv) } } - while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hl:I:iM:m:N:o:P:p:Ss:T:t:uvVW:y:Y:")) != EOF) { + while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hl:I:iL:M:m:N:o:P:p:Ss:T:t:uvVW:y:Y:")) != EOF) { switch (opt) { case 'B': @@ -1098,6 +1136,13 @@ int main(int argc, char **argv) ignore_missing_modules = 1; break; + case 'L': + vpi_path_list_size += 1; + vpi_path_list = (const char**)realloc(vpi_path_list, + vpi_path_list_size*sizeof(char*)); + vpi_path_list[vpi_path_list_size-1] = optarg; + break; + case 'l': process_file_name(optarg, 1); break; @@ -1108,8 +1153,7 @@ int main(int argc, char **argv) break; case 'm': - fprintf(iconfig_file, "module:%s\n", optarg); - add_sft_file(optarg); + add_vpi_file(optarg); break; case 'N': @@ -1198,12 +1242,10 @@ int main(int argc, char **argv) /* Write values to the iconfig file. */ fprintf(iconfig_file, "basedir:%s\n", base); - /* Tell the core where to find the system.sft. This file - describes the system functions so that elaboration knows - 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); + /* Tell the core where to find the system VPI modules. */ + fprintf(iconfig_file, "module:%s%csystem.vpi\n", base, sep); + fprintf(iconfig_file, "module:%s%cvhdl_sys.vpi\n", base, sep); + fprintf(iconfig_file, "module:%s%cvhdl_textio.vpi\n", base, sep); /* If verilog-2005/09/12 is enabled or icarus-misc or verilog-ams, * then include the v2005_math library. */ @@ -1212,23 +1254,20 @@ int main(int argc, char **argv) strcmp(generation, "2012") == 0 || strcmp(gen_icarus, "icarus-misc") == 0 || strcmp(gen_verilog_ams, "verilog-ams") == 0) { - fprintf(iconfig_file, "sys_func:%s%cv2005_math.sft\n", base, sep); - fprintf(iconfig_file, "module:v2005_math\n"); + fprintf(iconfig_file, "module:%s%cv2005_math.vpi\n", base, sep); } /* If verilog-ams or icarus_misc is enabled, then include the * va_math module as well. */ if (strcmp(gen_verilog_ams,"verilog-ams") == 0 || strcmp(gen_icarus, "icarus-misc") == 0) { - fprintf(iconfig_file, "sys_func:%s%cva_math.sft\n", base, sep); - fprintf(iconfig_file, "module:va_math\n"); + fprintf(iconfig_file, "module:%s%cva_math.vpi\n", base, sep); } /* If verilog-2009 (SystemVerilog) is enabled, then include the v2009 module. */ if (strcmp(generation, "2005-sv") == 0 || strcmp(generation, "2009") == 0 || strcmp(generation, "2012") == 0) { - fprintf(iconfig_file, "sys_func:%s%cv2009.sft\n", base, sep); - fprintf(iconfig_file, "module:v2009\n"); + fprintf(iconfig_file, "module:%s%cv2009.vpi\n", base, sep); } if (mtm != 0) fprintf(iconfig_file, "-T:%s\n", mtm); diff --git a/main.cc b/main.cc index 60ec62e7e..6dc4988de 100644 --- a/main.cc +++ b/main.cc @@ -136,6 +136,7 @@ void add_vpi_module(const char*name) vpi_module_list = tmp; } flags["VPI_MODULE_LIST"] = vpi_module_list; + load_vpi_module(name); } map missing_modules; @@ -900,11 +901,6 @@ int main(int argc, char*argv[]) } library_suff.push_back(strdup(".v")); - // 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; min_typ_max_warn = 10; diff --git a/sys_funcs.cc b/sys_funcs.cc index a39585c70..87edc0255 100644 --- a/sys_funcs.cc +++ b/sys_funcs.cc @@ -104,6 +104,24 @@ const struct sfunc_return_type* lookup_sys_func(const char*name) return sfunc_table + idx; } +void add_sys_func(const struct sfunc_return_type&ret_type) +{ + struct sfunc_return_type*def = find_in_sys_func_list(ret_type.name); + if (def) { + /* Keep the original definition, but flag that it + overrides a later definition. */ + def->override_flag = true; + return; + } + struct sfunc_return_type_cell*cell = new struct sfunc_return_type_cell; + cell->name = lex_strings.add(ret_type.name); + cell->type = ret_type.type; + cell->wid = ret_type.wid; + cell->signed_flag = ret_type.signed_flag; + cell->override_flag = ret_type.override_flag; + append_to_list(cell); +} + /* * This function loads a system functions descriptor file with the * format: diff --git a/vpi_modules.cc b/vpi_modules.cc new file mode 100644 index 000000000..ec75d6f4f --- /dev/null +++ b/vpi_modules.cc @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2019 Martin Whitaker (icarus@martin-whitaker.me.uk) + * + * 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. + */ + +#include "config.h" +#include "compiler.h" +#include "vpi_user.h" +#include "sv_vpi_user.h" +#include "vvp/ivl_dlfcn.h" + +/* The only VPI routines that can be legally called when the functions in + the vlog_startup_routines[] array are executed are vpi_register_systf() + and vpi_register_cb(), so we can simply provide stubs for the rest. We + aren't going to execute any callbacks, so we can just provide a stub for + vpi_register_cb() too. + + Note that the Icarus system module illegally calls vpi_get_vlog_info() + during startup, so take care to fill in the data structure for that. +*/ + +// callback related + +vpiHandle vpi_register_cb(p_cb_data) { return 0; } +PLI_INT32 vpi_remove_cb(vpiHandle) { return 0; } + +void vpi_get_systf_info(vpiHandle, p_vpi_systf_data) { } + +// for obtaining handles + +vpiHandle vpi_handle_by_name(const char*, vpiHandle) { return 0; } +vpiHandle vpi_handle_by_index(vpiHandle, PLI_INT32) { return 0; } + +// for traversing relationships + +vpiHandle vpi_handle(PLI_INT32, vpiHandle) { return 0; } +vpiHandle vpi_iterate(PLI_INT32, vpiHandle) { return 0; } +vpiHandle vpi_scan(vpiHandle) { return 0; } + +// for processing properties + +PLI_INT32 vpi_get(int, vpiHandle) { return 0; } +char* vpi_get_str(PLI_INT32, vpiHandle) { return 0; } + +// delay processing + +void vpi_get_delays(vpiHandle, p_vpi_delay) { } +void vpi_put_delays(vpiHandle, p_vpi_delay) { } + +// value processing + +void vpi_get_value(vpiHandle, p_vpi_value) { } +vpiHandle vpi_put_value(vpiHandle, p_vpi_value, p_vpi_time, PLI_INT32) { return 0; } + +// time processing + +void vpi_get_time(vpiHandle, s_vpi_time*) { } + +// data processing + +void* vpi_get_userdata(vpiHandle) { return 0; } +PLI_INT32 vpi_put_userdata(vpiHandle, void*) { return 0; } + +// I/O routines + +PLI_UINT32 vpi_mcd_open(char *) { return 0; } +PLI_UINT32 vpi_mcd_close(PLI_UINT32) { return 0; } +PLI_INT32 vpi_mcd_flush(PLI_UINT32) { return 0; } +char* vpi_mcd_name(PLI_UINT32) { return 0; } +PLI_INT32 vpi_mcd_printf(PLI_UINT32, const char*, ...) { return 0; } +PLI_INT32 vpi_mcd_vprintf(PLI_UINT32, const char*, va_list) { return 0; } + +PLI_INT32 vpi_flush(void) { return 0; } +PLI_INT32 vpi_printf(const char*, ...) { return 0; } +PLI_INT32 vpi_vprintf(const char*, va_list) { return 0; } + +// utility routines + +PLI_INT32 vpi_chk_error(p_vpi_error_info) { return 0; } +PLI_INT32 vpi_compare_objects(vpiHandle, vpiHandle) { return 0; } +PLI_INT32 vpi_free_object(vpiHandle) { return 0; } +PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info info) +{ + info->argc = 0; + info->argv = 0; + info->product = 0; + info->version = 0; + return 0; +} + +// control routines + +void vpi_control(PLI_INT32, ...) { } +void vpi_sim_control(PLI_INT32, ...) { } + +// proposed standard extensions + +PLI_INT32 vpi_fopen(const char*, const char*) { return 0; } +FILE* vpi_get_file(PLI_INT32) { return 0; } + +// Icarus extensions + +s_vpi_vecval vpip_calc_clog2(vpiHandle) +{ + s_vpi_vecval val = { 0, 0 }; + return val; +} +void vpip_count_drivers(vpiHandle, unsigned, unsigned [4]) { } +void vpip_format_strength(char*, s_vpi_value*, unsigned) { } +void vpip_make_systf_system_defined(vpiHandle) { } +void vpip_mcd_rawwrite(PLI_UINT32, const char*, size_t) { } +void vpip_set_return_value(int) { } + + +/* When a module registers a system function, extract and save the return + type for use during elaboration. */ +vpiHandle vpi_register_systf(const struct t_vpi_systf_data*ss) +{ + if (ss->type != vpiSysFunc) + return 0; + + struct sfunc_return_type ret_type; + ret_type.name = ss->tfname; + switch (ss->sysfunctype) { + case vpiIntFunc: + ret_type.type = IVL_VT_LOGIC; + ret_type.wid = 32; + ret_type.signed_flag = true; + break; + case vpiRealFunc: + ret_type.type = IVL_VT_REAL; + ret_type.wid = 1; + ret_type.signed_flag = true; + break; + case vpiTimeFunc: + ret_type.type = IVL_VT_LOGIC; + ret_type.wid = 64; + ret_type.signed_flag = false; + break; + case vpiSizedFunc: + ret_type.type = IVL_VT_LOGIC; + ret_type.wid = ss->sizetf ? ss->sizetf(ss->user_data) : 32; + ret_type.signed_flag = false; + break; + case vpiSizedSignedFunc: + ret_type.type = IVL_VT_LOGIC; + ret_type.wid = ss->sizetf ? ss->sizetf(ss->user_data) : 32; + ret_type.signed_flag = true; + break; + case vpiStringFunc: + ret_type.type = IVL_VT_STRING; + ret_type.wid = 0; + ret_type.signed_flag = false; + break; + case vpiOtherFunc: + ret_type.type = IVL_VT_NO_TYPE; + ret_type.wid = 0; + ret_type.signed_flag = false; + break; + default: + cerr << "warning: " << ss->tfname << " has an unknown return type. " + "Assuming 32 bit unsigned." << endl; + ret_type.type = IVL_VT_LOGIC; + ret_type.wid = 32; + ret_type.signed_flag = false; + break; + } + ret_type.override_flag = false; + add_sys_func(ret_type); + return 0; +} + +typedef void (*vlog_startup_routines_t)(void); + +bool load_vpi_module(const char*path) +{ + ivl_dll_t dll = ivl_dlopen(path, false); + if (dll == 0) { + cerr << "error: Failed to open '" << path << "' because:" << endl; + cerr << " : " << dlerror() << endl; + return false; + } + +#ifdef __MINGW32__ + void*table = ivl_dlsym(dll, "vlog_startup_routines"); +#else + void*table = ivl_dlsym(dll, LU "vlog_startup_routines" TU); +#endif + if (table == 0) { + cerr << "warning: '" << path << "' has no vlog_startup_routines" << endl; + ivl_dlclose(dll); + return true; + } + + vlog_startup_routines_t*routines = (vlog_startup_routines_t*)table; + for (unsigned idx = 0; routines[idx]; idx += 1) { + (routines[idx])(); + } + + ivl_dlclose(dll); + return true; +}