2011-06-25 03:42:43 +02:00
|
|
|
/*
|
2025-10-12 23:23:24 +02:00
|
|
|
* Copyright (c) 2011-2025 Stephen Williams (steve@icarus.com)
|
2011-06-25 03:42:43 +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
|
2012-08-29 03:41:23 +02:00
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2011-06-25 03:42:43 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
# include "vpi_config.h"
|
|
|
|
|
# include "vpi_user.h"
|
2011-10-06 03:49:02 +02:00
|
|
|
# include <assert.h>
|
2025-10-12 23:23:24 +02:00
|
|
|
# include <stdint.h>
|
2011-10-06 03:49:02 +02:00
|
|
|
# include "ivl_alloc.h"
|
2016-01-18 14:38:59 +01:00
|
|
|
# include "sys_priv.h"
|
2011-06-25 03:42:43 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* The $ivlh_attribute_event implements the VHDL <varname>'event
|
|
|
|
|
* attribute. It does this by monitoring value-change events on the
|
|
|
|
|
* operand, and noting the time. If the $ivlh_attribute_event is
|
|
|
|
|
* called at the same simulation time as a value-change, then the
|
|
|
|
|
* function returns logic true. Otherwise it returns false.
|
2015-03-05 16:12:40 +01:00
|
|
|
*
|
|
|
|
|
* $ivlh_{rising,falling}_edge implement the VHDL rising_edge() and
|
|
|
|
|
* falling_edge() system functions.
|
2011-06-25 03:42:43 +02:00
|
|
|
*/
|
|
|
|
|
struct monitor_data {
|
|
|
|
|
struct t_vpi_time last_event;
|
2015-03-05 16:12:40 +01:00
|
|
|
struct t_vpi_value last_value;
|
2011-06-25 03:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
2011-10-06 03:49:02 +02:00
|
|
|
static struct monitor_data **mdata = 0;
|
|
|
|
|
static unsigned mdata_count = 0;
|
|
|
|
|
|
2016-01-15 15:47:31 +01:00
|
|
|
typedef enum {
|
|
|
|
|
EVENT = 0,
|
|
|
|
|
RISING_EDGE = 1,
|
|
|
|
|
FALLING_EDGE = 2
|
|
|
|
|
} event_type_t;
|
|
|
|
|
static const char* attr_func_names[] = {
|
|
|
|
|
"$ivlh_attribute_event",
|
|
|
|
|
"$ivlh_rising_edge",
|
|
|
|
|
"$ivlh_falling_edge"
|
|
|
|
|
};
|
2016-01-18 14:38:59 +01:00
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
SHIFT_LEFT = 0,
|
|
|
|
|
SHIFT_RIGHT = 1,
|
|
|
|
|
} shift_type_t;
|
|
|
|
|
static const char* shift_func_names[] = {
|
|
|
|
|
"$ivlh_shift_left",
|
|
|
|
|
"$ivlh_shift_right",
|
2015-03-05 16:12:40 +01:00
|
|
|
};
|
|
|
|
|
|
2011-10-06 03:49:02 +02:00
|
|
|
/* To keep valgrind happy free the allocated memory. */
|
|
|
|
|
static PLI_INT32 cleanup_mdata(p_cb_data cause)
|
|
|
|
|
{
|
|
|
|
|
unsigned idx;
|
2014-07-09 23:16:57 +02:00
|
|
|
|
|
|
|
|
(void) cause; /* Parameter is not used. */
|
2011-10-06 03:49:02 +02:00
|
|
|
|
|
|
|
|
for (idx= 0; idx < mdata_count; idx += 1) {
|
|
|
|
|
free(mdata[idx]);
|
|
|
|
|
}
|
|
|
|
|
free(mdata);
|
|
|
|
|
mdata = 0;
|
|
|
|
|
mdata_count = 0;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-07-04 22:43:20 +02:00
|
|
|
|
|
|
|
|
static PLI_INT32 monitor_events(struct t_cb_data*cb)
|
2011-06-25 03:42:43 +02:00
|
|
|
{
|
2011-10-06 03:49:02 +02:00
|
|
|
struct monitor_data*mon = (struct monitor_data*)(cb->user_data);
|
2011-06-25 03:42:43 +02:00
|
|
|
|
|
|
|
|
assert(cb->time);
|
|
|
|
|
assert(cb->time->type == vpiSimTime);
|
2015-03-05 16:12:40 +01:00
|
|
|
|
2011-06-25 03:42:43 +02:00
|
|
|
mon->last_event = *(cb->time);
|
2015-03-05 16:12:40 +01:00
|
|
|
mon->last_value = *(cb->value);
|
2011-06-25 03:42:43 +02:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 16:12:40 +01:00
|
|
|
static PLI_INT32 ivlh_attribute_event_compiletf(ICARUS_VPI_CONST PLI_BYTE8*data)
|
2011-06-25 03:42:43 +02:00
|
|
|
{
|
2025-10-12 23:23:24 +02:00
|
|
|
event_type_t type = (event_type_t)(uintptr_t)data;
|
2011-06-25 03:42:43 +02:00
|
|
|
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
vpiHandle argv = vpi_iterate(vpiArgument, sys);
|
|
|
|
|
vpiHandle arg;
|
2011-10-06 03:49:02 +02:00
|
|
|
struct monitor_data*mon;
|
|
|
|
|
struct t_cb_data cb;
|
|
|
|
|
struct t_vpi_time tb;
|
2015-03-05 16:12:40 +01:00
|
|
|
struct t_vpi_value vb;
|
2011-06-25 03:42:43 +02:00
|
|
|
|
2011-10-06 03:49:02 +02:00
|
|
|
/* Check that there are arguments. */
|
2011-06-25 03:42:43 +02:00
|
|
|
if (argv == 0) {
|
2011-10-06 03:49:02 +02:00
|
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys),
|
|
|
|
|
(int)vpi_get(vpiLineNo, sys));
|
|
|
|
|
vpi_printf("(compiler error) %s requires a single argument.\n",
|
2016-01-15 15:47:31 +01:00
|
|
|
attr_func_names[type]);
|
2021-01-21 08:50:06 +01:00
|
|
|
vpip_set_return_value(1);
|
2011-06-25 03:42:43 +02:00
|
|
|
vpi_control(vpiFinish, 1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-06 03:49:02 +02:00
|
|
|
/* Icarus either returns 0 above or has one argument. */
|
2011-06-25 03:42:43 +02:00
|
|
|
arg = vpi_scan(argv);
|
|
|
|
|
assert(arg);
|
|
|
|
|
|
2011-10-06 03:49:02 +02:00
|
|
|
mon = malloc(sizeof(struct monitor_data));
|
|
|
|
|
/* Add this to the list of data. */
|
|
|
|
|
mdata_count += 1;
|
|
|
|
|
mdata = (struct monitor_data **) realloc(mdata,
|
|
|
|
|
sizeof(struct monitor_data **) *
|
|
|
|
|
mdata_count);
|
|
|
|
|
mdata[mdata_count-1] = mon;
|
2011-06-25 03:42:43 +02:00
|
|
|
|
|
|
|
|
tb.type = vpiSimTime;
|
2015-03-05 16:12:40 +01:00
|
|
|
vb.format = vpiScalarVal;
|
2011-06-25 03:42:43 +02:00
|
|
|
cb.reason = cbValueChange;
|
|
|
|
|
cb.cb_rtn = monitor_events;
|
|
|
|
|
cb.obj = arg;
|
|
|
|
|
cb.time = &tb;
|
2015-03-05 16:12:40 +01:00
|
|
|
cb.value = &vb;
|
2011-10-06 03:49:02 +02:00
|
|
|
cb.user_data = (char*) (mon);
|
2011-06-25 03:42:43 +02:00
|
|
|
vpi_register_cb(&cb);
|
2011-10-06 03:49:02 +02:00
|
|
|
vpi_put_userdata(sys, mon);
|
2011-06-25 03:42:43 +02:00
|
|
|
|
2012-02-16 18:37:51 +01:00
|
|
|
/* Check that there is no more than one argument. */
|
2011-06-25 03:42:43 +02:00
|
|
|
arg = vpi_scan(argv);
|
|
|
|
|
if (arg != 0) {
|
2011-10-06 03:49:02 +02:00
|
|
|
vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys),
|
|
|
|
|
(int)vpi_get(vpiLineNo, sys));
|
|
|
|
|
vpi_printf("(compiler error) %s only takes a single argument.\n",
|
2016-01-15 15:47:31 +01:00
|
|
|
attr_func_names[type]);
|
2011-10-06 03:49:02 +02:00
|
|
|
vpi_free_object(argv);
|
2021-01-21 08:50:06 +01:00
|
|
|
vpip_set_return_value(1);
|
2011-06-25 03:42:43 +02:00
|
|
|
vpi_control(vpiFinish, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 16:12:40 +01:00
|
|
|
static PLI_INT32 ivlh_attribute_event_calltf(ICARUS_VPI_CONST PLI_BYTE8*data)
|
2011-06-25 03:42:43 +02:00
|
|
|
{
|
2025-10-12 23:23:24 +02:00
|
|
|
event_type_t type = (event_type_t)(uintptr_t)data;
|
2011-06-25 03:42:43 +02:00
|
|
|
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
struct t_vpi_value rval;
|
2011-10-06 03:49:02 +02:00
|
|
|
struct monitor_data*mon;
|
2014-07-09 23:16:57 +02:00
|
|
|
|
2011-06-25 03:42:43 +02:00
|
|
|
rval.format = vpiScalarVal;
|
|
|
|
|
|
2011-10-06 03:49:02 +02:00
|
|
|
mon = (struct monitor_data*) (vpi_get_userdata(sys));
|
2011-06-25 03:42:43 +02:00
|
|
|
|
|
|
|
|
if (mon->last_event.type == 0) {
|
|
|
|
|
rval.value.scalar = vpi0;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
struct t_vpi_time tnow;
|
|
|
|
|
tnow.type = vpiSimTime;
|
2011-10-06 03:49:02 +02:00
|
|
|
vpi_get_time(0, &tnow);
|
2011-06-25 03:42:43 +02:00
|
|
|
|
|
|
|
|
rval.value.scalar = vpi1;
|
2015-03-05 16:12:40 +01:00
|
|
|
|
2015-08-17 03:20:47 +02:00
|
|
|
// Detect if change occurred in this moment
|
2011-06-25 03:42:43 +02:00
|
|
|
if (mon->last_event.high != tnow.high)
|
|
|
|
|
rval.value.scalar = vpi0;
|
|
|
|
|
if (mon->last_event.low != tnow.low)
|
|
|
|
|
rval.value.scalar = vpi0;
|
2015-03-05 16:12:40 +01:00
|
|
|
|
|
|
|
|
// Determine the edge, if required
|
2015-06-24 23:33:03 +02:00
|
|
|
if (type == RISING_EDGE && mon->last_value.value.scalar != vpi1)
|
2015-03-05 16:12:40 +01:00
|
|
|
rval.value.scalar = vpi0;
|
2015-06-24 23:33:03 +02:00
|
|
|
else if (type == FALLING_EDGE && mon->last_value.value.scalar != vpi0)
|
2015-03-05 16:12:40 +01:00
|
|
|
rval.value.scalar = vpi0;
|
2011-06-25 03:42:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vpi_put_value(sys, &rval, 0, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-03-05 16:12:40 +01:00
|
|
|
static PLI_INT32 ivlh_attribute_event_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type)
|
2011-07-27 22:50:37 +02:00
|
|
|
{
|
2015-03-05 16:12:40 +01:00
|
|
|
(void) type; /* Parameter is not used. */
|
2011-07-27 22:50:37 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-18 14:38:59 +01:00
|
|
|
static PLI_INT32 ivlh_shift_calltf(ICARUS_VPI_CONST PLI_BYTE8*data)
|
|
|
|
|
{
|
2025-10-12 23:23:24 +02:00
|
|
|
shift_type_t shift_type = (shift_type_t)(uintptr_t)data;
|
2016-01-18 14:38:59 +01:00
|
|
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
|
|
|
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
|
|
|
|
vpiHandle argh = vpi_scan(argv);
|
|
|
|
|
vpiHandle counth = vpi_scan(argv);
|
|
|
|
|
s_vpi_value val;
|
|
|
|
|
PLI_INT32 count;
|
|
|
|
|
|
|
|
|
|
vpi_free_object(argv);
|
|
|
|
|
|
|
|
|
|
val.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(counth, &val);
|
|
|
|
|
count = val.value.integer;
|
|
|
|
|
|
|
|
|
|
val.format = vpiIntVal;
|
|
|
|
|
vpi_get_value(argh, &val);
|
|
|
|
|
|
|
|
|
|
if(shift_type == SHIFT_LEFT)
|
|
|
|
|
val.value.integer <<= count;
|
|
|
|
|
else if(shift_type == SHIFT_RIGHT)
|
|
|
|
|
val.value.integer >>= count;
|
|
|
|
|
else
|
|
|
|
|
assert(0);
|
|
|
|
|
|
|
|
|
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PLI_INT32 ivlh_shift_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type)
|
|
|
|
|
{
|
|
|
|
|
(void) type; /* Parameter is not used. */
|
|
|
|
|
return 32;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-04 22:43:20 +02:00
|
|
|
static void vhdl_register(void)
|
2011-06-25 03:42:43 +02:00
|
|
|
{
|
|
|
|
|
s_vpi_systf_data tf_data;
|
2011-10-06 03:49:02 +02:00
|
|
|
s_cb_data cb;
|
2011-07-27 22:50:37 +02:00
|
|
|
vpiHandle res;
|
|
|
|
|
|
2016-01-15 15:47:31 +01:00
|
|
|
/* Event attribute functions */
|
2011-07-27 22:50:37 +02:00
|
|
|
tf_data.type = vpiSysFunc;
|
|
|
|
|
tf_data.sysfunctype = vpiSizedFunc;
|
|
|
|
|
tf_data.calltf = ivlh_attribute_event_calltf;
|
|
|
|
|
tf_data.compiletf = ivlh_attribute_event_compiletf;
|
|
|
|
|
tf_data.sizetf = ivlh_attribute_event_sizetf;
|
2016-01-15 15:47:31 +01:00
|
|
|
tf_data.tfname = attr_func_names[EVENT];
|
2015-03-05 16:12:40 +01:00
|
|
|
tf_data.user_data = (PLI_BYTE8*) EVENT;
|
|
|
|
|
res = vpi_register_systf(&tf_data);
|
|
|
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
|
|
2016-01-15 15:47:31 +01:00
|
|
|
tf_data.tfname = attr_func_names[RISING_EDGE];
|
2015-03-05 16:12:40 +01:00
|
|
|
tf_data.user_data = (PLI_BYTE8*) RISING_EDGE;
|
|
|
|
|
res = vpi_register_systf(&tf_data);
|
|
|
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
|
|
2016-01-15 15:47:31 +01:00
|
|
|
tf_data.tfname = attr_func_names[FALLING_EDGE];
|
2015-03-05 16:12:40 +01:00
|
|
|
tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE;
|
2011-07-27 22:50:37 +02:00
|
|
|
res = vpi_register_systf(&tf_data);
|
2016-01-18 14:38:59 +01:00
|
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
|
|
|
|
|
|
/* Shift functions */
|
|
|
|
|
tf_data.type = vpiSysFunc;
|
|
|
|
|
tf_data.sysfunctype = vpiSizedFunc;
|
|
|
|
|
tf_data.calltf = ivlh_shift_calltf;
|
|
|
|
|
tf_data.compiletf = sys_two_numeric_args_compiletf;
|
|
|
|
|
tf_data.sizetf = ivlh_shift_sizetf;
|
|
|
|
|
tf_data.tfname = shift_func_names[SHIFT_LEFT];
|
|
|
|
|
tf_data.user_data = (PLI_BYTE8*) SHIFT_LEFT;
|
|
|
|
|
res = vpi_register_systf(&tf_data);
|
|
|
|
|
vpip_make_systf_system_defined(res);
|
|
|
|
|
|
|
|
|
|
tf_data.tfname = shift_func_names[SHIFT_RIGHT];
|
|
|
|
|
tf_data.user_data = (PLI_BYTE8*) SHIFT_RIGHT;
|
|
|
|
|
res = vpi_register_systf(&tf_data);
|
2011-07-27 22:50:37 +02:00
|
|
|
vpip_make_systf_system_defined(res);
|
2011-10-06 03:49:02 +02:00
|
|
|
|
|
|
|
|
/* Create a callback to clear the monitor data memory when the
|
|
|
|
|
* simulator finishes. */
|
|
|
|
|
cb.time = NULL;
|
|
|
|
|
cb.reason = cbEndOfSimulation;
|
|
|
|
|
cb.cb_rtn = cleanup_mdata;
|
|
|
|
|
cb.user_data = NULL;
|
|
|
|
|
cb.obj = NULL;
|
|
|
|
|
|
|
|
|
|
vpi_register_cb(&cb);
|
2011-06-25 03:42:43 +02:00
|
|
|
}
|
|
|
|
|
|
2014-07-08 21:12:54 +02:00
|
|
|
void (*vlog_startup_routines[])(void) = {
|
2011-06-25 03:42:43 +02:00
|
|
|
vhdl_register,
|
|
|
|
|
0
|
|
|
|
|
};
|