diff --git a/include/verilated.h b/include/verilated.h index 77deeefb1..4a2e2895d 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -627,8 +627,9 @@ class VerilatedScope final { public: enum Type : uint8_t { SCOPE_MODULE, - SCOPE_OTHER - }; // Type of a scope, currently module is only interesting + SCOPE_OTHER, + SCOPE_PACKAGE + }; // Type of a scope, currently only module and package are interesting private: // Fastpath: VerilatedSyms* m_symsp = nullptr; // Symbol table diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 80c5d77eb..0a6c60373 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -474,12 +474,71 @@ public: } uint32_t type() const override { return vpiIterator; } vpiHandle dovpi_scan() override { - if (m_it == m_vec->end()) { - delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle - return nullptr; + while (true) { + if (m_it == m_vec->end()) { + delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + return nullptr; + } + const VerilatedScope::Type type = (*m_it)->type(); + const VerilatedScope* const modp = *m_it++; + if (type == VerilatedScope::SCOPE_MODULE) { + return (new VerilatedVpioModule{modp})->castVpiHandle(); + } + } + } +}; + +class VerilatedVpioPackage final : public VerilatedVpioScope { + std::string m_name; + std::string m_fullname; + +public: + explicit VerilatedVpioPackage(const VerilatedScope* modulep) + : VerilatedVpioScope{modulep} { + const char* fullname = m_scopep->name(); + if (std::strncmp(fullname, "TOP.", 4) == 0) fullname += 4; + m_fullname = std::string{fullname} + "::"; + if (m_fullname == "\\$unit ::") m_fullname = "$unit::"; + m_name = std::string(m_scopep->identifier()); + if (m_name == "\\$unit ") m_name = "$unit"; + } + static VerilatedVpioPackage* castp(vpiHandle h) { + return dynamic_cast(reinterpret_cast(h)); + } + uint32_t type() const override { return vpiPackage; } + const char* name() const override { return m_name.c_str(); } + const char* fullname() const override { return m_fullname.c_str(); } +}; + +class VerilatedVpioInstanceIter final : public VerilatedVpio { + const std::vector* m_vec; + std::vector::const_iterator m_it; + +public: + explicit VerilatedVpioInstanceIter(const std::vector& vec) + : m_vec{&vec} { + m_it = m_vec->begin(); + } + ~VerilatedVpioInstanceIter() override = default; + static VerilatedVpioInstanceIter* castp(vpiHandle h) { + return dynamic_cast(reinterpret_cast(h)); + } + uint32_t type() const override { return vpiIterator; } + vpiHandle dovpi_scan() override { + while (true) { + if (m_it == m_vec->end()) { + delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + return nullptr; + } + const VerilatedScope::Type type = (*m_it)->type(); + const VerilatedScope* const modp = *m_it++; + if (type == VerilatedScope::SCOPE_MODULE) { + return (new VerilatedVpioModule{modp})->castVpiHandle(); + } + if (type == VerilatedScope::SCOPE_PACKAGE) { + return (new VerilatedVpioPackage{modp})->castVpiHandle(); + } } - const VerilatedScope* const modp = *m_it++; - return (new VerilatedVpioModule{modp})->castVpiHandle(); } }; @@ -1857,6 +1916,13 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { if (it == map->end()) return nullptr; return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle()); } + case vpiInstance: { + if (object) return nullptr; + const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap(); + const auto it = vlstd::as_const(map)->find(nullptr); + if (it == map->end()) return nullptr; + return ((new VerilatedVpioInstanceIter{it->second})->castVpiHandle()); + } default: VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned", __func__, VerilatedVpiError::strFromVpiObjType(type)); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 15bdb03d7..b387b0797 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -318,7 +318,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { m_scopes.emplace_back(nodep, m_modp); if (v3Global.opt.vpi() && !nodep->isTop()) { - const string type = VN_IS(nodep->modp(), Package) ? "SCOPE_OTHER" : "SCOPE_MODULE"; + const string type = VN_IS(nodep->modp(), Package) ? "SCOPE_PACKAGE" : "SCOPE_MODULE"; const string name_pretty = AstNode::vpiName(nodep->shortName()); const int timeunit = m_modp->timeunit().powerOfTen(); m_vpiScopeCandidates.emplace( @@ -644,7 +644,9 @@ void EmitCSyms::emitScopeHier(bool destroy) { ++it) { const string name = it->second.m_prettyName; if (it->first == "TOP") continue; - if ((name.find('.') == string::npos) && (it->second.m_type == "SCOPE_MODULE")) { + const string scopeType = it->second.m_type; + if ((name.find('.') == string::npos) + && (scopeType == "SCOPE_MODULE" || scopeType == "SCOPE_PACKAGE")) { puts("__Vhier." + method + "(0, &" + protect("__Vscope_" + it->second.m_symName) + ");\n"); } diff --git a/test_regress/t/TestVpi.h b/test_regress/t/TestVpi.h index 01db39ced..71534d404 100644 --- a/test_regress/t/TestVpi.h +++ b/test_regress/t/TestVpi.h @@ -32,6 +32,7 @@ public: TestVpiHandle& operator=(vpiHandle h) { release(); m_handle = h; + m_freeit = true; return *this; } void release() { diff --git a/test_regress/t/t_vpi_package.cpp b/test_regress/t/t_vpi_package.cpp new file mode 100644 index 000000000..6ee46253f --- /dev/null +++ b/test_regress/t/t_vpi_package.cpp @@ -0,0 +1,217 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2011 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 "sv_vpi_user.h" + +#include + +#else + +#include "verilated.h" +#include "verilated_vcd_c.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_package.h" +#include "Vt_vpi_package__Dpi.h" +#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_package.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__; \ + } + +extern "C" { +int mon_check() { +#ifdef TEST_VERBOSE + printf("-mon_check()\n"); +#endif + + TestVpiHandle it = vpi_iterate(vpiModule, NULL); + + bool found_t = false; + while (true) { + TestVpiHandle handle = vpi_scan(it); + if (handle == NULL) break; + if (strcmp("t", vpi_get_str(vpiName, handle))) { + return __LINE__; + } else { + if (found_t) return __LINE__; + found_t = true; + } + } + it.freed(); + if (!found_t) return __LINE__; + + it = vpi_iterate(vpiInstance, NULL); + + found_t = false; + bool found_somepackage = false; + bool found_dollar_unit = false; + while (true) { + TestVpiHandle handle = vpi_scan(it); + if (handle == NULL) break; + const char* name = vpi_get_str(vpiName, handle); + const char* fullname = vpi_get_str(vpiFullName, handle); + if (!strcmp("t", name)) { + if (strcmp("t", fullname)) return __LINE__; + if (found_t) return __LINE__; + found_t = true; + } else if (!strcmp("somepackage", name)) { + if (strcmp("somepackage::", fullname)) return __LINE__; + if (found_somepackage) return __LINE__; + found_somepackage = true; + } else if (!strcmp("$unit", name)) { + if (strcmp("$unit::", fullname)) return __LINE__; + if (found_dollar_unit) return __LINE__; + found_dollar_unit = true; + } else { + return __LINE__; + } + } + it.freed(); + if (!found_t) return __LINE__; + if (!found_somepackage) return __LINE__; + if (!found_dollar_unit) return __LINE__; + + 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(); + contextp->timeInc(10); + + while (contextp->time() < sim_time && !contextp->gotFinish()) { + contextp->timeInc(1); + topp->eval(); + VerilatedVpi::callValueCbs(); + // 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_package.pl b/test_regress/t/t_vpi_package.pl new file mode 100755 index 000000000..4cb6a2780 --- /dev/null +++ b/test_regress/t/t_vpi_package.pl @@ -0,0 +1,29 @@ +#!/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, + verilator_flags2 => ["--exe --vpi --no-l2name $Self->{t_dir}/t_vpi_package.cpp"], + ); + +execute( + use_libvpi => 1, + check_finished => 1 + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_package.v b/test_regress/t/t_vpi_package.v new file mode 100644 index 000000000..3e7f1782d --- /dev/null +++ b/test_regress/t/t_vpi_package.v @@ -0,0 +1,34 @@ +// 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 + +import "DPI-C" context function int mon_check(); + +parameter int dollarUnitInt /*verilator public_flat_rd*/ = 3; + +package somepackage; + parameter int someInt /*verilator public_flat_rd*/ = 5; +endpackage + +module t (/*AUTOARG*/ + ); /*verilator public_module*/ + + parameter int someOtherInt /* verilator public_flat_rd*/ = 7; + + integer status; + + initial begin + status = mon_check(); + if (status!=0) begin + $write("%%Error: t_vpi_package.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t