2026-01-10 09:48:46 +01:00
|
|
|
// ======================================================================
|
2026-01-27 02:24:34 +01:00
|
|
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
|
|
|
|
// SPDX-FileCopyrightText: 2026 Christian Hecken
|
2026-01-10 09:48:46 +01:00
|
|
|
// SPDX-License-Identifier: CC0-1.0
|
|
|
|
|
// ======================================================================
|
|
|
|
|
|
|
|
|
|
// DESCRIPTION: vpi force and release test
|
|
|
|
|
//
|
|
|
|
|
// This test checks that forcing a signal using vpi_put_value with vpiForceFlag
|
|
|
|
|
// sets it to the correct value, and then releasing it with vpiReleaseFlag
|
|
|
|
|
// returns it to the initial state.
|
|
|
|
|
|
|
|
|
|
#include "verilated.h" // For VL_PRINTF
|
|
|
|
|
#include "verilated_vpi.h" // For VerilatedVpi::doInertialPuts();
|
|
|
|
|
|
|
|
|
|
#include "TestSimulator.h" // For is_verilator()
|
|
|
|
|
#include "TestVpi.h" // For CHECK_RESULT_NZ
|
|
|
|
|
#include "vpi_user.h"
|
|
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <memory> // For std::unique_ptr
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
constexpr int maxAllowedErrorLevel = vpiWarning;
|
|
|
|
|
const std::string scopeName = "t.test";
|
|
|
|
|
|
|
|
|
|
using signalValueTypes = union {
|
|
|
|
|
const char* str;
|
|
|
|
|
PLI_INT32 integer;
|
|
|
|
|
double real;
|
|
|
|
|
const struct t_vpi_vecval* vector;
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
using BitRange = const struct {
|
|
|
|
|
int lo;
|
|
|
|
|
int hi;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using PartialValue = const struct {
|
|
|
|
|
signalValueTypes fullSignalForceValue; // The value the signal has after partial forcing
|
|
|
|
|
signalValueTypes fullSignalReleaseValue; // The value the signal has after partial releasing
|
|
|
|
|
signalValueTypes partialForceValue; // *Only* the value of the bits being forced
|
|
|
|
|
signalValueTypes
|
|
|
|
|
partialReleaseValue; // *Only* the value of the bits being released (after release)
|
|
|
|
|
BitRange bitRange; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using BitValue = const struct {
|
|
|
|
|
signalValueTypes fullSignalForceValue; // The value the signal has after forcing one bit
|
|
|
|
|
signalValueTypes fullSignalReleaseValue; // The value the signal has after all bits were
|
|
|
|
|
// forced and just one bit is released
|
|
|
|
|
PLI_INT32
|
|
|
|
|
valueType; // Xcelium refuses to return non-printable characters for vpiStringVal, so for
|
|
|
|
|
// strings, a different format must be used to set individual bits to 0 or 1
|
|
|
|
|
// Assumption is that fullSignalForceValue and fullSignalReleaseValue use the same type as the
|
|
|
|
|
// parent TestSignal, and bitForceValue and bitReleaseValue use the valueType specified here.
|
|
|
|
|
signalValueTypes bitForceValue; // *Only* the value of the single bit being forced
|
|
|
|
|
signalValueTypes bitReleaseValue; // *Only* the value of the single bit after being released
|
|
|
|
|
int bitIndex;
|
|
|
|
|
};
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
using TestSignal = const struct {
|
|
|
|
|
const char* signalName;
|
|
|
|
|
PLI_INT32 valueType;
|
2026-03-21 01:24:45 +01:00
|
|
|
std::vector<int> packedIndices; // Allows access into multidimensional packed vectors
|
2026-01-10 09:48:46 +01:00
|
|
|
signalValueTypes releaseValue;
|
|
|
|
|
signalValueTypes forceValue;
|
2026-03-21 01:24:45 +01:00
|
|
|
bool shouldTestPartialForce;
|
|
|
|
|
// No std::optional on C++14, so shouldTestPartialForce is used to
|
|
|
|
|
// specify if a partial force should be tested for this signal. For a
|
|
|
|
|
// partial force, the first part of the signal is left at the release
|
|
|
|
|
// value, while the second part is forced to the force value.
|
|
|
|
|
PartialValue partialValue; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
|
|
|
|
BitValue bitValue; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
using ReleaseValue = const struct {
|
|
|
|
|
signalValueTypes
|
|
|
|
|
expectedReleaseValueInit; // Something that's *not* the expected release value, to ensure
|
|
|
|
|
// the test fails if the value is not updated
|
|
|
|
|
signalValueTypes expectedReleaseValue;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum class BitIndexingMethod : uint8_t {
|
|
|
|
|
ByIndex = 0, // vpi_handle_by_index
|
|
|
|
|
ByName = 1, // vpi_handle_by_name
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
enum class DimIndexingMethod : uint8_t {
|
|
|
|
|
ByRepeatedIndex = 0, // Repeatedly use vpi_handle_by_index
|
|
|
|
|
ByMultiIndex = 1, // vpi_handle_by_multi_index
|
|
|
|
|
ByName = 2, // vpi_handle_by_name
|
2026-01-10 09:48:46 +01:00
|
|
|
};
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
enum class Direction : uint8_t {
|
|
|
|
|
Descending = 0, // [hi:lo]
|
|
|
|
|
Ascending = 1, // [lo:hi]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#ifndef IVERILOG
|
|
|
|
|
const std::array<TestSignal, 35> TestSignals = {
|
|
|
|
|
#else // Multidimensional packed arrays aren't tested in Icarus
|
|
|
|
|
const std::array<TestSignal, 16> TestSignals = {
|
|
|
|
|
#endif
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"onebit",
|
|
|
|
|
vpiIntVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.integer = 1},
|
|
|
|
|
{.integer = 0},
|
2026-03-21 01:24:45 +01:00
|
|
|
false,
|
|
|
|
|
{}, // Can't partially force just one bit
|
|
|
|
|
{}}, // Can't index into a single bit
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"intval",
|
|
|
|
|
vpiIntVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.integer = -1431655766}, // 1010...1010
|
|
|
|
|
{.integer = 0x55555555}, // 0101...0101
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.integer = -1431677611}, // 1010...010_010...0101
|
|
|
|
|
{.integer = 0x5555AAAA},
|
|
|
|
|
{.integer = 0x5555},
|
|
|
|
|
{.integer = 0xAAAA},
|
|
|
|
|
{.lo = 0, .hi = 15}},
|
|
|
|
|
{{.integer = -1431655765}, // 1010...1011
|
|
|
|
|
{.integer = 0x55555554}, // 0101...0100
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 1},
|
|
|
|
|
{.integer = 0},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
TestSignal{"vectorC",
|
|
|
|
|
vpiVectorVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b10101010, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b01010101, 0}}},
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.vector = (t_vpi_vecval[]){{0b10100101, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b01011010, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x5, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0xA, 0}}},
|
|
|
|
|
{.lo = 0, .hi = 3}},
|
|
|
|
|
{{.vector = (t_vpi_vecval[]){{0b10101011, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b01010100, 0}}},
|
|
|
|
|
vpiVectorVal,
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b1, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b0, 0}}},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
|
|
|
|
|
TestSignal{
|
|
|
|
|
"vectorQ",
|
|
|
|
|
vpiVectorVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOTE: This is a 62 bit signal, so the first two bits of the MSBs (*second* vecval,
|
|
|
|
|
// since the LSBs come first) are set to 0, hence the 0x2 and 0x1, respectively.
|
|
|
|
|
|
|
|
|
|
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0}, {0x2AAAAAAAUL, 0}}}, // (00)1010...1010
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, {0x15555555UL, 0}}}, // (00)0101...0101
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.vector
|
|
|
|
|
= (t_vpi_vecval[]){{0xD5555555UL, 0}, {0x2AAAAAAAUL, 0}}}, // 1010...010_010...0101
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x2AAAAAAAUL, 0}, {0x15555555UL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555555, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x2AAAAAAAUL, 0}}},
|
|
|
|
|
{.lo = 0, .hi = 30}},
|
|
|
|
|
{{.vector = (t_vpi_vecval[]){{0xAAAAAAABUL, 0}, {0x2AAAAAAAUL, 0}}}, // (00)1010...1011
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555554UL, 0}, {0x15555555UL, 0}}}, // (00)0101...0100
|
|
|
|
|
vpiVectorVal,
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b1, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b0, 0}}},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
|
|
|
|
|
TestSignal{"vectorW",
|
|
|
|
|
vpiVectorVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0}, // 1010...1010
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, // 0101...0101
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0}}},
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
2026-01-10 09:48:46 +01:00
|
|
|
{{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, // 1010...010_010...0101
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0}}},
|
2026-03-21 01:24:45 +01:00
|
|
|
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, {0x55555555UL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0}, {0xAAAAAAAAUL, 0}}},
|
|
|
|
|
{.lo = 0, .hi = 63}},
|
|
|
|
|
{{.vector = (t_vpi_vecval[]){{0xAAAAAAABUL, 0}, // 1010...1011
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555554UL, 0}, // 0101...0100
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0}}},
|
|
|
|
|
vpiVectorVal,
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b1, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0b0, 0}}},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
|
|
|
|
|
TestSignal{"real1",
|
|
|
|
|
vpiRealVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.real = 1.0},
|
|
|
|
|
{.real = 123456.789},
|
2026-03-21 01:24:45 +01:00
|
|
|
false,
|
|
|
|
|
{}, // reals cannot be packed and individual bits cannot be accessed,so there is no
|
|
|
|
|
// way to partially force a real signal.
|
|
|
|
|
{}},
|
2026-01-10 09:48:46 +01:00
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
TestSignal{"textHalf",
|
|
|
|
|
vpiStringVal,
|
|
|
|
|
{},
|
|
|
|
|
{.str = "Hf"},
|
|
|
|
|
{.str = "T3"},
|
|
|
|
|
true,
|
|
|
|
|
{{.str = "H3"}, {.str = "Tf"}, {.str = "3"}, {.str = "f"}, {.lo = 0, .hi = 7}},
|
|
|
|
|
{{.str = "Hg"},
|
|
|
|
|
{.str = "T2"},
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
vpiStringVal,
|
|
|
|
|
{.str = "\1"}, // "f" = 0b0110_0110, so force LSB to 1
|
|
|
|
|
{.str = ""}, // Only null terminator
|
|
|
|
|
#else
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 1},
|
|
|
|
|
{.integer = 0},
|
|
|
|
|
#endif
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"textLong",
|
|
|
|
|
vpiStringVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.str = "Long64b"},
|
|
|
|
|
{.str = "44Four44"},
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.str = "Lonur44"},
|
|
|
|
|
{.str = "44Fog64b"},
|
|
|
|
|
{.str = "ur44"},
|
|
|
|
|
{.str = "g64b"},
|
|
|
|
|
{.lo = 0, .hi = 31}},
|
|
|
|
|
{{.str = "Long64`"},
|
|
|
|
|
{.str = "44Four46"}, // "4" = 00110100, so release second-to-last bit to 1 -> "6"
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
vpiStringVal,
|
|
|
|
|
{.str = ""}, // "b" = 0b01100010, so force second-to-last bit to 0 -> "`"
|
|
|
|
|
{.str = "\1"},
|
|
|
|
|
#else
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 0},
|
|
|
|
|
{.integer = 1},
|
|
|
|
|
#endif
|
|
|
|
|
1}}, // "b" and "4" both have 0 LSB, so force bit 1 instead to see a difference
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"text",
|
|
|
|
|
vpiStringVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.str = "Verilog Test module"},
|
|
|
|
|
{.str = "lorem ipsum"},
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.str = "Verilog Torem ipsum"},
|
|
|
|
|
{.str = "lest module"},
|
|
|
|
|
{.str = "orem ipsum"},
|
|
|
|
|
{.str = "est module"},
|
|
|
|
|
{.lo = 0, .hi = 79}},
|
|
|
|
|
{{.str = "Verilog Test modulm"}, // "e" = 0b01100101, force bit 3 to 1 -> "m"
|
|
|
|
|
{.str = "lorem ipsue"}, // "m" = 0b01101101, release bit 3 to 0 -> "e"
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
vpiStringVal,
|
|
|
|
|
{.str = "\1"},
|
|
|
|
|
{.str = ""},
|
|
|
|
|
#else
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 1},
|
|
|
|
|
{.integer = 0},
|
|
|
|
|
#endif
|
|
|
|
|
3}}, // Bits 0 through 2 for "m" and "e" are identical, so force bit 3 to see a
|
|
|
|
|
// difference
|
|
|
|
|
|
|
|
|
|
TestSignal{
|
|
|
|
|
"binString",
|
|
|
|
|
vpiBinStrVal,
|
|
|
|
|
{},
|
|
|
|
|
{.str = "10101010"},
|
|
|
|
|
{.str = "01010101"},
|
|
|
|
|
true,
|
|
|
|
|
{{.str = "10100101"},
|
|
|
|
|
{.str = "01011010"},
|
|
|
|
|
{.str = "0101"},
|
|
|
|
|
{.str = "1010"},
|
|
|
|
|
{.lo = 0, .hi = 3}},
|
|
|
|
|
{{.str = "10101011"}, {.str = "01010100"}, vpiBinStrVal, {.str = "1"}, {.str = "0"}, 0}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"octString",
|
|
|
|
|
vpiOctStrVal,
|
|
|
|
|
{},
|
|
|
|
|
{.str = "25252"}, // 010101010101010
|
|
|
|
|
{.str = "52525"}, // 101010101010101
|
|
|
|
|
true,
|
|
|
|
|
{{.str = "25325"}, // 010101011010101
|
|
|
|
|
{.str = "52452"}, // 101010100101010
|
|
|
|
|
{.str = "125"},
|
|
|
|
|
{.str = "052"}, // [xx]0101010 - note: the first character is 0 even though the actual
|
|
|
|
|
// triplet at that point in the signal would be `100`, because the bit
|
|
|
|
|
// select extracts only bits 0 through 6, and the 1 would be at bit 8.
|
|
|
|
|
{.lo = 0, .hi = 6}},
|
|
|
|
|
{{.str = "25253"}, // 010101010101011
|
|
|
|
|
{.str = "52524"}, // 101010101010100
|
|
|
|
|
vpiOctStrVal,
|
|
|
|
|
{.str = "1"},
|
|
|
|
|
{.str = "0"},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"hexString",
|
|
|
|
|
vpiHexStrVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.str = "aaaaaaaaaaaaaaaa"}, // 1010...1010
|
|
|
|
|
{.str = "5555555555555555"}, // 0101...0101
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.str = "aaaaaaaa55555555"}, // 1010...010_010...0101
|
|
|
|
|
{.str = "55555555aaaaaaaa"},
|
|
|
|
|
{.str = "55555555"},
|
|
|
|
|
{.str = "aaaaaaaa"},
|
|
|
|
|
{.lo = 0, .hi = 31}},
|
|
|
|
|
{{.str = "aaaaaaaaaaaaaaab"}, // 1010...1011
|
|
|
|
|
{.str = "5555555555555554"}, // 0101...0100
|
|
|
|
|
vpiHexStrVal,
|
|
|
|
|
{.str = "1"},
|
|
|
|
|
{.str = "0"},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
TestSignal{"decStringC",
|
|
|
|
|
vpiDecStrVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.str = "170"}, // 10101010
|
|
|
|
|
{.str = "85"}, // 01010101
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.str = "165"}, // 10100101
|
|
|
|
|
{.str = "90"}, // 01011010
|
|
|
|
|
{.str = "5"}, // 0101
|
|
|
|
|
{.str = "10"}, // 1010
|
|
|
|
|
{.lo = 0, .hi = 3}},
|
|
|
|
|
{{.str = "171"}, // 10101011
|
|
|
|
|
{.str = "84"}, // 01010100
|
|
|
|
|
vpiDecStrVal,
|
|
|
|
|
{.str = "1"},
|
|
|
|
|
{.str = "0"},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"decStringS",
|
|
|
|
|
vpiDecStrVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.str = "43690"}, // 1010...1010
|
|
|
|
|
{.str = "21845"}, // 0101...0101
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.str = "43605"}, // 1010...010_010...0101
|
|
|
|
|
{.str = "21930"}, // 0101...101_101...1010
|
|
|
|
|
{.str = "85"}, // 0b01010101
|
|
|
|
|
{.str = "170"}, // 0b10101010
|
|
|
|
|
{.lo = 0, .hi = 7}},
|
|
|
|
|
{{.str = "43691"}, // 1010...1011
|
|
|
|
|
{.str = "21844"}, // 0101...0100
|
|
|
|
|
vpiDecStrVal,
|
|
|
|
|
{.str = "1"},
|
|
|
|
|
{.str = "0"},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"decStringI",
|
|
|
|
|
vpiDecStrVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.str = "2863311530"}, // 1010...1010
|
|
|
|
|
{.str = "1431655765"}, // 0101...0101
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.str = "2863289685"}, // 1010...010_010...0101
|
|
|
|
|
{.str = "1431677610"}, // 0101...101_101...1010
|
|
|
|
|
{.str = "21845"}, // 0101...0101
|
|
|
|
|
{.str = "43690"}, // 1010...1010
|
|
|
|
|
{.lo = 0, .hi = 15}},
|
|
|
|
|
{{.str = "2863311531"}, // 1010...1011
|
|
|
|
|
{.str = "1431655764"}, // 0101...0100
|
|
|
|
|
vpiDecStrVal,
|
|
|
|
|
{.str = "1"},
|
|
|
|
|
{.str = "0"},
|
|
|
|
|
0}},
|
2026-01-10 09:48:46 +01:00
|
|
|
TestSignal{"decStringQ",
|
|
|
|
|
vpiDecStrVal,
|
2026-03-21 01:24:45 +01:00
|
|
|
{},
|
2026-01-10 09:48:46 +01:00
|
|
|
{.str = "12297829382473034410"}, // 1010...1010
|
|
|
|
|
{.str = "6148914691236517205"}, // 0101...0101
|
2026-03-21 01:24:45 +01:00
|
|
|
true,
|
|
|
|
|
{{.str = "12297829381041378645"}, // 1010...010_010...0101
|
|
|
|
|
{.str = "6148914692668172970"}, // 0101...101_101...1010
|
|
|
|
|
{.str = "1431655765"}, // 0x55555555
|
|
|
|
|
{.str = "2863311530"}, // 0xAAAAAAAA
|
|
|
|
|
{.lo = 0, .hi = 31}},
|
|
|
|
|
{{.str = "12297829382473034411"}, // 1010...1010
|
|
|
|
|
{.str = "6148914691236517204"}, // 0101...0100
|
|
|
|
|
vpiDecStrVal,
|
|
|
|
|
{.str = "1"},
|
|
|
|
|
{.str = "0"},
|
|
|
|
|
0}},
|
|
|
|
|
|
|
|
|
|
#ifndef IVERILOG
|
|
|
|
|
// Force the entire 2d packed array (no partial forcing possible since part selection of
|
|
|
|
|
// individual bits is only possible in the last dimension)
|
|
|
|
|
TestSignal{"packed2dC",
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{}, // No indexing, force entire array
|
|
|
|
|
{.integer = 0xAA},
|
|
|
|
|
{.integer = 0x55},
|
|
|
|
|
false,
|
|
|
|
|
{},
|
|
|
|
|
{}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"packed2dS", vpiIntVal, {}, {.integer = 0xAAAA}, {.integer = 0x5555}, false, {}, {}},
|
|
|
|
|
TestSignal{"packed2dI",
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{},
|
|
|
|
|
{.integer = 0xAAAAAA}, // 24 bit
|
|
|
|
|
{.integer = 0x555555},
|
|
|
|
|
false,
|
|
|
|
|
{},
|
|
|
|
|
{}},
|
|
|
|
|
TestSignal{"packed2dQ",
|
|
|
|
|
vpiVectorVal,
|
|
|
|
|
{},
|
|
|
|
|
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0}, {0xAAAAAAAAUL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, {0x55555555UL, 0}}},
|
|
|
|
|
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
false,
|
|
|
|
|
{},
|
|
|
|
|
{}},
|
|
|
|
|
TestSignal{"packed2dW",
|
|
|
|
|
vpiVectorVal,
|
|
|
|
|
{},
|
|
|
|
|
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0}}},
|
|
|
|
|
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
false,
|
|
|
|
|
{},
|
|
|
|
|
{}},
|
|
|
|
|
|
|
|
|
|
// Index into the first dimension and attempt to force all elements in it.
|
|
|
|
|
// Should have the same effect as forcing the entire packed2d array.
|
|
|
|
|
TestSignal{"packed3dS",
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-2}, // Index into second element of the first dimension (defined as [-1:-2]),
|
|
|
|
|
// leaving a 2D packed array of [1:0][1:0] with 4 elements of width 2 each
|
|
|
|
|
{.integer = 0xAA},
|
|
|
|
|
{.integer = 0x55},
|
|
|
|
|
false,
|
|
|
|
|
{},
|
|
|
|
|
{}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"packed3dI", vpiIntVal, {-2}, {.integer = 0xAAAA}, {.integer = 0x5555}, false, {}, {}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"packed3dQ", vpiIntVal, {-2}, {.integer = 0xAAAAAA}, {.integer = 0x555555}, false, {}, {}},
|
|
|
|
|
TestSignal{"packed3dW",
|
|
|
|
|
vpiVectorVal,
|
|
|
|
|
{-2},
|
|
|
|
|
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0},
|
|
|
|
|
{0xAAAAAAAAUL, 0}}},
|
|
|
|
|
{.vector = (t_vpi_vecval[]){{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0},
|
|
|
|
|
{0x55555555UL, 0}}},
|
|
|
|
|
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
|
|
|
|
false,
|
|
|
|
|
{},
|
|
|
|
|
{}},
|
|
|
|
|
|
|
|
|
|
// Attempt to force only one element in the 4D vectors, and also attempt partial and bit
|
|
|
|
|
// forcing
|
|
|
|
|
TestSignal{
|
|
|
|
|
"packed4dC", // 2-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 2},
|
|
|
|
|
{.integer = 0b10},
|
|
|
|
|
{.integer = 0b01},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0b11},
|
|
|
|
|
{.integer = 0b00},
|
|
|
|
|
{.integer = 0b1},
|
|
|
|
|
{.integer = 0b0},
|
|
|
|
|
{.lo = -1, .hi = -1}},
|
|
|
|
|
{{.integer = 0b11}, {.integer = 0b00}, vpiIntVal, {.integer = 0b1}, {.integer = 0b0}, -1}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"packed4dS", // 4-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 2},
|
|
|
|
|
{.integer = 0xA},
|
|
|
|
|
{.integer = 0x5},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0b1001},
|
|
|
|
|
{.integer = 0b0110},
|
|
|
|
|
{.integer = 0b01},
|
|
|
|
|
{.integer = 0b10},
|
|
|
|
|
{.lo = -3, .hi = -2}},
|
|
|
|
|
{{.integer = 0xB}, {.integer = 0x4}, vpiIntVal, {.integer = 0b1}, {.integer = 0b0}, -3}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"packed4dI", // 8-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 2},
|
|
|
|
|
{.integer = 0xAA},
|
|
|
|
|
{.integer = 0x55},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0xA5},
|
|
|
|
|
{.integer = 0x5A},
|
|
|
|
|
{.integer = 0x5},
|
|
|
|
|
{.integer = 0xA},
|
|
|
|
|
{.lo = -7, .hi = -4}},
|
|
|
|
|
{{.integer = 0xAB}, {.integer = 0x54}, vpiIntVal, {.integer = 0b1}, {.integer = 0b0}, -7}},
|
|
|
|
|
TestSignal{"packed4dQ", // 16-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 2},
|
|
|
|
|
{.integer = 0xAAAA},
|
|
|
|
|
{.integer = 0x5555},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0xAA55},
|
|
|
|
|
{.integer = 0x55AA},
|
|
|
|
|
{.integer = 0x55},
|
|
|
|
|
{.integer = 0xAA},
|
|
|
|
|
{.lo = -15, .hi = -8}},
|
|
|
|
|
{{.integer = 0xAAAB},
|
|
|
|
|
{.integer = 0x5554},
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 0b1},
|
|
|
|
|
{.integer = 0b0},
|
|
|
|
|
-15}},
|
|
|
|
|
TestSignal{"packed4dW", // 32-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 2},
|
|
|
|
|
{.integer = static_cast<PLI_INT32>(0xAAAAAAAA)},
|
|
|
|
|
{.integer = 0x55555555},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = static_cast<PLI_INT32>(0xAAAA5555)},
|
|
|
|
|
{.integer = 0x5555AAAA},
|
|
|
|
|
{.integer = 0x5555},
|
|
|
|
|
{.integer = 0xAAAA},
|
|
|
|
|
{.lo = -31, .hi = -16}},
|
|
|
|
|
{{.integer = static_cast<PLI_INT32>(0xAAAAAAAB)},
|
|
|
|
|
{.integer = 0x55555554},
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 0b1},
|
|
|
|
|
{.integer = 0b0},
|
|
|
|
|
-31}},
|
|
|
|
|
|
|
|
|
|
// Same elements as with packed4d* assuming identical data layout, but with ascending indices
|
|
|
|
|
TestSignal{
|
|
|
|
|
"ascPacked4dC", // 2-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 5},
|
|
|
|
|
{.integer = 0b10},
|
|
|
|
|
{.integer = 0b01},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0b11},
|
|
|
|
|
{.integer = 0b00},
|
|
|
|
|
{.integer = 0b1},
|
|
|
|
|
{.integer = 0b0},
|
|
|
|
|
{.lo = 9, .hi = 9}},
|
|
|
|
|
{{.integer = 0b11}, {.integer = 0b00}, vpiIntVal, {.integer = 0b1}, {.integer = 0b0}, 9}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"ascPacked4dS", // 4-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 5},
|
|
|
|
|
{.integer = 0xA},
|
|
|
|
|
{.integer = 0x5},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0b1001},
|
|
|
|
|
{.integer = 0b0110},
|
|
|
|
|
{.integer = 0b01},
|
|
|
|
|
{.integer = 0b10},
|
|
|
|
|
{.lo = 10, .hi = 11}},
|
|
|
|
|
{{.integer = 0xB}, {.integer = 0x4}, vpiIntVal, {.integer = 0b1}, {.integer = 0b0}, 11}},
|
|
|
|
|
TestSignal{
|
|
|
|
|
"ascPacked4dI", // 8-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 5},
|
|
|
|
|
{.integer = 0xAA},
|
|
|
|
|
{.integer = 0x55},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0xA5},
|
|
|
|
|
{.integer = 0x5A},
|
|
|
|
|
{.integer = 0x5},
|
|
|
|
|
{.integer = 0xA},
|
|
|
|
|
{.lo = 12, .hi = 15}},
|
|
|
|
|
{{.integer = 0xAB}, {.integer = 0x54}, vpiIntVal, {.integer = 0b1}, {.integer = 0b0}, 15}},
|
|
|
|
|
TestSignal{"ascPacked4dQ", // 16-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 5},
|
|
|
|
|
{.integer = 0xAAAA},
|
|
|
|
|
{.integer = 0x5555},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = 0xAA55},
|
|
|
|
|
{.integer = 0x55AA},
|
|
|
|
|
{.integer = 0x55},
|
|
|
|
|
{.integer = 0xAA},
|
|
|
|
|
{.lo = 16, .hi = 23}},
|
|
|
|
|
{{.integer = 0xAAAB},
|
|
|
|
|
{.integer = 0x5554},
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 0b1},
|
|
|
|
|
{.integer = 0b0},
|
|
|
|
|
23}},
|
|
|
|
|
TestSignal{"ascPacked4dW", // 32-bit elements
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{-3, 2, -1, 5},
|
|
|
|
|
{.integer = static_cast<PLI_INT32>(0xAAAAAAAA)},
|
|
|
|
|
{.integer = 0x55555555},
|
|
|
|
|
true,
|
|
|
|
|
{{.integer = static_cast<PLI_INT32>(0xAAAA5555)},
|
|
|
|
|
{.integer = 0x5555AAAA},
|
|
|
|
|
{.integer = 0x5555},
|
|
|
|
|
{.integer = 0xAAAA},
|
|
|
|
|
{.lo = 24, .hi = 39}},
|
|
|
|
|
{{.integer = static_cast<PLI_INT32>(0xAAAAAAAB)},
|
|
|
|
|
{.integer = 0x55555554},
|
|
|
|
|
vpiIntVal,
|
|
|
|
|
{.integer = 0b1},
|
|
|
|
|
{.integer = 0b0},
|
|
|
|
|
39}},
|
|
|
|
|
#endif
|
2026-01-10 09:48:46 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool vpiCheckErrorLevel(const int maxAllowedErrorLevel) {
|
|
|
|
|
t_vpi_error_info errorInfo{};
|
|
|
|
|
const bool errorOccured = vpi_chk_error(&errorInfo);
|
|
|
|
|
if (VL_UNLIKELY(errorOccured)) {
|
|
|
|
|
VL_PRINTF("%s", errorInfo.message);
|
|
|
|
|
return errorInfo.level > maxAllowedErrorLevel;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::pair<const std::string, const bool> vpiGetErrorMessage() {
|
|
|
|
|
t_vpi_error_info errorInfo{};
|
|
|
|
|
const bool errorOccured = vpi_chk_error(&errorInfo);
|
|
|
|
|
return {errorOccured ? errorInfo.message : std::string{}, errorOccured};
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
#ifdef VERILATOR
|
2026-01-10 09:48:46 +01:00
|
|
|
int expectVpiPutError(const std::string& signalName, s_vpi_value value_s, const int flag,
|
|
|
|
|
const std::string& expectedErrorMessage) {
|
|
|
|
|
const std::string fullSignalName = std::string{scopeName} + "." + signalName;
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(fullSignalName.c_str()), nullptr);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// Prevent program from terminating, so error message can be collected
|
|
|
|
|
Verilated::fatalOnVpiError(false);
|
|
|
|
|
vpi_put_value(signalHandle, &value_s, nullptr, flag);
|
|
|
|
|
// 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)
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe,performance-avoid-endl)
|
|
|
|
|
CHECK_RESULT(receivedErrorMessage, expectedErrorMessage);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
bool vpiValuesEqual(const std::size_t bitCount, const s_vpi_value& first,
|
|
|
|
|
const s_vpi_value& second) {
|
2026-03-21 01:24:45 +01:00
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
if (first.format != second.format) return false;
|
|
|
|
|
switch (first.format) {
|
|
|
|
|
case vpiIntVal: return first.value.integer == second.value.integer; break;
|
|
|
|
|
case vpiVectorVal: {
|
|
|
|
|
const t_vpi_vecval* const firstVecval = first.value.vector;
|
|
|
|
|
const t_vpi_vecval* const secondVecval = second.value.vector;
|
|
|
|
|
const std::size_t vectorElements = (bitCount + 31) / 32; // Ceil
|
|
|
|
|
for (std::size_t i{0}; i < vectorElements; ++i) {
|
|
|
|
|
if (firstVecval[i].aval != secondVecval[i].aval) return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
case vpiRealVal:
|
|
|
|
|
return std::abs(first.value.real - second.value.real)
|
|
|
|
|
< std::numeric_limits<double>::epsilon();
|
|
|
|
|
break;
|
|
|
|
|
case vpiStringVal:
|
|
|
|
|
case vpiBinStrVal:
|
|
|
|
|
case vpiOctStrVal:
|
|
|
|
|
case vpiDecStrVal:
|
|
|
|
|
case vpiHexStrVal: {
|
|
|
|
|
// Same as CHECK_RESULT_CSTR_STRIP, but should return true when equal, false otherwise
|
|
|
|
|
const std::string firstUnpadded = first.value.str + std::strspn(first.value.str, " ");
|
|
|
|
|
return std::string{firstUnpadded} == std::string{second.value.str};
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
VL_PRINTF("Unsupported value format %i passed to vpiValuesEqual\n", first.format);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<s_vpi_value> vpiValueWithFormat(const PLI_INT32 signalFormat,
|
|
|
|
|
const signalValueTypes value) {
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp = std::make_unique<s_vpi_value>();
|
|
|
|
|
value_sp->format = signalFormat;
|
|
|
|
|
|
|
|
|
|
switch (signalFormat) {
|
|
|
|
|
case vpiIntVal: value_sp->value = {.integer = value.integer}; break;
|
|
|
|
|
case vpiVectorVal: value_sp->value = {.vector = const_cast<p_vpi_vecval>(value.vector)}; break;
|
|
|
|
|
case vpiRealVal: value_sp->value = {.real = value.real}; break;
|
|
|
|
|
case vpiStringVal:
|
|
|
|
|
case vpiBinStrVal:
|
|
|
|
|
case vpiOctStrVal:
|
|
|
|
|
case vpiDecStrVal:
|
|
|
|
|
case vpiHexStrVal: value_sp->value = {.str = const_cast<PLI_BYTE8*>(value.str)}; break;
|
|
|
|
|
default:
|
|
|
|
|
VL_PRINTF("Unsupported value format %i passed to vpiValueWithFormat\n", signalFormat);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return value_sp;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
int checkHandleValue(const vpiHandle handle, //NOLINT(misc-misplaced-const)
|
|
|
|
|
// Raw handle because this function should not
|
|
|
|
|
// release the handle when it ends
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes expectedValue) {
|
2026-01-10 09:48:46 +01:00
|
|
|
std::unique_ptr<s_vpi_value> receivedValueSp = vpiValueWithFormat(signalFormat, {});
|
|
|
|
|
CHECK_RESULT_NZ(receivedValueSp); // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
vpi_get_value(handle, receivedValueSp.get());
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
const std::unique_ptr<s_vpi_value> expectedValueSp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedValue);
|
|
|
|
|
CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, handle), *receivedValueSp, *expectedValueSp));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int checkValue(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes expectedValue) {
|
|
|
|
|
const std::string testSignalFullName
|
|
|
|
|
= std::string{scopeName} + "." + std::string{testSignalName};
|
|
|
|
|
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(checkHandleValue(signalHandle, signalFormat, expectedValue));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto getDimIndexString(const std::vector<int>& indices) {
|
|
|
|
|
std::string indexString;
|
|
|
|
|
for (const int index : indices) { indexString += "[" + std::to_string(index) + "]"; }
|
|
|
|
|
return indexString;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto getBitIndexString(const int index) { return "[" + std::to_string(index) + "]"; };
|
|
|
|
|
|
|
|
|
|
auto getPartSelectString(const int hi, // NOLINT(readability-identifier-length)
|
|
|
|
|
const int lo, // NOLINT(readability-identifier-length)
|
|
|
|
|
const Direction direction) {
|
|
|
|
|
return direction == Direction::Descending
|
|
|
|
|
? "[" + std::to_string(hi) + ":" + std::to_string(lo) + "]"
|
|
|
|
|
: "[" + std::to_string(lo) + ":" + std::to_string(hi) + "]";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
TestVpiHandle getDimIndexedSignalHandle(const std::string& scopeName,
|
|
|
|
|
const std::string& testSignalName,
|
|
|
|
|
const std::vector<int>& indices,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
const std::string testSignalFullName
|
|
|
|
|
= std::string{scopeName} + "." + std::string{testSignalName};
|
|
|
|
|
|
|
|
|
|
vpiHandle signalHandle
|
|
|
|
|
= nullptr; // Use a raw vpiHandle here so that it isn't freed when this function ends
|
|
|
|
|
switch (dimIndexingMethod) {
|
|
|
|
|
case DimIndexingMethod::ByName:
|
|
|
|
|
signalHandle = vpi_handle_by_name(
|
|
|
|
|
const_cast<PLI_BYTE8*>((testSignalFullName + getDimIndexString(indices)).c_str()),
|
|
|
|
|
nullptr);
|
|
|
|
|
break;
|
|
|
|
|
case DimIndexingMethod::ByRepeatedIndex: {
|
|
|
|
|
signalHandle
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
|
|
|
|
|
for (const int index : indices) {
|
|
|
|
|
vpiHandle nextIndexHandle
|
|
|
|
|
= vpi_handle_by_index(signalHandle, static_cast<PLI_INT32>(index));
|
|
|
|
|
vpi_release_handle(signalHandle);
|
|
|
|
|
signalHandle = nextIndexHandle;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case DimIndexingMethod::ByMultiIndex: {
|
|
|
|
|
TestVpiHandle baseSignalHandle
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
|
|
|
|
|
signalHandle
|
|
|
|
|
= vpi_handle_by_multi_index(baseSignalHandle, static_cast<PLI_INT32>(indices.size()),
|
|
|
|
|
const_cast<PLI_INT32*>(indices.data()));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
VL_PRINTF("Unsupported DimIndexingMethod %i passed to getDimIndexedSignalHandle\n",
|
|
|
|
|
static_cast<int>(dimIndexingMethod));
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return signalHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestVpiHandle getBitIndexedSignalHandle(const std::string& scopeName,
|
|
|
|
|
const std::string& testSignalName, const int index,
|
|
|
|
|
const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
const std::string testSignalFullName
|
|
|
|
|
= std::string{scopeName} + "." + std::string{testSignalName};
|
|
|
|
|
|
|
|
|
|
vpiHandle signalHandle
|
|
|
|
|
= nullptr; // Use a raw vpiHandle here so that it isn't freed when this function ends
|
|
|
|
|
switch (bitIndexingMethod) {
|
|
|
|
|
case BitIndexingMethod::ByName:
|
|
|
|
|
signalHandle = vpi_handle_by_name(
|
|
|
|
|
const_cast<PLI_BYTE8*>((testSignalFullName + getBitIndexString(index)).c_str()),
|
|
|
|
|
nullptr);
|
|
|
|
|
break;
|
|
|
|
|
case BitIndexingMethod::ByIndex: {
|
|
|
|
|
TestVpiHandle baseSignalHandle
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
|
|
|
|
|
signalHandle = vpi_handle_by_index(baseSignalHandle, static_cast<PLI_INT32>(index));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
VL_PRINTF("Unsupported BitIndexingMethod %i passed to getBitIndexedSignalHandle\n",
|
|
|
|
|
static_cast<int>(bitIndexingMethod));
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return signalHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TestVpiHandle getDimAndBitIndexedSignalHandle(const std::string& scopeName,
|
|
|
|
|
const std::string& testSignalName,
|
|
|
|
|
const std::vector<int>& dimIndices,
|
|
|
|
|
const int bitIndex,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod,
|
|
|
|
|
const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
TestVpiHandle signalHandle
|
|
|
|
|
= getDimIndexedSignalHandle(scopeName, testSignalName, dimIndices, dimIndexingMethod);
|
|
|
|
|
if (!signalHandle) return nullptr;
|
|
|
|
|
|
|
|
|
|
vpiHandle bitHandle
|
|
|
|
|
= nullptr; // Use a raw vpiHandle here so that it isn't freed when this function ends
|
|
|
|
|
switch (bitIndexingMethod) {
|
|
|
|
|
case BitIndexingMethod::ByName:
|
|
|
|
|
bitHandle = vpi_handle_by_name(
|
|
|
|
|
const_cast<PLI_BYTE8*>((std::string{scopeName} + "." + std::string{testSignalName}
|
|
|
|
|
+ getDimIndexString(dimIndices) + getBitIndexString(bitIndex))
|
|
|
|
|
.c_str()),
|
|
|
|
|
nullptr);
|
|
|
|
|
break;
|
|
|
|
|
case BitIndexingMethod::ByIndex:
|
|
|
|
|
bitHandle = vpi_handle_by_index(signalHandle, static_cast<PLI_INT32>(bitIndex));
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
VL_PRINTF("Unsupported BitIndexingMethod %i passed to getDimAndBitIndexedSignalHandle\n",
|
|
|
|
|
static_cast<int>(bitIndexingMethod));
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bitHandle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int checkSingleBit(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes expectedValue,
|
|
|
|
|
const int index, const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
TestVpiHandle const signalHandle
|
|
|
|
|
= getBitIndexedSignalHandle(scopeName, testSignalName, index, bitIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(checkHandleValue(signalHandle, signalFormat, expectedValue));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int checkSingleDimIndexedBit(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes expectedValue,
|
|
|
|
|
const std::vector<int>& indices,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod, const int index,
|
|
|
|
|
const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
// Check the specific bit - can't just call checkSingleBit since that one takes a name rather
|
|
|
|
|
// than a handle and would redo the dimension indexing
|
|
|
|
|
TestVpiHandle const signalHandle = getDimAndBitIndexedSignalHandle(
|
|
|
|
|
scopeName, testSignalName, indices, index, dimIndexingMethod, bitIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(checkHandleValue(signalHandle, signalFormat, expectedValue));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int checkDimIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes expectedValue,
|
|
|
|
|
const std::vector<int>& indices,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
TestVpiHandle signalHandle
|
|
|
|
|
= getDimIndexedSignalHandle(scopeName, testSignalName, indices, dimIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(checkHandleValue(signalHandle, signalFormat, expectedValue));
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int forceSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes forceValue) {
|
|
|
|
|
const std::string testSignalFullName
|
|
|
|
|
= std::string{scopeName} + "." + std::string{testSignalName};
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
|
2026-03-21 01:24:45 +01:00
|
|
|
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp = vpiValueWithFormat(signalFormat, forceValue);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiForceFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int forceBitIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes forceValue,
|
|
|
|
|
const int index, const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
TestVpiHandle const signalHandle
|
|
|
|
|
= getBitIndexedSignalHandle(scopeName, testSignalName, index, bitIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp = vpiValueWithFormat(signalFormat, forceValue);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiForceFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int forceDimIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes forceValue,
|
|
|
|
|
const std::vector<int>& indices,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
TestVpiHandle signalHandle
|
|
|
|
|
= getDimIndexedSignalHandle(scopeName, testSignalName, indices, dimIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp = vpiValueWithFormat(signalFormat, forceValue);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiForceFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int forceDimAndBitIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes forceValue,
|
|
|
|
|
const std::vector<int>& dimIndices,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod, const int bitIndex,
|
|
|
|
|
const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
TestVpiHandle const signalHandle = getDimAndBitIndexedSignalHandle(
|
|
|
|
|
scopeName, testSignalName, dimIndices, bitIndex, dimIndexingMethod, bitIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp = vpiValueWithFormat(signalFormat, forceValue);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiForceFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int partiallyForceSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes forceValue,
|
|
|
|
|
const int hi, // NOLINT(readability-identifier-length)
|
|
|
|
|
const int lo, // NOLINT(readability-identifier-length)
|
|
|
|
|
const Direction direction) {
|
|
|
|
|
return forceSignal(scopeName,
|
|
|
|
|
std::string{testSignalName} + getPartSelectString(hi, lo, direction),
|
|
|
|
|
signalFormat, forceValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int partiallyForceDimIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const signalValueTypes forceValue,
|
|
|
|
|
const int hi, // NOLINT(readability-identifier-length)
|
|
|
|
|
const int lo, // NOLINT(readability-identifier-length)
|
|
|
|
|
const Direction direction, const std::vector<int>& indices) {
|
|
|
|
|
// Can't just wrap forceDimIndexedSignal by appending the part selection to the signal name,
|
|
|
|
|
// because dimension indexing has to be applied before part selection
|
|
|
|
|
// NOTE: Can't use DimIndexingMethod, because the part select can only be applied through
|
|
|
|
|
// vpi_handle_by_name
|
|
|
|
|
|
|
|
|
|
const std::string partSelectedSignalName
|
|
|
|
|
= std::string{scopeName} + "." + std::string{testSignalName} + getDimIndexString(indices)
|
|
|
|
|
+ getPartSelectString(hi, lo, direction);
|
|
|
|
|
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(partSelectedSignalName.c_str()), nullptr);
|
2026-01-10 09:48:46 +01:00
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp = vpiValueWithFormat(signalFormat, forceValue);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiForceFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int releaseSignal(const std::string& scopeName, const std::string& testSignalName,
|
2026-03-21 01:24:45 +01:00
|
|
|
const PLI_INT32 signalFormat, const ReleaseValue releaseValue) {
|
|
|
|
|
#ifdef XRUN
|
|
|
|
|
// For the packed*Continuously signals, Xrun returns the *force* value instead of the
|
|
|
|
|
// expected release value when vpi_put_value is called with vpiReleaseFlag. The signal is still
|
|
|
|
|
// released properly, as proven by value checks in the SystemVerilog testbench.
|
|
|
|
|
signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
if (testSignalName.find("Continuously") != std::string::npos
|
|
|
|
|
&& testSignalName.find("acked") != std::string::npos) { // Match packed/Packed in name
|
|
|
|
|
VL_PRINTF("Note: Running on Xrun, so expecting force value instead of release value for "
|
|
|
|
|
"signal %s\n",
|
|
|
|
|
testSignalName.c_str());
|
|
|
|
|
expectedReleaseValueInit = releaseValue.expectedReleaseValue;
|
|
|
|
|
expectedReleaseValue = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
const signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
const signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
const std::string testSignalFullName
|
|
|
|
|
= std::string{scopeName} + "." + std::string{testSignalName};
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(testSignalFullName.c_str()), nullptr);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// initialize value_sp to the value that is *not* expected (i.e. forceValue for continuously
|
|
|
|
|
// assigned signals, and releaseValue for clocked signals) to ensure the test fails if value_sp
|
|
|
|
|
// is not updated
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValueInit);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); //NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiReleaseFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
#ifdef XRUN
|
|
|
|
|
// Special case: real1Continuously is continuously assigned, but in xrun the value for the
|
|
|
|
|
// s_vpi_value output parameter upon releasing using vpi_put_value is *not* the releaseValue as
|
|
|
|
|
// expected, but the forceValue. This ifdef ensures the test still passes on xrun.
|
|
|
|
|
const std::unique_ptr<s_vpi_value> expectedValueSp = vpiValueWithFormat(
|
|
|
|
|
signalFormat, testSignalName == "real1Continuously" ? signalValueTypes{.real = 123456.789}
|
|
|
|
|
: expectedReleaseValue);
|
|
|
|
|
#else
|
|
|
|
|
const std::unique_ptr<s_vpi_value> expectedValueSp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValue);
|
|
|
|
|
#endif
|
|
|
|
|
CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), *value_sp, *expectedValueSp));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
int releaseBitIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const ReleaseValue releaseValue,
|
|
|
|
|
const int index, const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
const signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
const signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
TestVpiHandle const signalHandle
|
|
|
|
|
= getBitIndexedSignalHandle(scopeName, testSignalName, index, bitIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// initialize value_sp to the value that is *not* expected (i.e. forceValue for continuously
|
|
|
|
|
// assigned signals, and releaseValue for clocked signals) to ensure the test fails if value_sp
|
|
|
|
|
// is not updated
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValueInit);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); //NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiReleaseFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
const std::unique_ptr<s_vpi_value> expectedValueSp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValue);
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), *value_sp, *expectedValueSp));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int releaseDimIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const ReleaseValue releaseValue,
|
|
|
|
|
const std::vector<int>& indices,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
#ifdef XRUN
|
|
|
|
|
signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
if (testSignalName.find("Continuously") != std::string::npos
|
|
|
|
|
&& testSignalName.find("acked") != std::string::npos) {
|
|
|
|
|
VL_PRINTF("Note: Running on Xrun, so expecting force value instead of release value for "
|
|
|
|
|
"signal %s\n",
|
|
|
|
|
testSignalName.c_str());
|
|
|
|
|
expectedReleaseValueInit = releaseValue.expectedReleaseValue;
|
|
|
|
|
expectedReleaseValue = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
const signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
const signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
#endif
|
|
|
|
|
TestVpiHandle signalHandle
|
|
|
|
|
= getDimIndexedSignalHandle(scopeName, testSignalName, indices, dimIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// initialize value_sp to the value that is *not* expected (i.e. forceValue for continuously
|
|
|
|
|
// assigned signals, and releaseValue for clocked signals) to ensure the test fails if value_sp
|
|
|
|
|
// is not updated
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValueInit);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); //NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiReleaseFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
const std::unique_ptr<s_vpi_value> expectedValueSp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValue);
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), *value_sp, *expectedValueSp));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int releaseDimAndBitIndexedSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const ReleaseValue releaseValue,
|
|
|
|
|
const std::vector<int>& dimIndices,
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod, const int bitIndex,
|
|
|
|
|
const BitIndexingMethod bitIndexingMethod) {
|
|
|
|
|
#ifdef XRUN
|
|
|
|
|
signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
if (testSignalName.find("Continuously") != std::string::npos
|
|
|
|
|
&& testSignalName.find("acked") != std::string::npos) {
|
|
|
|
|
VL_PRINTF("Note: Running on Xrun, so expecting force value instead of release value for "
|
|
|
|
|
"signal %s\n",
|
|
|
|
|
testSignalName.c_str());
|
|
|
|
|
expectedReleaseValueInit = releaseValue.expectedReleaseValue;
|
|
|
|
|
expectedReleaseValue = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
const signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
const signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
#endif
|
|
|
|
|
TestVpiHandle const signalHandle = getDimAndBitIndexedSignalHandle(
|
|
|
|
|
scopeName, testSignalName, dimIndices, bitIndex, dimIndexingMethod, bitIndexingMethod);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// initialize value_sp to the value that is *not* expected (i.e. forceValue for continuously
|
|
|
|
|
// assigned signals, and releaseValue for clocked signals) to ensure the test fails if value_sp
|
|
|
|
|
// is not updated
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValueInit);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); //NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiReleaseFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
const std::unique_ptr<s_vpi_value> expectedValueSp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValue);
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), *value_sp, *expectedValueSp));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int partiallyReleaseSignal(const std::string& scopeName, const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const ReleaseValue releaseValue,
|
|
|
|
|
const int hi, // NOLINT(readability-identifier-length)
|
|
|
|
|
const int lo, // NOLINT(readability-identifier-length)
|
|
|
|
|
const Direction direction) {
|
|
|
|
|
return releaseSignal(scopeName,
|
|
|
|
|
std::string{testSignalName} + getPartSelectString(hi, lo, direction),
|
|
|
|
|
signalFormat, releaseValue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int partiallyReleaseDimIndexedSignal(const std::string& scopeName,
|
|
|
|
|
const std::string& testSignalName,
|
|
|
|
|
const PLI_INT32 signalFormat, const ReleaseValue releaseValue,
|
|
|
|
|
int hi, // NOLINT(readability-identifier-length)
|
|
|
|
|
const int lo, // NOLINT(readability-identifier-length)
|
|
|
|
|
const Direction direction, const std::vector<int>& indices) {
|
|
|
|
|
// Can't just wrap releaseDimIndexedSignal by appending the part selection to the signal name,
|
|
|
|
|
// because dimension indexing has to be applied before part selection
|
|
|
|
|
const signalValueTypes expectedReleaseValueInit = releaseValue.expectedReleaseValueInit;
|
|
|
|
|
const signalValueTypes expectedReleaseValue = releaseValue.expectedReleaseValue;
|
|
|
|
|
|
|
|
|
|
const std::string partSelectedSignalName
|
|
|
|
|
= std::string{scopeName} + "." + std::string{testSignalName} + getDimIndexString(indices)
|
|
|
|
|
+ getPartSelectString(hi, lo, direction); // Apply indexing before bit-select
|
|
|
|
|
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(partSelectedSignalName.c_str()), nullptr);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
// initialize value_sp to the value that is *not* expected (i.e. forceValue for continuously
|
|
|
|
|
// assigned signals, and releaseValue for clocked signals) to ensure the test fails if value_sp
|
|
|
|
|
// is not updated
|
|
|
|
|
std::unique_ptr<s_vpi_value> value_sp
|
|
|
|
|
= vpiValueWithFormat(signalFormat, expectedReleaseValueInit);
|
|
|
|
|
CHECK_RESULT_NZ(value_sp); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
vpi_put_value(signalHandle, value_sp.get(), nullptr, vpiReleaseFlag);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), *value_sp,
|
|
|
|
|
*vpiValueWithFormat(signalFormat, expectedReleaseValue)));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
extern "C" int checkNonContinuousValuesForced() {
|
|
|
|
|
// Non-continuously assigned (e.g. clocked) signals retain the forced value after releasing
|
|
|
|
|
// until the they are updated again, so check that they are still at the forced value
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, signal.signalName, signal.valueType, signal.forceValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.forceValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.forceValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.forceValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByName));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int checkValuesForced() {
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z(checkNonContinuousValuesForced());
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.forceValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.forceValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.forceValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.forceValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByName));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int checkContinuousValuesReleased() {
|
|
|
|
|
// Continuously assigned signals return to their original value immediately after releasing
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.releaseValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.releaseValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.releaseValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.releaseValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByName));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int
|
|
|
|
|
checkContinuousValuesPartiallyReleased( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
void) {
|
|
|
|
|
// The released bits should return to the release value immediately, while the still forced
|
|
|
|
|
// bits should still be at the force value
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
#ifdef XRUN
|
|
|
|
|
if (!signal.packedIndices.empty()) return 0;
|
|
|
|
|
#endif
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (!TestSimulator::is_icarus()) { // Icarus does not support bit selects in
|
|
|
|
|
// vpi_handle_by_name
|
|
|
|
|
const std::string dimIndex = signal.packedIndices.empty()
|
|
|
|
|
? ""
|
|
|
|
|
: getDimIndexString(signal.packedIndices);
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously" + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Descending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialReleaseValue));
|
|
|
|
|
#ifndef XRUN // Xcelium does not support ascending part selects in vpi_handle_by_name
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously" + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Ascending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialReleaseValue));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalReleaseValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int
|
|
|
|
|
checkValuesPartiallyReleased(void) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
// Check both the partial ranges and the full signal values
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
#ifdef XRUN
|
|
|
|
|
if (!signal.packedIndices.empty()) return 0;
|
|
|
|
|
#endif
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (!TestSimulator::is_icarus()) { // Icarus does not support bit selects in
|
|
|
|
|
// vpi_handle_by_name
|
|
|
|
|
const std::string dimIndex = signal.packedIndices.empty()
|
|
|
|
|
? ""
|
|
|
|
|
: getDimIndexString(signal.packedIndices);
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Descending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialReleaseValue));
|
|
|
|
|
#ifndef XRUN // Xcelium does not support ascending part selects in vpi_handle_by_name
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Ascending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialReleaseValue));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalReleaseValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
2026-03-21 01:24:45 +01:00
|
|
|
checkContinuousValuesPartiallyReleased();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int
|
|
|
|
|
checkNonContinuousValuesPartiallyForced( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
void) {
|
|
|
|
|
// Non-continuously assigned (e.g. clocked) signals retain the partially forced value after
|
|
|
|
|
// releasing until the they are updated again, so check that they are still at the partially
|
|
|
|
|
// forced value
|
2026-01-10 09:48:46 +01:00
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
#ifdef XRUN
|
|
|
|
|
if (!signal.packedIndices.empty()) return 0;
|
|
|
|
|
#endif
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (!TestSimulator::is_icarus()) { // Icarus does not support bit selects in
|
|
|
|
|
// vpi_handle_by_name
|
|
|
|
|
const std::string dimIndex = signal.packedIndices.empty()
|
|
|
|
|
? ""
|
|
|
|
|
: getDimIndexString(signal.packedIndices);
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Descending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialForceValue));
|
|
|
|
|
#ifndef XRUN // Xcelium does not support ascending part selects in vpi_handle_by_name
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Ascending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialForceValue));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalForceValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int
|
|
|
|
|
checkValuesPartiallyForced(void) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
// Check both the partial ranges and the full signal values
|
|
|
|
|
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z(checkNonContinuousValuesPartiallyForced());
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
2026-03-12 03:57:13 +01:00
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
#ifdef XRUN // Xcelium does not support a syntax like "signal[1][0][1:0]" in vpi_handle_by_name,
|
|
|
|
|
// so since it cannot partially force multi-dimensional signals through VPI, checking
|
|
|
|
|
// is skipped entirely, even if the signal was forced through SystemVerilog
|
|
|
|
|
if (!signal.packedIndices.empty()) return 0;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (!TestSimulator::is_icarus()) { // Icarus does not support bit selects in
|
|
|
|
|
// vpi_handle_by_name
|
|
|
|
|
const std::string dimIndex = signal.packedIndices.empty()
|
|
|
|
|
? ""
|
|
|
|
|
: getDimIndexString(signal.packedIndices);
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously" + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Descending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialForceValue));
|
|
|
|
|
#ifndef XRUN // Xcelium does not support ascending part selects in vpi_handle_by_name
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously" + dimIndex
|
|
|
|
|
+ getPartSelectString(signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo,
|
|
|
|
|
Direction::Ascending),
|
|
|
|
|
signal.valueType, signal.partialValue.partialForceValue));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
signal.partialValue.fullSignalForceValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices, DimIndexingMethod::ByName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-03-12 03:57:13 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int
|
|
|
|
|
checkNonContinuousSingleBitForced(void) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
// Non-continuously assigned (e.g. clocked) signals retain the forced value where one bit
|
|
|
|
|
// was forced after releasing until the they are updated again, so check that the bit is
|
|
|
|
|
// still at the forced value.
|
2026-03-12 03:57:13 +01:00
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.bitValue.bitForceValue, signal.bitValue.bitIndex,
|
|
|
|
|
BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.bitValue.bitForceValue, signal.bitValue.bitIndex,
|
|
|
|
|
BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.bitValue.fullSignalForceValue));
|
|
|
|
|
} else {
|
|
|
|
|
for (DimIndexingMethod dimIndexingMethod :
|
|
|
|
|
{DimIndexingMethod::ByRepeatedIndex, DimIndexingMethod::ByMultiIndex,
|
|
|
|
|
DimIndexingMethod::ByName}) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.bitValue.bitForceValue, signal.packedIndices,
|
|
|
|
|
dimIndexingMethod, signal.bitValue.bitIndex,
|
|
|
|
|
BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.bitValue.bitForceValue, signal.packedIndices,
|
|
|
|
|
dimIndexingMethod, signal.bitValue.bitIndex,
|
|
|
|
|
BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName,
|
|
|
|
|
signal.valueType,
|
|
|
|
|
signal.bitValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-03-12 03:57:13 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int checkSingleBitForced(void) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
// Check both single bit values and full signal values
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
// Clocked signals
|
2026-03-21 01:24:45 +01:00
|
|
|
CHECK_RESULT_Z(checkNonContinuousSingleBitForced());
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitForceValue,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitForceValue,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.fullSignalForceValue));
|
|
|
|
|
} else {
|
|
|
|
|
for (DimIndexingMethod dimIndexingMethod :
|
|
|
|
|
{DimIndexingMethod::ByRepeatedIndex, DimIndexingMethod::ByMultiIndex,
|
|
|
|
|
DimIndexingMethod::ByName}) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitForceValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitForceValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.fullSignalForceValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int
|
|
|
|
|
checkContinuousValuesSingleBitReleased( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
void) {
|
|
|
|
|
// For continuously assigned signals, the released bit should return to the release value
|
|
|
|
|
// immediately, while the still forced bits should still be at the force value
|
2026-03-12 03:57:13 +01:00
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitReleaseValue,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitReleaseValue,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.fullSignalReleaseValue));
|
|
|
|
|
} else {
|
|
|
|
|
for (DimIndexingMethod dimIndexingMethod :
|
|
|
|
|
{DimIndexingMethod::ByRepeatedIndex, DimIndexingMethod::ByMultiIndex,
|
|
|
|
|
DimIndexingMethod::ByName}) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitReleaseValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitReleaseValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-03-12 03:57:13 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int checkSingleBitReleased(void) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
// The released bit should return to the release value, while the other bits are still
|
|
|
|
|
// forced
|
|
|
|
|
|
|
|
|
|
// Check both single bit values and full signal values
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[](const TestSignal& signal) { // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName, std::string{signal.signalName},
|
|
|
|
|
signal.valueType, signal.bitValue.bitReleaseValue,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleBit(scopeName, std::string{signal.signalName},
|
|
|
|
|
signal.valueType, signal.bitValue.bitReleaseValue,
|
|
|
|
|
signal.bitValue.bitIndex, BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, std::string{signal.signalName}, signal.valueType,
|
|
|
|
|
signal.bitValue.fullSignalReleaseValue));
|
|
|
|
|
} else {
|
|
|
|
|
for (DimIndexingMethod dimIndexingMethod :
|
|
|
|
|
{DimIndexingMethod::ByRepeatedIndex, DimIndexingMethod::ByMultiIndex,
|
|
|
|
|
DimIndexingMethod::ByName}) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, std::string{signal.signalName}, signal.valueType,
|
|
|
|
|
signal.bitValue.bitReleaseValue, signal.packedIndices,
|
|
|
|
|
dimIndexingMethod, signal.bitValue.bitIndex,
|
|
|
|
|
BitIndexingMethod::ByName));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkSingleDimIndexedBit(
|
|
|
|
|
scopeName, std::string{signal.signalName}, signal.valueType,
|
|
|
|
|
signal.bitValue.bitReleaseValue, signal.packedIndices,
|
|
|
|
|
dimIndexingMethod, signal.bitValue.bitIndex,
|
|
|
|
|
BitIndexingMethod::ByIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, std::string{signal.signalName},
|
|
|
|
|
signal.valueType,
|
|
|
|
|
signal.bitValue.fullSignalReleaseValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
2026-03-21 01:24:45 +01:00
|
|
|
CHECK_RESULT_Z(checkContinuousValuesSingleBitReleased());
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int checkValuesReleased() {
|
|
|
|
|
// Clocked signals
|
2026-01-10 09:48:46 +01:00
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) {
|
2026-03-21 01:24:45 +01:00
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkValue(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.releaseValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.releaseValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByRepeatedIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.releaseValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByMultiIndex));
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
checkDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.releaseValue, signal.packedIndices,
|
|
|
|
|
DimIndexingMethod::ByName));
|
|
|
|
|
}
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-03-21 01:24:45 +01:00
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z(checkContinuousValuesReleased());
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef VERILATOR
|
|
|
|
|
// 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)
|
|
|
|
|
"str1", {.format = vpiStringVal, .value = {.str = const_cast<PLI_BYTE8*>("foo")}},
|
|
|
|
|
vpiForceFlag,
|
|
|
|
|
"vpi_put_value used with vpiForceFlag on non-forceable signal 't.test.str1'"));
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
"octString", {.format = vpiOctStrVal, .value = {.str = const_cast<PLI_BYTE8*>("123A")}},
|
|
|
|
|
vpiForceFlag,
|
|
|
|
|
"vpi_put_value: Non octal character 'A' in '123A' as value "
|
|
|
|
|
"vpiOctStrVal for t.test.octString__VforceVal"));
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
"decStringC", {.format = vpiDecStrVal, .value = {.str = const_cast<PLI_BYTE8*>("A123")}},
|
|
|
|
|
vpiForceFlag,
|
|
|
|
|
"vpi_put_value: Parsing failed for 'A123' as value vpiDecStrVal for "
|
|
|
|
|
"t.test.decStringC__VforceVal"));
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
"decStringC", {.format = vpiDecStrVal, .value = {.str = const_cast<PLI_BYTE8*>("123A")}},
|
|
|
|
|
vpiForceFlag,
|
|
|
|
|
"vpi_put_value: Trailing garbage 'A' in '123A' as value vpiDecStrVal for "
|
|
|
|
|
"t.test.decStringC__VforceVal"));
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
"hexString", {.format = vpiHexStrVal, .value = {.str = const_cast<PLI_BYTE8*>("12AG")}},
|
|
|
|
|
vpiForceFlag,
|
|
|
|
|
"vpi_put_value: Non hex character 'G' in '12AG' as value vpiHexStrVal for "
|
|
|
|
|
"t.test.hexString__VforceVal"));
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
// vop was replaced with baseSignalVop in vpi_put_value, so these tests are required to hit
|
|
|
|
|
// the test coverage target and ensure the error messages still work.
|
2026-01-10 09:48:46 +01:00
|
|
|
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
"onebit", {.format = vpiRawFourStateVal, .value = {}}, vpiForceFlag,
|
|
|
|
|
"vl_check_format: Unsupported format (vpiRawFourStateVal) for t.test.onebit"));
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
"onebit", {.format = vpiSuppressVal, .value = {}}, vpiForceFlag,
|
|
|
|
|
"vpi_put_value: Unsupported format (vpiSuppressVal) as "
|
|
|
|
|
"requested for t.test.onebit__VforceVal"));
|
|
|
|
|
|
|
|
|
|
CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
"onebit", {.format = vpiStringVal, .value = {}}, vpiInertialDelay,
|
|
|
|
|
"vpi_put_value: Unsupported p_vpi_value as requested for 't.test.onebit' with "
|
|
|
|
|
"vpiInertialDelay"));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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} + ".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)
|
|
|
|
|
|
|
|
|
|
s_vpi_value value_s{.format = vpiStringVal, .value = {.str = const_cast<PLI_BYTE8*>("foo")}};
|
|
|
|
|
|
|
|
|
|
vpi_put_value(stringSignalHandle, &value_s, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
value_s.value.str
|
|
|
|
|
= const_cast<PLI_BYTE8*>("bar"); // Ensure that test fails if value_s is not updated
|
|
|
|
|
|
|
|
|
|
vpi_get_value(stringSignalHandle, &value_s);
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
s_vpi_value expectedValueS{.format = vpiStringVal,
|
|
|
|
|
.value = {.str = const_cast<PLI_BYTE8*>("foo")}};
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, stringSignalHandle), value_s, expectedValueS));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
// This function is needed to ensure that vpiInertialDelay also works with forceable signals.
|
2026-01-10 09:48:46 +01:00
|
|
|
extern "C" int putInertialDelay() {
|
|
|
|
|
const std::string fullSignalName = std::string{scopeName} + ".delayed";
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(fullSignalName.c_str()), nullptr);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
constexpr int delayedValue = 123;
|
|
|
|
|
s_vpi_value value_s{.format = vpiIntVal, .value = {.integer = delayedValue}};
|
|
|
|
|
s_vpi_time time{.type = vpiSimTime, .high = 0, .low = 0, .real = {}};
|
|
|
|
|
vpi_put_value(signalHandle, &value_s, &time, vpiInertialDelay);
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
// Check that the put had no immediate effect
|
|
|
|
|
|
|
|
|
|
vpi_get_value(signalHandle, &value_s);
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
|
|
|
|
s_vpi_value expectedValueS{.format = vpiIntVal, .value = {.integer = 0}};
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), value_s, expectedValueS));
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
// NOTE: Because __VforceRd will only be updated in `eval_act`, `doInertialPuts` does not
|
2026-03-21 01:24:45 +01:00
|
|
|
// update the value read by `vpi_get_value` - that is only visible after another `eval`.
|
|
|
|
|
// Hence, `checkInertialDelay` must be called later to check success.
|
2026-01-10 09:48:46 +01:00
|
|
|
VerilatedVpi::doInertialPuts();
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int checkInertialDelay() {
|
|
|
|
|
const std::string fullSignalName = std::string{scopeName} + ".delayed";
|
|
|
|
|
TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const)
|
|
|
|
|
= vpi_handle_by_name(const_cast<PLI_BYTE8*>(fullSignalName.c_str()), nullptr);
|
|
|
|
|
CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
|
|
|
|
|
s_vpi_value value_s{.format = vpiIntVal,
|
|
|
|
|
.value
|
|
|
|
|
= {.integer = 0}}; // Ensure that test fails if value_s is not updated
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
vpi_get_value(signalHandle, &value_s);
|
|
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel))
|
|
|
|
|
|
2026-03-08 20:53:32 +01:00
|
|
|
constexpr int delayedValue = 123;
|
|
|
|
|
s_vpi_value expectedValueS{.format = vpiIntVal, .value = {.integer = delayedValue}};
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
|
|
|
|
CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), value_s, expectedValueS));
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int forceValues(const DimIndexingMethod dimIndexingMethod) {
|
2026-01-10 09:48:46 +01:00
|
|
|
if (!TestSimulator::is_verilator()) {
|
|
|
|
|
#ifdef VERILATOR
|
|
|
|
|
printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n");
|
|
|
|
|
return 1;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.forceValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceDimIndexedSignal(scopeName, signal.signalName,
|
|
|
|
|
signal.valueType, signal.forceValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(), [dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceSignal(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.forceValue));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceDimIndexedSignal(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.forceValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int partiallyForceValues( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
const Direction direction) {
|
2026-01-10 09:48:46 +01:00
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [direction](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyForceSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.partialForceValue,
|
|
|
|
|
signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo, direction));
|
|
|
|
|
}
|
|
|
|
|
#ifndef XRUN // Xcelium does not support a syntax like "signal[1][0][1:0]" in vpi_handle_by_name
|
|
|
|
|
else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyForceDimIndexedSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.partialValue.partialForceValue, signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo, direction, signal.packedIndices));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [direction](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyForceSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.partialForceValue,
|
|
|
|
|
signal.partialValue.bitRange.hi, signal.partialValue.bitRange.lo,
|
|
|
|
|
direction));
|
|
|
|
|
}
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyForceDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.partialValue.partialForceValue,
|
|
|
|
|
signal.partialValue.bitRange.hi, signal.partialValue.bitRange.lo,
|
|
|
|
|
direction, signal.packedIndices));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int forceSingleBit( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
const BitIndexingMethod bitIndexingMethod, const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[bitIndexingMethod, dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) { // Also applies to single bit forcing
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceBitIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.bitValue.bitForceValue,
|
|
|
|
|
signal.bitValue.bitIndex, bitIndexingMethod));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceDimAndBitIndexedSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
signal.bitValue.bitForceValue, signal.packedIndices,
|
|
|
|
|
dimIndexingMethod, signal.bitValue.bitIndex, bitIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[bitIndexingMethod, dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceBitIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitForceValue,
|
|
|
|
|
signal.bitValue.bitIndex, bitIndexingMethod));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
forceDimAndBitIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, signal.bitValue.bitForceValue,
|
|
|
|
|
signal.packedIndices, dimIndexingMethod,
|
|
|
|
|
signal.bitValue.bitIndex, bitIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2026-01-10 09:48:46 +01:00
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int releaseValues(const DimIndexingMethod dimIndexingMethod) {
|
2026-01-10 09:48:46 +01:00
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(), [dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.releaseValue, signal.forceValue}));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimIndexedSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.releaseValue, signal.forceValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(), [dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseSignal(scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, {signal.forceValue, signal.releaseValue}));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType, {signal.forceValue, signal.releaseValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int releasePartiallyForcedValues( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
// Skip any values that cannot be partially forced. Can't just reuse releaseValues, because
|
|
|
|
|
// the output argument s_vpi_value of vpi_put_value with vpiReleaseFlag differs depending
|
|
|
|
|
// on whether or not a signal was forced before, and not all signals are forced in the
|
|
|
|
|
// partial forcing test.
|
|
|
|
|
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(), [dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.releaseValue, signal.partialValue.fullSignalForceValue}));
|
|
|
|
|
}
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimIndexedSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.releaseValue, signal.partialValue.fullSignalForceValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(), [dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.partialValue.fullSignalForceValue, signal.releaseValue}));
|
|
|
|
|
}
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.partialValue.fullSignalForceValue, signal.releaseValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int releaseSingleBitForcedValues( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
// Release the entire signal that previously had a single bit forced in it
|
|
|
|
|
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(), [dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.releaseValue, signal.bitValue.fullSignalForceValue}));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimIndexedSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.releaseValue, signal.bitValue.fullSignalForceValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(), [dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.bitValue.fullSignalForceValue, signal.releaseValue}));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.bitValue.fullSignalForceValue, signal.releaseValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extern "C" int partiallyReleaseValues( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
const Direction direction) {
|
|
|
|
|
// Partially release a fully forced signal
|
|
|
|
|
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [direction](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyReleaseSignal(scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.partialValue.partialReleaseValue,
|
|
|
|
|
signal.partialValue.partialForceValue},
|
|
|
|
|
signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo, direction));
|
|
|
|
|
}
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyReleaseDimIndexedSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.partialValue.partialReleaseValue,
|
|
|
|
|
signal.partialValue.partialForceValue},
|
|
|
|
|
signal.partialValue.bitRange.hi, signal.partialValue.bitRange.lo,
|
|
|
|
|
direction, signal.packedIndices));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
2026-03-21 01:24:45 +01:00
|
|
|
std::any_of(TestSignals.begin(), TestSignals.end(), [direction](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyReleaseSignal(scopeName,
|
|
|
|
|
std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.partialValue.partialForceValue,
|
|
|
|
|
signal.partialValue.partialReleaseValue},
|
|
|
|
|
signal.partialValue.bitRange.hi,
|
|
|
|
|
signal.partialValue.bitRange.lo, direction));
|
|
|
|
|
}
|
|
|
|
|
#ifndef XRUN
|
|
|
|
|
else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
partiallyReleaseDimIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.partialValue.partialForceValue,
|
|
|
|
|
signal.partialValue.partialReleaseValue},
|
|
|
|
|
signal.partialValue.bitRange.hi, signal.partialValue.bitRange.lo,
|
|
|
|
|
direction, signal.packedIndices));
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2026-01-10 09:48:46 +01:00
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
extern "C" int releaseSingleBit( // NOLINT(readability-function-cognitive-complexity)
|
|
|
|
|
const BitIndexingMethod bitIndexingMethod, const DimIndexingMethod dimIndexingMethod) {
|
|
|
|
|
// Clocked signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[bitIndexingMethod, dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) { // Also applies to single bit releasing
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseBitIndexedSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.bitValue.bitReleaseValue, signal.bitValue.bitForceValue},
|
|
|
|
|
signal.bitValue.bitIndex, bitIndexingMethod));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimAndBitIndexedSignal(
|
|
|
|
|
scopeName, signal.signalName, signal.valueType,
|
|
|
|
|
{signal.bitValue.bitReleaseValue, signal.bitValue.bitForceValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod, signal.bitValue.bitIndex,
|
|
|
|
|
bitIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// Continuously assigned signals
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
std::any_of(
|
|
|
|
|
TestSignals.begin(), TestSignals.end(),
|
|
|
|
|
[bitIndexingMethod, dimIndexingMethod](const TestSignal& signal) {
|
|
|
|
|
if (signal.shouldTestPartialForce) {
|
|
|
|
|
if (signal.packedIndices.empty()) {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseBitIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.bitValue.bitForceValue, signal.bitValue.bitReleaseValue},
|
|
|
|
|
signal.bitValue.bitIndex, bitIndexingMethod));
|
|
|
|
|
} else {
|
|
|
|
|
CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe)
|
|
|
|
|
releaseDimAndBitIndexedSignal(
|
|
|
|
|
scopeName, std::string{signal.signalName} + "Continuously",
|
|
|
|
|
signal.valueType,
|
|
|
|
|
{signal.bitValue.bitForceValue, signal.bitValue.bitReleaseValue},
|
|
|
|
|
signal.packedIndices, dimIndexingMethod, signal.bitValue.bitIndex,
|
|
|
|
|
bitIndexingMethod));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}));
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
#ifdef IS_VPI
|
|
|
|
|
|
|
|
|
|
static int checkValuesForcedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkValuesForced();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 03:57:13 +01:00
|
|
|
static int checkNonContinuousValuesForcedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkNonContinuousValuesForced();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int checkContinuousValuesReleasedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkContinuousValuesReleased();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
static int checkContinuousValuesPartiallyReleasedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkContinuousValuesPartiallyReleased();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
static int checkValuesPartiallyForcedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkValuesPartiallyForced();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-12 03:57:13 +01:00
|
|
|
static int checkNonContinuousValuesPartiallyForcedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkNonContinuousValuesPartiallyForced();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
static int checkContinuousValuesSingleBitReleasedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkContinuousValuesSingleBitReleased();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int checkNonContinuousSingleBitForcedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkNonContinuousSingleBitForced();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int checkSingleBitForcedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkSingleBitForced();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
static int checkValuesReleasedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkValuesReleased();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
static int checkValuesPartiallyReleasedVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
|
|
|
|
vpi_value.value.integer = checkValuesPartiallyReleased();
|
|
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int checkSingleBitReleasedVpi() {
|
2026-01-10 09:48:46 +01:00
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
s_vpi_value vpi_value;
|
|
|
|
|
|
|
|
|
|
vpi_value.format = vpiIntVal;
|
2026-03-21 01:24:45 +01:00
|
|
|
vpi_value.value.integer = checkSingleBitReleased();
|
2026-01-10 09:48:46 +01:00
|
|
|
vpi_put_value(href, &vpi_value, NULL, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
static int forceValuesVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
TestVpiHandle arg = vpi_scan(it);
|
|
|
|
|
|
|
|
|
|
s_vpi_value dimIndexingMethodValue;
|
|
|
|
|
dimIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(arg, &dimIndexingMethodValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value vpiValue;
|
|
|
|
|
vpiValue.format = vpiIntVal;
|
|
|
|
|
vpiValue.value.integer
|
|
|
|
|
= forceValues(static_cast<DimIndexingMethod>(dimIndexingMethodValue.value.integer));
|
|
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int partiallyForceValuesVpi(PLI_BYTE8*) {
|
2026-01-10 09:48:46 +01:00
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
2026-03-21 01:24:45 +01:00
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
TestVpiHandle arg = vpi_scan(it);
|
|
|
|
|
|
|
|
|
|
s_vpi_value directionValue;
|
|
|
|
|
directionValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(arg, &directionValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value vpiValue;
|
|
|
|
|
vpiValue.format = vpiIntVal;
|
|
|
|
|
vpiValue.value.integer
|
|
|
|
|
= partiallyForceValues(static_cast<Direction>(directionValue.value.integer));
|
|
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int forceSingleBitVpi(PLI_BYTE8*) {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
CHECK_RESULT_NZ(it)
|
|
|
|
|
|
|
|
|
|
TestVpiHandle argBitIndexingMethod = vpi_scan(it);
|
|
|
|
|
CHECK_RESULT_NZ(argBitIndexingMethod)
|
|
|
|
|
TestVpiHandle argDimIndexingMethod = vpi_scan(it);
|
|
|
|
|
CHECK_RESULT_NZ(argDimIndexingMethod)
|
|
|
|
|
CHECK_RESULT_Z(vpi_scan(it))
|
|
|
|
|
|
|
|
|
|
s_vpi_value bitIndexingMethodValue;
|
|
|
|
|
bitIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(argBitIndexingMethod, &bitIndexingMethodValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value dimIndexingMethodValue;
|
|
|
|
|
dimIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(argDimIndexingMethod, &dimIndexingMethodValue);
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
s_vpi_value vpiValue;
|
2026-03-21 01:24:45 +01:00
|
|
|
vpiValue.format = vpiIntVal;
|
|
|
|
|
vpiValue.value.integer
|
|
|
|
|
= forceSingleBit(static_cast<BitIndexingMethod>(bitIndexingMethodValue.value.integer),
|
|
|
|
|
static_cast<DimIndexingMethod>(dimIndexingMethodValue.value.integer));
|
2026-01-10 09:48:46 +01:00
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int releaseValuesVpi(PLI_BYTE8*) {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
TestVpiHandle arg = vpi_scan(it);
|
|
|
|
|
|
|
|
|
|
s_vpi_value dimIndexingMethodValue;
|
|
|
|
|
dimIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(arg, &dimIndexingMethodValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value vpiValue;
|
2026-01-10 09:48:46 +01:00
|
|
|
vpiValue.format = vpiIntVal;
|
2026-03-21 01:24:45 +01:00
|
|
|
vpiValue.value.integer
|
|
|
|
|
= releaseValues(static_cast<DimIndexingMethod>(dimIndexingMethodValue.value.integer));
|
|
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int releasePartiallyForcedValuesVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
2026-03-21 01:24:45 +01:00
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
TestVpiHandle arg = vpi_scan(it);
|
|
|
|
|
|
|
|
|
|
s_vpi_value dimIndexingMethodValue;
|
|
|
|
|
dimIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(arg, &dimIndexingMethodValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value vpiValue;
|
|
|
|
|
vpiValue.format = vpiIntVal;
|
|
|
|
|
vpiValue.value.integer = releasePartiallyForcedValues(
|
|
|
|
|
static_cast<DimIndexingMethod>(dimIndexingMethodValue.value.integer));
|
|
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int releaseSingleBitForcedValuesVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
TestVpiHandle arg = vpi_scan(it);
|
|
|
|
|
|
|
|
|
|
s_vpi_value dimIndexingMethodValue;
|
|
|
|
|
dimIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(arg, &dimIndexingMethodValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value vpiValue;
|
|
|
|
|
vpiValue.format = vpiIntVal;
|
|
|
|
|
vpiValue.value.integer = releaseSingleBitForcedValues(
|
|
|
|
|
static_cast<DimIndexingMethod>(dimIndexingMethodValue.value.integer));
|
|
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int partiallyReleaseValuesVpi() {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
TestVpiHandle arg = vpi_scan(it);
|
|
|
|
|
|
|
|
|
|
s_vpi_value directionValue;
|
|
|
|
|
directionValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(arg, &directionValue);
|
|
|
|
|
|
2026-01-10 09:48:46 +01:00
|
|
|
s_vpi_value vpiValue;
|
2026-03-21 01:24:45 +01:00
|
|
|
vpiValue.format = vpiIntVal;
|
|
|
|
|
vpiValue.value.integer
|
|
|
|
|
= partiallyReleaseValues(static_cast<Direction>(directionValue.value.integer));
|
|
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int releaseSingleBitVpi(PLI_BYTE8*) {
|
|
|
|
|
TestVpiHandle href = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
|
|
|
|
|
TestVpiHandle it = vpi_iterate(vpiArgument, href);
|
|
|
|
|
CHECK_RESULT_NZ(it)
|
|
|
|
|
|
|
|
|
|
TestVpiHandle argBitIndexingMethod = vpi_scan(it);
|
|
|
|
|
CHECK_RESULT_NZ(argBitIndexingMethod)
|
|
|
|
|
TestVpiHandle argDimIndexingMethod = vpi_scan(it);
|
|
|
|
|
CHECK_RESULT_NZ(argDimIndexingMethod)
|
|
|
|
|
CHECK_RESULT_Z(vpi_scan(it))
|
2026-01-10 09:48:46 +01:00
|
|
|
|
2026-03-21 01:24:45 +01:00
|
|
|
s_vpi_value bitIndexingMethodValue;
|
|
|
|
|
bitIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(argBitIndexingMethod, &bitIndexingMethodValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value dimIndexingMethodValue;
|
|
|
|
|
dimIndexingMethodValue.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(argDimIndexingMethod, &dimIndexingMethodValue);
|
|
|
|
|
|
|
|
|
|
s_vpi_value vpiValue;
|
2026-01-10 09:48:46 +01:00
|
|
|
vpiValue.format = vpiIntVal;
|
2026-03-21 01:24:45 +01:00
|
|
|
vpiValue.value.integer
|
|
|
|
|
= releaseSingleBit(static_cast<BitIndexingMethod>(bitIndexingMethodValue.value.integer),
|
|
|
|
|
static_cast<DimIndexingMethod>(dimIndexingMethodValue.value.integer));
|
|
|
|
|
|
|
|
|
|
vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::array<s_vpi_systf_data, 20> vpi_systf_data = {
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$forceValues",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))forceValuesVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$releaseValues",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))releaseValuesVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$releasePartiallyForcedValues",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))releasePartiallyForcedValuesVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$releaseSingleBitForcedValues",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))releaseSingleBitForcedValuesVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkValuesForced",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkValuesForcedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkNonContinuousValuesForced",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkNonContinuousValuesForcedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkContinuousValuesReleased",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkContinuousValuesReleasedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkContinuousValuesPartiallyReleased",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkContinuousValuesPartiallyReleasedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkValuesPartiallyForced",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkValuesPartiallyForcedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkSingleBitForced",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkSingleBitForcedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{
|
|
|
|
|
vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkNonContinuousValuesPartiallyForced",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkNonContinuousValuesPartiallyForcedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkContinuousValuesSingleBitReleased",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkContinuousValuesSingleBitReleasedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkNonContinuousSingleBitForced",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkNonContinuousSingleBitForcedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkValuesReleased",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkValuesReleasedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkValuesPartiallyReleased",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkValuesPartiallyReleasedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$checkSingleBitReleased",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))checkSingleBitReleasedVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$partiallyForceValues",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))partiallyForceValuesVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$forceSingleBit",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))forceSingleBitVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$partiallyReleaseValues",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))partiallyReleaseValuesVpi, 0, 0, 0},
|
|
|
|
|
s_vpi_systf_data{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$releaseSingleBit",
|
|
|
|
|
(PLI_INT32(*)(PLI_BYTE8*))releaseSingleBitVpi, 0, 0, 0}};
|
2026-01-10 09:48:46 +01:00
|
|
|
|
|
|
|
|
// cver entry
|
|
|
|
|
extern "C" void vpi_compat_bootstrap(void) {
|
|
|
|
|
for (s_vpi_systf_data& systf : vpi_systf_data) vpi_register_systf(&systf);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// icarus entry
|
|
|
|
|
void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0};
|
|
|
|
|
#endif
|