mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Support vpiInertialDelay (#5087)
This commit is contained in:
parent
d841a791e6
commit
c99364b81a
@ -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:
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user