diff --git a/docs/guide/connecting.rst b/docs/guide/connecting.rst index 27c2e57a5..724c2938b 100644 --- a/docs/guide/connecting.rst +++ b/docs/guide/connecting.rst @@ -413,6 +413,10 @@ and it can be cleared with :code:`VerilatedVpi::clearEvalNeeded()`. Used togeth it is possible to skip :code:`eval()` calls if no model state has been changed since the last :code:`eval()`. +Any data written via :code:`vpi_put_value` with :code:`vpiInertialDelay` will +be deferred for later. These delayed values can be flushed to the model with +:code:`VerilatedVpi::doInertialPuts()`. + .. _VPI Example: diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 952000700..5cb9992fd 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -630,6 +630,96 @@ public: void invalidate() { m_id = 0; } }; +class VerilatedVpiPutHolder final { + const VerilatedVar* m_varp; + const VerilatedScope* m_scopep; + s_vpi_value m_value; + std::string m_str; + std::vector m_vector; + +public: + VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep) + : m_varp(vop->varp()) + , m_scopep(vop->scopep()) { + m_value.format = valuep->format; + switch (valuep->format) { + case vpiBinStrVal: + case vpiOctStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiStringVal: { + m_str = valuep->value.str; + m_value.value.str = &m_str[0]; + break; + } + case vpiScalarVal: { + m_value.value.scalar = valuep->value.scalar; + break; + } + case vpiIntVal: { + m_value.value.integer = valuep->value.integer; + break; + } + case vpiRealVal: { + m_value.value.real = valuep->value.real; + break; + } + case vpiVectorVal: { + size_t words = 0; + switch (vop->varp()->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: { + words = 1; + break; + } + case VLVT_UINT64: { + words = 2; + break; + } + case VLVT_WDATA: { + words = VL_WORDS_I(vop->varp()->packed().elements()); + break; + } + default: break; + } + m_vector.resize(words); + std::memcpy(m_vector.data(), valuep->value.vector, words * sizeof(s_vpi_vecval)); + m_value.value.vector = m_vector.data(); + break; + } + } + } + + const VerilatedVar* varp() { return m_varp; } + const VerilatedScope* scopep() { return m_scopep; } + p_vpi_value valuep() { return &m_value; } + + static bool canInertialDelay(p_vpi_value valuep) { + switch (valuep->format) { + case vpiBinStrVal: + case vpiOctStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiStringVal: { + if (VL_UNLIKELY(!valuep->value.str)) return false; + break; + } + case vpiScalarVal: + case vpiIntVal: + case vpiRealVal: break; + case vpiVectorVal: { + if (VL_UNLIKELY(!valuep->value.vector)) return false; + break; + } + default: { + return false; + } + } + return true; + } +}; + struct VerilatedVpiTimedCbsCmp final { // Ordering sets keyed by time, then callback unique id bool operator()(const std::pair& a, @@ -652,6 +742,7 @@ class VerilatedVpiImp final { std::array m_cbCurrentLists; VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks + std::list m_inertialPuts; // Pending vpi puts due to vpiInertialDelay 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 @@ -816,6 +907,16 @@ public: 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; } + static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) { + s().m_inertialPuts.emplace_back(vop, valuep); + } + static void doInertialPuts() { + for (auto it : s().m_inertialPuts) { + VerilatedVpioVar vo(it.varp(), it.scopep()); + vpi_put_value(vo.castVpiHandle(), it.valuep(), nullptr, vpiNoDelay); + } + s().m_inertialPuts.clear(); + } }; //====================================================================== @@ -921,6 +1022,8 @@ PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() { void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); } bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); } +void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); } + //====================================================================== // VerilatedVpiImp implementation @@ -2438,7 +2541,7 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { } vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/, - PLI_INT32 /*flags*/) { + PLI_INT32 flags) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep);); VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); @@ -2446,7 +2549,19 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer"); return nullptr; } + PLI_INT32 delay_mode = flags & 0xfff; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { + if (delay_mode == vpiInertialDelay) { + if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) { + VL_VPI_WARNING_( + __FILE__, __LINE__, + "%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay", + __func__, vop->fullname()); + return nullptr; + } + VerilatedVpiImp::inertialDelay(vop, valuep); + return object; + } VL_DEBUG_IF_PLI( VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), valuep->format, valuep->value.integer); diff --git a/include/verilated_vpi.h b/include/verilated_vpi.h index 149a2da48..a8e525a6f 100644 --- a/include/verilated_vpi.h +++ b/include/verilated_vpi.h @@ -59,6 +59,8 @@ public: static bool evalNeeded() VL_MT_UNSAFE_ONE; /// Clears VPI dirty state (see evalNeeded()) static void clearEvalNeeded() VL_MT_UNSAFE_ONE; + /// Perform inertially delayed puts + static void doInertialPuts() 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 0e6b50366..9945868fa 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -683,6 +683,41 @@ int _mon_check_quad() { return 0; } +int _mon_check_delayed() { + TestVpiHandle vh = VPI_HANDLE("delayed"); + CHECK_RESULT_NZ(vh); + + s_vpi_time t; + t.type = vpiSimTime; + t.high = 0; + t.low = 0; + + s_vpi_value v; + v.format = vpiIntVal; + v.value.integer = 123; + vpi_put_value(vh, &v, &t, vpiInertialDelay); + CHECK_RESULT_Z(vpi_chk_error(nullptr)); + vpi_get_value(vh, &v); + CHECK_RESULT(v.value.integer, 0); + + // test unsupported vpiInertialDelay cases + v.format = vpiStringVal; + v.value.str = nullptr; + vpi_put_value(vh, &v, &t, vpiInertialDelay); + CHECK_RESULT_NZ(vpi_chk_error(nullptr)); + + v.format = vpiVectorVal; + v.value.vector = nullptr; + vpi_put_value(vh, &v, &t, vpiInertialDelay); + CHECK_RESULT_NZ(vpi_chk_error(nullptr)); + + v.format = vpiObjTypeVal; + vpi_put_value(vh, &v, &t, vpiInertialDelay); + CHECK_RESULT_NZ(vpi_chk_error(nullptr)); + + return 0; +} + int _mon_check_string() { static struct { const char* name; @@ -891,6 +926,7 @@ extern "C" int mon_check() { if (int status = _mon_check_string()) return status; if (int status = _mon_check_putget_str(NULL)) return status; if (int status = _mon_check_vlog_info()) return status; + if (int status = _mon_check_delayed()) return status; #ifndef IS_VPI VerilatedVpi::selfTest(); #endif @@ -960,6 +996,7 @@ int main(int argc, char** argv) { while (vl_time_stamp64() < sim_time && !contextp->gotFinish()) { main_time += 1; + VerilatedVpi::doInertialPuts(); topp->eval(); VerilatedVpi::callValueCbs(); topp->clk = !topp->clk; diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index e25f0aebf..0e83708e7 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -41,6 +41,7 @@ extern "C" int mon_check(); reg [31:0] count /*verilator public_flat_rd */; reg [31:0] half_count /*verilator public_flat_rd */; + reg [31:0] delayed /*verilator public_flat_rw */; reg [7:0] text_byte /*verilator public_flat_rw @(posedge clk) */; reg [15:0] text_half /*verilator public_flat_rw @(posedge clk) */; @@ -58,6 +59,7 @@ extern "C" int mon_check(); // Test loop initial begin count = 0; + delayed = 0; onebit = 1'b0; fourthreetwoone[3] = 0; // stop icarus optimizing away text_byte = "B"; @@ -101,6 +103,7 @@ extern "C" int mon_check(); half_count <= half_count + 2; if (count == 1000) begin + if (delayed != 123) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index 34e4c9cc5..ba9dcbb55 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -54,6 +54,9 @@ extern "C" int mon_check(); /*verilator public_flat_rd_on*/ reg [31:0] count; reg [31:0] half_count; +/*verilator public_off*/ +/*verilator public_flat_rw_on*/ + reg [31:0] delayed; /*verilator public_off*/ reg invisible2; @@ -76,6 +79,7 @@ extern "C" int mon_check(); // Test loop initial begin count = 0; + delayed = 0; onebit = 1'b0; fourthreetwoone[3] = 0; // stop icarus optimizing away text_byte = "B"; @@ -119,6 +123,7 @@ extern "C" int mon_check(); half_count <= half_count + 2; if (count == 1000) begin + if (delayed != 123) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_vpi_var3.v b/test_regress/t/t_vpi_var3.v index 4071d0bfe..f46518ed7 100644 --- a/test_regress/t/t_vpi_var3.v +++ b/test_regress/t/t_vpi_var3.v @@ -41,6 +41,7 @@ extern "C" int mon_check(); reg [31:0] count; reg [31:0] half_count; + reg [31:0] delayed; reg [7:0] text_byte; reg [15:0] text_half; @@ -58,6 +59,7 @@ extern "C" int mon_check(); // Test loop initial begin count = 0; + delayed = 0; onebit = 1'b0; fourthreetwoone[3] = 0; // stop icarus optimizing away text_byte = "B"; @@ -101,6 +103,7 @@ extern "C" int mon_check(); half_count <= half_count + 2; if (count == 1000) begin + if (delayed != 123) $stop; $write("*-* All Finished *-*\n"); $finish; end