mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Fix VPI discoverability of forceable unpacked struct members (#8445)
This commit is contained in:
@@ -311,6 +311,7 @@ Steven Hugg
|
||||
Stuart Morris
|
||||
sumpster
|
||||
Szymon Gizler
|
||||
Szymon Jędras
|
||||
Sören Tempel
|
||||
Sumanth Kadiyala
|
||||
Sunimali Rathnayake
|
||||
|
||||
@@ -2485,7 +2485,7 @@ public:
|
||||
string vlArgType(bool named, bool forReturn, bool forFunc, const string& namespc = "",
|
||||
bool asRef = false, bool constRef = false) const;
|
||||
string vlEnumType() const; // Return VerilatorVarType: VLVT_UINT32, etc
|
||||
string vlEnumDir() const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlEnumDir(bool forMember = false) const; // Return VerilatorVarDir: VLVD_INOUT, etc
|
||||
string vlPropDecl(const string& propName) const; // Return VerilatorVarProps declaration
|
||||
void combineType(VVarType type);
|
||||
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||
|
||||
+4
-4
@@ -4102,7 +4102,7 @@ string AstVar::vlArgType(bool named, bool forReturn, bool forFunc, const string&
|
||||
}
|
||||
return ostatic + dtypep()->cType(oname, forFunc, asRef);
|
||||
}
|
||||
string AstVar::vlEnumDir() const {
|
||||
string AstVar::vlEnumDir(bool forMember) const {
|
||||
string out;
|
||||
if (isInout()) {
|
||||
out = "VLVD_INOUT";
|
||||
@@ -4119,17 +4119,17 @@ string AstVar::vlEnumDir() const {
|
||||
} else if (isSigUserRdPublic()) {
|
||||
out += "|VLVF_PUB_RD";
|
||||
}
|
||||
if (isForceable()) out += "|VLVF_FORCEABLE";
|
||||
if (isForceable() && !forMember) out += "|VLVF_FORCEABLE";
|
||||
if (isContinuously()) out += "|VLVF_CONTINUOUSLY";
|
||||
//
|
||||
if (const AstBasicDType* const bdtypep = basicp()) {
|
||||
if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY";
|
||||
}
|
||||
//
|
||||
if (dtypep()->skipRefp()->isSigned()) out += "|VLVF_SIGNED";
|
||||
if (dtypep()->skipRefp()->isSigned() && !forMember) out += "|VLVF_SIGNED";
|
||||
//
|
||||
if (AstBasicDType* const basicp = dtypep()->skipRefp()->basicp()) {
|
||||
if (basicp->keyword() == VBasicDTypeKwd::BIT) out += "|VLVF_BITVAR";
|
||||
if (basicp->keyword() == VBasicDTypeKwd::BIT && !forMember) out += "|VLVF_BITVAR";
|
||||
}
|
||||
if (isNet()) out += "|VLVF_NET";
|
||||
return out;
|
||||
|
||||
+13
-13
@@ -309,13 +309,13 @@ class EmitCSyms final : EmitCBaseVisitorConst {
|
||||
}
|
||||
static std::string memberVlEnumDir(const AstVar* const varp,
|
||||
const AstNodeDType* const dtypep) {
|
||||
std::string out = "((" + varp->vlEnumDir() + ") & ~(VLVF_SIGNED|VLVF_BITVAR))";
|
||||
std::string out = '(' + varp->vlEnumDir(/*forMember=*/true);
|
||||
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;
|
||||
return out + ')';
|
||||
}
|
||||
|
||||
static std::string insertVarStatement(const ScopeVarData& svd, const AstScope* const scopep,
|
||||
@@ -1408,21 +1408,21 @@ std::vector<std::string> EmitCSyms::getSymCtorStmts() {
|
||||
const std::string bounds = boundsString(dims);
|
||||
residual.emplace_back(
|
||||
insertVarStatement(svd, scopep, varp, dims.udim, dims.pdim, bounds) + ";");
|
||||
if (const AstNodeUOrStructDType* const sdtypep
|
||||
= VN_CAST(varp->dtypeSkipRefp(), NodeUOrStructDType)) {
|
||||
if (!sdtypep->packed()) {
|
||||
addUOrStructMemberVars(residual, svd, scopep, svd.m_varBasePretty,
|
||||
protect(varp->name()), sdtypep);
|
||||
}
|
||||
} else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) {
|
||||
addUnpackedArrayUOrStructMemberVars(residual, svd, scopep,
|
||||
svd.m_varBasePretty,
|
||||
protect(varp->name()), varp->dtypep());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: v3fatalSrc("Bad case");
|
||||
}
|
||||
if (kind == TableEntryKind::TABLE_ROW) continue;
|
||||
if (const AstNodeUOrStructDType* const sdtypep
|
||||
= VN_CAST(varp->dtypeSkipRefp(), NodeUOrStructDType)) {
|
||||
if (!sdtypep->packed()) {
|
||||
addUOrStructMemberVars(residual, svd, scopep, svd.m_varBasePretty,
|
||||
protect(varp->name()), sdtypep);
|
||||
}
|
||||
} else if (VN_IS(varp->dtypeSkipRefp(), UnpackArrayDType)) {
|
||||
addUnpackedArrayUOrStructMemberVars(residual, svd, scopep, svd.m_varBasePretty,
|
||||
protect(varp->name()), varp->dtypep());
|
||||
}
|
||||
}
|
||||
|
||||
if (!rows.empty()) {
|
||||
|
||||
+1
-1
@@ -911,7 +911,7 @@ class ForceDiscoveryVisitor final : public VNVisitorConst {
|
||||
"buildForceableUnpackedArray called with non-unpacked dtype");
|
||||
const AstNodeDType* const leafDtypep = dims.back()->subDTypep()->skipRefp();
|
||||
const AstBasicDType* const innerBasicp = leafDtypep->basicp();
|
||||
if (!ForceState::isBitwiseDType(innerBasicp)) {
|
||||
if (!innerBasicp || !ForceState::isBitwiseDType(innerBasicp)) {
|
||||
varp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Forcing unpacked arrays of non-bitwise inner type: "
|
||||
<< varp->name()); // (#4735)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
%Error-UNSUPPORTED: t/t_forceable_unpacked_struct_unsup.v:13:14: Unsupported: Forcing unpacked arrays of non-bitwise inner type: array_struct_top__DOT__response
|
||||
13 | response_t response[2] /*verilator forceable*/;
|
||||
| ^~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: Exiting due to
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,14 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
typedef struct {
|
||||
logic [31:0] a;
|
||||
logic [31:0] b;
|
||||
} response_t;
|
||||
|
||||
module array_struct_top;
|
||||
response_t response[2] /*verilator forceable*/;
|
||||
endmodule
|
||||
@@ -0,0 +1,123 @@
|
||||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// 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-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include "TestCheck.h"
|
||||
#include "TestSimulator.h"
|
||||
#include "TestVpi.h"
|
||||
#include "sv_vpi_user.h"
|
||||
#include "vpi_user.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace {
|
||||
|
||||
bool putValue(vpiHandle handle, PLI_INT32 val, PLI_INT32 flags = vpiNoDelay) {
|
||||
s_vpi_value value{};
|
||||
value.format = vpiIntVal;
|
||||
value.value.integer = val;
|
||||
return vpi_put_value(handle, &value, nullptr, flags);
|
||||
}
|
||||
|
||||
PLI_INT32 getValue(vpiHandle handle) {
|
||||
s_vpi_value value{};
|
||||
value.format = vpiIntVal;
|
||||
vpi_get_value(handle, &value);
|
||||
return value.value.integer;
|
||||
}
|
||||
|
||||
int errors = 0;
|
||||
|
||||
bool mon_check() {
|
||||
|
||||
TestVpiHandle forceable_response
|
||||
= vpi_handle_by_name(const_cast<PLI_BYTE8*>("t.forceable_response"), nullptr);
|
||||
TEST_CHECK_NZ(forceable_response);
|
||||
if (errors) return true;
|
||||
|
||||
std::unordered_set<std::string> discoverable_by_iterate;
|
||||
if (TestVpiHandle members = vpi_iterate(vpiMember, forceable_response)) {
|
||||
while (TestVpiHandle member = vpi_scan(members)) {
|
||||
discoverable_by_iterate.insert(vpi_get_str(vpiFullName, member));
|
||||
}
|
||||
members.freed();
|
||||
}
|
||||
const std::unordered_set<std::string> expected_members
|
||||
= {"t.forceable_response.a", "t.forceable_response.b", "t.forceable_response.nested"};
|
||||
TEST_CHECK(discoverable_by_iterate.size(), expected_members.size(),
|
||||
discoverable_by_iterate == expected_members);
|
||||
|
||||
TestVpiHandle a
|
||||
= vpi_handle_by_name(const_cast<PLI_BYTE8*>("t.forceable_response.a"), nullptr);
|
||||
TestVpiHandle b
|
||||
= vpi_handle_by_name(const_cast<PLI_BYTE8*>("t.forceable_response.b"), nullptr);
|
||||
TestVpiHandle nested
|
||||
= vpi_handle_by_name(const_cast<PLI_BYTE8*>("t.forceable_response.nested"), nullptr);
|
||||
TestVpiHandle c
|
||||
= vpi_handle_by_name(const_cast<PLI_BYTE8*>("t.forceable_response.nested.c"), nullptr);
|
||||
TEST_CHECK_NZ(a);
|
||||
TEST_CHECK_NZ(b);
|
||||
TEST_CHECK_NZ(nested);
|
||||
TEST_CHECK_NZ(c);
|
||||
if (errors) return true;
|
||||
|
||||
std::unordered_set<std::string> nested_members;
|
||||
if (TestVpiHandle members = vpi_iterate(vpiMember, nested)) {
|
||||
while (TestVpiHandle member = vpi_scan(members)) {
|
||||
nested_members.insert(vpi_get_str(vpiFullName, member));
|
||||
}
|
||||
members.freed();
|
||||
}
|
||||
const std::unordered_set<std::string> expected_nested_members
|
||||
= {"t.forceable_response.nested.c"};
|
||||
TEST_CHECK(nested_members.size(), expected_nested_members.size(),
|
||||
nested_members == expected_nested_members);
|
||||
|
||||
TEST_CHECK_EQ(vpi_get(vpiSize, a), 32);
|
||||
TEST_CHECK_EQ(vpi_get(vpiSize, b), 16);
|
||||
TEST_CHECK_EQ(vpi_get(vpiSize, c), 8);
|
||||
|
||||
putValue(a, 11);
|
||||
putValue(b, 22);
|
||||
putValue(c, 33);
|
||||
TEST_CHECK_EQ(getValue(a), 11);
|
||||
TEST_CHECK_EQ(getValue(b), 22);
|
||||
TEST_CHECK_EQ(getValue(c), 33);
|
||||
return errors;
|
||||
}
|
||||
|
||||
PLI_INT32 start_of_sim(t_cb_data* data) {
|
||||
if (mon_check()) vpi_control(vpiStop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vpi_compat_bootstrap() {
|
||||
static s_vpi_time vpi_time;
|
||||
vpi_time.high = 0;
|
||||
vpi_time.low = 0;
|
||||
vpi_time.type = vpiSimTime;
|
||||
|
||||
s_cb_data cb_data{};
|
||||
cb_data.reason = cbStartOfSimulation;
|
||||
cb_data.cb_rtn = &start_of_sim;
|
||||
cb_data.obj = NULL;
|
||||
cb_data.time = &vpi_time;
|
||||
cb_data.value = NULL;
|
||||
cb_data.index = 0;
|
||||
cb_data.user_data = NULL;
|
||||
TestVpiHandle callback_h = vpi_register_cb(&cb_data);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test module
|
||||
#
|
||||
# This file ONLY is placed under the Creative Commons Public Domain.
|
||||
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios("vlt_all", "xrun")
|
||||
|
||||
test.compile(
|
||||
make_top_shell=False,
|
||||
make_pli=True,
|
||||
verilator_flags2=["--binary", "--vpi", "--no-l2name", "--public-flat-rw", test.pli_filename])
|
||||
|
||||
test.execute(use_libvpi=True)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,22 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
typedef struct {logic [7:0] c;} nested_t;
|
||||
|
||||
typedef struct {
|
||||
logic [31:0] a;
|
||||
logic [15:0] b;
|
||||
nested_t nested;
|
||||
} response_t;
|
||||
|
||||
module t;
|
||||
response_t forceable_response /* verilator forceable */;
|
||||
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Reference in New Issue
Block a user