Internals: Extend VerilatedVar to hold force control signal pointers (#7217)

This commit is contained in:
Christian Hecken
2026-03-08 15:53:32 -04:00
committed by GitHub
parent 0019c12242
commit 9c5f4e2483
10 changed files with 463 additions and 319 deletions
+272 -64
View File
@@ -24,6 +24,7 @@
#include "V3Stats.h"
#include <algorithm>
#include <cstring>
#include <map>
#include <vector>
@@ -171,6 +172,259 @@ class EmitCSyms final : EmitCBaseVisitorConst {
return pos != std::string::npos ? scpname.substr(pos + 1) : scpname;
}
static std::tuple<int, int, std::string> getDimensions(const AstVar* const varp) {
int pdim = 0;
int udim = 0;
std::string bounds;
if (const AstBasicDType* const basicp = varp->basicp()) {
// Range is always first, it's not in "C" order
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
// Skip AstRefDType/AstTypedef, or return same node
dtypep = dtypep->skipRefp();
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
bounds += " ,";
bounds += std::to_string(adtypep->left());
bounds += ",";
bounds += std::to_string(adtypep->right());
if (VN_IS(dtypep, PackArrayDType))
pdim++;
else
udim++;
dtypep = adtypep->subDTypep();
} else {
if (basicp->isRanged()) {
bounds += " ,";
bounds += std::to_string(basicp->left());
bounds += ",";
bounds += std::to_string(basicp->right());
pdim++;
}
break; // AstBasicDType - nothing below, 1
}
}
}
return {pdim, udim, bounds};
}
static std::pair<bool, std::string> isForceControlSignal(const AstVar* const signalVarp) {
// __VforceRd should not show up here because it is never public, but just in case it does,
// it should be skipped because the VPI code lazily re-creates its value
for (const std::string forceControlSuffix : {"__VforceEn", "__VforceVal", "__VforceRd"}) {
const std::size_t suffixPos = signalVarp->name().find(forceControlSuffix);
const bool suffixFound = suffixPos != std::string::npos;
const bool suffixIsAtEnd
= suffixPos + forceControlSuffix.length() == signalVarp->name().length();
if (!(suffixFound && suffixIsAtEnd)) continue;
const std::string baseSignalName = signalVarp->name().substr(0, suffixPos);
return std::pair<bool, std::string>{true, baseSignalName};
}
return std::pair<bool, std::string>{false, ""};
}
static std::string insertVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
const AstVar* const varp, const int udim, const int pdim,
const std::string& bounds) {
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName) + "->varInsert(\"";
stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"';
const std::string varName = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect())
+ "." + protect(varp->name());
if (!varp->isParam()) {
stmt += ", &(";
stmt += varName;
stmt += "), false, ";
} else if (varp->vlEnumType() == "VLVT_STRING"
&& !VN_IS(varp->subDTypep(), UnpackArrayDType)) {
stmt += ", const_cast<void*>(static_cast<const void*>(";
stmt += varName;
stmt += ".c_str())), true, ";
} else {
stmt += ", const_cast<void*>(static_cast<const void*>(&(";
stmt += varName;
stmt += "))), true, ";
}
stmt += varp->vlEnumType(); // VLVT_UINT32 etc
stmt += ", ";
stmt += varp->vlEnumDir(); // VLVD_IN etc
stmt += ", ";
stmt += std::to_string(udim);
stmt += ", ";
stmt += std::to_string(pdim);
stmt += bounds;
stmt += ")";
return stmt;
}
std::string insertForceableVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
const AstVar* const varp, const int udim,
const int pdim, const std::string& bounds) {
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName) + "->forceableVarInsert(\"";
stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"';
const std::string varName = VIdProtect::protectIf(scopep->nameDotless(), scopep->protect())
+ "." + protect(varp->name());
assert(!varp->isParam()); // Forceable params do not make sense
stmt += ", &(";
stmt += varName;
stmt += "), false, ";
stmt += varp->vlEnumType(); // VLVT_UINT32 etc
stmt += ", ";
stmt += varp->vlEnumDir(); // VLVD_IN etc
stmt += ", {";
// Find __VforceEn
{
const std::string enableSignalKey = getKeyName(scopep, varp->name() + "__VforceEn");
const std::map<const std::string, ScopeVarData>::const_iterator itpair
= m_scopeVars.find(enableSignalKey);
if (itpair == m_scopeVars.end()) {
varp->v3fatalSrc("Signal " << varp->prettyNameQ()
<< " is marked forceable, but the force enable signal '"
<< varp->name() << "__VforceEn"
<< "' can not be found in m_scopeVars with key '"
<< enableSignalKey << "'.");
}
const ScopeVarData& svd = itpair->second;
const AstScope* const scopep = svd.m_scopep;
const AstVar* const varp = svd.m_varp;
const std::tuple<int, int, std::string> dimensions = getDimensions(varp);
const int pdim = std::get<0>(dimensions);
const int udim = std::get<1>(dimensions);
const std::string bounds = std::get<2>(dimensions);
stmt += insertVarStatement(svd, scopep, varp, udim, pdim, bounds);
}
stmt += ",";
// Find __VforceVal
{
const std::string valueSignalKey = getKeyName(scopep, varp->name() + "__VforceVal");
const std::map<const std::string, ScopeVarData>::const_iterator itpair
= m_scopeVars.find(valueSignalKey);
if (itpair == m_scopeVars.end()) {
varp->v3fatalSrc("Signal " << varp->prettyNameQ()
<< " is marked forceable, but the force value signal '"
<< varp->name() << "__VforceVal"
<< "' can not be found in m_scopeVars with key '"
<< valueSignalKey << "'.");
}
const ScopeVarData& svd = itpair->second;
const AstScope* const scopep = svd.m_scopep;
const AstVar* const varp = svd.m_varp;
const std::tuple<int, int, std::string> dimensions = getDimensions(varp);
const int pdim = std::get<0>(dimensions);
const int udim = std::get<1>(dimensions);
const std::string bounds = std::get<2>(dimensions);
stmt += insertVarStatement(svd, scopep, varp, udim, pdim, bounds);
}
stmt += "}";
stmt += ", ";
stmt += std::to_string(udim);
stmt += ", ";
stmt += std::to_string(pdim);
stmt += bounds;
stmt += ")";
return stmt;
}
static std::string getKeyName(const AstScope* const scopep, const std::string& signal_name) {
// Copies the process from `varsExpand` which created the keys in the first place, in order
// signal can be found.
std::string whole = scopep->name() + "__DOT__" + signal_name;
std::string scpName;
if (VString::startsWith(whole, "__DOT__TOP")) whole.replace(0, 10, "");
const std::string::size_type dpos = whole.rfind("__DOT__");
if (dpos != std::string::npos) { scpName = whole.substr(0, dpos); }
const std::string scpSym = scopeSymString(VName::dehash(scpName));
return scpSym + " " + signal_name;
}
bool baseSignalIsValid(const AstScope* const scopep, const AstVar* const controlSignalVarp,
const std::string& baseSignalName) const {
const std::string baseSignalKey = getKeyName(scopep, baseSignalName);
const std::map<const std::string, ScopeVarData>::const_iterator baseSignalIt
= m_scopeVars.find(baseSignalKey);
if (baseSignalIt == m_scopeVars.end()) {
// This means that the signal is forceable, but not public, so only the force control
// signals show up in the m_scopeVars, but not the base signal itself.
// Expect this branch not to be hit, because `baseSignalIsPublic` is checked before
// this, so this function will not get called in that case.
return false;
} else {
const AstVar* const baseSignalVarp = baseSignalIt->second.m_varp;
if (m_scopeVars.count(baseSignalName) > 1) {
baseSignalVarp->v3fatalSrc(
"Found Signal " << baseSignalName
<< ", but also found at least one other signal with name"
<< baseSignalName << " occurring in the same scope.");
return false;
}
if (!baseSignalVarp->isForceable()) {
controlSignalVarp->v3fatalSrc(
"Found signal " << controlSignalVarp->prettyNameQ()
<< " which is a force control signal, but the base signal "
<< baseSignalVarp->prettyNameQ()
<< " is not marked as forceable.");
return false;
}
}
return true;
}
bool baseSignalIsPublic(const AstScope* const scopep,
const std::string& baseSignalName) const {
const std::string baseSignalKey = getKeyName(scopep, baseSignalName);
const std::map<const std::string, ScopeVarData>::const_iterator baseSignalIt
= m_scopeVars.find(baseSignalKey);
if (baseSignalIt == m_scopeVars.end()) {
return false;
} else {
const AstVar* const baseSignalVarp = baseSignalIt->second.m_varp;
// Should not actually occur if the variable is in the m_scopeVars
if (!baseSignalVarp->isSigPublic()) return false;
}
return true;
}
bool forceControlSignalsAreValid(const AstScope* const scopep,
const AstVar* const baseSignalVarp) const {
constexpr std::array<const char*, 2> forceControlSuffixes = {"__VforceEn", "__VforceVal"};
return std::all_of(
forceControlSuffixes.begin(), forceControlSuffixes.end(),
[scopep, baseSignalVarp, this](const std::string& forceControlSuffix) {
const std::string controlSignalName = baseSignalVarp->name() + forceControlSuffix;
const std::string controlSignalKey = getKeyName(scopep, controlSignalName);
const std::size_t controlSignalCount = m_scopeVars.count(controlSignalKey);
if (controlSignalCount == 0) {
baseSignalVarp->v3fatalSrc("Signal "
<< baseSignalVarp->prettyNameQ()
<< " is marked forceable, but the control signal '"
<< controlSignalName
<< "' can not be found in m_scopeVars with key '"
<< controlSignalKey << "'.");
return false;
}
if (controlSignalCount > 1) {
baseSignalVarp->v3fatalSrc("The control signal '"
<< controlSignalName << "' for forceable signal "
<< baseSignalVarp->prettyNameQ()
<< " occurs several times within the same scope.");
return false;
}
return true;
});
}
/// (scp, m_vpiScopeCandidates, m_scopeNames) -> m_scopeNames
/// Look for parent scopes of scp in m_vpiScopeCandidates (separated by __DOT__ or ".")
/// Then add/update entry in m_scopeNames if not already there
@@ -770,74 +1024,28 @@ std::vector<std::string> EmitCSyms::getSymCtorStmts() {
const ScopeVarData& svd = itpair.second;
const AstScope* const scopep = svd.m_scopep;
const AstVar* const varp = svd.m_varp;
int pdim = 0;
int udim = 0;
std::string bounds;
if (const AstBasicDType* const basicp = varp->basicp()) {
// Range is always first, it's not in "C" order
for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) {
// Skip AstRefDType/AstTypedef, or return same node
dtypep = dtypep->skipRefp();
if (const AstNodeArrayDType* const adtypep = VN_CAST(dtypep, NodeArrayDType)) {
bounds += " ,";
bounds += std::to_string(adtypep->left());
bounds += ",";
bounds += std::to_string(adtypep->right());
if (VN_IS(dtypep, PackArrayDType))
pdim++;
else
udim++;
dtypep = adtypep->subDTypep();
} else {
if (basicp->isRanged()) {
bounds += " ,";
bounds += std::to_string(basicp->left());
bounds += ",";
bounds += std::to_string(basicp->right());
pdim++;
}
break; // AstBasicDType - nothing below, 1
}
}
const std::tuple<int, int, std::string> dimensions = getDimensions(varp);
const int pdim = std::get<0>(dimensions);
const int udim = std::get<1>(dimensions);
const std::string bounds = std::get<2>(dimensions);
const std::pair<bool, std::string> isForceControlResult = isForceControlSignal(varp);
const bool currentSignalIsForceControlSignal = isForceControlResult.first;
const std::string baseSignalName = isForceControlResult.second;
if (currentSignalIsForceControlSignal && baseSignalIsPublic(scopep, baseSignalName)
&& baseSignalIsValid(scopep, varp, baseSignalName)) {
continue;
}
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName) + "->varInsert(\"";
stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"';
const std::string varName
= VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "."
+ protect(varp->name());
if (!varp->isParam()) {
stmt += ", &(";
stmt += varName;
stmt += "), false, ";
} else if (varp->vlEnumType() == "VLVT_STRING"
&& !VN_IS(varp->subDTypep(), UnpackArrayDType)) {
stmt += ", const_cast<void*>(static_cast<const void*>(";
stmt += varName;
stmt += ".c_str())), true, ";
if (varp->isForceable() && forceControlSignalsAreValid(scopep, varp)) {
const std::string stmt
= insertForceableVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";";
add(stmt);
} else {
stmt += ", const_cast<void*>(static_cast<const void*>(&(";
stmt += varName;
stmt += "))), true, ";
const std::string stmt
= insertVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";";
add(stmt);
}
stmt += varp->vlEnumType(); // VLVT_UINT32 etc
stmt += ", ";
stmt += varp->vlEnumDir(); // VLVD_IN etc
if (varp->dtypep()->skipRefp()->isSigned()) stmt += "|VLVF_SIGNED";
if (AstBasicDType* const basicp = varp->dtypep()->skipRefp()->basicp()) {
if (basicp->keyword() == VBasicDTypeKwd::BIT) stmt += "|VLVF_BITVAR";
}
stmt += ", ";
stmt += std::to_string(udim);
stmt += ", ";
stmt += std::to_string(pdim);
stmt += bounds;
stmt += ");";
add(stmt);
}
}