Extract system function return types from VPI modules, not SFT files.

This commit is contained in:
Martin Whitaker 2019-10-21 13:40:40 +01:00
parent df38460d26
commit 9f93989944
6 changed files with 315 additions and 39 deletions

View File

@ -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)

View File

@ -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

View File

@ -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<feature>]\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);

View File

@ -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<perm_string,unsigned> 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;

View File

@ -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:

216
vpi_modules.cc Normal file
View File

@ -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;
}