diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index ee14ca390..795b7b141 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1101,6 +1101,12 @@ Summary: marking only those signals that need public_flat_rw is typically significantly better performing. +.. option:: --public-depth + + Enables public as with :vlopt:`--public-flat-rw`, but only to the specified depth of modules. + It operates at the module maximum level, so if a module's cells are A.B.X and A.X, the + a --public-depth 3 must be used to make module X public, and both A.B.X and A.X will be public. + .. option:: --public-params Declares all parameters public as if they had diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index ba3542ff6..5e0d78d67 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -248,7 +248,10 @@ private: // Maybe this variable has a signal attribute V3Config::applyVarAttr(m_modp, m_ftaskp, nodep); - if (v3Global.opt.publicFlatRW()) { + if (v3Global.opt.publicFlatRW() + || (v3Global.opt.publicDepth() && m_modp + && (m_modp->level() - 1) <= v3Global.opt.publicDepth())) { + switch (nodep->varType()) { case VVarType::VAR: // FALLTHRU case VVarType::GPARAM: // FALLTHRU diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 6bcdabeec..076fff117 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1381,6 +1381,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char m_protectIds = true; }); DECL_OPTION("-public", OnOff, &m_public); + DECL_OPTION("-public-depth", Set, &m_publicDepth); DECL_OPTION("-public-flat-rw", CbOnOff, [this](bool flag) { m_publicFlatRW = flag; v3Global.dpi(true); diff --git a/src/V3Options.h b/src/V3Options.h index 46ea949c8..12b261003 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -303,6 +303,7 @@ private: int m_outputSplitCFuncs = -1; // main switch: --output-split-cfuncs int m_outputSplitCTrace = -1; // main switch: --output-split-ctrace int m_pinsBv = 65; // main switch: --pins-bv + int m_publicDepth = 0; // main switch: --public-depth int m_reloopLimit = 40; // main switch: --reloop-limit VOptionBool m_skipIdentical; // main switch: --skip-identical int m_threads = 1; // main switch: --threads @@ -521,6 +522,7 @@ public: int outputSplitCFuncs() const { return m_outputSplitCFuncs; } int outputSplitCTrace() const { return m_outputSplitCTrace; } int pinsBv() const { return m_pinsBv; } + int publicDepth() const { return m_publicDepth; } int reloopLimit() const { return m_reloopLimit; } VOptionBool skipIdentical() const { return m_skipIdentical; } int threads() const VL_MT_SAFE { return m_threads; } diff --git a/test_regress/t/t_vpi_public_depth.cpp b/test_regress/t/t_vpi_public_depth.cpp new file mode 100644 index 000000000..ee1217bd4 --- /dev/null +++ b/test_regress/t/t_vpi_public_depth.cpp @@ -0,0 +1,247 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2023 by Wilson Snyder. 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 +// +//************************************************************************* + +#ifdef IS_VPI + +#include "vpi_user.h" + +#include + +#else + +#include "verilated.h" +#include "verilated_vcd_c.h" +#include "verilated_vpi.h" + +#include VM_PREFIX_INCLUDE +#ifdef T_VPI_PUBLIC_DEPTH +#include "Vt_vpi_public_depth__Dpi.h" +#elif defined(T_VPI_PUBLIC_DEPTH_OFF) +#include "Vt_vpi_public_depth_off__Dpi.h" +#else +#error "Bad test" +#endif +#include "svdpi.h" + +#endif + +#include +#include +#include + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +// __FILE__ is too long +#define FILENM "t_vpi_public_depth.cpp" + +#define DEBUG \ + if (0) printf + +#define CHECK_RESULT_NZ(got) \ + if (!(got)) { \ + printf("%%Error: %s:%d: GOT = NULL EXP = !NULL\n", FILENM, __LINE__); \ + return __LINE__; \ + } + +#define CHECK_RESULT_Z(got) \ + if (got) { \ + printf("%%Error: %s:%d: GOT = !NULL EXP = NULL\n", FILENM, __LINE__); \ + return __LINE__; \ + } + +#define CHECK_RESULT(got, exp) \ + if ((got) != (exp)) { \ + std::cout << std::dec << "%Error: " << FILENM << ":" << __LINE__ << ": GOT = " << (got) \ + << " EXP = " << (exp) << std::endl; \ + return __LINE__; \ + } + +#define CHECK_RESULT_CSTR(got, exp) \ + if (std::strcmp((got), (exp))) { \ + printf("%%Error: %s:%d: GOT = '%s' EXP = '%s'\n", FILENM, __LINE__, \ + (got) ? (got) : "", (exp) ? (exp) : ""); \ + return __LINE__; \ + } + +void modDump(const TestVpiHandle& it, int n) { + while (TestVpiHandle hndl = vpi_scan(it)) { + const char* nm = vpi_get_str(vpiName, hndl); + for (int i = 0; i < n; i++) printf(" "); + printf("%s\n", nm); + TestVpiHandle subIt = vpi_iterate(vpiModule, hndl); + if (subIt) modDump(subIt, n + 1); + } +} + +extern "C" { +int mon_check() { +#ifdef TEST_VERBOSE + printf("-mon_check()\n"); +#endif + + TestVpiHandle it = vpi_iterate(vpiModule, NULL); + CHECK_RESULT_NZ(it); + // Uncomment to see what other simulators return + // modDump(it, 0); + // return 1; + + TestVpiHandle topmod; + // both somepackage and t exist at the top level + while ((topmod = vpi_scan(it))) { + if (vpi_get(vpiType, topmod) == vpiModule) break; + } + CHECK_RESULT_NZ(topmod); + + const char* t_name = vpi_get_str(vpiName, topmod); + CHECK_RESULT_NZ(t_name); + + // Icarus reports the top most module as "top" + if (std::strcmp(t_name, "top") == 0) { + it = vpi_iterate(vpiModule, topmod); + CHECK_RESULT_NZ(it); + CHECK_RESULT(vpi_get(vpiType, it), vpiModule); + topmod = vpi_scan(it); + t_name = vpi_get_str(vpiName, topmod); + CHECK_RESULT_NZ(t_name); + } + CHECK_RESULT_CSTR(t_name, "t"); + TestVpiHandle topmod_done_should_be_0 = (vpi_scan(it)); + it.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + CHECK_RESULT_Z(topmod_done_should_be_0); + + TestVpiHandle it2 = vpi_iterate(vpiModule, topmod); + CHECK_RESULT_NZ(it2); + + TestVpiHandle mod2 = vpi_scan(it2); + CHECK_RESULT_NZ(mod2); + + const char* mod_a_name = vpi_get_str(vpiName, mod2); + CHECK_RESULT_CSTR(mod_a_name, "\\mod.a "); + + TestVpiHandle it3 = vpi_iterate(vpiModule, mod2); + +#ifdef T_VPI_PUBLIC_DEPTH + CHECK_RESULT_NZ(it3); + TestVpiHandle mod3 = vpi_scan(it3); + CHECK_RESULT_NZ(mod3); + + const char* mod_c_name = vpi_get_str(vpiName, mod3); + if (std::strcmp(mod_c_name, "\\mod_b$ ") == 0) { + // Full visibility in other simulators, skip mod_b + TestVpiHandle mod4 = vpi_scan(it3); + CHECK_RESULT_NZ(mod4); + mod_c_name = vpi_get_str(vpiName, mod4); + } + CHECK_RESULT_CSTR(mod_c_name, "\\mod\\c$ "); + +#elif defined(T_VPI_PUBLIC_DEPTH_OFF) + CHECK_RESULT_Z(it3); +#endif + + return 0; // Ok +} +} +//====================================================================== + +#ifdef IS_VPI + +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + const std::unique_ptr contextp{new VerilatedContext}; + + uint64_t sim_time = 1100; + contextp->debug(0); + contextp->commandArgs(argc, argv); + // We're going to be checking for these errors so don't crash out + contextp->fatalOnVpiError(0); + + { + // Construct and destroy + const std::unique_ptr topp{ + new VM_PREFIX{contextp.get(), + // Note null name - we're flattening it out + ""}}; + } + + // Test second construction + const std::unique_ptr topp{new VM_PREFIX{contextp.get(), + // Note null name - we're flattening it out + ""}}; + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + +#if VM_TRACE + contextp->traceEverOn(true); + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + topp->trace(tfp, 99); + tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); +#endif + + topp->eval(); + topp->clk = 0; + contextp->timeInc(10); + + while (contextp->time() < sim_time && !contextp->gotFinish()) { + contextp->timeInc(1); + topp->eval(); + VerilatedVpi::callValueCbs(); + topp->clk = !topp->clk; + // mon_do(); +#if VM_TRACE + if (tfp) tfp->dump(contextp->time()); +#endif + } + if (!contextp->gotFinish()) { + vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + return 0; +} + +#endif diff --git a/test_regress/t/t_vpi_public_depth.pl b/test_regress/t/t_vpi_public_depth.pl new file mode 100755 index 000000000..930a82c94 --- /dev/null +++ b/test_regress/t/t_vpi_public_depth.pl @@ -0,0 +1,32 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 by Wilson Snyder. 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(simulator => 1); + +skip("Known compiler limitation") + if $Self->cxx_version =~ /\(GCC\) 4.4/; + +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + iv_flags2 => ["-g2005-sv"], + verilator_flags2 => ["+define+USE_DOLLAR_C32 --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_public_depth.cpp --public-depth 3"], + make_flags => 'CPPFLAGS_ADD=-DTEST_VPI_PUBLIC_DEPTH', + ); + +execute( + use_libvpi => 1, + check_finished => 1, + v_flags2, + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_public_depth.v b/test_regress/t/t_vpi_public_depth.v new file mode 100644 index 000000000..eeaedae9a --- /dev/null +++ b/test_regress/t/t_vpi_public_depth.v @@ -0,0 +1,125 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2010 by Wilson Snyder. 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 + +`ifndef IVERILOG +import "DPI-C" context function int mon_check(); +`endif + +package somepackage; + int someint; +endpackage + +module t (/*AUTOARG*/ + // Inputs + clk + ); + +`ifdef USE_DOLLAR_C32 +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + integer status; + + wire a, b, x; + + A \mod.a (/*AUTOINST*/ + // Outputs + .x (x), + // Inputs + .clk (clk), + .a (a), + .b (b)); + + // Test loop + initial begin +`ifdef IVERILOG + status = $mon_check(); +`elsif USE_DOLLAR_C32 + status = $c32("mon_check()"); +`else + status = mon_check(); +`endif + if (status!=0) begin + $write("%%Error: t_vpi_module.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t + +module A(/*AUTOARG*/ + // Outputs + x, + // Inputs + clk, a, b + ); + + input clk; + + input a, b; + output x; + + wire y, c; + + B \mod_b$ (/*AUTOINST*/ + // Outputs + .y (y), + // Inputs + .b (b), + .c (c)); + + C \mod\c$ (/*AUTOINST*/ + // Outputs + .x (x), + // Inputs + .clk (clk), + .a (a), + .y (y)); + +endmodule : A + +module B(/*AUTOARG*/ + // Outputs + y, + // Inputs + b, c + ); + input b, c; + + output reg y; + + always @(*) begin : myproc + y = b ^ c; + end + +endmodule + +module C(/*AUTOARG*/ + // Outputs + x, + // Inputs + clk, a, y + ); + + input clk; + + input a, y; + + output reg x; + + always @(posedge clk) begin + x <= a & y; + end + +endmodule diff --git a/test_regress/t/t_vpi_public_depth_off.pl b/test_regress/t/t_vpi_public_depth_off.pl new file mode 100755 index 000000000..64de60c4d --- /dev/null +++ b/test_regress/t/t_vpi_public_depth_off.pl @@ -0,0 +1,34 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2010 by Wilson Snyder. 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(simulator => 1); + +pli_filename("t_vpi_public_depth.cpp"); +top_filename("t/t_vpi_public_depth.v"); + +skip("Known compiler limitation") + if $Self->cxx_version =~ /\(GCC\) 4.4/; + +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + iv_flags2 => ["-g2005-sv"], + verilator_flags2 => ["+define+USE_DOLLAR_C32 --exe --vpi --no-l2name $Self->{t_dir}/t_vpi_public_depth.cpp --public-depth 2"], + make_flags => 'CPPFLAGS_ADD=-DTEST_VPI_PUBLIC_DEPTH_OFF', + ); + +execute( + use_libvpi => 1, + check_finished => 1, + ); + +ok(1); +1;