iverilog/vvp/vpi_callback.cc

1061 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2001-2025 Stephen Williams (steve@icarus.com)
*
* This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU
* General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* Callbacks are objects that carry a function to be called when some
* event in the simulation occurs. The VPI code create a __vpiCallback
* object, and that object is put in some location that the simulation
* can look when the event in question is tripped.
*/
# include "vpi_user.h"
# include "vpi_priv.h"
# include "vpi_utils.h"
# include "vvp_net.h"
# include "schedule.h"
# include "event.h"
# include "vvp_net_sig.h"
# include "config.h"
#ifdef CHECK_WITH_VALGRIND
#include "vvp_cleanup.h"
#endif
# include <cstdio>
# include <cassert>
# include <cstdlib>
using namespace std;
static const char*cb_reason_name(PLI_INT32 reason)
{
switch (reason) {
case cbValueChange:
return "cbValueChange";
case cbStmt:
return "cbStmt";
case cbForce:
return "cbForce";
case cbRelease:
return "cbRelease";
case cbAtStartOfSimTime:
return "cbAtStartOfSimTime";
case cbReadWriteSynch:
return "cbReadWriteSynch";
case cbReadOnlySynch:
return "cbReadOnlySynch";
case cbNextSimTime:
return "cbNextSimTime";
case cbAfterDelay:
return "cbAfterDelay";
case cbEndOfCompile:
return "cbEndOfCompile";
case cbStartOfSimulation:
return "cbStartOfSimulation";
case cbEndOfSimulation:
return "cbEndOfSimulation";
case cbError:
return "cbError";
case cbTchkViolation:
return "cbTchkViolation";
case cbStartOfSave:
return "cbStartOfSave";
case cbEndOfSave:
return "cbEndOfSave";
case cbStartOfRestart:
return "cbStartOfRestart";
case cbEndOfRestart:
return "cbEndOfRestart";
case cbStartOfReset:
return "cbStartOfReset";
case cbEndOfReset:
return "cbEndOfReset";
case cbEnterInteractive:
return "cbEnterInteractive";
case cbExitInteractive:
return "cbExitInteractive";
case cbInteractiveScopeChange:
return "cbInteractiveScopeChange";
case cbUnresolvedSystf:
return "cbUnresolvedSystf";
case cbAtEndOfSimTime:
return "cbAtEndOfSimTime";
default:
return "unrecognised";
}
}
static bool check_callback_time(p_cb_data data, bool allow_suppress)
{
assert(data);
if (!data->time) {
if (!allow_suppress) {
fprintf(stderr, "VPI error: null value passed in cb_data.time "
"when registering %s callback\n.",
cb_reason_name(data->reason));
return false;
}
return true;
}
switch (data->time->type) {
case vpiSimTime:
break;
case vpiScaledRealTime:
break;
case vpiSuppressTime:
if (!allow_suppress) {
fprintf(stderr, "VPI error: vpiSuppressTime is not valid "
"when registering %s callback\n.",
cb_reason_name(data->reason));
return false;
}
break;
default:
fprintf(stderr, "VPI error: invalid type passed in cb_data time "
"structure when registering %s callback\n.",
cb_reason_name(data->reason));
return false;
}
return true;
}
static void set_callback_time(p_cb_data data)
{
assert(data && data->time);
data->time->low = 0;
data->time->high = 0;
data->time->real = 0.0;
switch (data->time->type) {
case vpiSimTime:
vpip_time_to_timestruct(data->time, schedule_simtime());
break;
case vpiScaledRealTime:
data->time->real = vpip_scaled_time_from_handle(schedule_simtime(), data->obj);
break;
case vpiSuppressTime:
break;
default:
assert(0);
break;
}
}
/*
* Callback handles are created when the VPI function registers a
* callback. The handle is stored by the run time, and it triggered
* when the run-time thing that it is waiting for happens.
*
* This is the thing that the VPI code references by the vpiHandle. It
* also points to callback data that the caller attached to the event,
* as well as the time structure to receive data.
*
* The cb_sync is a private member that points to the schedulable
* event that is triggered when the event happens. The sync_cb class
* represents the action to execute when the scheduler gets to this
* event. This member is only used for things like cbReadOnlySync.
*/
class sync_callback;
struct sync_cb : public vvp_gen_event_s {
sync_callback*handle;
bool sync_flag;
~sync_cb () override { }
virtual void run_run() override;
};
inline __vpiCallback::__vpiCallback()
{
next = 0;
}
__vpiCallback::~__vpiCallback()
{
}
int __vpiCallback::get_type_code(void) const
{ return vpiCallback; }
value_callback::value_callback(p_cb_data data)
{
cb_data = *data;
if (data->time) {
cb_time = *(data->time);
} else {
cb_time.type = vpiSuppressTime;
}
cb_data.time = &cb_time;
if (data->value) {
cb_value = *(data->value);
} else {
cb_value.format = vpiSuppressVal;
}
cb_data.value = &cb_value;
}
/*
* Normally, any assign to a value triggers a value change callback,
* so return a constant true here. This is a stub.
*/
bool value_callback::test_value_callback_ready(void)
{
return true;
}
static void vpip_real_value_change(value_callback*cbh, vpiHandle ref)
{
struct __vpiRealVar*rfp = dynamic_cast<__vpiRealVar*>(ref);
assert(rfp);
vvp_vpi_callback*obj = dynamic_cast<vvp_vpi_callback*>(rfp->net->fil);
assert(obj);
obj->add_vpi_callback(cbh);
}
class value_part_callback : public value_callback {
public:
explicit value_part_callback(p_cb_data data);
~value_part_callback() override;
bool test_value_callback_ready(void) override;
private:
char*value_bits_;
size_t value_off_;
};
inline value_part_callback::value_part_callback(p_cb_data data)
: value_callback(data)
{
struct __vpiPV*pobj = dynamic_cast<__vpiPV*>(data->obj);
assert(pobj);
vvp_vpi_callback*sig_fil;
sig_fil = dynamic_cast<vvp_vpi_callback*>(pobj->net->fil);
assert(sig_fil);
sig_fil->add_vpi_callback(this);
// Get a reference value that can be used to compare with an
// updated value. Use the filter get_value to get the value,
// and get it in BinStr form so that compares are easy. Note
// that the vpiBinStr format has the MSB first, but the tbase
// is lsb first.
s_vpi_value tmp_value;
tmp_value.format = vpiBinStrVal;
sig_fil->get_value(&tmp_value);
value_bits_ = new char[pobj->width+1];
value_off_ = pobj->parent->vpi_get(vpiSize) - pobj->width - pobj->tbase;
memcpy(value_bits_, tmp_value.value.str + value_off_, pobj->width);
value_bits_[pobj->width] = 0;
}
value_part_callback::~value_part_callback()
{
delete[]value_bits_;
}
bool value_part_callback::test_value_callback_ready(void)
{
struct __vpiPV*pobj = dynamic_cast<__vpiPV*>(cb_data.obj);
assert(pobj);
vvp_vpi_callback*sig_fil;
sig_fil = dynamic_cast<vvp_vpi_callback*>(pobj->net->fil);
assert(sig_fil);
// Get a reference value that can be used to compare with an
// updated value.
s_vpi_value tmp_value;
tmp_value.format = vpiBinStrVal;
sig_fil->get_value(&tmp_value);
if (memcmp(value_bits_, tmp_value.value.str + value_off_, pobj->width) == 0)
return false;
memcpy(value_bits_, tmp_value.value.str + value_off_, pobj->width);
return true;
}
/*
* Attach the __vpiCallback to the object that this part select
* selects from. The part select itself is not a vvp_vpi_callback
* object, but it refers to a net that is a vvp_vpi_callback, so
* add the callback to that object.
*/
static value_callback*make_value_change_part(p_cb_data data)
{
/* Attach the __vpiCallback object to the signal. */
value_callback*cbh = new value_part_callback(data);
return cbh;
}
/*
* A value change callback is tripped when a bit of a signal
* changes. This function creates that value change callback and
* attaches it to the relevant vpiSignal object. Also, if the signal
* does not already have them, create some callback functors to do the
* actual value change detection.
*/
static value_callback* make_value_change(p_cb_data data)
{
if (!check_callback_time(data, true))
return 0;
if (vpi_get(vpiAutomatic, data->obj)) {
fprintf(stderr, "vpi error: cannot place value change "
"callback on automatically allocated "
"variable '%s'\n",
vpi_get_str(vpiName, data->obj));
return 0;
}
// Special case: the target object is a vpiPartSelect
if (data->obj->get_type_code() == vpiPartSelect) {
if (data->obj->vpi_handle(vpiArray))
return vpip_array_word_change(data);
else
return make_value_change_part(data);
}
if (data->obj->get_type_code() == vpiMemoryWord)
return vpip_array_word_change(data);
if (data->obj->get_type_code() == vpiMemory)
return vpip_array_change(data);
value_callback*obj = new value_callback(data);
assert(data->obj);
switch (data->obj->get_type_code()) {
case vpiReg:
case vpiNet:
case vpiIntegerVar:
case vpiBitVar:
case vpiByteVar:
case vpiShortIntVar:
case vpiIntVar:
case vpiLongIntVar:
/* Attach the callback to the vvp_fun_signal node by
putting it in the vpi_callbacks list. */
struct __vpiSignal*sig;
sig = dynamic_cast<__vpiSignal*>(data->obj);
vvp_net_fil_t*sig_fil;
sig_fil = dynamic_cast<vvp_net_fil_t*>(sig->node->fil);
assert(sig_fil);
/* Attach the __vpiCallback object to the signal. */
sig_fil->add_vpi_callback(obj);
break;
case vpiRealVar:
vpip_real_value_change(obj, data->obj);
break;
case vpiNamedEvent:
__vpiNamedEvent*nev;
nev = dynamic_cast<__vpiNamedEvent*>(data->obj);
nev->add_vpi_callback(obj);
break;
case vpiModule:
case vpiConstant:
case vpiParameter:
/* These are constant, so there are no value change
lists to put them in. */
break;
default:
fprintf(stderr, "make_value_change: sorry: I cannot callback "
"values on type code=%d\n",
data->obj->get_type_code());
delete obj;
return 0;
}
return obj;
}
class sync_callback : public __vpiCallback {
public:
explicit sync_callback(p_cb_data data);
~sync_callback() override;
public:
// scheduled event
struct sync_cb* cb_sync;
// user supplied callback data
struct t_vpi_time cb_time;
private:
};
inline sync_callback::sync_callback(p_cb_data data)
{
cb_sync = 0;
cb_data = *data;
assert(data->time);
cb_time = *(data->time);
cb_data.time = &cb_time;
}
sync_callback::~sync_callback()
{
delete cb_sync;
}
void sync_cb::run_run()
{
if (handle == 0)
return;
sync_callback*cur = handle;
/* Run the callback. If the cb_rtn function pointer is set to
null, then just skip the whole thing and free it. This is
the usual way to cancel one-time callbacks of this sort. */
if (cur->cb_data.cb_rtn != 0) {
set_callback_time(&cur->cb_data);
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = sync_flag? VPI_MODE_ROSYNC : VPI_MODE_RWSYNC;
(cur->cb_data.cb_rtn)(&cur->cb_data);
vpi_mode_flag = VPI_MODE_NONE;
}
delete cur;
}
static vvp_time64_t get_sync_cb_time(sync_callback*obj)
{
vvp_time64_t tv = 0;
switch (obj->cb_time.type) {
case vpiSimTime:
tv = vpip_timestruct_to_time(&obj->cb_time);
break;
case vpiScaledRealTime:
tv = vpip_scaled_real_to_time64(obj->cb_time.real,
vpip_timescale_scope_from_handle(obj->cb_data.obj));
break;
default:
fprintf(stderr, "get_sync_cb_time: Unsupported time type %d.\n",
(int)obj->cb_time.type);
assert(0);
break;
}
return tv;
}
static sync_callback* make_sync(p_cb_data data, bool readonly_flag)
{
if (!check_callback_time(data, false))
return 0;
sync_callback*obj = new sync_callback(data);
struct sync_cb*cb = new sync_cb;
cb->sync_flag = readonly_flag? true : false;
cb->handle = obj;
obj->cb_sync = cb;
vvp_time64_t tv = get_sync_cb_time(obj);
schedule_generic(cb, tv, true, readonly_flag);
return obj;
}
static struct __vpiCallback* make_afterdelay(p_cb_data data)
{
if (!check_callback_time(data, false))
return 0;
sync_callback*obj = new sync_callback(data);
struct sync_cb*cb = new sync_cb;
cb->sync_flag = false;
cb->handle = obj;
obj->cb_sync = cb;
vvp_time64_t tv = get_sync_cb_time(obj);
schedule_generic(cb, tv, false);
return obj;
}
static struct __vpiCallback* make_at_start_of_sim_time(p_cb_data data)
{
if (!check_callback_time(data, false))
return 0;
sync_callback*obj = new sync_callback(data);
struct sync_cb*cb = new sync_cb;
cb->sync_flag = false;
cb->handle = obj;
obj->cb_sync = cb;
vvp_time64_t tv = get_sync_cb_time(obj);
vvp_time64_t cur = schedule_simtime();
if (cur > tv) {
tv = 0;
assert(0);
} else if (cur == tv) {
tv = 0;
} else {
tv -= cur;
}
schedule_at_start_of_simtime(cb, tv);
return obj;
}
static struct __vpiCallback* make_at_end_of_sim_time(p_cb_data data)
{
if (!check_callback_time(data, false))
return 0;
sync_callback*obj = new sync_callback(data);
struct sync_cb*cb = new sync_cb;
cb->sync_flag = false;
cb->handle = obj;
obj->cb_sync = cb;
vvp_time64_t tv = get_sync_cb_time(obj);
vvp_time64_t cur = schedule_simtime();
if (cur > tv) {
tv = 0;
assert(0);
} else if (cur == tv) {
tv = 0;
} else {
tv -= cur;
}
schedule_at_end_of_simtime(cb, tv);
return obj;
}
/*
* The following functions are the used for pre and post simulation
* callbacks.
*/
class simulator_callback : public __vpiCallback {
public:
explicit simulator_callback(const struct t_cb_data*data);
public:
struct t_vpi_time cb_time;
};
inline simulator_callback::simulator_callback(const struct t_cb_data*data)
{
cb_data = *data;
if ((data->reason == cbNextSimTime) && data->time) {
cb_time = *(data->time);
} else if (data->reason == cbEndOfSimulation) {
cb_time.type = vpiSimTime;
} else {
cb_time.type = vpiSuppressTime;
}
cb_data.time = &cb_time;
}
static simulator_callback*NextSimTime = 0;
static simulator_callback*EndOfCompile = 0;
static simulator_callback*StartOfSimulation = 0;
static simulator_callback*EndOfSimulation = 0;
#ifdef CHECK_WITH_VALGRIND
/* This is really only needed if the simulator aborts before starting the
* main event loop. For that reason we can skip the next sim time queue. */
void simulator_cb_delete(void)
{
simulator_callback* cur;
/* Delete all the end of compile callbacks. */
while (EndOfCompile) {
cur = EndOfCompile;
EndOfCompile = dynamic_cast<simulator_callback*>(cur->next);
delete cur;
}
/* Delete all the start of simulation callbacks. */
while (StartOfSimulation) {
cur = StartOfSimulation;
StartOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
delete cur;
}
/* Delete all the end of simulation callbacks. */
while (EndOfSimulation) {
cur = EndOfSimulation;
EndOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
delete cur;
}
}
#endif
void vpiEndOfCompile(void) {
simulator_callback* cur;
/*
* Walk the list of register callbacks, executing them and
* freeing them when done.
*/
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = VPI_MODE_RWSYNC;
while (EndOfCompile) {
cur = EndOfCompile;
EndOfCompile = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
}
vpi_mode_flag = VPI_MODE_NONE;
}
void vpiStartOfSim(void) {
simulator_callback* cur;
/*
* Walk the list of register callbacks, executing them and
* freeing them when done.
*/
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = VPI_MODE_RWSYNC;
while (StartOfSimulation) {
cur = StartOfSimulation;
StartOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
}
vpi_mode_flag = VPI_MODE_NONE;
}
void vpiPostsim(void) {
simulator_callback* cur;
/*
* Walk the list of register callbacks
*/
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = VPI_MODE_ROSYNC;
while (EndOfSimulation) {
cur = EndOfSimulation;
EndOfSimulation = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
set_callback_time(&cur->cb_data);
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
}
vpi_mode_flag = VPI_MODE_NONE;
}
/*
* The scheduler invokes this to clear out callbacks for the next
* simulation time.
*/
void vpiNextSimTime(void)
{
simulator_callback* cur;
simulator_callback* next = NextSimTime;
NextSimTime = 0;
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = VPI_MODE_RWSYNC;
while (next) {
cur = next;
next = dynamic_cast<simulator_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
set_callback_time(&cur->cb_data);
(cur->cb_data.cb_rtn)(&cur->cb_data);
}
delete cur;
}
vpi_mode_flag = VPI_MODE_NONE;
}
static simulator_callback* make_prepost(p_cb_data data)
{
if ((data->reason == cbNextSimTime) && !check_callback_time(data, true))
return 0;
simulator_callback*obj = new simulator_callback(data);
/* Insert at head of list */
switch (data->reason) {
case cbEndOfCompile:
obj->next = EndOfCompile;
EndOfCompile = obj;
break;
case cbStartOfSimulation:
obj->next = StartOfSimulation;
StartOfSimulation = obj;
break;
case cbEndOfSimulation:
obj->next = EndOfSimulation;
EndOfSimulation = obj;
break;
case cbNextSimTime:
obj->next = NextSimTime;
NextSimTime = obj;
break;
}
return obj;
}
vpiHandle vpi_register_cb(p_cb_data data)
{
struct __vpiCallback*obj = 0;
assert(data);
switch (data->reason) {
case cbValueChange:
obj = make_value_change(data);
break;
case cbReadOnlySynch:
obj = make_sync(data, true);
break;
case cbReadWriteSynch:
obj = make_sync(data, false);
break;
case cbAtStartOfSimTime:
obj = make_at_start_of_sim_time(data);
break;
case cbAtEndOfSimTime:
obj = make_at_end_of_sim_time(data);
break;
case cbAfterDelay:
obj = make_afterdelay(data);
break;
case cbEndOfCompile:
case cbStartOfSimulation:
case cbEndOfSimulation:
case cbNextSimTime:
obj = make_prepost(data);
break;
default:
fprintf(stderr, "vpi error: vpi_register_cb invalid or "
"unsupported callback reason: %d\n",
(int)data->reason);
break;
}
return obj;
}
/*
* Removing a callback doesn't really delete it right away. Instead,
* it clears the reference to the user callback function. This causes
* the callback to quietly reap itself.
*/
PLI_INT32 vpi_remove_cb(vpiHandle ref)
{
struct __vpiCallback*obj = dynamic_cast<__vpiCallback*>(ref);
assert(obj);
obj->cb_data.cb_rtn = 0;
return 1;
}
void callback_execute(struct __vpiCallback*cur)
{
const vpi_mode_t save_mode = vpi_mode_flag;
vpi_mode_flag = VPI_MODE_RWSYNC;
assert(cur->cb_data.cb_rtn);
set_callback_time(&cur->cb_data);
(cur->cb_data.cb_rtn)(&cur->cb_data);
vpi_mode_flag = save_mode;
}
/*
* Usually there is at most one array word associated with a vvp signal, but
* due to port collapsing, there may be more. Using a linked list to record
* the array words minimises memory use for the most common case (no array
* words) or next most common case (one array word).
*/
struct __vpi_array_word {
struct __vpi_array_word* next;
struct __vpiArray* array;
unsigned long word;
};
vvp_vpi_callback::vvp_vpi_callback()
{
vpi_callbacks_ = 0;
array_words_ = 0;
}
vvp_vpi_callback::~vvp_vpi_callback()
{
assert(vpi_callbacks_ == 0);
assert(array_words_ == 0);
}
void vvp_vpi_callback::attach_as_word(vvp_array_t arr, unsigned long addr)
{
struct __vpi_array_word*tmp = new __vpi_array_word;
tmp->array = arr;
tmp->word = addr;
tmp->next = array_words_;
array_words_ = tmp;
}
void vvp_vpi_callback::add_vpi_callback(value_callback*cb)
{
cb->next = vpi_callbacks_;
vpi_callbacks_ = cb;
}
#ifdef CHECK_WITH_VALGRIND
void vvp_vpi_callback::clear_all_callbacks()
{
while (vpi_callbacks_) {
value_callback *tmp = dynamic_cast<value_callback*>
(vpi_callbacks_->next);
delete vpi_callbacks_;
vpi_callbacks_ = tmp;
}
while (array_words_) {
struct __vpi_array_word*tmp = array_words_->next;
delete array_words_;
array_words_ = tmp;
}
}
#endif
/*
* A vvp_fun_signal uses this method to run its callbacks whenever it
* has a value change. If the cb_rtn is non-nil, then call the
* callback function. If the cb_rtn pointer is nil, then the object
* has been marked for deletion. Free it.
*/
void vvp_vpi_callback::run_vpi_callbacks()
{
struct __vpi_array_word*array_word = array_words_;
while (array_word) {
array_word->array->word_change(array_word->word);
array_word = array_word->next;
}
value_callback *next = vpi_callbacks_;
value_callback *prev = 0;
while (next) {
value_callback*cur = next;
next = dynamic_cast<value_callback*>(cur->next);
if (cur->cb_data.cb_rtn != 0) {
if (cur->test_value_callback_ready()) {
if (cur->cb_data.value)
get_value(cur->cb_data.value);
callback_execute(cur);
}
prev = cur;
} else if (prev == 0) {
vpi_callbacks_ = next;
cur->next = 0;
delete cur;
} else {
assert(prev->next == cur);
prev->next = next;
cur->next = 0;
delete cur;
}
}
}
void vvp_signal_value::get_signal_value(struct t_vpi_value*vp)
{
switch (vp->format) {
case vpiScalarVal:
// This works because vvp_bit4_t has the same encoding
// as a scalar value! See vpip_vec4_get_value() for a
// more robust method.
vp->value.scalar = value(0);
break;
case vpiBinStrVal:
case vpiOctStrVal:
case vpiDecStrVal:
case vpiHexStrVal:
case vpiIntVal:
case vpiVectorVal:
case vpiStringVal:
case vpiRealVal: {
unsigned wid = value_size();
vvp_vector4_t vec4(wid);
for (unsigned idx = 0; idx < wid; idx += 1) {
vec4.set_bit(idx, value(idx));
}
vpip_vec4_get_value(vec4, wid, false, vp);
break;
}
case vpiSuppressVal:
break;
default:
fprintf(stderr, "vpi_callback: value "
"format %d not supported (fun_signal)\n",
(int)vp->format);
}
}
static void real_signal_value(struct t_vpi_value*vp, double rval)
{
static const size_t RBUF_SIZE = 64 + 1;
char*rbuf = static_cast<char *>(need_result_buf(RBUF_SIZE, RBUF_VAL));
switch (vp->format) {
case vpiObjTypeVal:
vp->format = vpiRealVal;
// fallthrough
case vpiRealVal:
vp->value.real = rval;
break;
case vpiIntVal:
/* NaN or +/- infinity are translated as 0. */
if (rval != rval || (rval && (rval == 0.5*rval))) {
rval = 0.0;
} else {
rval = std::round(rval);
}
vp->value.integer = (PLI_INT32)rval;
break;
case vpiDecStrVal:
if (std::isnan(rval))
snprintf(rbuf, RBUF_SIZE, "%s", "nan");
else
snprintf(rbuf, RBUF_SIZE, "%0.0f", std::round(rval));
vp->value.str = rbuf;
break;
case vpiHexStrVal:
snprintf(rbuf, RBUF_SIZE, "%" PRIx64, vlg_round_to_u64(rval));
vp->value.str = rbuf;
break;
case vpiBinStrVal: {
const uint64_t val = vlg_round_to_u64(rval);
unsigned len = 0;
// Compute bitwidth; For val==0, this yields len==1
uint64_t tmp = val;
do {
len += 1;
tmp /= 2;
} while (tmp > 0);
tmp = val;
for (unsigned idx = 0; idx < len; idx += 1) {
rbuf[len - idx - 1] = (tmp & 1) ? '1' : '0';
tmp /= 2;
}
rbuf[len] = '\0';
vp->value.str = rbuf;
break;
}
case vpiSuppressVal:
break;
default:
fprintf(stderr, "vpi_callback: value "
"format %d not supported (fun_signal_real)\n",
(int)vp->format);
}
}
void vvp_fun_signal_real_aa::get_signal_value(struct t_vpi_value*vp)
{
real_signal_value(vp, real_value());
}
void vvp_wire_real::get_signal_value(struct t_vpi_value*vp)
{
real_signal_value(vp, real_value());
}
void vvp_fun_signal_string_aa::get_signal_value(struct t_vpi_value*)
{
assert(0);
}
#if 0
void vvp_wire_string::get_signal_value(struct t_vpi_value*vp)
{
assert(0);
}
#endif
void vvp_wire_vec4::get_value(struct t_vpi_value*val)
{
get_signal_value(val);
}
void vvp_wire_vec8::get_value(struct t_vpi_value*val)
{
get_signal_value(val);
}
void vvp_wire_real::get_value(struct t_vpi_value*val)
{
get_signal_value(val);
}
#if 0
void vvp_wire_string::get_value(struct t_vpi_value*val)
{
assert(0);
}
#endif