2001-06-22 00:54:12 +02:00
|
|
|
/*
|
2008-06-10 02:46:33 +02:00
|
|
|
* Copyright (c) 2001-2008 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>
|
|
|
|
|
# include "vpi_priv.h"
|
2005-07-06 06:29:25 +02:00
|
|
|
# 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"
|
2001-06-22 00:54:12 +02:00
|
|
|
# include <stdio.h>
|
|
|
|
|
# include <assert.h>
|
2001-09-15 20:27:04 +02:00
|
|
|
#ifdef HAVE_MALLOC_H
|
2001-08-08 03:05:06 +02:00
|
|
|
# include <malloc.h>
|
2001-09-15 20:27:04 +02:00
|
|
|
#endif
|
|
|
|
|
# include <stdlib.h>
|
2001-06-22 00:54:12 +02:00
|
|
|
|
2007-04-10 06:32:05 +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.
|
|
|
|
|
*/
|
2002-05-04 05:17:29 +02:00
|
|
|
static int free_simple_callback(vpiHandle ref)
|
|
|
|
|
{
|
2003-02-17 01:58:38 +01: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
|
2001-06-22 00:54:12 +02:00
|
|
|
};
|
|
|
|
|
|
2001-10-31 05:27:46 +01: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.
|
2001-10-31 05:27:46 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
struct sync_cb : public vvp_gen_event_s {
|
|
|
|
|
struct __vpiCallback*handle;
|
2003-09-09 02:56:45 +02:00
|
|
|
bool sync_flag;
|
2005-06-02 18:02:11 +02:00
|
|
|
|
2008-02-16 00:05:39 +01:00
|
|
|
~sync_cb () { }
|
2005-12-05 22:21:18 +01:00
|
|
|
|
2005-06-02 18:02:11 +02:00
|
|
|
virtual void run_run();
|
2001-10-31 05:27:46 +01:00
|
|
|
};
|
|
|
|
|
|
2001-07-13 05:02:34 +02:00
|
|
|
|
2002-05-19 07:18:16 +02:00
|
|
|
struct __vpiCallback* new_vpi_callback()
|
2001-07-13 05:02:34 +02:00
|
|
|
{
|
|
|
|
|
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;
|
2001-07-13 05:02:34 +02:00
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-21 03:30:56 +02:00
|
|
|
void delete_vpi_callback(struct __vpiCallback* ref)
|
2007-04-10 06:32:05 +02:00
|
|
|
{
|
|
|
|
|
assert(ref);
|
|
|
|
|
assert(ref->base.vpi_type);
|
|
|
|
|
assert(ref->base.vpi_type->type_code == vpiCallback);
|
2008-06-10 02:46:33 +02:00
|
|
|
if (ref->cb_sync != 0) delete ref->cb_sync;
|
2007-04-10 06:32:05 +02:00
|
|
|
delete ref;
|
|
|
|
|
}
|
|
|
|
|
|
2001-07-13 05:02:34 +02:00
|
|
|
|
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)
|
|
|
|
|
{
|
2008-11-09 01:26:55 +01:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2001-07-13 05:02:34 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2001-07-11 04:27:21 +02:00
|
|
|
obj->cb_data.time = &obj->cb_time;
|
2008-08-13 19:29:44 +02:00
|
|
|
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
|
|
|
|
2001-07-11 04:27:21 +02:00
|
|
|
assert(data->obj);
|
|
|
|
|
assert(data->obj->vpi_type);
|
2002-04-06 22:25:45 +02:00
|
|
|
|
2002-05-19 07:18:16 +02:00
|
|
|
switch (data->obj->vpi_type->type_code) {
|
|
|
|
|
|
|
|
|
|
case vpiReg:
|
|
|
|
|
case vpiNet:
|
2002-07-12 04:07:36 +02:00
|
|
|
case vpiIntegerVar:
|
2004-12-11 03:31:25 +01:00
|
|
|
/* Attach the callback to the vvp_fun_signal node by
|
|
|
|
|
putting it in the vpi_callbacks list. */
|
2002-05-19 07:18:16 +02:00
|
|
|
struct __vpiSignal*sig;
|
|
|
|
|
sig = reinterpret_cast<__vpiSignal*>(data->obj);
|
|
|
|
|
|
2005-11-25 18:55:26 +01:00
|
|
|
vvp_fun_signal_base*sig_fun;
|
|
|
|
|
sig_fun = dynamic_cast<vvp_fun_signal_base*>(sig->node->fun);
|
2004-12-11 03:31:25 +01:00
|
|
|
assert(sig_fun);
|
2002-05-19 07:18:16 +02:00
|
|
|
|
2004-12-11 03:31:25 +01:00
|
|
|
/* Attach the __vpiCallback object to the signal. */
|
2005-07-06 06:29:25 +02:00
|
|
|
sig_fun->add_vpi_callback(obj);
|
2002-05-19 07:18:16 +02:00
|
|
|
break;
|
|
|
|
|
|
2003-02-10 06:20:10 +01:00
|
|
|
case vpiRealVar:
|
|
|
|
|
vpip_real_value_change(obj, data->obj);
|
|
|
|
|
break;
|
|
|
|
|
|
2002-05-19 07:18:16 +02:00
|
|
|
case vpiNamedEvent:
|
|
|
|
|
struct __vpiNamedEvent*nev;
|
|
|
|
|
nev = reinterpret_cast<__vpiNamedEvent*>(data->obj);
|
|
|
|
|
obj->next = nev->callbacks;
|
|
|
|
|
nev->callbacks = obj;
|
|
|
|
|
break;
|
|
|
|
|
|
2008-05-21 03:30:56 +02:00
|
|
|
case vpiMemoryWord:
|
|
|
|
|
vpip_array_word_change(obj, data->obj);
|
|
|
|
|
break;
|
|
|
|
|
|
2008-09-05 05:19:07 +02:00
|
|
|
case vpiMemory:
|
|
|
|
|
vpip_array_change(obj, data->obj);
|
|
|
|
|
break;
|
|
|
|
|
|
2008-06-12 06:28:21 +02:00
|
|
|
case vpiPartSelect:
|
|
|
|
|
vpip_part_select_value_change(obj, data->obj);
|
|
|
|
|
break;
|
|
|
|
|
|
2002-06-11 05:47:34 +02:00
|
|
|
case vpiModule:
|
|
|
|
|
case vpiConstant:
|
2003-03-12 03:50:32 +01:00
|
|
|
case vpiParameter:
|
2002-06-11 05:47:34 +02:00
|
|
|
/* These are constant, so there are no value change
|
|
|
|
|
lists to put them in. */
|
|
|
|
|
break;
|
|
|
|
|
|
2002-05-19 07:18:16 +02:00
|
|
|
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;
|
2002-05-19 07:18:16 +02:00
|
|
|
}
|
2001-06-22 00:54:12 +02:00
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
2005-06-02 18:02:11 +02:00
|
|
|
void sync_cb::run_run()
|
2001-07-11 04:27:21 +02:00
|
|
|
{
|
2005-06-02 18:02:11 +02:00
|
|
|
if (handle == 0)
|
2001-07-11 04:27:21 +02:00
|
|
|
return;
|
|
|
|
|
|
2005-06-02 18:02:11 +02:00
|
|
|
struct __vpiCallback*cur = handle;
|
2001-07-11 04:27:21 +02:00
|
|
|
cur->cb_data.time->type = vpiSimTime;
|
2002-04-20 06:33:23 +02:00
|
|
|
vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime());
|
2002-05-18 04:34:11 +02:00
|
|
|
|
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);
|
2005-06-02 18:02:11 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2001-07-11 04:27:21 +02:00
|
|
|
|
2007-04-10 06:32:05 +02:00
|
|
|
delete_vpi_callback(cur);
|
2001-07-11 04:27:21 +02:00
|
|
|
}
|
|
|
|
|
|
2002-04-20 06:33:23 +02:00
|
|
|
static struct __vpiCallback* make_sync(p_cb_data data, bool readonly_flag)
|
2001-07-11 04:27:21 +02:00
|
|
|
{
|
2001-07-13 05:02:34 +02:00
|
|
|
struct __vpiCallback*obj = new_vpi_callback();
|
2001-07-11 04:27:21 +02:00
|
|
|
obj->cb_data = *data;
|
2002-05-09 05:34:31 +02:00
|
|
|
assert(data->time);
|
2001-07-11 04:27:21 +02:00
|
|
|
obj->cb_time = *(data->time);
|
|
|
|
|
obj->cb_data.time = &obj->cb_time;
|
|
|
|
|
|
|
|
|
|
obj->next = 0;
|
|
|
|
|
|
|
|
|
|
struct sync_cb*cb = new sync_cb;
|
2002-04-20 06:33:23 +02:00
|
|
|
cb->sync_flag = readonly_flag? true : false;
|
2001-07-11 04:27:21 +02:00
|
|
|
cb->handle = obj;
|
|
|
|
|
obj->cb_sync = cb;
|
|
|
|
|
|
2002-04-20 06:33:23 +02:00
|
|
|
switch (obj->cb_time.type) {
|
|
|
|
|
case vpiSuppressTime:
|
2006-09-29 03:24:34 +02:00
|
|
|
schedule_generic(cb, 0, true, readonly_flag);
|
2002-04-20 06:33:23 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case vpiSimTime:
|
|
|
|
|
{ vvp_time64_t tv = vpip_timestruct_to_time(&obj->cb_time);
|
|
|
|
|
vvp_time64_t tn = schedule_simtime();
|
|
|
|
|
if (tv < tn) {
|
2006-09-29 03:24:34 +02:00
|
|
|
schedule_generic(cb, 0, true, readonly_flag);
|
2002-04-20 06:33:23 +02:00
|
|
|
} else {
|
2006-09-29 03:24:34 +02:00
|
|
|
schedule_generic(cb, tv - tn, true, readonly_flag);
|
2002-04-20 06:33:23 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2007-08-23 00:15:32 +02:00
|
|
|
fprintf(stderr, "Unsupported time type %d.\n", obj->cb_time.type);
|
2002-04-20 06:33:23 +02:00
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2001-07-11 04:27:21 +02:00
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-20 04:42:11 +02:00
|
|
|
static struct __vpiCallback* make_afterdelay(p_cb_data data)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
switch (obj->cb_time.type) {
|
|
|
|
|
case vpiSimTime: {
|
|
|
|
|
vvp_time64_t tv = vpip_timestruct_to_time(&obj->cb_time);
|
2005-06-09 07:04:45 +02:00
|
|
|
schedule_generic(cb, tv, false);
|
2002-09-20 04:42:11 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2007-08-23 00:15:32 +02:00
|
|
|
fprintf(stderr, "Unsupported time type %d.\n", obj->cb_time.type);
|
2002-09-20 04:42:11 +02:00
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2007-08-24 03:24:51 +02:00
|
|
|
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
|
|
|
*/
|
2002-05-18 04:34:11 +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);
|
2007-04-10 06:32:05 +02:00
|
|
|
delete_vpi_callback(cur);
|
2002-05-04 05:03:17 +02:00
|
|
|
}
|
2002-05-04 05:17:29 +02:00
|
|
|
|
2007-08-24 03:24:51 +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);
|
2007-04-10 06:32:05 +02:00
|
|
|
delete_vpi_callback(cur);
|
2002-05-04 05:03:17 +02:00
|
|
|
}
|
2002-05-18 04:34:11 +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
|
|
|
|
|
*/
|
2002-05-18 04:34:11 +02:00
|
|
|
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;
|
2007-11-16 23:19:32 +01:00
|
|
|
/* 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);
|
2007-04-10 06:32:05 +02:00
|
|
|
delete_vpi_callback(cur);
|
2002-05-04 05:03:17 +02:00
|
|
|
}
|
2002-05-18 04:34:11 +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);
|
2007-04-10 06:32:05 +02:00
|
|
|
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;
|
|
|
|
|
|
2001-07-11 04:27:21 +02:00
|
|
|
case cbReadOnlySynch:
|
2002-04-20 06:33:23 +02:00
|
|
|
obj = make_sync(data, true);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case cbReadWriteSynch:
|
|
|
|
|
obj = make_sync(data, false);
|
2001-07-11 04:27:21 +02:00
|
|
|
break;
|
|
|
|
|
|
2002-09-20 04:42:11 +02:00
|
|
|
case cbAfterDelay:
|
|
|
|
|
obj = make_afterdelay(data);
|
|
|
|
|
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",
|
|
|
|
|
data->reason);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return obj? &obj->base : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-09-07 06:54:51 +02:00
|
|
|
/*
|
|
|
|
|
* 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.
|
|
|
|
|
*/
|
2004-02-18 03:51:59 +01:00
|
|
|
PLI_INT32 vpi_remove_cb(vpiHandle ref)
|
2001-06-22 00:54:12 +02:00
|
|
|
{
|
2001-07-11 04:27:21 +02:00
|
|
|
assert(ref);
|
|
|
|
|
assert(ref->vpi_type);
|
2001-06-22 00:54:12 +02:00
|
|
|
assert(ref->vpi_type->type_code == vpiCallback);
|
|
|
|
|
|
2002-09-07 06:54:51 +02:00
|
|
|
struct __vpiCallback*obj = (struct __vpiCallback*)ref;
|
|
|
|
|
obj->cb_data.cb_rtn = 0;
|
2001-06-22 00:54:12 +02:00
|
|
|
|
2003-02-17 01:58:38 +01:00
|
|
|
return 1;
|
2001-06-22 00:54:12 +02:00
|
|
|
}
|
|
|
|
|
|
2002-05-19 07:18:16 +02:00
|
|
|
void callback_execute(struct __vpiCallback*cur)
|
|
|
|
|
{
|
2002-05-29 00:55:20 +02:00
|
|
|
const vpi_mode_t save_mode = vpi_mode_flag;
|
2002-05-19 07:18:16 +02:00
|
|
|
vpi_mode_flag = VPI_MODE_RWSYNC;
|
|
|
|
|
|
2002-09-07 06:54:51 +02:00
|
|
|
assert(cur->cb_data.cb_rtn);
|
2008-08-13 19:29:44 +02:00
|
|
|
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",
|
|
|
|
|
cur->cb_data.time->type);
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2002-05-19 07:18:16 +02:00
|
|
|
(cur->cb_data.cb_rtn)(&cur->cb_data);
|
|
|
|
|
|
2002-05-29 00:55:20 +02:00
|
|
|
vpi_mode_flag = save_mode;
|
2002-05-19 07:18:16 +02:00
|
|
|
}
|
|
|
|
|
|
2005-07-06 06:29:25 +02:00
|
|
|
vvp_vpi_callback::vvp_vpi_callback()
|
|
|
|
|
{
|
|
|
|
|
vpi_callbacks_ = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vvp_vpi_callback::~vvp_vpi_callback()
|
|
|
|
|
{
|
|
|
|
|
assert(vpi_callbacks_ == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void vvp_vpi_callback::add_vpi_callback(__vpiCallback*cb)
|
|
|
|
|
{
|
|
|
|
|
cb->next = vpi_callbacks_;
|
|
|
|
|
vpi_callbacks_ = cb;
|
|
|
|
|
}
|
|
|
|
|
|
2008-11-13 10:09:20 +01:00
|
|
|
__vpiCallback *vvp_vpi_callback::has_vpi_callback(PLI_INT32(* cb_rtn)(struct t_cb_data *cb))
|
|
|
|
|
{
|
|
|
|
|
struct __vpiCallback *cur = vpi_callbacks_;
|
|
|
|
|
|
|
|
|
|
for (; cur ; cur = cur->next) {
|
|
|
|
|
if (cb_rtn == cur->cb_data.cb_rtn) return cur;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-06-22 01:05:08 +02:00
|
|
|
/*
|
2004-12-11 03:31:25 +01:00
|
|
|
* 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.
|
2001-06-22 01:05:08 +02:00
|
|
|
*/
|
2005-07-06 06:29:25 +02:00
|
|
|
void vvp_vpi_callback::run_vpi_callbacks()
|
2001-08-08 03:05:06 +02:00
|
|
|
{
|
2005-07-06 06:29:25 +02:00
|
|
|
struct __vpiCallback *next = vpi_callbacks_;
|
2002-09-07 06:54:51 +02:00
|
|
|
struct __vpiCallback *prev = 0;
|
2002-04-06 22:25:45 +02:00
|
|
|
|
2001-08-08 03:05:06 +02:00
|
|
|
while (next) {
|
2004-12-11 03:31:25 +01:00
|
|
|
struct __vpiCallback*cur = next;
|
2001-08-08 03:05:06 +02:00
|
|
|
next = cur->next;
|
2002-09-07 06:54:51 +02:00
|
|
|
|
|
|
|
|
if (cur->cb_data.cb_rtn != 0) {
|
2005-07-06 06:29:25 +02:00
|
|
|
if (cur->cb_data.value)
|
|
|
|
|
get_value(cur->cb_data.value);
|
2002-09-07 06:54:51 +02:00
|
|
|
|
|
|
|
|
callback_execute(cur);
|
|
|
|
|
prev = cur;
|
|
|
|
|
|
|
|
|
|
} else if (prev == 0) {
|
|
|
|
|
|
2005-07-06 06:29:25 +02:00
|
|
|
vpi_callbacks_ = next;
|
2002-09-07 06:54:51 +02:00
|
|
|
cur->next = 0;
|
2007-04-10 06:32:05 +02:00
|
|
|
delete_vpi_callback(cur);
|
2002-09-07 06:54:51 +02:00
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
assert(prev->next == cur);
|
|
|
|
|
prev->next = next;
|
|
|
|
|
cur->next = 0;
|
2007-04-10 06:32:05 +02:00
|
|
|
delete_vpi_callback(cur);
|
2001-10-25 06:19:53 +02:00
|
|
|
}
|
2001-06-22 00:54:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-21 03:30:56 +02:00
|
|
|
vvp_vpi_callback_wordable::vvp_vpi_callback_wordable()
|
|
|
|
|
{
|
|
|
|
|
array_ = 0;
|
|
|
|
|
array_word_ = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vvp_vpi_callback_wordable::~vvp_vpi_callback_wordable()
|
|
|
|
|
{
|
|
|
|
|
assert(array_ == 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void vvp_vpi_callback_wordable::run_vpi_callbacks()
|
|
|
|
|
{
|
|
|
|
|
if (array_) array_word_change(array_, array_word_);
|
|
|
|
|
|
|
|
|
|
vvp_vpi_callback::run_vpi_callbacks();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void vvp_vpi_callback_wordable::attach_as_word(vvp_array_t arr, unsigned long addr)
|
|
|
|
|
{
|
|
|
|
|
assert(array_ == 0);
|
|
|
|
|
array_ = arr;
|
|
|
|
|
array_word_ = addr;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-28 18:52:39 +01:00
|
|
|
void vvp_fun_signal4::get_value(struct t_vpi_value*vp)
|
2005-07-06 06:29:25 +02:00
|
|
|
{
|
|
|
|
|
switch (vp->format) {
|
|
|
|
|
case vpiScalarVal:
|
2008-08-13 19:29:44 +02:00
|
|
|
// This works because vvp_bit4_t has the same encoding
|
2008-09-04 18:41:51 +02:00
|
|
|
// as a scalar value! See vpip_vec4_get_value() for a
|
2008-08-13 19:29:44 +02:00
|
|
|
// more robust method.
|
2005-07-06 06:29:25 +02:00
|
|
|
vp->value.scalar = value(0);
|
|
|
|
|
break;
|
2008-08-13 19:29:44 +02:00
|
|
|
|
|
|
|
|
case vpiBinStrVal:
|
|
|
|
|
case vpiOctStrVal:
|
|
|
|
|
case vpiDecStrVal:
|
|
|
|
|
case vpiHexStrVal:
|
|
|
|
|
case vpiIntVal:
|
|
|
|
|
case vpiVectorVal:
|
|
|
|
|
case vpiStringVal:
|
|
|
|
|
case vpiRealVal: {
|
|
|
|
|
unsigned wid = 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;
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-06 06:29:25 +02:00
|
|
|
case vpiSuppressVal:
|
|
|
|
|
break;
|
2008-08-13 19:29:44 +02:00
|
|
|
|
2005-07-06 06:29:25 +02:00
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "vpi_callback: value "
|
2006-02-22 04:08:53 +01:00
|
|
|
"format %d not supported (fun_signal)\n",
|
2005-07-06 06:29:25 +02:00
|
|
|
vp->format);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-11-25 18:55:26 +01:00
|
|
|
void vvp_fun_signal8::get_value(struct t_vpi_value*vp)
|
|
|
|
|
{
|
|
|
|
|
switch (vp->format) {
|
|
|
|
|
case vpiScalarVal:
|
|
|
|
|
vp->value.scalar = value(0);
|
|
|
|
|
break;
|
|
|
|
|
case vpiSuppressVal:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "vpi_callback: value "
|
2006-02-22 04:08:53 +01:00
|
|
|
"format %d not supported (fun_signal8)\n",
|
2005-11-25 18:55:26 +01:00
|
|
|
vp->format);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-07-06 06:29:25 +02:00
|
|
|
void vvp_fun_signal_real::get_value(struct t_vpi_value*vp)
|
|
|
|
|
{
|
|
|
|
|
char*rbuf = need_result_buf(64 + 1, RBUF_VAL);
|
|
|
|
|
|
|
|
|
|
switch (vp->format) {
|
|
|
|
|
case vpiObjTypeVal:
|
|
|
|
|
vp->format = vpiRealVal;
|
|
|
|
|
|
|
|
|
|
case vpiRealVal:
|
|
|
|
|
vp->value.real = real_value();
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case vpiIntVal:
|
|
|
|
|
vp->value.integer = (int)(real_value() + 0.5);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case vpiDecStrVal:
|
|
|
|
|
sprintf(rbuf, "%0.0f", real_value());
|
|
|
|
|
vp->value.str = rbuf;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case vpiHexStrVal:
|
|
|
|
|
sprintf(rbuf, "%lx", (long)real_value());
|
|
|
|
|
vp->value.str = rbuf;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case vpiBinStrVal: {
|
|
|
|
|
unsigned long val = (unsigned long)real_value();
|
|
|
|
|
unsigned len = 0;
|
|
|
|
|
|
|
|
|
|
while (val > 0) {
|
|
|
|
|
len += 1;
|
|
|
|
|
val /= 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val = (unsigned long)real_value();
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2008-08-13 19:29:44 +02:00
|
|
|
case vpiSuppressVal:
|
|
|
|
|
break;
|
|
|
|
|
|
2005-07-06 06:29:25 +02:00
|
|
|
default:
|
|
|
|
|
fprintf(stderr, "vpi_callback: value "
|
2006-02-22 04:08:53 +01:00
|
|
|
"format %d not supported (fun_signal_real)\n",
|
2005-07-06 06:29:25 +02:00
|
|
|
vp->format);
|
|
|
|
|
}
|
|
|
|
|
}
|