Add VPI eval needed tracking (#5065)

This commit is contained in:
Todd Strader 2024-04-25 09:07:31 -04:00 committed by GitHub
parent a9664b8d16
commit 25fd8ef5c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 39 additions and 0 deletions

View File

@ -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:

View File

@ -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__,

View File

@ -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;

View File

@ -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");
}