Skip to content

Commit

Permalink
Fix VPI TOP level variable iteration (verilator#3919) (verilator#4618)
Browse files Browse the repository at this point in the history
  • Loading branch information
marlonjames authored Nov 7, 2023
1 parent dc10118 commit 5ba7084
Show file tree
Hide file tree
Showing 10 changed files with 899 additions and 32 deletions.
70 changes: 55 additions & 15 deletions include/verilated_vpi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,26 @@ class VerilatedVpioRangeIter final : public VerilatedVpio {
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<VerilatedVpioScope*>(reinterpret_cast<VerilatedVpio*>(h));
Expand All @@ -260,6 +276,7 @@ class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
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 {
Expand Down Expand Up @@ -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<VerilatedVpioVarIter*>(reinterpret_cast<VerilatedVpio*>(h));
Expand All @@ -375,6 +397,10 @@ class VerilatedVpioVarIter final : public VerilatedVpio {
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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
28 changes: 13 additions & 15 deletions src/V3EmitCSyms.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,25 +152,23 @@ class EmitCSyms final : EmitCBaseVisitorConst {

static string scopeDecodeIdentifier(const string& scpname) {
string out = scpname;
string::size_type pos = string::npos;

// Remove hierarchy
string::size_type pos = out.rfind('.');

// 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++; }
}
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;
Expand Down Expand Up @@ -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 "
Expand Down
Loading

0 comments on commit 5ba7084

Please sign in to comment.