diff --git a/docs/guide/connecting.rst b/docs/guide/connecting.rst index 8f06143d4..27c2e57a5 100644 --- a/docs/guide/connecting.rst +++ b/docs/guide/connecting.rst @@ -407,6 +407,12 @@ only a couple of instructions. For signal callbacks to work the main loop of the program must call :code:`VerilatedVpi::callValueCbs()`. +Verilator also tracks when the model state has been modified via the VPI with +an :code:`evalNeeded` flag. This flag can be checked with :code:`VerilatedVpi::evalNeeded()` +and it can be cleared with :code:`VerilatedVpi::clearEvalNeeded()`. Used together +it is possible to skip :code:`eval()` calls if no model state has been changed +since the last :code:`eval()`. + .. _VPI Example: diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 9e6596ddc..952000700 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -655,6 +655,7 @@ class VerilatedVpiImp final { VerilatedVpiError* m_errorInfop = nullptr; // Container for vpi error info VerilatedAssertOneThread m_assertOne; // Assert only called from single thread uint64_t m_nextCallbackId = 1; // Id to identify callback + bool m_evalNeeded = false; // Model has had signals updated via vpi_put_value() static VerilatedVpiImp& s() { // Singleton static VerilatedVpiImp s_s; @@ -813,6 +814,8 @@ public: } static void dumpCbs() VL_MT_UNSAFE_ONE; static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE; // getter for vpi error info + static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; } + static bool evalNeeded() { return s().m_evalNeeded; } }; //====================================================================== @@ -915,6 +918,9 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() { return 1; } +void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); } +bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); } + //====================================================================== // VerilatedVpiImp implementation @@ -2445,6 +2451,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), valuep->format, valuep->value.integer); VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap());); + VerilatedVpiImp::evalNeeded(true); if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { VL_VPI_WARNING_(__FILE__, __LINE__, diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h index 2c15d0583..149a2da48 100644 --- a/include/verilated_vpi.h +++ b/include/verilated_vpi.h @@ -54,6 +54,11 @@ public: static QData cbNextDeadline() VL_MT_UNSAFE_ONE; /// Debug dump of callbacks static void dumpCbs() VL_MT_UNSAFE_ONE; + /// Checks VPI dirty state (i.e. whether vpi_put_value() has + /// been called since the last clearEvalNeeded()) + static bool evalNeeded() VL_MT_UNSAFE_ONE; + /// Clears VPI dirty state (see evalNeeded()) + static void clearEvalNeeded() VL_MT_UNSAFE_ONE; // Self test, for internal use only static void selfTest() VL_MT_UNSAFE_ONE; diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 465bf0891..0e6b50366 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -443,6 +443,19 @@ int _mon_check_varlist() { return 0; } +void touch_signal() { + TestVpiHandle vh1 = VPI_HANDLE("count"); + TEST_CHECK_NZ(vh1); + s_vpi_value v; + v.format = vpiIntVal; + s_vpi_time t; + t.type = vpiSimTime; + t.high = 0; + t.low = 0; + v.value.integer = 0; + vpi_put_value(vh1, &v, &t, vpiNoDelay); +} + int _mon_check_ports() { #ifdef TEST_VERBOSE printf("-mon_check_ports()\n"); @@ -959,6 +972,14 @@ int main(int argc, char** argv) { CHECK_RESULT(callback_count_half, 250); CHECK_RESULT(callback_count_quad, 2); CHECK_RESULT(callback_count_strs, callback_count_strs_max); + VerilatedVpi::clearEvalNeeded(); + if (VerilatedVpi::evalNeeded()) { + vl_fatal(FILENM, __LINE__, "main", "%Error: Unexpected VPI dirty state"); + } + touch_signal(); + if (!VerilatedVpi::evalNeeded()) { + vl_fatal(FILENM, __LINE__, "main", "%Error: Unexpected VPI clean state"); + } if (!contextp->gotFinish()) { vl_fatal(FILENM, __LINE__, "main", "%Error: Timeout; never got a $finish"); }