Support VPI access to unpacked struct members (#7823)

This commit is contained in:
Nick Brereton
2026-06-23 07:04:51 -04:00
committed by GitHub
parent c76c94ef16
commit 6fbc7042a5
9 changed files with 1267 additions and 62 deletions
+162 -31
View File
@@ -176,26 +176,26 @@ 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) {
static std::tuple<int, int, std::string> getDimensions(const AstNodeDType* const rootDtypep) {
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 {
// Range is always first, it's not in "C" order
for (const AstNodeDType* dtypep = rootDtypep; 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 (const AstBasicDType* const basicp = dtypep->basicp()) {
if (basicp->isRanged()) {
bounds += " ,";
bounds += std::to_string(basicp->left());
@@ -203,13 +203,33 @@ class EmitCSyms final : EmitCBaseVisitorConst {
bounds += std::to_string(basicp->right());
pdim++;
}
break; // AstBasicDType - nothing below, 1
}
break; // Non-array leaf
}
}
return {pdim, udim, bounds};
}
static std::tuple<int, int, std::string> getDimensions(const AstVar* const varp) {
return getDimensions(varp->dtypep());
}
static int getUnpackedElements(const AstNodeDType* rootDtypep) {
int elements = 1;
for (const AstNodeDType* dtypep = rootDtypep; dtypep;) {
dtypep = dtypep->skipRefp();
const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType);
if (!adtypep) break;
elements *= adtypep->elementsConst();
dtypep = adtypep->subDTypep();
}
return elements;
}
static bool needsEmittedEntSize(const std::string& vlEnumType) {
return vlEnumType == "VLVT_STRUCT" || vlEnumType == "VLVT_UNION";
}
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 forceableVarInsert creates its VerilatedVar.
@@ -225,15 +245,47 @@ class EmitCSyms final : EmitCBaseVisitorConst {
return std::pair<bool, std::string>{false, ""};
}
static void appendVarProperties(std::string& stmt, const std::string& vlEnumType,
const std::string& vlEnumDir, const int udim, const int pdim,
const std::string& bounds, const std::string& entSize = "") {
stmt += vlEnumType; // VLVT_UINT32 etc
stmt += ", ";
stmt += vlEnumDir; // VLVD_IN etc
stmt += ", ";
stmt += std::to_string(udim);
if (!entSize.empty()) {
stmt += ", ";
stmt += entSize;
} else {
stmt += ", ";
stmt += std::to_string(pdim);
}
stmt += bounds;
stmt += ")";
}
static std::string memberVlEnumDir(const AstVar* const varp,
const AstNodeDType* const dtypep) {
std::string out = "((" + varp->vlEnumDir() + ") & ~(VLVF_SIGNED|VLVF_BITVAR))";
const AstNodeDType* const skipDTypep = dtypep->skipRefp();
if (skipDTypep->isSigned()) out += "|VLVF_SIGNED";
if (const AstBasicDType* const basicp = skipDTypep->basicp()) {
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
}
return out;
}
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());
const std::string vlEnumType = varp->vlEnumType();
const bool needsEntSize = needsEmittedEntSize(vlEnumType);
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName);
stmt += needsEntSize ? "->varInsertSized(\"" : "->varInsert(\"";
stmt += V3OutFormatter::quoteNameControls(protect(svd.m_varBasePretty)) + '"';
if (!varp->isParam()) {
stmt += ", &(";
@@ -250,18 +302,87 @@ class EmitCSyms final : EmitCBaseVisitorConst {
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 += ")";
const std::string entSize = needsEntSize
? "sizeof(" + varName + ") / "
+ std::to_string(getUnpackedElements(varp->dtypep()))
: "";
appendVarProperties(stmt, vlEnumType, varp->vlEnumDir(), udim, pdim, bounds, entSize);
return stmt;
}
static std::string insertDTypeVarStatement(const ScopeVarData& svd,
const AstScope* const scopep,
const std::string& prettyName,
const std::string& cName,
const AstNodeDType* const dtypep, const int udim,
const int pdim, const std::string& bounds) {
const std::string vlEnumType = dtypep->vlEnumType();
const bool needsEntSize = needsEmittedEntSize(vlEnumType);
std::string stmt;
stmt += protect("__Vscopep_" + svd.m_scopeName);
stmt += needsEntSize ? "->varInsertSized(\"" : "->varInsert(\"";
stmt += V3OutFormatter::quoteNameControls(prettyName) + '"';
stmt += ", &(";
stmt += VIdProtect::protectIf(scopep->nameDotless(), scopep->protect());
stmt += ".";
stmt += cName;
stmt += "), false, ";
const std::string varName
= VIdProtect::protectIf(scopep->nameDotless(), scopep->protect()) + "." + cName;
const std::string entSize
= needsEntSize
? "sizeof(" + varName + ") / " + std::to_string(getUnpackedElements(dtypep))
: "";
appendVarProperties(stmt, vlEnumType, memberVlEnumDir(svd.m_varp, dtypep), udim, pdim,
bounds, entSize);
return stmt;
}
static void addUOrStructMemberVars(std::vector<std::string>& stmts, const ScopeVarData& svd,
const AstScope* const scopep,
const std::string& prettyPrefix, const std::string& cPrefix,
const AstNodeUOrStructDType* const sdtypep) {
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
itemp = VN_AS(itemp->nextp(), MemberDType)) {
const AstNodeDType* const itemDTypep = itemp->dtypep();
const std::string prettyName
= prettyPrefix + "." + AstNode::vpiName(itemp->shortName());
const std::string cName = cPrefix + "." + itemp->nameProtect();
const std::tuple<int, int, std::string> dimensions = getDimensions(itemDTypep);
const int pdim = std::get<0>(dimensions);
const int udim = std::get<1>(dimensions);
const std::string bounds = std::get<2>(dimensions);
stmts.emplace_back(insertDTypeVarStatement(svd, scopep, prettyName, cName, itemDTypep,
udim, pdim, bounds)
+ ";");
if (const AstNodeUOrStructDType* const subp
= VN_CAST(itemDTypep->skipRefp(), NodeUOrStructDType)) {
if (!subp->packed())
addUOrStructMemberVars(stmts, svd, scopep, prettyName, cName, subp);
} else if (VN_IS(itemDTypep->skipRefp(), UnpackArrayDType)) {
addUnpackedArrayUOrStructMemberVars(stmts, svd, scopep, prettyName, cName,
itemDTypep);
}
}
}
static void addUnpackedArrayUOrStructMemberVars(std::vector<std::string>& stmts,
const ScopeVarData& svd,
const AstScope* const scopep,
const std::string& prettyPrefix,
const std::string& cPrefix,
const AstNodeDType* const dtypep) {
const AstNodeDType* const skipDTypep = dtypep->skipRefp();
if (const AstUnpackArrayDType* const adtypep = VN_CAST(skipDTypep, UnpackArrayDType)) {
addUnpackedArrayUOrStructMemberVars(stmts, svd, scopep, prettyPrefix, cPrefix + "[0]",
adtypep->subDTypep());
} else if (const AstNodeUOrStructDType* const sdtypep
= VN_CAST(skipDTypep, NodeUOrStructDType)) {
if (!sdtypep->packed())
addUOrStructMemberVars(stmts, svd, scopep, prettyPrefix, cPrefix, sdtypep);
}
}
std::string insertForceableVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
const AstVar* const varp, const int udim,
const int pdim, const std::string& bounds) {
@@ -1061,6 +1182,16 @@ std::vector<std::string> EmitCSyms::getSymCtorStmts() {
const std::string stmt
= insertVarStatement(svd, scopep, varp, udim, pdim, bounds) + ";";
add(stmt);
if (const AstNodeUOrStructDType* const sdtypep
= VN_CAST(varp->dtypeSkipRefp(), NodeUOrStructDType)) {
if (!sdtypep->packed()) {
addUOrStructMemberVars(stmts, svd, scopep, svd.m_varBasePretty,
protect(varp->name()), sdtypep);
}
} else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) {
addUnpackedArrayUOrStructMemberVars(stmts, svd, scopep, svd.m_varBasePretty,
protect(varp->name()), varp->dtypep());
}
}
}
}