diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 7d6becb74..337607008 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -896,6 +896,9 @@ public: if (VL_LIKELY(it != s().m_futureCbs.cend())) return it->first.first; return ~0ULL; // maxquad } + static bool hasCbs(const uint32_t reason) VL_MT_UNSAFE_ONE { + return !s().m_cbCurrentLists[reason].empty(); + } static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: callCbs reason=%u\n", reason);); assertOneCheck(); @@ -1056,6 +1059,10 @@ bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callCbs(reason); } +bool VerilatedVpi::hasCbs(uint32_t reason) VL_MT_UNSAFE_ONE { + return VerilatedVpiImp::hasCbs(reason); +} + // Historical, before we had multiple kinds of timed callbacks void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callCbs(cbAfterDelay); } diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h index a8e525a6f..51727cb01 100644 --- a/include/verilated_vpi.h +++ b/include/verilated_vpi.h @@ -49,6 +49,9 @@ public: /// Call callbacks of arbitrary types. /// User wrapper code should call this from their main loops. static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE; + /// Returns true if there are callbacks of the given reason registered. + /// User wrapper code should call this from their main loops. + static bool hasCbs(uint32_t reason) VL_MT_UNSAFE_ONE; /// Returns time of the next registered VPI callback, or /// ~(0ULL) if none are registered static QData cbNextDeadline() VL_MT_UNSAFE_ONE; diff --git a/test_regress/t/TestVpiMain.cpp b/test_regress/t/TestVpiMain.cpp index cbd09fa85..ab368cb24 100644 --- a/test_regress/t/TestVpiMain.cpp +++ b/test_regress/t/TestVpiMain.cpp @@ -107,32 +107,22 @@ int main(int argc, char** argv) { #endif while (!contextp->gotFinish()) { - // Call registered timed callbacks (e.g. clock timer) - // These are called at the beginning of the time step - // before the iterative regions (IEEE 1800-2012 4.4.1) - VerilatedVpi::callTimedCbs(); + do { + // We must evaluate whole design until we process all 'events' for + // this time step + do { + top->eval_step(); + VerilatedVpi::clearEvalNeeded(); + VerilatedVpi::doInertialPuts(); + settle_value_callbacks(); + } while (VerilatedVpi::evalNeeded()); - // Call Value Change callbacks triggered by Timer callbacks - // These can modify signal values - settle_value_callbacks(); + // Run ReadWrite callback as we are done processing this eval step + VerilatedVpi::callCbs(cbReadWriteSynch); + VerilatedVpi::doInertialPuts(); + settle_value_callbacks(); + } while (VerilatedVpi::evalNeeded() || VerilatedVpi::hasCbs(cbReadWriteSynch)); - // We must evaluate whole design until we process all 'events' - bool again = true; - while (again) { - // Evaluate design - top->eval_step(); - - // Call Value Change callbacks triggered by eval() - // These can modify signal values - again = settle_value_callbacks(); - - // Call registered ReadWrite callbacks - again |= VerilatedVpi::callCbs(cbReadWriteSynch); - - // Call Value Change callbacks triggered by ReadWrite callbacks - // These can modify signal values - again |= settle_value_callbacks(); - } top->eval_end_step(); // Call ReadOnly callbacks @@ -161,9 +151,12 @@ int main(int argc, char** argv) { // It should be called in simulation cycle before everything else // but not on first cycle VerilatedVpi::callCbs(cbNextSimTime); + settle_value_callbacks(); - // Call Value Change callbacks triggered by NextTimeStep callbacks - // These can modify signal values + // Call registered timed callbacks (e.g. clock timer) + // These are called at the beginning of the time step + // before the iterative regions (IEEE 1800-2012 4.4.1) + VerilatedVpi::callTimedCbs(); settle_value_callbacks(); }