V3EmitCSyms: drop redundant varTriggersStructExpansion guard, add VPI test

This commit is contained in:
Nick Brereton 2026-07-14 18:52:26 -04:00
parent 992d1dd0ff
commit 5c6315a880
3 changed files with 46 additions and 23 deletions

View File

@ -338,27 +338,6 @@ class EmitCSyms final : EmitCBaseVisitorConst {
return stmt;
}
// True if addUOrStructMemberVars()/addUnpackedArrayUOrStructMemberVars()
// would expand this var (residual path only).
static bool varTriggersStructExpansion(const AstVar* const varp) {
const AstNodeDType* const dtypep = varp->dtypeSkipRefp();
if (const AstNodeUOrStructDType* const sdtypep = VN_CAST(dtypep, NodeUOrStructDType)) {
return !sdtypep->packed();
}
if (VN_IS(dtypep, UnpackArrayDType)) {
const AstNodeDType* elemp = dtypep;
while (const AstUnpackArrayDType* const adtypep
= VN_CAST(elemp->skipRefp(), UnpackArrayDType)) {
elemp = adtypep->subDTypep();
}
if (const AstNodeUOrStructDType* const sdtypep
= VN_CAST(elemp->skipRefp(), NodeUOrStructDType)) {
return !sdtypep->packed();
}
}
return false;
}
// Computed once here so the forceable-eligibility check can't be
// re-derived (and desynced) by the caller.
enum class TableEntryKind : uint8_t { kTableRow, kForceableResidual, kPlainResidual };
@ -379,8 +358,6 @@ class EmitCSyms final : EmitCBaseVisitorConst {
const std::string vlEnumType = varp->vlEnumType();
if (needsEmittedEntSize(vlEnumType))
return TableEntryKind::kPlainResidual; // struct/union whole
if (varTriggersStructExpansion(varp))
return TableEntryKind::kPlainResidual; // member expansion
// Params are often 'static constexpr' (offsetof is invalid on those);
// string params also need a runtime .c_str().
if (varp->isParam()) return TableEntryKind::kPlainResidual;

View File

@ -671,6 +671,44 @@ int _mon_check_unpacked_struct_members() {
CHECK_RESULT(tmpValue.value.integer, 0xace0);
}
// unpacked array of a packed struct (element is a plain vector, not vpiStructVar)
{
TestVpiHandle vh210 = VPI_HANDLE("packed_struct_array_signal");
CHECK_RESULT_NZ(vh210);
CHECK_RESULT(vpi_get(vpiType, vh210), vpiRegArray);
CHECK_RESULT(vpi_get(vpiSize, vh210), 2);
TestVpiHandle vh211 = vpi_handle_by_index(vh210, 0);
CHECK_RESULT_NZ(vh211);
CHECK_RESULT(vpi_get(vpiType, vh211), vpiReg);
CHECK_RESULT(vpi_get(vpiSize, vh211), 8);
s_vpi_value putValue;
putValue.format = vpiIntVal;
putValue.value.integer = 0x5a;
vpi_put_value(vh211, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh211, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x5a);
TestVpiHandle vh212 = vpi_handle_by_index(vh210, 1);
CHECK_RESULT_NZ(vh212);
CHECK_RESULT(vpi_get(vpiType, vh212), vpiReg);
putValue.value.integer = 0x33;
vpi_put_value(vh212, &putValue, NULL, vpiNoDelay);
vpi_get_value(vh212, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x33);
vpi_get_value(vh211, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x5a);
TestVpiHandle vh213 = vpi_handle_by_name(
const_cast<PLI_BYTE8*>(TestSimulator::rooted("packed_struct_array_signal[1]")),
nullptr);
CHECK_RESULT_NZ(vh213);
vpi_get_value(vh213, &tmpValue);
CHECK_RESULT(tmpValue.value.integer, 0x33);
}
// array of unpacked structs with unpacked-array members
{
TestVpiHandle vh170 = VPI_HANDLE("parent_struct_array");

View File

@ -89,6 +89,14 @@ extern "C" int mon_check();
input wire unpacked_struct_t wire_struct_array_port [1:0] /*verilator public_flat_rw*/;
unpacked_struct_t struct_array_signal [1:0] /*verilator public_flat_rw*/;
// Unpacked array of a packed (not unpacked) struct element
typedef struct packed {
logic [6:0] packed_member_a;
logic packed_member_b;
} packed_leaf_struct_t;
packed_leaf_struct_t packed_struct_array_signal [1:0] /*verilator public_flat_rw*/;
typedef struct {
logic [6:0] unsigned_member;
logic signed [6:0] signed_member;