Support vpiInertialDelay (#5087)

This commit is contained in:
Todd Strader 2024-05-01 18:56:50 -04:00 committed by GitHub
parent d841a791e6
commit c99364b81a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 170 additions and 1 deletions

View File

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

View File

@ -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<s_vpi_vecval> 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<QData, uint64_t>& a,
@ -652,6 +742,7 @@ class VerilatedVpiImp final {
std::array<VpioCbList, CB_ENUM_MAX_VALUE> m_cbCurrentLists;
VpioFutureCbs m_futureCbs; // Time based callbacks for future timestamps
VpioFutureCbs m_nextCbs; // cbNextSimTime callbacks
std::list<VerilatedVpiPutHolder> 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);

View File

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

View File

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

View File

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

View File

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

View File

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