From 086bf351f2c6b79e82f39ccdf057ebc1007ec44b Mon Sep 17 00:00:00 2001 From: Christian Hecken <104357222+Christian-Hecken@users.noreply.github.com> Date: Sat, 21 Mar 2026 01:24:45 +0100 Subject: [PATCH] Fix VPI force of bit-selected signals (#7211) (#7301) --- include/verilated_vpi.cpp | 163 ++- test_regress/t/t_vpi_force.cpp | 2375 +++++++++++++++++++++++++++++--- test_regress/t/t_vpi_force.v | 1864 +++++++++++++++++++++++-- test_regress/t/t_vpi_var.cpp | 54 +- test_regress/t/t_vpi_var.v | 7 + test_regress/t/t_vpi_var2.v | 7 + test_regress/t/t_vpi_var3.v | 7 + 7 files changed, 4148 insertions(+), 329 deletions(-) diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index c37e5ceed..2cfa719fd 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -395,6 +395,7 @@ public: return dynamic_cast(reinterpret_cast(h)); } uint32_t bitOffset() const override { return m_bitOffset; } + int32_t partselBits() const { return m_partselBits; } uint32_t bitSize() const { if (m_partselBits >= 0) return static_cast(m_partselBits); return VerilatedVpioVarBase::bitSize(); @@ -1119,7 +1120,7 @@ public: } s().m_inertialPuts.clear(); } - static auto getForceControlSignals(const VerilatedVpioVarBase* vop); + static auto getForceControlSignals(const VerilatedVpioVar* vop); static std::size_t vlTypeSize(VerilatedVarType vltype); static void setAllBitsToValue(const VerilatedVpioVar* vop, uint8_t bitValue) { @@ -1314,15 +1315,16 @@ VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE { return s().m_errorInfop; } -auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVarBase* const baseSignalVop) { +auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVar* const baseSignalVop) { const VerilatedForceControlSignals* const forceControlSignals = baseSignalVop->varp()->forceControlSignals(); // LCOV_EXCL_START - Would require a Verilation time error, so cannot test if (VL_UNLIKELY(!forceControlSignals)) { - VL_VPI_ERROR_(__FILE__, __LINE__, - "%s: VPI force or release requested for '%s', but signal has no force " - "control signals. Ensure signal is marked as forceable", - __func__, baseSignalVop->fullname()); + VL_VPI_ERROR_( + __FILE__, __LINE__, + "%s: VPI put or get requested for forceable signal '%s', but signal has no force " + "control signals.", + __func__, baseSignalVop->fullname()); return std::pair, std::unique_ptr>{ nullptr, nullptr}; } // LCOV_EXCL_STOP @@ -1331,26 +1333,141 @@ auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVarBase* const b // LCOV_EXCL_START - Would require a Verilation time error, so cannot test if (VL_UNLIKELY(!forceEnableSignalVarp)) { VL_VPI_ERROR_(__FILE__, __LINE__, - "%s: VPI force or release requested for '%s', but force enable signal could " - "not be found. Ensure signal is marked as forceable", + "%s: VPI put or get requested for forceable signal '%s', but force enable " + "signal could not be found.", __func__, baseSignalVop->fullname()); return std::pair, std::unique_ptr>{ nullptr, nullptr}; } if (VL_UNLIKELY(!forceValueSignalVarp)) { - VL_VPI_ERROR_(__FILE__, __LINE__, - "%s: VPI force or release requested for '%s', but force value signal could " - "not be found. Ensure signal is marked as forceable", - __func__, baseSignalVop->fullname()); + VL_VPI_ERROR_( + __FILE__, __LINE__, + "%s: VPI put or get requested for forceable signal '%s', but force value signal could " + "not be found.", + __func__, baseSignalVop->fullname()); return std::pair, std::unique_ptr>{ nullptr, nullptr}; } // LCOV_EXCL_STOP - VerilatedVpioVar forceEnableSignal{forceEnableSignalVarp, baseSignalVop->scopep()}; - VerilatedVpioVar forceValueSignal{forceValueSignalVarp, baseSignalVop->scopep()}; + + // Adjust dimension and partselect to match the base signal, so that forcing a partial signal + // doesn't exceed the bounds given by the base signal + VerilatedVpioVar* forceEnableSignalVop + = new VerilatedVpioVar{forceEnableSignalVarp, baseSignalVop->scopep()}; + VerilatedVpioVar* forceValueSignalVop + = new VerilatedVpioVar{forceValueSignalVarp, baseSignalVop->scopep()}; + + for (int idx : baseSignalVop->index()) { + VerilatedVpioVar* nextForceEnableSignalVop = forceEnableSignalVop->withIndex(idx); + VerilatedVpioVar* nextForceValueSignalVop = forceValueSignalVop->withIndex(idx); + VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); + VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + forceEnableSignalVop = nextForceEnableSignalVop; + forceValueSignalVop = nextForceValueSignalVop; + if (!forceEnableSignalVop || !forceValueSignalVop) break; // LCOV_EXCL_LINE + } + + // LCOV_EXCL_START - Would require a Verilation time error, so cannot test + if (VL_UNLIKELY(!forceEnableSignalVop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: VPI put or get requested for forceable signal '%s', but force enable " + "signal could not be indexed to the same dimension as the base signal.", + __func__, baseSignalVop->fullname()); + if (VL_UNLIKELY(forceValueSignalVop)) + VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; + } + if (VL_UNLIKELY(!forceValueSignalVop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: VPI put or get requested for forceable signal '%s', but force value " + "signal could not be indexed to the same dimension as the base signal.", + __func__, baseSignalVop->fullname()); + if (VL_UNLIKELY(forceEnableSignalVop)) + VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; + } + // LCOV_EXCL_STOP + + if (VL_UNLIKELY(baseSignalVop->partselBits() != -1)) { + // Bits are stored left-to-right in memory, which can either be ascending or descending. To + // match the bitOffset of the base signal, the distance to the rightmost bit, rather than + // to the lowest indexed bit, must be determined + int currentDimRight = baseSignalVop->rangep()->right(); + int32_t offsetFromRight + = static_cast(static_cast(baseSignalVop->bitOffset()) + - static_cast(forceValueSignalVop->bitOffset())); + const bool isDescending + = baseSignalVop->rangep()->left() >= baseSignalVop->rangep()->right(); + + const int32_t partSelIndexRight + = isDescending ? currentDimRight + offsetFromRight : currentDimRight - offsetFromRight; + const int32_t partSelIndexLeft + = isDescending ? partSelIndexRight + (baseSignalVop->partselBits() - 1) + : partSelIndexRight - (baseSignalVop->partselBits() - 1); + const int32_t partSelIndexHigh = std::max(partSelIndexLeft, partSelIndexRight); + const int32_t partSelIndexLow = std::min(partSelIndexLeft, partSelIndexRight); + + VerilatedVpioVar* partIndexedForceEnableSignalVop + = forceEnableSignalVop->withPartSelect(partSelIndexHigh, partSelIndexLow); + VerilatedVpioVar* partIndexedForceValueSignalVop + = forceValueSignalVop->withPartSelect(partSelIndexHigh, partSelIndexLow); + + VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); + VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + forceEnableSignalVop = partIndexedForceEnableSignalVop; + forceValueSignalVop = partIndexedForceValueSignalVop; + } + + // LCOV_EXCL_START - Would require a Verilation time error, so cannot test + if (VL_UNLIKELY(!forceEnableSignalVop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: VPI put or get requested for forceable signal '%s', but part selection " + "could not be applied to the force enable signal.", + __func__, baseSignalVop->fullname()); + if (VL_UNLIKELY(forceValueSignalVop)) + VL_DO_DANGLING(delete forceValueSignalVop, forceValueSignalVop); + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; + } + if (VL_UNLIKELY(!forceValueSignalVop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: VPI put or get requested for forceable signal '%s', but part selection " + "could not be applied to the force value signal.", + __func__, baseSignalVop->fullname()); + if (VL_UNLIKELY(forceEnableSignalVop)) + VL_DO_DANGLING(delete forceEnableSignalVop, forceEnableSignalVop); + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; + } + // LCOV_EXCL_STOP + +#ifdef VL_DEBUG + // Sanity checks: Offsets, widths, and dimensions should all match between the base signal and + // the force control signals, so that they always refer to the same bits + assert(forceEnableSignalVop->bitSize() == baseSignalVop->bitSize()); + assert(forceValueSignalVop->bitSize() == baseSignalVop->bitSize()); + assert(forceEnableSignalVop->indexedDim() == baseSignalVop->indexedDim()); + assert(forceValueSignalVop->indexedDim() == baseSignalVop->indexedDim()); + assert(forceEnableSignalVop->index() == baseSignalVop->index()); + assert(forceValueSignalVop->index() == baseSignalVop->index()); + assert(forceEnableSignalVop->bitOffset() == baseSignalVop->bitOffset()); + assert(forceValueSignalVop->bitOffset() == baseSignalVop->bitOffset()); + assert(forceEnableSignalVop->partselBits() == baseSignalVop->partselBits()); + assert(forceValueSignalVop->partselBits() == baseSignalVop->partselBits()); + // entSize can differ because the force enable signal can have a different vltyp than the base + // signal (e.g. the base signal can be of type VLVT_REAL, while the force enable signal is + // still of type VLVT_UINT8), but for now entSize is only used for unpacked arrays, which + // cannot be forced through VPI yet + // assert(forceEnableSignalVop->entSize() == baseSignalVop->entSize()); + assert(forceValueSignalVop->entSize() == baseSignalVop->entSize()); + assert(forceEnableSignalVop->varDatap() == forceEnableSignalVarp->datap()); + assert(forceValueSignalVop->varDatap() == forceValueSignalVarp->datap()); +#endif // VL_DEBUG + return std::pair, std::unique_ptr>{ - std::make_unique(forceEnableSignal), - std::make_unique(forceValueSignal)}; + forceEnableSignalVop, forceValueSignalVop}; } std::size_t VerilatedVpiImp::vlTypeSize(const VerilatedVarType vltype) { @@ -2902,11 +3019,15 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { // __VforceRd already has the correct value, but that signal is not public and thus not present // in the scope's m_varsp map, will be removed entirely eventually (#7092), so its value has to // be recreated using the __VforceEn and __VforceVal signals. - const auto forceControlSignals - = vop->varp()->isForceable() - ? VerilatedVpiImp::getForceControlSignals(vop) - : std::pair, std::unique_ptr>{ - nullptr, nullptr}; + const auto forceControlSignals = [vop]() { + if (vop->varp()->isForceable()) { + const VerilatedVpioVar* varVop = dynamic_cast(vop); + assert(varVop); // VerilatedVpioParams cannot be forced + return VerilatedVpiImp::getForceControlSignals(varVop); + } + return std::pair, std::unique_ptr>{ + nullptr, nullptr}; + }(); const VerilatedVpioVarBase* const forceEnableSignalVop = forceControlSignals.first.get(); const VerilatedVpioVarBase* const forceValueSignalVop = forceControlSignals.second.get(); t_vpi_error_info getForceControlSignalsError{}; diff --git a/test_regress/t/t_vpi_force.cpp b/test_regress/t/t_vpi_force.cpp index 8ed831c27..0d6d2fd61 100644 --- a/test_regress/t/t_vpi_force.cpp +++ b/test_regress/t/t_vpi_force.cpp @@ -32,53 +32,151 @@ using signalValueTypes = union { const struct t_vpi_vecval* vector; }; +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; +}; + using TestSignal = const struct { const char* signalName; PLI_INT32 valueType; + std::vector packedIndices; // Allows access into multidimensional packed vectors signalValueTypes releaseValue; signalValueTypes forceValue; - std::pair - partialForceValue; // No std::optional on C++14, so the bool inside the pair 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. + 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) }; -constexpr std::array TestSignals = { +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 +}; + +enum class Direction : uint8_t { + Descending = 0, // [hi:lo] + Ascending = 1, // [lo:hi] +}; + +#ifndef IVERILOG +const std::array TestSignals = { +#else // Multidimensional packed arrays aren't tested in Icarus +const std::array TestSignals = { +#endif TestSignal{"onebit", vpiIntVal, + {}, {.integer = 1}, {.integer = 0}, - {{}, false}}, // Can't partially force just one bit + false, + {}, // Can't partially force just one bit + {}}, // Can't index into a single bit TestSignal{"intval", vpiIntVal, + {}, {.integer = -1431655766}, // 1010...1010 {.integer = 0x55555555}, // 0101...0101 - {{.integer = -1431677611}, true}}, // 1010...010_010...0101 + 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}}, TestSignal{"vectorC", vpiVectorVal, + {}, // NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays) {.vector = (t_vpi_vecval[]){{0b10101010, 0}}}, {.vector = (t_vpi_vecval[]){{0b01010101, 0}}}, - {{.vector = (t_vpi_vecval[]){{0b10100101, 0}}}, true}}, + 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}}, // NOLINTEND (cppcoreguidelines-avoid-c-arrays) TestSignal{ "vectorQ", vpiVectorVal, + {}, // 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 - {{.vector = (t_vpi_vecval[]){{0xD5555555UL, 0}, {0x2AAAAAAAUL, 0}}}, - true}}, // 1010...010_010...0101 + 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}}, // NOLINTEND (cppcoreguidelines-avoid-c-arrays) TestSignal{"vectorW", vpiVectorVal, + {}, // NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays) {.vector = (t_vpi_vecval[]){{0xAAAAAAAAUL, 0}, // 1010...1010 {0xAAAAAAAAUL, 0}, @@ -88,68 +186,460 @@ constexpr std::array TestSignals = { {0x55555555UL, 0}, {0x55555555UL, 0}, {0x55555555UL, 0}}}, + true, {{.vector = (t_vpi_vecval[]){{0x55555555UL, 0}, // 1010...010_010...0101 {0x55555555UL, 0}, {0xAAAAAAAAUL, 0}, {0xAAAAAAAAUL, 0}}}, - true}}, + {.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}}, // NOLINTEND (cppcoreguidelines-avoid-c-arrays) TestSignal{"real1", vpiRealVal, + {}, {.real = 1.0}, {.real = 123456.789}, - {{}, false}}, // reals cannot be packed and individual bits cannot be accessed, so - // there is no way to partially force a real signal. + false, + {}, // reals cannot be packed and individual bits cannot be accessed,so there is no + // way to partially force a real signal. + {}}, - TestSignal{"textHalf", vpiStringVal, {.str = "Hf"}, {.str = "T2"}, {{.str = "H2"}, true}}, + 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}}, TestSignal{"textLong", vpiStringVal, + {}, {.str = "Long64b"}, {.str = "44Four44"}, - {{.str = "Lonur44"}, true}}, + 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 TestSignal{"text", vpiStringVal, + {}, {.str = "Verilog Test module"}, {.str = "lorem ipsum"}, - {{.str = "Verilog Tesem ipsum"}, true}}, + 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"}, - {{.str = "10100101"}, true}}, - TestSignal{"octString", - vpiOctStrVal, - {.str = "25252"}, // 010101010101010 - {.str = "52525"}, // 101010101010101 - {{.str = "25325"}, true}}, // 010101011010101 + 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}}, TestSignal{"hexString", vpiHexStrVal, + {}, {.str = "aaaaaaaaaaaaaaaa"}, // 1010...1010 {.str = "5555555555555555"}, // 0101...0101 - {{.str = "aaaaaaaa55555555"}, true}}, // 1010...010_010...0101 + 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}}, TestSignal{"decStringC", vpiDecStrVal, + {}, {.str = "170"}, // 10101010 {.str = "85"}, // 01010101 - {{.str = "165"}, true}}, // 10100101 + 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}}, TestSignal{"decStringS", vpiDecStrVal, + {}, {.str = "43690"}, // 1010...1010 {.str = "21845"}, // 0101...0101 - {{.str = "43605"}, true}}, // 1010...010_010...0101 + 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}}, TestSignal{"decStringI", vpiDecStrVal, + {}, {.str = "2863311530"}, // 1010...1010 {.str = "1431655765"}, // 0101...0101 - {{.str = "2863289685"}, true}}, // 1010...010_010...0101 + 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}}, TestSignal{"decStringQ", vpiDecStrVal, + {}, {.str = "12297829382473034410"}, // 1010...1010 {.str = "6148914691236517205"}, // 0101...0101 - {{.str = "12297829381041378645"}, true}}, // 1010...010_010...0101 + 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(0xAAAAAAAA)}, + {.integer = 0x55555555}, + true, + {{.integer = static_cast(0xAAAA5555)}, + {.integer = 0x5555AAAA}, + {.integer = 0x5555}, + {.integer = 0xAAAA}, + {.lo = -31, .hi = -16}}, + {{.integer = static_cast(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(0xAAAAAAAA)}, + {.integer = 0x55555555}, + true, + {{.integer = static_cast(0xAAAA5555)}, + {.integer = 0x5555AAAA}, + {.integer = 0x5555}, + {.integer = 0xAAAA}, + {.lo = 24, .hi = 39}}, + {{.integer = static_cast(0xAAAAAAAB)}, + {.integer = 0x55555554}, + vpiIntVal, + {.integer = 0b1}, + {.integer = 0b0}, + 39}}, +#endif }; bool vpiCheckErrorLevel(const int maxAllowedErrorLevel) { @@ -196,6 +686,7 @@ int expectVpiPutError(const std::string& signalName, s_vpi_value value_s, const bool vpiValuesEqual(const std::size_t bitCount, const s_vpi_value& first, const s_vpi_value& second) { + if (first.format != second.format) return false; switch (first.format) { case vpiIntVal: return first.value.integer == second.value.integer; break; @@ -250,17 +741,13 @@ std::unique_ptr vpiValueWithFormat(const PLI_INT32 signalFormat, return value_sp; } -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(testSignalFullName.c_str()), nullptr); - CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe) - +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) { std::unique_ptr receivedValueSp = vpiValueWithFormat(signalFormat, {}); CHECK_RESULT_NZ(receivedValueSp); // NOLINT(concurrency-mt-unsafe) - vpi_get_value(signalHandle, receivedValueSp.get()); + vpi_get_value(handle, receivedValueSp.get()); // NOLINTNEXTLINE(concurrency-mt-unsafe) CHECK_RESULT_Z(vpiCheckErrorLevel(maxAllowedErrorLevel)) @@ -268,9 +755,184 @@ int checkValue(const std::string& scopeName, const std::string& testSignalName, const std::unique_ptr expectedValueSp = vpiValueWithFormat(signalFormat, expectedValue); CHECK_RESULT_NZ(expectedValueSp); // NOLINT(concurrency-mt-unsafe) + // NOLINTNEXTLINE(concurrency-mt-unsafe) - CHECK_RESULT_NZ( - vpiValuesEqual(vpi_get(vpiSize, signalHandle), *receivedValueSp, *expectedValueSp)); + 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(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& 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& 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((testSignalFullName + getDimIndexString(indices)).c_str()), + nullptr); + break; + case DimIndexingMethod::ByRepeatedIndex: { + signalHandle + = vpi_handle_by_name(const_cast(testSignalFullName.c_str()), nullptr); + for (const int index : indices) { + vpiHandle nextIndexHandle + = vpi_handle_by_index(signalHandle, static_cast(index)); + vpi_release_handle(signalHandle); + signalHandle = nextIndexHandle; + } + break; + } + case DimIndexingMethod::ByMultiIndex: { + TestVpiHandle baseSignalHandle + = vpi_handle_by_name(const_cast(testSignalFullName.c_str()), nullptr); + signalHandle + = vpi_handle_by_multi_index(baseSignalHandle, static_cast(indices.size()), + const_cast(indices.data())); + break; + } + default: + VL_PRINTF("Unsupported DimIndexingMethod %i passed to getDimIndexedSignalHandle\n", + static_cast(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((testSignalFullName + getBitIndexString(index)).c_str()), + nullptr); + break; + case BitIndexingMethod::ByIndex: { + TestVpiHandle baseSignalHandle + = vpi_handle_by_name(const_cast(testSignalFullName.c_str()), nullptr); + signalHandle = vpi_handle_by_index(baseSignalHandle, static_cast(index)); + break; + } + default: + VL_PRINTF("Unsupported BitIndexingMethod %i passed to getBitIndexedSignalHandle\n", + static_cast(bitIndexingMethod)); + return nullptr; + } + + return signalHandle; +} + +TestVpiHandle getDimAndBitIndexedSignalHandle(const std::string& scopeName, + const std::string& testSignalName, + const std::vector& 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((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(bitIndex)); + break; + default: + VL_PRINTF("Unsupported BitIndexingMethod %i passed to getDimAndBitIndexedSignalHandle\n", + static_cast(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& 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& 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)); return 0; } @@ -281,6 +943,103 @@ int forceSignal(const std::string& scopeName, const std::string& testSignalName, = std::string{scopeName} + "." + std::string{testSignalName}; TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const) = vpi_handle_by_name(const_cast(testSignalFullName.c_str()), nullptr); + + CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe) + + std::unique_ptr 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 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& indices, + const DimIndexingMethod dimIndexingMethod) { + TestVpiHandle signalHandle + = getDimIndexedSignalHandle(scopeName, testSignalName, indices, dimIndexingMethod); + CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe) + + std::unique_ptr 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& 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 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& 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(partSelectedSignalName.c_str()), nullptr); CHECK_RESULT_NZ(signalHandle); // NOLINT(concurrency-mt-unsafe) std::unique_ptr value_sp = vpiValueWithFormat(signalFormat, forceValue); @@ -295,10 +1054,26 @@ int forceSignal(const std::string& scopeName, const std::string& testSignalName, } int releaseSignal(const std::string& scopeName, const std::string& testSignalName, - const PLI_INT32 signalFormat, - const std::pair releaseValue) { - const signalValueTypes expectedReleaseValueInit = releaseValue.first; - const signalValueTypes expectedReleaseValue = releaseValue.second; + 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 + const std::string testSignalFullName = std::string{scopeName} + "." + std::string{testSignalName}; TestVpiHandle const signalHandle //NOLINT(misc-misplaced-const) @@ -336,111 +1111,773 @@ int releaseSignal(const std::string& scopeName, const std::string& testSignalNam return 0; } -} // namespace +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) -extern "C" int checkValuesForced(void) { - // Clocked signals - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, signal.signalName, signal.valueType, signal.forceValue)); - return 0; - })); + // 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 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 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)); - // Continuously assigned signals - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - const std::string continouslyAssignedSignal - = std::string{signal.signalName} + "Continuously"; - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, continouslyAssignedSignal, signal.valueType, - signal.forceValue)); - return 0; - })); return 0; } -extern "C" int checkNonContinuousValuesForced(void) { +int releaseDimIndexedSignal(const std::string& scopeName, const std::string& testSignalName, + const PLI_INT32 signalFormat, const ReleaseValue releaseValue, + const std::vector& 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 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 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& 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 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 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& 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(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 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) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, signal.signalName, signal.valueType, signal.forceValue)); - return 0; - })); - return 0; -} - -extern "C" int checkContinuousValuesReleased(void) { - // 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) { - const std::string continouslyAssignedSignal - = std::string{signal.signalName} + "Continuously"; - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, continouslyAssignedSignal, signal.valueType, - signal.releaseValue)); - return 0; - })); - return 0; -} - -extern "C" int checkValuesPartiallyForced(void) { - // Clocked signals - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - if (signal.partialForceValue.second) + if (signal.packedIndices.empty()) { CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, signal.signalName, signal.valueType, - signal.partialForceValue.first)); + 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.partialForceValue.second) + if (signal.packedIndices.empty()) { CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) checkValue(scopeName, std::string{signal.signalName} + "Continuously", - signal.valueType, signal.partialForceValue.first)); + 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 checkNonContinuousValuesPartiallyForced(void) { +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 + + // Clocked signals + 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} + 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; + })); + + // Continuously assigned signals + 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 CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - if (signal.partialForceValue.second) - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, signal.signalName, signal.valueType, - signal.partialForceValue.first)); - return 0; - })); + 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; + })); return 0; } -extern "C" int checkValuesReleased(void) { +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 + CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) + 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; + })); + return 0; +} + +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. + CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) + 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; + })); + return 0; +} + +extern "C" int checkSingleBitForced(void) { // NOLINT(readability-function-cognitive-complexity) + // Check both single bit values and full signal values + + // Clocked signals + CHECK_RESULT_Z(checkNonContinuousSingleBitForced()); + + // Continuously assigned signals + CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) + 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; + })); + return 0; +} + +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 + CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) + 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; + })); + return 0; +} + +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 + + // Clocked signals + CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) + 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; + })); + + // Continuously assigned signals + CHECK_RESULT_Z(checkContinuousValuesSingleBitReleased()); + return 0; +} + +extern "C" int checkValuesReleased() { // Clocked signals CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, signal.signalName, signal.valueType, signal.releaseValue)); + 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)); + } return 0; })); // Continuously assigned signals - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - checkValue(scopeName, std::string{signal.signalName} + "Continuously", - signal.valueType, signal.releaseValue)); - return 0; - })); + CHECK_RESULT_Z(checkContinuousValuesReleased()); return 0; } @@ -477,8 +1914,8 @@ extern "C" int tryInvalidPutOperations() { "vpi_put_value: Non hex character 'G' in '12AG' as value vpiHexStrVal for " "t.test.hexString__VforceVal")); - // 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. + // 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. CHECK_RESULT_Z(expectVpiPutError( // NOLINT(concurrency-mt-unsafe) "onebit", {.format = vpiRawFourStateVal, .value = {}}, vpiForceFlag, "vl_check_format: Unsupported format (vpiRawFourStateVal) for t.test.onebit")); @@ -551,8 +1988,8 @@ extern "C" int putInertialDelay() { CHECK_RESULT_NZ(vpiValuesEqual(vpi_get(vpiSize, signalHandle), value_s, expectedValueS)); // NOTE: Because __VforceRd will only be updated in `eval_act`, `doInertialPuts` does not - // update the value read by `vpi_get_value` - that is only visible after another `eval`. Hence, - // `checkInertialDelay` must be called later to check success. + // update the value read by `vpi_get_value` - that is only visible after another `eval`. + // Hence, `checkInertialDelay` must be called later to check success. VerilatedVpi::doInertialPuts(); return 0; @@ -582,7 +2019,7 @@ extern "C" int checkInertialDelay() { } #endif -extern "C" int forceValues(void) { +extern "C" int forceValues(const DimIndexingMethod dimIndexingMethod) { if (!TestSimulator::is_verilator()) { #ifdef VERILATOR printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n"); @@ -592,70 +2029,405 @@ extern "C" int forceValues(void) { // Clocked signals CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - forceSignal(scopeName, signal.signalName, signal.valueType, signal.forceValue)); + 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; + })); + + // 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) + 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; + })); + return 0; +} + +extern "C" int partiallyForceValues( // NOLINT(readability-function-cognitive-complexity) + const Direction direction) { + // 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) + 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 + } return 0; })); // Continuously assigned signals CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - forceSignal(scopeName, std::string{signal.signalName} + "Continuously", - signal.valueType, signal.forceValue)); + 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 + } return 0; })); return 0; } -extern "C" int releaseValues(void) { +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(), [](const TestSignal& signal) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - releaseSignal(scopeName, signal.signalName, signal.valueType, - {signal.releaseValue, signal.forceValue})); + 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; +} + +extern "C" int releaseValues(const DimIndexingMethod dimIndexingMethod) { + // Clocked 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, 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 + } return 0; })); // Continuously assigned signals CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - std::any_of(TestSignals.begin(), TestSignals.end(), [](const TestSignal& signal) { - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - releaseSignal(scopeName, std::string{signal.signalName} + "Continuously", - signal.valueType, {signal.forceValue, signal.releaseValue})); + 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 + } return 0; })); return 0; } -extern "C" int releasePartiallyForcedValues(void) { - // 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. - +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(), [](const TestSignal& signal) { - if (signal.partialForceValue.second) - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - releaseSignal(scopeName, signal.signalName, signal.valueType, - {signal.releaseValue, signal.partialForceValue.first})); - return 0; - })); + 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(), [](const TestSignal& signal) { - if (signal.partialForceValue.second) - CHECK_RESULT_Z( // NOLINT(concurrency-mt-unsafe) - releaseSignal(scopeName, std::string{signal.signalName} + "Continuously", - signal.valueType, - {signal.partialForceValue.first, signal.releaseValue})); - return 0; - })); + 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; } @@ -694,6 +2466,17 @@ static int checkContinuousValuesReleasedVpi() { return 0; } +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; +} + static int checkValuesPartiallyForcedVpi() { TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); s_vpi_value vpi_value; @@ -716,6 +2499,39 @@ static int checkNonContinuousValuesPartiallyForcedVpi() { return 0; } +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; +} + static int checkValuesReleasedVpi() { TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); s_vpi_value vpi_value; @@ -727,59 +2543,246 @@ static int checkValuesReleasedVpi() { return 0; } -static int forceValuesVpi() { +static int checkValuesPartiallyReleasedVpi() { TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); s_vpi_value vpi_value; vpi_value.format = vpiIntVal; - vpi_value.value.integer = forceValues(); + vpi_value.value.integer = checkValuesPartiallyReleased(); vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); return 0; } -static int releaseValuesVpi() { +static int checkSingleBitReleasedVpi() { TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); - s_vpi_value vpiValue; + s_vpi_value vpi_value; + vpi_value.format = vpiIntVal; + vpi_value.value.integer = checkSingleBitReleased(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +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 = releaseValues(); - vpi_put_value(href, &vpiValue, NULL, vpiNoDelay); + vpiValue.value.integer + = forceValues(static_cast(dimIndexingMethodValue.value.integer)); + vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay); + + return 0; +} + +static int partiallyForceValuesVpi(PLI_BYTE8*) { + 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); + + s_vpi_value vpiValue; + vpiValue.format = vpiIntVal; + vpiValue.value.integer + = partiallyForceValues(static_cast(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); + + s_vpi_value vpiValue; + vpiValue.format = vpiIntVal; + vpiValue.value.integer + = forceSingleBit(static_cast(bitIndexingMethodValue.value.integer), + static_cast(dimIndexingMethodValue.value.integer)); + + 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; + vpiValue.format = vpiIntVal; + vpiValue.value.integer + = releaseValues(static_cast(dimIndexingMethodValue.value.integer)); + vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay); return 0; } static int releasePartiallyForcedValuesVpi() { TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); - s_vpi_value vpiValue; + 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(); - vpi_put_value(href, &vpiValue, NULL, vpiNoDelay); + vpiValue.value.integer = releasePartiallyForcedValues( + static_cast(dimIndexingMethodValue.value.integer)); + vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay); return 0; } -std::array 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*)"$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*)"$checkValuesPartiallyForced", - (PLI_INT32(*)(PLI_BYTE8*))checkValuesPartiallyForcedVpi, 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*)"$checkValuesReleased", - (PLI_INT32(*)(PLI_BYTE8*))checkValuesReleasedVpi, 0, 0, 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(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); + + s_vpi_value vpiValue; + vpiValue.format = vpiIntVal; + vpiValue.value.integer + = partiallyReleaseValues(static_cast(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)) + + 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; + vpiValue.format = vpiIntVal; + vpiValue.value.integer + = releaseSingleBit(static_cast(bitIndexingMethodValue.value.integer), + static_cast(dimIndexingMethodValue.value.integer)); + + vpi_put_value(href, &vpiValue, nullptr, vpiNoDelay); + + return 0; +} + +std::array 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}}; // cver entry extern "C" void vpi_compat_bootstrap(void) { diff --git a/test_regress/t/t_vpi_force.v b/test_regress/t/t_vpi_force.v index e4cf37ed8..623a0fa23 100644 --- a/test_regress/t/t_vpi_force.v +++ b/test_regress/t/t_vpi_force.v @@ -19,6 +19,12 @@ `define PUBLIC_FORCEABLE `endif +`ifdef TEST_VERBOSE + `define verbose 1'b1 +`else + `define verbose 1'b0 +`endif + module t; reg clk; @@ -37,6 +43,23 @@ module Test ( input clk ); +typedef enum byte { + BitByIndex = 0, + BitByName = 1 +} BitIndexingMethod; + +typedef enum byte { + DimByRepeatedIndex = 0, + DimByMultiIndex = 1, + DimByName = 2 +} DimIndexingMethod; + +typedef enum byte { + Descending = 0, + Ascending = 1 +} Direction; + + `ifdef IVERILOG `elsif USE_VPI_NOT_DPI `ifdef VERILATOR @@ -45,15 +68,26 @@ module Test ( extern "C" int tryInvalidPutOperations(); extern "C" int putInertialDelay(); extern "C" int checkInertialDelay(); - extern "C" int forceValues(); - extern "C" int releaseValues(); - extern "C" int releasePartiallyForcedValues(); + extern "C" int forceValues(int dimIndexingMethod); + extern "C" int partiallyForceValues(int direction); + extern "C" int forceSingleBit(int bitIndexingMethod, int dimIndexingMethod); + extern "C" int releaseValues(int dimIndexingMethod); + extern "C" int releasePartiallyForcedValues(int dimIndexingMethod); + extern "C" int releaseSingleBitForcedValues(int dimIndexingMethod); + extern "C" int partiallyReleaseValues(int direction); + extern "C" int releaseSingleBit(int bitIndexingMethod, int dimIndexingMethod); extern "C" int checkValuesForced(); extern "C" int checkValuesPartiallyForced(); + extern "C" int checkSingleBitForced(); + extern "C" int checkValuesPartiallyReleased(); + extern "C" int checkSingleBitReleased(); extern "C" int checkValuesReleased(); extern "C" int checkNonContinuousValuesForced(); extern "C" int checkContinuousValuesReleased(); + extern "C" int checkContinuousValuesPartiallyReleased(); extern "C" int checkNonContinuousValuesPartiallyForced(); + extern "C" int checkContinuousValuesSingleBitReleased(); + extern "C" int checkNonContinuousSingleBitForced(); `verilog `endif `else @@ -63,15 +97,26 @@ module Test ( import "DPI-C" context function int putInertialDelay(); import "DPI-C" context function int checkInertialDelay(); `endif - import "DPI-C" context function int forceValues(); - import "DPI-C" context function int releaseValues(); - import "DPI-C" context function int releasePartiallyForcedValues(); + import "DPI-C" context function int forceValues(input byte dimIndexingMethod); + import "DPI-C" context function int partiallyForceValues(input byte direction); + import "DPI-C" context function int forceSingleBit(input byte bitIndexingMethod, input byte dimIndexingMethod); + import "DPI-C" context function int releaseValues(input byte dimIndexingMethod); + import "DPI-C" context function int releasePartiallyForcedValues(input byte dimIndexingMethod); + import "DPI-C" context function int releaseSingleBitForcedValues(input byte dimIndexingMethod); + import "DPI-C" context function int partiallyReleaseValues(input byte direction); + import "DPI-C" context function int releaseSingleBit(input byte bitIndexingMethod, input byte dimIndexingMethod); import "DPI-C" context function int checkValuesPartiallyForced(); + import "DPI-C" context function int checkSingleBitForced(); + import "DPI-C" context function int checkValuesPartiallyReleased(); import "DPI-C" context function int checkValuesForced(); + import "DPI-C" context function int checkSingleBitReleased(); import "DPI-C" context function int checkValuesReleased(); import "DPI-C" context function int checkNonContinuousValuesForced(); import "DPI-C" context function int checkContinuousValuesReleased(); + import "DPI-C" context function int checkContinuousValuesPartiallyReleased(); import "DPI-C" context function int checkNonContinuousValuesPartiallyForced(); + import "DPI-C" context function int checkContinuousValuesSingleBitReleased(); + import "DPI-C" context function int checkNonContinuousSingleBitForced(); `endif // Verify that vpi_put_value still works for strings @@ -115,6 +160,52 @@ module Test ( logic [ 31:0] decStringI `PUBLIC_FORCEABLE; // IData logic [ 63:0] decStringQ `PUBLIC_FORCEABLE; // QData + // Multidimensional signals - packed dimensions only for now, until forcing + // unpacked signals is fully supported (#4735) + // verilator lint_off ASCRANGE + + // These multidimensional packed arrays do not work well in Icarus: + // - Continuous assignments do not work + // - vpi_handle_by_multi_index is not implemented + // - vpi_handle_by_name and vpi_handle_by_index fail with negative indices + // Hence, these signals are excluded from testing with Icarus. Recommend + // Xcelium for cross-checking results. +`ifndef IVERILOG + // Force the entire packed array (no partial forcing possible, because + // partial indexing only works for bits, not dimension slices) + logic [1:0][0:1][0:-1] packed2dC `PUBLIC_FORCEABLE; // CData + logic [1:0][0:1][0:-3] packed2dS `PUBLIC_FORCEABLE; // SData + logic [1:0][0:2][0:-3] packed2dI `PUBLIC_FORCEABLE; // IData + logic [3:0][0:3][0:-3] packed2dQ `PUBLIC_FORCEABLE; // QData + logic [3:0][0:3][0:-7] packed2dW `PUBLIC_FORCEABLE; // VlWide + + // Index into the first dimension and attempt to force all elements in it. + // Should have the same effect as forcing the entire packed2d array, + // while the other elements in the first dimension should stay unforced + // Partial forcing is not possible here, because partial indexing only works + // for bits, not dimension slices. + logic [-1:-2][1:0][0:1][0:-1] packed3dS `PUBLIC_FORCEABLE; // SData + logic [-1:-2][1:0][0:1][0:-3] packed3dI `PUBLIC_FORCEABLE; // IData + logic [-1:-2][1:0][0:2][0:-3] packed3dQ `PUBLIC_FORCEABLE; // QData + logic [-1:-2][3:0][0:3][0:-3] packed3dW `PUBLIC_FORCEABLE; // VlWide + + // Attempt to force only one element + logic [-3:-3][2:2][-1:-1][3:0][0:-1] packed4dC `PUBLIC_FORCEABLE; // CData + logic [-3:-3][2:2][-1:-1][3:0][0:-3] packed4dS `PUBLIC_FORCEABLE; // SData + logic [-3:-3][2:2][-1:-1][3:0][0:-7] packed4dI `PUBLIC_FORCEABLE; // IData + logic [-3:-3][2:2][-1:-1][3:0][0:-15] packed4dQ `PUBLIC_FORCEABLE; // QData + logic [-3:-3][2:2][-1:-1][3:0][0:-31] packed4dW `PUBLIC_FORCEABLE; // VlWide + + // Same as with packed4d*, but with ascending range + logic [-3:-3][2:2][-1:-1][4:7][8:9] ascPacked4dC `PUBLIC_FORCEABLE; // CData + logic [-3:-3][2:2][-1:-1][4:7][8:11] ascPacked4dS `PUBLIC_FORCEABLE; // SData + logic [-3:-3][2:2][-1:-1][4:7][8:15] ascPacked4dI `PUBLIC_FORCEABLE; // IData + logic [-3:-3][2:2][-1:-1][4:7][8:23] ascPacked4dQ `PUBLIC_FORCEABLE; // QData + logic [-3:-3][2:2][-1:-1][4:7][8:39] ascPacked4dW `PUBLIC_FORCEABLE; // VlWide +`endif + + // verilator lint_on ASCRANGE + // Continuously assigned signals: // Force with vpiIntVal @@ -150,6 +241,45 @@ module Test ( wire [ 31:0] decStringIContinuously `PUBLIC_FORCEABLE; // IData wire [ 63:0] decStringQContinuously `PUBLIC_FORCEABLE; // QData + // Multidimensional signals (packed dimensions only for now) + // verilator lint_off ASCRANGE + +`ifndef IVERILOG // Continuous assignments to these signals do not work in Icarus + // Force the entire packed array (no partial forcing possible, because + // partial indexing only works for bits, not dimension slices) + logic [1:0][0:1][0:-1] packed2dCContinuously `PUBLIC_FORCEABLE; // CData + logic [1:0][0:1][0:-3] packed2dSContinuously `PUBLIC_FORCEABLE; // SData + logic [1:0][0:2][0:-3] packed2dIContinuously `PUBLIC_FORCEABLE; // IData + logic [3:0][0:3][0:-3] packed2dQContinuously `PUBLIC_FORCEABLE; // QData + logic [3:0][0:3][0:-7] packed2dWContinuously `PUBLIC_FORCEABLE; // VlWide + + // Index into the first dimension and attempt to force all elements in it. + // Should have the same effect as forcing the entire packed2d array, + // while the other elements in the first dimension should stay unforced + // Partial forcing is not possible here, because partial indexing only works + // for bits, not dimension slices. + logic [-1:-2][1:0][0:1][0:-1] packed3dSContinuously `PUBLIC_FORCEABLE; // SData + logic [-1:-2][1:0][0:1][0:-3] packed3dIContinuously `PUBLIC_FORCEABLE; // IData + logic [-1:-2][1:0][0:2][0:-3] packed3dQContinuously `PUBLIC_FORCEABLE; // QData + logic [-1:-2][3:0][0:3][0:-3] packed3dWContinuously `PUBLIC_FORCEABLE; // VlWide + + // Fully index into the array and attempt to force only one element + logic [-3:-3][2:2][-1:-1][3:0][0:-1] packed4dCContinuously `PUBLIC_FORCEABLE; // CData + logic [-3:-3][2:2][-1:-1][3:0][0:-3] packed4dSContinuously `PUBLIC_FORCEABLE; // SData + logic [-3:-3][2:2][-1:-1][3:0][0:-7] packed4dIContinuously `PUBLIC_FORCEABLE; // IData + logic [-3:-3][2:2][-1:-1][3:0][0:-15] packed4dQContinuously `PUBLIC_FORCEABLE; // QData + logic [-3:-3][2:2][-1:-1][3:0][0:-31] packed4dWContinuously `PUBLIC_FORCEABLE; // VlWide + + // Same as with packed4d*, but with ascending range + logic [-3:-3][2:2][-1:-1][4:7][8:9] ascPacked4dCContinuously `PUBLIC_FORCEABLE; // CData + logic [-3:-3][2:2][-1:-1][4:7][8:11] ascPacked4dSContinuously `PUBLIC_FORCEABLE; // SData + logic [-3:-3][2:2][-1:-1][4:7][8:15] ascPacked4dIContinuously `PUBLIC_FORCEABLE; // IData + logic [-3:-3][2:2][-1:-1][4:7][8:23] ascPacked4dQContinuously `PUBLIC_FORCEABLE; // QData + logic [-3:-3][2:2][-1:-1][4:7][8:39] ascPacked4dWContinuously `PUBLIC_FORCEABLE; // VlWide +`endif + + // verilator lint_on ASCRANGE + always @(posedge clk) begin nonPublic <= 1; @@ -174,6 +304,57 @@ module Test ( decStringS <= 16'hAAAA; decStringI <= 32'hAAAAAAAA; decStringQ <= 64'd12297829382473034410; // 0b1010... + +`ifndef IVERILOG + packed2dC <= '{'{2'h2, 2'h2}, '{2'h2, 2'h2}}; + packed2dS <= '{'{4'hA, 4'hA}, '{4'hA, 4'hA}}; + packed2dI <= '{'{4'hA, 4'hA, 4'hA}, '{4'hA, 4'hA, 4'hA}}; + packed2dQ <= '{ + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA} + }; + packed2dW <= '{ + '{8'hAA, 8'hAA, 8'hAA, 8'hAA}, + '{8'hAA, 8'hAA, 8'hAA, 8'hAA}, + '{8'hAA, 8'hAA, 8'hAA, 8'hAA}, + '{8'hAA, 8'hAA, 8'hAA, 8'hAA} + }; + + packed3dS <= '{'{'{2'h2, 2'h2}, '{2'h2, 2'h2}}, '{'{2'h2, 2'h2}, '{2'h2, 2'h2}}}; + packed3dI <= '{'{'{4'hA, 4'hA}, '{4'hA, 4'hA}}, '{'{4'hA, 4'hA}, '{4'hA, 4'hA}}}; + packed3dQ <= '{ + '{'{4'hA, 4'hA, 4'hA}, '{4'hA, 4'hA, 4'hA}}, + '{'{4'hA, 4'hA, 4'hA}, '{4'hA, 4'hA, 4'hA}} + }; + packed3dW <= '{ + '{ + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA} + }, + '{ + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA} + } + }; + + packed4dC <= '{'{'{'{2'h2, 2'h2, 2'h2, 2'h2}}}}; + packed4dS <= '{'{'{'{4'hA, 4'hA, 4'hA, 4'hA}}}}; + packed4dI <= '{'{'{'{8'hAA, 8'hAA, 8'hAA, 8'hAA}}}}; + packed4dQ <= '{'{'{'{16'hAAAA, 16'hAAAA, 16'hAAAA, 16'hAAAA}}}}; + packed4dW <= '{'{'{'{32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA}}}}; + + ascPacked4dC <= '{'{'{'{2'h2, 2'h2, 2'h2, 2'h2}}}}; + ascPacked4dS <= '{'{'{'{4'hA, 4'hA, 4'hA, 4'hA}}}}; + ascPacked4dI <= '{'{'{'{8'hAA, 8'hAA, 8'hAA, 8'hAA}}}}; + ascPacked4dQ <= '{'{'{'{16'hAAAA, 16'hAAAA, 16'hAAAA, 16'hAAAA}}}}; + ascPacked4dW <= '{'{'{'{32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA}}}}; +`endif end assign onebitContinuously = 1; @@ -198,6 +379,57 @@ module Test ( assign decStringIContinuously = 32'hAAAAAAAA; assign decStringQContinuously = 64'd12297829382473034410; // 0b1010... +`ifndef IVERILOG + assign packed2dCContinuously = '{'{2'h2, 2'h2}, '{2'h2, 2'h2}}; + assign packed2dSContinuously = '{'{4'hA, 4'hA}, '{4'hA, 4'hA}}; + assign packed2dIContinuously = '{'{4'hA, 4'hA, 4'hA}, '{4'hA, 4'hA, 4'hA}}; + assign packed2dQContinuously = '{ + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA} + }; + assign packed2dWContinuously = '{ + '{8'hAA, 8'hAA, 8'hAA, 8'hAA}, + '{8'hAA, 8'hAA, 8'hAA, 8'hAA}, + '{8'hAA, 8'hAA, 8'hAA, 8'hAA}, + '{8'hAA, 8'hAA, 8'hAA, 8'hAA} + }; + + assign packed3dSContinuously = '{'{'{2'h2, 2'h2}, '{2'h2, 2'h2}}, '{'{2'h2, 2'h2}, '{2'h2, 2'h2}}}; + assign packed3dIContinuously = '{'{'{4'hA, 4'hA}, '{4'hA, 4'hA}}, '{'{4'hA, 4'hA}, '{4'hA, 4'hA}}}; + assign packed3dQContinuously = '{ + '{'{4'hA, 4'hA, 4'hA}, '{4'hA, 4'hA, 4'hA}}, + '{'{4'hA, 4'hA, 4'hA}, '{4'hA, 4'hA, 4'hA}} + }; + assign packed3dWContinuously = '{ + '{ + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA} + }, + '{ + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA}, + '{4'hA, 4'hA, 4'hA, 4'hA} + } + }; + + assign packed4dCContinuously = '{'{'{'{2'h2, 2'h2, 2'h2, 2'h2}}}}; + assign packed4dSContinuously = '{'{'{'{4'hA, 4'hA, 4'hA, 4'hA}}}}; + assign packed4dIContinuously = '{'{'{'{8'hAA, 8'hAA, 8'hAA, 8'hAA}}}}; + assign packed4dQContinuously = '{'{'{'{16'hAAAA, 16'hAAAA, 16'hAAAA, 16'hAAAA}}}}; + assign packed4dWContinuously = '{'{'{'{32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA}}}}; + + assign ascPacked4dCContinuously = '{'{'{'{2'h2, 2'h2, 2'h2, 2'h2}}}}; + assign ascPacked4dSContinuously = '{'{'{'{4'hA, 4'hA, 4'hA, 4'hA}}}}; + assign ascPacked4dIContinuously = '{'{'{'{8'hAA, 8'hAA, 8'hAA, 8'hAA}}}}; + assign ascPacked4dQContinuously = '{'{'{'{16'hAAAA, 16'hAAAA, 16'hAAAA, 16'hAAAA}}}}; + assign ascPacked4dWContinuously = '{'{'{'{32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA}}}}; +`endif + task automatic svForceValues(); force onebit = 0; force intval = 32'h55555555; @@ -205,7 +437,7 @@ module Test ( force vectorQ = 62'h15555555_55555555; force vectorW = 128'h55555555_55555555_55555555_55555555; force real1 = 123456.789; - force textHalf = "T2"; + force textHalf = "T3"; force textLong = "44Four44"; force text = "lorem ipsum"; force binString = 8'b01010101; @@ -216,13 +448,56 @@ module Test ( force decStringI = 32'h55555555; force decStringQ = 64'd6148914691236517205; +`ifndef IVERILOG + force packed2dC = '{'{2'h1, 2'h1}, '{2'h1, 2'h1}}; + force packed2dS = '{'{4'h5, 4'h5}, '{4'h5, 4'h5}}; + force packed2dI = '{'{4'h5, 4'h5, 4'h5}, '{4'h5, 4'h5, 4'h5}}; + force packed2dQ = '{ + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5} + }; + force packed2dW = '{ + '{8'h55, 8'h55, 8'h55, 8'h55}, + '{8'h55, 8'h55, 8'h55, 8'h55}, + '{8'h55, 8'h55, 8'h55, 8'h55}, + '{8'h55, 8'h55, 8'h55, 8'h55} + }; + + force packed3dS[-2] = '{'{2'h1, 2'h1}, '{2'h1, 2'h1}}; + force packed3dI[-2] = '{'{4'h5, 4'h5}, '{4'h5, 4'h5}}; + force packed3dQ[-2] = '{'{4'h5, 4'h5, 4'h5}, '{4'h5, 4'h5, 4'h5}}; + force packed3dW[-2] = '{ + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5} + }; + + force packed4dC[-3][2][-1][2] = 2'h1; + force packed4dS[-3][2][-1][2] = 4'h5; + force packed4dI[-3][2][-1][2] = 8'h55; + force packed4dQ[-3][2][-1][2] = 16'h5555; + force packed4dW[-3][2][-1][2] = 32'h55555555; + + // For equivalent results as with packed4d* (i.e. forcing the second + // element from the left), force element 5 in the ascending range [4:7], + // which corresponds to index 2 in the descending range [3:0] + force ascPacked4dC[-3][2][-1][5] = 2'h1; + force ascPacked4dS[-3][2][-1][5] = 4'h5; + force ascPacked4dI[-3][2][-1][5] = 8'h55; + force ascPacked4dQ[-3][2][-1][5] = 16'h5555; + force ascPacked4dW[-3][2][-1][5] = 32'h55555555; +`endif + force onebitContinuously = 0; force intvalContinuously = 32'h55555555; force vectorCContinuously = 8'h55; force vectorQContinuously = 62'h15555555_55555555; force vectorWContinuously = 128'h55555555_55555555_55555555_55555555; force real1Continuously = 123456.789; - force textHalfContinuously = "T2"; + force textHalfContinuously = "T3"; force textLongContinuously = "44Four44"; force textContinuously = "lorem ipsum"; force binStringContinuously = 8'b01010101; @@ -232,6 +507,46 @@ module Test ( force decStringSContinuously = 16'h5555; force decStringIContinuously = 32'h55555555; force decStringQContinuously = 64'd6148914691236517205; + +`ifndef IVERILOG + force packed2dCContinuously = '{'{2'h1, 2'h1}, '{2'h1, 2'h1}}; + force packed2dSContinuously = '{'{4'h5, 4'h5}, '{4'h5, 4'h5}}; + force packed2dIContinuously = '{'{4'h5, 4'h5, 4'h5}, '{4'h5, 4'h5, 4'h5}}; + force packed2dQContinuously = '{ + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5} + }; + force packed2dWContinuously = '{ + '{8'h55, 8'h55, 8'h55, 8'h55}, + '{8'h55, 8'h55, 8'h55, 8'h55}, + '{8'h55, 8'h55, 8'h55, 8'h55}, + '{8'h55, 8'h55, 8'h55, 8'h55} + }; + + force packed3dSContinuously[-2] = '{'{2'h1, 2'h1}, '{2'h1, 2'h1}}; + force packed3dIContinuously[-2] = '{'{4'h5, 4'h5}, '{4'h5, 4'h5}}; + force packed3dQContinuously[-2] = '{'{4'h5, 4'h5, 4'h5}, '{4'h5, 4'h5, 4'h5}}; + force packed3dWContinuously[-2] = '{ + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5}, + '{4'h5, 4'h5, 4'h5, 4'h5} + }; + + force packed4dCContinuously[-3][2][-1][2] = 2'h1; + force packed4dSContinuously[-3][2][-1][2] = 4'h5; + force packed4dIContinuously[-3][2][-1][2] = 8'h55; + force packed4dQContinuously[-3][2][-1][2] = 16'h5555; + force packed4dWContinuously[-3][2][-1][2] = 32'h55555555; + + force ascPacked4dCContinuously[-3][2][-1][5] = 2'h1; + force ascPacked4dSContinuously[-3][2][-1][5] = 4'h5; + force ascPacked4dIContinuously[-3][2][-1][5] = 8'h55; + force ascPacked4dQContinuously[-3][2][-1][5] = 16'h5555; + force ascPacked4dWContinuously[-3][2][-1][5] = 32'h55555555; +`endif endtask task automatic svPartiallyForceValues(); @@ -241,9 +556,9 @@ module Test ( force vectorQ[30:0] = 31'h55555555; force vectorW[63:0] = 64'h55555555_55555555; - force textHalf[7:0] = "2"; + force textHalf[7:0] = "3"; force textLong[31:0] = "ur44"; - force text[63:0] = "em ipsum"; + force text[79:0] = "orem ipsum"; // VlWide part select force binString[3:0] = 4'b0101; force octString[6:0] = 7'o125; @@ -254,15 +569,31 @@ module Test ( force decStringI[15:0] = 16'h5555; force decStringQ[31:0] = 32'd1431655765; +`ifndef XRUN // Partial forcing multidimensional signals works only through SystemVerilog with Xrun, but not through VPI, so don't test it at all +`ifndef IVERILOG + force packed4dC[-3][2][-1][2][-1:-1] = 1'h1; + force packed4dS[-3][2][-1][2][-2:-3] = 2'h1; + force packed4dI[-3][2][-1][2][-4:-7] = 4'h5; + force packed4dQ[-3][2][-1][2][-8:-15] = 8'h55; + force packed4dW[-3][2][-1][2][-16:-31] = 16'h5555; + + force ascPacked4dC[-3][2][-1][5][9:9] = 1'h1; + force ascPacked4dS[-3][2][-1][5][10:11] = 2'h1; + force ascPacked4dI[-3][2][-1][5][12:15] = 4'h5; + force ascPacked4dQ[-3][2][-1][5][16:23] = 8'h55; + force ascPacked4dW[-3][2][-1][5][24:39] = 16'h5555; +`endif +`endif + force intvalContinuously[15:0] = 16'h5555; force vectorCContinuously[3:0] = 4'h5; force vectorQContinuously[30:0] = 31'h55555555; force vectorWContinuously[63:0] = 64'h55555555_55555555; - force textHalfContinuously[7:0] = "2"; + force textHalfContinuously[7:0] = "3"; force textLongContinuously[31:0] = "ur44"; - force textContinuously[63:0] = "em ipsum"; + force textContinuously[79:0] = "orem ipsum"; force binStringContinuously[3:0] = 4'b0101; force octStringContinuously[6:0] = 7'o125; @@ -272,6 +603,80 @@ module Test ( force decStringSContinuously[7:0] = 8'h55; force decStringIContinuously[15:0] = 16'h5555; force decStringQContinuously[31:0] = 32'd1431655765; + +`ifndef XRUN +`ifndef IVERILOG + force packed4dCContinuously[-3][2][-1][2][-1:-1] = 1'h1; + force packed4dSContinuously[-3][2][-1][2][-2:-3] = 2'h1; + force packed4dIContinuously[-3][2][-1][2][-4:-7] = 4'h5; + force packed4dQContinuously[-3][2][-1][2][-8:-15] = 8'h55; + force packed4dWContinuously[-3][2][-1][2][-16:-31] = 16'h5555; + + force ascPacked4dCContinuously[-3][2][-1][5][9:9] = 1'h1; + force ascPacked4dSContinuously[-3][2][-1][5][10:11] = 2'h1; + force ascPacked4dIContinuously[-3][2][-1][5][12:15] = 4'h5; + force ascPacked4dQContinuously[-3][2][-1][5][16:23] = 8'h55; + force ascPacked4dWContinuously[-3][2][-1][5][24:39] = 16'h5555; +`endif +`endif + endtask + + task automatic svForceSingleBit(); + force intval[0] = 1; + force vectorC[0] = 1; + force vectorQ[0] = 1; + force vectorW[0] = 1; + force textHalf[0] = 1; + force textLong[1] = 0; // "b" = 'b01100010, force second-to-last bit to 0 + force text[3] = 1; // "e" = 'b01100101, force bit 3 to 1 + force binString[0] = 1; + force octString[0] = 1; + force hexString[0] = 1; + force decStringC[0] = 1; + force decStringS[0] = 1; + force decStringI[0] = 1; + force decStringQ[0] = 1; +`ifndef IVERILOG + force packed4dC[-3][2][-1][2][-1] = 1; + force packed4dS[-3][2][-1][2][-3] = 1; + force packed4dI[-3][2][-1][2][-7] = 1; + force packed4dQ[-3][2][-1][2][-15] = 1; + force packed4dW[-3][2][-1][2][-31] = 1; + + force ascPacked4dC[-3][2][-1][5][9] = 1; + force ascPacked4dS[-3][2][-1][5][11] = 1; + force ascPacked4dI[-3][2][-1][5][15] = 1; + force ascPacked4dQ[-3][2][-1][5][23] = 1; + force ascPacked4dW[-3][2][-1][5][39] = 1; +`endif + + force intvalContinuously[0] = 1; + force vectorCContinuously[0] = 1; + force vectorQContinuously[0] = 1; + force vectorWContinuously[0] = 1; + force textHalfContinuously[0] = 1; + force textLongContinuously[1] = 0; // "b" = 'b01100010, force second-to-last bit to 0 + force textContinuously[3] = 1; // "e" = 'b01100101, force bit 3 to 1 + force binStringContinuously[0] = 1; + force octStringContinuously[0] = 1; + force hexStringContinuously[0] = 1; + force decStringCContinuously[0] = 1; + force decStringSContinuously[0] = 1; + force decStringIContinuously[0] = 1; + force decStringQContinuously[0] = 1; +`ifndef IVERILOG + force packed4dCContinuously[-3][2][-1][2][-1] = 1; + force packed4dSContinuously[-3][2][-1][2][-3] = 1; + force packed4dIContinuously[-3][2][-1][2][-7] = 1; + force packed4dQContinuously[-3][2][-1][2][-15] = 1; + force packed4dWContinuously[-3][2][-1][2][-31] = 1; + + force ascPacked4dCContinuously[-3][2][-1][5][9] = 1; + force ascPacked4dSContinuously[-3][2][-1][5][11] = 1; + force ascPacked4dIContinuously[-3][2][-1][5][15] = 1; + force ascPacked4dQContinuously[-3][2][-1][5][23] = 1; + force ascPacked4dWContinuously[-3][2][-1][5][39] = 1; +`endif endtask task automatic vpiPutString(); @@ -358,25 +763,99 @@ module Test ( end endtask - task automatic vpiForceValues(); + task automatic vpiForceValues(DimIndexingMethod dimIndexingMethod); integer vpiStatus = 1; `ifdef VERILATOR `ifdef USE_VPI_NOT_DPI - vpiStatus = $c32("forceValues()"); + vpiStatus = $c32("forceValues(",dimIndexingMethod,")"); `else - vpiStatus = forceValues(); + vpiStatus = forceValues(dimIndexingMethod); `endif `elsif IVERILOG - vpiStatus = $forceValues; + vpiStatus = $forceValues(dimIndexingMethod); `elsif USE_VPI_NOT_DPI - vpiStatus = $forceValues; + vpiStatus = $forceValues(dimIndexingMethod); `else - vpiStatus = forceValues(); + vpiStatus = forceValues(dimIndexingMethod); `endif if (vpiStatus != 0) begin $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); - $display("C Test failed (could not force value)"); + if (dimIndexingMethod == DimByRepeatedIndex) begin + $display("C Test failed (could not force value using vpi_handle_by_index)"); + end else if (dimIndexingMethod == DimByMultiIndex) begin + $display("C Test failed (could not force value using vpi_handle_by_multi_index)"); + end else begin + $display("C Test failed (could not force value using vpi_handle_by_name)"); + end + $stop; + end + endtask + + task automatic vpiPartiallyForceValues(Direction direction); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("partiallyForceValues(",direction,")"); +`else + vpiStatus = partiallyForceValues(direction); +`endif +`elsif IVERILOG + vpiStatus = $partiallyForceValues(direction); +`elsif USE_VPI_NOT_DPI + vpiStatus = $partiallyForceValues(direction); +`else + vpiStatus = partiallyForceValues(direction); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $write("C Test failed (could not partially force value in "); + if (direction == Ascending) begin + $write("ascending"); + end else begin + $write("descending"); + end + $write(" bit order"); + $display(")"); + $stop; + end + endtask + + task automatic vpiForceSingleBit(BitIndexingMethod bitIndexingMethod, DimIndexingMethod dimIndexingMethod); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("forceSingleBit(",bitIndexingMethod,",",dimIndexingMethod,")"); +`else + vpiStatus = forceSingleBit(bitIndexingMethod, dimIndexingMethod); +`endif +`elsif IVERILOG + vpiStatus = $forceSingleBit(bitIndexingMethod, dimIndexingMethod); +`elsif USE_VPI_NOT_DPI + vpiStatus = $forceSingleBit(bitIndexingMethod, dimIndexingMethod); +`else + vpiStatus = forceSingleBit(bitIndexingMethod, dimIndexingMethod); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $write("C Test failed (could not force single bit value using "); + if(dimIndexingMethod == DimByRepeatedIndex) begin + $write("vpi_handle_by_index"); + end else if (dimIndexingMethod == DimByMultiIndex) begin + $write("vpi_handle_by_multi_index"); + end else begin + $write("vpi_handle_by_name"); + end + $write(" dimension indexing and "); + if (bitIndexingMethod == BitByIndex) begin + $write("vpi_handle_by_index"); + end else begin + $write("vpi_handle_by_name"); + end + $write(" bit indexing"); + $display(")"); $stop; end endtask @@ -398,6 +877,27 @@ module Test ( release decStringS; release decStringI; release decStringQ; +`ifndef IVERILOG + release packed2dC; + release packed2dS; + release packed2dI; + release packed2dQ; + release packed2dW; + release packed3dS[-2]; + release packed3dI[-2]; + release packed3dQ[-2]; + release packed3dW[-2]; + release packed4dC[-3][2][-1][2]; + release packed4dS[-3][2][-1][2]; + release packed4dI[-3][2][-1][2]; + release packed4dQ[-3][2][-1][2]; + release packed4dW[-3][2][-1][2]; + release ascPacked4dC[-3][2][-1][5]; + release ascPacked4dS[-3][2][-1][5]; + release ascPacked4dI[-3][2][-1][5]; + release ascPacked4dQ[-3][2][-1][5]; + release ascPacked4dW[-3][2][-1][5]; +`endif release onebitContinuously; release intvalContinuously; @@ -415,71 +915,314 @@ module Test ( release decStringSContinuously; release decStringIContinuously; release decStringQContinuously; +`ifndef IVERILOG + release packed2dCContinuously; + release packed2dSContinuously; + release packed2dIContinuously; + release packed2dQContinuously; + release packed2dWContinuously; + release packed3dSContinuously[-2]; + release packed3dIContinuously[-2]; + release packed3dQContinuously[-2]; + release packed3dWContinuously[-2]; + release packed4dCContinuously[-3][2][-1][2]; + release packed4dSContinuously[-3][2][-1][2]; + release packed4dIContinuously[-3][2][-1][2]; + release packed4dQContinuously[-3][2][-1][2]; + release packed4dWContinuously[-3][2][-1][2]; + release ascPacked4dCContinuously[-3][2][-1][5]; + release ascPacked4dSContinuously[-3][2][-1][5]; + release ascPacked4dIContinuously[-3][2][-1][5]; + release ascPacked4dQContinuously[-3][2][-1][5]; + release ascPacked4dWContinuously[-3][2][-1][5]; +`endif endtask - task automatic vpiReleaseValues(); + task automatic vpiReleaseValues(DimIndexingMethod dimIndexingMethod); integer vpiStatus = 1; `ifdef VERILATOR `ifdef USE_VPI_NOT_DPI - vpiStatus = $c32("releaseValues()"); + vpiStatus = $c32("releaseValues(",dimIndexingMethod,")"); `else - vpiStatus = releaseValues(); + vpiStatus = releaseValues(dimIndexingMethod); `endif `elsif IVERILOG - vpiStatus = $releaseValues; + vpiStatus = $releaseValues(dimIndexingMethod); `elsif USE_VPI_NOT_DPI - vpiStatus = $releaseValues; + vpiStatus = $releaseValues(dimIndexingMethod); `else - vpiStatus = releaseValues(); + vpiStatus = releaseValues(dimIndexingMethod); `endif if (vpiStatus != 0) begin $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); - $display("C Test failed (could not release value)"); + if (dimIndexingMethod == DimByRepeatedIndex) begin + $display("C Test failed (could not release value using vpi_handle_by_index)"); + end else if (dimIndexingMethod == DimByMultiIndex) begin + $display("C Test failed (could not release value using vpi_handle_by_multi_index)"); + end else begin + $display("C Test failed (could not release value using vpi_handle_by_name)"); + end $stop; end endtask - task automatic vpiReleasePartiallyForcedValues(); + task automatic svPartiallyReleaseValues(); + release intval[15:0]; + + release vectorC[3:0]; + release vectorQ[30:0]; + release vectorW[63:0]; + + release textHalf[7:0]; + release textLong[31:0]; + release text[79:0]; + release binString[3:0]; + + release octString[6:0]; + release hexString[31:0]; + + release decStringC[3:0]; + release decStringS[7:0]; + release decStringI[15:0]; + release decStringQ[31:0]; + +`ifndef IVERILOG + release packed4dC[-3][2][-1][2][-1:-1]; + release packed4dS[-3][2][-1][2][-2:-3]; + release packed4dI[-3][2][-1][2][-4:-7]; + release packed4dQ[-3][2][-1][2][-8:-15]; + release packed4dW[-3][2][-1][2][-16:-31]; + + release ascPacked4dC[-3][2][-1][5][9:9]; + release ascPacked4dS[-3][2][-1][5][10:11]; + release ascPacked4dI[-3][2][-1][5][12:15]; + release ascPacked4dQ[-3][2][-1][5][16:23]; + release ascPacked4dW[-3][2][-1][5][24:39]; +`endif + + release intvalContinuously[15:0]; + + release vectorCContinuously[3:0]; + release vectorQContinuously[30:0]; + release vectorWContinuously[63:0]; + + release textHalfContinuously[7:0]; + release textLongContinuously[31:0]; + release textContinuously[79:0]; + release binStringContinuously[3:0]; + + release octStringContinuously[6:0]; + release hexStringContinuously[31:0]; + + release decStringCContinuously[3:0]; + release decStringSContinuously[7:0]; + release decStringIContinuously[15:0]; + release decStringQContinuously[31:0]; + +`ifndef IVERILOG + release packed4dCContinuously[-3][2][-1][2][-1:-1]; + release packed4dSContinuously[-3][2][-1][2][-2:-3]; + release packed4dIContinuously[-3][2][-1][2][-4:-7]; + release packed4dQContinuously[-3][2][-1][2][-8:-15]; + release packed4dWContinuously[-3][2][-1][2][-16:-31]; + + release ascPacked4dCContinuously[-3][2][-1][5][9:9]; + release ascPacked4dSContinuously[-3][2][-1][5][10:11]; + release ascPacked4dIContinuously[-3][2][-1][5][12:15]; + release ascPacked4dQContinuously[-3][2][-1][5][16:23]; + release ascPacked4dWContinuously[-3][2][-1][5][24:39]; +`endif + endtask + + task automatic vpiPartiallyReleaseValues(Direction direction); integer vpiStatus = 1; `ifdef VERILATOR `ifdef USE_VPI_NOT_DPI - vpiStatus = $c32("releasePartiallyForcedValues()"); + vpiStatus = $c32("partiallyReleaseValues(",direction,")"); `else - vpiStatus = releasePartiallyForcedValues(); + vpiStatus = partiallyReleaseValues(direction); `endif `elsif IVERILOG - vpiStatus = $releasePartiallyForcedValues; + vpiStatus = $partiallyReleaseValues(direction); `elsif USE_VPI_NOT_DPI - vpiStatus = $releasePartiallyForcedValues; + vpiStatus = $partiallyReleaseValues(direction); `else - vpiStatus = releasePartiallyForcedValues(); + vpiStatus = partiallyReleaseValues(direction); `endif if (vpiStatus != 0) begin $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); - $display("C Test failed (could not release value)"); + $write("C Test failed (could not partially release value in "); + if (direction == Ascending) begin + $write("ascending"); + end else begin + $write("descending"); + end + $write(" bit order"); + $display(")"); + $stop; + end + endtask + + task automatic vpiReleasePartiallyForcedValues(DimIndexingMethod dimIndexingMethod); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("releasePartiallyForcedValues(",dimIndexingMethod,")"); +`else + vpiStatus = releasePartiallyForcedValues(dimIndexingMethod); +`endif +`elsif IVERILOG + vpiStatus = $releasePartiallyForcedValues(dimIndexingMethod); +`elsif USE_VPI_NOT_DPI + vpiStatus = $releasePartiallyForcedValues(dimIndexingMethod); +`else + vpiStatus = releasePartiallyForcedValues(dimIndexingMethod); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + if (dimIndexingMethod == DimByRepeatedIndex) begin + $display("C Test failed (could not release partially forced value using vpi_handle_by_index)"); + end else if (dimIndexingMethod == DimByMultiIndex) begin + $display("C Test failed (could not release partially forced value using vpi_handle_by_multi_index)"); + end else begin + $display("C Test failed (could not release partially forced value using vpi_handle_by_name)"); + end + $stop; + end + endtask + + task automatic svReleaseSingleBit(); + release intval[0]; + release vectorC[0]; + release vectorQ[0]; + release vectorW[0]; + release textHalf[0]; + release textLong[1]; + release text[3]; + release binString[0]; + release octString[0]; + release hexString[0]; + release decStringC[0]; + release decStringS[0]; + release decStringI[0]; + release decStringQ[0]; +`ifndef IVERILOG + release packed4dC[-3][2][-1][2][-1]; + release packed4dS[-3][2][-1][2][-3]; + release packed4dI[-3][2][-1][2][-7]; + release packed4dQ[-3][2][-1][2][-15]; + release packed4dW[-3][2][-1][2][-31]; + + release ascPacked4dC[-3][2][-1][5][9]; + release ascPacked4dS[-3][2][-1][5][11]; + release ascPacked4dI[-3][2][-1][5][15]; + release ascPacked4dQ[-3][2][-1][5][23]; + release ascPacked4dW[-3][2][-1][5][39]; +`endif + + release intvalContinuously[0]; + release vectorCContinuously[0]; + release vectorQContinuously[0]; + release vectorWContinuously[0]; + release textHalfContinuously[0]; + release textLongContinuously[1]; + release textContinuously[3]; + release binStringContinuously[0]; + release octStringContinuously[0]; + release hexStringContinuously[0]; + release decStringCContinuously[0]; + release decStringSContinuously[0]; + release decStringIContinuously[0]; + release decStringQContinuously[0]; +`ifndef IVERILOG + release packed4dCContinuously[-3][2][-1][2][-1]; + release packed4dSContinuously[-3][2][-1][2][-3]; + release packed4dIContinuously[-3][2][-1][2][-7]; + release packed4dQContinuously[-3][2][-1][2][-15]; + release packed4dWContinuously[-3][2][-1][2][-31]; + + release ascPacked4dCContinuously[-3][2][-1][5][9]; + release ascPacked4dSContinuously[-3][2][-1][5][11]; + release ascPacked4dIContinuously[-3][2][-1][5][15]; + release ascPacked4dQContinuously[-3][2][-1][5][23]; + release ascPacked4dWContinuously[-3][2][-1][5][39]; +`endif + endtask + + task automatic vpiReleaseSingleBit(BitIndexingMethod bitIndexingMethod, DimIndexingMethod dimIndexingMethod); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("releaseSingleBit(",bitIndexingMethod,",",dimIndexingMethod,")"); +`else + vpiStatus = releaseSingleBit(bitIndexingMethod, dimIndexingMethod); +`endif +`elsif IVERILOG + vpiStatus = $releaseSingleBit(bitIndexingMethod, dimIndexingMethod); +`elsif USE_VPI_NOT_DPI + vpiStatus = $releaseSingleBit(bitIndexingMethod, dimIndexingMethod); +`else + vpiStatus = releaseSingleBit(bitIndexingMethod, dimIndexingMethod); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $write("C Test failed (could not release single bit value using "); + if(dimIndexingMethod == DimByRepeatedIndex) begin + $write("vpi_handle_by_index"); + end else if (dimIndexingMethod == DimByMultiIndex) begin + $write("vpi_handle_by_multi_index"); + end else begin + $write("vpi_handle_by_name"); + end + $write(" dimension indexing and "); + if (bitIndexingMethod == BitByIndex) begin + $write("vpi_handle_by_index"); + end else begin + $write("vpi_handle_by_name"); + end + $write(" bit indexing"); + $display(")"); + $stop; + end + endtask + + // Release the *entire* signal, whereas vpiReleaseSingleBit only releases + // the single bit that was forced + task automatic vpiReleaseSingleBitForcedValues(DimIndexingMethod dimIndexingMethod); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("releaseSingleBitForcedValues(",dimIndexingMethod,")"); +`else + vpiStatus = releaseSingleBitForcedValues(dimIndexingMethod); +`endif +`elsif IVERILOG + vpiStatus = $releaseSingleBitForcedValues(dimIndexingMethod); +`elsif USE_VPI_NOT_DPI + vpiStatus = $releaseSingleBitForcedValues(dimIndexingMethod); +`else + vpiStatus = releaseSingleBitForcedValues(dimIndexingMethod); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + if (dimIndexingMethod == DimByRepeatedIndex) begin + $display("C Test failed (could not use vpi_handle_by_index to release values that had single bit forced)"); + end else if (dimIndexingMethod == DimByMultiIndex) begin + $display("C Test failed (could not use vpi_handle_by_multi_index to release values that had single bit forced)"); + end else begin + $display("C Test failed (could not use vpi_handle_by_name to release values that had single bit forced)"); + end $stop; end endtask task automatic svCheckValuesForced(); - `checkh(onebit, 0); - `checkh(intval, 32'h55555555); - `checkh(vectorC, 8'h55); - `checkh(vectorQ, 62'h15555555_55555555); - `checkh(vectorW, 128'h55555555_55555555_55555555_55555555); - `checkr(real1, 123456.789); - `checks(textHalf, "T2"); - `checks(textLong, "44Four44"); - `checks(text, "lorem ipsum"); - `checkh(binString, 8'b01010101); - `checkh(octString, 15'o52525); - `checkh(hexString, 64'h5555555555555555); - `checkh(decStringC, 8'h55); - `checkh(decStringS, 16'h5555); - `checkh(decStringI, 32'h55555555); - `checkh(decStringQ, 64'd6148914691236517205); + svCheckNonContinuousValuesForced(); `checkh(onebitContinuously, 0); `checkh(intvalContinuously, 32'h55555555); @@ -487,7 +1230,7 @@ module Test ( `checkh(vectorQContinuously, 62'h15555555_55555555); `checkh(vectorWContinuously, 128'h55555555_55555555_55555555_55555555); `checkr(real1Continuously, 123456.789); - `checks(textHalfContinuously, "T2"); + `checks(textHalfContinuously, "T3"); `checks(textLongContinuously, "44Four44"); `checks(textContinuously, "lorem ipsum"); `checkh(binStringContinuously, 8'b01010101); @@ -497,6 +1240,30 @@ module Test ( `checkh(decStringSContinuously, 16'h5555); `checkh(decStringIContinuously, 32'h55555555); `checkh(decStringQContinuously, 64'd6148914691236517205); +`ifndef IVERILOG + `checkh(packed2dCContinuously, 8'h55); + `checkh(packed2dSContinuously, 16'h5555); + `checkh(packed2dIContinuously, 24'h555555); + `checkh(packed2dQContinuously, 64'h55555555_55555555); + `checkh(packed2dWContinuously, 128'h55555555_55555555_55555555_55555555); + `checkh(packed3dSContinuously, 16'hAA55); + `checkh(packed3dIContinuously, 32'hAAAA5555); + `checkh(packed3dQContinuously, 48'hAAAAAA_555555); + `checkh(packed3dWContinuously, 128'hAAAAAAAA_AAAAAAAA_55555555_55555555); + + // Forced only element 2 of the [3:0] dimension + `checkh(packed4dCContinuously, 8'b10_01_10_10); + `checkh(packed4dSContinuously, 16'hA_5_A_A); + `checkh(packed4dIContinuously, 32'hAA_55_AA_AA); + `checkh(packed4dQContinuously, 64'hAAAA_5555_AAAA_AAAA); + `checkh(packed4dWContinuously, 128'hAAAAAAAA_55555555_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dCContinuously, 8'b10_01_10_10); + `checkh(ascPacked4dSContinuously, 16'hA_5_A_A); + `checkh(ascPacked4dIContinuously, 32'hAA_55_AA_AA); + `checkh(ascPacked4dQContinuously, 64'hAAAA_5555_AAAA_AAAA); + `checkh(ascPacked4dWContinuously, 128'hAAAAAAAA_55555555_AAAAAAAA_AAAAAAAA); +`endif endtask task automatic vpiCheckValuesForced(); @@ -529,7 +1296,7 @@ module Test ( `checkh(vectorQ, 62'h15555555_55555555); `checkh(vectorW, 128'h55555555_55555555_55555555_55555555); `checkr(real1, 123456.789); - `checks(textHalf, "T2"); + `checks(textHalf, "T3"); `checks(textLong, "44Four44"); `checks(text, "lorem ipsum"); `checkh(binString, 8'b01010101); @@ -539,6 +1306,28 @@ module Test ( `checkh(decStringS, 16'h5555); `checkh(decStringI, 32'h55555555); `checkh(decStringQ, 64'd6148914691236517205); +`ifndef IVERILOG + `checkh(packed2dC, 8'h55); + `checkh(packed2dS, 16'h5555); + `checkh(packed2dI, 24'h555555); + `checkh(packed2dQ, 64'h55555555_55555555); + `checkh(packed2dW, 128'h55555555_55555555_55555555_55555555); + `checkh(packed3dS, 16'hAA55); + `checkh(packed3dI, 32'hAAAA5555); + `checkh(packed3dQ, 48'hAAAAAA_555555); + `checkh(packed3dW, 128'hAAAAAAAA_AAAAAAAA_55555555_55555555); + `checkh(packed4dC, 8'b10_01_10_10); + `checkh(packed4dS, 16'hA_5_A_A); + `checkh(packed4dI, 32'hAA_55_AA_AA); + `checkh(packed4dQ, 64'hAAAA_5555_AAAA_AAAA); + `checkh(packed4dW, 128'hAAAAAAAA_55555555_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dC, 8'b10_01_10_10); + `checkh(ascPacked4dS, 16'hA_5_A_A); + `checkh(ascPacked4dI, 32'hAA_55_AA_AA); + `checkh(ascPacked4dQ, 64'hAAAA_5555_AAAA_AAAA); + `checkh(ascPacked4dW, 128'hAAAAAAAA_55555555_AAAAAAAA_AAAAAAAA); +`endif endtask // Check that the values *after releasing* still have the forced value @@ -582,6 +1371,28 @@ module Test ( `checkh(decStringSContinuously, 16'hAAAA); `checkh(decStringIContinuously, 32'hAAAAAAAA); `checkh(decStringQContinuously, 64'd12297829382473034410); +`ifndef IVERILOG + `checkh(packed2dCContinuously, 8'hAA); + `checkh(packed2dSContinuously, 16'hAAAA); + `checkh(packed2dIContinuously, 24'hAAAAAA); + `checkh(packed2dQContinuously, 64'hAAAAAAAA_AAAAAAAA); + `checkh(packed2dWContinuously, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); + `checkh(packed3dSContinuously, 16'hAAAA); + `checkh(packed3dIContinuously, 32'hAAAAAAAA); + `checkh(packed3dQContinuously, 48'hAAAAAA_AAAAAA); + `checkh(packed3dWContinuously, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); + `checkh(packed4dCContinuously, 8'hAA); + `checkh(packed4dSContinuously, 16'hAAAA); + `checkh(packed4dIContinuously, 32'hAAAAAAAA); + `checkh(packed4dQContinuously, 64'hAAAAAAAA_AAAAAAAA); + `checkh(packed4dWContinuously, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dCContinuously, 8'hAA); + `checkh(ascPacked4dSContinuously, 16'hAAAA); + `checkh(ascPacked4dIContinuously, 32'hAAAAAAAA); + `checkh(ascPacked4dQContinuously, 64'hAAAAAAAA_AAAAAAAA); + `checkh(ascPacked4dWContinuously, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); +`endif endtask task automatic vpiCheckContinuousValuesReleased(); @@ -608,28 +1419,15 @@ module Test ( endtask task automatic svCheckValuesPartiallyForced(); - `checkh(intval, 32'hAAAA_5555); - `checkh(vectorC, 8'h A5); - `checkh(vectorQ, 62'h2AAAAAAAD5555555); - `checkh(vectorW, 128'hAAAAAAAA_AAAAAAAA_55555555_55555555); - `checks(textHalf, "H2"); - `checks(textLong, "Lonur44"); - `checks(text, "Verilog Tesem ipsum"); - `checkh(binString, 8'b1010_0101); - `checkh(octString, 15'b01010101_1010101); - `checkh(hexString, 64'hAAAAAAAA_55555555); - `checkh(decStringC, 8'hA5); - `checkh(decStringS, 16'hAA55); - `checkh(decStringI, 32'hAAAA_5555); - `checkh(decStringQ, 64'hAAAAAAAA_55555555); + svCheckNonContinuousValuesPartiallyForced(); `checkh(intvalContinuously, 32'hAAAA_5555); `checkh(vectorCContinuously, 8'h A5); `checkh(vectorQContinuously, 62'h2AAAAAAAD5555555); `checkh(vectorWContinuously, 128'hAAAAAAAA_AAAAAAAA_55555555_55555555); - `checks(textHalfContinuously, "H2"); + `checks(textHalfContinuously, "H3"); `checks(textLongContinuously, "Lonur44"); - `checks(textContinuously, "Verilog Tesem ipsum"); + `checks(textContinuously, "Verilog Torem ipsum"); `checkh(binStringContinuously, 8'b1010_0101); `checkh(octStringContinuously, 15'b01010101_1010101); `checkh(hexStringContinuously, 64'hAAAAAAAA_55555555); @@ -637,6 +1435,57 @@ module Test ( `checkh(decStringSContinuously, 16'hAA55); `checkh(decStringIContinuously, 32'hAAAA_5555); `checkh(decStringQContinuously, 64'hAAAAAAAA_55555555); + +`ifndef XRUN // Xcelium does not support a syntax like "signal[1][0][1:0]" in vpi_handle_by_name +`ifndef IVERILOG + // Forced only the lower bits of element 2 of the [3:0] dimension + `checkh(packed4dCContinuously, 8'b10_11_10_10); + `checkh(packed4dSContinuously, 16'b1010_1001_1010_1010); + `checkh(packed4dIContinuously, 32'hAA_A5_AA_AA); + `checkh(packed4dQContinuously, 64'hAAAA_AA55_AAAA_AAAA); + `checkh(packed4dWContinuously, 128'hAAAAAAAA_AAAA5555_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dCContinuously, 8'b10_11_10_10); + `checkh(ascPacked4dSContinuously, 16'b1010_1001_1010_1010); + `checkh(ascPacked4dIContinuously, 32'hAA_A5_AA_AA); + `checkh(ascPacked4dQContinuously, 64'hAAAA_AA55_AAAA_AAAA); + `checkh(ascPacked4dWContinuously, 128'hAAAAAAAA_AAAA5555_AAAAAAAA_AAAAAAAA); +`endif +`endif + endtask + + task automatic svCheckSingleBitForced(); + svCheckNonContinuousSingleBitForced(); + + `checkh(intvalContinuously, 32'hAAAAAAAB); + `checkh(vectorCContinuously, 8'hAB); + `checkh(vectorQContinuously, 62'h2AAAAAAA_AAAAAAAB); + `checkh(vectorWContinuously, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAB); + `checks(textHalfContinuously, "Hg"); + `checks(textLongContinuously, "Long64`"); + `checks(textContinuously, "Verilog Test modulm"); + `checkh(binStringContinuously, 8'b10101011); + `checkh(octStringContinuously, 15'o25253); + `checkh(hexStringContinuously, 64'hAAAAAAAAAAAAAAAB); + `checkh(decStringCContinuously, 8'hAB); + `checkh(decStringSContinuously, 16'hAAAB); + `checkh(decStringIContinuously, 32'hAAAAAAAB); + `checkh(decStringQContinuously, 64'd12297829382473034411); + +`ifndef IVERILOG + // Forced only the LSB of element 2 of the [3:0] dimension + `checkh(packed4dCContinuously, 8'b10_11_10_10); + `checkh(packed4dSContinuously, 16'hA_B_A_A); + `checkh(packed4dIContinuously, 32'hAA_AB_AA_AA); + `checkh(packed4dQContinuously, 64'hAAAA_AAAB_AAAA_AAAA); + `checkh(packed4dWContinuously, 128'hAAAAAAAA_AAAAAAAB_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dCContinuously, 8'b10_11_10_10); + `checkh(ascPacked4dSContinuously, 16'hA_B_A_A); + `checkh(ascPacked4dIContinuously, 32'hAA_AB_AA_AA); + `checkh(ascPacked4dQContinuously, 64'hAAAA_AAAB_AAAA_AAAA); + `checkh(ascPacked4dWContinuously, 128'hAAAAAAAA_AAAAAAAB_AAAAAAAA_AAAAAAAA); +`endif endtask task automatic vpiCheckValuesPartiallyForced(); @@ -662,14 +1511,37 @@ module Test ( end endtask + task automatic vpiCheckSingleBitForced(); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("checkSingleBitForced()"); +`else + vpiStatus = checkSingleBitForced(); +`endif +`elsif IVERILOG + vpiStatus = $checkSingleBitForced; +`elsif USE_VPI_NOT_DPI + vpiStatus = $checkSingleBitForced; +`else + vpiStatus = checkSingleBitForced(); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $display("C Test failed (value of after forcing single bit does not match expectation)"); + $stop; + end + endtask + task automatic svCheckNonContinuousValuesPartiallyForced(); `checkh(intval, 32'hAAAA_5555); `checkh(vectorC, 8'h A5); `checkh(vectorQ, 62'h2AAAAAAAD5555555); `checkh(vectorW, 128'hAAAAAAAA_AAAAAAAA_55555555_55555555); - `checks(textHalf, "H2"); + `checks(textHalf, "H3"); `checks(textLong, "Lonur44"); - `checks(text, "Verilog Tesem ipsum"); + `checks(text, "Verilog Torem ipsum"); `checkh(binString, 8'b1010_0101); `checkh(octString, 15'b01010101_1010101); `checkh(hexString, 64'hAAAAAAAA_55555555); @@ -677,6 +1549,23 @@ module Test ( `checkh(decStringS, 16'hAA55); `checkh(decStringI, 32'hAAAA_5555); `checkh(decStringQ, 64'hAAAAAAAA_55555555); + +`ifndef IVERILOG +`ifndef XRUN + // Forced only the lower bits of element 2 of the [3:0] dimension + `checkh(packed4dC, 8'b10_11_10_10); + `checkh(packed4dS, 16'b1010_1001_1010_1010); + `checkh(packed4dI, 32'hAA_A5_AA_AA); + `checkh(packed4dQ, 64'hAAAA_AA55_AAAA_AAAA); + `checkh(packed4dW, 128'hAAAAAAAA_AAAA5555_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dC, 8'b10_11_10_10); + `checkh(ascPacked4dS, 16'b1010_1001_1010_1010); + `checkh(ascPacked4dI, 32'hAA_A5_AA_AA); + `checkh(ascPacked4dQ, 64'hAAAA_AA55_AAAA_AAAA); + `checkh(ascPacked4dW, 128'hAAAAAAAA_AAAA5555_AAAAAAAA_AAAAAAAA); +`endif +`endif endtask // Check that the values *after releasing* still have the partially forced value @@ -703,6 +1592,61 @@ module Test ( end endtask + task automatic svCheckNonContinuousSingleBitForced(); + `checkh(intval, 32'hAAAAAAAB); + `checkh(vectorC, 8'hAB); + `checkh(vectorQ, 62'h2AAAAAAA_AAAAAAAB); + `checkh(vectorW, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAB); + `checks(textHalf, "Hg"); + `checks(textLong, "Long64`"); + `checks(text, "Verilog Test modulm"); + `checkh(binString, 8'b10101011); + `checkh(octString, 15'o25253); + `checkh(hexString, 64'hAAAAAAAAAAAAAAAB); + `checkh(decStringC, 8'hAB); + `checkh(decStringS, 16'hAAAB); + `checkh(decStringI, 32'hAAAAAAAB); + `checkh(decStringQ, 64'd12297829382473034411); + +`ifndef IVERILOG + // Forced only the LSB of element 2 of the [3:0] dimension + `checkh(packed4dC, 8'b10_11_10_10); + `checkh(packed4dS, 16'hA_B_A_A); + `checkh(packed4dI, 32'hAA_AB_AA_AA); + `checkh(packed4dQ, 64'hAAAA_AAAB_AAAA_AAAA); + `checkh(packed4dW, 128'hAAAAAAAA_AAAAAAAB_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dC, 8'b10_11_10_10); + `checkh(ascPacked4dS, 16'hA_B_A_A); + `checkh(ascPacked4dI, 32'hAA_AB_AA_AA); + `checkh(ascPacked4dQ, 64'hAAAA_AAAB_AAAA_AAAA); + `checkh(ascPacked4dW, 128'hAAAAAAAA_AAAAAAAB_AAAAAAAA_AAAAAAAA); +`endif + endtask + + task automatic vpiCheckNonContinuousSingleBitForced(); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("checkNonContinuousSingleBitForced()"); +`else + vpiStatus = checkNonContinuousSingleBitForced(); +`endif +`elsif IVERILOG + vpiStatus = $checkNonContinuousSingleBitForced; +`elsif USE_VPI_NOT_DPI + vpiStatus = $checkNonContinuousSingleBitForced; +`else + vpiStatus = checkNonContinuousSingleBitForced(); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $display("C Test failed (value of continuously assigned signal after releasing single bit does not match expectation)"); + $stop; + end + endtask + task automatic svCheckValuesReleased(); `checkh(onebit, 1); `checkh(intval, 32'hAAAAAAAA); @@ -720,23 +1664,30 @@ module Test ( `checkh(decStringS, 16'hAAAA); `checkh(decStringI, 32'hAAAAAAAA); `checkh(decStringQ, 64'd12297829382473034410); +`ifndef IVERILOG + `checkh(packed2dC, 8'hAA); + `checkh(packed2dS, 16'hAAAA); + `checkh(packed2dI, 24'hAAAAAA); + `checkh(packed2dQ, 64'hAAAAAAAA_AAAAAAAA); + `checkh(packed2dW, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); + `checkh(packed3dS, 16'hAAAA); + `checkh(packed3dI, 32'hAAAAAAAA); + `checkh(packed3dQ, 48'hAAAAAA_AAAAAA); + `checkh(packed3dW, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); + `checkh(packed4dC, 8'hAA); + `checkh(packed4dS, 16'hAAAA); + `checkh(packed4dI, 32'hAAAAAAAA); + `checkh(packed4dQ, 64'hAAAAAAAA_AAAAAAAA); + `checkh(packed4dW, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); - `checkh(onebitContinuously, 1); - `checkh(intvalContinuously, 32'hAAAAAAAA); - `checkh(vectorCContinuously, 8'hAA); - `checkh(vectorQContinuously, 62'h2AAAAAAA_AAAAAAAA); - `checkh(vectorWContinuously, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); - `checkr(real1Continuously, 1.0); - `checks(textHalfContinuously, "Hf"); - `checks(textLongContinuously, "Long64b"); - `checks(textContinuously, "Verilog Test module"); - `checkh(binStringContinuously, 8'b10101010); - `checkh(octStringContinuously, 15'o25252); - `checkh(hexStringContinuously, 64'hAAAAAAAAAAAAAAAA); - `checkh(decStringCContinuously, 8'hAA); - `checkh(decStringSContinuously, 16'hAAAA); - `checkh(decStringIContinuously, 32'hAAAAAAAA); - `checkh(decStringQContinuously, 64'd12297829382473034410); + `checkh(ascPacked4dC, 8'hAA); + `checkh(ascPacked4dS, 16'hAAAA); + `checkh(ascPacked4dI, 32'hAAAAAAAA); + `checkh(ascPacked4dQ, 64'hAAAAAAAA_AAAAAAAA); + `checkh(ascPacked4dW, 128'hAAAAAAAA_AAAAAAAA_AAAAAAAA_AAAAAAAA); +`endif + + svCheckContinuousValuesReleased(); endtask task automatic vpiCheckValuesReleased(); @@ -762,6 +1713,240 @@ module Test ( end endtask + task automatic svCheckValuesPartiallyReleased(); + `checkh(intval, 'h5555aaaa); + `checkh(vectorC, 'h5a); + `checkh(vectorQ, 'h155555552aaaaaaa); + `checkh(vectorW, 'h5555555555555555aaaaaaaaaaaaaaaa); + `checks(textHalf, "Tf"); + `checks(textLong, "44Fog64b"); + `checks(text, "lest module"); + `checkh(octString,'h552a); + `checkh(hexString,'h55555555aaaaaaaa); + `checkh(decStringC,'h5a); + `checkh(decStringS,'h55aa); + `checkh(decStringI,'h5555aaaa); + `checkh(decStringQ,'h55555555aaaaaaaa); + +`ifndef IVERILOG +`ifndef XRUN + // Only element 2 of the [3:0] dimension was forced, then only the lower + // bits of that element were released + `checkh(packed4dC, 8'b10_00_10_10); + `checkh(packed4dS, 16'b1010_0110_1010_1010); + `checkh(packed4dI, 32'hAA_5A_AA_AA); + `checkh(packed4dQ, 64'hAAAA_55AA_AAAA_AAAA); + `checkh(packed4dW, 128'hAAAAAAAA_5555AAAA_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dC, 8'b10_00_10_10); + `checkh(ascPacked4dS, 16'b1010_0110_1010_1010); + `checkh(ascPacked4dI, 32'hAA_5A_AA_AA); + `checkh(ascPacked4dQ, 64'hAAAA_55AA_AAAA_AAAA); + `checkh(ascPacked4dW, 128'hAAAAAAAA_5555AAAA_AAAAAAAA_AAAAAAAA); +`endif +`endif + + svCheckContinuousValuesPartiallyReleased(); + endtask + + task automatic svCheckContinuousValuesPartiallyReleased(); + `checkh(intvalContinuously, 'h5555aaaa); + `checkh(vectorCContinuously, 'h5a); + `checkh(vectorQContinuously, 'h155555552aaaaaaa); + `checkh(vectorWContinuously, 'h5555555555555555aaaaaaaaaaaaaaaa); + `checks(textHalfContinuously, "Tf"); + `checks(textLongContinuously, "44Fog64b"); + `checks(textContinuously, "lest module"); + `checkh(octStringContinuously,'h552a); + `checkh(hexStringContinuously,'h55555555aaaaaaaa); + `checkh(decStringCContinuously,'h5a); + `checkh(decStringSContinuously,'h55aa); + `checkh(decStringIContinuously,'h5555aaaa); + `checkh(decStringQContinuously,'h55555555aaaaaaaa); + +`ifndef IVERILOG +`ifndef XRUN + // Only element 2 of the [3:0] dimension was forced, then only the lower + // bits of that element were released + `checkh(packed4dCContinuously, 8'b10_00_10_10); + `checkh(packed4dSContinuously, 16'b1010_0110_1010_1010); + `checkh(packed4dIContinuously, 32'hAA_5A_AA_AA); + `checkh(packed4dQContinuously, 64'hAAAA_55AA_AAAA_AAAA); + `checkh(packed4dWContinuously, 128'hAAAAAAAA_5555AAAA_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dCContinuously, 8'b10_00_10_10); + `checkh(ascPacked4dSContinuously, 16'b1010_0110_1010_1010); + `checkh(ascPacked4dIContinuously, 32'hAA_5A_AA_AA); + `checkh(ascPacked4dQContinuously, 64'hAAAA_55AA_AAAA_AAAA); + `checkh(ascPacked4dWContinuously, 128'hAAAAAAAA_5555AAAA_AAAAAAAA_AAAAAAAA); +`endif +`endif + endtask + + task automatic vpiCheckValuesPartiallyReleased(); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("checkValuesPartiallyReleased()"); +`else + vpiStatus = checkValuesPartiallyReleased(); +`endif +`elsif IVERILOG + vpiStatus = $checkValuesPartiallyReleased; +`elsif USE_VPI_NOT_DPI + vpiStatus = $checkValuesPartiallyReleased; +`else + vpiStatus = checkValuesPartiallyReleased(); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $display("C Test failed (value after partial releasing does not match expectation)"); + $stop; + end + endtask + + task automatic vpiCheckContinuousValuesPartiallyReleased(); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("checkContinuousValuesPartiallyReleased()"); +`else + vpiStatus = checkContinuousValuesPartiallyReleased(); +`endif +`elsif IVERILOG + vpiStatus = $checkContinuousValuesPartiallyReleased; +`elsif USE_VPI_NOT_DPI + vpiStatus = $checkContinuousValuesPartiallyReleased; +`else + vpiStatus = checkContinuousValuesPartiallyReleased(); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $display("C Test failed (value of continuously assigned signal after partial releasing does not match expectation)"); + $stop; + end + endtask + + task automatic svCheckContinuousValuesSingleBitReleased(); + `checkh(intvalContinuously, 32'h55555554); + `checkh(vectorCContinuously, 8'h54); + `checkh(vectorQContinuously, 62'h15555555_55555554); + `checkh(vectorWContinuously, 128'h55555555_55555555_55555555_55555554); + `checks(textHalfContinuously, "T2"); + `checks(textLongContinuously, "44Four46"); + `checks(textContinuously, "lorem ipsue"); + `checkh(binStringContinuously, 8'b01010100); + `checkh(octStringContinuously, 15'o52524); + `checkh(hexStringContinuously, 64'h5555555555555554); + `checkh(decStringCContinuously, 8'h54); + `checkh(decStringSContinuously, 16'h5554); + `checkh(decStringIContinuously, 32'h55555554); + `checkh(decStringQContinuously, 64'd6148914691236517204); + +`ifndef IVERILOG + // Only element 2 of the [3:0] dimension was forced, then only the LSB + // of that element was released + `checkh(packed4dCContinuously, 8'b10_00_10_10); + `checkh(packed4dSContinuously, 16'b1010_0100_1010_1010); + `checkh(packed4dIContinuously, 32'hAA_54_AA_AA); + `checkh(packed4dQContinuously, 64'hAAAA_5554_AAAA_AAAA); + `checkh(packed4dWContinuously, 128'hAAAAAAAA_55555554_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dCContinuously, 8'b10_00_10_10); + `checkh(ascPacked4dSContinuously, 16'b1010_0100_1010_1010); + `checkh(ascPacked4dIContinuously, 32'hAA_54_AA_AA); + `checkh(ascPacked4dQContinuously, 64'hAAAA_5554_AAAA_AAAA); + `checkh(ascPacked4dWContinuously, 128'hAAAAAAAA_55555554_AAAAAAAA_AAAAAAAA); +`endif + endtask + + task automatic vpiCheckContinuousValuesSingleBitReleased(); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("checkContinuousValuesSingleBitReleased()"); +`else + vpiStatus = checkContinuousValuesSingleBitReleased(); +`endif +`elsif IVERILOG + vpiStatus = $checkContinuousValuesSingleBitReleased; +`elsif USE_VPI_NOT_DPI + vpiStatus = $checkContinuousValuesSingleBitReleased; +`else + vpiStatus = checkContinuousValuesSingleBitReleased(); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $display("C Test failed (value of continuously assigned signal after releasing single bit does not match expectation)"); + $stop; + end + endtask + + task automatic svCheckSingleBitReleased(); + `checkh(intval, 32'h55555554); + `checkh(vectorC, 8'h54); + `checkh(vectorQ, 62'h15555555_55555554); + `checkh(vectorW, 128'h55555555_55555555_55555555_55555554); + `checks(textHalf, "T2"); + `checks(textLong, "44Four46"); + `checks(text, "lorem ipsue"); + `checkh(binString, 8'b01010100); + `checkh(octString, 15'o52524); + `checkh(hexString, 64'h5555555555555554); + `checkh(decStringC, 8'h54); + `checkh(decStringS, 16'h5554); + `checkh(decStringI, 32'h55555554); + `checkh(decStringQ, 64'd6148914691236517204); + +`ifndef IVERILOG + // Only element 2 of the [3:0] dimension was forced, then only the LSB + // of that element was released + `checkh(packed4dC, 8'b10_00_10_10); + `checkh(packed4dS, 16'b1010_0100_1010_1010); + `checkh(packed4dI, 32'hAA_54_AA_AA); + `checkh(packed4dQ, 64'hAAAA_5554_AAAA_AAAA); + `checkh(packed4dW, 128'hAAAAAAAA_55555554_AAAAAAAA_AAAAAAAA); + + `checkh(ascPacked4dC, 8'b10_00_10_10); + `checkh(ascPacked4dS, 16'b1010_0100_1010_1010); + `checkh(ascPacked4dI, 32'hAA_54_AA_AA); + `checkh(ascPacked4dQ, 64'hAAAA_5554_AAAA_AAAA); + `checkh(ascPacked4dW, 128'hAAAAAAAA_55555554_AAAAAAAA_AAAAAAAA); +`endif + + svCheckContinuousValuesSingleBitReleased(); + endtask + + task automatic vpiCheckSingleBitReleased(); + integer vpiStatus = 1; +`ifdef VERILATOR +`ifdef USE_VPI_NOT_DPI + vpiStatus = $c32("checkSingleBitReleased()"); +`else + vpiStatus = checkSingleBitReleased(); +`endif +`elsif IVERILOG + vpiStatus = $checkSingleBitReleased; +`elsif USE_VPI_NOT_DPI + vpiStatus = $checkSingleBitReleased; +`else + vpiStatus = checkSingleBitReleased(); +`endif + + if (vpiStatus != 0) begin + $write("%%Error: t_vpi_force.cpp:%0d:", vpiStatus); + $display("C Test failed (value after releasing single bit does not match expectation)"); + $stop; + end + endtask + + BitIndexingMethod bitIndexingMethod; + DimIndexingMethod forceDimIndexingMethod; + DimIndexingMethod releaseDimIndexingMethod; + initial begin `ifdef WAVES $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); @@ -771,6 +1956,7 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); `ifdef VERILATOR vpiPutString(); vpiTryInvalidPutOperations(); + svReleaseValues(); // Reset any forced values from the invalid put test vpiPutInertialDelay(); #1 vpiCheckInertialDelay(); // Force and check non-public, but forceable signal @@ -780,44 +1966,83 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); #8 `checkh(nonPublic, 1); `endif + forceDimIndexingMethod = forceDimIndexingMethod.first(); + // Force through VPI, release through VPI - vpiForceValues(); - // Time delay to ensure setting and checking values does not happen - // at the same time, so that the signals can have their values overwritten - // by other processes - #8 vpiCheckValuesForced(); - svCheckValuesForced(); - // Wait until negedge, then release - @(negedge clk) vpiReleaseValues(); - // After release, but before posedge: Non-continuously assigned signals - // should still have their forced value, because the posedge re-assigning - // the non-continuously assigned signals has not happened yet, but - // continuously assigned signals should have their non-forced value again - #1 vpiCheckNonContinuousValuesForced(); - vpiCheckContinuousValuesReleased(); - svCheckNonContinuousValuesForced(); - svCheckContinuousValuesReleased(); - #8; // All signals should be released by now - vpiCheckValuesReleased(); - svCheckValuesReleased(); + if (`verbose) $display("*** Forcing through VPI, releasing through VPI ***"); + do begin + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing with %s, releasing with %s", forceDimIndexingMethod.name(), releaseDimIndexingMethod.name()); + vpiForceValues(forceDimIndexingMethod); + // Time delay to ensure setting and checking values does not happen + // at the same time, so that the signals can have their values overwritten + // by other processes + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + // Wait until negedge, then release + @(negedge clk) vpiReleaseValues(releaseDimIndexingMethod); + // After release, but before posedge: Non-continuously assigned signals + // should still have their forced value, because the posedge re-assigning + // the non-continuously assigned signals has not happened yet, but + // continuously assigned signals should have their non-forced value again + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesReleased(); + #8; // All signals should be released by now + vpiCheckValuesReleased(); + svCheckValuesReleased(); + + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); // Force through VPI, release through Verilog - #8 vpiForceValues(); - #8 vpiCheckValuesForced(); - svCheckValuesForced(); - @(negedge clk) svReleaseValues(); - #1 vpiCheckNonContinuousValuesForced(); - vpiCheckContinuousValuesReleased(); - svCheckNonContinuousValuesForced(); - svCheckContinuousValuesReleased(); - #8 vpiCheckValuesReleased(); - svCheckValuesReleased(); + if (`verbose) $display("*** Forcing through VPI, releasing through Verilog ***"); + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing with %s, releasing through Verilog", forceDimIndexingMethod.name()); + #8 vpiForceValues(forceDimIndexingMethod); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) svReleaseValues(); + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); // Force through Verilog, release through VPI + if (`verbose) $display("*** Forcing through Verilog, releasing through VPI ***"); + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing through Verilog, releasing with %s", releaseDimIndexingMethod.name()); + #8 svForceValues(); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) vpiReleaseValues(releaseDimIndexingMethod); + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + + // Force through Verilog, release through Verilog (but still check value through + // VPI) + if (`verbose) $display("*** Forcing through Verilog, releasing through Verilog ***"); #8 svForceValues(); #8 vpiCheckValuesForced(); svCheckValuesForced(); - @(negedge clk) vpiReleaseValues(); + @(negedge clk) svReleaseValues(); #1 vpiCheckNonContinuousValuesForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesForced(); @@ -825,12 +2050,39 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); #8 vpiCheckValuesReleased(); svCheckValuesReleased(); - // Force only some bits, check if __VforceRd yields correct signal, - // release through VPI - #8 svPartiallyForceValues(); + // Partial forcing tests obtain partial handles through + // vpi_handle_by_name("signalName[hi:lo]", nullptr), which is not + // supported by Icarus +`ifndef IVERILOG + // Execute for both ascending and descending bit ranges + + // Descending + if (`verbose) $display("*** Testing partial forcing with descending bit ranges ***"); + + // Partially force through VPI, release through VPI + if (`verbose) $display("*** Partially forcing through VPI, releasing through VPI ***"); + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Partially forcing with through VPI, releasing with %s", releaseDimIndexingMethod.name()); + #8 vpiPartiallyForceValues(Descending); + #8 vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(negedge clk) vpiReleasePartiallyForcedValues(releaseDimIndexingMethod); + #1 vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + + // Partially force through VPI, release through Verilog + if (`verbose) $display("*** Partially forcing through VPI, releasing through Verilog ***"); + #8 vpiPartiallyForceValues(Descending); #8 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); - @(negedge clk) vpiReleasePartiallyForcedValues(); + @(negedge clk) svReleaseValues(); #1 vpiCheckNonContinuousValuesPartiallyForced(); vpiCheckContinuousValuesReleased(); svCheckNonContinuousValuesPartiallyForced(); @@ -838,8 +2090,64 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); #8 vpiCheckValuesReleased(); svCheckValuesReleased(); - // Force only some bits, check if __VforceRd yields correct signal, - // release through Verilog + // Xrun doesn't support ascending bit ranges in vpi_handle_by_name +`ifndef XRUN + if (`verbose) $display("*** Testing partial forcing with ascending bit ranges ***"); + + // Partially force through VPI, release through VPI + if (`verbose) $display("*** Partially forcing through VPI, releasing through VPI ***"); + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Partially forcing through VPI, releasing with %s", releaseDimIndexingMethod.name()); + #8 vpiPartiallyForceValues(Ascending); + #8 vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(negedge clk) vpiReleasePartiallyForcedValues(releaseDimIndexingMethod); + #1 vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + + // Partially force through VPI, release through Verilog + if (`verbose) $display("*** Partially forcing through VPI, releasing through Verilog ***"); + #8 vpiPartiallyForceValues(Ascending); + #8 vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(negedge clk) svReleaseValues(); + #1 vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); +`endif + +`endif + + // Partially force through Verilog, release through VPI + if (`verbose) $display("*** Partially forcing through Verilog, releasing through VPI ***"); + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Partially forcing through Verilog, releasing with %s", releaseDimIndexingMethod.name()); + #8 svPartiallyForceValues(); + #8 vpiCheckValuesPartiallyForced(); + svCheckValuesPartiallyForced(); + @(negedge clk) vpiReleasePartiallyForcedValues(releaseDimIndexingMethod); + #1 vpiCheckNonContinuousValuesPartiallyForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousValuesPartiallyForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + + // Partially force through Verilog, release through Verilog + if (`verbose) $display("*** Partially forcing through Verilog, releasing through Verilog ***"); #8 svPartiallyForceValues(); #8 vpiCheckValuesPartiallyForced(); svCheckValuesPartiallyForced(); @@ -851,6 +2159,280 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); #8 vpiCheckValuesReleased(); svCheckValuesReleased(); + // Force through Verilog, partially release through Verilog + if (`verbose) $display("*** Forcing through Verilog, partially releasing through Verilog ***"); + #8 svForceValues(); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) svPartiallyReleaseValues(); + #1 vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); + #8 vpiCheckValuesPartiallyReleased(); + svCheckValuesPartiallyReleased(); + +`ifndef IVERILOG + // Execute for both ascending and descending bit ranges + + // Descending + if (`verbose) $display("*** Testing partial releasing with descending bit ranges ***"); + + // Force through VPI, partially release through VPI + if (`verbose) $display("*** Forcing through VPI, partially releasing through VPI ***"); + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing with %s, partially releasing through VPI", forceDimIndexingMethod.name()); + #8 vpiForceValues(forceDimIndexingMethod); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) vpiPartiallyReleaseValues(Descending); + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); + #8 vpiCheckValuesPartiallyReleased(); + svCheckValuesPartiallyReleased(); + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); + + // Force through Verilog, partially release through VPI + if (`verbose) $display("*** Forcing through Verilog, partially releasing through VPI ***"); + #8 svForceValues(); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) vpiPartiallyReleaseValues(Descending); + #1 vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); + #8 vpiCheckValuesPartiallyReleased(); + svCheckValuesPartiallyReleased(); + + // Ascending +`ifndef XRUN + if (`verbose) $display("*** Testing partial releasing with ascending bit ranges ***"); + + // Force through VPI, partially release through VPI + if (`verbose) $display("*** Forcing through VPI, partially releasing through VPI ***"); + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing through VPI, partially releasing with %s", forceDimIndexingMethod.name()); + #8 vpiForceValues(forceDimIndexingMethod); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) vpiPartiallyReleaseValues(Ascending); + #1 vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); + #8 vpiCheckValuesPartiallyReleased(); + svCheckValuesPartiallyReleased(); + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); + + // Force through Verilog, partially release through VPI + if (`verbose) $display("*** Forcing through Verilog, partially releasing through VPI ***"); + #8 svForceValues(); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) vpiPartiallyReleaseValues(Ascending); + #1 vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); + #8 vpiCheckValuesPartiallyReleased(); + svCheckValuesPartiallyReleased(); + +`endif + +`endif + + // Force through VPI, partially release through Verilog + if (`verbose) $display("*** Forcing through VPI, partially releasing through Verilog ***"); + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing with %s, partially releasing through Verilog", forceDimIndexingMethod.name()); + #8 vpiForceValues(forceDimIndexingMethod); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) svPartiallyReleaseValues(); + #1 vpiCheckNonContinuousValuesForced; + vpiCheckContinuousValuesPartiallyReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesPartiallyReleased(); + #8 vpiCheckValuesPartiallyReleased(); + svCheckValuesPartiallyReleased(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); + + // Release everything to reset for next test + svReleaseValues(); + #8 svCheckValuesReleased(); + + // Icarus does not support forcing single bits through VPI +`ifndef IVERILOG + // Xcelium supports forcing single bits through VPI, but crashes on some signals +`ifndef XRUN + + // Force single bit through VPI, release through VPI + if (`verbose) $display("*** Forcing single bit through VPI, releasing through VPI ***"); + bitIndexingMethod = bitIndexingMethod.first(); + do begin + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing single bit with bit indexing method %s and dimension indexing method %s, releasing with dimension indexing method %s", bitIndexingMethod.name(), forceDimIndexingMethod.name(), releaseDimIndexingMethod.name()); + #8 vpiForceSingleBit(bitIndexingMethod, forceDimIndexingMethod); + #8 vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(negedge clk) vpiReleaseSingleBitForcedValues(releaseDimIndexingMethod); + #1 vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); + bitIndexingMethod = bitIndexingMethod.next(); + end while (bitIndexingMethod != bitIndexingMethod.first()); + + // Force single bit through VPI, release through Verilog + if (`verbose) $display("*** Forcing single bit through VPI, releasing through Verilog ***"); + bitIndexingMethod = bitIndexingMethod.first(); + do begin + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing single bit with bit indexing method %s and dimension indexing method %s, releasing through Verilog", bitIndexingMethod.name(), forceDimIndexingMethod.name()); + #8 vpiForceSingleBit(bitIndexingMethod, forceDimIndexingMethod); + #8 vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(negedge clk) svReleaseValues(); + #1 vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); + bitIndexingMethod = bitIndexingMethod.next(); + end while (bitIndexingMethod != bitIndexingMethod.first()); + + // Force single bit through Verilog, release through VPI + if (`verbose) $display("*** Forcing single bit through Verilog, releasing through VPI ***"); + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing single bit through Verilog, releasing with %s", releaseDimIndexingMethod.name()); + #8 svForceSingleBit(); + #8 vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(negedge clk) vpiReleaseSingleBitForcedValues(releaseDimIndexingMethod); + #1 vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + + // Force single bit through Verilog, release through Verilog + if (`verbose) $display("*** Forcing single bit through Verilog, releasing through Verilog ***"); + #8 svForceSingleBit(); + #8 vpiCheckSingleBitForced(); + svCheckSingleBitForced(); + @(negedge clk) svReleaseValues(); + #1 vpiCheckNonContinuousSingleBitForced(); + vpiCheckContinuousValuesReleased(); + svCheckNonContinuousSingleBitForced(); + svCheckContinuousValuesReleased(); + #8 vpiCheckValuesReleased(); + svCheckValuesReleased(); + + // Force through VPI, release single bit through VPI + if (`verbose) $display("*** Forcing through VPI, releasing single bit through VPI ***"); + bitIndexingMethod = bitIndexingMethod.first(); + do begin + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing with %s, releasing single bit with bit indexing method %s and dimension indexing method %s", forceDimIndexingMethod.name(), bitIndexingMethod.name(), releaseDimIndexingMethod.name()); + #8 vpiForceValues(forceDimIndexingMethod); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) vpiReleaseSingleBit(bitIndexingMethod, releaseDimIndexingMethod); + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); + #8 vpiCheckSingleBitReleased(); + svCheckSingleBitReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); + bitIndexingMethod = bitIndexingMethod.next(); + end while (bitIndexingMethod != bitIndexingMethod.first()); + + // Force through VPI, release single bit through Verilog + if (`verbose) $display("*** Forcing through VPI, releasing single bit through Verilog ***"); + forceDimIndexingMethod = forceDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing with %s, releasing single bit through Verilog", forceDimIndexingMethod.name()); + #8 vpiForceValues(forceDimIndexingMethod); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) svReleaseSingleBit(); + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); + #8 vpiCheckSingleBitReleased(); + svCheckSingleBitReleased(); + forceDimIndexingMethod = forceDimIndexingMethod.next(); + end while (forceDimIndexingMethod != forceDimIndexingMethod.first()); + + // Force through Verilog, release single bit through VPI + if (`verbose) $display("*** Forcing through Verilog, releasing single bit through VPI ***"); + bitIndexingMethod = bitIndexingMethod.first(); + do begin + releaseDimIndexingMethod = releaseDimIndexingMethod.first(); + do begin + if (`verbose) $display("Forcing through Verilog, releasing single bit with bit indexing method %s and dimension indexing method %s", bitIndexingMethod.name(), releaseDimIndexingMethod.name()); + #8 svForceValues(); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) vpiReleaseSingleBit(bitIndexingMethod, releaseDimIndexingMethod); + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); + #8 vpiCheckSingleBitReleased(); + svCheckSingleBitReleased(); + releaseDimIndexingMethod = releaseDimIndexingMethod.next(); + end while (releaseDimIndexingMethod != releaseDimIndexingMethod.first()); + bitIndexingMethod = bitIndexingMethod.next(); + end while (bitIndexingMethod != bitIndexingMethod.first()); + + // Force through Verilog, release single bit through Verilog + if (`verbose) $display("*** Forcing through Verilog, releasing single bit through Verilog ***"); + #8 svForceValues(); + #8 vpiCheckValuesForced(); + svCheckValuesForced(); + @(negedge clk) svReleaseSingleBit(); + #1 vpiCheckNonContinuousValuesForced(); + vpiCheckContinuousValuesSingleBitReleased(); + svCheckNonContinuousValuesForced(); + svCheckContinuousValuesSingleBitReleased(); + #8 vpiCheckSingleBitReleased(); + svCheckSingleBitReleased(); +`endif +`endif #5 $display("*-* All Finished *-*"); $finish; @@ -882,6 +2464,26 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); $display("decStringI: %x", decStringI); $display("decStringQ: %x", decStringQ); + $display("packed2dC: %x", packed2dC); + $display("packed2dS: %x", packed2dS); + $display("packed2dI: %x", packed2dI); + $display("packed2dQ: %x", packed2dQ); + $display("packed2dW: %x", packed2dW); + $display("packed3dS: %x", packed3dS); + $display("packed3dI: %x", packed3dI); + $display("packed3dQ: %x", packed3dQ); + $display("packed3dW: %x", packed3dW); + $display("packed4dC: %x", packed4dC); + $display("packed4dS: %x", packed4dS); + $display("packed4dI: %x", packed4dI); + $display("packed4dQ: %x", packed4dQ); + $display("packed4dW: %x", packed4dW); + $display("ascPacked4dC: %x", ascPacked4dC); + $display("ascPacked4dS: %x", ascPacked4dS); + $display("ascPacked4dI: %x", ascPacked4dI); + $display("ascPacked4dQ: %x", ascPacked4dQ); + $display("ascPacked4dW: %x", ascPacked4dW); + $display("onebitContinuously: %x", onebitContinuously); $display("intvalContinuously: %x", intvalContinuously); $display("vectorCContinuously: %x", vectorCContinuously); @@ -899,6 +2501,26 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE)); $display("decStringIContinuously: %x", decStringIContinuously); $display("decStringQContinuously: %x", decStringQContinuously); + $display("packed2dCContinuously: %x", packed2dCContinuously); + $display("packed2dSContinuously: %x", packed2dSContinuously); + $display("packed2dIContinuously: %x", packed2dIContinuously); + $display("packed2dQContinuously: %x", packed2dQContinuously); + $display("packed2dWContinuously: %x", packed2dWContinuously); + $display("packed3dSContinuously: %x", packed3dSContinuously); + $display("packed3dIContinuously: %x", packed3dIContinuously); + $display("packed3dQContinuously: %x", packed3dQContinuously); + $display("packed3dWContinuously: %x", packed3dWContinuously); + $display("packed4dCContinuously: %x", packed4dCContinuously); + $display("packed4dSContinuously: %x", packed4dSContinuously); + $display("packed4dIContinuously: %x", packed4dIContinuously); + $display("packed4dQContinuously: %x", packed4dQContinuously); + $display("packed4dWContinuously: %x", packed4dWContinuously); + $display("ascPacked4dCContinuously: %x", ascPacked4dCContinuously); + $display("ascPacked4dSContinuously: %x", ascPacked4dSContinuously); + $display("ascPacked4dIContinuously: %x", ascPacked4dIContinuously); + $display("ascPacked4dQContinuously: %x", ascPacked4dQContinuously); + $display("ascPacked4dWContinuously: %x", ascPacked4dWContinuously); + $display("========================\n"); end `endif diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 10a0c75e8..8a8dbbcc8 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -1071,6 +1071,19 @@ int _mon_check_multi_index() { vpi_get_value(vh_3d, &v); CHECK_RESULT(v.value.integer, 7); // (1*4) + (1*2) + 1 + // 2D Packed array with negative indices: [8:-7] [3:-4] negative_multi_packed[0:-2] + TestVpiHandle vh_neg_packed_base + = vpi_handle_by_name((PLI_BYTE8*)"t.negative_multi_packed", nullptr); + CHECK_RESULT_NZ(vh_neg_packed_base); + PLI_INT32 idx_neg_packed[2] = {-1, -2}; + TestVpiHandle vh_neg_packed + = vpi_handle_by_multi_index(vh_neg_packed_base, 2, idx_neg_packed); + CHECK_RESULT_NZ(vh_neg_packed); + CHECK_RESULT(vpi_get(vpiType, vh_neg_packed), vpiReg); + CHECK_RESULT(vpi_get(vpiSize, vh_neg_packed), 8); + vpi_get_value(vh_neg_packed, &v); + CHECK_RESULT(v.value.integer, 4); + // Verify multi_index matches sequential vpi_handle_by_index TestVpiHandle vh_seq_base = vpi_handle_by_name((PLI_BYTE8*)"t.mem_2d", nullptr); CHECK_RESULT_NZ(vh_seq_base); @@ -1266,6 +1279,15 @@ int _mon_check_multi_index() { CHECK_RESULT(vpi_get(vpiSize, vh_3d), 96); vpi_get_value(vh_3d, &v); CHECK_RESULT(v.value.integer, 7); + + // Index into single bit with negative index + TestVpiHandle vh_neg_bit + = vpi_handle_by_name((PLI_BYTE8*)"t.negative_multi_packed[-1][-2][-2]", nullptr); + CHECK_RESULT_NZ(vh_neg_bit); + CHECK_RESULT(vpi_get(vpiSize, vh_neg_bit), 1); + vpi_get_value(vh_neg_bit, &v); + // Element [-1][-2] is 8'h4; elements are indexed as [3:-4], so bit -2 is 1 + CHECK_RESULT(v.value.integer, 1); } // Packed dimension indexing: quads[2] bit selection @@ -1322,6 +1344,24 @@ int _mon_check_multi_index() { CHECK_RESULT_NZ(vh_last); vpi_get_value(vh_last, &v); CHECK_RESULT(v.value.integer, 0xDD); + + // Negative indices: negative_multi_packed is defined as + // `[8:-7] [3:-4] negative_multi_packed[0:-2]` + TestVpiHandle vh_neg + = vpi_handle_by_name((PLI_BYTE8*)"t.negative_multi_packed[-1]", nullptr); + CHECK_RESULT_NZ(vh_neg); + CHECK_RESULT(vpi_get(vpiSize, vh_neg), 128); + TestVpiHandle vh_neg_packed = vpi_handle_by_index(vh_neg, -2); + CHECK_RESULT_NZ(vh_neg_packed); + CHECK_RESULT(vpi_get(vpiSize, vh_neg_packed), 8); + vpi_get_value(vh_neg_packed, &v); + CHECK_RESULT(v.value.integer, 4); + // Further into bit level + TestVpiHandle vh_neg_bit = vpi_handle_by_index(vh_neg_packed, -2); + CHECK_RESULT_NZ(vh_neg_bit); + CHECK_RESULT(vpi_get(vpiSize, vh_neg_bit), 1); + vpi_get_value(vh_neg_bit, &v); + CHECK_RESULT(v.value.integer, 1); } // Partial indexing (not all unpacked dimensions) @@ -1350,6 +1390,8 @@ int _mon_check_multi_index() { // Non-integer / non-decimal index values CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.mem_2d[0][abc]", nullptr)); CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.mem_2d[0x2][3]", nullptr)); + // Index out of bounds + CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.mem_2d[4][0]", nullptr)); CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.mem_2d[-1][0]", nullptr)); // Structural bracket errors CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.mem_2d[0][]", nullptr)); @@ -1366,7 +1408,8 @@ int _mon_check_multi_index() { CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.\\escaped_inst[0] .sig [3:0]", nullptr)); // Indexing non-array signals CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.onebit[0]", nullptr)); - CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.twoone[0]", nullptr)); + // Part-select on non-array signal + CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.onebit[0:0]", nullptr)); // Part-select on unpacked-only array CHECK_RESULT_Z(vpi_handle_by_name((PLI_BYTE8*)"t.unpacked_only[3:0]", nullptr)); // Range/slice syntax in non-last position or on unpacked dimensions @@ -1402,6 +1445,15 @@ int _mon_check_multi_index() { vpi_get_value(vh_desc_full, &v); CHECK_RESULT(v.value.integer, 24); // 0x18 + // Descending range that crosses zero + TestVpiHandle vh_desc_cross + = vpi_handle_by_name((PLI_BYTE8*)"t.negative_multi_packed[-1][-2][1:-3]", nullptr); + CHECK_RESULT_NZ(vh_desc_cross); + CHECK_RESULT(vpi_get(vpiSize, vh_desc_cross), 5); + vpi_get_value(vh_desc_cross, &v); + // Element [-1][-2] is 8'h4; elements are indexed as [3:-4], so bits [1:-3] = 0b00010 + CHECK_RESULT(v.value.integer, 2); + // Ascending packed range behavior is explicit: // mem_3d has packed declaration [0:95], so [3:0] selects the MSB-end nibble, // while [92:95] selects the LSB-end nibble where value 7 resides. diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index 66cd97c8b..cfb157808 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -50,6 +50,7 @@ extern "C" int mon_check(); reg [0:95] mem_3d[0:1][1:0][0:1] /*verilator public_flat_rw */; // Mixed: asc, desc, asc reg [0:15][0:3][7:0] multi_packed[2:0] /*verilator public_flat_rw */; + reg [8:-7] [3:-4] negative_multi_packed[0:-2] /*verilator public_flat_rw */; // verilator lint_on ASCRANGE reg unpacked_only[7:0]; @@ -131,6 +132,12 @@ extern "C" int mon_check(); end end + for (int i = -2; i <= 0; i++) begin + for (int j = -7; j <= 8; j++) begin + negative_multi_packed[i][j] = 8'(((i + 2) * 4) + (j + 2)); + end + end + `ifdef VERILATOR status = $c32("mon_check()"); `endif diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index db78f8842..45d49293d 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -66,6 +66,7 @@ extern "C" int mon_check(); // Signal with multiple packed dimensions reg [0:15][0:3][7:0] multi_packed[2:0]; + reg [8:-7] [3:-4] negative_multi_packed[0:-2]; // verilator lint_on ASCRANGE reg unpacked_only[7:0]; /*verilator public_off*/ @@ -149,6 +150,12 @@ extern "C" int mon_check(); end end + for (int i = -2; i <= 0; i++) begin + for (int j = -7; j <= 8; j++) begin + negative_multi_packed[i][j] = 8'(((i + 2) * 4) + (j + 2)); + end + end + `ifdef VERILATOR status = $c32("mon_check()"); `endif diff --git a/test_regress/t/t_vpi_var3.v b/test_regress/t/t_vpi_var3.v index c7442ecb3..e8a86438d 100644 --- a/test_regress/t/t_vpi_var3.v +++ b/test_regress/t/t_vpi_var3.v @@ -51,6 +51,7 @@ extern "C" int mon_check(); // Signal with multiple packed dimensions reg [0:15][0:3][7:0] multi_packed[2:0]; + reg [8:-7] [3:-4] negative_multi_packed[0:-2]; // verilator lint_on ASCRANGE reg unpacked_only[7:0]; @@ -129,6 +130,12 @@ extern "C" int mon_check(); end end + for (int i = -2; i <= 0; i++) begin + for (int j = -7; j <= 8; j++) begin + negative_multi_packed[i][j] = 8'(((i + 2) * 4) + (j + 2)); + end + end + `ifdef VERILATOR status = $c32("mon_check()"); `endif