diff --git a/Changes b/Changes index 830293acf..8b62759f9 100644 --- a/Changes +++ b/Changes @@ -11,11 +11,15 @@ contributors that suggested a given feature are shown in []. Thanks! Verilator 4.215 devel ========================== +**Major:** + +* Add --lib-create, similar to --protect-lib but without protections. + **Minor:** * Internal code cleanups and improvements. [Geza Lore] * Improve --thread verilation-time performance. -* Fix array method names with parens (#3181) (#3183). [Teng Huang] +* Fix array method names with parenthesis (#3181) (#3183). [Teng Huang] * Fix split_var assign merging (#3177) (#3179). [Yutetsu TAKATSUKASA] * Fix nested generate if genblk naming (#3189). [yanx21] diff --git a/bin/verilator b/bin/verilator index fbd06550a..505933437 100755 --- a/bin/verilator +++ b/bin/verilator @@ -336,6 +336,7 @@ detailed descriptions of these arguments. -LDFLAGS Linker pre-object arguments for makefile --l2-name Verilog scope name of the top module --language Default language standard to parse + --lib-create Create a DPI library +libext++[ext]... Extensions for finding modules --lint-only Lint, but do not make output -MAKEFLAGS Arguments to pass to make during --build diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index bd0fe1928..40d9fc8e2 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -575,6 +575,23 @@ Summary: "+libext+" is fairly standard across Verilog tools. Defaults to ".v+.sv". +.. option:: --lib-create + + Produces C++, Verilog wrappers and a Makefile which can in turn produce + a DPI library which can be used by Verilator or other simulators along + with the corresponding Verilog wrapper. The Makefile will build both a + static and dynamic version of the library named :file:`lib.a` and + :file:`lib.so` respectively. This is done because some simulators + require a dynamic library, but the static library is arguably easier to + use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`. + + When using :vlopt:`--lib-create` it is advised to also use + :vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the + model has a time resolution that is always compatible with the time + precision of the upper instantiating module. + + See also :vlopt:`--protect-lib`. + .. option:: --lint-only Check the files for lint violations only, do not create any other @@ -872,24 +889,15 @@ Summary: .. option:: --protect-lib - Produces C++, Verilog wrappers and a Makefile which can in turn produce - a DPI library which can be used by Verilator or other simulators along - with the corresponding Verilog wrapper. The Makefile will build both a - static and dynamic version of the library named :file:`lib.a` and - :file:`lib.so` respectively. This is done because some simulators - require a dynamic library, but the static library is arguably easier to - use if possible. :vlopt:`--protect-lib` implies :vlopt:`--protect-ids`. + Produces a DPI library similar to :vlopt:`--lib-create`, but hides + internal design details. :vlopt:`--protect-lib` implies + :vlopt:`--protect-ids`, and :vlopt:`--lib-create`. This allows for the secure delivery of sensitive IP without the need for encrypted RTL (i.e. IEEE P1735). See :file:`examples/make_protect_lib` in the distribution for a demonstration of how to build and use the DPI library. - When using :vlopt:`--protect-lib` it is advised to also use - :vlopt:`--timescale-override /1fs <--timescale-override>` to ensure the - model has a time resolution that is always compatible with the time - precision of the upper instantiating module. - .. option:: --private Opposite of :vlopt:`--public`. Is the default; this option exists for diff --git a/docs/guide/verilating.rst b/docs/guide/verilating.rst index b33dd4acd..e2e10c32b 100644 --- a/docs/guide/verilating.rst +++ b/docs/guide/verilating.rst @@ -70,10 +70,9 @@ Verilator is run in hierarchical mode on the whole SoC. Verilator will make two models, one for the CPU hierarchy block, and one for the SoC. The Verialted code for the SoC will automatically call the CPU Verilated model. -The current hierarchical Verilation is based on protect-lib. Each hierarchy -block is Verilated to a protect-lib. User modules of the hierarchy blocks -will see a tiny wrapper generated by protect-lib instead of the actual -design. +The current hierarchical Verilation is based on :vlopt:`--lib-create`. Each +hierarchy block is Verilated into a library. User modules of the hierarchy +blocks will see a tiny wrapper generated by :vlopt:`--lib-create`. Usage diff --git a/include/verilated.cpp b/include/verilated.cpp index fb349c41f..173f0fc7f 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -25,7 +25,7 @@ //========================================================================= // Internal note: // -// verilated.o may exist both in protect-lib (incrementally linked .a/.so) +// verilated.o may exist both in --lib-create (incrementally linked .a/.so) // and the main module. Both refer the same instance of static // variables/VL_THREAD_LOCAL in verilated.o such as Verilated, or // VerilatedImpData. This is important to share that state, but the diff --git a/src/V3EmitCMake.cpp b/src/V3EmitCMake.cpp index b9c875e41..d8b2170aa 100644 --- a/src/V3EmitCMake.cpp +++ b/src/V3EmitCMake.cpp @@ -178,8 +178,8 @@ class CMakeEmitter final { if (v3Global.opt.mtasks()) { global.emplace_back("${VERILATOR_ROOT}/include/verilated_threads.cpp"); } - if (!v3Global.opt.protectLib().empty()) { - global.emplace_back(v3Global.opt.makeDir() + "/" + v3Global.opt.protectLib() + ".cpp"); + if (!v3Global.opt.libCreate().empty()) { + global.emplace_back(v3Global.opt.makeDir() + "/" + v3Global.opt.libCreate() + ".cpp"); } *of << "# Global classes, need linked once per executable\n"; @@ -238,7 +238,7 @@ class CMakeEmitter final { // with .so << ")\n"; } - *of << "\n# Verilate the top module that refers protect-lib wrappers of above\n"; + *of << "\n# Verilate the top module that refers to lib-create wrappers of above\n"; *of << "verilate(${TOP_TARGET_NAME} PREFIX " << v3Global.opt.prefix() << " TOP_MODULE " << v3Global.rootp()->topModulep()->name() << " DIRECTORY " << deslash(v3Global.opt.makeDir()) << " SOURCES "; diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 0b152bd30..d74745109 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -145,8 +145,8 @@ public: if (v3Global.opt.exe()) { of.puts("default: " + v3Global.opt.exeName() + "\n"); - } else if (!v3Global.opt.protectLib().empty()) { - of.puts("default: lib" + v3Global.opt.protectLib() + "\n"); + } else if (!v3Global.opt.libCreate().empty()) { + of.puts("default: lib" + v3Global.opt.libCreate() + "\n"); } else { of.puts("default: " + v3Global.opt.prefix() + "__ALL.a\n"); } @@ -188,7 +188,7 @@ public: of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n"); of.puts("VM_USER_CFLAGS = \\\n"); - if (!v3Global.opt.protectLib().empty()) of.puts("\t-fPIC \\\n"); + if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n"); const V3StringList& cFlags = v3Global.opt.cFlags(); for (const string& i : cFlags) of.puts("\t" + i + " \\\n"); of.puts("\n"); @@ -243,19 +243,19 @@ public: of.puts("\n"); } - if (!v3Global.opt.protectLib().empty()) { - const string protectLibDeps = "$(VK_OBJS) $(VK_GLOBAL_OBJS) " - + v3Global.opt.protectLib() + ".o $(VM_HIER_LIBS)"; - of.puts("\n### Library rules from --protect-lib\n"); + if (!v3Global.opt.libCreate().empty()) { + const string libCreateDeps = "$(VK_OBJS) $(VK_GLOBAL_OBJS) " + v3Global.opt.libCreate() + + ".o $(VM_HIER_LIBS)"; + of.puts("\n### Library rules from --lib-create\n"); // The rule to create .a is defined in verilated.mk, so just define dependency here. - of.puts(v3Global.opt.protectLibName(false) + ": " + protectLibDeps + "\n"); + of.puts(v3Global.opt.libCreateName(false) + ": " + libCreateDeps + "\n"); of.puts("\n"); if (v3Global.opt.hierChild()) { // Hierarchical child does not need .so because hierTop() will create .so from .a - of.puts("lib" + v3Global.opt.protectLib() + ": " - + v3Global.opt.protectLibName(false) + "\n"); + of.puts("lib" + v3Global.opt.libCreate() + ": " + v3Global.opt.libCreateName(false) + + "\n"); } else { - of.puts(v3Global.opt.protectLibName(true) + ": " + protectLibDeps + "\n"); + of.puts(v3Global.opt.libCreateName(true) + ": " + libCreateDeps + "\n"); // Linker on mac emits an error if all symbols are not found here, // but some symbols that are referred as "DPI-C" can not be found at this moment. // So add dynamic_lookup @@ -267,9 +267,8 @@ public: "\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -shared -o $@ $^\n"); of.puts("endif\n"); of.puts("\n"); - of.puts("lib" + v3Global.opt.protectLib() + ": " - + v3Global.opt.protectLibName(false) + " " - + v3Global.opt.protectLibName(true) + "\n"); + of.puts("lib" + v3Global.opt.libCreate() + ": " + v3Global.opt.libCreateName(false) + + " " + v3Global.opt.libCreateName(true) + "\n"); } } diff --git a/src/V3HierBlock.cpp b/src/V3HierBlock.cpp index 18a5b1c3b..58103c813 100644 --- a/src/V3HierBlock.cpp +++ b/src/V3HierBlock.cpp @@ -18,8 +18,9 @@ // - time and memory for Verilation // - compilation time especially when a hierarchical block is used many times // -// Hierarchical Verilation internally creates protect-lib for each hierarchical block. -// Upper modules read wrapper for the protect-lib instead of the actual design. +// Hierarchical Verilation internally uses --lib-create for each +// hierarchical block. Upper modules read the wrapper from --lib-create +// instead of the Verilog design. // // Hierarchical Verilation runs as the following step // 1) Find modules marked by /*verilator hier_block*/ metacomment @@ -29,7 +30,7 @@ // // There are 3 kinds of Verilator run. // a) To create ${prefix}_hier.mk (--hierarchical) -// b) To create protect-lib for each hierarchical block (--hierarchical-child) +// b) To --lib-create on each hierarchical block (--hierarchical-child) // c) To load wrappers and Verilate the top module (... what primary flags?) // // Then user can build Verilated module as usual. @@ -52,7 +53,7 @@ // 4) V3LinkDot.cpp checks dotted access across hierarchical block boundary. // 5) In V3Dead.cpp, some parameters of parameterized modules are protected not to be deleted even // if the parameter is not referred. This protection is necessary to match step 6) below. -// 6) In V3Param.cpp, use protect-lib wrapper of parameterized module made in b) and c). +// 6) In V3Param.cpp, use --lib-create wrapper of the parameterized module made in b) and c). // If a hierarchical block is a parameterized module and instantiated in multiple locations, // all parameters must exactly match. // 7) In V3HierBlock.cpp, relationship among hierarchical blocks are checked in run a). @@ -152,8 +153,9 @@ V3StringList V3HierBlock::commandArgs(bool forCMake) const { opts.push_back(" --mod-prefix " + prefix); opts.push_back(" --top-module " + modp()->name()); } - opts.push_back(" --protect-lib " + modp()->name()); // mangled name - opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted()); + opts.push_back(" --lib-create " + modp()->name()); // possibly mangled name + if (v3Global.opt.protectKeyProvided()) + opts.push_back(" --protect-key " + v3Global.opt.protectKeyDefaulted()); opts.push_back(" --hierarchical-child"); const StrGParams gparamsStr = stringifyParams(gparams(), true); @@ -418,8 +420,10 @@ void V3HierBlockPlan::writeCommandArgsFiles(bool forCMake) const { *of << it->second->hierBlockArgs().front() << "\n"; } - if (!v3Global.opt.protectLib().empty()) { - *of << "--protect-lib " << v3Global.opt.protectLib() << "\n"; + if (!v3Global.opt.libCreate().empty()) { + *of << "--lib-create " << v3Global.opt.libCreate() << "\n"; + } + if (v3Global.opt.protectKeyProvided()) { *of << "--protect-key " << v3Global.opt.protectKeyDefaulted() << "\n"; } if (v3Global.opt.threads() > 0) { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 2273bc8ed..ef77a6ae9 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -263,9 +263,9 @@ private: virtual void visit(AstPragma* nodep) override { if (nodep->pragType() == AstPragmaType::HIER_BLOCK) { UASSERT_OBJ(m_modp, nodep, "HIER_BLOCK not under a module"); - // If this is hierarchical mode which is to create protect-lib, + // If this is hierarchical mode which is to lib-create, // sub modules do not have hier_block meta comment in the source code. - // But .vlt files may still mark a module which is actually a protect-lib wrapper + // But .vlt files may still mark a module which is actually a lib-create wrapper // hier_block. AstNodeModule::hierBlock() can be true only when --hierarchical is // specified. m_modp->hierBlock(v3Global.opt.hierarchical()); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index d2bb5c34d..c9cfcbbe5 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -500,9 +500,9 @@ string V3Options::filePathCheckOneDir(const string& modname, const string& dirna // 1: Delete the option which has no argument // 2: Delete the option and its argument int V3Options::stripOptionsForChildRun(const string& opt, bool forTop) { - if (opt == "Mdir" || opt == "clk" || opt == "f" || opt == "j" || opt == "l2-name" - || opt == "mod-prefix" || opt == "prefix" || opt == "protect-lib" || opt == "protect-key" - || opt == "threads" || opt == "top-module" || opt == "v") { + if (opt == "Mdir" || opt == "clk" || opt == "lib-create" || opt == "f" || opt == "j" + || opt == "l2-name" || opt == "mod-prefix" || opt == "prefix" || opt == "protect-lib" + || opt == "protect-key" || opt == "threads" || opt == "top-module" || opt == "v") { return 2; } if (opt == "build" || (!forTop && (opt == "cc" || opt == "exe" || opt == "sc")) @@ -732,11 +732,6 @@ void V3Options::notify() { if (m_hierChild && m_hierBlocks.empty()) { cmdfl->v3error("--hierarchical-block must be set when --hierarchical-child is set"); } - if (m_hierarchical && m_protectLib.empty() && m_protectKey.empty()) { - // Key for hierarchical Verilation is fixed to be ccache friendly when the aim of this run - // is not to create protec-lib. - m_protectKey = "VL-KEY-HIERARCHICAL"; - } if (protectIds()) { if (allPublic()) { @@ -1121,6 +1116,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char }; DECL_OPTION("-default-language", CbVal, setLang); DECL_OPTION("-language", CbVal, setLang); + DECL_OPTION("-lib-create", Set, &m_libCreate); DECL_OPTION("-lint-only", OnOff, &m_lintOnly); DECL_OPTION("-l2-name", Set, &m_l2Name); DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical @@ -1237,7 +1233,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-protect-ids", OnOff, &m_protectIds); DECL_OPTION("-protect-key", Set, &m_protectKey); DECL_OPTION("-protect-lib", CbVal, [this](const char* valp) { - m_protectLib = valp; + m_libCreate = valp; m_protectIds = true; }); DECL_OPTION("-public", OnOff, &m_public); diff --git a/src/V3Options.h b/src/V3Options.h index acea42e08..fb35022dc 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -321,12 +321,12 @@ private: string m_exeName; // main switch: -o {name} string m_flags; // main switch: -f {name} string m_l2Name; // main switch: --l2name; "" for top-module's name + string m_libCreate; // main switch: --lib-create {lib_name} string m_makeDir; // main switch: -Mdir string m_modPrefix; // main switch: --mod-prefix string m_pipeFilter; // main switch: --pipe-filter string m_prefix; // main switch: --prefix string m_protectKey; // main switch: --protect-key - string m_protectLib; // main switch: --protect-lib {lib_name} string m_topModule; // main switch: --top-module string m_unusedRegexp; // main switch: --unused-regexp string m_waiverOutput; // main switch: --waiver-output {filename} @@ -526,14 +526,9 @@ public: string exeName() const { return m_exeName != "" ? m_exeName : prefix(); } string l2Name() const { return m_l2Name; } - string makeDir() const { return m_makeDir; } - string modPrefix() const { return m_modPrefix; } - string pipeFilter() const { return m_pipeFilter; } - string prefix() const { return m_prefix; } - string protectKeyDefaulted(); // Set default key if not set by user - string protectLib() const { return m_protectLib; } - string protectLibName(bool shared) { - string libName = "lib" + protectLib(); + string libCreate() const { return m_libCreate; } + string libCreateName(bool shared) { + string libName = "lib" + libCreate(); if (shared) { libName += ".so"; } else { @@ -541,6 +536,13 @@ public: } return libName; } + string makeDir() const { return m_makeDir; } + string modPrefix() const { return m_modPrefix; } + string pipeFilter() const { return m_pipeFilter; } + string prefix() const { return m_prefix; } + // Not just called protectKey() to avoid bugs of not using protectKeyDefaulted() + bool protectKeyProvided() const { return !m_protectKey.empty(); } + string protectKeyDefaulted(); // Set default key if not set by user string topModule() const { return m_topModule; } string unusedRegexp() const { return m_unusedRegexp; } string waiverOutput() const { return m_waiverOutput; } diff --git a/src/V3Param.cpp b/src/V3Param.cpp index d9a192f69..28e8775db 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -127,7 +127,7 @@ public: if (m_hierBlockOptsByOrigName.find(origName) == m_hierBlockOptsByOrigName.end()) { return nullptr; } - // This module is a hierarchical block. Need to replace it by the protect-lib wrapper. + // This module is a hierarchical block. Need to replace it by the --lib-create wrapper. const std::pair candidates = m_hierBlockOptsByOrigName.equal_range(origName); const auto paramsIt = m_modParams.find(origName); @@ -168,7 +168,7 @@ public: } if (found && paramIdx == hierIt->second->params().size()) break; } - UASSERT_OBJ(hierIt != candidates.second, firstPinp, "No protect-lib wrapper found"); + UASSERT_OBJ(hierIt != candidates.second, firstPinp, "No --lib-create wrapper found"); // parameter settings will be removed in the bottom of caller visitCell(). const HierBlockModMap::const_iterator modIt = m_hierBlockMod.find(hierIt->second->mangledName()); @@ -258,7 +258,7 @@ class ParamProcessor final { AstNodeModule* m_modp = nullptr; // Current module being processed - // Database to get protect-lib wrapper that matches parameters in hierarchical Verilation + // Database to get lib-create wrapper that matches parameters in hierarchical Verilation ParameterizedHierBlocks m_hierBlocks; // Default parameter values key:parameter name, value:default value (can be nullptr) using DefaultValueMap = std::map; diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index cb44de52a..0b005fd4b 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -57,8 +57,8 @@ private: AstTextBlock* m_cSeqClksp = nullptr; // Sequential clock copy list AstTextBlock* m_cSeqOutsp = nullptr; // Sequential output copy list AstTextBlock* m_cIgnoreParamsp = nullptr; // Combo ignore parameter list - string m_libName; - string m_topName; + const string m_libName; + const string m_topName; bool m_foundTop = false; // Have seen the top module bool m_hasClk = false; // True if the top module has sequential logic @@ -396,7 +396,7 @@ private: } else if (nodep->direction() == VDirection::OUTPUT) { handleOutput(nodep); } else { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: protect-lib port direction: " + nodep->v3warn(E_UNSUPPORTED, "Unsupported: --lib-create port direction: " << nodep->direction().ascii()); } } @@ -484,7 +484,7 @@ private: public: explicit ProtectVisitor(AstNode* nodep) - : m_libName{v3Global.opt.protectLib()} + : m_libName{v3Global.opt.libCreate()} , m_topName{v3Global.opt.prefix()} { iterate(nodep); } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 6bde19f70..2d20c24c5 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -536,7 +536,7 @@ static void process() { } // Output DPI protected library files - if (!v3Global.opt.protectLib().empty()) { + if (!v3Global.opt.libCreate().empty()) { V3ProtectLib::protect(); V3EmitV::emitvFiles(); V3EmitC::emitcFiles(); diff --git a/test_regress/t/t_lib.pl b/test_regress/t/t_lib.pl new file mode 100755 index 000000000..93aa4bfd8 --- /dev/null +++ b/test_regress/t/t_lib.pl @@ -0,0 +1,70 @@ +#!/usr/bin/env perl +# Makes the test run with tracing enabled by default, can be overridden +# with --notrace +unshift(@ARGV, "--trace"); +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2019 by Todd Strader. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios( + vlt => 1, + xsim => 1, + ); + +top_filename("t/t_lib_prot.v"); + +$Self->{sim_time} = $Self->{benchmark} * 100 if $Self->{benchmark}; + +my $secret_prefix = "secret"; +my $secret_dir = "$Self->{obj_dir}/$secret_prefix"; +mkdir $secret_dir; + +while (1) { + # Always compile the secret file with Verilator no matter what simulator + # we are testing with + run(logfile => "$secret_dir/vlt_compile.log", + cmd => ["perl", + "$ENV{VERILATOR_ROOT}/bin/verilator", + "--prefix", + "Vt_lib_prot_secret", + "-cc", + "-Mdir", + $secret_dir, + "--lib-create", + $secret_prefix, + "t/t_lib_prot_secret.v"], + verilator_run => 1, + ); + last if $Self->{errors}; + + run(logfile => "$secret_dir/secret_gcc.log", + cmd=>[$ENV{MAKE}, + "-C", + $secret_dir, + "-f", + "Vt_lib_prot_secret.mk"]); + last if $Self->{errors}; + + compile( + verilator_flags2 => ["$secret_dir/secret.sv", + "-LDFLAGS", + "$secret_prefix/libsecret.a"], + xsim_flags2 => ["$secret_dir/secret.sv"], + ); + + execute( + check_finished => 1, + xsim_run_flags2 => ["--sv_lib", + "$secret_dir/libsecret", + "--dpi_absolute"], + ); + + ok(1); + last; +} +1; diff --git a/test_regress/t/t_lib_prot_inout_bad.out b/test_regress/t/t_lib_prot_inout_bad.out index 1b58aebdb..d2ff6d8e6 100644 --- a/test_regress/t/t_lib_prot_inout_bad.out +++ b/test_regress/t/t_lib_prot_inout_bad.out @@ -1,4 +1,4 @@ -%Error-UNSUPPORTED: t/t_lib_prot_inout_bad.v:9:28: Unsupported: protect-lib port direction: INOUT +%Error-UNSUPPORTED: t/t_lib_prot_inout_bad.v:9:28: Unsupported: --lib-create port direction: INOUT 9 | inout z, | ^ ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest