diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 3bab69be0..80c5d77eb 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -248,10 +248,26 @@ public: class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio { protected: const VerilatedScope* const m_scopep; + bool m_toplevel = false; public: explicit VerilatedVpioScope(const VerilatedScope* scopep) - : m_scopep{scopep} {} + : m_scopep{scopep} { + std::string scopename = m_scopep->name(); + std::string::size_type pos = std::string::npos; + // Look for '.' not inside escaped identifier + size_t i = 0; + while (i < scopename.length()) { + if (scopename[i] == '\\') { + while (i < scopename.length() && scopename[i] != ' ') ++i; + ++i; // Proc ' ', it should always be there. Then grab '.' on next cycle + } else { + while (i < scopename.length() && scopename[i] != '.') ++i; + if (i < scopename.length()) pos = i++; + } + } + if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true; + } ~VerilatedVpioScope() override = default; static VerilatedVpioScope* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); @@ -260,6 +276,7 @@ public: const VerilatedScope* scopep() const { return m_scopep; } const char* name() const override { return m_scopep->name(); } const char* fullname() const override { return m_scopep->name(); } + bool toplevel() const { return m_toplevel; } }; class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase { @@ -350,10 +367,15 @@ class VerilatedVpioVarIter final : public VerilatedVpio { const VerilatedScope* const m_scopep; VerilatedVarNameMap::const_iterator m_it; bool m_started = false; + const VerilatedScope* m_topscopep = nullptr; public: - explicit VerilatedVpioVarIter(const VerilatedScope* scopep) - : m_scopep{scopep} {} + explicit VerilatedVpioVarIter(const VerilatedVpioScope* vop) + : m_scopep{vop->scopep()} { + if (VL_UNLIKELY(vop->toplevel())) + // This is a toplevel, so get TOP scope to search for ports during vpi_scan. + m_topscopep = Verilated::threadContextp()->scopeFind("TOP"); + } ~VerilatedVpioVarIter() override = default; static VerilatedVpioVarIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); @@ -375,6 +397,10 @@ public: delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; } + if (VL_UNLIKELY(m_topscopep)) { + if (const VerilatedVar* topvarp = m_topscopep->varFind(m_it->second.name())) + return ((new VerilatedVpioVar{topvarp, m_topscopep})->castVpiHandle()); + } return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle()); } delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle @@ -1653,24 +1679,38 @@ vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) { return (new VerilatedVpioScope{scopep})->castVpiHandle(); } } - const char* baseNamep = scopeAndName.c_str(); + std::string basename = scopeAndName; std::string scopename; - const char* const dotp = std::strrchr(namep, '.'); - if (VL_LIKELY(dotp)) { - baseNamep = dotp + 1; - const size_t len = dotp - namep; - scopename = std::string{namep, len}; + std::string::size_type prevpos = std::string::npos; + std::string::size_type pos = std::string::npos; + // Split hierarchical names at last '.' not inside escaped identifier + size_t i = 0; + while (i < scopeAndName.length()) { + if (scopeAndName[i] == '\\') { + while (i < scopeAndName.length() && scopeAndName[i] != ' ') ++i; + ++i; // Proc ' ', it should always be there. Then grab '.' on next cycle + } else { + while (i < scopeAndName.length() && scopeAndName[i] != '.') ++i; + if (i < scopeAndName.length()) { + prevpos = pos; + pos = i++; + } + } } - - if (scopename.find('.') == std::string::npos) { - // This is a toplevel, hence search in our TOP ports first. + // Do the split + if (VL_LIKELY(pos != std::string::npos)) { + basename.erase(0, pos + 1); + scopename = scopeAndName.substr(0, pos); + } + if (prevpos == std::string::npos) { + // scopename is a toplevel (no '.' separator), so search in our TOP ports first. scopep = Verilated::threadContextp()->scopeFind("TOP"); - if (scopep) varp = scopep->varFind(baseNamep); + if (scopep) varp = scopep->varFind(basename.c_str()); } if (!varp) { scopep = Verilated::threadContextp()->scopeFind(scopename.c_str()); if (!scopep) return nullptr; - varp = scopep->varFind(baseNamep); + varp = scopep->varFind(basename.c_str()); } } if (!varp) return nullptr; @@ -1807,7 +1847,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { case vpiReg: { const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - return ((new VerilatedVpioVarIter{vop->scopep()})->castVpiHandle()); + return ((new VerilatedVpioVarIter{vop})->castVpiHandle()); } case vpiModule: { const VerilatedVpioModule* const vop = VerilatedVpioModule::castp(object); diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index bf020e06c..15bdb03d7 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -152,25 +152,23 @@ class EmitCSyms final : EmitCBaseVisitorConst { static string scopeDecodeIdentifier(const string& scpname) { string out = scpname; - // Remove hierarchy - string::size_type pos = out.rfind('.'); + string::size_type pos = string::npos; - // If there's more than one ident and an escape, find the true last ident - if (pos != string::npos && scpname.find('\\') != string::npos) { - size_t i = 0; - // always makes progress - while (i < scpname.length()) { - if (scpname[i] == '\\') { - while (i < scpname.length() && scpname[i] != ' ') ++i; - ++i; // Proc ' ', it should always be there. Then grab '.' on next cycle - } else { - while (i < scpname.length() && scpname[i] != '.') ++i; - if (i < scpname.length()) { pos = i++; } - } + // Remove hierarchy + size_t i = 0; + // always makes progress + while (i < scpname.length()) { + if (scpname[i] == '\\') { + while (i < scpname.length() && scpname[i] != ' ') ++i; + ++i; // Proc ' ', it should always be there. Then grab '.' on next cycle + } else { + while (i < scpname.length() && scpname[i] != '.') ++i; + if (i < scpname.length()) pos = i++; } } if (pos != std::string::npos) out.erase(0, pos + 1); + // Decode all escaped characters while ((pos = out.find("__0")) != string::npos) { unsigned int x; @@ -230,7 +228,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { } // UINFO(9, "For " << scopep->name() << " - " << varp->name() << " Scp " // << scpName << "Var " << varBase << endl); - const string varBasePretty = AstNode::prettyName(VName::dehash(varBase)); + const string varBasePretty = AstNode::vpiName(VName::dehash(varBase)); const string scpPretty = AstNode::prettyName(VName::dehash(scpName)); const string scpSym = scopeSymString(VName::dehash(scpName)); // UINFO(9, " scnameins sp " << scpName << " sp " << scpPretty << " ss " diff --git a/test_regress/t/t_vpi_escape.cpp b/test_regress/t/t_vpi_escape.cpp new file mode 100644 index 000000000..043a62a5d --- /dev/null +++ b/test_regress/t/t_vpi_escape.cpp @@ -0,0 +1,402 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2010-2023 by Wilson Snyder and Marlon James. 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" + +#else + +#include "verilated.h" +#include "verilated_vcd_c.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_escape.h" +#include "Vt_vpi_escape__Dpi.h" +#include "svdpi.h" + +#endif + +#include + +// These require the above. Comment prevents clang-format moving them +#include "TestCheck.h" +#include "TestSimulator.h" +#include "TestVpi.h" + +int errors = 0; +unsigned int main_time = 0; + +//====================================================================== + +// We cannot replace those with VL_STRINGIFY, not available when PLI is build +#define STRINGIFY(x) STRINGIFY2(x) +#define STRINGIFY2(x) #x + +const char* _sim_top() { + if (TestSimulator::is_verilator() || TestSimulator::is_icarus()) { + return "\\t.has.dots "; + } else { + return "top.\\t.has.dots "; + } +} + +const char* _my_rooted(const char* obj) { + static std::string buf; + std::ostringstream os; + os << _sim_top(); + if (*obj) os << "." << obj; + buf = os.str(); + return buf.c_str(); +} + +#define MY_VPI_HANDLE(signal) vpi_handle_by_name(const_cast(_my_rooted(signal)), NULL); + +int _mon_check_var() { +#ifdef TEST_VERBOSE + printf("-mon_check_var()\n"); +#endif + TestVpiHandle vh1 = vpi_handle_by_name(const_cast(_sim_top()), NULL); + TEST_CHECK_NZ(vh1); + + TestVpiHandle vh2 = MY_VPI_HANDLE("\\check;alias "); + TEST_CHECK_NZ(vh2); + + // scope attributes + const char* p; + p = vpi_get_str(vpiName, vh1); + TEST_CHECK_CSTR(p, "\\t.has.dots "); + p = vpi_get_str(vpiFullName, vh1); + TEST_CHECK_CSTR(p, _sim_top()); + p = vpi_get_str(vpiType, vh1); + TEST_CHECK_CSTR(p, "vpiModule"); + + TestVpiHandle vh3 = vpi_handle_by_name(const_cast("escaped_normal"), vh1); + TEST_CHECK_NZ(vh3); + + // onebit attributes + PLI_INT32 d; + d = vpi_get(vpiType, vh3); + if (TestSimulator::is_verilator()) { + TEST_CHECK_EQ(d, vpiReg); + } else { + TEST_CHECK_EQ(d, vpiNet); + } + if (TestSimulator::has_get_scalar()) { + d = vpi_get(vpiVector, vh3); + TEST_CHECK_EQ(d, 0); + } + + p = vpi_get_str(vpiName, vh3); + TEST_CHECK_CSTR(p, "escaped_normal"); + p = vpi_get_str(vpiFullName, vh3); + // Toplevel port returns TOP.xxxxx + TEST_CHECK_CSTR(p, "TOP.escaped_normal"); + p = vpi_get_str(vpiType, vh3); + if (TestSimulator::is_verilator()) { + TEST_CHECK_CSTR(p, "vpiReg"); + } else { + TEST_CHECK_CSTR(p, "vpiNet"); + } + + TestVpiHandle vh4 = MY_VPI_HANDLE("\\x.y "); + TEST_CHECK_NZ(vh4); + + // Test that the toplevel TOP.xxxxx search is skipped + // when the path to the scope has more than one level. + TestVpiHandle vh5 = MY_VPI_HANDLE("\\mod.with_dot .\\b.c "); + TEST_CHECK_NZ(vh5); + p = vpi_get_str(vpiFullName, vh5); + TEST_CHECK_CSTR(p, "\\t.has.dots .\\mod.with_dot .\\b.c "); + + return errors; +} + +int _mon_check_iter() { +#ifdef TEST_VERBOSE + printf("-mon_check_iter()\n"); +#endif + const char* p; + + TestVpiHandle vh2 = MY_VPI_HANDLE("\\mod.with_dot "); + TEST_CHECK_NZ(vh2); + p = vpi_get_str(vpiName, vh2); + TEST_CHECK_CSTR(p, "\\mod.with_dot "); + if (TestSimulator::is_verilator()) { + p = vpi_get_str(vpiDefName, vh2); + TEST_CHECK_CSTR(p, ""); // Unsupported + } + + TestVpiHandle vh10 = vpi_iterate(vpiReg, vh2); + TEST_CHECK_NZ(vh10); + TEST_CHECK_EQ(vpi_get(vpiType, vh10), vpiIterator); + + { + TestVpiHandle vh11 = vpi_scan(vh10); + TEST_CHECK_NZ(vh11); + p = vpi_get_str(vpiFullName, vh11); +#ifdef TEST_VERBOSE + printf(" scanned %s\n", p); +#endif + TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .\\b.c ")); + } + { + TestVpiHandle vh12 = vpi_scan(vh10); + TEST_CHECK_NZ(vh12); + p = vpi_get_str(vpiFullName, vh12); +#ifdef TEST_VERBOSE + printf(" scanned %s\n", p); +#endif + TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .cyc")); + } + { + TestVpiHandle vh13 = vpi_scan(vh10); + TEST_CHECK_NZ(vh13); + p = vpi_get_str(vpiFullName, vh13); +#ifdef TEST_VERBOSE + printf(" scanned %s\n", p); +#endif + TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .subsig1")); + } + { + TestVpiHandle vh14 = vpi_scan(vh10); + TEST_CHECK_NZ(vh14); + p = vpi_get_str(vpiFullName, vh14); +#ifdef TEST_VERBOSE + printf(" scanned %s\n", p); +#endif + TEST_CHECK_CSTR(p, _my_rooted("\\mod.with_dot .subsig2")); + } + { + TestVpiHandle vh15 = vpi_scan(vh10); + vh10.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + TEST_CHECK_EQ(vh15, 0); + } + return errors; +} + +int _mon_check_ports() { +#ifdef TEST_VERBOSE + printf("-mon_check_ports()\n"); +#endif + // test writing to input port + TestVpiHandle vh1 = MY_VPI_HANDLE("a"); + TEST_CHECK_NZ(vh1); + + PLI_INT32 d; + d = vpi_get(vpiType, vh1); + if (TestSimulator::is_verilator()) { + TEST_CHECK_EQ(d, vpiReg); + } else { + TEST_CHECK_EQ(d, vpiNet); + } + + const char* portFullName; + if (TestSimulator::is_verilator()) { + portFullName = "TOP.a"; + } else { + portFullName = "t.a"; + } + + const char* name = vpi_get_str(vpiFullName, vh1); + TEST_CHECK_EQ(strcmp(name, portFullName), 0); + std::string handleName1 = name; + + s_vpi_value v; + v.format = vpiIntVal; + vpi_get_value(vh1, &v); + TEST_CHECK_EQ(v.value.integer, 0); + + s_vpi_time t; + t.type = vpiSimTime; + t.high = 0; + t.low = 0; + v.value.integer = 2; + vpi_put_value(vh1, &v, &t, vpiNoDelay); + v.value.integer = 100; + vpi_get_value(vh1, &v); + TEST_CHECK_EQ(v.value.integer, 2); + + // get handle of toplevel module + TestVpiHandle vht = MY_VPI_HANDLE(""); + TEST_CHECK_NZ(vht); + + d = vpi_get(vpiType, vht); + TEST_CHECK_EQ(d, vpiModule); + + TestVpiHandle vhi = vpi_iterate(vpiReg, vht); + TEST_CHECK_NZ(vhi); + + TestVpiHandle vh11; + std::string handleName2; + while ((vh11 = vpi_scan(vhi))) { + const char* fn = vpi_get_str(vpiFullName, vh11); +#ifdef TEST_VERBOSE + printf(" scanned %s\n", fn); +#endif + if (0 == strcmp(fn, portFullName)) { + handleName2 = fn; + break; + } + } + TEST_CHECK_NZ(vh11); // If get zero we never found the variable + vhi.release(); + TEST_CHECK_EQ(vpi_get(vpiType, vh11), vpiReg); + + TEST_CHECK_EQ(handleName1, handleName2); + + TestVpiHandle vh2 = MY_VPI_HANDLE("\\b.c "); + TEST_CHECK_NZ(vh2); + + if (TestSimulator::is_verilator()) { + portFullName = "TOP.\\b.c "; + } else { + portFullName = "t.\\b.c "; + } + + name = vpi_get_str(vpiFullName, vh2); + TEST_CHECK_EQ(strcmp(name, portFullName), 0); + handleName1 = name; + + v.format = vpiIntVal; + vpi_get_value(vh2, &v); + TEST_CHECK_EQ(v.value.integer, 0); + + t.type = vpiSimTime; + t.high = 0; + t.low = 0; + v.value.integer = 1; + vpi_put_value(vh2, &v, &t, vpiNoDelay); + v.value.integer = 0; + vpi_get_value(vh2, &v); + TEST_CHECK_EQ(v.value.integer, 1); + + vhi = vpi_iterate(vpiReg, vht); + TEST_CHECK_NZ(vhi); + + while ((vh11 = vpi_scan(vhi))) { + const char* fn = vpi_get_str(vpiFullName, vh11); +#ifdef TEST_VERBOSE + printf(" scanned %s\n", fn); +#endif + if (0 == strcmp(fn, portFullName)) { + handleName2 = fn; + break; + } + } + TEST_CHECK_NZ(vh11); // If get zero we never found the variable + vhi.release(); + TEST_CHECK_EQ(vpi_get(vpiType, vh11), vpiReg); + + TEST_CHECK_EQ(handleName1, handleName2); + + return errors; +} + +extern "C" int mon_check() { + // Callback from initial block in monitor +#ifdef TEST_VERBOSE + printf("-mon_check()\n"); +#endif + + if (int status = _mon_check_var()) return status; + if (int status = _mon_check_iter()) return status; + if (int status = _mon_check_ports()) return status; +#ifndef IS_VPI + VerilatedVpi::selfTest(); +#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 + +double sc_time_stamp() { return main_time; } +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); + + 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; + main_time += 10; + + while (vl_time_stamp64() < sim_time && !contextp->gotFinish()) { + main_time += 1; + topp->eval(); + VerilatedVpi::callValueCbs(); + topp->clk = !topp->clk; + // mon_do(); +#if VM_TRACE + if (tfp) tfp->dump(main_time); +#endif + } + if (!contextp->gotFinish()) { + vl_fatal(__FILE__, __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_escape.pl b/test_regress/t/t_vpi_escape.pl new file mode 100755 index 000000000..111934bb1 --- /dev/null +++ b/test_regress/t/t_vpi_escape.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 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 + +scenarios(simulator => 1); + +pli_filename("t_vpi_escape.cpp"); +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + sim_time => 100, + iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI -DWAVES"], + v_flags2 => ["+define+USE_VPI_NOT_DPI"], + verilator_flags2 => ["--exe --vpi --no-l2name --public-flat-rw $Self->{t_dir}/$Self->{name}.cpp"], + ); + +execute( + # run_env => "VPI_TRACE=" . Cwd::getcwd() . "/$Self->{obj_dir}/$Self->{name}_vpi.log", + # run_env => "VPI_TRACE=/tmp/$Self->{name}_vpi.log", + use_libvpi => 1, + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_escape.v b/test_regress/t/t_vpi_escape.v new file mode 100644 index 000000000..b575dc199 --- /dev/null +++ b/test_regress/t/t_vpi_escape.v @@ -0,0 +1,118 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 USE_VPI_NOT_DPI +//We call it via $c so we can verify DPI isn't required - see bug572 +`else +import "DPI-C" context function int mon_check(); +`endif + +module \t.has.dots (/*AUTOARG*/ + // Outputs + \escaped_normal , double__underscore, \9num , \bra[ket]slash/dash-colon:9backslash\done , \x.y , + // Inputs + clk, \b.c , a + ); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + input [7:0] a; + input \b.c ; + + integer cyc; initial cyc=1; + + output \escaped_normal ; + wire \escaped_normal = cyc[0]; + + output double__underscore ; + wire double__underscore = cyc[0]; + + // C doesn't allow leading non-alpha, so must escape + output \9num ; + wire \9num = cyc[0]; + + output \bra[ket]slash/dash-colon:9backslash\done ; + wire \bra[ket]slash/dash-colon:9backslash\done = cyc[0]; + + output \x.y ; + wire \x.y = cyc[0]; + + wire \wire = cyc[0]; + + wire \check_alias = cyc[0]; + wire \check:alias = cyc[0]; + wire \check;alias = !cyc[0]; + + // These are *different entities*, bug83 + wire [31:0] \a0.cyc = ~a0.cyc; + wire [31:0] \other.cyc = ~a0.cyc; + + integer status; + + sub a0 (.cyc(cyc)); + + sub \mod.with_dot (.cyc(cyc)); + + initial begin + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif +`ifdef IVERILOG + status = $mon_check(); +`endif +`ifndef USE_VPI_NOT_DPI + status = mon_check(); +`endif + if (status != 0) begin + $write("%%Error: C Test failed with %0d error(s)\n", status); + $stop; + end + $write("%%Info: Checking results\n"); + end + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (escaped_normal != cyc[0]) $stop; + if (\escaped_normal != cyc[0]) $stop; + if (double__underscore != cyc[0]) $stop; + if (\9num != cyc[0]) $stop; + if (\bra[ket]slash/dash-colon:9backslash\done != cyc[0]) $stop; + if (\x.y != cyc[0]) $stop; + if (\wire != cyc[0]) $stop; + if (\check_alias != cyc[0]) $stop; + if (\check:alias != cyc[0]) $stop; + if (\check;alias != !cyc[0]) $stop; + if (\a0.cyc != ~cyc) $stop; + if (\other.cyc != ~cyc) $stop; + if (cyc==10) begin + if (a != 2) $stop; + if (\b.c != 1) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module sub ( + input [31:0] cyc + ); + reg \b.c ; + reg subsig1; + reg subsig2; +`ifdef IVERILOG + // stop icarus optimizing signals away + wire redundant = subsig1 | subsig2 | \b.c ; +`endif +endmodule diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index f7db91da2..465bf0891 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -22,6 +22,9 @@ #ifdef T_VPI_VAR2 #include "Vt_vpi_var2.h" #include "Vt_vpi_var2__Dpi.h" +#elif defined(T_VPI_VAR3) +#include "Vt_vpi_var3.h" +#include "Vt_vpi_var3__Dpi.h" #else #include "Vt_vpi_var.h" #include "Vt_vpi_var__Dpi.h" @@ -440,6 +443,79 @@ int _mon_check_varlist() { return 0; } +int _mon_check_ports() { +#ifdef TEST_VERBOSE + printf("-mon_check_ports()\n"); +#endif + // test writing to input port + TestVpiHandle vh1 = VPI_HANDLE("a"); + TEST_CHECK_NZ(vh1); + + PLI_INT32 d; + d = vpi_get(vpiType, vh1); + if (TestSimulator::is_verilator()) { + TEST_CHECK_EQ(d, vpiReg); + } else { + TEST_CHECK_EQ(d, vpiNet); + } + + const char* portFullName; + if (TestSimulator::is_verilator()) { + portFullName = "TOP.a"; + } else { + portFullName = "t.a"; + } + + const char* name = vpi_get_str(vpiFullName, vh1); + TEST_CHECK_EQ(strcmp(name, portFullName), 0); + std::string handleName1 = name; + + s_vpi_value v; + v.format = vpiIntVal; + vpi_get_value(vh1, &v); + TEST_CHECK_EQ(v.value.integer, 0); + + s_vpi_time t; + t.type = vpiSimTime; + t.high = 0; + t.low = 0; + v.value.integer = 2; + vpi_put_value(vh1, &v, &t, vpiNoDelay); + v.value.integer = 100; + vpi_get_value(vh1, &v); + TEST_CHECK_EQ(v.value.integer, 2); + + // get handle of toplevel module + TestVpiHandle vht = VPI_HANDLE(""); + TEST_CHECK_NZ(vht); + + d = vpi_get(vpiType, vht); + TEST_CHECK_EQ(d, vpiModule); + + TestVpiHandle vhi = vpi_iterate(vpiReg, vht); + TEST_CHECK_NZ(vhi); + + TestVpiHandle vh11; + std::string handleName2; + while ((vh11 = vpi_scan(vhi))) { + const char* fn = vpi_get_str(vpiFullName, vh11); +#ifdef TEST_VERBOSE + printf(" scanned %s\n", fn); +#endif + if (0 == strcmp(fn, portFullName)) { + handleName2 = fn; + break; + } + } + TEST_CHECK_NZ(vh11); // If get zero we never found the variable + vhi.release(); + TEST_CHECK_EQ(vpi_get(vpiType, vh11), vpiReg); + + TEST_CHECK_EQ(handleName1, handleName2); + + return errors; +} + int _mon_check_getput() { TestVpiHandle vh2 = VPI_HANDLE("onebit"); CHECK_RESULT_NZ(vh2); @@ -792,6 +868,10 @@ extern "C" int mon_check() { if (int status = _mon_check_var()) return status; if (int status = _mon_check_varlist()) return status; if (int status = _mon_check_var_long_name()) return status; +// Ports are not public_flat_rw in t_vpi_var +#if defined(T_VPI_VAR2) || defined(T_VPI_VAR3) + if (int status = _mon_check_ports()) return status; +#endif if (int status = _mon_check_getput()) return status; if (int status = _mon_check_getput_iter()) return status; if (int status = _mon_check_quad()) return status; diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index 495abb1f5..e25f0aebf 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -13,8 +13,10 @@ import "DPI-C" context function int mon_check(); `endif module t (/*AUTOARG*/ + // Outputs + x, // Inputs - clk + clk, a ); `ifdef VERILATOR @@ -25,6 +27,9 @@ extern "C" int mon_check(); input clk; + input [7:0] a; + output reg [7:0] x; + reg onebit /*verilator public_flat_rw @(posedge clk) */; reg [2:1] twoone /*verilator public_flat_rw @(posedge clk) */; reg [2:1] fourthreetwoone[4:3] /*verilator public_flat_rw @(posedge clk) */; diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index 6726f7a9b..34e4c9cc5 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -23,8 +23,10 @@ module t /* verilator public_off */ ) (/*AUTOARG*/ + // Outputs + x, // Inputs - clk + clk, a ); `ifdef VERILATOR @@ -34,6 +36,10 @@ extern "C" int mon_check(); `endif input clk; + + input [7:0] a /* verilator public_flat_rw */; + output reg [7:0] x /* verilator public_flat_rw */; + /*verilator public_flat_rw_on @(posedge clk)*/ reg onebit; reg [2:1] twoone; diff --git a/test_regress/t/t_vpi_var3.pl b/test_regress/t/t_vpi_var3.pl new file mode 100755 index 000000000..3ffa3323e --- /dev/null +++ b/test_regress/t/t_vpi_var3.pl @@ -0,0 +1,31 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 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 + +scenarios(simulator => 1); + +pli_filename("t_vpi_var.cpp"); +compile( + make_top_shell => 0, + make_main => 0, + make_pli => 1, + sim_time => 2100, + iv_flags2 => ["-g2005-sv -D USE_VPI_NOT_DPI -DWAVES -DT_VPI_VAR3"], + v_flags2 => ["+define+USE_VPI_NOT_DPI"], + verilator_flags2 => ["--exe --vpi --no-l2name --public-flat-rw $Self->{t_dir}/t_vpi_var.cpp"], + ); + +execute( + use_libvpi => 1, + check_finished => 1, + all_run_flags => ['+PLUS +INT=1234 +STRSTR'] + ); + +ok(1); +1; diff --git a/test_regress/t/t_vpi_var3.v b/test_regress/t/t_vpi_var3.v new file mode 100644 index 000000000..4071d0bfe --- /dev/null +++ b/test_regress/t/t_vpi_var3.v @@ -0,0 +1,155 @@ +// 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 + +`ifdef USE_VPI_NOT_DPI +//We call it via $c so we can verify DPI isn't required - see bug572 +`else +import "DPI-C" context function int mon_check(); +`endif + +module t (/*AUTOARG*/ + // Outputs + x, + // Inputs + clk, a + ); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + input [7:0] a; + output reg [7:0] x; + + reg onebit; + reg [2:1] twoone; + reg [2:1] fourthreetwoone[4:3]; + reg LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND; + + // verilator lint_off ASCRANGE + reg [0:61] quads[2:3]; + // verilator lint_on ASCRANGE + + reg [31:0] count; + reg [31:0] half_count; + + reg [7:0] text_byte; + reg [15:0] text_half; + reg [31:0] text_word; + reg [63:0] text_long; + reg [511:0] text; + + integer status; + + real real1; + string str1; + + sub sub(); + + // Test loop + initial begin + count = 0; + onebit = 1'b0; + fourthreetwoone[3] = 0; // stop icarus optimizing away + text_byte = "B"; + text_half = "Hf"; + text_word = "Word"; + text_long = "Long64b"; + text = "Verilog Test module"; + + real1 = 1.0; + str1 = "hello"; + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif +`ifdef IVERILOG + status = $mon_check(); +`endif +`ifndef USE_VPI_NOT_DPI + status = mon_check(); +`endif + if (status!=0) begin + $write("%%Error: t_vpi_var.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("%%Info: Checking results\n"); + if (onebit != 1'b1) $stop; + if (quads[2] != 62'h12819213_abd31a1c) $stop; + if (quads[3] != 62'h1c77bb9b_3784ea09) $stop; + if (text_byte != "A") $stop; + if (text_half != "T2") $stop; + if (text_word != "Tree") $stop; + if (text_long != "44Four44") $stop; + if (text != "lorem ipsum") $stop; + if (str1 != "something a lot longer than hello") $stop; + if (real1 > 123456.7895 || real1 < 123456.7885 ) $stop; + end + + always @(posedge clk) begin + count <= count + 2; + if (count[1]) + half_count <= half_count + 2; + + if (count == 1000) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + genvar i; + generate + for (i=1; i<=6; i=i+1) begin : arr + arr #(.LENGTH(i)) arr(); + end + endgenerate + + genvar k; + generate + for (k=1; k<=6; k=k+1) begin : subs + sub subsub(); + end + endgenerate + +endmodule : t + +module sub; + reg subsig1; + reg subsig2; +`ifdef IVERILOG + // stop icarus optimizing signals away + wire redundant = subsig1 | subsig2; +`endif +endmodule : sub + +module arr; + + parameter LENGTH = 1; + + reg [LENGTH-1:0] sig; + reg [LENGTH-1:0] rfr; + + reg check; + reg verbose; + + initial begin + sig = {LENGTH{1'b0}}; + rfr = {LENGTH{1'b0}}; + end + + always @(posedge check) begin + if (verbose) $display("%m : %x %x", sig, rfr); + if (check && sig != rfr) $stop; + check <= 0; + end + +endmodule : arr