diff -Nru iverilog-10.3/aclocal.m4 iverilog-11.0/aclocal.m4 --- iverilog-10.3/aclocal.m4 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/aclocal.m4 2020-09-26 22:44:25.000000000 +0000 @@ -23,11 +23,13 @@ # ------------------------------ # Sub-macro for AX_C_UNDERSCORES_LEADING and AX_C_UNDERSCORES_TRAILING. # Unwarranted assumptions: -# - the object file produced by AC_COMPILE_IFELSE is called "conftest.$ac_objext" -# - the nm(1) utility is available, and its name is "nm". +# - the object file produced by AC_COMPILE_IFELSE is called +# "conftest.$ac_objext" +# - the nm(1) utility or an equivalent is available, and its name +# is defined by the $NM variable. AC_DEFUN([_AX_C_UNDERSCORES_MATCH_IF], [AC_COMPILE_IFELSE([AC_LANG_SOURCE([void underscore(void){}])], -[AS_IF([nm conftest.$ac_objext|grep $1 >/dev/null 2>/dev/null],[$2],[$3])], +[AS_IF([$NM conftest.$ac_objext|grep $1 >/dev/null 2>/dev/null],[$2],[$3])], [AC_MSG_ERROR([underscore test crashed])] )]) @@ -225,7 +227,9 @@ [# On MinGW we need to jump through hoops to get a C99 compliant strtod(). # mingw-w64 doesn't need this, and the 64-bit version doesn't support it. case "${host}" in - *-pc-mingw32) + x86_64-w64-mingw32) + ;; + *-*-mingw32) LDFLAGS+=" -Wl,--undefined=___strtod,--wrap,strtod,--defsym,___wrap_strtod=___strtod" ;; esac @@ -244,3 +248,130 @@ _stamp_name=stamp-`expr //$_config_header : '.*/\([[^./]]*\)\.[[^./]]*$'`-h echo "timestamp for $_config_header" > `AS_DIRNAME(["$_config_header"])`/[]$_stamp_name ]) #_AC_AM_CONFIG_HEADER_HOOK + +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_CC_FOR_BUILD +# +# DESCRIPTION +# +# This macro searches for a C compiler that generates native executables, +# that is a C compiler that surely is not a cross-compiler. This can be +# useful if you have to generate source code at compile-time like for +# example GCC does. +# +# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything +# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). +# The value of these variables can be overridden by the user by specifying +# a compiler with an environment variable (like you do for standard CC). +# +# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object +# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if +# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are +# substituted in the Makefile. +# +# LICENSE +# +# Copyright (c) 2008 Paolo Bonzini +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) +AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([AC_EXEEXT])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl + +dnl Use the standard macros, but make them use other variable names +dnl +pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl +pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl +pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl +pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl +pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl +pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl +pushdef([ac_cv_objext], ac_cv_build_objext)dnl +pushdef([ac_exeext], ac_build_exeext)dnl +pushdef([ac_objext], ac_build_objext)dnl +pushdef([CC], CC_FOR_BUILD)dnl +pushdef([CPP], CPP_FOR_BUILD)dnl +pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl +pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl +pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl +pushdef([host], build)dnl +pushdef([host_alias], build_alias)dnl +pushdef([host_cpu], build_cpu)dnl +pushdef([host_vendor], build_vendor)dnl +pushdef([host_os], build_os)dnl +pushdef([ac_cv_host], ac_cv_build)dnl +pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl +pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl +pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl +pushdef([ac_cv_host_os], ac_cv_build_os)dnl +pushdef([ac_cpp], ac_build_cpp)dnl +pushdef([ac_compile], ac_build_compile)dnl +pushdef([ac_link], ac_build_link)dnl + +save_cross_compiling=$cross_compiling +save_ac_tool_prefix=$ac_tool_prefix +cross_compiling=no +ac_tool_prefix= + +AC_PROG_CC +AC_PROG_CPP +AC_EXEEXT + +ac_tool_prefix=$save_ac_tool_prefix +cross_compiling=$save_cross_compiling + +dnl Restore the old definitions +dnl +popdef([ac_link])dnl +popdef([ac_compile])dnl +popdef([ac_cpp])dnl +popdef([ac_cv_host_os])dnl +popdef([ac_cv_host_vendor])dnl +popdef([ac_cv_host_cpu])dnl +popdef([ac_cv_host_alias])dnl +popdef([ac_cv_host])dnl +popdef([host_os])dnl +popdef([host_vendor])dnl +popdef([host_cpu])dnl +popdef([host_alias])dnl +popdef([host])dnl +popdef([LDFLAGS])dnl +popdef([CPPFLAGS])dnl +popdef([CFLAGS])dnl +popdef([CPP])dnl +popdef([CC])dnl +popdef([ac_objext])dnl +popdef([ac_exeext])dnl +popdef([ac_cv_objext])dnl +popdef([ac_cv_exeext])dnl +popdef([ac_cv_prog_cc_g])dnl +popdef([ac_cv_prog_cc_cross])dnl +popdef([ac_cv_prog_cc_works])dnl +popdef([ac_cv_prog_gcc])dnl +popdef([ac_cv_prog_CPP])dnl + +dnl Finally, set Makefile variables +dnl +BUILD_EXEEXT=$ac_build_exeext +BUILD_OBJEXT=$ac_build_objext +AC_SUBST(BUILD_EXEEXT)dnl +AC_SUBST(BUILD_OBJEXT)dnl +AC_SUBST([CFLAGS_FOR_BUILD])dnl +AC_SUBST([CPPFLAGS_FOR_BUILD])dnl +AC_SUBST([LDFLAGS_FOR_BUILD])dnl +]) + diff -Nru iverilog-10.3/cadpli/Makefile.in iverilog-11.0/cadpli/Makefile.in --- iverilog-10.3/cadpli/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/cadpli/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -59,7 +59,7 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f $(INCLUDE_PATH) $^ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=cadpli/$@ @@ -71,17 +71,19 @@ $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< mv $*.d dep -SYSTEM_VPI_LDFLAGS = -L../vvp -lvpi +SYSTEM_VPI_LDFLAGS = -L../vpi -lvpi ifeq (@MINGW32@,yes) SYSTEM_VPI_LDFLAGS += @EXTRALIBS@ endif -cadpli.vpl: $O ../vvp/libvpi.a ../libveriuser/libveriuser.o +cadpli.vpl: $O ../vpi/libvpi.a ../libveriuser/libveriuser.o $(CC) @shared@ $(LDFLAGS) -o $@ $O ../libveriuser/libveriuser.o $(SYSTEM_VPI_LDFLAGS) -install: all installdirs $(vpidir)/cadpli.vpl +install: all installdirs installfiles -$(vpidir)/cadpli.vpl: ./cadpli.vpl +F = ./cadpli.vpl + +installfiles: $(F) | installdirs $(INSTALL_PROGRAM) ./cadpli.vpl "$(DESTDIR)$(vpidir)/cadpli.vpl" installdirs: $(srcdir)/../mkinstalldirs diff -Nru iverilog-10.3/compiler.h iverilog-11.0/compiler.h --- iverilog-10.3/compiler.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/compiler.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_compiler_H #define IVL_compiler_H /* - * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -104,6 +104,9 @@ /* Warn about level-appropriate anachronisms. */ extern bool warn_anachronisms; +/* Warn about nets that are references but not driven. */ +extern bool warn_floating_nets; + /* This is true if verbose output is requested. */ extern bool verbose_flag; @@ -114,6 +117,13 @@ extern bool debug_synth2; extern bool debug_optimizer; +/* Ignore errors about missing modules */ +extern bool ignore_missing_modules; + +/* Treat each source file as a separate compilation unit (as defined + by SystemVerilog). */ +extern bool separate_compilation; + /* Control evaluation of functions at compile time: * 0 = only for functions in constant expressions * 1 = only for automatic functions @@ -167,9 +177,12 @@ is false, then skip elaboration of specify behavior. */ extern bool gn_specify_blocks_flag; -/* If this flag is true, then elaborate assertions. If this flag is - false, then stub out assertion statements. */ -extern bool gn_assertions_flag; +/* If this flag is true, then elaborate supported assertion statements. If + this flag is false, then stub out supported assertion statements. */ +extern bool gn_supported_assertions_flag; +/* If this flag is true, then error on unsupported assertion statements. If + this flag is false, then stub out unsupported assertion statements. */ +extern bool gn_unsupported_assertions_flag; /* If this flag is true, then support/elaborate Verilog-AMS. */ extern bool gn_verilog_ams_flag; @@ -273,12 +286,20 @@ const char* name; ivl_variable_type_t type; unsigned wid; - int signed_flag; + bool signed_flag; + 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 -Nru iverilog-10.3/configure.in iverilog-11.0/configure.in --- iverilog-10.3/configure.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/configure.in 2020-09-26 22:44:25.000000000 +0000 @@ -54,6 +54,10 @@ exit 1 fi +if test "x$NM" = "x"; then + NM=nm +fi + AC_EXEEXT AC_SUBST(EXEEXT) @@ -135,7 +139,13 @@ # vvp uses these... AC_CHECK_LIB(termcap, tputs) AC_CHECK_LIB(readline, readline) +AC_CHECK_LIB(readline, add_history, NEED_LIBHISTORY=no, NEED_LIBHISTORY=yes) +if test "$NEED_LIBHISTORY" = "yes"; then AC_CHECK_LIB(history, add_history) +else +# libreadline includes libhistory functions +AC_DEFINE(HAVE_LIBHISTORY, 1) +fi AC_CHECK_HEADERS(readline/readline.h readline/history.h sys/resource.h) case "${host}" in *linux*) AC_DEFINE([LINUX], [1], [Host operating system is Linux.]) ;; esac @@ -325,5 +335,5 @@ AC_MSG_ERROR(cannot configure white space in libdir: $libdir) fi AC_MSG_RESULT(ok) - +AX_PROG_CC_FOR_BUILD AC_OUTPUT(Makefile ivlpp/Makefile vhdlpp/Makefile vvp/Makefile vpi/Makefile driver/Makefile driver-vpi/Makefile cadpli/Makefile libveriuser/Makefile tgt-null/Makefile tgt-stub/Makefile tgt-vvp/Makefile tgt-vhdl/Makefile tgt-fpga/Makefile tgt-verilog/Makefile tgt-pal/Makefile tgt-vlog95/Makefile tgt-pcb/Makefile tgt-blif/Makefile tgt-sizer/Makefile) diff -Nru iverilog-10.3/cppcheck.sup iverilog-11.0/cppcheck.sup --- iverilog-10.3/cppcheck.sup 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/cppcheck.sup 2020-09-26 22:44:25.000000000 +0000 @@ -1,3 +1,619 @@ // These are correct and are used to find the base (zero) pin. -thisSubtraction:netlist.h:4976 -thisSubtraction:netlist.h:4985 +thisSubtraction:netlist.h:5126 +thisSubtraction:netlist.h:5135 + +// These are the functions that the compiler exports to the targets. +//ivl_branch_island() +unusedFunction:t-dll-api.cc:39 +//ivl_branch_terminal() +unusedFunction:t-dll-api.cc:45 + +//ivl_const_bits() +unusedFunction:t-dll-api.cc:196 +//ivl_const_delay() +unusedFunction:t-dll-api.cc:214 +//ivl_const_file() +unusedFunction:t-dll-api.cc:221 +//ivl_const_lineno() +unusedFunction:t-dll-api.cc:227 +//ivl_const_nex() +unusedFunction:t-dll-api.cc:233 +//ivl_const_real() +unusedFunction:t-dll-api.cc:239 +//ivl_const_scope() +unusedFunction:t-dll-api.cc:246 +//ivl_const_signed() +unusedFunction:t-dll-api.cc:252 +//ivl_const_type() +unusedFunction:t-dll-api.cc:190 +//ivl_const_width() +unusedFunction:t-dll-api.cc:258 + +//ivl_design_const() +unusedFunction:t-dll-api.cc:127 +//ivl_design_consts() +unusedFunction:t-dll-api.cc:121 +//ivl_design_delay_sel() +unusedFunction:t-dll-api.cc:52 +//ivl_design_discipline() +unusedFunction:t-dll-api.cc:140 +//ivl_design_disciplines() +unusedFunction:t-dll-api.cc:134 +//ivl_design_flag() +unusedFunction:t-dll-api.cc:59 +//ivl_design_process() +unusedFunction:t-dll-api.cc:66 +//ivl_design_root() +unusedFunction:t-dll-api.cc:80 +//ivl_design_roots() +unusedFunction:t-dll-api.cc:89 +//ivl_design_time_precision() +unusedFunction:t-dll-api.cc:115 + +//ivl_discipline_domain() +unusedFunction:t-dll-api.cc:147 +//ivl_discipline_flow() +unusedFunction:t-dll-api.cc:153 +//ivl_discipline_name() +unusedFunction:t-dll-api.cc:159 +//ivl_discipline_potential() +unusedFunction:t-dll-api.cc:165 + +//ivl_enum_bits() +unusedFunction:t-dll-api.cc:277 +//ivl_enum_file() +unusedFunction:t-dll-api.cc:302 +//ivl_enum_lineno() +unusedFunction:t-dll-api.cc:308 +//ivl_enum_name() +unusedFunction:t-dll-api.cc:270 +//ivl_enum_names() +unusedFunction:t-dll-api.cc:264 +//ivl_enum_signed() +unusedFunction:t-dll-api.cc:296 +//ivl_enum_type() +unusedFunction:t-dll-api.cc:284 +//ivl_enum_width() +unusedFunction:t-dll-api.cc:290 + +//ivl_event_any() +unusedFunction:t-dll-api.cc:369 +//ivl_event_file() +unusedFunction:t-dll-api.cc:345 +//ivl_event_lineno() +unusedFunction:t-dll-api.cc:351 +//ivl_event_name() +unusedFunction:t-dll-api.cc:314 +//ivl_event_nany() +unusedFunction:t-dll-api.cc:363 +//ivl_event_neg() +unusedFunction:t-dll-api.cc:382 +//ivl_event_nneg() +unusedFunction:t-dll-api.cc:376 +//ivl_event_npos() +unusedFunction:t-dll-api.cc:389 +//ivl_event_pos() +unusedFunction:t-dll-api.cc:395 +//ivl_event_scope() +unusedFunction:t-dll-api.cc:357 + +//ivl_expr_bits() +unusedFunction:t-dll-api.cc:402 +//ivl_expr_branch() +unusedFunction:t-dll-api.cc:409 +//ivl_expr_def() +unusedFunction:t-dll-api.cc:416 +//ivl_expr_delay_val() +unusedFunction:t-dll-api.cc:432 +//ivl_expr_dvalue() +unusedFunction:t-dll-api.cc:439 +//ivl_expr_enumtype() +unusedFunction:t-dll-api.cc:446 +//ivl_expr_event() +unusedFunction:t-dll-api.cc:656 +//ivl_expr_file() +unusedFunction:t-dll-api.cc:178 +//ivl_expr_lineno() +unusedFunction:t-dll-api.cc:184 +//ivl_expr_name() +unusedFunction:t-dll-api.cc:459 +//ivl_expr_nature() +unusedFunction:t-dll-api.cc:483 +//ivl_expr_net_type() +unusedFunction:t-dll-api.cc:453 +//ivl_expr_opcode() +unusedFunction:t-dll-api.cc:490 +//ivl_expr_oper1() +unusedFunction:t-dll-api.cc:506 +//ivl_expr_oper2() +unusedFunction:t-dll-api.cc:544 +//ivl_expr_oper3() +unusedFunction:t-dll-api.cc:570 +//ivl_expr_parameter() +unusedFunction:t-dll-api.cc:584 +//ivl_expr_parm() +unusedFunction:t-dll-api.cc:599 +//ivl_expr_parms() +unusedFunction:t-dll-api.cc:626 +//ivl_expr_repeat() +unusedFunction:t-dll-api.cc:649 +//ivl_expr_scope() +unusedFunction:t-dll-api.cc:670 +//ivl_expr_sel_type() +unusedFunction:t-dll-api.cc:677 +//ivl_expr_signed() +unusedFunction:t-dll-api.cc:702 +//ivl_expr_sized() +unusedFunction:t-dll-api.cc:708 +//ivl_expr_string() +unusedFunction:t-dll-api.cc:714 +//ivl_expr_type() +unusedFunction:t-dll-api.cc:171 +//ivl_expr_uvalue() +unusedFunction:t-dll-api.cc:721 +//ivl_expr_value() +unusedFunction:t-dll-api.cc:747 +//ivl_expr_width() +unusedFunction:t-dll-api.cc:753 + +//ivl_file_table_index() +unusedFunction:t-dll-api.cc:795 +//ivl_file_table_item() +unusedFunction:t-dll-api.cc:785 +//ivl_file_table_size() +unusedFunction:t-dll-api.cc:813 + +//ivl_island_flag_set() +unusedFunction:t-dll-api.cc:822 +//ivl_island_flag_test() +unusedFunction:t-dll-api.cc:837 + +//ivl_logic_attr() +unusedFunction:t-dll-api.cc:864 +//ivl_logic_attr_cnt() +unusedFunction:t-dll-api.cc:880 +//ivl_logic_attr_val() +unusedFunction:t-dll-api.cc:886 +//ivl_logic_basename() +unusedFunction:t-dll-api.cc:935 +//ivl_logic_delay() +unusedFunction:t-dll-api.cc:974 +//ivl_logic_drive0() +unusedFunction:t-dll-api.cc:894 +//ivl_logic_drive1() +unusedFunction:t-dll-api.cc:911 +//ivl_logic_file() +unusedFunction:t-dll-api.cc:846 +//ivl_logic_is_cassign() +unusedFunction:t-dll-api.cc:858 +//ivl_logic_lineno() +unusedFunction:t-dll-api.cc:852 +//ivl_logic_name() +unusedFunction:t-dll-api.cc:928 +//ivl_logic_pins() +unusedFunction:t-dll-api.cc:953 +//ivl_logic_scope() +unusedFunction:t-dll-api.cc:941 +//ivl_logic_type() +unusedFunction:t-dll-api.cc:947 +//ivl_logic_udp() +unusedFunction:t-dll-api.cc:966 +//ivl_logic_width() +unusedFunction:t-dll-api.cc:981 + +//ivl_lpm_array() +unusedFunction:t-dll-api.cc:1109 +//ivl_lpm_aset_value() +unusedFunction:t-dll-api.cc:1160 +//ivl_lpm_async_clr() +unusedFunction:t-dll-api.cc:1054 +//ivl_lpm_async_set() +unusedFunction:t-dll-api.cc:1085 +//ivl_lpm_base() +unusedFunction:t-dll-api.cc:1121 +//ivl_lpm_basename() +unusedFunction:t-dll-api.cc:1048 +//ivl_lpm_clk() +unusedFunction:t-dll-api.cc:1148 +//ivl_lpm_data() +unusedFunction:t-dll-api.cc:1221 +//ivl_lpm_datab() +unusedFunction:t-dll-api.cc:1321 +//ivl_lpm_define() +unusedFunction:t-dll-api.cc:1183 +//ivl_lpm_delay() +unusedFunction:t-dll-api.cc:1078 +//ivl_lpm_drive0() +unusedFunction:t-dll-api.cc:1453 +//ivl_lpm_drive1() +unusedFunction:t-dll-api.cc:1470 +//ivl_lpm_enable() +unusedFunction:t-dll-api.cc:1195 +//ivl_lpm_file() +unusedFunction:t-dll-api.cc:1209 +//ivl_lpm_lineno() +unusedFunction:t-dll-api.cc:1215 +//ivl_lpm_name() +unusedFunction:t-dll-api.cc:1355 +//ivl_lpm_negedge() +unusedFunction:t-dll-api.cc:1136 +//ivl_lpm_select() +unusedFunction:t-dll-api.cc:1493 +//ivl_lpm_selects() +unusedFunction:t-dll-api.cc:1510 +//ivl_lpm_signed() +unusedFunction:t-dll-api.cc:1528 +//ivl_lpm_size() +unusedFunction:t-dll-api.cc:1587 +//ivl_lpm_sset_value() +unusedFunction:t-dll-api.cc:1171 +//ivl_lpm_string() +unusedFunction:t-dll-api.cc:1640 +//ivl_lpm_sync_clr() +unusedFunction:t-dll-api.cc:1066 +//ivl_lpm_sync_set() +unusedFunction:t-dll-api.cc:1097 +//ivl_lpm_trigger() +unusedFunction:t-dll-api.cc:1659 +//ivl_lpm_type() +unusedFunction:t-dll-api.cc:1647 +//ivl_lpm_width() +unusedFunction:t-dll-api.cc:1653 + +//ivl_lval_idx() +unusedFunction:t-dll-api.cc:1681 +//ivl_lval_mux() +unusedFunction:t-dll-api.cc:1676 +//ivl_lval_nest() +unusedFunction:t-dll-api.cc:1726 +//ivl_lval_part_off() +unusedFunction:t-dll-api.cc:1690 +//ivl_lval_property_idx() +unusedFunction:t-dll-api.cc:1708 +//ivl_lval_sel_type() +unusedFunction:t-dll-api.cc:1696 +//ivl_lval_sig() +unusedFunction:t-dll-api.cc:1714 + +//ivl_nature_name() +unusedFunction:t-dll-api.cc:1735 + +//ivl_nexus_get_private() +unusedFunction:t-dll-api.cc:1756 +//ivl_nexus_name() +unusedFunction:t-dll-api.cc:1745 +//ivl_nexus_ptr_branch() +unusedFunction:t-dll-api.cc:1799 +//ivl_nexus_ptr_con() +unusedFunction:t-dll-api.cc:1808 +//ivl_nexus_ptr_sig() +unusedFunction:t-dll-api.cc:1835 +//ivl_nexus_ptr_switch() +unusedFunction:t-dll-api.cc:1844 +//ivl_nexus_set_private() +unusedFunction:t-dll-api.cc:1762 + +//ivl_parameter_basename() +unusedFunction:t-dll-api.cc:1853 +//ivl_parameter_expr() +unusedFunction:t-dll-api.cc:1896 +//ivl_parameter_file() +unusedFunction:t-dll-api.cc:1902 +//ivl_parameter_lineno() +unusedFunction:t-dll-api.cc:1908 +//ivl_parameter_local() +unusedFunction:t-dll-api.cc:1859 +//ivl_parameter_lsb() +unusedFunction:t-dll-api.cc:1877 +//ivl_parameter_msb() +unusedFunction:t-dll-api.cc:1871 +//ivl_parameter_scope() +unusedFunction:t-dll-api.cc:1914 +//ivl_parameter_signed() +unusedFunction:t-dll-api.cc:1865 +//ivl_parameter_width() +unusedFunction:t-dll-api.cc:1887 + +//ivl_path_condit() +unusedFunction:t-dll-api.cc:1920 +//ivl_path_delay() +unusedFunction:t-dll-api.cc:1932 +//ivl_path_is_condit() +unusedFunction:t-dll-api.cc:1926 +//ivl_path_scope() +unusedFunction:t-dll-api.cc:1938 +//ivl_path_source() +unusedFunction:t-dll-api.cc:1945 +//ivl_path_source_negedge() +unusedFunction:t-dll-api.cc:1957 +//ivl_path_source_posedge() +unusedFunction:t-dll-api.cc:1951 + +//ivl_process_analog() +unusedFunction:t-dll-api.cc:1981 +//ivl_process_attr_cnt() +unusedFunction:t-dll-api.cc:1999 +//ivl_process_attr_val() +unusedFunction:t-dll-api.cc:2005 +//ivl_process_file() +unusedFunction:t-dll-api.cc:1963 +//ivl_process_lineno() +unusedFunction:t-dll-api.cc:1969 +//ivl_process_scope() +unusedFunction:t-dll-api.cc:1987 +//ivl_process_stmt() +unusedFunction:t-dll-api.cc:1993 +//ivl_process_type() +unusedFunction:t-dll-api.cc:1975 + +//ivl_scope_attr_cnt() +unusedFunction:t-dll-api.cc:2013 +//ivl_scope_attr_val() +unusedFunction:t-dll-api.cc:2019 +//ivl_scope_basename() +unusedFunction:t-dll-api.cc:2027 +//ivl_scope_child() +unusedFunction:t-dll-api.cc:2054 +//ivl_scope_children() +unusedFunction:t-dll-api.cc:2033 +//ivl_scope_childs() +unusedFunction:t-dll-api.cc:2047 +//ivl_scope_class() +unusedFunction:t-dll-api.cc:2061 +//ivl_scope_classes() +unusedFunction:t-dll-api.cc:2068 +//ivl_scope_def() +unusedFunction:t-dll-api.cc:2075 +//ivl_scope_def_file() +unusedFunction:t-dll-api.cc:2081 +//ivl_scope_def_lineno() +unusedFunction:t-dll-api.cc:2087 +//ivl_scope_enumerate() +unusedFunction:t-dll-api.cc:2099 +//ivl_scope_enumerates() +unusedFunction:t-dll-api.cc:2093 +//ivl_scope_event() +unusedFunction:t-dll-api.cc:2112 +//ivl_scope_events() +unusedFunction:t-dll-api.cc:2106 +//ivl_scope_file() +unusedFunction:t-dll-api.cc:2119 +//ivl_scope_func_signed +unusedFunction:t-dll-api.cc:2132 +//ivl_scope_func_type +unusedFunction:t-dll-api.cc:2125 +//ivl_scope_func_width +unusedFunction:t-dll-api.cc:2140 +//ivl_scope_is_auto() +unusedFunction:t-dll-api.cc:2148 +//ivl_scope_is_cell() +unusedFunction:t-dll-api.cc:2154 +//ivl_scope_lineno() +unusedFunction:t-dll-api.cc:2160 +//ivl_scope_log() +unusedFunction:t-dll-api.cc:2172 +//ivl_scope_logs() +unusedFunction:t-dll-api.cc:2166 +//ivl_scope_lpm() +unusedFunction:t-dll-api.cc:2185 +//ivl_scope_lpms() +unusedFunction:t-dll-api.cc:2179 +//ivl_scope_mod_module_port_name() +unusedFunction:t-dll-api.cc:2264 +//ivl_scope_mod_module_port_type() +unusedFunction:t-dll-api.cc:2273 +//ivl_scope_mod_module_port_width() +unusedFunction:t-dll-api.cc:2285 +//ivl_scope_mod_module_ports() +unusedFunction:t-dll-api.cc:2257 +//ivl_scope_mod_port() +unusedFunction:t-dll-api.cc:2310 +//ivl_scope_param() +unusedFunction:t-dll-api.cc:2243 +//ivl_scope_params() +unusedFunction:t-dll-api.cc:2237 +//ivl_scope_parent() +unusedFunction:t-dll-api.cc:2250 +//ivl_scope_port() +unusedFunction:t-dll-api.cc:2301 +//ivl_scope_ports() +unusedFunction:t-dll-api.cc:2292 +//ivl_scope_sig() +unusedFunction:t-dll-api.cc:2324 +//ivl_scope_sigs() +unusedFunction:t-dll-api.cc:2318 +//ivl_scope_switch() +unusedFunction:t-dll-api.cc:2337 +//ivl_scope_switches() +unusedFunction:t-dll-api.cc:2331 +//ivl_scope_time_precision() +unusedFunction:t-dll-api.cc:2344 +//ivl_scope_time_units() +unusedFunction:t-dll-api.cc:2350 +//ivl_scope_tname() +unusedFunction:t-dll-api.cc:2362 +//ivl_scope_type() +unusedFunction:t-dll-api.cc:2356 + +//ivl_signal_array_addr_swapped() +unusedFunction:t-dll-api.cc:2380 +//ivl_signal_array_base() +unusedFunction:t-dll-api.cc:2368 +//ivl_signal_array_count() +unusedFunction:t-dll-api.cc:2374 +//ivl_signal_attr() +unusedFunction:t-dll-api.cc:2398 +//ivl_signal_attr_cnt() +unusedFunction:t-dll-api.cc:2414 +//ivl_signal_attr_val() +unusedFunction:t-dll-api.cc:2420 +//ivl_signal_basename() +unusedFunction:t-dll-api.cc:2427 +//ivl_signal_data_type() +unusedFunction:t-dll-api.cc:2578 +//ivl_signal_dimensions() +unusedFunction:t-dll-api.cc:2386 +//ivl_signal_discipline() +unusedFunction:t-dll-api.cc:2392 +//ivl_signal_file() +unusedFunction:t-dll-api.cc:2555 +//ivl_signal_forced_net() +unusedFunction:t-dll-api.cc:2549 +//ivl_signal_integer() +unusedFunction:t-dll-api.cc:2567 +//ivl_signal_lineno() +unusedFunction:t-dll-api.cc:2561 +//ivl_signal_local() +unusedFunction:t-dll-api.cc:2536 +//ivl_signal_lsb() +unusedFunction:t-dll-api.cc:2501 +//ivl_signal_module_port_index() +unusedFunction:t-dll-api.cc:2530 +//ivl_signal_msb() +unusedFunction:t-dll-api.cc:2491 +//ivl_signal_name() +unusedFunction:t-dll-api.cc:2433 +//ivl_signal_nex() +unusedFunction:t-dll-api.cc:2454 +//ivl_signal_npath() +unusedFunction:t-dll-api.cc:2591 +//ivl_signal_packed_dimensions() +unusedFunction:t-dll-api.cc:2471 +//ivl_signal_packed_lsb() +unusedFunction:t-dll-api.cc:2484 +//ivl_signal_packed_msb() +unusedFunction:t-dll-api.cc:2477 +//ivl_signal_path() +unusedFunction:t-dll-api.cc:2597 +//ivl_signal_port() +unusedFunction:t-dll-api.cc:2524 +//ivl_signal_scope() +unusedFunction:t-dll-api.cc:2511 +//ivl_signal_signed() +unusedFunction:t-dll-api.cc:2542 +//ivl_signal_width() +unusedFunction:t-dll-api.cc:2517 + +//ivl_statement_type() +unusedFunction:t-dll-api.cc:2610 + +//ivl_stmt_block_count() +unusedFunction:t-dll-api.cc:2643 +//ivl_stmt_block_scope() +unusedFunction:t-dll-api.cc:2628 +//ivl_stmt_block_stmt() +unusedFunction:t-dll-api.cc:2658 +//ivl_stmt_call() +unusedFunction:t-dll-api.cc:2674 +//ivl_stmt_case_count() +unusedFunction:t-dll-api.cc:2695 +//ivl_stmt_case_expr() +unusedFunction:t-dll-api.cc:2710 +//ivl_stmt_case_stmt() +unusedFunction:t-dll-api.cc:2727 +//ivl_stmt_cond_expr() +unusedFunction:t-dll-api.cc:2744 +//ivl_stmt_cond_false() +unusedFunction:t-dll-api.cc:2771 +//ivl_stmt_cond_true() +unusedFunction:t-dll-api.cc:2781 +//ivl_stmt_delay_expr() +unusedFunction:t-dll-api.cc:2791 +//ivl_stmt_delay_val() +unusedFunction:t-dll-api.cc:2808 +//ivl_stmt_events() +unusedFunction:t-dll-api.cc:2834 +//ivl_stmt_file() +unusedFunction:t-dll-api.cc:2616 +//ivl_stmt_lexp() +unusedFunction:t-dll-api.cc:2862 +//ivl_stmt_lineno() +unusedFunction:t-dll-api.cc:2622 +//ivl_stmt_lval() +unusedFunction:t-dll-api.cc:2874 +//ivl_stmt_lvals() +unusedFunction:t-dll-api.cc:2893 +//ivl_stmt_lwidth() +unusedFunction:t-dll-api.cc:2911 +//ivl_stmt_name() +unusedFunction:t-dll-api.cc:2944 +//ivl_stmt_nevent() +unusedFunction:t-dll-api.cc:2815 +//ivl_stmt_opcode() +unusedFunction:t-dll-api.cc:2957 +//ivl_stmt_parm() +unusedFunction:t-dll-api.cc:2969 +//ivl_stmt_parm_count() +unusedFunction:t-dll-api.cc:2983 +//ivl_stmt_rval() +unusedFunction:t-dll-api.cc:2995 +//ivl_stmt_sfunc_as_task() +unusedFunction:t-dll-api.cc:3013 +//ivl_stmt_sub_stmt() +unusedFunction:t-dll-api.cc:3026 + +//ivl_switch_a() +unusedFunction:t-dll-api.cc:3067 +//ivl_switch_b() +unusedFunction:t-dll-api.cc:3073 +//ivl_switch_basename() +unusedFunction:t-dll-api.cc:3049 +//ivl_switch_delay() +unusedFunction:t-dll-api.cc:3103 +//ivl_switch_enable() +unusedFunction:t-dll-api.cc:3079 +//ivl_switch_file() +unusedFunction:t-dll-api.cc:3110 +//ivl_switch_island() +unusedFunction:t-dll-api.cc:3116 +//ivl_switch_lineno() +unusedFunction:t-dll-api.cc:3122 +//ivl_switch_offset() +unusedFunction:t-dll-api.cc:3097 +//ivl_switch_part() +unusedFunction:t-dll-api.cc:3091 +//ivl_switch_scope() +unusedFunction:t-dll-api.cc:3055 +//ivl_switch_type() +unusedFunction:t-dll-api.cc:3061 +//ivl_switch_width() +unusedFunction:t-dll-api.cc:3085 + +//ivl_type_base() +unusedFunction:t-dll-api.cc:3128 +//ivl_type_element() +unusedFunction:t-dll-api.cc:3134 +//ivl_type_name() +unusedFunction:t-dll-api.cc:3166 +//ivl_type_packed_dimensions() +unusedFunction:t-dll-api.cc:3143 +//ivl_type_packed_lsb() +unusedFunction:t-dll-api.cc:3150 +//ivl_type_packed_msb() +unusedFunction:t-dll-api.cc:3158 +//ivl_type_prop_type() +unusedFunction:t-dll-api.cc:3191 +//ivl_type_properties() +unusedFunction:t-dll-api.cc:3174 +//ivl_type_signed() +unusedFunction:t-dll-api.cc:3199 + +//ivl_udp_file() +unusedFunction:t-dll-api.cc:1036 +//ivl_udp_init() +unusedFunction:t-dll-api.cc:999 +//ivl_udp_lineno() +unusedFunction:t-dll-api.cc:1042 +//ivl_udp_name() +unusedFunction:t-dll-api.cc:1029 +//ivl_udp_nin() +unusedFunction:t-dll-api.cc:993 +//ivl_udp_port() +unusedFunction:t-dll-api.cc:1005 +//ivl_udp_row() +unusedFunction:t-dll-api.cc:1014 +//ivl_udp_rows() +unusedFunction:t-dll-api.cc:1023 +// ivl_udp_sequ() +unusedFunction:t-dll-api.cc:987 + diff -Nru iverilog-10.3/debian/changelog iverilog-11.0/debian/changelog --- iverilog-10.3/debian/changelog 2020-05-25 17:44:26.000000000 +0000 +++ iverilog-11.0/debian/changelog 2020-10-02 15:56:04.000000000 +0000 @@ -1,3 +1,16 @@ +iverilog (11.0-1) unstable; urgency=medium + + * New upstream version 11.0 + * rebuild patch queue from patch-queue branch + added patches: + 0001-typo-fix-correct-misspelled-word-variable.patch + 0002-typo-fix-correct-misspelled-word-Parameter.patch + removed patch (obsolete due new upstream version): + gcc10-extern.patch + * d/rules: enable hardening functionality + + -- Carsten Schoenert Fri, 02 Oct 2020 17:56:04 +0200 + iverilog (10.3-2) unstable; urgency=medium * Update standards version to 4.5.0 diff -Nru iverilog-10.3/debian/patches/0001-typo-fix-correct-misspelled-word-variable.patch iverilog-11.0/debian/patches/0001-typo-fix-correct-misspelled-word-variable.patch --- iverilog-10.3/debian/patches/0001-typo-fix-correct-misspelled-word-variable.patch 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/debian/patches/0001-typo-fix-correct-misspelled-word-variable.patch 2020-09-28 19:39:00.000000000 +0000 @@ -0,0 +1,21 @@ +From: Carsten Schoenert +Date: Mon, 28 Sep 2020 21:10:53 +0200 +Subject: typo fix: correct misspelled word variable + +--- + driver/iverilog.man.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/driver/iverilog.man.in b/driver/iverilog.man.in +index 1850542..c3a6821 100644 +--- a/driver/iverilog.man.in ++++ b/driver/iverilog.man.in +@@ -149,7 +149,7 @@ compiler will not include it in an implicit event_expression list it + calculates for that statement or any enclosing statement. This allows + the same control variable to be used in multiple processes without risk + of entering an infinite loop caused by each process triggering all other +-processes that use the same varaible. For strict compliance with the ++processes that use the same variable. For strict compliance with the + standards, this behaviour should be disabled. + .TP 8 + .B -I\fIincludedir\fP diff -Nru iverilog-10.3/debian/patches/0002-typo-fix-correct-misspelled-word-Parameter.patch iverilog-11.0/debian/patches/0002-typo-fix-correct-misspelled-word-Parameter.patch --- iverilog-10.3/debian/patches/0002-typo-fix-correct-misspelled-word-Parameter.patch 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/debian/patches/0002-typo-fix-correct-misspelled-word-Parameter.patch 2020-09-28 19:39:00.000000000 +0000 @@ -0,0 +1,21 @@ +From: Carsten Schoenert +Date: Mon, 28 Sep 2020 21:16:33 +0200 +Subject: typo fix: correct misspelled word Parameter + +--- + elab_expr.cc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/elab_expr.cc b/elab_expr.cc +index 4d96948..bb18b1a 100644 +--- a/elab_expr.cc ++++ b/elab_expr.cc +@@ -4077,7 +4077,7 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope, + if (par != 0) { + + if (member_path.size() > 0) { +- cerr << get_fileline() << ": error: Paramater name " << base_path ++ cerr << get_fileline() << ": error: Parameter name " << base_path + << " can't have member names (member_path=" << member_path << ")." + << endl; + des->errors += 1; diff -Nru iverilog-10.3/debian/patches/gcc10-extern.patch iverilog-11.0/debian/patches/gcc10-extern.patch --- iverilog-10.3/debian/patches/gcc10-extern.patch 2020-05-25 17:44:26.000000000 +0000 +++ iverilog-11.0/debian/patches/gcc10-extern.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -Description: Fix fails to build with -fno-common or gcc-10 - See also: https://bugs.gentoo.org/706366 - - gcc-10 and above flipped a default from -fcommon to -fno-common: - https://gcc.gnu.org/PR85678 - - Usually all it takes is to add a few 'extern' declarations and - move definitions from header files to modules. I've port iverilog - to gcc-10 according to this guide: - https://wiki.gentoo.org/wiki/Gcc_10_porting_notes/fno_common - - To fix this, I analyzed the code, and found ``pli_trace`` has been - defined at here: - https://github.com/steveicarus/iverilog/blob/v10_3/libveriuser/priv.c#L24 - - So I changed ``FILE* pli_trace;`` to ``extern FILE* pli_trace;``. - - The var ``current_file`` only in ``cfparse_misc.h``, I changed it - from ``char *current_file;`` to ``extern char *current_file;`` and - declaring it in cflexor.lex - - And then it works. -Origin: commit:d49d26a5c502faf132a7a65e5cc7173ac0dfa1f5 -Bug-Debian: https://bugs.debian.org/957381 -diff --git a/driver/cflexor.lex b/driver/cflexor.lex -index 5e9e2f50..1bf7cec1 100644 ---- a/driver/cflexor.lex -+++ b/driver/cflexor.lex -@@ -27,6 +27,8 @@ - # include "globals.h" - # include - -+char *current_file = NULL; -+ - static int comment_enter; - static char* trim_trailing_white(char*txt, int trim); - -diff --git a/driver/cfparse_misc.h b/driver/cfparse_misc.h -index 3cb7ddd6..0323690c 100644 ---- a/driver/cfparse_misc.h -+++ b/driver/cfparse_misc.h -@@ -39,6 +39,6 @@ int cferror(const char *); - int cfparse(void); - void switch_to_command_file(const char *); - void destroy_lexor(void); --char *current_file; -+extern char *current_file; - - #endif /* IVL_cfparse_misc_H */ -diff --git a/libveriuser/priv.h b/libveriuser/priv.h -index 8256e16d..8d356608 100644 ---- a/libveriuser/priv.h -+++ b/libveriuser/priv.h -@@ -31,6 +31,6 @@ extern char* __acc_newstring(const char*txt); - /* - * Trace file for logging ACC and TF calls. - */ --FILE* pli_trace; -+extern FILE* pli_trace; - - #endif /* IVL_priv_H */ diff -Nru iverilog-10.3/debian/patches/series iverilog-11.0/debian/patches/series --- iverilog-10.3/debian/patches/series 2020-05-25 17:44:26.000000000 +0000 +++ iverilog-11.0/debian/patches/series 2020-09-28 19:39:00.000000000 +0000 @@ -1 +1,2 @@ -gcc10-extern.patch +0001-typo-fix-correct-misspelled-word-variable.patch +0002-typo-fix-correct-misspelled-word-Parameter.patch diff -Nru iverilog-10.3/debian/rules iverilog-11.0/debian/rules --- iverilog-10.3/debian/rules 2020-05-25 17:44:26.000000000 +0000 +++ iverilog-11.0/debian/rules 2020-10-02 15:50:27.000000000 +0000 @@ -1,5 +1,7 @@ #!/usr/bin/make -f +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + %: dh $@ diff -Nru iverilog-10.3/design_dump.cc iverilog-11.0/design_dump.cc --- iverilog-10.3/design_dump.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/design_dump.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 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 @@ -187,9 +187,15 @@ case NetCaseCmp::NEQ: fd << "!=="; break; - case NetCaseCmp::XEQ: + case NetCaseCmp::WEQ: fd << "==?"; break; + case NetCaseCmp::WNE: + fd << "!=?"; + break; + case NetCaseCmp::XEQ: + fd << "==x?"; + break; case NetCaseCmp::ZEQ: fd << "==z?"; break; @@ -226,13 +232,15 @@ ostream& netqueue_t::debug_dump(ostream&fd) const { - fd << "queue of " << *element_type(); + fd << "queue of "; + if (max_idx_ >= 0) fd << "(maximum of " << max_idx_+1 << " elements) "; + fd << *element_type(); return fd; } ostream& netvector_t::debug_dump(ostream&o) const { - o << type_ << (signed_? " signed" : " unsigned") << packed_dims_; + o << "netvector_t:" << type_ << (signed_? " signed" : " unsigned") << packed_dims_; return o; } @@ -344,6 +352,10 @@ if (has_condit()) o << " if"; else o << " ifnone"; } + if (parallel_) + o << " parallel"; + else + o << " full"; o << " src " << "(" << transition_delays_[IVL_PE_01] << "," << transition_delays_[IVL_PE_10] @@ -688,6 +700,14 @@ dump_obj_attr(o, ind+4); } +void NetLatch::dump_node(ostream&o, unsigned ind) const +{ + o << setw(ind) << "" << "LPM_LATCH: " << name() + << " scope=" << scope_path(scope()) << endl; + dump_node_pins(o, ind+4); + dump_obj_attr(o, ind+4); +} + void NetLiteral::dump_node(ostream&o, unsigned ind) const { o << setw(ind) << "" << "constant real " << real_ @@ -721,6 +741,12 @@ case CMOS: o << "cmos"; break; + case EQUIV: + o << "<->"; + break; + case IMPL: + o << "->"; + break; case NAND: o << "nand"; break; @@ -976,6 +1002,18 @@ o << "always /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; break; + case IVL_PR_ALWAYS_COMB: + o << "always_comb /* " << get_fileline() << " in " + << scope_path(scope_) << " */" << endl; + break; + case IVL_PR_ALWAYS_FF: + o << "always_ff /* " << get_fileline() << " in " + << scope_path(scope_) << " */" << endl; + break; + case IVL_PR_ALWAYS_LATCH: + o << "always_latch /* " << get_fileline() << " in " + << scope_path(scope_) << " */" << endl; + break; case IVL_PR_FINAL: o << "final /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; @@ -1003,6 +1041,13 @@ << scope_path(scope_) << " */" << endl; break; + // These are not used in an analog context. + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: + assert(0); + break; + case IVL_PR_FINAL: o << "analog final /* " << get_fileline() << " in " << scope_path(scope_) << " */" << endl; @@ -1020,7 +1065,11 @@ void NetAssign_::dump_lval(ostream&o) const { if (sig_) o << sig_->name(); - else if (nest_) nest_->dump_lval(o); + else if (nest_) { + o << "("; + nest_->dump_lval(o); + o << ")"; + } else o << ""; if (! member_.nil()) { @@ -1113,15 +1162,29 @@ void NetCase::dump(ostream&o, unsigned ind) const { + o << setw(ind) << ""; + switch (quality_) { + case IVL_CASE_QUALITY_BASIC: + break; + case IVL_CASE_QUALITY_UNIQUE: + o << "unique "; + break; + case IVL_CASE_QUALITY_UNIQUE0: + o << "unique0 "; + break; + case IVL_CASE_QUALITY_PRIORITY: + o << "priority "; + break; + } switch (type_) { case EQ: - o << setw(ind) << "" << "case (" << *expr_ << ")" << endl; + o << "case (" << *expr_ << ")" << endl; break; case EQX: - o << setw(ind) << "" << "casex (" << *expr_ << ")" << endl; + o << "casex (" << *expr_ << ")" << endl; break; case EQZ: - o << setw(ind) << "" << "casez (" << *expr_ << ")" << endl; + o << "casez (" << *expr_ << ")" << endl; break; } @@ -1382,6 +1445,8 @@ if (is_interface()) o << " (interface)"; o << " " << children_.size() << " children, " << classes_.size() << " classes" << endl; + if (unit() && !is_unit()) + o << " in compilation unit " << unit()->basename() << endl; for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1) o << " (* " << attr_key(idx) << " = " @@ -1621,11 +1686,14 @@ case 'A': o << "~&"; break; + case 'e': + o << "=="; + break; case 'E': o << "==="; break; - case 'e': - o << "=="; + case 'w': + o << "==?"; break; case 'G': o << ">="; @@ -1642,6 +1710,9 @@ case 'N': o << "!=="; break; + case 'W': + o << "!=?"; + break; case 'o': o << "||"; break; @@ -1651,6 +1722,12 @@ case 'p': o << "**"; break; + case 'q': + o << "->"; + break; + case 'Q': + o << "<->"; + break; case 'r': o << ">>"; break; @@ -1670,7 +1747,7 @@ { if (op_=='2') fd << "bool<" << expr_width() << ">(" << *expr_ << ")"; - else if (op_=='4') + else if (op_=='v') fd << "logic<" << expr_width() << ">(" << *expr_ << ")"; else NetEUnary::dump(fd); @@ -1894,18 +1971,6 @@ cur->second->dump(o); } - o << "$ROOT CLASSES:" << endl; - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++cur) { - cur->second->dump_scope(o); - } - - o << "$ROOT TASKS/FUNCTIONS:" << endl; - for (map::const_iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { - cur->first->dump(o); - } - o << "SCOPES:" << endl; for (list::const_iterator scope = root_scopes_.begin(); scope != root_scopes_.end(); ++ scope ) { diff -Nru iverilog-10.3/dosify.c iverilog-11.0/dosify.c --- iverilog-10.3/dosify.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/dosify.c 2020-09-26 22:44:25.000000000 +0000 @@ -19,7 +19,7 @@ /* * This is a simple program to make a dosified copy of the - * original. That is, it converts unix style line ends to DOS + * original. That is, it converts Unix style line ends to DOS * style. This is useful for installing text files. * * The exact substitution is to replace \n with \r\n. If the line diff -Nru iverilog-10.3/driver/cflexor.lex iverilog-11.0/driver/cflexor.lex --- iverilog-10.3/driver/cflexor.lex 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/driver/cflexor.lex 2020-09-26 22:44:25.000000000 +0000 @@ -27,6 +27,8 @@ # include "globals.h" # include +char *current_file = NULL; + static int comment_enter; static char* trim_trailing_white(char*txt, int trim); diff -Nru iverilog-10.3/driver/cfparse_misc.h iverilog-11.0/driver/cfparse_misc.h --- iverilog-10.3/driver/cfparse_misc.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/driver/cfparse_misc.h 2020-09-26 22:44:25.000000000 +0000 @@ -39,6 +39,6 @@ int cfparse(void); void switch_to_command_file(const char *); void destroy_lexor(void); -char *current_file; +extern char *current_file; #endif /* IVL_cfparse_misc_H */ diff -Nru iverilog-10.3/driver/iverilog.man.in iverilog-11.0/driver/iverilog.man.in --- iverilog-10.3/driver/iverilog.man.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/driver/iverilog.man.in 2020-09-26 22:44:25.000000000 +0000 @@ -1,14 +1,15 @@ -.TH iverilog 1 "Oct 2nd, 2016" "" "Version %M.%n%E" +.TH iverilog 1 "Aug 10th, 2020" "" "Version %M.%n%E" .SH NAME iverilog - Icarus Verilog compiler .SH SYNOPSIS .B iverilog -[\-ESVv] [\-Bpath] [\-ccmdfile|\-fcmdfile] [\-Dmacro[=defn]] +[\-EiSuVv] [\-Bpath] [\-ccmdfile|\-fcmdfile] [\-Dmacro[=defn]] [\-Pparameter=value] [\-pflag=value] [\-dname] [\-g1995\:|\-g2001\:|\-g2005\:|\-g2005-sv\:|\-g2009\:|\-g2012\:|\-g] -[\-Iincludedir] [\-mmodule] [\-M[mode=]file] [\-Nfile] [\-ooutputfilename] -[\-stopmodule] [\-ttype] [\-Tmin/typ/max] [\-Wclass] [\-ypath] [\-lfile] +[\-Iincludedir] [\-Lmoduledir] [\-mmodule] [\-M[mode=]file] [\-Nfile] +[\-ooutputfilename] [\-stopmodule] [\-ttype] [\-Tmin/typ/max] [\-Wclass] +[\-ypath] [\-lfile] sourcefile .SH DESCRIPTION @@ -80,6 +81,12 @@ Enable or disable (default) support for Verilog\-AMS. Very little Verilog\-AMS specific functionality is currently supported. .TP 8 +.B -gassertions\fI|\fP-gsupported-assertions\fI|\fP-gno-assertions +Enable (default) or disable SystemVerilog assertions. When enabled, +assertion statements are elaborated. When disabled, assertion statements +are parsed but ignored. The \fB\-gsupported-assertions\fP option only +enables assertions that are currently supported by the compiler. +.TP 8 .B -gspecify\fI|\fP-gno-specify Enable or disable (default) specify block support. When enabled, specify block code is elaborated. When disabled, specify blocks are @@ -124,7 +131,7 @@ input value(s) are not re-evaluated. If an expression contains a call to a function that doesn't depend solely on its input values or that has side effects, the resulting behavior will differ from that -required by the standard. Using \fI\-gstrict\-ca\-eval\fP will force +required by the standard. Using \fB\-gstrict\-ca\-eval\fP will force standard compliant behavior (with some loss in performance). .TP 8 .B -gstrict-expr-width\fI|\fP-gno-strict-expr-width @@ -135,7 +142,7 @@ numbers are not truncated to integer width. .TP 8 .B -gshared-loop-index\fI|\fP-gno-shared-loop-index -Enable or disable (default) the exclusion of for-loop control variables +Enable (default) or disable the exclusion of for-loop control variables from implicit event_expression lists. When enabled, if a for-loop control variable (loop index) is only used inside the for-loop statement, the compiler will not include it in an implicit event_expression list it @@ -151,6 +158,19 @@ to specify several directories to search, the directories are searched in the order they appear on the command line. .TP 8 +.B -i +Ignore missing modules. Normally it is an error if a module instantiation +refers to an undefined module. This option causes the compiler to skip +over that instantiation. It will also stop the compiler returning an +error if there are no top level modules. This allows the compiler to be +used to check incomplete designs for errors. +.TP 8 +.B -L\fIpath\fP +This flag adds a directory to the path list used to locate VPI +modules. The default path includes only the install directory for the +system.vpi module, but this flag can add other directories. Multiple +paths are allowed, and the paths will be searched in order. +.TP 8 .B -l\fIfile\fP Add the specified file to the list of source files to be compiled, but mark it as a library file. All modules contained within that @@ -177,9 +197,13 @@ .B -m\fImodule\fP Add this module to the list of VPI modules to be loaded by the simulation. Many modules can be specified, and all will be loaded, in -the order specified. The system module is implicit and always included. -If a System Function Table file (.sft) exists for the module it -will be loaded automatically. +the order specified. The system module is implicit and always included +(and loaded last). + +If the specified name includes at least one directory character, it is +assumed to be prefixed by the path to the module, otherwise the module +is searched for in the paths specified by preceding \fB-L\fP options, +and if not found there, in the \fIiverilog\fP base directory. .TP 8 .B -N\fIpath\fP This is used for debugging the compiler proper. Dump the final netlist @@ -222,6 +246,12 @@ Use this switch to specify the target output format. See the \fBTARGETS\fP section below for a list of valid output formats. .TP 8 +.B -u +Treat each source file as a separate compilation unit (as defined in +SystemVerilog). If compiling for an \fIIEEE1364\fP generation, this +will just reset all compiler directives (including macro definitions) +before each new file is processed. +.TP 8 .B -v Turn on verbose messages. This will print the command lines that are executed to perform the actual compilation, along with version @@ -287,7 +317,10 @@ runtime. The output is a complete program that simulates the design but must be run by the \fBvvp\fP command. The -pfileline=1 option can be used to add procedural statement debugging opcodes to the -generated code. +generated code. These opcodes are also used to generate file and +line information for procedural warning/error messages. To enable +the debug command tracing us the trace command (trace on) from +the vvp interactive prompt. .TP 8 .B fpga This is a synthesis target that supports a variety of fpga devices, @@ -310,8 +343,9 @@ .TP 8 .B all -This enables the anachronisms, implicit, portbind, select\-range, -timescale, and sensitivity\-entire\-array warning categories. +This enables the anachronisms, implicit, macro-replacement, portbind, +select\-range, timescale, and sensitivity\-entire\-array warning +categories. .TP 8 .B anachronisms @@ -325,6 +359,13 @@ source, this will print a warning at its first use. .TP 8 +.B macro-redefinition\fI | \fPmacro-replacement +This enables preprocessor warnings when a macro is being redefined. +The first variant prints a warning any time a macro is redefined. +The second variant only prints a warning if the macro text changes. +Use \fBno-macro-redefinition\fP to turn off all warnings of this type. + +.TP 8 .B portbind This enables warnings for ports of module instantiations that are not connected but probably should be. Dangling input ports, for example, @@ -375,13 +416,23 @@ standard, it is not what might be expected and can have performance implications if the array is large. -.SH "SYSTEM FUNCTION TABLE FILES" -If the source file name as a \fB.sft\fP suffix, then it is taken to be -a system function table file. A System function table file is used to -describe to the compiler the return types for system functions. This +.SH "VPI MODULES" +If the source file name has a \fB.vpi\fP or \fB.vpl\fP suffix, then it +is taken to be a VPI module. VPI modules supplied by the user are scanned +to determine the return types of any system functions they provide. This is necessary because the compiler needs this information to elaborate -expressions that contain these system functions, but cannot run the -sizetf functions since it has no run-time. +expressions that contain these system functions. The module path/name is +passed on to the target to allow the VPI module to be automatically loaded +at the start of simulation. + +VPI modules may also be supplied using the \fB-L\fP and \fB-m\fP options. + +.SH "SYSTEM FUNCTION TABLE FILES [deprecated]" +If the source file name has a \fB.sft\fP suffix, then it is taken to be a +system function table file. A system function table file is the old method +used to describe to the compiler the return types for system functions. +Users are encouraged to switch to the new method of simply supplying the +VPI module. The format of the table is ASCII, one function per line. Empty lines are ignored, and lines that start with the '\fI#\fP' character are @@ -402,6 +453,11 @@ The function returns a vector with the given width, and is signed or unsigned according to the flag. +.TP 8 +.B vpiSysFuncString +The function returns a string. This is an Icarus-specific extension, not +available in the VPI standard. + .SH "COMMAND FILES" The command file allows the user to place source file names and certain command line switches into a text file instead of on a long @@ -449,7 +505,7 @@ .TP 8 .B +libext+\fIext\fP -The \fB+libext\fP token in command files fives file extensions to try +The \fB+libext\fP token in command files lists file extensions to try when looking for a library file. This is useful in conjunction with \fB\-y\fP flags to list suffixes to try in each directory before moving on to the next library directory. @@ -530,6 +586,24 @@ .B __VAMS_ENABLE__ = 1 This is defined if Verilog\-AMS is enabled. +.SH ENVIRONMENT +.PP +\fIiverilog\fP also accepts some environment variables that control +its behavior. These can be used to make semi-permanent changes. + +.TP 8 +.B IVERILOG_ICONFIG=\fIfile-name\fP +This sets the name used for the temporary file that passes parameters +to the compiler proper, and prevents that file being deleted after the +compiler has exited. + +.TP 8 +.B IVERILOG_VPI_MODULE_PATH=\fI/some/path:/some/other/path\fP +This adds additional components to the VPI module search path. Paths +specified in this way are searched after paths specified with \fB-L\fP, +but before the default search path. Multiple paths can be separated with +colons (semicolons if using Windows). + .SH EXAMPLES These examples assume that you have a Verilog source file called hello.v in the current directory @@ -559,7 +633,7 @@ .SH COPYRIGHT .nf -Copyright \(co 2002\-2016 Stephen Williams +Copyright \(co 2002\-2020 Stephen Williams This document can be freely redistributed according to the terms of the GNU General Public License version 2.0 diff -Nru iverilog-10.3/driver/main.c iverilog-11.0/driver/main.c --- iverilog-10.3/driver/main.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/driver/main.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 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 @@ -38,9 +38,9 @@ ; const char HELP[] = -"Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]\n" +"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" @@ -99,13 +99,16 @@ #ifdef __MINGW32__ const char sep = '\\'; +const char path_sep = ';'; #else const char sep = '/'; +const char path_sep = ':'; #endif extern void cfreset(FILE*fd, const char*path); const char*base = 0; +const char*vpi_dir = 0; const char*ivlpp_dir = 0; const char*vhdlpp_dir= 0; const char*vhdlpp_work = 0; @@ -128,7 +131,7 @@ const char*gen_io_range_error = "io-range-error"; const char*gen_strict_ca_eval = "no-strict-ca-eval"; const char*gen_strict_expr_width = "no-strict-expr-width"; -const char*gen_shared_loop_index = "no-shared-loop-index"; +const char*gen_shared_loop_index = "shared-loop-index"; const char*gen_verilog_ams = "no-verilog-ams"; /* Boolean: true means use a default include dir, false means don't */ @@ -138,7 +141,12 @@ of the include list. */ int gen_relative_include = 0; -char warning_flags[16] = "n"; +char warning_flags[17] = "n"; + +int separate_compilation_flag = 0; + +/* Boolean: true means ignore errors about missing modules */ +int ignore_missing_modules = 0; unsigned integer_width = 32; @@ -164,6 +172,12 @@ static char iconfig_common_path[4096] = ""; +static const char**vpi_path_list = 0; +static unsigned vpi_path_list_size = 0; + +static const char**env_vpi_path_list = 0; +static unsigned env_vpi_path_list_size = 0; + int synth_flag = 0; int verbose_flag = 0; @@ -341,10 +355,15 @@ static void build_preprocess_command(int e_flag) { - snprintf(tmp, sizeof tmp, "%s%civlpp %s%s -F\"%s\" -f\"%s\" -p\"%s\" ", - ivlpp_dir, sep, verbose_flag?" -v":"", - e_flag?"":" -L", defines_path, source_path, - compiled_defines_path); + snprintf(tmp, sizeof tmp, "%s%civlpp %s%s%s -F\"%s\" -f\"%s\" -p\"%s\"%s", + ivlpp_dir, sep, + verbose_flag ? " -v" : "", + e_flag ? "" : " -L", + strchr(warning_flags, 'r') ? " -Wredef-all " : + strchr(warning_flags, 'R') ? " -Wredef-chg " : "", + defines_path, source_path, + compiled_defines_path, + e_flag ? "" : " | "); } static int t_preprocess_only(void) @@ -407,8 +426,12 @@ { unsigned rc; - /* Start by building the preprocess command line. */ - build_preprocess_command(0); + /* Start by building the preprocess command line, if required. + This pipes into the main ivl command. */ + if (!separate_compilation_flag) + build_preprocess_command(0); + else + strcpy(tmp, ""); size_t ncmd = strlen(tmp); char*cmd = malloc(ncmd + 1); @@ -418,8 +441,8 @@ int rtn; #endif - /* Build the ivl command and pipe it to the preprocessor. */ - snprintf(tmp, sizeof tmp, " | %s%civl", base, sep); + /* Build the ivl command. */ + snprintf(tmp, sizeof tmp, "%s%civl", base, sep); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); @@ -447,7 +470,16 @@ strcpy(cmd+ncmd, tmp); ncmd += rc; - snprintf(tmp, sizeof tmp, " -C\"%s\" -- -", iconfig_common_path); + snprintf(tmp, sizeof tmp, " -C\"%s\"", iconfig_common_path); + rc = strlen(tmp); + cmd = realloc(cmd, ncmd+rc+1); + strcpy(cmd+ncmd, tmp); + ncmd += rc; + + if (separate_compilation_flag) + snprintf(tmp, sizeof tmp, " -F\"%s\"", source_path); + else + snprintf(tmp, sizeof tmp, " -- -"); rc = strlen(tmp); cmd = realloc(cmd, ncmd+rc+1); strcpy(cmd+ncmd, tmp); @@ -498,6 +530,7 @@ process_warning_switch("anachronisms"); process_warning_switch("implicit"); process_warning_switch("implicit-dimensions"); + process_warning_switch("macro-replacement"); process_warning_switch("portbind"); process_warning_switch("select-range"); process_warning_switch("timescale"); @@ -505,12 +538,21 @@ } else if (strcmp(name,"anachronisms") == 0) { if (! strchr(warning_flags, 'n')) strcat(warning_flags, "n"); + } else if (strcmp(name,"floating-nets") == 0) { + if (! strchr(warning_flags, 'f')) + strcat(warning_flags, "f"); } else if (strcmp(name,"implicit") == 0) { if (! strchr(warning_flags, 'i')) strcat(warning_flags, "i"); } else if (strcmp(name,"implicit-dimensions") == 0) { if (! strchr(warning_flags, 'd')) strcat(warning_flags, "d"); + } else if (strcmp(name,"macro-redefinition") == 0) { + if (! strchr(warning_flags, 'r')) + strcat(warning_flags, "r"); + } else if (strcmp(name,"macro-replacement") == 0) { + if (! strchr(warning_flags, 'R')) + strcat(warning_flags, "R"); } else if (strcmp(name,"portbind") == 0) { if (! strchr(warning_flags, 'p')) strcat(warning_flags, "p"); @@ -537,6 +579,12 @@ cp[0] = cp[1]; cp += 1; } + } else if (strcmp(name,"no-floating-nets") == 0) { + char*cp = strchr(warning_flags, 'f'); + if (cp) while (*cp) { + cp[0] = cp[1]; + cp += 1; + } } else if (strcmp(name,"no-implicit") == 0) { char*cp = strchr(warning_flags, 'i'); if (cp) while (*cp) { @@ -549,6 +597,17 @@ cp[0] = cp[1]; cp += 1; } + } else if (strcmp(name,"no-macro-redefinition") == 0) { + char*cp = strchr(warning_flags, 'r'); + if (cp) while (*cp) { + cp[0] = cp[1]; + cp += 1; + } + cp = strchr(warning_flags, 'R'); + if (cp) while (*cp) { + cp[0] = cp[1]; + cp += 1; + } } else if (strcmp(name,"no-portbind") == 0) { char*cp = strchr(warning_flags, 'p'); if (cp) while (*cp) { @@ -623,14 +682,19 @@ /* * 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; @@ -698,6 +762,9 @@ else if (strcmp(name,"assertions") == 0) gen_assertions = "assertions"; + else if (strcmp(name,"supported-assertions") == 0) + gen_assertions = "supported-assertions"; + else if (strcmp(name,"no-assertions") == 0) gen_assertions = "no-assertions"; @@ -754,6 +821,7 @@ " 2009 -- IEEE1800-2009\n" " 2012 -- IEEE1800-2012\n" "Other generation flags:\n" + " assertions | supported-assertions | no-assertions\n" " specify | no-specify\n" " verilog-ams | no-verilog-ams\n" " std-include | no-std-include\n" @@ -802,71 +870,229 @@ return 0; } +static void add_env_vpi_module_path(const char*path) +{ + env_vpi_path_list_size += 1; + env_vpi_path_list = (const char**)realloc(env_vpi_path_list, + env_vpi_path_list_size*sizeof(char*)); + env_vpi_path_list[env_vpi_path_list_size-1] = path; +} + +static void get_env_vpi_module_paths(void) +{ + char *var = getenv("IVERILOG_VPI_MODULE_PATH"); + char *ptr, *end; + + if (!var) + return; + + var = strdup(var); +#ifdef __MINGW32__ + convert_to_MS_path(var); +#endif + ptr = var; + end = var+strlen(var); + int len = 0; + while (ptr <= end) { + if (*ptr == 0 || *ptr == path_sep) { + *ptr = 0; + if (len > 0) { + add_env_vpi_module_path(var); + } + len = 0; + var = ptr+1; + } else { + len++; + } + ptr++; + } +} + +static void add_vpi_module_path(const char*path) +{ +#ifdef __MINGW32__ + char*tmp_path = strdup(path); + convert_to_MS_path(tmp_path); + path = tmp_path; +#endif + 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] = path; +} + +static int probe_for_vpi_module(const char*base_path, const char*name, + char*path, unsigned path_size) +{ + snprintf(path, path_size, "%s%c%s.vpi", base_path, sep, name); + if (access(path, R_OK) == 0) + return 1; + + snprintf(path, path_size, "%s%c%s.vpl", base_path, sep, name); + if (access(path, R_OK) == 0) + return 1; + + return 0; +} + /* - * 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; + const char*base_dir = vpi_dir ? vpi_dir : base; + + char path[4096]; - file = (char *) malloc(strlen(base)+1+strlen(module)+4+1); - 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); +#ifdef __MINGW32__ + char*tmp_name = strdup(name); + convert_to_MS_path(tmp_name); + name = tmp_name; +#endif + + 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; + } + } else { + strncpy(path, name, sizeof(path) - 1); + } + } else { + for (unsigned idx = 0; !found && (idx < vpi_path_list_size); idx += 1) { + found = probe_for_vpi_module(vpi_path_list[idx], name, + path, sizeof(path)); + } + for (unsigned idx = 0; !found && (idx < env_vpi_path_list_size); idx += 1) { + found = probe_for_vpi_module(env_vpi_path_list[idx], name, + path, sizeof(path)); + } + if (!found) { + found = probe_for_vpi_module(base_dir, name, + path, sizeof(path)); + } + } + if (found) { + fprintf(iconfig_file, "module:%s\n", path); + } else { + fprintf(stderr, "Unable to find VPI module '%s'\n", name); + } +#ifdef __MINGW32__ + free(tmp_name); +#endif } -int main(int argc, char **argv) +static void find_ivl_root_failed(const char *reason) { - int e_flag = 0; - int version_flag = 0; - int opt; + fprintf(stderr, "Cannot locate IVL modules : %s\n", reason); + exit(1); +} +static void find_ivl_root(void) +{ #ifdef __MINGW32__ - /* Calculate the ivl_root from the path to the command. This - is necessary because of the installation process on - Windows. Mostly, it is those darn drive letters, but oh - well. We know the command path is formed like this: + const char *ivl_lib_prefix = "\\lib"; + const char *ivl_lib_suffix = "\\ivl" IVL_SUFFIX; +#else + const char *ivl_lib_prefix = IVL_LIB; + const char *ivl_lib_suffix = "/ivl" IVL_SUFFIX; +#endif + ssize_t len = 0; + char *s; + +#ifndef __MINGW32__ + /* First try the location specified in the build process. */ + if (access(IVL_ROOT, F_OK) != -1) { + assert(strlen(IVL_ROOT) < sizeof ivl_root); + strcpy(ivl_root, IVL_ROOT); + return; + } +#endif + /* If that fails, calculate the ivl_root from the path to the + command. This is always necessary on Windows because of the + installation process, but may also be necessary on other OSs + if the package has been relocated. - D:\iverilog\bin\iverilog.exe + On Windows we know the command path is formed like this: + + $(prefix)\bin\iverilog.exe The module path in a Windows installation is the path: - D:\iverilog\lib\ivl$(suffix) + $(prefix)\lib\ivl$(suffix) so we chop the file name and the last directory by turning the last two \ characters to null. Then we append - the lib\ivl$(suffix) to finish. */ - char *s; + the lib\ivl$(suffix) to finish. + + On other OSs, we expect the command path to be: + + $(prefix)/bin/iverilog + + and the module path to be: + + $(prefix)/$(lib)/ivl$(suffix) + + so we extract the $(prefix) from the command location as for + Windows and the $(lib) from IVL_LIB. This will of course fail + if the user has overridden $(bindir) or $(libdir), but there's + not a lot we can do in that case. + */ +#ifdef __MINGW32__ char tmppath[MAXSIZE]; - GetModuleFileName(NULL, tmppath, sizeof tmppath); + len = GetModuleFileName(NULL, tmppath, sizeof tmppath); + if (len >= (ssize_t) sizeof ivl_root) { + find_ivl_root_failed("command path exceeds size of string buffer."); + } /* Convert to a short name to remove any embedded spaces. */ - GetShortPathName(tmppath, ivl_root, sizeof ivl_root); + len = GetShortPathName(tmppath, ivl_root, sizeof ivl_root); +#else + len = readlink("/proc/self/exe", ivl_root, sizeof ivl_root); +#endif + if (len >= (ssize_t) sizeof ivl_root) { + find_ivl_root_failed("command path exceeds size of string buffer."); + } + if (len <= 0) { + find_ivl_root_failed("couldn't get command path from OS."); + } s = strrchr(ivl_root, sep); - if (s) *s = 0; - else { - fprintf(stderr, "%s: Missing first %c in exe path!\n", - argv[0], sep); - exit(1); + if (s == 0) { + find_ivl_root_failed("missing first separator in command path."); } + *s = 0; s = strrchr(ivl_root, sep); - if (s) *s = 0; - else { - fprintf(stderr, "%s: Missing second %c in exe path!\n", - argv[0], sep); - exit(1); + if (s == 0) { + find_ivl_root_failed("missing second separator in command path."); } - strcat(ivl_root, "\\lib\\ivl" IVL_SUFFIX); + *s = 0; + len = s - ivl_root; + s = strrchr(ivl_lib_prefix, sep); + assert(s); + if (len + strlen(s) + strlen(ivl_lib_suffix) >= (ssize_t) sizeof ivl_root) { + find_ivl_root_failed("module path exceeds size of string buffer."); + } + strcat(ivl_root, s); + strcat(ivl_root, ivl_lib_suffix); +} - base = ivl_root; +int main(int argc, char **argv) +{ + int e_flag = 0; + int version_flag = 0; + int opt; -#else - /* In a UNIX environment, the IVL_ROOT from the Makefile is - dependable. It points to the $prefix/lib/ivl directory, - where the sub-parts are installed. */ - strcpy(ivl_root, IVL_ROOT); + find_ivl_root(); base = ivl_root; -#endif + + get_env_vpi_module_paths(); /* Create a temporary file for communicating input parameters to the preprocessor. */ @@ -928,7 +1154,7 @@ } } - while ((opt = getopt(argc, argv, "B:c:D:d:Ef:g:hl:I:M:m:N:o:P:p:Ss:T:t:vVW: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': @@ -937,6 +1163,9 @@ character of the path indicates which path the user is specifying. */ switch (optarg[0]) { + case 'M': /* Path for the VPI modules */ + vpi_dir = optarg+1; + break; case 'P': /* Path for the ivlpp preprocessor */ ivlpp_dir = optarg+1; break; @@ -983,6 +1212,14 @@ process_include_dir(optarg); break; + case 'i': + ignore_missing_modules = 1; + break; + + case 'L': + add_vpi_module_path(optarg); + break; + case 'l': process_file_name(optarg, 1); break; @@ -993,8 +1230,7 @@ break; case 'm': - fprintf(iconfig_file, "module:%s\n", optarg); - add_sft_file(optarg); + add_vpi_file(optarg); break; case 'N': @@ -1027,6 +1263,9 @@ case 't': targ = optarg; break; + case 'u': + separate_compilation_flag = 1; + break; case 'v': verbose_flag = 1; break; @@ -1062,6 +1301,8 @@ } } + if (vpi_dir == 0) + vpi_dir = base; if (ivlpp_dir == 0) ivlpp_dir = base; if (vhdlpp_dir == 0) @@ -1069,7 +1310,7 @@ if (version_flag || verbose_flag) { printf("Icarus Verilog version " VERSION " (" VERSION_TAG ")\n\n"); - printf("Copyright 1998-2015 Stephen Williams\n\n"); + printf("Copyright 1998-2020 Stephen Williams\n\n"); puts(NOTICE); } @@ -1080,11 +1321,10 @@ /* 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); + /* Tell the core where to find the system VPI modules. */ + fprintf(iconfig_file, "module:%s%csystem.vpi\n", vpi_dir, sep); + fprintf(iconfig_file, "module:%s%cvhdl_sys.vpi\n", vpi_dir, sep); + fprintf(iconfig_file, "module:%s%cvhdl_textio.vpi\n", vpi_dir, sep); /* If verilog-2005/09/12 is enabled or icarus-misc or verilog-ams, * then include the v2005_math library. */ @@ -1093,23 +1333,20 @@ 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", vpi_dir, 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", vpi_dir, 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", vpi_dir, sep); } if (mtm != 0) fprintf(iconfig_file, "-T:%s\n", mtm); @@ -1124,6 +1361,7 @@ fprintf(iconfig_file, "generation:%s\n", gen_verilog_ams); fprintf(iconfig_file, "generation:%s\n", gen_icarus); fprintf(iconfig_file, "warnings:%s\n", warning_flags); + fprintf(iconfig_file, "ignore_missing_modules:%s\n", ignore_missing_modules ? "true" : "false"); fprintf(iconfig_file, "out:%s\n", opath); if (depfile) { fprintf(iconfig_file, "depfile:%s\n", depfile); @@ -1200,8 +1438,12 @@ will append to the file, so this is necessary to make sure it starts out empty. */ if (depfile) { - FILE*fd = fopen(depfile, "w"); - fclose(fd); + FILE *fd = fopen(depfile, "w"); + if (!fd) { + fprintf(stderr, "%s: can't open %s file.\n\n%s\n", argv[0], depfile, HELP); + return 1; + } + fclose(fd); } if (source_count == 0 && !version_flag) { @@ -1216,8 +1458,12 @@ /* Write the preprocessor command needed to preprocess a single file. This may be used to preprocess library files. */ - fprintf(iconfig_file, "ivlpp:%s%civlpp -L -F\"%s\" -P\"%s\"\n", - ivlpp_dir, sep, defines_path, compiled_defines_path); + fprintf(iconfig_file, "ivlpp:%s%civlpp %s -L -F\"%s\" -P\"%s\"\n", + ivlpp_dir, sep, + strchr(warning_flags, 'r') ? "-Wredef-all" : + strchr(warning_flags, 'R') ? "-Wredef-chg" : "", + defines_path, compiled_defines_path + ); /* Done writing to the iconfig file. Close it now. */ fclose(iconfig_file); diff -Nru iverilog-10.3/driver/Makefile.in iverilog-11.0/driver/Makefile.in --- iverilog-10.3/driver/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/driver/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -67,7 +67,7 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ @@ -85,10 +85,9 @@ cflexor.c: $(srcdir)/cflexor.lex $(LEX) -s -t $< > $@ -# Build this in two steps to avoid parallel build issues (see pr3462585) -cfparse.c: $(srcdir)/cfparse.y - $(YACC) --verbose -t -p cf -d -o $@ $< -cfparse.h: cfparse.c +# Use pattern rules to avoid parallel build issues (see pr3462585) +cfparse%c cfparse%h: $(srcdir)/cfparse%y + $(YACC) --verbose -t -p cf -d -o cfparse.c $< %.o: %.c $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o @@ -112,32 +111,35 @@ ifeq (@MINGW32@,yes) ifeq ($(MAN),none) -INSTALL_DOC = $(mandir)/man1/iverilog$(suffix).1 +INSTALL_DOC = installman else ifeq ($(PS2PDF),none) -INSTALL_DOC = $(mandir)/man1/iverilog$(suffix).1 +INSTALL_DOC = installman else -INSTALL_DOC = $(prefix)/iverilog$(suffix).pdf $(mandir)/man1/iverilog$(suffix).1 +INSTALL_DOC = installpdf installman all: iverilog.pdf endif endif INSTALL_DOCDIR = $(mandir)/man1 else -INSTALL_DOC = $(mandir)/man1/iverilog$(suffix).1 +INSTALL_DOC = installman INSTALL_DOCDIR = $(mandir)/man1 endif -install: all installdirs $(bindir)/iverilog$(suffix)@EXEEXT@ $(INSTALL_DOC) +install: all installdirs installfiles -$(bindir)/iverilog$(suffix)@EXEEXT@: ./iverilog@EXEEXT@ - $(INSTALL_PROGRAM) ./iverilog@EXEEXT@ "$(DESTDIR)$(bindir)/iverilog$(suffix)@EXEEXT@" +F = ./iverilog@EXEEXT@ \ + $(INSTALL_DOC) -$(mandir)/man1/iverilog$(suffix).1: iverilog.man +installman: iverilog.man installdirs $(INSTALL_DATA) iverilog.man "$(DESTDIR)$(mandir)/man1/iverilog$(suffix).1" -$(prefix)/iverilog$(suffix).pdf: iverilog.pdf +installpdf: iverilog.pdf installdirs $(INSTALL_DATA) iverilog.pdf "$(DESTDIR)$(prefix)/iverilog$(suffix).pdf" +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./iverilog@EXEEXT@ "$(DESTDIR)$(bindir)/iverilog$(suffix)@EXEEXT@" + installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(INSTALL_DOCDIR)" diff -Nru iverilog-10.3/driver-vpi/Makefile.in iverilog-11.0/driver-vpi/Makefile.in --- iverilog-10.3/driver-vpi/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/driver-vpi/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -36,6 +36,7 @@ dllib=@DLLIB@ CC = @CC@ +HOSTCC := @CC@ WINDRES = @WINDRES@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -65,7 +66,7 @@ rm -f Makefile config.log cppcheck: main.c - cppcheck --enable=all -f $(INCLUDE_PATH) $^ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=driver-vpi/$@ @@ -93,10 +94,19 @@ $(WINDRES) -i res.rc -o res.o # -install: all installdirs $(bindir)/iverilog-vpi$(suffix)@EXEEXT@ +install: all installdirs installfiles -$(bindir)/iverilog-vpi$(suffix)@EXEEXT@: ./iverilog-vpi@EXEEXT@ +F = ./iverilog-vpi@EXEEXT@ + +installfiles: $(F) | installdirs $(INSTALL_PROGRAM) ./iverilog-vpi@EXEEXT@ "$(bindir)/iverilog-vpi$(suffix)@EXEEXT@" +ifeq (@WIN32@,yes) +ifneq ($(HOSTCC),$(CC)) + $(INSTALL_PROGRAM) $(shell $(HOSTCC) --print-file-name=libwinpthread-1.dll) "$(DESTDIR)$(bindir)" + $(INSTALL_PROGRAM) $(shell $(HOSTCC) --print-file-name=libgcc_s_sjlj-1.dll) "$(DESTDIR)$(bindir)" + $(INSTALL_PROGRAM) $(shell $(HOSTCC) --print-file-name=libstdc++-6.dll) "$(DESTDIR)$(bindir)" +endif +endif installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(bindir)" diff -Nru iverilog-10.3/dup_expr.cc iverilog-11.0/dup_expr.cc --- iverilog-10.3/dup_expr.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/dup_expr.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -233,7 +233,7 @@ NetESFunc* NetESFunc::dup_expr() const { - NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms()); + NetESFunc*tmp = new NetESFunc(name_, type_, expr_width(), nparms(), is_overridden_); ivl_assert(*this, tmp); tmp->cast_signed(has_sign()); diff -Nru iverilog-10.3/elab_expr.cc iverilog-11.0/elab_expr.cc --- iverilog-10.3/elab_expr.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/elab_expr.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -35,6 +35,7 @@ # include "discipline.h" # include "netmisc.h" # include "netdarray.h" +# include "netqueue.h" # include "netstruct.h" # include "netscalar.h" # include "util.h" @@ -163,15 +164,11 @@ return 1; } -NetExpr* PExpr::elaborate_expr(Design*des, NetScope*, ivl_type_t, unsigned) const +NetExpr* PExpr::elaborate_expr(Design*des, NetScope*scope, ivl_type_t, unsigned flags) const { - cerr << get_fileline() << ": internal error: I do not know how to" - << " elaborate (ivl_type_t) this expression. " << endl; - cerr << get_fileline() << ": : Expression is: " << *this - << endl; - cerr << get_fileline() << ": : Expression type: " << typeid(*this).name() << endl; - des->errors += 1; - return 0; + // Fall back to the old method. Currently the new method won't be used + // if the target is a vector type, so we can use an arbitrary width. + return elaborate_expr(des, scope, 1, flags); } @@ -181,6 +178,7 @@ << " elaborate this expression. " << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; + cerr << get_fileline() << ": : Expression type: " << typeid(*this).name() << endl; des->errors += 1; return 0; } @@ -204,16 +202,18 @@ ivl_type_t ntype, unsigned flags) const { // Special case: If this is an empty pattern (i.e. '{}) and - // the expected type is a DARRAY, then convert this to a null - // handle. Internally, Icarus Verilog uses this to represent - // nil dynamic arrays. - if (parms_.size() == 0 && ntype->base_type()==IVL_VT_DARRAY) { + // the expected type is a DARRAY or QUEUE, then convert this + // to a null handle. Internally, Icarus Verilog uses this to + // represent nil dynamic arrays. + if (parms_.size() == 0 && (ntype->base_type()==IVL_VT_DARRAY || + ntype->base_type()==IVL_VT_QUEUE)) { NetENull*tmp = new NetENull; tmp->set_line(*this); return tmp; } - if (ntype->base_type()==IVL_VT_DARRAY) + if (ntype->base_type()==IVL_VT_DARRAY || + ntype->base_type()==IVL_VT_QUEUE) return elaborate_expr_darray_(des, scope, ntype, flags); cerr << get_fileline() << ": sorry: I don't know how to elaborate " @@ -322,18 +322,20 @@ min_width_ = UINT_MAX; // disable width pruning break; - case 'l': // << Should be handled by PEBShift - case 'r': // >> Should be handled by PEBShift - case 'R': // >>> Should be handled by PEBShift + case 'l': // << Should be handled by PEBLeftWidth + case 'r': // >> Should be handled by PEBLeftWidth + case 'R': // >>> Should be handled by PEBLeftWidth case '<': // < Should be handled by PEBComp case '>': // > Should be handled by PEBComp case 'e': // == Should be handled by PEBComp case 'E': // === Should be handled by PEBComp + case 'w': // ==? Should be handled by PEBComp case 'L': // <= Should be handled by PEBComp case 'G': // >= Should be handled by PEBComp case 'n': // != Should be handled by PEBComp case 'N': // !== Should be handled by PEBComp - case 'p': // ** should be handled by PEBPower + case 'W': // !=? Should be handled by PEBComp + case 'p': // ** should be handled by PEBLeftWidth ivl_assert(*this, 0); default: break; @@ -411,6 +413,8 @@ case 'a': case 'o': + case 'q': + case 'Q': cerr << get_fileline() << ": internal error: " << "Elaboration of " << human_readable_op(op_) << " Should have been handled in NetEBLogic::elaborate." @@ -668,6 +672,18 @@ return 0; } break; + case 'w': /* ==? */ + case 'W': /* !=? */ + if ((lp->expr_type() != IVL_VT_BOOL && lp->expr_type() != IVL_VT_LOGIC) || + (rp->expr_type() != IVL_VT_BOOL && rp->expr_type() != IVL_VT_LOGIC)) { + cerr << get_fileline() << ": error: " + << human_readable_op(op_) + << " operator may only have INTEGRAL operands." + << endl; + des->errors += 1; + return 0; + } + break; default: break; } @@ -1093,12 +1109,14 @@ bool rc = eval_as_long(value, nexpr); ivl_assert(*this, rc && value>=0); - // The argument type/width is self-determined and doesn't - // affect the result type/width. + // The argument width is self-determined and doesn't + // affect the result width. width_mode_t arg_mode = SIZED; parms_[0]->test_width(des, scope, arg_mode); + expr_type_ = pexpr->expr_type(); expr_width_ = value; + min_width_ = value; signed_flag_= false; return expr_width_; } @@ -1189,6 +1207,8 @@ min_width_ = expr_width_; signed_flag_ = sfunc_info->signed_flag; + is_overridden_ = sfunc_info->override_flag; + if (debug_elaborate) cerr << get_fileline() << ": debug: test_width " << "of system function " << name @@ -1270,7 +1290,6 @@ return 0; perm_string member_name; - ivl_type_t member_type = 0; pform_name_t use_path = path_; perm_string method_name = peek_tail_name(use_path); use_path.pop_back(); @@ -1310,11 +1329,8 @@ const netclass_t* class_type = net->class_type(); int midx = class_type->property_idx_from_name(member_name); - if (midx >= 0) - member_type = class_type->get_prop_type(midx); - else - member_type = 0; - use_path = tmp_path; + ivl_type_t member_type = 0; + if (midx >= 0) member_type = class_type->get_prop_type(midx); use_darray = dynamic_cast (member_type); @@ -1403,7 +1419,7 @@ if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::cast_to_width_: " << "cast to " << wid - << " bits " << (signed_flag_?"signed":"unsigned") + << " bits " << (signed_flag_ ? "signed" : "unsigned") << " from expr_width()=" << expr->expr_width() << endl; } @@ -1519,7 +1535,7 @@ PExpr*expr = parms_[0]; - verinum val (expr->has_sign()? verinum::V1 : verinum::V0, 1); + verinum val (expr->has_sign() ? verinum::V1 : verinum::V0, 1); NetEConst*sub = new NetEConst(val); sub->set_line(*this); @@ -1538,10 +1554,19 @@ if ((nparms == 1) && (parms_[0] == 0)) nparms = 0; - NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms); + NetESFunc*fun = new NetESFunc(name, expr_type_, expr_width_, nparms, is_overridden_); fun->set_line(*this); - if (!fun->is_built_in()) { + bool need_const = NEED_CONST & flags; + + /* We don't support evaluating overridden functions. */ + if (is_overridden_ && (need_const || scope->need_const_func())) { + cerr << get_fileline() << ": sorry: Cannot evaluate " + "overridden system function." << endl; + des->errors += 1; + } + + if (is_overridden_ || !fun->is_built_in()) { if (scope->need_const_func()) { cerr << get_fileline() << ": error: " << name << " is not a built-in function, so cannot" @@ -1558,7 +1583,17 @@ expression as much as possible, and use the reduced expression if one is created. */ - bool need_const = NEED_CONST & flags; + /* These functions can work in a constant context with a signal expression. */ + if ((nparms == 1) && (dynamic_cast(parms_[0]))) { + if (strcmp(name, "$dimensions") == 0) need_const = false; + else if (strcmp(name, "$high") == 0) need_const = false; + else if (strcmp(name, "$increment") == 0) need_const = false; + else if (strcmp(name, "$left") == 0) need_const = false; + else if (strcmp(name, "$low") == 0) need_const = false; + else if (strcmp(name, "$right") == 0) need_const = false; + else if (strcmp(name, "$size") == 0) need_const = false; + else if (strcmp(name, "$unpacked_dimensions") == 0) need_const = false; + } unsigned parm_errors = 0; unsigned missing_parms = 0; @@ -1656,7 +1691,7 @@ static NetExpr* check_for_enum_methods(const LineInfo*li, Design*des, NetScope*scope, const netenum_t*netenum, - pform_name_t use_path, + const pform_name_t&use_path, perm_string method_name, NetExpr*expr, unsigned rtn_wid, @@ -1899,81 +1934,274 @@ Design*des, NetScope*scope, NetNet*net, const list&base_index, - const name_component_t&comp) + pform_name_t member_path) { - unsigned long off; - const netstruct_t::member_t*mem = get_struct_member(li, des, 0, net, - comp.name, off); - if (mem == 0) return 0; + const netstruct_t*struct_type = net->struct_type(); + ivl_assert(*li, struct_type); - ivl_assert(*li, mem->net_type && mem->net_type->packed()); + if (! struct_type->packed()) { + cerr << li->get_fileline() << ": sorry: " + << "Unpacked structures not supported here." + << endl; + des->errors += 1; + return 0; + } - unsigned use_width = mem->net_type->packed_width(); + // These make up the "part" select that is the equivilent of + // following the member path through the nested structs. To + // start with, the off[set] is zero, and use_width is the + // width of the entire variable. The first member_comp is at + // some offset within the variable, and will have a reduced + // width. As we step through the member_path the off + // increases, and use_width shrinks. + unsigned long off = 0; + unsigned long use_width = struct_type->packed_width(); + + pform_name_t completed_path; + do { + const name_component_t member_comp = member_path.front(); + const perm_string&member_name = member_comp.name; - if (debug_elaborate) { - cerr << li->get_fileline() << ": debug: check_for_struct_members: " - << "Found struct member " << mem->name - << " At offset " << off - << ", member width = " << use_width << endl; - } - - // The struct member may be a packed array. Process index - // expression that address the member element. - if ( ! comp.index.empty() ) { - const netvector_t*mem_vec = dynamic_cast (mem->net_type); - ivl_assert(*li, mem_vec); - - const vector&packed_dims = mem_vec->packed_dims(); - - // Evaluate all but the last index expression, into prefix_indices. - listprefix_indices; - bool rc = evaluate_index_prefix(des, scope, prefix_indices, comp.index); - ivl_assert(*li, rc); - - // Make sure that index values that select array - // elements are in fact like bit selects. The tail may - // be part selects only if we are taking the part-select - // of the word of an array. - ivl_assert(*li, comp.index.size() >= packed_dims.size() || comp.index.back().sel == index_component_t::SEL_BIT); - - // Evaluate the part/bit select expressions. This may be - // a bit select or a part select. In any case, assume - // the arguments are constant and generate a part select - // of the appropriate width. - long poff = 0; - unsigned long pwid = 0; - rc = calculate_part(li, des, scope, comp.index.back(), poff, pwid); - ivl_assert(*li, rc); - - // Now use the prefix_to_slice function to calculate the - // offset and width of the addressed slice of the member. - long loff; - unsigned long lwid; - prefix_to_slice(packed_dims, prefix_indices, poff, loff, lwid); + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_members: " + << "Processing member_comp=" << member_comp + << " (completed_path=" << completed_path << ")" + << endl; + } + // Calculate the offset within the packed structure of the + // member, and any indices. We will add in the offset of the + // struct into the packed array later. Note that this works + // for packed unions as well (although the offset will be 0 + // for union members). + unsigned long tmp_off; + const netstruct_t::member_t* member = struct_type->packed_member(member_name, tmp_off); + + if (member == 0) { + cerr << li->get_fileline() << ": error: Member " << member_name + << " is not a member of struct type of " + << net->name() + << "." << completed_path << endl; + des->errors += 1; + return 0; + } if (debug_elaborate) { - cerr << li->get_fileline() << ": debug: check_for_struct_members: " - << "Evaluate prefix gives slice loff=" << loff - << ", lwid=" << lwid << ", part select pwid=" << pwid << endl; + cerr << li->get_fileline() << ": check_for_struct_members: " + << "Member type: " << *(member->net_type) + << " (" << typeid(*(member->net_type)).name() << ")" + << endl; } - off += loff; - if (comp.index.size() >= packed_dims.size()) - use_width = pwid; - else - use_width = lwid; - } + off += tmp_off; + ivl_assert(*li, use_width >= (unsigned long)member->net_type->packed_width()); + use_width = member->net_type->packed_width(); + + // At this point, off and use_width are the part select + // expressed by the member_comp, which is a member of the + // struct. We can further refine the part select with any + // indices that might be present. + + if (const netstruct_t*tmp_struct = dynamic_cast(member->net_type)) { + // If the member is itself a struct, then get + // ready to go on to the next iteration. + struct_type = tmp_struct; + + } else if (const netenum_t*tmp_enum = dynamic_cast (member->net_type)) { + + // If the element is an enum, then we don't have + // anything special to do. + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_members: " + << "Tail element is an enum" << *tmp_enum + << endl; + } + struct_type = 0; + + } else if (const netvector_t*mem_vec = dynamic_cast(member->net_type)) { + + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_members: " + << "member_comp=" << member_comp + << " has " << member_comp.index.size() << " indices." + << endl; + } + + // If the member type is a netvector_t, then it is a + // vector of atom or scaler objects. For example, if the + // l-value expression is "foo.member[1][2]", + // then the member should be something like: + // ... logic [h:l][m:n] member; + // There should be index expressions index the vector + // down, but there doesn't need to be all of them. We + // can, for example, be selecting a part of the vector. + + // We only need to process this if there are any + // index expressions. If not, then the packed + // vector can be handled atomically. + + // In any case, this should be the tail of the + // member_path, because the array element of this + // kind of array cannot be a struct. + if (member_comp.index.size() > 0) { + // These are the dimensions defined by the type + const vector&mem_packed_dims = mem_vec->packed_dims(); + + if (member_comp.index.size() > mem_packed_dims.size()) { + cerr << li->get_fileline() << ": error: " + << "Too many index expressions for member." << endl; + des->errors += 1; + return 0; + } + + // Evaluate all but the last index expression, into prefix_indices. + listprefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index); + ivl_assert(*li, rc); + + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_members: " + << "prefix_indices.size()==" << prefix_indices.size() + << ", mem_packed_dims.size()==" << mem_packed_dims.size() + << endl; + } + + long tail_off = 0; + unsigned long tail_wid = 0; + rc = calculate_part(li, des, scope, member_comp.index.back(), tail_off, tail_wid); + if (! rc) return 0; + + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_member: " + << "calculate_part for tail returns tail_off=" << tail_off + << ", tail_wid=" << tail_wid + << endl; + } + + + // Now use the prefix_to_slice function to calculate the + // offset and width of the addressed slice + // of the member. The lwid comming out of + // the prefix_to_slice is the number of + // elements, and should be 1. The tmp_wid it + // the bit with of the result. + long loff; + unsigned long lwid; + prefix_to_slice(mem_packed_dims, prefix_indices, tail_off, loff, lwid); + + if (debug_elaborate) { + cerr << li->get_fileline() << ": check_for_struct_members: " + << "Calculate loff=" << loff << " lwid=" << lwid + << " tail_off=" << tail_off << " tail_wid=" << tail_wid + << " off=" << off << " use_width=" << use_width + << endl; + } + + off += loff; + use_width = lwid * tail_wid; + } + + // The netvector_t only has atom elements, so + // there is no next struct type. + struct_type = 0; + + } else if (const netparray_t*array = dynamic_cast(member->net_type)) { + + // If the member is a parray, then the elements + // are themselves packed object, including + // possibly a struct. Handle this by taking the + // part select of the current part of the + // variable, then stepping to the element type to + // possibly iterate through more of the member_path. + ivl_assert(*li, array->packed()); + ivl_assert(*li, member_comp.index.size() > 0); + + // These are the dimensions defined by the type + const vector&mem_packed_dims = array->static_dimensions(); + + if (member_comp.index.size() != mem_packed_dims.size()) { + cerr << li->get_fileline() << ": error: " + << "Incorrect number of index expressions for member " + << member_name << "." << endl; + des->errors += 1; + return 0; + } + + // Evaluate all but the last index expression, into prefix_indices. + listprefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index); + ivl_assert(*li, rc); + + // Evaluate the last index expression into a constant long. + NetExpr*texpr = elab_and_eval(des, scope, member_comp.index.back().msb, -1, true); + long tmp; + if (texpr == 0 || !eval_as_long(tmp, texpr)) { + cerr << li->get_fileline() << ": error: " + << "Array index expressions for member " << member_name + << " must be constant here." << endl; + des->errors += 1; + return 0; + } + + delete texpr; + + // Now use the prefix_to_slice function to calculate the + // offset and width of the addressed slice of the member. + long loff; + unsigned long lwid; + prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid); + + ivl_type_t element_type = array->element_type(); + long element_width = element_type->packed_width(); + if (debug_elaborate) { + cerr << li->get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "parray subselection loff=" << loff + << ", lwid=" << lwid + << ", element_width=" << element_width + << endl; + } + + // The width and offset calculated from the + // indices is actually in elements, and not + // bits. In fact, in this context, the lwid should + // come down to 1 (one element). + off += loff * element_width; + ivl_assert(*li, lwid==1); + use_width = element_width; + + // To move on to the next component in the member + // path, get the element type. For example, for + // the path a.b[1].c, we are processing b[1] here, + // and the element type should be a netstruct_t + // that will wind up containing the member c. + struct_type = dynamic_cast (element_type); + + } else { + // Unknown type? + cerr << li->get_fileline() << ": internal error: " + << "Unexpected member type? " << *(member->net_type) + << endl; + des->errors += 1; + struct_type = 0; + } + + // Complete this component of the path, mark it + // completed, and set up for the next component. + completed_path .push_back(member_comp); + member_path.pop_front(); + + } while (member_path.size() > 0 && struct_type != 0); + + // The dimensions in the expression must match the packed + // dimensions that are declared for the variable. For example, + // if foo is a packed array of struct, then this expression + // must be "b[n][m]" with the right number of dimensions to + // match the declaration of "b". + // Note that one of the packed dimensions is the packed struct + // itself. + ivl_assert(*li, base_index.size()+1 == net->packed_dimensions()); - // If the base symbol has dimensions, then this is a packed - // array of structures. Convert an array of indices to a - // single part select. For example, "net" is a packed array - // of struct, and "mem" is the struct member. In Verilog it - // looks something like "net[idx].mem". We've already - // converted "mem" to an offset into the packed struct, so now - // we just canonicalize "[idx]" and add the ".mem" offset to - // get a collapsed index. NetExpr*packed_base = 0; - if(net->packed_dimensions() > 1) { + if (net->packed_dimensions() > 1) { listtmp_index = base_index; index_component_t member_select; member_select.sel = index_component_t::SEL_BIT; @@ -1989,20 +2217,23 @@ long tmp; if (packed_base && eval_as_long(tmp, packed_base)) { - off = tmp; + off += tmp; delete packed_base; packed_base = 0; } NetESignal*sig = new NetESignal(net); - NetExpr *base = packed_base? packed_base : make_const_val(off); + NetExpr *base = packed_base? packed_base : make_const_val(off); + NetESelect*sel = new NetESelect(sig, base, use_width); if (debug_elaborate) { - cerr << li->get_fileline() << ": debug: check_for_struct_members: " - << "Convert packed indices/member select into part select: " << *base << endl; + cerr << li->get_fileline() << ": check_for_struct_member: " + << "Finally, completed_path=" << completed_path + << ", off=" << off << ", use_width=" << use_width + << ", base=" << *base + << endl; } - NetESelect*sel = new NetESelect(sig, base, use_width); return sel; } @@ -2263,8 +2494,8 @@ } cerr << get_fileline() << ": internal error: Unable to locate " - "function return value for " << path_ - << " in " << dscope->basename() << "." << endl; + "function return value for " << path_ + << " in " << dscope->basename() << "." << endl; des->errors += 1; return 0; } @@ -2437,28 +2668,57 @@ if (net->darray_type()) { if (method_name == "size") { + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: size() method " + << "takes no arguments" << endl; + des->errors += 1; + } NetESFunc*sys_expr = new NetESFunc("$size", IVL_VT_BOOL, 32, 1); - sys_expr->parm(0, new NetESignal(net)); sys_expr->set_line(*this); + + NetESignal*arg = new NetESignal(net); + arg->set_line(*net); + + sys_expr->parm(0, arg); return sys_expr; } + } + if (net->queue_type()) { if (method_name == "pop_back") { - NetESFunc*sys_expr = new NetESFunc("$ivl_darray_method$pop_back", + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_back() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_back", expr_type_, expr_width_, 1); - sys_expr->parm(0, new NetESignal(net)); sys_expr->set_line(*this); + + NetESignal*arg = new NetESignal(net); + arg->set_line(*net); + + sys_expr->parm(0, arg); return sys_expr; } if (method_name == "pop_front") { - NetESFunc*sys_expr = new NetESFunc("$ivl_darray_method$pop_front", + if (parms_.size() != 0) { + cerr << get_fileline() << ": error: pop_front() method " + << "takes no arguments" << endl; + des->errors += 1; + } + NetESFunc*sys_expr = new NetESFunc("$ivl_queue_method$pop_front", expr_type_, expr_width_, 1); - sys_expr->parm(0, new NetESignal(net)); sys_expr->set_line(*this); + + NetESignal*arg = new NetESignal(net); + arg->set_line(*net); + + sys_expr->parm(0, arg); return sys_expr; } @@ -2535,13 +2795,15 @@ ivl_assert(*this, size_); ivl_assert(*this, base_); - // When changing size, a cast behaves exactly like an assignment, - // so the result size affects the final expression width. + // A cast behaves exactly like an assignment to a temporary variable, + // so the temporary result size may affect the sub-expression width. unsigned cast_width = base_->expr_width(); if (cast_width < expr_width_) cast_width = expr_width_; NetExpr*sub = base_->elaborate_expr(des, scope, cast_width, flags); + if (sub == 0) + return 0; // Perform the cast. The extension method (zero/sign), if needed, // depends on the type of the base expression. @@ -2552,33 +2814,33 @@ return pad_to_width(tmp, expr_wid, signed_flag_, *this); } -unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&wid) +unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&) { ivl_type_t t = target_->elaborate_type(des, scope); - base_->test_width(des, scope, wid); - if(const netdarray_t*use_darray = dynamic_cast (t)) { + width_mode_t tmp_mode = PExpr::SIZED; + base_->test_width(des, scope, tmp_mode); + + if (const netdarray_t*use_darray = dynamic_cast(t)) { expr_type_ = use_darray->element_base_type(); expr_width_ = use_darray->element_width(); - } - else if(const netstring_t*use_string = dynamic_cast (t)) { + } else if (const netstring_t*use_string = dynamic_cast(t)) { expr_type_ = use_string->base_type(); expr_width_ = 8; - } - else { + } else { expr_type_ = t->base_type(); expr_width_ = t->packed_width(); } - + min_width_ = expr_width_; signed_flag_ = t->get_signed(); - min_width_ = expr_width_; + return expr_width_; } NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, - ivl_type_t type, unsigned) const + ivl_type_t type, unsigned flags) const { const netdarray_t*darray = NULL; const netvector_t*vector = NULL; @@ -2607,60 +2869,68 @@ } // Fallback - return elaborate_expr(des, scope, (unsigned) 0, 0); + return elaborate_expr(des, scope, (unsigned) 0, flags); } NetExpr* PECastType::elaborate_expr(Design*des, NetScope*scope, - unsigned, unsigned) const + unsigned expr_wid, unsigned flags) const { - NetExpr*expr = base_->elaborate_expr(des, scope, base_->expr_width(), NO_FLAGS); - - if(dynamic_cast(target_)) { - return cast_to_real(expr); - } + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag - if(const atom2_type_t*atom = dynamic_cast(target_)) { - if(base_->expr_width() > expr_width_) { - cerr << get_fileline() << ": cast type is not wide enough to store the result." << endl; - ivl_assert(*this, 0); - } + // A cast behaves exactly like an assignment to a temporary variable, + // so the temporary result size may affect the sub-expression width. + unsigned cast_width = base_->expr_width(); + if (type_is_vectorable(base_->expr_type()) && (cast_width < expr_width_)) + cast_width = expr_width_; - if(base_->has_sign() != atom->signed_flag) { - cerr << get_fileline() << ": cast type and subject differ in signedness." << endl; - ivl_assert(*this, 0); - } + NetExpr*sub = base_->elaborate_expr(des, scope, cast_width, flags); + if (sub == 0) + return 0; - // That is how you both resize & cast to integers - return new NetECast('2', expr, expr_width_, expr->has_sign()); + if (dynamic_cast(target_)) { + return cast_to_real(sub); } - if(const vector_type_t*vec = dynamic_cast(target_)) { - switch(vec->base_type) { - case IVL_VT_BOOL: - return cast_to_int2(expr, expr_width_); - - case IVL_VT_LOGIC: - return cast_to_int4(expr, expr_width_); - - default: - break; /* Suppress warnings */ - } + NetExpr*tmp = 0; + if (dynamic_cast(target_)) { + tmp = cast_to_int2(sub, expr_width_); } + if (const vector_type_t*vec = dynamic_cast(target_)) { + switch (vec->base_type) { + case IVL_VT_BOOL: + tmp = cast_to_int2(sub, expr_width_); + break; - else if(dynamic_cast(target_)) { - if(base_->expr_type() == IVL_VT_STRING) - return expr; // no conversion - - if((base_->expr_type() != IVL_VT_BOOL) && - (base_->expr_type() != IVL_VT_LOGIC)) { - cerr << get_fileline() << ": cannot be cast to a string." << endl; - ivl_assert(*this, false); - } + case IVL_VT_LOGIC: + tmp = cast_to_int4(sub, expr_width_); + break; - return expr; + default: + break; + } + } + if (tmp) { + if (tmp == sub) { + // We already had the correct base type, so we just need to + // fix the size. Note that even if the size is already correct, + // we still need to isolate the sub-expression from changes in + // the signedness pushed down from the main expression. + tmp = cast_to_width(sub, expr_width_, sub->has_sign(), *this); + } + return pad_to_width(tmp, expr_wid, signed_flag_, *this); + } + + if (dynamic_cast(target_)) { + if (base_->expr_type() == IVL_VT_STRING) + return sub; // no conversion + + if (base_->expr_type() == IVL_VT_LOGIC + || base_->expr_type() == IVL_VT_BOOL) + return sub; // handled by the target as special cases } cerr << get_fileline() << ": sorry: This cast operation is not yet supported." << endl; + des->errors += 1; return 0; } @@ -2692,7 +2962,7 @@ expr_is_string = NO; } - expr_type_ = (expr_is_string==YES)? IVL_VT_STRING : IVL_VT_LOGIC; + expr_type_ = (expr_is_string==YES) ? IVL_VT_STRING : IVL_VT_LOGIC; signed_flag_ = false; // If there is a repeat expression, then evaluate the constant @@ -2749,17 +3019,35 @@ // Keep track of the concatenation/repeat depth. static int concat_depth = 0; -NetExpr* PEConcat::elaborate_expr(Design*, NetScope*, - ivl_type_t type, unsigned /*flags*/) const +NetExpr* PEConcat::elaborate_expr(Design*des, NetScope*scope, + ivl_type_t ntype, unsigned flags) const { - switch (type->base_type()) { + switch (ntype->base_type()) { case IVL_VT_QUEUE: +// FIXME: Does a DARRAY support a zero size? + case IVL_VT_DARRAY: if (parms_.size() == 0) { NetENull*tmp = new NetENull; tmp->set_line(*this); return tmp; + } else { + const netdarray_t*array_type = dynamic_cast (ntype); + ivl_assert(*this, array_type); + + // This is going to be an array pattern, so run through the + // elements of the expression and elaborate each as if they + // are element_type expressions. + ivl_type_t elem_type = array_type->element_type(); + vector elem_exprs (parms_.size()); + for (size_t idx = 0 ; idx < parms_.size() ; idx += 1) { + NetExpr*tmp = parms_[idx]->elaborate_expr(des, scope, elem_type, flags); + elem_exprs[idx] = tmp; + } + + NetEArrayPattern*res = new NetEArrayPattern(array_type, elem_exprs); + res->set_line(*this); + return res; } - // fallthrough default: cerr << get_fileline() << ": internal error: " << "I don't know how to elaborate(ivl_type_t)" @@ -2854,7 +3142,7 @@ concat->set(idx, parms[off+idx]); } - if (wid_sum == 0) { + if (wid_sum == 0 && expr_type_ != IVL_VT_STRING) { cerr << get_fileline() << ": error: Concatenation/replication " << "may not have zero width in this context." << endl; des->errors += 1; @@ -3046,7 +3334,7 @@ NetExpr*wid_ex = elab_and_eval(des, scope, index_tail.lsb, -1, true); NetEConst*wid_c = dynamic_cast(wid_ex); - wid = wid_c? wid_c->value().as_ulong() : 0; + wid = wid_c ? wid_c->value().as_ulong() : 0; if (wid == 0) { cerr << index_tail.lsb->get_fileline() << ": error: " "Indexed part widths must be constant and greater than zero." @@ -3147,6 +3435,25 @@ } } + // Look for the enumeration attributes. + if (const netenum_t*netenum = net->enumeration()) { + if (member_name == "num") { + expr_type_ = IVL_VT_BOOL; + expr_width_ = 32; + min_width_ = 32; + signed_flag_= true; + return 32; + } + if ((member_name == "first") || (member_name == "last") || + (member_name == "next") || (member_name == "prev")) { + expr_type_ = netenum->base_type(); + expr_width_ = netenum->packed_width();; + min_width_ = expr_width_; + signed_flag_ = netenum->get_signed(); + return expr_width_; + } + } + return 0; } @@ -3195,7 +3502,7 @@ bool parts_defined; calculate_parts_(des, scope, msb, lsb, parts_defined); if (parts_defined) - use_width = 1 + ((msb>lsb)? (msb-lsb) : (lsb-msb)); + use_width = 1 + ((msb>lsb) ? (msb-lsb) : (lsb-msb)); else use_width = UINT_MAX; break; @@ -3229,7 +3536,7 @@ ivl_assert(*this, 0); } - if (const netdarray_t*darray = net? net->darray_type() : 0) { + if (const netdarray_t*darray = net ? net->darray_type() : 0) { switch (use_sel) { case index_component_t::SEL_BIT: case index_component_t::SEL_BIT_LAST: @@ -3283,7 +3590,7 @@ << net->name() << " is a net, " << "type=" << expr_type_ << ", width=" << expr_width_ - << ", signed_=" << (signed_flag_?"true":"false") + << ", signed_=" << (signed_flag_ ? "true" : "false") << ", use_depth=" << use_depth << ", packed_dimensions=" << net->packed_dimensions() << ", unpacked_dimensions=" << net->unpacked_dimensions() @@ -3426,6 +3733,10 @@ ex1, ex2); if (net == 0 && gn_system_verilog() && path_.size() >= 2) { + // NOTE: this is assuming the member_path is only one + // component long, and that the use_path will wind up + // being the path to the variable. This is not + // necessarily true. Should fix this. pform_name_t use_path = path_; name_component_t member_comp = use_path.back(); use_path.pop_back(); @@ -3437,9 +3748,11 @@ // Nope, no struct/class with member. } else if (net->struct_type() != 0) { + pform_name_t member_path; + member_path.push_back( member_comp ); return check_for_struct_members(this, des, use_scope, net, use_path.back().index, - member_comp); + member_path); } else if (net->class_type()!=0) { if (debug_elaborate) { @@ -3502,6 +3815,7 @@ << endl; } +// FIXME: The real array to queue is failing here. if (net->unpacked_dimensions() != use_comp.index.size()) { cerr << get_fileline() << ": sorry: " << "Net " << net->name() @@ -3658,59 +3972,6 @@ return tmp; } -NetExpr* PEIdent::elaborate_expr_method_(Design*des, NetScope*scope, - unsigned, unsigned) const -{ - if (!gn_system_verilog()) - return 0; - if (path_.size() < 2) - return 0; - - pform_name_t use_path = path_; - perm_string member_name = peek_tail_name(path_); - use_path.pop_back(); - - if (debug_elaborate) { - cerr << get_fileline() << ": PEIdent::elaborate_expr_method_: " - << "Try to find method=" << member_name - << " of signal " << use_path << endl; - } - - NetNet*net = 0; - const NetExpr*par = 0; - NetEvent*eve = 0; - const NetExpr*ex1 = 0, *ex2 = 0; - symbol_search(this, des, scope, use_path, net, par, eve, ex1, ex2); - if (net == 0) { - if (debug_elaborate) - cerr << get_fileline() << ": PEIdent::elaborate_expr_method_: " - << "Only nets can have methods, so give up here." << endl; - return 0; - } - - if (net->darray_type()) { - if (member_name == "size") { - NetESFunc*fun = new NetESFunc("$size", IVL_VT_BOOL, 32, 1); - fun->set_line(*this); - - NetESignal*arg = new NetESignal(net); - arg->set_line(*net); - - fun->parm(0, arg); - return fun; - } - - return 0; - } - - if (debug_elaborate) { - cerr << get_fileline() << ": PEIdent::elaborate_expr_method_: " - << "Give up trying to find method " << member_name - << " of " << path_ << "." << endl; - } - - return 0; -} /* * Elaborate an identifier in an expression. The identifier can be a @@ -3734,6 +3995,12 @@ const NetExpr*ex1, *ex2; + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "path_=" << path_ + << endl; + } + // Special case: Detect the special situation that this name // is the name of a variable in the class, and this is a class // method. We sense that this might be the case by noting that @@ -3763,30 +4030,59 @@ scope->is_const_func(false); } - if (debug_elaborate) - cerr << get_fileline() << ": PEIdent::elaborate_expr: path_=" << path_ << endl; - + // If this identifier is pulled from a package, then switch + // the scope we are using. NetScope*use_scope = scope; if (package_) { use_scope = des->find_package(package_->pscope_name()); ivl_assert(*this, use_scope); } - // Special case: Detect the special situation that the name is - // a method of an object (including built-in methods) that has - // no arguments. For example, "foo.size" is the call to the - // size() method if foo is an array type. - if (NetExpr*tmp = elaborate_expr_method_(des, scope, expr_wid, flags)) { - return tmp; + + // Find the net/parameter/event object that this name refers + // to. The path_ may be a scoped path, and may include method + // or member name parts. For example, main.a.b.c may refer to + // a net called "b" in the scope "main.a" and with a member + // named "c". This loop tries to figure that out and the + // result is the complete path_ split into a base_path (that + // locates the object) and the member_path that selects parts + // in the object. + pform_name_t base_path = path_; + pform_name_t member_path; + NetScope*found_in = 0; + while (net==0 && par==0 && eve==0 && base_path.size()>0) { + found_in = symbol_search(this, des, use_scope, base_path, + net, par, eve, ex1, ex2); + if (net) break; + if (par) break; + if (eve) break; + // Not found. Try to pop another name off the base_path + // and push it to the front of the member path. + member_path.push_front( base_path.back() ); + base_path.pop_back(); } - NetScope*found_in = symbol_search(this, des, use_scope, path_, - net, par, eve, - ex1, ex2); + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Symbol search found base_path=" << base_path + << ", member_path=" << member_path + << ", par=" << par + << ", net=" << net + << ", eve=" << eve + << endl; + } // If the identifier name is a parameter name, then return // the parameter value. if (par != 0) { + + if (member_path.size() > 0) { + cerr << get_fileline() << ": error: Paramater name " << base_path + << " can't have member names (member_path=" << member_path << ")." + << endl; + des->errors += 1; + } + NetExpr*tmp = elaborate_expr_param_(des, scope, par, found_in, ex1, ex2, expr_wid, flags); @@ -3795,7 +4091,7 @@ return pad_to_width(tmp, expr_wid, signed_flag_, *this); } - // If the identifier names a signal (a register or wire) + // If the identifier names a signal (a variable or a net) // then create a NetESignal node to handle it. if (net != 0) { if (NEED_CONST & flags) { @@ -3816,6 +4112,122 @@ scope->is_const_func(false); } + // If this is a struct, and there are members in the + // member_path, then generate an expression that + // reflects the member selection. + if (net->struct_type() && member_path.size() > 0) { + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Ident " << base_path + << " look for struct member " << member_path + << endl; + } + + NetExpr*tmp = check_for_struct_members(this, des, use_scope, + net, base_path.back().index, + member_path); + if (!tmp) return 0; + else return pad_to_width(tmp, expr_wid, signed_flag_, *this); + } + + // If this is an array object, and there are members in + // the member_path, check for array properties. + if (net->darray_type() && member_path.size() > 0) { + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Ident " << base_path + << " looking for array property " << member_path + << endl; + } + + ivl_assert(*this, member_path.size() == 1); + const name_component_t member_comp = member_path.front(); + if (member_comp.name == "size") { + NetESFunc*fun = new NetESFunc("$size", IVL_VT_BOOL, 32, 1); + fun->set_line(*this); + + NetESignal*arg = new NetESignal(net); + arg->set_line(*net); + + fun->parm(0, arg); + return fun; + } + } + + // If this is a queue object, and there are members in + // the member_path, check for array properties. + if (net->queue_type() && member_path.size() > 0) { + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Ident " << base_path + << " looking for queue property " << member_path + << endl; + } + + ivl_assert(*this, member_path.size() == 1); + const name_component_t member_comp = member_path.front(); + const netqueue_t*queue = net->queue_type(); + ivl_variable_type_t qelem_type = queue->element_base_type(); + unsigned qelem_width = queue->element_width(); + if (member_comp.name == "pop_back") { + NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_back", + qelem_type, qelem_width, 1); + fun->set_line(*this); + + NetESignal*arg = new NetESignal(net); + arg->set_line(*net); + + fun->parm(0, arg); + return fun; + } + + if (member_comp.name == "pop_front") { + NetESFunc*fun = new NetESFunc("$ivl_queue_method$pop_front", + qelem_type, qelem_width, 1); + fun->set_line(*this); + + NetESignal*arg = new NetESignal(net); + arg->set_line(*net); + + fun->parm(0, arg); + return fun; + } + } + + if (net->class_type() && member_path.size() > 0) { + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Ident " << base_path + << " look for class property " << member_path + << endl; + } + + ivl_assert(*this, member_path.size() == 1); + const name_component_t member_comp = member_path.front(); + return check_for_class_property(this, des, use_scope, + net, member_comp); + } + + if (net->enumeration() && member_path.size() > 0) { + const netenum_t*netenum = net->enumeration(); + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr: " + << "Ident " << base_path + << " look for enumeration method " << member_path + << endl; + } + + NetESignal*expr = new NetESignal(net); + expr->set_line(*this); + ivl_assert(*this, member_path.size() == 1); + const name_component_t member_comp = member_path.front(); + ivl_assert(*this, member_comp.index.empty()); + return check_for_enum_methods(this, des, use_scope, + netenum, base_path, member_comp.name, + expr, expr_wid, NULL, 0); + } + + ivl_assert(*this, member_path.size() == 0); NetExpr*tmp = elaborate_expr_net(des, scope, net, found_in, expr_wid, flags); @@ -3852,6 +4264,13 @@ scope->is_const_func(false); } + if (member_path.size() > 0) { + cerr << get_fileline() << ": error: Event name " << base_path + << " can't have member names (member_path=" << member_path << ")" + << endl; + des->errors += 1; + } + NetEEvent*tmp = new NetEEvent(eve); tmp->set_line(*this); return tmp; @@ -3874,75 +4293,6 @@ return tmp; } - // Maybe this is a method attached to an enumeration name? If - // this is SystemVerilog, then test to see if the name is - // really a method attached to an object. - if (gn_system_verilog() && found_in==0 && path_.size() >= 2) { - pform_name_t use_path = path_; - name_component_t member_comp = use_path.back(); - use_path.pop_back(); - - if (debug_elaborate) - cerr << get_fileline() << ": PEIdent::elaborate_expr: " - << "Look for base_path " << use_path - << " for member " << member_comp << "." << endl; - - ivl_assert(*this, net == 0); - symbol_search(this, des, use_scope, use_path, net, par, eve, ex1, ex2); - - // Check to see if we have a net and if so is it an - // enumeration? If so then check to see if this is an - // enumeration method call. - if (net != 0) { - // If this net is actually an enum, the method may - // be an enumeration method. - if (const netenum_t*netenum = net->enumeration()) { - // We may need the net expression for the - // enumeration variable so get it. - NetESignal*expr = new NetESignal(net); - expr->set_line(*this); - // This expression cannot be a select! - assert(use_path.back().index.empty()); - - return check_for_enum_methods(this, des, use_scope, - netenum, - use_path, member_comp.name, - expr, expr_wid, NULL, 0); - } - - // If this net is a struct, the method name may be - // a struct member. - if (net->struct_type() != 0) { - if (debug_elaborate) { - cerr << get_fileline() << ": debug: " - << "PEIdent::elaborate_expr: " - << "Ident " << use_path - << " is a struct." - << " Expecting " << net->packed_dims().size() - << "-1 dimensions, " - << "got " << use_path.back().index.size() << "." << endl; - } - - NetExpr*tmp = check_for_struct_members(this, des, use_scope, - net, use_path.back().index, - member_comp); - if (!tmp) return 0; - - return pad_to_width(tmp, expr_wid, signed_flag_, *this); - } - - if (net->class_type() != 0) { - if (debug_elaborate) { - cerr << get_fileline() << ": PEIdent::elaborate_expr: " - << "Ident " << use_path - << " look for property " << member_comp << endl; - } - - return check_for_class_property(this, des, use_scope, - net, member_comp); - } - } - } // At this point we've exhausted all the possibilities that // are not scopes. If this is not a system task argument, then @@ -3951,7 +4301,7 @@ if ( !(SYS_TASK_ARG & flags) ) { // I cannot interpret this identifier. Error message. cerr << get_fileline() << ": error: Unable to bind " - << (NEED_CONST & flags ? "parameter" : "wire/reg/memory") + << ((NEED_CONST & flags) ? "parameter" : "wire/reg/memory") << " `" << path_ << "' in `" << scope_path(scope) << "'" << endl; if (scope->need_const_func()) { @@ -4045,7 +4395,7 @@ // If the input is a string, and the part select is working on // byte boundaries, then make the result into a string. if (par_val.is_string() && (labs(lsv)%8 == 0) && (wid%8 == 0)) - return result.as_string(); + return verinum(result.as_string()); return result; } @@ -4562,6 +4912,13 @@ const name_component_t&name_tail = path_.back(); + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_net_word_: " + << "expr_wid=" << expr_wid + << ", net->get_scalar()==" << (net->get_scalar()?"true":"false") + << endl; + } + // Special case: This is the entire array, and we are a direct // argument of a system task. if (name_tail.index.empty() && (SYS_TASK_ARG & flags)) { @@ -5256,6 +5613,15 @@ unsigned expr_wid, unsigned flags) const { + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_expr_net: " + << "net=" << net->name() + << ", net->unpacked_dimensions()=" << net->unpacked_dimensions() + << ", net->get_scalar()=" << (net->get_scalar()?"true":"false") + << ", net->net_type()=" << *net->net_type() + << endl; + } + if (net->unpacked_dimensions() > 0) return elaborate_expr_net_word_(des, scope, net, found_in, expr_wid, flags); @@ -5269,8 +5635,7 @@ if (! path_.back().index.empty()) use_sel = path_.back().index.back().sel; - if (net->get_scalar() && - use_sel != index_component_t::SEL_NONE) { + if (net->get_scalar() && use_sel != index_component_t::SEL_NONE) { cerr << get_fileline() << ": error: can not select part of "; if (node->expr_type() == IVL_VT_REAL) cerr << "real: "; else cerr << "scalar: "; @@ -5354,12 +5719,11 @@ return tmp; } -/* - * This method should never actually be called. - */ -NetExpr* PENewArray::elaborate_expr(Design*, NetScope*, unsigned, unsigned) const +NetExpr* PENewArray::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const { - ivl_assert(*this, 0); + cerr << get_fileline() << ": error: The new array constructor may " + "only be used in an assignment to a dynamic array." << endl; + des->errors += 1; return 0; } @@ -5646,7 +6010,7 @@ unsigned PEString::test_width(Design*, NetScope*, width_mode_t&) { expr_type_ = IVL_VT_BOOL; - expr_width_ = text_? verinum(text_).len() : 0; + expr_width_ = text_ ? verinum(text_).len() : 0; min_width_ = expr_width_; signed_flag_ = false; diff -Nru iverilog-10.3/elab_lval.cc iverilog-11.0/elab_lval.cc --- iverilog-10.3/elab_lval.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/elab_lval.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2019 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -29,6 +29,7 @@ # include "netdarray.h" # include "netparray.h" # include "netvector.h" +# include "netenum.h" # include "compiler.h" # include # include @@ -75,15 +76,8 @@ */ NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool) const { - NetNet*ll = 0; - if (ll == 0) { - cerr << get_fileline() << ": Assignment l-value too complex." - << endl; - return 0; - } - - NetAssign_*lv = new NetAssign_(ll); - return lv; + cerr << get_fileline() << ": Assignment l-value too complex." << endl; + return 0; } /* @@ -153,57 +147,6 @@ return res; } -NetAssign_*PEIdent::scan_lname_for_nested_members_(Design*des, NetScope*scope, - const pform_name_t&cur_path) const -{ - if (cur_path.size() == 1) - return 0; - - pform_name_t use_path = cur_path; - name_component_t tail = use_path.back(); - use_path.pop_back(); - - NetNet* reg = 0; - const NetExpr*par = 0; - NetEvent* eve = 0; - symbol_search(this, des, scope, use_path, reg, par, eve); - - if (reg == 0) { - NetAssign_*tmp = scan_lname_for_nested_members_(des, scope, use_path); - if (tmp == 0) - return 0; - - tmp = new NetAssign_(tmp); - tmp->set_property(tail.name); - return tmp; - } - - if (reg->struct_type() && reg->struct_type()->packed()) { - NetAssign_*tmp = new NetAssign_(reg); - elaborate_lval_net_packed_member_(des, scope, tmp, tail); - return tmp; - } -#if 0 - if (reg->struct_type() && reg->struct_type()->packed()) { - cerr << get_fileline() << ": sorry: " - << "I don't know what to do with packed struct " << use_path - << " with member " << tail << "." << endl; - return 0; - } -#endif - if (reg->struct_type() && !reg->struct_type()->packed()) { - cerr << get_fileline() << ": sorry: " - << "I don't know what to do with unpacked struct " << use_path - << " with member " << tail << "." << endl; - return 0; - } - - if (reg->class_type()) { - return elaborate_lval_net_class_member_(des, scope, reg, tail.name); - } - - return 0; -} /* * Handle the ident as an l-value. This includes bit and part selects @@ -217,7 +160,6 @@ NetNet* reg = 0; const NetExpr*par = 0; NetEvent* eve = 0; - perm_string method_name; if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval: " @@ -238,35 +180,31 @@ ivl_assert(*this, use_scope); } - symbol_search(this, des, use_scope, path_, reg, par, eve); - - /* If the signal is not found, check to see if this is a - member of a struct. Take the name of the form "a.b.member", - remove the member and store it into method_name, and retry - the search with "a.b". */ - if (reg == 0 && path_.size() >= 2) { - pform_name_t use_path = path_; - perm_string tmp_name = peek_tail_name(use_path); - use_path.pop_back(); - symbol_search(this, des, use_scope, use_path, reg, par, eve); - - if (reg && reg->struct_type()) { - method_name = tmp_name; - - } else if (reg && reg->class_type()) { - method_name = tmp_name; - - } else if (NetAssign_*subl = scan_lname_for_nested_members_(des, use_scope, path_)) { - return subl; - - } else { - reg = 0; - } + /* Try to find the base part of the path that names the + variable. The remainer is the member path. For example, if + the path is a.b.c.d, and a.b is the path to a variable, + then a.b becomes the base_path and c.d becomes the + member_path. If we cannot find the variable with any + prefix, then the base_path will be empty after this loop + and reg will remain nil. */ + pform_name_t base_path = path_; + pform_name_t member_path; + while (reg == 0 && base_path.size() > 0) { + symbol_search(this, des, use_scope, base_path, reg, par, eve); + // Found it! + if (reg != 0) break; + // Not found. Try to pop another name off the base_path + // and push it to the front of the member_path. + member_path.push_front( base_path.back() ); + base_path.pop_back(); } + + /* The l-value must be a variable. If not, then give up and + print a useful error message. */ if (reg == 0) { if (use_scope->type()==NetScope::FUNC - && use_scope->func_def()->return_sig()==0 + && use_scope->func_def()->is_void() && use_scope->basename()==peek_tail_name(path_)) { cerr << get_fileline() << ": error: " << "Cannot assign to " << path_ @@ -285,7 +223,10 @@ if (debug_elaborate) { cerr << get_fileline() << ": PEIdent::elaborate_lval: " - << "Found l-value as reg." + << "Found l-value path_=" << path_ + << " as reg=" << reg->name() + << " base_path=" << base_path + << ", member_path=" << member_path << " unpacked_dimensions()=" << reg->unpacked_dimensions() << endl; } @@ -331,21 +272,27 @@ return 0; } - if (reg->struct_type() && !method_name.nil()) { + + // If we find that the matched variable is a packed struct, + // then we can handled it with the net_packed_member_ method. + if (reg->struct_type() && member_path.size() > 0) { NetAssign_*lv = new NetAssign_(reg); - name_component_t tmp_name (method_name); - elaborate_lval_net_packed_member_(des, use_scope, lv, tmp_name); + elaborate_lval_net_packed_member_(des, use_scope, lv, member_path); return lv; } - if (reg->class_type() && !method_name.nil() && gn_system_verilog()) { - NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, method_name); + // If the variable is a class object, then handle it with the + // net_class_member_ method. + if (reg->class_type() && member_path.size() > 0 && gn_system_verilog()) { + NetAssign_*lv = elaborate_lval_net_class_member_(des, use_scope, reg, member_path); return lv; } + // Past this point, we should have taken care of the cases // where the name is a member/method of a struct/class. - ivl_assert(*this, method_name.nil()); + // XXXX ivl_assert(*this, method_name.nil()); + ivl_assert(*this, member_path.size() == 0); bool need_const_idx = is_cassign || is_force || (reg->type()==NetNet::UNRESOLVED_WIRE); @@ -412,7 +359,7 @@ { if (!gn_system_verilog()) return 0; - if (scope->parent() == 0) + if (scope->parent() == 0 || scope->type() == NetScope::CLASS) return 0; if (path_.size() != 1) return 0; @@ -1070,87 +1017,140 @@ return true; } +/* + * When the l-value turns out to be a class object, this method is + * called with the bound variable, and the method path. For example, + * if path_=a.b.c and a.b binds to the variable, then sig is b, and + * member_path=c. if path_=obj.base.x, and base_path=obj, then sig is + * obj, and member_path=base.x. + */ NetAssign_* PEIdent::elaborate_lval_net_class_member_(Design*des, NetScope*scope, - NetNet*sig, const perm_string&method_name) const + NetNet*sig, pform_name_t member_path) const { if (debug_elaborate) { - cerr << get_fileline() << ": elaborate_lval_net_class_member_: " - << "l-value is property " << method_name + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: " + << "l-value is property " << member_path << " of " << sig->name() << "." << endl; } const netclass_t*class_type = sig->class_type(); ivl_assert(*this, class_type); - /* Make sure the property is really present in the class. If - not, then generate an error message and return an error. */ - int pidx = class_type->property_idx_from_name(method_name); - if (pidx < 0) { - cerr << get_fileline() << ": error: Class " << class_type->get_name() - << " does not have a property " << method_name << "." << endl; - des->errors += 1; - return 0; - } + // Iterate over the member_path. This handles nested class + // object, by generating nested NetAssign_ object. We start + // with lv==0, so the front of the member_path is the member + // of the outermost class. This generates an lv from sig. Then + // iterate over the remaining of the member_path, replacing + // the outer lv with an lv that nests the lv from the previous + // iteration. + NetAssign_*lv = 0; + do { + // Start with the first component of the member path... + perm_string method_name = peek_head_name(member_path); + // Pull that component from the member_path. We need to + // know the current member being worked on, and will + // need to know if there are more members to be worked on. + name_component_t member_cur = member_path.front(); + member_path.pop_front(); - property_qualifier_t qual = class_type->get_prop_qual(pidx); - if (qual.test_local() && ! class_type->test_scope_is_method(scope)) { - cerr << get_fileline() << ": error: " - << "Local property " << class_type->get_prop_name(pidx) - << " is not accessible (l-value) in this context." - << " (scope=" << scope_path(scope) << ")" << endl; - des->errors += 1; + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_class_member_: " + << "Processing member_cur=" << member_cur + << endl; + } - } else if (qual.test_static()) { + // Make sure the property is really present in the class. If + // not, then generate an error message and return an error. + int pidx = class_type->property_idx_from_name(method_name); + if (pidx < 0) { + cerr << get_fileline() << ": error: Class " << class_type->get_name() + << " does not have a property " << method_name << "." << endl; + des->errors += 1; + return 0; + } - // Special case: this is a static property. Ignore the - // "this" sig and use the property itself, which is not - // part of the sig, as the l-value. - NetNet*psig = class_type->find_static_property(method_name); - ivl_assert(*this, psig); + property_qualifier_t qual = class_type->get_prop_qual(pidx); + if (qual.test_local() && ! class_type->test_scope_is_method(scope)) { + cerr << get_fileline() << ": error: " + << "Local property " << class_type->get_prop_name(pidx) + << " is not accessible (l-value) in this context." + << " (scope=" << scope_path(scope) << ")" << endl; + des->errors += 1; - NetAssign_*lv = new NetAssign_(psig); - return lv; + } else if (qual.test_static()) { - } else if (qual.test_const()) { - cerr << get_fileline() << ": error: " - << "Property " << class_type->get_prop_name(pidx) - << " is constant in this context." << endl; - des->errors += 1; - } + // Special case: this is a static property. Ignore the + // "this" sig and use the property itself, which is not + // part of the sig, as the l-value. + NetNet*psig = class_type->find_static_property(method_name); + ivl_assert(*this, psig); - NetAssign_*lv = new NetAssign_(sig); - lv->set_property(method_name); + lv = new NetAssign_(psig); + return lv; - ivl_type_t ptype = class_type->get_prop_type(pidx); - const netdarray_t*mtype = dynamic_cast (ptype); - if (mtype) { - const name_component_t&name_tail = path_.back(); - if (! name_tail.index.empty()) { - cerr << get_fileline() << ": sorry: " - << "Array index of array properties not supported." - << endl; + } else if (qual.test_const()) { + cerr << get_fileline() << ": error: " + << "Property " << class_type->get_prop_name(pidx) + << " is constant in this context." << endl; des->errors += 1; } - } + + lv = lv? new NetAssign_(lv) : new NetAssign_(sig); + lv->set_property(method_name); + + // Now get the type of the property. + ivl_type_t ptype = class_type->get_prop_type(pidx); + const netdarray_t*mtype = dynamic_cast (ptype); + if (mtype) { + if (! member_cur.index.empty()) { + cerr << get_fileline() << ": sorry: " + << "Array index of array properties not supported." + << endl; + des->errors += 1; + } + } + + // If the current member is a class object, then get the + // type. We may wind up iterating, and need the proper + // class type. + class_type = dynamic_cast(ptype); + + } while (member_path.size() > 0); + return lv; } - +/* + * This method is caled to handle l-value identifiers that are packed + * structs. The lv is already attached to the variable, so this method + * calculates the part select that is defined by the member_path. For + * example, if the path_ is main.sub.sub_local, and the variable is + * main, then we know at this point that main is a packed struct, and + * lv contains the reference to the bound variable (main). In this + * case member_path==sub.sub_local, and it is up to this method to + * work out the part select that the member_path represents. + */ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope, NetAssign_*lv, - const name_component_t&member_comp) const + pform_name_t member_path) const { - const perm_string&member_name = member_comp.name; + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "path_=" << path_ + << " member_path=" << member_path + << endl; + } + NetNet*reg = lv->sig(); ivl_assert(*this, reg); const netstruct_t*struct_type = reg->struct_type(); ivl_assert(*this, struct_type); - if (debug_elaborate) { - cerr << get_fileline() << ": debug: elaborate lval packed member: " - << "path_=" << path_ << endl; + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "Type=" << *struct_type + << endl; } if (! struct_type->packed()) { @@ -1160,99 +1160,302 @@ return false; } - // Shouldn't be seeing unpacked arrays of packed structs... - ivl_assert(*this, reg->unpacked_dimensions() == 0); - - // This is a packed member, so the name is of the form - // "a.b[...].c[...]" which means that the path_ must have at - // least 2 components. We are processing "c[...]" at that - // point (otherwise known as member_name) so we'll save a - // reference to it in name_tail. We are also processing "b[]" - // so save that as name_base. - - ivl_assert(*this, path_.size() >= 2); - + // Looking for the base name. We need that to know about + // indices we may need to apply. This is to handle the case + // that the base is an array of structs, and not just a + // struct. pform_name_t::const_reverse_iterator name_idx = path_.rbegin(); - ivl_assert(*this, name_idx->name == member_name); - const name_component_t&name_tail = *name_idx; - ++ name_idx; - const name_component_t&name_base = *name_idx; - - // Calculate the offset within the packed structure of the - // member, and any indices. We will add in the offset of the - // struct into the packed array later. Note that this works - // for packed unions as well (although the offset will be 0 - // for union members). - unsigned long off; - const netstruct_t::member_t* member = struct_type->packed_member(member_name, off); - - if (member == 0) { - cerr << get_fileline() << ": error: Member " << member_name - << " is not a member of variable " << reg->name() << endl; + for (size_t idx = 1 ; idx < member_path.size() ; idx += 1) + ++ name_idx; + if (name_idx->name != peek_head_name(member_path)) { + cerr << get_fileline() << ": internal error: " + << "name_idx=" << name_idx->name + << ", expecting member_name=" << peek_head_name(member_path) + << endl; des->errors += 1; return false; } + ivl_assert(*this, name_idx->name == peek_head_name(member_path)); + ++ name_idx; + const name_component_t&name_base = *name_idx; - unsigned long use_width = member->net_type->packed_width(); - - // Get the index component type. At this point, we only - // support bit select or none. - index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; - if (!name_tail.index.empty()) - use_sel = name_tail.index.back().sel; + // Shouldn't be seeing unpacked arrays of packed structs... + ivl_assert(*this, reg->unpacked_dimensions() == 0); - if (use_sel != index_component_t::SEL_NONE && use_sel != index_component_t::SEL_BIT) { - cerr << get_fileline() << ": sorry: Assignments to part selects of " - "a struct member are not yet supported." << endl; - des->errors += 1; - return false; - } + // These make up the "part" select that is the equivilent of + // following the member path through the nested structs. To + // start with, the off[set] is zero, and use_width is the + // width of the entire variable. The first member_comp is at + // some offset within the variable, and will have a reduced + // width. As we step through the member_path the off + // increases, and use_width shrinks. + unsigned long off = 0; + unsigned long use_width = struct_type->packed_width(); + + pform_name_t completed_path; + do { + const name_component_t member_comp = member_path.front(); + const perm_string&member_name = member_comp.name; - if (! name_tail.index.empty()) { + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "Processing member_comp=" << member_comp + << " (completed_path=" << completed_path << ")" + << endl; + } - // If there are index expressions in this l-value - // expression, then the implicit assumption is that the - // member is a vector type with packed dimensions. For - // example, if the l-value expression is "foo.member[1][2]", - // then the member should be something like: - // ... logic [h:l][m:n] foo; - // Get the dimensions from the netvector_t that this implies. - const netvector_t*mem_vec = dynamic_cast(member->net_type); - ivl_assert(*this, mem_vec); - const vector&mem_packed_dims = mem_vec->packed_dims(); + // This is a packed member, so the name is of the form + // "a.b[...].c[...]" which means that the path_ must have at + // least 2 components. We are processing "c[...]" at that + // point (otherwise known as member_name) and we have a + // reference to it in member_comp. + + // The member_path is the members we want to follow for the + // variable. For example, main[N].a.b may have main[N] as the + // base_name, and member_path=a.b. The member_name is the + // start of the member_path, and is "a". The member_name + // should be a member of the struct_type type. + + // Calculate the offset within the packed structure of the + // member, and any indices. We will add in the offset of the + // struct into the packed array later. Note that this works + // for packed unions as well (although the offset will be 0 + // for union members). + unsigned long tmp_off; + const netstruct_t::member_t* member = struct_type->packed_member(member_name, tmp_off); + + if (member == 0) { + cerr << get_fileline() << ": error: Member " << member_name + << " is not a member of struct type of " + << reg->name() + << "." << completed_path << endl; + des->errors += 1; + return false; + } + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "Member type: " << *(member->net_type) + << endl; + } - if (name_tail.index.size() > mem_packed_dims.size()) { - cerr << get_fileline() << ": error: " - << "Too many index expressions for member." << endl; + off += tmp_off; + ivl_assert(*this, use_width >= (unsigned long)member->net_type->packed_width()); + use_width = member->net_type->packed_width(); + + // At this point, off and use_width are the part select + // expressed by the member_comp, which is a member of the + // struct. We can further refine the part select with any + // indices that might be present. + + // Get the index component type. At this point, we only + // support bit select or none. + index_component_t::ctype_t use_sel = index_component_t::SEL_NONE; + if (!member_comp.index.empty()) + use_sel = member_comp.index.back().sel; + + if (use_sel != index_component_t::SEL_NONE + && use_sel != index_component_t::SEL_BIT + && use_sel != index_component_t::SEL_PART) { + cerr << get_fileline() << ": sorry: Assignments to part selects of " + "a struct member are not yet supported." << endl; des->errors += 1; return false; } - // Evaluate all but the last index expression, into prefix_indices. - listprefix_indices; - bool rc = evaluate_index_prefix(des, scope, prefix_indices, name_tail.index); - ivl_assert(*this, rc); - - // Evaluate the last index expression into a constant long. - NetExpr*texpr = elab_and_eval(des, scope, name_tail.index.back().msb, -1, true); - long tmp; - if (texpr == 0 || !eval_as_long(tmp, texpr)) { - cerr << get_fileline() << ": error: " - "Array index expressions must be constant here." << endl; + if (const netvector_t*mem_vec = dynamic_cast(member->net_type)) { + // If the member type is a netvector_t, then it is a + // vector of atom or scaler objects. For example, if the + // l-value expression is "foo.member[1][2]", + // then the member should be something like: + // ... logic [h:l][m:n] member; + // There should be index expressions index the vector + // down, but there doesn't need to be all of them. We + // can, for example, be selecting a part of the vector. + + // We only need to process this if there are any + // index expressions. If not, then the packed + // vector can be handled atomically. + + // In any case, this should be the tail of the + // member_path, because the array element of this + // kind of array cannot be a struct. + if (member_comp.index.size() > 0) { + // These are the dimensions defined by the type + const vector&mem_packed_dims = mem_vec->packed_dims(); + + if (member_comp.index.size() > mem_packed_dims.size()) { + cerr << get_fileline() << ": error: " + << "Too many index expressions for member." << endl; + des->errors += 1; + return false; + } + + // Evaluate all but the last index expression, into prefix_indices. + listprefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index); + ivl_assert(*this, rc); + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "prefix_indices.size()==" << prefix_indices.size() + << ", mem_packed_dims.size()==" << mem_packed_dims.size() + << " (netvector_t context)" + << endl; + } + + long tail_off = 0; + unsigned long tail_wid = 0; + rc = calculate_part(this, des, scope, member_comp.index.back(), tail_off, tail_wid); + ivl_assert(*this, rc); + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "calculate_part for tail returns tail_off=" << tail_off + << ", tail_wid=" << tail_wid + << endl; + } + + // Now use the prefix_to_slice function to calculate the + // offset and width of the addressed slice of the member. + long loff; + unsigned long lwid; + prefix_to_slice(mem_packed_dims, prefix_indices, tail_off, loff, lwid); + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "Calculate loff=" << loff << " lwid=" << lwid + << " tail_off=" << tail_off << " tail_wid=" << tail_wid + << " off=" << off << " use_width=" << use_width + << endl; + } + + off += loff; + use_width = lwid * tail_wid; + } + + // The netvector_t only has atom elements, to + // there is no next struct type. + struct_type = 0; + + } else if (const netparray_t*array = dynamic_cast (member->net_type)) { + // If the member is a parray, then the elements + // are themselves packed object, including + // possibly a struct. Handle this by taking the + // part select of the current part of the + // variable, then stepping to the element type to + // possibly iterate through more of the member_path. + + ivl_assert(*this, array->packed()); + ivl_assert(*this, member_comp.index.size() > 0); + + // These are the dimensions defined by the type + const vector&mem_packed_dims = array->static_dimensions(); + + if (member_comp.index.size() != mem_packed_dims.size()) { + cerr << get_fileline() << ": error: " + << "Incorrect number of index expressions for member " + << member_name << "." << endl; + des->errors += 1; + return false; + } + + // Evaluate all but the last index expression, into prefix_indices. + listprefix_indices; + bool rc = evaluate_index_prefix(des, scope, prefix_indices, member_comp.index); + ivl_assert(*this, rc); + + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "prefix_indices.size()==" << prefix_indices.size() + << ", mem_packed_dims.size()==" << mem_packed_dims.size() + << " (netparray_t context)" + << endl; + } + + // Evaluate the last index expression into a constant long. + NetExpr*texpr = elab_and_eval(des, scope, member_comp.index.back().msb, -1, true); + long tmp; + if (texpr == 0 || !eval_as_long(tmp, texpr)) { + cerr << get_fileline() << ": error: " + << "Array index expressions for member " << member_name + << " must be constant here." << endl; + des->errors += 1; + return false; + } + + delete texpr; + + // Now use the prefix_to_slice function to calculate the + // offset and width of the addressed slice of the member. + long loff; + unsigned long lwid; + prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid); + + ivl_type_t element_type = array->element_type(); + long element_width = element_type->packed_width(); + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "parray subselection loff=" << loff + << ", lwid=" << lwid + << ", element_width=" << element_width + << endl; + } + + // The width and offset calculated from the + // indices is actually in elements, and not + // bits. In fact, in this context, the lwid should + // come down to 1 (one element). + off += loff * element_width; + ivl_assert(*this, lwid==1); + use_width = element_width; + + // To move on to the next component in the member + // path, get the element type. For example, for + // the path a.b[1].c, we are processing b[1] here, + // and the element type should be a netstruct_t + // that will wind up containing the member c. + struct_type = dynamic_cast (element_type); + + } else if (const netstruct_t*tmp_struct = dynamic_cast (member->net_type)) { + // If the member is itself a struct, then get + // ready to go on to the next iteration. + struct_type = tmp_struct; + + } else if (const netenum_t*tmp_enum = dynamic_cast (member->net_type)) { + // If the element is an enum, then we don't have + // anything special to do. + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "Tail element is an enum: " << *tmp_enum + << endl; + } + struct_type = 0; + + } else { + // Unknown type? + cerr << get_fileline() << ": internal error: " + << "Unexpected member type? " << *(member->net_type) + << endl; des->errors += 1; return false; + struct_type = 0; } - delete texpr; + // Complete this component of the path, mark it + // completed, and set up for the next component. + completed_path .push_back(member_comp); + member_path.pop_front(); - // Now use the prefix_to_slice function to calculate the - // offset and width of the addressed slice of the member. - long loff; - unsigned long lwid; - prefix_to_slice(mem_packed_dims, prefix_indices, tmp, loff, lwid); + } while (member_path.size() > 0 && struct_type != 0); - off += loff; - use_width = lwid; + if (debug_elaborate) { + cerr << get_fileline() << ": PEIdent::elaborate_lval_net_packed_member_: " + << "After processing member_path, " + << "off=" << off + << ", use_width=" << use_width + << ", completed_path=" << completed_path + << ", member_path=" << member_path + << endl; } // The dimensions in the expression must match the packed diff -Nru iverilog-10.3/elaborate.cc iverilog-11.0/elaborate.cc --- iverilog-10.3/elaborate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/elaborate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -29,6 +29,7 @@ # include # include +# include # include # include # include "pform.h" @@ -109,24 +110,6 @@ return; } -#if 0 - // MTW, 01-Mar-2013. The expression elaboration rework should have - // ensured that this can no longer occur. Leaving this here for the - // moment, but it should be safe to remove it. - if (type_is_vectorable(rval_expr->expr_type()) - && type_is_vectorable(lval->data_type()) - && rval_expr->expr_width() < lval->vector_width()) { - if (debug_elaborate) { - cerr << get_fileline() << ": debug: " - << "r-value expressions width "<expr_width() - << " of " << (rval_expr->has_sign()? "signed":"unsigned") - << " expression is to small for l-value width " - << lval->vector_width() << "." << endl; - } - rval_expr = pad_to_width(rval_expr, lval->vector_width(), *this); - } -#endif - NetNet*rval = rval_expr->synthesize(des, scope, rval_expr); if (rval == 0) { @@ -1136,6 +1119,37 @@ des->errors += 1; } +static void isolate_and_connect(Design*des, NetScope*scope, const PGModule*mod, + NetNet*port, NetNet*sig, NetNet::PortType ptype) +{ + switch (ptype) { + case NetNet::POUTPUT: + { + NetBUFZ*tmp = new NetBUFZ(scope, scope->local_symbol(), + sig->vector_width(), true); + tmp->set_line(*mod); + des->add_node(tmp); + connect(tmp->pin(1), port->pin(0)); + connect(tmp->pin(0), sig->pin(0)); + } + break; + case NetNet::PINOUT: + { + NetTran*tmp = new NetTran(scope, scope->local_symbol(), + sig->vector_width(), + sig->vector_width(), 0); + tmp->set_line(*mod); + des->add_node(tmp); + connect(tmp->pin(1), port->pin(0)); + connect(tmp->pin(0), sig->pin(0)); + } + break; + default: + ivl_assert(*mod, 0); + break; + } +} + /* * Instantiate a module by recursively elaborating it. Set the path of * the recursive elaboration so that signal names get properly @@ -1160,6 +1174,7 @@ // the source list is rearranged by name binding into this list. vectorpins (rmod->port_count()); vectorpins_fromwc (rmod->port_count(), false); + vectorpins_is_explicitly_not_connected (rmod->port_count(), false); // If the instance has a pins_ member, then we know we are // binding by name. Therefore, make up a pins array that @@ -1173,7 +1188,7 @@ // Handle wildcard named port if (pins_[idx].name[0] == '*') { for (unsigned j = 0 ; j < nexp ; j += 1) { - if (!pins[j]) { + if ((!pins[j]) && (!pins_is_explicitly_not_connected[j])) { pins_fromwc[j] = true; NetNet* net = 0; const NetExpr*par = 0; @@ -1228,6 +1243,8 @@ // OK, do the binding by placing the expression in // the right place. pins[pidx] = pins_[idx].parm; + if (!pins[pidx]) + pins_is_explicitly_not_connected[pidx] = true; } @@ -1440,7 +1457,8 @@ // that connects to the port. NetNet*sig = 0; - if (prts.empty() || (prts[0]->port_type() == NetNet::PINPUT)) { + NetNet::PortType ptype = prts[0]->port_type(); + if (prts.empty() || (ptype == NetNet::PINPUT)) { // Special case: If the input port is an unpacked // array, then there should be no sub-ports and @@ -1533,7 +1551,7 @@ sig->vector_width()); } - } else if (prts[0]->port_type() == NetNet::PINOUT) { + } else if (ptype == NetNet::PINOUT) { // For now, do not support unpacked array outputs. ivl_assert(*this, prts[0]->unpacked_dimensions()==0); @@ -1594,7 +1612,7 @@ } else { /* Port type must be OUTPUT here. */ - ivl_assert(*this, prts[0]->port_type() == NetNet::POUTPUT); + ivl_assert(*this, ptype == NetNet::POUTPUT); // Special case: If the output port is an unpacked // array, then there should be no sub-ports and @@ -1657,11 +1675,9 @@ } prts_vector_width = sig->vector_width(); for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { - prts[pidx]->port_type(NetNet::NOT_A_PORT); prts[pidx] = cast_to_int4(des, scope, prts[pidx], prts_vector_width / instance.size()); - prts[pidx]->port_type(NetNet::POUTPUT); } } @@ -1670,9 +1686,7 @@ if ((sig->data_type() == IVL_VT_REAL ) && !prts.empty() && (prts[0]->data_type() != IVL_VT_REAL )) { prts_vector_width -= prts[0]->vector_width() - 1; - prts[0]->port_type(NetNet::NOT_A_PORT); prts[0] = cast_to_real(des, scope, prts[0]); - prts[0]->port_type(NetNet::POUTPUT); // No support for multiple real drivers. if (instance.size() != 1) { cerr << pins[idx]->get_fileline() << ": error: " @@ -1690,10 +1704,8 @@ if ((sig->data_type() == IVL_VT_BOOL ) && !prts.empty() && (prts[0]->data_type() == IVL_VT_LOGIC )) { for (unsigned pidx = 0; pidx < prts.size(); pidx += 1) { - prts[pidx]->port_type(NetNet::NOT_A_PORT); prts[pidx] = cast_to_int2(des, scope, prts[pidx], prts[pidx]->vector_width()); - prts[pidx]->port_type(NetNet::POUTPUT); } } @@ -1717,7 +1729,7 @@ #ifndef NDEBUG if ((! prts.empty()) - && (prts[0]->port_type() != NetNet::PINPUT)) { + && (ptype != NetNet::PINPUT)) { assert(sig->type() != NetNet::REG); } #endif @@ -1750,7 +1762,7 @@ && (prts_vector_width != sig->vector_width())) { bool as_signed = false; - switch (prts[0]->port_type()) { + switch (ptype) { case NetNet::POUTPUT: as_signed = prts[0]->get_signed(); break; @@ -1774,7 +1786,7 @@ " bits, got " << sig->vector_width() << "." << endl; // Delete this when inout ports pad correctly. - if (prts[0]->port_type() == NetNet::PINOUT) { + if (ptype == NetNet::PINOUT) { if (prts_vector_width > sig->vector_width()) { cerr << get_fileline() << ": : Leaving " << (prts_vector_width-sig->vector_width()) @@ -1794,7 +1806,7 @@ << " high bits of the port." << endl; } else { - if (prts[0]->port_type() == NetNet::PINPUT) { + if (ptype == NetNet::PINPUT) { cerr << get_fileline() << ": : Pruning "; } else { cerr << get_fileline() << ": : Padding "; @@ -1806,7 +1818,7 @@ } sig = resize_net_to_port_(des, scope, sig, prts_vector_width, - prts[0]->port_type(), as_signed); + ptype, as_signed); } // Connect the sig expression that is the context of the @@ -1837,8 +1849,15 @@ // The simplest case, there are no // parts/concatenations on the inside of the // module, so the port and sig need simply be - // connected directly. - connect(prts[0]->pin(0), sig->pin(0)); + // connected directly. But don't collapse ports + // that are a delay path destination, to avoid + // the delay being applied to other drivers of + // the external signal. + if (prts[0]->delay_paths() > 0) { + isolate_and_connect(des, scope, this, prts[0], sig, ptype); + } else { + connect(prts[0]->pin(0), sig->pin(0)); + } } else if (sig->vector_width()==prts_vector_width/instance.size() && prts.size()/instance.size() == 1) { @@ -1854,10 +1873,15 @@ // The signal width is exactly the width of a // single instance of the port. In this case, // connect the sig to all the ports identically. - for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1) - connect(prts[ldx]->pin(0), sig->pin(0)); + for (unsigned ldx = 0 ; ldx < prts.size() ; ldx += 1) { + if (prts[ldx]->delay_paths() > 0) { + isolate_and_connect(des, scope, this, prts[ldx], sig, ptype); + } else { + connect(prts[ldx]->pin(0), sig->pin(0)); + } + } - } else switch (prts[0]->port_type()) { + } else switch (ptype) { case NetNet::POUTPUT: ctmp = new NetConcat(scope, scope->local_symbol(), prts_vector_width, prts.size()); @@ -2227,8 +2251,10 @@ return; } - cerr << get_fileline() << ": internal error: Unknown module type: " << - type_ << endl; + if (!ignore_missing_modules) { + cerr << get_fileline() << ": internal error: Unknown module type: " << + type_ << endl; + } } void PGModule::elaborate_scope(Design*des, NetScope*sc) const @@ -2277,9 +2303,11 @@ // Not a module or primitive that I know about or can find by // any means, so give up. - cerr << get_fileline() << ": error: Unknown module type: " << type_ << endl; - missing_modules[type_] += 1; - des->errors += 1; + if (!ignore_missing_modules) { + cerr << get_fileline() << ": error: Unknown module type: " << type_ << endl; + missing_modules[type_] += 1; + des->errors += 1; + } } @@ -2335,10 +2363,17 @@ { ivl_assert(*this, rval_); - NetExpr*rv = rval_->elaborate_expr(des, scope, net_type, 0); + NetExpr*rv = elab_and_eval(des, scope, rval_, net_type, is_constant_); - ivl_assert(*this, !is_constant_); - return rv; + if (!is_constant_ || !rv) return rv; + + cerr << get_fileline() << ": error: " + "The RHS expression must be constant." << endl; + cerr << get_fileline() << " : " + "This expression violates the rule: " << *rv << endl; + des->errors += 1; + delete rv; + return 0; } NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope, @@ -2379,6 +2414,18 @@ { NetExpr*dex = elab_and_eval(des, scope, expr, -1); + // If the elab_and_eval returns nil, then the function + // failed. It should already have printed an error message, + // but we can add some detail. Lets add the error count, just + // in case. + if (dex == 0) { + cerr << expr->get_fileline() << ": error: " + << "Unable to elaborate (or evaluate) delay expression." + << endl; + des->errors += 1; + return 0; + } + check_for_inconsistent_delays(scope); /* If the delay expression is a real constant or vector @@ -2930,11 +2977,12 @@ if (nscope == 0) nscope = scope; - // Handle the special case that the block contains only one - // statement. There is no need to keep the block node. Also, - // don't elide named blocks, because they might be referenced - // elsewhere. - if ((list_.size() == 1) && (pscope_name() == 0)) { + // Handle the special case that the sequential block contains + // only one statement. There is no need to keep the block node. + // Also, don't elide named blocks, because they might be + // referenced elsewhere. + if ((type == NetBlock::SEQU) && (list_.size() == 1) && + (pscope_name() == 0)) { assert(list_[0]); NetProc*tmp = list_[0]->elaborate(des, nscope); return tmp; @@ -3125,7 +3173,7 @@ icount += cur->expr.size(); } - NetCase*res = new NetCase(type_, expr, icount); + NetCase*res = new NetCase(quality_, type_, expr, icount); res->set_line(*this); /* Iterate over all the case items (guard/statement pairs) @@ -3548,6 +3596,21 @@ vectorargv (1 + nparms); argv[0] = sig; + if (method_name == "delete") { + // The queue delete method takes an optional element. + if (net->queue_type()) { + if (nparms > 1) { + cerr << get_fileline() << ": error: queue delete() " + << "method takes zero or one argument." << endl; + des->errors += 1; + } + } else if (nparms > 0) { + cerr << get_fileline() << ": error: darray delete() " + << "method takes no arguments." << endl; + des->errors += 1; + } + } + for (unsigned idx = 0 ; idx < nparms ; idx += 1) { PExpr*ex = parms_[idx]; if (ex != 0) { @@ -3564,6 +3627,80 @@ return sys; } +/* + * This private method is called to elaborate queue push methods. The + * sys_task_name is the internal system-task name to use. + */ +NetProc* PCallTask::elaborate_queue_method_(Design*des, NetScope*scope, + NetNet*net, + perm_string method_name, + const char*sys_task_name) const +{ + NetESignal*sig = new NetESignal(net); + sig->set_line(*this); + + unsigned nparms = parms_.size(); + // insert() requires two arguments. + if ((method_name == "insert") && (nparms != 2)) { + cerr << get_fileline() << ": error: " << method_name + << "() method requires two arguments." << endl; + des->errors += 1; + } + // push_front() and push_back() require one argument. + if ((method_name != "insert") && (nparms != 1)) { + cerr << get_fileline() << ": error: " << method_name + << "() method requires a single argument." << endl; + des->errors += 1; + } + + // Get the context width if this is a logic type. + ivl_variable_type_t base_type = net->darray_type()->element_base_type(); + int context_width = -1; + switch (base_type) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + context_width = net->darray_type()->element_width(); + break; + default: + break; + } + + vectorargv (nparms+1); + argv[0] = sig; + if (method_name != "insert") { + if ((nparms == 0) || (parms_[0] == 0)) { + argv[1] = 0; + cerr << get_fileline() << ": error: " << method_name + << "() methods first argument is missing." << endl; + des->errors += 1; + } else + argv[1] = elab_and_eval(des, scope, parms_[0], context_width, + false, false, base_type); + } else { + if ((nparms == 0) || (parms_[0] == 0)) { + argv[1] = 0; + cerr << get_fileline() << ": error: " << method_name + << "() methods first argument is missing." << endl; + des->errors += 1; + } else + argv[1] = elab_and_eval(des, scope, parms_[0], 32, + false, false, IVL_VT_LOGIC); + + if ((nparms < 2) || (parms_[1] == 0)) { + argv[2] = 0; + cerr << get_fileline() << ": error: " << method_name + << "() methods second argument is missing." << endl; + des->errors += 1; + } else + argv[2] = elab_and_eval(des, scope, parms_[1], context_width, + false, false, base_type); + } + + NetSTask*sys = new NetSTask(sys_task_name, IVL_SFUNC_AS_TASK_IGNORE, argv); + sys->set_line(*this); + return sys; +} + NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope, bool add_this_flag) const { @@ -3597,20 +3734,20 @@ // Is this a delete method for dynamic arrays? if (net->darray_type() && method_name=="delete") { - return elaborate_sys_task_method_(des, scope, net, - method_name, + return elaborate_sys_task_method_(des, scope, net, method_name, "$ivl_darray_method$delete"); } if (net->queue_type()) { - if (method_name=="push_back") - return elaborate_sys_task_method_(des, scope, net, - method_name, - "$ivl_queue_method$push_back"); - if (method_name=="push_front") - return elaborate_sys_task_method_(des, scope, net, - method_name, - "$ivl_queue_method$push_front"); + if (method_name == "push_back") + return elaborate_queue_method_(des, scope, net, method_name, + "$ivl_queue_method$push_back"); + else if (method_name == "push_front") + return elaborate_queue_method_(des, scope, net, method_name, + "$ivl_queue_method$push_front"); + else if (method_name == "insert") + return elaborate_queue_method_(des, scope, net, method_name, + "$ivl_queue_method$insert"); } if (const netclass_t*class_type = net->class_type()) { @@ -3670,6 +3807,9 @@ // call with a missing return assignment. if (! func) return 0; + if (gn_system_verilog() && func->is_void()) + return elaborate_void_function_(des, scope, func); + // Generate a function call version of this task call. PExpr*rval = new PECallFunction(package_, path_, parms_); rval->set_file(get_file()); @@ -3684,6 +3824,21 @@ return tmp->elaborate(des, scope); } +NetProc* PCallTask::elaborate_void_function_(Design*des, NetScope*scope, + NetFuncDef*def) const +{ + NetScope*dscope = def->scope(); + + if (debug_elaborate) { + cerr << get_fileline() << ": PCallTask::elaborate_void_function_: " + << "function void " << scope_path(dscope) + << endl; + } + + ivl_assert(*this, dscope->elab_stage() >= 3); + return elaborate_build_call_(des, scope, dscope, 0); +} + NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope, NetScope*task, NetExpr*use_this) const { @@ -3699,7 +3854,7 @@ } else if (task->type() == NetScope::FUNC) { NetFuncDef*tmp = task->func_def(); - if (tmp->return_sig() != 0) { + if (!tmp->is_void()) { cerr << get_fileline() << ": error: " << "Calling a non-void function as a task." << endl; des->errors += 1; @@ -4214,6 +4369,7 @@ NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*this); + ev->local_flag(true); unsigned expr_count = 0; NetEvWait*wa = new NetEvWait(enet); @@ -4224,14 +4380,18 @@ if (expr_.count() == 0) { assert(enet); - /* For synthesis we want just the inputs, but for the rest we - * want inputs and outputs that may cause a value to change. */ + /* For synthesis or always_comb/latch we want just the inputs, + * but for the rest we want inputs and outputs that may cause + * a value to change. */ extern bool synthesis; /* Synthesis flag from main.cc */ bool rem_out = false; - if (synthesis) { + if (synthesis || always_sens_) { rem_out = true; } - NexusSet*nset = enet->nex_input(rem_out); + // If this is an always_comb/latch then we need an implicit T0 + // trigger of the event expression. + if (always_sens_) wa->set_t0_trigger(); + NexusSet*nset = enet->nex_input(rem_out, always_sens_); if (nset == 0) { cerr << get_fileline() << ": error: Unable to elaborate:" << endl; @@ -4241,6 +4401,8 @@ } if (nset->size() == 0) { + if (always_sens_) return wa; + cerr << get_fileline() << ": warning: @* found no " "sensitivities so it will never trigger." << endl; @@ -4260,8 +4422,40 @@ NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::ANYEDGE, nset->size()); - for (unsigned idx = 0 ; idx < nset->size() ; idx += 1) - connect(nset->at(idx).lnk, pr->pin(idx)); + for (unsigned idx = 0 ; idx < nset->size() ; idx += 1) { + unsigned wid = nset->at(idx).wid; + unsigned vwid = nset->at(idx).lnk.nexus()->vector_width(); + // Is this a part select? + if (always_sens_ && (wid != vwid)) { + cerr << get_fileline() << ": sorry: constant " + "selects in always_* processes are not " + "currently supported (all bits will be " + "included)." << endl; +# if 0 + unsigned base = nset->at(idx).base; +cerr << get_fileline() << ": base = " << base << endl; +// FIXME: make this work with selects that go before the base. + assert(base < vwid); + if (base + wid > vwid) wid = vwid - base; +cerr << get_fileline() << ": base = " << base << ", width = " << wid + << ", expr width = " << vwid << endl; +nset->at(idx).lnk.dump_link(cerr, 4); +cerr << endl; +// FIXME: Convert the link to the appropriate NetNet + netvector_t*tmp_vec = new netvector_t(IVL_VT_BOOL, vwid, 0); + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::IMPLICIT, tmp_vec); + NetPartSelect*tmp = new NetPartSelect(sig, base, wid, NetPartSelect::VP); + des->add_node(tmp); + tmp->set_line(*this); +// FIXME: create a part select to get the correct bits to connect. + connect(tmp->pin(1), nset->at(idx).lnk); + connect(tmp->pin(0), pr->pin(idx)); +# endif + connect(nset->at(idx).lnk, pr->pin(idx)); + } else { + connect(nset->at(idx).lnk, pr->pin(idx)); + } + } delete nset; des->add_node(pr); @@ -4281,7 +4475,13 @@ const NetExpr*par = 0; NetEvent* eve = 0; - NetScope*found_in = symbol_search(this, des, scope, + NetScope*use_scope = scope; + if (id->package()) { + use_scope = des->find_package(id->package()->pscope_name()); + ivl_assert(*this, use_scope); + } + + NetScope*found_in = symbol_search(this, des, use_scope, id->path(), sig, par, eve); @@ -4327,8 +4527,9 @@ NetExpr*tmp = elab_and_eval(des, scope, expr_[idx]->expr(), -1); if (tmp == 0) { - expr_[idx]->dump(cerr); - cerr << endl; + cerr << get_fileline() << ": error: " + "Failed to evaluate event expression '" + << *expr_[idx] << "'." << endl; des->errors += 1; continue; } @@ -4490,6 +4691,8 @@ /* Create an event wait and an otherwise unreferenced event variable to force a perpetual wait. */ NetEvent*wait_event = new NetEvent(scope->local_symbol()); + wait_event->set_line(*this); + wait_event->local_flag(true); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0); @@ -4510,6 +4713,8 @@ eval_expr(expr); NetEvent*wait_event = new NetEvent(scope->local_symbol()); + wait_event->set_line(*this); + wait_event->local_flag(true); scope->add_event(wait_event); NetEvWait*wait = new NetEvWait(0 /* noop */); @@ -5188,9 +5393,20 @@ des->errors += 1; return 0; } + ivl_assert(*this, target->type() == NetScope::FUNC); + + if (target->func_def()->is_void()) { + if (expr_) { + cerr << get_fileline() << ": error: " + << "A value can't be returned from a void function." << endl; + des->errors += 1; + return 0; + } + NetDisable*disa = new NetDisable(target); + disa->set_line( *this ); + return disa; + } - // We don't yet support void functions, so require an - // expression for the return statement. if (expr_ == 0) { cerr << get_fileline() << ": error: " << "Return from " << scope_path(target) @@ -5200,6 +5416,7 @@ } NetNet*res = target->find_signal(target->basename()); + ivl_assert(*this, res); ivl_variable_type_t lv_type = res->data_type(); unsigned long wid = res->vector_width(); NetAssign_*lv = new NetAssign_(res); @@ -5303,11 +5520,17 @@ { assert(scope); + NetScope*use_scope = scope; + if (package_) { + use_scope = des->find_package(package_->pscope_name()); + ivl_assert(*this, use_scope); + } + NetNet* sig = 0; const NetExpr*par = 0; NetEvent* eve = 0; - NetScope*found_in = symbol_search(this, des, scope, event_, + NetScope*found_in = symbol_search(this, des, use_scope, event_, sig, par, eve); if (found_in == 0) { @@ -5384,7 +5607,10 @@ gets into its wait statement before non-combinational code is executed. */ do { - if (top->type() != IVL_PR_ALWAYS) + if ((top->type() != IVL_PR_ALWAYS) && + (top->type() != IVL_PR_ALWAYS_COMB) && + (top->type() != IVL_PR_ALWAYS_FF) && + (top->type() != IVL_PR_ALWAYS_LATCH)) break; NetEvWait*st = dynamic_cast(top->statement()); @@ -5551,7 +5777,7 @@ NetDelaySrc*path = new NetDelaySrc(scope, scope->local_symbol(), src.size(), condit_sig, - conditional); + conditional, !full_flag_); path->set_line(*this); // The presence of the data_source_expression indicates @@ -5699,6 +5925,10 @@ // Elaborate class definitions. elaborate_classes(des, scope, classes); + // Elaborate the variable initialization statements, making a + // single initial process out of them. + result_flag &= elaborate_var_inits_(des, scope); + return result_flag; } @@ -6064,7 +6294,7 @@ class top_defparams : public elaborator_work_item_t { public: - top_defparams(Design*des__) + explicit top_defparams(Design*des__) : elaborator_work_item_t(des__) { } @@ -6095,7 +6325,7 @@ class later_defparams : public elaborator_work_item_t { public: - later_defparams(Design*des__) + explicit later_defparams(Design*des__) : elaborator_work_item_t(des__) { } @@ -6129,30 +6359,101 @@ } }; +static ostream& operator<< (ostream&o, ivl_process_type_t t) +{ + switch (t) { + case IVL_PR_ALWAYS: + o << "always"; + break; + case IVL_PR_ALWAYS_COMB: + o << "always_comb"; + break; + case IVL_PR_ALWAYS_FF: + o << "always_ff"; + break; + case IVL_PR_ALWAYS_LATCH: + o << "always_latch"; + break; + case IVL_PR_INITIAL: + o << "initial"; + break; + case IVL_PR_FINAL: + o << "final"; + break; + } + return o; +} + bool Design::check_proc_delay() const { - bool result_flag = true; + bool result = false; for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) { - /* If this is an always block and we have no or zero delay then - * a runtime infinite loop will happen. If we possible have some + /* If this is an always process and we have no or zero delay then + * a runtime infinite loop will happen. If we possibly have some * delay then print a warning that an infinite loop is possible. */ if (pr->type() == IVL_PR_ALWAYS) { DelayType dly_type = pr->statement()->delay_type(); if (dly_type == NO_DELAY || dly_type == ZERO_DELAY) { - cerr << pr->get_fileline() << ": error: always" - << " statement does not have any delay." << endl; - cerr << pr->get_fileline() << ": : A runtime" - << " infinite loop will occur." << endl; - result_flag = false; + cerr << pr->get_fileline() << ": error: always " + "process does not have any delay." << endl; + cerr << pr->get_fileline() << ": : A runtime " + "infinite loop will occur." << endl; + result = true; } else if (dly_type == POSSIBLE_DELAY && warn_inf_loop) { - cerr << pr->get_fileline() << ": warning: always" - << " statement may not have any delay." << endl; - cerr << pr->get_fileline() << ": : A runtime" - << " infinite loop may be possible." << endl; + cerr << pr->get_fileline() << ": warning: always " + "process may not have any delay." << endl; + cerr << pr->get_fileline() << ": : A runtime " + << "infinite loop may be possible." << endl; + } + } + + // The always_comb/ff/latch processes have special delay rules + // that need to be checked. + if ((pr->type() == IVL_PR_ALWAYS_COMB) || + (pr->type() == IVL_PR_ALWAYS_FF) || + (pr->type() == IVL_PR_ALWAYS_LATCH)) { + const NetEvWait *wait = dynamic_cast (pr->statement()); + if (! wait) { + // The always_comb/latch processes have an event + // control added automatically by the compiler. + assert(pr->type() == IVL_PR_ALWAYS_FF); + cerr << pr->get_fileline() << ": error: the first " + "statement of an always_ff process must be " + "an event control statement." << endl; + result = true; + } else if (wait->statement()->delay_type(true) != NO_DELAY) { + cerr << pr->get_fileline() << ": error: there must "; + + if (pr->type() == IVL_PR_ALWAYS_FF) { + cerr << "only be a single event control and " + "no blocking delays in an always_ff " + "process."; + } else { + cerr << "be no event controls or blocking " + "delays in an " << pr->type() + << " process."; + } + cerr << endl; + result = true; + } + + if ((pr->type() != IVL_PR_ALWAYS_FF) && + (wait->nevents() == 0)) { + if (pr->type() == IVL_PR_ALWAYS_LATCH) { + cerr << pr->get_fileline() << ": error: " + "always_latch process has no event " + "control." << endl; + result = true; + } else { + assert(pr->type() == IVL_PR_ALWAYS_COMB); + cerr << pr->get_fileline() << ": warning: " + "always_comb process has no " + "sensitivities." << endl; + } } } @@ -6163,37 +6464,287 @@ if (pr->type() == IVL_PR_FINAL) { DelayType dly_type = pr->statement()->delay_type(); - if (dly_type != NO_DELAY && dly_type != ZERO_DELAY) { + if (dly_type != NO_DELAY) { cerr << pr->get_fileline() << ": error: final" << " statement contains a delay." << endl; - result_flag = false; + result = true; } } } - return result_flag; + return result; } -void Design::root_elaborate(void) +static void print_nexus_name(const Nexus*nex) { - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++ cur) { - netclass_t*cur_class = cur->second; - PClass*cur_pclass = class_to_pclass_[cur_class]; - cur_class->elaborate(this, cur_pclass); + for (const Link*cur = nex->first_nlink(); cur; cur = cur->next_nlink()) { + if (cur->get_dir() != Link::OUTPUT) continue; + const NetPins*obj = cur->get_obj(); + // For a NetNet (signal) just use the name. + if (const NetNet*net = dynamic_cast(obj)) { + cerr << net->name(); + return; + // For a NetPartSelect calculate the name. + } else if (const NetPartSelect*ps = dynamic_cast(obj)) { + assert(ps->pin_count() >= 2); + assert(ps->pin(1).get_dir() == Link::INPUT); + assert(ps->pin(1).is_linked()); + print_nexus_name(ps->pin(1).nexus()); + cerr << "[]"; + return; + // For a NetUReduce calculate the name. + } else if (const NetUReduce*reduce = dynamic_cast(obj)) { + assert(reduce->pin_count() == 2); + assert(reduce->pin(1).get_dir() == Link::INPUT); + assert(reduce->pin(1).is_linked()); + switch (reduce->type()) { + case NetUReduce::AND: + cerr << "&"; + break; + case NetUReduce::OR: + cerr << "|"; + break; + case NetUReduce::XOR: + cerr << "^"; + break; + case NetUReduce::NAND: + cerr << "~&"; + break; + case NetUReduce::NOR: + cerr << "~|"; + break; + case NetUReduce::XNOR: + cerr << "~^"; + break; + case NetUReduce::NONE: + assert(0); + } + print_nexus_name(reduce->pin(1).nexus()); + return; + } else if (const NetLogic*logic = dynamic_cast(obj)) { + assert(logic->pin_count() >= 2); + assert(logic->pin(1).get_dir() == Link::INPUT); + assert(logic->pin(1).is_linked()); + switch (logic->type()) { + case NetLogic::NOT: + cerr << "~"; + break; + default: + // The other operators should never be used here, + // so just return the nexus name. + cerr << nex->name(); + return; + } + print_nexus_name(logic->pin(1).nexus()); + return; + } + // Use the following to find the type of anything that may be missing: + // cerr << "(" << typeid(*obj).name() << ") "; } + // Otherwise just use the nexus name so somthing is printed. + cerr << nex->name(); +} - for (map::iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { +static void print_event_probe_name(const NetEvProbe *prb) +{ + assert(prb->pin_count() == 1); + assert(prb->pin(0).get_dir() == Link::INPUT); + assert(prb->pin(0).is_linked()); + print_nexus_name(prb->pin(0).nexus()); +} - if (debug_elaborate) { - cerr << cur->second->get_fileline() << ": Design::root_elaborate: " - << "Elaborate for root task/func " << scope_path(cur->first) << endl; +static void check_event_probe_width(const LineInfo *info, const NetEvProbe *prb) +{ + assert(prb->pin_count() == 1); + assert(prb->pin(0).get_dir() == Link::INPUT); + assert(prb->pin(0).is_linked()); + if (prb->edge() == NetEvProbe::ANYEDGE) return; + if (prb->pin(0).nexus()->vector_width() > 1) { + cerr << info->get_fileline() << " Warning: Synthesis wants " + "the sensitivity list expressions for '"; + switch (prb->edge()) { + case NetEvProbe::POSEDGE: + cerr << "posedge "; + break; + case NetEvProbe::NEGEDGE: + cerr << "negedge "; + break; + default: + break; } + print_nexus_name(prb->pin(0).nexus()); + cerr << "' to be a single bit." << endl; + } +} - cur->second->elaborate(this, cur->first); +static void check_ff_sensitivity(const NetProc* statement) +{ + const NetEvWait *evwt = dynamic_cast (statement); + // We have already checked for and reported if the first statmemnt is + // not a wait. + if (! evwt) return; + + for (unsigned cevt = 0; cevt < evwt->nevents(); cevt += 1) { + const NetEvent *evt = evwt->event(cevt); + for (unsigned cprb = 0; cprb < evt->nprobe(); cprb += 1) { + const NetEvProbe *prb = evt->probe(cprb); + check_event_probe_width(evwt, prb); + if (prb->edge() == NetEvProbe::ANYEDGE) { + cerr << evwt->get_fileline() << " Warning: Synthesis " + "requires the sensitivity list of an " + "always_ff process to only be edge " + "sensitive. "; + print_event_probe_name(prb); + cerr << " is missing a pos/negedge." << endl; + } + } } +} +/* + * Check to see if the always_* processes only contain synthesizable + * constructs. + */ +bool Design::check_proc_synth() const +{ + bool result = false; + for (const NetProcTop*pr = procs_ ; pr ; pr = pr->next_) { + if ((pr->type() == IVL_PR_ALWAYS_COMB) || + (pr->type() == IVL_PR_ALWAYS_FF) || + (pr->type() == IVL_PR_ALWAYS_LATCH)) { + result |= pr->statement()->check_synth(pr->type(), + pr->scope()); + if (pr->type() == IVL_PR_ALWAYS_FF) { + check_ff_sensitivity(pr->statement()); + } + } + } + return result; +} + +/* + * Check whether all design elements have an explicit timescale or all + * design elements use the default timescale. If a mixture of explicit + * and default timescales is found, a warning message is output. Note + * that we only need to check the top level design elements - nested + * design elements will always inherit the timescale from their parent + * if they don't have any local timescale declarations. + * + * NOTE: Strictly speaking, this should be an error for SystemVerilog + * (1800-2012 section 3.14.2). + */ +static void check_timescales(bool&some_explicit, bool&some_implicit, + const PScope*scope) +{ + if (scope->time_unit_is_default) + some_implicit = true; + else + some_explicit = true; + if (scope->time_prec_is_default) + some_implicit = true; + else + some_explicit = true; +} + +static void check_timescales() +{ + bool some_explicit = false; + bool some_implicit = false; + map::iterator mod; + for (mod = pform_modules.begin(); mod != pform_modules.end(); ++mod) { + const Module*mp = (*mod).second; + check_timescales(some_explicit, some_implicit, mp); + if (some_explicit && some_implicit) + break; + } + map::iterator pkg; + if (gn_system_verilog() && !(some_explicit && some_implicit)) { + for (pkg = pform_packages.begin(); pkg != pform_packages.end(); ++pkg) { + const PPackage*pp = (*pkg).second; + check_timescales(some_explicit, some_implicit, pp); + if (some_explicit && some_implicit) + break; + } + } + if (gn_system_verilog() && !(some_explicit && some_implicit)) { + for (unsigned idx = 0; idx < pform_units.size(); idx += 1) { + const PPackage*pp = pform_units[idx]; + // We don't need a timescale if the compilation unit + // contains no items outside a design element. + if (pp->parameters.empty() && + pp->localparams.empty() && + pp->wires.empty() && + pp->tasks.empty() && + pp->funcs.empty() && + pp->classes.empty()) + continue; + + check_timescales(some_explicit, some_implicit, pp); + if (some_explicit && some_implicit) + break; + } + } + + if (!(some_explicit && some_implicit)) + return; + + if (gn_system_verilog()) { + cerr << "warning: " + << "Some design elements have no explicit time unit and/or" + << endl; + cerr << " : " + << "time precision. This may cause confusing timing results." + << endl; + cerr << " : " + << "Affected design elements are:" + << endl; + } else { + cerr << "warning: " + << "Some modules have no timescale. This may cause" + << endl; + cerr << " : " + << "confusing timing results. Affected modules are:" + << endl; + } + + for (mod = pform_modules.begin(); mod != pform_modules.end(); ++mod) { + Module*mp = (*mod).second; + if (mp->has_explicit_timescale()) + continue; + cerr << " : -- module " << (*mod).first + << " declared here: " << mp->get_fileline() << endl; + } + + if (!gn_system_verilog()) + return; + + for (pkg = pform_packages.begin(); pkg != pform_packages.end(); ++pkg) { + PPackage*pp = (*pkg).second; + if (pp->has_explicit_timescale()) + continue; + cerr << " : -- package " << (*pkg).first + << " declared here: " << pp->get_fileline() << endl; + } + + for (unsigned idx = 0; idx < pform_units.size(); idx += 1) { + PPackage*pp = pform_units[idx]; + if (pp->has_explicit_timescale()) + continue; + + if (pp->parameters.empty() && + pp->localparams.empty() && + pp->wires.empty() && + pp->tasks.empty() && + pp->funcs.empty() && + pp->classes.empty()) + continue; + + cerr << " : -- compilation unit"; + if (pform_units.size() > 1) { + cerr << " from: " << pp->get_file(); + } + cerr << endl; + } } /* @@ -6215,8 +6766,13 @@ Design* elaborate(listroots) { + unsigned npackages = pform_packages.size(); + if (gn_system_verilog()) + npackages += pform_units.size(); + vector root_elems(roots.size()); - vector pack_elems(pform_packages.size()); + vector pack_elems(npackages); + map unit_scopes; bool rc = true; unsigned i = 0; @@ -6224,24 +6780,39 @@ // module and elaborate what I find. Design*des = new Design; - // Elaborate enum sets in $root scope. - elaborate_rootscope_enumerations(des); + // Elaborate the compilation unit scopes. From here on, these are + // treated as an additional set of packages. + if (gn_system_verilog()) { + for (i = 0; i < pform_units.size(); i += 1) { + PPackage*unit = pform_units[i]; + NetScope*scope = des->make_package_scope(unit->pscope_name(), 0, true); + scope->set_line(unit); + scope->add_imports(&unit->explicit_imports); + set_scope_timescale(des, scope, unit); + + elaborator_work_item_t*es = new elaborate_package_t(des, scope, unit); + des->elaboration_work_list.push_back(es); - // Elaborate tasks and functions in $root scope. - elaborate_rootscope_tasks(des); + pack_elems[i].pack = unit; + pack_elems[i].scope = scope; - // Elaborate classes in $root scope. - elaborate_rootscope_classes(des); + unit_scopes[unit] = scope; + } + } // Elaborate the packages. Package elaboration is simpler - // because there are fewer sub-scopes involved. - i = 0; + // because there are fewer sub-scopes involved. Note that + // in SystemVerilog, packages are not allowed to refer to + // the compilation unit scope, but the VHDL preprocessor + // assumes they can. for (map::iterator pac = pform_packages.begin() ; pac != pform_packages.end() ; ++ pac) { ivl_assert(*pac->second, pac->first == pac->second->pscope_name()); - NetScope*scope = des->make_package_scope(pac->first); + NetScope*unit_scope = unit_scopes[pac->second->parent_scope()]; + NetScope*scope = des->make_package_scope(pac->first, unit_scope, false); scope->set_line(pac->second); + scope->add_imports(&pac->second->explicit_imports); set_scope_timescale(des, scope, pac->second); elaborator_work_item_t*es = new elaborate_package_t(des, scope, pac->second); @@ -6271,14 +6842,19 @@ // Get the module definition for this root instance. Module *rmod = (*mod).second; + // Get the compilation unit scope for this module. + NetScope*unit_scope = unit_scopes[rmod->parent_scope()]; + // Make the root scope. This makes a NetScope object and // pushes it into the list of root scopes in the Design. - NetScope*scope = des->make_root_scope(*root, rmod->program_block, + NetScope*scope = des->make_root_scope(*root, unit_scope, + rmod->program_block, rmod->is_interface); // Collect some basic properties of this scope from the // Module definition. scope->set_line(rmod); + scope->add_imports(&rmod->explicit_imports); set_scope_timescale(des, scope, rmod); // Save this scope, along with its definition, in the @@ -6343,6 +6919,10 @@ if (des->errors > 0) return des; + // Now we have the full design, check for timescale inconsistencies. + if (warn_timescale) + check_timescales(); + if (debug_elaborate) { cerr << ": elaborate: " << "Start calling Package elaborate_sig methods." << endl; @@ -6371,8 +6951,6 @@ << "Start calling $root elaborate_sig methods." << endl; } - des->root_elaborate_sig(); - if (debug_elaborate) { cerr << ": elaborate: " << "Start calling root module elaborate_sig methods." << endl; @@ -6433,8 +7011,6 @@ rc &= pkg->elaborate(des, scope); } - des->root_elaborate(); - for (i = 0; i < root_elems.size(); i++) { Module *rmod = root_elems[i].mod; NetScope *scope = root_elems[i].scope; @@ -6449,10 +7025,11 @@ // Now that everything is fully elaborated verify that we do // not have an always block with no delay (an infinite loop), // or a final block with a delay. - if (des->check_proc_delay() == false) { - delete des; - des = 0; - } + bool has_failure = des->check_proc_delay(); + + // Check to see if the always_comb/ff/latch processes only have + // synthesizable constructs + has_failure |= des->check_proc_synth(); if (debug_elaborate) { cerr << "" << ": debug: " @@ -6460,5 +7037,10 @@ << des->find_root_scopes().size() << " root scopes " << endl; } + if (has_failure) { + delete des; + des = 0; + } + return des; } diff -Nru iverilog-10.3/elab_scope.cc iverilog-11.0/elab_scope.cc --- iverilog-10.3/elab_scope.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/elab_scope.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -59,11 +59,11 @@ { scope->time_unit(pscope->time_unit); scope->time_precision(pscope->time_precision); - scope->time_from_timescale(pscope->time_from_timescale); + scope->time_from_timescale(pscope->has_explicit_timescale()); des->set_precision(pscope->time_precision); } -typedef map::const_iterator mparm_it_t; +typedef map::const_iterator mparm_it_t; static void collect_parm_item_(Design*des, NetScope*scope, perm_string name, const LexicalScope::param_expr_t&cur, @@ -110,65 +110,37 @@ } static void collect_scope_parameters_(Design*des, NetScope*scope, - const map¶meters) + const map¶meters) { for (mparm_it_t cur = parameters.begin() ; cur != parameters.end() ; ++ cur ) { - // A parameter can not have the same name as a genvar. - if (scope->find_genvar((*cur).first)) { - cerr << cur->second.get_fileline() - << ": error: parameter and genvar in '" - << scope->fullname() << "' have the same name '" - << (*cur).first << "'." << endl; - des->errors += 1; - } - - collect_parm_item_(des, scope, (*cur).first, (*cur).second, false, false); + collect_parm_item_(des, scope, cur->first, *(cur->second), false, false); } } static void collect_scope_localparams_(Design*des, NetScope*scope, - const map&localparams) + const map&localparams) { for (mparm_it_t cur = localparams.begin() ; cur != localparams.end() ; ++ cur ) { - // A localparam can not have the same name as a genvar. - if (scope->find_genvar((*cur).first)) { - cerr << cur->second.get_fileline() - << ": error: localparam and genvar in '" - << scope->fullname() << "' have the same name '" - << (*cur).first << "'." << endl; - des->errors += 1; - } - - collect_parm_item_(des, scope, (*cur).first, (*cur).second, false, true); + collect_parm_item_(des, scope, cur->first, *(cur->second), false, true); } } static void collect_scope_specparams_(Design*des, NetScope*scope, - const map&specparams) + const map&specparams) { for (mparm_it_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { - // A specparam can not have the same name as a genvar. - if (scope->find_genvar((*cur).first)) { - cerr << cur->second.get_fileline() - << ": error: specparam and genvar in '" - << scope->fullname() << "' have the same name '" - << (*cur).first << "'." << endl; - des->errors += 1; - } - - collect_parm_item_(des, scope, (*cur).first, (*cur).second, true, false); + collect_parm_item_(des, scope, cur->first, *(cur->second), true, false); } } /* - * Elaborate the enumeration into the given scope. If scope==0, then - * the enumeration goes into $root instead of a scope. + * Elaborate the enumeration into the given scope. */ static void elaborate_scope_enumeration(Design*des, NetScope*scope, enum_type_t*enum_type) @@ -193,10 +165,7 @@ enum_type); use_enum->set_line(enum_type->li); - if (scope) - scope->add_enumeration_set(enum_type, use_enum); - else - des->add_enumeration_set(enum_type, use_enum); + scope->add_enumeration_set(enum_type, use_enum); size_t name_idx = 0; // Find the enumeration width. @@ -364,10 +333,7 @@ } rc_flag = use_enum->insert_name(name_idx, cur->name, cur_value); - if (scope) - rc_flag &= scope->add_enumeration_name(use_enum, cur->name); - else - rc_flag &= des->add_enumeration_name(use_enum, cur->name); + rc_flag &= scope->add_enumeration_name(use_enum, cur->name); if (! rc_flag) { cerr << use_enum->get_fileline() @@ -397,15 +363,6 @@ } } -void elaborate_rootscope_enumerations(Design*des) -{ - for (set::const_iterator cur = pform_enum_sets.begin() - ; cur != pform_enum_sets.end() ; ++ cur) { - enum_type_t*curp = *cur; - elaborate_scope_enumeration(des, 0, curp); - } -} - /* * If the pclass includes an implicit and explicit constructor, then * merge the implicit constructor into the explicit constructor as @@ -496,7 +453,9 @@ if (debug_scopes) { cerr << pclass->get_fileline() <<": elaborate_scope_class: " - << "Elaborate scope class " << pclass->pscope_name() << endl; + << "Elaborate scope class " << pclass->pscope_name() + << " within scope " << scope_path(scope) + << endl; } class_type_t*base_class = dynamic_cast (use_type->base_type); @@ -509,10 +468,7 @@ netclass_t*use_base_class = 0; if (base_class) { - if (scope) - use_base_class = scope->find_class(base_class->name); - if (use_base_class == 0) - use_base_class = des->find_class(base_class->name); + use_base_class = scope->find_class(base_class->name); if (use_base_class == 0) { cerr << pclass->get_fileline() << ": error: " << "Base class " << base_class->name @@ -526,22 +482,34 @@ ivl_assert(*pclass, use_type->save_elaborated_type == 0); use_type->save_elaborated_type = use_class; - // Class scopes have no parent scope, because references are - // not allowed to escape a class method. - NetScope*class_scope = new NetScope(0, hname_t(pclass->pscope_name()), - NetScope::CLASS); + NetScope*class_scope = new NetScope(scope, hname_t(pclass->pscope_name()), + NetScope::CLASS, scope->unit()); class_scope->set_line(pclass); class_scope->set_class_def(use_class); use_class->set_class_scope(class_scope); use_class->set_definition_scope(scope); set_scope_timescale(des, class_scope, pclass); + class_scope->add_typedefs(&pclass->typedefs); + + // Elaborate enum types declared in the class. We need these + // now because enumeration constants can be used during scope + // elaboration. + if (debug_scopes) { + cerr << pclass->get_fileline() << ": elaborate_scope_class: " + << "Elaborate " << pclass->enum_sets.size() << " enumerations" + << " in class " << scope_path(class_scope) + << ", scope=" << scope_path(scope) << "." + << endl; + } + elaborate_scope_enumerations(des, class_scope, pclass->enum_sets); + // Collect the properties, elaborate them, and add them to the // elaborated class definition. for (map::iterator cur = use_type->properties.begin() ; cur != use_type->properties.end() ; ++ cur) { - ivl_type_s*tmp = cur->second.type->elaborate_type(des, scope); + ivl_type_s*tmp = cur->second.type->elaborate_type(des, class_scope); ivl_assert(*pclass, tmp); if (debug_scopes) { cerr << pclass->get_fileline() << ": elaborate_scope_class: " @@ -560,6 +528,7 @@ // Task methods are always automatic... method_scope->is_auto(true); method_scope->set_line(cur->second); + method_scope->add_imports(&cur->second->explicit_imports); if (debug_scopes) { cerr << cur->second->get_fileline() << ": elaborate_scope_class: " @@ -578,6 +547,7 @@ // Function methods are always automatic... method_scope->is_auto(true); method_scope->set_line(cur->second); + method_scope->add_imports(&cur->second->explicit_imports); if (debug_scopes) { cerr << cur->second->get_fileline() << ": elaborate_scope_class: " @@ -588,12 +558,7 @@ cur->second->elaborate_scope(des, method_scope); } - if (scope) { - scope->add_class(use_class); - - } else { - des->add_class(use_class, pclass); - } + scope->add_class(use_class); } static void elaborate_scope_classes(Design*des, NetScope*scope, @@ -605,14 +570,6 @@ } } -void elaborate_rootscope_classes(Design*des) -{ - for (size_t idx = 0 ; idx < pform_classes.size() ; idx += 1) { - blend_class_constructors(pform_classes[idx]); - elaborate_scope_class(des, 0, pform_classes[idx]); - } -} - static void replace_scope_parameters_(NetScope*scope, const LineInfo&loc, const Module::replace_t&replacements) { @@ -661,11 +618,7 @@ NetScope*task_scope = new NetScope(scope, use_name, NetScope::TASK); task_scope->is_auto(task->is_auto()); task_scope->set_line(task); - - if (scope==0) { - set_scope_timescale(des, task_scope, task); - des->add_root_task(task_scope, task); - } + task_scope->add_imports(&task->explicit_imports); if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_task: " @@ -683,39 +636,6 @@ for (tasks_it_t cur = tasks.begin() ; cur != tasks.end() ; ++ cur ) { - hname_t use_name( (*cur).first ); - // A task can not have the same name as another scope object. - const NetScope *child = scope->child(use_name); - if (child) { - cerr << cur->second->get_fileline() << ": error: task and "; - child->print_type(cerr); - cerr << " in '" << scope->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - continue; - } - - // A task can not have the same name as a genvar. - if (scope->find_genvar((*cur).first)) { - cerr << cur->second->get_fileline() - << ": error: task and genvar in '" - << scope->fullname() << "' have the same name '" - << (*cur).first << "'." << endl; - des->errors += 1; - } - - // A task can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = scope->get_parameter(des, (*cur).first, - ex_msb, ex_lsb); - if (parm) { - cerr << cur->second->get_fileline() - << ": error: task and parameter in '" - << scope->fullname() << "' have the same name '" - << (*cur).first << "'." << endl; - des->errors += 1; - } - elaborate_scope_task(des, scope, cur->second); } @@ -728,11 +648,7 @@ NetScope*task_scope = new NetScope(scope, use_name, NetScope::FUNC); task_scope->is_auto(task->is_auto()); task_scope->set_line(task); - - if (scope==0) { - set_scope_timescale(des, task_scope, task); - des->add_root_task(task_scope, task); - } + task_scope->add_imports(&task->explicit_imports); if (debug_scopes) { cerr << task->get_fileline() << ": elaborate_scope_func: " @@ -750,67 +666,11 @@ for (funcs_it_t cur = funcs.begin() ; cur != funcs.end() ; ++ cur ) { - hname_t use_name( (*cur).first ); - // A function can not have the same name as another scope object. - const NetScope *child = scope->child(use_name); - if (child) { - cerr << cur->second->get_fileline() - << ": error: function and "; - child->print_type(cerr); - cerr << " in '" << scope->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - continue; - } - - // A function can not have the same name as a genvar. - if (scope->find_genvar((*cur).first)) { - cerr << cur->second->get_fileline() - << ": error: function and genvar in '" - << scope->fullname() << "' have the same name '" - << (*cur).first << "'." << endl; - des->errors += 1; - } - - // A function can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = scope->get_parameter(des, (*cur).first, - ex_msb, ex_lsb); - if (parm) { - cerr << cur->second->get_fileline() - << ": error: function and parameter in '" - << scope->fullname() << "' have the same name '" - << (*cur).first << "'." << endl; - des->errors += 1; - } - elaborate_scope_func(des, scope, cur->second); } } -void elaborate_rootscope_tasks(Design*des) -{ - for (map::iterator cur = pform_tasks.begin() - ; cur != pform_tasks.end() ; ++ cur) { - - if (PTask*task = dynamic_cast (cur->second)) { - elaborate_scope_task(des, 0, task); - continue; - } - - if (PFunction*func = dynamic_cast(cur->second)) { - elaborate_scope_func(des, 0, func); - continue; - } - - cerr << cur->second->get_fileline() << ": internal error: " - << "elaborate_rootscope_tasks does not understand " - << "this object," << endl; - des->errors += 1; - } -} - class generate_schemes_work_item_t : public elaborator_work_item_t { public: generate_schemes_work_item_t(Design*des__, NetScope*scope, Module*mod) @@ -850,12 +710,23 @@ << "Elaborate package " << scope_path(scope) << "." << endl; } + scope->add_typedefs(&typedefs); + collect_scope_parameters_(des, scope, parameters); collect_scope_localparams_(des, scope, localparams); + + if (debug_scopes) { + cerr << get_fileline() << ": PPackage::elaborate_scope: " + << "Elaborate " << enum_sets.size() << " enumerations" + << " in package scope " << scope_path(scope) << "." + << endl; + } elaborate_scope_enumerations(des, scope, enum_sets); + elaborate_scope_classes(des, scope, classes_lexical); elaborate_scope_funcs(des, scope, funcs); elaborate_scope_tasks(des, scope, tasks); + elaborate_scope_events_(des, scope, events); return true; } @@ -867,6 +738,8 @@ << "Elaborate " << scope_path(scope) << "." << endl; } + scope->add_typedefs(&typedefs); + // Add the genvars to the scope. typedef map::const_iterator genvar_it_t; for (genvar_it_t cur = genvars.begin(); cur != genvars.end(); ++ cur ) { @@ -889,6 +762,12 @@ replace_scope_parameters_(scope, *this, replacements); + if (debug_scopes) { + cerr << get_fileline() << ": Module::elaborate_scope: " + << "Elaborate " << enum_sets.size() << " enumerations" + << " in scope " << scope_path(scope) << "." + << endl; + } elaborate_scope_enumerations(des, scope, enum_sets); assert(classes.size() == classes_lexical.size()); @@ -1020,17 +899,19 @@ */ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) { - // Check that the loop_index variable was declared in a - // genvar statement. - NetScope*cscope = container; - while (cscope && !cscope->find_genvar(loop_index)) - cscope = cscope->parent(); - if (!cscope) { - cerr << get_fileline() << ": error: genvar is missing for " - "generate \"loop\" variable '" << loop_index << "'." - << endl; - des->errors += 1; - return false; + if (!local_index) { + // Check that the loop_index variable was declared in a + // genvar statement. + NetScope*cscope = container; + while (cscope && !cscope->find_genvar(loop_index)) + cscope = cscope->parent(); + if (!cscope) { + cerr << get_fileline() << ": error: genvar is missing for " + "generate \"loop\" variable '" << loop_index << "'." + << endl; + des->errors += 1; + return false; + } } // We're going to need a genvar... @@ -1048,56 +929,6 @@ return false; } - // Check the generate block name. - - // A generate "loop" can not have the same name as another - // scope object. Find any scope with this name, not just an - // exact match scope. - const NetScope *child = container->child_byname(scope_name); - if (child) { - cerr << get_fileline() << ": error: generate \"loop\" and "; - child->print_type(cerr); - cerr << " in '" << container->fullname() - << "' have the same name '" << scope_name << "'." << endl; - des->errors += 1; - return false; - } - - // A generate "loop" can not have the same name as a genvar. - if (container->find_genvar(scope_name)) { - cerr << get_fileline() << ": error: generate \"loop\" and " - "genvar in '" << container->fullname() - << "' have the same name '" << scope_name << "'." << endl; - des->errors += 1; - } - - // A generate "loop" can not have the same name as a named event. - const NetEvent *event = container->find_event(scope_name); - if (event) { - cerr << get_fileline() << ": error: generate \"loop\" and " - "named event in '" << container->fullname() - << "' have the same name '" << scope_name << "'." << endl; - des->errors += 1; - } - - // A generate "loop" can not have the same name as a parameter. - const NetExpr*tmsb; - const NetExpr*tlsb; - const NetExpr*texpr = container->get_parameter(des, scope_name, - tmsb, tlsb); - if (texpr != 0) { - cerr << get_fileline() << ": error: generate \"loop\" and " - "parameter in '" << container->fullname() - << "' have the same name '" << scope_name << "'." << endl; - des->errors += 1; - } - - // These have all been checked so we just need to skip the actual - // generation for these name conflicts. Not skipping these two will - // cause the compiler to have problems (assert, inf. loop, etc.). - if (container->get_parameter(des, loop_index, tmsb, tlsb)) return false; - if (container->find_event(loop_index)) return false; - genvar = init->value().as_long(); delete init_ex; @@ -1128,6 +959,7 @@ NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); + scope->add_imports(&explicit_imports); // Set in the scope a localparam for the value of the // genvar within this instance of the generate @@ -1167,7 +999,7 @@ container->genvar_tmp_val = genvar; delete step; delete test_ex; - test_ex = elab_and_eval(des, container, loop_test, -1); + test_ex = elab_and_eval(des, container, loop_test, -1, true); test = dynamic_cast(test_ex); assert(test); } @@ -1204,45 +1036,6 @@ } hname_t use_name (scope_name); - // A generate "if" can not have the same name as another scope object. - const NetScope *child = container->child(use_name); - if (child) { - cerr << get_fileline() << ": error: generate \"if\" and "; - child->print_type(cerr); - cerr << " in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - return false; - } - - // A generate "if" can not have the same name as a genvar. - if (container->find_genvar(scope_name)) { - cerr << get_fileline() << ": error: generate \"if\" and " - "genvar in '" << container->fullname() - << "' have the same name '" << scope_name << "'." << endl; - des->errors += 1; - } - - // A generate "if" can not have the same name as a named event. - const NetEvent *event = container->find_event(scope_name); - if (event) { - cerr << get_fileline() << ": error: generate \"if\" and " - "named event in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - - // A generate "if" can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = container->get_parameter(des, scope_name, - ex_msb, ex_lsb); - if (parm) { - cerr << get_fileline() << ": error: generate \"if\" and " - "parameter in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - if (debug_scopes) cerr << get_fileline() << ": debug: Generate condition " << (else_flag? "(else)" : "(if)") @@ -1263,6 +1056,7 @@ // for myself. That is what I will pass to the subscope. NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); + scope->add_imports(&explicit_imports); elaborate_subscope_(des, scope); @@ -1346,44 +1140,6 @@ // The name of the scope to generate, whatever that item is. hname_t use_name (item->scope_name); - // A generate "case" can not have the same name as another scope object. - const NetScope *child = container->child(use_name); - if (child) { - cerr << get_fileline() << ": error: generate \"case\" and "; - child->print_type(cerr); - cerr << " in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - return false; - } - - // A generate "case" can not have the same name as a genvar. - if (container->find_genvar(item->scope_name)) { - cerr << get_fileline() << ": error: generate \"case\" and " - "genvar in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - - // A generate "case" can not have the same name as a named event. - const NetEvent *event = container->find_event(item->scope_name); - if (event) { - cerr << get_fileline() << ": error: generate \"case\" and " - "named event in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - - // A generate "case" can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = container->get_parameter(des, item->scope_name, - ex_msb, ex_lsb); - if (parm) { - cerr << get_fileline() << ": error: generate \"case\" and " - "parameter in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } item->probe_for_direct_nesting_(); if (item->direct_nested_) { @@ -1403,6 +1159,8 @@ NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); + scope->add_imports(&explicit_imports); + item->elaborate_subscope_(des, scope); return true; @@ -1411,46 +1169,6 @@ bool PGenerate::generate_scope_nblock_(Design*des, NetScope*container) { hname_t use_name (scope_name); - // A generate "block" can not have the same name as another scope - // object. - const NetScope *child = container->child(use_name); - if (child) { - cerr << get_fileline() << ": error: generate \"block\" and "; - child->print_type(cerr); - cerr << " in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - return false; - } - - // A generate "block" can not have the same name as a genvar. - if (container->find_genvar(scope_name)) { - cerr << get_fileline() << ": error: generate \"block\" and " - "genvar in '" << container->fullname() - << "' have the same name '" << scope_name << "'." << endl; - des->errors += 1; - } - - // A generate "block" can not have the same name as a named event. - const NetEvent *event = container->find_event(scope_name); - if (event) { - cerr << get_fileline() << ": error: generate \"block\" and " - "named event in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - - // A generate "block" can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = container->get_parameter(des, scope_name, - ex_msb, ex_lsb); - if (parm) { - cerr << get_fileline() << ": error: generate \"block\" and " - "parameter in '" << container->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - if (debug_scopes) cerr << get_fileline() << ": debug: Generate named block " << ": Generate scope=" << use_name << endl; @@ -1458,6 +1176,7 @@ NetScope*scope = new NetScope(container, use_name, NetScope::GENBLOCK); scope->set_line(get_file(), get_lineno()); + scope->add_imports(&explicit_imports); elaborate_subscope_(des, scope); @@ -1481,6 +1200,8 @@ void PGenerate::elaborate_subscope_(Design*des, NetScope*scope) { + scope->add_typedefs(&typedefs); + // Add the genvars to this scope. typedef map::const_iterator genvar_it_t; for (genvar_it_t cur = genvars.begin(); cur != genvars.end(); ++ cur ) { @@ -1589,36 +1310,6 @@ // Missing module instance names have already been rejected. assert(get_name() != ""); - // A module instance can not have the same name as another scope object. - const NetScope *child = sc->child(hname_t(get_name())); - if (child) { - cerr << get_fileline() << ": error: module <" << mod->mod_name() - << "> instance and "; - child->print_type(cerr); - cerr << " in '" << sc->fullname() - << "' have the same name '" << get_name() << "'." << endl; - des->errors += 1; - return; - } - - // A module instance can not have the same name as a genvar. - if (sc->find_genvar(get_name())) { - cerr << get_fileline() << ": error: module <" << mod->mod_name() - << "> instance and genvar in '" << sc->fullname() - << "' have the same name '" << get_name() << "'." << endl; - des->errors += 1; - } - - // A module instance can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = sc->get_parameter(des, get_name(), ex_msb, ex_lsb); - if (parm) { - cerr << get_fileline() << ": error: module <" << mod->mod_name() - << "> instance and parameter in '" << sc->fullname() - << "' have the same name '" << get_name() << "'." << endl; - des->errors += 1; - } - // check for recursive instantiation by scanning the current // scope and its parents. Look for a module instantiation of // the same module, but farther up in the scope. @@ -1726,6 +1417,10 @@ << "." << endl; } + struct attrib_list_t*attrib_list; + unsigned attrib_list_n = 0; + attrib_list = evaluate_attributes(attributes, attrib_list_n, des, sc); + // Run through the module instances, and make scopes out of // them. Also do parameter overrides that are done on the // instantiation line. @@ -1752,13 +1447,17 @@ // Create the new scope as a MODULE with my name. Note // that if this is a nested module, mark it thus so that // scope searches will continue into the parent scope. - NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE, + NetScope*my_scope = new NetScope(sc, use_name, NetScope::MODULE, 0, bound_type_? true : false, mod->program_block, mod->is_interface); my_scope->set_line(get_file(), mod->get_file(), get_lineno(), mod->get_lineno()); my_scope->set_module_name(mod->mod_name()); + my_scope->add_imports(&mod->explicit_imports); + + for (unsigned adx = 0 ; adx < attrib_list_n ; adx += 1) + my_scope->attribute(attrib_list[adx].key, attrib_list[adx].val); instances[idx] = my_scope; @@ -1818,6 +1517,7 @@ mod->elaborate_scope(des, my_scope, replace); } + delete[]attrib_list; /* Stash the instance array of scopes into the parent scope. Later elaboration passes will use this vector to @@ -1836,36 +1536,8 @@ * no hierarchy, but neither does the NetEvent, until it is stored in * the NetScope object. */ -void PEvent::elaborate_scope(Design*des, NetScope*scope) const +void PEvent::elaborate_scope(Design*, NetScope*scope) const { - // A named event can not have the same name as another scope object. - const NetScope *child = scope->child(hname_t(name_)); - if (child) { - cerr << get_fileline() << ": error: named event and "; - child->print_type(cerr); - cerr << " in '" << scope->fullname() - << "' have the same name '" << name_ << "'." << endl; - des->errors += 1; - } - - // A named event can not have the same name as a genvar. - if (scope->find_genvar(name_)) { - cerr << get_fileline() << ": error: named event and " - << "genvar in '" << scope->fullname() - << "' have the same name '" << name_ << "'." << endl; - des->errors += 1; - } - - // A named event can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = scope->get_parameter(des, name_, ex_msb, ex_lsb); - if (parm) { - cerr << get_fileline() << ": error: named event and " - << "parameter in '" << scope->fullname() - << "' have the same name '" << name_ << "'." << endl; - des->errors += 1; - } - NetEvent*ev = new NetEvent(name_); ev->set_line(*this); scope->add_event(ev); @@ -1883,6 +1555,8 @@ // find otherwise. scope->is_const_func(true); + scope->add_typedefs(&typedefs); + // Scan the parameters in the function, and store the information // needed to evaluate the parameter expressions. @@ -1901,6 +1575,8 @@ { assert(scope->type() == NetScope::TASK); + scope->add_typedefs(&typedefs); + // Scan the parameters in the task, and store the information // needed to evaluate the parameter expressions. @@ -1936,37 +1612,6 @@ if (pscope_name() != 0) { hname_t use_name(pscope_name()); - // A named block can not have the same name as another scope - // object. - const NetScope *child = scope->child(use_name); - if (child) { - cerr << get_fileline() << ": error: named block and "; - child->print_type(cerr); - cerr << " in '" << scope->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - return; - } - - // A named block can not have the same name as a genvar. - if (scope->find_genvar(pscope_name())) { - cerr << get_fileline() << ": error: named block and " - "genvar in '" << scope->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - - // A named block can not have the same name as a parameter. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = scope->get_parameter(des, pscope_name(), - ex_msb, ex_lsb); - if (parm) { - cerr << get_fileline() << ": error: named block and " - "parameter in '" << scope->fullname() - << "' have the same name '" << use_name << "'." << endl; - des->errors += 1; - } - if (debug_scopes) cerr << get_fileline() << ": debug: " << "Elaborate block scope " << use_name @@ -1979,6 +1624,8 @@ : NetScope::BEGIN_END); my_scope->set_line(get_file(), get_lineno()); my_scope->is_auto(scope->is_auto()); + my_scope->add_imports(&explicit_imports); + my_scope->add_typedefs(&typedefs); // Scan the parameters in the scope, and store the information // needed to evaluate the parameter expressions. diff -Nru iverilog-10.3/elab_sig.cc iverilog-11.0/elab_sig.cc --- iverilog-10.3/elab_sig.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/elab_sig.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -47,6 +47,8 @@ using namespace std; +#if 0 +/* These functions are not currently used. */ static bool get_const_argument(NetExpr*exp, verinum&res) { switch (exp->expr_type()) { @@ -73,8 +75,6 @@ return true; } -#if 0 -/* This function is not currently used. */ static bool get_const_argument(NetExpr*exp, long&res) { verinum tmp; @@ -876,13 +876,13 @@ return 0; } -static netparray_t* elaborate_parray_type(Design*des, NetScope*scope, +static netparray_t* elaborate_parray_type(Design*des, NetScope*scope, const LineInfo*li, parray_type_t*data_type) { vectorpacked_dimensions; - bool bad_range = evaluate_ranges(des, scope, packed_dimensions, * data_type->dims); - ivl_assert(*data_type, !bad_range); + bool dimensions_ok = evaluate_ranges(des, scope, li, packed_dimensions, * data_type->dims); + ivl_assert(*data_type, dimensions_ok); ivl_type_s*element_type = elaborate_type(des, scope, data_type->base_type); @@ -932,53 +932,34 @@ wtype = NetNet::WIRE; is_implicit_scalar = true; } + // Certain contexts, such as arguments to functions, presume + // "reg" instead of "wire". The parser reports these as + // IMPLICIT_REG. Also, certain cases, such as: + // fun(string arg1) ... + // are implicitly NOT scalar, so detect that case here. if (wtype == NetNet::IMPLICIT_REG) { wtype = NetNet::REG; - is_implicit_scalar = true; + if (data_type_!=IVL_VT_STRING) + is_implicit_scalar = true; + } + + if (debug_elaborate) { + cerr << get_fileline() << ": PWire::elaborate_sig: " + << "Signal " << basename() + << ", wtype=" << wtype + << ", data_type_=" << data_type_ + << ", is_implicit_scalar=" << (is_implicit_scalar?"true":"false") + << endl; } unsigned wid = 1; vectorpacked_dimensions; - des->errors += error_cnt_; + NetScope*base_type_scope = scope; + if (set_data_type_ && !set_data_type_->name.nil()) + base_type_scope = scope->find_typedef_scope(des, set_data_type_); - // A signal can not have the same name as a scope object. - const NetScope *child = scope->child_byname(name_); - if (child) { - cerr << get_fileline() << ": error: signal and "; - child->print_type(cerr); - cerr << " in '" << scope->fullname() - << "' have the same name '" << name_ << "'." << endl; - des->errors += 1; - } - // A signal can not have the same name as a genvar. - const LineInfo *genvar = scope->find_genvar(name_); - if (genvar) { - cerr << get_fileline() << ": error: signal and genvar in '" - << scope->fullname() << "' have the same name '" << name_ - << "'." << endl; - des->errors += 1; - } - // A signal can not have the same name as a parameter. Note - // that we treat enumeration literals similar to parameters, - // so if the name matches an enumeration literal, it will be - // caught here. - const NetExpr *ex_msb, *ex_lsb; - const NetExpr *parm = scope->get_parameter(des, name_, ex_msb, ex_lsb); - if (parm) { - cerr << get_fileline() << ": error: signal and parameter in '" - << scope->fullname() << "' have the same name '" << name_ - << "'." << endl; - des->errors += 1; - } - // A signal can not have the same name as a named event. - const NetEvent *event = scope->find_event(name_); - if (event) { - cerr << get_fileline() << ": error: signal and named event in '" - << scope->fullname() << "' have the same name '" << name_ - << "'." << endl; - des->errors += 1; - } + des->errors += error_cnt_; if (port_set_ || net_set_) { @@ -998,7 +979,7 @@ << " inherits dimensions from var/net." << endl; } - bool bad_range = false; + bool dimensions_ok = true; vector plist, nlist; /* If they exist get the port definition MSB and LSB */ if (port_set_ && !port_.empty()) { @@ -1006,7 +987,7 @@ cerr << get_fileline() << ": PWire::elaborate_sig: " << "Evaluate ranges for port " << basename() << endl; } - bad_range |= evaluate_ranges(des, scope, plist, port_); + dimensions_ok &= evaluate_ranges(des, scope, this, plist, port_); nlist = plist; /* An implicit port can have a range so note that here. */ is_implicit_scalar = false; @@ -1014,13 +995,13 @@ assert(port_set_ || port_.empty()); /* If they exist get the net/etc. definition MSB and LSB */ - if (net_set_ && !net_.empty() && !bad_range) { + if (net_set_ && !net_.empty() && dimensions_ok) { nlist.clear(); if (debug_elaborate) { cerr << get_fileline() << ": PWire::elaborate_sig: " << "Evaluate ranges for net " << basename() << endl; } - bad_range |= evaluate_ranges(des, scope, nlist, net_); + dimensions_ok &= evaluate_ranges(des, base_type_scope, this, nlist, net_); } assert(net_set_ || net_.empty()); @@ -1091,6 +1072,10 @@ listunpacked_dimensions; netdarray_t*netdarray = 0; + NetScope*array_type_scope = scope; + if (uarray_type_ && !uarray_type_->name.nil()) + array_type_scope = scope->find_typedef_scope(des, uarray_type_); + for (list::const_iterator cur = unpacked_.begin() ; cur != unpacked_.end() ; ++cur) { PExpr*use_lidx = cur->first; @@ -1109,51 +1094,51 @@ } // Special case: Detect the mark for a QUEUE - // declaration, which is the dimensions [null:]. - if (use_ridx==0 && dynamic_cast(use_lidx)) { + // declaration, which is the dimensions [null:max_idx]. + if (dynamic_cast(use_lidx)) { netvector_t*vec = new netvector_t(packed_dimensions, data_type_); vec->set_signed(get_signed()); packed_dimensions.clear(); ivl_assert(*this, netdarray==0); - netdarray = new netqueue_t(vec); + long max_idx; + if (use_ridx) { + NetExpr*tmp = elab_and_eval(des, array_type_scope, use_ridx, + -1, true); + NetEConst*cv = dynamic_cast(tmp); + if (cv == 0) { + cerr << get_fileline() << ": error: queue '" << name_ + << "' bound must be a constant!" << endl; + des->errors += 1; + max_idx = -1; + } else { + verinum res = cv->value(); + if (res.is_defined()) { + max_idx = res.as_long(); + if (max_idx < 0) { + cerr << get_fileline() << ": error: queue '" + << name_ << "' bound must be positive (" + << max_idx << ")!" << endl; + des->errors += 1; + max_idx = -1; + } + } else { + cerr << get_fileline() << ": error: queue '" << name_ + << "' bound is undefined!" << endl; + des->errors += 1; + max_idx = -1; + } + } + } else max_idx = -1; + netdarray = new netqueue_t(vec, max_idx); continue; } // Cannot handle dynamic arrays of arrays yet. ivl_assert(*this, netdarray==0); - ivl_assert(*this, use_lidx && use_ridx); - - NetExpr*lexp = elab_and_eval(des, scope, use_lidx, -1, true); - NetExpr*rexp = elab_and_eval(des, scope, use_ridx, -1, true); - - if ((lexp == 0) || (rexp == 0)) { - cerr << get_fileline() << ": internal error: There is " - << "a problem evaluating indices for ``" - << name_ << "''." << endl; - des->errors += 1; - return 0; - } - - bool const_flag = true; - verinum lval, rval; - const_flag &= get_const_argument(lexp, lval); - const_flag &= get_const_argument(rexp, rval); - delete rexp; - delete lexp; long index_l, index_r; - if (! const_flag) { - cerr << get_fileline() << ": error: The indices " - << "are not constant for array ``" - << name_ << "''." << endl; - des->errors += 1; - /* Attempt to recover from error, */ - index_l = 0; - index_r = 0; - } else { - index_l = lval.as_long(); - index_r = rval.as_long(); - } + evaluate_range(des, array_type_scope, this, *cur, index_l, index_r); + if (abs(index_r - index_l) > warn_dimension_size) { cerr << get_fileline() << ": warning: Array dimension " "is greater than " << warn_dimension_size @@ -1243,7 +1228,7 @@ } else if (enum_type_t*enum_type = dynamic_cast(set_data_type_)) { list::const_iterator sample_name = enum_type->names->begin(); - const netenum_t*use_enum = scope->find_enumeration_for_name(sample_name->name); + const netenum_t*use_enum = base_type_scope->find_enumeration_for_name(des, sample_name->name); if (debug_elaborate) { cerr << get_fileline() << ": debug: Create signal " << wtype @@ -1276,7 +1261,7 @@ // The trick here is that the parray type has an // arbitrary sub-type, and not just a scalar bit... - netparray_t*use_type = elaborate_parray_type(des, scope, parray_type); + netparray_t*use_type = elaborate_parray_type(des, scope, this, parray_type); // Should not be getting packed dimensions other than // through the parray type declaration. ivl_assert(*this, packed_dimensions.empty()); @@ -1338,27 +1323,3 @@ return sig; } - - -void Design::root_elaborate_sig(void) -{ - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++ cur) { - - netclass_t*cur_class = cur->second; - PClass*cur_pclass = class_to_pclass_[cur_class]; - - cur_class->elaborate_sig(this, cur_pclass); - } - - for (map::iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { - - if (debug_elaborate) { - cerr << cur->second->get_fileline() << ": root_elaborate_sig: " - << "Elaborate_sig for root task/func " << scope_path(cur->first) << endl; - } - - cur->second->elaborate_sig(this, cur->first); - } -} diff -Nru iverilog-10.3/elab_type.cc iverilog-11.0/elab_type.cc --- iverilog-10.3/elab_type.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/elab_type.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2020 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 @@ -78,9 +78,13 @@ */ ivl_type_s* data_type_t::elaborate_type(Design*des, NetScope*scope) { + // User-defined types must be elaborated in the context + // where they were defined. + if (!name.nil()) + scope = scope->find_typedef_scope(des, this); + + ivl_assert(*this, scope); Definitions*use_definitions = scope; - if (use_definitions == 0) - use_definitions = des; map::iterator pos = cache_type_elaborate_.lower_bound(use_definitions); if (pos != cache_type_elaborate_.end() && pos->first == use_definitions) @@ -147,13 +151,13 @@ * available at the right time. At that time, the netenum_t* object is * stashed in the scope so that I can retrieve it here. */ -ivl_type_s* enum_type_t::elaborate_type_raw(Design*des, NetScope*scope) const +ivl_type_s* enum_type_t::elaborate_type_raw(Design*, NetScope*scope) const { ivl_assert(*this, scope); ivl_type_s*tmp = scope->enumeration_for_key(this); - if (tmp) return tmp; - - tmp = des->enumeration_for_key(this); + if (tmp == 0 && scope->unit()) { + tmp = scope->unit()->enumeration_for_key(this); +} return tmp; } @@ -216,9 +220,9 @@ // There may be several names that are the same type: // name1, name2, ...; // Process all the member, and give them a type. - for (list::iterator name = curp->names->begin() - ; name != curp->names->end() ; ++ name) { - decl_assignment_t*namep = *name; + for (list::iterator cur_name = curp->names->begin() + ; cur_name != curp->names->end() ; ++ cur_name) { + decl_assignment_t*namep = *cur_name; netstruct_t::member_t memb; memb.name = namep->name; @@ -248,19 +252,20 @@ } // Special case: if the dimension is null:nil. this is a queue. - if (cur->second==0 && dynamic_cast(cur->first)) { + if (dynamic_cast(cur->first)) { cerr << get_fileline() << ": sorry: " << "SV queues inside classes are not yet supported." << endl; des->errors += 1; - ivl_type_s*res = new netqueue_t(btype); + // FIXME: Need to set the max size if cur->second is defined + ivl_type_s*res = new netqueue_t(btype, -1); return res; } vector dimensions; - bool bad_range = evaluate_ranges(des, scope, dimensions, *dims); + bool dimensions_ok = evaluate_ranges(des, scope, this, dimensions, *dims); - if (bad_range) { + if (!dimensions_ok) { cerr << get_fileline() << " : warning: " << "Bad dimensions for type here." << endl; } diff -Nru iverilog-10.3/emit.cc iverilog-11.0/emit.cc --- iverilog-10.3/emit.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/emit.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2017 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 @@ -123,6 +123,12 @@ return true; } +bool NetLatch::emit_node(struct target_t*tgt) const +{ + tgt->lpm_latch(this); + return true; +} + bool NetLiteral::emit_node(struct target_t*tgt) const { return tgt->net_literal(this); @@ -504,24 +510,12 @@ if (tgt->start_design(this) == false) return -2; - for (map::const_iterator scope = root_tasks_.begin() - ; scope != root_tasks_.end() ; ++ scope) { - scope->first->emit_scope(tgt); - } - // enumerate package scopes for (map::const_iterator scope = packages_.begin() ; scope != packages_.end() ; ++ scope) { scope->second->emit_scope(tgt); } - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++cur) { - const NetScope*use_scope = cur->second->class_scope(); - cur->second->emit_scope(tgt); - tgt->class_type(use_scope, cur->second); - } - // enumerate root scopes for (list::const_iterator scope = root_scopes_.begin() ; scope != root_scopes_.end(); ++ scope ) { @@ -546,12 +540,6 @@ // emit task and function definitions bool tasks_rc = true; - for (map::const_iterator scope = root_tasks_.begin() - ; scope != root_tasks_.end() ; ++ scope) - tasks_rc &= scope->first->emit_defs(tgt); - for (map::const_iterator cur = classes_.begin() - ; cur != classes_.end() ; ++cur) - tasks_rc &= cur->second->emit_defs(tgt); for (map::const_iterator scope = packages_.begin() ; scope != packages_.end() ; ++ scope ) tasks_rc &= scope->second->emit_defs(tgt); diff -Nru iverilog-10.3/eval_tree.cc iverilog-11.0/eval_tree.cc --- iverilog-10.3/eval_tree.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/eval_tree.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2020 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 @@ -545,7 +545,7 @@ bool flag = get_real_arguments(le, re, lval, rval); if (! flag) return 0; - verinum result(((lval == rval) ^ ne_flag) ? + verinum result(((lval == rval) != ne_flag) ? verinum::V1 : verinum::V0, 1); NetEConst*res = new NetEConst(result); ivl_assert(*this, res); @@ -570,11 +570,11 @@ const verinum::V ne_res = ne_flag? verinum::V1 : verinum::V0; verinum::V res = eq_res; - unsigned top = lv.len(); - if (rv.len() < top) - top = rv.len(); - for (unsigned idx = 0 ; idx < top ; idx += 1) { + // The two expressions should already be padded to the same size. + ivl_assert(*this, lv.len() == rv.len()); + + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) { bool x_bit_present = false; @@ -611,58 +611,34 @@ } } - if (res != verinum::Vx) { - verinum::V lpad = verinum::V0; - verinum::V rpad = verinum::V0; - - if (lv.has_sign() && lv.get(lv.len()-1) == verinum::V1) - lpad = verinum::V1; - if (rv.has_sign() && rv.get(rv.len()-1) == verinum::V1) - rpad = verinum::V1; - - for (unsigned idx = top ; idx < lv.len() ; idx += 1) - switch (lv.get(idx)) { - - case verinum::Vx: - case verinum::Vz: - res = verinum::Vx; - break; - - case verinum::V0: - if (res != verinum::Vx && rpad != verinum::V0) - res = ne_res; - break; - - case verinum::V1: - if (res != verinum::Vx && rpad != verinum::V1) - res = ne_res; - break; + NetEConst*result = new NetEConst(verinum(res, 1)); + ivl_assert(*this, result); + return result; +} - default: - break; - } +NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const +{ + const NetEConst*lc = dynamic_cast(le); + const NetEConst*rc = dynamic_cast(re); + if (lc == 0 || rc == 0) return 0; - for (unsigned idx = top ; idx < rv.len() ; idx += 1) - switch (rv.get(idx)) { + const verinum&lv = lc->value(); + const verinum&rv = rc->value(); - case verinum::Vx: - case verinum::Vz: - res = verinum::Vx; - break; + verinum::V res = verinum::V1; - case verinum::V0: - if (res != verinum::Vx && lpad != verinum::V0) - res = ne_res; - break; + // The two expressions should already be padded to the same size. + ivl_assert(*this, lv.len() == rv.len()); - case verinum::V1: - if (res != verinum::Vx && lpad != verinum::V1) - res = ne_res; - break; + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) + if (lv.get(idx) != rv.get(idx)) { + res = verinum::V0; + break; + } - default: - break; - } + if (ne_flag) { + if (res == verinum::V0) res = verinum::V1; + else res = verinum::V0; } NetEConst*result = new NetEConst(verinum(res, 1)); @@ -670,7 +646,7 @@ return result; } -NetEConst* NetEBComp::eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const +NetEConst* NetEBComp::eval_weqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const { const NetEConst*lc = dynamic_cast(le); const NetEConst*rc = dynamic_cast(re); @@ -679,51 +655,39 @@ const verinum&lv = lc->value(); const verinum&rv = rc->value(); - verinum::V res = verinum::V1; + const verinum::V eq_res = ne_flag ? verinum::V0 : verinum::V1; + const verinum::V ne_res = ne_flag ? verinum::V1 : verinum::V0; - // Find the smallest argument length. - unsigned cnt = lv.len(); - if (cnt > rv.len()) cnt = rv.len(); + verinum::V res = eq_res; - // Check the common bits. - for (unsigned idx = 0 ; idx < cnt ; idx += 1) - if (lv.get(idx) != rv.get(idx)) { - res = verinum::V0; + // The two expressions should already be padded to the same size. + ivl_assert(*this, lv.len() == rv.len()); + + for (unsigned idx = 0 ; idx < lv.len() ; idx += 1) { + // An X or Z in the R-value matches any L-value. + switch (rv.get(idx)) { + case verinum::Vx: + case verinum::Vz: + continue; + default: break; } - bool is_signed = lv.has_sign() && rv.has_sign(); - - // If the left value is longer check it against the pad bit. - if (res == verinum::V1) { - verinum::V pad = verinum::V0; - if (is_signed) - pad = rv.get(rv.len()-1); - - for (unsigned idx = cnt ; idx < lv.len() ; idx += 1) - if (lv.get(idx) != pad) { - res = verinum::V0; - break; - } - } - - // If the right value is longer check it against the pad bit. - if (res == verinum::V1) { - verinum::V pad = verinum::V0; - if (is_signed) - pad = lv.get(lv.len()-1); - - for (unsigned idx = cnt ; idx < rv.len() ; idx += 1) { - if (rv.get(idx) != pad) { - res = verinum::V0; - break; - } + // An X or Z in the L-value that is not matches by an R-value X/Z returns undefined. + switch (lv.get(idx)) { + case verinum::Vx: + case verinum::Vz: + res = verinum::Vx; + continue; + default: + break; } - } - if (ne_flag) { - if (res == verinum::V0) res = verinum::V1; - else res = verinum::V0; + // A hard (0/1) mismatch gives a not-equal result. + if (rv.get(idx) != lv.get(idx)) { + res = ne_res; + break; + } } NetEConst*result = new NetEConst(verinum(res, 1)); @@ -744,6 +708,10 @@ res = eval_eqeq_(false, l, r); break; + case 'w': // Wild equality (==?) + res = eval_weqeq_(false, l, r); + break; + case 'G': // >= res = eval_gteq_(l, r); break; @@ -760,6 +728,10 @@ res = eval_eqeq_(true, l, r); break; + case 'W': // Wild not-equal (!=?) + res = eval_weqeq_(true, l, r); + break; + case '<': // Less than res = eval_less_(l, r); break; @@ -836,44 +808,10 @@ return tmp; } -NetEConst* NetEBLogic::eval_tree_real_(const NetExpr*l, const NetExpr*r) const -{ - double lval; - double rval; - - bool flag = get_real_arguments(l, r, lval, rval); - if (! flag) return 0; - - verinum::V res; - switch (op_) { - case 'a': // Logical AND (&&) - if ((lval != 0.0) && (rval != 0.0)) - res = verinum::V1; - else - res = verinum::V0; - break; - - case 'o': // Logical OR (||) - if ((lval != 0.0) || (rval != 0.0)) - res = verinum::V1; - else - res = verinum::V0; - break; - - default: - return 0; - } - - NetEConst*tmp = new NetEConst(verinum(res, 1)); - ivl_assert(*this, tmp); - eval_debug(this, tmp, true); - return tmp; -} - NetEConst* NetEBLogic::eval_arguments_(const NetExpr*l, const NetExpr*r) const { - if (l->expr_type() == IVL_VT_REAL || r->expr_type() == IVL_VT_REAL) - return eval_tree_real_(l,r); + // NetEBLogic arguments should have already been reduced so real is not possible. + ivl_assert(*this, (l->expr_type() != IVL_VT_REAL) && (r->expr_type() != IVL_VT_REAL)); assert(expr_type() == IVL_VT_LOGIC); const NetEConst*lc = dynamic_cast(l); @@ -906,25 +844,39 @@ case 'a': // Logical AND (&&) if ((lv == verinum::V0) || (rv == verinum::V0)) res = verinum::V0; - else if ((lv == verinum::V1) && (rv == verinum::V1)) res = verinum::V1; - else res = verinum::Vx; - break; case 'o': // Logical OR (||) if ((lv == verinum::V1) || (rv == verinum::V1)) res = verinum::V1; - else if ((lv == verinum::V0) && (rv == verinum::V0)) res = verinum::V0; + else + res = verinum::Vx; + break; + case 'q': // Logical implication (->) + if ((lv == verinum::V0) || (rv == verinum::V1)) + res = verinum::V1; + else if ((lv == verinum::V1) && (rv == verinum::V0)) + res = verinum::V0; else res = verinum::Vx; + break; + case 'Q': // Logical equivalence (<->) + if (((lv == verinum::V0) && (rv == verinum::V0)) || + ((lv == verinum::V1) && (rv == verinum::V1))) + res = verinum::V1; + else if (((lv == verinum::V0) && (rv == verinum::V1)) || + ((lv == verinum::V1) && (rv == verinum::V0))) + res = verinum::V0; + else + res = verinum::Vx; break; default: @@ -1941,15 +1893,111 @@ return res; } -NetEConst* NetESFunc::evaluate_countbits_(const NetExpr* /*arg0*/, - const NetExpr* /*arg1*/) const +static void no_string_arg(const NetESFunc*info, unsigned arg_num) { - return 0; + cerr << info->get_fileline() << ": error: constant function " + << info->name() << "() does not support a string argument (" + << arg_num+1 << ")." << endl; } -NetEConst* NetESFunc::evaluate_countones_(const NetExpr* /*arg*/) const +NetEConst* NetESFunc::evaluate_countbits_() const { - return 0; + const NetEConst*tmpi = dynamic_cast(parms_[0]); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + /* Find which values need to be counted. */ + bool count_0 = false; + bool count_1 = false; + bool count_z = false; + bool count_x = false; + for (unsigned arg=1; arg < parms_.size(); ++arg) { + const NetEConst*argi = dynamic_cast(parms_[arg]); + if (! argi) return 0; + verinum check_for = argi->value(); + if (check_for.is_string()) { + no_string_arg(this, arg); + return 0; + } + switch (check_for[0]) { + case verinum::V0: + count_0 = true; + break; + case verinum::V1: + count_1 = true; + break; + case verinum::Vz: + count_z = true; + break; + case verinum::Vx: + count_x = true; + break; + } + } + + /* Search each bit of the vector looking for the values to + * be counted. */ + int count = 0; + for (unsigned bit=0; bit < value.len(); ++bit) { + switch (value[bit]) { + case verinum::V0: + if (count_0) ++count; + break; + case verinum::V1: + if (count_1) ++count; + break; + case verinum::Vz: + if (count_z) ++count; + break; + case verinum::Vx: + if (count_x) ++count; + break; + } + } + + verinum tmp (count, integer_width); + tmp.has_sign(true); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; +} + +NetEConst* NetESFunc::evaluate_countones_(const NetExpr* arg) const +{ + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + int count = 0; + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + for (unsigned bit=0; bit < value.len(); ++bit) { + if (value[bit] == verinum::V1) ++count; + } + + verinum tmp (count, integer_width); + tmp.has_sign(true); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; } /* Get the total number of dimensions for the given expression. */ @@ -1972,19 +2020,92 @@ return new NetEConst(verinum(verinum(res), integer_width)); } -NetEConst* NetESFunc::evaluate_isunknown_(const NetExpr* /*arg*/) const +NetEConst* NetESFunc::evaluate_isunknown_(const NetExpr* arg) const { - return 0; + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + unsigned is_unknown = 1; + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + if (value.is_defined()) is_unknown = 0; + + verinum tmp (is_unknown, 1U); + tmp.has_sign(false); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; } -NetEConst* NetESFunc::evaluate_onehot_(const NetExpr* /*arg*/) const +static bool is_onehot(verinum&value, bool zero_is_okay) { - return 0; + bool found_a_one = false; + + for (unsigned bit=0; bit < value.len(); ++bit) { + if (value[bit] == verinum::V1) { + if (found_a_one) return false; + found_a_one = true; + } + } + + /* If no one bit was found return true if zero is okay. */ + if (zero_is_okay) found_a_one = true; + return found_a_one; } -NetEConst* NetESFunc::evaluate_onehot0_(const NetExpr* /*arg*/) const +NetEConst* NetESFunc::evaluate_onehot_(const NetExpr* arg) const { - return 0; + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + verinum tmp (is_onehot(value, false), 1U); + tmp.has_sign(false); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; +} + +NetEConst* NetESFunc::evaluate_onehot0_(const NetExpr* arg) const +{ + const NetEConst*tmpi = dynamic_cast(arg); + + NetEConst*res = 0; + + if (tmpi) { + verinum value = tmpi->value(); + + if (value.is_string()) { + no_string_arg(this, 0); + return 0; + } + + verinum tmp (is_onehot(value, true), 1U); + tmp.has_sign(false); + res = new NetEConst(tmp); + ivl_assert(*this, res); + } + + return res; } /* Get the number of unpacked dimensions for the given expression. */ @@ -2156,7 +2277,7 @@ { switch (id) { case CTBITS: - return evaluate_countbits_(arg0, arg1); + return evaluate_countbits_(); /* The array functions are handled together. */ case HIGH: case INCR: @@ -2227,12 +2348,12 @@ built_in_func["$unpacked_dimensions" ] = UPDIMS; } - /* These are available in 1800-2009 and later. */ + /* This is available in 1800-2009 and later. */ if (funcs_need_init && (generation_flag >= GN_VER2009)) { built_in_func["$countones" ] = CTONES; } - /* These are available in 1800-2012 and later. */ + /* This is available in 1800-2012 and later. */ if (funcs_need_init && (generation_flag >= GN_VER2012)) { built_in_func["$countbits" ] = CTBITS; } @@ -2259,7 +2380,11 @@ NetExpr* NetESFunc::eval_tree() { - /* Get the ID for this system function if it is can be used as a + /* We don't support evaluating overridden functions. */ + if (is_overridden_) + return 0; + + /* Get the ID for this system function if it can be used as a * constant function. */ ID id = built_in_id_(); if (id == NOT_BUILT_IN) return 0; @@ -2267,8 +2392,9 @@ switch (parms_.size()) { case 1: if (! takes_nargs_(id, 1)) { - cerr << get_fileline() << ": error: " << name_ - << "() does not support a single argument." << endl; + cerr << get_fileline() << ": error: constant function " + << name_ << "() does not support a single argument." + << endl; return 0; } eval_expr(parms_[0]); @@ -2276,8 +2402,9 @@ case 2: if (! takes_nargs_(id, 2)) { - cerr << get_fileline() << ": error: " << name_ - << "() does not support two arguments." << endl; + cerr << get_fileline() << ": error: constant function " + << name_ << "() does not support two arguments." + << endl; return 0; } eval_expr(parms_[0]); @@ -2287,15 +2414,21 @@ default: /* Check to see if the function was called correctly. */ if (! takes_nargs_(id, parms_.size())) { - cerr << get_fileline() << ": error: " << name_ - << "() does not support " << parms_.size() + cerr << get_fileline() << ": error: constant function " + << name_ << "() does not support " << parms_.size() << " arguments." << endl; return 0; } -// HERE: Need to add support for a multi argument $countbits(). - cerr << get_fileline() << ": sorry: functions with " - << parms_.size() << " arguments are not supported: " - << name_ << "()." << endl; + if (id == CTBITS) { + for (unsigned bit = 0; bit < parms_.size(); ++bit) { + eval_expr(parms_[bit]); + } + return evaluate_countbits_(); + } else { + cerr << get_fileline() << ": sorry: constant functions with " + << parms_.size() << " arguments are not supported: " + << name_ << "()." << endl; + } return 0; } } diff -Nru iverilog-10.3/exposenodes.cc iverilog-11.0/exposenodes.cc --- iverilog-10.3/exposenodes.cc 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/exposenodes.cc 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016 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 +# include +# include "netlist.h" +# include "functor.h" +# include "compiler.h" +# include "ivl_assert.h" + + +/* + * The exposenodes functor is primarily provided for use by the vlog95 + * target. To implement some LPM objects, it needs to take a bit or part + * of one of the LPM inputs. If that input is not connected to a real + * net in the design, we need to create a net at that point so that + * there is something to which we can apply a bit or part select. This + * has the effect of splitting the synthesised structure at that point. + * Rather than creating a new net, we just look for a temporary net + * created by the synthesis process (there should be at least one) and + * reset its "local" flag. We also prepend another '_' to the synthetic + * name to avoid name collisions when we recompile the vlog95 output + * (because NetScope::local_symbol() doesn't actually check that the + * name it generates is unique). + */ + +struct exposenodes_functor : public functor_t { + + unsigned count; + + virtual void lpm_mux(Design*des, NetMux*obj); + virtual void lpm_part_select(Design*des, NetPartSelect*obj); + virtual void lpm_substitute(Design*des, NetSubstitute*obj); +}; + +static bool expose_nexus(Nexus*nex) +{ + NetNet*sig = 0; + for (Link*cur = nex->first_nlink() ; cur ; cur = cur->next_nlink()) { + + // Don't expose nodes that are attached to constants + if (dynamic_cast (cur->get_obj())) + return false; + if (dynamic_cast (cur->get_obj())) + return false; + + NetNet*cur_sig = dynamic_cast (cur->get_obj()); + if (cur_sig == 0) + continue; + + if (!cur_sig->local_flag()) + return false; + + sig = cur_sig; + } + assert(sig); + ostringstream res; + res << "_" << sig->name(); + sig->rename(lex_strings.make(res.str())); + sig->local_flag(false); + return true; +} + +/* + * The vlog95 target implements a wide mux as a hierarchy of 2:1 muxes, + * picking off one bit of the select input at each level of the hierarchy. + */ +void exposenodes_functor::lpm_mux(Design*, NetMux*obj) +{ + if (obj->sel_width() == 1) + return; + + if (expose_nexus(obj->pin_Sel().nexus())) + count += 1; +} + +/* + * A VP part select is going to select a part from its input. + */ +void exposenodes_functor::lpm_part_select(Design*, NetPartSelect*obj) +{ + if (obj->dir() != NetPartSelect::VP) + return; + + if (expose_nexus(obj->pin(1).nexus())) + count += 1; +} + +/* + * A substitute is going to select one or two parts from the wider input signal. + */ +void exposenodes_functor::lpm_substitute(Design*, NetSubstitute*obj) +{ + if (expose_nexus(obj->pin(1).nexus())) + count += 1; +} + +void exposenodes(Design*des) +{ + exposenodes_functor exposenodes; + exposenodes.count = 0; + if (verbose_flag) { + cout << " ... Look for intermediate nodes" << endl << flush; + } + des->functor(&exposenodes); + if (verbose_flag) { + cout << " ... Exposed " << exposenodes.count + << " intermediate signals." << endl << flush; + } +} diff -Nru iverilog-10.3/expr_synth.cc iverilog-11.0/expr_synth.cc --- iverilog-10.3/expr_synth.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/expr_synth.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2020 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 @@ -274,7 +274,18 @@ if (op_ == 'E' || op_ == 'N') { NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(), - width, op_=='E'?NetCaseCmp::EEQ:NetCaseCmp::NEQ); + width, op_=='E' ? NetCaseCmp::EEQ : NetCaseCmp::NEQ); + gate->set_line(*this); + connect(gate->pin(0), osig->pin(0)); + connect(gate->pin(1), lsig->pin(0)); + connect(gate->pin(2), rsig->pin(0)); + des->add_node(gate); + return osig; + } + + if (op_ == 'w' || op_ == 'W') { + NetCaseCmp*gate = new NetCaseCmp(scope, scope->local_symbol(), + width, op_=='w' ? NetCaseCmp::WEQ : NetCaseCmp::WNE); gate->set_line(*this); connect(gate->pin(0), osig->pin(0)); connect(gate->pin(1), lsig->pin(0)); @@ -534,27 +545,40 @@ return 0; } - netvector_t*osig_tmp = new netvector_t(expr_type()); - NetNet*osig = new NetNet(scope, scope->local_symbol(), - NetNet::IMPLICIT, osig_tmp); - osig->set_line(*this); - osig->local_flag(true); - NetLogic*olog; perm_string oname = scope->local_symbol(); /* Create the logic OR/AND gate. This has a single bit output, * with single bit inputs for the two operands. */ - if (op() == 'o') { - olog = new NetLogic(scope, oname, 3, NetLogic::OR, 1, true); - } else { - assert(op() == 'a'); + switch (op()) { + case 'a': olog = new NetLogic(scope, oname, 3, NetLogic::AND, 1, true); + break; + case 'o': + olog = new NetLogic(scope, oname, 3, NetLogic::OR, 1, true); + break; + case 'q': + olog = new NetLogic(scope, oname, 3, NetLogic::IMPL, 1, true); + break; + case 'Q': + olog = new NetLogic(scope, oname, 3, NetLogic::EQUIV, 1, true); + break; + default: + cerr << get_fileline() << ": sorry: " + << human_readable_op(op_) + << " is not currently supported." << endl; + des->errors += 1; + return 0; } - olog->set_line(*this); des->add_node(olog); + netvector_t*osig_tmp = new netvector_t(expr_type()); + NetNet*osig = new NetNet(scope, scope->local_symbol(), + NetNet::IMPLICIT, osig_tmp); + osig->set_line(*this); + osig->local_flag(true); + connect(osig->pin(0), olog->pin(0)); /* The left and right operands have already been reduced to a @@ -736,12 +760,18 @@ } } - if (flag == false) return 0; + if (flag == false) { + delete[]tmp; + return 0; + } ivl_assert(*this, data_type != IVL_VT_NO_TYPE); /* If this is a replication of zero just return 0. */ - if (expr_width() == 0) return 0; + if (expr_width() == 0) { + delete[]tmp; + return 0; + } /* Make a NetNet object to carry the output vector. */ perm_string path = scope->local_symbol(); @@ -1344,6 +1374,7 @@ if (nset && (nset->size() > 0)) { NetEvent*ev = new NetEvent(scope->local_symbol()); ev->set_line(*root); + ev->local_flag(true); NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(), ev, NetEvProbe::ANYEDGE, diff -Nru iverilog-10.3/functor.cc iverilog-11.0/functor.cc --- iverilog-10.3/functor.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/functor.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -78,6 +78,10 @@ { } +void functor_t::lpm_latch(Design*, NetLatch*) +{ +} + void functor_t::lpm_logic(Design*, NetLogic*) { } @@ -102,6 +106,10 @@ { } +void functor_t::lpm_substitute(Design*, NetSubstitute*) +{ +} + void functor_t::lpm_ureduce(Design*, NetUReduce*) { } @@ -215,6 +223,11 @@ fun->lpm_ff(des, this); } +void NetLatch::functor_node(Design*des, functor_t*fun) +{ + fun->lpm_latch(des, this); +} + void NetLiteral::functor_node(Design*des, functor_t*fun) { fun->lpm_literal(des, this); @@ -255,6 +268,11 @@ fun->sign_extend(des, this); } +void NetSubstitute::functor_node(Design*des, functor_t*fun) +{ + fun->lpm_substitute(des, this); +} + void NetUReduce::functor_node(Design*des, functor_t*fun) { fun->lpm_ureduce(des, this); diff -Nru iverilog-10.3/functor.h iverilog-11.0/functor.h --- iverilog-10.3/functor.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/functor.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_functor_H #define IVL_functor_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -75,6 +75,9 @@ /* This method is called for each FF in the design. */ virtual void lpm_ff(class Design*des, class NetFF*); + /* This method is called for each LATCH in the design. */ + virtual void lpm_latch(class Design*des, class NetLatch*); + /* Handle LPM combinational logic devices. */ virtual void lpm_logic(class Design*des, class NetLogic*); @@ -89,6 +92,9 @@ /* This method is called for each power. */ virtual void lpm_pow(class Design*des, class NetPow*); + /* This method is called for each part substitute. */ + virtual void lpm_substitute(class Design*des, class NetSubstitute*); + /* This method is called for each unary reduction gate. */ virtual void lpm_ureduce(class Design*des, class NetUReduce*); diff -Nru iverilog-10.3/iverilog-vpi.sh iverilog-11.0/iverilog-vpi.sh --- iverilog-10.3/iverilog-vpi.sh 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/iverilog-vpi.sh 2020-09-26 22:44:25.000000000 +0000 @@ -96,6 +96,11 @@ exit; ;; + --ccflags) + echo "$CXXFLAGS" + exit; + ;; + --ldflags) echo "$LDFLAGS" exit; diff -Nru iverilog-10.3/ivl.def iverilog-11.0/ivl.def --- iverilog-10.3/ivl.def 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/ivl.def 2020-09-26 22:44:25.000000000 +0000 @@ -181,6 +181,7 @@ ivl_path_condit ivl_path_delay ivl_path_is_condit +ivl_path_is_parallel ivl_path_scope ivl_path_source ivl_path_source_negedge @@ -211,6 +212,9 @@ ivl_scope_event ivl_scope_events ivl_scope_file +ivl_scope_func_type +ivl_scope_func_signed +ivl_scope_func_width ivl_scope_is_auto ivl_scope_is_cell ivl_scope_lineno @@ -277,6 +281,7 @@ ivl_stmt_call ivl_stmt_case_count ivl_stmt_case_expr +ivl_stmt_case_quality ivl_stmt_case_stmt ivl_stmt_cond_expr ivl_stmt_cond_false @@ -291,6 +296,7 @@ ivl_stmt_lvals ivl_stmt_lwidth ivl_stmt_name +ivl_stmt_needs_t0_trigger ivl_stmt_nevent ivl_stmt_opcode ivl_stmt_parm diff -Nru iverilog-10.3/ivlpp/globals.h iverilog-11.0/ivlpp/globals.h --- iverilog-10.3/ivlpp/globals.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/ivlpp/globals.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_globals_H #define IVL_globals_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -53,6 +53,9 @@ extern int verbose_flag; +extern int warn_redef; +extern int warn_redef_all; + /* This is the entry to the lexer. */ extern int yylex(void); diff -Nru iverilog-10.3/ivlpp/lexor.lex iverilog-11.0/ivlpp/lexor.lex --- iverilog-10.3/ivlpp/lexor.lex 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/ivlpp/lexor.lex 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ %option prefix="yy" %{ /* - * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2020 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 @@ -153,28 +153,17 @@ size_t rc = fread(buf, 1, max_size, istack->file); \ result = (rc == 0) ? YY_NULL : rc; \ } else { \ - /* We are expanding a macro. Handle the SV macro escape \ - sequences. There doesn't seem to be any good reason \ - not to allow them in traditional Verilog as well. */ \ - while ((istack->str[0] == '`') && \ - (istack->str[1] == '`')) { \ - istack->str += 2; \ + /* We are expanding a macro. Handle the SV `` delimiter. \ + If the delimiter terminates a compiler directive, leave \ + it in place, otherwise remove it now. */ \ + if (yytext[0] != '`') { \ + while ((istack->str[0] == '`') && \ + (istack->str[1] == '`')) { \ + istack->str += 2; \ + } \ } \ if (*istack->str == 0) { \ result = YY_NULL; \ - } else if ((istack->str[0] == '`') && \ - (istack->str[1] == '"')) { \ - istack->str += 2; \ - buf[0] = '"'; \ - result = 1; \ - } else if ((istack->str[0] == '`') && \ - (istack->str[1] == '\\')&& \ - (istack->str[2] == '`') && \ - (istack->str[3] == '"')) { \ - istack->str += 4; \ - buf[0] = '\\'; \ - buf[1] = '"'; \ - result = 2; \ } else { \ buf[0] = *istack->str++; \ result = 1; \ @@ -185,12 +174,12 @@ static int comment_enter = 0; static int pragma_enter = 0; static int string_enter = 0; +static int prev_state = 0; static int ma_parenthesis_level = 0; %} %option stack -%option nounput %option noinput %option noyy_top_state %option noyywrap @@ -208,6 +197,11 @@ %x CSTRING %x ERROR_LINE +%x IFDEF_NAME +%x IFNDEF_NAME +%x ELSIF_NAME +%x ELSIF_SUPR + %x IFDEF_FALSE %s IFDEF_TRUE %x IFDEF_SUPR @@ -262,9 +256,7 @@ if (macro_needs_args(yytext+1)) yy_push_state(MA_START); else do_expand(0); } - /* Strings do not contain preprocessor directives, but can expand - * macros. If that happens, they get expanded in the context of the - * string. + /* Strings do not contain preprocessor directives or macro expansions. */ \" { string_enter = YY_START; BEGIN(CSTRING); ECHO; } \\\\ | @@ -407,56 +399,51 @@ * condition that stacks on top of the IFDEF_FALSE so that output is * not accidentally turned on within nested ifdefs. */ -`ifdef{W}[a-zA-Z_][a-zA-Z0-9_$]* { - char* name = strchr(yytext, '`'); assert(name); - - name += 6; - name += strspn(name, " \t\b\f"); - +`ifdef{W} { ifdef_enter(); - - if (is_defined(name)) - yy_push_state(IFDEF_TRUE); - else - yy_push_state(IFDEF_FALSE); + yy_push_state(IFDEF_NAME); } -`ifndef{W}[a-zA-Z_][a-zA-Z0-9_$]* { - char* name = strchr(yytext, '`'); assert(name); - - name += 7; - name += strspn(name, " \t\b\f"); - +`ifndef{W} { ifdef_enter(); - - if (!is_defined(name)) - yy_push_state(IFDEF_TRUE); - else - yy_push_state(IFDEF_FALSE); + yy_push_state(IFNDEF_NAME); } `ifdef{W} | `ifndef{W} { ifdef_enter(); yy_push_state(IFDEF_SUPR); } -`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { BEGIN(IFDEF_SUPR); } +`elsif{W} { prev_state = YYSTATE; BEGIN(ELSIF_SUPR); } +`elsif{W} { prev_state = YYSTATE; BEGIN(ELSIF_NAME); } +`elsif{W} { prev_state = YYSTATE; BEGIN(ELSIF_SUPR); } -`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { - char* name = strchr(yytext, '`'); assert(name); +`else { BEGIN(IFDEF_SUPR); } +`else { BEGIN(IFDEF_TRUE); } +`else {} - name += 6; - name += strspn(name, " \t\b\f"); +[a-zA-Z_][a-zA-Z0-9_$]* { + if (is_defined(yytext)) + BEGIN(IFDEF_TRUE); + else + BEGIN(IFDEF_FALSE); +} - if (is_defined(name)) +[a-zA-Z_][a-zA-Z0-9_$]* { + if (!is_defined(yytext)) BEGIN(IFDEF_TRUE); else BEGIN(IFDEF_FALSE); } -`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { } +[a-zA-Z_][a-zA-Z0-9_$]* { + if (is_defined(yytext)) + BEGIN(IFDEF_TRUE); + else + BEGIN(IFDEF_FALSE); +} -`else { BEGIN(IFDEF_SUPR); } -`else { BEGIN(IFDEF_TRUE); } -`else {} +[a-zA-Z_][a-zA-Z0-9_$]* { + BEGIN(IFDEF_SUPR); +} "//"[^\r\n]* {} @@ -477,25 +464,45 @@ `endif { ifdef_leave(); yy_pop_state(); } +(\n|\r) | +. | `ifdef { error_count += 1; fprintf(stderr, "%s:%u: `ifdef without a macro name - ignored.\n", istack->path, istack->lineno+1); + if (YY_START == IFDEF_NAME) { + ifdef_leave(); + yy_pop_state(); + unput(yytext[0]); + } } +(\n|\r) | +. | `ifndef { error_count += 1; fprintf(stderr, "%s:%u: `ifndef without a macro name - ignored.\n", istack->path, istack->lineno+1); + if (YY_START == IFNDEF_NAME) { + ifdef_leave(); + yy_pop_state(); + unput(yytext[0]); + } } +(\n|\r) | +. | `elsif { error_count += 1; fprintf(stderr, "%s:%u: `elsif without a macro name - ignored.\n", istack->path, istack->lineno+1); + if (YY_START != INITIAL) { + BEGIN(prev_state); + unput(yytext[0]); + } } -`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { +`elsif{W}[a-zA-Z_][a-zA-Z0-9_$]* { error_count += 1; fprintf(stderr, "%s:%u: `elsif without a matching `ifdef - ignored.\n", istack->path, istack->lineno+1); @@ -531,16 +538,33 @@ /* Stringified version of macro expansion. This is an Icarus extension. When expanding macro text, the SV usage of `` takes precedence. */ ``[a-zA-Z_][a-zA-Z0-9_$]* { - assert(istack->file); - assert(do_expand_stringify_flag == 0); - do_expand_stringify_flag = 1; - fputc('"', yyout); - if (macro_needs_args(yytext+2)) - yy_push_state(MA_START); - else - do_expand(0); + if (istack->file) { + assert(do_expand_stringify_flag == 0); + do_expand_stringify_flag = 1; + fputc('"', yyout); + if (macro_needs_args(yytext+2)) + yy_push_state(MA_START); + else + do_expand(0); + } else { + REJECT; + } } + /* If we are expanding a macro, remove the SV `` delimiter, otherwise + * leave it to be handled by the normal rules. + */ +`` { if (istack->file) REJECT; } + + /* If we are expanding a macro, handle the SV `" override. This avoids + * entering CSTRING state, thus allowing nested macro expansions. + */ +`\" { if (!istack->file) fputc('"', yyout); else REJECT; } + + /* If we are expanding a macro, handle the SV `\`" escape sequence. + */ +`\\`\" { if (!istack->file) fputs("\\\"", yyout); else REJECT; } + \( { BEGIN(MA_ADD); macro_start_args(); } {W} {} @@ -575,7 +599,7 @@ {W} { macro_add_to_arg(1); } -"(" { macro_add_to_arg(0); ma_parenthesis_level++; } +[({] { macro_add_to_arg(0); ma_parenthesis_level++; } "," { if (ma_parenthesis_level > 0) @@ -584,7 +608,7 @@ macro_finish_arg(); } -")" { +[)}] { if (ma_parenthesis_level > 0) { macro_add_to_arg(0); ma_parenthesis_level--; @@ -858,6 +882,25 @@ { int idx; struct define_t* def; + struct define_t* prev; + + /* Verilog has a very nasty system of macros jumping from + * file to file, resulting in a global macro scope. Here + * we optionally warn about any redefinitions. + * + * If istack is empty, we are processing a configuration + * or precompiled macro file, so don't want to check for + * redefinitions - when a precompiled macro file is used, + * it will contain copies of any predefined macros. + */ + if (warn_redef && istack) { + prev = def_lookup(name); + if (prev && (warn_redef_all || (strcmp(prev->value, value) != 0))) { + emit_pathline(istack); + fprintf(stderr, "warning: redefinition of macro %s from value '%s' to '%s'\n", + name, prev->value, value); + } + } def = malloc(sizeof(struct define_t)); def->name = strdup(name); @@ -1662,7 +1705,8 @@ } for (idx = start ; idx < include_cnt ; idx += 1) { - sprintf(path, "%s/%s", include_dir[idx], standby->path); + snprintf(path, sizeof(path), "%s/%s", + include_dir[idx], standby->path); if ((standby->file = fopen(path, "r"))) { standby->file_close = fclose; diff -Nru iverilog-10.3/ivlpp/main.c iverilog-11.0/ivlpp/main.c --- iverilog-10.3/ivlpp/main.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/ivlpp/main.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "Copyright (c) 1999-2011,2015 Stephen Williams (steve@icarus.com)"; + "Copyright (c) 1999-2020 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 @@ -98,6 +98,10 @@ unsigned error_count = 0; FILE *depend_file = NULL; +/* Should we warn about macro redefinitions? */ +int warn_redef = 0; +int warn_redef_all = 0; + static int flist_read_flags(const char*path) { char line_buf[2048]; @@ -282,7 +286,7 @@ include_dir[0] = 0; /* 0 is reserved for the current files path. */ include_dir[1] = strdup("."); - while ((opt=getopt(argc, argv, "F:f:K:Lo:p:P:vV")) != EOF) switch (opt) { + while ((opt=getopt(argc, argv, "F:f:K:Lo:p:P:vVW:")) != EOF) switch (opt) { case 'F': flist_read_flags(optarg); @@ -336,6 +340,15 @@ break; } + case 'W': + if (strcmp(optarg, "redef-all") == 0) { + warn_redef_all = 1; + warn_redef = 1; + } else if (strcmp(optarg, "redef-chg") == 0) { + warn_redef = 1; + } + break; + case 'v': fprintf(stderr, "Icarus Verilog Preprocessor version " VERSION " (" VERSION_TAG ")\n\n"); @@ -366,7 +379,10 @@ " -p - Write precompiled defines to \n" " -P - Read precompiled defines from \n" " -v - Verbose\n" - " -V - Print version information and quit\n", + " -V - Print version information and quit\n" + " -W - Enable extra ivlpp warning category:\n" + " o redef-all - all macro redefinitions\n" + " o redef-chg - macro definition changes\n", argv[0]); return flag_errors; } diff -Nru iverilog-10.3/ivlpp/Makefile.in iverilog-11.0/ivlpp/Makefile.in --- iverilog-10.3/ivlpp/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/ivlpp/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -60,7 +60,7 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f $(INCLUDE_PATH) $^ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status cd ..; ./config.status --file=ivlpp/$@ @@ -71,9 +71,11 @@ lexor.c: $(srcdir)/lexor.lex $(LEX) -t $< > $@ -install: all installdirs $(libdir)/ivl$(suffix)/ivlpp@EXEEXT@ +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/ivlpp@EXEEXT@: ivlpp@EXEEXT@ +F = ivlpp@EXEEXT@ + +installfiles: $(F) | installdirs $(INSTALL_PROGRAM) ./ivlpp@EXEEXT@ "$(DESTDIR)$(libdir)/ivl$(suffix)/ivlpp@EXEEXT@" installdirs: $(srcdir)/../mkinstalldirs diff -Nru iverilog-10.3/ivl_target.h iverilog-11.0/ivl_target.h --- iverilog-10.3/ivl_target.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/ivl_target.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_ivl_target_H #define IVL_ivl_target_H /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 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 @@ -265,6 +265,8 @@ IVL_LO_BUFT = 24, /* transparent bufz. (NOT "tri-state") */ IVL_LO_BUFZ = 5, IVL_LO_CMOS = 22, + IVL_LO_EQUIV = 25, + IVL_LO_IMPL = 26, IVL_LO_NAND = 6, IVL_LO_NMOS = 7, IVL_LO_NOR = 8, @@ -280,7 +282,6 @@ IVL_LO_RPMOS = 16, IVL_LO_XNOR = 18, IVL_LO_XOR = 19, - IVL_LO_UDP = 21 } ivl_logic_t; @@ -306,8 +307,10 @@ IVL_LPM_CONCAT = 16, IVL_LPM_CONCATZ = 36, /* Transparent concat */ IVL_LPM_CMP_EEQ= 18, /* Case EQ (===) */ - IVL_LPM_CMP_EQX= 37, /* Wildcard EQ (==?) */ + IVL_LPM_CMP_EQX= 37, /* Wildcard EQ (casex) */ IVL_LPM_CMP_EQZ= 38, /* casez EQ */ + IVL_LPM_CMP_WEQ= 41, + IVL_LPM_CMP_WNE= 42, IVL_LPM_CMP_EQ = 10, IVL_LPM_CMP_GE = 1, IVL_LPM_CMP_GT = 2, @@ -315,6 +318,7 @@ IVL_LPM_CMP_NEE= 19, /* Case NE (!==) */ IVL_LPM_DIVIDE = 12, IVL_LPM_FF = 3, + IVL_LPM_LATCH = 40, IVL_LPM_MOD = 13, IVL_LPM_MULT = 4, IVL_LPM_MUX = 5, @@ -352,9 +356,12 @@ /* Processes are initial, always, or final blocks with a statement. This is the type of the ivl_process_t object. */ typedef enum ivl_process_type_e ENUM_UNSIGNED_INT { - IVL_PR_INITIAL = 0, - IVL_PR_ALWAYS = 1, - IVL_PR_FINAL = 2 + IVL_PR_INITIAL = 0, + IVL_PR_ALWAYS = 1, + IVL_PR_ALWAYS_COMB = 3, + IVL_PR_ALWAYS_FF = 4, + IVL_PR_ALWAYS_LATCH = 5, + IVL_PR_FINAL = 2 } ivl_process_type_t; /* These are the sorts of reasons a scope may come to be. These types @@ -428,6 +435,14 @@ IVL_ST_WHILE = 23 } ivl_statement_type_t; +/* Case statements can be tagged as unique/unique0/priority. */ +typedef enum ivl_case_quality_t { + IVL_CASE_QUALITY_BASIC = 0, /* no quality flags */ + IVL_CASE_QUALITY_UNIQUE = 1, + IVL_CASE_QUALITY_UNIQUE0 = 2, + IVL_CASE_QUALITY_PRIORITY = 3 +} ivl_case_quality_t; + /* SystemVerilog allows a system function to be called as a task. */ typedef enum ivl_sfunc_as_task_e { IVL_SFUNC_AS_TASK_ERROR = 0, @@ -510,6 +525,10 @@ * ivl_path_is_condit * Is this a conditional structure? Needed for ifnone. * + * ivl_path_is_parallel + * This returns true if the path is a parallel connection and + * false if the path is a full connection. + * * ivl_path_source_posedge * ivl_path_source_negedge * These functions return true if the source is edge sensitive. @@ -520,6 +539,8 @@ extern ivl_nexus_t ivl_path_condit(ivl_delaypath_t obj); extern int ivl_path_is_condit(ivl_delaypath_t obj); +extern int ivl_path_is_parallel(ivl_delaypath_t obj); + extern int ivl_path_source_posedge(ivl_delaypath_t obj); extern int ivl_path_source_negedge(ivl_delaypath_t obj); @@ -1311,8 +1332,13 @@ * inputs and the Q. All the types must be exactly the same. * * - D-FlipFlop (IVL_LPM_FF) - * This data is an edge sensitive register. The ivl_lpm_q output and - * single ivl_lpm_data input are the same with, ivl_lpm_width. This + * This device is an edge sensitive register. The ivl_lpm_q output and + * single ivl_lpm_data input are the same width, ivl_lpm_width. This + * device carries a vector like other LPM devices. + * + * - Latch (IVL_LPM_LATCH) + * This device is an asynchronous latch. The ivl_lpm_q output and + * single ivl_lpm_data input are the same width, ivl_lpm_width. This * device carries a vector like other LPM devices. * * - Memory port (IVL_LPM_RAM) (deprecated in favor of IVL_LPM_ARRAY) @@ -1430,18 +1456,18 @@ extern ivl_nexus_t ivl_lpm_clk(ivl_lpm_t net); /* IVL_LPM_UFUNC */ extern ivl_scope_t ivl_lpm_define(ivl_lpm_t net); - /* IVL_LPM_FF */ + /* IVL_LPM_FF IVL_LPM_LATCH*/ extern ivl_nexus_t ivl_lpm_enable(ivl_lpm_t net); /* IVL_LPM_ADD IVL_LPM_CONCAT IVL_LPM_FF IVL_LPM_PART IVL_LPM_MULT IVL_LPM_MUX IVL_LPM_POW IVL_LPM_SHIFTL IVL_LPM_SHIFTR IVL_LPM_SUB - IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE */ + IVL_LPM_UFUNC IVL_LPM_SUBSTITUTE IVL_LPM_LATCH */ extern ivl_nexus_t ivl_lpm_data(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_MULT IVL_LPM_POW IVL_LPM_SUB IVL_LPM_CMP_EQ IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE */ extern ivl_nexus_t ivl_lpm_datab(ivl_lpm_t net, unsigned idx); /* IVL_LPM_ADD IVL_LPM_FF IVL_LPM_MULT IVL_LPM_PART IVL_LPM_POW IVL_LPM_SUB IVL_LPM_UFUNC IVL_LPM_CMP_EEQ IVL_LPM_CMP_EQX - IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE */ + IVL_LPM_CMP_EQZ IVL_LPM_CMP_NEE IVL_LPM_SUBSTITUTE IVL_LPM_LATCH */ extern ivl_nexus_t ivl_lpm_q(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive0(ivl_lpm_t net); extern ivl_drive_t ivl_lpm_drive1(ivl_lpm_t net); @@ -1747,6 +1773,13 @@ * ivl_scope_lineno * Returns the instantiation file and line for this scope. * + * ivl_scope_func_type + * ivl_scope_func_signed + * ivl_scope_func_width + * + * If the scope is a function, these function can be used to get + * the type of the return value. + * * ivl_scope_is_auto * Is the task or function declared to be automatic? * @@ -1865,6 +1898,9 @@ extern int ivl_scope_time_precision(ivl_scope_t net); extern int ivl_scope_time_units(ivl_scope_t net); +extern ivl_variable_type_t ivl_scope_func_type(ivl_scope_t net); +extern int ivl_scope_func_signed(ivl_scope_t net); +extern unsigned ivl_scope_func_width(ivl_scope_t net); /* SIGNALS * Signals are named things in the Verilog source, like wires and @@ -2086,6 +2122,7 @@ * handle disable statements. * * ivl_stmt_events + * ivl_stmt_needs_t0_trigger * ivl_stmt_nevent * Statements that have event arguments (TRIGGER and WAIT) make * those event objects available through these methods. @@ -2200,6 +2237,8 @@ extern unsigned ivl_stmt_case_count(ivl_statement_t net); /* IVL_ST_CASE,IVL_ST_CASER,IVL_ST_CASEX,IVL_ST_CASEZ */ extern ivl_expr_t ivl_stmt_case_expr(ivl_statement_t net, unsigned i); + /* IVL+ST_CASE,IVL_ST_CASER,IVL_ST_CASEX,IVL_ST_CASEZ */ +extern ivl_case_quality_t ivl_stmt_case_quality(ivl_statement_t net); /* IVL_ST_CASE,IVL_ST_CASER,IVL_ST_CASEX,IVL_ST_CASEZ */ extern ivl_statement_t ivl_stmt_case_stmt(ivl_statement_t net, unsigned i); /* IVL_ST_CONDIT IVL_ST_CASE IVL_ST_REPEAT IVL_ST_WHILE */ @@ -2213,6 +2252,7 @@ /* IVL_ST_DELAY */ extern uint64_t ivl_stmt_delay_val(ivl_statement_t net); /* IVL_ST_WAIT IVL_ST_TRIGGER */ +extern unsigned ivl_stmt_needs_t0_trigger(ivl_statement_t net); extern unsigned ivl_stmt_nevent(ivl_statement_t net); extern ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx); /* IVL_ST_CONTRIB */ @@ -2321,7 +2361,7 @@ extern ivl_type_t ivl_type_prop_type(ivl_type_t net, int idx); -#if defined(__MINGW32__) || defined (__CYGWIN32__) +#if defined(__MINGW32__) || defined (__CYGWIN__) # define DLLEXPORT __declspec(dllexport) #else # define DLLEXPORT diff -Nru iverilog-10.3/ivl_target_priv.h iverilog-11.0/ivl_target_priv.h --- iverilog-10.3/ivl_target_priv.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/ivl_target_priv.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_ivl_target_priv_H #define IVL_ivl_target_priv_H /* - * Copyright (c) 2008-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2019 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 @@ -51,8 +51,6 @@ ivl_process_t threads_; // Keep arrays of root scopes. - std::map classes; - std::map root_tasks; std::vector packages; std::vector roots; diff -Nru iverilog-10.3/lexor.lex iverilog-11.0/lexor.lex --- iverilog-10.3/lexor.lex 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/lexor.lex 2020-09-26 22:44:25.000000000 +0000 @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 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 @@ -51,6 +51,22 @@ */ extern YYLTYPE yylloc; +char* yytext_string_filter(const char*str, size_t str_len) +{ + if (str == 0) return 0; + char*buf = new char[str_len+1]; + for (size_t idx = 0 ; idx < str_len ; idx += 1) { + if (str[idx] == 0) { + VLerror(yylloc, "error: Found nil (\\000) in string literal, replacing with space (\\015) character."); + buf[idx] = ' '; + } else { + buf[idx] = str[idx]; + } + } + buf[str_len] = 0; + return buf; +} + char* strdupnew(char const *str) { return str ? strcpy(new char [strlen(str)+1], str) : 0; @@ -72,6 +88,7 @@ void reset_lexor(); static void line_directive(); static void line_directive2(); +static void reset_all(); verinum*make_unsized_binary(const char*txt); verinum*make_undef_highz_dec(const char*txt); @@ -187,8 +204,11 @@ "!=" { return K_NE; } "===" { return K_CEQ; } "!==" { return K_CNE; } +"==?" { return K_WEQ; } +"!=?" { return K_WNE; } "||" { return K_LOR; } "&&" { return K_LAND; } +"<->" { return K_LEQUIV; } "&&&" { return K_TAND; } "~|" { return K_NOR; } "~^" { return K_NXOR; } @@ -227,12 +247,12 @@ \\\\ { yymore(); /* Catch \\, which is a \ escaping itself */ } \\\" { yymore(); /* Catch \", which is an escaped quote */ } \n { BEGIN(0); - yylval.text = strdupnew(yytext); + yylval.text = yytext_string_filter(yytext, yyleng); VLerror(yylloc, "Missing close quote of string."); yylloc.first_line += 1; return STRING; } \" { BEGIN(0); - yylval.text = strdupnew(yytext); + yylval.text = yytext_string_filter(yytext, yyleng); yylval.text[strlen(yytext)-1] = 0; return STRING; } . { yymore(); } @@ -310,15 +330,6 @@ BEGIN(UDPTABLE); break; - /* Translate these to checks if we already have or are - * outside the declaration region. */ - case K_timeunit: - if (have_timeunit_decl) rc = K_timeunit_check; - break; - case K_timeprecision: - if (have_timeprec_decl) rc = K_timeprecision_check; - break; - default: yylval.text = 0; break; @@ -368,7 +379,7 @@ /* If this identifier names a previously declared type, then return this as a TYPE_IDENTIFIER instead. */ if (rc == IDENTIFIER && gn_system_verilog()) { - if (data_type_t*type = pform_test_type_identifier(yylval.text)) { + if (data_type_t*type = pform_test_type_identifier(yylloc, yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; rc = TYPE_IDENTIFIER; @@ -389,7 +400,7 @@ } } if (gn_system_verilog()) { - if (data_type_t*type = pform_test_type_identifier(yylval.text)) { + if (data_type_t*type = pform_test_type_identifier(yylloc, yylval.text)) { yylval.type_identifier.text = yylval.text; yylval.type_identifier.type = type; return TYPE_IDENTIFIER; @@ -428,6 +439,12 @@ if (strcmp(yytext,"$attribute") == 0) return KK_attribute; + + if (gn_system_verilog() && strcmp(yytext,"$unit") == 0) { + yylval.package = pform_units.back(); + return PACKAGE_IDENTIFIER; + } + yylval.text = strdupnew(yytext); return SYSTEM_IDENTIFIER; } @@ -566,11 +583,7 @@ "definition." << endl; error_count += 1; } else { - pform_set_default_nettype(NetNet::WIRE, yylloc.text, - yylloc.first_line); - in_celldefine = false; - uc_drive = UCD_NONE; - pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); + reset_all(); } } /* Notice and handle the `unconnected_drive directive. */ @@ -872,6 +885,14 @@ for (const char*idx = ptr ; *idx ; idx += 1) if (*idx != '_') size += 1; + if (size == 0) { + VLerror(yylloc, "Numeric literal has no digits in it."); + verinum*out = new verinum(); + out->has_sign(sign_flag); + out->is_single(single_flag); + return out; + } + if ((based_size > 0) && (size > based_size)) yywarn(yylloc, "extra digits given for sized binary constant."); @@ -1589,6 +1610,18 @@ yylloc.first_line = lineno; } +/* + * Reset all compiler directives. This will be called when a `resetall + * directive is encountered or when a new compilation unit is started. + */ +static void reset_all() +{ + pform_set_default_nettype(NetNet::WIRE, yylloc.text, yylloc.first_line); + in_celldefine = false; + uc_drive = UCD_NONE; + pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); +} + extern FILE*vl_input; void reset_lexor() { @@ -1597,6 +1630,14 @@ /* Announce the first file name. */ yylloc.text = set_file_name(strdupnew(vl_file.c_str())); + + if (separate_compilation) { + reset_all(); + if (!keyword_mask_stack.empty()) { + lexor_keyword_mask = keyword_mask_stack.back(); + keyword_mask_stack.clear(); + } + } } /* diff -Nru iverilog-10.3/libmisc/StringHeap.cc iverilog-11.0/libmisc/StringHeap.cc --- iverilog-10.3/libmisc/StringHeap.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/libmisc/StringHeap.cc 2020-09-26 22:44:25.000000000 +0000 @@ -29,10 +29,12 @@ static unsigned string_pool_count = 0; #endif +static const unsigned DEFAULT_CELL_SIZE = 0x10000; + + StringHeap::StringHeap() { cell_base_ = 0; - cell_size_ = 0; cell_ptr_ = 0; } @@ -45,17 +47,37 @@ const char* StringHeap::add(const char*text) { unsigned len = strlen(text); - unsigned rem = cell_size_ - cell_ptr_; + unsigned rem = cell_base_==0? 0 : (DEFAULT_CELL_SIZE - cell_ptr_); + + // Special case: huge items get their own allocation. + if ( (len+1) >= DEFAULT_CELL_SIZE ) { + char*buf = strdup(text); +#ifdef CHECK_WITH_VALGRIND + string_pool_count += 1; + string_pool = (char**)realloc(string_pool, + string_pool_count*sizeof(char**)); + string_pool[string_pool_count-1] = buf; +#endif + return buf; + } + + // If the current cell that I'm working through cannot hold + // the current string, then set it aside and get another cell + // to put the string into. if (rem < (len+1)) { - // release any unused memory + // release any unused memory. This assumes that a + // realloc shrink of the memory region will return the + // same pointer. if (rem > 0) { + char*old = cell_base_; cell_base_ = (char*)realloc(cell_base_, cell_ptr_); assert(cell_base_ != 0); + assert(cell_base_ == old); } // start new cell - cell_size_ = (len+1) > DEFAULT_CELL_SIZE ? len+1 : DEFAULT_CELL_SIZE; - cell_base_ = (char*)malloc(cell_size_); + cell_base_ = (char*)malloc(DEFAULT_CELL_SIZE); cell_ptr_ = 0; + rem = DEFAULT_CELL_SIZE; assert(cell_base_ != 0); #ifdef CHECK_WITH_VALGRIND string_pool_count += 1; @@ -65,12 +87,13 @@ #endif } + assert( (len+1) <= rem ); char*res = cell_base_ + cell_ptr_; memcpy(res, text, len); cell_ptr_ += len; cell_base_[cell_ptr_++] = 0; - assert(cell_ptr_ <= cell_size_); + assert(cell_ptr_ <= DEFAULT_CELL_SIZE); return res; } diff -Nru iverilog-10.3/libmisc/StringHeap.h iverilog-11.0/libmisc/StringHeap.h --- iverilog-10.3/libmisc/StringHeap.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/libmisc/StringHeap.h 2020-09-26 22:44:25.000000000 +0000 @@ -46,7 +46,7 @@ private: friend class StringHeap; friend class StringHeapLex; - perm_string(const char*t) : text_(t) { }; + explicit perm_string(const char*t) : text_(t) { }; private: const char*text_; @@ -78,10 +78,7 @@ perm_string make(const char*); private: - static const unsigned DEFAULT_CELL_SIZE = 0x10000; - char*cell_base_; - unsigned cell_size_; unsigned cell_ptr_; private: // not implemented diff -Nru iverilog-10.3/libveriuser/cppcheck.sup iverilog-11.0/libveriuser/cppcheck.sup --- iverilog-10.3/libveriuser/cppcheck.sup 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/libveriuser/cppcheck.sup 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,187 @@ +// These are the functions that the runtime exports. + +// The ACC functions. + +// acc_close() +unusedFunction:a_close.c:23 +// acc_compare_handles() +unusedFunction:a_compare_handles.c:23 +// acc_configure() +unusedFunction:a_configure.c:25 + +// acc_fetch_argc() +unusedFunction:a_fetch_argc.c:27 +// acc_fetch_argv() +unusedFunction:a_fetch_argv.c:27 +// acc_fetch_defname() +unusedFunction:a_fetch_fullname.c:37 +// acc_fetch_direction() +unusedFunction:a_fetch_dir.c:26 +// acc_fetch_fulltype() +unusedFunction:a_fetch_type.c:66 +// acc_fetch_itfarg() +unusedFunction:a_fetch_tfarg.c:27 +// acc_fetch_location() +unusedFunction:a_fetch_location.c:23 +// acc_fetch_name() +unusedFunction:a_fetch_fullname.c:32 +// acc_fetch_paramtype() +unusedFunction:a_fetch_type_str.c:47 +// acc_fetch_paramval() +unusedFunction:a_fetch_param.c:25 +// acc_fetch_range() +unusedFunction:a_fetch_range.c:26 +// acc_fetch_size() +unusedFunction:a_fetch_type.c:24 +// acc_fetch_tfarg() +unusedFunction:a_fetch_tfarg.c:56 +// acc_fetch_tfarg_int() +unusedFunction:a_fetch_tfarg.c:91 +// acc_fetch_timescale_info() +unusedFunction:a_fetch_time.c:24 +// acc_fetch_type() +unusedFunction:a_fetch_type.c:29 +// acc_fetch_type_str() +unusedFunction:a_fetch_type_str.c:24 +// acc_fetch_value() +unusedFunction:a_fetch_value.c:115 + +// acc_handle_by_name() +unusedFunction:a_handle_by_name.c:29 +// acc_handle_hiconn() +unusedFunction:a_handle_hiconn.c:26 +// acc_handle_object() +unusedFunction:a_handle_object.c:26 +// acc_handle_parent() +unusedFunction:a_handle_parent.c:24 +// acc_handle_scope() +unusedFunction:a_handle_parent.c:34 +// acc_handle_simulated_net() +unusedFunction:a_handle_simulated_net.c:26 +// acc_handle_tfarg() +unusedFunction:a_handle_tfarg.c:26 +// acc_handle_tfinst() +unusedFunction:a_handle_tfarg.c:53 + +// acc_initialize() +unusedFunction:a_initialize.c:24 + +// acc_next_bit() +unusedFunction:a_next_bit.c:26 +// acc_next_port() +unusedFunction:a_next_port.c:26 +// acc_next_scope() +unusedFunction:a_next.c:86 +// acc_next_topmod() +unusedFunction:a_next_topmod.c:30 + +// acc_product_version() +unusedFunction:a_product_version.c:23 + +// acc_set_scope() +unusedFunction:a_handle_object.c:40 + +// acc_set_value() +unusedFunction:a_set_value.c:27 + +// acc_vcl_add() +unusedFunction:a_vcl.c:157 +// acc_vcl_delete() +unusedFunction:a_vcl.c:196 + +// acc_version() +unusedFunction:a_version.c:23 + +// These are the TF routines. + +// io_printf() +unusedFunction:io_print.c:26 + +// mc_scan_plusargs() +unusedFunction:mc_scan_plusargs.c:27 + +// tf_asynchoff() +unusedFunction:asynch.c:34 +// tf_asynchon() +unusedFunction:asynch.c:28 +// tf_dofinish() +unusedFunction:finish.c:26 +// tf_dostop() +unusedFunction:finish.c:32 +// tf_error() +unusedFunction:io_print.c:45 +// tf_exprinfo() +unusedFunction:exprinfo.c:26 +// tf_getcstringp() +unusedFunction:getcstringp.c:26 +// tf_getlongp() +unusedFunction:getlongp.c:29 +// tf_getlongtime() +unusedFunction:getsimtime.c:112 +// tf_getp() +unusedFunction:getp.c:73 +// tf_getrealp() +unusedFunction:getp.c:120 +// tf_gettime() +unusedFunction:getsimtime.c:77 +// tf_getworkarea() +unusedFunction:workarea.c:62 +// tf_igettimeprecision() +unusedFunction:getsimtime.c:198 +// tf_igettimeunit() +unusedFunction:getsimtime.c:228 +// tf_long_to_real() +unusedFunction:math.c:47 +// tf_message() +unusedFunction:io_print.c:56 +// tf_mipname() +unusedFunction:spname.c:47 +// tf_multiply_long() +unusedFunction:math.c:27 +// tf_nodeinfo() +unusedFunction:nodeinfo.c:27 +// tf_nump() +unusedFunction:nump.c:42 +// tf_putlongp() +unusedFunction:putlongp.c:28 +// tf_putp() +unusedFunction:putp.c:88 +// tf_putrealp() +unusedFunction:putp.c:137 +// tf_real_to_long() +unusedFunction:math.c:40 +// tf_rosynchronize() +unusedFunction:veriusertfs.c:391 +// tf_scale_longdelay() +unusedFunction:getsimtime.c:136 +// tf_scale_realdelay() +unusedFunction:getsimtime.c:161 +// tf_setdelay() +unusedFunction:delay.c:75 +// tf_setrealdelay() +unusedFunction:veriusertfs.c:426 +// tf_setworkarea() +unusedFunction:workarea.c:36 +// tf_spname() +unusedFunction:spname.c:25 +// tf_strgetp() +unusedFunction:getp.c:177 +// tf_strgettime() +unusedFunction:getsimtime.c:85 +// tf_synchronize() +unusedFunction:veriusertfs.c:364 +// tf_typep() +unusedFunction:typep.c:24 +// tf_unscale_longdelay() +unusedFunction:getsimtime.c:144 +// tf_unscale_realdelay() +unusedFunction:getsimtime.c:171 +// tf_warning() +unusedFunction:io_print.c:34 + +// Non-standard TF routines the Icarus provides. + +// tf_getlongsimtime() +unusedFunction:getsimtime.c:126 +// veriusertfs_register_table() +unusedFunction:veriusertfs.c:76 diff -Nru iverilog-10.3/libveriuser/Makefile.in iverilog-11.0/libveriuser/Makefile.in --- iverilog-10.3/libveriuser/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/libveriuser/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -76,7 +76,9 @@ rm -f config.h stamp-config-h cppcheck: $(O:.o=.c) - cppcheck --enable=all -f $(INCLUDE_PATH) $^ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ + --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in cd ..; ./config.status --file=libveriuser/$@ @@ -101,9 +103,11 @@ $(CC) $(CPPFLAGS) $(CFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep -install:: all installdirs $(libdir)/libveriuser$(suffix).a $(INSTALL32) +install:: all installdirs installfiles -$(libdir)/libveriuser$(suffix).a: ./libveriuser.a +F = ./libveriuser.a + +installfiles: $(F) | installdirs $(INSTALL_DATA) ./libveriuser.a "$(DESTDIR)$(libdir)/libveriuser$(suffix).a" installdirs: $(srcdir)/../mkinstalldirs diff -Nru iverilog-10.3/libveriuser/priv.h iverilog-11.0/libveriuser/priv.h --- iverilog-10.3/libveriuser/priv.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/libveriuser/priv.h 2020-09-26 22:44:25.000000000 +0000 @@ -31,6 +31,6 @@ /* * Trace file for logging ACC and TF calls. */ -FILE* pli_trace; +extern FILE* pli_trace; #endif /* IVL_priv_H */ diff -Nru iverilog-10.3/link_const.cc iverilog-11.0/link_const.cc --- iverilog-10.3/link_const.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/link_const.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2016 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 @@ -102,6 +102,17 @@ break; } + const NetSubstitute*ps = dynamic_cast(cur->get_obj()); + if (ps) { + if (ps->pin(1).nexus()->drivers_constant() && + ps->pin(2).nexus()->drivers_constant() ) { + constant_drivers += 1; + continue; + } + driven_ = VAR; + return false; + } + if (! dynamic_cast(cur->get_obj())) { driven_ = VAR; return false; @@ -162,13 +173,13 @@ if ((sig->type() == NetNet::SUPPLY0) || (sig->type() == NetNet::TRI0)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val == verinum::Vz); + ivl_assert(*sig, val == verinum::Vz); val = verinum::V0; } if ((sig->type() == NetNet::SUPPLY1) || (sig->type() == NetNet::TRI1)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val == verinum::Vz); + ivl_assert(*sig, val == verinum::Vz); val = verinum::V1; } } @@ -199,10 +210,12 @@ const Link*cur = list_; verinum val; + verinum pval; unsigned width = 0; for (cur = first_nlink() ; cur ; cur = cur->next_nlink()) { + const NetSubstitute*ps; const NetConst*obj; const NetNet*sig; if ((obj = dynamic_cast(cur->get_obj()))) { @@ -212,6 +225,18 @@ val = obj->value(); width = val.len(); + } else if ((ps = dynamic_cast(cur->get_obj()))) { + if (cur->get_pin() != 0) + continue; + + // Multiple drivers are not currently supported. + ivl_assert(*ps, val.len() == 0); + val = ps->pin(1).nexus()->driven_vector(); + pval = ps->pin(2).nexus()->driven_vector(); + for (unsigned idx = 0; idx < pval.len(); idx += 1) + val.set(ps->base() + idx, pval.get(idx)); + width = val.len(); + } else if ((sig = dynamic_cast(cur->get_obj()))) { width = sig->vector_width(); @@ -222,13 +247,13 @@ if ((sig->type() == NetNet::SUPPLY0) || (sig->type() == NetNet::TRI0)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val.len() == 0); + ivl_assert(*sig, val.len() == 0); val = verinum(verinum::V0, width); } if ((sig->type() == NetNet::SUPPLY1) || (sig->type() == NetNet::TRI1)) { // Multiple drivers are not currently supported. - ivl_assert(*obj, val.len() == 0); + ivl_assert(*sig, val.len() == 0); val = verinum(verinum::V1, width); } } diff -Nru iverilog-10.3/load_module.cc iverilog-11.0/load_module.cc --- iverilog-10.3/load_module.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/load_module.cc 2020-09-26 22:44:25.000000000 +0000 @@ -73,7 +73,8 @@ if (cur == lcur->name_map.end()) continue; - sprintf(path, "%s%c%s", lcur->dir, dir_character, (*cur).second); + snprintf(path, sizeof(path), "%s%c%s", + lcur->dir, dir_character, (*cur).second); if(depend_file) { if (depfile_mode == 'p') { @@ -84,36 +85,10 @@ fflush(depend_file); } - if (ivlpp_string) { - char*cmdline = (char*)malloc(strlen(ivlpp_string) + - strlen(path) + 4); - strcpy(cmdline, ivlpp_string); - strcat(cmdline, " \""); - strcat(cmdline, path); - strcat(cmdline, "\""); - - if (verbose_flag) - cerr << "Executing: " << cmdline << endl<< flush; - - FILE*file = popen(cmdline, "r"); - - if (verbose_flag) - cerr << "...parsing output from preprocessor..." << endl << flush; - - parser_errors = pform_parse(path, file); - pclose(file); - free(cmdline); - - } else { - if (verbose_flag) - cerr << "Loading library file " - << path << "." << endl; - - FILE*file = fopen(path, "r"); - assert(file); - parser_errors = pform_parse(path, file); - fclose(file); - } + if (verbose_flag) + cerr << "Loading library file " << path << "." << endl; + + parser_errors = pform_parse(path); if (verbose_flag) cerr << "... Load module complete." << endl << flush; @@ -121,7 +96,6 @@ return parser_errors == 0; } - return false; } diff -Nru iverilog-10.3/macosx.txt iverilog-11.0/macosx.txt --- iverilog-10.3/macosx.txt 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/macosx.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ - -This file describes the procedure to build and install Icarus Verilog -on Mac OS X. I assume that you have experience with Unix and -Terminal.app and a basic knowledge of how to download, compile and -install software from source form. - -Yasuhisa Kato wrote another set of instructions that has also been -known to work: . You may try -those instructions instead of these, although they are essentially -quite similar. - -1) Obtain and install a libdl compatibility library. - - If you don't already have /usr/local/lib/libdl.{a,dylib} and - /usr/local/include/dlfcn.h, you can obtain the source for a - compatibility layer from at least one of two places: - - http://download.sourceforge.net/fink/dlcompat-20010831.tar.gz - http://www.omnigroup.com/~bungi/dlcompat-20010831.tar.gz - - Unpack this tar file and read the README and Makefile. Install the - library according to the instructions. Installation in /usr/local - is strongly recommended since otherwise autoconf very likely won't - be able to find it. - -2) Make sure you have a copy of the 'gperf' tool. This does not come - with the Mac OS X 10.1 developer tools, so you probably don't. You - can check with: - - % which gperf - - If not found, grab a gperf source package and install it. See "GPERF - FOR MACOSX" below. - - Snapshots of Icarus Verilog source now come with the - lexor_keyword.cc file pre-made, so if you have trouble with gperf, - then just make sure the distributed lexor_keyword.cc is newer than - lexor_keyword.gperf, and use that. - -3) If working with source from git, you must run autoconf in the top - directory. This is simplified by the 'autoconf.sh' script at the - top of the source tree: - - sh ./autoconf.sh - - This will also run the gperf command, so make sure you've completed - step #2 first. - -4) Configure, build and install the Icarus Verilog sources as normal. - - The only change you need to make here is to use a configure command like: - - % CC="cc -no-cpp-precomp" ./configure - - This assumes you are using 'sh', 'zsh', or 'bash'. If you are using - 'csh' or 'tcsh', then you'll want something like: - - % setenv CC "cc -no-cpp-precomp" - % ./configure - - You can, of course, add other configure options. - -6) NOTE: 'make check' will not work until after 'make install' has been run - since dynamically loaded code is searched for in the install location - rather than the build location. The dlopen emulation library doesn't - support a search path option. - - If you are worried about overwriting a working installation with a new, - potentially broken one, you can always configure using --prefix="/some/path", - and install there to make sure everything is working and then re-configure - with the real path you want to install at, make clean, and make install. - -5) Done! - - -GPERF FOR MACOSX - - Get version 2.7.2 of gperf from here: - - - - Get a MacosX patch from here: - - http://www.eternal.nest.or.jp/~shiro/binaries/gperf-2.7.2-macosx-patch.gz - - Apply the patch to the gperf-2.7.2 source that you previously - downloaded, then follow the remaining gperf installation - instructions. diff -Nru iverilog-10.3/main.cc iverilog-11.0/main.cc --- iverilog-10.3/main.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/main.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ const char COPYRIGHT[] = - "Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com)"; + "Copyright (c) 1998-2020 Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it @@ -68,7 +68,7 @@ extern "C" const char*optarg; #endif -#if defined(__CYGWIN32__) && !defined(HAVE_GETOPT_H) +#if defined(__CYGWIN__) && !defined(HAVE_GETOPT_H) extern "C" int getopt(int argc, char*argv[], const char*fmt); extern "C" int optind; extern "C" const char*optarg; @@ -104,11 +104,12 @@ bool gn_icarus_misc_flag = true; bool gn_cadence_types_flag = true; bool gn_specify_blocks_flag = true; -bool gn_assertions_flag = true; +bool gn_supported_assertions_flag = true; +bool gn_unsupported_assertions_flag = true; bool gn_io_range_error_flag = true; bool gn_strict_ca_eval_flag = false; bool gn_strict_expr_width_flag = false; -bool gn_shared_loop_index_flag = false; +bool gn_shared_loop_index_flag = true; bool gn_verilog_ams_flag = false; /* @@ -135,11 +136,14 @@ vpi_module_list = tmp; } flags["VPI_MODULE_LIST"] = vpi_module_list; + load_vpi_module(name); } map missing_modules; map library_file_map; +vector source_files; + list library_suff; list roots; @@ -162,6 +166,12 @@ bool warn_sens_entire_vec = false; bool warn_sens_entire_arr = false; bool warn_anachronisms = false; +bool warn_floating_nets = false; + +/* + * Ignore errors about missing modules + */ +bool ignore_missing_modules = false; /* * Debug message class flags. @@ -174,6 +184,11 @@ bool debug_optimizer = false; /* + * Compilation control flags. + */ +bool separate_compilation = false; + +/* * Optimization control flags. */ unsigned opt_const_func = 0; @@ -226,6 +241,7 @@ bool synthesis = false; extern void cprop(Design*des); +extern void exposenodes(Design*des); extern void synth(Design*des); extern void synth2(Design*des); extern void syn_rules(Design*des); @@ -236,10 +252,11 @@ const char*name; void (*func)(Design*); } func_table[] = { - { "cprop", &cprop }, - { "nodangle",&nodangle }, - { "synth", &synth }, - { "synth2", &synth2 }, + { "cprop", &cprop }, + { "exposenodes", &exposenodes }, + { "nodangle", &nodangle }, + { "synth", &synth }, + { "synth2", &synth2 }, { "syn-rules", &syn_rules }, { 0, 0 } }; @@ -316,10 +333,16 @@ gn_specify_blocks_flag = false; } else if (strcmp(gen,"assertions") == 0) { - gn_assertions_flag = true; + gn_supported_assertions_flag = true; + gn_unsupported_assertions_flag = true; + + } else if (strcmp(gen,"supported-assertions") == 0) { + gn_supported_assertions_flag = true; + gn_unsupported_assertions_flag = false; } else if (strcmp(gen,"no-assertions") == 0) { - gn_assertions_flag = false; + gn_supported_assertions_flag = false; + gn_unsupported_assertions_flag = false; } else if (strcmp(gen,"verilog-ams") == 0) { gn_verilog_ams_flag = true; @@ -567,11 +590,15 @@ * * warnings: * Warning flag letters. + * + * ignore_missing_modules: + * true to ignore errors about missing modules */ bool had_timescale = false; static void read_iconfig_file(const char*ipath) { char buf[8*1024]; + vector > to_build_library_index; FILE*ifile = fopen(ipath, "r"); if (ifile == 0) { @@ -580,6 +607,14 @@ } while (fgets(buf, sizeof buf, ifile) != 0) { + assert(strlen(buf) < sizeof buf); + if ((strlen(buf) == ((sizeof buf) - 1)) + && buf[sizeof buf -2] != '\n') { + cerr << "WARNING: Line buffer overflow reading iconfig file: " + << ipath + << "." << endl; + assert(0); + } if (buf[0] == '#') continue; char*cp = strchr(buf, ':'); @@ -683,6 +718,9 @@ } else if (strcmp(buf,"warnings") == 0) { /* Scan the warnings enable string for warning flags. */ for ( ; *cp ; cp += 1) switch (*cp) { + case 'f': + warn_floating_nets = true; + break; case 'i': warn_implicit = true; break; @@ -714,11 +752,15 @@ break; } + } else if (strcmp(buf, "ignore_missing_modules") == 0) { + if (strcmp(cp, "true") == 0) + ignore_missing_modules = true; + } else if (strcmp(buf, "-y") == 0) { - build_library_index(cp, CASE_SENSITIVE); + to_build_library_index.push_back(make_pair(strdup(cp), CASE_SENSITIVE)); } else if (strcmp(buf, "-yl") == 0) { - build_library_index(cp, false); + to_build_library_index.push_back(make_pair(strdup(cp), false)); } else if (strcmp(buf, "-Y") == 0) { library_suff.push_back(strdup(cp)); @@ -757,6 +799,54 @@ } } fclose(ifile); + for (vector >::iterator it = to_build_library_index.begin() ; + it != to_build_library_index.end() ; ++ it ) { + build_library_index(it->first, it->second); + free(it->first); + } +} + +/* + * This function reads a list of source file names. Each name starts + * with the first non-space character, and ends with the last non-space + * character. Spaces in the middle are OK. + */ +static void read_sources_file(const char*path) +{ + char line_buf[2048]; + + FILE*fd = fopen(path, "r"); + if (fd == 0) { + cerr << "ERROR: Unable to read source file list: " << path << endl; + return; + } + + while (fgets(line_buf, sizeof line_buf, fd) != 0) { + // assertion test that we are not overflowing the line + // buffer. Really should make this more robust, but + // better to assert then go weird. + assert(strlen(line_buf) < sizeof line_buf); + if ((strlen(line_buf) == ((sizeof line_buf) - 1)) + && line_buf[sizeof line_buf -2] != '\n') { + cerr << "WARNING: Line buffer overflow reading sources file: " + << path + << "." << endl; + assert(0); + } + char*cp = line_buf + strspn(line_buf, " \t\r\b\f"); + char*tail = cp + strlen(cp); + while (tail > cp) { + if (! isspace((int)tail[-1])) + break; + tail -= 1; + tail[0] = 0; + } + + if (cp < tail) + source_files.push_back(filename_strings.make(cp)); + } + + fclose(fd); } extern Design* elaborate(list root); @@ -836,20 +926,18 @@ } library_suff.push_back(strdup(".v")); - // Start the module list with the base system module. - add_vpi_module("system"); - add_vpi_module("vhdl_sys"); - flags["-o"] = strdup("a.out"); min_typ_max_flag = TYP; min_typ_max_warn = 10; - while ((opt = getopt(argc, argv, "C:f:hN:P:p:Vv")) != EOF) switch (opt) { + while ((opt = getopt(argc, argv, "C:F:f:hN:P:p:Vv")) != EOF) switch (opt) { case 'C': read_iconfig_file(optarg); break; - + case 'F': + read_sources_file(optarg); + break; case 'f': parm_to_flagmap(optarg); break; @@ -902,6 +990,7 @@ "usage: ivl \n" "options:\n" "\t-C Config file from driver.\n" +"\t-F List of source files from driver.\n" "\t-h Print usage information, and exit.\n" "\t-N Dump the elaborated netlist to .\n" "\t-P Write the parsed input to .\n" @@ -917,11 +1006,19 @@ return 0; } - if (optind == argc) { + int arg = optind; + while (arg < argc) { + perm_string path = filename_strings.make(argv[arg++]); + source_files.push_back(path); + } + + if (source_files.empty()) { cerr << "No input files." << endl; return 1; } + separate_compilation = source_files.size() > 1; + if( depfile_name ) { depend_file = fopen(depfile_name, "a"); if(! depend_file) { @@ -1022,8 +1119,10 @@ if (flag_tmp) disable_concatz_generation = strcmp(flag_tmp,"true")==0; /* Parse the input. Make the pform. */ - pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); - int rc = pform_parse(argv[optind]); + int rc = 0; + for (unsigned idx = 0; idx < source_files.size(); idx += 1) { + rc += pform_parse(source_files[idx]); + } if (pf_path) { ofstream out (pf_path); @@ -1037,21 +1136,16 @@ ; cur != disciplines.end() ; ++ cur ) { pform_dump(out, (*cur).second); } - out << "PFORM DUMP $ROOT TASKS/FUNCTIONS:" << endl; - for (map::iterator cur = pform_tasks.begin() - ; cur != pform_tasks.end() ; ++ cur) { - pform_dump(out, cur->second); - } - out << "PFORM DUMP $ROOT CLASSES:" << endl; - for (size_t idx = 0 ; idx < pform_classes.size() ; idx += 1) { - pform_dump(out, pform_classes[idx]); + out << "PFORM DUMP COMPILATION UNITS:" << endl; + for (vector::iterator pac = pform_units.begin() + ; pac != pform_units.end() ; ++ pac) { + pform_dump(out, *pac); } out << "PFORM DUMP PACKAGES:" << endl; for (map::iterator pac = pform_packages.begin() ; pac != pform_packages.end() ; ++ pac) { pform_dump(out, pac->second); } - out << "PFORM DUMP MODULES:" << endl; for (map::iterator mod = pform_modules.begin() ; mod != pform_modules.end() ; ++ mod ) { @@ -1108,7 +1202,7 @@ if (roots.empty()) { cerr << "No top level modules, and no -s option." << endl; - return 1; + return ignore_missing_modules ? 0 : 1; } @@ -1169,12 +1263,6 @@ (*idx).second = 0; } - for(map::iterator it = pform_typedefs.begin() - ; it != pform_typedefs.end() ; ++it) { - delete (*it).second; - (*it).second = 0; - } - if (verbose_flag) { if (times_flag) { times(cycles+2); diff -Nru iverilog-10.3/Makefile.in iverilog-11.0/Makefile.in --- iverilog-10.3/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -70,7 +70,8 @@ HOSTCC = @CC@ HOSTCFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ -CC = @CC@ +BUILDCC = @CC_FOR_BUILD@ +BUILDEXT = @BUILD_EXEEXT@ CXX = @CXX@ DLLTOOL = @DLLTOOL@ INSTALL = @INSTALL@ @@ -100,7 +101,7 @@ M = LineInfo.o StringHeap.o TT = t-dll.o t-dll-api.o t-dll-expr.o t-dll-proc.o t-dll-analog.o -FF = cprop.o nodangle.o synth.o synth2.o syn-rules.o +FF = cprop.o exposenodes.o nodangle.o synth.o synth2.o syn-rules.o O = main.o async.o design_dump.o discipline.o dup_expr.o elaborate.o \ elab_expr.o elaborate_analog.o elab_lval.o elab_net.o \ @@ -116,10 +117,10 @@ 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 \ - Attrib.o HName.o Module.o PClass.o PDelays.o PEvent.o PExpr.o PGate.o \ - PGenerate.o PModport.o PPackage.o PScope.o PSpec.o PTask.o PUdp.o \ - PFunction.o PWire.o Statement.o AStatement.o $M $(FF) $(TT) + 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) all: dep config.h _pli_types.h version_tag.h ivl@EXEEXT@ version.exe iverilog-vpi.man $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true @@ -127,9 +128,9 @@ # In the windows world, the installer will need a dosify program to # dosify text files. ifeq (@MINGW32@,yes) -all: dosify.exe -dosify.exe: $(srcdir)/dosify.c - $(HOSTCC) $(HOSTCFLAGS) -o dosify.exe $(srcdir)/dosify.c +all: dosify$(BUILDEXT) +dosify$(BUILDEXT): $(srcdir)/dosify.c + $(BUILDCC) $(CFLAGS) -o dosify$(BUILDEXT) $(srcdir)/dosify.c endif # This rule rules the compiler in the trivial hello.vl program to make @@ -137,7 +138,7 @@ check: all $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true test -r check.conf || cp $(srcdir)/check.conf . - driver/iverilog -B. -BPivlpp -tcheck -ocheck.vvp $(srcdir)/examples/hello.vl + driver/iverilog -B. -BMvpi -BPivlpp -tcheck -ocheck.vvp $(srcdir)/examples/hello.vl ifeq (@WIN32@,yes) ifeq (@install_suffix@,) vvp/vvp -M- -M./vpi ./check.vvp | grep 'Hello, World' @@ -156,7 +157,7 @@ $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true rm -f *.o parse.cc parse.h lexor.cc rm -f ivl.exp iverilog-vpi.man iverilog-vpi.pdf iverilog-vpi.ps - rm -f parse.output syn-rules.output dosify.exe ivl@EXEEXT@ check.vvp + rm -f parse.output syn-rules.output dosify$(BUILDEXT) ivl@EXEEXT@ check.vvp rm -f lexor_keyword.cc libivl.a libvpi.a iverilog-vpi syn-rules.cc rm -rf dep rm -f version.exe @@ -174,7 +175,8 @@ rm -rf autom4te.cache cppcheck: $(O:.o=.cc) $(srcdir)/dosify.c $(srcdir)/version.c - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ @@ -239,7 +241,7 @@ endif version.exe: $(srcdir)/version.c $(srcdir)/version_base.h version_tag.h - $(HOSTCC) $(HOSTCFLAGS) -o version.exe -I. -I$(srcdir) $(srcdir)/version.c + $(BUILDCC) $(CFLAGS) -o version.exe -I. -I$(srcdir) $(srcdir)/version.c %.o: %.cc config.h $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o @@ -252,12 +254,9 @@ parse.o: parse.cc -# Build this in two steps to avoid parallel build issues (see pr3462585) -parse.cc: $(srcdir)/parse.y - $(YACC) --verbose -t -p VL -d -o $@ $< -parse.h: parse.cc - mv parse.cc.h $@ 2>/dev/null || mv parse.hh $@ - touch $@ +# Use pattern rules to avoid parallel build issues (see pr3462585) +parse%cc parse%h: $(srcdir)/parse%y + $(YACC) --verbose -t -p VL --defines=parse.h -o parse.cc $< syn-rules.cc: $(srcdir)/syn-rules.y $(YACC) --verbose -t -p syn_ -o $@ $< @@ -306,67 +305,62 @@ ifeq (@MINGW32@,yes) ifeq ($(MAN),none) -INSTALL_DOC = $(mandir)/man1/iverilog-vpi$(suffix).1 +INSTALL_DOC = installman else ifeq ($(PS2PDF),none) -INSTALL_DOC = $(mandir)/man1/iverilog-vpi$(suffix).1 +INSTALL_DOC = installman else -INSTALL_DOC = $(prefix)/iverilog-vpi$(suffix).pdf $(mandir)/man1/iverilog-vpi$(suffix).1 +INSTALL_DOC = installpdf installman all: dep iverilog-vpi.pdf endif endif INSTALL_DOCDIR = $(mandir)/man1 else -INSTALL_DOC = $(mandir)/man1/iverilog-vpi$(suffix).1 +INSTALL_DOC = installman INSTALL_DOCDIR = $(mandir)/man1 endif ifeq (@MINGW32@,yes) WIN32_INSTALL = else -WIN32_INSTALL = $(bindir)/iverilog-vpi$(suffix) +WIN32_INSTALL = installwin32 endif -install: all installdirs $(libdir)/ivl$(suffix)/ivl@EXEEXT@ $(libdir)/ivl$(suffix)/include/constants.vams $(libdir)/ivl$(suffix)/include/disciplines.vams $(includedir)/ivl_target.h $(includedir)/_pli_types.h $(includedir)/sv_vpi_user.h $(includedir)/vpi_user.h $(includedir)/acc_user.h $(includedir)/veriuser.h $(WIN32_INSTALL) $(INSTALL_DOC) +install: all installdirs installfiles $(foreach dir,$(SUBDIRS),$(MAKE) -C $(dir) $@ && ) true -$(bindir)/iverilog-vpi$(suffix): ./iverilog-vpi +F = ./ivl@EXEEXT@ \ + $(srcdir)/constants.vams \ + $(srcdir)/disciplines.vams \ + $(srcdir)/ivl_target.h \ + ./_pli_types.h \ + $(srcdir)/sv_vpi_user.h \ + $(srcdir)/vpi_user.h \ + $(srcdir)/acc_user.h \ + $(srcdir)/veriuser.h \ + $(INSTALL_DOC) \ + $(WIN32_INSTALL) + +installwin32: ./iverilog-vpi installdirs $(INSTALL_SCRIPT) ./iverilog-vpi "$(DESTDIR)$(bindir)/iverilog-vpi$(suffix)" -$(libdir)/ivl$(suffix)/ivl@EXEEXT@: ./ivl@EXEEXT@ - $(INSTALL_PROGRAM) ./ivl@EXEEXT@ "$(DESTDIR)$(libdir)/ivl$(suffix)/ivl@EXEEXT@" +installman: iverilog-vpi.man installdirs + $(INSTALL_DATA) iverilog-vpi.man "$(DESTDIR)$(mandir)/man1/iverilog-vpi$(suffix).1" -$(libdir)/ivl$(suffix)/include/constants.vams: $(srcdir)/constants.vams - $(INSTALL_DATA) $(srcdir)/constants.vams "$(DESTDIR)$(libdir)/ivl$(suffix)/include/constants.vams" +installpdf: iverilog-vpi.pdf installdirs + $(INSTALL_DATA) iverilog-vpi.pdf "$(DESTDIR)$(prefix)/iverilog-vpi$(suffix).pdf" -$(libdir)/ivl$(suffix)/include/disciplines.vams: $(srcdir)/disciplines.vams +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./ivl@EXEEXT@ "$(DESTDIR)$(libdir)/ivl$(suffix)/ivl@EXEEXT@" + $(INSTALL_DATA) $(srcdir)/constants.vams "$(DESTDIR)$(libdir)/ivl$(suffix)/include/constants.vams" $(INSTALL_DATA) $(srcdir)/disciplines.vams "$(DESTDIR)$(libdir)/ivl$(suffix)/include/disciplines.vams" - -$(includedir)/ivl_target.h: $(srcdir)/ivl_target.h $(INSTALL_DATA) $(srcdir)/ivl_target.h "$(DESTDIR)$(includedir)/ivl_target.h" - -$(includedir)/_pli_types.h: _pli_types.h - $(INSTALL_DATA) $< "$(DESTDIR)$(includedir)/_pli_types.h" - -$(includedir)/sv_vpi_user.h: $(srcdir)/sv_vpi_user.h + $(INSTALL_DATA) ./_pli_types.h "$(DESTDIR)$(includedir)/_pli_types.h" $(INSTALL_DATA) $(srcdir)/sv_vpi_user.h "$(DESTDIR)$(includedir)/sv_vpi_user.h" - -$(includedir)/vpi_user.h: $(srcdir)/vpi_user.h $(INSTALL_DATA) $(srcdir)/vpi_user.h "$(DESTDIR)$(includedir)/vpi_user.h" - -$(includedir)/acc_user.h: $(srcdir)/acc_user.h $(INSTALL_DATA) $(srcdir)/acc_user.h "$(DESTDIR)$(includedir)/acc_user.h" - -$(includedir)/veriuser.h: $(srcdir)/veriuser.h $(INSTALL_DATA) $(srcdir)/veriuser.h "$(DESTDIR)$(includedir)/veriuser.h" -$(mandir)/man1/iverilog-vpi$(suffix).1: iverilog-vpi.man - $(INSTALL_DATA) iverilog-vpi.man "$(DESTDIR)$(mandir)/man1/iverilog-vpi$(suffix).1" - -$(prefix)/iverilog-vpi$(suffix).pdf: iverilog-vpi.pdf - $(INSTALL_DATA) iverilog-vpi.pdf "$(DESTDIR)$(prefix)/iverilog-vpi$(suffix).pdf" - - installdirs: $(srcdir)/mkinstalldirs $(srcdir)/mkinstalldirs "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(includedir)" \ diff -Nru iverilog-10.3/mingw-cross.txt iverilog-11.0/mingw-cross.txt --- iverilog-10.3/mingw-cross.txt 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/mingw-cross.txt 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,27 @@ + +These are instructions for building Icarus Verilog binaries for +Windows using mingw cross compiler tools on Linux. + +To start with, you need the mingw64-cross-* packages for your linux +distribution, which gives you the x86_64-w64-mingw32-* commands +installed on your system. Installing the cross environment is outside +the scope of this writeup. + +First, configure with this command: + + $ ./configure --host=x86_64-w64-mingw32 + +This generates the Makefiles needed to cross compile everything with +the mingw32 compiler. The configure script will generate the command +name paths, so long as commands line x86_64-w64-mingw32-gcc +et. al. are in your path. + +Next, compile with the command: + + $ make + +The configure generated the cross compiler flags, but there are a few +bits that need to be compiled with the native compiler. (version.exe +for example is used by the build process but is not installed.) The +configure script should have gotten all that right. + diff -Nru iverilog-10.3/Module.cc iverilog-11.0/Module.cc --- iverilog-10.3/Module.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/Module.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -32,9 +32,9 @@ { library_flag = false; is_cell = false; + is_interface = false; program_block = false; uc_drive = UCD_NONE; - timescale_warn_done = false; } Module::~Module() @@ -122,3 +122,13 @@ { return gates_; } + +PNamedItem::SymbolType Module::symbol_type() const +{ + if (program_block) + return PROGRAM; + if (is_interface) + return INTERFACE; + + return MODULE; +} diff -Nru iverilog-10.3/Module.h iverilog-11.0/Module.h --- iverilog-10.3/Module.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/Module.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_Module_H #define IVL_Module_H /* - * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -28,7 +28,7 @@ # include "HName.h" # include "named.h" # include "PScope.h" -# include "LineInfo.h" +# include "PNamedItem.h" # include "netlist.h" # include "pform_types.h" class PExpr; @@ -54,7 +54,7 @@ * these containers as well. */ -class Module : public PScopeExtra, public LineInfo { +class Module : public PScopeExtra, public PNamedItem { /* The module ports are in general a vector of port_t objects. Each port has a name and an ordered list of @@ -97,7 +97,7 @@ /* specparams are simpler than other parameters, in that they can have a range, but not an explicit type. The restrictions are enforced by the parser. */ - mapspecparams; + mapspecparams; /* The module also has defparam assignments which don't create new parameters within the module, but may be used to set @@ -121,8 +121,6 @@ map attributes; - bool timescale_warn_done; - /* The module has a list of generate schemes that appear in the module definition. These are used at elaboration time. */ list generate_schemes; @@ -162,6 +160,8 @@ bool elaborate_sig(Design*, NetScope*scope) const; + SymbolType symbol_type() const; + private: void dump_specparams_(ostream&out, unsigned indent) const; list gates_; diff -Nru iverilog-10.3/net_assign.cc iverilog-11.0/net_assign.cc --- iverilog-10.3/net_assign.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_assign.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2015 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 @@ -42,6 +42,7 @@ NetAssign_::NetAssign_(NetAssign_*n) : nest_(n), sig_(0), word_(0), base_(0), sel_type_(IVL_SEL_OTHER) { + lwid_ = 0; more = 0; signed_ = false; turn_sig_to_wire_on_release_ = false; diff -Nru iverilog-10.3/netdarray.cc iverilog-11.0/netdarray.cc --- iverilog-10.3/netdarray.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/netdarray.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2020 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 @@ -18,6 +18,7 @@ */ # include "netdarray.h" +# include "netqueue.h" # include using namespace std; @@ -38,9 +39,16 @@ bool netdarray_t::test_compatibility(ivl_type_t that) const { - const netdarray_t*that_da = dynamic_cast(that); - if (that_da == 0) + ivl_type_t elem_type = 0; + + if (const netdarray_t*that_da = dynamic_cast(that)) + elem_type = that_da->element_type(); + + if (const netqueue_t*that_q = dynamic_cast(that)) + elem_type = that_q->element_type(); + + if (elem_type == 0) return false; - return element_type()->type_compatible(that_da->element_type()); + return element_type()->type_compatible(elem_type); } diff -Nru iverilog-10.3/net_design.cc iverilog-11.0/net_design.cc --- iverilog-10.3/net_design.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_design.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2019 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 @@ -101,11 +101,11 @@ return val; } -NetScope* Design::make_root_scope(perm_string root, bool program_block, - bool is_interface) +NetScope* Design::make_root_scope(perm_string root, NetScope*unit_scope, + bool program_block, bool is_interface) { NetScope *root_scope_; - root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE, + root_scope_ = new NetScope(0, hname_t(root), NetScope::MODULE, unit_scope, false, program_block, is_interface); /* This relies on the fact that the basename return value is permallocated. */ @@ -125,31 +125,18 @@ return root_scopes_; } -NetScope* Design::make_package_scope(perm_string name) +NetScope* Design::make_package_scope(perm_string name, NetScope*unit_scope, + bool is_unit) { NetScope*scope; - scope = new NetScope(0, hname_t(name), NetScope::PACKAGE, false, false); + scope = new NetScope(0, hname_t(name), NetScope::PACKAGE, unit_scope, + false, false, false, is_unit); scope->set_module_name(scope->basename()); packages_[name] = scope; return scope; } -void Design::add_class(netclass_t*cl, PClass*pclass) -{ - Definitions::add_class(cl); - class_to_pclass_[cl] = pclass; -} - -netclass_t* Design::find_class(perm_string name) const -{ - map::const_iterator cur = classes_.find(name); - if (cur != classes_.end()) - return cur->second; - - return 0; -} - NetScope* Design::find_package(perm_string name) const { map::const_iterator cur = packages_.find(name); @@ -170,17 +157,6 @@ return res; } -list Design::find_roottask_scopes() const -{ - listres; - for (map::const_iterator cur = root_tasks_.begin() - ; cur != root_tasks_.end() ; ++ cur) { - res.push_back (cur->first); - } - - return res; -} - /* * This method locates a scope in the design, given its rooted * hierarchical name. Each component of the key is used to scan one @@ -211,25 +187,6 @@ } } - for (map::const_iterator root = root_tasks_.begin() - ; root != root_tasks_.end() ; ++ root) { - - NetScope*cur = root->first; - if (path.front() != cur->fullname()) - continue; - - std::list tmp = path; - tmp.pop_front(); - - while (cur) { - if (tmp.empty()) return cur; - - cur = cur->child( tmp.front() ); - - tmp.pop_front(); - } - } - return 0; } @@ -253,6 +210,55 @@ return 0; } +static bool is_design_unit(NetScope*scope) +{ + return (scope->type() == NetScope::MODULE && !scope->nested_module()) + || (scope->type() == NetScope::PACKAGE); +} + +static bool is_subroutine(NetScope::TYPE type) +{ + return type == NetScope::TASK || type == NetScope::FUNC; +} + +/* + * This method locates a scope within another scope, given its relative + * hierarchical name. Each component of the key is used to scan one + * more step down the tree until the name runs out or the search + * fails. + */ +NetScope* Design::find_scope_(NetScope*scope, const std::list&path, + NetScope::TYPE type) const +{ + std::list tmp = path; + + do { + hname_t key = tmp.front(); + /* If we are looking for a module or we are not + * looking at the last path component check for + * a name match (second line). */ + if (scope->type() == NetScope::MODULE + && (type == NetScope::MODULE || tmp.size() > 1) + && scope->module_name()==key.peek_name()) { + + /* Up references may match module name */ + + } else { + NetScope*found_scope = scope->child(key); + if (found_scope == 0) { + found_scope = scope->find_import(this, key.peek_name()); + if (found_scope) + found_scope = found_scope->child(key); + } + scope = found_scope; + if (scope == 0) break; + } + tmp.pop_front(); + } while (! tmp.empty()); + + return scope; +} + /* * This is a relative lookup of a scope by name. The starting point is * the scope parameter within which I start looking for the scope. If @@ -266,30 +272,36 @@ if (path.empty()) return scope; - for ( ; scope ; scope = scope->parent()) { + // Record the compilation unit scope for use later. + NetScope*unit_scope = scope->unit(); - std::list tmp = path; + // First search upwards through the hierarchy. + while (scope) { + NetScope*found_scope = find_scope_(scope, path, type); + if (found_scope) + return found_scope; + + // Avoid searching the unit scope twice. + if (scope == unit_scope) + unit_scope = 0; + + // Special case - see IEEE 1800-2012 section 23.8.1. + if (unit_scope && is_design_unit(scope) && is_subroutine(type)) { + found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; - NetScope*cur = scope; - do { - hname_t key = tmp.front(); - /* If we are looking for a module or we are not - * looking at the last path component check for - * a name match (second line). */ - if (cur->type() == NetScope::MODULE - && (type == NetScope::MODULE || tmp.size() > 1) - && cur->module_name()==key.peek_name()) { - - /* Up references may match module name */ - - } else { - cur = cur->child( key ); - if (cur == 0) break; - } - tmp.pop_front(); - } while (! tmp.empty()); + unit_scope = 0; + } + + scope = scope->parent(); + } - if (cur) return cur; + // If we haven't already done so, search the compilation unit scope. + if (unit_scope) { + NetScope*found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; } // Last chance. Look for the name starting at the root. @@ -297,6 +309,32 @@ } /* + * This method locates a scope within another scope, given its relative + * hierarchical name. Each component of the key is used to scan one + * more step down the tree until the name runs out or the search + * fails. + */ +NetScope* Design::find_scope_(NetScope*scope, const hname_t&path, + NetScope::TYPE type) const +{ + /* If we are looking for a module or we are not + * looking at the last path component check for + * a name match (second line). */ + if ((scope->type() == NetScope::MODULE) && (type == NetScope::MODULE) + && (scope->module_name() == path.peek_name())) { + /* Up references may match module name */ + return scope; + } + NetScope*found_scope = scope->child(path); + if (found_scope == 0) { + found_scope = scope->find_import(this, path.peek_name()); + if (found_scope) + found_scope = found_scope->child(path); + } + return found_scope; +} + +/* * This is a relative lookup of a scope by name. The starting point is * the scope parameter within which I start looking for the scope. If * I do not find the scope within the passed scope, start looking in @@ -307,24 +345,36 @@ { assert(scope); - for ( ; scope ; scope = scope->parent()) { - - NetScope*cur = scope; + // Record the compilation unit scope for use later. + NetScope*unit_scope = scope->unit(); - /* If we are looking for a module or we are not - * looking at the last path component check for - * a name match (second line). */ - if (cur->type() == NetScope::MODULE - && (type == NetScope::MODULE) - && cur->module_name()==path.peek_name()) { - - /* Up references may match module name */ + // First search upwards through the hierarchy. + while (scope) { + NetScope*found_scope = find_scope_(scope, path, type); + if (found_scope) + return found_scope; + + // Avoid searching the unit scope twice. + if (scope == unit_scope) + unit_scope = 0; + + // Special case - see IEEE 1800-2012 section 23.8.1. + if (unit_scope && is_design_unit(scope) && is_subroutine(type)) { + found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; - } else { - cur = cur->child( path ); + unit_scope = 0; } - if (cur) return cur; + scope = scope->parent(); + } + + // If we haven't already done so, search the compilation unit scope. + if (unit_scope) { + NetScope*found_scope = find_scope_(unit_scope, path, type); + if (found_scope) + return found_scope; } // Last chance. Look for the name starting at the root. @@ -826,6 +876,11 @@ if (NetNet*net = scope->find_signal(key)) return net; + if (NetScope*import_scope = scope->find_import(this, key)) { + scope = import_scope; + continue; + } + if (scope->type() == NetScope::MODULE) break; @@ -867,11 +922,6 @@ return 0; } -void Design::add_root_task(NetScope*tscope, PTaskFunc*tf) -{ - root_tasks_[tscope] = tf; -} - void Design::add_node(NetNode*net) { assert(net->design_ == 0); @@ -890,8 +940,8 @@ void Design::del_node(NetNode*net) { - assert(net->design_ == this); assert(net != 0); + assert(net->design_ == this); /* Interact with the Design::functor method by manipulating the cur and nxt pointers that it is using. */ diff -Nru iverilog-10.3/net_event.cc iverilog-11.0/net_event.cc --- iverilog-10.3/net_event.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_event.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -28,6 +28,7 @@ NetEvent::NetEvent(perm_string n) : name_(n) { + local_flag_ = false; scope_ = 0; snext_ = 0; probes_ = 0; @@ -345,7 +346,7 @@ } NetEvWait::NetEvWait(NetProc*pr) -: statement_(pr) +: statement_(pr), has_t0_trigger_(false) { } @@ -369,6 +370,7 @@ tmp->next = tmp->next->next; delete tmp; } + delete tgt; } events_.clear(); } @@ -441,3 +443,8 @@ { return statement_; } + +const NetProc* NetEvWait::statement() const +{ + return statement_; +} diff -Nru iverilog-10.3/net_expr.cc iverilog-11.0/net_expr.cc --- iverilog-10.3/net_expr.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_expr.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2019 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 @@ -461,15 +461,15 @@ } NetESFunc::NetESFunc(const char*n, ivl_variable_type_t t, - unsigned width, unsigned np) -: name_(0), type_(t), enum_type_(0), parms_(np) + unsigned width, unsigned np, bool is_overridden) +: name_(0), type_(t), enum_type_(0), parms_(np), is_overridden_(is_overridden) { name_ = lex_strings.add(n); expr_width(width); } NetESFunc::NetESFunc(const char*n, ivl_type_t rtype, unsigned np) -: NetExpr(rtype), name_(0), type_(IVL_VT_NO_TYPE), enum_type_(0), parms_(np) +: NetExpr(rtype), name_(0), type_(IVL_VT_NO_TYPE), enum_type_(0), parms_(np), is_overridden_(false) { name_ = lex_strings.add(n); expr_width(rtype->packed_width()); @@ -485,7 +485,7 @@ } NetESFunc::NetESFunc(const char*n, const netenum_t*enum_type, unsigned np) -: name_(0), type_(enum_type->base_type()), enum_type_(enum_type), parms_(np) +: name_(0), type_(enum_type->base_type()), enum_type_(enum_type), parms_(np), is_overridden_(false) { name_ = lex_strings.add(n); expr_width(enum_type->packed_width()); diff -Nru iverilog-10.3/net_link.cc iverilog-11.0/net_link.cc --- iverilog-10.3/net_link.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_link.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2016 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 @@ -479,6 +479,17 @@ } return 0; +} + +NetNode* Nexus::pick_any_node() +{ + for (Link*cur = first_nlink() ; cur ; cur = cur->next_nlink()) { + NetNode*node = dynamic_cast(cur->get_obj()); + if (node != 0) + return node; + } + + return 0; } const char* Nexus::name() const diff -Nru iverilog-10.3/netlist.cc iverilog-11.0/netlist.cc --- iverilog-10.3/netlist.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/netlist.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 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 @@ -24,6 +24,7 @@ # include # include # include +# include # include "compiler.h" # include "netlist.h" # include "netmisc.h" @@ -175,6 +176,7 @@ NetPins::NetPins(unsigned npins) : npins_(npins) { + default_dir_ = Link::PASSIVE; pins_ = NULL; // Wait until someone asks. if (disable_virtual_pins) devirtualize_pins(); // Ask. Bummer. } @@ -307,11 +309,12 @@ } NetDelaySrc::NetDelaySrc(NetScope*s, perm_string n, unsigned npins, - bool condit_src, bool conditional) + bool condit_src, bool conditional, bool parallel) : NetObj(s, n, npins + (condit_src?1:0)) { condit_flag_ = false; conditional_ = conditional; + parallel_ = parallel; posedge_ = false; negedge_ = false; for (unsigned idx = 0 ; idx < npins ; idx += 1) { @@ -470,6 +473,11 @@ return pin(pin_count()-1); } +bool NetDelaySrc::is_parallel() const +{ + return parallel_; +} + PortType::Enum PortType::merged( Enum lhs, Enum rhs ) { if( lhs == NOT_A_PORT || rhs == NOT_A_PORT ) @@ -557,7 +565,7 @@ ivl_assert(*this, ! slice_wids_.empty()); slice_wids_[0] = netrange_width(slice_dims_); vector::const_iterator cur = slice_dims_.begin(); - for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1, cur++) { + for (size_t idx = 1 ; idx < slice_wids_.size() ; idx += 1, ++cur) { slice_wids_[idx] = slice_wids_[idx-1] / cur->width(); } } @@ -1021,11 +1029,36 @@ NetProcTop::NetProcTop(NetScope*s, ivl_process_type_t t, NetProc*st) : type_(t), statement_(st), scope_(s) { + synthesized_design_ = 0; } NetProcTop::~NetProcTop() { + if (!synthesized_design_) { + delete statement_; + return; + } + + NexusSet nex_set; + statement_->nex_output(nex_set); + delete statement_; + + bool flag = false; + for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { + + NetNet*net = nex_set[idx].lnk.nexus()->pick_any_net(); + if (net->peek_lref() > 0) { + cerr << get_fileline() << ": warning: '" << net->name() + << "' is driven by more than one process." << endl; + flag = true; + } + } + if (flag) { + cerr << get_fileline() << ": sorry: Cannot synthesize signals " + "that are driven by more than one process." << endl; + synthesized_design_->errors += 1; + } } NetProc* NetProcTop::statement() @@ -1280,6 +1313,60 @@ return sset_value_; } +/* + * The NetLatch class represents an LPM_LATCH device. The pinout is assigned + * like so: + * 0 -- Enable + * 1 -- Data + * 2 -- Q + */ + +NetLatch::NetLatch(NetScope*s, perm_string n, unsigned width__) +: NetNode(s, n, 3), width_(width__) +{ + pin_Enable().set_dir(Link::INPUT); + pin_Data().set_dir(Link::INPUT); + pin_Q().set_dir(Link::OUTPUT); +} + +NetLatch::~NetLatch() +{ +} + +unsigned NetLatch::width() const +{ + return width_; +} + +Link& NetLatch::pin_Enable() +{ + return pin(0); +} + +const Link& NetLatch::pin_Enable() const +{ + return pin(0); +} + +Link& NetLatch::pin_Data() +{ + return pin(1); +} + +const Link& NetLatch::pin_Data() const +{ + return pin(1); +} + +Link& NetLatch::pin_Q() +{ + return pin(2); +} + +const Link& NetLatch::pin_Q() const +{ + return pin(2); +} NetAbs::NetAbs(NetScope*s, perm_string n, unsigned w) : NetNode(s, n, 2), width_(w) @@ -2154,6 +2241,14 @@ return IVL_VT_VOID; } +const netenum_t* NetEUFunc::enumeration() const +{ + if (result_sig_) + return result_sig_->enumeration(); + + return 0; +} + NetUTask::NetUTask(NetScope*def) : task_(def) { @@ -2443,6 +2538,20 @@ delete false_val_; } +const netenum_t* NetETernary::enumeration() const +{ + // If the condition can evaluate to an ambiguous value, + // the result may be blended, and so is not guaranteed + // to be a valid enumeration value. + if (cond_->expr_type() != IVL_VT_BOOL) + return 0; + + if (true_val_->enumeration() != false_val_->enumeration()) + return 0; + + return true_val_->enumeration(); +} + const NetExpr* NetETernary::cond_expr() const { return cond_; @@ -2688,7 +2797,7 @@ * The looping structures can use the same basic code so put it here * instead of duplicating it for each one (repeat and while). */ -static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc) +static DelayType get_loop_delay_type(const NetExpr*expr, const NetProc*proc, bool print_delay) { DelayType result; @@ -2699,12 +2808,20 @@ break; /* We have a constant true expression so the body always runs. */ case DEFINITE_DELAY: - result = proc->delay_type(); + if (proc) { + result = proc->delay_type(print_delay); + } else { + result = NO_DELAY; + } break; /* We don't know if the body will run so reduce a DEFINITE_DELAY * to a POSSIBLE_DELAY. All other stay the same. */ case POSSIBLE_DELAY: - result = combine_delays(NO_DELAY, proc->delay_type()); + if (proc) { + result = combine_delays(NO_DELAY, proc->delay_type(print_delay)); + } else { + result = NO_DELAY; + } break; /* This should never happen since delay_type_from_expr() only * returns three different values. */ @@ -2717,12 +2834,12 @@ } /* The default object does not have any delay. */ -DelayType NetProc::delay_type() const +DelayType NetProc::delay_type(bool /* print_delay */ ) const { return NO_DELAY; } -DelayType NetBlock::delay_type() const +DelayType NetBlock::delay_type(bool print_delay) const { // A join_none has no delay. if (type() == PARA_JOIN_NONE) return NO_DELAY; @@ -2732,25 +2849,25 @@ if (type() == PARA_JOIN_ANY) { result = DEFINITE_DELAY; for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { - DelayType dt = cur->delay_type(); + DelayType dt = cur->delay_type(print_delay); if (dt < result) result = dt; - if (dt == NO_DELAY) break; + if ((dt == NO_DELAY) && !print_delay) break; } // A begin or join has the maximum delay. } else { result = NO_DELAY; for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { - DelayType dt = cur->delay_type(); + DelayType dt = cur->delay_type(print_delay); if (dt > result) result = dt; - if (dt == DEFINITE_DELAY) break; + if ((dt == DEFINITE_DELAY) && !print_delay) break; } } return result; } -DelayType NetCase::delay_type() const +DelayType NetCase::delay_type(bool print_delay) const { DelayType result = NO_DELAY; bool def_stmt = false; @@ -2758,7 +2875,7 @@ for (unsigned idx = 0; idx < nstmts; idx += 1) { if (!expr(idx)) def_stmt = true; - DelayType dt = stat(idx) ? stat(idx)->delay_type() : NO_DELAY; + DelayType dt = stat(idx) ? stat(idx)->delay_type(print_delay) : NO_DELAY; if (idx == 0) { result = dt; } else { @@ -2766,6 +2883,7 @@ } } +// FIXME: If all the cases are covered (e.g. an enum) then this is not true. /* If we don't have a default statement we don't know for sure * that we have a delay. */ if (!def_stmt) result = combine_delays(NO_DELAY, result); @@ -2773,75 +2891,636 @@ return result; } -DelayType NetCondit::delay_type() const +DelayType NetCondit::delay_type(bool print_delay) const { - DelayType if_type = if_ ? if_->delay_type() : NO_DELAY; - DelayType el_type = else_? else_->delay_type() : NO_DELAY; + DelayType if_type = if_ ? if_->delay_type(print_delay) : NO_DELAY; + DelayType el_type = else_? else_->delay_type(print_delay) : NO_DELAY; return combine_delays(if_type, el_type); } /* * A do/while will execute the body at least once. */ -DelayType NetDoWhile::delay_type() const +DelayType NetDoWhile::delay_type(bool print_delay) const { - ivl_assert(*this, proc_); - return proc_->delay_type(); + if (proc_) return proc_->delay_type(print_delay); + + return ZERO_DELAY; } -DelayType NetEvWait::delay_type() const +DelayType NetEvWait::delay_type(bool print_delay) const { + if (print_delay) { + cerr << get_fileline() << ": error: an event control is not allowed " + "in an always_comb, always_ff or always_latch process." + << endl; + } + return DEFINITE_DELAY; } -DelayType NetForever::delay_type() const +DelayType NetForever::delay_type(bool print_delay) const { - ivl_assert(*this, statement_); - return statement_->delay_type(); + if (statement_) return statement_->delay_type(print_delay); + + return ZERO_DELAY; } -DelayType NetForLoop::delay_type() const +DelayType NetForLoop::delay_type(bool print_delay) const { - ivl_assert(*this, statement_); - return get_loop_delay_type(condition_, statement_); + return get_loop_delay_type(condition_, statement_, print_delay); } -DelayType NetPDelay::delay_type() const +DelayType NetPDelay::delay_type(bool print_delay) const { + if (print_delay) { + cerr << get_fileline() << ": error: a blocking delay is not allowed " + "in an always_comb, always_ff or always_latch process." + << endl; + } + if (expr_) { - return delay_type_from_expr(expr_); - } else { - if (delay() > 0) { - return DEFINITE_DELAY; + if (statement_) { + return combine_delays(delay_type_from_expr(expr_), + statement_->delay_type(print_delay)); } else { - if (statement_) { - return combine_delays(ZERO_DELAY, - statement_->delay_type()); - } else { - return ZERO_DELAY; - } + return delay_type_from_expr(expr_); + } + } + + if (delay() > 0) return DEFINITE_DELAY; + + if (statement_) { + return combine_delays(ZERO_DELAY, + statement_->delay_type(print_delay)); + } else { + return ZERO_DELAY; + } +} + +DelayType NetRepeat::delay_type(bool print_delay) const +{ + return get_loop_delay_type(expr_, statement_, print_delay); +} + +DelayType NetTaskDef::delay_type(bool print_delay) const +{ + if (proc_) { + return proc_->delay_type(print_delay); + } else { + return NO_DELAY; + } +} + +DelayType NetUTask::delay_type(bool print_delay) const +{ + return task()->task_def()->delay_type(print_delay); +} + +static bool do_expr_event_match(const NetExpr*expr, const NetEvWait*evwt) +{ + // The event wait should only have a single event. + if (evwt->nevents() != 1) return false; + // The event should have a single probe. + const NetEvent *evt = evwt->event(0); + if (evt->nprobe() != 1) return false; + // The probe should be for any edge. + const NetEvProbe *prb = evt->probe(0); + if (prb->edge() != NetEvProbe::ANYEDGE) return false; + // Create a NexusSet from the event probe signals. + NexusSet *ns_evwt = new NexusSet; + for (unsigned idx =0; idx < prb->pin_count(); idx += 1) { + if (! prb->pin(idx).is_linked()) { + delete ns_evwt; + return false; + } + // Casting away const is safe since this nexus set is only being read. + ns_evwt->add(const_cast (prb->pin(idx).nexus()), + 0, prb->pin(idx).nexus()->vector_width()); + } + // Get the NexusSet for the expression. + NexusSet *ns_expr = expr->nex_input(); + // Make sure the event and expression NexusSets match exactly. + if (ns_evwt->size() != ns_expr->size()) { + delete ns_evwt; + delete ns_expr; + return false; + } + ns_expr->rem(*ns_evwt); + delete ns_evwt; + if (ns_expr->size() != 0) { + delete ns_expr; + return false; + } + delete ns_expr; + + return true; +} + +static bool while_is_wait(const NetExpr*expr, const NetProc*stmt) +{ + if (const NetEvWait*evwt = dynamic_cast(stmt)) { + if (evwt->statement()) return false; + const NetEBComp*cond = dynamic_cast(expr); + if (! cond) return false; + if (cond->op() != 'N') return false; + const NetEConst*cval = dynamic_cast(cond->right()); + if (! cval) return false; + const verinum val = cval->value(); + if (val.len() != 1) return false; + if (val.get(0) != verinum::V1) return false; + if (! do_expr_event_match(cond->left(), evwt)) return false; + if (evwt->get_lineno() != cond->get_lineno()) return false; + if (evwt->get_file() != cond->get_file()) return false; + return true; + } + return false; +} + +DelayType NetWhile::delay_type(bool print_delay) const +{ + // If the wait was a constant value the compiler already removed it + // so we know we can only have a possible delay. + if (while_is_wait(cond_, proc_)) { + if (print_delay) { + cerr << get_fileline() << ": error: a wait statement is " + "not allowed in an " + "always_comb, always_ff or always_latch process." + << endl; } + return POSSIBLE_DELAY; } + return get_loop_delay_type(cond_, proc_, print_delay); } -DelayType NetRepeat::delay_type() const +/* + * These are the check_synth() functions. They are used to print + * a warning if the item is not synthesizable. + */ +static const char * get_process_type_as_string(ivl_process_type_t pr_type) { - ivl_assert(*this, statement_); - return get_loop_delay_type(expr_, statement_); + switch (pr_type) { + case IVL_PR_ALWAYS_COMB: + return "in an always_comb process."; + break; + case IVL_PR_ALWAYS_FF: + return "in an always_ff process."; + break; + case IVL_PR_ALWAYS_LATCH: + return "in an always_latch process."; + break; + default: + assert(0); + return 0; + } } -DelayType NetTaskDef::delay_type() const +static void print_synth_warning(const NetProc *net_proc, const char *name, + ivl_process_type_t pr_type) { - return proc_->delay_type(); + cerr << net_proc->get_fileline() << ": warning: " << name + << " statement cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; } -DelayType NetUTask::delay_type() const +static void check_if_logic_l_value(const NetAssignBase *base, + ivl_process_type_t pr_type) { - return task()->task_def()->delay_type(); + if (base->l_val_count() != 1) return; + + const NetAssign_*lval = base->l_val(0); + if (! lval) return; + + NetNet*sig = lval->sig(); + if (! sig) return; + + if ((sig->data_type() != IVL_VT_BOOL) && + (sig->data_type() != IVL_VT_LOGIC)) { + cerr << base->get_fileline() << ": warning: Assinging to a " + "non-integral variable ("<< sig->name() + << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +/* By default elements can be synthesized or ignored. */ +bool NetProc::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +// FIXME: User function calls still need to be checked (NetEUFunc). +// : Non-constant system functions need a warning (NetESFunc). +// : Constant functions should already be elaborated. + +/* By default assign elements can be synthesized. */ +bool NetAssignBase::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +bool NetAssign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + check_if_logic_l_value(this, pr_type); + +// FIXME: Check that ff/latch only use this for internal signals. + return false; } -DelayType NetWhile::delay_type() const +bool NetAssignNB::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const { - ivl_assert(*this, proc_); - return get_loop_delay_type(cond_, proc_); + bool result = false; + if (pr_type == IVL_PR_ALWAYS_COMB) { + cerr << get_fileline() << ": warning: A non-blocking assignment " + "should not be used in an always_comb process." << endl; + } + + if (event_) { + cerr << get_fileline() << ": error: A non-blocking assignment " + "cannot be synthesized with an event control " + << get_process_type_as_string(pr_type) << endl; + result = true; + } + + check_if_logic_l_value(this, pr_type); + + return result; +} + +bool NetBlock::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + // Only a begin/end can be synthesized. + if (type() != SEQU) { + cerr << get_fileline() << ": error: A fork/"; + switch (type()) { + case PARA: + cerr << "join"; + break; + case PARA_JOIN_ANY: + cerr << "join_any"; + break; + case PARA_JOIN_NONE: + cerr << "join_none"; + break; + default: + assert(0); + } + cerr << " statement cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + result = true; + } + + const NetScope*save_scope = scope; + if (subscope()) scope = subscope(); + if (scope != save_scope) { + result |= scope->check_synth(pr_type, scope); + } + for (const NetProc*cur = proc_first(); cur; cur = proc_next(cur)) { + result |= cur->check_synth(pr_type, scope); + } + scope = save_scope; + return result; +} + +bool NetCase::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + for (unsigned idx = 0; idx < nitems(); idx += 1) { + if (stat(idx)) result |= stat(idx)->check_synth(pr_type, scope); + } +// FIXME: Check for ff/latch/comb structures. + return result; +} + +bool NetCAssign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A procedural assign", pr_type); + return false; +} + +bool NetCondit::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + if (if_) result |= if_->check_synth(pr_type, scope); + if (else_) result |= else_->check_synth(pr_type, scope); +// FIXME: Check for ff/latch/comb structures. + return result; +} + +bool NetDeassign::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A procedural deassign", pr_type); + return false; +} + +bool NetDisable::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + while (scope) { + if (scope != target_) scope = scope->parent(); + else break; + } + + + if (! scope) { + cerr << get_fileline() << ": warning: A disable statement can " + "only be synthesized when disabling an enclosing block " + << get_process_type_as_string(pr_type) << endl; + } + return false; +} + +bool NetDoWhile::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A do/while", pr_type); + if (proc_) result |= proc_->check_synth(pr_type, scope); + return result; +} + +bool NetEvTrig::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "An event trigger", pr_type); + return false; +} + +// The delay check above has already marked this as an error. +bool NetEvWait::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +bool NetForce::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A force", pr_type); + return false; +} + +bool NetForever::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A forever", pr_type); + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +/* + * A bunch of private routines to verify that a for loop has the correct + * structure for synthesis. + */ +static void print_for_idx_warning(const NetProc*proc, const char*check, + ivl_process_type_t pr_type, NetNet*idx) +{ + cerr << proc->get_fileline() << ": warning: A for statement must use " + "the index (" << idx->name() << ") in the " << check + << " expression to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_for_const_synth(const NetExpr*expr, const NetProc*proc, + const char*str, ivl_process_type_t pr_type) +{ + if (! dynamic_cast(expr)) { + cerr << proc-> get_fileline() << ": warning: A for " + "statement must " << str + << " value to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +static void check_for_bin_synth(const NetExpr*left,const NetExpr*right, + const char*str, const char*check, + const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + const NetESignal*lsig = dynamic_cast(left); + const NetESignal*rsig = dynamic_cast(right); + + if (lsig && (lsig->sig() == index)) { + check_for_const_synth(right, proc, str, pr_type); + } else if (rsig && (rsig->sig() == index)) { + check_for_const_synth(left, proc, str, pr_type); + } else { + print_for_idx_warning(proc, check, pr_type, index); + } +} + +static void print_for_step_warning(const NetProc*proc, + ivl_process_type_t pr_type) +{ + cerr << proc->get_fileline() << ": warning: A for statement step must " + "be a simple assignment statement to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void print_for_step_warning(const NetProc*proc, + ivl_process_type_t pr_type, NetNet*idx) +{ + cerr << proc->get_fileline() << ": warning: A for statement step must " + "be an assignment to the index variable (" + << idx->name() << ") to be synthesized " + << get_process_type_as_string(pr_type) << endl; +} + +static void check_for_bstep_synth(const NetExpr*expr, const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + if (const NetECast*tmp = dynamic_cast(expr)) { + expr = tmp->expr(); + } + + if (const NetEBAdd*tmp = dynamic_cast(expr)) { + check_for_bin_synth(tmp->left(), tmp->right(), + "change by a constant", "step", proc, pr_type, + index); + } else { + cerr << proc->get_fileline() << ": warning: A for statement " + "step must be a simple binary +/- " + "to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } +} + +static void check_for_step_synth(const NetAssign*assign, const NetProc*proc, + ivl_process_type_t pr_type, NetNet*index) +{ + if (assign->l_val_count() != 1) { + print_for_step_warning(proc, pr_type); + } else if (assign->l_val(0)->sig() != index) { + print_for_step_warning(proc, pr_type, index); + } else { + switch (assign->assign_operator()) { + case '+': + case '-': + check_for_const_synth(assign->rval(), proc, + "have a constant step", pr_type); + break; + case 0: + check_for_bstep_synth(assign->rval(), proc, pr_type, index); + break; + default: + cerr << proc->get_fileline() << ": warning: A for statement " + "step does not support operator '" + << assign->assign_operator() + << "' it must be +/- to be synthesized " + << get_process_type_as_string(pr_type) << endl; + break; + } + } +} + +bool NetForLoop::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + +// FIXME: What about an enum (NetEConstEnum)? + if (! dynamic_cast(init_expr_)) { + cerr << get_fileline() << ": warning: A for statement must " + "have a constant initial value to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + +// FIXME: Do the following also need to be supported in the condition? +// It would seem like they are hard to use to find the bounds. +// From NetEBinary +// What about NetEBits sig & constant, etc. +// From NetEUnary +// What about NetEUBits ! sig or ! (sig == constat) +// What about NetEUReduce &signal + if (const NetESignal*tmp = dynamic_cast(condition_)) { + if (tmp->sig() != index_) { + print_for_idx_warning(this, "condition", pr_type, index_); + } + } else if (const NetEBComp*cmp = dynamic_cast(condition_)) { + check_for_bin_synth(cmp->left(), cmp->right(), + "compare against a constant", "condition", + this, pr_type, index_); + } else { + print_for_idx_warning(this, "condition", pr_type, index_); + } + + if (const NetAssign*tmp = dynamic_cast(step_statement_)) { + check_for_step_synth(tmp, this, pr_type, index_); + } else { + print_for_step_warning(this, pr_type); + } + + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +// The delay check above has already marked this as an error. +bool NetPDelay::check_synth(ivl_process_type_t /* pr_type */, + const NetScope* /* scope */ ) const +{ + return false; +} + +bool NetRelease::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */ ) const +{ + print_synth_warning(this, "A release", pr_type); + return false; +} + +bool NetRepeat::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + print_synth_warning(this, "A repeat", pr_type); + if (statement_) result |= statement_->check_synth(pr_type, scope); + return result; +} + +bool NetScope::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */) const +{ + bool result = false; + // Skip local events/signals + for (NetEvent*cur = events_ ; cur ; cur = cur->snext_) { + if (cur->local_flag()) continue; + cerr << cur->get_fileline() << ": warning: An event (" + << cur->name() << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + for (signals_map_iter_t cur = signals_map_.begin(); + cur != signals_map_.end() ; ++ cur) { + const NetNet*sig = cur->second; + if ((sig->data_type() != IVL_VT_BOOL) && + (sig->data_type() != IVL_VT_LOGIC)) { + cerr << sig->get_fileline() << ": warning: A non-integral " + "variable (" << sig->name() << ") cannot be " + "synthesized " + << get_process_type_as_string(pr_type) << endl; + } + } + return result; +} + +bool NetSTask::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */) const +{ + if (strcmp(name(), "$ivl_darray_method$delete") == 0) { + cerr << get_fileline() << ": warning: Dynamic array " + "delete method cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } else { + cerr << get_fileline() << ": warning: System task (" + << name() << ") cannot be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + return false; +} + +bool NetTaskDef::check_synth(ivl_process_type_t pr_type, + const NetScope* /* scope */) const +{ + bool result = false; + const NetScope *tscope = this->scope(); + result |= tscope->check_synth(pr_type, tscope); + if (! tscope->is_auto()) { + cerr << tscope->get_def_file() << ":" + << tscope->get_def_lineno() + << ": warning: user task (" << tscope->basename() + << ") must be automatic to be synthesized " + << get_process_type_as_string(pr_type) << endl; + } + if (proc_) result |= proc_->check_synth(pr_type, tscope); + return result; +} + +bool NetUTask::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + return task()->task_def()->check_synth(pr_type, scope); +} + +bool NetWhile::check_synth(ivl_process_type_t pr_type, + const NetScope* scope) const +{ + bool result = false; + // A wait is already maked as an error in the delay check above. + if (! while_is_wait(cond_, proc_)) { + print_synth_warning(this, "A while", pr_type); + if (proc_) result |= proc_->check_synth(pr_type, scope); + } + return result; } diff -Nru iverilog-10.3/netlist.h iverilog-11.0/netlist.h --- iverilog-10.3/netlist.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/netlist.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_netlist_H #define IVL_netlist_H /* - * Copyright (c) 1998-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -77,6 +77,7 @@ class PClass; class PExpr; class PFunction; +class PPackage; class PTaskFunc; struct enum_type_t; class netclass_t; @@ -251,7 +252,6 @@ class NetObj : public NetPins, public Attrib { public: - public: // The name of the object must be a permallocated string. A // lex_strings string, for example. explicit NetObj(NetScope*s, perm_string n, unsigned npins); @@ -261,6 +261,7 @@ const NetScope* scope() const; perm_string name() const { return name_; } + void rename(perm_string n) { name_ = n; } const NetExpr* rise_time() const { return delay1_; } const NetExpr* fall_time() const { return delay2_; } @@ -290,7 +291,7 @@ class IslandBranch { public: - IslandBranch(ivl_discipline_t dis =0) : island_(0), discipline_(dis) { } + explicit IslandBranch(ivl_discipline_t dis =0) : island_(0), discipline_(dis) { } ivl_island_t get_island() const { return island_; } @@ -379,6 +380,8 @@ NetNet* pick_any_net(); + NetNode* pick_any_node(); + /* This method counts the number of input and output links for this nexus, and assigns the results to the output arguments. */ void count_io(unsigned&inp, unsigned&out) const; @@ -551,7 +554,7 @@ public: explicit NetDelaySrc(NetScope*s, perm_string n, unsigned nsrc, - bool condit_src, bool conditional); + bool condit_src, bool conditional, bool parallel); ~NetDelaySrc(); // These functions set the delays from the values in the @@ -587,12 +590,15 @@ Link&condit_pin(); const Link&condit_pin() const; + bool is_parallel() const; + void dump(ostream&, unsigned ind) const; private: uint64_t transition_delays_[12]; bool condit_flag_; bool conditional_; + bool parallel_; bool posedge_; bool negedge_; @@ -925,10 +931,13 @@ public: enum TYPE { MODULE, CLASS, TASK, FUNC, BEGIN_END, FORK_JOIN, GENBLOCK, PACKAGE }; - /* Create a new scope, and attach it to the given parent. The - name is expected to have been permallocated. */ - NetScope(NetScope*up, const hname_t&name, TYPE t, bool nest=false, - bool program=false, bool interface=false); + /* Create a new scope associated with a given compilation unit, + and attach it to the given parent. If no compilation unit is + specified, the parent's compilation unit is used. The name + is expected to have been permallocated. */ + NetScope(NetScope*up, const hname_t&name, TYPE t, NetScope*in_unit=0, + bool nest=false, bool program=false, bool interface=false, + bool compilation_unit=false); ~NetScope(); /* Rename the scope using the name generated by inserting as @@ -937,9 +946,17 @@ if a unique name couldn't be generated. */ bool auto_name(const char* prefix, char pad, const char* suffix); + void add_imports(const map*imports); + NetScope*find_import(const Design*des, perm_string name); + + void add_typedefs(const map*typedefs); + + /* Search the scope hierarchy for the scope where 'type' was defined. */ + NetScope*find_typedef_scope(const Design*des, data_type_t*type); + /* Routine to search for the enumeration given a name. It basically * does what enumeration_for_name() does but searched the hierarchy. */ - const netenum_t*find_enumeration_for_name(perm_string name); + const netenum_t*find_enumeration_for_name(const Design*des, perm_string name); /* Parameters exist within a scope, and these methods allow one to manipulate the set. In these cases, the name is the @@ -1000,10 +1017,12 @@ netclass_t* find_class(perm_string name); - /* The parent and child() methods allow users of NetScope - objects to locate nearby scopes. */ + /* The unit(), parent(), and child() methods allow users of + NetScope objects to locate nearby scopes. */ + NetScope* unit() { return unit_; } NetScope* parent() { return up_; } NetScope* child(const hname_t&name); + const NetScope* unit() const { return unit_; } const NetScope* parent() const { return up_; } const NetScope* child(const hname_t&name) const; @@ -1021,7 +1040,8 @@ // Program blocks and interfaces have elaboration constraints. inline bool program_block() const { return program_block_; } inline bool is_interface() const { return is_interface_; } - TYPE type() const; + inline bool is_unit() const { return is_unit_; } + inline TYPE type() const { return type_; } void print_type(ostream&) const; // This provides a link to the variable initialisation process @@ -1150,6 +1170,9 @@ perm_string local_symbol(); void dump(ostream&) const; + // Check to see if the scope has items that are not allowed + // in an always_comb/ff/latch process. + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; void emit_scope(struct target_t*tgt) const; bool emit_defs(struct target_t*tgt) const; @@ -1157,6 +1180,12 @@ children of this node as well. */ void run_functor(Design*des, functor_t*fun); + /* These are used in synthesis. They provide shared pullup and + pulldown nodes for this scope. */ + void add_tie_hi(Design*des); + void add_tie_lo(Design*des); + Link&tie_hi() const { return tie_hi_->pin(0); }; + Link&tie_lo() const { return tie_lo_->pin(0); }; /* This member is used during elaboration to pass defparam assignments from the scope pass to the parameter evaluation @@ -1241,6 +1270,8 @@ bool program_block_; // True if the scope is an interface bool is_interface_; + // True if the scope is a compilation unit + bool is_unit_; perm_string file_; perm_string def_file_; @@ -1250,6 +1281,10 @@ signed char time_unit_, time_prec_; bool time_from_timescale_; + const map*imports_; + + const map*typedefs_; + NetEvent *events_; map genvars_; @@ -1271,6 +1306,7 @@ const PFunction*func_pform_; unsigned elab_stage_; + NetScope*unit_; NetScope*up_; map children_; @@ -1280,6 +1316,9 @@ /* Final procedures sets this to notify statements that they are part of a final procedure. */ bool in_final_; + + NetNode*tie_hi_; + NetNode*tie_lo_; }; /* @@ -1681,6 +1720,35 @@ verinum sset_value_; }; + +/* + * This class represents an LPM_LATCH device. There is no literal gate + * type in Verilog that maps, but gates of this type can be inferred. + */ +class NetLatch : public NetNode { + + public: + NetLatch(NetScope*s, perm_string n, unsigned vector_width); + ~NetLatch(); + + unsigned width() const; + + Link& pin_Enable(); + Link& pin_Data(); + Link& pin_Q(); + + const Link& pin_Enable() const; + const Link& pin_Data() const; + const Link& pin_Q() const; + + virtual void dump_node(ostream&, unsigned ind) const; + virtual bool emit_node(struct target_t*) const; + virtual void functor_node(Design*des, functor_t*fun); + + private: + unsigned width_; +}; + /* * This class implements a basic LPM_MULT combinational multiplier. It * is used as a structural representation of the * operator. The @@ -1996,7 +2064,8 @@ // Get the Nexus that are the input to this // expression. Normally this descends down to the reference to // a signal that reads from its input. - virtual NexusSet* nex_input(bool rem_out = true) =0; + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const =0; // Return a version of myself that is structural. This is used // for converting expressions to gates. The arguments are: @@ -2039,7 +2108,8 @@ void dump(ostream&) const; NetEArrayPattern* dup_expr() const; - NexusSet* nex_input(bool rem_out =true); + NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; private: std::vector items_; @@ -2072,7 +2142,8 @@ virtual NetEConst* dup_expr() const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; @@ -2144,7 +2215,8 @@ virtual NetECReal* dup_expr() const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*); - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; @@ -2258,6 +2330,7 @@ virtual void dump_node(ostream&, unsigned ind) const; virtual bool emit_node(struct target_t*tgt) const; + virtual void functor_node(Design*des, functor_t*fun); private: unsigned wid_; @@ -2315,6 +2388,8 @@ enum kind_t { EEQ, // === NEQ, // !== + WEQ, // ==? + WNE, // !=? XEQ, // casex guard tests ZEQ // casez guard tests }; @@ -2417,9 +2492,9 @@ class NetLogic : public NetNode { public: - enum TYPE { AND, BUF, BUFIF0, BUFIF1, CMOS, NAND, NMOS, NOR, NOT, - NOTIF0, NOTIF1, OR, PULLDOWN, PULLUP, RCMOS, RNMOS, RPMOS, - PMOS, XNOR, XOR }; + enum TYPE { AND, BUF, BUFIF0, BUFIF1, CMOS, EQUIV, IMPL, NAND, NMOS, + NOR, NOT, NOTIF0, NOTIF1, OR, PULLDOWN, PULLUP, RCMOS, + RNMOS, RPMOS, PMOS, XNOR, XOR }; explicit NetLogic(NetScope*s, perm_string n, unsigned pins, TYPE t, unsigned wid, bool is_cassign__=false); @@ -2585,7 +2660,8 @@ // Find the nexa that are input by the statement. This is used // for example by @* to find the inputs to the process for the // sensitivity list. - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; // Find the nexa that are set by the statement. Add the output // values to the set passed as a parameter. @@ -2616,44 +2692,63 @@ // process. Most process types are not. virtual bool is_synchronous(); - // Synthesize as asynchronous logic, and return true. The - // nex_out is where this function attaches its output - // results. The accumulated_nex_out is used by sequential - // blocks to show outputs from the previous code. + // Synthesize as asynchronous logic, and return true on success. + // + // nex_map holds the set of nexuses that are driven by this + // process, nex_out holds the accumulated outputs from this and + // preceding sequential processes (i.e statements in the same + // block), enables holds the accumulated clock/gate enables, + // and bitmasks holds the accumulated masks that flag which bits + // are unconditionally driven (i.e. driven by every clause in + // every statement). On output, the values passed in to nex_out, + // enables, and bitmasks may either be merged with or replaced + // by the values originating from this process, depending on the + // type of statement this process represents. + // + // The clock/gate enables generated by synthesis operate at a + // vector level (i.e. they are asserted if any bit(s) in the + // vector are driven). + typedef vector mask_t; virtual bool synth_async(Design*des, NetScope*scope, - NexusSet&nex_map, - NetBus&nex_out, - NetBus&accumulated_nex_out); - - // Synthesize as synchronous logic, and return true. That - // means binding the outputs to the data port of a FF, and the - // event inputs to a FF clock. Only some key NetProc sub-types + NexusSet&nex_map, NetBus&nex_out, + NetBus&enables, vector&bitmasks); + + // Synthesize as synchronous logic, and return true on success. + // That means binding the outputs to the data port of a FF, and + // the event inputs to a FF clock. Only some key NetProc sub-types // that have specific meaning in synchronous statements. The // remainder reduce to a call to synth_async that connects the // output to the Data input of the FF. // - // The events argument is filled in be the NetEvWait - // implementation of this method with the probes that it does - // not itself pick off as a clock. These events should be - // picked off by e.g. condit statements as set/reset inputs to - // the flipflop being generated. + // The nex_map, nex_out, ff_ce, and bitmasks arguments serve + // the same purpose as in the synth_async method (where ff_ce + // is equivalent to enables). The events argument is filled + // in by the NetEvWait implementation of this method with the + // probes that it does not itself pick off as a clock. These + // events should be picked off by e.g. condit statements as + // asynchronous set/reset inputs to the flipflop being generated. virtual bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, NetNet*ff_clock, NetBus&ff_ce, NetBus&ff_aclr, NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); virtual void dump(ostream&, unsigned ind) const; // Recursively checks to see if there is delay in this element. - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + // Check to see if the item is synthesizable. + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; protected: bool synth_async_block_substatement_(Design*des, NetScope*scope, NexusSet&nex_map, - NetBus&accumulated_nex_out, + NetBus&nex_out, + NetBus&enables, + vector&bitmasks, NetProc*substmt); private: friend class NetBlock; @@ -2667,13 +2762,16 @@ class NetAlloc : public NetProc { public: - NetAlloc(NetScope*); + explicit NetAlloc(NetScope*); ~NetAlloc(); const string name() const; const NetScope* scope() const; + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; @@ -2781,7 +2879,8 @@ // being outputs. For example foo[idx] = ... is the l-value // (NetAssign_ object) with a foo l-value and the input // expression idx. - NexusSet* nex_input(bool rem_out = true); + NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; // Figuring out nex_output to process ultimately comes down to // this method. @@ -2829,7 +2928,8 @@ void set_delay(NetExpr*); const NetExpr* get_delay() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&o); @@ -2839,11 +2939,12 @@ bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); // This dumps all the lval structures. void dump_lval(ostream&) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetAssign_*lval_; @@ -2865,6 +2966,7 @@ virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -2887,6 +2989,7 @@ virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; unsigned nevents() const; const NetEvent*event(unsigned) const; @@ -2928,7 +3031,7 @@ // synthesize as asynchronous logic, and return true. bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, @@ -2936,6 +3039,7 @@ NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); // This version of emit_recurse scans all the statements of @@ -2943,12 +3047,14 @@ // for sequential blocks. void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: const Type type_; @@ -2963,6 +3069,9 @@ * way the comparisons are performed. Also, it is likely that the * target may be able to optimize differently. * + * Case statements can have unique, unique0, or priority attached to + * them. If not otherwise adorned, it is QBASIC. + * * Case can be one of three types: * EQ -- All bits must exactly match * EQZ -- z bits are don't care @@ -2972,13 +3081,15 @@ public: enum TYPE { EQ, EQX, EQZ }; - NetCase(TYPE c, NetExpr*ex, unsigned cnt); + + NetCase(ivl_case_quality_t q, TYPE c, NetExpr*ex, unsigned cnt); ~NetCase(); void set_case(unsigned idx, NetExpr*ex, NetProc*st); void prune(); + inline ivl_case_quality_t case_quality() const { return quality_; } TYPE type() const; const NetExpr*expr() const { return expr_; } inline unsigned nitems() const { return items_.size(); } @@ -2986,16 +3097,18 @@ inline const NetExpr*expr(unsigned idx) const { return items_[idx].guard;} inline const NetProc*stat(unsigned idx) const { return items_[idx].statement; } - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&out); bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3007,8 +3120,9 @@ bool synth_async_casez_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); + ivl_case_quality_t quality_; TYPE type_; struct Item { @@ -3032,9 +3146,9 @@ explicit NetCAssign(NetAssign_*lv, NetExpr*rv); ~NetCAssign(); - virtual NexusSet* nex_input(bool rem_out = true); virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetCAssign(const NetCAssign&); @@ -3065,13 +3179,14 @@ bool emit_recurse_if(struct target_t*) const; bool emit_recurse_else(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&o); bool is_asynchronous(); bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, @@ -3079,12 +3194,14 @@ NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); virtual bool emit_proc(struct target_t*) const; virtual int match_proc(struct proc_match_t*); virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3130,6 +3247,7 @@ virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetDeassign(const NetDeassign&); @@ -3153,8 +3271,12 @@ const NetScope*target() const; + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3181,11 +3303,13 @@ void emit_proc_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3242,6 +3366,9 @@ perm_string name() const; + bool local_flag() const { return local_flag_; } + void local_flag(bool f) { local_flag_ = f; } + // Get information about probes connected to me. unsigned nprobe() const; NetEvProbe* probe(unsigned); @@ -3273,6 +3400,7 @@ private: perm_string name_; + bool local_flag_; // The NetScope class uses these to list the events. NetScope*scope_; @@ -3310,8 +3438,12 @@ const NetEvent*event() const; + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetEvent*event_; @@ -3327,12 +3459,15 @@ void add_event(NetEvent*tgt); void replace_event(NetEvent*orig, NetEvent*repl); + inline void set_t0_trigger() { has_t0_trigger_ = true; }; inline unsigned nevents() const { return events_.size(); } inline const NetEvent*event(unsigned idx) const { return events_[idx]; } inline NetEvent*event(unsigned idx) { return events_[idx]; } + inline bool has_t0_trigger() const { return has_t0_trigger_; }; NetProc*statement(); + const NetProc*statement() const; virtual bool emit_proc(struct target_t*) const; bool emit_recurse(struct target_t*) const; @@ -3346,12 +3481,13 @@ // process? This method checks. virtual bool is_synchronous(); - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&out); virtual bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); virtual bool synth_sync(Design*des, NetScope*scope, bool&ff_negedge, @@ -3359,17 +3495,20 @@ NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const std::vector&events); virtual void dump(ostream&, unsigned ind) const; // This will ignore any statement. virtual void dump_inline(ostream&) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetProc*statement_; // Events that I might wait for. std::vectorevents_; + bool has_t0_trigger_; }; ostream& operator << (ostream&out, const NetEvWait&obj); @@ -3412,10 +3551,9 @@ explicit NetForce(NetAssign_*l, NetExpr*r); ~NetForce(); - virtual NexusSet* nex_input(bool rem_out = true); - virtual void dump(ostream&, unsigned ind) const; virtual bool emit_proc(struct target_t*) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; }; /* @@ -3430,10 +3568,13 @@ void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3452,18 +3593,20 @@ void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; // synthesize as asynchronous logic, and return true. bool synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out); + NetBus&enables, vector&bitmasks); private: NetNet*index_; @@ -3482,13 +3625,16 @@ class NetFree : public NetProc { public: - NetFree(NetScope*); + explicit NetFree(NetScope*); ~NetFree(); const string name() const; const NetScope* scope() const; + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; @@ -3514,6 +3660,13 @@ const std::vector&pd); ~NetFuncDef(); + // Return true if the function returns "void". We still treat + // it as a function since we need to check that the contents + // meet the requirements of a function, but we need to know + // that it is void because it can be evaluated differently. + inline bool is_void() const { return result_sig_ == 0; } + + // Non-void functions have a return value as a signal. const NetNet*return_sig() const; // When we want to evaluate the function during compile time, @@ -3552,12 +3705,14 @@ uint64_t delay() const; const NetExpr*expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; bool emit_proc_recurse(struct target_t*) const; @@ -3579,10 +3734,13 @@ const NetExpr*expr() const; void emit_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3605,6 +3763,7 @@ virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: }; @@ -3630,10 +3789,12 @@ const NetExpr* parm(unsigned idx) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3663,7 +3824,8 @@ ~NetTaskDef(); void dump(ostream&, unsigned) const; - DelayType delay_type() const; + DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: // not implemented NetTaskDef(const NetTaskDef&); @@ -3689,7 +3851,8 @@ virtual void expr_scan(struct expr_scan_t*) const; virtual NetELast*dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; private: NetNet*sig_; @@ -3714,11 +3877,13 @@ const NetScope* func() const; virtual ivl_variable_type_t expr_type() const; + virtual const netenum_t* enumeration() const; virtual void dump(ostream&) const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEUFunc*dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual NetExpr* eval_tree(); virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; @@ -3754,7 +3919,8 @@ virtual void expr_scan(struct expr_scan_t*) const; virtual NetEAccess*dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; private: NetBranch*branch_; @@ -3769,18 +3935,20 @@ class NetUTask : public NetProc { public: - NetUTask(NetScope*); + explicit NetUTask(NetScope*); ~NetUTask(); const string name() const; const NetScope* task() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; private: NetScope*task_; @@ -3801,16 +3969,18 @@ void emit_proc_recurse(struct target_t*) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void nex_output(NexusSet&); virtual bool emit_proc(struct target_t*) const; virtual void dump(ostream&, unsigned ind) const; - virtual DelayType delay_type() const; + virtual DelayType delay_type(bool print_delay=false) const; + virtual bool check_synth(ivl_process_type_t pr_type, const NetScope*scope) const; virtual bool evaluate_function(const LineInfo&loc, map&ctx) const; private: - NetExpr* cond_; + NetExpr*cond_; NetProc*proc_; }; @@ -3851,8 +4021,14 @@ bool emit(struct target_t*tgt) const; private: + bool tie_off_floating_inputs_(Design*des, + NexusSet&nex_map, NetBus&nex_in, + vector&bitmasks, + bool is_ff_input); + const ivl_process_type_t type_; NetProc*const statement_; + Design*synthesized_design_; NetScope*scope_; friend class Design; @@ -3938,7 +4114,8 @@ virtual NetExpr* eval_tree(); virtual NetExpr* evaluate_function(const LineInfo&loc, map&ctx) const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -4061,6 +4238,7 @@ NetEConst*eval_gt_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_gteq_(const NetExpr*le, const NetExpr*re) const; NetEConst*eval_eqeqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; + NetEConst*eval_weqeq_(bool ne_flag, const NetExpr*le, const NetExpr*re) const; }; /* @@ -4081,7 +4259,6 @@ private: NetEConst* eval_arguments_(const NetExpr*l, const NetExpr*r) const; - NetEConst* eval_tree_real_(const NetExpr*l, const NetExpr*r) const; }; /* @@ -4191,7 +4368,8 @@ NetExpr* parm(unsigned idx) const { return parms_[idx]; } virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual bool has_width() const; virtual NetEConcat* dup_expr() const; virtual NetEConst* eval_tree(); @@ -4239,7 +4417,8 @@ // sub-expression. virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual bool has_width() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEConst* eval_tree(); @@ -4261,14 +4440,15 @@ class NetEEvent : public NetExpr { public: - NetEEvent(NetEvent*); + explicit NetEEvent(NetEvent*); ~NetEEvent(); const NetEvent* event() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEEvent* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void dump(ostream&os) const; @@ -4284,14 +4464,15 @@ class NetENetenum : public NetExpr { public: - NetENetenum(const netenum_t*); + explicit NetENetenum(const netenum_t*); ~NetENetenum(); const netenum_t* netenum() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetENetenum* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void dump(ostream&os) const; @@ -4315,7 +4496,8 @@ virtual void expr_scan(struct expr_scan_t*) const; virtual NetENew* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void dump(ostream&os) const; @@ -4337,7 +4519,8 @@ virtual void expr_scan(struct expr_scan_t*) const; virtual NetENull* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void dump(ostream&os) const; }; @@ -4363,7 +4546,8 @@ ivl_variable_type_t expr_type() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEProperty* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void dump(ostream&os) const; @@ -4381,14 +4565,15 @@ class NetEScope : public NetExpr { public: - NetEScope(NetScope*); + explicit NetEScope(NetScope*); ~NetEScope(); const NetScope* scope() const; virtual void expr_scan(struct expr_scan_t*) const; virtual NetEScope* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void dump(ostream&os) const; @@ -4405,7 +4590,7 @@ public: NetESFunc(const char*name, ivl_variable_type_t t, - unsigned width, unsigned nprms); + unsigned width, unsigned nprms, bool is_overridden =false); NetESFunc(const char*name, ivl_type_t rtype, unsigned nprms); NetESFunc(const char*name, const netenum_t*enum_type, unsigned nprms); ~NetESFunc(); @@ -4422,7 +4607,8 @@ map&ctx) const; virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual const netenum_t* enumeration() const; virtual void dump(ostream&) const; @@ -4484,7 +4670,7 @@ /* Added in SystemVerilog 2009 and later. */ CTONES = 0x00020024, /* $countones takes one argument. */ /* Added in SystemVerilog 2012 and later. */ - CTBITS = 0xfffe0025, /* $countbits takes one or more arguments. */ + CTBITS = 0xfffc0025, /* $countbits takes two or more arguments. */ /* Added as Icarus extensions to Verilog-A. */ ABS = 0x00020026, /* $abs takes one argument. */ MAX = 0x00040027, /* $max takes two argument. */ @@ -4494,13 +4680,14 @@ bool takes_nargs_(ID func, unsigned nargs) { if (nargs > 15) nargs = 15; - return func & (1 << (nargs + 16)); + return func & (1U << (nargs + 16)); } const char* name_; ivl_variable_type_t type_; const netenum_t*enum_type_; std::vectorparms_; + bool is_overridden_; ID built_in_id_() const; @@ -4536,8 +4723,7 @@ NetEConst* evaluate_array_funcs_(ID id, const NetExpr*arg0, const NetExpr*arg1) const; - NetEConst* evaluate_countbits_(const NetExpr*arg0, - const NetExpr*arg1) const; + NetEConst* evaluate_countbits_(void) const; public: bool is_built_in() const { return built_in_id_() != NOT_BUILT_IN; }; @@ -4557,7 +4743,8 @@ virtual void expr_scan(struct expr_scan_t*) const; virtual NetEShallowCopy* dup_expr() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void dump(ostream&os) const; @@ -4580,6 +4767,8 @@ NetETernary(NetExpr*c, NetExpr*t, NetExpr*f, unsigned wid, bool signed_flag); ~NetETernary(); + const netenum_t* enumeration() const; + const NetExpr*cond_expr() const; const NetExpr*true_expr() const; const NetExpr*false_expr() const; @@ -4589,7 +4778,8 @@ virtual NetExpr*evaluate_function(const LineInfo&loc, map&ctx) const; virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root); @@ -4644,7 +4834,8 @@ virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root); virtual ivl_variable_type_t expr_type() const; - virtual NexusSet* nex_input(bool rem_out = true); + virtual NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; virtual void expr_scan(struct expr_scan_t*) const; virtual void dump(ostream&) const; @@ -4712,7 +4903,7 @@ class NetESignal : public NetExpr { public: - NetESignal(NetNet*n); + explicit NetESignal(NetNet*n); NetESignal(NetNet*n, NetExpr*word_index); ~NetESignal(); @@ -4720,7 +4911,10 @@ virtual NetESignal* dup_expr() const; NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root); - NexusSet* nex_input(bool rem_out = true); + NexusSet* nex_input(bool rem_out = true, bool always_sens = false, + bool nested_func = false) const; + NexusSet* nex_input_base(bool rem_out, bool always_sens, bool nested_func, + unsigned base, unsigned width) const; const netenum_t*enumeration() const; virtual NetExpr*evaluate_function(const LineInfo&loc, @@ -4768,7 +4962,7 @@ * This class contains an entire design. It includes processes and a * netlist, and can be passed around from function to function. */ -class Design : public Definitions { +class Design { public: Design(); @@ -4790,12 +4984,13 @@ const char* get_flag(const string&key) const; - NetScope* make_root_scope(perm_string name, bool program_block, - bool is_interface); + NetScope* make_root_scope(perm_string name, NetScope*unit_scope, + bool program_block, bool is_interface); NetScope* find_root_scope(); std::list find_root_scopes() const; - NetScope* make_package_scope(perm_string name); + NetScope* make_package_scope(perm_string name, NetScope*unit_scope, + bool is_unit); std::list find_package_scopes() const; /* Attempt to set the precision to the specified value. If the @@ -4845,11 +5040,6 @@ // Look for defparams that never matched, and print warnings. void residual_defparams(); - // Do elaborate_sig for objects in $root scope. - void root_elaborate_sig(void); - - void root_elaborate(void); - /* This method locates a signal, starting at a given scope. The name parameter may be partially hierarchical, so this method, unlike the NetScope::find_signal method, @@ -4862,12 +5052,6 @@ // Tasks NetScope* find_task(NetScope*scope, const pform_name_t&name); - void add_root_task(NetScope*tscope, PTaskFunc*tf); - std::list find_roottask_scopes(void) const; - - // Find a class in the $root scope. - void add_class(netclass_t*cl, PClass*pclass); - netclass_t* find_class(perm_string name) const; // NODES void add_node(NetNode*); @@ -4881,6 +5065,7 @@ void add_process(NetAnalogTop*); void delete_process(NetProcTop*); bool check_proc_delay() const; + bool check_proc_synth() const; NetNet* find_discipline_reference(ivl_discipline_t dis, NetScope*scope); @@ -4895,6 +5080,12 @@ unsigned errors; private: + NetScope* find_scope_(NetScope*, const hname_t&name, + NetScope::TYPE type = NetScope::MODULE) const; + + NetScope* find_scope_(NetScope*, const std::list&path, + NetScope::TYPE type = NetScope::MODULE) const; + // Keep a tree of scopes. The NetScope class handles the wide // tree and per-hop searches for me. listroot_scopes_; @@ -4903,12 +5094,6 @@ // packages do not nest. std::mappackages_; - // Tasks in the $root scope - std::maproot_tasks_; - - // Need this for elaboration of $root scope pclass objects. - std::map class_to_pclass_; - // List the nodes in the design. NetNode*nodes_; // These are in support of the node functor iterator. diff -Nru iverilog-10.3/netmisc.cc iverilog-11.0/netmisc.cc --- iverilog-10.3/netmisc.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/netmisc.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -158,7 +158,7 @@ NetExpr* cast_to_int4(NetExpr*expr, unsigned width) { // Special case: The expression is already LOGIC or BOOL - if (expr->expr_type() != IVL_VT_REAL) + if (expr->expr_type() == IVL_VT_LOGIC || expr->expr_type() == IVL_VT_BOOL) return expr; if (debug_elaborate) @@ -925,6 +925,19 @@ if (tmp == 0) return 0; if ((cast_type != IVL_VT_NO_TYPE) && (cast_type != tmp->expr_type())) { + switch (tmp->expr_type()) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + case IVL_VT_REAL: + break; + default: + cerr << tmp->get_fileline() << ": error: " + "The expression '" << *pe << "' cannot be implicitly " + "cast to the target type." << endl; + des->errors += 1; + delete tmp; + return 0; + } switch (cast_type) { case IVL_VT_REAL: tmp = cast_to_real(tmp); @@ -996,6 +1009,41 @@ flags |= PExpr::NEED_CONST; NetExpr*tmp = pe->elaborate_expr(des, scope, lv_net_type, flags); + if (tmp == 0) return 0; + + ivl_variable_type_t cast_type = ivl_type_base(lv_net_type); + if ((cast_type != IVL_VT_NO_TYPE) && (cast_type != tmp->expr_type())) { + // Catch some special cases. + switch (cast_type) { + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + if (NetESignal*net = dynamic_cast(tmp)) { + ivl_variable_type_t type = net->expr_type(); + if ((type == IVL_VT_DARRAY) || (type == IVL_VT_QUEUE)) + return tmp; + } + if (dynamic_cast(pe)) + return tmp; + // fall through + case IVL_VT_STRING: + if (dynamic_cast(pe)) + return tmp; + break; + case IVL_VT_CLASS: + if (dynamic_cast(pe)) + return tmp; + break; + default: + break; + } + + cerr << tmp->get_fileline() << ": error: " + "The expression '" << *pe << "' cannot be implicitly " + "cast to the target type." << endl; + des->errors += 1; + delete tmp; + return 0; + } return tmp; } @@ -1037,50 +1085,94 @@ return tmp; } -bool evaluate_ranges(Design*des, NetScope*scope, - vector&llist, - const list&rlist) +bool evaluate_range(Design*des, NetScope*scope, const LineInfo*li, + const pform_range_t&range, long&index_l, long&index_r) { - bool bad_msb = false, bad_lsb = false; + bool dimension_ok = true; - for (list::const_iterator cur = rlist.begin() - ; cur != rlist.end() ; ++cur) { - long use_msb, use_lsb; - - NetExpr*texpr = elab_and_eval(des, scope, cur->first, -1, true); - if (! eval_as_long(use_msb, texpr)) { - cerr << cur->first->get_fileline() << ": error: " - "Range expressions must be constant." << endl; - cerr << cur->first->get_fileline() << " : " - "This MSB expression violates the rule: " - << *cur->first << endl; - des->errors += 1; - bad_msb = true; - } + // Unsized and queue dimensions should be handled before calling + // this function. If we find them here, we are in a context where + // they are not allowed. + if (range.first == 0) { + cerr << li->get_fileline() << ": error: " + "An unsized dimension is not allowed here." << endl; + dimension_ok = false; + des->errors += 1; + } else if (dynamic_cast(range.first)) { + cerr << li->get_fileline() << ": error: " + "A queue dimension is not allowed here." << endl; + dimension_ok = false; + des->errors += 1; + } else { + NetExpr*texpr = elab_and_eval(des, scope, range.first, -1, true); + if (! eval_as_long(index_l, texpr)) { + cerr << range.first->get_fileline() << ": error: " + "Dimensions must be constant." << endl; + cerr << range.first->get_fileline() << " : " + << (range.second ? "This MSB" : "This size") + << " expression violates the rule: " + << *range.first << endl; + dimension_ok = false; + des->errors += 1; + } + delete texpr; - delete texpr; + if (range.second == 0) { + // This is a SystemVerilog [size] dimension. The IEEE + // standard does not allow this in a packed dimension, + // but we do. At least one commercial simulator does too. + if (!dimension_ok) { + // bail out + } else if (index_l > 0) { + index_l = index_l - 1; + index_r = 0; + } else { + cerr << range.first->get_fileline() << ": error: " + "Dimension size must be greater than zero." << endl; + cerr << range.first->get_fileline() << " : " + "This size expression violates the rule: " + << *range.first << endl; + dimension_ok = false; + des->errors += 1; + } + } else { + texpr = elab_and_eval(des, scope, range.second, -1, true); + if (! eval_as_long(index_r, texpr)) { + cerr << range.second->get_fileline() << ": error: " + "Dimensions must be constant." << endl; + cerr << range.second->get_fileline() << " : " + "This LSB expression violates the rule: " + << *range.second << endl; + dimension_ok = false; + des->errors += 1; + } + delete texpr; + } + } - texpr = elab_and_eval(des, scope, cur->second, -1, true); - if (! eval_as_long(use_lsb, texpr)) { - cerr << cur->second->get_fileline() << ": error: " - "Range expressions must be constant." << endl; - cerr << cur->second->get_fileline() << " : " - "This LSB expression violates the rule: " - << *cur->second << endl; - des->errors += 1; - bad_lsb = true; - } + /* Error recovery */ + if (!dimension_ok) { + index_l = 0; + index_r = 0; + } - delete texpr; + return dimension_ok; +} - /* Error recovery */ - if (bad_lsb) use_lsb = 0; - if (bad_msb) use_msb = use_lsb; +bool evaluate_ranges(Design*des, NetScope*scope, const LineInfo*li, + vector&llist, + const list&rlist) +{ + bool dimensions_ok = true; - llist.push_back(netrange_t(use_msb, use_lsb)); + for (list::const_iterator cur = rlist.begin() + ; cur != rlist.end() ; ++cur) { + long index_l, index_r; + dimensions_ok &= evaluate_range(des, scope, li, *cur, index_l, index_r); + llist.push_back(netrange_t(index_l, index_r)); } - return bad_msb | bad_lsb; + return dimensions_ok; } void eval_expr(NetExpr*&expr, int context_width) @@ -1249,9 +1341,11 @@ case '|': type = "|"; break; // Bitwise OR case 'O': type = "~|"; break; // NOR - case '!': type = "!"; break; // Logical NOT - case 'a': type = "&&"; break; // Logical AND - case 'o': type = "||"; break; // Logical OR + case '!': type = "!"; break; // Logical NOT + case 'a': type = "&&"; break; // Logical AND + case 'o': type = "||"; break; // Logical OR + case 'q': type = "->"; break; // Logical implication + case 'Q': type = "<->"; break; // Logical equivalence case 'e': type = "=="; break; case 'n': type = "!="; break; @@ -1260,6 +1354,8 @@ if (unary) type = "~|"; // NOR else type = "!=="; // Case inequality break; + case 'w': type = "==?"; break; // Wild equality + case 'W': type = "!=?"; break; // Wild inequality case 'l': type = "<<(<)"; break; // Left shifts case 'r': type = ">>"; break; // Logical right shift @@ -1443,8 +1539,14 @@ { list::const_iterator icur = indices.begin(); for (size_t idx = 0 ; (idx+1) < indices.size() ; idx += 1, ++icur) { - ivl_assert(*(icur->msb), icur != indices.end()); - ivl_assert(*(icur->msb), icur->sel == index_component_t::SEL_BIT); + assert(icur != indices.end()); + if (icur->sel != index_component_t::SEL_BIT) { + cerr << icur->msb->get_fileline() << ": error: " + "All but the final index in a chain of indices must be " + "a single value, not a range." << endl; + des->errors += 1; + return false; + } NetExpr*texpr = elab_and_eval(des, scope, icur->msb, -1, true); long tmp; diff -Nru iverilog-10.3/netmisc.h iverilog-11.0/netmisc.h --- iverilog-10.3/netmisc.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/netmisc.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_netmisc_H #define IVL_netmisc_H /* - * Copyright (c) 1999-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -175,7 +175,7 @@ * index values in the form [<>][<>].... */ template struct __IndicesManip { - inline __IndicesManip(const std::list&v) : val(v) { } + explicit inline __IndicesManip(const std::list&v) : val(v) { } const std::list&val; }; template inline __IndicesManip as_indices(const std::list&indices) @@ -321,7 +321,11 @@ bool need_const =false, bool force_unsigned =false); -extern bool evaluate_ranges(Design*des, NetScope*scope, +extern bool evaluate_range(Design*des, NetScope*scope, const LineInfo*li, + const pform_range_t&range, + long&index_l, long&index_r); + +extern bool evaluate_ranges(Design*des, NetScope*scope, const LineInfo*li, std::vector&llist, const std::list&rlist); /* diff -Nru iverilog-10.3/net_nex_input.cc iverilog-11.0/net_nex_input.cc --- iverilog-10.3/net_nex_input.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_nex_input.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2019 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 @@ -19,37 +19,37 @@ # include "config.h" -# include - +# include +# include # include # include # include "compiler.h" # include "netlist.h" # include "netmisc.h" -NexusSet* NetExpr::nex_input(bool) +NexusSet* NetExpr::nex_input(bool, bool, bool) const { cerr << get_fileline() << ": internal error: nex_input not implemented: " << *this << endl; - return 0; + return new NexusSet; } -NexusSet* NetProc::nex_input(bool) +NexusSet* NetProc::nex_input(bool, bool, bool) const { cerr << get_fileline() << ": internal error: NetProc::nex_input not implemented" << endl; - return 0; + return new NexusSet; } -NexusSet* NetEArrayPattern::nex_input(bool rem_out) +NexusSet* NetEArrayPattern::nex_input(bool rem_out, bool always_sens, bool nested_func) const { NexusSet*result = new NexusSet; for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { if (items_[idx]==0) continue; - NexusSet*tmp = items_[idx]->nex_input(rem_out); + NexusSet*tmp = items_[idx]->nex_input(rem_out, always_sens, nested_func); if (tmp == 0) continue; result->add(*tmp); @@ -58,32 +58,32 @@ return result; } -NexusSet* NetEBinary::nex_input(bool rem_out) +NexusSet* NetEBinary::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result = left_->nex_input(rem_out); - NexusSet*tmp = right_->nex_input(rem_out); + NexusSet*result = left_->nex_input(rem_out, always_sens, nested_func); + NexusSet*tmp = right_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; return result; } -NexusSet* NetEConcat::nex_input(bool rem_out) +NexusSet* NetEConcat::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - if (parms_[0] == NULL) return NULL; - NexusSet*result = parms_[0]->nex_input(rem_out); + if (parms_[0] == NULL) return new NexusSet; + NexusSet*result = parms_[0]->nex_input(rem_out, always_sens, nested_func); for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { if (parms_[idx] == NULL) { delete result; - return NULL; + return new NexusSet; } - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } return result; } -NexusSet* NetEAccess::nex_input(bool) +NexusSet* NetEAccess::nex_input(bool, bool, bool) const { return new NexusSet; } @@ -91,63 +91,73 @@ /* * A constant has not inputs, so always return an empty set. */ -NexusSet* NetEConst::nex_input(bool) +NexusSet* NetEConst::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetECReal::nex_input(bool) +NexusSet* NetECReal::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetEEvent::nex_input(bool) +NexusSet* NetEEvent::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetELast::nex_input(bool) +NexusSet* NetELast::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetENetenum::nex_input(bool) +NexusSet* NetENetenum::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetENew::nex_input(bool) +NexusSet* NetENew::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetENull::nex_input(bool) +NexusSet* NetENull::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetEProperty::nex_input(bool) +NexusSet* NetEProperty::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetEScope::nex_input(bool) +NexusSet* NetEScope::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetESelect::nex_input(bool rem_out) +NexusSet* NetESelect::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet(); - NexusSet*tmp = expr_->nex_input(rem_out); - if (tmp == NULL) { - delete result; - return NULL; + NexusSet*result = base_? base_->nex_input(rem_out, always_sens, nested_func) : new NexusSet(); + NexusSet*tmp = expr_->nex_input(rem_out, always_sens, nested_func); + bool const_select = result->size() == 0; + if (always_sens && const_select) { + if (NetEConst *val = dynamic_cast (base_)) { + assert(select_type() == IVL_SEL_OTHER); + if (NetESignal *sig = dynamic_cast (expr_)) { + delete tmp; + tmp = sig->nex_input_base(rem_out, always_sens, nested_func, + val->value().as_unsigned(), expr_width()); + } else { + cerr << get_fileline() << ": Sorry, cannot determine the sensitivity " + << "for the select of " << *expr_ << ", using all bits." << endl; + } + } } result->add(*tmp); delete tmp; /* See the comment for NetESignal below. */ - if (base_ && warn_sens_entire_vec) { + if (base_ && ! always_sens && warn_sens_entire_vec) { cerr << get_fileline() << ": warning: @* is sensitive to all " "bits in '" << *expr_ << "'." << endl; } @@ -157,30 +167,35 @@ /* * The $fread, etc. system functions can have NULL arguments. */ -NexusSet* NetESFunc::nex_input(bool rem_out) +NexusSet* NetESFunc::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - if (parms_.empty()) - return new NexusSet; + NexusSet*result = new NexusSet; - NexusSet*result; - if (parms_[0]) result = parms_[0]->nex_input(rem_out); - else result = new NexusSet; - for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { + if (parms_.empty()) return result; + + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } } + return result; } -NexusSet* NetEShallowCopy::nex_input(bool) +NexusSet* NetEShallowCopy::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetESignal::nex_input(bool rem_out) +NexusSet* NetESignal::nex_input(bool rem_out, bool always_sens, bool nested_func) const +{ + return nex_input_base(rem_out, always_sens, nested_func, 0, 0); +} + +NexusSet* NetESignal::nex_input_base(bool rem_out, bool always_sens, bool nested_func, + unsigned base, unsigned width) const { /* * This is not what I would expect for the various selects (bit, @@ -188,48 +203,88 @@ * instead of building the appropriate select and then using it * as the trigger. Other simulators also add everything. */ + bool const_select = false; + unsigned const_word = 0; NexusSet*result = new NexusSet; /* Local signals are not added to the sensitivity list. */ if (net_->local_flag()) return result; /* If we have an array index add it to the sensitivity list. */ if (word_) { NexusSet*tmp; - tmp = word_->nex_input(rem_out); + tmp = word_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; - if (warn_sens_entire_arr) { + if (!always_sens && warn_sens_entire_arr) { cerr << get_fileline() << ": warning: @* is sensitive to all " << net_->unpacked_count() << " words in array '" << name() << "'." << endl; } + if (always_sens) if (NetEConst *val = dynamic_cast (word_)) { + const_select = true; + const_word = val->value().as_unsigned(); + } + } + + if ((base == 0) && (width == 0)) width = net_->vector_width(); + + if (const_select) { + result->add(net_->pin(const_word).nexus(), base, width); + } else { + for (unsigned idx = 0 ; idx < net_->pin_count() ; idx += 1) + result->add(net_->pin(idx).nexus(), base, width); } - for (unsigned idx = 0 ; idx < net_->pin_count() ; idx += 1) - result->add(net_->pin(idx).nexus(), 0, net_->vector_width()); return result; } -NexusSet* NetETernary::nex_input(bool rem_out) +NexusSet* NetETernary::nex_input(bool rem_out, bool always_sens, bool nested_func) const { NexusSet*tmp; - NexusSet*result = cond_->nex_input(rem_out); + NexusSet*result = cond_->nex_input(rem_out, always_sens, nested_func); - tmp = true_val_->nex_input(rem_out); + tmp = true_val_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; - tmp = false_val_->nex_input(rem_out); + tmp = false_val_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; return result; } -NexusSet* NetEUFunc::nex_input(bool rem_out) +NexusSet* NetEUFunc::nex_input(bool rem_out, bool always_sens, bool nested_func) const { NexusSet*result = new NexusSet; + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + + if (always_sens) { + NetFuncDef*func = func_->func_def(); + + // Avoid recursive function calls. + static set func_set; + if (!nested_func) + func_set.clear(); + + if (!func_set.insert(func).second) + return result; + + NexusSet*tmp = func->proc()->nex_input(rem_out, always_sens, true); + // Remove the function inputs + NexusSet*in = new NexusSet; + for (unsigned idx = 0 ; idx < func->port_count() ; idx += 1) { + NetNet*net = func->port(idx); + assert(net->pin_count() == 1); + in->add(net->pin(0).nexus(), 0, net->vector_width()); + } + tmp->rem(*in); + delete in; + result->add(*tmp); delete tmp; } @@ -237,21 +292,28 @@ return result; } -NexusSet* NetEUnary::nex_input(bool rem_out) +NexusSet* NetEUnary::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - return expr_->nex_input(rem_out); + return expr_->nex_input(rem_out, always_sens, nested_func); +} + +NexusSet* NetAlloc::nex_input(bool, bool, bool) const +{ + return new NexusSet; } -NexusSet* NetAssign_::nex_input(bool rem_out) +NexusSet* NetAssign_::nex_input(bool rem_out, bool always_sens, bool nested_func) const { + assert(! nest_); NexusSet*result = new NexusSet; + if (word_) { - NexusSet*tmp = word_->nex_input(rem_out); + NexusSet*tmp = word_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } if (base_) { - NexusSet*tmp = base_->nex_input(rem_out); + NexusSet*tmp = base_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } @@ -259,15 +321,21 @@ return result; } -NexusSet* NetAssignBase::nex_input(bool rem_out) +NexusSet* NetAssignBase::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result = rval_->nex_input(rem_out); + NexusSet*result = new NexusSet; + // For the deassign and release statements there is no R-value. + if (rval_) { + NexusSet*tmp = rval_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } /* It is possible that the lval_ can have nex_input values. In particular, index expressions are statement inputs as well, so should be addressed here. */ for (NetAssign_*cur = lval_ ; cur ; cur = cur->more) { - NexusSet*tmp = cur->nex_input(rem_out); + NexusSet*tmp = cur->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } @@ -292,16 +360,15 @@ * In this example, "t" should not be in the input set because it is * used by the sequence as a temporary value. */ -NexusSet* NetBlock::nex_input(bool rem_out) +NexusSet* NetBlock::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - if (last_ == 0) - return new NexusSet; + if (last_ == 0) return new NexusSet; - if (type_ != SEQU) { + if (! always_sens && (type_ != SEQU)) { cerr << get_fileline() << ": internal error: Sorry, " << "I don't know how to synthesize fork/join blocks." << endl; - return 0; + return new NexusSet; } NetProc*cur = last_->next_; @@ -312,10 +379,10 @@ do { /* Get the inputs for the current statement. */ - NexusSet*tmp = cur->nex_input(rem_out); + NexusSet*tmp = cur->nex_input(rem_out, always_sens, nested_func); /* Add the current input set to the accumulated input set. */ - if (tmp != 0) result->add(*tmp); + result->add(*tmp); delete tmp; /* Add the current outputs to the accumulated output set if @@ -339,11 +406,9 @@ * the inputs to all the guards, and the inputs to all the guarded * statements. */ -NexusSet* NetCase::nex_input(bool rem_out) +NexusSet* NetCase::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result = expr_->nex_input(rem_out); - if (result == 0) - return 0; + NexusSet*result = expr_->nex_input(rem_out, always_sens, nested_func); for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { @@ -351,8 +416,7 @@ if (items_[idx].statement == 0) continue; - NexusSet*tmp = items_[idx].statement->nex_input(rem_out); - assert(tmp); + NexusSet*tmp = items_[idx].statement->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; @@ -360,8 +424,7 @@ case is special and is identified by a null guard. The default guard obviously has no input. */ if (items_[idx].guard) { - tmp = items_[idx].guard->nex_input(rem_out); - assert(tmp); + tmp = items_[idx].guard->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } @@ -370,24 +433,18 @@ return result; } -NexusSet* NetCAssign::nex_input(bool) +NexusSet* NetCondit::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - cerr << get_fileline() << ": internal warning: NetCAssign::nex_input()" - << " not implemented." << endl; - return new NexusSet; -} + NexusSet*result = expr_->nex_input(rem_out, always_sens, nested_func); -NexusSet* NetCondit::nex_input(bool rem_out) -{ - NexusSet*result = expr_->nex_input(rem_out); if (if_ != 0) { - NexusSet*tmp = if_->nex_input(rem_out); + NexusSet*tmp = if_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } if (else_ != 0) { - NexusSet*tmp = else_->nex_input(rem_out); + NexusSet*tmp = else_->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } @@ -395,51 +452,85 @@ return result; } -NexusSet* NetDoWhile::nex_input(bool rem_out) +NexusSet* NetDisable::nex_input(bool, bool, bool) const { - NexusSet*result = proc_->nex_input(rem_out); - NexusSet*tmp = cond_->nex_input(rem_out); - result->add(*tmp); - delete tmp; - return result; + return new NexusSet; } -NexusSet* NetEvWait::nex_input(bool rem_out) +NexusSet* NetDoWhile::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result; - if (statement_) - result = statement_->nex_input(rem_out); - else - result = new NexusSet; + NexusSet*result = cond_->nex_input(rem_out, always_sens, nested_func); + + if (proc_) { + NexusSet*tmp = proc_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } return result; } -NexusSet* NetForce::nex_input(bool) +NexusSet* NetEvTrig::nex_input(bool, bool, bool) const { - cerr << get_fileline() << ": internal warning: NetForce::nex_input()" - << " not implemented." << endl; return new NexusSet; } -NexusSet* NetForLoop::nex_input(bool rem_out) +NexusSet* NetEvWait::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result = init_expr_->nex_input(rem_out); + NexusSet*result = new NexusSet; - NexusSet*tmp = condition_->nex_input(rem_out); - result->add(*tmp); - delete tmp; + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } - tmp = statement_->nex_input(rem_out); - result->add(*tmp); - delete tmp; + return result; +} - tmp = step_statement_->nex_input(rem_out); - result->add(*tmp); - delete tmp; +NexusSet* NetForever::nex_input(bool rem_out, bool always_sens, bool nested_func) const +{ + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + + return result; +} + +NexusSet* NetForLoop::nex_input(bool rem_out, bool always_sens, bool nested_func) const +{ + NexusSet*result = new NexusSet; + + if (init_expr_) { + NexusSet*tmp = init_expr_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + + if (condition_) { + NexusSet*tmp = condition_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + + if (step_statement_) { + NexusSet*tmp = step_statement_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } if (gn_shared_loop_index_flag) { - tmp = new NexusSet(); + NexusSet*tmp = new NexusSet(); for (unsigned idx = 0 ; idx < index_->pin_count() ; idx += 1) tmp->add(index_->pin(idx).nexus(), 0, index_->vector_width()); @@ -450,10 +541,9 @@ return result; } -NexusSet* NetForever::nex_input(bool rem_out) +NexusSet* NetFree::nex_input(bool, bool, bool) const { - NexusSet*result = statement_->nex_input(rem_out); - return result; + return new NexusSet; } /* @@ -465,36 +555,44 @@ * include the input set of the because it does not affect the * result. The statement can be omitted. */ -NexusSet* NetPDelay::nex_input(bool rem_out) +NexusSet* NetPDelay::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - if (statement_ == 0) return 0; - NexusSet*result = statement_->nex_input(rem_out); + NexusSet*result = new NexusSet; + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + return result; } -NexusSet* NetRepeat::nex_input(bool rem_out) +NexusSet* NetRepeat::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result = statement_->nex_input(rem_out); - NexusSet*tmp = expr_->nex_input(rem_out); - result->add(*tmp); - delete tmp; + NexusSet*result = expr_->nex_input(rem_out, always_sens, nested_func); + + if (statement_) { + NexusSet*tmp = statement_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + return result; } /* * The $display, etc. system tasks can have NULL arguments. */ -NexusSet* NetSTask::nex_input(bool rem_out) +NexusSet* NetSTask::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - if (parms_.empty()) - return new NexusSet; + NexusSet*result = new NexusSet; - NexusSet*result; - if (parms_[0]) result = parms_[0]->nex_input(rem_out); - else result = new NexusSet; - for (unsigned idx = 1 ; idx < parms_.size() ; idx += 1) { + if (parms_.empty()) return result; + + for (unsigned idx = 0 ; idx < parms_.size() ; idx += 1) { if (parms_[idx]) { - NexusSet*tmp = parms_[idx]->nex_input(rem_out); + NexusSet*tmp = parms_[idx]->nex_input(rem_out, always_sens, nested_func); result->add(*tmp); delete tmp; } @@ -508,16 +606,20 @@ * parameters to consider, because the compiler already removed them * and converted them to blocking assignments. */ -NexusSet* NetUTask::nex_input(bool) +NexusSet* NetUTask::nex_input(bool, bool, bool) const { return new NexusSet; } -NexusSet* NetWhile::nex_input(bool rem_out) +NexusSet* NetWhile::nex_input(bool rem_out, bool always_sens, bool nested_func) const { - NexusSet*result = proc_->nex_input(rem_out); - NexusSet*tmp = cond_->nex_input(rem_out); - result->add(*tmp); - delete tmp; + NexusSet*result = cond_->nex_input(rem_out, always_sens, nested_func); + + if (proc_) { + NexusSet*tmp = proc_->nex_input(rem_out, always_sens, nested_func); + result->add(*tmp); + delete tmp; + } + return result; } diff -Nru iverilog-10.3/net_nex_output.cc iverilog-11.0/net_nex_output.cc --- iverilog-10.3/net_nex_output.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_nex_output.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2017 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 @@ -36,8 +36,13 @@ << endl; } +void NetAlloc::nex_output(NexusSet&) +{ +} + void NetAssign_::nex_output(NexusSet&out) { + assert(! nest_); assert(sig_); unsigned use_word = 0; unsigned use_base = 0; @@ -89,8 +94,7 @@ void NetBlock::nex_output(NexusSet&out) { - if (last_ == 0) - return; + if (last_ == 0) return; NetProc*cur = last_; do { @@ -104,10 +108,8 @@ for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { // Empty statements clearly have no output. - if (items_[idx].statement == 0) - continue; + if (items_[idx].statement == 0) continue; - assert(items_[idx].statement); items_[idx].statement->nex_output(out); } @@ -115,22 +117,31 @@ void NetCondit::nex_output(NexusSet&out) { - if (if_ != 0) - if_->nex_output(out); - if (else_ != 0) - else_->nex_output(out); + if (if_) if_->nex_output(out); + if (else_) else_->nex_output(out); +} + +void NetDisable::nex_output(NexusSet&) +{ } void NetDoWhile::nex_output(NexusSet&out) { - if (proc_ != 0) - proc_->nex_output(out); + if (proc_) proc_->nex_output(out); +} + +void NetEvTrig::nex_output(NexusSet&) +{ } void NetEvWait::nex_output(NexusSet&out) { - assert(statement_); - statement_->nex_output(out); + if (statement_) statement_->nex_output(out); +} + +void NetForever::nex_output(NexusSet&out) +{ + if (statement_) statement_->nex_output(out); } void NetForLoop::nex_output(NexusSet&out) @@ -138,11 +149,20 @@ if (statement_) statement_->nex_output(out); } +void NetFree::nex_output(NexusSet&) +{ +} + void NetPDelay::nex_output(NexusSet&out) { if (statement_) statement_->nex_output(out); } +void NetRepeat::nex_output(NexusSet&out) +{ + if (statement_) statement_->nex_output(out); +} + /* * For the purposes of synthesis, system task calls have no output at * all. This is OK because most system tasks are not synthesizable in @@ -163,6 +183,5 @@ void NetWhile::nex_output(NexusSet&out) { - if (proc_ != 0) - proc_->nex_output(out); + if (proc_) proc_->nex_output(out); } diff -Nru iverilog-10.3/net_proc.cc iverilog-11.0/net_proc.cc --- iverilog-10.3/net_proc.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_proc.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2015 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 @@ -83,8 +83,8 @@ return cur->next_; } -NetCase::NetCase(NetCase::TYPE c, NetExpr*ex, unsigned cnt) -: type_(c), expr_(ex), items_(cnt) +NetCase::NetCase(ivl_case_quality_t q, NetCase::TYPE c, NetExpr*ex, unsigned cnt) +: quality_(q), type_(c), expr_(ex), items_(cnt) { ivl_assert(*this, expr_); } @@ -199,6 +199,7 @@ NetForLoop::NetForLoop(NetNet*ind, NetExpr*iexpr, NetExpr*cond, NetProc*sub, NetProc*step) : index_(ind), init_expr_(iexpr), condition_(cond), statement_(sub), step_statement_(step) { + as_block_ = NULL; } void NetForLoop::wrap_up() diff -Nru iverilog-10.3/netqueue.cc iverilog-11.0/netqueue.cc --- iverilog-10.3/netqueue.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/netqueue.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2014-2020 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 @@ -22,8 +22,8 @@ using namespace std; -netqueue_t::netqueue_t(ivl_type_t vec) -: netdarray_t(vec) +netqueue_t::netqueue_t(ivl_type_t vec, long max_idx) +: netdarray_t(vec), max_idx_(max_idx) { } @@ -38,9 +38,16 @@ bool netqueue_t::test_compatibility(ivl_type_t that) const { - const netqueue_t*that_q = dynamic_cast(that); - if (that_q == 0) + ivl_type_t elem_type = 0; + + if (const netqueue_t*that_q = dynamic_cast(that)) + elem_type = that_q->element_type(); + + if (const netdarray_t*that_da = dynamic_cast(that)) + elem_type = that_da->element_type(); + + if (elem_type == 0) return false; - return element_type()->type_compatible(that_q->element_type()); + return element_type()->type_compatible(elem_type); } diff -Nru iverilog-10.3/netqueue.h iverilog-11.0/netqueue.h --- iverilog-10.3/netqueue.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/netqueue.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL__netqueue_H #define IVL__netqueue_H /* - * Copyright (c) 2014-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2014-2020 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 @@ -30,7 +30,7 @@ class netqueue_t : public netdarray_t { public: - explicit netqueue_t(ivl_type_t vec); + explicit netqueue_t(ivl_type_t vec, long max_idx); ~netqueue_t(); // This is the "base_type()" virtual method of the @@ -41,10 +41,16 @@ // A queue may have a type that is signed. inline bool get_signed() const { return element_type()->get_signed(); } + // Use the packed width to pass the element width + long packed_width(void) const { return element_type()->packed_width(); } + + long max_idx(void) const { return max_idx_; } + std::ostream& debug_dump(std::ostream&) const; private: bool test_compatibility(ivl_type_t that) const; + long max_idx_; }; #endif diff -Nru iverilog-10.3/net_scope.cc iverilog-11.0/net_scope.cc --- iverilog-10.3/net_scope.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/net_scope.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,6 @@ /* - * Copyright (c) 2000-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2019 Stephen Williams (steve@icarus.com) + * Copyright (c) 2016 CERN Michele Castellana (michele.castellana@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 @@ -23,6 +24,8 @@ # include "netlist.h" # include "netclass.h" # include "netenum.h" +# include "netvector.h" +# include "PPackage.h" # include # include # include @@ -110,11 +113,13 @@ * in question. */ -NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, bool nest, - bool program, bool interface) +NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, NetScope*in_unit, + bool nest, bool program, bool interface, bool compilation_unit) : type_(t), name_(n), nested_module_(nest), program_block_(program), - is_interface_(interface), up_(up) + is_interface_(interface), is_unit_(compilation_unit), unit_(in_unit), up_(up) { + imports_ = 0; + typedefs_ = 0; events_ = 0; lcounter_ = 0; is_auto_ = false; @@ -122,8 +127,10 @@ calls_stask_ = false; in_final_ = false; + if (compilation_unit) + unit_ = this; + if (up) { - assert(t!=CLASS); need_const_func_ = up->need_const_func_; is_const_func_ = up->is_const_func_; time_unit_ = up->time_unit(); @@ -131,6 +138,8 @@ time_from_timescale_ = up->time_from_timescale(); // Need to check for duplicate names? up_->children_[name_] = this; + if (unit_ == 0) + unit_ = up_->unit_; } else { need_const_func_ = false; is_const_func_ = false; @@ -162,6 +171,8 @@ lineno_ = 0; def_lineno_ = 0; genvar_tmp_val = 0; + tie_hi_ = 0; + tie_lo_ = 0; } NetScope::~NetScope() @@ -196,16 +207,72 @@ def_lineno_ = def_lineno; } +void NetScope::add_imports(const map*imports) +{ + if (!imports->empty()) + imports_ = imports; +} + +NetScope*NetScope::find_import(const Design*des, perm_string name) +{ + if (imports_ == 0) + return 0; + + map::const_iterator cur = imports_->find(name); + if (cur != imports_->end()) { + return des->find_package(cur->second->pscope_name()); + } else + return 0; +} + +void NetScope::add_typedefs(const map*typedefs) +{ + if (!typedefs->empty()) + typedefs_ = typedefs; +} + +NetScope*NetScope::find_typedef_scope(const Design*des, data_type_t*type) +{ + assert(type); + + NetScope *cur_scope = this; + while (cur_scope) { + if (cur_scope->typedefs_ && cur_scope->typedefs_->find(type->name) != cur_scope->typedefs_->end()) + return cur_scope; + NetScope*import_scope = cur_scope->find_import(des, type->name); + if (import_scope) + cur_scope = import_scope; + else if (cur_scope == unit_) + return 0; + else + cur_scope = cur_scope->parent(); + + if (cur_scope == 0) + cur_scope = unit_; + } + + return 0; +} + /* * Look for the enumeration in the current scope and any parent scopes. */ -const netenum_t*NetScope::find_enumeration_for_name(perm_string name) +const netenum_t*NetScope::find_enumeration_for_name(const Design*des, perm_string name) { NetScope *cur_scope = this; while (cur_scope) { NetEConstEnum*tmp = cur_scope->enum_names_[name]; if (tmp) break; - cur_scope = cur_scope->parent(); + NetScope*import_scope = cur_scope->find_import(des, name); + if (import_scope) + cur_scope = import_scope; + else if (cur_scope == unit_) + return 0; + else + cur_scope = cur_scope->parent(); + + if (cur_scope == 0) + cur_scope = unit_; } assert(cur_scope); @@ -360,12 +427,7 @@ msb = 0; lsb = 0; const NetExpr*tmp = enumeration_expr(key); - if (tmp) return tmp; - - tmp = des->enumeration_expr(key); - if (tmp) return tmp; - - return 0; + return tmp; } NetScope::param_ref_t NetScope::find_parameter(perm_string key) @@ -382,11 +444,6 @@ return idx; } -NetScope::TYPE NetScope::type() const -{ - return type_; -} - void NetScope::print_type(ostream&stream) const { switch (type_) { @@ -505,6 +562,7 @@ unsigned long width ) { assert(type_ == MODULE); + assert(ports_.size() > idx); PortInfo &info = ports_[idx]; info.name = name; info.type = ptype; @@ -652,15 +710,11 @@ if (type_==CLASS && name_==hname_t(name)) return class_def_; - // Look for the class that directly within this scope. + // Look for the class directly within this scope. map::const_iterator cur = classes_.find(name); if (cur != classes_.end()) return cur->second; - // If this is a module scope, then look no further. - if (type_==MODULE) - return 0; - if (up_==0 && type_==CLASS) { assert(class_def_); @@ -668,12 +722,16 @@ return def_parent->find_class(name); } - // If there is no further to look, ... - if (up_ == 0) - return 0; - // Try looking up for the class. - return up_->find_class(name); + if (up_!=0 && type_!=MODULE) + return up_->find_class(name); + + // Try the compilation unit. + if (unit_ != 0) + return unit_->find_class(name); + + // Nowhere left to try... + return 0; } /* @@ -741,7 +799,56 @@ perm_string NetScope::local_symbol() { - ostringstream res; - res << "_s" << (lcounter_++); - return lex_strings.make(res.str()); + perm_string sym; + do { + ostringstream res; + res << "_ivl_" << (lcounter_++); + perm_string sym_tmp = lex_strings.make(res.str()); + + // If the name already exists as a signal, try again. + if (signals_map_.find(sym_tmp) != signals_map_.end()) + continue; + // If the name already exists as a parameter, try again. + if (parameters.find(sym_tmp) != parameters.end()) + continue; + if (genvars_.find(sym_tmp) != genvars_.end()) + continue; + // If the name already exists as a class, try again. + if (classes_.find(sym_tmp) != classes_.end()) + continue; + + // No collisions, this is the one. + sym = sym_tmp; + } while (sym.nil()); + return sym; +} + +void NetScope::add_tie_hi(Design*des) +{ + if (tie_hi_ == 0) { + NetNet*sig = new NetNet(this, lex_strings.make("_LOGIC1"), + NetNet::WIRE, &netvector_t::scalar_logic); + sig->local_flag(true); + + tie_hi_ = new NetLogic(this, local_symbol(), + 1, NetLogic::PULLUP, 1); + des->add_node(tie_hi_); + + connect(sig->pin(0), tie_hi_->pin(0)); + } +} + +void NetScope::add_tie_lo(Design*des) +{ + if (tie_lo_ == 0) { + NetNet*sig = new NetNet(this, lex_strings.make("_LOGIC0"), + NetNet::WIRE, &netvector_t::scalar_logic); + sig->local_flag(true); + + tie_lo_ = new NetLogic(this, local_symbol(), + 1, NetLogic::PULLDOWN, 1); + des->add_node(tie_lo_); + + connect(sig->pin(0), tie_lo_->pin(0)); + } } diff -Nru iverilog-10.3/nodangle.cc iverilog-11.0/nodangle.cc --- iverilog-10.3/nodangle.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/nodangle.cc 2020-09-26 22:44:25.000000000 +0000 @@ -122,10 +122,63 @@ } } +static bool floating_net_tested(NetNet*sig) +{ + static set tested_set; + + pair< set::iterator, bool > cur = tested_set.insert(sig); + return !cur.second; +} + +static void check_is_floating(NetNet*sig) +{ + // Some signal types are implicitly driven if nothing else. + if (sig->type() == NetNet::SUPPLY0) return; + if (sig->type() == NetNet::SUPPLY1) return; + if (sig->type() == NetNet::TRI0) return; + if (sig->type() == NetNet::TRI1) return; + if (sig->type() == NetNet::IMPLICIT_REG) return; + if (sig->type() == NetNet::REG) return ; + + // Assignments drive a signal. + if (sig->peek_lref() > 0) return; + + for (unsigned idx = 0 ; idx < sig->pin_count() ; idx += 1) { + if (sig->pin(idx).get_dir() == Link::OUTPUT) + continue; + + if (sig->pin(idx).nexus()->drivers_present()) + continue; + + if (sig->port_type() == PortType::NOT_A_PORT && sig->pin_count()==1) { + cerr << sig->get_fileline() << ": warning: " + << "Signal " << scope_path(sig->scope()) + << "." << sig->name() + << " has no drivers." << endl; + } else if (sig->port_type()==PortType::NOT_A_PORT) { + cerr << sig->get_fileline() << ": warning: " + << "Signal " << scope_path(sig->scope()) + << "." << sig->name() + << "[" << idx << "]" + << " has no drivers." << endl; + } else { + cerr << sig->get_fileline() << ": warning: " + << "Port " << sig->name() + << " of " << scope_path(sig->scope()) + << " has no drivers." << endl; + } + } + +} + void nodangle_f::signal(Design*, NetNet*sig) { if (scomplete) return; + if (warn_floating_nets && !sig->local_flag() && !floating_net_tested(sig)) { + check_is_floating(sig); + } + /* Cannot delete signals referenced in an expression or an l-value. */ if (sig->get_refs() > 0) diff -Nru iverilog-10.3/parse_api.h iverilog-11.0/parse_api.h --- iverilog-10.3/parse_api.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/parse_api.h 2020-09-26 22:44:25.000000000 +0000 @@ -42,28 +42,21 @@ */ extern std::map pform_modules; extern std::map pform_primitives; -extern std::map pform_typedefs; -extern std::set pform_enum_sets; -extern std::map pform_tasks; -extern std::vector pform_classes; +extern std::vector pform_units; extern std::map pform_packages; extern void pform_dump(std::ostream&out, const PClass*pac); extern void pform_dump(std::ostream&out, const PPackage*pac); extern void pform_dump(std::ostream&out, const PTaskFunc*tf); -extern void elaborate_rootscope_enumerations(Design*des); -extern void elaborate_rootscope_classes(Design*des); -extern void elaborate_rootscope_tasks(Design*des); - /* - * This code actually invokes the parser to make modules. The first - * parameter is the name of the file that is to be parsed. The - * optional second parameter is the opened descriptor for the file. If - * the descriptor is 0 (or skipped) then the function will attempt to - * open the file on its own. + * This code actually invokes the parser to make modules. If the path + * parameter is "-", the parser reads from stdin, otherwise it attempts + * to open and read the specified file. When reading from a file, if + * the ivlpp_string variable is not set to null, the file will be piped + * through the command specified by ivlpp_string before being parsed. */ -extern int pform_parse(const char*path, FILE*file =0); +extern int pform_parse(const char*path); extern string vl_file; diff -Nru iverilog-10.3/parse_misc.cc iverilog-11.0/parse_misc.cc --- iverilog-10.3/parse_misc.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/parse_misc.cc 2020-09-26 22:44:25.000000000 +0000 @@ -39,6 +39,11 @@ return o; } +void VLwarn(const char*msg) +{ + warn_count += 1; + cerr << yylloc.text << ":" << yylloc.first_line << ": " << msg << endl; +} void VLerror(const char*msg) { diff -Nru iverilog-10.3/parse_misc.h iverilog-11.0/parse_misc.h --- iverilog-10.3/parse_misc.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/parse_misc.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_parse_misc_H #define IVL_parse_misc_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -59,6 +59,7 @@ extern void VLerror(const char*msg); extern void VLerror(const YYLTYPE&loc, const char*msg, ...) __attribute__((format(printf,2,3))); #define yywarn VLwarn +extern void VLwarn(const char*msg); extern void VLwarn(const YYLTYPE&loc, const char*msg); extern void destroy_lexor(); @@ -71,12 +72,6 @@ extern bool in_celldefine; enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; extern UCDriveType uc_drive; -/* - * Flags to control if we are declaring or checking a timeunit or - * timeprecision statement. - */ -extern bool have_timeunit_decl; -extern bool have_timeprec_decl; /* * The parser signals back to the lexor that the next identifier @@ -93,7 +88,7 @@ * parser detects typedefs and marks the typedef'ed identifiers as * type names. */ -extern data_type_t* pform_test_type_identifier(const char*txt); +extern data_type_t* pform_test_type_identifier(const YYLTYPE&loc, const char*txt); extern data_type_t* pform_test_type_identifier(PPackage*pkg, const char*txt); extern bool pform_test_type_identifier_local(perm_string txt); diff -Nru iverilog-10.3/parse.y iverilog-11.0/parse.y --- iverilog-10.3/parse.y 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/parse.y 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -35,9 +35,6 @@ extern void lex_end_table(); -bool have_timeunit_decl = false; -bool have_timeprec_decl = false; - static list* param_active_range = 0; static bool param_active_signed = false; static ivl_variable_type_t param_active_type = IVL_VT_LOGIC; @@ -411,6 +408,7 @@ svector*event_expr; + ivl_case_quality_t case_quality; NetNet::Type nettype; PGBuiltin::Type gatetype; NetNet::PortType porttype; @@ -450,6 +448,11 @@ list*exprs; } class_declaration_extends; + struct { + char*text; + PExpr*expr; + } genvar_iter; + verinum* number; verireal* realtime; @@ -468,12 +471,12 @@ %token BASED_NUMBER DEC_NUMBER UNBASED_NUMBER %token REALTIME %token K_PLUS_EQ K_MINUS_EQ K_INCR K_DECR -%token K_LE K_GE K_EG K_EQ K_NE K_CEQ K_CNE K_LP K_LS K_RS K_RSS K_SG +%token K_LE K_GE K_EG K_EQ K_NE K_CEQ K_CNE K_WEQ K_WNE K_LP K_LS K_RS K_RSS K_SG /* K_CONTRIBUTE is <+, the contribution assign. */ %token K_CONTRIBUTE %token K_PO_POS K_PO_NEG K_POW %token K_PSTAR K_STARP K_DOTSTAR -%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER +%token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER K_LEQUIV %token K_SCOPE_RES %token K_edge_descriptor @@ -532,8 +535,6 @@ %token K_tagged K_this K_throughout K_timeprecision K_timeunit K_type %token K_typedef K_union K_unique K_var K_virtual K_void K_wait_order %token K_wildcard K_with K_within - /* Fake tokens that are passed once we have an initial token. */ -%token K_timeprecision_check K_timeunit_check /* The new tokens from 1800-2009. */ %token K_accept_on K_checker K_endchecker K_eventually K_global K_implies @@ -566,7 +567,7 @@ %type number pos_neg_number %type signing unsigned_signed_opt signed_unsigned_opt %type import_export -%type K_packed_opt K_reg_opt K_static_opt K_virtual_opt +%type K_genvar_opt K_packed_opt K_reg_opt K_static_opt K_virtual_opt %type udp_reg_opt edge_operator %type drive_strength drive_strength_opt dr_strength0 dr_strength1 %type udp_input_sym udp_output_sym @@ -580,6 +581,7 @@ %type udp_initial_expr_opt %type register_variable net_variable event_variable endlabel_opt class_declaration_endlabel_opt +%type block_identifier_opt %type register_variable_list net_variable_list event_variable_list %type list_of_identifiers loop_variables %type list_of_port_identifiers list_of_variable_port_identifiers @@ -657,6 +659,9 @@ %type statement statement_item statement_or_null %type compressed_statement %type loop_statement for_step jump_statement +%type concurrent_assertion_statement +%type deferred_immediate_assertion_statement +%type simple_immediate_assertion_statement %type procedural_assertion_statement %type statement_or_null_list statement_or_null_list_opt @@ -671,21 +676,28 @@ %type specify_edge_path specify_edge_path_decl %type non_integer_type +%type assert_or_assume +%type deferred_mode %type atom2_type %type module_start module_end %type lifetime lifetime_opt +%type unique_priority + +%type genvar_iteration + %token K_TAND -%right K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ -%right K_XOR_EQ K_LS_EQ K_RS_EQ K_RSS_EQ +%nonassoc K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ +%nonassoc K_XOR_EQ K_LS_EQ K_RS_EQ K_RSS_EQ +%right K_TRIGGER K_LEQUIV %right '?' ':' K_inside %left K_LOR %left K_LAND %left '|' %left '^' K_NXOR K_NOR %left '&' K_NAND -%left K_EQ K_NE K_CEQ K_CNE +%left K_EQ K_NE K_CEQ K_CNE K_WEQ K_WNE %left K_GE K_LE '<' '>' %left K_LS K_RS K_RSS %left '+' '-' @@ -694,7 +706,7 @@ %left UNARY_PREC -/* to resolve dangling else ambiguity. */ + /* to resolve dangling else ambiguity. */ %nonassoc less_than_K_else %nonassoc K_else @@ -702,15 +714,33 @@ %nonassoc '(' %nonassoc K_exclude + /* to resolve timeunits declaration/redeclaration ambiguity */ +%nonassoc no_timeunits_declaration +%nonassoc one_timeunits_declaration +%nonassoc K_timeunit K_timeprecision + %% /* IEEE1800-2005: A.1.2 */ /* source_text ::= [ timeunits_declaration ] { description } */ -source_text : description_list | ; +source_text + : timeunits_declaration_opt + { pform_set_scope_timescale(yyloc); } + description_list + | /* empty */ + ; + +assert_or_assume + : K_assert + { $$ = 1; } /* IEEE1800-2012: Table 20-7 */ + | K_assume + { $$ = 4; } /* IEEE1800-2012: Table 20-7 */ + ; assertion_item /* IEEE1800-2012: A.6.10 */ : concurrent_assertion_item + | deferred_immediate_assertion_item ; assignment_pattern /* IEEE1800-2005: A.6.7.1 */ @@ -731,7 +761,9 @@ implements it in a LALR way. */ block_identifier_opt /* */ : IDENTIFIER ':' + { $$ = $1; } | + { $$ = 0; } ; class_declaration /* IEEE1800-2005: A.1.2 */ @@ -806,12 +838,14 @@ class_declaration_extends_opt /* IEEE1800-2005: A.1.2 */ : K_extends TYPE_IDENTIFIER - { $$.type = $2.type; + { pform_set_type_referenced(@2, $2.text); + $$.type = $2.type; $$.exprs= 0; delete[]$2.text; } | K_extends TYPE_IDENTIFIER '(' expression_list_with_nuls ')' - { $$.type = $2.type; + { pform_set_type_referenced(@2, $2.text); + $$.type = $2.type; $$.exprs = $4; delete[]$2.text; } @@ -851,7 +885,7 @@ current_function = 0; } - /* Class properties... */ + /* IEEE1800-2017: A.1.9 Class items: Class properties... */ | property_qualifier_opt data_type list_of_variable_decl_assignments ';' { pform_class_property(@2, $1, $2, $3); } @@ -859,7 +893,15 @@ | K_const class_item_qualifier_opt data_type list_of_variable_decl_assignments ';' { pform_class_property(@1, $2 | property_qualifier_t::make_const(), $3, $4); } - /* Class methods... */ + /* IEEEE1800-2017: A.1.9 Class items: class_item ::= { property_qualifier} data_declaration */ + + | property_qualifier_opt K_typedef data_type IDENTIFIER dimensions_opt ';' + { perm_string name = lex_strings.make($4); + delete[]$4; + pform_set_typedef(name, $3, $5); + } + + /* IEEE1800-1017: A.1.9 Class items: Class methods... */ | method_qualifier_opt task_declaration { /* The task_declaration rule puts this into the class */ } @@ -965,16 +1007,98 @@ concurrent_assertion_statement and checker_instantiation rules. */ concurrent_assertion_item /* IEEE1800-2012 A.2.10 */ - : block_identifier_opt K_assert K_property '(' property_spec ')' statement_or_null + : block_identifier_opt concurrent_assertion_statement + { delete $1; + delete $2; + } + ; + +concurrent_assertion_statement /* IEEE1800-2012 A.2.10 */ + : assert_or_assume K_property '(' property_spec ')' statement_or_null %prec less_than_K_else + { /* */ + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: concurrent_assertion_item not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + $$ = 0; + } + | assert_or_assume K_property '(' property_spec ')' K_else statement_or_null + { /* */ + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: concurrent_assertion_item not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + $$ = 0; + } + | assert_or_assume K_property '(' property_spec ')' statement_or_null K_else statement_or_null { /* */ - if (gn_assertions_flag) { + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: concurrent_assertion_item not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + $$ = 0; + } + | K_cover K_property '(' property_spec ')' statement_or_null + { /* */ + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: concurrent_assertion_item not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + $$ = 0; + } + /* For now, cheat, and use property_spec for the sequence specification. + They are syntactically identical. */ + | K_cover K_sequence '(' property_spec ')' statement_or_null + { /* */ + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: concurrent_assertion_item not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + $$ = 0; + } + | K_restrict K_property '(' property_spec ')' ';' + { /* */ + if (gn_unsupported_assertions_flag) { yyerror(@2, "sorry: concurrent_assertion_item not supported." - " Try -gno-assertion to turn this message off."); + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); } + $$ = 0; + } + | assert_or_assume K_property '(' error ')' statement_or_null %prec less_than_K_else + { yyerrok; + yyerror(@2, "error: Error in property_spec of concurrent assertion item."); + $$ = 0; + } + | assert_or_assume K_property '(' error ')' K_else statement_or_null + { yyerrok; + yyerror(@2, "error: Error in property_spec of concurrent assertion item."); + $$ = 0; } - | block_identifier_opt K_assert K_property '(' error ')' statement_or_null + | assert_or_assume K_property '(' error ')' statement_or_null K_else statement_or_null { yyerrok; yyerror(@2, "error: Error in property_spec of concurrent assertion item."); + $$ = 0; + } + | K_cover K_property '(' error ')' statement_or_null + { yyerrok; + yyerror(@2, "error: Error in property_spec of concurrent assertion item."); + $$ = 0; + } + | K_cover K_sequence '(' error ')' statement_or_null + { yyerrok; + yyerror(@2, "error: Error in property_spec of concurrent assertion item."); + $$ = 0; + } + | K_restrict K_property '(' error ')' ';' + { yyerrok; + yyerror(@2, "error: Error in property_spec of concurrent assertion item."); + $$ = 0; } ; @@ -1035,6 +1159,10 @@ } pform_makewire(@2, 0, str_strength, $3, NetNet::IMPLICIT_REG, data_type); } + | attribute_list_opt K_event event_variable_list ';' + { if ($3) pform_make_events($3, @2.text, @2.first_line); + } + | attribute_list_opt package_import_declaration ; data_type /* IEEE1800-2005: A.2.2.1 */ @@ -1075,14 +1203,15 @@ tmp->integer_flag = true; $$ = tmp; } - | K_time + | K_time unsigned_signed_opt { list*pd = make_range_from_width(64); - vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, false, pd); + vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, $2, pd); tmp->reg_flag = !gn_system_verilog(); $$ = tmp; } | TYPE_IDENTIFIER dimensions_opt - { if ($2) { + { pform_set_type_referenced(@1, $1.text); + if ($2) { parray_type_t*tmp = new parray_type_t($1.type, $2); FILE_NAME(tmp, @1); $$ = tmp; @@ -1139,11 +1268,85 @@ } ; - /* NOTE 1: We pull the "timeunits_declaration" into the description - here in order to be a little more flexible with where timeunits - statements may go. This may be a bad idea, but it is legacy now. */ +deferred_immediate_assertion_item /* IEEE1800-2012: A.6.10 */ + : block_identifier_opt deferred_immediate_assertion_statement + { delete $1; + delete $2; + } + ; - /* NOTE 2: The "module" rule of the description combines the +deferred_immediate_assertion_statement /* IEEE1800-2012 A.6.10 */ + : assert_or_assume deferred_mode '(' expression ')' statement_or_null %prec less_than_K_else + { + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: Deferred assertions are not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + delete $4; + delete $6; + $$ = 0; + } + | assert_or_assume deferred_mode '(' expression ')' K_else statement_or_null + { + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: Deferred assertions are not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + delete $4; + delete $7; + $$ = 0; + } + | assert_or_assume deferred_mode '(' expression ')' statement_or_null K_else statement_or_null + { + if (gn_unsupported_assertions_flag) { + yyerror(@1, "sorry: Deferred assertions are not supported." + " Try -gno-assertions or -gsupported-assertions" + " to turn this message off."); + } + delete $4; + delete $6; + delete $8; + $$ = 0; + } + | K_cover deferred_mode '(' expression ')' statement_or_null + { + /* Coverage collection is not currently supported. */ + delete $4; + delete $6; + $$ = 0; + } + | assert_or_assume deferred_mode '(' error ')' statement_or_null %prec less_than_K_else + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $6; + } + | assert_or_assume deferred_mode '(' error ')' K_else statement_or_null + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $7; + } + | assert_or_assume deferred_mode '(' error ')' statement_or_null K_else statement_or_null + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $6; + } + | K_cover deferred_mode '(' error ')' statement_or_null + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $6; + } + ; + +deferred_mode + : '#' DEC_NUMBER + { if (!$2->is_zero()) { + yyerror(@2, "error: Delay value must be zero for deferred assertion."); + } + delete $2; + $$ = 0; } + | K_final + { $$ = 1; } + ; + + /* NOTE: The "module" rule of the description combines the module_declaration, program_declaration, and interface_declaration rules from the standard description. */ @@ -1209,7 +1412,7 @@ { assert(current_function == 0); current_function = pform_push_function_scope(@1, $4, $2); } - function_item_list statement_or_null_list_opt + function_item_list_opt statement_or_null_list_opt K_endfunction { current_function->set_ports($7); current_function->set_return($3); @@ -1297,6 +1500,29 @@ ; +genvar_iteration /* IEEE1800-2012: A.4.2 */ + : IDENTIFIER '=' expression + { $$.text = $1; + $$.expr = $3; + } + | IDENTIFIER K_INCR + { $$.text = $1; + $$.expr = pform_genvar_inc_dec(@1, $1, true); + } + | IDENTIFIER K_DECR + { $$.text = $1; + $$.expr = pform_genvar_inc_dec(@1, $1, false); + } + | K_INCR IDENTIFIER + { $$.text = $2; + $$.expr = pform_genvar_inc_dec(@1, $2, true); + } + | K_DECR IDENTIFIER + { $$.text = $2; + $$.expr = pform_genvar_inc_dec(@1, $2, false); + } + ; + import_export /* IEEE1800-2012: A.2.9 */ : K_import { $$ = true; } | K_export { $$ = false; } @@ -1405,8 +1631,7 @@ char for_block_name [64]; snprintf(for_block_name, sizeof for_block_name, "$ivl_for_loop%u", for_counter); for_counter += 1; - PBlock*tmp = pform_push_block_scope(for_block_name, PBlock::BL_SEQ); - FILE_NAME(tmp, @1); + PBlock*tmp = pform_push_block_scope(@1, for_block_name, PBlock::BL_SEQ); current_block_stack.push(tmp); listassign_list; @@ -1419,7 +1644,7 @@ { pform_name_t tmp_hident; tmp_hident.push_back(name_component_t(lex_strings.make($4))); - PEIdent*tmp_ident = pform_new_ident(tmp_hident); + PEIdent*tmp_ident = pform_new_ident(@4, tmp_hident); FILE_NAME(tmp_ident, @4); PForStatement*tmp_for = new PForStatement(tmp_ident, $6, $8, $10, $13); @@ -1467,8 +1692,7 @@ snprintf(for_block_name, sizeof for_block_name, "$ivl_foreach%u", foreach_counter); foreach_counter += 1; - PBlock*tmp = pform_push_block_scope(for_block_name, PBlock::BL_SEQ); - FILE_NAME(tmp, @1); + PBlock*tmp = pform_push_block_scope(@1, for_block_name, PBlock::BL_SEQ); current_block_stack.push(tmp); pform_make_foreach_declarations(@1, $5); @@ -1553,12 +1777,19 @@ delete[]$1; $$ = tmp; } - | IDENTIFIER '=' K_new '(' ')' + | IDENTIFIER '=' class_new { decl_assignment_t*tmp = new decl_assignment_t; tmp->name = lex_strings.make($1); - PENewClass*expr = new PENewClass; - FILE_NAME(expr, @3); - tmp->expr .reset(expr); + tmp->expr .reset($3); + delete[]$1; + $$ = tmp; + } + | IDENTIFIER dimensions '=' dynamic_array_new + { decl_assignment_t*tmp = new decl_assignment_t; + tmp->name = lex_strings.make($1); + tmp->index = *$2; + tmp->expr .reset($4); + delete $2; delete[]$1; $$ = tmp; } @@ -1643,7 +1874,7 @@ delete[] $3; } | modport_ports_list ',' - { yyerror(@2, "error: NULL port declarations are not allowed"); } + { yyerror(@2, "error: Superfluous comma in port declaration list."); } ; modport_ports_declaration @@ -1728,17 +1959,18 @@ package_declaration /* IEEE1800-2005 A.1.2 */ : K_package lifetime_opt IDENTIFIER ';' - { pform_start_package_declaration(@1, $3, $2); - } + { pform_start_package_declaration(@1, $3, $2); } + timeunits_declaration_opt + { pform_set_scope_timescale(@1); } package_item_list_opt K_endpackage endlabel_opt { pform_end_package_declaration(@1); // If an end label is present make sure it match the package name. - if ($8) { - if (strcmp($3,$8) != 0) { - yyerror(@8, "error: End label doesn't match package name"); + if ($10) { + if (strcmp($3,$10) != 0) { + yyerror(@10, "error: End label doesn't match package name"); } - delete[]$8; + delete[]$10; } delete[]$3; } @@ -1814,23 +2046,17 @@ | { $$ = NetNet::PIMPLICIT; } ; -property_expr /* IEEE1800-2012 A.2.10 */ - : expression +procedural_assertion_statement /* IEEE1800-2012 A.6.10 */ + : concurrent_assertion_statement + { $$ = $1; } + | simple_immediate_assertion_statement + { $$ = $1; } + | deferred_immediate_assertion_statement + { $$ = $1; } ; -procedural_assertion_statement /* IEEE1800-2012 A.6.10 */ - : K_assert '(' expression ')' statement %prec less_than_K_else - { yyerror(@1, "sorry: Simple immediate assertion statements not implemented."); - $$ = 0; - } - | K_assert '(' expression ')' K_else statement - { yyerror(@1, "sorry: Simple immediate assertion statements not implemented."); - $$ = 0; - } - | K_assert '(' expression ')' statement K_else statement - { yyerror(@1, "sorry: Simple immediate assertion statements not implemented."); - $$ = 0; - } +property_expr /* IEEE1800-2012 A.2.10 */ + : expression ; /* The property_qualifier rule is as literally described in the LRM, @@ -1883,6 +2109,72 @@ | K_unsigned { $$ = false; } ; +simple_immediate_assertion_statement /* IEEE1800-2012 A.6.10 */ + : assert_or_assume '(' expression ')' statement_or_null %prec less_than_K_else + { + if (gn_supported_assertions_flag) { + listarg_list; + PCallTask*tmp1 = new PCallTask(lex_strings.make("$error"), arg_list); + FILE_NAME(tmp1, @1); + PCondit*tmp2 = new PCondit($3, $5, tmp1); + FILE_NAME(tmp2, @1); + $$ = tmp2; + } else { + delete $3; + delete $5; + $$ = 0; + } + } + | assert_or_assume '(' expression ')' K_else statement_or_null + { + if (gn_supported_assertions_flag) { + PCondit*tmp = new PCondit($3, 0, $6); + FILE_NAME(tmp, @1); + $$ = tmp; + } else { + delete $3; + delete $6; + $$ = 0; + } + } + | assert_or_assume '(' expression ')' statement_or_null K_else statement_or_null + { + if (gn_supported_assertions_flag) { + PCondit*tmp = new PCondit($3, $5, $7); + FILE_NAME(tmp, @1); + $$ = tmp; + } else { + delete $3; + delete $5; + delete $7; + $$ = 0; + } + } + | K_cover '(' expression ')' statement_or_null + { + /* Coverage collection is not currently supported. */ + delete $3; + delete $5; + $$ = 0; + } + | assert_or_assume '(' error ')' statement_or_null %prec less_than_K_else + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $5; + } + | assert_or_assume '(' error ')' K_else statement_or_null + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $6; + } + | assert_or_assume '(' error ')' statement_or_null K_else statement_or_null + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $5; + } + | K_cover '(' error ')' statement_or_null + { yyerror(@1, "error: Malformed conditional expression."); + $$ = $5; + } + ; + simple_type_or_string /* IEEE1800-2005: A.2.2.1 */ : integer_vector_type { ivl_variable_type_t use_vtype = $1; @@ -1920,7 +2212,8 @@ $$ = tmp; } | TYPE_IDENTIFIER - { $$ = $1.type; + { pform_set_type_referenced(@1, $1.text); + $$ = $1.type; delete[]$1.text; } | PACKAGE_IDENTIFIER K_SCOPE_RES @@ -2163,6 +2456,15 @@ $$ = tmp; } + + /* Ports can be string. */ + + | port_direction K_string list_of_identifiers ';' + { vector*tmp = pform_make_task_ports(@1, $1, IVL_VT_STRING, true, + 0, $3); + $$ = tmp; + } + ; @@ -2284,7 +2586,7 @@ $$ = $3; } | tf_port_item_list ',' - { yyerror(@2, "error: NULL port declarations are not allowed."); + { yyerror(@2, "error: Superfluous comma in port declaration list."); $$ = $1; } | tf_port_item_list ';' @@ -2293,20 +2595,24 @@ } ; - /* NOTE: Icarus Verilog is a little more generous with the - timeunits declarations by allowing them to happen in multiple - places in the file. So the rule is adjusted to be invoked by the - "description" rule. This theoretically allows files to be - concatenated together and still compile. */ timeunits_declaration /* IEEE1800-2005: A.1.2 */ : K_timeunit TIME_LITERAL ';' - { pform_set_timeunit($2, false, false); } + { pform_set_timeunit($2, allow_timeunit_decl); } | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, false, false); - pform_set_timeprecision($4, false, false); + { bool initial_decl = allow_timeunit_decl && allow_timeprec_decl; + pform_set_timeunit($2, initial_decl); + pform_set_timeprec($4, initial_decl); } | K_timeprecision TIME_LITERAL ';' - { pform_set_timeprecision($2, false, false); } + { pform_set_timeprec($2, allow_timeprec_decl); } + ; + + /* Allow zero, one, or two declarations. The second declaration might + be a repeat declaration, but the pform functions take care of that. */ +timeunits_declaration_opt + : /* empty */ %prec no_timeunits_declaration + | timeunits_declaration %prec one_timeunits_declaration + | timeunits_declaration timeunits_declaration ; value_range /* IEEE1800-2005: A.8.3 */ @@ -2331,9 +2637,7 @@ << "Use at least -g2005-sv to remove this warning." << endl; } list *tmp = new list; - pform_range_t index; - index.first = new PENumber(new verinum((uint64_t)0, integer_width)); - index.second = new PEBinary('-', $2, new PENumber(new verinum((uint64_t)1, integer_width))); + pform_range_t index ($2,0); tmp->push_back(index); $$ = tmp; } @@ -2353,6 +2657,16 @@ tmp->push_back(index); $$ = tmp; } + | '[' '$' ':' expression ']' + { // SystemVerilog queue with a max size + list *tmp = new list; + pform_range_t index (new PENull,$4); + if (!gn_system_verilog()) { + yyerror("error: Queue declarations require SystemVerilog."); + } + tmp->push_back(index); + $$ = tmp; + } ; variable_lifetime @@ -2467,6 +2781,10 @@ | type_declaration + /* Blocks can have imports. */ + + | package_import_declaration + /* Recover from errors that happen within variable lists. Use the trailing semi-colon to resync the parser. */ @@ -3096,7 +3414,8 @@ event_control /* A.K.A. clocking_event */ : '@' hierarchy_identifier - { PEIdent*tmpi = new PEIdent(*$2); + { PEIdent*tmpi = pform_new_ident(@2, *$2); + FILE_NAME(tmpi, @2); PEEvent*tmpe = new PEEvent(PEEvent::ANYEDGE, tmpi); PEventStatement*tmps = new PEventStatement(tmpe); FILE_NAME(tmps, @1); @@ -3341,6 +3660,11 @@ FILE_NAME(tmp, @2); $$ = tmp; } + | expression K_WEQ attribute_list_opt expression + { PEBinary*tmp = new PEBComp('w', $1, $4); + FILE_NAME(tmp, @2); + $$ = tmp; + } | expression K_LE attribute_list_opt expression { PEBinary*tmp = new PEBComp('L', $1, $4); FILE_NAME(tmp, @2); @@ -3361,6 +3685,11 @@ FILE_NAME(tmp, @2); $$ = tmp; } + | expression K_WNE attribute_list_opt expression + { PEBinary*tmp = new PEBComp('W', $1, $4); + FILE_NAME(tmp, @2); + $$ = tmp; + } | expression K_LOR attribute_list_opt expression { PEBinary*tmp = new PEBLogic('o', $1, $4); FILE_NAME(tmp, @2); @@ -3371,6 +3700,19 @@ FILE_NAME(tmp, @2); $$ = tmp; } +/* + FIXME: This creates shift/reduce issues that need to be solved + | expression K_TRIGGER attribute_list_opt expression + { PEBinary*tmp = new PEBLogic('q', $1, $4); + FILE_NAME(tmp, @2); + $$ = tmp; + } +*/ + | expression K_LEQUIV attribute_list_opt expression + { PEBinary*tmp = new PEBLogic('Q', $1, $4); + FILE_NAME(tmp, @2); + $$ = tmp; + } | expression '?' attribute_list_opt expression ':' expression { PETernary*tmp = new PETernary($1, $4, $6); FILE_NAME(tmp, @2); @@ -3430,6 +3772,7 @@ expression_list_with_nuls : expression_list_with_nuls ',' expression { list*tmp = $1; + if (tmp->empty()) tmp->push_back(0); tmp->push_back($3); $$ = tmp; } @@ -3440,11 +3783,11 @@ } | { list*tmp = new list; - tmp->push_back(0); $$ = tmp; } | expression_list_with_nuls ',' { list*tmp = $1; + if (tmp->empty()) tmp->push_back(0); tmp->push_back(0); $$ = tmp; } @@ -3469,7 +3812,8 @@ /* There are a few special cases (notably $bits argument) where the expression may be a type name. Let the elaborator sort this out. */ | TYPE_IDENTIFIER - { PETypename*tmp = new PETypename($1.type); + { pform_set_type_referenced(@1, $1.text); + PETypename*tmp = new PETypename($1.type); FILE_NAME(tmp,@1); $$ = tmp; delete[]$1.text; @@ -3522,7 +3866,7 @@ indexed arrays and part selects */ | hierarchy_identifier - { PEIdent*tmp = pform_new_ident(*$1); + { PEIdent*tmp = pform_new_ident(@1, *$1); FILE_NAME(tmp, @1); $$ = tmp; delete $1; @@ -4222,8 +4566,7 @@ } | list_of_port_declarations ',' { - yyerror(@2, "error: NULL port declarations are not " - "allowed."); + yyerror(@2, "error: Superfluous comma in port declaration list."); } | list_of_port_declarations ';' { @@ -4316,9 +4659,8 @@ use_type = NetNet::IMPLICIT_REG; } else if (dynamic_cast ($4)) { use_type = NetNet::IMPLICIT_REG; - } else if (enum_type_t*etype = dynamic_cast ($4)) { - if(etype->base_type == IVL_VT_LOGIC) - use_type = NetNet::IMPLICIT_REG; + } else if (dynamic_cast ($4)) { + use_type = NetNet::IMPLICIT_REG; } } ptmp = pform_module_port_reference(name, @2.text, @2.first_line); @@ -4418,7 +4760,7 @@ rule to reflect the rules for assignment l-values. */ lpvalue : hierarchy_identifier - { PEIdent*tmp = pform_new_ident(*$1); + { PEIdent*tmp = pform_new_ident(@1, *$1); FILE_NAME(tmp, @1); $$ = tmp; delete $1; @@ -4473,47 +4815,6 @@ { $$ = $1; } ; - /* We allow zero, one or two unique declarations. */ -local_timeunit_prec_decl_opt - : /* Empty */ - | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, true, false); - have_timeunit_decl = true; - pform_set_timeprecision($4, true, false); - have_timeprec_decl = true; - } - | local_timeunit_prec_decl - | local_timeunit_prec_decl local_timeunit_prec_decl2 - ; - - /* By setting the appropriate have_time???_decl we allow only - one declaration of each type in this module. */ -local_timeunit_prec_decl - : K_timeunit TIME_LITERAL ';' - { pform_set_timeunit($2, true, false); - have_timeunit_decl = true; - } - | K_timeprecision TIME_LITERAL ';' - { pform_set_timeprecision($2, true, false); - have_timeprec_decl = true; - } - ; -local_timeunit_prec_decl2 - : K_timeunit TIME_LITERAL ';' - { pform_set_timeunit($2, true, false); - have_timeunit_decl = true; - } - | K_timeprecision TIME_LITERAL ';' - { pform_set_timeprecision($2, true, false); - have_timeprec_decl = true; - } - /* As the second item this form is always a check. */ - | K_timeunit TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, true, true); - pform_set_timeprecision($4, true, true); - } - ; - /* This is the global structure of a module. A module is a start section, with optional ports, then an optional list of module items, and finally an end marker. */ @@ -4526,11 +4827,8 @@ module_port_list_opt module_attribute_foreign ';' { pform_module_set_ports($8); } - local_timeunit_prec_decl_opt - { have_timeunit_decl = true; // Every thing past here is - have_timeprec_decl = true; // a check! - pform_check_timeunit_prec(); - } + timeunits_declaration_opt + { pform_set_scope_timescale(@2); } module_item_list_opt module_end { Module::UCDriveType ucd; @@ -4567,8 +4865,6 @@ } } pform_endmodule($4, in_celldefine, ucd); - have_timeunit_decl = false; // We will allow decls again. - have_timeprec_decl = false; } endlabel_opt { // Last step: check any closing name. This is done late so @@ -4647,15 +4943,27 @@ ports. These are simply advance ways to declare parameters, so that the port declarations may use them. */ module_parameter_port_list_opt - : - | '#' '(' module_parameter_port_list ')' - ; + : + | '#' '(' module_parameter_port_list ')' + ; module_parameter_port_list - : K_parameter param_type parameter_assign - | module_parameter_port_list ',' parameter_assign - | module_parameter_port_list ',' K_parameter param_type parameter_assign - ; + : K_parameter param_type parameter_assign + | K_localparam param_type localparam_assign + { if (!gn_system_verilog()) { + yyerror(@1, "error: Local parameters in module parameter " + "port lists requires SystemVerilog."); + } + } + | module_parameter_port_list ',' parameter_assign + | module_parameter_port_list ',' K_parameter param_type parameter_assign + | module_parameter_port_list ',' K_localparam param_type localparam_assign + { if (!gn_system_verilog()) { + yyerror(@3, "error: Local parameters in module parameter " + "port lists requires SystemVerilog."); + } + } + ; module_item @@ -4922,9 +5230,8 @@ | attribute_list_opt IDENTIFIER parameter_value_opt gate_instance_list ';' { perm_string tmp1 = lex_strings.make($2); - pform_make_modgates(@2, tmp1, $3, $4); + pform_make_modgates(@2, tmp1, $3, $4, $1); delete[]$2; - if ($1) delete $1; } | attribute_list_opt @@ -4947,6 +5254,18 @@ { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS, $3, $1); FILE_NAME(tmp, @2); } + | attribute_list_opt K_always_comb statement_item + { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS_COMB, $3, $1); + FILE_NAME(tmp, @2); + } + | attribute_list_opt K_always_ff statement_item + { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS_FF, $3, $1); + FILE_NAME(tmp, @2); + } + | attribute_list_opt K_always_latch statement_item + { PProcess*tmp = pform_make_behavior(IVL_PR_ALWAYS_LATCH, $3, $1); + FILE_NAME(tmp, @2); + } | attribute_list_opt K_initial statement_item { PProcess*tmp = pform_make_behavior(IVL_PR_INITIAL, $3, $1); FILE_NAME(tmp, @2); @@ -4961,6 +5280,8 @@ | attribute_list_opt assertion_item + | timeunits_declaration + | class_declaration | task_declaration @@ -4986,34 +5307,32 @@ | K_genvar list_of_identifiers ';' { pform_genvars(@1, $2); } - | K_for '(' IDENTIFIER '=' expression ';' + | K_for '(' K_genvar_opt IDENTIFIER '=' expression ';' expression ';' - IDENTIFIER '=' expression ')' - { pform_start_generate_for(@1, $3, $5, $7, $9, $11); } + genvar_iteration ')' + { pform_start_generate_for(@2, $3, $4, $6, $8, $10.text, $10.expr); } generate_block - { pform_endgenerate(); } + { pform_endgenerate(false); } | generate_if generate_block_opt K_else { pform_start_generate_else(@1); } generate_block - { pform_endgenerate(); } + { pform_endgenerate(true); } | generate_if generate_block_opt %prec less_than_K_else - { pform_endgenerate(); } + { pform_endgenerate(true); } | K_case '(' expression ')' { pform_start_generate_case(@1, $3); } generate_case_items K_endcase - { pform_endgenerate(); } + { pform_endgenerate(true); } | modport_declaration - | package_import_declaration - /* 1364-2001 and later allow specparam declarations outside specify blocks. */ | attribute_list_opt K_specparam @@ -5084,14 +5403,6 @@ | KK_attribute '(' error ')' ';' { yyerror(@1, "error: Malformed $attribute parameter list."); } - | K_timeunit_check TIME_LITERAL ';' - { pform_set_timeunit($2, true, true); } - | K_timeunit_check TIME_LITERAL '/' TIME_LITERAL ';' - { pform_set_timeunit($2, true, true); - pform_set_timeprecision($4, true, true); - } - | K_timeprecision_check TIME_LITERAL ';' - { pform_set_timeprecision($2, true, true); } ; module_item_list @@ -5113,9 +5424,9 @@ generate_case_item : expression_list_proper ':' { pform_generate_case_item(@1, $1); } generate_block_opt - { pform_endgenerate(); } + { pform_endgenerate(false); } | K_default ':' { pform_generate_case_item(@1, 0); } generate_block_opt - { pform_endgenerate(); } + { pform_endgenerate(false); } ; generate_item @@ -5136,7 +5447,7 @@ warn_count += 1; cerr << @1 << ": warning: Anachronistic use of named begin/end to surround generate schemes." << endl; } - pform_endgenerate(); + pform_endgenerate(false); } ; @@ -5268,6 +5579,12 @@ param_active_signed = true; param_active_type = IVL_VT_BOOL; } + | TYPE_IDENTIFIER + { pform_set_type_referenced(@1, $1.text); + pform_set_param_from_type(@1, $1.type, $1.text, param_active_range, + param_active_signed, param_active_type); + delete[]$1.text; + } ; /* parameter and localparam assignment lists are broken into @@ -5705,6 +6022,20 @@ pform_make_var_init(@1, name, $4); $$ = $1; } + | IDENTIFIER dimensions_opt '=' dynamic_array_new + { if (pform_peek_scope()->var_init_needs_explicit_lifetime() + && (var_lifetime == LexicalScope::INHERITED)) { + cerr << @3 << ": warning: Static variable initialization requires " + "explicit lifetime in this context." << endl; + warn_count += 1; + } + perm_string name = lex_strings.make($1); + pform_makewire(@1, name, NetNet::REG, + NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); + pform_set_reg_idx(name, $2); + pform_make_var_init(@1, name, $4); + $$ = $1; + } ; register_variable_list @@ -5797,7 +6128,7 @@ pform_module_specify_path(tmp); } | K_ifnone specify_edge_path_decl ';' - { yyerror(@1, "Sorry: ifnone with an edge-sensitive path is " + { yywarn(@1, "Sorry: ifnone with an edge-sensitive path is " "not supported."); yyerrok; } @@ -6192,8 +6523,7 @@ } /* In SystemVerilog an unnamed block can contain variable declarations. */ | K_begin - { PBlock*tmp = pform_push_block_scope(0, PBlock::BL_SEQ); - FILE_NAME(tmp, @1); + { PBlock*tmp = pform_push_block_scope(@1, 0, PBlock::BL_SEQ); current_block_stack.push(tmp); } block_item_decls_opt @@ -6227,8 +6557,7 @@ $$ = tmp; } | K_begin ':' IDENTIFIER - { PBlock*tmp = pform_push_block_scope($3, PBlock::BL_SEQ); - FILE_NAME(tmp, @1); + { PBlock*tmp = pform_push_block_scope(@1, $3, PBlock::BL_SEQ); current_block_stack.push(tmp); } block_item_decls_opt @@ -6265,8 +6594,7 @@ } /* In SystemVerilog an unnamed block can contain variable declarations. */ | K_fork - { PBlock*tmp = pform_push_block_scope(0, PBlock::BL_PAR); - FILE_NAME(tmp, @1); + { PBlock*tmp = pform_push_block_scope(@1, 0, PBlock::BL_PAR); current_block_stack.push(tmp); } block_item_decls_opt @@ -6301,8 +6629,7 @@ $$ = tmp; } | K_fork ':' IDENTIFIER - { PBlock*tmp = pform_push_block_scope($3, PBlock::BL_PAR); - FILE_NAME(tmp, @1); + { PBlock*tmp = pform_push_block_scope(@1, $3, PBlock::BL_PAR); current_block_stack.push(tmp); } block_item_decls_opt @@ -6340,12 +6667,18 @@ FILE_NAME(tmp, @1); $$ = tmp; } - | K_TRIGGER hierarchy_identifier ';' - { PTrigger*tmp = new PTrigger(*$2); - FILE_NAME(tmp, @1); - delete $2; - $$ = tmp; - } + | K_TRIGGER hierarchy_identifier ';' + { PTrigger*tmp = pform_new_trigger(@2, 0, *$2); + FILE_NAME(tmp, @1); + delete $2; + $$ = tmp; + } + | K_TRIGGER PACKAGE_IDENTIFIER K_SCOPE_RES hierarchy_identifier + { PTrigger*tmp = pform_new_trigger(@4, $2, *$4); + FILE_NAME(tmp, @1); + delete $4; + $$ = tmp; + } | procedural_assertion_statement { $$ = $1; } @@ -6353,27 +6686,28 @@ | jump_statement { $$ = $1; } - | K_case '(' expression ')' case_items K_endcase - { PCase*tmp = new PCase(NetCase::EQ, $3, $5); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | K_casex '(' expression ')' case_items K_endcase - { PCase*tmp = new PCase(NetCase::EQX, $3, $5); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | K_casez '(' expression ')' case_items K_endcase - { PCase*tmp = new PCase(NetCase::EQZ, $3, $5); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | K_case '(' expression ')' error K_endcase - { yyerrok; } - | K_casex '(' expression ')' error K_endcase - { yyerrok; } - | K_casez '(' expression ')' error K_endcase - { yyerrok; } + | unique_priority K_case '(' expression ')' case_items K_endcase + { PCase*tmp = new PCase($1, NetCase::EQ, $4, $6); + FILE_NAME(tmp, @2); + $$ = tmp; + } + | unique_priority K_casex '(' expression ')' case_items K_endcase + { PCase*tmp = new PCase($1, NetCase::EQX, $4, $6); + FILE_NAME(tmp, @2); + $$ = tmp; + } + | unique_priority K_casez '(' expression ')' case_items K_endcase + { PCase*tmp = new PCase($1, NetCase::EQZ, $4, $6); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | unique_priority K_case '(' expression ')' error K_endcase + { yyerrok; } + | unique_priority K_casex '(' expression ')' error K_endcase + { yyerrok; } + | unique_priority K_casez '(' expression ')' error K_endcase + { yyerrok; } + | K_if '(' expression ')' statement_or_null %prec less_than_K_else { PCondit*tmp = new PCondit($3, $5, 0); FILE_NAME(tmp, @1); @@ -6525,7 +6859,7 @@ $$ = tmp; } | K_wait K_fork ';' - { PEventStatement*tmp = new PEventStatement(0); + { PEventStatement*tmp = new PEventStatement((PEEvent*)0); FILE_NAME(tmp,@1); $$ = tmp; } @@ -7028,10 +7362,18 @@ } ; +unique_priority + : { $$ = IVL_CASE_QUALITY_BASIC; } + | K_unique { $$ = IVL_CASE_QUALITY_UNIQUE; } + | K_unique0 { $$ = IVL_CASE_QUALITY_UNIQUE0; } + | K_priority { $$ = IVL_CASE_QUALITY_PRIORITY; } + ; + /* Many keywords can be optional in the syntax, although their presence is significant. This is a fairly common pattern so collect those rules here. */ +K_genvar_opt : K_genvar { $$ = true; } | { $$ = false; } ; K_packed_opt : K_packed { $$ = true; } | { $$ = false; } ; K_reg_opt : K_reg { $$ = true; } | { $$ = false; } ; K_static_opt : K_static { $$ = true; } | { $$ = false; } ; diff -Nru iverilog-10.3/PClass.cc iverilog-11.0/PClass.cc --- iverilog-10.3/PClass.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PClass.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2019 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 @@ -28,3 +28,8 @@ PClass::~PClass() { } + +PNamedItem::SymbolType PClass::symbol_type() const +{ + return CLASS; +} diff -Nru iverilog-10.3/PClass.h iverilog-11.0/PClass.h --- iverilog-10.3/PClass.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PClass.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PClass_H #define IVL_PClass_H /* - * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2019 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 @@ -20,7 +20,7 @@ */ # include "PScope.h" -# include "LineInfo.h" +# include "PNamedItem.h" # include "StringHeap.h" # include @@ -32,7 +32,7 @@ * collected. */ -class PClass : public PScopeExtra, public LineInfo { +class PClass : public PScopeExtra, public PNamedItem { public: explicit PClass (perm_string name, LexicalScope*parent); @@ -40,6 +40,8 @@ void dump(std::ostream&out, unsigned indent) const; + SymbolType symbol_type() const; + public: class_type_t*type; }; diff -Nru iverilog-10.3/PEvent.cc iverilog-11.0/PEvent.cc --- iverilog-10.3/PEvent.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PEvent.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2019 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 @@ -35,3 +35,7 @@ return name_; } +PNamedItem::SymbolType PEvent::symbol_type() const +{ + return EVENT; +} diff -Nru iverilog-10.3/PEvent.h iverilog-11.0/PEvent.h --- iverilog-10.3/PEvent.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PEvent.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PEvent_H #define IVL_PEvent_H /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2019 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 @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -# include "LineInfo.h" +# include "PNamedItem.h" # include "StringHeap.h" # include @@ -31,7 +31,7 @@ * are declared in Verilog as ``event foo;'' The name passed to the * constructor is the "foo" part of the declaration. */ -class PEvent : public LineInfo { +class PEvent : public PNamedItem { public: // The name is a perm-allocated string. It is the simple name @@ -43,6 +43,8 @@ void elaborate_scope(Design*des, NetScope*scope) const; + SymbolType symbol_type() const; + private: perm_string name_; diff -Nru iverilog-10.3/PExpr.cc iverilog-11.0/PExpr.cc --- iverilog-10.3/PExpr.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PExpr.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2016 Stephen Williams + * Copyright (c) 1998-2020 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -126,9 +126,8 @@ void PEBinary::declare_implicit_nets(LexicalScope*scope, NetNet::Type type) { - assert(left_ && right_); - left_->declare_implicit_nets(scope, type); - right_->declare_implicit_nets(scope, type); + if (left_) left_->declare_implicit_nets(scope, type); + if (right_) right_->declare_implicit_nets(scope, type); } bool PEBinary::has_aa_term(Design*des, NetScope*scope) const @@ -169,7 +168,7 @@ PEBLogic::PEBLogic(char op, PExpr*l, PExpr*r) : PEBinary(op, l, r) { - assert(op == 'a' || op == 'o'); + assert(op == 'a' || op == 'o' || op == 'q' || op == 'Q'); } PEBLogic::~PEBLogic() @@ -204,12 +203,12 @@ } PECallFunction::PECallFunction(const pform_name_t&n, const vector &parms) -: package_(0), path_(n), parms_(parms) +: package_(0), path_(n), parms_(parms), is_overridden_(false) { } PECallFunction::PECallFunction(PPackage*pkg, const pform_name_t&n, const vector &parms) -: package_(pkg), path_(n), parms_(parms) +: package_(pkg), path_(n), parms_(parms), is_overridden_(false) { } @@ -222,7 +221,7 @@ } PECallFunction::PECallFunction(PPackage*pkg, perm_string n, const list &parms) -: package_(pkg), path_(pn_from_ps(n)), parms_(parms.size()) +: package_(pkg), path_(pn_from_ps(n)), parms_(parms.size()), is_overridden_(false) { int tmp_idx = 0; assert(parms_.size() == parms.size()); @@ -232,18 +231,18 @@ } PECallFunction::PECallFunction(perm_string n, const vector&parms) -: package_(0), path_(pn_from_ps(n)), parms_(parms) +: package_(0), path_(pn_from_ps(n)), parms_(parms), is_overridden_(false) { } PECallFunction::PECallFunction(perm_string n) -: package_(0), path_(pn_from_ps(n)) +: package_(0), path_(pn_from_ps(n)), is_overridden_(false) { } // NOTE: Anachronism. Try to work all use of svector out. PECallFunction::PECallFunction(const pform_name_t&n, const list &parms) -: package_(0), path_(n), parms_(parms.size()) +: package_(0), path_(n), parms_(parms.size()), is_overridden_(false) { int tmp_idx = 0; assert(parms_.size() == parms.size()); @@ -253,7 +252,7 @@ } PECallFunction::PECallFunction(perm_string n, const list&parms) -: package_(0), path_(pn_from_ps(n)), parms_(parms.size()) +: package_(0), path_(pn_from_ps(n)), parms_(parms.size()), is_overridden_(false) { int tmp_idx = 0; assert(parms_.size() == parms.size()); diff -Nru iverilog-10.3/PExpr.h iverilog-11.0/PExpr.h --- iverilog-10.3/PExpr.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PExpr.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PExpr_H #define IVL_PExpr_H /* - * Copyright (c) 1998-2016 Stephen Williams + * Copyright (c) 1998-2019 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -379,6 +379,8 @@ virtual bool is_collapsible_net(Design*des, NetScope*scope) const; + const PPackage* package() const { return package_; } + const pform_name_t& path() const { return path_; } private: @@ -433,10 +435,10 @@ bool need_const_idx) const; NetAssign_*elaborate_lval_net_class_member_(Design*, NetScope*, NetNet*, - const perm_string&) const; + pform_name_t) const; bool elaborate_lval_net_packed_member_(Design*, NetScope*, NetAssign_*, - const name_component_t&) const; + pform_name_t member_path) const; bool elaborate_lval_darray_bit_(Design*, NetScope*, NetAssign_*) const; @@ -521,17 +523,13 @@ unsigned flags) const; unsigned test_width_method_(Design*des, NetScope*scope, width_mode_t&mode); - NetExpr*elaborate_expr_method_(Design*des, - NetScope*scope, - unsigned expr_wid, - unsigned flags) const; + private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, bool bidirectional_flag) const; - NetAssign_*scan_lname_for_nested_members_(Design*des, NetScope*scope, - const pform_name_t&path) const; + bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig, long&midx, long&lidx) const; }; @@ -930,6 +928,9 @@ pform_name_t path_; std::vector parms_; + // For system functions. + bool is_overridden_; + bool check_call_matches_definition_(Design*des, NetScope*dscope) const; diff -Nru iverilog-10.3/pform.cc iverilog-11.0/pform.cc --- iverilog-10.3/pform.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -63,20 +63,19 @@ map pform_primitives; /* - * typedefs in the $root scope go here. - */ -mappform_typedefs; -setpform_enum_sets; - -/* - * Class definitions in the $root scope go here. - */ -vector pform_classes; - -/* - * Task and function definitions in the $root scope go here. - */ -map pform_tasks; + * The pform_units is a list of the SystemVerilog compilation unit scopes. + * The current compilation unit is the last element in the list. All items + * declared or defined at the top level (outside any design element) are + * added to the current compilation unit scope. + */ +vector pform_units; + +static bool is_compilation_unit(LexicalScope*scope) +{ + // A compilation unit is the only scope that doesn't have a parent. + assert(scope); + return scope->parent_scope() == 0; +} std::string vlltype::get_fileline() const { @@ -350,6 +349,13 @@ always within a module. */ static PGenerate*pform_cur_generate = 0; + /* Blocks within the same conditional generate construct may have + the same name. Here we collect the set of names used in each + construct, so they can be added to the local scope without + conflicting with each other. Generate constructs may nest, so + we need a stack. */ +static list > conditional_block_names; + /* This tracks the current modport list being processed. This is always within an interface. */ static PModport*pform_cur_modport = 0; @@ -357,30 +363,27 @@ static NetNet::Type pform_default_nettype = NetNet::WIRE; /* - * These variables track the current time scale, as well as where the - * timescale was set. This supports warnings about tangled timescales. + * These variables track the time scale set by the most recent `timescale + * directive. Time scales set by SystemVerilog timeunit and timeprecision + * declarations are stored directly in the current lexical scope. */ static int pform_time_unit; static int pform_time_prec; -/* These two flags check the initial timeprecision and timeunit - * declaration inside a module. - */ -static bool tp_decl_flag = false; -static bool tu_decl_flag = false; - /* - * Flags used to set time_from_timescale based on timeunit and - * timeprecision. + * These variables track where the most recent `timescale directive + * occurred. This allows us to warn about time scales that are inherited + * from another file. */ -static bool tu_global_flag = false; -static bool tp_global_flag = false; -static bool tu_local_flag = false; -static bool tp_local_flag = false; - static char*pform_timescale_file = 0; static unsigned pform_timescale_line; +/* + * These variables track whether we can accept new timeunits declarations. + */ +bool allow_timeunit_decl = true; +bool allow_timeprec_decl = true; + static inline void FILE_NAME(LineInfo*obj, const char*file, unsigned lineno) { obj->set_lineno(lineno); @@ -404,8 +407,18 @@ void pform_pop_scope() { + LexicalScope*scope = lexical_scope; + assert(scope); + + map::const_iterator cur; + for (cur = scope->possible_imports.begin(); cur != scope->possible_imports.end(); ++cur) { + if (scope->local_symbols.find(cur->first) == scope->local_symbols.end()) + scope->explicit_imports[cur->first] = cur->second; + } + scope->possible_imports.clear(); + + lexical_scope = scope->parent_scope(); assert(lexical_scope); - lexical_scope = lexical_scope->parent_scope(); } static LexicalScope::lifetime_t find_lifetime(LexicalScope::lifetime_t lifetime) @@ -413,10 +426,7 @@ if (lifetime != LexicalScope::INHERITED) return lifetime; - if (lexical_scope != 0) - return lexical_scope->default_lifetime; - - return LexicalScope::STATIC; + return lexical_scope->default_lifetime; } static PScopeExtra* find_nearest_scopex(LexicalScope*scope) @@ -429,18 +439,143 @@ return scopex; } -/* - * Set the local time unit/precision to the global value. - */ -static void pform_set_scope_timescale(PScope*scope, const struct vlltype&loc) +static void add_local_symbol(LexicalScope*scope, perm_string name, PNamedItem*item) +{ + assert(scope); + + // Check for conflict with another local symbol. + map::const_iterator cur_sym + = scope->local_symbols.find(name); + if (cur_sym != scope->local_symbols.end()) { + cerr << item->get_fileline() << ": error: " + "'" << name << "' has already been declared " + "in this scope." << endl; + cerr << cur_sym->second->get_fileline() << ": : " + "It was declared here as " + << cur_sym->second->symbol_type() << "." << endl; + error_count += 1; + return; + } + + // Check for conflict with an explicit import. + map::const_iterator cur_pkg + = scope->explicit_imports.find(name); + if (cur_pkg != scope->explicit_imports.end()) { + cerr << item->get_fileline() << ": error: " + "'" << name << "' has already been " + "imported into this scope from package '" + << cur_pkg->second->pscope_name() << "'." << endl; + error_count += 1; + return; + } + + scope->local_symbols[name] = item; +} + +static PPackage*find_potential_import(const struct vlltype&loc, LexicalScope*scope, + perm_string name, bool tf_call, bool make_explicit) +{ + assert(scope); + + PPackage*found_pkg = 0; + for (set::const_iterator cur_pkg = scope->potential_imports.begin(); + cur_pkg != scope->potential_imports.end(); ++cur_pkg) { + PPackage*search_pkg = *cur_pkg; + map::const_iterator cur_sym + = search_pkg->local_symbols.find(name); + if (cur_sym != search_pkg->local_symbols.end()) { + if (found_pkg && make_explicit) { + cerr << loc.get_fileline() << ": error: " + "Ambiguous use of '" << name << "'. " + "It is exported by both '" + << found_pkg->pscope_name() + << "' and by '" + << search_pkg->pscope_name() + << "'." << endl; + error_count += 1; + } else { + found_pkg = search_pkg; + if (make_explicit) { + if (tf_call) + scope->possible_imports[name] = found_pkg; + else + scope->explicit_imports[name] = found_pkg; + } + } + } + } + return found_pkg; +} + +static void check_potential_imports(const struct vlltype&loc, perm_string name, bool tf_call) { - scope->time_unit = pform_time_unit; - scope->time_precision = pform_time_prec; - /* If we have a timescale file then the time information is from - * a timescale directive. */ - scope->time_from_timescale = pform_timescale_file != 0; + LexicalScope*scope = lexical_scope; + while (scope) { + if (scope->local_symbols.find(name) != scope->local_symbols.end()) + return; + if (scope->explicit_imports.find(name) != scope->explicit_imports.end()) + return; + if (find_potential_import(loc, scope, name, tf_call, true)) + return; + + scope = scope->parent_scope(); + } +} + +/* + * Set the local time unit/precision. This version is used for setting + * the time scale for design elements (modules, packages, etc.) and is + * called after any initial timeunit and timeprecision declarations + * have been parsed. + */ +void pform_set_scope_timescale(const struct vlltype&loc) +{ + PScopeExtra*scope = dynamic_cast(lexical_scope); + assert(scope); + + PScopeExtra*parent = find_nearest_scopex(scope->parent_scope()); + + bool used_global_timescale = false; + if (scope->time_unit_is_default) { + if (is_compilation_unit(scope)) { + scope->time_unit = def_ts_units; + } else if (!is_compilation_unit(parent)) { + scope->time_unit = parent->time_unit; + scope->time_unit_is_default = parent->time_unit_is_default; + } else if (pform_timescale_file != 0) { + scope->time_unit = pform_time_unit; + scope->time_unit_is_default = false; + used_global_timescale = true; + } else /* parent is compilation unit */ { + scope->time_unit = parent->time_unit; + scope->time_unit_is_default = parent->time_unit_is_default; + } + } + if (scope->time_prec_is_default) { + if (is_compilation_unit(scope)) { + scope->time_precision = def_ts_prec; + } else if (!is_compilation_unit(parent)) { + scope->time_precision = parent->time_precision; + scope->time_prec_is_default = parent->time_prec_is_default; + } else if (pform_timescale_file != 0) { + scope->time_precision = pform_time_prec; + scope->time_prec_is_default = false; + used_global_timescale = true; + } else { + scope->time_precision = parent->time_precision; + scope->time_prec_is_default = parent->time_prec_is_default; + } + } + + if (gn_system_verilog() && (scope->time_unit < scope->time_precision)) { + if (scope->time_unit_is_local || scope->time_prec_is_local) { + VLerror("error: a timeprecision is missing or is too large!"); + } + } else { + assert(scope->time_unit >= scope->time_precision); + } - if (warn_timescale && (lexical_scope == 0) && pform_timescale_file + if (warn_timescale && used_global_timescale && (strcmp(pform_timescale_file, loc.text) != 0)) { cerr << loc.get_fileline() << ": warning: " @@ -449,6 +584,22 @@ cerr << pform_timescale_file << ":" << pform_timescale_line << ": ...: The inherited timescale is here." << endl; } + + allow_timeunit_decl = false; + allow_timeprec_decl = false; +} + +/* + * Set the local time unit/precision. This version is used for setting + * the time scale for subsidiary items (classes, subroutines, etc.), + * which simply inherit their time scale from their parent scope. + */ +static void pform_set_scope_timescale(PScope*scope, const PScope*parent) +{ + scope->time_unit = parent->time_unit; + scope->time_precision = parent->time_precision; + scope->time_unit_is_default = parent->time_unit_is_default; + scope->time_prec_is_default = parent->time_prec_is_default; } PClass* pform_push_class_scope(const struct vlltype&loc, perm_string name, @@ -458,26 +609,12 @@ class_scope->default_lifetime = find_lifetime(lifetime); FILE_NAME(class_scope, loc); - pform_set_scope_timescale(class_scope, loc); - PScopeExtra*scopex = find_nearest_scopex(lexical_scope); - + assert(scopex); assert(!pform_cur_generate); - /* If no scope was found then this is being defined in the - * compilation unit scope. */ - if (scopex == 0) { - pform_classes.push_back(class_scope); - lexical_scope = class_scope; - return class_scope; - } + pform_set_scope_timescale(class_scope, scopex); - if (scopex->classes.find(name) != scopex->classes.end()) { - cerr << class_scope->get_fileline() << ": error: duplicate " - "definition for class '" << name << "' in '" - << scopex->pscope_name() << "'." << endl; - error_count += 1; - } scopex->classes[name] = class_scope; scopex->classes_lexical .push_back(class_scope); @@ -492,7 +629,8 @@ pkg_scope->default_lifetime = find_lifetime(lifetime); FILE_NAME(pkg_scope, loc); - pform_set_scope_timescale(pkg_scope, loc); + allow_timeunit_decl = true; + allow_timeprec_decl = true; lexical_scope = pkg_scope; return pkg_scope; @@ -510,44 +648,22 @@ task->default_lifetime = default_lifetime; FILE_NAME(task, loc); - pform_set_scope_timescale(task, loc); - PScopeExtra*scopex = find_nearest_scopex(lexical_scope); - if ((scopex == 0) && !gn_system_verilog()) { + assert(scopex); + if (is_compilation_unit(scopex) && !gn_system_verilog()) { cerr << task->get_fileline() << ": error: task declarations " "must be contained within a module." << endl; error_count += 1; } - if (pform_cur_generate) { - // Check if the task is already in the dictionary. - if (pform_cur_generate->tasks.find(task->pscope_name()) != - pform_cur_generate->tasks.end()) { - cerr << task->get_fileline() << ": error: duplicate " - "definition for task '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "' (generate)." - << endl; - error_count += 1; - } - pform_cur_generate->tasks[task->pscope_name()] = task; - } else if (scopex) { - // Check if the task is already in the dictionary. - if (scopex->tasks.find(task->pscope_name()) != scopex->tasks.end()) { - cerr << task->get_fileline() << ": error: duplicate " - "definition for task '" << name << "' in '" - << scopex->pscope_name() << "'." << endl; - error_count += 1; - } - scopex->tasks[task->pscope_name()] = task; + pform_set_scope_timescale(task, scopex); + if (pform_cur_generate) { + add_local_symbol(pform_cur_generate, task_name, task); + pform_cur_generate->tasks[task_name] = task; } else { - if (pform_tasks.find(task_name) != pform_tasks.end()) { - cerr << task->get_fileline() << ": error: " - << "Duplicate definition for task '" << name - << "' in $root scope." << endl; - error_count += 1; - } - pform_tasks[task_name] = task; + add_local_symbol(scopex, task_name, task); + scopex->tasks[task_name] = task; } lexical_scope = task; @@ -567,45 +683,23 @@ func->default_lifetime = default_lifetime; FILE_NAME(func, loc); - pform_set_scope_timescale(func, loc); - PScopeExtra*scopex = find_nearest_scopex(lexical_scope); - if ((scopex == 0) && !gn_system_verilog()) { + assert(scopex); + if (is_compilation_unit(scopex) && !gn_system_verilog()) { cerr << func->get_fileline() << ": error: function declarations " "must be contained within a module." << endl; error_count += 1; } - if (pform_cur_generate) { - // Check if the function is already in the dictionary. - if (pform_cur_generate->funcs.find(func->pscope_name()) != - pform_cur_generate->funcs.end()) { - cerr << func->get_fileline() << ": error: duplicate " - "definition for function '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "' (generate)." - << endl; - error_count += 1; - } - pform_cur_generate->funcs[func->pscope_name()] = func; + pform_set_scope_timescale(func, scopex); - } else if (scopex != 0) { - // Check if the function is already in the dictionary. - if (scopex->funcs.find(func->pscope_name()) != scopex->funcs.end()) { - cerr << func->get_fileline() << ": error: duplicate " - "definition for function '" << name << "' in '" - << scopex->pscope_name() << "'." << endl; - error_count += 1; - } - scopex->funcs[func->pscope_name()] = func; + if (pform_cur_generate) { + add_local_symbol(pform_cur_generate, func_name, func); + pform_cur_generate->funcs[func_name] = func; } else { - if (pform_tasks.find(func_name) != pform_tasks.end()) { - cerr << func->get_fileline() << ": error: " - << "Duplicate definition for function '" << name - << "' in $root scope." << endl; - error_count += 1; - } - pform_tasks[func_name] = func; + add_local_symbol(scopex, func_name, func); + scopex->funcs[func_name] = func; } lexical_scope = func; @@ -613,7 +707,8 @@ return func; } -PBlock* pform_push_block_scope(char*name, PBlock::BL_TYPE bt) +PBlock* pform_push_block_scope(const struct vlltype&loc, char*name, + PBlock::BL_TYPE bt) { perm_string block_name; if (name) block_name = lex_strings.make(name); @@ -627,26 +722,34 @@ } PBlock*block = new PBlock(block_name, lexical_scope, bt); + FILE_NAME(block, loc); block->default_lifetime = find_lifetime(LexicalScope::INHERITED); + if (name) add_local_symbol(lexical_scope, block_name, block); lexical_scope = block; return block; } /* - * Create a new identifier. Check if this is an imported name. + * Create a new identifier. */ -PEIdent* pform_new_ident(const pform_name_t&name) +PEIdent* pform_new_ident(const struct vlltype&loc, const pform_name_t&name) { - LexicalScope*scope = pform_peek_scope(); - map::const_iterator pkg = scope->imports.find(name.front().name); - if (pkg == scope->imports.end()) - return new PEIdent(name); + if (gn_system_verilog()) + check_potential_imports(loc, name.front().name, false); - // XXXX For now, do not support indexed imported names. - assert(name.back().index.size() == 0); + return new PEIdent(name); +} + +PTrigger* pform_new_trigger(const struct vlltype&loc, PPackage*pkg, + const pform_name_t&name) +{ + if (gn_system_verilog()) + check_potential_imports(loc, name.front().name, false); - return new PEIdent(pkg->second, name); + PTrigger*tmp = new PTrigger(pkg, name); + FILE_NAME(tmp, loc); + return tmp; } PGenerate* pform_parent_generate(void) @@ -700,29 +803,50 @@ static void pform_put_wire_in_scope(perm_string name, PWire*net) { + add_local_symbol(lexical_scope, name, net); lexical_scope->wires[name] = net; } static void pform_put_enum_type_in_scope(enum_type_t*enum_set) { - if (lexical_scope) { - ivl_assert(*enum_set, lexical_scope); - lexical_scope->enum_sets.insert(enum_set); - } else { - pform_enum_sets.insert(enum_set); + if (lexical_scope->enum_sets.count(enum_set)) + return; + + set enum_names; + list::const_iterator cur; + for (cur = enum_set->names->begin(); cur != enum_set->names->end(); ++cur) { + if (enum_names.count(cur->name)) { + cerr << enum_set->get_fileline() << ": error: " + "Duplicate enumeration name '" + << cur->name << "'." << endl; + error_count += 1; + } else { + add_local_symbol(lexical_scope, cur->name, enum_set); + enum_names.insert(cur->name); + } } + + lexical_scope->enum_sets.insert(enum_set); } -PWire*pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_type, NetNet::PortType port_type, ivl_variable_type_t vt_type) +PWire*pform_get_make_wire_in_scope(const struct vlltype&, perm_string name, + NetNet::Type net_type, NetNet::PortType port_type, + ivl_variable_type_t vt_type) { PWire*cur = pform_get_wire_in_scope(name); + + // If the wire already exists and is fully defined, this + // must be a redeclaration. Start again with a new wire. + // The error will be reported when we add the new wire + // to the scope. Do not delete the old wire - it will + // remain in the local symbol map. + if (cur && cur->get_data_type() != IVL_VT_NO_TYPE) + cur = 0; + if (cur == 0) { cur = new PWire(name, net_type, port_type, vt_type); pform_put_wire_in_scope(name, cur); } else { - // If this is a duplicate wire, the data type has already - // been set, then return NULL. - if (cur->get_data_type() != IVL_VT_NO_TYPE) return 0; bool rc = cur->set_wire_type(net_type); assert(rc); rc = cur->set_data_type(vt_type); @@ -737,40 +861,28 @@ if(unp_ranges) data_type = new uarray_type_t(data_type, unp_ranges); - // If we are in a lexical scope (i.e. a package or module) - // then put the typedef into that scope. Otherwise, put it - // into the $root scope. - data_type_t*&ref = lexical_scope - ? lexical_scope->typedefs[name] - : pform_typedefs[name]; + add_local_symbol(lexical_scope, name, data_type); + + data_type_t*&ref = lexical_scope->typedefs[name]; ivl_assert(*data_type, ref == 0); ref = data_type; + ref->name = name; - if (enum_type_t*enum_type = dynamic_cast(data_type)) { + if (enum_type_t*enum_type = dynamic_cast(data_type)) pform_put_enum_type_in_scope(enum_type); - } } -static data_type_t* test_type_identifier_in_root(perm_string name) +void pform_set_type_referenced(const struct vlltype&loc, const char*name) { - map::iterator cur = pform_typedefs.find(name); - if (cur != pform_typedefs.end()) - return cur->second; - else - return 0; + perm_string lex_name = lex_strings.make(name); + check_potential_imports(loc, lex_name, false); } -data_type_t* pform_test_type_identifier(const char*txt) +data_type_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) { perm_string name = lex_strings.make(txt); - // If there is no lexical_scope yet, then look only in the - // $root scope for typedefs. - if (lexical_scope == 0) { - return test_type_identifier_in_root(name); - } - LexicalScope*cur_scope = lexical_scope; do { map::iterator cur; @@ -782,8 +894,8 @@ // the name has at least shadowed any other possible // meaning for this name. map::iterator cur_pkg; - cur_pkg = cur_scope->imports.find(name); - if (cur_pkg != cur_scope->imports.end()) { + cur_pkg = cur_scope->explicit_imports.find(name); + if (cur_pkg != cur_scope->explicit_imports.end()) { PPackage*pkg = cur_pkg->second; cur = pkg->typedefs.find(name); if (cur != pkg->typedefs.end()) @@ -797,13 +909,19 @@ if (cur != cur_scope->typedefs.end()) return cur->second; + PPackage*pkg = find_potential_import(loc, cur_scope, name, false, false); + if (pkg) { + cur = pkg->typedefs.find(name); + if (cur != pkg->typedefs.end()) + return cur->second; + + // Not a type. Give up. + return 0; + } + cur_scope = cur_scope->parent_scope(); } while (cur_scope); - // See if there is a typedef in the $root scope. - if (data_type_t*tmp = test_type_identifier_in_root(name)) - return tmp; - return 0; } @@ -814,13 +932,6 @@ */ bool pform_test_type_identifier_local(perm_string name) { - if (lexical_scope == 0) { - if (test_type_identifier_in_root(name)) - return true; - else - return false; - } - LexicalScope*cur_scope = lexical_scope; map::iterator cur; @@ -836,29 +947,10 @@ const pform_name_t&name, const list&parms) { - PECallFunction*tmp = 0; - - // First try to get the function name from a package. Check - // the imports, and if the name is there, make the function as - // a package member. - do { - if (name.size() != 1) - break; - - perm_string use_name = peek_tail_name(name); - - map::iterator cur_pkg; - cur_pkg = lexical_scope->imports.find(use_name); - if (cur_pkg == lexical_scope->imports.end()) - break; - - tmp = new PECallFunction(cur_pkg->second, use_name, parms); - } while(0); - - if (tmp == 0) { - tmp = new PECallFunction(name, parms); - } + if (gn_system_verilog()) + check_potential_imports(loc, name.front().name, true); + PECallFunction*tmp = new PECallFunction(name, parms); FILE_NAME(tmp, loc); return tmp; } @@ -867,26 +959,10 @@ const pform_name_t&name, const list&parms) { - PCallTask*tmp = 0; - - do { - if (name.size() != 1) - break; - - perm_string use_name = peek_tail_name(name); - - map::iterator cur_pkg; - cur_pkg = lexical_scope->imports.find(use_name); - if (cur_pkg == lexical_scope->imports.end()) - break; - - tmp = new PCallTask(cur_pkg->second, name, parms); - } while (0); - - if (tmp == 0) { - tmp = new PCallTask(name, parms); - } + if (gn_system_verilog()) + check_potential_imports(loc, name.front().name, true); + PCallTask*tmp = new PCallTask(name, parms); FILE_NAME(tmp, loc); return tmp; } @@ -971,64 +1047,23 @@ /* * The lexor calls this function to set the active timescale when it * detects a `timescale directive. The function saves the directive - * values (for use by modules) and if warnings are enabled checks to - * see if some modules have no timescale. + * values (for use by subsequent design elements) and if warnings are + * enabled checks to see if some design elements have no timescale. */ void pform_set_timescale(int unit, int prec, const char*file, unsigned lineno) { - bool first_flag = true; - assert(unit >= prec); pform_time_unit = unit; pform_time_prec = prec; - /* A `timescale clears the timeunit/timeprecision state. */ - tu_global_flag = false; - tp_global_flag = false; if (pform_timescale_file) { free(pform_timescale_file); - first_flag = false; } if (file) pform_timescale_file = strdup(file); else pform_timescale_file = 0; pform_timescale_line = lineno; - - if (!warn_timescale || !first_flag || !file) return; - - /* Look to see if we have any modules without a timescale. */ - bool have_no_ts = false; - map::iterator mod; - for (mod = pform_modules.begin(); mod != pform_modules.end(); ++ mod ) { - const Module*mp = (*mod).second; - if (mp->time_from_timescale || - mp->timescale_warn_done) continue; - have_no_ts = true; - break; - } - - /* If we do then print a message for the new ones. */ - if (have_no_ts) { - cerr << file << ":" << lineno << ": warning: " - << "Some modules have no timescale. This may cause" - << endl; - cerr << file << ":" << lineno << ": : " - << "confusing timing results. Affected modules are:" - << endl; - - for (mod = pform_modules.begin() - ; mod != pform_modules.end() ; ++ mod ) { - Module*mp = (*mod).second; - if (mp->time_from_timescale || - mp->timescale_warn_done) continue; - mp->timescale_warn_done = true; - - cerr << file << ":" << lineno << ": : " - << " -- module " << (*mod).first - << " declared here: " << mp->get_fileline() << endl; - } - } } bool get_time_unit(const char*cp, int &unit) @@ -1148,70 +1183,56 @@ return true; } -void pform_set_timeunit(const char*txt, bool in_module, bool only_check) +void pform_set_timeunit(const char*txt, bool initial_decl) { int val; if (get_time_unit_prec(txt, val, true)) return; - if (in_module) { - if (!only_check) { - pform_cur_module.front()->time_unit = val; - tu_decl_flag = true; - tu_local_flag = true; - } else if (!tu_decl_flag) { - VLerror(yylloc, "error: repeat timeunit found and the " - "initial module timeunit is missing."); - return; - } else if (pform_cur_module.front()->time_unit != val) { - VLerror(yylloc, "error: repeat timeunit does not match " - "the initial module timeunit " - "declaration."); - return; - } + PScopeExtra*scope = dynamic_cast(lexical_scope); + assert(scope); - } else { - /* Skip a global timeunit when `timescale is defined. */ - if (pform_timescale_file) return; - tu_global_flag = true; - pform_time_unit = val; + if (initial_decl) { + scope->time_unit = val; + scope->time_unit_is_local = true; + scope->time_unit_is_default = false; + allow_timeunit_decl = false; + } else if (!scope->time_unit_is_local) { + VLerror(yylloc, "error: repeat timeunit found and the initial " + "timeunit for this scope is missing."); + } else if (scope->time_unit != val) { + VLerror(yylloc, "error: repeat timeunit does not match the " + "initial timeunit for this scope."); } } int pform_get_timeunit() { - if (pform_cur_module.empty()) - return pform_time_unit; - else - return pform_cur_module.front()->time_unit; + PScopeExtra*scopex = find_nearest_scopex(lexical_scope); + assert(scopex); + return scopex->time_unit; } -void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) +void pform_set_timeprec(const char*txt, bool initial_decl) { int val; if (get_time_unit_prec(txt, val, false)) return; - if (in_module) { - if (!only_check) { - pform_cur_module.front()->time_precision = val; - tp_decl_flag = true; - tp_local_flag = true; - } else if (!tp_decl_flag) { - VLerror(yylloc, "error: repeat timeprecision found and the " - "initial module timeprecision is missing."); - return; - } else if (pform_cur_module.front()->time_precision != val) { - VLerror(yylloc, "error: repeat timeprecision does not match " - "the initial module timeprecision " - "declaration."); - return; - } - } else { - /* Skip a global timeprecision when `timescale is defined. */ - if (pform_timescale_file) return; - pform_time_prec = val; - tp_global_flag=true; + PScopeExtra*scope = dynamic_cast(lexical_scope); + assert(scope); + + if (initial_decl) { + scope->time_precision = val; + scope->time_prec_is_local = true; + scope->time_prec_is_default = false; + allow_timeprec_decl = false; + } else if (!scope->time_prec_is_local) { + VLerror(yylloc, "error: repeat timeprecision found and the initial " + "timeprecision for this scope is missing."); + } else if (scope->time_precision != val) { + VLerror(yylloc, "error: repeat timeprecision does not match the " + "initial timeprecision for this scope."); } } @@ -1229,16 +1250,21 @@ verinum::V pad; - switch (val->get(val->len()-1)) { - case verinum::Vz: - pad = verinum::Vz; - break; - case verinum::Vx: + if (val->len() == 0) { pad = verinum::Vx; - break; - default: - pad = verinum::V0; - break; + } else { + + switch (val->get(val->len()-1)) { + case verinum::Vz: + pad = verinum::Vz; + break; + case verinum::Vx: + pad = verinum::Vx; + break; + default: + pad = verinum::V0; + break; + } } verinum*res = new verinum(pad, size, true); @@ -1311,14 +1337,15 @@ FILE_NAME(cur_module, loc); - pform_set_scope_timescale(cur_module, loc); - tu_local_flag = tu_global_flag; - tp_local_flag = tp_global_flag; - cur_module->library_flag = pform_library_flag; pform_cur_module.push_front(cur_module); + allow_timeunit_decl = true; + allow_timeprec_decl = true; + + add_local_symbol(lexical_scope, lex_name, cur_module); + lexical_scope = cur_module; /* The generate scheme numbering starts with *1*, not @@ -1329,21 +1356,6 @@ } /* - * In SystemVerilog we can have separate timeunit and timeprecision - * declarations. We need to have the values worked out by time this - * task is called. - */ -void pform_check_timeunit_prec() -{ - assert(! pform_cur_module.empty()); - if (gn_system_verilog() && - (pform_cur_module.front()->time_unit < pform_cur_module.front()->time_precision)) { - VLerror("error: a timeprecision is missing or is too large!"); - } else assert(pform_cur_module.front()->time_unit >= - pform_cur_module.front()->time_precision); -} - -/* * This function is called by the parser to make a simple port * reference. This is a name without a .X(...), so the internal name * should be generated to be the same as the X. @@ -1382,14 +1394,49 @@ void pform_endmodule(const char*name, bool inside_celldefine, Module::UCDriveType uc_drive_def) { + // The parser will not call pform_endmodule() without first + // calling pform_startmodule(). Thus, it is impossible for the + // pform_cur_module stack to be empty at this point. assert(! pform_cur_module.empty()); Module*cur_module = pform_cur_module.front(); pform_cur_module.pop_front(); - - cur_module->time_from_timescale = (tu_local_flag && tp_local_flag) - || (pform_timescale_file != 0); perm_string mod_name = cur_module->mod_name(); + + // Oops, there may be some sort of nesting problem. If + // SystemVerilog is activated, it is possible for modules to + // be nested. But if the nested module is broken, the parser + // will recover and treat is as an invalid module item, + // leaving the pform_cur_module stack in an inconsistent + // state. For example, this: + // module foo; + // module bar blah blab blah error; + // endmodule + // may leave the pform_cur_module stack with the dregs of the + // bar module. Try to find the foo module in the stack, and + // print error messages as we go. + if (strcmp(name, mod_name) != 0) { + while (pform_cur_module.size() > 0) { + Module*tmp_module = pform_cur_module.front(); + perm_string tmp_name = tmp_module->mod_name(); + pform_cur_module.pop_front(); + ostringstream msg; + msg << "Module " << mod_name + << " was nested within " << tmp_name + << " but broken."; + VLerror(msg.str().c_str()); + + ivl_assert(*cur_module, lexical_scope == cur_module); + pform_pop_scope(); + delete cur_module; + + cur_module = tmp_module; + mod_name = tmp_name; + if (strcmp(name, mod_name) == 0) + break; + } + } assert(strcmp(name, mod_name) == 0); + cur_module->is_cell = inside_celldefine; cur_module->uc_drive = uc_drive_def; @@ -1414,49 +1461,32 @@ use_module_map[mod_name] = cur_module; } - // The current lexical scope should be this module by now, and - // this module should not have a parent lexical scope. + // The current lexical scope should be this module by now. ivl_assert(*cur_module, lexical_scope == cur_module); pform_pop_scope(); - ivl_assert(*cur_module, ! pform_cur_module.empty() || lexical_scope == 0); - - tp_decl_flag = false; - tu_decl_flag = false; - tu_local_flag = false; - tp_local_flag = false; -} - -static void pform_add_genvar(const struct vlltype&li, const perm_string&name, - map&genvars) -{ - LineInfo*lni = new LineInfo(); - FILE_NAME(lni, li); - if (genvars.find(name) != genvars.end()) { - cerr << lni->get_fileline() << ": error: genvar '" - << name << "' has already been declared." << endl; - cerr << genvars[name]->get_fileline() - << ": the previous declaration is here." << endl; - error_count += 1; - delete lni; - } else { - genvars[name] = lni; - } } void pform_genvars(const struct vlltype&li, list*names) { list::const_iterator cur; for (cur = names->begin(); cur != names->end() ; *cur++) { - if (pform_cur_generate) - pform_add_genvar(li, *cur, pform_cur_generate->genvars); - else - pform_add_genvar(li, *cur, pform_cur_module.front()->genvars); + PGenvar*genvar = new PGenvar(); + FILE_NAME(genvar, li); + + if (pform_cur_generate) { + add_local_symbol(pform_cur_generate, *cur, genvar); + pform_cur_generate->genvars[*cur] = genvar; + } else { + add_local_symbol(pform_cur_module.front(), *cur, genvar); + pform_cur_module.front()->genvars[*cur] = genvar; + } } delete names; } void pform_start_generate_for(const struct vlltype&li, + bool local_index, char*ident1, PExpr*init, PExpr*test, char*ident2, PExpr*next) @@ -1470,6 +1500,7 @@ pform_cur_generate->scheme_type = PGenerate::GS_LOOP; + pform_cur_generate->local_index = local_index; pform_cur_generate->loop_index = lex_strings.make(ident1); pform_cur_generate->loop_init = init; pform_cur_generate->loop_test = test; @@ -1493,6 +1524,8 @@ pform_cur_generate->loop_init = 0; pform_cur_generate->loop_test = test; pform_cur_generate->loop_step = 0; + + conditional_block_names.push_front(set()); } void pform_start_generate_else(const struct vlltype&li) @@ -1501,7 +1534,7 @@ assert(pform_cur_generate->scheme_type == PGenerate::GS_CONDIT); PGenerate*cur = pform_cur_generate; - pform_endgenerate(); + pform_endgenerate(false); PGenerate*gen = new PGenerate(lexical_scope, scope_generate_counter++); lexical_scope = gen; @@ -1535,6 +1568,8 @@ pform_cur_generate->loop_init = 0; pform_cur_generate->loop_test = expr; pform_cur_generate->loop_step = 0; + + conditional_block_names.push_front(set()); } /* @@ -1557,6 +1592,10 @@ pform_cur_generate->scope_name = lex_strings.make(name); delete[]name; + + add_local_symbol(pform_cur_generate->parent_scope(), + pform_cur_generate->scope_name, + pform_cur_generate); } /* @@ -1598,14 +1637,36 @@ { assert(pform_cur_generate != 0); assert(pform_cur_generate->scope_name == 0); - pform_cur_generate->scope_name = lex_strings.make(name); + perm_string scope_name = lex_strings.make(name); + pform_cur_generate->scope_name = scope_name; + + if (pform_cur_generate->scheme_type == PGenerate::GS_CONDIT + || pform_cur_generate->scheme_type == PGenerate::GS_ELSE + || pform_cur_generate->scheme_type == PGenerate::GS_CASE_ITEM) { + + if (conditional_block_names.front().count(scope_name)) + return; + + conditional_block_names.front().insert(scope_name); + } + + LexicalScope*parent_scope = pform_cur_generate->parent_scope(); + assert(parent_scope); + if (pform_cur_generate->scheme_type == PGenerate::GS_CASE_ITEM) + // Skip over the PGenerate::GS_CASE container. + parent_scope = parent_scope->parent_scope(); + + add_local_symbol(parent_scope, scope_name, pform_cur_generate); } -void pform_endgenerate() +void pform_endgenerate(bool end_conditional) { assert(pform_cur_generate != 0); assert(! pform_cur_module.empty()); + if (end_conditional) + conditional_block_names.pop_front(); + // If there is no explicit block name then generate a temporary // name. This will be replaced by the correct name later, once // we know all the explicit names in the surrounding scope. If @@ -1917,7 +1978,7 @@ // Put the primitive into the primitives table if (pform_primitives[name]) { - VLerror("UDP primitive already exists."); + VLwarn("UDP primitive already exists."); } else { PUdp*udp = new PUdp(name, parms->size()); @@ -2094,18 +2155,10 @@ */ static void pform_make_event(perm_string name, const char*fn, unsigned ln) { - // Check if the named event is already in the dictionary. - if (lexical_scope->events.find(name) != lexical_scope->events.end()) { - LineInfo tloc; - FILE_NAME(&tloc, fn, ln); - cerr << tloc.get_fileline() << ": error: duplicate definition " - "for named event '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "'." << endl; - error_count += 1; - } - PEvent*event = new PEvent(name); FILE_NAME(event, fn, ln); + + add_local_symbol(lexical_scope, name, event); lexical_scope->events[name] = event; } @@ -2159,10 +2212,13 @@ cur->strength1(str.str1); FILE_NAME(cur, info.file, info.lineno); - if (pform_cur_generate) + if (pform_cur_generate) { + if (dev_name != "") add_local_symbol(pform_cur_generate, dev_name, cur); pform_cur_generate->add_gate(cur); - else + } else { + if (dev_name != "") add_local_symbol(pform_cur_module.front(), dev_name, cur); pform_cur_module.front()->add_gate(cur); + } } void pform_makegates(const struct vlltype&loc, @@ -2211,7 +2267,8 @@ struct parmvalue_t*overrides, list*wires, PExpr*msb, PExpr*lsb, - const char*fn, unsigned ln) + const char*fn, unsigned ln, + std::list*attr) { for (list::iterator idx = wires->begin() ; idx != wires->end() ; ++idx) { @@ -2238,10 +2295,14 @@ cur->set_parameters(overrides->by_order); } - if (pform_cur_generate) + if (pform_cur_generate) { + if (name != "") add_local_symbol(pform_cur_generate, name, cur); pform_cur_generate->add_gate(cur); - else + } else { + if (name != "") add_local_symbol(pform_cur_module.front(), name, cur); pform_cur_module.front()->add_gate(cur); + } + pform_bind_attributes(cur->attributes, attr); } static void pform_make_modgate(perm_string type, @@ -2249,7 +2310,8 @@ struct parmvalue_t*overrides, list*bind, PExpr*msb, PExpr*lsb, - const char*fn, unsigned ln) + const char*fn, unsigned ln, + std::list*attr) { unsigned npins = bind->size(); named*pins = new named[npins]; @@ -2281,19 +2343,38 @@ cur->set_parameters(overrides->by_order); } - - if (pform_cur_generate) + if (pform_cur_generate) { + add_local_symbol(pform_cur_generate, name, cur); pform_cur_generate->add_gate(cur); - else + } else { + add_local_symbol(pform_cur_module.front(), name, cur); pform_cur_module.front()->add_gate(cur); + } + pform_bind_attributes(cur->attributes, attr); } void pform_make_modgates(const struct vlltype&loc, perm_string type, struct parmvalue_t*overrides, - svector*gates) + svector*gates, + std::list*attr) { + // The grammer should not allow module gates to happen outside + // an active module. But if really bad input errors combine in + // an ugly way with error recovery, then catch this + // implausible situation and return an error. + if (pform_cur_module.empty()) { + cerr << loc << ": internal error: " + << "Module instantiations outside module scope are not possible." + << endl; + error_count += 1; + delete gates; + return; + } assert(! pform_cur_module.empty()); + + // Detect some more realistic errors. + if (pform_cur_module.front()->program_block) { cerr << loc << ": error: Module instantiations are not allowed in " << "program blocks." << endl; @@ -2313,7 +2394,7 @@ pform_make_modgate(type, cur_name, overrides, cur.parms_by_name, cur.range.first, cur.range.second, - cur.file, cur.lineno); + cur.file, cur.lineno, attr); } else if (cur.parms) { @@ -2327,14 +2408,14 @@ pform_make_modgate(type, cur_name, overrides, cur.parms, cur.range.first, cur.range.second, - cur.file, cur.lineno); + cur.file, cur.lineno, attr); } else { list*wires = new list; pform_make_modgate(type, cur_name, overrides, wires, cur.range.first, cur.range.second, - cur.file, cur.lineno); + cur.file, cur.lineno, attr); } } @@ -2429,7 +2510,7 @@ PEIdent*lval = new PEIdent(name); FILE_NAME(lval, li); - PAssign*ass = new PAssign(lval, expr, true); + PAssign*ass = new PAssign(lval, expr, !gn_system_verilog()); FILE_NAME(ass, li); lexical_scope->var_inits.push_back(ass); @@ -2619,20 +2700,15 @@ // If the wire already exists and is fully defined, this // must be a redeclaration. Start again with a new wire. - if (cur) { - LineInfo tloc; - FILE_NAME(&tloc, li); - cerr << tloc.get_fileline() << ": error: duplicate declaration " - "for net or variable '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "'." << endl; - error_count += 1; - delete cur; - } + // The error will be reported when we add the new wire + // to the scope. Do not delete the old wire - it will + // remain in the local symbol map. cur = new PWire(name, type, ptype, dtype); FILE_NAME(cur, li.text, li.first_line); pform_put_wire_in_scope(name, cur); + return cur; } @@ -2759,25 +2835,24 @@ * net_decl_assign_t argument. */ void pform_makewire(const struct vlltype&li, - std::list*, str_pair_t , + std::list*delay, + str_pair_t str, std::list*assign_list, NetNet::Type type, data_type_t*data_type) { - if ((lexical_scope == 0) && !gn_system_verilog()) { + if (is_compilation_unit(lexical_scope) && !gn_system_verilog()) { VLerror(li, "error: variable declarations must be contained within a module."); return; } - if (lexical_scope == 0) { - VLerror(li, "sorry: variable declarations in the $root scope are not yet supported."); - return; - } list*names = new list; for (list::iterator cur = assign_list->begin() ; cur != assign_list->end() ; ++ cur) { decl_assignment_t* curp = *cur; + pform_makewire(li, curp->name, type, NetNet::NOT_A_PORT, IVL_VT_NO_TYPE, 0); + pform_set_reg_idx(curp->name, &curp->index); names->push_back(curp->name); } @@ -2786,8 +2861,16 @@ while (! assign_list->empty()) { decl_assignment_t*first = assign_list->front(); assign_list->pop_front(); - // For now, do not handle assignment expressions. - assert(! first->expr.get()); + if (PExpr*expr = first->expr.release()) { + if (type == NetNet::REG || type == NetNet::IMPLICIT_REG) { + pform_make_var_init(li, first->name, expr); + } else { + PEIdent*lval = new PEIdent(first->name); + FILE_NAME(lval, li.text, li.first_line); + PGAssign*ass = pform_make_pgassign(lval, expr, delay, str); + FILE_NAME(ass, li.text, li.first_line); + } + } delete first; } } @@ -2945,7 +3028,9 @@ ret = do_make_task_ports(loc, pt, IVL_VT_CLASS, class_type, names); } - ret = do_make_task_ports(loc, pt, IVL_VT_NO_TYPE, vtype, names); + if (! ret) { + ret = do_make_task_ports(loc, pt, IVL_VT_NO_TYPE, vtype, names); + } if (unpacked_dims) { for (list::iterator cur = names->begin() @@ -2996,6 +3081,25 @@ return tmp; } +PExpr* pform_genvar_inc_dec(const struct vlltype&loc, const char*name, bool inc_flag) +{ + if (!gn_system_verilog()) { + cerr << loc << ": error: Increment/decrement operators " + "require SystemVerilog." << endl; + error_count += 1; + } + + PExpr*lval = new PEIdent(lex_strings.make(name)); + PExpr*rval = new PENumber(new verinum((uint64_t)1, 1)); + FILE_NAME(lval, loc); + FILE_NAME(rval, loc); + + PEBinary*tmp = new PEBinary(inc_flag ? '+' : '-', lval, rval); + FILE_NAME(tmp, loc); + + return tmp; +} + void pform_set_attrib(perm_string name, perm_string key, char*value) { if (PWire*cur = lexical_scope->wires_find(name)) { @@ -3069,73 +3173,41 @@ LexicalScope::range_t*value_range) { LexicalScope*scope = lexical_scope; - if ((scope == 0) && !gn_system_verilog()) { + if (is_compilation_unit(scope) && !gn_system_verilog()) { VLerror(loc, "error: parameter declarations must be contained within a module."); return; } - if (scope == 0) { - VLerror(loc, "sorry: parameter declarations in the $root scope are not yet supported."); - return; - } if (scope == pform_cur_generate) { VLerror("parameter declarations are not permitted in generate blocks"); return; } - // Check if the parameter name is already in the dictionary. - if (scope->parameters.find(name) != scope->parameters.end()) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: duplicate definition " - "for parameter '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "'." << endl; - error_count += 1; - } - if (scope->localparams.find(name) != scope->localparams.end()) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: localparam and " - << "parameter in '" << pform_cur_module.front()->mod_name() - << "' have the same name '" << name << "'." << endl; - error_count += 1; - } - // Only a Module scope has specparams. - if ((dynamic_cast (scope)) && - (scope == pform_cur_module.front()) && - (pform_cur_module.front()->specparams.find(name) != - pform_cur_module.front()->specparams.end())) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: specparam and " - "parameter in '" << pform_cur_module.front()->mod_name() - << "' have the same name '" << name << "'." << endl; - error_count += 1; - } - assert(expr); - Module::param_expr_t&parm = scope->parameters[name]; - FILE_NAME(&parm, loc); + Module::param_expr_t*parm = new Module::param_expr_t(); + FILE_NAME(parm, loc); - parm.expr = expr; + add_local_symbol(scope, name, parm); + scope->parameters[name] = parm; - parm.type = type; + parm->expr = expr; + + parm->type = type; if (range) { assert(range->size() == 1); pform_range_t&rng = range->front(); assert(rng.first); assert(rng.second); - parm.msb = rng.first; - parm.lsb = rng.second; + parm->msb = rng.first; + parm->lsb = rng.second; } else { - parm.msb = 0; - parm.lsb = 0; + parm->msb = 0; + parm->lsb = 0; } - parm.signed_flag = signed_flag; - parm.range = value_range; + parm->signed_flag = signed_flag; + parm->range = value_range; // Only a Module keeps the position of the parameter. - if ((dynamic_cast (scope)) && - (scope == pform_cur_module.front())) + if ((dynamic_cast(scope)) && (scope == pform_cur_module.front())) pform_cur_module.front()->param_names.push_back(name); } @@ -3144,64 +3216,34 @@ bool signed_flag, list*range, PExpr*expr) { LexicalScope*scope = lexical_scope; - if ((scope == 0) && !gn_system_verilog()) { + if (is_compilation_unit(scope) && !gn_system_verilog()) { VLerror(loc, "error: localparam declarations must be contained within a module."); return; } - if (scope == 0) { - VLerror(loc, "sorry: localparam declarations in the $root scope are not yet supported."); - return; - } - - // Check if the localparam name is already in the dictionary. - if (scope->localparams.find(name) != scope->localparams.end()) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: duplicate definition " - "for localparam '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "'." << endl; - error_count += 1; - } - if (scope->parameters.find(name) != scope->parameters.end()) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: parameter and " - << "localparam in '" << pform_cur_module.front()->mod_name() - << "' have the same name '" << name << "'." << endl; - error_count += 1; - } - - if ((! pform_cur_module.empty()) && - (scope == pform_cur_module.front()) && - (pform_cur_module.front()->specparams.find(name) != pform_cur_module.front()->specparams.end())) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: specparam and " - "localparam in '" << pform_cur_module.front()->mod_name() - << "' have the same name '" << name << "'." << endl; - error_count += 1; - } assert(expr); - Module::param_expr_t&parm = scope->localparams[name]; - FILE_NAME(&parm, loc); + Module::param_expr_t*parm = new Module::param_expr_t(); + FILE_NAME(parm, loc); - parm.expr = expr; + add_local_symbol(scope, name, parm); + scope->localparams[name] = parm; - parm.type = type; + parm->expr = expr; + + parm->type = type; if (range) { assert(range->size() == 1); pform_range_t&rng = range->front(); assert(rng.first); assert(rng.second); - parm.msb = rng.first; - parm.lsb = rng.second; + parm->msb = rng.first; + parm->lsb = rng.second; } else { - parm.msb = 0; - parm.lsb = 0; + parm->msb = 0; + parm->lsb = 0; } - parm.signed_flag = signed_flag; - parm.range = 0; + parm->signed_flag = signed_flag; + parm->range = 0; } void pform_set_specparam(const struct vlltype&loc, perm_string name, @@ -3211,55 +3253,30 @@ Module*scope = pform_cur_module.front(); assert(scope == lexical_scope); - // Check if the specparam name is already in the dictionary. - if (pform_cur_module.front()->specparams.find(name) != - pform_cur_module.front()->specparams.end()) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: duplicate definition " - "for specparam '" << name << "' in '" - << pform_cur_module.front()->mod_name() << "'." << endl; - error_count += 1; - } - if (scope->parameters.find(name) != scope->parameters.end()) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: parameter and " - "specparam in '" << pform_cur_module.front()->mod_name() - << "' have the same name '" << name << "'." << endl; - error_count += 1; - } - if (scope->localparams.find(name) != scope->localparams.end()) { - LineInfo tloc; - FILE_NAME(&tloc, loc); - cerr << tloc.get_fileline() << ": error: localparam and " - "specparam in '" << pform_cur_module.front()->mod_name() - << "' have the same name '" << name << "'." << endl; - error_count += 1; - } - assert(expr); + Module::param_expr_t*parm = new Module::param_expr_t(); + FILE_NAME(parm, loc); - Module::param_expr_t&parm = pform_cur_module.front()->specparams[name]; - FILE_NAME(&parm, loc); + add_local_symbol(scope, name, parm); + pform_cur_module.front()->specparams[name] = parm; - parm.expr = expr; + parm->expr = expr; if (range) { assert(range->size() == 1); pform_range_t&rng = range->front(); assert(rng.first); assert(rng.second); - parm.type = IVL_VT_LOGIC; - parm.msb = rng.first; - parm.lsb = rng.second; - } else { - parm.type = IVL_VT_NO_TYPE; - parm.msb = 0; - parm.lsb = 0; + parm->type = IVL_VT_LOGIC; + parm->msb = rng.first; + parm->lsb = rng.second; + } else { + parm->type = IVL_VT_NO_TYPE; + parm->msb = 0; + parm->lsb = 0; } - parm.signed_flag = false; - parm.range = 0; + parm->signed_flag = false; + parm->range = 0; } void pform_set_defparam(const pform_name_t&name, PExpr*expr) @@ -3271,6 +3288,28 @@ pform_cur_module.front()->defparms.push_back(make_pair(name,expr)); } +void pform_set_param_from_type(const struct vlltype&loc, + const data_type_t *data_type, + const char *name, + list *¶m_range, + bool ¶m_signed, + ivl_variable_type_t ¶m_type) +{ + if (const vector_type_t *vec = dynamic_cast (data_type)) { + param_range = vec->pdims.get(); + param_signed = vec->signed_flag; + param_type = vec->base_type; + return; + } + + param_range = 0; + param_signed = false; + param_type = IVL_VT_NO_TYPE; + cerr << loc.get_fileline() << ": sorry: cannot currently create a " + "parameter of type '" << name << "' which was defined at: " + << data_type->get_fileline() << "." << endl; + error_count += 1; +} /* * Specify paths. */ @@ -3423,9 +3462,9 @@ delete attr; } -static void pform_set_integer_2atom(uint64_t width, bool signed_flag, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_integer_2atom(const struct vlltype&li, uint64_t width, bool signed_flag, perm_string name, NetNet::Type net_type, list*attr) { - PWire*cur = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_BOOL); + PWire*cur = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, IVL_VT_BOOL); assert(cur); cur->set_signed(signed_flag); @@ -3439,12 +3478,12 @@ pform_bind_attributes(cur->attributes, attr, true); } -static void pform_set_integer_2atom(uint64_t width, bool signed_flag, list*names, NetNet::Type net_type, list*attr) +static void pform_set_integer_2atom(const struct vlltype&li, uint64_t width, bool signed_flag, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur ) { perm_string txt = *cur; - pform_set_integer_2atom(width, signed_flag, txt, net_type, attr); + pform_set_integer_2atom(li, width, signed_flag, txt, net_type, attr); } } @@ -3455,9 +3494,8 @@ VLerror(li, "Compound type is not PACKED in this context."); } - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, base_type); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, base_type); assert(net); - net->set_data_type(data_type); pform_bind_attributes(net->attributes, attr, true); } @@ -3469,11 +3507,11 @@ } } -static void pform_set_enum(enum_type_t*enum_type, +static void pform_set_enum(const struct vlltype&li, enum_type_t*enum_type, perm_string name, NetNet::Type net_type, std::list*attr) { - PWire*cur = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, enum_type->base_type); + PWire*cur = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, enum_type->base_type); assert(cur); cur->set_signed(enum_type->signed_flag); @@ -3481,7 +3519,6 @@ assert(enum_type->range.get() != 0); assert(enum_type->range->size() == 1); //XXXXcur->set_range(*enum_type->range, SR_NET); - cur->set_data_type(enum_type); // If this is an integer enumeration switch the wire to an integer. if (enum_type->integer_flag) { bool res = cur->set_wire_type(NetNet::INTEGER); @@ -3504,15 +3541,16 @@ // Add the file and line information to the enumeration type. FILE_NAME(&(enum_type->li), li); - // Attach the enumeration to the current scope. - pform_put_enum_type_in_scope(enum_type); + // If this is an anonymous enumeration, attach it to the current scope. + if (enum_type->name.nil()) + pform_put_enum_type_in_scope(enum_type); // Now apply the checked enumeration type to the variables // that are being declared with this type. for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { perm_string txt = *cur; - pform_set_enum(enum_type, txt, net_type, attr); + pform_set_enum(li, enum_type, txt, net_type, attr); } } @@ -3523,24 +3561,21 @@ */ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type, list*names, NetNet::Type net_type, list*attr) { - const std::list*unpacked_dims = NULL; - if (data_type == 0) { VLerror(li, "internal error: data_type==0."); assert(0); } - if(uarray_type_t*uarray_type = dynamic_cast (data_type)) { - unpacked_dims = uarray_type->dims.get(); + uarray_type_t*uarray_type = dynamic_cast (data_type); + if (uarray_type) data_type = uarray_type->base_type; - } if (atom2_type_t*atom2_type = dynamic_cast (data_type)) { - pform_set_integer_2atom(atom2_type->type_code, atom2_type->signed_flag, names, net_type, attr); + pform_set_integer_2atom(li, atom2_type->type_code, atom2_type->signed_flag, names, net_type, attr); } else if (struct_type_t*struct_type = dynamic_cast (data_type)) { - pform_set_struct_type(struct_type, names, net_type, attr); + pform_set_struct_type(li, struct_type, names, net_type, attr); } else if (enum_type_t*enum_type = dynamic_cast (data_type)) { @@ -3561,7 +3596,7 @@ } else if (class_type_t*class_type = dynamic_cast (data_type)) { - pform_set_class_type(class_type, names, net_type, attr); + pform_set_class_type(li, class_type, names, net_type, attr); } else if (parray_type_t*array_type = dynamic_cast (data_type)) { @@ -3569,19 +3604,21 @@ } else if (string_type_t*string_type = dynamic_cast (data_type)) { - pform_set_string_type(string_type, names, net_type, attr); + pform_set_string_type(li, string_type, names, net_type, attr); } else { VLerror(li, "internal error: Unexpected data_type."); assert(0); } - if(unpacked_dims) { - for (list::iterator cur = names->begin() - ; cur != names->end() ; ++ cur ) { - PWire*wire = pform_get_wire_in_scope(*cur); - wire->set_unpacked_idx(*unpacked_dims); + for (list::iterator cur = names->begin() + ; cur != names->end() ; ++ cur ) { + PWire*wire = pform_get_wire_in_scope(*cur); + if (uarray_type) { + wire->set_unpacked_idx(*uarray_type->dims.get()); + wire->set_uarray_type(uarray_type); } + wire->set_data_type(data_type); } delete names; @@ -3610,6 +3647,16 @@ PProcess* pform_make_behavior(ivl_process_type_t type, Statement*st, list*attr) { + // Add an implicit @* around the statement for the always_comb and + // always_latch statements. + if ((type == IVL_PR_ALWAYS_COMB) || (type == IVL_PR_ALWAYS_LATCH)) { + PEventStatement *tmp = new PEventStatement(true); + tmp->set_file(st->get_file()); + tmp->set_lineno(st->get_lineno()); + tmp->set_statement(st); + st = tmp; + } + PProcess*pp = new PProcess(type, st); // If we are in a part of the code where the meta-comment @@ -3629,8 +3676,10 @@ pform_put_behavior_in_scope(pp); ivl_assert(*st, ! pform_cur_module.empty()); - if (pform_cur_module.front()->program_block && type == IVL_PR_ALWAYS) { - cerr << st->get_fileline() << ": error: Always statements not allowed" + if (pform_cur_module.front()->program_block && + ((type == IVL_PR_ALWAYS) || (type == IVL_PR_ALWAYS_COMB) || + (type == IVL_PR_ALWAYS_FF) || (type == IVL_PR_ALWAYS_LATCH))) { + cerr << st->get_fileline() << ": error: Always statements are not allowed" << " in program blocks." << endl; error_count += 1; } @@ -3647,12 +3696,10 @@ perm_string use_name = lex_strings.make(name); pform_cur_modport = new PModport(use_name); FILE_NAME(pform_cur_modport, loc); - if (scope->modports.find(use_name) != scope->modports.end()) { - cerr << loc << ": error: duplicate declaration for modport '" - << name << "' in '" << scope->mod_name() << "'." << endl; - error_count += 1; - } + + add_local_symbol(scope, use_name, pform_cur_modport); scope->modports[use_name] = pform_cur_modport; + delete[] name; } @@ -3682,31 +3729,76 @@ FILE*vl_input = 0; extern void reset_lexor(); -int pform_parse(const char*path, FILE*file) +int pform_parse(const char*path) { vl_file = path; - if (file == 0) { + if (strcmp(path, "-") == 0) { + vl_input = stdin; + } else if (ivlpp_string) { + char*cmdline = (char*)malloc(strlen(ivlpp_string) + + strlen(path) + 4); + strcpy(cmdline, ivlpp_string); + strcat(cmdline, " \""); + strcat(cmdline, path); + strcat(cmdline, "\""); - if (strcmp(path, "-") == 0) - vl_input = stdin; - else - vl_input = fopen(path, "r"); + if (verbose_flag) + cerr << "Executing: " << cmdline << endl<< flush; + + vl_input = popen(cmdline, "r"); if (vl_input == 0) { - cerr << "Unable to open " <default_lifetime = LexicalScope::STATIC; + unit->set_file(filename_strings.make(path)); + unit->set_lineno(1); + pform_units.push_back(unit); + + pform_cur_module.clear(); + pform_cur_generate = 0; + pform_cur_modport = 0; + + pform_set_timescale(def_ts_units, def_ts_prec, 0, 0); + + allow_timeunit_decl = true; + allow_timeprec_decl = true; + + lexical_scope = unit; + } reset_lexor(); error_count = 0; warn_count = 0; int rc = VLparse(); - if (file == 0) - fclose(vl_input); + if (vl_input != stdin) { + if (ivlpp_string) + pclose(vl_input); + else + fclose(vl_input); + } if (rc) { cerr << "I give up." << endl; diff -Nru iverilog-10.3/pform_class_type.cc iverilog-11.0/pform_class_type.cc --- iverilog-10.3/pform_class_type.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_class_type.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Picture Elements, Inc. + * Copyright (c) 2012-2019 Picture Elements, Inc. * Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -22,18 +22,17 @@ # include "parse_misc.h" # include "ivl_assert.h" -static void pform_set_class_type(class_type_t*class_type, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_class_type(const struct vlltype&li, class_type_t*, perm_string name, NetNet::Type net_type, list*attr) { - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_CLASS); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, IVL_VT_CLASS); assert(net); - net->set_data_type(class_type); pform_bind_attributes(net->attributes, attr, true); } -void pform_set_class_type(class_type_t*class_type, list*names, NetNet::Type net_type, list*attr) +void pform_set_class_type(const struct vlltype&li, class_type_t*class_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { - pform_set_class_type(class_type, *cur, net_type, attr); + pform_set_class_type(li, class_type, *cur, net_type, attr); } } diff -Nru iverilog-10.3/pform_dump.cc iverilog-11.0/pform_dump.cc --- iverilog-10.3/pform_dump.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_dump.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -99,6 +99,10 @@ ostream& operator<< (ostream&o, const pform_name_t&that) { pform_name_t::const_iterator cur; + if (that.size() == 0) { + o << ""; + return o; + } cur = that.begin(); o << *cur; @@ -121,6 +125,15 @@ case IVL_PR_ALWAYS: out << "always"; break; + case IVL_PR_ALWAYS_COMB: + out << "always_comb"; + break; + case IVL_PR_ALWAYS_FF: + out << "always_ff"; + break; + case IVL_PR_ALWAYS_LATCH: + out << "always_latch"; + break; case IVL_PR_FINAL: out << "final"; break; @@ -191,7 +204,10 @@ if (pdims.get()) { for (list::iterator cur = pdims->begin() ; cur != pdims->end() ; ++cur) { - fd << "[" << *(cur->first) << ":" << *(cur->second) << "]"; + fd << "["; + if (cur->first) fd << *(cur->first); + if (cur->second) fd << ":" << *(cur->second); + fd << "]"; } } fd << endl; @@ -515,10 +531,12 @@ if (signed_) { out << " signed"; } - if (get_isint()) { out << " integer"; } + if (get_scalar()) { + out << " scalar"; + } if (set_data_type_) { out << " set_data_type_=" << typeid(*set_data_type_).name(); } @@ -533,8 +551,12 @@ } else { out << " port"; for (list::const_iterator cur = port_.begin() - ; cur != port_.end() ; ++cur) - out << "[" << *cur->first << ":" << *cur->second << "]"; + ; cur != port_.end() ; ++cur) { + out << "["; + if (cur->first) out << *cur->first; + if (cur->second) out << ":" << *cur->second; + out << "]"; + } } } if (net_set_) { @@ -543,8 +565,12 @@ } else { out << " net"; for (list::const_iterator cur = net_.begin() - ; cur != net_.end() ; ++cur) - out << "[" << *cur->first << ":" << *cur->second << "]"; + ; cur != net_.end() ; ++cur) { + out << "["; + if (cur->first) out << *cur->first; + if (cur->second) out << ":" << *cur->second; + out << "]"; + } } } @@ -627,10 +653,10 @@ out << "bufif1 "; break; case PGBuiltin::NOTIF0: - out << "bufif0 "; + out << "notif0 "; break; case PGBuiltin::NOTIF1: - out << "bufif1 "; + out << "notif1 "; break; case PGBuiltin::NAND: out << "nand "; @@ -695,10 +721,11 @@ if (parms_) { assert(overrides_ == 0); out << "#("; - out << "." << parms_[0].name << "(" << *parms_[0].parm << ")"; - for (unsigned idx = 1 ; idx < nparms_ ; idx += 1) { - out << ", ." << parms_[idx].name << "(" << - *parms_[idx].parm << ")"; + for (unsigned idx = 0 ; idx < nparms_ ; idx += 1) { + if (idx > 0) out << ", "; + out << "." << parms_[idx].name << "("; + if (parms_[idx].parm) out << *parms_[idx].parm; + out << ")"; } out << ") "; } @@ -729,6 +756,7 @@ dump_pins(out); } out << ");" << endl; + dump_attributes_map(out, attributes, 8); } void Statement::dump(ostream&out, unsigned ind) const @@ -826,6 +854,19 @@ void PCase::dump(ostream&out, unsigned ind) const { out << setw(ind) << ""; + switch (quality_) { + case IVL_CASE_QUALITY_BASIC: + break; + case IVL_CASE_QUALITY_UNIQUE: + out << "unique "; + break; + case IVL_CASE_QUALITY_UNIQUE0: + out << "unique0 "; + break; + case IVL_CASE_QUALITY_PRIORITY: + out << "priority "; + break; + } switch (type_) { case NetCase::EQ: out << "case"; @@ -843,7 +884,7 @@ for (unsigned idx = 0 ; idx < items_->count() ; idx += 1) { PCase::Item*cur = (*items_)[idx]; - if (cur->expr.size()) { + if (! cur->expr.empty()) { out << setw(ind+2) << "" << "default:"; } else { @@ -1175,6 +1216,11 @@ case IVL_PR_ALWAYS: out << setw(ind) << "" << "analog"; break; + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: + assert(0); + break; case IVL_PR_FINAL: out << setw(ind) << "" << "analog final"; break; @@ -1340,22 +1386,22 @@ void LexicalScope::dump_parameters_(ostream&out, unsigned indent) const { - typedef map::const_iterator parm_iter_t; + typedef map::const_iterator parm_iter_t; for (parm_iter_t cur = parameters.begin() ; cur != parameters.end() ; ++ cur ) { out << setw(indent) << "" << "parameter " - << (*cur).second.type << " "; - if ((*cur).second.signed_flag) + << (*cur).second->type << " "; + if ((*cur).second->signed_flag) out << "signed "; - if ((*cur).second.msb) - out << "[" << *(*cur).second.msb << ":" - << *(*cur).second.lsb << "] "; + if ((*cur).second->msb) + out << "[" << *(*cur).second->msb << ":" + << *(*cur).second->lsb << "] "; out << (*cur).first << " = "; - if ((*cur).second.expr) - out << *(*cur).second.expr; + if ((*cur).second->expr) + out << *(*cur).second->expr; else out << "/* ERROR */"; - for (LexicalScope::range_t*tmp = (*cur).second.range + for (LexicalScope::range_t*tmp = (*cur).second->range ; tmp ; tmp = tmp->next) { if (tmp->exclude_flag) out << " exclude "; @@ -1389,16 +1435,16 @@ void LexicalScope::dump_localparams_(ostream&out, unsigned indent) const { - typedef map::const_iterator parm_iter_t; + typedef map::const_iterator parm_iter_t; for (parm_iter_t cur = localparams.begin() ; cur != localparams.end() ; ++ cur ) { out << setw(indent) << "" << "localparam "; - if ((*cur).second.msb) - out << "[" << *(*cur).second.msb << ":" - << *(*cur).second.lsb << "] "; + if ((*cur).second->msb) + out << "[" << *(*cur).second->msb << ":" + << *(*cur).second->lsb << "] "; out << (*cur).first << " = "; - if ((*cur).second.expr) - out << *(*cur).second.expr << ";" << endl; + if ((*cur).second->expr) + out << *(*cur).second->expr << ";" << endl; else out << "/* ERROR */;" << endl; } @@ -1494,16 +1540,16 @@ void Module::dump_specparams_(ostream&out, unsigned indent) const { - typedef map::const_iterator parm_iter_t; + typedef map::const_iterator parm_iter_t; for (parm_iter_t cur = specparams.begin() ; cur != specparams.end() ; ++ cur ) { out << setw(indent) << "" << "specparam "; - if ((*cur).second.msb) - out << "[" << *(*cur).second.msb << ":" - << *(*cur).second.lsb << "] "; + if ((*cur).second->msb) + out << "[" << *(*cur).second->msb << ":" + << *(*cur).second->lsb << "] "; out << (*cur).first << " = "; - if ((*cur).second.expr) - out << *(*cur).second.expr << ";" << endl; + if ((*cur).second->expr) + out << *(*cur).second->expr << ";" << endl; else out << "/* ERROR */;" << endl; } @@ -1704,9 +1750,12 @@ out << "package " << pscope_name() << endl; dump_localparams_(out, 4); dump_parameters_(out, 4); + dump_typedefs_(out, 4); dump_enumerations_(out, 4); + dump_wires_(out, 4); dump_tasks_(out, 4); dump_funcs_(out, 4); + dump_var_inits_(out, 4); out << "endpackage" << endl; } diff -Nru iverilog-10.3/pform.h iverilog-11.0/pform.h --- iverilog-10.3/pform.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_pform_H #define IVL_pform_H /* - * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 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 @@ -104,7 +104,7 @@ /* The lgate is gate instantiation information. */ struct lgate { - inline lgate(int =0) + explicit inline lgate(int =0) : parms(0), parms_by_name(0), file(NULL), lineno(0) { } @@ -147,7 +147,11 @@ */ extern PWire* pform_get_wire_in_scope(perm_string name); -extern PWire* pform_get_make_wire_in_scope(perm_string name, NetNet::Type net_type, NetNet::PortType port_type, ivl_variable_type_t vt_type); +extern PWire* pform_get_make_wire_in_scope(const struct vlltype&li, + perm_string name, + NetNet::Type net_type, + NetNet::PortType port_type, + ivl_variable_type_t vt_type); /* * The parser uses startmodule and endmodule together to build up a @@ -165,8 +169,8 @@ bool program_block, bool is_interface, LexicalScope::lifetime_t lifetime, list*attr); -extern void pform_check_timeunit_prec(); extern void pform_module_set_ports(vector*); +extern void pform_set_scope_timescale(const struct vlltype&loc); /* These functions are used when we have a complete port definition, either in an ansi style or non-ansi style declaration. In this case, we have @@ -242,7 +246,10 @@ * This creates an identifier aware of names that may have been * imported from other packages. */ -extern PEIdent* pform_new_ident(const pform_name_t&name); +extern PEIdent* pform_new_ident(const struct vlltype&loc, const pform_name_t&name); + +extern PTrigger* pform_new_trigger(const struct vlltype&loc, PPackage*pkg, + const pform_name_t&name); /* * Enter/exit name scopes. The push_scope function pushes the scope @@ -270,7 +277,8 @@ extern PFunction*pform_push_function_scope(const struct vlltype&loc, const char*name, LexicalScope::lifetime_t lifetime); -extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt); +extern PBlock*pform_push_block_scope(const struct vlltype&loc, char*name, + PBlock::BL_TYPE tt); extern void pform_put_behavior_in_scope(AProcess*proc); @@ -284,6 +292,7 @@ extern void pform_genvars(const struct vlltype&li, list*names); extern void pform_start_generate_for(const struct vlltype&li, + bool local_index, char*ident1, PExpr*init, PExpr*test, @@ -295,7 +304,7 @@ extern void pform_start_generate_nblock(const struct vlltype&lp, char*name); extern void pform_generate_case_item(const struct vlltype&lp, list*test); extern void pform_generate_block_name(char*name); -extern void pform_endgenerate(); +extern void pform_endgenerate(bool end_conditional); /* * This function returns the lexically containing generate scheme, if @@ -307,9 +316,10 @@ extern void pform_set_typedef(perm_string name, data_type_t*data_type, std::list*unp_ranges); +extern void pform_set_type_referenced(const struct vlltype&loc, const char*name); + /* - * This function makes a PECallFunction of the named function. Decide - * if this function is in the scope or is imported from a package. + * This function makes a PECallFunction of the named function. */ extern PECallFunction* pform_make_call_function(const struct vlltype&loc, const pform_name_t&name, @@ -386,11 +396,11 @@ extern void pform_set_data_type(const struct vlltype&li, data_type_t*, list*names, NetNet::Type net_type, list*attr); -extern void pform_set_struct_type(struct_type_t*struct_type, std::list*names, NetNet::Type net_type, std::list*attr); +extern void pform_set_struct_type(const struct vlltype&li, struct_type_t*struct_type, std::list*names, NetNet::Type net_type, std::list*attr); -extern void pform_set_string_type(const string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); +extern void pform_set_string_type(const struct vlltype&li, const string_type_t*string_type, std::list*names, NetNet::Type net_type, std::list*attr); -extern void pform_set_class_type(class_type_t*class_type, std::list*names, NetNet::Type net_type, std::list*addr); +extern void pform_set_class_type(const struct vlltype&li, class_type_t*class_type, std::list*names, NetNet::Type net_type, std::list*addr); /* pform_set_attrib and pform_set_type_attrib exist to support the @@ -424,6 +434,13 @@ PExpr*expr); extern void pform_set_defparam(const pform_name_t&name, PExpr*expr); +extern void pform_set_param_from_type(const struct vlltype&loc, + const data_type_t *data_type, + const char *name, + list *¶m_range, + bool ¶m_signed, + ivl_variable_type_t ¶m_type); + /* * Functions related to specify blocks. */ @@ -471,7 +488,8 @@ extern void pform_make_modgates(const struct vlltype&loc, perm_string type, struct parmvalue_t*overrides, - svector*gates); + svector*gates, + list*attr); /* Make a continuous assignment node, with optional bit- or part- select. */ extern void pform_make_pgassign_list(list*alist, @@ -503,6 +521,13 @@ PExpr*exp); /* + * The parser uses this function to convert a genvar increment/decrement + * expression to the equivalent binary add/subtract expression. + */ +extern PExpr* pform_genvar_inc_dec(const struct vlltype&loc, const char*name, + bool inc_flag); + +/* * These are functions that the outside-the-parser code uses the do * interesting things to the Verilog. The parse function reads and * parses the source file and places all the modules it finds into the @@ -559,8 +584,12 @@ */ extern bool get_time_unit(const char*cp, int &unit); extern int pform_get_timeunit(); -extern void pform_set_timeunit(const char*txt, bool in_module, bool only_check); -extern void pform_set_timeprecision(const char*txt, bool in_module, - bool only_check); +extern void pform_set_timeunit(const char*txt, bool initial_decl); +extern void pform_set_timeprec(const char*txt, bool initial_decl); +/* + * Flags to determine whether this is an initial declaration. + */ +extern bool allow_timeunit_decl; +extern bool allow_timeprec_decl; #endif /* IVL_pform_H */ diff -Nru iverilog-10.3/pform_package.cc iverilog-11.0/pform_package.cc --- iverilog-10.3/pform_package.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_package.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2019 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -72,92 +72,58 @@ * package is declared in pform ahead of time (it is) and that we can * simply transfer definitions to the current scope (we can). */ -void pform_package_import(const struct vlltype&, PPackage*pkg, const char*ident) +void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ident) { LexicalScope*scope = pform_peek_scope(); if (ident) { perm_string use_ident = lex_strings.make(ident); - map::const_iterator cur - = pkg->parameters.find(use_ident); - if (cur != pkg->parameters.end()) { - scope->imports[cur->first] = pkg; + // Check that the requested symbol is available. + map::const_iterator cur_sym + = pkg->local_symbols.find(use_ident); + if (cur_sym == pkg->local_symbols.end()) { + cerr << loc.get_fileline() << ": error: " + "'" << use_ident << "' is not exported by '" + << pkg->pscope_name() << "'." << endl; + error_count += 1; return; } - cur = pkg->localparams.find(use_ident); - if (cur != pkg->localparams.end()) { - scope->imports[cur->first] = pkg; + // Check for conflict with local symbol. + cur_sym = scope->local_symbols.find(use_ident); + if (cur_sym != scope->local_symbols.end()) { + cerr << loc.get_fileline() << ": error: " + "'" << use_ident << "' has already been declared " + "in this scope." << endl; + cerr << cur_sym->second->get_fileline() << ": : " + "It was declared here as " + << cur_sym->second->symbol_type() << "." << endl; + error_count += 1; return; } - map::const_iterator tcur; - tcur = pkg->typedefs.find(use_ident); - if (tcur != pkg->typedefs.end()) { - scope->imports[tcur->first] = pkg; + // Check for conflict with previous import. + map::const_iterator cur_pkg + = scope->explicit_imports.find(use_ident); + if (cur_pkg != scope->explicit_imports.end()) { + if (cur_pkg->second != pkg) { + cerr << loc.get_fileline() << ": error: " + "'" << use_ident << "' has already been " + "imported into this scope from package '" + << cur_pkg->second->pscope_name() << "'." << endl; + error_count += 1; + } return; } - map::const_iterator fcur; - fcur = pkg->funcs.find(use_ident); - if (fcur != pkg->funcs.end()) { - scope->imports[fcur->first] = pkg; - return; - } - - map::const_iterator ttcur; - ttcur = pkg->tasks.find(use_ident); - if (ttcur != pkg->tasks.end()) { - scope->imports[ttcur->first] = pkg; - return; - } - - map::const_iterator wcur; - wcur = pkg->wires.find(use_ident); - if (wcur != pkg->wires.end()) { - scope->imports[wcur->first] = pkg; - return; - } - - ostringstream msg; - msg << "Symbol " << use_ident - << " not found in package " << pkg->pscope_name() << "." << ends; - VLerror(msg.str().c_str()); - return; + scope->explicit_imports[use_ident] = pkg; } else { - - // Handle the pkg::* case by importing everything from - // the package. - for (map::const_iterator cur = pkg->parameters.begin() - ; cur != pkg->parameters.end() ; ++cur) { - - scope->imports[cur->first] = pkg; - } - - for (map::const_iterator cur = pkg->localparams.begin() - ; cur != pkg->localparams.end() ; ++cur) { - - scope->imports[cur->first] = pkg; - } - - for (map::const_iterator cur = pkg->typedefs.begin() - ; cur != pkg->typedefs.end() ; ++cur) { - - scope->imports[cur->first] = pkg; - } - - for (map::const_iterator cur = pkg->funcs.begin() - ; cur != pkg->funcs.end() ; ++cur) { - - scope->imports[cur->first] = pkg; - } - - for (set::const_iterator cur = pkg->enum_sets.begin() - ; cur != pkg->enum_sets.end() ; ++ cur) { - scope->enum_sets.insert(*cur); - } + set::const_iterator cur_pkg + = scope->potential_imports.find(pkg); + if (cur_pkg == scope->potential_imports.end()) + scope->potential_imports.insert(pkg); } } diff -Nru iverilog-10.3/pform_pclass.cc iverilog-11.0/pform_pclass.cc --- iverilog-10.3/pform_pclass.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_pclass.cc 2020-09-26 22:44:25.000000000 +0000 @@ -67,6 +67,10 @@ { assert(pform_cur_class); + if (enum_type_t*enum_set = dynamic_cast(data_type)) { + pform_cur_class->enum_sets .insert(enum_set); + } + // Add the non-static properties to the class type // object. Unwind the list of names to make a map of name to // type. diff -Nru iverilog-10.3/pform_string_type.cc iverilog-11.0/pform_string_type.cc --- iverilog-10.3/pform_string_type.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_string_type.cc 2020-09-26 22:44:25.000000000 +0000 @@ -21,18 +21,17 @@ # include "parse_misc.h" # include "ivl_assert.h" -static void pform_set_string_type(const string_type_t*, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_string_type(const struct vlltype&li, const string_type_t*, perm_string name, NetNet::Type net_type, list*attr) { - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, IVL_VT_STRING); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, IVL_VT_STRING); assert(net); pform_bind_attributes(net->attributes, attr, true); } -void pform_set_string_type(const string_type_t*string_type, list*names, NetNet::Type net_type, list*attr) +void pform_set_string_type(const struct vlltype&li, const string_type_t*string_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { - pform_set_string_type(string_type, *cur, net_type, attr); + pform_set_string_type(li, string_type, *cur, net_type, attr); } } - diff -Nru iverilog-10.3/pform_struct_type.cc iverilog-11.0/pform_struct_type.cc --- iverilog-10.3/pform_struct_type.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_struct_type.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2019 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 @@ -62,20 +62,19 @@ * out the base type of the packed variable. Elaboration, later on, * well figure out the rest. */ -static void pform_set_packed_struct(struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_packed_struct(const struct vlltype&li, struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) { ivl_variable_type_t base_type = struct_type->figure_packed_base_type(); - PWire*net = pform_get_make_wire_in_scope(name, net_type, NetNet::NOT_A_PORT, base_type); + PWire*net = pform_get_make_wire_in_scope(li, name, net_type, NetNet::NOT_A_PORT, base_type); assert(net); - net->set_data_type(struct_type); pform_bind_attributes(net->attributes, attr, true); } -static void pform_set_struct_type(struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) +static void pform_set_struct_type(const struct vlltype&li, struct_type_t*struct_type, perm_string name, NetNet::Type net_type, list*attr) { if (struct_type->packed_flag) { - pform_set_packed_struct(struct_type, name, net_type, attr); + pform_set_packed_struct(li, struct_type, name, net_type, attr); return; } @@ -83,11 +82,11 @@ // a "sorry" message, so no need to do anything here. } -void pform_set_struct_type(struct_type_t*struct_type, list*names, NetNet::Type net_type, list*attr) +void pform_set_struct_type(const struct vlltype&li, struct_type_t*struct_type, list*names, NetNet::Type net_type, list*attr) { for (list::iterator cur = names->begin() ; cur != names->end() ; ++ cur) { - pform_set_struct_type(struct_type, *cur, net_type, attr); + pform_set_struct_type(li, struct_type, *cur, net_type, attr); } } @@ -99,7 +98,7 @@ { ivl_variable_type_t base_type = struct_type->figure_packed_base_type(); - PWire*cur = pform_get_make_wire_in_scope(name, NetNet::WIRE, ptype, base_type); + PWire*cur = pform_get_make_wire_in_scope(li, name, NetNet::WIRE, ptype, base_type); assert(cur); FILE_NAME(cur, li); cur->set_data_type(struct_type); diff -Nru iverilog-10.3/pform_types.cc iverilog-11.0/pform_types.cc --- iverilog-10.3/pform_types.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_types.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 2007-2019 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 @@ -24,6 +24,11 @@ { } +PNamedItem::SymbolType data_type_t::symbol_type() const +{ + return TYPE; +} + string_type_t::~string_type_t() { } @@ -44,3 +49,13 @@ } atom2_type_t size_type (32, true); + +PNamedItem::SymbolType enum_type_t::symbol_type() const +{ + return ENUM; +} + +PNamedItem::SymbolType class_type_t::symbol_type() const +{ + return CLASS; +} diff -Nru iverilog-10.3/pform_types.h iverilog-11.0/pform_types.h --- iverilog-10.3/pform_types.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/pform_types.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_pform_types_H #define IVL_pform_types_H /* - * Copyright (c) 2007-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 2007-2019 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 @@ -21,7 +21,7 @@ // This for the perm_string type. # include "StringHeap.h" -# include "LineInfo.h" +# include "PNamedItem.h" # include "verinum.h" # include "named.h" # include "netstruct.h" @@ -137,7 +137,7 @@ * "data_type" rule in the parse rule. We make the type virtual so * that dynamic types will work. */ -class data_type_t : public LineInfo { +class data_type_t : public PNamedItem { public: inline explicit data_type_t() { } virtual ~data_type_t() = 0; @@ -149,6 +149,10 @@ ivl_type_s* elaborate_type(Design*des, NetScope*scope); + virtual SymbolType symbol_type() const; + + perm_string name; + private: // Elaborate the type to an ivl_type_s type. virtual ivl_type_s* elaborate_type_raw(Design*des, NetScope*scope) const; @@ -171,6 +175,8 @@ // Return the elaborated version of the type. virtual ivl_type_s*elaborate_type_raw(Design*des, NetScope*scope) const; + SymbolType symbol_type() const; + ivl_variable_type_t base_type; bool signed_flag; bool integer_flag; // True if "integer" was used @@ -295,14 +301,11 @@ struct class_type_t : public data_type_t { inline explicit class_type_t(perm_string n) - : name(n), base_type(0), save_elaborated_type(0) { } + : base_type(0), save_elaborated_type(0) { name = n; } void pform_dump(std::ostream&out, unsigned indent) const; void pform_dump_init(std::ostream&out, unsigned indent) const; - // This is the name of the class type. - perm_string name; - // This is the named type that is supposed to be the base // class that we are extending. This is nil if there is no // hierarchy. If there are arguments to the base class, then @@ -335,6 +338,8 @@ // type. The elaborate_type_raw() method uses this pointer, // and it is used in some other situations as well. netclass_t* save_elaborated_type; + + virtual SymbolType symbol_type() const; }; /* diff -Nru iverilog-10.3/PFunction.cc iverilog-11.0/PFunction.cc --- iverilog-10.3/PFunction.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PFunction.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -83,3 +83,8 @@ return res; } + +PNamedItem::SymbolType PFunction::symbol_type() const +{ + return FUNCTION; +} diff -Nru iverilog-10.3/PGate.cc iverilog-11.0/PGate.cc --- iverilog-10.3/PGate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PGate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -117,6 +117,11 @@ return delay_.delay_count(); } +PNamedItem::SymbolType PGate::symbol_type() const +{ + return INSTANCE; +} + PGAssign::PGAssign(list*pins) : PGate(perm_string(), pins) { diff -Nru iverilog-10.3/PGate.h iverilog-11.0/PGate.h --- iverilog-10.3/PGate.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PGate.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PGate_H #define IVL_PGate_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -22,7 +22,7 @@ # include "svector.h" # include "StringHeap.h" # include "named.h" -# include "LineInfo.h" +# include "PNamedItem.h" # include "PDelays.h" # include "netlist.h" # include @@ -47,7 +47,7 @@ * single strength pair. There is a strength of the 0 drive, and a * strength of the 1 drive. */ -class PGate : public LineInfo { +class PGate : public PNamedItem { public: explicit PGate(perm_string name, list*pins, @@ -88,6 +88,8 @@ virtual void elaborate_scope(Design*des, NetScope*sc) const; virtual bool elaborate_sig(Design*des, NetScope*scope) const; + SymbolType symbol_type() const; + protected: const vector& get_pins() const { return pins_; } @@ -217,6 +219,8 @@ // method to pass the range to the pform. void set_range(PExpr*msb, PExpr*lsb); + map attributes; + virtual void dump(ostream&out, unsigned ind =4) const; virtual void elaborate(Design*, NetScope*scope) const; virtual void elaborate_scope(Design*des, NetScope*sc) const; diff -Nru iverilog-10.3/PGenerate.cc iverilog-11.0/PGenerate.cc --- iverilog-10.3/PGenerate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PGenerate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2006-2020 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 @@ -26,6 +26,7 @@ { direct_nested_ = false; scheme_type = GS_NONE; + local_index = false; loop_init = 0; loop_test = 0; loop_step = 0; @@ -112,3 +113,8 @@ } return out; } + +PNamedItem::SymbolType PGenerate::symbol_type() const +{ + return GENBLOCK; +} diff -Nru iverilog-10.3/PGenerate.h iverilog-11.0/PGenerate.h --- iverilog-10.3/PGenerate.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PGenerate.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PGenerate_H #define IVL_PGenerate_H /* - * Copyright (c) 2006-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2006-2020 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 @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -# include "LineInfo.h" +# include "PNamedItem.h" # include "StringHeap.h" # include "HName.h" # include "PScope.h" @@ -50,7 +50,7 @@ * The parent points to the GS_CASE that contains this item. * the loop_test is compared with the parent->loop_test expression. */ -class PGenerate : public LineInfo, public LexicalScope { +class PGenerate : public PNamedItem, public LexicalScope { public: explicit PGenerate(LexicalScope*parent, unsigned id_number); @@ -71,6 +71,9 @@ // generate loops have an index variable and three // expressions: for (index = ; ; index=) + // the index is local if it was declared in the init expression, + // e.g. for (genvar index = ; ; index=) + bool local_index; perm_string loop_index; PExpr*loop_init; PExpr*loop_test; @@ -107,6 +110,8 @@ void dump(ostream&out, unsigned indent) const; + SymbolType symbol_type() const; + private: bool generate_scope_loop_(Design*des, NetScope*container); bool generate_scope_condit_(Design*des, NetScope*container, bool else_flag); diff -Nru iverilog-10.3/PModport.cc iverilog-11.0/PModport.cc --- iverilog-10.3/PModport.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PModport.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2015-2019 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 @@ -29,3 +29,8 @@ PModport::~PModport() { } + +PNamedItem::SymbolType PModport::symbol_type() const +{ + return MODPORT; +} diff -Nru iverilog-10.3/PModport.h iverilog-11.0/PModport.h --- iverilog-10.3/PModport.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PModport.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PModport_H #define IVL_PModport_H /* - * Copyright (c) 2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2015-2019 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 @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -# include "LineInfo.h" +# include "PNamedItem.h" # include "PScope.h" # include "StringHeap.h" # include "netlist.h" @@ -28,7 +28,7 @@ /* * The PModport class represents a parsed SystemVerilog modport list. */ -class PModport : public LineInfo { +class PModport : public PNamedItem { public: // The name is a perm-allocated string. It is the simple name @@ -41,6 +41,8 @@ typedef pair simple_port_t; map simple_ports; + SymbolType symbol_type() const; + private: perm_string name_; diff -Nru iverilog-10.3/PNamedItem.cc iverilog-11.0/PNamedItem.cc --- iverilog-10.3/PNamedItem.cc 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/PNamedItem.cc 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,116 @@ +/* + * 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 "PNamedItem.h" +# include + +PNamedItem::PNamedItem() +{ +} + +PNamedItem::~PNamedItem() +{ +} + +PNamedItem::SymbolType PNamedItem::symbol_type() const +{ + return ANY; +} + +std::ostream& operator << (std::ostream&o, PNamedItem::SymbolType st) +{ + switch (st) { + case PNamedItem::ANY: + o << "a symbol"; + break; + case PNamedItem::PARAM: + o << "a parameter"; + break; + case PNamedItem::NET: + o << "a net"; + break; + case PNamedItem::VAR: + o << "a variable"; + break; + case PNamedItem::GENVAR: + o << "a genvar"; + break; + case PNamedItem::EVENT: + o << "an event"; + break; + case PNamedItem::TYPE: + o << "a type"; + break; + case PNamedItem::ENUM: + o << "an enum type or value"; + break; + case PNamedItem::CLASS: + o << "a class"; + break; + case PNamedItem::FUNCTION: + o << "a function"; + break; + case PNamedItem::TASK: + o << "a task"; + break; + case PNamedItem::BLOCK: + o << "a named block"; + break; + case PNamedItem::GENBLOCK: + o << "a generate block"; + break; + case PNamedItem::MODPORT: + o << "a modport"; + break; + case PNamedItem::PACKAGE: + o << "a package"; + break; + case PNamedItem::MODULE: + o << "a module"; + break; + case PNamedItem::PROGRAM: + o << "a program"; + break; + case PNamedItem::INTERFACE: + o << "an interface"; + break; + case PNamedItem::PRIMITIVE: + o << "a primitive"; + break; + case PNamedItem::INSTANCE: + o << "an instance name"; + break; + default: + break; + } + return o; +} + +PGenvar::PGenvar() +{ +} + +PGenvar::~PGenvar() +{ +} + +PNamedItem::SymbolType PGenvar::symbol_type() const +{ + return GENVAR; +} diff -Nru iverilog-10.3/PNamedItem.h iverilog-11.0/PNamedItem.h --- iverilog-10.3/PNamedItem.h 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/PNamedItem.h 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,57 @@ +#ifndef IVL_PNamedItem_H +#define IVL_PNamedItem_H +/* + * 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 "LineInfo.h" + +/* + * The PNamedItem class is the base class for all items that can be added + * to a scope's local symbol map. + */ +class PNamedItem : virtual public LineInfo { + + public: + enum SymbolType { ANY, PARAM, NET, VAR, GENVAR, EVENT, TYPE, ENUM, + CLASS, FUNCTION, TASK, BLOCK, GENBLOCK, MODPORT, + PACKAGE, MODULE, PROGRAM, INTERFACE, PRIMITIVE, + INSTANCE }; + + explicit PNamedItem(); + virtual ~PNamedItem(); + + virtual SymbolType symbol_type() const; +}; + +extern std::ostream& operator << (std::ostream&, PNamedItem::SymbolType); + +/* + * The PGenvar class represents a genvar. This is only used to represent + * genvar in a scope's local symbol map. + */ +class PGenvar : public PNamedItem { + + public: + explicit PGenvar(); + virtual ~PGenvar(); + + SymbolType symbol_type() const; +}; + +#endif /* IVL_PNamedItem_H */ diff -Nru iverilog-10.3/PScope.cc iverilog-11.0/PScope.cc --- iverilog-10.3/PScope.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PScope.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008,2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2019 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 @@ -33,12 +33,18 @@ return (*cur).second; } +PNamedItem::SymbolType LexicalScope::param_expr_t::symbol_type() const +{ + return PARAM; +} + PScope::PScope(perm_string n, LexicalScope*parent) : LexicalScope(parent), name_(n) { time_unit = 0; time_precision = 0; - time_from_timescale = false; + time_unit_is_default = true; + time_prec_is_default = true; } PScope::~PScope() @@ -51,6 +57,8 @@ PScopeExtra::PScopeExtra(perm_string n, LexicalScope*parent) : PScope(n, parent) { + time_unit_is_local = false; + time_prec_is_local = false; } PScopeExtra::~PScopeExtra() diff -Nru iverilog-10.3/PScope.h iverilog-11.0/PScope.h --- iverilog-10.3/PScope.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PScope.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PScope_H #define IVL_PScope_H /* - * Copyright (c) 2008-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2019 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 @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -# include "LineInfo.h" +# include "PNamedItem.h" # include "StringHeap.h" # include "pform_types.h" # include "ivl_target.h" @@ -61,6 +61,24 @@ lifetime_t default_lifetime; + // Symbols that are defined or declared in this scope. + std::maplocal_symbols; + + // Symbols that are explicitly imported. Bind the imported name + // to the package from which the name is imported. + std::mapexplicit_imports; + + // Packages that are wildcard imported. When identifiers from + // these packages are referenced, they will be added to the + // explicit imports (IEEE 1800-2012 26.3). + std::setpotential_imports; + + // A task or function call may reference a task or function defined + // later in the scope. So here we stash the potential imports for + // task and function calls. They will be added to the explicit + // imports if we don't find a local definition. + std::mappossible_imports; + struct range_t { // True if this is an exclude bool exclude_flag; @@ -79,7 +97,7 @@ /* The scope has parameters that are evaluated when the scope is elaborated. During parsing, I put the parameters into this map. */ - struct param_expr_t : public LineInfo { + struct param_expr_t : public PNamedItem { param_expr_t() : type(IVL_VT_NO_TYPE), msb(0), lsb(0), signed_flag(false), expr(0), range(0) { } // Type information ivl_variable_type_t type; @@ -90,9 +108,11 @@ PExpr*expr; // If there are range constraints, list them here range_t*range; + + SymbolType symbol_type() const; }; - mapparameters; - maplocalparams; + mapparameters; + maplocalparams; // Defined types in the scope. maptypedefs; @@ -100,10 +120,6 @@ // Named events in the scope. mapevents; - // Symbols that are imported. Bind the imported name to the - // package from which the name is imported. - std::mapimports; - // Nets and variables (wires) in the scope mapwires; PWire* wires_find(perm_string name); @@ -164,11 +180,18 @@ perm_string pscope_name() const { return name_; } - /* These are the timescale for this scope. The default is + /* These are the timescale for this scope. The value is set by the `timescale directive or, in SystemVerilog, by timeunit and timeprecision statements. */ int time_unit, time_precision; - bool time_from_timescale; + + /* Flags used to support warnings about timescales. */ + bool time_unit_is_default; + bool time_prec_is_default; + + bool has_explicit_timescale() const { + return !(time_unit_is_default || time_prec_is_default); + } protected: bool elaborate_sig_wires_(Design*des, NetScope*scope) const; @@ -199,6 +222,10 @@ elaboration to choose an elaboration order. */ std::vector classes_lexical; + /* Flags used to support warnings about timescales. */ + bool time_unit_is_local; + bool time_prec_is_local; + protected: void dump_classes_(ostream&out, unsigned indent) const; void dump_tasks_(ostream&out, unsigned indent) const; diff -Nru iverilog-10.3/PTask.cc iverilog-11.0/PTask.cc --- iverilog-10.3/PTask.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PTask.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2008,2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -73,3 +73,8 @@ assert(statement_ == 0); statement_ = s; } + +PNamedItem::SymbolType PTask::symbol_type() const +{ + return TASK; +} diff -Nru iverilog-10.3/PTask.h iverilog-11.0/PTask.h --- iverilog-10.3/PTask.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PTask.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PTask_H #define IVL_PTask_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -19,8 +19,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -# include "LineInfo.h" # include "PScope.h" +# include "PNamedItem.h" # include "StringHeap.h" # include # include @@ -35,7 +35,7 @@ class PExpr; -class PTaskFunc : public PScope, public LineInfo { +class PTaskFunc : public PScope, public PNamedItem { public: PTaskFunc(perm_string name, LexicalScope*parent); @@ -99,6 +99,8 @@ void dump(ostream&, unsigned) const; + SymbolType symbol_type() const; + private: Statement*statement_; bool is_auto_; @@ -148,6 +150,8 @@ void dump(ostream&, unsigned) const; + SymbolType symbol_type() const; + private: data_type_t* return_type_; Statement *statement_; diff -Nru iverilog-10.3/PWire.cc iverilog-11.0/PWire.cc --- iverilog-10.3/PWire.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PWire.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -29,7 +29,7 @@ : name_(n), type_(t), port_type_(pt), data_type_(dt), signed_(false), isint_(false), port_set_(false), net_set_(false), is_scalar_(false), - error_cnt_(0), set_data_type_(0), + error_cnt_(0), uarray_type_(0), set_data_type_(0), discipline_(0) { if (t == NetNet::INTEGER) { @@ -67,6 +67,7 @@ isint_ = true; return true; } + if (t == NetNet::IMPLICIT_REG) return true; return false; case NetNet::REG: if (t == NetNet::INTEGER) { @@ -282,3 +283,15 @@ { return discipline_; } + +PNamedItem::SymbolType PWire::symbol_type() const +{ + switch (type_) { + case NetNet::IMPLICIT_REG: + case NetNet::INTEGER: + case NetNet::REG: + return VAR; + default: + return NET; + } +} diff -Nru iverilog-10.3/PWire.h iverilog-11.0/PWire.h --- iverilog-10.3/PWire.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/PWire.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_PWire_H #define IVL_PWire_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -20,7 +20,7 @@ */ # include "netlist.h" -# include "LineInfo.h" +# include "PNamedItem.h" # include # include # include "StringHeap.h" @@ -51,7 +51,7 @@ * from that perspective, sub-scopes within the module are a part of * the wire name. */ -class PWire : public LineInfo { +class PWire : public PNamedItem { public: PWire(perm_string name, @@ -80,6 +80,7 @@ void set_range(const std::list&ranges, PWSRType type); void set_unpacked_idx(const std::list&ranges); + void set_uarray_type(uarray_type_t*type) { uarray_type_ = type; } void set_data_type(data_type_t*type); @@ -93,6 +94,8 @@ NetNet* elaborate_sig(Design*, NetScope*scope) const; + SymbolType symbol_type() const; + private: perm_string name_; NetNet::Type type_; @@ -117,6 +120,7 @@ // If this wire is actually a memory, these indices will give // me the size and address ranges of the memory. std::listunpacked_; + uarray_type_t*uarray_type_; // This is the complex type of the wire. the data_type_ may // modify how this is interpreted. diff -Nru iverilog-10.3/README.txt iverilog-11.0/README.txt --- iverilog-10.3/README.txt 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/README.txt 2020-09-26 22:44:25.000000000 +0000 @@ -6,7 +6,7 @@ Icarus Verilog is intended to compile ALL of the Verilog HDL as described in the IEEE-1364 standard. Of course, it's not quite there -yet. It does currently handle a mix of structural and behavioral +yet. It does currently handle a mix of structural and behavioural constructs. For a view of the current state of Icarus Verilog, see its home page at . @@ -20,13 +20,13 @@ 2.0 Building/Installing Icarus Verilog From Source -If you are starting from source, the build process is designed to be +If you are starting from the source, the build process is designed to be as simple as practical. Someone basically familiar with the target system and C/C++ compilation should be able to build the source distribution with little effort. Some actual programming skills are not required, but helpful in case of problems. -If you are building for Windows, see the mingw.txt file. +If you are building on Windows, see the mingw.txt file. 2.1 Compile Time Prerequisites @@ -46,8 +46,10 @@ to work. MSVC++ 5 and 6 are known to definitely *not* work. - bison and flex - It has been reported that bison 2.3 on MacOS generates broken - code, but bison 3.0.4 works. + OSX note: bison 2.3 shipped with MacOS including Catalina generates + broken code, but bison 3+ works. We recommend using the Fink + project version of bison and flex (finkproject.org), brew version + works fine either. - gperf 3.0 or later The lexical analyzer doesn't recognize keywords directly, @@ -64,7 +66,7 @@ that are needed. - termcap - The readline library in turn uses termcap. + The readline library, in turn, uses termcap. If you are building from git, you will also need software to generate the configure scripts. @@ -77,15 +79,21 @@ 2.2 Compilation Unpack the tar-ball and cd into the verilog-######### directory -(presumably that is how you got to this README) and compile the source +(presumably, that is how you got to this README) and compile the source with the commands: ./configure make +If you are building from git, you have to run the command below before +compiling the source. This will generate the "configure" file, which is +automatically done when building from tarball. + + sh autoconf.sh + Normally, this command automatically figures out everything it needs to know. It generally works pretty well. There are a few flags to the -configure script that modify its behavior: +configure script that modify its behaviour: --prefix= The default is /usr/local, which causes the tool suite to @@ -108,6 +116,12 @@ with the same prefix but different suffix are guaranteed to not interfere with each other. + --host= + Compile iverilog for a different platform. You can use: + x64_64-w64-mingw32 for building 64-bit Windows executables + i686-w64-mingw32 for building 32-bit Windows executables + Both options require installing the required mingw-w64 packages. + 2.3 (Optional) Testing To run a simple test before installation, execute @@ -157,7 +171,7 @@ step. There may be dangling references, and it is not yet clear which module is the root. -One can see a human readable version of the final pform by using the +One can see a human-readable version of the final pform by using the ``-P '' flag to the ``ivl'' subcommand. This will cause ivl to dump the pform into the file named . (Note that this is not normally done, unless debugging the ``ivl'' subcommand.) @@ -169,17 +183,17 @@ resolves references and expands the instantiations to form the design netlist. (See netlist.txt.) Final semantic checks are performed during elaboration, and some simple optimizations are performed. The netlist -includes all the behavioral descriptions, as well as gates and wires. +includes all the behavioural descriptions, as well as gates and wires. The elaborate() function performs the elaboration. -One can see a human readable version of the final, elaborated and +One can see a human-readable version of the final, elaborated and optimized netlist by using the ``-N '' flag to the compiler. If elaboration succeeds, the final netlist (i.e., after optimizations but before code generation) will be dumped into the file named . -Elaboration is actually performed in two steps: scopes and parameters -first, followed by the structural and behavioral elaboration. +Elaboration is performed in two steps: scopes and parameters +first, followed by the structural and behavioural elaboration. 3.3.1 Scope Elaboration @@ -188,7 +202,7 @@ with the root module represented by the root NetScope object. The elab_scope.cc file contains most of the code for handling this phase. -The tail of the elaborate_scope behavior (after the pform is +The tail of the elaborate_scope behaviour (after the pform is traversed) includes a scan of the NetScope tree to locate defparam assignments that were collected during scope elaboration. This is when the defparam overrides are applied to the parameters. @@ -197,14 +211,14 @@ After the scopes and parameters are generated and the NetScope tree fully formed, the elaboration runs through the pform again, this time -generating the structural and behavioral netlist. Parameters are +generating the structural and behavioural netlist. Parameters are elaborated and evaluated by now so all the constants of code generation are now known locally, so the netlist can be generated by simply passing through the pform. 3.4 Optimization -This is actually a collection of processing steps that perform +This is a collection of processing steps that perform optimizations that do not depend on the target technology. Examples of some useful transformations are @@ -222,7 +236,7 @@ design to suit the technology. The emit() method of the Design class performs this step. It runs -through the design elements, calling target functions as need arises +through the design elements, calling target functions as the need arises to generate actual output. The user selects the target code generator with the -t flag on the @@ -230,7 +244,7 @@ 3.6 ATTRIBUTES - NOTE: The $attribute syntax will soon be deprecated in favor of the + NOTE: The $attribute syntax will soon be deprecated in favour of the Verilog-2001 attribute syntax, which is cleaner and standardized. The parser accepts, as an extension to Verilog, the $attribute module @@ -253,7 +267,7 @@ attribute is given to every instantiation of the primitive. The syntax for the attribute statement is the same, except that the names a primitive earlier in the compilation unit and the statement is -placed in global scope, instead of within a module. The semicolon is +placed in the global scope, instead of within a module. The semicolon is not part of a type attribute. Note that attributes are also occasionally used for communication @@ -312,7 +326,7 @@ Icarus Verilog is in development - as such it still only supports a (growing) subset of Verilog. Below is a description of some of the -currently unsupported Verilog features. This list is not exhaustive, +currently unsupported Verilog features. This list is not exhaustive and does not account for errors in the compiler. See the Icarus Verilog web page for the current state of support for Verilog, and in particular, browse the bug report database for reported unsupported @@ -341,7 +355,7 @@ 5.1 Nonstandard Constructs or Behaviors Icarus Verilog includes some features that are not part of the -IEEE1364 standard, but have well defined meaning, and also sometimes +IEEE1364 standard, but have well-defined meaning, and also sometimes gives nonstandard (but extended) meanings to some features of the language that are defined. See the "extensions.txt" documentation for more details. @@ -358,7 +372,7 @@ function is undefined if the argument doesn't have a self-determined size. - The $sizeof function is deprecated in favor of $bits, which is + The $sizeof function is deprecated in favour of $bits, which is the same thing, but included in the SystemVerilog definition. $simtime @@ -384,8 +398,8 @@ Builtin system functions - Certain of the system functions have well defined meanings, so - can theoretically be evaluated at compile time, instead of + Certain of the system functions have well-defined meanings, so + can theoretically be evaluated at compile-time, instead of using runtime VPI code. Doing so means that VPI cannot override the definitions of functions handled in this manner. On the other hand, this makes them synthesizable, and @@ -404,8 +418,8 @@ Icarus Verilog does preprocess modules that are loaded from libraries via the -y mechanism. However, the only macros - defined during compilation of that file are those that it - defines itself (or includes) or that are defined on the + defined during the compilation of that file are those that it + defines itself (or includes) or that are defined in the command line or command file. Specifically, macros defined in the non-library source files @@ -422,7 +436,7 @@ Standard Verilog does not allow width fields in the %t formats of display strings. For example, this is illegal: - $display("Time is %0t", %time); + $display("Time is %0t", $time); Standard Verilog instead relies on the $timeformat to completely specify the format. @@ -446,7 +460,7 @@ time 0 race resolution. - Combinational logic is routinely modeled using always + Combinational logic is routinely modelled using always blocks. However, this can lead to race conditions if the inputs to the combinational block are initialized in initial statements. Icarus Verilog slightly modifies time 0 scheduling @@ -457,7 +471,7 @@ Nets with Types - Icarus Verilog support an extension syntax that allows nets + Icarus Verilog supports an extended syntax that allows nets and regs to be explicitly typed. The currently supported types are logic, bool and real. This implies that "logic" and "bool" are new keywords. Typical syntax is: @@ -474,6 +488,6 @@ Except where otherwise noted, Icarus Verilog, ivl and ivlpp are Copyright Stephen Williams. The proper notices are in the head of each file. However, I have early on received aid in the form of fixes, -Verilog guidance, and especially testing from many people. Testers in -particular include a larger community of people interested in a GPL +Verilog guidance, and especially testing from many people. Testers, in +particular, include a larger community of people interested in a GPL Verilog for Linux. diff -Nru iverilog-10.3/scripts/MAKE_RELEASE.sh iverilog-11.0/scripts/MAKE_RELEASE.sh --- iverilog-10.3/scripts/MAKE_RELEASE.sh 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/scripts/MAKE_RELEASE.sh 2020-09-26 22:44:25.000000000 +0000 @@ -4,13 +4,13 @@ # the number for a snapshot and the path to a temporary directory. # for example: # -# sh scripts/MAKE_RELEASE.sh 10.1 ~/tmp +# sh scripts/MAKE_RELEASE.sh 11.0 ~/tmp # -# The above assumes that there is a tag "v10_1" at the point +# The above assumes that there is a tag "v11_0" at the point # to be released. (The tag has the "v", but the argument to this # script does not have the "v"). This script extracts based on the # tag, uses the temporary directory to stage intermediate results, -# and finally creates a file called verilog-10.1.tar.gz that +# and finally creates a file called verilog-11.0.tar.gz that # contains the release ready to go. # # The complete steps to make a release x.y generally are: @@ -19,11 +19,11 @@ # # Edit verilog.spec to suit. # -# git tag -a v10_1 +# git tag -a v11_0 # (Make the tag in the local git repository.) # -# sh scripts/MAKE_RELEASE.sh 10.1 ~/tmp -# (Make the snapshot bundle verilog-10.1.tar.gz) +# sh scripts/MAKE_RELEASE.sh 11.0 ~/tmp +# (Make the snapshot bundle verilog-11.0.tar.gz) # # git push --tags # (Publish the tag to the repository.) diff -Nru iverilog-10.3/Statement.cc iverilog-11.0/Statement.cc --- iverilog-10.3/Statement.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/Statement.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -158,6 +158,11 @@ list_[0] = that; } +PNamedItem::SymbolType PBlock::symbol_type() const +{ + return BLOCK; +} + PCallTask::PCallTask(const pform_name_t&n, const list&p) : package_(0), path_(n), parms_(p.size()) { @@ -201,8 +206,8 @@ return path_; } -PCase::PCase(NetCase::TYPE t, PExpr*ex, svector*l) -: type_(t), expr_(ex), items_(l) +PCase::PCase(ivl_case_quality_t q, NetCase::TYPE t, PExpr*ex, svector*l) +: quality_(q), type_(t), expr_(ex), items_(l) { } @@ -294,20 +299,20 @@ } PEventStatement::PEventStatement(const svector&ee) -: expr_(ee), statement_(0) +: expr_(ee), statement_(0), always_sens_(false) { assert(expr_.count() > 0); } PEventStatement::PEventStatement(PEEvent*ee) -: expr_(1), statement_(0) +: expr_(1), statement_(0), always_sens_(false) { expr_[0] = ee; } -PEventStatement::PEventStatement(void) -: statement_(0) +PEventStatement::PEventStatement(bool always_sens) +: statement_(0), always_sens_(always_sens) { } @@ -411,8 +416,8 @@ delete expr_; } -PTrigger::PTrigger(const pform_name_t&e) -: event_(e) +PTrigger::PTrigger(PPackage*pkg, const pform_name_t&e) +: package_(pkg), event_(e) { } diff -Nru iverilog-10.3/Statement.h iverilog-11.0/Statement.h --- iverilog-10.3/Statement.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/Statement.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_Statement_H #define IVL_Statement_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2020 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 @@ -75,7 +75,7 @@ * fact, the Statement class is abstract and represents all the * possible kinds of statements that exist in Verilog. */ -class Statement : public LineInfo { +class Statement : virtual public LineInfo { public: Statement() { } @@ -170,7 +170,7 @@ * statements before constructing this object, so it knows a priori * what is contained. */ -class PBlock : public PScope, public Statement { +class PBlock : public PScope, public Statement, public PNamedItem { public: enum BL_TYPE { BL_SEQ, BL_PAR, BL_JOIN_NONE, BL_JOIN_ANY }; @@ -205,6 +205,8 @@ virtual void elaborate_scope(Design*des, NetScope*scope) const; virtual void elaborate_sig(Design*des, NetScope*scope) const; + SymbolType symbol_type() const; + private: BL_TYPE bl_type_; std::vectorlist_; @@ -230,6 +232,8 @@ NetProc*elaborate_method_(Design*des, NetScope*scope, bool add_this_flag = false) const; NetProc*elaborate_function_(Design*des, NetScope*scope) const; + NetProc*elaborate_void_function_(Design*des, NetScope*scope, + NetFuncDef*def) const; NetProc*elaborate_build_call_(Design*des, NetScope*scope, NetScope*task, NetExpr*use_this) const; @@ -237,6 +241,10 @@ NetNet*net, perm_string method_name, const char*sys_task_name) const; + NetProc*elaborate_queue_method_(Design*des, NetScope*scope, + NetNet*net, + perm_string method_name, + const char*sys_task_name) const; bool test_task_calls_ok_(Design*des, NetScope*scope) const; PPackage*package_; @@ -252,7 +260,7 @@ Statement*stat; }; - PCase(NetCase::TYPE, PExpr*ex, svector*); + PCase(ivl_case_quality_t, NetCase::TYPE, PExpr*ex, svector*); ~PCase(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; @@ -261,6 +269,7 @@ virtual void dump(ostream&out, unsigned ind) const; private: + ivl_case_quality_t quality_; NetCase::TYPE type_; PExpr*expr_; @@ -292,7 +301,7 @@ */ class PChainConstructor : public Statement { public: - PChainConstructor(const list&parms); + explicit PChainConstructor(const list&parms); ~PChainConstructor(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; @@ -402,8 +411,9 @@ explicit PEventStatement(const svector&ee); explicit PEventStatement(PEEvent*ee); - // Make an @* statement. - explicit PEventStatement(void); + // Make an @* statement or make a special @* version with the items + // from functions added and outputs removed for always_comb/latch. + explicit PEventStatement(bool always_sens = false); ~PEventStatement(); @@ -429,6 +439,7 @@ private: svectorexpr_; Statement*statement_; + bool always_sens_; }; ostream& operator << (ostream&o, const PEventStatement&obj); @@ -559,13 +570,14 @@ class PTrigger : public Statement { public: - explicit PTrigger(const pform_name_t&ev); + explicit PTrigger(PPackage*pkg, const pform_name_t&ev); ~PTrigger(); virtual NetProc* elaborate(Design*des, NetScope*scope) const; virtual void dump(ostream&out, unsigned ind) const; private: + PPackage*package_; pform_name_t event_; }; diff -Nru iverilog-10.3/svector.h iverilog-11.0/svector.h --- iverilog-10.3/svector.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/svector.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_svector_H #define IVL_svector_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -39,7 +39,7 @@ explicit svector(unsigned size) : nitems_(size), items_(new TYPE[size]) { for (unsigned idx = 0 ; idx < size ; idx += 1) - items_[idx] = 0; + items_[idx] = TYPE(0); } svector(const svector&that) @@ -57,7 +57,7 @@ items_[l.nitems_+idx] = r[idx]; } - svector(const svector&l, TYPE r) + svector(const svector&l, TYPE&r) : nitems_(l.nitems_ + 1), items_(new TYPE[nitems_]) { for (unsigned idx = 0 ; idx < l.nitems_ ; idx += 1) items_[idx] = l[idx]; diff -Nru iverilog-10.3/sv_vpi_user.h iverilog-11.0/sv_vpi_user.h --- iverilog-10.3/sv_vpi_user.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/sv_vpi_user.h 2020-09-26 22:44:25.000000000 +0000 @@ -21,7 +21,7 @@ # include "vpi_user.h" -#if defined(__MINGW32__) || defined (__CYGWIN32__) +#if defined(__MINGW32__) || defined (__CYGWIN__) # define DLLEXPORT __declspec(dllexport) #else # define DLLEXPORT @@ -72,6 +72,13 @@ /********* Many-to-One ***********/ #define vpiMember 742 +/********* task/function properties **********/ +#define vpiOtherFunc 6 + +/* Icarus-specific function type to use string as the return type */ +#define vpiStringFunc 10 +#define vpiSysFuncString vpiSysFuncString + EXTERN_C_END #endif /* SV_VPI_USER_H */ diff -Nru iverilog-10.3/symbol_search.cc iverilog-11.0/symbol_search.cc --- iverilog-10.3/symbol_search.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/symbol_search.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -66,6 +66,7 @@ bool prefix_scope = false; bool recurse_flag = false; + assert(li); ivl_assert(*li, ! path.empty()); name_component_t path_tail = path.back(); path.pop_back(); @@ -94,7 +95,7 @@ scope = recurse.scope; prefix_scope = true; - if (scope->is_auto() && li) { + if (scope->is_auto()) { cerr << li->get_fileline() << ": error: Hierarchical " "reference to automatically allocated item " "`" << path_tail.name << "' in path `" << path << "'" << endl; @@ -107,6 +108,9 @@ } while (scope) { + if (scope->genvar_tmp.str() && path_tail.name == scope->genvar_tmp) + return false; + if (path_tail.name == "#") { cerr << li->get_fileline() << ": sorry: " << "Implicit class handle \"super\" not supported." << endl; @@ -131,6 +135,11 @@ return true; } + if (NetScope*import_scope = scope->find_import(des, path_tail.name)) { + scope = import_scope; + continue; + } + if (recurse_flag) { bool flag = false; hname_t path_item = eval_path_component(des, start_scope, path_tail, flag); @@ -142,14 +151,21 @@ } } - // Don't scan up past a module boundary. - if (scope->type()==NetScope::MODULE && !scope->nested_module()) - break; // Don't scan up if we are searching within a prefixed scope. if (prefix_scope) break; - scope = scope->parent(); + // Don't scan up past a module boundary. + if (scope->type()==NetScope::MODULE && !scope->nested_module()) + scope = 0; + else + scope = scope->parent(); + + // Last chance - try the compilation unit. + if (scope == 0 && start_scope != 0) { + scope = start_scope->unit(); + start_scope = 0; + } } // Last chance: this is a single name, so it might be the name diff -Nru iverilog-10.3/syn-rules.y iverilog-11.0/syn-rules.y --- iverilog-10.3/syn-rules.y 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/syn-rules.y 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -304,6 +304,15 @@ last_ = first_; ptr_ = first_; + // Can the following be converted into S_ALWAYS? + if ((t->type() == IVL_PR_ALWAYS_COMB) || + (t->type() == IVL_PR_ALWAYS_FF) || + (t->type() == IVL_PR_ALWAYS_LATCH)) { + cerr << t->get_fileline() << ": internal error: " + << " Need to check if this can be synthesized." << endl; + assert(0); + } + first_->token = (t->type() == IVL_PR_ALWAYS)? S_ALWAYS : S_INITIAL; first_->top = t; first_->next_ = 0; diff -Nru iverilog-10.3/synth2.cc iverilog-11.0/synth2.cc --- iverilog-10.3/synth2.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/synth2.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2020 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 @@ -28,35 +28,273 @@ using namespace std; -bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&) +/* General notes on enables and bitmasks. + * + * When synthesising an asynchronous process that contains conditional + * statements (if/case statements), we need to determine the conditions + * that cause each nexus driven by that process to be updated. If a + * nexus is not updated under all circumstances, we must infer a latch. + * To this end, we generate an enable signal for each output nexus. As + * we walk the statement tree for the process, for each substatement we + * pass the enable signals generated so far into the synth_async method, + * and on return from the synth_async method, the enable signals will be + * updated to reflect any conditions introduced by that substatement. + * Once we have synthesised all the statements for that process, if an + * enable signal is not tied high, we must infer a latch for that nexus. + * + * When synthesising a synchronous process, we use the synth_async method + * to synthesise the combinatorial inputs to the D pins of the flip-flops + * we infer for that process. In this case the enable signal can be used + * as a clock enable for the flip-flop. This saves us explicitly feeding + * back the flip-flop output to undriven inputs of any synthesised muxes. + * + * The strategy described above is not sufficient when not all bits in + * a nexus are treated identically (i.e. different conditional clauses + * drive differing parts of the same vector). To handle this properly, + * we would (potentially) need to generate a separate enable signal for + * each bit in the vector. This would be a lot of work, particularly if + * we wanted to eliminate duplicates. For now, the strategy employed is + * to maintain a bitmask for each output nexus that identifies which bits + * in the nexus are unconditionally driven (driven by every clause). When + * we finish synthesising an asynchronous process, if the bitmask is not + * all ones, we must infer a latch. This currently results in an error, + * because to safely synthesise such a latch we would need the bit-level + * gate enables. When we finish synthesising a synchronous process, if + * the bitmask is not all ones, we explicitly feed the flip-flop outputs + * back to undriven inputs of any synthesised muxes to ensure undriven + * parts of the vector retain their previous state when the flip-flop is + * clocked. + * + * The enable signals are passed as links to the current output nexus + * for each signal. If an enable signal is not linked, this is treated + * as if the signal was tied low. + * + * The bitmasks are passed as bool vectors. 'true' indicates a bit is + * unconditionally driven. An empty vector (size = 0) indicates that + * the current substatement doesn't drive any bits in the nexus. + */ + +static void qualify_enable(Design*des, NetScope*scope, NetNet*qualifier, + bool active_state, NetLogic::TYPE gate_type, + Link&enable_i, Link&enable_o) { - return false; + if (enable_i.is_linked(scope->tie_lo())) { + connect(enable_o, scope->tie_lo()); + return; + } + + if (active_state == false) { + NetLogic*gate = new NetLogic(scope, scope->local_symbol(), + 2, NetLogic::NOT, 1); + des->add_node(gate); + connect(gate->pin(1), qualifier->pin(0)); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); + + qualifier = sig; + } + + if (enable_i.is_linked(scope->tie_hi())) { + connect(enable_o, qualifier->pin(0)); + return; + } + + NetLogic*gate = new NetLogic(scope, scope->local_symbol(), + 3, gate_type, 1); + des->add_node(gate); + connect(gate->pin(1), qualifier->pin(0)); + connect(gate->pin(2), enable_i); + connect(enable_o, gate->pin(0)); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); } -bool NetProc::synth_sync(Design*des, NetScope*scope, - bool& /* ff_negedge */, - NetNet* /* ff_clk */, NetBus& /* ff_ce */, - NetBus& /* ff_aclr*/, NetBus& /* ff_aset*/, - vector& /*ff_aset_value*/, - NexusSet&nex_map, NetBus&nex_out, - const vector&events) +static void multiplex_enables(Design*des, NetScope*scope, NetNet*select, + Link&enable_1, Link&enable_0, Link&enable_o) { - if (events.size() > 0) { - cerr << get_fileline() << ": error: Events are unaccounted" - << " for in process synthesis." << endl; - des->errors += 1; + if (!enable_1.is_linked() && + !enable_0.is_linked() ) + return; + + if ( enable_1.is_linked(scope->tie_hi()) && + enable_0.is_linked(scope->tie_hi()) ) { + connect(enable_o, scope->tie_hi()); + return; } - if (debug_synth2) { - cerr << get_fileline() << ": NetProc::synth_sync: " - << "This statement is an async input to a sync process." << endl; + if (enable_1.is_linked(scope->tie_lo()) || !enable_1.is_linked()) { + qualify_enable(des, scope, select, false, NetLogic::AND, + enable_0, enable_o); + return; + } + if (enable_0.is_linked(scope->tie_lo()) || !enable_0.is_linked()) { + qualify_enable(des, scope, select, true, NetLogic::AND, + enable_1, enable_o); + return; + } + if (enable_1.is_linked(scope->tie_hi())) { + qualify_enable(des, scope, select, true, NetLogic::OR, + enable_0, enable_o); + return; + } + if (enable_0.is_linked(scope->tie_hi())) { + qualify_enable(des, scope, select, false, NetLogic::OR, + enable_1, enable_o); + return; } - /* Synthesize the input to the DFF. */ - NetBus accumulated_nex_out (scope, nex_out.pin_count()); - return synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); + NetMux*mux = new NetMux(scope, scope->local_symbol(), 1, 2, 1); + des->add_node(mux); + connect(mux->pin_Sel(), select->pin(0)); + connect(mux->pin_Data(1), enable_1); + connect(mux->pin_Data(0), enable_0); + connect(enable_o, mux->pin_Result()); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), mux->pin_Result()); } +static void merge_sequential_enables(Design*des, NetScope*scope, + Link&top_enable, Link&sub_enable) +{ + if (!sub_enable.is_linked()) + return; + + if (top_enable.is_linked(scope->tie_hi())) + return; + + if (sub_enable.is_linked(scope->tie_hi())) + top_enable.unlink(); + + if (top_enable.is_linked()) { + NetLogic*gate = new NetLogic(scope, scope->local_symbol(), + 3, NetLogic::OR, 1); + des->add_node(gate); + connect(gate->pin(1), sub_enable); + connect(gate->pin(2), top_enable); + top_enable.unlink(); + connect(top_enable, gate->pin(0)); + + NetNet*sig = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, + &netvector_t::scalar_logic); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); + } else { + connect(top_enable, sub_enable); + } +} + +static void merge_sequential_masks(NetProc::mask_t&top_mask, NetProc::mask_t&sub_mask) +{ + if (sub_mask.size() == 0) + return; + + if (top_mask.size() == 0) { + top_mask = sub_mask; + return; + } + + assert(top_mask.size() == sub_mask.size()); + for (unsigned idx = 0 ; idx < top_mask.size() ; idx += 1) { + if (sub_mask[idx] == true) + top_mask[idx] = true; + } +} + +static void merge_parallel_masks(NetProc::mask_t&top_mask, NetProc::mask_t&sub_mask) +{ + if (sub_mask.size() == 0) + return; + + if (top_mask.size() == 0) { + top_mask = sub_mask; + return; + } + + assert(top_mask.size() == sub_mask.size()); + for (unsigned idx = 0 ; idx < top_mask.size() ; idx += 1) { + if (sub_mask[idx] == false) + top_mask[idx] = false; + } +} + +static bool all_bits_driven(NetProc::mask_t&mask) +{ + if (mask.size() == 0) + return false; + + for (unsigned idx = 0 ; idx < mask.size() ; idx += 1) { + if (mask[idx] == false) + return false; + } + return true; +} + +bool NetProcTop::tie_off_floating_inputs_(Design*des, + NexusSet&nex_map, NetBus&nex_in, + vector&bitmasks, + bool is_ff_input) +{ + bool flag = true; + for (unsigned idx = 0 ; idx < nex_in.pin_count() ; idx += 1) { + if (nex_in.pin(idx).nexus()->has_floating_input()) { + if (all_bits_driven(bitmasks[idx])) { + // If all bits are unconditionally driven, we can + // use the enable signal to prevent the flip-flop/ + // latch from updating when an undriven mux input + // is selected, so we can just tie off the input. + unsigned width = nex_map[idx].wid; + NetLogic*gate = new NetLogic(scope(), scope()->local_symbol(), + 1, NetLogic::PULLDOWN, width); + des->add_node(gate); + connect(nex_in.pin(idx), gate->pin(0)); + + if (nex_in.pin(idx).nexus()->pick_any_net()) + continue; + + ivl_variable_type_t data_type = IVL_VT_LOGIC; + netvector_t*tmp_vec = new netvector_t(data_type, width-1,0); + NetNet*sig = new NetNet(scope(), scope()->local_symbol(), + NetNet::WIRE, tmp_vec); + sig->local_flag(true); + connect(sig->pin(0), gate->pin(0)); + } else if (is_ff_input) { + // For a flip-flop, we can feed back the output + // to ensure undriven bits hold their last value. + connect(nex_in.pin(idx), nex_map[idx].lnk); + } else { + // This infers a latch, but without generating + // gate enable signals at the bit-level, we + // can't safely latch the undriven bits (we + // shouldn't generate combinatorial loops). + cerr << get_fileline() << ": warning: A latch " + << "has been inferred for some bits of '" + << nex_map[idx].lnk.nexus()->pick_any_net()->name() + << "'." << endl; + + cerr << get_fileline() << ": sorry: Bit-level " + "latch gate enables are not currently " + "supported in synthesis." << endl; + des->errors += 1; + flag = false; + } + } + } + return flag; +} + +bool NetProc::synth_async(Design*, NetScope*, NexusSet&, NetBus&, NetBus&, vector&) +{ + return false; +} /* * Async synthesis of assignments is done by synthesizing the rvalue @@ -70,8 +308,17 @@ */ bool NetAssignBase::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { + if (dynamic_cast(this) || dynamic_cast(this) || + dynamic_cast(this) || dynamic_cast(this)) { + cerr << get_fileline() << ": sorry: Procedural continuous " + "assignment is not currently supported in synthesis." + << endl; + des->errors += 1; + return false; + } + /* If the lval is a concatenation, synthesise each part separately. */ if (lval_->more ) { @@ -90,7 +337,7 @@ eval_expr(rval_, width); NetAssign_*more = lval_->more; lval_->more = 0; - if (!synth_async(des, scope, nex_map, nex_out, accumulated_nex_out)) + if (!synth_async(des, scope, nex_map, nex_out, enables, bitmasks)) flag = false; lval_ = lval_->more = more; offset += width; @@ -100,11 +347,12 @@ return flag; } + assert(rval_); NetNet*rsig = rval_->synthesize(des, scope, rval_); assert(rsig); if (lval_->word() && ! dynamic_cast(lval_->word())) { - cerr << get_fileline() << ": sorry: assignment to variable " + cerr << get_fileline() << ": sorry: Assignment to variable " "location in memory is not currently supported in " "synthesis." << endl; des->errors += 1; @@ -114,7 +362,7 @@ NetNet*lsig = lval_->sig(); if (!lsig) { cerr << get_fileline() << ": error: " - << "NetAssignBase::synth_async on unsupported lval "; + "NetAssignBase::synth_async on unsupported lval "; dump_lval(cerr); cerr << endl; des->errors += 1; @@ -138,17 +386,37 @@ << ", nex_out.pin_count()==" << nex_out.pin_count() << endl; } + unsigned ptr = 0; + if (nex_out.pin_count() > 1) { + NexusSet tmp_set; + nex_output(tmp_set); + ivl_assert(*this, tmp_set.size() == 1); + ptr = nex_map.find_nexus(tmp_set[0]); + ivl_assert(*this, nex_out.pin_count() > ptr); + ivl_assert(*this, enables.pin_count() > ptr); + ivl_assert(*this, bitmasks.size() > ptr); + } else { + ivl_assert(*this, nex_out.pin_count() == 1); + ivl_assert(*this, enables.pin_count() == 1); + ivl_assert(*this, bitmasks.size() == 1); + } + + unsigned lval_width = lval_->lwidth(); + unsigned lsig_width = lsig->vector_width(); + ivl_assert(*this, nex_map[ptr].wid == lsig_width); + // Here we note if the l-value is actually a bit/part // select. If so, generate a NetPartSelect to perform the select. - if ((lval_->lwidth()!=lsig->vector_width()) && !scope->loop_index_tmp.empty()) { + bool is_part_select = lval_width != lsig_width; + + long base_off = 0; + if (is_part_select && !scope->loop_index_tmp.empty()) { // If we are within a NetForLoop, there may be an index // value. That is collected from the scope member // loop_index_tmp, and the evaluate_function method // knows how to apply it. ivl_assert(*this, !scope->loop_index_tmp.empty()); - ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); - - long base_off = 0; + ivl_assert(*this, lval_width < lsig_width); // Evaluate the index expression to a constant. const NetExpr*base_expr_raw = lval_->get_base(); @@ -160,65 +428,67 @@ ivl_assert(*this, base_off >= 0); ivl_variable_type_t tmp_data_type = rsig->data_type(); - netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); + netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); - NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_->lwidth(), NetPartSelect::PV); + NetPartSelect*ps = new NetPartSelect(tmp, base_off, lval_width, NetPartSelect::PV); ps->set_line(*this); des->add_node(ps); connect(ps->pin(0), rsig->pin(0)); rsig = tmp; - } else if (lval_->lwidth() != lsig->vector_width()) { + } else if (is_part_select) { // In this case, there is no loop_index_tmp, so we are // not within a NetForLoop. Generate a NetSubstitute // object to handle the bit/part-select in the l-value. ivl_assert(*this, scope->loop_index_tmp.empty()); - ivl_assert(*this, lval_->lwidth() < lsig->vector_width()); - - long base_off = 0; + ivl_assert(*this, lval_width < lsig_width); const NetExpr*base_expr_raw = lval_->get_base(); ivl_assert(*this, base_expr_raw); NetExpr*base_expr = base_expr_raw->evaluate_function(*this, scope->loop_index_tmp); if (! eval_as_long(base_off, base_expr)) { - ivl_assert(*this, 0); + cerr << get_fileline() << ": sorry: assignment to variable " + "bit location is not currently supported in " + "synthesis." << endl; + des->errors += 1; + return false; } ivl_assert(*this, base_off >= 0); ivl_variable_type_t tmp_data_type = rsig->data_type(); - netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig->vector_width()-1,0); + netvector_t*tmp_type = new netvector_t(tmp_data_type, lsig_width-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); - ivl_assert(*this, accumulated_nex_out.pin_count()==1); - NetNet*use_lsig = lsig; - if (accumulated_nex_out.pin(0).is_linked()) { + NetNet*isig = nex_out.pin(ptr).nexus()->pick_any_net(); + if (isig) { if (debug_synth2) { cerr << get_fileline() << ": NetAssignBase::synth_async: " - << " Found a use_sig:" << endl; - accumulated_nex_out.pin(0).dump_link(cerr, 8); + << " Found an isig:" << endl; + nex_out.pin(ptr).dump_link(cerr, 8); } - Nexus*tmp_nex = accumulated_nex_out.pin(0).nexus(); - use_lsig = tmp_nex->pick_any_net(); - ivl_assert(*this, use_lsig); } else { if (debug_synth2) { cerr << get_fileline() << ": NetAssignBase::synth_async: " - << " Found no use_sig, resorting to lsig." << endl; + << " Found no isig, resorting to lsig." << endl; } + isig = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, NetNet::not_an_array, tmp_type); + isig->local_flag(true); + isig->set_line(*this); + connect(isig->pin(0), nex_out.pin(ptr)); } - NetSubstitute*ps = new NetSubstitute(use_lsig, rsig, - tmp->vector_width(), - base_off); + ivl_assert(*this, isig); + NetSubstitute*ps = new NetSubstitute(isig, rsig, lsig_width, base_off); ps->set_line(*this); des->add_node(ps); @@ -226,29 +496,36 @@ rsig = tmp; } - rsig = crop_to_width(des, rsig, lsig->vector_width()); - - if (nex_out.pin_count() > 1) { - NexusSet tmp_set; - nex_output(tmp_set); - ivl_assert(*this, tmp_set.size()==1); - unsigned ptr = nex_map.find_nexus(tmp_set[0]); - ivl_assert(*this, rsig->pin_count()==1); - ivl_assert(*this, nex_map.size()==nex_out.pin_count()); - ivl_assert(*this, nex_out.pin_count() > ptr); - connect(nex_out.pin(ptr), rsig->pin(0)); + rsig = crop_to_width(des, rsig, lsig_width); + ivl_assert(*this, rsig->pin_count()==1); + nex_out.pin(ptr).unlink(); + enables.pin(ptr).unlink(); + connect(nex_out.pin(ptr), rsig->pin(0)); + connect(enables.pin(ptr), scope->tie_hi()); + + mask_t&bitmask = bitmasks[ptr]; + if (is_part_select) { + if (bitmask.size() == 0) { + bitmask = mask_t (lsig_width, false); + } + ivl_assert(*this, bitmask.size() == lsig_width); + for (unsigned idx = 0; idx < lval_width; idx += 1) { + bitmask[base_off + idx] = true; + } + } else if (bitmask.size() > 0) { + for (unsigned idx = 0; idx < bitmask.size(); idx += 1) { + bitmask[idx] = true; + } } else { - ivl_assert(*this, nex_out.pin_count()==1); - ivl_assert(*this, rsig->pin_count()==1); - connect(nex_out.pin(0), rsig->pin(0)); + bitmask = mask_t (lsig_width, true); } /* This lval_ represents a reg that is a WIRE in the synthesized results. This function signals the destructor to change the REG that this l-value refers to into a WIRE. It is done then, at the last minute, so that pending - synthesis can continue to work with it as a WIRE. */ + synthesis can continue to work with it as a REG. */ lval_->turn_sig_to_wire_on_release(); return true; @@ -256,9 +533,15 @@ bool NetProc::synth_async_block_substatement_(Design*des, NetScope*scope, NexusSet&nex_map, - NetBus&accumulated_nex_out, + NetBus&nex_out, + NetBus&enables, + vector&bitmasks, NetProc*substmt) { + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); + // Create a temporary map of the output only from this statement. NexusSet tmp_map; substmt->nex_output(tmp_map); @@ -267,69 +550,69 @@ << "tmp_map.size()==" << tmp_map.size() << " for statement at " << substmt->get_fileline() << endl; - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " - << "accumulated_nex_out[" << idx << "] dump link" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); + << "incoming nex_out[" << idx << "] dump link" << endl; + nex_out.pin(idx).dump_link(cerr, 8); } } - /* Create also a temporary NetBus to collect the - output from the synthesis. */ + // Create temporary variables to collect the output from the synthesis. NetBus tmp_out (scope, tmp_map.size()); + NetBus tmp_ena (scope, tmp_map.size()); + vector tmp_masks (tmp_map.size()); - // Map (and move) the accumulated_nex_out for this block - // to the version that we can pass to the next - // statement. We will move the result back later. - NetBus accumulated_tmp_out (scope, tmp_map.size()); - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { - unsigned ptr = tmp_map.find_nexus(nex_map[idx]); - if (ptr >= tmp_map.size()) - continue; - - connect(accumulated_tmp_out.pin(ptr), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); + // Map (and move) the accumulated nex_out for this block + // to the version that we can pass to the next statement. + // We will move the result back later. + for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { + unsigned ptr = nex_map.find_nexus(tmp_map[idx]); + ivl_assert(*this, ptr < nex_out.pin_count()); + connect(tmp_out.pin(idx), nex_out.pin(ptr)); + nex_out.pin(ptr).unlink(); } if (debug_synth2) { for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: nex_map[" << idx << "] dump link, base=" << nex_map[idx].base << ", wid=" << nex_map[idx].wid << endl; nex_map[idx].lnk.dump_link(cerr, 8); - } + } for (unsigned idx = 0 ; idx < tmp_map.size() ; idx += 1) { - cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_map[" << idx << "] dump link, base=" << tmp_map[idx].base << ", wid=" << tmp_map[idx].wid << endl; + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_map[" << idx << "] dump link, base=" << tmp_map[idx].base << ", wid=" << tmp_map[idx].wid << endl; tmp_map[idx].lnk.dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < accumulated_tmp_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: accumulated_tmp_out[" << idx << "] dump link" << endl; - accumulated_tmp_out.pin(idx).dump_link(cerr, 8); - } + } + for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: tmp_out[" << idx << "] dump link" << endl; + tmp_out.pin(idx).dump_link(cerr, 8); + } } - bool ok_flag = substmt->synth_async(des, scope, tmp_map, tmp_out, accumulated_tmp_out); + + bool flag = substmt->synth_async(des, scope, tmp_map, tmp_out, tmp_ena, tmp_masks); if (debug_synth2) { cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " - "substmt->synch_async(...) --> " << (ok_flag? "true" : "false") + "substmt->synch_async(...) --> " << (flag? "true" : "false") << " for statement at " << substmt->get_fileline() << "." << endl; } - if (ok_flag == false) - return false; + if (!flag) return false; // Now map the output from the substatement back to the - // accumulated_nex_out for this block. Look for the - // nex_map pin that is linked to the tmp_map.pin(idx) - // pin, and link that to the tmp_out.pin(idx) output link. + // outputs for this block. for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { unsigned ptr = nex_map.find_nexus(tmp_map[idx]); - ivl_assert(*this, ptr < accumulated_nex_out.pin_count()); + ivl_assert(*this, ptr < nex_out.pin_count()); if (debug_synth2) { - cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " + cerr << get_fileline() << ": NetProc::synth_async_block_substatement_: " << "tmp_out.pin(" << idx << "):" << endl; tmp_out.pin(idx).dump_link(cerr, 8); - } - connect(accumulated_nex_out.pin(ptr), tmp_out.pin(idx)); + } + connect(nex_out.pin(ptr), tmp_out.pin(idx)); + + merge_sequential_enables(des, scope, enables.pin(ptr), tmp_ena.pin(idx)); + + merge_sequential_masks(bitmasks[ptr], tmp_masks[idx]); } return true; @@ -337,13 +620,13 @@ /* * Sequential blocks are translated to asynchronous logic by - * translating each statement of the block, in order, into gates. The - * nex_out for the block is the union of the nex_out for all the - * substatements. + * translating each statement of the block, in order, into gates. + * The nex_out for the block is the union of the nex_out for all + * the substatements. */ bool NetBlock::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { if (last_ == 0) { return true; @@ -354,19 +637,12 @@ do { cur = cur->next_; - bool ok_flag = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - cur); - flag = flag && ok_flag; - if (ok_flag == false) - continue; + bool sub_flag = synth_async_block_substatement_(des, scope, nex_map, nex_out, + enables, bitmasks, cur); + flag = flag && sub_flag; } while (cur != last_); - // The output from the block is now the accumulated outputs. - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) - connect(nex_out.pin(idx), accumulated_nex_out.pin(idx)); - return flag; } @@ -411,7 +687,7 @@ // Create the concat: osig = {...,...} NetConcat*osig_cat = new NetConcat(scope, scope->local_symbol(), - sel_need, 2, true); + sel_need, 2, !disable_concatz_generation); osig_cat->set_line(loc); des->add_node(osig_cat); connect(osig_cat->pin(0), osig->pin(0)); @@ -435,13 +711,13 @@ // Create the part select esig[M-1:N-1] NetPartSelect*ps1 = new NetPartSelect(esig, sel_need-1, - sel_got-sel_need, + sel_got-sel_need+1, NetPartSelect::VP); ps1->set_line(loc); des->add_node(ps1); connect(ps1->pin(1), esig->pin(0)); - netvector_t*ps1_vec = new netvector_t(osig_data_type, sel_got-sel_need-1, 0); + netvector_t*ps1_vec = new netvector_t(osig_data_type, sel_got-sel_need, 0); NetNet*ps1_sig = new NetNet(scope, scope->local_symbol(), NetNet::TRI, ps1_vec); ps1_sig->local_flag(true); @@ -450,7 +726,7 @@ // Create the reduction OR: | esig[M-1:N-1] NetUReduce*ered = new NetUReduce(scope, scope->local_symbol(), - NetUReduce::OR, sel_got-sel_need); + NetUReduce::OR, sel_got-sel_need+1); ered->set_line(loc); des->add_node(ered); connect(ered->pin(1), ps1_sig->pin(0)); @@ -469,17 +745,23 @@ bool NetCase::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { if (type()==NetCase::EQZ || type()==NetCase::EQX) - return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out); + return synth_async_casez_(des, scope, nex_map, nex_out, + enables, bitmasks); // Special case: If the case expression is constant, then this // is a pattern where the guards are non-constant and tested // against a constant case. Handle this as chained conditions // instead. if (dynamic_cast (expr_)) - return synth_async_casez_(des, scope, nex_map, nex_out, accumulated_nex_out); + return synth_async_casez_(des, scope, nex_map, nex_out, + enables, bitmasks); + + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " @@ -497,8 +779,6 @@ << "selector width (sel_width) = " << sel_width << endl; } - ivl_assert(*this, nex_map.size() == nex_out.pin_count()); - vector mux_width (nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { mux_width[idx] = nex_map[idx].wid; @@ -509,27 +789,33 @@ } } - // The accumulated_nex_out is taken as the input for this - // statement. Since there are collection of statements that - // start at this same point, we save all these inputs and - // reuse them for each statement. + // The incoming nex_out is taken as the input for this + // statement. Since there are collection of statements + // that start at this same point, we save all these + // inputs and reuse them for each statement. Unlink the + // nex_out now, so we can hook up the mux outputs. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); + connect(statement_input.pin(idx), nex_out.pin(idx)); + nex_out.pin(idx).unlink(); + if (debug_synth2) { + cerr << get_fileline() << ": NetCase::synth_async: " + << "statement_input.pin(" << idx << "):" << endl; + statement_input.pin(idx).dump_link(cerr, 8); + } } - /* Collect all the statements into a map of index to - statement. The guard expression it evaluated to be the - index of the mux value, and the statement is bound to that - index. */ + /* Collect all the statements into a map of index to statement. + The guard expression it evaluated to be the index of the mux + value, and the statement is bound to that index. */ unsigned long max_guard_value = 0; mapstatement_map; - NetProc*statement_default = 0; + NetProc*default_statement = 0; for (size_t item = 0 ; item < items_.size() ; item += 1) { if (items_[item].guard == 0) { - statement_default = items_[item].statement; + default_statement = items_[item].statement; continue; } @@ -559,8 +845,13 @@ if (sel_idx > max_guard_value) max_guard_value = sel_idx; - ivl_assert(*this, items_[item].statement); - statement_map[sel_idx] = items_[item].statement; + if (items_[item].statement) { + statement_map[sel_idx] = items_[item].statement; + continue; + } + + // Handle the special case of an empty statement. + statement_map[sel_idx] = this; } // The minimum selector width is the number of inputs that @@ -570,7 +861,7 @@ // If the sel_width can select more than just the explicit // guard values, and there is a default statement, then adjust // the sel_need to allow for the implicit selections. - if (statement_default && (sel_width > sel_need)) + if (default_statement && (sel_width > sel_need)) sel_need += 1; // The mux size is always an exact power of 2. @@ -599,139 +890,163 @@ esig = mux_selector_reduce_width(des, scope, *this, esig, sel_need); } - if (!statement_default && (statement_map.size() != ((size_t)1 << sel_width))) { - cerr << get_fileline() << ": sorry: Latch inferred from " - << "incomplete case statement. This is not supported " - << "in synthesis." << endl; - des->errors += 1; - return false; + /* If there is a default clause, synthesize it once and we'll + link it in wherever it is needed. If there isn't, create + a dummy default to pass on the accumulated nex_out from + preceding statements. */ + NetBus default_out (scope, nex_out.pin_count()); + NetBus default_ena (scope, nex_out.pin_count()); + vector default_masks (nex_out.pin_count()); + + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + connect(default_out.pin(idx), statement_input.pin(idx)); + connect(default_ena.pin(idx), scope->tie_lo()); } - /* If there is a default clause, synthesize it once and we'll - link it in wherever it is needed. */ - NetBus default_bus (scope, nex_map.size()); - if (statement_default) { - - bool flag = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - statement_default); - if (!flag) { - return false; - } + if (default_statement) { - for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) { - connect(default_bus.pin(idx), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); - } + bool flag = synth_async_block_substatement_(des, scope, nex_map, default_out, + default_ena, default_masks, + default_statement); + if (!flag) return false; if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async: " - << "synthesize default clause at " << statement_default->get_fileline() + << "synthesize default clause at " << default_statement->get_fileline() << " is done." << endl; } } - vector mux (mux_width.size()); - for (size_t mdx = 0 ; mdx < mux_width.size() ; mdx += 1) { - mux[mdx] = new NetMux(scope, scope->local_symbol(), - mux_width[mdx], mux_size, sel_need); - des->add_node(mux[mdx]); + vector out_mux (nex_out.pin_count()); + vector ena_mux (nex_out.pin_count()); + vector full_case (nex_out.pin_count()); + for (size_t mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + out_mux[mdx] = new NetMux(scope, scope->local_symbol(), + mux_width[mdx], mux_size, sel_need); + des->add_node(out_mux[mdx]); // The select signal is already synthesized, and is // common for every mux of this case statement. Simply // hook it up. - connect(mux[mdx]->pin_Sel(), esig->pin(0)); + connect(out_mux[mdx]->pin_Sel(), esig->pin(0)); // The outputs are in the nex_out, and connected to the // mux Result pins. - connect(mux[mdx]->pin_Result(), nex_out.pin(mdx)); + connect(out_mux[mdx]->pin_Result(), nex_out.pin(mdx)); // Make sure the output is now connected to a net. If // not, then create a fake one to carry the net-ness of // the pin. - if (mux[mdx]->pin_Result().nexus()->pick_any_net() == 0) { + if (out_mux[mdx]->pin_Result().nexus()->pick_any_net() == 0) { ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; - netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); + netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, tmp_vec); + NetNet::WIRE, tmp_vec); tmp->local_flag(true); ivl_assert(*this, tmp->vector_width() != 0); - connect(mux[mdx]->pin_Result(), tmp->pin(0)); + connect(out_mux[mdx]->pin_Result(), tmp->pin(0)); } + + // Create a mux for the enables, but don't hook it up + // until we know we need it. + ena_mux[mdx] = new NetMux(scope, scope->local_symbol(), + 1, mux_size, sel_need); + + // Assume a full case to start with. We'll check this as + // we synthesise each clause. + full_case[mdx] = true; } for (unsigned idx = 0 ; idx < mux_size ; idx += 1) { NetProc*stmt = statement_map[idx]; - if (stmt==0 && statement_default) { - ivl_assert(*this, default_bus.pin_count() == mux.size()); - for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) - connect(mux[mdx]->pin_Data(idx), default_bus.pin(mdx)); - + if (stmt==0) { + ivl_assert(*this, default_out.pin_count() == out_mux.size()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(out_mux[mdx]->pin_Data(idx), default_out.pin(mdx)); + connect(ena_mux[mdx]->pin_Data(idx), default_ena.pin(mdx)); + merge_parallel_masks(bitmasks[mdx], default_masks[mdx]); + if (!default_ena.pin(mdx).is_linked(scope->tie_hi())) + full_case[mdx] = false; + } continue; } ivl_assert(*this, stmt); - - NetBus accumulated_tmp (scope, nex_map.size()); - for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1) - connect(accumulated_tmp.pin(pin), statement_input.pin(pin)); - - - synth_async_block_substatement_(des, scope, nex_map, accumulated_tmp, stmt); - - for (size_t mdx = 0 ; mdx < mux.size() ; mdx += 1) { - connect(mux[mdx]->pin_Data(idx), accumulated_tmp.pin(mdx)); - - if (mux[mdx]->pin_Data(idx).nexus()->pick_any_net()==0) { - cerr << get_fileline() << ": warning: case " << idx - << " has no input for mux slice " << mdx << "." << endl; - - ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; - netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); - NetNet*tmpn = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, tmp_vec); - tmpn->local_flag(true); - ivl_assert(*this, tmpn->vector_width() != 0); - connect(mux[mdx]->pin_Data(idx), tmpn->pin(0)); + if (stmt == this) { + // Handle the special case of an empty statement. + ivl_assert(*this, statement_input.pin_count() == out_mux.size()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(out_mux[mdx]->pin_Data(idx), statement_input.pin(mdx)); + connect(ena_mux[mdx]->pin_Data(idx), scope->tie_lo()); + bitmasks[mdx] = mask_t (mux_width[mdx], false); + full_case[mdx] = false; } - ivl_assert(*this, mux[mdx]->pin_Data(idx).nexus()->pick_any_net()); + continue; + } + + NetBus tmp_out (scope, nex_out.pin_count()); + NetBus tmp_ena (scope, nex_out.pin_count()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(tmp_out.pin(mdx), statement_input.pin(mdx)); + connect(tmp_ena.pin(mdx), scope->tie_lo()); + } + vector tmp_masks (nex_out.pin_count()); + bool flag = synth_async_block_substatement_(des, scope, nex_map, tmp_out, + tmp_ena, tmp_masks, stmt); + if (!flag) return false; + + for (size_t mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + connect(out_mux[mdx]->pin_Data(idx), tmp_out.pin(mdx)); + connect(ena_mux[mdx]->pin_Data(idx), tmp_ena.pin(mdx)); + merge_parallel_masks(bitmasks[mdx], tmp_masks[mdx]); + if (!tmp_ena.pin(mdx).is_linked(scope->tie_hi())) + full_case[mdx] = false; } } + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + // Optimize away the enable mux if we have a full case, + // otherwise hook it up. + if (full_case[mdx]) { + connect(enables.pin(mdx), scope->tie_hi()); + delete ena_mux[mdx]; + continue; + } + + des->add_node(ena_mux[mdx]); + + connect(ena_mux[mdx]->pin_Sel(), esig->pin(0)); + + connect(enables.pin(mdx), ena_mux[mdx]->pin_Result()); + + NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet::WIRE, &netvector_t::scalar_logic); + tmp->local_flag(true); + connect(ena_mux[mdx]->pin_Result(), tmp->pin(0)); + } return true; } /* * casez statements are hard to implement as a single wide mux because * the test doesn't really map to a select input. Instead, implement - * it as a chain of binary muxes. This gives the synthesizer my + * it as a chain of binary muxes. This gives the synthesizer more * flexibility, and is more typically what is desired from a casez anyhow. */ bool NetCase::synth_async_casez_(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); + /* Synthesize the select expression. */ NetNet*esig = expr_->synthesize(des, scope, expr_); unsigned sel_width = esig->vector_width(); ivl_assert(*this, sel_width > 0); - ivl_assert(*this, nex_map.size() == nex_out.pin_count()); - - if (debug_synth2) { - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCase::synth_async_casez_: " - << "nex_out.pin(" << idx << "):" << endl; - nex_out.pin(idx).dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCase::synth_async_casez_: " - << "accumulated_nex_out.pin(" << idx << "):" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); - } - } - vectormux_width (nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { mux_width[idx] = nex_map[idx].wid; @@ -742,42 +1057,50 @@ } } - // The accumulated_nex_out is taken as the input for this - // statement. Since there are collection of statements that - // start at this same point, we save all these inputs and - // reuse them for each statement. + // The incoming nex_out is taken as the input for this + // statement. Since there are collection of statements + // that start at this same point, we save all these + // inputs and reuse them for each statement. Unlink the + // nex_out now, so we can hook up the mux outputs. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); + connect(statement_input.pin(idx), nex_out.pin(idx)); + nex_out.pin(idx).unlink(); + if (debug_synth2) { + cerr << get_fileline() << ": NetCase::synth_async_casez_: " + << "statement_input.pin(" << idx << "):" << endl; + statement_input.pin(idx).dump_link(cerr, 8); + } + } // Look for a default statement. - NetProc*statement_default = 0; + NetProc*default_statement = 0; for (size_t item = 0 ; item < items_.size() ; item += 1) { if (items_[item].guard != 0) continue; - ivl_assert(*this, statement_default==0); - statement_default = items_[item].statement; + ivl_assert(*this, default_statement==0); + default_statement = items_[item].statement; } - NetBus default_bus (scope, nex_out.pin_count()); - if (statement_default) { - bool flag = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - statement_default); - if (!flag) { - return false; - } - - for (unsigned idx = 0 ; idx < default_bus.pin_count() ; idx += 1) { - connect(default_bus.pin(idx), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); - } + /* If there is a default clause, synthesize it once and we'll + link it in wherever it is needed. If there isn't, create + a dummy default to pass on the accumulated nex_out from + preceding statements. */ + NetBus default_out (scope, nex_out.pin_count()); + + for (unsigned idx = 0 ; idx < default_out.pin_count() ; idx += 1) + connect(default_out.pin(idx), statement_input.pin(idx)); + + if (default_statement) { + bool flag = synth_async_block_substatement_(des, scope, nex_map, default_out, + enables, bitmasks, default_statement); + if (!flag) return false; if (debug_synth2) { cerr << get_fileline() << ": NetCase::synth_async_casez_: " - << "synthesize default clause at " << statement_default->get_fileline() + << "synthesize default clause at " << default_statement->get_fileline() << " is done." << endl; } } @@ -804,7 +1127,7 @@ // the case select with the guard expression. The true input // (data1) is the current statement, and the false input is // the result of a later statement. - vectormux_prev (mux_width.size()); + vectorprev_mux (nex_out.pin_count()); for (size_t idx = 0 ; idx < items_.size() ; idx += 1) { size_t item = items_.size()-idx-1; if (items_[item].guard == 0) @@ -833,53 +1156,59 @@ connect(condit_dev->pin(0), condit->pin(0)); // Synthesize the guarded statement. - NetBus true_bus (scope, nex_out.pin_count()); - for (unsigned pin = 0 ; pin < nex_map.size() ; pin += 1) - connect(true_bus.pin(pin), statement_input.pin(pin)); - - synth_async_block_substatement_(des, scope, nex_map, true_bus, stmt); - - for (unsigned mdx = 0 ; mdx < mux_width.size() ; mdx += 1) { - NetMux*mux_cur = new NetMux(scope, scope->local_symbol(), - mux_width[mdx], 2, 1); - des->add_node(mux_cur); - mux_cur->set_line(*this); - connect(mux_cur->pin_Sel(), condit->pin(0)); + NetBus tmp_out (scope, nex_out.pin_count()); + NetBus tmp_ena (scope, nex_out.pin_count()); + vector tmp_masks (nex_out.pin_count()); + + for (unsigned pdx = 0 ; pdx < nex_out.pin_count() ; pdx += 1) + connect(tmp_out.pin(pdx), statement_input.pin(pdx)); + + synth_async_block_substatement_(des, scope, nex_map, tmp_out, + tmp_ena, tmp_masks, stmt); + + NetBus prev_ena (scope, nex_out.pin_count()); + for (unsigned mdx = 0 ; mdx < nex_out.pin_count() ; mdx += 1) { + NetMux*mux = new NetMux(scope, scope->local_symbol(), + mux_width[mdx], 2, 1); + des->add_node(mux); + mux->set_line(*this); + connect(mux->pin_Sel(), condit->pin(0)); - connect(mux_cur->pin_Data(1), true_bus.pin(mdx)); + connect(mux->pin_Data(1), tmp_out.pin(mdx)); // If there is a previous mux, then use that as the - // false clause input. Otherwise, use the - // default. But wait, if there is no default, then - // use the accumulated input. - if (mux_prev[mdx]) { - connect(mux_cur->pin_Data(0), mux_prev[mdx]->pin_Result()); - } else if (default_bus.pin(mdx).is_linked()) { - connect(mux_cur->pin_Data(0), default_bus.pin(mdx)); - - } else { - connect(mux_cur->pin_Data(0), statement_input.pin(mdx)); - } + // false clause input. Otherwise, use the default. + if (prev_mux[mdx]) + connect(mux->pin_Data(0), prev_mux[mdx]->pin_Result()); + else + connect(mux->pin_Data(0), default_out.pin(mdx)); // Make a NetNet for the result. ivl_variable_type_t mux_data_type = IVL_VT_LOGIC; - netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1, 0); + netvector_t*tmp_vec = new netvector_t(mux_data_type, mux_width[mdx]-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::TRI, tmp_vec); + NetNet::WIRE, tmp_vec); tmp->local_flag(true); tmp->set_line(*this); ivl_assert(*this, tmp->vector_width() != 0); - connect(mux_cur->pin_Result(), tmp->pin(0)); + connect(mux->pin_Result(), tmp->pin(0)); // This mux becomes the "false" input to the next mux. - mux_prev[mdx] = mux_cur; + prev_mux[mdx] = mux; + + connect(prev_ena.pin(mdx), enables.pin(mdx)); + enables.pin(mdx).unlink(); + + multiplex_enables(des, scope, condit, tmp_ena.pin(mdx), + prev_ena.pin(mdx), enables.pin(mdx)); + + merge_parallel_masks(bitmasks[mdx], tmp_masks[mdx]); } } // Connect the last mux to the output. - for (size_t mdx = 0 ; mdx < mux_prev.size() ; mdx += 1) { - connect(mux_prev[mdx]->pin_Result(), nex_out.pin(mdx)); - } + for (size_t mdx = 0 ; mdx < prev_mux.size() ; mdx += 1) + connect(prev_mux[mdx]->pin_Result(), nex_out.pin(mdx)); return true; } @@ -892,45 +1221,30 @@ */ bool NetCondit::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { - if (if_ == 0) { - return false; - } - if (else_ == 0) { - bool latch_flag = false; - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - if (! accumulated_nex_out.pin(idx).is_linked()) - latch_flag = true; - } - if (latch_flag) { - cerr << get_fileline() << ": error: Asynchronous if statement" - << " cannot synthesize missing \"else\"" - << " without generating latches." << endl; - //return false; - } - } + // Handle the unlikely case that both clauses are empty. + if ((if_ == 0) && (else_ == 0)) + return true; + + ivl_assert(*this, nex_map.size() == nex_out.pin_count()); + ivl_assert(*this, nex_map.size() == enables.pin_count()); + ivl_assert(*this, nex_map.size() == bitmasks.size()); - ivl_assert(*this, if_ != 0); // Synthesize the condition. This will act as a select signal // for a binary mux. NetNet*ssig = expr_->synthesize(des, scope, expr_); - assert(ssig); - - if (debug_synth2) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "Synthesize if clause at " << if_->get_fileline() - << endl; - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_nex_out.pin(" << idx << "):" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); - } - } + ivl_assert(*this, ssig); + // The incoming nex_out is taken as the input for this + // statement. Since there are two statements that start + // at this same point, we save all these inputs and reuse + // them for both statements. Unlink the nex_out now, so + // we can hook up the mux outputs. NetBus statement_input (scope, nex_out.pin_count()); for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(statement_input.pin(idx), accumulated_nex_out.pin(idx)); + connect(statement_input.pin(idx), nex_out.pin(idx)); + nex_out.pin(idx).unlink(); if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "statement_input.pin(" << idx << "):" << endl; @@ -938,105 +1252,85 @@ } } - bool flag; - NetBus asig(scope, nex_out.pin_count()); - flag = if_->synth_async(des, scope, nex_map, asig, accumulated_nex_out); - if (!flag) { - return false; - } - - NetBus btmp(scope, nex_out.pin_count()); - NetBus bsig(scope, nex_out.pin_count()); + NetBus a_out (scope, nex_out.pin_count()); + NetBus a_ena (scope, nex_out.pin_count()); + vector a_masks (nex_out.pin_count()); + if (if_) { + if (debug_synth2) { + cerr << get_fileline() << ": NetCondit::synth_async: " + << "Synthesize if clause at " << if_->get_fileline() + << endl; + } - if (else_==0) { - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(bsig.pin(idx), accumulated_nex_out.pin(idx)); - accumulated_nex_out.pin(idx).unlink(); + for (unsigned idx = 0 ; idx < a_out.pin_count() ; idx += 1) { + connect(a_out.pin(idx), statement_input.pin(idx)); } + bool flag = synth_async_block_substatement_(des, scope, nex_map, a_out, + a_ena, a_masks, if_); + if (!flag) return false; + } else { + for (unsigned idx = 0 ; idx < a_out.pin_count() ; idx += 1) { + connect(a_out.pin(idx), statement_input.pin(idx)); + connect(a_ena.pin(idx), scope->tie_lo()); + } + } + NetBus b_out(scope, nex_out.pin_count()); + NetBus b_ena(scope, nex_out.pin_count()); + vector b_masks (nex_out.pin_count()); + if (else_) { if (debug_synth2) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Synthesize else clause at " << else_->get_fileline() << endl; - for (unsigned idx = 0 ; idx < accumulated_nex_out.pin_count(); idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_nex_out.pin(" << idx << "):" << endl; - accumulated_nex_out.pin(idx).dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < statement_input.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "statement_input.pin(" << idx << "):" << endl; - statement_input.pin(idx).dump_link(cerr, 8); - } } - NetBus accumulated_btmp_out (scope, statement_input.pin_count()); - for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { - if (statement_input.pin(idx).is_linked()) - connect(accumulated_btmp_out.pin(idx), statement_input.pin(idx)); - else - connect(accumulated_btmp_out.pin(idx), accumulated_nex_out.pin(idx)); - } - if (debug_synth2) { - for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_btmp_out.pin(" << idx << "):" << endl; - accumulated_btmp_out.pin(idx).dump_link(cerr, 8); - } + for (unsigned idx = 0 ; idx < b_out.pin_count() ; idx += 1) { + connect(b_out.pin(idx), statement_input.pin(idx)); } - flag = synth_async_block_substatement_(des, scope, nex_map, accumulated_btmp_out, else_); - if (!flag) { - return false; - } - if (debug_synth2) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "synthesize else clause at " << else_->get_fileline() - << " is done." << endl; - for (unsigned idx = 0 ; idx < accumulated_btmp_out.pin_count() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "accumulated_btmp_out.pin(" << idx << "):" << endl; - accumulated_btmp_out.pin(idx).dump_link(cerr, 8); - } - } - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - connect(bsig.pin(idx), accumulated_btmp_out.pin(idx)); - accumulated_btmp_out.pin(idx).unlink(); - } + bool flag = synth_async_block_substatement_(des, scope, nex_map, b_out, + b_ena, b_masks, else_); + if (!flag) return false; + } else { + for (unsigned idx = 0 ; idx < b_out.pin_count() ; idx += 1) { + connect(b_out.pin(idx), statement_input.pin(idx)); + connect(b_ena.pin(idx), scope->tie_lo()); + } } - /* The nex_out output, asig input, and bsig input all have the + /* The nex_out output, a_out input, and b_out input all have the same pin count (usually, but not always 1) because they are net arrays of the same dimension. The for loop below creates a NetMux for each pin of the output. (Note that pins may be, in fact usually are, vectors.) */ - ivl_assert(*this, nex_out.pin_count()==asig.pin_count()); - ivl_assert(*this, nex_out.pin_count()==bsig.pin_count()); - - bool rc_flag = true; for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { - // It should not be possible for the a (true) or b - // (false) signals to be missing. If either is, print a - // warning and clear a flag so that the rest of this - // code can find a way to cope. - bool asig_is_present = true; - if (! asig.pin(idx).nexus()->pick_any_net()) { - cerr << get_fileline() << ": warning: " - << "True clause of conditional statement might not" - << " drive all expected outputs." << endl; - asig_is_present = false; + + bool a_driven = a_out.pin(idx).nexus()->pick_any_net(); + bool b_driven = b_out.pin(idx).nexus()->pick_any_net(); + if (!a_driven && !b_driven) { + connect(nex_out.pin(idx), statement_input.pin(idx)); + continue; } - bool bsig_is_present = true; - if (! bsig.pin(idx).nexus()->pick_any_net()) { - cerr << get_fileline() << ": warning: " - << "False clause of conditional statement might not" - << " drive all expected outputs." << endl; - bsig_is_present = false; + merge_parallel_masks(bitmasks[idx], a_masks[idx]); + merge_parallel_masks(bitmasks[idx], b_masks[idx]); + + // If one clause is empty and the other clause unconditionally + // drives all bits of the vector, we can rely on the enable + // to prevent the flip-flop or latch updating when the empty + // clause is selected, and hence don't need a mux. + if (!a_driven && all_bits_driven(b_masks[idx])) { + connect(nex_out.pin(idx), b_out.pin(idx)); + continue; + } + if (!b_driven && all_bits_driven(a_masks[idx])) { + connect(nex_out.pin(idx), a_out.pin(idx)); + continue; } // Guess the mux type from the type of the output. @@ -1046,38 +1340,22 @@ } unsigned mux_off = 0; - unsigned mux_width; - if (asig_is_present) - mux_width = asig.pin(idx).nexus()->vector_width(); - else if (bsig_is_present) - mux_width = bsig.pin(idx).nexus()->vector_width(); - else - mux_width = 0; + unsigned mux_width = nex_map[idx].wid; if (debug_synth2) { - if (asig_is_present) - cerr << get_fileline() << ": NetCondit::synth_async: " - << "asig_is_present," - << " asig width=" << asig.pin(idx).nexus()->vector_width() - << endl; - if (bsig_is_present) - cerr << get_fileline() << ": NetCondit::synth_async: " - << "bsig_is_present," - << " bsig width=" << bsig.pin(idx).nexus()->vector_width() - << endl; cerr << get_fileline() << ": NetCondit::synth_async: " << "Calculated mux_width=" << mux_width << endl; } - NetPartSelect*apv = detect_partselect_lval(asig.pin(idx)); + NetPartSelect*apv = detect_partselect_lval(a_out.pin(idx)); if (debug_synth2 && apv) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Assign-to-part apv base=" << apv->base() << ", width=" << apv->width() << endl; } - NetPartSelect*bpv = detect_partselect_lval(bsig.pin(idx)); + NetPartSelect*bpv = detect_partselect_lval(b_out.pin(idx)); if (debug_synth2 && bpv) { cerr << get_fileline() << ": NetCondit::synth_async: " << "Assign-to-part bpv base=" << bpv->base() @@ -1094,10 +1372,10 @@ // manipulates the width of the part. mux_width = apv->width(); mux_off = apv->base(); - asig.pin(idx).unlink(); - bsig.pin(idx).unlink(); - connect(asig.pin(idx), apv->pin(0)); - connect(bsig.pin(idx), bpv->pin(0)); + a_out.pin(idx).unlink(); + b_out.pin(idx).unlink(); + connect(a_out.pin(idx), apv->pin(0)); + connect(b_out.pin(idx), bpv->pin(0)); delete apv; delete bpv; } else { @@ -1106,33 +1384,10 @@ bpv = 0; } - if (bsig_is_present && mux_width != bsig.pin(idx).nexus()->vector_width()) { - cerr << get_fileline() << ": internal error: " - << "NetCondit::synth_async: " - << "Mux input sizes do not match." - << " A size=" << mux_lwidth - << ", B size=" << bsig.pin(idx).nexus()->vector_width() - << endl; - cerr << get_fileline() << ": : " - << "asig node pins:" << endl; - asig.dump_node_pins(cerr, 8); - cerr << get_fileline() << ": : " - << "if_ statement:" << endl; - if_->dump(cerr, 8); - cerr << get_fileline() << ": : " - << "bsig node pins:" << endl; - bsig.dump_node_pins(cerr, 4); - if (else_) { - cerr << get_fileline() << ": : " - << "else_ statement:" << endl; - else_->dump(cerr, 8); - } - rc_flag = false; - } - NetMux*mux = new NetMux(scope, scope->local_symbol(), mux_width, 2, 1); mux->set_line(*this); + des->add_node(mux); netvector_t*tmp_type = 0; if (mux_width==1) @@ -1148,92 +1403,55 @@ connect(mux->pin_Result(),otmp->pin(0)); connect(mux->pin_Sel(), ssig->pin(0)); - connect(mux->pin_Data(1), asig.pin(idx)); - connect(mux->pin_Data(0), bsig.pin(idx)); + connect(mux->pin_Data(1), a_out.pin(idx)); + connect(mux->pin_Data(0), b_out.pin(idx)); - if (! asig_is_present) { - tmp_type = new netvector_t(mux_data_type, mux_width-1,0); - NetNet*tmp = new NetNet(scope, scope->local_symbol(), - NetNet::WIRE, NetNet::not_an_array, tmp_type); - tmp->local_flag(true); - tmp->set_line(*this); - connect(mux->pin_Data(1), tmp->pin(0)); - } + // If we are only muxing a part of the output vector, make a + // NetSubstitute to blend the mux output with the accumulated + // output from previous statements. + if (mux_width < mux_lwidth) { + tmp_type = new netvector_t(mux_data_type, mux_lwidth-1,0); - if (! bsig_is_present) { - tmp_type = new netvector_t(mux_data_type, mux_width-1,0); - NetNet*tmp = new NetNet(scope, scope->local_symbol(), + NetNet*itmp = statement_input.pin(idx).nexus()->pick_any_net(); + if (itmp == 0) { + itmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); - tmp->local_flag(true); - tmp->set_line(*this); - connect(mux->pin_Data(0), tmp->pin(0)); - } + itmp->local_flag(true); + itmp->set_line(*this); + connect(itmp->pin(0), statement_input.pin(idx)); + } - // We are only muxing a part of the output vector, so - // make a NetPartSelect::PV to widen the vector to the - // output at hand. - if (mux_width < mux_lwidth) { - tmp_type = new netvector_t(mux_data_type, mux_lwidth-1,0); NetNet*tmp = new NetNet(scope, scope->local_symbol(), NetNet::WIRE, NetNet::not_an_array, tmp_type); tmp->local_flag(true); tmp->set_line(*this); - NetPartSelect*ps = new NetPartSelect(tmp, mux_off, mux_width, NetPartSelect::PV); + NetSubstitute*ps = new NetSubstitute(itmp, otmp, mux_lwidth, mux_off); des->add_node(ps); - connect(ps->pin(0), otmp->pin(0)); + connect(ps->pin(0), tmp->pin(0)); otmp = tmp; } connect(nex_out.pin(idx), otmp->pin(0)); + } - // Handle the special case that this NetMux is only - // assigning to a part of the vector. If that is the - // case, then we need to blend this output with the - // already calculated input to this statement so that we - // don't accidentally disconnect the other drivers to - // other bits. - // FIXME: NEED TO CHECK THAT THESE DRIVERS DON'T - // OVERLAP. THIS CODE CURRENTLY DOESN'T DO THAT TEST. - if (mux_width < mux_lwidth && if_ && else_) { - if (debug_synth2) { - cerr << get_fileline() << ": NetCondit::synth_async: " - << "This NetMux only impacts a few bits of output," - << " so combine nex_out with statement input." - << endl; - cerr << get_fileline() << ": NetCondit::synth_async: " - << "MISSING TEST FOR CORRECTNESS OF THE BLEND!" - << endl; - } - vectormask = statement_input.pin(idx).nexus()->driven_mask(); - // If the mask is empty then there are no bits in the - // nexus to check yet. - if (! mask.empty()) { - for (size_t bit = mux_off; - bit < mux_off+mux_width; - bit += 1) { - ivl_assert(*this, mask[bit]==false); - } - } - connect(nex_out.pin(idx), statement_input.pin(idx)); - } - - des->add_node(mux); + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + multiplex_enables(des, scope, ssig, a_ena.pin(idx), b_ena.pin(idx), enables.pin(idx)); } - return rc_flag; + return true; } bool NetEvWait::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { - bool flag = statement_->synth_async(des, scope, nex_map, nex_out, accumulated_nex_out); + bool flag = statement_->synth_async(des, scope, nex_map, nex_out, enables, bitmasks); return flag; } bool NetForLoop::synth_async(Design*des, NetScope*scope, NexusSet&nex_map, NetBus&nex_out, - NetBus&accumulated_nex_out) + NetBus&enables, vector&bitmasks) { if (debug_synth2) { cerr << get_fileline() << ": NetForLoop::synth_async: " @@ -1289,9 +1507,16 @@ ivl_assert(*this, scope->loop_index_tmp.empty()); scope->loop_index_tmp = index_args; - rc = synth_async_block_substatement_(des, scope, nex_map, - accumulated_nex_out, - statement_); + NetBus tmp_ena (scope, nex_out.pin_count()); + vector tmp_masks (nex_out.pin_count()); + + rc = synth_async_block_substatement_(des, scope, nex_map, nex_out, + tmp_ena, tmp_masks, statement_); + + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) { + merge_sequential_enables(des, scope, enables.pin(idx), tmp_ena.pin(idx)); + merge_sequential_masks(bitmasks[idx], tmp_masks[idx]); + } scope->loop_index_tmp.clear(); @@ -1326,10 +1551,6 @@ delete index_var.value; - // The output from the block is now the accumulated outputs. - for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) - connect(nex_out.pin(idx), accumulated_nex_out.pin(idx)); - return true; } @@ -1351,32 +1572,93 @@ << "Process has " << nex_set.size() << " outputs." << endl; } - NetBus nex_q (scope(), nex_set.size()); + NetBus nex_out (scope(), nex_set.size()); + NetBus enables (scope(), nex_set.size()); + vector bitmasks (nex_set.size()); + + // Save links to the initial nex_out. These will be used later + // to detect floating part-substitute and mux inputs that need + // to be tied off. + NetBus nex_in (scope(), nex_out.pin_count()); + for (unsigned idx = 0 ; idx < nex_out.pin_count() ; idx += 1) + connect(nex_in.pin(idx), nex_out.pin(idx)); + + bool flag = statement_->synth_async(des, scope(), nex_set, nex_out, enables, bitmasks); + if (!flag) return false; + + flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, false); + if (!flag) return false; + for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { - NexusSet::elem_t&item = nex_set[idx]; - if (item.base != 0 || item.wid!=item.lnk.nexus()->vector_width()) { - ivl_variable_type_t tmp_data_type = IVL_VT_LOGIC; - netvector_t*tmp_type = new netvector_t(tmp_data_type, item.lnk.nexus()->vector_width()-1,0); - NetNet*tmp_sig = new NetNet(scope(), scope()->local_symbol(), - NetNet::WIRE, NetNet::not_an_array, tmp_type); - tmp_sig->local_flag(true); - tmp_sig->set_line(*this); - - NetPartSelect*tmp = new NetPartSelect(tmp_sig, item.base, - item.wid, NetPartSelect::PV); - des->add_node(tmp); - tmp->set_line(*this); - connect(tmp->pin(0), nex_q.pin(idx)); - connect(item.lnk, tmp_sig->pin(0)); + if (enables.pin(idx).is_linked(scope()->tie_hi())) { + connect(nex_set[idx].lnk, nex_out.pin(idx)); } else { - connect(item.lnk, nex_q.pin(idx)); + cerr << get_fileline() << ": warning: " + << "A latch has been inferred for '" + << nex_set[idx].lnk.nexus()->pick_any_net()->name() + << "'." << endl; + + if (enables.pin(idx).nexus()->pick_any_net()->local_flag()) { + cerr << get_fileline() << ": warning: The latch " + "enable is connected to a synthesized " + "expression. The latch may be sensitive " + "to glitches." << endl; + } + + if (debug_synth2) { + cerr << get_fileline() << ": debug: " + << "Top level making a " + << nex_set[idx].wid << "-wide " + << "NetLatch device." << endl; + } + + NetLatch*latch = new NetLatch(scope(), scope()->local_symbol(), + nex_set[idx].wid); + des->add_node(latch); + latch->set_line(*this); + + NetNet*tmp = nex_out.pin(idx).nexus()->pick_any_net(); + tmp->set_line(*this); + assert(tmp); + + tmp = crop_to_width(des, tmp, latch->width()); + + connect(nex_set[idx].lnk, latch->pin_Q()); + connect(tmp->pin(0), latch->pin_Data()); + + assert (enables.pin(idx).is_linked()); + connect(enables.pin(idx), latch->pin_Enable()); } } - NetBus tmp_q (scope(), nex_set.size()); - bool flag = statement_->synth_async(des, scope(), nex_set, nex_q, tmp_q); - return flag; + synthesized_design_ = des; + return true; +} + + +bool NetProc::synth_sync(Design*des, NetScope*scope, + bool& /* ff_negedge */, + NetNet* /* ff_clk */, NetBus&ff_ce, + NetBus& /* ff_aclr*/, NetBus& /* ff_aset*/, + vector& /*ff_aset_value*/, + NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, + const vector&events) +{ + if (events.size() > 0) { + cerr << get_fileline() << ": error: Events are unaccounted" + << " for in process synthesis." << endl; + des->errors += 1; + } + + if (debug_synth2) { + cerr << get_fileline() << ": NetProc::synth_sync: " + << "This statement is an async input to a sync process." << endl; + } + + /* Synthesize the input to the DFF. */ + return synth_async(des, scope, nex_map, nex_out, ff_ce, bitmasks); } /* @@ -1385,13 +1667,13 @@ * invoked for input like this: * * always @(posedge clk...) begin - * - * - * ... + * + * + * ... * end * * This needs to be split into a DFF bank for each statement, because - * the statements may each infer different reset and enable signals. + * the statements may each infer different reset and enables signals. */ bool NetBlock::synth_sync(Design*des, NetScope*scope, bool&ff_negedge, @@ -1399,6 +1681,7 @@ NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const vector&events_in) { if (debug_synth2) { @@ -1406,44 +1689,33 @@ << "Examine this block for synchronous logic." << endl; } + if (last_ == 0) { + return true; + } + bool flag = true; NetProc*cur = last_; do { cur = cur->next_; - /* Create a temporary nex_map for the substatement. */ - NexusSet tmp_set; - cur->nex_output(tmp_set); - - /* Create also a temporary net_out to collect the - output. The tmp1 and tmp2 map and out sets together - are used to collect the outputs from the substatement - for the inputs of the FF bank. */ - NetBus tmp_out (scope, tmp_set.size()); + // Create a temporary nex_map for the substatement. + NexusSet tmp_map; + cur->nex_output(tmp_map); + + // Create temporary variables to collect the output from the synthesis. + NetBus tmp_out (scope, tmp_map.size()); + NetBus tmp_ce (scope, tmp_map.size()); + vector tmp_masks (tmp_map.size()); + + // Map (and move) the accumulated nex_out for this block + // to the version that we can pass to the next statement. + // We will move the result back later. for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { - unsigned ptr = nex_map.find_nexus(tmp_set[idx]); + unsigned ptr = nex_map.find_nexus(tmp_map[idx]); ivl_assert(*this, ptr < nex_out.pin_count()); - if (nex_out.pin(ptr).is_linked()) { - cerr << get_fileline() << ": sorry: multiple statements " - "assigning to the same flip-flop are not yet " - "supported in synthesis." << endl; - return false; - } - } - - /* Create a temporary ff_ce (FF clock-enable) that - accounts for the subset of outputs that this - substatement drives. This allows for the possibility - that the substatement has CE patterns of its own. */ - NetBus tmp_ce (scope, tmp_set.size()); - for (unsigned idx = 0 ; idx < tmp_ce.pin_count() ; idx += 1) { - unsigned ptr = nex_map.find_nexus(tmp_set[idx]); - ivl_assert(*this, ptr < nex_out.pin_count()); - if (ff_ce.pin(ptr).is_linked()) { - connect(tmp_ce.pin(idx), ff_ce.pin(ptr)); - ff_ce.pin(ptr).unlink(); - } + connect(tmp_out.pin(idx), nex_out.pin(ptr)); + nex_out.pin(ptr).unlink(); } /* Now go on with the synchronous synthesis for this @@ -1453,22 +1725,23 @@ bool ok_flag = cur->synth_sync(des, scope, ff_negedge, ff_clk, tmp_ce, ff_aclr, ff_aset, ff_aset_value, - tmp_set, tmp_out, events_in); + tmp_map, tmp_out, tmp_masks, + events_in); flag = flag && ok_flag; if (ok_flag == false) continue; - /* Use the nex_map to link up the output from the - substatement to the output of the block as a - whole. It is occasionally possible to have outputs - beyond the input set, for example when the l-value of - an assignment is smaller than the r-value. */ + // Now map the output from the substatement back to the + // outputs for this block. for (unsigned idx = 0 ; idx < tmp_out.pin_count() ; idx += 1) { - unsigned ptr = nex_map.find_nexus(tmp_set[idx]); + unsigned ptr = nex_map.find_nexus(tmp_map[idx]); ivl_assert(*this, ptr < nex_out.pin_count()); connect(nex_out.pin(ptr), tmp_out.pin(idx)); - connect(ff_ce.pin(ptr), tmp_ce.pin(idx)); + + merge_sequential_enables(des, scope, ff_ce.pin(ptr), tmp_ce.pin(idx)); + + merge_sequential_masks(bitmasks[ptr], tmp_masks[idx]); } } while (cur != last_); @@ -1493,6 +1766,7 @@ NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const vector&events_in) { /* First try to turn the condition expression into an @@ -1515,22 +1789,55 @@ NetNet*rst = expr_->synthesize(des, scope, expr_); ivl_assert(*this, rst->pin_count() == 1); - /* XXXX I really should find a way to check that the - edge used on the reset input is correct. This would - involve interpreting the expression that is fed by the - reset expression. */ - ivl_assert(*this, ev->edge() == NetEvProbe::POSEDGE); + // Check that the edge used on the set/reset input is correct. + switch (ev->edge()) { + case NetEvProbe::POSEDGE: + if (ev->pin(0).nexus() != rst->pin(0).nexus()) { + cerr << get_fileline() << ": error: " + << "Condition for posedge asynchronous set/reset " + << "must exactly match the event expression." << endl; + des->errors += 1; + return false; + } + break; + case NetEvProbe::NEGEDGE: { + bool is_inverter = false; + NetNode*node = rst->pin(0).nexus()->pick_any_node(); + if (NetLogic*gate = dynamic_cast(node)) { + if (gate->type() == NetLogic::NOT) + is_inverter = true; + } + if (NetUReduce*gate = dynamic_cast(node)) { + if (gate->type() == NetUReduce::NOR) + is_inverter = true; + } + if (!is_inverter || ev->pin(0).nexus() != node->pin(1).nexus()) { + cerr << get_fileline() << ": error: " + << "Condition for negedge asynchronous set/reset must be " + << "a simple inversion of the event expression." << endl; + des->errors += 1; + return false; + } + break; + } + default: + cerr << get_fileline() << ": error: " + << "Asynchronous set/reset event must be " + << "edge triggered." << endl; + des->errors += 1; + return false; + } // Synthesize the true clause to figure out what kind of // set/reset we have. This should synthesize down to a // constant. If not, we have an asynchronous LOAD, a // very different beast. ivl_assert(*this, if_); - bool flag; NetBus tmp_out(scope, nex_out.pin_count()); - NetBus accumulated_tmp_out(scope, nex_out.pin_count()); - flag = if_->synth_async(des, scope, nex_map, tmp_out, accumulated_tmp_out); - if (! flag) return false; + NetBus tmp_ena(scope, nex_out.pin_count()); + vector tmp_masks (nex_out.pin_count()); + bool flag = if_->synth_async(des, scope, nex_map, tmp_out, tmp_ena, tmp_masks); + if (!flag) return false; ivl_assert(*this, tmp_out.pin_count() == ff_aclr.pin_count()); ivl_assert(*this, tmp_out.pin_count() == ff_aset.pin_count()); @@ -1538,10 +1845,30 @@ for (unsigned pin = 0 ; pin < tmp_out.pin_count() ; pin += 1) { Nexus*rst_nex = tmp_out.pin(pin).nexus(); - if (! rst_nex->drivers_constant()) { - cerr << get_fileline() << ": sorry: " - << "Asynchronous LOAD not implemented." << endl; - return false; + if (!all_bits_driven(tmp_masks[pin])) { + cerr << get_fileline() << ": sorry: Not all bits of '" + << nex_map[idx].lnk.nexus()->pick_any_net()->name() + << "' are asynchronously set or reset. This is " + << "not currently supported in synthesis." << endl; + des->errors += 1; + return false; + } + + if (! rst_nex->drivers_constant() || + ! tmp_ena.pin(pin).is_linked(scope->tie_hi()) ) { + cerr << get_fileline() << ": sorry: Asynchronous load " + << "is not currently supported in synthesis." << endl; + des->errors += 1; + return false; + } + + if (ff_aclr.pin(pin).is_linked() || + ff_aset.pin(pin).is_linked()) { + cerr << get_fileline() << ": sorry: More than " + "one asynchronous set/reset clause is " + "not currently supported in synthesis." << endl; + des->errors += 1; + return false; } verinum rst_drv = rst_nex->driven_vector(); @@ -1567,13 +1894,22 @@ } } + if (else_ == 0) + return true; + + vector events; + for (unsigned jdx = 0 ; jdx < events_in.size() ; jdx += 1) { + if (jdx != idx) + events.push_back(events_in[jdx]); + } return else_->synth_sync(des, scope, ff_negedge, ff_clk, ff_ce, ff_aclr, ff_aset, ff_aset_value, - nex_map, nex_out, vector(0)); + nex_map, nex_out, bitmasks, events); } delete expr_input; + #if 0 /* Detect the case that this is a *synchronous* set/reset. It is not asynchronous because we know the condition is not @@ -1610,7 +1946,7 @@ use the Sclr input. Otherwise, use the Aset input and save the set value. */ verinum tmp (verinum::V0, ff->width()); - for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) { + for (unsigned bit = 0 ; bit < ff->width() ; bit += 1) { assert(asig->pin(bit).nexus()->drivers_constant()); tmp.set(bit, asig->pin(bit).nexus()->driven_value()); @@ -1639,6 +1975,10 @@ delete a_set; #endif +#if 0 + /* This gives a false positive for strange coding styles, + such as ivltests/conditsynth3.v. */ + /* Failed to find an asynchronous set/reset, so any events input are probably in error. */ if (events_in.size() > 0) { @@ -1646,90 +1986,9 @@ << " for in process synthesis." << endl; des->errors += 1; } +#endif - - /* If this is an if/then/else, then it is likely a - combinational if, and I should synthesize it that way. */ - if (if_ && else_) { - NetBus tmp (scope, nex_out.pin_count()); - bool flag = synth_async(des, scope, nex_map, nex_out, tmp); - return flag; - } - - ivl_assert(*this, if_); - ivl_assert(*this, !else_); - - /* Synthesize the enable expression. */ - NetNet*ce = expr_->synthesize(des, scope, expr_); - ivl_assert(*this, ce && ce->pin_count()==1 && ce->vector_width()==1); - - if (debug_synth2) { - NexusSet if_set; - if_->nex_output(if_set); - - cerr << get_fileline() << ": NetCondit::synth_sync: " - << "Found ce pattern." - << " ff_ce.pin_count()=" << ff_ce.pin_count() - << endl; - for (unsigned idx = 0 ; idx < nex_map.size() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_sync: " - << "nex_map[" << idx << "]: " - << "base=" << nex_map[idx].base - << ", wid=" << nex_map[idx].wid - << endl; - nex_map[idx].lnk.dump_link(cerr, 8); - } - for (unsigned idx = 0 ; idx < if_set.size() ; idx += 1) { - cerr << get_fileline() << ": NetCondit::synth_sync: " - << "if_set[" << idx << "]: " - << "base=" << if_set[idx].base - << ", wid=" << if_set[idx].wid - << endl; - if_set[idx].lnk.dump_link(cerr, 8); - } - } - - /* What's left, is a synchronous CE statement like this: - - if (expr_) ; - - The expr_ expression has already been synthesized to the ce - net, so we connect it here to the FF. What's left is to - synthesize the substatement as a combinational - statement. - - Watch out for the special case that there is already a CE - connected to this FF. This can be caused by code like this: - - if (a) if (b) ; - - In this case, we are working on the inner IF, so we AND the - a and b expressions to make a new CE. */ - - for (unsigned idx = 0 ; idx < ff_ce.pin_count() ; idx += 1) { - if (ff_ce.pin(idx).is_linked()) { - NetLogic*ce_and = new NetLogic(scope, - scope->local_symbol(), 3, - NetLogic::AND, 1); - des->add_node(ce_and); - connect(ff_ce.pin(idx), ce_and->pin(1)); - connect(ce->pin(0), ce_and->pin(2)); - - ff_ce.pin(idx).unlink(); - connect(ff_ce.pin(idx), ce_and->pin(0)); - - } else { - - connect(ff_ce.pin(idx), ce->pin(0)); - } - } - - bool flag = if_->synth_sync(des, scope, - ff_negedge, ff_clk, ff_ce, - ff_aclr, ff_aset, ff_aset_value, - nex_map, nex_out, events_in); - - return flag; + return synth_async(des, scope, nex_map, nex_out, ff_ce, bitmasks); } bool NetEvWait::synth_sync(Design*des, NetScope*scope, @@ -1738,6 +1997,7 @@ NetBus&ff_aclr,NetBus&ff_aset, vector&ff_aset_value, NexusSet&nex_map, NetBus&nex_out, + vector&bitmasks, const vector&events_in) { if (debug_synth2) { @@ -1780,7 +2040,7 @@ if (pclk != 0) { cerr << get_fileline() << ": error: Too many " << "clocks for synchronous logic." << endl; - cerr << get_fileline() << ": : Perhaps an" + cerr << get_fileline() << ": : Perhaps an" << " asynchronous set/reset is misused?" << endl; des->errors += 1; } @@ -1796,6 +2056,7 @@ << " are valid clock inputs." << endl; cerr << get_fileline() << ": : Perhaps the clock" << " is read by a statement or expression?" << endl; + des->errors += 1; return false; } @@ -1816,12 +2077,10 @@ } /* Synthesize the input to the DFF. */ - bool flag = statement_->synth_sync(des, scope, - ff_negedge, ff_clk, ff_ce, - ff_aclr, ff_aset, ff_aset_value, - nex_map, nex_out, events); - - return flag; + return statement_->synth_sync(des, scope, + ff_negedge, ff_clk, ff_ce, + ff_aclr, ff_aset, ff_aset_value, + nex_map, nex_out, bitmasks, events); } /* @@ -1851,40 +2110,42 @@ clock->local_flag(true); clock->set_line(*this); -#if 0 - NetNet*ce = new NetNet(scope(), scope()->local_symbol(), - NetNet::TRI, &netvector_t::scalar_logic); - ce->local_flag(true); -#else - NetBus ce (scope(), nex_set.size()); -#endif + NetBus ce (scope(), nex_set.size()); NetBus nex_d (scope(), nex_set.size()); NetBus nex_q (scope(), nex_set.size()); - NetBus aclr (scope(), nex_set.size()); - NetBus aset (scope(), nex_set.size()); + NetBus aclr (scope(), nex_set.size()); + NetBus aset (scope(), nex_set.size()); + vector bitmasks (nex_set.size()); + + // Save links to the initial nex_d. These will be used later + // to detect floating part-substitute and mux inputs that need + // to be tied off. + NetBus nex_in (scope(), nex_d.pin_count()); + for (unsigned idx = 0 ; idx < nex_in.pin_count() ; idx += 1) + connect(nex_in.pin(idx), nex_d.pin(idx)); + + // The Q of the NetFF devices is connected to the output that + // we are. The nex_q is a bundle of the outputs. + for (unsigned idx = 0 ; idx < nex_q.pin_count() ; idx += 1) + connect(nex_q.pin(idx), nex_set[idx].lnk); - /* The Q of the NetFF devices is connected to the output that - we are. The nex_q is a bundle of the outputs. We will also - pass the nex_q as a map to the statement's synth_sync - method to map it to the correct nex_d pin. */ - for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { - connect(nex_set[idx].lnk, nex_q.pin(idx)); - } - - // Connect the input later. + // Connect the D of the NetFF devices later. /* Synthesize the input to the DFF. */ bool negedge = false; bool flag = statement_->synth_sync(des, scope(), negedge, clock, ce, aclr, aset, aset_value, - nex_set, nex_d, + nex_set, nex_d, bitmasks, vector()); if (! flag) { delete clock; return false; } + flag = tie_off_floating_inputs_(des, nex_set, nex_in, bitmasks, true); + if (!flag) return false; + for (unsigned idx = 0 ; idx < nex_set.size() ; idx += 1) { //ivl_assert(*this, nex_set[idx].nex); @@ -1912,7 +2173,7 @@ connect(clock->pin(0), ff2->pin_Clock()); if (ce.pin(idx).is_linked()) - connect(ce.pin(idx), ff2->pin_Enable()); + connect(ce.pin(idx), ff2->pin_Enable()); if (aclr.pin(idx).is_linked()) connect(aclr.pin(idx), ff2->pin_Aclr()); if (aset.pin(idx).is_linked()) @@ -1925,17 +2186,16 @@ #endif } - // The "clock" and "ce" nets were just to carry the connection - // back to the flip-flop. Delete them now. The connections - // will persist. + // The "clock" net was just to carry the connection back + // to the flip-flop. Delete it now. The connection will + // persist. delete clock; -#if 0 - delete ce; -#endif + + synthesized_design_ = des; return true; } -class synth2_f : public functor_t { +class synth2_f : public functor_t { public: void process(Design*, NetProcTop*); @@ -1958,11 +2218,17 @@ if (top->scope()->attribute(perm_string::literal("ivl_synthesis_cell")).len() > 0) return; + /* Create shared pullup and pulldown nodes (if they don't already + exist) for use when creating clock/gate enables. */ + top->scope()->add_tie_hi(des); + top->scope()->add_tie_lo(des); + if (top->is_synchronous()) { bool flag = top->synth_sync(des); if (! flag) { cerr << top->get_fileline() << ": error: " - << "Unable to synthesize synchronous process." << endl; + << "Unable to synthesize synchronous process." + << endl; des->errors += 1; return; } @@ -1997,8 +2263,8 @@ if (! top->synth_async(des)) { cerr << top->get_fileline() << ": error: " - << "failed to synthesize asynchronous " - << "logic for this process." << endl; + << "Unable to synthesize asynchronous process." + << endl; des->errors += 1; return; } diff -Nru iverilog-10.3/synth.cc iverilog-11.0/synth.cc --- iverilog-10.3/synth.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/synth.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017 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 @@ -134,6 +134,9 @@ top_ = top; switch (top->type()) { case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: proc_always_(des); break; case IVL_PR_INITIAL: diff -Nru iverilog-10.3/sys_funcs.cc iverilog-11.0/sys_funcs.cc --- iverilog-10.3/sys_funcs.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/sys_funcs.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2019 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 @@ -29,26 +29,31 @@ * via the lookup_sys_func function. */ -static const struct sfunc_return_type sfunc_table[] = { - { "$realtime", IVL_VT_REAL, 1, 0 }, - { "$bitstoreal", IVL_VT_REAL, 1, 0 }, - { "$itor", IVL_VT_REAL, 1, 0 }, - { "$realtobits", IVL_VT_LOGIC, 64, 0 }, - { "$time", IVL_VT_LOGIC, 64, 0 }, - { "$stime", IVL_VT_LOGIC, 32, 0 }, - { "$simtime", IVL_VT_LOGIC, 64, 0 }, - { 0, IVL_VT_LOGIC, 32, 0 } -}; +static const struct sfunc_return_type default_return_type = + { 0, IVL_VT_LOGIC, 32, false, false }; struct sfunc_return_type_cell : sfunc_return_type { struct sfunc_return_type_cell*next; }; -static struct sfunc_return_type_cell*sfunc_stack = 0; +static struct sfunc_return_type_cell*sfunc_list_head = 0; +static struct sfunc_return_type_cell*sfunc_list_tail = 0; + +void append_to_list(struct sfunc_return_type_cell*cell) +{ + if (sfunc_list_tail) { + sfunc_list_tail->next = cell; + sfunc_list_tail = cell; + } else { + sfunc_list_head = cell; + sfunc_list_tail = cell; + } + cell->next = 0; +} void cleanup_sys_func_table() { - struct sfunc_return_type_cell *next, *cur = sfunc_stack; + struct sfunc_return_type_cell *next, *cur = sfunc_list_head; while (cur) { next = cur->next; delete cur; @@ -56,10 +61,9 @@ } } -const struct sfunc_return_type* lookup_sys_func(const char*name) +static struct sfunc_return_type* find_in_sys_func_list(const char*name) { - /* First, try to find the name in the function stack. */ - struct sfunc_return_type_cell*cur = sfunc_stack; + struct sfunc_return_type_cell*cur = sfunc_list_head; while (cur) { if (strcmp(cur->name, name) == 0) return cur; @@ -67,19 +71,36 @@ cur = cur->next; } - /* Next, look in the core table. */ - unsigned idx = 0; - while (sfunc_table[idx].name) { + return 0; +} - if (strcmp(sfunc_table[idx].name, name) == 0) - return sfunc_table + idx; +const struct sfunc_return_type* lookup_sys_func(const char*name) +{ + /* First, try to find the name in the function list. */ + struct sfunc_return_type*def = find_in_sys_func_list(name); + if (def) + return def; - idx += 1; - } + /* No luck finding, so return the default description. */ + return &default_return_type; +} - /* No luck finding, so return the trailer, which give a - default description. */ - 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); } /* @@ -87,6 +108,10 @@ * format: * * [] + * + * The driver passes us user-provided tables first, so we add new entries + * to the end of the list. This allows user-defined functions to override + * built-in functions. */ int load_sys_func_table(const char*path) { @@ -130,14 +155,22 @@ cp = stype + strcspn(stype, " \t\r\n"); if (cp[0]) *cp++ = 0; + struct sfunc_return_type*def = find_in_sys_func_list(name); + if (def) { + /* Keep the original definition, but flag that it + overrides a later definition. */ + def->override_flag = true; + continue; + } + if (strcmp(stype,"vpiSysFuncReal") == 0) { cell = new struct sfunc_return_type_cell; cell->name = lex_strings.add(name); cell->type = IVL_VT_REAL; cell->wid = 1; cell->signed_flag = true; - cell->next = sfunc_stack; - sfunc_stack = cell; + cell->override_flag = false; + append_to_list(cell); continue; } @@ -147,8 +180,8 @@ cell->type = IVL_VT_LOGIC; cell->wid = 32; cell->signed_flag = true; - cell->next = sfunc_stack; - sfunc_stack = cell; + cell->override_flag = false; + append_to_list(cell); continue; } @@ -188,8 +221,8 @@ cell->type = IVL_VT_LOGIC; cell->wid = width; cell->signed_flag = signed_flag; - cell->next = sfunc_stack; - sfunc_stack = cell; + cell->override_flag = false; + append_to_list(cell); continue; } @@ -199,8 +232,19 @@ cell->type = IVL_VT_VOID; cell->wid = 0; cell->signed_flag = false; - cell->next = sfunc_stack; - sfunc_stack = cell; + cell->override_flag = false; + append_to_list(cell); + continue; + } + + if (strcmp(stype,"vpiSysFuncString") == 0) { + cell = new struct sfunc_return_type_cell; + cell->name = lex_strings.add(name); + cell->type = IVL_VT_STRING; + cell->wid = 0; // string is a dynamic length type + cell->signed_flag = false; + cell->override_flag = false; + append_to_list(cell); continue; } diff -Nru iverilog-10.3/target.cc iverilog-11.0/target.cc --- iverilog-10.3/target.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/target.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2013 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -183,6 +183,12 @@ "Unhandled NetFF." << endl; } +void target_t::lpm_latch(const NetLatch*) +{ + cerr << "target (" << typeid(*this).name() << "): " + "Unhandled NetLatch." << endl; +} + void target_t::lpm_mult(const NetMult*) { cerr << "target (" << typeid(*this).name() << "): " diff -Nru iverilog-10.3/target.h iverilog-11.0/target.h --- iverilog-10.3/target.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/target.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_target_H #define IVL_target_H /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 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 @@ -95,6 +95,7 @@ virtual void lpm_divide(const NetDivide*); virtual void lpm_modulo(const NetModulo*); virtual void lpm_ff(const NetFF*); + virtual void lpm_latch(const NetLatch*); virtual void lpm_mult(const NetMult*); virtual void lpm_mux(const NetMux*); virtual void lpm_pow(const NetPow*); diff -Nru iverilog-10.3/t-dll-api.cc iverilog-11.0/t-dll-api.cc --- iverilog-10.3/t-dll-api.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/t-dll-api.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,6 +1,7 @@ /* - * Copyright (c) 2000-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) + * Copyright (c) 2016 CERN Michele Castellana (michele.castellana@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 @@ -37,22 +38,28 @@ extern "C" ivl_island_t ivl_branch_island(ivl_branch_t net) { + assert(net); return net->island; } extern "C" ivl_nexus_t ivl_branch_terminal(ivl_branch_t net, int idx) { + assert(net); assert(idx >= 0); - assert( idx < 2); + assert(idx < 2); return net->pins[idx]; } extern "C" const char*ivl_design_delay_sel(ivl_design_t des) { + assert(des); + assert(des->self); return des->self->get_delay_sel(); } extern "C" const char*ivl_design_flag(ivl_design_t des, const char*key) { + assert(des); + assert(des->self); return des->self->get_flag(key); } @@ -60,6 +67,7 @@ ivl_process_f func, void*cd) { + assert(des); for (ivl_process_t idx = des->threads_; idx; idx = idx->next_) { int rc = (func)(idx, cd); if (rc != 0) @@ -72,8 +80,8 @@ extern "C" ivl_scope_t ivl_design_root(ivl_design_t des) { cerr << "ANACHRONISM: ivl_design_root called. " - "Use ivl_design_roots instead." << endl; - + "Use ivl_design_roots instead." << endl; + assert(des); assert (des->roots.size() > 0); return des->roots[0]; } @@ -81,17 +89,11 @@ extern "C" void ivl_design_roots(ivl_design_t des, ivl_scope_t **scopes, unsigned int *nscopes) { + assert(des); assert (nscopes && scopes); if (des->root_scope_list.size() == 0) { size_t fill = 0; - des->root_scope_list.resize(des->root_tasks.size() + des->packages.size() + des->roots.size() + des->classes.size()); - for (map::iterator idx = des->root_tasks.begin() - ; idx != des->root_tasks.end() ; ++ idx) - des->root_scope_list[fill++] = idx->second; - - for (map::iterator idx = des->classes.begin() - ; idx != des->classes.end() ; ++ idx) - des->root_scope_list[fill++] = idx->second; + des->root_scope_list.resize(des->packages.size() + des->roots.size()); for (size_t idx = 0 ; idx < des->packages.size() ; idx += 1) des->root_scope_list[fill++] = des->packages[idx]; @@ -105,16 +107,19 @@ extern "C" int ivl_design_time_precision(ivl_design_t des) { + assert(des); return des->time_precision; } extern "C" unsigned ivl_design_consts(ivl_design_t des) { + assert(des); return des->consts.size(); } extern "C" ivl_net_const_t ivl_design_const(ivl_design_t des, unsigned idx) { + assert(des); assert(idx < des->consts.size()); return des->consts[idx]; } @@ -134,21 +139,25 @@ extern "C" ivl_dis_domain_t ivl_discipline_domain(ivl_discipline_t net) { + assert(net); return net->domain(); } extern "C" ivl_nature_t ivl_discipline_flow(ivl_discipline_t net) { + assert(net); return net->flow(); } extern "C" const char* ivl_discipline_name(ivl_discipline_t net) { + assert(net); return net->name(); } extern "C" ivl_nature_t ivl_discipline_potential(ivl_discipline_t net) { + assert(net); return net->potential(); } @@ -197,6 +206,7 @@ extern "C" ivl_expr_t ivl_const_delay(ivl_net_const_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } @@ -296,6 +306,7 @@ extern "C" const char* ivl_event_name(ivl_event_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -320,6 +331,7 @@ extern "C" const char* ivl_event_basename(ivl_event_t net) { + assert(net); return net->name; } @@ -337,6 +349,7 @@ extern "C" ivl_scope_t ivl_event_scope(ivl_event_t net) { + assert(net); return net->scope; } @@ -381,13 +394,15 @@ extern "C" const char* ivl_expr_bits(ivl_expr_t net) { - assert(net && (net->type_ == IVL_EX_NUMBER)); + assert(net); + assert(net->type_ == IVL_EX_NUMBER); return net->u_.number_.bits_; } extern "C" ivl_branch_t ivl_expr_branch(ivl_expr_t net) { - assert(net && (net->type_ == IVL_EX_BACCESS)); + assert(net); + assert(net->type_ == IVL_EX_BACCESS); return net->u_.branch_.branch; } @@ -409,29 +424,34 @@ extern "C" uint64_t ivl_expr_delay_val(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_DELAY); return net->u_.delay_.value; } extern "C" double ivl_expr_dvalue(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_REALNUM); return net->u_.real_.value; } extern "C" ivl_enumtype_t ivl_expr_enumtype(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_ENUMTYPE); return net->u_.enumtype_.type; } extern "C" ivl_type_t ivl_expr_net_type(ivl_expr_t net) { + assert(net); return net->net_type; } extern "C" const char* ivl_expr_name(ivl_expr_t net) { + assert(net); switch (net->type_) { case IVL_EX_SFUNC: @@ -455,7 +475,8 @@ extern "C" ivl_nature_t ivl_expr_nature(ivl_expr_t net) { - assert(net && (net->type_ == IVL_EX_BACCESS)); + assert(net); + assert(net->type_ == IVL_EX_BACCESS); return net->u_.branch_.nature; } @@ -685,12 +706,14 @@ extern "C" const char* ivl_expr_string(ivl_expr_t net) { + assert(net); assert(net->type_ == IVL_EX_STRING); return net->u_.string_.value_; } extern "C" unsigned long ivl_expr_uvalue(ivl_expr_t net) { + assert(net); switch (net->type_) { case IVL_EX_ULONG: @@ -722,7 +745,7 @@ extern "C" unsigned ivl_expr_width(ivl_expr_t net) { -// assert(net); + assert(net); return net->width_; } @@ -791,6 +814,7 @@ extern "C" int ivl_island_flag_set(ivl_island_t net, unsigned flag, int value) { + assert(net); if (flag >= net->flags.size()) { if (value == 0) return 0; @@ -805,6 +829,7 @@ extern "C" int ivl_island_flag_test(ivl_island_t net, unsigned flag) { + assert(net); if (flag >= net->flags.size()) return 0; else @@ -847,12 +872,14 @@ extern "C" unsigned ivl_logic_attr_cnt(ivl_net_logic_t net) { + assert(net); return net->nattr; } extern "C" ivl_attribute_t ivl_logic_attr_val(ivl_net_logic_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } @@ -912,22 +939,26 @@ extern "C" ivl_logic_t ivl_logic_type(ivl_net_logic_t net) { + assert(net); return net->type_; } extern "C" unsigned ivl_logic_pins(ivl_net_logic_t net) { + assert(net); return net->npins_; } extern "C" ivl_nexus_t ivl_logic_pin(ivl_net_logic_t net, unsigned pin) { + assert(net); assert(pin < net->npins_); return net->pins_[pin]; } extern "C" ivl_udp_t ivl_logic_udp(ivl_net_logic_t net) { + assert(net); assert(net->type_ == IVL_LO_UDP); assert(net->udp); return net->udp; @@ -935,6 +966,7 @@ extern "C" ivl_expr_t ivl_logic_delay(ivl_net_logic_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } @@ -947,21 +979,25 @@ extern "C" int ivl_udp_sequ(ivl_udp_t net) { + assert(net); return net->sequ; } extern "C" unsigned ivl_udp_nin(ivl_udp_t net) { + assert(net); return net->nin; } extern "C" char ivl_udp_init(ivl_udp_t net) { + assert(net); return net->init; } extern "C" const char* ivl_udp_port(ivl_udp_t net, unsigned idx) { + assert(net); assert(idx <= net->nin); assert(net->ports); assert(net->ports[idx].c_str()); @@ -970,6 +1006,7 @@ extern "C" const char* ivl_udp_row(ivl_udp_t net, unsigned idx) { + assert(net); assert(idx < net->nrows); assert(net->table); assert(net->table[idx]); @@ -978,27 +1015,32 @@ extern "C" unsigned ivl_udp_rows(ivl_udp_t net) { + assert(net); return net->nrows; } extern "C" const char* ivl_udp_name(ivl_udp_t net) { + assert(net); assert(net->name); return net->name; } extern "C" const char* ivl_udp_file(ivl_udp_t net) { + assert(net); return net->file.str(); } extern "C" unsigned ivl_udp_lineno(ivl_udp_t net) { + assert(net); return net->lineno; } extern "C" const char* ivl_lpm_basename(ivl_lpm_t net) { + assert(net); return net->name; } @@ -1028,6 +1070,7 @@ extern "C" ivl_expr_t ivl_lpm_delay(ivl_lpm_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } @@ -1148,6 +1191,8 @@ switch (net->type) { case IVL_LPM_FF: return net->u_.ff.we; + case IVL_LPM_LATCH: + return net->u_.latch.e; default: assert(0); return 0; @@ -1156,11 +1201,13 @@ extern "C" const char* ivl_lpm_file(ivl_lpm_t net) { + assert(net); return net->file.str(); } extern "C" unsigned ivl_lpm_lineno(ivl_lpm_t net) { + assert(net); return net->lineno; } @@ -1184,6 +1231,8 @@ case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: @@ -1220,6 +1269,9 @@ case IVL_LPM_FF: assert(idx == 0); return net->u_.ff.d.pin; + case IVL_LPM_LATCH: + assert(idx == 0); + return net->u_.latch.d.pin; case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: @@ -1297,6 +1349,7 @@ */ extern "C" const char* ivl_lpm_name(ivl_lpm_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -1335,6 +1388,8 @@ case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: @@ -1344,6 +1399,8 @@ case IVL_LPM_FF: return net->u_.ff.q.pin; + case IVL_LPM_LATCH: + return net->u_.latch.q.pin; case IVL_LPM_MUX: return net->u_.mux.q; @@ -1432,6 +1489,7 @@ extern "C" ivl_nexus_t ivl_lpm_select(ivl_lpm_t net) { + assert(net); switch (net->type) { case IVL_LPM_MUX: @@ -1448,6 +1506,7 @@ extern "C" unsigned ivl_lpm_selects(ivl_lpm_t net) { + assert(net); switch (net->type) { case IVL_LPM_MUX: return net->u_.mux.swid; @@ -1482,6 +1541,8 @@ case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_DIVIDE: case IVL_LPM_MOD: case IVL_LPM_MULT: @@ -1525,6 +1586,7 @@ extern "C" unsigned ivl_lpm_size(ivl_lpm_t net) { + assert(net); switch (net->type) { case IVL_LPM_MUX: return net->u_.mux.size; @@ -1577,12 +1639,14 @@ extern "C" const char* ivl_lpm_string(ivl_lpm_t net) { + assert(net); assert(net->type == IVL_LPM_SFUNC); return net->u_.sfunc.fun_name; } extern "C" ivl_lpm_type_t ivl_lpm_type(ivl_lpm_t net) { + assert(net); return net->type; } @@ -1670,6 +1734,7 @@ extern "C" const char* ivl_nature_name(ivl_nature_t net) { + assert(net); return net->name(); } @@ -1864,6 +1929,12 @@ return obj->conditional ? 1 : 0; } +extern "C" int ivl_path_is_parallel(ivl_delaypath_t obj) +{ + assert(obj); + return obj->parallel ? 1 : 0; +} + extern uint64_t ivl_path_delay(ivl_delaypath_t obj, ivl_path_edge_t edg) { assert(obj); @@ -1879,16 +1950,19 @@ extern ivl_nexus_t ivl_path_source(ivl_delaypath_t net) { + assert(net); return net->src; } extern int ivl_path_source_posedge(ivl_delaypath_t net) { + assert(net); return net->posedge ? 1 : 0; } extern int ivl_path_source_negedge(ivl_delaypath_t net) { + assert(net); return net->negedge ? 1 : 0; } @@ -1906,32 +1980,38 @@ extern "C" ivl_process_type_t ivl_process_type(ivl_process_t net) { + assert(net); return net->type_; } extern "C" int ivl_process_analog(ivl_process_t net) { + assert(net); return net->analog_flag != 0; } extern "C" ivl_scope_t ivl_process_scope(ivl_process_t net) { + assert(net); return net->scope_; } extern "C" ivl_statement_t ivl_process_stmt(ivl_process_t net) { + assert(net); return net->stmt_; } extern "C" unsigned ivl_process_attr_cnt(ivl_process_t net) { + assert(net); return net->nattr; } extern "C" ivl_attribute_t ivl_process_attr_val(ivl_process_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } @@ -1945,6 +2025,7 @@ extern "C" ivl_attribute_t ivl_scope_attr_val(ivl_scope_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } @@ -1952,7 +2033,6 @@ extern "C" const char* ivl_scope_basename(ivl_scope_t net) { assert(net); - return net->name_; } @@ -1972,24 +2052,28 @@ extern "C" size_t ivl_scope_childs(ivl_scope_t net) { + assert(net); assert(net->child.size() == net->children.size()); return net->child.size(); } extern "C" ivl_scope_t ivl_scope_child(ivl_scope_t net, size_t idx) { - assert(net && idx < net->child.size()); + assert(net); + assert(idx < net->child.size()); return net->child[idx]; } extern "C" ivl_type_t ivl_scope_class(ivl_scope_t net, unsigned idx) { + assert(net); assert(idx < net->classes.size()); return net->classes[idx]; } extern "C" unsigned ivl_scope_classes(ivl_scope_t net) { + assert(net); return net->classes.size(); } @@ -2044,6 +2128,29 @@ return net->file.str(); } +extern "C" ivl_variable_type_t ivl_scope_func_type(ivl_scope_t net) +{ + assert(net); + assert(net->type_ == IVL_SCT_FUNCTION); + return net->func_type; +} + +extern "C" int ivl_scope_func_signed(ivl_scope_t net) +{ + assert(net); + assert(net->type_==IVL_SCT_FUNCTION); + assert(net->func_type==IVL_VT_LOGIC || net->func_type==IVL_VT_BOOL); + return net->func_signed? 1 : 0; +} + +extern "C" unsigned ivl_scope_func_width(ivl_scope_t net) +{ + assert(net); + assert(net->type_ == IVL_SCT_FUNCTION); + assert(net->func_type==IVL_VT_LOGIC || net->func_type==IVL_VT_BOOL); + return net->func_width; +} + extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net) { assert(net); @@ -2100,6 +2207,7 @@ static void push_scope_basename(ivl_scope_t net, char*buf) { + assert(net); if (net->parent == 0) { strcpy(buf, net->name_); return; @@ -2112,6 +2220,7 @@ extern "C" const char* ivl_scope_name(ivl_scope_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -2154,21 +2263,22 @@ extern "C" unsigned ivl_scope_mod_module_ports(ivl_scope_t net) { assert(net); - assert (net->type_ == IVL_SCT_MODULE ); + assert(net->type_ == IVL_SCT_MODULE ); return static_cast(net->module_ports_info.size()); } extern "C" const char *ivl_scope_mod_module_port_name(ivl_scope_t net, unsigned idx ) { assert(net); - assert (net->type_ == IVL_SCT_MODULE ); - assert( idx < net->module_ports_info.size()); + assert(net->type_ == IVL_SCT_MODULE ); + assert(idx < net->module_ports_info.size()); return net->module_ports_info[idx].name; } extern "C" ivl_signal_port_t ivl_scope_mod_module_port_type(ivl_scope_t net, unsigned idx ) { + assert(net); switch( net->module_ports_info[idx].type ) { case PortType::PINPUT : return IVL_SIP_INPUT; @@ -2180,6 +2290,7 @@ extern "C" unsigned ivl_scope_mod_module_port_width(ivl_scope_t net, unsigned idx ) { + assert(net); return net->module_ports_info[idx].width; } @@ -2262,31 +2373,37 @@ extern "C" int ivl_signal_array_base(ivl_signal_t net) { + assert(net); return net->array_base; } extern "C" unsigned ivl_signal_array_count(ivl_signal_t net) { + assert(net); return net->array_words; } extern "C" unsigned ivl_signal_array_addr_swapped(ivl_signal_t net) { + assert(net); return net->array_addr_swapped; } extern "C" unsigned ivl_signal_dimensions(ivl_signal_t net) { + assert(net); return net->array_dimensions_; } extern "C" ivl_discipline_t ivl_signal_discipline(ivl_signal_t net) { + assert(net); return net->discipline; } extern "C" const char* ivl_signal_attr(ivl_signal_t net, const char*key) { + assert(net); if (net->nattr == 0) return 0; @@ -2302,22 +2419,26 @@ extern "C" unsigned ivl_signal_attr_cnt(ivl_signal_t net) { + assert(net); return net->nattr; } extern "C" ivl_attribute_t ivl_signal_attr_val(ivl_signal_t net, unsigned idx) { + assert(net); assert(idx < net->nattr); return net->attr + idx; } extern "C" const char* ivl_signal_basename(ivl_signal_t net) { + assert(net); return net->name_; } extern "C" const char* ivl_signal_name(ivl_signal_t net) { + assert(net); static char*name_buffer = 0; static unsigned name_size = 0; @@ -2338,6 +2459,7 @@ extern "C" ivl_nexus_t ivl_signal_nex(ivl_signal_t net, unsigned word) { + assert(net); assert(word < net->array_words); if (net->array_words > 1) { if (net->pins) { @@ -2354,23 +2476,27 @@ extern "C" unsigned ivl_signal_packed_dimensions(ivl_signal_t net) { + assert(net); return net->packed_dims.size(); } extern "C" int ivl_signal_packed_msb(ivl_signal_t net, unsigned dim) { + assert(net); assert(dim < net->packed_dims.size()); return net->packed_dims[dim].get_msb(); } extern "C" int ivl_signal_packed_lsb(ivl_signal_t net, unsigned dim) { + assert(net); assert(dim < net->packed_dims.size()); return net->packed_dims[dim].get_lsb(); } extern "C" int ivl_signal_msb(ivl_signal_t net) { + assert(net); if (net->packed_dims.empty()) return 0; @@ -2380,6 +2506,7 @@ extern "C" int ivl_signal_lsb(ivl_signal_t net) { + assert(net); if (net->packed_dims.empty()) return 0; @@ -2395,31 +2522,39 @@ extern "C" unsigned ivl_signal_width(ivl_signal_t net) { + assert(net); + assert(net->net_type); return net->net_type->packed_width(); } extern "C" ivl_signal_port_t ivl_signal_port(ivl_signal_t net) { + assert(net); return net->port_; } extern "C" int ivl_signal_module_port_index(ivl_signal_t net) { + assert(net); return net->module_port_index_; } extern "C" int ivl_signal_local(ivl_signal_t net) { + assert(net); return net->local_; } extern "C" int ivl_signal_signed(ivl_signal_t net) { + assert(net); + assert(net->net_type); return net->net_type->get_signed()? 1 : 0; } extern "C" unsigned ivl_signal_forced_net(ivl_signal_t net) { + assert(net); return net->forced_net_; } @@ -2437,6 +2572,7 @@ extern "C" int ivl_signal_integer(ivl_signal_t net) { + assert(net); if (const netvector_t*vec = dynamic_cast (net->net_type)) return vec->get_isint()? 1 : 0; else if (const netenum_t*enm = dynamic_cast (net->net_type)) @@ -2447,47 +2583,57 @@ extern "C" ivl_variable_type_t ivl_signal_data_type(ivl_signal_t net) { + assert(net); + assert(net->net_type); return net->net_type->base_type(); } extern "C" ivl_type_t ivl_signal_net_type(ivl_signal_t net) { + assert(net); return net->net_type; } extern "C" unsigned ivl_signal_npath(ivl_signal_t net) { + assert(net); return net->npath; } extern "C" ivl_delaypath_t ivl_signal_path(ivl_signal_t net, unsigned idx) { + assert(net); assert(idx < net->npath); return net->path + idx; } extern "C" ivl_signal_type_t ivl_signal_type(ivl_signal_t net) { + assert(net); return net->type_; } extern "C" ivl_statement_type_t ivl_statement_type(ivl_statement_t net) { + assert(net); return net->type_; } extern "C" const char* ivl_stmt_file(ivl_statement_t net) { + assert(net); return net->file.str(); } extern "C" unsigned ivl_stmt_lineno(ivl_statement_t net) { + assert(net); return net->lineno; } extern "C" ivl_scope_t ivl_stmt_block_scope(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: @@ -2502,6 +2648,7 @@ extern "C" unsigned ivl_stmt_block_count(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: @@ -2517,6 +2664,7 @@ extern "C" ivl_statement_t ivl_stmt_block_stmt(ivl_statement_t net, unsigned i) { + assert(net); switch (net->type_) { case IVL_ST_BLOCK: case IVL_ST_FORK: @@ -2531,6 +2679,7 @@ extern "C" ivl_scope_t ivl_stmt_call(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ALLOC: return net->u_.alloc_.scope; @@ -2551,6 +2700,7 @@ extern "C" unsigned ivl_stmt_case_count(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: @@ -2565,6 +2715,7 @@ extern "C" ivl_expr_t ivl_stmt_case_expr(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: @@ -2579,8 +2730,25 @@ } } +extern "C" ivl_case_quality_t ivl_stmt_case_quality(ivl_statement_t net) +{ + assert(net); + switch (net->type_) { + case IVL_ST_CASE: + case IVL_ST_CASER: + case IVL_ST_CASEX: + case IVL_ST_CASEZ: + return net->u_.case_.quality; + + default: + assert(0); + return IVL_CASE_QUALITY_BASIC; + } +} + extern "C" ivl_statement_t ivl_stmt_case_stmt(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_CASE: case IVL_ST_CASER: @@ -2597,6 +2765,7 @@ extern "C" ivl_expr_t ivl_stmt_cond_expr(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN_NB: return net->u_.assign_.count; @@ -2623,6 +2792,7 @@ extern "C" ivl_statement_t ivl_stmt_cond_false(ivl_statement_t net) { + assert(net); assert(net->type_ == IVL_ST_CONDIT); if (net->u_.condit_.stmt_[1].type_ == IVL_ST_NONE) return 0; @@ -2632,6 +2802,7 @@ extern "C" ivl_statement_t ivl_stmt_cond_true(ivl_statement_t net) { + assert(net); assert(net->type_ == IVL_ST_CONDIT); if (net->u_.condit_.stmt_[0].type_ == IVL_ST_NONE) return 0; @@ -2641,6 +2812,7 @@ extern "C" ivl_expr_t ivl_stmt_delay_expr(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2657,12 +2829,24 @@ extern "C" uint64_t ivl_stmt_delay_val(ivl_statement_t net) { + assert(net); assert(net->type_ == IVL_ST_DELAY); return net->u_.delay_.value; } +extern "C" unsigned ivl_stmt_needs_t0_trigger(ivl_statement_t net) +{ + assert(net); + if (net->type_ == IVL_ST_WAIT) { + return net->u_.wait_.needs_t0_trigger; + } else { + return 0; + } +} + extern "C" unsigned ivl_stmt_nevent(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN_NB: return net->u_.assign_.nevent; @@ -2681,6 +2865,7 @@ extern "C" ivl_event_t ivl_stmt_events(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN_NB: assert(idx < net->u_.assign_.nevent); @@ -2708,6 +2893,7 @@ extern "C" ivl_expr_t ivl_stmt_lexp(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_CONTRIB: return net->u_.contrib_.lval; @@ -2719,6 +2905,7 @@ extern "C" ivl_lval_t ivl_stmt_lval(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2737,6 +2924,7 @@ extern "C" unsigned ivl_stmt_lvals(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2754,6 +2942,7 @@ extern "C" unsigned ivl_stmt_lwidth(ivl_statement_t net) { + assert(net); assert((net->type_ == IVL_ST_ASSIGN) || (net->type_ == IVL_ST_ASSIGN_NB) || (net->type_ == IVL_ST_CASSIGN) @@ -2786,6 +2975,7 @@ extern "C" const char* ivl_stmt_name(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.name_; @@ -2798,6 +2988,7 @@ extern "C" char ivl_stmt_opcode(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: return net->u_.assign_.oper; @@ -2809,6 +3000,7 @@ extern "C" ivl_expr_t ivl_stmt_parm(ivl_statement_t net, unsigned idx) { + assert(net); switch (net->type_) { case IVL_ST_STASK: assert(idx < net->u_.stask_.nparm_); @@ -2822,6 +3014,7 @@ extern "C" unsigned ivl_stmt_parm_count(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.nparm_; @@ -2833,6 +3026,7 @@ extern "C" ivl_expr_t ivl_stmt_rval(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_ASSIGN: case IVL_ST_ASSIGN_NB: @@ -2850,6 +3044,7 @@ extern "C" ivl_sfunc_as_task_t ivl_stmt_sfunc_as_task(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_STASK: return net->u_.stask_.sfunc_as_task_; @@ -2862,6 +3057,7 @@ extern "C" ivl_statement_t ivl_stmt_sub_stmt(ivl_statement_t net) { + assert(net); switch (net->type_) { case IVL_ST_DELAY: return net->u_.delay_.stmt_; @@ -2884,67 +3080,80 @@ extern "C" const char*ivl_switch_basename(ivl_switch_t net) { + assert(net); return net->name; } extern "C" ivl_scope_t ivl_switch_scope(ivl_switch_t net) { + assert(net); return net->scope; } extern "C" ivl_switch_type_t ivl_switch_type(ivl_switch_t net) { + assert(net); return net->type; } extern "C" ivl_nexus_t ivl_switch_a(ivl_switch_t net) { + assert(net); return net->pins[0]; } extern "C" ivl_nexus_t ivl_switch_b(ivl_switch_t net) { + assert(net); return net->pins[1]; } extern "C" ivl_nexus_t ivl_switch_enable(ivl_switch_t net) { + assert(net); return net->pins[2]; } extern "C" unsigned ivl_switch_width(ivl_switch_t net) { + assert(net); return net->width; } extern "C" unsigned ivl_switch_part(ivl_switch_t net) { + assert(net); return net->part; } extern "C" unsigned ivl_switch_offset(ivl_switch_t net) { + assert(net); return net->offset; } extern "C" ivl_expr_t ivl_switch_delay(ivl_switch_t net, unsigned transition) { + assert(net); assert(transition < 3); return net->delay[transition]; } extern "C" const char* ivl_switch_file(ivl_switch_t net) { + assert(net); return net->file; } extern "C" ivl_island_t ivl_switch_island(ivl_switch_t net) { + assert(net); return net->island; } extern "C" unsigned ivl_switch_lineno(ivl_switch_t net) { + assert(net); return net->lineno; } @@ -2965,12 +3174,14 @@ extern "C" unsigned ivl_type_packed_dimensions(ivl_type_t net) { + assert(net); vector slice = net->slice_dimensions(); return slice.size(); } extern "C" int ivl_type_packed_lsb(ivl_type_t net, unsigned dim) { + assert(net); vector slice = net->slice_dimensions(); assert(dim < slice.size()); return slice[dim].get_lsb(); @@ -2978,6 +3189,7 @@ extern "C" int ivl_type_packed_msb(ivl_type_t net, unsigned dim) { + assert(net); vector slice = net->slice_dimensions(); assert(dim < slice.size()); return slice[dim].get_msb(); @@ -2985,9 +3197,8 @@ extern "C" const char* ivl_type_name(ivl_type_t net) { - if (const netclass_t*class_type = dynamic_cast(net)) { + if (const netclass_t*class_type = dynamic_cast(net)) return class_type->get_name(); - } return 0; } @@ -3019,5 +3230,6 @@ extern "C" int ivl_type_signed(ivl_type_t net) { + assert(net); return net->get_signed()? 1 : 0; } diff -Nru iverilog-10.3/t-dll.cc iverilog-11.0/t-dll.cc --- iverilog-10.3/t-dll.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/t-dll.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -27,6 +27,7 @@ # include "compiler.h" # include "t-dll.h" # include "netclass.h" +# include "netqueue.h" # include "netmisc.h" # include "discipline.h" # include @@ -103,6 +104,13 @@ { return strerror( errno ); } #endif +ivl_scope_s::ivl_scope_s() +{ + func_type = IVL_VT_NO_TYPE; + func_signed = false; + func_width = 0; +} + /* * The custom new operator for the ivl_nexus_s type allows us to * allocate nexus objects in blocks. There are generally lots of them @@ -249,17 +257,6 @@ return 0; } - if (cur->type() == NetScope::CLASS) { - ivl_scope_t tmp = des.classes[cur]; - return tmp; - } - - if (cur->type()==NetScope::TASK || cur->type()==NetScope::FUNC) { - map::const_iterator idx = des.root_tasks.find(cur); - if (idx != des.root_tasks.end()) - return idx->second; - } - for (unsigned idx = 0; idx < des.roots.size(); idx += 1) { assert(des.roots[idx]); ivl_scope_t scope = find_scope_from_root(des.roots[idx], cur); @@ -274,20 +271,6 @@ return scope; } - for (map::iterator idx = des.classes.begin() - ; idx != des.classes.end() ; ++ idx) { - ivl_scope_t scope = find_scope_from_root(idx->second, cur); - if (scope) - return scope; - } - - for (map::iterator idx = des.root_tasks.begin() - ; idx != des.root_tasks.end() ; ++ idx) { - ivl_scope_t scope = find_scope_from_root(idx->second, cur); - if (scope) - return scope; - } - return 0; } @@ -596,6 +579,28 @@ expr_ = 0; } +static void fill_in_scope_function(ivl_scope_t scope, const NetScope*net) +{ + scope->type_ = IVL_SCT_FUNCTION; + const NetFuncDef*def = net->func_def(); + assert(def); + + if (def->is_void()) { + // Special case: If there is no return signal, this is + // apparently a VOID function. + scope->func_type = IVL_VT_VOID; + scope->func_signed = 0; + scope->func_width = 0; + } else { + const NetNet*return_sig = def->return_sig(); + scope->func_type = return_sig->data_type(); + scope->func_signed = return_sig->get_signed(); + scope->func_width = return_sig->vector_width(); + } + + scope->tname_ = def->scope()->basename(); +} + void dll_target::add_root(const NetScope *s) { ivl_scope_t root_ = new struct ivl_scope_s; @@ -628,22 +633,6 @@ case NetScope::CLASS: root_->type_ = IVL_SCT_CLASS; break; - case NetScope::TASK: { - const NetTaskDef*def = s->task_def(); - if (def == 0) { - cerr << "?:?" << ": internal error: " - << "task " << root_->name_ - << " has no definition." << endl; - } - assert(def); - root_->type_ = IVL_SCT_TASK; - root_->tname_ = def->scope()->basename(); - break; - } - break; - case NetScope::FUNC: - root_->type_ = IVL_SCT_FUNCTION; - break; default: assert(0); } @@ -667,16 +656,6 @@ des_.packages.push_back(root_); break; - case NetScope::CLASS: - root_->ports = 0; - des_.classes[s] = root_; - break; - - case NetScope::TASK: - case NetScope::FUNC: - des_.root_tasks[s] = root_; - break; - default: assert(0); break; @@ -718,11 +697,7 @@ } assert(idx == des_.disciplines.size()); - list scope_list = des->find_roottask_scopes(); - for (list::const_iterator cur = scope_list.begin() - ; cur != scope_list.end() ; ++ cur) { - add_root(*cur); - } + list scope_list; scope_list = des->find_package_scopes(); for (list::const_iterator cur = scope_list.begin() @@ -997,6 +972,12 @@ case NetLogic::CMOS: obj->type_ = IVL_LO_CMOS; break; + case NetLogic::EQUIV: + obj->type_ = IVL_LO_EQUIV; + break; + case NetLogic::IMPL: + obj->type_ = IVL_LO_IMPL; + break; case NetLogic::NAND: obj->type_ = IVL_LO_NAND; break; @@ -1253,6 +1234,12 @@ case NetCaseCmp::NEQ: obj->type = IVL_LPM_CMP_NEE; break; + case NetCaseCmp::WEQ: + obj->type = IVL_LPM_CMP_WEQ; + break; + case NetCaseCmp::WNE: + obj->type = IVL_LPM_CMP_WNE; + break; case NetCaseCmp::XEQ: obj->type = IVL_LPM_CMP_EQX; break; @@ -2029,6 +2016,39 @@ nexus_lpm_add(obj->u_.ff.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); } +void dll_target::lpm_latch(const NetLatch*net) +{ + ivl_lpm_t obj = new struct ivl_lpm_s; + obj->type = IVL_LPM_LATCH; + obj->name = net->name(); + obj->scope = find_scope(des_, net->scope()); + assert(obj->scope); + FILE_NAME(obj, net); + + obj->width = net->width(); + + scope_add_lpm(obj->scope, obj); + + const Nexus*nex; + + nex = net->pin_Enable().nexus(); + assert(nex->t_cookie()); + obj->u_.latch.e = nex->t_cookie(); + assert(obj->u_.latch.e); + nexus_lpm_add(obj->u_.latch.e, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); + + nex = net->pin_Q().nexus(); + assert(nex->t_cookie()); + obj->u_.latch.q.pin = nex->t_cookie(); + nexus_lpm_add(obj->u_.latch.q.pin, obj, 0, + IVL_DR_STRONG, IVL_DR_STRONG); + + nex = net->pin_Data().nexus(); + assert(nex->t_cookie()); + obj->u_.latch.d.pin = nex->t_cookie(); + nexus_lpm_add(obj->u_.latch.d.pin, obj, 0, IVL_DR_HiZ, IVL_DR_HiZ); +} + /* * Make the NetMult object into an IVL_LPM_MULT node. */ @@ -2448,16 +2468,7 @@ void dll_target::scope(const NetScope*net) { - if (net->parent()==0 && net->type()==NetScope::CLASS) { - - if (debug_emit) { - cerr << "dll_target::scope: " - << "Add class " << scope_path(net) - << " as a root scope." << endl; - } - add_root(net); - - } if (net->parent() == 0) { + if (net->parent() == 0) { // Root scopes are already created... @@ -2516,8 +2527,7 @@ break; } case NetScope::FUNC: - scop->type_ = IVL_SCT_FUNCTION; - scop->tname_ = net->func_def()->scope()->basename(); + fill_in_scope_function(scop, net); break; case NetScope::BEGIN_END: scop->type_ = IVL_SCT_BEGIN; @@ -2532,7 +2542,8 @@ scop->tname_ = scop->name_; break; case NetScope::CLASS: - assert(0); + scop->type_ = IVL_SCT_CLASS; + scop->tname_ = scop->name_; break; } } @@ -2691,11 +2702,18 @@ // The back-end API doesn't yet support multi-dimension // unpacked arrays, so just report the canonical dimensions. obj->array_base = 0; - obj->array_words = net->unpacked_count(); + // For a queue we pass the maximum queue size as the array words. + if (obj->net_type->base_type() == IVL_VT_QUEUE) { + long max_size = net->queue_type()->max_idx()+1; + ivl_assert(*net, max_size >= 0); + obj->array_words = max_size; + } else + obj->array_words = net->unpacked_count(); obj->array_addr_swapped = 0; } - ivl_assert(*net, obj->array_words == net->pin_count()); + ivl_assert(*net, (obj->array_words == net->pin_count()) || + (obj->net_type->base_type() == IVL_VT_QUEUE)); if (debug_optimizer && obj->array_words > 1000) cerr << "debug: " "t-dll creating nexus array " << obj->array_words << " long" << endl; if (obj->array_words > 1 && net->pins_are_virtual()) { @@ -2789,6 +2807,7 @@ obj->path[ptr].src = nex->t_cookie(); obj->path[ptr].condit = path_condit; obj->path[ptr].conditional = src->is_condit(); + obj->path[ptr].parallel = src->is_parallel(); obj->path[ptr].posedge = src->is_posedge(); obj->path[ptr].negedge = src->is_negedge(); for (unsigned pe = 0 ; pe < 12 ; pe += 1) { diff -Nru iverilog-10.3/t-dll-expr.cc iverilog-11.0/t-dll-expr.cc --- iverilog-10.3/t-dll-expr.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/t-dll-expr.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 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 @@ -68,7 +68,7 @@ off >>= 1; } - /* Now make the subtractor (x-4 in the above example) + /* Now make the subtracter (x-4 in the above example) that has as input A the index expression and input B the constant to subtract. */ ivl_expr_t tmps = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); @@ -104,7 +104,7 @@ val >>= 1; } - /* Now make the subtractor (x-4 in the above example) + /* Now make the subtracter (x-4 in the above example) that has as input A the index expression and input B the constant to subtract. */ ivl_expr_t tmps = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); @@ -549,7 +549,6 @@ ivl_expr_t expr = (ivl_expr_t)calloc(1, sizeof(struct ivl_expr_s)); expr->type_ = IVL_EX_SFUNC; - FILE_NAME(expr, net); expr->value_= net->expr_type(); expr->net_type=net->net_type(); expr->width_= net->expr_width(); diff -Nru iverilog-10.3/t-dll.h iverilog-11.0/t-dll.h --- iverilog-10.3/t-dll.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/t-dll.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_t_dll_H #define IVL_t_dll_H /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 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 @@ -76,6 +76,7 @@ void lpm_compare(const NetCompare*); void lpm_divide(const NetDivide*); void lpm_ff(const NetFF*); + void lpm_latch(const NetLatch*); void lpm_modulo(const NetModulo*); void lpm_mult(const NetMult*); void lpm_mux(const NetMux*); @@ -205,6 +206,7 @@ ivl_nexus_t src; ivl_nexus_t condit; bool conditional; + bool parallel; bool posedge; bool negedge; uint64_t delay[12]; @@ -388,6 +390,17 @@ ivl_expr_t aset_value; ivl_expr_t sset_value; } ff; + struct ivl_lpm_latch_s { + ivl_nexus_t e; + union { + ivl_nexus_t*pins; + ivl_nexus_t pin; + } q; + union { + ivl_nexus_t*pins; + ivl_nexus_t pin; + } d; + } latch; struct ivl_lpm_mux_s { unsigned size; @@ -633,7 +646,7 @@ * that generally only matters for VPI calls. */ struct ivl_process_s { - ivl_process_type_t type_ : 2; + ivl_process_type_t type_ : 3; unsigned int analog_flag : 1; ivl_scope_t scope_; ivl_statement_t stmt_; @@ -653,6 +666,8 @@ * there. */ struct ivl_scope_s { + ivl_scope_s(); + ivl_scope_t parent; std::map children; // This is just like the children map above, but in vector @@ -686,6 +701,9 @@ /* Scopes that are tasks/functions have a definition. */ ivl_statement_t def; unsigned is_auto; + ivl_variable_type_t func_type; + bool func_signed; + unsigned func_width; unsigned is_cell; @@ -793,6 +811,7 @@ } block_; struct { /* IVL_ST_CASE, IVL_ST_CASEX, IVL_ST_CASEZ */ + ivl_case_quality_t quality; ivl_expr_t cond; unsigned ncase; ivl_expr_t*case_ex; @@ -845,6 +864,7 @@ } utask_; struct { /* IVL_ST_TRIGGER IVL_ST_WAIT */ + unsigned needs_t0_trigger; unsigned nevent; union { ivl_event_t event; diff -Nru iverilog-10.3/t-dll-proc.cc iverilog-11.0/t-dll-proc.cc --- iverilog-10.3/t-dll-proc.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/t-dll-proc.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -497,6 +497,7 @@ } assert(stmt_cur_->type_ != IVL_ST_NONE); + stmt_cur_->u_.case_.quality = net->case_quality(); assert(expr_ == 0); assert(net->expr()); net->expr()->expr_scan(this); @@ -847,12 +848,15 @@ /* This is a wait fork statement. */ if ((net->nevents() == 1) && (net->event(0) == 0)) { + stmt_cur_->u_.wait_.needs_t0_trigger = 0; stmt_cur_->u_.wait_.event = 0; stmt_cur_->type_ = IVL_ST_WAIT; stmt_cur_->u_.wait_.stmt_->type_ = IVL_ST_NOOP; return true; } + stmt_cur_->u_.wait_.needs_t0_trigger = net->has_t0_trigger(); + // This event processing code is also in the NB assign above. if (net->nevents() > 1) { stmt_cur_->u_.wait_.events = (ivl_event_t*) diff -Nru iverilog-10.3/tgt-blif/blif.cc iverilog-11.0/tgt-blif/blif.cc --- iverilog-10.3/tgt-blif/blif.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-blif/blif.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2013-2020 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 @@ -35,7 +35,7 @@ static const char*version_string = "Icarus Verilog BLIF Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2013,2015 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2013-2020 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" diff -Nru iverilog-10.3/tgt-blif/logic_gate.cc iverilog-11.0/tgt-blif/logic_gate.cc --- iverilog-10.3/tgt-blif/logic_gate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-blif/logic_gate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -96,6 +96,20 @@ fprintf(fd, "0 1\n"); break; + case IVL_LO_PULLDOWN: + assert(ivl_logic_pins(net)==1); + fprintf(fd, "0\n"); + break; + case IVL_LO_PULLUP: + assert(ivl_logic_pins(net)==1); + fprintf(fd, "1\n"); + break; + + case IVL_LO_BUFZ: + assert(ivl_logic_pins(net)==2); + fprintf(fd, "1 1\n"); + break; + default: fprintf(fd, "# ERROR: Logic type %d not handled\n", ivl_logic_type(net)); rc += 1; diff -Nru iverilog-10.3/tgt-blif/lpm.cc iverilog-11.0/tgt-blif/lpm.cc --- iverilog-10.3/tgt-blif/lpm.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-blif/lpm.cc 2020-09-26 22:44:25.000000000 +0000 @@ -67,8 +67,9 @@ int print_lpm(FILE*fd, ivl_lpm_t net) { int rc = 0; + ivl_lpm_type_t type = ivl_lpm_type(net); - switch (ivl_lpm_type(net)) { + switch (type) { case IVL_LPM_ADD: rc += print_lpm_add(fd, net); break; @@ -108,6 +109,13 @@ case IVL_LPM_SUB: rc += print_lpm_sub(fd, net); break; + case IVL_LPM_SHIFTL: + case IVL_LPM_SHIFTR: + rc += print_lpm_shift(fd, net, type == IVL_LPM_SHIFTL); + break; + case IVL_LPM_SIGN_EXT: + rc += print_lpm_sign_ext(fd, net); + break; default: fprintf(fd, "# XXXX ivl_lpm_type(net) --> %d\n", ivl_lpm_type(net)); fprintf(stderr, "%s:%u: sorry: ivl_lpm_type(net)==%d not implemented.\n", diff -Nru iverilog-10.3/tgt-blif/lpm_shift.cc iverilog-11.0/tgt-blif/lpm_shift.cc --- iverilog-10.3/tgt-blif/lpm_shift.cc 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/tgt-blif/lpm_shift.cc 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2016 Yury Gribov (tetra2005@gmail.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +# include "priv.h" +# include "nex_data.h" +# include + +/* + * Implement IVL_LPM_SHIFT devices (via standard layered barrel shifter design) + */ +int print_lpm_shift(FILE*fd, ivl_lpm_t net, bool left) +{ + fprintf(fd, "# %s:%u: IVL_LPM_SHIFT%c: width=%u\n", + ivl_lpm_file(net), ivl_lpm_lineno(net), left ? 'L' : 'R', + ivl_lpm_width(net)); + + ivl_nexus_t q_nex = ivl_lpm_q(net); + ivl_nexus_t d_nex = ivl_lpm_data(net,0); + ivl_nexus_t s_nex = ivl_lpm_data(net,1); + + blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); + blif_nex_data_t*d_ned = blif_nex_data_t::get_nex_data(d_nex); + blif_nex_data_t*s_ned = blif_nex_data_t::get_nex_data(s_nex); + + unsigned dataw = ivl_lpm_width(net); + size_t shiftw = s_ned->get_width(); + bool signed_ = ivl_lpm_signed(net); + + assert(dataw == q_ned->get_width()); + assert(dataw == d_ned->get_width()); + + // TODO: output width can be larger than data + + // TODO: optimizations: + // * too large shift widths (more than data size) + // * shift width of 1 + // * data width of 1 + + for (size_t lvl = 0 ; lvl < shiftw ; lvl += 1) { + for (unsigned idx = 0 ; idx < dataw ; idx += 1) { + unsigned idx_2 = left ? idx - (1 << lvl) : idx + (1 << lvl); + bool borrow = idx_2 >= dataw; + bool signed_borrow = borrow && !left && signed_; + + // First arg (shift) + fprintf(fd, ".names %s%s", + s_ned->get_name(), s_ned->get_name_index(lvl)); + + // Multiplexed bits + if (!borrow) { + if (lvl == 0) { + fprintf(fd, " %s%s %s%s", + d_ned->get_name(), d_ned->get_name_index(idx), + d_ned->get_name(), d_ned->get_name_index(idx_2)); + } else { + fprintf(fd, " %s/%zu/%u %s/%zu/%u", + q_ned->get_name(), lvl - 1, idx, + q_ned->get_name(), lvl - 1, idx_2); + } + } else if (signed_borrow) { + if (lvl == 0) { + fprintf(fd, " %s%s %s%s", + d_ned->get_name(), d_ned->get_name_index(idx), + d_ned->get_name(), d_ned->get_name_index(dataw - 1)); + } else { + fprintf(fd, " %s/%zu/%u %s%s", + q_ned->get_name(), lvl - 1, idx, + d_ned->get_name(), d_ned->get_name_index(dataw - 1)); + } + } else { + if (lvl == 0) { + fprintf(fd, " %s%s", + d_ned->get_name(), d_ned->get_name_index(idx)); + } else { + fprintf(fd, " %s/%zu/%u", + q_ned->get_name(), lvl - 1, idx); + } + } + + // Output + if (lvl == shiftw - 1) { + fprintf(fd, " %s%s\n", + q_ned->get_name(), q_ned->get_name_index(idx)); + } else { + fprintf(fd, " %s/%zu/%u\n", + q_ned->get_name(), lvl, idx); + } + + if (!borrow || signed_borrow) + fputs("1-1 1\n" + "01- 1\n", + fd); + else + fputs("01 1\n", fd); + } + } + + return 0; +} + diff -Nru iverilog-10.3/tgt-blif/lpm_sign_ext.cc iverilog-11.0/tgt-blif/lpm_sign_ext.cc --- iverilog-10.3/tgt-blif/lpm_sign_ext.cc 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/tgt-blif/lpm_sign_ext.cc 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2016 Yury Gribov (tetra2005@gmail.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +# include "priv.h" +# include "nex_data.h" +# include + +/* + * Implement IVL_LPM_SIGN_EXT devices + */ +int print_lpm_sign_ext(FILE*fd, ivl_lpm_t net) +{ + fprintf(fd, "# %s:%u: IVL_LPM_SIGN_EXT: width=%u\n", + ivl_lpm_file(net), ivl_lpm_lineno(net), ivl_lpm_width(net)); + + ivl_nexus_t q_nex = ivl_lpm_q(net); + ivl_nexus_t d_nex = ivl_lpm_data(net,0); + + blif_nex_data_t*q_ned = blif_nex_data_t::get_nex_data(q_nex); + blif_nex_data_t*d_ned = blif_nex_data_t::get_nex_data(d_nex); + + unsigned inw = d_ned->get_width(); + unsigned outw = ivl_lpm_width(net); + +//printf("Shift: LPM width = %u, output width = %zd, input width = %u\n", outw, q_ned->get_width(), inw); + + assert(outw == q_ned->get_width()); + assert(inw < outw); + + for (unsigned idx = 0 ; idx < outw ; idx += 1) { + unsigned idx_in = idx < inw ? idx : inw - 1; + + fprintf(fd, ".names %s%s %s%s\n", + d_ned->get_name(), d_ned->get_name_index(idx_in), + q_ned->get_name(), q_ned->get_name_index(idx)); + fprintf(fd, "1 1\n"); + } + + return 0; +} + diff -Nru iverilog-10.3/tgt-blif/Makefile.in iverilog-11.0/tgt-blif/Makefile.in --- iverilog-10.3/tgt-blif/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-blif/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -44,7 +44,8 @@ LDFLAGS = @LDFLAGS@ O = blif.o constants.o logic_gate.o lpm.o lpm_add.o lpm_cmp_eq.o lpm_cmp_gt.o \ - lpm_ff.o lpm_mux.o lpm_part_vp.o lpm_re_logic.o nex_data.o + lpm_ff.o lpm_mux.o lpm_part_vp.o lpm_re_logic.o lpm_shift.o lpm_sign_ext.o \ + nex_data.o all: dep blif.tgt @@ -57,7 +58,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.cc) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -81,18 +83,17 @@ blif.tgt: $O $(TGTDEPLIBS) $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -install: all installdirs $(libdir)/ivl$(suffix)/blif.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/blif.conf $(libdir)/ivl$(suffix)/blif-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/blif.tgt: ./blif.tgt - $(INSTALL_PROGRAM) ./blif.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/blif.tgt" +F = ./blif.tgt \ + $(srcdir)/blif.conf \ + $(srcdir)/blif-s.conf -$(libdir)/ivl$(suffix)/blif.conf: $(srcdir)/blif.conf +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./blif.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/blif.tgt" $(INSTALL_DATA) $(srcdir)/blif.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/blif.conf" - -$(libdir)/ivl$(suffix)/blif-s.conf: $(srcdir)/blif-s.conf $(INSTALL_DATA) $(srcdir)/blif-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/blif-s.conf" - installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-blif/nex_data.h iverilog-11.0/tgt-blif/nex_data.h --- iverilog-10.3/tgt-blif/nex_data.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-blif/nex_data.h 2020-09-26 22:44:25.000000000 +0000 @@ -32,7 +32,7 @@ private: // The constructors are private. Only the get_nex_data() // function can create these objects. - blif_nex_data_t(ivl_nexus_t nex); + explicit blif_nex_data_t(ivl_nexus_t nex); ~blif_nex_data_t(); public: diff -Nru iverilog-10.3/tgt-blif/priv.h iverilog-11.0/tgt-blif/priv.h --- iverilog-10.3/tgt-blif/priv.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-blif/priv.h 2020-09-26 22:44:25.000000000 +0000 @@ -40,6 +40,8 @@ extern int print_lpm_mux(FILE*fd, ivl_lpm_t net); extern int print_lpm_part_vp(FILE*fd, ivl_lpm_t net); extern int print_lpm_re_logic(FILE*fd, ivl_lpm_t net); +extern int print_lpm_shift(FILE*fd, ivl_lpm_t net, bool left); +extern int print_lpm_sign_ext(FILE*fd, ivl_lpm_t net); /* * Emit all the constants for a model. This works by scanning the diff -Nru iverilog-10.3/tgt-fpga/Makefile.in iverilog-11.0/tgt-fpga/Makefile.in --- iverilog-10.3/tgt-fpga/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-fpga/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -61,7 +61,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -92,38 +93,38 @@ ps2pdf iverilog-fpga.ps iverilog-fpga.pdf ifeq (@WIN32@,yes) -INSTALL_DOC = $(prefix)/iverilog-fpga$(suffix).pdf $(mandir)/man1/iverilog-fpga$(suffix).1 +INSTALL_DOC = installpdf installman INSTALL_DOCDIR = $(mandir)/man1 all: iverilog-fpga.pdf else -INSTALL_DOC = $(mandir)/man1/iverilog-fpga$(suffix).1 +INSTALL_DOC = installman INSTALL_DOCDIR = $(mandir)/man1 endif -install: all installdirs $(libdir)/ivl$(suffix)/fpga.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/fpga.conf $(libdir)/ivl$(suffix)/fpga-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/fpga.tgt: ./fpga.tgt - $(INSTALL_PROGRAM) ./fpga.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.tgt" - -$(libdir)/ivl$(suffix)/fpga.conf: $(srcdir)/fpga.conf - $(INSTALL_DATA) $(srcdir)/fpga.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.conf" - -$(libdir)/ivl$(suffix)/fpga-s.conf: $(srcdir)/fpga-s.conf - $(INSTALL_DATA) $(srcdir)/fpga-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga-s.conf" +F = ./fpga.tgt \ + $(srcdir)/fpga.conf \ + $(srcdir)/fpga-s.conf \ + $(INSTALL_DOC) - -$(mandir)/man1/iverilog-fpga$(suffix).1: $(srcdir)/iverilog-fpga.man +installman: $(srcdir)/iverilog-fpga.man installdirs $(INSTALL_DATA) $(srcdir)/iverilog-fpga.man "$(DESTDIR)$(mandir)/man1/iverilog-fpga$(suffix).1" -$(prefix)/iverilog-fpga$(suffix).pdf: iverilog-fpga.pdf +installpdf: iverilog-fpga.pdf installdirs $(INSTALL_DATA) iverilog-fpga.pdf "$(DESTDIR)$(prefix)/iverilog-fpga$(suffix).pdf" +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./fpga.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.tgt" + $(INSTALL_DATA) $(srcdir)/fpga.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.conf" + $(INSTALL_DATA) $(srcdir)/fpga-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga-s.conf" + installdirs: $(srcdir)/../mkinstalldirs - $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" + $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" "$(DESTDIR)$(INSTALL_DOCDIR)" uninstall: rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.tgt" - rm -f "$(DESTDIR)$(INSTALL_DOC)" + rm -f "$(DESTDIR)$(prefix)/iverilog-fpga$(suffix).pdf" "$(DESTDIR)$(mandir)/man1/iverilog-fpga$(suffix).1" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga-s.conf" rm -f "$(DESTDIR)$(libdir)/ivl$(suffix)/fpga.conf" diff -Nru iverilog-10.3/tgt-null/Makefile.in iverilog-11.0/tgt-null/Makefile.in --- iverilog-10.3/tgt-null/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-null/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -56,7 +56,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -80,18 +81,17 @@ null.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -install: all installdirs $(libdir)/ivl$(suffix)/null.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/null.conf $(libdir)/ivl$(suffix)/null-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/null.tgt: ./null.tgt - $(INSTALL_PROGRAM) ./null.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/null.tgt" +F = ./null.tgt \ + $(srcdir)/null.conf \ + $(srcdir)/null-s.conf -$(libdir)/ivl$(suffix)/null.conf: $(srcdir)/null.conf +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./null.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/null.tgt" $(INSTALL_DATA) $(srcdir)/null.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/null.conf" - -$(libdir)/ivl$(suffix)/null-s.conf: $(srcdir)/null-s.conf $(INSTALL_DATA) $(srcdir)/null-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/null-s.conf" - installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-null/null.c iverilog-11.0/tgt-null/null.c --- iverilog-10.3/tgt-null/null.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-null/null.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 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 @@ -30,7 +30,7 @@ static const char*version_string = "Icarus Verilog NULL Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2000-2020 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" diff -Nru iverilog-10.3/tgt-pal/Makefile.in iverilog-11.0/tgt-pal/Makefile.in --- iverilog-10.3/tgt-pal/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-pal/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -55,7 +55,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -79,11 +80,12 @@ pal.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -lipal -install: all installdirs $(libdir)/ivl/pal.tgt +install: all installdirs installfiles -$(libdir)/ivl/pal.tgt: ./pal.tgt - $(INSTALL_PROGRAM) ./pal.tgt "$(DESTDIR)$(libdir)/ivl/pal.tgt" +F = ./pal.tgt +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./pal.tgt "$(DESTDIR)$(libdir)/ivl/pal.tgt" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)/$(libdir)/ivl" diff -Nru iverilog-10.3/tgt-pcb/fp.y iverilog-11.0/tgt-pcb/fp.y --- iverilog-10.3/tgt-pcb/fp.y 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-pcb/fp.y 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ -%pure-parser +%define api.pure %parse-param {const char*file_path} %{ diff -Nru iverilog-10.3/tgt-pcb/Makefile.in iverilog-11.0/tgt-pcb/Makefile.in --- iverilog-10.3/tgt-pcb/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-pcb/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -61,7 +61,8 @@ rm -f stamp-pcb_config-h pcb_config.h cppcheck: $(O:.o=.cc) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ @@ -87,11 +88,8 @@ fp_lex.cc: $(srcdir)/fp.lex $(LEX) -s -ofp_lex.cc $(srcdir)/fp.lex -fp.cc: $(srcdir)/fp.y - $(YACC) --verbose -t -p fp -d -o $@ $< -fp.h: fp.cc - mv fp.cc.h $@ 2>/dev/null || mv fp.hh $@ - touch $@ +fp%cc fp%h: $(srcdir)/fp%y + $(YACC) --verbose -t -p fp --defines=fp.h -o fp.cc $< ifeq (@WIN32@,yes) TGTLDFLAGS=-L.. -livl @@ -104,18 +102,17 @@ pcb.tgt: $O $(TGTDEPLIBS) $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -install: all installdirs $(libdir)/ivl$(suffix)/pcb.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/pcb.conf $(libdir)/ivl$(suffix)/pcb-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/pcb.tgt: ./pcb.tgt - $(INSTALL_PROGRAM) ./pcb.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb.tgt" +F = ./pcb.tgt \ + $(srcdir)/pcb.conf \ + $(srcdir)/pcb-s.conf -$(libdir)/ivl$(suffix)/pcb.conf: $(srcdir)/pcb.conf +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./pcb.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb.tgt" $(INSTALL_DATA) $(srcdir)/pcb.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb.conf" - -$(libdir)/ivl$(suffix)/pcb-s.conf: $(srcdir)/pcb-s.conf $(INSTALL_DATA) $(srcdir)/pcb-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/pcb-s.conf" - installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-pcb/pcb.cc iverilog-11.0/tgt-pcb/pcb.cc --- iverilog-10.3/tgt-pcb/pcb.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-pcb/pcb.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2020 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 @@ -32,7 +32,7 @@ static const char*version_string = "Icarus Verilog PCB Netlist Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2011 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2011-2020 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" diff -Nru iverilog-10.3/tgt-sizer/Makefile.in iverilog-11.0/tgt-sizer/Makefile.in --- iverilog-10.3/tgt-sizer/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-sizer/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -56,7 +56,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.cc) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -80,18 +81,17 @@ sizer.tgt: $O $(TGTDEPLIBS) $(CXX) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -install: all installdirs $(libdir)/ivl$(suffix)/sizer.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/sizer.conf $(libdir)/ivl$(suffix)/sizer-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/sizer.tgt: ./sizer.tgt - $(INSTALL_PROGRAM) ./sizer.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.tgt" +F = ./sizer.tgt \ + $(srcdir)/sizer.conf \ + $(srcdir)/sizer-s.conf -$(libdir)/ivl$(suffix)/sizer.conf: $(srcdir)/sizer.conf +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./sizer.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.tgt" $(INSTALL_DATA) $(srcdir)/sizer.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer.conf" - -$(libdir)/ivl$(suffix)/sizer-s.conf: $(srcdir)/sizer-s.conf $(INSTALL_DATA) $(srcdir)/sizer-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/sizer-s.conf" - installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-sizer/sizer.cc iverilog-11.0/tgt-sizer/sizer.cc --- iverilog-10.3/tgt-sizer/sizer.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-sizer/sizer.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014,2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2014-2020 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 @@ -34,7 +34,7 @@ static const char*version_string = "Icarus Verilog SIZER Statistics Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2014,2015 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2014-2020 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" diff -Nru iverilog-10.3/tgt-stub/Makefile.in iverilog-11.0/tgt-stub/Makefile.in --- iverilog-10.3/tgt-stub/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-stub/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -57,7 +57,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -81,17 +82,16 @@ stub.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -install: all installdirs $(libdir)/ivl$(suffix)/stub.tgt \ - $(libdir)/ivl$(suffix)/stub.conf $(libdir)/ivl$(suffix)/stub-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/stub.tgt: ./stub.tgt - $(INSTALL_PROGRAM) ./stub.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.tgt" - -$(libdir)/ivl$(suffix)/stub.conf: stub.conf - $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.conf" +F = ./stub.tgt \ + $(srcdir)/stub.conf \ + $(srcdir)/stub-s.conf -$(libdir)/ivl$(suffix)/stub-s.conf: stub-s.conf - $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/stub-s.conf" +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./stub.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.tgt" + $(INSTALL_DATA) $(srcdir)/stub.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/stub.conf" + $(INSTALL_DATA) $(srcdir)/stub-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/stub-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-stub/statement.c iverilog-11.0/tgt-stub/statement.c --- iverilog-10.3/tgt-stub/statement.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-stub/statement.c 2020-09-26 22:44:25.000000000 +0000 @@ -375,8 +375,24 @@ case IVL_ST_CASEZ: case IVL_ST_CASER: case IVL_ST_CASE: { + ivl_case_quality_t qual = ivl_stmt_case_quality(net); unsigned cnt = ivl_stmt_case_count(net); - fprintf(out, "%*scase (...) <%u cases>\n", ind, "", cnt); + const char*qual_txt = ""; + switch (qual) { + case IVL_CASE_QUALITY_BASIC: + qual_txt = "basic"; + break; + case IVL_CASE_QUALITY_UNIQUE: + qual_txt = "unique"; + break; + case IVL_CASE_QUALITY_UNIQUE0: + qual_txt = "unique0"; + break; + case IVL_CASE_QUALITY_PRIORITY: + qual_txt = "priority"; + break; + } + fprintf(out, "%*scase (...) <%u cases, %s>\n", ind, "", cnt, qual_txt); show_expression(ivl_stmt_cond_expr(net), ind+4); for (idx = 0 ; idx < cnt ; idx += 1) { diff -Nru iverilog-10.3/tgt-stub/stub.c iverilog-11.0/tgt-stub/stub.c --- iverilog-10.3/tgt-stub/stub.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-stub/stub.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 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 @@ -36,7 +36,7 @@ static const char*version_string = "Icarus Verilog STUB Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2000-2015 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2000-2020 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" @@ -394,6 +394,12 @@ case IVL_LPM_CMP_NEE: str = "NEE"; break; + case IVL_LPM_CMP_WEQ: + str = "WEQ"; + break; + case IVL_LPM_CMP_WNE: + str = "WNE"; + break; default: assert(0); break; @@ -559,6 +565,39 @@ } +static void show_lpm_latch(ivl_lpm_t net) +{ + ivl_nexus_t nex; + unsigned width = ivl_lpm_width(net); + + fprintf(out, " LPM_LATCH %s: \n", + ivl_lpm_basename(net), width); + + nex = ivl_lpm_enable(net); + fprintf(out, " E: %p\n", nex); + if (width_of_nexus(nex) != 1) { + fprintf(out, " E: ERROR: Nexus width is %u\n", + width_of_nexus(nex)); + stub_errors += 1; + } + + nex = ivl_lpm_data(net,0); + fprintf(out, " D: %p\n", nex); + if (width_of_nexus(nex) != width) { + fprintf(out, " D: ERROR: Nexus width is %u\n", + width_of_nexus(nex)); + stub_errors += 1; + } + + nex = ivl_lpm_q(net); + fprintf(out, " Q: %p\n", nex); + if (width_of_nexus(nex) != width) { + fprintf(out, " Q: ERROR: Nexus width is %u\n", + width_of_nexus(nex)); + stub_errors += 1; + } +} + static void show_lpm_mod(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -1010,6 +1049,8 @@ case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: show_lpm_cmp_eeq(net); break; @@ -1017,6 +1058,10 @@ show_lpm_ff(net); break; + case IVL_LPM_LATCH: + show_lpm_latch(net); + break; + case IVL_LPM_CMP_GE: show_lpm_cmp_ge(net); break; @@ -1121,6 +1166,24 @@ else fprintf(out, "always\n"); break; + case IVL_PR_ALWAYS_COMB: + if (ivl_process_analog(net)) + assert(0); + else + fprintf(out, "always_comb\n"); + break; + case IVL_PR_ALWAYS_FF: + if (ivl_process_analog(net)) + assert(0); + else + fprintf(out, "always_ff\n"); + break; + case IVL_PR_ALWAYS_LATCH: + if (ivl_process_analog(net)) + assert(0); + else + fprintf(out, "always_latch\n"); + break; case IVL_PR_FINAL: if (ivl_process_analog(net)) fprintf(out, "analog final\n"); diff -Nru iverilog-10.3/tgt-verilog/Makefile.in iverilog-11.0/tgt-verilog/Makefile.in --- iverilog-10.3/tgt-verilog/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-verilog/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -55,7 +55,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -79,12 +80,14 @@ verilog.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O $(TGTLDFLAGS) -install: all installdirs $(libdir)/ivl/verilog.tgt \ - $(includedir)/vpi_user.h +install: all installdirs installfiles -$(libdir)/ivl/verilog.tgt: ./verilog.tgt - $(INSTALL_PROGRAM) ./verilog.tgt "$(DESTDIR)$(libdir)/ivl/verilog.tgt" +F = ./verilog.tgt \ + $(srcdir)/vpi_user.h +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./verilog.tgt "$(DESTDIR)$(libdir)/ivl/verilog.tgt" + $(INSTALL_DATA) $(srcdir)/vpi_user.h "$(DESTDIR)$(includedir)/vpi_user.h" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl" diff -Nru iverilog-10.3/tgt-verilog/verilog.c iverilog-11.0/tgt-verilog/verilog.c --- iverilog-10.3/tgt-verilog/verilog.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-verilog/verilog.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2017 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 @@ -393,6 +393,9 @@ fprintf(out, " initial\n"); break; case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: fprintf(out, " always\n"); break; case IVL_PR_FINAL: diff -Nru iverilog-10.3/tgt-vhdl/Makefile.in iverilog-11.0/tgt-vhdl/Makefile.in --- iverilog-10.3/tgt-vhdl/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vhdl/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -60,7 +60,8 @@ rm -f stamp-vhdl_config-h vhdl_config.h cppcheck: $(O:.o=.cc) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -89,17 +90,16 @@ cd ..; ./config.status --header=tgt-vhdl/vhdl_config.h vhdl_config.h: stamp-vhdl_config-h -install: all installdirs $(libdir)/ivl$(suffix)/vhdl.tgt $(libdir)/ivl$(suffix)/vhdl.conf \ - $(libdir)/ivl$(suffix)/vhdl-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/vhdl.tgt: ./vhdl.tgt - $(INSTALL_PROGRAM) ./vhdl.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.tgt" - -$(libdir)/ivl$(suffix)/vhdl.conf: vhdl.conf - $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.conf" +F = ./vhdl.tgt \ + $(srcdir)/vhdl.conf \ + $(srcdir)/vhdl-s.conf -$(libdir)/ivl$(suffix)/vhdl-s.conf: vhdl-s.conf - $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl-s.conf" +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./vhdl.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.tgt" + $(INSTALL_DATA) $(srcdir)/vhdl.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl.conf" + $(INSTALL_DATA) $(srcdir)/vhdl-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdl-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-vhdl/state.cc iverilog-11.0/tgt-vhdl/state.cc --- iverilog-10.3/tgt-vhdl/state.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vhdl/state.cc 2020-09-26 22:44:25.000000000 +0000 @@ -144,7 +144,7 @@ // Compare the name of an entity against a string struct cmp_ent_name { - cmp_ent_name(const string& n) : name_(n) {} + explicit cmp_ent_name(const string& n) : name_(n) {} bool operator()(const vhdl_entity* ent) const { diff -Nru iverilog-10.3/tgt-vhdl/support.hh iverilog-11.0/tgt-vhdl/support.hh --- iverilog-10.3/tgt-vhdl/support.hh 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vhdl/support.hh 2020-09-26 22:44:25.000000000 +0000 @@ -41,7 +41,7 @@ class support_function : public vhdl_function { public: - support_function(support_function_t type) + explicit support_function(support_function_t type) : vhdl_function(function_name(type), function_type(type)), type_(type) {} void emit(std::ostream &of, int level) const; diff -Nru iverilog-10.3/tgt-vhdl/vhdl.cc iverilog-11.0/tgt-vhdl/vhdl.cc --- iverilog-10.3/tgt-vhdl/vhdl.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vhdl/vhdl.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ /* * VHDL code generator for Icarus Verilog. * - * Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk) + * Copyright (C) 2008-2020 Nick Gasson (nick@nickg.me.uk) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ static const char*version_string = "Icarus Verilog VHDL Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (C) 2008 Nick Gasson (nick@nickg.me.uk)\n\n" +"Copyright (C) 2008-2020 Nick Gasson (nick@nickg.me.uk)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" diff -Nru iverilog-10.3/tgt-vhdl/vhdl_syntax.hh iverilog-11.0/tgt-vhdl/vhdl_syntax.hh --- iverilog-10.3/tgt-vhdl/vhdl_syntax.hh 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vhdl/vhdl_syntax.hh 2020-09-26 22:44:25.000000000 +0000 @@ -178,7 +178,7 @@ class vhdl_const_string : public vhdl_expr { public: - vhdl_const_string(const string& value) + explicit vhdl_const_string(const string& value) : vhdl_expr(vhdl_type::string(), true), value_(value) {} void emit(std::ostream &of, int level) const; @@ -207,7 +207,7 @@ class vhdl_const_bit : public vhdl_expr { public: - vhdl_const_bit(char bit) + explicit vhdl_const_bit(char bit) : vhdl_expr(vhdl_type::std_logic(), true), bit_(bit) {} void emit(std::ostream &of, int level) const; vhdl_expr *to_boolean(); @@ -237,7 +237,7 @@ class vhdl_const_int : public vhdl_expr { public: - vhdl_const_int(int64_t value) + explicit vhdl_const_int(int64_t value) : vhdl_expr(vhdl_type::integer(), true), value_(value) {} void emit(std::ostream &of, int level) const; vhdl_expr *to_vector(vhdl_type_name_t name, int w); @@ -247,7 +247,7 @@ class vhdl_const_bool : public vhdl_expr { public: - vhdl_const_bool(bool value) + explicit vhdl_const_bool(bool value) : vhdl_expr(vhdl_type::boolean(), true), value_(value) {} void emit(std::ostream &of, int level) const; private: @@ -478,7 +478,7 @@ class vhdl_assert_stmt : public vhdl_report_stmt { public: - vhdl_assert_stmt(const char *reason); + explicit vhdl_assert_stmt(const char *reason); void emit(ostream &of, int level) const; }; @@ -486,7 +486,7 @@ class vhdl_if_stmt : public vhdl_seq_stmt { public: - vhdl_if_stmt(vhdl_expr *test); + explicit vhdl_if_stmt(vhdl_expr *test); ~vhdl_if_stmt(); stmt_container *get_then_container() { return &then_part_; } @@ -513,7 +513,7 @@ class vhdl_case_branch : public vhdl_element { friend class vhdl_case_stmt; public: - vhdl_case_branch(vhdl_expr *when) : when_(when) {} + explicit vhdl_case_branch(vhdl_expr *when) : when_(when) {} ~vhdl_case_branch(); stmt_container *get_container() { return &stmts_; } @@ -527,7 +527,7 @@ class vhdl_case_stmt : public vhdl_seq_stmt { public: - vhdl_case_stmt(vhdl_expr *test) : test_(test) {} + explicit vhdl_case_stmt(vhdl_expr *test) : test_(test) {} ~vhdl_case_stmt(); void add_branch(vhdl_case_branch *b) { branches_.push_back(b); } @@ -554,7 +554,7 @@ class vhdl_while_stmt : public vhdl_loop_stmt { public: - vhdl_while_stmt(vhdl_expr *test) : test_(test) {} + explicit vhdl_while_stmt(vhdl_expr *test) : test_(test) {} ~vhdl_while_stmt(); void emit(std::ostream &of, int level) const; @@ -584,7 +584,7 @@ */ class vhdl_pcall_stmt : public vhdl_seq_stmt { public: - vhdl_pcall_stmt(const char *name) : name_(name) {} + explicit vhdl_pcall_stmt(const char *name) : name_(name) {} void emit(std::ostream &of, int level) const; void add_expr(vhdl_expr *e) { exprs_.add_expr(e); } @@ -658,7 +658,7 @@ void emit(std::ostream &of, int level) const; private: - vhdl_component_decl(const char *name); + explicit vhdl_component_decl(const char *name); decl_list_t ports_; }; @@ -852,7 +852,7 @@ class vhdl_forward_fdecl : public vhdl_decl { public: - vhdl_forward_fdecl(const vhdl_function *f) + explicit vhdl_forward_fdecl(const vhdl_function *f) : vhdl_decl((f->get_name() + "_Forward").c_str()), f_(f) {} void emit(std::ostream &of, int level) const; @@ -863,7 +863,7 @@ class vhdl_process : public vhdl_conc_stmt, public vhdl_procedural { public: - vhdl_process(const char *name = "") : name_(name) {} + explicit vhdl_process(const char *name = "") : name_(name) {} void emit(std::ostream &of, int level) const; void add_sensitivity(const std::string &name); diff -Nru iverilog-10.3/tgt-vlog95/event.c iverilog-11.0/tgt-vlog95/event.c --- iverilog-10.3/tgt-vlog95/event.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/event.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2013 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2019 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,24 @@ # include "config.h" # include "vlog95_priv.h" +static unsigned need_ivl_top_module = 0; + +void emit_icarus_generated_top_module() +{ + if (need_ivl_top_module) { + fprintf(vlog_out, +"\n" +"/*\n" +" * This module is used to trigger any always_comb or always_latch processes\n" +" * at time zero to make sure all the outputs have the correct values.\n" +"*/\n" +"module IVL_top_priv_module;\n" +" event IVL_T0_trigger_event;\n" +" initial #0 -> IVL_T0_trigger_event;\n" +"endmodule /* IVL_top_priv_module */\n"); + } +} + void emit_event(ivl_scope_t scope, ivl_statement_t stmt) { unsigned eidx, nevents, first = 1; @@ -68,4 +86,14 @@ emit_id(ivl_event_basename(event)); } } + + /* If this is an always_comb/latch then we need to add a trigger to + * get the correct functionality. */ + if (ivl_stmt_needs_t0_trigger(stmt)) { + if (! first) { + fprintf(vlog_out, " or "); + } + fprintf(vlog_out, "IVL_top_priv_module.IVL_T0_trigger_event"); + need_ivl_top_module = 1; + }; } diff -Nru iverilog-10.3/tgt-vlog95/expr.c iverilog-11.0/tgt-vlog95/expr.c --- iverilog-10.3/tgt-vlog95/expr.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/expr.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2018 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2020 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -44,8 +44,10 @@ switch (ivl_expr_opcode(expr)) { case 'E': case 'e': + case 'w': case 'N': case 'n': + case 'W': case '<': case 'L': case '>': @@ -136,6 +138,8 @@ case 'X': /* The reduction operators always act as if the argument is * unsigned. */ + case '!': + /* The logical negation operator works on either type. */ break; case 'r': /* For a cast to real the expression should be signed and no @@ -417,8 +421,10 @@ case 'p': oper = "**"; break; case 'E': oper = "==="; break; case 'e': oper = "=="; break; + case 'w': oper = "==?"; break; case 'N': oper = "!=="; break; case 'n': oper = "!="; break; + case 'W': oper = "!=?"; break; case '<': oper = "<"; break; case 'L': oper = "<="; break; case '>': oper = ">"; break; @@ -466,6 +472,14 @@ fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, wid, 0, can_skip_unsigned, is_full_prec); break; + case 'w': + case 'W': + fprintf(stderr, "%s:%u: vlog95 error: The wild equality operators " + "cannot be converted.\n", + ivl_expr_file(expr), + ivl_expr_lineno(expr)); + vlog_errors += 1; + // fallthrough case 'E': case 'e': case 'N': @@ -486,6 +500,17 @@ fprintf(vlog_out, " %s ", oper); emit_expr(scope, oper2, ivl_expr_width(oper2), 0, 1, 0); break; + case 'q': // The arguments have already been reduced + fprintf(vlog_out, "~"); + emit_expr(scope, oper1, ivl_expr_width(oper1), 0, 1, 0); + fprintf(vlog_out, " | "); + emit_expr(scope, oper2, ivl_expr_width(oper2), 0, 1, 0); + break; + case 'Q': // The arguments have already been reduced + emit_expr(scope, oper1, ivl_expr_width(oper1), 0, 1, 0); + fprintf(vlog_out, " ~^ "); + emit_expr(scope, oper2, ivl_expr_width(oper2), 0, 1, 0); + break; case 'R': if (! allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: >>> operator is not " diff -Nru iverilog-10.3/tgt-vlog95/logic_lpm.c iverilog-11.0/tgt-vlog95/logic_lpm.c --- iverilog-10.3/tgt-vlog95/logic_lpm.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/logic_lpm.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2018 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2020 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,8 @@ } lpm_sign_t; static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, - int*base, unsigned*array_word); + int*msb, int*lsb, unsigned*array_word, + ivl_net_const_t*accept_const); /* * Look to see if the nexus driver is signed. @@ -68,12 +69,12 @@ * local signal to get the sign information. */ if (ivl_logic_type(t_nlogic) == IVL_LO_BUFZ) { unsigned array_word = 0; - int base = 0; + int msb = 0, lsb = 0; ivl_signal_t sig; assert(ivl_logic_pins(t_nlogic) == 2); sig = nexus_is_signal(scope, ivl_logic_pin(t_nlogic, 0), - &base, &array_word); + &msb, &lsb, &array_word, 0); assert(sig); is_signed = ivl_signal_signed(sig); } @@ -88,6 +89,19 @@ return is_signed; } +static unsigned get_nexus_width(ivl_nexus_t nex) +{ + unsigned idx, count = ivl_nexus_ptrs(nex); + + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); + if (sig) return ivl_signal_width(sig); + } + assert(0); + return 0; +} + static lpm_sign_t lpm_get_sign_type(ivl_lpm_t lpm, unsigned can_skip_unsigned) { @@ -196,6 +210,14 @@ "gate", ivl_logic_file(nlogic), ivl_logic_lineno(nlogic)); } +static void emit_part_selector(int msb, int lsb) +{ + if (msb != lsb) + fprintf(vlog_out, "[%d:%d]", msb, lsb); + else + fprintf(vlog_out, "[%d]", lsb); +} + /* * Look for a single driver behind an LPM that passes strength information * and get the real drive information from it. @@ -285,6 +307,44 @@ fprintf(vlog_out, ")"); } +static void emit_driver_delay(ivl_scope_t scope, ivl_nexus_t nex) +{ + ivl_expr_t rise = 0; + ivl_expr_t fall = 0; + ivl_expr_t decay = 0; + unsigned idx, count = ivl_nexus_ptrs(nex); + + for (idx = 0; idx < count; idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + ivl_lpm_t t_lpm = ivl_nexus_ptr_lpm(nex_ptr); + ivl_net_const_t t_net_const = ivl_nexus_ptr_con(nex_ptr); + ivl_net_logic_t t_nlogic = ivl_nexus_ptr_log(nex_ptr); + ivl_drive_t cur_drive1 = ivl_nexus_ptr_drive1(nex_ptr); + ivl_drive_t cur_drive0 = ivl_nexus_ptr_drive0(nex_ptr); + if ((cur_drive1 == IVL_DR_HiZ) && + (cur_drive0 == IVL_DR_HiZ)) continue; + /* Only one driver can set the delay. */ + assert( ! rise); + if (t_lpm) { + rise = ivl_lpm_delay(t_lpm, 0); + fall = ivl_lpm_delay(t_lpm, 1); + decay = ivl_lpm_delay(t_lpm, 2); + } + if (t_net_const) { + rise = ivl_const_delay(t_net_const, 0); + fall = ivl_const_delay(t_net_const, 1); + decay = ivl_const_delay(t_net_const, 2); + } + if (t_nlogic) { + rise = ivl_logic_delay(t_nlogic, 0); + fall = ivl_logic_delay(t_nlogic, 1); + decay = ivl_logic_delay(t_nlogic, 2); + } + } + + emit_delay(scope, rise, fall, decay, 3); +} + static unsigned is_local_nexus(ivl_scope_t scope, ivl_nexus_t nex) { unsigned idx, count = ivl_nexus_ptrs(nex); @@ -294,16 +354,17 @@ ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); ivl_signal_t sig = ivl_nexus_ptr_sig(nex_ptr); if (! sig) continue; - /* Check to see if there is an output port driving into - * the local scope. */ + /* Check to see if there is an output or inout port + * driving into the local scope. */ if ((scope == ivl_scope_parent(ivl_signal_scope(sig))) && - (ivl_signal_port(sig) == IVL_SIP_OUTPUT)) { + ((ivl_signal_port(sig) == IVL_SIP_OUTPUT) || + (ivl_signal_port(sig) == IVL_SIP_INOUT))) { has_output_driver = 1; continue; } if (scope != ivl_signal_scope(sig)) continue; if ((ivl_nexus_ptr_drive1(nex_ptr) != IVL_DR_HiZ) || - (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) continue; + (ivl_nexus_ptr_drive0(nex_ptr) != IVL_DR_HiZ)) continue; if (ivl_signal_local(sig)) { is_local = 1; } else { @@ -311,9 +372,10 @@ break; } } - /* We return is_local=true only if there is not an output driving - * into this scope. This is needed since some module outputs are - * combined with a concatenation. */ + /* We return is_local=true only if there is not an output or inout + * driving into this scope. This is needed since some module outputs + * are combined with a concatenation and some inouts are connected + * with a tran_VP. */ return is_local && !has_output_driver; } @@ -338,10 +400,13 @@ case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_CONCAT: case IVL_LPM_CONCATZ: case IVL_LPM_DIVIDE: case IVL_LPM_FF: + case IVL_LPM_LATCH: case IVL_LPM_MOD: case IVL_LPM_MULT: case IVL_LPM_MUX: @@ -585,7 +650,8 @@ } else if (sig) { // HERE: should these be allow_UD? if (must_be_sig) { - emit_nexus_as_ca(scope, ivl_signal_nex(sig, word), + emit_nexus_as_ca(ivl_signal_scope(sig), + ivl_signal_nex(sig, word), 0, 0); } else emit_name_of_nexus(scope, nex, 0); // HERE: The assert causes pr1703959 to fail. @@ -628,6 +694,22 @@ assert(inputs == 1); emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); break; + case IVL_LO_EQUIV: + assert(inputs == 2); + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); + fprintf(vlog_out, " ~^ "); + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); + fprintf(vlog_out, ")"); + break; + case IVL_LO_IMPL: + assert(inputs == 2); + fprintf(vlog_out, "(~"); + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 1), 0, 0); + fprintf(vlog_out, " | "); + emit_nexus_as_ca(scope, ivl_logic_pin(nlogic, 2), 0, 0); + fprintf(vlog_out, ")"); + break; case IVL_LO_NAND: assert(inputs == 2); fprintf(vlog_out, "~("); @@ -758,7 +840,8 @@ } static ivl_signal_t nexus_is_signal(ivl_scope_t scope, ivl_nexus_t nex, - int*base, unsigned*array_word) + int*msb, int*lsb, unsigned*array_word, + ivl_net_const_t*accept_const) { unsigned idx, count = ivl_nexus_ptrs(nex); ivl_lpm_t lpm = 0; @@ -767,10 +850,16 @@ ivl_signal_t sig; /* Look for a signal in the local scope first. */ sig = find_local_signal(scope, nex, array_word); - if (sig) return sig; + if (sig) { + get_sig_msb_lsb(sig, msb, lsb); + return sig; + } /* Now look for an output signal driving into the local scope. */ sig = find_output_signal(scope, nex, array_word); - if (sig) return sig; + if (sig) { + get_sig_msb_lsb(sig, msb, lsb); + return sig; + } /* Now scan the nexus looking for a driver. */ for (idx = 0; idx < count; idx += 1) { ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); @@ -785,11 +874,18 @@ /* The real signal could be hidden behind a select. */ if (ivl_lpm_type(t_lpm) == IVL_LPM_PART_VP) { t_sig = nexus_is_signal(scope, ivl_lpm_data(t_lpm, 0), - base, array_word); + msb, lsb, array_word, 0); } - if (t_sig) *base += ivl_lpm_base(t_lpm); - else lpm = t_lpm; + if (t_sig) { + if (*msb >= *lsb) { + *lsb += ivl_lpm_base(t_lpm); + *msb = *lsb + (int)ivl_lpm_width(t_lpm) - 1; + } else { + *lsb -= ivl_lpm_base(t_lpm); + *msb = *lsb - (int)ivl_lpm_width(t_lpm) + 1; + } + } else lpm = t_lpm; } if (t_net_const) { assert(! net_const); @@ -802,7 +898,7 @@ assert(ivl_logic_pins(t_nlogic) == 2); t_sig = nexus_is_signal(scope, ivl_logic_pin(t_nlogic, 1), - base, array_word); + msb, lsb, array_word, 0); } else nlogic = t_nlogic; } if (t_sig) { @@ -812,7 +908,14 @@ *array_word = ivl_nexus_ptr_pin(nex_ptr); } } - return sig; + if (sig) return sig; + + if (accept_const && net_const) { + *lsb = 0; + *msb = ivl_const_width(net_const) - 1; + *accept_const = net_const; + } + return 0; } static void emit_lpm_part_select(ivl_scope_t scope, ivl_lpm_t lpm, @@ -821,9 +924,9 @@ unsigned width = ivl_lpm_width(lpm); unsigned array_word = 0; int base = ivl_lpm_base(lpm); - int msb, lsb; + int msb = 0, lsb = 0; ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0), - &base, &array_word); + &msb, &lsb, &array_word, 0); if (sign_extend && !allow_signed) { fprintf(stderr, "%s:%u: vlog95 error: >>> operator is not " @@ -854,9 +957,9 @@ fprintf(vlog_out, "[%d]", array_idx); } - get_sig_msb_lsb(sig, &msb, &lsb); if (sign_extend) { assert(base != lsb); +// HERE: This looks wrong. if (msb >= lsb) base += lsb; else base = lsb - base; fprintf(vlog_out, " >>> %d)", base); @@ -880,7 +983,7 @@ } } else { /* Skip a select of the entire bit. */ - if ((msb == lsb) && (lsb == base)) return; + if ((msb == lsb) && (base == 0)) return; fprintf(vlog_out, "["); if (msb >= lsb) base += lsb; else base = lsb - base; @@ -926,6 +1029,120 @@ } } +static void emit_mux_select_bit(ivl_scope_t scope, ivl_lpm_t lpm, unsigned bit) +{ + unsigned array_word = 0; + int msb = 0, lsb = 0; + + ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_select(lpm), + &msb, &lsb, &array_word, 0); + assert(sig); + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int array_idx = (int) array_word + ivl_signal_array_base(sig); + fprintf(vlog_out, "[%d]", array_idx); + } + if (msb >= lsb) { + fprintf(vlog_out, "[%d]", lsb + (int)bit); + } else { + fprintf(vlog_out, "[%d]", lsb - (int)bit); + } +} + +static void emit_lpm_mux(ivl_scope_t scope, ivl_lpm_t lpm, + unsigned sel_bit, unsigned offset) +{ + assert(sel_bit < sizeof(unsigned)*8); + fprintf(vlog_out, "("); + emit_mux_select_bit(scope, lpm, sel_bit); + fprintf(vlog_out, " ? "); + if (sel_bit > 0) { + emit_lpm_mux(scope, lpm, sel_bit - 1, offset + (1U << sel_bit)); + fprintf(vlog_out, " : "); + emit_lpm_mux(scope, lpm, sel_bit - 1, offset); + } else { + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, offset + 1), 0, 0); + fprintf(vlog_out, " : "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, offset + 0), 0, 0); + } + fprintf(vlog_out, ")"); +} + +static void emit_lpm_substitute(ivl_scope_t scope, ivl_lpm_t lpm) +{ + unsigned array_word = 0; + int msb = 0, lsb = 0; + unsigned base = ivl_lpm_base(lpm); + unsigned width; + int psb; + int wid; + + /* Find the wider signal. Accept a constant if there's no signal. */ + ivl_net_const_t net_const = 0; + ivl_signal_t sig = nexus_is_signal(scope, ivl_lpm_data(lpm, 0), + &msb, &lsb, &array_word, + &net_const); + assert(sig || net_const); + + // Get the width of the part being substituted. + width = get_nexus_width(ivl_lpm_data(lpm, 1)); + + fprintf(vlog_out, "{"); + + if (msb >= lsb) { + psb = lsb + base + width; wid = 1 + msb - psb; + } else { + psb = lsb - base - width; wid = 1 + psb - msb; + } + if (wid > 0) { + if (sig) { + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int array_idx = (int) array_word + ivl_signal_array_base(sig); + fprintf(vlog_out, "[%d]", array_idx); + } + emit_part_selector(msb, psb); + } else { + assert (msb >= psb); + emit_number(ivl_const_bits(net_const) + psb, wid, 0, + ivl_const_file(net_const), + ivl_const_lineno(net_const)); + } + + fprintf(vlog_out, ", "); + } + + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + + if (msb >= lsb) { + psb = lsb + base - 1; wid = 1 + psb - lsb; + } else { + psb = lsb - base + 1; wid = 1 + lsb - psb; + } + if (wid > 0) { + fprintf(vlog_out, ", "); + + if (sig) { + emit_scope_call_path(scope, ivl_signal_scope(sig)); + emit_id(ivl_signal_basename(sig)); + if (ivl_signal_dimensions(sig)) { + int array_idx = (int) array_word + ivl_signal_array_base(sig); + fprintf(vlog_out, "[%d]", array_idx); + } + emit_part_selector(psb, lsb); + } else { + assert (psb >= lsb); + emit_number(ivl_const_bits(net_const), wid, 0, + ivl_const_file(net_const), + ivl_const_lineno(net_const)); + } + } + + fprintf(vlog_out, "}"); +} + static void emit_lpm_as_ca(ivl_scope_t scope, ivl_lpm_t lpm, unsigned sign_extend) { @@ -980,7 +1197,7 @@ case IVL_LPM_CAST_INT: case IVL_LPM_CAST_INT2: case IVL_LPM_CAST_REAL: - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0); break; case IVL_LPM_CMP_EEQ: fprintf(vlog_out, "("); @@ -997,19 +1214,19 @@ fprintf(vlog_out, ")"); break; case IVL_LPM_CMP_EQX: -// HERE: Need to heck that this is not a real nexus. +// HERE: Need to check that this is not a real nexus. fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " ==? "); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); - fprintf(stderr, "%s:%u: vlog95 error: Compare wildcard equal " + fprintf(stderr, "%s:%u: vlog95 error: Compare wildcard equal (caseX) " "operator is not supported.\n", ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); vlog_errors += 1; break; case IVL_LPM_CMP_EQZ: -// HERE: Need to heck that this is not a real nexus. +// HERE: Need to check that this is not a real nexus. fprintf(vlog_out, "("); emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); fprintf(vlog_out, " == "); @@ -1048,6 +1265,28 @@ emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); fprintf(vlog_out, ")"); break; + case IVL_LPM_CMP_WEQ: + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + fprintf(vlog_out, " ==? "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + fprintf(vlog_out, ")"); + fprintf(stderr, "%s:%u: vlog95 error: Wild equality " + "operator is not supported.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + break; + case IVL_LPM_CMP_WNE: + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + fprintf(vlog_out, " !=? "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + fprintf(vlog_out, ")"); + fprintf(stderr, "%s:%u: vlog95 error: Wild inequality " + "operator is not supported.\n", + ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); + vlog_errors += 1; + break; /* A concat-Z should never be generated, but report it as an * error if one is generated. */ case IVL_LPM_CONCATZ: @@ -1088,13 +1327,17 @@ fprintf(vlog_out, ")"); break; case IVL_LPM_MUX: - fprintf(vlog_out, "("); - emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0); - fprintf(vlog_out, " ? "); - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); - fprintf(vlog_out, " : "); - emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); - fprintf(vlog_out, ")"); + if (ivl_lpm_selects(lpm) > 1) { + emit_lpm_mux(scope, lpm, ivl_lpm_selects(lpm) - 1, 0); + } else { + fprintf(vlog_out, "("); + emit_nexus_as_ca(scope, ivl_lpm_select(lpm), 0, 0); + fprintf(vlog_out, " ? "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 1), 0, 0); + fprintf(vlog_out, " : "); + emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 0, 0); + fprintf(vlog_out, ")"); + } break; case IVL_LPM_PART_PV: emit_nexus_as_ca(scope, ivl_lpm_data(lpm, 0), 1, 0); @@ -1189,11 +1432,7 @@ fprintf(vlog_out, ")"); break; case IVL_LPM_SUBSTITUTE: - fprintf(vlog_out, ""); - fprintf(stderr, "%s:%u: vlog95 sorry: Substitute LPMs are " - "not currently translated.\n", - ivl_lpm_file(lpm), ivl_lpm_lineno(lpm)); - vlog_errors += 1; + emit_lpm_substitute(scope, lpm); break; case IVL_LPM_UFUNC: emit_scope_path(scope, ivl_lpm_define(lpm)); @@ -1281,8 +1520,27 @@ fprintf(vlog_out, "endprimitive\n"); } +static void emit_latch_prim(void) +{ + fprintf(vlog_out, "\n"); + fprintf(vlog_out, "/* Icarus generated UDP to represent a synthesized " + "LATCH. */\n"); + fprintf(vlog_out, "primitive IVL_LATCH " + "(q, en, d);\n"); + fprintf(vlog_out, "%*coutput q;\n", indent_incr, ' '); + fprintf(vlog_out, "%*cinput en, d;\n", indent_incr, ' '); + fprintf(vlog_out, "%*creg q;\n", indent_incr, ' '); + fprintf(vlog_out, "%*ctable\n", indent_incr, ' '); + fprintf(vlog_out, "%*c 1 0 : ? : 0 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c 1 1 : ? : 1 ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*c 0 ? : ? : - ;\n", 2*indent_incr, ' '); + fprintf(vlog_out, "%*cendtable\n", indent_incr, ' '); + fprintf(vlog_out, "endprimitive\n"); +} + static unsigned need_posedge_dff_prim = 0; static unsigned need_negedge_dff_prim = 0; +static unsigned need_latch_prim = 0; /* * Synthesis creates a D-FF LPM object. To allow this to be simulated as @@ -1300,14 +1558,14 @@ { /* Emit the copyright information and LGPL note and then emit any * needed primitives. */ - if (need_posedge_dff_prim || need_negedge_dff_prim) { + if (need_posedge_dff_prim || need_negedge_dff_prim || need_latch_prim) { fprintf(vlog_out, "\n" "/*\n" " * This is the copyright information for the following primitive(s)\n" " * (library elements).\n" " *\n" -" * Copyright (C) 2011-2015 Cary R. (cygcary@yahoo.com)\n" +" * Copyright (C) 2011-2016 Cary R. (cygcary@yahoo.com)\n" " *\n" " * This library is free software; you can redistribute it and/or\n" " * modify it under the terms of the GNU Lesser General Public\n" @@ -1328,6 +1586,7 @@ } if (need_posedge_dff_prim) emit_posedge_dff_prim(); if (need_negedge_dff_prim) emit_negedge_dff_prim(); + if (need_latch_prim) emit_latch_prim(); } static void emit_lpm_ff(ivl_scope_t scope, ivl_lpm_t lpm) @@ -1429,7 +1688,7 @@ emitted = 1; } nex = ivl_lpm_async_set(lpm); - if (aset_bits && (aset_bits[0] != '0')) nex = 0; + if (!aset_bits || (aset_bits[0] != '0')) nex = 0; if (nex) { if (emitted) fprintf(vlog_out, " | "); emit_nexus_as_ca(scope, nex, 0, 0); @@ -1450,6 +1709,41 @@ need_posedge_dff_prim = 1; } +static void emit_lpm_latch(ivl_scope_t scope, ivl_lpm_t lpm) +{ + ivl_nexus_t nex; + unsigned emitted; + + fprintf(vlog_out, "%*c", indent, ' '); + fprintf(vlog_out, "IVL_LATCH"); + emit_lpm_strength(lpm); + /* The lpm LATCH does not support any delays. */ + /* The LATCH name is a temporary so we don't bother to print it unless + * we have a range. Then we need to use a made up name. */ + if (ivl_lpm_width(lpm) > 1) { + fprintf(vlog_out, " synth_%p [%u:0]", lpm, ivl_lpm_width(lpm)-1U); + } + fprintf(vlog_out, " ("); + /* Emit the q pin. */ + emit_name_of_nexus(scope, ivl_lpm_q(lpm), 0); + fprintf(vlog_out, ", "); + /* Emit the enable pin expression(s) if needed. */ + emitted = 0; + nex = ivl_lpm_enable(lpm); + if (nex) { + emit_nexus_as_ca(scope, nex, 0, 0); + emitted = 1; + } + if (!emitted) fprintf(vlog_out, "1'b1"); + fprintf(vlog_out, ", "); + /* Emit the data pin expression(s). */ + nex = ivl_lpm_data(lpm, 0); + assert (nex); + emit_nexus_as_ca(scope, nex, 0, 0); + fprintf(vlog_out, ");\n"); + need_latch_prim = 1; +} + static ivl_signal_t get_output_from_nexus(ivl_scope_t scope, ivl_nexus_t nex, int64_t*array_idx) { @@ -1573,19 +1867,28 @@ emit_lpm_ff(scope, lpm); return; } + if (type == IVL_LPM_LATCH) { + emit_lpm_latch(scope, lpm); + return; + } // HERE: Look for a select passed to a pull device (pr2019553). /* Skip assignments to a module instantiation input. */ if (output_is_module_instantiation_input(scope, output)) return; fprintf(vlog_out, "%*cassign", indent, ' '); emit_lpm_strength(lpm); - emit_delay(scope, - ivl_lpm_delay(lpm, 0), - ivl_lpm_delay(lpm, 1), - ivl_lpm_delay(lpm, 2), - 3); - fprintf(vlog_out, " "); - if (type == IVL_LPM_PART_PV) emit_lpm_part_pv(scope, lpm); - else emit_name_of_nexus(scope, output, 0); + if (type == IVL_LPM_PART_PV) { + emit_driver_delay(scope, ivl_lpm_data(lpm, 0)); + fprintf(vlog_out, " "); + emit_lpm_part_pv(scope, lpm); + } else { + emit_delay(scope, + ivl_lpm_delay(lpm, 0), + ivl_lpm_delay(lpm, 1), + ivl_lpm_delay(lpm, 2), + 3); + fprintf(vlog_out, " "); + emit_name_of_nexus(scope, output, 0); + } fprintf(vlog_out, " = "); emit_lpm_as_ca(scope, lpm, 0); fprintf(vlog_out, ";"); @@ -1707,7 +2010,8 @@ fprintf(vlog_out, "bufif1"); dly_count = 3; break; -// case IVL_LO_BUFT: + case IVL_LO_BUFT: + return; case IVL_LO_BUFZ: emit_bufz(scope, nlogic); return; @@ -2043,6 +2347,7 @@ case IVL_LPM_CMP_NEE: fprintf(stderr, "nee"); break; case IVL_LPM_DIVIDE: fprintf(stderr, "divide"); break; case IVL_LPM_FF: fprintf(stderr, "dff"); break; + case IVL_LPM_LATCH: fprintf(stderr, "latch"); break; case IVL_LPM_MOD: fprintf(stderr, "mod"); break; case IVL_LPM_MULT: fprintf(stderr, "mult"); break; case IVL_LPM_MUX: fprintf(stderr, "mux"); break; diff -Nru iverilog-10.3/tgt-vlog95/Makefile.in iverilog-11.0/tgt-vlog95/Makefile.in --- iverilog-10.3/tgt-vlog95/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -56,7 +56,8 @@ rm -f Makefile config.log cppcheck: $(O:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -80,18 +81,17 @@ vlog95.tgt: $O $(TGTDEPLIBS) $(CC) @shared@ $(LDFLAGS) -o $@ $O -lm $(TGTLDFLAGS) -install: all installdirs $(libdir)/ivl$(suffix)/vlog95.tgt $(INSTALL_DOC) $(libdir)/ivl$(suffix)/vlog95.conf $(libdir)/ivl$(suffix)/vlog95-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/vlog95.tgt: ./vlog95.tgt - $(INSTALL_PROGRAM) ./vlog95.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95.tgt" +F = ./vlog95.tgt \ + $(srcdir)/vlog95.conf \ + $(srcdir)/vlog95-s.conf -$(libdir)/ivl$(suffix)/vlog95.conf: $(srcdir)/vlog95.conf +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./vlog95.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95.tgt" $(INSTALL_DATA) $(srcdir)/vlog95.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95.conf" - -$(libdir)/ivl$(suffix)/vlog95-s.conf: $(srcdir)/vlog95-s.conf $(INSTALL_DATA) $(srcdir)/vlog95-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vlog95-s.conf" - installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-vlog95/misc.c iverilog-11.0/tgt-vlog95/misc.c --- iverilog-10.3/tgt-vlog95/misc.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/misc.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2016 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2020 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -711,6 +711,8 @@ // HERE: Does this work correctly with an array reference created from @*? void emit_name_of_nexus(ivl_scope_t scope, ivl_nexus_t nex, unsigned allow_UD) { + unsigned idx; + ivl_scope_t mod_scope; /* First look in the local scope for the nexus name. */ if (find_signal_in_nexus(scope, nex)) return; @@ -737,8 +739,19 @@ // multiples are found in a given scope. This all needs to be before // the constant code. - /* It is possible that the nexus does not have a name. For this - * case do not print an actual name. */ + /* It is possible that the nexus does not have a name. First check + if it drives another nexus through a transparent buffer. */ + for (idx = 0; idx < ivl_nexus_ptrs(nex); idx += 1) { + ivl_nexus_ptr_t nex_ptr = ivl_nexus_ptr(nex, idx); + ivl_net_logic_t nlogic = ivl_nexus_ptr_log(nex_ptr); + if (nlogic && ivl_logic_type(nlogic) == IVL_LO_BUFT + && ivl_logic_pin(nlogic, 1) == nex) { + emit_name_of_nexus(scope, ivl_logic_pin(nlogic, 0), allow_UD); + return; + } + } + + /* If not, do not print an actual name. */ fprintf(vlog_out, "/* Empty */"); // dump_nexus_information(scope, nex); } diff -Nru iverilog-10.3/tgt-vlog95/scope.c iverilog-11.0/tgt-vlog95/scope.c --- iverilog-10.3/tgt-vlog95/scope.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/scope.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2016 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2020 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,10 @@ static void emit_func_return(ivl_signal_t sig) { + // Handle SV void functions. + if (sig == 0) + return; + if (ivl_signal_dimensions(sig) > 0) { fprintf(stderr, "%s:%u: vlog95 error: A function cannot return " "an array.\n", ivl_signal_file(sig), @@ -679,17 +683,17 @@ } /* - * In SystemVerilog a task or function can have a process to initialize - * variables. In reality SystemVerilog requires this to be before the - * initial/always blocks are processed, but that's not how it is currently - * implemented in Icarus! + * In SystemVerilog a task, function, or block can have a process to + * initialize variables. SystemVerilog requires this to be before the + * initial/always blocks are processed, but there's no way to express + * this in Verilog-95. */ -static int find_tf_process(ivl_process_t proc, ivl_scope_t scope) +static int find_tfb_process(ivl_process_t proc, ivl_scope_t scope) { if (scope == ivl_process_scope(proc)) { ivl_scope_t mod_scope = scope; - /* A task or function can only have initial processes that - * are used to set local variables. */ + /* A task or function or named block can only have initial + * processes that are used to set local variables. */ assert(ivl_process_type(proc) == IVL_PR_INITIAL); /* Find the module scope for this task/function. */ while (ivl_scope_type(mod_scope) != IVL_SCT_MODULE) { @@ -704,15 +708,16 @@ } /* - * Emit any initial blocks for the tasks or functions in a module. + * Emit any initial blocks for the tasks/functions/named blocks in a module. */ -static int emit_tf_process(ivl_scope_t scope, ivl_scope_t parent) +static int emit_tfb_process(ivl_scope_t scope, ivl_scope_t parent) { ivl_scope_type_t sc_type = ivl_scope_type(scope); (void)parent; /* Parameter is not used. */ - if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK)) { + if ((sc_type == IVL_SCT_FUNCTION) || (sc_type == IVL_SCT_TASK) || + (sc_type == IVL_SCT_BEGIN) || (sc_type == IVL_SCT_FORK)) { /* Output the initial/always blocks for this module. */ - ivl_design_process(design, (ivl_process_f)find_tf_process, scope); + ivl_design_process(design, (ivl_process_f)find_tfb_process, scope); } return 0; } @@ -793,7 +798,11 @@ has_edge = 1; } emit_nexus_as_ca(scope, source, 0, 0); - fprintf(vlog_out, " =>"); + if (ivl_path_is_parallel(dpath)) { + fprintf(vlog_out, " =>"); + } else { + fprintf(vlog_out, " *>"); + } /* The compiler does not keep the source expression for an edge * sensitive path so add a constant to get the syntax right. */ if (has_edge) { @@ -1145,9 +1154,10 @@ emit_tran(scope, ivl_scope_switch(scope, idx)); } - /* Output any initial blocks for tasks or functions defined - * in this module. Used to initialize local variables. */ - ivl_scope_children(scope, (ivl_scope_f*) emit_tf_process, scope); + /* Output any initial blocks for tasks or functions or named + * blocks defined in this module. Used to initialize local + * variables. */ + ivl_scope_children(scope, (ivl_scope_f*) emit_tfb_process, scope); /* Output the initial/always blocks for this module. */ ivl_design_process(design, (ivl_process_f)find_process, scope); diff -Nru iverilog-10.3/tgt-vlog95/stmt.c iverilog-11.0/tgt-vlog95/stmt.c --- iverilog-10.3/tgt-vlog95/stmt.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/stmt.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2014 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2020 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -94,12 +94,18 @@ emit_id(ivl_signal_basename(sig)); if (array_idx) { int msb, lsb; - assert(ivl_signal_dimensions(sig)); + ivl_type_t net_type = ivl_signal_net_type(sig); fprintf(vlog_out, "["); - /* For an array the LSB/MSB order is not important. They are - * always accessed from base counting up. */ - lsb = ivl_signal_array_base(sig); - msb = lsb + ivl_signal_array_count(sig) - 1; + if (ivl_type_base(net_type) == IVL_VT_QUEUE) { + lsb = 0; + msb = 1; + } else { + assert(ivl_signal_dimensions(sig)); + /* For an array the LSB/MSB order is not important. + * They are always accessed from base counting up. */ + lsb = ivl_signal_array_base(sig); + msb = lsb + ivl_signal_array_count(sig) - 1; + } emit_scaled_expr(scope, array_idx, msb, lsb); fprintf(vlog_out, "]"); } @@ -266,7 +272,8 @@ /* If there are no selects then just print the name. */ sel_expr = ivl_lval_part_off(lval); - if (! sel_expr && (width == ivl_signal_width(sig))) { + if (! sel_expr && ((width == ivl_signal_width(sig)) || + (ivl_signal_data_type(sig) == IVL_VT_QUEUE))) { emit_stmt_lval_name(scope, lval, sig); return; } @@ -667,6 +674,7 @@ static unsigned utask_in_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); + unsigned first_arg = is_void_function(scope) ? 1 : 0; ivl_lval_t lval = ivl_stmt_lval(stmt, 0); ivl_signal_t lsig = ivl_lval_sig(lval); const char *sig_name; @@ -682,7 +690,7 @@ if (scope != ivl_signal_scope(lsig)) return ports; /* It must be an input or inout port of the task. */ sig_name = ivl_signal_basename(lsig); - for (idx = 0; idx < ports; idx += 1) { + for (idx = first_arg; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_INPUT) && @@ -699,6 +707,7 @@ static unsigned utask_out_port_idx(ivl_scope_t scope, ivl_statement_t stmt) { unsigned idx, ports = ivl_scope_ports(scope); + unsigned first_arg = is_void_function(scope) ? 1 : 0; ivl_expr_t rval = ivl_stmt_rval(stmt); ivl_signal_t rsig = 0; ivl_expr_type_t expr_type = ivl_expr_type(rval); @@ -731,7 +740,7 @@ if (ivl_signal_dimensions(rsig)) return ports; /* It must be an output or inout port of the task. */ sig_name = ivl_signal_basename(rsig); - for (idx = 0; idx < ports; idx += 1) { + for (idx = first_arg; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_OUTPUT) && @@ -794,6 +803,7 @@ unsigned lineno = ivl_stmt_lineno(stmt); unsigned start, stop, is_auto = 0; ivl_scope_t task_scope = 0; + unsigned is_void_func = 0; port_expr_t port_exprs; /* Check to see if the block is of the basic form first. */ for (idx = 0; idx < count; idx += 1) { @@ -809,7 +819,8 @@ if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) { task_idx = idx; task_scope = ivl_stmt_call(tmp); - assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); + is_void_func = is_void_function(task_scope); + assert(ivl_scope_type(task_scope) == IVL_SCT_TASK || is_void_func); continue; } /* For an automatic task the FREE must be last. */ @@ -871,7 +882,8 @@ } /* Verify that all the ports were defined. */ - for (idx = 0; idx < ports; idx += 1) { + start = is_void_func ? 1 : 0; + for (idx = start; idx < ports; idx += 1) { if (port_exprs[idx].type == IVL_SIP_NONE) { free(port_exprs); return 0; @@ -880,14 +892,18 @@ /* Now that we have the arguments figured out, print the task call. */ fprintf(vlog_out, "%*c", get_indent(), ' '); + if (is_void_func) + fprintf(vlog_out, "if ("); emit_scope_path(scope, task_scope); fprintf(vlog_out, "("); - emit_port(scope, port_exprs[0]); - for (idx = 1; idx < ports; idx += 1) { + emit_port(scope, port_exprs[start]); + for (idx = start + 1; idx < ports; idx += 1) { fprintf(vlog_out, ", "); emit_port(scope, port_exprs[idx]); } free(port_exprs); + if (is_void_func) + fprintf(vlog_out, ")"); fprintf(vlog_out, ");"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); @@ -1419,10 +1435,16 @@ static void emit_stmt_utask(ivl_scope_t scope, ivl_statement_t stmt) { ivl_scope_t task_scope = ivl_stmt_call(stmt); - assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); - assert(ivl_scope_ports(task_scope) == 0); + assert((ivl_scope_type(task_scope) == IVL_SCT_TASK + && ivl_scope_ports(task_scope) == 0) + || (is_void_function(task_scope) + && ivl_scope_ports(task_scope) == 1)); fprintf(vlog_out, "%*c", get_indent(), ' '); + if (is_void_function(task_scope)) + fprintf(vlog_out, "if ("); emit_scope_path(scope, task_scope); + if (is_void_function(task_scope)) + fprintf(vlog_out, "(1'bx))"); fprintf(vlog_out, ";"); emit_stmt_file_line(stmt); fprintf(vlog_out, "\n"); @@ -1606,6 +1628,9 @@ fprintf(vlog_out, "initial"); break; case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: fprintf(vlog_out, "always"); break; case IVL_PR_FINAL: diff -Nru iverilog-10.3/tgt-vlog95/vlog95.c iverilog-11.0/tgt-vlog95/vlog95.c --- iverilog-10.3/tgt-vlog95/vlog95.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/vlog95.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2016 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2020 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,7 +30,7 @@ static const char*version_string = "Icarus Verilog VLOG95 Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (C) 2010-2015 Cary R. (cygcary@yahoo.com)\n\n" +"Copyright (C) 2010-2020 Cary R. (cygcary@yahoo.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" @@ -219,9 +219,12 @@ /* Emit any UDP definitions that the design used. */ emit_udp_list(); - /* Emit any UDPs that are Icarus generated (D-FF). */ + /* Emit any UDPs that are Icarus generated (D-FF or latch). */ emit_icarus_generated_udps(); + /* Emit the Icarus top module used to trigger translated always_comb/latch processes at T0. */ + emit_icarus_generated_top_module(); + /* If there were errors then add this information to the output. */ if (vlog_errors) { fprintf(vlog_out, "\n"); diff -Nru iverilog-10.3/tgt-vlog95/vlog95_priv.h iverilog-11.0/tgt-vlog95/vlog95_priv.h --- iverilog-10.3/tgt-vlog95/vlog95_priv.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/vlog95_priv.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_vlog95_priv_H #define IVL_vlog95_priv_H /* - * Copyright (C) 2010-2016 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2010-2019 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -106,7 +106,6 @@ extern void emit_nexus_port_driver_as_ca(ivl_scope_t scope, ivl_nexus_t nex); extern void emit_const_nexus(ivl_scope_t scope, ivl_net_const_t const_net); extern void emit_signal_net_const_as_ca(ivl_scope_t scope, ivl_signal_t sig); -extern void emit_icarus_generated_udps(void); extern void add_udp_to_list(ivl_udp_t udp); extern void emit_udp_list(void); @@ -118,6 +117,9 @@ const char *file, unsigned lineno); extern void emit_string(const char *string); +extern void emit_icarus_generated_udps(void); +extern void emit_icarus_generated_top_module(void); + /* * Find the enclosing module scope. */ @@ -160,4 +162,10 @@ */ extern void dump_nexus_information(ivl_scope_t scope, ivl_nexus_t nex); +static inline unsigned is_void_function(ivl_scope_t scope) +{ + return ivl_scope_type(scope) == IVL_SCT_FUNCTION + && ivl_scope_port(scope, 0) == 0; +} + #endif /* IVL_vlog95_priv_H */ diff -Nru iverilog-10.3/tgt-vlog95/vlog95-s.conf iverilog-11.0/tgt-vlog95/vlog95-s.conf --- iverilog-10.3/tgt-vlog95/vlog95-s.conf 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vlog95/vlog95-s.conf 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,7 @@ functor:synth2 functor:synth functor:syn-rules +functor:nodangle +functor:exposenodes flag:DLL=vlog95.tgt flag:DISABLE_CONCATZ_GENERATION=true diff -Nru iverilog-10.3/tgt-vvp/cppcheck.sup iverilog-11.0/tgt-vvp/cppcheck.sup --- iverilog-10.3/tgt-vvp/cppcheck.sup 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/cppcheck.sup 2020-09-26 22:44:25.000000000 +0000 @@ -2,7 +2,7 @@ // are not used here. // target_design() -unusedFunction:vvp.c:149 +unusedFunction:vvp.c:148 // target_query() unusedFunction:vvp.c:246 diff -Nru iverilog-10.3/tgt-vvp/draw_mux.c iverilog-11.0/tgt-vvp/draw_mux.c --- iverilog-10.3/tgt-vvp/draw_mux.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/draw_mux.c 2020-09-26 22:44:25.000000000 +0000 @@ -83,7 +83,7 @@ net, select_input); for (idx = 0 ; idx < ivl_lpm_size(net) ; idx += 2) { - fprintf(vvp_out, "L_%p/0/%d .functor %s %u", + fprintf(vvp_out, "L_%p/0/%u .functor %s %u", net, idx/2, muxz, width); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+0))); fprintf(vvp_out, ", %s", draw_net_input(ivl_lpm_data(net,idx+1))); @@ -95,10 +95,10 @@ net, level, select_input, level, level); for (idx = 0 ; idx < (ivl_lpm_size(net) >> level); idx += 2) { - fprintf(vvp_out, "L_%p/%u/%d .functor %s %u", + fprintf(vvp_out, "L_%p/%u/%u .functor %s %u", net, level, idx/2, muxz, width); - fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx+0); - fprintf(vvp_out, ", L_%p/%d/%d", net, level-1, idx+1); + fprintf(vvp_out, ", L_%p/%u/%u", net, level-1, idx+0); + fprintf(vvp_out, ", L_%p/%u/%u", net, level-1, idx+1); fprintf(vvp_out, ", L_%p/%us", net, level); fprintf(vvp_out, ", C4<>;\n"); } @@ -106,14 +106,14 @@ } - fprintf(vvp_out, "L_%p/%ds .part %s, %d, 1; Bit %d of the select\n", + fprintf(vvp_out, "L_%p/%us .part %s, %u, 1; Bit %u of the select\n", net, swidth-1, select_input, swidth-1, swidth-1); fprintf(vvp_out, "L_%p .functor %s %u", net, muxz, width); - fprintf(vvp_out, ", L_%p/%d/0", net, swidth-2); - fprintf(vvp_out, ", L_%p/%d/1", net, swidth-2); - fprintf(vvp_out, ", L_%p/%ds", net, swidth-1); + fprintf(vvp_out, ", L_%p/%u/0", net, swidth-2); + fprintf(vvp_out, ", L_%p/%u/1", net, swidth-2); + fprintf(vvp_out, ", L_%p/%us", net, swidth-1); fprintf(vvp_out, ", C4<>;\n"); free(select_input); diff -Nru iverilog-10.3/tgt-vvp/draw_net_input.c iverilog-11.0/tgt-vvp/draw_net_input.c --- iverilog-10.3/tgt-vvp/draw_net_input.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/draw_net_input.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -387,6 +387,7 @@ if (lpm) switch (ivl_lpm_type(lpm)) { case IVL_LPM_FF: + case IVL_LPM_LATCH: case IVL_LPM_ABS: case IVL_LPM_ADD: case IVL_LPM_ARRAY: @@ -397,6 +398,8 @@ case IVL_LPM_CONCATZ: case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: case IVL_LPM_CMP_EQX: case IVL_LPM_CMP_EQZ: case IVL_LPM_CMP_GE: @@ -651,14 +654,15 @@ 0.0 into the net. */ if (ndrivers == 0) { unsigned wid = width_of_nexus(nex); + int pull = (res == IVL_SIT_TRI0) || (res == IVL_SIT_TRI1); /* For real nets put 0.0. */ if (signal_data_type_of_nexus(nex) == IVL_VT_REAL) { nex_private = draw_Cr_to_string(0.0); } else { unsigned jdx; - char*tmp = malloc(wid + 5); + char*tmp = malloc((pull ? 3 : 1) * wid + 5); nex_private = tmp; - strcpy(tmp, "C4<"); + strcpy(tmp, pull ? "C8<" : "C4<"); tmp += strlen(tmp); switch (res) { case IVL_SIT_TRI: @@ -669,12 +673,18 @@ *tmp++ = 'z'; break; case IVL_SIT_TRI0: - for (jdx = 0 ; jdx < wid ; jdx += 1) + for (jdx = 0 ; jdx < wid ; jdx += 1) { + *tmp++ = '5'; + *tmp++ = '5'; *tmp++ = '0'; + } break; case IVL_SIT_TRI1: - for (jdx = 0 ; jdx < wid ; jdx += 1) + for (jdx = 0 ; jdx < wid ; jdx += 1) { + *tmp++ = '5'; + *tmp++ = '5'; *tmp++ = '1'; + } break; default: assert(0); @@ -687,8 +697,13 @@ to do this so that .nets have something to hang onto. */ char buf[64]; snprintf(buf, sizeof buf, "o%p", nex); - fprintf(vvp_out, "%s .functor BUFZ %u, %s; HiZ drive\n", - buf, wid, nex_private); + if (pull) { + fprintf(vvp_out, "%s .functor BUFT %u, %s, C4<0>, C4<0>, C4<0>; pull drive\n", + buf, wid, nex_private); + } else { + fprintf(vvp_out, "%s .functor BUFZ %u, %s; HiZ drive\n", + buf, wid, nex_private); + } nex_private = realloc(nex_private, strlen(buf)+1); strcpy(nex_private, buf); @@ -719,9 +734,9 @@ char*nex_str = draw_net_input_drive(nex, drivers[0]); char modpath_label[64]; snprintf(modpath_label, sizeof modpath_label, - "V_%p/m", path_sig); + "V_%p_0/m", path_sig); nex_private = strdup(modpath_label); - draw_modpath(path_sig, nex_str); + draw_modpath(path_sig, nex_str, 0); } else { nex_private = draw_net_input_drive(nex, drivers[0]); @@ -742,8 +757,20 @@ } driver_labels = malloc(ndrivers * sizeof(char*)); - for (idx = 0; idx < ndrivers; idx += 1) { - driver_labels[idx] = draw_net_input_drive(nex, drivers[idx]); + ivl_signal_t path_sig = find_modpath(nex); + if (path_sig) { + for (idx = 0; idx < ndrivers; idx += 1) { + char*nex_str = draw_net_input_drive(nex, drivers[idx]); + char modpath_label[64]; + snprintf(modpath_label, sizeof modpath_label, + "V_%p_%u/m", path_sig, idx); + driver_labels[idx] = strdup(modpath_label); + draw_modpath(path_sig, nex_str, idx); + } + } else { + for (idx = 0; idx < ndrivers; idx += 1) { + driver_labels[idx] = draw_net_input_drive(nex, drivers[idx]); + } } fprintf(vvp_out, "RS_%p .resolv %s", nex, resolv_type); for (idx = 0; idx < ndrivers; idx += 1) { diff -Nru iverilog-10.3/tgt-vvp/draw_switch.c iverilog-11.0/tgt-vvp/draw_switch.c --- iverilog-10.3/tgt-vvp/draw_switch.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/draw_switch.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2008-2020 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 @@ -73,6 +73,15 @@ } switch (ivl_switch_type(sw)) { + case IVL_SW_RTRAN: + fprintf(vvp_out, " .rtran"); + break; + case IVL_SW_RTRANIF0: + fprintf(vvp_out, " .rtranif0"); + break; + case IVL_SW_RTRANIF1: + fprintf(vvp_out, " .rtranif1"); + break; case IVL_SW_TRAN: fprintf(vvp_out, " .tran"); break; @@ -88,8 +97,7 @@ break; default: - fprintf(stderr, "%s:%u: tgt-vvp sorry: resistive switch modeling " - "is not currently supported.\n", + fprintf(stderr, "%s:%u: vvp.tgt error: unrecognised switch type.\n", ivl_switch_file(sw), ivl_switch_lineno(sw)); vvp_errors += 1; return; diff -Nru iverilog-10.3/tgt-vvp/draw_ufunc.c iverilog-11.0/tgt-vvp/draw_ufunc.c --- iverilog-10.3/tgt-vvp/draw_ufunc.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/draw_ufunc.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2005-2016 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 @@ -130,10 +130,36 @@ } /* Call the function */ - fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); - fprintf(vvp_out, ", S_%p;\n", def); - fprintf(vvp_out, " %%join;\n"); - + switch (ivl_expr_value(expr)) { + case IVL_VT_VOID: + fprintf(vvp_out, " %%callf/void TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_REAL: + fprintf(vvp_out, " %%callf/real TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + fprintf(vvp_out, " %%callf/vec4 TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_STRING: + fprintf(vvp_out, " %%callf/str TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + case IVL_VT_CLASS: + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + fprintf(vvp_out, " %%callf/obj TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + break; + default: + fprintf(vvp_out, " %%fork TD_%s", vvp_mangle_id(ivl_scope_name(def))); + fprintf(vvp_out, ", S_%p;\n", def); + fprintf(vvp_out, " %%join;\n"); + break; + } } static void draw_ufunc_epilogue(ivl_expr_t expr) @@ -159,48 +185,33 @@ void draw_ufunc_vec4(ivl_expr_t expr) { - ivl_scope_t def = ivl_expr_def(expr); - ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call function code. */ draw_ufunc_preamble(expr); - assert(ivl_signal_dimensions(retval) == 0); - fprintf(vvp_out, " %%load/vec4 v%p_0;\n", retval); - draw_ufunc_epilogue(expr); } void draw_ufunc_real(ivl_expr_t expr) { - ivl_scope_t def = ivl_expr_def(expr); - ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); - /* Return value signal cannot be an array. */ - assert(ivl_signal_dimensions(retval) == 0); - - /* Load the result into a word. */ - fprintf(vvp_out, " %%load/real v%p_0;\n", retval); + /* The %callf/real function emitted by the preamble leaves + the result in the stack for us. */ draw_ufunc_epilogue(expr); } void draw_ufunc_string(ivl_expr_t expr) { - ivl_scope_t def = ivl_expr_def(expr); - ivl_signal_t retval = ivl_scope_port(def, 0); /* Take in arguments to function and call the function code. */ draw_ufunc_preamble(expr); - /* Return value signal cannot be an array. */ - assert(ivl_signal_dimensions(retval) == 0); - - /* Load the result into a word. */ - fprintf(vvp_out, " %%load/str v%p_0;\n", retval); + /* The %callf/str function emitted by the preamble leaves + the result in the stack for us. */ draw_ufunc_epilogue(expr); } diff -Nru iverilog-10.3/tgt-vvp/draw_vpi.c iverilog-11.0/tgt-vvp/draw_vpi.c --- iverilog-10.3/tgt-vvp/draw_vpi.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/draw_vpi.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2020 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 @@ -94,9 +94,19 @@ If I don't need to do any evaluating, then skip it as I'll be passing the handle to the signal itself. */ - if (ivl_expr_width(expr) != - ivl_signal_width(ivl_expr_signal(expr))) { + if ((ivl_expr_width(expr) != + ivl_signal_width(ivl_expr_signal(expr))) && + ivl_expr_value(expr) != IVL_VT_DARRAY) { /* This should never happen since we have IVL_EX_SELECT. */ + /* For a queue (type of darray) we return the maximum + size of the queue as the signal width. */ + assert(0); + return 0; + + } else if (signal_is_return_value(ivl_expr_signal(expr))) { + /* If the signal is the return value of a function, + then this can't be handled as a true signal, so + fall back on general expression processing. */ return 0; } else if (ivl_expr_signed(expr) != @@ -484,9 +494,10 @@ ivl_stmt_lineno(tnet), ivl_stmt_name(tnet)); } else { char call_string[1024]; - sprintf(call_string, " %s %u %u \"%s\"", command, - ivl_file_table_index(ivl_stmt_file(tnet)), - ivl_stmt_lineno(tnet), ivl_stmt_name(tnet)); + snprintf(call_string, sizeof(call_string), + " %s %u %u \"%s\"", command, + ivl_file_table_index(ivl_stmt_file(tnet)), + ivl_stmt_lineno(tnet), ivl_stmt_name(tnet)); draw_vpi_taskfunc_args(call_string, tnet, 0); } } @@ -495,10 +506,11 @@ { char call_string[1024]; - sprintf(call_string, " %%vpi_func %u %u \"%s\" %u", - ivl_file_table_index(ivl_expr_file(fnet)), - ivl_expr_lineno(fnet), ivl_expr_name(fnet), - ivl_expr_width(fnet)); + snprintf(call_string, sizeof(call_string), + " %%vpi_func %u %u \"%s\" %u", + ivl_file_table_index(ivl_expr_file(fnet)), + ivl_expr_lineno(fnet), ivl_expr_name(fnet), + ivl_expr_width(fnet)); draw_vpi_taskfunc_args(call_string, 0, fnet); } @@ -507,9 +519,22 @@ { char call_string[1024]; - sprintf(call_string, " %%vpi_func/r %u %u \"%s\"", - ivl_file_table_index(ivl_expr_file(fnet)), - ivl_expr_lineno(fnet), ivl_expr_name(fnet)); + snprintf(call_string, sizeof(call_string), + " %%vpi_func/r %u %u \"%s\"", + ivl_file_table_index(ivl_expr_file(fnet)), + ivl_expr_lineno(fnet), ivl_expr_name(fnet)); + + draw_vpi_taskfunc_args(call_string, 0, fnet); +} + +void draw_vpi_sfunc_call(ivl_expr_t fnet) +{ + char call_string[1024]; + + snprintf(call_string, sizeof(call_string), + " %%vpi_func/s %u %u \"%s\"", + ivl_file_table_index(ivl_expr_file(fnet)), + ivl_expr_lineno(fnet), ivl_expr_name(fnet)); draw_vpi_taskfunc_args(call_string, 0, fnet); } diff -Nru iverilog-10.3/tgt-vvp/eval_condit.c iverilog-11.0/tgt-vvp/eval_condit.c --- iverilog-10.3/tgt-vvp/eval_condit.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/eval_condit.c 2020-09-26 22:44:25.000000000 +0000 @@ -189,21 +189,48 @@ char use_opcode = ivl_expr_opcode(expr); char s_flag = (ivl_expr_signed(le) && ivl_expr_signed(re)) ? 's' : 'u'; - /* If this is a > or >=, then convert it to < or <= by - swapping the operands. Adjust the opcode to match. */ - switch (use_opcode) { - case 'G': - tmp = le; - le = re; - re = tmp; - use_opcode = 'L'; - break; - case '>': + if (test_immediate_vec4_ok(le) && !test_immediate_vec4_ok(re)) { tmp = le; le = re; re = tmp; - use_opcode = '<'; - break; + + switch (use_opcode) { + case 'G': + use_opcode = 'L'; + break; + case 'L': + use_opcode = 'G'; + break; + case '>': + use_opcode = '<'; + break; + case '<': + use_opcode = '>'; + break; + default: + assert(0); + break; + } + + } else if (!test_immediate_vec4_ok(re)) { + /* If this is a > or >=, then convert it to < or <= by + swapping the operands. Adjust the opcode to match. Do + this because the instruction really only supports < + and <= and we can avoid a %flag_inv instruction. */ + switch (use_opcode) { + case 'G': + tmp = le; + le = re; + re = tmp; + use_opcode = 'L'; + break; + case '>': + tmp = le; + le = re; + re = tmp; + use_opcode = '<'; + break; + } } /* NOTE: I think I would rather the elaborator handle the @@ -231,6 +258,13 @@ } switch (use_opcode) { + case '>': + fprintf(vvp_out, " %%flag_or 5, 4; GT is !LE\n"); + fprintf(vvp_out, " %%flag_inv 5;\n"); + return 5; + case 'G': + fprintf(vvp_out, " %%flag_inv 5; GE is !LT\n"); + return 5; case '<': return 5; case 'L': diff -Nru iverilog-10.3/tgt-vvp/eval_expr.c iverilog-11.0/tgt-vvp/eval_expr.c --- iverilog-10.3/tgt-vvp/eval_expr.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/eval_expr.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -96,6 +96,17 @@ if (pad_bit == '1' && !negative_ok_flag) return 0; + /* Check if all the bits are either x or z. */ + if ((bits[0] == 'x') || (bits[0] == 'z')) { + char first_bit = bits[0]; + unsigned bits_match = 1; + for (idx = 1 ; idx < nbits ; idx += 1) + if (bits[idx] != first_bit) { + bits_match = 0; + break; + } + if (bits_match) return 1; + } for (idx = lim_wid ; idx < nbits ; idx += 1) if (bits[idx] != pad_bit) return 0; diff -Nru iverilog-10.3/tgt-vvp/eval_object.c iverilog-11.0/tgt-vvp/eval_object.c --- iverilog-10.3/tgt-vvp/eval_object.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/eval_object.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2020 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 @@ -21,62 +21,75 @@ # include # include -static int eval_darray_new(ivl_expr_t ex) +void darray_new(ivl_type_t element_type, unsigned size_reg) { - int errors = 0; - unsigned size_reg = allocate_word(); - ivl_expr_t size_expr = ivl_expr_oper1(ex); - ivl_expr_t init_expr = ivl_expr_oper2(ex); - draw_eval_expr_into_integer(size_expr, size_reg); - - // The new function has a net_type that contains the details - // of the type. - ivl_type_t net_type = ivl_expr_net_type(ex); - assert(net_type); - - ivl_type_t element_type = ivl_type_element(net_type); - assert(element_type); + int wid; + char*signed_char; + ivl_variable_type_t type = ivl_type_base(element_type); - switch (ivl_type_base(element_type)) { - int msb, lsb, wid; - case IVL_VT_REAL: - // REAL objects are not packable. - assert(ivl_type_packed_dimensions(element_type) == 0); - fprintf(vvp_out, " %%new/darray %u, \"r\";\n", size_reg); - break; - case IVL_VT_STRING: - // STRING objects are not packable. - assert(ivl_type_packed_dimensions(element_type) == 0); - fprintf(vvp_out, " %%new/darray %u, \"S\";\n", size_reg); - break; - case IVL_VT_BOOL: + if ((type == IVL_VT_BOOL) || (type == IVL_VT_LOGIC)) { + int msb, lsb; // bool objects are vectorable, but for now only support // a single dimensions. assert(ivl_type_packed_dimensions(element_type) == 1); msb = ivl_type_packed_msb(element_type, 0); lsb = ivl_type_packed_lsb(element_type, 0); - wid = msb>=lsb? msb - lsb : lsb - msb; + wid = msb>=lsb ? msb - lsb : lsb - msb; wid += 1; - fprintf(vvp_out, " %%new/darray %u, \"%sb%d\";\n", size_reg, - ivl_type_signed(element_type) ? "s" : "", wid); + signed_char = ivl_type_signed(element_type) ? "s" : ""; + } else { + // REAL or STRING objects are not packable. + assert(ivl_type_packed_dimensions(element_type) == 0); + wid = 0; + signed_char = ""; + } + + switch (type) { + case IVL_VT_REAL: + fprintf(vvp_out, " %%new/darray %u, \"r\";\n", + size_reg); + break; + + case IVL_VT_STRING: + fprintf(vvp_out, " %%new/darray %u, \"S\";\n", + size_reg); break; + + case IVL_VT_BOOL: + fprintf(vvp_out, " %%new/darray %u, \"%sb%d\";\n", + size_reg, signed_char, wid); + break; + case IVL_VT_LOGIC: - // logic objects are vectorable, but for now only support - // a single dimensions. - assert(ivl_type_packed_dimensions(element_type) == 1); - msb = ivl_type_packed_msb(element_type, 0); - lsb = ivl_type_packed_lsb(element_type, 0); - wid = msb>=lsb? msb - lsb : lsb - msb; - wid += 1; - fprintf(vvp_out, " %%new/darray %u, \"%sv%d\";\n", size_reg, - ivl_type_signed(element_type) ? "s" : "", wid); + fprintf(vvp_out, " %%new/darray %u, \"%sv%d\";\n", + size_reg, signed_char, wid); break; default: assert(0); break; } + clr_word(size_reg); +} + +static int eval_darray_new(ivl_expr_t ex) +{ + int errors = 0; + unsigned size_reg = allocate_word(); + ivl_expr_t size_expr = ivl_expr_oper1(ex); + ivl_expr_t init_expr = ivl_expr_oper2(ex); + draw_eval_expr_into_integer(size_expr, size_reg); + + // The new function has a net_type that contains the details + // of the type. + ivl_type_t net_type = ivl_expr_net_type(ex); + assert(net_type); + + ivl_type_t element_type = ivl_type_element(net_type); + assert(element_type); + + darray_new(element_type, size_reg); if (init_expr && ivl_expr_type(init_expr)==IVL_EX_ARRAY_PATTERN) { unsigned idx; diff -Nru iverilog-10.3/tgt-vvp/eval_real.c iverilog-11.0/tgt-vvp/eval_real.c --- iverilog-10.3/tgt-vvp/eval_real.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/eval_real.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2020 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 @@ -32,13 +32,13 @@ int allocate_word() { int res = 4; - int max = WORD_COUNT; - while (res < max && (1U << res) & word_alloc_mask) + while (res < WORD_COUNT && (1U << res) & word_alloc_mask) res += 1; - if (res >= max) { - fprintf(stderr, "vvp.tgt error: Thread words exhausted.\n"); + if (res >= WORD_COUNT) { + fprintf(stderr, "vvp.tgt error: Thread words (%d) exhausted " + "during VVP code generation.\n", WORD_COUNT); exit(1); } word_alloc_mask |= 1U << res; @@ -47,8 +47,7 @@ void clr_word(int res) { - int max = WORD_COUNT; - assert(res < max); + assert(res < WORD_COUNT); word_alloc_mask &= ~ (1U << res); } @@ -57,6 +56,8 @@ switch (ivl_expr_opcode(expr)) { case 'E': case 'N': + case 'w': + case 'W': case 'l': case 'r': case 'R': @@ -107,7 +108,7 @@ break; default: - fprintf(stderr, "XXXX draw_binary_real(%c)\n", + fprintf(stderr, "vvp.tgt error: draw_binary_real(%c)\n", ivl_expr_opcode(expr)); assert(0); } @@ -247,19 +248,7 @@ { draw_eval_vec4(expr); const char*sign_flag = ivl_expr_signed(expr)? "/s" : ""; - - if (ivl_expr_width(expr) > 64) { - fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag); - } else { - int res = allocate_word(); - fprintf(vvp_out, " %%ix/vec4%s %d;\n", sign_flag, res); - - if (ivl_expr_signed(expr)) - fprintf(vvp_out, " %%cvt/rs %d;\n", res); - else - fprintf(vvp_out, " %%cvt/ru %d;\n", res); - clr_word(res); - } + fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag); } static void draw_select_real(ivl_expr_t expr) @@ -271,12 +260,28 @@ /* Assume the sub-expression is a signal */ ivl_signal_t sig = ivl_expr_signal(sube); - assert(ivl_signal_data_type(sig) == IVL_VT_DARRAY); + assert(ivl_signal_data_type(sig) == IVL_VT_DARRAY || ivl_signal_data_type(sig) == IVL_VT_QUEUE); draw_eval_expr_into_integer(shift, 3); fprintf(vvp_out, " %%load/dar/r v%p_0;\n", sig); } +static void real_ex_pop(ivl_expr_t expr) +{ + const char*fb; + ivl_expr_t arg; + + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) + fb = "b"; + else + fb = "f"; + + arg = ivl_expr_parm(expr, 0); + assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); + + fprintf(vvp_out, " %%qpop/%s/real v%p_0;\n", fb, ivl_expr_signal(arg)); +} + static void draw_sfunc_real(ivl_expr_t expr) { switch (ivl_expr_value(expr)) { @@ -309,6 +314,16 @@ { ivl_signal_t sig = ivl_expr_signal(expr); + /* Special Case: If the signal is the return value of the function, + then use a different opcode to get the value. */ + if (signal_is_return_value(sig)) { + assert(ivl_signal_dimensions(sig) == 0); + fprintf(vvp_out, " %%retload/real 0; Load %s (draw_signal_real_real)\n", + ivl_signal_basename(sig)); + return; + } + + if (ivl_signal_dimensions(sig) == 0) { fprintf(vvp_out, " %%load/real v%p_0;\n", sig); return; @@ -512,7 +527,12 @@ break; case IVL_EX_SFUNC: - draw_sfunc_real(expr); + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) + real_ex_pop(expr); + else if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_front")==0) + real_ex_pop(expr); + else + draw_sfunc_real(expr); break; case IVL_EX_SIGNAL: @@ -535,16 +555,10 @@ if (ivl_expr_value(expr) == IVL_VT_VECTOR) { draw_eval_vec4(expr); const char*sign_flag = ivl_expr_signed(expr)? "/s" : ""; - - int res = allocate_word(); - - fprintf(vvp_out, " %%ix/vec4%s %d;\n", sign_flag, res); - fprintf(vvp_out, " %%cvt/rs %d;\n", res); - - clr_word(res); + fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag); } else { - fprintf(stderr, "XXXX Evaluate real expression (%d)\n", + fprintf(stderr, "vvp.tgt error: XXXX Evaluate real expression (%d)\n", ivl_expr_type(expr)); fprintf(vvp_out, " ; XXXX Evaluate real expression (%d)\n", ivl_expr_type(expr)); diff -Nru iverilog-10.3/tgt-vvp/eval_string.c iverilog-11.0/tgt-vvp/eval_string.c --- iverilog-10.3/tgt-vvp/eval_string.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/eval_string.c 2020-09-26 22:44:25.000000000 +0000 @@ -76,6 +76,15 @@ return; } + /* Special Case: If the signal is the return value of the + function, then use a different opcode to get the value. */ + if (signal_is_return_value(sig)) { + assert(ivl_signal_dimensions(sig) == 0); + fprintf(vvp_out, " %%retload/str 0; Load %s (string_ex_signal)\n", + ivl_signal_basename(sig)); + return; + } + /* Simple case: This is a simple variable. Generate a load statement to load the string into the stack. */ if (ivl_signal_dimensions(sig) == 0) { @@ -152,7 +161,7 @@ const char*fb; ivl_expr_t arg; - if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) fb = "b"; else fb = "f"; @@ -163,6 +172,12 @@ fprintf(vvp_out, " %%qpop/%s/str v%p_0;\n", fb, ivl_expr_signal(arg)); } +static void draw_sfunc_string(ivl_expr_t expr) +{ + assert(ivl_expr_value(expr) == IVL_VT_STRING); + draw_vpi_sfunc_call(expr); +} + void draw_eval_string(ivl_expr_t expr) { @@ -190,12 +205,12 @@ case IVL_EX_SFUNC: if (strcmp(ivl_expr_name(expr), "$ivl_string_method$substr") == 0) string_ex_substr(expr); - else if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) + else if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) string_ex_pop(expr); - else if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_front")==0) + else if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_front")==0) string_ex_pop(expr); else - fallback_eval(expr); + draw_sfunc_string(expr); break; case IVL_EX_UFUNC: diff -Nru iverilog-10.3/tgt-vvp/eval_vec4.c iverilog-11.0/tgt-vvp/eval_vec4.c --- iverilog-10.3/tgt-vvp/eval_vec4.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/eval_vec4.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2013-2020 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 @@ -227,8 +227,8 @@ break; case 'n': /* != */ fprintf(vvp_out, " %%cmp/wr;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); - fprintf(vvp_out, " %%inv;\n"); break; default: assert(0); @@ -247,8 +247,8 @@ break; case 'n': /* != */ fprintf(vvp_out, " %%cmp/str;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); - fprintf(vvp_out, " %%inv;\n"); break; default: assert(0); @@ -298,9 +298,9 @@ fprintf(vvp_out, " %%test_nul/a v%p, %d;\n", sig, word_ix); clr_word(word_ix); } - fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (ivl_expr_opcode(expr) == 'n') - fprintf(vvp_out, " %%inv;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); return; } @@ -320,9 +320,9 @@ fprintf(vvp_out, " %%load/obj v%p_0;\n", sig); fprintf(vvp_out, " %%test_nul/prop %u, %d;\n", pidx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); - fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (ivl_expr_opcode(expr) == 'n') - fprintf(vvp_out, " %%inv;\n"); + fprintf(vvp_out, " %%flag_inv 4;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); if (idx != 0) clr_word(idx); return; @@ -385,24 +385,53 @@ fprintf(vvp_out, " %%flag_get/vec4 4;\n"); break; case 'n': /* != */ - fprintf(vvp_out, " %%cmp/e;\n"); + fprintf(vvp_out, " %%cmp/ne;\n"); fprintf(vvp_out, " %%flag_get/vec4 4;\n"); - fprintf(vvp_out, " %%inv;\n"); break; case 'E': /* === */ fprintf(vvp_out, " %%cmp/e;\n"); fprintf(vvp_out, " %%flag_get/vec4 6;\n"); break; case 'N': /* !== */ - fprintf(vvp_out, " %%cmp/e;\n"); + fprintf(vvp_out, " %%cmp/ne;\n"); fprintf(vvp_out, " %%flag_get/vec4 6;\n"); - fprintf(vvp_out, " %%inv;\n"); + break; + case 'w': /* ==? */ + fprintf(vvp_out, " %%cmp/we;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); + break; + case 'W': /* !=? */ + fprintf(vvp_out, " %%cmp/wne;\n"); + fprintf(vvp_out, " %%flag_get/vec4 4;\n"); break; default: assert(0); } } +static void draw_binary_vec4_limpl(ivl_expr_t expr) +{ + fprintf(stderr, "vvp.tgt sorry: No support for logical implication (%s:%u).\n", + ivl_expr_file(expr), ivl_expr_lineno(expr)); + assert(0); +} + +static void draw_binary_vec4_lequiv(ivl_expr_t expr) +{ + ivl_expr_t le = ivl_expr_oper1(expr); + ivl_expr_t re = ivl_expr_oper2(expr); + + /* The arguments should have already been reduced. */ + assert(ivl_expr_width(le) == 1); + draw_eval_vec4(le); + assert(ivl_expr_width(re) == 1); + draw_eval_vec4(re); + + fprintf(vvp_out, " %%xnor;\n"); + + assert(ivl_expr_width(expr) == 1); +} + static void draw_binary_vec4_land(ivl_expr_t expr) { ivl_expr_t le = ivl_expr_oper1(expr); @@ -689,8 +718,10 @@ case 'e': /* == */ case 'E': /* === */ - case 'n': /* !== */ + case 'n': /* != */ case 'N': /* !== */ + case 'w': /* ==? */ + case 'W': /* !=? */ draw_binary_vec4_compare(expr); break; @@ -711,6 +742,14 @@ draw_binary_vec4_lor(expr); break; + case 'q': /* -> (logical implication) */ + draw_binary_vec4_limpl(expr); + break; + + case 'Q': /* <-> (logical equivalence) */ + draw_binary_vec4_lequiv(expr); + break; + default: fprintf(stderr, "vvp.tgt error: unsupported binary (%c)\n", ivl_expr_opcode(expr)); @@ -953,7 +992,7 @@ /* * This function handles the special case of a call to the internal - * functions $ivl_darray_method$pop_back et al. The first (and only) + * functions $ivl_queue_method$pop_back et al. The first (and only) * argument is the signal that represents a dynamic queue. Generate a * %qpop instruction to pop a value and push it to the vec4 stack. */ @@ -961,7 +1000,7 @@ { const char*fb; - if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) fb = "b"; else fb = "f"; @@ -969,7 +1008,8 @@ ivl_expr_t arg = ivl_expr_parm(expr, 0); assert(ivl_expr_type(arg) == IVL_EX_SIGNAL); - fprintf(vvp_out, " %%qpop/%s/v v%p_0;\n", fb, ivl_expr_signal(arg)); + fprintf(vvp_out, " %%qpop/%s/v v%p_0, %u;\n", fb, ivl_expr_signal(arg), + ivl_expr_width(expr)); } static void draw_sfunc_vec4(ivl_expr_t expr) @@ -989,11 +1029,11 @@ return; } - if (strcmp(ivl_expr_name(expr), "$ivl_darray_method$pop_back")==0) { + if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0) { draw_darray_pop(expr); return; } - if (strcmp(ivl_expr_name(expr),"$ivl_darray_method$pop_front")==0) { + if (strcmp(ivl_expr_name(expr),"$ivl_queue_method$pop_front")==0) { draw_darray_pop(expr); return; } @@ -1005,6 +1045,15 @@ { ivl_signal_t sig = ivl_expr_signal(expr); + /* Special Case: If the signal is the return value of the function, + then use a different opcode to get the value. */ + if (signal_is_return_value(sig)) { + assert(ivl_signal_dimensions(sig) == 0); + fprintf(vvp_out, " %%retload/vec4 0; Load %s (draw_signal_vec4)\n", + ivl_signal_basename(sig)); + return; + } + /* Handle the simple case, a signal expression that is a simple vector, no array dimensions. */ if (ivl_signal_dimensions(sig) == 0) { @@ -1238,10 +1287,24 @@ local_count += 1; break; - case 'v': /* Cast real to vec4 */ - assert(ivl_expr_value(sub) == IVL_VT_REAL); - draw_eval_real(sub); - fprintf(vvp_out, " %%cvt/vr %u;\n", ivl_expr_width(expr)); + case 'v': /* Cast expression to vec4 */ + switch (ivl_expr_value(sub)) { + case IVL_VT_REAL: + draw_eval_real(sub); + fprintf(vvp_out, " %%cvt/vr %u;\n", ivl_expr_width(expr)); + break; + case IVL_VT_STRING: + draw_eval_string(sub); + fprintf(vvp_out, " %%cast/vec4/str %u;\n", ivl_expr_width(expr)); + break; + case IVL_VT_DARRAY: + draw_eval_object(sub); + fprintf(vvp_out, " %%cast/vec4/dar %u;\n", ivl_expr_width(expr)); + break; + default: + assert(0); + break; + } break; case '2': /* Cast expression to bool */ @@ -1259,6 +1322,14 @@ draw_eval_real(sub); fprintf(vvp_out, " %%cvt/vr %u;\n", ivl_expr_width(expr)); break; + case IVL_VT_STRING: + draw_eval_string(sub); + fprintf(vvp_out, " %%cast/vec4/str %u;\n", ivl_expr_width(expr)); + break; + case IVL_VT_DARRAY: + draw_eval_object(sub); + fprintf(vvp_out, " %%cast/vec2/dar %u;\n", ivl_expr_width(expr)); + break; default: assert(0); break; diff -Nru iverilog-10.3/tgt-vvp/Makefile.in iverilog-11.0/tgt-vvp/Makefile.in --- iverilog-10.3/tgt-vvp/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -53,7 +53,7 @@ eval_condit.o \ eval_expr.o eval_object.o eval_real.o eval_string.o \ eval_vec4.o \ - modpath.o stmt_assign.o vector.o \ + modpath.o stmt_assign.o \ vvp_process.o vvp_scope.o all: dep vvp.tgt vvp.conf vvp-s.conf @@ -68,7 +68,8 @@ rm -f stamp-vvp_config-h vvp_config.h cppcheck: $(O:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ --relative-paths=$(srcdir) $(INCLUDE_PATH) $^ Makefile: $(srcdir)/Makefile.in ../config.status @@ -103,17 +104,16 @@ cd ..; ./config.status --header=tgt-vvp/vvp_config.h vvp_config.h: stamp-vvp_config-h -install: all installdirs $(libdir)/ivl$(suffix)/vvp.tgt $(libdir)/ivl$(suffix)/vvp.conf $(libdir)/ivl$(suffix)/vvp-s.conf +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/vvp.tgt: ./vvp.tgt - $(INSTALL_PROGRAM) ./vvp.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.tgt" - -$(libdir)/ivl$(suffix)/vvp.conf: vvp.conf - $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.conf" - -$(libdir)/ivl$(suffix)/vvp-s.conf: vvp-s.conf - $(INSTALL_DATA) $< "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp-s.conf" +F = ./vvp.tgt \ + ./vvp.conf \ + ./vvp-s.conf +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./vvp.tgt "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.tgt" + $(INSTALL_DATA) ./vvp.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp.conf" + $(INSTALL_DATA) ./vvp-s.conf "$(DESTDIR)$(libdir)/ivl$(suffix)/vvp-s.conf" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)/ivl$(suffix)" diff -Nru iverilog-10.3/tgt-vvp/modpath.c iverilog-11.0/tgt-vvp/modpath.c --- iverilog-10.3/tgt-vvp/modpath.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/modpath.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2010 Stephen Williams (steve@icarus.com) + * Copyright (c) 2007-2020 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 @@ -127,16 +127,18 @@ struct modpath_item { ivl_signal_t path_sig; char*drive_label; + unsigned drive_index; struct modpath_item*next; }; static struct modpath_item*modpath_list = 0; -void draw_modpath(ivl_signal_t path_sig, char*drive_label) +void draw_modpath(ivl_signal_t path_sig, char*drive_label, unsigned drive_index) { struct modpath_item*cur = calloc(1, sizeof(struct modpath_item)); cur->path_sig = path_sig; cur->drive_label = drive_label; + cur->drive_index = drive_index; cur->next = modpath_list; modpath_list = cur; } @@ -149,7 +151,7 @@ modpath_list = cur->next; - snprintf(modpath_label, sizeof modpath_label, "V_%p/m", cur->path_sig); + snprintf(modpath_label, sizeof modpath_label, "V_%p_%u/m", cur->path_sig, cur->drive_index); draw_modpath_record(modpath_label, cur->drive_label, cur->path_sig); free(cur->drive_label); free(cur); diff -Nru iverilog-10.3/tgt-vvp/stmt_assign.c iverilog-11.0/tgt-vvp/stmt_assign.c --- iverilog-10.3/tgt-vvp/stmt_assign.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/stmt_assign.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2020 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 @@ -111,7 +111,12 @@ slice->type = SLICE_SIMPLE_VECTOR; slice->u_.simple_vector.use_word = use_word; - fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + if (signal_is_return_value(sig)) { + assert(use_word==0); + fprintf(vvp_out, " %%retload/vec4 0;\n"); + } else { + fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + } } else if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0) { @@ -120,7 +125,12 @@ slice->type = SLICE_PART_SELECT_STATIC; slice->u_.part_select_static.part_off = part_off; - fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + if (signal_is_return_value(sig)) { + assert(use_word==0); + fprintf(vvp_out, " %%retload/vec4 0;\n"); + } else { + fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); + } fprintf(vvp_out, " %%pushi/vec4 %lu, 0, 32;\n", part_off); fprintf(vvp_out, " %%part/u %u;\n", wid); @@ -128,6 +138,7 @@ assert(use_word == 0); assert(part_off == 0); + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = SLICE_PART_SELECT_DYNAMIC; @@ -143,6 +154,8 @@ } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED + slice->type = SLICE_MEMORY_WORD_STATIC; slice->u_.memory_word_static.use_word = use_word; if (use_word < ivl_signal_array_count(sig)) { @@ -156,6 +169,7 @@ } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = SLICE_MEMORY_WORD_DYNAMIC; slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); @@ -202,6 +216,49 @@ } +static void put_vec_to_ret_slice(ivl_signal_t sig, struct vec_slice_info*slice, + unsigned wid) +{ + int part_off_idx; + + /* If the slice of the l-value is a BOOL variable, then cast + the data to a BOOL vector so that the stores can be valid. */ + if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { + fprintf(vvp_out, " %%cast2;\n"); + } + + switch (slice->type) { + default: + fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); + assert(0); + break; + + case SLICE_SIMPLE_VECTOR: + assert(slice->u_.simple_vector.use_word == 0); + fprintf(vvp_out, " %%ret/vec4 0, 0, %u;\n", wid); + break; + + case SLICE_PART_SELECT_STATIC: + part_off_idx = allocate_word(); + fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", + part_off_idx, slice->u_.part_select_static.part_off); + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + fprintf(vvp_out, " %%ret/vec4 0, %d, %u;\n", part_off_idx, wid); + clr_word(part_off_idx); + break; + + case SLICE_PART_SELECT_DYNAMIC: + fprintf(vvp_out, " %%flag_mov 4, %u;\n", + slice->u_.part_select_dynamic.x_flag); + fprintf(vvp_out, " %%ret/vec4 0, %d, %u;\n", + slice->u_.part_select_dynamic.word_idx_reg, wid); + clr_word(slice->u_.part_select_dynamic.word_idx_reg); + clr_flag(slice->u_.part_select_dynamic.x_flag); + break; + + } +} + static void put_vec_to_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, unsigned wid) { @@ -209,6 +266,15 @@ ivl_signal_t sig = ivl_lval_sig(lval); int part_off_idx; + + /* Special Case: If the l-value signal is named after its scope, + and the scope is a function, then this is an assign to a return + value and should be handled differently. */ + if (signal_is_return_value(sig)) { + put_vec_to_ret_slice(sig, slice, wid); + return; + } + /* If the slice of the l-value is a BOOL variable, then cast the data to a BOOL vector so that the stores can be valid. */ if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { @@ -375,7 +441,10 @@ draw_eval_expr_into_integer(part_off_ex, offset_index); /* Note that flag4 is set by the eval above. */ assert(lsig); - if (ivl_signal_type(lsig)==IVL_SIT_UWIRE) { + if (signal_is_return_value(lsig)) { + fprintf(vvp_out, " %%ret/vec4 0, %d, %u; Assign to %s (store_vec4_to_lval)\n", + offset_index, lwid, ivl_signal_basename(lsig)); + } else if (ivl_signal_type(lsig)==IVL_SIT_UWIRE) { fprintf(vvp_out, " %%force/vec4/off v%p_0, %d;\n", lsig, offset_index); } else { @@ -390,6 +459,7 @@ member. We will use a property assign function. */ assert(!lsig); + ivl_type_t sub_type = draw_lval_expr(nest); assert(ivl_type_base(sub_type) == IVL_VT_CLASS); fprintf(vvp_out, " %%store/prop/v %d, %u;\n", @@ -400,7 +470,13 @@ /* No offset expression, so use simpler store function. */ assert(lsig); assert(lwid == ivl_signal_width(lsig)); - fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", lsig, lwid); + if (signal_is_return_value(lsig)) { + fprintf(vvp_out, " %%ret/vec4 0, 0, %u; Assign to %s (store_vec4_to_lval)\n", + lwid, ivl_signal_basename(lsig)); + } else { + fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", + lsig, lwid); + } } } } @@ -417,6 +493,7 @@ of the l-value. We need these values as part of the r-value calculation. */ if (ivl_stmt_opcode(net) != 0) { + fprintf(vvp_out, " ; show_stmt_assign_vector: Get l-value for compressed %c= operand\n", ivl_stmt_opcode(net)); slices = calloc(ivl_stmt_lvals(net), sizeof(struct vec_slice_info)); get_vec_from_lval(net, slices); } @@ -438,37 +515,6 @@ value pushed. */ fprintf(vvp_out, " %%cvt/vr %u;\n", wid); - } else if (ivl_expr_value(rval) == IVL_VT_STRING) { - /* Special case: string to vector casting */ - ivl_lval_t lval = ivl_stmt_lval(net, 0); - fprintf(vvp_out, " %%vpi_call %u %u \"$ivl_string_method$to_vec\", v%p_0, v%p_0 {0 0 0};\n", - ivl_file_table_index(ivl_stmt_file(net)), ivl_stmt_lineno(net), - ivl_expr_signal(rval), ivl_lval_sig(lval)); - if (slices) free(slices); - return 0; - - } else if (ivl_expr_value(rval) == IVL_VT_DARRAY) { - /* Special case: dynamic array to vector casting */ - ivl_lval_t lval = ivl_stmt_lval(net, 0); - void*rval_addr = NULL; - - /* Even more special case: function call returning dynamic array */ - if(ivl_expr_type(rval) == IVL_EX_UFUNC) { - rval_addr = ivl_scope_port(ivl_expr_def(rval), 0); - draw_ufunc_object(rval); - /* We do not need to store the result, it is going to be - converted to vector quite soon. */ - fprintf(vvp_out, " %%pop/obj 1, 0; drop the result\n"); - } else { - rval_addr = ivl_expr_signal(rval); - } - - fprintf(vvp_out, " %%vpi_call %u %u \"$ivl_darray_method$to_vec\", v%p_0, v%p_0 {0 0 0};\n", - ivl_file_table_index(ivl_stmt_file(net)), ivl_stmt_lineno(net), - rval_addr, ivl_lval_sig(lval)); - if (slices) free(slices); - return 0; - } else { unsigned wid = ivl_stmt_lwidth(net); draw_eval_vec4(rval); @@ -602,10 +648,17 @@ slice->type = REAL_SIMPLE_WORD; slice->u_.simple_word.use_word = use_word; - fprintf(vvp_out, " %%load/real v%p_%lu;\n", sig, use_word); + if (signal_is_return_value(sig)) { + assert(use_word==0); + fprintf(vvp_out, " %%retload/real 0;\n"); + } else { + fprintf(vvp_out, " %%load/real v%p_%lu;\n", sig, use_word); + } } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED + slice->type = REAL_MEMORY_WORD_STATIC; slice->u_.memory_word_static.use_word = use_word; if (use_word < ivl_signal_array_count(sig)) { @@ -618,6 +671,7 @@ } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { + assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = REAL_MEMORY_WORD_DYNAMIC; slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); @@ -636,6 +690,15 @@ { ivl_signal_t sig = ivl_lval_sig(lval); + /* Special Case: If the l-value signal is named after its scope, + and the scope is a function, then this is an assign to a return + value and should be handled differently. */ + if (signal_is_return_value(sig)) { + assert(slice->u_.simple_word.use_word == 0); + fprintf(vvp_out, " %%ret/real 0;\n"); + return; + } + switch (slice->type) { default: fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); @@ -676,6 +739,18 @@ var = ivl_lval_sig(lval); assert(var != 0); + /* Special Case: If the l-value signal is named after its scope, + and the scope is a function, then this is an assign to a return + value and should be handled differently. */ + ivl_scope_t sig_scope = ivl_signal_scope(var); + if ((ivl_scope_type(sig_scope) == IVL_SCT_FUNCTION) + && (strcmp(ivl_signal_basename(var), ivl_scope_basename(sig_scope)) == 0)) { + assert(ivl_signal_dimensions(var) == 0); + fprintf(vvp_out, " %%ret/real 0; Assign to %s\n", + ivl_signal_basename(var)); + return; + } + if (ivl_signal_dimensions(var) == 0) { fprintf(vvp_out, " %%store/real v%p_0;\n", var); return; @@ -788,6 +863,19 @@ assert(ivl_stmt_lvals(net) == 1); assert(ivl_stmt_opcode(net) == 0); + /* Special case: If the l-value signal (string) is named after + its scope, and the scope is a function, then this is an + assign to a return value and should be handled + differently. */ + if (signal_is_return_value(var)) { + assert(ivl_signal_dimensions(var) == 0); + assert(part == 0 && aidx == 0); + draw_eval_string(rval); + fprintf(vvp_out, " %%ret/str 0; Assign to %s\n", + ivl_signal_basename(var)); + return 0; + } + /* Simplest case: no mux. Evaluate the r-value as a string and store the result into the variable. Note that the %store/str opcode pops the string result. */ @@ -848,12 +936,14 @@ ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); - ivl_signal_t var= ivl_lval_sig(lval); + ivl_signal_t var = ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); assert(ivl_type_base(var_type) == IVL_VT_DARRAY); ivl_type_t element_type = ivl_type_element(var_type); unsigned idx; + unsigned size_reg = allocate_word(); + #if 0 unsigned element_width = 1; if (ivl_type_base(element_type) == IVL_VT_BOOL) @@ -861,6 +951,16 @@ else if (ivl_type_base(element_type) == IVL_VT_LOGIC) element_width = width_of_packed_type(element_type); #endif + +// FIXME: At the moment we reallocate the array space. +// This probably should be a resize to avoid values glitching + /* Allocate at least enough space for the array patter. */ + fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", size_reg, ivl_expr_parms(rval)); + /* This can not have have a X/Z value so clear flag 4. */ + fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); + darray_new(element_type, size_reg); + fprintf(vvp_out, " %%store/obj v%p_0;\n", var); + assert(ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN); for (idx = 0 ; idx < ivl_expr_parms(rval) ; idx += 1) { switch (ivl_type_base(element_type)) { @@ -910,33 +1010,26 @@ assert(ivl_stmt_opcode(net) == 0); assert(part == 0); - if (mux && (ivl_type_base(element_type)==IVL_VT_REAL)) { + if (mux && (ivl_type_base(element_type) == IVL_VT_REAL)) { draw_eval_real(rval); - - /* The %set/dar expects the array index to be in index + /* The %store/dar/r expects the array index to be in index register 3. Calculate the index in place. */ draw_eval_expr_into_integer(mux, 3); - fprintf(vvp_out, " %%store/dar/r v%p_0;\n", var); - } else if (mux && ivl_type_base(element_type)==IVL_VT_STRING) { - - /* Evaluate the rval into the top of the string stack. */ + } else if (mux && ivl_type_base(element_type) == IVL_VT_STRING) { draw_eval_string(rval); - - /* The %store/dar/s expects the array index to me in index + /* The %store/dar/str expects the array index to me in index register 3. Calculate the index in place. */ draw_eval_expr_into_integer(mux, 3); - fprintf(vvp_out, " %%store/dar/str v%p_0;\n", var); } else if (mux) { draw_eval_vec4(rval); - + resize_vec4_wid(rval, ivl_stmt_lwidth(net)); /* The %store/dar/vec4 expects the array index to be in index register 3. Calculate the index in place. */ draw_eval_expr_into_integer(mux, 3); - fprintf(vvp_out, " %%store/dar/vec4 v%p_0;\n", var); } else if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { @@ -956,27 +1049,147 @@ return errors; } +/* + * This function handles the special case that we assign an array + * pattern to a queue. Handle this by assigning each element. + * The array pattern will have a fixed size. + */ +static int show_stmt_assign_queue_pattern(ivl_signal_t var, ivl_expr_t rval, + ivl_type_t element_type, int max_idx) +{ + int errors = 0; + unsigned idx; + unsigned max_size; + unsigned max_elems; + assert(ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN); + max_size = ivl_signal_array_count(var); + max_elems = ivl_expr_parms(rval); + if ((max_size != 0) && (max_elems > max_size)) { + fprintf(stderr, "%s:%u: Warning: Array pattern assignment has more elements " + "(%u) than bounded queue '%s' supports (%u).\n" + " Only using first %u elements.\n", + ivl_expr_file(rval), ivl_expr_lineno(rval), + max_elems, ivl_signal_basename(var), max_size, max_size); + max_elems = max_size; + } + for (idx = 0 ; idx < max_elems ; idx += 1) { + switch (ivl_type_base(element_type)) { + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + draw_eval_vec4(ivl_expr_parm(rval,idx)); + fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); + fprintf(vvp_out, " %%store/qdar/v v%p_0, %d, %u;\n", var, max_idx, + width_of_packed_type(element_type)); + break; + + case IVL_VT_REAL: + draw_eval_real(ivl_expr_parm(rval,idx)); + fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); + fprintf(vvp_out, " %%store/qdar/r v%p_0, %d;\n", var, max_idx); + break; + + case IVL_VT_STRING: + draw_eval_string(ivl_expr_parm(rval,idx)); + fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); + fprintf(vvp_out, " %%store/qdar/str v%p_0, %d;\n", var, max_idx); + break; + + default: + fprintf(vvp_out, "; ERROR: show_stmt_assign_queue_pattern: " + "type_base=%d not implemented\n", ivl_type_base(element_type)); + errors += 1; + break; + } + } + + if ((max_size == 0) || (max_elems < max_size)) { + int del_idx = allocate_word(); + assert(del_idx >= 0); + /* Save the first queue element to delete. */ + fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", del_idx, max_elems); + fprintf(vvp_out, " %%delete/tail v%p_0, %u;\n", var, del_idx); + clr_word(del_idx); + } + + return errors; +} + static int show_stmt_assign_sig_queue(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); + ivl_expr_t part = ivl_lval_part_off(lval); ivl_signal_t var= ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t mux = ivl_lval_idx(lval); + + assert(ivl_stmt_lvals(net) == 1); + assert(ivl_stmt_opcode(net) == 0); + assert(part == 0); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); - switch (ivl_expr_type(rval)) { - case IVL_EX_NULL: + int idx = allocate_word(); + assert(idx >= 0); + /* Save the queue maximum index value to an integer register. */ + fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", idx, ivl_signal_array_count(var)); + + if (ivl_expr_type(rval) == IVL_EX_NULL) { errors += draw_eval_object(rval); - break; - default: - fprintf(stderr, "XXXX: I don't know how to handle expr_type=%d here\n", ivl_expr_type(rval)); - fprintf(vvp_out, " ; XXXX expr_type=%d\n", ivl_expr_type(rval)); - errors += 1; - break; + fprintf(vvp_out, " %%store/obj v%p_0;\n", var); + + } else if (mux && (ivl_type_base(element_type) == IVL_VT_REAL)) { + draw_eval_real(rval); + /* The %store/qdar expects the array index to be in + index register 3. */ + draw_eval_expr_into_integer(mux, 3); + fprintf(vvp_out, " %%store/qdar/r v%p_0, %d;\n", var, idx); + + } else if (mux && ivl_type_base(element_type) == IVL_VT_STRING) { + draw_eval_string(rval); + /* The %store/qdar expects the array index to be in + index register 3. */ + draw_eval_expr_into_integer(mux, 3); + fprintf(vvp_out, " %%store/qdar/str v%p_0, %d;\n", var, idx); + + } else if (mux) { // What is left must be some form of vector + assert(ivl_type_base(element_type) == IVL_VT_BOOL || + ivl_type_base(element_type) == IVL_VT_LOGIC); + draw_eval_vec4(rval); + resize_vec4_wid(rval, ivl_stmt_lwidth(net)); + /* The %store/qdar expects the array index to be in + index register 3. */ + draw_eval_expr_into_integer(mux, 3); + fprintf(vvp_out, " %%store/qdar/v v%p_0, %d, %u;\n", var, idx, + width_of_packed_type(element_type)); + + } else if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { + /* There is no l-value mux, but the r-value is an array + pattern. This is a special case of an assignment to + the l-value. */ + errors += show_stmt_assign_queue_pattern(var, rval, element_type, idx); + + } else { + /* There is no l-value mux, so this must be an + assignment to the array as a whole. Evaluate the + "object", and store the evaluated result. */ + errors += draw_eval_object(rval); + if (ivl_type_base(element_type) == IVL_VT_REAL) + fprintf(vvp_out, " %%store/qobj/r v%p_0, %d;\n", var, idx); + else if (ivl_type_base(element_type) == IVL_VT_STRING) + fprintf(vvp_out, " %%store/qobj/str v%p_0, %d;\n", var, idx); + else { + assert(ivl_type_base(element_type) == IVL_VT_BOOL || + ivl_type_base(element_type) == IVL_VT_LOGIC); + fprintf(vvp_out, " %%store/qobj/v v%p_0, %d, %u;\n", + var, idx, width_of_packed_type(element_type)); + } } + clr_word(idx); - fprintf(vvp_out, " %%store/obj v%p_0;\n", var); return errors; } @@ -995,8 +1208,9 @@ ivl_type_t prop_type = ivl_type_prop_type(sig_type, prop_idx); if (ivl_type_base(prop_type) == IVL_VT_BOOL) { - assert(ivl_type_packed_dimensions(prop_type) == 1); - assert(ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0)); + assert(ivl_type_packed_dimensions(prop_type) == 0 || + (ivl_type_packed_dimensions(prop_type) == 1 && + ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0))); draw_eval_vec4(rval); if (ivl_expr_value(rval)!=IVL_VT_BOOL) @@ -1008,8 +1222,9 @@ fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_LOGIC) { - assert(ivl_type_packed_dimensions(prop_type) == 1); - assert(ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0)); + assert(ivl_type_packed_dimensions(prop_type) == 0 || + (ivl_type_packed_dimensions(prop_type) == 1 && + ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0))); draw_eval_vec4(rval); diff -Nru iverilog-10.3/tgt-vvp/vector.c iverilog-11.0/tgt-vvp/vector.c --- iverilog-10.3/tgt-vvp/vector.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/vector.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2002-2014 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -# include "vvp_priv.h" -# include diff -Nru iverilog-10.3/tgt-vvp/vvp.c iverilog-11.0/tgt-vvp/vvp.c --- iverilog-10.3/tgt-vvp/vvp.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/vvp.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -28,7 +28,7 @@ static const char*version_string = "Icarus Verilog VVP Code Generator " VERSION " (" VERSION_TAG ")\n\n" -"Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com)\n\n" +"Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com)\n\n" " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" " the Free Software Foundation; either version 2 of the License, or\n" @@ -50,7 +50,8 @@ int debug_draw = 0; -# define FLAGS_COUNT 256 +/* This needs to match the actual flag count in the VVP thread. */ +# define FLAGS_COUNT 512 static uint32_t allocate_flag_mask[FLAGS_COUNT / 32] = { 0x000000ff, 0 }; @@ -80,15 +81,16 @@ const char*cp = ivl_design_flag(des, "VPI_MODULE_LIST"); while (*cp) { - char buffer[128]; const char*comma = strchr(cp, ','); if (comma == 0) comma = cp + strlen(cp); + char*buffer = malloc(comma - cp + 1); strncpy(buffer, cp, comma-cp); buffer[comma-cp] = 0; fprintf(vvp_out, ":vpi_module \"%s\";\n", buffer); + free(buffer); cp = comma; if (*cp) cp += 1; @@ -108,7 +110,9 @@ return idx; } - return -1; + fprintf(stderr, "vvp.tgt error: Exceeded the maximum flag count of " + "%d during VVP code generation.\n", FLAGS_COUNT); + exit(1); } void clr_flag(int idx) @@ -170,20 +174,20 @@ long fl_value = strtol(fileline, &eptr, 0); /* Nothing usable in the file/line string. */ if (fileline == eptr) { - fprintf(stderr, "vvp error: Unable to extract file/line " + fprintf(stderr, "vvp.tgt error: Unable to extract file/line " "information from string: %s\n", fileline); return 1; } /* Extra stuff at the end. */ if (*eptr != 0) { - fprintf(stderr, "vvp error: Extra characters '%s' " + fprintf(stderr, "vvp.tgt error: Extra characters '%s' " "included at end of file/line string: %s\n", eptr, fileline); return 1; } /* The file/line flag must be positive. */ if (fl_value < 0) { - fprintf(stderr, "vvp error: File/line flag (%ld) must " + fprintf(stderr, "vvp.tgt error: File/line flag (%ld) must " "be positive.\n", fl_value); return 1; } diff -Nru iverilog-10.3/tgt-vvp/vvp_priv.h iverilog-11.0/tgt-vvp/vvp_priv.h --- iverilog-10.3/tgt-vvp/vvp_priv.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/vvp_priv.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_vvp_priv_H #define IVL_vvp_priv_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -120,7 +120,7 @@ * Note: draw_modpath drive_label must be malloc'ed by the * caller. This function will free the string sometime in the future. */ -extern void draw_modpath(ivl_signal_t path_sig, char*drive_label); +extern void draw_modpath(ivl_signal_t path_sig, char*drive_label, unsigned drive_index); extern void cleanup_modpath(void); /* @@ -133,6 +133,7 @@ extern void draw_vpi_func_call(ivl_expr_t expr); extern void draw_vpi_rfunc_call(ivl_expr_t expr); +extern void draw_vpi_sfunc_call(ivl_expr_t expr); extern void draw_class_in_scope(ivl_type_t classtype); @@ -208,6 +209,10 @@ */ extern int draw_eval_condition(ivl_expr_t expr); +/* + * Return true if the signal is the return value of a function. + */ +extern int signal_is_return_value(ivl_signal_t sig); extern int number_is_unknown(ivl_expr_t ex); extern int number_is_immediate(ivl_expr_t ex, unsigned lim_wid, int negative_is_ok); @@ -281,4 +286,7 @@ extern unsigned local_count; extern unsigned thread_count; +extern void darray_new(ivl_type_t element_type, unsigned size_reg); + + #endif /* IVL_vvp_priv_H */ diff -Nru iverilog-10.3/tgt-vvp/vvp_process.c iverilog-11.0/tgt-vvp/vvp_process.c --- iverilog-10.3/tgt-vvp/vvp_process.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/vvp_process.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -225,14 +225,9 @@ const unsigned long use_word = 0; - if (ivl_signal_type(sig) == IVL_SIT_UWIRE) { - fprintf(stderr, "%s:%u: tgt-vvp sorry: V10 does not support " - "mixed continuous and non-blocking assignments to " - "different parts of the same vector (%s).\n", - ivl_signal_file(sig), ivl_signal_lineno(sig), - ivl_signal_basename(sig)); - vvp_errors += 1; - } + const char*assign_op = "%assign"; + if (ivl_signal_type(sig) == IVL_SIT_UWIRE) + assign_op = "%force"; // Detect the case that this is actually a non-blocking assign // to an array word. In that case, run off somewhere else to @@ -272,8 +267,8 @@ to know to skip the assign. */ draw_eval_expr_into_integer(part_off_ex, offset_index); /* If the index expression has XZ bits, skip the assign. */ - fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", - sig, use_word, offset_index, delay_index); + fprintf(vvp_out, " %s/vec4/off/d v%p_%lu, %d, %d;\n", + assign_op, sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); @@ -282,8 +277,8 @@ int offset_index = allocate_word(); /* Event control delay... */ draw_eval_expr_into_integer(part_off_ex, offset_index); - fprintf(vvp_out, " %%assign/vec4/off/e v%p_%lu, %d;\n", - sig, use_word, offset_index); + fprintf(vvp_out, " %s/vec4/off/e v%p_%lu, %d;\n", + assign_op, sig, use_word, offset_index); fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); @@ -301,8 +296,8 @@ to know to skip the assign. */ draw_eval_expr_into_integer(part_off_ex, offset_index); /* If the index expression has XZ bits, skip the assign. */ - fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", - sig, use_word, offset_index, delay_index); + fprintf(vvp_out, " %s/vec4/off/d v%p_%lu, %d, %d;\n", + assign_op, sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); } @@ -315,8 +310,8 @@ fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", offset_index, part_off); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); - fprintf(vvp_out, " %%assign/vec4/off/e v%p_%lu, %d;\n", - sig, use_word, offset_index); + fprintf(vvp_out, " %s/vec4/off/e v%p_%lu, %d;\n", + assign_op, sig, use_word, offset_index); fprintf(vvp_out, " %%evctl/c;\n"); clr_word(offset_index); @@ -334,8 +329,8 @@ delay_index, low_d, hig_d); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); } - fprintf(vvp_out, " %%assign/vec4/off/d v%p_%lu, %d, %d;\n", - sig, use_word, offset_index, delay_index); + fprintf(vvp_out, " %s/vec4/off/d v%p_%lu, %d, %d;\n", + assign_op, sig, use_word, offset_index, delay_index); clr_word(offset_index); clr_word(delay_index); } @@ -344,13 +339,13 @@ /* Calculated delay... */ int delay_index = allocate_word(); draw_eval_expr_into_integer(dexp, delay_index); - fprintf(vvp_out, " %%assign/vec4/d v%p_%lu, %d;\n", - sig, use_word, delay_index); + fprintf(vvp_out, " %s/vec4/d v%p_%lu, %d;\n", + assign_op, sig, use_word, delay_index); clr_word(delay_index); } else if (nevents != 0) { /* Event control delay... */ - fprintf(vvp_out, " %%assign/vec4/e v%p_%lu;\n", - sig, use_word); + fprintf(vvp_out, " %s/vec4/e v%p_%lu;\n", + assign_op, sig, use_word); fprintf(vvp_out, " %%evctl/c;\n"); } else { @@ -362,12 +357,12 @@ int delay_index = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, %lu;\n", delay_index, low_d, hig_d); - fprintf(vvp_out, " %%assign/vec4/d v%p_%lu, %d;\n", - sig, use_word, delay_index); + fprintf(vvp_out, " %s/vec4/d v%p_%lu, %d;\n", + assign_op, sig, use_word, delay_index); clr_word(delay_index); } else { - fprintf(vvp_out, " %%assign/vec4 v%p_%lu, %lu;\n", - sig, use_word, low_d); + fprintf(vvp_out, " %s/vec4 v%p_%lu, %lu;\n", + assign_op, sig, use_word, low_d); } } } @@ -649,6 +644,7 @@ static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope) { int rc = 0; + ivl_case_quality_t qual = ivl_stmt_case_quality(net); ivl_expr_t expr = ivl_stmt_cond_expr(net); unsigned count = ivl_stmt_case_count(net); @@ -656,6 +652,12 @@ unsigned idx, default_case; + if (qual != IVL_CASE_QUALITY_BASIC && qual != IVL_CASE_QUALITY_PRIORITY) { + fprintf(stderr, "%s:%u: vvp.tgt sorry: " + "Case unique/unique0 qualities are ignored.\n", + ivl_stmt_file(net), ivl_stmt_lineno(net)); + } + show_stmt_file_line(net, "Case statement."); local_count += count + 1; @@ -714,6 +716,23 @@ ivl_statement_t cst = ivl_stmt_case_stmt(net, default_case); rc += show_statement(cst, sscope); } + /* Emit code to check unique and priority have handled a value + (when there is no default). */ + else if (default_case == count) { + switch (qual) { + case IVL_CASE_QUALITY_UNIQUE: + case IVL_CASE_QUALITY_PRIORITY: + fprintf(vvp_out, " %%vpi_call/w %u %u \"$warning\", " + "\"value is unhandled for priority or unique case statement\"" + " {0 0 0};\n", + ivl_file_table_index(ivl_stmt_file(net)), + ivl_stmt_lineno(net)); + break; + + default: + break; + } + } /* Jump to the out of the case. */ fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, @@ -863,7 +882,7 @@ /* We do not currently support using a word from a variable array as the L-value (SystemVerilog / Icarus extension). */ if (ivl_signal_type(lsig) == IVL_SIT_REG) { - fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " + fprintf(stderr, "%s:%u: vvp.tgt sorry: cannot %s to the " "word of a variable array (%s[%ld]).\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name, ivl_signal_basename(lsig), @@ -919,7 +938,7 @@ /* We do not currently support using a word from a variable array as the L-value (SystemVerilog / Icarus extension). */ if (ivl_signal_type(lsig) == IVL_SIT_REG) { - fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " + fprintf(stderr, "%s:%u: vvp.tgt sorry: cannot %s to the " "word of a variable array (%s[%ld]).\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name, ivl_signal_basename(lsig), @@ -965,7 +984,7 @@ ivl_expr_type(rval) == IVL_EX_REALNUM) return; - fprintf(stderr, "%s:%u: tgt-vvp sorry: procedural continuous " + fprintf(stderr, "%s:%u: vvp.tgt sorry: procedural continuous " "assignments are not yet fully supported. The RHS of " "this assignment will only be evaluated once, at the " "time the assignment statement is executed.\n", @@ -1009,7 +1028,7 @@ long use_wid = ivl_signal_width(rsig); long use_lsb, use_msb; if (ivl_signal_packed_dimensions(lsig) > 1) { - fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s part of a " + fprintf(stderr, "%s:%u: vvp.tgt sorry: cannot %s part of a " "packed array.\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name); @@ -1024,7 +1043,7 @@ use_msb = use_lsb; use_msb -= use_wid - 1; } - fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s signal to a ", + fprintf(stderr, "%s:%u: vvp.tgt sorry: cannot %s signal to a ", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name); if (use_wid > 1) { fprintf(stderr, "part select (%s[%ld:%ld]).\n", @@ -1051,7 +1070,7 @@ /* Normalize the array access. */ long real_word = use_lword; real_word += ivl_signal_array_base(lsig); - fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s to the " + fprintf(stderr, "%s:%u: vvp.tgt sorry: cannot %s to the " "word of a variable array (%s[%ld]).\n", ivl_stmt_file(net), ivl_stmt_lineno(net), command_name, ivl_signal_basename(lsig), real_word); @@ -1062,7 +1081,7 @@ if ((rword_idx = ivl_expr_oper1(rval)) != 0) { assert(ivl_signal_dimensions(rsig) != 0); if (!number_is_immediate(rword_idx, IMM_WID, 0)) { - fprintf(stderr, "%s:%u: tgt-vvp sorry: procedural continuous " + fprintf(stderr, "%s:%u: vvp.tgt sorry: procedural continuous " "assignments are not yet fully supported. The RHS of " "this assignment will only be evaluated once, at the " "time the assignment statement is executed.\n", @@ -1081,7 +1100,7 @@ /* Normalize the array access. */ long real_word = use_rword; real_word += ivl_signal_array_base(rsig); - fprintf(stderr, "%s:%u: tgt-vvp sorry: cannot %s from the " + fprintf(stderr, "%s:%u: vvp.tgt sorry: cannot %s from the " "word of a variable array (%s[%ld]).\n", ivl_expr_file(rval), ivl_expr_lineno(rval), command_name, ivl_signal_basename(rsig), real_word); @@ -1630,16 +1649,25 @@ show_stmt_file_line(net, "User task call."); - fprintf(vvp_out, " %%fork TD_%s", - vvp_mangle_id(ivl_scope_name(task))); - fprintf(vvp_out, ", S_%p;\n", task); - fprintf(vvp_out, " %%join;\n"); + if (ivl_scope_type(task) == IVL_SCT_FUNCTION) { + // A function called as a task is (presumably) a void function. + // Use the %callf/void instruction to call it. + fprintf(vvp_out, " %%callf/void TD_%s", + vvp_mangle_id(ivl_scope_name(task))); + fprintf(vvp_out, ", S_%p;\n", task); + } else { + fprintf(vvp_out, " %%fork TD_%s", + vvp_mangle_id(ivl_scope_name(task))); + fprintf(vvp_out, ", S_%p;\n", task); + fprintf(vvp_out, " %%join;\n"); + } return 0; } static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope) { + static unsigned int cascade_counter = 0; /* Look to see if this is a SystemVerilog wait fork. */ if ((ivl_stmt_nevent(net) == 1) && (ivl_stmt_events(net, 0) == 0)) { assert(ivl_statement_type(ivl_stmt_sub_stmt(net)) == IVL_ST_NOOP); @@ -1650,13 +1678,22 @@ show_stmt_file_line(net, "Event wait (@) statement."); - if (ivl_stmt_nevent(net) == 1) { + if (ivl_stmt_nevent(net) == 0) { + assert(ivl_stmt_needs_t0_trigger(net) == 1); + fprintf(vvp_out, " %%wait E_0x0;\n"); + } else if (ivl_stmt_nevent(net) == 1) { ivl_event_t ev = ivl_stmt_events(net, 0); - fprintf(vvp_out, " %%wait E_%p;\n", ev); + if (ivl_stmt_needs_t0_trigger(net)) { + fprintf(vvp_out, "Ewait_%u .event/or E_%p, E_0x0;\n", + cascade_counter, ev); + fprintf(vvp_out, " %%wait Ewait_%u;\n", cascade_counter); + cascade_counter += 1; + } else { + fprintf(vvp_out, " %%wait E_%p;\n", ev); + } } else { unsigned idx; - static unsigned int cascade_counter = 0; ivl_event_t ev = ivl_stmt_events(net, 0); fprintf(vvp_out, "Ewait_%u .event/or E_%p", cascade_counter, ev); @@ -1664,6 +1701,7 @@ ev = ivl_stmt_events(net, idx); fprintf(vvp_out, ", E_%p", ev); } + assert(ivl_stmt_needs_t0_trigger(net) == 0); fprintf(vvp_out, ";\n %%wait Ewait_%u;\n", cascade_counter); cascade_counter += 1; } @@ -1710,14 +1748,68 @@ show_stmt_file_line(net, "Delete object"); unsigned parm_count = ivl_stmt_parm_count(net); - if (parm_count < 1) + if ((parm_count < 1) || (parm_count > 2)) return 1; ivl_expr_t parm = ivl_stmt_parm(net, 0); assert(ivl_expr_type(parm) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm); - fprintf(vvp_out, " %%delete/obj v%p_0;\n", var); + /* If this is a queue then it can have an element to delete. */ + if (parm_count == 2) { + if (ivl_type_base(ivl_signal_net_type(var)) != IVL_VT_QUEUE) + return 1; + draw_eval_expr_into_integer(ivl_stmt_parm(net, 1), 3); + fprintf(vvp_out, " %%delete/elem v%p_0;\n", var); + } else { + fprintf(vvp_out, " %%delete/obj v%p_0;\n", var); + } + return 0; +} + +static int show_insert_method(ivl_statement_t net) +{ + show_stmt_file_line(net, "queue: insert"); + + unsigned parm_count = ivl_stmt_parm_count(net); + if (parm_count != 3) + return 1; + + ivl_expr_t parm0 = ivl_stmt_parm(net,0); + assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); + ivl_signal_t var = ivl_expr_signal(parm0); + ivl_type_t var_type = ivl_signal_net_type(var); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + /* Save the queue maximum index value to an integer register. */ + fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", idx, ivl_signal_array_count(var)); + + ivl_type_t element_type = ivl_type_element(var_type); + + ivl_expr_t parm1 = ivl_stmt_parm(net,1); + /* The %qinsert expects the array index to be in index register 3. */ + draw_eval_expr_into_integer(parm1, 3); + ivl_expr_t parm2 = ivl_stmt_parm(net,2); + switch (ivl_type_base(element_type)) { + case IVL_VT_REAL: + draw_eval_real(parm2); + fprintf(vvp_out, " %%qinsert/real v%p_0, %u;\n", + var, idx); + break; + case IVL_VT_STRING: + draw_eval_string(parm2); + fprintf(vvp_out, " %%qinsert/str v%p_0, %u;\n", + var, idx); + break; + default: + draw_eval_vec4(parm2); + fprintf(vvp_out, " %%qinsert/v v%p_0, %u, %u;\n", + var, idx, + width_of_packed_type(element_type)); + break; + } return 0; } @@ -1740,7 +1832,12 @@ assert(ivl_expr_type(parm0) == IVL_EX_SIGNAL); ivl_signal_t var = ivl_expr_signal(parm0); ivl_type_t var_type = ivl_signal_net_type(var); - assert(ivl_type_base(var_type)== IVL_VT_QUEUE); + assert(ivl_type_base(var_type) == IVL_VT_QUEUE); + + int idx = allocate_word(); + assert(idx >= 0); + /* Save the queue maximum index value to an integer register. */ + fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", idx, ivl_signal_array_count(var)); ivl_type_t element_type = ivl_type_element(var_type); @@ -1748,18 +1845,22 @@ switch (ivl_type_base(element_type)) { case IVL_VT_REAL: draw_eval_real(parm1); - fprintf(vvp_out, " %%store/%s/r v%p_0;\n", type_code, var); + fprintf(vvp_out, " %%store/%s/r v%p_0, %u;\n", + type_code, var, idx); break; case IVL_VT_STRING: draw_eval_string(parm1); - fprintf(vvp_out, " %%store/%s/str v%p_0;\n", type_code, var); + fprintf(vvp_out, " %%store/%s/str v%p_0, %u;\n", + type_code, var, idx); break; default: draw_eval_vec4(parm1); - fprintf(vvp_out, " %%store/%s/v v%p_0, %u;\n", - type_code, var, width_of_packed_type(element_type)); + fprintf(vvp_out, " %%store/%s/v v%p_0, %u, %u;\n", + type_code, var, idx, + width_of_packed_type(element_type)); break; } + clr_word(idx); return 0; } @@ -1771,6 +1872,9 @@ if (strcmp(stmt_name,"$ivl_darray_method$delete") == 0) return show_delete_method(net); + if (strcmp(stmt_name,"$ivl_queue_method$insert") == 0) + return show_insert_method(net); + if (strcmp(stmt_name,"$ivl_queue_method$push_front") == 0) return show_push_frontback_method(net, true); @@ -1993,6 +2097,7 @@ sig_name = ivl_signal_basename(lsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); + if (!port) continue; ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_INPUT) && (port_type != IVL_SIP_INOUT)) continue; @@ -2032,6 +2137,7 @@ sig_name = ivl_signal_basename(rsig); for (idx = 0; idx < ports; idx += 1) { ivl_signal_t port = ivl_scope_port(scope, idx); + if (!port) continue; ivl_signal_port_t port_type = ivl_signal_port(port); if ((port_type != IVL_SIP_OUTPUT) && (port_type != IVL_SIP_INOUT)) continue; @@ -2071,6 +2177,7 @@ ivl_statement_t stmt) { unsigned idx, ports, task_idx = 0; + unsigned is_void_func; unsigned count = ivl_stmt_block_count(stmt); unsigned lineno = ivl_stmt_lineno(stmt); ivl_scope_t task_scope = 0; @@ -2085,7 +2192,8 @@ if (ivl_statement_type(tmp) == IVL_ST_UTASK && !task_scope) { task_idx = idx; task_scope = ivl_stmt_call(tmp); - assert(ivl_scope_type(task_scope) == IVL_SCT_TASK); + assert((ivl_scope_type(task_scope) == IVL_SCT_TASK) || + (ivl_scope_type(task_scope) == IVL_SCT_FUNCTION)); continue; } return 0; @@ -2136,7 +2244,11 @@ } /* Verify that all the ports were defined. */ - for (idx = 0; idx < ports; idx += 1) { + is_void_func = (ivl_scope_type(task_scope) == IVL_SCT_FUNCTION); + if (is_void_func) { + assert(port_exprs[0].type == IVL_SIP_NONE); + } + for (idx = is_void_func; idx < ports; idx += 1) { if (port_exprs[idx].type == IVL_SIP_NONE) { free(port_exprs); return 0; @@ -2355,6 +2467,9 @@ break; case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: fprintf(vvp_out, " %%jmp T_%u;\n", thread_count); break; } @@ -2365,6 +2480,9 @@ case IVL_PR_INITIAL: case IVL_PR_ALWAYS: + case IVL_PR_ALWAYS_COMB: + case IVL_PR_ALWAYS_FF: + case IVL_PR_ALWAYS_LATCH: if (init_flag) { fprintf(vvp_out, " .thread T_%u, $init;\n", thread_count); } else if (push_flag) { diff -Nru iverilog-10.3/tgt-vvp/vvp_scope.c iverilog-11.0/tgt-vvp/vvp_scope.c --- iverilog-10.3/tgt-vvp/vvp_scope.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/tgt-vvp/vvp_scope.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -290,6 +290,16 @@ return rtn; } +int signal_is_return_value(ivl_signal_t sig) +{ + ivl_scope_t sig_scope = ivl_signal_scope(sig); + if (ivl_scope_type(sig_scope) != IVL_SCT_FUNCTION) + return 0; + if (strcmp(ivl_signal_basename(sig), ivl_scope_basename(sig_scope)) == 0) + return 1; + return 0; +} + /* * This tests a bufz device against an output receiver, and determines * if the device can be skipped. If this function returns false, then a @@ -449,6 +459,33 @@ break; } + /* Special Case: If this variable is the return value of a function, + then it need to exist as an actual variable. */ + if ((ivl_signal_data_type(sig)==IVL_VT_REAL) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is REAL return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + if ((ivl_signal_data_type(sig)==IVL_VT_STRING) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is string return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + if ((ivl_signal_data_type(sig)==IVL_VT_LOGIC) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is vec4 return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + if ((ivl_signal_data_type(sig)==IVL_VT_BOOL) + && signal_is_return_value(sig)) { + fprintf(vvp_out, "; Variable %s is bool return value of scope S_%p\n", + ivl_signal_basename(sig), ivl_signal_scope(sig)); + return; + } + const char *datatype_flag = ivl_signal_integer(sig) ? "/i" : ivl_signal_signed(sig)? "/s" : ""; const char *local_flag = local_flag_str(sig); @@ -503,13 +540,15 @@ swapped ? first: last, swapped ? last : first, msb, lsb); } else if (ivl_signal_data_type(sig) == IVL_VT_DARRAY) { - fprintf(vvp_out, "v%p_0 .var/darray \"%s\";%s\n", sig, + fprintf(vvp_out, "v%p_0 .var/darray \"%s\", %u;%s\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), + ivl_signal_width(sig), ivl_signal_local(sig)? " Local signal" : ""); } else if (ivl_signal_data_type(sig) == IVL_VT_QUEUE) { - fprintf(vvp_out, "v%p_0 .var/queue \"%s\";%s\n", sig, + fprintf(vvp_out, "v%p_0 .var/queue \"%s\", %u;%s\n", sig, vvp_mangle_name(ivl_signal_basename(sig)), + ivl_signal_width(sig), ivl_signal_local(sig)? " Local signal" : ""); } else if (ivl_signal_data_type(sig) == IVL_VT_STRING) { @@ -870,6 +909,32 @@ if (need_delay_flag) draw_logic_delay(lptr); } +static void draw_equiv_impl_in_scope(ivl_net_logic_t lptr) +{ + unsigned ninp; + const char *lval; + const char *rval; + const char*ltype = "?"; + + assert(width_of_nexus(ivl_logic_pin(lptr, 0)) == 1); + + assert(ivl_logic_pins(lptr) > 0); + ninp = ivl_logic_pins(lptr) - 1; + assert(ninp == 2); + + lval = draw_net_input(ivl_logic_pin(lptr, 1)); + rval = draw_net_input(ivl_logic_pin(lptr, 2)); + + if (ivl_logic_type(lptr) == IVL_LO_EQUIV) { + ltype = "EQUIV"; + } else { + assert(ivl_logic_type(lptr) == IVL_LO_IMPL); + ltype = "IMPL"; + } + + fprintf(vvp_out, "L_%p .functor %s 1, %s, %s, C4<0>, C4<0>;\n", lptr, ltype, lval, rval); +} + static void draw_logic_in_scope(ivl_net_logic_t lptr) { unsigned pdx; @@ -947,6 +1012,11 @@ ltype = "BUFIF1"; break; + case IVL_LO_EQUIV: + case IVL_LO_IMPL: + draw_equiv_impl_in_scope(lptr); + return; + case IVL_LO_NAND: ltype = "NAND"; lcasc = "AND"; @@ -1043,7 +1113,7 @@ } for (pdx = inst; pdx < (unsigned)ninp && pdx < inst+4 ; pdx += 1) { if (level) { - fprintf(vvp_out, ", L_%p/%d/%d", + fprintf(vvp_out, ", L_%p/%d/%u", lptr, level - 1, pdx*4); } else { fprintf(vvp_out, ", %s", input_strings[pdx]); @@ -1485,6 +1555,16 @@ type = "nee"; signed_string = ""; break; + case IVL_LPM_CMP_WEQ: + assert(dtc != IVL_VT_REAL); /* Should never get here! */ + type = "weq"; + signed_string = ""; + break; + case IVL_LPM_CMP_WNE: + assert(dtc != IVL_VT_REAL); /* Should never get here! */ + type = "wne"; + signed_string = ""; + break; default: assert(0); } @@ -1701,29 +1781,29 @@ nex = ivl_lpm_data(net,0); assert(nex); fprintf(vvp_out, "%s", draw_net_input(nex)); - assert(width_of_nexus(nex) == width);; + assert(width_of_nexus(nex) == width); nex = ivl_lpm_clk(net); assert(nex); - assert(width_of_nexus(nex) == 1);; + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); nex = ivl_lpm_enable(net); if (nex) { - assert(width_of_nexus(nex) == 1);; + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); } else { fprintf(vvp_out, ", C4<1>"); } if ( (nex = ivl_lpm_async_clr(net)) ) { - assert(width_of_nexus(nex) == 1);; + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); } if ( (nex = ivl_lpm_async_set(net)) ) { ivl_expr_t val = ivl_lpm_aset_value(net); - assert(width_of_nexus(nex) == 1);; + assert(width_of_nexus(nex) == 1); fprintf(vvp_out, ", %s", draw_net_input(nex)); if (val) { unsigned nbits = ivl_expr_width(val); @@ -1740,6 +1820,31 @@ fprintf(vvp_out, ";\n"); } +/* + * Emit a LATCH primitive. This uses the following syntax: + * + * .latch , ; + */ +static void draw_lpm_latch(ivl_lpm_t net) +{ + ivl_nexus_t nex; + + unsigned width = ivl_lpm_width(net); + fprintf(vvp_out, "L_%p .latch %u ", net, width); + + nex = ivl_lpm_data(net,0); + assert(nex); + fprintf(vvp_out, "%s", draw_net_input(nex)); + assert(width_of_nexus(nex) == width); + + nex = ivl_lpm_enable(net); + assert(nex); + assert(width_of_nexus(nex) == 1); + fprintf(vvp_out, ", %s", draw_net_input(nex)); + + fprintf(vvp_out, ";\n"); +} + static void draw_lpm_shiftl(ivl_lpm_t net) { unsigned width = ivl_lpm_width(net); @@ -1879,6 +1984,19 @@ ivl_variable_type_t dt = data_type_of_nexus(ivl_lpm_q(net)); const char*dly = draw_lpm_output_delay(net, dt); + const char*type_string = ""; + switch (dt) { + case IVL_VT_REAL: + type_string = "/real"; + break; + case IVL_VT_BOOL: + case IVL_VT_LOGIC: + type_string = "/vec4"; + break; + default: + break; + } + /* Get all the input labels that I will use for net signals that connect to the inputs of the function. */ ninp = ivl_lpm_size(net); @@ -1891,7 +2009,7 @@ vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net), ivl_lpm_trigger(net)); else - fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly, + fprintf(vvp_out, "L_%p%s .ufunc%s TD_%s, %u", net, dly, type_string, vvp_mangle_id(ivl_scope_name(def)), ivl_lpm_width(net)); @@ -1919,7 +2037,7 @@ } fprintf(vvp_out, ")"); - +#if 0 /* Now print the reference to the signal from which the result is collected. */ { ivl_signal_t psig = ivl_scope_port(def, 0); @@ -1928,7 +2046,7 @@ fprintf(vvp_out, " v%p_0", psig); } - +#endif /* Finally, print the scope identifier. */ fprintf(vvp_out, " S_%p;\n", def); } @@ -2057,6 +2175,10 @@ draw_lpm_ff(net); return; + case IVL_LPM_LATCH: + draw_lpm_latch(net); + return; + case IVL_LPM_CMP_EEQ: case IVL_LPM_CMP_EQ: case IVL_LPM_CMP_EQX: @@ -2065,6 +2187,8 @@ case IVL_LPM_CMP_GT: case IVL_LPM_CMP_NE: case IVL_LPM_CMP_NEE: + case IVL_LPM_CMP_WEQ: + case IVL_LPM_CMP_WNE: draw_lpm_cmp(net); return; @@ -2142,6 +2266,9 @@ const char *type; const char*prefix = ivl_scope_is_auto(net) ? "auto" : ""; + char suffix[32]; + + suffix[0] = 0; switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: type = "module"; break; @@ -2155,8 +2282,40 @@ default: type = "?"; assert(0); } - fprintf(vvp_out, "S_%p .scope %s%s, \"%s\" \"%s\" %u %u", - net, prefix, type, + if (ivl_scope_type(net)==IVL_SCT_FUNCTION) { + switch (ivl_scope_func_type(net)) { + case IVL_VT_LOGIC: + snprintf(suffix, sizeof suffix, ".vec4.%c%u", + ivl_scope_func_signed(net)? 'u' : 's', + ivl_scope_func_width(net)); + break; + case IVL_VT_BOOL: + snprintf(suffix, sizeof suffix, ".vec2.%c%u", + ivl_scope_func_signed(net)? 'u' : 's', + ivl_scope_func_width(net)); + break; + case IVL_VT_REAL: + snprintf(suffix, sizeof suffix, ".real"); + break; + case IVL_VT_STRING: + snprintf(suffix, sizeof suffix, ".str"); + break; + case IVL_VT_CLASS: + case IVL_VT_DARRAY: + case IVL_VT_QUEUE: + snprintf(suffix, sizeof suffix, ".obj"); + break; + case IVL_VT_VOID: + snprintf(suffix, sizeof suffix, ".void"); + break; + default: + assert(0); + break; + } + } + + fprintf(vvp_out, "S_%p .scope %s%s%s, \"%s\" \"%s\" %u %u", + net, prefix, type, suffix, vvp_mangle_name(ivl_scope_basename(net)), vvp_mangle_name(ivl_scope_tname(net)), ivl_file_table_index(ivl_scope_file(net)), @@ -2183,7 +2342,7 @@ unsigned width = ivl_scope_mod_module_port_width(net,idx); if( name == 0 ) name = ""; - fprintf( vvp_out, " .port_info %u %s %u \"%s\"\n", + fprintf( vvp_out, " .port_info %u %s %u \"%s\";\n", idx, vvp_port_info_type_str(ptype), width, vvp_mangle_name(name) ); } diff -Nru iverilog-10.3/.travis.yml iverilog-11.0/.travis.yml --- iverilog-10.3/.travis.yml 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/.travis.yml 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,71 @@ +language: cpp +sudo: required +dist: xenial + +addons: + apt: + packages: + - gperf + +sudo: false + +notifications: + email: false + +jobs: + include: + + - stage: Test + os: linux + dist: xenial + before_install: + - git clone https://github.com/steveicarus/ivtest.git + - export PATH=$HOME/bin:$PATH + script: + - autoconf + - ./configure --prefix=$HOME + - make install + - make check + - cd ivtest + - perl vvp_reg.pl + - diff regression_report-devel.txt regression_report.txt + - perl vpi_reg.pl + + - stage: Test + os: linux + dist: bionic + before_install: + - git clone https://github.com/steveicarus/ivtest.git + - export PATH=$HOME/bin:$PATH + script: + - autoconf + - ./configure --prefix=$HOME + - make install + - make check + - cd ivtest + - perl vvp_reg.pl + - diff regression_report-devel.txt regression_report.txt + - perl vpi_reg.pl + + - stage: Test + os: windows + before_install: + - git clone https://github.com/steveicarus/ivtest.git + - choco uninstall -y mingw + - choco upgrade --no-progress -y msys2 + - export msys2='cmd //C RefreshEnv.cmd ' + - export msys2+='& set MSYS=winsymlinks:nativestrict ' + - export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start' + - export mingw64="$msys2 -mingw64 -full-path -here -c "\"\$@"\" --" + - export msys2+=" -msys2 -c "\"\$@"\" --" + - $msys2 pacman --sync --noconfirm --needed base-devel mingw-w64-x86_64-toolchain + script: + - $mingw64 ./autoconf.sh + - $mingw64 ./configure + - $mingw64 make install + - $mingw64 make check + - cd ivtest + - $mingw64 perl update_msys2_report.pl + - $mingw64 perl vvp_reg.pl + - diff --strip-trailing-cr regression_report-msys2.txt regression_report.txt + - $mingw64 perl vpi_reg.pl diff -Nru iverilog-10.3/verilog.spec iverilog-11.0/verilog.spec --- iverilog-10.3/verilog.spec 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/verilog.spec 2020-09-26 22:44:25.000000000 +0000 @@ -1,6 +1,5 @@ #norootforbuild # -%define rev_date 20190815 # Normally, the suff-ix is %nil, meaning the suffix is to not be used. # But if the builder wants to make a suffixed package, he may set this # to a value (i.e. -test) to cause suffixes to be put in all the right @@ -10,7 +9,7 @@ # Summary: Icarus Verilog Name: verilog%{suff} -Version: 10.3 +Version: 11.0 Release: 0 License: GPL Group: Productivity/Scientific/Electronics @@ -32,7 +31,7 @@ to the IEEE-1364 standard. %prep -%setup -n verilog-%{version} +%setup -n verilog%{suff}-%{version} %build if test X%{suff} != X @@ -92,16 +91,12 @@ %attr(-,root,root) %{_libdir}/ivl%{suff}/pcb.tgt %attr(-,root,root) %{_libdir}/ivl%{suff}/pcb.conf %attr(-,root,root) %{_libdir}/ivl%{suff}/pcb-s.conf -%attr(-,root,root) %{_libdir}/ivl%{suff}/system.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/system.vpi -%attr(-,root,root) %{_libdir}/ivl%{suff}/va_math.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/va_math.vpi -%attr(-,root,root) %{_libdir}/ivl%{suff}/v2005_math.sft %attr(-,root,root) %{_libdir}/ivl%{suff}/v2005_math.vpi -%attr(-,root,root) %{_libdir}/ivl%{suff}/v2009.sft %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.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 -Nru iverilog-10.3/verinum.h iverilog-11.0/verinum.h --- iverilog-10.3/verinum.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/verinum.h 2020-09-26 22:44:25.000000000 +0000 @@ -42,9 +42,9 @@ enum V { V0 = 0, V1, Vx, Vz }; verinum(); - verinum(const string&str); + explicit verinum(const string&str); verinum(const V*v, unsigned nbits, bool has_len =true); - verinum(V, unsigned nbits =1, bool has_len =true); + explicit verinum(V, unsigned nbits =1, bool has_len =true); verinum(uint64_t val, unsigned bits); verinum(double val, bool); verinum(const verinum&); diff -Nru iverilog-10.3/veriuser.h iverilog-11.0/veriuser.h --- iverilog-10.3/veriuser.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/veriuser.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef VERIUSER_H #define VERIUSER_H /* - * Copyright (c) 2002-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2019 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 @@ -152,6 +152,9 @@ */ extern s_tfcell veriusertfs[]; extern void veriusertfs_register_table(p_tfcell vtable); +#if defined(__MINGW32__) || defined (__CYGWIN__) +extern __declspec(dllexport) void (*vlog_startup_routines[])(void); +#endif #define usertask 1 #define userfunction 2 diff -Nru iverilog-10.3/version_base.h iverilog-11.0/version_base.h --- iverilog-10.3/version_base.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/version_base.h 2020-09-26 22:44:25.000000000 +0000 @@ -3,8 +3,8 @@ * Edit this definition in version_base.in to define the base version * number for the compiled result. */ -# define VERSION_MAJOR 10 -# define VERSION_MINOR 3 +# define VERSION_MAJOR 11 +# define VERSION_MINOR 0 /* * This will be appended to the version. Use this to mark development diff -Nru iverilog-10.3/version.c iverilog-11.0/version.c --- iverilog-10.3/version.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/version.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009 Stephen Williams (steve@icarus.com) + * Copyright (c) 2009-2015 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 @@ -29,10 +29,10 @@ if (cp[0] == '%' && cp[1] != 0) { switch (cp[1]) { case 'M': - fprintf(stdout, "%u", VERSION_MAJOR); + fprintf(stdout, "%d", VERSION_MAJOR); break; case 'n': - fprintf(stdout, "%u", VERSION_MINOR); + fprintf(stdout, "%d", VERSION_MINOR); break; case 'E': fprintf(stdout, "%s", VERSION_EXTRA); diff -Nru iverilog-10.3/vhdlpp/architec.cc iverilog-11.0/vhdlpp/architec.cc --- iverilog-10.3/vhdlpp/architec.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/architec.cc 2020-09-26 22:44:25.000000000 +0000 @@ -20,6 +20,7 @@ # include "architec.h" # include "expression.h" # include "parse_types.h" +# include "sequential.h" // Need this for parse_errors? # include "parse_api.h" # include @@ -28,7 +29,7 @@ Architecture::Architecture(perm_string name, const ActiveScope&ref, list&s) -: Scope(ref), name_(name), cur_component_(NULL) +: Scope(ref), name_(name), cur_component_(NULL), cur_process_(NULL) { statements_.splice(statements_.end(), s); } @@ -67,6 +68,14 @@ return false; } +Variable* Architecture::find_variable(perm_string by_name) const +{ + if(cur_process_) + return cur_process_->find_variable(by_name); + + return ScopeBase::find_variable(by_name); +} + void Architecture::push_genvar_type(perm_string gname, const VType*gtype) { genvar_type_t tmp; @@ -136,7 +145,7 @@ } ForGenerate::ForGenerate(perm_string gname, perm_string genvar, - prange_t*rang, std::list&s) + ExpRange*rang, std::list&s) : GenerateStatement(gname, s), genvar_(genvar), lsb_(rang->lsb()), msb_(rang->msb()) { @@ -177,6 +186,21 @@ delete lval_; } +CondSignalAssignment::CondSignalAssignment(ExpName*target, std::list&options) +: lval_(target) +{ + options_.splice(options_.end(), options); +} + +CondSignalAssignment::~CondSignalAssignment() +{ + delete lval_; + for(list::iterator it = options_.begin(); + it != options_.end(); ++it) { + delete *it; + } +} + ComponentInstantiation::ComponentInstantiation(perm_string i, perm_string c, list*parms, list*ports) @@ -229,18 +253,34 @@ return p->second; } +StatementList::StatementList(std::list*statement_list) +{ + if(statement_list) + statements_.splice(statements_.end(), *statement_list); +} + +StatementList::~StatementList() +{ + for(std::list::iterator it = statements_.begin(); + it != statements_.end(); ++it) { + delete *it; + } +} ProcessStatement::ProcessStatement(perm_string iname, + const ActiveScope&ref, std::list*sensitivity_list, std::list*statements_list) -: iname_(iname) +: StatementList(statements_list), Scope(ref), iname_(iname) { if (sensitivity_list) sensitivity_list_.splice(sensitivity_list_.end(), *sensitivity_list); - if (statements_list) - statements_list_.splice(statements_list_.end(), *statements_list); } ProcessStatement::~ProcessStatement() { + for(std::list::iterator it = sensitivity_list_.begin(); + it != sensitivity_list_.end(); ++it) { + delete *it; + } } diff -Nru iverilog-10.3/vhdlpp/architec_debug.cc iverilog-11.0/vhdlpp/architec_debug.cc --- iverilog-10.3/vhdlpp/architec_debug.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/architec_debug.cc 2020-09-26 22:44:25.000000000 +0000 @@ -95,6 +95,42 @@ } } +void CondSignalAssignment::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "CondSignalAssignment file=" << get_fileline() << endl; + lval_->dump(out, indent+1); + out << setw(indent+2) << "" << "<= ..." << endl; + + for(list::const_iterator it = options_.begin(); + it != options_.end(); ++it) { + (*it)->dump(out, indent+2); + } +} + +void StatementList::dump(ostream&out, int indent) const +{ + out << setw(indent+3) << "" << "sequence of statements:" << endl; + + for (list::const_iterator cur = statements_.begin() + ; cur != statements_.end() ; ++cur) { + (*cur)->dump(out, indent+4); + } +} + +void InitialStatement::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "InitialStatement file=" << get_fileline() << endl; + + StatementList::dump(out, indent); +} + +void FinalStatement::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "FinalStatement file=" << get_fileline() << endl; + + StatementList::dump(out, indent); +} + void ProcessStatement::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ProcessStatement name_=" << iname_ @@ -107,10 +143,5 @@ (*cur)->dump(out, indent+4); } - out << setw(indent+3) << "" << "sequence of statements:" << endl; - - for (list::const_iterator cur = statements_list_.begin() - ; cur != statements_list_.end() ; ++cur) { - (*cur)->dump(out, indent+4); - } + StatementList::dump(out, indent); } diff -Nru iverilog-10.3/vhdlpp/architec_elaborate.cc iverilog-11.0/vhdlpp/architec_elaborate.cc --- iverilog-10.3/vhdlpp/architec_elaborate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/architec_elaborate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -21,6 +21,7 @@ # include "entity.h" # include "expression.h" # include "sequential.h" +# include "subprogram.h" # include # include @@ -44,21 +45,39 @@ // Elaborate initializer expressions for signals & variables for (map::iterator cur = old_signals_.begin() ; cur != old_signals_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } for (map::iterator cur = new_signals_.begin() ; cur != new_signals_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } for (map::iterator cur = old_variables_.begin() ; cur != old_variables_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } for (map::iterator cur = new_variables_.begin() ; cur != new_variables_.end() ; ++cur) { - cur->second->elaborate_init_expr(entity, this); + cur->second->elaborate(entity, this); } + // Elaborate subprograms + for (map::const_iterator cur = cur_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + errors += (*it)->elaborate(); + } + } + // Create 'initial' and 'final' blocks for implicit + // initialization and clean-up actions + if(!initializers_.empty()) + statements_.push_front(new InitialStatement(&initializers_)); + + if(!finalizers_.empty()) + statements_.push_front(new FinalStatement(&finalizers_)); + for (list::iterator cur = statements_.begin() ; cur != statements_.end() ; ++cur) { @@ -167,141 +186,33 @@ return errors; } -/* - * This method attempts to rewrite the process content as an - * always-@(n-edge ) version of the same statement. This makes - * for a more natural translation to Verilog, if it comes to that. - */ -int ProcessStatement::rewrite_as_always_edge_(Entity*, Architecture*) +int StatementList::elaborate(Entity*ent, ScopeBase*scope) { - // If there are multiple sensitivity expressions, I give up. - if (sensitivity_list_.size() != 1) - return -1; - - // If there are multiple statements, I give up. - if (statements_list_.size() != 1) - return -1; - - Expression*se = sensitivity_list_.front(); - SequentialStmt*stmt_raw = statements_list_.front(); - - // If the statement is not an if-statement, I give up. - IfSequential*stmt = dynamic_cast (stmt_raw); - if (stmt == 0) - return -1; - - // If the "if" statement has a false clause, then give up. - if (stmt->false_size() != 0) - return -1; - - const Expression*ce_raw = stmt->peek_condition(); - - // Here we expect the condition to be - // 'event AND ='1'. - // So if ce_raw is not a logical AND, I give up. - const ExpLogical*ce = dynamic_cast (ce_raw); - if (ce == 0) - return -1; - if (ce->logic_fun() != ExpLogical::AND) - return -1; - - const Expression*op1_raw = ce->peek_operand1(); - const Expression*op2_raw = ce->peek_operand2(); - if (dynamic_cast(op2_raw)) { - const Expression*tmp = op1_raw; - op1_raw = op2_raw; - op2_raw = tmp; - } - - // If operand1 is not an 'event attribute, I give up. - const ExpAttribute*op1 = dynamic_cast(op1_raw); - if (op1 == 0) - return -1; - if (op1->peek_attribute() != "event") - return -1; - - const ExpRelation*op2 = dynamic_cast(op2_raw); - if (op2 == 0) - return -1; - if (op2->relation_fun() != ExpRelation::EQ) - return -1; - - const Expression*op2a_raw = op2->peek_operand1(); - const Expression*op2b_raw = op2->peek_operand2(); - - if (dynamic_cast(op2a_raw)) { - const Expression*tmp = op2b_raw; - op2b_raw = op2a_raw; - op2a_raw = tmp; - } - - if (! se->symbolic_compare(op1->peek_base())) - return -1; - - const ExpCharacter*op2b = dynamic_cast(op2b_raw); - if (op2b->value() != '1' && op2b->value() != '0') - return -1; - - // We've matched this pattern: - // process () if ('event and = ) then ... - // And we can convert it to: - // always @(edge ) ... - - // Replace the sensitivity expression with an edge - // expression. The ExpEdge expression signals that this is an - // always-@(edge) statement. - ExpEdge*edge = new ExpEdge(op2b->value()=='1'? ExpEdge::POSEDGE : ExpEdge::NEGEDGE, se); - assert(sensitivity_list_.size() == 1); - sensitivity_list_.pop_front(); - sensitivity_list_.push_front(edge); - - // Replace the statement with the body of the always - // statement, which is the true clause of the top "if" - // statement. There should be no "else" clause. - assert(statements_list_.size() == 1); - statements_list_.pop_front(); - - stmt->extract_true(statements_list_); - - delete stmt; - - return 0; -} - -/* - * Change the "process () " into "always @() ..." - */ -int ProcessStatement::extract_anyedge_(Entity*, Architecture*) -{ - - vector se; - while (! sensitivity_list_.empty()) { - se.push_back(sensitivity_list_.front()); - sensitivity_list_.pop_front(); - } + int errors = 0; - for (size_t idx = 0 ; idx < se.size() ; idx += 1) { - ExpEdge*edge = new ExpEdge(ExpEdge::ANYEDGE, se[idx]); - FILE_NAME(edge, se[idx]); - sensitivity_list_.push_back(edge); + for (std::list::iterator it = statements_.begin(); + it != statements_.end(); ++it) { + errors += (*it)->elaborate(ent, scope); } - return 0; + return errors; } int ProcessStatement::elaborate(Entity*ent, Architecture*arc) { int errors = 0; - if (rewrite_as_always_edge_(ent, arc) >= 0) { - extract_anyedge_(ent, arc); - } + arc->set_cur_process(this); - for (list::iterator cur = statements_list_.begin() - ; cur != statements_list_.end() ; ++cur) { - errors += (*cur)->elaborate(ent, arc); + for (map::iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + cur->second->elaborate(ent, arc); } + StatementList::elaborate(ent, arc); + + arc->set_cur_process(NULL); + return errors; } @@ -330,4 +241,49 @@ } return errors; +} + +int CondSignalAssignment::elaborate(Entity*ent, Architecture*arc) +{ + int errors = 0; + + // Visitor to extract signal names occurring in the conditional + // statements to create the sensitivity list + struct name_extractor_t : public ExprVisitor { + name_extractor_t(list& name_list) + : name_list_(name_list) {} + void operator() (Expression*s) { + if(const ExpName*name = dynamic_cast(s)) + name_list_.push_back(name); + } + + private: + list& name_list_; + } name_extractor(sens_list_); + + // Elaborate the l-value expression. + errors += lval_->elaborate_lval(ent, arc, true); + + // The elaborate_lval should have resolved the type of the + // l-value expression. We'll use that type to elaborate the + // r-value. + const VType*lval_type = lval_->peek_type(); + if (lval_type == 0) { + if (errors == 0) { + errors += 1; + cerr << get_fileline() + << ": error: Unable to calculate type for l-value expression." + << endl; + } + return errors; + } + + for(list::iterator it = options_.begin(); + it != options_.end(); ++it) { + ExpConditional::case_t*cas = (*it); + cas->elaborate_expr(ent, arc, lval_type); + cas->visit(name_extractor); + } + + return errors; } diff -Nru iverilog-10.3/vhdlpp/architec_emit.cc iverilog-11.0/vhdlpp/architec_emit.cc --- iverilog-10.3/vhdlpp/architec_emit.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/architec_emit.cc 2020-09-26 22:44:25.000000000 +0000 @@ -23,42 +23,33 @@ # include "sequential.h" # include "subprogram.h" # include "vsignal.h" +# include "std_types.h" # include # include # include -int Scope::emit_signals(ostream&out, Entity*entity, Architecture*arc) +int Scope::emit_signals(ostream&out, Entity*entity, ScopeBase*scope) { - int errors = 0; - - for (map::iterator cur = old_signals_.begin() - ; cur != old_signals_.end() ; ++cur) { + int errors = 0; - errors += cur->second->emit(out, entity, arc); - } - for (map::iterator cur = new_signals_.begin() - ; cur != new_signals_.end() ; ++cur) { + for (map::iterator cur = new_signals_.begin() + ; cur != new_signals_.end() ; ++cur) { + errors += cur->second->emit(out, entity, scope); + } - errors += cur->second->emit(out, entity, arc); - } - return errors; + return errors; } -int Scope::emit_variables(ostream&out, Entity*entity, Architecture*arc) +int Scope::emit_variables(ostream&out, Entity*entity, ScopeBase*scope) { - int errors = 0; - - for (map::iterator cur = old_variables_.begin() - ; cur != old_variables_.end() ; ++cur) { + int errors = 0; - errors += cur->second->emit(out, entity, arc); - } - for (map::iterator cur = new_variables_.begin() - ; cur != new_variables_.end() ; ++cur) { + for (map::iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + errors += cur->second->emit(out, entity, scope); + } - errors += cur->second->emit(out, entity, arc); - } - return errors; + return errors; } int Architecture::emit(ostream&out, Entity*entity) @@ -70,15 +61,16 @@ // of the full definition. typedef_context_t typedef_ctx; - //for (map::iterator cur = use_types_.begin() - //; cur != use_types_.end() ; ++cur) { + for (map::iterator cur = use_types_.begin() + ; cur != use_types_.end() ; ++cur) { + if(is_global_type(cur->first)) + continue; - //if(const VTypeDef*def = dynamic_cast(cur->second)) - //errors += def->emit_typedef(out, typedef_ctx); - //} + if(const VTypeDef*def = dynamic_cast(cur->second)) + errors += def->emit_typedef(out, typedef_ctx); + } for (map::iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { - if(const VTypeDef*def = dynamic_cast(cur->second)) errors += def->emit_typedef(out, typedef_ctx); } @@ -101,11 +93,18 @@ errors += emit_signals(out, entity, this); errors += emit_variables(out, entity, this); - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++ cur) { - // Do not emit unbounded functions, we will just need fixed instances later - if(!cur->second->unbounded()) - errors += cur->second->emit_package(out); + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + SubprogramHeader*subp = *it; + + // Do not emit unbounded functions, we will just need fixed instances later + if(!subp->unbounded()) + errors += subp->emit_package(out); + } } for (list::iterator cur = statements_.begin() @@ -130,19 +129,76 @@ int errors = 0; ivl_assert(*this, rval_.size() == 1); - Expression*rval = rval_.front(); + const Expression*rval = rval_.front(); out << "// " << get_fileline() << endl; out << "assign "; + if(const ExpDelay*delayed = dynamic_cast(rval)) { + out << "#("; + delayed->peek_delay()->emit(out, ent, arc); + out << ") "; + rval = delayed->peek_expr(); + } errors += lval_->emit(out, ent, arc); out << " = "; - errors += rval->emit(out, ent, arc); - out << ";" << endl; + return errors; } +int CondSignalAssignment::emit(ostream&out, Entity*ent, Architecture*arc) +{ + int errors = 0; + + out << "// " << get_fileline() << endl; + out << "always begin" << endl; + bool first = true; + + for(list::iterator it = options_.begin(); + it != options_.end(); ++it) { + ExpConditional::case_t*cas = *it; + ivl_assert(*this, cas->true_clause().size() == 1); + const Expression*rval = cas->true_clause().front(); + + if(first) + first = false; + else + out << "else "; + + if(Expression*cond = cas->condition()) { + out << "if("; + cond->emit(out, ent, arc); + out << ") "; + } + + out << endl; + lval_->emit(out, ent, arc); + out << " = "; + rval->emit(out, ent, arc); + out << ";" << endl; + } + + // Sensitivity list + first = true; + out << "@("; + + for(list::const_iterator it = sens_list_.begin(); + it != sens_list_.end(); ++it) { + if(first) + first = false; + else + out << ","; + + errors += (*it)->emit(out, ent, arc); + } + + out << ");" << endl; + out << "end" << endl; + + return errors; +} + int ComponentInstantiation::emit(ostream&out, Entity*ent, Architecture*arc) { const char*comma = ""; @@ -246,6 +302,36 @@ return errors; } +int StatementList::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + for (std::list::iterator it = statements_.begin(); + it != statements_.end(); ++it) { + errors += (*it)->emit(out, ent, scope); + } + + return errors; +} + +int InitialStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + out << "initial begin" << endl; + int errors = StatementList::emit(out, ent, scope); + out << "end" << endl; + + return errors; +} + +int FinalStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + out << "final begin" << endl; + int errors = StatementList::emit(out, ent, scope); + out << "end" << endl; + + return errors; +} + /* * Emit a process statement using "always" syntax. * @@ -254,16 +340,25 @@ * beginning. In VHDL, all the statements are initially executed once * before blocking in the first wait on the sensitivity list. */ -int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*arc) +int ProcessStatement::emit(ostream&out, Entity*ent, Architecture*) { int errors = 0; - out << "always begin" << endl; + /* Check if the process has no sensitivity list and ends up with + * a final wait. If so, convert the process to an initial block. */ + const WaitStmt*wait_stmt = NULL; + if (!stmt_list().empty()) + wait_stmt = dynamic_cast(stmt_list().back()); + + if (wait_stmt && wait_stmt->type() == WaitStmt::FINAL) + out << "initial begin : "; + else + out << "always begin : "; - for (list::iterator cur = statements_list_.begin() - ; cur != statements_list_.end() ; ++cur) { - errors += (*cur)->emit(out, ent, arc); - } + out << peek_name() << endl; + + errors += emit_variables(out, ent, this); + errors += StatementList::emit(out, ent, this); if (! sensitivity_list_.empty()) { out << "@("; @@ -272,13 +367,12 @@ ; cur != sensitivity_list_.end() ; ++cur) { if (comma) out << comma; - errors += (*cur)->emit(out, ent, arc); + errors += (*cur)->emit(out, ent, this); comma = ", "; } - out << ") /* sensitivity list for process */;" << endl; + out << "); /* sensitivity list for process */" << endl; } - out << "end" << endl; + out << "end /* " << peek_name() << " */" << endl; return errors; - } diff -Nru iverilog-10.3/vhdlpp/architec.h iverilog-11.0/vhdlpp/architec.h --- iverilog-10.3/vhdlpp/architec.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/architec.h 2020-09-26 22:44:25.000000000 +0000 @@ -30,10 +30,11 @@ class Expression; class ExpName; class GenerateStatement; +class ProcessStatement; class SequentialStmt; class Signal; class named_expr_t; -class prange_t; +class ExpRange; /* * The Architecture class carries the contents (name, statements, @@ -65,9 +66,20 @@ perm_string get_name() const { return name_; } - // Sets the currently processed component (to be able to reach its parameters). - void set_cur_component(ComponentInstantiation*component) { cur_component_ = component; } bool find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const; + Variable* find_variable(perm_string by_name) const; + + // Sets the currently processed component (to be able to reach its parameters). + void set_cur_component(ComponentInstantiation*component) { + assert(!cur_component_ || !component); + cur_component_ = component; + } + + // Sets the currently elaborated process (to use its scope for variable resolving). + void set_cur_process(ProcessStatement*process) { + assert(!cur_process_ || !process); + cur_process_ = process; + } // Elaborate this architecture in the context of the given entity. int elaborate(Entity*entity); @@ -113,7 +125,8 @@ // Currently processed component (or NULL if none). ComponentInstantiation*cur_component_; - private: // Not implemented + // Currently elaborated process (or NULL if none). + ProcessStatement*cur_process_; }; /* @@ -142,7 +155,7 @@ public: ForGenerate(perm_string gname, perm_string genvar, - prange_t*rang, std::list&s); + ExpRange*rang, std::list&s); ~ForGenerate(); int elaborate(Entity*ent, Architecture*arc); @@ -189,6 +202,25 @@ std::list rval_; }; +class CondSignalAssignment : public Architecture::Statement { + + public: + CondSignalAssignment(ExpName*target, std::list&options); + ~CondSignalAssignment(); + + int elaborate(Entity*ent, Architecture*arc); + int emit(ostream&out, Entity*entity, Architecture*arc); + void dump(ostream&out, int ident =0) const; + + private: + ExpName*lval_; + std::list options_; + + // List of signals that should be emitted in the related process + // sensitivity list. It is filled during the elaboration step. + std::listsens_list_; +}; + class ComponentInstantiation : public Architecture::Statement { public: @@ -215,28 +247,67 @@ std::map port_map_; }; -class ProcessStatement : public Architecture::Statement { +class StatementList : public Architecture::Statement { + public: + StatementList(std::list*statement_list); + virtual ~StatementList(); + + int elaborate(Entity*ent, Architecture*arc) { + return elaborate(ent, static_cast(arc)); + } + + int emit(ostream&out, Entity*ent, Architecture*arc) { + return emit(out, ent, static_cast(arc)); + } + + virtual int elaborate(Entity*ent, ScopeBase*scope); + virtual int emit(ostream&out, Entity*entity, ScopeBase*scope); + virtual void dump(ostream&out, int indent =0) const; + + std::list& stmt_list() { return statements_; } + + private: + std::list statements_; +}; + +// There is no direct VHDL counterpart to SV 'initial' statement, +// but we can still use it during the translation process. +class InitialStatement : public StatementList { + public: + InitialStatement(std::list*statement_list) + : StatementList(statement_list) {} + + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void dump(ostream&out, int indent =0) const; +}; + +// There is no direct VHDL counterpart to SV 'final' statement, +// but we can still use it during the translation process. +class FinalStatement : public StatementList { + public: + FinalStatement(std::list*statement_list) + : StatementList(statement_list) {} + + int emit(ostream&out, Entity*entity, ScopeBase*scope); + void dump(ostream&out, int indent =0) const; +}; + +class ProcessStatement : public StatementList, public Scope { public: ProcessStatement(perm_string iname, + const ActiveScope&ref, std::list*sensitivity_list, std::list*statement_list); ~ProcessStatement(); - virtual int elaborate(Entity*ent, Architecture*arc); - virtual int emit(ostream&out, Entity*entity, Architecture*arc); - virtual void dump(ostream&out, int indent =0) const; - - private: - int rewrite_as_always_edge_(Entity*ent, Architecture*arc); - int extract_anyedge_(Entity*ent, Architecture*arc); + int elaborate(Entity*ent, Architecture*arc); + int emit(ostream&out, Entity*entity, Architecture*arc); + void dump(ostream&out, int indent =0) const; private: perm_string iname_; - std::list sensitivity_list_; - std::list statements_list_; - }; #endif /* IVL_architec_H */ diff -Nru iverilog-10.3/vhdlpp/compiler.cc iverilog-11.0/vhdlpp/compiler.cc --- iverilog-10.3/vhdlpp/compiler.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/compiler.cc 2020-09-26 22:44:25.000000000 +0000 @@ -21,3 +21,5 @@ StringHeapLex lex_strings; StringHeapLex filename_strings; + +StringHeapLex gen_strings; diff -Nru iverilog-10.3/vhdlpp/compiler.h iverilog-11.0/vhdlpp/compiler.h --- iverilog-10.3/vhdlpp/compiler.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/compiler.h 2020-09-26 22:44:25.000000000 +0000 @@ -30,8 +30,13 @@ extern bool debug_elaboration; extern std::ofstream debug_log_file; +// Stores strings created by the lexer extern StringHeapLex lex_strings; +// Stores file names extern StringHeapLex filename_strings; +// Stores generated strings (e.g. scope names) +extern StringHeapLex gen_strings; + #endif /* IVL_compiler_H */ diff -Nru iverilog-10.3/vhdlpp/debug.cc iverilog-11.0/vhdlpp/debug.cc --- iverilog-10.3/vhdlpp/debug.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/debug.cc 2020-09-26 22:44:25.000000000 +0000 @@ -101,7 +101,7 @@ } } -void Scope::dump_scope(ostream&out) const +void ScopeBase::dump_scope(ostream&out) const { // Dump types out << " -- imported types" << endl; @@ -150,22 +150,35 @@ } // Dump subprograms out << " -- Imported Subprograms" << endl; - for (map::const_iterator cur = use_subprograms_.begin() - ; cur != use_subprograms_.end() ; ++cur) { - out << " subprogram " << cur->first << " is" << endl; - cur->second->dump(out); - if(cur->second->body()) - cur->second->body()->dump(out); - out << " end subprogram " << cur->first << endl; + for (map::const_iterator cur = use_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++ cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + const SubprogramHeader*subp = *it; + out << " subprogram " << cur->first << " is" << endl; + subp->dump(out); + if(subp->body()) + subp->body()->dump(out); + out << " end subprogram " << cur->first << endl; + } } + out << " -- Subprograms from this scope" << endl; - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { - out << " subprogram " << cur->first << " is" << endl; - cur->second->dump(out); - if(cur->second->body()) - cur->second->body()->dump(out); - out << " end subprogram " << cur->first << endl; + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + const SubprogramHeader*subp = *it; + out << " subprogram " << cur->first << " is" << endl; + subp->dump(out); + if(subp->body()) + subp->body()->dump(out); + out << " end subprogram " << cur->first << endl; + } } // Dump component declarations out << " -- Components" << endl; @@ -251,9 +264,16 @@ out << setw(indent) << "" << "?choice_t?" << endl; } -void ExpAttribute::dump(ostream&out, int indent) const +void ExpTypeAttribute::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Attribute (type-related) " << name_ + << " at " << get_fileline() << endl; + base_->show(out); +} + +void ExpObjAttribute::dump(ostream&out, int indent) const { - out << setw(indent) << "" << "Attribute " << name_ + out << setw(indent) << "" << "Attribute (object-related) " << name_ << " at " << get_fileline() << endl; base_->dump(out, indent+4); } @@ -375,10 +395,13 @@ << " at " << get_fileline() << endl; if (prefix_.get()) prefix_->dump(out, indent+8); - if (index_) - index_->dump(out, indent+6); - if (lsb_) - lsb_->dump(out, indent+6); + + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + (*it)->dump(out, indent+6); + } + } } void ExpNameALL::dump(ostream&out, int indent) const @@ -413,46 +436,12 @@ dump_operands(out, indent+4); } -void ExpString::dump(ostream&out, int indent) const -{ - out << setw(indent) << "" << "String \""; - for(vector::const_iterator it = value_.begin(); - it != value_.end(); ++it) - out << *it; - out << "\"" - << " at " << get_fileline() << endl; -} - -void ExpUAbs::dump(ostream&out, int indent) const -{ - out << setw(indent) << "" << "abs() at " << get_fileline() << endl; - dump_operand1(out, indent+4); -} - -void ExpUnary::dump_operand1(ostream&out, int indent) const -{ - operand1_->dump(out, indent); -} - -void ExpUNot::dump(ostream&out, int indent) const -{ - out << setw(indent) << "" << "not() at " << get_fileline() << endl; - dump_operand1(out, indent+4); -} - void named_expr_t::dump(ostream&out, int indent) const { out << setw(indent) << "" << name_ << "=>"; expr_->dump(out, indent); } -void prange_t::dump(ostream&out, int indent) const -{ - left_->dump(out, indent); - out << setw(indent) << "" << (direction_ ? "downto" : "to"); - right_->dump(out, indent); -} - ostream& Expression::dump_inline(ostream&out) const { out << typeid(*this).name(); diff -Nru iverilog-10.3/vhdlpp/entity.h iverilog-11.0/vhdlpp/entity.h --- iverilog-10.3/vhdlpp/entity.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/entity.h 2020-09-26 22:44:25.000000000 +0000 @@ -41,10 +41,14 @@ : mode(mod), name(nam), type(typ), expr(exp) {} - InterfacePort(const VType*typ) + explicit InterfacePort(const VType*typ) : mode(PORT_NONE), type(typ), expr(NULL) {} + InterfacePort(const VType*typ, port_mode_t mod) + : mode(mod), type(typ), expr(NULL) + {} + // Port direction from the source code. port_mode_t mode; // Name of the port from the source code @@ -64,7 +68,7 @@ class ComponentBase : public LineInfo { public: - ComponentBase(perm_string name); + explicit ComponentBase(perm_string name); ~ComponentBase(); // Entities have names. @@ -101,7 +105,7 @@ class Entity : public ComponentBase { public: - Entity(perm_string name); + explicit Entity(perm_string name); ~Entity(); // bind an architecture to the entity, and return the diff -Nru iverilog-10.3/vhdlpp/expression.cc iverilog-11.0/vhdlpp/expression.cc --- iverilog-10.3/vhdlpp/expression.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/expression.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,6 +1,7 @@ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2015 / Stephen Williams (steve@icarus.com), + * Copyright CERN 2016 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it @@ -23,6 +24,7 @@ # include "subprogram.h" # include "parse_types.h" # include "scope.h" +# include "library.h" # include # include # include @@ -54,29 +56,97 @@ return false; } -ExpAttribute::ExpAttribute(ExpName*bas, perm_string nam) -: base_(bas), name_(nam) +ExpAttribute::ExpAttribute(perm_string nam, list*args) +: name_(nam), args_(args) { } ExpAttribute::~ExpAttribute() { - /* Different attributes can point to the same base so we cannot delete this here. - * Look at the vhdl_range test with valgrind to see this issue. */ -// delete base_; + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + delete *it; + } + } + + delete args_; +} + +list*ExpAttribute::clone_args() const { + list*new_args = NULL; + + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + new_args->push_back((*it)->clone()); + } + } + + return new_args; +} + +void ExpAttribute::visit_args(ExprVisitor& func) +{ + func.down(); + func(this); + + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + (*it)->visit(func); + } + } + + func.up(); } -Expression*ExpAttribute::clone() const +ExpObjAttribute::ExpObjAttribute(ExpName*base, perm_string name, list*args) +: ExpAttribute(name, args), base_(base) { - return new ExpAttribute(static_cast(base_->clone()), name_); } -void ExpAttribute::visit(ExprVisitor& func) +ExpObjAttribute::~ExpObjAttribute() { + delete base_; +} + +Expression*ExpObjAttribute::clone() const +{ + return new ExpObjAttribute(static_cast(base_->clone()), + name_, clone_args()); +} + +void ExpObjAttribute::visit(ExprVisitor&func) +{ + func.down(); + func(this); + visit_args(func); base_->visit(func); + func.up(); +} + +ExpTypeAttribute::ExpTypeAttribute(const VType*base, perm_string name, list*args) +: ExpAttribute(name, args), base_(base) +{ +} + +Expression*ExpTypeAttribute::clone() const +{ + return new ExpTypeAttribute(base_, name_, clone_args()); +} + +void ExpTypeAttribute::visit(ExprVisitor&func) +{ + func.down(); func(this); + visit_args(func); + func.up(); } +const perm_string ExpAttribute::LEFT = perm_string::literal("left"); +const perm_string ExpAttribute::RIGHT = perm_string::literal("right"); + ExpBinary::ExpBinary(Expression*op1, Expression*op2) : operand1_(op1), operand2_(op2) { @@ -88,21 +158,23 @@ delete operand2_; } -bool ExpBinary::eval_operand1(ScopeBase*scope, int64_t&val) const +bool ExpBinary::eval_operand1(Entity*ent, ScopeBase*scope, int64_t&val) const { - return operand1_->evaluate(scope, val); + return operand1_->evaluate(ent, scope, val); } -bool ExpBinary::eval_operand2(ScopeBase*scope, int64_t&val) const +bool ExpBinary::eval_operand2(Entity*ent, ScopeBase*scope, int64_t&val) const { - return operand2_->evaluate(scope, val); + return operand2_->evaluate(ent, scope, val); } -void ExpBinary::visit(ExprVisitor& func) +void ExpBinary::visit(ExprVisitor&func) { + func.down(); + func(this); operand1_->visit(func); operand2_->visit(func); - func(this); + func.up(); } ExpUnary::ExpUnary(Expression*op1) @@ -115,10 +187,12 @@ delete operand1_; } -void ExpUnary::visit(ExprVisitor& func) +void ExpUnary::visit(ExprVisitor&func) { - operand1_->visit(func); + func.down(); func(this); + operand1_->visit(func); + func.up(); } ExpAggregate::ExpAggregate(std::list*el) @@ -165,8 +239,11 @@ return new ExpAggregate(new_elements); } -void ExpAggregate::visit(ExprVisitor& func) +void ExpAggregate::visit(ExprVisitor&func) { + func.down(); + func(this); + for(std::vector::iterator it = elements_.begin(); it != elements_.end(); ++it) { (*it)->extract_expression()->visit(func); @@ -180,7 +257,7 @@ it->expr->visit(func); } - func(this); + func.up(); } ExpAggregate::choice_t::choice_t(Expression*exp) @@ -192,7 +269,7 @@ { } -ExpAggregate::choice_t::choice_t(prange_t*rang) +ExpAggregate::choice_t::choice_t(ExpRange*rang) : range_(rang) { } @@ -203,7 +280,7 @@ expr_.reset(e->clone()); if(other.range_.get()) - range_.reset(new prange_t(*other.range_.get())); + range_.reset(static_cast(other.range_.get()->clone())); } ExpAggregate::choice_t::~choice_t() @@ -221,7 +298,7 @@ return res; } -prange_t*ExpAggregate::choice_t::range_expressions(void) +ExpRange*ExpAggregate::choice_t::range_expressions(void) { return range_.get(); } @@ -304,11 +381,13 @@ delete operand2_; } -void ExpConcat::visit(ExprVisitor& func) +void ExpConcat::visit(ExprVisitor&func) { - operand1_->visit(func); - operand2_->visit(func); - func(this); + func.down(); + func(this); + operand1_->visit(func); + operand2_->visit(func); + func.up(); } ExpConditional::ExpConditional(Expression*co, list*tru, @@ -342,14 +421,16 @@ return new ExpConditional(NULL, NULL, new_options); } -void ExpConditional::visit(ExprVisitor& func) +void ExpConditional::visit(ExprVisitor&func) { + func.down(); + func(this); + for(std::list::iterator it = options_.begin(); - it != options_.end(); ++it) { + it != options_.end(); ++it) (*it)->visit(func); - } - func(this); + func.up(); } ExpConditional::case_t::case_t(Expression*cond, std::list*tru) @@ -411,15 +492,16 @@ return new ExpSelected(selector_->clone(), new_options); } -void ExpConditional::case_t::visit(ExprVisitor& func) +void ExpConditional::case_t::visit(ExprVisitor&func) { + func.down(); if(cond_) - func(cond_); + cond_->visit(func); for(std::list::iterator it = true_clause_.begin(); - it != true_clause_.end(); ++it) { - func(*it); - } + it != true_clause_.end(); ++it) + (*it)->visit(func); + func.up(); } ExpEdge::ExpEdge(ExpEdge::fun_t typ, Expression*op) @@ -469,14 +551,18 @@ return f; } -void ExpFunc::visit(ExprVisitor& func) { +void ExpFunc::visit(ExprVisitor&func) +{ + func.down(); + func(this); + if(!argv_.empty()) { for(std::vector::iterator it = argv_.begin(); it != argv_.end(); ++it) (*it)->visit(func); } - func(this); + func.up(); } const VType* ExpFunc::func_ret_type() const @@ -484,6 +570,32 @@ return def_ ? def_->peek_return_type() : NULL; } +SubprogramHeader*ExpFunc::match_signature(Entity*ent, ScopeBase*scope) const +{ + SubprogramHeader*prog = NULL; + list arg_types; + + // Create a list of argument types to find a matching subprogram + for(vector::const_iterator it = argv_.begin(); + it != argv_.end(); ++it) { + arg_types.push_back((*it)->probe_type(ent, scope)); + } + + prog = scope->match_subprogram(name_, &arg_types); + + if(!prog) + prog = library_match_subprogram(name_, &arg_types); + + if(!prog) { + cerr << get_fileline() << ": sorry: could not find function "; + emit_subprogram_sig(cerr, name_, arg_types); + cerr << endl; + ivl_assert(*this, false); + } + + return prog; +} + ExpInteger::ExpInteger(int64_t val) : value_(val) { @@ -493,7 +605,7 @@ { } -bool ExpInteger::evaluate(ScopeBase*, int64_t&val) const +bool ExpInteger::evaluate(Entity*, ScopeBase*, int64_t&val) const { val = value_; return true; @@ -518,39 +630,54 @@ } ExpName::ExpName(perm_string nn) -: name_(nn), index_(0), lsb_(0) +: name_(nn), indices_(NULL) { } ExpName::ExpName(perm_string nn, list*indices) -: name_(nn), index_(0), lsb_(0) +: name_(nn), indices_(indices) { - /* For now, assume a single index. */ - ivl_assert(*this, indices->size() == 1); - - index_ = indices->front(); - indices->pop_front(); } -ExpName::ExpName(perm_string nn, Expression*msb, Expression*lsb) -: name_(nn), index_(msb), lsb_(lsb) +ExpName::ExpName(ExpName*prefix, perm_string nn, std::list*indices) +: prefix_(prefix), name_(nn), indices_(indices) { } -ExpName::ExpName(ExpName*prefix, perm_string nn) -: prefix_(prefix), name_(nn), index_(0), lsb_(0) +ExpName::~ExpName() { + if(indices_) { + for(list::iterator it = indices_->begin(); + it != indices_->end(); ++it) { + delete *it; + } + + delete indices_; + } } -ExpName::ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb) -: prefix_(prefix), name_(nn), index_(msb), lsb_(lsb) -{ +Expression*ExpName::clone() const { + list*new_indices = NULL; + + if(indices_) { + new_indices = new list(); + + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + new_indices->push_back((*it)->clone()); + } + } + + return new ExpName(static_cast(safe_clone(prefix_.get())), + name_, new_indices); } -ExpName::~ExpName() +void ExpName::add_index(std::list*idx) { - delete index_; - delete lsb_; + if(!indices_) + indices_ = new list(); + + indices_->splice(indices_->end(), *idx); } bool ExpName::symbolic_compare(const Expression*that) const @@ -562,42 +689,69 @@ if (name_ != that_name->name_) return false; - if (that_name->index_ && !index_) + if (that_name->indices_ && !indices_) return false; - if (index_ && !that_name->index_) + if (indices_ && !that_name->indices_) return false; - if (index_) { - assert(that_name->index_); - return index_->symbolic_compare(that_name->index_); + if (indices_) { + assert(that_name->indices_); + + if(indices_->size() != that_name->indices_->size()) + return false; + + list::const_iterator it, jt; + it = indices_->begin(); + jt = that_name->indices_->begin(); + + for(unsigned int i = 0; i < indices_->size(); ++i) { + if(!(*it)->symbolic_compare(*jt)) + return false; + + ++it; + ++jt; + } } return true; } -void ExpName::set_range(Expression*msb, Expression*lsb) +Expression*ExpName::index(unsigned int number) const { - assert(index_==0); - index_ = msb; - assert(lsb_==0); - lsb_ = lsb; + if(!indices_) + return NULL; + + if(number >= indices_->size()) + return NULL; + + if(number == 0) + return indices_->front(); + + list::const_iterator it = indices_->begin(); + advance(it, number); + + return *it; } -void ExpName::visit(ExprVisitor& func) +void ExpName::visit(ExprVisitor&func) { + func.down(); + func(this); + if(prefix_.get()) prefix_.get()->visit(func); - if(index_) - index_->visit(func); - - if(lsb_) - lsb_->visit(func); + if(indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + (*it)->visit(func); + } + } - func(this); + func.up(); } -int ExpName::index_t::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpName::index_t::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; @@ -628,16 +782,45 @@ { } +ExpScopedName::ExpScopedName(perm_string scope, ExpName*exp) +: scope_name_(scope), scope_(NULL), name_(exp) +{ +} + +ExpScopedName::~ExpScopedName() +{ + delete name_; +} + +void ExpScopedName::visit(ExprVisitor&func) +{ + func.down(); + func(this); + name_->visit(func); + func.up(); +} + +ScopeBase*ExpScopedName::get_scope(const ScopeBase*scope) +{ + if(!scope_) + scope_ = scope->find_scope(scope_name_); + + return scope_; +} + +ScopeBase*ExpScopedName::get_scope(const ScopeBase*scope) const +{ + return scope_ ? scope_ : scope->find_scope(scope_name_); +} + ExpShift::ExpShift(ExpShift::shift_t op, Expression*op1, Expression*op2) : ExpBinary(op1, op2), shift_(op) { } ExpString::ExpString(const char* value) -: value_(strlen(value)) +: value_(value) { - for(size_t idx = 0; idx < value_.size(); idx += 1) - value_[idx] = value[idx]; } ExpString::~ExpString() @@ -662,6 +845,15 @@ { } +ExpUMinus::ExpUMinus(Expression*op1) +: ExpUnary(op1) +{ +} + +ExpUMinus::~ExpUMinus() +{ +} + ExpCast::ExpCast(Expression*base, const VType*type) : base_(base), type_(type) { @@ -671,10 +863,12 @@ { } -void ExpCast::visit(ExprVisitor& func) +void ExpCast::visit(ExprVisitor&func) { - base_->visit(func); + func.down(); func(this); + base_->visit(func); + func.up(); } ExpNew::ExpNew(Expression*size) : @@ -687,10 +881,12 @@ delete size_; } -void ExpNew::visit(ExprVisitor& func) +void ExpNew::visit(ExprVisitor&func) { - size_->visit(func); + func.down(); func(this); + size_->visit(func); + func.up(); } ExpTime::ExpTime(uint64_t amount, timeunit_t unit) @@ -714,3 +910,95 @@ return val; } + +ExpRange::ExpRange(Expression*left_idx, Expression*right_idx, range_dir_t dir) +: left_(left_idx), right_(right_idx), direction_(dir), range_expr_(false), + range_base_(NULL) +{ +} + +ExpRange::ExpRange(ExpName*base, bool reverse_range) +: left_(NULL), right_(NULL), direction_(AUTO), range_expr_(true), + range_base_(base), range_reverse_(reverse_range) +{ +} + +ExpRange::~ExpRange() +{ + delete left_; + delete right_; + delete range_base_; +} + +Expression*ExpRange::clone() const +{ + if(range_expr_) + return new ExpRange(static_cast(range_base_->clone()), range_reverse_); + else + return new ExpRange(left_->clone(), right_->clone(), direction_); +} + +Expression* ExpRange::msb() +{ + ivl_assert(*this, direction() != AUTO); + + switch(direction()) { + case DOWNTO: return left_; + case TO: return right_; + default: return NULL; + } + + return NULL; +} + +Expression* ExpRange::lsb() +{ + ivl_assert(*this, direction() != AUTO); + + switch(direction()) { + case DOWNTO: return right_; + case TO: return left_; + default: return NULL; + } + + return NULL; +} + +Expression*ExpRange::left() +{ + if(range_expr_ && !left_) + // TODO check if it is an object or type + left_ = new ExpObjAttribute(static_cast(range_base_->clone()), + ExpAttribute::LEFT, NULL); + + return left_; +} + +Expression*ExpRange::right() +{ + if(range_expr_ && !right_) + // TODO check if it is an object or type + right_ = new ExpObjAttribute(static_cast(range_base_->clone()), + ExpAttribute::RIGHT, NULL); + return right_; +} + +ExpDelay::ExpDelay(Expression*expr, Expression*delay) +: expr_(expr), delay_(delay) +{ +} + +ExpDelay::~ExpDelay() +{ + delete expr_; + delete delay_; +} + +void ExpDelay::visit(ExprVisitor&func) +{ + func.down(); + func(this); + expr_->visit(func); + delay_->visit(func); + func.up(); +} diff -Nru iverilog-10.3/vhdlpp/expression_debug.cc iverilog-11.0/vhdlpp/expression_debug.cc --- iverilog-10.3/vhdlpp/expression_debug.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/expression_debug.cc 2020-09-26 22:44:25.000000000 +0000 @@ -71,7 +71,7 @@ void ExpCast::dump(ostream&out, int indent) const { - out << "Casting "; + out << setw(indent) << "" << "Casting "; base_->dump(out, indent+4); out << " to "; type_->emit_def(out, empty_perm_string); @@ -79,10 +79,17 @@ void ExpNew::dump(ostream&out, int indent) const { - out << "New dynamic array size: "; + out << setw(indent) << "" << "New dynamic array size: " << endl; size_->dump(out, indent); } +void ExpScopedName::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Scoped name expression: " << endl; + out << " scope " << scope_name_ << " " << scope_ << endl; + name_->dump(out, indent+4); +} + void ExpShift::dump(ostream&out, int indent) const { const char*fun_name = "?"; @@ -112,8 +119,51 @@ dump_operands(out, indent+4); } +void ExpString::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "String \"" << value_; + out << "\"" << " at " << get_fileline() << endl; +} + +void ExpUAbs::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "abs() at " << get_fileline() << endl; + dump_operand1(out, indent+4); +} + +void ExpUnary::dump_operand1(ostream&out, int indent) const +{ + operand1_->dump(out, indent); +} + +void ExpUNot::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "not() at " << get_fileline() << endl; + dump_operand1(out, indent+4); +} + +void ExpUMinus::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "unary_minus() at " << get_fileline() << endl; + dump_operand1(out, indent+4); +} + void ExpTime::dump(ostream&out, int indent) const { out << setw(indent) << "" << "Time "; write_to_stream(out); } + +void ExpRange::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Range "; + write_to_stream(out); +} + +void ExpDelay::dump(ostream&out, int indent) const +{ + out << setw(indent) << "" << "Expression "; + expr_->write_to_stream(out); + out << " delayed by "; + delay_->write_to_stream(out); +} diff -Nru iverilog-10.3/vhdlpp/expression_elaborate.cc iverilog-11.0/vhdlpp/expression_elaborate.cc --- iverilog-10.3/vhdlpp/expression_elaborate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/expression_elaborate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,6 +1,8 @@ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) + * Copyright CERN 2016 + * @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 @@ -24,7 +26,6 @@ # include "entity.h" # include "vsignal.h" # include "subprogram.h" -# include "library.h" # include "std_types.h" # include # include @@ -57,7 +58,8 @@ return res; } -const VType*ExpName::elaborate_adjust_type_with_range_(Entity*, ScopeBase*scope, const VType*type) +const VType*ExpName::elaborate_adjust_type_with_range_(Entity*ent, ScopeBase*scope, + const VType*type) { // Unfold typedefs while (const VTypeDef*tdef = dynamic_cast(type)) { @@ -65,23 +67,24 @@ } if (const VTypeArray*array = dynamic_cast(type)) { - if (index_ && !lsb_) { - // If the name is an array or a vector, then an - // indexed name has the type of the element. - type = array->element_type(); + Expression*idx = index(0); - } else if (index_ && lsb_) { + if (ExpRange*range = dynamic_cast(idx)) { // If the name is an array, then a part select is // also an array, but with different bounds. int64_t use_msb, use_lsb; - bool flag; + bool flag = true; - flag = index_->evaluate(scope, use_msb); - ivl_assert(*this, flag); - flag = lsb_->evaluate(scope, use_lsb); - ivl_assert(*this, flag); + flag &= range->msb()->evaluate(ent, scope, use_msb); + flag &= range->lsb()->evaluate(ent, scope, use_lsb); - type = new VTypeArray(array->element_type(), use_msb, use_lsb); + if(flag) + type = new VTypeArray(array->element_type(), use_msb, use_lsb); + } + else if(idx) { + // If the name is an array or a vector, then an + // indexed name has the type of the element. + type = array->element_type(); } } @@ -96,10 +99,14 @@ debug_log_file << get_fileline() << ": ExpName::elaborate_lval_: " << "name_=" << name_ << ", suffix->name()=" << suffix->name(); - if (index_) - debug_log_file << ", index_=" << *index_; - if (lsb_) - debug_log_file << ", lsb_=" << *lsb_; + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + debug_log_file << "["; + debug_log_file << **it; + debug_log_file << "]"; + } + } debug_log_file << endl; } @@ -216,36 +223,44 @@ const VType*found_type = 0; - if (const InterfacePort*cur = ent->find_port(name_)) { - if (cur->mode != PORT_OUT && cur->mode != PORT_INOUT) { - cerr << get_fileline() << ": error: Assignment to " - "input port " << name_ << "." << endl; - return errors += 1; - } + if (ent) { + if (const InterfacePort*cur = ent->find_port(name_)) { + if (cur->mode != PORT_OUT && cur->mode != PORT_INOUT) { + cerr << get_fileline() << ": error: Assignment to " + "input port " << name_ << "." << endl; + return errors += 1; + } - if (is_sequ) - ent->set_declaration_l_value(name_, is_sequ); + if (is_sequ) + ent->set_declaration_l_value(name_, is_sequ); - found_type = cur->type; + found_type = cur->type; - } else if (ent->find_generic(name_)) { + } else if (ent->find_generic(name_)) { - cerr << get_fileline() << ": error: Assignment to generic " - << name_ << " from entity " - << ent->get_name() << "." << endl; - return 1; + cerr << get_fileline() << ": error: Assignment to generic " + << name_ << " from entity " + << ent->get_name() << "." << endl; + return 1; + } + } - } else if (Signal*sig = scope->find_signal(name_)) { - // Tell the target signal that this may be a sequential l-value. - if (is_sequ) sig->count_ref_sequ(); + if (!found_type && scope) { + if (Signal*sig = scope->find_signal(name_)) { + // Tell the target signal that this may be a sequential l-value. + if (is_sequ) sig->count_ref_sequ(); - found_type = sig->peek_type(); + found_type = sig->peek_type(); - } else if (Variable*var = scope->find_variable(name_)) { - // Tell the target signal that this may be a sequential l-value. - if (is_sequ) var->count_ref_sequ(); + } else if (Variable*var = scope->find_variable(name_)) { + // Tell the target signal that this may be a sequential l-value. + if (is_sequ) var->count_ref_sequ(); - found_type = var->peek_type(); + found_type = var->peek_type(); + + } else if (const InterfacePort*port = scope->find_param(name_)) { + found_type = port->type; + } } if (found_type == 0) { @@ -316,14 +331,10 @@ return errors; } -int ExpNameALL::elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ) -{ - return Expression::elaborate_lval(ent, scope, is_sequ); -} - int Expression::elaborate_expr(Entity*, ScopeBase*, const VType*) { - cerr << get_fileline() << ": internal error: I don't know how to elaborate expression type=" << typeid(*this).name() << endl; + cerr << get_fileline() << ": internal error: I don't know how to " + << "elaborate expression type=" << typeid(*this).name() << endl; return 1; } @@ -337,8 +348,10 @@ if (t2 == 0) return t1; - if (t1 == t2) + if (t1->type_match(t2)) return t1; + if (t2->type_match(t1)) + return t2; if (const VType*tb = resolve_operand_types_(t1, t2)) return tb; @@ -376,6 +389,11 @@ return operand1_->fit_type(ent, scope, atype); } +const VType*ExpUnary::probe_type(Entity*ent, ScopeBase*scope) const +{ + return operand1_->probe_type(ent, scope); +} + int ExpUnary::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) { ivl_assert(*this, ltype != 0); @@ -383,11 +401,6 @@ return operand1_->elaborate_expr(ent, scope, ltype); } -const VType*ExpAggregate::probe_type(Entity*ent, ScopeBase*scope) const -{ - return Expression::probe_type(ent, scope); -} - const VType*ExpAggregate::fit_type(Entity*, ScopeBase*, const VTypeArray*host) const { ivl_assert(*this, elements_.size() == 1); @@ -398,13 +411,13 @@ elements_[0]->map_choices(&ce[0]); ivl_assert(*this, ce.size() == 1); - prange_t*prange = ce[0].choice->range_expressions(); + ExpRange*prange = ce[0].choice->range_expressions(); ivl_assert(*this, prange); Expression*use_msb = prange->msb(); Expression*use_lsb = prange->lsb(); - ivl_assert(*this, host->dimensions() == 1); + ivl_assert(*this, host->dimensions().size() == 1); vector range (1); range[0] = VTypeArray::range_t(use_msb, use_lsb); @@ -565,6 +578,7 @@ const VType* ExpArithmetic::resolve_operand_types_(const VType*t1, const VType*t2) const { + // Ranges while (const VTypeRange*tmp = dynamic_cast (t1)) t1 = tmp->base_type(); while (const VTypeRange*tmp = dynamic_cast (t2)) @@ -572,31 +586,107 @@ if (t1->type_match(t2)) return t1; - if (t2->type_match(t2)) - return t2; - return 0; + // Signed & unsigned (resized to the widest argument) + const VTypeArray*t1_arr = dynamic_cast(t1); + const VTypeArray*t2_arr = dynamic_cast(t2); + + if(t1_arr && t2_arr) { + const VTypeArray*t1_parent = t1_arr->get_parent_type(); + const VTypeArray*t2_parent = t2_arr->get_parent_type(); + + if(t1_parent == t2_parent + && (t1_parent == &primitive_SIGNED || t1_parent == &primitive_UNSIGNED)) { + int t1_size = t1_arr->get_width(NULL); + int t2_size = t2_arr->get_width(NULL); + + // Easy, the same sizes, so we do not need to resize + if(t1_size == t2_size && t1_size > 0) + return t1; // == t2 + + VTypeArray*resolved = new VTypeArray(t1_parent->element_type(), + std::max(t1_size, t2_size) - 1, 0, t1_parent->signed_vector()); + resolved->set_parent_type(t1_parent); + + return resolved; + } + + } else if(t1_arr) { + if(const VTypePrimitive*prim = dynamic_cast(t2)) { + const VTypeArray*t1_parent = t1_arr->get_parent_type(); + VTypePrimitive::type_t t2_type = prim->type(); + + if((t2_type == VTypePrimitive::NATURAL || t2_type == VTypePrimitive::INTEGER) + && t1_parent == &primitive_SIGNED) + return t1; + + if((t2_type == VTypePrimitive::NATURAL) && t1_parent == &primitive_UNSIGNED) + return t1; + } + + } else if(t2_arr) { + if(const VTypePrimitive*prim = dynamic_cast(t1)) { + const VTypeArray*t2_parent = t2_arr->get_parent_type(); + VTypePrimitive::type_t t1_type = prim->type(); + + if((t1_type == VTypePrimitive::NATURAL || t1_type == VTypePrimitive::INTEGER) + && t2_parent == &primitive_SIGNED) + return t2; + + if((t1_type == VTypePrimitive::NATURAL) && t2_parent == &primitive_UNSIGNED) + return t2; + } + } + + return 0; } -const VType* ExpAttribute::probe_type(Entity*ent, ScopeBase*scope) const +int ExpAttribute::elaborate_args(Entity*ent, ScopeBase*scope, const VType*ltype) { - base_->probe_type(ent, scope); + int errors = 0; - if (name_ == "length" || name_ == "left" || name_ == "right") { - return &primitive_NATURAL; + if(args_) { + for(list::iterator it = args_->begin(); + it != args_->end(); ++it) { + errors += (*it)->elaborate_expr(ent, scope, ltype); + } } - return 0; + return errors; } -int ExpAttribute::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) +int ExpObjAttribute::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) { int errors = 0; const VType*sub_type = base_->probe_type(ent, scope); + + errors += elaborate_args(ent, scope, sub_type); errors += base_->elaborate_expr(ent, scope, sub_type); + return errors; } +const VType* ExpObjAttribute::probe_type(Entity*, ScopeBase*) const +{ + if (name_ == "length" || name_ == "left" || name_ == "right") + return &primitive_NATURAL; + + return NULL; +} + +int ExpTypeAttribute::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) +{ + return elaborate_args(ent, scope, ltype); +} + +const VType* ExpTypeAttribute::probe_type(Entity*, ScopeBase*) const +{ + if(name_ == "image") + return &primitive_STRING; + + return NULL; +} + const VType*ExpBitstring::fit_type(Entity*, ScopeBase*, const VTypeArray*atype) const { // Really should check that this string can work with the @@ -638,7 +728,7 @@ if(const VTypeArray*arr = dynamic_cast(types[i])) { types[i] = arr->element_type(); - ivl_assert(*this, arr->dimensions() == 1); + ivl_assert(*this, arr->dimensions().size() == 1); const VTypeArray::range_t&dim = arr->dimension(0); sizes[i] = new ExpArithmetic(ExpArithmetic::MINUS, dim.msb(), dim.lsb()); } else { @@ -653,8 +743,8 @@ new ExpArithmetic(ExpArithmetic::PLUS, sizes[0], sizes[1]), new ExpInteger(1)); - std::list ranges; - ranges.push_front(new prange_t(size, new ExpInteger(0), true)); + std::list ranges; + ranges.push_front(new ExpRange(size, new ExpInteger(0), ExpRange::DOWNTO)); const VType*array = new VTypeArray(types[1], &ranges); return array; @@ -693,7 +783,7 @@ int errors = 0; // For now, only support single-dimension arrays here. - ivl_assert(*this, atype->dimensions() == 1); + ivl_assert(*this, atype->dimensions().size() == 1); const VType*type1 = operand1_->fit_type(ent, scope, atype); ivl_assert(*this, type1); @@ -749,60 +839,37 @@ return errors; } -const VType*ExpFunc::probe_type(Entity*, ScopeBase*scope) const +const VType*ExpFunc::probe_type(Entity*ent, ScopeBase*scope) const { - SubprogramHeader*prog = def_; - - if(!prog) { - prog = scope->find_subprogram(name_); - } + if(!def_) + def_ = match_signature(ent, scope); - if(!prog) - prog = library_find_subprogram(name_); - - if(!prog) { - cerr << get_fileline() << ": sorry: VHDL function " << name_ << " not yet implemented" << endl; - ivl_assert(*this, false); - } - - return prog->peek_return_type(); + return def_ ? def_->exact_return_type(argv_, ent, scope) : NULL; } int ExpFunc::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) { int errors = 0; - ivl_assert(*this, scope); - SubprogramHeader*prog = scope->find_subprogram(name_); + if(def_) + return 0; - if(!prog) - prog = library_find_subprogram(name_); + def_ = match_signature(ent, scope); - ivl_assert(*this, def_==0); - def_ = prog; + if(!def_) + return 1; // Elaborate arguments - for (size_t idx = 0 ; idx < argv_.size() ; idx += 1) { - const VType*tmp = argv_[idx]->probe_type(ent, scope); - const VType*param_type = prog ? prog->peek_param_type(idx) : NULL; - - if(!tmp && param_type) - tmp = param_type; - - errors += argv_[idx]->elaborate_expr(ent, scope, tmp); + for (size_t idx = 0; idx < argv_.size(); ++idx) { + errors += def_->elaborate_argument(argv_[idx], idx, ent, scope); } // SystemVerilog functions work only with defined size data types, therefore // if header does not specify argument or return type size, create a function // instance that work with this particular size. if(def_ && !def_->is_std() && def_->unbounded()) { - def_ = prog->make_instance(argv_, scope); - name_ = def_->name(); - } - - if(!def_) { - cerr << get_fileline() << ": error: could not find function " << name_ << endl; - ++errors; + def_ = def_->make_instance(argv_, scope); + name_ = def_->name(); // TODO necessary? } return errors; @@ -810,12 +877,15 @@ const VType* ExpFunc::fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*) const { - return probe_type(ent, scope); + return probe_type(ent, scope); } const VType* ExpInteger::probe_type(Entity*, ScopeBase*) const { - return &primitive_INTEGER; + if(value_ >= 0) + return &primitive_NATURAL; + else + return &primitive_INTEGER; } int ExpInteger::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) @@ -880,40 +950,49 @@ */ const VType* ExpName::probe_prefixed_type_(Entity*ent, ScopeBase*scope) const { - // First, get the type of the prefix. + // First, get the type of the prefix. const VType*prefix_type = prefix_->probe_prefix_type_(ent, scope); if (prefix_type == 0) { - return 0; + return 0; } while (const VTypeDef*def = dynamic_cast (prefix_type)) { - prefix_type = def->peek_definition(); + prefix_type = def->peek_definition(); } - // If the prefix type is a record, then the current name is - // the name of a member. - if (const VTypeRecord*pref_record = dynamic_cast (prefix_type)) { - const VTypeRecord::element_t*element = pref_record->element_by_name(name_); - ivl_assert(*this, element); + const VType*element_type = prefix_type; + bool type_changed = true; - const VType*element_type = element->peek_type(); - ivl_assert(*this, element_type); + // Keep unwinding the type until we find the basic element type + while (type_changed) { + type_changed = false; - return element_type; - } + // If the prefix type is a record, then the current name is + // the name of a member. + if (const VTypeRecord*pref_record = dynamic_cast(element_type)) { + const VTypeRecord::element_t*element = pref_record->element_by_name(name_); + ivl_assert(*this, element); - if (const VTypeArray*pref_array = dynamic_cast (prefix_type)) { - const VType*element_type = pref_array->element_type(); - ivl_assert(*this, element_type); + element_type = element->peek_type(); + ivl_assert(*this, element_type); + type_changed = true; + } - return element_type; + if (const VTypeArray*pref_array = dynamic_cast(element_type)) { + element_type = pref_array->basic_type(false); + ivl_assert(*this, element_type); + type_changed = true; + } } - cerr << get_fileline() << ": sorry: I don't know how to probe " - << "prefix type " << typeid(*prefix_type).name() - << " of " << name_ << "." << endl; + if(!element_type) { + cerr << get_fileline() << ": sorry: I don't know how to probe " + << "prefix type " << typeid(*prefix_type).name() + << " of " << name_ << "." << endl; + return NULL; + } - return 0; + return element_type; } const VType* ExpName::probe_type(Entity*ent, ScopeBase*scope) const @@ -923,31 +1002,31 @@ if(ent) { if (const InterfacePort*cur = ent->find_port(name_)) { - ivl_assert(*this, cur->type); - return cur->type; + ivl_assert(*this, cur->type); + return cur->type; } if (const InterfacePort*cur = ent->find_generic(name_)) { - ivl_assert(*this, cur->type); - return cur->type; + ivl_assert(*this, cur->type); + return cur->type; } } if(scope) { if (Signal*sig = scope->find_signal(name_)) - return sig->peek_type(); + return sig->peek_type(); if (Variable*var = scope->find_variable(name_)) - return var->peek_type(); + return var->peek_type(); const VType*type = 0; Expression*cval = 0; if (scope->find_constant(name_, type, cval)) - return type; + return type; Architecture*arc = dynamic_cast(scope); if (arc && (type = arc->probe_genvar_type(name_))) { - return type; + return type; } if (const InterfacePort*port = scope->find_param(name_)) { @@ -959,8 +1038,14 @@ } } - cerr << get_fileline() << ": error: Signal/variable " << name_ - << " not found in this context." << endl; + if(ent || scope) { + // Do not display error messages if there was no entity or scope + // specified. There are functions that are called without any specific + // context and they still may want to probe the expression type. + cerr << get_fileline() << ": error: Signal/variable " << name_ + << " not found in this context." << endl; + } + return 0; } @@ -979,11 +1064,12 @@ if(prefix_.get()) prefix_.get()->elaborate_expr(ent, scope, NULL); - if(index_) - index_->elaborate_expr(ent, scope, &primitive_INTEGER); - - if(lsb_) - lsb_->elaborate_expr(ent, scope, &primitive_INTEGER); + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + (*it)->elaborate_expr(ent, scope, &primitive_INTEGER); + } + } return 0; } @@ -1058,3 +1144,26 @@ set_type(&primitive_INTEGER); return 0; } + +int ExpRange::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*) +{ + int errors = 0; + + if(left_) + errors += left_->elaborate_expr(ent, scope, &primitive_INTEGER); + + if(right_) + errors += right_->elaborate_expr(ent, scope, &primitive_INTEGER); + + return errors; +} + +int ExpDelay::elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) +{ + int errors = 0; + + errors += expr_->elaborate_expr(ent, scope, ltype); + errors += delay_->elaborate_expr(ent, scope, ltype); + + return errors; +} diff -Nru iverilog-10.3/vhdlpp/expression_emit.cc iverilog-11.0/vhdlpp/expression_emit.cc --- iverilog-10.3/vhdlpp/expression_emit.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/expression_emit.cc 2020-09-26 22:44:25.000000000 +0000 @@ -62,7 +62,7 @@ return 0; } -int Expression::emit(ostream&out, Entity*, ScopeBase*) +int Expression::emit(ostream&out, Entity*, ScopeBase*) const { out << " /* " << get_fileline() << ": internal error: " << "I don't know how to emit this expression! " @@ -70,7 +70,7 @@ return 1; } -int Expression::emit_package(ostream&out) +int Expression::emit_package(ostream&out) const { out << " /* " << get_fileline() << ": internal error: " << "I don't know how to emit_package this expression! " @@ -83,7 +83,7 @@ return false; } -int ExpBinary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) +int ExpBinary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; bool oper_primary = operand1_->is_primary(); @@ -93,7 +93,7 @@ return errors; } -int ExpBinary::emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) +int ExpBinary::emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; bool oper_primary = operand2_->is_primary(); @@ -103,14 +103,14 @@ return errors; } -int ExpUnary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) +int ExpUnary::emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; errors += operand1_->emit(out, ent, scope); return errors; } -int ExpAggregate::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpAggregate::emit(ostream&out, Entity*ent, ScopeBase*scope) const { if (peek_type() == 0) { out << "/* " << get_fileline() << ": internal error: " @@ -134,13 +134,13 @@ return 1; } -int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*atype) +int ExpAggregate::emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*atype) const { int errors = 0; // Special case: The aggregate is a single "others" item. if (aggregate_.size() == 1 && aggregate_[0].choice->others()) { - assert(atype->dimensions() == 1); + assert(atype->dimensions().size() == 1); const VTypeArray::range_t&rang = atype->dimension(0); assert(! rang.is_box()); @@ -194,8 +194,8 @@ if(use_msb < use_lsb) swap(use_msb, use_lsb); - map element_map; - choice_element*element_other = 0; + map element_map; + const choice_element*element_other = 0; bool positional_section = true; int64_t positional_idx = use_msb; @@ -224,7 +224,7 @@ // If this is a range choice, then calculate the bounds // of the range and scan through the values, mapping the // value to the aggregate_[idx] element. - if (prange_t*range = aggregate_[idx].choice->range_expressions()) { + if (ExpRange*range = aggregate_[idx].choice->range_expressions()) { int64_t begin_val, end_val; if (! range->msb()->evaluate(ent, scope, begin_val)) { @@ -283,7 +283,7 @@ out << "{"; for (int64_t idx = use_msb ; idx >= use_lsb ; idx -= 1) { - choice_element*cur = element_map[idx]; + const choice_element*cur = element_map[idx]; if (cur == 0) cur = element_other; @@ -303,7 +303,7 @@ return errors; } -int ExpAggregate::emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*) +int ExpAggregate::emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*) const { int errors = 0; @@ -331,13 +331,13 @@ return errors; } -int ExpAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpObjAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; // Try to evaluate first int64_t val; - if(evaluate(scope, val)) { + if(evaluate(ent, scope, val)) { out << val; return 0; } @@ -365,16 +365,70 @@ return errors; } + // Fallback out << "$ivl_attribute("; errors += base_->emit(out, ent, scope); out << ", \"" << name_ << "\")"; return errors; } -int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpTypeAttribute::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; + // Special case: The image attribute + if (name_=="image") { + if(!args_ || args_->size() != 1) { + out << "/* Invalid 'image attribute */" << endl; + cerr << get_fileline() << ": error: 'image attribute takes " + << "exactly one argument." << endl; + ++errors; + } else { + out << "$sformatf(\""; + + if(base_->type_match(&primitive_INTEGER)) + out << "%0d"; + else if(base_->type_match(&primitive_REAL)) + out << "%f"; + else if(base_->type_match(&primitive_CHARACTER)) + out << "'%c'"; + else if(base_->type_match(&primitive_TIME)) + out << "%+0t"; + + out << "\","; + args_->front()->emit(out, ent, scope); + out << ")"; + } + return errors; + } + + // Fallback + out << "$ivl_attribute("; + errors += base_->emit_def(out, empty_perm_string); + out << ", \"" << name_ << "\")"; + return errors; +} + +int ExpArithmetic::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + if(fun_ == REM) { + // Special case: division remainder, defined in the VHDL standard 1076-2008/9.2.7 + // there is no direct counterpart, therefore output the formula to + // compute a remainder: A rem B = A - (A/B) * B; + out << "(("; + errors += emit_operand1(out, ent, scope); + out << ")-(("; + errors += emit_operand1(out, ent, scope); + out << ")/("; + errors += emit_operand2(out, ent, scope); + out << "))*("; + errors += emit_operand2(out, ent, scope); + out << "))"; + return errors; + } + errors += emit_operand1(out, ent, scope); switch (fun_) { @@ -396,9 +450,8 @@ case POW: out << " ** "; break; - case REM: - out << " /* ?remainder? */ "; - break; + case REM: // should not happen as it is handled above, suppress warnings + ivl_assert(*this, 0); case xCONCAT: ivl_assert(*this, 0); out << " /* ?concat? */ "; @@ -410,7 +463,7 @@ return errors; } -int ExpBitstring::emit(ostream&out, Entity*, ScopeBase*) +int ExpBitstring::emit(ostream&out, Entity*, ScopeBase*) const { int errors = 0; @@ -422,7 +475,7 @@ } int ExpCharacter::emit_primitive_bit_(ostream&out, Entity*, ScopeBase*, - const VTypePrimitive*etype) + const VTypePrimitive*etype) const { out << "1'b"; int res = emit_logic(value_, out, etype->type()); @@ -435,18 +488,17 @@ return res; } -int ExpCharacter::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpCharacter::emit(ostream&out, Entity*ent, ScopeBase*scope) const { const VType*etype = peek_type(); + const VTypeArray*array; - if (const VTypePrimitive*use_type = dynamic_cast(etype)) { - return emit_primitive_bit_(out, ent, scope, use_type); + if (etype != &primitive_CHARACTER && (array = dynamic_cast(etype))) { + etype = array->element_type(); } - if (const VTypeArray*array = dynamic_cast(etype)) { - if (const VTypePrimitive*use_type = dynamic_cast(array->element_type())) { - return emit_primitive_bit_(out, ent, scope, use_type); - } + if (const VTypePrimitive*use_type = dynamic_cast(etype)) { + return emit_primitive_bit_(out, ent, scope, use_type); } out << "\"" << value_ << "\""; @@ -467,7 +519,7 @@ return true; } -int ExpConcat::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConcat::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "{"; @@ -478,7 +530,7 @@ return errors; } -int ExpConditional::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConditional::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "("; @@ -486,10 +538,10 @@ // Draw out any when-else expressions. These are all the else_ // clauses besides the last. if (options_.size() > 1) { - list::iterator last = options_.end(); + list::const_iterator last = options_.end(); --last; - for (list::iterator cur = options_.begin() + for (list::const_iterator cur = options_.begin() ; cur != last ; ++cur) { errors += (*cur)->emit_option(out, ent, scope); } @@ -508,7 +560,7 @@ return errors; } -int ExpConditional::case_t::emit_option(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConditional::case_t::emit_option(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; assert(cond_ != 0); @@ -530,7 +582,7 @@ return errors; } -int ExpConditional::case_t::emit_default(ostream&out, Entity*ent, ScopeBase*scope) +int ExpConditional::case_t::emit_default(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; // Trailing else must have no condition. @@ -547,7 +599,7 @@ return errors; } -int ExpEdge::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpEdge::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; switch (fun_) { @@ -564,22 +616,16 @@ return errors; } -int ExpFunc::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpFunc::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; - ivl_assert(*this, def_); - - // If this function has an elaborated definition, and if - // that definition is in a package, then include the - // package name as a scope qualifier. This assures that - // the SV elaborator finds the correct VHDL elaborated - // definition. - const Package*pkg = dynamic_cast (def_->get_parent()); - if (pkg != 0) - out << "\\" << pkg->name() << " ::"; + if(!def_) { + cerr << get_fileline() << ": error: unknown function: " << name_ << endl; + return 1; + } - errors += def_->emit_name(argv_, out, ent, scope); + def_->emit_full_name(argv_, out, ent, scope); out << " ("; def_->emit_args(argv_, out, ent, scope); out << ")"; @@ -587,25 +633,25 @@ return errors; } -int ExpInteger::emit(ostream&out, Entity*, ScopeBase*) +int ExpInteger::emit(ostream&out, Entity*, ScopeBase*) const { out << "32'd" << value_; return 0; } -int ExpInteger::emit_package(ostream&out) +int ExpInteger::emit_package(ostream&out) const { out << value_; return 0; } -int ExpReal::emit(ostream&out, Entity*, ScopeBase*) +int ExpReal::emit(ostream&out, Entity*, ScopeBase*) const { out << value_; return 0; } -int ExpReal::emit_package(ostream&out) +int ExpReal::emit_package(ostream&out) const { out << value_; return 0; @@ -616,7 +662,7 @@ return true; } -int ExpLogical::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpLogical::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; @@ -648,7 +694,23 @@ return errors; } -int ExpName::emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) +int ExpName::emit_indices(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + if (indices_) { + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + out << "["; + errors += (*it)->emit(out, ent, scope); + out << "]"; + } + } + + return errors; +} + +int ExpName::emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; if (prefix_.get()) { @@ -656,17 +718,12 @@ } out << "\\" << name_ << " "; - if (index_) { - out << "["; - errors += index_->emit(out, ent, scope); - out << "]"; - ivl_assert(*this, lsb_ == 0); - } + errors += emit_indices(out, ent, scope); out << "."; return errors; } -int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpName::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; int field_size = 0; @@ -693,26 +750,19 @@ else out << "\\" << name_ << " "; - if (index_) { - out << "["; - errors += index_->emit(out, ent, scope); - - if (lsb_) { - out << ":"; - errors += lsb_->emit(out, ent, scope); - } - out << "]"; - } + errors += emit_indices(out, ent, scope); return errors; } bool ExpName::try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, - list& indices, int& data_size) + list& indices, int& data_size) const { Expression*exp = NULL; bool wrkand_required = false; const VType*type = NULL; + Expression*idx = index(0); + ExpRange*range = dynamic_cast(idx); if(!scope) return false; @@ -720,7 +770,7 @@ if(prefix_.get()) prefix_->try_workarounds_(out, ent, scope, indices, data_size); - if(index_ && !lsb_ && scope->find_constant(name_, type, exp)) { + if(idx && !range && scope->find_constant(name_, type, exp)) { while(const VTypeDef*type_def = dynamic_cast(type)) { type = type_def->peek_definition(); } @@ -732,7 +782,7 @@ if(prefix_.get() && scope->find_constant(prefix_->name_, type, exp)) { // Handle the case of array of records - if(prefix_->index_) { + if(prefix_->index(0)) { const VTypeArray*arr = dynamic_cast(type); assert(arr); type = arr->element_type(); @@ -749,17 +799,23 @@ wrkand_required |= check_const_record_workaround_(rec, scope, indices, data_size); } + // Workarounds are currently implemented only for one-dimensional arrays + assert(!indices_ || indices_->size() == 1 || !wrkand_required); + return wrkand_required; } bool ExpName::check_const_array_workaround_(const VTypeArray*arr, ScopeBase*scope, list&indices, int&data_size) const { + assert(indices_ && indices_->size() == 1); + const VType*element = arr->element_type(); data_size = element->get_width(scope); if(data_size < 0) return false; - indices.push_back(new index_t(index_->clone(), new ExpInteger(data_size))); + + indices.push_back(new index_t(index(0)->clone(), new ExpInteger(data_size))); return true; } @@ -784,7 +840,7 @@ data_size = tmp_field; indices.push_back(new index_t(NULL, NULL, new ExpInteger(tmp_offset))); - if(index_) { + if(index(0)) { const VTypeArray*arr = dynamic_cast(type); assert(arr); return check_const_array_workaround_(arr, scope, indices, data_size); @@ -805,7 +861,7 @@ } int ExpName::emit_workaround_(ostream&out, Entity*ent, ScopeBase*scope, - const list& indices, int field_size) + const list& indices, int field_size) const { int errors = 0; @@ -827,14 +883,26 @@ return true; } -int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpRelation::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; errors += emit_operand1(out, ent, scope); + const VType*type1 = peek_operand1()->probe_type(ent, scope); + const VType*type2 = peek_operand2()->probe_type(ent, scope); + bool logical_compare = false; + + // Apply case equality operator if any of the operands is of logic type + if(((type1 && (type1->type_match(&primitive_STDLOGIC) || + type1->type_match(&primitive_STDLOGIC_VECTOR))) + || (type2 && (type2->type_match(&primitive_STDLOGIC) || + type2->type_match(&primitive_STDLOGIC_VECTOR))))) { + logical_compare = true; + } + switch (fun_) { case EQ: - out << " == "; + out << (logical_compare ? " === " : " == "); break; case LT: out << " < "; @@ -843,7 +911,7 @@ out << " > "; break; case NEQ: - out << " != "; + out << (logical_compare ? " !== " : " != "); break; case LE: out << " <= "; @@ -857,7 +925,7 @@ return errors; } -int ExpShift::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpShift::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; @@ -892,34 +960,32 @@ return true; } -int ExpString::emit(ostream& out, Entity*ent, ScopeBase*scope) +int ExpString::emit(ostream& out, Entity*ent, ScopeBase*scope) const { + const VTypeArray*arr; const VType*type = peek_type(); assert(type != 0); - if (const VTypeArray*arr = dynamic_cast(type)) { + if (type != &primitive_STRING && (arr = dynamic_cast(type))) { return emit_as_array_(out, ent, scope, arr); } - out << "\""; - for(vector::const_iterator it = value_.begin() - ; it != value_.end(); ++it) - out << *it; - out << "\""; + out << "\"" << escape_quot(value_) << "\""; + return 0; } -int ExpString::emit_as_array_(ostream& out, Entity*, ScopeBase*, const VTypeArray*arr) +int ExpString::emit_as_array_(ostream& out, Entity*, ScopeBase*, const VTypeArray*arr) const { int errors = 0; - assert(arr->dimensions() == 1); + assert(arr->dimensions().size() == 1); const VTypePrimitive*etype = dynamic_cast (arr->basic_type()); assert(etype); // Detect the special case that this is an array of // CHARACTER. In this case, emit at a Verilog string. - if (etype->type()==VTypePrimitive::CHARACTER) { + if (arr->element_type() == &primitive_CHARACTER) { vector tmp (value_.size() + 3); tmp[0] = '"'; memcpy(&tmp[1], &value_[0], value_.size()); @@ -944,7 +1010,20 @@ return errors; } -int ExpUAbs::emit(ostream&out, Entity*ent, ScopeBase*scope) +std::string ExpString::escape_quot(const std::string& str) +{ + size_t idx = 0; + string result(str); + + while((idx = result.find('"', idx)) != string::npos) { + result.replace(idx, 1, "\\\""); + idx += 2; + } + + return result; +} + +int ExpUAbs::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "abs("; @@ -953,7 +1032,7 @@ return errors; } -int ExpUNot::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpUNot::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; @@ -969,7 +1048,16 @@ return errors; } -int ExpCast::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpUMinus::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + out << "-("; + errors += emit_operand1(out, ent, scope); + out << ")"; + return errors; +} + +int ExpCast::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; errors += type_->emit_def(out, empty_perm_string); @@ -979,7 +1067,7 @@ return errors; } -int ExpNew::emit(ostream&out, Entity*ent, ScopeBase*scope) +int ExpNew::emit(ostream&out, Entity*ent, ScopeBase*scope) const { int errors = 0; out << "new["; @@ -988,7 +1076,7 @@ return errors; } -int ExpTime::emit(ostream&out, Entity*, ScopeBase*) +int ExpTime::emit(ostream&out, Entity*, ScopeBase*) const { out << amount_; @@ -1003,3 +1091,37 @@ return 0; } + +int ExpRange::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + if(range_expr_) { + out << "$left("; + errors += range_base_->emit(out, ent, scope); + out << "):$right("; + errors += range_base_->emit(out, ent, scope); + out << ")"; + } else if(direction_ == AUTO) { + ivl_assert(*this, false); + out << "/* auto dir */"; + } else { + errors += left_->emit(out, ent, scope); + out << ":"; + errors += right_->emit(out, ent, scope); + } + + return errors; +} + +int ExpDelay::emit(ostream&out, Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + out << "#("; + errors += delay_->emit(out, ent, scope); + out << ") "; + errors += expr_->emit(out, ent, scope); + + return errors; +} diff -Nru iverilog-10.3/vhdlpp/expression_evaluate.cc iverilog-11.0/vhdlpp/expression_evaluate.cc --- iverilog-10.3/vhdlpp/expression_evaluate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/expression_evaluate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -23,27 +23,18 @@ # include "architec.h" # include # include +# include -bool Expression::evaluate(ScopeBase*, int64_t&) const -{ - return false; -} - -bool Expression::evaluate(Entity*, ScopeBase*scope, int64_t&val) const -{ - return evaluate(scope, val); -} - -bool ExpArithmetic::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpArithmetic::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { int64_t val1, val2; bool rc; - rc = eval_operand1(scope, val1); + rc = eval_operand1(ent, scope, val1); if (rc == false) return false; - rc = eval_operand2(scope, val2); + rc = eval_operand2(ent, scope, val2); if (rc == false) return false; @@ -68,9 +59,13 @@ val = val1 % val2; break; case REM: + if (val2 == 0) + return false; + val = val1 - (val1 / val2) * val2; return false; case POW: - return false; + val = (int64_t) pow(val1, val2); + break; case xCONCAT: // not possible return false; } @@ -78,151 +73,98 @@ return true; } -bool ExpAttribute::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpAttribute::test_array_type(const VType*type) const { - /* Special Case: The array attributes can sometimes be calculated all - the down to a literal integer at compile time, and all it - needs is the type of the base expression. (The base - expression doesn't even need to be evaluated.) */ - if (name_ == "length" || name_ == "right" || name_ == "left") { - const VType*base_type = base_->peek_type(); - - if(!base_type) { - const ExpName*name = NULL; - - if(scope && (name = dynamic_cast(base_))) { - const perm_string& n = name->peek_name(); - if(const Variable*var = scope->find_variable(n)) - base_type = var->peek_type(); - else if(const Signal*sig = scope->find_signal(n)) - base_type = sig->peek_type(); - else if(const InterfacePort*port = scope->find_param(n)) - base_type = port->type; - } - } - - if(!base_type) - return false; // I tried really hard, sorry - - const VTypeArray*arr = dynamic_cast(base_type); - if (arr == 0) { - cerr << endl << get_fileline() << ": error: " - << "Cannot apply the '" << name_ << " attribute to non-array objects" - << endl; - ivl_assert(*this, false); - return false; - } - - if(name_ == "length") { - int64_t size = arr->get_width(scope); + const VTypeArray*arr = dynamic_cast(type); - if(size > 0) - val = size; - else - return false; - } else if(name_ == "left") { - arr->dimension(0).msb()->evaluate(scope, val); - } else if(name_ == "right") { - arr->dimension(0).lsb()->evaluate(scope, val); - } else ivl_assert(*this, false); - - return true; + if (arr == 0) { + cerr << endl << get_fileline() << ": error: " + << "Cannot apply the '" << name_ << " attribute to non-array objects" + << endl; + ivl_assert(*this, false); + return false; } - return false; -} - -bool ExpAttribute::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const -{ - if (!ent || !scope) { // it's impossible to evaluate, probably it is inside a subprogram - return false; + if (arr->dimensions().size() > 1) { + cerr << endl << get_fileline() << ": error: " + << "Cannot apply the '" << name_ + << " attribute to multidimensional arrays" << endl; + return false; } - if (name_ == "left" || name_ == "right") { - const VType*base_type = base_->peek_type(); - if (base_type == 0) - base_type = base_->probe_type(ent, scope); + if (arr->dimension(0).is_box()) + return false; - ivl_assert(*this, base_type); - - const VTypeArray*arr = dynamic_cast(base_type); - if (arr == 0) { - cerr << endl << get_fileline() << ": error: " - << "Cannot apply the '" << name_ - << " attribute to non-array objects" << endl; - ivl_assert(*this, false); - return false; - } + return true; +} - ivl_assert(*this, arr->dimensions() == 1); - if(name_ == "left") - arr->dimension(0).msb()->evaluate(ent, scope, val); - else // "right" - arr->dimension(0).lsb()->evaluate(ent, scope, val); +bool ExpAttribute::evaluate_type_attr(const VType*type, Entity*ent, ScopeBase*scope, int64_t&val) const +{ + if (name_ == "length" && test_array_type(type)) { + int64_t size = type->get_width(scope); - return true; + if(size > 0) { + val = size; + return true; + } + } else if (name_ == "left" && test_array_type(type)) { + const VTypeArray*arr = dynamic_cast(type); + return arr->dimension(0).msb()->evaluate(ent, scope, val); + } else if (name_ == "right" && test_array_type(type)) { + const VTypeArray*arr = dynamic_cast(type); + return arr->dimension(0).lsb()->evaluate(ent, scope, val); } - return evaluate(scope, val); -} - -/* - * I don't yet know how to evaluate concatenations. It is not likely - * to come up anyhow. - */ -bool ExpConcat::evaluate(ScopeBase*, int64_t&) const -{ return false; } -bool ExpName::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpObjAttribute::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { - const VType*type; - Expression*exp; + const VType*base_type = base_->peek_type(); - if (prefix_.get()) { - cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; - return false; - } + if (base_type == NULL) + base_type = base_->probe_type(ent, scope); - if (!scope) - return false; + if (base_type) + return evaluate_type_attr(base_type, ent, scope, val); - if (!scope->find_constant(name_, type, exp)) - return false; + return false; +} - return exp->evaluate(scope, val); +bool ExpTypeAttribute::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const +{ + return evaluate_type_attr(base_, ent, scope, val); } bool ExpName::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { if (prefix_.get()) { - cerr << get_fileline() << ": sorry: I don't know how to evaluate ExpName prefix parts." << endl; - return false; + cerr << get_fileline() << ": sorry: I don't know how to evaluate " + << "ExpName prefix parts." << endl; + return false; } - const InterfacePort*gen = ent->find_generic(name_); - if (gen) { - cerr << get_fileline() << ": sorry: I don't necessarily handle generic overrides." << endl; + if (scope) { + const VType*type; + Expression*exp; - // Evaluate the default expression and use that. - if (gen->expr) - return gen->expr->evaluate(ent, scope, val); + if (scope->find_constant(name_, type, exp)) + return exp->evaluate(ent, scope, val); } - return evaluate(scope, val); + return false; } -bool ExpShift::evaluate(ScopeBase*scope, int64_t&val) const +bool ExpShift::evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const { int64_t val1, val2; bool rc; - rc = eval_operand1(scope, val1); + rc = eval_operand1(ent, scope, val1); if (rc == false) return false; - rc = eval_operand2(scope, val2); + rc = eval_operand2(ent, scope, val2); if (rc == false) return false; @@ -247,7 +189,7 @@ return true; } -bool ExpTime::evaluate(ScopeBase*, int64_t&val) const +/*bool ExpTime::evaluate(Entity*, ScopeBase*, int64_t&val) const { double v = to_fs(); @@ -260,8 +202,4 @@ val = v; return true; } - -bool ExpTime::evaluate(Entity*, ScopeBase*, int64_t&val) const -{ - return evaluate(NULL, NULL, val); -} +}*/ diff -Nru iverilog-10.3/vhdlpp/expression.h iverilog-11.0/vhdlpp/expression.h --- iverilog-10.3/vhdlpp/expression.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/expression.h 2020-09-26 22:44:25.000000000 +0000 @@ -3,6 +3,7 @@ /* * Copyright (c) 2011-2018 Stephen Williams (steve@icarus.com) * Copyright CERN 2015 / Stephen Williams (steve@icarus.com), + * Copyright CERN 2016 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it @@ -28,9 +29,9 @@ # include # include # include +# include -class prange_t; -class Entity; +class ExpRange; class ScopeBase; class SubprogramHeader; class VType; @@ -42,9 +43,25 @@ #define unique_ptr auto_ptr #endif +/* + * Helper class to recursively traverse an expression tree + * (i.e. complex expressions). + */ struct ExprVisitor { - virtual ~ExprVisitor() {}; + ExprVisitor() : level_(0) {} + virtual ~ExprVisitor() {} virtual void operator() (Expression*s) = 0; + + // Methods to manage recursion depth. Every Expression::visit() method + // should call down() in the beginning and up() in the end. + inline void down() { ++level_; } + inline void up() { --level_; assert(level_ >= 0); } + +protected: + int level() const { return level_; } + +private: + int level_; }; /* @@ -101,18 +118,18 @@ // The emit virtual method is called by architecture emit to // output the generated code for the expression. The derived // class fills in the details of what exactly happened. - virtual int emit(ostream&out, Entity*ent, ScopeBase*scope) =0; + virtual int emit(ostream&out, Entity*ent, ScopeBase*scope) const =0; // The emit_package virtual message is similar, but is called // in a package context and to emit SV packages. - virtual int emit_package(std::ostream&out); + virtual int emit_package(std::ostream&out) const; // The evaluate virtual method tries to evaluate expressions // to constant literal values. Return true and set the val // argument if the evaluation works, or return false if it // cannot be done. - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; - virtual bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; + virtual bool evaluate(Entity*, ScopeBase*, int64_t&) const { return false; } + bool evaluate(ScopeBase*scope, int64_t&val) const { return evaluate(NULL, scope, val); } // The symbolic compare returns true if the two expressions // are equal without actually calculating the value. @@ -129,8 +146,8 @@ virtual void dump(ostream&out, int indent = 0) const =0; virtual ostream& dump_inline(ostream&out) const; - // Recursively visits a tree of expressions (useful of complex expressions). - virtual void visit(ExprVisitor& func) { func(this); } + // Recursively visits a tree of expressions (useful for complex expressions). + virtual void visit(ExprVisitor& func) { func.down(); func(this); func.up(); } protected: // This function is called by the derived class during @@ -166,12 +183,13 @@ class ExpUnary : public Expression { public: - ExpUnary(Expression*op1); + explicit ExpUnary(Expression*op1); virtual ~ExpUnary() =0; inline const Expression*peek_operand() const { return operand1_; } const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; + const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void visit(ExprVisitor& func); @@ -179,7 +197,7 @@ inline void write_to_stream_operand1(std::ostream&fd) const { operand1_->write_to_stream(fd); } - int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope); + int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const; void dump_operand1(ostream&out, int indent = 0) const; private: @@ -205,11 +223,11 @@ protected: int elaborate_exprs(Entity*, ScopeBase*, const VType*); - int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope); - int emit_operand2(ostream&out, Entity*ent, ScopeBase*scope); + int emit_operand1(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_operand2(ostream&out, Entity*ent, ScopeBase*scope) const; - bool eval_operand1(ScopeBase*scope, int64_t&val) const; - bool eval_operand2(ScopeBase*scope, int64_t&val) const; + bool eval_operand1(Entity*ent, ScopeBase*scope, int64_t&val) const; + bool eval_operand2(Entity*ent, ScopeBase*scope, int64_t&val) const; inline void write_to_stream_operand1(std::ostream&out) const { operand1_->write_to_stream(out); } @@ -242,7 +260,7 @@ // Create a named choice explicit choice_t(perm_string name); // discreate_range choice - explicit choice_t(prange_t*ran); + explicit choice_t(ExpRange*ran); choice_t(const choice_t&other); @@ -252,15 +270,15 @@ bool others() const; // Return expression if this represents a simple_expression. Expression*simple_expression(bool detach_flag =true); - // Return prange_t if this represents a range_expression - prange_t*range_expressions(void); + // Return ExpRange if this represents a range_expression + ExpRange*range_expressions(void); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: std::unique_ptrexpr_; - std::unique_ptr range_; + std::unique_ptr range_; private: // not implemented choice_t& operator= (const choice_t&); }; @@ -303,24 +321,23 @@ }; public: - ExpAggregate(std::list*el); + explicit ExpAggregate(std::list*el); ~ExpAggregate(); Expression*clone() const; - const VType*probe_type(Entity*ent, ScopeBase*scope) const; const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); private: int elaborate_expr_array_(Entity*ent, ScopeBase*scope, const VTypeArray*ltype); int elaborate_expr_record_(Entity*ent, ScopeBase*scope, const VTypeRecord*ltype); - int emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*ltype); - int emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*ltype); + int emit_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*ltype) const; + int emit_record_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeRecord*ltype) const; private: // This is the elements as directly parsed. @@ -346,8 +363,8 @@ int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + virtual bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: @@ -360,27 +377,70 @@ class ExpAttribute : public Expression { public: - ExpAttribute(ExpName*base, perm_string name); - ~ExpAttribute(); + ExpAttribute(perm_string name,std::list*args); + virtual ~ExpAttribute(); + + inline perm_string peek_attribute() const { return name_; } + + // Constants for the standard attributes + static const perm_string LEFT; + static const perm_string RIGHT; + + protected: + std::list*clone_args() const; + int elaborate_args(Entity*ent, ScopeBase*scope, const VType*ltype); + void visit_args(ExprVisitor& func); + + bool evaluate_type_attr(const VType*type, Entity*ent, ScopeBase*scope, int64_t&val) const; + bool test_array_type(const VType*type) const; + + perm_string name_; + std::list*args_; +}; + +class ExpObjAttribute : public ExpAttribute { + public: + ExpObjAttribute(ExpName*base, perm_string name, std::list*args); + ~ExpObjAttribute(); Expression*clone() const; - inline perm_string peek_attribute() const { return name_; } inline const ExpName* peek_base() const { return base_; } + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); // Some attributes can be evaluated at compile time - bool evaluate(ScopeBase*scope, int64_t&val) const; bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); private: ExpName*base_; - perm_string name_; +}; + +class ExpTypeAttribute : public ExpAttribute { + public: + ExpTypeAttribute(const VType*base, perm_string name, std::list*args); + // no destructor - VType objects (base_) are shared between many expressions + + Expression*clone() const; + + inline const VType* peek_base() const { return base_; } + + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + const VType*probe_type(Entity*ent, ScopeBase*scope) const; + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void write_to_stream(std::ostream&fd) const; + // Some attributes can be evaluated at compile time + bool evaluate(ScopeBase*scope, int64_t&val) const; + bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; + void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); + + private: + const VType*base_; }; class ExpBitstring : public Expression { @@ -395,7 +455,7 @@ const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: @@ -406,7 +466,7 @@ class ExpCharacter : public Expression { public: - ExpCharacter(char val); + explicit ExpCharacter(char val); ExpCharacter(const ExpCharacter&other) : Expression() { value_ = other.value_; } ~ExpCharacter(); @@ -415,7 +475,7 @@ const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; @@ -423,7 +483,7 @@ private: int emit_primitive_bit_(ostream&out, Entity*ent, ScopeBase*scope, - const VTypePrimitive*etype); + const VTypePrimitive*etype) const; private: char value_; @@ -443,8 +503,7 @@ const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); @@ -471,12 +530,13 @@ case_t(const case_t&other); ~case_t(); - inline Expression*condition() { return cond_; } + inline Expression*condition() const { return cond_; } inline void set_condition(Expression*cond) { cond_ = cond; } + inline const std::list& true_clause() const { return true_clause_; } int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*lt); - int emit_option(ostream&out, Entity*ent, ScopeBase*scope); - int emit_default(ostream&out, Entity*ent, ScopeBase*scope); + int emit_option(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_default(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; std::list& extract_true_clause() { return true_clause_; } void visit(ExprVisitor& func); @@ -496,7 +556,7 @@ const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); @@ -536,7 +596,7 @@ inline fun_t edge_fun() const { return fun_; } void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: @@ -562,20 +622,23 @@ const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; - void visit(ExprVisitor& func); // NOTE: does not handle expressions in subprogram + void visit(ExprVisitor& func); // NOTE: does not handle expressions in subprogram body + + // Returns a subprogram header that matches the function call + SubprogramHeader*match_signature(Entity*ent, ScopeBase*scope) const; private: perm_string name_; std::vector argv_; - SubprogramHeader*def_; + mutable SubprogramHeader*def_; }; class ExpInteger : public Expression { public: - ExpInteger(int64_t val); + explicit ExpInteger(int64_t val); ExpInteger(const ExpInteger&other) : Expression(), value_(other.value_) {} ~ExpInteger(); @@ -584,10 +647,10 @@ const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - int emit_package(std::ostream&out); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_package(std::ostream&out) const; bool is_primary(void) const { return true; } - bool evaluate(ScopeBase*scope, int64_t&val) const; + bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; virtual ostream& dump_inline(ostream&out) const; @@ -598,7 +661,7 @@ class ExpReal : public Expression { public: - ExpReal(double val); + explicit ExpReal(double val); ExpReal(const ExpReal&other) : Expression(), value_(other.value_) {} ~ExpReal(); @@ -607,8 +670,8 @@ const VType*probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - int emit_package(std::ostream&out); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit_package(std::ostream&out) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; virtual ostream& dump_inline(ostream&out) const; @@ -634,7 +697,7 @@ int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: @@ -651,31 +714,26 @@ public: explicit ExpName(perm_string nn); ExpName(perm_string nn, std::list*indices); - ExpName(perm_string nn, Expression*msb, Expression*lsb); - ExpName(ExpName*prefix, perm_string nn); - ExpName(ExpName*prefix, perm_string nn, Expression*msb, Expression*lsb); - ~ExpName(); + ExpName(ExpName*prefix, perm_string nn, std::list*indices = NULL); + virtual ~ExpName(); public: // Base methods - Expression*clone() const { - return new ExpName(static_cast(safe_clone(prefix_.get())), - name_, safe_clone(index_), safe_clone(lsb_)); - } + Expression*clone() const; int elaborate_lval(Entity*ent, ScopeBase*scope, bool); int elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*); const VType* probe_type(Entity*ent, ScopeBase*scope) const; const VType* fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*host) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit_indices(ostream&out, Entity*ent, ScopeBase*scope) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; - bool evaluate(ScopeBase*scope, int64_t&val) const; bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; bool symbolic_compare(const Expression*that) const; void dump(ostream&out, int indent = 0) const; inline const char* name() const { return name_; } inline const perm_string& peek_name() const { return name_; } - void set_range(Expression*msb, Expression*lsb); + void add_index(std::list*idx); void visit(ExprVisitor& func); private: @@ -689,7 +747,7 @@ delete offset_; } - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; private: Expression*idx_; @@ -703,14 +761,14 @@ const VType* probe_prefix_type_(Entity*ent, ScopeBase*scope) const; const VType* probe_prefixed_type_(Entity*ent, ScopeBase*scope) const; - int emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope); + int emit_as_prefix_(ostream&out, Entity*ent, ScopeBase*scope) const; // There are some workarounds required for constant arrays/records, as // they are currently emitted as flat localparams (without any type // information). The following workarounds adjust the access indices // to select appropriate parts of the localparam. bool try_workarounds_(ostream&out, Entity*ent, ScopeBase*scope, - list&indices, int&data_size); + list&indices, int&data_size) const; bool check_const_array_workaround_(const VTypeArray*arr, ScopeBase*scope, list&indices, int&data_size) const; @@ -719,22 +777,22 @@ list&indices, int&data_size) const; int emit_workaround_(ostream&out, Entity*ent, ScopeBase*scope, - const list&indices, int field_size); + const list&indices, int field_size) const; private: + Expression*index(unsigned int number) const; + std::unique_ptr prefix_; perm_string name_; - Expression*index_; - Expression*lsb_; + std::list*indices_; }; class ExpNameALL : public ExpName { public: - ExpNameALL() : ExpName(perm_string()) { } + ExpNameALL() : ExpName(empty_perm_string) { } public: - int elaborate_lval(Entity*ent, ScopeBase*scope, bool); const VType* probe_type(Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent =0) const; }; @@ -757,13 +815,71 @@ const VType* probe_type(Entity*ent, ScopeBase*scope) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; private: fun_t fun_; }; +/* + * Helper class to handle name expressions coming from another scope. As such, + * we get more information regarding their type, etc. from the associated scope. + */ +class ExpScopedName : public Expression { + public: + ExpScopedName(perm_string scope, ExpName*exp); + ~ExpScopedName(); + + Expression*clone() const + { return new ExpScopedName(scope_name_, static_cast(name_->clone())); } + + int elaborate_lval(Entity*ent, ScopeBase*scope, bool is_sequ) + { return name_->elaborate_lval(ent, get_scope(scope), is_sequ); } + + int elaborate_rval(Entity*ent, ScopeBase*scope, const InterfacePort*lval) + { return name_->elaborate_rval(ent, get_scope(scope), lval); } + + const VType* probe_type(Entity*ent, ScopeBase*scope) const + { return name_->probe_type(ent, get_scope(scope)); } + + const VType* fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*host) const + { return name_->fit_type(ent, get_scope(scope), host); } + + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype) + { return name_->elaborate_expr(ent, get_scope(scope), ltype); } + + void write_to_stream(std::ostream&fd) const + { name_->write_to_stream(fd); } + + int emit(ostream&out, Entity*ent, ScopeBase*scope) const { + out << scope_name_ << "."; + return name_->emit(out, ent, scope); + } + + bool is_primary(void) const + { return name_->is_primary(); } + + bool evaluate(Entity*ent, ScopeBase*, int64_t&val) const + { return name_->evaluate(ent, scope_, val); } + + bool symbolic_compare(const Expression*that) const + { return name_->symbolic_compare(that); } + + void dump(ostream&out, int indent = 0) const; + + void visit(ExprVisitor&func); + + private: + // Functions that resolve the origin scope for the name expression + ScopeBase*get_scope(const ScopeBase*scope); + ScopeBase*get_scope(const ScopeBase*scope) const; + + perm_string scope_name_; + ScopeBase*scope_; + ExpName*name_; +}; + class ExpShift : public ExpBinary { public: enum shift_t { SRL, SLL, SRA, SLA, ROL, ROR }; @@ -777,8 +893,8 @@ int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - virtual bool evaluate(ScopeBase*scope, int64_t&val) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: @@ -797,41 +913,58 @@ const VType*fit_type(Entity*ent, ScopeBase*scope, const VTypeArray*atype) const; int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; bool is_primary(void) const; void dump(ostream&out, int indent = 0) const; - const std::vector& get_value() const { return value_; } + const std::string& get_value() const { return value_; } + + // Converts quotation marks (") to its escaped + // counterpart in SystemVerilog (\") + static std::string escape_quot(const std::string& str); private: - int emit_as_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*arr); + int emit_as_array_(ostream&out, Entity*ent, ScopeBase*scope, const VTypeArray*arr) const; private: - std::vector value_; + std::string value_; }; class ExpUAbs : public ExpUnary { public: - ExpUAbs(Expression*op1); + explicit ExpUAbs(Expression*op1); ~ExpUAbs(); Expression*clone() const { return new ExpUAbs(peek_operand()->clone()); } void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; }; class ExpUNot : public ExpUnary { public: - ExpUNot(Expression*op1); + explicit ExpUNot(Expression*op1); ~ExpUNot(); Expression*clone() const { return new ExpUNot(peek_operand()->clone()); } void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + void dump(ostream&out, int indent = 0) const; +}; + +class ExpUMinus : public ExpUnary { + + public: + explicit ExpUMinus(Expression*op1); + ~ExpUMinus(); + + Expression*clone() const { return new ExpUMinus(peek_operand()->clone()); } + + void write_to_stream(std::ostream&fd) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; }; @@ -850,7 +983,7 @@ return base_->elaborate_expr(ent, scope, type_); } void write_to_stream(std::ostream&fd) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); @@ -866,14 +999,14 @@ class ExpNew : public Expression { public: - ExpNew(Expression*size); + explicit ExpNew(Expression*size); ~ExpNew(); Expression*clone() const { return new ExpNew(size_->clone()); } // There is no 'new' in VHDL - do not emit anything void write_to_stream(std::ostream&) const {}; - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; void dump(ostream&out, int indent = 0) const; void visit(ExprVisitor& func); @@ -891,9 +1024,8 @@ int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); void write_to_stream(std::ostream&) const; - int emit(ostream&out, Entity*ent, ScopeBase*scope); - bool evaluate(ScopeBase*scope, int64_t&val) const; - bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + //bool evaluate(Entity*ent, ScopeBase*scope, int64_t&val) const; void dump(ostream&out, int indent = 0) const; private: @@ -903,6 +1035,68 @@ timeunit_t unit_; }; +class ExpRange : public Expression { + public: + typedef enum { DOWNTO, TO, AUTO } range_dir_t; + + // Regular range + ExpRange(Expression*left_idx, Expression*right_idx, range_dir_t dir); + // 'range/'reverse range attribute + ExpRange(ExpName*base, bool reverse_range); + ~ExpRange(); + + Expression*clone() const; + + // Returns the upper boundary + Expression*msb(); + // Returns the lower boundary + Expression*lsb(); + + Expression*left(); + Expression*right(); + + range_dir_t direction() const { return direction_; } + + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void write_to_stream(std::ostream&) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + void dump(ostream&out, int indent = 0) const; + private: + // Regular range related fields + Expression*left_, *right_; + range_dir_t direction_; + + // 'range/'reverse_range attribute related fields + // Flag to indicate it is a 'range/'reverse_range expression + bool range_expr_; + // Object name to which the attribute is applied + ExpName*range_base_; + // Flag to distinguish between 'range & 'reverse_range + bool range_reverse_; +}; + +// Helper class that wraps other expression to specify delay. +class ExpDelay : public Expression { +public: + ExpDelay(Expression*expr, Expression*delay); + ~ExpDelay(); + + Expression*clone() const { return new ExpDelay(expr_->clone(), delay_->clone()); } + + int elaborate_expr(Entity*ent, ScopeBase*scope, const VType*ltype); + void write_to_stream(std::ostream&) const; + int emit(ostream&out, Entity*ent, ScopeBase*scope) const; + void dump(ostream&out, int indent = 0) const; + void visit(ExprVisitor& func); + + const Expression*peek_expr() const { return expr_; } + const Expression*peek_delay() const { return delay_; } + +private: + Expression*expr_; + Expression*delay_; +}; + #if __cplusplus < 201103L #undef unique_ptr #endif diff -Nru iverilog-10.3/vhdlpp/expression_stream.cc iverilog-11.0/vhdlpp/expression_stream.cc --- iverilog-10.3/vhdlpp/expression_stream.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/expression_stream.cc 2020-09-26 22:44:25.000000000 +0000 @@ -61,13 +61,9 @@ return; } - if (prange_t*rp = range_expressions()) { - rp->msb()->write_to_stream(fd); - if (rp->is_downto()) - fd << " downto "; - else - fd << " to "; - rp->msb()->write_to_stream(fd); + if (ExpRange*rp = range_expressions()) { + rp->write_to_stream(fd); + return; } fd << "/* ERROR */"; @@ -111,7 +107,13 @@ out << ")"; } -void ExpAttribute::write_to_stream(ostream&fd) const +void ExpObjAttribute::write_to_stream(ostream&fd) const +{ + base_->write_to_stream(fd); + fd << "'" << name_; +} + +void ExpTypeAttribute::write_to_stream(ostream&fd) const { base_->write_to_stream(fd); fd << "'" << name_; @@ -179,9 +181,32 @@ fd << value_; } -void ExpLogical::write_to_stream(ostream&) const +void ExpLogical::write_to_stream(ostream&out) const { - ivl_assert(*this, !"Not supported"); + peek_operand1()->write_to_stream(out); + + switch (fun_) { + case AND: + out << " and "; + break; + case OR: + out << " or "; + break; + case XOR: + out << " xor "; + break; + case NAND: + out << " nand "; + break; + case NOR: + out << " nor "; + break; + case XNOR: + out << " xnor "; + break; + } + + peek_operand2()->write_to_stream(out); } void ExpName::write_to_stream(ostream&fd) const @@ -192,14 +217,20 @@ } fd << name_; - if (index_) { - fd << "("; - index_->write_to_stream(fd); - if (lsb_) { - fd << " downto "; - lsb_->write_to_stream(fd); - } - fd << ")"; + + if (indices_) { + fd << "("; + bool first = true; + for(list::const_iterator it = indices_->begin(); + it != indices_->end(); ++it) { + if(first) + first = false; + else + fd << ","; + + (*it)->write_to_stream(fd); + } + fd << ")"; } } @@ -272,10 +303,15 @@ void ExpString::write_to_stream(ostream&fd) const { fd << "\""; - for(vector::const_iterator it = value_.begin(); - it != value_.end(); ++it) { - fd << *it; + + // Restore double quotation marks + for(string::const_iterator it = value_.begin(); it != value_.end(); ++it) { + if(*it == '"') + fd << "\"\""; + else + fd << *it; } + fd << "\""; } @@ -291,6 +327,13 @@ write_to_stream_operand1(fd); } +void ExpUMinus::write_to_stream(ostream&fd) const +{ + fd << "-("; + write_to_stream_operand1(fd); + fd << ")"; +} + void ExpCast::write_to_stream(ostream&fd) const { // Type casting is introduced only for a few specific cases in @@ -311,3 +354,26 @@ case S: fd << " s"; break; } } + +void ExpRange::write_to_stream(ostream&fd) const +{ + if(range_expr_) { + range_base_->write_to_stream(fd); + fd << (range_reverse_ ? "'reverse_range" : "'range"); + } else { + left_->write_to_stream(fd); + switch(direction_) { + case DOWNTO: fd << " downto "; break; + case TO: fd << " to "; break; + default: ivl_assert(*this, false); break; + } + right_->write_to_stream(fd); + } +} + +void ExpDelay::write_to_stream(ostream&out) const +{ + expr_->write_to_stream(out); + out << " after "; + delay_->write_to_stream(out); +} diff -Nru iverilog-10.3/vhdlpp/lexor.lex iverilog-11.0/vhdlpp/lexor.lex --- iverilog-10.3/vhdlpp/lexor.lex 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/lexor.lex 2020-09-26 22:44:25.000000000 +0000 @@ -111,9 +111,7 @@ return CHARACTER_LITERAL; } -(\"([^\"]|(\"\"))*?\")|(\"[^\"]*\") { -/* first pattern: string literals with doubled quotation mark */ -/* second pattern: string literals without doubled quotation */ +(\"([^\"]|(\"\"))*?\") { yylval.text = escape_quot_and_dup(yytext); assert(yylval.text); return STRING_LITERAL; @@ -165,6 +163,9 @@ } {based_literal} { + for(char*cp = yytext ; *cp ; ++cp) + *cp = tolower(*cp); + if(!are_underscores_correct(yytext) || !is_based_correct(yytext)) std::cerr << "An invalid form of based literal:" << yytext << std::endl; @@ -237,6 +238,14 @@ return 1; } +static bool is_char_ok(char c, int base) +{ + if(base <= 10) + return '0' <= c && c - '0' < base; + else + return isdigit(c) || (c >= 'a' && c < 'a' + base - 10); +} + /** * This function checks if the format of a based number * is correct according to the VHDL standard @@ -248,8 +257,8 @@ { char* ptr; //BASE examination - char clean_base[4]; - clean_base[3] = '\0'; + char clean_base[4] = {0,}; + char* clean_base_end = clean_base + sizeof(clean_base); char* clean_base_ptr = clean_base; for(ptr = text; ptr != strchr(text, '#'); ++ptr) { @@ -257,7 +266,7 @@ ++ptr; if(!(*ptr >= '0' && *ptr <= '9')) //the base uses chars other than digits return 0; - if(*clean_base_ptr == '\0') + if(clean_base_ptr == clean_base_end) break; *clean_base_ptr = *ptr; ++clean_base_ptr; @@ -280,20 +289,7 @@ return 0; } bool point = false; - set allowed_chars; - unsigned c; - if(base <= 10) { - for(c = 0; c < base; ++c) - allowed_chars.insert(c + '0'); - } - else - { - for(c = 0; c < 10; ++c) - allowed_chars.insert(c + '0'); - for(c = 0; c < base - 10; ++c) - allowed_chars.insert(c + 'a'); - } //MANTISSA examination for(ptr = strchr(text, '#') + 1, length = 0; ptr != strrchr(text, '#'); ++ptr) { @@ -309,9 +305,10 @@ continue; } } - //the number consists of other chars than allowed - if(allowed_chars.find(*ptr) == allowed_chars.end()) + //check if the number consists of other chars than allowed + if(!is_char_ok(*ptr, base)) return 0; + ++length; } if(length == 0) diff -Nru iverilog-10.3/vhdlpp/library.cc iverilog-11.0/vhdlpp/library.cc --- iverilog-10.3/vhdlpp/library.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/library.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,6 +1,8 @@ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) + * Copyright CERN 2016 + * @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 @@ -23,6 +25,7 @@ # include "compiler.h" # include "package.h" # include "std_types.h" +# include "std_funcs.h" # include # include # include @@ -73,9 +76,9 @@ library_search_path.push_front(directory); } -SubprogramHeader*library_find_subprogram(perm_string name) +SubprogramHeader*library_match_subprogram(perm_string name, const list*params) { - SubprogramHeader*subp = NULL; + SubprogramHeader*subp; map::const_iterator lib_it; for(lib_it = libraries.begin(); lib_it != libraries.end(); ++lib_it) { @@ -83,7 +86,7 @@ map::const_iterator pack_it; for(pack_it = lib.packages.begin(); pack_it != lib.packages.end(); ++pack_it) { - if((subp = pack_it->second->find_subprogram(name))) + if((subp = pack_it->second->match_subprogram(name, params))) return subp; } } @@ -108,7 +111,7 @@ } static void import_ieee(void); -static void import_ieee_use(ActiveScope*res, perm_string package, perm_string name); +static void import_ieee_use(const YYLTYPE&loc, ActiveScope*res, perm_string package, perm_string name); static void import_std_use(const YYLTYPE&loc, ActiveScope*res, perm_string package, perm_string name); static void dump_library_package(ostream&file, perm_string lname, perm_string pname, Package*pack) @@ -240,7 +243,7 @@ // Special case handling for the IEEE library. if (use_library == "ieee") { - import_ieee_use(res, use_package, use_name); + import_ieee_use(loc, res, use_package, use_name); return; } // Special case handling for the STD library. @@ -310,8 +313,30 @@ } } -static void import_ieee_use_std_logic_arith(ActiveScope*, perm_string) +static void import_ieee_use_std_logic_misc(ActiveScope*, perm_string name) { + bool all_flag = name=="all"; + list*args; + + if (all_flag || name == "or_reduce") { + /* function or_reduce(arg : std_logic_vector) return std_logic; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("or_reduce"), + perm_string::literal("|"), + args, &primitive_STDLOGIC)); + } + + if (all_flag || name == "and_reduce") { + /* function and_reduce(arg : std_logic_vector) return std_logic; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("and_reduce"), + perm_string::literal("&"), + args, &primitive_STDLOGIC)); + } } static void import_ieee_use_numeric_bit(ActiveScope*res, perm_string name) @@ -338,7 +363,8 @@ } } -static void import_ieee_use(ActiveScope*res, perm_string package, perm_string name) +static void import_ieee_use(const YYLTYPE&/*loc*/, ActiveScope*res, + perm_string package, perm_string name) { if (package == "std_logic_1164") { import_ieee_use_std_logic_1164(res, name); @@ -346,10 +372,20 @@ } if (package == "std_logic_arith") { - import_ieee_use_std_logic_arith(res, name); + // arithmetic operators for std_logic_vector return; } + if (package == "std_logic_misc") { + import_ieee_use_std_logic_misc(res, name); + return; + } + + if (package == "std_logic_unsigned") { + // arithmetic operators for std_logic_vector + return; + } + if (package == "numeric_bit") { import_ieee_use_numeric_bit(res, name); return; @@ -359,18 +395,24 @@ import_ieee_use_numeric_std(res, name); return; } + + cerr << "Warning: Package ieee." << package.str() <<" is not yet supported" << endl; } -static void import_std_use(const YYLTYPE&loc, ActiveScope*/*res*/, perm_string package, perm_string name) +static void import_std_use(const YYLTYPE&/*loc*/, ActiveScope*res, + perm_string package, perm_string /*name*/) { if (package == "standard") { // do nothing return; } else if (package == "textio") { - cerr << "warning: textio package not really supported" << endl; + res->use_name(perm_string::literal("text"), &primitive_INTEGER); + res->use_name(perm_string::literal("line"), &primitive_STRING); + res->use_name(type_FILE_OPEN_KIND.peek_name(), &type_FILE_OPEN_KIND); + res->use_name(type_FILE_OPEN_STATUS.peek_name(), &type_FILE_OPEN_STATUS); return; } else { - sorrymsg(loc, "package %s of library %s not yet supported", package.str(), name.str()); + cerr << "Warning: Package std." << package.str() <<" is not yet supported" << endl; return; } } @@ -415,3 +457,27 @@ return 0; } + +static int elaborate_library_packages(mappackages) +{ + int errors = 0; + + for (map::iterator cur = packages.begin() + ; cur != packages.end() ; ++cur) { + errors += cur->second->elaborate(); + } + + return errors; +} + +int elaborate_libraries() +{ + int errors = 0; + + for (map::iterator cur = libraries.begin() + ; cur != libraries.end() ; ++cur) { + errors += elaborate_library_packages(cur->second.packages); + } + + return errors; +} diff -Nru iverilog-10.3/vhdlpp/library.h iverilog-11.0/vhdlpp/library.h --- iverilog-10.3/vhdlpp/library.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/library.h 2020-09-26 22:44:25.000000000 +0000 @@ -19,12 +19,18 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + class SubprogramHeader; +class VType; + +void library_set_work_path(const char*work_path); +void library_add_directory(const char*directory); -extern void library_set_work_path(const char*work_path); -extern void library_add_directory(const char*directory); +int elaborate_libraries(void); +int emit_packages(void); -extern SubprogramHeader*library_find_subprogram(perm_string name); +SubprogramHeader*library_match_subprogram(perm_string name, const list*params); #endif /* IVL_library_H */ diff -Nru iverilog-10.3/vhdlpp/main.cc iverilog-11.0/vhdlpp/main.cc --- iverilog-10.3/vhdlpp/main.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/main.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,6 +1,6 @@ const char COPYRIGHT[] = - "Copyright (c) 2011-2012 Stephen Williams (steve@icarus.com)\n" + "Copyright (c) 2011-2015 Stephen Williams (steve@icarus.com)\n" "Copyright CERN 2012 / Stephen Williams (steve@icarus.com)"; /* * This source code is free software; you can redistribute it @@ -236,20 +236,27 @@ return 3; } + errors = elaborate_libraries(); + if (errors > 0) { + fprintf(stderr, "%d errors elaborating libraries.\n", errors); + parser_cleanup(); + return 4; + } + emit_std_types(cout); errors = emit_packages(); if (errors > 0) { fprintf(stderr, "%d errors emitting packages.\n", errors); parser_cleanup(); - return 4; + return 5; } errors = emit_entities(); if (errors > 0) { fprintf(stderr, "%d errors emitting design.\n", errors); - parser_cleanup(); - return 4; + parser_cleanup(); + return 6; } parser_cleanup(); diff -Nru iverilog-10.3/vhdlpp/Makefile.in iverilog-11.0/vhdlpp/Makefile.in --- iverilog-10.3/vhdlpp/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -85,7 +85,7 @@ rm -f stamp-vhdlpp_config-h vhdlpp_config.h cppcheck: $(O:.o=.cc) - cppcheck --enable=all -f \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ @@ -115,21 +115,20 @@ lexor.cc: $(srcdir)/lexor.lex $(LEX) -s -olexor.cc $(srcdir)/lexor.lex -# Build this in two steps to avoid parallel build issues (see pr3462585) -parse.cc: $(srcdir)/parse.y - $(YACC) --verbose -t -d -o $@ $< -parse.h: parse.cc - mv parse.cc.h $@ 2>/dev/null || mv parse.hh $@ - touch $@ +# Use pattern rules to avoid parallel build issues (see pr3462585) +parse%cc parse%h: $(srcdir)/parse%y + $(YACC) --verbose -t --defines=parse.h -o parse.cc $< lexor_keyword.o: lexor_keyword.cc parse.h lexor_keyword.cc: $(srcdir)/lexor_keyword.gperf gperf -o -i 7 --ignore-case -C -k 1-4,6,9,$$ -H keyword_hash -N check_identifier -t $(srcdir)/lexor_keyword.gperf > lexor_keyword.cc || (rm -f lexor_keyword.cc ; false) -install: all installdirs $(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@ +install: all installdirs installfiles -$(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@: vhdlpp@EXEEXT@ +F = vhdlpp@EXEEXT@ + +installfiles: $(F) | installdirs $(INSTALL_PROGRAM) ./vhdlpp@EXEEXT@ "$(DESTDIR)$(libdir)/ivl$(suffix)/vhdlpp@EXEEXT@" installdirs: $(srcdir)/../mkinstalldirs diff -Nru iverilog-10.3/vhdlpp/package.cc iverilog-11.0/vhdlpp/package.cc --- iverilog-10.3/vhdlpp/package.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/package.cc 2020-09-26 22:44:25.000000000 +0000 @@ -24,6 +24,8 @@ # include "parse_misc.h" # include "std_types.h" # include "ivl_assert.h" +# include +# include Package::Package(perm_string n, const ActiveScope&ref) : Scope(ref), name_(n) @@ -41,6 +43,24 @@ from_library_ = lname; } +int Package::elaborate() +{ + int errors = 0; + + for (map::iterator cur = cur_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++ cur) { + SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + (*it)->set_package(this); + errors += (*it)->elaborate(); + } + } + + return errors; +} + /* * The Package::write_to_stream is used to write the package to the * work space (or library) so writes proper VHDL that the library @@ -55,9 +75,6 @@ // and identifiers. for (map::const_iterator cur = use_types_.begin() ; cur != use_types_.end() ; ++cur) { - const VTypeDef*def = dynamic_cast (cur->second); - if (def == 0) - continue; // Do not include global types in types dump if (is_global_type(cur->first)) @@ -67,9 +84,6 @@ } for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { - const VTypeDef*def = dynamic_cast (cur->second); - if (def == 0) - continue; // Do not include global types in types dump if (is_global_type(cur->first)) @@ -80,31 +94,11 @@ for (map::const_iterator cur = use_types_.begin() ; cur != use_types_.end() ; ++cur) { - - // Do not include global types in types dump - if (is_global_type(cur->first)) - continue; - - if(!dynamic_cast(cur->second)) - fd << "sub"; - - fd << "type " << cur->first << " is "; - cur->second->write_type_to_stream(fd); - fd << "; -- imported" << endl; + cur->second->write_typedef_to_stream(fd, cur->first); } for (map::const_iterator cur = cur_types_.begin() ; cur != cur_types_.end() ; ++cur) { - - // Do not include global types in types dump - if (is_global_type(cur->first)) - continue; - - if(!dynamic_cast(cur->second)) - fd << "sub"; - - fd << "type " << cur->first << " is "; - cur->second->write_type_to_stream(fd); - fd << ";" << endl; + cur->second->write_typedef_to_stream(fd, cur->first); } for (map::const_iterator cur = cur_constants_.begin() @@ -122,10 +116,15 @@ fd << ";" << endl; } - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { - cur->second->write_to_stream(fd); - fd << ";" << endl; + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + (*it)->write_to_stream(fd); + fd << ";" << endl; + } } for (map::const_iterator cur = old_components_.begin() @@ -142,14 +141,20 @@ fd << "end package " << name_ << ";" << endl; fd << "package body " << name_ << " is" << endl; - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++cur) { - SubprogramHeader*subp = cur->second; - if(subp->body()) { - subp->write_to_stream(fd); - fd << " is" << endl; - subp->body()->write_to_stream(fd); + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + const SubprogramHeader*subp = *it; + + if(subp->body()) { + subp->write_to_stream(fd); + fd << " is" << endl; + subp->body()->write_to_stream(fd); + } } - } + } fd << "end " << name_ << ";" << endl; } diff -Nru iverilog-10.3/vhdlpp/package_emit.cc iverilog-11.0/vhdlpp/package_emit.cc --- iverilog-10.3/vhdlpp/package_emit.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/package_emit.cc 2020-09-26 22:44:25.000000000 +0000 @@ -20,8 +20,9 @@ # include "package.h" # include "subprogram.h" -# include # include "ivl_assert.h" +# include +# include using namespace std; @@ -64,14 +65,22 @@ //} fd << "package \\" << name() << " ;" << endl; - for (map::const_iterator cur = cur_subprograms_.begin() + for (map::const_iterator cur = cur_subprograms_.begin() ; cur != cur_subprograms_.end() ; ++ cur) { - // Do not emit unbounded functions, we will just need fixed instances later - if(!cur->second->unbounded()) - errors += cur->second->emit_package(fd); - else - fd << "/* function " << cur->second->name() << - " has to be instantiated, skipping */" << endl; + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + SubprogramHeader*header = *it; + + // Do not emit unbounded functions, we will just need fixed instances later + if(!header->unbounded()) + errors += header->emit_package(fd); + else + fd << "/* function " << header->name() + << " has to be instantiated, skipping */" << endl; + } + } fd << "endpackage /* " << name() << " */" << endl; diff -Nru iverilog-10.3/vhdlpp/package.h iverilog-11.0/vhdlpp/package.h --- iverilog-10.3/vhdlpp/package.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/package.h 2020-09-26 22:44:25.000000000 +0000 @@ -36,12 +36,11 @@ perm_string name() const { return name_; } - SubprogramHeader* recall_subprogram(perm_string name) const; - // This method writes a package header to a library file. void write_to_stream(std::ostream&fd) const; int emit_package(std::ostream&fd) const; + int elaborate(); private: perm_string from_library_; diff -Nru iverilog-10.3/vhdlpp/parse_misc.cc iverilog-11.0/vhdlpp/parse_misc.cc --- iverilog-10.3/vhdlpp/parse_misc.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/parse_misc.cc 2020-09-26 22:44:25.000000000 +0000 @@ -29,6 +29,7 @@ # include "compiler.h" # include # include +# include using namespace std; @@ -76,7 +77,7 @@ const VType*base_type = parse_type_by_name(lex_strings.make(base_name)); if (base_type == 0) { - errormsg(loc, "Unable to find base type %s of array.\n", base_name); + errormsg(loc, "Unable to find array base type '%s'.\n", base_name); return 0; } @@ -96,7 +97,7 @@ vector range (base_array->dimensions()); // For now, I only know how to handle 1 dimension - assert(base_array->dimensions() == 1); + assert(base_array->dimensions().size() == 1); range[0] = VTypeArray::range_t(array_left, array_right, downto); @@ -113,14 +114,18 @@ } const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, - ScopeBase*scope, list*ranges) + ScopeBase*scope, list*ranges) { if (ranges->size() == 1) { - prange_t*tmpr = ranges->front(); - Expression*lef = tmpr->expr_left(); - Expression*rig = tmpr->expr_right(); + ExpRange*tmpr = ranges->front(); + Expression*lef = tmpr->left(); + Expression*rig = tmpr->right(); return calculate_subtype_array(loc, base_name, scope, - lef, tmpr->is_downto(), rig); + lef, + (tmpr->direction() == ExpRange::DOWNTO + ? true + : false), + rig); } sorrymsg(loc, "Don't know how to handle multiple ranges here.\n"); @@ -130,28 +135,37 @@ const VType* calculate_subtype_range(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, Expression*range_left, - bool /* downto*/ , + int direction, Expression*range_right) { const VType*base_type = parse_type_by_name(lex_strings.make(base_name)); if (base_type == 0) { - errormsg(loc, "Unable to find base type %s of range.\n", base_name); + errormsg(loc, "Unable to find range base type '%s'.\n", base_name); return 0; } assert(range_left && range_right); int64_t left_val, right_val; - bool rc = range_left->evaluate(scope, left_val); - if (rc == false) - return 0; + VTypeRange*subtype; - rc = range_right->evaluate(scope, right_val); - if (rc == false) - return 0; + if(range_left->evaluate(scope, left_val) && range_right->evaluate(scope, right_val)) { + subtype = new VTypeRangeConst(base_type, left_val, right_val); + } else { + subtype = new VTypeRangeExpr(base_type, range_left, range_right, direction); + } + + return subtype; +} + +ExpString*parse_char_enums(const char*str) +{ + if(!strcasecmp(str, "LF")) + return new ExpString("\\n"); - VTypeRange*sub_type = new VTypeRange(base_type, left_val, right_val); + if(!strcasecmp(str, "CR")) + return new ExpString("\\r"); - return sub_type; + return NULL; } diff -Nru iverilog-10.3/vhdlpp/parse_misc.h iverilog-11.0/vhdlpp/parse_misc.h --- iverilog-10.3/vhdlpp/parse_misc.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/parse_misc.h 2020-09-26 22:44:25.000000000 +0000 @@ -26,7 +26,8 @@ class Architecture; class Expression; class Package; -class prange_t; +class ExpRange; +class ExpString; class ScopeBase; class VType; @@ -35,11 +36,11 @@ extern const VType* calculate_subtype_array(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, - std::list*ranges); + std::list*ranges); extern const VType* calculate_subtype_range(const YYLTYPE&loc, const char*base_name, ScopeBase*scope, Expression*range_left, - bool downto, + int direction, Expression*range_right); /* @@ -63,4 +64,11 @@ extern void library_use(const YYLTYPE&loc, ActiveScope*res, const char*libname, const char*pack, const char*ident); +/* + * Converts CHARACTER enums to an ExpString* if applicable. + * See the standard VHDL library (package STANDARD) or VHDL-2008/16.3 + * for more details). + */ +extern ExpString*parse_char_enums(const char*str); + #endif /* IVL_parse_misc_H */ diff -Nru iverilog-10.3/vhdlpp/parse_types.h iverilog-11.0/vhdlpp/parse_types.h --- iverilog-10.3/vhdlpp/parse_types.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/parse_types.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_parse_types_H #define IVL_parse_types_H /* - * Copyright (c) 2011,2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2015 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -26,7 +26,8 @@ class named_expr_t { public: - named_expr_t (perm_string n, Expression*e) : name_(n), expr_(e) { } + named_expr_t(perm_string n, Expression*e) : name_(n), expr_(e) { } + ~named_expr_t() { delete expr_; } perm_string name() const { return name_; } Expression* expr() const { return expr_; } @@ -68,38 +69,28 @@ std::list* labels_; }; -class prange_t { - public: - prange_t(Expression* left, Expression* right, bool dir) - : left_(left), right_(right), direction_(dir), auto_dir_(false) {} - prange_t(const prange_t&other) : - left_(other.left_->clone()), right_(other.right_->clone()), - direction_(other.direction_), auto_dir_(other.auto_dir_) {} - ~prange_t() { delete left_; delete right_; } - void dump(ostream&out, int indent) const; - - inline Expression*msb() { return direction_? left_ : right_; } - inline Expression*lsb() { return direction_? right_: left_; } +struct adding_term { + ExpArithmetic::fun_t op; + Expression*term; +}; - inline bool is_downto() const { return direction_; } - inline void set_auto_dir(bool enabled = true) { auto_dir_ = enabled; }; - inline bool is_auto_dir() const { return auto_dir_; } +// Stores information for file declarations containing a file name and open mode +// (VHDL-2008 6.4.2.5) +class file_open_info_t { + public: + file_open_info_t(ExpString*filename__, ExpName*kind__ = NULL) + : kind_(kind__), filename_(filename__) { + // By default files are opened in read-only mode + if(!kind_) kind_ = new ExpName(perm_string::literal("read_mode")); + } + ~file_open_info_t() { delete kind_; delete filename_; } - inline Expression*expr_left() { return left_; } - inline Expression*expr_right() { return right_; } + ExpName*kind() { return kind_; } + ExpString*filename() { return filename_; } private: - Expression *left_, *right_; - bool direction_; - bool auto_dir_; - - private: //not implemented - prange_t operator=(const prange_t&); -}; - -struct adding_term { - ExpArithmetic::fun_t op; - Expression*term; + ExpName*kind_; + ExpString*filename_; }; #endif /* IVL_parse_types_H */ diff -Nru iverilog-10.3/vhdlpp/parse.y iverilog-11.0/vhdlpp/parse.y --- iverilog-10.3/vhdlpp/parse.y 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/parse.y 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ -%pure-parser +%define api.pure %lex-param { yyscan_t yyscanner } %parse-param {yyscan_t yyscanner } %parse-param {const char*file_path} @@ -7,7 +7,7 @@ %{ /* * Copyright (c) 2011-2013 Stephen Williams (steve@icarus.com) - * Copyright CERN 2012-2014 / Stephen Williams (steve@icarus.com), + * Copyright CERN 2012-2016 / Stephen Williams (steve@icarus.com), * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it @@ -86,10 +86,7 @@ static ActiveScope*active_scope = new ActiveScope; static stack scope_stack; static SubprogramHeader*active_sub = NULL; - -// perm_strings for attributes -const static perm_string left_attr = perm_string::literal("left"); -const static perm_string right_attr = perm_string::literal("right"); +static ActiveScope*arc_scope = NULL; /* * When a scope boundary starts, call the push_scope function to push @@ -245,8 +242,9 @@ const VType* vtype; - prange_t* range; - std::list*range_list; + ExpRange*range; + std::list*range_list; + ExpRange::range_dir_t range_dir; ExpArithmetic::fun_t arithmetic_op; std::list*adding_terms; @@ -266,6 +264,8 @@ ReportStmt::severity_t severity; SubprogramHeader*subprogram; + + file_open_info_t*file_info; }; /* The keywords are all tokens. */ @@ -303,8 +303,6 @@ /* The rules may have types. */ -%type direction - %type adding_operator %type simple_expression_terms @@ -321,6 +319,7 @@ %type concurrent_statement component_instantiation_statement %type concurrent_conditional_signal_assignment %type concurrent_signal_assignment_statement concurrent_simple_signal_assignment +%type concurrent_assertion_statement %type for_generate_statement generate_statement if_generate_statement %type process_statement selected_signal_assignment %type architecture_statement_part generate_statement_body @@ -333,16 +332,18 @@ %type expression factor primary relation %type expression_logical expression_logical_and expression_logical_or %type expression_logical_xnor expression_logical_xor -%type name prefix selected_name +%type name prefix selected_name indexed_name %type shift_expression signal_declaration_assign_opt %type simple_expression simple_expression_2 term %type variable_declaration_assign_opt waveform_element interface_element_expression %type waveform waveform_elements -%type name_list expression_list +%type name_list expression_list argument_list argument_list_opt %type process_sensitivity_list process_sensitivity_list_opt %type selected_names use_clause +%type file_open_information file_open_information_opt + %type association_element %type association_list port_map_aspect port_map_aspect_opt %type generic_map_aspect generic_map_aspect_opt @@ -353,7 +354,7 @@ %type element_declaration element_declaration_list %type architecture_body_start package_declaration_start -%type package_body_start +%type package_body_start process_start %type identifier_opt identifier_colon_opt logical_name suffix instantiated_unit %type logical_name_list identifier_list @@ -368,6 +369,7 @@ %type range %type range_list index_constraint +%type direction %type case_statement_alternative %type case_statement_alternative_list @@ -376,7 +378,7 @@ %type if_statement_elsif_list if_statement_elsif_list_opt %type else_when_waveform selected_waveform -%type else_when_waveforms selected_waveform_list +%type else_when_waveforms else_when_waveforms_opt selected_waveform_list %type function_specification procedure_specification %type subprogram_specification subprogram_body_start @@ -411,6 +413,8 @@ delete[]$3; delete $8; pop_scope(); + assert(arc_scope); + arc_scope = NULL; if ($11) delete[]$11; } ; @@ -419,6 +423,8 @@ : K_architecture IDENTIFIER { $$ = $2; push_scope(); + assert(!arc_scope); + arc_scope = active_scope; } ; /* @@ -444,11 +450,18 @@ } ; +argument_list : '(' expression_list ')' { $$ = $2; }; + +argument_list_opt + : argument_list { $$ = $1; } + | { $$ = 0; } + ; + assertion_statement : K_assert expression report_statement { ReportStmt*report = dynamic_cast($3); assert(report); - AssertStmt*tmp = new AssertStmt($2, report->message().c_str(), report->severity()); + AssertStmt*tmp = new AssertStmt($2, report->message(), report->severity()); delete report; FILE_NAME(tmp,@2); $$ = tmp; @@ -525,7 +538,7 @@ { /* Save the signal declaration in the block_signals map. */ for (std::list::iterator cur = $2->begin() ; cur != $2->end() ; ++cur) { - Signal*sig = new Signal(*cur, $4, $5); + Signal*sig = new Signal(*cur, $4, $5 ? $5->clone() : 0); FILE_NAME(sig, @1); active_scope->bind_name(*cur, sig); } @@ -540,6 +553,8 @@ | subprogram_body + | subtype_declaration + | type_declaration | use_clause_lib @@ -748,33 +763,52 @@ /* unbounded_array_definition IEEE 1076-2008 P5.3.2.1 */ | K_array '(' index_subtype_definition_list ')' K_of subtype_indication - { std::list r; - r.push_back(new prange_t(NULL, NULL, true)); // NULL boundaries indicate unbounded array type - VTypeArray*tmp = new VTypeArray($6, &r); - $$ = tmp; + { std::list r; + // NULL boundaries indicate unbounded array type + ExpRange*tmp = new ExpRange(NULL, NULL, ExpRange::DOWNTO); + r.push_back(tmp); + FILE_NAME(tmp, @1); + VTypeArray*arr = new VTypeArray($6, &r); + $$ = arr; } | record_type_definition { $$ = $1; } ; +concurrent_assertion_statement + : assertion_statement + { + /* See more explanations at IEEE 1076-2008 P11.5 */ + std::list stmts; + stmts.push_back($1); + stmts.push_back(new WaitStmt(WaitStmt::FINAL, NULL)); + push_scope(); + ProcessStatement*tmp = new ProcessStatement(empty_perm_string, *active_scope, + NULL, &stmts); + pop_scope(); + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + /* The when...else..when...else syntax is not a general expression in VHDL but a specific sort of assignment statement model. We create Expression objects for it, but the parser will only recognize it it in specific situations. */ concurrent_conditional_signal_assignment /* IEEE 1076-2008 P11.6 */ - : name LEQ waveform K_when expression else_when_waveforms ';' - { ExpConditional*tmp = new ExpConditional($5, $3, $6); - FILE_NAME(tmp, @3); - delete $3; - delete $6; - - ExpName*name = dynamic_cast ($1); - assert(name); - SignalAssignment*tmpa = new SignalAssignment(name, tmp); - FILE_NAME(tmpa, @1); + : name LEQ waveform K_when expression else_when_waveforms_opt ';' + { std::list*options; + options = $6 ? $6 : new std::list; + options->push_front(new ExpConditional::case_t($5, $3)); + + ExpName*name = dynamic_cast($1); + assert(name); + CondSignalAssignment*tmp = new CondSignalAssignment(name, *options); - $$ = tmpa; + FILE_NAME(tmp, @1); + delete options; + $$ = tmp; } /* Error recovery rules. */ @@ -818,6 +852,12 @@ } ; +else_when_waveforms_opt + : else_when_waveforms { $$ = $1; } + | { $$ = 0; } + ; + + else_when_waveform : K_else waveform K_when expression { ExpConditional::case_t*tmp = new ExpConditional::case_t($4, $2); @@ -867,6 +907,7 @@ concurrent_statement : component_instantiation_statement | concurrent_signal_assignment_statement + | concurrent_assertion_statement | generate_statement | process_statement ; @@ -979,8 +1020,10 @@ | design_unit ; - /* Indicate the direction as a flag, with "downto" being TRUE. */ -direction : K_to { $$ = false; } | K_downto { $$ = true; } ; +direction + : K_to { $$ = ExpRange::TO; } + | K_downto { $$ = ExpRange::DOWNTO; } + ; element_association : choices ARROW expression @@ -1116,6 +1159,8 @@ expression : expression_logical { $$ = $1; } + | range + { $$ = $1; } ; /* @@ -1235,6 +1280,68 @@ } ; +file_declaration + : K_file identifier_list ':' IDENTIFIER file_open_information_opt ';' + { + if (strcasecmp($4, "TEXT")) + sorrymsg(@1, "file declaration currently handles only TEXT type.\n"); + + for (std::list::iterator cur = $2->begin() + ; cur != $2->end() ; ++cur) { + Variable*var = new Variable(*cur, &primitive_INTEGER); + FILE_NAME(var, @1); + active_scope->bind_name(*cur, var); + + // there was a file name specified, so it needs an implicit call + // to open it at the beginning of simulation and close it at the end + if($5) { + std::list params; + + // add file_open() call in 'initial' block + params.push_back(new ExpScopedName(active_scope->peek_name(), new ExpName(*cur))); + params.push_back($5->filename()->clone()); + params.push_back($5->kind()->clone()); + ProcedureCall*fopen_call = new ProcedureCall( + perm_string::literal("file_open"), ¶ms); + arc_scope->add_initializer(fopen_call); + + // add file_close() call in 'final' block + params.clear(); + params.push_back(new ExpScopedName(active_scope->peek_name(), new ExpName(*cur))); + ProcedureCall*fclose_call = new ProcedureCall( + perm_string::literal("file_close"), ¶ms); + arc_scope->add_finalizer(fclose_call); + + delete $5; + } + } + + delete $2; + } + | K_file error ';' + { errormsg(@2, "Syntax error in file declaration.\n"); + yyerrok; + } + ; + +file_open_information + : K_open IDENTIFIER K_is STRING_LITERAL + { + ExpName*mode = new ExpName(lex_strings.make($2)); + delete[]$2; + FILE_NAME(mode, @1); + $$ = new file_open_info_t(new ExpString($4), mode); + } + | K_is STRING_LITERAL + { + $$ = new file_open_info_t(new ExpString($2)); + } + +file_open_information_opt + : file_open_information { $$ = $1; } + | { $$ = 0; } + ; + for_generate_statement : IDENTIFIER ':' K_for IDENTIFIER K_in range K_generate generate_statement_body @@ -1437,7 +1544,7 @@ | '(' error ')' { errormsg(@2, "Errors in the index constraint.\n"); yyerrok; - $$ = new list; + $$ = new list; } ; @@ -1553,14 +1660,8 @@ if($1) delete[]$1; if($8) delete[]$8; - ExpLogical* cond = dynamic_cast($3); - if(!cond) { - errormsg(@3, "Iteration condition is not a correct logical expression.\n"); - } - WhileLoopStatement* tmp = new WhileLoopStatement(loop_name, cond, $5); + WhileLoopStatement* tmp = new WhileLoopStatement(loop_name, $3, $5); FILE_NAME(tmp, @1); - - sorrymsg(@1, "Loop statements are not supported.\n"); $$ = tmp; } @@ -1597,7 +1698,6 @@ BasicLoopStatement* tmp = new BasicLoopStatement(loop_name, $3); FILE_NAME(tmp, @2); - sorrymsg(@1, "Loop statements are not supported.\n"); $$ = tmp; }; @@ -1611,7 +1711,17 @@ name /* IEEE 1076-2008 P8.1 */ : IDENTIFIER /* simple_name (IEEE 1076-2008 P8.2) */ - { Expression*tmp = new ExpName(lex_strings.make($1)); + { Expression*tmp; + /* Check if the IDENTIFIER is one of CHARACTER enums (LF, CR, etc.) */ + tmp = parse_char_enums($1); + if(!tmp) { + perm_string name = lex_strings.make($1); + /* There are functions that have the same name types, e.g. integer */ + if(!active_scope->find_subprogram(name).empty() && !parse_type_by_name(name)) + tmp = new ExpFunc(name); + else + tmp = new ExpName(name); + } FILE_NAME(tmp, @1); delete[]$1; $$ = tmp; @@ -1620,37 +1730,39 @@ | selected_name { $$ = $1; } + | indexed_name + { $$ = $1; } + + | selected_name '(' expression_list ')' + { + ExpName*name = dynamic_cast($1); + assert(name); + name->add_index($3); + delete $3; // contents of the list is moved to the selected_name + } + ; + +indexed_name /* Note that this rule can match array element selects and various function calls. The only way we can tell the difference is from left context, namely whether the name is a type name or function name. If none of the above, treat it as a array element select. */ - | IDENTIFIER '(' expression_list ')' - { perm_string name = lex_strings.make($1); - delete[]$1; - if (active_scope->is_vector_name(name) || is_subprogram_param(name)) { - ExpName*tmp = new ExpName(name, $3); - $$ = tmp; - } else { - ExpFunc*tmp = new ExpFunc(name, $3); - $$ = tmp; - } - FILE_NAME($$, @1); - } - | IDENTIFIER '(' range ')' - { ExpName*tmp = new ExpName(lex_strings.make($1), $3->msb(), $3->lsb()); - FILE_NAME(tmp, @1); - delete[]$1; - $$ = tmp; - } - | selected_name '(' range ')' - { ExpName*tmp = dynamic_cast ($1); - tmp->set_range($3->msb(), $3->lsb()); - $$ = tmp; + : IDENTIFIER '(' expression_list ')' + { Expression*tmp; + perm_string name = lex_strings.make($1); + delete[]$1; + if (active_scope->is_vector_name(name) || is_subprogram_param(name)) + tmp = new ExpName(name, $3); + else + tmp = new ExpFunc(name, $3); + FILE_NAME(tmp, @1); + $$ = tmp; } - | selected_name '(' expression ')' - { ExpName*tmp = dynamic_cast ($1); - tmp->set_range($3, NULL); - $$ = tmp; + | indexed_name '(' expression_list ')' + { ExpName*name = dynamic_cast($1); + assert(name); + name->add_index($3); + $$ = $1; } ; @@ -1821,11 +1933,19 @@ primary : name { $$ = $1; } - | name '\'' IDENTIFIER - { perm_string name = lex_strings.make($3); - ExpName*base = dynamic_cast ($1); - ExpAttribute*tmp = new ExpAttribute(base, name); - FILE_NAME(tmp, @3); + | name '\'' IDENTIFIER argument_list_opt + { ExpAttribute*tmp = NULL; + perm_string attr = lex_strings.make($3); + ExpName*base = dynamic_cast($1); + const VType*type = parse_type_by_name(base->peek_name()); + + if(type) { + tmp = new ExpTypeAttribute(type, attr, $4); + } else { + tmp = new ExpObjAttribute(base, attr, $4); + } + + FILE_NAME(tmp, @1); delete[]$3; $$ = tmp; } @@ -1842,8 +1962,8 @@ } | REAL_LITERAL { ExpReal*tmp = new ExpReal($1); - FILE_NAME(tmp, @1); - $$ = tmp; + FILE_NAME(tmp, @1); + $$ = tmp; } | STRING_LITERAL { ExpString*tmp = new ExpString($1); @@ -1916,20 +2036,23 @@ : IDENTIFIER ';' { ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1)); + FILE_NAME(tmp, @1); delete[] $1; $$ = tmp; } | IDENTIFIER '(' association_list ')' ';' { ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1), $3); + FILE_NAME(tmp, @1); delete[] $1; $$ = tmp; } - | IDENTIFIER '(' expression_list ')' ';' + | IDENTIFIER argument_list ';' { - ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1), $3); + ProcedureCall* tmp = new ProcedureCall(lex_strings.make($1), $2); + FILE_NAME(tmp, @1); delete[] $1; - delete $3; // parameters are copied in this variant + delete $2; // parameters are copied in this variant $$ = tmp; } | IDENTIFIER '(' error ')' ';' @@ -1961,6 +2084,7 @@ process_declarative_item : variable_declaration + | file_declaration ; process_declarative_part @@ -1973,33 +2097,40 @@ | ; -process_statement +process_start : identifier_colon_opt K_postponed_opt K_process - process_sensitivity_list_opt K_is_opt + { push_scope(); + $$ = $1; + } + ; + +process_statement + : process_start process_sensitivity_list_opt K_is_opt process_declarative_part_opt K_begin sequence_of_statements K_end K_postponed_opt K_process identifier_opt ';' - { perm_string iname = $1? lex_strings.make($1) : perm_string(); + { perm_string iname = $1? lex_strings.make($1) : empty_perm_string; if ($1) delete[]$1; - if ($12) { + if ($10) { if (iname.nil()) { - errormsg(@12, "Process end name %s for un-named processes.\n", $12); - } else if (iname != $12) { - errormsg(@12, "Process name %s does not match opening name %s.\n", - $12, $1); + errormsg(@10, "Process end name %s for un-named processes.\n", $10); + } else if (iname != $10) { + errormsg(@10, "Process name %s does not match opening name %s.\n", + $10, $1); } - delete[]$12; + delete[]$10; } - ProcessStatement*tmp = new ProcessStatement(iname, $4, $8); + ProcessStatement*tmp = new ProcessStatement(iname, *active_scope, $2, $6); + arc_scope->bind_scope(tmp->peek_name(), tmp); + pop_scope(); FILE_NAME(tmp, @3); - delete $4; - delete $8; + delete $2; + delete $6; $$ = tmp; } - | identifier_colon_opt K_postponed_opt K_process - process_sensitivity_list_opt K_is_opt + | process_start process_sensitivity_list_opt K_is_opt process_declarative_part_opt K_begin error K_end K_postponed_opt K_process identifier_opt ';' @@ -2040,18 +2171,17 @@ range : simple_expression direction simple_expression - { prange_t* tmp = new prange_t($1, $3, $2); + { ExpRange* tmp = new ExpRange($1, $3, $2); + FILE_NAME(tmp, @1); $$ = tmp; } | name '\'' K_range { - prange_t*tmp = NULL; + ExpRange*tmp = NULL; ExpName*name = NULL; if((name = dynamic_cast($1))) { - ExpAttribute*left = new ExpAttribute(name, left_attr); - ExpAttribute*right = new ExpAttribute(name, right_attr); - tmp = new prange_t(left, right, true); - tmp->set_auto_dir(); + tmp = new ExpRange(name, false); + FILE_NAME(tmp, @1); } else { errormsg(@1, "'range attribute can be used with named expressions only"); } @@ -2059,13 +2189,11 @@ } | name '\'' K_reverse_range { - prange_t*tmp = NULL; + ExpRange*tmp = NULL; ExpName*name = NULL; if((name = dynamic_cast($1))) { - ExpAttribute*left = new ExpAttribute(name, left_attr); - ExpAttribute*right = new ExpAttribute(name, right_attr); - tmp = new prange_t(left, right, false); - tmp->set_auto_dir(); + tmp = new ExpRange(name, true); + FILE_NAME(tmp, @1); } else { errormsg(@1, "'reverse_range attribute can be used with named expressions only"); } @@ -2075,12 +2203,12 @@ range_list : range - { list*tmp = new list; + { list*tmp = new list; tmp->push_back($1); $$ = tmp; } | range_list ',' range - { list*tmp = $1; + { list*tmp = $1; tmp->push_back($3); $$ = tmp; } @@ -2129,10 +2257,9 @@ ; report_statement - : K_report STRING_LITERAL severity_opt ';' + : K_report expression severity_opt ';' { ReportStmt*tmp = new ReportStmt($2, $3); FILE_NAME(tmp,@2); - delete[]$2; $$ = tmp; } @@ -2357,11 +2484,6 @@ } ; -sign - : '+' - | '-' - ; - signal_declaration_assign_opt : VASSIGN expression { $$ = $2; } | { $$ = 0; } @@ -2387,10 +2509,10 @@ * list fixes up the associations. */ simple_expression - : sign simple_expression_2 - { sorrymsg(@1, "Unary expression +- not supported.\n"); - $$ = $2; - } + : '-' simple_expression_2 + { $$ = new ExpUMinus($2); } + | '+' simple_expression_2 + { $$ = $2; } | simple_expression_2 { $$ = $1; } ; @@ -2410,6 +2532,7 @@ tmp = new ExpArithmetic(item.op, tmp, item.term); } delete lst; + FILE_NAME(tmp, @1); $$ = tmp; } ; @@ -2470,12 +2593,10 @@ K_begin subprogram_statement_part K_end subprogram_kind_opt identifier_opt ';' { SubprogramHeader*prog = $1; - SubprogramHeader*tmp = active_scope->recall_subprogram(prog->name()); - if (tmp && prog->compare_specification(tmp)) { + SubprogramHeader*tmp = active_scope->recall_subprogram(prog); + if (tmp) { delete prog; prog = tmp; - } else if (tmp) { - errormsg(@1, "Subprogram specification for %s doesn't match specification in package header.\n", prog->name().str()); } SubprogramBody*body = new SubprogramBody(); @@ -2483,7 +2604,7 @@ body->set_statements($4); prog->set_body(body); - active_scope->bind_name(prog->name(), prog); + active_scope->bind_subprogram(prog->name(), prog); active_sub = NULL; } @@ -2501,11 +2622,12 @@ subprogram_declaration : subprogram_specification ';' - { if ($1) active_scope->bind_name($1->name(), $1); } + { if ($1) active_scope->bind_subprogram($1->name(), $1); } ; subprogram_declarative_item /* IEEE 1079-2008 P4.3 */ : variable_declaration + | file_declaration ; subprogram_declarative_item_list @@ -2541,12 +2663,21 @@ subtype_declaration : K_subtype IDENTIFIER K_is subtype_indication ';' { perm_string name = lex_strings.make($2); - delete[] $2; if ($4 == 0) { errormsg(@1, "Failed to declare type name %s.\n", name.str()); } else { - active_scope->bind_name(name, $4); + VTypeDef*tmp; + map::iterator cur = active_scope->incomplete_types.find(name); + if (cur == active_scope->incomplete_types.end()) { + tmp = new VSubTypeDef(name, $4); + active_scope->bind_name(name, tmp); + } else { + tmp = cur->second; + tmp->set_definition($4); + active_scope->incomplete_types.erase(cur); + } } + delete[]$2; } ; @@ -2631,9 +2762,6 @@ tmp->set_definition($4); active_scope->incomplete_types.erase(cur); } - if(const VTypeEnum*enum_type = dynamic_cast($4)) { - active_scope->use_enum(enum_type); - } } delete[]$2; } @@ -2658,6 +2786,7 @@ type_definition : '(' enumeration_literal_list ')' { VTypeEnum*tmp = new VTypeEnum($2); + active_scope->use_enum(tmp); delete $2; $$ = tmp; } @@ -2755,6 +2884,11 @@ FILE_NAME(tmp, @1); $$ = tmp; } + | K_wait ';' + { WaitStmt*tmp = new WaitStmt(WaitStmt::FINAL, NULL); + FILE_NAME(tmp, @1); + $$ = tmp; + } ; waveform @@ -2780,6 +2914,10 @@ waveform_element : expression { $$ = $1; } + | expression K_after expression + { ExpDelay*tmp = new ExpDelay($1, $3); + FILE_NAME(tmp, @1); + $$ = tmp; } | K_null { $$ = 0; } ; diff -Nru iverilog-10.3/vhdlpp/scope.cc iverilog-11.0/vhdlpp/scope.cc --- iverilog-10.3/vhdlpp/scope.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/scope.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -24,53 +24,30 @@ # include "entity.h" # include "std_funcs.h" # include "std_types.h" +# include "compiler.h" # include # include # include +# include +# include # include +# include using namespace std; -/* - * If the merge_flag is passed in, then the new scope is a merge of - * the parent scopes. This brings in all of the parent scopes into the - * "old_*_" variables. This clears up the "new_*_" variables to - * accumulate new scope values. - */ +static int scope_counter = 0; + ScopeBase::ScopeBase(const ActiveScope&ref) -: use_constants_(ref.use_constants_), cur_constants_(ref.cur_constants_) +: old_signals_(ref.old_signals_), new_signals_(ref.new_signals_), + old_variables_(ref.old_variables_), new_variables_(ref.new_variables_), + old_components_(ref.old_components_), new_components_(ref.new_components_), + use_types_(ref.use_types_), cur_types_(ref.cur_types_), + use_constants_(ref.use_constants_), cur_constants_(ref.cur_constants_), + use_subprograms_(ref.use_subprograms_), cur_subprograms_(ref.cur_subprograms_), + scopes_(ref.scopes_), use_enums_(ref.use_enums_), + initializers_(ref.initializers_), finalizers_(ref.finalizers_), + package_header_(ref.package_header_), name_(ref.name_) { - merge(ref.old_signals_.begin(), ref.old_signals_.end(), - ref.new_signals_.begin(), ref.new_signals_.end(), - insert_iterator >( - old_signals_, old_signals_.end()) - ); - merge(ref.old_variables_.begin(), ref.old_variables_.end(), - ref.new_variables_.begin(), ref.new_variables_.end(), - insert_iterator >( - old_variables_, old_variables_.end()) - ); - merge(ref.old_components_.begin(), ref.old_components_.end(), - ref.new_components_.begin(), ref.new_components_.end(), - insert_iterator >( - old_components_, old_components_.end()) - ); - use_types_ = ref.use_types_; - cur_types_ = ref.cur_types_; - - use_subprograms_ = ref.use_subprograms_; - cur_subprograms_ = ref.cur_subprograms_; - - use_enums_ = ref.use_enums_; - - // This constructor is invoked when the parser is finished with - // an active scope and is making the actual scope. At this point - // we know that "this" is the parent scope for the subprograms, - // so set it now. - for (map::iterator cur = cur_subprograms_.begin() - ; cur != cur_subprograms_.end(); ++cur) { - cur->second->set_parent(this); - } } ScopeBase::~ScopeBase() @@ -87,10 +64,24 @@ * objects from the other scopes untouched. */ delete_all(new_signals_); + delete_all(new_variables_); delete_all(new_components_); delete_all(cur_types_); delete_all(cur_constants_); - delete_all(cur_subprograms_); + for (map::iterator cur = cur_subprograms_.begin() + ; cur != cur_subprograms_.end() ; ++cur) { + delete_all(cur->second); + } +} + +ScopeBase*ScopeBase::find_scope(perm_string name) const +{ + map::const_iterator it = scopes_.find(name); + + if(it != scopes_.end()) + return it->second; + + return NULL; } const VType*ScopeBase::find_type(perm_string by_name) @@ -99,11 +90,10 @@ if (cur == cur_types_.end()) { cur = use_types_.find(by_name); if (cur == use_types_.end()) - return 0; - else - return cur->second; - } else - return cur->second; + return NULL; // nothing found + } + + return cur->second; } bool ScopeBase::find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const @@ -115,19 +105,12 @@ if (cur == cur_constants_.end()) { cur = use_constants_.find(by_name); if (cur == use_constants_.end()) - return false; - else { - typ = cur->second->typ; - exp = cur->second->val; - return true; - } - } else { - typ = cur->second->typ; - exp = cur->second->val; - return true; + return false; // nothing found } - return false; + typ = cur->second->typ; + exp = cur->second->val; + return true; } Signal* ScopeBase::find_signal(perm_string by_name) const @@ -136,12 +119,10 @@ if (cur == new_signals_.end()) { cur = old_signals_.find(by_name); if (cur == old_signals_.end()) - return 0; - else - return cur->second; - } else { - return cur->second; + return NULL; // nothing found } + + return cur->second; } Variable* ScopeBase::find_variable(perm_string by_name) const @@ -150,12 +131,10 @@ if (cur == new_variables_.end()) { cur = old_variables_.find(by_name); if (cur == old_variables_.end()) - return 0; - else - return cur->second; - } else { - return cur->second; + return 0; // nothing found } + + return cur->second; } const InterfacePort* ScopeBase::find_param(perm_string) const @@ -165,32 +144,42 @@ const InterfacePort* ScopeBase::find_param_all(perm_string by_name) const { - for(map::const_iterator it = use_subprograms_.begin(); - it != use_subprograms_.end(); ++it) { - if(const InterfacePort*port = it->second->find_param(by_name)) - return port; + for(map::const_iterator cur = use_subprograms_.begin(); + cur != use_subprograms_.end(); ++cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + if(const InterfacePort*port = (*it)->find_param(by_name)) + return port; + } } - for(map::const_iterator it = cur_subprograms_.begin(); - it != cur_subprograms_.end(); ++it) { - if(const InterfacePort*port = it->second->find_param(by_name)) - return port; + for(map::const_iterator cur = cur_subprograms_.begin(); + cur != cur_subprograms_.end(); ++cur) { + const SubHeaderList& subp_list = cur->second; + + for(SubHeaderList::const_iterator it = subp_list.begin(); + it != subp_list.end(); ++it) { + if(const InterfacePort*port = (*it)->find_param(by_name)) + return port; + } } return NULL; } -SubprogramHeader* ScopeBase::find_subprogram(perm_string name) const +SubHeaderList ScopeBase::find_subprogram(perm_string name) const { - map::const_iterator cur; + map::const_iterator cur; cur = cur_subprograms_.find(name); if (cur != cur_subprograms_.end()) - return cur->second; + return cur->second; cur = use_subprograms_.find(name); if (cur != use_subprograms_.end()) - return cur->second; + return cur->second; return find_std_subprogram(name); } @@ -225,14 +214,13 @@ old_components_[cur->first] = cur->second; } - for (map::const_iterator cur = that->cur_subprograms_.begin() + for (map::const_iterator cur = that->cur_subprograms_.begin() ; cur != that->cur_subprograms_.end() ; ++ cur) { - if (cur->second == 0) + if (cur->second.empty()) continue; use_subprograms_[cur->first] = cur->second; } - for (map::const_iterator cur = that->cur_types_.begin() ; cur != that->cur_types_.end() ; ++ cur) { if (cur->second == 0) @@ -275,21 +263,89 @@ } } -void ActiveScope::set_package_header(Package*pkg) +SubprogramHeader*ScopeBase::match_subprogram(perm_string name, + const list*params) const +{ + int req_param_count = params ? params->size() : 0; + + // Find all subprograms with matching name + SubHeaderList l = find_std_subprogram(name); + map::const_iterator cur; + + cur = use_subprograms_.find(name); + if (cur != use_subprograms_.end()) + copy(cur->second.begin(), cur->second.end(), + front_insert_iterator(l)); + + cur = cur_subprograms_.find(name); + if(cur != cur_subprograms_.end()) + copy(cur->second.begin(), cur->second.end(), + front_insert_iterator(l)); + + // Find the matching one + for(SubHeaderList::iterator it = l.begin(); it != l.end(); ++it) { + SubprogramHeader*subp = *it; + + if(req_param_count != subp->param_count()) + continue; + + // Do not check the return type here, it might depend on the arguments + + if(params) { + list::const_iterator p = params->begin(); + bool ok = true; + + for(int i = 0; i < req_param_count; ++i) { + const VType*param_type = subp->peek_param_type(i); + + if(*p && param_type && !param_type->type_match(*p)) { + ok = false; + break; + } + + ++p; + } + + if(!ok) + continue; // check another function + } + + // Yay, we have a match! + return subp; + } + + return NULL; +} + +void ScopeBase::generate_name() { - assert(package_header_ == 0); - package_header_ = pkg; + char buf[64]; + + // Generate a name for the scope + snprintf(buf, sizeof(buf), "__scope_%d", scope_counter++); + name_ = gen_strings.make(buf); } -SubprogramHeader* ActiveScope::recall_subprogram(perm_string name) const +SubprogramHeader* ActiveScope::recall_subprogram(const SubprogramHeader*subp) const { - if (SubprogramHeader*tmp = find_subprogram(name)) - return tmp; + list arg_types; + SubprogramHeader*tmp; - if (package_header_) - return package_header_->find_subprogram(name); + for(int i = 0; i < subp->param_count(); ++i) + arg_types.push_back(subp->peek_param_type(i)); - return 0; + if ((tmp = match_subprogram(subp->name(), &arg_types))) { + assert(!tmp->body()); + return tmp; + } + + if (package_header_) { + tmp = package_header_->match_subprogram(subp->name(), &arg_types); + assert(!tmp || !tmp->body()); + return tmp; + } + + return NULL; } bool ActiveScope::is_vector_name(perm_string name) const @@ -310,15 +366,6 @@ return false; } -Scope::Scope(const ActiveScope&ref) -: ScopeBase(ref) -{ -} - -Scope::~Scope() -{ -} - ComponentBase* Scope::find_component(perm_string by_name) { map::const_iterator cur = new_components_.find(by_name); @@ -331,3 +378,37 @@ } else return cur->second; } + +ActiveScope::ActiveScope(const ActiveScope*par) +: ScopeBase(*par), context_entity_(par->context_entity_) +{ + generate_name(); + + // Move all the objects available in higher level scopes to use*/old* maps. + // This way we can store the new items in now empty cur*/new* maps. + merge(par->old_signals_.begin(), par->old_signals_.end(), + par->new_signals_.begin(), par->new_signals_.end(), + insert_iterator >( + old_signals_, old_signals_.end()) + ); + merge(par->old_variables_.begin(), par->old_variables_.end(), + par->new_variables_.begin(), par->new_variables_.end(), + insert_iterator >( + old_variables_, old_variables_.end()) + ); + merge(par->old_components_.begin(), par->old_components_.end(), + par->new_components_.begin(), par->new_components_.end(), + insert_iterator >( + old_components_, old_components_.end()) + ); + merge(par->use_types_.begin(), par->use_types_.end(), + par->cur_types_.begin(), par->cur_types_.end(), + insert_iterator >( + use_types_, use_types_.end()) + ); + merge(par->use_subprograms_.begin(), par->use_subprograms_.end(), + par->cur_subprograms_.begin(), par->cur_subprograms_.end(), + insert_iterator >( + use_subprograms_, use_subprograms_.end()) + ); +} diff -Nru iverilog-10.3/vhdlpp/scope.h iverilog-11.0/vhdlpp/scope.h --- iverilog-10.3/vhdlpp/scope.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/scope.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_scope_H #define IVL_scope_H /* - * Copyright (c) 2011-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -34,6 +34,9 @@ class Package; class SubprogramHeader; class VType; +class SequentialStmt; + +typedef list SubHeaderList; template struct delete_object{ @@ -48,31 +51,61 @@ class ScopeBase { public: - ScopeBase() { } + ScopeBase() : package_header_(0) { } explicit ScopeBase(const ActiveScope&ref); virtual ~ScopeBase() =0; + ScopeBase* find_scope(perm_string name) const; const VType* find_type(perm_string by_name); virtual bool find_constant(perm_string by_name, const VType*&typ, Expression*&exp) const; Signal* find_signal(perm_string by_name) const; - Variable* find_variable(perm_string by_name) const; + virtual Variable* find_variable(perm_string by_name) const; virtual const InterfacePort* find_param(perm_string by_name) const; const InterfacePort* find_param_all(perm_string by_name) const; - SubprogramHeader* find_subprogram(perm_string by_name) const; + SubHeaderList find_subprogram(perm_string by_name) const; // Checks if a string is one of possible enum values. If so, the enum // type is returned, otherwise NULL. const VTypeEnum* is_enum_name(perm_string name) const; + virtual bool is_subprogram() const { return false; } + // Moves signals, variables and components from another scope to // this one. After the transfer new_* maps are cleared in the source scope. enum transfer_type_t { SIGNALS = 1, VARIABLES = 2, COMPONENTS = 4, ALL = 0xffff }; void transfer_from(ScopeBase&ref, transfer_type_t what = ALL); inline void bind_subprogram(perm_string name, SubprogramHeader*obj) - { map::iterator it; + { map::iterator it; if((it = use_subprograms_.find(name)) != use_subprograms_.end() ) - use_subprograms_.erase(it); - cur_subprograms_[name] = obj; + it->second.remove(obj); + cur_subprograms_[name].push_back(obj); + } + + // Adds a statement to implicit initializers list + // (emitted in a 'initial block). + void add_initializer(SequentialStmt* s) + { + initializers_.push_back(s); + } + + // Adds a statement to implicit finalizers list + // (emitted in a 'final' block). + void add_finalizer(SequentialStmt* s) + { + finalizers_.push_back(s); + } + + void dump_scope(ostream&out) const; + + // Looks for a subprogram with specified name and parameter types. + SubprogramHeader*match_subprogram(perm_string name, + const list*params) const; + + perm_string peek_name() const { return name_; } + + void set_package_header(Package*pkg) { + assert(package_header_ == 0); + package_header_ = pkg; } protected: @@ -117,29 +150,44 @@ std::map use_constants_; //imported constants std::map cur_constants_; //current constants - std::map use_subprograms_; //imported - std::map cur_subprograms_; //current + std::map use_subprograms_; //imported + std::map cur_subprograms_; //current + + std::map scopes_; std::list use_enums_; + // List of statements that should be emitted in a 'initial' block + std::list initializers_; + + // List of statements that should be emitted in a 'final' block + std::list finalizers_; + void do_use_from(const ScopeBase*that); + + // If this is a package body, then there is a Package header + // already declared. + Package*package_header_; + + // Generates an unique name for the scope + void generate_name(); + +private: + perm_string name_; }; class Scope : public ScopeBase { public: - explicit Scope(const ActiveScope&ref); - ~Scope(); + explicit Scope(const ActiveScope&ref) : ScopeBase(ref) {} + virtual ~Scope() {} ComponentBase* find_component(perm_string by_name); - public: - void dump_scope(ostream&out) const; - protected: // Helper method for emitting signals in the scope. - int emit_signals(ostream&out, Entity*ent, Architecture*arc); - int emit_variables(ostream&out, Entity*ent, Architecture*arc); + int emit_signals(ostream&out, Entity*ent, ScopeBase*scope); + int emit_variables(ostream&out, Entity*ent, ScopeBase*scope); }; /* @@ -151,13 +199,11 @@ class ActiveScope : public ScopeBase { public: - ActiveScope() : package_header_(0), context_entity_(0) { } - ActiveScope(ActiveScope*par) : ScopeBase(*par), package_header_(0), context_entity_(0) { } + ActiveScope() : context_entity_(0) { } + explicit ActiveScope(const ActiveScope*par); ~ActiveScope() { } - void set_package_header(Package*); - // Pull items from "that" scope into "this" scope as is // defined by a "use" directive. The parser uses this method // to implement the "use ::*" directive. @@ -171,7 +217,7 @@ // Locate the subprogram by name. The subprogram body uses // this to locate the subprogram declaration. Note that the // subprogram may be in a package header. - SubprogramHeader* recall_subprogram(perm_string name) const; + SubprogramHeader* recall_subprogram(const SubprogramHeader*subp) const; /* All bind_name function check if the given name was present * in previous scopes. If it is found, it is erased (but the pointer @@ -207,6 +253,12 @@ cur_types_[name] = t; } + void bind_scope(perm_string name, ScopeBase*scope) + { + assert(scopes_.find(name) == scopes_.end()); + scopes_[name] = scope; + } + inline void use_enum(const VTypeEnum* t) { use_enums_.push_back(t); } @@ -220,13 +272,6 @@ cur_constants_[name] = new const_t(obj, val); } - inline void bind_name(perm_string name, SubprogramHeader*obj) - { map::iterator it; - if((it = use_subprograms_.find(name)) != use_subprograms_.end() ) - use_subprograms_.erase(it); - cur_subprograms_[name] = obj; - } - void bind(Entity*ent) { context_entity_ = ent; } @@ -240,10 +285,6 @@ std::map incomplete_types; private: - // If this is a package body, then there is a Package header - // already declared. - Package*package_header_; - Entity*context_entity_; }; diff -Nru iverilog-10.3/vhdlpp/sequential.cc iverilog-11.0/vhdlpp/sequential.cc --- iverilog-10.3/vhdlpp/sequential.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/sequential.cc 2020-09-26 22:44:25.000000000 +0000 @@ -185,7 +185,7 @@ for(std::list::const_iterator it = param_list->begin(); it != param_list->end(); ++it) { - param_list_->push_back(new named_expr_t(empty_perm_string, (*it)->clone())); + param_list_->push_back(new named_expr_t(empty_perm_string, *it)); } } @@ -199,6 +199,8 @@ param_list_->pop_front(); delete cur; } + + delete param_list_; } ReturnStmt::ReturnStmt(Expression*val) @@ -238,7 +240,7 @@ func(this); } -ForLoopStatement::ForLoopStatement(perm_string scope_name, perm_string it, prange_t* range, list* stmts) +ForLoopStatement::ForLoopStatement(perm_string scope_name, perm_string it, ExpRange* range, list* stmts) : LoopStatement(scope_name, stmts), it_(it), range_(range) { } @@ -259,7 +261,7 @@ delete rval_; } -WhileLoopStatement::WhileLoopStatement(perm_string lname, ExpLogical* cond, list* stmts) +WhileLoopStatement::WhileLoopStatement(perm_string lname, Expression* cond, list* stmts) : LoopStatement(lname, stmts), cond_(cond) { } @@ -278,33 +280,31 @@ { } -ReportStmt::ReportStmt(const char*msg, severity_t sev) +ReportStmt::ReportStmt(Expression*msg, severity_t sev) : msg_(msg), severity_(sev) { if(sev == ReportStmt::UNSPECIFIED) severity_ = ReportStmt::NOTE; } -AssertStmt::AssertStmt(Expression*condition, const char*msg, ReportStmt::severity_t sev) -: ReportStmt("", sev), cond_(condition) +AssertStmt::AssertStmt(Expression*condition, Expression*msg, ReportStmt::severity_t sev) +: ReportStmt(msg, sev), cond_(condition) { if(msg == NULL) - msg_ = default_msg_; - else - msg_ = std::string(msg); + msg_ = new ExpString(default_msg_); if(sev == ReportStmt::UNSPECIFIED) severity_ = ReportStmt::ERROR; } -const std::string AssertStmt::default_msg_ = std::string("Assertion violation."); +const char*AssertStmt::default_msg_ = "Assertion violation."; WaitForStmt::WaitForStmt(Expression*delay) : delay_(delay) { } -WaitStmt::WaitStmt(wait_type_t type, Expression*expr) -: type_(type), expr_(expr) +WaitStmt::WaitStmt(wait_type_t typ, Expression*expr) +: type_(typ), expr_(expr) { } diff -Nru iverilog-10.3/vhdlpp/sequential_debug.cc iverilog-11.0/vhdlpp/sequential_debug.cc --- iverilog-10.3/vhdlpp/sequential_debug.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/sequential_debug.cc 2020-09-26 22:44:25.000000000 +0000 @@ -169,16 +169,24 @@ void ReportStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "ReportStmt at file=" << get_fileline() << endl; - out << setw(indent+3) << "" << "severity: " << severity_ << endl; - out << setw(indent+3) << "" << "message: " << msg_ << endl; + dump_sev_msg(out, indent+3); +} + +void ReportStmt::dump_sev_msg(ostream&out, int indent) const +{ + out << setw(indent) << "" << "severity: " << severity_ << endl; + + if(msg_) { + out << setw(indent) << "" << "message: "; + msg_->dump(out, indent); + } } void AssertStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "AssertStmt at file=" << get_fileline() << endl; out << setw(indent+3) << "" << "condition: "; - cond_->dump(out, indent+3); - ReportStmt::dump(out, indent+3); + dump_sev_msg(out, indent+3); } void WaitForStmt::dump(ostream&out, int indent) const @@ -191,6 +199,16 @@ void WaitStmt::dump(ostream&out, int indent) const { out << setw(indent) << "" << "WaitStmt at file=" << get_fileline() << endl; - out << setw(indent+3) << "" << "expression: "; - expr_->dump(out, indent+3); + out << setw(indent+3) << "type = "; + + switch(type_) { + case ON: out << "ON" << endl; break; + case UNTIL: out << "UNTIL" << endl; break; + case FINAL: out << "FINAL" << endl; break; + } + + if(type_ != FINAL) { + out << setw(indent+3) << "" << "expression: "; + expr_->dump(out, indent+3); + } } diff -Nru iverilog-10.3/vhdlpp/sequential_elaborate.cc iverilog-11.0/vhdlpp/sequential_elaborate.cc --- iverilog-10.3/vhdlpp/sequential_elaborate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/sequential_elaborate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -22,6 +22,7 @@ # include "scope.h" # include "library.h" # include "subprogram.h" +# include "std_types.h" int SequentialStmt::elaborate(Entity*, ScopeBase*) { @@ -98,7 +99,7 @@ { int errors = 0; - errors += cond_->elaborate_expr(ent, scope, 0); + errors += cond_->elaborate_expr(ent, scope, &type_BOOLEAN); for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) { @@ -122,7 +123,7 @@ { int errors = 0; - errors += cond_->elaborate_expr(ent, scope, 0); + errors += cond_->elaborate_expr(ent, scope, &type_BOOLEAN); for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) { @@ -132,6 +133,22 @@ return errors; } +int ReturnStmt::elaborate(Entity*ent, ScopeBase*scope) +{ + const VType*ltype = NULL; + + // Try to determine the expression type by + // looking up the function return type. + const SubprogramBody*subp = dynamic_cast(scope); + if(subp) { + if(const SubprogramHeader*header = subp->header()) { + ltype = header->peek_return_type(); + } + } + + return val_->elaborate_expr(ent, scope, ltype); +} + int SignalSeqAssignment::elaborate(Entity*ent, ScopeBase*scope) { int errors = 0; @@ -161,25 +178,37 @@ { int errors = 0; - def_ = scope->find_subprogram(name_); + assert(!def_); // do not elaborate twice + + // Create a list of argument types to find a matching subprogram + list arg_types; + if(param_list_) { + for(list::iterator it = param_list_->begin(); + it != param_list_->end(); ++it) { + named_expr_t* e = *it; + arg_types.push_back(e->expr()->probe_type(ent, scope)); + } + } + + def_ = scope->match_subprogram(name_, &arg_types); if(!def_) - def_ = library_find_subprogram(name_); + def_ = library_match_subprogram(name_, &arg_types); - assert(def_); + if(!def_) { + cerr << get_fileline() << ": error: could not find procedure "; + emit_subprogram_sig(cerr, name_, arg_types); + cerr << endl; + return 1; + } // Elaborate arguments size_t idx = 0; if(param_list_) { for(list::iterator cur = param_list_->begin() ; cur != param_list_->end() ; ++cur) { - const VType*tmp = (*cur)->expr()->probe_type(ent, scope); - const VType*param_type = def_ ? def_->peek_param_type(idx) : NULL; - - if(!tmp && param_type) - tmp = param_type; - - errors += (*cur)->expr()->elaborate_expr(ent, scope, tmp); + errors += def_->elaborate_argument((*cur)->expr(), idx, ent, scope); + ++idx; } } @@ -208,32 +237,42 @@ return errors; } -int WhileLoopStatement::elaborate(Entity*, ScopeBase*) +int WhileLoopStatement::elaborate(Entity*ent, ScopeBase*scope) +{ + int errors = 0; + errors += elaborate_substatements(ent, scope); + errors += cond_->elaborate_expr(ent, scope, cond_->probe_type(ent, scope)); + return errors; +} + +int BasicLoopStatement::elaborate(Entity*ent, ScopeBase*scope) { - //TODO:check whether there is any wait statement in the statements (there should be) - return 0; + return elaborate_substatements(ent, scope); } -int BasicLoopStatement::elaborate(Entity*, ScopeBase*) +int ReportStmt::elaborate(Entity*ent, ScopeBase*scope) { - return 0; + return msg_->elaborate_expr(ent, scope, &primitive_STRING); } int AssertStmt::elaborate(Entity*ent, ScopeBase*scope) { - return cond_->elaborate_expr(ent, scope, 0); + int errors = 0; + errors += ReportStmt::elaborate(ent, scope); + errors += cond_->elaborate_expr(ent, scope, cond_->probe_type(ent, scope)); + return errors; } int WaitForStmt::elaborate(Entity*ent, ScopeBase*scope) { - return delay_->elaborate_expr(ent, scope, 0); + return delay_->elaborate_expr(ent, scope, &primitive_TIME); } int WaitStmt::elaborate(Entity*ent, ScopeBase*scope) { if(type_ == UNTIL) { struct fill_sens_list_t : public ExprVisitor { - fill_sens_list_t(set& sig_list) + explicit fill_sens_list_t(set& sig_list) : sig_list_(sig_list) {}; void operator() (Expression*s) { @@ -247,6 +286,8 @@ // Fill the sensitivity list expr_->visit(fill_sens_list); + } else if(type_ == FINAL) { + return 0; // nothing to be elaborated } return expr_->elaborate_expr(ent, scope, 0); diff -Nru iverilog-10.3/vhdlpp/sequential_emit.cc iverilog-11.0/vhdlpp/sequential_emit.cc --- iverilog-10.3/vhdlpp/sequential_emit.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/sequential_emit.cc 2020-09-26 22:44:25.000000000 +0000 @@ -26,9 +26,11 @@ # include "package.h" # include "compiler.h" # include "subprogram.h" +# include "std_types.h" # include # include # include +# include # include int SequentialStmt::emit(ostream&out, Entity*, ScopeBase*) @@ -82,7 +84,7 @@ { fd << "if "; cond_->write_to_stream(fd); - fd << " then " << endl; + fd << " then" << endl; for (list::iterator cur = if_.begin() ; cur != if_.end() ; ++cur) @@ -210,24 +212,28 @@ int ProcedureCall::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; + vectorargv; - std::vectorparams(param_list_->size()); - int i = 0; - for(std::list::iterator it = param_list_->begin(); - it != param_list_->end(); ++it) - params[i++] = (*it)->expr(); - - const Package*pkg = dynamic_cast (def_->get_parent()); - if (pkg != 0) - out << "\\" << pkg->name() << " ::"; - - errors += def_->emit_name(params, out, ent, scope); + if(!def_) { + cerr << get_fileline() << ": error: unknown procedure: " << name_ << endl; + return 1; + } - out << " ("; + // Convert the parameter list to vector if(param_list_) { - errors += def_->emit_args(params, out, ent, scope); + argv.reserve(param_list_->size()); + + for(std::list::iterator it = param_list_->begin(); + it != param_list_->end(); ++it) + argv.push_back((*it)->expr()); } + def_->emit_full_name(argv, out, ent, scope); + out << " ("; + + if(param_list_) + errors += def_->emit_args(argv, out, ent, scope); + out << ");" << endl; return errors; } @@ -352,16 +358,38 @@ } } +int WhileLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + int errors = 0; + + out << "while("; + errors += cond_->emit(out, ent, scope); + out << ") begin" << endl; + errors += emit_substatements(out, ent, scope); + out << "end" << endl; + + return errors; +} + +void WhileLoopStatement::write_to_stream(ostream&out) +{ + out << "while("; + cond_->write_to_stream(out); + out << ") loop" << endl; + write_to_stream_substatements(out); + out << "end loop;" << endl; +} + int ForLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) { int errors = 0; ivl_assert(*this, range_); int64_t start_val; - bool start_rc = range_->msb()->evaluate(ent, scope, start_val); + bool start_rc = range_->left()->evaluate(ent, scope, start_val); int64_t finish_val; - bool finish_rc = range_->lsb()->evaluate(ent, scope, finish_val); + bool finish_rc = range_->right()->evaluate(ent, scope, finish_val); perm_string scope_name = loop_name(); if (scope_name.nil()) { @@ -378,48 +406,31 @@ // determined during the run-time errors += emit_runtime_(out, ent, scope); } else { - bool dir = range_->is_downto(); + ExpRange::range_dir_t dir = range_->direction(); - if (!dir) { - int64_t tmp = start_val; - start_val = finish_val; - finish_val = tmp; - } - - if (dir && (start_val < finish_val)) { - if(range_->is_auto_dir()) { - dir = false; - } else { - out << "begin /* Degenerate loop at " << get_fileline() - << ": " << start_val - << " downto " << finish_val << " */ end" << endl - << "end" << endl; - return errors; - } - } + if(dir == ExpRange::AUTO) + dir = start_val < finish_val ? ExpRange::TO : ExpRange::DOWNTO; - else if (!dir && start_val > finish_val) { - if(range_->is_auto_dir()) { - dir = true; - } else { - out << "begin /* Degenerate loop at " << get_fileline() - << ": " << start_val - << " to " << finish_val << " */ end" << endl - << "end" << endl; - return errors; - } + if ((dir == ExpRange::DOWNTO && start_val < finish_val) || + (dir == ExpRange::TO && start_val > finish_val)) { + out << "begin /* Degenerate loop at " << get_fileline() + << ": " << start_val; + out << (dir == ExpRange::DOWNTO ? " downto " : " to "); + out << finish_val << " */ end" << endl + << "end" << endl; + return errors; } out << "for (\\" << it_ << " = " << start_val << " ; "; - if (dir) + if (dir == ExpRange::DOWNTO) out << "\\" << it_ << " >= " << finish_val; else out << "\\" << it_ << " <= " << finish_val; out << "; \\" << it_ << " = \\" << it_; - if (dir) + if (dir == ExpRange::DOWNTO) out << " - 1)"; else out << " + 1)"; @@ -438,9 +449,7 @@ void ForLoopStatement::write_to_stream(ostream&fd) { fd << "for " << it_ << " in "; - range_->expr_left()->write_to_stream(fd); - fd << " to "; - range_->expr_right()->write_to_stream(fd); + range_->write_to_stream(fd); fd << " loop" << endl; write_to_stream_substatements(fd); fd << "end loop;" << endl; @@ -451,41 +460,100 @@ int errors = 0; out << "for (\\" << it_ << " = "; - errors += range_->expr_left()->emit(out, ent, scope); + errors += range_->left()->emit(out, ent, scope); // Twisted way of determining the loop direction at runtime out << " ;\n("; - errors += range_->expr_left()->emit(out, ent, scope); + errors += range_->left()->emit(out, ent, scope); out << " < "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << " ? \\" << it_ << " <= "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << " : \\" << it_ << " >= "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << ");\n\\" << it_ << " = \\" << it_ << " + ("; - errors += range_->expr_left()->emit(out, ent, scope); + errors += range_->left()->emit(out, ent, scope); out << " < "; - errors += range_->expr_right()->emit(out, ent, scope); + errors += range_->right()->emit(out, ent, scope); out << " ? 1 : -1))"; return errors; } -int ReportStmt::emit(ostream&out, Entity*, ScopeBase*) +int BasicLoopStatement::emit(ostream&out, Entity*ent, ScopeBase*scope) { - out << "$display(\""; + int errors = 0; + + out << "forever begin" << endl; + errors += emit_substatements(out, ent, scope); + out << "end" << endl; + + return errors; +} + +void BasicLoopStatement::write_to_stream(std::ostream&fd) +{ + fd << "loop" << endl; + write_to_stream_substatements(fd); + fd << "end loop;" << endl; +} + +int ReportStmt::emit(ostream&out, Entity*ent, ScopeBase*scope) +{ + out << "$display(\"** "; switch(severity_) { - case NOTE: out << "** Note: "; break; - case WARNING: out << "** Warning: "; break; - case ERROR: out << "** Error: "; break; - case FAILURE: out << "** Failure: "; break; + case NOTE: out << "Note"; break; + case WARNING: out << "Warning"; break; + case ERROR: out << "Error"; break; + case FAILURE: out << "Failure"; break; case UNSPECIFIED: ivl_assert(*this, false); break; } - out << msg_; - out << " (" << get_fileline() << ")\");"; + out << ": \","; + + struct emitter : public ExprVisitor { + emitter(ostream&outp, Entity*enti, ScopeBase*scop) + : out_(outp), ent_(enti), scope_(scop), + level_lock_(numeric_limits::max()) {} + + void operator() (Expression*s) { + if(!dynamic_cast(s)) { + if(level() > level_lock_) + return; + + if(dynamic_cast(s)) { + level_lock_ = level(); + } else { + level_lock_ = numeric_limits::max(); + } + + const VType*type = s->probe_type(ent_, scope_); + + if(dynamic_cast(s) && type + && type->type_match(&primitive_STRING)) { + out_ << "$sformatf(\"%s\", ("; + s->emit(out_, ent_, scope_); + out_ << "))"; + } else { + s->emit(out_, ent_, scope_); + } + + out_ << ", "; + } + } + + private: + ostream&out_; + Entity*ent_; + ScopeBase*scope_; + int level_lock_; + } emit_visitor(out, ent, scope); + + msg_->visit(emit_visitor); + + out << "\" (" << get_fileline() << ")\");"; if(severity_ == FAILURE) out << "$finish();"; @@ -497,7 +565,8 @@ void ReportStmt::write_to_stream(std::ostream&fd) { - fd << "report \"" << msg_ << "\"" << std::endl; + fd << "report "; + msg_->write_to_stream(fd); fd << "severity "; switch(severity_) @@ -538,7 +607,7 @@ out << "#("; errors += delay_->emit(out, ent, scope); - out << ")"; + out << ");" << std::endl; return errors; } @@ -561,6 +630,7 @@ case UNTIL: if(!sens_list_.empty()) { out << "@("; + for(std::set::iterator it = sens_list_.begin(); it != sens_list_.end(); ++it) { if(it != sens_list_.begin()) @@ -569,11 +639,15 @@ (*it)->emit(out, ent, scope); } - out << ");"; + out << "); "; } out << "wait("; break; + + case FINAL: + out << "/* final wait */" << endl; + return 0; // no expression to be emitted } errors += expr_->emit(out, ent, scope); @@ -592,6 +666,10 @@ case UNTIL: fd << "wait until "; break; + + case FINAL: + fd << "wait"; + return; // no expression to be emitted } expr_->write_to_stream(fd); diff -Nru iverilog-10.3/vhdlpp/sequential.h iverilog-11.0/vhdlpp/sequential.h --- iverilog-10.3/vhdlpp/sequential.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/sequential.h 2020-09-26 22:44:25.000000000 +0000 @@ -131,10 +131,11 @@ class ReturnStmt : public SequentialStmt { public: - ReturnStmt(Expression*val); + explicit ReturnStmt(Expression*val); ~ReturnStmt(); public: + int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; @@ -201,7 +202,7 @@ class ProcedureCall : public SequentialStmt { public: - ProcedureCall(perm_string name); + explicit ProcedureCall(perm_string name); ProcedureCall(perm_string name, std::list* param_list); ProcedureCall(perm_string name, std::list* param_list); ~ProcedureCall(); @@ -235,20 +236,22 @@ class WhileLoopStatement : public LoopStatement { public: WhileLoopStatement(perm_string loop_name, - ExpLogical*, list*); + Expression*, list*); ~WhileLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope); + void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; private: - ExpLogical* cond_; + Expression* cond_; }; class ForLoopStatement : public LoopStatement { public: ForLoopStatement(perm_string loop_name, - perm_string index, prange_t*, list*); + perm_string index, ExpRange*, list*); ~ForLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); @@ -262,7 +265,7 @@ int emit_runtime_(ostream&out, Entity*ent, ScopeBase*scope); perm_string it_; - prange_t* range_; + ExpRange* range_; }; class BasicLoopStatement : public LoopStatement { @@ -271,6 +274,8 @@ ~BasicLoopStatement(); int elaborate(Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope); + void write_to_stream(std::ostream&fd); void dump(ostream&out, int indent) const; }; @@ -278,24 +283,28 @@ public: typedef enum { UNSPECIFIED, NOTE, WARNING, ERROR, FAILURE } severity_t; - ReportStmt(const char*message, severity_t severity = NOTE); + ReportStmt(Expression*message, severity_t severity); virtual ~ReportStmt() {} void dump(ostream&out, int indent) const; + int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); - inline const std::string& message() const { return msg_; } + inline Expression*message() const { return msg_; } inline severity_t severity() const { return severity_; } protected: - std::string msg_; + void dump_sev_msg(ostream&out, int indent) const; + + Expression*msg_; severity_t severity_; }; class AssertStmt : public ReportStmt { public: - AssertStmt(Expression*condition, const char*message, ReportStmt::severity_t severity = ReportStmt::ERROR); + AssertStmt(Expression*condition, Expression*message, + ReportStmt::severity_t severity = ReportStmt::ERROR); void dump(ostream&out, int indent) const; int elaborate(Entity*ent, ScopeBase*scope); @@ -306,12 +315,12 @@ Expression*cond_; // Message displayed when there is no report assigned. - static const std::string default_msg_; + static const char*default_msg_; }; class WaitForStmt : public SequentialStmt { public: - WaitForStmt(Expression*delay); + explicit WaitForStmt(Expression*delay); void dump(ostream&out, int indent) const; int elaborate(Entity*ent, ScopeBase*scope); @@ -324,14 +333,16 @@ class WaitStmt : public SequentialStmt { public: - typedef enum { ON, UNTIL } wait_type_t; - WaitStmt(wait_type_t type, Expression*expression); + typedef enum { ON, UNTIL, FINAL } wait_type_t; + WaitStmt(wait_type_t typ, Expression*expression); void dump(ostream&out, int indent) const; int elaborate(Entity*ent, ScopeBase*scope); int emit(ostream&out, Entity*entity, ScopeBase*scope); void write_to_stream(std::ostream&fd); + inline wait_type_t type() const { return type_; } + private: wait_type_t type_; Expression*expr_; diff -Nru iverilog-10.3/vhdlpp/std_funcs.cc iverilog-11.0/vhdlpp/std_funcs.cc --- iverilog-10.3/vhdlpp/std_funcs.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/std_funcs.cc 2020-09-26 22:44:25.000000000 +0000 @@ -22,19 +22,22 @@ #include "std_types.h" #include "scope.h" -static std::map std_subprograms; +static std::map std_subprograms; + +void register_std_subprogram(SubprogramHeader*header) +{ + std_subprograms[header->name()].push_back(header); +} // Special case: to_integer function -static class SubprogramToInteger : public SubprogramHeader { +class SubprogramToInteger : public SubprogramStdHeader { public: SubprogramToInteger() - : SubprogramHeader(perm_string::literal("to_integer"), NULL, &primitive_REAL) { - ports_ = new std::list(); + : SubprogramStdHeader(perm_string::literal("to_integer"), NULL, &primitive_REAL) { + ports_ = new list(); ports_->push_back(new InterfacePort(&primitive_INTEGER)); } - bool is_std() const { return true; } - int emit_name(const std::vector&argv, std::ostream&out, Entity*ent, ScopeBase*scope) const { bool signed_flag = false; @@ -56,142 +59,291 @@ out << (signed_flag ? "$signed" : "$unsigned"); return 0; } -}*fn_to_integer; +}; // Special case: size casting (e.g. conv_std_logic_vector() / resize()). -static class SubprogramSizeCast : public SubprogramHeader { +class SubprogramSizeCast : public SubprogramStdHeader { public: - SubprogramSizeCast(perm_string nam) - : SubprogramHeader(nam, NULL, &primitive_STDLOGIC_VECTOR) { - ports_ = new std::list(); - ports_->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); - ports_->push_back(new InterfacePort(&primitive_INTEGER)); + explicit SubprogramSizeCast(perm_string nam, const VType*base, const VType*target) + : SubprogramStdHeader(nam, NULL, target) { + ports_ = new list(); + ports_->push_back(new InterfacePort(base)); + ports_->push_back(new InterfacePort(&primitive_NATURAL)); } - bool is_std() const { return true; } + int emit_name(const std::vector&, + std::ostream&, Entity*, ScopeBase*) const { + return 0; + } - int emit_name(const std::vector&argv, + int emit_args(const std::vector&argv, std::ostream&out, Entity*ent, ScopeBase*scope) const { - int64_t use_size; - bool rc = argv[1]->evaluate(ent, scope, use_size); + int64_t new_size, old_size; + + const VType*type = argv[0]->probe_type(ent, scope); - if(!rc) { - cerr << get_fileline() << ": sorry: Could not evaluate the " + if(!type) { + cerr << get_fileline() << ": sorry: Could not determine " + << "the argument type. Size casting impossible." << endl; + return 1; + } + + old_size = type->get_width(scope); + + if(old_size <= 0) { + cerr << get_fileline() << ": sorry: Could not determine " + << "the argument size. Size casting impossible." << endl; + return 1; + } + + if(!argv[1]->evaluate(ent, scope, new_size)) { + cerr << get_fileline() << ": sorry: Could not evaluate the requested" << "expression size. Size casting impossible." << endl; return 1; } - out << use_size << "'"; - return 0; + + out << new_size << "'(" << old_size << "'("; + + if(const VTypeArray*arr = dynamic_cast(type)) + out << (arr->signed_vector() ? "$signed" : "$unsigned"); + + out << "("; + bool res = argv[0]->emit(out, ent, scope); + out << ")))"; + + return res; } +}; + +class SubprogramReadWrite : public SubprogramBuiltin { + public: + SubprogramReadWrite(perm_string nam, perm_string newnam, bool hex = false) + : SubprogramBuiltin(nam, newnam, NULL, NULL), hex_format_(hex) { + ports_ = new list(); + ports_->push_back(new InterfacePort(&primitive_STRING)); + ports_->push_back(new InterfacePort(NULL)); + } + + // Format types handled by $ivlh_read/write (see vpi/vhdl_textio.c) + enum format_t { FORMAT_STD, FORMAT_BOOL, FORMAT_TIME, FORMAT_HEX, FORMAT_STRING }; int emit_args(const std::vector&argv, std::ostream&out, Entity*ent, ScopeBase*scope) const { - return argv[0]->emit(out, ent, scope); - } -}*fn_conv_std_logic_vector, *fn_resize; + int errors = 0; -static SubprogramBuiltin*fn_std_logic_vector; -static SubprogramBuiltin*fn_to_unsigned; -static SubprogramBuiltin*fn_unsigned; -static SubprogramBuiltin*fn_integer; + for(int i = 0; i < 2; ++i) { + errors += argv[i]->emit(out, ent, scope); + out << ", "; + } -static SubprogramBuiltin*fn_rising_edge; -static SubprogramBuiltin*fn_falling_edge; + const VType*arg_type = argv[1]->probe_type(ent, scope); -static SubprogramBuiltin*fn_and_reduce; -static SubprogramBuiltin*fn_or_reduce; + while(const VTypeDef*tdef = dynamic_cast(arg_type)) + arg_type = tdef->peek_definition(); + + // Pick the right format + if(hex_format_) { + out << FORMAT_HEX; + } else if(arg_type) { + if(arg_type->type_match(&primitive_TIME)) { + out << FORMAT_TIME; + } else if(arg_type->type_match(&type_BOOLEAN)) { + out << FORMAT_BOOL; + } else if(arg_type->type_match(&primitive_CHARACTER)) { + out << FORMAT_STRING; + } else { + const VTypeArray*arr = dynamic_cast(arg_type); + + if(arr && arr->element_type() == &primitive_CHARACTER) + out << FORMAT_STRING; + else + out << FORMAT_STD; + } + } else { + out << FORMAT_STD; + } + + return errors; + } + + private: + bool hex_format_; +}; void preload_std_funcs(void) { + list*args; + + /* function now */ + SubprogramBuiltin*fn_now = new SubprogramBuiltin(perm_string::literal("now"), + perm_string::literal("$time"), NULL, NULL); + register_std_subprogram(fn_now); + /* numeric_std library * function unsigned */ - std::list*fn_unsigned_args = new std::list(); - fn_unsigned_args->push_back(new InterfacePort(&primitive_INTEGER)); - fn_unsigned = new SubprogramBuiltin(perm_string::literal("unsigned"), + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("unsigned"), + perm_string::literal("$unsigned"), + args, &primitive_UNSIGNED)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("unsigned"), perm_string::literal("$unsigned"), - fn_unsigned_args, &primitive_UNSIGNED); - std_subprograms[fn_unsigned->name()] = fn_unsigned; + args, &primitive_UNSIGNED)); /* function integer */ - std::list*fn_integer_args = new std::list(); - fn_integer_args->push_back(new InterfacePort(&primitive_INTEGER)); - fn_integer = new SubprogramBuiltin(perm_string::literal("integer"), + args = new list(); + args->push_back(new InterfacePort(&primitive_REAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("integer"), perm_string::literal("int'"), - fn_integer_args, &primitive_INTEGER); - std_subprograms[fn_integer->name()] = fn_integer; + args, &primitive_INTEGER)); /* function std_logic_vector Special case: The std_logic_vector function casts its argument to std_logic_vector. Internally, we don't have to do anything for that to work. */ - std::list*fn_std_logic_vector_args = new std::list(); - fn_std_logic_vector_args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); - fn_std_logic_vector = new SubprogramBuiltin(perm_string::literal("std_logic_vector"), + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("std_logic_vector"), + empty_perm_string, + args, &primitive_STDLOGIC_VECTOR)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("std_logic_vector"), empty_perm_string, - fn_std_logic_vector_args, &primitive_STDLOGIC_VECTOR); - std_subprograms[fn_std_logic_vector->name()] = fn_std_logic_vector; + args, &primitive_STDLOGIC_VECTOR)); + + /* numeric_std library + * function shift_left (arg: unsigned; count: natural) return unsigned; + * function shift_left (arg: signed; count: natural) return signed; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_left"), + perm_string::literal("$ivlh_shift_left"), + args, &primitive_UNSIGNED)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_left"), + perm_string::literal("$ivlh_shift_left"), + args, &primitive_SIGNED)); + + /* numeric_std library + * function shift_right (arg: unsigned; count: natural) return unsigned; + * function shift_right (arg: signed; count: natural) return signed; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_right"), + perm_string::literal("$ivlh_shift_right"), + args, &primitive_UNSIGNED)); + + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("shift_right"), + perm_string::literal("$ivlh_shift_right"), + args, &primitive_SIGNED)); /* function resize */ - fn_resize = new SubprogramSizeCast(perm_string::literal("resize")); - std_subprograms[fn_resize->name()] = fn_resize; + register_std_subprogram(new SubprogramSizeCast(perm_string::literal("resize"), + &primitive_UNSIGNED, &primitive_UNSIGNED)); + + register_std_subprogram(new SubprogramSizeCast(perm_string::literal("resize"), + &primitive_SIGNED, &primitive_SIGNED)); - /* function conv_std_logic_vector + /* std_logic_arith library + * function conv_std_logic_vector(arg: integer; size: integer) return std_logic_vector; */ - fn_conv_std_logic_vector = new SubprogramSizeCast(perm_string::literal("conv_std_logic_vector")); - std_subprograms[fn_conv_std_logic_vector->name()] = fn_conv_std_logic_vector; + register_std_subprogram(new SubprogramSizeCast( + perm_string::literal("conv_std_logic_vector"), + &primitive_INTEGER, &primitive_STDLOGIC_VECTOR)); /* numeric_bit library * function to_integer (arg: unsigned) return natural; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_UNSIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_integer"), + perm_string::literal("$unsigned"), + args, &primitive_NATURAL)); + + /* numeric_bit library * function to_integer (arg: signed) return integer; */ - fn_to_integer = new SubprogramToInteger(); - std_subprograms[fn_to_integer->name()] = fn_to_integer; + args = new list(); + args->push_back(new InterfacePort(&primitive_SIGNED)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_integer"), + perm_string::literal("$signed"), + args, &primitive_INTEGER)); + + /* std_logic_1164 library + * function to_bit (signal s : std_ulogic) return bit; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_bit"), + empty_perm_string, + args, &primitive_BIT)); + + /* std_logic_1164 library + * function to_bitvector (signal s : std_logic_vector) return bit_vector; + * function to_bitvector (signal s : std_ulogic_vector) return bit_vector; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_bitvector"), + empty_perm_string, + args, &primitive_BIT_VECTOR)); /* std_logic_1164 library * function rising_edge (signal s : std_ulogic) return boolean; */ - std::list*fn_rising_edge_args = new std::list(); - fn_rising_edge_args->push_back(new InterfacePort(&primitive_STDLOGIC)); - fn_rising_edge = new SubprogramBuiltin(perm_string::literal("rising_edge"), + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("rising_edge"), perm_string::literal("$ivlh_rising_edge"), - fn_rising_edge_args, &type_BOOLEAN); - std_subprograms[fn_rising_edge->name()] = fn_rising_edge; + args, &type_BOOLEAN)); /* std_logic_1164 library * function falling_edge (signal s : std_ulogic) return boolean; */ - std::list*fn_falling_edge_args = new std::list(); - fn_falling_edge_args->push_back(new InterfacePort(&primitive_STDLOGIC)); - fn_falling_edge = new SubprogramBuiltin(perm_string::literal("falling_edge"), + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("falling_edge"), perm_string::literal("$ivlh_falling_edge"), - fn_falling_edge_args, &type_BOOLEAN); - std_subprograms[fn_falling_edge->name()] = fn_falling_edge; + args, &type_BOOLEAN)); /* reduce_pack library * function or_reduce(arg : std_logic_vector) return std_logic; */ - std::list*fn_or_reduce_args = new std::list(); - fn_or_reduce_args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); - fn_or_reduce = new SubprogramBuiltin(perm_string::literal("or_reduce"), + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("or_reduce"), perm_string::literal("|"), - fn_or_reduce_args, &primitive_STDLOGIC); - std_subprograms[fn_or_reduce->name()] = fn_or_reduce; + args, &primitive_STDLOGIC)); /* reduce_pack library * function and_reduce(arg : std_logic_vector) return std_logic; */ - std::list*fn_and_reduce_args = new std::list(); - fn_and_reduce_args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); - fn_and_reduce = new SubprogramBuiltin(perm_string::literal("and_reduce"), + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("and_reduce"), perm_string::literal("&"), - fn_and_reduce_args, &primitive_STDLOGIC); - std_subprograms[fn_and_reduce->name()] = fn_and_reduce; + args, &primitive_STDLOGIC)); /* fixed_pkg library * function to_unsigned ( @@ -199,28 +351,131 @@ * constant size : natural) -- length of output * return unsigned; */ - std::list*fn_to_unsigned_args = new std::list(); - fn_to_unsigned_args->push_back(new InterfacePort(&primitive_REAL)); - fn_to_unsigned_args->push_back(new InterfacePort(&primitive_NATURAL)); - fn_to_unsigned = new SubprogramBuiltin(perm_string::literal("to_unsigned"), + args = new list(); + args->push_back(new InterfacePort(&primitive_REAL)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_unsigned"), perm_string::literal("$ivlh_to_unsigned"), - fn_to_unsigned_args, &primitive_UNSIGNED); - std_subprograms[fn_to_unsigned->name()] = fn_to_unsigned; + args, &primitive_UNSIGNED)); + /* numeric_std library + * function to_unsigned(arg, size : natural) return unsigned; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_NATURAL)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_unsigned"), + perm_string::literal("$ivlh_to_unsigned"), + args, &primitive_UNSIGNED)); + + /* numeric_std library + * function to_unsigned(arg : std_logic_vector, size : natural) return unsigned; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_STDLOGIC_VECTOR)); + args->push_back(new InterfacePort(&primitive_NATURAL)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("to_unsigned"), + perm_string::literal("$ivlh_to_unsigned"), + args, &primitive_UNSIGNED)); + + /* procedure file_open (file f: text; filename: in string, file_open_kind: in mode); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_IN)); + args->push_back(new InterfacePort(&type_FILE_OPEN_KIND, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("file_open"), + perm_string::literal("$ivlh_file_open"), + args, NULL)); + + /* procedure file_open (status: out file_open_status, file f: text; filename: in string, file_open_kind: in mode); + */ + args = new list(); + args->push_back(new InterfacePort(&type_FILE_OPEN_STATUS, PORT_OUT)); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_IN)); + args->push_back(new InterfacePort(&type_FILE_OPEN_KIND, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("file_open"), + perm_string::literal("$ivlh_file_open"), + args, NULL)); + + /* std.textio library + * procedure file_close (file f: text); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("file_close"), + perm_string::literal("$fclose"), + args, NULL)); + + /* std.textio library + * procedure read (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("read"), + perm_string::literal("$ivlh_read"))); + + /* std.textio library + * procedure write (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("write"), + perm_string::literal("$ivlh_write"))); + + /* std.textio library + * procedure hread (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("hread"), + perm_string::literal("$ivlh_read"), true)); + + /* std.textio library + * procedure hwrite (l: inout line; value: out bit/bit_vector/boolean/character/integer/real/string/time); + */ + register_std_subprogram(new SubprogramReadWrite(perm_string::literal("hwrite"), + perm_string::literal("$ivlh_write"), true)); + + /* std.textio library + * procedure readline (file f: text; l: inout line); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_OUT)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("readline"), + perm_string::literal("$ivlh_readline"), + args, NULL)); + + /* std.textio library + * procedure writeline (file f: text; l: inout line); + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + args->push_back(new InterfacePort(&primitive_STRING, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("writeline"), + perm_string::literal("$ivlh_writeline"), + args, NULL)); + + /* function endline (file f: text) return boolean; + */ + args = new list(); + args->push_back(new InterfacePort(&primitive_INTEGER, PORT_IN)); + register_std_subprogram(new SubprogramBuiltin(perm_string::literal("endfile"), + perm_string::literal("$feof"), + args, &type_BOOLEAN)); } void delete_std_funcs() { - for(std::map::iterator it = std_subprograms.begin(); - it != std_subprograms.end(); ++it) { - delete it->second; + for(std::map::iterator cur = std_subprograms.begin(); + cur != std_subprograms.end(); ++cur) { + for(SubHeaderList::const_iterator it = cur->second.begin(); + it != cur->second.end(); ++it) { + delete *it; + } } } -SubprogramHeader*find_std_subprogram(perm_string name) +SubHeaderList find_std_subprogram(perm_string name) { - map::const_iterator cur = std_subprograms.find(name); - if (cur != std_subprograms.end()) - return cur->second; + map::const_iterator cur = std_subprograms.find(name); + if(cur != std_subprograms.end()) + return cur->second; - return NULL; + return SubHeaderList(); } diff -Nru iverilog-10.3/vhdlpp/std_funcs.h iverilog-11.0/vhdlpp/std_funcs.h --- iverilog-10.3/vhdlpp/std_funcs.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/std_funcs.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_std_funcs_H #define IVL_std_funcs_H /* - * Copyright CERN 2015 + * Copyright CERN 2016 * @author Maciej Suminski (maciej.suminski@cern.ch) * * This source code is free software; you can redistribute it @@ -28,7 +28,10 @@ // Destroys subprogram headers for standard VHDL library functions. void delete_std_funcs(); +// Adds a subprogram to the standard library subprogram set +void register_std_subprogram(SubprogramHeader*header); + // Returns subprogram header for a requested function or NULL if it does not exist. -SubprogramHeader*find_std_subprogram(perm_string name); +SubHeaderList find_std_subprogram(perm_string name); #endif /* IVL_std_funcs_H */ diff -Nru iverilog-10.3/vhdlpp/std_types.cc iverilog-11.0/vhdlpp/std_types.cc --- iverilog-10.3/vhdlpp/std_types.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/std_types.cc 2020-09-26 22:44:25.000000000 +0000 @@ -21,20 +21,22 @@ #include "std_types.h" #include "scope.h" -static std::map std_types; +static map std_types; // this list contains enums used by typedefs in the std_types map -static std::list std_enums; +static list std_enums; const VTypePrimitive primitive_BIT(VTypePrimitive::BIT, true); const VTypePrimitive primitive_INTEGER(VTypePrimitive::INTEGER); const VTypePrimitive primitive_NATURAL(VTypePrimitive::NATURAL); const VTypePrimitive primitive_REAL(VTypePrimitive::REAL); const VTypePrimitive primitive_STDLOGIC(VTypePrimitive::STDLOGIC, true); -const VTypePrimitive primitive_CHARACTER(VTypePrimitive::CHARACTER); -const VTypePrimitive primitive_TIME(VTypePrimitive::TIME); +const VTypePrimitive primitive_TIME(VTypePrimitive::TIME, true); VTypeDef type_BOOLEAN(perm_string::literal("boolean")); +VTypeDef type_FILE_OPEN_KIND(perm_string::literal("file_open_kind")); +VTypeDef type_FILE_OPEN_STATUS(perm_string::literal("file_open_status")); +const VTypeArray primitive_CHARACTER(&primitive_BIT, 7, 0); const VTypeArray primitive_BIT_VECTOR(&primitive_BIT, vector (1)); const VTypeArray primitive_BOOL_VECTOR(&type_BOOLEAN, vector (1)); const VTypeArray primitive_STDLOGIC_VECTOR(&primitive_STDLOGIC, vector (1)); @@ -45,14 +47,35 @@ void generate_global_types(ActiveScope*res) { // boolean - std::list*enum_BOOLEAN_vals = new std::list; - enum_BOOLEAN_vals->push_back(perm_string::literal("false")); - enum_BOOLEAN_vals->push_back(perm_string::literal("true")); - VTypeEnum*enum_BOOLEAN = new VTypeEnum(enum_BOOLEAN_vals); + list enum_BOOLEAN_vals; + enum_BOOLEAN_vals.push_back(perm_string::literal("false")); + enum_BOOLEAN_vals.push_back(perm_string::literal("true")); + VTypeEnum*enum_BOOLEAN = new VTypeEnum(&enum_BOOLEAN_vals); type_BOOLEAN.set_definition(enum_BOOLEAN); std_types[type_BOOLEAN.peek_name()] = &type_BOOLEAN; std_enums.push_back(enum_BOOLEAN); + // file_open_kind + list enum_FILE_OPEN_KIND_vals; + enum_FILE_OPEN_KIND_vals.push_back(perm_string::literal("read_mode")); + enum_FILE_OPEN_KIND_vals.push_back(perm_string::literal("write_mode")); + enum_FILE_OPEN_KIND_vals.push_back(perm_string::literal("append_mode")); + VTypeEnum*enum_FILE_OPEN_KIND = new VTypeEnum(&enum_FILE_OPEN_KIND_vals); + type_FILE_OPEN_KIND.set_definition(enum_FILE_OPEN_KIND); + std_types[type_FILE_OPEN_KIND.peek_name()] = &type_FILE_OPEN_KIND; + std_enums.push_back(enum_FILE_OPEN_KIND); + + // file_open_status + list enum_FILE_OPEN_STATUS_vals; + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("open_ok")); + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("status_error")); + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("name_error")); + enum_FILE_OPEN_STATUS_vals.push_back(perm_string::literal("mode_error")); + VTypeEnum*enum_FILE_OPEN_STATUS = new VTypeEnum(&enum_FILE_OPEN_STATUS_vals); + type_FILE_OPEN_STATUS.set_definition(enum_FILE_OPEN_STATUS); + std_types[type_FILE_OPEN_STATUS.peek_name()] = &type_FILE_OPEN_STATUS; + std_enums.push_back(enum_FILE_OPEN_STATUS); + res->use_name(type_BOOLEAN.peek_name(), &type_BOOLEAN); res->use_name(perm_string::literal("bit"), &primitive_BIT); res->use_name(perm_string::literal("bit_vector"), &primitive_BIT_VECTOR); @@ -69,10 +92,12 @@ { typedef_context_t typedef_ctx; for(map::iterator cur = std_types.begin(); - cur != std_types.end() ; ++ cur) { + cur != std_types.end(); ++cur) { delete cur->second->peek_definition(); delete cur->second; } + + // std_enums are destroyed above } const VTypeEnum*find_std_enum_name(perm_string name) diff -Nru iverilog-10.3/vhdlpp/std_types.h iverilog-11.0/vhdlpp/std_types.h --- iverilog-10.3/vhdlpp/std_types.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/std_types.h 2020-09-26 22:44:25.000000000 +0000 @@ -23,23 +23,25 @@ class ActiveScope; void emit_std_types(ostream&out); -int emit_packages(void); void generate_global_types(ActiveScope*res); bool is_global_type(perm_string type_name); void delete_global_types(); const VTypeEnum*find_std_enum_name(perm_string name); -extern const VTypePrimitive primitive_BOOLEAN; extern const VTypePrimitive primitive_BIT; extern const VTypePrimitive primitive_INTEGER; extern const VTypePrimitive primitive_NATURAL; extern const VTypePrimitive primitive_REAL; extern const VTypePrimitive primitive_STDLOGIC; -extern const VTypePrimitive primitive_CHARACTER; extern const VTypePrimitive primitive_TIME; +extern const VTypePrimitive primitive_TEXT; +extern const VTypePrimitive primitive_LINE; extern VTypeDef type_BOOLEAN; +extern VTypeDef type_FILE_OPEN_KIND; +extern VTypeDef type_FILE_OPEN_STATUS; +extern const VTypeArray primitive_CHARACTER; extern const VTypeArray primitive_BIT_VECTOR; extern const VTypeArray primitive_BOOL_VECTOR; extern const VTypeArray primitive_STDLOGIC_VECTOR; diff -Nru iverilog-10.3/vhdlpp/subprogram.cc iverilog-11.0/vhdlpp/subprogram.cc --- iverilog-10.3/vhdlpp/subprogram.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/subprogram.cc 2020-09-26 22:44:25.000000000 +0000 @@ -53,6 +53,18 @@ statements_ = stmt; } +int SubprogramBody::elaborate() +{ + int errors = 0; + + for (list::const_iterator cur = statements_->begin() + ; cur != statements_->end() ; ++cur) { + errors += (*cur)->elaborate(0, this); + } + + return errors; +} + void SubprogramBody::write_to_stream(ostream&fd) const { for (map::const_iterator cur = new_variables_.begin() @@ -70,12 +82,13 @@ } else { fd << "--empty body" << endl; } - fd << "end function;" << endl; + + fd << "end function " << header_->name() << ";" << endl; } SubprogramHeader::SubprogramHeader(perm_string nam, list*ports, const VType*return_type) -: name_(nam), ports_(ports), return_type_(return_type), body_(NULL), parent_(NULL) +: name_(nam), ports_(ports), return_type_(return_type), body_(NULL), package_(NULL) { } @@ -138,7 +151,7 @@ return NULL; } -const VType*SubprogramHeader::peek_param_type(int idx) const +const InterfacePort*SubprogramHeader::peek_param(int idx) const { if(!ports_ || idx < 0 || (size_t)idx >= ports_->size()) return NULL; @@ -146,13 +159,35 @@ std::list::const_iterator p = ports_->begin(); std::advance(p, idx); - return (*p)->type; + return *p; +} + +const VType*SubprogramHeader::peek_param_type(int idx) const +{ + const InterfacePort*port = peek_param(idx); + + if(port) + return port->type; + + return NULL; } -void SubprogramHeader::set_parent(const ScopeBase*par) +const VType*SubprogramHeader::exact_return_type(const std::vector&argv, Entity*ent, ScopeBase*scope) { - ivl_assert(*this, !parent_); - parent_ = par; + const VTypeArray*orig_ret = dynamic_cast(return_type_); + + if(!orig_ret) + return return_type_; + + const VTypeArray*arg = dynamic_cast(argv[0]->fit_type(ent, scope, orig_ret)); + + if(!arg) + return return_type_; + + VTypeArray*ret = new VTypeArray(orig_ret->element_type(), arg->dimensions(), orig_ret->signed_vector()); + ret->set_parent_type(orig_ret); + + return ret; } bool SubprogramHeader::unbounded() const { @@ -178,6 +213,35 @@ bdy->header_ = this; } +int SubprogramHeader::elaborate_argument(Expression*expr, int idx, + Entity*ent, ScopeBase*scope) +{ + const VType*type = expr->probe_type(ent, scope); + const InterfacePort*param = peek_param(idx); + + if(!param) { + cerr << expr->get_fileline() + << ": error: Too many arguments when calling " + << name_ << "." << endl; + return 1; + } + + // Enable reg_flag for variables that might be modified in subprograms + if(param->mode == PORT_OUT || param->mode == PORT_INOUT) { + if(const ExpName*e = dynamic_cast(expr)) { + if(Signal*sig = scope->find_signal(e->peek_name())) + sig->count_ref_sequ(); + else if(Variable*var = scope->find_variable(e->peek_name())) + var->count_ref_sequ(); + } + } + + if(!type) + type = param->type; + + return expr->elaborate_expr(ent, scope, type); +} + SubprogramHeader*SubprogramHeader::make_instance(std::vector arguments, ScopeBase*scope) const { assert(arguments.size() == ports_->size()); @@ -211,7 +275,7 @@ } body_inst->set_statements(body_->statements_); - instance->set_parent(scope); + instance->set_package(package_); instance->set_body(body_inst); instance->fix_return_type(); } @@ -222,7 +286,7 @@ } struct check_return_type : public SeqStmtVisitor { - check_return_type(const SubprogramBody*subp) : subp_(subp), ret_type_(NULL) {} + explicit check_return_type(const SubprogramBody*subp) : subp_(subp), ret_type_(NULL) {} void operator() (SequentialStmt*s) { @@ -312,13 +376,3 @@ return_type_->write_to_stream(fd); } } - -SubprogramBuiltin::SubprogramBuiltin(perm_string vhdl_name, perm_string sv_name, - std::list*ports, const VType*return_type) - : SubprogramHeader(vhdl_name, ports, return_type), sv_name_(sv_name) -{ -} - -SubprogramBuiltin::~SubprogramBuiltin() -{ -} diff -Nru iverilog-10.3/vhdlpp/subprogram_emit.cc iverilog-11.0/vhdlpp/subprogram_emit.cc --- iverilog-10.3/vhdlpp/subprogram_emit.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/subprogram_emit.cc 2020-09-26 22:44:25.000000000 +0000 @@ -21,19 +21,32 @@ # include "subprogram.h" # include "sequential.h" # include "vtype.h" +# include "package.h" # include using namespace std; -int SubprogramBody::emit_package(ostream&fd) const +int SubprogramBody::emit_package(ostream&fd) { int errors = 0; for (map::const_iterator cur = new_variables_.begin() ; cur != new_variables_.end() ; ++cur) { - // Workaround to enable reg_flag for variables - cur->second->count_ref_sequ(); - errors += cur->second->emit(fd, NULL, NULL); + // Enable reg_flag for variables + cur->second->count_ref_sequ(); + errors += cur->second->emit(fd, NULL, this, false); + } + + // Emulate automatic functions (add explicit initial value assignments) + for (map::const_iterator cur = new_variables_.begin() + ; cur != new_variables_.end() ; ++cur) { + Variable*var = cur->second; + + if(const Expression*init = var->peek_init_expr()) { + fd << cur->first << " = "; + init->emit(fd, NULL, this); + fd << "; // automatic function emulation" << endl; + } } if (statements_) { @@ -53,13 +66,13 @@ int errors = 0; if (return_type_) { - fd << "function "; + fd << "function automatic "; return_type_->emit_def(fd, empty_perm_string); } else { - fd << "task"; + fd << "task automatic"; } - fd << " \\" << name_ << " ("; + fd << " \\" << name_ << " ("; for (list::const_iterator cur = ports_->begin() ; cur != ports_->end() ; ++cur) { @@ -97,6 +110,22 @@ return errors; } +int SubprogramHeader::emit_full_name(const std::vector&argv, + std::ostream&out, Entity*ent, ScopeBase*scope) const +{ + // If this function has an elaborated definition, and if + // that definition is in a package, then include the + // package name as a scope qualifier. This assures that + // the SV elaborator finds the correct VHDL elaborated + // definition. It should not be emitted only if we call another + // function from the same package. + const SubprogramBody*subp = dynamic_cast(scope); + if (package_ && (!subp || !subp->header() || subp->header()->get_package() != package_)) + out << "\\" << package_->name() << " ::"; + + return emit_name(argv, out, ent, scope); +} + int SubprogramHeader::emit_name(const std::vector&, std::ostream&out, Entity*, ScopeBase*) const { @@ -124,3 +153,23 @@ out << sv_name_; return 0; } + +void emit_subprogram_sig(ostream&out, perm_string name, + const list&arg_types) +{ + out << name << "("; + bool first = true; + for(list::const_iterator it = arg_types.begin(); + it != arg_types.end(); ++it) { + if(first) + first = false; + else + out << ", "; + + if(*it) + (*it)->write_to_stream(out); + else + out << ""; + } + out << ")"; +} diff -Nru iverilog-10.3/vhdlpp/subprogram.h iverilog-11.0/vhdlpp/subprogram.h --- iverilog-10.3/vhdlpp/subprogram.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/subprogram.h 2020-09-26 22:44:25.000000000 +0000 @@ -31,8 +31,8 @@ class InterfacePort; class SequentialStmt; +class Package; class VType; -class SubprogramHeader; class SubprogramBody : public LineInfo, public ScopeBase { @@ -45,14 +45,18 @@ void set_statements(std::list*statements); inline bool empty_statements() const { return !statements_ || statements_->empty(); } + int elaborate(); int emit(ostream&out, Entity*ent, ScopeBase*scope); // Emit body as it would show up in a package. - int emit_package(std::ostream&fd) const; + int emit_package(std::ostream&fd); void write_to_stream(std::ostream&fd) const; void dump(std::ostream&fd) const; + const SubprogramHeader*header() const { return header_; } + bool is_subprogram() const { return true; } + private: std::list*statements_; SubprogramHeader*header_; @@ -70,12 +74,18 @@ // matches this subprogram and that subprogram. bool compare_specification(SubprogramHeader*that) const; + int param_count() const { return ports_ ? ports_->size() : 0; } const InterfacePort*find_param(perm_string nam) const; + const InterfacePort*peek_param(int idx) const; const VType*peek_param_type(int idx) const; const VType*peek_return_type() const { return return_type_; } - void set_parent(const ScopeBase*par); - inline const ScopeBase*get_parent() const { return parent_; } + // Computes the exact return type (e.g. std_logic_vector(7 downto 0) + // instead of generic std_logic_vector) + virtual const VType*exact_return_type(const std::vector&, Entity*, ScopeBase*); + + inline void set_package(const Package*pkg) { assert(!package_); package_ = pkg; } + inline const Package*get_package() const { return package_; } // Checks if either return type or parameters are unbounded vectors. bool unbounded() const; @@ -88,11 +98,20 @@ inline perm_string name() const { return name_; } + int elaborate() { return (body_ ? body_->elaborate() : 0); } + + // Elaborates an argument basing on the types stored in the subprogram header. + int elaborate_argument(Expression*expr, int idx, Entity*ent, ScopeBase*scope); + + // Emits the function name, including the package if required. + int emit_full_name(const std::vector&argv, + std::ostream&out, Entity*, ScopeBase*) const; + // Function name used in the emission step. The main purpose of this // method is to handle functions offered by standard VHDL libraries. // Allows to return different function names depending on the arguments // (think of size casting or signed/unsigned functions). - virtual int emit_name(const std::vector&, + virtual int emit_name(const std::vector&argv, std::ostream&out, Entity*, ScopeBase*) const; // Emit arguments for a specific call. It allows to reorder or skip @@ -125,18 +144,29 @@ std::list*ports_; const VType*return_type_; SubprogramBody*body_; - const ScopeBase*parent_; + const Package*package_; }; // Class to define functions headers defined in the standard VHDL libraries. -class SubprogramBuiltin : public SubprogramHeader +class SubprogramStdHeader : public SubprogramHeader { public: - SubprogramBuiltin(perm_string vhdl_name, perm_string sv_name, - std::list*ports, const VType*return_type); - ~SubprogramBuiltin(); + SubprogramStdHeader(perm_string nam, std::list*ports, + const VType*return_type) : + SubprogramHeader(nam, ports, return_type) {} + virtual ~SubprogramStdHeader() {}; bool is_std() const { return true; } +}; + +// The simplest case, when only function name has to be changed. +class SubprogramBuiltin : public SubprogramStdHeader +{ + public: + SubprogramBuiltin(perm_string vhdl_name, perm_string sv_name, + std::list*ports, const VType*return_type) : + SubprogramStdHeader(vhdl_name, ports, return_type), sv_name_(sv_name) {} + ~SubprogramBuiltin() {} int emit_name(const std::vector&, std::ostream&out, Entity*, ScopeBase*) const; @@ -145,4 +175,8 @@ perm_string sv_name_; }; +// Helper function to print out a human-readable function signature. +void emit_subprogram_sig(std::ostream&out, perm_string name, + const std::list&arg_types); + #endif /* IVL_subprogram_H */ diff -Nru iverilog-10.3/vhdlpp/vsignal.cc iverilog-11.0/vhdlpp/vsignal.cc --- iverilog-10.3/vhdlpp/vsignal.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vsignal.cc 2020-09-26 22:44:25.000000000 +0000 @@ -21,7 +21,9 @@ # include "vsignal.h" # include "expression.h" +# include "scope.h" # include "vtype.h" +# include "std_types.h" # include using namespace std; @@ -35,11 +37,12 @@ { } -void SigVarBase::elaborate_init_expr(Entity*ent, ScopeBase*scope) +void SigVarBase::elaborate(Entity*ent, ScopeBase*scope) { - if(init_expr_) { + if(init_expr_) init_expr_->elaborate_expr(ent, scope, peek_type()); - } + + type_->elaborate(ent, scope); } void SigVarBase::type_elaborate_(VType::decl_t&decl) @@ -47,37 +50,45 @@ decl.type = type_; } -int Signal::emit(ostream&out, Entity*ent, ScopeBase*scope) +int Signal::emit(ostream&out, Entity*ent, ScopeBase*scope, bool initialize) { int errors = 0; VType::decl_t decl; type_elaborate_(decl); - if (peek_refcnt_sequ_() > 0 || !peek_type()->can_be_packed()) + + const VType*type = peek_type(); + if (peek_refcnt_sequ_() > 0 + || (!type->can_be_packed() && dynamic_cast(type))) decl.reg_flag = true; errors += decl.emit(out, peek_name()); - Expression*init_expr = peek_init_expr(); - if (init_expr) { - out << " = "; - init_expr->emit(out, ent, scope); + const Expression*init_expr = peek_init_expr(); + if (initialize && init_expr) { + /* Emit initialization value for wires as a weak assignment */ + if(!decl.reg_flag && !type->type_match(&primitive_REAL)) + out << ";" << endl << "/*init*/ assign (weak1, weak0) " << peek_name(); + + out << " = "; + init_expr->emit(out, ent, scope); } out << ";" << endl; return errors; } -int Variable::emit(ostream&out, Entity*ent, ScopeBase*scope) +int Variable::emit(ostream&out, Entity*ent, ScopeBase*scope, bool initialize) { int errors = 0; + out << (!scope->is_subprogram() ? "static " : "automatic "); + VType::decl_t decl; type_elaborate_(decl); - if (peek_refcnt_sequ_() > 0 || !peek_type()->can_be_packed()) - decl.reg_flag = true; + decl.reg_flag = true; errors += decl.emit(out, peek_name()); - Expression*init_expr = peek_init_expr(); - if (init_expr) { + const Expression*init_expr = peek_init_expr(); + if (initialize && init_expr) { out << " = "; init_expr->emit(out, ent, scope); } diff -Nru iverilog-10.3/vhdlpp/vsignal.h iverilog-11.0/vhdlpp/vsignal.h --- iverilog-10.3/vhdlpp/vsignal.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vsignal.h 2020-09-26 22:44:25.000000000 +0000 @@ -42,18 +42,17 @@ void dump(ostream&out, int indent = 0) const; - // Elaborates initializer expressions if needed. - void elaborate_init_expr(Entity*ent, ScopeBase*scope); + // Elaborates type & initializer expressions. + void elaborate(Entity*ent, ScopeBase*scope); perm_string peek_name() const { return name_; } + const Expression* peek_init_expr() const { return init_expr_; } protected: unsigned peek_refcnt_sequ_() const { return refcnt_sequ_; } void type_elaborate_(VType::decl_t&decl); - Expression* peek_init_expr() const { return init_expr_; } - private: perm_string name_; const VType*type_; @@ -71,7 +70,7 @@ public: Signal(perm_string name, const VType*type, Expression*init_expr); - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope, bool initalize = true); }; class Variable : public SigVarBase { @@ -79,7 +78,7 @@ public: Variable(perm_string name, const VType*type, Expression*init_expr = NULL); - int emit(ostream&out, Entity*ent, ScopeBase*scope); + int emit(ostream&out, Entity*ent, ScopeBase*scope, bool initialize = true); void write_to_stream(std::ostream&fd); }; diff -Nru iverilog-10.3/vhdlpp/vtype.cc iverilog-11.0/vhdlpp/vtype.cc --- iverilog-10.3/vhdlpp/vtype.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vtype.cc 2020-09-26 22:44:25.000000000 +0000 @@ -58,9 +58,6 @@ case BIT: out << "BIT"; break; - case CHARACTER: - out << "CHARACTER"; - break; case INTEGER: out << "INTEGER"; break; @@ -90,9 +87,6 @@ case NATURAL: return 32; - case CHARACTER: - return 8; - default: std::cerr << "sorry: primitive type " << type_ << " has no get_width() implementation." << std::endl; @@ -114,21 +108,21 @@ /* * Create a VTypeArray range set from a list of parsed ranges. - * FIXME: We are copying pointers from the prange_t object into the - * range_t. This means that we cannot delete the prange_t object + * FIXME: We are copying pointers from the ExpRange object into the + * range_t. This means that we cannot delete the ExpRange object * unless we invent a way to remove the pointers from that object. So * this is a memory leak. Something to fix. */ -VTypeArray::VTypeArray(const VType*element, std::list*r, bool sv) +VTypeArray::VTypeArray(const VType*element, std::list*r, bool sv) : etype_(element), ranges_(r->size()), signed_flag_(sv), parent_(NULL) { for (size_t idx = 0 ; idx < ranges_.size() ; idx += 1) { - prange_t*curp = r->front(); + ExpRange*curp = r->front(); r->pop_front(); - Expression*msb = curp->msb(); - Expression*lsb = curp->lsb(); - bool dir = curp->is_downto(); - ranges_[idx] = range_t(msb, lsb, dir); + ranges_[idx] = range_t(curp->msb(), curp->lsb(), + (curp->direction() == ExpRange::DOWNTO + ? true + : false)); } } @@ -271,8 +265,8 @@ } } -VTypeRange::VTypeRange(const VType*base, int64_t end_val, int64_t start_val) -: base_(base), end_(end_val), start_(start_val) +VTypeRange::VTypeRange(const VType*base) +: base_(base) { } @@ -280,6 +274,28 @@ { } +VTypeRangeConst::VTypeRangeConst(const VType*base, int64_t start_val, int64_t end_val) +: VTypeRange(base), start_(start_val), end_(end_val) +{ +} + +VTypeRangeExpr::VTypeRangeExpr(const VType*base, Expression*start_expr, + Expression*end_expr, bool downto) +: VTypeRange(base), start_(start_expr), end_(end_expr), downto_(downto) +{ +} + +VTypeRangeExpr::~VTypeRangeExpr() +{ + delete start_; + delete end_; +} + +VType*VTypeRangeExpr::clone() const { + return new VTypeRangeExpr(base_type()->clone(), start_->clone(), + end_->clone(), downto_); +} + VTypeEnum::VTypeEnum(const std::list*names) : names_(names->size()) { diff -Nru iverilog-10.3/vhdlpp/vtype_elaborate.cc iverilog-11.0/vhdlpp/vtype_elaborate.cc --- iverilog-10.3/vhdlpp/vtype_elaborate.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vtype_elaborate.cc 2020-09-26 22:44:25.000000000 +0000 @@ -29,7 +29,8 @@ int VTypeArray::elaborate(Entity*ent, ScopeBase*scope) const { int errors = 0; - etype_->elaborate(ent, scope); + + errors += etype_->elaborate(ent, scope); for (vector::const_iterator cur = ranges_.begin() ; cur != ranges_.end() ; ++ cur) { @@ -43,3 +44,14 @@ return errors; } + +int VTypeRangeExpr::elaborate(Entity*ent, ScopeBase*scope) const +{ + int errors = 0; + + errors += base_->elaborate(ent, scope); + errors += start_->elaborate_expr(ent, scope, 0); + errors += end_->elaborate_expr(ent, scope, 0); + + return errors; +} diff -Nru iverilog-10.3/vhdlpp/vtype_emit.cc iverilog-11.0/vhdlpp/vtype_emit.cc --- iverilog-10.3/vhdlpp/vtype_emit.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vtype_emit.cc 2020-09-26 22:44:25.000000000 +0000 @@ -22,6 +22,7 @@ # include "vtype.h" # include "expression.h" +# include "std_types.h" # include # include # include @@ -64,7 +65,15 @@ const VTypePrimitive*base = dynamic_cast (raw_base); if (base) { - assert(dimensions() == 1); + assert(dimensions().size() == 1); + + // If this is a string type without any boundaries specified, then + // there is a direct counterpart in SV called.. 'string' + if(this == &primitive_STRING) { + out << "string"; + emit_name(out, name); + return errors; + } base->emit_def(out, empty_perm_string); if (signed_flag_) @@ -89,9 +98,21 @@ list dims; const VTypeArray*cur = this; - while (const VTypeArray*sub = dynamic_cast (cur->element_type())) { - dims.push_back(cur); - cur = sub; + bool added_dim = true; + + while(added_dim) { + added_dim = false; + const VType*el_type = cur->element_type(); + + while(const VTypeDef*tdef = dynamic_cast(el_type)) { + el_type = tdef->peek_definition(); + } + + if(const VTypeArray*sub = dynamic_cast(el_type)) { + dims.push_back(cur); + cur = sub; + added_dim = true; + } } dims.push_back(cur); @@ -106,16 +127,16 @@ name_emitted = true; } - for(unsigned i = 0; i < cur->dimensions(); ++i) { + for(unsigned i = 0; i < cur->dimensions().size(); ++i) { if(cur->dimension(i).is_box() && !name_emitted) { emit_name(out, name); name_emitted = true; } out << "["; - if (!cur->dimension(i).is_box()) { // if not unbounded { + if (!cur->dimension(i).is_box()) { // if not unbounded errors += cur->dimension(i).msb()->emit(out, 0, 0); - out << ":"; + out << ":"; errors += cur->dimension(i).lsb()->emit(out, 0, 0); } out << "]"; @@ -132,7 +153,7 @@ int VTypeEnum::emit_def(ostream&out, perm_string name) const { int errors = 0; - out << "enum integer {"; + out << "enum int {"; assert(names_.size() >= 1); out << "\\" << names_[0] << " "; for (size_t idx = 1 ; idx < names_.size() ; idx += 1) @@ -144,6 +165,17 @@ return errors; } +int VTypeEnum::emit_decl(std::ostream&out, perm_string name, bool reg_flag) const +{ + if (!reg_flag) + out << "wire "; + + out << "int"; + emit_name(out, name); + + return 0; +} + int VTypePrimitive::emit_primitive_type(ostream&out) const { int errors = 0; @@ -163,9 +195,6 @@ case REAL: out << "real"; break; - case CHARACTER: - out << "byte"; - break; case TIME: out << "time"; break; @@ -217,28 +246,15 @@ */ int VTypeDef::emit_def(ostream&out, perm_string name) const { - int errors = 0; emit_name(out, name_); emit_name(out, name); - return errors; + + return 0; } int VTypeDef::emit_decl(ostream&out, perm_string name, bool reg_flag) const { - int errors = 0; - - if (!dynamic_cast(type_)) - out << (reg_flag ? "reg " : "wire "); - - if(dynamic_cast(type_)) { - errors += type_->emit_def(out, name); - } else { - assert(name_ != empty_perm_string); - cout << "\\" << name_; - emit_name(out, name); - } - - return errors; + return type_->emit_decl(out, name, reg_flag); } int VTypeDef::emit_typedef(ostream&out, typedef_context_t&ctx) const diff -Nru iverilog-10.3/vhdlpp/vtype.h iverilog-11.0/vhdlpp/vtype.h --- iverilog-10.3/vhdlpp/vtype.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vtype.h 2020-09-26 22:44:25.000000000 +0000 @@ -33,7 +33,7 @@ class ScopeBase; class Entity; class Expression; -class prange_t; +class ExpRange; class VTypeDef; class ScopeBase; @@ -72,6 +72,10 @@ // definitions. Most types accept the default definition of this. virtual void write_type_to_stream(std::ostream&fd) const; + // Emits a type definition. This is used to distinguish types and + // subtypes. + virtual void write_typedef_to_stream(std::ostream&fd, perm_string name) const; + // This virtual method writes a human-readable version of the // type to a given file for debug purposes. (Question: is this // really necessary given the write_to_stream method?) @@ -105,8 +109,6 @@ // to evaluate. virtual int get_width(ScopeBase*) const { return -1; } - private: - friend struct decl_t; // This virtual method is called to emit the declaration. This // is used by the decl_t object to emit variable/wire/port declarations. virtual int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; @@ -156,7 +158,7 @@ class VTypePrimitive : public VType { public: - enum type_t { BIT, INTEGER, NATURAL, REAL, STDLOGIC, CHARACTER, TIME }; + enum type_t { BIT, INTEGER, NATURAL, REAL, STDLOGIC, TIME }; public: VTypePrimitive(type_t tt, bool packed = false); @@ -164,6 +166,7 @@ VType*clone() const { return new VTypePrimitive(*this); } + bool type_match(const VType*that) const; void write_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*scope) const; @@ -210,19 +213,20 @@ public: VTypeArray(const VType*etype, const std::vector&r, bool signed_vector = false); - VTypeArray(const VType*etype, std::list*r, bool signed_vector = false); + VTypeArray(const VType*etype, std::list*r, bool signed_vector = false); VTypeArray(const VType*etype, int msb, int lsb, bool signed_vector = false); ~VTypeArray(); VType*clone() const; int elaborate(Entity*ent, ScopeBase*scope) const; + bool type_match(const VType*that) const; void write_to_stream(std::ostream&fd) const; void write_type_to_stream(std::ostream&fd) const; void show(std::ostream&) const; int get_width(ScopeBase*scope) const; - inline size_t dimensions() const { return ranges_.size(); }; + const std::vector&dimensions() const { return ranges_; }; const range_t&dimension(size_t idx) const { return ranges_[idx]; } @@ -249,6 +253,8 @@ // To handle subtypes inline void set_parent_type(const VTypeArray*parent) { parent_ = parent; } + const VTypeArray*get_parent_type() const { return parent_; } + // Wherever it is possible, replaces range lsb & msb expressions with // constant integers. void evaluate_ranges(ScopeBase*scope); @@ -269,27 +275,62 @@ class VTypeRange : public VType { public: - VTypeRange(const VType*base, int64_t end, int64_t start); - ~VTypeRange(); + VTypeRange(const VType*base); + virtual ~VTypeRange() = 0; - VType*clone() const { return new VTypeRange(base_->clone(), start_, end_); } + bool write_std_types(std::ostream&fd) const; + int emit_def(std::ostream&out, perm_string name) const; + bool type_match(const VType*that) const; // Get the type that is limited by the range. - inline const VType* base_type() const { return base_; } + inline const VType*base_type() const { return base_; } + + protected: + const VType*base_; +}; + +class VTypeRangeConst : public VTypeRange { + + public: + VTypeRangeConst(const VType*base, int64_t end, int64_t start); + + VType*clone() const { + return new VTypeRangeConst(base_type()->clone(), start_, end_); + } + + int64_t start() const { return start_; } + int64_t end() const { return end_; } + + void write_to_stream(std::ostream&fd) const; + + private: + const int64_t start_, end_; +}; + +class VTypeRangeExpr : public VTypeRange { + + public: + VTypeRangeExpr(const VType*base, Expression*end, Expression*start, bool downto); + ~VTypeRangeExpr(); + + VType*clone() const; + int elaborate(Entity*end, ScopeBase*scope) const; public: // Virtual methods void write_to_stream(std::ostream&fd) const; - int emit_def(std::ostream&out, perm_string name) const; private: - const VType*base_; - int64_t end_, start_; + // Boundaries + Expression*start_, *end_; + + // Range direction (downto/to) + bool downto_; }; class VTypeEnum : public VType { public: - VTypeEnum(const std::list*names); + explicit VTypeEnum(const std::list*names); ~VTypeEnum(); VType*clone() const { return new VTypeEnum(*this); } @@ -299,6 +340,7 @@ int get_width(ScopeBase*) const { return 32; } int emit_def(std::ostream&out, perm_string name) const; + int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; // Checks if the name is stored in the enum. bool has_name(perm_string name) const; @@ -352,7 +394,7 @@ public: explicit VTypeDef(perm_string name); explicit VTypeDef(perm_string name, const VType*is); - ~VTypeDef(); + virtual ~VTypeDef(); VType*clone() const { return new VTypeDef(*this); } @@ -368,22 +410,28 @@ // type, and this method gets it for us. inline const VType* peek_definition(void) const { return type_; } - void write_to_stream(std::ostream&fd) const; + virtual void write_to_stream(std::ostream&fd) const; void write_type_to_stream(std::ostream&fd) const; int get_width(ScopeBase*scope) const { return type_->get_width(scope); } int emit_typedef(std::ostream&out, typedef_context_t&ctx) const; int emit_def(std::ostream&out, perm_string name) const; + int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; bool can_be_packed() const { return type_->can_be_packed(); } bool is_unbounded() const { return type_->is_unbounded(); } - private: - int emit_decl(std::ostream&out, perm_string name, bool reg_flag) const; - private: + protected: perm_string name_; const VType*type_; }; +class VSubTypeDef : public VTypeDef { + public: + explicit VSubTypeDef(perm_string name) : VTypeDef(name) {} + explicit VSubTypeDef(perm_string name, const VType*is) : VTypeDef(name, is) {} + void write_typedef_to_stream(std::ostream&fd, perm_string name) const; +}; + #endif /* IVL_vtype_H */ diff -Nru iverilog-10.3/vhdlpp/vtype_match.cc iverilog-11.0/vhdlpp/vtype_match.cc --- iverilog-10.3/vhdlpp/vtype_match.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vtype_match.cc 2020-09-26 22:44:25.000000000 +0000 @@ -38,5 +38,69 @@ if(VType::type_match(that)) return true; - return VType::type_match(type_); + return type_->type_match(that); +} + +bool VTypePrimitive::type_match(const VType*that) const +{ + if(VType::type_match(that)) + return true; + + if(const VTypePrimitive*prim = dynamic_cast(that)) { + // TODO it is not always true, but works for many cases + type_t that_type = prim->type(); + return ((type_ == NATURAL || type_ == INTEGER) && + (that_type == NATURAL || that_type == INTEGER)); + } + + if(const VTypeRangeConst*range = dynamic_cast(that)) { + if (type_ == INTEGER) + return true; + if (type_ == NATURAL && range->start() >= 0 && range->end() >= 0) + return true; + } + + return false; +} + +bool VTypeArray::type_match(const VType*that) const +{ + if(VType::type_match(that)) + return true; + + // Check if both arrays are of the same size + if(const VTypeArray*arr = dynamic_cast(that)) { + const VTypeArray*this_parent = this; + while(const VTypeArray*tmp = this_parent->get_parent_type()) + this_parent = tmp; + + const VTypeArray*that_parent = arr; + while(const VTypeArray*tmp = that_parent->get_parent_type()) + that_parent = tmp; + + if(this_parent != that_parent) + return false; + + int this_width = get_width(NULL); + int that_width = arr->get_width(NULL); + + // Either one of the sizes is undefined, or both are the same size + if(this_width > 0 && that_width > 0 && this_width != that_width) + return false; + + return true; + } + + return false; +} + +bool VTypeRange::type_match(const VType*that) const +{ + if(VType::type_match(that)) + return true; + + if(base_->type_match(that)) + return true; + + return false; } diff -Nru iverilog-10.3/vhdlpp/vtype_stream.cc iverilog-11.0/vhdlpp/vtype_stream.cc --- iverilog-10.3/vhdlpp/vtype_stream.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vhdlpp/vtype_stream.cc 2020-09-26 22:44:25.000000000 +0000 @@ -36,6 +36,16 @@ write_to_stream(fd); } +void VType::write_typedef_to_stream(ostream&fd, perm_string name) const +{ + if(is_global_type(name)) + return; + + fd << "type " << name << " is "; + write_type_to_stream(fd); + fd << ";" << endl; +} + void VTypeArray::write_to_stream(ostream&fd) const { if(write_special_case(fd)) @@ -161,9 +171,6 @@ case STDLOGIC: fd << "std_logic"; break; - case CHARACTER: - fd << "character"; - break; case TIME: fd << "time"; break; @@ -174,21 +181,41 @@ } } -void VTypeRange::write_to_stream(ostream&fd) const +bool VTypeRange::write_std_types(ostream&fd) const { - // Detect some special cases that can be written as ieee or - // standard types. - if (const VTypePrimitive*tmp = dynamic_cast (base_)) { - if (tmp->type()==VTypePrimitive::NATURAL) { - fd << "natural"; - return; - } - } + // Detect some special cases that can be written as ieee or + // standard types. + if (const VTypePrimitive*tmp = dynamic_cast(base_)) { + if (tmp->type()==VTypePrimitive::NATURAL) { + fd << "natural"; + return true; + } + } - base_->write_to_stream(fd); - fd << " range " << start_; - fd << (start_ < end_ ? " to " : " downto "); - fd << end_; + return false; +} + +void VTypeRangeConst::write_to_stream(ostream&fd) const +{ + if(write_std_types(fd)) + return; + + base_type()->write_to_stream(fd); + fd << " range " << start_; + fd << (start_ < end_ ? " to " : " downto "); + fd << end_; +} + +void VTypeRangeExpr::write_to_stream(ostream&fd) const +{ + if(write_std_types(fd)) + return; + + base_type()->write_to_stream(fd); + fd << " range "; + start_->write_to_stream(fd); + fd << (downto_ ? " downto " : " to "); + end_->write_to_stream(fd); } void VTypeRecord::write_to_stream(ostream&fd) const @@ -221,3 +248,12 @@ fd << ")"; } +void VSubTypeDef::write_typedef_to_stream(ostream&fd, perm_string name) const +{ + if(is_global_type(name)) + return; + + fd << "subtype " << name << " is "; + write_type_to_stream(fd); + fd << ";" << endl; +} diff -Nru iverilog-10.3/vpi/cppcheck.sup iverilog-11.0/vpi/cppcheck.sup --- iverilog-10.3/vpi/cppcheck.sup 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/cppcheck.sup 2020-09-26 22:44:25.000000000 +0000 @@ -3,150 +3,178 @@ // problems will not be fixed. // fstapi.c from GTKWave -obsoleteFunctionsasctime:fstapi.c:930 -obsoleteFunctionsalloca:fstapi.c:2305 +asctimeCalled:fstapi.c:935 +allocaCalled:fstapi.c:2310 +unreadVariable:fstapi.c:188 +unreadVariable:fstapi.c:189 +unreadVariable:fstapi.c:1602 +unreadVariable:fstapi.c:1610 +unreadVariable:fstapi.c:1614 +unreadVariable:fstapi.c:1619 +unreadVariable:fstapi.c:1642 unreadVariable:fstapi.c:1648 -variableScope:fstapi.c:666 -variableScope:fstapi.c:1338 -variableScope:fstapi.c:1339 -variableScope:fstapi.c:1396 -variableScope:fstapi.c:1979 -variableScope:fstapi.c:2108 -variableScope:fstapi.c:2541 -variableScope:fstapi.c:2542 -variableScope:fstapi.c:2733 -variableScope:fstapi.c:2734 +unreadVariable:fstapi.c:1649 +unreadVariable:fstapi.c:1653 +unreadVariable:fstapi.c:2742 +unreadVariable:fstapi.c:6324 +variableScope:fstapi.c:1035 +variableScope:fstapi.c:668 +variableScope:fstapi.c:1342 +variableScope:fstapi.c:1343 +variableScope:fstapi.c:1344 +variableScope:fstapi.c:1401 +variableScope:fstapi.c:1984 +variableScope:fstapi.c:2113 +variableScope:fstapi.c:2546 +variableScope:fstapi.c:2547 +variableScope:fstapi.c:2736 +variableScope:fstapi.c:2737 variableScope:fstapi.c:2738 -variableScope:fstapi.c:2854 -variableScope:fstapi.c:2893 -variableScope:fstapi.c:2894 -variableScope:fstapi.c:3612 -variableScope:fstapi.c:3786 -variableScope:fstapi.c:3788 -variableScope:fstapi.c:4230 -variableScope:fstapi.c:4231 -variableScope:fstapi.c:4240 -variableScope:fstapi.c:4503 -variableScope:fstapi.c:4774 -variableScope:fstapi.c:4777 -variableScope:fstapi.c:5271 -variableScope:fstapi.c:5275 -variableScope:fstapi.c:5276 -variableScope:fstapi.c:5432 -variableScope:fstapi.c:5490 -variableScope:fstapi.c:5803 -variableScope:fstapi.c:5806 -variableScope:fstapi.c:6042 -variableScope:fstapi.c:6147 -variableScope:fstapi.c:6148 -variableScope:fstapi.c:6179 -variableScope:fstapi.c:6407 +variableScope:fstapi.c:2739 +variableScope:fstapi.c:2740 +variableScope:fstapi.c:2741 +variableScope:fstapi.c:2742 +variableScope:fstapi.c:2843 +variableScope:fstapi.c:2844 +variableScope:fstapi.c:2848 +variableScope:fstapi.c:2964 +variableScope:fstapi.c:3003 +variableScope:fstapi.c:3004 +variableScope:fstapi.c:3723 +variableScope:fstapi.c:3901 +variableScope:fstapi.c:3903 +variableScope:fstapi.c:4345 +variableScope:fstapi.c:4346 +variableScope:fstapi.c:4355 +variableScope:fstapi.c:4618 +variableScope:fstapi.c:4889 +variableScope:fstapi.c:4892 +variableScope:fstapi.c:5388 +variableScope:fstapi.c:5392 +variableScope:fstapi.c:5392 +variableScope:fstapi.c:5393 +variableScope:fstapi.c:5551 +variableScope:fstapi.c:5609 +variableScope:fstapi.c:5922 +variableScope:fstapi.c:5925 +variableScope:fstapi.c:6219 +variableScope:fstapi.c:6324 +variableScope:fstapi.c:6325 +variableScope:fstapi.c:6356 +variableScope:fstapi.c:6584 +variableScope:fstapi.c:6760 +variableScope:fstapi.c:6761 +variableScope:fstapi.c:6762 + // These functions are not used by Icarus // fstReaderClrFacProcessMask() -unusedFunction:fstapi.c:3344 +unusedFunction:fstapi.c:3455 // fstReaderClrFacProcessMaskAll() -unusedFunction:fstapi.c:3373 +unusedFunction:fstapi.c:3484 // fstReaderGetAliasCount() -unusedFunction:fstapi.c:3436 +unusedFunction:fstapi.c:3547 // fstReaderGetCurrentFlatScope() -unusedFunction:fstapi.c:3182 -// fstReaderGetAliasCount() -unusedFunction:fstapi.c:3279 +unusedFunction:fstapi.c:3293 +// fstReaderGetCurrentScopeLen() +unusedFunction:fstapi.c:3390 // fstReaderGetCurrentScopeUserInfo() -unusedFunction:fstapi.c:3196 +unusedFunction:fstapi.c:3307 // fstReaderGetDateString() -unusedFunction:fstapi.c:3464 +unusedFunction:fstapi.c:3575 // fstReaderGetDoubleEndianMatchState() -unusedFunction:fstapi.c:3450 +unusedFunction:fstapi.c:3561 // fstReaderGetDumpActivityChangeTime() -unusedFunction:fstapi.c:3492 +unusedFunction:fstapi.c:3603 // fstReaderGetDumpActivityChangeValue() -unusedFunction:fstapi.c:3507 +unusedFunction:fstapi.c:3618 // fstReaderGetEndTime() -unusedFunction:fstapi.c:3401 +unusedFunction:fstapi.c:3512 // fstReaderGetFacProcessMask() -unusedFunction:fstapi.c:3307 +unusedFunction:fstapi.c:3418 // fstReaderGetFileType() -unusedFunction:fstapi.c:3471 +unusedFunction:fstapi.c:3582 // fstReaderGetFseekFailed() -unusedFunction:fstapi.c:3292 +unusedFunction:fstapi.c:3403 // fstReaderGetMaxHandle() -unusedFunction:fstapi.c:3429 +unusedFunction:fstapi.c:3540 // fstReaderGetMemoryUsedByWriter() -unusedFunction:fstapi.c:3408 +unusedFunction:fstapi.c:3519 // fstReaderGetNumberDumpActivityChanges() -unusedFunction:fstapi.c:3485 +unusedFunction:fstapi.c:3596 // fstReaderGetScopeCount() -unusedFunction:fstapi.c:3415 +unusedFunction:fstapi.c:3526 // fstReaderGetStartTime() -unusedFunction:fstapi.c:3394 +unusedFunction:fstapi.c:3505 // fstReaderGetTimescale() -unusedFunction:fstapi.c:3387 +unusedFunction:fstapi.c:3498 // fstReaderGetTimezero() -unusedFunction:fstapi.c:3478 +unusedFunction:fstapi.c:3589 // fstReaderGetValueChangeSectionCount() -unusedFunction:fstapi.c:3443 +unusedFunction:fstapi.c:3554 // fstReaderGetValueFromHandleAtTime() -unusedFunction:fstapi.c:5685 +unusedFunction:fstapi.c:5804 // fstReaderGetVarCount() -unusedFunction:fstapi.c:3422 +unusedFunction:fstapi.c:3533 // fstReaderGetVersionString() -unusedFunction:fstapi.c:3457 +unusedFunction:fstapi.c:3568 // fstReaderIterBlocks() -unusedFunction:fstapi.c:4652 +unusedFunction:fstapi.c:4767 // fstReaderIterBlocksSetNativeDoublesOnCallback() -unusedFunction:fstapi.c:3557 +unusedFunction:fstapi.c:3668 // fstReaderIterateHier() -unusedFunction:fstapi.c:3782 +unusedFunction:fstapi.c:3897 // fstReaderIterateHierRewind() -unusedFunction:fstapi.c:3762 +unusedFunction:fstapi.c:3877 // fstReaderOpen() -unusedFunction:fstapi.c:4550 +unusedFunction:fstapi.c:4665 // fstReaderOpenForUtilitiesOnly() -unusedFunction:fstapi.c:4542 +unusedFunction:fstapi.c:4657 // fstReaderPushScope() -unusedFunction:fstapi.c:3244 +unusedFunction:fstapi.c:3355 // fstReaderResetScope() -unusedFunction:fstapi.c:3233 +unusedFunction:fstapi.c:3344 // fstReaderSetFacProcessMask() -unusedFunction:fstapi.c:3326 +unusedFunction:fstapi.c:3437 // fstReaderSetFacProcessMaskAll() -unusedFunction:fstapi.c:3362 +unusedFunction:fstapi.c:3473 // fstReaderSetLimitTimeRange() -unusedFunction:fstapi.c:3522 +unusedFunction:fstapi.c:3633 // fstReaderSetUnlimitedTimeRange() -unusedFunction:fstapi.c:3535 +unusedFunction:fstapi.c:3646 // fstReaderSetVcdExtensions() -unusedFunction:fstapi.c:3546 -// fstUtilityEscToBin() -unusedFunction:fstapi.c:6485 +unusedFunction:fstapi.c:3657 +// fstUtilityExtractEnumTableFromString() +unusedFunction:fstapi.c:6757 +// fstUtilityFreeEnumTable() +unusedFunction:fstapi.c:6818 +// fstWriterCreateEnumTable() +unusedFunction:fstapi.c:2731 // fstWriterCreateVar2() -unusedFunction:fstapi.c:2528 +unusedFunction:fstapi.c:2533 +// fstWriterEmitEnumTableRef() +unusedFunction:fstapi.c:2826 // fstWriterEmitVariableLengthValueChange() -unusedFunction:fstapi.c:2847 +unusedFunction:fstapi.c:2957 // fstWriterGetFseekFailed() -unusedFunction:fstapi.c:2511 +unusedFunction:fstapi.c:2516 // fstWriterSetAttrEnd() -unusedFunction:fstapi.c:2714 +unusedFunction:fstapi.c:2719 // fstWriterSetComment() -unusedFunction:fstapi.c:2360 +unusedFunction:fstapi.c:2365 // fstWriterSetEnvVar() -unusedFunction:fstapi.c:2372 +unusedFunction:fstapi.c:2377 // fstWriterSetFileType() -unusedFunction:fstapi.c:2235 +unusedFunction:fstapi.c:2240 // fstWriterSetParallelMode() -unusedFunction:fstapi.c:2471 +unusedFunction:fstapi.c:2476 // fstWriterSetTimezero() -unusedFunction:fstapi.c:2436 +unusedFunction:fstapi.c:2441 // fstWriterSetValueList() -unusedFunction:fstapi.c:2366 - -// These functions are not used by Icarus -//unusedFunction:fstapi.c:226 +unusedFunction:fstapi.c:2371 // lxt2_write.c from GTKWave -obsoleteFunctionsalloca:lxt2_write.c:1813 -obsoleteFunctionsalloca:lxt2_write.c:1819 +allocaCalled:lxt2_write.c:1813 +allocaCalled:lxt2_write.c:1819 variableScope:lxt2_write.c:33 variableScope:lxt2_write.c:63 variableScope:lxt2_write.c:196 @@ -174,13 +202,15 @@ // lxt2_wr_set_partial_preference() unusedFunction:lxt2_write.c:812 // lxt2_wr_set_timezero() -unusedFunction:lxt2_write.c:2198 +unusedFunction:lxt2_write.c:2201 // lxt2_wr_symbol_bracket_stripping() unusedFunction:lxt2_write.c:1581 // lxt2_wr_symbol_find() unusedFunction:lxt2_write.c:877 // lxt_write.c from GTKWave +shiftNegative:lxt_write.c:2700 +shiftNegative:lxt_write.c:2744 variableScope:lxt_write.c:31 variableScope:lxt_write.c:83 variableScope:lxt_write.c:527 @@ -227,70 +257,78 @@ unusedFunction:lxt_write.c:1277 // fastlz.c from GTKWave +unreadVariable:fastlz.c:421 + +unusedLabel:fastlz.c:545 // These functions are not used by Icarus // fastlz_compress_level() unusedFunction:fastlz.c:150 +// FASTLZ_COMPRESSOR() +unusedFunction:fastlz.c:162 +// FASTLZ_DECOMPRESSOR() +unusedFunction:fastlz.c:416 // lz4.c from GTKWave +unusedStructMember:lz4.c:140 // These functions are not used by Icarus // LZ4_compress_continue() -unusedFunction:lz4.c:1460 +unusedFunction:lz4.c:1463 // LZ4_compress_destSize() -unusedFunction:lz4.c:912 +unusedFunction:lz4.c:915 // LZ4_compress_fast_force() -unusedFunction:lz4.c:705 +unusedFunction:lz4.c:708 // LZ4_compress_forceExtDict() -unusedFunction:lz4.c:1063 +unusedFunction:lz4.c:1066 // LZ4_compress_limitedOutput() -unusedFunction:lz4.c:1455 +unusedFunction:lz4.c:1458 // LZ4_compress_limitedOutput_continue() -unusedFunction:lz4.c:1459 +unusedFunction:lz4.c:1462 // LZ4_compress_limitedOutput_withState() -unusedFunction:lz4.c:1457 +unusedFunction:lz4.c:1460 // LZ4_compress_withState() -unusedFunction:lz4.c:1458 +unusedFunction:lz4.c:1461 // LZ4_create() -unusedFunction:lz4.c:1489 +unusedFunction:lz4.c:1492 // LZ4_createStream() -unusedFunction:lz4.c:935 +unusedFunction:lz4.c:938 // LZ4_createStreamDecode() -unusedFunction:lz4.c:1319 +unusedFunction:lz4.c:1322 // LZ4_decompress_fast_continue() -unusedFunction:lz4.c:1384 +unusedFunction:lz4.c:1387 // LZ4_decompress_fast_usingDict() -unusedFunction:lz4.c:1439 +unusedFunction:lz4.c:1442 // LZ4_decompress_fast_withPrefix64k() -unusedFunction:lz4.c:1510 +unusedFunction:lz4.c:1513 // LZ4_decompress_safe_continue() -unusedFunction:lz4.c:1355 +unusedFunction:lz4.c:1358 // LZ4_decompress_safe_forceExtDict() -unusedFunction:lz4.c:1445 +unusedFunction:lz4.c:1448 // LZ4_decompress_safe_usingDict() -unusedFunction:lz4.c:1434 +unusedFunction:lz4.c:1437 // LZ4_decompress_safe_withPrefix64k() -unusedFunction:lz4.c:1505 +unusedFunction:lz4.c:1508 // LZ4_freeStream() -unusedFunction:lz4.c:948 +unusedFunction:lz4.c:951 // LZ4_freeStreamDecode() -unusedFunction:lz4.c:1325 +unusedFunction:lz4.c:1328 // LZ4_loadDict() -unusedFunction:lz4.c:956 +unusedFunction:lz4.c:959 // LZ4_resetStreamState() -unusedFunction:lz4.c:1482 +unusedFunction:lz4.c:1485 // LZ4_setStreamDecode() -unusedFunction:lz4.c:1338 +unusedFunction:lz4.c:1341 // LZ4_sizeofState() -unusedFunction:lz4.c:373 +unusedFunction:lz4.c:376 // LZ4_sizeofStreamState() -unusedFunction:lz4.c:1474 +unusedFunction:lz4.c:1477 // LZ4_slideInputBuffer() -unusedFunction:lz4.c:1496 +unusedFunction:lz4.c:1499 // LZ4_uncompress() -unusedFunction:lz4.c:1468 +unusedFunction:lz4.c:1471 // LZ4_uncompress_unknownOutputSize() -unusedFunction:lz4.c:1469 +unusedFunction:lz4.c:1472 // LZ4_versionNumber() -unusedFunction:lz4.c:371 +unusedFunction:lz4.c:374 // The routines in sys_random.c are exact copies from IEEE1364-2005 and // they have scope warnings that we need to ignore. diff -Nru iverilog-10.3/vpi/fastlz.c iverilog-11.0/vpi/fastlz.c --- iverilog-10.3/vpi/fastlz.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/fastlz.c 2020-09-26 22:44:25.000000000 +0000 @@ -22,6 +22,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT */ #include "fastlz.h" diff -Nru iverilog-10.3/vpi/fastlz.h iverilog-11.0/vpi/fastlz.h --- iverilog-10.3/vpi/fastlz.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/fastlz.h 2020-09-26 22:44:25.000000000 +0000 @@ -22,6 +22,8 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + SPDX-License-Identifier: MIT */ #ifndef FASTLZ_H diff -Nru iverilog-10.3/vpi/fstapi.c iverilog-11.0/vpi/fstapi.c --- iverilog-10.3/vpi/fstapi.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/fstapi.c 2020-09-26 22:44:25.000000000 +0000 @@ -18,6 +18,8 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ /* @@ -37,11 +39,15 @@ * */ -#include +#ifndef FST_CONFIG_INCLUDE +# define FST_CONFIG_INCLUDE +#endif +#include FST_CONFIG_INCLUDE #include "fstapi.h" #include "fastlz.h" #include "lz4.h" +#include #ifndef HAVE_LIBPTHREAD #undef FST_WRITER_PARALLEL @@ -128,6 +134,16 @@ #include #endif +#ifdef __GNUC__ +/* Boolean expression more often true than false */ +#define FST_LIKELY(x) __builtin_expect(!!(x), 1) +/* Boolean expression more often false than true */ +#define FST_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define FST_LIKELY(x) (!!(x)) +#define FST_UNLIKELY(x) (!!(x)) +#endif + #define FST_APIMESS "FSTAPI | " /***********************/ @@ -723,6 +739,9 @@ uint32_t *valpos_mem; unsigned char *curval_mem; +unsigned char *outval_mem; /* for two-state / Verilator-style value changes */ +uint32_t outval_alloc_siz; + char *filename; fstHandle maxhandle; @@ -775,6 +794,7 @@ pthread_attr_t thread_attr; struct fstWriterContext *xc_parent; #endif +unsigned in_pthread : 1; size_t fst_orig_break_size; size_t fst_orig_break_add_size; @@ -952,6 +972,19 @@ /* * mmap functions */ +static void fstWriterMmapSanity(void *pnt, const char *file, int line, const char *usage) +{ +#if !defined(__CYGWIN__) && !defined(__MINGW32__) +if(pnt == MAP_FAILED) + { + fprintf(stderr, "fstMmap() assigned to %s failed: errno: %d, file %s, line %d.\n", usage, errno, file, line); + perror("Why"); + pnt = NULL; + } +#endif +} + + static void fstWriterCreateMmaps(struct fstWriterContext *xc) { off_t curpos = ftello(xc->handle); @@ -974,19 +1007,29 @@ if(!xc->valpos_mem) { fflush(xc->valpos_handle); - xc->valpos_mem = (uint32_t *)fstMmap(NULL, xc->maxhandle * 4 * sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->valpos_handle), 0); + errno = 0; + if(xc->maxhandle) + { + fstWriterMmapSanity(xc->valpos_mem = (uint32_t *)fstMmap(NULL, xc->maxhandle * 4 * sizeof(uint32_t), PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->valpos_handle), 0), __FILE__, __LINE__, "xc->valpos_mem"); + } } if(!xc->curval_mem) { fflush(xc->curval_handle); - xc->curval_mem = (unsigned char *)fstMmap(NULL, xc->maxvalpos, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->curval_handle), 0); + errno = 0; + if(xc->maxvalpos) + { + fstWriterMmapSanity(xc->curval_mem = (unsigned char *)fstMmap(NULL, xc->maxvalpos, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->curval_handle), 0), __FILE__, __LINE__, "xc->curval_handle"); + } } } static void fstDestroyMmaps(struct fstWriterContext *xc, int is_closing) { +#if !defined __CYGWIN__ && !defined __MINGW32__ (void)is_closing; +#endif fstMunmap(xc->valpos_mem, xc->maxhandle * 4 * sizeof(uint32_t)); xc->valpos_mem = NULL; @@ -1682,7 +1725,8 @@ tlen = ftello(xc->tchn_handle); fstWriterFseeko(xc, xc->tchn_handle, 0, SEEK_SET); -tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->tchn_handle), 0); +errno = 0; +fstWriterMmapSanity(tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->tchn_handle), 0), __FILE__, __LINE__, "tmem"); if(tmem) { unsigned long destlen = tlen; @@ -1763,10 +1807,9 @@ { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; +pthread_mutex_lock(&(xc->xc_parent->mutex)); fstWriterFlushContextPrivate2(xc); -pthread_mutex_unlock(&(xc->xc_parent->mutex)); - #ifdef FST_REMOVE_DUPLICATE_VC free(xc->curval_mem); #endif @@ -1775,6 +1818,9 @@ tmpfile_close(&xc->tchn_handle, &xc->tchn_handle_nam); free(xc); +xc->xc_parent->in_pthread = 0; +pthread_mutex_unlock(&(xc->xc_parent->mutex)); + return(NULL); } @@ -1822,7 +1868,15 @@ xc->section_header_only = 0; xc->secnum++; + while (xc->in_pthread) + { + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + }; + pthread_mutex_lock(&xc->mutex); + xc->in_pthread = 1; + pthread_mutex_unlock(&xc->mutex); pthread_create(&xc->thread, &xc->thread_attr, fstWriterFlushContextPrivate1, xc2); } @@ -1874,7 +1928,7 @@ if(xc && !xc->already_in_close && !xc->already_in_flush) { - unsigned char *tmem; + unsigned char *tmem = NULL; off_t fixup_offs, tlen, hlen; xc->already_in_close = 1; /* never need to zero this out as it is freed at bottom */ @@ -1890,7 +1944,7 @@ xc->skip_writing_section_hdr = 1; if(!xc->size_limit_locked) { - if(xc->is_initial_time) /* simulation time never advanced so mock up the changes as time zero ones */ + if(FST_UNLIKELY(xc->is_initial_time)) /* simulation time never advanced so mock up the changes as time zero ones */ { fstHandle dupe_idx; @@ -1904,15 +1958,31 @@ #ifdef FST_WRITER_PARALLEL pthread_mutex_lock(&xc->mutex); pthread_mutex_unlock(&xc->mutex); + + while (xc->in_pthread) + { + pthread_mutex_lock(&xc->mutex); + pthread_mutex_unlock(&xc->mutex); + }; #endif } } fstDestroyMmaps(xc, 1); + if(xc->outval_mem) + { + free(xc->outval_mem); xc->outval_mem = NULL; + xc->outval_alloc_siz = 0; + } /* write out geom section */ fflush(xc->geom_handle); tlen = ftello(xc->geom_handle); - tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->geom_handle), 0); + errno = 0; + if(tlen) + { + fstWriterMmapSanity(tmem = (unsigned char *)fstMmap(NULL, tlen, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->geom_handle), 0), __FILE__, __LINE__, "tmem"); + } + if(tmem) { unsigned long destlen = tlen; @@ -2020,14 +2090,18 @@ { int lz4_maxlen; unsigned char *mem; - unsigned char *hmem; + unsigned char *hmem = NULL; int packed_len; fflush(xc->handle); lz4_maxlen = LZ4_compressBound(xc->hier_file_len); mem = (unsigned char *)malloc(lz4_maxlen); - hmem = (unsigned char *)fstMmap(NULL, xc->hier_file_len, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->hier_handle), 0); + errno = 0; + if(xc->hier_file_len) + { + fstWriterMmapSanity(hmem = (unsigned char *)fstMmap(NULL, xc->hier_file_len, PROT_READ|PROT_WRITE, MAP_SHARED, fileno(xc->hier_handle), 0), __FILE__, __LINE__, "hmem"); + } packed_len = LZ4_compress((char *)hmem, (char *)mem, xc->hier_file_len); fstMunmap(hmem, xc->hier_file_len); @@ -2843,12 +2917,12 @@ uint32_t offs; int len; -if((xc) && (handle <= xc->maxhandle)) +if(FST_LIKELY((xc) && (handle <= xc->maxhandle))) { uint32_t fpos; uint32_t *vm4ip; - if(!xc->valpos_mem) + if(FST_UNLIKELY(!xc->valpos_mem)) { xc->vc_emitted = 1; fstWriterCreateMmaps(xc); @@ -2858,17 +2932,17 @@ vm4ip = &(xc->valpos_mem[4*handle]); len = vm4ip[1]; - if(len) /* len of zero = variable length, use fstWriterEmitVariableLengthValueChange */ + if(FST_LIKELY(len)) /* len of zero = variable length, use fstWriterEmitVariableLengthValueChange */ { - if(!xc->is_initial_time) + if(FST_LIKELY(!xc->is_initial_time)) { fpos = xc->vchg_siz; - if((fpos + len + 10) > xc->vchg_alloc_siz) + if(FST_UNLIKELY((fpos + len + 10) > xc->vchg_alloc_siz)) { xc->vchg_alloc_siz += (xc->fst_break_add_size + len); /* +len added in the case of extremely long vectors and small break add sizes */ xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); - if(!xc->vchg_mem) + if(FST_UNLIKELY(!xc->vchg_mem)) { fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitValueChange, exiting.\n"); exit(255); @@ -2953,18 +3027,139 @@ } } +void fstWriterEmitValueChange32(void *ctx, fstHandle handle, + uint32_t bits, uint32_t val) { + char buf[32]; + char *s = buf; + uint32_t i; + for (i = 0; i < bits; ++i) + { + *s++ = '0' + ((val >> (bits - i - 1)) & 1); + } + fstWriterEmitValueChange(ctx, handle, buf); +} +void fstWriterEmitValueChange64(void *ctx, fstHandle handle, + uint32_t bits, uint64_t val) { + char buf[64]; + char *s = buf; + uint32_t i; + for (i = 0; i < bits; ++i) + { + *s++ = '0' + ((val >> (bits - i - 1)) & 1); + } + fstWriterEmitValueChange(ctx, handle, buf); +} +void fstWriterEmitValueChangeVec32(void *ctx, fstHandle handle, + uint32_t bits, const uint32_t *val) { + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (FST_UNLIKELY(bits <= 32)) + { + fstWriterEmitValueChange32(ctx, handle, bits, val[0]); + } + else if(FST_LIKELY(xc)) + { + int bq = bits / 32; + int br = bits & 31; + int i; + int w; + uint32_t v; + unsigned char* s; + if (FST_UNLIKELY(bits > xc->outval_alloc_siz)) + { + xc->outval_alloc_siz = bits*2 + 1; + xc->outval_mem = (unsigned char*)realloc(xc->outval_mem, xc->outval_alloc_siz); + if (FST_UNLIKELY(!xc->outval_mem)) + { + fprintf(stderr, + FST_APIMESS "Could not realloc() in fstWriterEmitValueChangeVec32, exiting.\n"); + exit(255); + } + } + s = xc->outval_mem; + { + w = bq; + v = val[w]; + for (i = 0; i < br; ++i) + { + *s++ = '0' + ((v >> (br - i - 1)) & 1); + } + } + for (w = bq - 1; w >= 0; --w) + { + v = val[w]; + for (i = (32 - 4); i >= 0; i -= 4) { + s[0] = '0' + ((v >> (i + 3)) & 1); + s[1] = '0' + ((v >> (i + 2)) & 1); + s[2] = '0' + ((v >> (i + 1)) & 1); + s[3] = '0' + ((v >> (i + 0)) & 1); + s += 4; + } + } + fstWriterEmitValueChange(ctx, handle, xc->outval_mem); + } +} +void fstWriterEmitValueChangeVec64(void *ctx, fstHandle handle, + uint32_t bits, const uint64_t *val) { + struct fstWriterContext *xc = (struct fstWriterContext *)ctx; + if (FST_UNLIKELY(bits <= 64)) + { + fstWriterEmitValueChange64(ctx, handle, bits, val[0]); + } + else if(FST_LIKELY(xc)) + { + int bq = bits / 64; + int br = bits & 63; + int i; + int w; + uint32_t v; + unsigned char* s; + if (FST_UNLIKELY(bits > xc->outval_alloc_siz)) + { + xc->outval_alloc_siz = bits*2 + 1; + xc->outval_mem = (unsigned char*)realloc(xc->outval_mem, xc->outval_alloc_siz); + if (FST_UNLIKELY(!xc->outval_mem)) + { + fprintf(stderr, + FST_APIMESS "Could not realloc() in fstWriterEmitValueChangeVec64, exiting.\n"); + exit(255); + } + } + s = xc->outval_mem; + { + w = bq; + v = val[w]; + for (i = 0; i < br; ++i) + { + *s++ = '0' + ((v >> (br - i - 1)) & 1); + } + } + for (w = bq - 1; w >= 0; --w) { + v = val[w]; + for (i = (64 - 4); i >= 0; i -= 4) + { + s[0] = '0' + ((v >> (i + 3)) & 1); + s[1] = '0' + ((v >> (i + 2)) & 1); + s[2] = '0' + ((v >> (i + 1)) & 1); + s[3] = '0' + ((v >> (i + 0)) & 1); + s += 4; + } + } + fstWriterEmitValueChange(ctx, handle, xc->outval_mem); + } +} + void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len) { struct fstWriterContext *xc = (struct fstWriterContext *)ctx; const unsigned char *buf = (const unsigned char *)val; -if((xc) && (handle <= xc->maxhandle)) +if(FST_LIKELY((xc) && (handle <= xc->maxhandle))) { uint32_t fpos; uint32_t *vm4ip; - if(!xc->valpos_mem) + if(FST_UNLIKELY(!xc->valpos_mem)) { xc->vc_emitted = 1; fstWriterCreateMmaps(xc); @@ -2974,15 +3169,15 @@ vm4ip = &(xc->valpos_mem[4*handle]); /* there is no initial time dump for variable length value changes */ - if(!vm4ip[1]) /* len of zero = variable length */ + if(FST_LIKELY(!vm4ip[1])) /* len of zero = variable length */ { fpos = xc->vchg_siz; - if((fpos + len + 10 + 5) > xc->vchg_alloc_siz) + if(FST_UNLIKELY((fpos + len + 10 + 5) > xc->vchg_alloc_siz)) { xc->vchg_alloc_siz += (xc->fst_break_add_size + len + 5); /* +len added in the case of extremely long vectors and small break add sizes */ xc->vchg_mem = (unsigned char *)realloc(xc->vchg_mem, xc->vchg_alloc_siz); - if(!xc->vchg_mem) + if(FST_UNLIKELY(!xc->vchg_mem)) { fprintf(stderr, FST_APIMESS "Could not realloc() in fstWriterEmitVariableLengthValueChange, exiting.\n"); exit(255); @@ -3004,7 +3199,7 @@ int skip = 0; if(xc) { - if(xc->is_initial_time) + if(FST_UNLIKELY(xc->is_initial_time)) { if(xc->size_limit_locked) /* this resets xc->is_initial_time to one */ { diff -Nru iverilog-10.3/vpi/fstapi.h iverilog-11.0/vpi/fstapi.h --- iverilog-10.3/vpi/fstapi.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/fstapi.h 2020-09-26 22:44:25.000000000 +0000 @@ -18,6 +18,8 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ #ifndef FST_API_H @@ -355,6 +357,14 @@ void fstWriterEmitDumpActive(void *ctx, int enable); void fstWriterEmitEnumTableRef(void *ctx, fstEnumHandle handle); void fstWriterEmitValueChange(void *ctx, fstHandle handle, const void *val); +void fstWriterEmitValueChange32(void *ctx, fstHandle handle, + uint32_t bits, uint32_t val); +void fstWriterEmitValueChange64(void *ctx, fstHandle handle, + uint32_t bits, uint64_t val); +void fstWriterEmitValueChangeVec32(void *ctx, fstHandle handle, + uint32_t bits, const uint32_t *val); +void fstWriterEmitValueChangeVec64(void *ctx, fstHandle handle, + uint32_t bits, const uint64_t *val); void fstWriterEmitVariableLengthValueChange(void *ctx, fstHandle handle, const void *val, uint32_t len); void fstWriterEmitTimeChange(void *ctx, uint64_t tim); void fstWriterFlushContext(void *ctx); diff -Nru iverilog-10.3/vpi/libvpi.c iverilog-11.0/vpi/libvpi.c --- iverilog-10.3/vpi/libvpi.c 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/vpi/libvpi.c 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,303 @@ +/* + * 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. + */ + +#if defined(__MINGW32__) || defined (__CYGWIN__) + +#include "vpi_user.h" +#include + +static vpip_routines_s*vpip_routines = 0; + +// callback related + +vpiHandle vpi_register_cb(p_cb_data data) +{ + assert(vpip_routines); + return vpip_routines->register_cb(data); +} +PLI_INT32 vpi_remove_cb(vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->remove_cb(ref); +} + +vpiHandle vpi_register_systf(const struct t_vpi_systf_data*ss) +{ + assert(vpip_routines); + return vpip_routines->register_systf(ss); +} +void vpi_get_systf_info(vpiHandle obj, p_vpi_systf_data data) +{ + assert(vpip_routines); + vpip_routines->get_systf_info(obj, data); +} + +// for obtaining handles + +vpiHandle vpi_handle_by_name(const char*name, vpiHandle scope) +{ + assert(vpip_routines); + return vpip_routines->handle_by_name(name, scope); +} +vpiHandle vpi_handle_by_index(vpiHandle ref, PLI_INT32 idx) +{ + assert(vpip_routines); + return vpip_routines->handle_by_index(ref, idx); +} + +// for traversing relationships + +vpiHandle vpi_handle(PLI_INT32 type, vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->handle(type, ref); +} +vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->iterate(type, ref); +} +vpiHandle vpi_scan(vpiHandle iter) +{ + assert(vpip_routines); + return vpip_routines->scan(iter); +} + +// for processing properties + +PLI_INT32 vpi_get(int property, vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->get(property, ref); +} +char*vpi_get_str(PLI_INT32 property, vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->get_str(property, ref); +} + +// delay processing + +void vpi_get_delays(vpiHandle expr, p_vpi_delay delays) +{ + assert(vpip_routines); + vpip_routines->get_delays(expr, delays); +} +void vpi_put_delays(vpiHandle expr, p_vpi_delay delays) +{ + assert(vpip_routines); + vpip_routines->put_delays(expr, delays); +} + +// value processing + +void vpi_get_value(vpiHandle expr, p_vpi_value value) +{ + assert(vpip_routines); + vpip_routines->get_value(expr, value); +} +vpiHandle vpi_put_value(vpiHandle obj, p_vpi_value value, p_vpi_time when, PLI_INT32 flags) +{ + assert(vpip_routines); + return vpip_routines->put_value(obj, value, when, flags); +} + +// time processing + +void vpi_get_time(vpiHandle obj, s_vpi_time*t) +{ + assert(vpip_routines); + vpip_routines->get_time(obj, t); +} + +// data processing + +void*vpi_get_userdata(vpiHandle obj) +{ + assert(vpip_routines); + return vpip_routines->get_userdata(obj); +} +PLI_INT32 vpi_put_userdata(vpiHandle obj, void*data) +{ + assert(vpip_routines); + return vpip_routines->put_userdata(obj, data); +} + +// I/O routines + +PLI_UINT32 vpi_mcd_open(char*name) +{ + assert(vpip_routines); + return vpip_routines->mcd_open(name); +} +PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) +{ + assert(vpip_routines); + return vpip_routines->mcd_close(mcd); +} +PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) +{ + assert(vpip_routines); + return vpip_routines->mcd_flush(mcd); +} +char*vpi_mcd_name(PLI_UINT32 mcd) +{ + assert(vpip_routines); + return vpip_routines->mcd_name(mcd); +} +PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, const char*fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + assert(vpip_routines); + PLI_INT32 rv = vpip_routines->mcd_vprintf(mcd, fmt, ap); + va_end(ap); + return rv; +} +PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, const char*fmt, va_list ap) +{ + assert(vpip_routines); + return vpip_routines->mcd_vprintf(mcd, fmt, ap); +} + +PLI_INT32 vpi_flush(void) +{ + assert(vpip_routines); + return vpip_routines->flush(); +} +PLI_INT32 vpi_printf(const char*fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + assert(vpip_routines); + PLI_INT32 rv = vpip_routines->vprintf(fmt, ap); + va_end(ap); + return rv; +} +PLI_INT32 vpi_vprintf(const char*fmt, va_list ap) +{ + assert(vpip_routines); + return vpip_routines->vprintf(fmt, ap); +} + +// utility routines + +PLI_INT32 vpi_chk_error(p_vpi_error_info info) +{ + assert(vpip_routines); + return vpip_routines->chk_error(info); +} +PLI_INT32 vpi_compare_objects(vpiHandle obj1, vpiHandle obj2) +{ + assert(vpip_routines); + return vpip_routines->compare_objects(obj1, obj2); +} +PLI_INT32 vpi_free_object(vpiHandle ref) +{ + assert(vpip_routines); + return vpip_routines->free_object(ref); +} +PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info info) +{ + assert(vpip_routines); + return vpip_routines->get_vlog_info(info); + +} + +// control routines + +void vpi_control(PLI_INT32 operation, ...) +{ + va_list ap; + va_start(ap, operation); + assert(vpip_routines); + vpip_routines->vcontrol(operation, ap); + va_end(ap); +} +void vpi_sim_control(PLI_INT32 operation, ...) +{ + va_list ap; + va_start(ap, operation); + assert(vpip_routines); + vpip_routines->vcontrol(operation, ap); + va_end(ap); +} + +// proposed standard extensions + +PLI_INT32 vpi_fopen(const char*name, const char*mode) +{ + assert(vpip_routines); + return vpip_routines->fopen(name, mode); +} +FILE*vpi_get_file(PLI_INT32 fd) +{ + assert(vpip_routines); + return vpip_routines->get_file(fd); +} + +// Icarus extensions + +s_vpi_vecval vpip_calc_clog2(vpiHandle arg) +{ + assert(vpip_routines); + return vpip_routines->calc_clog2(arg); +} +void vpip_count_drivers(vpiHandle ref, unsigned idx, unsigned counts[4]) +{ + assert(vpip_routines); + vpip_routines->count_drivers(ref, idx, counts); +} +void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit) +{ + assert(vpip_routines); + vpip_routines->format_strength(str, value, bit); +} +void vpip_make_systf_system_defined(vpiHandle ref) +{ + assert(vpip_routines); + vpip_routines->make_systf_system_defined(ref); +} +void vpip_mcd_rawwrite(PLI_UINT32 mcd, const char*buf, size_t count) +{ + assert(vpip_routines); + vpip_routines->mcd_rawwrite(mcd, buf, count); +} +void vpip_set_return_value(int value) +{ + assert(vpip_routines); + vpip_routines->set_return_value(value); +} + +DLLEXPORT PLI_UINT32 vpip_set_callback(vpip_routines_s*routines, PLI_UINT32 version) +{ + if (version != vpip_routines_version) + return 0; + + vpip_routines = routines; + return 1; +} + +#else + +void __libvpi_c_dummy_function(void) +{ +} + +#endif diff -Nru iverilog-10.3/vpi/lz4.c iverilog-11.0/vpi/lz4.c --- iverilog-10.3/vpi/lz4.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/lz4.c 2020-09-26 22:44:25.000000000 +0000 @@ -27,6 +27,8 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + SPDX-License-Identifier: BSD-2-Clause + You can contact the author at : - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c diff -Nru iverilog-10.3/vpi/lz4.h iverilog-11.0/vpi/lz4.h --- iverilog-10.3/vpi/lz4.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/lz4.h 2020-09-26 22:44:25.000000000 +0000 @@ -28,6 +28,8 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + SPDX-License-Identifier: BSD-2-Clause + You can contact the author at : - LZ4 source repository : https://github.com/Cyan4973/lz4 - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c diff -Nru iverilog-10.3/vpi/Makefile.in iverilog-11.0/vpi/Makefile.in --- iverilog-10.3/vpi/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -33,6 +33,8 @@ CC = @CC@ CXX = @CXX@ +AR = @AR@ +RANLIB = @RANLIB@ INSTALL = @INSTALL@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_DATA = @INSTALL_DATA@ @@ -51,7 +53,8 @@ LDFLAGS = @LDFLAGS@ # Object files for system.vpi -O = sys_table.o sys_convert.o sys_countdrivers.o sys_darray.o sys_deposit.o sys_display.o \ +O = sys_table.o sys_convert.o sys_countdrivers.o sys_darray.o sys_deposit.o \ + sys_display.o \ sys_fileio.o sys_finish.o sys_icarus.o sys_plusargs.o sys_queue.o \ sys_random.o sys_random_mti.o sys_readmem.o sys_readmem_lex.o sys_scanf.o \ sys_sdf.o sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o mt19937int.o \ @@ -68,27 +71,30 @@ endif # Object files for v2005_math.vpi -M = sys_clog2.o v2005_math.o +V2005 = sys_clog2.o v2005_math.o # Object files for va_math.vpi -V = va_math.o +VA_MATH = va_math.o -V2009 = v2009_table.o v2009_array.o v2009_enum.o v2009_string.o +V2009 = v2009_table.o v2009_array.o v2009_bitvec.o v2009_enum.o v2009_string.o \ + sys_priv.o -VHDL_SYS = vhdl_table.o +VHDL_SYS = vhdl_table.o sys_priv.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 libvpi.a system.vpi va_math.vpi v2005_math.vpi v2009.vpi vhdl_sys.vpi vhdl_textio.vpi vpi_debug.vpi $(ALL32) check: all clean: - rm -rf *.o sys_readmem_lex.c dep system.vpi + rm -rf *.o sys_readmem_lex.c dep libvpi.a system.vpi 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 @@ -97,7 +103,8 @@ # The -U flag is used to skip checking paths that depend on that define having # an explicit value (i.e. the define is expected to be real code). cppcheck: $(O:.o=.c) $(OPP:.o=.cc) $(M:.o=.c) $(V:.o=.c) - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ @@ -117,6 +124,12 @@ $(CXX) $(CPPFLAGS) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o mv $*.d dep +libvpi.a: libvpi.c ../vpi_user.h + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< + rm -f libvpi.a + $(AR) cqv libvpi.a libvpi.o + $(RANLIB) libvpi.a + LIBS = @LIBS@ SYSTEM_VPI_LDFLAGS = $(LIBS) VA_MATH_LDFLAGS = @@ -125,8 +138,8 @@ VA_MATH_LDFLAGS += @EXTRALIBS@ endif -system.vpi: $O $(OPP) ../vvp/libvpi.a - $(CXX) @shared@ -o $@ $O $(OPP) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) +system.vpi: $O $(OPP) libvpi.a + $(CXX) @shared@ -o $@ $O $(OPP) -L. $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) sys_readmem_lex.c: $(srcdir)/sys_readmem_lex.lex $(LEX) -t $< > $@ @@ -136,99 +149,81 @@ sdf_lexor.c: $(srcdir)/sdf_lexor.lex $(LEX) -t $< > $@ -# Build this in two steps to avoid parallel build issues (see pr3462585) -sdf_parse.c: $(srcdir)/sdf_parse.y - $(YACC) --verbose -t -p sdf -d -o $@ $< -sdf_parse.h: sdf_parse.c +# Use pattern rules to avoid parallel build issues (see pr3462585) +sdf_parse%c sdf_parse%h: $(srcdir)/sdf_parse%y + $(YACC) --verbose -t -p sdf -d -o sdf_parse.c $< table_mod_lexor.o: table_mod_lexor.c table_mod_parse.h table_mod_lexor.c: $(srcdir)/table_mod_lexor.lex $(LEX) -t $< > $@ -# Build this in two steps to avoid parallel build issues (see pr3462585) -table_mod_parse.c: $(srcdir)/table_mod_parse.y - $(YACC) --verbose -t -p tblmod -d -o $@ $< -table_mod_parse.h: table_mod_parse.c +# Use pattern rules to avoid parallel build issues (see pr3462585) +table_mod_parse%c table_mod_parse%h: $(srcdir)/table_mod_parse%y + $(YACC) --verbose -t -p tblmod -d -o table_mod_parse.c $< -v2005_math.vpi: $M ../vvp/libvpi.a - $(CC) @shared@ -o $@ $M -L../vvp $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS) +v2005_math.vpi: $(V2005) libvpi.a + $(CC) @shared@ -o $@ $(V2005) -L. $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS) -v2009.vpi: $(V2009) ../vvp/libvpi.a - $(CC) @shared@ -o $@ $(V2009) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) +v2009.vpi: $(V2009) libvpi.a + $(CC) @shared@ -o $@ $(V2009) -L. $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) -va_math.vpi: $V ../vvp/libvpi.a - $(CC) @shared@ -o $@ $V -L../vvp $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS) +va_math.vpi: $(VA_MATH) libvpi.a + $(CC) @shared@ -o $@ $(VA_MATH) -L. $(LDFLAGS) -lvpi $(VA_MATH_VPI_LDFLAGS) -vhdl_sys.vpi: $(VHDL_SYS) ../vvp/libvpi.a - $(CC) @shared@ -o $@ $(VHDL_SYS) -L../vvp $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) +vhdl_sys.vpi: $(VHDL_SYS) libvpi.a + $(CC) @shared@ -o $@ $(VHDL_SYS) -L. $(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) +vhdl_textio.vpi: $(VHDL_TEXTIO) libvpi.a + $(CC) @shared@ -o $@ $(VHDL_TEXTIO) -L. $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) + +vpi_debug.vpi: $(VPI_DEBUG) libvpi.a + $(CC) @shared@ -o $@ $(VPI_DEBUG) -L. $(LDFLAGS) -lvpi $(SYSTEM_VPI_LDFLAGS) stamp-vpi_config-h: $(srcdir)/vpi_config.h.in ../config.status @rm -f $@ cd ..; ./config.status --header=vpi/vpi_config.h vpi_config.h: stamp-vpi_config-h -install: all installdirs \ - $(vpidir)/system.vpi $(vpidir)/system.sft \ - $(vpidir)/va_math.vpi $(vpidir)/va_math.sft \ - $(vpidir)/v2005_math.vpi $(vpidir)/v2005_math.sft \ - $(vpidir)/v2009.vpi $(vpidir)/v2009.sft \ - $(vpidir)/vhdl_sys.vpi $(vpidir)/vhdl_sys.sft \ - $(vpidir)/vpi_debug.vpi +install: all installdirs installfiles -$(vpidir)/system.vpi: ./system.vpi - $(INSTALL_PROGRAM) ./system.vpi "$(DESTDIR)$(vpidir)/system.vpi" +F = ./libvpi.a \ + ./system.vpi \ + ./va_math.vpi \ + ./v2005_math.vpi \ + ./v2009.vpi \ + ./vhdl_sys.vpi \ + ./vhdl_textio.vpi \ + ./vpi_debug.vpi -$(vpidir)/system.sft: system.sft - $(INSTALL_DATA) $< "$(DESTDIR)$@" - -$(vpidir)/va_math.vpi: ./va_math.vpi +installfiles: $(F) | installdirs + $(INSTALL_DATA) ./libvpi.a "$(DESTDIR)$(libdir)/libvpi$(suffix).a" + $(INSTALL_PROGRAM) ./system.vpi "$(DESTDIR)$(vpidir)/system.vpi" $(INSTALL_PROGRAM) ./va_math.vpi "$(DESTDIR)$(vpidir)/va_math.vpi" - -$(vpidir)/va_math.sft: va_math.sft - $(INSTALL_DATA) $< "$(DESTDIR)$@" - -$(vpidir)/v2005_math.vpi: ./v2005_math.vpi $(INSTALL_PROGRAM) ./v2005_math.vpi "$(DESTDIR)$(vpidir)/v2005_math.vpi" - -$(vpidir)/v2005_math.sft: v2005_math.sft - $(INSTALL_DATA) $< "$(DESTDIR)$@" - -$(vpidir)/v2009.vpi: ./v2009.vpi $(INSTALL_PROGRAM) ./v2009.vpi "$(DESTDIR)$(vpidir)/v2009.vpi" - -$(vpidir)/v2009.sft: v2009.sft - $(INSTALL_DATA) $< "$(DESTDIR)$@" - -$(vpidir)/vhdl_sys.vpi: ./vhdl_sys.vpi $(INSTALL_PROGRAM) ./vhdl_sys.vpi "$(DESTDIR)$(vpidir)/vhdl_sys.vpi" - -$(vpidir)/vhdl_sys.sft: vhdl_sys.sft - $(INSTALL_DATA) $< "$(DESTDIR)$@" - -$(vpidir)/vpi_debug.vpi: ./vpi_debug.vpi + $(INSTALL_PROGRAM) ./vhdl_textio.vpi "$(DESTDIR)$(vpidir)/vhdl_textio.vpi" $(INSTALL_PROGRAM) ./vpi_debug.vpi "$(DESTDIR)$(vpidir)/vpi_debug.vpi" installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(libdir)" "$(DESTDIR)$(vpidir)" uninstall: + rm -f "$(DESTDIR)$(libdir)/libvpi$(suffix).a" rm -f "$(DESTDIR)$(vpidir)/system.vpi" - rm -f "$(DESTDIR)$(vpidir)/system.sft" rm -f "$(DESTDIR)$(vpidir)/va_math.vpi" - rm -f "$(DESTDIR)$(vpidir)/va_math.sft" rm -f "$(DESTDIR)$(vpidir)/v2005_math.vpi" - rm -f "$(DESTDIR)$(vpidir)/v2005_math.sft" rm -f "$(DESTDIR)$(vpidir)/v2009.vpi" - 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)/vpi_debug.vpi" -include $(patsubst %.o, dep/%.d, $O) -include $(patsubst %.o, dep/%.d, $(OPP)) --include $(patsubst %.o, dep/%.d, $M) --include $(patsubst %.o, dep/%.d, $V) +-include $(patsubst %.o, dep/%.d, $(V2005)) +-include $(patsubst %.o, dep/%.d, $(VA_MATH)) +-include $(patsubst %.o, dep/%.d, $(V2009)) +-include $(patsubst %.o, dep/%.d, $(VHDL_SYS)) +-include $(patsubst %.o, dep/%.d, $(VHDL_TEXTIO)) +-include $(patsubst %.o, dep/%.d, $(VPI_DEBUG)) diff -Nru iverilog-10.3/vpi/sdf_lexor.lex iverilog-11.0/vpi/sdf_lexor.lex --- iverilog-10.3/vpi/sdf_lexor.lex 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sdf_lexor.lex 2020-09-26 22:44:25.000000000 +0000 @@ -5,7 +5,7 @@ %{ /* - * Copyright (c) 2007-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2007-2019 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 @@ -144,6 +144,9 @@ { "INTERCONNECT", K_INTERCONNECT }, { "INSTANCE", K_INSTANCE }, { "IOPATH", K_IOPATH }, + { "PATHPULSE", K_PATHPULSE }, + { "PATHPULSEPERCENT", K_PATHPULSEPERCENT }, + { "PERIOD", K_PERIOD }, { "PROCESS", K_PROCESS }, { "PROGRAM", K_PROGRAM }, { "RECREM", K_RECREM }, diff -Nru iverilog-10.3/vpi/sdf_parse.y iverilog-11.0/vpi/sdf_parse.y --- iverilog-10.3/vpi/sdf_parse.y 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sdf_parse.y 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ %{ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2019 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 @@ -46,7 +46,8 @@ %token K_ABSOLUTE K_CELL K_CELLTYPE K_COND K_CONDELSE K_DATE K_DELAYFILE %token K_DELAY K_DESIGN K_DIVIDER K_HOLD K_INCREMENT K_INSTANCE -%token K_INTERCONNECT K_IOPATH K_NEGEDGE K_POSEDGE K_PROCESS K_PROGRAM +%token K_INTERCONNECT K_IOPATH K_NEGEDGE K_PATHPULSE K_PATHPULSEPERCENT +%token K_PERIOD K_POSEDGE K_PROCESS K_PROGRAM %token K_RECREM K_RECOVERY K_REMOVAL K_SDFVERSION K_SETUP K_SETUPHOLD %token K_TEMPERATURE K_TIMESCALE K_TIMINGCHECK K_VENDOR K_VERSION %token K_VOLTAGE K_WIDTH @@ -65,7 +66,7 @@ %type port port_instance port_interconnect %type signed_real_number -%type delval rvalue rtriple signed_real_number_opt +%type delval rvalue_opt rvalue rtriple signed_real_number_opt %type edge_identifier %type port_edge port_spec @@ -246,7 +247,9 @@ ; deltype - : '(' K_ABSOLUTE del_def_list ')' + : '(' K_PATHPULSE input_output_path_opt rvalue rvalue_opt ')' + | '(' K_PATHPULSEPERCENT input_output_path_opt rvalue rvalue_opt ')' + | '(' K_ABSOLUTE del_def_list ')' | '(' K_INCREMENT del_def_list ')' | '(' error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed delay type\n", @@ -324,6 +327,7 @@ | '(' K_RECREM port_tchk port_tchk rvalue rvalue ')' | '(' K_REMOVAL port_tchk port_tchk rvalue ')' | '(' K_WIDTH port_tchk rvalue ')' + | '(' K_PERIOD port_tchk rvalue ')' ; port_tchk @@ -394,6 +398,15 @@ | K_LOGICAL_ZERO ; +input_output_path_opt + : input_output_path + | + ; + +input_output_path + : port_instance port_instance + ; + port_spec : port_instance { $$.vpi_edge = vpiNoEdge; $$.string_val = $1; } | port_edge { $$ = $1; } @@ -466,6 +479,14 @@ } ; +rvalue_opt + : /* When missing. */ + { $$.value = 0.0; + $$.defined = 0; + } + | rvalue + ; + rvalue : '(' signed_real_number ')' { $$.defined = 1; diff -Nru iverilog-10.3/vpi/sys_convert.c iverilog-11.0/vpi/sys_convert.c --- iverilog-10.3/vpi/sys_convert.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_convert.c 2020-09-26 22:44:25.000000000 +0000 @@ -90,11 +90,6 @@ vpi_control(vpiFinish, 1); } -static PLI_INT32 sizetf_32 (ICARUS_VPI_CONST PLI_BYTE8*name) -{ - (void)name; /* Parameter is not used. */ - return 32; -} static PLI_INT32 sizetf_64 (ICARUS_VPI_CONST PLI_BYTE8*name) { (void)name; /* Parameter is not used. */ @@ -252,39 +247,43 @@ s_vpi_systf_data tf_data; vpiHandle res; - tf_data.type = vpiSysFunc; - tf_data.user_data = "$bitstoreal"; - tf_data.tfname = tf_data.user_data; - tf_data.sizetf = sizetf_64; - tf_data.compiletf = sys_convert_compiletf; - tf_data.calltf = sys_bitstoreal_calltf; + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiRealFunc; + tf_data.user_data = "$bitstoreal"; + tf_data.tfname = tf_data.user_data; + tf_data.sizetf = 0; + tf_data.compiletf = sys_convert_compiletf; + tf_data.calltf = sys_bitstoreal_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - tf_data.type = vpiSysFunc; - tf_data.user_data = "$itor"; - tf_data.tfname = tf_data.user_data; - tf_data.sizetf = sizetf_64; - tf_data.compiletf = sys_convert_compiletf; - tf_data.calltf = sys_itor_calltf; + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiRealFunc; + tf_data.user_data = "$itor"; + tf_data.tfname = tf_data.user_data; + tf_data.sizetf = 0; + tf_data.compiletf = sys_convert_compiletf; + tf_data.calltf = sys_itor_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - tf_data.type = vpiSysFunc; - tf_data.user_data = "$realtobits"; - tf_data.tfname = tf_data.user_data; - tf_data.sizetf = sizetf_64; - tf_data.compiletf = sys_convert_compiletf; - tf_data.calltf = sys_realtobits_calltf; + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.user_data = "$realtobits"; + tf_data.tfname = tf_data.user_data; + tf_data.sizetf = sizetf_64; + tf_data.compiletf = sys_convert_compiletf; + tf_data.calltf = sys_realtobits_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - tf_data.type = vpiSysFunc; - tf_data.user_data = "$rtoi"; - tf_data.tfname = tf_data.user_data; - tf_data.sizetf = sizetf_32; - tf_data.compiletf = sys_convert_compiletf; - tf_data.calltf = sys_rtoi_calltf; + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.user_data = "$rtoi"; + tf_data.tfname = tf_data.user_data; + tf_data.sizetf = 0; + tf_data.compiletf = sys_convert_compiletf; + tf_data.calltf = sys_rtoi_calltf; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } diff -Nru iverilog-10.3/vpi/sys_countdrivers.c iverilog-11.0/vpi/sys_countdrivers.c --- iverilog-10.3/vpi/sys_countdrivers.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_countdrivers.c 2020-09-26 22:44:25.000000000 +0000 @@ -78,6 +78,12 @@ vpi_control(vpiFinish, 1); } +static PLI_INT32 sys_countdrivers_sizetf(ICARUS_VPI_CONST PLI_BYTE8*name) +{ + (void)name; /* Parameter is not used. */ + return 1; +} + /* * Check that the given $countdrivers() call has valid arguments. */ @@ -204,12 +210,13 @@ s_vpi_systf_data tf_data; vpiHandle res; - tf_data.type = vpiSysFunc; - tf_data.tfname = "$countdrivers"; - tf_data.calltf = sys_countdrivers_calltf; - tf_data.compiletf = sys_countdrivers_compiletf; - tf_data.sizetf = 0; - tf_data.user_data = "$countdrivers"; + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.tfname = "$countdrivers"; + tf_data.calltf = sys_countdrivers_calltf; + tf_data.compiletf = sys_countdrivers_compiletf; + tf_data.sizetf = sys_countdrivers_sizetf; + tf_data.user_data = "$countdrivers"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); } diff -Nru iverilog-10.3/vpi/sys_darray.c iverilog-11.0/vpi/sys_darray.c --- iverilog-10.3/vpi/sys_darray.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_darray.c 2020-09-26 22:44:25.000000000 +0000 @@ -97,287 +97,6 @@ return 0; } -static PLI_INT32 to_from_vec_compiletf(ICARUS_VPI_CONST PLI_BYTE8*name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv, arg; - - argv = vpi_iterate(vpiArgument, callh); - 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; - } - - /* The first argument must be a dynamic array. */ - arg = vpi_scan(argv); /* This should never be zero. */ - assert(arg); - if (vpi_get(vpiType, arg) != vpiArrayVar) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s first argument must be a dynamic array, " - "given a %s.\n", name, vpi_get_str(vpiType, arg)); - vpi_control(vpiFinish, 1); - } - if (vpi_get(vpiArrayType, arg) != vpiDynamicArray) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s first argument must be a dynamic array.\n", name); - vpi_control(vpiFinish, 1); - } -// HERE: Still need to verify that this is not a real or string array. -// That will require adding TypeSpec support to the VPI. - - /* The second argument must be a net, reg or bit variable. */ - arg = vpi_scan(argv); - if (arg == 0) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s requires a second argument.\n", name); - vpi_control(vpiFinish, 1); - } - switch(vpi_get(vpiType, arg)) { - case vpiNet: - case vpiReg: - case vpiBitVar: - case vpiIntegerVar: - case vpiConstant: - break; - default: - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s second argument must be a logical net or " - "variable.\n", name); - vpi_control(vpiFinish, 1); - } - - arg = vpi_scan(argv); - if (arg != 0) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s has too many arguments.\n", name); - vpi_control(vpiFinish, 1); - vpi_free_object(argv); - } - - return 0; -} - -static const size_t BPW = 8 * sizeof(PLI_INT32); -static const size_t BPWM1 = 8 * sizeof(PLI_INT32) - 1; - -static PLI_INT32 to_vec_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle darr = vpi_scan(argv); - vpiHandle vec = vpi_scan(argv); - - vpi_free_object(argv); - - /* Calculate and check the basic array and vector information. */ - int darr_size = vpi_get(vpiSize, darr); - int darr_word_size = vpi_get(vpiSize, vpi_handle_by_index(darr, 0)); - assert(darr_word_size > 0); - int darr_bit_size = darr_size * darr_word_size; - int vec_size = vpi_get(vpiSize, vec); - - if (darr_size <= 0) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s cannot cast an empty dynamic array.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - if (darr_bit_size != vec_size) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s dynamic array and vector size do not match " - "(%d != %d).\n", name, darr_bit_size, vec_size); - vpi_control(vpiFinish, 1); - return 0; - } - - /* Calculate the number of words needed to hold the dynamic array - * bits and allocate enough space for them. */ - size_t vec_words = (darr_bit_size + BPWM1) / BPW; - s_vpi_vecval *vec_val = calloc(vec_words, sizeof(s_vpi_vecval)); - s_vpi_vecval *vec_ptr = vec_val; - - /* The number of words in each array element. */ - unsigned darr_words = (darr_word_size + BPWM1) / BPW; - - /* The offset in bits into the current vector value. */ - unsigned offset = 0; - - /* We want to get each array word as a vector. */ - s_vpi_value darr_val; - darr_val.format = vpiVectorVal; - - /* We have to reverse the order of the dynamic array words. */ - for (PLI_INT32 i = darr_size - 1; i >= 0; --i) { - /* Get the vector value for the current array word. */ - vpiHandle darr_word = vpi_handle_by_index(darr, i); - vpi_get_value(darr_word, &darr_val); - assert(darr_val.format == vpiVectorVal); - /* The number of bits to copy for this array word. */ - unsigned bits_to_copy = (unsigned)darr_word_size; - - /* Copy the current array bits to the vector and update the - * the vector pointer accordingly. */ - for (unsigned j = 0; j < darr_words; ++j) { - /* Get the current array part and copy it into the - * correct place. */ - PLI_UINT32 aval = darr_val.value.vector->aval; - PLI_UINT32 bval = darr_val.value.vector->bval; - assert(offset < BPW); - vec_ptr->aval |= (aval << offset); - vec_ptr->bval |= (bval << offset); - - /* Calculate the new offset into the vector. */ - offset += (bits_to_copy > BPW) ? BPW : bits_to_copy; - - /* If the new offset is past the end of the vector part - * then the next vector part also needs to be used. */ - if (offset >= BPW) { - ++vec_ptr; - - /* Does the current array part also go into the - * next vector part? */ - if (offset > BPW) { - /* This code has not been tested since the - * current implementation only supports dynamic - * array elements of size 8, 16, 32 or 64 bits - * so currently this code is never run. For - * now assert since it has not been checked. */ - assert(0); - - /* Copy the rest of the array part that did not - * fit in the previous vector part to the next - * vector part. */ - offset -= BPW; - vec_ptr->aval |= (aval >> (darr_word_size - - offset)); - vec_ptr->bval |= (bval >> (darr_word_size - - offset)); - /* Start at the beginning of the next vector part. */ - } else { - offset = 0; - } - } - - /* Advance to the next part of the array. */ - bits_to_copy -= BPW; - darr_val.value.vector++; - } - } - - /* Put the result to the vector and free the allocated space. */ - darr_val.format = vpiVectorVal; - darr_val.value.vector = vec_val; - vpi_put_value(vec, &darr_val, 0, vpiNoDelay); - - free(vec_val); - - return 0; -} - -static PLI_INT32 from_vec_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) -{ - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle darr = vpi_scan(argv); - vpiHandle vec = vpi_scan(argv); - - vpi_free_object(argv); - - /* Calculate and check the basic array and vector information. */ - int darr_size = vpi_get(vpiSize, darr); - int darr_word_size = vpi_get(vpiSize, vpi_handle_by_index(darr, 0)); - assert(darr_word_size > 0); - int darr_bit_size = darr_size * darr_word_size; - - int vec_size = vpi_get(vpiSize, vec); - - if (vec_size <= 0) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s cannot cast an empty vector array.\n", name); - vpi_control(vpiFinish, 1); - return 0; - } - - if (darr_bit_size != vec_size) { - vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), - (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s dynamic array and vector size do not match " - "(%d != %d).\n", name, darr_bit_size, vec_size); - vpi_control(vpiFinish, 1); - return 0; - } - - /* Calculate the number of words needed to hold the dynamic array - * word bits and allocate enough space for them. */ - size_t darr_words = (darr_word_size + BPWM1) / BPW; - s_vpi_vecval *darr_val = calloc(darr_words, sizeof(s_vpi_vecval)); - - /* Get the vector value. */ - s_vpi_value vec_val; - vec_val.format = vpiVectorVal; - vpi_get_value(vec, &vec_val); - - /* The offset in bits into the vector value. */ - unsigned offset = 0; - - /* We have to reverse the order of the dynamic array words. */ - for (int i = darr_size - 1; i >= 0; --i) { - unsigned bits_to_copy = darr_word_size; - s_vpi_vecval *darr_ptr = darr_val; - - /* Copy some of the vector bits to the current array word. */ - while (bits_to_copy > 0) { - unsigned copied_bits = (bits_to_copy > BPW) ? BPW : - bits_to_copy; - /* Start with the current vector part. */ - PLI_UINT32 aval = vec_val.value.vector[offset / BPW].aval; - PLI_UINT32 bval = vec_val.value.vector[offset / BPW].bval; - - /* If this isn't aligned then we may need to get bits - * from the next part as well. */ - unsigned rem_bits = offset % BPW; - if (rem_bits) { - aval >>= rem_bits; - aval |= vec_val.value.vector[offset / BPW + 1].aval << - (BPW - rem_bits); - bval >>= rem_bits; - bval |= vec_val.value.vector[offset / BPW + 1].bval << - (BPW - rem_bits); - } - - /* Advance to the next part of the array and vector. */ - darr_ptr->aval = aval; - darr_ptr->bval = bval; - darr_ptr++; - offset += copied_bits; - bits_to_copy -= copied_bits; - } - - /* Put part of the vector to the current dynamic array word. */ - s_vpi_value result; - result.format = vpiVectorVal; - result.value.vector = darr_val; - vpiHandle darr_word = vpi_handle_by_index(darr, i); - vpi_put_value(darr_word, &result, 0, vpiNoDelay); - } - - free(darr_val); - - return 0; -} - void sys_darray_register(void) { s_vpi_systf_data tf_data; @@ -392,25 +111,4 @@ tf_data.user_data = "$size"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - - tf_data.type = vpiSysTask; - tf_data.sysfunctype = 0; - tf_data.tfname = "$ivl_darray_method$to_vec"; - tf_data.calltf = to_vec_calltf; - tf_data.compiletf = to_from_vec_compiletf; - tf_data.sizetf = 0; - tf_data.user_data = "$ivl_darray_method$to_vec"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - - tf_data.type = vpiSysTask; - tf_data.sysfunctype = 0; - tf_data.tfname = "$ivl_darray_method$from_vec"; - tf_data.calltf = from_vec_calltf; - tf_data.compiletf = to_from_vec_compiletf; - tf_data.sizetf = 0; - tf_data.user_data = "$ivl_darray_method$from_vec"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); - } diff -Nru iverilog-10.3/vpi/sys_display.c iverilog-11.0/vpi/sys_display.c --- iverilog-10.3/vpi/sys_display.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_display.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2020 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 @@ -27,8 +27,6 @@ # include # include "ivl_alloc.h" -#define IS_MCD(mcd) !((mcd)>>31&1) - // Flag to enable better compatibility with other simulators static unsigned compatible_flag = 0; @@ -169,10 +167,11 @@ int default_format; switch(name[ strlen(name)-1 ]){ - /* writE/strobE or monitoR or displaY/fdisplaY or sformaT */ + /* writE/strobE or monitoR or displaY/fdisplaY or sformaT/sformatF */ case 'e': case 'r': case 't': + case 'f': case 'y': default_format = vpiDecStrVal; break; case 'h': default_format = vpiHexStrVal; break; case 'o': default_format = vpiOctStrVal; break; @@ -229,7 +228,7 @@ shift -= 1; } if (prec > 0) strcat(rtn, "."); - while(prec > 0) { + while (prec > 0) { strcat(rtn, "0"); prec -= 1; } @@ -620,10 +619,6 @@ case 't': case 'T': *idx += 1; - if (plus != 0) { - vpi_printf("WARNING: %s:%d: invalid format %s%s.\n", - info->filename, info->lineno, info->name, fmtb); - } if (*idx >= info->nitems) { vpi_printf("WARNING: %s:%d: missing argument for %s%s.\n", info->filename, info->lineno, info->name, fmtb); @@ -645,8 +640,35 @@ vpi_printf("WARNING: %s:%d: incompatible value for %s%s.\n", info->filename, info->lineno, info->name, fmtb); } else { - char *tbuf; + char *tbuf, *prev_suff = 0; PLI_INT32 time_units = vpi_get(vpiTimeUnit, info->scope); + + if (plus != 0) { + /* Icarus-specific extension to print out time units. + * It is needed by vhdlpp to correctly implement time'image(). */ + PLI_INT32 time_prec = vpi_get(vpiTimePrecision, info->scope); + + /* We are about to override the suffix string set with $timeformat(), + * therefore we need to restore it after the call. */ + prev_suff = timeformat_info.suff; + + if (time_units < -12) + timeformat_info.suff = strdup(" fs"); + else if (time_units < -9) + timeformat_info.suff = strdup(" ps"); + else if (time_units < -6) + timeformat_info.suff = strdup(" ns"); + else if (time_units < -3) + timeformat_info.suff = strdup(" us"); + else if (time_units < 0) + timeformat_info.suff = strdup(" ms"); + else + timeformat_info.suff = strdup(" s"); + + /* Adjust shift for get_time(), so the number indeed matches the unit */ + time_units += (3 + (time_units % 3)) % 3 + (time_prec - time_units); + } + unsigned swidth, free_flag = 0; unsigned suff_len = strlen(timeformat_info.suff); char *cp; @@ -667,6 +689,13 @@ cp = tbuf; swidth = strlen(tbuf); + if (plus != 0) { + /* Restore the previous suffix string, overridden by + * a vhdlpp-specific $sformatf() call. */ + free(timeformat_info.suff); + timeformat_info.suff = prev_suff; + } + if (ld_zero == 1) { /* No leading zeros are created by this conversion so just make * the width 0 for this case. */ @@ -955,10 +984,10 @@ value.format = vpiRealVal; vpi_get_value(item, &value); #if !defined(__GNUC__) - if(compatible_flag) + if (compatible_flag) sprintf(buf, "%g", value.value.real); else { - if(value.value.real == 0.0 || value.value.real == -0.0) + if (value.value.real == 0.0 || value.value.real == -0.0) sprintf(buf, "%.05f", value.value.real); else sprintf(buf, "%#g", value.value.real); @@ -1205,7 +1234,7 @@ callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); - if(name[1] == 'f') { + if (name[1] == 'f') { /* Check that there is a fd/mcd and that it is numeric. */ if (argv == 0) { vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), @@ -1238,7 +1267,8 @@ return sys_common_compiletf(name, 0, 0); } -/* This implements the $display/$fdisplay and the $write/$fwrite based tasks. */ +/* This implements the $sformatf, $display/$fdisplay + * and the $write/$fwrite based tasks. */ static PLI_INT32 sys_display_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) { vpiHandle callh, argv, scope; @@ -1246,37 +1276,24 @@ char* result; unsigned int size; PLI_UINT32 fd_mcd; + s_vpi_value val; callh = vpi_handle(vpiSysTfCall, 0); argv = vpi_iterate(vpiArgument, callh); /* Get the file/MC descriptor and verify it is valid. */ - if(name[1] == 'f') { - errno = 0; - vpiHandle arg = vpi_scan(argv); - s_vpi_value val; - val.format = vpiIntVal; - vpi_get_value(arg, &val); - fd_mcd = val.value.integer; - - /* If the MCD is zero we have nothing to do so just return. */ - if (fd_mcd == 0) { - vpi_free_object(argv); - return 0; - } - - if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) || - ( IS_MCD(fd_mcd) && my_mcd_printf(fd_mcd, "") == EOF)) { - 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", (unsigned int)fd_mcd, name); - errno = EBADF; - vpi_free_object(argv); - return 0; - } + if (name[1] == 'f') { + vpiHandle fd = vpi_scan(argv); + if (get_fd_mcd_from_arg(&fd_mcd, fd, callh, name)) { + vpi_free_object(argv); + return 0; + } + } else if (strncmp(name, "$sformatf", 9) == 0) { + /* return as a string */ + fd_mcd = 0; } else { - fd_mcd = 1; + /* stdout */ + fd_mcd = 1; } scope = vpi_handle(vpiScope, callh); @@ -1293,13 +1310,21 @@ /* Because %u and %z may put embedded NULL characters into the * returned string strlen() may not match the real size! */ result = get_display(&size, &info); - my_mcd_rawwrite(fd_mcd, result, size); - if ((strncmp(name,"$display",8) == 0) || - (strncmp(name,"$fdisplay",9) == 0)) my_mcd_rawwrite(fd_mcd, "\n", 1); + if (fd_mcd > 0) { + my_mcd_rawwrite(fd_mcd, result, size); + if ((strncmp(name,"$display",8) == 0) || + (strncmp(name,"$fdisplay",9) == 0)) my_mcd_rawwrite(fd_mcd, "\n", 1); + } else { + /* Return as a string ($sformatf) */ + val.format = vpiStringVal; + val.value.str = result; + vpi_put_value(callh, &val, 0, vpiNoDelay); + } + + free(result); free(info.filename); free(info.items); - free(result); return 0; } @@ -1317,8 +1342,7 @@ /* We really need to cancel any $fstrobe() calls for a file when it * is closed, but for now we will just skip processing the result. * Which has the same basic effect. */ - if ((! IS_MCD(info->fd_mcd) && vpi_get_file(info->fd_mcd) != NULL) || - ( IS_MCD(info->fd_mcd) && my_mcd_printf(info->fd_mcd, "") != EOF)) { + if (is_valid_fd_mcd(info->fd_mcd)) { char* result; unsigned int size; /* Because %u and %z may put embedded NULL characters into the @@ -1355,32 +1379,15 @@ argv = vpi_iterate(vpiArgument, callh); /* Get the file/MC descriptor and verify it is valid. */ - if(name[1] == 'f') { - errno = 0; - vpiHandle arg = vpi_scan(argv); - s_vpi_value val; - val.format = vpiIntVal; - vpi_get_value(arg, &val); - fd_mcd = val.value.integer; - - /* If the MCD is zero we have nothing to do so just return. */ - if (fd_mcd == 0) { - vpi_free_object(argv); - return 0; - } - - if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) || - ( IS_MCD(fd_mcd) && my_mcd_printf(fd_mcd, "") == EOF)) { - 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", (unsigned int)fd_mcd, name); - errno = EBADF; - vpi_free_object(argv); - return 0; - } + if (name[1] == 'f') { + vpiHandle fd = vpi_scan(argv); + if (get_fd_mcd_from_arg(&fd_mcd, fd, callh, name)) { + vpi_free_object(argv); + return 0; + } + } else { - fd_mcd = 1; + fd_mcd = 1; } scope = vpi_handle(vpiScope, callh); @@ -1408,6 +1415,7 @@ cb.value = 0; cb.user_data = (char*)info; vpi_register_cb(&cb); + return 0; } @@ -1665,7 +1673,7 @@ if (argv == 0) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s requires at least two argument.\n", name); + vpi_printf("%s requires at least two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } @@ -1686,7 +1694,7 @@ if (arg == 0) { vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); - vpi_printf("%s requires at least two argument.\n", name); + vpi_printf("%s requires at least two arguments.\n", name); vpi_control(vpiFinish, 1); return 0; } @@ -1754,6 +1762,46 @@ return 0; } +static PLI_INT32 sys_sformatf_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + PLI_INT32 type; + + /* Check that there are arguments. */ + if (argv == 0) { + vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires at least one argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The first argument must be a string, a register or a SV string. */ + arg = vpi_scan(argv); + if (arg == 0) { + vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s requires at least one argument.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + type = vpi_get(vpiType, arg); + if (((type != vpiConstant && type != vpiParameter) || + vpi_get(vpiConstType, arg) != vpiStringConst) && + type != vpiReg && type != vpiStringVar) { + vpi_printf("ERROR:%s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("%s's first argument must be a string or a register.\n", name); + vpi_control(vpiFinish, 1); + return 0; + } + + if (sys_check_args(callh, argv, name, 0, 0)) vpi_control(vpiFinish, 1); + return 0; +} + static PLI_INT32 sys_end_of_compile(p_cb_data cb_data) { (void)cb_data; /* Parameter is not used. */ @@ -2077,6 +2125,9 @@ free(dstr); if (strncmp(name,"$fatal",6) == 0) { + /* Set the exit code from vvp as an error code. */ + vpip_set_return_value(1); + /* Now tell the simulator to finish. */ vpi_control(vpiFinish, finish_number.value.integer); } @@ -2431,6 +2482,16 @@ res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiStringFunc; + tf_data.tfname = "$sformatf"; + tf_data.calltf = sys_display_calltf; + tf_data.compiletf = sys_sformatf_compiletf; + tf_data.sizetf = 0; + tf_data.user_data = "$sformatf"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + /*============================ timeformat */ tf_data.type = vpiSysTask; tf_data.tfname = "$timeformat"; diff -Nru iverilog-10.3/vpi/sys_fileio.c iverilog-11.0/vpi/sys_fileio.c --- iverilog-10.3/vpi/sys_fileio.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_fileio.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2020 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 @@ -26,7 +26,6 @@ # include # include "ivl_alloc.h" -#define IS_MCD(mcd) !((mcd)>>31&1) /* * Implement the $fopen system function. @@ -86,11 +85,11 @@ /* Get the mode handle if it exists. */ if (mode) { - char *esc_md; - val.format = vpiStringVal; - vpi_get_value(mode, &val); + 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)) { + if (val.format != vpiStringVal || !*(val.value.str)) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); @@ -146,7 +145,7 @@ } } - mode_string = strdup(val.value.str); + mode_string = strdup(val.value.str); vpi_free_object(argv); } @@ -212,32 +211,40 @@ 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; - 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, "%s", "") == 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", - (unsigned int)fd_mcd, name); - errno = EBADF; - return 0; - } + if (get_fd_mcd_from_arg(&fd_mcd, fd, callh, name)) 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); + fd_mcd = vpi_mcd_close(fd_mcd); + + if (fd_mcd) { + char *fd_mcd_name; + switch(fd_mcd) { + case 0x00000001: // MCD STDOUT + case 0x80000001: // FD STDOUT + fd_mcd_name = "STDOUT "; + break; + case 0x80000000: // FD STDIN + fd_mcd_name = "STDIN "; + break; + case 0x80000002: // FD STDERR + fd_mcd_name = "STDERR "; + break; + default: + fd_mcd_name = ""; + } + vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("could not close %s %s(0x%x) in %s().\n", + IS_MCD(fd_mcd) ? "MCD" : "file descriptor", + fd_mcd_name, (unsigned int)fd_mcd, name); + } return 0; } @@ -249,10 +256,8 @@ { vpiHandle callh = vpi_handle(vpiSysTfCall, 0); vpiHandle argv = vpi_iterate(vpiArgument, callh); - vpiHandle arg; - s_vpi_value val; + vpiHandle fd; PLI_UINT32 fd_mcd; - errno = 0; /* If we have no argument then flush all the streams. */ if (argv == 0) { @@ -261,32 +266,14 @@ } /* Get the file/MC descriptor and verify that it is valid. */ - arg = vpi_scan(argv); + fd = vpi_scan(argv); vpi_free_object(argv); - val.format = vpiIntVal; - vpi_get_value(arg, &val); - fd_mcd = val.value.integer; - - /* If the MCD is zero we have nothing to do so just return. */ - if (fd_mcd == 0) return 0; - - if ((! IS_MCD(fd_mcd) && vpi_get_file(fd_mcd) == NULL) || - ( IS_MCD(fd_mcd) && vpi_mcd_printf(fd_mcd, "%s", "") == 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", - (unsigned int)fd_mcd, name); - errno = EBADF; - return 0; - } + if (get_fd_mcd_from_arg(&fd_mcd, fd, callh, name)) return 0; if (IS_MCD(fd_mcd)) { vpi_mcd_flush(fd_mcd); } else { - /* If we have a valid file descriptor flush the file. */ - FILE *fp = vpi_get_file(fd_mcd); - if (fp) fflush(fp); + fflush(vpi_get_file(fd_mcd)); } return 0; @@ -652,7 +639,7 @@ val.format = vpiIntVal; vpi_get_value(arg, &val); count = val.value.integer; - if (count > max-start) { + if (count > max-start+1) { vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), (int)vpi_get(vpiLineNo, callh)); @@ -670,11 +657,11 @@ start = min; count = max - min + 1; } - width = vpi_get(vpiSize, vpi_handle_by_index(mem_reg, start)); + width = vpi_get(vpiSize, vpi_handle_by_index(mem_reg, start)); } else { start = 0; count = 1; - width = vpi_get(vpiSize, mem_reg); + width = vpi_get(vpiSize, mem_reg); vpi_free_object(argv); } @@ -883,12 +870,11 @@ #if defined(__GNUC__) val.value.integer = fseek(fp, offset, oper); #else - if(oper < 0) { - val.value.integer = EOF; - errno = EINVAL; - } - else - val.value.integer = fseek(fp, offset, oper); + if (oper < 0) { + val.value.integer = EOF; + errno = EINVAL; + } else + val.value.integer = fseek(fp, offset, oper); #endif vpi_put_value(callh, &val, 0 , vpiNoDelay); diff -Nru iverilog-10.3/vpi/sys_finish.c iverilog-11.0/vpi/sys_finish.c --- iverilog-10.3/vpi/sys_finish.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_finish.c 2020-09-26 22:44:25.000000000 +0000 @@ -42,6 +42,8 @@ return 0; } + vpip_set_return_value(0); + vpi_control(vpiFinish, diag_msg); return 0; } diff -Nru iverilog-10.3/vpi/sys_fst.c iverilog-11.0/vpi/sys_fst.c --- iverilog-10.3/vpi/sys_fst.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_fst.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -35,6 +35,8 @@ static char *dump_path = NULL; static struct fstContext *dump_file = NULL; +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + struct vcd_info { vpiHandle item; vpiHandle cb; @@ -182,6 +184,7 @@ if (!vcd_dmp_list) { cb = *cause; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -248,7 +251,6 @@ __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -259,8 +261,7 @@ return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; @@ -434,7 +435,7 @@ return 0; } - path = get_filename(callh, name, vpi_scan(argv)); + path = get_filename_with_suffix(callh, name, vpi_scan(argv), "fst"); vpi_free_object(argv); if (! path) return 0; diff -Nru iverilog-10.3/vpi/sys_lxt2.c iverilog-11.0/vpi/sys_lxt2.c --- iverilog-10.3/vpi/sys_lxt2.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_lxt2.c 2020-09-26 22:44:25.000000000 +0000 @@ -40,6 +40,8 @@ static void* lxt2_thread(void*arg); +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + /* * Manage a table of all the dump-enabled vcd item. The cells of this * table are allocated incrementally, but are released all at once, so @@ -105,7 +107,6 @@ */ # define VCD_INFO_ENDP ((struct vcd_info*)1) static struct vcd_info *vcd_dmp_list = VCD_INFO_ENDP; -static struct t_vpi_time vcd_dmp_time; static PLI_UINT64 vcd_cur_time = 0; static int dump_is_off = 0; @@ -302,9 +303,8 @@ } if (vcd_dmp_list == VCD_INFO_ENDP) { - vcd_dmp_time.type = vpiSuppressTime; cb = *cause; - cb.time = &vcd_dmp_time; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -358,7 +358,6 @@ __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -369,8 +368,7 @@ return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; @@ -545,7 +543,7 @@ return 0; } - path = get_filename(callh, name, vpi_scan(argv)); + path = get_filename_with_suffix(callh, name, vpi_scan(argv), "lx2"); vpi_free_object(argv); if (! path) return 0; diff -Nru iverilog-10.3/vpi/sys_lxt.c iverilog-11.0/vpi/sys_lxt.c --- iverilog-10.3/vpi/sys_lxt.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_lxt.c 2020-09-26 22:44:25.000000000 +0000 @@ -38,6 +38,8 @@ static char *dump_path = NULL; static struct lt_trace *dump_file = NULL; +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + struct vcd_info { vpiHandle item; vpiHandle cb; @@ -261,6 +263,7 @@ if (!vcd_dmp_list) { cb = *cause; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -320,7 +323,6 @@ __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -331,8 +333,7 @@ return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; @@ -489,7 +490,7 @@ return 0; } - path = get_filename(callh, name, vpi_scan(argv)); + path = get_filename_with_suffix(callh, name, vpi_scan(argv), "lxt"); vpi_free_object(argv); if (! path) return 0; diff -Nru iverilog-10.3/vpi/sys_priv.c iverilog-11.0/vpi/sys_priv.c --- iverilog-10.3/vpi/sys_priv.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_priv.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2003-2020 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 @@ -20,6 +20,7 @@ #include "sys_priv.h" #include #include +#include #include #include #include "ivl_alloc.h" @@ -103,6 +104,26 @@ return strdup(val.value.str); } +char* get_filename_with_suffix(vpiHandle callh, const char *name, vpiHandle file, const char*suff) +{ + char*path = get_filename(callh, name, file); + if (path == 0) return 0; + + /* If the name already has a suffix, then don't replace it or + add another suffix. Just return this path. */ + char*tailp = strrchr(path, '.'); + if (tailp != 0) return path; + + /* The name doesn't have a suffix, so append the passed in + suffix to the file name. */ + char*new_path = malloc(strlen(path) + strlen(suff) + 2); + strcpy(new_path, path); + strcat(new_path, "."); + strcat(new_path, suff); + free(path); + return new_path; +} + void check_for_extra_args(vpiHandle argv, vpiHandle callh, const char *name, const char *arg_str, unsigned opt) { @@ -229,6 +250,56 @@ /* + * Check if the file descriptor or MCD is valid. + * For a MCD check that every bit set is a valid file and + * for a FD make sure it exists. + */ +unsigned is_valid_fd_mcd(PLI_UINT32 fd_mcd) +{ + assert(fd_mcd); // Should already be handled! + + if (IS_MCD(fd_mcd)){ + if (vpi_mcd_printf(fd_mcd, "%s", "") == EOF) return 0; + } else { + if (vpi_get_file(fd_mcd) == NULL) return 0; + } + + return 1; +} + +/* + * Get a FD/MCD from the given argument and check if it is valid. Return the + * FD/MCD in the fd_mcd argument and return 0 if it is valid and 1 if it is + * invalid. We do not print a warning mesage if the FD/MCD is zero. + */ +unsigned get_fd_mcd_from_arg(PLI_UINT32 *fd_mcd, vpiHandle arg, + vpiHandle callh, const char *name) +{ + s_vpi_value val; + + val.format = vpiIntVal; + vpi_get_value(arg, &val); + *fd_mcd = val.value.integer; + + if (*fd_mcd == 0) return 1; + + errno = 0; + if (! is_valid_fd_mcd(*fd_mcd)) { + vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("invalid %s (0x%x) given to %s().\n", + IS_MCD(*fd_mcd) ? "MCD" : "file descriptor", + (unsigned int)*fd_mcd, + name); + errno = EBADF; + return 1; + } + + return 0; +} + + +/* * Find the enclosing module. If there is no enclosing module (which can be * the case in SystemVerilog), return the highest enclosing scope. */ @@ -399,3 +470,23 @@ return 0; } + +/* Return an integer value to the caller. */ +void put_integer_value(vpiHandle callh, PLI_INT32 result) +{ + s_vpi_value val; + + val.format = vpiIntVal; + val.value.integer = result; + vpi_put_value(callh, &val, 0, vpiNoDelay); +} + +/* Return a scalar value to the caller. */ +void put_scalar_value(vpiHandle callh, PLI_INT32 result) +{ + s_vpi_value val; + + val.format = vpiScalarVal; + val.value.scalar = result; + vpi_put_value(callh, &val, 0, vpiNoDelay); +} diff -Nru iverilog-10.3/vpi/sys_priv.h iverilog-11.0/vpi/sys_priv.h --- iverilog-10.3/vpi/sys_priv.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_priv.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_sys_priv_H #define IVL_sys_priv_H /* - * Copyright (c) 2002-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2020 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 @@ -22,6 +22,8 @@ #include "vpi_config.h" #include "sv_vpi_user.h" +#define IS_MCD(mcd) !((mcd)>>31&1) + /* * Context structure for PRNG in mt19937int.c */ @@ -37,6 +39,8 @@ extern char *as_escaped(char *arg); extern char *get_filename(vpiHandle callh, const char *name, vpiHandle file); +extern char *get_filename_with_suffix(vpiHandle callh, const char*name, + vpiHandle file, const char*suff); extern void check_for_extra_args(vpiHandle argv, vpiHandle callh, const char *name, const char *arg_str, unsigned opt); @@ -54,6 +58,10 @@ extern unsigned is_numeric_obj(vpiHandle obj); extern unsigned is_string_obj(vpiHandle obj); +extern unsigned is_valid_fd_mcd(PLI_UINT32 fd_mcd); +extern unsigned get_fd_mcd_from_arg(PLI_UINT32 *fd_mcd, vpiHandle arg, + vpiHandle callh, const char *name); + extern vpiHandle sys_func_module(vpiHandle obj); /* @@ -65,4 +73,10 @@ extern PLI_INT32 sys_two_numeric_args_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); extern PLI_INT32 sys_one_string_arg_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name); +/* + * The standard put/return a value to the caller routines. + */ +extern void put_integer_value(vpiHandle callh, PLI_INT32 result); +extern void put_scalar_value(vpiHandle callh, PLI_INT32 result); + #endif /* IVL_sys_priv_H */ diff -Nru iverilog-10.3/vpi/sys_readmem.c iverilog-11.0/vpi/sys_readmem.c --- iverilog-10.3/vpi/sys_readmem.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_readmem.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2019 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 @@ -332,9 +332,9 @@ /* Configure the readmem lexer */ if (strcmp(name,"$readmemb") == 0) - sys_readmem_start_file(file, 1, wwid, value.value.vector); + sys_readmem_start_file(callh, file, 1, wwid, value.value.vector); else - sys_readmem_start_file(file, 0, wwid, value.value.vector); + sys_readmem_start_file(callh, file, 0, wwid, value.value.vector); /*======================================== Read memory file */ diff -Nru iverilog-10.3/vpi/sys_readmem_lex.h iverilog-11.0/vpi/sys_readmem_lex.h --- iverilog-10.3/vpi/sys_readmem_lex.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_readmem_lex.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_sys_readmem_lex_H #define IVL_sys_readmem_lex_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2014,2019 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 @@ -28,7 +28,7 @@ extern char *readmem_error_token; -extern void sys_readmem_start_file(FILE*in, int bin_flag, +extern void sys_readmem_start_file(vpiHandle callh, FILE*in, int bin_flag, unsigned width, struct t_vpi_vecval*val); extern int readmemlex(void); diff -Nru iverilog-10.3/vpi/sys_readmem_lex.lex iverilog-11.0/vpi/sys_readmem_lex.lex --- iverilog-10.3/vpi/sys_readmem_lex.lex 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_readmem_lex.lex 2020-09-26 22:44:25.000000000 +0000 @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 1999-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2017,2019 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 @@ -57,6 +57,12 @@ . { readmem_error_token = yytext; return MEM_ERROR; } %% + /* The call_handle is the handle for the call to the $readmem() + system task. This is used for adding line numbers to warnings and + error messages. */ +static vpiHandle call_handle = 0; +static int too_many_digits_warning = 0; + static unsigned word_width = 0; static struct t_vpi_vecval*vecval = 0; @@ -134,6 +140,26 @@ word_max -= 32; } } + + /* If there are more text digits then needed to fill the + memory word, count those digits and print a warning + message. Print that warning only once per call to + $readmemh() so that the user isn't flooded. */ + int count_extra_digits = 0; + while (end > beg) { + end -= 1; + if (*end == '_') continue; + count_extra_digits += 1; + } + + if (count_extra_digits && too_many_digits_warning==0) { + vpi_printf("WARNING: %s:%d: Excess hex digits (%d of '%s') while reading %d-bit words.\n", + vpi_get_str(vpiFile, call_handle), + vpi_get(vpiLineNo, call_handle), + count_extra_digits, beg, + word_width); + too_many_digits_warning += 1; + } } static void make_bin_value(void) @@ -181,11 +207,33 @@ word_max -= 32; } } + + /* If there are more text digits then needed to fill the + memory word, count those digits and print a warning + message. Print that warning only once per call to + $readmem() so that the user isn't flooded. */ + int count_extra_digits = 0; + while (end > beg) { + end -= 1; + if (*end == '_') continue; + count_extra_digits += 1; + } + + if (count_extra_digits && too_many_digits_warning==0) { + vpi_printf("WARNING: %s:%d: Excess binary digits (%d of '%s') while reading %d-bit words.\n", + vpi_get_str(vpiFile, call_handle), + vpi_get(vpiLineNo, call_handle), + count_extra_digits, beg, + word_width); + too_many_digits_warning += 1; + } } -void sys_readmem_start_file(FILE*in, int bin_flag, +void sys_readmem_start_file(vpiHandle callh, FILE*in, int bin_flag, unsigned width, struct t_vpi_vecval *vv) { + call_handle = callh; + too_many_digits_warning = 0; yyrestart(in); BEGIN(bin_flag? BIN : HEX); word_width = width; diff -Nru iverilog-10.3/vpi/sys_sdf.c iverilog-11.0/vpi/sys_sdf.c --- iverilog-10.3/vpi/sys_sdf.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_sdf.c 2020-09-26 22:44:25.000000000 +0000 @@ -186,7 +186,7 @@ delays.time_type = vpiScaledRealTime; delays.mtm_flag = 0; delays.append_flag = 0; - delays.plusere_flag = 0; + delays.pulsere_flag = 0; vpi_get_delays(path, &delays); for (idx = 0 ; idx < delval_list->count ; idx += 1) { diff -Nru iverilog-10.3/vpi/sys_table.c iverilog-11.0/vpi/sys_table.c --- iverilog-10.3/vpi/sys_table.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_table.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2010,2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2018 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 diff -Nru iverilog-10.3/vpi/system.sft iverilog-11.0/vpi/system.sft --- iverilog-10.3/vpi/system.sft 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/system.sft 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# -# This is the system function descriptor table for the -# builtin (system) functions. -# - -$random vpiSysFuncInt -$rtoi vpiSysFuncInt -$urandom vpiSysFuncSized 32 unsigned -$urandom_range vpiSysFuncSized 32 unsigned -$dist_uniform vpiSysFuncInt -$dist_normal vpiSysFuncInt -$dist_exponential vpiSysFuncInt -$dist_poisson vpiSysFuncInt -$dist_chi_square vpiSysFuncInt -$dist_t vpiSysFuncInt -$dist_erlang vpiSysFuncInt -$clog2 vpiSysFuncInt -$q_full vpiSysFuncInt - -$abstime vpiSysFuncReal -$simparam vpiSysFuncReal -$simparam$str vpiSysFuncSized 1024 unsigned -$table_model vpiSysFuncReal - -$ivl_string_method$len vpiSysFuncInt -$ivl_string_method$to_vec vpiSysFuncVoid diff -Nru iverilog-10.3/vpi/sys_vcd.c iverilog-11.0/vpi/sys_vcd.c --- iverilog-10.3/vpi/sys_vcd.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/sys_vcd.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 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 @@ -34,6 +34,8 @@ static char *dump_path = NULL; static FILE *dump_file = NULL; +static struct t_vpi_time zero_delay = { vpiSimTime, 0, 0, 0.0 }; + struct vcd_info { vpiHandle item; vpiHandle cb; @@ -216,6 +218,7 @@ if (!vcd_dmp_list) { cb = *cause; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = variable_cb_2; vpi_register_cb(&cb); @@ -283,7 +286,6 @@ __inline__ static int install_dumpvars_callback(void) { struct t_cb_data cb; - static struct t_vpi_time now; if (dumpvars_status == 1) return 0; @@ -294,8 +296,7 @@ return 1; } - now.type = vpiSimTime; - cb.time = &now; + cb.time = &zero_delay; cb.reason = cbReadOnlySynch; cb.cb_rtn = dumpvars_cb; cb.user_data = 0x0; @@ -466,7 +467,7 @@ return 0; } - path = get_filename(callh, name, vpi_scan(argv)); + path = get_filename_with_suffix(callh, name, vpi_scan(argv), "vcd"); vpi_free_object(argv); if (! path) return 0; diff -Nru iverilog-10.3/vpi/v2005_math.sft iverilog-11.0/vpi/v2005_math.sft --- iverilog-10.3/vpi/v2005_math.sft 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/v2005_math.sft 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -# -# This is the system function descriptor table for the -# Verilog-2005 math functions. -# - -# Single argument functions. -$sqrt vpiSysFuncReal -$ln vpiSysFuncReal -$log10 vpiSysFuncReal -$exp vpiSysFuncReal -$ceil vpiSysFuncReal -$floor vpiSysFuncReal -$sin vpiSysFuncReal -$cos vpiSysFuncReal -$tan vpiSysFuncReal -$asin vpiSysFuncReal -$acos vpiSysFuncReal -$atan vpiSysFuncReal -$sinh vpiSysFuncReal -$cosh vpiSysFuncReal -$tanh vpiSysFuncReal -$asinh vpiSysFuncReal -$acosh vpiSysFuncReal -$atanh vpiSysFuncReal -# Double argument functions. -$pow vpiSysFuncReal -$atan2 vpiSysFuncReal -$hypot vpiSysFuncReal diff -Nru iverilog-10.3/vpi/v2009_bitvec.c iverilog-11.0/vpi/v2009_bitvec.c --- iverilog-10.3/vpi/v2009_bitvec.c 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/vpi/v2009_bitvec.c 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2018 Cary R. (cygcary@yahoo.com) + * + * This program is free software; you can redistribute it and/or modify + * it 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 +#include "vpi_user.h" +#include "sys_priv.h" + +/* + * Check that $couintbits() is called with the correct arguments. + */ +static PLI_INT32 countbits_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv, arg; + int cb_count = 1; + + assert(callh != 0); + argv = vpi_iterate(vpiArgument, callh); + (void)name; /* Parameter is not used. */ + + /* $countbits() must have arguments. */ + if (argv == 0) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$countbits() requires at least two arguments.\n"); + vpi_control(vpiFinish, 1); + return 0; + } + + /* The 1st argument must be numeric. */ + arg = vpi_scan(argv); + if (! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("The first argument to $countbits() must be numeric.\n"); + vpi_control(vpiFinish, 1); + } + + /* We need one or more numeric control bit arguments. */ + arg = vpi_scan(argv); + if (! arg) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("$countbits() requires at least one control bit " + "argument.\n"); + vpi_control(vpiFinish, 1); + } + + do { + if (arg && ! is_numeric_obj(arg)) { + vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Control bit argument %d to $countbits() must " + "be numeric.\n", cb_count); + vpi_control(vpiFinish, 1); + } + ++cb_count; + if (arg) arg = vpi_scan(argv); + } while (arg); + + return 0; +} + +/* Count the number of bits in the expression that match the search bits. */ +static PLI_INT32 count_bits_in_expr(vpiHandle expr_arg, char search[4]) +{ + s_vpi_value val; + PLI_INT32 result; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + result = 0; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + unsigned offset = lp / 32; + unsigned bit = lp % 32; + unsigned abit, bbit; + abit = (val.value.vector[offset].aval >> bit) & 0x1; + bbit = (val.value.vector[offset].bval >> bit) & 0x1; + if (search[(bbit<<1)|abit]) ++result; + } + + return result; +} + +static PLI_INT32 countbits_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + vpiHandle arg; + char search[4]; + (void)name; /* Parameter is not used. */ + + /* Scan the control bit arguments and mark which control bits to + * include in the count. */ + for (unsigned lp = 0; lp < 4 ; ++lp) search[lp] = 0; + while ((arg = vpi_scan(argv))) { + s_vpi_value val; + val.format = vpiScalarVal; + vpi_get_value(arg, &val); + switch (val.value.scalar) { + case vpi0: + search[0] = 1; + break; + case vpi1: + search[1] = 1; + break; + case vpiZ: + search[2] = 1; + break; + case vpiX: + search[3] = 1; + break; + default: + vpi_printf("WARNING: %s:%d: ", vpi_get_str(vpiFile, callh), + (int)vpi_get(vpiLineNo, callh)); + vpi_printf("Unknown scalar control bit argument %d passed " + "to $countbits() will be ignored.\n", + val.value.scalar); + break; + } + } + + put_integer_value(callh, count_bits_in_expr(expr_arg, search)); + + return 0; +} + +/* Count the number of ones in the expression. */ +static PLI_INT32 count_ones_in_expr(vpiHandle expr_arg) +{ + s_vpi_value val; + PLI_INT32 result; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + result = 0; + size = (size + 31) / 32; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + PLI_UINT32 ones = ~val.value.vector[lp].bval & + val.value.vector[lp].aval; + while (ones) { + if (ones & 0x1) ++result; + ones >>= 1; + } + } + + return result; +} + +static PLI_INT32 countones_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_integer_value(callh, count_ones_in_expr(expr_arg)); + + return 0; +} + +/* Check to see if the expression is onehot. */ +static PLI_INT32 is_onehot(vpiHandle expr_arg, unsigned zero_is_okay) +{ + s_vpi_value val; + unsigned found_a_one; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + found_a_one = 0; + size = (size + 31) / 32; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + PLI_UINT32 ones = ~val.value.vector[lp].bval & + val.value.vector[lp].aval; + while (ones) { + if (ones & 0x1) { + if (found_a_one) return vpi0; + found_a_one = 1; + } + ones >>= 1; + } + } + + if (found_a_one) return vpi1; + else if (zero_is_okay) return vpi1; + return vpi0; +} + +static PLI_INT32 onehot_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_scalar_value(callh, is_onehot(expr_arg, 0)); + + return 0; +} + +static PLI_INT32 onehot0_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_scalar_value(callh, is_onehot(expr_arg, 1)); + + return 0; +} + +/* Check to see if the expression has an undefined value. */ +static PLI_INT32 is_unknown(vpiHandle expr_arg) +{ + s_vpi_value val; + PLI_INT32 size = vpi_get(vpiSize, expr_arg); + assert(size > 0); + + val.format = vpiVectorVal; + vpi_get_value(expr_arg, &val); + + size = (size + 31) / 32; + for (unsigned lp = 0; lp < (unsigned)size; ++lp) { + if (val.value.vector[lp].bval) return vpi1; + } + + return vpi0; +} + +static PLI_INT32 isunknown_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle expr_arg = vpi_scan(argv); + (void)name; /* Parameter is not used. */ + + vpi_free_object(argv); + + put_scalar_value(callh, is_unknown(expr_arg)); + + return 0; +} + +static PLI_INT32 bit_vec_sizetf(ICARUS_VPI_CONST PLI_BYTE8 *name) +{ + (void)name; /* Parameter is not used. */ + + return 1; +} + +/* + * Register the functions with Verilog. + */ +void v2009_bitvec_register(void) +{ + s_vpi_systf_data tf_data; + vpiHandle res; + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.calltf = countbits_calltf; + tf_data.compiletf = countbits_compiletf; + tf_data.sizetf = 0; + tf_data.tfname = "$countbits"; + tf_data.user_data = 0; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiIntFunc; + tf_data.calltf = countones_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = 0; + tf_data.tfname = "$countones"; + tf_data.user_data = "$countones"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.calltf = onehot_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = bit_vec_sizetf; + tf_data.tfname = "$onehot"; + tf_data.user_data = "$onehot"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.calltf = onehot0_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = bit_vec_sizetf; + tf_data.tfname = "$onehot0"; + tf_data.user_data = "$onehot0"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiSizedFunc; + tf_data.calltf = isunknown_calltf; + tf_data.compiletf = sys_one_numeric_arg_compiletf; + tf_data.sizetf = bit_vec_sizetf; + tf_data.tfname = "$isunknown"; + tf_data.user_data = "$isunknown"; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); +} diff -Nru iverilog-10.3/vpi/v2009_enum.c iverilog-11.0/vpi/v2009_enum.c --- iverilog-10.3/vpi/v2009_enum.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/v2009_enum.c 2020-09-26 22:44:25.000000000 +0000 @@ -505,6 +505,7 @@ vpiHandle res; tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiOtherFunc; tf_data.calltf = ivl_enum_method_name_calltf; tf_data.compiletf = ivl_enum_method_name_compiletf; tf_data.sizetf = 0; @@ -515,6 +516,7 @@ vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiOtherFunc; tf_data.calltf = ivl_enum_method_next_prev_calltf; tf_data.compiletf = ivl_enum_method_next_prev_compiletf; tf_data.sizetf = 0; @@ -525,6 +527,7 @@ vpip_make_systf_system_defined(res); tf_data.type = vpiSysFunc; + tf_data.sysfunctype = vpiOtherFunc; tf_data.calltf = ivl_enum_method_next_prev_calltf; tf_data.compiletf = ivl_enum_method_next_prev_compiletf; tf_data.sizetf = 0; diff -Nru iverilog-10.3/vpi/v2009.sft iverilog-11.0/vpi/v2009.sft --- iverilog-10.3/vpi/v2009.sft 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/v2009.sft 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -# -# This is the system function descriptor table for the -# builtin (SystemVerilog) functions. -# - -$dimensions vpiSysFuncInt -$unpacked_dimensions vpiSysFuncInt -$left vpiSysFuncInt -$right vpiSysFuncInt -$low vpiSysFuncInt -$high vpiSysFuncInt -$increment vpiSysFuncInt -$size vpiSysFuncInt - -$ivl_array_method$to_vec vpiSysFuncVoid -$ivl_array_method$from_vec vpiSysFuncVoid diff -Nru iverilog-10.3/vpi/v2009_string.c iverilog-11.0/vpi/v2009_string.c --- iverilog-10.3/vpi/v2009_string.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/v2009_string.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2019 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 @@ -78,120 +78,6 @@ return 0; } - -static PLI_INT32 to_vec_compiletf(ICARUS_VPI_CONST PLI_BYTE8*user_data) -{ - (void) user_data; /* Parameter is not used. */ - - vpiHandle systf_handle, arg_iterator, arg_handle; - PLI_INT32 arg_type[2]; - - /* obtain a handle to the system task instance */ - systf_handle = vpi_handle(vpiSysTfCall, NULL); - if (systf_handle == NULL) { - vpi_printf("ERROR: $ivl_string_method$to_vec failed to obtain systf handle\n"); - vpi_control(vpiFinish,0); /* abort simulation */ - return 0; - } - - /* obtain handles to system task arguments */ - arg_iterator = vpi_iterate(vpiArgument, systf_handle); - if (arg_iterator == NULL) { - vpi_printf("ERROR: $ivl_string_method$to_vec requires 2 arguments\n"); - vpi_control(vpiFinish, 0); - return 0; - } - - /* check the type of object in system task arguments */ - arg_handle = vpi_scan(arg_iterator); - for(int i = 0; i < 2; ++i) { - arg_type[i] = vpi_get(vpiType, arg_handle); - arg_handle = vpi_scan(arg_iterator); - } - - if (arg_handle != NULL) { /* are there more arguments? */ - vpi_printf("ERROR: $ivl_string_method$to_vec can only have 2 arguments\n"); - vpi_free_object(arg_iterator); - vpi_control(vpiFinish, 0); - return 0; - } - - if ((arg_type[0] != vpiStringVar) || - (arg_type[1] != vpiNet && arg_type[1] != vpiReg)) { - vpi_printf("ERROR: $ivl_string_method$to_vec value arguments must be a string and a net or reg\n"); - vpi_free_object(arg_iterator); - vpi_control(vpiFinish, 0); - return 0; - } - - return 0; -} - -static PLI_INT32 to_vec_calltf(ICARUS_VPI_CONST PLI_BYTE8*name) -{ - (void)name; /* Parameter is not used. */ - - vpiHandle callh = vpi_handle(vpiSysTfCall, 0); - vpiHandle argv, str, vec; - s_vpi_value str_val; - s_vpi_vecval*vec_val; - - /* Fetch arguments */ - argv = vpi_iterate(vpiArgument, callh); - assert(argv); - str = vpi_scan(argv); - assert(str); - vec = vpi_scan(argv); - assert(vec); - vpi_free_object(argv); - - int str_size = vpi_get(vpiSize, str); - int vec_size = vpi_get(vpiSize, vec); - if(str_size <= 0) { - vpi_printf("ERROR: Cannot cast empty string"); - vpi_control(vpiFinish, 0); - return 0; - } - - if(vec_size != str_size * 8) { - vpi_printf("ERROR: String and vector size do not match"); - vpi_control(vpiFinish, 0); - return 0; - } - - str_val.format = vpiStringVal; - vpi_get_value(str, &str_val); - assert(str_val.value.str); - - /* Conversion part */ - int vec_number = ceil((double)str_size / sizeof(PLI_INT32)); - vec_val = calloc(vec_number, sizeof(s_vpi_vecval)); - PLI_BYTE8*str_ptr = &str_val.value.str[str_size - 1]; - - /* We have to reverse the order of string, no memcpy here */ - for(int i = 0; i < vec_number; ++i) { - int copy_size = str_size > (int)sizeof(PLI_INT32) ? - (int)sizeof(PLI_INT32) : str_size; - - /* Clear the part responsible for X & Z values */ - memset(&vec_val[i].bval, 0x00, sizeof(PLI_INT32)); - PLI_BYTE8*dest = (PLI_BYTE8*)&vec_val[i].aval; - - for(int j = 0; j < copy_size; ++j) - *dest++ = *str_ptr--; - - str_size -= copy_size; - } - - str_val.format = vpiVectorVal; - str_val.value.vector = vec_val; - vpi_put_value(vec, &str_val, 0, vpiNoDelay); - - free(vec_val); - - return 0; -} - void v2009_string_register(void) { s_vpi_systf_data tf_data; @@ -206,14 +92,4 @@ tf_data.user_data = "$ivl_string_method$len"; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - - tf_data.type = vpiSysTask; - tf_data.sysfunctype = 0; - tf_data.tfname = "$ivl_string_method$to_vec"; - tf_data.calltf = to_vec_calltf; - tf_data.compiletf = to_vec_compiletf; - tf_data.sizetf = 0; - tf_data.user_data = "$ivl_string_method$to_vec"; - res = vpi_register_systf(&tf_data); - vpip_make_systf_system_defined(res); } diff -Nru iverilog-10.3/vpi/v2009_table.c iverilog-11.0/vpi/v2009_table.c --- iverilog-10.3/vpi/v2009_table.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/v2009_table.c 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2010-2019 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 @@ -17,12 +17,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +# include "vpi_user.h" + extern void v2009_array_register(void); +extern void v2009_bitvec_register(void); extern void v2009_enum_register(void); extern void v2009_string_register(void); void (*vlog_startup_routines[])(void) = { v2009_array_register, + v2009_bitvec_register, v2009_enum_register, v2009_string_register, 0 diff -Nru iverilog-10.3/vpi/va_math.sft iverilog-11.0/vpi/va_math.sft --- iverilog-10.3/vpi/va_math.sft 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/va_math.sft 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -# -# This is the system function descriptor table for the -# Verilog-A math functions. -# - -# Single argument functions. -$abs vpiSysFuncReal -# Double argument functions. -$min vpiSysFuncReal -$max vpiSysFuncReal diff -Nru iverilog-10.3/vpi/vhdl_sys.sft iverilog-11.0/vpi/vhdl_sys.sft --- iverilog-10.3/vpi/vhdl_sys.sft 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/vhdl_sys.sft 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -$ivlh_attribute_event vpiSysFuncSized 1 unsigned -$ivlh_rising_edge vpiSysFuncSized 1 unsigned -$ivlh_falling_edge vpiSysFuncSized 1 unsigned diff -Nru iverilog-10.3/vpi/vhdl_table.c iverilog-11.0/vpi/vhdl_table.c --- iverilog-10.3/vpi/vhdl_table.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/vhdl_table.c 2020-09-26 22:44:25.000000000 +0000 @@ -21,6 +21,7 @@ # include "vpi_user.h" # include # include "ivl_alloc.h" +# include "sys_priv.h" /* * The $ivlh_attribute_event implements the VHDL 'event @@ -40,11 +41,24 @@ static struct monitor_data **mdata = 0; static unsigned mdata_count = 0; -typedef enum { EVENT = 0, RISING_EDGE = 1, FALLING_EDGE = 2 } event_type_t; -static const char* func_names[] = { - "$ivlh_attribute_event", - "$ivlh_rising_edge", - "$ivlh_falling_edge" +typedef enum { + EVENT = 0, + RISING_EDGE = 1, + FALLING_EDGE = 2 +} event_type_t; +static const char* attr_func_names[] = { + "$ivlh_attribute_event", + "$ivlh_rising_edge", + "$ivlh_falling_edge" +}; + +typedef enum { + SHIFT_LEFT = 0, + SHIFT_RIGHT = 1, +} shift_type_t; +static const char* shift_func_names[] = { + "$ivlh_shift_left", + "$ivlh_shift_right", }; /* To keep valgrind happy free the allocated memory. */ @@ -93,7 +107,7 @@ vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys), (int)vpi_get(vpiLineNo, sys)); vpi_printf("(compiler error) %s requires a single argument.\n", - func_names[type]); + attr_func_names[type]); vpi_control(vpiFinish, 1); return 0; } @@ -127,7 +141,7 @@ vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys), (int)vpi_get(vpiLineNo, sys)); vpi_printf("(compiler error) %s only takes a single argument.\n", - func_names[type]); + attr_func_names[type]); vpi_free_object(argv); vpi_control(vpiFinish, 1); } @@ -180,39 +194,83 @@ return 1; } +static PLI_INT32 ivlh_shift_calltf(ICARUS_VPI_CONST PLI_BYTE8*data) +{ + shift_type_t shift_type = (shift_type_t) data; + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle argh = vpi_scan(argv); + vpiHandle counth = vpi_scan(argv); + s_vpi_value val; + PLI_INT32 count; + + vpi_free_object(argv); + + val.format = vpiIntVal; + vpi_get_value(counth, &val); + count = val.value.integer; + + val.format = vpiIntVal; + vpi_get_value(argh, &val); + + if(shift_type == SHIFT_LEFT) + val.value.integer <<= count; + else if(shift_type == SHIFT_RIGHT) + val.value.integer >>= count; + else + assert(0); + + vpi_put_value(callh, &val, 0, vpiNoDelay); + + return 0; +} + +static PLI_INT32 ivlh_shift_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type) +{ + (void) type; /* Parameter is not used. */ + return 32; +} + static void vhdl_register(void) { s_vpi_systf_data tf_data; s_cb_data cb; vpiHandle res; + /* Event attribute functions */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; tf_data.calltf = ivlh_attribute_event_calltf; tf_data.compiletf = ivlh_attribute_event_compiletf; tf_data.sizetf = ivlh_attribute_event_sizetf; - tf_data.tfname = func_names[EVENT]; + tf_data.tfname = attr_func_names[EVENT]; tf_data.user_data = (PLI_BYTE8*) EVENT; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); - tf_data.type = vpiSysFunc; - tf_data.sysfunctype = vpiSizedFunc; - tf_data.calltf = ivlh_attribute_event_calltf; - tf_data.compiletf = ivlh_attribute_event_compiletf; - tf_data.sizetf = ivlh_attribute_event_sizetf; - tf_data.tfname = func_names[RISING_EDGE]; + tf_data.tfname = attr_func_names[RISING_EDGE]; tf_data.user_data = (PLI_BYTE8*) RISING_EDGE; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); + tf_data.tfname = attr_func_names[FALLING_EDGE]; + tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + /* Shift functions */ tf_data.type = vpiSysFunc; tf_data.sysfunctype = vpiSizedFunc; - tf_data.calltf = ivlh_attribute_event_calltf; - tf_data.compiletf = ivlh_attribute_event_compiletf; - tf_data.sizetf = ivlh_attribute_event_sizetf; - tf_data.tfname = func_names[FALLING_EDGE]; - tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE; + tf_data.calltf = ivlh_shift_calltf; + tf_data.compiletf = sys_two_numeric_args_compiletf; + tf_data.sizetf = ivlh_shift_sizetf; + tf_data.tfname = shift_func_names[SHIFT_LEFT]; + tf_data.user_data = (PLI_BYTE8*) SHIFT_LEFT; + res = vpi_register_systf(&tf_data); + vpip_make_systf_system_defined(res); + + tf_data.tfname = shift_func_names[SHIFT_RIGHT]; + tf_data.user_data = (PLI_BYTE8*) SHIFT_RIGHT; res = vpi_register_systf(&tf_data); vpip_make_systf_system_defined(res); diff -Nru iverilog-10.3/vpi/vhdl_textio.c iverilog-11.0/vpi/vhdl_textio.c --- iverilog-10.3/vpi/vhdl_textio.c 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/vpi/vhdl_textio.c 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,1018 @@ +/* + * 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 +# 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 }; +enum file_open_status_t { FS_OPEN_OK, FS_STATUS_ERROR, FS_NAME_ERROR, FS_MODE_ERROR }; + +/* bits per vector, in a single s_vpi_vecval struct */ +static const size_t BPW = 8 * sizeof(PLI_INT32); + +/* string buffer size */ +static const size_t STRING_BUF_SIZE = 1024; + +static int is_integer_var(vpiHandle obj) +{ + PLI_INT32 type = vpi_get(vpiType, obj); + + return (type == vpiIntegerVar || type == vpiShortIntVar || + type == vpiIntVar || type == vpiLongIntVar); +} + +static int is_const(vpiHandle obj) +{ + return vpi_get(vpiType, obj) == vpiConstant; +} + +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(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': + case 'Z': + v->bval |= (1 << bit); + v->aval &= ~(1 << bit); + break; + + case 'x': + 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, "%" PLI_UINT64_FMT " %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, int count) { + char buf[STRING_BUF_SIZE]; + int processed_chars; + char format_str[32]; + + /* No string length limit imposed */ + if(count <= 0 || count >= (int)STRING_BUF_SIZE) + count = STRING_BUF_SIZE - 1; + + snprintf(format_str, 32, "%%%ds%%n", count); + + if(sscanf(string, format_str, buf, &processed_chars) != 1) + return 0; + + val->format = vpiStringVal; + val->value.str = strdup(buf); + + 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, "%" PLI_UINT64_FMT " %cs", period, prefix); + else + sprintf(string, "%" PLI_UINT64_FMT " 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, arg; + assert(callh != 0); + int ok = 1; + + argv = vpi_iterate(vpiArgument, callh); + + /* Check that there is a file name argument and that it is a string. */ + if (argv == 0) + ok = 0; + + arg = vpi_scan(argv); + if (!arg || !is_integer_var(arg)) + ok = 0; + + arg = vpi_scan(argv); + if (arg && is_integer_var(arg)) + arg = vpi_scan(argv); + + // no vpi_scan() here, if we had both 'status' and 'file' arguments, + // then the next arg is read in the above if, otherwise we are going + // to check the second argument once again + if (!arg || !is_string_obj(arg)) + ok = 0; + + arg = vpi_scan(argv); + if (arg && !is_const(arg)) + ok = 0; + + if (!ok) { + show_error_line(callh); + vpi_printf("%s() function is available in following variants:\n", name); + vpi_printf("* (file f: text; filename: in string, file_open_kind: in mode)\n"); + vpi_printf("* (status: out file_open_status, file f: text; filename: in string, file_open_kind: in mode)\n"); + vpi_control(vpiFinish, 1); + } + + /* Make sure there are no extra arguments. */ + check_for_extra_args(argv, callh, name, "four 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 fstatush = vpi_scan(argv); + vpiHandle fhandleh = vpi_scan(argv); + vpiHandle fnameh = vpi_scan(argv); + vpiHandle modeh = vpi_scan(argv); + + if(!modeh) { + /* There are only three arguments, so rearrange handles */ + modeh = fnameh; + fnameh = fhandleh; + fhandleh = fstatush; + fstatush = 0; + } else { + vpi_free_object(argv); + } + + /* Get the mode handle */ + val.format = vpiIntVal; + vpi_get_value(modeh, &val); + mode = val.value.integer; + + 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 */ + PLI_INT32 result = -1; + switch(mode) { + case FILE_MODE_READ: + result = vpi_fopen(fname, "r"); + break; + + case FILE_MODE_WRITE: + result = vpi_fopen(fname, "w"); + break; + + case FILE_MODE_APPEND: + result = vpi_fopen(fname, "a"); + break; + } + + if(fstatush) { + val.format = vpiIntVal; + + if(!result) { + switch(errno) { + case ENOENT: + case ENAMETOOLONG: + val.value.integer = FS_NAME_ERROR; + break; + + case EINVAL: + case EACCES: + case EEXIST: + case EISDIR: + val.value.integer = FS_MODE_ERROR; + break; + + default: + val.value.integer = FS_STATUS_ERROR; + break; + } + } else { + val.value.integer = FS_OPEN_OK; + } + + vpi_put_value(fstatush, &val, 0, vpiNoDelay); + } + + val.format = vpiIntVal; + val.value.integer = result; + 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; + char buf[STRING_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, STRING_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 == STRING_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); + } + + /* Remove the newline character(s) */ + while (len > 0 && (buf[len-1] == '\n' || buf[len-1] == '\r')) { + buf[len-1] = 0; + len--; + } + /* Return the characters to the register. */ + text = strdup(buf); + val.format = vpiStringVal; + val.value.str = text; + vpi_put_value(stringh, &val, 0, vpiNoDelay); + free(text); + + /* Set end-of-file flag if we have just reached the end of the file. + * Otherwise the flag would be set only after the next read operation. */ + int c = fgetc(fp); + ungetc(c, fp); + + 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, dest_size; + char *string = 0; + unsigned int processed_chars = 0, fail = 0; + + /* Get the string */ + stringh = vpi_scan(argv); + val.format = vpiStringVal; + vpi_get_value(stringh, &val); + + 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 destination variable */ + varh = vpi_scan(argv); + type = vpi_get(vpiType, varh); + dest_size = vpi_get(vpiSize, 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); + + 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, dest_size / 8); + 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, dest_size / 8); + 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 == STRING_BUF_SIZE) { + 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 == vpiStringVal) + 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; + unsigned int fail = 0, res = 0; + char buf[STRING_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 vpiOctConst: + case vpiHexConst: + type = vpiIntVar; + break; + + case vpiBinaryConst: + type = vpiBitVar; + 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, STRING_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, STRING_BUF_SIZE, "%s%s", string, val.value.str); + break; + + case vpiRealVar: + val.format = vpiRealVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%lf", string, val.value.real); + break; + + case vpiStringVar: + val.format = vpiStringVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_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, STRING_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, STRING_BUF_SIZE, "%s%s", string, tmp); + } + break; + + case FORMAT_HEX: + val.format = vpiIntVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%X", string, val.value.integer); + break; + + case FORMAT_STRING: + val.format = vpiStringVal; + vpi_get_value(varh, &val); + res = snprintf(buf, STRING_BUF_SIZE, "%s%s", string, val.value.str); + break; + } + + if(res >= STRING_BUF_SIZE) + fail = 1; + + if(!fail) { + /* Strip the read token from the string */ + char* tmp = strdup(buf); + 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 -Nru iverilog-10.3/vpi/wavealloca.h iverilog-11.0/vpi/wavealloca.h --- iverilog-10.3/vpi/wavealloca.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi/wavealloca.h 2020-09-26 22:44:25.000000000 +0000 @@ -18,6 +18,8 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ #ifndef WAVE_ALLOCA_H diff -Nru iverilog-10.3/vpi_modules.cc iverilog-11.0/vpi_modules.cc --- iverilog-10.3/vpi_modules.cc 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/vpi_modules.cc 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2019-2020 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) { } +void vpi_vcontrol(PLI_INT32, va_list) { } + + +/* 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; +} + +#if defined(__MINGW32__) || defined (__CYGWIN__) +vpip_routines_s vpi_routines = { + .register_cb = vpi_register_cb, + .remove_cb = vpi_remove_cb, + .register_systf = vpi_register_systf, + .get_systf_info = vpi_get_systf_info, + .handle_by_name = vpi_handle_by_name, + .handle_by_index = vpi_handle_by_index, + .handle = vpi_handle, + .iterate = vpi_iterate, + .scan = vpi_scan, + .get = vpi_get, + .get_str = vpi_get_str, + .get_delays = vpi_get_delays, + .put_delays = vpi_put_delays, + .get_value = vpi_get_value, + .put_value = vpi_put_value, + .get_time = vpi_get_time, + .get_userdata = vpi_get_userdata, + .put_userdata = vpi_put_userdata, + .mcd_open = vpi_mcd_open, + .mcd_close = vpi_mcd_close, + .mcd_flush = vpi_mcd_flush, + .mcd_name = vpi_mcd_name, + .mcd_vprintf = vpi_mcd_vprintf, + .flush = vpi_flush, + .vprintf = vpi_vprintf, + .chk_error = vpi_chk_error, + .compare_objects = vpi_compare_objects, + .free_object = vpi_free_object, + .get_vlog_info = vpi_get_vlog_info, + .vcontrol = vpi_vcontrol, + .fopen = vpi_fopen, + .get_file = vpi_get_file, + .calc_clog2 = vpip_calc_clog2, + .count_drivers = vpip_count_drivers, + .format_strength = vpip_format_strength, + .make_systf_system_defined = vpip_make_systf_system_defined, + .mcd_rawwrite = vpip_mcd_rawwrite, + .set_return_value = vpip_set_return_value, +}; + +typedef PLI_UINT32 (*vpip_set_callback_t)(vpip_routines_s*, PLI_UINT32); +#endif +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; + } + +#if defined(__MINGW32__) || defined (__CYGWIN__) + void*function = ivl_dlsym(dll, "vpip_set_callback"); + if (function == 0) { + cerr << "warning: '" << path << "' has no vpip_set_callback()" << endl; + ivl_dlclose(dll); + return true; + } + vpip_set_callback_t set_callback = (vpip_set_callback_t)function; + if (!set_callback(&vpi_routines, vpip_routines_version)) { + cerr << "error: Failed to link '" << path << "'. " + "Try rebuilding it with iverilog-vpi." << endl; + ivl_dlclose(dll); + return true; + } +#endif + + void*table = ivl_dlsym(dll, LU "vlog_startup_routines" TU); + 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; +} diff -Nru iverilog-10.3/vpi.txt iverilog-11.0/vpi.txt --- iverilog-10.3/vpi.txt 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi.txt 2020-09-26 22:44:25.000000000 +0000 @@ -4,13 +4,16 @@ The VPI interface for Icarus Verilog works by creating from a collection of PLI applications a single vpi module. The vpi module includes compiled code for the applications linked together (with any -other libraries that the applications need) into a module with a -single exported symbol, the vlog_startup_routines array. - -The product that wishes to invoke the module (normally at run time) -loads the module, locates the vlog_startup_routines table, and calls -all the startup routines contained in that table. It is possible for a -product to link with many modules. In that case, all the modules are +other libraries that the applications need) into a module with two +exported symbols, the vpip_set_callback function and the +vlog_startup_routines array. + +The product that wishes to invoke the module (normally at run time) loads +the module, locates and calls the vpip_set_callback function to pass the +the module a jump table that allows the module to access the VPI routines +implemented by the product, then locates the vlog_startup_routines table +and calls all the startup routines contained in that table. It is possible +for a product to link with many modules. In that case, all the modules are linked in and startup routines are called in order. The product that uses vpi modules uses the environment variable @@ -19,9 +22,11 @@ means the product supports) the module search path is scanned until the module is located. -The special module name "system.vpi" is part of the core Icarus -Verilog distribution and includes implementations of the standard -system tasks/functions. +The special module names "system.vpi", "v2005_math.vpi", "v2009.vpi", +and "va_math.vpi" are part of the core Icarus Verilog distribution and +include implementations of the standard system tasks/functions. The +additional special module names "vhdl_sys.vpi" and "vhdl_textio.vpi" +include implementations of private functions used to support VHDL. COMPILING A VPI MODULE @@ -40,4 +45,3 @@ normally. Also, the format of the tracing messages will change according to my needs (and whim) so don't expect to be able to parse it in software. - diff -Nru iverilog-10.3/vpi_user.h iverilog-11.0/vpi_user.h --- iverilog-10.3/vpi_user.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vpi_user.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef VPI_USER_H #define VPI_USER_H /* - * Copyright (c) 1999-2018 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2020 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 @@ -20,7 +20,7 @@ */ -#if defined(__MINGW32__) || defined (__CYGWIN32__) +#if defined(__MINGW32__) || defined (__CYGWIN__) # define DLLEXPORT __declspec(dllexport) #else # define DLLEXPORT @@ -231,7 +231,7 @@ PLI_INT32 time_type; /* vpiScaledRealTime, vpiSimTime */ PLI_INT32 mtm_flag; PLI_INT32 append_flag; - PLI_INT32 plusere_flag; + PLI_INT32 pulsere_flag; } s_vpi_delay, *p_vpi_delay; @@ -286,12 +286,14 @@ #define vpiNamedEvent 34 #define vpiNamedFork 35 #define vpiNet 36 +#define vpiNetBit 37 #define vpiParameter 41 #define vpiPartSelect 42 #define vpiPathTerm 43 #define vpiPort 44 #define vpiRealVar 47 #define vpiReg 48 +#define vpiRegBit 49 #define vpiSysFuncCall 56 #define vpiSysTaskCall 57 #define vpiTask 59 @@ -495,6 +497,7 @@ #define cbExitInteractive 22 #define cbInteractiveScopeChange 23 #define cbUnresolvedSystf 24 +#define cbAtEndOfSimTime 31 extern vpiHandle vpi_register_cb(p_cb_data data); extern PLI_INT32 vpi_remove_cb(vpiHandle ref); @@ -630,7 +633,14 @@ /* Format a scalar a la %v. The str points to a 4byte character buffer. The value must be a vpiStrengthVal. */ extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit); + /* Set the return value to return from the vvp run time. This is + usually 0 or 1. This is the exit code that the vvp process + returns, and in distinct from the finish_number that is an + argument to $fatal and other severity tasks. The $fatal and + $finish system tasks bundled with iverilog use this function to + tell vvp to exit SUCCESS or FAILURE. */ extern void vpip_set_return_value(int value); + extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg); extern void vpip_make_systf_system_defined(vpiHandle ref); @@ -668,6 +678,63 @@ # define _vpi_at_APV 6 #endif +#if defined(__MINGW32__) || defined (__CYGWIN__) +/* + * In Linux, when loaded, a shared library can automatically bind to functions + * provided by its client. In Windows, a DLL can only do this statically at + * link time, and is then tied to a specific client. So to enable VPI modules + * to be used by both the compiler and the simulator, we construct a jump table + * for the VPI routines that we can pass down to the VPI modules. + */ + +// Increment the version number any time vpip_routines_s is changed. +static const PLI_UINT32 vpip_routines_version = 1; + +typedef struct { + vpiHandle (*register_cb)(p_cb_data); + PLI_INT32 (*remove_cb)(vpiHandle); + vpiHandle (*register_systf)(const struct t_vpi_systf_data*ss); + void (*get_systf_info)(vpiHandle, p_vpi_systf_data); + vpiHandle (*handle_by_name)(const char*, vpiHandle); + vpiHandle (*handle_by_index)(vpiHandle, PLI_INT32); + vpiHandle (*handle)(PLI_INT32, vpiHandle); + vpiHandle (*iterate)(PLI_INT32, vpiHandle); + vpiHandle (*scan)(vpiHandle); + PLI_INT32 (*get)(int, vpiHandle); + char* (*get_str)(PLI_INT32, vpiHandle); + void (*get_delays)(vpiHandle, p_vpi_delay); + void (*put_delays)(vpiHandle, p_vpi_delay); + void (*get_value)(vpiHandle, p_vpi_value); + vpiHandle (*put_value)(vpiHandle, p_vpi_value, p_vpi_time, PLI_INT32); + void (*get_time)(vpiHandle, s_vpi_time*); + void* (*get_userdata)(vpiHandle); + PLI_INT32 (*put_userdata)(vpiHandle, void*); + PLI_UINT32 (*mcd_open)(char *); + PLI_UINT32 (*mcd_close)(PLI_UINT32); + PLI_INT32 (*mcd_flush)(PLI_UINT32); + char* (*mcd_name)(PLI_UINT32); + PLI_INT32 (*mcd_vprintf)(PLI_UINT32, const char*, va_list); + PLI_INT32 (*flush)(void); + PLI_INT32 (*vprintf)(const char*, va_list); + PLI_INT32 (*chk_error)(p_vpi_error_info); + PLI_INT32 (*compare_objects)(vpiHandle, vpiHandle); + PLI_INT32 (*free_object)(vpiHandle); + PLI_INT32 (*get_vlog_info)(p_vpi_vlog_info info) ; + void (*vcontrol)(PLI_INT32, va_list); + PLI_INT32 (*fopen)(const char*, const char*); + FILE* (*get_file)(PLI_INT32); + s_vpi_vecval(*calc_clog2)(vpiHandle); + void (*count_drivers)(vpiHandle, unsigned, unsigned [4]); + void (*format_strength)(char*, s_vpi_value*, unsigned); + void (*make_systf_system_defined)(vpiHandle); + void (*mcd_rawwrite)(PLI_UINT32, const char*, size_t); + void (*set_return_value)(int); +} vpip_routines_s; + +extern DLLEXPORT PLI_UINT32 vpip_set_callback(vpip_routines_s*routines, PLI_UINT32 version); + +#endif // defined(__MINGW32__) || defined (__CYGWIN__) + EXTERN_C_END #endif /* VPI_USER_H */ diff -Nru iverilog-10.3/vvp/arith.cc iverilog-11.0/vvp/arith.cc --- iverilog-10.3/vvp/arith.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/arith.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2017 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 @@ -830,7 +830,7 @@ void vvp_cmp_gtge_base_::recv_vec4_base_(vvp_net_ptr_t ptr, - vvp_vector4_t bit, + const vvp_vector4_t&bit, vvp_bit4_t out_if_equal) { dispatch_operand_(ptr, bit); @@ -868,6 +868,76 @@ recv_vec4_base_(ptr, bit, BIT4_0); } +vvp_cmp_weq::vvp_cmp_weq(unsigned wid) +: vvp_arith_(wid) +{ +} + +void vvp_cmp_weq::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + vvp_context_t) +{ + dispatch_operand_(ptr, bit); + + vvp_vector4_t eeq (1); + eeq.set_bit(0, BIT4_1); + + assert(op_a_.size() == op_b_.size()); + for (unsigned idx = 0 ; idx < op_a_.size() ; idx += 1) { + vvp_bit4_t a = op_a_.value(idx); + vvp_bit4_t b = op_b_.value(idx); + if (b == BIT4_X) + continue; + else if (b == BIT4_Z) + continue; + else if (a == BIT4_X) + eeq.set_bit(0, BIT4_X); + else if (a == BIT4_Z) + eeq.set_bit(0, BIT4_X); + else if (a != b) { + eeq.set_bit(0, BIT4_0); + break; + } + } + + vvp_net_t*net = ptr.ptr(); + net->send_vec4(eeq, 0); +} + +vvp_cmp_wne::vvp_cmp_wne(unsigned wid) +: vvp_arith_(wid) +{ +} + +void vvp_cmp_wne::recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + vvp_context_t) +{ + dispatch_operand_(ptr, bit); + + vvp_vector4_t eeq (1); + eeq.set_bit(0, BIT4_0); + + assert(op_a_.size() == op_b_.size()); + for (unsigned idx = 0 ; idx < op_a_.size() ; idx += 1) { + vvp_bit4_t a = op_a_.value(idx); + vvp_bit4_t b = op_b_.value(idx); + if (b == BIT4_X) + continue; + else if (b == BIT4_Z) + continue; + else if (a == BIT4_X) + eeq.set_bit(0, BIT4_X); + else if (a == BIT4_Z) + eeq.set_bit(0, BIT4_X); + else if (a != b) { + eeq.set_bit(0, BIT4_1); + break; + } + } + + vvp_net_t*net = ptr.ptr(); + net->send_vec4(eeq, 0); +} + vvp_shiftl::vvp_shiftl(unsigned wid) : vvp_arith_(wid) @@ -885,13 +955,14 @@ vvp_vector4_t out (op_a_.size()); + bool overflow_flag; unsigned long shift; - if (! vector4_to_value(op_b_, shift)) { + if (! vector4_to_value(op_b_, overflow_flag, shift)) { ptr.ptr()->send_vec4(x_val_, 0); return; } - if (shift > out.size()) + if (overflow_flag || shift > out.size()) shift = out.size(); for (unsigned idx = 0 ; idx < shift ; idx += 1) @@ -919,13 +990,14 @@ vvp_vector4_t out (op_a_.size()); + bool overflow_flag; unsigned long shift; - if (! vector4_to_value(op_b_, shift)) { + if (! vector4_to_value(op_b_, overflow_flag, shift)) { ptr.ptr()->send_vec4(x_val_, 0); return; } - if (shift > out.size()) + if (overflow_flag || shift > out.size()) shift = out.size(); for (unsigned idx = shift ; idx < out.size() ; idx += 1) diff -Nru iverilog-10.3/vvp/arith.h iverilog-11.0/vvp/arith.h --- iverilog-10.3/vvp/arith.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/arith.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_arith_H #define IVL_arith_H /* - * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2017 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 @@ -200,6 +200,24 @@ }; +class vvp_cmp_weq : public vvp_arith_ { + + public: + explicit vvp_cmp_weq(unsigned wid); + void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + vvp_context_t); + +}; + +class vvp_cmp_wne : public vvp_arith_ { + + public: + explicit vvp_cmp_wne(unsigned wid); + void recv_vec4(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + vvp_context_t); + +}; + /* * This base class implements both GT and GE comparisons. The derived @@ -212,7 +230,7 @@ explicit vvp_cmp_gtge_base_(unsigned wid, bool signed_flag); protected: - void recv_vec4_base_(vvp_net_ptr_t ptr, vvp_vector4_t bit, + void recv_vec4_base_(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, vvp_bit4_t out_if_equal); private: bool signed_flag_; diff -Nru iverilog-10.3/vvp/array.cc iverilog-11.0/vvp/array.cc --- iverilog-10.3/vvp/array.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/array.cc 2020-09-26 22:44:25.000000000 +0000 @@ -276,7 +276,7 @@ return get_size(); case vpiAutomatic: - return (int) scope->is_automatic; + return scope->is_automatic()? 1 : 0; default: return 0; @@ -361,8 +361,23 @@ assert(val.format == vpiIntVal); return val.value.integer; + case vpiIndex: + { + int base_offset = 0; + struct __vpiArray*base = dynamic_cast<__vpiArray*> (parent); + if (base) { + val.format = vpiIntVal; + base->first_addr.vpi_get_value(&val); + base_offset += val.value.integer; + } + val.format = vpiIntVal; + obj->as_index.vpi_get_value(&val); + assert(val.format == vpiIntVal); + return val.value.integer + base_offset; + } + case vpiAutomatic: - return (int) parent->get_scope()->is_automatic; + return parent->get_scope()->is_automatic()? 1 : 0; #if defined(CHECK_WITH_VALGRIND) || defined(BR916_STOPGAP_FIX) case _vpiFromThr: @@ -394,7 +409,7 @@ return (int)get_address() + array->first_addr.get_value(); case vpiAutomatic: - return (int) array->get_scope()->is_automatic; + return array->get_scope()->is_automatic() ? 1 : 0; #if defined(CHECK_WITH_VALGRIND) || defined(BR916_STOPGAP_FIX) case _vpiFromThr: @@ -502,7 +517,7 @@ return part_bit; case vpiAutomatic: - return (int) array->get_scope()->is_automatic; + return array->get_scope()->is_automatic() ? 1 : 0; #if defined(CHECK_WITH_VALGRIND) || defined(BR916_STOPGAP_FIX) case _vpiFromThr: @@ -875,7 +890,7 @@ /* Make the words. */ arr->vals_width = labs(msb-lsb) + 1; - if (vpip_peek_current_scope()->is_automatic) { + if (vpip_peek_current_scope()->is_automatic()) { arr->vals4 = new vvp_vector4array_aa(arr->vals_width, arr->get_size()); } else { @@ -1119,7 +1134,7 @@ private: void check_word_change_(unsigned long addr, vvp_context_t context); - struct __vpiScope*context_scope_; + __vpiScope*context_scope_; unsigned context_idx_; }; @@ -1220,7 +1235,7 @@ void vvp_fun_arrayport_aa::check_word_change(unsigned long addr) { - if (arr_->get_scope()->is_automatic) { + if (arr_->get_scope()->is_automatic()) { assert(vthread_get_wt_context()); check_word_change_(addr, vthread_get_wt_context()); } else { @@ -1237,7 +1252,7 @@ assert(fun->next_ == 0); fun->next_ = array->ports_; array->ports_ = fun; - if (!array->get_scope()->is_automatic) { + if (!array->get_scope()->is_automatic()) { /* propagate initial values for variable arrays */ if (array->vals4) { vvp_vector4_t tmp(array->vals_width, BIT4_X); @@ -1374,12 +1389,12 @@ vvp_fun_arrayport*fun; if (use_addr) - if (vpip_peek_current_scope()->is_automatic) + if (vpip_peek_current_scope()->is_automatic()) fun = new vvp_fun_arrayport_aa(mem, ptr, addr); else fun = new vvp_fun_arrayport_sa(mem, ptr, addr); else - if (vpip_peek_current_scope()->is_automatic) + if (vpip_peek_current_scope()->is_automatic()) fun = new vvp_fun_arrayport_aa(mem, ptr); else fun = new vvp_fun_arrayport_sa(mem, ptr); diff -Nru iverilog-10.3/vvp/class_type.cc iverilog-11.0/vvp/class_type.cc --- iverilog-10.3/vvp/class_type.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/class_type.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2019 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 @@ -36,7 +36,7 @@ */ class class_property_t { public: - explicit inline class_property_t() { } + inline class_property_t() { } virtual ~class_property_t() =0; // How much space does an instance of this property require? virtual size_t instance_size() const =0; @@ -143,7 +143,7 @@ class property_bit : public class_property_t { public: - inline property_bit(size_t wid): wid_(wid) { } + explicit inline property_bit(size_t wid): wid_(wid) { } ~property_bit() { } size_t instance_size() const { return sizeof(vvp_vector2_t); } @@ -168,7 +168,7 @@ class property_logic : public class_property_t { public: - inline property_logic(size_t wid): wid_(wid) { } + explicit inline property_logic(size_t wid): wid_(wid) { } ~property_logic() { } size_t instance_size() const { return sizeof(vvp_vector4_t); } @@ -441,6 +441,9 @@ } else if (type[0] == 'L') { size_t wid = strtoul(type.c_str()+1,0,0); properties_[idx].type = new property_logic(wid); + } else if (type[0] == 's' && type[1] == 'L') { + size_t wid = strtoul(type.c_str()+2,0,0); + properties_[idx].type = new property_logic(wid); } else { properties_[idx].type = 0; } @@ -595,7 +598,7 @@ void compile_class_done(void) { - struct __vpiScope*scope = vpip_peek_current_scope(); + __vpiScope*scope = vpip_peek_current_scope(); assert(scope); assert(compile_class); compile_class->finish_setup(); diff -Nru iverilog-10.3/vvp/codes.cc iverilog-11.0/vvp/codes.cc --- iverilog-10.3/vvp/codes.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/codes.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2012 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2016 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 @@ -113,7 +113,8 @@ count_opcodes -= 1; if ((cur+idx)->opcode == &of_VPI_CALL) { vpi_call_delete((cur+idx)->handle); - } else if ((cur+idx)->opcode == &of_EXEC_UFUNC) { + } else if (((cur+idx)->opcode == &of_EXEC_UFUNC_REAL) || + ((cur+idx)->opcode == &of_EXEC_UFUNC_VEC4)) { exec_ufunc_delete((cur+idx)); } else if ((cur+idx)->opcode == &of_FILE_LINE) { delete((cur+idx)->handle); diff -Nru iverilog-10.3/vvp/codes.h iverilog-11.0/vvp/codes.h --- iverilog-10.3/vvp/codes.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/codes.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_codes_H #define IVL_codes_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -56,11 +56,19 @@ extern bool of_BLEND(vthread_t thr, vvp_code_t code); extern bool of_BLEND_WR(vthread_t thr, vvp_code_t code); extern bool of_BREAKPOINT(vthread_t thr, vvp_code_t code); +extern bool of_CALLF_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_CALLF_REAL(vthread_t thr, vvp_code_t code); +extern bool of_CALLF_STR(vthread_t thr, vvp_code_t code); +extern bool of_CALLF_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_CALLF_VOID(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_LINK(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_VEC4(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_VEC4_OFF(vthread_t thr, vvp_code_t code); extern bool of_CASSIGN_WR(vthread_t thr, vvp_code_t code); extern bool of_CAST2(vthread_t thr, vvp_code_t code); +extern bool of_CAST_VEC2_DAR(vthread_t thr, vvp_code_t code); +extern bool of_CAST_VEC4_DAR(vthread_t thr, vvp_code_t code); +extern bool of_CAST_VEC4_STR(vthread_t thr, vvp_code_t code); extern bool of_CMPE(vthread_t thr, vvp_code_t code); extern bool of_CMPIE(vthread_t thr, vvp_code_t code); extern bool of_CMPINE(vthread_t thr, vvp_code_t code); @@ -70,6 +78,8 @@ extern bool of_CMPSTR(vthread_t thr, vvp_code_t code); extern bool of_CMPU(vthread_t thr, vvp_code_t code); extern bool of_CMPIU(vthread_t thr, vvp_code_t code); +extern bool of_CMPWE(vthread_t thr, vvp_code_t code); +extern bool of_CMPWNE(vthread_t thr, vvp_code_t code); extern bool of_CMPWR(vthread_t thr, vvp_code_t code); extern bool of_CMPWS(vthread_t thr, vvp_code_t code); extern bool of_CMPWU(vthread_t thr, vvp_code_t code); @@ -79,8 +89,6 @@ extern bool of_CONCATI_STR(vthread_t thr, vvp_code_t code); extern bool of_CONCAT_VEC4(vthread_t thr, vvp_code_t code); extern bool of_CONCATI_VEC4(vthread_t thr, vvp_code_t code); -extern bool of_CVT_RS(vthread_t thr, vvp_code_t code); -extern bool of_CVT_RU(vthread_t thr, vvp_code_t code); extern bool of_CVT_RV(vthread_t thr, vvp_code_t code); extern bool of_CVT_RV_S(vthread_t thr, vvp_code_t code); extern bool of_CVT_SR(vthread_t thr, vvp_code_t code); @@ -91,7 +99,9 @@ extern bool of_DEBUG_THR(vthread_t thr, vvp_code_t code); extern bool of_DELAY(vthread_t thr, vvp_code_t code); extern bool of_DELAYX(vthread_t thr, vvp_code_t code); +extern bool of_DELETE_ELEM(vthread_t thr, vvp_code_t code); extern bool of_DELETE_OBJ(vthread_t thr, vvp_code_t code); +extern bool of_DELETE_TAIL(vthread_t thr, vvp_code_t code); extern bool of_DISABLE(vthread_t thr, vvp_code_t code); extern bool of_DISABLE_FORK(vthread_t thr, vvp_code_t code); extern bool of_DIV(vthread_t thr, vvp_code_t code); @@ -115,6 +125,7 @@ extern bool of_FORCE_LINK(vthread_t thr, vvp_code_t code); extern bool of_FORCE_VEC4(vthread_t thr, vvp_code_t code); extern bool of_FORCE_VEC4_OFF(vthread_t thr, vvp_code_t code); +extern bool of_FORCE_VEC4_OFF_D(vthread_t thr, vvp_code_t code); extern bool of_FORCE_WR(vthread_t thr, vvp_code_t code); extern bool of_FORK(vthread_t thr, vvp_code_t code); extern bool of_FREE(vthread_t thr, vvp_code_t code); @@ -178,8 +189,13 @@ extern bool of_POW(vthread_t thr, vvp_code_t code); extern bool of_POW_S(vthread_t thr, vvp_code_t code); extern bool of_POW_WR(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_REAL(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_STR(vthread_t thr, vvp_code_t code); +extern bool of_QINSERT_V(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_B_REAL(vthread_t thr, vvp_code_t code); extern bool of_QPOP_B_STR(vthread_t thr, vvp_code_t code); extern bool of_QPOP_B_V(vthread_t thr, vvp_code_t code); +extern bool of_QPOP_F_REAL(vthread_t thr, vvp_code_t code); extern bool of_QPOP_F_STR(vthread_t thr, vvp_code_t code); extern bool of_QPOP_F_V(vthread_t thr, vvp_code_t code); extern bool of_PROP_OBJ(vthread_t thr, vvp_code_t code); @@ -195,6 +211,12 @@ extern bool of_RELEASE_REG(vthread_t thr, vvp_code_t code); extern bool of_RELEASE_WR(vthread_t thr, vvp_code_t code); extern bool of_REPLICATE(vthread_t thr, vvp_code_t code); +extern bool of_RET_REAL(vthread_t thr, vvp_code_t code); +extern bool of_RET_STR(vthread_t thr, vvp_code_t code); +extern bool of_RET_VEC4(vthread_t thr, vvp_code_t code); +extern bool of_RETLOAD_REAL(vthread_t thr, vvp_code_t code); +extern bool of_RETLOAD_STR(vthread_t thr, vvp_code_t code); +extern bool of_RETLOAD_VEC4(vthread_t thr, vvp_code_t code); extern bool of_SCOPY(vthread_t thr, vvp_code_t code); extern bool of_SET_DAR_OBJ_REAL(vthread_t thr, vvp_code_t code); extern bool of_SET_DAR_OBJ_STR(vthread_t thr, vvp_code_t code); @@ -207,18 +229,24 @@ extern bool of_STORE_DAR_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_DAR_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_DAR_VEC4(vthread_t thr, vvp_code_t code); -extern bool of_STORE_QB_R(vthread_t thr, vvp_code_t code); -extern bool of_STORE_QB_STR(vthread_t thr, vvp_code_t code); -extern bool of_STORE_QB_V(vthread_t thr, vvp_code_t code); -extern bool of_STORE_QF_R(vthread_t thr, vvp_code_t code); -extern bool of_STORE_QF_STR(vthread_t thr, vvp_code_t code); -extern bool of_STORE_QF_V(vthread_t thr, vvp_code_t code); extern bool of_STORE_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_OBJA(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_OBJ(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_R(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_STR(vthread_t thr, vvp_code_t code); extern bool of_STORE_PROP_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QB_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QB_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QB_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QDAR_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QDAR_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QDAR_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QF_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QF_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QF_V(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QOBJ_R(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QOBJ_STR(vthread_t thr, vvp_code_t code); +extern bool of_STORE_QOBJ_V(vthread_t thr, vvp_code_t code); extern bool of_STORE_REAL(vthread_t thr, vvp_code_t code); extern bool of_STORE_REALA(vthread_t thr, vvp_code_t code); extern bool of_STORE_STR(vthread_t thr, vvp_code_t code); @@ -244,7 +272,8 @@ extern bool of_ZOMBIE(vthread_t thr, vvp_code_t code); -extern bool of_EXEC_UFUNC(vthread_t thr, vvp_code_t code); +extern bool of_EXEC_UFUNC_REAL(vthread_t thr, vvp_code_t code); +extern bool of_EXEC_UFUNC_VEC4(vthread_t thr, vvp_code_t code); extern bool of_REAP_UFUNC(vthread_t thr, vvp_code_t code); extern bool of_CHUNK_LINK(vthread_t thr, vvp_code_t code); @@ -261,7 +290,7 @@ vvp_code_t cptr; vvp_array_t array; class __vpiHandle*handle; - struct __vpiScope*scope; + __vpiScope*scope; const char*text; }; diff -Nru iverilog-10.3/vvp/compile.cc iverilog-11.0/vvp/compile.cc --- iverilog-10.3/vvp/compile.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/compile.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2016 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -65,6 +65,7 @@ OA_BIT2, /* The operand is a pointer to code space */ OA_CODE_PTR, + OA_CODE_PTR2, /* The operand is a variable or net pointer */ OA_FUNC_PTR, /* The operand is a second functor pointer */ @@ -107,31 +108,39 @@ { "%blend", of_BLEND, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%blend/wr", of_BLEND_WR,0, {OA_NONE, OA_NONE, OA_NONE} }, { "%breakpoint", of_BREAKPOINT, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%callf/obj", of_CALLF_OBJ, 2,{OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} }, + { "%callf/real", of_CALLF_REAL, 2,{OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} }, + { "%callf/str", of_CALLF_STR, 2,{OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} }, + { "%callf/vec4", of_CALLF_VEC4, 2,{OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} }, + { "%callf/void", of_CALLF_VOID, 2,{OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} }, { "%cassign/link", of_CASSIGN_LINK, 2,{OA_FUNC_PTR,OA_FUNC_PTR2,OA_NONE} }, { "%cassign/vec4", of_CASSIGN_VEC4, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%cassign/vec4/off",of_CASSIGN_VEC4_OFF,2,{OA_FUNC_PTR,OA_BIT1, OA_NONE} }, { "%cassign/wr", of_CASSIGN_WR, 1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, - { "%cast2", of_CAST2, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/e", of_CMPE, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/ne", of_CMPNE, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/s", of_CMPS, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/str",of_CMPSTR, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/u", of_CMPU, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/wr", of_CMPWR, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/ws", of_CMPWS, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, - { "%cmp/wu", of_CMPWU, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, - { "%cmp/x", of_CMPX, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmp/z", of_CMPZ, 0, {OA_NONE, OA_NONE, OA_NONE} }, - { "%cmpi/e", of_CMPIE, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, - { "%cmpi/ne",of_CMPINE, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, - { "%cmpi/s", of_CMPIS, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, - { "%cmpi/u", of_CMPIU, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%cast/vec2/dar", of_CAST_VEC2_DAR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%cast/vec4/dar", of_CAST_VEC4_DAR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%cast/vec4/str", of_CAST_VEC4_STR, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%cast2", of_CAST2, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/e", of_CMPE, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/ne", of_CMPNE, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/s", of_CMPS, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/str", of_CMPSTR, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/u", of_CMPU, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/we", of_CMPWE, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/wne", of_CMPWNE, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/wr", of_CMPWR, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/ws", of_CMPWS, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, + { "%cmp/wu", of_CMPWU, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, + { "%cmp/x", of_CMPX, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmp/z", of_CMPZ, 0, {OA_NONE, OA_NONE, OA_NONE} }, + { "%cmpi/e", of_CMPIE, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%cmpi/ne", of_CMPINE, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%cmpi/s", of_CMPIS, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, + { "%cmpi/u", of_CMPIU, 3, {OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%concat/str", of_CONCAT_STR, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%concat/vec4", of_CONCAT_VEC4, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%concati/str", of_CONCATI_STR, 1,{OA_STRING,OA_NONE, OA_NONE} }, { "%concati/vec4",of_CONCATI_VEC4,3,{OA_BIT1, OA_BIT2, OA_NUMBER} }, - { "%cvt/rs", of_CVT_RS, 1, {OA_BIT1, OA_NONE, OA_NONE} }, - { "%cvt/ru", of_CVT_RU, 1, {OA_BIT1, OA_NONE, OA_NONE} }, { "%cvt/rv", of_CVT_RV, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%cvt/rv/s", of_CVT_RV_S,0, {OA_NONE, OA_NONE, OA_NONE} }, { "%cvt/sr", of_CVT_SR, 1, {OA_BIT1, OA_NONE, OA_NONE} }, @@ -142,7 +151,10 @@ { "%debug/thr", of_DEBUG_THR, 1,{OA_STRING, OA_NONE, OA_NONE} }, { "%delay", of_DELAY, 2, {OA_BIT1, OA_BIT2, OA_NONE} }, { "%delayx", of_DELAYX, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, + { "%delete/elem",of_DELETE_ELEM,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, { "%delete/obj",of_DELETE_OBJ,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, + { "%delete/tail",of_DELETE_TAIL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%disable", of_DISABLE, 1, {OA_VPI_PTR,OA_NONE, OA_NONE} }, { "%disable/fork",of_DISABLE_FORK,0,{OA_NONE,OA_NONE, OA_NONE} }, { "%div", of_DIV, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%div/s", of_DIV_S, 0, {OA_NONE, OA_NONE, OA_NONE} }, @@ -164,7 +176,9 @@ { "%force/link", of_FORCE_LINK,2,{OA_FUNC_PTR, OA_FUNC_PTR2, OA_NONE} }, { "%force/vec4", of_FORCE_VEC4, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} }, { "%force/vec4/off",of_FORCE_VEC4_OFF,2,{OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%force/vec4/off/d",of_FORCE_VEC4_OFF_D,3,{OA_FUNC_PTR, OA_BIT1, OA_BIT2} }, { "%force/wr", of_FORCE_WR, 1,{OA_FUNC_PTR, OA_NONE, OA_NONE} }, + { "%fork", of_FORK, 2, {OA_CODE_PTR2,OA_VPI_PTR, OA_NONE} }, { "%free", of_FREE, 1, {OA_VPI_PTR, OA_NONE, OA_NONE} }, { "%inv", of_INV, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%ix/add", of_IX_ADD, 3, {OA_NUMBER, OA_BIT1, OA_BIT2} }, @@ -235,14 +249,25 @@ { "%pushi/vec4",of_PUSHI_VEC4,3,{OA_BIT1, OA_BIT2, OA_NUMBER} }, { "%pushv/str", of_PUSHV_STR, 0,{OA_NONE, OA_NONE, OA_NONE} }, { "%putc/str/vec4",of_PUTC_STR_VEC4,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, - { "%qpop/b/str",of_QPOP_B_STR,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, - { "%qpop/b/v", of_QPOP_B_V, 1,{OA_FUNC_PTR,OA_NONE, OA_BIT2} }, - { "%qpop/f/str",of_QPOP_F_STR,1,{OA_FUNC_PTR,OA_NONE, OA_NONE} }, - { "%qpop/f/v", of_QPOP_F_V, 1,{OA_FUNC_PTR,OA_NONE, OA_BIT2} }, + { "%qinsert/real",of_QINSERT_REAL,2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qinsert/str", of_QINSERT_STR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qinsert/v", of_QINSERT_V, 3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, + { "%qpop/b/real",of_QPOP_B_REAL,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, + { "%qpop/b/str", of_QPOP_B_STR, 1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, + { "%qpop/b/v", of_QPOP_B_V, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, + { "%qpop/f/real",of_QPOP_F_REAL,1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, + { "%qpop/f/str", of_QPOP_F_STR, 1,{OA_FUNC_PTR,OA_NONE,OA_NONE} }, + { "%qpop/f/v", of_QPOP_F_V, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%release/net",of_RELEASE_NET,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/reg",of_RELEASE_REG,3,{OA_FUNC_PTR,OA_BIT1,OA_BIT2} }, { "%release/wr", of_RELEASE_WR, 2,{OA_FUNC_PTR,OA_BIT1,OA_NONE} }, { "%replicate", of_REPLICATE, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%ret/real", of_RET_REAL, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%ret/str", of_RET_STR, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%ret/vec4", of_RET_VEC4, 3,{OA_NUMBER, OA_BIT1,OA_BIT2} }, + { "%retload/real",of_RETLOAD_REAL,1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%retload/str", of_RETLOAD_STR, 1,{OA_NUMBER, OA_NONE,OA_NONE} }, + { "%retload/vec4",of_RETLOAD_VEC4,1,{OA_NUMBER, OA_NONE,OA_NONE} }, { "%scopy", of_SCOPY, 0, {OA_NONE, OA_NONE, OA_NONE} }, { "%set/dar/obj/real",of_SET_DAR_OBJ_REAL,1,{OA_NUMBER,OA_NONE,OA_NONE} }, { "%set/dar/obj/str", of_SET_DAR_OBJ_STR, 1,{OA_NUMBER,OA_NONE,OA_NONE} }, @@ -260,12 +285,18 @@ { "%store/prop/r", of_STORE_PROP_R, 1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/str",of_STORE_PROP_STR,1, {OA_NUMBER, OA_NONE, OA_NONE} }, { "%store/prop/v", of_STORE_PROP_V, 2, {OA_NUMBER, OA_BIT1, OA_NONE} }, - { "%store/qb/r", of_STORE_QB_R, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, - { "%store/qb/str", of_STORE_QB_STR, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, - { "%store/qb/v", of_STORE_QB_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, - { "%store/qf/r", of_STORE_QF_R, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, - { "%store/qf/str", of_STORE_QF_STR, 1, {OA_FUNC_PTR, OA_NONE, OA_NONE} }, - { "%store/qf/v", of_STORE_QF_V, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%store/qb/r", of_STORE_QB_R, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%store/qb/str", of_STORE_QB_STR, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%store/qb/v", of_STORE_QB_V, 3, {OA_FUNC_PTR, OA_BIT1, OA_BIT2} }, + { "%store/qdar/r", of_STORE_QDAR_R, 2,{OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%store/qdar/str",of_STORE_QDAR_STR,2,{OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%store/qdar/v", of_STORE_QDAR_V, 3,{OA_FUNC_PTR, OA_BIT1, OA_BIT2} }, + { "%store/qf/r", of_STORE_QF_R, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%store/qf/str", of_STORE_QF_STR, 2, {OA_FUNC_PTR, OA_BIT1, OA_NONE} }, + { "%store/qf/v", of_STORE_QF_V, 3, {OA_FUNC_PTR, OA_BIT1, OA_BIT2} }, + { "%store/qobj/r", of_STORE_QOBJ_R, 2, {OA_FUNC_PTR,OA_BIT1, OA_NONE} }, + { "%store/qobj/str",of_STORE_QOBJ_STR,2, {OA_FUNC_PTR,OA_BIT1, OA_NONE} }, + { "%store/qobj/v", of_STORE_QOBJ_V, 3, {OA_FUNC_PTR,OA_BIT1, OA_BIT2} }, { "%store/real", of_STORE_REAL, 1, {OA_FUNC_PTR,OA_NONE, OA_NONE} }, { "%store/reala", of_STORE_REALA, 2, {OA_ARR_PTR, OA_BIT1, OA_NONE} }, { "%store/str", of_STORE_STR, 1, {OA_FUNC_PTR,OA_NONE, OA_NONE} }, @@ -360,6 +391,8 @@ vvp_net_t* vvp_net_lookup(const char*label) { + static bool t0_trigger_generated = false; + /* First, look to see if the symbol is a vpi object of some sort. If it is, then get the vvp_ipoint_t pointer out of the vpiHandle. */ @@ -407,6 +440,23 @@ /* Failing that, look for a general functor. */ vvp_net_t*tmp = lookup_functor_symbol(label); + // This is a special label used to create a T0 trigger for the + // always_comb/latch processes. + if (! t0_trigger_generated && (strcmp(label, "E_0x0") == 0)) { + // This should never happen, but if it does then the E_0x0 + // event generation may need to be explicitly generated in + // the compiler output instead of implicitly in this code. + assert(! vpip_peek_current_scope()->is_automatic()); + t0_trigger_generated = true; + // Create a local event with no name for the T0 trigger + compile_named_event(strdup(label), strcpy(new char [1],""), true); + tmp = vvp_net_lookup(label); + assert(tmp); + // Create a trigger for the T0 event. + vvp_net_ptr_t ptr (tmp, 0); + schedule_t0_trigger(ptr); + } + return tmp; } @@ -455,7 +505,7 @@ */ struct vvp_net_resolv_list_s: public resolv_list_s { - vvp_net_resolv_list_s(char*l) : resolv_list_s(l) { } + explicit vvp_net_resolv_list_s(char*l) : resolv_list_s(l) { } // port to be driven by the located node. vvp_net_ptr_t port; virtual bool resolve(bool mes); @@ -629,10 +679,12 @@ */ struct code_label_resolv_list_s: public resolv_list_s { - code_label_resolv_list_s(char*lab) : resolv_list_s(lab) { + explicit code_label_resolv_list_s(char*lab, bool cptr2) : resolv_list_s(lab) { code = NULL; + cptr2_flag = cptr2; } struct vvp_code_s *code; + bool cptr2_flag; virtual bool resolve(bool mes); }; @@ -640,7 +692,7 @@ { symbol_value_t val = sym_get_value(sym_codespace, label()); if (val.ptr) { - if (code->opcode == of_FORK) + if (cptr2_flag) code->cptr2 = reinterpret_cast(val.ptr); else code->cptr = reinterpret_cast(val.ptr); @@ -653,10 +705,10 @@ return false; } -void code_label_lookup(struct vvp_code_s *code, char *label) +void code_label_lookup(struct vvp_code_s *code, char *label, bool cptr2) { struct code_label_resolv_list_s *res - = new struct code_label_resolv_list_s(label); + = new struct code_label_resolv_list_s(label, cptr2); res->code = code; @@ -664,7 +716,7 @@ } struct code_array_resolv_list_s: public resolv_list_s { - code_array_resolv_list_s(char*lab) : resolv_list_s(lab) { + explicit code_array_resolv_list_s(char*lab) : resolv_list_s(lab) { code = NULL; } struct vvp_code_s *code; @@ -1387,6 +1439,38 @@ make_arith(arith, label, argc, argv); } +void compile_cmp_weq(char*label, long wid, + unsigned argc, struct symb_s*argv) +{ + assert( wid > 0 ); + + if (argc != 2) { + fprintf(stderr, "%s .cmp/weq has wrong number of symbols\n",label); + compile_errors += 1; + return; + } + + vvp_arith_ *arith = new vvp_cmp_weq(wid); + + make_arith(arith, label, argc, argv); +} + +void compile_cmp_wne(char*label, long wid, + unsigned argc, struct symb_s*argv) +{ + assert( wid > 0 ); + + if (argc != 2) { + fprintf(stderr, "%s .cmp/wne has wrong number of symbols\n",label); + compile_errors += 1; + return; + } + + vvp_arith_ *arith = new vvp_cmp_wne(wid); + + make_arith(arith, label, argc, argv); +} + void compile_delay(char*label, unsigned width, vvp_delay_t*delay, struct symb_s arg) @@ -1459,8 +1543,8 @@ static struct __vpiModPathSrc*make_modpath_src(struct __vpiModPath*path, char edge, - struct symb_s src, - struct numbv_s vals, + const struct symb_s&src, + struct numbv_s&vals, bool ifnone) { vvp_fun_modpath*dst = path->modpath; @@ -1522,10 +1606,10 @@ } void compile_modpath_src(struct __vpiModPath*dst, char edge, - struct symb_s src, - struct numbv_s vals, - struct symb_s condit_src, - struct symb_s path_term_in) + const struct symb_s&src, + struct numbv_s&vals, + const struct symb_s&condit_src, + const struct symb_s&path_term_in) { struct __vpiModPathSrc*obj = make_modpath_src(dst, edge, src, vals, false); @@ -1534,10 +1618,10 @@ } void compile_modpath_src(struct __vpiModPath*dst, char edge, - struct symb_s src, - struct numbv_s vals, + const struct symb_s&src, + struct numbv_s&vals, int condit_src, - struct symb_s path_term_in, + const struct symb_s&path_term_in, bool ifnone) { assert(condit_src == 0); @@ -1726,13 +1810,15 @@ break; case OA_CODE_PTR: + case OA_CODE_PTR2: if (opa->argv[idx].ltype != L_SYMB) { yyerror("operand format"); break; } assert(opa->argv[idx].symb.idx == 0); - code_label_lookup(code, opa->argv[idx].symb.text); + code_label_lookup(code, opa->argv[idx].symb.text, + op->argt[idx]==OA_CODE_PTR2); break; case OA_FUNC_PTR: @@ -1807,42 +1893,6 @@ free(label); } - -void compile_disable(char*label, struct symb_s symb) -{ - if (label) - compile_codelabel(label); - - /* Fill in the basics of the %disable in the instruction. */ - vvp_code_t code = codespace_allocate(); - code->opcode = of_DISABLE; - - compile_vpi_lookup(&code->handle, symb.text); -} - -/* - * The %fork instruction is a little different from other instructions - * in that it has an extended field that holds the information needed - * to create the new thread. This includes the target PC and scope. - * I get these from the parser in the form of symbols. - */ -void compile_fork(char*label, struct symb_s dest, struct symb_s scope) -{ - if (label) - compile_codelabel(label); - - - /* Fill in the basics of the %fork in the instruction. */ - vvp_code_t code = codespace_allocate(); - code->opcode = of_FORK; - - /* Figure out the target PC. */ - code_label_lookup(code, dest.text); - - /* Figure out the target SCOPE. */ - compile_vpi_lookup(&code->handle, scope.text); -} - void compile_file_line(char*label, long file_idx, long lineno, char*description) { diff -Nru iverilog-10.3/vvp/compile.h iverilog-11.0/vvp/compile.h --- iverilog-10.3/vvp/compile.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/compile.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_compile_H #define IVL_compile_H /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -195,6 +195,10 @@ unsigned argc, struct symb_s*argv); extern void compile_cmp_gt(char*label, long width, bool signed_flag, unsigned argc, struct symb_s*argv); +extern void compile_cmp_weq(char*label, long width, + unsigned argc, struct symb_s*argv); +extern void compile_cmp_wne(char*label, long width, + unsigned argc, struct symb_s*argv); extern void compile_arith_mult_r(char*label, unsigned argc, struct symb_s*argv); @@ -225,6 +229,10 @@ struct symb_s arg_a, char*asc_value); +extern void compile_latch(char*label, unsigned width, + struct symb_s arg_d, + struct symb_s arg_e); + extern void compile_enum2_type(char*label, long width, bool signed_flag, std::list*names); extern void compile_enum4_type(char*label, long width, bool signed_flag, @@ -237,24 +245,24 @@ struct symb_s dest); extern void compile_modpath_src(__vpiModPath*dst, char edge, - struct symb_s input, - struct numbv_s d, - int condit_input, /* match with '0' */ - struct symb_s path_term_input, - bool ifnone); + const struct symb_s&src, + struct numbv_s&vals, + const struct symb_s&condit_src, + const struct symb_s&path_term_in); extern void compile_modpath_src(__vpiModPath*dst, char edge, - struct symb_s input, - struct numbv_s d, - struct symb_s condit_input, - struct symb_s path_term_input); - -extern void compile_reduce_and(char*label, struct symb_s arg); -extern void compile_reduce_or(char*label, struct symb_s arg); -extern void compile_reduce_xor(char*label, struct symb_s arg); -extern void compile_reduce_nand(char*label, struct symb_s arg); -extern void compile_reduce_nor(char*label, struct symb_s arg); -extern void compile_reduce_xnor(char*label, struct symb_s arg); + const struct symb_s&src, + struct numbv_s&vals, + int condit_src, /* match with '0' */ + const struct symb_s&path_term_in, + bool ifnone); + +extern void compile_reduce_and(char*label, const struct symb_s&arg); +extern void compile_reduce_or(char*label, const struct symb_s&arg); +extern void compile_reduce_xor(char*label, const struct symb_s&arg); +extern void compile_reduce_nand(char*label, const struct symb_s&arg); +extern void compile_reduce_nor(char*label, const struct symb_s&arg); +extern void compile_reduce_xnor(char*label, const struct symb_s&arg); extern void compile_extend_signed(char*label, long width, struct symb_s arg); @@ -339,8 +347,11 @@ * code points to a code structure that points to the instruction * field that receives the result, and the label is the name to * lookup. The lookup will free the label text when it is done. + * + * The cptr2 flag tells the lookup to write the code pointer into the + * cptr2 member of the code, instead of the cptr member. */ -extern void code_label_lookup(struct vvp_code_s *code, char *label); +extern void code_label_lookup(struct vvp_code_s *code, char *label, bool cptr2); /* * The `compile_udp_def' function creates a UDP. The `table' is a @@ -390,11 +401,14 @@ /* * Compile the .ufunc statement. */ -extern void compile_ufunc(char*label, char*code, unsigned wid, +extern void compile_ufunc_real(char*label, char*code, unsigned wid, unsigned argc, struct symb_s*argv, unsigned portc, struct symb_s*portv, - struct symb_s retv, char*scope_label, - char*trigger_label); + char*scope_label, char*trigger_label); +extern void compile_ufunc_vec4(char*label, char*code, unsigned wid, + unsigned argc, struct symb_s*argv, + unsigned portc, struct symb_s*portv, + char*scope_label, char*trigger_label); /* * The compile_event function takes the parts of the event statement @@ -404,7 +418,7 @@ */ extern void compile_event(char*label, char*type, unsigned argc, struct symb_s*argv); -extern void compile_named_event(char*label, char*type); +extern void compile_named_event(char*label, char*type, bool local_flag=false); /* @@ -434,7 +448,6 @@ typedef struct comp_operands_s*comp_operands_t; extern void compile_code(char*label, char*mnem, comp_operands_t opa); -extern void compile_disable(char*label, struct symb_s symb); extern void compile_file_line(char*label, long file_idx, long lineno, char*description); @@ -460,8 +473,6 @@ unsigned string_stack); extern void print_vpi_call_errors(); -extern void compile_fork(char*label, struct symb_s targ_s, - struct symb_s scope_s); extern void compile_codelabel(char*label); /* @@ -493,9 +504,9 @@ extern void compile_var_real(char*label, char*name); extern void compile_var_string(char*label, char*name); -extern void compile_var_darray(char*label, char*name); +extern void compile_var_darray(char*label, char*name, unsigned size); extern void compile_var_cobject(char*label, char*name); -extern void compile_var_queue(char*label, char*name); +extern void compile_var_queue(char*label, char*name, unsigned size); /* * This function is used to create a scope port @@ -536,9 +547,6 @@ int msb, int lsb, unsigned argc, struct symb_s*argv); -extern void compile_aliasw(char*label, char*array_symbol, - unsigned long array_addr, int msb, int lsb, - unsigned argc, struct symb_s*argv); extern void compile_island(char*label, char*type); extern void compile_island_port(char*label, char*island, char*src); @@ -548,7 +556,7 @@ extern void compile_island_tran(char*label); extern void compile_island_tranif(int sense, char*island, - char*ba, char*bb, char*src); + char*ba, char*bb, char*src, bool resistive); extern void compile_island_tranvp(char*island, char*ba, char*bb, unsigned width, unsigned part, unsigned off); diff -Nru iverilog-10.3/vvp/config.h.in iverilog-11.0/vvp/config.h.in --- iverilog-10.3/vvp/config.h.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/config.h.in 2020-09-26 22:44:25.000000000 +0000 @@ -195,4 +195,17 @@ #define TU "" #endif +#ifdef __MINGW32__ +# include +# include +static inline char*strndup(const char*s, size_t n) +{ + if (strlen(s) < n) return strdup(s); + char*tmp = (char*)malloc(n); + strncpy(tmp, s, n); + tmp[n-1] = 0; + return tmp; +} +#endif + #endif /* IVL_config_H */ diff -Nru iverilog-10.3/vvp/cppcheck.sup iverilog-11.0/vvp/cppcheck.sup --- iverilog-10.3/vvp/cppcheck.sup 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/cppcheck.sup 2020-09-26 22:44:25.000000000 +0000 @@ -1,3 +1,47 @@ // The new() operator is always used to allocate space for this class and // pool is defined there. uninitVar:vvp_net.cc:167 + +// These functions are not used by Icarus +// vpi_chk_error() +unusedFunction:vpi_priv.cc:193 +// vpi_compare_objects() +unusedFunction:vpi_priv.cc:209 +// vpi_control() +unusedFunction:vpi_priv.cc:1556 +// vpi_flush() +unusedFunction:vpi_priv.cc:1519 +// vpi_fopen() +unusedFunction:vpi_mcd.cc:287 +// vpi_get_file() +unusedFunction:vpi_mcd.cc:321 +// vpi_get_systf_info() +unusedFunction:vpi_priv.cc:225 +// vpi_get_time() +unusedFunction:vpi_priv.cc:532 +// vpi_get_userdata() +unusedFunction:vpi_tasks.cc:1020 +// vpi_get_vlog_info() +unusedFunction:vpi_priv.cc:561 +// vpi_handle_by_index() +unusedFunction:vpi_priv.cc:1273 +// vpi_handle_by_name() +unusedFunction:vpi_priv.cc:1356 +// vpi_mcd_close() +unusedFunction:vpi_mcd.cc:111 +// vpi_mcd_name() +unusedFunction:vpi_mcd.cc:138 +// vpi_mcd_open() +unusedFunction:vpi_mcd.cc:153 +// vpi_put_userdata() +unusedFunction:vpi_tasks.cc:1010 +// vpi_register_cb() +unusedFunction:vpi_callback.cc:552 +// vpi_register_systf() +unusedFunction:vpi_tasks.cc:990 +// vpi_remove_cb() +unusedFunction:vpi_callback.cc:601 +// vpi_sim_control() +unusedFunction:vpi_priv.cc:1548 +// vpi_vprintf() +unusedFunction:vpi_priv.cc:1505 diff -Nru iverilog-10.3/vvp/delay.cc iverilog-11.0/vvp/delay.cc --- iverilog-10.3/vvp/delay.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/delay.cc 2020-09-26 22:44:25.000000000 +0000 @@ -164,7 +164,7 @@ initial_ = true; // Calculate the values used when converting variable delays // to simulation time units. - struct __vpiScope*scope = vpip_peek_current_scope(); + __vpiScope*scope = vpip_peek_current_scope(); int pow = scope->time_units - scope->time_precision; round_ = 1; @@ -783,7 +783,7 @@ return rfp->dest->scope; case vpiModule: - { struct __vpiScope*scope = rfp->dest->scope; + { __vpiScope*scope = rfp->dest->scope; while (scope && scope->get_type_code() != vpiModule) scope = scope->scope; assert(scope); diff -Nru iverilog-10.3/vvp/delay.h iverilog-11.0/vvp/delay.h --- iverilog-10.3/vvp/delay.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/delay.h 2020-09-26 22:44:25.000000000 +0000 @@ -76,7 +76,7 @@ enum delay_type_t {UNKNOWN_DELAY, VEC4_DELAY, VEC8_DELAY, REAL_DELAY}; struct event_ { - event_(vvp_time64_t s) : sim_time(s) { + explicit event_(vvp_time64_t s) : sim_time(s) { ptr_real = 0.0; next = NULL; } @@ -197,7 +197,7 @@ friend class vvp_fun_modpath; public: - vvp_fun_modpath_src(vvp_time64_t d[12]); + explicit vvp_fun_modpath_src(vvp_time64_t d[12]); protected: ~vvp_fun_modpath_src(); diff -Nru iverilog-10.3/vvp/dff.cc iverilog-11.0/vvp/dff.cc --- iverilog-10.3/vvp/dff.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/dff.cc 2020-09-26 22:44:25.000000000 +0000 @@ -83,7 +83,7 @@ tmp = clk_; clk_ = bit.value(0); if (clk_ == clk_active_ && tmp != clk_active_) - port.ptr()->send_vec4(d_, 0); + schedule_propagate_vector(port.ptr(), 0, d_); break; case 2: // CE @@ -125,17 +125,17 @@ void vvp_dff_aclr::recv_async(vvp_net_ptr_t port) { - port.ptr()->send_vec4(vvp_vector4_t(d_.size(), BIT4_0), 0); + schedule_propagate_vector(port.ptr(), 0, vvp_vector4_t(d_.size(), BIT4_0)); } void vvp_dff_aset::recv_async(vvp_net_ptr_t port) { - port.ptr()->send_vec4(vvp_vector4_t(d_.size(), BIT4_1), 0); + schedule_propagate_vector(port.ptr(), 0, vvp_vector4_t(d_.size(), BIT4_1)); } void vvp_dff_asc::recv_async(vvp_net_ptr_t port) { - port.ptr()->send_vec4(asc_value_, 0); + schedule_propagate_vector(port.ptr(), 0, asc_value_); } void compile_dff(char*label, unsigned width, bool negedge, diff -Nru iverilog-10.3/vvp/event.cc iverilog-11.0/vvp/event.cc --- iverilog-10.3/vvp/event.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/event.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2020 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 @@ -343,29 +343,143 @@ } } +class anyedge_value { + + public: + anyedge_value() {}; + virtual ~anyedge_value() {}; + + virtual void reset() = 0; + + virtual void duplicate(anyedge_value*&dup) = 0; +}; + +class anyedge_vec4_value : public anyedge_value { + + public: + anyedge_vec4_value() {}; + virtual ~anyedge_vec4_value() {}; + + void reset() { old_bits.set_to_x(); } + + void set(const vvp_vector4_t&bit) { old_bits = bit; }; + + void duplicate(anyedge_value*&dup); + + bool recv_vec4(const vvp_vector4_t&bit); + + bool recv_vec4_pv(const vvp_vector4_t&bit, unsigned base, + unsigned wid, unsigned vwid); + + private: + vvp_vector4_t old_bits; +}; + +static anyedge_vec4_value*get_vec4_value(anyedge_value*&value) +{ + anyedge_vec4_value*vec4_value = dynamic_cast(value); + if (!value) { + vec4_value = new anyedge_vec4_value(); + delete value; + value = vec4_value; + } + return vec4_value; +} + +class anyedge_real_value : public anyedge_value { + + public: + anyedge_real_value() : old_bits(0.0) {}; + virtual ~anyedge_real_value() {}; + + void reset() { old_bits = 0.0; } + + void set(double bit) { old_bits = bit; }; + + void duplicate(anyedge_value*&dup); + + bool recv_real(double bit); + + private: + double old_bits; +}; + +static anyedge_real_value*get_real_value(anyedge_value*&value) +{ + anyedge_real_value*real_value = dynamic_cast(value); + if (!value) { + real_value = new anyedge_real_value(); + delete value; + value = real_value; + } + return real_value; +} + +class anyedge_string_value : public anyedge_value { + + public: + anyedge_string_value() {}; + virtual ~anyedge_string_value() {}; + + void reset() { old_bits.clear(); } + + void set(const std::string&bit) { old_bits = bit; }; + + void duplicate(anyedge_value*&dup); + + bool recv_string(const std::string&bit); + + private: + std::string old_bits; +}; + +static anyedge_string_value*get_string_value(anyedge_value*&value) +{ + anyedge_string_value*string_value = dynamic_cast(value); + if (!value) { + string_value = new anyedge_string_value(); + delete value; + value = string_value; + } + return string_value; +} + struct vvp_fun_anyedge_state_s : public waitable_state_s { vvp_fun_anyedge_state_s() { for (unsigned idx = 0 ; idx < 4 ; idx += 1) - bitsr[idx] = 0.0; + last_value_[idx] = 0; } - vvp_vector4_t bits[4]; - double bitsr[4]; + ~vvp_fun_anyedge_state_s() + { + for (unsigned idx = 0 ; idx < 4 ; idx += 1) + delete last_value_[idx]; + } + + anyedge_value *last_value_[4]; }; vvp_fun_anyedge::vvp_fun_anyedge() { for (unsigned idx = 0 ; idx < 4 ; idx += 1) - bitsr_[idx] = 0.0; + last_value_[idx] = 0; } vvp_fun_anyedge::~vvp_fun_anyedge() { + for (unsigned idx = 0 ; idx < 4 ; idx += 1) + delete last_value_[idx]; } -bool vvp_fun_anyedge::recv_vec4_(const vvp_vector4_t&bit, - vvp_vector4_t&old_bits, vthread_t&threads) +void anyedge_vec4_value::duplicate(anyedge_value*&dup) +{ + anyedge_vec4_value*dup_vec4 = get_vec4_value(dup); + assert(dup_vec4); + dup_vec4->set(old_bits); +} + +bool anyedge_vec4_value::recv_vec4(const vvp_vector4_t&bit) { bool flag = false; @@ -396,18 +510,52 @@ if (flag) { old_bits = bit; - run_waiting_threads_(threads); } return flag; } -bool vvp_fun_anyedge::recv_real_(double bit, - double&old_bits, vthread_t&threads) +bool anyedge_vec4_value::recv_vec4_pv(const vvp_vector4_t&bit, unsigned base, + unsigned wid, unsigned vwid) +{ + vvp_vector4_t tmp = old_bits; + if (tmp.size() == 0) + tmp = vvp_vector4_t(vwid, BIT4_Z); + assert(wid == bit.size()); + assert(base+wid <= vwid); + assert(tmp.size() == vwid); + tmp.set_vec(base, bit); + + return recv_vec4(tmp); +} + +void anyedge_real_value::duplicate(anyedge_value*&dup) +{ + anyedge_real_value*dup_real = get_real_value(dup); + assert(dup_real); + dup_real->set(old_bits); +} + +bool anyedge_real_value::recv_real(double bit) +{ + if (old_bits != bit) { + old_bits = bit; + return true; + } + return false; +} + +void anyedge_string_value::duplicate(anyedge_value*&dup) +{ + anyedge_string_value*dup_string = get_string_value(dup); + assert(dup_string); + dup_string->set(old_bits); +} + +bool anyedge_string_value::recv_string(const std::string&bit) { if (old_bits != bit) { old_bits = bit; - run_waiting_threads_(threads); return true; } return false; @@ -433,7 +581,10 @@ void vvp_fun_anyedge_sa::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_context_t) { - if (recv_vec4_(bit, bits_[port.port()], threads_)) { + anyedge_vec4_value*value = get_vec4_value(last_value_[port.port()]); + assert(value); + if (value->recv_vec4(bit)) { + run_waiting_threads_(threads_); vvp_net_t*net = port.ptr(); net->send_vec4(bit, 0); } @@ -443,15 +594,10 @@ unsigned base, unsigned wid, unsigned vwid, vvp_context_t) { - vvp_vector4_t tmp = bits_[port.port()]; - if (tmp.size() == 0) - tmp = vvp_vector4_t(vwid, BIT4_Z); - assert(wid == bit.size()); - assert(base+wid <= vwid); - assert(tmp.size() == vwid); - tmp.set_vec(base, bit); - - if (recv_vec4_(tmp, bits_[port.port()], threads_)) { + anyedge_vec4_value*value = get_vec4_value(last_value_[port.port()]); + assert(value); + if (value->recv_vec4_pv(bit, base, wid, vwid)) { + run_waiting_threads_(threads_); vvp_net_t*net = port.ptr(); net->send_vec4(bit, 0); } @@ -460,7 +606,22 @@ void vvp_fun_anyedge_sa::recv_real(vvp_net_ptr_t port, double bit, vvp_context_t) { - if (recv_real_(bit, bitsr_[port.port()], threads_)) { + anyedge_real_value*value = get_real_value(last_value_[port.port()]); + assert(value); + if (value->recv_real(bit)) { + run_waiting_threads_(threads_); + vvp_net_t*net = port.ptr(); + net->send_vec4(vvp_vector4_t(), 0); + } +} + +void vvp_fun_anyedge_sa::recv_string(vvp_net_ptr_t port, const std::string&bit, + vvp_context_t) +{ + anyedge_string_value*value = get_string_value(last_value_[port.port()]); + assert(value); + if (value->recv_string(bit)) { + run_waiting_threads_(threads_); vvp_net_t*net = port.ptr(); net->send_vec4(vvp_vector4_t(), 0); } @@ -489,8 +650,10 @@ state->threads = 0; for (unsigned idx = 0 ; idx < 4 ; idx += 1) { - state->bits[idx] = bits_[idx]; - state->bitsr[idx] = bitsr_[idx]; + if (last_value_[idx]) + last_value_[idx]->duplicate(state->last_value_[idx]); + else if (state->last_value_[idx]) + state->last_value_[idx]->reset(); } } @@ -521,7 +684,10 @@ vvp_fun_anyedge_state_s*state = static_cast (vvp_get_context_item(context, context_idx_)); - if (recv_vec4_(bit, state->bits[port.port()], state->threads)) { + anyedge_vec4_value*value = get_vec4_value(state->last_value_[port.port()]); + assert(value); + if (value->recv_vec4(bit)) { + run_waiting_threads_(state->threads); vvp_net_t*net = port.ptr(); net->send_vec4(bit, context); } @@ -531,7 +697,9 @@ recv_vec4(port, bit, context); context = vvp_get_next_context(context); } - bits_[port.port()] = bit; + anyedge_vec4_value*value = get_vec4_value(last_value_[port.port()]); + assert(value); + value->set(bit); } } @@ -542,7 +710,10 @@ vvp_fun_anyedge_state_s*state = static_cast (vvp_get_context_item(context, context_idx_)); - if (recv_real_(bit, state->bitsr[port.port()], state->threads)) { + anyedge_real_value*value = get_real_value(state->last_value_[port.port()]); + assert(value); + if (value->recv_real(bit)) { + run_waiting_threads_(state->threads); vvp_net_t*net = port.ptr(); net->send_vec4(vvp_vector4_t(), context); } @@ -552,7 +723,35 @@ recv_real(port, bit, context); context = vvp_get_next_context(context); } - bitsr_[port.port()] = bit; + anyedge_real_value*value = get_real_value(last_value_[port.port()]); + assert(value); + value->set(bit); + } +} + +void vvp_fun_anyedge_aa::recv_string(vvp_net_ptr_t port, const std::string&bit, + vvp_context_t context) +{ + if (context) { + vvp_fun_anyedge_state_s*state = static_cast + (vvp_get_context_item(context, context_idx_)); + + anyedge_string_value*value = get_string_value(state->last_value_[port.port()]); + assert(value); + if (value->recv_string(bit)) { + run_waiting_threads_(state->threads); + vvp_net_t*net = port.ptr(); + net->send_vec4(vvp_vector4_t(), context); + } + } else { + context = context_scope_->live_contexts; + while (context) { + recv_string(port, bit, context); + context = vvp_get_next_context(context); + } + anyedge_string_value*value = get_string_value(last_value_[port.port()]); + assert(value); + value->set(bit); } } @@ -768,7 +967,7 @@ free(type); - if (vpip_peek_current_scope()->is_automatic) { + if (vpip_peek_current_scope()->is_automatic()) { fun = new vvp_fun_anyedge_aa; } else { fun = new vvp_fun_anyedge_sa; @@ -786,7 +985,7 @@ assert(argc <= 4); free(type); - if (vpip_peek_current_scope()->is_automatic) { + if (vpip_peek_current_scope()->is_automatic()) { fun = new vvp_fun_edge_aa(edge); } else { fun = new vvp_fun_edge_sa(edge); @@ -807,7 +1006,7 @@ static void compile_event_or(char*label, unsigned argc, struct symb_s*argv) { vvp_net_t* ptr = new vvp_net_t; - if (vpip_peek_current_scope()->is_automatic) { + if (vpip_peek_current_scope()->is_automatic()) { ptr->fun = new vvp_fun_event_or_aa; } else { ptr->fun = new vvp_fun_event_or_sa; @@ -829,20 +1028,20 @@ * inputs, it is only accessed by behavioral trigger statements, which * in vvp are %set instructions. */ -void compile_named_event(char*label, char*name) +void compile_named_event(char*label, char*name, bool local_flag) { vvp_net_t*ptr = new vvp_net_t; vpiHandle obj = vpip_make_named_event(name, ptr); - if (vpip_peek_current_scope()->is_automatic) { + if (vpip_peek_current_scope()->is_automatic()) { ptr->fun = new vvp_named_event_aa(obj); } else { ptr->fun = new vvp_named_event_sa(obj); } define_functor_symbol(label, ptr); compile_vpi_symbol(label, obj); - vpip_attach_to_current_scope(obj); + if (! local_flag) vpip_attach_to_current_scope(obj); free(label); delete[] name; diff -Nru iverilog-10.3/vvp/event.h iverilog-11.0/vvp/event.h --- iverilog-10.3/vvp/event.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/event.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_event_H #define IVL_event_H /* - * Copyright (c) 2004-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2004-2020 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 @@ -212,11 +212,17 @@ vvp_context_t context); private: - struct __vpiScope*context_scope_; + __vpiScope*context_scope_; unsigned context_idx_; }; /* + * This is the base object for storing the last received values for a + * vvp_fun_anyedge functor. + */ +class anyedge_value; + +/* * The vvp_fun_anyedge functor checks to see if any value in an input * vector changes. Unlike the vvp_fun_edge, which watches for the LSB * of its inputs to change in a particular direction, the anyedge @@ -233,14 +239,7 @@ virtual ~vvp_fun_anyedge(); protected: - bool recv_vec4_(const vvp_vector4_t&bit, - vvp_vector4_t&old_bits, vthread_t&threads); - bool recv_real_(double bit, - double&old_bits, vthread_t&threads); - - vvp_vector4_t bits_[4]; - // In case I'm a real-valued event. - double bitsr_[4]; + anyedge_value*last_value_[4]; }; /* @@ -263,6 +262,9 @@ void recv_real(vvp_net_ptr_t port, double bit, vvp_context_t context); + void recv_string(vvp_net_ptr_t port, const std::string&bit, + vvp_context_t context); + private: vthread_t threads_; }; @@ -290,8 +292,11 @@ void recv_real(vvp_net_ptr_t port, double bit, vvp_context_t context); + void recv_string(vvp_net_ptr_t port, const std::string&bit, + vvp_context_t context); + private: - struct __vpiScope*context_scope_; + __vpiScope*context_scope_; unsigned context_idx_; }; @@ -345,7 +350,7 @@ vvp_context_t context); private: - struct __vpiScope*context_scope_; + __vpiScope*context_scope_; unsigned context_idx_; }; diff -Nru iverilog-10.3/vvp/examples/assign_reg.vvp iverilog-11.0/vvp/examples/assign_reg.vvp --- iverilog-10.3/vvp/examples/assign_reg.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/assign_reg.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/copy.vvp iverilog-11.0/vvp/examples/copy.vvp --- iverilog-10.3/vvp/examples/copy.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/copy.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/disable.vvp iverilog-11.0/vvp/examples/disable.vvp --- iverilog-10.3/vvp/examples/disable.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/disable.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/edge.vvp iverilog-11.0/vvp/examples/edge.vvp --- iverilog-10.3/vvp/examples/edge.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/edge.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/fork.vvp iverilog-11.0/vvp/examples/fork.vvp --- iverilog-10.3/vvp/examples/fork.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/fork.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/hello2.vvp iverilog-11.0/vvp/examples/hello2.vvp --- iverilog-10.3/vvp/examples/hello2.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/hello2.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/hello.vvp iverilog-11.0/vvp/examples/hello.vvp --- iverilog-10.3/vvp/examples/hello.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/hello.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/memory.vvp iverilog-11.0/vvp/examples/memory.vvp --- iverilog-10.3/vvp/examples/memory.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/memory.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/set_reg.vvp iverilog-11.0/vvp/examples/set_reg.vvp --- iverilog-10.3/vvp/examples/set_reg.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/set_reg.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/sum.vvp iverilog-11.0/vvp/examples/sum.vvp --- iverilog-10.3/vvp/examples/sum.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/sum.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/time.vvp iverilog-11.0/vvp/examples/time.vvp --- iverilog-10.3/vvp/examples/time.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/time.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) diff -Nru iverilog-10.3/vvp/examples/vector.vvp iverilog-11.0/vvp/examples/vector.vvp --- iverilog-10.3/vvp/examples/vector.vvp 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/examples/vector.vvp 2020-09-26 22:44:25.000000000 +0000 @@ -1,4 +1,4 @@ -:ivl_version "10.0" "vec4-stack"; +:ivl_version "11.0" "vec4-stack"; :vpi_module "system"; ; This program is free software; you can redistribute it and/or modify diff -Nru iverilog-10.3/vvp/file_line.cc iverilog-11.0/vvp/file_line.cc --- iverilog-10.3/vvp/file_line.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/file_line.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011-2012 Cary R. (cygcary@yahoo.com) + * Copyright (C) 2011-2020 Cary R. (cygcary@yahoo.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -80,8 +80,7 @@ { struct __vpiFileLine*obj = new struct __vpiFileLine; - /* Turn on the diagnostic output when we find a %file_line. */ - show_file_line = true; + /* You can turn on the diagnostic output if we find a %file_line. */ code_is_instrumented = true; if (description) obj->description = vpip_name_string(description); diff -Nru iverilog-10.3/vvp/island_tran.cc iverilog-11.0/vvp/island_tran.cc --- iverilog-10.3/vvp/island_tran.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/island_tran.cc 2020-09-26 22:44:25.000000000 +0000 @@ -401,7 +401,8 @@ compile_island_base(label, use_island); } -void compile_island_tranif(int sense, char*island, char*pa, char*pb, char*pe) +void compile_island_tranif(int sense, char*island, char*pa, char*pb, char*pe, + bool resistive) { vvp_island*use_island = compile_find_island(island); assert(use_island); @@ -418,7 +419,7 @@ vvp_island_branch_tran*br = new vvp_island_branch_tran(en, sense ? true : false, - 0, 0, 0, false); + 0, 0, 0, resistive); use_island->add_branch(br, pa, pb); diff -Nru iverilog-10.3/vvp/latch.cc iverilog-11.0/vvp/latch.cc --- iverilog-10.3/vvp/latch.cc 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/vvp/latch.cc 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016 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 "compile.h" +# include "schedule.h" +# include "latch.h" +# include +# include +# include +# include +# include + +/* We need to ensure an initial output value is propagated. This is + achieved by scheduling an initial value to be sent to port 3. Any + value received on port 3 will propagate an initial value of 'bx. */ + +vvp_latch::vvp_latch(unsigned width) +: en_(BIT4_X), d_(width, BIT4_X) +{ +} + +vvp_latch::~vvp_latch() +{ +} + +void vvp_latch::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, + vvp_context_t) +{ + vvp_bit4_t tmp; + + switch (port.port()) { + + case 0: // D + d_ = bit; + if (en_ == BIT4_1) + schedule_propagate_vector(port.ptr(), 0, d_); + break; + + case 1: // EN + assert(bit.size() == 1); + tmp = en_; + en_ = bit.value(0); + if (en_ == BIT4_1 && tmp != BIT4_1) + schedule_propagate_vector(port.ptr(), 0, d_); + break; + + case 2: + assert(0); + break; + + case 3: + port.ptr()->send_vec4(vvp_vector4_t(d_.size(), BIT4_X), 0); + break; + } +} + +void vvp_latch::recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx) +{ + recv_vec4_pv_(ptr, bit, base, wid, vwid, ctx); +} + +void compile_latch(char*label, unsigned width, + struct symb_s arg_d, + struct symb_s arg_e) +{ + vvp_net_t*ptr = new vvp_net_t; + vvp_latch*fun = new vvp_latch(width); + + ptr->fun = fun; + define_functor_symbol(label, ptr); + free(label); + input_connect(ptr, 0, arg_d.text); + input_connect(ptr, 1, arg_e.text); + + vvp_vector4_t init_val = vvp_vector4_t(1, BIT4_1); + schedule_init_vector(vvp_net_ptr_t(ptr,3), init_val); +} diff -Nru iverilog-10.3/vvp/latch.h iverilog-11.0/vvp/latch.h --- iverilog-10.3/vvp/latch.h 1970-01-01 00:00:00.000000000 +0000 +++ iverilog-11.0/vvp/latch.h 2020-09-26 22:44:25.000000000 +0000 @@ -0,0 +1,49 @@ +#ifndef IVL_latch_H +#define IVL_latch_H +/* + * Copyright (c) 2016 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 "vvp_net.h" + +/* + * The vvp_latch implements an arbitrary width D-type transparent latch. + * The latch enable is a single bit. Ports are: + * + * port-0: D input + * port-1: EN input + */ +class vvp_latch : public vvp_net_fun_t { + + public: + explicit vvp_latch(unsigned width); + ~vvp_latch(); + + void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, + vvp_context_t); + + void recv_vec4_pv(vvp_net_ptr_t ptr, const vvp_vector4_t&bit, + unsigned base, unsigned wid, unsigned vwid, + vvp_context_t ctx); + + private: + vvp_bit4_t en_; + vvp_vector4_t d_; +}; + +#endif /* IVL_latch_H */ diff -Nru iverilog-10.3/vvp/lexor.lex iverilog-11.0/vvp/lexor.lex --- iverilog-10.3/vvp/lexor.lex 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/lexor.lex 2020-09-26 22:44:25.000000000 +0000 @@ -4,7 +4,7 @@ %{ /* - * Copyright (c) 2001-2017 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2018 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 @@ -102,8 +102,6 @@ /* These are some keywords that are recognized. */ -".alias" { return K_ALIAS; } -".alias/real" { return K_ALIAS_R; } ".abs" { return K_ARITH_ABS; } ".arith/div" { return K_ARITH_DIV; } ".arith/div.r" { return K_ARITH_DIV_R; } @@ -148,6 +146,8 @@ ".cmp/gt" { return K_CMP_GT; } ".cmp/gt.r" { return K_CMP_GT_R; } ".cmp/gt.s" { return K_CMP_GT_S; } +".cmp/weq" { return K_CMP_WEQ; } +".cmp/wne" { return K_CMP_WNE; } ".concat" { return K_CONCAT; } ".concat8" { return K_CONCAT8; } ".delay" { return K_DELAY; } @@ -168,6 +168,7 @@ ".functor" { return K_FUNCTOR; } ".import" { return K_IMPORT; } ".island" { return K_ISLAND; } +".latch" { return K_LATCH; } ".modpath" { return K_MODPATH; } ".net" { return K_NET; } ".net/2s" { return K_NET_2S; } @@ -195,6 +196,9 @@ ".reduce/xnor" { return K_REDUCE_XNOR; } ".repeat" { return K_REPEAT; } ".resolv" { return K_RESOLV; } +".rtran" { return K_RTRAN; } +".rtranif0" { return K_RTRANIF0; } +".rtranif1" { return K_RTRANIF1; } ".scope" { return K_SCOPE; } ".sfunc" { return K_SFUNC; } ".sfunc/e" { return K_SFUNC_E; } @@ -208,7 +212,8 @@ ".tranif0" { return K_TRANIF0; } ".tranif1" { return K_TRANIF1; } ".tranvp" { return K_TRANVP; } -".ufunc" { return K_UFUNC; } +".ufunc/real" { return K_UFUNC_REAL; } +".ufunc/vec4" { return K_UFUNC_VEC4; } ".ufunc/e" { return K_UFUNC_E; } ".var" { return K_VAR; } ".var/cobj" { return K_VAR_COBJECT; } @@ -234,8 +239,7 @@ "%vpi_call/i" { return K_vpi_call_i; } "%vpi_func" { return K_vpi_func; } "%vpi_func/r" { return K_vpi_func_r; } -"%disable" { return K_disable; } -"%fork" { return K_fork; } +"%vpi_func/s" { return K_vpi_func_s; } "%file_line" { return K_file_line; } /* Handle the specialized variable access functions. */ diff -Nru iverilog-10.3/vvp/libvpi.c iverilog-11.0/vvp/libvpi.c --- iverilog-10.3/vvp/libvpi.c 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/libvpi.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2003 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/* - * Things that should be statically linked by VPI modules go here. - */ - -void __libvpi_c_dummy_function(void) -{ -} diff -Nru iverilog-10.3/vvp/logic.cc iverilog-11.0/vvp/logic.cc --- iverilog-10.3/vvp/logic.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/logic.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -110,6 +110,54 @@ ptr->send_vec4(result, 0); } +vvp_fun_equiv::vvp_fun_equiv() +: vvp_fun_boolean_(1) +{ + count_functors_logic += 1; +} + +vvp_fun_equiv::~vvp_fun_equiv() +{ +} + +void vvp_fun_equiv::run_run() +{ + vvp_net_t*ptr = net_; + net_ = 0; + + assert(input_[0].size() == 1); + assert(input_[1].size() == 1); + + vvp_bit4_t bit = ~(input_[0].value(0) ^ input_[1].value(0)); + vvp_vector4_t result (1, bit); + + ptr->send_vec4(result, 0); +} + +vvp_fun_impl::vvp_fun_impl() +: vvp_fun_boolean_(1) +{ + count_functors_logic += 1; +} + +vvp_fun_impl::~vvp_fun_impl() +{ +} + +void vvp_fun_impl::run_run() +{ + vvp_net_t*ptr = net_; + net_ = 0; + + assert(input_[0].size() == 1); + assert(input_[1].size() == 1); + + vvp_bit4_t bit = ~input_[0].value(0) | input_[1].value(0); + vvp_vector4_t result (1, bit); + + ptr->send_vec4(result, 0); +} + vvp_fun_buf::vvp_fun_buf(unsigned wid) : input_(wid, BIT4_Z) { @@ -600,6 +648,12 @@ obj = new vvp_fun_bufif(false,false, ostr0, ostr1); strength_aware = true; + } else if (strcmp(type, "EQUIV") == 0) { + obj = new vvp_fun_equiv(); + + } else if (strcmp(type, "IMPL") == 0) { + obj = new vvp_fun_impl(); + } else if (strcmp(type, "NAND") == 0) { obj = new vvp_fun_and(width, true); diff -Nru iverilog-10.3/vvp/logic.h iverilog-11.0/vvp/logic.h --- iverilog-10.3/vvp/logic.h 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/logic.h 2020-09-26 22:44:25.000000000 +0000 @@ -1,7 +1,7 @@ #ifndef IVL_logic_H #define IVL_logic_H /* - * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 2000-2020 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 @@ -54,6 +54,26 @@ bool invert_; }; +class vvp_fun_equiv : public vvp_fun_boolean_ { + + public: + explicit vvp_fun_equiv(); + ~vvp_fun_equiv(); + + private: + void run_run(); +}; + +class vvp_fun_impl : public vvp_fun_boolean_ { + + public: + explicit vvp_fun_impl(); + ~vvp_fun_impl(); + + private: + void run_run(); +}; + /* * The buffer functor is a very primitive functor that takes the input * from port-0 (and only port-0) and retransmits it as a vvp_vector4_t. diff -Nru iverilog-10.3/vvp/main.cc iverilog-11.0/vvp/main.cc --- iverilog-10.3/vvp/main.cc 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/main.cc 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 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 @@ -262,43 +262,6 @@ extern bool stop_is_finish; extern int stop_is_finish_exit_code; -#ifdef __MINGW32__ - /* Calculate the module path from the path to the command. - This is necessary because of the installation process on - Windows. Mostly, it is those darn drive letters, but oh - well. We know the command path is formed like this: - - D:\iverilog\bin\iverilog.exe - - The IVL_ROOT in a Windows installation is the path: - - D:\iverilog\lib\ivl$(suffix) - - so we chop the file name and the last directory by - turning the last two \ characters to null. Then we append - the lib\ivl$(suffix) to finish. */ - char *s; - char basepath[4096], tmp[4096]; - GetModuleFileName(NULL, tmp, sizeof tmp); - /* Convert to a short name to remove any embedded spaces. */ - GetShortPathName(tmp, basepath, sizeof basepath); - s = strrchr(basepath, '\\'); - if (s) *s = 0; - else { - fprintf(stderr, "%s: Missing first \\ in exe path!\n", argv[0]); - exit(1); - } - s = strrchr(basepath, '\\'); - if (s) *s = 0; - else { - fprintf(stderr, "%s: Missing second \\ in exe path!\n", argv[0]); - exit(1); - } - strcat(s, "\\lib\\ivl" IVL_SUFFIX); - vpip_module_path[0] = strdup(basepath); -#endif - - if( ::getenv("VVP_WAIT_FOR_DEBUGGER") != 0 ) { fprintf( stderr, "Waiting for debugger...\n"); bool debugger_release = false; @@ -340,10 +303,9 @@ break; case 'M': if (strcmp(optarg,"-") == 0) { - vpip_module_path_cnt = 0; - vpip_module_path[0] = 0; + vpip_clear_module_paths(); } else { - vpip_module_path[vpip_module_path_cnt++] = optarg; + vpip_add_module_path(optarg); } break; case 'm': @@ -369,13 +331,15 @@ flag_errors += 1; } + vpip_add_env_and_default_module_paths(); + if (flag_errors) return flag_errors; if (version_flag) { fprintf(stderr, "Icarus Verilog runtime version " VERSION " (" VERSION_TAG ")\n\n"); - fprintf(stderr, "Copyright 1998-2015 Stephen Williams\n\n"); + fprintf(stderr, "Copyright 1998-2020 Stephen Williams\n\n"); fprintf(stderr, " This program is free software; you can redistribute it and/or modify\n" " it under the terms of the GNU General Public License as published by\n" diff -Nru iverilog-10.3/vvp/Makefile.in iverilog-11.0/vvp/Makefile.in --- iverilog-10.3/vvp/Makefile.in 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/Makefile.in 2020-09-26 22:44:25.000000000 +0000 @@ -35,11 +35,9 @@ HOSTCC = @CC@ HOSTCFLAGS = @WARNING_FLAGS@ @WARNING_FLAGS_CC@ @CFLAGS@ -CC = @CC@ +BUILDCC = @CC_FOR_BUILD@ +BUILDEXT = @BUILD_EXEEXT@ CXX = @CXX@ -DLLTOOL = @DLLTOOL@ -AR = @AR@ -RANLIB = @RANLIB@ INSTALL = @INSTALL@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -65,14 +63,14 @@ MDIR1 = -DMODULE_DIR1='"$(libdir)/ivl$(suffix)"' -VPI = vpi_modules.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \ +VPI = vpi_modules.o vpi_bit.o vpi_callback.o vpi_cobject.o vpi_const.o vpi_darray.o \ vpi_event.o vpi_iter.o vpi_mcd.o \ vpi_priv.o vpi_scope.o vpi_real.o vpi_signal.o vpi_string.o vpi_tasks.o vpi_time.o \ vpi_vthr_vector.o vpip_bin.o vpip_hex.o vpip_oct.o \ vpip_to_dec.o vpip_format.o vvp_vpi.o O = main.o parse.o parse_misc.o lexor.o arith.o array_common.o array.o bufif.o compile.o \ - concat.o dff.o class_type.o enum_type.o extend.o file_line.o npmos.o part.o \ + concat.o dff.o class_type.o enum_type.o extend.o file_line.o latch.o npmos.o part.o \ permaheap.o reduce.o resolv.o \ sfunc.o stop.o \ substitute.o \ @@ -81,7 +79,7 @@ vvp_object.o vvp_cobject.o vvp_darray.o event.o logic.o delay.o \ words.o island_tran.o $(VPI) -all: dep vvp@EXEEXT@ libvpi.a vvp.man +all: dep vvp@EXEEXT@ vvp.man check: all ifeq (@WIN32@,yes) @@ -100,14 +98,15 @@ clean: rm -f *.o *~ parse.cc parse.h lexor.cc tables.cc - rm -rf dep vvp@EXEEXT@ libvpi.a parse.output vvp.man vvp.ps vvp.pdf vvp.exp + rm -rf dep vvp@EXEEXT@ parse.output vvp.man vvp.ps vvp.pdf vvp.exp distclean: clean rm -f Makefile config.log rm -f stamp-config-h config.h -cppcheck: $(O:.o=.cc) libvpi.c draw_tt.c - cppcheck --enable=all -f --suppressions-list=$(srcdir)/cppcheck.sup \ +cppcheck: $(O:.o=.cc) draw_tt.c + cppcheck --enable=all --std=posix --std=c99 --std=c++03 -f \ + --suppressions-list=$(srcdir)/cppcheck.sup \ -UMODULE_DIR1 -UMODULE_DIR2 -UYY_USER_INIT \ -UYYPARSE_PARAM -UYYPRINT -Ushort -Usize_t -Uyyoverflow \ -UYYTYPE_INT8 -UYYTYPE_INT16 -UYYTYPE_UINT8 -UYYTYPE_UINT16 \ @@ -119,30 +118,8 @@ dep: mkdir dep -ifeq (@WIN32@,yes) -# Under Windows (mingw) I need to make the vvp.exe in two steps. -# The first step makes an vvp.exe that dlltool can use to make an -# export and import library, and the last link makes a, vvp.exe -# that really exports the things that the import library imports. -# -# To get this to work correctly we must use the suffixed version of the -# executable to build the library. -vvp@EXEEXT@ libvpi.a: $O $(srcdir)/vvp.def - $(CXX) -o vvp$(suffix)@EXEEXT@ $(LDFLAGS) $O $(dllib) $(LIBS) - $(DLLTOOL) --dllname vvp$(suffix)@EXEEXT@ --def $(srcdir)/vvp.def \ - --output-lib libvpi.a --output-exp vvp.exp - rm -f vvp$(suffix)@EXEEXT@ - $(CXX) $(LDFLAGS) -o vvp@EXEEXT@ vvp.exp $(LDFLAGS) $O $(dllib) $(LIBS) -else -libvpi.a: libvpi.c - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< - rm -f libvpi.a - $(AR) cqv libvpi.a libvpi.o - $(RANLIB) libvpi.a - vvp@EXEEXT@: $O $(CXX) $(LDFLAGS) -o vvp@EXEEXT@ $O $(LIBS) $(dllib) -endif %.o: %.cc config.h $(CXX) $(CPPFLAGS) -DIVL_SUFFIX='"$(suffix)"' $(MDIR1) $(MDIR2) $(CXXFLAGS) @DEPENDENCY_FLAG@ -c $< -o $*.o @@ -153,9 +130,9 @@ mv $*.d dep/$*.d tables.cc: $(srcdir)/draw_tt.c - $(HOSTCC) $(HOSTCFLAGS) -o draw_tt.exe $(srcdir)/draw_tt.c - ./draw_tt.exe > tables.cc - rm draw_tt.exe + $(CC) $(CFLAGS) -o draw_tt$(BUILDEXT) $(srcdir)/draw_tt.c + ./draw_tt$(BUILDEXT) > tables.cc + rm draw_tt$(BUILDEXT) lexor.o: lexor.cc parse.h @@ -163,12 +140,9 @@ tables.o: tables.cc -# Build this in two steps to avoid parallel build issues (see pr3462585) -parse.cc: $(srcdir)/parse.y - $(YACC) --verbose -t -d -o $@ $< -parse.h: parse.cc - mv parse.cc.h $@ 2>/dev/null || mv parse.hh $@ - touch $@ +# Use pattern rules to avoid parallel build issues (see pr3462585) +parse%cc parse%h: $(srcdir)/parse%y + $(YACC) --verbose -t --defines=parse.h -o parse.cc $< lexor.cc: $(srcdir)/lexor.lex $(LEX) -s -olexor.cc $(srcdir)/lexor.lex @@ -185,18 +159,18 @@ ifeq (@MINGW32@,yes) ifeq ($(MAN),none) -INSTALL_DOC = $(mandir)/man1/vvp$(suffix).1 +INSTALL_DOC = installman else ifeq ($(PS2PDF),none) -INSTALL_DOC = $(mandir)/man1/vvp$(suffix).1 +INSTALL_DOC = installman else -INSTALL_DOC = $(prefix)/vvp$(suffix).pdf $(mandir)/man1/vvp$(suffix).1 +INSTALL_DOC = installpdf installman all: vvp.pdf endif endif INSTALL_DOCDIR = $(mandir)/man1 else -INSTALL_DOC = $(mandir)/man1/vvp$(suffix).1 +INSTALL_DOC = installman INSTALL_DOCDIR = $(mandir)/man1 endif @@ -205,27 +179,25 @@ cd ..; ./config.status --header=vvp/config.h config.h: stamp-config-h -install: all installdirs $(bindir)/vvp$(suffix)@EXEEXT@ $(libdir)/libvpi$(suffix).a $(INSTALL_DOC) +install: all installdirs installfiles -$(bindir)/vvp$(suffix)@EXEEXT@: ./vvp@EXEEXT@ - $(INSTALL_PROGRAM) ./vvp@EXEEXT@ "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@" +F = ./vvp@EXEEXT@ $(INSTALL_DOC) -$(libdir)/libvpi$(suffix).a : ./libvpi.a - $(INSTALL_DATA) libvpi.a "$(DESTDIR)$(libdir)/libvpi$(suffix).a" - -$(mandir)/man1/vvp$(suffix).1: vvp.man +installman: vvp.man installdirs $(INSTALL_DATA) vvp.man "$(DESTDIR)$(mandir)/man1/vvp$(suffix).1" -$(prefix)/vvp$(suffix).pdf: vvp.pdf +installpdf: vvp.pdf installdirs $(INSTALL_DATA) vvp.pdf "$(DESTDIR)$(prefix)/vvp$(suffix).pdf" +installfiles: $(F) | installdirs + $(INSTALL_PROGRAM) ./vvp@EXEEXT@ "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@" + installdirs: $(srcdir)/../mkinstalldirs $(srcdir)/../mkinstalldirs "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(INSTALL_DOCDIR)" uninstall: $(UNINSTALL32) rm -f "$(DESTDIR)$(bindir)/vvp$(suffix)@EXEEXT@" - rm -f "$(DESTDIR)$(libdir)/libvpi$(suffix).a" rm -f "$(DESTDIR)$(mandir)/man1/vvp$(suffix).1" "$(DESTDIR)$(prefix)/vvp$(suffix).pdf" -include $(patsubst %.o, dep/%.d, $O) diff -Nru iverilog-10.3/vvp/opcodes.txt iverilog-11.0/vvp/opcodes.txt --- iverilog-10.3/vvp/opcodes.txt 2019-08-15 15:31:11.000000000 +0000 +++ iverilog-11.0/vvp/opcodes.txt 2020-09-26 22:44:25.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com) + * Copyright (c) 2001-2020 Stephen Williams (steve@icarus.com) * */ @@ -39,19 +39,11 @@ boolean values, for example comparisons. They are also used as inputs for test and branch opcodes. -* %abs/wr , +* %abs/wr This instruction calculates the absolute value of a real value. It uses -the fabs() function in the run-time to do the work. - -* %add , , (XXXX Old version) - -This instruction adds the right vector into the left vector, the -vectors having the width . If any of the bits of either vector -are x or z, the result is x. Otherwise, the result is the arithmetic -sum. - -See also the %sub instruction. +the fabs() function in the run-time to do the work, and manipulates +the top of the real-value stack. * %add * %addi , , @@ -65,7 +57,7 @@ See also the %sub instruction. -* %add/wr , +* %add/wr This is the real valued version of the %add instruction. The arguments are popped from the stack, right operand then left, and the result @@ -226,6 +218,22 @@ This may not work on all platforms. If run-time debugging is compiled out, then this function is a no-op. +* %callf/obj , +* %callf/real , +* %callf/str , +* %callf/vec4 , +* %callf/void , + +More directly implement function calling. This subsumes the %fork and +%join of the more general task and block calling, but is optimized for +functions, which are threads of a special, constrained sort. + +The different variants reflect the different return types for the +called function. For example, if the function returns a string, the +%callf/str opcode is used, and will push the string return value into +the caller's string stack. The %callf/void function is special in that +is pushes no value onto any stack. + * %cassign/vec4 * %cassign/vec4/off , @@ -250,6 +258,26 @@ Pop a value from the vec4 stack, convert it using Verilog rules to a vector2 (binary) value, and push the result. +* %cast/vec2/dar + +Pop a dynamic array value from the object stack, convert it to a 2-state +vector that is bits wide, and push the result to the vec4 stack. +If the dynamic array does not fit exactly in bits, print an error +message and stop the simulation. + +* %cast/vec4/dar + +Pop a dynamic array value from the object stack, convert it to a 4-state +vector that is bits wide, and push the result to the vec4 stack. +If the dynamic array does not fit exactly in bits, print an error +message and stop the simulation. + +* %cast/vec4/str + +Pop a value from the string stack, convert it to a vector that is +bits wide, and push the result to the vec4 stack. If the string does not +fit exactly in bits, print an error message and stop the simulation. + * %cmp/s * %cmp/u * %cmp/e @@ -294,6 +322,18 @@ eliminate the need for a %flag_inv instruction to implement != and !== operations. +* %cmp/we +* %cmp/wne + +These instructions perform a wild comparison of two vectors of equal +size. Two values are pulled from the top of the stack, and not replaced. +The results are written into flag bit 4. The comparisons work like eq/ne +except an x/z bit in the r-value will match any l-value bit. + +The %cmp/wne variant is the same as %cmp/we, but the 4 flag is inverted +in order to eliminate the need for a %flag_inv instruction to implement +the !=? operator. + * %cmp/wr Compare real values for equality and less-then. This opcode pops to @@ -351,31 +391,19 @@ to the value on the top of the stack. See the %pushi/vec4 instruction for how to describe the immediate value. -* %cvt/sr -* %cvt/rs - -Copy a word from r to l, converting it from real to signed integer (sr) -or signed integer to real (rs) in the process. The source and destination -may be the same word address, leading to a convert in place. Precision -may be lost in the conversion. - -The %cvt/sr gets the real value from the top of the real value -stack (and pops the value) and writes it to the indexed register. - +* %cvt/sr * %cvt/ur -* %cvt/ru -Copy a word from r to l, converting it from real to unsigned integer (ur) -or signed integer to real (ru) in the process. The source and destination -may be the same word address, leading to a convert in place. Precision -may be lost in the conversion. - -* %cvt/rv , -* %cvt/rv/s , - -The %cvt/rv instruction converts a thread vector starting at -and with the width to a real word. Push the result onto the real -value stack. Precision may be lost in the conversion. +Pop a word from the real-value stack, convert it to a signed or +unsigned integer, and write it to the index +register. Precision may be lost in the conversion. + +* %cvt/rv +* %cvt/rv/s + +The %cvt/rv instruction pops a value from the thread vec4 stack and +converts it to a real word. Push the result onto the real value +stack. Precision may be lost in the conversion. The %cvt/rv/s instruction is the same as %cvt/rv, but treats the thread vector as a signed value. @@ -504,6 +532,10 @@ is a string, if string is 0 then the following default message is used: "Procedural tracing.". +* %flag_inv + +This instruct inverts a flag bit. + * %flag_mov , This instruction copies the flag bit from to . @@ -528,15 +560,17 @@ * %force/vec4