iverilog/vvp/vpi_callback.cc

718 lines
17 KiB
C++
Raw Normal View History

2001-06-22 00:54:12 +02:00
/*
* Copyright (c) 2001-2010 Stephen Williams (steve@icarus.com)
2001-06-22 00:54:12 +02:00
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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"
2001-06-22 00:54:12 +02:00
# include "vpi_priv.h"
# include "vvp_net.h"
2001-06-22 00:54:12 +02:00
# include "schedule.h"
2001-11-06 04:07:21 +01:00
# include "event.h"
# include "vvp_net_sig.h"
# include "config.h"
# include <cstdio>
# include <cassert>
# include <cstdlib>
2001-06-22 00:54:12 +02:00
/*
* The vpi_free_object() call to a callback doesn't actually delete
* anything, we instead allow the object to run its course and delete
* itself. The semantics of vpi_free_object for a callback is that it
* deletes the *handle*, and not the object itself, so given the vvp
* implementation, there is nothing to do here.
*/
static int free_simple_callback(vpiHandle)
2002-05-04 05:17:29 +02:00
{
return 1;
2002-05-04 05:17:29 +02:00
}
2001-06-22 00:54:12 +02:00
const struct __vpirt callback_rt = {
vpiCallback,
0,
0,
0,
0,
0,
0,
2002-05-04 05:17:29 +02:00
0,
&free_simple_callback,
0,
0
2001-06-22 00:54:12 +02:00
};
/*
* 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.
2002-04-06 22:25:45 +02:00
*
* 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.
*/
struct sync_cb : public vvp_gen_event_s {
struct __vpiCallback*handle;
bool sync_flag;
~sync_cb () { }
2005-12-05 22:21:18 +01:00
virtual void run_run();
};
struct __vpiCallback* new_vpi_callback()
{
struct __vpiCallback* obj;
2002-04-06 22:25:45 +02:00
obj = new __vpiCallback;
obj->base.vpi_type = &callback_rt;
obj->cb_sync = 0;
obj->next = 0;
return obj;
}
void delete_vpi_callback(struct __vpiCallback* ref)
{
assert(ref);
assert(ref->base.vpi_type);
assert(ref->base.vpi_type->type_code == vpiCallback);
delete ref->cb_sync;
delete ref;
}
2001-06-22 00:54:12 +02:00
/*
* A value change callback is tripped when a bit of a signal
* changes. This function creates that value change callback and
2003-02-10 00:33:26 +01:00
* attaches it to the relevant vpiSignal object. Also, if the signal
2002-04-06 22:25:45 +02:00
* does not already have them, create some callback functors to do the
* actual value change detection.
2001-06-22 00:54:12 +02:00
*/
static struct __vpiCallback* make_value_change(p_cb_data data)
{
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;
}
struct __vpiCallback*obj = new_vpi_callback();
2001-06-22 00:54:12 +02:00
obj->cb_data = *data;
2002-05-09 05:34:31 +02:00
if (data->time) {
obj->cb_time = *(data->time);
} else {
obj->cb_time.type = vpiSuppressTime;
}
obj->cb_data.time = &obj->cb_time;
if (data->value) {
obj->cb_value = *(data->value);
} else {
obj->cb_value.format = vpiSuppressVal;
}
obj->cb_data.value = &obj->cb_value;
2001-06-22 00:54:12 +02:00
assert(data->obj);
assert(data->obj->vpi_type);
2002-04-06 22:25:45 +02:00
switch (data->obj->vpi_type->type_code) {
case vpiReg:
case vpiNet:
2002-07-12 04:07:36 +02:00
case vpiIntegerVar:
/* Attach the callback to the vvp_fun_signal node by
putting it in the vpi_callbacks list. */
struct __vpiSignal*sig;
sig = reinterpret_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:
struct __vpiNamedEvent*nev;
nev = reinterpret_cast<__vpiNamedEvent*>(data->obj);
obj->next = nev->callbacks;
nev->callbacks = obj;
break;
case vpiMemoryWord:
vpip_array_word_change(obj, data->obj);
break;
case vpiMemory:
vpip_array_change(obj, data->obj);
break;
case vpiPartSelect:
vpip_part_select_value_change(obj, data->obj);
break;
case vpiModule:
case vpiConstant:
2003-03-12 03:50:32 +01:00
case vpiParameter:
/* These are constant, so there are no value change
lists to put them in. */
break;
default:
2003-03-12 03:50:32 +01:00
fprintf(stderr, "make_value_change: sorry: I cannot callback "
"values on type code=%d\n",
data->obj->vpi_type->type_code);
delete obj;
return 0;
}
2001-06-22 00:54:12 +02:00
return obj;
}
void sync_cb::run_run()
{
if (handle == 0)
return;
struct __vpiCallback*cur = handle;
cur->cb_data.time->type = vpiSimTime;
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
2003-04-25 06:36:42 +02:00
/* 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) {
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = sync_flag? VPI_MODE_ROSYNC : VPI_MODE_RWSYNC;
2003-04-25 06:36:42 +02:00
(cur->cb_data.cb_rtn)(&cur->cb_data);
vpi_mode_flag = VPI_MODE_NONE;
}
delete_vpi_callback(cur);
}
static struct __vpiCallback* make_sync(p_cb_data data, bool readonly_flag)
{
struct __vpiCallback*obj = new_vpi_callback();
obj->cb_data = *data;
2002-05-09 05:34:31 +02:00
assert(data->time);
obj->cb_time = *(data->time);
obj->cb_data.time = &obj->cb_time;
obj->next = 0;
struct sync_cb*cb = new sync_cb;
cb->sync_flag = readonly_flag? true : false;
cb->handle = obj;
obj->cb_sync = cb;
switch (obj->cb_time.type) {
case vpiSuppressTime:
schedule_generic(cb, 0, true, readonly_flag);
break;
case vpiSimTime:
{ vvp_time64_t tv = vpip_timestruct_to_time(&obj->cb_time);
vvp_time64_t tn = schedule_simtime();
if (tv < tn) {
schedule_generic(cb, 0, true, readonly_flag);
} else {
schedule_generic(cb, tv - tn, true, readonly_flag);
}
break;
}
default:
fprintf(stderr, "Unsupported time type %d.\n",
(int)obj->cb_time.type);
assert(0);
break;
}
return obj;
}
2009-11-01 18:26:09 +01:00
static struct __vpiCallback* make_afterdelay(p_cb_data data, bool simtime_flag)
2002-09-20 04:42:11 +02:00
{
struct __vpiCallback*obj = new_vpi_callback();
obj->cb_data = *data;
assert(data->time);
obj->cb_time = *(data->time);
obj->cb_data.time = &obj->cb_time;
obj->next = 0;
struct sync_cb*cb = new sync_cb;
cb->sync_flag = false;
cb->handle = obj;
obj->cb_sync = cb;
2009-11-01 18:26:09 +01:00
vvp_time64_t tv;
2002-09-20 04:42:11 +02:00
switch (obj->cb_time.type) {
2009-11-01 18:26:09 +01:00
case vpiSimTime:
tv = vpip_timestruct_to_time(&obj->cb_time);
break;
2002-09-20 04:42:11 +02:00
default:
fprintf(stderr, "Unsupported time type %d.\n",
(int)obj->cb_time.type);
2002-09-20 04:42:11 +02:00
assert(0);
2009-11-01 18:26:09 +01:00
tv = 0;
2002-09-20 04:42:11 +02:00
break;
}
2009-11-01 18:26:09 +01:00
if (simtime_flag) {
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);
} else {
schedule_generic(cb, tv, false);
}
2002-09-20 04:42:11 +02:00
return obj;
}
2002-05-04 05:03:17 +02:00
/*
* The following functions are the used for pre and post simulation
* callbacks.
*/
2003-04-20 01:32:57 +02:00
static struct __vpiCallback*NextSimTime = 0;
2002-05-04 05:03:17 +02:00
static struct __vpiCallback*EndOfCompile = NULL;
static struct __vpiCallback*StartOfSimulation = NULL;
static struct __vpiCallback*EndOfSimulation = NULL;
void vpiEndOfCompile(void) {
2002-05-04 05:03:17 +02:00
struct __vpiCallback* cur;
/*
2002-05-04 05:17:29 +02:00
* Walk the list of register callbacks, executing them and
* freeing them when done.
2002-05-04 05:03:17 +02:00
*/
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = VPI_MODE_RWSYNC;
2002-05-04 05:17:29 +02:00
while (EndOfCompile) {
cur = EndOfCompile;
EndOfCompile = cur->next;
(cur->cb_data.cb_rtn)(&cur->cb_data);
delete_vpi_callback(cur);
2002-05-04 05:03:17 +02:00
}
2002-05-04 05:17:29 +02:00
vpi_mode_flag = VPI_MODE_NONE;
}
void vpiStartOfSim(void) {
struct __vpiCallback* 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;
2002-05-04 05:17:29 +02:00
while (StartOfSimulation) {
cur = StartOfSimulation;
StartOfSimulation = cur->next;
(cur->cb_data.cb_rtn)(&cur->cb_data);
delete_vpi_callback(cur);
2002-05-04 05:03:17 +02:00
}
vpi_mode_flag = VPI_MODE_NONE;
2002-05-04 05:03:17 +02:00
}
2003-04-20 01:32:57 +02:00
void vpiPostsim(void) {
2002-05-04 05:03:17 +02:00
struct __vpiCallback* cur;
/*
* Walk the list of register callbacks
*/
assert(vpi_mode_flag == VPI_MODE_NONE);
vpi_mode_flag = VPI_MODE_ROSYNC;
2002-05-04 05:17:29 +02:00
while (EndOfSimulation) {
cur = EndOfSimulation;
EndOfSimulation = cur->next;
/* Only set the time if it is not NULL. */
if (cur->cb_data.time)
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
2002-05-04 05:17:29 +02:00
(cur->cb_data.cb_rtn)(&cur->cb_data);
delete_vpi_callback(cur);
2002-05-04 05:03:17 +02:00
}
vpi_mode_flag = VPI_MODE_NONE;
2002-05-04 05:03:17 +02:00
}
2003-04-20 01:32:57 +02:00
/*
* The scheduler invokes this to clear out callbacks for the next
* simulation time.
*/
void vpiNextSimTime(void)
{
struct __vpiCallback* cur;
while (NextSimTime) {
cur = NextSimTime;
NextSimTime = cur->next;
(cur->cb_data.cb_rtn)(&cur->cb_data);
delete_vpi_callback(cur);
2003-04-20 01:32:57 +02:00
}
}
2002-05-04 05:03:17 +02:00
static struct __vpiCallback* make_prepost(p_cb_data data)
{
struct __vpiCallback*obj = new_vpi_callback();
obj->cb_data = *data;
/* Insert at head of list */
switch (data->reason) {
case cbEndOfCompile:
2003-04-20 01:32:57 +02:00
obj->next = EndOfCompile;
EndOfCompile = obj;
break;
2002-05-04 05:03:17 +02:00
case cbStartOfSimulation:
2003-04-20 01:32:57 +02:00
obj->next = StartOfSimulation;
StartOfSimulation = obj;
break;
2002-05-04 05:03:17 +02:00
case cbEndOfSimulation:
2003-04-20 01:32:57 +02:00
obj->next = EndOfSimulation;
EndOfSimulation = obj;
break;
case cbNextSimTime:
obj->next = NextSimTime;
NextSimTime = obj;
2002-05-04 05:03:17 +02:00
}
return obj;
}
2001-06-22 00:54:12 +02:00
vpiHandle vpi_register_cb(p_cb_data data)
{
struct __vpiCallback*obj = 0;
2002-06-02 21:05:50 +02:00
assert(data);
2001-06-22 00:54:12 +02:00
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;
2009-11-01 18:26:09 +01:00
case cbAtStartOfSimTime:
obj = make_afterdelay(data, true);
break;
2002-09-20 04:42:11 +02:00
case cbAfterDelay:
2009-11-01 18:26:09 +01:00
obj = make_afterdelay(data, false);
2002-09-20 04:42:11 +02:00
break;
2002-05-04 05:03:17 +02:00
case cbEndOfCompile:
case cbStartOfSimulation:
case cbEndOfSimulation:
2003-04-20 01:32:57 +02:00
case cbNextSimTime:
2002-05-04 05:03:17 +02:00
obj = make_prepost(data);
break;
2001-06-22 00:54:12 +02:00
default:
fprintf(stderr, "vpi error: vpi_register_cb invalid or "
"unsupported callback reason: %d\n",
(int)data->reason);
2001-06-22 00:54:12 +02:00
break;
}
return obj? &obj->base : 0;
}
/*
* 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)
2001-06-22 00:54:12 +02:00
{
assert(ref);
assert(ref->vpi_type);
2001-06-22 00:54:12 +02:00
assert(ref->vpi_type->type_code == vpiCallback);
struct __vpiCallback*obj = (struct __vpiCallback*)ref;
obj->cb_data.cb_rtn = 0;
2001-06-22 00:54:12 +02:00
return 1;
2001-06-22 00:54:12 +02:00
}
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);
switch (cur->cb_data.time->type) {
case vpiSimTime:
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
break;
case vpiScaledRealTime: {
cur->cb_data.time->real =
vpip_time_to_scaled_real(schedule_simtime(),
(struct __vpiScope *) vpi_handle(vpiScope,
cur->cb_data.obj));
break;
}
case vpiSuppressTime:
break;
default:
fprintf(stderr, "Unsupported time format %d.\n",
(int)cur->cb_data.time->type);
assert(0);
break;
}
(cur->cb_data.cb_rtn)(&cur->cb_data);
vpi_mode_flag = save_mode;
}
vvp_vpi_callback::vvp_vpi_callback()
{
vpi_callbacks_ = 0;
array_ = 0;
array_word_ = 0;
}
vvp_vpi_callback::~vvp_vpi_callback()
{
assert(vpi_callbacks_ == 0);
assert(array_ == 0);
}
void vvp_vpi_callback::attach_as_word(vvp_array_t arr, unsigned long addr)
{
assert(array_ == 0);
array_ = arr;
array_word_ = addr;
}
void vvp_vpi_callback::add_vpi_callback(__vpiCallback*cb)
{
cb->next = vpi_callbacks_;
vpi_callbacks_ = cb;
}
#ifdef CHECK_WITH_VALGRIND
void vvp_vpi_callback::clear_all_callbacks()
{
while (vpi_callbacks_) {
struct __vpiCallback*tmp = vpi_callbacks_->next;
delete_vpi_callback(vpi_callbacks_);
vpi_callbacks_ = 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()
{
if (array_) array_word_change(array_, array_word_);
struct __vpiCallback *next = vpi_callbacks_;
struct __vpiCallback *prev = 0;
2002-04-06 22:25:45 +02:00
while (next) {
struct __vpiCallback*cur = next;
next = cur->next;
if (cur->cb_data.cb_rtn != 0) {
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_vpi_callback(cur);
} else {
assert(prev->next == cur);
prev->next = next;
cur->next = 0;
delete_vpi_callback(cur);
}
2001-06-22 00:54:12 +02:00
}
}
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 "
2006-02-22 04:08:53 +01:00
"format %d not supported (fun_signal)\n",
(int)vp->format);
}
}
static void real_signal_value(struct t_vpi_value*vp, double rval)
{
char*rbuf = need_result_buf(64 + 1, RBUF_VAL);
switch (vp->format) {
case vpiObjTypeVal:
vp->format = vpiRealVal;
case vpiRealVal:
vp->value.real = rval;
break;
case vpiIntVal:
vp->value.integer = (int)(rval + 0.5);
break;
case vpiDecStrVal:
sprintf(rbuf, "%0.0f", rval);
vp->value.str = rbuf;
break;
case vpiHexStrVal:
sprintf(rbuf, "%lx", (long)rval);
vp->value.str = rbuf;
break;
case vpiBinStrVal: {
unsigned long val = (unsigned long)rval;
unsigned len = 0;
while (val > 0) {
len += 1;
val /= 2;
}
val = (unsigned long)rval;
for (unsigned idx = 0 ; idx < len ; idx += 1) {
rbuf[len-idx-1] = (val & 1)? '1' : '0';
val /= 2;
}
rbuf[len] = 0;
if (len == 0) {
rbuf[0] = '0';
rbuf[1] = 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_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);
}