Add Verilation time check for forceable strings

This commit is contained in:
Christian Hecken 2025-12-16 11:50:36 +01:00
parent b0adf5f7af
commit f39e56111e
8 changed files with 42 additions and 71 deletions

View File

@ -2837,14 +2837,6 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
return;
} else if (valuep->format == vpiStringVal) {
if (varp->vltype() == VLVT_STRING) {
if (VL_UNLIKELY(varp->isForceable())) {
VL_VPI_ERROR_(
__FILE__, __LINE__,
"Attempting to retrieve value of forceable signal '%s' with data type "
"VLVT_STRING, but strings cannot be forced.",
vop->fullname());
}
if (varp->isParam()) {
valuep->value.str = reinterpret_cast<char*>(varDatap);
return;

View File

@ -464,6 +464,13 @@ class ForceConvertVisitor final : public VNVisitor {
return;
}
const AstBasicDType* const bdtypep = nodep->varp()->basicp();
const bool strtype = bdtypep && bdtypep->keyword() == VBasicDTypeKwd::STRING;
if (strtype) {
nodep->varp()->v3error(
"Forcing strings is not permitted: " << nodep->varp()->name());
}
const ForceState::ForceComponentsVarScope& fc = m_state.getForceComponents(nodep);
fc.m_enVscp->varp()->sigUserRWPublic(true);
fc.m_valVscp->varp()->sigUserRWPublic(true);

View File

@ -0,0 +1,5 @@
%Error: t/t_forceable_string_bad.v:8:10: Forcing strings is not permitted: t__DOT__str
8 | string str /*verilator forceable*/;
| ^~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 by Wilson Snyder. 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-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()

View File

@ -0,0 +1,9 @@
// ======================================================================
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty.
// SPDX-License-Identifier: CC0-1.0
// ======================================================================
module t;
string str /*verilator forceable*/;
endmodule

View File

@ -585,43 +585,13 @@ extern "C" int checkValuesReleased(void) {
}
#ifdef VERILATOR
// This function only makes sense with Verilator, because other simulators fail at elaboration time
// when trying to force a string. The error message check is specific to verilated_vpi.cpp.
extern "C" int tryCheckingForceableString(void) {
const std::string forceableStringName = std::string{scopeName} + ".str1";
TestVpiHandle const stringSignalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(forceableStringName.c_str()), nullptr);
CHECK_RESULT_NZ(stringSignalHandle); // NOLINT(concurrency-mt-unsafe)
s_vpi_value value_s{.format = vpiStringVal, .value = {}};
// Prevent program from terminating, so error message can be collected
Verilated::fatalOnVpiError(false);
vpi_get_value(stringSignalHandle, &value_s);
// Re-enable so tests that should pass properly terminate the simulation on failure
Verilated::fatalOnVpiError(true);
std::pair<const std::string, const bool> receivedError = vpiGetErrorMessage();
const bool errorOccurred = receivedError.second;
const std::string receivedErrorMessage = receivedError.first;
CHECK_RESULT_NZ(errorOccurred); // NOLINT(concurrency-mt-unsafe)
const std::string expectedErrorMessage
= "Attempting to retrieve value of forceable signal '" + forceableStringName
+ "' with data type VLVT_STRING, but strings cannot be forced.";
// NOLINTNEXTLINE(concurrency-mt-unsafe,performance-avoid-endl)
CHECK_RESULT(receivedErrorMessage, expectedErrorMessage);
return 0;
}
// This function only makes sense with Verilator, because its purpose is testing error messages
// emitted from verilated_vpi.
extern "C" int tryInvalidPutOperations() {
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
"str2", {.format = vpiStringVal, .value = {.str = const_cast<PLI_BYTE8*>("foo")}},
"str1", {.format = vpiStringVal, .value = {.str = const_cast<PLI_BYTE8*>("foo")}},
vpiForceFlag,
"vpi_put_value was used with vpiForceFlag on non-forceable signal 't.test.str2' : "
"vpi_put_value was used with vpiForceFlag on non-forceable signal 't.test.str1' : "
"'Test'"));
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
@ -670,7 +640,7 @@ extern "C" int tryInvalidPutOperations() {
// This function is just needed for hitting the test coverage target for verilated_vpi.cpp and
// ensuring that vpi_put_value to a string without vpiForceFlag still works.
extern "C" int putString() {
const std::string stringName = std::string{scopeName} + ".str2";
const std::string stringName = std::string{scopeName} + ".str1";
TestVpiHandle const stringSignalHandle //NOLINT(misc-misplaced-const)
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(stringName.c_str()), nullptr);
CHECK_RESULT_NZ(stringSignalHandle); // NOLINT(concurrency-mt-unsafe)

View File

@ -34,7 +34,6 @@ module Test (
`elsif USE_VPI_NOT_DPI
`ifdef VERILATOR
`systemc_header
extern "C" int tryCheckingForceableString();
extern "C" int putString();
extern "C" int tryInvalidPutOperations();
extern "C" int putInertialDelay();
@ -48,7 +47,6 @@ module Test (
`endif
`else
`ifdef VERILATOR
import "DPI-C" context function int tryCheckingForceableString();
import "DPI-C" context function int putString();
import "DPI-C" context function int tryInvalidPutOperations();
import "DPI-C" context function int putInertialDelay();
@ -61,11 +59,8 @@ module Test (
import "DPI-C" context function int checkValuesReleased();
`endif
// Non-forceable signal should raise error
string str1 `PUBLIC_FORCEABLE; // std::string
// Verify that vpi_put_value still works for strings
string str2 /*verilator public_flat_rw*/; // std::string
string str1 /*verilator public_flat_rw*/; // std::string
// Verify that vpi_put_value still works with vpiInertialDelay
logic [ 31:0] delayed `PUBLIC_FORCEABLE; // IData
@ -257,27 +252,6 @@ module Test (
force decStringQContinuously[31:0] = 32'd1431655765;
endtask
task automatic vpiTryCheckingForceableString();
integer vpiStatus = 1; // Default to failed status to ensure that a function *not* getting
// called also causes simulation termination
`ifdef VERILATOR
`ifdef USE_VPI_NOT_DPI
vpiStatus = $c32("tryCheckingForceableString()");
`else
vpiStatus = tryCheckingForceableString();
`endif
`else
$stop; // This task only makes sense with Verilator, since other simulators ignore the "verilator forceable" metacomment.
`endif
if (vpiStatus != 0) begin
$write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus);
$display(
"C Test failed (forcing string either succeeded even though it should have failed, or produced unexpected error message)");
$stop;
end
endtask
task automatic vpiPutString();
integer vpiStatus = 1; // Default to failed status to ensure that a function *not* getting
// called also causes simulation termination
@ -626,7 +600,6 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE));
`endif
`ifdef VERILATOR
vpiTryCheckingForceableString();
vpiPutString();
vpiTryInvalidPutOperations();
vpiPutInertialDelay();
@ -688,7 +661,6 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE));
$display("time: %0t\tclk:%b", $time, clk);
$display("str1: %s", str1);
$display("str2: %s", str2);
$display("delayed: %x", delayed);
$display("onebit: %x", onebit);

View File

@ -1,2 +1,2 @@
%Error: .../verilated_vpi.cpp:2939: vpi_put_value was used with vpiForceFlag on non-forceable signal 't.nonForceableSignal' : 't'
%Error: .../verilated_vpi.cpp:2931: vpi_put_value was used with vpiForceFlag on non-forceable signal 't.nonForceableSignal' : 't'
Aborting...